ukui-screensaver/0000775000175000017500000000000015172041106012770 5ustar fengfengukui-screensaver/tests/0000775000175000017500000000000015172041106014132 5ustar fengfengukui-screensaver/tests/unit_test_biometric_helper/0000775000175000017500000000000015172041106021544 5ustar fengfengukui-screensaver/tests/unit_test_biometric_helper/CMakeLists.txt0000664000175000017500000000356015172041106024310 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/biometrichelper.cpp ../../src/common/biodefines.cpp ../../src/dbusifs/giodbus.cpp unit_test_biometric_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/biodefines.h ../../src/dbusifs/biometrichelper.h ../../src/dbusifs/giodbus.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${GLIB2_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_biometric_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_biometric_helper Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_biometric_helper GTest::GTest GTest::Main Threads::Threads ${GLIB2_LIBRARIES} ) ukui-screensaver/tests/unit_test_biometric_helper/main.cpp0000664000175000017500000000156215172041035023201 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_biometric_helper/unit_test_biometric_helper.cpp0000664000175000017500000000765415172041035027677 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/biometrichelper.h" #include "../../src/dbusifs/giodbus.h" #include "stubext.h" using namespace stub_ext; class BiometricHelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pABiometricHelperDbus = new BiometricHelper(); } static void TearDownTestSuite() { delete m_pABiometricHelperDbus; m_pABiometricHelperDbus = nullptr; } static BiometricHelper *m_pABiometricHelperDbus; }; BiometricHelper *BiometricHelperTest::m_pABiometricHelperDbus = nullptr; TEST_F(BiometricHelperTest, Identify) { get_server_gvariant_stdout(-1); QDBusPendingCall value = m_pABiometricHelperDbus->Identify(32, 1000, 0, -1); ASSERT_EQ(value.isValid(), false); } TEST_F(BiometricHelperTest, UkeyIdentify) { QDBusPendingCall value = m_pABiometricHelperDbus->UkeyIdentify(100, 2, 1000); ASSERT_EQ(value.isValid(), false); } TEST_F(BiometricHelperTest, GetHasUkeyFeature) { bool value = m_pABiometricHelperDbus->GetHasUkeyFeature(1000, 0, -1); ASSERT_EQ(value, false); } TEST_F(BiometricHelperTest, GetFeatureCount) { bool value = m_pABiometricHelperDbus->GetFeatureCount(1000, 0, -1); ASSERT_EQ(value, false); } TEST_F(BiometricHelperTest, SetExtraInfo) { int value = m_pABiometricHelperDbus->SetExtraInfo("extra_info", "extra_info"); ASSERT_EQ(value, 0); } TEST_F(BiometricHelperTest, StopOps) { int value = m_pABiometricHelperDbus->StopOps(32, 3000); ASSERT_EQ(value, 0); } TEST_F(BiometricHelperTest, GetUserDevCount) { int value = m_pABiometricHelperDbus->GetUserDevCount(1000); ASSERT_EQ(value, 0); } TEST_F(BiometricHelperTest, GetUserDevFeatureCount) { int value = m_pABiometricHelperDbus->GetUserDevFeatureCount(1000, 32); ASSERT_EQ(value, 0); } TEST_F(BiometricHelperTest, GetDevList) { DeviceList value = m_pABiometricHelperDbus->GetDevList(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BiometricHelperTest, GetUserFeatures) { FeatureMap value = m_pABiometricHelperDbus->GetUserFeatures(1000); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BiometricHelperTest, GetDevCount) { int value = m_pABiometricHelperDbus->GetDevCount(); ASSERT_EQ(value, 0); } TEST_F(BiometricHelperTest, GetDevMesg) { QString value = m_pABiometricHelperDbus->GetDevMesg(32); std::string str = value.toStdString(); const char* cstr = str.data(); ASSERT_EQ(cstr, ""); } TEST_F(BiometricHelperTest, GetNotifyMesg) { QString value = m_pABiometricHelperDbus->GetNotifyMesg(32); std::string str = value.toStdString(); const char* cstr = str.data(); ASSERT_EQ(cstr, ""); } TEST_F(BiometricHelperTest, GetOpsMesg) { QString value = m_pABiometricHelperDbus->GetOpsMesg(32); std::string str = value.toStdString(); const char* cstr = str.data(); ASSERT_EQ(cstr, ""); } TEST_F(BiometricHelperTest, UpdateStatus) { StatusReslut status = m_pABiometricHelperDbus->UpdateStatus(32); ASSERT_EQ(status.result , 0); ASSERT_EQ(status.enable , 0); ASSERT_EQ(status.devNum , 0); ASSERT_EQ(status.devStatus , 0); ASSERT_EQ(status.opsStatus , 0); ASSERT_EQ(status.notifyMessageId , 0); } ukui-screensaver/tests/unit_test_session_helper/0000775000175000017500000000000015172041106021252 5ustar fengfengukui-screensaver/tests/unit_test_session_helper/CMakeLists.txt0000664000175000017500000000420015172041106024006 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(QGS REQUIRED gsettings-qt) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control -fno-exceptions") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/sessionhelper.cpp ../../src/dbusifs/login1helper.cpp ../../src/dbusifs/dbusservermanager.cpp ../../src/userinfo.cpp ../../src/common/global_utils.cpp ../../src/lock-backend/gsettingshelper.cpp unit_test_session_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/userinfo.h ../../src/common/global_utils.h ../../src/dbusifs/sessionhelper.h ../../src/dbusifs/login1helper.h ../../src/dbusifs/dbusservermanager.h ../../src/lock-backend/gsettingshelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_session_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_session_helper Qt5::Core Qt5::Gui Qt5::DBus ${QGS_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_session_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_session_helper/main.cpp0000664000175000017500000000156215172041035022707 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_session_helper/unit_test_session_helper.cpp0000664000175000017500000000545415172041035027107 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/sessionhelper.h" #include "../../src/dbusifs/login1helper.h" #include "stubext.h" using namespace stub_ext; class SessionHelperTest : public testing::Test, public QObject { protected: static void SetUpTestSuite() { m_login1Helper = QSharedPointer(new Login1Helper(nullptr)); m_pSessionHelperDbus = new SessionHelper(m_login1Helper); } static void TearDownTestSuite() { delete m_pSessionHelperDbus; m_pSessionHelperDbus = nullptr; } static SessionHelper *m_pSessionHelperDbus; static QSharedPointer m_login1Helper; }; SessionHelper *SessionHelperTest::m_pSessionHelperDbus = nullptr; QSharedPointer SessionHelperTest::m_login1Helper = nullptr; TEST_F(SessionHelperTest, canAction) { bool value = m_pSessionHelperDbus->canAction(PowerHibernate); bool value1 = m_pSessionHelperDbus->canAction(PowerSuspend); bool value2 = m_pSessionHelperDbus->canAction(PowerMonitorOff); bool value3 = m_pSessionHelperDbus->canAction(PowerLogout); bool value4 = m_pSessionHelperDbus->canAction(PowerReboot); bool value5 = m_pSessionHelperDbus->canAction(PowerShutdown); bool value6 = m_pSessionHelperDbus->canAction(PowerSwitchUser); // bool value7 = m_pSessionHelperDbus->canAction(TestAction); ASSERT_EQ(value, true); ASSERT_EQ(value1, true); ASSERT_EQ(value2, true); ASSERT_EQ(value3, true); ASSERT_EQ(value4, true); ASSERT_EQ(value5, true); ASSERT_EQ(value6, true); } TEST_F(SessionHelperTest, doAction) { bool value = m_pSessionHelperDbus->doAction("Suspend"); ASSERT_EQ(value, true); } TEST_F(SessionHelperTest, getLockCheckStatus) { QStringList value = m_pSessionHelperDbus->getLockCheckStatus("shutdown"); ASSERT_EQ(value.isEmpty(), true); } TEST_F(SessionHelperTest, playShutdownMusic) { bool value = m_pSessionHelperDbus->playShutdownMusic("Logout"); m_pSessionHelperDbus->playShutdownMusic("Reboot"); m_pSessionHelperDbus->playShutdownMusic("SwitchUser"); ASSERT_EQ(value, true); } ukui-screensaver/tests/unit_test_account_helper/0000775000175000017500000000000015172041106021223 5ustar fengfengukui-screensaver/tests/unit_test_account_helper/unit_test_account_helper.cpp0000664000175000017500000000450015172041035027020 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/accountshelper.h" #include "stubext.h" using namespace stub_ext; class AccountsHelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pAccountsHelperDbus = new AccountsHelper(); } static void TearDownTestSuite() { delete m_pAccountsHelperDbus; m_pAccountsHelperDbus = nullptr; } static AccountsHelper *m_pAccountsHelperDbus; }; AccountsHelper *AccountsHelperTest::m_pAccountsHelperDbus = nullptr; TEST_F(AccountsHelperTest, UserLanguageByName) { QString value = m_pAccountsHelperDbus->getUserLanguageByName(getenv("USER")); std::string str = value.toStdString(); const char* cstr = str.data(); ASSERT_STREQ(cstr, "zh_CN"); } TEST_F(AccountsHelperTest, UserBackgroundByName) { QString value = m_pAccountsHelperDbus->getUserBackgroundByName(getenv("USER")); std::string str = value.toStdString(); const char* cstr = str.data(); ASSERT_STREQ(cstr, "zh_CN"); } TEST_F(AccountsHelperTest, UserSessionByName) { QString value = m_pAccountsHelperDbus->getUserSessionByName(getenv("USER")); std::string str = value.toStdString(); const char* cstr = str.data(); ASSERT_STREQ(cstr, "zh_CN"); } TEST_F(AccountsHelperTest, AccountBackground) { QString value = m_pAccountsHelperDbus->getAccountBackground(1000); std::string str = value.toStdString(); const char* cstr = str.data(); ASSERT_STREQ(cstr, "zh_CN"); } TEST_F(AccountsHelperTest, UserList) { QStringList value = m_pAccountsHelperDbus->getUserList(); ASSERT_EQ(value.isEmpty(), false); } ukui-screensaver/tests/unit_test_account_helper/CMakeLists.txt0000664000175000017500000000330215172041106023761 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/accountshelper.cpp ../../src/userinfo.cpp ../../src/common/global_utils.cpp unit_test_account_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/userinfo.h ../../src/common/global_utils.h ../../src/dbusifs/accountshelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_account_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_account_helper Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_account_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_account_helper/main.cpp0000664000175000017500000000156215172041035022660 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/auto_test.sh0000775000175000017500000000303515172041035016502 0ustar fengfeng#!/bin/bash # 设置退出脚本当命令失败时 (非零退出状态) #set -e # 函数:运行单元测试 run_unit_tests() { local pattern="unit_test_*" # 遍历所有匹配模式的目录 for dir in $pattern; do if [ -d "$dir" ] && [ -x "$dir/$(basename "$dir")" ]; then echo "Running tests in $dir..." # 执行单元测试 (cd "$dir" && "./$(basename "$dir")") # 收集代码覆盖率数据 echo "Collecting coverage data..." (cd "$dir" && (find ./ -name '*.o' | xargs gcov --preserve-paths)) else echo "Skipping non-existent or non-executable directory: $dir, $dir/$(basename "$dir")" fi done } # 上传 result.zip 到平台 URL=$1 upload_result() { echo "current pwd : $(pwd)" # 收集覆盖率信息 lcov -d . -c -o r.info # 删除不需要的文件或路径 lcov -r r.info "$(pwd)/unit_test_*" "$(pwd)/../registeredSession/universalinterface.cpp" "/usr/include/*" "/opt/*" -o coverage.info # 生成html覆盖率报告 genhtml "$(pwd)/coverage.info" -o result # 打包 zip -r result.zip result # 上传平台 curl --insecure -X POST -F "file=@/$(pwd)/result.zip" -F "package=ukui-control-center" -F "username=hesisheng" $URL echo "all parameter : $@" } # 编译项目 echo "Compiling the project..." cmake . && make # 运行各个单元测试 echo "Running unit tests..." run_unit_tests # 上传到平台 #upload_result echo "All tests have been run successfully, and coverage data has been collected." ukui-screensaver/tests/unit_test_common/0000775000175000017500000000000015172041106017520 5ustar fengfengukui-screensaver/tests/unit_test_common/CMakeLists.txt0000664000175000017500000000472115172041106022264 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui Widgets X11Extras Svg DBus REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(QGS REQUIRED gsettings-qt) find_package(OpenSSL REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) find_package(X11 REQUIRED) pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/common/biodefines.cpp ../../src/common/commonfunc.cpp ../../src/common/configuration.cpp ../../src/common/global_utils.cpp ../../src/common/kyrsac.cpp ../../src/common/utils.cpp ../../src/lock-dialog/languagesetting.cpp unit_test_common.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/common/biodefines.h ../../src/common/commonfunc.h ../../src/common/configuration.h ../../src/common/global_utils.h ../../src/common/kyrsac.h ../../src/common/utils.h ../../src/lock-dialog/languagesetting.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ${GIOUNIX2_INCLUDE_DIRS} ${X11_INCLUDE_DIRS} ) qt5_add_resources(unit_test_common assets.qrc ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_common ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_common Qt5::Core Qt5::Gui Qt5::DBus Qt5::Widgets Qt5::Svg Qt5::X11Extras ${QGS_LIBRARIES} OpenSSL::Crypto ) # 链接 GTest 库 target_link_libraries(unit_test_common GTest::GTest GTest::Main Threads::Threads ${GIOUNIX2_LIBRARIES} ${X11_LIBRARIES} ) ukui-screensaver/tests/unit_test_common/main.cpp0000664000175000017500000000156215172041035021155 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_common/unit_test_common.cpp0000664000175000017500000002036215172041035023616 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "../../src/common/definetypes.h" #include "../../src/common/biodefines.h" #include "../../src/common/commonfunc.h" #include "../../src/common/configuration.h" #include "../../src/common/global_utils.h" #include "../../src/common/kyrsac.h" #include #include #include #include #include #include #include #include #include "../../src/common/kyrsac.h" #include "../../src/common/utils.h" #include "../../src/lock-dialog/languagesetting.h" #include "stubext.h" using namespace stub_ext; // 辅助函数,用于初始化OpenSSL库 void InitOpenSSL() { OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); } // 辅助函数,用于清理OpenSSL库 void CleanupOpenSSL() { EVP_cleanup(); ERR_free_strings(); } class CommonTest : public testing::Test { protected: void SetUp() override { InitOpenSSL(); } void TearDown() override { CleanupOpenSSL(); } KyRSAC rsac; }; TEST_F(CommonTest, scaledPixmap) { setCursorCenter(); QPixmap pixmap = QIcon::fromTheme("view-refresh-symbolic").pixmap(48, 48); QPixmap newPixmap = scaledPixmap(pixmap); ASSERT_EQ(newPixmap.isNull(), false); } TEST_F(CommonTest, PixmapToRound) { QPixmap pixmap = QIcon::fromTheme("view-refresh-symbolic").pixmap(48, 48); QPixmap newPixmap = PixmapToRound(pixmap, 4); ASSERT_EQ(newPixmap.isNull(), false); } TEST_F(CommonTest, PixmapToRound2) { QPixmap pixmap = QIcon::fromTheme("view-refresh-symbolic").pixmap(48, 48); QPixmap newPixmap = PixmapToRound(pixmap, 0, 0, 0, 0); ASSERT_EQ(newPixmap.isNull(), false); } TEST_F(CommonTest, loadSvg) { QPixmap iconPixmap = loadSvg(":/image/assets/show-password.png", "white", 16); ASSERT_EQ(iconPixmap.isNull(), false); } TEST_F(CommonTest, drawSymbolicColoredPixmap) { QPixmap retryIcon = QIcon::fromTheme("view-refresh-symbolic").pixmap(48, 48); ASSERT_EQ(drawSymbolicColoredPixmap(retryIcon, "white").isNull(), false); ASSERT_EQ(getLoadingIcon(16).width(), 16); } TEST_F(CommonTest, scaleBlurPixmap) { QPixmap *pixmap = new QPixmap(scaleBlurPixmap(16, 16, "/usr/share/background/house.png")); ASSERT_EQ(pixmap->width(), 16); } TEST_F(CommonTest, blurPixmap) { QPixmap pixmap = QIcon::fromTheme("view-refresh-symbolic").pixmap(48, 48); QPixmap newpixmap = blurPixmap(pixmap); ASSERT_EQ(newpixmap.width(), 16); } TEST_F(CommonTest, getDeviceTypeTr) { getDeviceTypeTr(BioT_FingerVein); getDeviceTypeTr(BioT_Iris); getDeviceTypeTr(BioT_Face); getDeviceTypeTr(BioT_VoicePrint); getDeviceTypeTr(UniT_General_Ukey); getDeviceTypeTr(UniT_Remote); getDeviceTypeTr(-1); ASSERT_EQ(getDeviceTypeTr(BioT_FingerPrint).isEmpty(), false); } TEST_F(CommonTest, getValue) { if (Configuration::instance()) { QString color = Configuration::instance()->getValue("background-color").toString(); ASSERT_EQ(color.isEmpty(), false); } } TEST_F(CommonTest, getCurrentUser) { if (Configuration::instance()) { Configuration::instance()->getCurrentUser(getenv("USER")); } } TEST_F(CommonTest, setValue) { if (Configuration::instance()) { QString color = Configuration::instance()->getValue("backgroundPath").toString(); QString oldvalue = Configuration::instance()->getUserConfig("backgroundPath").toString(); Configuration::instance()->setValue("backgroundPath", oldvalue); ASSERT_EQ(oldvalue.isEmpty(), false); } } TEST_F(CommonTest, hasValue) { if (Configuration::instance()) { bool value = Configuration::instance()->hasValue("backgroundPath"); ASSERT_EQ(value, false); } } TEST_F(CommonTest, getIs990) { if (Configuration::instance()) { bool value = Configuration::instance()->getIs990(); ASSERT_EQ(value, false); } } TEST_F(CommonTest, getLastNumLock) { if (Configuration::instance()) { bool value = Configuration::instance()->getLastNumLock(); Configuration::instance()->saveLastNumLock(value); ASSERT_EQ(value, false); } } TEST_F(CommonTest, getRootBackgroundOption) { if (Configuration::instance()) { int value = Configuration::instance()->getRootBackgroundOption(getenv("USER")); ASSERT_EQ(value, 1); } } TEST_F(CommonTest, getDefaultBackgroundName) { if (Configuration::instance()) { QString background = Configuration::instance()->getDefaultBackgroundName(); ASSERT_EQ(background.isEmpty(), false); } } TEST_F(CommonTest, onLanguageChanged) { if (LanguageSetting::instance()) { LanguageSetting::instance()->onLanguageChanged("zh_HK.UTF-8"); LanguageSetting::instance()->onLanguageChanged("zh_CN.UTF-8"); } } TEST_F(CommonTest, isCurUserSelf) { ASSERT_EQ(isCurUserSelf(getenv("USER")), true); ASSERT_EQ(isCurUserSelf("lightdm"), false); ASSERT_EQ(isCurUserSelf("test"), false); } TEST_F(CommonTest, checkCapsLockState) { checkIslivecd(); KillFocusOfKydroid(); ASSERT_EQ(checkCapsLockState(), false); } TEST_F(CommonTest, getDefaultFontSize) { ASSERT_EQ(getDefaultFontSize(), 14.0); } TEST_F(CommonTest, getUserFontSize) { ASSERT_EQ(getUserFontSize(getenv("USER")), 14.0); } TEST_F(CommonTest, getUserThemeColor) { ASSERT_EQ(getUserThemeColor(getenv("USER")), "test"); } // 测试密钥生成并保存到文件 TEST_F(CommonTest, GenerateKeyPairToFile) { QString priKeyFile = "test_private.pem"; QString pubKeyFile = "test_public.pem"; rsac.genKeyPair(priKeyFile, pubKeyFile, 1024); // 这里可以添加额外的检查来验证文件是否成功生成且内容有效 // 例如,打开文件并读取内容,检查是否为有效的PEM格式密钥 // 但由于这是一个简单的测试示例,我们省略了这些步骤 } // 测试密钥生成并保存到QByteArray TEST_F(CommonTest, GenerateKeyPairToByteArray) { QByteArray privateKey; QByteArray publicKey; rsac.genKeyPair(privateKey, publicKey, 1024); // 检查生成的密钥是否非空 EXPECT_FALSE(privateKey.isEmpty()); EXPECT_FALSE(publicKey.isEmpty()); // 这里可以添加额外的检查来验证密钥的有效性 // 例如,使用OpenSSL函数解析生成的密钥并检查其属性 // 但由于这是一个简单的测试示例,我们省略了这些步骤 } // 测试加密和解密 TEST_F(CommonTest, EncryptDecrypt) { QByteArray privateKey; QByteArray publicKey; rsac.genKeyPair(privateKey, publicKey, 1024); QByteArray plaintext = "Hello, RSA!"; QByteArray ciphertext; QByteArray decryptedtext; bool encryptResult = rsac.encryptData(plaintext, ciphertext, publicKey); EXPECT_TRUE(encryptResult); bool decryptResult = rsac.decryptData(ciphertext, decryptedtext, privateKey); EXPECT_TRUE(decryptResult); EXPECT_EQ(plaintext, decryptedtext); } // 测试签名和验签 TEST_F(CommonTest, SignVerify) { QByteArray privateKey; QByteArray publicKey; rsac.genKeyPair(privateKey, publicKey, 1024); QByteArray message = "This is a test message."; QByteArray signature; bool signResult = rsac.signData(message, signature, privateKey); EXPECT_TRUE(signResult); bool verifyResult = rsac.verifyData(message, signature, publicKey); EXPECT_TRUE(verifyResult); // 尝试使用错误的消息进行验签,应该失败 bool verifyWithWrongMessageResult = rsac.verifyData("Wrong message.", signature, publicKey); EXPECT_FALSE(verifyWithWrongMessageResult); } ukui-screensaver/tests/unit_test_screensaver/0000775000175000017500000000000015172041106020550 5ustar fengfengukui-screensaver/tests/unit_test_screensaver/unit_test_screensaver.cpp0000664000175000017500000001340515172041035025676 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/ukccplugins/sessiondbus/screensaverinterface.h" #include "../../src/lock-backend/lightdmhelper.h" #include #include "stubext.h" using namespace stub_ext; class ScreensaverInterfaceTest : public testing::Test { protected: static void SetUpTestSuite() { StubExt st; st.set_lamda(&LightDMHelper::connectToDaemonSync, []() { return true; }); st.set_lamda(&isGreeterMode, []() { return true; }); m_pDbusUpperInterfaceDbus = new DbusUpperInterface(); m_pDbusUpperInterfaceDbus->init(); m_pScreenSaverInterface = new ScreensaverInterface(m_pDbusUpperInterfaceDbus, m_pDbusUpperInterfaceDbus); } static void TearDownTestSuite() { if (m_pScreenSaverInterface) { delete m_pScreenSaverInterface; m_pScreenSaverInterface = nullptr; } delete m_pDbusUpperInterfaceDbus; m_pDbusUpperInterfaceDbus = nullptr; } static ScreensaverInterface *m_pScreenSaverInterface; static DbusUpperInterface *m_pDbusUpperInterfaceDbus; }; DbusUpperInterface *ScreensaverInterfaceTest::m_pDbusUpperInterfaceDbus = nullptr; ScreensaverInterface *ScreensaverInterfaceTest::m_pScreenSaverInterface = nullptr; TEST_F(ScreensaverInterfaceTest, setPreentryTime) { int oldValue = m_pScreenSaverInterface->property("preentryTime").toInt(); m_pScreenSaverInterface->setPreentryTime(10); int newValue = m_pScreenSaverInterface->property("preentryTime").toInt(); ASSERT_EQ(newValue, 10); m_pScreenSaverInterface->setPreentryTime(oldValue); } TEST_F(ScreensaverInterfaceTest, setScreensaverType) { QGSettings gsetting("org.ukui.screensaver"); std::string oldValue = m_pScreenSaverInterface->property("screensaverType").toString().toStdString(); m_pScreenSaverInterface->setScreensaverType("ukui"); std::string newValue = gsetting.get("mode").toString().toStdString(); ASSERT_STREQ(newValue.c_str(), "default-ukui"); m_pScreenSaverInterface->setScreensaverType(oldValue.c_str()); } TEST_F(ScreensaverInterfaceTest, setShowBreakTimeUkuiCustom) { bool oldValue = m_pScreenSaverInterface->property("showBreakTimeCustom").toBool(); m_pScreenSaverInterface->setShowBreakTimeCustom(true); bool newValue = m_pScreenSaverInterface->property("showBreakTimeCustom").toBool(); ASSERT_EQ(newValue, true); m_pScreenSaverInterface->setShowBreakTimeCustom(oldValue); } TEST_F(ScreensaverInterfaceTest, setShowBreakTimeUkui) { bool oldValue = m_pScreenSaverInterface->property("showBreakTimeUkui").toBool(); m_pScreenSaverInterface->setShowBreakTimeUkui(false); bool newValue = m_pScreenSaverInterface->property("showBreakTimeUkui").toBool(); ASSERT_EQ(newValue, false); m_pScreenSaverInterface->setShowBreakTimeUkui(oldValue); } TEST_F(ScreensaverInterfaceTest, setScreenLockEnabled) { bool oldValue = m_pScreenSaverInterface->property("screenLockEnabled").toBool(); m_pScreenSaverInterface->setScreenLockEnabled(false); bool newValue = m_pScreenSaverInterface->property("screenLockEnabled").toBool(); ASSERT_EQ(newValue, false); m_pScreenSaverInterface->setScreenLockEnabled(oldValue); } TEST_F(ScreensaverInterfaceTest, setCustomPath) { std::string oldValue = m_pScreenSaverInterface->property("customPath").toString().toStdString(); m_pScreenSaverInterface->setCustomPath("/test/path"); std::string newValue = m_pScreenSaverInterface->property("customPath").toString().toStdString(); ASSERT_STREQ(newValue.c_str(), "/test/path"); m_pScreenSaverInterface->setCustomPath(oldValue.c_str()); } TEST_F(ScreensaverInterfaceTest, setSwitchRandom) { bool oldValue = m_pScreenSaverInterface->property("switchRandom").toBool(); m_pScreenSaverInterface->setSwitchRandom(false); bool newValue = m_pScreenSaverInterface->property("switchRandom").toBool(); ASSERT_EQ(newValue, false); m_pScreenSaverInterface->setSwitchRandom(oldValue); } TEST_F(ScreensaverInterfaceTest, setCycleTime) { int oldValue = m_pScreenSaverInterface->property("cycleTime").toInt(); m_pScreenSaverInterface->setCycleTime(60); int newValue = m_pScreenSaverInterface->property("cycleTime").toInt(); ASSERT_EQ(newValue, 60); m_pScreenSaverInterface->setCycleTime(oldValue); } TEST_F(ScreensaverInterfaceTest, setCustomText) { std::string oldValue = m_pScreenSaverInterface->property("customText").toString().toStdString(); m_pScreenSaverInterface->setCustomText("test custom text"); std::string newValue = m_pScreenSaverInterface->property("customText").toString().toStdString(); ASSERT_STREQ(newValue.c_str(), "test custom text"); m_pScreenSaverInterface->setCustomText(oldValue.c_str()); } TEST_F(ScreensaverInterfaceTest, setCustomTextCentered) { bool oldValue = m_pScreenSaverInterface->property("customTextCentered").toBool(); m_pScreenSaverInterface->setCustomTextCentered(false); bool newValue = m_pScreenSaverInterface->property("customTextCentered").toBool(); ASSERT_EQ(newValue, false); m_pScreenSaverInterface->setCustomTextCentered(oldValue); } ukui-screensaver/tests/unit_test_screensaver/CMakeLists.txt0000664000175000017500000001225115172041106023311 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core DBus Network Test REQUIRED) find_package(OpenSSL REQUIRED) find_package(PkgConfig REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) pkg_check_modules(QGS REQUIRED gsettings-qt) pkg_check_modules(LIGHTDM-QT5-3 REQUIRED liblightdm-qt5-3) pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd) # 查找pam动态库全路径并缓存到PAM_LIBRARIES变量 find_library(PAM_LIBRARIES pam) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/authpamthread.cpp ../../src/lock-backend/pamauthenticate.cpp ../../src/lock-backend/lightdmhelper.cpp ../../src/lock-backend/dbusupperinterface.cpp ../../src/lock-backend/gsettingshelper.cpp ../../src/lock-backend/bioauthenticate.cpp ../../src/lock-backend/switchuserutils.cpp ../../src/dbusifs/accountshelper.cpp ../../src/userinfo.cpp ../../src/common/global_utils.cpp ../../src/common/configuration.cpp ../../src/common/kyrsac.cpp ../../src/dbusifs/giodbus.cpp ../../src/dbusifs/uniauthservice.cpp ../../src/lock-backend/securityuser.cpp ../../src/dbusifs/freedesktophelper.cpp ../../src/dbusifs/login1helper.cpp ../../src/dbusifs/usdhelper.cpp ../../src/dbusifs/upowerhelper.cpp ../../src/dbusifs/sessionhelper.cpp ../../src/dbusifs/dbusservermanager.cpp ../../src/dbusifs/systemupgradehelper.cpp ../../src/lock-backend/sessionwatcher.cpp ../../src/dbusifs/kglobalaccelhelper.cpp ../../src/dbusifs/libinputswitchevent.cpp ../../src/lock-backend/personalizeddata.cpp ../../src/dbusifs/biometrichelper.cpp ../../src/dbusifs/device.cpp ../../src/dbusifs/machinemodel.cpp ../../src/dbusifs/enginedevice.cpp ../../src/common/biodefines.cpp ../../src/QtSingleApplication/qtlocalpeer.cpp ../kt-test-utils/cpp-stub-ext/stub-shadow.cpp ../../src/ukccplugins/sessiondbus/screensaverinterface.cpp unit_test_screensaver.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-backend/authpamthread.h ../../src/lock-backend/pamauthenticate.h ../../src/lock-backend/lightdmhelper.h ../../src/lock-backend/dbusupperinterface.h ../../src/lock-backend/gsettingshelper.h ../../src/lock-backend/bioauthenticate.h ../../src/lock-backend/switchuserutils.h ../../src/common/definetypes.h ../../src/userinfo.h ../../src/common/global_utils.h ../../src/common/configuration.h ../../src/common/kyrsac.h ../../src/dbusifs/giodbus.h ../../src/dbusifs/accountshelper.h ../../src/common/configuration.h ../../src/dbusifs/uniauthservice.h ../../src/lock-backend/securityuser.h ../../src/dbusifs/freedesktophelper.h ../../src/dbusifs/login1helper.h ../../src/dbusifs/usdhelper.h ../../src/dbusifs/upowerhelper.h ../../src/dbusifs/dbusservermanager.h ../../src/dbusifs/sessionhelper.h ../../src/dbusifs/systemupgradehelper.h ../../src/lock-backend/sessionwatcher.h ../../src/dbusifs/kglobalaccelhelper.h ../../src/dbusifs/libinputswitchevent.h ../../src/lock-backend/personalizeddata.h ../../src/dbusifs/biometrichelper.h ../../src/dbusifs/device.h ../../src/dbusifs/enginedevice.h ../../src/dbusifs/machinemodel.h ../../src/common/biodefines.h ../kt-test-utils/cpp-stub-ext/stubext.h ../kt-test-utils/cpp-stub-ext/stub-shadow.h ../../src/QtSingleApplication/qtlocalpeer.h ../../src/ukccplugins/sessiondbus/screensaverinterface.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ${Qt5Test_INCLUDE_DIRS} ${LIGHTDM-QT5-3_INCLUDE_DIRS} ${LIBSYSTEMD_INCLUDE_DIRS} ${GIOUNIX2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}../../src/QtSingleApplication/ ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_screensaver ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_screensaver Qt5::Core Qt5::DBus Qt5::Test Qt5::Network ${QGS_LIBRARIES} ${PAM_LIBRARIES} ${LIGHTDM-QT5-3_LIBRARIES} ${LIBSYSTEMD_LIBRARIES} OpenSSL::Crypto -lukuiinputgatherclient ${GIOUNIX2_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_screensaver GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_screensaver/main.cpp0000664000175000017500000000146215172041035022204 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_auth_pam/0000775000175000017500000000000015172041106020026 5ustar fengfengukui-screensaver/tests/unit_test_auth_pam/unit_test_auth_pam.cpp0000664000175000017500000003271715172041035024441 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/accountshelper.h" #include "../../src/lock-backend/authpamthread.h" #include "../../src/lock-backend/pamauthenticate.h" #include "../../src/lock-backend/lightdmhelper.h" class AuthPamThreadTest : public ::testing::Test { protected: static void SetUpTestSuite() { m_pAuthPamThreadDbus = new AuthPamThread(); } static void TearDownTestSuite() { delete m_pAuthPamThreadDbus; m_pAuthPamThreadDbus = nullptr; } static AuthPamThread *m_pAuthPamThreadDbus; // 辅助函数,模拟文件描述符操作 int createMockPipe() { int fds[2]; if (pipe(fds) == -1) { return -1; } return fds[0]; } // 辅助函数,模拟写入数据到文件描述符 void writeToFd(int fd, const char *data) { write(fd, data, strnlen(data, 1024)); } // 辅助函数,从文件描述符读取数据 std::string readFromFd(int fd) { char buffer[1024]; ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1); if (bytesRead > 0) { buffer[bytesRead] = '\0'; return buffer; } return ""; } // 辅助函数,模拟 pam_start 的返回结果 int mockPamStart(pam_handle_t **pamh, const char *service, const char *user, const struct pam_conv *conv) { *pamh = reinterpret_cast(1); return PAM_SUCCESS; } // 辅助函数,模拟 pam_authenticate 的返回结果 int mockPamAuthenticate(pam_handle_t *pamh, int flags) { return PAM_SUCCESS; } // 辅助函数,模拟 pam_acct_mgmt 的返回结果 int mockPamAcctMgmt(pam_handle_t *pamh, int flags) { return PAM_SUCCESS; } // 辅助函数,模拟 pam_get_item 的返回结果 int mockPamGetItem(pam_handle_t *pamh, int item_type, const void **item) { *item = nullptr; return PAM_SUCCESS; } // 辅助函数,模拟 pam_strerror 的返回结果 const char *mockPamStrerror(pam_handle_t *pamh, int pam_err) { return "Mock Error"; } // 辅助函数,模拟 pam_end 的行为 int mockPamEnd(pam_handle_t *pamh, int pam_status) { return PAM_SUCCESS; } }; AuthPamThread *AuthPamThreadTest::m_pAuthPamThreadDbus = nullptr; // 测试 _authenticate 函数 TEST_F(AuthPamThreadTest, Authenticate) { m_pAuthPamThreadDbus->m_strUserName = getenv("USER"); qDebug() << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"; // 模拟 pam 函数 auto originalPamStart = pam_start; auto originalPamAuthenticate = pam_authenticate; auto originalPamAcctMgmt = pam_acct_mgmt; auto originalPamGetItem = pam_get_item; auto originalPamStrerror = pam_strerror; auto originalPamEnd = pam_end; // pam_start = mockPamStart; // pam_authenticate = mockPamAuthenticate; // pam_acct_mgmt = mockPamAcctMgmt; // pam_get_item = mockPamGetItem; // pam_strerror = mockPamStrerror; // pam_end = mockPamEnd; qDebug() << "??????????????????????????????????????"; // m_pAuthPamThreadDbus->_authenticate(m_pAuthPamThreadDbus->m_strUserName.toLocal8Bit().data()); // 恢复原始函数 // pam_start = originalPamStart; // pam_authenticate = originalPamAuthenticate; // pam_acct_mgmt = originalPamAcctMgmt; // pam_get_item = originalPamGetItem; // pam_strerror = originalPamStrerror; // pam_end = originalPamEnd; } // 测试 startAuthPam 函数 TEST_F(AuthPamThreadTest, StartAuthPam) { qDebug() << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"; int fdRead = createMockPipe(); int fdWrite = createMockPipe(); m_pAuthPamThreadDbus->startAuthPam(fdRead, fdWrite, getenv("USER")); EXPECT_TRUE(m_pAuthPamThreadDbus->isRunning()); close(fdRead); close(fdWrite); } // 测试 run 函数 TEST_F(AuthPamThreadTest, Run) { int fdRead = createMockPipe(); int fdWrite = createMockPipe(); m_pAuthPamThreadDbus->m_fdRead = fdRead; m_pAuthPamThreadDbus->m_fdWrite = fdWrite; m_pAuthPamThreadDbus->m_strUserName = getenv("USER"); // m_pAuthPamThreadDbus->run(); close(fdRead); close(fdWrite); } // 测试 stopAuthPam 函数 TEST_F(AuthPamThreadTest, StopAuthPam) { m_pAuthPamThreadDbus->startAuthPam(0, 0, getenv("USER")); m_pAuthPamThreadDbus->stopAuthPam(); EXPECT_FALSE(m_pAuthPamThreadDbus->isRunning()); } // 测试 PamAuthenticate 类 class PamAuthenticateTest : public ::testing::Test { protected: static AuthPamThread *m_pAuthPamThreadDbus; QSharedPointer lightDMHelper; QSharedPointer pamAuthenticate; void SetUp() override { lightDMHelper = QSharedPointer( new LightDMHelper(QSharedPointer(new AccountsHelper(nullptr)), nullptr)); pamAuthenticate = QSharedPointer(new PamAuthenticate(lightDMHelper)); } void TearDown() override { pamAuthenticate.reset(); lightDMHelper.reset(); } // 辅助函数,模拟 QSocketNotifier 的 activated 信号 void simulateSocketNotifierActivated(QSocketNotifier *socketNotifier) { // Q_EMIT socketNotifier->activated(socketNotifier->socket()); } // 辅助函数,模拟管道操作 int createMockPipe() { int fds[2]; if (pipe(fds) == -1) { return -1; } return fds[0]; } // 辅助函数,模拟 AuthPamThread 的行为 class MockAuthPamThread : public AuthPamThread { public: }; // 辅助函数,模拟 LightDMHelper 的行为 class MockLightDMHelper : public LightDMHelper { public: MockLightDMHelper() : LightDMHelper(QSharedPointer(new AccountsHelper(nullptr)), nullptr, nullptr) { } }; }; // 测试 PamAuthenticate 构造函数 TEST_F(PamAuthenticateTest, Constructor) { // 检查信号和槽的连接 // EXPECT_TRUE(QObject::connect(lightDMHelper.get(), SIGNAL(showMessage(QString, // QLightDM::Greeter::MessageType)), // pamAuthenticate.get(), SLOT(onLDMShowMessage(QString, QLightDM::Greeter::MessageType))); // EXPECT_TRUE(QObject::connect(lightDMHelper.get(), SIGNAL(showPrompt(QString, QLightDM::Greeter::PromptType)), // pamAuthenticate.get(), SLOT(onLDMShowPrompt(QString, QLightDM::Greeter::PromptType))); // EXPECT_TRUE(QObject::connect(lightDMHelper.get(), SIGNAL(authenticationComplete()), // pamAuthenticate.get(), SIGNAL(authenticationComplete())); } // 测试 inAuthentication 函数 TEST_F(PamAuthenticateTest, InAuthentication) { // 测试 m_isOtherUser 为 false 时的情况 EXPECT_FALSE(pamAuthenticate->inAuthentication()); // 测试 m_isOtherUser 为 true 时的情况 pamAuthenticate->m_isOtherUser = true; EXPECT_FALSE(pamAuthenticate->inAuthentication()); } // 测试 isAuthenticated 函数 TEST_F(PamAuthenticateTest, IsAuthenticated) { // 测试 m_isOtherUser 为 false 时的情况 EXPECT_FALSE(pamAuthenticate->isAuthenticated()); // 测试 m_isOtherUser 为 true 时的情况 pamAuthenticate->m_isOtherUser = true; EXPECT_FALSE(pamAuthenticate->isAuthenticated()); } // 测试 authenticationUser 函数 TEST_F(PamAuthenticateTest, AuthenticationUser) { // 测试 m_isOtherUser 为 false 时的情况 EXPECT_EQ(pamAuthenticate->authenticationUser(), ""); // 测试 m_isOtherUser 为 true 时的情况 pamAuthenticate->m_isOtherUser = true; EXPECT_EQ(pamAuthenticate->authenticationUser(), ""); } // 测试 authenticate 函数 TEST_F(PamAuthenticateTest, Authenticate) { // 测试普通用户的情况 pamAuthenticate->authenticate("testuser"); EXPECT_EQ(pamAuthenticate->m_strUserName, "testuser"); EXPECT_NE(pamAuthenticate->m_threadAuthPam, nullptr); EXPECT_NE(pamAuthenticate->m_socketNotifier, nullptr); // 测试访客用户的情况 pamAuthenticate->authenticate("*guest"); // 测试 m_isOtherUser 为 true 的情况 pamAuthenticate->m_isOtherUser = true; pamAuthenticate->authenticate("otheruser"); } // 测试 respond 函数 TEST_F(PamAuthenticateTest, Respond) { // 测试正常响应的情况 pamAuthenticate->m_nPrompts = 1; pamAuthenticate->respond("response"); EXPECT_EQ(pamAuthenticate->m_responseList.size(), 1); // 测试 m_nPrompts 为 0 的情况 pamAuthenticate->m_nPrompts = 0; pamAuthenticate->m_isOtherUser = false; pamAuthenticate->respond("response"); EXPECT_EQ(pamAuthenticate->m_responseList.size(), 1); } // 测试 cancelAuthentication 函数 TEST_F(PamAuthenticateTest, CancelAuthentication) { // 模拟 AuthPamThread 和 QSocketNotifier pamAuthenticate->m_threadAuthPam = new MockAuthPamThread(); pamAuthenticate->m_socketNotifier = new QSocketNotifier(0, QSocketNotifier::Read); pamAuthenticate->m_isOtherUser = false; pamAuthenticate->cancelAuthentication(); EXPECT_EQ(pamAuthenticate->m_threadAuthPam, nullptr); EXPECT_EQ(pamAuthenticate->m_socketNotifier, nullptr); EXPECT_FALSE(pamAuthenticate->m_isAuthenticated); EXPECT_FALSE(pamAuthenticate->m_isInAuthentication); EXPECT_EQ(pamAuthenticate->m_messageList.size(), 0); EXPECT_EQ(pamAuthenticate->m_responseList.size(), 0); EXPECT_EQ(pamAuthenticate->m_nPrompts, 0); } // 测试 onLDMShowMessage 函数 TEST_F(PamAuthenticateTest, OnLDMShowMessage) { pamAuthenticate->onLDMShowMessage("test",QLightDM::Greeter::MessageTypeInfo); } // 测试 onLDMShowPrompt 函数 TEST_F(PamAuthenticateTest, OnLDMShowPrompt) { pamAuthenticate->onLDMShowPrompt("",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("password",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("BIOMETRIC_PAM",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t0,\n\t\"binaryStr\":\t\"2fa_otp\"\n}",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t1,\n\t\"binaryInt\":\t\"2\"\n}",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t2,\n\t\"binaryStr\":\t\"test\"\n}",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t3,\n\t\"binaryStr\":\t\"test\"\n}",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t4,\n\t\"binaryStr\":\t\"otpauth\"\n}",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t5,\n\t\"binaryStr\":\t\"输入动态口令 \"\n}",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t6,\n\t\"binaryStr\":\t\"输入动态口令 \"\n}",QLightDM::Greeter::PromptTypeQuestion); pamAuthenticate->onLDMShowPrompt("{\n\t\"type\":\t7,\n\t\"binaryStr\":\t\"输入动态口令 \"\n}",QLightDM::Greeter::PromptTypeQuestion); } // 测试 onSockRead 函数 TEST_F(PamAuthenticateTest, OnSockRead) { // 模拟管道 int fdToParent[2]; if (pipe(fdToParent) == -1) { FAIL() << "Failed to create pipe"; } pamAuthenticate->m_fdToParent[0] = fdToParent[0]; // 测试认证完成的情况 int authComplete = 1; int authRet = PAM_SUCCESS; write(pamAuthenticate->m_fdToParent[0], &authComplete, sizeof(authComplete)); write(pamAuthenticate->m_fdToParent[0], &authRet, sizeof(authRet)); QSignalSpy authenticationCompleteSpy(pamAuthenticate.get(), SIGNAL(authenticationComplete())); simulateSocketNotifierActivated(pamAuthenticate->m_socketNotifier); EXPECT_EQ(pamAuthenticate->m_isAuthenticated, true); EXPECT_EQ(authenticationCompleteSpy.count(), 1); // 测试消息接收的情况 authComplete = 0; int msgLength = 1; write(pamAuthenticate->m_fdToParent[0], &authComplete, sizeof(authComplete)); write(pamAuthenticate->m_fdToParent[0], &msgLength, sizeof(msgLength)); struct pam_message message; message.msg_style = PAM_PROMPT_ECHO_OFF; message.msg = "Enter password"; write(pamAuthenticate->m_fdToParent[0], &message.msg_style, sizeof(message.msg_style)); write(pamAuthenticate->m_fdToParent[0], message.msg, strnlen(message.msg, 1024) + 1); QSignalSpy showPromptSpy(pamAuthenticate.get(), SIGNAL(showPrompt(QString, PamAuth::PromptType))); simulateSocketNotifierActivated(pamAuthenticate->m_socketNotifier); EXPECT_EQ(showPromptSpy.count(), 1); close(fdToParent[0]); close(fdToParent[1]); } ukui-screensaver/tests/unit_test_auth_pam/CMakeLists.txt0000664000175000017500000000530515172041106022571 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus Test REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(QGS REQUIRED gsettings-qt) pkg_check_modules(LIGHTDM-QT5-3 REQUIRED liblightdm-qt5-3) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 查找pam动态库全路径并缓存到PAM_LIBRARIES变量 find_library(PAM_LIBRARIES pam) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/authpamthread.cpp ../../src/lock-backend/pamauthenticate.cpp ../../src/lock-backend/lightdmhelper.cpp ../../src/dbusifs/accountshelper.cpp ../../src/userinfo.cpp ../../src/common/global_utils.cpp ../../src/dbusifs/uniauthservice.cpp ../../src/lock-backend/securityuser.cpp ../../src/dbusifs/freedesktophelper.cpp unit_test_auth_pam.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-backend/authpamthread.h ../../src/lock-backend/pamauthenticate.h ../../src/lock-backend/lightdmhelper.h ../../src/common/definetypes.h ../../src/userinfo.h ../../src/common/global_utils.h ../../src/dbusifs/accountshelper.h ../../src/common/configuration.h ../../src/dbusifs/uniauthservice.h ../../src/lock-backend/securityuser.h ../../src/dbusifs/freedesktophelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ${Qt5Test_INCLUDE_DIRS} ${LIGHTDM-QT5-3_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_auth_pam ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_auth_pam Qt5::Core Qt5::Gui Qt5::DBus Qt5::Test ${QGS_LIBRARIES} ${PAM_LIBRARIES} ${LIGHTDM-QT5-3_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_auth_pam GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_auth_pam/main.cpp0000664000175000017500000000156215172041035021463 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_usd_helper/0000775000175000017500000000000015172041106020362 5ustar fengfengukui-screensaver/tests/unit_test_usd_helper/CMakeLists.txt0000664000175000017500000000306615172041106023127 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/usdhelper.cpp unit_test_usd_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/dbusifs/usdhelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_usd_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_usd_helper Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_usd_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_usd_helper/main.cpp0000664000175000017500000000156215172041035022017 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_usd_helper/unit_test_usd_helper.cpp0000664000175000017500000000247015172041035025322 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/usdhelper.h" #include "stubext.h" using namespace stub_ext; class UsdHelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pUsdHelperDbus = new UsdHelper(); } static void TearDownTestSuite() { delete m_pUsdHelperDbus; m_pUsdHelperDbus = nullptr; } static UsdHelper *m_pUsdHelperDbus; }; UsdHelper *UsdHelperTest::m_pUsdHelperDbus = nullptr; TEST_F(UsdHelperTest, usdExternalDoAction) { bool value = m_pUsdHelperDbus->usdExternalDoAction(-1); ASSERT_EQ(value, false); } ukui-screensaver/tests/unit_test_dbus_interface/0000775000175000017500000000000015172041106021205 5ustar fengfengukui-screensaver/tests/unit_test_dbus_interface/unit_test_dbus_interface.cpp0000664000175000017500000005644215172041106026777 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include "../../src/lock-backend/authpamthread.h" #include "../../src/lock-backend/pamauthenticate.h" #include "../../src/lock-backend/lightdmhelper.h" #include "../../src/lock-backend/dbusupperinterface.h" #include "../../src/dbusifs/accountshelper.h" #include "stubext.h" using namespace stub_ext; class DbusUpperInterfaceTest : public testing::Test { protected: static void SetUpTestSuite() { m_pAccountsHelperDbus = QSharedPointer(new AccountsHelper()); StubExt st; st.set_lamda(&LightDMHelper::connectToDaemonSync, []() { return true; }); st.set_lamda(&isGreeterMode, []() { return true; }); m_pLightDMHelperDbus = new LightDMHelper(m_pAccountsHelperDbus, Configuration::instance()); m_pDbusUpperInterfaceDbus = new DbusUpperInterface(); m_pDbusUpperInterfaceDbus->init(); } static void TearDownTestSuite() { delete m_pDbusUpperInterfaceDbus; m_pDbusUpperInterfaceDbus = nullptr; delete m_pLightDMHelperDbus; m_pLightDMHelperDbus = nullptr; m_pAccountsHelperDbus.reset(); } static DbusUpperInterface *m_pDbusUpperInterfaceDbus; static LightDMHelper *m_pLightDMHelperDbus; static QSharedPointer m_pAccountsHelperDbus; }; DbusUpperInterface *DbusUpperInterfaceTest::m_pDbusUpperInterfaceDbus = nullptr; LightDMHelper *DbusUpperInterfaceTest::m_pLightDMHelperDbus = nullptr; QSharedPointer DbusUpperInterfaceTest::m_pAccountsHelperDbus = nullptr; TEST_F(DbusUpperInterfaceTest, GetSlpState) { m_pDbusUpperInterfaceDbus->onNameLost("test"); bool value = m_pDbusUpperInterfaceDbus->GetSlpState(); m_pLightDMHelperDbus->getLoginUserCount(); m_pLightDMHelperDbus->findUserByUid(getuid()); ASSERT_EQ(value, false); } TEST_F(DbusUpperInterfaceTest, GetBlankState) { bool value = m_pDbusUpperInterfaceDbus->GetBlankState(); ASSERT_EQ(value, false); } TEST_F(DbusUpperInterfaceTest, GetLockState) { bool value = m_pDbusUpperInterfaceDbus->GetLockState(); ASSERT_EQ(value, false); } TEST_F(DbusUpperInterfaceTest, CheckAppVersion) { bool value = m_pDbusUpperInterfaceDbus->CheckAppVersion(); ASSERT_EQ(value, false); } TEST_F(DbusUpperInterfaceTest, checkScreenDialogRunning) { m_pDbusUpperInterfaceDbus->LockStartupMode(); m_pDbusUpperInterfaceDbus->SessionTools(); m_pDbusUpperInterfaceDbus->Lock(); m_pDbusUpperInterfaceDbus->SwitchUser(); m_pDbusUpperInterfaceDbus->AppBlockWindow("Suspend"); m_pDbusUpperInterfaceDbus->MultiUserBlockWindow("Reboot"); m_pDbusUpperInterfaceDbus->onShowBlankScreensaver(); m_pDbusUpperInterfaceDbus->ShowScreensaver(); m_pDbusUpperInterfaceDbus->LockScreensaver(); // m_pDbusUpperInterfaceDbus->Suspend(); bool value = m_pDbusUpperInterfaceDbus->checkScreenDialogRunning(); ASSERT_EQ(value, false); } TEST_F(DbusUpperInterfaceTest, GenerateBatteryArgsList) { QJsonArray value = m_pDbusUpperInterfaceDbus->GenerateBatteryArgsList(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(DbusUpperInterfaceTest, getSleepLockCheck) { QJsonArray value = m_pDbusUpperInterfaceDbus->getSleepLockCheck(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(DbusUpperInterfaceTest, getShutdownLockcheck) { QJsonArray value = m_pDbusUpperInterfaceDbus->getShutdownLockcheck(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(DbusUpperInterfaceTest, getSaverTheme) { QJsonArray value = m_pDbusUpperInterfaceDbus->getSaverTheme(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(DbusUpperInterfaceTest, getBatteryIconName) { QString value = m_pDbusUpperInterfaceDbus->getBatteryIconName(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(DbusUpperInterfaceTest, getIsBattery) { bool value = m_pDbusUpperInterfaceDbus->getIsBattery(); ASSERT_EQ(value, false); } TEST_F(DbusUpperInterfaceTest, GetDefaultAuthUser) { StubExt st; st.set_lamda(&isGreeterMode, []() { return true; }); QString value = m_pDbusUpperInterfaceDbus->GetDefaultAuthUser(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(DbusUpperInterfaceTest, GetCurrentSession) { QString value = m_pDbusUpperInterfaceDbus->GetCurrentSession(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(DbusUpperInterfaceTest, GenerateSessionInfoList) { QJsonArray value = m_pDbusUpperInterfaceDbus->GenerateSessionInfoList(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(DbusUpperInterfaceTest, GetCurrentUser) { QString value = m_pDbusUpperInterfaceDbus->GetCurrentUser(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(DbusUpperInterfaceTest, getCurTabletMode) { bool value = m_pDbusUpperInterfaceDbus->getCurTabletMode(); ASSERT_EQ(value, false); } TEST_F(DbusUpperInterfaceTest, onPamShowMessage) { m_pDbusUpperInterfaceDbus->onPamShowMessage("",1); } TEST_F(DbusUpperInterfaceTest, onPamShowPrompt) { m_pDbusUpperInterfaceDbus->onPamShowPrompt("",1); } TEST_F(DbusUpperInterfaceTest, onPamShowBinaryPrompt) { m_pDbusUpperInterfaceDbus->onPamShowBinaryPrompt("",1); } TEST_F(DbusUpperInterfaceTest, onPamAuthCompleted) { m_pDbusUpperInterfaceDbus->onPamAuthCompleted(); } TEST_F(DbusUpperInterfaceTest, onBioAuthShowMessage) { m_pDbusUpperInterfaceDbus->onBioAuthShowMessage(""); } TEST_F(DbusUpperInterfaceTest, GenerateUserInfoList) { m_pDbusUpperInterfaceDbus->GenerateUserInfoList(); m_pDbusUpperInterfaceDbus->onUsersInfoChanged(); m_pDbusUpperInterfaceDbus->onLogin1ReqLock(); m_pDbusUpperInterfaceDbus->onLogin1ReqUnLock(); m_pDbusUpperInterfaceDbus->onLogin1PrepareForSleep(true); m_pDbusUpperInterfaceDbus->onLogin1PrepareForSleep(false); m_pDbusUpperInterfaceDbus->onLogin1SessionActiveChanged(true); m_pDbusUpperInterfaceDbus->onBlockInhibitedChanged("ShutDown"); m_pDbusUpperInterfaceDbus->delayLockScreen(); m_pDbusUpperInterfaceDbus->stopDelayLockScreen(); m_pDbusUpperInterfaceDbus->onBatteryStatusChanged("test"); m_pDbusUpperInterfaceDbus->onSessionIdleReceived(); m_pDbusUpperInterfaceDbus->onSessionIdleExit(); m_pDbusUpperInterfaceDbus->onLockScreenTimeout(); m_pDbusUpperInterfaceDbus->onBatteryChanged(QStringList()); m_pDbusUpperInterfaceDbus->onLidStateChanged(false); } TEST_F(DbusUpperInterfaceTest, SendUpdateInfoSig) { QJsonObject getjsonCmd; QJsonParseError jsonParseError; QJsonObject objRes; QVariant varValue; getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_LOCKSCREEN_CONF; getjsonCmd["Key"] = KEY_IDLE_DELAY; const QJsonDocument jsonDoc = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc.object(); varValue = objRes.value(KEY_IDLE_DELAY); m_pDbusUpperInterfaceDbus->onLockScreenConfigChanged(KEY_IDLE_DELAY, varValue.toDouble()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_SCREENSAVER_CONF; getjsonCmd["Key"] = KEY_SHOW_REST_TIME; const QJsonDocument jsonDoc1 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc1.object(); varValue = objRes.value(KEY_SHOW_REST_TIME); m_pDbusUpperInterfaceDbus->onScreenSaverConfigChanged(KEY_SHOW_REST_TIME, varValue.toDouble()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_POWERMANAGER_CONF; getjsonCmd["Key"] = KEY_SLEEP_COMPUTER_AC; const QJsonDocument jsonDoc2 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc2.object(); varValue = objRes.value(KEY_SLEEP_COMPUTER_AC); m_pDbusUpperInterfaceDbus->onPowerManagerConfigChanged(KEY_SLEEP_COMPUTER_AC, varValue.toDouble()); // getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_MATEBG_CONF; // getjsonCmd["Key"] = KEY_PICTURE_FILENAME; // const QJsonDocument jsonDoc3 = // QJsonDocument::fromJson(m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), // &jsonParseError); objRes = jsonDoc3.object(); varValue = objRes.value(KEY_PICTURE_FILENAME); // m_pDbusUpperInterfaceDbus->onMateBgConfigChanged(KEY_PICTURE_FILENAME, varValue.toString()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_UKCCPLUGINS_CONF; getjsonCmd["Key"] = KEY_HOUR_SYSTEM; const QJsonDocument jsonDoc4 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc4.object(); varValue = objRes.value(KEY_HOUR_SYSTEM); m_pDbusUpperInterfaceDbus->onUkccPluginsConfigChanged(KEY_HOUR_SYSTEM, varValue.toDouble()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_THEMESTYLE_CONF; getjsonCmd["Key"] = KEY_SYSTEM_FONT_SIZE; const QJsonDocument jsonDoc5 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc5.object(); varValue = objRes.value(KEY_SYSTEM_FONT_SIZE); m_pDbusUpperInterfaceDbus->onThemeStyleConfigChanged(KEY_SYSTEM_FONT_SIZE, varValue.toDouble()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_SESSION_CONF; getjsonCmd["Key"] = KEY_SESSION_IDLE; const QJsonDocument jsonDoc6 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc6.object(); varValue = objRes.value(KEY_SESSION_IDLE); m_pDbusUpperInterfaceDbus->onSessionConfigChanged(KEY_SESSION_IDLE, varValue.toDouble()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_KEYBOARD_CONF; getjsonCmd["Key"] = KEY_CAPSLOCK_STATUS; const QJsonDocument jsonDoc7 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc7.object(); varValue = objRes.value(KEY_CAPSLOCK_STATUS); m_pDbusUpperInterfaceDbus->onKeyboardConfigChanged(KEY_CAPSLOCK_STATUS, varValue.toBool()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIAKEYS_CONF; getjsonCmd["Key"] = KEY_AREA_SCREENSHOT; const QJsonDocument jsonDoc8 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc8.object(); varValue = objRes.value(KEY_AREA_SCREENSHOT); m_pDbusUpperInterfaceDbus->onUsdMediaKeysConfigChanged(KEY_AREA_SCREENSHOT, varValue.toString()); getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIA_STATE_KEYS_CONF; getjsonCmd["Key"] = KEY_RFKILL_STATE; const QJsonDocument jsonDoc9 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); objRes = jsonDoc9.object(); varValue = objRes.value(KEY_RFKILL_STATE); m_pDbusUpperInterfaceDbus->onUsdMediaStateKeysConfigChanged(KEY_RFKILL_STATE, varValue.toDouble()); } TEST_F(DbusUpperInterfaceTest, GetInformation) { // QList argumentList; // argumentList << QVariant::fromValue(QString(QJsonDocument(jsonCmd).toJson())); QList list{ /*LOCK_CMD_ID_GET_USERINFO_LIST, */ LOCK_CMD_ID_GET_DEFAULT_AUTH_USER, LOCK_CMD_ID_GET_CURRENT_USER, LOCK_CMD_ID_GET_SESSIONS_LIST, LOCK_CMD_ID_GET_CURRENT_SESSION, LOCK_CMD_ID_LOGIN1_IS_SESSION_ACTIVE, LOCK_CMD_ID_GET_AGREEMENT, LOCK_CMD_ID_GSETTINGS_GET_LOCKSCREEN_CONF, LOCK_CMD_ID_GSETTINGS_GET_SCREENSAVER_CONF, LOCK_CMD_ID_GSETTINGS_GET_POWERMANAGER_CONF, /*LOCK_CMD_ID_GSETTINGS_GET_MATEBG_CONF,*/ LOCK_CMD_ID_GSETTINGS_GET_UKCCPLUGINS_CONF, LOCK_CMD_ID_GSETTINGS_GET_THEMESTYLE_CONF, LOCK_CMD_ID_GSETTINGS_GET_SESSION_CONF, LOCK_CMD_ID_GSETTINGS_GET_KEYBOARD_CONF, LOCK_CMD_ID_PAMAUTH_IS_AUTHENTICATED, LOCK_CMD_ID_PAMAUTH_IS_INAUTHTICATION, LOCK_CMD_ID_PAMAUTH_GET_AUTHUSER, LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIAKEYS_CONF, LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIA_STATE_KEYS_CONF, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANHIBERNATE, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANPOWEROFF, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANREBOOT, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSUSPEND, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOGOUT, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOCKSCREEN, LOCK_CMD_ID_SYSTEM_UPGRADE_CHECK, LOCK_CMD_ID_UPOWER_BATTERY_STATUS, LOCK_CMD_ID_UPOWER_IS_BATTERY, LOCK_CMD_ID_UPOWER_BATTERY, LOCK_CMD_ID_SESSION_GET_SLEEP_LOCKCHECK, LOCK_CMD_ID_SESSION_GET_SHUTDOWN_LOCKCHECK, LOCK_CMD_ID_LOCK_SCREEN_GET_THEMES, LOCK_CMD_ID_BIOAUTH_GET_AVAILABLE_DEVICES, LOCK_CMD_ID_BIOAUTH_GET_DISABLED_DEVICES, LOCK_CMD_ID_BIOAUTH_GET_STATE, LOCK_CMD_ID_BIOAUTH_GET_CURDEVICE, LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_ID, LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_NAME, LOCK_CMD_ID_BIOAUTH_GET_DEFAULT_DEVICE, LOCK_CMD_ID_GET_PUBLIC_KEY, LOCK_CMD_ID_TABLET_MODE, -1 }; for (int i = 0; i < list.count(); i++) { QJsonObject jsonCmd; jsonCmd["CmdId"] = list.at(i); m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(jsonCmd).toJson())); } } TEST_F(DbusUpperInterfaceTest, SetInformation) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SET_USER; jsonCmd["Content"] = getenv("USER"); m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd).toJson()).toUtf8()); QJsonObject jsonCmd1; jsonCmd1["CmdId"] = LOCK_CMD_ID_SWITCH_TO_USER; jsonCmd1["Content"] = getenv("USER"); m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd1).toJson()).toUtf8()); QJsonObject jsonCmd2; jsonCmd2["CmdId"] = LOCK_CMD_ID_SET_SESSION; jsonCmd2["Content"] = "testSession"; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd2).toJson()).toUtf8()); QJsonObject jsonCmd3; jsonCmd3["CmdId"] = LOCK_CMD_ID_START_SESSION; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd3).toJson()).toUtf8()); QJsonObject getjsonCmd; getjsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_LOCKSCREEN_CONF; getjsonCmd["Key"] = KEY_IDLE_DELAY; QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd).toJson())).toUtf8(), &jsonParseError); QJsonObject objRes = jsonDoc.object(); QVariant varValue = objRes.value(KEY_IDLE_DELAY); QJsonObject jsonCmd4; jsonCmd4["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_LOCKSCREEN_CONF; jsonCmd4["Content"] = varValue.toDouble(); jsonCmd4["Key"] = KEY_IDLE_DELAY; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd4).toJson()).toUtf8()); QJsonObject getjsonCmd5; getjsonCmd5["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_SCREENSAVER_CONF; getjsonCmd5["Key"] = KEY_VIDEO_SIZE; QJsonParseError jsonParseError5; const QJsonDocument jsonDoc5 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd5).toJson())).toUtf8(), &jsonParseError5); QJsonObject objRes5 = jsonDoc5.object(); QVariant varValue5 = objRes5.value(KEY_VIDEO_SIZE); QJsonObject jsonCmd5; jsonCmd5["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_SCREENSAVER_CONF; jsonCmd5["Content"] = varValue5.toDouble(); jsonCmd5["Key"] = KEY_VIDEO_SIZE; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd5).toJson()).toUtf8()); QJsonObject getjsonCmd6; getjsonCmd6["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_POWERMANAGER_CONF; getjsonCmd6["Key"] = KEY_SLEEP_COMPUTER_AC; QJsonParseError jsonParseError6; const QJsonDocument jsonDoc6 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd6).toJson())).toUtf8(), &jsonParseError6); QJsonObject objRes6 = jsonDoc6.object(); QVariant varValue6 = objRes6.value(KEY_SLEEP_COMPUTER_AC); QJsonObject jsonCmd6; jsonCmd6["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_POWERMANAGER_CONF; jsonCmd6["Content"] = varValue6.toDouble(); jsonCmd6["Key"] = KEY_SLEEP_COMPUTER_AC; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd6).toJson()).toUtf8()); QJsonObject getjsonCmd7; getjsonCmd7["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_UKCCPLUGINS_CONF; getjsonCmd7["Key"] = KEY_HOUR_SYSTEM; QJsonParseError jsonParseError7; const QJsonDocument jsonDoc7 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd7).toJson())).toUtf8(), &jsonParseError7); QJsonObject objRes7 = jsonDoc7.object(); QVariant varValue7 = objRes7.value(KEY_HOUR_SYSTEM); QJsonObject jsonCmd7; jsonCmd7["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_UKCCPLUGINS_CONF; jsonCmd7["Content"] = varValue7.toDouble(); jsonCmd7["Key"] = KEY_HOUR_SYSTEM; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd7).toJson()).toUtf8()); QJsonObject getjsonCmd8; getjsonCmd8["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_THEMESTYLE_CONF; getjsonCmd8["Key"] = KEY_SYSTEM_FONT_SIZE; QJsonParseError jsonParseError8; const QJsonDocument jsonDoc8 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd8).toJson())).toUtf8(), &jsonParseError8); QJsonObject objRes8 = jsonDoc8.object(); QVariant varValue8 = objRes8.value(KEY_SYSTEM_FONT_SIZE); QJsonObject jsonCmd8; jsonCmd8["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_THEMESTYLE_CONF; jsonCmd8["Content"] = varValue8.toDouble(); jsonCmd8["Key"] = KEY_SYSTEM_FONT_SIZE; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd8).toJson()).toUtf8()); QJsonObject getjsonCmd9; getjsonCmd9["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_SESSION_CONF; getjsonCmd9["Key"] = KEY_SESSION_IDLE; QJsonParseError jsonParseError9; const QJsonDocument jsonDoc9 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd9).toJson())).toUtf8(), &jsonParseError9); QJsonObject objRes9 = jsonDoc9.object(); QVariant varValue9 = objRes9.value(KEY_SESSION_IDLE); QJsonObject jsonCmd9; jsonCmd9["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_SESSION_CONF; jsonCmd9["Content"] = varValue9.toDouble(); jsonCmd9["Key"] = KEY_SESSION_IDLE; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd9).toJson()).toUtf8()); QJsonObject jsonCmd10; jsonCmd10["CmdId"] = LOCK_CMD_ID_PAMAUTH_AUTHENTICATE; jsonCmd10["Content"] = getenv("USER"); m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd10).toJson()).toUtf8()); QJsonObject jsonCmd11; jsonCmd11["CmdId"] = LOCK_CMD_ID_PAMAUTH_RESPOND; jsonCmd11["Content"] = "test"; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd11).toJson()).toUtf8()); QJsonObject jsonCmd12; jsonCmd12["CmdId"] = LOCK_CMD_ID_PAMAUTH_AUTHENTICATE_CANCEL; // jsonCmd12["Content"] = "test"; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd12).toJson()).toUtf8()); QJsonObject jsonCmd13; jsonCmd13["CmdId"] = LOCK_CMD_ID_USD_MEDIAKEYS; jsonCmd13["Content"] = VOLUME_DOWN_KEY; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd13).toJson()).toUtf8()); QJsonObject jsonCmd14; jsonCmd14["CmdId"] = LOCK_CMD_ID_LOGIN1_SET_POWER_MANAGER; jsonCmd14["Content"] = "LockScreen"; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd14).toJson()).toUtf8()); QJsonObject jsonCmd15; jsonCmd15["CmdId"] = LOCK_CMD_ID_LOCK_STATE_CHANGED; jsonCmd15["Content"] = false; jsonCmd15["SessionTools"] = false; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd15).toJson()).toUtf8()); QJsonObject jsonCmd16; jsonCmd16["CmdId"] = LOCK_CMD_ID_BIOAUTH_STARTAUTH; jsonCmd16["UserId"] = 1000; jsonCmd16["DevId"] = 101; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd16).toJson()).toUtf8()); QJsonObject jsonCmd17; jsonCmd17["CmdId"] = LOCK_CMD_ID_BIOAUTH_STOPAUTH; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd17).toJson()).toUtf8()); QJsonObject jsonCmd18; jsonCmd18["CmdId"] = LOCK_CMD_ID_KWIN_BLOCK_SHORTCUT; jsonCmd18["Content"] = true; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd18).toJson()).toUtf8()); QJsonObject jsonCmd19; jsonCmd19["CmdId"] = LOCK_CMD_ID_KWIN_BLOCK_SHORTCUT; jsonCmd19["Content"] = false; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd19).toJson()).toUtf8()); QJsonObject getjsonCmd20; getjsonCmd20["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIA_STATE_KEYS_CONF; getjsonCmd20["Key"] = KEY_RFKILL_STATE; QJsonParseError jsonParseError20; const QJsonDocument jsonDoc20 = QJsonDocument::fromJson( m_pDbusUpperInterfaceDbus->GetInformation(QString(QJsonDocument(getjsonCmd20).toJson())).toUtf8(), &jsonParseError20); QJsonObject objRes20 = jsonDoc20.object(); QVariant varValue20 = objRes20.value(KEY_RFKILL_STATE); QJsonObject jsonCmd20; jsonCmd20["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_USD_MEDIA_STATE_KEYS_CONF; jsonCmd20["Content"] = varValue20.toDouble(); jsonCmd20["Key"] = KEY_RFKILL_STATE; m_pDbusUpperInterfaceDbus->SetInformation(QString(QJsonDocument(jsonCmd20).toJson()).toUtf8()); } ukui-screensaver/tests/unit_test_dbus_interface/CMakeLists.txt0000664000175000017500000001233015172041106023744 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus Network Test REQUIRED) find_package(OpenSSL REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) pkg_check_modules(QGS REQUIRED gsettings-qt) pkg_check_modules(LIGHTDM-QT5-3 REQUIRED liblightdm-qt5-3) pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 查找pam动态库全路径并缓存到PAM_LIBRARIES变量 find_library(PAM_LIBRARIES pam) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/authpamthread.cpp ../../src/lock-backend/pamauthenticate.cpp ../../src/lock-backend/lightdmhelper.cpp ../../src/lock-backend/dbusupperinterface.cpp ../../src/lock-backend/gsettingshelper.cpp ../../src/lock-backend/bioauthenticate.cpp ../../src/lock-backend/switchuserutils.cpp ../../src/dbusifs/accountshelper.cpp ../../src/userinfo.cpp ../../src/common/global_utils.cpp ../../src/common/configuration.cpp ../../src/common/kyrsac.cpp ../../src/dbusifs/giodbus.cpp ../../src/dbusifs/uniauthservice.cpp ../../src/lock-backend/securityuser.cpp ../../src/dbusifs/freedesktophelper.cpp ../../src/dbusifs/login1helper.cpp ../../src/dbusifs/usdhelper.cpp ../../src/dbusifs/upowerhelper.cpp ../../src/dbusifs/sessionhelper.cpp ../../src/dbusifs/dbusservermanager.cpp ../../src/dbusifs/systemupgradehelper.cpp ../../src/lock-backend/sessionwatcher.cpp ../../src/dbusifs/kglobalaccelhelper.cpp ../../src/dbusifs/libinputswitchevent.cpp ../../src/lock-backend/personalizeddata.cpp ../../src/dbusifs/biometrichelper.cpp ../../src/dbusifs/device.cpp ../../src/dbusifs/machinemodel.cpp ../../src/dbusifs/enginedevice.cpp ../../src/common/biodefines.cpp ../../src/QtSingleApplication/qtlocalpeer.cpp ../kt-test-utils/cpp-stub-ext/stub-shadow.cpp unit_test_dbus_interface.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-backend/authpamthread.h ../../src/lock-backend/pamauthenticate.h ../../src/lock-backend/lightdmhelper.h ../../src/lock-backend/dbusupperinterface.h ../../src/lock-backend/gsettingshelper.h ../../src/lock-backend/bioauthenticate.h ../../src/lock-backend/switchuserutils.h ../../src/common/definetypes.h ../../src/userinfo.h ../../src/common/global_utils.h ../../src/common/configuration.h ../../src/common/kyrsac.h ../../src/dbusifs/giodbus.h ../../src/dbusifs/accountshelper.h ../../src/common/configuration.h ../../src/dbusifs/uniauthservice.h ../../src/lock-backend/securityuser.h ../../src/dbusifs/freedesktophelper.h ../../src/dbusifs/login1helper.h ../../src/dbusifs/usdhelper.h ../../src/dbusifs/upowerhelper.h ../../src/dbusifs/dbusservermanager.h ../../src/dbusifs/sessionhelper.h ../../src/dbusifs/systemupgradehelper.h ../../src/lock-backend/sessionwatcher.h ../../src/dbusifs/kglobalaccelhelper.h ../../src/dbusifs/libinputswitchevent.h ../../src/lock-backend/personalizeddata.h ../../src/dbusifs/biometrichelper.h ../../src/dbusifs/device.h ../../src/dbusifs/enginedevice.h ../../src/dbusifs/machinemodel.h ../../src/common/biodefines.h ../kt-test-utils/cpp-stub-ext/stubext.h ../kt-test-utils/cpp-stub-ext/stub-shadow.h ../../src/QtSingleApplication/qtlocalpeer.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ${Qt5Test_INCLUDE_DIRS} ${LIGHTDM-QT5-3_INCLUDE_DIRS} ${LIBSYSTEMD_INCLUDE_DIRS} ${GIOUNIX2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}../../src/QtSingleApplication/ ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_dbus_interface ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_dbus_interface Qt5::Core Qt5::Gui Qt5::DBus Qt5::Test Qt5::Network ${QGS_LIBRARIES} ${PAM_LIBRARIES} ${LIGHTDM-QT5-3_LIBRARIES} ${LIBSYSTEMD_LIBRARIES} OpenSSL::Crypto -lukuiinputgatherclient ${GIOUNIX2_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_dbus_interface GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_dbus_interface/main.cpp0000664000175000017500000000156215172041035022642 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_backend_dbus/0000775000175000017500000000000015172041106020634 5ustar fengfengukui-screensaver/tests/unit_test_backend_dbus/CMakeLists.txt0000664000175000017500000000362015172041106023375 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-dialog/backenddbushelper.cpp ../../src/userinfo.cpp ../../src/agreementinfo.cpp ../../src/common/biodefines.cpp ../../src/common/global_utils.cpp ../../src/dbusifs/freedesktophelper.cpp unit_test_backend_dbus.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-dialog/backenddbushelper.h ../../src/common/definetypes.h ../../src/common/biodefines.h ../../src/userinfo.h ../../src/agreementinfo.h ../../src/common/global_utils.h ../../src/dbusifs/freedesktophelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_backend_dbus ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_backend_dbus Qt5::Core Qt5::Gui Qt5::DBus # 链接 GTest 库 target_link_libraries(unit_test_backend_dbus GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_backend_dbus/main.cpp0000664000175000017500000000156215172041035022271 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_backend_dbus/unit_test_backend_dbus.cpp0000664000175000017500000003517415172041035026055 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/lock-dialog/backenddbushelper.h" #include "../../src/common/definetypes.h" #include "../../src/agreementinfo.h" #include "stubext.h" using namespace stub_ext; class BackendDbusHelperTest : public testing::Test { protected: static void SetUpTestSuite() { QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); QString sessionDbus = QString("%1%2").arg(QString(SS_DBUS_SERVICE)).arg(displayNum); m_pBackendDbusHelperDbus = new BackendDbusHelper(sessionDbus, SS_DBUS_PATH, QDBusConnection::sessionBus()); if (!m_pBackendDbusHelperDbus->isValid()) { delete m_pBackendDbusHelperDbus; m_pBackendDbusHelperDbus = new BackendDbusHelper(SS_DBUS_SERVICE, SS_DBUS_PATH, QDBusConnection::sessionBus()); } } static void TearDownTestSuite() { delete m_pBackendDbusHelperDbus; m_pBackendDbusHelperDbus = nullptr; } static BackendDbusHelper *m_pBackendDbusHelperDbus; }; BackendDbusHelper *BackendDbusHelperTest::m_pBackendDbusHelperDbus = nullptr; TEST_F(BackendDbusHelperTest, getUsersInfo) { QList value = m_pBackendDbusHelperDbus->getUsersInfo(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BackendDbusHelperTest, getAgreementInfo) { AgreementInfoPtr value = m_pBackendDbusHelperDbus->getAgreementInfo(); // ASSERT_EQ(value, NULL); } TEST_F(BackendDbusHelperTest, getSessionsInfo) { QList value = m_pBackendDbusHelperDbus->getSessionsInfo(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BackendDbusHelperTest, getBatteryArgs) { QStringList value = m_pBackendDbusHelperDbus->getBatteryArgs(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BackendDbusHelperTest, getSleepLockcheck) { QStringList value = m_pBackendDbusHelperDbus->getSleepLockcheck(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BackendDbusHelperTest, isSessionActive) { bool value = m_pBackendDbusHelperDbus->isSessionActive(); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, switchToUser) { int value = m_pBackendDbusHelperDbus->switchToUser(getenv("USER")); ASSERT_EQ(value, 0); } TEST_F(BackendDbusHelperTest, setCurrentUser) { bool value = m_pBackendDbusHelperDbus->setCurrentUser(getenv("USER")); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, setPowerManager) { bool value = m_pBackendDbusHelperDbus->setPowerManager(false, "LockScreen"); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, sendPassword) { QByteArray decryptText; bool value = m_pBackendDbusHelperDbus->sendPassword(getenv("USER"), decryptText); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, getBatteryIconName) { QString value = m_pBackendDbusHelperDbus->getBatteryIconName(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, getIsBattery) { bool value = m_pBackendDbusHelperDbus->getIsBattery(); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, getCurTabletMode) { bool value = m_pBackendDbusHelperDbus->getCurTabletMode(); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, lockStateChanged) { bool value = m_pBackendDbusHelperDbus->lockStateChanged(false, false); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, pamIsInAuthentication) { m_pBackendDbusHelperDbus->startSession(); m_pBackendDbusHelperDbus->pamAuthenticate(getenv("USER")); m_pBackendDbusHelperDbus->bioStartAuth(getuid(), 32); m_pBackendDbusHelperDbus->bioStopAuth(); m_pBackendDbusHelperDbus->pamRespond(""); m_pBackendDbusHelperDbus->pamAuthenticateCancel(); bool value = m_pBackendDbusHelperDbus->pamIsInAuthentication(); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, pamIsAuthenticated) { bool value = m_pBackendDbusHelperDbus->pamIsAuthenticated(); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, pamAuthenticateUser) { QString value = m_pBackendDbusHelperDbus->pamAuthenticateUser(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, bioGetAvailableDevices) { QList value = m_pBackendDbusHelperDbus->bioGetAvailableDevices(101); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, bioGetDisabledDevices) { QList value = m_pBackendDbusHelperDbus->bioGetDisabledDevices(101); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, bioGetBioAuthState) { int value = m_pBackendDbusHelperDbus->bioGetBioAuthState(); ASSERT_EQ(value, 0); } TEST_F(BackendDbusHelperTest, bioGetCurBioInfo) { DeviceInfo bioValue1 = m_pBackendDbusHelperDbus->bioGetCurBioInfo(); DeviceInfo bioValue2 = m_pBackendDbusHelperDbus->bioFindDeviceById(getuid(), 101); DeviceInfo bioValue3 = m_pBackendDbusHelperDbus->bioFindDeviceByName(getuid(), "uru4000"); QString value = m_pBackendDbusHelperDbus->bioGetDefaultDevice(getuid(), getenv("USER")); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, getCurrentSession) { QString value = m_pBackendDbusHelperDbus->getCurrentSession(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, setCurrentSession) { bool value = m_pBackendDbusHelperDbus->setCurrentSession("testSession"); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, onUpdateInformation) { QList list{LOCK_CMD_ID_USERINFO_CHANGED, LOCK_CMD_ID_CURRENT_USER_CHANGED, LOCK_CMD_ID_CURRENT_SESSION_CHANGED, LOCK_CMD_ID_LOGIN1_REQ_LOCK, LOCK_CMD_ID_LOGIN1_REQ_UNLOCK, LOCK_CMD_ID_LOGIN1_PREPAREFORSLEEP, LOCK_CMD_ID_LOGIN1_SESSION_ACTIVE_CHANGED, LOCK_CMD_ID_GSETTINGS_LOCKSCREEN_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_SCREENSAVER_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_POWERMANAGER_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_MATEBG_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_UKCCPLUGINS_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_THEMESTYLE_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_SESSION_CONF_CHANGED, LOCK_CMD_ID_LOGIN1_BLOCKINHIBITED_CHANGED, LOCK_CMD_ID_GSETTINGS_KEYBOARD_CONF_CHANGED, LOCK_CMD_ID_PAMAUTH_SHOWMESSAGE, LOCK_CMD_ID_PAMAUTH_SHOWPROMPT, LOCK_CMD_ID_PAMAUTH_AUTH_COMPLETED, LOCK_CMD_ID_GSETTINGS_USD_MEDIAKEYS_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_USD_MEDIA_STATE_KEYS_CONF_CHANGED, LOCK_CMD_ID_UPOWER_BATTERY_STATUS, LOCK_CMD_ID_UPOWER_BATTERY, LOCK_CMD_ID_BIOAUTH_SERVICE_STATUS_CHANGED, LOCK_CMD_ID_BIOAUTH_DEVICE_CHANGED, LOCK_CMD_ID_BIOAUTH_SHOW_MESSAGE, LOCK_CMD_ID_BIOAUTH_AUTHSTATE_CHANGED, LOCK_CMD_ID_BIOAUTH_FRAME_DATA, LOCK_CMD_ID_BIOAUTH_COMPLETE, LOCK_CMD_ID_TABLET_MODE_CHANGED}; for (int i = 0; i < list.count(); i++) { QJsonObject retObj; retObj["CmdId"] = list.at(i); m_pBackendDbusHelperDbus->onUpdateInformation(QString(QJsonDocument(retObj).toJson())); } } TEST_F(BackendDbusHelperTest, usdExternalDoAction) { bool value = m_pBackendDbusHelperDbus->usdExternalDoAction(VOLUME_DOWN_KEY); m_pBackendDbusHelperDbus->usdExternalDoAction(VOLUME_UP_KEY); ASSERT_EQ(value, true); } TEST_F(BackendDbusHelperTest, getDefaultAuthUser) { QString value = m_pBackendDbusHelperDbus->getDefaultAuthUser(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, getCurrentUser) { QString value = m_pBackendDbusHelperDbus->getCurrentUser(); ASSERT_EQ(value.isEmpty(), true); } TEST_F(BackendDbusHelperTest, getScreenSaverConf) { bool value = m_pBackendDbusHelperDbus->getScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE).toBool(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, setScreenSaverConf) { bool oldvalue = m_pBackendDbusHelperDbus->getScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE).toBool(); bool value = m_pBackendDbusHelperDbus->setScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE, !oldvalue); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE, oldvalue); } TEST_F(BackendDbusHelperTest, getLockScreenConf) { bool value = m_pBackendDbusHelperDbus->getLockScreenConf(KEY_LOCK_ENABLED).toBool(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, setLockScreenConf) { bool oldvalue = m_pBackendDbusHelperDbus->getLockScreenConf(KEY_LOCK_ENABLED).toBool(); bool value = m_pBackendDbusHelperDbus->setLockScreenConf(KEY_LOCK_ENABLED, !oldvalue); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setLockScreenConf(KEY_LOCK_ENABLED, oldvalue); } TEST_F(BackendDbusHelperTest, getShutdownLockcheck) { QStringList value = m_pBackendDbusHelperDbus->getShutdownLockcheck(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BackendDbusHelperTest, getSaverThemes) { QStringList value = m_pBackendDbusHelperDbus->getSaverThemes(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BackendDbusHelperTest, getPowerManagerConf) { bool value = m_pBackendDbusHelperDbus->getPowerManagerConf(KEY_LOCK_BLANKSCREEN).toBool(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, setPowerManagerConf) { bool oldvalue = m_pBackendDbusHelperDbus->getPowerManagerConf(KEY_LOCK_BLANKSCREEN).toBool(); bool value = m_pBackendDbusHelperDbus->setPowerManagerConf(KEY_LOCK_BLANKSCREEN, !oldvalue); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setPowerManagerConf(KEY_LOCK_BLANKSCREEN, oldvalue); } TEST_F(BackendDbusHelperTest, getMateBgConf) { QString value = m_pBackendDbusHelperDbus->getMateBgConf(KEY_PICTURE_FILENAME).toString(); ASSERT_EQ(value.isEmpty(), false); } TEST_F(BackendDbusHelperTest, setMateBgConf) { QString oldvalue = m_pBackendDbusHelperDbus->getMateBgConf(KEY_PICTURE_FILENAME).toString(); return ; bool value = m_pBackendDbusHelperDbus->setMateBgConf("picture-filename", "sss"); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setMateBgConf("picture-filename", oldvalue); } TEST_F(BackendDbusHelperTest, getUkccPluginsConf) { QString value = m_pBackendDbusHelperDbus->getUkccPluginsConf(KEY_DATE).toString(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, setUkccPluginsConf) { QString oldvalue = m_pBackendDbusHelperDbus->getUkccPluginsConf(KEY_DATE).toString(); bool value = m_pBackendDbusHelperDbus->setUkccPluginsConf(KEY_DATE, "sss"); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setUkccPluginsConf(KEY_DATE, oldvalue); } TEST_F(BackendDbusHelperTest, getThemeStyleConf) { QString value = m_pBackendDbusHelperDbus->getThemeStyleConf(KEY_THEME_COLOR).toString(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, setThemeStyleConf) { QString oldvalue = m_pBackendDbusHelperDbus->getThemeStyleConf(KEY_THEME_COLOR).toString(); bool value = m_pBackendDbusHelperDbus->setThemeStyleConf(KEY_THEME_COLOR, "sss"); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setThemeStyleConf(KEY_THEME_COLOR, oldvalue); } TEST_F(BackendDbusHelperTest, getSessionConf) { int value = m_pBackendDbusHelperDbus->getSessionConf(KEY_SESSION_IDLE).toInt(); ASSERT_EQ(value, 60); } TEST_F(BackendDbusHelperTest, setSessionConf) { int oldvalue = m_pBackendDbusHelperDbus->getSessionConf(KEY_SESSION_IDLE).toInt(); bool value = m_pBackendDbusHelperDbus->setSessionConf(KEY_SESSION_IDLE, 30); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setSessionConf(KEY_SESSION_IDLE, oldvalue); } TEST_F(BackendDbusHelperTest, getKeyboardConf) { bool value = m_pBackendDbusHelperDbus->getKeyboardConf(KEY_CAPSLOCK_STATUS).toBool(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, setKeyboardConf) { bool oldvalue = m_pBackendDbusHelperDbus->getKeyboardConf(KEY_CAPSLOCK_STATUS).toBool(); bool value = m_pBackendDbusHelperDbus->setKeyboardConf(KEY_CAPSLOCK_STATUS, !oldvalue); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setKeyboardConf(KEY_CAPSLOCK_STATUS, oldvalue); } TEST_F(BackendDbusHelperTest, getUsdMediaStateKeys) { int value = m_pBackendDbusHelperDbus->getUsdMediaStateKeys(KEY_RFKILL_STATE).toInt(); ASSERT_EQ(value, 60); } TEST_F(BackendDbusHelperTest, setUsdMediaStateKeys) { int oldvalue = m_pBackendDbusHelperDbus->getUsdMediaStateKeys(KEY_RFKILL_STATE).toInt(); bool value = m_pBackendDbusHelperDbus->setUsdMediaStateKeys(KEY_RFKILL_STATE, 0); ASSERT_EQ(value, false); m_pBackendDbusHelperDbus->setUsdMediaStateKeys(KEY_RFKILL_STATE, oldvalue); } TEST_F(BackendDbusHelperTest, getUsdMediaKeys) { QString value = m_pBackendDbusHelperDbus->getUsdMediaKeys(KEY_WINDOW_SCREENSHOT).toString(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPowerManagerCanSuspend) { bool value = m_pBackendDbusHelperDbus->getPowerManagerCanSuspend(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPowerManagerCanReboot) { bool value = m_pBackendDbusHelperDbus->getPowerManagerCanReboot(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPowerManagerCanPowerOff) { bool value = m_pBackendDbusHelperDbus->getPowerManagerCanPowerOff(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPowerManagerCanSwitchUser) { bool value = m_pBackendDbusHelperDbus->getPowerManagerCanSwitchUser(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPowerManagerCanHibernate) { bool value = m_pBackendDbusHelperDbus->getPowerManagerCanHibernate(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPowerManagerCanLogout) { bool value = m_pBackendDbusHelperDbus->getPowerManagerCanLogout(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPowerManagerCanLockScreen) { bool value = m_pBackendDbusHelperDbus->getPowerManagerCanLockScreen(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, checkSystemUpgrade) { bool value = m_pBackendDbusHelperDbus->checkSystemUpgrade(); ASSERT_EQ(value, false); } TEST_F(BackendDbusHelperTest, getPublicEncrypt) { QString value = m_pBackendDbusHelperDbus->getPublicEncrypt(); ASSERT_EQ(value.isEmpty(), false); } ukui-screensaver/tests/unit_test_gsettings_helper/0000775000175000017500000000000015172041106021576 5ustar fengfengukui-screensaver/tests/unit_test_gsettings_helper/CMakeLists.txt0000664000175000017500000000330215172041106024334 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(QGS REQUIRED gsettings-qt) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/gsettingshelper.cpp unit_test_gsettings_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/lock-backend/gsettingshelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_gsettings_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_gsettings_helper Qt5::Core Qt5::Gui Qt5::DBus ${QGS_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_gsettings_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_gsettings_helper/unit_test_gsettings_helper.cpp0000664000175000017500000004214115172041035027751 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/lock-backend/gsettingshelper.h" #include "../../src/common/definetypes.h" #include "stubext.h" using namespace stub_ext; class GSettingsHelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pGSettingsHelperDbus = new GSettingsHelper(); } static void TearDownTestSuite() { delete m_pGSettingsHelperDbus; m_pGSettingsHelperDbus = nullptr; } static GSettingsHelper *m_pGSettingsHelperDbus; }; GSettingsHelper *GSettingsHelperTest::m_pGSettingsHelperDbus = nullptr; TEST_F(GSettingsHelperTest, GetScreenSaverConf) { QVariant result1 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_SHOW_REST_TIME); bool value1 = result1.toBool(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_SHOW_REST_TIME); QVariant result2 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_SHOW_CUSTOM_REST_TIME); bool value2 = result2.toBool(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_SHOW_CUSTOM_REST_TIME); QVariant result3 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_SHOW_UKUI_REST_TIME); bool value3 = result3.toBool(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_SHOW_UKUI_REST_TIME); QVariant result4 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_CYCLE_TIME); int value4 = result4.toInt(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_CYCLE_TIME); QVariant result5 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE); bool value5 = result5.toBool(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_AUTOMATIC_SWITCHING_ENABLE); QVariant result6 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_BACKGROUND_PATH); QString value6 = result6.toString(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_BACKGROUND_PATH); QVariant result7 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_MYTEXT); QString value7 = result7.toString(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_MYTEXT); QVariant result8 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_TEXT_IS_CENTER); bool value8 = result8.toBool(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_TEXT_IS_CENTER); QVariant result9 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_SHOW_MESSAGE_ENABLED); bool value9 = result9.toBool(); QVariant result10 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_MESSAGE_NUMBER); int value10 = result10.toInt(); QVariant result11 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_VIDEO_FORMAT); QString value11 = result11.toString(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_VIDEO_FORMAT); QVariant result12 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_VIDEO_PATH); QString value12 = result12.toString(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_VIDEO_PATH); QVariant result13 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_VIDEO_SIZE); int value13 = result13.toInt(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_VIDEO_SIZE); QVariant result14 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_VIDEO_WIDTH); int value14 = result14.toInt(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_VIDEO_WIDTH); QVariant result15 = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_VIDEO_HEIGHT); int value15 = result15.toInt(); m_pGSettingsHelperDbus->onScreenSaverConfigChanged(KEY_VIDEO_HEIGHT); ASSERT_EQ(value1, true); ASSERT_EQ(value2, true); ASSERT_EQ(value3, true); ASSERT_EQ(value4, 300); ASSERT_EQ(value5, false); ASSERT_EQ(value6.isEmpty(), false); ASSERT_EQ(value7.isEmpty(), true); ASSERT_EQ(value8, true); ASSERT_EQ(value9, false); ASSERT_EQ(value10, 0); ASSERT_EQ(value11.isEmpty(), false); ASSERT_EQ(value12.isEmpty(), false); ASSERT_EQ(value13, 100); ASSERT_EQ(value14, 1920); ASSERT_EQ(value15, 1080); } TEST_F(GSettingsHelperTest, SetScreenSaverConf) { QVariant result = m_pGSettingsHelperDbus->GetScreenSaverConf(KEY_SHOW_REST_TIME); bool oldValue = result.toBool(); bool value = m_pGSettingsHelperDbus->SetScreenSaverConf(KEY_SHOW_REST_TIME, true); m_pGSettingsHelperDbus->SetScreenSaverConf(KEY_SHOW_REST_TIME, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetLockScreenConf) { QVariant result1 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IDLE_DELAY); int value1 = result1.toInt(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IDLE_DELAY); QVariant result2 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IDLE_LOCK); int value2 = result2.toInt(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IDLE_LOCK); QVariant result3 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IDLE_LOCK); bool value3 = result3.toBool(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IDLE_LOCK); QVariant result4 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IDLE_LOCK); bool value4 = result4.toBool(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IDLE_LOCK); QVariant result5 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IDLE_LOCK); int value5 = result5.toInt(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IDLE_LOCK); QVariant result6 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IDLE_LOCK); bool value6 = result6.toBool(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IDLE_LOCK); QVariant result7 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_SLEEP_ACTIVATION_ENABLED); bool value7 = result7.toBool(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_SLEEP_ACTIVATION_ENABLED); QVariant result8 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_LOCK_ENABLED); bool value8 = result8.toBool(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_LOCK_ENABLED); QVariant result9 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_BACKGROUND); QString value9 = result9.toString(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_BACKGROUND); QVariant result10 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_MODE); QString value10 = result10.toString(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_MODE); QVariant result11 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_THEMES); QStringList value11 = result11.toStringList(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_THEMES); QVariant result12 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IMAGE_TRANSITION_EFFECT); int value12 = result12.toInt(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IMAGE_TRANSITION_EFFECT); QVariant result13 = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IMAGE_SWITCH_INTERVAL); int value13 = result13.toInt(); m_pGSettingsHelperDbus->onLockScreenConfigChanged(KEY_IMAGE_SWITCH_INTERVAL); ASSERT_EQ(value1, 5); ASSERT_EQ(value2, -1); ASSERT_EQ(value3, true); ASSERT_EQ(value4, true); ASSERT_EQ(value5, 10); ASSERT_EQ(value6, true); ASSERT_EQ(value7, true); ASSERT_EQ(value8, true); ASSERT_EQ(value9.isEmpty(), false); ASSERT_EQ(value10.isEmpty(), false); ASSERT_EQ(value11.isEmpty(), false); ASSERT_EQ(value12, 0); ASSERT_EQ(value13, 60); } TEST_F(GSettingsHelperTest, SetLockScreenConf) { QVariant result = m_pGSettingsHelperDbus->GetLockScreenConf(KEY_IDLE_DELAY); int oldValue = result.toInt(); bool value = m_pGSettingsHelperDbus->SetLockScreenConf(KEY_IDLE_DELAY, 1); m_pGSettingsHelperDbus->SetLockScreenConf(KEY_IDLE_DELAY, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetPowerManagerConf) { QVariant result1 = m_pGSettingsHelperDbus->GetPowerManagerConf(KEY_LOCK_SUSPEND); bool value1 = result1.toBool(); m_pGSettingsHelperDbus->onPowerManagerConfigChanged(KEY_LOCK_SUSPEND); QVariant result2 = m_pGSettingsHelperDbus->GetPowerManagerConf(KEY_LOCK_HIBERNATE); bool value2 = result2.toBool(); m_pGSettingsHelperDbus->onPowerManagerConfigChanged(KEY_LOCK_HIBERNATE); QVariant result3 = m_pGSettingsHelperDbus->GetPowerManagerConf(KEY_LOCK_BLANKSCREEN); bool value3 = result3.toBool(); m_pGSettingsHelperDbus->onPowerManagerConfigChanged(KEY_LOCK_BLANKSCREEN); QVariant result4 = m_pGSettingsHelperDbus->GetPowerManagerConf(KEY_SLEEP_COMPUTER_AC); int value4 = result4.toInt(); m_pGSettingsHelperDbus->onPowerManagerConfigChanged(KEY_SLEEP_COMPUTER_AC); QVariant result5 = m_pGSettingsHelperDbus->GetPowerManagerConf(KEY_SLEEP_DISPLAY_AC); int value5 = result5.toInt(); m_pGSettingsHelperDbus->onPowerManagerConfigChanged(KEY_SLEEP_DISPLAY_AC); QVariant result6 = m_pGSettingsHelperDbus->GetPowerManagerConf(KEY_BUTTON_LID_AC); int value6 = result6.toInt(); m_pGSettingsHelperDbus->onPowerManagerConfigChanged(KEY_BUTTON_LID_AC); ASSERT_EQ(value1, false); ASSERT_EQ(value2, false); ASSERT_EQ(value3, true); ASSERT_EQ(value4, -1); ASSERT_EQ(value5, -1); ASSERT_EQ(value6, 0); } TEST_F(GSettingsHelperTest, SetPowerManagerConf) { QVariant result = m_pGSettingsHelperDbus->GetPowerManagerConf(KEY_LOCK_SUSPEND); bool oldValue = result.toBool(); bool value = m_pGSettingsHelperDbus->SetPowerManagerConf(KEY_LOCK_SUSPEND, false); m_pGSettingsHelperDbus->SetPowerManagerConf(KEY_LOCK_SUSPEND, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetMateBgConf) { QVariant result1 = m_pGSettingsHelperDbus->GetMateBgConf(KEY_PICTURE_FILENAME); QString value1 = result1.toString(); // m_pGSettingsHelperDbus->onMateBgConfigChanged(KEY_PICTURE_FILENAME); QVariant result2 = m_pGSettingsHelperDbus->GetMateBgConf(KEY_PICTURE_OPTIONS); QString value2 = result2.toString(); // m_pGSettingsHelperDbus->onMateBgConfigChanged(KEY_PICTURE_OPTIONS); QVariant result3 = m_pGSettingsHelperDbus->GetMateBgConf(KEY_PRIMARY_COLOR); QString value3 = result3.toString(); // m_pGSettingsHelperDbus->onMateBgConfigChanged(KEY_PRIMARY_COLOR); ASSERT_EQ(value1.isEmpty(), false); ASSERT_EQ(value2.isEmpty(), false); ASSERT_EQ(value3.isEmpty(), false); } TEST_F(GSettingsHelperTest, SetMateBgConf) { QVariant result = m_pGSettingsHelperDbus->GetMateBgConf(KEY_PICTURE_FILENAME); QString oldValue = result.toString(); bool value = m_pGSettingsHelperDbus->SetMateBgConf(KEY_PICTURE_FILENAME, "ssss"); m_pGSettingsHelperDbus->SetMateBgConf(KEY_PICTURE_FILENAME, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetUkccPluginsConf) { QVariant result1 = m_pGSettingsHelperDbus->GetUkccPluginsConf(KEY_HOUR_SYSTEM); int value1 = result1.toInt(); m_pGSettingsHelperDbus->onUkccPluginsConfigChanged(KEY_HOUR_SYSTEM); QVariant result2 = m_pGSettingsHelperDbus->GetUkccPluginsConf(KEY_DATE); QString value2 = result2.toString(); m_pGSettingsHelperDbus->onUkccPluginsConfigChanged(KEY_DATE); ASSERT_EQ(value1, 24); ASSERT_EQ(value2.isEmpty(), false); } TEST_F(GSettingsHelperTest, SetUkccPluginsConf) { QVariant result = m_pGSettingsHelperDbus->GetUkccPluginsConf(KEY_HOUR_SYSTEM); int oldValue = result.toInt(); bool value = m_pGSettingsHelperDbus->SetUkccPluginsConf(KEY_HOUR_SYSTEM, 100); m_pGSettingsHelperDbus->SetUkccPluginsConf(KEY_HOUR_SYSTEM, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetThemeStyleConf) { QVariant result1 = m_pGSettingsHelperDbus->GetThemeStyleConf(KEY_SYSTEM_FONT_SIZE); double value1 = result1.toDouble(); m_pGSettingsHelperDbus->onThemeStyleConfigChanged(KEY_SYSTEM_FONT_SIZE); QVariant result2 = m_pGSettingsHelperDbus->GetThemeStyleConf(KEY_THEME_COLOR); QString value2 = result2.toString(); m_pGSettingsHelperDbus->onThemeStyleConfigChanged(KEY_THEME_COLOR); QVariant result3 = m_pGSettingsHelperDbus->GetThemeStyleConf(KEY_MENU_TRANSPARENCY); int value3 = result3.toInt(); m_pGSettingsHelperDbus->onThemeStyleConfigChanged(KEY_MENU_TRANSPARENCY); QVariant result4 = m_pGSettingsHelperDbus->GetThemeStyleConf(KEY_STYLE_NAME); QString value4 = result4.toString(); m_pGSettingsHelperDbus->onThemeStyleConfigChanged(KEY_STYLE_NAME); QVariant result5 = m_pGSettingsHelperDbus->GetThemeStyleConf(KEY_SYSTEM_FONT); QString value5 = result5.toString(); m_pGSettingsHelperDbus->onThemeStyleConfigChanged(KEY_SYSTEM_FONT); ASSERT_EQ(value1, 11); ASSERT_EQ(value2.isEmpty(), false); ASSERT_EQ(value3, 35); ASSERT_EQ(value4.isEmpty(), false); ASSERT_EQ(value5.isEmpty(), false); } TEST_F(GSettingsHelperTest, SetThemeStyleConf) { QVariant result = m_pGSettingsHelperDbus->GetThemeStyleConf(KEY_SYSTEM_FONT_SIZE); int oldValue = result.toInt(); bool value = m_pGSettingsHelperDbus->SetThemeStyleConf(KEY_SYSTEM_FONT_SIZE, 14); m_pGSettingsHelperDbus->SetThemeStyleConf(KEY_SYSTEM_FONT_SIZE, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetSessionConf) { QVariant result1 = m_pGSettingsHelperDbus->GetSessionConf(KEY_SESSION_IDLE); int value1 = result1.toInt(); m_pGSettingsHelperDbus->onSessionConfigChanged(KEY_SESSION_IDLE); QVariant result2 = m_pGSettingsHelperDbus->GetSessionConf(KEY_SESSION_LOGOUT_MUSIC); bool value2 = result2.toBool(); m_pGSettingsHelperDbus->onSessionConfigChanged(KEY_SESSION_LOGOUT_MUSIC); QVariant result3 = m_pGSettingsHelperDbus->GetSessionConf(KEY_SESSION_POWEROFF_MUSIC); bool value3 = result3.toBool(); m_pGSettingsHelperDbus->onSessionConfigChanged(KEY_SESSION_POWEROFF_MUSIC); ASSERT_EQ(value1, 1); ASSERT_EQ(value2, false); ASSERT_EQ(value3, false); } TEST_F(GSettingsHelperTest, SetSessionConf) { QVariant result = m_pGSettingsHelperDbus->GetSessionConf(KEY_SESSION_LOGOUT_MUSIC); bool oldValue = result.toBool(); bool value = m_pGSettingsHelperDbus->SetSessionConf(KEY_SESSION_LOGOUT_MUSIC, false); m_pGSettingsHelperDbus->SetSessionConf(KEY_SESSION_LOGOUT_MUSIC, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetKeyboardConf) { QVariant result1 = m_pGSettingsHelperDbus->GetKeyboardConf(KEY_CAPSLOCK_STATUS); bool value1 = result1.toBool(); m_pGSettingsHelperDbus->onKeyboardConfigChanged(KEY_CAPSLOCK_STATUS); ASSERT_EQ(value1, false); } TEST_F(GSettingsHelperTest, SetKeyboardConf) { QVariant result = m_pGSettingsHelperDbus->GetKeyboardConf(KEY_CAPSLOCK_STATUS); bool oldValue = result.toBool(); bool value = m_pGSettingsHelperDbus->SetKeyboardConf(KEY_CAPSLOCK_STATUS, false); m_pGSettingsHelperDbus->SetKeyboardConf(KEY_CAPSLOCK_STATUS, oldValue); ASSERT_EQ(value, true); } TEST_F(GSettingsHelperTest, GetUsdMediaKeys) { QVariant result1 = m_pGSettingsHelperDbus->GetUsdMediaKeys(KEY_AREA_SCREENSHOT); QString value1 = result1.toString(); m_pGSettingsHelperDbus->onUsdMediaKeysConfigChanged(KEY_AREA_SCREENSHOT); QVariant result2 = m_pGSettingsHelperDbus->GetUsdMediaKeys(KEY_AREA_SCREENSHOT2); QString value2 = result2.toString(); m_pGSettingsHelperDbus->onUsdMediaKeysConfigChanged(KEY_AREA_SCREENSHOT2); QVariant result3 = m_pGSettingsHelperDbus->GetUsdMediaKeys(KEY_SCREEN_SHOT); QString value3 = result3.toString(); m_pGSettingsHelperDbus->onUsdMediaKeysConfigChanged(KEY_SCREEN_SHOT); QVariant result4 = m_pGSettingsHelperDbus->GetUsdMediaKeys(KEY_SCREEN_SHOT2); QString value4 = result4.toString(); m_pGSettingsHelperDbus->onUsdMediaKeysConfigChanged(KEY_SCREEN_SHOT2); QVariant result5 = m_pGSettingsHelperDbus->GetUsdMediaKeys(KEY_WINDOW_SCREENSHOT); QString value5 = result5.toString(); m_pGSettingsHelperDbus->onUsdMediaKeysConfigChanged(KEY_WINDOW_SCREENSHOT); ASSERT_EQ(value1.isEmpty(), false); ASSERT_EQ(value2.isEmpty(), false); ASSERT_EQ(value3.isEmpty(), false); ASSERT_EQ(value4.isEmpty(), false); ASSERT_EQ(value5.isEmpty(), false); } TEST_F(GSettingsHelperTest, GetUsdMediaStateKeys) { QVariant result1 = m_pGSettingsHelperDbus->GetUsdMediaStateKeys(KEY_RFKILL_STATE); bool value1 = result1.toBool(); m_pGSettingsHelperDbus->onUsdMediaStateKeysConfigChanged(KEY_RFKILL_STATE); ASSERT_EQ(value1, false); } TEST_F(GSettingsHelperTest, SetUsdMediaStateKeys) { QVariant result = m_pGSettingsHelperDbus->GetUsdMediaStateKeys(KEY_RFKILL_STATE); bool oldValue = result.toBool(); bool value = m_pGSettingsHelperDbus->SetUsdMediaStateKeys(KEY_RFKILL_STATE, false); m_pGSettingsHelperDbus->SetUsdMediaStateKeys(KEY_RFKILL_STATE, oldValue); ASSERT_EQ(value, true); } ukui-screensaver/tests/unit_test_gsettings_helper/main.cpp0000664000175000017500000000156215172041035023233 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_session_watcher/0000775000175000017500000000000015172041106021430 5ustar fengfengukui-screensaver/tests/unit_test_session_watcher/CMakeLists.txt0000664000175000017500000000426315172041106024175 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus X11Extras REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(QGS REQUIRED gsettings-qt) pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) find_package(X11 REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/sessionwatcher.cpp ../../src/common/utils.cpp ../../src/common/global_utils.cpp ../../src/lock-backend/gsettingshelper.cpp unit_test_session_watcher.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/common/utils.h ../../src/common/global_utils.h ../../src/lock-backend/sessionwatcher.h ../../src/lock-backend/gsettingshelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ${GIOUNIX2_INCLUDE_DIRS} ${X11_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_session_watcher ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_session_watcher Qt5::Core Qt5::Gui Qt5::DBus Qt5::X11Extras ${QGS_LIBRARIES} ${GIOUNIX2_LIBRARIES} ${X11_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_session_watcher GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_session_watcher/main.cpp0000664000175000017500000000156215172041035023065 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_session_watcher/unit_test_session_watcher.cpp0000664000175000017500000000315715172041035027441 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/lock-backend/sessionwatcher.h" #include "stubext.h" using namespace stub_ext; class SessionWatcherTest : public testing::Test { protected: QSharedPointer m_pGSettingsHelperDbus; QSharedPointer m_pSessionWatcherDbus; void SetUp() override { m_pGSettingsHelperDbus = QSharedPointer(new GSettingsHelper()); m_pSessionWatcherDbus = QSharedPointer(new SessionWatcher(m_pGSettingsHelperDbus)); } void TearDown() override { m_pSessionWatcherDbus.reset(); m_pGSettingsHelperDbus.reset(); } }; TEST_F(SessionWatcherTest, GetCurUserName) { m_pSessionWatcherDbus->onStatusChanged(0); m_pSessionWatcherDbus->onStatusChanged(1); m_pSessionWatcherDbus->onStatusChanged(2); m_pSessionWatcherDbus->onStatusChanged(3); } ukui-screensaver/tests/unit_test_kglobalaccel_helper/0000775000175000017500000000000015172041106022172 5ustar fengfengukui-screensaver/tests/unit_test_kglobalaccel_helper/unit_test_kglobalaccel_helper.cpp0000664000175000017500000000301615172041035030737 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/kglobalaccelhelper.h" #include "stubext.h" using namespace stub_ext; class KglobalAccelHelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pKglobalAccelHelperDbus = new KglobalAccelHelper(); } static void TearDownTestSuite() { delete m_pKglobalAccelHelperDbus; m_pKglobalAccelHelperDbus = nullptr; } static KglobalAccelHelper *m_pKglobalAccelHelperDbus; }; KglobalAccelHelper *KglobalAccelHelperTest::m_pKglobalAccelHelperDbus = nullptr; TEST_F(KglobalAccelHelperTest, blockShortcut) { bool oldValue = m_pKglobalAccelHelperDbus->blockShortcut(true); ASSERT_EQ(oldValue, true); bool newValue = m_pKglobalAccelHelperDbus->blockShortcut(false); ASSERT_EQ(newValue, true); } ukui-screensaver/tests/unit_test_kglobalaccel_helper/CMakeLists.txt0000664000175000017500000000315415172041106024735 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/kglobalaccelhelper.cpp unit_test_kglobalaccel_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/dbusifs/kglobalaccelhelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_kglobalaccel_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_kglobalaccel_helper Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_kglobalaccel_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_kglobalaccel_helper/main.cpp0000664000175000017500000000156215172041035023627 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_display_service/0000775000175000017500000000000015172041106021415 5ustar fengfengukui-screensaver/tests/unit_test_display_service/unit_test_display_service.cpp0000664000175000017500000000501015172041035027401 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/common/displayservice.h" class DisplayServiceTest : public testing::Test { protected: // static void SetUpTestSuite() // { // m_pGSettingsHelperDbus = new GSettingsHelper(); // } // static void TearDownTestSuite() // { // delete m_pGSettingsHelperDbus; // m_pGSettingsHelperDbus = nullptr; // } // static GSettingsHelper *m_pGSettingsHelperDbus; }; //GSettingsHelper *DisplayServiceTest::m_pGSettingsHelperDbus = nullptr; // 测试 switchDisplayMode 函数 TEST_F(DisplayServiceTest, SwitchDisplayMode) { // 测试 DISPLAY_MODE_ORI bool value = DisplayService::instance()->switchDisplayMode(DISPLAY_MODE_ORI); EXPECT_EQ(value, false); // 测试 DISPLAY_MODE_CLONE bool value1 = DisplayService::instance()->switchDisplayMode(DISPLAY_MODE_CLONE); EXPECT_EQ(value1, false); // 测试 DISPLAY_MODE_EXTEND bool value2 = DisplayService::instance()->switchDisplayMode(DISPLAY_MODE_EXTEND); EXPECT_EQ(value2, false); // 测试 DISPLAY_MODE_ONLY_OUT bool value3 = DisplayService::instance()->switchDisplayMode(DISPLAY_MODE_ONLY_OUT); EXPECT_EQ(value3, false); } // 测试 setOneDisplayMode 函数 TEST_F(DisplayServiceTest, SetOneDisplayMode) { // 测试使用 xrandr DisplayService::instance()->setOneDisplayMode(); } // 测试 setCurUserName 函数 TEST_F(DisplayServiceTest, SetCurUserName) { DisplayService::instance()->setCurUserName(getenv("USER")); } // 测试 isSaveParamInUsed 函数 TEST_F(DisplayServiceTest, IsSaveParamInUsed) { EXPECT_TRUE(DisplayService::instance()->isSaveParamInUsed()); EXPECT_FALSE(DisplayService::instance()->isSaveParamInUsed()); } // 测试 isJJW7200 函数 TEST_F(DisplayServiceTest, IsJJW7200) { int value = DisplayService::instance()->isJJW7200(); EXPECT_EQ(value, 0); } ukui-screensaver/tests/unit_test_display_service/CMakeLists.txt0000664000175000017500000000302715172041106024157 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/common/displayservice.cpp unit_test_display_service.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/displayservice.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_display_service ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_display_service Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_display_service GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_display_service/main.cpp0000664000175000017500000000156215172041035023052 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_switch_user/0000775000175000017500000000000015172041106020567 5ustar fengfengukui-screensaver/tests/unit_test_switch_user/CMakeLists.txt0000664000175000017500000000362115172041106023331 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/switchuserutils.cpp ../../src/common/global_utils.cpp unit_test_switch_user.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-backend/switchuserutils.h ../../src/common/global_utils.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${GLIB2_INCLUDE_DIRS} ${LIBSYSTEMD_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_switch_user ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_switch_user Qt5::Core Qt5::Gui Qt5::DBus ${LIBSYSTEMD_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_switch_user GTest::GTest GTest::Main Threads::Threads ${GLIB2_LIBRARIES} ) ukui-screensaver/tests/unit_test_switch_user/main.cpp0000664000175000017500000000156215172041035022224 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_switch_user/unit_test_switch_user.cpp0000664000175000017500000000531715172041035025737 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/common/global_utils.h" #include "../../src/lock-backend/switchuserutils.h" #include "stubext.h" using namespace stub_ext; class SwitchUserUtilsTest : public testing::Test { protected: static void SetUpTestSuite() { m_pSwitchUserUtilsDbus = new SwitchUserUtils(); } static void TearDownTestSuite() { delete m_pSwitchUserUtilsDbus; m_pSwitchUserUtilsDbus = nullptr; } static SwitchUserUtils *m_pSwitchUserUtilsDbus; }; SwitchUserUtils *SwitchUserUtilsTest::m_pSwitchUserUtilsDbus = nullptr; // 测试 GetCurUserName 函数 TEST_F(SwitchUserUtilsTest, GetCurUserName) { QString userName = m_pSwitchUserUtilsDbus->GetCurUserName(); EXPECT_EQ(userName, "testuser"); } // 测试 GetUidByName 函数 TEST_F(SwitchUserUtilsTest, GetUidByName) { int uid = m_pSwitchUserUtilsDbus->GetUidByName(getenv("USER")); EXPECT_EQ(uid, 1000); } // 测试 GetUserUDII 函数 TEST_F(SwitchUserUtilsTest, GetUserUDII) { UserDisplayIfInfo userDisplayIfInfo = m_pSwitchUserUtilsDbus->GetUserUDII(getenv("USER")); EXPECT_EQ(userDisplayIfInfo.strSeatPath, "/org/freedesktop/DisplayManager/Seat1"); EXPECT_EQ(userDisplayIfInfo.strUserName, "testuser"); EXPECT_EQ(userDisplayIfInfo.strSessionPath, "/org/freedesktop/DisplayManager/Session1"); } // 测试 SwitchToUserSession 函数 TEST_F(SwitchUserUtilsTest, SwitchToUserSession) { UserDisplayIfInfo userDisplayIfInfo; userDisplayIfInfo.strSeatPath = "/org/freedesktop/DisplayManager/Seat1"; userDisplayIfInfo.strUserName = "testuser"; userDisplayIfInfo.strSessionPath = "/org/freedesktop/DisplayManager/Session1"; int result = m_pSwitchUserUtilsDbus->SwitchToUserSession("/org/freedesktop/DisplayManager/Seat1", userDisplayIfInfo); EXPECT_EQ(result, 0); } // 测试 SwitchToUserLock 函数 TEST_F(SwitchUserUtilsTest, SwitchToUserLock) { bool result = m_pSwitchUserUtilsDbus->SwitchToUserLock(); EXPECT_TRUE(result); } ukui-screensaver/tests/unit_test_freedesktop_helper/0000775000175000017500000000000015172041106022102 5ustar fengfengukui-screensaver/tests/unit_test_freedesktop_helper/unit_test_freedesktop_helper.cpp0000664000175000017500000000312215172041035030555 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/freedesktophelper.h" #include "stubext.h" using namespace stub_ext; class FreedesktopHelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pFreedesktopHelperDbus = new FreedesktopHelper(); } static void TearDownTestSuite() { delete m_pFreedesktopHelperDbus; m_pFreedesktopHelperDbus = nullptr; } static FreedesktopHelper *m_pFreedesktopHelperDbus; }; FreedesktopHelper *FreedesktopHelperTest::m_pFreedesktopHelperDbus = nullptr; TEST_F(FreedesktopHelperTest, NameHasOwner) { bool value = m_pFreedesktopHelperDbus->NameHasOwner("org.ukui.Biometric"); ASSERT_EQ(value, false); } TEST_F(FreedesktopHelperTest, isServiceActivable) { bool value = m_pFreedesktopHelperDbus->isServiceActivable("org.ukui.UniauthBackend"); ASSERT_EQ(value, false); } ukui-screensaver/tests/unit_test_freedesktop_helper/CMakeLists.txt0000664000175000017500000000321515172041106024643 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/freedesktophelper.cpp unit_test_freedesktop_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/dbusifs/freedesktophelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_freedesktop_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_freedesktop_helper Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_freedesktop_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_freedesktop_helper/main.cpp0000664000175000017500000000156215172041035023537 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/kt-test-utils/0000775000175000017500000000000015172041035016664 5ustar fengfengukui-screensaver/tests/kt-test-utils/cpp-stub-ext/0000775000175000017500000000000015172041035021217 5ustar fengfengukui-screensaver/tests/kt-test-utils/cpp-stub-ext/stub-shadow.cpp0000664000175000017500000000314715172041035024170 0ustar fengfeng/* * Author: Zhang Yu * Maintainer: Zhang Yu * * MIT License * * Copyright (c) 2020 Zhang Yu * * 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 "stub-shadow.h" namespace stub_ext { WrapperMap stub_wrappers; Wrapper::Wrapper() { } Wrapper::~Wrapper() { } void freeWrapper(Wrapper *wrapper) { if (!wrapper) return; for (auto iter = stub_wrappers.begin(); iter != stub_wrappers.end();) { if (iter->second == wrapper) iter = stub_wrappers.erase(iter); else ++iter; } delete wrapper; } } ukui-screensaver/tests/kt-test-utils/cpp-stub-ext/stubext.h0000664000175000017500000000734115172041035023073 0ustar fengfeng #ifndef STUBEXT_H #define STUBEXT_H /* * Author: Zhang Yu * Maintainer: Zhang Yu * * MIT License * * Copyright (c) 2020 Zhang Yu * * 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. */ //需修改Stub的私用成员函数和成员变量为保护类型 #include "stub.h" #include "stub-shadow.h" #ifdef DEBUG_STUB_INVOKE // use to make sure the stub function is invoked. # define __DBG_STUB_INVOKE__ printf("stub at %s:%d is invoked.\n", __FILE__, __LINE__); #else # define __DBG_STUB_INVOKE__ #endif #define VADDR(CLASS_NAME, MEMBER_NAME) (typename stub_ext::VFLocator::Func)(&CLASS_NAME::MEMBER_NAME) namespace stub_ext { class StubExt : public Stub { public: StubExt() : Stub() { } template bool set_lamda(T addr, Lamda lamda) { char *fn = addrof(addr); if (m_result.find(fn) != m_result.end()) reset(addr); Wrapper *wrapper = nullptr; auto addr_stub = depictShadow(&wrapper, addr, lamda); if (set(addr, addr_stub)) { m_wrappers.insert(std::make_pair(fn, wrapper)); return true; } else { freeWrapper(wrapper); } return false; } template void reset(T addr) { Stub::reset(addr); char *fn = addrof(addr); auto iter = m_wrappers.find(fn); if (iter != m_wrappers.end()) { freeWrapper(iter->second); m_wrappers.erase(iter); } } ~StubExt() { clear(); } void clear() override { Stub::clear(); for (auto iter = m_wrappers.begin(); iter != m_wrappers.end(); ++iter) { freeWrapper(iter->second); } m_wrappers.clear(); } template static void *get_ctor_addr(bool start = true) { // the start vairable must be true, or the compiler will optimize out. if (start) goto Start; Call_Constructor: // This line of code will not be executed. // The purpose of the code is to allow the compiler to generate the assembly code that calls the constructor. T(); Start: // The address of the line of code T() obtained by assembly char *p = (char *)&&Call_Constructor; // https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html // CALL rel32 void *ret = 0; char pos; char call = 0xe8; do { pos = *p; if (pos == call) { ret = p + 5 + (*(int *)(p + 1)); } } while (!ret && (++p)); return ret; } protected: std::map m_wrappers; }; } #endif // STUBEXT_H ukui-screensaver/tests/kt-test-utils/cpp-stub-ext/stub-shadow.h0000664000175000017500000001143415172041035023633 0ustar fengfeng #ifndef STUBSHADOW_H #define STUBSHADOW_H /* * Author: Zhang Yu * Maintainer: Zhang Yu * * MIT License * * Copyright (c) 2020 Zhang Yu * * 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 namespace stub_ext { #define LAMDA_FUNCTION_TYPE decltype(&Lamda::operator()) class Wrapper { public: Wrapper(); virtual ~Wrapper(); }; typedef std::unordered_map WrapperMap; extern WrapperMap stub_wrappers; template class LamdaWrapper : public Wrapper { public: LamdaWrapper(Lamda func): Wrapper(),_func(func){} ~LamdaWrapper(){} Lamda _func; }; template struct VFLocator { }; template struct VFLocator { typedef Ret (*Func)(Obj*, Args...); }; template struct VFLocator { typedef Ret (*Func)(Obj*, Args...); }; template struct LamdaCaller { }; template struct LamdaCaller { template static Ret call(LamdaWrapper *wrapper, OrgArgs&&... args) { return wrapper->_func(std::forward(args)...); } }; template struct LamdaCaller { template static Ret call(LamdaWrapper *wrapper, OrgArgs&&... args) { return wrapper->_func(); } }; template struct FuncShadow { }; template struct FuncShadow { typedef Ret (*Shadow)(Args...); typedef Ret RetType; static Ret call(Args ...args) { Shadow shadow = &call; long id = (long)shadow; auto iter = stub_wrappers.find(id); assert(stub_wrappers.find(id) != stub_wrappers.end()); LamdaWrapper *wrapper = dynamic_cast *>(iter->second); return LamdaCaller::call(wrapper, args...); } }; template struct FuncShadow { typedef Ret (*Shadow)(Obj *,Args...); typedef Ret RetType; static Ret call(Obj *obj, Args ...args) { Shadow shadow = &call; long id = (long)shadow; auto iter = stub_wrappers.find(id); assert(stub_wrappers.find(id) != stub_wrappers.end()); LamdaWrapper *wrapper = dynamic_cast *>(iter->second); return LamdaCaller::call(wrapper, obj, args...); } }; template struct FuncShadow { typedef Ret (*Shadow)(Obj *,Args...); typedef Ret RetType; static Ret call(Obj *obj, Args ...args) { Shadow shadow = &call; long id = (long)shadow; auto iter = stub_wrappers.find(id); assert(stub_wrappers.find(id) != stub_wrappers.end()); LamdaWrapper *wrapper = dynamic_cast *>(iter->second); return LamdaCaller::call(wrapper, obj, args...); } }; template typename FuncShadow::Shadow depictShadow(Wrapper **wrapper, Func func, Lamda lamda) { *wrapper = new LamdaWrapper(lamda); typename FuncShadow::Shadow shadow = &FuncShadow::call; long id = (long)shadow; assert(stub_wrappers.find(id) == stub_wrappers.end()); stub_wrappers.insert(std::make_pair(id,*wrapper)); return shadow; } void freeWrapper(Wrapper *wrapper); } #endif // STUBSHADOW_H ukui-screensaver/tests/kt-test-utils/cpp-stub/0000775000175000017500000000000015172041035020421 5ustar fengfengukui-screensaver/tests/kt-test-utils/cpp-stub/stub.h0000664000175000017500000002341715172041035021556 0ustar fengfeng#ifndef __STUB_H__ #define __STUB_H__ #ifdef _WIN32 //windows #include #include #else //linux #include #include #include #endif //c #include #include //c++ #include #define ADDR(CLASS_NAME,MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME) /********************************************************** replace function **********************************************************/ #ifdef _WIN32 #define CACHEFLUSH(addr, size) FlushInstructionCache(GetCurrentProcess(), addr, size) #else #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) #endif #if defined(__aarch64__) || defined(_M_ARM64) #define CODESIZE 16U #define CODESIZE_MIN 16U #define CODESIZE_MAX CODESIZE // ldr x9, +8 // br x9 // addr #define REPLACE_FAR(t, fn, fn_stub)\ ((uint32_t*)fn)[0] = 0x58000040 | 9;\ ((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\ *(long long *)(fn + 8) = (long long )fn_stub;\ CACHEFLUSH((char *)fn, CODESIZE); #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__arm__) || defined(_M_ARM) #define CODESIZE 8U #define CODESIZE_MIN 8U #define CODESIZE_MAX CODESIZE // ldr pc, [pc, #-4] #define REPLACE_FAR(t, fn, fn_stub)\ ((uint32_t*)fn)[0] = 0xe51ff004;\ ((uint32_t*)fn)[1] = (uint32_t)fn_stub;\ CACHEFLUSH((char *)fn, CODESIZE); #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__mips64) #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) #define CODESIZE 80U #define CODESIZE_MIN 80U #define CODESIZE_MAX CODESIZE //mips没有PC指针,所以需要手动入栈出栈 //120000ce0: 67bdffe0 daddiu sp, sp, -32 //入栈 //120000ce4: ffbf0018 sd ra, 24(sp) //120000ce8: ffbe0010 sd s8, 16(sp) //120000cec: ffbc0008 sd gp, 8(sp) //120000cf0: 03a0f025 move s8, sp //120000d2c: 03c0e825 move sp, s8 //出栈 //120000d30: dfbf0018 ld ra, 24(sp) //120000d34: dfbe0010 ld s8, 16(sp) //120000d38: dfbc0008 ld gp, 8(sp) //120000d3c: 67bd0020 daddiu sp, sp, 32 //120000d40: 03e00008 jr ra #define REPLACE_FAR(t, fn, fn_stub)\ ((uint32_t *)fn)[0] = 0x67bdffe0;\ ((uint32_t *)fn)[1] = 0xffbf0018;\ ((uint32_t *)fn)[2] = 0xffbe0010;\ ((uint32_t *)fn)[3] = 0xffbc0008;\ ((uint32_t *)fn)[4] = 0x03a0f025;\ *(uint16_t *)(fn + 20) = (long long)fn_stub >> 32;\ *(fn + 22) = 0x19;\ *(fn + 23) = 0x24;\ ((uint32_t *)fn)[6] = 0x0019cc38;\ *(uint16_t *)(fn + 28) = (long long)fn_stub >> 16;\ *(fn + 30) = 0x39;\ *(fn + 31) = 0x37;\ ((uint32_t *)fn)[8] = 0x0019cc38;\ *(uint16_t *)(fn + 36) = (long long)fn_stub;\ *(fn + 38) = 0x39;\ *(fn + 39) = 0x37;\ ((uint32_t *)fn)[10] = 0x0320f809;\ ((uint32_t *)fn)[11] = 0x00000000;\ ((uint32_t *)fn)[12] = 0x00000000;\ ((uint32_t *)fn)[13] = 0x03c0e825;\ ((uint32_t *)fn)[14] = 0xdfbf0018;\ ((uint32_t *)fn)[15] = 0xdfbe0010;\ ((uint32_t *)fn)[16] = 0xdfbc0008;\ ((uint32_t *)fn)[17] = 0x67bd0020;\ ((uint32_t *)fn)[18] = 0x03e00008;\ ((uint32_t *)fn)[19] = 0x00000000;\ CACHEFLUSH((char *)fn, CODESIZE); #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__thumb__) || defined(_M_THUMB) #error "Thumb is not supported" #else //__i386__ _x86_64__ #define CODESIZE 13U #define CODESIZE_MIN 5U #define CODESIZE_MAX CODESIZE //13 byte(jmp m16:64) //movabs $0x102030405060708,%r11 //jmpq *%r11 static void REPLACE_FAR(void *t, char *fn, char *fn_stub) { *fn = 0x49; *(fn + 1) = 0xbb; *(long long *)(fn + 2) = (long long)fn_stub; *(fn + 10) = 0x41; *(fn + 11) = 0xff; *(fn + 12) = 0xe3; CACHEFLUSH((char *)fn, CODESIZE); } //5 byte(jmp rel32) #define REPLACE_NEAR(t, fn, fn_stub)\ *fn = 0xE9;\ *(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN);\ CACHEFLUSH((char *)fn, CODESIZE); #endif struct func_stub { char *fn; unsigned char code_buf[CODESIZE]; bool far_jmp; }; class Stub { public: Stub() { #ifdef _WIN32 SYSTEM_INFO sys_info; GetSystemInfo(&sys_info); m_pagesize = sys_info.dwPageSize; #else m_pagesize = sysconf(_SC_PAGE_SIZE); #endif if (m_pagesize < 0) { m_pagesize = 4096; } } ~Stub() { clear(); } virtual void clear() { std::map::iterator iter; struct func_stub *pstub; for(iter=m_result.begin(); iter != m_result.end(); iter++) { pstub = iter->second; #ifdef _WIN32 DWORD lpflOldProtect; if(0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (0 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { if(pstub->far_jmp) { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); } else { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); } #ifdef _WIN32 VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect); #else CACHEFLUSH(pstub->fn,CODESIZE); mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC); #endif } iter->second = NULL; delete pstub; } m_result.clear(); return; } template bool set(T addr, S addr_stub) { char * fn; char * fn_stub; fn = addrof(addr); fn_stub = addrof(addr_stub); struct func_stub *pstub; std::map::iterator iter = m_result.find(fn); if (iter == m_result.end()) { pstub = new func_stub; //start pstub->fn = fn; if(distanceof(fn, fn_stub)) { pstub->far_jmp = true; std::memcpy(pstub->code_buf, fn, CODESIZE_MAX); } else { pstub->far_jmp = false; std::memcpy(pstub->code_buf, fn, CODESIZE_MIN); } } else { pstub = iter->second; pstub->far_jmp = distanceof(fn, fn_stub); } #ifdef _WIN32 DWORD lpflOldProtect; if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), static_cast(m_pagesize * 2), PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { // throw("stub set memory protect to w+r+x faild"); return false; } if(pstub->far_jmp) { REPLACE_FAR(this, fn, fn_stub); } else { REPLACE_NEAR(this, fn, fn_stub); } #ifdef _WIN32 if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) #endif { // throw("stub set memory protect to r+x failed"); return false; } m_result.insert(std::pair(fn,pstub)); return true; } template bool reset(T addr) { char * fn; fn = addrof(addr); std::map::iterator iter = m_result.find(fn); if (iter == m_result.end()) { return true; } struct func_stub *pstub; pstub = iter->second; #ifdef _WIN32 DWORD lpflOldProtect; if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { // throw("stub reset memory protect to w+r+x faild"); return false; } if(pstub->far_jmp) { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); } else { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); } #ifdef _WIN32 if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) #else CACHEFLUSH(pstub->fn,CODESIZE); if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) #endif { // throw("stub reset memory protect to r+x failed"); return false; } m_result.erase(iter); delete pstub; return true; } protected: char *pageof(char* addr) { #ifdef _WIN32 return (char *)((unsigned long long)addr & ~(m_pagesize - 1)); #else return (char *)((unsigned long)addr & ~(m_pagesize - 1)); #endif } template char* addrof(T addr) { union { T _s; char* _d; }ut; ut._s = addr; return ut._d; } bool distanceof(char* addr, char* addr_stub) { std::ptrdiff_t diff = addr_stub >= addr ? addr_stub - addr : addr - addr_stub; if((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0)) { return true; } return false; } protected: #ifdef _WIN32 //LLP64 long long m_pagesize; #else //LP64 long m_pagesize; #endif std::map m_result; }; #endif ukui-screensaver/tests/kt-test-utils/cpp-stub/elfio.hpp0000664000175000017500000045310415172041035022237 0ustar fengfeng /*** Start of inlined file: elfio_dump.hpp ***/ #ifndef ELFIO_DUMP_HPP #define ELFIO_DUMP_HPP #include #include #include #include #include /*** Start of inlined file: elfio.hpp ***/ #ifndef ELFIO_HPP #define ELFIO_HPP #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4996 ) #pragma warning( disable : 4355 ) #pragma warning( disable : 4244 ) #endif #include #include #include #include #include #include #include #include /*** Start of inlined file: elf_types.hpp ***/ #ifndef ELFTYPES_H #define ELFTYPES_H #ifndef ELFIO_NO_OWN_TYPES #if !defined( ELFIO_NO_CSTDINT ) && !defined( ELFIO_NO_INTTYPES ) #include #else typedef unsigned char uint8_t; typedef signed char int8_t; typedef unsigned short uint16_t; typedef signed short int16_t; #ifdef _MSC_VER typedef unsigned __int32 uint32_t; typedef signed __int32 int32_t; typedef unsigned __int64 uint64_t; typedef signed __int64 int64_t; #else typedef unsigned int uint32_t; typedef signed int int32_t; typedef unsigned long long uint64_t; typedef signed long long int64_t; #endif // _MSC_VER #endif // ELFIO_NO_CSTDINT #endif // ELFIO_NO_OWN_TYPES namespace ELFIO { // Attention! Platform depended definitions. typedef uint16_t Elf_Half; typedef uint32_t Elf_Word; typedef int32_t Elf_Sword; typedef uint64_t Elf_Xword; typedef int64_t Elf_Sxword; typedef uint32_t Elf32_Addr; typedef uint32_t Elf32_Off; typedef uint64_t Elf64_Addr; typedef uint64_t Elf64_Off; #define Elf32_Half Elf_Half #define Elf64_Half Elf_Half #define Elf32_Word Elf_Word #define Elf64_Word Elf_Word #define Elf32_Sword Elf_Sword #define Elf64_Sword Elf_Sword /////////////////////// // ELF Header Constants // File type #define ET_NONE 0 #define ET_REL 1 #define ET_EXEC 2 #define ET_DYN 3 #define ET_CORE 4 #define ET_LOOS 0xFE00 #define ET_HIOS 0xFEFF #define ET_LOPROC 0xFF00 #define ET_HIPROC 0xFFFF #define EM_NONE 0 // No machine #define EM_M32 1 // AT&T WE 32100 #define EM_SPARC 2 // SUN SPARC #define EM_386 3 // Intel 80386 #define EM_68K 4 // Motorola m68k family #define EM_88K 5 // Motorola m88k family #define EM_486 6 // Intel 80486// Reserved for future use #define EM_860 7 // Intel 80860 #define EM_MIPS 8 // MIPS R3000 (officially, big-endian only) #define EM_S370 9 // IBM System/370 #define EM_MIPS_RS3_LE \ 10 // MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated #define EM_res011 11 // Reserved #define EM_res012 12 // Reserved #define EM_res013 13 // Reserved #define EM_res014 14 // Reserved #define EM_PARISC 15 // HPPA #define EM_res016 16 // Reserved #define EM_VPP550 17 // Fujitsu VPP500 #define EM_SPARC32PLUS 18 // Sun's "v8plus" #define EM_960 19 // Intel 80960 #define EM_PPC 20 // PowerPC #define EM_PPC64 21 // 64-bit PowerPC #define EM_S390 22 // IBM S/390 #define EM_SPU 23 // Sony/Toshiba/IBM SPU #define EM_res024 24 // Reserved #define EM_res025 25 // Reserved #define EM_res026 26 // Reserved #define EM_res027 27 // Reserved #define EM_res028 28 // Reserved #define EM_res029 29 // Reserved #define EM_res030 30 // Reserved #define EM_res031 31 // Reserved #define EM_res032 32 // Reserved #define EM_res033 33 // Reserved #define EM_res034 34 // Reserved #define EM_res035 35 // Reserved #define EM_V800 36 // NEC V800 series #define EM_FR20 37 // Fujitsu FR20 #define EM_RH32 38 // TRW RH32 #define EM_MCORE 39 // Motorola M*Core // May also be taken by Fujitsu MMA #define EM_RCE 39 // Old name for MCore #define EM_ARM 40 // ARM #define EM_OLD_ALPHA 41 // Digital Alpha #define EM_SH 42 // Renesas (formerly Hitachi) / SuperH SH #define EM_SPARCV9 43 // SPARC v9 64-bit #define EM_TRICORE 44 // Siemens Tricore embedded processor #define EM_ARC 45 // ARC Cores #define EM_H8_300 46 // Renesas (formerly Hitachi) H8/300 #define EM_H8_300H 47 // Renesas (formerly Hitachi) H8/300H #define EM_H8S 48 // Renesas (formerly Hitachi) H8S #define EM_H8_500 49 // Renesas (formerly Hitachi) H8/500 #define EM_IA_64 50 // Intel IA-64 Processor #define EM_MIPS_X 51 // Stanford MIPS-X #define EM_COLDFIRE 52 // Motorola Coldfire #define EM_68HC12 53 // Motorola M68HC12 #define EM_MMA 54 // Fujitsu Multimedia Accelerator #define EM_PCP 55 // Siemens PCP #define EM_NCPU 56 // Sony nCPU embedded RISC processor #define EM_NDR1 57 // Denso NDR1 microprocesspr #define EM_STARCORE 58 // Motorola Star*Core processor #define EM_ME16 59 // Toyota ME16 processor #define EM_ST100 60 // STMicroelectronics ST100 processor #define EM_TINYJ 61 // Advanced Logic Corp. TinyJ embedded processor #define EM_X86_64 62 // Advanced Micro Devices X86-64 processor #define EM_PDSP 63 // Sony DSP Processor #define EM_PDP10 64 // Digital Equipment Corp. PDP-10 #define EM_PDP11 65 // Digital Equipment Corp. PDP-11 #define EM_FX66 66 // Siemens FX66 microcontroller #define EM_ST9PLUS 67 // STMicroelectronics ST9+ 8/16 bit microcontroller #define EM_ST7 68 // STMicroelectronics ST7 8-bit microcontroller #define EM_68HC16 69 // Motorola MC68HC16 Microcontroller #define EM_68HC11 70 // Motorola MC68HC11 Microcontroller #define EM_68HC08 71 // Motorola MC68HC08 Microcontroller #define EM_68HC05 72 // Motorola MC68HC05 Microcontroller #define EM_SVX 73 // Silicon Graphics SVx #define EM_ST19 74 // STMicroelectronics ST19 8-bit cpu #define EM_VAX 75 // Digital VAX #define EM_CRIS 76 // Axis Communications 32-bit embedded processor #define EM_JAVELIN 77 // Infineon Technologies 32-bit embedded cpu #define EM_FIREPATH 78 // Element 14 64-bit DSP processor #define EM_ZSP 79 // LSI Logic's 16-bit DSP processor #define EM_MMIX 80 // Donald Knuth's educational 64-bit processor #define EM_HUANY 81 // Harvard's machine-independent format #define EM_PRISM 82 // SiTera Prism #define EM_AVR 83 // Atmel AVR 8-bit microcontroller #define EM_FR30 84 // Fujitsu FR30 #define EM_D10V 85 // Mitsubishi D10V #define EM_D30V 86 // Mitsubishi D30V #define EM_V850 87 // NEC v850 #define EM_M32R 88 // Renesas M32R (formerly Mitsubishi M32R) #define EM_MN10300 89 // Matsushita MN10300 #define EM_MN10200 90 // Matsushita MN10200 #define EM_PJ 91 // picoJava #define EM_OPENRISC 92 // OpenRISC 32-bit embedded processor #define EM_ARC_A5 93 // ARC Cores Tangent-A5 #define EM_XTENSA 94 // Tensilica Xtensa Architecture #define EM_VIDEOCORE 95 // Alphamosaic VideoCore processor #define EM_TMM_GPP 96 // Thompson Multimedia General Purpose Processor #define EM_NS32K 97 // National Semiconductor 32000 series #define EM_TPC 98 // Tenor Network TPC processor #define EM_SNP1K 99 // Trebia SNP 1000 processor #define EM_ST200 100 // STMicroelectronics ST200 microcontroller #define EM_IP2K 101 // Ubicom IP2022 micro controller #define EM_MAX 102 // MAX Processor #define EM_CR 103 // National Semiconductor CompactRISC #define EM_F2MC16 104 // Fujitsu F2MC16 #define EM_MSP430 105 // TI msp430 micro controller #define EM_BLACKFIN 106 // ADI Blackfin #define EM_SE_C33 107 // S1C33 Family of Seiko Epson processors #define EM_SEP 108 // Sharp embedded microprocessor #define EM_ARCA 109 // Arca RISC Microprocessor #define EM_UNICORE \ 110 // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University #define EM_EXCESS 111 // eXcess: 16/32/64-bit configurable embedded CPU #define EM_DXP 112 // Icera Semiconductor Inc. Deep Execution Processor #define EM_ALTERA_NIOS2 113 // Altera Nios II soft-core processor #define EM_CRX 114 // National Semiconductor CRX #define EM_XGATE 115 // Motorola XGATE embedded processor #define EM_C166 116 // Infineon C16x/XC16x processor #define EM_M16C 117 // Renesas M16C series microprocessors #define EM_DSPIC30F \ 118 // Microchip Technology dsPIC30F Digital Signal Controller #define EM_CE 119 // Freescale Communication Engine RISC core #define EM_M32C 120 // Renesas M32C series microprocessors #define EM_res121 121 // Reserved #define EM_res122 122 // Reserved #define EM_res123 123 // Reserved #define EM_res124 124 // Reserved #define EM_res125 125 // Reserved #define EM_res126 126 // Reserved #define EM_res127 127 // Reserved #define EM_res128 128 // Reserved #define EM_res129 129 // Reserved #define EM_res130 130 // Reserved #define EM_TSK3000 131 // Altium TSK3000 core #define EM_RS08 132 // Freescale RS08 embedded processor #define EM_res133 133 // Reserved #define EM_ECOG2 134 // Cyan Technology eCOG2 microprocessor #define EM_SCORE 135 // Sunplus Score #define EM_SCORE7 135 // Sunplus S+core7 RISC processor #define EM_DSP24 136 // New Japan Radio (NJR) 24-bit DSP Processor #define EM_VIDEOCORE3 137 // Broadcom VideoCore III processor #define EM_LATTICEMICO32 138 // RISC processor for Lattice FPGA architecture #define EM_SE_C17 139 // Seiko Epson C17 family #define EM_TI_C6000 140 // Texas Instruments TMS320C6000 DSP family #define EM_TI_C2000 141 // Texas Instruments TMS320C2000 DSP family #define EM_TI_C5500 142 // Texas Instruments TMS320C55x DSP family #define EM_res143 143 // Reserved #define EM_res144 144 // Reserved #define EM_res145 145 // Reserved #define EM_res146 146 // Reserved #define EM_res147 147 // Reserved #define EM_res148 148 // Reserved #define EM_res149 149 // Reserved #define EM_res150 150 // Reserved #define EM_res151 151 // Reserved #define EM_res152 152 // Reserved #define EM_res153 153 // Reserved #define EM_res154 154 // Reserved #define EM_res155 155 // Reserved #define EM_res156 156 // Reserved #define EM_res157 157 // Reserved #define EM_res158 158 // Reserved #define EM_res159 159 // Reserved #define EM_MMDSP_PLUS 160 // STMicroelectronics 64bit VLIW Data Signal Processor #define EM_CYPRESS_M8C 161 // Cypress M8C microprocessor #define EM_R32C 162 // Renesas R32C series microprocessors #define EM_TRIMEDIA 163 // NXP Semiconductors TriMedia architecture family #define EM_QDSP6 164 // QUALCOMM DSP6 Processor #define EM_8051 165 // Intel 8051 and variants #define EM_STXP7X 166 // STMicroelectronics STxP7x family #define EM_NDS32 \ 167 // Andes Technology compact code size embedded RISC processor family #define EM_ECOG1 168 // Cyan Technology eCOG1X family #define EM_ECOG1X 168 // Cyan Technology eCOG1X family #define EM_MAXQ30 169 // Dallas Semiconductor MAXQ30 Core Micro-controllers #define EM_XIMO16 170 // New Japan Radio (NJR) 16-bit DSP Processor #define EM_MANIK 171 // M2000 Reconfigurable RISC Microprocessor #define EM_CRAYNV2 172 // Cray Inc. NV2 vector architecture #define EM_RX 173 // Renesas RX family #define EM_METAG 174 // Imagination Technologies META processor architecture #define EM_MCST_ELBRUS 175 // MCST Elbrus general purpose hardware architecture #define EM_ECOG16 176 // Cyan Technology eCOG16 family #define EM_CR16 177 // National Semiconductor CompactRISC 16-bit processor #define EM_ETPU 178 // Freescale Extended Time Processing Unit #define EM_SLE9X 179 // Infineon Technologies SLE9X core #define EM_L1OM 180 // Intel L1OM #define EM_INTEL181 181 // Reserved by Intel #define EM_INTEL182 182 // Reserved by Intel #define EM_res183 183 // Reserved by ARM #define EM_res184 184 // Reserved by ARM #define EM_AVR32 185 // Atmel Corporation 32-bit microprocessor family #define EM_STM8 186 // STMicroeletronics STM8 8-bit microcontroller #define EM_TILE64 187 // Tilera TILE64 multicore architecture family #define EM_TILEPRO 188 // Tilera TILEPro multicore architecture family #define EM_MICROBLAZE 189 // Xilinx MicroBlaze 32-bit RISC soft processor core #define EM_CUDA 190 // NVIDIA CUDA architecture #define EM_TILEGX 191 // Tilera TILE-Gx multicore architecture family #define EM_CLOUDSHIELD 192 // CloudShield architecture family #define EM_COREA_1ST 193 // KIPO-KAIST Core-A 1st generation processor family #define EM_COREA_2ND 194 // KIPO-KAIST Core-A 2nd generation processor family #define EM_ARC_COMPACT2 195 // Synopsys ARCompact V2 #define EM_OPEN8 196 // Open8 8-bit RISC soft processor core #define EM_RL78 197 // Renesas RL78 family #define EM_VIDEOCORE5 198 // Broadcom VideoCore V processor #define EM_78KOR 199 // Renesas 78KOR family #define EM_56800EX 200 // Freescale 56800EX Digital Signal Controller (DSC) #define EM_BA1 201 // Beyond BA1 CPU architecture #define EM_BA2 202 // Beyond BA2 CPU architecture #define EM_XCORE 203 // XMOS xCORE processor family #define EM_MCHP_PIC 204 // Microchip 8-bit PIC(r) family #define EM_INTEL205 205 // Reserved by Intel #define EM_INTEL206 206 // Reserved by Intel #define EM_INTEL207 207 // Reserved by Intel #define EM_INTEL208 208 // Reserved by Intel #define EM_INTEL209 209 // Reserved by Intel #define EM_KM32 210 // KM211 KM32 32-bit processor #define EM_KMX32 211 // KM211 KMX32 32-bit processor #define EM_KMX16 212 // KM211 KMX16 16-bit processor #define EM_KMX8 213 // KM211 KMX8 8-bit processor #define EM_KVARC 214 // KM211 KVARC processor #define EM_CDP 215 // Paneve CDP architecture family #define EM_COGE 216 // Cognitive Smart Memory Processor #define EM_COOL 217 // iCelero CoolEngine #define EM_NORC 218 // Nanoradio Optimized RISC #define EM_CSR_KALIMBA 219 // CSR Kalimba architecture family #define EM_Z80 220 // Zilog Z80 #define EM_VISIUM 221 // Controls and Data Services VISIUMcore processor #define EM_FT32 222 // FTDI Chip FT32 high performance 32-bit RISC architecture #define EM_MOXIE 223 // Moxie processor family #define EM_AMDGPU 224 // AMD GPU architecture #define EM_RISCV 243 // RISC-V #define EM_LANAI 244 // Lanai processor #define EM_CEVA 245 // CEVA Processor Architecture Family #define EM_CEVA_X2 246 // CEVA X2 Processor Family #define EM_BPF 247 // Linux BPF – in-kernel virtual machine #define EM_GRAPHCORE_IPU 248 // Graphcore Intelligent Processing Unit #define EM_IMG1 249 // Imagination Technologies #define EM_NFP 250 // Netronome Flow Processor (P) #define EM_CSKY 252 // C-SKY processor family // File version #define EV_NONE 0 #define EV_CURRENT 1 // Identification index #define EI_MAG0 0 #define EI_MAG1 1 #define EI_MAG2 2 #define EI_MAG3 3 #define EI_CLASS 4 #define EI_DATA 5 #define EI_VERSION 6 #define EI_OSABI 7 #define EI_ABIVERSION 8 #define EI_PAD 9 #define EI_NIDENT 16 // Magic number #define ELFMAG0 0x7F #define ELFMAG1 'E' #define ELFMAG2 'L' #define ELFMAG3 'F' // File class #define ELFCLASSNONE 0 #define ELFCLASS32 1 #define ELFCLASS64 2 // Encoding #define ELFDATANONE 0 #define ELFDATA2LSB 1 #define ELFDATA2MSB 2 // OS extensions #define ELFOSABI_NONE 0 // No extensions or unspecified #define ELFOSABI_HPUX 1 // Hewlett-Packard HP-UX #define ELFOSABI_NETBSD 2 // NetBSD #define ELFOSABI_LINUX 3 // Linux #define ELFOSABI_SOLARIS 6 // Sun Solaris #define ELFOSABI_AIX 7 // AIX #define ELFOSABI_IRIX 8 // IRIX #define ELFOSABI_FREEBSD 9 // FreeBSD #define ELFOSABI_TRU64 10 // Compaq TRU64 UNIX #define ELFOSABI_MODESTO 11 // Novell Modesto #define ELFOSABI_OPENBSD 12 // Open BSD #define ELFOSABI_OPENVMS 13 // Open VMS #define ELFOSABI_NSK 14 // Hewlett-Packard Non-Stop Kernel #define ELFOSABI_AROS 15 // Amiga Research OS #define ELFOSABI_FENIXOS 16 // The FenixOS highly scalable multi-core OS // 64-255 Architecture-specific value range #define ELFOSABI_AMDGPU_HSA \ 64 // AMDGPU OS for HSA compatible compute // kernels. #define ELFOSABI_AMDGPU_PAL \ 65 // AMDGPU OS for AMD PAL compatible graphics // shaders and compute kernels. #define ELFOSABI_AMDGPU_MESA3D \ 66 // AMDGPU OS for Mesa3D compatible graphics // shaders and compute kernels. // AMDGPU specific e_flags #define EF_AMDGPU_MACH 0x0ff // AMDGPU processor selection mask. #define EF_AMDGPU_XNACK \ 0x100 // Indicates if the XNACK target feature is // enabled for all code contained in the ELF. // AMDGPU processors #define EF_AMDGPU_MACH_NONE 0x000 // Unspecified processor. #define EF_AMDGPU_MACH_R600_R600 0x001 #define EF_AMDGPU_MACH_R600_R630 0x002 #define EF_AMDGPU_MACH_R600_RS880 0x003 #define EF_AMDGPU_MACH_R600_RV670 0x004 #define EF_AMDGPU_MACH_R600_RV710 0x005 #define EF_AMDGPU_MACH_R600_RV730 0x006 #define EF_AMDGPU_MACH_R600_RV770 0x007 #define EF_AMDGPU_MACH_R600_CEDAR 0x008 #define EF_AMDGPU_MACH_R600_CYPRESS 0x009 #define EF_AMDGPU_MACH_R600_JUNIPER 0x00a #define EF_AMDGPU_MACH_R600_REDWOOD 0x00b #define EF_AMDGPU_MACH_R600_SUMO 0x00c #define EF_AMDGPU_MACH_R600_BARTS 0x00d #define EF_AMDGPU_MACH_R600_CAICOS 0x00e #define EF_AMDGPU_MACH_R600_CAYMAN 0x00f #define EF_AMDGPU_MACH_R600_TURKS 0x010 #define EF_AMDGPU_MACH_R600_RESERVED_FIRST 0x011 #define EF_AMDGPU_MACH_R600_RESERVED_LAST 0x01f #define EF_AMDGPU_MACH_R600_FIRST EF_AMDGPU_MACH_R600_R600 #define EF_AMDGPU_MACH_R600_LAST EF_AMDGPU_MACH_R600_TURKS #define EF_AMDGPU_MACH_AMDGCN_GFX600 0x020 #define EF_AMDGPU_MACH_AMDGCN_GFX601 0x021 #define EF_AMDGPU_MACH_AMDGCN_GFX700 0x022 #define EF_AMDGPU_MACH_AMDGCN_GFX701 0x023 #define EF_AMDGPU_MACH_AMDGCN_GFX702 0x024 #define EF_AMDGPU_MACH_AMDGCN_GFX703 0x025 #define EF_AMDGPU_MACH_AMDGCN_GFX704 0x026 #define EF_AMDGPU_MACH_AMDGCN_GFX801 0x028 #define EF_AMDGPU_MACH_AMDGCN_GFX802 0x029 #define EF_AMDGPU_MACH_AMDGCN_GFX803 0x02a #define EF_AMDGPU_MACH_AMDGCN_GFX810 0x02b #define EF_AMDGPU_MACH_AMDGCN_GFX900 0x02c #define EF_AMDGPU_MACH_AMDGCN_GFX902 0x02d #define EF_AMDGPU_MACH_AMDGCN_GFX904 0x02e #define EF_AMDGPU_MACH_AMDGCN_GFX906 0x02f #define EF_AMDGPU_MACH_AMDGCN_RESERVED0 0x027 #define EF_AMDGPU_MACH_AMDGCN_RESERVED1 0x030 #define EF_AMDGPU_MACH_AMDGCN_FIRST EF_AMDGPU_MACH_AMDGCN_GFX600 #define EF_AMDGPU_MACH_AMDGCN_LAST EF_AMDGPU_MACH_AMDGCN_GFX906 ///////////////////// // Sections constants // Section indexes #define SHN_UNDEF 0 #define SHN_LORESERVE 0xFF00 #define SHN_LOPROC 0xFF00 #define SHN_HIPROC 0xFF1F #define SHN_LOOS 0xFF20 #define SHN_HIOS 0xFF3F #define SHN_ABS 0xFFF1 #define SHN_COMMON 0xFFF2 #define SHN_XINDEX 0xFFFF #define SHN_HIRESERVE 0xFFFF // Section types #define SHT_NULL 0 #define SHT_PROGBITS 1 #define SHT_SYMTAB 2 #define SHT_STRTAB 3 #define SHT_RELA 4 #define SHT_HASH 5 #define SHT_DYNAMIC 6 #define SHT_NOTE 7 #define SHT_NOBITS 8 #define SHT_REL 9 #define SHT_SHLIB 10 #define SHT_DYNSYM 11 #define SHT_INIT_ARRAY 14 #define SHT_FINI_ARRAY 15 #define SHT_PREINIT_ARRAY 16 #define SHT_GROUP 17 #define SHT_SYMTAB_SHNDX 18 #define SHT_LOOS 0x60000000 #define SHT_HIOS 0x6fffffff #define SHT_LOPROC 0x70000000 #define SHT_HIPROC 0x7FFFFFFF #define SHT_LOUSER 0x80000000 #define SHT_HIUSER 0xFFFFFFFF // Section attribute flags #define SHF_WRITE 0x1 #define SHF_ALLOC 0x2 #define SHF_EXECINSTR 0x4 #define SHF_MERGE 0x10 #define SHF_STRINGS 0x20 #define SHF_INFO_LINK 0x40 #define SHF_LINK_ORDER 0x80 #define SHF_OS_NONCONFORMING 0x100 #define SHF_GROUP 0x200 #define SHF_TLS 0x400 #define SHF_MASKOS 0x0ff00000 #define SHF_MASKPROC 0xF0000000 // Section group flags #define GRP_COMDAT 0x1 #define GRP_MASKOS 0x0ff00000 #define GRP_MASKPROC 0xf0000000 // Symbol binding #define STB_LOCAL 0 #define STB_GLOBAL 1 #define STB_WEAK 2 #define STB_LOOS 10 #define STB_HIOS 12 #define STB_MULTIDEF 13 #define STB_LOPROC 13 #define STB_HIPROC 15 // Note types #define NT_AMDGPU_METADATA 1 #define NT_AMD_AMDGPU_HSA_METADATA 10 #define NT_AMD_AMDGPU_ISA 11 #define NT_AMD_AMDGPU_PAL_METADATA 12 // Symbol types #define STT_NOTYPE 0 #define STT_OBJECT 1 #define STT_FUNC 2 #define STT_SECTION 3 #define STT_FILE 4 #define STT_COMMON 5 #define STT_TLS 6 #define STT_LOOS 10 #define STT_AMDGPU_HSA_KERNEL 10 #define STT_HIOS 12 #define STT_LOPROC 13 #define STT_HIPROC 15 // Symbol visibility #define STV_DEFAULT 0 #define STV_INTERNAL 1 #define STV_HIDDEN 2 #define STV_PROTECTED 3 // Undefined name #define STN_UNDEF 0 // Relocation types #define R_386_NONE 0 #define R_X86_64_NONE 0 #define R_AMDGPU_NONE 0 #define R_386_32 1 #define R_X86_64_64 1 #define R_AMDGPU_ABS32_LO 1 #define R_386_PC32 2 #define R_X86_64_PC32 2 #define R_AMDGPU_ABS32_HI 2 #define R_386_GOT32 3 #define R_X86_64_GOT32 3 #define R_AMDGPU_ABS64 3 #define R_386_PLT32 4 #define R_X86_64_PLT32 4 #define R_AMDGPU_REL32 4 #define R_386_COPY 5 #define R_X86_64_COPY 5 #define R_AMDGPU_REL64 5 #define R_386_GLOB_DAT 6 #define R_X86_64_GLOB_DAT 6 #define R_AMDGPU_ABS32 6 #define R_386_JMP_SLOT 7 #define R_X86_64_JUMP_SLOT 7 #define R_AMDGPU_GOTPCREL 7 #define R_386_RELATIVE 8 #define R_X86_64_RELATIVE 8 #define R_AMDGPU_GOTPCREL32_LO 8 #define R_386_GOTOFF 9 #define R_X86_64_GOTPCREL 9 #define R_AMDGPU_GOTPCREL32_HI 9 #define R_386_GOTPC 10 #define R_X86_64_32 10 #define R_AMDGPU_REL32_LO 10 #define R_386_32PLT 11 #define R_X86_64_32S 11 #define R_AMDGPU_REL32_HI 11 #define R_X86_64_16 12 #define R_X86_64_PC16 13 #define R_AMDGPU_RELATIVE64 13 #define R_386_TLS_TPOFF 14 #define R_X86_64_8 14 #define R_386_TLS_IE 15 #define R_X86_64_PC8 15 #define R_386_TLS_GOTIE 16 #define R_X86_64_DTPMOD64 16 #define R_386_TLS_LE 17 #define R_X86_64_DTPOFF64 17 #define R_386_TLS_GD 18 #define R_X86_64_TPOFF64 18 #define R_386_TLS_LDM 19 #define R_X86_64_TLSGD 19 #define R_386_16 20 #define R_X86_64_TLSLD 20 #define R_386_PC16 21 #define R_X86_64_DTPOFF32 21 #define R_386_8 22 #define R_X86_64_GOTTPOFF 22 #define R_386_PC8 23 #define R_X86_64_TPOFF32 23 #define R_386_TLS_GD_32 24 #define R_X86_64_PC64 24 #define R_386_TLS_GD_PUSH 25 #define R_X86_64_GOTOFF64 25 #define R_386_TLS_GD_CALL 26 #define R_X86_64_GOTPC32 26 #define R_386_TLS_GD_POP 27 #define R_X86_64_GOT64 27 #define R_386_TLS_LDM_32 28 #define R_X86_64_GOTPCREL64 28 #define R_386_TLS_LDM_PUSH 29 #define R_X86_64_GOTPC64 29 #define R_386_TLS_LDM_CALL 30 #define R_X86_64_GOTPLT64 30 #define R_386_TLS_LDM_POP 31 #define R_X86_64_PLTOFF64 31 #define R_386_TLS_LDO_32 32 #define R_386_TLS_IE_32 33 #define R_386_TLS_LE_32 34 #define R_X86_64_GOTPC32_TLSDESC 34 #define R_386_TLS_DTPMOD32 35 #define R_X86_64_TLSDESC_CALL 35 #define R_386_TLS_DTPOFF32 36 #define R_X86_64_TLSDESC 36 #define R_386_TLS_TPOFF32 37 #define R_X86_64_IRELATIVE 37 #define R_386_SIZE32 38 #define R_386_TLS_GOTDESC 39 #define R_386_TLS_DESC_CALL 40 #define R_386_TLS_DESC 41 #define R_386_IRELATIVE 42 #define R_386_GOT32X 43 #define R_X86_64_GNU_VTINHERIT 250 #define R_X86_64_GNU_VTENTRY 251 // Segment types #define PT_NULL 0 #define PT_LOAD 1 #define PT_DYNAMIC 2 #define PT_INTERP 3 #define PT_NOTE 4 #define PT_SHLIB 5 #define PT_PHDR 6 #define PT_TLS 7 #define PT_LOOS 0x60000000 #define PT_HIOS 0x6fffffff #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7FFFFFFF // Segment flags #define PF_X 1 // Execute #define PF_W 2 // Write #define PF_R 4 // Read #define PF_MASKOS 0x0ff00000 // Unspecified #define PF_MASKPROC 0xf0000000 // Unspecified // Dynamic Array Tags #define DT_NULL 0 #define DT_NEEDED 1 #define DT_PLTRELSZ 2 #define DT_PLTGOT 3 #define DT_HASH 4 #define DT_STRTAB 5 #define DT_SYMTAB 6 #define DT_RELA 7 #define DT_RELASZ 8 #define DT_RELAENT 9 #define DT_STRSZ 10 #define DT_SYMENT 11 #define DT_INIT 12 #define DT_FINI 13 #define DT_SONAME 14 #define DT_RPATH 15 #define DT_SYMBOLIC 16 #define DT_REL 17 #define DT_RELSZ 18 #define DT_RELENT 19 #define DT_PLTREL 20 #define DT_DEBUG 21 #define DT_TEXTREL 22 #define DT_JMPREL 23 #define DT_BIND_NOW 24 #define DT_INIT_ARRAY 25 #define DT_FINI_ARRAY 26 #define DT_INIT_ARRAYSZ 27 #define DT_FINI_ARRAYSZ 28 #define DT_RUNPATH 29 #define DT_FLAGS 30 #define DT_ENCODING 32 #define DT_PREINIT_ARRAY 32 #define DT_PREINIT_ARRAYSZ 33 #define DT_MAXPOSTAGS 34 #define DT_LOOS 0x6000000D #define DT_HIOS 0x6ffff000 #define DT_LOPROC 0x70000000 #define DT_HIPROC 0x7FFFFFFF // DT_FLAGS values #define DF_ORIGIN 0x1 #define DF_SYMBOLIC 0x2 #define DF_TEXTREL 0x4 #define DF_BIND_NOW 0x8 #define DF_STATIC_TLS 0x10 // ELF file header struct Elf32_Ehdr { unsigned char e_ident[EI_NIDENT]; Elf_Half e_type; Elf_Half e_machine; Elf_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf_Word e_flags; Elf_Half e_ehsize; Elf_Half e_phentsize; Elf_Half e_phnum; Elf_Half e_shentsize; Elf_Half e_shnum; Elf_Half e_shstrndx; }; struct Elf64_Ehdr { unsigned char e_ident[EI_NIDENT]; Elf_Half e_type; Elf_Half e_machine; Elf_Word e_version; Elf64_Addr e_entry; Elf64_Off e_phoff; Elf64_Off e_shoff; Elf_Word e_flags; Elf_Half e_ehsize; Elf_Half e_phentsize; Elf_Half e_phnum; Elf_Half e_shentsize; Elf_Half e_shnum; Elf_Half e_shstrndx; }; // Section header struct Elf32_Shdr { Elf_Word sh_name; Elf_Word sh_type; Elf_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf_Word sh_size; Elf_Word sh_link; Elf_Word sh_info; Elf_Word sh_addralign; Elf_Word sh_entsize; }; struct Elf64_Shdr { Elf_Word sh_name; Elf_Word sh_type; Elf_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf_Xword sh_size; Elf_Word sh_link; Elf_Word sh_info; Elf_Xword sh_addralign; Elf_Xword sh_entsize; }; // Segment header struct Elf32_Phdr { Elf_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf_Word p_filesz; Elf_Word p_memsz; Elf_Word p_flags; Elf_Word p_align; }; struct Elf64_Phdr { Elf_Word p_type; Elf_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf_Xword p_filesz; Elf_Xword p_memsz; Elf_Xword p_align; }; // Symbol table entry struct Elf32_Sym { Elf_Word st_name; Elf32_Addr st_value; Elf_Word st_size; unsigned char st_info; unsigned char st_other; Elf_Half st_shndx; }; struct Elf64_Sym { Elf_Word st_name; unsigned char st_info; unsigned char st_other; Elf_Half st_shndx; Elf64_Addr st_value; Elf_Xword st_size; }; #define ELF_ST_BIND( i ) ( ( i ) >> 4 ) #define ELF_ST_TYPE( i ) ( (i)&0xf ) #define ELF_ST_INFO( b, t ) ( ( ( b ) << 4 ) + ( (t)&0xf ) ) #define ELF_ST_VISIBILITY( o ) ( (o)&0x3 ) // Relocation entries struct Elf32_Rel { Elf32_Addr r_offset; Elf_Word r_info; }; struct Elf32_Rela { Elf32_Addr r_offset; Elf_Word r_info; Elf_Sword r_addend; }; struct Elf64_Rel { Elf64_Addr r_offset; Elf_Xword r_info; }; struct Elf64_Rela { Elf64_Addr r_offset; Elf_Xword r_info; Elf_Sxword r_addend; }; #define ELF32_R_SYM( i ) ( ( i ) >> 8 ) #define ELF32_R_TYPE( i ) ( (unsigned char)( i ) ) #define ELF32_R_INFO( s, t ) ( ( ( s ) << 8 ) + (unsigned char)( t ) ) #define ELF64_R_SYM( i ) ( ( i ) >> 32 ) #define ELF64_R_TYPE( i ) ( (i)&0xffffffffL ) #define ELF64_R_INFO( s, t ) \ ( ( ( ( int64_t )( s ) ) << 32 ) + ( (t)&0xffffffffL ) ) // Dynamic structure struct Elf32_Dyn { Elf_Sword d_tag; union { Elf_Word d_val; Elf32_Addr d_ptr; } d_un; }; struct Elf64_Dyn { Elf_Sxword d_tag; union { Elf_Xword d_val; Elf64_Addr d_ptr; } d_un; }; } // namespace ELFIO #endif // ELFTYPES_H /*** End of inlined file: elf_types.hpp ***/ /*** Start of inlined file: elfio_version.hpp ***/ #define ELFIO_VERSION "3.8" /*** End of inlined file: elfio_version.hpp ***/ /*** Start of inlined file: elfio_utils.hpp ***/ #ifndef ELFIO_UTILS_HPP #define ELFIO_UTILS_HPP #define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \ TYPE get_##NAME() const { return ( *convertor )( FIELD ); } #define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \ void set_##NAME( TYPE value ) \ { \ FIELD = value; \ FIELD = ( *convertor )( FIELD ); \ } #define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \ TYPE get_##NAME() const { return ( *convertor )( FIELD ); } \ void set_##NAME( TYPE value ) \ { \ FIELD = value; \ FIELD = ( *convertor )( FIELD ); \ } #define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0 #define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \ virtual void set_##NAME( TYPE value ) = 0 #define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \ virtual TYPE get_##NAME() const = 0; \ virtual void set_##NAME( TYPE value ) = 0 namespace ELFIO { //------------------------------------------------------------------------------ class endianess_convertor { public: //------------------------------------------------------------------------------ endianess_convertor() { need_conversion = false; } //------------------------------------------------------------------------------ void setup( unsigned char elf_file_encoding ) { need_conversion = ( elf_file_encoding != get_host_encoding() ); } //------------------------------------------------------------------------------ uint64_t operator()( uint64_t value ) const { if ( !need_conversion ) { return value; } value = ( ( value & 0x00000000000000FFull ) << 56 ) | ( ( value & 0x000000000000FF00ull ) << 40 ) | ( ( value & 0x0000000000FF0000ull ) << 24 ) | ( ( value & 0x00000000FF000000ull ) << 8 ) | ( ( value & 0x000000FF00000000ull ) >> 8 ) | ( ( value & 0x0000FF0000000000ull ) >> 24 ) | ( ( value & 0x00FF000000000000ull ) >> 40 ) | ( ( value & 0xFF00000000000000ull ) >> 56 ); return value; } //------------------------------------------------------------------------------ int64_t operator()( int64_t value ) const { if ( !need_conversion ) { return value; } return ( int64_t )( *this )( (uint64_t)value ); } //------------------------------------------------------------------------------ uint32_t operator()( uint32_t value ) const { if ( !need_conversion ) { return value; } value = ( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) | ( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 ); return value; } //------------------------------------------------------------------------------ int32_t operator()( int32_t value ) const { if ( !need_conversion ) { return value; } return ( int32_t )( *this )( (uint32_t)value ); } //------------------------------------------------------------------------------ uint16_t operator()( uint16_t value ) const { if ( !need_conversion ) { return value; } value = ( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 ); return value; } //------------------------------------------------------------------------------ int16_t operator()( int16_t value ) const { if ( !need_conversion ) { return value; } return ( int16_t )( *this )( (uint16_t)value ); } //------------------------------------------------------------------------------ int8_t operator()( int8_t value ) const { return value; } //------------------------------------------------------------------------------ uint8_t operator()( uint8_t value ) const { return value; } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ unsigned char get_host_encoding() const { static const int tmp = 1; if ( 1 == *(const char*)&tmp ) { return ELFDATA2LSB; } else { return ELFDATA2MSB; } } //------------------------------------------------------------------------------ private: bool need_conversion; }; //------------------------------------------------------------------------------ inline uint32_t elf_hash( const unsigned char* name ) { uint32_t h = 0, g; while ( *name ) { h = ( h << 4 ) + *name++; g = h & 0xf0000000; if ( g != 0 ) h ^= g >> 24; h &= ~g; } return h; } } // namespace ELFIO #endif // ELFIO_UTILS_HPP /*** End of inlined file: elfio_utils.hpp ***/ /*** Start of inlined file: elfio_header.hpp ***/ #ifndef ELF_HEADER_HPP #define ELF_HEADER_HPP #include namespace ELFIO { class elf_header { public: virtual ~elf_header(){}; virtual bool load( std::istream& stream ) = 0; virtual bool save( std::ostream& stream ) const = 0; // ELF header functions ELFIO_GET_ACCESS_DECL( unsigned char, class ); ELFIO_GET_ACCESS_DECL( unsigned char, elf_version ); ELFIO_GET_ACCESS_DECL( unsigned char, encoding ); ELFIO_GET_ACCESS_DECL( Elf_Half, header_size ); ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size ); ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version ); ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi ); ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset ); ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index ); }; template struct elf_header_impl_types; template <> struct elf_header_impl_types { typedef Elf32_Phdr Phdr_type; typedef Elf32_Shdr Shdr_type; static const unsigned char file_class = ELFCLASS32; }; template <> struct elf_header_impl_types { typedef Elf64_Phdr Phdr_type; typedef Elf64_Shdr Shdr_type; static const unsigned char file_class = ELFCLASS64; }; template class elf_header_impl : public elf_header { public: //------------------------------------------------------------------------------ elf_header_impl( endianess_convertor* convertor_, unsigned char encoding ) { convertor = convertor_; std::fill_n( reinterpret_cast( &header ), sizeof( header ), '\0' ); header.e_ident[EI_MAG0] = ELFMAG0; header.e_ident[EI_MAG1] = ELFMAG1; header.e_ident[EI_MAG2] = ELFMAG2; header.e_ident[EI_MAG3] = ELFMAG3; header.e_ident[EI_CLASS] = elf_header_impl_types::file_class; header.e_ident[EI_DATA] = encoding; header.e_ident[EI_VERSION] = EV_CURRENT; header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT ); header.e_ehsize = ( sizeof( header ) ); header.e_ehsize = ( *convertor )( header.e_ehsize ); header.e_shstrndx = ( *convertor )( (Elf_Half)1 ); header.e_phentsize = sizeof( typename elf_header_impl_types::Phdr_type ); header.e_shentsize = sizeof( typename elf_header_impl_types::Shdr_type ); header.e_phentsize = ( *convertor )( header.e_phentsize ); header.e_shentsize = ( *convertor )( header.e_shentsize ); } //------------------------------------------------------------------------------ bool load( std::istream& stream ) { stream.seekg( 0 ); stream.read( reinterpret_cast( &header ), sizeof( header ) ); return ( stream.gcount() == sizeof( header ) ); } //------------------------------------------------------------------------------ bool save( std::ostream& stream ) const { stream.seekp( 0 ); stream.write( reinterpret_cast( &header ), sizeof( header ) ); return stream.good(); } //------------------------------------------------------------------------------ // ELF header functions ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] ); ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] ); ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] ); ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize ); ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize ); ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize ); ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version ); ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] ); ELFIO_GET_SET_ACCESS( unsigned char, abi_version, header.e_ident[EI_ABIVERSION] ); ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type ); ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine ); ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags ); ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx ); ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry ); ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum ); ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff ); ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum ); ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff ); private: T header; endianess_convertor* convertor; }; } // namespace ELFIO #endif // ELF_HEADER_HPP /*** End of inlined file: elfio_header.hpp ***/ /*** Start of inlined file: elfio_section.hpp ***/ #ifndef ELFIO_SECTION_HPP #define ELFIO_SECTION_HPP #include #include #include namespace ELFIO { class section { friend class elfio; public: virtual ~section(){}; ELFIO_GET_ACCESS_DECL( Elf_Half, index ); ELFIO_GET_SET_ACCESS_DECL( std::string, name ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset ); ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); virtual const char* get_data() const = 0; virtual void set_data( const char* pData, Elf_Word size ) = 0; virtual void set_data( const std::string& data ) = 0; virtual void append_data( const char* pData, Elf_Word size ) = 0; virtual void append_data( const std::string& data ) = 0; virtual size_t get_stream_size() const = 0; virtual void set_stream_size( size_t value ) = 0; protected: ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); ELFIO_SET_ACCESS_DECL( Elf_Half, index ); virtual void load( std::istream& stream, std::streampos header_offset ) = 0; virtual void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) = 0; virtual bool is_address_initialized() const = 0; }; template class section_impl : public section { public: //------------------------------------------------------------------------------ section_impl( const endianess_convertor* convertor_ ) : convertor( convertor_ ) { std::fill_n( reinterpret_cast( &header ), sizeof( header ), '\0' ); is_address_set = false; data = 0; data_size = 0; index = 0; stream_size = 0; } //------------------------------------------------------------------------------ ~section_impl() { delete[] data; } //------------------------------------------------------------------------------ // Section info functions ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type ); ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags ); ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size ); ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link ); ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info ); ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign ); ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize ); ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name ); ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr ); //------------------------------------------------------------------------------ Elf_Half get_index() const { return index; } //------------------------------------------------------------------------------ std::string get_name() const { return name; } //------------------------------------------------------------------------------ void set_name( std::string name_ ) { name = name_; } //------------------------------------------------------------------------------ void set_address( Elf64_Addr value ) { header.sh_addr = value; header.sh_addr = ( *convertor )( header.sh_addr ); is_address_set = true; } //------------------------------------------------------------------------------ bool is_address_initialized() const { return is_address_set; } //------------------------------------------------------------------------------ const char* get_data() const { return data; } //------------------------------------------------------------------------------ void set_data( const char* raw_data, Elf_Word size ) { if ( get_type() != SHT_NOBITS ) { delete[] data; data = new ( std::nothrow ) char[size]; if ( 0 != data && 0 != raw_data ) { data_size = size; std::copy( raw_data, raw_data + size, data ); } else { data_size = 0; } } set_size( data_size ); } //------------------------------------------------------------------------------ void set_data( const std::string& str_data ) { return set_data( str_data.c_str(), (Elf_Word)str_data.size() ); } //------------------------------------------------------------------------------ void append_data( const char* raw_data, Elf_Word size ) { if ( get_type() != SHT_NOBITS ) { if ( get_size() + size < data_size ) { std::copy( raw_data, raw_data + size, data + get_size() ); } else { data_size = 2 * ( data_size + size ); char* new_data = new ( std::nothrow ) char[data_size]; if ( 0 != new_data ) { std::copy( data, data + get_size(), new_data ); std::copy( raw_data, raw_data + size, new_data + get_size() ); delete[] data; data = new_data; } else { size = 0; } } set_size( get_size() + size ); } } //------------------------------------------------------------------------------ void append_data( const std::string& str_data ) { return append_data( str_data.c_str(), (Elf_Word)str_data.size() ); } //------------------------------------------------------------------------------ protected: //------------------------------------------------------------------------------ ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset ); //------------------------------------------------------------------------------ void set_index( Elf_Half value ) { index = value; } //------------------------------------------------------------------------------ void load( std::istream& stream, std::streampos header_offset ) { std::fill_n( reinterpret_cast( &header ), sizeof( header ), '\0' ); stream.seekg( 0, stream.end ); set_stream_size( stream.tellg() ); stream.seekg( header_offset ); stream.read( reinterpret_cast( &header ), sizeof( header ) ); Elf_Xword size = get_size(); if ( 0 == data && SHT_NULL != get_type() && SHT_NOBITS != get_type() && size < get_stream_size() ) { data = new ( std::nothrow ) char[size + 1]; if ( ( 0 != size ) && ( 0 != data ) ) { stream.seekg( ( *convertor )( header.sh_offset ) ); stream.read( data, size ); data[size] = 0; // Ensure data is ended with 0 to avoid oob read data_size = size; } else { data_size = 0; } } } //------------------------------------------------------------------------------ void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) { if ( 0 != get_index() ) { header.sh_offset = data_offset; header.sh_offset = ( *convertor )( header.sh_offset ); } save_header( stream, header_offset ); if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL && get_size() != 0 && data != 0 ) { save_data( stream, data_offset ); } } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ void save_header( std::ostream& stream, std::streampos header_offset ) const { stream.seekp( header_offset ); stream.write( reinterpret_cast( &header ), sizeof( header ) ); } //------------------------------------------------------------------------------ void save_data( std::ostream& stream, std::streampos data_offset ) const { stream.seekp( data_offset ); stream.write( get_data(), get_size() ); } //------------------------------------------------------------------------------ size_t get_stream_size() const { return stream_size; } //------------------------------------------------------------------------------ void set_stream_size( size_t value ) { stream_size = value; } //------------------------------------------------------------------------------ private: T header; Elf_Half index; std::string name; char* data; Elf_Word data_size; const endianess_convertor* convertor; bool is_address_set; size_t stream_size; }; } // namespace ELFIO #endif // ELFIO_SECTION_HPP /*** End of inlined file: elfio_section.hpp ***/ /*** Start of inlined file: elfio_segment.hpp ***/ #ifndef ELFIO_SEGMENT_HPP #define ELFIO_SEGMENT_HPP #include #include #include namespace ELFIO { class segment { friend class elfio; public: virtual ~segment(){}; ELFIO_GET_ACCESS_DECL( Elf_Half, index ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address ); ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size ); ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size ); ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); virtual const char* get_data() const = 0; virtual Elf_Half add_section_index( Elf_Half index, Elf_Xword addr_align ) = 0; virtual Elf_Half get_sections_num() const = 0; virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0; virtual bool is_offset_initialized() const = 0; protected: ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); ELFIO_SET_ACCESS_DECL( Elf_Half, index ); virtual const std::vector& get_sections() const = 0; virtual void load( std::istream& stream, std::streampos header_offset ) = 0; virtual void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) = 0; }; //------------------------------------------------------------------------------ template class segment_impl : public segment { public: //------------------------------------------------------------------------------ segment_impl( endianess_convertor* convertor_ ) : stream_size( 0 ), index( 0 ), data( 0 ), convertor( convertor_ ) { is_offset_set = false; std::fill_n( reinterpret_cast( &ph ), sizeof( ph ), '\0' ); } //------------------------------------------------------------------------------ virtual ~segment_impl() { delete[] data; } //------------------------------------------------------------------------------ // Section info functions ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type ); ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags ); ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align ); ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr ); ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr ); ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz ); ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz ); ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset ); size_t stream_size; //------------------------------------------------------------------------------ size_t get_stream_size() const { return stream_size; } //------------------------------------------------------------------------------ void set_stream_size( size_t value ) { stream_size = value; } //------------------------------------------------------------------------------ Elf_Half get_index() const { return index; } //------------------------------------------------------------------------------ const char* get_data() const { return data; } //------------------------------------------------------------------------------ Elf_Half add_section_index( Elf_Half sec_index, Elf_Xword addr_align ) { sections.push_back( sec_index ); if ( addr_align > get_align() ) { set_align( addr_align ); } return (Elf_Half)sections.size(); } //------------------------------------------------------------------------------ Elf_Half get_sections_num() const { return (Elf_Half)sections.size(); } //------------------------------------------------------------------------------ Elf_Half get_section_index_at( Elf_Half num ) const { if ( num < sections.size() ) { return sections[num]; } return Elf_Half( -1 ); } //------------------------------------------------------------------------------ protected: //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void set_offset( Elf64_Off value ) { ph.p_offset = value; ph.p_offset = ( *convertor )( ph.p_offset ); is_offset_set = true; } //------------------------------------------------------------------------------ bool is_offset_initialized() const { return is_offset_set; } //------------------------------------------------------------------------------ const std::vector& get_sections() const { return sections; } //------------------------------------------------------------------------------ void set_index( Elf_Half value ) { index = value; } //------------------------------------------------------------------------------ void load( std::istream& stream, std::streampos header_offset ) { stream.seekg( 0, stream.end ); set_stream_size( stream.tellg() ); stream.seekg( header_offset ); stream.read( reinterpret_cast( &ph ), sizeof( ph ) ); is_offset_set = true; if ( PT_NULL != get_type() && 0 != get_file_size() ) { stream.seekg( ( *convertor )( ph.p_offset ) ); Elf_Xword size = get_file_size(); if ( size > get_stream_size() ) { data = 0; } else { data = new (std::nothrow) char[size + 1]; if ( 0 != data ) { stream.read( data, size ); data[size] = 0; } } } } //------------------------------------------------------------------------------ void save( std::ostream& stream, std::streampos header_offset, std::streampos data_offset ) { ph.p_offset = data_offset; ph.p_offset = ( *convertor )( ph.p_offset ); stream.seekp( header_offset ); stream.write( reinterpret_cast( &ph ), sizeof( ph ) ); } //------------------------------------------------------------------------------ private: T ph; Elf_Half index; char* data; std::vector sections; endianess_convertor* convertor; bool is_offset_set; }; } // namespace ELFIO #endif // ELFIO_SEGMENT_HPP /*** End of inlined file: elfio_segment.hpp ***/ /*** Start of inlined file: elfio_strings.hpp ***/ #ifndef ELFIO_STRINGS_HPP #define ELFIO_STRINGS_HPP #include #include #include namespace ELFIO { //------------------------------------------------------------------------------ template class string_section_accessor_template { public: //------------------------------------------------------------------------------ string_section_accessor_template( S* section_ ) : string_section( section_ ) { } //------------------------------------------------------------------------------ const char* get_string( Elf_Word index ) const { if ( string_section ) { if ( index < string_section->get_size() ) { const char* data = string_section->get_data(); if ( 0 != data ) { return data + index; } } } return 0; } //------------------------------------------------------------------------------ Elf_Word add_string( const char* str ) { Elf_Word current_position = 0; if ( string_section ) { // Strings are addeded to the end of the current section data current_position = (Elf_Word)string_section->get_size(); if ( current_position == 0 ) { char empty_string = '\0'; string_section->append_data( &empty_string, 1 ); current_position++; } string_section->append_data( str, (Elf_Word)std::strlen( str ) + 1 ); } return current_position; } //------------------------------------------------------------------------------ Elf_Word add_string( const std::string& str ) { return add_string( str.c_str() ); } //------------------------------------------------------------------------------ private: S* string_section; }; using string_section_accessor = string_section_accessor_template
; using const_string_section_accessor = string_section_accessor_template; } // namespace ELFIO #endif // ELFIO_STRINGS_HPP /*** End of inlined file: elfio_strings.hpp ***/ #define ELFIO_HEADER_ACCESS_GET( TYPE, FNAME ) \ TYPE get_##FNAME() const { return header ? ( header->get_##FNAME() ) : 0; } #define ELFIO_HEADER_ACCESS_GET_SET( TYPE, FNAME ) \ TYPE get_##FNAME() const \ { \ return header ? ( header->get_##FNAME() ) : 0; \ } \ void set_##FNAME( TYPE val ) \ { \ if ( header ) { \ header->set_##FNAME( val ); \ } \ } namespace ELFIO { //------------------------------------------------------------------------------ class elfio { public: //------------------------------------------------------------------------------ elfio() : sections( this ), segments( this ) { header = 0; current_file_pos = 0; create( ELFCLASS32, ELFDATA2LSB ); } //------------------------------------------------------------------------------ ~elfio() { clean(); } //------------------------------------------------------------------------------ void create( unsigned char file_class, unsigned char encoding ) { clean(); convertor.setup( encoding ); header = create_header( file_class, encoding ); create_mandatory_sections(); } //------------------------------------------------------------------------------ bool load( const std::string& file_name ) { std::ifstream stream; stream.open( file_name.c_str(), std::ios::in | std::ios::binary ); if ( !stream ) { return false; } return load( stream ); } //------------------------------------------------------------------------------ bool load( std::istream& stream ) { clean(); unsigned char e_ident[EI_NIDENT]; // Read ELF file signature stream.read( reinterpret_cast( &e_ident ), sizeof( e_ident ) ); // Is it ELF file? if ( stream.gcount() != sizeof( e_ident ) || e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ) { return false; } if ( ( e_ident[EI_CLASS] != ELFCLASS64 ) && ( e_ident[EI_CLASS] != ELFCLASS32 ) ) { return false; } convertor.setup( e_ident[EI_DATA] ); header = create_header( e_ident[EI_CLASS], e_ident[EI_DATA] ); if ( 0 == header ) { return false; } if ( !header->load( stream ) ) { return false; } load_sections( stream ); bool is_still_good = load_segments( stream ); return is_still_good; } //------------------------------------------------------------------------------ bool save( const std::string& file_name ) { std::ofstream stream; stream.open( file_name.c_str(), std::ios::out | std::ios::binary ); if ( !stream ) { return false; } return save( stream ); } //------------------------------------------------------------------------------ bool save( std::ostream& stream ) { if ( !stream || !header ) { return false; } bool is_still_good = true; // Define layout specific header fields // The position of the segment table is fixed after the header. // The position of the section table is variable and needs to be fixed // before saving. header->set_segments_num( segments.size() ); header->set_segments_offset( segments.size() ? header->get_header_size() : 0 ); header->set_sections_num( sections.size() ); header->set_sections_offset( 0 ); // Layout the first section right after the segment table current_file_pos = header->get_header_size() + header->get_segment_entry_size() * (Elf_Xword)header->get_segments_num(); calc_segment_alignment(); is_still_good = layout_segments_and_their_sections(); is_still_good = is_still_good && layout_sections_without_segments(); is_still_good = is_still_good && layout_section_table(); is_still_good = is_still_good && save_header( stream ); is_still_good = is_still_good && save_sections( stream ); is_still_good = is_still_good && save_segments( stream ); return is_still_good; } //------------------------------------------------------------------------------ // ELF header access functions ELFIO_HEADER_ACCESS_GET( unsigned char, class ); ELFIO_HEADER_ACCESS_GET( unsigned char, elf_version ); ELFIO_HEADER_ACCESS_GET( unsigned char, encoding ); ELFIO_HEADER_ACCESS_GET( Elf_Word, version ); ELFIO_HEADER_ACCESS_GET( Elf_Half, header_size ); ELFIO_HEADER_ACCESS_GET( Elf_Half, section_entry_size ); ELFIO_HEADER_ACCESS_GET( Elf_Half, segment_entry_size ); ELFIO_HEADER_ACCESS_GET_SET( unsigned char, os_abi ); ELFIO_HEADER_ACCESS_GET_SET( unsigned char, abi_version ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, type ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, machine ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Word, flags ); ELFIO_HEADER_ACCESS_GET_SET( Elf64_Addr, entry ); ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, sections_offset ); ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, segments_offset ); ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, section_name_str_index ); //------------------------------------------------------------------------------ const endianess_convertor& get_convertor() const { return convertor; } //------------------------------------------------------------------------------ Elf_Xword get_default_entry_size( Elf_Word section_type ) const { switch ( section_type ) { case SHT_RELA: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Rela ); } else { return sizeof( Elf32_Rela ); } case SHT_REL: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Rel ); } else { return sizeof( Elf32_Rel ); } case SHT_SYMTAB: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Sym ); } else { return sizeof( Elf32_Sym ); } case SHT_DYNAMIC: if ( header->get_class() == ELFCLASS64 ) { return sizeof( Elf64_Dyn ); } else { return sizeof( Elf32_Dyn ); } default: return 0; } } //------------------------------------------------------------------------------ private: bool is_offset_in_section( Elf64_Off offset, const section* sec ) const { return ( offset >= sec->get_offset() ) && ( offset < ( sec->get_offset() + sec->get_size() ) ); } //------------------------------------------------------------------------------ public: //! returns an empty string if no problems are detected, //! or a string containing an error message if problems are found std::string validate() const { // check for overlapping sections in the file for ( int i = 0; i < sections.size(); ++i ) { for ( int j = i + 1; j < sections.size(); ++j ) { const section* a = sections[i]; const section* b = sections[j]; if ( !( a->get_type() & SHT_NOBITS ) && !( b->get_type() & SHT_NOBITS ) && ( a->get_size() > 0 ) && ( b->get_size() > 0 ) && ( a->get_offset() > 0 ) && ( b->get_offset() > 0 ) ) { if ( is_offset_in_section( a->get_offset(), b ) || is_offset_in_section( a->get_offset() + a->get_size() - 1, b ) || is_offset_in_section( b->get_offset(), a ) || is_offset_in_section( b->get_offset() + b->get_size() - 1, a ) ) { return "Sections " + a->get_name() + " and " + b->get_name() + " overlap in file"; } } } } // more checks to be added here... return ""; } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ void clean() { delete header; header = 0; std::vector::const_iterator it; for ( it = sections_.begin(); it != sections_.end(); ++it ) { delete *it; } sections_.clear(); std::vector::const_iterator it1; for ( it1 = segments_.begin(); it1 != segments_.end(); ++it1 ) { delete *it1; } segments_.clear(); } //------------------------------------------------------------------------------ elf_header* create_header( unsigned char file_class, unsigned char encoding ) { elf_header* new_header = 0; if ( file_class == ELFCLASS64 ) { new_header = new elf_header_impl( &convertor, encoding ); } else if ( file_class == ELFCLASS32 ) { new_header = new elf_header_impl( &convertor, encoding ); } else { return 0; } return new_header; } //------------------------------------------------------------------------------ section* create_section() { section* new_section; unsigned char file_class = get_class(); if ( file_class == ELFCLASS64 ) { new_section = new section_impl( &convertor ); } else if ( file_class == ELFCLASS32 ) { new_section = new section_impl( &convertor ); } else { return 0; } new_section->set_index( (Elf_Half)sections_.size() ); sections_.push_back( new_section ); return new_section; } //------------------------------------------------------------------------------ segment* create_segment() { segment* new_segment; unsigned char file_class = header->get_class(); if ( file_class == ELFCLASS64 ) { new_segment = new segment_impl( &convertor ); } else if ( file_class == ELFCLASS32 ) { new_segment = new segment_impl( &convertor ); } else { return 0; } new_segment->set_index( (Elf_Half)segments_.size() ); segments_.push_back( new_segment ); return new_segment; } //------------------------------------------------------------------------------ void create_mandatory_sections() { // Create null section without calling to 'add_section' as no string // section containing section names exists yet section* sec0 = create_section(); sec0->set_index( 0 ); sec0->set_name( "" ); sec0->set_name_string_offset( 0 ); set_section_name_str_index( 1 ); section* shstrtab = sections.add( ".shstrtab" ); shstrtab->set_type( SHT_STRTAB ); shstrtab->set_addr_align( 1 ); } //------------------------------------------------------------------------------ Elf_Half load_sections( std::istream& stream ) { Elf_Half entry_size = header->get_section_entry_size(); Elf_Half num = header->get_sections_num(); Elf64_Off offset = header->get_sections_offset(); for ( Elf_Half i = 0; i < num; ++i ) { section* sec = create_section(); sec->load( stream, (std::streamoff)offset + (std::streampos)i * entry_size ); sec->set_index( i ); // To mark that the section is not permitted to reassign address // during layout calculation sec->set_address( sec->get_address() ); } Elf_Half shstrndx = get_section_name_str_index(); if ( SHN_UNDEF != shstrndx ) { string_section_accessor str_reader( sections[shstrndx] ); for ( Elf_Half i = 0; i < num; ++i ) { Elf_Word section_offset = sections[i]->get_name_string_offset(); const char* p = str_reader.get_string( section_offset ); if ( p != 0 ) { sections[i]->set_name( p ); } } } return num; } //------------------------------------------------------------------------------ //! Checks whether the addresses of the section entirely fall within the given segment. //! It doesn't matter if the addresses are memory addresses, or file offsets, //! they just need to be in the same address space bool is_sect_in_seg( Elf64_Off sect_begin, Elf_Xword sect_size, Elf64_Off seg_begin, Elf64_Off seg_end ) { return ( seg_begin <= sect_begin ) && ( sect_begin + sect_size <= seg_end ) && ( sect_begin < seg_end ); // this is important criteria when sect_size == 0 // Example: seg_begin=10, seg_end=12 (-> covering the bytes 10 and 11) // sect_begin=12, sect_size=0 -> shall return false! } //------------------------------------------------------------------------------ bool load_segments( std::istream& stream ) { Elf_Half entry_size = header->get_segment_entry_size(); Elf_Half num = header->get_segments_num(); Elf64_Off offset = header->get_segments_offset(); for ( Elf_Half i = 0; i < num; ++i ) { segment* seg; unsigned char file_class = header->get_class(); if ( file_class == ELFCLASS64 ) { seg = new segment_impl( &convertor ); } else if ( file_class == ELFCLASS32 ) { seg = new segment_impl( &convertor ); } else { return false; } seg->load( stream, (std::streamoff)offset + (std::streampos)i * entry_size ); seg->set_index( i ); // Add sections to the segments (similar to readelfs algorithm) Elf64_Off segBaseOffset = seg->get_offset(); Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size(); Elf64_Off segVBaseAddr = seg->get_virtual_address(); Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size(); for ( Elf_Half j = 0; j < sections.size(); ++j ) { const section* psec = sections[j]; // SHF_ALLOC sections are matched based on the virtual address // otherwise the file offset is matched if ( ( psec->get_flags() & SHF_ALLOC ) ? is_sect_in_seg( psec->get_address(), psec->get_size(), segVBaseAddr, segVEndAddr ) : is_sect_in_seg( psec->get_offset(), psec->get_size(), segBaseOffset, segEndOffset ) ) { // Alignment of segment shall not be updated, to preserve original value // It will be re-calculated on saving. seg->add_section_index( psec->get_index(), 0 ); } } // Add section into the segments' container segments_.push_back( seg ); } return true; } //------------------------------------------------------------------------------ bool save_header( std::ostream& stream ) { return header->save( stream ); } //------------------------------------------------------------------------------ bool save_sections( std::ostream& stream ) { for ( unsigned int i = 0; i < sections_.size(); ++i ) { section* sec = sections_.at( i ); std::streampos headerPosition = (std::streamoff)header->get_sections_offset() + (std::streampos)header->get_section_entry_size() * sec->get_index(); sec->save( stream, headerPosition, sec->get_offset() ); } return true; } //------------------------------------------------------------------------------ bool save_segments( std::ostream& stream ) { for ( unsigned int i = 0; i < segments_.size(); ++i ) { segment* seg = segments_.at( i ); std::streampos headerPosition = header->get_segments_offset() + (std::streampos)header->get_segment_entry_size() * seg->get_index(); seg->save( stream, headerPosition, seg->get_offset() ); } return true; } //------------------------------------------------------------------------------ bool is_section_without_segment( unsigned int section_index ) { bool found = false; for ( unsigned int j = 0; !found && ( j < segments.size() ); ++j ) { for ( unsigned int k = 0; !found && ( k < segments[j]->get_sections_num() ); ++k ) { found = segments[j]->get_section_index_at( k ) == section_index; } } return !found; } //------------------------------------------------------------------------------ bool is_subsequence_of( segment* seg1, segment* seg2 ) { // Return 'true' if sections of seg1 are a subset of sections in seg2 const std::vector& sections1 = seg1->get_sections(); const std::vector& sections2 = seg2->get_sections(); bool found = false; if ( sections1.size() < sections2.size() ) { found = std::includes( sections2.begin(), sections2.end(), sections1.begin(), sections1.end() ); } return found; } //------------------------------------------------------------------------------ std::vector get_ordered_segments() { std::vector res; std::deque worklist; res.reserve( segments.size() ); std::copy( segments_.begin(), segments_.end(), std::back_inserter( worklist ) ); // Bring the segments which start at address 0 to the front size_t nextSlot = 0; for ( size_t i = 0; i < worklist.size(); ++i ) { if ( i != nextSlot && worklist[i]->is_offset_initialized() && worklist[i]->get_offset() == 0 ) { if ( worklist[nextSlot]->get_offset() == 0 ) { ++nextSlot; } std::swap( worklist[i], worklist[nextSlot] ); ++nextSlot; } } while ( !worklist.empty() ) { segment* seg = worklist.front(); worklist.pop_front(); size_t i = 0; for ( ; i < worklist.size(); ++i ) { if ( is_subsequence_of( seg, worklist[i] ) ) { break; } } if ( i < worklist.size() ) worklist.push_back( seg ); else res.push_back( seg ); } return res; } //------------------------------------------------------------------------------ bool layout_sections_without_segments() { for ( unsigned int i = 0; i < sections_.size(); ++i ) { if ( is_section_without_segment( i ) ) { section* sec = sections_[i]; Elf_Xword section_align = sec->get_addr_align(); if ( section_align > 1 && current_file_pos % section_align != 0 ) { current_file_pos += section_align - current_file_pos % section_align; } if ( 0 != sec->get_index() ) sec->set_offset( current_file_pos ); if ( SHT_NOBITS != sec->get_type() && SHT_NULL != sec->get_type() ) { current_file_pos += sec->get_size(); } } } return true; } //------------------------------------------------------------------------------ void calc_segment_alignment() { for ( std::vector::iterator s = segments_.begin(); s != segments_.end(); ++s ) { segment* seg = *s; for ( int i = 0; i < seg->get_sections_num(); ++i ) { section* sect = sections_[seg->get_section_index_at( i )]; if ( sect->get_addr_align() > seg->get_align() ) { seg->set_align( sect->get_addr_align() ); } } } } //------------------------------------------------------------------------------ bool layout_segments_and_their_sections() { std::vector worklist; std::vector section_generated( sections.size(), false ); // Get segments in a order in where segments which contain a // sub sequence of other segments are located at the end worklist = get_ordered_segments(); for ( unsigned int i = 0; i < worklist.size(); ++i ) { Elf_Xword segment_memory = 0; Elf_Xword segment_filesize = 0; Elf_Xword seg_start_pos = current_file_pos; segment* seg = worklist[i]; // Special case: PHDR segment // This segment contains the program headers but no sections if ( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) { seg_start_pos = header->get_segments_offset(); segment_memory = segment_filesize = header->get_segment_entry_size() * (Elf_Xword)header->get_segments_num(); } // Special case: else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) { seg_start_pos = 0; if ( seg->get_sections_num() ) { segment_memory = segment_filesize = current_file_pos; } } // New segments with not generated sections // have to be aligned else if ( seg->get_sections_num() && !section_generated[seg->get_section_index_at( 0 )] ) { Elf_Xword align = seg->get_align() > 0 ? seg->get_align() : 1; Elf64_Off cur_page_alignment = current_file_pos % align; Elf64_Off req_page_alignment = seg->get_virtual_address() % align; Elf64_Off error = req_page_alignment - cur_page_alignment; current_file_pos += ( seg->get_align() + error ) % align; seg_start_pos = current_file_pos; } else if ( seg->get_sections_num() ) { seg_start_pos = sections[seg->get_section_index_at( 0 )]->get_offset(); } // Write segment's data for ( unsigned int j = 0; j < seg->get_sections_num(); ++j ) { Elf_Half index = seg->get_section_index_at( j ); section* sec = sections[index]; // The NULL section is always generated if ( SHT_NULL == sec->get_type() ) { section_generated[index] = true; continue; } Elf_Xword secAlign = 0; // Fix up the alignment if ( !section_generated[index] && sec->is_address_initialized() && SHT_NOBITS != sec->get_type() && SHT_NULL != sec->get_type() && 0 != sec->get_size() ) { // Align the sections based on the virtual addresses // when possible (this is what matters for execution) Elf64_Off req_offset = sec->get_address() - seg->get_virtual_address(); Elf64_Off cur_offset = current_file_pos - seg_start_pos; if ( req_offset < cur_offset ) { // something has gone awfully wrong, abort! // secAlign would turn out negative, seeking backwards and overwriting previous data return false; } secAlign = req_offset - cur_offset; } else if ( !section_generated[index] && !sec->is_address_initialized() ) { // If no address has been specified then only the section // alignment constraint has to be matched Elf_Xword align = sec->get_addr_align(); if ( align == 0 ) { align = 1; } Elf64_Off error = current_file_pos % align; secAlign = ( align - error ) % align; } else if ( section_generated[index] ) { // Alignment for already generated sections secAlign = sec->get_offset() - seg_start_pos - segment_filesize; } // Determine the segment file and memory sizes // Special case .tbss section (NOBITS) in non TLS segment if ( ( sec->get_flags() & SHF_ALLOC ) && !( ( sec->get_flags() & SHF_TLS ) && ( seg->get_type() != PT_TLS ) && ( SHT_NOBITS == sec->get_type() ) ) ) segment_memory += sec->get_size() + secAlign; if ( SHT_NOBITS != sec->get_type() ) segment_filesize += sec->get_size() + secAlign; // Nothing to be done when generating nested segments if ( section_generated[index] ) { continue; } current_file_pos += secAlign; // Set the section addresses when missing if ( !sec->is_address_initialized() ) sec->set_address( seg->get_virtual_address() + current_file_pos - seg_start_pos ); if ( 0 != sec->get_index() ) sec->set_offset( current_file_pos ); if ( SHT_NOBITS != sec->get_type() ) current_file_pos += sec->get_size(); section_generated[index] = true; } seg->set_file_size( segment_filesize ); // If we already have a memory size from loading an elf file (value > 0), // it must not shrink! // Memory size may be bigger than file size and it is the loader's job to do something // with the surplus bytes in memory, like initializing them with a defined value. if ( seg->get_memory_size() < segment_memory ) { seg->set_memory_size( segment_memory ); } seg->set_offset( seg_start_pos ); } return true; } //------------------------------------------------------------------------------ bool layout_section_table() { // Simply place the section table at the end for now Elf64_Off alignmentError = current_file_pos % 4; current_file_pos += ( 4 - alignmentError ) % 4; header->set_sections_offset( current_file_pos ); return true; } //------------------------------------------------------------------------------ public: friend class Sections; class Sections { public: //------------------------------------------------------------------------------ Sections( elfio* parent_ ) : parent( parent_ ) {} //------------------------------------------------------------------------------ Elf_Half size() const { return (Elf_Half)parent->sections_.size(); } //------------------------------------------------------------------------------ section* operator[]( unsigned int index ) const { section* sec = 0; if ( index < parent->sections_.size() ) { sec = parent->sections_[index]; } return sec; } //------------------------------------------------------------------------------ section* operator[]( const std::string& name ) const { section* sec = 0; std::vector::const_iterator it; for ( it = parent->sections_.begin(); it != parent->sections_.end(); ++it ) { if ( ( *it )->get_name() == name ) { sec = *it; break; } } return sec; } //------------------------------------------------------------------------------ section* add( const std::string& name ) { section* new_section = parent->create_section(); new_section->set_name( name ); Elf_Half str_index = parent->get_section_name_str_index(); section* string_table( parent->sections_[str_index] ); string_section_accessor str_writer( string_table ); Elf_Word pos = str_writer.add_string( name ); new_section->set_name_string_offset( pos ); return new_section; } //------------------------------------------------------------------------------ std::vector::iterator begin() { return parent->sections_.begin(); } //------------------------------------------------------------------------------ std::vector::iterator end() { return parent->sections_.end(); } //------------------------------------------------------------------------------ std::vector::const_iterator begin() const { return parent->sections_.cbegin(); } //------------------------------------------------------------------------------ std::vector::const_iterator end() const { return parent->sections_.cend(); } //------------------------------------------------------------------------------ private: elfio* parent; } sections; //------------------------------------------------------------------------------ public: friend class Segments; class Segments { public: //------------------------------------------------------------------------------ Segments( elfio* parent_ ) : parent( parent_ ) {} //------------------------------------------------------------------------------ Elf_Half size() const { return (Elf_Half)parent->segments_.size(); } //------------------------------------------------------------------------------ segment* operator[]( unsigned int index ) const { return parent->segments_[index]; } //------------------------------------------------------------------------------ segment* add() { return parent->create_segment(); } //------------------------------------------------------------------------------ std::vector::iterator begin() { return parent->segments_.begin(); } //------------------------------------------------------------------------------ std::vector::iterator end() { return parent->segments_.end(); } //------------------------------------------------------------------------------ std::vector::const_iterator begin() const { return parent->segments_.cbegin(); } //------------------------------------------------------------------------------ std::vector::const_iterator end() const { return parent->segments_.cend(); } //------------------------------------------------------------------------------ private: elfio* parent; } segments; //------------------------------------------------------------------------------ private: elf_header* header; std::vector sections_; std::vector segments_; endianess_convertor convertor; Elf_Xword current_file_pos; }; } // namespace ELFIO /*** Start of inlined file: elfio_symbols.hpp ***/ #ifndef ELFIO_SYMBOLS_HPP #define ELFIO_SYMBOLS_HPP namespace ELFIO { //------------------------------------------------------------------------------ template class symbol_section_accessor_template { public: //------------------------------------------------------------------------------ symbol_section_accessor_template( const elfio& elf_file_, S* symbol_section_ ) : elf_file( elf_file_ ), symbol_section( symbol_section_ ) { find_hash_section(); } //------------------------------------------------------------------------------ Elf_Xword get_symbols_num() const { Elf_Xword nRet = 0; if ( 0 != symbol_section->get_entry_size() ) { nRet = symbol_section->get_size() / symbol_section->get_entry_size(); } return nRet; } //------------------------------------------------------------------------------ bool get_symbol( Elf_Xword index, std::string& name, Elf64_Addr& value, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { bool ret = false; if ( elf_file.get_class() == ELFCLASS32 ) { ret = generic_get_symbol( index, name, value, size, bind, type, section_index, other ); } else { ret = generic_get_symbol( index, name, value, size, bind, type, section_index, other ); } return ret; } //------------------------------------------------------------------------------ bool get_symbol( const std::string& name, Elf64_Addr& value, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { bool ret = false; if ( 0 != get_hash_table_index() ) { Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data(); Elf_Word nchain = *(const Elf_Word*)( hash_section->get_data() + sizeof( Elf_Word ) ); Elf_Word val = elf_hash( (const unsigned char*)name.c_str() ); Elf_Word y = *(const Elf_Word*)( hash_section->get_data() + ( 2 + val % nbucket ) * sizeof( Elf_Word ) ); std::string str; get_symbol( y, str, value, size, bind, type, section_index, other ); while ( str != name && STN_UNDEF != y && y < nchain ) { y = *(const Elf_Word*)( hash_section->get_data() + ( 2 + nbucket + y ) * sizeof( Elf_Word ) ); get_symbol( y, str, value, size, bind, type, section_index, other ); } if ( str == name ) { ret = true; } } else { for ( Elf_Xword i = 0; i < get_symbols_num() && !ret; i++ ) { std::string symbol_name; if ( get_symbol( i, symbol_name, value, size, bind, type, section_index, other ) ) { if ( symbol_name == name ) { ret = true; } } } } return ret; } //------------------------------------------------------------------------------ bool get_symbol( const Elf64_Addr& value, std::string& name, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { const endianess_convertor& convertor = elf_file.get_convertor(); Elf_Xword idx = 0; bool match = false; Elf64_Addr v = 0; if ( elf_file.get_class() == ELFCLASS32 ) { match = generic_search_symbols( [&]( const Elf32_Sym* sym ) { return convertor( sym->st_value ) == value; }, idx ); } else { match = generic_search_symbols( [&]( const Elf64_Sym* sym ) { return convertor( sym->st_value ) == value; }, idx ); } if ( match ) { return get_symbol( idx, name, v, size, bind, type, section_index, other ); } return false; } //------------------------------------------------------------------------------ Elf_Word add_symbol( Elf_Word name, Elf64_Addr value, Elf_Xword size, unsigned char info, unsigned char other, Elf_Half shndx ) { Elf_Word nRet; if ( symbol_section->get_size() == 0 ) { if ( elf_file.get_class() == ELFCLASS32 ) { nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); } else { nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); } } if ( elf_file.get_class() == ELFCLASS32 ) { nRet = generic_add_symbol( name, value, size, info, other, shndx ); } else { nRet = generic_add_symbol( name, value, size, info, other, shndx ); } return nRet; } //------------------------------------------------------------------------------ Elf_Word add_symbol( Elf_Word name, Elf64_Addr value, Elf_Xword size, unsigned char bind, unsigned char type, unsigned char other, Elf_Half shndx ) { return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other, shndx ); } //------------------------------------------------------------------------------ Elf_Word add_symbol( string_section_accessor& pStrWriter, const char* str, Elf64_Addr value, Elf_Xword size, unsigned char info, unsigned char other, Elf_Half shndx ) { Elf_Word index = pStrWriter.add_string( str ); return add_symbol( index, value, size, info, other, shndx ); } //------------------------------------------------------------------------------ Elf_Word add_symbol( string_section_accessor& pStrWriter, const char* str, Elf64_Addr value, Elf_Xword size, unsigned char bind, unsigned char type, unsigned char other, Elf_Half shndx ) { return add_symbol( pStrWriter, str, value, size, ELF_ST_INFO( bind, type ), other, shndx ); } //------------------------------------------------------------------------------ Elf_Xword arrange_local_symbols( std::function func = nullptr ) { int nRet = 0; if ( elf_file.get_class() == ELFCLASS32 ) { nRet = generic_arrange_local_symbols( func ); } else { nRet = generic_arrange_local_symbols( func ); } return nRet; } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ void find_hash_section() { hash_section = 0; hash_section_index = 0; Elf_Half nSecNo = elf_file.sections.size(); for ( Elf_Half i = 0; i < nSecNo && 0 == hash_section_index; ++i ) { const section* sec = elf_file.sections[i]; if ( sec->get_link() == symbol_section->get_index() ) { hash_section = sec; hash_section_index = i; } } } //------------------------------------------------------------------------------ Elf_Half get_string_table_index() const { return (Elf_Half)symbol_section->get_link(); } //------------------------------------------------------------------------------ Elf_Half get_hash_table_index() const { return hash_section_index; } //------------------------------------------------------------------------------ template const T* generic_get_symbol_ptr( Elf_Xword index ) const { if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { const T* pSym = reinterpret_cast( symbol_section->get_data() + index * symbol_section->get_entry_size() ); return pSym; } return nullptr; } //------------------------------------------------------------------------------ template bool generic_search_symbols( std::function match, Elf_Xword& idx ) const { for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) { const T* symPtr = generic_get_symbol_ptr( i ); if ( symPtr == nullptr ) return false; if ( match( symPtr ) ) { idx = i; return true; } } return false; } //------------------------------------------------------------------------------ template bool generic_get_symbol( Elf_Xword index, std::string& name, Elf64_Addr& value, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { bool ret = false; if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { const T* pSym = reinterpret_cast( symbol_section->get_data() + index * symbol_section->get_entry_size() ); const endianess_convertor& convertor = elf_file.get_convertor(); section* string_section = elf_file.sections[get_string_table_index()]; string_section_accessor str_reader( string_section ); const char* pStr = str_reader.get_string( convertor( pSym->st_name ) ); if ( 0 != pStr ) { name = pStr; } value = convertor( pSym->st_value ); size = convertor( pSym->st_size ); bind = ELF_ST_BIND( pSym->st_info ); type = ELF_ST_TYPE( pSym->st_info ); section_index = convertor( pSym->st_shndx ); other = pSym->st_other; ret = true; } return ret; } //------------------------------------------------------------------------------ template Elf_Word generic_add_symbol( Elf_Word name, Elf64_Addr value, Elf_Xword size, unsigned char info, unsigned char other, Elf_Half shndx ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; entry.st_name = convertor( name ); entry.st_value = value; entry.st_value = convertor( entry.st_value ); entry.st_size = size; entry.st_size = convertor( entry.st_size ); entry.st_info = convertor( info ); entry.st_other = convertor( other ); entry.st_shndx = convertor( shndx ); symbol_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); Elf_Word nRet = symbol_section->get_size() / sizeof( entry ) - 1; return nRet; } //------------------------------------------------------------------------------ template Elf_Xword generic_arrange_local_symbols( std::function func ) { const endianess_convertor& convertor = elf_file.get_convertor(); const Elf_Xword size = symbol_section->get_entry_size(); Elf_Xword first_not_local = 1; // Skip the first entry. It is always NOTYPE Elf_Xword current = 0; Elf_Xword count = get_symbols_num(); while ( true ) { T* p1 = nullptr; T* p2 = nullptr; while ( first_not_local < count ) { p1 = const_cast( generic_get_symbol_ptr( first_not_local ) ); if ( ELF_ST_BIND( convertor( p1->st_info ) ) != STB_LOCAL ) break; ++first_not_local; } current = first_not_local + 1; while ( current < count ) { p2 = const_cast( generic_get_symbol_ptr( current ) ); if ( ELF_ST_BIND( convertor( p2->st_info ) ) == STB_LOCAL ) break; ++current; } if ( first_not_local < count && current < count ) { if ( func ) func( first_not_local, current ); // Swap the symbols T tmp; std::copy( p1, p1 + 1, &tmp ); std::copy( p2, p2 + 1, p1 ); std::copy( &tmp, &tmp + 1, p2 ); } else { // Update 'info' field of the section symbol_section->set_info( first_not_local ); break; } } // Elf_Word nRet = symbol_section->get_size() / sizeof(entry) - 1; return first_not_local; } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* symbol_section; Elf_Half hash_section_index; const section* hash_section; }; using symbol_section_accessor = symbol_section_accessor_template
; using const_symbol_section_accessor = symbol_section_accessor_template; } // namespace ELFIO #endif // ELFIO_SYMBOLS_HPP /*** End of inlined file: elfio_symbols.hpp ***/ /*** Start of inlined file: elfio_note.hpp ***/ #ifndef ELFIO_NOTE_HPP #define ELFIO_NOTE_HPP namespace ELFIO { //------------------------------------------------------------------------------ // There are discrepancies in documentations. SCO documentation // (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section) // requires 8 byte entries alignment for 64-bit ELF file, // but Oracle's definition uses the same structure // for 32-bit and 64-bit formats. // (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html) // // It looks like EM_X86_64 Linux implementation is similar to Oracle's // definition. Therefore, the same alignment works for both formats //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ template class note_section_accessor_template { public: //------------------------------------------------------------------------------ note_section_accessor_template( const elfio& elf_file_, S* section_ ) : elf_file( elf_file_ ), note_section( section_ ) { process_section(); } //------------------------------------------------------------------------------ Elf_Word get_notes_num() const { return (Elf_Word)note_start_positions.size(); } //------------------------------------------------------------------------------ bool get_note( Elf_Word index, Elf_Word& type, std::string& name, void*& desc, Elf_Word& descSize ) const { if ( index >= note_section->get_size() ) { return false; } const char* pData = note_section->get_data() + note_start_positions[index]; int align = sizeof( Elf_Word ); const endianess_convertor& convertor = elf_file.get_convertor(); type = convertor( *(const Elf_Word*)( pData + 2 * align ) ); Elf_Word namesz = convertor( *(const Elf_Word*)( pData ) ); descSize = convertor( *(const Elf_Word*)( pData + sizeof( namesz ) ) ); Elf_Xword max_name_size = note_section->get_size() - note_start_positions[index]; if ( namesz < 1 || namesz > max_name_size || (Elf_Xword)namesz + descSize > max_name_size ) { return false; } name.assign( pData + 3 * align, namesz - 1 ); if ( 0 == descSize ) { desc = 0; } else { desc = const_cast( pData + 3 * align + ( ( namesz + align - 1 ) / align ) * align ); } return true; } //------------------------------------------------------------------------------ void add_note( Elf_Word type, const std::string& name, const void* desc, Elf_Word descSize ) { const endianess_convertor& convertor = elf_file.get_convertor(); int align = sizeof( Elf_Word ); Elf_Word nameLen = (Elf_Word)name.size() + 1; Elf_Word nameLenConv = convertor( nameLen ); std::string buffer( reinterpret_cast( &nameLenConv ), align ); Elf_Word descSizeConv = convertor( descSize ); buffer.append( reinterpret_cast( &descSizeConv ), align ); type = convertor( type ); buffer.append( reinterpret_cast( &type ), align ); buffer.append( name ); buffer.append( 1, '\x00' ); const char pad[] = { '\0', '\0', '\0', '\0' }; if ( nameLen % align != 0 ) { buffer.append( pad, align - nameLen % align ); } if ( desc != 0 && descSize != 0 ) { buffer.append( reinterpret_cast( desc ), descSize ); if ( descSize % align != 0 ) { buffer.append( pad, align - descSize % align ); } } note_start_positions.push_back( note_section->get_size() ); note_section->append_data( buffer ); } private: //------------------------------------------------------------------------------ void process_section() { const endianess_convertor& convertor = elf_file.get_convertor(); const char* data = note_section->get_data(); Elf_Xword size = note_section->get_size(); Elf_Xword current = 0; note_start_positions.clear(); // Is it empty? if ( 0 == data || 0 == size ) { return; } Elf_Word align = sizeof( Elf_Word ); while ( current + (Elf_Xword)3 * align <= size ) { note_start_positions.push_back( current ); Elf_Word namesz = convertor( *(const Elf_Word*)( data + current ) ); Elf_Word descsz = convertor( *(const Elf_Word*)( data + current + sizeof( namesz ) ) ); current += (Elf_Xword)3 * sizeof( Elf_Word ) + ( ( namesz + align - 1 ) / align ) * (Elf_Xword)align + ( ( descsz + align - 1 ) / align ) * (Elf_Xword)align; } } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* note_section; std::vector note_start_positions; }; using note_section_accessor = note_section_accessor_template
; using const_note_section_accessor = note_section_accessor_template; } // namespace ELFIO #endif // ELFIO_NOTE_HPP /*** End of inlined file: elfio_note.hpp ***/ /*** Start of inlined file: elfio_relocation.hpp ***/ #ifndef ELFIO_RELOCATION_HPP #define ELFIO_RELOCATION_HPP namespace ELFIO { template struct get_sym_and_type; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF32_R_SYM( (Elf_Word)info ); } static int get_r_type( Elf_Xword info ) { return ELF32_R_TYPE( (Elf_Word)info ); } }; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF32_R_SYM( (Elf_Word)info ); } static int get_r_type( Elf_Xword info ) { return ELF32_R_TYPE( (Elf_Word)info ); } }; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } }; template <> struct get_sym_and_type { static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } }; //------------------------------------------------------------------------------ template class relocation_section_accessor_template { public: //------------------------------------------------------------------------------ relocation_section_accessor_template( const elfio& elf_file_, S* section_ ) : elf_file( elf_file_ ), relocation_section( section_ ) { } //------------------------------------------------------------------------------ Elf_Xword get_entries_num() const { Elf_Xword nRet = 0; if ( 0 != relocation_section->get_entry_size() ) { nRet = relocation_section->get_size() / relocation_section->get_entry_size(); } return nRet; } //------------------------------------------------------------------------------ bool get_entry( Elf_Xword index, Elf64_Addr& offset, Elf_Word& symbol, Elf_Word& type, Elf_Sxword& addend ) const { if ( index >= get_entries_num() ) { // Is index valid return false; } if ( elf_file.get_class() == ELFCLASS32 ) { if ( SHT_REL == relocation_section->get_type() ) { generic_get_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_get_entry_rela( index, offset, symbol, type, addend ); } } else { if ( SHT_REL == relocation_section->get_type() ) { generic_get_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_get_entry_rela( index, offset, symbol, type, addend ); } } return true; } //------------------------------------------------------------------------------ bool get_entry( Elf_Xword index, Elf64_Addr& offset, Elf64_Addr& symbolValue, std::string& symbolName, Elf_Word& type, Elf_Sxword& addend, Elf_Sxword& calcValue ) const { // Do regular job Elf_Word symbol; bool ret = get_entry( index, offset, symbol, type, addend ); // Find the symbol Elf_Xword size; unsigned char bind; unsigned char symbolType; Elf_Half section; unsigned char other; symbol_section_accessor symbols( elf_file, elf_file.sections[get_symbol_table_index()] ); ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size, bind, symbolType, section, other ); if ( ret ) { // Was it successful? switch ( type ) { case R_386_NONE: // none calcValue = 0; break; case R_386_32: // S + A calcValue = symbolValue + addend; break; case R_386_PC32: // S + A - P calcValue = symbolValue + addend - offset; break; case R_386_GOT32: // G + A - P calcValue = 0; break; case R_386_PLT32: // L + A - P calcValue = 0; break; case R_386_COPY: // none calcValue = 0; break; case R_386_GLOB_DAT: // S case R_386_JMP_SLOT: // S calcValue = symbolValue; break; case R_386_RELATIVE: // B + A calcValue = addend; break; case R_386_GOTOFF: // S + A - GOT calcValue = 0; break; case R_386_GOTPC: // GOT + A - P calcValue = 0; break; default: // Not recognized symbol! calcValue = 0; break; } } return ret; } //------------------------------------------------------------------------------ bool set_entry( Elf_Xword index, Elf64_Addr offset, Elf_Word symbol, Elf_Word type, Elf_Sxword addend ) { if ( index >= get_entries_num() ) { // Is index valid return false; } if ( elf_file.get_class() == ELFCLASS32 ) { if ( SHT_REL == relocation_section->get_type() ) { generic_set_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_set_entry_rela( index, offset, symbol, type, addend ); } } else { if ( SHT_REL == relocation_section->get_type() ) { generic_set_entry_rel( index, offset, symbol, type, addend ); } else if ( SHT_RELA == relocation_section->get_type() ) { generic_set_entry_rela( index, offset, symbol, type, addend ); } } return true; } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Xword info ) { if ( elf_file.get_class() == ELFCLASS32 ) { generic_add_entry( offset, info ); } else { generic_add_entry( offset, info ); } } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned char type ) { Elf_Xword info; if ( elf_file.get_class() == ELFCLASS32 ) { info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } add_entry( offset, info ); } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) { if ( elf_file.get_class() == ELFCLASS32 ) { generic_add_entry( offset, info, addend ); } else { generic_add_entry( offset, info, addend ); } } //------------------------------------------------------------------------------ void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned char type, Elf_Sxword addend ) { Elf_Xword info; if ( elf_file.get_class() == ELFCLASS32 ) { info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } add_entry( offset, info, addend ); } //------------------------------------------------------------------------------ void add_entry( string_section_accessor str_writer, const char* str, symbol_section_accessor sym_writer, Elf64_Addr value, Elf_Word size, unsigned char sym_info, unsigned char other, Elf_Half shndx, Elf64_Addr offset, unsigned char type ) { Elf_Word str_index = str_writer.add_string( str ); Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size, sym_info, other, shndx ); add_entry( offset, sym_index, type ); } //------------------------------------------------------------------------------ void swap_symbols( Elf_Xword first, Elf_Xword second ) { Elf64_Addr offset; Elf_Word symbol; Elf_Word rtype; Elf_Sxword addend; for ( Elf_Word i = 0; i < get_entries_num(); i++ ) { get_entry( i, offset, symbol, rtype, addend ); if ( symbol == first ) { set_entry( i, offset, (Elf_Word)second, rtype, addend ); } if ( symbol == second ) { set_entry( i, offset, (Elf_Word)first, rtype, addend ); } } } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ Elf_Half get_symbol_table_index() const { return (Elf_Half)relocation_section->get_link(); } //------------------------------------------------------------------------------ template void generic_get_entry_rel( Elf_Xword index, Elf64_Addr& offset, Elf_Word& symbol, Elf_Word& type, Elf_Sxword& addend ) const { const endianess_convertor& convertor = elf_file.get_convertor(); const T* pEntry = reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ); offset = convertor( pEntry->r_offset ); Elf_Xword tmp = convertor( pEntry->r_info ); symbol = get_sym_and_type::get_r_sym( tmp ); type = get_sym_and_type::get_r_type( tmp ); addend = 0; } //------------------------------------------------------------------------------ template void generic_get_entry_rela( Elf_Xword index, Elf64_Addr& offset, Elf_Word& symbol, Elf_Word& type, Elf_Sxword& addend ) const { const endianess_convertor& convertor = elf_file.get_convertor(); const T* pEntry = reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ); offset = convertor( pEntry->r_offset ); Elf_Xword tmp = convertor( pEntry->r_info ); symbol = get_sym_and_type::get_r_sym( tmp ); type = get_sym_and_type::get_r_type( tmp ); addend = convertor( pEntry->r_addend ); } //------------------------------------------------------------------------------ template void generic_set_entry_rel( Elf_Xword index, Elf64_Addr offset, Elf_Word symbol, Elf_Word type, Elf_Sxword ) { const endianess_convertor& convertor = elf_file.get_convertor(); T* pEntry = const_cast( reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ) ); if ( elf_file.get_class() == ELFCLASS32 ) { pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } pEntry->r_offset = offset; pEntry->r_offset = convertor( pEntry->r_offset ); pEntry->r_info = convertor( pEntry->r_info ); } //------------------------------------------------------------------------------ template void generic_set_entry_rela( Elf_Xword index, Elf64_Addr offset, Elf_Word symbol, Elf_Word type, Elf_Sxword addend ) { const endianess_convertor& convertor = elf_file.get_convertor(); T* pEntry = const_cast( reinterpret_cast( relocation_section->get_data() + index * relocation_section->get_entry_size() ) ); if ( elf_file.get_class() == ELFCLASS32 ) { pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); } else { pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); } pEntry->r_offset = offset; pEntry->r_addend = addend; pEntry->r_offset = convertor( pEntry->r_offset ); pEntry->r_info = convertor( pEntry->r_info ); pEntry->r_addend = convertor( pEntry->r_addend ); } //------------------------------------------------------------------------------ template void generic_add_entry( Elf64_Addr offset, Elf_Xword info ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; entry.r_offset = offset; entry.r_info = info; entry.r_offset = convertor( entry.r_offset ); entry.r_info = convertor( entry.r_info ); relocation_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); } //------------------------------------------------------------------------------ template void generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; entry.r_offset = offset; entry.r_info = info; entry.r_addend = addend; entry.r_offset = convertor( entry.r_offset ); entry.r_info = convertor( entry.r_info ); entry.r_addend = convertor( entry.r_addend ); relocation_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* relocation_section; }; using relocation_section_accessor = relocation_section_accessor_template
; using const_relocation_section_accessor = relocation_section_accessor_template; } // namespace ELFIO #endif // ELFIO_RELOCATION_HPP /*** End of inlined file: elfio_relocation.hpp ***/ /*** Start of inlined file: elfio_dynamic.hpp ***/ #ifndef ELFIO_DYNAMIC_HPP #define ELFIO_DYNAMIC_HPP namespace ELFIO { //------------------------------------------------------------------------------ template class dynamic_section_accessor_template { public: //------------------------------------------------------------------------------ dynamic_section_accessor_template( const elfio& elf_file_, S* section_ ) : elf_file( elf_file_ ), dynamic_section( section_ ) { } //------------------------------------------------------------------------------ Elf_Xword get_entries_num() const { Elf_Xword nRet = 0; if ( 0 != dynamic_section->get_entry_size() ) { nRet = dynamic_section->get_size() / dynamic_section->get_entry_size(); } return nRet; } //------------------------------------------------------------------------------ bool get_entry( Elf_Xword index, Elf_Xword& tag, Elf_Xword& value, std::string& str ) const { if ( index >= get_entries_num() ) { // Is index valid return false; } if ( elf_file.get_class() == ELFCLASS32 ) { generic_get_entry_dyn( index, tag, value ); } else { generic_get_entry_dyn( index, tag, value ); } // If the tag may have a string table reference, prepare the string if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH || tag == DT_RUNPATH ) { string_section_accessor strsec = elf_file.sections[get_string_table_index()]; const char* result = strsec.get_string( value ); if ( 0 == result ) { str.clear(); return false; } str = result; } else { str.clear(); } return true; } //------------------------------------------------------------------------------ void add_entry( Elf_Xword tag, Elf_Xword value ) { if ( elf_file.get_class() == ELFCLASS32 ) { generic_add_entry( tag, value ); } else { generic_add_entry( tag, value ); } } //------------------------------------------------------------------------------ void add_entry( Elf_Xword tag, const std::string& str ) { string_section_accessor strsec = elf_file.sections[get_string_table_index()]; Elf_Xword value = strsec.add_string( str ); add_entry( tag, value ); } //------------------------------------------------------------------------------ private: //------------------------------------------------------------------------------ Elf_Half get_string_table_index() const { return (Elf_Half)dynamic_section->get_link(); } //------------------------------------------------------------------------------ template void generic_get_entry_dyn( Elf_Xword index, Elf_Xword& tag, Elf_Xword& value ) const { const endianess_convertor& convertor = elf_file.get_convertor(); // Check unusual case when dynamic section has no data if ( dynamic_section->get_data() == 0 || ( index + 1 ) * dynamic_section->get_entry_size() > dynamic_section->get_size() ) { tag = DT_NULL; value = 0; return; } const T* pEntry = reinterpret_cast( dynamic_section->get_data() + index * dynamic_section->get_entry_size() ); tag = convertor( pEntry->d_tag ); switch ( tag ) { case DT_NULL: case DT_SYMBOLIC: case DT_TEXTREL: case DT_BIND_NOW: value = 0; break; case DT_NEEDED: case DT_PLTRELSZ: case DT_RELASZ: case DT_RELAENT: case DT_STRSZ: case DT_SYMENT: case DT_SONAME: case DT_RPATH: case DT_RELSZ: case DT_RELENT: case DT_PLTREL: case DT_INIT_ARRAYSZ: case DT_FINI_ARRAYSZ: case DT_RUNPATH: case DT_FLAGS: case DT_PREINIT_ARRAYSZ: value = convertor( pEntry->d_un.d_val ); break; case DT_PLTGOT: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_RELA: case DT_INIT: case DT_FINI: case DT_REL: case DT_DEBUG: case DT_JMPREL: case DT_INIT_ARRAY: case DT_FINI_ARRAY: case DT_PREINIT_ARRAY: default: value = convertor( pEntry->d_un.d_ptr ); break; } } //------------------------------------------------------------------------------ template void generic_add_entry( Elf_Xword tag, Elf_Xword value ) { const endianess_convertor& convertor = elf_file.get_convertor(); T entry; switch ( tag ) { case DT_NULL: case DT_SYMBOLIC: case DT_TEXTREL: case DT_BIND_NOW: value = 0; case DT_NEEDED: case DT_PLTRELSZ: case DT_RELASZ: case DT_RELAENT: case DT_STRSZ: case DT_SYMENT: case DT_SONAME: case DT_RPATH: case DT_RELSZ: case DT_RELENT: case DT_PLTREL: case DT_INIT_ARRAYSZ: case DT_FINI_ARRAYSZ: case DT_RUNPATH: case DT_FLAGS: case DT_PREINIT_ARRAYSZ: entry.d_un.d_val = convertor( value ); break; case DT_PLTGOT: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_RELA: case DT_INIT: case DT_FINI: case DT_REL: case DT_DEBUG: case DT_JMPREL: case DT_INIT_ARRAY: case DT_FINI_ARRAY: case DT_PREINIT_ARRAY: default: entry.d_un.d_ptr = convertor( value ); break; } entry.d_tag = convertor( tag ); dynamic_section->append_data( reinterpret_cast( &entry ), sizeof( entry ) ); } //------------------------------------------------------------------------------ private: const elfio& elf_file; S* dynamic_section; }; using dynamic_section_accessor = dynamic_section_accessor_template
; using const_dynamic_section_accessor = dynamic_section_accessor_template; } // namespace ELFIO #endif // ELFIO_DYNAMIC_HPP /*** End of inlined file: elfio_dynamic.hpp ***/ /*** Start of inlined file: elfio_modinfo.hpp ***/ #ifndef ELFIO_MODINFO_HPP #define ELFIO_MODINFO_HPP #include #include namespace ELFIO { //------------------------------------------------------------------------------ template class modinfo_section_accessor_template { public: //------------------------------------------------------------------------------ modinfo_section_accessor_template( S* section_ ) : modinfo_section( section_ ) { process_section(); } //------------------------------------------------------------------------------ Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); } //------------------------------------------------------------------------------ bool get_attribute( Elf_Word no, std::string& field, std::string& value ) const { if ( no < content.size() ) { field = content[no].first; value = content[no].second; return true; } return false; } //------------------------------------------------------------------------------ bool get_attribute( std::string field_name, std::string& value ) const { for ( auto i = content.begin(); i != content.end(); i++ ) { if ( field_name == i->first ) { value = i->second; return true; } } return false; } //------------------------------------------------------------------------------ Elf_Word add_attribute( std::string field, std::string value ) { Elf_Word current_position = 0; if ( modinfo_section ) { // Strings are addeded to the end of the current section data current_position = (Elf_Word)modinfo_section->get_size(); std::string attribute = field + "=" + value; modinfo_section->append_data( attribute + '\0' ); content.push_back( std::pair( field, value ) ); } return current_position; } //------------------------------------------------------------------------------ private: void process_section() { const char* pdata = modinfo_section->get_data(); if ( pdata ) { ELFIO::Elf_Xword i = 0; while ( i < modinfo_section->get_size() ) { while ( i < modinfo_section->get_size() && !pdata[i] ) i++; if ( i < modinfo_section->get_size() ) { std::string info = pdata + i; size_t loc = info.find( '=' ); std::pair attribute( info.substr( 0, loc ), info.substr( loc + 1 ) ); content.push_back( attribute ); i += info.length(); } } } } //------------------------------------------------------------------------------ private: S* modinfo_section; std::vector> content; }; using modinfo_section_accessor = modinfo_section_accessor_template
; using const_modinfo_section_accessor = modinfo_section_accessor_template; } // namespace ELFIO #endif // ELFIO_MODINFO_HPP /*** End of inlined file: elfio_modinfo.hpp ***/ #ifdef _MSC_VER #pragma warning( pop ) #endif #endif // ELFIO_HPP /*** End of inlined file: elfio.hpp ***/ namespace ELFIO { static struct class_table_t { const char key; const char* str; } class_table[] = { { ELFCLASS32, "ELF32" }, { ELFCLASS64, "ELF64" }, }; static struct endian_table_t { const char key; const char* str; } endian_table[] = { { ELFDATANONE, "None" }, { ELFDATA2LSB, "Little endian" }, { ELFDATA2MSB, "Big endian" }, }; static struct version_table_t { const Elf64_Word key; const char* str; } version_table[] = { { EV_NONE, "None" }, { EV_CURRENT, "Current" }, }; static struct type_table_t { const Elf32_Half key; const char* str; } type_table[] = { { ET_NONE, "No file type" }, { ET_REL, "Relocatable file" }, { ET_EXEC, "Executable file" }, { ET_DYN, "Shared object file" }, { ET_CORE, "Core file" }, }; static struct machine_table_t { const Elf64_Half key; const char* str; } machine_table[] = { { EM_NONE, "No machine" }, { EM_M32, "AT&T WE 32100" }, { EM_SPARC, "SUN SPARC" }, { EM_386, "Intel 80386" }, { EM_68K, "Motorola m68k family" }, { EM_88K, "Motorola m88k family" }, { EM_486, "Intel 80486// Reserved for future use" }, { EM_860, "Intel 80860" }, { EM_MIPS, "MIPS R3000 (officially, big-endian only)" }, { EM_S370, "IBM System/370" }, { EM_MIPS_RS3_LE, "MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated" }, { EM_res011, "Reserved" }, { EM_res012, "Reserved" }, { EM_res013, "Reserved" }, { EM_res014, "Reserved" }, { EM_PARISC, "HPPA" }, { EM_res016, "Reserved" }, { EM_VPP550, "Fujitsu VPP500" }, { EM_SPARC32PLUS, "Sun's v8plus" }, { EM_960, "Intel 80960" }, { EM_PPC, "PowerPC" }, { EM_PPC64, "64-bit PowerPC" }, { EM_S390, "IBM S/390" }, { EM_SPU, "Sony/Toshiba/IBM SPU" }, { EM_res024, "Reserved" }, { EM_res025, "Reserved" }, { EM_res026, "Reserved" }, { EM_res027, "Reserved" }, { EM_res028, "Reserved" }, { EM_res029, "Reserved" }, { EM_res030, "Reserved" }, { EM_res031, "Reserved" }, { EM_res032, "Reserved" }, { EM_res033, "Reserved" }, { EM_res034, "Reserved" }, { EM_res035, "Reserved" }, { EM_V800, "NEC V800 series" }, { EM_FR20, "Fujitsu FR20" }, { EM_RH32, "TRW RH32" }, { EM_MCORE, "Motorola M*Core // May also be taken by Fujitsu MMA" }, { EM_RCE, "Old name for MCore" }, { EM_ARM, "ARM" }, { EM_OLD_ALPHA, "Digital Alpha" }, { EM_SH, "Renesas (formerly Hitachi) / SuperH SH" }, { EM_SPARCV9, "SPARC v9 64-bit" }, { EM_TRICORE, "Siemens Tricore embedded processor" }, { EM_ARC, "ARC Cores" }, { EM_H8_300, "Renesas (formerly Hitachi) H8/300" }, { EM_H8_300H, "Renesas (formerly Hitachi) H8/300H" }, { EM_H8S, "Renesas (formerly Hitachi) H8S" }, { EM_H8_500, "Renesas (formerly Hitachi) H8/500" }, { EM_IA_64, "Intel IA-64 Processor" }, { EM_MIPS_X, "Stanford MIPS-X" }, { EM_COLDFIRE, "Motorola Coldfire" }, { EM_68HC12, "Motorola M68HC12" }, { EM_MMA, "Fujitsu Multimedia Accelerator" }, { EM_PCP, "Siemens PCP" }, { EM_NCPU, "Sony nCPU embedded RISC processor" }, { EM_NDR1, "Denso NDR1 microprocesspr" }, { EM_STARCORE, "Motorola Star*Core processor" }, { EM_ME16, "Toyota ME16 processor" }, { EM_ST100, "STMicroelectronics ST100 processor" }, { EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor" }, { EM_X86_64, "Advanced Micro Devices X86-64 processor" }, { EM_PDSP, "Sony DSP Processor" }, { EM_PDP10, "Digital Equipment Corp. PDP-10" }, { EM_PDP11, "Digital Equipment Corp. PDP-11" }, { EM_FX66, "Siemens FX66 microcontroller" }, { EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller" }, { EM_ST7, "STMicroelectronics ST7 8-bit microcontroller" }, { EM_68HC16, "Motorola MC68HC16 Microcontroller" }, { EM_68HC11, "Motorola MC68HC11 Microcontroller" }, { EM_68HC08, "Motorola MC68HC08 Microcontroller" }, { EM_68HC05, "Motorola MC68HC05 Microcontroller" }, { EM_SVX, "Silicon Graphics SVx" }, { EM_ST19, "STMicroelectronics ST19 8-bit cpu" }, { EM_VAX, "Digital VAX" }, { EM_CRIS, "Axis Communications 32-bit embedded processor" }, { EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu" }, { EM_FIREPATH, "Element 14 64-bit DSP processor" }, { EM_ZSP, "LSI Logic's 16-bit DSP processor" }, { EM_MMIX, "Donald Knuth's educational 64-bit processor" }, { EM_HUANY, "Harvard's machine-independent format" }, { EM_PRISM, "SiTera Prism" }, { EM_AVR, "Atmel AVR 8-bit microcontroller" }, { EM_FR30, "Fujitsu FR30" }, { EM_D10V, "Mitsubishi D10V" }, { EM_D30V, "Mitsubishi D30V" }, { EM_V850, "NEC v850" }, { EM_M32R, "Renesas M32R (formerly Mitsubishi M32R)" }, { EM_MN10300, "Matsushita MN10300" }, { EM_MN10200, "Matsushita MN10200" }, { EM_PJ, "picoJava" }, { EM_OPENRISC, "OpenRISC 32-bit embedded processor" }, { EM_ARC_A5, "ARC Cores Tangent-A5" }, { EM_XTENSA, "Tensilica Xtensa Architecture" }, { EM_VIDEOCORE, "Alphamosaic VideoCore processor" }, { EM_TMM_GPP, "Thompson Multimedia General Purpose Processor" }, { EM_NS32K, "National Semiconductor 32000 series" }, { EM_TPC, "Tenor Network TPC processor" }, { EM_SNP1K, "Trebia SNP 1000 processor" }, { EM_ST200, "STMicroelectronics ST200 microcontroller" }, { EM_IP2K, "Ubicom IP2022 micro controller" }, { EM_MAX, "MAX Processor" }, { EM_CR, "National Semiconductor CompactRISC" }, { EM_F2MC16, "Fujitsu F2MC16" }, { EM_MSP430, "TI msp430 micro controller" }, { EM_BLACKFIN, "ADI Blackfin" }, { EM_SE_C33, "S1C33 Family of Seiko Epson processors" }, { EM_SEP, "Sharp embedded microprocessor" }, { EM_ARCA, "Arca RISC Microprocessor" }, { EM_UNICORE, "Microprocessor series from PKU-Unity Ltd. and MPRC of " "Peking University" }, { EM_EXCESS, "eXcess: 16/32/64-bit configurable embedded CPU" }, { EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor" }, { EM_ALTERA_NIOS2, "Altera Nios II soft-core processor" }, { EM_CRX, "National Semiconductor CRX" }, { EM_XGATE, "Motorola XGATE embedded processor" }, { EM_C166, "Infineon C16x/XC16x processor" }, { EM_M16C, "Renesas M16C series microprocessors" }, { EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller" }, { EM_CE, "Freescale Communication Engine RISC core" }, { EM_M32C, "Renesas M32C series microprocessors" }, { EM_res121, "Reserved" }, { EM_res122, "Reserved" }, { EM_res123, "Reserved" }, { EM_res124, "Reserved" }, { EM_res125, "Reserved" }, { EM_res126, "Reserved" }, { EM_res127, "Reserved" }, { EM_res128, "Reserved" }, { EM_res129, "Reserved" }, { EM_res130, "Reserved" }, { EM_TSK3000, "Altium TSK3000 core" }, { EM_RS08, "Freescale RS08 embedded processor" }, { EM_res133, "Reserved" }, { EM_ECOG2, "Cyan Technology eCOG2 microprocessor" }, { EM_SCORE, "Sunplus Score" }, { EM_SCORE7, "Sunplus S+core7 RISC processor" }, { EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor" }, { EM_VIDEOCORE3, "Broadcom VideoCore III processor" }, { EM_LATTICEMICO32, "RISC processor for Lattice FPGA architecture" }, { EM_SE_C17, "Seiko Epson C17 family" }, { EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family" }, { EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family" }, { EM_TI_C5500, "Texas Instruments TMS320C55x DSP family" }, { EM_res143, "Reserved" }, { EM_res144, "Reserved" }, { EM_res145, "Reserved" }, { EM_res146, "Reserved" }, { EM_res147, "Reserved" }, { EM_res148, "Reserved" }, { EM_res149, "Reserved" }, { EM_res150, "Reserved" }, { EM_res151, "Reserved" }, { EM_res152, "Reserved" }, { EM_res153, "Reserved" }, { EM_res154, "Reserved" }, { EM_res155, "Reserved" }, { EM_res156, "Reserved" }, { EM_res157, "Reserved" }, { EM_res158, "Reserved" }, { EM_res159, "Reserved" }, { EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor" }, { EM_CYPRESS_M8C, "Cypress M8C microprocessor" }, { EM_R32C, "Renesas R32C series microprocessors" }, { EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family" }, { EM_QDSP6, "QUALCOMM DSP6 Processor" }, { EM_8051, "Intel 8051 and variants" }, { EM_STXP7X, "STMicroelectronics STxP7x family" }, { EM_NDS32, "Andes Technology compact code size embedded RISC processor family" }, { EM_ECOG1, "Cyan Technology eCOG1X family" }, { EM_ECOG1X, "Cyan Technology eCOG1X family" }, { EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core Micro-controllers" }, { EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor" }, { EM_MANIK, "M2000 Reconfigurable RISC Microprocessor" }, { EM_CRAYNV2, "Cray Inc. NV2 vector architecture" }, { EM_RX, "Renesas RX family" }, { EM_METAG, "Imagination Technologies META processor architecture" }, { EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture" }, { EM_ECOG16, "Cyan Technology eCOG16 family" }, { EM_CR16, "National Semiconductor CompactRISC 16-bit processor" }, { EM_ETPU, "Freescale Extended Time Processing Unit" }, { EM_SLE9X, "Infineon Technologies SLE9X core" }, { EM_L1OM, "Intel L1OM" }, { EM_INTEL181, "Reserved by Intel" }, { EM_INTEL182, "Reserved by Intel" }, { EM_res183, "Reserved by ARM" }, { EM_res184, "Reserved by ARM" }, { EM_AVR32, "Atmel Corporation 32-bit microprocessor family" }, { EM_STM8, "STMicroeletronics STM8 8-bit microcontroller" }, { EM_TILE64, "Tilera TILE64 multicore architecture family" }, { EM_TILEPRO, "Tilera TILEPro multicore architecture family" }, { EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core" }, { EM_CUDA, "NVIDIA CUDA architecture " }, }; static struct section_type_table_t { const Elf64_Half key; const char* str; } section_type_table[] = { { SHT_NULL, "NULL" }, { SHT_PROGBITS, "PROGBITS" }, { SHT_SYMTAB, "SYMTAB" }, { SHT_STRTAB, "STRTAB" }, { SHT_RELA, "RELA" }, { SHT_HASH, "HASH" }, { SHT_DYNAMIC, "DYNAMIC" }, { SHT_NOTE, "NOTE" }, { SHT_NOBITS, "NOBITS" }, { SHT_REL, "REL" }, { SHT_SHLIB, "SHLIB" }, { SHT_DYNSYM, "DYNSYM" }, { SHT_INIT_ARRAY, "INIT_ARRAY" }, { SHT_FINI_ARRAY, "FINI_ARRAY" }, { SHT_PREINIT_ARRAY, "PREINIT_ARRAY" }, { SHT_GROUP, "GROUP" }, { SHT_SYMTAB_SHNDX, "SYMTAB_SHNDX " }, }; static struct segment_type_table_t { const Elf_Word key; const char* str; } segment_type_table[] = { { PT_NULL, "NULL" }, { PT_LOAD, "LOAD" }, { PT_DYNAMIC, "DYNAMIC" }, { PT_INTERP, "INTERP" }, { PT_NOTE, "NOTE" }, { PT_SHLIB, "SHLIB" }, { PT_PHDR, "PHDR" }, { PT_TLS, "TLS" }, }; static struct segment_flag_table_t { const Elf_Word key; const char* str; } segment_flag_table[] = { { 0, "" }, { 1, "X" }, { 2, "W" }, { 3, "WX" }, { 4, "R" }, { 5, "RX" }, { 6, "RW" }, { 7, "RWX" }, }; static struct symbol_bind_t { const Elf_Word key; const char* str; } symbol_bind_table[] = { { STB_LOCAL, "LOCAL" }, { STB_GLOBAL, "GLOBAL" }, { STB_WEAK, "WEAK" }, { STB_LOOS, "LOOS" }, { STB_HIOS, "HIOS" }, { STB_MULTIDEF, "MULTIDEF" }, { STB_LOPROC, "LOPROC" }, { STB_HIPROC, "HIPROC" }, }; static struct symbol_type_t { const Elf_Word key; const char* str; } symbol_type_table[] = { { STT_NOTYPE, "NOTYPE" }, { STT_OBJECT, "OBJECT" }, { STT_FUNC, "FUNC" }, { STT_SECTION, "SECTION" }, { STT_FILE, "FILE" }, { STT_COMMON, "COMMON" }, { STT_TLS, "TLS" }, { STT_LOOS, "LOOS" }, { STT_HIOS, "HIOS" }, { STT_LOPROC, "LOPROC" }, { STT_HIPROC, "HIPROC" }, }; static struct dynamic_tag_t { const Elf_Word key; const char* str; } dynamic_tag_table[] = { { DT_NULL, "NULL" }, { DT_NEEDED, "NEEDED" }, { DT_PLTRELSZ, "PLTRELSZ" }, { DT_PLTGOT, "PLTGOT" }, { DT_HASH, "HASH" }, { DT_STRTAB, "STRTAB" }, { DT_SYMTAB, "SYMTAB" }, { DT_RELA, "RELA" }, { DT_RELASZ, "RELASZ" }, { DT_RELAENT, "RELAENT" }, { DT_STRSZ, "STRSZ" }, { DT_SYMENT, "SYMENT" }, { DT_INIT, "INIT" }, { DT_FINI, "FINI" }, { DT_SONAME, "SONAME" }, { DT_RPATH, "RPATH" }, { DT_SYMBOLIC, "SYMBOLIC" }, { DT_REL, "REL" }, { DT_RELSZ, "RELSZ" }, { DT_RELENT, "RELENT" }, { DT_PLTREL, "PLTREL" }, { DT_DEBUG, "DEBUG" }, { DT_TEXTREL, "TEXTREL" }, { DT_JMPREL, "JMPREL" }, { DT_BIND_NOW, "BIND_NOW" }, { DT_INIT_ARRAY, "INIT_ARRAY" }, { DT_FINI_ARRAY, "FINI_ARRAY" }, { DT_INIT_ARRAYSZ, "INIT_ARRAYSZ" }, { DT_FINI_ARRAYSZ, "FINI_ARRAYSZ" }, { DT_RUNPATH, "RUNPATH" }, { DT_FLAGS, "FLAGS" }, { DT_ENCODING, "ENCODING" }, { DT_PREINIT_ARRAY, "PREINIT_ARRAY" }, { DT_PREINIT_ARRAYSZ, "PREINIT_ARRAYSZ" }, { DT_MAXPOSTAGS, "MAXPOSTAGS" }, }; static const ELFIO::Elf_Xword MAX_DATA_ENTRIES = 64; //------------------------------------------------------------------------------ class dump { #define DUMP_DEC_FORMAT( width ) \ std::setw( width ) << std::setfill( ' ' ) << std::dec << std::right #define DUMP_HEX_FORMAT( width ) \ std::setw( width ) << std::setfill( '0' ) << std::hex << std::right #define DUMP_STR_FORMAT( width ) \ std::setw( width ) << std::setfill( ' ' ) << std::hex << std::left public: //------------------------------------------------------------------------------ static void header( std::ostream& out, const elfio& reader ) { if ( !reader.get_header_size() ) { return; } out << "ELF Header" << std::endl << std::endl << " Class: " << str_class( reader.get_class() ) << std::endl << " Encoding: " << str_endian( reader.get_encoding() ) << std::endl << " ELFVersion: " << str_version( reader.get_elf_version() ) << std::endl << " Type: " << str_type( reader.get_type() ) << std::endl << " Machine: " << str_machine( reader.get_machine() ) << std::endl << " Version: " << str_version( reader.get_version() ) << std::endl << " Entry: " << "0x" << std::hex << reader.get_entry() << std::endl << " Flags: " << "0x" << std::hex << reader.get_flags() << std::endl << std::endl; } //------------------------------------------------------------------------------ static void section_headers( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); if ( n == 0 ) { return; } out << "Section Headers:" << std::endl; if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit out << "[ Nr ] Type Addr Size ES Flg Lk Inf " "Al Name" << std::endl; } else { // Output for 64-bit out << "[ Nr ] Type Addr Size " " ES Flg" << std::endl << " Lk Inf Al Name" << std::endl; } for ( Elf_Half i = 0; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; section_header( out, i, sec, reader.get_class() ); } out << "Key to Flags: W (write), A (alloc), X (execute)\n\n" << std::endl; } //------------------------------------------------------------------------------ static void section_header( std::ostream& out, Elf_Half no, const section* sec, unsigned char elf_class ) { std::ios_base::fmtflags original_flags = out.flags(); if ( elf_class == ELFCLASS32 ) { // Output for 32-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) << " " << DUMP_HEX_FORMAT( 8 ) << sec->get_address() << " " << DUMP_HEX_FORMAT( 8 ) << sec->get_size() << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_entry_size() << " " << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_link() << " " << DUMP_HEX_FORMAT( 3 ) << sec->get_info() << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_addr_align() << " " << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; } else { // Output for 64-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) << " " << DUMP_HEX_FORMAT( 16 ) << sec->get_address() << " " << DUMP_HEX_FORMAT( 16 ) << sec->get_size() << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_entry_size() << " " << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) << " " << std::endl << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_link() << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_info() << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_addr_align() << " " << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; } out.flags( original_flags ); return; } //------------------------------------------------------------------------------ static void segment_headers( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.segments.size(); if ( n == 0 ) { return; } out << "Segment headers:" << std::endl; if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit out << "[ Nr ] Type VirtAddr PhysAddr FileSize Mem.Size " "Flags Align" << std::endl; } else { // Output for 64-bit out << "[ Nr ] Type VirtAddr PhysAddr " "Flags" << std::endl << " FileSize Mem.Size " "Align" << std::endl; } for ( Elf_Half i = 0; i < n; ++i ) { segment* seg = reader.segments[i]; segment_header( out, i, seg, reader.get_class() ); } out << std::endl; } //------------------------------------------------------------------------------ static void segment_header( std::ostream& out, Elf_Half no, const segment* seg, unsigned int elf_class ) { std::ios_base::fmtflags original_flags = out.flags(); if ( elf_class == ELFCLASS32 ) { // Output for 32-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_virtual_address() << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_physical_address() << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_file_size() << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_memory_size() << " " << DUMP_STR_FORMAT( 8 ) << str_segment_flag( seg->get_flags() ) << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_align() << " " << std::endl; } else { // Output for 64-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_virtual_address() << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_physical_address() << " " << DUMP_STR_FORMAT( 16 ) << str_segment_flag( seg->get_flags() ) << " " << std::endl << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_file_size() << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_memory_size() << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_align() << " " << std::endl; } out.flags( original_flags ); } //------------------------------------------------------------------------------ static void symbol_tables( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); for ( Elf_Half i = 0; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; if ( SHT_SYMTAB == sec->get_type() || SHT_DYNSYM == sec->get_type() ) { symbol_section_accessor symbols( reader, sec ); Elf_Xword sym_no = symbols.get_symbols_num(); if ( sym_no > 0 ) { out << "Symbol table (" << sec->get_name() << ")" << std::endl; if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit out << "[ Nr ] Value Size Type Bind " "Sect Name" << std::endl; } else { // Output for 64-bit out << "[ Nr ] Value Size Type " " Bind Sect" << std::endl << " Name" << std::endl; } for ( Elf_Xword i = 0; i < sym_no; ++i ) { std::string name; Elf64_Addr value = 0; Elf_Xword size = 0; unsigned char bind = 0; unsigned char type = 0; Elf_Half section = 0; unsigned char other = 0; symbols.get_symbol( i, name, value, size, bind, type, section, other ); symbol_table( out, i, name, value, size, bind, type, section, reader.get_class() ); } out << std::endl; } } } } //------------------------------------------------------------------------------ static void symbol_table( std::ostream& out, Elf_Xword no, std::string& name, Elf64_Addr value, Elf_Xword size, unsigned char bind, unsigned char type, Elf_Half section, unsigned int elf_class ) { std::ios_base::fmtflags original_flags = out.flags(); if ( elf_class == ELFCLASS32 ) { // Output for 32-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_HEX_FORMAT( 8 ) << value << " " << DUMP_HEX_FORMAT( 8 ) << size << " " << DUMP_STR_FORMAT( 7 ) << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) << section << " " << DUMP_STR_FORMAT( 1 ) << name << " " << std::endl; } else { // Output for 64-bit out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_HEX_FORMAT( 16 ) << value << " " << DUMP_HEX_FORMAT( 16 ) << size << " " << DUMP_STR_FORMAT( 7 ) << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) << section << " " << std::endl << " " << DUMP_STR_FORMAT( 1 ) << name << " " << std::endl; } out.flags( original_flags ); } //------------------------------------------------------------------------------ static void notes( std::ostream& out, const elfio& reader ) { Elf_Half no = reader.sections.size(); for ( Elf_Half i = 0; i < no; ++i ) { // For all sections section* sec = reader.sections[i]; if ( SHT_NOTE == sec->get_type() ) { // Look at notes note_section_accessor notes( reader, sec ); Elf_Word no_notes = notes.get_notes_num(); if ( no > 0 ) { out << "Note section (" << sec->get_name() << ")" << std::endl << " No Type Name" << std::endl; for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes Elf_Word type; std::string name; void* desc; Elf_Word descsz; if ( notes.get_note( j, type, name, desc, descsz ) ) { // 'name' usually contains \0 at the end. Try to fix it name = name.c_str(); note( out, j, type, name ); } } out << std::endl; } } } } //------------------------------------------------------------------------------ static void modinfo( std::ostream& out, const elfio& reader ) { Elf_Half no = reader.sections.size(); for ( Elf_Half i = 0; i < no; ++i ) { // For all sections section* sec = reader.sections[i]; if ( ".modinfo" == sec->get_name() ) { // Look for the section out << "Section .modinfo" << std::endl; const_modinfo_section_accessor modinfo( sec ); for ( Elf_Word i = 0; i < modinfo.get_attribute_num(); i++ ) { std::string field; std::string value; if ( modinfo.get_attribute( i, field, value ) ) { out << " " << std::setw( 20 ) << field << std::setw( 0 ) << " = " << value << std::endl; } } out << std::endl; break; } } } //------------------------------------------------------------------------------ static void note( std::ostream& out, int no, Elf_Word type, const std::string& name ) { out << " [" << DUMP_DEC_FORMAT( 2 ) << no << "] " << DUMP_HEX_FORMAT( 8 ) << type << " " << DUMP_STR_FORMAT( 1 ) << name << std::endl; } //------------------------------------------------------------------------------ static void dynamic_tags( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); for ( Elf_Half i = 0; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; if ( SHT_DYNAMIC == sec->get_type() ) { dynamic_section_accessor dynamic( reader, sec ); Elf_Xword dyn_no = dynamic.get_entries_num(); if ( dyn_no > 0 ) { out << "Dynamic section (" << sec->get_name() << ")" << std::endl; out << "[ Nr ] Tag Name/Value" << std::endl; for ( Elf_Xword i = 0; i < dyn_no; ++i ) { Elf_Xword tag = 0; Elf_Xword value = 0; std::string str; dynamic.get_entry( i, tag, value, str ); dynamic_tag( out, i, tag, value, str, reader.get_class() ); if ( DT_NULL == tag ) { break; } } out << std::endl; } } } } //------------------------------------------------------------------------------ static void dynamic_tag( std::ostream& out, Elf_Xword no, Elf_Xword tag, Elf_Xword value, std::string str, unsigned int /*elf_class*/ ) { out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " << DUMP_STR_FORMAT( 16 ) << str_dynamic_tag( tag ) << " "; if ( str.empty() ) { out << DUMP_HEX_FORMAT( 16 ) << value << " "; } else { out << DUMP_STR_FORMAT( 32 ) << str << " "; } out << std::endl; } //------------------------------------------------------------------------------ static void section_data( std::ostream& out, const section* sec ) { std::ios_base::fmtflags original_flags = out.flags(); out << sec->get_name() << std::endl; const char* pdata = sec->get_data(); if ( pdata ) { ELFIO::Elf_Xword i; for ( i = 0; i < std::min( sec->get_size(), MAX_DATA_ENTRIES ); ++i ) { if ( i % 16 == 0 ) { out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; } out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); if ( i % 16 == 15 ) { out << std::endl; } } if ( i % 16 != 0 ) { out << std::endl; } out.flags( original_flags ); } return; } //------------------------------------------------------------------------------ static void section_datas( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.sections.size(); if ( n == 0 ) { return; } out << "Section Data:" << std::endl; for ( Elf_Half i = 1; i < n; ++i ) { // For all sections section* sec = reader.sections[i]; if ( sec->get_type() == SHT_NOBITS ) { continue; } section_data( out, sec ); } out << std::endl; } //------------------------------------------------------------------------------ static void segment_data( std::ostream& out, Elf_Half no, const segment* seg ) { std::ios_base::fmtflags original_flags = out.flags(); out << "Segment # " << no << std::endl; const char* pdata = seg->get_data(); if ( pdata ) { ELFIO::Elf_Xword i; for ( i = 0; i < std::min( seg->get_file_size(), MAX_DATA_ENTRIES ); ++i ) { if ( i % 16 == 0 ) { out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; } out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); if ( i % 16 == 15 ) { out << std::endl; } } if ( i % 16 != 0 ) { out << std::endl; } out.flags( original_flags ); } return; } //------------------------------------------------------------------------------ static void segment_datas( std::ostream& out, const elfio& reader ) { Elf_Half n = reader.segments.size(); if ( n == 0 ) { return; } out << "Segment Data:" << std::endl; for ( Elf_Half i = 0; i < n; ++i ) { // For all sections segment* seg = reader.segments[i]; segment_data( out, i, seg ); } out << std::endl; } private: //------------------------------------------------------------------------------ template std::string static find_value_in_table( const T& table, const K& key ) { std::string res = "?"; for ( unsigned int i = 0; i < sizeof( table ) / sizeof( table[0] ); ++i ) { if ( table[i].key == key ) { res = table[i].str; break; } } return res; } //------------------------------------------------------------------------------ template static std::string format_assoc( const T& table, const K& key ) { std::string str = find_value_in_table( table, key ); if ( str == "?" ) { std::ostringstream oss; oss << str << " (0x" << std::hex << key << ")"; str = oss.str(); } return str; } //------------------------------------------------------------------------------ template static std::string format_assoc( const T& table, const char key ) { return format_assoc( table, (const int)key ); } //------------------------------------------------------------------------------ static std::string section_flags( Elf_Xword flags ) { std::string ret = ""; if ( flags & SHF_WRITE ) { ret += "W"; } if ( flags & SHF_ALLOC ) { ret += "A"; } if ( flags & SHF_EXECINSTR ) { ret += "X"; } return ret; } //------------------------------------------------------------------------------ #define STR_FUNC_TABLE( name ) \ template static std::string str_##name( const T key ) \ { \ return format_assoc( name##_table, key ); \ } STR_FUNC_TABLE( class ) STR_FUNC_TABLE( endian ) STR_FUNC_TABLE( version ) STR_FUNC_TABLE( type ) STR_FUNC_TABLE( machine ) STR_FUNC_TABLE( section_type ) STR_FUNC_TABLE( segment_type ) STR_FUNC_TABLE( segment_flag ) STR_FUNC_TABLE( symbol_bind ) STR_FUNC_TABLE( symbol_type ) STR_FUNC_TABLE( dynamic_tag ) #undef STR_FUNC_TABLE #undef DUMP_DEC_FORMAT #undef DUMP_HEX_FORMAT #undef DUMP_STR_FORMAT }; // class dump }; // namespace ELFIO #endif // ELFIO_DUMP_HPP /*** End of inlined file: elfio_dump.hpp ***/ ukui-screensaver/tests/kt-test-utils/cpp-stub/addr_pri.h0000664000175000017500000002436415172041035022367 0ustar fengfeng#ifndef __ADDR_PRI_H__ #define __ADDR_PRI_H__ #include #include //base on C++11 /********************************************************** access private function **********************************************************/ namespace std { template using enable_if_t = typename enable_if::type; template using remove_reference_t = typename remove_reference::type; } // std // Unnamed namespace is used to avoid duplicate symbols if the macros are used namespace { namespace private_access_detail { // @tparam TagType, used to declare different "get" funciton overloads for // different members/statics template struct private_access { // Normal lookup cannot find in-class defined (inline) friend functions. friend PtrType get(TagType) { return PtrValue; } }; } // namespace private_access_detail } // namespace // Used macro naming conventions: // The "namespace" of this macro library is PRIVATE_ACCESS, i.e. all // macro here has this prefix. // All implementation macro, which are not meant to be used directly have the // PRIVATE_ACCESS_DETAIL prefix. // Some macros have the ABCD_IMPL form, which means they contain the // implementation details for the specific ABCD macro. #define PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) x##y #define PRIVATE_ACCESS_DETAIL_CONCATENATE(x, y) \ PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) // @param PtrTypeKind E.g if we have "class A", then it can be "A::*" in case of // members, or it can be "*" in case of statics. #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, \ PtrTypeKind) \ namespace { \ namespace private_access_detail { \ /* Tag type, used to declare different get funcitons for different \ * members \ */ \ struct Tag {}; \ /* Explicit instantiation */ \ template struct private_access; \ /* We can build the PtrType only with two aliases */ \ /* E.g. using PtrType = int(int) *; would be illformed */ \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) = Type; \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) = \ PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) PtrTypeKind; \ /* Declare the friend function, now it is visible in namespace scope. \ * Note, \ * we could declare it inside the Tag type too, in that case ADL would \ * find \ * the declaration. By choosing to declare it here, the Tag type remains \ * a \ * simple tag type, it has no other responsibilities. */ \ PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) get(Tag); \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(Tag, Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ namespace { \ namespace access_private_field { \ Type &Class##Name(Class &&t) { return t.*get(private_access_detail::Tag{}); } \ Type &Class##Name(Class &t) { return t.*get(private_access_detail::Tag{}); } \ /* The following usings are here to avoid duplicate const qualifier \ * warnings \ */ \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag) = Type; \ using PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) = \ const PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag); \ PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) & Class##Name(const Class &t) {\ return t.*get(private_access_detail::Tag{}); \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(Tag, Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ namespace { \ namespace call_private_fun { \ /* We do perfect forwarding, but we want to restrict the overload set \ * only for objects which have the type Class. */ \ template , \ Class>::value> * = nullptr, \ typename... Args> \ auto Class##Name(Obj &&o, Args &&... args) -> decltype( \ (std::forward(o).* \ get(private_access_detail::Tag{}))(std::forward(args)...)) { \ return (std::forward(o).*get(private_access_detail::Tag{}))( \ std::forward(args)...); \ } \ } \ namespace get_private_fun { \ auto Class##Name() -> decltype( \ get(private_access_detail::Tag{})) { \ return (get(private_access_detail::Tag{})); \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD(Tag, Class, Type, \ Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ namespace { \ namespace access_private_static_field { \ namespace Class { \ Type &Class##Name() { return *get(private_access_detail::Tag{}); } \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN(Tag, Class, Type, \ Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ namespace { \ namespace call_private_static_fun { \ namespace Class { \ template \ auto Class##Name(Args &&... args) -> decltype( \ get(private_access_detail::Tag{})(std::forward(args)...)) { \ return get(private_access_detail::Tag{})( \ std::forward(args)...); \ } \ } \ } \ namespace get_private_static_fun { \ namespace Class { \ auto Class##Name() -> decltype(get(private_access_detail::Tag{})) { \ return get(private_access_detail::Tag{}); \ } \ } \ } \ } #define PRIVATE_ACCESS_DETAIL_UNIQUE_TAG \ PRIVATE_ACCESS_DETAIL_CONCATENATE(PrivateAccessTag, __COUNTER__) #define ACCESS_PRIVATE_FIELD(Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ Class, Type, Name) #define ACCESS_PRIVATE_FUN(Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ Class, Type, Name) #define ACCESS_PRIVATE_STATIC_FIELD(Class, Type, Name) \ Type Class::Name; \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD( \ PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) #define ACCESS_PRIVATE_STATIC_FUN(Class, Type, Name) \ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN( \ PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) #endif ukui-screensaver/tests/kt-test-utils/cpp-stub/addr_any.h0000664000175000017500000002115215172041035022354 0ustar fengfeng#ifndef __ADDR_ANY_H__ #define __ADDR_ANY_H__ //linux #include #include //c #include #include #include //c++ #include #include //project #include "elfio.hpp" class AddrAny { public: AddrAny() { m_init = get_exe_pathname(m_fullname); m_baseaddr = 0; } AddrAny(std::string libname) { m_init = get_lib_pathname_and_baseaddr(libname, m_fullname, m_baseaddr); } int get_local_func_addr_symtab(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_SYMTAB, STB_LOCAL, func_name_regex_str, result); } int get_global_func_addr_symtab(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_SYMTAB, STB_GLOBAL, func_name_regex_str, result); } int get_weak_func_addr_symtab(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_SYMTAB, STB_WEAK, func_name_regex_str, result); } int get_global_func_addr_dynsym( std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_DYNSYM, STB_GLOBAL, func_name_regex_str, result); } int get_weak_func_addr_dynsym(std::string func_name_regex_str, std::map& result) { return get_func_addr(SHT_DYNSYM, STB_WEAK, func_name_regex_str, result); } private: bool demangle(std::string& s, std::string& name) { int status; char* pname = abi::__cxa_demangle(s.c_str(), 0, 0, &status); if (status != 0) { switch(status) { case -1: name = "memory allocation error"; break; case -2: name = "invalid name given"; break; case -3: name = "internal error: __cxa_demangle: invalid argument"; break; default: name = "unknown error occured"; break; } return false; } name = pname; free(pname); return true; } bool get_exe_pathname( std::string& name) { char line[512]; FILE *fp; uintptr_t base_addr; char perm[5]; unsigned long offset; int pathname_pos; char *pathname; size_t pathname_len; int match = 0; if(NULL == (fp = fopen("/proc/self/maps", "r"))) { return false; } while(fgets(line, sizeof(line), fp)) { if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; if(0 != offset) continue; //get pathname while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) pathname_pos += 1; if(pathname_pos >= (int)(sizeof(line) - 1)) continue; pathname = line + pathname_pos; pathname_len = strnlen(pathname, 1024); if(0 == pathname_len) continue; if(pathname[pathname_len - 1] == '\n') { pathname[pathname_len - 1] = '\0'; pathname_len -= 1; } if(0 == pathname_len) continue; if('[' == pathname[0]) continue; name = pathname; match = 1; break; } fclose(fp); if(0 == match) { return false; } else { return true; } } bool get_lib_pathname_and_baseaddr(std::string pathname_regex_str, std::string& name, unsigned long& addr) { char line[512]; FILE *fp; uintptr_t base_addr; char perm[5]; unsigned long offset; int pathname_pos; char *pathname; size_t pathname_len; int match; regex_t pathname_regex; regcomp(&pathname_regex, pathname_regex_str.c_str(), 0); if(NULL == (fp = fopen("/proc/self/maps", "r"))) { return false; } while(fgets(line, sizeof(line), fp)) { if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; //check permission if(perm[0] != 'r') continue; if(perm[3] != 'p') continue; //do not touch the shared memory //check offset // //We are trying to find ELF header in memory. //It can only be found at the beginning of a mapped memory regions //whose offset is 0. if(0 != offset) continue; //get pathname while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) pathname_pos += 1; if(pathname_pos >= (int)(sizeof(line) - 1)) continue; pathname = line + pathname_pos; pathname_len = strnlen(pathname, 1024); if(0 == pathname_len) continue; if(pathname[pathname_len - 1] == '\n') { pathname[pathname_len - 1] = '\0'; pathname_len -= 1; } if(0 == pathname_len) continue; if('[' == pathname[0]) continue; //check pathname //if we need to hook this elf? match = 0; if(0 == regexec(&pathname_regex, pathname, 0, NULL, 0)) { match = 1; name = pathname; addr = (unsigned long)base_addr; break; } if(0 == match) continue; } fclose(fp); if(0 == match) { return false; } else { return true; } } int get_func_addr(unsigned int ttype, unsigned int stype, std::string& func_name_regex_str, std::map& result) { // Create an elfio reader ELFIO::elfio reader; int count = 0; regex_t pathname_regex; if(!m_init) { return -1; } regcomp(&pathname_regex, func_name_regex_str.c_str(), 0); // Load ELF data if(!reader.load(m_fullname.c_str())) { return -1; } ELFIO::Elf_Half sec_num = reader.sections.size(); for(int i = 0; i < sec_num; ++i) { ELFIO::section* psec = reader.sections[i]; // Check section type if(psec->get_type() == ttype) { const ELFIO::symbol_section_accessor symbols( reader, psec ); for ( unsigned int j = 0; j < symbols.get_symbols_num(); ++j ) { std::string name; std::string name_mangle; ELFIO::Elf64_Addr value; ELFIO::Elf_Xword size; unsigned char bind; unsigned char type; ELFIO::Elf_Half section_index; unsigned char other; // Read symbol properties symbols.get_symbol( j, name, value, size, bind, type, section_index, other ); if(type == STT_FUNC && bind == stype) { bool ret = demangle(name,name_mangle); if(ret == true) { if (0 == regexec(&pathname_regex, name_mangle.c_str(), 0, NULL, 0)) { result.insert ( std::pair(name_mangle,(void*)(value + m_baseaddr))); count++; } } else { if (0 == regexec(&pathname_regex, name.c_str(), 0, NULL, 0)) { result.insert ( std::pair(name,(void*)(value + m_baseaddr))); count++; } } } } break; } } return count; } private: bool m_init; std::string m_name; std::string m_fullname; unsigned long m_baseaddr; }; #endif ukui-screensaver/tests/unit_test_lock_dialog_model/0000775000175000017500000000000015172041106021657 5ustar fengfengukui-screensaver/tests/unit_test_lock_dialog_model/unit_test_lock_dialog_model.cpp0000664000175000017500000000460615172041035030117 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/lock-dialog/lockdialogmodel.h" #include "../../src/common/definetypes.h" #include "../../src/agreementinfo.h" #include "stubext.h" using namespace stub_ext; // 测试夹具类,用于初始化和清理测试环境 class LockDialogModelTest : public ::testing::Test { protected: LockDialogModel* model; virtual void SetUp() { model = new LockDialogModel(nullptr); } virtual void TearDown() { delete model; } }; TEST_F(LockDialogModelTest, ParseCmdArguments) { QStringList args; LockDialogModel::CommandLineArgs cmdArgs; // 天翼云情况测试(模拟天翼云平台) args << "--lock-startup"; setenv("HostCloundPlatform", "ctyun", 1); EXPECT_TRUE(model->parseCmdArguments(args, cmdArgs)); unsetenv("HostCloundPlatform"); // 非天翼云且参数不全情况测试 args.clear(); // EXPECT_FALSE(model->parseCmdArguments(args, cmdArgs)); // 完整参数设置测试 args << "--lock"; EXPECT_TRUE(model->parseCmdArguments(args, cmdArgs)); EXPECT_TRUE(cmdArgs.isLock); } TEST_F(LockDialogModelTest, FindUserByName) { // 查找已存在的用户 UserInfoPtr result = model->findUserByName("testuser"); EXPECT_NE(result, nullptr); // 查找不存在的用户 result = model->findUserByName("nonexistentuser"); EXPECT_EQ(result, nullptr); } TEST_F(LockDialogModelTest, GetScreensaver) { ScreenSaver* saver = model->getScreensaver(); EXPECT_EQ(saver->mode, SAVER_RANDOM); delete saver; saver = model->getScreensaver(); EXPECT_EQ(saver->mode, SAVER_IMAGE); delete saver; } ukui-screensaver/tests/unit_test_lock_dialog_model/CMakeLists.txt0000664000175000017500000000455115172041106024424 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Widgets X11Extras Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) find_package(X11 REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) pkg_check_modules(XCB REQUIRED xcb) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-dialog/lockdialogmodel.cpp ../../src/userinfo.cpp ../../src/agreementinfo.cpp ../../src/common/biodefines.cpp ../../src/common/global_utils.cpp ../..//src/widgets/screensavermode.cpp ../../src/common/utils.cpp unit_test_lock_dialog_model.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-dialog/lockdialogmodel.h ../../src/common/definetypes.h ../../src/common/biodefines.h ../../src/userinfo.h ../../src/agreementinfo.h ../../src/common/global_utils.h ../..//src/widgets/screensavermode.h ../../src/common/utils.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${GIOUNIX2_INCLUDE_DIRS} ${X11_INCLUDE_DIRS} ${XCB_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_lock_dialog_model ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_lock_dialog_model Qt5::Core Qt5::Gui Qt5::DBus Qt5::Widgets Qt5::X11Extras ) # 链接 GTest 库 target_link_libraries(unit_test_lock_dialog_model GTest::GTest GTest::Main Threads::Threads ${GIOUNIX2_LIBRARIES} ${X11_LIBRARIES} ${XCB_LIBRARIES} ) ukui-screensaver/tests/unit_test_lock_dialog_model/main.cpp0000664000175000017500000000156215172041035023314 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_personal_data/0000775000175000017500000000000015172041106021044 5ustar fengfengukui-screensaver/tests/unit_test_personal_data/unit_test_personal_data.cpp0000664000175000017500000001533715172041035026474 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "../../src/lock-backend/personalizeddata.h" #include "gtest/gtest.h" #include #include #include #include "stubext.h" using namespace stub_ext; // 测试夹具类,用于初始化和清理测试环境 class PersonalizedDataTest : public ::testing::Test { protected: PersonalizedData* personalizedData; // PersonalizedDataMng* personalizedDataMng; virtual void SetUp() override { personalizedData = new PersonalizedData("testUser"); // personalizedDataMng = new PersonalizedDataMng(); } virtual void TearDown() override { delete personalizedData; // delete personalizedDataMng; } }; // 测试 PersonalizedData 类的构造函数和析构函数 TEST_F(PersonalizedDataTest, PersonalizedDataConstructorAndDestructor) { // 构造函数应该正确设置用户 KYLINUSERDATAMNG::getInstance()->GetUserPtr(getenv("USER")); KYLINUSERDATAMNG::getInstance()->GetConfInformation(getenv("USER")); // KYLINUSERDATAMNG::getInstance()->__getUserInfomation(); // EXPECT_EQ(personalizedData->m_user, "testUser"); } // 测试 PersonalizedData::getJsonData 函数 TEST_F(PersonalizedDataTest, PersonalizedDataGetJsonData) { QJsonObject json; personalizedData->getJsonData(json); // 检查生成的 JSON 对象是否包含正确的信息 EXPECT_EQ(json["user"].toString(), "testUser"); EXPECT_EQ(json["dateType"].toString(), "testDateType"); EXPECT_EQ(json["fontSize"].toInt(), 12); EXPECT_EQ(json["timeType"].toInt(), 24); EXPECT_EQ(json["backgroundPath"].toString(), "/path/to/background"); EXPECT_EQ(json["color"].toString(), "red"); } // 测试 PersonalizedData::setJson 函数 TEST_F(PersonalizedDataTest, PersonalizedDataSetJson) { QJsonObject json; json["user"] = "testUser"; json["dateType"] = "newDateType"; json["fontSize"] = 14; json["timeType"] = 12; json["backgroundPath"] = "/new/path/to/background"; json["color"] = "blue"; personalizedData->setJson(json); // 检查属性是否被正确设置 // EXPECT_EQ(personalizedData->m_dateType, "newDateType"); // EXPECT_EQ(personalizedData->m_fontSize, 14); // EXPECT_EQ(personalizedData->m_timeType, 12); // EXPECT_EQ(personalizedData->m_backgroundPath, "/new/path/to/background"); // EXPECT_EQ(personalizedData->m_color, "blue"); } //// 测试 PersonalizedDataMng::GetConfInformation 函数 //TEST_F(PersonalizedDataTest, PersonalizedDataMngGetConfInformation) { // // 先添加一些用户信息 // personalizedDataMng->m_userPersonalizedData["testUser"] = KylinUserDatePtr(new PersonalizedData("testUser")); // personalizedDataMng->m_userPersonalizedData["testUser"]->m_dateType = "testDateType"; // personalizedDataMng->m_userPersonalizedData["testUser"]->m_fontSize = 12; // personalizedDataMng->m_userPersonalizedData["testUser"]->m_timeType = 24; // personalizedDataMng->m_userPersonalizedData["testUser"]->m_backgroundPath = "/path/to/background"; // personalizedDataMng->m_userPersonalizedData["testUser"]->m_color = "red"; // QString jsonStr = personalizedDataMng->GetConfInformation("testUser"); // QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8()); // QJsonObject json = doc.object(); // QJsonObject userJson = json["testUser"].toObject(); // // 检查生成的 JSON 对象是否包含正确的信息 // EXPECT_EQ(userJson["greeter"]["dateType"].toString(), "testDateType"); // EXPECT_EQ(userJson["greeter"]["fontSize"].toInt(), 12); // EXPECT_EQ(userJson["greeter"]["timeType"].toInt(), 24); // EXPECT_EQ(userJson["greeter"]["backgroundPath"].toString(), "/path/to/background"); // EXPECT_EQ(userJson["greeter"]["color"].toString(), "red"); //} //// 测试 PersonalizedDataMng::GetUserPtr 函数 //TEST_F(PersonalizedDataTest, PersonalizedDataMngGetUserPtr) { // personalizedDataMng->m_userPersonalizedData["testUser"] = KylinUserDatePtr(new PersonalizedData("testUser")); // KylinUserDatePtr userPtr = personalizedDataMng->GetUserPtr("testUser"); // // 检查是否能正确获取用户指针 // EXPECT_NE(userPtr, nullptr); // EXPECT_EQ(userPtr->m_user, "testUser"); //} //// 测试 PersonalizedDataMng::updateUserInformation 函数 //TEST_F(PersonalizedDataTest, PersonalizedDataMngUpdateUserInformation) { // QString jsonString = "{\"user\":\"testUser\",\"operation\":0,\"greeter\":{\"dateType\":\"newDateType\",\"fontSize\":14,\"timeType\":12,\"backgroundPath\":\"/new/path/to/background\",\"color\":\"blue\"}}"; // personalizedDataMng->updateUserInformation(jsonString); // KylinUserDatePtr userPtr = personalizedDataMng->GetUserPtr("testUser"); // // 检查用户信息是否被正确更新 // EXPECT_EQ(userPtr->m_dateType, "newDateType"); // EXPECT_EQ(userPtr->m_fontSize, 14); // EXPECT_EQ(userPtr->m_timeType, 12); // EXPECT_EQ(userPtr->m_backgroundPath, "/new/path/to/background"); // EXPECT_EQ(userPtr->m_color, "blue"); //} //// 测试 PersonalizedDataMng::__getUserInfomation 函数 //TEST_F(PersonalizedDataTest, PersonalizedDataMngGetUserInfomation) { // // 模拟从 DBus 获取的用户信息 // QString jsonString = "{\"testUser\":{\"greeter\":{\"dateType\":\"newDateType\",\"fontSize\":14,\"timeType\":12,\"backgroundPath\":\"/new/path/to/background\",\"color\":\"blue\"}}"; // QDBusInterface iface( // static_dbus_service_name, static_dbus_path, static_dbus_interface, QDBusConnection::systemBus()); // QDBusPendingCall pcall; // QDBusPendingReply reply(jsonString); // QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, personalizedDataMng); // personalizedDataMng->wait_for_finish(watcher); // personalizedDataMng->__getUserInfomation(); // KylinUserDatePtr userPtr = personalizedDataMng->GetUserPtr("testUser"); // // 检查用户信息是否被正确设置 // EXPECT_EQ(userPtr->m_dateType, "newDateType"); // EXPECT_EQ(userPtr->m_fontSize, 14); // EXPECT_EQ(userPtr->m_timeType, 12); // EXPECT_EQ(userPtr->m_backgroundPath, "/new/path/to/background"); // EXPECT_EQ(userPtr->m_color, "blue"); //} ukui-screensaver/tests/unit_test_personal_data/CMakeLists.txt0000664000175000017500000000327115172041106023607 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(LIGHTDM-QT5-3 REQUIRED liblightdm-qt5-3) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/personalizeddata.cpp unit_test_personal_data.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-backend/personalizeddata.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${LIGHTDM-QT5-3_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_personal_data ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_personal_data Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_personal_data GTest::GTest GTest::Main Threads::Threads ${LIGHTDM-QT5-3_LIBRARIES} ) ukui-screensaver/tests/unit_test_personal_data/main.cpp0000664000175000017500000000156215172041035022501 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/CMakeLists.txt0000664000175000017500000000365715172041106016705 0ustar fengfengfind_package(PkgConfig REQUIRED) include_directories( ) add_subdirectory(unit_test_biometric_helper) add_subdirectory(unit_test_account_helper) add_subdirectory(unit_test_freedesktop_helper) add_subdirectory(unit_test_kglobalaccel_helper) add_subdirectory(unit_test_login1_helper) add_subdirectory(unit_test_session_helper) add_subdirectory(unit_test_systemupgrade_helper) add_subdirectory(unit_test_uniauth_service) add_subdirectory(unit_test_usd_helper) add_subdirectory(unit_test_gsettings_helper) add_subdirectory(unit_test_engine_device) add_subdirectory(unit_test_inputswitch_event) add_subdirectory(unit_test_auth_pam) add_subdirectory(unit_test_common) add_subdirectory(unit_test_switch_user) add_subdirectory(unit_test_session_watcher) add_subdirectory(unit_test_display_service) add_subdirectory(unit_test_dbus_interface) add_subdirectory(unit_test_backend_dbus) add_subdirectory(unit_test_lock_dialog_model) add_subdirectory(unit_test_personal_data) add_subdirectory(unit_test_screensaver) add_subdirectory(unit_test_screenlock) # 处理打桩工具相关配置 set(TEST_UTILS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/kt-test-utils") # 收集打桩工具的头文件,递归查找符合条件的头文件 file(GLOB_RECURSE UTILS_HEADERS "${TEST_UTILS_PATH}/cpp-stub/*.h" "${TEST_UTILS_PATH}/cpp-stub/*.hpp" "${TEST_UTILS_PATH}/cpp-stub-ext/*.h" ) # 收集打桩工具的源文件,递归查找符合条件的源文件 file(GLOB_RECURSE UTILS_SOURCES "${TEST_UTILS_PATH}/cpp-stub/*.cpp" "${TEST_UTILS_PATH}/cpp-stub-ext/*.cpp" ) # 创建一个库(这里以静态库为例,可以根据需求改为共享库等)来存放打桩工具相关代码 add_library(utils_lib STATIC ${UTILS_SOURCES} ${UTILS_HEADERS}) # 将打桩工具库的头文件目录添加到包含路径,方便其他目标使用 target_include_directories(utils_lib PUBLIC "${TEST_UTILS_PATH}/cpp-stub" "${TEST_UTILS_PATH}/cpp-stub-ext" ) ukui-screensaver/tests/unit_test_uniauth_service/0000775000175000017500000000000015172041106021425 5ustar fengfengukui-screensaver/tests/unit_test_uniauth_service/unit_test_uniauth_service.cpp0000664000175000017500000001263115172041035027430 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/uniauthservice.h" #include "stubext.h" using namespace stub_ext; class UniAuthServiceTest : public testing::Test { //protected: // static void SetUpTestSuite() // { // m_pUniAuthServiceDbus = new UniAuthService(); // } // static void TearDownTestSuite() // { // delete m_pUniAuthServiceDbus; // m_pUniAuthServiceDbus = nullptr; // } // static UniAuthService *m_pUniAuthServiceDbus; }; //UniAuthService *UniAuthServiceTest::m_pUniAuthServiceDbus = nullptr; TEST_F(UniAuthServiceTest, DefaultDevice) { if (UniAuthService::instance()) { QString oldValue = UniAuthService::instance()->getDefaultDevice(getenv("USER"), 0); UniAuthService::instance()->setDefaultDevice(0, "testValue"); QString newValue = UniAuthService::instance()->getDefaultDevice(getenv("USER"), 0); std::string str = newValue.toStdString(); const char* cstr = str.data(); ASSERT_STREQ(cstr, "testValue"); UniAuthService::instance()->setDefaultDevice(0, oldValue); } } TEST_F(UniAuthServiceTest, BioAuthStatus) { if (UniAuthService::instance()) { bool oldValue = UniAuthService::instance()->getBioAuthStatus(getenv("USER"), 0); UniAuthService::instance()->setBioAuthStatus(0, !oldValue); bool newValue = UniAuthService::instance()->getBioAuthStatus(getenv("USER"), 0); ASSERT_EQ(newValue, !oldValue); UniAuthService::instance()->setBioAuthStatus(0, oldValue); } } TEST_F(UniAuthServiceTest, AllDefaultDevice) { if (UniAuthService::instance()) { QStringList defaultDeviceList = UniAuthService::instance()->getAllDefaultDevice(getenv("USER")); ASSERT_EQ(defaultDeviceList.isEmpty(), false); } } TEST_F(UniAuthServiceTest, MaxFailedTimes) { if (UniAuthService::instance()) { int maxFailedTimes = UniAuthService::instance()->getMaxFailedTimes(); ASSERT_EQ(maxFailedTimes, 5); } } TEST_F(UniAuthServiceTest, QRCodeEnable) { if (UniAuthService::instance()) { bool qRCodeEnable = UniAuthService::instance()->getQRCodeEnable(); ASSERT_EQ(qRCodeEnable, true); } } TEST_F(UniAuthServiceTest, DoubleAuth) { if (UniAuthService::instance()) { bool doubleAuth = UniAuthService::instance()->getDoubleAuth(); ASSERT_EQ(doubleAuth, false); } } TEST_F(UniAuthServiceTest, UserBind) { if (UniAuthService::instance()) { bool userBind = UniAuthService::instance()->getUserBind(); ASSERT_EQ(userBind, false); } } TEST_F(UniAuthServiceTest, IsShownInControlCenter) { if (UniAuthService::instance()) { bool isShownInControlCenter = UniAuthService::instance()->getIsShownInControlCenter(); ASSERT_EQ(isShownInControlCenter, false); } } TEST_F(UniAuthServiceTest, HiddenSwitchButton) { if (UniAuthService::instance()) { bool hiddenSwitchButton = UniAuthService::instance()->getHiddenSwitchButton(); ASSERT_EQ(hiddenSwitchButton, false); } } TEST_F(UniAuthServiceTest, getFTimeoutTimes) { if (UniAuthService::instance()) { int hiddenSwitchButton = UniAuthService::instance()->getFTimeoutTimes(); ASSERT_EQ(hiddenSwitchButton, 5); } } TEST_F(UniAuthServiceTest, isActivatable) { if (UniAuthService::instance()) { bool hiddenSwitchButton = UniAuthService::instance()->isActivatable(); ASSERT_EQ(hiddenSwitchButton, true); } } TEST_F(UniAuthServiceTest, isUserNameValid) { if (UniAuthService::instance()) { bool hiddenSwitchButton = UniAuthService::instance()->isUserNameValid(getenv("USER")); ASSERT_EQ(hiddenSwitchButton, true); } } TEST_F(UniAuthServiceTest, GetLastLoginUser) { if (UniAuthService::instance()) { QString hiddenSwitchButton = UniAuthService::instance()->GetLastLoginUser(); ASSERT_EQ(hiddenSwitchButton.isEmpty(), false); } } TEST_F(UniAuthServiceTest, GetQuickLoginUser) { if (UniAuthService::instance()) { QString hiddenSwitchButton = UniAuthService::instance()->GetQuickLoginUser(); ASSERT_EQ(hiddenSwitchButton.isEmpty(), false); } } TEST_F(UniAuthServiceTest, getUseFirstDevice) { if (UniAuthService::instance()) { bool value = UniAuthService::instance()->getUseFirstDevice(); UniAuthService::instance()->SwitchToUser(getenv("USER")); UniAuthService::instance()->SwitchToGreeterUser(getenv("USER")); UniAuthService::instance()->getWillSwitchUser(); UniAuthService::instance()->SaveLastLoginUser(UniAuthService::instance()->GetLastLoginUser()); UniAuthService::instance()->SaveQuickLoginUser(UniAuthService::instance()->GetQuickLoginUser()); ASSERT_EQ(value, false); } } ukui-screensaver/tests/unit_test_uniauth_service/CMakeLists.txt0000664000175000017500000000321615172041106024167 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/uniauthservice.cpp ../../src/dbusifs/freedesktophelper.cpp unit_test_uniauth_service.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/dbusifs/uniauthservice.h ../../src/dbusifs/freedesktophelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_uniauth_service ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_uniauth_service Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_uniauth_service GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_uniauth_service/main.cpp0000664000175000017500000000156115172041035023061 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_systemupgrade_helper/0000775000175000017500000000000015172041106022463 5ustar fengfengukui-screensaver/tests/unit_test_systemupgrade_helper/unit_test_systemupgrade_helper.cpp0000664000175000017500000000335715172041035031531 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/systemupgradehelper.h" #include "stubext.h" using namespace stub_ext; class SystemUpgradeHelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pSystemUpgradeHelperDbus = new SystemUpgradeHelper(); } static void TearDownTestSuite() { delete m_pSystemUpgradeHelperDbus; m_pSystemUpgradeHelperDbus = nullptr; } static SystemUpgradeHelper *m_pSystemUpgradeHelperDbus; }; SystemUpgradeHelper *SystemUpgradeHelperTest::m_pSystemUpgradeHelperDbus = nullptr; TEST_F(SystemUpgradeHelperTest, checkSystemUpgrade) { bool value = m_pSystemUpgradeHelperDbus->checkSystemUpgrade(); ASSERT_EQ(value, false); } TEST_F(SystemUpgradeHelperTest, doUpgradeThenRboot) { bool value = m_pSystemUpgradeHelperDbus->doUpgradeThenRboot(); ASSERT_EQ(value, false); } TEST_F(SystemUpgradeHelperTest, doUpgradeThenShutdown) { bool value = m_pSystemUpgradeHelperDbus->doUpgradeThenShutdown(); ASSERT_EQ(value, false); } ukui-screensaver/tests/unit_test_systemupgrade_helper/CMakeLists.txt0000664000175000017500000000306315172041106025225 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/systemupgradehelper.cpp unit_test_systemupgrade_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/dbusifs/systemupgradehelper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_systemupgrade_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_systemupgrade_helper Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_systemupgrade_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_systemupgrade_helper/main.cpp0000664000175000017500000000156215172041035024120 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_screenlock/0000775000175000017500000000000015172041106020360 5ustar fengfengukui-screensaver/tests/unit_test_screenlock/unit_test_screenlock.cpp0000664000175000017500000000710515172041035025316 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/ukccplugins/sessiondbus/screenlockinterface.h" #include "../../src/lock-backend/lightdmhelper.h" #include #include "stubext.h" using namespace stub_ext; class ScreenlockInterfaceTest : public testing::Test { protected: static void SetUpTestSuite() { StubExt st; st.set_lamda(&LightDMHelper::connectToDaemonSync, []() { return true; }); st.set_lamda(&isGreeterMode, []() { return true; }); m_pDbusUpperInterfaceDbus = new DbusUpperInterface(); m_pDbusUpperInterfaceDbus->init(); m_pScreenlockInterface = new ScreenlockInterface(m_pDbusUpperInterfaceDbus, m_pDbusUpperInterfaceDbus); } static void TearDownTestSuite() { if (m_pScreenlockInterface) { delete m_pScreenlockInterface; m_pScreenlockInterface = nullptr; } delete m_pDbusUpperInterfaceDbus; m_pDbusUpperInterfaceDbus = nullptr; } static ScreenlockInterface *m_pScreenlockInterface; static DbusUpperInterface *m_pDbusUpperInterfaceDbus; }; DbusUpperInterface *ScreenlockInterfaceTest::m_pDbusUpperInterfaceDbus = nullptr; ScreenlockInterface *ScreenlockInterfaceTest::m_pScreenlockInterface = nullptr; TEST_F(ScreenlockInterfaceTest, setShowOnLogin) { bool oldValue = m_pScreenlockInterface->property("showOnLogin").toBool(); m_pScreenlockInterface->setShowOnLogin(false); bool newValue = m_pScreenlockInterface->property("showOnLogin").toBool(); ASSERT_EQ(newValue, false); m_pScreenlockInterface->setShowOnLogin(oldValue); } TEST_F(ScreenlockInterfaceTest, setWallpaper) { QGSettings gset("org.ukui.screensaver"); std::string oldValue = gset.get("background").toString().toStdString(); m_pScreenlockInterface->setWallpaper("/usr/share/backgrounds/city.jpg"); std::string newValue = gset.get("background").toString().toStdString(); ASSERT_STREQ(newValue.c_str(), "/usr/share/backgrounds/city.jpg"); m_pScreenlockInterface->setWallpaper(oldValue.c_str()); } TEST_F(ScreenlockInterfaceTest, resetDefault) { QGSettings gset("org.ukui.screensaver"); std::string oldValue = gset.get("background").toString().toStdString(); m_pScreenlockInterface->resetDefault(); std::string newValue = gset.get("background").toString().toStdString(); ASSERT_STREQ(newValue.c_str(), "/usr/share/backgrounds/1-openkylin.jpg"); m_pScreenlockInterface->setWallpaper(oldValue.c_str()); } TEST_F(ScreenlockInterfaceTest, getPreviewWallpapers) { int size = m_pScreenlockInterface->getPreviewWallpapers().size(); ASSERT_GE(size, 0); } TEST_F(ScreenlockInterfaceTest, getSourceWallpapers) { int size = m_pScreenlockInterface->getSourceWallpapers().size(); ASSERT_GE(size, 0); } TEST_F(ScreenlockInterfaceTest, getWallpaper) { int size = m_pScreenlockInterface->getWallpaper().size(); ASSERT_GE(size, 0); } ukui-screensaver/tests/unit_test_screenlock/CMakeLists.txt0000664000175000017500000001224415172041106023123 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core DBus Network Test REQUIRED) find_package(OpenSSL REQUIRED) find_package(PkgConfig REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) pkg_check_modules(QGS REQUIRED gsettings-qt) pkg_check_modules(LIGHTDM-QT5-3 REQUIRED liblightdm-qt5-3) pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd) # 查找pam动态库全路径并缓存到PAM_LIBRARIES变量 find_library(PAM_LIBRARIES pam) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/lock-backend/authpamthread.cpp ../../src/lock-backend/pamauthenticate.cpp ../../src/lock-backend/lightdmhelper.cpp ../../src/lock-backend/dbusupperinterface.cpp ../../src/lock-backend/gsettingshelper.cpp ../../src/lock-backend/bioauthenticate.cpp ../../src/lock-backend/switchuserutils.cpp ../../src/dbusifs/accountshelper.cpp ../../src/userinfo.cpp ../../src/common/global_utils.cpp ../../src/common/configuration.cpp ../../src/common/kyrsac.cpp ../../src/dbusifs/giodbus.cpp ../../src/dbusifs/uniauthservice.cpp ../../src/lock-backend/securityuser.cpp ../../src/dbusifs/freedesktophelper.cpp ../../src/dbusifs/login1helper.cpp ../../src/dbusifs/usdhelper.cpp ../../src/dbusifs/upowerhelper.cpp ../../src/dbusifs/sessionhelper.cpp ../../src/dbusifs/dbusservermanager.cpp ../../src/dbusifs/systemupgradehelper.cpp ../../src/lock-backend/sessionwatcher.cpp ../../src/dbusifs/kglobalaccelhelper.cpp ../../src/dbusifs/libinputswitchevent.cpp ../../src/lock-backend/personalizeddata.cpp ../../src/dbusifs/biometrichelper.cpp ../../src/dbusifs/device.cpp ../../src/dbusifs/machinemodel.cpp ../../src/dbusifs/enginedevice.cpp ../../src/common/biodefines.cpp ../../src/QtSingleApplication/qtlocalpeer.cpp ../kt-test-utils/cpp-stub-ext/stub-shadow.cpp ../../src/ukccplugins/sessiondbus/screenlockinterface.cpp unit_test_screenlock.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/lock-backend/authpamthread.h ../../src/lock-backend/pamauthenticate.h ../../src/lock-backend/lightdmhelper.h ../../src/lock-backend/dbusupperinterface.h ../../src/lock-backend/gsettingshelper.h ../../src/lock-backend/bioauthenticate.h ../../src/lock-backend/switchuserutils.h ../../src/common/definetypes.h ../../src/userinfo.h ../../src/common/global_utils.h ../../src/common/configuration.h ../../src/common/kyrsac.h ../../src/dbusifs/giodbus.h ../../src/dbusifs/accountshelper.h ../../src/common/configuration.h ../../src/dbusifs/uniauthservice.h ../../src/lock-backend/securityuser.h ../../src/dbusifs/freedesktophelper.h ../../src/dbusifs/login1helper.h ../../src/dbusifs/usdhelper.h ../../src/dbusifs/upowerhelper.h ../../src/dbusifs/dbusservermanager.h ../../src/dbusifs/sessionhelper.h ../../src/dbusifs/systemupgradehelper.h ../../src/lock-backend/sessionwatcher.h ../../src/dbusifs/kglobalaccelhelper.h ../../src/dbusifs/libinputswitchevent.h ../../src/lock-backend/personalizeddata.h ../../src/dbusifs/biometrichelper.h ../../src/dbusifs/device.h ../../src/dbusifs/enginedevice.h ../../src/dbusifs/machinemodel.h ../../src/common/biodefines.h ../kt-test-utils/cpp-stub-ext/stubext.h ../kt-test-utils/cpp-stub-ext/stub-shadow.h ../../src/QtSingleApplication/qtlocalpeer.h ../../src/ukccplugins/sessiondbus/screenlockinterface.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ${Qt5Test_INCLUDE_DIRS} ${LIGHTDM-QT5-3_INCLUDE_DIRS} ${LIBSYSTEMD_INCLUDE_DIRS} ${GIOUNIX2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}../../src/QtSingleApplication/ ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_screenlock ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_screenlock Qt5::Core Qt5::DBus Qt5::Test Qt5::Network ${QGS_LIBRARIES} ${PAM_LIBRARIES} ${LIGHTDM-QT5-3_LIBRARIES} ${LIBSYSTEMD_LIBRARIES} OpenSSL::Crypto -lukuiinputgatherclient ${GIOUNIX2_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_screenlock GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_screenlock/main.cpp0000664000175000017500000000146215172041035022014 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_login1_helper/0000775000175000017500000000000015172041106020760 5ustar fengfengukui-screensaver/tests/unit_test_login1_helper/unit_test_login1_helper.cpp0000664000175000017500000000600015172041035026307 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/login1helper.h" #include "stubext.h" using namespace stub_ext; class Login1HelperTest : public testing::Test { protected: static void SetUpTestSuite() { m_pLogin1HelperDbus = new Login1Helper(); } static void TearDownTestSuite() { delete m_pLogin1HelperDbus; m_pLogin1HelperDbus = nullptr; } static Login1Helper *m_pLogin1HelperDbus; }; Login1Helper *Login1HelperTest::m_pLogin1HelperDbus = nullptr; TEST_F(Login1HelperTest, isSessionActive) { bool value = m_pLogin1HelperDbus->isSessionActive(); ASSERT_EQ(value, true); } TEST_F(Login1HelperTest, onPrepareForSleep) { bool oldSleep = m_pLogin1HelperDbus->m_isSleeping; m_pLogin1HelperDbus->onPrepareForSleep(true); m_pLogin1HelperDbus->onHibitedWatcherMessage(); m_pLogin1HelperDbus->onPrepareForSleep(oldSleep); ASSERT_EQ(m_pLogin1HelperDbus->m_isSleeping, true); } TEST_F(Login1HelperTest, setPowerManager) { m_pLogin1HelperDbus->setPowerManager("test"); m_pLogin1HelperDbus->Unlock(); } TEST_F(Login1HelperTest, getCanPowerManager) { bool value = m_pLogin1HelperDbus->getCanPowerManager("CanReboot"); ASSERT_EQ(value, true); } TEST_F(Login1HelperTest, isCanHibernate) { bool value = m_pLogin1HelperDbus->isCanHibernate(); ASSERT_EQ(value, true); } TEST_F(Login1HelperTest, isCanSuspend) { bool value = m_pLogin1HelperDbus->isCanSuspend(); ASSERT_EQ(value, true); } TEST_F(Login1HelperTest, isCanReboot) { bool value = m_pLogin1HelperDbus->isCanReboot(); ASSERT_EQ(value, true); } TEST_F(Login1HelperTest, isCanPowerOff) { bool value = m_pLogin1HelperDbus->isCanPowerOff(); ASSERT_EQ(value, true); } TEST_F(Login1HelperTest, canAction) { bool value = m_pLogin1HelperDbus->canAction(PowerHibernate); bool value1 = m_pLogin1HelperDbus->canAction(PowerSuspend); bool value2 = m_pLogin1HelperDbus->canAction(PowerMonitorOff); bool value3 = m_pLogin1HelperDbus->canAction(PowerLogout); bool value4 = m_pLogin1HelperDbus->canAction(PowerReboot); bool value5 = m_pLogin1HelperDbus->canAction(PowerShutdown); ASSERT_EQ(value, true); ASSERT_EQ(value1, true); ASSERT_EQ(value2, true); ASSERT_EQ(value3, true); ASSERT_EQ(value4, true); ASSERT_EQ(value5, true); } ukui-screensaver/tests/unit_test_login1_helper/CMakeLists.txt0000664000175000017500000000315715172041106023526 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/login1helper.cpp unit_test_login1_helper.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/common/definetypes.h ../../src/dbusifs/login1helper.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_login1_helper ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_login1_helper Qt5::Core Qt5::Gui Qt5::DBus ) # 链接 GTest 库 target_link_libraries(unit_test_login1_helper GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_login1_helper/main.cpp0000664000175000017500000000156215172041035022415 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_engine_device/0000775000175000017500000000000015172041106021014 5ustar fengfengukui-screensaver/tests/unit_test_engine_device/CMakeLists.txt0000664000175000017500000000373715172041106023566 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus Test REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(QGS REQUIRED gsettings-qt) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/enginedevice.cpp ../../src/dbusifs/device.cpp ../../src/dbusifs/upowerhelper.cpp ../../src/dbusifs/freedesktophelper.cpp unit_test_engine_device.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/dbusifs/enginedevice.h ../../src/dbusifs/upowerhelper.h ../../src/dbusifs/freedesktophelper.h ../../src/dbusifs/device.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ${Qt5Test_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_engine_device ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_engine_device Qt5::Core Qt5::Gui Qt5::DBus Qt5::Test ${QGS_LIBRARIES} ) # 链接 GTest 库 target_link_libraries(unit_test_engine_device GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_engine_device/main.cpp0000664000175000017500000000156215172041035022451 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/tests/unit_test_engine_device/unit_test_engine_device.cpp0000664000175000017500000003606515172041035026415 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "../../src/dbusifs/enginedevice.h" #include "../../src/dbusifs/device.h" #include "../../src/dbusifs/upowerhelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stubext.h" using namespace stub_ext; class EngineDeviceTest : public testing::Test { protected: static void SetUpTestSuite() { m_pEngineDeviceDbus = new EngineDevice(); } static void TearDownTestSuite() { delete m_pEngineDeviceDbus; m_pEngineDeviceDbus = nullptr; } static EngineDevice *m_pEngineDeviceDbus; }; EngineDevice *EngineDeviceTest::m_pEngineDeviceDbus = nullptr; // 测试 power_device_get_devices 函数 TEST_F(EngineDeviceTest, power_device_get_devices) { // 首先,确保 QDBus 服务正常运行,这里假设 QDBus 服务可以正常返回一些测试数据 m_pEngineDeviceDbus->power_device_get_devices(); // 检查 devices 列表是否不为空 EXPECT_GT(m_pEngineDeviceDbus->devices.size(), 0); // 可以进一步检查 devices 列表中元素的属性,例如: if (!m_pEngineDeviceDbus->devices.isEmpty()) { DEVICE *device = m_pEngineDeviceDbus->devices.first(); EXPECT_FALSE(device->m_dev.path.isEmpty()); } } // 测试 getProperty 函数 TEST_F(EngineDeviceTest, getProperty) { DEV dev; QString testPath = "/test/path"; // 这里假设 QDBus 服务会正常响应 m_pEngineDeviceDbus->getProperty(testPath, dev); // 检查返回的属性是否符合预期 EXPECT_EQ(dev.kind, UP_DEVICE_KIND_UNKNOWN); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.Type, ""); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.Model, ""); // 根据实际情况修改期望的结果 EXPECT_FALSE(dev.IsPresent); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.PowerSupply, ""); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.Percentage, 0.0); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.Online, ""); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.State, UP_DEVICE_STATE_UNKNOWN); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.TimeToEmpty, 0); // 根据实际情况修改期望的结果 EXPECT_EQ(dev.TimeToFull, 0); // 根据实际情况修改期望的结果 } // 测试 boolToString 函数 TEST_F(EngineDeviceTest, boolToString) { EXPECT_EQ(m_pEngineDeviceDbus->boolToString(true), "yes"); EXPECT_EQ(m_pEngineDeviceDbus->boolToString(false), "no"); } // 测试 putAttributes 函数 TEST_F(EngineDeviceTest, putAttributes) { QMap map; map["TimeToFull"] = QVariant(200); map["TimeToEmpty"] = QVariant(100); map["State"] = QVariant(2); map["Percentage"] = QVariant(50.0); map["PowerSupply"] = QVariant(true); map["IsPresent"] = QVariant(true); DEV btrDetailData; m_pEngineDeviceDbus->putAttributes(map, btrDetailData); EXPECT_EQ(btrDetailData.TimeToFull, 200); EXPECT_EQ(btrDetailData.TimeToEmpty, 100); EXPECT_EQ(btrDetailData.State, 2); EXPECT_EQ(btrDetailData.Percentage, 5.0); EXPECT_EQ(btrDetailData.PowerSupply, "Yes"); EXPECT_EQ(btrDetailData.IsPresent, true); } // 测试 power_device_change_callback 函数 TEST_F(EngineDeviceTest, power_device_change_callback) { // DEVICE *device = new DEVICE; // device->m_dev.path = "/test/path"; // engineDevice->devices.append(device); // QDBusMessage msg = QDBusMessage::createMethodCall(DBUS_SERVICE, "/test/path", DBUS_INTERFACE_PRO, "PropertiesChanged"); // msg.setArguments({ QVariant("interface"), QVariant::fromValue(QDBusArgument::fromVariantMap({ // { "TimeToFull", QVariant(300) } // })) }); // engineDevice->power_device_change_callback(msg, "/test/path"); // EXPECT_EQ(device->m_dev.TimeToFull, 300); // delete device; } // 测试 power_device_recalculate_state 函数 TEST_F(EngineDeviceTest, power_device_recalculate_state) { // 准备一些测试数据 DEVICE *device = new DEVICE; device->m_dev.IsPresent = true; device->m_dev.kind = UP_DEVICE_KIND_BATTERY; device->m_dev.State = UP_DEVICE_STATE_CHARGING; m_pEngineDeviceDbus->devices.append(device); // 使用 QSignalSpy 监听信号 // QSignalSpy summarySpy(m_pEngineDeviceDbus->get(), SIGNAL(engine_signal_summary_change(QString))); // QSignalSpy stateSpy(m_pEngineDeviceDbus->get(), SIGNAL(engine_signal_Battery_State(QStringList))); m_pEngineDeviceDbus->power_device_recalculate_state(); // 检查信号是否触发 // EXPECT_EQ(summarySpy.count(), 1); // EXPECT_EQ(stateSpy.count(), 1); delete device; } // 测试 engine_recalculate_summary 函数 TEST_F(EngineDeviceTest, engine_recalculate_summary) { // 准备一些测试数据 DEVICE *device = new DEVICE; device->m_dev.IsPresent = true; device->m_dev.kind = UP_DEVICE_KIND_BATTERY; device->m_dev.State = UP_DEVICE_STATE_CHARGING; m_pEngineDeviceDbus->devices.append(device); // 使用 QSignalSpy 监听信号 // QSignalSpy summarySpy(m_pEngineDeviceDbus->get(), SIGNAL(engine_signal_summary_change(QString))); // QSignalSpy stateSpy(m_pEngineDeviceDbus->get(), SIGNAL(engine_signal_Battery_State(QStringList))); bool result = m_pEngineDeviceDbus->engine_recalculate_summary(); // 检查函数返回值和信号触发情况 EXPECT_TRUE(result); // EXPECT_EQ(summarySpy.count(), 1); // EXPECT_EQ(stateSpy.count(), 1); delete device; } // 测试 engine_get_state 函数 TEST_F(EngineDeviceTest, engine_get_state) { // 准备一些测试数据 DEVICE *device = new DEVICE; device->m_dev.IsPresent = true; device->m_dev.kind = UP_DEVICE_KIND_BATTERY; device->m_dev.State = UP_DEVICE_STATE_CHARGING; m_pEngineDeviceDbus->devices.append(device); QStringList stateList = m_pEngineDeviceDbus->engine_get_state(); // 检查返回的状态列表是否符合预期 EXPECT_FALSE(stateList.isEmpty()); delete device; } // 测试 engine_get_Battery_State 函数 TEST_F(EngineDeviceTest, engine_get_Battery_State) { DEVICE *device = new DEVICE; device->m_dev.IsPresent = true; device->m_dev.kind = UP_DEVICE_KIND_BATTERY; device->m_dev.State = UP_DEVICE_STATE_CHARGING; device->m_dev.Percentage = 50.0; device->m_dev.TimeToEmpty = 100; QStringList stateList = m_pEngineDeviceDbus->engine_get_Battery_State(device); // 检查返回的状态列表元素是否符合预期 EXPECT_EQ(stateList.size(), 3); EXPECT_EQ(stateList[0], "50"); EXPECT_EQ(stateList[1], QString::number(UP_DEVICE_STATE_CHARGING)); EXPECT_EQ(stateList[2], "100"); delete device; } // 测试 engine_get_summary 函数 TEST_F(EngineDeviceTest, engine_get_summary) { // 准备一些测试数据 DEVICE *device = new DEVICE; device->m_dev.IsPresent = true; device->m_dev.kind = UP_DEVICE_KIND_BATTERY; device->m_dev.State = UP_DEVICE_STATE_CHARGING; device->m_dev.Percentage = 50.0; device->m_dev.TimeToEmpty = 100; m_pEngineDeviceDbus->devices.append(device); QString summary = m_pEngineDeviceDbus->engine_get_summary(); // 检查返回的摘要是否符合预期 EXPECT_FALSE(summary.isEmpty()); delete device; } // 测试 engine_get_device_summary 函数 TEST_F(EngineDeviceTest, engine_get_device_summary) { // 准备一些测试数据 DEVICE *device = new DEVICE; device->m_dev.IsPresent = true; device->m_dev.kind = UP_DEVICE_KIND_BATTERY; device->m_dev.State = UP_DEVICE_STATE_CHARGING; device->m_dev.Percentage = 50.0; device->m_dev.TimeToEmpty = 100; QString summary = m_pEngineDeviceDbus->engine_get_device_summary(device); // 检查返回的摘要是否符合预期 EXPECT_FALSE(summary.isEmpty()); delete device; } // 测试 engine_kind_to_localised_text 函数 TEST_F(EngineDeviceTest, engine_kind_to_localised_text) { EXPECT_EQ(m_pEngineDeviceDbus->engine_kind_to_localised_text(UP_DEVICE_KIND_LINE_POWER, 0), "AC adapter"); EXPECT_EQ(m_pEngineDeviceDbus->engine_kind_to_localised_text(UP_DEVICE_KIND_BATTERY, 0), "Laptop battery"); // 测试其他枚举值 } const static QString upowerService = QStringLiteral("org.freedesktop.UPower"); const static QString upowerPath = QStringLiteral("/org/freedesktop/UPower"); const static QString upowerInterface = QStringLiteral("org.freedesktop.UPower"); const static QString propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); const static QString upowerDisplayPath = QStringLiteral("/org/freedesktop/UPower/devices/DisplayDevice"); const static QString upowerDeviceInterface = QStringLiteral("org.freedesktop.UPower.Device"); // 测试 UpowerHelper 类 class UpowerHelperTest : public ::testing::Test { protected: std::unique_ptr upowerHelper; std::unique_ptr engineDevice; void SetUp() override { engineDevice = std::make_unique(); upowerHelper = std::make_unique(nullptr); upowerHelper->m_engineDevice = engineDevice.get(); } void TearDown() override { upowerHelper.reset(); engineDevice.reset(); } }; // 测试 UpowerHelper 构造函数中的 DBus 连接和属性初始化 TEST_F(UpowerHelperTest, UpowerHelperConstructor) { // 检查 m_upowerService 是否有效 EXPECT_TRUE(upowerHelper->m_upowerService->isValid()); // 检查 m_upowerInterface 是否有效 EXPECT_TRUE(upowerHelper->m_upowerInterface->isValid()); // 检查是否正确连接了信号 // QDBusConnectionInterface *connectionInterface = QDBusConnection::systemBus().interface(); // bool isConnected = connectionInterface->isConnected(upowerService, upowerPath, propertiesInterface, "PropertiesChanged", upowerHelper.get()); // EXPECT_TRUE(isConnected); // 检查 m_isBattery 的初始化 QDBusReply interfaceReply = upowerHelper->m_upowerInterface->call("Get", "org.freedesktop.UPower", "LidIsPresent"); bool expectedIsBattery = interfaceReply.isValid()? interfaceReply.value().toBool() : false; EXPECT_EQ(upowerHelper->m_isBattery, expectedIsBattery); // 检查是否正确连接了电池设备的信号 // if (!upowerHelper->m_batInterface.isNull()) { // isConnected = connectionInterface->isConnected(upowerService, upowerHelper->m_batInterface->path(), propertiesInterface, "PropertiesChanged", upowerHelper.get()); // EXPECT_TRUE(isConnected); // } // 检查是否正确连接了 EngineDevice 的信号 bool isSignalConnected = QObject::connect(engineDevice.get(), &EngineDevice::engine_signal_Battery_State, upowerHelper.get(), &UpowerHelper::onBatteryChanged); EXPECT_TRUE(isSignalConnected); } // 测试 getBatteryIconName 函数 TEST_F(UpowerHelperTest, GetBatteryIconName) { // 模拟电池状态和百分比 upowerHelper->m_upowerInterface->setProperty("OnBattery", QVariant(true)); if (upowerHelper->m_batInterface) { upowerHelper->m_batInterface->setProperty("Percentage", QVariant(50.0)); } QString iconName = upowerHelper->getBatteryIconName(); EXPECT_EQ(iconName, "battery-level-50-symbolic"); upowerHelper->m_upowerInterface->setProperty("OnBattery", QVariant(false)); iconName = upowerHelper->getBatteryIconName(); EXPECT_EQ(iconName, "battery-level-50-charging-symbolic"); // 测试异常情况,如百分比为负数 if (upowerHelper->m_batInterface) { upowerHelper->m_batInterface->setProperty("Percentage", QVariant(-10.0)); } iconName = upowerHelper->getBatteryIconName(); EXPECT_EQ(iconName, "battery-level--10-symbolic"); // 测试无效的 DBus 接口 // upowerHelper->m_upowerService->setValid(false); iconName = upowerHelper->getBatteryIconName(); EXPECT_EQ(iconName, QString()); } // 测试 getBatteryArgs 函数 TEST_F(UpowerHelperTest, GetBatteryArgs) { // 模拟 engineDevice 的状态 DEVICE device; device.m_dev.IsPresent = true; device.m_dev.kind = UP_DEVICE_KIND_BATTERY; device.m_dev.State = UP_DEVICE_STATE_CHARGING; engineDevice->devices.append(&device); QStringList args = upowerHelper->getBatteryArgs(); // 检查是否调用了 engine_get_state 并得到了正确的结果 EXPECT_FALSE(args.isEmpty()); // 测试 engineDevice 为空的情况 upowerHelper->m_engineDevice = nullptr; args = upowerHelper->getBatteryArgs(); EXPECT_TRUE(args.isEmpty()); } // 测试 onBatteryChanged 函数 TEST_F(UpowerHelperTest, OnBatteryChanged) { QStringList args = {"arg1", "arg2"}; // 使用 QSignalSpy 监听 batteryChanged 信号 QSignalSpy spy(upowerHelper.get(), SIGNAL(batteryChanged(QStringList))); upowerHelper->onBatteryChanged(args); // 检查信号是否正确触发 EXPECT_EQ(spy.count(), 1); EXPECT_EQ(spy[0][0].toStringList(), args); } // 测试 dealMessage 函数 TEST_F(UpowerHelperTest, DealMessage) { // 使用 QSignalSpy 监听 batteryStatusChanged 信号 QSignalSpy spy(upowerHelper.get(), SIGNAL(batteryStatusChanged(QString))); QDBusMessage message; upowerHelper->dealMessage(message); // // 检查信号是否正确触发 EXPECT_EQ(spy.count(), 1); // 测试信号传递的参数 upowerHelper->m_upowerService->setProperty("OnBattery", QVariant(true)); if (upowerHelper->m_batInterface) { upowerHelper->m_batInterface->setProperty("Percentage", QVariant(30.0)); } upowerHelper->dealMessage(message); EXPECT_EQ(spy[1][0].toString(), "battery-level-30-symbolic"); } // 测试 onLidWatcherMessage 函数 TEST_F(UpowerHelperTest, OnLidWatcherMessage) { // 使用 QSignalSpy 监听 lidStateChanged 信号 QSignalSpy spy(upowerHelper.get(), SIGNAL(lidStateChanged(bool))); QDBusMessage message; upowerHelper->onLidWatcherMessage(); // 检查信号是否正确触发 EXPECT_EQ(spy.count(), 1); // 测试信号传递的参数 QDBusInterface iface(upowerService, upowerPath, propertiesInterface, QDBusConnection::systemBus()); iface.setProperty("LidIsClosed", QVariant(true)); upowerHelper->onLidWatcherMessage(); EXPECT_EQ(spy[1][0].toBool(), true); iface.setProperty("LidIsClosed", QVariant(false)); upowerHelper->onLidWatcherMessage(); EXPECT_EQ(spy[2][0].toBool(), false); } ukui-screensaver/tests/unit_test_inputswitch_event/0000775000175000017500000000000015172041106022012 5ustar fengfengukui-screensaver/tests/unit_test_inputswitch_event/unit_test_inputswitch_event.cpp0000664000175000017500000001046615172041035030406 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "../../src/dbusifs/libinputswitchevent.h" #include "../../src/dbusifs/machinemodel.h" // 辅助函数,模拟文件读取内容 QString simulateFileRead(const QString &filePath) { if (filePath == "/sys/class/dmi/id/sys_vendor") { return "Vendor"; } else if (filePath == "/sys/class/dmi/id/product_name") { return "Product"; } else if (filePath == "/sys/class/dmi/id/product_family") { return "Family"; } return ""; } // 测试 MachineModel 类 class MachineModelTest : public ::testing::Test { protected: std::shared_ptr machineModel; void SetUp() override { machineModel = MachineModel::getMachineModelInstance(); } void TearDown() override { machineModel.reset(); } }; // 测试 getSysVendor 函数 TEST_F(MachineModelTest, getSysVendor) { QString vendor = machineModel->getSysVendor(); EXPECT_EQ(vendor, "Vendor"); } // 测试 getProductName 函数 TEST_F(MachineModelTest, getProductName) { QString productName = machineModel->getProductName(); EXPECT_EQ(productName, "Product"); } // 测试 getProductFamily 函数 TEST_F(MachineModelTest, getProductFamily) { QString productFamily = machineModel->getProductFamily(); EXPECT_EQ(productFamily, "Family"); } // 测试 initMachineType 函数 TEST_F(MachineModelTest, initMachineType) { machineModel->initMachineType(); QString machineType = machineModel->getTheMachineType(); EXPECT_EQ(machineType, ""); // 这里根据实际的配置文件设置进行调整 } // 测试 LibinputSwitchEvent 类 class LibinputSwitchEventTest : public ::testing::Test { protected: std::unique_ptr libinputSwitchEvent; void SetUp() override { // 使用模拟的 QProcess qputenv("QT_QPA_PLATFORM", "minimal"); qRegisterMetaType("Event*"); libinputSwitchEvent = std::make_unique(nullptr); } void TearDown() override { libinputSwitchEvent.reset(); } }; // 测试 geInitDevicesStatus 函数 TEST_F(LibinputSwitchEventTest, geInitDevicesStatus) { // 测试不同的机器类型 libinputSwitchEvent->m_machineType = "SLATE"; EXPECT_TRUE(libinputSwitchEvent->geInitDevicesStatus()); libinputSwitchEvent->m_machineType = "LAPTOP"; // EXPECT_TRUE(libinputSwitchEvent->geInitDevicesStatus()); libinputSwitchEvent->m_machineType = "ALLINONE"; EXPECT_FALSE(libinputSwitchEvent->geInitDevicesStatus()); libinputSwitchEvent->m_machineType = "OTHER"; EXPECT_FALSE(libinputSwitchEvent->geInitDevicesStatus()); } // 测试 dealEvent 函数 TEST_F(LibinputSwitchEventTest, dealEvent) { Event event; event.type = LIBINPUT_EVENT_SWITCH_TOGGLE; event.event.switchEventDate.switchType = LIBINPUT_SWITCH_TABLET_MODE; // 测试切换到平板模式 // event.event.switchEventDate.switchState = LIBINPUT_SWITCH_STATE_ON; // QSignalSpy spy(libinputSwitchEvent.get(), SIGNAL(tabletModeStatusChanged(int))); // libinputSwitchEvent->dealEvent(&event); // EXPECT_EQ(spy.count(), 1); // EXPECT_EQ(spy[0][0].value(), 1); // // 测试切换出平板模式 // event.event.switchEventDate.switchState = LIBINPUT_SWITCH_STATE_OFF; // spy.clear(); // libinputSwitchEvent->dealEvent(&event); // EXPECT_EQ(spy.count(), 1); // EXPECT_EQ(spy[0][0].value(), 0); // 测试其他事件类型 // event.type = LIBINPUT_EVENT_NONE; // spy.clear(); // libinputSwitchEvent->dealEvent(&event); // EXPECT_EQ(spy.count(), 0); } ukui-screensaver/tests/unit_test_inputswitch_event/CMakeLists.txt0000664000175000017500000000351415172041106024555 0ustar fengfeng# CMake 最低版本要求 cmake_minimum_required(VERSION 3.10) find_package(Qt5 COMPONENTS Core Gui DBus REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(QGS REQUIRED gsettings-qt) # 包含 GTest 库和 pthread 库 find_package(GTest REQUIRED) find_package(Threads REQUIRED) # 设置 C++ 标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 开启代码覆盖率相关编译选项(对应QMAKE_LFLAGS和QMAKE_CXXFLAGS中代码覆盖率相关设置) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage --coverage -fno-inline -fno-access-control") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") # 定义源文件列表,对应原来的SOURCES变量 set(SOURCES ../../src/dbusifs/libinputswitchevent.cpp ../../src/dbusifs/machinemodel.cpp unit_test_inputswitch_event.cpp main.cpp ) # 定义头文件列表,对应原来的HEADERS变量 set(HEADERS ../../src/dbusifs/libinputswitchevent.h ../../src/dbusifs/machinemodel.h ) # 包含头文件的路径设置,对应原来的INCLUDEPATH变量 include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub ${CMAKE_CURRENT_SOURCE_DIR}/../kt-test-utils/cpp-stub-ext ${QGS_INCLUDE_DIRS} ) # 使用qt5_wrap_cpp生成元对象代码相关的源文件 qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) # 添加可执行文件或库目标,将元对象代码源文件一起添加进去 add_executable(unit_test_inputswitch_event ${SOURCES} ${MOC_SOURCES}) # 链接Qt相关的库 target_link_libraries(unit_test_inputswitch_event Qt5::Core Qt5::Gui Qt5::DBus ${QGS_LIBRARIES} -lukuiinputgatherclient ) # 链接 GTest 库 target_link_libraries(unit_test_inputswitch_event GTest::GTest GTest::Main Threads::Threads ) ukui-screensaver/tests/unit_test_inputswitch_event/main.cpp0000664000175000017500000000156215172041035023447 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 a(argc, argv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ukui-screensaver/man/0000775000175000017500000000000015172041106013543 5ustar fengfengukui-screensaver/man/ukui-screensaver-dialog.10000664000175000017500000000424215172041035020360 0ustar fengfeng.\" ukui-screensaver manual page .\" .\" This is free software; you may redistribute it and/or modify .\" it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2, .\" or (at your option) any later version. .\" .\" This is distributed in the hope that it will be useful, but .\" WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU 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. .\" .TH UKUI-SCREENSAVER 1 "03 May 2018" "UKUI Desktop Environment" .\" Please adjust this date whenever revising the manpage. .\" .SH "NAME" ukui-screensaver \- The UKUI Desktop Screensaver and Locker .SH "SYNOPSIS" .B ukui-screensaver [OPTIONS] .SH "DESCRIPTION" UKUI Screensaver is the default screen saver and locker for the UKUI Desktop. It is designed with simplicity and security in mind. \fBukui-screensaver\fP integrates well with the UKUI desktop, and it supports User Switching and the ability to lock down configuration settings. .SH "OPTIONS" .TP \fB\-\-display=DISPLAY\fR X display to use. .TP \fB\-\-no\-daemon\fR Don't start as a daemon .TP \fB\-\-debug\fR Enable debugging messages for \fBukui-screensaver\fP .TP \fB\-\-version\fR Output version information and exit. .TP \fB\-h, \-\-help\fR Print standard command line options. .TP \fB\-\-help\-all\fR Print all command line options. .P This program also accepts the standard GTK options. .SH "BUGS" .SS Should you encounter any bugs, they may be reported at: http://github.com/ukui-desktop/ukui-screensaver/issues .SH "AUTHORS" .SS ukui-screensaver is maintained by: .nf Hao Lee .fi .SS This Man Page has been updated for the UKUI Desktop Environment by: Hao Lee (2018) .SH "SEE ALSO" .SS Further information may also be available at: https://www.ukui.org/doc/index-cn.html .P .BR ukui-screensaver-command (1), .BR ukui-screensaver-preferences (1), .BR gtk-options (7) ukui-screensaver/man/start-greeter-session.10000664000175000017500000000343015172041106020076 0ustar fengfeng.\" ukui-screensaver manual page .\" .\" This is free software; you may redistribute it and/or modify .\" it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2, .\" or (at your option) any later version. .\" .\" This is distributed in the hope that it will be useful, but .\" WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU 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. .\" .TH UKUI-SCREENSAVER 1 "03 May 2018" "UKUI Desktop Environment" .\" Please adjust this date whenever revising the manpage. .\" .SH "NAME" start-greeter-session \- Start the UKUI Login Manager session .SH "SYNOPSIS" .B start-greeter-session .SH "DESCRIPTION" \fBstart-greeter-session\fR starts the UKUI login manager (greeter) session. It is typically invoked automatically by a display manager (such as lightdm or gdm). .SH "OPTIONS" .TP \fB\-\-version\fR Output version information and exit. .TP \fB\-h, \-\-help\fR Print standard command line options. .P This program also accepts the standard GTK options. .SH "BUGS" .SS Should you encounter any bugs, they may be reported at: http://github.com/ukui-desktop/ukui-screensaver/issues .SH "AUTHORS" .SS ukui-screensaver is maintained by: .nf Hao Lee .fi .SS This Man Page has been updated/re-written for the UKUI Desktop Environment by: Hao Lee (2018) .SH "SEE ALSO" .SS Further information may also be available at: https://www.ukui.org/doc/index-cn.html .P .BR start-greeter-session (1), ukui-screensaver/man/ukui-screensaver-command.10000664000175000017500000000416415172041035020542 0ustar fengfeng.\" ukui-screensaver manual page .\" .\" This is free software; you may redistribute it and/or modify .\" it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2, .\" or (at your option) any later version. .\" .\" This is distributed in the hope that it will be useful, but .\" WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU 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. .\" .TH UKUI-SCREENSAVER 1 "03 May 2018" "UKUI Desktop Environment" .\" Please adjust this date whenever revising the manpage. .\" .SH "NAME" ukui-screensaver \- The UKUI Desktop Screensaver and Locker .SH "SYNOPSIS" .B ukui-screensaver [OPTIONS] .SH "DESCRIPTION" UKUI Screensaver is the default screen saver and locker for the UKUI Desktop. It is designed with simplicity and security in mind. \fBukui-screensaver\fP integrates well with the UKUI desktop, and it supports User Switching and the ability to lock down configuration settings. .SH "OPTIONS" .TP \fB\-\-display=DISPLAY\fR X display to use. .TP \fB\-\-no\-daemon\fR Don't start as a daemon .TP \fB \-\-debug\fR Enable debugging messages for \fBukui-screensaver\fP .TP \fB\-\-version\fR Output version information and exit. .TP \fB\-h, \-\-help\fR Print standard command line options. .TP \fB\-\-help\-all\fR Print all command line options. .P This program also accepts the standard GTK options. .SH "BUGS" .SS Should you encounter any bugs, they may be reported at: http://github.com/ukui-desktop/ukui-screensaver/issues .SH "AUTHORS" .SS ukui-screensaver is maintained by: .nf Hao Lee .fi .SS This Man Page has been updated/re-written for the UKUI Desktop Environment by: Hao Lee (2018) .SH "SEE ALSO" .SS Further information may also be available at: https://www.ukui.org/doc/index-cn.html .P .BR ukui-screensaver-command (1), ukui-screensaver/man/ukui-screensaver-backend.10000664000175000017500000000424215172041035020510 0ustar fengfeng.\" ukui-screensaver manual page .\" .\" This is free software; you may redistribute it and/or modify .\" it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2, .\" or (at your option) any later version. .\" .\" This is distributed in the hope that it will be useful, but .\" WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU 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. .\" .TH UKUI-SCREENSAVER 1 "03 May 2018" "UKUI Desktop Environment" .\" Please adjust this date whenever revising the manpage. .\" .SH "NAME" ukui-screensaver \- The UKUI Desktop Screensaver and Locker .SH "SYNOPSIS" .B ukui-screensaver [OPTIONS] .SH "DESCRIPTION" UKUI Screensaver is the default screen saver and locker for the UKUI Desktop. It is designed with simplicity and security in mind. \fBukui-screensaver\fP integrates well with the UKUI desktop, and it supports User Switching and the ability to lock down configuration settings. .SH "OPTIONS" .TP \fB\-\-display=DISPLAY\fR X display to use. .TP \fB\-\-no\-daemon\fR Don't start as a daemon .TP \fB\-\-debug\fR Enable debugging messages for \fBukui-screensaver\fP .TP \fB\-\-version\fR Output version information and exit. .TP \fB\-h, \-\-help\fR Print standard command line options. .TP \fB\-\-help\-all\fR Print all command line options. .P This program also accepts the standard GTK options. .SH "BUGS" .SS Should you encounter any bugs, they may be reported at: http://github.com/ukui-desktop/ukui-screensaver/issues .SH "AUTHORS" .SS ukui-screensaver is maintained by: .nf Hao Lee .fi .SS This Man Page has been updated for the UKUI Desktop Environment by: Hao Lee (2018) .SH "SEE ALSO" .SS Further information may also be available at: https://www.ukui.org/doc/index-cn.html .P .BR ukui-screensaver-command (1), .BR ukui-screensaver-preferences (1), .BR gtk-options (7) ukui-screensaver/i18n_ts/0000775000175000017500000000000015172041106014255 5ustar fengfengukui-screensaver/i18n_ts/ar.ts0000664000175000017500000045130115172041106015233 0ustar fengfeng AgreementWindow I know أعرف AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry اعاده المحاوله UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. يرجى المحاولة مرة أخرى في ٪1 دقيقة. Please try again in %1 seconds. يرجى المحاولة مرة أخرى في ٪1 ثانية. Account locked permanently. الحساب مغلق بشكل دائم. Verify face recognition or enter password to unlock تحقق من التعرف على الوجه أو أدخل كلمة المرور لفتح القفل Guest ضيف Press fingerprint or enter password to unlock اضغط على بصمة الإصبع أو أدخل كلمة المرور لفتح القفل Verify voiceprint or enter password to unlock تحقق من البصمة الصوتية أو أدخل كلمة المرور لفتح القفل Verify finger vein or enter password to unlock تحقق من وريد الإصبع أو أدخل كلمة المرور لفتح القفل Verify iris or enter password to unlock تحقق من قزحية العين أو أدخل كلمة المرور لفتح القفل Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock The authentication has been cancelled! Password: شعار: Input Password إدخال كلمة المرور Username اسم المستخدم User name input error! خطأ في إدخال اسم المستخدم! login تسجيل الدخول Failed to verify %1, please enter password to unlock فشل التحقق من ٪1 ، الرجاء إدخال كلمة المرور لإلغاء القفل Unable to verify %1, please enter password to unlock تعذر التحقق من ٪1 ، يرجى إدخال كلمة المرور لإلغاء القفل Abnormal network شبكة غير طبيعية Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. انتهى وقت انتظار التعرف على الوجوه ، يرجى النقر فوق تحديث أو إدخال كلمة المرور لإلغاء القفل. FingerPrint بصمة الإصبع FingerVein الوريد العمد Iris زنبق Face وجه VoicePrint الطباعة الصوتية Ukey أوكي QRCode QRCode fingerprint بصمة الإصبع fingervein الوريد iris زنبق face وجه voiceprint بصمة صوتية Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty لا يمكن أن تكون كلمة المرور فارغة Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities تعذر إثبات ملكية ٪1، لا يزال لديك ٪2 فرص إثبات ملكية Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again فشل المصادقة، يرجى المحاولة مرة أخرى Use the bound wechat scanning code or enter the password to unlock استخدم رمز مسح wechat المرتبط أو أدخل كلمة المرور لإلغاء القفل Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port 请将安全密钥插入USB端口 Password 密码 Login تسجيل الدخول Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... شحن... fully charged مشحونة بالكامل PowerMode وضع الطاقة BatteryMode وضع البطارية BindDialog Close غلق Ok 确定 Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel إلغاء الأمر Confirm كونفريم If you do not perform any operation, the system will automatically %1 after %2 seconds. إذا لم تقم بأي عملية ، فسيقوم النظام تلقائيا ٪1 بعد ٪2 ثانية. The following programs prevent restarting, you can click "Cancel" and then close these programs. تمنع البرامج التالية إعادة التشغيل ، يمكنك النقر فوق "إلغاء" ثم إغلاق هذه البرامج. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. تمنع البرامج التالية إيقاف التشغيل ، يمكنك النقر فوق "إلغاء" ثم إغلاق هذه البرامج. The following programs prevent suspend, you can click "Cancel" and then close these programs. البرامج التالية تمنع التعليق ، يمكنك النقر فوق "إلغاء" ثم إغلاق هذه البرامج. The following programs prevent hibernation, you can click "Cancel" and then close these programs. تمنع البرامج التالية الإسبات ، يمكنك النقر فوق "إلغاء" ثم إغلاق هذه البرامج. The following programs prevent you from logging out, you can click "Cancel" and then close them. تمنعك البرامج التالية من تسجيل الخروج ، يمكنك النقر فوق "إلغاء" ثم إغلاقها. shut down إيقاف التشغيل restart اعاده تشغيل CharsMoreWidget &&?! &&?! CharsWidget More أكثر ABC ABC 123 123 ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 EngineDevice yes نعم no لا Yes نعم No لا %1% available, charged ٪1٪ متوفرة، مشحونة Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK 提交 InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state حالة معلقة Close غلق Welt status حالة Welt KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LettersWidget &&?! &&?! 123 123 LightDMHelper failed to start session. فشل في بدء الجلسة. Login تسجيل الدخول Guest ضيف LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser 切换用户 Power 电源 VirtualKeyboard 虚拟键盘 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession Power Information Network 网络 system-monitor مراقب النظام Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". ٪1 قد يتسبب في فقدان المستخدمين الذين قاموا بتسجيل الدخول إلى هذا الكمبيوتر للمحتوى الذي لم يتم تخزينه بعد ، للاستمرار في الأداء ، يرجى النقر فوق "تأكيد". Restart اعاده تشغيل Multiple users are logged in at the same time.Are you sure you want to %1 this system? يتم تسجيل دخول عدة مستخدمين في نفس الوقت. هل أنت متأكد من أنك تريد ٪1 هذا النظام؟ Reboot 重启 PowerOff 关机 Shut Down إيقاف التشغيل LoginOptionsWidget Login Options خيارات تسجيل الدخول Password شعار Other آخر FingerPrint بصمة الإصبع FingerVein الوريد العمد Iris زنبق Face وجه VoicePrint الطباعة الصوتية Ukey أوكي QRCode QRCode Wechat 微信 Identify device removed! تحديد الجهاز الذي تمت إزالته! MPRISSaverWidget No music detected Unknown مجهول MPRISWidget Unknown مجهول MusicFileInformation Unknown singer Unknown album MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN شبكة LAN WLAN شبكة WLAN NumbersWidget &&?! &&?! Return أعاد OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate السبات Turn off your computer, but the app stays open. When the computer is turned on, it can be restored to the state you left قم بإيقاف تشغيل الكمبيوتر، ولكن يظل التطبيق مفتوحا. عند تشغيل الكمبيوتر، يمكن استعادته إلى الحالة التي غادرتها Suspend تعليق The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off يظل الكمبيوتر قيد التشغيل ، لكنه يستهلك طاقة أقل. يظل التطبيق مفتوحا ويمكنه الاستيقاظ بسرعة والعودة إلى حيث توقفت Restart اعاده تشغيل Close all apps, and then restart your computer أغلق جميع التطبيقات، ثم أعد تشغيل الكمبيوتر Shut Down إيقاف التشغيل The current user logs out of the system, terminates the session, and returns to the login page يقوم المستخدم الحالي بتسجيل الخروج من النظام وإنهاء الجلسة والعودة إلى صفحة تسجيل الدخول Logout الخروج Power Off 关机 Close all apps, and then shut down your computer أغلق جميع التطبيقات، ثم قم بإيقاف تشغيل الكمبيوتر SwitchUser SwitchUser LockScreen قفل الشاشة Log Out تسجيل الخروج Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off UpgradeThenRestart الترقية من ثم إعادة التشغيل Close all apps, turn off your computer, and then turn your computer back on أغلق جميع التطبيقات، وقم بإيقاف تشغيل الكمبيوتر، ثم أعد تشغيل الكمبيوتر UpgradeThenShutdown UpgradeThenShutdown PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open. When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. شاشة التوقف نشطة. The screensaver is inactive. شاشة التوقف غير نشطة. FingerPrint بصمة الإصبع FingerVein الوريد العمد Iris زنبق Face وجه VoicePrint الطباعة الصوتية Ukey أوكي QRCode QRCode Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm 确认 Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock /Screenlock/Screenlock The date and time are displayed /Screenlock/The date and time are displayed Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Bluetooth No bluetooth adapter detected, can not use Leave Lock. Monitor Off Screensaver Related Settings Set Please select device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist الصورة غير موجودة Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View منظر Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Idle time /Screensaver/Idle time Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. Show rest time /Screensaver/Show rest time Select Screensaver Weather Music Albun The date and time are displayed /Screensaver/The date and time are displayed Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir SleepTime You have rested: 您已休息: You have rested لقد استراحت SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer UserListWidget Login تسجيل الدخول Guest ضيف Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account Input password 输入密码 Enter the verification code Get verification code Reacquire(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 Widget Widget LoadPlugin تحميل البرنامج المساعد action which block type أي نوع كتلة which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout أي نوع كتلة ، معلمة: تعليق / سبات / إعادة تشغيل / إيقاف تشغيل / تسجيل الخروج which block type,param:Shutdown/Restart أي نوع كتلة ، معلمة: إيقاف التشغيل / إعادة التشغيل delay how long to show lock كم من الوقت لإظهار القفل has-lock if show lock إذا عرض القفل main Start command for the ukui ScreenSaver. الأمر Start ل ukui ScreenSaver. lock the screen immediately قفل الشاشة على الفور query the status of the screen saver الاستعلام عن حالة شاشة التوقف unlock the screen saver افتح شاشة التوقف show the screensaver إظهار شاشة التوقف show blank and delay to lock,param:idle/lid/lowpower إظهار فارغة وتأخير للقفل ، param: الخمول / الغطاء / lowpower show the session tools إظهار أدوات الجلسة show the switchuser window إظهار نافذة SwitchUser show the app block window إظهار نافذة "مكون التطبيقات" show the multiUsers block window إظهار نافذة كتلة المستخدمين المتعددين Backend for the ukui ScreenSaver. lock the screen by startup قفل الشاشة عن طريق بدء التشغيل Dialog for the ukui ScreenSaver. مربع حوار ل ukui ScreenSaver. activated by session idle signal يتم تنشيطه بواسطة إشارة خمول الجلسة lock the screen and show screensaver immediately قفل الشاشة وإظهار شاشة التوقف على الفور show screensaver immediately إظهار شاشة التوقف على الفور show blank screensaver immediately and delay time to show lock إظهار شاشة التوقف الفارغة على الفور وتأخير الوقت لإظهار القفل show blank screensaver immediately and if lock إظهار شاشة التوقف الفارغة على الفور وإذا كان القفل show switch user window إظهار نافذة مستخدم التبديل switch to user and show window Screensaver for ukui-screensaver شاشة التوقف ل ukui-screensaver show on root window إظهار على نافذة الجذر show on window. تظهر على النافذة. window id معرف النافذة switchtouser switch to someone user ukui-screensaver/i18n_ts/tr.ts0000664000175000017500000025621315172041106015263 0ustar fengfeng AgreementWindow I know AuthDialog More Devices Diğer Cihazlar Biometric Biometrik Password Parola Retry Yeniden Dene UnLock Kilidi Aç Password: Parola Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts %2 başarısız denemeden dolayı hesap %1 dakika kilitlendi Password Incorrect, Please try again Parola yanlış, Lütfen tekrar deneyin Authentication failure,there are still %1 remaining opportunities Kimlik doğrulama hatası, hala %1 kalan denemen var Login Guest Misafir Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock Password cannot be empty The authentication has been cancelled! Password: Input Password Username User name input error! Authentication failure, Please try again login Please try again in %1 minutes. Please try again in %1 seconds. Account locked permanently. Failed to verify %1, please enter password to unlock Unable to verify %1, please enter password to unlock Failed to verify %1, you still have %2 verification opportunities Abnormal network Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. FingerPrint Parmak İzi FingerVein Iris Göz Face Yüz VoicePrint Ses İzi Ukey QRCode fingerprint fingervein iris face voiceprint Insert the ukey into the USB port Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Biometric Authentication Biyometrik Kimlik Doğrulama Password Authentication Parola Doğrulama Other Devices Diğer Ayıtlar BatteryWidget Charging... fully charged PowerMode BatteryMode No battery detected Pil algılanmadı BindDialog Close Ok Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint Parmak İzi FingerVein Damar İzi Iris Göz Face Yüz VoicePrint Ses İzi BioDevicesWidget Please select other biometric devices Lütfen diğer biyometrik cihazları seçin Device Type: Aygıt Türü: Device Name: Aygıt Adı: BiometricAuthWidget Current device: Şuanki aygıt: Identify failed, Please retry. Tanımlama başarısız, Lütfen tekrar deneyin. BiometricDevicesWidget Please select the biometric device Lütfen biyometrik aygıtı seçin Device type: Aygıt türü: Device name: Aygıt adı: OK Tamam BlockWidget Cancel Confirm If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. The following programs prevent suspend, you can click "Cancel" and then close these programs. The following programs prevent hibernation, you can click "Cancel" and then close these programs. The following programs prevent you from logging out, you can click "Cancel" and then close them. shut down restart CharsMoreWidget &&?! CharsWidget More ABC 123 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint Parmak İzi FingerVein Parmak Damarı Iris Göz Face Yüz VoicePrint Ses İzi DlgConnHidWifiLeap Password Parola DlgConnHidWifiSecFast Password Parola DlgConnHidWifiSecLeap Password Parola DlgConnHidWifiSecPeap Password Parola DlgConnHidWifiSecPwd Password Parola DlgConnHidWifiSecTunnelTLS Password Parola DlgConnHidWifiWpa Password Parola DlgHotspotCreate Password Parola EngineDevice yes no Yes No %1% available, charged Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK Tamam KBTitle Suspended state Close Welt status LettersWidget &&?! 123 LightDMHelper failed to start session. Login Guest Misafir LockWidget Date Tarih Time Zaman Guest Misafir SwitchUser Kullanıcı Değiştir Network VirtualKeyboard Power system-monitor Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Multiple users are logged in at the same time.Are you sure you want to %1 this system? Restart Yeniden Başlat SwitchSession Power Information Shut Down LoginOptionsWidget Login Options Password Parola Other Identify device removed! FingerPrint Parmak İzi FingerVein Iris Göz Face Yüz VoicePrint Ses İzi Ukey QRCode MPRISSaverWidget No music detected Unknown MusicFileInformation Unknown singer Unknown album MyNetworkWidget LAN WLAN NumbersWidget &&?! Return PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Suspend The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off Restart Yeniden Başlat Close all apps, and then restart your computer Shut Down Close all apps, and then shut down your computer Log Out Çıkış The current user logs out of the system, terminates the session, and returns to the login page SwitchUser Kullanıcı Değiştir LockScreen UpgradeThenRestart UpgradeThenShutdown Power Off Bilgisayarı Kapat Close all apps, turn off your computer, and then turn your computer back on PowerManager lock kilit SwitchUser Kullanıcı Değiştir logout Çıkış reboot yeniden başlat shutdown kapat Lock Screen Ekranı Kilitle Switch User Kullanıcı Değiştir Log Out Çıkış Restart Yeniden Başlat Power Off Bilgisayarı Kapat QObject FingerPrint Parmak İzi FingerVein Iris Göz Face Yüz VoicePrint Ses İzi Ukey QRCode The screensaver is active. The screensaver is inactive. Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver exit(Esc) çıkış(Esc) exit çıkış Set as desktop wallpaper Masaüstü arkaplanı olarak ayarla Automatic switching Otomatik değiştir Picture does not exist View Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested TalksWidget Enter your answer UserListWidget Login Guest Misafir VerifyKyidWidget Enter your Kylin ID account Input password Enter the verification code Get verification code Reacquire(%1) Widget Widget LoadPlugin action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock has-lock if show lock main Start command for the ukui ScreenSaver. Ukui Ekran Koruyucu için başlatma komutu. lock the screen immediately Ekranı hemen kilitle query the status of the screen saver unlock the screen saver show the screensaver show blank and delay to lock,param:idle/lid/lowpower show the session tools show the switchuser window show the app block window show the multiUsers block window Backend for the ukui ScreenSaver. lock the screen by startup Dialog for the ukui ScreenSaver. activated by session idle signal lock the screen and show screensaver immediately show screensaver immediately show blank screensaver immediately and delay time to show lock show blank screensaver immediately and if lock show switch user window switch to user and show window Screensaver for ukui-screensaver show on root window show on window. window id switchtouser switch to someone user ukui-screensaver/i18n_ts/fr.ts0000664000175000017500000045457415172041106015257 0ustar fengfeng AgreementWindow I know Je sais AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry Réessayer UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. Veuillez réessayer dans %1 minutes. Please try again in %1 seconds. Veuillez réessayer dans %1 secondes. Account locked permanently. Compte verrouillé définitivement. Verify face recognition or enter password to unlock Vérifier la reconnaissance faciale ou saisir le mot de passe pour déverrouiller Guest 游客 Press fingerprint or enter password to unlock Appuyez sur l’empreinte digitale ou entrez le mot de passe pour déverrouiller Verify voiceprint or enter password to unlock Vérifier l’empreinte vocale ou saisir le mot de passe pour déverrouiller Verify finger vein or enter password to unlock Vérifiez la veine du doigt ou entrez le mot de passe pour déverrouiller Verify iris or enter password to unlock Vérifiez l’iris ou entrez le mot de passe pour déverrouiller Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock The authentication has been cancelled! Password: Input Password Mot de passe d’entrée Username Nom d’utilisateur User name input error! login Failed to verify %1, please enter password to unlock Impossible de vérifier %1, veuillez entrer le mot de passe pour déverrouiller Unable to verify %1, please enter password to unlock Impossible de vérifier %1, veuillez entrer le mot de passe pour déverrouiller Abnormal network Réseau anormal Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey (en anglais seulement) QRCode QRCode fingerprint fingervein iris face voiceprint Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty Le mot de passe ne peut pas être vide Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities Échec de la vérification %1, vous avez encore %2 possibilités de vérification Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again Échec de l’authentification, veuillez réessayer Use the bound wechat scanning code or enter the password to unlock Utilisez le code d’analyse wechat lié ou entrez le mot de passe pour déverrouiller Enter the ukey password Entrez le mot de passe ukey Insert the ukey into the USB port Insérez l’ukey dans le port USB Password Mot de passe Login Connectez-vous Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... fully charged PowerMode BatteryMode BindDialog Close Ok D’accord Cancel Annuler Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: Appareil actuel : Identify failed, Please retry. Identifier l’échec, veuillez réessayer. BiometricDevicesWidget Please select the biometric device Veuillez sélectionner le dispositif biométrique Device type: Type d’appareil : Device name: Nom de l’appareil : OK D’ACCORD BlockWidget Cancel Annuler Confirm If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. The following programs prevent suspend, you can click "Cancel" and then close these programs. The following programs prevent hibernation, you can click "Cancel" and then close these programs. The following programs prevent you from logging out, you can click "Cancel" and then close them. shut down restart The following program is running to prevent the system from reboot! Le programme suivant est en cours d’exécution pour empêcher le système de redémarrer ! The following program is running to prevent the system from shutting down! Le programme suivant est en cours d’exécution pour empêcher le système de s’arrêter ! The following program is running to prevent the system from suspend! Le programme suivant est en cours d’exécution pour empêcher le système de se suspendre ! The following program is running to prevent the system from hibernate! Le programme suivant est en cours d’exécution pour empêcher le système de hiberner ! CharsMoreWidget &&?! &&?! CharsWidget More Plus ABC ABC 123 123 ConfForm edit network Modifier le réseau LAN name: Nom du réseau local : Method: Méthode: Address: Adresse: Netmask: Masque de réseau : Gateway: Passerelle: DNS 1: DNS 1 : DNS 2: DNS 2 : Edit Conn Modifier Conn Auto(DHCP) Auto (DHCP) Manual Manuelle Cancel Annuler Save Sauvegarder Ok D’accord Can not create new wired network for without wired card Impossible de créer un nouveau réseau câblé sans carte filaire New network already created Nouveau réseau déjà créé New network settings already finished Les nouveaux paramètres réseau sont déjà terminés Edit Network Modifier le réseau Add Wired Network Ajouter un réseau câblé create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective De nouveaux paramètres déjà en vigueur There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint Empreinte digitale FingerVein Veine du doigt Iris Iris Face Visage VoicePrint Voiceprint Ukey Ukey (en anglais seulement) QRCode QRCode DigitalAuthDialog LoginByUEdu LoginByUEdu now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? RéinitialiserPWD ? SetNewUEduPWD SetNewUEduPWD ConfirmNewUEduPWD ConfirmNewUEduPWD The two password entries are inconsistent, please reset Les deux entrées de mot de passe sont incohérentes, veuillez réinitialiser Password entered incorrectly, please try again Mot de passe saisi incorrectement, veuillez réessayer clear clair DlgConnHidWifi Add Hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Wi-Fi name Nom du Wi-Fi Wi-Fi security Sécurité Wi-Fi Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Username Nom d’utilisateur Password Mot de passe Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Authentication Authentification Anonymous identity Identité anonyme Allow automatic PAC pro_visioning Autoriser l’pro_visioning PAC automatique PAC file Fichier PAC Inner authentication Authentification interne Username Nom d’utilisateur Password Mot de passe Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise Tunneled TLS TLS tunnelisé Protected EAP (PEAP) PAE protégé (PEAP) Anonymous Anonyme Authenticated Authentifié Both Les deux DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Authentication Authentification Username Nom d’utilisateur Password Mot de passe Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise Tunneled TLS TLS tunnelisé Protected EAP (PEAP) PAE protégé (PEAP) DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Authentication Authentification Anonymous identity Identité anonyme Domain Domaine CA certificate Certificat d’autorité de certification CA certificate password Mot de passe du certificat de l’autorité de certification No CA certificate is required Aucun certificat d’autorité de certification n’est requis PEAP version Version PEAP Inner authentication Authentification interne Username Nom d’utilisateur Password Mot de passe Cancel Annuler Connect Relier None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise Tunneled TLS TLS tunnelisé Protected EAP (PEAP) PAE protégé (PEAP) Choose from file Choisir dans le fichier Automatic Automatique Version 0 Variante 0 Version 1 Variante 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Authentication Authentification Username Nom d’utilisateur Password Mot de passe Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise Tunneled TLS TLS tunnelisé Protected EAP (PEAP) PAE protégé (PEAP) DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Authentication Authentification Identity Identité Domain Domaine CA certificate Certificat d’autorité de certification CA certificate password Mot de passe du certificat de l’autorité de certification No CA certificate is required Aucun certificat d’autorité de certification n’est requis User certificate Certificat d’utilisateur User certificate password Mot de passe du certificat d’utilisateur User private key Clé privée de l’utilisateur User key password Mot de passe de la clé utilisateur Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise Tunneled TLS TLS tunnelisé Protected EAP (PEAP) PAE protégé (PEAP) Choose from file Choisir dans le fichier DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Authentication Authentification Anonymous identity Identité anonyme Domain Domaine CA certificate Certificat d’autorité de certification CA certificate password Mot de passe du certificat de l’autorité de certification No CA certificate is required Aucun certificat d’autorité de certification n’est requis Inner authentication Authentification interne Username Nom d’utilisateur Password Mot de passe Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise Tunneled TLS TLS tunnelisé Protected EAP (PEAP) PAE protégé (PEAP) Choose from file Choisir dans le fichier DlgConnHidWifiWep Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Key Clé WEP index Indice WEP Authentication Authentification Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) Clé WEP 40/128 bits (hexadécimal ou ASCII) WEP 128-bit Passphrase Phrase secrète WEP 128 bits Dynamic WEP (802.1X) WEP dynamique (802.1X) WPA & WPA2 Enterprise WPA et WPA2 Entreprise 1(default) 1 (par défaut) Open System Système ouvert Shared Key Clé partagée DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Add Hidden Wi-Fi Ajouter une connexion Wi-Fi cachée Connection Connexion Wi-Fi name Nom du Wi-Fi Wi-Fi security Sécurité Wi-Fi Password Mot de passe Cancel Annuler Connect Relier C_reate… C_reate… None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Dialog Dialogue Create Hotspot Créer un point d’accès Network name Nom du réseau Wi-Fi security Sécurité Wi-Fi Password Mot de passe Cancel Annuler Ok D’accord None Aucun WPA & WPA2 Personal WPA et WPA2 Personnel EngineDevice yes no Yes No %1% available, charged Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK D’ACCORD InputInfos Service exception... Service d’exception... Invaild parameters... Paramètres d’invaild... Unknown fault:%1 Défaut inconnu :%1 Recapture(60s) Recapture (années 60) Recapture(%1s) Recapture(%1s) Get code Obtenir le code KBTitle Suspended state Close Welt status KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm kylin-nm LAN LAN Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced Avancé Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot Point chaud FlyMode Mode Vol Show MainWindow 显示网络连接界面 Inactivated LAN LAN inactivé Inactivated WLAN 未激活 Other WLAN Autre WLAN WLAN Réseau local sans fil (WLAN) Show KylinNM Afficher KylinNM No wireless card detected Aucune carte sans fil n’a été détectée Activated LAN LAN activé Activated WLAN WLAN activé Not connected Non connecté Disconnected Coupé No Other Wired Network Scheme Pas d’autre schéma de réseau câblé Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme Pas d’autre schéma de réseau sans fil Wired net is disconnected Le réseau câblé est déconnecté Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card Confirmez votre mot de passe Wi-Fi ou votre carte sans fil utilisable Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list Aucun réseau utilisable dans la liste NetOn, NetOn, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success Succès de Conn Ethernet Conn Ethernet Fail Échec de l’Ethernet Conn Conn Wifi Success Succès de Conn Wifi LettersWidget &&?! &&?! 123 123 Ctrl Ctrl Alt Alt LightDMHelper failed to start session. Login Connectez-vous Guest 游客 LockWidget Date Date Time Heure Guest 游客 SwitchUser SwitchUser (Utilisateur commuté) Power Pouvoir VirtualKeyboard Clavier virtuel Multiple users are logged in at the same time.Are you sure you want to reboot this system? Plusieurs utilisateurs sont connectés en même temps. Êtes-vous sûr de vouloir redémarrer ce système ? LAN LAN WLAN Réseau local sans fil (WLAN) system-monitor Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Multiple users are logged in at the same time.Are you sure you want to %1 this system? Restart Redémarrer SwitchSession Power Information Network 网络 Shut Down Arrêter LoginOptionsWidget Login Options Options de connexion Password Mot de passe Other Autre FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey (en anglais seulement) QRCode QRCode Wechat 微信 Identify device removed! Identifier l’appareil supprimé ! MPRISSaverWidget No music detected Unknown MusicFileInformation Unknown singer Unknown album MyLineEdit Verification code Code de vérification MyNetworkWidget LAN LAN WLAN Réseau local sans fil (WLAN) NumbersWidget &&?! &&?! Return Rendre OneConnForm Form Forme Automatically join the network Rejoindre automatiquement le réseau Input password 输入密码 Config 设置 Connect Relier Disconnect Déconnecter Input Password... Entrez le mot de passe... Connect to Hidden Wi-Fi Network Connectez-vous à un réseau Wi-Fi caché Signal: Signal: Public 开放 Safe 安全 Rate 速率 None Aucun WiFi Security: Sécurité Wi-Fi : MAC: MAC: Conn Wifi Failed Échec de Conn Wifi OneLancForm Form Forme Config 设置 Connect Relier Disconnect Déconnecter No Configuration Pas de configuration IPv4: IPv4 : IPv6: IPv6 : BandWidth: Bande passante: MAC: MAC: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhoneAuthWidget Verification by phoneNum Vérification par phoneNum 「 Use bound Phone number to verification 」 「 Utiliser le numéro de téléphone lié à la vérification 」 「 Use SMS to verification 」 「 Utiliser les SMS pour vérifier 」 commit commettre Network not connected~ Réseau non connecté~ Network unavailable~ Réseau indisponible~ Verification Code invalid! Le code de vérification n’est pas valide ! Verification Code incorrect.Please retry! Le code de vérification est incorrect. Veuillez réessayer ! Failed time over limit!Retry after 1 hour! Dépassement de la limite de temps manquée ! Réessayez après 1 heure ! verifaction failed! Échec de la vérification ! PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate Hiberner Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Éteignez votre ordinateur, mais l’application reste ouverte. Lorsque l’ordinateur est allumé, il peut être restauré dans l’état que vous avez laissé Suspend Suspendre The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off L’ordinateur reste allumé, mais consomme moins d’énergie. L’application reste ouverte et peut rapidement se réveiller et revenir à l’endroit où vous vous étiez arrêté The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off Restart Redémarrer Close all apps, and then restart your computer Shut Down Arrêter Log Out Se déconnecter The current user logs out of the system, terminates the session, and returns to the login page SwitchUser LockScreen UpgradeThenRestart UpgradeThenShutdown Power Off Mise hors tension Close all apps, and then shut down your computer Fermez toutes les applications, puis éteignez votre ordinateur Close all apps, turn off your computer, and then turn your computer back on Fermez toutes les applications, éteignez votre ordinateur, puis rallumez-le PowerManager lock serrure SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out Se déconnecter Restart Redémarrer Power Off Mise hors tension Close all apps, turn off your computer, and then turn your computer back on Fermez toutes les applications, éteignez votre ordinateur, puis rallumez-le Close all apps, and then shut down your computer Fermez toutes les applications, puis éteignez votre ordinateur Shut Down Arrêter Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Éteignez votre ordinateur, mais l’application reste ouverte. Lorsque l’ordinateur est allumé, il peut être restauré dans l’état que vous avez laissé Hibernate Hiberner The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off L’ordinateur reste allumé, mais consomme moins d’énergie. L’application reste ouverte et peut rapidement se réveiller et revenir à l’endroit où vous vous étiez arrêté Suspend Suspendre Sleep 休眠 QObject The screensaver is active. L’économiseur d’écran est actif. The screensaver is inactive. L’économiseur d’écran est inactif. FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey (en anglais seulement) QRCode QRCode Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm Confirmer Cancel Annuler Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel Annuler ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist L’image n’existe pas Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification Vous avez reçu une nouvelle notification View Vue Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Annuler Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested: 您已休息: You have rested Vous vous êtes reposé SureWindow Form Forme TextLabel TextLabel (Libellé textuel) Cancel Annuler Confirm Confirmer Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! Le programme suivant est en cours d’exécution pour empêcher le système de se suspendre ! The following program is running to prevent the system from hibernate! Le programme suivant est en cours d’exécution pour empêcher le système de hiberner ! The following program is running to prevent the system from shutting down! Le programme suivant est en cours d’exécution pour empêcher le système de s’arrêter ! The following program is running to prevent the system from reboot! Le programme suivant est en cours d’exécution pour empêcher le système de redémarrer ! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD uEduPWD Wechat Wechat (en anglais seulement) TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old Le nouveau mot de passe est le même que l’ancien Reset password error:%1 Erreur de réinitialisation du mot de passe :%1 Please scan by correct WeChat S’il vous plaît scanner par WeChat correct Cancel Annuler Back Précédent Skip Sautiller TalksWidget Enter your answer UserListWidget Login Connectez-vous Guest 游客 Utils kylin network applet desktop message Kylin Network applet message de bureau VerificationWidget Please scan by bound WeChat S’il vous plaît scanner par WeChat lié VerifyKyidWidget Enter your Kylin ID account Input password 输入密码 Enter the verification code Get verification code Reacquire(%1) VerticalVerificationWidget Please scan by bound WeChat S’il vous plaît scanner par WeChat lié WeChatAuthDialog Login by wechat Connexion par wechat Verification by wechat Vérification par wechat 「 Use registered WeChat account to login 」 「 Utilisez un compte WeChat enregistré pour vous connecter 」 「 Use bound WeChat account to verification 」 「 Utiliser le compte WeChat lié à la vérification 」 Network not connected~ Réseau non connecté~ Scan code successfully Scannez le code avec succès Timeout!Try again! Timeout! Réessayez! Login failed 登录失败 Widget Widget LoadPlugin action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock Combien de temps pour afficher le verrouillage has-lock if show lock si afficher le verrou main Start command for the ukui ScreenSaver. Démarrer la commande pour l’économiseur d’écran ukui. lock the screen immediately Verrouillez l’écran immédiatement query the status of the screen saver Interroger l’état de l’économiseur d’écran unlock the screen saver Déverrouiller l’économiseur d’écran show the screensaver Afficher l’économiseur d’écran show blank and delay to lock,param:idle/lid/lowpower afficher le vide et le délai de verrouillage, param :idle/couvercle/lowpower show the session tools show the switchuser window show the app block window show the multiUsers block window Dialog for the ukui ScreenSaver. Boîte de dialogue pour l’économiseur d’écran ukui. activated by session idle signal activé par le signal d’inactivité de session lock the screen and show screensaver immediately Verrouillez l’écran et affichez immédiatement l’économiseur d’écran show screensaver immediately Afficher l’économiseur d’écran immédiatement show blank screensaver immediately and delay time to show lock Afficher immédiatement l’économiseur d’écran vide et retarder le temps d’affichage du verrouillage show blank screensaver immediately and if lock Afficher l’économiseur d’écran vide immédiatement et si le verrouillage show switch user window switch to user and show window Screensaver for ukui-screensaver Économiseur d’écran pour ukui-screensaver show on root window Afficher à la racine de la fenêtre show on window. Afficher sur la fenêtre. window id ID de la fenêtre Backend for the ukui ScreenSaver. lock the screen by startup switchtouser switch to someone user ukui-screensaver/i18n_ts/bo.ts0000664000175000017500000023517115172041106015236 0ustar fengfeng AgreementWindow I know AuthDialog Login Guest Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock Password cannot be empty The authentication has been cancelled! Password: Password Prompt:%1 Input Password Username User name input error! Authentication failure, Please try again login Retry Please try again in %1 minutes. Please try again in %1 seconds. Account locked permanently. Failed to verify %1, please enter password to unlock Unable to verify %1, please enter password to unlock Failed to verify %1, you still have %2 verification opportunities Abnormal network Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. FingerPrint FingerVein Iris Face VoicePrint Ukey QRCode fingerprint fingervein iris face voiceprint Insert the ukey into the USB port Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication BatteryWidget Charging... fully charged PowerMode BatteryMode BindDialog Close Ok Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BlockWidget Cancel Confirm If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. The following programs prevent suspend, you can click "Cancel" and then close these programs. The following programs prevent hibernation, you can click "Cancel" and then close these programs. The following programs prevent you from logging out, you can click "Cancel" and then close them. shut down restart CharsMoreWidget &&?! CharsWidget More ABC 123 CustomAlbumViewer No photos detected, please re-select them EngineDevice yes no Yes No %1% available, charged Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK KBTitle Suspended state Close Welt status LettersWidget &&?! 123 LightDMHelper failed to start session. Login Guest LockWidget system-monitor Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Multiple users are logged in at the same time.Are you sure you want to %1 this system? Restart SwitchSession Power Information SwitchUser Network VirtualKeyboard Power Shut Down LoginOptionsWidget Login Options Password Other Identify device removed! FingerPrint FingerVein Iris Face VoicePrint Ukey QRCode MPRISSaverWidget No music detected Unknown MusicFileInformation Unknown singer Unknown album MyNetworkWidget LAN WLAN NumbersWidget &&?! Return PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Suspend The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off Restart Close all apps, and then restart your computer Shut Down Close all apps, and then shut down your computer Log Out The current user logs out of the system, terminates the session, and returns to the login page SwitchUser LockScreen UpgradeThenRestart UpgradeThenShutdown Close all apps, turn off your computer, and then turn your computer back on QObject FingerPrint FingerVein Iris Face VoicePrint Ukey QRCode The screensaver is active. The screensaver is inactive. Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver Picture does not exist View Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested TalksWidget Enter your answer UserListWidget Login Guest VerifyKyidWidget Enter your Kylin ID account Input password Enter the verification code Get verification code Reacquire(%1) Widget Widget LoadPlugin action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock has-lock if show lock main Backend for the ukui ScreenSaver. lock the screen by startup Start command for the ukui ScreenSaver. lock the screen immediately query the status of the screen saver unlock the screen saver show the screensaver show blank and delay to lock,param:idle/lid/lowpower show the session tools show the switchuser window show the app block window show the multiUsers block window Dialog for the ukui ScreenSaver. activated by session idle signal lock the screen and show screensaver immediately show screensaver immediately show blank screensaver immediately and delay time to show lock show blank screensaver immediately and if lock show switch user window switch to user and show window Screensaver for ukui-screensaver show on root window show on window. window id switchtouser switch to someone user ukui-screensaver/i18n_ts/ky.ts0000664000175000017500000046323115172041106015261 0ustar fengfeng AgreementWindow I know بىلدىم AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry قايرا قايرا سىنوو UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. .سىز 1% مىنۇت ىچىندە قايرا سىناپ باعىڭ Please try again in %1 seconds. .سىز 1% سىكونت ىچىندە قايرا سىناپ باعىڭ Account locked permanently. .ەسابات تۉبۅلۉك قۇلۇپلىنىدۇ Verify face recognition or enter password to unlock چىراي تاانىشتى دالىلدۅ كۅرۉنۉشتۅرۉ جاشىرۇۇن نومۇر كىرگىزۉۉ ارقىلۇۇ قۇلپ اچۇۇ Guest قونوق (اتى جوق تىزىمدەتىش ) Press fingerprint or enter password to unlock بارماق ىزىن باسۇۇدا كۅرۉنۉشتۅرۉ جاشىرۇۇن نومۇر كىرگىزۉۉ ارقىلۇۇ قۇلپ اچۇۇ Verify voiceprint or enter password to unlock دووش ىزىن دالىلدۅ كۅرۉنۉشتۅرۉ جاشىرۇۇن نومۇر كىرگىزۉۉ ارقىلۇۇ قۇلپ اچۇۇ Verify finger vein or enter password to unlock بارماق ئزى ارقىلۇۇ انىقتاش كۅرۉنۉشتۅرۉ جاشىرۇۇن نومۇر كىرگىزۉۉ ارقىلۇۇ قۇلپ اچۇۇ Verify iris or enter password to unlock نى انىقتاش كۅرۉنۉشتۅرۉ جاشىرۇۇن نومۇر كىرگىزۉۉ ارقىلۇۇ قۇلپ اچۇۇ Iris Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock The authentication has been cancelled! Password: جاشىرۇۇن نومۇر Input Password جاشىرۇۇن نومۇردۇ كىرگىزىڭ Username كەرەكتۅۅچۉ ناامى User name input error! ىشتەتۉۉچۉ اتى قاتاا كىرگىزىلدى login كىرۉۉ Failed to verify %1, please enter password to unlock دالىلدۅ 1% جەڭىلۉۉ بولدۇ ، جاشىرۇۇن نومۇر كىرگىزىڭ Unable to verify %1, please enter password to unlock دالىلدۅۅگۅ بولبودۇ 1%، جاشىرۇۇن نومۇردۇ كىيىرىپ قۇلۇپنى اچىڭ Abnormal network بۅتۅنچۅ تور Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. چىراي تونۇتۇشنىڭ ساقتوو ۇباقتى اشىپ كەتتى، جاڭىلوونۇ چەگىپ كۅرۉنۉشتۅرۉ جاشىرۇۇن نومۇر كىيىرىپ قۇلۇپنى اچىڭ. FingerPrint بارماق ئزى FingerVein بارماق ۋېناسى Iris رەڭدار پاردا Face ادام بەتى VoicePrint دووش ئزى Ukey جاشىرۇۇن اچقىچ QRCode ەكىلىك قۇپۇيا نومۇر fingerprint بارماق ئزى fingervein بارماق ۋېنا تومۇزى iris رەڭدار پاردا face ادام بەتى voiceprint دووش ئزى Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty جاشىرۇۇن نومۇردۇ بوش ،بەكەر قويۇشقا بولبويت Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities دالىلدۅ 1% جەڭىلۉۉ بولدۇ ، داعى 2% دالىلدۅ وڭۇتۇڭۇز بار Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again دالىلدۅ جەڭىلۉۉ بولدۇ ، قايرا سىناپ باعىڭ Use the bound wechat scanning code or enter the password to unlock بايلانعان ئۈندىدارنى ىسكاننىردوو قۇپۇيا نومۇرۇ كۅرۉنۉشتۅرۉ جاشىرۇۇن نومۇرۇن كىيىرىپ قۇلپ اچۇۇ Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port 请将安全密钥插入USB端口 Password 密码 Login كىرۉۉ Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... زەرەتلىنىۋاتىدۇ... fully charged تۅك تولۇق قاچالاندى PowerMode تۅك قامدوو ۉلگۉسۉ BatteryMode باتارىيا ابالى BindDialog Close جابۇۇ Ok 确定 Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel ارعادان قالتىرىش Confirm بەكىتۉۉ If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. تۅمۅندۅكۉ پىروگراممىلار قايتادان قوزعوتۇشتۇ توسۇپ قالساڭىز بولوت ،سىز «ارعادان قالتىرىش »نى چەگىپ اندان بۇل تارتىپتەردى ئېتىۋەتسىڭىز بولوت . The following programs prevent the shutdown, you can click "Cancel" and then close these programs. تۅمۅندۅكۉ پراگرامما ئېتىۋېتىلگەنلىكىنى توسۇپ قالات،سىز «ارعادان قالتىرىش »نى چەگىپ اندان بۇل پىروگىراممالاردى ئېتىۋەتسىڭىز بولوت . The following programs prevent suspend, you can click "Cancel" and then close these programs. تۅمۅندۅكۉ پراگرامما ئۇيقۇنى توسۇپ قالات،سىز «ارعادان قالتىرىش »نى چەگىپ اندان بۇل پىروگىراممالاردى ئېتىۋەتسىڭىز بولوت . The following programs prevent hibernation, you can click "Cancel" and then close these programs. تۅمۅندۅكۉ پراگرامما ئۇيقۇنى توسۇپ قالات،سىز «ارعادان قالتىرىش »نى چەگىپ اندان بۇل پىروگىراممالاردى ئېتىۋەتسىڭىز بولوت . The following programs prevent you from logging out, you can click "Cancel" and then close them. تۅمۅندۅكۉ پىروگراممىلار بەكەر قىلۇۇنۇنۇ توسۇپ قالات،سىز «ارعادان قالتىرىش »نى چەگىپ اندان بۇل پىروگىراممالاردى ئېتىۋەتسىڭىز بولوت . The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. ᠪᠦᠬᠦ ᠪᠠᠭᠤᠯᠵᠠᠭᠠᠨᠢ ᠳᠤᠷᠬᠠᠢ ᠵᠠᠬᠢᠷᠠᠯ ᠪᠠᠨ ᠬᠠᠨᠳᠢᠨ᠎ᠠ ᠴᠢᠬᠤᠯᠠᠨ "ᠴᠤᠴᠠᠯᠬᠤ" ᠳᠤ ᠳᠠᠷᠠᠭᠠᠳ ᠡᠳᠡᠭᠡᠷ ᠪᠠᠭᠤᠯᠵᠠᠭᠠᠨᠢ ᠬᠠᠭᠠᠴᠠᠭᠠᠢ᠃ ᠡᠷᠡᠳᠡᠭᠡᠷ "ᠪᠠᠲᠠᠯᠭᠠᠵᠤ ᠦᠵᠡᠬᠦ" ᠳᠤ ᠳᠠᠷᠠᠭᠠᠳ ᠬᠣᠲᠠᠭᠠᠢᠯᠠᠨ ᠬᠠᠭᠠᠴᠠᠭᠠᠢ ᠪᠠᠨ ᠦᠷᠭᠡᠨ᠎ᠡ᠃​​ The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. ᠪᠠᠭᠤᠯᠵᠠᠭᠠᠨᠤᠳᠤ ᠬᠠᠭᠠᠴᠠᠯᠠᠭᠠᠢ ᠪᠤᠢ᠃ "ᠴᠤᠴᠠᠯᠬᠤ" ᠳᠤ ᠳᠠᠷᠠᠭᠠᠳ ᠡᠳᠡᠭᠡᠷ ᠡᠳᠭᠡᠯ ᠪᠠᠭᠤᠯᠵᠠᠭᠠᠨᠢ ᠬᠠᠭᠠᠴᠠᠭᠠᠢ᠃ ᠡᠷᠡᠳᠡᠭᠡᠷ "ᠪᠠᠲᠠᠯᠭᠠᠵᠤ ᠦᠵᠡᠬᠦ" ᠳᠤ ᠳᠠᠷᠠᠭᠠᠳ ᠬᠣᠲᠠᠭᠠᠢᠯᠠᠨ ᠬᠠᠭᠠᠴᠠᠭᠠᠢ ᠪᠠᠨ ᠦᠷᠭᠡᠨ᠎ᠡ᠃​​ The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. ​ᠪᠠᠭᠤᠯᠵᠠᠭᠠᠨᠤᠳᠤ ᠨᠡᠷᠡᠬᠦ ᠠᠮᠢᠳᠠᠭᠤᠯᠬᠤ ᠵᠠᠬᠢᠷᠠᠯᠠᠵᠠᠢ᠃ "ᠴᠤᠴᠠᠯᠬᠤ" ᠳᠤ ᠳᠠᠷᠠᠭᠠᠳ ᠡᠳᠡᠭᠡᠷ ᠡᠳᠭᠡᠯ ᠪᠠᠭᠤᠯᠵᠠᠭᠠᠨᠢ ᠬᠠᠭᠠᠴᠠᠭᠠᠢ᠃ ᠡᠷᠡᠳᠡᠭᠡᠷ "ᠪᠠᠲᠠᠯᠭᠠᠵᠤ ᠦᠵᠡᠬᠦ" ᠳᠤ ᠳᠠᠷᠠᠭᠠᠳ ᠬᠣᠲᠠᠭᠠᠢᠯᠠᠨ ᠬᠠᠭᠠᠴᠠᠭᠠᠢ ᠪᠠᠨ ᠦᠷᠭᠡᠨ᠎ᠡ᠃​​ shut down restart CharsMoreWidget &&?! &&?! CharsWidget More داعى ەلە كۅپ ABC ABC 123 123 ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 EngineDevice yes وشوندوي no جوق Yes وشوندوي No جوق %1% available, charged %1% نى ىشتەتكەلى بولوت ، اقى الىپ بولدۇ . Left %1h %2m (%3%) سول تاراپ (%1 h %2 m (%3 %)) %1% available %1% ىشتەتكەلى بولوت Left %1h %2m to full سولعو (%1h %2 m) لىق توشتۇ charging (%1%) زەرەتلىنىۋاتىدۇ (%1) %1 waiting to discharge (%2%) %1(%دوختۇرخانىدىن چىعۇۇنۇ ساقتاپ جاتات (%2 %1 waiting to charge (%2%) %1 تۅك قاچالوونۇ ساقتاپ جاتات (%2 AC adapter پىكىرى الماشتىرۇۇ شايكەشتىرگىچ Laptop battery جازما دارەك كومپىيۇتەر باتارېيەسى UPS Monitor كۉزۅتكۉچ Mouse ماۇس Keyboard كۇنۇپكا تاقتاسى PDA Cell phone ۇقۇق ،امال تەلەفون Media player ارعا قويعۇچ Tablet تاقتا Computer كومپىيۇتەر unrecognised ايىرما ەتىلبەدى IconEdit OK 提交 InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state ۇباقتىلۇۇ توقتوتۇلعان ابالى Close جابۇۇ Welt status چەت ابالى KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LettersWidget &&?! &&?! 123 123 LightDMHelper failed to start session. دىيالوگنى باشتوو جەڭىلۉۉ بولدۇ Login كىرۉۉ Guest قونوق (اتى جوق تىزىمدەتىش ) LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser 切换用户 Power 电源 VirtualKeyboard 虚拟键盘 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession Power Information Network 网络 system-monitor سەستىمالۇۇ كۉزۅتكۉچ Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Restart قايرا قوزعوتۇۇ Multiple users are logged in at the same time.Are you sure you want to %1 this system? بىر قانچا كەرەكتۅۅچۉ بىر لا ۇباقىتتا ساامالىقا كىردى، بۇل %1 سەستىمانى ىشتەتىشتى ايقىنداشتىراسىزبى؟ Reboot 重启 PowerOff 关机 Shut Down تەلفوندۇ ەتىپ جىبەرۉۉ LoginOptionsWidget Login Options كىرۉۉ تاندالمالارى Password جاشىرۇۇن نومۇر Other باشقا FingerPrint بارماق ئزى FingerVein بارماق ۋېناسى Iris رەڭدار پاردا Face ادام بەتى VoicePrint دووش ئزى Ukey جاشىرۇۇن اچقىچ QRCode ەكىلىك قۇپۇيا نومۇر Wechat 微信 Identify device removed! !اسپاپتى ايىرمالاندىرىش چىقىرىۋېتىلدى MPRISSaverWidget No music detected Unknown بەلگىسىز MPRISWidget Unknown بەلگىسىز MusicFileInformation Unknown singer Unknown album MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN سىمدۇۇ تور WLAN تار گۅۅلۅمدۉ سەمىز تور NumbersWidget &&?! &&?! Return قايتۇۇ OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate ئۈچەككە كىرۉۉ Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left كومپىيۇتەردى ەتىك، بىروق ئەپ اچىق ابالىن ساقتايت . كومپىيۇتەر اچىلعان ۇچۇردا ، سىز ايرىلعان ۇچۇرداقى ابالعا قايتات Suspend توقتوتۇپ قويۇش The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off كومپىيۇتەر اچىق ابالىن ساقتايت ، بىر وق قۇۋۋەت سەرپىياتى بىر عاچا تۅمۅن. بۇل ئەپ اچىق ابالدا تۇرات، تەز تەزدىكتە ئويغىنىدۇ داعى سىز توقتوعون جايعا قايتارعالى بولوت Restart قايرا قوزعوتۇۇ Close all apps, and then restart your computer باردىق ئەپنى ۅچۉرۉپ، اندان كومپىيۇتەردى قايرا قوزعوتۇۇ Shut Down تەلفوندۇ ەتىپ جىبەرۉۉ The current user logs out of the system, terminates the session, and returns to the login page گەزەكتە ابونتتار سەستىمادان بەكەر قىلىنىپ، سۉيلۅشۉۉنۉ ئاخىرلاشتۇردى داعى تىزىمدەتىش كۅرۉنۉشۉنۅ قايتات. Logout بەكەر قىلىۋەتمەك Power Off 关机 Close all apps, and then shut down your computer باردىق ئەپنى ۅچۉرۉپ، اندان كومپىيۇتەردى ۅچۉرۉۉ SwitchUser سووداگەر ، جولووچۇ ئالماشتۇرماق LockScreen قۇلپ ەكرانى Log Out بەكەر قىلىۋەتمەك The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off كومپىيۇتەر اچىق ابالىن ساقتايت ، بىر وق قۇۋۋەت سەرپىياتى بىر عاچا تۅمۅن. بۇل ئەپ اچىق ابالدا تۇرات، تەز تەزدىكتە ئويغىنىدۇ داعى سىز توقتوعون جايعا قايتارعالى بولوت UpgradeThenRestart جاڭىرتقاندان كىيىن قايتادان قوزعوتۇۇ Close all apps, turn off your computer, and then turn your computer back on باردىق ئەپنى ۅچۉرۉپ، كومپىيۇتەردى ۅچۉرۉپ، اندان كومپىيۇتەردى قايرا اچۇۇ UpgradeThenShutdown جاڭىلاعاندان كىيىن اپارات ەتىلەت PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. .ەكىران قورعوو ساامالىعى اراكەتچان ابالدا The screensaver is inactive. .ەكىران قورعوو ساامالىعى اراكەتسىز ابالدا FingerPrint بارماق ئزى FingerVein بارماق ۋېناسى Iris رەڭدار پاردا Face ادام بەتى VoicePrint دووش ئزى Ukey جاشىرۇۇن اچقىچ QRCode ەكىلىك قۇپۇيا نومۇر Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm 确认 Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist سۉرۅت باربولۇۇسۇ ەمەس Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View قاروو Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested: 您已休息: You have rested ەس الۇۇ ئالدىڭىز SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer UserListWidget Login كىرۉۉ Guest قونوق (اتى جوق تىزىمدەتىش ) Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account Input password 输入密码 Enter the verification code Get verification code Reacquire(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 Widget Widget كىچىك قۇرۇلما LoadPlugin كىچىنەكەي زاپاس action which block type which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart delay how long to show lock قانچەلىك ۇباقىت قۇلۇپلانغانلىقىنى كۅرسۅتۉۉ has-lock if show lock ەگەر بەكىتىشتى كۅرسۅتمۅكچۉ بولسو main Start command for the ukui ScreenSaver. ەكىران قورعوو پراگرامماسىنىن بۇيرۇعۇن قوزعوتۇۇ ukui lock the screen immediately ەكراندى داروو قۇلۇپتوو query the status of the screen saver ەكىران قورعوو پراگرامماسىنىن ابالىن ىزىكتەش unlock the screen saver ەكىران قورعوو پراگرامماسىنىن قۇلۇپتۇۇ اچۇۇ show the screensaver ەكىران قورعوو پراگرامماسىن كۅرسۅتۉۉ show blank and delay to lock,param:idle/lid/lowpower idle/lid/lowpower :قۇرۇقنى كۅرسۅتۉۉ جانا بەكىتىشتى كەچىكتىرۉۉ، پارامەتىرى show the session tools show the switchuser window show the app block window show the multiUsers block window Backend for the ukui ScreenSaver. .ەكىران قورعوو پراگرامماسىنىن ارتى ووزۇ ukui lock the screen by startup قوزعولتقوندو ەكراندى قۇلۇپتوو Dialog for the ukui ScreenSaver. .ەكىران قورعوو پراگرامماسىنىن دىيالوگ كەرەنەسى ukui activated by session idle signal بەكەر سەگنال ارقىلۇۇ اكتىپتەشىرۉۉ lock the screen and show screensaver immediately ەكراندى قۇلۇپتوو داعى داروو ەكىران قورعوو پراگرامماسىن كۅرسۅتۉۉ show screensaver immediately ەكىران قورعوو پراگرامماسىن داروو كۅرسۅتۉۉ show blank screensaver immediately and delay time to show lock كۅڭدۅي ەكىران قورعوو پراگرامماسىن داروو كۅرسۅتۉۉ داعى قۇلۇپتوو ۇباقتىن كەچكىتىرىپ كۅرسۅتۉۉ show blank screensaver immediately and if lock ەگەر قۇلۇپلانسا، كۅڭدۅي ەكىران قورعوو پراگرامماسىن داروو كۅرسۅتۉۉ show switch user window switch to user and show window Screensaver for ukui-screensaver ەكىران قورعوو پراگرامماسىنىن ەكىران قورعوو پراگرامماسى ukui show on root window جىلدىز كۅزۅنۅكتۅ كۅرسۅتۉۉ show on window. .كۅزۅنۅكتۅ كۅرسۅتۉۉ window id id كۅزۅنۅك switchtouser switch to someone user ukui-screensaver/i18n_ts/ru.ts0000664000175000017500000025302115172041106015256 0ustar fengfeng AgreementWindow I know AuthDialog Form форма More Devices Дополнительные устройства Biometric Биометрические Password пароль Retry Retry UnLock отпереть LoggedIn LoggedIn Password Incorrect, Please try again Пароль неверен, повторите попытку FingerPrint FingerPrint FingerVein FingerVein Iris Ирис Face Лицо VoicePrint Voiceprint Guest гость Login Password cannot be empty Input Password Username User name input error! Authentication failure, Please try again login Please try again in %1 minutes. Please try again in %1 seconds. Account locked permanently. Failed to verify %1, please enter password to unlock Unable to verify %1, please enter password to unlock Failed to verify %1, you still have %2 verification opportunities Abnormal network Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. Ukey QRCode fingerprint fingervein iris face voiceprint Password: Password Prompt:%1 Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock Enter username The authentication has been cancelled! Insert the ukey into the USB port Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication wechat Use the bound %1 scanning code to unlock BatteryWidget Charging... fully charged PowerMode BatteryMode BindDialog Close Ok Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BioAuthWidget Form форма TextLabel TextLabel More Больше Retry Retry Password пароль BioDevices FingerPrint FingerPrint FingerVein FingerVein Iris Ирис Face Лицо VoicePrint Voiceprint BioDevicesWidget Form форма Please select other biometric devices Выберите другие биометрические устройства Device Type: Тип устройства: Device Name: Имя устройства: BlockWidget Cancel Confirm The following programs prevent restarting, you can click "Cancel" and then close these programs. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. The following programs prevent suspend, you can click "Cancel" and then close these programs. The following programs prevent hibernation, you can click "Cancel" and then close these programs. The following programs prevent you from logging out, you can click "Cancel" and then close them. If you do not perform any operation, the system will automatically %1 after %2 seconds. shut down restart CharsMoreWidget &&?! CharsWidget More Больше ABC 123 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint FingerPrint FingerVein FingerVein Iris Ирис Face Лицо VoicePrint Voiceprint DlgConnHidWifiLeap Password пароль DlgConnHidWifiSecFast Password пароль DlgConnHidWifiSecLeap Password пароль DlgConnHidWifiSecPeap Password пароль DlgConnHidWifiSecPwd Password пароль DlgConnHidWifiSecTunnelTLS Password пароль DlgConnHidWifiWpa Password пароль DlgHotspotCreate Password пароль EngineDevice yes no Yes No %1% available, charged Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK KBTitle Suspended state Close Welt status KeyboardWidget KeyboardWidget KeyboardWidget LettersWidget &&?! 123 LightDMHelper Guest гость failed to start session. Login LockWidget Form форма Date Дата Time Время Guest гость SwitchUser Сменить пользователя Multiple users are logged in at the same time.Are you sure you want to %1 this system? system-monitor Shut Down Restart %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". SwitchSession Power Information Network VirtualKeyboard Power Reset Pwd LoginOptionsWidget Password пароль FingerPrint FingerPrint FingerVein FingerVein Iris Ирис Face Лицо VoicePrint Voiceprint Login Options Other Identify device removed! Ukey QRCode MPRISSaverWidget No music detected Unknown MusicFileInformation Unknown singer Unknown album MyNetworkWidget LAN WLAN NumbersWidget &&?! Return OneConnForm Form форма OneLancForm Form форма PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Suspend Restart Shut Down Close all apps, and then shut down your computer Close all apps, turn off your computer, and then turn your computer back on Close all apps, and then restart your computer Log Out The current user logs out of the system, terminates the session, and returns to the login page SwitchUser Сменить пользователя LockScreen UpgradeThenRestart UpgradeThenShutdown The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off PowerManager SwitchUser Сменить пользователя QObject FingerPrint FingerPrint FingerVein FingerVein Iris Ирис Face Лицо VoicePrint Voiceprint Ukey QRCode The screensaver is active. The screensaver is inactive. Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver Picture does not exist View Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested SureWindow Form форма TextLabel TextLabel TalksWidget Enter your answer UserListWidget Guest гость Login VerifyKyidWidget Enter your Kylin ID account Input password Enter the verification code Get verification code Reacquire(%1) Widget Widget LoadPlugin action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock has-lock if show lock main Start command for the ukui ScreenSaver. Начните команду для ukui ScreenSaver. lock the screen immediately немедленно заблокируйте экран Dialog for the ukui ScreenSaver. Диалог для экранного экрана ukui. activated by session idle signal активируется сигналом холостого хода Backend for the ukui ScreenSaver. lock the screen by startup query the status of the screen saver unlock the screen saver show the screensaver show blank and delay to lock,param:idle/lid/lowpower lock the screen and show screensaver immediately show screensaver immediately show blank screensaver immediately and delay time to show lock show blank screensaver immediately and if lock switch to user and show window Screensaver for ukui-screensaver show on root window show on window. window id show the session tools show the switchuser window show the app block window show the multiUsers block window show switch user window switchtouser switch to someone user ukui-screensaver/i18n_ts/th.ts0000664000175000017500000037163415172041106015256 0ustar fengfeng AgreementWindow I know รับทราบ AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry ลองอีกครั้ง UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 พร้อมท์รหัสผ่าน:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. โปรดลองอีกครั้งใน %1 นาที Please try again in %1 seconds. โปรดลองอีกครั้งใน %1 วินาที Account locked permanently. บัญชีถูกล็อคอย่างถาวร Verify face recognition or enter password to unlock 验证人脸识别或输入密码解锁 Guest แขก Press fingerprint or enter password to unlock 按压指纹或输入密码解锁 Verify voiceprint or enter password to unlock 验证声纹或输入密码解锁 Verify finger vein or enter password to unlock 验证指静脉或输入密码解锁 Verify iris or enter password to unlock 验证虹膜或输入密码解锁 Enter username กรอกชื่อผู้ใช้ Facial recognition to unlock การจดจำใบหน้าเพื่อปลดล็อค Fingerprint recognition to unlock การจดจำลายนิ้วมือเพื่อปลดล็อค Voiceprint recognition to unlock การจดจำการพิมพ์เสียงเพื่อปลดล็อค Finger vein recognition to unlock การจดจำหลอดเลือดดำของนิ้วเพื่อปลดล็อค Iris recognition to unlock การจดจำม่านตาเพื่อปลดล็อค Use the bound wechat scanning code to unlock 微信扫码解锁 wechat วีแชท Use the bound %1 scanning code to unlock ใช้รหัสสแกน %1 ที่ผูกไว้เพื่อปลดล็อค The authentication has been cancelled! การรับรองความถูกต้องถูกยกเลิก! Password: รหัสผ่าน: Input Password ใส่รหัสผ่าน Username ชื่อผู้ใช้ User name input error! การป้อนชื่อผู้ใช้ผิดพลาด! login เข้าสู่ระบบ Failed to verify %1, please enter password to unlock ไม่สามารถตรวจสอบ %1 ได้ โปรดป้อนรหัสผ่านเพื่อปลดล็อค Unable to verify %1, please enter password to unlock ไม่สามารถยืนยัน %1 ได้ โปรดป้อนรหัสผ่านเพื่อปลดล็อค Abnormal network เครือข่ายผิดปกติ Acquisition failure การดึงข้อมูลล้มเหลว Face recognition waiting time out, please click refresh or enter the password to unlock. การรอการจดจำใบหน้าหมดเวลา โปรดคลิกรีเฟรชหรือป้อนรหัสผ่านเพื่อปลดล็อค FingerPrint ลายนิ้วมือ FingerVein เส้นเลือดดำนิ้วมือ Iris ม่านตา Face ใบหน้า VoicePrint กราฟแสดงลักษณะเสียงพูดของมนุษย์ Ukey อูกี้ QRCode คิวอาร์โค้ด fingerprint ลายนิ้วมือ fingervein เส้นเลือดดำนิ้วมือ iris ม่านตา face ใบหน้า voiceprint กราฟแสดงลักษณะเสียงพูดของมนุษย์ Two-Factor OTP Dynamic Password Authentication การตรวจสอบรหัสผ่านแบบไดนามิก OTP แบบสองปัจจัย Two-Factor Security Key Authentication การตรวจสอบสิทธิ์คีย์ความปลอดภัยแบบสองปัจจัย Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty รหัสผ่านต้องไม่เว้นว่าง Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities ไม่สามารถยืนยัน %1 คุณยังคงมีโอกาสยืนยัน %2 Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again การตรวจสอบสิทธิ์ล้มเหลว โปรดลองอีกครั้ง Use the bound wechat scanning code or enter the password to unlock 使用绑定的微信扫码或输入密码解锁 Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port เสียบอูกี้เข้ากับพอร์ต USB Password 密码 Login เข้าสู่ระบบ Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... กำลังชาร์จ... fully charged ชาร์จเต็มแล้ว PowerMode โหมดพลังงาน BatteryMode โหมดแบตเตอรี่ BindDialog Close ปิด Ok Ok Cancel ยกเลิก Mfa authentication is on, please bind OTP code การตรวจสอบสิทธิ์ Mfa เปิดอยู่ โปรดผูกรหัส OTP Go to Bind ไปที่ผูก Bind ผูก BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel ยกเลิก Confrim คอนเฟิร์ม If you do not perform any operation, the system will automatically %1 after %2 seconds. หากคุณไม่ดำเนินการใดๆ ระบบจะ %1 โดยอัตโนมัติหลังจาก %2 วินาที The following programs prevent restarting, you can click "Cancel" and then close these programs. โปรแกรมต่อไปนี้ป้องกันการรีสตาร์ท คุณสามารถคลิก "ยกเลิก" แล้วปิดโปรแกรมเหล่านี้ได้ The following programs prevent the shutdown, you can click "Cancel" and then close these programs. โปรแกรมต่อไปนี้ป้องกันการปิดเครื่อง คุณสามารถคลิก "ยกเลิก" จากนั้นปิดโปรแกรมเหล่านี้ได้ The following programs prevent suspend, you can click "Cancel" and then close these programs. โปรแกรมต่อไปนี้ป้องกันการระงับ คุณสามารถคลิก "ยกเลิก" จากนั้นปิดโปรแกรมเหล่านี้ The following programs prevent hibernation, you can click "Cancel" and then close these programs. โปรแกรมต่อไปนี้ป้องกันการไฮเบอร์เนต คุณสามารถคลิก "ยกเลิก" จากนั้นปิดโปรแกรมเหล่านี้ The following programs prevent you from logging out, you can click "Cancel" and then close them. โปรแกรมต่อไปนี้ป้องกันไม่ให้คุณออกจากระบบ คุณสามารถคลิก "ยกเลิก" แล้วปิดโปรแกรมเหล่านั้นได้ The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. โปรแกรมต่อไปนี้ป้องกันไม่ให้คุณรีสตาร์ท คุณสามารถคลิก "ยกเลิก" แล้วปิดโปรแกรมเหล่านั้น หรือคลิก "ตกลง" เพื่อบังคับปิดและดำเนินการต่อ The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. โปรแกรมต่อไปนี้ป้องกันไม่ให้คุณปิดเครื่อง คุณสามารถคลิก "ยกเลิก" จากนั้นปิดโปรแกรมเหล่านั้น หรือคลิก "ตกลง" เพื่อบังคับปิดและดำเนินการต่อ The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. โปรแกรมต่อไปนี้ป้องกันไม่ให้คุณออกจากระบบ คุณสามารถคลิก "ยกเลิก" แล้วปิดโปรแกรมเหล่านั้น หรือคลิก "ตกลง" เพื่อบังคับปิดและดำเนินการต่อ shut down ปิดตัวลง restart เริ่มต้นใหม่อีกครั้ง CharsWidget More มากกว่า ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them ไม่พบรูปภาพ กรุณาเลือกใหม่ DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 IconEdit OK โอเค InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state รัฐถูกระงับ Close ปิด Welt status สถานะดาม KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LightDMHelper failed to start session. ไม่สามารถเริ่มเซสชั่นได้ Login เข้าสู่ระบบ Guest แขก LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser ผู้ใช้สวิตช์ Power พลัง VirtualKeyboard คีย์บอร์ดเสมือน Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession สวิตช์เซสชั่น Power Information ข้อมูลพลังงาน Network เครือข่าย system-monitor การตรวจสอบระบบ Reset Pwd รีเซ็ตรหัสผ่าน %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". %1 อาจทำให้ผู้ใช้ที่เข้าสู่ระบบคอมพิวเตอร์เครื่องนี้สูญเสียเนื้อหาที่ยังไม่ได้จัดเก็บ หากต้องการดำเนินการ โปรดคลิก "ยืนยัน" Restart เริ่มต้นใหม่อีกครั้ง Multiple users are logged in at the same time.Are you sure you want to %1 this system? ผู้ใช้หลายคนเข้าสู่ระบบพร้อมกัน คุณแน่ใจหรือไม่ว่าต้องการ %1 ระบบนี้? Reboot 重启 PowerOff 关机 Shut Down ปิดตัวลง LoginOptionsWidget Login Options ตัวเลือกการเข้าระบบ Password รหัสผ่าน Other อื่น ๆ FingerPrint ลายนิ้วมือ FingerVein เส้นเลือดดำนิ้วมือ Iris ม่านตา Face ใบหน้า VoicePrint กราฟแสดงลักษณะเสียงพูดของมนุษย์ Ukey อูกี้ QRCode คิวอาร์โค้ด Wechat 微信 Identify device removed! ระบุอุปกรณ์ที่ถูกลบออก! MPRISSaverWidget No music detected ไม่พบเพลง Unknown ไม่ทราบ MPRISWidget Unknown 未知 MusicFileInformation Unknown singer นักร้องที่ไม่รู้จัก Unknown album อัลบั้มที่ไม่รู้ MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN LAN WLAN WLAN NumbersWidget Return กลับ OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters รหัสผ่านสั้นกว่าจำนวน %1 ตัวอักษร The password contains less than %1 character classes รหัสผ่านมีประเภทอักขระน้อยกว่า %1 ประเภท The password is the same as the old one รหัสผ่านเหมือนกับรหัสผ่านเก่า The password contains the user name in some form รหัสผ่านมีชื่อผู้ใช้อยู่ในบางรูปแบบ The password differs with case changes only รหัสผ่านแตกต่างกันแค่ตัวพิมพ์เล็ก–ใหญ่ The password is too similar to the old one รหัสผ่านคล้ายกับรหัสผ่านเก่าเกินไป The password is a palindrome รหัสผ่านเป็นพาลินโดรม PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PowerListWidget Hibernate ไฮเบอร์เนต Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left ปิดคอมพิวเตอร์ของคุณ แต่แอปยังคงเปิดอยู่ เมื่อคอมพิวเตอร์เปิดอยู่ จะสามารถคืนสถานะเป็นสถานะที่คุณทิ้งไว้ได้ Suspend ระงับ The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Restart เริ่มต้นใหม่อีกครั้ง Close all apps, and then restart your computer ปิดแอปทั้งหมด จากนั้นรีสตาร์ทคอมพิวเตอร์ Shut Down ปิดตัวลง The current user logs out of the system, terminates the session, and returns to the login page ผู้ใช้ปัจจุบันออกจากระบบ ยุติเซสชัน และกลับสู่หน้าเข้าสู่ระบบ Logout 注销 Power Off 关机 Close all apps, and then shut down your computer ปิดแอพทั้งหมด จากนั้นปิดคอมพิวเตอร์ของคุณ SwitchUser ผู้ใช้สวิตช์ LockScreen ล็อคหน้าจอ Log Out ออกจากระบบ The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off คอมพิวเตอร์เปิดอยู่แต่ใช้พลังงานน้อยลง แอปยังคงเปิดอยู่และสามารถปลุกและกลับสู่จุดที่คุณค้างไว้ได้อย่างรวดเร็ว UpgradeThenRestart อัปเกรดแล้วรีสตาร์ท Close all apps, turn off your computer, and then turn your computer back on ปิดแอพทั้งหมด ปิดคอมพิวเตอร์ของคุณ จากนั้นเปิดคอมพิวเตอร์ของคุณอีกครั้ง UpgradeThenShutdown อัปเกรดแล้วปิดเครื่อง PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. สกรีนเซฟเวอร์ทำงานอยู่ The screensaver is inactive. สกรีนเซฟเวอร์ไม่ทำงาน FingerPrint ลายนิ้วมือ FingerVein เส้นเลือดดำนิ้วมือ Iris ม่านตา Face ใบหน้า VoicePrint กราฟแสดงลักษณะเสียงพูดของมนุษย์ Ukey อูกี้ QRCode คิวอาร์โค้ด ResetPwdWidget Reset your password 重置密码 Security Question คำถามความปลอดภัย Kylin ID รหัสไคลิน Enter new password ป้อนรหัสผ่านใหม่ Confirm your password ยืนยันรหัสผ่าน Inconsistency with pwd ไม่สอดคล้องกับรหัสผ่าน Confirm ยืนยัน Cancel ยกเลิก Contains illegal characters! มีอักขระที่ผิดกฎหมาย! The service is abnormal การบริการมีความผิดปกติ The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) รหัสยืนยันทาง SMS ถูกส่งไปยังโทรศัพท์มือถือที่มีหมายเลขหาง %1 แล้ว (ใช้ได้ 5 นาที) Failed to get the verification code ไม่สามารถรับรหัสยืนยันได้ The account does not exist, please re-enter it บัญชีไม่มีอยู่ โปรดป้อนใหม่อีกครั้ง The account does not exist, please complete the registration first บัญชีไม่มีอยู่ กรุณาลงทะเบียนให้เสร็จสิ้นก่อน The user is already locked, contact the administrator to unlock it ผู้ใช้ถูกล็อคแล้ว โปรดติดต่อผู้ดูแลระบบเพื่อปลดล็อค If there are too many login errors, please try again in 5 minutes หากมีข้อผิดพลาดในการเข้าสู่ระบบมากเกินไป โปรดลองอีกครั้งใน 5 นาที The SMS verification code is incorrect or has expired รหัสยืนยันทาง SMS ไม่ถูกต้องหรือหมดอายุแล้ว Wrong username or password ชื่อผู้ใช้หรือรหัสผ่านผิด Password decryption error ข้อผิดพลาดในการถอดรหัสรหัสผ่าน Verify Kylin ID Reset Password ตรวจสอบรหัสผ่านรีเซ็ตไคลิน ID Answer the security questions and reset your password ตอบคำถามเพื่อความปลอดภัยและรีเซ็ตรหัสผ่านของคุณ One or more wrong answers คำตอบที่ผิดอย่างน้อยหนึ่งคำตอบ S: tablet device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist รูปภาพไม่มีอยู่ Not connected to a network ไม่ได้เชื่อมต่อกับเครือข่าย The weather update failed อัปเดตสภาพอากาศล้มเหลว Smart Saver สมาร์ทเซฟเวอร์ Weather สภาพอากาศ Music เพลง Photo Albun อัลบั้มรูป Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View สำรวจ SleepTime You have rested: 您已休息: You have rested คุณได้พักผ่อนแล้ว SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer ป้อนคำตอบของคุณ UserListWidget Login เข้าสู่ระบบ Guest แขก Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account ป้อนบัญชีไคลิน ID ของคุณ Input password ใส่รหัสผ่าน Enter the verification code ป้อนรหัสยืนยัน Get verification code รับรหัสยืนยัน Reacquire(%1) รับอีกครั้ง(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 WeatherWidget Not connected to a network 网络未连接 delay how long to show lock จะแสดงล็อคนานแค่ไหน has-lock if show lock ถ้าแสดงล็อค main Start command for the ukui ScreenSaver. คำสั่งเริ่มสำหรับ ukui สกรีนเซเวอร์ lock the screen immediately ล็อคหน้าจอทันที query the status of the screen saver สอบถามสถานะของโปรแกรมรักษาหน้าจอ unlock the screen saver ปลดล็อคโปรแกรมรักษาหน้าจอ show the screensaver แสดงสกรีนเซฟเวอร์ show the session tools แสดงเครื่องมือเซสชั่น show the app block window แสดงวินโดว์บล็อกแอป show the multiUsers block window แสดงวินโดว์บล็อกผู้ใช้หลายคน Backend for the ukui ScreenSaver. แบ็กเอนด์สำหรับ ukui สกรีนเซเวอร์ ukui-screensaver/i18n_ts/bo_CN.ts0000664000175000017500000040423415172041106015614 0ustar fengfeng AgreementWindow I know ངས་ཤེས་སོང་། AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry ཡང་བསྐྱར་ཚོད་ལྟ་ UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 གསང་བའི་གསལ་འདེབས། བརྒྱ་ཆ་%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. སྐར་མ་%1འགོར་རྗེས་ཡང་བསྐྱར་ཚོད་ལྟ་བྱས། Please try again in %1 seconds. དུས་ཚོད་སྐར་ཆ་%1འགོར་རྗེས་ཡང་བསྐྱར་ཚོད་ལྟ་བྱེད་རོགས། Account locked permanently. དུས་གཏན་དུ་ཟྭ་བརྒྱབ་པའི་རྩིས་ཐོ། Verify face recognition or enter password to unlock 验证人脸识别或输入密码解锁 Guest མགྲོན་པོ། Press fingerprint or enter password to unlock 按压指纹或输入密码解锁 Verify voiceprint or enter password to unlock 验证声纹或输入密码解锁 Verify finger vein or enter password to unlock 验证指静脉或输入密码解锁 Verify iris or enter password to unlock 验证虹膜或输入密码解锁 Enter username སྤྱོད་མཁན་གྱི་མིང་ནང་འཇུག་བྱེད Facial recognition to unlock གདོང་རིས་ངོས་བཟུང་ནས་ཁ་འབྱེད། Fingerprint recognition to unlock སོར་མོའི་རིས་ངོས་འཛིན་གྱིས་སྒོ་རྒྱག་འབྱེད་པ། Voiceprint recognition to unlock སྒྲ་རིས་ངོས་བཟུང་ནས་ཁ་འབྱེད། Finger vein recognition to unlock འཛུབ་མོའི་སྡོད་རྩ་ངོས་བཟུང་ནས་ཁ་འབྱེད། Iris recognition to unlock འཇའ་སྐྱི་ངོས་བཟུང་ནས་ཁ་འབྱེད། Use the bound wechat scanning code to unlock 微信扫码解锁 wechat འཕྲིན་ཕྲན། Use the bound %1 scanning code to unlock བརྒྱ་ཆ་%1ལ་ཟྭ་བརྒྱབ་པ་རེད། The authentication has been cancelled! དཔང་དངོས་བདེན་པ་ཡིན་པའི་ར་སྤྲོད་མེད་པར་ Password: གསང་གྲངས་ནི། Input Password ནང་འཇུག་གི་གསང་གྲངས། Username སྤྱོད་མཁན་མིང་།: User name input error! སྤྱོད་མཁན་གྱི་མིང་ནང་འཇུག་ནོར་འཁྲུལ་བྱུང་བ login ཐོ་འགོད། Failed to verify %1, please enter password to unlock %1ལ་ཞིབ་བཤེར་བྱེད་མ་ཐུབ་ན། གསང་གྲངས་ནང་འཇུག་བྱས་ནས་ཟྭ་རྒྱག་རོགས། Unable to verify %1, please enter password to unlock %1ལ་ཞིབ་བཤེར་བྱེད་ཐབས་བྲལ་བ་དང་། གསང་གྲངས་ནང་འཇུག་བྱས་ནས་ཟྭ་རྒྱག་རོགས། Abnormal network རྒྱུན་ལྡན་མིན་པའི་དྲ་ Acquisition failure ཕམ་ཉེས་བྱུང་བ་རེད། Face recognition waiting time out, please click refresh or enter the password to unlock. མིའི་གདོང་ལ་དབྱེ་འབྱེད་བྱེད་པར་རེ་སྒུག་བྱེད་དུས་གསང་གྲངས་ཀྱི་སྒོ་ལྕགས་རྒྱག་རོགས། FingerPrint མཛུབ་རིས། FingerVein མཛུབ་སྡོད་རྩ། Iris འཇའ་སྐྱི། Face མིའི་གདོང་ངོས་འཛིན། VoicePrint སྒྲ་རིམ། Ukey བདེ་འཇགས་གསང་ལྡེ། QRCode དོན་ཚན་གཉིས་པ། fingerprint མཛུབ་རིས། fingervein སྡོད་རྩ་ཞེས་པ་ནི་སྡོད་རྩར iris འཇའ་སྐྱི། face ངོ་གདོང་། voiceprint སྒྲ་གདངས་ཀྱི་རི་མོ། Two-Factor OTP Dynamic Password Authentication རྒྱུ་རྐྱེན་གཉིས་OTPཡི་འགུལ་རྣམ་གྱི་བཀའ་རྒྱར་བདེན་དཔང་ར་སྤྲོད་བྱས། Two-Factor Security Key Authentication རྒྱུ་རྐྱེན་གཉིས་བདེ་འཇགས་ཀྱི་ལྡེ་མིག་བདེན་དཔང་ར་སྤྲོད་བྱས། Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty གསང་གྲངས་སྟོང་པ་ཡིན་མི་སྲིད། Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities ཚོད་ལྟས་ར་སྤྲོད་%1ཕམ་ཉེས་བྱུང་།ཁྱེད་རང་ད་དུང་ཡོད།%2ཚོད་ལྟ་བྱེད་པའི་གོ་སྐབས་ཤིག་རེད། Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again བདེན་དཔང་ར་སྤྲོད་བྱེད་མ་ཐུབ་ན། ཡང་བསྐྱར་ཚོད་ལྟ་ཞིག Use the bound wechat scanning code or enter the password to unlock 使用绑定的微信扫码或输入密码解锁 Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port བདེ་འཇགས་ཀྱི་གསང་བའི་ལྡེ་མིག་དེ་USBཡི་སྣེ་འདྲེན་དུ་འཇུག་རོགས། Password 密码 Login ཐོ་འགོད། Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... གློག་གསོག་བཞིན་པ། fully charged གློག་གསོག་ཚད་ཚང་མ་ PowerMode གློག་ཁུངས་དཔེ་དབྱིབས། BatteryMode གློག་གཡིས་དཔེ་དབྱིབས། No battery detected གློག་རྫས་རྙེད་མ་སོང་། BindDialog Close ཁ་རྒྱག Ok ཐག་ཆོད (_O) Cancel དོར་བ། Mfa authentication is on, please bind OTP code རྒྱུད་རིམ་ལྡན་པའི་སྒོ་ནས་རྒྱུ་རྐྱེན་གཉིས་ཀྱི་དཔང་དངོས་བདེན་པ་ཡིན་པའི་ར་སྤྲོད་ཡི་གེ་ཐེངས་གཉིས་གཏན་འཁེལ་གནང་རོགས། Go to Bind སྦྲེལ་དུ་སོང་། Bind གཅུན་སྡོམ་བྱེད་པ། BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel དོར་བ། Confirm ཐག་ཆོད If you do not perform any operation, the system will automatically %1 after %2 seconds. གལ་ཏེ་ཁྱེད་ཀྱིས་བཀོལ་སྤྱོད་གང་ཡང་མ་བྱས་ན། མ་ལག་ནི་%2ཡི་རྗེས་སུ་རང་འགུལ་%1 The following programs prevent restarting, you can click "Cancel" and then close these programs. གཤམ་གསལ་གྱི་བ་རིམ་གྱིས་བསྐྱར་སློང་ལ་བཀཀ་འགོག་བྱས།ཁྱེད་ཀྱིས་“དོར་བ”མནན་ནས་བ་རིམ་འདི་དག་ཁ་རྒྱབ་ཆོག。 The following programs prevent the shutdown, you can click "Cancel" and then close these programs. གཤམ་གསལ་གྱི་བ་རིམ་གྱིས་འཁོར་ཁ་རྒྱག་པ་ལ་བཀཀ་འགོག་བྱས།ཁྱེད་ཀྱིས་“དོར་བ”་མནན་ནས་བ་རིམ་འདི་དག་ཁ་རྒྱབ་ཆོག。 The following programs prevent suspend, you can click "Cancel" and then close these programs. གཤམ་གསལ་གྱི་བ་རིམ་གྱིས་གཉིད་ལ་བཀཀ་འགོག་བྱས།ཁྱེད་ཀྱིས་“དོར་བ”་མནན་ནས་བ་རིམ་འདི་དག་ཁ་རྒྱབ་ཆོག。 The following programs prevent hibernation, you can click "Cancel" and then close these programs. གཤམ་གསལ་གྱི་བ་རིམ་གྱིས་གཉིད་ཡུར་བ་ལ་བཀཀ་འགོག་བྱས།ཁྱེད་ཀྱིས་“དོར་བ”་མནན་ནས་བ་རིམ་འདི་དག་ཁ་རྒྱབ་ཆོག。 The following programs prevent you from logging out, you can click "Cancel" and then close them. གཤམ་གསལ་གྱི་བ་རིམ་གྱིས་འདོར་བ་ལ་བཀཀ་འགོག་བྱས།ཁྱེད་ཀྱིས་“དོར་བ”་མནན་ནས་བ་རིམ་འདི་དག་ཁ་རྒྱབ་ཆོག。 The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. གཤམ་གྱི་གོ་རིམ་གྱིས་བསྐྱར་དུ་སྒོ་ཕྱེ་བར་བཀག་འགོག་བྱས་ན། ཁྱེད་ཀྱིས་"ཕྱིར་འཐེན་"བྱས་ཆོག་པ་དང་། དེ་ནས་གོ་རིམ་དེ་དག་སྒོ་རྒྱག་པའམ་ཡང་ན་"བཙན་ཤེད་ཀྱིས་སྒོ་རྒྱག་རྒྱུ་གཏན་འཁེལ་བྱས་ཆོག་པ་མ་ཟད་མུ་མཐུད་ནས་སྤེལ་ The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. གཤམ་གསལ་གོ་རིམ་གྱིས་སྒོ་རྒྱག་རྒྱུ་བཀག་འགོག་བྱེད་སྐབས་ཁྱེད་ཀྱིས་"མེད་པར་བཟོས་"རྗེས་གོ་རིམ་དེ་དག་སྒོ་རྒྱག་པའམ་ཡང་ན་"བཙན་ཤེད་ཀྱིས་སྒོ་རྒྱག་རྒྱུ་གཏན་འཁེལ་བྱས་ཆོག་པ་མ་ཟད་མུ་མཐུད་ནས་སྤེལ་ཆོག The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. གཤམ་གྱི་གོ་རིམ་གྱིས་ཐོ་ཁོངས་ནས་སུབ་པར་བཀག་འགོག་བྱས་ན། ཁྱེད་ཀྱིས་"མེད་པར་བཟོ་"བའི་"རྗེས་སུ་གོ་རིམ་དེ་དག་སྒོ་རྒྱག་པའམ་ཡང་ན་"བཙན་ཤེད་ཀྱིས་སྒོ་རྒྱག་རྒྱུ་གཏན་འཁེལ་བྱས་ཆོག་པ་མ་ཟད་མུ་མཐུད་ནས་སྤེལ་ཆོག shut down ལས་མཚམས་འཇོག restart ཡང་བསྐྱར་འགོ་འཛུགས་ CharsWidget More དེ་བས་མང་བ་མངོན་ཡོད། ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them པར་བཤེར་འཇལ་བྱེད་མ་ཐུབ།ཡང་བསྐྱར་འདེམ་རོགས། DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 IconEdit OK ཁ་འབྱེད་པ། InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state དཔྱང་འཕྱོའི་རྣམ་པ། Close ཁ་རྒྱག Welt status འགྲམ་སྦྱོར་རྣམ་པ། KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LightDMHelper failed to start session. ཚོགས་འདུ་འགོ་འཛུགས་མ་ཐུབ་པ་རེད། Login ཐོ་འགོད། Guest མགྲོན་པོ། LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser སྤྱོད་མཁན་བརྗེ་དགོས། Power སྟོབས་ཤུགས། VirtualKeyboard རྟོག་བཟོའི་མཐེབ་གཞོང་། Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession གཏམ་གླེང་བརྗེ་བ། Power Information གློག་ཁོངས་ཆ་འཕྲིན། Network དྲ་རྒྱ། system-monitor མ་ལག་ལྟ་ཞིབ་ཚད་ལེན Reset Pwd གསང་གྲངས་གསར་སྒྲིག %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". %1རྩིས་འཁོར་དེ་ཉིད་ཐོ་འགོད་བྱས་ཟིན་པའི་སྤྱད་མཁན་གྱིས་ད་དུང་གསོག་ཉར་བྱས་མེད་པའི་ནང་དོན་བསླངས་ནས་མུ་མཐུད་དུ་ལག་བསྟར་བྱེད་དགོས་པ་དང་། "དངོས་སུ་ཁས་ལེན་"བྱེད་རོགས། Restart ཡང་བསྐྱར་འགོ་འཛུགས་ Multiple users are logged in at the same time.Are you sure you want to %1 this system? དུས་མཉམ་དུ་སྤྱོད་མཁན་མང་པོ་ཐོ་འགོད་ཀྱི་རྣམ་པར་གནས།ཁྱེད་ལ་%1དགོས་པ་ཐག་ཆོད་ཡིན་ནམ།? Reboot 重启 PowerOff 关机 Shut Down ལས་མཚམས་འཇོག LoginOptionsWidget Login Options ཐོ་འགོད་ཀྱི་བསལ་འདེམས་ཀྱི་དབང་ཆ། Password ཕར་འགྲོ་ཚུར་འོང་བྱེད་མཁན། Other དེ་མིན། FingerPrint མཛུབ་རིས། FingerVein མཛུབ་སྡོད་རྩ། Iris འཇའ་སྐྱི། Face མིའི་གདོང་ངོས་འཛིན། VoicePrint སྒྲ་རིམ། Ukey བདེ་འཇགས་གསང་ལྡེ། QRCode དོན་ཚན་གཉིས་པ། Wechat 微信 Identify device removed! དབྱེ་འབྱེད་སྒྲིག་ཆས་མེད་པར་བཟོ་དགོས། MPRISSaverWidget No music detected གླུ་དབྱངས་བཤེར་འཇལ་མ་ཐུབ། Unknown སྔོན་ཤེས་མེད་པ། MPRISWidget Unknown 未知 MusicFileInformation Unknown singer མ་རྟོགས་པའི་གླུ་བ། Unknown album མ་ཤེས་པའི་ཆེད་སྒྲིག། MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN སྐུད་ཡོད་དྲ་བ། WLAN སྐུད་མེད་ཁོངས་ཆུང་དྲ་རྒྱ། NumbersWidget Return ཕྱིར་ལོག OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters གསང་ཨང་ %1 ཡིག་རྟགས་ལས་ཉུང་བ། The password contains less than %1 character classes གསང་ཨང་ནང་དུ་འདུས་པའི་ཡིག་རྟགས་རིགས་%1 ་ལས་ཉུང་བ། The password is the same as the old one གསང་ཨང་གསར་པ་དང་གསང་ཨང་རྙིང་པ་གཅིག་པ་རེད། The password contains the user name in some form གསང་ཨང་ནང་དུ་རྣམ་པ་ག་གེ་མོའི་སྤྱོད་མཁན་གྱི་མིང་འདུས་ཡོད། The password differs with case changes only གསང་ཨང་ནང་དུ་ཡིག་འབྲི་ཆེ་ཆུང་བསྒྱུར་བཅོས་ཁོ་ན་འདུས་ཡོད། The password is too similar to the old one གསང་ཨང་གསར་པ་དང་གསང་ཨང་རྙིང་པ་འདྲ་དྲགས་པ། The password is a palindrome གསང་ཨང་ནི་ཀུན་འཁོར་ཡིན་པ། PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PowerListWidget Hibernate མངལ་གནས་སུ་སྦས་པ། Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left གློག་ཀླད་ཀྱི་སྒོ་བརྒྱབ་ནས་བཀོལ་སྤྱོད་བྱེད་སྐབས་ཐོག་མཐའ་བར་གསུམ་དུ་རྣམ་པ་རྒྱུན་འཁྱོངས་བྱེད་ཐུབ། གློག་ཀླད་ཀྱི་ཁ་ཕྱེ་དུས་ཁྱོད་དང་ཁ་བྲལ་བའི་རྣམ་པ་སླར་གསོ་བྱེད་ཐུབ། Suspend ལས་མཚམས་བཞག་པ། The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Restart ཡང་བསྐྱར་འགོ་འཛུགས་ Close all apps, and then restart your computer ཉེར་སྤྱོད་ཚང་མའི་ཁ་རྒྱབ་ནས་གློག་ཀླད་བསྐྱར་སློང་བྱས་རོགས། Shut Down ལས་མཚམས་འཇོག The current user logs out of the system, terminates the session, and returns to the login page མིག་སྔའི་སྤྱོད་མཁན་རྒྱུད་ཁོངས་ལས་དོར་འདུག དེའི་གཏམ་གླེང་མཇུག་བསྡུས་ནས་ཐོ་འགོད་མཚམས་ངོས་ལ་ལོག Logout 注销 Power Off 关机 Close all apps, and then shut down your computer བཀོལ་སྤྱོད་ཡོད་ཚད་སྒོ་བརྒྱབ་ནས་གློག་ཀླད་སྒོ་རྒྱག་དགོས། SwitchUser སྤྱོད་མཁན་བརྗེ་དགོས། LockScreen བརྙན་ཡོལ་བ་རྒྱག Log Out དོར་བ The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off གློག་ཀླད་ཀྱི་ཁ་ཕྱེ་བའི་རྣམ་པ་རྒྱུན་འཁྱོངས་བྱས་མོད། འོན་ཀྱང་གློག་ཟད་ཚད་ཅུང་ཉུང་། ཉེར་སྤྱོད་ཚོགས་འདུའི་ཐོག་ཁ་ཕྱེ་ནས་མགྱོགས་མྱུར་ངང་གློག་ཀླད་དཀྲོགས་ཏེ་ཁྱོད་དང་ཁ་བྲལ་བའི་རྣམ་པ་སླར་གསོ་བྱེད་ཐུབ། UpgradeThenRestart གསར་སྒྱུར་བྱས་རྗེས་བསྐྱར་སློང་བྱས། Close all apps, turn off your computer, and then turn your computer back on སྤྱོད་སྒོ་ཡོད་ཚད་སྒོ་བརྒྱབ་ནས་གློག་ཀླད་སྒོ་རྒྱག་པ་དང་། དེ་ནས་ཡང་བསྐྱར་གློག་ཀླད་ཀྱི་ཁ་ཕྱེ་བ་རེད། UpgradeThenShutdown རིམ་པ་འཕར་བའི་དུས་ཚོད། PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. བརྙན་ཡོལ་སྲུང་སྐྱོབ་སྒྲིག་འགོད་གྲུང་སློང་ཟིན The screensaver is inactive. བརྙན་ཡོལ་སྲུང་སྐྱོབ་སྒྲིག་འགོད་གྲུབ་སློང་བྱས་མེད། FingerPrint མཛུབ་རིས། FingerVein མཛུབ་སྡོད་རྩ། Iris འཇའ་སྐྱི། Face མིའི་གདོང་ངོས་འཛིན། VoicePrint སྒྲ་རིམ། Ukey བདེ་འཇགས་གསང་ལྡེ། QRCode དོན་ཚན་གཉིས་པ། ResetPwdWidget Reset your password 重置密码 Security Question བདེ་འཇགས་ཀྱི་གནད་དོན། Kylin ID ཆི་ལིནID Enter new password གསང་གྲངས་གསར་བ་ནང་འཇུག་བྱས Confirm your password གསང་གྲངས་ཐག་ཆོད Inconsistency with pwd གསང་གྲངས་གསར་རྙིང་གཅིག་མཐུན་མ་བྱུང་། Confirm ཐག་ཆོད། Cancel དོར་བ། Contains illegal characters! དེའི་ནང་དུ་ཁྲིམས་འགལ་གྱི་མི་སྣ་འདུས་ཡོད། The service is abnormal ཞབས་ཞུ་རྒྱུན་ལྡན་མིན་པ། The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) འཕྲིན་ཐུང་ཚོད་ལྟས་ར་སྤྲོད་ཨང་གྲངས་དེ་མཇུག་མཐའི་ཨང་གྲངས་1བར་བསྐུར་ཟིན་པའི་ལག་ཐོགས་ཁ་པར།(སྐར་མ་5ནང་དུ་རྩིས་འགྲོ་ཡོད། ) Failed to get the verification code ཚོད་ལྟས་ར་སྤྲོད་ཀྱི་ཨང་གྲངས་ཐོབ་པ་ཕམ་སོང་ The account does not exist, please re-enter it རྩིས་ཐོ་མེད་པས་བསྐྱར་དུ་ནང་འདྲེན་གནང་རོགས། The account does not exist, please complete the registration first རྩིས་ཐོའི་ཨང་གྲངས་མེད་ན་སྔོན་ལ་ཐོ་འགོད་བྱེད་རོགས། The user is already locked, contact the administrator to unlock it སྤྱོད་མཁན་དེས་ཟྭ་བརྒྱབ་ཟིན་པས་དོ་དམ་པར་འབྲེལ་གཏུག་བྱས་ནས་གཏན་ཁེལ་བྱེད་རོགས། If there are too many login errors, please try again in 5 minutes ནོར་འཁྲུལ་གྱི་ཐེངས་གྲངས་མང་དྲགས་པས་སྐར་མ་ལྔའི་རྗེས་སུ་ཡང་བསྐྱར་ཚོད་ལྟ་བྱེད་རོགས། The SMS verification code is incorrect or has expired འཕྲིན་ཐུང་གིས་ཨང་གྲངས་ནོར་བ་འམ་ཡང་ན་དུས་ཚོད་ཡོལ་ཟིན་པ་ར་སྤྲོད Wrong username or password སྤྱོད་མཁན་གྱི་མིང་ངམ་གསང་གྲངས་ནོར་འཁྲུལ་རེད། Password decryption error གསང་བའི་གསང་བ་གྲོལ་བ་ནོར་སོང་། Verify Kylin ID Reset Password ཚོད་ལྟས་ར་སྤྲད ཆི་ལིནIDགྱིས་གསང་གྲངས་བསྐྱར་དུ་བཀོད་ཡོད། Answer the security questions and reset your password བདེ་འཇགས་ཀྱི་གནད་དོན་ལ་ལན་འདེབས་རྒྱུ་དེ་གསང་གྲངས་བསྐྱར་ One or more wrong answers གཅིག་གམ་དུ་མ་ནོར་བའི་ལན། S: tablet device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist འདྲ་པར་མེད། Not connected to a network དྲ་རྒྱ་སྦྲལ་མཐུད་མ་བྱུང་། The weather update failed གནམ་གཤིས་གསར་སྒྱར་ལ་ཕམ་ཁ་བྱུང་། Smart Saver བློ་གྲོས་ཡོལ་སྲུང་། Weather གནམ་གཤིས་ Music གླུ་དབྱངས། Photo Albun འདྲ་པར Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View དཔེ་རིས་ལ་བལྟ་བ། SleepTime You have rested: 您已休息: You have rested ཁྱེད་ཀྱི་ངལ་གསོ་ཟིན། SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer ལན་ནང་འཇུག UserListWidget Login ཐོ་འགོད། Guest མགྲོན་པོ། Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account ཆི་ལིནIDཡི་ཐོ་ཁོངས་ནང་འཇུག་བྱས་ཡོད། Input password གསང་ཨང་ནང་འཇུག་རོགས་། Enter the verification code ནང་འདྲེན་ཚོད་ལྟས་ར་སྤྲོད་ཀྱི་ཨང་གྲངས།། Get verification code ཚོད་ལྟས་ར་སྤྲོད་ཀྱི་ཨང་གྲངས་ཐོབ་པ་ Reacquire(%1) བསྐྱར་དུ་བསྡུ་ལེན་བྱ་དགོས། (%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 WeatherWidget Not connected to a network 网络未连接 delay how long to show lock བརྙན་ཡོལ་ཁ་རྒྱབ་པ་ནམ་ལ་འཆར། has-lock if show lock བརྙན་ཡོལ་ཁ་རྒྱག་གམ main Start command for the ukui ScreenSaver. བརྙན་ཡོལ་ཁ་རྒྱག་པ་བཀའ་ལ་སློང་ lock the screen immediately ལམ་སང་བརྙན་ཡོལ་བ་རྒྱོབ། query the status of the screen saver བརྙན་ཡོལ་ཁ་རྒྱབ་པའི་རྣམ་པ་ཐོབ་ཐབས་བྱེད unlock the screen saver བརྙན་ཡོལ་གྱི་ཁ་ཕྱེ show the screensaver ཡོལ་སྲུང་འཆར་བ show the session tools གཏམ་གླེང་དོ་དམ་འཆར་བ show the app block window ཉེར་སྤྱོད་འགག་ནས་འཁོར་ཁ་རྒྱག་པའི་མཚམས་ངོས་འཆར། show the multiUsers block window སྤྱོད་མཁན་འགག་ནས་འཁོར་ཁ་རྒྱག་པའི་མཚམས་ངོས་འཆར། Backend for the ukui ScreenSaver. ukui-screensaver/i18n_ts/es.ts0000664000175000017500000045254115172041106015247 0ustar fengfeng AgreementWindow I know Lo sé AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry Reintentar UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. Inténtelo de nuevo en %1 minutos. Please try again in %1 seconds. Inténtelo de nuevo en %1 segundos. Account locked permanently. Cuenta bloqueada permanentemente. Verify face recognition or enter password to unlock Verifique el reconocimiento facial o ingrese la contraseña para desbloquear Guest 游客 Press fingerprint or enter password to unlock Presione la huella dactilar o ingrese la contraseña para desbloquear Verify voiceprint or enter password to unlock Verifica la huella de voz o ingresa la contraseña para desbloquear Verify finger vein or enter password to unlock Verifique la vena del dedo o ingrese la contraseña para desbloquear Verify iris or enter password to unlock Verifique el iris o ingrese la contraseña para desbloquear Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock The authentication has been cancelled! Password: Input Password Contraseña de entrada Username Nombre de usuario User name input error! login Failed to verify %1, please enter password to unlock No se pudo verificar %1, ingrese la contraseña para desbloquear Unable to verify %1, please enter password to unlock No se puede verificar %1, ingrese la contraseña para desbloquear Abnormal network Red anormal Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey QRCode QRCode fingerprint fingervein iris face voiceprint Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty La contraseña no puede estar vacía Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities No se pudo verificar %1, todavía tiene %2 oportunidades de verificación Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again Error de autenticación, inténtelo de nuevo Use the bound wechat scanning code or enter the password to unlock Use el código de escaneo de wechat vinculado o ingrese la contraseña para desbloquear Enter the ukey password Introduzca la contraseña de ukey Insert the ukey into the USB port Inserte la ukey en el puerto USB Password Contraseña Login Iniciar sesión Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... fully charged PowerMode BatteryMode BindDialog Close Ok De acuerdo Cancel Cancelar Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: Dispositivo actual: Identify failed, Please retry. Identifique el error, vuelva a intentarlo. BiometricDevicesWidget Please select the biometric device Seleccione el dispositivo biométrico Device type: Tipo de dispositivo: Device name: Nombre del dispositivo: OK De acuerdo BlockWidget Cancel Cancelar Confirm If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. The following programs prevent suspend, you can click "Cancel" and then close these programs. The following programs prevent hibernation, you can click "Cancel" and then close these programs. The following programs prevent you from logging out, you can click "Cancel" and then close them. shut down restart The following program is running to prevent the system from reboot! ¡El siguiente programa se está ejecutando para evitar que el sistema se reinicie! The following program is running to prevent the system from shutting down! ¡El siguiente programa se está ejecutando para evitar que el sistema se apague! The following program is running to prevent the system from suspend! ¡El siguiente programa se está ejecutando para evitar que el sistema se suspenda! The following program is running to prevent the system from hibernate! ¡El siguiente programa se está ejecutando para evitar que el sistema hiberne! CharsMoreWidget &&?! &&?! CharsWidget More Más ABC ABECEDARIO 123 123 ConfForm edit network Editar red LAN name: Nombre de la LAN: Method: Método: Address: Dirección: Netmask: Máscara de red: Gateway: Entrada: DNS 1: DNS 1: DNS 2: DNS 2: Edit Conn Editar Conn Auto(DHCP) Automático (DHCP) Manual Manual Cancel Cancelar Save Salvar Ok De acuerdo Can not create new wired network for without wired card No se puede crear una nueva red cableada sin tarjeta cableada New network already created Nueva red ya creada New network settings already finished La nueva configuración de red ya ha finalizado Edit Network Editar red Add Wired Network Agregar red cableada create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective Nuevos ajustes ya efectivos There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint Huellas FingerVein FingerVein Iris Iris Face Cara VoicePrint Huella de voz Ukey Ukey QRCode QRCode DigitalAuthDialog LoginByUEdu LoginByUEdu now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? ¿ReiniciarPWD? SetNewUEduPWD SetNewUEduPWD ConfirmNewUEduPWD ConfirmNewUEduPWD The two password entries are inconsistent, please reset Las dos entradas de contraseña son inconsistentes, restablezca Password entered incorrectly, please try again Contraseña introducida incorrectamente, inténtelo de nuevo clear claro DlgConnHidWifi Add Hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Wi-Fi name Nombre de Wi-Fi Wi-Fi security Seguridad Wi-Fi Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Username Nombre de usuario Password Contraseña Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Authentication Autenticación Anonymous identity Identidad anónima Allow automatic PAC pro_visioning Permitir pro_visioning PAC automáticos PAC file Archivo PAC Inner authentication Autenticación interna Username Nombre de usuario Password Contraseña Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise Tunneled TLS TLS tunelizado Protected EAP (PEAP) EAP protegido (PEAP) Anonymous Anónimo Authenticated Autenticado Both Ambos DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Authentication Autenticación Username Nombre de usuario Password Contraseña Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise Tunneled TLS TLS tunelizado Protected EAP (PEAP) EAP protegido (PEAP) DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Authentication Autenticación Anonymous identity Identidad anónima Domain Dominio CA certificate Certificado de CA CA certificate password Contraseña del certificado de CA No CA certificate is required No se requiere ningún certificado de CA PEAP version Versión PEAP Inner authentication Autenticación interna Username Nombre de usuario Password Contraseña Cancel Cancelar Connect Conectar None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise Tunneled TLS TLS tunelizado Protected EAP (PEAP) EAP protegido (PEAP) Choose from file Elegir del archivo Automatic Automático Version 0 Versión 0 Version 1 Versión 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Authentication Autenticación Username Nombre de usuario Password Contraseña Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise Tunneled TLS TLS tunelizado Protected EAP (PEAP) EAP protegido (PEAP) DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Authentication Autenticación Identity Identidad Domain Dominio CA certificate Certificado de CA CA certificate password Contraseña del certificado de CA No CA certificate is required No se requiere ningún certificado de CA User certificate Certificado de usuario User certificate password Contraseña del certificado de usuario User private key Clave privada de usuario User key password Clave de usuario contraseña Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise Tunneled TLS TLS tunelizado Protected EAP (PEAP) EAP protegido (PEAP) Choose from file Elegir del archivo DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Authentication Autenticación Anonymous identity Identidad anónima Domain Dominio CA certificate Certificado de CA CA certificate password Contraseña del certificado de CA No CA certificate is required No se requiere ningún certificado de CA Inner authentication Autenticación interna Username Nombre de usuario Password Contraseña Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise Tunneled TLS TLS tunelizado Protected EAP (PEAP) EAP protegido (PEAP) Choose from file Elegir del archivo DlgConnHidWifiWep Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Key Llave WEP index Índice WEP Authentication Autenticación Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) Clave WEP de 40/128 bits (hexadecimal o ASCII) WEP 128-bit Passphrase Frase de contraseña WEP de 128 bits Dynamic WEP (802.1X) WEP dinámico (802.1X) WPA & WPA2 Enterprise WPA y WPA2 Enterprise 1(default) 1(predeterminado) Open System Sistema abierto Shared Key Clave compartida DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Add Hidden Wi-Fi Agregar Wi-Fi oculto Connection Conexión Wi-Fi name Nombre de Wi-Fi Wi-Fi security Seguridad Wi-Fi Password Contraseña Cancel Cancelar Connect Conectar C_reate… C_reate… None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Dialog Diálogo Create Hotspot Crear zona interactiva Network name Nombre de la red Wi-Fi security Seguridad Wi-Fi Password Contraseña Cancel Cancelar Ok De acuerdo None Ninguno WPA & WPA2 Personal WPA y WPA2 Personal EngineDevice yes no Yes No %1% available, charged Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK De acuerdo InputInfos Service exception... Excepción de servicio... Invaild parameters... Parámetros de Invaild... Unknown fault:%1 Fallo desconocido:%1 Recapture(60s) Recaptura (años 60) Recapture(%1s) Recaptura(%1s) Get code Obtener código KBTitle Suspended state Close Welt status KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm Kylin-NM LAN LAN Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced Avanzado Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot Punto caliente FlyMode Modo de vuelo Show MainWindow 显示网络连接界面 Inactivated LAN LAN inactivada Inactivated WLAN 未激活 Other WLAN Otras WLAN WLAN WLAN Show KylinNM Mostrar KylinNM No wireless card detected No se ha detectado ninguna tarjeta inalámbrica Activated LAN LAN activada Activated WLAN WLAN activada Not connected No conectado Disconnected Desconectado No Other Wired Network Scheme Ningún otro esquema de red cableada Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme Ningún otro esquema de red inalámbrica Wired net is disconnected La red cableada está desconectada Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card Confirme su contraseña de Wi-Fi o la tarjeta inalámbrica utilizable Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list No hay red utilizable en la lista NetOn, NetOn, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success Éxito de Conn Ethernet Conn Ethernet Fail Falla de Ethernet de Conn Conn Wifi Success Éxito de Conn Wifi LettersWidget &&?! &&?! 123 123 Ctrl Ctrl Alt Alt LightDMHelper failed to start session. Login Iniciar sesión Guest 游客 LockWidget Form Forma Date Fecha Time Hora Guest 游客 SwitchUser SwitchUser (Usuario) Power Poder VirtualKeyboard Teclado virtual Multiple users are logged in at the same time.Are you sure you want to reboot this system? Varios usuarios han iniciado sesión al mismo tiempo. ¿Estás seguro de que quieres reiniciar este sistema? LAN LAN WLAN WLAN system-monitor Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Multiple users are logged in at the same time.Are you sure you want to %1 this system? Restart Reanudar SwitchSession Power Information Network 网络 Shut Down Apaga LoginOptionsWidget Login Options Opciones de inicio de sesión Password Contraseña Other Otro FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey QRCode QRCode Wechat 微信 Identify device removed! ¡Identifique el dispositivo eliminado! MPRISSaverWidget No music detected Unknown MusicFileInformation Unknown singer Unknown album MyLineEdit Verification code Código de verificación MyNetworkWidget LAN LAN WLAN WLAN NumbersWidget &&?! &&?! Return Devolución OneConnForm Form Forma Automatically join the network Unirse automáticamente a la red Input password 输入密码 Config 设置 Connect Conectar Disconnect Desconectar Input Password... Ingrese la contraseña... Connect to Hidden Wi-Fi Network Conéctese a una red Wi-Fi oculta Signal: Señal: Public 开放 Safe 安全 Rate 速率 None Ninguno WiFi Security: Seguridad WiFi: MAC: MAC: Conn Wifi Failed Falló Conn Wifi OneLancForm Form Forma Config 设置 Connect Conectar Disconnect Desconectar No Configuration Sin configuración IPv4: IPv4: IPv6: IPv6: BandWidth: Ancho de banda: MAC: MAC: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhoneAuthWidget Verification by phoneNum Verificación por teléfonoNum 「 Use bound Phone number to verification 」 「 Use el número de teléfono vinculado para la verificación 」 「 Use SMS to verification 」 「Usa SMS para verificar」 commit cometer Network not connected~ Red no conectada~ Network unavailable~ Red no disponible~ Verification Code invalid! ¡El código de verificación no es válido! Verification Code incorrect.Please retry! Código de verificación incorrecto. ¡Por favor, vuelva a intentarlo! Failed time over limit!Retry after 1 hour! ¡Tiempo fallido por encima del límite! ¡Vuelva a intentarlo después de 1 hora! verifaction failed! ¡La verificación falló! PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate Hibernar Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Apague su computadora, pero la aplicación permanece abierta. Cuando se enciende la computadora, se puede restaurar al estado en el que la dejó Suspend Suspender The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off El ordenador permanece encendido, pero consume menos energía. La aplicación permanece abierta y puede despertarse rápidamente y volver a donde la dejó The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off Restart Reanudar Close all apps, and then restart your computer Shut Down Apaga Log Out Cerrar sesión The current user logs out of the system, terminates the session, and returns to the login page SwitchUser LockScreen UpgradeThenRestart UpgradeThenShutdown Power Off Apagar Close all apps, and then shut down your computer Cierre todas las aplicaciones y, a continuación, apague el equipo Close all apps, turn off your computer, and then turn your computer back on Cierre todas las aplicaciones, apague el equipo y, a continuación, vuelva a encenderlo PowerManager lock cerradura SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out Cerrar sesión Restart Reanudar Power Off Apagar Close all apps, turn off your computer, and then turn your computer back on Cierre todas las aplicaciones, apague el equipo y, a continuación, vuelva a encenderlo Close all apps, and then shut down your computer Cierre todas las aplicaciones y, a continuación, apague el equipo Shut Down Apaga Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Apague su computadora, pero la aplicación permanece abierta. Cuando se enciende la computadora, se puede restaurar al estado en el que la dejó Hibernate Hibernar The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off El ordenador permanece encendido, pero consume menos energía. La aplicación permanece abierta y puede despertarse rápidamente y volver a donde la dejó Suspend Suspender Sleep 休眠 QObject The screensaver is active. El protector de pantalla está activo. The screensaver is inactive. El protector de pantalla está inactivo. FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey QRCode QRCode Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm Confirmar Cancel Cancelar Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel Cancelar ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist La imagen no existe Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification Tienes una nueva notificación View Vista Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Cancelar Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested: 您已休息: You have rested Has descansado SureWindow Form Forma TextLabel Etiqueta de texto Cancel Cancelar Confirm Confirmar Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! ¡El siguiente programa se está ejecutando para evitar que el sistema se suspenda! The following program is running to prevent the system from hibernate! ¡El siguiente programa se está ejecutando para evitar que el sistema hiberne! The following program is running to prevent the system from shutting down! ¡El siguiente programa se está ejecutando para evitar que el sistema se apague! The following program is running to prevent the system from reboot! ¡El siguiente programa se está ejecutando para evitar que el sistema se reinicie! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD uEduPWD Wechat Wechat TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old La nueva contraseña es la misma que la anterior Reset password error:%1 Error de restablecimiento de contraseña:%1 Please scan by correct WeChat Por favor, escanee por WeChat correcto Cancel Cancelar Back Atrás Skip Saltarse TalksWidget Enter your answer UserListWidget Login Iniciar sesión Guest 游客 Utils kylin network applet desktop message Mensaje de escritorio del applet de red Kylin VerificationWidget Please scan by bound WeChat Escanee por WeChat enlazado VerifyKyidWidget Enter your Kylin ID account Input password 输入密码 Enter the verification code Get verification code Reacquire(%1) VerticalVerificationWidget Please scan by bound WeChat Escanee por WeChat enlazado WeChatAuthDialog Login by wechat Iniciar sesión por wechat Verification by wechat Verificación por wechat 「 Use registered WeChat account to login 」 「 Use una cuenta registrada de WeChat para iniciar sesión」 「 Use bound WeChat account to verification 」 「Usar la cuenta de WeChat vinculada para la verificación」 Network not connected~ Red no conectada~ Scan code successfully Escanear el código con éxito Timeout!Try again! ¡Interrupción! ¡Vuelve a intentarlo! Login failed 登录失败 Widget Widget LoadPlugin action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock Cuánto tiempo mostrar el bloqueo has-lock if show lock if show lock main Start command for the ukui ScreenSaver. Comando de inicio para el ukui ScreenSaver. lock the screen immediately Bloquee la pantalla inmediatamente query the status of the screen saver Consultar el estado del protector de pantalla unlock the screen saver Desbloquear el protector de pantalla show the screensaver Mostrar el protector de pantalla show blank and delay to lock,param:idle/lid/lowpower mostrar en blanco y retardo para bloquear, param:idle/lid/lowpower show the session tools show the switchuser window show the app block window show the multiUsers block window Dialog for the ukui ScreenSaver. Diálogo para el protector de pantalla ukui. activated by session idle signal Activado por la señal de inactividad de la sesión lock the screen and show screensaver immediately Bloquee la pantalla y muestre el protector de pantalla inmediatamente show screensaver immediately Mostrar protector de pantalla inmediatamente show blank screensaver immediately and delay time to show lock Mostrar protector de pantalla en blanco inmediatamente y retrasar el tiempo para mostrar el bloqueo show blank screensaver immediately and if lock Mostrar protector de pantalla en blanco inmediatamente y si se bloquea show switch user window switch to user and show window Screensaver for ukui-screensaver Protector de pantalla para ukui-screensaver show on root window Mostrar en la ventana raíz show on window. mostrar en la ventana. window id ID de ventana Backend for the ukui ScreenSaver. lock the screen by startup switchtouser switch to someone user ukui-screensaver/i18n_ts/ms.ts0000664000175000017500000035236515172041106015262 0ustar fengfeng AgreementWindow I know Saya tahu AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry Cuba semula UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Gesaan Kata laluan: %1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. Sila cuba lagi dalam %1 minit. Please try again in %1 seconds. Sila cuba lagi dalam %1 minit. Account locked permanently. Akaun dikunci selamanya. Verify face recognition or enter password to unlock 验证人脸识别或输入密码解锁 Guest Tetamu Press fingerprint or enter password to unlock 按压指纹或输入密码解锁 Verify voiceprint or enter password to unlock 验证声纹或输入密码解锁 Verify finger vein or enter password to unlock 验证指静脉或输入密码解锁 Verify iris or enter password to unlock 验证虹膜或输入密码解锁 Enter username Masukkan nama pengguna Facial recognition to unlock Pengecaman wajah untuk membuka kunci Fingerprint recognition to unlock Pengecaman ibu jari untuk membuka kunci Voiceprint recognition to unlock Pengecaman suara untuk membuka kunci Finger vein recognition to unlock Pengecaman Vena jari untuk membuka kunci Iris recognition to unlock Pengenalan Iris untuk membuka kunci Use the bound wechat scanning code to unlock 微信扫码解锁 wechat WeChat Use the bound %1 scanning code to unlock Gunakan kod pengimbasan %1 terikat untuk membuka kunci The authentication has been cancelled! Pengesahan telah dibatalkan! Password: Kata laluan: Input Password Masukkan Kata laluan Username Nama pengguna User name input error! Kesalahan Masukan Nama Pengguna! login Log masuk Failed to verify %1, please enter password to unlock Gagal mengesahkan %1, sila masukkan kata laluan untuk membuka kunci Unable to verify %1, please enter password to unlock Tidak dapat mengesahkan %1, sila masukkan kata laluan untuk membuka kunci Abnormal network Rangkaian Tidak Normal Acquisition failure Pemerolehan gagal Face recognition waiting time out, please click refresh or enter the password to unlock. Masa tunggu pengenalan wajah tamat, sila klik muat semula atau masukkan kata laluan untuk membuka kunci. FingerPrint Cap Jari FingerVein Vena Jari Iris Iris Face Muka VoicePrint Cetakan Suara Ukey Kunci USB QRCode Kod QR fingerprint Cap Jari fingervein Vena Jari iris Iris face Muka voiceprint Cetakan Suara Two-Factor OTP Dynamic Password Authentication Pengesahan Kata laluan Dinamik OTP Dua Faktor Two-Factor Security Key Authentication Pengesahan Kunci Keselamatan Dua Faktor Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty Kata laluan tidak boleh kosong Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities Gagal mengesahkan %1, anda masih mempunyai %2 peluang pengesahan Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again Gagal pengesahan, sila cuba lagi Use the bound wechat scanning code or enter the password to unlock 使用绑定的微信扫码或输入密码解锁 Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port Sisipkan ukey ke dalam port USB Password 密码 Login Log masuk Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... Mengecas... fully charged Bateri penuh PowerMode Mod Kuasa BatteryMode Mod Bateri BindDialog Close Menutup Ok Ok Cancel Batal Mfa authentication is on, please bind OTP code Autentikasi MFA telah dihidupkan, sila ikat kod OTP Go to Bind Pergi untuk Ikat Bind Ikat BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel Batal Confrim Sahkan If you do not perform any operation, the system will automatically %1 after %2 seconds. Jika anda tidak melakukan sebarang operasi, sistem akan automatik %1 selepas %2 saat. The following programs prevent restarting, you can click "Cancel" and then close these programs. Program-program berikut menghalang muat semula, anda boleh klik "Batal" dan kemudian tutup program-program ini. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. Program-program berikut menghalang penutupan, anda boleh klik "Batal" dan kemudian tutup program-program ini. The following programs prevent suspend, you can click "Cancel" and then close these programs. Program-program berikut menghalang penghentian sementara, anda boleh klik "Batal" dan kemudian tutup program-program ini. The following programs prevent hibernation, you can click "Cancel" and then close these programs. Program-program berikut menghalang mod tidur, anda boleh klik "Batal" dan kemudian tutup program-program ini. The following programs prevent you from logging out, you can click "Cancel" and then close them. Program berikut menghalang anda daripada log keluar, anda boleh klik "Batal" dan kemudian menutupnya. The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. Program berikut menghalang anda memulakan semula, anda boleh mengklik "Batal" dan kemudian menutupnya, atau klik 'OK' untuk menutup paksa dan meneruskan. The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. Program berikut menghalang anda memulakan semula, anda boleh klik "Batal" dan kemudian menutupnya, atau klik 'OK' untuk menutup paksa dan meneruskan. The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. Program berikut menghalang anda memulakan semula, anda boleh klik "Batal" dan kemudian menutupnya, atau klik 'OK' untuk menutup paksa dan meneruskan. shut down Tutup restart Mulakan Semula CharsWidget More Lebih banyak ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them Tiada foto dikesan, sila pilih semula DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 IconEdit OK OK InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state Status Terhenti Sementara Close Tutup Welt status Status Wel KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LightDMHelper failed to start session. gagal memulakan sesi. Login Log masuk Guest Tetamu LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser Tukar Pengguna Power Kuasa VirtualKeyboard Papan Kekunci Maya Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession Tukar Sesi Power Information Maklumat Kuasa Network Rangkaian system-monitor pemantau-sistem Reset Pwd Set Semula Kata Laluan %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". %1 boleh menyebabkan pengguna yang telah log masuk ke komputer ini kehilangan kandungan yang belum disimpan,Untuk terus melakukan sila klik "Sahkan". Restart Mulakan semula Multiple users are logged in at the same time.Are you sure you want to %1 this system? Berbilang pengguna log masuk pada masa yang sama. Adakah anda pasti anda mahu %1 sistem ini? Reboot 重启 PowerOff 关机 Shut Down Tutup LoginOptionsWidget Login Options Pilihan Log Masuk Password Kata Laluan Other Lain FingerPrint Cap Jari FingerVein Vena Jari Iris Iris Face Muka VoicePrint Cetakan Suara Ukey Kunci USB QRCode Kod QR Wechat 微信 Identify device removed! Peranti yang dikenal pasti telah dikeluarkan! MPRISSaverWidget No music detected Tiada muzik dikesan Unknown Tidak diketahui MPRISWidget Unknown 未知 MusicFileInformation Unknown singer Penyanyi Tidak Dikenal Unknown album Album Tidak Dikenal MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN LAN WLAN WLAN NumbersWidget Return Kembali OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters Kata laluan lebih pendek daripada %1 aksara The password contains less than %1 character classes Kata laluan mengandungi kurang daripada %1 kategori aksara The password is the same as the old one Kata laluan sama dengan kata laluan lama The password contains the user name in some form Kata laluan mengandungi nama pengguna dalam beberapa bentuk The password differs with case changes only Kata laluan berbeza dengan perubahan kes sahaja The password is too similar to the old one Kata laluan terlalu serupa dengan kata laluan lama The password is a palindrome Kata laluan adalah palindrom PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PowerListWidget Hibernate Hibernasi Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Langkaukan penutupan penuh komputer, dengan mengekalkan aplikasi dalam keadaan aktif di latar belakang. Semasa komputer dihidupkan semula, keadaan operasi sebelumnya boleh dipulihkan automatik Suspend Tangguh The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Restart Mulakan semula Close all apps, and then restart your computer Sila tutup semua aplikasi, selepas itu hidupkan semula komputer anda Shut Down Tutup The current user logs out of the system, terminates the session, and returns to the login page <p>Pengguna semasa log keluar daripada sistem, menamatkan sesi mereka dan kembali ke skrin log masuk </p> Logout 注销 Power Off 关机 Close all apps, and then shut down your computer Sila tutup semua aplikasi yang sedang berjalan, selepas itu hidupkan semula komputer anda SwitchUser Tukar Pengguna LockScreen Skrin Kunci Log Out Log Keluar The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off Komputer kekal hidup tetapi menggunakan tenaga yang lebih sedikit. Aplikasi kekal beroperasi di latar belakang, boleh hidupkan cepat dan kembali ke keadaan sebelumnya anda tinggalkan UpgradeThenRestart Naik Taraf Kemudian Muat Semula Close all apps, turn off your computer, and then turn your computer back on Tutup semua aplikasi, matikan komputer anda, kemudian hidupkan semula komputer anda UpgradeThenShutdown Naik Taraf Kemudian Matikan PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. Penjaga skrin telah diaktifkan. The screensaver is inactive. Penjaga skrin belum diaktifkan. FingerPrint Cap Jari FingerVein Vena Jari Iris Iris Face Muka VoicePrint Cetakan Suara Ukey Kunci USB QRCode Kod QR ResetPwdWidget Reset your password 重置密码 Security Question Soalan keselamatan Kylin ID ID Kylin Enter new password Masukkan kata laluan baru Confirm your password Sahkan kata laluan anda Inconsistency with pwd Kesalahan padanan kata laluan Confirm Sahkan Cancel Batal Contains illegal characters! Mengandungi aksara haram! The service is abnormal Perkhidmatan tidak normal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Kod pengesahan SMS telah dihantar ke telefon bimbit dengan nombor ekor %1 (Sah selama 5 minit) Failed to get the verification code Gagal mendapatkan kod pengesahan The account does not exist, please re-enter it Akaun tidak wujud, sila masukkan semula The account does not exist, please complete the registration first Akaun tidak wujud, sila mendaftarkan terlebih dahulu The user is already locked, contact the administrator to unlock it Pengguna telah terkunci, hubungi pentadbir untuk membuka kuncinya If there are too many login errors, please try again in 5 minutes Jika terdapat terlalu banyak kali gagal log masuk, sila cuba lagi dalam masa 5 minit The SMS verification code is incorrect or has expired Kod pengesahan SMS tidak betul atau telah tamat tempoh Wrong username or password Nama pengguna atau kata laluan salah Password decryption error Kesalahan penyahkodan kata laluan Verify Kylin ID Reset Password Sahkan Tetapkan Semula Kata Laluan Kylin ID Answer the security questions and reset your password Jawab soalan keselamatan dan tetapkan semula kata laluan anda One or more wrong answers Satu atau lebih jawapan yang salah S: tablet device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist Gambar tidak wujud Not connected to a network Tidak disambungkan ke rangkaian The weather update failed Kemas kini cuaca gagal Smart Saver Pelindung Pintar Weather Cuaca Music Muzik Photo Albun Album Foto Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View Lihat SleepTime You have rested: 您已休息: You have rested Anda telah berehat SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer Masukkan jawapan anda UserListWidget Login Log masuk Guest Tetamu Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account Masukkan akaun Kylin ID anda Input password Masukkan Kata laluan Enter the verification code Masukkan kod pengesahan Get verification code Dapatkan kod pengesahan Reacquire(%1) Perolehi semula(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 WeatherWidget Not connected to a network 网络未连接 delay how long to show lock Berapa lama untuk menunjukkan kunci has-lock if show lock Adakah tunjukkan kunci main Start command for the ukui ScreenSaver. Mulakan arahan untuk Pelindung Skrin ukui. lock the screen immediately Segera Kunci Skrin query the status of the screen saver Tanya status pelindung skrin unlock the screen saver Buka kunci pelindung skrin show the screensaver tunjukkan pelindung skrin show the session tools Tunjukkan alatan sesi show the app block window Tunjukkan tetingkap blok aplikasi show the multiUsers block window tunjukkan tetingkap blok berbilang pengguna Backend for the ukui ScreenSaver. Bahagian belakang untuk Pelindung Skrin ukui. ukui-screensaver/i18n_ts/zh_HK.ts0000664000175000017500000034746315172041106015651 0ustar fengfeng AgreementWindow I know 我已知曉 AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry 重試 UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 密碼提示:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. 請%1分鐘后再試 Please try again in %1 seconds. 請%1秒後再試 Account locked permanently. 帳號已被永久鎖定 Verify face recognition or enter password to unlock 验证人脸识别或输入密码解锁 Guest 遊客 Press fingerprint or enter password to unlock 按压指纹或输入密码解锁 Verify voiceprint or enter password to unlock 验证声纹或输入密码解锁 Verify finger vein or enter password to unlock 验证指静脉或输入密码解锁 Verify iris or enter password to unlock 验证虹膜或输入密码解锁 Enter username 輸入登錄使用者名 Facial recognition to unlock 識別人臉解鎖 Fingerprint recognition to unlock 識別指紋解鎖 Voiceprint recognition to unlock 識別聲紋解鎖 Finger vein recognition to unlock 識別指靜脈解鎖 Iris recognition to unlock 識別虹膜解鎖 Use the bound wechat scanning code to unlock 微信扫码解锁 wechat 微信 Use the bound %1 scanning code to unlock %1掃碼解鎖 The authentication has been cancelled! 認證被取消! Password: 密碼: Input Password 輸入密碼 Username 使用者名稱: User name input error! 使用者名輸入錯誤 login 登錄 Failed to verify %1, please enter password to unlock 驗證%1失敗,請輸入密碼解鎖 Unable to verify %1, please enter password to unlock 無法驗證%1,請輸入密碼解鎖 Abnormal network 網路異常 Acquisition failure 獲取失敗 Face recognition waiting time out, please click refresh or enter the password to unlock. 人臉識別等待超時,請點擊刷新或輸入密碼解鎖 FingerPrint 指紋 FingerVein 指靜脈 Iris 虹膜 Face 人臉識別 VoicePrint 聲紋 Ukey 安全金鑰 QRCode 二維碼 fingerprint 指紋 fingervein 指靜脈 iris 虹膜 face 人臉識別 voiceprint 聲紋 Two-Factor OTP Dynamic Password Authentication 雙因數OTP動態口令認證 Two-Factor Security Key Authentication 雙因數安全金鑰認證 Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty 密碼不能為空 Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities 驗證%1失敗,您還有%2次嘗試機會 Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again 認證失敗,請重試 Use the bound wechat scanning code or enter the password to unlock 使用绑定的微信扫码或输入密码解锁 Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port 請將安全金鑰插入USB埠 Password 密码 Login 登錄 Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... 正在充電... fully charged 已充滿電 PowerMode 電源模式 BatteryMode 電池模式 No battery detected 未檢測到電池 BindDialog Close 關閉 Ok 確定 Cancel 取消 Mfa authentication is on, please bind OTP code 系統已啟用雙因數認證,請綁定二次認證口令。 Go to Bind 去綁定 Bind 綁定 OTP added successfully OTP添加成功 BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel 取消 Confirm 確認 If you do not perform any operation, the system will automatically %1 after %2 seconds. 如果您不執行任何操作,系統將在%2秒後自動%1 The following programs prevent restarting, you can click "Cancel" and then close these programs. 以下程式阻止重啟,您可以點擊“取消”然後關閉這些程式。 The following programs prevent the shutdown, you can click "Cancel" and then close these programs. 以下程式阻止關機,您可以點擊“取消”然後關閉這些程式。 The following programs prevent suspend, you can click "Cancel" and then close these programs. 以下程式阻止睡眠,您可以點擊“取消”然後關閉這些程式。 The following programs prevent hibernation, you can click "Cancel" and then close these programs. 以下程式阻止休眠,您可以點擊“取消”然後關閉這些程式。 The following programs prevent you from logging out, you can click "Cancel" and then close them. 以下程式阻止註銷,您可以點擊“取消”然後關閉這些程式。 The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. 以下程式阻止重啟,您可以點擊“取消”然後關閉這些程式,或點擊“確定”強制關閉並繼續。 The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. 以下程式阻止關機,您可以點擊“取消”然後關閉這些程式,或點擊“確定”強制關閉並繼續。 The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. 以下程式阻止註銷,您可以點擊“取消”然後關閉這些程式,或點擊“確定”強制關閉並繼續。 shut down 關機 restart 重啟 CharsWidget More 更多 ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them 未檢測到照片,請重新選擇 DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 IconEdit OK 提交 InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state 懸浮狀態 Close 關閉 Welt status 貼邊狀態 KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LightDMHelper failed to start session. 啟動工作階段失敗。 Login 登錄 Guest 遊客 LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser 切換使用者 Power 電源 VirtualKeyboard 虛擬鍵盤 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession 切換會話 Power Information 電源資訊 Network 網路 system-monitor 系統監視器 Reset Pwd 重置密碼 %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". %1可能導致已登錄此電腦的用戶丟失尚未儲存的內容,仍要執行請點擊“確認”。 Restart 重啟 Multiple users are logged in at the same time.Are you sure you want to %1 this system? 同時有多個使用者處於登錄狀態,你確定要%1嗎? Reboot 重启 PowerOff 关机 Shut Down 關機 LoginOptionsWidget Login Options 登錄選項 Password 密碼 Other 其他 FingerPrint 指紋 FingerVein 指靜脈 Iris 虹膜 Face 人臉識別 VoicePrint 聲紋 Ukey 安全金鑰 QRCode 二維碼 Wechat 微信 Identify device removed! 檢查裝置已移除! MPRISSaverWidget No music detected 未檢測到歌曲 Unknown 未知 MPRISWidget Unknown 未知 MusicFileInformation Unknown singer 未知歌手 Unknown album 未知專輯 MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN 有線網路 WLAN 無線局域網 NumbersWidget Return 返回 OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters 密碼少於%1個字元 The password contains less than %1 character classes 密碼包含少於%1種字元 The password is the same as the old one 新舊密碼一樣 The password contains the user name in some form 密碼包含使用者名 The password differs with case changes only 密碼僅因大小寫更改而異 The password is too similar to the old one 新密碼與舊密碼太過相似 The password is a palindrome 密碼是回文 PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PowerListWidget Hibernate 休眠 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 關閉電腦,但是應用會一直保持打開狀態,當打開電腦時,可以恢復到你離開的狀態 Suspend 睡眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Restart 重啟 Close all apps, and then restart your computer 關閉所有應用,然後重啟電腦 Shut Down 關機 The current user logs out of the system, terminates the session, and returns to the login page 當前使用者從系統中註銷,結束其會話並返回登錄介面 Logout 注销 Power Off 关机 Close all apps, and then shut down your computer 關閉所有應用,然後關閉電腦 SwitchUser 切換使用者 LockScreen 鎖屏 Log Out 註銷 The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off 電腦保持開機狀態,但耗電較少,應用會一直保持打開狀態,可快速喚醒電腦並恢復到你離開的狀態 UpgradeThenRestart 更新並重啟 Close all apps, turn off your computer, and then turn your computer back on 關閉所有應用,關閉電腦,然後重新打開電腦 UpgradeThenShutdown 更新並關機 PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. 屏幕保護程式已啟動 The screensaver is inactive. 屏幕保護程式未啟動 FingerPrint 指紋 FingerVein 指靜脈 Iris 虹膜 Face 人臉識別 VoicePrint 聲紋 Ukey 安全金鑰 QRCode 二維碼 ResetPwdWidget Reset your password 重置密码 Security Question 安全問題 Kylin ID 麒麟ID Enter new password 輸入新密碼 Confirm your password 確認密碼 Inconsistency with pwd 與新密碼不一致 Confirm 確認 Cancel 取消 Contains illegal characters! 存在非法字元 The service is abnormal 服務異常 The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) 簡訊驗證碼已發送至尾號%1的手機(5分鐘內有效) Failed to get the verification code 獲取驗證碼失敗 The account does not exist, please re-enter it 帳戶不存在,請重新輸入 The account does not exist, please complete the registration first 帳號不存在,請先完成註冊 The user is already locked, contact the administrator to unlock it 該用戶已經鎖定,請聯繫管理員解除鎖定 If there are too many login errors, please try again in 5 minutes 登錄錯誤次數過多,請5分鐘后重試 The SMS verification code is incorrect or has expired 簡訊驗證碼錯誤或已過期 Wrong username or password 使用者名或密碼錯誤 Password decryption error 密碼解密出錯 Verify Kylin ID Reset Password 驗證麒麟ID重置密碼 Answer the security questions and reset your password 回答安全問題重置密碼 One or more wrong answers 一個或多個錯誤答案 S: tablet device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist 圖片不存在 Not connected to a network 網路未連接 The weather update failed 天氣更新失敗 Smart Saver 智慧屏保 Weather 天氣 Music 音樂 Photo Albun 相冊 Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View 預覽 SleepTime You have rested: 您已休息: You have rested 您已休息 SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer 輸入答案 UserListWidget Login 登錄 Guest 遊客 Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account 輸入麒麟ID帳戶 Input password 輸入密碼 Enter the verification code 輸入驗證碼 Get verification code 獲取驗證碼 Reacquire(%1) 重新取得(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 WeatherWidget Not connected to a network 网络未连接 delay how long to show lock 多久顯示鎖屏 has-lock if show lock 是否鎖屏 main Start command for the ukui ScreenSaver. 啟動鎖屏到命令 lock the screen immediately 馬上鎖定螢幕 query the status of the screen saver 獲取鎖屏狀態 unlock the screen saver 解鎖螢幕 show the screensaver 顯示幕保 show the session tools 顯示工作階段管理 show the app block window 顯示應用阻塞關機介面 show the multiUsers block window 顯示多使用者阻塞關機介面 Backend for the ukui ScreenSaver. ukui-screensaver/i18n_ts/kk.ts0000664000175000017500000045557515172041106015257 0ustar fengfeng AgreementWindow I know بىلدىم AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry قاتە قاتە سىناۋ UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. .ٴسىز 1% مينۋت ٸشٸندە قاتە سىناپ كور Please try again in %1 seconds. .ٴسىز 1% سەكونت ٸشٸندە قاتە سىناپ كور Account locked permanently. .ەسەپات ماڭگىلىك قۇلىپتالادى Verify face recognition or enter password to unlock چىراي تانىسن دالەلدەۋ ياكي قۇپيا نومەر كىرگىزۋ ارقىلى قۇلىپ ٸشٸۋ Guest مەيمان (اتسىز تٸزٸمدەۇ) Press fingerprint or enter password to unlock بارماقشى ٴٸزدٸ باسٸۋ ياكي قۇپيا نومەر كىرگىزۋ ارقىلى قۇلىپ ٸشٸۋ Verify voiceprint or enter password to unlock اۋا ٴٸزدٸ دالەلدەۋ ياكي قۇپيا نومەر كىرگىزۋ ارقىلى قۇلىپ ٸشٸۋ Verify finger vein or enter password to unlock بارماقشى ٴٸزٸ ارقىلى انىقتاۋ ياكي قۇپيا نومەر كىرگىزۋ ارقىلى قۇلىپ ٸشٸۋ Verify iris or enter password to unlock نى انىقتاۋ ياكي قۇپيا نومەر كىرگىزۋ ارقىلى قۇلىپ ٸشٸۋ Iris Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock The authentication has been cancelled! Password: قۇپيا نومەر Input Password قۇپيا نۇمىردى كىرگىزىڭىز Username ابونت مى User name input error! پايدالانۋشٸ اتٸ قاتە كىرگىزىلدى login كىرۋ Failed to verify %1, please enter password to unlock دالەلدەۋ 1% جەڭىلىپ قالدى، قۇپيا نومەر كىرگىزىڭىز Unable to verify %1, please enter password to unlock دالەلدەۋگە بولمادى 1%، قۇپيا نۇمىردى كىرەۈزۈپ قۇلىبىن ٴٸشڭٸز Abnormal network بينورمال تور Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. چىراي تونۇتۇشنىڭ ساقتاۋ ۋاقىتى ٸشٸپ كەتتى، جاڭالاۋدى تۇرتىپ ياكي قۇپيا نومەر كىرەۈزۈپ قۇلىبىن ٴٸشڭٸز. FingerPrint بارماقشى ٴٸزٸ FingerVein بارماقشى ۋېناسى Iris تۇستى پەردە Face ادام بەتى VoicePrint اۋا ٴٸزٸ Ukey قۇپيا كىلت QRCode ەكٸلٸك رازمەر fingerprint بارماقشى ٴٸزٸ fingervein بارماقشى ۋېنا تومۇزى iris تۇستى پەردە face ادام بەتى voiceprint اۋا ٴٸزٸ Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty قۇپيا نۇمىردى بوس قويۋعا بولمايدى Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities دالەلدەۋ 1% جەڭىلىپ قالدى، جانە 2% دالەلدەۋ ورايڭىز بار Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again دالەلدەۋ جەڭىلىپ قالدى، قاتە سىناپ كور Use the bound wechat scanning code or enter the password to unlock بايلانعان ئۈندىدارنى كەسكىندەۋ بەلگىسى ياكي قۇپيا نۇمىردى كىرەۈزۈپ قۇلىپ ٸشٸۋ Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port 请将安全密钥插入USB端口 Password 密码 Login كىرۋ Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... زەرەتلىنىۋاتىدۇ... fully charged توك تولٸق تۇسىرىلدى PowerMode توك قامداعٸش فورماسى BatteryMode باتاريا كۇيى BindDialog Close جابۋ Ok 确定 Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel كۇشىنەن قالدىرۋ Confirm تۇراقتاندىرۋ If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. تومەندەگٸ پىروگراممىلار قايتادان قوزعالتۋدى توسىپ قالاساڭىز بولادٸ،ٴسىز «كۇشىنەن قالدىرۋ»نى تۇرتىپ سونان نۇ تارتىپتەردى اتىپ ەتسەڭىز بولادٸ. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. تومەندەگٸ پروگرامما ئېتىۋېتىلگەنلىكىنى توسىپ قالدى،ٴسىز «كۇشىنەن قالدىرۋ»نى تۇرتىپ سونان نۇ پرٶگراممالاردى اتىپ ەتسەڭىز بولادٸ. The following programs prevent suspend, you can click "Cancel" and then close these programs. تومەندەگٸ پروگرامما ئۇيقۇنى توسىپ قالدى،ٴسىز «كۇشىنەن قالدىرۋ»نى تۇرتىپ سونان نۇ پرٶگراممالاردى اتىپ ەتسەڭىز بولادٸ. The following programs prevent hibernation, you can click "Cancel" and then close these programs. تومەندەگٸ پروگرامما ئۇيقۇنى توسىپ قالدى،ٴسىز «كۇشىنەن قالدىرۋ»نى تۇرتىپ سونان نۇ پرٶگراممالاردى اتىپ ەتسەڭىز بولادٸ. The following programs prevent you from logging out, you can click "Cancel" and then close them. تومەندەگٸ پىروگراممىلار بوس ورىندالۋىن توسىپ قالدى،ٴسىز «كۇشىنەن قالدىرۋ»نى تۇرتىپ سونان نۇ پرٶگراممالاردى اتىپ ەتسەڭىز بولادٸ. shut down restart CharsMoreWidget &&?! &&?! CharsWidget More الٸدە كوپ ABC ABC 123 123 ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 EngineDevice yes سونداي no جوق Yes سونداي No جوق %1% available, charged %1% نى ٸستەتكەلٸ بولادٸ، ادىلەت الىپ قالدى. Left %1h %2m (%3%) سول جاق(1%h %2 m (%3 %)) %1% available %1% ٸستەتكەلٸ بولادٸ Left %1h %2m to full سولعا (%1 h %2 m) لىق تولدٸ charging (%1%) زەرەتلىنىۋاتىدۇ (%1) %1 waiting to discharge (%2%) %1(%دوختۇرخانىدىن شىعۋدى ساقتاپ جاتىر (%2 %1 waiting to charge (%2%) %1 توك تۇسىرۋدى ساقتاپ جاتىر (%2 AC adapter پٸكٸر سايكەستىرۋ ۇيلەستىرۋ Laptop battery ەستەلىك كومپيۋتەر باتارېيەسى UPS Monitor كۇزەتۋشى Mouse تاقىرىپ Keyboard كىنوپكا تاقتاسى PDA Cell phone جان تەلەفون Media player تاسىل قۇيعىش Tablet تاقتا Computer كومپيۋتەر unrecognised پارٸق بەكتلمەدى IconEdit OK 提交 InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state قەرەللىك توقتاتىلعان بەينە Close جابۋ Welt status شەت كۇيى KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LettersWidget &&?! &&?! 123 123 LightDMHelper failed to start session. دىيالوگنى باستاۋ جەڭىلىپ قالدى Login كىرۋ Guest مەيمان (اتسىز تٸزٸمدەۇ) LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser 切换用户 Power 电源 VirtualKeyboard 虚拟键盘 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession Power Information Network 网络 system-monitor سەستامالىق كۇزەتۋشى Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Restart قاتە قوزعالتۋ Multiple users are logged in at the same time.Are you sure you want to %1 this system? بٸر قانشا ابونت بٸرلا ۋاقىتتا سەستاماعا كىردى، نۇ %1 سەستامانى ٸستەتلۋٸن انىقتايسىزبا؟ Reboot 重启 PowerOff 关机 Shut Down قولفوندى جابىپ الۋ LoginOptionsWidget Login Options كىرۋ تالدانبالارٸ Password قۇپيا نومەر Other باسقا FingerPrint بارماقشى ٴٸزٸ FingerVein بارماقشى ۋېناسى Iris تۇستى پەردە Face ادام بەتى VoicePrint اۋا ٴٸزٸ Ukey قۇپيا كىلت QRCode ەكٸلٸك رازمەر Wechat 微信 Identify device removed! !اسبابٸن پارىقتاندىرۋ چىقىرىۋېتىلدى MPRISSaverWidget No music detected Unknown كۋالىك MPRISWidget Unknown كۋالىك MusicFileInformation Unknown singer Unknown album MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN سىمدى تور WLAN سۇيىر كولەمدىك سىمسىز تور NumbersWidget &&?! &&?! Return قايتۋ OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate قىسقى ۇيقىعا كىرۋ Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left كومپيۋتەردى بەكتۋلى، ٴبىراق جيۋ ايقٸن قالىپىن ساقتايدى. كومپيۋتەر اشىلعان كەزدە، ٴسىز ايىرىلعان كەزدەگى قالىپقا قايتادى Suspend توقتاتٸپ قويۋ The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off كومپيۋتەر ايقٸن قالىپىن ساقتايدى، ٴبٸراق قۋات سارىپ قىلعانى بٸر دەيىن تومەن. نۇ جيۋ ايقٸن كۇيدە تۇرٸ، تەز جىلدامدىقتا ئويغىنىدۇ ونىڭ ۇستىنە ٴسىز توقتاعان جايغا قايتارعالى بولادٸ Restart قاتە قوزعالتۋ Close all apps, and then restart your computer بارلٸق ئەپنى ٶشٸرٸپ، سونان كومپيۋتەردى قاتە قوزعالتۋ Shut Down قولفوندى جابىپ الۋ The current user logs out of the system, terminates the session, and returns to the login page كەزەكتە ابونتتار سەستامادان بوس ورىندالىپ، سويلەسۋدى اقىرلاستىردى ونىڭ ۇستىنە تٸزٸمدەۇ كورىنسكە قايتادى. Logout بوس قىلىۋەتمەك Power Off 关机 Close all apps, and then shut down your computer بارلٸق ئەپنى ٶشٸرٸپ، سونان كومپيۋتەردى ٴوشىرۋ SwitchUser قاريدار ايلاندىرماق LockScreen قۇلىپ ەكٸرانٸ Log Out بوس قىلىۋەتمەك The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off كومپيۋتەر ايقٸن قالىپىن ساقتايدى، ٴبٸراق قۋات سارىپ قىلعانى بٸر دەيىن تومەن. نۇ جيۋ ايقٸن كۇيدە تۇرٸ، تەز جىلدامدىقتا ئويغىنىدۇ ونىڭ ۇستىنە ٴسىز توقتاعان جايغا قايتارعالى بولادٸ UpgradeThenRestart جاڭالاعاننان كەيىن قايتادان قوزعالتۋ Close all apps, turn off your computer, and then turn your computer back on بارلٸق ئەپنى ٶشٸرٸپ، كومپيۋتەردى ٶشٸرٸپ، سونان كومپيۋتەردى قاتە ٸشٸۋ UpgradeThenShutdown جاڭالانعاننان كەيىن اپپارات بەكتلەدى PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. .ەكٸران قورعاۋ سەستيماسى ارەكەتتەنگىش كۇيدە The screensaver is inactive. .ەكٸران قورعاۋ سەستيماسى ارەكەتسز كۇيدە FingerPrint بارماقشى ٴٸزٸ FingerVein بارماقشى ۋېناسى Iris تۇستى پەردە Face ادام بەتى VoicePrint اۋا ٴٸزٸ Ukey قۇپيا كىلت QRCode ەكٸلٸك رازمەر Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm 确认 Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist راسىم ساقتالعان ەمەس Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View كوز قاراس Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested: 您已休息: You have rested دەمالٸس ئالدىڭىز SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer UserListWidget Login كىرۋ Guest مەيمان (اتسىز تٸزٸمدەۇ) Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account Input password 输入密码 Enter the verification code Get verification code Reacquire(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 Widget Widget كشكەنە قۇرىلما LoadPlugin ۇساق اسپاپ action which block type which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart delay how long to show lock قانشالىق ۋاقىت قۇلۇپلانغانلىقىنى كورسەتۋ has-lock if show lock قيسٸق قۇلپىلاۋدى كورسەتپەكشى بولسا main Start command for the ukui ScreenSaver. ەكٸران قورعاۋ پىروگىرامماسىنڭ بۇيرٸقتٸ قوزعالتۋ ukui lock the screen immediately ەكٸرانٸ دەرەۋ قۇلپىلاۋ query the status of the screen saver ەكٸران قورعاۋ پىروگىرامماسىنڭ كۇيىن سۇراستىرۋ unlock the screen saver ەكٸران قورعاۋ پىروگىرامماسىنڭ قۇلىبن ٸشٸۋ show the screensaver ەكٸران قورعاۋ پروگىرامماسىن كورسەتۋ show blank and delay to lock,param:idle/lid/lowpower idle/lid/lowpower :قۇرۇقنى كورسەتۋ ۋا قۇلپىلاۋدى كەشىكتىرۋ، پارامەتٸرٸ show the session tools show the switchuser window show the app block window show the multiUsers block window Backend for the ukui ScreenSaver. .ەكٸران قورعاۋ پىروگىرامماسىنڭ كەيىنى اۋٸزٸ ukui lock the screen by startup قوزعالتقاندىق ەكٸرانٸ قۇلپىلاۋ Dialog for the ukui ScreenSaver. .ەكٸران قورعاۋ پىروگىرامماسىنڭ دىيالوگ رامكاسى ukui activated by session idle signal بوس سەگنال ارقىلى اكتيۆتەندٸرۋ lock the screen and show screensaver immediately ەكٸرانٸ قۇلپىلاۋ ونىڭ ۇستىنە دەرەۋ ەكٸران قورعاۋ پروگىرامماسىن كورسەتۋ show screensaver immediately ەكٸران قورعاۋ پروگىرامماسىن دەرەۋ كورسەتۋ show blank screensaver immediately and delay time to show lock قۇرعاق ەكٸران قورعاۋ پروگىرامماسىن دەرەۋ كورسەتۋ ونىڭ ۇستىنە قۇلپىلاۋ ۋاقىتىن كەشىكتىرىپ كورسەتۋ show blank screensaver immediately and if lock قيسٸق قۇلۇپلانسا، قۇرعاق ەكٸران قورعاۋ پروگىرامماسىن دەرەۋ كورسەتۋ show switch user window switch to user and show window Screensaver for ukui-screensaver ەكٸران قورعاۋ پىروگىرامماسىنڭ ەكٸران قورعاۋ پىروگىرامماسى ukui show on root window جۇلدىز تەرەزەدە كورسەتۋ show on window. .تەرەزەدە كورسەتۋ window id id كوز بەك switchtouser switch to someone user ukui-screensaver/i18n_ts/pt.ts0000664000175000017500000025223115172041106015255 0ustar fengfeng AgreementWindow I know AuthDialog Form Formato More Devices Mais dispositivos Biometric Biométrico Password Senha Retry Tente novamente UnLock Desbloquear LoggedIn Logado Password Incorrect, Please try again Senha incorreta, por favor tente novamente FingerPrint Impressão digital FingerVein FingerVein Iris Íris Face Face VoicePrint VoicePrint Guest Convidado Login Password cannot be empty Input Password Username User name input error! Authentication failure, Please try again login Please try again in %1 minutes. Please try again in %1 seconds. Account locked permanently. Failed to verify %1, please enter password to unlock Unable to verify %1, please enter password to unlock Failed to verify %1, you still have %2 verification opportunities Abnormal network Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. Ukey QRCode fingerprint fingervein iris face voiceprint Password: Password Prompt:%1 Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock Enter username The authentication has been cancelled! Insert the ukey into the USB port Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication wechat Use the bound %1 scanning code to unlock BatteryWidget Charging... fully charged PowerMode BatteryMode BindDialog Close Ok Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BioAuthWidget Form Formato TextLabel TextLabel More Mais Retry Tente novamente Password Senha BioDevices FingerPrint Impressão digital FingerVein FingerVein Iris Íris Face Face VoicePrint VoicePrint BioDevicesWidget Form Formato Please select other biometric devices Por favor, selecione outros dispositivos biométricos Device Type: Tipo de dispositivo: Device Name: Nome do dispositivo: BlockWidget Cancel Confirm The following programs prevent restarting, you can click "Cancel" and then close these programs. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. The following programs prevent suspend, you can click "Cancel" and then close these programs. The following programs prevent hibernation, you can click "Cancel" and then close these programs. The following programs prevent you from logging out, you can click "Cancel" and then close them. If you do not perform any operation, the system will automatically %1 after %2 seconds. shut down restart CharsMoreWidget &&?! CharsWidget More Mais ABC 123 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint Impressão digital FingerVein FingerVein Iris Íris Face Face VoicePrint VoicePrint DlgConnHidWifiLeap Password Senha DlgConnHidWifiSecFast Password Senha DlgConnHidWifiSecLeap Password Senha DlgConnHidWifiSecPeap Password Senha DlgConnHidWifiSecPwd Password Senha DlgConnHidWifiSecTunnelTLS Password Senha DlgConnHidWifiWpa Password Senha DlgHotspotCreate Password Senha EngineDevice yes no Yes No %1% available, charged Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK KBTitle Suspended state Close Welt status KeyboardWidget KeyboardWidget KeyboardWidget LettersWidget &&?! 123 LightDMHelper Guest Convidado failed to start session. Login LockWidget Form Formato Date Encontro Time Tempo Guest Convidado SwitchUser Mudar de utilizador Multiple users are logged in at the same time.Are you sure you want to %1 this system? system-monitor Shut Down Restart %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". SwitchSession Power Information Network VirtualKeyboard Power Reset Pwd LoginOptionsWidget Password Senha FingerPrint Impressão digital FingerVein FingerVein Iris Íris Face Face VoicePrint VoicePrint Login Options Other Identify device removed! Ukey QRCode MPRISSaverWidget No music detected Unknown MusicFileInformation Unknown singer Unknown album MyNetworkWidget LAN WLAN NumbersWidget &&?! Return OneConnForm Form Formato OneLancForm Form Formato PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Suspend Restart Shut Down Close all apps, and then shut down your computer Close all apps, turn off your computer, and then turn your computer back on Close all apps, and then restart your computer Log Out The current user logs out of the system, terminates the session, and returns to the login page SwitchUser Mudar de utilizador LockScreen UpgradeThenRestart UpgradeThenShutdown The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off PowerManager SwitchUser Mudar de utilizador QObject FingerPrint Impressão digital FingerVein FingerVein Iris Íris Face Face VoicePrint VoicePrint Ukey QRCode The screensaver is active. The screensaver is inactive. Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver Picture does not exist View Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested SureWindow Form Formato TextLabel TextLabel TalksWidget Enter your answer UserListWidget Guest Convidado Login VerifyKyidWidget Enter your Kylin ID account Input password Enter the verification code Get verification code Reacquire(%1) Widget Widget LoadPlugin action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock has-lock if show lock main Start command for the ukui ScreenSaver. Inicie o comando para o ScreenSaver do ukui. lock the screen immediately bloquear a tela imediatamente Dialog for the ukui ScreenSaver. Diálogo para o ScreenSaver ukui. activated by session idle signal ativado por sinal ocioso de sessão Backend for the ukui ScreenSaver. lock the screen by startup query the status of the screen saver unlock the screen saver show the screensaver show blank and delay to lock,param:idle/lid/lowpower lock the screen and show screensaver immediately show screensaver immediately show blank screensaver immediately and delay time to show lock show blank screensaver immediately and if lock switch to user and show window Screensaver for ukui-screensaver show on root window show on window. window id show the session tools show the switchuser window show the app block window show the multiUsers block window show switch user window switchtouser switch to someone user ukui-screensaver/i18n_ts/vi.ts0000664000175000017500000035515515172041106015261 0ustar fengfeng AgreementWindow I know Tôi đã biết AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry Thử lại UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Lời nhắc mật khẩu:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. Vui lòng thử lại sau %1 phút. Please try again in %1 seconds. Vui lòng thử lại sau %1 giây. Account locked permanently. Tài khoản đã bị khóa vĩnh viễn. Verify face recognition or enter password to unlock 验证人脸识别或输入密码解锁 Guest Đăng nhập khách Press fingerprint or enter password to unlock 按压指纹或输入密码解锁 Verify voiceprint or enter password to unlock 验证声纹或输入密码解锁 Verify finger vein or enter password to unlock 验证指静脉或输入密码解锁 Verify iris or enter password to unlock 验证虹膜或输入密码解锁 Enter username Nhập tên người dùng Facial recognition to unlock nhận diện khuôn mặt để mở khóa Fingerprint recognition to unlock nhận diện dấu vân tay để mở Voiceprint recognition to unlock nhận diện giọng nói để mở khóa Finger vein recognition to unlock nhận dạng mạch ngón tay để mở khóa Iris recognition to unlock nhận diện mống mắt để mở khóa Use the bound wechat scanning code to unlock 微信扫码解锁 wechat Wechat Use the bound %1 scanning code to unlock Sử dụng mã quét %1 liên kết để mở khóa The authentication has been cancelled! Xác thực đã bị hủy! Password: Mật khẩu: Input Password Nhập mật khẩu Username Tên người dùng User name input error! Lỗi nhập tên người dùng! login Đăng nhập Failed to verify %1, please enter password to unlock Xác thực %1 thất bại. Vui lòng nhập mật khẩu để mở khóa Unable to verify %1, please enter password to unlock Không thể xác thực %1,vui lòng nhập mật khẩu để mở khóa Abnormal network Lỗi mạng Acquisition failure Lỗi khi tải Face recognition waiting time out, please click refresh or enter the password to unlock. Nhận diện khuôn mặt đã chờ quá lâu. Vui lòng nhấn làm mới hoặc nhập mật khẩu để mở khóa. FingerPrint Vân tay FingerVein Mạch máu ngón tay Iris Võng mạc Face Nhận diện khuôn mặt VoicePrint Sinh trắc giọng nói Ukey Khóa bảo mật QRCode Mã QR fingerprint Vân tay fingervein Mạch máu ngón tay iris Võng mạc face Nhận diện khuôn mặt voiceprint Sinh trắc giọng nói Two-Factor OTP Dynamic Password Authentication Xác thực mật khẩu động OTP hai yếu tố Two-Factor Security Key Authentication Xác thực khóa bảo mật hai yếu tố Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty Mật khẩu mới không được để trống Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities Xác thực %1 thất bại,bạn còn %2 lần thử Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again Xác thực thất bại, vui lòng thử lại Use the bound wechat scanning code or enter the password to unlock 使用绑定的微信扫码或输入密码解锁 Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port Vui lòng cắm khóa bảo mật vào cổng USB Password 密码 Login Đăng nhập Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... Đang sạc... fully charged Đã sạc đầy PowerMode Chế độ nguồn điện BatteryMode Chế độ pin BindDialog Close Thoát Ok Ok Cancel Hủy Mfa authentication is on, please bind OTP code Đã bật xác thực đa yếu tố, vui lòng liên kết mã OTP Go to Bind bắt đầu liên kết Bind Ràng buộc BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel Hủy Confrim xác nhận If you do not perform any operation, the system will automatically %1 after %2 seconds. Nếu bạn không thực hiện bất kỳ thao tác nào, hệ thống sẽ tự động %1 sau %2 giây. The following programs prevent restarting, you can click "Cancel" and then close these programs. Các chương trình sau ngăn việc khởi động lại, bạn có thể nhấp vào "Hủy" và sau đó đóng các chương trình này. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. Các chương trình sau ngăn chặn việc tắt máy, bạn có thể nhấp vào "Hủy" và sau đó đóng các chương trình này. The following programs prevent suspend, you can click "Cancel" and then close these programs. Các chương trình sau đây ngăn chặn việc tạm ngưng, bạn có thể nhấp vào "Hủy" và sau đó đóng các chương trình này. The following programs prevent hibernation, you can click "Cancel" and then close these programs. Các chương trình sau ngăn việc tạm nghỉ, bạn có thể nhấp vào "Hủy" và sau đó đóng các chương trình này. The following programs prevent you from logging out, you can click "Cancel" and then close them. Các chương trình sau đây ngăn bạn đăng xuất, bạn có thể nhấp vào "Hủy" và sau đó đóng chúng. The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. Các chương trình sau ngăn bạn khởi động lại máy, bạn có thể nhấp vào "Hủy" rồi đóng chúng hoặc nhấp vào 'OK' để buộc đóng và tiếp tục. The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. Các chương trình sau ngăn bạn tắt máy, bạn có thể nhấp vào "Hủy" rồi đóng chúng hoặc nhấp vào 'OK' để buộc đóng và tiếp tục. The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. Các chương trình sau đây ngăn bạn đăng xuất, bạn có thể nhấp vào "Hủy" rồi đóng chúng hoặc nhấp vào 'OK' để buộc đóng và tiếp tục. shut down Tắt máy restart Khởi động lại CharsWidget More Xem thêm ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them Không phát hiện thấy ảnh, vui lòng chọn lại DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 IconEdit OK OK InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state Trạng thái treo Close Thoát Welt status trạng thái Welt KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LightDMHelper failed to start session. Khởi động phiên làm việc thất bại. Login Đăng nhập Guest Đăng nhập khách LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser Chuyển đổi người dùng Power Nguồn điện VirtualKeyboard Bàn phím ảo Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession Phiên chuyển đổi Power Information thông tin nguồn điện Network Trạng thái mạng system-monitor Trình giám sát hệ thống Reset Pwd Đặt lại mật khẩu %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". %1 có thể gây mất dữ liệu chưa được lưu của người dùng đã đăng nhập vào máy tính này. Nếu vẫn muốn tiếp tục, vui lòng nhấn "Xác nhận". Restart Khởi động lại Multiple users are logged in at the same time.Are you sure you want to %1 this system? Nhiều người dùng được đăng nhập cùng một lúc. Bạn có chắc chắn muốn %1 hệ thống này không? Reboot 重启 PowerOff 关机 Shut Down Tắt máy LoginOptionsWidget Login Options Tùy chọn đăng nhập Password Mật khẩu Other Khác FingerPrint Vân tay FingerVein Mạch máu ngón tay Iris Võng mạc Face Nhận diện khuôn mặt VoicePrint Sinh trắc giọng nói Ukey Khóa bảo mật QRCode Mã QR Wechat 微信 Identify device removed! Thiết bị xác thực đã bị gỡ bỏ! MPRISSaverWidget No music detected không phát hiện ra nhạc Unknown Chưa xác định MPRISWidget Unknown 未知 MusicFileInformation Unknown singer Ca sĩ không rõ Unknown album Album không rõ MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN Mạng có dây WLAN Mạng cục bộ không dây NumbersWidget Return Quay về OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters Mật khẩu ngắn hơn %1 ký tự The password contains less than %1 character classes Mật khẩu chứa ít hơn %1 loại ký tự The password is the same as the old one Mật khẩu này trùng với mật khẩu cũ The password contains the user name in some form Mật khẩu chứa tên người dùng dưới một số hình thức The password differs with case changes only Mật khẩu chỉ khác với thay đổi chữ hoa chữ thường The password is too similar to the old one Mật khẩu quá giống với mật khẩu cũ The password is a palindrome Mật khẩu là một palindrome PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PowerListWidget Hibernate Ngủ đông Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Tắt máy tính, nhưng ứng dụng sẽ vẫn giữ trạng thái mở, khi mở máy tính lại, có thể khôi phục về trạng thái bạn đã rời đi Suspend Dừng lại The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Restart Khởi động lại Close all apps, and then restart your computer đóng tất cả ứng dụng, sau đó khởi động lại máy Shut Down Tắt máy The current user logs out of the system, terminates the session, and returns to the login page Người dùng hiện tại đã đăng xuất khỏi hệ thống, kết thúc phiên làm việc và trở về giao diện đăng nhập Logout 注销 Power Off 关机 Close all apps, and then shut down your computer Đóng tất cả các ứng dụng, sau đó tắt máy tính SwitchUser Chuyển đổi người dùng LockScreen Màn hình khóa Log Out Đăng xuất The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off Máy tính giữ trạng thái bật, nhưng tiêu thụ ít điện năng hơn, các ứng dụng sẽ luôn giữ trạng thái mở, có thể nhanh chóng đánh thức máy tính và khôi phục về trạng thái bạn đã rời đi UpgradeThenRestart Nâng cấp Sau đó Khởi động lại Close all apps, turn off your computer, and then turn your computer back on Đóng tất cả các ứng dụng, tắt máy tính, sau đó mở lại máy tính UpgradeThenShutdown Cập nhật và tắt máy PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. Chế độ bảo vệ màn hình đã được kích hoạt. The screensaver is inactive. Chế độ bảo vệ màn hình chưa được kích hoạt. FingerPrint Vân tay FingerVein Mạch máu ngón tay Iris Võng mạc Face Nhận diện khuôn mặt VoicePrint Sinh trắc giọng nói Ukey Khóa bảo mật QRCode Mã QR ResetPwdWidget Reset your password 重置密码 Security Question Vấn đề an toàn Kylin ID ID Kylin Enter new password Nhập mật khẩu mới Confirm your password Xác nhận mật khẩu Inconsistency with pwd Không khớp với mật khẩu mới Confirm Lưu sửa đổi Cancel Hủy Contains illegal characters! Chứa ký tự không hợp lệ! The service is abnormal Dịch vụ bị lỗi The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Mã xác nhận SMS đã được gửi đến số điện thoại có đuôi %1(có hiệu lực trong vòng 5 phút) Failed to get the verification code Lấy mã xác thực thất bại The account does not exist, please re-enter it Tài khoản không tồn tại, vui lòng nhập lại The account does not exist, please complete the registration first Tài khoản không tồn tại, vui lòng đăng ký trước The user is already locked, contact the administrator to unlock it Người dùng này đã bị khóa, vui lòng liên hệ với quản trị viên để mở khóa If there are too many login errors, please try again in 5 minutes Nhập sai quá nhiều lần, vui lòng thử lại sau 5 phút The SMS verification code is incorrect or has expired Mã xác nhận SMS sai hoặc đã hết hạn Wrong username or password Tên người dùng hoặc mật khẩu không đúng Password decryption error Lỗi giải mã mật khẩu Verify Kylin ID Reset Password Xác thực ID Kylin để đặt lại mật khẩu Answer the security questions and reset your password Trả lời câu hỏi bảo mật để đặt lại mật khẩu One or more wrong answers Một hoặc nhiều câu trả lời sai S: tablet device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist Hình ảnh không tồn tại Not connected to a network Không được kết nối mạng The weather update failed cập nhật thời tiết thất bại Smart Saver Tiết kiệm thông minh Weather Thời tiết Music Bài hát Photo Albun album ảnh Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View Xem SleepTime You have rested: 您已休息: You have rested Bạn đã nghỉ ngơi SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer Nhập đáp án UserListWidget Login Đăng nhập Guest Đăng nhập khách Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account Nhập tài khoản ID Kylin Input password Nhập mật khẩu Enter the verification code Nhập mã xác thực Get verification code Lấy mã xác thực Reacquire(%1) Lấy lại (%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 WeatherWidget Not connected to a network 网络未连接 delay how long to show lock Thời gian hiển thị khóa là bao lâu has-lock if show lock Nếu hiển thị khóa main Start command for the ukui ScreenSaver. Khởi động khóa màn hình bằng lệnh. lock the screen immediately Khóa màn hình ngay lập tức query the status of the screen saver Lấy trạng thái khóa màn hình unlock the screen saver Mở khóa màn hình show the screensaver Hiển thị màn hình chờ show the session tools Hiển thị các công cụ phiên show the app block window Hiển thị cửa sổ chặn ứng dụng show the multiUsers block window hiện cửa sổ chặn nhóm đa người dùng Backend for the ukui ScreenSaver. Phần phụ trợ cho Trình bảo vệ màn hình ukui. ukui-screensaver/i18n_ts/CMakeLists.txt0000664000175000017500000000036015172041106017014 0ustar fengfengfind_package(Qt6Linguist) file(GLOB ts_files *.ts) qt6_add_translation(qm_files ${ts_files}) add_custom_target(i18n DEPENDS ${qm_files} SOURCES ${ts_files} ) install(FILES ${qm_files} DESTINATION /usr/share/ukui-screensaver/i18n_qm/) ukui-screensaver/i18n_ts/mn.ts0000664000175000017500000040734115172041106015250 0ustar fengfeng AgreementWindow I know ᠪᠢ ᠮᠡᠳᠡᠵᠡᠢ AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry ᠳᠠᠬᠢᠵᠤ ᠲᠤᠷᠰᠢᠬᠤ UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠤᠨ ᠰᠠᠨᠠᠭᠤᠯᠤᠮᠵᠢ ᠄ %1 ᠃ Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. %1 ᠮᠢᠨᠦ᠋ᠲ᠎ᠦᠨ ᠳᠠᠷᠠᠭ᠎ᠠ ᠳᠠᠬᠢᠵᠤ ᠳᠤᠷᠱᠢᠭᠠᠷᠠᠢ᠃ Please try again in %1 seconds. %1 ᠮᠢᠨᠦ᠋ᠲ᠎ᠦᠨ ᠳᠠᠷᠠᠭ᠎ᠠ ᠳᠠᠬᠢᠵᠤ ᠳᠤᠷᠱᠢᠭᠠᠷᠠᠢ᠃ Account locked permanently. ᠳᠠᠩᠰᠠ ᠨᠢᠭᠡᠨᠳᠡ ᠦᠨᠢᠳᠡ ᠤᠨᠢᠰᠤᠯᠠᠭᠳᠠᠪᠠ᠃ Verify face recognition or enter password to unlock 验证人脸识别或输入密码解锁 Guest ᠵᠤᠷᠴᠢᠭᠴᠢ (᠎ᠨᠡᠷ᠎ᠡ ᠪᠤᠷᠤᠭᠤᠯᠠᠨ ᠨᠡᠪᠳᠡᠷᠡᠬᠦ) Press fingerprint or enter password to unlock 按压指纹或输入密码解锁 Verify voiceprint or enter password to unlock 验证声纹或输入密码解锁 Verify finger vein or enter password to unlock 验证指静脉或输入密码解锁 Verify iris or enter password to unlock 验证虹膜或输入密码解锁 Enter username ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ᠎ᠶᠢᠨ ᠨᠡᠷ᠎ᠡ ᠣᠷᠣᠭᠤᠯᠬᠤ Facial recognition to unlock ᠬᠦᠮᠦᠨ ᠤ ᠨᠢᠭᠤᠷ ᠢ ᠢᠯᠭᠠᠬᠤ ᠣᠨᠢᠰᠤ ᠶᠢ ᠢᠯᠭᠠᠬᠤ᠃ Fingerprint recognition to unlock ᠬᠤᠷᠤᠭᠤᠨ ᠣᠷᠣᠮ ᠢ ᠢᠯᠭᠠᠵᠤ ᠣᠨᠢᠰᠤᠯᠠᠬᠤ Voiceprint recognition to unlock ᠳᠠᠭᠤᠨ ᠤ ᠢᠯᠭᠠᠬᠤ ᠣᠨᠢᠰᠤ ᠶᠢ ᠢᠯᠭᠠᠬᠤ Finger vein recognition to unlock ᠰᠤᠳᠠᠯ ᠢ ᠢᠯᠭᠠᠬᠤ ᠣᠨᠢᠰᠤ ᠶᠢ ᠵᠢᠭᠠᠨ᠎ᠠ ᠃ Iris recognition to unlock ᠾᠦᠩ ᠰᠦᠯᠢᠶᠣᠤ ᠶᠢ ᠢᠯᠭᠠᠬᠤ ᠣᠨᠢᠰᠤ ᠶᠢ ᠢᠯᠭᠠᠬᠤ Use the bound wechat scanning code to unlock 微信扫码解锁 wechat ᠸᠢᠴᠠᠲ Use the bound %1 scanning code to unlock %1 ᠰᠢᠷᠪᠢᠵᠤ ᠣᠨᠢᠰᠣ ᠳᠠᠢᠯᠠᠬᠤ The authentication has been cancelled! ᠬᠡᠷᠡᠴᠢᠯᠡᠯ ᠦᠬᠡᠢᠰᠬᠡᠭᠳᠡᠪᠡ! Password: ᠨᠢᠭᠤᠴᠠ ᠺᠣᠳ᠋: Input Password ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠤᠷᠤᠭᠤᠯᠬᠤ Username ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠵᠢᠨ ᠨᠡᠷ᠎ᠡ User name input error! ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠵᠢᠨ ᠨᠡᠷ᠎ᠡ ᠵᠢ ᠪᠤᠷᠤᠭᠤ ᠤᠷᠤᠭᠤᠯᠪᠠ! login ᠳᠠᠩᠰᠠᠯᠠᠨ ᠤᠷᠤᠬᠤ Failed to verify %1, please enter password to unlock %1᠎ᠶᠢ/᠎ᠢ ᠪᠠᠳᠤᠯᠭᠠᠵᠢᠭᠤᠯᠬᠤ ᠠᠷᠭ᠎ᠠ ᠦᠬᠡᠢ ᠂ ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠤᠷᠤᠭᠤᠯᠵᠤ ᠤᠨᠢᠰᠤ᠎ᠶᠢ ᠳᠠᠢᠯᠤᠭᠠᠷᠠᠢ Unable to verify %1, please enter password to unlock %1᠎ᠶᠢ/᠎ᠢ ᠪᠠᠳᠤᠯᠭᠠᠵᠢᠭᠤᠯᠬᠤ ᠠᠷᠭ᠎ᠠ ᠦᠬᠡᠢ ᠂ ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠤᠷᠤᠭᠤᠯᠵᠤ ᠤᠨᠢᠰᠤ᠎ᠶᠢ ᠳᠠᠢᠯᠤᠭᠠᠷᠠᠢ Abnormal network ᠲᠤᠷ ᠰᠦᠯᠵᠢᠶ᠎ᠡ ᠬᠡᠪ ᠤ᠋ᠨ ᠪᠤᠰᠤ Acquisition failure ᠣᠯᠵᠤ ᠴᠢᠳᠠᠭᠰᠠᠨ ᠥᠬᠡᠢ Face recognition waiting time out, please click refresh or enter the password to unlock. ᠬᠦᠮᠦᠨ ᠤ ᠨᠢᠭᠤᠷ ᠢ ᠢᠯᠭᠠᠬᠤ ᠦᠶᠡᠰ᠂ ᠰᠢᠨᠡᠳᠭᠡᠬᠦ ᠪᠤᠶᠤ ᠨᠢᠭᠤᠴᠠ ᠨᠤᠮᠸᠷ ᠤᠨ ᠣᠨᠢᠰᠤ ᠶᠢ ᠲᠠᠷᠤᠭᠠᠷᠠᠢ᠃ FingerPrint ᠬᠤᠷᠤᠭᠤᠨ ᠤ᠋ ᠤᠷᠤᠮ FingerVein ᠬᠤᠷᠤᠭᠤᠨ ᠤ᠋ ᠨᠠᠮᠵᠢᠭᠤᠨ ᠰᠤᠳᠠᠰᠤ Iris ᠰᠤᠯᠤᠩᠭᠠᠨ ᠪᠦᠷᠬᠦᠪᠴᠢ Face ᠬᠥᠮᠦᠨ᠎ᠦ᠌ ᠨᠢᠭᠤᠷ ᠲᠠᠨᠢᠬᠤ VoicePrint ᠳᠠᠭᠤᠨ᠎ᠤ ᠢᠷᠠᠯᠵᠢ Ukey ᠠᠮᠤᠷ ᠲᠦᠪᠰᠢᠨ ᠪᠢᠲᠡᠭᠦᠮᠵᠢᠯᠡᠭᠦᠷ ᠃ QRCode ᠬᠣᠶᠠᠷ ᠬᠡᠮᠵᠢᠯᠳᠡᠳᠦ ᠺᠣᠳ᠋ fingerprint ᠬᠤᠷᠤᠭᠤᠨ ᠤᠷᠤᠮ fingervein ᠨᠠᠮ ᠵᠢᠮ ᠰᠤᠳᠠᠯ ᠢ ᠵᠢᠭᠠᠨ᠎ᠠ iris ᠬᠠᠯᠢᠰᠤ face ᠨᠢᠭᠤᠷ ᠴᠢᠷᠠᠢ voiceprint ᠳᠠᠭᠤᠨ ᠤ ᠣᠷᠣᠮ Two-Factor OTP Dynamic Password Authentication ᠬᠣᠣᠰ ᠹᠠᠺᠲ᠋ᠣᠷ᠎ᠤ᠋ᠨ OTP ᠬᠥᠳᠡᠯᠦᠩᠭᠦᠢ ᠠᠮᠠᠨ ᠵᠠᠷᠯᠢᠭ᠎ᠤ᠋ᠨ ᠭᠡᠷᠡᠴᠢᠯᠡᠯ Two-Factor Security Key Authentication ᠬᠣᠣᠰ ᠹᠠᠺᠲ᠋ᠣᠷ᠎ᠤ᠋ᠨ ᠠᠮᠤᠷ ᠳᠦᠪᠰᠢᠨ ᠤ᠋ ᠨᠢᠭᠤᠴᠠ ᠳᠦᠯᠬᠢᠬᠦᠷ ᠤ᠋ᠨ ᠬᠡᠷᠡᠴᠢᠯᠡᠯ Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠬᠤᠭᠤᠰᠤᠨ ᠪᠠᠢᠵᠤ ᠪᠤᠯᠬᠤ ᠦᠬᠡᠢ Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities %1 ᠢ᠋\ ᠵᠢ ᠪᠠᠳᠤᠯᠠᠭᠠᠵᠢᠭᠤᠯᠵᠤ ᠴᠢᠳᠠᠭᠰᠠᠨ ᠥᠬᠡᠢ᠂ ᠲᠠ ᠪᠠᠰᠠ %2 ᠤᠳᠠᠭᠠᠨ ᠤ᠋ ᠳᠤᠷᠰᠢᠬᠤ ᠵᠠᠪᠰᠢᠶᠠᠨ ᠪᠤᠢ Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again ᠪᠠᠳᠤᠯᠭᠠᠵᠢᠭᠤᠯᠤᠯᠳᠠ ᠢᠯᠠᠭᠳᠠᠪᠠ ᠂ ᠳᠠᠬᠢᠵᠤ ᠳᠤᠷᠱᠢᠭᠠᠷᠠᠢ Use the bound wechat scanning code or enter the password to unlock 使用绑定的微信扫码或输入密码解锁 Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port ᠠᠮᠤᠷ ᠳᠦᠪᠰᠢᠨ ᠤ᠋ ᠨᠢᠭᠤᠴᠠ ᠳᠦᠯᠬᠢᠬᠦᠷ ᠢ᠋ USB ᠦᠵᠦᠭᠦᠷ ᠲᠤ᠌ ᠬᠠᠪᠴᠢᠭᠤᠯᠬᠤ Password 密码 Login ᠳᠠᠩᠰᠠᠯᠠᠨ ᠤᠷᠤᠬᠤ Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... ᠶᠠᠭ ᠴᠡᠨᠡᠭᠯᠡᠵᠤ ᠪᠠᠢᠨ᠎ᠠ··· fully charged ᠴᠡᠨᠡᠭᠯᠡᠵᠤ ᠳᠠᠭᠤᠰᠪᠠ PowerMode ᠴᠠᠬᠢᠯᠭᠠᠨ ᠡᠭᠦᠰᠭᠡᠭᠴᠢ ᠶᠢᠨ ᠵᠠᠭᠪᠤᠷ BatteryMode ᠳ᠋ᠢᠶᠠᠨ ᠢ ᠶᠢᠨ ᠵᠠᠭᠪᠤᠷ No battery detected Батерей олдсонгүй BindDialog Close ᠬᠠᠭᠠᠬᠤ Ok ᠵᠥᠪᠰᠢᠶᠡᠷᠡᠪᠡ Cancel ᠪᠣᠯᠢᠬᠤ Mfa authentication is on, please bind OTP code ᠰᠢᠰᠲ᠋ᠧᠮ ᠨᠢ ᠨᠢᠭᠡᠨᠲᠡ ᠬᠣᠣᠰ ᠹᠠᠺᠲ᠋ᠣᠷ᠎ᠤ᠋ᠨ ᠭᠡᠷᠡᠴᠢᠯᠡᠯ᠎ᠢ᠋ ᠡᠬᠢᠯᠡᠨ ᠬᠡᠷᠡᠭᠯᠡᠪᠡ ᠂ ᠬᠣᠶᠠᠳᠤᠭᠠᠷ ᠤᠳᠠᠭᠠᠨ᠎ᠤ᠋ ᠭᠡᠷᠡᠴᠢᠯᠡᠬᠦ ᠠᠮᠠᠨ ᠳᠣᠬᠢᠶ᠎ᠠ ᠵᠢ ᠤᠶᠠᠭᠠᠷᠠᠢ Go to Bind ᠣᠴᠢᠵᠤ ᠤᠶᠠᠬᠤ Bind ᠤᠶᠠᠬᠤ OTP added successfully ᠬᠦᠳᠡᠯᠦᠩᠬᠦᠢ ᠠᠮᠠᠨ ᠳᠣᠬᠢᠶ᠎ᠠ ᠵᠢ ᠨᠡᠮᠡᠪᠡ BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel ᠪᠣᠯᠢᠬᠤ Confirm ᠨᠤᠲᠠᠯᠠᠨ ᠲᠣᠭᠲᠠᠭᠠᠬᠤ If you do not perform any operation, the system will automatically %1 after %2 seconds. ᠬᠡᠷᠪᠡ ᠲᠠ ᠶᠠᠮᠠᠷ ᠴᠤ ᠠᠵᠢᠯᠯᠠᠭᠤᠯᠬᠤ ᠦᠭᠡᠢ ᠪᠣᠯ ᠰᠢᠰᠲ᠋ᠧᠮ ᠨᠢ %2 ᠰᠧᠺᠦᠢᠨᠳ᠋ ᠦᠨ ᠳᠠᠷᠠᠭ᠎ᠠ ᠠᠦᠢᠲ᠋ᠣᠴᠢᠯᠠᠨ %1 ᠪᠣᠯᠣᠨ᠎ᠠ The following programs prevent restarting, you can click "Cancel" and then close these programs. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠳᠠᠬᠢᠨ ᠡᠬᠢᠯᠡᠬᠦ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠢ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠭᠰᠠᠨ ᠤ ᠳᠠᠷᠠᠭ᠎ᠠ ᠡᠳᠡᠭᠡᠷ ᠫᠷᠦᠭ᠍ᠷᠠᠮ ᠢ ᠬᠠᠭᠠᠵᠤ ᠪᠣᠯᠣᠨ᠎ᠠ The following programs prevent the shutdown, you can click "Cancel" and then close these programs. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠨᠢ ᠬᠠᠭᠠᠯᠭ᠎ᠠ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠢ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠭᠰᠠᠨ ᠤ ᠳᠠᠷᠠᠭ᠎ᠠ ᠡᠳᠡᠭᠡᠷ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢ ᠬᠠᠭᠠᠵᠤ ᠪᠣᠯᠣᠨ᠎ᠠ The following programs prevent suspend, you can click "Cancel" and then close these programs. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠤᠨ ᠨᠣᠶᠢᠷ ᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠢ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠭᠰᠠᠨ ᠤ ᠳᠠᠷᠠᠭ᠎ᠠ ᠡᠳᠡᠭᠡᠷ ᠫᠷᠦᠭ᠍ᠷᠠᠮ ᠢ ᠬᠠᠭᠠᠵᠤ ᠪᠣᠯᠣᠨ᠎ᠠ The following programs prevent hibernation, you can click "Cancel" and then close these programs. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢᠶᠠᠷ ᠤᠨᠲᠠᠬᠤ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠢ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠭᠠᠳ ᠡᠳᠡᠭᠡᠷ ᠫᠷᠦᠭ᠍ᠷᠠᠮ ᠢ ᠬᠠᠭᠠᠵᠤ ᠪᠣᠯᠣᠨ᠎ᠠ The following programs prevent you from logging out, you can click "Cancel" and then close them. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠨᠢ ᠬᠦᠴᠦᠨ ᠦᠭᠡᠢ ᠪᠣᠯᠭᠠᠬᠤ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠢ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠭᠰᠠᠨ ᠤ ᠳᠠᠷᠠᠭ᠎ᠠ ᠡᠳᠡᠭᠡᠷ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢ ᠬᠠᠭᠠᠵᠤ ᠪᠣᠯᠣᠨ᠎ᠠ The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠳᠠᠬᠢᠨ ᠡᠬᠢᠯᠡᠬᠦ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠶ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠵᠤ ᠡᠳᠡᠭᠡᠷ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢ ᠬᠠᠭᠠᠬᠤ ᠪᠤᠶᠤ 《 ᠠᠯᠪᠠᠳᠠᠯᠭ᠎ᠠ ᠪᠠᠷ ᠬᠠᠭᠠᠬᠤ 》 ᠶᠢ ᠲᠣᠭᠲᠠᠭᠠᠵᠤ ᠦᠷᠭᠦᠯᠵᠢᠯᠡᠨ᠎ᠡ ᠃ The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠨᠢ ᠬᠠᠭᠠᠯᠭ᠎ᠠ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠶ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠭᠰᠠᠨ ᠤ ᠳᠠᠷᠠᠭ᠎ᠠ ᠡᠳᠡᠭᠡᠷ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢ ᠬᠠᠭᠠᠬᠤ ᠪᠤᠶᠤ 《 ᠲᠣᠭᠲᠠᠭᠠᠬᠤ 》 ᠶᠢ ᠠᠯᠪᠠᠳᠠᠯᠭ᠎ᠠ ᠪᠠᠷ ᠬᠠᠭᠠᠬᠤ ᠶᠢᠨ ᠬᠠᠮᠲᠤ ᠦᠷᠭᠦᠯᠵᠢᠯᠡᠨ ᠦᠷᠭᠦᠯᠵᠢᠯᠡᠵᠦ ᠪᠣᠯᠤᠨ᠎ᠠ ᠃ The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. ᠳᠣᠣᠷᠠᠬᠢ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢ ᠬᠦᠴᠦᠨ ᠦᠭᠡᠶ ᠪᠣᠯᠭᠠᠬᠤ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠳᠤ ᠲᠠ 《 ᠬᠦᠴᠦᠨ ᠦᠭᠡᠶ ᠪᠣᠯᠭᠠᠬᠤ 》 ᠶᠢ ᠳᠠᠷᠤᠭᠰᠠᠨ ᠤ ᠳᠠᠷᠠᠭ᠎ᠠ ᠡᠳᠡᠭᠡᠷ ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢ ᠬᠠᠭᠠᠬᠤ ᠪᠤᠶᠤ 《 ᠲᠣᠭᠲᠠᠭᠠᠬᠤ 》 ᠶᠢ ᠠᠯᠪᠠᠳᠠᠯᠭ᠎ᠠ ᠪᠠᠷ ᠬᠠᠭᠠᠬᠤ ᠶᠢᠨ ᠬᠠᠮᠲᠤ ᠦᠷᠭᠦᠯᠵᠢᠯᠡᠨ ᠦᠷᠭᠦᠯᠵᠢᠯᠡᠵᠦ ᠪᠣᠯᠤᠨ᠎ᠠ ᠃ shut down ᠬᠠᠭᠠᠬᠤ restart ᠳᠠᠬᠢᠨ ᠰᠡᠩᠭᠡᠷᠡᠭᠦᠯᠬᠦ CharsWidget More ᠨᠡᠩ ᠠᠷᠪᠢᠨ ᠢᠯᠡᠷᠡᠭᠦᠯᠵᠡᠢ ᠃ ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them ᠭᠡᠷᠡᠯ ᠵᠢᠷᠤᠭ ᠢ ᠪᠠᠶᠢᠴᠠᠭᠠᠨ ᠰᠢᠯᠭᠠᠭᠰᠠᠨ ᠦᠭᠡᠢ ᠪᠣᠯ ᠳᠠᠬᠢᠨ ᠰᠣᠩᠭᠣᠭᠠᠷᠠᠢ ᠃ DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 IconEdit OK ᠪᠠᠳᠤᠯᠠᠬᠤ InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state ᠳᠡᠭᠦᠵᠢᠯᠡᠭᠦᠷ ᠦᠨ ᠵᠠᠭᠪᠤᠷ Close ᠬᠠᠭᠠᠬᠤ Welt status ᠨᠠᠭᠠᠬᠤ ᠵᠠᠭᠪᠤᠷ KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LightDMHelper failed to start session. ᠶᠠᠷᠢᠯᠴᠠᠯᠭ᠎ᠠ ᠵᠢ ᠡᠬᠢᠯᠡᠬᠦᠯᠵᠤ ᠴᠢᠳᠠᠭᠰᠠᠨ ᠦᠬᠡᠢ Login ᠳᠠᠩᠰᠠᠯᠠᠨ ᠤᠷᠤᠬᠤ Guest ᠵᠤᠷᠴᠢᠭᠴᠢ (᠎ᠨᠡᠷ᠎ᠡ ᠪᠤᠷᠤᠭᠤᠯᠠᠨ ᠨᠡᠪᠳᠡᠷᠡᠬᠦ) LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠶᠢ ᠰᠣᠯᠢᠨ᠎ᠠ Power ᠴᠠᠬᠢᠯᠭᠠᠨ ᠡᠭᠦᠰᠭᠡᠭᠴᠢ VirtualKeyboard ᠬᠡᠶᠢᠰᠬᠡᠷ ᠲᠠᠷᠤᠭᠤᠯ ᠤᠨ ᠫᠠᠨᠰᠠ᠃ Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession ᠬᠤᠷᠠᠯ ᠤᠨ ᠦᠭᠡ ᠰᠣᠯᠢᠨ᠎ᠠ ᠃ Power Information ᠳ᠋ᠢᠶᠠᠨ ᠢ ᠶᠢᠨ ᠰᠤᠷᠠᠭ ᠵᠠᠩᠭᠢ ᠃ Network ᠨᠧᠲ system-monitor ᠰᠢᠰᠲ᠋ᠧᠮ ᠤ᠋ᠨ ᠬᠢᠨᠠᠭᠤᠷ Reset Pwd ᠨᠢᠭᠤᠴᠠ ᠺᠣᠳ᠋ ᠢ᠋ ᠳᠠᠬᠢᠨ ᠳᠤᠬᠢᠷᠠᠭᠤᠯᠬᠤ %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". %1 ᠨᠢ ᠡᠨᠡ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠲᠦ ᠨᠢᠭᠡᠨᠲᠡ ᠭᠠᠷᠴᠤ ᠮᠡᠳᠡᠭᠰᠡᠨ ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢᠳ ᠬᠠᠳᠠᠭᠠᠯᠠᠭᠠᠳᠤᠶ ᠠᠭᠤᠯᠭ᠎ᠠ ᠪᠠᠨ ᠭᠡᠭᠡᠵᠦ ᠮᠡᠳᠡᠨ᠎ᠡ ᠂ ᠮᠥᠨ ᠬᠦ 《 ᠨᠤᠲᠠᠯᠠᠨ ᠲᠣᠭᠲᠠᠭᠠᠬᠤ 》 ᠶᠢ ᠮᠥᠨ ᠬᠦ ᠭᠦᠢᠴᠡᠳᠬᠡᠬᠦ ᠬᠡᠷᠡᠭᠲᠡᠶ ᠃ Restart ᠳᠠᠬᠢᠨ ᠡᠬᠢᠯᠡᠬᠦᠯᠬᠦ Multiple users are logged in at the same time.Are you sure you want to %1 this system? ᠣᠯᠠᠨ ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠬᠠᠮᠲᠤ ᠳᠤᠨᠢ ᠲᠡᠮᠳᠡᠭ᠍ᠯᠡᠵᠡᠢ ᠃ ᠲᠠ %1 ᠡᠨᠡ ᠰᠢᠰᠲ᠋ᠧᠮ ᠢ ᠲᠣᠭᠲᠠᠭᠠᠨ᠎ᠠ ᠤᠤ ? Reboot 重启 PowerOff 关机 Shut Down ᠮᠠᠰᠢᠨ ᠤᠨᠳᠠᠷᠠᠭᠠᠬᠤ LoginOptionsWidget Login Options ᠨᠡᠪᠳᠡᠷᠡᠬᠦ ᠰᠤᠩᠭᠤᠭᠳᠠᠬᠤᠨ Password ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ Other ᠪᠤᠰᠤᠳ FingerPrint ᠬᠤᠷᠤᠭᠤᠨ ᠤ᠋ ᠤᠷᠤᠮ FingerVein ᠬᠤᠷᠤᠭᠤᠨ ᠤ᠋ ᠨᠠᠮᠵᠢᠭᠤᠨ ᠰᠤᠳᠠᠰᠤ Iris ᠰᠤᠯᠤᠩᠭᠠᠨ ᠪᠦᠷᠬᠦᠪᠴᠢ Face ᠬᠥᠮᠦᠨ᠎ᠦ᠌ ᠨᠢᠭᠤᠷ ᠲᠠᠨᠢᠬᠤ VoicePrint ᠳᠠᠭᠤᠨ᠎ᠤ ᠢᠷᠠᠯᠵᠢ Ukey ᠠᠮᠤᠷ ᠲᠦᠪᠰᠢᠨ ᠪᠢᠲᠡᠭᠦᠮᠵᠢᠯᠡᠭᠦᠷ ᠃ QRCode ᠬᠣᠶᠠᠷ ᠬᠡᠮᠵᠢᠯᠳᠡᠳᠦ ᠺᠣᠳ᠋ Wechat 微信 Identify device removed! ᠬᠠᠷᠭᠤᠭᠤᠯᠵᠤ ᠱᠢᠯᠭᠠᠬᠤ ᠳᠦᠬᠦᠬᠡᠷᠦᠮᠵᠢ᠎ᠶᠢ ᠨᠢᠭᠡᠨᠳᠡ ᠱᠢᠯᠵᠢᠬᠦᠯᠦᠨ ᠬᠠᠰᠤᠪᠠ! MPRISSaverWidget No music detected ᠳᠠᠭᠤᠤ ᠬᠥᠭ᠍ᠵᠢᠮ ᠢ ᠪᠠᠢ᠌ᠴᠠᠭᠠᠵᠤ ᠣᠯᠬᠤ ᠦᠭᠡᠢ ᠃ Unknown ᠦᠯᠦ ᠮᠡᠳᠡᠬᠦ ᠬᠡᠪᠯᠡᠯ MPRISWidget Unknown 未知 MusicFileInformation Unknown singer ᠦᠯᠦ ᠮᠡᠳᠡᠬᠦ ᠳᠠᠭᠤᠴᠢᠨ Unknown album ᠦᠯᠦ ᠮᠡᠳᠡᠬᠦ ᠲᠤᠰᠭᠠᠢ ᠹᠢᠯᠢᠮ MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN ᠤᠲᠠᠰᠤᠲᠤ ᠲᠣᠣᠷ ᠰᠦᠯᠵᠢᠶ᠎ᠡ WLAN ᠤᠲᠠᠰᠤᠭᠤᠢ ᠬᠡᠰᠡᠭ ᠬᠡᠪᠴᠢᠶᠡᠨ᠎ᠦ᠌ ᠲᠣᠣᠷ NumbersWidget Return ᠡᠬᠡᠬᠦᠯᠬᠦ OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters ᠨᠢᠭᠤᠴᠠ ᠺᠣᠳ᠋ ᠨᠢ %1 ᠦᠰᠦᠭ ᠲᠡᠮᠳᠡᠭ᠎ᠡᠴᠡ ᠴᠥᠭᠡᠨ The password contains less than %1 character classes ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠲᠤ᠌ ᠠᠭᠤᠯᠠᠭᠰᠠᠨ ᠳᠡᠮᠳᠡᠭ ᠤ᠋ᠨ ᠳᠦᠷᠦᠯ ᠵᠦᠢᠯ ᠨᠢ %1 ᠳᠦᠷᠦᠯ ᠡᠴᠡ ᠪᠠᠭ᠎ᠠ The password is the same as the old one ᠬᠠᠭᠤᠴᠢᠨ ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠲᠠᠢ ᠠᠳᠠᠯᠢᠪᠳᠤᠷ The password contains the user name in some form ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠲᠤ᠌ ᠶᠠᠮᠠᠷ ᠨᠢᠭᠡ ᠬᠡᠯᠪᠡᠷᠢ ᠵᠢᠨ ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠵᠢᠨ ᠨᠡᠷ᠎ᠡ ᠠᠭᠤᠯᠠᠭᠳᠠᠪᠠ The password differs with case changes only ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠲᠤ᠌ ᠵᠦᠪᠬᠡᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ ᠪᠢᠴᠢᠯᠭᠡ ᠵᠢᠨ ᠬᠤᠪᠢᠷᠠᠯᠳᠠ ᠵᠢ ᠪᠠᠭᠳᠠᠭᠠᠨ᠎ᠠ The password is too similar to the old one ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠨᠢ ᠤᠤᠯ ᠤ᠋ᠨ ᠬᠢ ᠲᠠᠢ ᠳᠡᠩᠳᠡᠬᠦᠦ ᠠᠳᠠᠯᠢ The password is a palindrome ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠨᠢ ᠨᠢᠭᠡ ᠳᠡᠭᠰᠢ ᠬᠡᠮᠳᠦ ᠮᠠᠲᠸᠷᠢᠶᠠᠯ PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PowerListWidget Hibernate ᠢᠴᠡᠬᠡᠯᠡᠬᠦ᠌ Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠢ ᠬᠠᠭᠠᠬᠤ ᠪᠣᠯᠪᠠᠴᠤ ᠬᠡᠷᠡᠭᠯᠡᠭᠡᠨ ᠦ ᠬᠤᠷᠠᠯ ᠨᠡᠭᠡᠭᠡᠬᠦ ᠪᠠᠶᠢᠳᠠᠯ ᠢᠶᠠᠨ ᠪᠠᠷᠢᠮᠲᠠᠯᠠᠳᠠᠭ ᠃ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠢ ᠨᠡᠭᠡᠭᠡᠬᠦ ᠦᠶ᠎ᠡ ᠳᠦ ᠂ ᠴᠢᠨᠦ ᠰᠠᠯᠤᠭᠰᠠᠨ ᠪᠠᠶᠢᠳᠠᠯ ᠢᠶᠠᠨ ᠰᠡᠷᠭᠦᠭᠡᠵᠦ ᠪᠣᠯᠣᠨ᠎ᠠ Suspend ᠵᠤᠭ᠍ᠰᠤᠭᠠᠬᠤ The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Restart ᠳᠠᠬᠢᠨ ᠡᠬᠢᠯᠡᠬᠦᠯᠬᠦ Close all apps, and then restart your computer ᠪᠤᠢ ᠪᠥᠬᠥᠢ ᠬᠡᠷᠡᠭ᠍ᠯᠡᠭᠡ ᠶᠢ ᠬᠠᠭᠠᠵᠤ ᠂ ᠳᠠᠷᠠᠭ᠎ᠠ ᠨᠢ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠢ ᠳᠠᠬᠢᠨ ᠰᠡᠩᠭᠡᠷᠡᠭᠦᠯᠦᠨ᠎ᠡ Shut Down ᠮᠠᠰᠢᠨ ᠤᠨᠳᠠᠷᠠᠭᠠᠬᠤ The current user logs out of the system, terminates the session, and returns to the login page ᠣᠳᠣᠬᠠᠨ ᠳᠤ ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠰᠢᠰᠲ᠋ᠧᠮ ᠡᠴᠡ ᠬᠦᠴᠦᠨ ᠦᠭᠡᠶ ᠪᠣᠯᠭᠠᠵᠤ ᠂ ᠲᠡᠭᠦᠨ ᠦ ᠬᠤᠷᠠᠯ ᠤᠨ ᠦᠭᠡ ᠪᠡᠨ ᠳᠠᠭᠤᠰᠬᠠᠬᠤ ᠶᠢᠨ ᠬᠠᠮᠲᠤ ᠲᠡᠮᠳᠡᠭᠯᠡᠭᠰᠡᠨ ᠨᠢᠭᠤᠷ ᠲᠤ ᠪᠤᠴᠠᠵᠠᠶ Logout 注销 Power Off 关机 Close all apps, and then shut down your computer ᠪᠤᠢ ᠪᠥᠬᠥᠢ ᠬᠡᠷᠡᠭ᠍ᠯᠡᠭᠡ ᠶᠢ ᠬᠠᠭᠠᠵᠤ ᠂ ᠳᠠᠷᠠᠭ᠎ᠠ ᠨᠢ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠢ ᠬᠠᠭᠠᠬᠤ SwitchUser ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠶᠢ ᠰᠣᠯᠢᠨ᠎ᠠ LockScreen ᠳᠡᠯᠪᠡᠴᠢᠬᠦ Log Out ᠬᠦᠴᠦᠨ ᠦᠭᠡᠶ ᠪᠣᠯᠭᠠᠬᠤ The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠮᠠᠰᠢᠨ ᠢᠶᠠᠨ ᠨᠡᠭᠡᠭᠡᠬᠦ ᠪᠠᠶᠢᠳᠠᠯ ᠢᠶᠠᠨ ᠪᠠᠷᠢᠮᠲᠠᠯᠠᠳᠠᠭ ᠂ ᠭᠡᠪᠡᠴᠦ ᠴᠠᠬᠢᠯᠭᠠᠨ ᠬᠣᠷᠣᠭᠳᠠᠭᠤᠯᠭ᠎ᠠ ᠨᠡᠯᠢᠶᠡᠳ ᠪᠠᠭ᠎ᠠ ᠃ ᠬᠡᠷᠡᠭᠯᠡᠭᠡᠨ ᠦ ᠬᠤᠷᠠᠯ ᠨᠡᠭᠡᠭᠡᠬᠦ ᠪᠠᠶᠢᠳᠠᠯ ᠢᠶᠠᠨ ᠦᠷᠭᠦᠯᠵᠢᠯᠡᠭᠦᠯᠦᠭᠰᠡᠭᠡᠷ ᠂ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠢ ᠲᠦᠷᠭᠡᠨ ᠰᠡᠷᠢᠭᠡᠬᠦ ᠶᠢᠨ ᠬᠠᠮᠲᠤ ᠴᠢᠨᠦ ᠰᠠᠯᠤᠭᠰᠠᠨ ᠪᠠᠶᠢᠳᠠᠯ ᠢᠶᠠᠨ ᠰᠡᠷᠭᠦᠭᠡᠵᠦ ᠪᠣᠯᠣᠨ᠎ᠠ UpgradeThenRestart ᠰᠢᠨᠡᠳᠬᠡᠭᠰᠡᠨ ᠦ ᠳᠠᠷᠠᠭ᠎ᠠ ᠳᠠᠬᠢᠨ ᠰᠡᠩᠬᠡᠷᠡᠭᠦᠯᠪᠡ Close all apps, turn off your computer, and then turn your computer back on ᠪᠤᠢ ᠪᠥᠬᠥᠢ ᠬᠡᠷᠡᠭ᠍ᠯᠡᠭᠡ ᠶᠢ ᠬᠠᠭᠠᠵᠤ ᠂ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠢ ᠬᠠᠭᠠᠵᠤ ᠂ ᠳᠠᠷᠠᠭ᠎ᠠ ᠨᠢ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ ᠢ ᠳᠠᠬᠢᠨ ᠨᠡᠭᠡᠭᠡᠨ᠎ᠡ UpgradeThenShutdown ᠰᠢᠨᠡᠴᠢᠯᠡᠵᠤ ᠮᠠᠰᠢᠨ ᠤᠨᠳᠠᠷᠠᠭᠠᠬᠤ PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠫᠠᠷᠦᠭᠷᠡᠮ᠎ᠢ ᠨᠢᠭᠡᠨᠳᠡ ᠢᠳᠡᠪᠬᠢᠵᠢᠬᠦᠯᠪᠡ The screensaver is inactive. ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠫᠠᠷᠦᠭᠷᠡᠮ᠎ᠢ ᠢᠳᠡᠪᠬᠢᠵᠢᠬᠦᠯᠦᠬᠡ ᠦᠬᠡᠢ FingerPrint ᠬᠤᠷᠤᠭᠤᠨ ᠤ᠋ ᠤᠷᠤᠮ FingerVein ᠬᠤᠷᠤᠭᠤᠨ ᠤ᠋ ᠨᠠᠮᠵᠢᠭᠤᠨ ᠰᠤᠳᠠᠰᠤ Iris ᠰᠤᠯᠤᠩᠭᠠᠨ ᠪᠦᠷᠬᠦᠪᠴᠢ Face ᠬᠥᠮᠦᠨ᠎ᠦ᠌ ᠨᠢᠭᠤᠷ ᠲᠠᠨᠢᠬᠤ VoicePrint ᠳᠠᠭᠤᠨ᠎ᠤ ᠢᠷᠠᠯᠵᠢ Ukey ᠠᠮᠤᠷ ᠲᠦᠪᠰᠢᠨ ᠪᠢᠲᠡᠭᠦᠮᠵᠢᠯᠡᠭᠦᠷ ᠃ QRCode ᠬᠣᠶᠠᠷ ᠬᠡᠮᠵᠢᠯᠳᠡᠳᠦ ᠺᠣᠳ᠋ ResetPwdWidget Reset your password 重置密码 Security Question ᠠᠮᠤᠷ ᠲᠦᠪᠰᠢᠨ᠎ᠦ᠌ ᠠᠰᠠᠭᠤᠳᠠᠯ Kylin ID ᠴᠢ ᠯᠢᠨ ID Enter new password ᠰᠢᠨ᠎ᠡ ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠢ ᠣᠷᠣᠭᠤᠯᠤᠨ᠎ᠠ ᠃ Confirm your password ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠢ ᠨᠤᠲᠠᠯᠠᠨ ᠲᠣᠭᠲᠠᠭᠠᠨ᠎ᠠ ᠃ Inconsistency with pwd ᠰᠢᠨ᠎ᠡ ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠲᠠᠢ ᠢᠵᠢᠯ ᠪᠤᠰᠤ Confirm ᠲᠣᠭᠲᠠᠭᠠᠬᠤ Cancel ᠪᠣᠯᠢᠬᠤ Contains illegal characters! ᠳᠦᠷᠢᠮ ᠳ᠋ᠤ᠌ ᠨᠡᠢᠴᠡᠬᠦ ᠦᠬᠡᠢ ᠳᠡᠮᠳᠡᠭᠡᠳ ᠠᠭᠤᠯᠵᠤ ᠪᠠᠢᠨ᠎ᠠ! The service is abnormal ᠦᠢᠯᠡᠴᠢᠯᠡᠭᠡ ᠨᠢ ᠬᠡᠪ ᠦᠨ ᠪᠤᠰᠤ ᠃ The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) ᠣᠬᠣᠷ ᠮᠡᠳᠡᠭᠡᠨ ᠦ ᠰᠢᠯᠭᠠᠨ ᠪᠠᠶᠢᠴᠠᠭᠠᠭᠰᠠᠨ ᠨᠣᠮᠧᠷ ᠢ ᠰᠡᠭᠦᠯ ᠦᠨ ᠨᠣᠮᠧᠷ ᠨᠢ ア ᠬᠦᠷᠭᠡᠭᠰᠡᠨ ᠭᠠᠷ ᠤᠲᠠᠰᠤ %1 ( 5 ᠮᠢᠨᠦᠢᠲ᠋ ᠦᠨ ᠳᠣᠲᠣᠷ᠎ᠠ ᠬᠦᠴᠦᠨ ᠲᠡᠶ ) Failed to get the verification code ᠰᠢᠯᠭᠠᠨ ᠭᠡᠷᠡᠴᠢᠯᠡᠬᠦ ᠨᠣᠮᠧᠷ ᠣᠯᠬᠤ ᠳᠤ ᠢᠯᠠᠭᠳᠠᠪᠠ ᠃ The account does not exist, please re-enter it ᠳᠠᠩᠰᠠᠨ ᠡᠷᠦᠬᠡ ᠣᠷᠣᠰᠢᠬᠤ ᠦᠭᠡᠢ ᠂ ᠳᠠᠬᠢᠨ ᠣᠷᠣᠭᠤᠯᠬᠤ ᠪᠣᠯᠪᠠᠤ ᠃ The account does not exist, please complete the registration first ᠳᠠᠩᠰᠠᠨ ᠤ ᠨᠣᠮᠧᠷ ᠣᠷᠣᠰᠢᠬᠤ ᠦᠭᠡᠢ ᠂ ᠤᠷᠢᠳᠠᠪᠠᠷ ᠳᠠᠩᠰᠠᠯᠠᠭᠤᠯᠤᠭᠠᠷᠠᠢ ᠃ The user is already locked, contact the administrator to unlock it ᠲᠤᠰ ᠬᠡᠷᠡᠭ᠍ᠯᠡᠭ᠍ᠴᠢ ᠨᠢᠭᠡᠨᠲᠡ ᠲᠣᠭᠲᠠᠭᠠᠵᠠᠢ ᠂ ᠬᠠᠮᠢᠶᠠᠷᠤᠭᠴᠢ ᠲᠠᠢ ᠬᠠᠷᠢᠯᠴᠠᠵᠤ ᠣᠨᠢᠰᠤᠭ᠎ᠠ ᠪᠠᠨ ᠬᠦᠴᠦᠨ ᠦᠭᠡᠢ ᠪᠣᠯᠭᠠᠭᠠᠷᠠᠢ ᠃ If there are too many login errors, please try again in 5 minutes ᠪᠤᠷᠤᠭᠤ ᠲᠡᠮᠳᠡᠭ᠍ᠯᠡᠭᠡ ᠶᠢᠨ ᠤᠳᠠᠭ᠎ᠠ ᠬᠡᠲᠦᠷᠬᠡᠢ ᠠᠷᠪᠢᠨ ᠂ ᠲᠠᠪᠤᠨ ᠮᠢᠨᠦᠢᠲ᠋ ᠦᠨ ᠳᠠᠷᠠᠭ᠎ᠠ ᠳᠠᠬᠢᠨ ᠰᠢᠯᠭᠠᠭᠠᠷᠠᠢ ᠃ The SMS verification code is incorrect or has expired ᠣᠬᠣᠷ ᠮᠡᠳᠡᠭᠡᠨ ᠦ ᠭᠡᠷᠡᠴᠢᠯᠡᠭᠰᠡᠨ ᠨᠣᠮᠧᠷ ᠨᠢ ᠪᠤᠷᠤᠭᠤ ᠪᠤᠶᠤ ᠨᠢᠭᠡᠨᠲᠡ ᠬᠤᠭᠤᠴᠠᠭ᠎ᠠ ᠨᠢ ᠬᠡᠲᠦᠷᠡᠵᠡᠢ ᠃ Wrong username or password ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ ᠶᠢᠨ ᠨᠡᠷ᠎ᠡ ᠪᠤᠶᠤ ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠤᠨ ᠪᠤᠷᠤᠭᠤ ᠃ Password decryption error ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠨᠢ ᠨᠢᠭᠤᠴᠠ ᠪᠠᠷ ᠪᠤᠷᠤᠭᠤ ᠭᠠᠷᠬᠤ ᠶᠢ ᠲᠠᠶᠢᠯᠤᠨ᠎ᠠ ᠃ Verify Kylin ID Reset Password ᠾᠧ ᠯᠢᠨ ID ᠶᠢᠨ ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠢ ᠳᠠᠬᠢᠨ ᠲᠣᠬᠢᠷᠠᠭᠤᠯᠤᠨ᠎ᠠ ᠃ Answer the security questions and reset your password ᠠᠮᠤᠷ ᠲᠦᠪᠰᠢᠨ ᠦ ᠠᠰᠠᠭᠤᠳᠠᠯ ᠢ ᠬᠠᠷᠢᠭᠤᠯᠬᠤ ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠢ ᠳᠠᠬᠢᠨ ᠲᠣᠬᠢᠷᠠᠭᠤᠯᠤᠨ᠎ᠠ ᠃ One or more wrong answers ᠨᠢᠭᠡ ᠪᠤᠶᠤ ᠣᠯᠠᠨ ᠪᠤᠷᠤᠭᠤ ᠬᠠᠷᠢᠭᠤᠯᠲᠠ ᠃ S: tablet device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist ᠵᠢᠷᠤᠭ ᠪᠠᠢᠬᠤ ᠥᠬᠡᠢ Not connected to a network ᠲᠣᠣᠷ ᠰᠦᠯᠵᠢᠶ᠎ᠡ ᠶᠢ ᠬᠣᠯᠪᠣᠭᠰᠠᠨ ᠦᠭᠡᠢ ᠃ The weather update failed ᠠᠭᠤᠷ ᠠᠮᠢᠰᠭᠤᠯ ᠰᠢᠨᠡᠳᠭᠡᠬᠦ ᠳᠦ ᠢᠯᠠᠭᠳᠠᠪᠠ ᠃ Smart Saver ᠤᠶᠤᠨᠲᠤ ᠳᠡᠯᠬᠡᠴᠡ ᠶᠢᠨ ᠪᠠᠲᠤᠯᠠᠭ᠎ᠠ ᠃ Weather ᠴᠠᠭ ᠠᠭᠤᠷ Music ᠳᠠᠭᠤᠤ ᠬᠥᠭᠵᠢᠮ Photo Albun ᠳᠡᠪᠲᠡᠷ ᠃ Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View ᠵᠢᠷᠤᠭ ᠎ᠢ ᠬᠠᠷᠠᠨ᠎ᠠ ᠃ SleepTime You have rested: 您已休息: You have rested ᠲᠠ ᠨᠢᠭᠡᠨᠲᠡ ᠠᠮᠠᠷᠠᠵᠠᠢ SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer ᠬᠠᠷᠢᠭᠤᠯᠲᠠ ᠶᠢ ᠣᠷᠣᠭᠤᠯᠤᠨ᠎ᠠ ᠃ UserListWidget Login ᠳᠠᠩᠰᠠᠯᠠᠨ ᠤᠷᠤᠬᠤ Guest ᠵᠤᠷᠴᠢᠭᠴᠢ (᠎ᠨᠡᠷ᠎ᠡ ᠪᠤᠷᠤᠭᠤᠯᠠᠨ ᠨᠡᠪᠳᠡᠷᠡᠬᠦ) Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account ᠾᠷᠢᠨ ᠢᠦᠢᠳ ᠳᠠᠩᠰᠠᠨ ᠡᠷᠦᠬᠡ ᠳᠦ ᠣᠷᠣᠭᠤᠯᠤᠨ᠎ᠠ ᠃ Input password ᠨᠢᠭᠤᠴᠠ ᠺᠤᠳ᠋ ᠤᠷᠤᠭᠤᠯᠬᠤ Enter the verification code ᠭᠡᠷᠡᠴᠢᠯᠡᠬᠦ ᠨᠣᠮᠧᠷ ᠢ ᠣᠷᠣᠭᠤᠯᠤᠨ᠎ᠠ ᠃ Get verification code ᠰᠢᠯᠭᠠᠨ ᠪᠠᠶᠢᠴᠠᠭᠠᠭᠰᠠᠨ ᠨᠣᠮᠧᠷ ᠣᠯᠬᠤ ᠬᠡᠷᠡᠭᠲᠡᠶ ᠃ Reacquire(%1) ᠳᠠᠬᠢᠨ ᠣᠯᠪᠠ (%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 WeatherWidget Not connected to a network 网络未连接 delay how long to show lock ᠡᠳᠦᠢ ᠤᠷᠲᠤ ᠬᠤᠭᠤᠴᠠᠭ᠎ᠠ᠎ᠪᠠᠷ ᠣᠨᠢᠰᠣ᠎ᠶ᠋ᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠬᠦ has-lock if show lock ᠣᠨᠢᠰᠤᠯᠠᠬᠤ᠎ᠶ᠋ᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠪᠡᠯ main Start command for the ukui ScreenSaver. ᠳᠡᠯᠭᠡᠴᠡ ᠤᠨᠢᠰᠤᠯᠠᠬᠤ᠎ᠶᠢ ᠡᠬᠢᠯᠡᠬᠦᠯᠵᠤ ᠵᠠᠷᠯᠢᠭ᠎ᠲᠤ ᠬᠦᠷᠬᠡᠬᠦ᠌ lock the screen immediately ᠤᠳᠤᠬᠠᠨ ᠳᠡᠯᠬᠡᠴᠡ᠎ᠶᠢ ᠤᠨᠢᠰᠤᠯᠠᠬᠤ query the status of the screen saver ᠳᠡᠯᠭᠡᠴᠡ ᠤᠨᠢᠰᠤᠯᠠᠭᠰᠠᠨ ᠪᠠᠢᠳᠠᠯ᠎ᠢ ᠤᠯᠵᠠᠯᠠᠬᠤ unlock the screen saver ᠳᠡᠯᠭᠡᠴᠡᠨ᠎ᠦ ᠤᠨᠢᠰᠤ᠎ᠶᠢ ᠳᠠᠢᠯᠠᠬᠤ show the screensaver ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠫᠷᠦᠭᠷᠡᠮ᠎ᠢ ᠢᠯᠡᠷᠡᠬᠦᠯᠬᠦ᠌ show the session tools ᠬᠤᠷᠠᠯ ᠤᠨ ᠪᠠᠭᠠᠵᠢ ᠶᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠵᠡᠢ show the app block window ᠬᠡᠷᠡᠭᠯᠡᠭᠡᠨ ᠦ ᠴᠣᠩᠬᠣ ᠶᠢ ᠬᠣᠷᠢᠭᠯᠠᠬᠤ ᠶᠢ ᠬᠠᠷᠰᠢᠯᠠᠭᠰᠠᠨ ᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠦᠨ᠎ᠡ show the multiUsers block window multiUsrs ᠬᠡᠰᠡᠭ ᠴᠣᠩᠬᠣ ᠶᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠵᠡᠢ Backend for the ukui ScreenSaver. ukuui Screenar ᠤ᠋ᠨ\ ᠵᠢᠨ ᠠᠷᠤ ᠲᠠᠯ᠎ᠠ ukui-screensaver/i18n_ts/ug.ts0000664000175000017500000046411515172041106015253 0ustar fengfeng AgreementWindow I know بىلدىم AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry قايتا قايتا سىناش UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. .سىز 1% مىنۇت ئىچىدە قايتا سىناپ بېقىڭ Please try again in %1 seconds. .سىز 1% سېكۇنت ئىچىدە قايتا سىناپ بېقىڭ Account locked permanently. .ھېسابات مەڭگۈلۈك قۇلۇپلىنىدۇ Verify face recognition or enter password to unlock چىراي تونۇشنى دەلىللەش ياكى مەخپىي نومۇر كىرگۈزۈش ئارقىلىق قۇلۇپ ئېچىش Guest مېھمان (نامسىز تىزىملىتىش) Press fingerprint or enter password to unlock بارماق ئىزىنى بېسىش ياكى مەخپىي نومۇر كىرگۈزۈش ئارقىلىق قۇلۇپ ئېچىش Verify voiceprint or enter password to unlock ئاۋاز ئىزىنى دەلىللەش ياكى مەخپىي نومۇر كىرگۈزۈش ئارقىلىق قۇلۇپ ئېچىش Verify finger vein or enter password to unlock بارماق ئىزى ئارقىلىق ئېنىقلاش ياكى مەخپىي نومۇر كىرگۈزۈش ئارقىلىق قۇلۇپ ئېچىش Verify iris or enter password to unlock نى ئېنىقلاش ياكى مەخپىي نومۇر كىرگۈزۈش ئارقىلىق قۇلۇپ ئېچىش Iris Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock The authentication has been cancelled! Password: مەخپىي نومۇر Input Password مەخپىي نومۇرنى كىرگۈزۈڭ Username ئابۇنت نامى User name input error! ئىشلەتكۈچى ئىسمى خاتا كىرگۈزۈلدى login كىرىش Failed to verify %1, please enter password to unlock دەلىللەش 1% مەغلۇپ بولدى، مەخپىي نومۇر كىرگۈزۈڭ Unable to verify %1, please enter password to unlock دەلىللەشكە بولمىدى 1%، مەخپىي نومۇرنى كىرگۈزۈپ قۇلۇپنى ئېچىڭ Abnormal network بىنورمال تور Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. چىراي تونۇتۇشنىڭ ساقلاش ۋاقتى ئېشىپ كەتتى، يېڭىلاشنى چېكىپ ياكى مەخپىي نومۇر كىرگۈزۈپ قۇلۇپنى ئېچىڭ. FingerPrint بارماق ئىزى FingerVein بارماق ۋېناسى Iris رەڭدار پەردە Face ئادەم يۈزى VoicePrint ئاۋاز ئىزى Ukey مەخپىي ئاچقۇچ QRCode ئىككىلىك كود fingerprint بارماق ئىزى fingervein بارماق ۋېنا تومۇزى iris رەڭدار پەردە face ئادەم يۈزى voiceprint ئاۋاز ئىزى Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty مەخپىي نومۇرنى بوش قويۇشقا بولمايدۇ Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities دەلىللەش 1% مەغلۇپ بولدى، يەنە 2% دەلىللەش پۇرسىتىڭىز بار Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again دەلىللەش مەغلۇپ بولدى، قايتا سىناپ بېقىڭ Use the bound wechat scanning code or enter the password to unlock باغلانغان ئۈندىدارنى سىكاننېرلاش كودى ياكى مەخپىي نومۇرىنى كىرگۈزۈپ قۇلۇپ ئېچىش Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port 请将安全密钥插入USB端口 Password مەخپىي نۇمۇر Login كىرىش Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... زەرەتلىنىۋاتىدۇ... fully charged توك تولۇق قاچىلاندى PowerMode توك تەمىنلەش ئەندىزىسى BatteryMode باتارېيە ھالىتى BindDialog Close يېپىش Ok 确定 Cancel Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel ئەمەلدىن قالدۇرۇش Confirm جەزملەشتۈرۈش If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. تۆۋەندىكى پىروگراممىلار قايتىدىن قوزغىتىشنى توسۇپ قالسىڭىز بولىدۇ،سىز «ئەمەلدىن قالدۇرۇش»نى چېكىپ ئاندىن بۇ تەرتىپلەرنى ئېتىۋەتسىڭىز بولىدۇ. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. تۆۋەندىكى پىروگرامما ئېتىۋېتىلگەنلىكىنى توسۇپ قالىدۇ،سىز «ئەمەلدىن قالدۇرۇش»نى چېكىپ ئاندىن بۇ پىروگراممىلارنى ئېتىۋەتسىڭىز بولىدۇ. The following programs prevent suspend, you can click "Cancel" and then close these programs. تۆۋەندىكى پىروگرامما ئۇيقۇنى توسۇپ قالىدۇ،سىز «ئەمەلدىن قالدۇرۇش»نى چېكىپ ئاندىن بۇ پىروگراممىلارنى ئېتىۋەتسىڭىز بولىدۇ. The following programs prevent hibernation, you can click "Cancel" and then close these programs. تۆۋەندىكى پىروگرامما ئۇيقۇنى توسۇپ قالىدۇ،سىز «ئەمەلدىن قالدۇرۇش»نى چېكىپ ئاندىن بۇ پىروگراممىلارنى ئېتىۋەتسىڭىز بولىدۇ. The following programs prevent you from logging out, you can click "Cancel" and then close them. تۆۋەندىكى پىروگراممىلار بىكار قىلىنىشنى توسۇپ قالىدۇ،سىز «ئەمەلدىن قالدۇرۇش»نى چېكىپ ئاندىن بۇ پىروگراممىلارنى ئېتىۋەتسىڭىز بولىدۇ. The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. بۇ پروگراملار قايتا قوزغاشقا توسقۇنلۇق قىلىۋاتىدۇ. "بىكار قىل" نى بېسىپ بۇ پروگراملارنى يېپىپ قويۇڭ، ياكى "جەزىملە"نى بېسىپ مەجبۇرىي يېپىپ داۋاملاشتۇرۇڭ​ The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. بۇ پروگراملار كومپيۇتېرنى ئۆچۈرۈشكە توسقۇنلۇق قىلىۋاتىدۇ. "بىكار قىل" نى بېسىپ بۇ پروگراملارنى يېپىپ قويۇڭ، ياكى "جەزىملە" نى بېسىپ مەجبۇرىي يېپىپ داۋاملاشتۇرۇڭ.​​​ The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. ​بۇ پروگراملار تىزىمدىن چىقىشقا توسقۇنلۇق قىلىۋاتىدۇ. "بىكار قىل" نى بېسىپ بۇ پروگراملارنى يېپىپ قويۇڭ، ياكى "جەزىملە" نى بېسىپ مەجبۇرىي يېپىپ داۋاملاشتۇرۇڭ.​​​ shut down restart The following program is running to prevent the system from reboot! !سېستىمىنىڭ قايتا قوزغىلىشىنىڭ ئالدىنى ئېلىش ئۈچۈن تۆۋەندىكى پروگرامما ئىجرا بولىۋاتىدۇ The following program is running to prevent the system from shutting down! !تۆۋەندىكى پروگرامما سىستېمىنىڭ تاقىلىپ قېلىشىنىڭ ئالدىنى ئېلىش ئۈچۈن يۈگۈرۈۋاتىدۇ The following program is running to prevent the system from suspend! !سېستىمىنىڭ توختىشىنىڭ ئالدىنى ئېلىش ئۈچۈن تۆۋەندىكى پروگرامما ئىجرا قىلىنىۋاتىدۇ The following program is running to prevent the system from hibernate! !سېستىمىنىڭ تىزگىنلەپ قېلىشىنىڭ ئالدىنى ئېلىش ئۈچۈن تۆۋەندىكى پروگرامما ئىجرا بولىۋاتىدۇ CharsMoreWidget &&?! &&?! CharsWidget More تېخىمۇ كۆپ ABC ABC 123 123 ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 EngineDevice yes شۇنداق no ياق Yes شۇنداق No ياق %1% available, charged %1% نى ئىشلەتكىلى بولىدۇ، ھەق ئېلىپ بولدى. Left %1h %2m (%3%) سول تەرەپ %1 h %2 m (%3 %) %1% available %1% ئىشلەتكىلى بولىدۇ Left %1h %2m to full سولغا %1 h %2 m لىق توشتى charging (%1%) زەرەتلىنىۋاتىدۇ (%1) %1 waiting to discharge (%2%) %1 دوختۇرخانىدىن چىقىشنى ساقلاۋاتىدۇ (%2%) %1 waiting to charge (%2%) %1 توك قاچىلاشنى ساقلاۋاتىدۇ (%2 AC adapter پىكىر ئالماشتۇرۇش ماسلاشتۇرغۇچ Laptop battery خاتىرە كومپيۇتېر باتارېيەسى UPS UPS Monitor كۆزەتكۈچ Mouse مائۇس Keyboard كۇنۇپكا تاختىسى PDA PDA Cell phone يان تېلېفون Media player ۋاسىتە قويغۇچ Tablet تاختا Computer كومپيۇتېر unrecognised پەرق ئېتىلمىدى IconEdit OK 提交 InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state ۋاقىتلىق توختىتىلغان ھالەت Close يېپىش Welt status گىرۋەك ھالىتى KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LettersWidget &&?! &&?! 123 123 LightDMHelper failed to start session. دىيالوگنى باشلاش مەغلۇپ بولدى Login كىرىش Guest مېھمان (نامسىز تىزىملىتىش) LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser 切换用户 Power 电源 VirtualKeyboard 虚拟键盘 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 system-monitor سىستېمىلىق كۆزەتكۈچ Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Multiple users are logged in at the same time.Are you sure you want to %1 this system? بىر قانچە ئابۇنت بىرلا ۋاقىتتا سىستېمىغا كىردى، بۇ %1 سىستېمىنى ئىشلىتىشنى جەزملەشتۈرەمسىز؟ Restart قايتا قوزغىتىش SwitchSession Power Information Network 网络 Shut Down تېلېفوننى ئېتىۋېتىش LoginOptionsWidget Login Options كىرىش تاللانمىلىرى Password مەخپىي نومۇر Other باشقا FingerPrint بارماق ئىزى FingerVein بارماق ۋېناسى Iris رەڭدار پەردە Face ئادەم يۈزى VoicePrint ئاۋاز ئىزى Ukey مەخپىي ئاچقۇچ QRCode ئىككىلىك كود Wechat 微信 Identify device removed! !ئۈسكۈنىنى پەرقلەندۈرۈش چىقىرىۋېتىلدى MPRISSaverWidget No music detected Unknown نامەلۇم MPRISWidget Unknown نامەلۇم MusicFileInformation Unknown singer Unknown album MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN سىملىق تور WLAN تار دائىرىلىك سىمسىز تور NumbersWidget &&?! &&?! Return قايتىش OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate ئۈچەككە كىرىش Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left كومپيۇتېرنى ئېتىك، ئەمما ئەپ ئوچۇق ھالەتنى ساقلايدۇ. كومپيۇتېر ئېچىلغان چاغدا، سىز ئايرىلغان چاغدىكى ھالەتكە قايتىدۇ Suspend توختىتىپ قويۇش The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off كومپيۇتېر ئوچۇق ھالەتنى ساقلايدۇ، ئەمما قۇۋۋەت سەرپىياتى بىر قەدەر تۆۋەن. بۇ ئەپ ئوچۇق ھالەتتە تۇرىدۇ، تېز سۈرئەتتە ئويغىنىدۇ ھەمدە سىز توختىغان جايغا قايتۇرغىلى بولىدۇ The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off كومپيۇتېر ئوچۇق ھالەتنى ساقلايدۇ، ئەمما قۇۋۋەت سەرپىياتى بىر قەدەر تۆۋەن. بۇ ئەپ ئوچۇق ھالەتتە تۇرىدۇ، تېز سۈرئەتتە ئويغىنىدۇ ھەمدە سىز توختىغان جايغا قايتۇرغىلى بولىدۇ Restart قايتا قوزغىتىش Close all apps, and then restart your computer بارلىق ئەپنى ئۆچۈرۈپ، ئاندىن كومپيۇتېرنى قايتا قوزغىتىش Shut Down تېلېفوننى ئېتىۋېتىش Log Out بىكار قىلىۋەتمەك The current user logs out of the system, terminates the session, and returns to the login page نۆۋەتتە ئابونتلار سىستېمىدىن بىكار قىلىنىپ، سۆزلىشىشنى ئاخىرلاشتۇردى ھەمدە تىزىملىتىش كۆرۈنۈشىگە قايتىدۇ. SwitchUser خېرىدار ئالماشتۇرماق LockScreen قۇلۇپ ئېكرانى UpgradeThenRestart يېڭىلىغاندىن كېيىن قايتىدىن قوزغىتىش UpgradeThenShutdown يېڭىلانغاندىن كېيىن ئاپپارات ئېتىلىدۇ Logout بىكار قىلىۋەتمەك Power Off 关机 Close all apps, and then shut down your computer بارلىق ئەپنى ئۆچۈرۈپ، ئاندىن كومپيۇتېرنى ئۆچۈرۈش Close all apps, turn off your computer, and then turn your computer back on بارلىق ئەپنى ئۆچۈرۈپ، كومپيۇتېرنى ئۆچۈرۈپ، ئاندىن كومپيۇتېرنى قايتا ئېچىش PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. .ئېكران قوغداش سىستېمىسى ھەرىكەتچان ھالەتتە The screensaver is inactive. .ئېكران قوغداش سىستېمىسى ھەرىكەتسىز ھالەتتە FingerPrint بارماق ئىزى FingerVein بارماق ۋېناسى Iris رەڭدار پەردە Face ئادەم يۈزى VoicePrint ئاۋاز ئىزى Ukey مەخپىي ئاچقۇچ QRCode ئىككىلىك كود Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm 确认 Cancel Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist رەسىم مەۋجۇت ئەمەس Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View قاراش Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested: 您已休息: You have rested ئارام ئالدىڭىز SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer UserListWidget Login كىرىش Guest مېھمان (نامسىز تىزىملىتىش) Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account Input password 输入密码 Enter the verification code Get verification code Reacquire(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 Widget Widget كىچىك قۇرۇلما LoadPlugin ئۇششاق زاپچاس action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock قانچىلىك ۋاقىت قۇلۇپلانغانلىقىنى كۆرسىتىش has-lock if show lock ئەگەر قۇلۇپلاشنى كۆرسەتمەكچى بولسا main Start command for the ukui ScreenSaver. ئېكران قوغداش پىروگراممىسىنىڭ بۇيرۇقىنى قوزغىتىش ukui lock the screen immediately ئېكراننى دەرھال قۇلۇپلاش query the status of the screen saver ئېكران قوغداش پىروگراممىسىنىڭ ھالىتىنى سۈرۈشتۈرۈش unlock the screen saver ئېكران قوغداش پىروگراممىسىنىڭ قۇلۇپىنى ئېچىش show the screensaver ئېكران قوغداش پىروگراممىسىنى كۆرسىتىش show blank and delay to lock,param:idle/lid/lowpower idle/lid/lowpower :قۇرۇقنى كۆرسىتىش ۋە قۇلۇپلاشنى كېچىكتۈرۈش، پارامېتىرى show the session tools show the switchuser window show the app block window show the multiUsers block window Backend for the ukui ScreenSaver. .ئېكران قوغداش پىروگراممىسىنىڭ كەينى ئېغىزى ukui lock the screen by startup قوزغاتقاندا ئېكراننى قۇلۇپلاش Dialog for the ukui ScreenSaver. .ئېكران قوغداش پىروگراممىسىنىڭ دىيالوگ رامكىسى ukui activated by session idle signal بىكار سىگنال ئارقىلىق ئاكتىپلاشتۇرۇش lock the screen and show screensaver immediately ئېكراننى قۇلۇپلاش ھەمدە دەرھال ئېكران قوغداش پىروگراممىسىنى كۆرسىتىش show screensaver immediately ئېكران قوغداش پىروگراممىسىنى دەرھال كۆرسىتىش show blank screensaver immediately and delay time to show lock قۇرۇق ئېكران قوغداش پىروگراممىسىنى دەرھال كۆرسىتىش ھەمدە قۇلۇپلاش ۋاقتىنى كېچىكتۈرۈپ كۆرسىتىش show blank screensaver immediately and if lock ئەگەر قۇلۇپلانسا، قۇرۇق ئېكران قوغداش پىروگراممىسىنى دەرھال كۆرسىتىش show switch user window switch to user and show window Screensaver for ukui-screensaver ئېكران قوغداش پىروگراممىسىنىڭ ئېكران قوغداش پىروگراممىسى ukui show on root window يىلتىز كۆزنەكتە كۆرسىتىش show on window. .كۆزنەكتە كۆرسىتىش window id id كۆزنەك switchtouser switch to someone user ukui-screensaver/i18n_ts/zh_CN.ts0000664000175000017500000034732215172041106015641 0ustar fengfeng AgreementWindow I know 我已知晓 AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry 重试 UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 密码提示:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. 请%1分钟后再试 Please try again in %1 seconds. 请%1秒后再试 Account locked permanently. 账号已被永久锁定 Verify face recognition or enter password to unlock 验证人脸识别或输入密码解锁 Guest 游客 Press fingerprint or enter password to unlock 按压指纹或输入密码解锁 Verify voiceprint or enter password to unlock 验证声纹或输入密码解锁 Verify finger vein or enter password to unlock 验证指静脉或输入密码解锁 Verify iris or enter password to unlock 验证虹膜或输入密码解锁 Enter username 输入登录用户名 Facial recognition to unlock 识别人脸解锁 Fingerprint recognition to unlock 识别指纹解锁 Voiceprint recognition to unlock 识别声纹解锁 Finger vein recognition to unlock 识别指静脉解锁 Iris recognition to unlock 识别虹膜解锁 Use the bound wechat scanning code to unlock 微信扫码解锁 wechat 微信 Use the bound %1 scanning code to unlock %1扫码解锁 The authentication has been cancelled! 认证被取消! Password: 密码: Input Password 输入密码 Username 用户名: User name input error! 用户名输入错误 login 登录 Failed to verify %1, please enter password to unlock 验证%1失败,请输入密码解锁 Unable to verify %1, please enter password to unlock 无法验证%1,请输入密码解锁 Abnormal network 网络异常 Acquisition failure 获取失败 Face recognition waiting time out, please click refresh or enter the password to unlock. 人脸识别等待超时,请点击刷新或输入密码解锁 FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 fingerprint 指纹 fingervein 指静脉 iris 虹膜 face 人脸 voiceprint 声纹 Two-Factor OTP Dynamic Password Authentication 双因子OTP动态口令认证 Two-Factor Security Key Authentication 双因子安全密钥认证 Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty 密码不能为空 Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities 验证%1失败,您还有%2次尝试机会 Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again 认证失败,请重试 Use the bound wechat scanning code or enter the password to unlock 使用绑定的微信扫码或输入密码解锁 Enter the ukey password 输入安全密钥密码 Insert the ukey into the USB port 请将安全密钥插入USB端口 Password 密码 Login 登录 Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... 正在充电... fully charged 已充满电 PowerMode 电源模式 BatteryMode 电池模式 No battery detected 未检测到电池 BindDialog Close 关闭 Ok 确定 Cancel 取消 Mfa authentication is on, please bind OTP code 系统已启用双因子认证,请绑定二次认证口令。 Go to Bind 去绑定 Bind 绑定 OTP added successfully 动态口令添加成功 BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: 当前设备: Identify failed, Please retry. 识别失败,请重试 BiometricDevicesWidget Please select the biometric device 请选择生物设备 Device type: 设备类型: Device name: 设备型号: OK 确定 BlockWidget Cancel 取消 Confirm 确认 If you do not perform any operation, the system will automatically %1 after %2 seconds. 如果您不执行任何操作,系统将在%2秒后自动%1 The following programs prevent restarting, you can click "Cancel" and then close these programs. 以下程序阻止重启,您可以点击“取消”然后关闭这些程序。 The following programs prevent the shutdown, you can click "Cancel" and then close these programs. 以下程序阻止关机,您可以点击“取消”然后关闭这些程序。 The following programs prevent suspend, you can click "Cancel" and then close these programs. 以下程序阻止睡眠,您可以点击“取消”然后关闭这些程序。 The following programs prevent hibernation, you can click "Cancel" and then close these programs. 以下程序阻止休眠,您可以点击“取消”然后关闭这些程序。 The following programs prevent you from logging out, you can click "Cancel" and then close them. 以下程序阻止注销,您可以点击“取消”然后关闭这些程序。 The following programs prevent you restarting, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. 以下程序阻止重启,您可以点击“取消”然后关闭这些程序,或点击“确定”强制关闭并继续。 The following programs prevent you shutdown, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. 以下程序阻止关机,您可以点击“取消”然后关闭这些程序,或点击“确定”强制关闭并继续。 The following programs prevent you from logging out, you can click "Cancel" and then close them, or click 'OK' to force close and proceed. 以下程序阻止注销,您可以点击“取消”然后关闭这些程序,或点击“确定”强制关闭并继续。 shut down 关机 restart 重启 CharsWidget More 更多 ConfForm edit network 网络属性 LAN name: 网络名称: Method: 编辑IP设置: Address: IP地址: Netmask: 子网掩码: Gateway: 默认网关: DNS 1: 首选DNS: DNS 2: 备选DNS: Edit Conn 网络设置 Auto(DHCP) 自动(DHCP) Manual 手动 Cancel 返回 Save 确定 Ok 确定 Can not create new wired network for without wired card 缺少有线网卡 无法新建网络 New network already created 已创建新的有线网络 New network settings already finished 新的网络配置已经完成 Edit Network 网络属性 Add Wired Network 新建有线网络 create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective 新的设置已经生效 There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them 未检测到照片,请重新选择 DeviceType FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 DigitalAuthDialog LoginByUEdu 请输入锁屏密码 now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? 忘记密码? SetNewUEduPWD 设置新锁屏密码 ConfirmNewUEduPWD 确认新锁屏密码 The two password entries are inconsistent, please reset 两次密码输入不一致,请重设 Password entered incorrectly, please try again 密码输入错误,请重试 clear 清空 DlgConnHidWifi Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Cancel 返回 Connect 确定 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Allow automatic PAC pro_visioning 自动PAC配置: PAC file PAC文件: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Anonymous 匿名 Authenticated 已认证 Both 两者兼用 DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 PEAP version PEAP版本: Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... Automatic 自动 Version 0 版本 0 Version 1 版本 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Identity 身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 User certificate 用户证书: User certificate password 用户证书密码: User private key 用户私钥: User key password 用户密钥密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Authentication 认证: Anonymous identity 匿名身份: Domain 域名: CA certificate CA 证书: CA certificate password CA 证书密码: No CA certificate is required 不需要CA证书 Inner authentication 内部认证: Username 用户名: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Tunneled TLS 隧道 TLS Protected EAP (PEAP) 受保护的 EAP Choose from file 从文件选择... DlgConnHidWifiWep Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add hidden Wi-Fi 加入无线网络 Connection 连接设置: Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Key 密钥 WEP index WEP 检索 Authentication 认证: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 1(default) 1(默认) Open System 开放式系统 Shared Key 共享密钥 DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Add Hidden Wi-Fi 加入无线网络 Connection 连接设置: Wi-Fi name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Connect 连接 C_reate… 新建... None WPA & WPA2 Personal WPA 及 WPA2 个人 WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Create Hotspot 创建个人热点 Network name 网络名称: Wi-Fi security Wi-Fi 安全性: Password 密码: Cancel 返回 Ok 确定 None WPA & WPA2 Personal WPA 及 WPA2 个人 IconEdit OK 提交 InputInfos Service exception... 服务异常,重试中... Invaild parameters... 参数异常,重试中... Unknown fault:%1 未知错误:%1 Recapture(60s) 重新获取(60s) Recapture(%1s) 重新获取(%1s) Get code 获取验证码 KBTitle Suspended state 悬浮状态 Close 关闭 Welt status 贴边状态 KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm 网络工具 LAN 有线网络 Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced 设置网络 Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot 个人热点 FlyMode 飞行模式 Show MainWindow 显示网络连接界面 Inactivated LAN 未激活 Inactivated WLAN 未激活 Other WLAN 其他 WLAN 无线局域网 No wireless card detected 未检测到无线网卡 Activated LAN 已激活 Activated WLAN 已激活 Not connected 未连接任何网络 Disconnected 未连接 No Other Wired Network Scheme 列表中无其他有线网络 Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme 未检测到其他无线网络 Wired net is disconnected 断开有线网络 Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card 请确认Wi-Fi密码或无线设备 Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list 列表暂无可连接网络 NetOn, 已连接, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success 连接有线网络成功 Conn Ethernet Fail 连接有线网络失败 Conn Wifi Success 连接无线网络成功 LightDMHelper failed to start session. 启动会话失败。 Login 登录 Guest 游客 LockWidget Date 日期 Time 时间 Guest 游客 SwitchUser 切换用户 Power 电源 VirtualKeyboard 虚拟键盘 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? LAN 有线网络 WLAN 无线局域网 SwitchSession 切换会话 Power Information 电源信息 Network 网络 system-monitor 系统监视器 Reset Pwd 重置密码 %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". %1可能导致已登录此电脑的用户丢失尚未存储的内容,仍要执行请点击“确认”。 Restart 重启 Multiple users are logged in at the same time.Are you sure you want to %1 this system? 同时有多个用户处于登录状态,你确定要%1吗? Reboot 重启 PowerOff 关机 Shut Down 关机 LoginOptionsWidget Login Options 登录选项 Password 密码 Other 其他 FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 Wechat 微信 Identify device removed! 校验设备已移除! MPRISSaverWidget No music detected 未检测到歌曲 Unknown 未知 MPRISWidget Unknown 未知 MusicFileInformation Unknown singer 未知音乐 Unknown album 未知专辑 MyLineEdit Verification code 短信验证码 MyNetworkWidget LAN 有线网络 WLAN 无线局域网 NumbersWidget Return 返回 OneConnForm Form -- Automatically join the network 自动加入该网络 Input password 输入密码 Config 设置 Connect 连接 Disconnect 断开连接 Input Password... 输入密码... Connect to Hidden Wi-Fi Network 连接到隐藏 Wi-Fi 网络 Signal: 信号强度: Public 开放 Safe 安全 Rate 速率 None WiFi Security: WiFi安全性: MAC: 物理地址: Conn Wifi Failed 连接无线网络失败 OneLancForm Form -- Config 设置 Connect 连接 Disconnect 断开连接 No Configuration 未配置 IPv4: IPv4地址: IPv6: IPv6地址: BandWidth: 带宽: MAC: 物理地址: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters 密码少于%1个字符 The password contains less than %1 character classes 密码包含少于%1种字符 The password is the same as the old one 新旧密码一样 The password contains the user name in some form 密码包含用户名 The password differs with case changes only The password is too similar to the old one 新密码与旧密码太过相似 The password is a palindrome PhoneAuthWidget Verification by phoneNum 手机号验证 「 Use SMS to verification 」 「 请使用绑定该账户手机号验证 」 commit 提交 Network not connected~ 系统未联网,请检查网络连接~ Network unavailable~ 网络状态差,请检查网络连接~ Verification Code invalid! 验证码失效 Verification Code incorrect.Please retry! 验证码错误!请填写正确的验证码! Failed time over limit!Retry after 1 hour! 验证码错误次数超过10次,1小时后再试 verifaction failed! 手机验证失败 PowerListWidget Hibernate 休眠 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态,当打开电脑时,可以恢复到你离开的状态 Suspend 睡眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Restart 重启 Close all apps, and then restart your computer 关闭所有应用,然后重启电脑 Shut Down 关机 The current user logs out of the system, terminates the session, and returns to the login page 当前用户从系统中注销,结束其会话并返回登录界面 Logout 注销 Power Off 关机 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑 SwitchUser 切换用户 LockScreen 锁屏 Log Out 注销 The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少,应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态 UpgradeThenRestart 更新并重启 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑 UpgradeThenShutdown 更新并关机 PowerManager lock 锁定 SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out 注销 Restart 重启 Power Off 关机 Close all apps, turn off your computer, and then turn your computer back on 关闭所有应用,关闭电脑,然后重新打开电脑。 Close all apps, and then shut down your computer 关闭所有应用,然后关闭电脑。 Shut Down 关机 Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left 关闭电脑,但是应用会一直保持打开状态。当打开电脑时,可以恢复到你离开的状态。 Hibernate 休眠 The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off 电脑保持开机状态,但耗电较少。应用会一直保持打开状态,可快速唤醒电脑并恢复到你离开的状态。 Suspend 睡眠 Sleep 休眠 QObject The screensaver is active. 屏幕保护程序已激活 The screensaver is inactive. 屏幕保护程序未激活 FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸识别 VoicePrint 声纹 Ukey 安全密钥 QRCode 二维码 ResetPwdWidget Reset your password 重置密码 Security Question 安全问题 Kylin ID 麒麟ID Enter new password 输入新密码 Confirm your password 确认密码 Inconsistency with pwd 与新密码不一致 Confirm 确认 Cancel 取消 Contains illegal characters! 存在非法字符 The service is abnormal 服务异常 The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) 短信验证码已发送至尾号%1的手机(5分钟内有效) Failed to get the verification code 获取验证码失败 The account does not exist, please re-enter it 账户不存在,请重新输入 The account does not exist, please complete the registration first 账号不存在,请先完成注册 The user is already locked, contact the administrator to unlock it 该用户已经锁定,请联系管理员解除锁定 If there are too many login errors, please try again in 5 minutes 登录错误次数过多,请5分钟后重试 The SMS verification code is incorrect or has expired 短信验证码错误或已过期 Wrong username or password 用户名或密码错误 Password decryption error 密码解密出错 Verify Kylin ID Reset Password 验证麒麟ID重置密码 Answer the security questions and reset your password 回答安全问题重置密码 One or more wrong answers 一个或多个错误答案 S: tablet device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist 图片不存在 Not connected to a network 网络未连接 The weather update failed 天气更新失败 Smart Saver 智慧屏保 Weather 天气 Music 音乐 Photo Albun 相册 Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification 您有新的消息 View 预览 SleepTime You have rested: 您已休息: You have rested 您已休息 SureWindow Form -- Cancel 取消 Confirm 确认 Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! 以下程序正在运行,阻止系统进入睡眠! The following program is running to prevent the system from hibernate! 以下程序正在运行,阻止系统进入休眠! The following program is running to prevent the system from shutting down! 以下程序正在运行,阻止系统关机! The following program is running to prevent the system from reboot! 以下程序正在运行,阻止系统重启! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD 密码登录 Wechat 微信登录 TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old 新密码与原密码相同 Reset password error:%1 重置密码失败:%1 Please scan by correct WeChat 请使用正确的微信扫码 Cancel 取消 Back 返回 Skip 跳过 TalksWidget Enter your answer 输入答案 UserListWidget Login 登录 Guest 游客 Utils kylin network applet desktop message 麒麟网络工具信息提示 VerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 VerifyKyidWidget Enter your Kylin ID account 输入麒麟ID账户 Input password 输入密码 Enter the verification code 输入验证码 Get verification code 获取验证码 Reacquire(%1) 重新获取(%1) VerticalVerificationWidget Please scan by bound WeChat 请使用已绑定的微信扫码 WeChatAuthDialog Login by wechat 微信登录 Verification by wechat 微信验证 「 Use registered WeChat account to login 」 「 使用已注册的微信号登录 」 「 Use bound WeChat account to verification 」 「 请使用绑定该账号的微信验证 」 Network not connected~ 系统未联网,请检查网络连接~ Scan code successfully 扫码成功 Timeout!Try again! 超时!请重新扫码! Login failed 登录失败 WeatherWidget Not connected to a network 网络未连接 delay how long to show lock 多久显示锁屏 has-lock if show lock 是否锁屏 main Start command for the ukui ScreenSaver. 启动锁屏到命令 lock the screen immediately 马上锁定屏幕 query the status of the screen saver 获取锁屏状态 unlock the screen saver 解锁屏幕 show the screensaver 显示屏保 show the session tools 显示会话管理 show the app block window 显示应用阻塞关机界面 show the multiUsers block window 显示多用户阻塞关机界面 Backend for the ukui ScreenSaver. ukui-screensaver/i18n_ts/de.ts0000664000175000017500000045376615172041106015242 0ustar fengfeng AgreementWindow I know Ich weiß AuthDialog More Devices 选择其他设备 Biometric 使用生物识别认证 Password 使用密码认证 Retry Wiederholen UnLock 解锁 Slide to unlock 向上滑动解锁 You have %1 unread message 您有%1条未读消息 LoggedIn 已登录 Password: 密码: Password Prompt:%1 Account locked %1 minutes due to %2 fail attempts 账户锁定%1分钟由于%2次错误尝试 Please try again in %1 minutes. Versuchen Sie es in %1 Minuten erneut. Please try again in %1 seconds. Bitte versuchen Sie es in %1 Sekunden erneut. Account locked permanently. Das Konto wurde dauerhaft gesperrt. Verify face recognition or enter password to unlock Überprüfen Sie die Gesichtserkennung oder geben Sie das Passwort ein, um zu entsperren Guest 游客 Press fingerprint or enter password to unlock Drücken Sie den Fingerabdruck oder geben Sie das Passwort ein, um zu entsperren Verify voiceprint or enter password to unlock Überprüfen Sie den Stimmabdruck oder geben Sie das Passwort ein, um zu entsperren Verify finger vein or enter password to unlock Überprüfen Sie die Fingervene oder geben Sie das Passwort ein, um zu entsperren Verify iris or enter password to unlock Überprüfen Sie die Iris oder geben Sie das Passwort ein, um zu entsperren Enter username Facial recognition to unlock Fingerprint recognition to unlock Voiceprint recognition to unlock Finger vein recognition to unlock Iris recognition to unlock wechat Use the bound %1 scanning code to unlock The authentication has been cancelled! Password: Input Password Passwort eingeben Username Nutzername User name input error! login Failed to verify %1, please enter password to unlock %1 konnte nicht verifiziert werden, bitte geben Sie das Kennwort zum Entsperren ein Unable to verify %1, please enter password to unlock %1 kann nicht verifiziert werden, bitte geben Sie das Kennwort zum Entsperren ein Abnormal network Ungewöhnliches Netzwerk Acquisition failure Face recognition waiting time out, please click refresh or enter the password to unlock. FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey QRCode QRCode fingerprint fingervein iris face voiceprint Two-Factor OTP Dynamic Password Authentication Two-Factor Security Key Authentication Use the bound wechat scanning code or enter the password to log in 使用绑定的微信扫码或输入密码登录 Password cannot be empty Das Kennwort darf nicht leer sein. Failed to verify %1, please enter password. 验证%1失败,请输入密码. Unable to verify %1, please enter password. 无法验证%1,请输入密码. Failed to verify %1, you still have %2 verification opportunities %1 konnte nicht verifiziert werden, Sie haben immer noch %2 Überprüfungsmöglichkeiten Biometric/code scan authentication failed too many times, please enter the password. 生物/扫码验证失败达最大次数,请使用密码解锁 Bioauth/code scan authentication failed, you still have %1 verification opportunities 生物/扫码验证失败,您还有%1次尝试机会 NET Exception 网络异常 Password Incorrect, Please try again 密码错误,请重试 Authentication failure,there are still %1 remaining opportunities 认证失败,还剩%1次尝试机会 Please enter your password or enroll your fingerprint 请输入密码或者录入指纹 Authentication failure, Please try again Authentifizierungsfehler, Bitte versuchen Sie es erneut Use the bound wechat scanning code or enter the password to unlock Verwenden Sie den gebundenen Wechat-Scan-Code oder geben Sie das Passwort zum Entsperren ein Enter the ukey password Geben Sie das ukey-Passwort ein Insert the ukey into the USB port Stecken Sie den Ukey in den USB-Anschluss Password Passwort Login Einloggen Biometric Authentication 生物识别认证 Password Authentication 密码认证 Other Devices 其他设备 Too many unsuccessful attempts,please enter password. 指纹验证失败达最大次数,请使用密码登录 Fingerprint authentication failed, you still have %1 verification opportunities 指纹验证失败,您还有%1次尝试机会 BatteryWidget Charging... fully charged PowerMode BatteryMode BindDialog Close Ok Okay Cancel Abbrechen Mfa authentication is on, please bind OTP code Go to Bind Bind BioDevices FingerPrint 指纹 FingerVein 指静脉 Iris 虹膜 Face 人脸 VoicePrint 声纹 BioDevicesWidget Please select other biometric devices 请选择其他生物识别设备 Device Type: 设备类型: Device Name: 设备名称: BiometricAuthWidget Current device: Aktuelles Gerät: Identify failed, Please retry. Identifizierung fehlgeschlagen, Bitte wiederholen Sie den Vorgang. BiometricDevicesWidget Please select the biometric device Bitte wählen Sie das biometrische Gerät aus Device type: Gerätetyp: Device name: Name des Geräts: OK OKAY BlockWidget Cancel Abbrechen Confirm If you do not perform any operation, the system will automatically %1 after %2 seconds. The following programs prevent restarting, you can click "Cancel" and then close these programs. The following programs prevent the shutdown, you can click "Cancel" and then close these programs. The following programs prevent suspend, you can click "Cancel" and then close these programs. The following programs prevent hibernation, you can click "Cancel" and then close these programs. The following programs prevent you from logging out, you can click "Cancel" and then close them. shut down restart The following program is running to prevent the system from reboot! Das folgende Programm wird ausgeführt, um einen Neustart des Systems zu verhindern! The following program is running to prevent the system from shutting down! Das folgende Programm wird ausgeführt, um das Herunterfahren des Systems zu verhindern! The following program is running to prevent the system from suspend! Das folgende Programm wird ausgeführt, um zu verhindern, dass das System angehalten wird! The following program is running to prevent the system from hibernate! Das folgende Programm wird ausgeführt, um zu verhindern, dass das System in den Ruhezustand versetzt wird! CharsMoreWidget &&?! &&?! CharsWidget More Mehr ABC ABC 123 123 ConfForm edit network Netzwerk bearbeiten LAN name: LAN-Name: Method: Methode: Address: Adresse: Netmask: Netzmaske: Gateway: Tor: DNS 1: DNS 1: DNS 2: DNS 2: Edit Conn Conn bearbeiten Auto(DHCP) Auto (DHCP) Manual Manuell Cancel Abbrechen Save Retten Ok Okay Can not create new wired network for without wired card Es kann kein neues kabelgebundenes Netzwerk ohne kabelgebundene Karte erstellt werden New network already created Neues Netzwerk bereits angelegt New network settings already finished Neue Netzwerkeinstellungen bereits fertiggestellt Edit Network Netzwerk bearbeiten Add Wired Network Kabelgebundenes Netzwerk hinzufügen create wired network successfully 已创建新的有线网络 change configuration of wired network successfully 新的设置已经生效 New settings already effective Neue Einstellungen bereits wirksam There is a same named LAN exsits. 已有同名连接存在 CustomAlbumViewer No photos detected, please re-select them DeviceType FingerPrint Fingerabdruck FingerVein Fingervene Iris Iris Face Gesicht VoicePrint Stimmenausdruck Ukey Ukey QRCode QRCode DigitalAuthDialog LoginByUEdu LoginByUEdu now is authing, wait a moment 认证中,请稍后 Password Incorrect, Please try again 密码错误,请重试 ResetPWD? ZurücksetzenPWD? SetNewUEduPWD SetNewUEduPWD ConfirmNewUEduPWD ConfirmNewUEduPWD The two password entries are inconsistent, please reset Die beiden Passworteingaben sind inkonsistent, bitte zurücksetzen Password entered incorrectly, please try again Passwort falsch eingegeben, bitte versuchen Sie es erneut clear klar DlgConnHidWifi Add Hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Wi-Fi name Wi-Fi-Name Wi-Fi security Wi-Fi-Sicherheit Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk DlgConnHidWifiLeap Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Username Nutzername Password Passwort Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise DlgConnHidWifiSecFast Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Authentication Authentifizierung Anonymous identity Anonyme Identität Allow automatic PAC pro_visioning Automatische PAC-pro_visioning zulassen PAC file PAC-Datei Inner authentication Innere Authentifizierung Username Nutzername Password Passwort Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise Tunneled TLS Getunneltes TLS Protected EAP (PEAP) Geschütztes EAP (PEAP) Anonymous Anonym Authenticated Beglaubigt Both Beide DlgConnHidWifiSecLeap Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Authentication Authentifizierung Username Nutzername Password Passwort Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise Tunneled TLS Getunneltes TLS Protected EAP (PEAP) Geschütztes EAP (PEAP) DlgConnHidWifiSecPeap Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Authentication Authentifizierung Anonymous identity Anonyme Identität Domain Domäne CA certificate CA-Zertifikat CA certificate password Kennwort für das CA-Zertifikat No CA certificate is required Es ist kein CA-Zertifikat erforderlich PEAP version PEAP-Ausführung Inner authentication Innere Authentifizierung Username Nutzername Password Passwort Cancel Abbrechen Connect Verbinden None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise Tunneled TLS Getunneltes TLS Protected EAP (PEAP) Geschütztes EAP (PEAP) Choose from file Wählen Sie aus Datei Automatic Automatisch Version 0 Variante 0 Version 1 Variante 1 DlgConnHidWifiSecPwd Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Authentication Authentifizierung Username Nutzername Password Passwort Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise Tunneled TLS Getunneltes TLS Protected EAP (PEAP) Geschütztes EAP (PEAP) DlgConnHidWifiSecTls Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Authentication Authentifizierung Identity Identität Domain Domäne CA certificate CA-Zertifikat CA certificate password Kennwort für das CA-Zertifikat No CA certificate is required Es ist kein CA-Zertifikat erforderlich User certificate Benutzerzertifikat User certificate password Kennwort für das Benutzerzertifikat User private key Privater Schlüssel des Benutzers User key password Kennwort des Benutzerschlüssels Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise Tunneled TLS Getunneltes TLS Protected EAP (PEAP) Geschütztes EAP (PEAP) Choose from file Wählen Sie aus Datei DlgConnHidWifiSecTunnelTLS Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Authentication Authentifizierung Anonymous identity Anonyme Identität Domain Domäne CA certificate CA-Zertifikat CA certificate password Kennwort für das CA-Zertifikat No CA certificate is required Es ist kein CA-Zertifikat erforderlich Inner authentication Innere Authentifizierung Username Nutzername Password Passwort Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise Tunneled TLS Getunneltes TLS Protected EAP (PEAP) Geschütztes EAP (PEAP) Choose from file Wählen Sie aus Datei DlgConnHidWifiWep Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Key Schlüssel WEP index WEP-Index Authentication Authentifizierung Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128-Bit-Schlüssel (Hexadezimal- oder ASCII-Schlüssel) WEP 128-bit Passphrase WEP-128-Bit-Passphrase Dynamic WEP (802.1X) Dynamisches WEP (802.1X) WPA & WPA2 Enterprise WPA und WPA2 Enterprise 1(default) 1 (Standardeinstellung) Open System Offenes System Shared Key Gemeinsamer Schlüssel DlgConnHidWifiWpa Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Add Hidden Wi-Fi Verstecktes WLAN hinzufügen Connection Verbindung Wi-Fi name Wi-Fi-Name Wi-Fi security Wi-Fi-Sicherheit Password Passwort Cancel Abbrechen Connect Verbinden C_reate… C_reate… None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich WEP 40/128-bit Key (Hex or ASCII) WEP 40/128 位密钥(十六进制或ASCII) WEP 128-bit Passphrase WEP 128 位密码句 Dynamic WEP (802.1X) 动态 WEP (802.1x) WPA & WPA2 Enterprise WPA 及 WPA2 企业 DlgHotspotCreate Dialog Dialogfeld Create Hotspot Hotspot erstellen Network name Name des Netzwerks Wi-Fi security Wi-Fi-Sicherheit Password Passwort Cancel Abbrechen Ok Okay None Nichts WPA & WPA2 Personal WPA & WPA2 Persönlich EngineDevice yes no Yes No %1% available, charged Left %1h %2m (%3%) %1% available Left %1h %2m to full charging (%1%) %1 waiting to discharge (%2%) %1 waiting to charge (%2%) AC adapter Laptop battery UPS Monitor Mouse Keyboard PDA Cell phone Media player Tablet Computer unrecognised IconEdit OK OKAY InputInfos Service exception... Service-Ausnahme... Invaild parameters... Invaild-Parameter... Unknown fault:%1 Unbekannter Fehler:%1 Recapture(60s) Wiedereinfangen (60er Jahre) Recapture(%1s) Rückeroberung(%1s) Get code Code abrufen KBTitle Suspended state Close Welt status KylinDBus kylin network applet desktop message 麒麟网络工具信息提示 KylinNM kylin-nm Kylin-NM LAN LAN Enabel LAN List 其他有线网络 WiFi 无线网络 Enabel WiFi List 其他无线网络 New WiFi 加入其他网络 Network 网络 Advanced Fortgeschritten Ethernet 有线网络 Connect Hide Network 加入网络 Wifi 无线网络 Enabled 已开启 Disabled 已关闭 HotSpot HotSpot (Hotspot) FlyMode Flugmodus Show MainWindow 显示网络连接界面 Inactivated LAN Inaktiviertes LAN Inactivated WLAN 未激活 Other WLAN Sonstiges WLAN WLAN WLAN Show KylinNM KylinNM anzeigen No wireless card detected Keine Wireless-Karte erkannt Activated LAN Aktiviertes LAN Activated WLAN Aktiviertes WLAN Not connected Nicht verbunden Disconnected Entfernt No Other Wired Network Scheme Kein anderes kabelgebundenes Netzwerkschema Edit 编辑 Done 完成 No wifi connected. 未连接任何网络 No Other Wireless Network Scheme Kein anderes drahtloses Netzwerkschema Wired net is disconnected Kabelgebundenes Netz ist getrennt Wi-Fi is disconnected 断开无线网络 Confirm your Wi-Fi password or usable of wireless card Bestätigen Sie Ihr WLAN-Passwort oder die Verwendung der WLAN-Karte Ethernet Networks 其他有线网络 New LAN 新建有线网络 Hide WiFi 加入网络 No usable network in the list Kein nutzbares Netzwerk in der Liste NetOn, NetOn, Wifi Networks 其他无线网络 None keep wired network switch is on before turning on wireless switch 打开无线网开关前保持有线网开关打开 please insert the wireless network adapter 请先插入无线网卡 Abnormal connection exist, program will delete it 正在断开异常连接的网络 update Wi-Fi list now, click again 正在更新 Wi-Fi列表 请再次点击 update Wi-Fi list now 正在更新 Wi-Fi列表 Conn Ethernet Success Conn-Ethernet-Erfolg Conn Ethernet Fail Conn-Ethernet-Ausfall Conn Wifi Success Conn Wifi Erfolg LettersWidget &&?! &&?! 123 123 Ctrl Strg Alt Alt LightDMHelper failed to start session. Login Einloggen Guest 游客 LockWidget Date Datum Time Zeit Guest 游客 SwitchUser SwitchUser (SwitchUser) Power Macht VirtualKeyboard Virtuelle Tastatur Multiple users are logged in at the same time.Are you sure you want to reboot this system? Mehrere Benutzer sind gleichzeitig angemeldet. Sind Sie sicher, dass Sie dieses System neu starten möchten? LAN LAN WLAN WLAN system-monitor Reset Pwd %1 may cause users who have logged in to this computer to lose content that has not yet been stored,To still perform please click "Confirm". Multiple users are logged in at the same time.Are you sure you want to %1 this system? Restart Neustarten SwitchSession Power Information Network 网络 Shut Down Herunterfahren LoginOptionsWidget Login Options Login-Optionen Password Passwort Other Andere FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey QRCode QRCode Wechat 微信 Identify device removed! Gerät identifizieren entfernt! MPRISSaverWidget No music detected Unknown MusicFileInformation Unknown singer Unknown album MyLineEdit Verification code Verifizierungscode MyNetworkWidget LAN LAN WLAN WLAN NumbersWidget &&?! &&?! Return Rückgabe OneConnForm Form Form Automatically join the network Automatischer Beitritt zum Netzwerk Input password 输入密码 Config 设置 Connect Verbinden Disconnect Trennen Input Password... Passwort eingeben... Connect to Hidden Wi-Fi Network Verbinden Sie sich mit einem versteckten Wi-Fi-Netzwerk Signal: Signal: Public 开放 Safe 安全 Rate 速率 None Nichts WiFi Security: WLAN-Sicherheit: MAC: MAC: Conn Wifi Failed Conn-WLAN fehlgeschlagen OneLancForm Form Form Config 设置 Connect Verbinden Disconnect Trennen No Configuration Keine Konfiguration IPv4: IPv4: IPv6: IPv6: BandWidth: Bandbreite: MAC: MAC: Auto 自动 PasswdCheckUtil The password is shorter than %1 characters The password contains less than %1 character classes The password is the same as the old one The password contains the user name in some form The password differs with case changes only The password is too similar to the old one The password is a palindrome PhoneAuthWidget Verification by phoneNum Verifizierung per phoneNum 「 Use bound Phone number to verification 」 「 Gebundene Telefonnummer zur Verifizierung verwenden 」 「 Use SMS to verification 」 「 Verwenden Sie SMS zur Verifizierung 」 commit begehen Network not connected~ Netzwerk nicht verbunden~ Network unavailable~ Netzwerk nicht verfügbar~ Verification Code invalid! Verifizierungscode ungültig! Verification Code incorrect.Please retry! Der Verifizierungscode ist falsch. Bitte versuchen Sie es erneut! Failed time over limit!Retry after 1 hour! Fehlgeschlagene Zeit über dem Limit! Wiederholen Sie es nach 1 Stunde! verifaction failed! Verifizierung fehlgeschlagen! PhotoCorver Right open the file location Remove the file PowerListWidget Hibernate Überwintern Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Schalten Sie Ihren Computer aus, aber die App bleibt geöffnet. Wenn der Computer eingeschaltet ist, kann er in den Zustand zurückversetzt werden, den Sie verlassen haben Suspend Aufhängen The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off Der Computer bleibt eingeschaltet, verbraucht aber weniger Strom. Die App bleibt geöffnet und kann schnell wieder aufwachen und dort weitermachen, wo Sie aufgehört haben The computer stays on, but consumes less power, The app stays open and can quickly wake up and revert to where you left off Restart Neustarten Close all apps, and then restart your computer Shut Down Herunterfahren Log Out Ausloggen The current user logs out of the system, terminates the session, and returns to the login page SwitchUser LockScreen UpgradeThenRestart UpgradeThenShutdown Power Off Ausschalten Close all apps, and then shut down your computer Schließen Sie alle Apps, und fahren Sie dann den Computer herunter Close all apps, turn off your computer, and then turn your computer back on Schließen Sie alle Apps, schalten Sie den Computer aus und wieder ein PowerManager lock Schleuse SwitchUser 切换用户 logout 注销 reboot 重启 shutdown 关机 Lock Screen 锁屏 Switch User 切换用户 Log Out Ausloggen Restart Neustarten Power Off Ausschalten Close all apps, turn off your computer, and then turn your computer back on Schließen Sie alle Apps, schalten Sie den Computer aus und wieder ein Close all apps, and then shut down your computer Schließen Sie alle Apps, und fahren Sie dann den Computer herunter Shut Down Herunterfahren Turn off your computer, but the app stays open, When the computer is turned on, it can be restored to the state you left Schalten Sie Ihren Computer aus, aber die App bleibt geöffnet. Wenn der Computer eingeschaltet ist, kann er in den Zustand zurückversetzt werden, den Sie verlassen haben Hibernate Überwintern The computer stays on, but consumes less power. The app stays open and can quickly wake up and revert to where you left off Der Computer bleibt eingeschaltet, verbraucht aber weniger Strom. Die App bleibt geöffnet und kann schnell wieder aufwachen und dort weitermachen, wo Sie aufgehört haben Suspend Aufhängen Sleep 休眠 QObject The screensaver is active. Der Bildschirmschoner ist aktiv. The screensaver is inactive. Der Bildschirmschoner ist inaktiv. FingerPrint FingerVein Iris Face VoicePrint Ukey Ukey QRCode QRCode Database Error ResetPwdWidget Security Question Kylin ID Enter new password Confirm your password Inconsistency with pwd Confirm Bestätigen Cancel Abbrechen Contains illegal characters! The service is abnormal The SMS verification code has been sent to the mobile phone with the tail number %1 (Valid for 5 minutes) Failed to get the verification code The account does not exist, please re-enter it The account does not exist, please complete the registration first The user is already locked, contact the administrator to unlock it If there are too many login errors, please try again in 5 minutes The SMS verification code is incorrect or has expired Wrong username or password Password decryption error Verify Kylin ID Reset Password Answer the security questions and reset your password One or more wrong answers S: tablet device Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel Abbrechen ScreenlockUi Screenlock /Screenlock/Screenlock Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin The date and time are displayed /Screenlock/The date and time are displayed Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Bluetooth Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Related Settings Monitor Off Set Screensaver No bluetooth adapter detected, can not use Leave Lock. Please select device Screensaver exit(Esc) 退出(Esc) exit 退出 Picture does not exist Bild existiert nicht Not connected to a network The weather update failed Smart Saver Weather Music Photo Albun Set as desktop wallpaper 设置为桌面壁纸 Automatic switching 自动切换 You have %1 unread message 您有%1条未读消息 You have new notification Sie haben eine neue Benachrichtigung View Ansehen Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Select Screensaver Weather Music Albun Idle time /Screensaver/Idle time The date and time are displayed /Screensaver/The date and time are displayed Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Random Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Add image folder Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Abbrechen Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. SleepTime You have rested: 您已休息: You have rested Du hast dich ausgeruht SureWindow Form Form TextLabel TextBeschriftung Cancel Abbrechen Confirm Bestätigen Multiple users are logged in at the same time.Are you sure you want to reboot this system? 同时有多个用户登录系统,您确定要退出系统吗? The following program is running to prevent the system from suspend! Das folgende Programm wird ausgeführt, um zu verhindern, dass das System angehalten wird! The following program is running to prevent the system from hibernate! Das folgende Programm wird ausgeführt, um zu verhindern, dass das System in den Ruhezustand versetzt wird! The following program is running to prevent the system from shutting down! Das folgende Programm wird ausgeführt, um das Herunterfahren des Systems zu verhindern! The following program is running to prevent the system from reboot! Das folgende Programm wird ausgeführt, um einen Neustart des Systems zu verhindern! SwitchButton login by password 密码登录 login by qr code 微信登录 SwitchButtonGroup uEduPWD uEduPWD Wechat Wechat TabletLockWidget You have %1 unread message 您有%1条未读消息 Slide to unlock 向上滑动解锁 New password is the same as old Das neue Passwort ist das gleiche wie das alte Reset password error:%1 Fehler beim Zurücksetzen des Kennworts:%1 Please scan by correct WeChat Bitte scannen Sie mit korrektem WeChat Cancel Abbrechen Back Zurück Skip Hüpfen TalksWidget Enter your answer UserListWidget Login Einloggen Guest 游客 Utils kylin network applet desktop message Kylin Network Applet Desktop-Nachricht VerificationWidget Please scan by bound WeChat Bitte scannen Sie über gebundenes WeChat VerifyKyidWidget Enter your Kylin ID account Input password 输入密码 Enter the verification code Get verification code Reacquire(%1) VerticalVerificationWidget Please scan by bound WeChat Bitte scannen Sie über gebundenes WeChat WeChatAuthDialog Login by wechat Login per Wechat Verification by wechat Verifizierung per Wechat 「 Use registered WeChat account to login 」 「 Verwenden Sie ein registriertes WeChat-Konto zum Anmelden 」 「 Use bound WeChat account to verification 」 「 Verwenden Sie ein gebundenes WeChat-Konto zur Verifizierung 」 Network not connected~ Netzwerk nicht verbunden~ Scan code successfully Code erfolgreich scannen Timeout!Try again! Zeitüberschreitung! Wiederholen! Login failed 登录失败 Widget Widget LoadPlugin action which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout which block type,param:Shutdown/Restart which block type delay how long to show lock Wie lange soll die Sperre angezeigt werden? has-lock if show lock if show lock main Start command for the ukui ScreenSaver. Startbefehl für den ukui ScreenSaver. lock the screen immediately Sperren Sie den Bildschirm sofort query the status of the screen saver Abfrage des Status des Bildschirmschoners unlock the screen saver Entsperren des Bildschirmschoners show the screensaver Bildschirmschoner anzeigen show blank and delay to lock,param:idle/lid/lowpower Leerzeichen und Verzögerung zum Sperren anzeigen, Parameter: Leerlauf / Deckel / geringe Leistung show the session tools show the switchuser window show the app block window show the multiUsers block window Dialog for the ukui ScreenSaver. Dialog für den ukui ScreenSaver. activated by session idle signal aktiviert durch Session-Leerlaufsignal lock the screen and show screensaver immediately Sperren Sie den Bildschirm und zeigen Sie den Bildschirmschoner sofort an show screensaver immediately Bildschirmschoner sofort anzeigen show blank screensaver immediately and delay time to show lock Leeren Bildschirmschoner sofort anzeigen und Zeit bis zur Anzeige der Sperre verzögern show blank screensaver immediately and if lock Leeren Bildschirmschoner sofort anzeigen und bei Sperre show switch user window switch to user and show window Screensaver for ukui-screensaver Bildschirmschoner für ukui-screensaver show on root window Im Root-Fenster anzeigen show on window. Im Fenster anzeigen. window id Fenster-ID Backend for the ukui ScreenSaver. lock the screen by startup switchtouser switch to someone user ukui-screensaver/config.h.in0000664000175000017500000000020415172041106015007 0ustar fengfeng#cmakedefine USE_INTEL // == 下面是测试用的 #cmakedefine01 TEST_ON #cmakedefine01 TEST_OF #ifndef __${VAR}___@VAR@ #endif ukui-screensaver/README.md0000664000175000017500000000154715172041035014257 0ustar fengfeng## ukui-screensaver ![build](https://github.com/ukui/ukui-screensaver/tree/master) ukui-screensaver是UKUI桌面环境的锁屏及屏保 ### 依赖 ------ ### 编译依赖 - KF5 - libkf5windowsystem-dev - cmake (>=2.6) - qtbase5-dev - libqt5x11extras5-dev - libpam0g-dev - qttools5-dev - qttools5-dev-tools - libglib2.0-dev - libopencv-dev - libx11-dev - libxtst-dev - libqt5svg5-dev - libgsettings-qt-dev - libmatemixer-dev - libukui-log4qt-dev ### 运行依赖 - ukui-session-manager - ethtool - mate-desktop-common ### 编译 ------ ```shell $ cd ukui-screensaver $ mkdir build $ cd build $ cmake .. $ make ``` ### 安装 ------ ```shell $ sudo make install ``` ### 主体框架 - **InProgress** - [x] 界面绘制 - [x] 功能实现 - [x] 界面美化 - **TROUBLE** - 无 - **TODO** - 功能插件 - 应用窗口置顶 ukui-screensaver/NOTICE0000664000175000017500000000453315172041035013702 0ustar fengfengOPEN SOURCE SOFTWARE NOTICE Please note we provide an open source software notice for the third party open source software along with this software and/or this software component (in the following just “this SOFTWARE”). The open source software licenses are granted by the respective right holders. Warranty Disclaimer THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. Copyright Notice and License Texts ------------------------------------------------------------------------ Software: qt-solutions Files: src/QtSingleApplication/* Copyright: 2013, Digia Plc and/or its subsidiary(-ies). License: BSD-3-clause The BSD License . 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 the copyright holder 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 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. ------------------------------------------------------------------------ ukui-screensaver/data/0000775000175000017500000000000015172041106013701 5ustar fengfengukui-screensaver/data/desktop/0000775000175000017500000000000015172041106015352 5ustar fengfengukui-screensaver/data/desktop/ukui-kglobal.desktop0000664000175000017500000000114315172041106021332 0ustar fengfeng[Desktop Entry] Type=Application Name=Screensaver Name[tr]=Ekran Koruyucu Name[zh_CN]=屏幕保护程序 Name[zh_HK]=螢幕保護程式 Name[zh_TW]=螢幕保護程式 Comment[tr]=Ekran koruyucuyu çalıştır ve programı kilitler Comment[zh_CN]=运行屏幕保护及锁定程序 Comment[zh_TW]=啟動螢幕保護程式以及鎖定程式 Exec=kglobalaccel5 NoDisplay=true X-UKUI-Autostart-Phase=Application X-UKUI-Autostart-Notify=true X-UKUI-Bugzilla-Bugzilla=UKUI X-UKUI-Bugzilla-Product=ukui-screensaver X-UKUI-Bugzilla-Component=general X-UKUI-Bugzilla-Version=1.0.0 OnlyShowIn=UKUI X-UKUI-AutoRestart=true ukui-screensaver/data/desktop/ukui-settings-daemon.desktop0000664000175000017500000000577415172041106023036 0ustar fengfeng[Desktop Entry] Type=Application Name=UKUI Settings Daemon Name[am]=የ ሜት ማሰናጃ ረዳት Name[ar]=محثّ إعدادات متّة Name[as]=UKUI Settings ডেমন Name[ast]=Degorriu d'axustes de UKUI Name[be]=Дэман настáўленняў UKUI Name[bg]=Сървър за настройките на UKUI Name[bn]=UKUI Settings ডেমন Name[bn_IN]=UKUI Settings ডেমন Name[ca]=Dimoni dels ajusts de UKUI Name[ca@valencia]=Dimoni de paràmetres del UKUI Name[cmn]=UKUI 設定值守護程式 Name[crh]=UKUI Ayarları Cını Name[cs]=Démon nastavení UKUI Name[da]=UKUI-indstillingsdæmon Name[de]=UKUI-Einstellungsdienst Name[el]=Δαίμονας ρυθμίσεων UKUI Name[en_AU]=UKUI Settings Daemon Name[en_GB]=UKUI Settings Daemon Name[es]=Administrador de preferencias de UKUI Name[et]=UKUI sätetedeemon Name[eu]=UKUIren ezarpenen daemon-a Name[fi]=Maten asetuspalvelin Name[fr]=Service des paramètres UKUI Name[ga]=Deamhan Socruithe UKUI Name[gl]=Servizo de configuracións do UKUI Name[gu]=UKUI સુયોજનો ડેઇમન Name[he]=מנוע ההגדרות של UKUI Name[hi]=गनोम सेटिंग डेमॉन Name[hu]=UKUI beállításdémon Name[hy]=UKUI Կարգաբերման պարամետրերի օժանդակ ծրագիր Name[id]=Daemon Penataan UKUI Name[it]=Demone di impostazioni per UKUI Name[ja]=UKUI 設定デーモン Name[kn]=UKUI ಸಂಯೋಜನೆಗಳ ಡೀಮನ್(Daemon) Name[ko]=마테 설정 데몬 Name[lt]=UKUI nustatymų tarnyba Name[lv]=UKUI iestatījumu dēmons Name[mk]=Демонот за поставувања на UKUI Name[ml]=ഗ്നോമിലെ സജ്ജീകരണങ്ങളുടെ നിരന്തരപ്രവൃത്തി Name[mr]=UKUI संयोजना डीमन Name[ms]=Daemon Tetapan UKUI Name[nb]=UKUI oppsettstjenestenisse Name[nl]=Mate-instellingenvoorziening Name[nn]=UKUI innstillingsnisse Name[or]=UKUI ବିନ୍ୟାସ ଡେମନ Name[pa]=ਗਨੋਮ ਸੈਟਿੰਗ ਡੈਮਨ Name[pl]=Usługa ustawień środowiska UKUI Name[pt]=Servidor de Definições UKUI Name[pt_BR]=Daemon de configurações do UKUI Name[ro]=Serviciul de configurări UKUI Name[ru]=Демон параметров UKUI Name[sk]=Démon nastavení UKUI Name[sl]=Ozadnji program nastavitev UKUI Name[sr]=Мејтов услужник подешавања Name[sr@latin]=Uslužni program za podešavanje Gnoma Name[sv]=UKUI-inställningsdemon Name[ta]=UKUI அமைவுகள் கிங்கரன் Name[te]=UKUI అమర్పుల డెమోన్ Name[th]=ดีมอนค่าตั้ง UKUI Name[tr]=UKUI Ayarlar Servisi Name[uk]=Служба параметрів UKUI Name[ur]=میٹ ترتیبات ڈیمن Name[vi]=Trình nền Thiết lập UKUI Name[zh_CN]=UKUI 设置守护程序 Name[zh_HK]=UKUI 設定值伺服程式 Name[zh_TW]=UKUI 設定值守護程式 Exec=/usr/bin/ukui-settings-daemon OnlyShowIn=UKUI; X-UKUI-Autostart-Phase=Initialization X-UKUI-Autostart-Notify=true X-UKUI-AutoRestart=true ukui-screensaver/data/desktop/ukui-screensaver.desktop0000664000175000017500000000122115172041106022234 0ustar fengfeng[Desktop Entry] Type=Application Name=Screensaver Name[tr]=Ekran Koruyucu Name[zh_CN]=屏幕保护程序 Name[zh_HK]=螢幕保護程式 Name[zh_TW]=螢幕保護程式 Comment[tr]=Ekran koruyucuyu çalıştır ve programı kilitler Comment[zh_CN]=运行屏幕保护及锁定程序 Comment[zh_TW]=啟動螢幕保護程式以及鎖定程式 Exec=ukui-screensaver-backend --lock-startup #Exec=mate-terminal NoDisplay=true X-UKUI-Autostart-Phase=Application X-UKUI-Autostart-Notify=true X-UKUI-Bugzilla-Bugzilla=UKUI X-UKUI-Bugzilla-Product=ukui-screensaver X-UKUI-Bugzilla-Component=general X-UKUI-Bugzilla-Version=1.0.0 OnlyShowIn=UKUI X-UKUI-AutoRestart=true ukui-screensaver/data/desktop/kwin.desktop0000664000175000017500000000054615172041106017722 0ustar fengfeng[Desktop Entry] Type=Application Name=KWin Exec=kwin_x11 --replace NoDisplay=true # name of loadable control center module X-UKUI-WMSettingsModule=kwin # name we put on the WM spec check window X-UKUI-WMName=KWin # back compat only X-UKUI-Autostart-Phase=WindowManager X-UKUI-Provides=windowmanager X-UKUI-Autostart-Notify=true X-Ubuntu-Gettext-Domain=kwin ukui-screensaver/data/ukui-screensaver.desktop0000664000175000017500000000117515172041035020574 0ustar fengfeng[Desktop Entry] Type=Application Name=Screensaver Name[tr]=Ekran Koruyucu Name[zh_CN]=屏幕保护程序 Name[zh_HK]=螢幕保護程式 Name[zh_TW]=螢幕保護程式 Comment[tr]=Ekran koruyucuyu çalıştır ve programı kilitler Comment[zh_CN]=运行屏幕保护及锁定程序 Comment[zh_TW]=啟動螢幕保護程式以及鎖定程式 Exec=/usr/bin/ukui-screensaver-backend %U NoDisplay=true X-UKUI-Autostart-Phase=Initialization X-UKUI-Autostart-Notify=true X-UKUI-Bugzilla-Bugzilla=UKUI X-UKUI-Bugzilla-Product=ukui-screensaver X-UKUI-Bugzilla-Component=general X-UKUI-Bugzilla-Version=1.0.0 OnlyShowIn=UKUI X-UKUI-AutoRestart=true ukui-screensaver/data/ukui-screensavers.menu0000664000175000017500000000074715172041035020256 0ustar fengfeng Screensavers ukui-screensaver.directory Screensaver ukui-screensaver/data/screensaver-startup.sh0000775000175000017500000000042615172041035020263 0ustar fengfeng#!/bin/bash ukui-screensaver-dialog --lock-startup & >/dev/null 2>&1 #/usr/lib/ukui-screensaver/screensaver-focus-helper & >/dev/null 2>&1 #如果锁屏和桌面进程并行启动,会对锁屏启动时间有较大影响,因此先启动锁屏,1s后再往下执行 sleep 1 ukui-screensaver/data/ukui-greeter/0000775000175000017500000000000015172041106016311 5ustar fengfengukui-screensaver/data/ukui-greeter/start-quick-session0000775000175000017500000000027415172041035022173 0ustar fengfeng#!/bin/sh # dbus-send --system --dest=org.ukui.UniauthBackend --type=method_call /org/ukui/UniauthBackend org.ukui.UniauthBackend.GetLastLoginUser & exec /usr/bin/ukui-session quickstart ukui-screensaver/data/ukui-greeter/95-ukui-greeter.conf0000664000175000017500000000023615172041106022024 0ustar fengfeng[Seat:*] greeter-session=ukui-greeter user-session=ukui enable-autologin-user-lock=false #greeter-setup-script=/usr/lib/ukui-greeter/ukui-greeter-nm-start.sh ukui-screensaver/data/ukui-greeter/97-ukui-greeter-wlcom.conf0000664000175000017500000000014615172041106023145 0ustar fengfeng[Seat:*] greeter-session=ukui-greeter-wlcom user-session=kylin-wlcom enable-autologin-user-lock=false ukui-screensaver/data/ukui-greeter/start-greeter-session0000775000175000017500000000006115172041035022506 0ustar fengfeng#!/bin/sh exec /usr/bin/ukui-session simpleDesk ukui-screensaver/data/ukui-greeter/lightdm-session-setup.sh0000775000175000017500000000057015172041035023122 0ustar fengfeng#!/bin/sh if [ "$XDG_SESSION_TYPE" != "wayland" ]; then bootinfo=$(cat /proc/cmdline | grep "boot=casper") if [ ! "$bootinfo" = "" ]; then echo "This is Try or install mode!" else username=`whoami` if [ -x "$(command -v save-param)" ]; then save-param -u $username fi fi else echo "Session type is wayland!" fi ukui-screensaver/data/ukui-greeter/ukui-greeter-wlcom.desktop0000664000175000017500000000032315172041106023431 0ustar fengfeng[Desktop Entry] Name=UKUI Greeter for wlcom Comment=UKUI Greeter for wlcom Exec=kylin-wlcom -s /usr/bin/start-greeter-session Type=Application X-Ubuntu-Gettext-Domain=ukui-greeter X-LightDM-Session-Type=wayland ukui-screensaver/data/ukui-greeter/ukui-greeter.desktop0000664000175000017500000000021215172041035022310 0ustar fengfeng[Desktop Entry] Name=UKUI Greeter Comment=UKUI Greeter Exec=ukui-session simpleDesk Type=Application X-Ubuntu-Gettext-Domain=ukui-greeter ukui-screensaver/data/ukui-screensaver.directory0000664000175000017500000001630115172041035021124 0ustar fengfeng[Desktop Entry] Encoding=UTF-8 Name=Screensavers Name[am]=የመመልከቻው ማዳኛዎች Name[ar]=حافظات الشاشة Name[as]=স্ক্ৰীনছেভাৰ Name[ast]=Curiapantalles Name[be]=Ахоўнікі экрану Name[be@latin]=Źbierahalniki ekranu Name[bg]=Предпазители на екрана Name[bn]=স্ক্রীন সেভার Name[bn_IN]=স্ক্রীনসেভার Name[br]=Damanterioù skramm Name[ca]=Estalvis de pantalla Name[ca@valencia]=Estalvis de pantalla Name[cmn]=螢幕保護程式 Name[crh]=Ekran qoruyıcıları Name[cs]=Šetřiče obrazovky Name[cy]=Arbedwyr Sgrin Name[da]=Pauseskærme Name[de]=Bildschirmschoner Name[dz]=གསལ་གཞི་ཉེན་སྲུང་ཚུ། Name[el]=Θέματα Name[en_AU]=Screensavers Name[en_CA]=Screensavers Name[en_GB]=Screensavers Name[es]=Salvapantallas Name[es_AR]=Protectores de pantalla Name[et]=Ekraanisäästjad Name[eu]=Pantaila-babeslea Name[fa]=محافظ‌های صفحهٔ نمایش Name[fi]=Näytönsäästäjät Name[fr]=Économiseurs d'écran Name[ga]=Spárálaithe Scáileáin Name[gl]=Protectores de pantalla Name[gu]=સ્ક્રીનસેવરો Name[he]=שומרי מסך Name[hi]=स्क्रीनसेवरs Name[hr]=Zaslonski čuvari Name[hu]=Képernyővédők Name[id]=Screensaver Name[is]=Skjáhvílur Name[it]=Salvaschermo Name[ja]=いろいろなスクリーンセーバーを格納するフォルダ Name[ka]=ეკრანმზოგები Name[kn]=ಸ್ಕ್ರೀನ್‌ಸೇವರುಗಳು Name[ko]=화면 보호기 Name[ku]=Parêzvanên ekranê Name[ky]=Экран сактагычтар Name[lt]=Ekrano užsklandos Name[lv]=Ekrānsaudzētāji Name[mai]=स्क्रीनसेवर Name[mg]=Sary mitsitsy Name[mk]=Чувари на екранот Name[ml]=സ്ക്രീന്‍സേവറുകള്‍ Name[mn]=_Дэлгэц гамнагчүүд Name[mr]=स्क्रीनसेवर Name[ms]=Penyelamat skrin Name[nb]=Skjermsparere Name[nds]=Billschirmschoners Name[ne]=स्क्रिनसेभर Name[nl]=Screensavers Name[nn]=Skjermsparare Name[oc]=Estalviaires d'ecran Name[or]=ପରଦା ସଂରକ୍ଷକ Name[pa]=ਸਕਰੀਨ-ਸੇਵਰ Name[pl]=Wygaszacze ekranu Name[ps]=پرده ساتونکي Name[pt]=Protetores de Ecrã Name[pt_BR]=Proteções de tela Name[ro]=Economizoare de ecran Name[ru]=Хранители экрана Name[si]=තිරආවරක Name[sk]=Šetriče obrazovky Name[sl]=Ohranjevalniki zaslona Name[sq]=Ruajtës ekrani Name[sr]=Чувари екрана Name[sr@latin]=Čuvari ekrana Name[sv]=Skärmsläckare Name[ta]=திரைசேமிப்பிகள் Name[te]=తెరకాపరులు Name[th]=โปรแกรมรักษาหน้าจอ Name[tr]=Ekran koruyucular Name[uk]=Заставки екрану Name[ur]=سکرین سیور Name[vi]=Ảnh bảo vệ màn hình Name[zh_CN]=屏幕保护程序 Name[zh_HK]=螢幕保護程式 Name[zh_TW]=螢幕保護程式 Comment=Screensaver themes Comment[am]=የመመልከቻው ማዳኛ ገጽታዎች Comment[ar]=سِمات حافظة الشاشة Comment[as]=স্ক্ৰীনছেভাৰ থিম Comment[ast]=Temes del Curiapantalles Comment[be]=Тэмы ахоўніка экрану Comment[be@latin]=Matyvy źbierahalnika ekranu Comment[bg]=Теми за предпазителя Comment[bn]=স্ক্রীনসেভার থীম Comment[bn_IN]=স্ক্রীনসেভার থিম Comment[br]=Neuzioù an damanter skramm Comment[ca]=Temes de l'estalvi de pantalla Comment[ca@valencia]=Temes de l'estalvi de pantalla Comment[cmn]=螢幕保護程式布景主題 Comment[crh]=Ekran qoruyıcısı temaları Comment[cs]=Motivy šetřiče obrazovky Comment[cy]=Themâu arbedwr sgrin Comment[da]=Pauseskærm temaer Comment[de]=Bildschirmschonerthemen Comment[dz]=གསལ་གཞི་ཉེན་སྲུང་བརྗོད་དོན་ཚུ། Comment[el]=Θέματα προστασίας οθόνης Comment[en@shaw]=𐑕𐑒𐑮𐑰𐑯𐑕𐑱𐑝𐑼 𐑔𐑰𐑥𐑟 Comment[en_AU]=Screensaver themes Comment[en_CA]=Screensaver themes Comment[en_GB]=Screensaver themes Comment[es]=Temas del salvapantallas Comment[es_AR]=Temas de protector de pantalla Comment[et]=Ekraanisäästja teemad Comment[eu]=Pantaila-babeslearen gaiak Comment[fa]=تم‌های محافظ صفحهٔ نمایش Comment[fi]=Näytönsäästäjän teemat Comment[fr]=Thèmes de l'économiseur d'écran Comment[ga]=Téamaí spárálaí scáileáin Comment[gl]=Temas do protector de pantalla Comment[gu]=સ્ક્રીનસેવર થીમો Comment[he]=ערכות נושא לשומר המסך Comment[hi]=स्क्रीनसेवर थीम Comment[hr]=Teme zaslonskog čuvara Comment[hu]=Képernyővédő-témák Comment[id]=Tema screensaver Comment[is]=Ásýnd skjáhvílu Comment[it]=Temi del salvaschermo Comment[ja]=スクリーンセーバーのテーマ Comment[ka]=ეკრანმზოგის გაფორმების თემები Comment[kn]=ಸ್ಕ್ರೀನ್‌ಸೇವರ್ ಥೀಮ್‌ಗಳು Comment[ko]=화면 보호기 테마 Comment[ku]=Dirbên dîmenderparêzê Comment[ky]=Экран сактагыч темалары Comment[lt]=Ekrano užsklandos temos Comment[lv]=Ekrānsaudzētāja tēmas Comment[mai]=स्क्रीनसेवर थीम Comment[mg]=Endri-tsary mitsitsy Comment[mk]=Теми за чуварот на екранот Comment[ml]=സ്ക്രീന്‍സേവറിന്റെ പ്രമേയങ്ങള്‍ Comment[mn]=_Дэлгэц гамнагчийн загварууд Comment[mr]=स्क्रीनसेवर सुत्रयोजना Comment[ms]=Tema penyelamat skrin Comment[nb]=Tema for skjermsparer Comment[nds]=Billschirmschonerthemen Comment[ne]=स्क्रिनसेभर विषयवस्तु Comment[nl]=Screensavers Comment[nn]=Tema for skjermsparar Comment[oc]=Tèmas d'estalviaire d'ecran Comment[or]=ପରଦା ସଂରକ୍ଷକ ପ୍ରସଙ୍ଗ Comment[pa]=ਸਕਰੀਨ-ਸੇਵਰ ਥੀਮ Comment[pl]=Motywy wygaszacza ekranu Comment[ps]=د پرده ساتونکي کوندې Comment[pt]=Temas do Protetor de Ecrã Comment[pt_BR]=Temas de proteção de tela Comment[ro]=Teme pentru economizor Comment[ru]=Темы хранителя экрана Comment[si]=තිරආවරකයේ තේමා Comment[sk]=Témy šetriča obrazovky Comment[sl]=Teme ohranjevalnika zaslona Comment[sq]=Temat e ruajtësit të ekranit Comment[sr]=Теме чувара екрана Comment[sr@latin]=Teme čuvara ekrana Comment[sv]=Skärmsläckarteman Comment[ta]=திரைசேமிப்பி சூழல்கள் Comment[te]=తెరకాపరి వైవిద్యాంశాలు Comment[th]=ชุดโปรแกรมรักษาหน้าจอ Comment[tr]=Ekran koruyucu temaları Comment[uk]=Теми збереження екрану Comment[ur]=سکرین سیور کی تھیم Comment[vi]=Sắc thái của ảnh bảo vệ màn hình Comment[zh_CN]=屏幕保护程序主题 Comment[zh_HK]=螢幕保護程式佈景主題 Comment[zh_TW]=螢幕保護程式布景主題 Icon=screensaver Type=Directory NoDisplay=false ukui-screensaver/data/CMakeLists.txt0000664000175000017500000000164715172041106016451 0ustar fengfenginstall(FILES org.ukui.screensaver.gschema.xml DESTINATION /usr/share/glib-2.0/schemas) install(FILES ukui-screensaver.desktop DESTINATION /etc/xdg/autostart) install(FILES ukui-screensaver.directory DESTINATION /usr/share/desktop-directories) install(FILES ukui-screensavers.menu DESTINATION /etc/xdg/menus) install(FILES ukui-screensaver-qt DESTINATION /etc/pam.d) #install(PROGRAMS screensaver-startup.sh DESTINATION /usr/bin) install(FILES ukui-screensaver-dialog.desktop DESTINATION /usr/share/applications) install(FILES org.ukui.screensaver.conf DESTINATION /usr/share/dbus-1/system.d) install(FILES ukui-greeter/ukui-greeter-wlcom.desktop DESTINATION /usr/share/xgreeters/) #install(FILES ukui-greeter/97-ukui-greeter-wlcom.conf DESTINATION /usr/share/lightdm/lightdm.conf.d/) install(PROGRAMS ukui-greeter/start-greeter-session DESTINATION /usr/bin/) #install(PROGRAMS ukui-greeter/start-quick-session DESTINATION /usr/bin/) ukui-screensaver/data/org.ukui.screensaver.conf0000664000175000017500000000060215172041035020631 0ustar fengfeng ukui-screensaver/data/org.ukui.screensaver.gschema.xml0000664000175000017500000003101315172041106022111 0ustar fengfeng false Automatic switching background Set this to TRUE to activate the automatic switching background. false show rest time on screensaver Set this to TRUE to show rest time on screensaver. false show rest time on custom screensaver Set this to TRUE to show rest time on custom screensaver. false show rest time on ukui screensaver Set this to TRUE to show rest time on ukui screensaver. "" set user text on screensaver. set user text on screensaver. "/usr/share/backgrounds" set background path on screensaver. set background path on screensaver. true set text postion Set this to TRUE to show text on screensaver center. 300 Time to update background on ukui-screensavaer-default The number of seconds to change screensaver background. "/usr/share/backgrounds/video/dynamicsaver.mp4" The path to the dynamic screensaver video The path to the dynamic screensaver video. 100 The maximum size of the dynamic screensaver video The maximum size of the dynamic screensaver video. "mp4,webm" The format of the dynamic screensaver video The format of the dynamic screensaver video. 1920 The maximum width of a dynamic screensaver video The maximum width of a dynamic screensaver video 1080 The maximum height of a dynamic screensaver video The maximum height of a dynamic screensaver video 0 Set the screensaver mode Set the screensaver mode 101010100 The location ID of the weather screensaver The location ID of the weather screensaver "" Music file directory Music file directory true Saver music follow system. Saver music follow system. 0 Album images show cycle mode Album images show cycle mode 60 The frequency of the interval between the display of album images The frequency of the interval between the display of album images ['/usr/share/backgrounds'] Defaultalbum image directory Default album image directory [] User custom album image directory User custom album image directory '/usr/share/backgrounds' User select album image directory User select album image directory "" URL background storage address. URL background storage address. true Activate when idle Set this to TRUE to activate the screensaver when the session is idle. true Activation when lock Set this to TRUE to lock the screen when the the session is idle. true Lock on activation Set this to TRUE to lock the screen when the screensaver goes active. 5 idle delay to lock Wait idle delay to lock -1 idle lock to lock Display the lock screen 10 screensaver with lock timeout Display the screensaver with lock's timeout true Lock when sleep Set this to TRUE to lock the screen when the system goes sleep. true Lock when close screen Set this to TRUE to lock the screen when the screen goes close. 'default-ukui' Screensaver theme selection mode The selection mode used by screensaver. May be "blank-only" to enable the screensaver without using any theme on activation, "single" to enable screensaver using only one theme on activation (specified in "themes" key), and "random" to enable the screensaver using a random theme on activation. 'fade-in-out' Effect for images transition The selection effect used by screensaer which play images. May be "none" to play images without any transition effect. It's ignored when "mode" is not "image". 60 Time interval of switching images The number of seconds of time interval of switching images [] Screensaver themes This key specifies the list of themes to be used by the screensaver. It's ignored when "mode" key is "blank-only", should provide the theme name when "mode" is "single", and should provide a list of themes when "mode" is "random". 30 Time before power management baseline The number of seconds of inactivity before signalling to power management. This key is set and maintained by the session power management agent. 10 Time before theme change The number of minutes to run before changing the screensaver theme. 0 Time before locking The number of minutes after screensaver activation before locking the screen. false Allow embedding a keyboard into the window Set this to TRUE to allow embedding a keyboard into the window when trying to unlock. The "keyboard_command" key must be set with the appropriate command. '' Embedded keyboard command The command that will be run, if the "embedded_keyboard_enabled" key is set to TRUE, to embed a keyboard widget into the window. This command should implement an XEMBED plug interface and output a window XID on the standard output. false Allow logout Set this to TRUE to offer an option in the unlock dialog to allow logging out after a delay. The delay is specified in the "logout_delay" key. 120 Time before logout option The number of minutes after the screensaver activation before a logout option will appear in the unlock dialog. This key has effect only if the "logout_enable" key is set to TRUE. '' Logout command The command to invoke when the logout button is clicked. This command should simply log the user out without any interaction. This key has effect only if the "logout_enable" key is set to TRUE. true Allow user switching Set this to TRUE to offer an option in the unlock dialog to switch to a different user account. true Allow the session status message to be displayed Allow the session status message to be displayed when the screen is locked. '/usr/share/backgrounds/1-openkylin.jpg' the background iamge of lockscreen Allow the user to set the background iamge of lockscreen. 0 Number of side-bar notify messages The number of sider notify messages true Allow to show messages Set this to TRUE to if u want to see how many masseges not read ukui-screensaver/data/ukui-screensaver-dialog.desktop0000664000175000017500000000131515172041035022025 0ustar fengfeng[Desktop Entry] Type=Application Name=Lockscreen Dialog Name[tr]=Ekran pencere Name[zh_CN]=锁屏窗口程序 Name[zh_HK]=鎖屏窗口程式 Name[zh_TW]=鎖屏窗口程式 Name[ug_CN]=ئېكران قۇلۇپلاش كۆزنەك پىروگراممىسى Name[kk_KZ]=ەكٸران قۇلپىلاۋ كوز بەك پىروگىرامماسى Name[ky_KG]=ەكىران قۇلۇپتوو كۅزۅنۅك   پراگرامماسى Comment[tr]=Ekran kilidini ve kurtarıcı göster Comment[zh_CN]=展示屏幕锁定和保护程序 Comment[zh_TW]=展示屏幕鎖定和保護程式 Exec=/usr/bin/ukui-screensaver-dialog NoDisplay=true OnlyShowIn=UKUI X-KDE-Wayland-Interfaces=org_kde_plasma_window_management,org_kde_kwin_keystate ukui-screensaver/data/ukui-screensaver-qt0000664000175000017500000000054015172041035017541 0ustar fengfengauth requisite pam_nologin.so auth sufficient pam_succeed_if.so user ingroup nopasswdlogin @include common-auth auth optional pam_gnome_keyring.so #If you are using Arch,comment out the #above and use the following. #auth include system-auth #account include system-auth #password include system-auth #session include system-auth ukui-screensaver/ukccplugins_i18n_ts/0000775000175000017500000000000015172041106016664 5ustar fengfengukui-screensaver/ukccplugins_i18n_ts/tr.ts0000664000175000017500000005264715172041106017677 0ustar fengfeng Screenlock Screenlock Ekran kilidi Screenlock Interface Ekran Kilidi Arayüzü Screenlock Set Ekran Kilidi Ayarı Lock screen when screensaver boot Ekran koruyucu açıldığında ekranı kilitle Lock screen delay Ekran kilidi gecikmesi Min Dk Select screenlock background Kilit Ekranı Arkaplanını Seç Browser online wp İnternetten Al Browser local wp Bilgisayardan Ekle Show picture of screenlock on screenlogin Ekran girişinde ekran kilidinin resmini göster Enabel screenlock Ekran kilidi aktif Open screenlock Ekran kilidi picture Resim Never Asla 5m 5 dk 10m 10 dk 30m 30 dk 45m 45 dk 1m 1 dk 1h 1 sa 1.5h 1.5 sa 3h 3 sa 2h 3 sa {2h?} Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) Duvarkağıdı Dosyaları(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) Tüm Dosyalar(*.*) Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application select custom wallpaper file Özel duvar kağıdı dosyasını seç Select Position: Konum: FileName: Dosya Adı: FileType: Cancel İptal Set Ayarla ScreenlockUi Show picture of screenlock on screenlogin Ekran girişinde ekran kilidinin resmini göster /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock Ekran kilidi /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Bluetooth No bluetooth adapter detected, can not use Leave Lock. Monitor Off /Screenlock/Monitor Off Screensaver Ekran Koruyucu /Screenlock/Screensaver Related Settings /Screenlock/Related Settings Set Ayarla Please select device Screensaver Screensaver Ekran Koruyucu Enable screensaver Ekran Koruyucu Aktif Screensaver program Ekran Koruyucu Programı idle time Bekleme Süresi Min Dk Lock screen when screensaver boot Ekran koruyucu açıldığında ekranı kilitle screensaver Ekran Koruyucu Default_ukui Ukui Varsayılanı UKUI Blank_Only Sadece Kalın Customize 5min 10min 30min 15min 1hour Position: Konum: FileName: Dosya Adı: Cancel İptal 1min Random Rasgele Never Asla 5m 5 dk 10m 10 dk 30m 30 dk 45m 45 dk 1m 1 dk 1h 1 sa 1.5h 1.5 sa 3h 3 sa ScreensaverUi Screensaver Ekran Koruyucu /Screensaver/Screensaver Idle time /Screensaver/Idle time Screensaver program Ekran Koruyucu Programı /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random Rasgele Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) Show rest time /Screensaver/Show rest time Lock screen when screensaver boot Ekran koruyucu açıldığında ekranı kilitle /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: Konum: FileName: Dosya Adı: FileType: Cancel İptal ukui-screensaver/ukccplugins_i18n_ts/fr.ts0000664000175000017500000006551115172041106017653 0ustar fengfeng Screenlock Screenlock Verrouillage de l’écran Screenlock Interface 锁屏界面 Show message on lock screen Afficher le message sur l’écran de verrouillage Browse 浏览 Online Picture 线上图片 Local Pictures Photos locales Online Pictures Photos en ligne Reset To Default Réinitialiser les paramètres par défaut Related Settings Paramètres associés Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay Délai de verrouillage de l’écran Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin Afficher l’image du verrouillage de l’écran lors de la connexion à l’écran Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never Jamais 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) Fichiers de fond d’écran(* .jpg * .jpeg * .bmp * .dib * .png * .jfif *.jpe * .gif * .tif * .tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1 min 5min 5 minutes 10min 10 minutes 30min 30 minutes 45min 45 min 1hour 1 heure 2hour 2 heures 3hour 3 heures Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application select custom wallpaper file Sélectionnez le fichier de fond d’écran personnalisé Select Choisir Position: Position: FileName: Fichier: FileType: Type de fichier : Cancel Annuler Monitor Off Moniteur éteint Screensaver Économiseur d’écran Set Poser ScreenlockUi Show picture of screenlock on screenlogin Afficher l’image du verrouillage de l’écran lors de la connexion à l’écran /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock Verrouillage de l’écran /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures Photos locales /Screenlock/Local Pictures Online Pictures Photos en ligne /Screenlock/Online Pictures Reset To Default Réinitialiser les paramètres par défaut /Screenlock/Reset To Default Bluetooth 蓝牙 No bluetooth adapter detected, can not use Leave Lock. Monitor Off Moniteur éteint /Screenlock/Monitor Off Screensaver Économiseur d’écran /Screenlock/Screensaver Related Settings Paramètres associés /Screenlock/Related Settings Set Poser Please select device Screensaver Screensaver Économiseur d’écran Idle time Temps d’inactivité Lock screen when activating screensaver Verrouiller l’écran lors de l’activation de l’économiseur d’écran Enable screensaver 开启屏保 Screensaver program Programme d’économiseur d’écran idle time 等待时间 Min 分钟 Lock screen when screensaver boot Écran de verrouillage lors du démarrage de l’économiseur d’écran screensaver 屏保 Default_ukui 默认屏保 View Vue Text(up to 30 characters): Texte (jusqu’à 30 caractères) : Show rest time Afficher le temps de repos UKUI UKUI (en anglais seulement) Blank_Only Blank_Only Customize Personnaliser 5min 5 minutes 10min 10 minutes 15min 15 min 30min 30 minutes 1hour 1 heure Screensaver source Source de l’économiseur d’écran Select Choisir Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Fichiers de fond d’écran(* .jpg * .jpeg * .bmp * .dib * .png * .jfif *.jpe * .gif * .tif * .tiff * .wdp * .svg) select custom screensaver dir Sélectionnez le répertoire de l’économiseur d’écran personnalisé Position: Position: FileName: Fichier: FileType: Type de fichier : Cancel Annuler Switching time Temps de commutation 1min 1 min Ordinal Ordinal Random switching Commutation aléatoire Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position Position du texte Centered Centré Randow(Bubble text) Randow(Texte à bulles) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random Aléatoire Never Jamais ScreensaverUi Screensaver Économiseur d’écran /Screensaver/Screensaver Idle time Temps d’inactivité /Screensaver/Idle time Screensaver program Programme d’économiseur d’écran /Screensaver/Screensaver program Screensaver source Source de l’économiseur d’écran /Screensaver/Screensaver source Select Choisir Ordinal Ordinal Random Aléatoire Random switching Commutation aléatoire /Screensaver/Random switching Switching time Temps de commutation /Screensaver/Switching time Text(up to 30 characters): Texte (jusqu’à 30 caractères) : /Screensaver/Text(up to 30 characters): Text position Position du texte /Screensaver/Text position Centered Centré Randow(Bubble text) Randow(Texte à bulles) Show rest time Afficher le temps de repos /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Fichiers de fond d’écran(* .jpg * .jpeg * .bmp * .dib * .png * .jfif *.jpe * .gif * .tif * .tiff * .wdp * .svg) select custom screensaver dir Sélectionnez le répertoire de l’économiseur d’écran personnalisé Position: Position: FileName: Fichier: FileType: Type de fichier : Cancel Annuler ukui-screensaver/ukccplugins_i18n_ts/bo.ts0000664000175000017500000003662515172041106017650 0ustar fengfeng Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Bluetooth No bluetooth adapter detected, can not use Leave Lock. Monitor Off /Screenlock/Monitor Off Screensaver /Screenlock/Screensaver Related Settings /Screenlock/Related Settings Set Please select device Screensaver Screensaver UKUI Customize 15min 1hour Never 1min 5min 10min 30min ScreensaverUi Screensaver /Screensaver/Screensaver Idle time /Screensaver/Idle time Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel ukui-screensaver/ukccplugins_i18n_ts/en.ts0000664000175000017500000003662515172041106017652 0ustar fengfeng Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Bluetooth No bluetooth adapter detected, can not use Leave Lock. Monitor Off /Screenlock/Monitor Off Screensaver /Screenlock/Screensaver Related Settings /Screenlock/Related Settings Set Please select device Screensaver Screensaver UKUI Customize 5min 10min 15min 30min 1hour Never 1min ScreensaverUi Screensaver /Screensaver/Screensaver Idle time /Screensaver/Idle time Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel ukui-screensaver/ukccplugins_i18n_ts/fa.ts0000664000175000017500000003664315172041106017636 0ustar fengfeng Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Bluetooth No bluetooth adapter detected, can not use Leave Lock. Monitor Off /Screenlock/Monitor Off Screensaver /Screenlock/Screensaver Related Settings /Screenlock/Related Settings Set Please select device Screensaver Screensaver 5min 10min 15min 30min 1hour Never 1min UKUI Customize ScreensaverUi Screensaver /Screensaver/Screensaver Idle time /Screensaver/Idle time Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) Show rest time /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel ukui-screensaver/ukccplugins_i18n_ts/en_US.ts0000664000175000017500000005153615172041106020257 0ustar fengfeng PhotoCorver Right open the file location Remove the file Screenlock Screenlock Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) select custom wallpaper file Select Position: FileName: FileType: Cancel ScreenlockUi Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures /Screenlock/Local Pictures Online Pictures /Screenlock/Online Pictures Reset To Default /Screenlock/Reset To Default Bluetooth No bluetooth adapter detected, can not use Leave Lock. Monitor Off Screensaver Related Settings Set Please select device Screensaver Screensaver UKUI Customize 5min 10min 15min 30min 1hour Never 1min ScreensaverUi Screensaver /Screensaver/Screensaver Idle time /Screensaver/Idle time Screensaver program /Screensaver/Screensaver program Screensaver source /Screensaver/Screensaver source Select Ordinal Random Random switching /Screensaver/Random switching Switching time /Screensaver/Switching time Text(up to 30 characters): /Screensaver/Text(up to 30 characters): Text position /Screensaver/Text position Centered Randow(Bubble text) Show rest time /Screensaver/Show rest time Select Screensaver Weather Music Albun Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Go to App Settings Current position /Screensaver/Show rest time Prioritize the list of songs from the Music app /Screensaver/Prioritize the list of songs from the Music app Priority playlists /Screensaver/Priority playlists Select the folder Switch mode /Screensaver/Switch mode In order Switching frequency /Screensaver/Switching frequency Image source /Screensaver/Image source Add image folder System wallpaper gallery Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. ukui-screensaver/ukccplugins_i18n_ts/ky.ts0000664000175000017500000010774715172041106017677 0ustar fengfeng PhotoCorver Right open the file location ۅجۅت وردۇن اچۇۇ Remove the file ۅندۉرۉپ تاشتاش Screenlock Screenlock ەكىران قۇلۇپى Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) تام قاعازى ۅجۅتۉۉ(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Your system does not have any browser application installed or the default browser application is not set up سەستىماڭىزدا تور كۅرگۉچ پىروگىرامماسى قاچالانباعان كۅرۉنۉشتۅرۉ سۈكۈتتىكى تور كۅرگۉچ پىروگىرامماسى تەڭشەلبەگەن Unable to open browser application تور كۅرگۉچ پىروگىرامماسىن اچىلبادى select custom wallpaper file ۅزۉ كۅرسۅتۉلگۅن تام قاعازى ۅجۅتۉن تانداڭ Select تانداڭ Position: :وردۇ FileName: :ۅجۅت ناامى FileType: :ۅجۅت تۉرۉ Cancel ارعادان قالتىرىش Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin كۅرسۅتۉۉ ئېكرانىدىكى تام قاعازى كىرۉۉ كۅرۉنمۅ بەتىندە /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) قۇلۇپنى قالتىرىپ قويۇش (بىر جۇپ ەسەپتەمەك جوعولۇپ كەتكەندە ساامالىق قۇلۇپلىنىدۇ) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device بەلگىلەنگەن شايمان No paired phone. Please turn to 'Bluetooth' to pair. جۇپ تەلەفون جوق 'كۆكچىش' نى قوشۇۋېلىپ قوشۇڭ. Screenlock ەكىران قۇلۇپى /Screenlock/Screenlock Choose Picture سۉرۅت تانداڭ /Wallpaper/Choose Picture Local Pictures جەردىك سۈرەتلەر /Screenlock/Local Pictures Online Pictures توردوعۇ سۈرەتلەر /Screenlock/Online Pictures Reset To Default كۆڭۈلدىكىگە قايرا بەلگىلۅۅ /Screenlock/Reset To Default Bluetooth كۆكچىش No bluetooth adapter detected, can not use Leave Lock. كۆكچىش شايكەشتىرگىچ بايقالبادى، Leave Lock نى ىشتەتكەلى بولبويت . Monitor Off كۉزۅتكۉچتۉ ۅچۉرۉۉ /Screenlock/Monitor Off Screensaver ەكىران قورعوو /Screenlock/Screensaver Related Settings بايلانىشتۇۇ تەڭشەكتەر /Screenlock/Related Settings Set تەڭشەك Please select device اسپاپتى تانداڭ The date and time are displayed كۅرسۅتۉۉ ۇباقىت ۇباقتى /Screenlock/The date and time are displayed Screensaver Screensaver ەكىران قورعوو Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI Blank_Only 黑屏 Customize قاسىيەتتەشتىرۉۉ 5min 5 10min ون مىنۇت 15min 15 30min 30 مىنوت 1hour بىر ساات Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1 مىنوت Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never مۇرداتان ScreensaverUi Screensaver ەكىران قورعوو /Screensaver/Screensaver Idle time بەكەر ۇباقتى /Screensaver/Idle time Screensaver program ەكىران قورعوو پراگرامماسى /Screensaver/Screensaver program Screensaver source ەكىران بەتى قاينارى /Screensaver/Screensaver source Select تانداڭ Ordinal جۅرۅلگۅلۉ Random ۅزەركى مەنەن Select Screensaver ەكىران اسىراشتى تانداش Weather ابا ىرايى Music ىر-مۇزىكا Albun البوم Go to App Settings قولدونۇشچان قۇرۇۇ ، اچۇۇ ، باشتوو جاسوو ،اتقارۇۇ Current position گەزەكتەكى وردۇ /Screensaver/Show rest time Prioritize the list of songs from the Music app الدىن «مۇزىكا »نىڭ قولدونۇلاتۇرعان ىر تىزىمدىگىن قويۇش كەرەك /Screensaver/Prioritize the list of songs from the Music app Priority playlists الدىن تىزىمدىكتى قويۇش /Screensaver/Priority playlists Select the folder ۅجۅت قىپچىعىچ تانداش Switch mode الماشتىرۇۇ ابالى /Screensaver/Switch mode In order تارتىپ بويۇنچا Switching frequency الماشتىرۇۇ جىشتىق القاعى /Screensaver/Switching frequency Image source سۉرۅت قاينارى /Screensaver/Image source Add image folder سۉرۅتتۉ ۅجۅت قىپچىعىچ قوشۇڭ Random switching قاالاعانچا الماشتىرۇۇ /Screensaver/Random switching Switching time الماشتىرۇۇ ۇباقتى /Screensaver/Switching time Text(up to 30 characters): تەكىست(ەڭ كۅپ بولعوندو 30 تامعا): /Screensaver/Text(up to 30 characters): Text position تەكىست وردۇ /Screensaver/Text position Centered مەركەزلەشتۈرۈلگەن Randow(Bubble text) ۅزەركى مەنەن(كۆپۈكچە تەكىست) Show rest time ەس الۇۇ الۇۇ ۇباقتىن كۅرسۅتۉۉ /Screensaver/Show rest time Lock screen when screensaver boot ەكراندى كۅزۅنۅك جابۇۇدا ەكراندى قۇلۇپتوو /Screensaver/Lock screen when screensaver boot The date and time are displayed كۅرسۅتۉۉ ۇباقىت ۇباقتى /Screensaver/The date and time are displayed System wallpaper gallery سەستىما تام قاعازى چېرتىيوژى قازىناعى Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) تام قاعازى ۅجۅتتۅرۉ(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir قاسىيەت ەكىران كۅرگۉچتۉ تانداش Position: :وردۇ FileName: :ۅجۅت ناامى FileType: :ۅجۅت تۉرۉ Cancel ارعادان قالتىرىش Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) دووشتۇ ۅجۅت (*.mp3*.wav*.flac .ogg.aac*.m4a *.wma.opus.aiff.ape) select screensaver music dir ەكراندۇۇ دووشتۇ ۅجۅت قىپچىعىچتى تانداش The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. سۉرۅت ۅجۅت قىپچىعىچ قوشۇلعان ساندىن جوعورۇ چەگىنە جەتى ، بۆلۈۋەتكەندىن كىيىن جالعاشتۇۇ قوشۇۇعا بولوت Screensaver Picture does not exist سۉرۅت باربولۇۇسۇ ەمەس Not connected to a network تور ۇلانبادى The weather update failed ھاۋارايىنىڭ جاڭىرىلىشى جەڭىلۉۉ بولدۇ MPRISSaverWidget No music detected ىردى تەكشەرىپ بايقابادى Unknown بەلگىسىز CustomAlbumViewer No photos detected, please re-select them سۉرۅتتۉ تەكشۈرمىگەندە، قايتادان تانداڭ ukui-screensaver/ukccplugins_i18n_ts/th.ts0000664000175000017500000011145715172041106017660 0ustar fengfeng PhotoCorver Right open the file location เปิดตำแหน่งไฟล์ทางขวา Remove the file ลบไฟล์ Screenlock Screenlock ล็อคหน้าจอ Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) ไฟล์วอลเปเปอร์ (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Your system does not have any browser application installed or the default browser application is not set up ระบบของคุณไม่ได้ติดตั้งแอปพลิเคชันเบราว์เซอร์หรือไม่ได้ตั้งค่าเบราว์เซอร์เริ่มต้น Unable to open browser application ไม่สามารถเปิดแอปพลิเคชันเบราว์เซอร์ select custom wallpaper file เลือกไฟล์วอลเปเปอร์แบบกำหนดเอง Select เลือก Position: ตำแหน่ง: FileName: ชื่อไฟล์: FileType: ประเภทไฟล์: Cancel ยกเลิก Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin แสดงภาพล็อคหน้าจอบนหน้าจอเข้าสู่ระบบ /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) ล็อคเมื่อออกห่าง (ระบบจะถูกล็อคเมื่อโทรศัพท์ที่จับคู่หายไป) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device อุปกรณ์ที่ระบุ No paired phone. Please turn to 'Bluetooth' to pair. ไม่มีโทรศัพท์ที่จับคู่ กรุณาไปที่ 'บลูทูธ' เพื่อจับคู่ Screenlock ล็อคหน้าจอ /Screenlock/Screenlock Choose Picture เลือกรูปภาพ /Wallpaper/Choose Picture Local Pictures รูปภาพในเครื่อง /Screenlock/Local Pictures Online Pictures รูปภาพออนไลน์ /Screenlock/Online Pictures Reset To Default รีเซ็ตเป็นค่าเริ่มต้น /Screenlock/Reset To Default Bluetooth บลูทูธ No bluetooth adapter detected, can not use Leave Lock. ไม่พบอะแดปเตอร์บลูทูธ ไม่สามารถใช้ล็อคเมื่อออกห่าง Monitor Off ปิดจอภาพ /Screenlock/Monitor Off Screensaver สกรีนเซฟเวอร์ /Screenlock/Screensaver Related Settings การตั้งค่าที่เกี่ยวข้อง /Screenlock/Related Settings Set ตั้งค่า Please select device กรุณาเลือกอุปกรณ์ The date and time are displayed แสดงวันที่และเวลา /Screenlock/The date and time are displayed Screensaver Screensaver สกรีนเซฟเวอร์ Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI UKUI Blank_Only 黑屏 Customize ปรับแต่ง 5min 5 นาที 10min 10 นาที 15min 15 นาที 30min 30 นาที 1hour 1 ชั่วโมง Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1 นาที Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never ไม่เคย ScreensaverUi Screensaver สกรีนเซฟเวอร์ /Screensaver/Screensaver Idle time เวลาว่าง /Screensaver/Idle time Screensaver program โปรแกรมสกรีนเซฟเวอร์ /Screensaver/Screensaver program Screensaver source แหล่งที่มาสกรีนเซฟเวอร์ /Screensaver/Screensaver source Select เลือก Ordinal ตามลำดับ Random สุ่ม Select Screensaver เลือกสกรีนเซฟเวอร์ Weather สภาพอากาศ Music เพลง Albun อัลบั้ม Go to App Settings ไปที่การตั้งค่าแอป Current position ตำแหน่งปัจจุบัน /Screensaver/Show rest time Prioritize the list of songs from the Music app ให้ความสำคัญกับรายการเพลงจากแอปเพลง /Screensaver/Prioritize the list of songs from the Music app Priority playlists เพลย์ลิสต์ที่มีลำดับความสำคัญ /Screensaver/Priority playlists Select the folder เลือกโฟลเดอร์ Switch mode โหมดการสลับ /Screensaver/Switch mode In order ตามลำดับ Switching frequency ความถี่ในการสลับ /Screensaver/Switching frequency Image source แหล่งที่มาของภาพ /Screensaver/Image source Add image folder เพิ่มโฟลเดอร์ภาพ Random switching สลับแบบสุ่ม /Screensaver/Random switching Switching time เวลาในการสลับ /Screensaver/Switching time Text(up to 30 characters): ข้อความ (ไม่เกิน 30 ตัวอักษร): /Screensaver/Text(up to 30 characters): Text position ตำแหน่งข้อความ /Screensaver/Text position Centered กึ่งกลาง Randow(Bubble text) สุ่ม(ข้อความฟองอากาศ) Show rest time แสดงเวลาพัก /Screensaver/Show rest time Lock screen when screensaver boot ล็อคหน้าจอเมื่อสกรีนเซฟเวอร์เริ่มทำงาน /Screensaver/Lock screen when screensaver boot The date and time are displayed แสดงวันที่และเวลา /Screensaver/The date and time are displayed System wallpaper gallery แกลเลอรี่วอลเปเปอร์ระบบ Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) ไฟล์วอลเปเปอร์ (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir เลือกไดเรกทอรีสกรีนเซฟเวอร์ที่กำหนดเอง Position: ตำแหน่ง: FileName: ชื่อไฟล์: FileType: ประเภทไฟล์: Cancel ยกเลิก Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) ไฟล์เพลง (*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir เลือกไดเรกทอรีเพลงสกรีนเซฟเวอร์ The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. ถึงขีดจำกัดสูงสุดของจำนวนรูปภาพในโฟลเดอร์แล้ว คุณสามารถลบบางส่วนออกก่อนที่จะเพิ่มเพิ่มเติม Screensaver Picture does not exist รูปภาพไม่มีอยู่ Not connected to a network ไม่ได้เชื่อมต่อกับเครือข่าย The weather update failed อัปเดตสภาพอากาศล้มเหลว MPRISSaverWidget No music detected ไม่พบเพลง Unknown ไม่ทราบ CustomAlbumViewer No photos detected, please re-select them ไม่พบรูปภาพ กรุณาเลือกใหม่ ukui-screensaver/ukccplugins_i18n_ts/zh_Hant.ts0000664000175000017500000006420415172041106020635 0ustar fengfeng Screenlock Screenlock 鎖屏 Screenlock Interface 锁屏界面 Show message on lock screen 鎖屏時顯示消息 Browse 浏览 Online Picture 线上图片 Local Pictures 本地圖片 Online Pictures 線上圖片 Reset To Default 恢復預設 Related Settings 相關設置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此時間段后鎖屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 顯示鎖屏壁紙在登錄介面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 從不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) 圖片檔(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1 分鐘 5min 5 分鐘 10min 10 分鐘 30min 30 分鐘 45min 45 分鐘 1hour 1 小時 2hour 2 小時 3hour 3 小時 Your system does not have any browser application installed or the default browser application is not set up 您的系統未安裝任何瀏覽器應用或未設置預設瀏覽器應用 Unable to open browser application 無法打開瀏覽器應用 select custom wallpaper file 選擇自訂壁紙檔 Select 選擇 Position: 位置: FileName: 檔案名稱: FileType: 檔案類型: Cancel 取消 Monitor Off 關閉顯示器 Screensaver 屏幕保護 Set 去設置 ScreenlockUi Show picture of screenlock on screenlogin 顯示鎖屏壁紙在登錄介面 /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock 鎖屏 /Screenlock/Screenlock Choose Picture 選擇圖片 /Wallpaper/Choose Picture Local Pictures 本地圖片 /Screenlock/Local Pictures Online Pictures 線上圖片 /Screenlock/Online Pictures Reset To Default 恢復預設 /Screenlock/Reset To Default Bluetooth 蓝牙 No bluetooth adapter detected, can not use Leave Lock. Monitor Off 關閉顯示器 /Screenlock/Monitor Off Screensaver /Screenlock/Screensaver Related Settings 相關設置 /Screenlock/Related Settings Set 去設置 Please select device Screensaver Screensaver 屏保 Idle time 此時間段后開啟屏保 Lock screen when activating screensaver 啟動屏保時鎖住螢幕 Enable screensaver 开启屏保 Screensaver program 屏幕保護程式 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 啟動屏保時鎖定螢幕 screensaver 屏保 Default_ukui 默认屏保 View 預覽 Text(up to 30 characters): 屏保文本(最多 30 個字): Show rest time 顯示休息時間 UKUI UKUI Blank_Only 黑屏 Customize 自訂 5min 5 分鐘 10min 10 分鐘 15min 15 分鐘 30min 30 分鐘 1hour 1 小時 Screensaver source 屏保來源 Select 選擇 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁紙檔(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 選擇自定義屏保路徑 Position: 位置: FileName: 檔案名稱: FileType: 檔案類型: Cancel 取消 Switching time 切換頻率 1min 1 分鐘 Ordinal 按順序 Random switching 隨機切換 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 隨機(氣泡文字) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 隨機 Never 從不 ScreensaverUi Screensaver /Screensaver/Screensaver Idle time 此時間段后開啟屏保 /Screensaver/Idle time Screensaver program 屏幕保護程式 /Screensaver/Screensaver program Screensaver source 屏保來源 /Screensaver/Screensaver source Select 選擇 Ordinal 按順序 Random 隨機 Random switching 隨機切換 /Screensaver/Random switching Switching time 切換頻率 /Screensaver/Switching time Text(up to 30 characters): 屏保文本(最多 30 個字): /Screensaver/Text(up to 30 characters): Text position 文本位置 /Screensaver/Text position Centered 居中 Randow(Bubble text) 隨機(氣泡文字) Show rest time 顯示休息時間 /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁紙檔(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 選擇自定義屏保路徑 Position: 位置: FileName: 檔案名稱: FileType: 檔案類型: Cancel 取消 ukui-screensaver/ukccplugins_i18n_ts/bo_CN.ts0000664000175000017500000010763115172041106020224 0ustar fengfeng CustomAlbumViewer No photos detected, please re-select them པར་བཤེར་འཇལ་བྱེད་མ་ཐུབ།ཡང་བསྐྱར་འདེམ་རོགས། MPRISSaverWidget No music detected གླུ་དབྱངས་བཤེར་འཇལ་མ་ཐུབ། Unknown ཤེས་མེད་པ། PhotoCorver Right open the file location ཡིག་ཆའི་གནས་ཡུལ་གྱི་ཁ་ཕྱེ་བ། Remove the file སྤོ་བ། Screenlock Screenlock བརྙན་ཤེལ་གྱི་སྒོ་བརྒྱབ་པ Screenlock Interface བརྙན་ཤེལ་གྱི་འབྲེལ་མཐུད། Show message on lock screen ཟྭ་ངོས་སུ་ཆ་འཕྲིན་མངོན་པར་བྱས་ཡོད། Show picture of screenlock on screenlogin བརྙན་ཤེལ་སྟེང་གི་བརྙན་ཤེལ་གྱི་པར་རིས་འགྲེམས་སྟོན་བྱས། Related Settings འབྲེལ་ལྡན་སྒྲིག་འགོད་ Monitor Off སྒོ་རྒྱག་འཆར་ཆས་ Set ཉི་ནུབ Screensaver བརྙན་ཡོལ་སྲུང་སྐྱོབ་ Lock screen when screensaver boot བརྙན་ཤེལ་གྱི་ལྷམ་ཡུ་རིང་གི་དུས་སུ་བརྙན་ཤེལ་ལ་ཟྭ་རྒྱག Lock screen delay བརྙན་ཤེལ་གྱི་དུས་ཚོད་འགོར་འགྱངས Online Picture དྲ་ཐོག་པར་རིས། 1min 1min 5min 5min 10min 10min 30min 30min 45min 45min 1hour 1hour 2hour 2hour 3hour 3hour Never གཏན་ནས་བྱེད་མི་ Your system does not have any browser application installed or the default browser application is not set up ཁྱེད་ཀྱི་རྒྱུད་ཁོངས་ལ་གང་རུང་གི་མིག་བཤར་ཆས་བེད་སྤྱོད་དང་སོར་བཞག་མིག་བཤར་ཆས་བེད་སྤྱོད་སྒྲིག་འགོད་བྱས་མེད། Unable to open browser application མིག་བཤར་ཆས་བེད་སྤྱོད་ཁ་འབྱེད་མི་ཐུབ། Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) པར་རིས་ཡིག་ཆWallpaper files (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *jpe *.gif *.tif *.tiff *wdp) select custom wallpaper file མཚན་ཉིད་རང་འཇོག་གི་གྱང་ཤོག་ཡིག་ཆ་བདམས་པ Select བདམ་གསེས། Position: ས་གནས་ནི། FileName: ཡིག་ཆའི་མིང་ནི། FileType: ཡིག་ཆའི་རིགས་ནི། Cancel མེད་པར་བཟོ་དགོས The date and time are displayed དུས་ཚོད་ཀྱི་ཚེས་གྲངས་གསལ་པོར་མངོན་པ། /Screenlock/The date and time are displayed ScreenlockUi Show picture of screenlock on screenlogin བརྙན་ཤེལ་སྟེང་གི་བརྙན་ཤེལ་གྱི་པར་རིས་འགྲེམས་སྟོན་བྱས། /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) ཟྭ་དང་བྲལ་ནས་(ལག་ཁྱེར་ཁ་པར་མེད་པར་གྱུར་རྗེས་མ་ལག་ལ་སྒོ་བརྒྱབ་ནས་གཏན་ཁེལ་བྱེད་ངེས་ ) Specified device སྒྲིག་ཆས་དམིགས་འཛུགས་བྱ། No paired phone. Please turn to 'Bluetooth' to pair. ལག་ཁྱེར་ཁ་པར་ཆ་འགྲིག་མེད'སོ་སྔོན་པོ་'རུ་བསྒྱུར་ནས་སྡེབ་སྒྲིག་བྱེད་རོགས།。 Screenlock བརྙན་ཤེལ་གྱི་སྒོ་བརྒྱབ་པ /Screenlock/Screenlock The date and time are displayed དུས་ཚོད་ཀྱི་ཚེས་གྲངས་གསལ་པོར་མངོན་པ། /Screenlock/The date and time are displayed Choose Picture པར་རིས་འདེམ་པ། /Wallpaper/Choose Picture Local Pictures རང་སའི་རི་མོ། /Screenlock/Local Pictures Online Pictures དྲ་ཐོག་པར་རིས། /Screenlock/Online Pictures Reset To Default བསྐྱར་དུ་ཁ་ཆད་དང་འགལ་བའི་གནས་ /Screenlock/Reset To Default Bluetooth སོ་སྔོན་སྒྲིག་འགོད། No bluetooth adapter detected, can not use Leave Lock. སོ་སྔོན་པོའི་སྡེབ་སྦྱོར་ཡོ་བྱད་ལ་ཞིབ་དཔྱད་ཚད་ལེན་མ་བྱས་ན། བཀོད་སྤྱོད་བྱེད་ཐབས་མེད།。 Monitor Off འཆར་ཆས་ཁ་རྒྱོབ Screensaver བརྙན་ཡོལ་སྲུང་སྐྱོབ་ Related Settings འབྲེལ་ཡོད་སྒྲིག་འགོད་ Set སྒྲིག་འགོད་བྱེད་ཏུ་སོང་། Please select device དམིགས་འཛིན་གྱི་སྒྲིག་ཆས་གདམ་རོགས། Screensaver Screensaver བརྙན་ཡོལ་སྲུང་སྐྱོབ་ Idle time སྒྱིད་ལུག་གི་དུས་ཚོད། Lock screen when activating screensaver བརྙན་ཤེལ་ལ་སྐུལ་སློང་བྱེད་སྐབས་བརྙན་ཤེལ་ལ་ཟྭ་རྒྱག་པ། Screensaver program བརྙན་ཤེལ་གྱི་འཆར་གཞི། View ལྟ་ཚུལ། UKUI UKUI Blank_Only Blank_Only Customize རང་མཚན་འཇོག་པ། 5min 5སྐར་མ་ 10min 10སྐར་མ་ 15min 15སྐར་མ་ 30min 30སྐར་མ་ 1hour 1ཆུ་ཚོད། Never གཏན་ནས་མི་བྱེད Screensaver source བརྙན་ཤེལ་གྱི་འབྱུང་ཁུངས། Select བདམས་ཐོན་བྱུང་བ། Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Wallpaper files (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *jpe *.gif *.tif *.tiff *.svg) select custom screensaver dir ཡུལ་སྲོལ་གོམས་གཤིས་ཀྱི་བརྙན་ཤེལ་གྲོན་ཆུང་བྱེད་མཁན་བདམས་པ་ Position: གོ་གནས་ནི། FileName: ཡིག་ཆའི་མིང་ནི། FileType: ཡིག་ཆའི་རིགས་དབྱིབས་ནི། Cancel ཕྱིར་འཐེན། Switching time བརྗེ་རེས་བྱེད་པའི་དུས་ཚོད། 1min 1སྐར་མ་ Ordinal སྲོལ་ཡིག Random སྐབས་བསྟུན་རང་བཞིན། Random switching སྐབས་བསྟུན་གྱིས་བརྗེ་རེས་བྱེད་པ Text(up to 30 characters): ཡི་གེ(ཆེས་མང་ན་ཡི་གེ་30ཡོད་པ་གཤམ་གསལ། ) Show rest time ངལ་གསོའི་དུས་ཚོད་མངོན་པ། Lock screen when screensaver boot བརྙན་ཤེལ་གྱི་ལྷམ་ཡུ་རིང་གི་དུས་སུ་བརྙན་ཤེལ་ལ་ཟྭ་རྒྱག Text position ཡི་གེའི་གོ་གནས། Centered ལྟེ་བར་འཛིན་པ། Randow(Bubble text) ལན་ཏུའོ་(ལྦུ་བ་ཅན་གྱི་ཡི་གེ) Picture does not exist འདྲ་པར་མེད། Not connected to a network དྲ་རྒྱ་སྦྲལ་མཐུད་མ་བྱུང་། The weather update failed གནམ་གཤིས་གསར་སྒྱར་ལ་ཕམ་ཁ་བྱུང་། ScreensaverUi Screensaver བརྙན་ཡོལ་སྲུང་སྐྱོབ་ /Screensaver/Screensaver Idle time དུས་ཚོད་འདི་འགོར་རྗེས་བརྙན་ཤེལ་སྲུང་བྱད་ཀྱི་ཁ་འབྱེད། /Screensaver/Idle time Screensaver program བརྙན་ཤེལ་གྱི་སྲུང་སྐྱོབ་བ་རིམ /Screensaver/Screensaver program Screensaver source བརྙན་ཤེལ་གྱི་འབྱུང་ཁུངས། /Screensaver/Screensaver source Select བདམ་གསེས། Ordinal གོ་རིམ་ལྟར Random སྐབས་བསྟུན་རང་བཞིན། Random switching སྐབས་བསྟུན་གྱིས་བརྗེ་རེས་བྱེད་པ /Screensaver/Random switching Switching time བརྗེ་རེས་བྱེད་པའི་ཟློས་ཕྱོད /Screensaver/Switching time Text(up to 30 characters): ཡི་གེ(ཆེས་མང་ན་ཡི་གེ་30ཡོད་པ་གཤམ་གསལ། ): /Screensaver/Text(up to 30 characters): Text position ཡི་གེའི་གནས། /Screensaver/Text position Centered ལྟེ་བར་འཛིན་པ། Randow(Bubble text) སྐབས་བསྟུན(ལྦུ་བ་ཅན་གྱི་ཡི་གེ) Show rest time ངལ་གསོའི་དུས་ཚོད་མངོན་པ། /Screensaver/Show rest time Select Screensaver སྲུང་ཡོལ་གདམ་གསེས་བྱེད་པ། Weather གནམ་གཤིས། Music གླུ་དབྱངས། Albun འདྲ་པར། Lock screen when screensaver boot བརྙན་ཤེལ་གྱི་གྲུང་སློང་བྱེད་དུས་བརྙན་ཡོལ་ཟྭ་རྒྱག /Screensaver/Lock screen when screensaver boot The date and time are displayed དུས་ཚོད་ཀྱི་ཚེས་གྲངས་གསལ་པོར་མངོན་པ། /Screensaver/The date and time are displayed Go to App Settings སྔོན་ལ་བེད་སྤྱོད་གཏོང་བའི་སྒྲིག་གཞི་འཛུགས་དགོས། Current position དེང་སྐབས་ཀྱི་གནས་བབ། /Screensaver/Show rest time Prioritize the list of songs from the Music app "རོལ་མོ་"ཉེར་སྤྱོད་ཀྱི་གླུ་གཞས་རེའུ་མིག་སྔོན་ལ་གཏོང་། /Screensaver/Prioritize the list of songs from the Music app Priority playlists དམིགས་སུ་བཀར་ནས་སྔོན་ལ་རེའུ་མིག་གཏོང་། /Screensaver/Priority playlists Select the folder ཡིག་ཆ་བཙུར་བ། Switch mode བྱེད་སྟངས་བརྗེ་རེས་བྱེད་པ། /Screensaver/Switch mode In order གོ་རིམ་ལྟར་བྱ་དགོས། Switching frequency སྐབས་བསྟུན་བྱེད་པ། /Screensaver/Switching frequency Image source པར་རིས་འབྱུང་ཁུངས། /Screensaver/Image source Add image folder པར་རིས་ཡིག་ཆའི་བཙུར་མ་བསྣན་པ། System wallpaper gallery མ་ལག་པར་རིས་ཐོན་ཁུངས། Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Wallpaper files (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *jpe *.gif *.tif *.tiff *.svg) select custom screensaver dir ཡུལ་སྲོལ་གོམས་གཤིས་ཀྱི་བརྙན་ཤེལ་གྲོན་ཆུང་བྱེད་མཁན་བདམས་པ་ Position: ས་གནས་ནི། FileName: ཡིག་ཆའི་མིང་ནི། FileType: ཡིག་ཆའི་རིགས་ནི། Cancel མེད་པར་བཟོ་དགོས Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir རོལ་དབྱངས་ཀྱི་ཐབས་ལམ་གདམ་ག་བྱེད་དགོས། The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. ཡིག་ཆལ་ཚོད་འཛིན་ཆེ་ཤོས་བྱས་ཟིན་པས།ཁྱེད་ཀྱིས་མུ་མཐུད་དུ་ཁ་སྣོན་བྱས་ཆོག ukui-screensaver/ukccplugins_i18n_ts/es.ts0000664000175000017500000006526015172041106017654 0ustar fengfeng Screenlock Screenlock Bloqueo de pantalla Screenlock Interface 锁屏界面 Show message on lock screen Mostrar mensaje en la pantalla de bloqueo Browse 浏览 Online Picture 线上图片 Local Pictures Fotos locales Online Pictures Imágenes en línea Reset To Default Restablecer a los valores predeterminados Related Settings Ajustes relacionados Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay Retraso de la pantalla de bloqueo Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin Mostrar imagen de bloqueo de pantalla en el inicio de sesión en pantalla Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never Nunca 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) Archivos de fondos de pantalla(*.jpg * .jpeg * .bmp * .dib * .png * .jfif *.jpe * .gif * .tif * .tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1 minuto 5min 5 minutos 10min 10 minutos 30min 30 minutos 45min 45min 1hour 1 hora 2hour 2 horas 3hour 3 horas Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application select custom wallpaper file Seleccionar archivo de fondo de pantalla personalizado Select Escoger Position: Posición: FileName: Nombre: FileType: Tipo de archivo: Cancel Cancelar Monitor Off Monitor apagado Screensaver Protector de pantalla Set Poner ScreenlockUi Show picture of screenlock on screenlogin Mostrar imagen de bloqueo de pantalla en el inicio de sesión en pantalla /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock Bloqueo de pantalla /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures Fotos locales /Screenlock/Local Pictures Online Pictures Imágenes en línea /Screenlock/Online Pictures Reset To Default Restablecer a los valores predeterminados /Screenlock/Reset To Default Bluetooth 蓝牙 No bluetooth adapter detected, can not use Leave Lock. Monitor Off Monitor apagado /Screenlock/Monitor Off Screensaver Protector de pantalla /Screenlock/Screensaver Related Settings Ajustes relacionados /Screenlock/Related Settings Set Poner Please select device Screensaver Screensaver Protector de pantalla Idle time Tiempo de inactividad Lock screen when activating screensaver Pantalla de bloqueo al activar el protector de pantalla Enable screensaver 开启屏保 Screensaver program Programa protector de pantalla idle time 等待时间 Min 分钟 Lock screen when screensaver boot Pantalla de bloqueo al arrancar el protector de pantalla screensaver 屏保 Default_ukui 默认屏保 View Vista Text(up to 30 characters): Texto (hasta 30 caracteres): Show rest time Mostrar tiempo de descanso UKUI UKUI Blank_Only Blank_Only Customize Personalizar 5min 5 minutos 10min 10 minutos 15min 15 minutos 30min 30 minutos 1hour 1 hora Screensaver source Fuente del protector de pantalla Select Escoger Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Archivos de fondos de pantalla(*.jpg * .jpeg * .bmp * .dib * .png * .jfif *.jfpe * .gif * .tif * .tiff *.wdp * .svg) select custom screensaver dir Seleccione el directorio del protector de pantalla personalizado Position: Posición: FileName: Nombre: FileType: Tipo de archivo: Cancel Cancelar Switching time Tiempo de conmutación 1min 1 minuto Ordinal Ordinal Random switching Conmutación aleatoria Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position Posición del texto Centered Centrado Randow(Bubble text) Randow(Texto de burbuja) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random Aleatorio Never Nunca ScreensaverUi Screensaver Protector de pantalla /Screensaver/Screensaver Idle time Tiempo de inactividad /Screensaver/Idle time Screensaver program Programa protector de pantalla /Screensaver/Screensaver program Screensaver source Fuente del protector de pantalla /Screensaver/Screensaver source Select Escoger Ordinal Ordinal Random Aleatorio Random switching Conmutación aleatoria /Screensaver/Random switching Switching time Tiempo de conmutación /Screensaver/Switching time Text(up to 30 characters): Texto (hasta 30 caracteres): /Screensaver/Text(up to 30 characters): Text position Posición del texto /Screensaver/Text position Centered Centrado Randow(Bubble text) Randow(Texto de burbuja) Show rest time Mostrar tiempo de descanso /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Archivos de fondos de pantalla(*.jpg * .jpeg * .bmp * .dib * .png * .jfif *.jfpe * .gif * .tif * .tiff *.wdp * .svg) select custom screensaver dir Seleccione el directorio del protector de pantalla personalizado Position: Posición: FileName: Nombre: FileType: Tipo de archivo: Cancel Cancelar ukui-screensaver/ukccplugins_i18n_ts/ms.ts0000664000175000017500000010443415172041106017661 0ustar fengfeng PhotoCorver Right open the file location Buka lokasi fail Remove the file Alih keluar fail Screenlock Screenlock Kunci Skrin Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) Fail kertas dinding(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Your system does not have any browser application installed or the default browser application is not set up Sistem anda tidak mempunyai sebarang aplikasi penyemak imbas yang dipasang atau aplikasi penyemak imbas lalai tidak disediakan Unable to open browser application Tidak dapat membuka aplikasi penyemak imbas select custom wallpaper file pilih fail kertas dinding tersuai Select Pilih Position: Jawatan: FileName: Nama fail : FileType: Jenis Fail: Cancel Batal Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin Tunjukkan gambar kunci skrin pada log masuk skrin /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) Kunci tinggalkan (Sistem akan dikunci apabila telefon yang dipasangkan hilang) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device Peranti yang ditentukan No paired phone. Please turn to 'Bluetooth' to pair. Tiada telefon berpasangan. Sila beralih kepada 'Bluetooth' untuk berpasangan. Screenlock Kunci Skrin /Screenlock/Screenlock Choose Picture Pilih Gambar /Wallpaper/Choose Picture Local Pictures Gambar Tempatan /Screenlock/Local Pictures Online Pictures Gambar Dalam Talian /Screenlock/Online Pictures Reset To Default Tetapkan Semula Kepada Lalai /Screenlock/Reset To Default Bluetooth Bluetooth No bluetooth adapter detected, can not use Leave Lock. Tiada penyesuai bluetooth dikesan, tidak boleh menggunakan Kunci Tinggalkan. Monitor Off Matikan Monitor /Screenlock/Monitor Off Screensaver Pelindung skrin /Screenlock/Screensaver Related Settings Tetapan Berkaitan /Screenlock/Related Settings Set Tetapkan Please select device Sila pilih peranti The date and time are displayed Tarikh dan masa dipaparkan /Screenlock/The date and time are displayed Screensaver Screensaver Pelindung skrin Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI UKUI Blank_Only 黑屏 Customize Tersuai 5min 5 min 10min 10 min 15min 15 min 30min 30 min 1hour 1 jam Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1 min Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never Tidak pernah ScreensaverUi Screensaver Pelindung skrin /Screensaver/Screensaver Idle time Masa terbiar /Screensaver/Idle time Screensaver program Program pelindung skrin /Screensaver/Screensaver program Screensaver source Sumber pelindung skrin /Screensaver/Screensaver source Select Pilih Ordinal Ordinal Random Rawak Select Screensaver Pilih Pelindung Skrin Weather Cuaca Music Muzik Albun Album Go to App Settings Pergi ke Tetapan Aplikasi Current position Kedudukan semasa /Screensaver/Show rest time Prioritize the list of songs from the Music app Utamakan senarai lagu daripada aplikasi Muzik /Screensaver/Prioritize the list of songs from the Music app Priority playlists Senarai main keutamaan /Screensaver/Priority playlists Select the folder Pilih folder Switch mode Tukar mod /Screensaver/Switch mode In order Mengikut tertib Switching frequency Kekerapan menukar /Screensaver/Switching frequency Image source Sumber imej /Screensaver/Image source Add image folder Tambah folder imej Random switching Penukaran rawak /Screensaver/Random switching Switching time Masa bertukar /Screensaver/Switching time Text(up to 30 characters): Teks (sehingga 30 aksara): /Screensaver/Text(up to 30 characters): Text position Kedudukan teks /Screensaver/Text position Centered Berpusat Randow(Bubble text) Rawak (Teks gelembung) Show rest time Tunjukkan waktu rehat /Screensaver/Show rest time Lock screen when screensaver boot Kunci skrin apabila pelindung skrin but /Screensaver/Lock screen when screensaver boot The date and time are displayed Tarikh dan masa dipaparkan /Screensaver/The date and time are displayed System wallpaper gallery Tarikh dan masa dipaparkan Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Fail kertas dinding(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir pilih dir pelindung skrin tersuai Position: Posisi: FileName: Nama fail : FileType: Jenis Fail: Cancel Batal Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) Fail muzik(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir pilih dir muzik pelindung skrin The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. Had maksimum untuk bilangan imej dalam folder telah dicapai; anda boleh mengalih keluar beberapa sebelum terus menambah lagi. Screensaver Picture does not exist Gambar tidak wujud Not connected to a network Tidak disambungkan ke rangkaian The weather update failed Kemas kini cuaca gagal MPRISSaverWidget No music detected Tiada muzik dikesan Unknown Tidak diketahui CustomAlbumViewer No photos detected, please re-select them Tiada foto dikesan, sila pilih semula ukui-screensaver/ukccplugins_i18n_ts/zh_HK.ts0000664000175000017500000010371115172041106020242 0ustar fengfeng PhotoCorver Right open the file location 打開檔案位置 Remove the file 拿掉 Screenlock Screenlock 鎖屏 Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) 圖片檔(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Your system does not have any browser application installed or the default browser application is not set up 您的系統未安裝任何瀏覽器應用或未設置預設瀏覽器應用 Unable to open browser application 無法打開瀏覽器應用 select custom wallpaper file 選擇自訂壁紙檔 Select 選擇 Position: 位置: FileName: 檔案名稱: FileType: 檔案類型: Cancel 取消 Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin 顯示鎖屏壁紙在登錄介面 /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) 動態鎖 (系統將在已配對的藍牙手機離開時自動鎖定螢幕) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device 指定設備 No paired phone. Please turn to 'Bluetooth' to pair. 無配對手機,請轉到 「藍牙」 以連接手機。 Screenlock 鎖屏 /Screenlock/Screenlock Choose Picture 選擇圖片 /Wallpaper/Choose Picture Local Pictures 本地圖片 /Screenlock/Local Pictures Online Pictures 線上圖片 /Screenlock/Online Pictures Reset To Default 恢復預設 /Screenlock/Reset To Default Bluetooth 藍牙設置 No bluetooth adapter detected, can not use Leave Lock. 未檢測到藍牙適配器,無法使用動態鎖功能。 Monitor Off 關閉顯示器 /Screenlock/Monitor Off Screensaver 屏幕保護 /Screenlock/Screensaver Related Settings 相關設置 /Screenlock/Related Settings Set 去設置 Please select device 請選擇指定設備 The date and time are displayed 顯示時間日期 /Screenlock/The date and time are displayed Screensaver Screensaver 屏保 Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI UKUI Blank_Only 黑屏 Customize 自訂 5min 5 分鐘 10min 10 分鐘 15min 15 分鐘 30min 30 分鐘 1hour 1 小時 Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1分鐘 Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never 從不 ScreensaverUi Screensaver 屏保 /Screensaver/Screensaver Idle time 此時間段后開啟屏保 /Screensaver/Idle time Screensaver program 屏幕保護程式 /Screensaver/Screensaver program Screensaver source 屏保來源 /Screensaver/Screensaver source Select 選擇 Ordinal 按順序 Random 隨機 Select Screensaver 選擇屏保 Weather 天氣 Music 音樂 Albun 相冊 Go to App Settings 前往應用設置 Current position 當前位置 /Screensaver/Show rest time Prioritize the list of songs from the Music app 優先播放「音樂」應用的歌曲清單 /Screensaver/Prioritize the list of songs from the Music app Priority playlists 優先播放清單 /Screensaver/Priority playlists Select the folder 選擇資料夾 Switch mode 切換方式 /Screensaver/Switch mode In order 按順序 Switching frequency 切換頻率 /Screensaver/Switching frequency Image source 圖片來源 /Screensaver/Image source Add image folder 添加圖片資料夾 Random switching 隨機切換 /Screensaver/Random switching Switching time 切換頻率 /Screensaver/Switching time Text(up to 30 characters): 屏保文本(最多 30 個字): /Screensaver/Text(up to 30 characters): Text position 文本位置 /Screensaver/Text position Centered 居中 Randow(Bubble text) 隨機(氣泡文字) Show rest time 顯示休息時間 /Screensaver/Show rest time Lock screen when screensaver boot 啟動屏保時鎖定螢幕 /Screensaver/Lock screen when screensaver boot The date and time are displayed 顯示時間日期 /Screensaver/The date and time are displayed System wallpaper gallery 系統壁紙圖庫 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁紙檔(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 選擇自定義屏保路徑 Position: 位置: FileName: 檔案名稱: FileType: 檔案類型: Cancel 取消 Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) 音訊檔(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir 選擇屏保音訊資料夾 The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. 圖片資料夾添加已達數量上限,可移除後繼續添加 Screensaver Picture does not exist 圖片不存在 Not connected to a network 網路未連接 The weather update failed 天氣更新失敗 MPRISSaverWidget No music detected 未檢測到歌曲 Unknown 未知 CustomAlbumViewer No photos detected, please re-select them 未檢測到照片,請重新選擇 ukui-screensaver/ukccplugins_i18n_ts/kk.ts0000664000175000017500000010756615172041106017660 0ustar fengfeng PhotoCorver Right open the file location ۅجۅت وردۇن اچۇۇ Remove the file ۅندۉرۉپ تاشتاش Screenlock Screenlock ەكٸران قۇلىبى Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) تام قاعازى حۇجاتى(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Your system does not have any browser application installed or the default browser application is not set up سەستىماڭىزدا تور كۅرگۉچ پىروگىرامماسى قاچالانباعان كۅرۉنۉشتۅرۉ سۈكۈتتىكى تور كۅرگۉچ پىروگىرامماسى تەڭشەلبەگەن Unable to open browser application تور شولىعىش پٸروگٸرامماسٸن اشاالاما select custom wallpaper file ٶزى بەلگىلەەن تام قاعازى حۇجاتىن تالدا Select تالداۋ Position: :ورنى FileName: :حۇجات مى FileType: :حۇجات تۇرى Cancel كۇشىنەن قالدىرۋ Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin كورسەتۋ ەكرانداعى تام قاعازى كىرۋ كورىنبە بەتىندە /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) قۇلىبىن قالدىرىپ قويۋ (بٸر جۇپ تەلەفون جوعالىپ كەتكەندە سەستيما قۇلىپتالادى) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device بەلگٸلەنگەن اسباب No paired phone. Please turn to 'Bluetooth' to pair. جۇپ تەلەفون جوق 'كۆكچىش' نى قوشۇۋېلىپ قوشۇڭ. Screenlock ەكٸران قۇلىبى /Screenlock/Screenlock Choose Picture راسىم تالدا /Wallpaper/Choose Picture Local Pictures جەرلىك سۋرەتتەر /Screenlock/Local Pictures Online Pictures تورداعٸ سۋرەتتەر /Screenlock/Online Pictures Reset To Default كۆڭۈلدىكىگە قايرا بەلگىلۅۅ /Screenlock/Reset To Default Bluetooth كۆكچىش No bluetooth adapter detected, can not use Leave Lock. كۆكچىش شايكەشتىرگىچ بايقالبادى، Leave Lock نى ىشتەتكەلى بولبويت . Monitor Off قاداعالاۋ ٴوشىرۋ /Screenlock/Monitor Off Screensaver ەكٸران قورعاۋ /Screenlock/Screensaver Related Settings قاتىناستىق تەڭشەۋلەر /Screenlock/Related Settings Set تەڭشە Please select device اسبابٸن تالدا The date and time are displayed كۅرسۅتۉۉ ۇباقىت ۇباقتى /Screenlock/The date and time are displayed Screensaver Screensaver ەكٸران قورعاۋ Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI Blank_Only 黑屏 Customize ٶز ولشەم 5min 5 10min ون مينۋت 15min 15 مينوت 30min 30 مىنوت 1hour بٸر ساعات Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1 مىنوت Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never بۇرٸننان ScreensaverUi Screensaver ەكٸران قورعاۋ /Screensaver/Screensaver Idle time بوس ۋاقىتى /Screensaver/Idle time Screensaver program ەكٸران قورعاۋ پىروگىرامماسى /Screensaver/Screensaver program Screensaver source ەكٸران بەتى قاينارى /Screensaver/Screensaver source Select تالداۋ Ordinal داستۇرلٸ Random ەرٸكتٸ Select Screensaver ەكىران اسىراشتى تانداش Weather اۋارايى Music مۇزيكا Albun البوم Go to App Settings قولدونۇشچان قۇرۇۇ ، اچۇۇ ، باشتوو جاسوو ،اتقارۇۇ Current position گەزەكتەكى وردۇ /Screensaver/Show rest time Prioritize the list of songs from the Music app الدىن «مۇزىكا »نىڭ قولدونۇلاتۇرعان ىر تىزىمدىگىن قويۇش كەرەك /Screensaver/Prioritize the list of songs from the Music app Priority playlists الدىن تىزىمدىكتى قويۇش /Screensaver/Priority playlists Select the folder ۅجۅت قىپچىعىچ تانداش Switch mode سايكەستىرۋ كۇيى /Screensaver/Switch mode In order تارتىپ بويىنشا Switching frequency سايكەستىرۋ جيىلىگى /Screensaver/Switching frequency Image source سۋرەت قاينارى /Screensaver/Image source Add image folder سۉرۅتتۉ ۅجۅت قىپچىعىچ قوشۇڭ Random switching قالاعانىڭىزشا سايكەستىرۋ /Screensaver/Random switching Switching time سايكەستىرۋ ۋاقىتى /Screensaver/Switching time Text(up to 30 characters): تەكىسىت(ەڭ كوپ بولعاندا 30 ٴارىپ): /Screensaver/Text(up to 30 characters): Text position تەكىسىت ورنى /Screensaver/Text position Centered جيناقتالىنعان Randow(Bubble text) ەرٸكتٸ(كوبىكشە تەكىسىت) Show rest time دەمالٸس الۋ ۋاقىتىن كورسەتۋ /Screensaver/Show rest time Lock screen when screensaver boot ەكٸرانٸ كوز بەك سارالاۋعا ەكٸرانٸ قۇلپىلاۋ /Screensaver/Lock screen when screensaver boot The date and time are displayed كۅرسۅتۉۉ ۇباقىت ۇباقتى /Screensaver/The date and time are displayed System wallpaper gallery سەستىما تام قاعازى چېرتىيوژى قازىناعى Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) تام قاعازى حۇجاتتارى(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir ەرەكشە ەكٸران باقىلاۋدى تالداۋ Position: :ورنى FileName: :حۇجات مى FileType: :حۇجات تۇرى Cancel كۇشىنەن قالدىرۋ Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) دووشتۇ ۅجۅت (*.mp3*.wav*.flac .ogg.aac*.m4a *.wma.opus.aiff.ape) select screensaver music dir ەكراندۇۇ دووشتۇ ۅجۅت قىپچىعىچتى تانداش The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. سۉرۅت ۅجۅت قىپچىعىچ قوشۇلعان ساندىن جوعورۇ چەگىنە جەتى ، بۆلۈۋەتكەندىن كىيىن جالعاشتۇۇ قوشۇۇعا بولوت Screensaver Picture does not exist راسىم ساقتالعان ەمەس Not connected to a network تور ۇلانبادى The weather update failed ھاۋارايىنىڭ جاڭىرىلىشى جەڭىلۉۉ بولدۇ MPRISSaverWidget No music detected ىردى تەكشەرىپ بايقابادى Unknown كۋالىك CustomAlbumViewer No photos detected, please re-select them سۉرۅتتۉ تەكشۈرمىگەندە، قايتادان تانداڭ ukui-screensaver/ukccplugins_i18n_ts/vi.ts0000664000175000017500000010575215172041106017664 0ustar fengfeng PhotoCorver Right open the file location ngay tại vị trí mở tệp Remove the file loại bỏ tệp Screenlock Screenlock Khóa màn hình Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) Tệp ảnh (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Your system does not have any browser application installed or the default browser application is not set up Hệ thống của bạn không có bất kỳ ứng dụng trình duyệt nào được cài đặt hoặc ứng dụng trình duyệt mặc định không được thiết lập Unable to open browser application không thể mở ứng dụng select custom wallpaper file Chọn tệp hình nền tùy chỉnh Select Chọn Position: Vị trí FileName: Tên tệp FileType: Loại Cancel Hủy Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin Hiển thị hình nền màn hình khóa ở giao diện đăng nhập /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) khóa động thái (Hệ thống sẽ bị khóa khi điện thoại được kết nối bị ngắt) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device thiết bị chỉ định No paired phone. Please turn to 'Bluetooth' to pair. không có điện thoại được kết nối. vui lòng bật kết nối Bluetooth để kết nối. Screenlock Khóa màn hình /Screenlock/Screenlock Choose Picture Chọn hình ảnh /Wallpaper/Choose Picture Local Pictures Ảnh cục bộ /Screenlock/Local Pictures Online Pictures Ảnh trực tuyến /Screenlock/Online Pictures Reset To Default Khôi phục về mặc định /Screenlock/Reset To Default Bluetooth Bluetooth No bluetooth adapter detected, can not use Leave Lock. không phát hiện bộ điều hợp Bluetooth, không thể dùng Khóa động thái. Monitor Off Tắt màn hình /Screenlock/Monitor Off Screensaver bảo vệ màn hình /Screenlock/Screensaver Related Settings Cài đặt liên quan /Screenlock/Related Settings Set Cài đặt Please select device vui lòng chọn thiết bị The date and time are displayed hiển thị thời gian ngày tháng /Screenlock/The date and time are displayed Screensaver Screensaver bảo vệ màn hình Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI UKUI Blank_Only 黑屏 Customize Tùy chỉnh 5min 5 phút 10min 10 phút 15min 15 phút 30min 30 phút 1hour 1 giờ Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1 phút Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never Không bao giờ ScreensaverUi Screensaver bảo vệ màn hình /Screensaver/Screensaver Idle time Bật trình bảo vệ màn hình sau thời gian này /Screensaver/Idle time Screensaver program Trình bảo vệ màn hình /Screensaver/Screensaver program Screensaver source Nguồn trình bảo vệ màn hình /Screensaver/Screensaver source Select Chọn Ordinal Theo thứ tự Random Phát ngẫu nhiên Select Screensaver chọn bảo vệ màn hình Weather Thời tiết Music Bài hát Albun Album Go to App Settings đi tới cài đặt ứng dụng Current position vị trí hiện tại /Screensaver/Show rest time Prioritize the list of songs from the Music app ưu tiên danh sách nhạc từ ứng dụng Music /Screensaver/Prioritize the list of songs from the Music app Priority playlists ưu tiên danh sách phát /Screensaver/Priority playlists Select the folder chọn tệp Switch mode Chuyển đổi chế độ mạng /Screensaver/Switch mode In order theo thứ tự Switching frequency Tần số chuyển đổi /Screensaver/Switching frequency Image source Nguồn hình ảnh /Screensaver/Image source Add image folder thêm tệp ảnh Random switching Chuyển đổi ngẫu nhiên /Screensaver/Random switching Switching time Tần số chuyển đổi /Screensaver/Switching time Text(up to 30 characters): Văn bản bảo vệ màn hình (tối đa 30 ký tự): /Screensaver/Text(up to 30 characters): Text position vị trí văn bản /Screensaver/Text position Centered Trung tâm Randow(Bubble text) Ngẫu nhiên (văn bản bong bóng) Show rest time Hiển thị thời gian nghỉ trên trình bảo vệ màn hình /Screensaver/Show rest time Lock screen when screensaver boot Khóa màn hình khi kích hoạt trình bảo vệ màn hình /Screensaver/Lock screen when screensaver boot The date and time are displayed hiển thị thời gian ngày tháng /Screensaver/The date and time are displayed System wallpaper gallery Thư viện hình nền hệ thống Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Tệp hình nền (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Chọn đường dẫn bảo vệ màn hình tùy chỉnh Position: Vị trí FileName: Tên tệp FileType: Loại Cancel Hủy Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) Tệp nhạc (* .mp3 * .wav * .ogg * .flac * .aac * .m4a * .wma * .opus * .aiff .ape) select screensaver music dir Chọn bảo vệ màn hình thư mục âm nhạc The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. Đã đạt đến giới hạn tối đa về số lượng hình ảnh trong thư mục; Bạn có thể xóa một số trước khi tiếp tục thêm nhiều hơn. Screensaver Picture does not exist Hình ảnh không tồn tại Not connected to a network Không được kết nối mạng The weather update failed cập nhật thời tiết thất bại MPRISSaverWidget No music detected không phát hiện ra nhạc Unknown Chưa xác định CustomAlbumViewer No photos detected, please re-select them Không phát hiện thấy ảnh, vui lòng chọn lại ukui-screensaver/ukccplugins_i18n_ts/CMakeLists.txt0000664000175000017500000000053515172041106021427 0ustar fengfengfind_package(Qt6Linguist) file(GLOB ts_files *.ts) qt6_add_translation(qm_files ${ts_files}) add_custom_target(ukccplugins_i18n DEPENDS ${qm_files} SOURCES ${ts_files} ) install(FILES ${ts_files} DESTINATION /usr/share/ukui-screensaver/ukccplugins/i18n/) install(FILES ${qm_files} DESTINATION /usr/share/ukui-screensaver/ukccplugins/i18n_qm/) ukui-screensaver/ukccplugins_i18n_ts/mn.ts0000664000175000017500000012241115172041106017647 0ustar fengfeng CustomAlbumViewer No photos detected, please re-select them ᠭᠡᠷᠡᠯ ᠵᠢᠷᠤᠭ ᠢ ᠪᠠᠶᠢᠴᠠᠭᠠᠨ ᠰᠢᠯᠭᠠᠭᠰᠠᠨ ᠦᠭᠡᠢ ᠪᠣᠯ ᠳᠠᠬᠢᠨ ᠰᠣᠩᠭᠣᠭᠠᠷᠠᠢ ᠃ MPRISSaverWidget No music detected ᠳᠠᠭᠤᠤ ᠬᠥᠭ᠍ᠵᠢᠮ ᠢ ᠪᠠᠢ᠌ᠴᠠᠭᠠᠵᠤ ᠣᠯᠬᠤ ᠦᠭᠡᠢ ᠃ Unknown ᠦᠯᠦ ᠮᠡᠳᠡᠬᠦ PhotoCorver Right open the file location ᠪᠢᠴᠢᠭ᠌ ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ ᠤᠨ ᠪᠠᠢ᠌ᠷᠢ ᠶᠢ ᠨᠡᠭᠡᠭᠡᠨ᠎ᠡ ᠃ Remove the file ᠪᠢᠴᠢᠭ᠌ ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ ᠢ ᠠᠷᠢᠯᠭᠠᠬᠤ Screenlock Screenlock ᠳᠡᠯᠭᠡᠴᠡ ᠣᠨᠢᠰᠤᠯᠠᠬᠤ Show message on lock screen ᠳᠡᠯᠭᠡᠴᠡ ᠴᠣᠣᠵᠢᠯᠠᠬᠤ ᠦᠶᠡᠰ ᠵᠠᠩᠭᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠬᠦ Browse ᠦᠵᠡ ᠃ Online Picture ᠰᠦᠯᠵᠢᠶᠡᠨ ᠳᠡᠭᠡᠷᠡᠬᠢ ᠵᠢᠷᠤᠭ Local Pictures ᠲᠤᠰ ᠭᠠᠵᠠᠷ᠎ᠤ᠋ᠨ ᠵᠢᠷᠤᠭ Online Pictures ᠰᠦᠯᠵᠢᠶᠡᠨ ᠳᠡᠭᠡᠷᠡᠬᠢ ᠵᠢᠷᠤᠭ Reset To Default ᠠᠶᠠᠳᠠᠯ ᠢ᠋ ᠰᠡᠷᠬᠦᠬᠡᠬᠦ Related Settings ᠬᠠᠮᠢᠶ᠎ᠠ ᠪᠦᠬᠦᠢ ᠳᠤᠬᠢᠷᠠᠭᠤᠯᠭ᠎ᠠ Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay ᠲᠤᠰ ᠴᠠᠭ᠎ᠤ᠋ᠨ ᠬᠡᠰᠡᠭ᠎ᠦ᠋ᠨ ᠠᠷᠤ ᠳᠡᠯᠭᠡᠴᠡ Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin ᠣᠨᠢᠰᠤᠯᠠᠭᠰᠠᠨ ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠨᠠᠨ ᠵᠢᠷᠤᠭ ᠨᠡᠪᠳᠡᠷᠡᠬᠦ ᠵᠠᠭᠠᠭ ᠭᠠᠳᠠᠷᠭᠤ ᠳᠡᠭᠡᠷ᠎ᠡ ᠢᠯᠡᠷᠡᠬᠦ Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never ᠶᠡᠷᠦ ᠡᠴᠡ ᠥᠬᠡᠢ 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) ᠵᠢᠷᠤᠭ ᠤ᠋ᠨ ᠹᠠᠢᠯ (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1 ᠮᠢᠨᠦ᠋ᠲ 5min 5 ᠮᠢᠨᠦ᠋ᠲ 10min 10 ᠮᠢᠨᠦ᠋ᠲ 30min 30 ᠮᠢᠨᠦ᠋ᠲ 45min 45 ᠮᠢᠨᠦ᠋ᠲ 1hour 1 ᠴᠠᠭ 2hour 2 ᠴᠠᠭ 3hour 3 ᠴᠠᠭ Your system does not have any browser application installed or the default browser application is not set up ᠲᠠᠨ ᠤ᠋ ᠰᠢᠰᠲ᠋ᠧᠮ ᠳ᠋ᠤ᠌ ᠶᠠᠮᠠᠷ ᠴᠤ᠌ ᠦᠵᠡᠬᠦᠷ ᠬᠡᠷᠡᠭᠯᠡᠭᠡ ᠵᠢ ᠤᠭᠰᠠᠷᠠᠭᠰᠠᠨ ᠥᠬᠡᠢ ᠪᠤᠶᠤ ᠠᠶᠠᠳᠠᠯ ᠦᠵᠡᠬᠦᠷ ᠬᠡᠷᠡᠭᠯᠡᠭᠡ ᠵᠢ ᠳᠤᠬᠢᠷᠠᠭᠤᠯᠤᠭᠰᠠᠨ ᠥᠬᠡᠢ Unable to open browser application ᠦᠵᠡᠬᠦᠷ ᠬᠡᠷᠡᠭᠯᠡᠭᠡ ᠵᠢ ᠨᠡᠬᠡᠬᠡᠬᠦ ᠵᠢᠨ ᠠᠷᠭ᠎ᠠ ᠥᠬᠡᠢ select custom wallpaper file ᠦᠪᠡᠷᠳᠡᠭᠡᠨ ᠳᠤᠭᠳᠠᠭᠠᠬᠤ ᠬᠠᠨᠠᠨ ᠵᠢᠷᠤᠭ ᠤ᠋ᠨ ᠹᠠᠢᠯ ᠢ᠋ ᠰᠣᠩᠭᠣᠬᠤ Select ᠰᠤᠩᠭᠤᠬᠤ Position: ᠪᠠᠢᠷᠢᠯᠠᠯ: FileName: ᠹᠠᠢᠯ᠎ᠤᠨ ᠨᠡᠷ᠎ᠡ ᠄ FileType: ᠹᠠᠢᠯ ᠳᠦᠷᠦᠯ ᠵᠦᠢᠯ: Cancel ᠦᠬᠡᠢᠰᠬᠡᠬᠦ Monitor Off ᠦᠵᠡᠬᠦᠷ ᠢ᠋ ᠬᠠᠭᠠᠬᠤ Screensaver ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ Set ᠣᠴᠢᠵᠤ ᠳᠤᠬᠢᠷᠠᠭᠤᠯᠬᠤ The date and time are displayed ᠴᠠᠭ ᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠦᠨ᠎ᠡ ᠃ /Screenlock/The date and time are displayed ScreenlockUi Show picture of screenlock on screenlogin ᠣᠨᠢᠰᠤᠯᠠᠭᠰᠠᠨ ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠨᠠᠨ ᠵᠢᠷᠤᠭ ᠨᠡᠪᠳᠡᠷᠡᠬᠦ ᠵᠠᠭᠠᠭ ᠭᠠᠳᠠᠷᠭᠤ ᠳᠡᠭᠡᠷ᠎ᠡ ᠢᠯᠡᠷᠡᠬᠦ /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) ᠬᠦᠳᠡᠯᠦᠩᠬᠦᠢ ᠣᠨᠢᠰᠣ ( ᠰᠢᠰᠲ᠋ᠧᠮ ᠨᠢ ᠨᠢᠬᠡᠨᠳᠡ ᠠᠪᠴᠠᠯᠳᠤᠭᠰᠠᠨ ᠯᠠᠨᠶᠠ ᠭᠠᠷ ᠤᠳᠠᠰᠤᠨ ᠡᠴᠡ ᠰᠠᠯᠬᠤ ᠦᠶ᠎ᠡ ᠳ᠋ᠤ᠌ ᠠᠦ᠋ᠲ᠋ᠣ᠋ ᠪᠡᠷ ᠳᠡᠯᠭᠡᠴᠡ ᠵᠢ ᠣᠨᠢᠰᠤᠯᠠᠨ᠎ᠠ) Specified device ᠳᠦᠬᠦᠬᠡᠷᠦᠮᠵᠢ ᠵᠢ ᠳᠤᠭᠳᠠᠭᠠᠬᠤ No paired phone. Please turn to 'Bluetooth' to pair. ᠭᠠᠷ ᠤᠳᠠᠰᠤ ᠲᠠᠢ ᠠᠪᠴᠠᠯᠳᠤᠭᠰᠠᠨ ᠥᠬᠡᠢ᠂ ' ᠯᠠᠨᠶᠠ ' ᠪᠡᠷ ᠭᠠᠷ ᠤᠳᠠᠰᠤ ᠲᠠᠢ ᠬᠣᠯᠪᠣᠭᠠᠷᠠᠢ᠃ Screenlock ᠳᠡᠯᠭᠡᠴᠡ ᠣᠨᠢᠰᠤᠯᠠᠬᠤ /Screenlock/Screenlock The date and time are displayed ᠴᠠᠭ ᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠦᠨ᠎ᠡ ᠃ /Screenlock/The date and time are displayed Choose Picture ᠵᠢᠷᠤᠭ ᠰᠣᠩᠭᠣᠬᠤ /Wallpaper/Choose Picture Local Pictures ᠲᠤᠰ ᠭᠠᠵᠠᠷ᠎ᠤ᠋ᠨ ᠵᠢᠷᠤᠭ /Screenlock/Local Pictures Online Pictures ᠰᠦᠯᠵᠢᠶᠡᠨ ᠳᠡᠭᠡᠷᠡᠬᠢ ᠵᠢᠷᠤᠭ /Screenlock/Online Pictures Reset To Default ᠠᠶᠠᠳᠠᠯ ᠰᠡᠷᠭᠦᠭᠡᠬᠦ /Screenlock/Reset To Default Bluetooth ᠯᠠᠨᠶᠠ ᠳᠤᠬᠢᠷᠠᠭᠤᠯᠭ᠎ᠠ No bluetooth adapter detected, can not use Leave Lock. ᠯᠠᠨᠶᠠ ᠲᠠᠢ ᠠᠪᠴᠠᠯᠳᠤᠬᠤ ᠬᠡᠷᠡᠭᠰᠡᠯ ᠢ᠋ ᠬᠢᠨᠠᠨ ᠬᠡᠮᠵᠢᠵᠤ ᠤᠯᠤᠭᠰᠠᠨ ᠥᠬᠡᠢ᠂ ᠬᠦᠳᠡᠯᠦᠩᠬᠦᠢ ᠣᠨᠢᠰᠣ ᠵᠢᠨ ᠴᠢᠳᠠᠪᠬᠢ ᠵᠢ ᠬᠡᠷᠡᠭᠯᠡᠬᠦ ᠵᠢᠨ ᠠᠷᠭ᠎ᠠ ᠥᠬᠡᠢ᠃ Monitor Off ᠦᠵᠡᠬᠦᠷ ᠢ᠋ ᠬᠠᠭᠠᠬᠤ Screensaver ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠬᠡᠯᠪᠡᠷᠢ Related Settings ᠬᠠᠮᠢᠶ᠎ᠠ ᠪᠦᠬᠦᠢ ᠳᠤᠬᠢᠷᠠᠭᠤᠯᠭ᠎ᠠ Set ᠳᠤᠬᠢᠷᠠᠭᠤᠯᠬᠤ Please select device ᠳᠤᠭᠳᠠᠭᠠᠭᠰᠠᠨ ᠳᠦᠬᠦᠬᠡᠷᠦᠮᠵᠢ ᠵᠢ ᠰᠣᠩᠭᠣᠭᠠᠷᠠᠢ Screensaver Screensaver ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠬᠡᠯᠪᠡᠷᠢ Idle time ᠲᠤᠰ ᠴᠠᠭ ᠤ᠋ᠨ ᠬᠡᠰᠡᠭ ᠤ᠋ᠨ ᠳᠠᠷᠠᠭ᠎ᠠ ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠮᠠᠭᠠᠯᠠᠯᠳᠠ ᠵᠢ ᠡᠬᠢᠯᠡᠬᠦᠯᠬᠦ Lock screen when activating screensaver ᠳᠡᠯᠭᠡᠴᠡ᠎ᠶ᠋ᠢ ᠢᠳᠡᠪᠬᠢᠵᠢᠭᠦᠯᠬᠦ᠎ᠳ᠋ᠦ᠍ ᠳᠡᠯᠭᠡᠴᠡ᠎ᠶ᠋ᠢ ᠣᠨᠢᠰᠤᠯᠠᠬᠤ Enable screensaver 开启屏保 Screensaver program ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠫᠷᠣᠭ᠌ᠷᠠᠮ idle time 等待时间 Min 分钟 Lock screen when screensaver boot ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠮᠠᠭᠠᠯᠠᠯᠳᠠ ᠵᠢ ᠡᠬᠢᠯᠡᠬᠦᠯᠬᠦ ᠦᠶ᠎ᠡ ᠳ᠋ᠤ᠌ ᠳᠡᠯᠭᠡᠴᠡ ᠵᠢ ᠣᠨᠢᠰᠤᠯᠠᠬᠤ screensaver 屏保 Default_ukui 默认屏保 View ᠪᠠᠢᠴᠠᠭᠠᠵᠤ ᠦᠵᠡᠬᠦ Text(up to 30 characters): ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠲᠸᠺᠰᠲ ( ᠬᠠᠮᠤᠭ ᠤ᠋ᠨ ᠣᠯᠠᠨ ᠳ᠋ᠤ᠌ ᠪᠡᠨ 30 ᠦᠰᠦᠭ): Show rest time ᠠᠮᠠᠷᠠᠬᠤ ᠴᠠᠭ ᠢ᠋ ᠢᠯᠡᠷᠡᠬᠦᠯᠬᠦ UKUI UKUI Blank_Only ᠬᠠᠷ᠎ᠠ ᠳᠡᠯᠭᠡᠴᠡ Customize ᠦᠪᠡᠷᠳᠡᠭᠡᠨ ᠳᠤᠳᠤᠷᠬᠠᠢᠯᠠᠬᠤ 5min 5 ᠮᠢᠨᠦ᠋ᠲ 10min 10 ᠮᠢᠨᠦ᠋ᠲ 15min 15 ᠮᠢᠨᠦ᠋ᠲ 30min 30 ᠮᠢᠨᠦ᠋ᠲ 1hour 1 ᠴᠠᠭ Screensaver source ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠢᠷᠡᠯᠳᠡ Select ᠰᠤᠩᠭᠤᠬᠤ Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) ᠬᠠᠨᠠᠨ ᠵᠢᠷᠤᠭ ᠤ᠋ᠨ ᠹᠠᠢᠯ (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir ᠦᠪᠡᠷᠳᠡᠭᠡᠨ ᠳᠤᠭᠳᠠᠭᠠᠬᠤ ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠮᠠᠭᠠᠯᠠᠯᠳᠠ ᠵᠢᠨ ᠵᠢᠮ ᠢ᠋ ᠰᠣᠩᠭᠣᠬᠤ Position: ᠪᠠᠢᠷᠢ: FileName: ᠹᠠᠢᠯ ᠤ᠋ᠨ ᠨᠡᠷ᠎ᠡ: FileType: ᠹᠠᠢᠯ ᠤ᠋ᠨ ᠳᠦᠷᠦᠯ ᠵᠦᠢᠯ: Cancel ᠦᠬᠡᠢᠰᠬᠡᠬᠦ Switching time ᠳᠠᠪᠳᠠᠮᠵᠢ ᠵᠢ ᠰᠣᠯᠢᠬᠤ 1min 1 ᠮᠢᠨᠦ᠋ᠲ Ordinal ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠵᠢᠡᠷ Random switching ᠳᠠᠰᠢᠷᠠᠮ ᠵᠢᠡᠷ ᠰᠣᠯᠢᠬᠤ Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position ᠲᠸᠺᠰᠲ ᠤ᠋ᠨ ᠪᠠᠢᠷᠢ Centered ᠳᠦᠪᠯᠡᠷᠡᠬᠦᠯᠬᠦ Randow(Bubble text) ᠳᠠᠰᠢᠷᠠᠮ ( ᠬᠦᠬᠡᠰᠦ ᠲᠸᠺᠰᠲ) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random ᠳᠠᠰᠢᠷᠠᠮ Never ᠶᠡᠷᠦ ᠡᠴᠡ ᠥᠬᠡᠢ Picture does not exist ᠵᠢᠷᠤᠭ ᠪᠠᠢᠬᠤ ᠥᠬᠡᠢ Not connected to a network ᠲᠣᠣᠷ ᠰᠦᠯᠵᠢᠶ᠎ᠡ ᠶᠢ ᠬᠣᠯᠪᠣᠭᠰᠠᠨ ᠦᠭᠡᠢ ᠃ The weather update failed ᠠᠭᠤᠷ ᠠᠮᠢᠰᠭᠤᠯ ᠰᠢᠨᠡᠳᠭᠡᠬᠦ ᠳᠦ ᠢᠯᠠᠭᠳᠠᠪᠠ ᠃ ScreensaverUi Screensaver ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠬᠡᠯᠪᠡᠷᠢ /Screensaver/Screensaver Idle time ᠲᠤᠰ ᠴᠠᠭ ᠤ᠋ᠨ ᠬᠡᠰᠡᠭ ᠤ᠋ᠨ ᠳᠠᠷᠠᠭ᠎ᠠ ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠮᠠᠭᠠᠯᠠᠯᠳᠠ ᠵᠢ ᠡᠬᠢᠯᠡᠬᠦᠯᠬᠦ /Screensaver/Idle time Screensaver program ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠫᠷᠣᠭ᠌ᠷᠠᠮ /Screensaver/Screensaver program Screensaver source ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠢᠷᠡᠯᠳᠡ /Screensaver/Screensaver source Select ᠰᠤᠩᠭᠤᠬᠤ Ordinal ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠵᠢᠡᠷ Random ᠳᠠᠰᠢᠷᠠᠮ Random switching ᠳᠠᠰᠢᠷᠠᠮ ᠵᠢᠡᠷ ᠰᠣᠯᠢᠬᠤ /Screensaver/Random switching Switching time ᠳᠠᠪᠳᠠᠮᠵᠢ ᠵᠢ ᠰᠣᠯᠢᠬᠤ /Screensaver/Switching time Text(up to 30 characters): ᠳᠡᠯᠭᠡᠴᠡ ᠬᠠᠮᠠᠭᠠᠯᠠᠬᠤ ᠲᠸᠺᠰᠲ ( ᠬᠠᠮᠤᠭ ᠤ᠋ᠨ ᠣᠯᠠᠨ ᠳ᠋ᠤ᠌ ᠪᠡᠨ 30 ᠦᠰᠦᠭ): /Screensaver/Text(up to 30 characters): Text position ᠲᠸᠺᠰᠲ ᠤ᠋ᠨ ᠪᠠᠢᠷᠢᠯᠠᠯ /Screensaver/Text position Centered ᠳᠦᠪᠯᠡᠷᠡᠬᠦᠯᠬᠦ Randow(Bubble text) ᠳᠠᠰᠢᠷᠠᠮ ( ᠬᠦᠬᠡᠰᠦ ᠲᠸᠺᠰᠲ) Show rest time ᠠᠮᠠᠷᠠᠬᠤ ᠴᠠᠭ ᠢ᠋ ᠢᠯᠡᠷᠡᠬᠦᠯᠬᠦ /Screensaver/Show rest time Select Screensaver ᠳᠡᠯᠬᠡᠴᠡ ᠶᠢᠨ ᠪᠠᠲᠤᠯᠠᠭ᠎ᠠ ᠶᠢ ᠰᠣᠩᠭᠣᠨ᠎ᠠ ᠃ Weather ᠴᠠᠭ ᠠᠭᠤᠷ Music ᠳᠠᠭᠤᠤ ᠬᠥᠭᠵᠢᠮ Albun ᠵᠢᠷᠤᠭ ᠤ᠋ᠨ ᠳᠡᠪᠲᠡᠷ Lock screen when screensaver boot ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠮᠠᠭᠠᠯᠠᠯᠳᠠ ᠵᠢ ᠢᠳᠡᠪᠬᠢᠵᠢᠬᠦᠯᠬᠦ ᠦᠶ᠎ᠡ ᠳ᠋ᠤ᠌ ᠳᠡᠯᠭᠡᠴᠡ ᠵᠢ ᠣᠨᠢᠰᠤᠯᠠᠬᠤ /Screensaver/Lock screen when screensaver boot The date and time are displayed ᠴᠠᠭ ᠢ ᠢᠯᠡᠷᠡᠭᠦᠯᠦᠨ᠎ᠡ ᠃ /Screensaver/The date and time are displayed Go to App Settings ᠬᠡᠷᠡᠭᠯᠡᠭᠡᠨ ᠦ ᠪᠠᠶᠢᠷᠢᠯᠠᠭᠤᠯᠤᠯᠲᠠ ᠳᠤ ᠬᠦᠷᠦᠨ᠎ᠡ ᠃ Current position ᠣᠳᠣᠬᠠᠨ ᠤ ᠪᠠᠶᠢᠷᠢ ᠰᠠᠭᠤᠷᠢ ᠃ /Screensaver/Show rest time Prioritize the list of songs from the Music app 《 ᠳᠠᠭᠤᠤ ᠬᠥᠭ᠍ᠵᠢᠮ 》 ᠦᠨ ᠬᠡᠷᠡᠭᠯᠡᠯᠲᠡ ᠶᠢᠨ ᠳᠠᠭᠤᠤ ᠶᠢᠨ ᠵᠢᠭᠰᠠᠭᠠᠯᠲᠠ ᠶᠢᠨ ᠬᠦᠰᠦᠨᠦᠭᠲᠦ ᠶᠢ ᠡᠩ ᠲᠡᠷᠢᠭᠦᠨ ᠳᠦ ᠨᠡᠪᠲᠡᠷᠡᠭᠦᠯᠦᠨ᠎ᠡ ᠃ /Screensaver/Prioritize the list of songs from the Music app Priority playlists ᠬᠦᠰᠦᠨᠦᠭᠲᠦ ᠶᠢ ᠡᠩ ᠲᠡᠷᠢᠭᠦᠨ ᠳᠦ ᠨᠡᠪᠲᠡᠷᠡᠭᠦᠯᠦᠨ᠎ᠡ ᠃ /Screensaver/Priority playlists Select the folder ᠪᠢᠴᠢᠭ᠌ ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ ᠤᠨ ᠬᠠᠪᠤᠳᠠᠷ ᠢ ᠰᠣᠩᠭᠣᠨ᠎ᠠ ᠃ Switch mode ᠬᠡᠪ ᠵᠠᠭᠪᠤᠷ ᠢᠶᠠᠨ ᠰᠣᠯᠢᠨ᠎ᠠ ᠃ /Screensaver/Switch mode In order ᠳᠡᠰ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ ᠢᠶᠠᠷ ᠨᠢ ᠃ Switching frequency ᠲᠠᠰᠢᠷᠠᠮ ᠢᠶᠠᠷ ᠃ /Screensaver/Switching frequency Image source ᠵᠢᠷᠤᠭ ᠤ᠋ᠨ ᠢᠷᠡᠯᠳᠡ /Screensaver/Image source Add image folder ᠵᠢᠷᠤᠭ ᠳᠦᠷᠰᠦᠲᠦ ᠪᠢᠴᠢᠭ᠌ ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ ᠤᠨ ᠬᠠᠪᠤᠳᠠᠷ ᠨᠡᠮᠡ ᠃ System wallpaper gallery ᠰᠢᠰᠲ᠋ᠧᠮᠲᠦ ᠵᠢᠷᠤᠭ ᠤᠨ ᠡᠬᠢ ᠪᠠᠶᠠᠯᠢᠭ ᠤᠨ ᠬᠥᠮᠥᠷᠭᠡ ᠃ Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) ᠬᠠᠨᠠᠨ ᠵᠢᠷᠤᠭ ᠤ᠋ᠨ ᠹᠠᠢᠯ (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir ᠦᠪᠡᠷᠳᠡᠭᠡᠨ ᠳᠤᠭᠳᠠᠭᠠᠬᠤ ᠳᠡᠯᠭᠡᠴᠡᠨ ᠤ᠋ ᠬᠠᠮᠠᠭᠠᠯᠠᠯᠳᠠ ᠵᠢᠨ ᠵᠢᠮ ᠢ᠋ ᠰᠣᠩᠭᠣᠬᠤ Position: ᠪᠠᠢᠷᠢᠯᠠᠯ᠄ FileName: ᠹᠠᠢᠯ᠎ᠤᠨ ᠨᠡᠷ᠎ᠡ ᠄ FileType: ᠹᠠᠢᠯ ᠳᠦᠷᠦᠯ ᠵᠦᠢᠯ: Cancel ᠦᠬᠡᠢᠰᠬᠡᠬᠦ Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) ᠳᠠᠭᠤᠨ ᠤ ᠳᠠᠪᠲᠠᠮᠵᠢ ᠶᠢᠨ ᠪᠢᠴᠢᠭ᠌ ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ ᠃(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir ᠳᠡᠯᠬᠡᠴᠡ ᠪᠠᠲᠤᠯᠠᠬᠤ ᠳᠠᠭᠤᠤ ᠬᠥᠭ᠍ᠵᠢᠮ ᠦᠨ ᠪᠢᠴᠢᠭ᠌ ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ ᠤᠨ ᠬᠠᠪᠤᠳᠠᠷ ᠢ ᠰᠣᠩᠭᠣᠨ᠎ᠠ ᠃ The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. ᠵᠢᠷᠤᠭ ᠤ᠋ᠨ ᠹᠠᠢᠯ ᠤ᠋ᠨ ᠬᠠᠪᠳᠠᠰᠤ ᠵᠢ ᠨᠡᠮᠡᠭᠰᠡᠨ ᠲᠣᠭ᠎ᠠ ᠬᠢᠵᠠᠭᠠᠷ ᠲᠤ᠌ ᠳᠤᠯᠪᠠ᠂ ᠰᠢᠯᠵᠢᠬᠦᠯᠦᠭᠰᠡᠨ ᠤ᠋ ᠳᠠᠷᠠᠭ᠎ᠠ ᠳᠠᠬᠢᠨ ᠨᠡᠮᠡᠵᠤ ᠪᠤᠯᠤᠨ᠎ᠠ᠃ ukui-screensaver/ukccplugins_i18n_ts/ug.ts0000664000175000017500000010760015172041106017653 0ustar fengfeng PhotoCorver Right open the file location ھۆججەت ئورنىنى ئېچىش Remove the file چىقىرىپ تاشلاش Screenlock Screenlock ئېكران قۇلۇپى Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) تام قەغىزى ھۆججىتى(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Your system does not have any browser application installed or the default browser application is not set up سىستېمىڭىزدا تور كۆرگۈچ پروگراممىسى قاچىلانمىغان ياكى سۈكۈتتىكى تور كۆرگۈچ پروگراممىسى تەڭشەلمىگەن Unable to open browser application تور كۆرگۈچ پروگراممىسىنى ئېچىلمىدى select custom wallpaper file ئۆزى بەلگىلىگەن تام قەغىزى ھۆججىتىنى تاللاڭ Select تاللاش Position: :ئورنى FileName: :ھۆججەت نامى FileType: :ھۆججەت تۈرى Cancel ئەمەلدىن قالدۇرۇش Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin كۆرسىتىش ئېكرانىدىكى تام قەغىزى كىرىش كۆرۈنمە يۈزىدە /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) قۇلۇپنى قالدۇرۇپ قويۇش (بىر جۈپ يانفون يوقاپ كەتكەندە سىستېما قۇلۇپلىنىدۇ) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device بەلگىلەنگەن ئۈسكۈنە No paired phone. Please turn to 'Bluetooth' to pair. جۈپ تېلېفون يوق 'كۆكچىش' نى قوشۇۋېلىپ قوشۇڭ. Screenlock ئېكران قۇلۇپى /Screenlock/Screenlock Choose Picture رەسىم تاللاڭ /Wallpaper/Choose Picture Local Pictures يەرلىك سۈرەتلەر /Screenlock/Local Pictures Online Pictures توردىكى سۈرەتلەر /Screenlock/Online Pictures Reset To Default كۆڭۈلدىكىگە قايتا بەلگىلەش /Screenlock/Reset To Default Bluetooth كۆكچىش No bluetooth adapter detected, can not use Leave Lock. كۆكچىش ماسلاشتۇرغۇچ بايقالمىدى، Leave Lock نى ئىشلەتكىلى بولمايدۇ. Monitor Off كۆزەتكۈچنى ئۆچۈرۈش /Screenlock/Monitor Off Screensaver ئېكران قوغداش /Screenlock/Screensaver Related Settings مۇناسىۋەتلىك تەڭشەكلەر /Screenlock/Related Settings Set تەڭشەك Please select device ئۈسكۈنىنى تاللاڭ The date and time are displayed كۆرسىتىش ۋاقىت ۋاقتى /Screenlock/The date and time are displayed Screensaver Screensaver ئېكران قوغداش Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI UKUI Blank_Only 黑屏 Customize ئۆز ئۆلچەم 5min 5 10min ئون مىنۇت 15min 15 30min 30 مىنوت 1hour بىر سائەت Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1 مىنوت Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never ئەزەلدىن ScreensaverUi Screensaver ئېكران قوغداش /Screensaver/Screensaver Idle time بىكار ۋاقتى /Screensaver/Idle time Screensaver program ئېكران قوغداش پىروگراممىسى /Screensaver/Screensaver program Screensaver source ئېكران يۈزى مەنبەسى /Screensaver/Screensaver source Select تاللاش Ordinal ئەنئەنىۋى Random ئىختىيارى Select Screensaver ئېكران ئاسراشنى تاللاش Weather ھاۋارايى Music ناخشا-مۇزىكا Albun ئالبوم Go to App Settings قوللىنىشچان تەسىس قىلىش Current position نۆۋەتتىكى ئورنى /Screensaver/Show rest time Prioritize the list of songs from the Music app ئالدىن «مۇزىكا»نىڭ قوللىنىلىدىغان ناخشا تىزىملىكىنى قويۇش كېرەك /Screensaver/Prioritize the list of songs from the Music app Priority playlists ئالدىن تىزىملىكنى قويۇش /Screensaver/Priority playlists Select the folder ھۆججەت قىسقۇچ تاللاش Switch mode ئالماشتۇرۇش ھالىتى /Screensaver/Switch mode In order تەرتىپ بويىچە Switching frequency ئالماشتۇرۇش چاستوتىسى /Screensaver/Switching frequency Image source سۈرەت مەنبەسى /Screensaver/Image source Add image folder رەسىملىك ھۆججەت قىسقۇچ قوشۇڭ Random switching خالىغانچە ئالماشتۇرۇش /Screensaver/Random switching Switching time ئالماشتۇرۇش ۋاقتى /Screensaver/Switching time Text(up to 30 characters): تېكىست(ئەڭ كۆپ بولغاندا 30 ھەرپ): /Screensaver/Text(up to 30 characters): Text position تېكىست ئورنى /Screensaver/Text position Centered مەركەزلەشتۈرۈلگەن Randow(Bubble text) ئىختىيارى(كۆپۈكچە تېكىست) Show rest time ئارام ئېلىش ۋاقتىنى كۆرسىتىش /Screensaver/Show rest time Lock screen when screensaver boot ئېكراننى كۆزنەك تاقاشتا ئېكراننى قۇلۇپلاش /Screensaver/Lock screen when screensaver boot The date and time are displayed كۆرسىتىش ۋاقىت ۋاقتى /Screensaver/The date and time are displayed System wallpaper gallery سىستىما تام قەغىزى چېرتىيوژى ئامبىرى Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) تام قەغىزى ھۆججەتلىرى(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir خاس ئېكران كۆرگۈچنى تاللاش Position: :ئورنى FileName: :ھۆججەت نامى FileType: :ھۆججەت تۈرى Cancel ئەمەلدىن قالدۇرۇش Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) ئاۋازلىق ھۆججەت (*.mp3*.wav*.flac .ogg.aac*.m4a *.wma.opus.aiff.ape) select screensaver music dir ئېكرانلىق ئاۋازلىق ھۆججەت قىسقۇچنى تاللاش The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. سۈرەت ھۆججەت قىسقۇچ قوشۇلغان ساننىڭ يۇقىرى چېكىگە يەتتى، بۆلۈۋەتكەندىن كېيىن داۋاملىق قوشۇشقا بولىدۇ Screensaver Picture does not exist رەسىم مەۋجۇت ئەمەس Not connected to a network تور ئۇلانمىدى The weather update failed ھاۋارايىنىڭ يېڭىلىنىشى مەغلۇپ بولدى MPRISSaverWidget No music detected ناخشىنى تەكشۈرۈپ بايقىمىدى Unknown نامەلۇم CustomAlbumViewer No photos detected, please re-select them رەسىمنى تەكشۈرمىگەندە، قايتىدىن تاللاڭ ukui-screensaver/ukccplugins_i18n_ts/lzh.ts0000664000175000017500000004631615172041035020044 0ustar fengfeng Screenlock Screenlock Screenlock Interface Show message on lock screen Browse /Screenlock/Browse Online Picture /Screenlock/Online Picture Reset To Default /Screenlock/Reset To Default Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin /Screenlock/Show picture of screenlock on screenlogin Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 5min 10min 30min 45min 1hour 2hour 3hour select custom wallpaper file Select Position: FileName: FileType: Cancel Screensaver Screensaver Idle time /Screensaver/Idle time Lock screen when activating screensaver Enable screensaver 开启屏保 Screensaver program /Screensaver/Screensaver program idle time 等待时间 Min 分钟 Lock screen when screensaver boot screensaver 屏保 Default_ukui 默认屏保 View Text(up to 30 characters): Show rest time UKUI Blank_Only Customize 5min 10min 15min 30min 1hour Screensaver source Select Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Position: FileName: FileType: Cancel Switching time 1min Ordinal Random switching Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position Centered Randow(Bubble text) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random Never ukui-screensaver/ukccplugins_i18n_ts/zh_CN.ts0000664000175000017500000010415415172041106020242 0ustar fengfeng PhotoCorver Right open the file location 打开文件位置 Remove the file 移除 Screenlock Screenlock 锁屏 Screenlock Interface 锁屏界面 Show message on lock screen 锁屏时显示消息 Related Settings 相关设置 Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay 此时间段后锁屏 Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never 从不 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) 图片文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1分钟 5min 5分钟 10min 10分钟 30min 30分钟 45min 45分钟 1hour 1小时 2hour 2小时 3hour 3小时 Cannot access online images, please check the system's default browser settings. 无法访问线上图片,请检查系统默认浏览器设置。 Set up the browser 设置浏览器 select custom wallpaper file 选择自定义壁纸文件 Select 选择 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Monitor Off 关闭显示器 Screensaver 屏幕保护 Set 去设置 ScreenlockUi Show picture of screenlock on screenlogin 显示锁屏壁纸在登录界面 /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) 动态锁 (系统将在已配对的蓝牙手机离开时自动锁定屏幕) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device 指定设备 No paired phone. Please turn to 'Bluetooth' to pair. 无配对手机,请转到 "蓝牙" 以连接手机。 Screenlock 锁屏 /Screenlock/Screenlock Choose Picture 选择图片 /Wallpaper/Choose Picture Local Pictures 本地图片 /Screenlock/Local Pictures Online Pictures 线上图片 /Screenlock/Online Pictures Reset To Default 恢复默认 /Screenlock/Reset To Default Bluetooth 蓝牙设置 No bluetooth adapter detected, can not use Leave Lock. 未检测到蓝牙适配器,无法使用动态锁功能。 Monitor Off 关闭显示器 /Screenlock/Monitor Off Screensaver 屏幕保护 /Screenlock/Screensaver Related Settings 相关设置 /Screenlock/Related Settings Set 去设置 Please select device 请选择指定设备 The date and time are displayed 显示时间日期 /Screenlock/The date and time are displayed Screensaver Screensaver 屏保 Idle time 此时间段后开启屏保 Lock screen when activating screensaver 激活屏保时锁住屏幕 Enable screensaver 开启屏保 Screensaver program 屏幕保护程序 idle time 等待时间 Min 分钟 Lock screen when screensaver boot 激活屏保时锁定屏幕 screensaver 屏保 Default_ukui 默认屏保 View 预览 Text(up to 30 characters): 屏保文本(最多30个字): Show rest time 显示休息时间 UKUI UKUI Blank_Only 黑屏 Customize 自定义 5min 5 分钟 10min 10 分钟 15min 15 分钟 30min 30 分钟 1hour 1 小时 Screensaver source 屏保来源 Select 选择 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Switching time 切换频率 1min 1分钟 Ordinal 按顺序 Random switching 随机切换 Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position 文本位置 Centered 居中 Randow(Bubble text) 随机(气泡文本) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random 随机 Never 从不 ScreensaverUi Screensaver 屏保 /Screensaver/Screensaver Idle time 此时间段后开启屏保 /Screensaver/Idle time Screensaver program 屏幕保护程序 /Screensaver/Screensaver program Screensaver source 屏保来源 /Screensaver/Screensaver source Select 选择 Ordinal 按顺序 Random 随机 Select Screensaver 选择屏保 Weather 天气 Music 音乐 Albun 相册 Go to App Settings 前往应用设置 Current position 当前位置 /Screensaver/Show rest time Prioritize the list of songs from the Music app 优先播放“音乐”应用的歌曲列表 /Screensaver/Prioritize the list of songs from the Music app Priority playlists 优先播放列表 /Screensaver/Priority playlists Select the folder 选择文件夹 Switch mode 切换方式 /Screensaver/Switch mode In order 按顺序 Switching frequency 切换频率 /Screensaver/Switching frequency Image source 图片来源 /Screensaver/Image source Add image folder 添加图片文件夹 Random switching 随机切换 /Screensaver/Random switching Switching time 切换频率 /Screensaver/Switching time Text(up to 30 characters): 屏保文本(最多 30 个字): /Screensaver/Text(up to 30 characters): Text position 文本位置 /Screensaver/Text position Centered 居中 Randow(Bubble text) 随机(气泡文本) Show rest time 显示休息时间 /Screensaver/Show rest time Lock screen when screensaver boot 激活屏保时锁定屏幕 /Screensaver/Lock screen when screensaver boot The date and time are displayed 显示时间日期 /Screensaver/The date and time are displayed System wallpaper gallery 系统壁纸图库 Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) 壁纸文件(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir 选择自定义屏保路径 Position: 位置: FileName: 文件名: FileType: 文件类型: Cancel 取消 Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) 音频文件(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape) select screensaver music dir 选择屏保音频文件夹 The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more. 图片文件夹添加已达数量上限,可移除后继续添加 Screensaver Picture does not exist 图片不存在 Not connected to a network 网络未连接 The weather update failed 天气更新失败 MPRISSaverWidget No music detected 未检测到歌曲 Unknown 未知 CustomAlbumViewer No photos detected, please re-select them 未检测到照片,请重新选择 SleepTime You have rested 您已休息 ukui-screensaver/ukccplugins_i18n_ts/de.ts0000664000175000017500000006464415172041106017642 0ustar fengfeng Screenlock Screenlock Bildschirmsperre Screenlock Interface 锁屏界面 Show message on lock screen Nachricht auf dem Sperrbildschirm anzeigen Browse 浏览 Online Picture 线上图片 Local Pictures Lokale Bilder Online Pictures Online-Bilder Reset To Default Auf Standard zurücksetzen Related Settings Verwandte Einstellungen Screenlock Set 锁屏设置 Lock screen when screensaver boot 激活屏保时锁定屏幕 Lock screen delay Verzögerung des Sperrbildschirms Min 分钟 Select screenlock background 选择锁屏背景 Browser online wp 浏览线上壁纸 Browser local wp 浏览本地壁纸 Show picture of screenlock on screenlogin Bild der Bildschirmsperre beim Screenlogin anzeigen Enabel screenlock 开启锁屏 Open 浏览 screenlock 锁屏 picture 图片 Never Nie 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h 2h 2h Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) Hintergrunddateien (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp) allFiles(*.*) 所有文件(*.*) 1min 1min 5min 5min 10min 10min 30min 30 Minuten 45min 45 Minuten 1hour 1 Stunde 2hour 2 Stunden 3hour 3 Stunden Your system does not have any browser application installed or the default browser application is not set up Unable to open browser application select custom wallpaper file Wählen Sie eine benutzerdefinierte Hintergrundbilddatei aus Select Auswählen Position: Position: FileName: Dateiname: FileType: Dateityp: Cancel Abbrechen Monitor Off Monitor aus Screensaver Bildschirmschoner Set Garnitur ScreenlockUi Show picture of screenlock on screenlogin Bild der Bildschirmsperre beim Screenlogin anzeigen /Screenlock/Show picture of screenlock on screenlogin Leave lock (System will be locked when the paired phone gone) /Screenlock/Leave lock (System will be locked when the paired phone gone) Specified device No paired phone. Please turn to 'Bluetooth' to pair. Screenlock Bildschirmsperre /Screenlock/Screenlock Choose Picture /Wallpaper/Choose Picture Local Pictures Lokale Bilder /Screenlock/Local Pictures Online Pictures Online-Bilder /Screenlock/Online Pictures Reset To Default Auf Standard zurücksetzen /Screenlock/Reset To Default Bluetooth 蓝牙 No bluetooth adapter detected, can not use Leave Lock. Monitor Off Monitor aus /Screenlock/Monitor Off Screensaver Bildschirmschoner /Screenlock/Screensaver Related Settings Verwandte Einstellungen /Screenlock/Related Settings Set Garnitur Please select device Screensaver Screensaver Bildschirmschoner Idle time Stillstandszeit Lock screen when activating screensaver Sperrbildschirm beim Aktivieren des Bildschirmschoners Enable screensaver 开启屏保 Screensaver program Bildschirmschoner-Programm idle time 等待时间 Min 分钟 Lock screen when screensaver boot Sperrbildschirm beim Starten des Bildschirmschoners screensaver 屏保 Default_ukui 默认屏保 View Ansehen Text(up to 30 characters): Text (bis zu 30 Zeichen): Show rest time Ruhezeit anzeigen UKUI UKUI Blank_Only Blank_Only Customize Anpassen 5min 5min 10min 10min 15min 15 Minuten 30min 30 Minuten 1hour 1 Stunde Screensaver source Quelle des Bildschirmschoners Select Auswählen Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Hintergrundbilder (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Benutzerdefiniertes Bildschirmschoner-Verzeichnis auswählen Position: Position: FileName: Dateiname: FileType: Dateityp: Cancel Abbrechen Switching time Schaltzeit 1min 1min Ordinal Ordinale Random switching Zufällige Umschaltung Display text 显示文本 Enter text, up to 30 characters 输入文本,最多30个字符 Text position Position des Textes Centered Zentriert Randow(Bubble text) Randow(Sprechblasentext) 1m 1m 5m 5m 10m 10m 30m 30m 45m 45m 1h 1h 1.5h 1.5h 3h 3h Random Zufällig Never Nie ScreensaverUi Screensaver Bildschirmschoner /Screensaver/Screensaver Idle time Stillstandszeit /Screensaver/Idle time Screensaver program Bildschirmschoner-Programm /Screensaver/Screensaver program Screensaver source Quelle des Bildschirmschoners /Screensaver/Screensaver source Select Auswählen Ordinal Ordinale Random Zufällig Random switching Zufällige Umschaltung /Screensaver/Random switching Switching time Schaltzeit /Screensaver/Switching time Text(up to 30 characters): Text (bis zu 30 Zeichen): /Screensaver/Text(up to 30 characters): Text position Position des Textes /Screensaver/Text position Centered Zentriert Randow(Bubble text) Randow(Sprechblasentext) Show rest time Ruhezeit anzeigen /Screensaver/Show rest time Lock screen when screensaver boot /Screensaver/Lock screen when screensaver boot Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) Hintergrundbilder (*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg) select custom screensaver dir Benutzerdefiniertes Bildschirmschoner-Verzeichnis auswählen Position: Position: FileName: Dateiname: FileType: Dateityp: Cancel Abbrechen ukui-screensaver/CMakeLists.txt0000664000175000017500000000174415172041106015536 0ustar fengfengcmake_minimum_required(VERSION 3.5) project(ukui-screensaver) # 更新翻译字段 file(GLOB TS_FILES ./i18n_ts/*.ts) add_custom_command( OUTPUT ${TS_FILES} COMMAND lupdate src/ -ts ${TS_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target( i18n_ts DEPENDS ${TS_FILES} ) add_compile_options(-fPIC) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") set(CMAKE_CXX_STANDARD 11) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INSTALL_PREFIX /usr) set(Debug ON) if(Debug) set(CMAKE_BUILD_TYPE "Debug") endif() add_subdirectory(src) #add_subdirectory(tests) add_subdirectory(i18n_ts) add_subdirectory(ukccplugins_i18n_ts) add_subdirectory(data) # 让主目标依赖翻译文件,这样才会执行i18n add_dependencies(ukui-screensaver-dialog i18n ukccplugins_i18n) ukui-screensaver/examples/0000775000175000017500000000000015172041035014607 5ustar fengfengukui-screensaver/examples/LoadCustomPlugin/0000775000175000017500000000000015172041035020040 5ustar fengfengukui-screensaver/examples/LoadCustomPlugin/widget.ui0000664000175000017500000000170615172041035021666 0ustar fengfeng Widget 0 0 400 300 Widget 140 30 111 31 LoadPlugin 30 90 331 141 ukui-screensaver/examples/LoadCustomPlugin/widget.h0000664000175000017500000000200215172041035021466 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef WIDGET_H #define WIDGET_H #include namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); public slots: void slot_load_plugin(); private: Ui::Widget *ui; }; #endif // WIDGET_H ukui-screensaver/examples/LoadCustomPlugin/LoadCustomPlugin.pro0000664000175000017500000000062715172041035024020 0ustar fengfeng#------------------------------------------------- # # Project created by QtCreator 2022-07-01T18:13:09 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = LoadCustomPlugin TEMPLATE = app SOURCES += \ main.cpp \ widget.cpp HEADERS += \ widget.h FORMS += \ widget.ui ukui-screensaver/examples/LoadCustomPlugin/main.cpp0000664000175000017500000000155415172041035021475 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "widget.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); } ukui-screensaver/examples/LoadCustomPlugin/widget.cpp0000664000175000017500000000372515172041035022036 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #pragma execution_character_set("utf-8") #include "widget.h" #include "ui_widget.h" #include #include #include #include #include Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); connect(ui->load_plugin_btn,SIGNAL(clicked(bool)),this, SLOT(slot_load_plugin())); } Widget::~Widget() { delete ui; } void Widget::slot_load_plugin() { /* QString file_path = QFileDialog::getOpenFileName(NULL,"加载插件",".","dll (*.dll *.so)"); if(file_path.isEmpty()) { return; } */ QPluginLoader pluginLoader("/usr/lib/ukui-screensaver/libscreensaver-default.so"); pluginLoader.load(); QObject* plugin = pluginLoader.instance(); if (plugin) { std::unique_ptr interface_ptr = std::unique_ptr(qobject_cast(plugin)); QWidget* widget = interface_ptr->createWidget(false,this); widget->setFixedHeight(180); widget->setFixedWidth(300); QHBoxLayout* mainLayout = new QHBoxLayout(this); mainLayout->addWidget(widget); ui->widget->setLayout(mainLayout); } } ukui-screensaver/COPYING0000664000175000017500000010451515172041106014031 0ustar fengfeng 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 . ukui-screensaver/src/0000775000175000017500000000000015172041106013557 5ustar fengfengukui-screensaver/src/userinfo.h0000664000175000017500000000747615172041035015601 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef USERINFO_H #define USERINFO_H #include #include #include class UserInfo : public QObject { Q_OBJECT public: explicit UserInfo(QObject *parent = nullptr); explicit UserInfo(const UserInfo &userInfo); virtual ~UserInfo(); bool operator==(const UserInfo &userInfo) const; public: inline bool isLoggedIn() const { return m_isLoggedIn; } inline uid_t uid() const { return m_uId; } inline QString headImage() const { return m_strHeadImage; } inline QString fullName() const { return m_strFullName; } inline QString backGround() const { return m_strBackground; } inline QString lang() const { return m_strLang; } inline QString name() const { return m_strName; } inline QString greeterBackGround() const { return m_strGreeterBackground; } inline QString backGroundColor() const { return m_strBackgroundColor; } inline bool isSystemAccount() const { return m_isSystemAccount; } virtual inline QString path() const { return QString(); } public: void updateLoggedIn(const bool &isLoggedIn); void updateUid(const uid_t &id); void updateHeadImage(const QString &path); void updateFullName(const QString &fullName); void updateBackground(const QString &backGround); void updateLang(const QString &lang); void updateName(const QString &name); void updateGreeterBackground(const QString &background); void updateBackgroundColor(const QString &color); void updateSystemAccount(const bool &isSystemAccount); Q_SIGNALS: void userPropChanged(const QString &userName); protected: bool m_isLoggedIn; // 是否已登录 uid_t m_uId; // 用户 uid QString m_strHeadImage; // 用户头像 QString m_strFullName; // 用户全名 QString m_strBackground; // 用户界面背景 QString m_strLang; // 用户语言 QString m_strName; // 用户名 QString m_strGreeterBackground; // 登录界面背景 QString m_strBackgroundColor; // 桌面背景颜色 bool m_isSystemAccount; // 系统用户 }; typedef std::shared_ptr UserInfoPtr; QDebug operator<<(QDebug stream, const UserInfo &userInfo); class LocalUserInfo : public UserInfo { Q_OBJECT public: explicit LocalUserInfo(const uid_t &uId, QObject *parent = nullptr); explicit LocalUserInfo(const QString &strPath, QObject *parent = nullptr); explicit LocalUserInfo(const LocalUserInfo &localUser); virtual ~LocalUserInfo(); inline QString path() const override { return m_strPath; } private: void initData(); void initConnections(); private Q_SLOTS: void onPropertiesChanged(const QString &, const QVariantMap &, const QStringList &); private: QString m_strPath; bool m_propertiesChangedConnected = false; }; typedef std::shared_ptr LocalUserInfoPtr; QDebug operator<<(QDebug stream, const LocalUserInfo &userInfo); #endif // USERINFO_H ukui-screensaver/src/liblightdm-qt/0000775000175000017500000000000015172041106016320 5ustar fengfengukui-screensaver/src/liblightdm-qt/greeter.cpp0000664000175000017500000002172515172041106020470 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson * Copyright (C) 2010-2011 Robert Ancell * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #include #include "QLightDM/greeter.h" #include #include #include #include using namespace QLightDM; class QLightDM::GreeterPrivate { public: GreeterPrivate(Greeter *parent); LightDMGreeter *ldmGreeter; protected: Greeter* q_ptr; static void cb_showPrompt(LightDMGreeter *greeter, const gchar *text, LightDMPromptType type, gpointer data); static void cb_showMessage(LightDMGreeter *greeter, const gchar *text, LightDMMessageType type, gpointer data); static void cb_authenticationComplete(LightDMGreeter *greeter, gpointer data); static void cb_autoLoginExpired(LightDMGreeter *greeter, gpointer data); static void cb_idle(LightDMGreeter *greeter, gpointer data); static void cb_reset(LightDMGreeter *greeter, gpointer data); private: Q_DECLARE_PUBLIC(Greeter) }; GreeterPrivate::GreeterPrivate(Greeter *parent) : q_ptr(parent) { #if !defined(GLIB_VERSION_2_36) g_type_init(); #endif ldmGreeter = lightdm_greeter_new(); g_signal_connect (ldmGreeter, LIGHTDM_GREETER_SIGNAL_SHOW_PROMPT, G_CALLBACK (cb_showPrompt), this); g_signal_connect (ldmGreeter, LIGHTDM_GREETER_SIGNAL_SHOW_MESSAGE, G_CALLBACK (cb_showMessage), this); g_signal_connect (ldmGreeter, LIGHTDM_GREETER_SIGNAL_AUTHENTICATION_COMPLETE, G_CALLBACK (cb_authenticationComplete), this); g_signal_connect (ldmGreeter, LIGHTDM_GREETER_SIGNAL_AUTOLOGIN_TIMER_EXPIRED, G_CALLBACK (cb_autoLoginExpired), this); g_signal_connect (ldmGreeter, LIGHTDM_GREETER_SIGNAL_IDLE, G_CALLBACK (cb_idle), this); g_signal_connect (ldmGreeter, LIGHTDM_GREETER_SIGNAL_RESET, G_CALLBACK (cb_reset), this); } void GreeterPrivate::cb_showPrompt(LightDMGreeter *greeter, const gchar *text, LightDMPromptType type, gpointer data) { Q_UNUSED(greeter); GreeterPrivate *that = static_cast(data); QString message = QString::fromUtf8(text); Q_EMIT that->q_func()->showPrompt(message, type == LIGHTDM_PROMPT_TYPE_QUESTION ? Greeter::PromptTypeQuestion : Greeter::PromptTypeSecret); } void GreeterPrivate::cb_showMessage(LightDMGreeter *greeter, const gchar *text, LightDMMessageType type, gpointer data) { Q_UNUSED(greeter); GreeterPrivate *that = static_cast(data); QString message = QString::fromUtf8(text); Q_EMIT that->q_func()->showMessage(message, type == LIGHTDM_MESSAGE_TYPE_INFO ? Greeter::MessageTypeInfo : Greeter::MessageTypeError); } void GreeterPrivate::cb_authenticationComplete(LightDMGreeter *greeter, gpointer data) { Q_UNUSED(greeter); GreeterPrivate *that = static_cast(data); Q_EMIT that->q_func()->authenticationComplete(); } void GreeterPrivate::cb_autoLoginExpired(LightDMGreeter *greeter, gpointer data) { Q_UNUSED(greeter); GreeterPrivate *that = static_cast(data); Q_EMIT that->q_func()->autologinTimerExpired(); } void GreeterPrivate::cb_idle(LightDMGreeter *greeter, gpointer data) { Q_UNUSED(greeter); GreeterPrivate *that = static_cast(data); Q_EMIT that->q_func()->idle(); } void GreeterPrivate::cb_reset(LightDMGreeter *greeter, gpointer data) { Q_UNUSED(greeter); GreeterPrivate *that = static_cast(data); Q_EMIT that->q_func()->reset(); } Greeter::Greeter(QObject *parent) : QObject(parent), d_ptr(new GreeterPrivate(this)) { } Greeter::~Greeter() { delete d_ptr; } bool Greeter::connectToDaemonSync() { Q_D(Greeter); return lightdm_greeter_connect_to_daemon_sync(d->ldmGreeter, NULL); } bool Greeter::connectSync() { Q_D(Greeter); return lightdm_greeter_connect_to_daemon_sync(d->ldmGreeter, NULL); } void Greeter::authenticate(const QString &username) { Q_D(Greeter); lightdm_greeter_authenticate(d->ldmGreeter, username.toLocal8Bit().data(), NULL); } void Greeter::authenticateAsGuest() { Q_D(Greeter); lightdm_greeter_authenticate_as_guest(d->ldmGreeter, NULL); } void Greeter::authenticateAutologin() { Q_D(Greeter); lightdm_greeter_authenticate_autologin(d->ldmGreeter, NULL); } void Greeter::authenticateRemote(const QString &session, const QString &username) { Q_D(Greeter); lightdm_greeter_authenticate_remote(d->ldmGreeter, session.toLocal8Bit().data(), username.toLocal8Bit().data(), NULL); } void Greeter::respond(const QString &response) { Q_D(Greeter); lightdm_greeter_respond(d->ldmGreeter, response.toLocal8Bit().data(), NULL); } void Greeter::cancelAuthentication() { Q_D(Greeter); lightdm_greeter_cancel_authentication(d->ldmGreeter, NULL); } void Greeter::cancelAutologin() { Q_D(Greeter); lightdm_greeter_cancel_autologin(d->ldmGreeter); } bool Greeter::inAuthentication() const { Q_D(const Greeter); return lightdm_greeter_get_in_authentication(d->ldmGreeter); } bool Greeter::isAuthenticated() const { Q_D(const Greeter); return lightdm_greeter_get_is_authenticated(d->ldmGreeter); } QString Greeter::authenticationUser() const { Q_D(const Greeter); return QString::fromUtf8(lightdm_greeter_get_authentication_user(d->ldmGreeter)); } void Greeter::setLanguage (const QString &language) { Q_D(Greeter); lightdm_greeter_set_language(d->ldmGreeter, language.toLocal8Bit().constData(), NULL); } void Greeter::setResettable (bool resettable) { Q_D(Greeter); lightdm_greeter_set_resettable(d->ldmGreeter, resettable); } bool Greeter::startSessionSync(const QString &session) { Q_D(Greeter); return lightdm_greeter_start_session_sync(d->ldmGreeter, session.toLocal8Bit().constData(), NULL); } QString Greeter::ensureSharedDataDirSync(const QString &username) { Q_D(Greeter); return QString::fromUtf8(lightdm_greeter_ensure_shared_data_dir_sync(d->ldmGreeter, username.toLocal8Bit().constData(), NULL)); } QString Greeter::getHint(const QString &name) const { Q_D(const Greeter); return lightdm_greeter_get_hint(d->ldmGreeter, name.toLocal8Bit().constData()); } QString Greeter::defaultSessionHint() const { Q_D(const Greeter); return QString::fromUtf8(lightdm_greeter_get_default_session_hint(d->ldmGreeter)); } bool Greeter::hideUsersHint() const { Q_D(const Greeter); return lightdm_greeter_get_hide_users_hint(d->ldmGreeter); } bool Greeter::showManualLoginHint() const { Q_D(const Greeter); return lightdm_greeter_get_show_manual_login_hint(d->ldmGreeter); } bool Greeter::showRemoteLoginHint() const { Q_D(const Greeter); return lightdm_greeter_get_show_remote_login_hint(d->ldmGreeter); } bool Greeter::lockHint() const { Q_D(const Greeter); return lightdm_greeter_get_lock_hint(d->ldmGreeter); } bool Greeter::hasGuestAccountHint() const { Q_D(const Greeter); return lightdm_greeter_get_has_guest_account_hint(d->ldmGreeter); } QString Greeter::selectUserHint() const { Q_D(const Greeter); return QString::fromUtf8(lightdm_greeter_get_select_user_hint(d->ldmGreeter)); } bool Greeter::selectGuestHint() const { Q_D(const Greeter); return lightdm_greeter_get_select_guest_hint(d->ldmGreeter); } QString Greeter::autologinUserHint() const { Q_D(const Greeter); return QString::fromUtf8(lightdm_greeter_get_autologin_user_hint(d->ldmGreeter)); } QString Greeter::autologinSessionHint() const { Q_D(const Greeter); return QString::fromUtf8(lightdm_greeter_get_autologin_session_hint(d->ldmGreeter)); } bool Greeter::autologinGuestHint() const { Q_D(const Greeter); return lightdm_greeter_get_autologin_guest_hint(d->ldmGreeter); } int Greeter::autologinTimeoutHint() const { Q_D(const Greeter); return lightdm_greeter_get_autologin_timeout_hint(d->ldmGreeter); } QString Greeter::hostname() const { return QString::fromUtf8(lightdm_get_hostname()); } QString Greeter::osName() const { return QString::fromUtf8(lightdm_get_os_name()); } QString Greeter::osId() const { return QString::fromUtf8(lightdm_get_os_id()); } QString Greeter::osPrettyName() const { return QString::fromUtf8(lightdm_get_os_pretty_name()); } QString Greeter::osVersion() const { return QString::fromUtf8(lightdm_get_os_version()); } QString Greeter::osVersionId() const { return QString::fromUtf8(lightdm_get_os_version_id()); } QString Greeter::motd() const { return QString::fromUtf8(lightdm_get_motd()); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) //#include "greeter_moc6.cpp" #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include "greeter_moc5.cpp" #else #include "greeter_moc4.cpp" #endif ukui-screensaver/src/liblightdm-qt/power.cpp0000664000175000017500000000315615172041106020165 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson * Copyright (C) 2010-2011 Robert Ancell * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #include #include "QLightDM/power.h" using namespace QLightDM; class PowerInterface::PowerInterfacePrivate { public: PowerInterfacePrivate(); }; PowerInterface::PowerInterfacePrivate::PowerInterfacePrivate() { } PowerInterface::PowerInterface(QObject *parent) : QObject(parent), d(new PowerInterfacePrivate) { } PowerInterface::~PowerInterface() { delete d; } bool PowerInterface::canSuspend() { return lightdm_get_can_suspend (); } bool PowerInterface::suspend() { return lightdm_suspend (NULL); } bool PowerInterface::canHibernate() { return lightdm_get_can_hibernate (); } bool PowerInterface::hibernate() { return lightdm_hibernate (NULL); } bool PowerInterface::canShutdown() { return lightdm_get_can_shutdown (); } bool PowerInterface::shutdown() { return lightdm_shutdown (NULL); } bool PowerInterface::canRestart() { return lightdm_get_can_restart (); } bool PowerInterface::restart() { return lightdm_restart (NULL); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) //#include "power_moc6.cpp" #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include "power_moc5.cpp" #else #include "power_moc4.cpp" #endif ukui-screensaver/src/liblightdm-qt/sessionsmodel.cpp0000664000175000017500000000735315172041106021723 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson. * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #include #include "QLightDM/sessionsmodel.h" #include #include using namespace QLightDM; class SessionItem { public: QString key; QString type; QString name; QString comment; }; class SessionsModelPrivate { public: SessionsModelPrivate(SessionsModel *parent); QList items; void loadSessions(SessionsModel::SessionType sessionType); protected: SessionsModel* q_ptr; private: Q_DECLARE_PUBLIC(SessionsModel) }; SessionsModelPrivate::SessionsModelPrivate(SessionsModel *parent) : q_ptr(parent) { #if !defined(GLIB_VERSION_2_36) g_type_init(); #endif } void SessionsModelPrivate::loadSessions(SessionsModel::SessionType sessionType) { GList *ldmSessions; switch (sessionType) { case SessionsModel::RemoteSessions: ldmSessions = lightdm_get_remote_sessions(); break; case SessionsModel::LocalSessions: /* Fall through*/ default: ldmSessions = lightdm_get_sessions(); break; } for (GList* item = ldmSessions; item; item = item->next) { LightDMSession *ldmSession = static_cast(item->data); Q_ASSERT(ldmSession); SessionItem session; session.key = QString::fromUtf8(lightdm_session_get_key(ldmSession)); session.type = QString::fromUtf8(lightdm_session_get_session_type(ldmSession)); session.name = QString::fromUtf8(lightdm_session_get_name(ldmSession)); session.comment = QString::fromUtf8(lightdm_session_get_comment(ldmSession)); items.append(session); } //this happens in the constructor so we don't need beginInsertRows() etc. } //deprecated constructor for ABI compatability. SessionsModel::SessionsModel(QObject *parent) : QAbstractListModel(parent), d_ptr(new SessionsModelPrivate(this)) { Q_D(SessionsModel); d->loadSessions(SessionsModel::LocalSessions); } SessionsModel::SessionsModel(SessionsModel::SessionType sessionType, QObject *parent) : QAbstractListModel(parent), d_ptr(new SessionsModelPrivate(this)) { Q_D(SessionsModel); d->loadSessions(sessionType); } SessionsModel::~SessionsModel() { delete d_ptr; } QHash SessionsModel::roleNames() const { QHash roles; roles[KeyRole] = "key"; roles[Qt::DisplayRole] = "display"; roles[Qt::ToolTipRole] = "toolTip"; return roles; } int SessionsModel::rowCount(const QModelIndex &parent) const { Q_D(const SessionsModel); if (parent == QModelIndex()) { //if top level return d->items.size(); } else { return 0; // no child elements. } } QVariant SessionsModel::data(const QModelIndex &index, int role) const { Q_D(const SessionsModel); if (! index.isValid()) { return QVariant(); } int row = index.row(); switch (role) { case SessionsModel::KeyRole: return d->items[row].key; case SessionsModel::TypeRole: return d->items[row].type; case Qt::DisplayRole: return d->items[row].name; case Qt::ToolTipRole: return d->items[row].comment; } return QVariant(); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // #include "sessionsmodel_moc6.cpp" #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include "sessionsmodel_moc5.cpp" #else #include "sessionsmodel_moc4.cpp" #endif ukui-screensaver/src/liblightdm-qt/QLightDM/0000775000175000017500000000000015172041106017731 5ustar fengfengukui-screensaver/src/liblightdm-qt/QLightDM/SessionsModel0000664000175000017500000000004315172041106022440 0ustar fengfeng#include "QLightDM/sessionsmodel.h"ukui-screensaver/src/liblightdm-qt/QLightDM/greeter.h0000664000175000017500000000702115172041106021537 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson. * Copyright (C) 2010-2011 Robert Ancell * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #ifndef QLIGHTDM_GREETER_H #define QLIGHTDM_GREETER_H #include #include namespace QLightDM { class GreeterPrivate; class Q_DECL_EXPORT Greeter : public QObject { Q_OBJECT Q_PROPERTY(bool authenticated READ isAuthenticated ) //NOTFIY authenticationComplete Q_PROPERTY(QString authenticationUser READ authenticationUser ) Q_PROPERTY(QString defaultSession READ defaultSessionHint CONSTANT) Q_PROPERTY(QString selectUser READ selectUserHint CONSTANT) Q_PROPERTY(bool selectGuest READ selectGuestHint CONSTANT) Q_PROPERTY(QString hostname READ hostname CONSTANT) Q_PROPERTY(QString osId READ osId CONSTANT) Q_PROPERTY(QString osName READ osName CONSTANT) Q_PROPERTY(QString osPrettyName READ osPrettyName CONSTANT) Q_PROPERTY(QString osVersion READ osVersion CONSTANT) Q_PROPERTY(QString osVersionId READ osVersionId CONSTANT) Q_PROPERTY(QString motd READ motd CONSTANT) Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT) Q_PROPERTY(bool locked READ lockHint CONSTANT) Q_ENUMS(PromptType MessageType) public: enum PromptType { PromptTypeQuestion, PromptTypeSecret }; enum MessageType { MessageTypeInfo, MessageTypeError }; explicit Greeter(QObject* parent=0); virtual ~Greeter(); QString getHint(const QString &name) const; QString defaultSessionHint() const; bool hideUsersHint() const; bool showManualLoginHint() const; bool showRemoteLoginHint() const; bool lockHint () const; bool hasGuestAccountHint() const; QString selectUserHint() const; bool selectGuestHint() const; QString autologinUserHint() const; QString autologinSessionHint() const; bool autologinGuestHint() const; int autologinTimeoutHint() const; bool inAuthentication() const; bool isAuthenticated() const; QString authenticationUser() const; QString hostname() const; QString osId() const; QString osName() const; QString osPrettyName() const; QString osVersion() const; QString osVersionId() const; QString motd() const; public Q_SLOTS: bool connectToDaemonSync(); bool connectSync(); void authenticate(const QString &username=QString()); void authenticateAsGuest(); void authenticateAutologin(); void authenticateRemote(const QString &session=QString(), const QString &username=QString()); void respond(const QString &response); void cancelAuthentication(); void cancelAutologin(); void setLanguage (const QString &language); void setResettable (bool resettable); bool startSessionSync(const QString &session=QString()); QString ensureSharedDataDirSync(const QString &username); Q_SIGNALS: void showMessage(QString text, QLightDM::Greeter::MessageType type); void showPrompt(QString text, QLightDM::Greeter::PromptType type); void authenticationComplete(); void autologinTimerExpired(); void idle(); void reset(); private: GreeterPrivate *d_ptr; Q_DECLARE_PRIVATE(Greeter) }; } #endif // QLIGHTDM_GREETER_H ukui-screensaver/src/liblightdm-qt/QLightDM/usersmodel.h0000664000175000017500000000271315172041106022267 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson. * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #ifndef QLIGHTDM_USER_H #define QLIGHTDM_USER_H #include #include #include namespace QLightDM { class UsersModelPrivate; class Q_DECL_EXPORT UsersModel : public QAbstractListModel { Q_OBJECT Q_ENUMS(UserModelRoles) public: explicit UsersModel(QObject *parent = 0); ~UsersModel(); enum UserModelRoles {NameRole = Qt::UserRole, RealNameRole, LoggedInRole, BackgroundRole, SessionRole, HasMessagesRole, ImagePathRole, BackgroundPathRole, UidRole, IsLockedRole }; QHash roleNames() const; int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; protected: private: UsersModelPrivate * const d_ptr; Q_DECLARE_PRIVATE(UsersModel) }; } #endif // QLIGHTDM_USER_H ukui-screensaver/src/liblightdm-qt/QLightDM/Greeter0000664000175000017500000000003515172041106021247 0ustar fengfeng#include "QLightDM/greeter.h"ukui-screensaver/src/liblightdm-qt/QLightDM/Power0000664000175000017500000000003315172041106020744 0ustar fengfeng#include "QLightDM/power.h"ukui-screensaver/src/liblightdm-qt/QLightDM/sessionsmodel.h0000664000175000017500000000310615172041106022771 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson. * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #ifndef QLIGHTDM_SESSIONS_MODEL_H #define QLIGHTDM_SESSIONS_MODEL_H #include class SessionsModelPrivate; namespace QLightDM { class Q_DECL_EXPORT SessionsModel : public QAbstractListModel { Q_OBJECT Q_ENUMS(SessionModelRoles SessionType) public: enum SessionModelRoles { //name is exposed as Qt::DisplayRole //comment is exposed as Qt::TooltipRole KeyRole = Qt::UserRole, IdRole = KeyRole, /** Deprecated */ TypeRole }; enum SessionType { LocalSessions, RemoteSessions }; explicit SessionsModel(QObject *parent = 0); /** Deprecated. Loads local sessions*/ explicit SessionsModel(SessionsModel::SessionType, QObject *parent = 0); virtual ~SessionsModel(); QHash roleNames() const; int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; protected: SessionsModelPrivate *d_ptr; private: Q_DECLARE_PRIVATE(SessionsModel) }; } #endif // QLIGHTDM_SESSION_H ukui-screensaver/src/liblightdm-qt/QLightDM/power.h0000664000175000017500000000247115172041106021242 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson. * Copyright (C) 2010-2011 Robert Ancell * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #ifndef QLIGHTDM_POWER_H #define QLIGHTDM_POWER_H #include namespace QLightDM { class Q_DECL_EXPORT PowerInterface : public QObject { Q_OBJECT public: Q_PROPERTY(bool canSuspend READ canSuspend() CONSTANT) Q_PROPERTY(bool canHibernate READ canHibernate() CONSTANT) Q_PROPERTY(bool canShutdown READ canShutdown() CONSTANT) Q_PROPERTY(bool canRestart READ canRestart() CONSTANT) PowerInterface(QObject *parent=0); virtual ~PowerInterface(); bool canSuspend(); bool canHibernate(); bool canShutdown(); bool canRestart(); public Q_SLOTS: bool suspend(); bool hibernate(); bool shutdown(); bool restart(); private: class PowerInterfacePrivate; PowerInterfacePrivate * const d; }; } #endif // QLIGHTDM_POWER_H ukui-screensaver/src/liblightdm-qt/QLightDM/UsersModel0000664000175000017500000000004015172041106021730 0ustar fengfeng#include "QLightDM/usersmodel.h"ukui-screensaver/src/liblightdm-qt/usersmodel.cpp0000664000175000017500000002062215172041106021210 0ustar fengfeng/* * Copyright (C) 2010-2011 David Edmundson. * Author: David Edmundson * * 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 or version 3 of the License. * See http://www.gnu.org/copyleft/lgpl.html the full text of the license. */ #include #include "QLightDM/usersmodel.h" #include #include using namespace QLightDM; class UserItem { public: QString name; QString realName; QString homeDirectory; QString image; QString background; QString session; bool isLoggedIn; bool hasMessages; quint64 uid; QString displayName() const; bool isLocked; }; QString UserItem::displayName() const { if (realName.isEmpty()){ return name; } else { return realName; } } namespace QLightDM { class UsersModelPrivate { public: UsersModelPrivate(UsersModel *parent); virtual ~UsersModelPrivate(); QList users; protected: UsersModel * const q_ptr; void loadUsers(); static void cb_userAdded(LightDMUserList *user_list, LightDMUser *user, gpointer data); static void cb_userChanged(LightDMUserList *user_list, LightDMUser *user, gpointer data); static void cb_userRemoved(LightDMUserList *user_list, LightDMUser *user, gpointer data); private: Q_DECLARE_PUBLIC(UsersModel) }; } UsersModelPrivate::UsersModelPrivate(UsersModel* parent) : q_ptr(parent) { #if !defined(GLIB_VERSION_2_36) g_type_init(); #endif } UsersModelPrivate::~UsersModelPrivate() { g_signal_handlers_disconnect_by_data(lightdm_user_list_get_instance(), this); } void UsersModelPrivate::loadUsers() { Q_Q(UsersModel); int rowCount = lightdm_user_list_get_length(lightdm_user_list_get_instance()); if (rowCount == 0) { return; } else { q->beginInsertRows(QModelIndex(), 0, rowCount-1); const GList *items, *item; items = lightdm_user_list_get_users(lightdm_user_list_get_instance()); for (item = items; item; item = item->next) { LightDMUser *ldmUser = static_cast(item->data); UserItem user; user.name = QString::fromUtf8(lightdm_user_get_name(ldmUser)); user.homeDirectory = QString::fromUtf8(lightdm_user_get_home_directory(ldmUser)); user.realName = QString::fromUtf8(lightdm_user_get_real_name(ldmUser)); user.image = QString::fromUtf8(lightdm_user_get_image(ldmUser)); user.background = QString::fromUtf8(lightdm_user_get_background(ldmUser)); user.session = QString::fromUtf8(lightdm_user_get_session(ldmUser)); user.isLoggedIn = lightdm_user_get_logged_in(ldmUser); user.hasMessages = lightdm_user_get_has_messages(ldmUser); user.uid = (quint64)lightdm_user_get_uid(ldmUser); user.isLocked = lightdm_user_get_is_locked(ldmUser); users.append(user); } q->endInsertRows(); } g_signal_connect(lightdm_user_list_get_instance(), LIGHTDM_USER_LIST_SIGNAL_USER_ADDED, G_CALLBACK (cb_userAdded), this); g_signal_connect(lightdm_user_list_get_instance(), LIGHTDM_USER_LIST_SIGNAL_USER_CHANGED, G_CALLBACK (cb_userChanged), this); g_signal_connect(lightdm_user_list_get_instance(), LIGHTDM_USER_LIST_SIGNAL_USER_REMOVED, G_CALLBACK (cb_userRemoved), this); } void UsersModelPrivate::cb_userAdded(LightDMUserList *user_list, LightDMUser *ldmUser, gpointer data) { Q_UNUSED(user_list) UsersModelPrivate *that = static_cast(data); that->q_func()->beginInsertRows(QModelIndex(), that->users.size(), that->users.size()); UserItem user; user.name = QString::fromUtf8(lightdm_user_get_name(ldmUser)); user.homeDirectory = QString::fromUtf8(lightdm_user_get_home_directory(ldmUser)); user.realName = QString::fromUtf8(lightdm_user_get_real_name(ldmUser)); user.image = QString::fromUtf8(lightdm_user_get_image(ldmUser)); user.background = QString::fromUtf8(lightdm_user_get_background(ldmUser)); user.isLoggedIn = lightdm_user_get_logged_in(ldmUser); user.hasMessages = lightdm_user_get_has_messages(ldmUser); user.uid = (quint64)lightdm_user_get_uid(ldmUser); user.isLocked = lightdm_user_get_is_locked(ldmUser); that->users.append(user); that->q_func()->endInsertRows(); } void UsersModelPrivate::cb_userChanged(LightDMUserList *user_list, LightDMUser *ldmUser, gpointer data) { Q_UNUSED(user_list) UsersModelPrivate *that = static_cast(data); QString userToChange = QString::fromUtf8(lightdm_user_get_name(ldmUser)); for (int i=0;iusers.size();i++) { if (that->users[i].name == userToChange) { that->users[i].homeDirectory = QString::fromUtf8(lightdm_user_get_home_directory(ldmUser)); that->users[i].realName = QString::fromUtf8(lightdm_user_get_real_name(ldmUser)); that->users[i].image = QString::fromUtf8(lightdm_user_get_image(ldmUser)); that->users[i].background = QString::fromUtf8(lightdm_user_get_background(ldmUser)); that->users[i].isLoggedIn = lightdm_user_get_logged_in(ldmUser); that->users[i].hasMessages = lightdm_user_get_has_messages(ldmUser); that->users[i].uid = (quint64)lightdm_user_get_uid(ldmUser); that->users[i].isLocked = lightdm_user_get_is_locked(ldmUser); QModelIndex index = that->q_ptr->createIndex(i, 0); that->q_ptr->dataChanged(index, index); break; } } } void UsersModelPrivate::cb_userRemoved(LightDMUserList *user_list, LightDMUser *ldmUser, gpointer data) { Q_UNUSED(user_list) UsersModelPrivate *that = static_cast(data); QString userToRemove = QString::fromUtf8(lightdm_user_get_name(ldmUser)); for (int i=0;iusers.size();i++) { if (that->users[i].name == userToRemove) { that->q_ptr->beginRemoveRows(QModelIndex(), i, i); that->users.removeAt(i); that->q_ptr->endRemoveRows(); break; } } } UsersModel::UsersModel(QObject *parent) : QAbstractListModel(parent), d_ptr(new UsersModelPrivate(this)) { Q_D(UsersModel); d->loadUsers(); } UsersModel::~UsersModel() { delete d_ptr; } QHash UsersModel::roleNames() const { QHash roles; roles[Qt::DisplayRole] = "display"; roles[Qt::DecorationRole] = "decoration"; roles[NameRole] = "name"; roles[RealNameRole] = "realName"; roles[LoggedInRole] = "loggedIn"; roles[BackgroundRole] = "background"; roles[BackgroundPathRole] = "backgroundPath"; roles[SessionRole] = "session"; roles[HasMessagesRole] = "hasMessages"; roles[ImagePathRole] = "imagePath"; roles[UidRole] = "uid"; roles[IsLockedRole] = "isLocked"; return roles; } int UsersModel::rowCount(const QModelIndex &parent) const { Q_D(const UsersModel); if (parent == QModelIndex()) { return d->users.size(); } return 0; } QVariant UsersModel::data(const QModelIndex &index, int role) const { Q_D(const UsersModel); if (!index.isValid()) { return QVariant(); } int row = index.row(); switch (role) { case Qt::DisplayRole: return d->users[row].displayName(); case Qt::DecorationRole: return QVariant(); case UsersModel::NameRole: return d->users[row].name; case UsersModel::RealNameRole: return d->users[row].realName; case UsersModel::SessionRole: return d->users[row].session; case UsersModel::LoggedInRole: return d->users[row].isLoggedIn; case UsersModel::BackgroundRole: return QVariant(); case UsersModel::BackgroundPathRole: return d->users[row].background; case UsersModel::HasMessagesRole: return d->users[row].hasMessages; case UsersModel::ImagePathRole: return d->users[row].image; case UsersModel::UidRole: return d->users[row].uid; case UsersModel::IsLockedRole: return d->users[row].isLocked; } return QVariant(); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) //#include "usersmodel_moc6.cpp" #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include "usersmodel_moc5.cpp" #else #include "usersmodel_moc4.cpp" #endif ukui-screensaver/src/ukccplugins/0000775000175000017500000000000015172041035016107 5ustar fengfengukui-screensaver/src/ukccplugins/screensaver/0000775000175000017500000000000015172041106020426 5ustar fengfengukui-screensaver/src/ukccplugins/screensaver/photocorver.cpp0000664000175000017500000000717215172041106023513 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "photocorver.h" #include #include #include #include PhotoCorver::PhotoCorver(QWidget *parent, bool isSystemCorver, bool isScreensaver) : QLabel{parent}, m_bSystemCorver(isSystemCorver), m_bScreensaver(isScreensaver) { if (isScreensaver){ setFixedSize(201, 113); } else { setFixedSize(198, 134); } installEventFilter(this); m_maskLabel = new MaskLable(this, true); m_maskLabel->setGeometry(0, 0, this->width(), this->height()); m_maskLabel->hide(); } void PhotoCorver::setBackground(QPixmap background) { m_background = background; repaint(); } void PhotoCorver::setClickStatus(bool state) { m_bSelectStatus = state; m_maskLabel->setVisible(m_bSelectStatus); } bool PhotoCorver::eventFilter(QObject *watched, QEvent *event) { if (parentWidget() && !parentWidget()->isEnabled()) { event->ignore(); return QWidget::eventFilter(watched, event); } if (event->type() == QEvent::Enter) { m_maskLabel->show(); } else if (event->type() == QEvent::Leave) { if (!m_bSelectStatus) { m_maskLabel->hide(); } } return QWidget::eventFilter(watched, event); } void PhotoCorver::mouseReleaseEvent(QMouseEvent *event) { if (parentWidget() && !parentWidget()->isEnabled()) { event->ignore(); return; } if (event->button() == Qt::LeftButton) { m_bSelectStatus = true; m_maskLabel->show(); Q_EMIT photoCorverLeftClicked(); } QWidget::mouseReleaseEvent(event); } void PhotoCorver::contextMenuEvent(QContextMenuEvent *event) { if (parentWidget() && !parentWidget()->isEnabled()) { event->ignore(); return; } if (!m_bSelectStatus) { m_maskLabel->hide(); } if (!m_bSystemCorver) { if (!m_rightClickMenu) { m_rightClickMenu = new QMenu(this); QAction *openFileAction = new QAction(tr("Right open the file location"), m_rightClickMenu); connect(openFileAction, &QAction::triggered, this, [&]{ Q_EMIT openFileFolderClicked(); }); QAction *deleteAction = new QAction(tr("Remove the file"), m_rightClickMenu); connect(deleteAction, &QAction::triggered, this, [&]{ Q_EMIT removePathClicked(); }); m_rightClickMenu->addActions({openFileAction, deleteAction}); } m_rightClickMenu->exec(event->globalPos()); } } void PhotoCorver::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.fillRect(rect(), palette().color(QPalette::Base)); QRect bgRect = QRect(4, 4, width() - 8, height() - 8); QPainterPath path; path.addRoundedRect(bgRect, 8, 8); painter.setClipPath(path); painter.drawPixmap(bgRect, m_background, m_background.rect()); } ukui-screensaver/src/ukccplugins/screensaver/screensaver.h0000664000175000017500000000605215172041106023122 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVER_H #define SCREENSAVER_H #include #include #include #include #include "screensaverpluginso.h" #include #include "screensaverui.h" #include #include using namespace ukcc; using namespace kdk; class Screensaver : public QWidget, CommonInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.ukcc.CommonInterface") Q_INTERFACES(CommonInterface) public: Screensaver(); ~Screensaver(); QString plugini18nName() Q_DECL_OVERRIDE; int pluginTypes() Q_DECL_OVERRIDE; QWidget * pluginUi() Q_DECL_OVERRIDE; const QString name() const Q_DECL_OVERRIDE; void plugin_leave()Q_DECL_OVERRIDE; bool isShowOnHomePage() const Q_DECL_OVERRIDE; QIcon icon() const Q_DECL_OVERRIDE; bool isEnable() const Q_DECL_OVERRIDE; virtual QString translationPath() const { return QStringLiteral("/usr/share/ukui-screensaver/ukccplugins/i18n/%1.ts"); } public: void initOptions(); void initContent(); void initPreview(); void initIdleTime(); void initScreensaver(); void initCustomSource(); void initSwicthMode(); void initSwitchTime(); void initPhotoSwitchTime(); void initCustomText(); void initTextPosition(); void initShowBreakTimeUkui(); void initShowBreakTimeCustom(); void initLockscreen(); void initSubMode(); void initPosition(); void initPlaySettings(); void initMusicPath(); void initPhothPath(); void initPhothSelectPath(); void initPhotoCycleMode(); void initWeatherLocal(); void initBackground(); void initSaverDatetimeEnable(); void connectUiSignals(); void loadPictureInfo(); void convertPixmapSlot(const QString &filename, int num); private: void preInitialize(); void connectToServer(); private Q_SLOTS: void keyChangedSlot(const QString &key); void dataChanged(const QString &key); private: QString pluginName; bool mFirstLoad; int pluginType; std::unique_ptr screensaverPtr; private: ScreensaverUi *screensaverUi = nullptr; QDBusInterface *screensaverInterface = nullptr; QString toChangeKey; QList m_picInfoList; QFutureWatcher m_watcher; QStringList m_previewPictures; QStringList m_sourcePictures; }; #endif // SCREENSAVER_H ukui-screensaver/src/ukccplugins/screensaver/screensaverui.cpp0000664000175000017500000013312315172041106024013 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screensaverui.h" #include "commonfunc.h" #include #include #include #define DEFAULT_PHOTO_PATH "/usr/share/backgrounds" #define LOCAL_WALLPAPER_PATH (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/ukui-control-center/wallpaperData/")) ScreensaverUi::ScreensaverUi() { setAttribute(Qt::WA_DeleteOnClose); uiLayout = new QVBoxLayout(this); uiLayout->setSpacing(8); uiLayout->setContentsMargins(0, 0, 0, 0); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // 允许自由拉伸 uiLayout->setSizeConstraint(QLayout::SetMinAndMaxSize ); // 既允许布局根据内容自动调整,又不会阻止窗口拉伸 uiLayout->setContentsMargins(0, 0, 0, 0); // 移除边距 initUi(); sendSignals(); } ScreensaverUi::~ScreensaverUi() { } void ScreensaverUi::initUi() { initPreview(); initBasisSettings(); initThumbnailSaver(); initWeatherSettings(); initMusicSettings(); initPhotoSettings(); initPhotoSource(); initSetting(); uiLayout->addStretch(); } void ScreensaverUi::initPreview() { screensaverTitle = new KLabel(this); screensaverTitle->setContentsMargins(16, 0, 0, 0); previewFrame = new UkccFrame(this); previewLayout = new QHBoxLayout(previewFrame); previewWidget = new PreviewWidget(this); uiLayout->addWidget(screensaverTitle); uiLayout->addWidget(previewFrame); //~ contents_path /Screensaver/Screensaver screensaverTitle->setText(tr("Screensaver")); previewWidget->setFixedSize(276, 160); QVBoxLayout *leftLayout = new QVBoxLayout(); leftLayout->setContentsMargins(0, 8, 0, 0); leftLayout->addWidget(previewWidget); leftLayout->addStretch(); previewLayout->addLayout(leftLayout); QHBoxLayout *layout = new QHBoxLayout(previewWidget); layout->setContentsMargins(0, 0, 0, 0); } void ScreensaverUi::initThumbnailSaver() { m_thumbnailSaverGroup = new SettingGroup(this); m_thumbnailSaverGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); m_thumbnailFrame = new UkccFrame(this); m_thumbnailFrame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); UkccFrame *saverFrame = new UkccFrame(m_thumbnailSaverGroup, UkccFrame::Top); QHBoxLayout *topLayout = new QHBoxLayout(saverFrame); KLabel *selectSaverLabel = new KLabel(saverFrame); selectSaverLabel->setText(tr("Select Screensaver")); topLayout->addWidget(selectSaverLabel); topLayout->addStretch(); m_thumbnailLayout = new FlowLayout(m_thumbnailFrame, 8, 8, 8); QWidget *saverWidget = new QWidget(); QVBoxLayout *saverLayout = new QVBoxLayout(saverWidget); saverLayout->setContentsMargins(0, 0, 0, 0); saverLayout->setSpacing(4); m_defaulSaverWidget = new SaverThumbnailWidget(this, true); KLabel *saverLabel = new KLabel(m_thumbnailFrame); saverLabel->setFixedWidth(m_defaulSaverWidget->width()); saverLabel->setAlignment(Qt::AlignCenter); saverLabel->setText("UKUI"); saverLayout->addWidget(m_defaulSaverWidget); saverLayout->addWidget(saverLabel); m_thumbnailLayout->addWidget(saverWidget); QWidget *weatherWidget = new QWidget(); QVBoxLayout *weatherLayout = new QVBoxLayout(weatherWidget); weatherLayout->setContentsMargins(0, 0, 0, 0); weatherLayout->setSpacing(4); m_weatherThumbnailWidget = new WeatherThumbnailWidget(this, true); KLabel *weatherLabel = new KLabel(m_thumbnailFrame); weatherLabel->setFixedWidth(m_weatherThumbnailWidget->width()); weatherLabel->setAlignment(Qt::AlignCenter); weatherLabel->setText(tr("Weather")); weatherLayout->addWidget(m_weatherThumbnailWidget); weatherLayout->addWidget(weatherLabel); m_thumbnailLayout->addWidget(weatherWidget); QWidget *musicWidget = new QWidget(); QVBoxLayout *musicLayout = new QVBoxLayout(musicWidget); musicLayout->setContentsMargins(0, 0, 0, 0); musicLayout->setSpacing(4); m_musicWidget = new MusicThumbnailWidget(this, true); KLabel *musicLabel = new KLabel(m_thumbnailFrame); musicLabel->setFixedWidth(m_musicWidget->width()); musicLabel->setAlignment(Qt::AlignCenter); musicLabel->setText(tr("Music")); musicLayout->addWidget(m_musicWidget); musicLayout->addWidget(musicLabel); m_thumbnailLayout->addWidget(musicWidget); QWidget *albunWidget = new QWidget(); QVBoxLayout *albunLayout = new QVBoxLayout(albunWidget); albunLayout->setContentsMargins(0, 0, 0, 0); albunLayout->setSpacing(4); m_albunWidget = new AlbumThumbnailWidget(this, true); KLabel *albunLabel = new KLabel(m_thumbnailFrame); albunLabel->setFixedWidth(m_albunWidget->width()); albunLabel->setAlignment(Qt::AlignCenter); albunLabel->setText(tr("Albun")); albunLayout->addWidget(m_albunWidget); albunLayout->addWidget(albunLabel); m_thumbnailLayout->addWidget(albunWidget); // mainLayout->addWidget(selectSaverLabel); // mainLayout->addLayout(m_thumbnailLayout); m_thumbnailSaverGroup->addWidget(saverFrame); m_thumbnailSaverGroup->addWidget(m_thumbnailFrame); uiLayout->addWidget(m_thumbnailSaverGroup); } void ScreensaverUi::initBasisSettings() { m_basisGroup = new KBackgroundGroup(this); m_basisGroup->setFixedHeight(275); initIdleTimeFrame(); initScreenlockFrame(); initSaverDatetimeFrame(); initBreakTimeFrame(); previewLayout->addWidget(m_basisGroup); } void ScreensaverUi::initIdleTimeFrame() { QWidget *idleTimeWidget = new QWidget(m_basisGroup); QHBoxLayout *idleTimeLayout = new QHBoxLayout(idleTimeWidget); KLabel *idleTimeLabel = new KLabel(idleTimeWidget); idleTimeLayout->setContentsMargins(16, 0, 16, 0); idleTimeLayout->addWidget(idleTimeLabel); m_idleTimeBox = new QComboBox(idleTimeWidget); idleTimeLayout->addStretch(); idleTimeLayout->addWidget(m_idleTimeBox); //~ contents_path /Screensaver/Idle time idleTimeLabel->setText(tr("Idle time")); m_basisGroup->addWidget(idleTimeWidget); } void ScreensaverUi::initSaverDatetimeFrame() { QWidget *saverDatetimeWidget = new QWidget(m_basisGroup); QHBoxLayout *saverDatetimeLayout = new QHBoxLayout(saverDatetimeWidget); KLabel *saverDatetimeLabel = new KLabel(saverDatetimeWidget); saverDatetimeLayout->setContentsMargins(16, 0, 16, 0); saverDatetimeLayout->addWidget(saverDatetimeLabel); m_saverDatetimeBtn = new KSwitchButton(saverDatetimeWidget); saverDatetimeLayout->addStretch(); saverDatetimeLayout->addWidget(m_saverDatetimeBtn); //~ contents_path /Screensaver/The date and time are displayed saverDatetimeLabel->setText(tr("The date and time are displayed")); m_basisGroup->addWidget(saverDatetimeWidget); } void ScreensaverUi::initBreakTimeFrame() { QWidget *showTimeFrame = new QWidget(m_basisGroup); QHBoxLayout *showTimeLayout = new QHBoxLayout(showTimeFrame); KLabel *showTimeLabel = new KLabel(showTimeFrame); showTimeLayout->setContentsMargins(16, 0, 16, 0); showTimeLayout->addWidget(showTimeLabel); //添加休息时间显示按钮 showCustomTimeBtn = new KSwitchButton(showTimeFrame); showUkuiTimeBtn = new KSwitchButton(showTimeFrame); showTimeLayout->addStretch(); showTimeLayout->addWidget(showCustomTimeBtn); showTimeLayout->addWidget(showUkuiTimeBtn); //~ contents_path /Screensaver/Show rest time showTimeLabel->setText(tr("Show rest time")); m_basisGroup->addWidget(showTimeFrame); } void ScreensaverUi::initScreenlockFrame() { QWidget *lockFrame = new QWidget(m_basisGroup); QHBoxLayout *lockLayout = new QHBoxLayout(lockFrame); KLabel *lockLabel = new KLabel(); //~ contents_path /Screensaver/Lock screen when screensaver boot lockLabel->setText(tr("Lock screen when screensaver boot")); lockLayout->setContentsMargins(16, 0, 16, 0); lockLayout->addWidget(lockLabel); lockButton = new KSwitchButton(lockFrame); lockLayout->addStretch(); lockLayout->addWidget(lockButton); m_basisGroup->addWidget(lockFrame); } void ScreensaverUi::initWeatherSettings() { m_weatherFrame = new UkccFrame(this, UkccFrame::BorderRadiusStyle::Around, true); QHBoxLayout *weatherSettingsLayout = new QHBoxLayout(m_weatherFrame); KLabel *weatherSettingsLabel = new KLabel(); weatherSettingsLayout->setContentsMargins(16, 0, 16, 0); weatherSettingsLayout->addWidget(weatherSettingsLabel); m_weatherSettingBtn = new QPushButton(m_weatherFrame); m_weatherSettingBtn->setText(tr("Go to App Settings")); m_localLabel = new KLabel(m_weatherFrame); QPixmap localIcon = QIcon::fromTheme("mark-location-symbolic").pixmap(QSize(16, 16)); m_localLabel->setPixmap(localIcon); m_localLabel->setMinimumWidth(80); weatherSettingsLayout->addStretch(); weatherSettingsLayout->addWidget(m_localLabel); weatherSettingsLayout->addWidget(m_weatherSettingBtn); weatherSettingsLabel->setFixedWidth(220); //~ contents_path /Screensaver/Show rest time weatherSettingsLabel->setText(tr("Current position")); uiLayout->addWidget(m_weatherFrame); } void ScreensaverUi::initMusicSettings() { m_musicSaverGroup = new SettingGroup(this); UkccFrame *followSystemFrame = new UkccFrame(m_musicSaverGroup, UkccFrame::BorderRadiusStyle::Top, true); QHBoxLayout *followSystemLayout = new QHBoxLayout(followSystemFrame); followSystemLayout->setContentsMargins(16, 0, 16, 0); followSystemLayout->setSpacing(8); //~ contents_path /Screensaver/Prioritize the list of songs from the Music app m_followSystemBtn = new QRadioButton(tr("Prioritize the list of songs from the Music app"), followSystemFrame); followSystemLayout->addWidget(m_followSystemBtn); followSystemLayout->addStretch(); UkccFrame *musicSourceFrame = new UkccFrame(m_musicSaverGroup, UkccFrame::BorderRadiusStyle::Bottom, true); QHBoxLayout *sourceLayout = new QHBoxLayout(musicSourceFrame); //~ contents_path /Screensaver/Priority playlists m_customMusicBtn = new QRadioButton(tr("Priority playlists"), musicSourceFrame); m_musicPathLine = new QLineEdit(musicSourceFrame); m_musicSourceBtn = new QPushButton(musicSourceFrame); m_musicSourceBtn->setText(tr("Select the folder")); sourceLayout->setContentsMargins(16, 0, 16, 0); sourceLayout->setSpacing(8); sourceLayout->addWidget(m_customMusicBtn); sourceLayout->addWidget(m_musicPathLine); sourceLayout->addWidget(m_musicSourceBtn); m_musicPathLine->setMinimumWidth(252); m_musicPathLine->setReadOnly(true); m_musicSourceBtn->adjustSize(); m_musicSourceBtn->raise(); m_musicSaverGroup->addWidget(followSystemFrame); m_musicSaverGroup->addWidget(musicSourceFrame); uiLayout->addWidget(m_musicSaverGroup); } void ScreensaverUi::initPhotoSettings() { m_PhotoSettingGroup = new SettingGroup(this); //~ contents_path /Screensaver/Switch mode m_photoSwitchWidget = new RadioButtonWidget(tr("Switch mode"), m_PhotoSettingGroup, UkccFrame::Top); m_orderBtn = new QRadioButton(tr("In order"), m_photoSwitchWidget); m_randomBtn = new QRadioButton(tr("Random"), m_photoSwitchWidget); m_photoSwitchWidget->addButton(m_orderBtn, 0); m_photoSwitchWidget->addButton(m_randomBtn, 1, true); //~ contents_path /Screensaver/Switching frequency m_photoFrequencyWidget = new ComboxWidget(tr("Switching frequency"), m_PhotoSettingGroup); m_PhotoSettingGroup->addWidget(m_photoSwitchWidget); m_PhotoSettingGroup->addWidget(m_photoFrequencyWidget); uiLayout->addWidget(m_PhotoSettingGroup); } void ScreensaverUi::initPhotoSource() { m_photoSourceGroup = new SettingGroup(this); UkccFrame *imageSourceFrame = new UkccFrame(m_photoSourceGroup, UkccFrame::Top, false); imageSourceFrame->setMinimumHeight(50); QHBoxLayout *topLayout = new QHBoxLayout(imageSourceFrame); topLayout->setContentsMargins(16, 0, 16, 0); KLabel *sourceLabel = new KLabel(m_photoSourceFrame); //~ contents_path /Screensaver/Image source sourceLabel->setText(tr("Image source")); m_photoSourceBtn = new QPushButton(tr("Add image folder"), m_photoSourceFrame); m_photoSourceBtn->setIcon(QIcon::fromTheme("list-add-symbolic")); topLayout->addWidget(sourceLabel); topLayout->addStretch(); topLayout->addWidget(m_photoSourceBtn); m_photoSourceFrame = new UkccFrame(m_photoSourceGroup, UkccFrame::Bottom, false); m_photoSourceLayout = new FlowLayout(m_photoSourceFrame, -1, 8); m_photoSourceGroup->addWidget(imageSourceFrame); m_photoSourceGroup->addWidget(m_photoSourceFrame); uiLayout->addWidget(m_photoSourceGroup); } void ScreensaverUi::initSetting() { screensaverGroup = new SettingGroup(this); //~ contents_path /Screensaver/Screensaver program programWidget = new ComboxWidget(tr("Screensaver program"), screensaverGroup); customizeGroup = new SettingGroup(screensaverGroup); uiLayout->addWidget(screensaverGroup); screensaverGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); screensaverGroup->addWidget(programWidget); screensaverGroup->addWidget(customizeGroup); initSourceFrame(); initSwitchModeFrame(); initSwitchTimeFrame(); initCustomTextFrame(); initTextPositionFrame(); } void ScreensaverUi::initSourceFrame() { UkccFrame *screensaverSourceFrame = new UkccFrame(customizeGroup, UkccFrame::BorderRadiusStyle::None, true); QHBoxLayout *sourceLayout = new QHBoxLayout(screensaverSourceFrame); KLabel *sourceLabel = new KLabel(); sourcePathLine = new QLineEdit(); sourceButton = new QPushButton(); sourceLayout->setContentsMargins(16, 0, 16, 0); sourceLayout->addWidget(sourceLabel); sourceLayout->addWidget(sourcePathLine); sourceLayout->addWidget(sourceButton); //~ contents_path /Screensaver/Screensaver source sourceLabel->setText(tr("Screensaver source")); sourceLabel->setFixedWidth(220); sourcePathLine->setMinimumWidth(252); sourcePathLine->setReadOnly(true); sourceButton->adjustSize(); sourceButton->setText(tr("Select")); sourceButton->raise(); customizeGroup->addWidget(screensaverSourceFrame); } void ScreensaverUi::initSwitchModeFrame() { UkccFrame *pictureSwitchFrame = new UkccFrame(customizeGroup, UkccFrame::BorderRadiusStyle::Around, true); QHBoxLayout *randomLayout = new QHBoxLayout(pictureSwitchFrame); KLabel *randomLabel = new KLabel(); QRadioButton *radioOrder = new QRadioButton(); QRadioButton *radioRand = new QRadioButton(); switchModeGroup = new QButtonGroup(); switchModeGroup->addButton(radioOrder, 0); switchModeGroup->addButton(radioRand, 1); radioOrder->setFixedWidth(220); radioRand->adjustSize(); radioOrder->setText(tr("Ordinal")); radioRand->setText(tr("Random")); randomLayout->setContentsMargins(16, 0, 16, 0); randomLayout->addWidget(randomLabel); randomLayout->addWidget(radioOrder); randomLayout->addWidget(radioRand); //~ contents_path /Screensaver/Random switching randomLabel->setText(tr("Random switching")); randomLabel->setFixedWidth(220); randomLayout->addStretch(); customizeGroup->addWidget(pictureSwitchFrame); } void ScreensaverUi::initSwitchTimeFrame() { UkccFrame *timeSetFrame = new UkccFrame(customizeGroup, UkccFrame::BorderRadiusStyle::Around, true); QHBoxLayout *timeSetLayout = new QHBoxLayout(); KLabel *timeSetLabel = new KLabel(); switchTimeComboBox = new QComboBox(); timeSetFrame->setLayout(timeSetLayout); timeSetLayout->setContentsMargins(16, 0, 16, 0); timeSetLayout->addWidget(timeSetLabel); timeSetLayout->addWidget(switchTimeComboBox); //~ contents_path /Screensaver/Switching time timeSetLabel->setText(tr("Switching time")); timeSetLabel->setFixedWidth(220); switchTimeComboBox->setMinimumWidth(340); customizeGroup->addWidget(timeSetFrame); } void ScreensaverUi::initCustomTextFrame() { UkccFrame *showTextFrame = new UkccFrame(customizeGroup, UkccFrame::BorderRadiusStyle::Around, true); QHBoxLayout *showTextLayout = new QHBoxLayout(); KLabel *showLabel = new KLabel(); textLineEdit = new QLineEdit(); //用户输入文字 showTextFrame->setLayout(showTextLayout); showTextLayout->setContentsMargins(16, 6, 15, 8); showTextLayout->addWidget(showLabel); showTextLayout->addWidget(textLineEdit); //~ contents_path /Screensaver/Text(up to 30 characters): showLabel->setText(tr("Text(up to 30 characters):")); showLabel->setFixedWidth(220); textLineEdit->setMaxLength(30); customizeGroup->addWidget(showTextFrame); } void ScreensaverUi::initTextPositionFrame() { UkccFrame *showTextSetFrame = new UkccFrame(customizeGroup, UkccFrame::BorderRadiusStyle::Around, true); QHBoxLayout *textSetLayout = new QHBoxLayout(showTextSetFrame); KLabel *textSetLabel = new KLabel(showTextSetFrame); QRadioButton *radioRandom = new QRadioButton(this); QRadioButton *radioCenter = new QRadioButton(this); textPositionGroup = new QButtonGroup(); textPositionGroup->addButton(radioRandom, 0); textPositionGroup->addButton(radioCenter, 1); showTextSetFrame->setFixedHeight(60); textSetLayout->addWidget(textSetLabel); textSetLayout->setContentsMargins(16,0,16,0); //~ contents_path /Screensaver/Text position textSetLabel->setText(tr("Text position")); textSetLabel->setFixedWidth(220); radioRandom->setFixedWidth(220); radioCenter->adjustSize(); textSetLayout->addWidget(radioRandom); textSetLayout->addWidget(radioCenter); textSetLayout->addStretch(); radioCenter->setText(tr("Centered")); radioRandom->setText(tr("Randow(Bubble text)")); customizeGroup->addWidget(showTextSetFrame); } void ScreensaverUi::setIdleTimeOptions(const QStringList &textList, const QList &dataList) { if (textList.size() == dataList.size()) { m_idleTimeBox->clear(); for (int i = 0; i < textList.size(); ++i) { m_idleTimeBox->addItem(textList.at(i), dataList.at(i)); } } } void ScreensaverUi::setScreensaverOptions(const QStringList &textList, const QStringList &dataList) { if (textList.size() == dataList.size()) { programWidget->comboBox()->clear(); for (int i = 0; i < textList.size(); ++i) { programWidget->comboBox()->addItem(textList.at(i), dataList.at(i)); } } } void ScreensaverUi::setSwitchTimeOptions(const QStringList &textList, const QList &dataList) { if (textList.size() == dataList.size()) { switchTimeComboBox->clear(); for (int i = 0; i < textList.size(); ++i) { switchTimeComboBox->addItem(textList.at(i), dataList.at(i)); } } } void ScreensaverUi::setPhotoSwitchTimeOptions(const QStringList &textList, const QList &dataList) { if (textList.size() == dataList.size()) { m_photoFrequencyWidget->comboBox()->clear(); for (int i = 0; i < textList.size(); ++i) { m_photoFrequencyWidget->comboBox()->addItem(textList.at(i), dataList.at(i)); } } } void ScreensaverUi::setIdleTime(int time) { for (int i = 0; i < m_idleTimeBox->count(); ++i) { if (time == m_idleTimeBox->itemData(i)) { m_idleTimeBox->blockSignals(true); m_idleTimeBox->setCurrentIndex(i); m_idleTimeBox->blockSignals(false); } } } void ScreensaverUi::setScreensaver(QString type) { for (int i = 0; i < programWidget->comboBox()->count(); ++i) { if (type == programWidget->comboBox()->itemData(i)) { programWidget->comboBox()->blockSignals(true); programWidget->comboBox()->setCurrentIndex(i); programWidget->comboBox()->blockSignals(false); } } if (type == "ukui") { customizeGroup->setVisible(false); showUkuiTimeBtn->setVisible(true); showCustomTimeBtn->setVisible(false); m_bCustomSaver = false; } else if (type == "custom") { customizeGroup->setVisible(true); showUkuiTimeBtn->setVisible(false); showCustomTimeBtn->setVisible(true); m_bCustomSaver = true; } } void ScreensaverUi::updatePreview(QWidget *w) { for (QObject *o : previewWidget->children()) { if (o->objectName() == "screensaverWidget") { delete o; o = nullptr; } } w->setObjectName("screensaverWidget"); previewWidget->layout()->addWidget(w); previewWidget->update(); } void ScreensaverUi::closePreview() { if (!previewWidget) { return; } for (QObject *o : previewWidget->children()) { if (o->objectName() == "screensaverWidget") { delete o; o = nullptr; } } previewWidget->update(); } void ScreensaverUi::setSourcePath(const QString &text) { if (sourcePathLine == nullptr) { return; } QFontMetrics fontMetrics(sourcePathLine->font()); int fontSize = fontMetrics.horizontalAdvance(text); if (fontSize > sourcePathLine->width()) { sourcePathLine->setText(fontMetrics.elidedText(text, Qt::ElideRight, sourcePathLine->width())); sourcePathLine->setToolTip(text); } else { sourcePathLine->setText(text); sourcePathLine->setToolTip(""); } } void ScreensaverUi::setSwitchRandom(const bool &b) { switchModeGroup->blockSignals(true); if (b) { switchModeGroup->button(1)->setChecked(true); } else { switchModeGroup->button(0)->setChecked(true); } switchModeGroup->blockSignals(false); } void ScreensaverUi::setSwitchTime(const int &time) { for (int i = 0; i < switchTimeComboBox->count(); ++i) { if (time == switchTimeComboBox->itemData(i)) { switchTimeComboBox->blockSignals(true); switchTimeComboBox->setCurrentIndex(i); switchTimeComboBox->blockSignals(false); } } } void ScreensaverUi::setPhotoSwitchTime(const int &time) { for (int i = 0; i < m_photoFrequencyWidget->comboBox()->count(); ++i) { if (time == m_photoFrequencyWidget->comboBox()->itemData(i)) { m_photoFrequencyWidget->comboBox()->blockSignals(true); m_photoFrequencyWidget->comboBox()->setCurrentIndex(i); m_photoFrequencyWidget->comboBox()->blockSignals(false); } } } void ScreensaverUi::setCustomText(const QString &text) { textLineEdit->blockSignals(true); textLineEdit->setText(text); textLineEdit->blockSignals(false); } void ScreensaverUi::setTextCentered(const bool &b) { textPositionGroup->blockSignals(true); if (b) { textPositionGroup->button(1)->setChecked(true); } else { textPositionGroup->button(0)->setChecked(true); } textPositionGroup->blockSignals(false); } void ScreensaverUi::setShowUkuiTime(const bool &b) { showUkuiTimeBtn->blockSignals(true); showUkuiTimeBtn->setChecked(b); showUkuiTimeBtn->blockSignals(false); } void ScreensaverUi::setShowCustomTime(const bool &b) { showCustomTimeBtn->blockSignals(true); showCustomTimeBtn->setChecked(b); showCustomTimeBtn->blockSignals(false); } void ScreensaverUi::setLockscreen(const bool &b) { lockButton->blockSignals(true); lockButton->setChecked(b); lockButton->blockSignals(false); } void ScreensaverUi::setThumbnailBackground(QString background) { QString thumbnailPath = mapToThumbnailPath(background); m_defaulSaverWidget->setBackground(loadFromFile(thumbnailPath)); QPixmap scaleBackground = scaleBlurPixmap(m_defaulSaverWidget->width(), m_defaulSaverWidget->height(), thumbnailPath); m_weatherThumbnailWidget->setBackground(scaleBackground); m_musicWidget->setBackground(scaleBackground); m_albunWidget->setBackground(scaleBackground); m_albunWidget->setDefaultPhoto(loadFromFile(thumbnailPath)); } void ScreensaverUi::setSubMode(int mode) { if (mode == 3) { // 相册模式 m_photoSourceGroup->setMinimumHeight(200); // 设置一个合理的最小高度 } else { m_photoSourceGroup->setMinimumHeight(0); // 其他模式恢复自动高度 } switch (mode) { case 0: m_defaulSaverWidget->setSelectStatus(true); m_weatherThumbnailWidget->setSelectStatus(false); m_musicWidget->setSelectStatus(false); m_albunWidget->setSelectStatus(false); if (m_bCustomSaver) { customizeGroup->setVisible(true); } programWidget->setVisible(true); m_weatherFrame->setVisible(false); m_musicSaverGroup->setVisible(false); m_PhotoSettingGroup->setVisible(false); m_photoSourceGroup->setVisible(false); break; case 1: m_defaulSaverWidget->setSelectStatus(false); m_weatherThumbnailWidget->setSelectStatus(true); m_musicWidget->setSelectStatus(false); m_albunWidget->setSelectStatus(false); programWidget->setVisible(false); customizeGroup->setVisible(false); m_weatherFrame->setVisible(true); m_musicSaverGroup->setVisible(false); m_PhotoSettingGroup->setVisible(false); m_photoSourceGroup->setVisible(false); break; case 2: m_defaulSaverWidget->setSelectStatus(false); m_weatherThumbnailWidget->setSelectStatus(false); m_musicWidget->setSelectStatus(true); m_albunWidget->setSelectStatus(false); programWidget->setVisible(false); customizeGroup->setVisible(false); m_weatherFrame->setVisible(false); m_musicSaverGroup->setVisible(true); m_PhotoSettingGroup->setVisible(false); m_photoSourceGroup->setVisible(false); break; case 3: m_defaulSaverWidget->setSelectStatus(false); m_weatherThumbnailWidget->setSelectStatus(false); m_musicWidget->setSelectStatus(false); m_albunWidget->setSelectStatus(true); programWidget->setVisible(false); customizeGroup->setVisible(false); m_weatherFrame->setVisible(false); m_musicSaverGroup->setVisible(false); m_PhotoSettingGroup->setVisible(true); m_photoSourceGroup->setVisible(true); break; default: break; } } void ScreensaverUi::setCurrentPosition(QString position) { m_localLabel->setText(position); m_localLabel->adjustSize(); } void ScreensaverUi::setMusicFollowSystem(const bool &b) { m_followSystemBtn->setChecked(b); m_customMusicBtn->setChecked(!b); } void ScreensaverUi::setMusicPath(QString path) { if (m_musicPathLine == nullptr) { return; } QFontMetrics fontMetrics(m_musicPathLine->font()); int fontSize = fontMetrics.horizontalAdvance(path); if (fontSize > m_musicPathLine->width()) { m_musicPathLine->setText(fontMetrics.elidedText(path, Qt::ElideRight, m_musicPathLine->width())); m_musicPathLine->setToolTip(path); } else { m_musicPathLine->setText(path); m_musicPathLine->setToolTip(""); } } void ScreensaverUi::setPhotoCycleMode(int mode) { if (mode == 0) { m_orderBtn->setChecked(true); m_randomBtn->setChecked(false); } else { m_orderBtn->setChecked(false); m_randomBtn->setChecked(true); } } void ScreensaverUi::setSaverDatetimeEnable(const bool &b) { m_saverDatetimeBtn->blockSignals(true); m_saverDatetimeBtn->setChecked(b); m_saverDatetimeBtn->blockSignals(false); } void ScreensaverUi::setPhotoPath(QStringList paths) { QSet oldSet = QSet(m_currentPhotoSource.begin(), m_currentPhotoSource.end()); QSet newSet = QSet(paths.begin(), paths.end()); QSet deleteSet = oldSet - newSet; QSet addSet = newSet - oldSet; QStringList deleteSource(deleteSet.begin(), deleteSet.end()); QStringList addSource(addSet.begin(), addSet.end()); if (!deleteSource.isEmpty()) { for (const QString& folderPath : deleteSource) { for (int i = 0; i < m_photoSourceLayout->count(); ++i) { QLayoutItem* item = m_photoSourceLayout->itemAt(i); if (item && item->widget()) { if (item->widget() && folderPath == item->widget()->objectName()) { m_photoSourceLayout->removeItem(item); item->widget()->deleteLater(); } } } } } int bgIndex = addSource.indexOf(DEFAULT_PHOTO_PATH); if (bgIndex != -1) { addSource.move(bgIndex, 0); } qDebug() << "m_currentPhotoSource = " << paths; if (!addSource.isEmpty()) { for (const QString& folderPath : addSource) { QString imagePath =getFirstImageFileIterator(folderPath); if (!imagePath.isEmpty()) { bool bSystemCorver = (folderPath == DEFAULT_PHOTO_PATH) ? true : false; QPixmap coverPixmap; if (bSystemCorver) { QString thumbnailPath = mapToThumbnailPath(imagePath); coverPixmap = loadFromFile(thumbnailPath); } else { coverPixmap = loadFromFile(imagePath); } QWidget *photoCorverWidget = new QWidget(m_photoSourceFrame); PhotoCorver *photoCorver = new PhotoCorver(photoCorverWidget, bSystemCorver); photoCorver->setBackground(coverPixmap); photoCorverWidget->setObjectName(folderPath); photoCorver->setObjectName(folderPath); connect(photoCorver, &PhotoCorver::photoCorverLeftClicked, this, [=]() { Q_EMIT photoSelectPathChanged(folderPath); }); connect(photoCorver, &PhotoCorver::openFileFolderClicked, this, [=]() { Q_EMIT openFileFolderClicked(folderPath); }); connect(photoCorver, &PhotoCorver::removePathClicked, this, [=]() { if (!bSystemCorver) { Q_EMIT removePhotoPathClicked(folderPath); } }); QVBoxLayout *photoCorverLayout = new QVBoxLayout(photoCorverWidget); photoCorverLayout->setContentsMargins(0, 0, 0, 0); photoCorverLayout->setSpacing(4); KLabel *photoCorverLabel = new KLabel(photoCorverWidget); photoCorverLabel->setFixedWidth(photoCorver->width()); if (bSystemCorver) { photoCorverLabel->setText(tr("System wallpaper gallery")); photoCorverLabel->setAlignment(Qt::AlignCenter);//居中 } else { QDir dir(folderPath); photoCorverLabel->setText(dir.dirName()); QFontMetrics fm(photoCorverLabel->font()); if (fm.horizontalAdvance(dir.dirName()) <= photoCorverLabel->width()) { photoCorverLabel->setAlignment(Qt::AlignCenter);//居中 } } photoCorverLayout->addWidget(photoCorver); photoCorverLayout->addWidget(photoCorverLabel); m_photoSourceLayout->addWidget(photoCorverWidget); } } } m_currentPhotoSource = paths; } void ScreensaverUi::setPhotoSelectPath(QString path) { for (int i = 0; i < m_photoSourceLayout->count(); ++i) { QLayoutItem* item = m_photoSourceLayout->itemAt(i); if (item && item->widget()) { if (item->widget() && path == item->widget()->objectName()) { PhotoCorver* photoCover = item->widget()->findChild(); if (photoCover) { photoCover->setClickStatus(true); } } else { PhotoCorver* photoCover = item->widget()->findChild(); if (photoCover) { photoCover->setClickStatus(false); } } } } if (path == DEFAULT_PHOTO_PATH) { path = LOCAL_WALLPAPER_PATH; } m_albunWidget->setDefaultPath(path); } void ScreensaverUi::sendSignals() { connect(m_idleTimeBox, QOverload::of(&QComboBox::currentIndexChanged), this, [=](int index) { Q_UNUSED(index) Q_EMIT idleTimeChanged(m_idleTimeBox->currentData().toInt()); }); connect(programWidget->comboBox(), QOverload::of(&QComboBox::currentIndexChanged), this, [=](int index) { Q_UNUSED(index) QString type = programWidget->comboBox()->currentData().toString(); Q_EMIT screensaverChanged(type); if (type == "ukui") { customizeGroup->setVisible(false); showUkuiTimeBtn->setVisible(true); showCustomTimeBtn->setVisible(false); m_bCustomSaver = false; } else if (type == "custom") { customizeGroup->setVisible(true); showUkuiTimeBtn->setVisible(false); showCustomTimeBtn->setVisible(true); m_bCustomSaver = true; } }); connect(sourceButton, &QPushButton::clicked, this, [=]() { QString filter; filter = tr("Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg)"); QFileDialog fd(this); QList usb_list = fd.sidebarUrls(); int sidebarNum = 8;// 最大添加U盘数,可以自己定义 QString home_path = QDir::homePath().section("/", -1, -1); QString mnt = "/media/" + home_path + "/"; QDir mntDir(mnt); mntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList file_list = mntDir.entryInfoList(); QList mntUrlList; for (int i = 0; i < sidebarNum && i < file_list.size(); ++i) { QFileInfo fi = file_list.at(i); mntUrlList << QUrl("file://" + fi.filePath()); } QFileSystemWatcher m_fileSystemWatcher(&fd); m_fileSystemWatcher.addPath("/media/" + home_path + "/"); connect(&m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, &fd, [=, &sidebarNum, &mntUrlList, &usb_list, &fd](const QString path) { QDir m_wmntDir(path); m_wmntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList m_wfilist = m_wmntDir.entryInfoList(); mntUrlList.clear(); for (int i = 0; i < sidebarNum && i < m_wfilist.size(); ++i) { QFileInfo m_fi = m_wfilist.at(i); mntUrlList << QUrl("file://" + m_fi.filePath()); } fd.setSidebarUrls(usb_list + mntUrlList); fd.update(); }); connect(&fd, &QFileDialog::finished, &fd, [=, &usb_list, &fd]() { fd.setSidebarUrls(usb_list); }); fd.setDirectory(QString(sourcePathLine->text())); fd.setAcceptMode(QFileDialog::AcceptOpen); fd.setViewMode(QFileDialog::List); fd.setNameFilter(filter); fd.setFileMode(QFileDialog::Directory); fd.setWindowTitle(tr("select custom screensaver dir")); fd.setLabelText(QFileDialog::Accept, tr("Select")); fd.setLabelText(QFileDialog::LookIn, tr("Position: ")); fd.setLabelText(QFileDialog::FileName, tr("FileName: ")); fd.setLabelText(QFileDialog::FileType, tr("FileType: ")); fd.setLabelText(QFileDialog::Reject, tr("Cancel")); fd.setSidebarUrls(usb_list + mntUrlList); if (fd.exec() != QDialog::Accepted) { return; } QString selectedfile; selectedfile = fd.selectedFiles().first(); if(selectedfile != "") { //非点击【取消】时 setSourcePath(selectedfile); Q_EMIT customPathChanged(selectedfile); } }); connect(switchModeGroup, &QButtonGroup::idClicked, this, [=](int id) { if (id == 0) { Q_EMIT switchRandomChanged(false); } else { Q_EMIT switchRandomChanged(true); } }); connect(switchTimeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [=](int index) { Q_UNUSED(index) Q_EMIT switchTimeChanged(switchTimeComboBox->currentData().toInt()); }); connect(m_photoFrequencyWidget->comboBox(), QOverload::of(&QComboBox::currentIndexChanged), this, [=](int index) { Q_UNUSED(index) Q_EMIT photoSwitchTimeChanged(m_photoFrequencyWidget->comboBox()->currentData().toInt()); }); connect(textLineEdit, &QLineEdit::textChanged, this, [=](){ Q_EMIT customTextChanged(textLineEdit->text()); }); connect(textPositionGroup, &QButtonGroup::idClicked, this, [=](int id) { if (id == 0) { Q_EMIT textCenteredChanged(false); } else { Q_EMIT textCenteredChanged(true); } }); connect(showUkuiTimeBtn, &KSwitchButton::stateChanged, this, [=]() { Q_EMIT showUkuiTimeChanged(showUkuiTimeBtn->isChecked()); }); connect(showCustomTimeBtn, &KSwitchButton::stateChanged, this, [=]() { Q_EMIT showCustomChanged(showCustomTimeBtn->isChecked()); }); connect(lockButton, &KSwitchButton::stateChanged, this, [=]() { Q_EMIT lockscreenChanged(lockButton->isChecked()); }); connect(m_saverDatetimeBtn, &KSwitchButton::stateChanged, this, [=]() { Q_EMIT saverDatetimeEnableChanged(m_saverDatetimeBtn->isChecked()); }); connect(m_defaulSaverWidget, &SaverThumbnailWidget::defaultSaverModeClicked, this, [=]() { subModeChanged(0); setSubMode(0); }); connect(m_weatherThumbnailWidget, &WeatherThumbnailWidget::weatherModeClicked, this, [=]() { subModeChanged(1); setSubMode(1); }); connect(m_musicWidget, &MusicThumbnailWidget::musicModeClicked, this, [=]() { subModeChanged(2); setSubMode(2); }); connect(m_albunWidget, &AlbumThumbnailWidget::albumModeClicked, this, [=]() { subModeChanged(3); setSubMode(3); }); connect(m_weatherSettingBtn, &QPushButton::clicked, this, [=]() { QProcess::startDetached("kylin-weather", QStringList("showmainwindow")); }); connect(m_followSystemBtn, &QRadioButton::clicked, this, [=]() { if (m_followSystemBtn->isChecked()) { musicFollowSystemChanged(m_followSystemBtn->isChecked()); m_customMusicBtn->setChecked(!m_followSystemBtn->isChecked()); } else { m_followSystemBtn->setChecked(true); } }); connect(m_customMusicBtn, &QRadioButton::clicked, this, [=]() { if (m_customMusicBtn->isChecked()) { musicFollowSystemChanged(!m_customMusicBtn->isChecked()); m_followSystemBtn->setChecked(!m_customMusicBtn->isChecked()); } else { m_customMusicBtn->setChecked(true); } }); connect(m_orderBtn, &QRadioButton::clicked, this, [=]() { int mode = m_orderBtn->isChecked() ? 1 : 0; photoCycleModeChanged(!mode); m_randomBtn->setChecked(!m_orderBtn->isChecked()); }); connect(m_randomBtn, &QRadioButton::clicked, this, [=]() { int mode = m_randomBtn->isChecked() ? 1 : 0; photoCycleModeChanged(mode); m_orderBtn->setChecked(!m_randomBtn->isChecked()); }); connect(m_musicSourceBtn, &QPushButton::clicked, this, [=]() { QString filter; filter = tr("Music files(*.mp3 *.wav *.ogg *.flac *.aac *.m4a *.wma *.opus *.aiff .ape)"); QFileDialog fd(this); QList usb_list = fd.sidebarUrls(); int sidebarNum = 8;// 最大添加U盘数,可以自己定义 QString home_path = QDir::homePath().section("/", -1, -1); QString mnt = "/media/" + home_path + "/"; QDir mntDir(mnt); mntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList file_list = mntDir.entryInfoList(); QList mntUrlList; for (int i = 0; i < sidebarNum && i < file_list.size(); ++i) { QFileInfo fi = file_list.at(i); mntUrlList << QUrl("file://" + fi.filePath()); } QFileSystemWatcher m_fileSystemWatcher(&fd); m_fileSystemWatcher.addPath("/media/" + home_path + "/"); connect(&m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, &fd, [=, &sidebarNum, &mntUrlList, &usb_list, &fd](const QString path) { QDir m_wmntDir(path); m_wmntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList m_wfilist = m_wmntDir.entryInfoList(); mntUrlList.clear(); for (int i = 0; i < sidebarNum && i < m_wfilist.size(); ++i) { QFileInfo m_fi = m_wfilist.at(i); mntUrlList << QUrl("file://" + m_fi.filePath()); } fd.setSidebarUrls(usb_list + mntUrlList); fd.update(); }); connect(&fd, &QFileDialog::finished, &fd, [=, &usb_list, &fd]() { fd.setSidebarUrls(usb_list); }); fd.setDirectory(QString(sourcePathLine->text())); fd.setAcceptMode(QFileDialog::AcceptOpen); fd.setViewMode(QFileDialog::List); fd.setNameFilter(filter); fd.setFileMode(QFileDialog::Directory); fd.setWindowTitle(tr("select screensaver music dir")); fd.setLabelText(QFileDialog::Accept, tr("Select")); fd.setLabelText(QFileDialog::LookIn, tr("Position: ")); fd.setLabelText(QFileDialog::FileName, tr("FileName: ")); fd.setLabelText(QFileDialog::FileType, tr("FileType: ")); fd.setLabelText(QFileDialog::Reject, tr("Cancel")); fd.setSidebarUrls(usb_list + mntUrlList); if (fd.exec() != QDialog::Accepted) { return; } QString selectedfile; selectedfile = fd.selectedFiles().first(); if(selectedfile != "") { //非点击【取消】时 setMusicPath(selectedfile); Q_EMIT musicPathChanged(selectedfile); } }); connect(m_photoSourceBtn, &QPushButton::clicked, this, [=]() { bool bPhotoSourceBtnShow = m_photoSourceLayout->count() > 49 ? true : false; if (bPhotoSourceBtnShow) { QMessageBox msg(this); msg.setIcon(QMessageBox::Icon::Information); msg.setIconPixmap(QIcon::fromTheme("dialog-info").pixmap(24,24)); msg.setText(tr("The maximum limit for the number of images in the folder has been reached; you can remove some before continuing to add more.")); msg.exec(); } else { QString filter; filter = tr("Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp *.svg)"); QFileDialog fd(this); QList usb_list = fd.sidebarUrls(); int sidebarNum = 8;// 最大添加U盘数,可以自己定义 QString home_path = QDir::homePath().section("/", -1, -1); QString mnt = "/media/" + home_path + "/"; QDir mntDir(mnt); mntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList file_list = mntDir.entryInfoList(); QList mntUrlList; for (int i = 0; i < sidebarNum && i < file_list.size(); ++i) { QFileInfo fi = file_list.at(i); mntUrlList << QUrl("file://" + fi.filePath()); } QFileSystemWatcher m_fileSystemWatcher(&fd); m_fileSystemWatcher.addPath("/media/" + home_path + "/"); connect(&m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, &fd, [=, &sidebarNum, &mntUrlList, &usb_list, &fd](const QString path) { QDir m_wmntDir(path); m_wmntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList m_wfilist = m_wmntDir.entryInfoList(); mntUrlList.clear(); for (int i = 0; i < sidebarNum && i < m_wfilist.size(); ++i) { QFileInfo m_fi = m_wfilist.at(i); mntUrlList << QUrl("file://" + m_fi.filePath()); } fd.setSidebarUrls(usb_list + mntUrlList); fd.update(); }); connect(&fd, &QFileDialog::finished, &fd, [=, &usb_list, &fd]() { fd.setSidebarUrls(usb_list); }); fd.setDirectory(QString(sourcePathLine->text())); fd.setAcceptMode(QFileDialog::AcceptOpen); fd.setViewMode(QFileDialog::List); fd.setNameFilter(filter); fd.setFileMode(QFileDialog::Directory); fd.setWindowTitle(tr("select custom screensaver dir")); fd.setLabelText(QFileDialog::Accept, tr("Select")); fd.setLabelText(QFileDialog::LookIn, tr("Position: ")); fd.setLabelText(QFileDialog::FileName, tr("FileName: ")); fd.setLabelText(QFileDialog::FileType, tr("FileType: ")); fd.setLabelText(QFileDialog::Reject, tr("Cancel")); fd.setSidebarUrls(usb_list + mntUrlList); if (fd.exec() != QDialog::Accepted) { return; } QString selectedfile; selectedfile = fd.selectedFiles().first(); if(selectedfile != "") { //非点击【取消】时 Q_EMIT photoSourceChanged(selectedfile); } } }); } ukui-screensaver/src/ukccplugins/screensaver/screensaverpluginso.h0000664000175000017500000000307415172041106024704 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVERPLUGINSO_H #define SCREENSAVERPLUGINSO_H #include class ScreensaverPlugin { public: virtual ~ScreensaverPlugin() {} //插件实例的名称 virtual QString name() const = 0; //创建UI的实例 virtual QWidget* createWidget(bool isScreensaver, QPixmap *background, QWidget* parent) = 0; //获得插件的展示名称 virtual QString displayName() const = 0; }; //定义了在QT系统中该接口的全局唯一的ID //实现该SDK的插件也要定义相同的ID //接口的ID中包含了版本信息,通过该ID我们可以区别不同版本的SDK和插件 //Q_DECLARE_INTERFACE宏将类型和ID关联起来,这样QT就可以验证加载的插件是否可以转换成MyPluginInterface类型 #define interface_iid "org.ukui.screensaver.screensaver-default1.0.0" Q_DECLARE_INTERFACE(ScreensaverPlugin, interface_iid) #endif // SCREENSAVERPLUGINSO_H ukui-screensaver/src/ukccplugins/screensaver/screensaverui.h0000664000175000017500000001470415172041106023463 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVERUI_H #define SCREENSAVERUI_H #include #include #include #include #include #include #include "screensaverpluginso.h" #include "previewwidget.h" #include "photocorver.h" #include "../../screensaver/albumthumbnailwidget.h" #include "../../screensaver/saverthumbnailwidget.h" #include "../../screensaver/weatherthumbnailwidget.h" #include "../../screensaver/musicthumbnailwidget.h" #include #include #include #include #include #include #include #include #include #include using namespace kdk; class ScreensaverUi : public QWidget { Q_OBJECT public: ScreensaverUi(); ~ScreensaverUi(); void setIdleTimeOptions(const QStringList &textList, const QList &dataList); void setScreensaverOptions(const QStringList &textList, const QStringList &dataList); void setSwitchTimeOptions(const QStringList &textList, const QList &dataList); void setPhotoSwitchTimeOptions(const QStringList &textList, const QList &dataList); void updatePreview(QWidget *w); void closePreview(); void setSourcePath(const QString &text); void setSwitchRandom(const bool &b); void setSwitchTime(const int &time); void setPhotoSwitchTime(const int &time); void setCustomText(const QString &text); void setTextCentered(const bool &b); void setShowUkuiTime(const bool &b); void setShowCustomTime(const bool &b); void setLockscreen(const bool &b); void setThumbnailBackground(QString background); void setSubMode(int mode); void setCurrentPosition(QString position); void setMusicFollowSystem(const bool &b); void setMusicPath(QString path); void setPhotoCycleMode(int mode); void setPhotoPath(QStringList paths); void setPhotoSelectPath(QString path); void setSaverDatetimeEnable(const bool &b); // void setPhotoSource(); public: void setIdleTime(int time); void setScreensaver(QString type); private: void initUi(); void initThumbnailSaver(); void initPreview(); void initBasisSettings(); void initWeatherSettings(); void initMusicSettings(); void initPhotoSettings(); void initPhotoSource(); void initSetting(); void initSourceFrame(); void initSwitchModeFrame(); void initSwitchTimeFrame(); void initCustomTextFrame(); void initIdleTimeFrame(); void initTextPositionFrame(); void initBreakTimeFrame(); void initScreenlockFrame(); void initSaverDatetimeFrame(); void sendSignals(); private: QVBoxLayout *uiLayout = nullptr; bool m_bCustomSaver = false; QStringList m_currentPhotoSource; private: KLabel *screensaverTitle = nullptr; UkccFrame *previewFrame = nullptr; QHBoxLayout *previewLayout = nullptr; PreviewWidget *previewWidget = nullptr; SettingGroup *screensaverGroup = nullptr; QComboBox *m_idleTimeBox = nullptr; ComboxWidget *programWidget = nullptr; SettingGroup *customizeGroup = nullptr; KBackgroundGroup *m_basisGroup = nullptr; SettingGroup *m_thumbnailSaverGroup = nullptr; UkccFrame *m_thumbnailFrame = nullptr; FlowLayout *m_thumbnailLayout = nullptr; MusicThumbnailWidget *m_musicWidget = nullptr; AlbumThumbnailWidget *m_albunWidget = nullptr; SaverThumbnailWidget *m_defaulSaverWidget = nullptr; WeatherThumbnailWidget *m_weatherThumbnailWidget = nullptr; UkccFrame *m_weatherFrame = nullptr; SettingGroup *m_musicSaverGroup = nullptr; SettingGroup *m_PhotoSettingGroup = nullptr; RadioButtonWidget *m_photoSwitchWidget = nullptr; ComboxWidget *m_photoFrequencyWidget = nullptr; SettingGroup *m_photoSourceGroup = nullptr; UkccFrame *m_photoSourceFrame = nullptr; private: QLineEdit *sourcePathLine = nullptr; QPushButton *sourceButton = nullptr; QButtonGroup *switchModeGroup = nullptr; QComboBox *switchTimeComboBox = nullptr; QLineEdit *textLineEdit = nullptr; QButtonGroup *textPositionGroup = nullptr; KSwitchButton *showCustomTimeBtn = nullptr; KSwitchButton *showUkuiTimeBtn = nullptr; KSwitchButton *lockButton = nullptr; KSwitchButton *m_saverDatetimeBtn = nullptr; QPushButton *m_weatherSettingBtn = nullptr; KLabel *m_localLabel = nullptr; QRadioButton *m_followSystemBtn = nullptr; QRadioButton *m_customMusicBtn = nullptr; QLineEdit *m_musicPathLine = nullptr; QPushButton *m_musicSourceBtn = nullptr; QRadioButton *m_orderBtn = nullptr; QRadioButton *m_randomBtn = nullptr; QPushButton *m_photoSourceBtn = nullptr; FlowLayout *m_photoSourceLayout =nullptr; Q_SIGNALS: void idleTimeChanged(int time); void screensaverChanged(QString type); void customPathChanged(QString path); void switchRandomChanged(bool b); void switchTimeChanged(int time); void photoSwitchTimeChanged(int time); void customTextChanged(QString text); void textCenteredChanged(bool b); void showUkuiTimeChanged(bool b); void showCustomChanged(bool b); void lockscreenChanged(bool b); void saverDatetimeEnableChanged(bool b); void subModeChanged(int mode); void musicPathChanged(QString path); void musicFollowSystemChanged(bool b); void photoCycleModeChanged(int mode); void photoCycleInterval(int time); void photoSourceChanged(QString path); void photoSelectPathChanged(QString path); void removePhotoPathClicked(QString path); void openFileFolderClicked(QString path); }; #endif // SCREENSAVERUI_H ukui-screensaver/src/ukccplugins/screensaver/CMakeLists.txt0000664000175000017500000000451515172041106023173 0ustar fengfengpkg_check_modules(QGS REQUIRED gsettings-qt6) pkg_check_modules(GIO2.0 REQUIRED gio-2.0) pkg_check_modules(GIOUNIX2.0 REQUIRED gio-unix-2.0) pkg_check_modules(KYSDKQTWIDGETS REQUIRED kysdk-qtwidgets) pkg_check_modules(KYSDKDIAGNOSTICS REQUIRED kysdk-diagnostics) include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_SOURCE_DIR}/Common) include_directories( ${QGS_INCLUDE_DIRS} ${GIO2.0_INCLUDE_DIRS} ${GIOUNIX2.0_INCLUDE_DIRS} ${KYSDKQTWIDGETS_INCLUDE_DIRS} ${KYSDKDIAGNOSTICS_INCLUDE_DIRS} ${KYSDKSYSTIME_INCLUDE_DIRS} /usr/include/ukcc ) set(EXTRA_LIBS ${EXTRA_LIBS} ${QGS_LIBRARIES} ${GIO2.0_LIBRARIES} ${GIOUNIX2.0_LIBRARIES} ${KYSDKQTWIDGETS_LIBRARIES} ${KYSDKDIAGNOSTICS_LIBRARIES} ${KYSDKSYSTIME_LIBRARIES} -lukcc ) qt6_add_resources(screensaver_Plugin_SRC ) qt6_wrap_cpp(screensaver_Plugin_SRC previewwidget.h screensaver.h screensaverpluginso.h screensaverui.h photocorver.h ../../screensaver/albumthumbnailwidget.h ../../screensaver/saverthumbnailwidget.h ../../screensaver/weatherthumbnailwidget.h ../../screensaver/musicthumbnailwidget.h ../../screensaver/customalbumviewer.h ../../screensaver/masklable.h ../../screensaver/timethumbnailwidget.h ../../screensaver/weatherwidget.h ../../screensaver/adaptivelabel.h ../../common/commonfunc.h ../../widgets/customlabel.h ) set(screensaver_Plugin_SRC ${screensaver_Plugin_SRC} previewwidget.cpp screensaver.cpp screensaverui.cpp photocorver.cpp ../../screensaver/albumthumbnailwidget.cpp ../../screensaver/saverthumbnailwidget.cpp ../../screensaver/weatherthumbnailwidget.cpp ../../screensaver/musicthumbnailwidget.cpp ../../screensaver/customalbumviewer.cpp ../../screensaver/masklable.cpp ../../screensaver/timethumbnailwidget.cpp ../../screensaver/weatherwidget.cpp ../../screensaver/adaptivelabel.cpp ../../common/commonfunc.cpp ../../widgets/customlabel.cpp ) add_library(screensaver SHARED ${screensaver_Plugin_SRC}) target_link_libraries(screensaver Qt6::Widgets Qt6::DBus Qt6::Svg ${EXTRA_LIBS}) install(TARGETS screensaver DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE}/ukui-control-center) ukui-screensaver/src/ukccplugins/screensaver/screensaver.cpp0000664000175000017500000005215115172041106023456 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screensaver.h" #include #include #include #include #include #define PREENTRY_TIME "preentryTime" #define SCREENSAVER_TYPE "screensaverType" #define SHOWTIME_UKUI "showBreakTimeUkui" #define SHOWTIME_CUSTOM "showBreakTimeCustom" #define SCREENLOCK_ENABLED "screenLockEnabled" #define CUSTOM_PATH "customPath" #define SWITCH_RANDOW "switchRandom" #define CYCLE_TIME "cycleTime" #define CUSTOM_TEXT "customText" #define CUSTOM_TEXT_CENTERED "customTextCentered" #define SUB_MODE "subMode" #define FOLLOW_SYSTEM "followSystem" #define MUSIC_PATH "musicPath" #define PHOTO_PATH "photoPath" #define PHOTO_SELECT_PATH "photoSelectPath" #define PHOTO_REMOVE_PATH "photoRemovePath" #define PHOTO_CYCLE_MODE "photoCycleMode" #define PHOTO_CYCLE_INTERVAL "photoCycleInterval" #define WEATHER_DATA "weatherData" #define BACKGROUND "background" #define DEFAULT_PHOTO_PATH "/usr/share/backgrounds" #define LOCAL_WALLPAPER_PATH (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/ukui-control-center/wallpaperData/")) #define SAVER_DATETIME_ENADLE "saverDatetimeEnable" Screensaver::Screensaver() : mFirstLoad(true) { QTranslator *translator = new QTranslator(this); QLocale locale; translator->load("/usr/share/ukui-screensaver/ukccplugins/i18n_qm/" + locale.name() + ".qm"); QApplication::installTranslator(translator); pluginName = tr("Screensaver"); pluginType = PERSONALIZED; // QtConcurrent::run([=] { preInitialize(); }); screensaverInterface = new QDBusInterface( "org.ukui.ScreenSaver", "/Screensaver", "org.ukui.ScreenSaver.session.Screensaver", QDBusConnection::sessionBus(), this); if (!screensaverInterface->isValid()) { qCritical() << "org.ukui.ScreenSaver.session.ScreenSaver DBus error:" << screensaverInterface->lastError(); return; } QDBusMessage message = screensaverInterface->call("ping"); // 判断dbus路径是否存在 if (message.type() == QDBusMessage::ErrorMessage && message.errorMessage().contains("No such object path", Qt::CaseInsensitive)) { qCritical() << screensaverInterface << ":" << message.errorMessage(); } } Screensaver::~Screensaver() { if (!mFirstLoad) { screensaverUi->deleteLater(); screensaverUi = nullptr; } } QString Screensaver::plugini18nName() { return pluginName; } int Screensaver::pluginTypes() { return pluginType; } void Screensaver::preInitialize() { QPluginLoader pluginLoader("/usr/lib/ukui-screensaver/libscreensaver-default.so"); pluginLoader.load(); QObject *plugin = pluginLoader.instance(); screensaverPtr = nullptr; if (plugin) { screensaverPtr = std::unique_ptr(qobject_cast(plugin)); } else { qWarning() << "pluginLoader '/usr/lib/ukui-screensaver/libscreensaver-default.so' failed"; } m_previewPictures = screensaverInterface->property("previewWallpapers").toStringList(); m_sourcePictures = screensaverInterface->property("sourceWallpapers").toStringList(); m_picInfoList.clear(); } QWidget *Screensaver::pluginUi() { if (mFirstLoad) { preInitialize(); mFirstLoad = false; loadPictureInfo(); screensaverUi = new ScreensaverUi; initOptions(); initContent(); connectUiSignals(); connectToServer(); } else { QTimer::singleShot(0, [&]{ initPreview(); }); } return screensaverUi; } const QString Screensaver::name() const { return QStringLiteral("Screensaver"); } void Screensaver::plugin_leave() { if (screensaverUi) { screensaverUi->closePreview(); } return; } bool Screensaver::isShowOnHomePage() const { return true; } QIcon Screensaver::icon() const { return QIcon::fromTheme("ukui-screensaver-symbolic"); } bool Screensaver::isEnable() const { return true; } void Screensaver::dataChanged(const QString &key) { if (key != toChangeKey) { if (key == PREENTRY_TIME) { initIdleTime(); } else if (key == SCREENSAVER_TYPE) { initScreensaver(); } else if (key == CUSTOM_PATH) { initCustomSource(); } else if (key == SWITCH_RANDOW) { initSwicthMode(); } else if (key == CYCLE_TIME) { initSwitchTime(); } else if (key == CUSTOM_TEXT) { initCustomText(); } else if (key == CUSTOM_TEXT_CENTERED) { initTextPosition(); } else if (key == SHOWTIME_UKUI) { initShowBreakTimeUkui(); } else if (key == SHOWTIME_CUSTOM) { initShowBreakTimeCustom(); } else if (key == SCREENLOCK_ENABLED) { initLockscreen(); } else if (key == BACKGROUND) { initBackground(); } else if (key == WEATHER_DATA) { initWeatherLocal(); } else if (key == SUB_MODE) { initSubMode(); } else if (key == FOLLOW_SYSTEM) { initPlaySettings(); } else if (key == MUSIC_PATH) { initMusicPath(); } else if (key == PHOTO_PATH) { initPhothPath(); } else if (key == PHOTO_SELECT_PATH) { initPhothSelectPath(); } else if (key == PHOTO_CYCLE_MODE) { initPhotoCycleMode(); } else if (key == PHOTO_CYCLE_INTERVAL) { initPhotoSwitchTime(); } } toChangeKey = ""; } void Screensaver::initOptions() { QStringList textList = {}; QList dataList = {}; QStringList dataList2 = {}; textList.clear(); textList << tr("5min") << tr("10min") << tr("15min") << tr("30min") << tr("1hour") << tr("Never"); dataList.clear(); dataList << 5 << 10 << 15 << 30 << 60 << -1; #ifdef OPENKYLIN textList.insert(0, tr("1min")); dataList.insert(0, 1); #endif screensaverUi->setIdleTimeOptions(textList, dataList); textList.clear(); textList << tr("UKUI") << tr("Customize"); dataList2.clear(); dataList2 << "ukui" << "custom"; screensaverUi->setScreensaverOptions(textList, dataList2); textList.clear(); textList << tr("1min") << tr("5min") << tr("10min") << tr("30min"); dataList.clear(); dataList << 60 << 300 << 600 << 1800; screensaverUi->setSwitchTimeOptions(textList, dataList); textList.clear(); textList << tr("1min") << tr("5min") << tr("10min") << tr("30min"); dataList.clear(); dataList << 60 << 300 << 600 << 1800; screensaverUi->setPhotoSwitchTimeOptions(textList, dataList); } void Screensaver::initContent() { initIdleTime(); initScreensaver(); initCustomSource(); initSwicthMode(); initSwitchTime(); initPhotoSwitchTime(); initCustomText(); initTextPosition(); initShowBreakTimeUkui(); initShowBreakTimeCustom(); initLockscreen(); initSubMode(); initPlaySettings(); initMusicPath(); initPhothPath(); initPhotoCycleMode(); initWeatherLocal(); initBackground(); initPhothSelectPath(); initSaverDatetimeEnable(); } void Screensaver::convertPixmapSlot(const QString &filename, int num) { QPixmap qPixmap = QPixmap(filename); if (qPixmap.isNull()){ QFile file(filename); qint64 fileSize = file.size(); if (fileSize <= 100000000 && file.open(QIODevice::ReadOnly)){ qPixmap.loadFromData(file.readAll()); file.close(); } } QSize IMAGE_SIZE(166, 110); QPixmap pixmap = qPixmap.scaled(IMAGE_SIZE); QString tmpFile = filename; tmpFile.replace("/", "-"); QFile wallpaperFile; wallpaperFile.setFileName(LOCAL_WALLPAPER_PATH + QString::number(num) + tmpFile); QFileInfo fileinfo = QFileInfo(tmpFile); pixmap.save(&wallpaperFile, fileinfo.suffix().toUtf8().data()); } void Screensaver::loadPictureInfo() { if (m_previewPictures.size() != m_sourcePictures.size()) { qWarning() << "picturePathList.size():" << m_previewPictures.size() << " != sourcePathList.size():" << m_sourcePictures.size(); return; } QDir dir(LOCAL_WALLPAPER_PATH); QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot | QDir::Dirs); qDebug() << Q_FUNC_INFO << "fileInfoList size" << fileInfoList.size() << "m_sourcePictures size" << m_sourcePictures.size(); if ((fileInfoList.size() - 1) == m_sourcePictures.size()) { qDebug() << Q_FUNC_INFO << "equal file num, skip load"; m_picInfoList.clear(); for (int i = 0; i < m_previewPictures.size(); ++i) { m_picInfoList.append(m_previewPictures.at(i)); } return; } int num = 0; for (QString filename : m_sourcePictures) { m_watcher.setFuture(QtConcurrent::run([=, filename, num]() { convertPixmapSlot(filename, num); })); num++; } } void Screensaver::initPreview() { if (screensaverPtr) { qInfo() << "screensaverPtr can be used"; QWidget *widget = screensaverPtr->createWidget(false, nullptr, screensaverUi); widget->show(); screensaverUi->updatePreview(widget); } else { qInfo() << "screensaverPtr can not be used"; } } void Screensaver::initIdleTime() { int time = screensaverInterface->property(PREENTRY_TIME).toInt(); screensaverUi->setIdleTime(time); } void Screensaver::initScreensaver() { QString type = screensaverInterface->property(SCREENSAVER_TYPE).toString(); screensaverUi->setScreensaver(type); initPreview(); } void Screensaver::initCustomSource() { QString path = screensaverInterface->property(CUSTOM_PATH).toString(); screensaverUi->setSourcePath(path); } void Screensaver::initSwicthMode() { bool random = screensaverInterface->property(SWITCH_RANDOW).toBool(); screensaverUi->setSwitchRandom(random); } void Screensaver::initSwitchTime() { int time = screensaverInterface->property(CYCLE_TIME).toInt(); screensaverUi->setSwitchTime(time); } void Screensaver::initPhotoSwitchTime() { int time = screensaverInterface->property(PHOTO_CYCLE_INTERVAL).toInt(); screensaverUi->setPhotoSwitchTime(time); } void Screensaver::initCustomText() { QString text = screensaverInterface->property(CUSTOM_TEXT).toString(); screensaverUi->setCustomText(text); } void Screensaver::initTextPosition() { bool centered = screensaverInterface->property(CUSTOM_TEXT_CENTERED).toBool(); screensaverUi->setTextCentered(centered); } void Screensaver::initShowBreakTimeUkui() { bool b = screensaverInterface->property(SHOWTIME_UKUI).toBool(); screensaverUi->setShowUkuiTime(b); } void Screensaver::initShowBreakTimeCustom() { bool b = screensaverInterface->property(SHOWTIME_CUSTOM).toBool(); screensaverUi->setShowCustomTime(b); } void Screensaver::initLockscreen() { bool b = screensaverInterface->property(SCREENLOCK_ENABLED).toBool(); screensaverUi->setLockscreen(b); } void Screensaver::initSubMode() { int b = screensaverInterface->property(SUB_MODE).toInt(); screensaverUi->setSubMode(b); } void Screensaver::initPlaySettings() { bool b = screensaverInterface->property(FOLLOW_SYSTEM).toBool(); screensaverUi->setMusicFollowSystem(b); } void Screensaver::initMusicPath() { QString path = screensaverInterface->property(MUSIC_PATH).toString(); screensaverUi->setMusicPath(path); } void Screensaver::initPhothPath() { QStringList b = screensaverInterface->property(PHOTO_PATH).toStringList(); screensaverUi->setPhotoPath(b); } void Screensaver::initPhothSelectPath() { QString b = screensaverInterface->property(PHOTO_SELECT_PATH).toString(); if (b.isEmpty()) { b = DEFAULT_PHOTO_PATH; } screensaverUi->setPhotoSelectPath(b); } void Screensaver::initPhotoCycleMode() { int b = screensaverInterface->property(PHOTO_CYCLE_MODE).toInt(); screensaverUi->setPhotoCycleMode(b); } void Screensaver::initWeatherLocal() { QString weather = screensaverInterface->property(WEATHER_DATA).toString(); QStringList weatherInfoList = weather.split(","); if (weatherInfoList.count() > 3) { screensaverUi->setCurrentPosition(weatherInfoList.at(2)); } else { qInfo() << "weather error!!!!" << weatherInfoList; } } void Screensaver::initBackground() { QString background = screensaverInterface->property(BACKGROUND).toString(); screensaverUi->setThumbnailBackground(background); } void Screensaver::initSaverDatetimeEnable() { bool saverDateTimeEnable = screensaverInterface->property(SAVER_DATETIME_ENADLE).toBool(); screensaverUi->setSaverDatetimeEnable(saverDateTimeEnable); } void Screensaver::connectUiSignals() { QDBusConnection::sessionBus().connect( "org.ukui.ScreenSaver", "/Screensaver", "org.ukui.ScreenSaver.session.Screensaver", "changed", this, SLOT(dataChanged(QString))); connect(screensaverUi, &ScreensaverUi::idleTimeChanged, this, [=](int time) { toChangeKey = PREENTRY_TIME; screensaverInterface->call("setPreentryTime", time); UkccCommon::buriedSettings(name(), QString("Idle time"), QString("select"), QString::number(time)); }); connect(screensaverUi, &ScreensaverUi::screensaverChanged, this, [=](QString type) { toChangeKey = SCREENSAVER_TYPE; screensaverInterface->call("setScreensaverType", type); UkccCommon::buriedSettings(name(), QString("Screensaver program"), QString("select"), type); }); connect(screensaverUi, &ScreensaverUi::customPathChanged, this, [=](QString path) { toChangeKey = CUSTOM_PATH; screensaverInterface->call("setCustomPath", path); UkccCommon::buriedSettings(name(), QString("Screensaver source Select"), QString("select"), path); }); connect(screensaverUi, &ScreensaverUi::switchRandomChanged, this, [=](bool b) { toChangeKey = SWITCH_RANDOW; screensaverInterface->call("setSwitchRandom", b); UkccCommon::buriedSettings(name(), QString("Random switching"), QString("settings"), b ? "Random" : "Ordinal"); }); connect(screensaverUi, &ScreensaverUi::switchTimeChanged, this, [=](int time) { toChangeKey = CYCLE_TIME; screensaverInterface->call("setCycleTime", time); UkccCommon::buriedSettings(name(), QString("Switching time"), QString("select"), QString::number(time)); }); connect(screensaverUi, &ScreensaverUi::photoSwitchTimeChanged, this, [=](int time) { toChangeKey = PHOTO_CYCLE_INTERVAL; screensaverInterface->call("setPhotoCycleInterval", time); UkccCommon::buriedSettings(name(), QString("Photo switching time"), QString("select"), QString::number(time)); }); connect(screensaverUi, &ScreensaverUi::customTextChanged, this, [=](QString text) { toChangeKey = CUSTOM_TEXT; screensaverInterface->call("setCustomText", text); UkccCommon::buriedSettings(name(), QString("Text(up to 30 characters):"), QString("settings"), text); }); connect(screensaverUi, &ScreensaverUi::textCenteredChanged, this, [=](bool b) { toChangeKey = CUSTOM_TEXT_CENTERED; screensaverInterface->call("setCustomTextCentered", b); UkccCommon::buriedSettings( name(), QString("Text position"), QString("settings"), b ? "Centered" : "Randow(Bubble text)"); }); connect(screensaverUi, &ScreensaverUi::showUkuiTimeChanged, this, [=](bool b) { toChangeKey = SHOWTIME_UKUI; screensaverInterface->call("setShowBreakTimeUkui", b); UkccCommon::buriedSettings( name(), QString("custom Show rest time"), QString("settings"), UkccCommon::boolToString(b)); }); connect(screensaverUi, &ScreensaverUi::showCustomChanged, this, [=](bool b) { toChangeKey = SHOWTIME_CUSTOM; screensaverInterface->call("setShowBreakTimeCustom", b); UkccCommon::buriedSettings( name(), QString("ukui Show rest time"), QString("settings"), UkccCommon::boolToString(b)); }); connect(screensaverUi, &ScreensaverUi::lockscreenChanged, this, [=](bool b) { toChangeKey = SCREENLOCK_ENABLED; screensaverInterface->call("setScreenLockEnabled", b); UkccCommon::buriedSettings( name(), QString("Lock screen when screensaver boot"), QString("settings"), UkccCommon::boolToString(b)); }); connect(screensaverUi, &ScreensaverUi::saverDatetimeEnableChanged, this, [=](bool b) { toChangeKey = SAVER_DATETIME_ENADLE; screensaverInterface->call("setSaverDatetimeEnable", b); UkccCommon::buriedSettings( name(), QString("The date and time are displayedt"), QString("settings"), UkccCommon::boolToString(b)); }); // 如果在dbus端监听gsetting的改变信号,再发送changed信号,此时再去initPreview可能会异常 // 因为界面端程序此时去读取gsetting的值可能还是修改前的值,该问题应该是qgsetting的设计逻辑问题 // 因此这里去监听gsetting的改变而不是dbus的changed QGSettings *mScreensaverGsettings = nullptr; if (!mScreensaverGsettings) { const QByteArray id("org.ukui.screensaver"); if (QGSettings::isSchemaInstalled(id)) { mScreensaverGsettings = new QGSettings(id, QByteArray(), this); } connect(mScreensaverGsettings, &QGSettings::changed, this, [=](QString key) { if (key == "mode") { initPreview(); } }); } connect(screensaverUi, &ScreensaverUi::subModeChanged, this, [=](int mode) { toChangeKey = SUB_MODE; screensaverInterface->call("setSubMode", mode); UkccCommon::buriedSettings( name(), QString("ScreenSaver subMode changed"), QString("settings"), UkccCommon::boolToString(mode)); }); connect(screensaverUi, &ScreensaverUi::musicFollowSystemChanged, this, [=](bool b) { toChangeKey = FOLLOW_SYSTEM; screensaverInterface->call("setMusicFollowSystem", b); UkccCommon::buriedSettings(name(), QString("Screensaver music follow system changed"), QString("settings"), UkccCommon::boolToString(b)); }); connect(screensaverUi, &ScreensaverUi::musicPathChanged, this, [=](QString path) { toChangeKey = MUSIC_PATH; screensaverInterface->call("setMusicPath", path); UkccCommon::buriedSettings(name(), QString("Screensaver music path Select"), QString("select"), path); }); connect(screensaverUi, &ScreensaverUi::photoCycleModeChanged, this, [=](int mode) { toChangeKey = PHOTO_CYCLE_MODE; screensaverInterface->call("setPhotoCycleMode", mode); UkccCommon::buriedSettings( name(), QString("ScreenSaver photo cucle mode changed"), QString("settings"), UkccCommon::boolToString(mode)); }); connect(screensaverUi, &ScreensaverUi::photoSourceChanged, this, [=](QString path) { toChangeKey = PHOTO_PATH; screensaverInterface->call("setPhotoCustomPath", path); UkccCommon::buriedSettings(name(), QString("Screensaver photo path changed"), QString("select"), path); initPhothPath(); }); connect(screensaverUi, &ScreensaverUi::photoSelectPathChanged, this, [=](QString path) { toChangeKey = PHOTO_SELECT_PATH; screensaverInterface->call("setSelectPhotoPath", path); UkccCommon::buriedSettings(name(), QString("Screensaver select photo path changed"), QString("select"), path); initPhothSelectPath(); }); connect(screensaverUi, &ScreensaverUi::removePhotoPathClicked, this, [=](QString path) { toChangeKey = PHOTO_REMOVE_PATH; screensaverInterface->call("removePhotoPath", path); UkccCommon::buriedSettings(name(), QString("Screensaver remove photo path"), QString("remove"), path); initPhothPath(); initPhothSelectPath(); }); connect(screensaverUi, &ScreensaverUi::openFileFolderClicked, this, [=](QString path) { screensaverInterface->call("openFileFolder", path); UkccCommon::buriedSettings(name(), QString("Screensaver open photo path folder"), QString("open"), path); }); } void Screensaver::connectToServer() { QThread *NetThread = new QThread; MThread *NetWorker = new MThread; NetWorker->moveToThread(NetThread); connect(NetThread, &QThread::started, NetWorker, &MThread::run); connect(NetWorker, &MThread::keychangedsignal, this, &Screensaver::keyChangedSlot); connect(NetThread, &QThread::finished, NetWorker, &MThread::deleteLater); NetThread->start(); } void Screensaver::keyChangedSlot(const QString &key) { if (key == "ukui-screensaver") { initContent(); } } ukui-screensaver/src/ukccplugins/screensaver/previewwidget.h0000664000175000017500000000207615172041035023472 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef PREVIEWWIDGET_H #define PREVIEWWIDGET_H #include #include #include class PreviewWidget : public QWidget { Q_OBJECT public: PreviewWidget(QWidget *parent = nullptr); ~PreviewWidget(); void previewScreensaver(); protected: void paintEvent(QPaintEvent *e); private: void mousePressEvent(QMouseEvent *e); }; #endif // PREVIEWWIDGET_H ukui-screensaver/src/ukccplugins/screensaver/photocorver.h0000664000175000017500000000325415172041106023155 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef PHOTOCORVER_H #define PHOTOCORVER_H #include #include #include #include #include #include "../../screensaver/masklable.h" class PhotoCorver : public QLabel { Q_OBJECT public: explicit PhotoCorver(QWidget *parent = nullptr, bool isSystemCorver = false, bool isScreensaver = true); void setBackground(QPixmap background); void setClickStatus(bool state); protected: void paintEvent(QPaintEvent *event); bool eventFilter(QObject *watched, QEvent *event); void mouseReleaseEvent(QMouseEvent *event); void contextMenuEvent(QContextMenuEvent *event); Q_SIGNALS: void photoCorverLeftClicked(); void openFileFolderClicked(); void removePathClicked(); private: QPixmap m_background; bool m_bHoverStatus = false; bool m_bSelectStatus = false; MaskLable *m_maskLabel = nullptr; QMenu *m_rightClickMenu = nullptr; bool m_bSystemCorver = false; bool m_bScreensaver = true; }; #endif // PHOTOCORVER_H ukui-screensaver/src/ukccplugins/screensaver/previewwidget.cpp0000664000175000017500000000303215172041106024015 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "previewwidget.h" #include PreviewWidget::PreviewWidget(QWidget *parent):QWidget(parent) { } PreviewWidget::~PreviewWidget() { } void PreviewWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter p(this); p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::SmoothPixmapTransform); p.setPen(Qt::transparent); p.setBrush(QColor(0, 0, 0, 0)); p.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 10, 10); } void PreviewWidget::mousePressEvent(QMouseEvent *e) { if (parentWidget() && !parentWidget()->isEnabled()) { e->ignore(); return ; } if(e->button() == Qt::LeftButton) { previewScreensaver(); } } void PreviewWidget::previewScreensaver() { static QProcess *viewProcess = new QProcess(); viewProcess->start("ukui-screensaver-command --screensaver"); } ukui-screensaver/src/ukccplugins/CMakeLists.txt0000664000175000017500000000013715172041035020650 0ustar fengfengadd_definitions(-DOPENKYLIN -DNile) add_subdirectory(screenlock) add_subdirectory(screensaver) ukui-screensaver/src/ukccplugins/screenlock/0000775000175000017500000000000015172041106020236 5ustar fengfengukui-screensaver/src/ukccplugins/screenlock/screenlockui.h0000664000175000017500000001066015172041106023100 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENLOCKUI_H #define SCREENLOCKUI_H #include #include #include #include #include #include #include "pictureunit.h" #include #include #include #include #include #include "../screensaver/photocorver.h" using namespace kdk; class ScreenlockUi : public QWidget { Q_OBJECT public: ScreenlockUi(); ~ScreenlockUi(); void setShowOnLogin(bool b); void setLockDatetimeEnable(bool b); void setPicture(const QString &data); void setPictures(const QStringList &picturePathList, const QStringList &sourcePathList); void createPictureUnit(const QPixmap &pixmap, const QString &filename, const bool &clicked); void resetClickedPic(); private: void initUi(); void initScreenLock(); void initSignals(); void initLeavLockStatus(); bool hasBluetoothAdapter(); bool isBluetoothPowered(); bool getLeavLockOn(); QString getLeaveLockDev(); bool getLeaveLockPower(); void setLeaveLockPower(bool on); QMap getBtPairedDevices(); private slots: void addPairedDev(QString dev, bool paired); void powerChanged(bool powered); void setLeaveLock(bool on); void defaultAdapterChanged(QString address); void adapterAdded(QString address); void adapterRemoved(QString address); void setBtPushButtonClickSlot(); void btLeaveLockButtonClickSlot(bool clicked); void bluetoothWidgetIdxChangeSlot(int index); void devConnectStatusChanged(QString dev, bool status); void leaveLockSuc(); private: QVBoxLayout *m_uiLayout = nullptr; QVBoxLayout *m_screenlockLayout = nullptr; QVBoxLayout *m_relateLayout = nullptr; private: KLabel *m_wallpaperTitleLabel = nullptr; UkccFrame *m_previewFrame = nullptr; QHBoxLayout *m_previewLayout = nullptr; QLabel *m_previewLabel = nullptr; SettingGroup *m_centerGroup = nullptr; QWidget *m_noBtWidget = nullptr; UkccFrame *m_picBtnFrame = nullptr; QVBoxLayout *m_picBtnLayout = nullptr; UkccFrame *m_pictureFrame = nullptr; FlowLayout *m_pictureLayout = nullptr; UkccFrame *m_bottomFrame = nullptr; QHBoxLayout *m_bottomLayout = nullptr; QPushButton *m_localBtn = nullptr; QPushButton *m_onlineBtn = nullptr; QPushButton *m_resetBtn = nullptr; KSwitchButton* m_showPicSwitchButton = nullptr; KSwitchButton* m_lockDatetimeEnableBtn = nullptr; KSwitchButton* m_btLeaveLockSwitchButton = nullptr; KPushButton* m_setBtPushButton = nullptr; QWidget* m_setBtWidget = nullptr; UkccFrame* m_relateFrame = nullptr; QWidget* m_bluetoothWidget = nullptr; QComboBox* m_bluetoothComboBox = nullptr; KBackgroundGroup* m_contentBackgroundGroup = nullptr; QScrollArea *previewScrollArea = nullptr; private: KLabel *relateTitleLabel = nullptr; SettingGroup *relatedGroup = nullptr; PushButtonWidget *monitorWidget = nullptr; PushButtonWidget *screensaverWidget = nullptr; private: PhotoCorver *prePictureUnit = nullptr; bool canLeaveLock = false; QString currentFile; QString lockdev; Q_SIGNALS: void showOnLoginChanged(const bool &b); void lockDatetimeEnableChanged(const bool &b); void pictureChanged(const QString &picture); void localButtonClicked(); void onlineButtonClicked(); void resetButtonClicked(); void toSetMonitor(); void toSetScreensaver(); }; #endif // SCREENLOCKUI_H ukui-screensaver/src/ukccplugins/screenlock/screenlock.cpp0000664000175000017500000003464515172041106023106 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screenlock.h" #include using namespace ukcc; #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOCAL_USR_PATH QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) #define LOCAL_APP_PATH QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)\ %QStringLiteral("/ukui-control-center/") #define LOCAL_WALLPAPER_PATH QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)\ %QStringLiteral("/ukui-control-center/wallpaperData/") #define LOCAL_WALLPAPER_HASH_PATH QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)\ %QStringLiteral("/ukui-control-center/wallpaperData/HASH") #define HTTPSTYPE "x-scheme-handler/https" #define LOCAL_CONFIG_DIR "/.config/" #define SYSTEM_CONFIG_DIR "/usr/share/applications/" Screenlock::Screenlock() : mFirstLoad(true) { QTranslator *translator = new QTranslator(this); QLocale locale; translator->load("/usr/share/ukui-screensaver/ukccplugins/i18n_qm/" + locale.name() + ".qm"); QApplication::installTranslator(translator); pluginName = tr("Screenlock"); pluginType = PERSONALIZED; screenlockInterface = new QDBusInterface( "org.ukui.ScreenSaver", "/Screenlock", "org.ukui.ScreenSaver.session.Screenlock", QDBusConnection::sessionBus(), this); m_previewPictures = screenlockInterface->property("previewWallpapers").toStringList(); m_sourcePictures = screenlockInterface->property("sourceWallpapers").toStringList(); m_picInfoList.clear(); } Screenlock::~Screenlock() {} QString Screenlock::plugini18nName() { return pluginName; } int Screenlock::pluginTypes() { return pluginType; } QWidget *Screenlock::pluginUi() { if (mFirstLoad) { mFirstLoad = false; screenlockui = new ScreenlockUi(); if (screenlockInterface->isValid()) { // 判断服务是否存在 QDBusMessage message = screenlockInterface->call("ping"); // 判断dbus路径是否存在 if (message.type() == QDBusMessage::ErrorMessage && message.errorMessage().contains("No such object path", Qt::CaseInsensitive)) { qWarning() << screenlockInterface << ":" << message.errorMessage(); } else { QDBusConnection::sessionBus().connect( "org.ukui.ScreenSaver", "/Screenlock", "org.ukui.ScreenSaver.session.Screenlock", "changed", this, SLOT(dataChanged(QString))); initContent(); connectUiSignals(); connectToServer(); } } else { qCritical() << "org.ukui.ScreenSaver.session.Screenlock DBus error:" << screenlockInterface->lastError(); } } else { screenlockui->resize(screenlockui->size() - QSize(1, 1)); screenlockui->resize(screenlockui->size() + QSize(1, 1)); } return screenlockui; } const QString Screenlock::name() const { return QStringLiteral("Screenlock"); } bool Screenlock::isShowOnHomePage() const { return true; } QIcon Screenlock::icon() const { return QIcon::fromTheme("ukui-lock-screen-symbolic"); } bool Screenlock::isEnable() const { return true; } void Screenlock::dataChanged(const QString &key) { if (toChangeKey != key) { if (key == "showOnLogin") { initShowOnLogin(); } else if (key == "wallpaper") { initWallpaper(); } } toChangeKey = ""; } void Screenlock::convertPixmapSlot(const QString &filename, int num) { QPixmap qPixmap = QPixmap(filename); if (qPixmap.isNull()){ QFile file(filename); qint64 fileSize = file.size(); if (fileSize <= 100000000 && file.open(QIODevice::ReadOnly)){ qPixmap.loadFromData(file.readAll()); file.close(); } } QSize IMAGE_SIZE(166, 110); QPixmap pixmap = qPixmap.scaled(IMAGE_SIZE); QString tmpFile = filename; tmpFile.replace("/", "-"); QFile wallpaperFile; wallpaperFile.setFileName(LOCAL_WALLPAPER_PATH + QString::number(num) + tmpFile); QFileInfo fileinfo = QFileInfo(tmpFile); pixmap.save(&wallpaperFile, fileinfo.suffix().toUtf8().data()); } void Screenlock::loadPictureInfo() { if (m_previewPictures.size() != m_sourcePictures.size()) { qWarning() << "picturePathList.size():" << m_previewPictures.size() << " != sourcePathList.size():" << m_sourcePictures.size(); return; } QDir dir(LOCAL_WALLPAPER_PATH); QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot | QDir::Dirs); qDebug() << Q_FUNC_INFO << "fileInfoList size" << fileInfoList.size() << "m_sourcePictures size" << m_sourcePictures.size(); if ((fileInfoList.size() - 1) == m_sourcePictures.size()) { qDebug() << Q_FUNC_INFO << "equal file num, skip load"; m_picInfoList.clear(); for (int i = 0; i < m_previewPictures.size(); ++i) { m_picInfoList.append(m_previewPictures.at(i)); } return; } int num = 0; for (QString filename : m_sourcePictures) { m_watcher.setFuture(QtConcurrent::run([=, filename, num]() { convertPixmapSlot(filename, num); })); num++; } } void Screenlock::loadPictureInfoSlot() { screenlockui->setPictures(m_previewPictures, m_sourcePictures); } void Screenlock::initContent() { loadPictureInfo(); if (!m_picInfoList.empty()) { loadPictureInfoSlot(); } else { QObject::connect(&m_watcher, &QFutureWatcher::finished, [=]() { QTimer::singleShot(300, [=]() { loadPictureInfoSlot(); }); }); } initWallpaper(); initShowOnLogin(); initLockDatetimeEnable(); } void Screenlock::initLockDatetimeEnable() { screenlockui->setLockDatetimeEnable(screenlockInterface->property("lockDatetimeEnable").toBool()); } void Screenlock::initShowOnLogin() { screenlockui->setShowOnLogin(screenlockInterface->property("showOnLogin").toBool()); } void Screenlock::initWallpaper() { screenlockui->setPicture(screenlockInterface->property("wallpaper").toString()); } QString Screenlock::getDefaultAppId(const char *contentType) { QString localfile = QDir::homePath() + LOCAL_CONFIG_DIR + "mimeapps.list"; QString systemfile = SYSTEM_CONFIG_DIR + QString("ukui-mimeapps.list"); if (QFile(localfile).exists()) { QSettings* mimeappFile = new QSettings(localfile, QSettings::IniFormat); QString str = mimeappFile->value(QString("Default Applications/%1").arg(contentType)).toString(); if (!str.isEmpty()) { if (QFile(SYSTEM_CONFIG_DIR +str).exists()) { return str; } else { return QString(""); } } delete mimeappFile; mimeappFile = nullptr; } if (QFile(systemfile).exists()) { QSettings* mimeappFile = new QSettings(systemfile, QSettings::IniFormat); QString str = mimeappFile->value(QString("Default Applications/%1").arg(contentType)).toString(); if (!str.isEmpty()) { if (QFile(SYSTEM_CONFIG_DIR +str).exists()) { return str; } else { return QString(""); } } delete mimeappFile; mimeappFile = nullptr; } return QString(""); } void Screenlock::connectUiSignals() { connect(screenlockui, &ScreenlockUi::showOnLoginChanged, this, [=](bool b) { toChangeKey = "showOnLogin"; screenlockInterface->call("setShowOnLogin", b); UkccCommon::buriedSettings( name(), QString("Show picture of screenlock on screenlogin"), QString("settings"), UkccCommon::boolToString(b)); }); connect(screenlockui, &ScreenlockUi::lockDatetimeEnableChanged, this, [=](bool b) { toChangeKey = "lockDatetimeEnable"; screenlockInterface->call("setLockDatetimeEnable", b); UkccCommon::buriedSettings( name(), QString("The date and time are displayed"), QString("settings"), UkccCommon::boolToString(b)); }); connect(screenlockui, &ScreenlockUi::pictureChanged, this, [=](QString file) { toChangeKey = "wallpaper"; screenlockInterface->call("setWallpaper", file); }); connect(screenlockui, &ScreenlockUi::localButtonClicked, this, [=]() { showLocalWpDialog(); }); connect(screenlockui, &ScreenlockUi::onlineButtonClicked, this, [=]() { bool openUrl = QDesktopServices::openUrl(QUrl(QLatin1String("https://www.ubuntukylin.com/wallpaper.html"))); if(!openUrl) { QMessageBox *msg = new QMessageBox(screenlockui); msg->setIcon(QMessageBox::Icon::Critical); msg->setText(tr("Cannot access online images, please check the system's default browser settings.")); msg->addButton(tr("Cancel"), QMessageBox::RejectRole); msg->addButton(tr("Set up the browser"), QMessageBox::AcceptRole); connect(msg, &QMessageBox::accepted, this, [=]() { QProcess::startDetached( "ukui-control-center", QStringList() << "-m" << "Defaultapp"); }); msg->exec(); } UkccCommon::buriedSettings(name(), QString("onlineBtnonlineButton"), QString("clicked")); }); connect(screenlockui, &ScreenlockUi::resetButtonClicked, this, [=]() { screenlockInterface->call("resetDefault"); UkccCommon::buriedSettings(name(), QString("resetBtn"), QString("clicked")); }); connect(screenlockui, &ScreenlockUi::toSetMonitor, this, [=]() { UkccCommon::buriedSettings(name(), "setBtn(Monitor Off)", QString("clicked")); QProcess::startDetached( "ukui-control-center", QStringList() << "-m" << "Power"); }); connect(screenlockui, &ScreenlockUi::toSetScreensaver, this, [=]() { UkccCommon::buriedSettings(name(), "setBtn(Screensaver)", QString("clicked")); QProcess::startDetached( "ukui-control-center", QStringList() << "-m" << "Screensaver"); }); } void Screenlock::showLocalWpDialog() { QStringList filters; filters << tr("Wallpaper files(*.jpg *.jpeg *.bmp *.dib *.png *.jfif *.jpe *.gif *.tif *.tiff *.wdp)"); QFileDialog fd; QList usb_list = fd.sidebarUrls(); int sidebarNum = 8; // 最大添加U盘数,可以自己定义 QString home_path = QDir::homePath().section("/", -1, -1); QString mnt = "/media/" + home_path + "/"; QDir mntDir(mnt); mntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList file_list = mntDir.entryInfoList(); QList mntUrlList; for (int i = 0; i < sidebarNum && i < file_list.size(); ++i) { QFileInfo fi = file_list.at(i); mntUrlList << QUrl("file://" + fi.filePath()); } QFileSystemWatcher m_fileSystemWatcher(&fd); m_fileSystemWatcher.addPath("/media/" + home_path + "/"); connect( &m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, &fd, [=, &sidebarNum, &mntUrlList, &usb_list, &fd](const QString path) { QDir m_wmntDir(path); m_wmntDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList m_wfilist = m_wmntDir.entryInfoList(); mntUrlList.clear(); for (int i = 0; i < sidebarNum && i < m_wfilist.size(); ++i) { QFileInfo m_fi = m_wfilist.at(i); mntUrlList << QUrl("file://" + m_fi.filePath()); } fd.setSidebarUrls(usb_list + mntUrlList); fd.update(); }); connect(&fd, &QFileDialog::finished, &fd, [=, &usb_list, &fd]() { fd.setSidebarUrls(usb_list); }); fd.setDirectory(QString(const_cast(g_get_user_special_dir(G_USER_DIRECTORY_PICTURES)))); fd.setAcceptMode(QFileDialog::AcceptOpen); fd.setViewMode(QFileDialog::List); fd.setNameFilters(filters); fd.setFileMode(QFileDialog::ExistingFile); fd.setWindowTitle(tr("select custom wallpaper file")); fd.setLabelText(QFileDialog::Accept, tr("Select")); fd.setLabelText(QFileDialog::LookIn, tr("Position: ")); fd.setLabelText(QFileDialog::FileName, tr("FileName: ")); fd.setLabelText(QFileDialog::FileType, tr("FileType: ")); fd.setLabelText(QFileDialog::Reject, tr("Cancel")); fd.setSidebarUrls(usb_list + mntUrlList); if (fd.exec() != QDialog::Accepted) { return; } QString selectedfile; selectedfile = fd.selectedFiles().first(); screenlockInterface->call("setWallpaper", selectedfile); UkccCommon::buriedSettings(name(), QString("browserLocalwpBtn"), QString("clicked")); } void Screenlock::connectToServer() { QThread *NetThread = new QThread; MThread *NetWorker = new MThread; NetWorker->moveToThread(NetThread); connect(NetThread, &QThread::started, NetWorker, &MThread::run); connect(NetWorker, &MThread::keychangedsignal, this, &Screenlock::keyChangedSlot); connect(NetThread, &QThread::finished, NetWorker, &MThread::deleteLater); NetThread->start(); } void Screenlock::keyChangedSlot(const QString &key) { if (key == "ukui-screensaver") { if (!bIsCloudService) { bIsCloudService = true; } initShowOnLogin(); initWallpaper(); } } ukui-screensaver/src/ukccplugins/screenlock/CMakeLists.txt0000664000175000017500000000256515172041106023006 0ustar fengfengpkg_check_modules(QGS REQUIRED gsettings-qt6) pkg_check_modules(GIO2.0 REQUIRED gio-2.0) pkg_check_modules(KYSDKQTWIDGETS REQUIRED kysdk-qtwidgets) pkg_check_modules(KYSDKDIAGNOSTICS REQUIRED kysdk-diagnostics) include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_SOURCE_DIR}/Common) include_directories( ${QGS_INCLUDE_DIRS} ${GIO2.0_INCLUDE_DIRS} ${KYSDKQTWIDGETS_INCLUDE_DIRS} ${KYSDKDIAGNOSTICS_INCLUDE_DIRS} /usr/include/ukcc ) set(EXTRA_LIBS ${EXTRA_LIBS} ${QGS_LIBRARIES} ${GIO2.0_LIBRARIES} ${KYSDKQTWIDGETS_LIBRARIES} ${KYSDKDIAGNOSTICS_LIBRARIES} -lukcc ) qt6_add_resources(lockscreen_Plugin_SRC ) qt6_wrap_cpp(lockscreen_Plugin_SRC pictureunit.h screenlock.h screenlockui.h ../../screensaver/masklable.h ../screensaver/photocorver.h ../../common/commonfunc.h ) set(lockscreen_Plugin_SRC ${lockscreen_Plugin_SRC} pictureunit.cpp screenlock.cpp screenlockui.cpp ../../screensaver/masklable.cpp ../screensaver/photocorver.cpp ../../common/commonfunc.cpp ) add_library(screenlock SHARED ${lockscreen_Plugin_SRC}) target_link_libraries(screenlock Qt6::Widgets Qt6::DBus Qt6::Xml Qt6::Svg ${EXTRA_LIBS}) install(TARGETS screenlock DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE}/ukui-control-center) ukui-screensaver/src/ukccplugins/screenlock/pictureunit.cpp0000664000175000017500000000722615172041106023324 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "pictureunit.h" #include #include #include #include #include #include PictureUnit::PictureUnit(QWidget *parent) : QLabel(parent) { _filename = ""; hoverStyleSheet = QString("border-width: 3px;border-style: solid;border-color: palette(highlight);"); clickedStyleSheet = QString("border-width: 4px;border-style: solid;border-color: palette(highlight);"); if (QGSettings::isSchemaInstalled("org.ukui.style")) { QGSettings *qtSettings = new QGSettings("org.ukui.style", QByteArray(), this); connect(qtSettings, &QGSettings::changed, this, [=](const QString &key) { if (key == "themeColor" && this->clickedFlag) { this->setStyleSheet(clickedStyleSheet); } }); } clickedFlag = false; setAttribute(Qt::WA_DeleteOnClose); setFixedSize(QSize(198, 136)); setScaledContents(true); MaskWidget *maskWidget = new MaskWidget(this); maskWidget->setGeometry(0, 0, this->width(), this->height()); isTouchControl = false; qApp->installEventFilter(this); this->setAttribute(Qt::WA_AcceptTouchEvents); } PictureUnit::~PictureUnit() {} void PictureUnit::setFilenameText(QString fn) { _filename = fn; } QString PictureUnit::filenameText() { return _filename; } void PictureUnit::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton && !isTouchControl && e->pos().x() >= 0 && e->pos().y() >= 0 && e->pos().x() <= width() && e->pos().y() <= height()) { changeClickedFlag(true); Q_EMIT clicked(_filename); } } void PictureUnit::enterEvent(QEvent *e) { if (getClickedFlag() == false && !isTouchControl) { setFrameShape(QFrame::Box); setStyleSheet(hoverStyleSheet); } } void PictureUnit::leaveEvent(QEvent *e) { if (getClickedFlag() == false) { setStyleSheet("border-width: 0px;"); } } bool PictureUnit::getClickedFlag() { return clickedFlag; } void PictureUnit::changeClickedFlag(bool flag) { clickedFlag = flag; if (flag) { this->setFrameShape(QFrame::Box); this->setStyleSheet(clickedStyleSheet); } else { this->setStyleSheet("border-width: 0px;"); } } bool PictureUnit::eventFilter(QObject *w, QEvent *e) { if (w == this) { if (e->type() == QEvent::TouchBegin) { isTouchControl = true; QTouchEvent *touchE = static_cast(e); touchBeginPoint = touchE->touchPoints().at(0).screenPos(); } } else if (e->type() == QEvent::TouchEnd && isTouchControl) { isTouchControl = false; QTouchEvent *touchE = static_cast(e); touchEndPoint = touchE->touchPoints().at(0).screenPos(); if (qFabs(touchEndPoint.x() - touchBeginPoint.x()) < 1.0 && qFabs(touchEndPoint.y() - touchBeginPoint.y()) < 1.0) { emit clicked(_filename); } } return qApp->eventFilter(w, e); } ukui-screensaver/src/ukccplugins/screenlock/screenlock.h0000664000175000017500000000526015172041106022542 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENLOCK_H #define SCREENLOCK_H #include #include #include "screenlockui.h" #include #include #include #ifdef signals #undef signals #endif #include #include class Screenlock : public QObject, CommonInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.ukcc.CommonInterface") Q_INTERFACES(CommonInterface) public: Screenlock(); ~Screenlock(); QString plugini18nName() Q_DECL_OVERRIDE; int pluginTypes() Q_DECL_OVERRIDE; QWidget * pluginUi() Q_DECL_OVERRIDE; const QString name() const Q_DECL_OVERRIDE; bool isShowOnHomePage() const Q_DECL_OVERRIDE; QIcon icon() const Q_DECL_OVERRIDE; bool isEnable() const Q_DECL_OVERRIDE; virtual QString translationPath() const { return QStringLiteral("/usr/share/ukui-screensaver/ukccplugins/i18n/%1.ts"); } private: void connectToServer(); /** * @brief getDefaultAppId * @param contentType * @return */ QString getDefaultAppId(const char *contentType); public Q_SLOTS: void keyChangedSlot(const QString &key); public: void initContent(); void initShowOnLogin(); void initLockDatetimeEnable(); void initWallpaper(); void connectUiSignals(); void showLocalWpDialog(); void loadPictureInfo(); void convertPixmapSlot(const QString &filename, int num); void picUnitClickSlot(PictureUnit* picUnit, QString fn); void loadPictureInfoSlot(); public Q_SLOTS: void dataChanged(const QString &key); private: QString toChangeKey; QString pluginName; int pluginType; bool mFirstLoad; ScreenlockUi *screenlockui = nullptr; QDBusInterface *screenlockInterface = nullptr; bool bIsCloudService = false; QList m_picInfoList; QFutureWatcher m_watcher; QStringList m_previewPictures; QStringList m_sourcePictures; }; #endif // SCREENLOCK_H ukui-screensaver/src/ukccplugins/screenlock/screenlockui.cpp0000664000175000017500000007534015172041106023441 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screenlockui.h" #include #include ScreenlockUi::ScreenlockUi() { setAttribute(Qt::WA_DeleteOnClose); m_screenlockLayout = new QVBoxLayout(this); m_screenlockLayout->setContentsMargins(0,0,0,0); initUi(); } ScreenlockUi::~ScreenlockUi() { } void ScreenlockUi::initUi() { initScreenLock(); #ifdef Nile initLeavLockStatus(); #endif initSignals(); } void ScreenlockUi::initScreenLock() { // 1 锁屏 m_wallpaperTitleLabel = new KLabel(this); m_wallpaperTitleLabel->setContentsMargins(16, 0, 0, 0); //~ contents_path /Screenlock/Screenlock m_wallpaperTitleLabel->setText(tr("Screenlock")); // 2.1 预览图 m_previewFrame = new UkccFrame(this); m_previewFrame->setFixedHeight(193); m_previewLayout = new QHBoxLayout(m_previewFrame); m_previewLayout->setContentsMargins(16, 12, 16, 16); m_previewLabel = new QLabel(this); m_previewLabel->setFixedSize(280, 160); m_centerGroup = new SettingGroup(this); m_contentBackgroundGroup = new KBackgroundGroup(m_previewFrame); QList contentList; // 登录界面显示锁屏壁纸 QWidget* showPicWidget = new QWidget(); QHBoxLayout* typeLayout = new QHBoxLayout(showPicWidget); typeLayout->setContentsMargins(16, 0, 0, 0); typeLayout->setSpacing(0); //~ contents_path /Screenlock/Show picture of screenlock on screenlogin KLabel* showPicLabel = new KLabel(showPicWidget); showPicLabel->setText(tr("Show picture of screenlock on screenlogin")); showPicLabel->setMinimumWidth(50); m_showPicSwitchButton = new KSwitchButton(showPicWidget); typeLayout->addWidget(showPicLabel); typeLayout->addStretch(); typeLayout->addWidget(m_showPicSwitchButton); contentList.append(showPicWidget); QWidget* lockDatetimeEnableWidget = new QWidget(); QHBoxLayout* datetimeeLayout = new QHBoxLayout(lockDatetimeEnableWidget); datetimeeLayout->setContentsMargins(16, 0, 0, 0); datetimeeLayout->setSpacing(0); //~ contents_path /Screenlock/The date and time are displayed KLabel* datetimeLabel = new KLabel(lockDatetimeEnableWidget); datetimeLabel->setText(tr("The date and time are displayed")); datetimeLabel->setMinimumWidth(50); m_lockDatetimeEnableBtn = new KSwitchButton(lockDatetimeEnableWidget); datetimeeLayout->addWidget(datetimeLabel); datetimeeLayout->addStretch(); datetimeeLayout->addWidget(m_lockDatetimeEnableBtn); contentList.append(lockDatetimeEnableWidget); #ifdef Nile // 动态锁 QWidget* btLeaveLockWidget = new QWidget(); QHBoxLayout* btLeaveLockLayout = new QHBoxLayout(btLeaveLockWidget); btLeaveLockLayout->setContentsMargins(16, 0, 0, 0); btLeaveLockLayout->setSpacing(0); KLabel* btLeaveLockLabel = new KLabel(btLeaveLockWidget); btLeaveLockLabel->setText(tr("Leave lock (System will be locked when the paired phone gone)")); btLeaveLockLabel->setMinimumWidth(50); m_btLeaveLockSwitchButton = new KSwitchButton(btLeaveLockWidget); btLeaveLockLayout->addWidget(btLeaveLockLabel); btLeaveLockLayout->addStretch(); btLeaveLockLayout->addWidget(m_btLeaveLockSwitchButton); contentList.append(btLeaveLockWidget); // 蓝牙设备 m_bluetoothWidget = new QWidget(); KLabel* bluetoothLabel = new KLabel(m_bluetoothWidget); bluetoothLabel->setText(tr("Specified device")); m_bluetoothComboBox = new QComboBox(m_bluetoothWidget); QHBoxLayout* bluetoothLayout = new QHBoxLayout(m_bluetoothWidget); bluetoothLayout->setContentsMargins(16, 0, 0, 0); bluetoothLayout->setSpacing(0); bluetoothLayout->addWidget(bluetoothLabel); bluetoothLayout->addStretch(); bluetoothLayout->addWidget(m_bluetoothComboBox); m_bluetoothWidget->setVisible(false); contentList.append(m_bluetoothWidget); // 无配对手机去设置蓝牙 m_setBtWidget = new QWidget(); QHBoxLayout* setBtLayout = new QHBoxLayout(m_setBtWidget); setBtLayout->setContentsMargins(16, 0, 0, 0); setBtLayout->setSpacing(0); KLabel* setBtLabel = new KLabel(m_setBtWidget); setBtLabel->setText(tr("No paired phone. Please turn to 'Bluetooth' to pair.")); setBtLabel->setMinimumWidth(50); QLabel* setBticonLabel = new QLabel(m_setBtWidget); QIcon icon = QIcon::fromTheme("kylin-dialog-warning"); if (icon.isNull()) { icon = QIcon::fromTheme("dialog-warning"); } setBticonLabel->setPixmap(icon.pixmap(16, 16)); m_setBtPushButton = new KPushButton(m_setBtWidget); m_setBtPushButton->setText(tr("Bluetooth")); setBtLayout->addWidget(setBticonLabel); setBtLayout->addWidget(setBtLabel); setBtLayout->addStretch(); setBtLayout->addWidget(m_setBtPushButton); m_setBtWidget->setVisible(false); contentList.append(m_setBtWidget); m_noBtWidget = new QWidget(); contentList.append(m_noBtWidget); #endif m_contentBackgroundGroup->addWidgetList(contentList); // 预览区域滚动条设置 previewScrollArea = new QScrollArea; previewScrollArea->setFixedHeight(192); previewScrollArea->viewport()->setAttribute(Qt::WA_TranslucentBackground); previewScrollArea->setStyleSheet("QScrollArea{background-color: transparent; border-radius: 6px}"); previewScrollArea->verticalScrollBar()->setProperty("drawScrollBarGroove", false); previewScrollArea->verticalScrollBar()->setEnabled(false); previewScrollArea->horizontalScrollBar()->setEnabled(false); previewScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); previewScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); previewScrollArea->setWidgetResizable(true); previewScrollArea->setWidget(m_previewFrame); QVBoxLayout* rightPreviewLayout = new QVBoxLayout(); rightPreviewLayout->setContentsMargins(8, 0, 16, 0); rightPreviewLayout->setSpacing(0); rightPreviewLayout->addWidget(m_contentBackgroundGroup); rightPreviewLayout->addStretch(); m_previewLayout->addWidget(m_previewLabel); m_previewLayout->addSpacing(0); m_previewLayout->addLayout(rightPreviewLayout); m_picBtnFrame = new UkccFrame(m_centerGroup); m_picBtnFrame->setLineWidth(0); m_pictureFrame = new UkccFrame(m_picBtnFrame); m_pictureFrame->setLineWidth(0); m_pictureLayout = new FlowLayout(m_pictureFrame, 8, -1, -1); m_pictureLayout->setContentsMargins(16, 0, 16, 0); m_bottomFrame = new UkccFrame(m_picBtnFrame); // 3.1 选择图片 //~ contents_path /Wallpaper/Choose Picture QLabel* chooseLabel = new QLabel(tr("Choose Picture"), m_bottomFrame); // 3.2 本地图片 //~ contents_path /Screenlock/Local Pictures m_localBtn = new QPushButton(tr("Local Pictures"), m_bottomFrame); // 3.3 在线图片 //~ contents_path /Screenlock/Online Pictures m_onlineBtn = new QPushButton(tr("Online Pictures"), m_bottomFrame); // 3.4 恢复默认 //~ contents_path /Screenlock/Reset To Default m_resetBtn = new QPushButton(tr("Reset To Default"), m_bottomFrame); m_bottomLayout = new QHBoxLayout(m_bottomFrame); m_bottomLayout->setContentsMargins(16, 8, 16, 8); m_bottomLayout->addWidget(chooseLabel); m_bottomLayout->addStretch(); m_bottomLayout->addWidget(m_localBtn); m_bottomLayout->addWidget(m_onlineBtn); m_bottomLayout->addWidget(m_resetBtn); // 相关设置 m_relateFrame = new UkccFrame(m_picBtnFrame); m_relateLayout = new QVBoxLayout(m_relateFrame); UkccFrame* titleFrame = new UkccFrame(); QHBoxLayout* relateTitleLayout = new QHBoxLayout(titleFrame); relateTitleLayout->setContentsMargins(0, 0, 0, 0); relateTitleLabel = new KLabel(m_relateFrame); relateTitleLabel->setContentsMargins(16, 0, 0, 0); relateTitleLayout->addWidget(relateTitleLabel); relateTitleLabel->setText(tr("Related Settings")); KBackgroundGroup* relateGroup = new KBackgroundGroup(); monitorWidget = new PushButtonWidget(tr("Monitor Off"), m_relateFrame); monitorWidget->setButtonText(tr("Set")); screensaverWidget = new PushButtonWidget(tr("Screensaver"), m_relateFrame); screensaverWidget->setButtonText(tr("Set")); relateGroup->addWidget(titleFrame); relateGroup->addWidget(monitorWidget); relateGroup->addWidget(screensaverWidget); m_relateLayout->addWidget(relateGroup); m_picBtnLayout = new QVBoxLayout(m_picBtnFrame); m_picBtnLayout->setContentsMargins(0, 0, 0, 0); m_picBtnLayout->setSpacing(0); m_picBtnLayout->addWidget(m_bottomFrame); m_picBtnLayout->addWidget(m_pictureFrame); m_picBtnLayout->addWidget(m_relateFrame); m_centerGroup->addWidget(m_picBtnFrame); QScrollArea *contentScrollArea = new QScrollArea; contentScrollArea->viewport()->setAttribute(Qt::WA_TranslucentBackground); contentScrollArea->setStyleSheet("QScrollArea{background-color: transparent;}"); contentScrollArea->verticalScrollBar()->setProperty("drawScrollBarGroove", false); contentScrollArea->setWidgetResizable(true); contentScrollArea->setFrameShape(QFrame::Box); contentScrollArea->setFrameShadow(QFrame::Plain); contentScrollArea->setWidget(m_centerGroup); m_screenlockLayout->addWidget(m_wallpaperTitleLabel); m_screenlockLayout->addWidget(previewScrollArea); m_screenlockLayout->addWidget(contentScrollArea); #ifdef Nile QHBoxLayout *noBtVLayout = new QHBoxLayout(); QLabel *noBtWarnIcon = new QLabel(m_noBtWidget); QLabel *noBtMessage = new QLabel(tr("No bluetooth adapter detected, can not use Leave Lock."),m_noBtWidget); noBtWarnIcon->setPixmap(icon.pixmap(18,18)); noBtVLayout->setContentsMargins(16, 0, 16, 0); noBtVLayout->setSpacing(16); noBtVLayout->addWidget(noBtWarnIcon); noBtVLayout->addWidget(noBtMessage,Qt::AlignLeft); m_noBtWidget->setLayout(noBtVLayout); m_noBtWidget->setVisible(false); m_bluetoothWidget->setVisible(false); #endif } void ScreenlockUi::initLeavLockStatus() { bool ispowered = isBluetoothPowered(); bool leaveLockOn = getLeavLockOn();//实际蓝牙维护的离座锁功能 bool leaveLockPower = getLeaveLockPower();//离座锁按钮 qDebug() << "leavelockpower" << leaveLockPower; bool hasAdapter = hasBluetoothAdapter(); QMap btDevices = getBtPairedDevices(); m_bluetoothComboBox->clear(); qDebug() << "checked" << m_btLeaveLockSwitchButton->isChecked(); m_btLeaveLockSwitchButton->setChecked(leaveLockPower); if (leaveLockPower){ m_previewFrame->setFixedHeight(245); previewScrollArea->setFixedHeight(244); } canLeaveLock = !btDevices.isEmpty(); m_bluetoothComboBox->addItem(tr("Please select device")); for (auto dev : btDevices.keys()) { m_bluetoothComboBox->addItem(btDevices.value(dev), dev); } qDebug() << canLeaveLock << ispowered; if (canLeaveLock && ispowered) { lockdev = getLeaveLockDev(); qDebug()<< "lockdev" << lockdev; if (leaveLockOn && !lockdev.isEmpty() && canLeaveLock) { m_bluetoothComboBox->setCurrentIndex(m_bluetoothComboBox->findData(lockdev)); } else { m_bluetoothComboBox->setCurrentIndex(0); } } if (!hasAdapter && leaveLockPower) { m_noBtWidget->setVisible(!hasAdapter); return; } if (m_bluetoothComboBox->count() == 1 && leaveLockPower) { m_setBtWidget->setVisible(true); m_noBtWidget->setVisible(false); m_bluetoothWidget->setVisible(false); return; } m_bluetoothWidget->setVisible(leaveLockPower && canLeaveLock && ispowered); m_setBtWidget->setVisible((!canLeaveLock || !ispowered) && leaveLockPower); } void ScreenlockUi::setBtPushButtonClickSlot() { QProcess *process = new QProcess(); QString cmd = "ukui-control-center"; QStringList arg; arg.clear(); arg << "-m"; arg << "Bluetooth"; process->startDetached(cmd,arg); } void ScreenlockUi::btLeaveLockButtonClickSlot(bool clicked) { setLeaveLockPower(clicked); if (!clicked) { m_noBtWidget->setVisible(false); m_setBtWidget->setVisible(false); m_bluetoothWidget->setVisible(false); if (!lockdev.isEmpty()) { setLeaveLock(false); } lockdev.clear(); m_bluetoothComboBox->setCurrentIndex(0); m_previewFrame->setFixedHeight(193); previewScrollArea->setFixedHeight(192); return; } m_previewFrame->setFixedHeight(245); previewScrollArea->setFixedHeight(244); m_bluetoothComboBox->setCurrentIndex(0); bool ispowered = isBluetoothPowered(); bool hasAdapter = hasBluetoothAdapter(); if (!hasAdapter) { m_noBtWidget->setVisible(!hasAdapter); return; } if (m_bluetoothComboBox->count() == 1) { m_setBtWidget->setVisible(true); m_noBtWidget->setVisible(false); m_bluetoothWidget->setVisible(false); return; } m_bluetoothWidget->setVisible(canLeaveLock && ispowered); m_setBtWidget->setVisible(!canLeaveLock || !ispowered); } void ScreenlockUi::bluetoothWidgetIdxChangeSlot(int index) { if (!lockdev.isEmpty()) { setLeaveLock(false); } if (index == 0) { lockdev.clear(); return; } lockdev = m_bluetoothComboBox->currentData().toString(); setLeaveLock(true); } void ScreenlockUi::initSignals() { connect(m_showPicSwitchButton, &KSwitchButton::clicked, this, [=](){ Q_EMIT showOnLoginChanged(m_showPicSwitchButton->isChecked()); }); connect(m_lockDatetimeEnableBtn, &KSwitchButton::clicked, this, [=](){ Q_EMIT lockDatetimeEnableChanged(m_lockDatetimeEnableBtn->isChecked()); }); #ifdef Nile connect(m_btLeaveLockSwitchButton, &KSwitchButton::clicked, this, [=](bool clicked){ btLeaveLockButtonClickSlot(clicked); }); connect(m_bluetoothComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [=](int index) { bluetoothWidgetIdxChangeSlot(index); }); connect(m_setBtPushButton, &QPushButton::clicked, this, [=](){ setBtPushButtonClickSlot(); }); #endif connect(monitorWidget, &PushButtonWidget::clicked, this, [=](){ Q_EMIT toSetMonitor(); }); connect(screensaverWidget, &PushButtonWidget::clicked, this, [=](){ Q_EMIT toSetScreensaver(); }); connect(m_localBtn, &QPushButton::clicked, this, [=](){ Q_EMIT localButtonClicked(); }); connect(m_onlineBtn, &QPushButton::clicked, this, [=](){ Q_EMIT onlineButtonClicked(); }); connect(m_resetBtn, &QPushButton::clicked, this, [=](){ Q_EMIT resetButtonClicked(); }); #ifdef Nile QDBusConnection::sessionBus().connect("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "devConnectStatusSignal", this, SLOT(devConnectStatusChanged(QString, bool))); QDBusConnection::sessionBus().connect("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "leaveLockSuc", this, SLOT(leaveLockSuc())); QDBusConnection::sessionBus().connect("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "devPairSignal", this, SLOT(addPairedDev(QString,bool))); QDBusConnection::sessionBus().connect("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "defaultAdapterPowerChanged", this, SLOT(powerChanged(bool))); QDBusConnection::sessionBus().connect("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "defaultAdapterChanged", this, SLOT(defaultAdapterChanged(QString))); QDBusConnection::sessionBus().connect("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "adapterAddSignal", this, SLOT(adapterAdded(QString))); QDBusConnection::sessionBus().connect("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "adapterRemoveSignal", this, SLOT(adapterRemoved(QString))); #endif } void ScreenlockUi::setShowOnLogin(bool b) { m_showPicSwitchButton->blockSignals(true); m_showPicSwitchButton->setChecked(b); m_showPicSwitchButton->blockSignals(false); } void ScreenlockUi::setLockDatetimeEnable(bool b) { m_lockDatetimeEnableBtn->blockSignals(true); m_lockDatetimeEnableBtn->setChecked(b); m_lockDatetimeEnableBtn->blockSignals(false); } void ScreenlockUi::setPicture(const QString &data) { currentFile = data; QImageReader reader(data); reader.setDecideFormatFromContent(true); m_previewLabel->setPixmap(QPixmap::fromImage(reader.read()).scaled(m_previewLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); resetClickedPic(); } void ScreenlockUi::setPictures(const QStringList &picturePathList, const QStringList &sourcePathList) { if (picturePathList.size() != sourcePathList.size()) { qWarning()<<"picturePathList.size():"<setBackground(pixmap); if (clicked) { prePictureUnit = photoCorver; photoCorver->setClickStatus(clicked); setPicture(filename); } photoCorver->setObjectName(filename); connect(photoCorver, &PhotoCorver::photoCorverLeftClicked, [=](){ setPicture(filename); Q_EMIT pictureChanged(filename); }); m_pictureLayout->addWidget(photoCorver); } void ScreenlockUi::resetClickedPic() { if (prePictureUnit != nullptr) { prePictureUnit->setClickStatus(false); prePictureUnit = nullptr; } for (int i = m_pictureLayout->count() - 1; i >= 0; --i) { QLayoutItem *it = m_pictureLayout->itemAt(i); PhotoCorver *picUnit = static_cast(it->widget()); if (currentFile == picUnit->objectName()) { picUnit->setClickStatus(true); prePictureUnit = picUnit; } } } QMap ScreenlockUi::getBtPairedDevices() { QMap pairedList; QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "getPairedPhoneAddr"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) { QStringList ret = response.arguments().takeFirst().toStringList(); if (ret.isEmpty()) return pairedList; for (int i = 0; i < ret.size(); i++) { if (pairedList.contains(ret.at(i))) continue; QDBusMessage _message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "getDevName"); _message << ret.at(i); QDBusMessage _response = QDBusConnection::sessionBus().call(_message); if (_response.type() == QDBusMessage::ReplyMessage) { QString devName = _response.arguments().takeFirst().toString(); if (devName == "") continue; pairedList.insert(ret.at(i), devName); } } } return pairedList; } bool ScreenlockUi::hasBluetoothAdapter() { QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", \ "getAdapterDevAddressList"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) { QStringList ret = response.arguments().takeFirst().toStringList(); return !ret.isEmpty(); } else return false; } bool ScreenlockUi::isBluetoothPowered() { QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "getDefaultAdapterPower"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) { return response.arguments().takeFirst().toBool(); } else return false; } void ScreenlockUi::devConnectStatusChanged(QString dev, bool status) { qDebug() << "dev" << dev << "status" << status << "lockdev" << lockdev; if (dev == lockdev && !status) { return ; } if (dev != lockdev) { initLeavLockStatus(); } } void ScreenlockUi::leaveLockSuc() { setLeaveLock(false); initLeavLockStatus(); } void ScreenlockUi::addPairedDev(QString dev, bool paired) { int index = m_bluetoothComboBox->findData(dev); if (paired && index >= 0) return; if (!paired && index >= 0) { m_bluetoothComboBox->removeItem(m_bluetoothComboBox->findData(dev)); if (m_bluetoothComboBox->count() == 1) { m_noBtWidget->setVisible(false); m_bluetoothWidget->setVisible(false); m_setBtWidget->setVisible(true); } if (lockdev == dev) lockdev.clear(); return; } QDBusMessage _message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "getDevName"); _message << dev; QDBusMessage _response = QDBusConnection::sessionBus().call(_message); if (_response.type() == QDBusMessage::ReplyMessage) { QString devName = _response.arguments().takeFirst().toString(); if (devName != "") { m_bluetoothComboBox->addItem(devName, dev); m_setBtWidget->setVisible(false); m_bluetoothWidget->setVisible(true); } } } void ScreenlockUi::powerChanged(bool powered) { if (!m_btLeaveLockSwitchButton->isChecked()) return; if (!hasBluetoothAdapter()) { m_noBtWidget->setVisible(true); m_setBtWidget->setVisible(false); m_bluetoothWidget->setVisible(false); return; } m_noBtWidget->setVisible(false); m_setBtWidget->setVisible(!powered); m_bluetoothWidget->setVisible(powered); setLeaveLock(false); } void ScreenlockUi::setLeaveLockPower(bool on) { QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "setLeaveLockPower"); message << on; QDBusConnection::sessionBus().call(message); } bool ScreenlockUi::getLeaveLockPower() { QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "getLeaveLockPower"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) return response.arguments().takeFirst().toBool(); else return false; } void ScreenlockUi::setLeaveLock(bool on) { if (lockdev.isEmpty()) return; QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "setLeaveLock"); message << lockdev << on; QDBusConnection::sessionBus().call(message); } bool ScreenlockUi::getLeavLockOn() { QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "getLeaveLock"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) return response.arguments().takeFirst().toBool(); else return false; } QString ScreenlockUi::getLeaveLockDev() { QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", "getLeaveLockDev"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) return response.arguments().takeFirst().toString(); else return QString(""); } void ScreenlockUi::defaultAdapterChanged(QString address) { Q_UNUSED(address); if (!m_btLeaveLockSwitchButton->isChecked()) return; bool ispowered = isBluetoothPowered(); bool leaveLockOn = getLeavLockOn(); bool hasAdapter = hasBluetoothAdapter(); QMap btDevices = getBtPairedDevices(); lockdev.clear(); m_bluetoothComboBox->clear(); if (leaveLockOn && !m_btLeaveLockSwitchButton->isChecked()) { m_btLeaveLockSwitchButton->click(); } canLeaveLock = !btDevices.isEmpty(); m_bluetoothComboBox->addItem(tr("Please select device")); for (auto dev : btDevices.keys()) m_bluetoothComboBox->addItem(btDevices.value(dev), dev); if (!hasAdapter && leaveLockOn) { m_noBtWidget->setVisible(!hasAdapter); return; } m_bluetoothWidget->setVisible(leaveLockOn && canLeaveLock && ispowered); m_setBtWidget->setVisible((!canLeaveLock || !ispowered) && leaveLockOn); } void ScreenlockUi::adapterAdded(QString address) { Q_UNUSED(address); if (!m_btLeaveLockSwitchButton->isChecked()) return; QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", \ "getAdapterDevAddressList"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) { QStringList ret = response.arguments().takeFirst().toStringList(); if (ret.size() > 1) return; else { bool ispowered = isBluetoothPowered(); QMap btDevices = getBtPairedDevices(); lockdev.clear(); m_bluetoothComboBox->clear(); canLeaveLock = !btDevices.isEmpty(); m_bluetoothComboBox->addItem(tr("Please select device")); for (auto dev : btDevices.keys()) { m_bluetoothComboBox->addItem(btDevices.value(dev), dev); } m_noBtWidget->setVisible(false); m_setBtWidget->setVisible(false); m_bluetoothWidget->setVisible(canLeaveLock && ispowered); m_setBtWidget->setVisible(!canLeaveLock || !ispowered); } } } void ScreenlockUi::adapterRemoved(QString address) { Q_UNUSED(address); if (!m_btLeaveLockSwitchButton->isChecked()) return; QDBusMessage message = QDBusMessage::createMethodCall("com.ukui.bluetooth", "/com/ukui/bluetooth", "com.ukui.bluetooth", \ "getAdapterDevAddressList"); QDBusMessage response = QDBusConnection::sessionBus().call(message); if (response.type() == QDBusMessage::ReplyMessage) { QStringList ret = response.arguments().takeFirst().toStringList(); if (ret.size() < 1){ lockdev.clear(); m_bluetoothComboBox->clear(); m_noBtWidget->setVisible(true); m_bluetoothWidget->setVisible(false); m_setBtWidget->setVisible(false); } } } ukui-screensaver/src/ukccplugins/screenlock/pictureunit.h0000664000175000017500000000274415172041035022772 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef PICTUREUNIT_H #define PICTUREUNIT_H #include #include #include class PictureUnit : public QLabel { Q_OBJECT public: explicit PictureUnit(QWidget *parent = nullptr); ~PictureUnit(); public: void setFilenameText(QString fn); QString filenameText(); void changeClickedFlag(bool flag); bool getClickedFlag(); void enterEvent(QEvent *e); void leaveEvent(QEvent *e); public: QString _filename; QString clickedStyleSheet; protected: void mouseReleaseEvent(QMouseEvent * e); bool eventFilter(QObject *w, QEvent *e); private: bool clickedFlag; QString hoverStyleSheet; bool isTouchControl; QPointF touchBeginPoint; QPointF touchEndPoint; Q_SIGNALS: void clicked(QString filename); }; #endif // PICTUREUNIT_H ukui-screensaver/src/ukccplugins/sessiondbus/0000775000175000017500000000000015172041106020447 5ustar fengfengukui-screensaver/src/ukccplugins/sessiondbus/screensaverinterface.h0000664000175000017500000001031015172041106025014 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVERINTERFACE_H #define SCREENSAVERINTERFACE_H #include #include #include "../../lock-backend/dbusupperinterface.h" class ScreensaverInterface : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.ukui.ScreenSaver.session.Screensaver") Q_PROPERTY(int preentryTime READ getPreentryTime) Q_PROPERTY(QString screensaverType READ getScreensaverType) Q_PROPERTY(bool showBreakTimeUkui READ getShowBreakTimeUkui) Q_PROPERTY(bool showBreakTimeCustom READ getShowBreakTimeCustom) Q_PROPERTY(bool screenLockEnabled READ getScreenLockEnabled) Q_PROPERTY(QString customPath READ getcustomPath) Q_PROPERTY(bool switchRandom READ getSwitchRandom) Q_PROPERTY(int cycleTime READ getCycleTime) Q_PROPERTY(QString customText READ getCustomText) Q_PROPERTY(bool customTextCentered READ getCustomTextCentered) Q_PROPERTY(QString background READ getBackground) Q_PROPERTY(int subMode READ getSubMode) Q_PROPERTY(bool followSystem READ getMusicFollowSystem) Q_PROPERTY(QString musicPath READ getMusicPath) Q_PROPERTY(QStringList photoPath READ getPhotoPath) Q_PROPERTY(QString photoSelectPath READ getPhotoSelectPath) Q_PROPERTY(int photoCycleMode READ getPhotoCycleMode) Q_PROPERTY(int photoCycleInterval READ getPhotoCycleInterval) Q_PROPERTY(QString weatherData READ getWeatherDate) Q_PROPERTY(QStringList previewWallpapers READ getPreviewWallpapers) Q_PROPERTY(QStringList sourceWallpapers READ getSourceWallpapers) Q_PROPERTY(bool saverDatetimeEnable READ getSaverDatetimeEnable) public: ScreensaverInterface(DbusUpperInterface *upperInterface, QObject *parent = nullptr); ~ScreensaverInterface(); int getPreentryTime(); QString getScreensaverType(); bool getShowBreakTimeUkui(); bool getShowBreakTimeCustom(); bool getScreenLockEnabled(); QString getcustomPath(); bool getSwitchRandom(); int getCycleTime(); QString getCustomText(); bool getCustomTextCentered(); QString getBackground(); int getSubMode(); bool getMusicFollowSystem(); QString getMusicPath(); QStringList getPhotoPath(); QString getPhotoSelectPath(); int getPhotoCycleMode(); int getPhotoCycleInterval(); QString getWeatherDate(); QStringList getPreviewWallpapers(); QStringList getSourceWallpapers(); bool getSaverDatetimeEnable(); private: bool initData(); private: QString mScreensaverType; DbusUpperInterface *m_upperInterface = nullptr; bool m_isInited = false; public Q_SLOTS: void setPreentryTime(int time); void setScreensaverType(QString type); void setShowBreakTimeUkui(bool b); void setShowBreakTimeCustom(bool b); void setScreenLockEnabled(bool b); void setCustomPath(QString path); void setSwitchRandom(bool b); void setCycleTime(int time); void setCustomText(QString Text); void setCustomTextCentered(bool b); void setSubMode(int mode); void setMusicFollowSystem(bool b); void setMusicPath(QString path); void setPhotoCustomPath(QString path); void setPhotoCycleMode(int mode); void setPhotoCycleInterval(int time); void setSelectPhotoPath(QString path); void removePhotoPath(QString path); void openFileFolder(QString path); void setSaverDatetimeEnable(bool enable); private Q_SLOTS: void onLockScreenConfigChanged(QString strKey, QVariant value); void onScreenSaverConfigChanged(QString strKey, QVariant value); Q_SIGNALS: void changed(QString key); }; #endif // SCREENSAVERINTERFACE_H ukui-screensaver/src/ukccplugins/sessiondbus/screensaverinterface.cpp0000664000175000017500000003237115172041106025362 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screensaverinterface.h" #include "../../lock-backend/gsettingshelper.h" ScreensaverInterface::ScreensaverInterface(DbusUpperInterface *upperInterface, QObject *parent) : QObject(parent), m_upperInterface(upperInterface) { } bool ScreensaverInterface::initData() { if (!m_isInited) { mScreensaverType.clear(); QString mode = m_upperInterface->getGSettingsInstance()->GetLockScreenConf(KEY_MODE).toString(); if (mode == "default-ukui") { mScreensaverType = "ukui"; } else if (mode == "default-ukui-custom") { mScreensaverType = "custom"; } else { mScreensaverType = "other"; } connect( m_upperInterface->getGSettingsInstance().get(), &GSettingsHelper::lockScreenConfigChanged, this, &ScreensaverInterface::onLockScreenConfigChanged); connect(m_upperInterface->getGSettingsInstance().get(), &GSettingsHelper::weatherKeysConfigChanged, this, [=](QString strKey, QVariant value) { if (strKey == KEY_WEATHER) { Q_EMIT changed("weatherData"); } }); connect(m_upperInterface->getGSettingsInstance().get(), &GSettingsHelper::screenSaverConfigChanged, this, &ScreensaverInterface::onScreenSaverConfigChanged); m_isInited = true; } return m_isInited; } ScreensaverInterface::~ScreensaverInterface() {} void ScreensaverInterface::onScreenSaverConfigChanged(QString strKey, QVariant value) { if (strKey == KEY_MODE) { Q_EMIT changed("screensaverType"); } else if (strKey == KEY_SUB_MODE) { Q_EMIT changed("subMode"); } else if (strKey == KEY_BACKGROUND_PATH) { Q_EMIT changed("customPath"); } else if (strKey == KEY_AUTOMATIC_SWITCHING_ENABLE) { Q_EMIT changed("switchRandom"); } else if (strKey == KEY_CYCLE_TIME) { Q_EMIT changed("cycleTime"); } else if (strKey == KEY_MYTEXT) { Q_EMIT changed("customText"); } else if (strKey == KEY_TEXT_IS_CENTER) { Q_EMIT changed("customTextCentered"); } else if (strKey == KEY_MUSIC_FOLLOW_SYSTEM) { Q_EMIT changed("followSystem"); } else if (strKey == KEY_MUSIC_PATH) { Q_EMIT changed("musicPath"); } else if (strKey == KEY_PHOTO_CUSTOM_PATH) { Q_EMIT changed("photoPath"); } else if (strKey == KEY_PHOTO_SELECT_PATH) { Q_EMIT changed("photoSelectPath"); } else if (strKey == KEY_PHOTO_CYCLE_MODE) { Q_EMIT changed("photoCycleMode"); } else if (strKey == KEY_PHOTO_CYCLE_INTERVAL) { Q_EMIT changed("photoCycleInterval"); } } void ScreensaverInterface::onLockScreenConfigChanged(QString strKey, QVariant value) { if (strKey == KEY_MODE) { QString mode = value.toString(); if (mode == "default-ukui") { mScreensaverType = "ukui"; } else if (mode == "default-ukui-custom") { mScreensaverType = "custom"; } else { mScreensaverType = "other"; } } else if (strKey == KEY_BACKGROUND) { Q_EMIT changed("background"); } else if (strKey == KEY_LOCK_ENABLED) { Q_EMIT changed("screenLockEnabled"); } else if (strKey == KEY_IDLE_DELAY) { Q_EMIT changed("preentryTime"); } } int ScreensaverInterface::getPreentryTime() { initData(); return m_upperInterface->getGSettingsInstance()->GetLockScreenConf(KEY_IDLE_DELAY).toInt(); } void ScreensaverInterface::setPreentryTime(int time) { initData(); m_upperInterface->getGSettingsInstance()->SetLockScreenConf(KEY_IDLE_DELAY, time); Q_EMIT changed("preentryTime"); } QString ScreensaverInterface::getScreensaverType() { initData(); return mScreensaverType; } void ScreensaverInterface::setScreensaverType(QString type) { initData(); if (type == "ukui") { m_upperInterface->getGSettingsInstance()->SetLockScreenConf(KEY_MODE, "default-ukui"); } else if (type == "custom") { m_upperInterface->getGSettingsInstance()->SetLockScreenConf(KEY_MODE, "default-ukui-custom"); } Q_EMIT changed("screensaverType"); } bool ScreensaverInterface::getShowBreakTimeUkui() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_SHOW_UKUI_REST_TIME).toBool(); } void ScreensaverInterface::setShowBreakTimeUkui(bool b) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_SHOW_UKUI_REST_TIME, b); Q_EMIT changed("showBreakTimeUkui"); } bool ScreensaverInterface::getShowBreakTimeCustom() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_SHOW_CUSTOM_REST_TIME).toBool(); } void ScreensaverInterface::setShowBreakTimeCustom(bool b) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_SHOW_CUSTOM_REST_TIME, b); Q_EMIT changed("showBreakTimeCustom"); } bool ScreensaverInterface::getScreenLockEnabled() { initData(); return m_upperInterface->getGSettingsInstance()->GetLockScreenConf(KEY_LOCK_ENABLED).toBool(); } void ScreensaverInterface::setScreenLockEnabled(bool b) { initData(); m_upperInterface->getGSettingsInstance()->SetLockScreenConf(KEY_LOCK_ENABLED, b); Q_EMIT changed("screenLockEnabled"); } QString ScreensaverInterface::getcustomPath() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_BACKGROUND_PATH).toString(); } void ScreensaverInterface::setCustomPath(QString path) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_BACKGROUND_PATH, path); Q_EMIT changed("customPath"); } bool ScreensaverInterface::getSwitchRandom() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE).toBool(); } void ScreensaverInterface::setSwitchRandom(bool b) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE, b); Q_EMIT changed("switchRandom"); } int ScreensaverInterface::getCycleTime() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_CYCLE_TIME).toInt(); } void ScreensaverInterface::setCycleTime(int time) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_CYCLE_TIME, time); Q_EMIT changed("cycleTime"); } QString ScreensaverInterface::getCustomText() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_MYTEXT).toString(); } void ScreensaverInterface::setCustomText(QString text) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_MYTEXT, text); Q_EMIT changed("customText"); } bool ScreensaverInterface::getCustomTextCentered() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_TEXT_IS_CENTER).toBool(); } void ScreensaverInterface::setCustomTextCentered(bool b) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_TEXT_IS_CENTER, b); Q_EMIT changed("customTextCentered"); } QString ScreensaverInterface::getBackground() { initData(); return m_upperInterface->getGSettingsInstance()->GetLockScreenConf(KEY_BACKGROUND).toString(); } int ScreensaverInterface::getSubMode() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_SUB_MODE).toInt(); } bool ScreensaverInterface::getMusicFollowSystem() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_MUSIC_FOLLOW_SYSTEM).toBool(); } QString ScreensaverInterface::getMusicPath() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_MUSIC_PATH).toString(); } QStringList ScreensaverInterface::getPhotoPath() { initData(); QStringList defaultPath = m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_PHOTO_DEFAULT_PATH).toStringList(); QStringList customPath = m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_PHOTO_CUSTOM_PATH).toStringList(); QSet set; for (const QString &str : customPath) { set.insert(str); } for (const QString &str : defaultPath) { set.insert(str); } QStringList allPhotoPath = set.values(); qDebug() << "allList = " << allPhotoPath; return allPhotoPath; } QString ScreensaverInterface::getPhotoSelectPath() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_PHOTO_SELECT_PATH).toString(); } int ScreensaverInterface::getPhotoCycleMode() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_PHOTO_CYCLE_MODE).toInt(); } int ScreensaverInterface::getPhotoCycleInterval() { initData(); return m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_PHOTO_CYCLE_INTERVAL).toInt(); } QString ScreensaverInterface::getWeatherDate() { initData(); return m_upperInterface->getGSettingsInstance()->GetWeatherKeys(KEY_WEATHER).toString(); } QStringList ScreensaverInterface::getPreviewWallpapers() { initData(); QDBusInterface interface( "org.ukui.ukcc.session", "/Wallpaper", "org.ukui.ukcc.session.Wallpaper", QDBusConnection::sessionBus(), this); if (interface.isValid()) { return interface.property("previewWallpapers").toStringList(); } return QStringList(); } QStringList ScreensaverInterface::getSourceWallpapers() { QDBusInterface interface( "org.ukui.ukcc.session", "/Wallpaper", "org.ukui.ukcc.session.Wallpaper", QDBusConnection::sessionBus(), this); if (interface.isValid()) { return interface.property("sourceWallpapers").toStringList(); } return QStringList(); } bool ScreensaverInterface::getSaverDatetimeEnable() { initData(); return m_upperInterface->GetSaverDatetimeEnable(); } void ScreensaverInterface::setSubMode(int mode) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_SUB_MODE, mode); Q_EMIT changed("subMode"); } void ScreensaverInterface::setMusicPath(QString path) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_MUSIC_PATH, path); Q_EMIT changed("musicPath"); } void ScreensaverInterface::setMusicFollowSystem(bool b) { initData(); m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_MUSIC_FOLLOW_SYSTEM, b); Q_EMIT changed("musicFollowSystem"); } void ScreensaverInterface::setPhotoCustomPath(QString path) { initData(); QStringList customPath = m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_PHOTO_CUSTOM_PATH).toStringList(); QSet set; for (const QString &str : customPath) { set.insert(str); } set.insert(path); QStringList allPhotoPath = set.values(); qDebug() << "allList = " << allPhotoPath; m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_PHOTO_CUSTOM_PATH, allPhotoPath); Q_EMIT changed("photoCustomPath"); } void ScreensaverInterface::removePhotoPath(QString path) { initData(); QString selectPath = getPhotoSelectPath(); if (selectPath == path) { setSelectPhotoPath(DEFAULT_PHOTO_PATH); } QStringList customPath = m_upperInterface->getGSettingsInstance()->GetScreenSaverConf(KEY_PHOTO_CUSTOM_PATH).toStringList(); QSet set; for (const QString &str : customPath) { set.insert(str); } set.remove(path); QStringList allPhotoPath = set.values(); qDebug() << "allList = " << allPhotoPath; m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_PHOTO_CUSTOM_PATH, allPhotoPath); Q_EMIT changed("photoCustomPath"); } void ScreensaverInterface::setPhotoCycleMode(int mode) { m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_PHOTO_CYCLE_MODE, mode); Q_EMIT changed("photoCycleMode"); } void ScreensaverInterface::setPhotoCycleInterval(int time) { m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_PHOTO_CYCLE_INTERVAL, time); Q_EMIT changed("photoCycleInterval"); } void ScreensaverInterface::setSelectPhotoPath(QString path) { m_upperInterface->getGSettingsInstance()->SetScreenSaverConf(KEY_PHOTO_SELECT_PATH, path); Q_EMIT changed("photoSelectPath"); } void ScreensaverInterface::openFileFolder(QString path) { QStringList paths; paths.append(path); m_upperInterface->ShowFolder(paths); Q_EMIT changed("photoSelectPath"); } void ScreensaverInterface::setSaverDatetimeEnable(bool enable) { m_upperInterface->SetSaverDatetimeEnable(enable); Q_EMIT changed("saverDatetimeEnable"); } ukui-screensaver/src/ukccplugins/sessiondbus/screenlockinterface.h0000664000175000017500000000412515172041106024633 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENLOCKINTERFACE_H #define SCREENLOCKINTERFACE_H #include #include #include "../../lock-backend/dbusupperinterface.h" class ScreenlockInterface : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.ukui.ScreenSaver.session.Screenlock") Q_PROPERTY(bool showOnLogin READ getShowOnLogin) Q_PROPERTY(QString wallpaper READ getWallpaper) Q_PROPERTY(QStringList previewWallpapers READ getPreviewWallpapers) Q_PROPERTY(QStringList sourceWallpapers READ getSourceWallpapers) Q_PROPERTY(bool lockDatetimeEnable READ getLockDatetimeEnable) public: ScreenlockInterface(DbusUpperInterface *upperInterface, QObject *parent = nullptr); ~ScreenlockInterface(); bool getShowOnLogin(); QString getWallpaper(); QStringList getPreviewWallpapers(); QStringList getSourceWallpapers(); bool getLockDatetimeEnable(); public Q_SLOTS: void setShowOnLogin(bool b); void setWallpaper(QString f); void resetDefault(); void setLockDatetimeEnable(bool enable); private: QString copyLoginFile(QString fileName); bool initData(); private: QStringList previewWallpaperList; QStringList sourceWallpaperList; QString mUKCConfig = ""; QSettings *lockSetting = nullptr; DbusUpperInterface *m_upperInterface = nullptr; bool m_isInited = false; Q_SIGNALS: void changed(QString key); }; #endif // SCREENLOCKINTERFACE_H ukui-screensaver/src/ukccplugins/sessiondbus/screenlockinterface.cpp0000664000175000017500000001167115172041106025172 0ustar fengfeng/* * Copyright (C) 2025 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screenlockinterface.h" #include #include "../../lock-backend/gsettingshelper.h" ScreenlockInterface::ScreenlockInterface(DbusUpperInterface *upperInterface, QObject *parent) : QObject(parent), m_upperInterface(upperInterface) { } bool ScreenlockInterface::initData() { if (!m_isInited) { mUKCConfig = QDir::homePath() + "/.config/ukui/ukui-control-center.conf"; lockSetting = new QSettings(mUKCConfig, QSettings::IniFormat, this); m_isInited = true; } return m_isInited; } ScreenlockInterface::~ScreenlockInterface() {} void ScreenlockInterface::setShowOnLogin(bool b) { initData(); QString bgStr; struct stat fileStat; if (b && m_upperInterface) { bgStr = m_upperInterface->getGSettingsInstance()->GetLockScreenConf(KEY_BACKGROUND).toString(); stat(bgStr.toStdString().c_str(), &fileStat); if (fileStat.st_uid != 0) { // 在普通用户下 bgStr = copyLoginFile(bgStr); } } else if (!b) { bgStr = ""; } lockSetting->beginGroup("ScreenLock"); lockSetting->setValue("lockStatus", b); lockSetting->endGroup(); QString name = qgetenv("USER"); if (name.isEmpty()) { name = qgetenv("USERNAME"); } QString lockfilename = "/var/lib/lightdm-data/" + name + "/ukui-greeter.conf"; QSettings lockLoginSettings(lockfilename, QSettings::IniFormat, this); lockLoginSettings.beginGroup("greeter"); lockLoginSettings.setValue("backgroundPath", bgStr); lockLoginSettings.endGroup(); Q_EMIT changed("showOnLogin"); } void ScreenlockInterface::setWallpaper(QString f) { initData(); if (m_upperInterface) { m_upperInterface->getGSettingsInstance()->SetLockScreenConf(KEY_BACKGROUND, f); } setShowOnLogin(getShowOnLogin()); Q_EMIT changed("wallpaper"); } bool ScreenlockInterface::getShowOnLogin() { initData(); if (!QFile::exists(mUKCConfig)) { setShowOnLogin(true); } lockSetting->beginGroup("ScreenLock"); lockSetting->sync(); bool status = lockSetting->value("lockStatus").toBool(); lockSetting->endGroup(); return status; } QString ScreenlockInterface::getWallpaper() { initData(); QString initBgStr = ""; if (m_upperInterface) { initBgStr = m_upperInterface->getGSettingsInstance()->GetLockScreenConf(KEY_BACKGROUND).toString(); } // 锁屏壁纸不存在,则展示默认壁纸 if (!QFile::exists(initBgStr)) { initBgStr = "/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg"; if (!QFile::exists(initBgStr)) { initBgStr = "/usr/share/backgrounds/1-openkylin.jpg"; } } return initBgStr; } QStringList ScreenlockInterface::getPreviewWallpapers() { initData(); QDBusInterface interface( "org.ukui.ukcc.session", "/Wallpaper", "org.ukui.ukcc.session.Wallpaper", QDBusConnection::sessionBus(), this); if (interface.isValid()) { return interface.property("previewWallpapers").toStringList(); } return QStringList(); } QStringList ScreenlockInterface::getSourceWallpapers() { QDBusInterface interface( "org.ukui.ukcc.session", "/Wallpaper", "org.ukui.ukcc.session.Wallpaper", QDBusConnection::sessionBus(), this); if (interface.isValid()) { return interface.property("sourceWallpapers").toStringList(); } return QStringList(); } bool ScreenlockInterface::getLockDatetimeEnable() { initData(); return m_upperInterface->GetLockDatetimeEnable(); } void ScreenlockInterface::resetDefault() { initData(); if (m_upperInterface) { QString strDefBg = m_upperInterface->getGSettingsInstance()->ResetLockScreenConf(KEY_BACKGROUND).toString(); setWallpaper(strDefBg); } } QString ScreenlockInterface::copyLoginFile(QString fileName) { QString name = qgetenv("USER"); if (name.isEmpty()) { name = qgetenv("USERNAME"); } QString loginFilename = "/var/lib/lightdm-data/" + name + "/" + "loginBackground"; QString loginCmd = QString("cp '%1' %2").arg(fileName).arg(loginFilename); system(loginCmd.toUtf8().data()); return loginFilename; } void ScreenlockInterface::setLockDatetimeEnable(bool enable) { m_upperInterface->SetLockDatetimeEnable(enable); Q_EMIT changed("lockDatetimeEnable"); } ukui-screensaver/src/lock-backend/0000775000175000017500000000000015172041106016074 5ustar fengfengukui-screensaver/src/lock-backend/dbusupperinterface.cpp0000664000175000017500000034633415172041106022507 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "dbusupperinterface.h" #include "lightdmhelper.h" #include "authpamthread.h" #include "../../src/dbusifs/login1helper.h" #include "../../src/common/definetypes.h" #include "../../src/common/configuration.h" #include "../../src/lock-backend/gsettingshelper.h" #include "../../src/dbusifs/usdhelper.h" #include #include #include #include #include #include #include #include "../../src/QtSingleApplication/qtlocalpeer.h" #include "../../src/lock-backend/switchuserutils.h" #include "../../src/lock-backend/pamauthenticate.h" #include "../../src/dbusifs/upowerhelper.h" #include "../../src/dbusifs/accountshelper.h" #include "../../src/dbusifs/sessionhelper.h" #include "../../src/dbusifs/systemupgradehelper.h" #include "../../src/lock-backend/sessionwatcher.h" #include "../../src/dbusifs/freedesktophelper.h" #include "../../src/lock-backend/bioauthenticate.h" #include "../../src/common/kyrsac.h" #include "../../src/dbusifs/kglobalaccelhelper.h" #include "../../src/lock-backend/personalizeddata.h" #ifdef SUPPORT_TABLET_MODE #include "../../src/dbusifs/libinputswitchevent.h" #endif #include "../../src/dbusifs/uniauthservice.h" #include "../../src/dbusifs/greeterconfig.h" #include "../../src/dbusifs/networkmanagerhelper.h" #include "../../src/dbusifs/filemanagerhelper.h" #include #define CONFIG_FILE "/usr/share/ukui-greeter/ukui-greeter.conf" DbusUpperInterface::DbusUpperInterface(QObject *parent) : QObject(parent) { m_bLockState = false; m_bSlpState = false; } DbusUpperInterface::~DbusUpperInterface() { // 断开所有信号,用disconnect的方法直接断开没有效果 // disconnect(this, nullptr, nullptr, nullptr); // 提前手动管理连接并逐个断开conect,避免在析构时因为信号槽的连接导致访问到已销毁的对象 for (const auto &connection : m_connections) { QObject::disconnect(connection); } } void DbusUpperInterface::init() { initData(); initConnections(); inhibit(); } void DbusUpperInterface::initData() { syslog(LOG_INFO, "[ukui-screensaver-backend] DbusUpperInterface initData begin!!"); m_config = Configuration::instance(this); m_accountsHelper = QSharedPointer(new AccountsHelper(this)); m_lightDmHelper = QSharedPointer(new LightDMHelper(m_accountsHelper, m_config, this)); m_login1Helper = QSharedPointer(new Login1Helper(this)); m_gsettingsHelper = QSharedPointer(new GSettingsHelper(this)); #ifdef ENABLE_BIO_BACKEND m_bioAuth = QSharedPointer(new BioAuthenticate(this)); #endif m_pamAuth = QSharedPointer(new PamAuthenticate(m_lightDmHelper, this)); m_usdHelper = QSharedPointer(new UsdHelper(this)); m_upowerHelper = QSharedPointer(new UpowerHelper(this)); m_sessionHelper = QSharedPointer(new SessionHelper(m_login1Helper, this)); m_systemsUpgradeHelper = QSharedPointer(new SystemUpgradeHelper(this)); if (!isGreeterMode()) { m_sessionWatcher = QSharedPointer(new SessionWatcher(m_gsettingsHelper, this)); } m_kglobalHelper = QSharedPointer(new KglobalAccelHelper(this)); #ifdef SUPPORT_TABLET_MODE m_libinputSwitchEvent = QSharedPointer(new LibinputSwitchEvent(this)); #endif m_networkManagerHelper = QSharedPointer(new NetWorkManagerHelper(this)); m_fileManagerHelper = QSharedPointer(new FileManagerHelper(this)); if (!isGreeterMode()) { m_config->initShareConfig(); m_config->setShareConfigValue("timeType", m_gsettingsHelper->GetUkccPluginsConf(KEY_HOUR_SYSTEM).toInt()); m_config->setShareConfigValue("dateType", m_gsettingsHelper->GetUkccPluginsConf(KEY_DATE).toString()); m_config->setShareConfigValue( "fontSize", m_gsettingsHelper->GetThemeStyleConf(KEY_SYSTEM_FONT_SIZE).toDouble()); m_config->setShareConfigValue("themeColor", m_gsettingsHelper->GetThemeStyleConf(KEY_THEME_COLOR).toString()); } syslog(LOG_INFO, "[ukui-screensaver-backend] DbusUpperInterface initData end!!"); } void DbusUpperInterface::initConnections() { if (m_lightDmHelper) { m_connections.append(connect( m_lightDmHelper.get(), &LightDMHelper::usersInfoChanged, this, &DbusUpperInterface::onUsersInfoChanged)); m_connections.append(connect( m_lightDmHelper.get(), &LightDMHelper::currentSessionChanged, this, &DbusUpperInterface::onCurrentSessionChanged)); } if (m_login1Helper) { m_connections.append( connect(m_login1Helper.get(), &Login1Helper::requestLock, this, &DbusUpperInterface::onLogin1ReqLock)); m_connections.append( connect(m_login1Helper.get(), &Login1Helper::requestUnlock, this, &DbusUpperInterface::onLogin1ReqUnLock)); m_connections.append(connect( m_login1Helper.get(), &Login1Helper::PrepareForSleep, this, &DbusUpperInterface::onLogin1PrepareForSleep)); m_connections.append(connect( m_login1Helper.get(), &Login1Helper::sessionActiveChanged, this, &DbusUpperInterface::onLogin1SessionActiveChanged)); m_connections.append(connect( m_login1Helper.get(), &Login1Helper::blockInhibitedChanged, this, &DbusUpperInterface::onBlockInhibitedChanged)); } if (m_gsettingsHelper) { m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::lockScreenConfigChanged, this, &DbusUpperInterface::onLockScreenConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::screenSaverConfigChanged, this, &DbusUpperInterface::onScreenSaverConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::powerManagerConfigChanged, this, &DbusUpperInterface::onPowerManagerConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::mateBgConfigChanged, this, &DbusUpperInterface::onMateBgConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::ukccPluginsConfigChanged, this, &DbusUpperInterface::onUkccPluginsConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::themeStyleConfigChanged, this, &DbusUpperInterface::onThemeStyleConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::sessionConfigChanged, this, &DbusUpperInterface::onSessionConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::keyboardConfigChanged, this, &DbusUpperInterface::onKeyboardConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::usdMediaKeysConfigChanged, this, &DbusUpperInterface::onUsdMediaKeysConfigChanged)); m_connections.append(connect( m_gsettingsHelper.get(), &GSettingsHelper::usdMediaStateKeysConfigChanged, this, &DbusUpperInterface::onUsdMediaStateKeysConfigChanged)); } #ifdef ENABLE_BIO_BACKEND if (m_bioAuth) { m_connections.append(connect( m_bioAuth.get(), &BioAuthenticate::bioAuthShowMessage, this, &DbusUpperInterface::onBioAuthShowMessage)); m_connections.append(connect( m_bioAuth.get(), &BioAuthenticate::bioServiceStatusChanged, this, &DbusUpperInterface::onBioServiceStatusChanged)); m_connections.append(connect( m_bioAuth.get(), &BioAuthenticate::bioDeviceChanged, this, &DbusUpperInterface::onBioDeviceChanged)); m_connections.append(connect( m_bioAuth.get(), &BioAuthenticate::bioAuthStateChanged, this, &DbusUpperInterface::onBioAuthStateChanged)); m_connections.append(connect( m_bioAuth.get(), &BioAuthenticate::bioAuthFrameData, this, &DbusUpperInterface::onBioAuthFrameData)); m_connections.append(connect( m_bioAuth.get(), &BioAuthenticate::bioAuthCompleted, this, &DbusUpperInterface::onBioAuthCompleted)); if (m_pamAuth) { m_connections.append(connect( m_pamAuth.get(), &PamAuthenticate::authenticationComplete, m_bioAuth.get(), &BioAuthenticate::onPamAuthComplete)); } } #endif if (m_pamAuth) { m_connections.append( connect(m_pamAuth.get(), &PamAuthenticate::showMessage, this, &DbusUpperInterface::onPamShowMessage)); m_connections.append( connect(m_pamAuth.get(), &PamAuthenticate::showPrompt, this, &DbusUpperInterface::onPamShowPrompt)); m_connections.append(connect( m_pamAuth.get(), &PamAuthenticate::showBinaryPrompt, this, &DbusUpperInterface::onPamShowBinaryPrompt)); m_connections.append(connect( m_pamAuth.get(), &PamAuthenticate::authenticationComplete, this, &DbusUpperInterface::onPamAuthCompleted)); } if (m_upowerHelper) { m_connections.append(connect( m_upowerHelper.get(), &UpowerHelper::upowerServiceReady, this, &DbusUpperInterface::onUPowerServiceReady)); m_connections.append(connect( m_upowerHelper.get(), &UpowerHelper::batteryStatusChanged, this, &DbusUpperInterface::onBatteryStatusChanged)); m_connections.append( connect(m_upowerHelper.get(), &UpowerHelper::batteryChanged, this, &DbusUpperInterface::onBatteryChanged)); m_connections.append(connect( m_upowerHelper.get(), &UpowerHelper::lidStateChanged, this, &DbusUpperInterface::onLidStateChanged)); } if (m_sessionWatcher) { m_connections.append(connect( m_sessionWatcher.get(), &SessionWatcher::sessionIdle, this, &DbusUpperInterface::onSessionIdleReceived)); m_connections.append(connect(m_sessionWatcher.get(), &SessionWatcher::sessionIdle, this, [=]() { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "SessionIdle"; QDBusConnection::sessionBus().send(message); })); m_connections.append(connect( m_sessionWatcher.get(), &SessionWatcher::sessionIdleExit, this, &DbusUpperInterface::onSessionIdleExit)); } if (m_sessionHelper) { m_connections.append(connect( m_sessionHelper.get(), &SessionHelper::blockLogoutInhibitedChanged, this, &DbusUpperInterface::onBlockLogoutInhibitedChanged)); } m_connections.append(connect( &m_procLockDialog, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onLockDialogProcExit(int, QProcess::ExitStatus)))); #ifdef SUPPORT_TABLET_MODE if (m_libinputSwitchEvent) { m_connections.append(connect( m_libinputSwitchEvent.get(), &LibinputSwitchEvent::tabletModeStatusChanged, this, &DbusUpperInterface::onTabletModeChanged)); } #endif m_connections.append(connect( GreeterConfig::instance(), &GreeterConfig::screensaverDatetimeChanged, this, &DbusUpperInterface::onScreensaverDatetimeChanged)); m_connections.append(connect( GreeterConfig::instance(), &GreeterConfig::lockScreenDatetimeEnableChanged, this, &DbusUpperInterface::onScreenlockDatetimeChanged)); } void DbusUpperInterface::onNameLost(const QString &serviceName) { if (serviceName == "cn.kylinos.ScreenSaver") exit(0); } void DbusUpperInterface::GetInitData(QJsonObject &reqObj, QJsonObject &retObj) { Q_UNUSED(reqObj); retObj["UsersInfo"] = GenerateUserInfoList(); retObj["SessionsInfo"] = GenerateSessionInfoList(); retObj["IsSessionActive"] = m_login1Helper->isSessionActive(); retObj["DefAuthUser"] = GetDefaultAuthUser(); retObj["CurUserName"] = GetCurrentUser(); retObj["CurSession"] = GetCurrentSession(); retObj["AgreementInfo"] = GenerateAgreementInfo(); retObj["CapsLock"] = m_gsettingsHelper->GetKeyboardConf(KEY_CAPSLOCK_STATUS).toBool(); retObj["MediaKeyAreaScreenShot"] = m_gsettingsHelper->GetUsdMediaKeys(KEY_AREA_SCREENSHOT).toString(); retObj["MediaKeyAreaScreenShot2"] = m_gsettingsHelper->GetUsdMediaKeys(KEY_AREA_SCREENSHOT2).toString(); retObj["MediaKeyScreenShot"] = m_gsettingsHelper->GetUsdMediaKeys(KEY_SCREEN_SHOT).toString(); retObj["MediaKeyScreenShot2"] = m_gsettingsHelper->GetUsdMediaKeys(KEY_SCREEN_SHOT2).toString(); retObj["MediaKeyWinScreenShot"] = m_gsettingsHelper->GetUsdMediaKeys(KEY_WINDOW_SCREENSHOT).toString(); retObj["MediaKeyRfkillState"] = m_gsettingsHelper->GetUsdMediaKeys(KEY_RFKILL_STATE).toInt(); retObj["PowerButton"] = m_gsettingsHelper->GetPowerManagerConf(KEY_BUTTON_POWER).toString(); retObj["ScreenSaverMode"] = m_gsettingsHelper->GetLockScreenConf(KEY_MODE).toString(); retObj["ScreenSaverThemes"] = getSaverTheme(); retObj["ImageTransitionEffect"] = m_gsettingsHelper->GetLockScreenConf(KEY_IMAGE_TRANSITION_EFFECT).toInt(); retObj["ImageSwitchInterval"] = m_gsettingsHelper->GetLockScreenConf(KEY_IMAGE_SWITCH_INTERVAL).toInt(); retObj["BatteryArgs"] = GenerateBatteryArgsList(); retObj["BatteryIconName"] = getBatteryIconName(); retObj["IsBattery"] = getIsBattery(); retObj["SleepLockCheck"] = getSleepLockCheck(); retObj["ShutdownLockCheck"] = getShutdownLockcheck(); retObj["LogoutLockCheck"] = getLogoutLockCheck(); retObj["LockTimeout"] = m_gsettingsHelper->GetLockScreenConf(KEY_LOCK_TIMEOUT).toInt(); retObj["IdleDelay"] = m_gsettingsHelper->GetLockScreenConf(KEY_IDLE_DELAY).toInt(); retObj["LockEnable"] = m_gsettingsHelper->GetLockScreenConf(KEY_LOCK_ENABLED).toBool(); retObj["SleepActivationEnable"] = m_gsettingsHelper->GetLockScreenConf(KEY_SLEEP_ACTIVATION_ENABLED).toBool(); retObj["SystemFontSize"] = m_gsettingsHelper->GetThemeStyleConf(KEY_SYSTEM_FONT_SIZE).toInt(); retObj["SystemFont"] = m_gsettingsHelper->GetThemeStyleConf(KEY_SYSTEM_FONT).toString(); retObj["TabletMode"] = getCurTabletMode(); retObj["LogoutMusic"] = m_gsettingsHelper->GetSessionConf(KEY_SESSION_LOGOUT_MUSIC).toBool(); retObj["PoweroffMusic"] = m_gsettingsHelper->GetSessionConf(KEY_SESSION_POWEROFF_MUSIC).toBool(); retObj["LockDatetimeEnable"] = GetLockDatetimeEnable(); retObj["CanSwitchUser"] = GetCanSwitchUser(); } bool DbusUpperInterface::GetSlpState() { return ((m_procLockDialog.state() != QProcess::NotRunning) && m_bSlpState); } void DbusUpperInterface::LockStartupMode() { if (GetLockState()) return; if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "StartupLock"; QDBusConnection::sessionBus().send(message); emitLockState(true); return; } qDebug() << "LockStartupMode requested"; m_bLockState = false; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("XMODIFIERS", "@im=fcitx"); env.insert("QT_IM_MODULE", "fcitx"); m_procLockDialog.setEnvironment(env.toStringList()); QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--lock-startup"); emitLockState(true); } bool DbusUpperInterface::GetBlankState() { return m_bBlankState; } bool DbusUpperInterface::GetLockState() { if (m_procLockDialog.state() == QProcess::NotRunning && !checkScreenDialogRunning()) { return false; } else { return m_bLockState; } return false; } void DbusUpperInterface::Lock() { if (GetLockState()) return; if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "CmdLock"; QDBusConnection::sessionBus().send(message); emitLockState(true); return; } qDebug() << "Lock requested"; m_bLockState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--lock"); emitLockState(true); } void DbusUpperInterface::SwitchUser() { if (GetLockState()) return; if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "CmdSwitchUser"; QDBusConnection::sessionBus().send(message); emitLockState(true); return; } qDebug() << "SwitchUser requested"; m_bLockState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--switchuser"); emitLockState(true); } void DbusUpperInterface::SwitchToUser(QString strUserName) { if (GetLockState()) return; if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << QString("CmdSwitchToUser&%1").arg(strUserName); QDBusConnection::sessionBus().send(message); emitLockState(true); return; } qDebug() << "SwitchToUser requested:" << strUserName; m_bLockState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--switchtouser" << QString("%1").arg(strUserName)); emitLockState(true); } bool DbusUpperInterface::CheckAppVersion() { return true; } void DbusUpperInterface::SessionTools() { if (GetLockState()) return; if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "CmdSessionTools"; QDBusConnection::sessionBus().send(message); emitLockState(false, true); return; } m_bLockState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--session-tools"); emitLockState(false, true); } void DbusUpperInterface::AppBlockWindow(QString actionType, bool isHardInhibit) { QStringList lockcheck; if (actionType == "PowerOff" || actionType == "Reboot" || actionType == "Logout") { if (isHardInhibit) { lockcheck = m_sessionHelper->getLockCheckStatus("shutdown"); } else { lockcheck = m_sessionHelper->getLockCheckStatus("logout"); } } else if (actionType == "Suspend" || actionType == "Hibernate") { lockcheck = m_sessionHelper->getLockCheckStatus("sleep"); } qDebug() << "lockCheck = " << lockcheck; if (lockcheck.isEmpty()) { return; } if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); if (isHardInhibit) { if (actionType == "Reboot") message << "CmdRestartAppBlock"; else if (actionType == "PowerOff") message << "CmdPowerOffAppBlock"; else if (actionType == "Suspend") message << "CmdSuspendAppBlock"; else if (actionType == "Hibernate") message << "CmdHibernateAppBlock"; else if (actionType == "Logout") message << "CmdLogoutAppBlock"; else return; } else { if (actionType == "Reboot") message << "CmdRestartAppSoftBlock"; else if (actionType == "PowerOff") message << "CmdPowerOffAppSoftBlock"; else if (actionType == "Logout") message << "CmdLogoutAppSoftBlock"; else return; } QDBusConnection::sessionBus().send(message); emitLockState(false, true); return; } if (actionType != "Reboot" && actionType != "PowerOff" && actionType != "Suspend" && actionType != "Hibernate" && actionType != "Logout") return; m_bLockState = false; QString cmd; cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--app-block" << QString("%1").arg(actionType)); emitLockState(false, true); } void DbusUpperInterface::MultiUserBlockWindow(QString actionType) { if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); if (actionType == "Reboot") message << "CmdRestartMulTiUserBlock"; else if (actionType == "PowerOff") message << "CmdPowerOffMulTiUserBlock"; else return; QDBusConnection::sessionBus().send(message); emitLockState(false, true); return; } if (actionType != "Reboot" && actionType != "PowerOff") return; m_bLockState = false; QString cmd; cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--multiusers-block" << QString("%1").arg(actionType)); emitLockState(false, true); } void DbusUpperInterface::Suspend() { if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("sleep"); if (lockCheckList.count() == 0) { doPrepareForSleep(true, "Suspend"); } else { AppBlockWindow("Suspend"); } } } void DbusUpperInterface::Hibernate() { if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("sleep"); if (lockCheckList.count() == 0) { doPrepareForSleep(true, "Hibernate"); } else { AppBlockWindow("Hibernate"); } } } void DbusUpperInterface::Reboot() { if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("shutdown"); if (lockCheckList.count() == 0) { QStringList logoutLockCheckList = m_sessionHelper->getLockCheckStatus("logout"); if (logoutLockCheckList.count() == 0) { if (m_lightDmHelper && m_lightDmHelper->getLoginUserCount() > 1) { MultiUserBlockWindow("Reboot"); } else { m_sessionHelper->doAction("Reboot"); } } else { AppBlockWindow("Logout", false); } } else { AppBlockWindow("Reboot"); } } } void DbusUpperInterface::PowerOff() { if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("shutdown"); if (lockCheckList.count() == 0) { QStringList logoutLockCheckList = m_sessionHelper->getLockCheckStatus("logout"); if (logoutLockCheckList.count() == 0) { if (m_lightDmHelper && m_lightDmHelper->getLoginUserCount() > 1) { MultiUserBlockWindow("PowerOff"); } else { m_sessionHelper->doAction("PowerOff"); } } else { AppBlockWindow("Logout", false); } } else { AppBlockWindow("PowerOff"); } } } void DbusUpperInterface::Logout() { if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("shutdown"); if (lockCheckList.count() == 0) { QStringList logoutLockCheckList = m_sessionHelper->getLockCheckStatus("logout"); if (logoutLockCheckList.count() == 0) { m_sessionHelper->doAction("Logout"); } else { AppBlockWindow("Logout", false); } } else { AppBlockWindow("Logout"); } } } void DbusUpperInterface::UnLock() { qDebug() << "manual unlcok!!"; if (m_login1Helper) { m_login1Helper->Unlock(); } else { if (m_procLockDialog.state() != QProcess::NotRunning) m_procLockDialog.kill(); } } bool DbusUpperInterface::LockByBlank(int nType) { qDebug() << "LockByBlank:" << nType; bool isNeedLock = true; if (!checkStatus(SESSION_STATUS_SCREENCLOSE)) { qDebug() << "m_nStatus:" << m_nStatus << " can't do screenclose lock!"; if (nType == 0) { int closeDisplayTime = m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_DISPLAY_AC).toInt(); int nIdleDelay = m_gsettingsHelper->GetScreenSaverConf(KEY_IDLE_DELAY).toInt(); bool isCloseActive = m_gsettingsHelper->GetScreenSaverConf(KEY_CLOSE_ACTIVATION_ENABLED).toBool(); qDebug() << "DelayState:" << closeDisplayTime << nIdleDelay << isCloseActive; if (-1 == closeDisplayTime || !isCloseActive) return false; if (-1 != nIdleDelay && closeDisplayTime == nIdleDelay * 60) { // 屏保与关屏时间一致 if (!m_gsettingsHelper->GetScreenSaverConf(KEY_LOCK_ENABLED).toBool()) { // 仅启动屏保 isNeedLock = false; } } else { return false; } } else { qDebug() << "Don't need blank lock:" << nType; return false; } } if (isNeedLock) { qDebug() << "LockByBlank ---"; m_bLockState = false; m_bSlpState = false; if (m_gsettingsHelper->GetLockScreenConf(KEY_LOCK_TIMEOUT).toInt() != -1 && isNeedLock) { // 空闲关屏且关屏时间与睡眠时间一致时直接锁定 if (nType == 0 && ((m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_DISPLAY_AC).toInt() == m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_COMPUTER_AC).toInt()) && m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_COMPUTER_AC).toInt() != -1)) { qDebug() << "close delay same with sleep delay then lock!"; if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); QString strParam = QString("CloseLock&delay=%1&hasLock=%2").arg(1000).arg(isNeedLock ? 1 : 0); message << strParam; QDBusConnection::sessionBus().send(message); } else { QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--blank" << "--delay" << QString("%1").arg(1000) << "--has-lock" << QString("%1").arg(isNeedLock ? 1 : 0)); } emitLockState(true); } else { delayLockScreen(); } } else { if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); QString strParam = QString("CloseLock&delay=%1&hasLock=%2").arg(2000).arg(isNeedLock ? 1 : 0); message << strParam; QDBusConnection::sessionBus().send(message); } else { QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--blank" << "--delay" << QString("%1").arg(2000) << "--has-lock" << QString("%1").arg(isNeedLock ? 1 : 0)); } emitLockState(true); } return true; } else { return false; } } void DbusUpperInterface::SetLockState() { m_bLockState = true; } void DbusUpperInterface::ClearLockState() { m_bLockState = false; } void DbusUpperInterface::onShowBlankScreensaver() { if (GetLockState()) return; if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "SleepLock"; QDBusConnection::sessionBus().send(message); return; } qDebug() << "lock and show screensaver"; m_bLockState = false; m_bSlpState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--blank"); emitLockState(true); } void DbusUpperInterface::ShowScreensaver() { if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { if (!m_bLockState) { emitLockState(true); } QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "Screensaver"; QDBusConnection::sessionBus().send(message); return; } qDebug() << "only show screensaver"; m_bLockState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--screensaver"); emitLockState(true); } void DbusUpperInterface::LockScreensaver() { if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { if (!m_bLockState) { emitLockState(true); } QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "ScreensaverLock"; QDBusConnection::sessionBus().send(message); return; } m_bSlpState = true; qDebug() << "lock and show screensaver"; m_bLockState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--lock-screensaver"); emitLockState(true); } bool DbusUpperInterface::checkScreenDialogRunning() { bool isRunning = false; QString strDisplay = ""; if (QString(qgetenv("XDG_SESSION_TYPE")) == "wayland") { strDisplay = QLatin1String(getenv("WAYLAND_DISPLAY")); } else { strDisplay = QLatin1String(getenv("DISPLAY")); } QString id = QString("ukui-screensaver-dialog" + strDisplay); QtLocalPeer *lPeer = new QtLocalPeer(this, id); isRunning = lPeer->isClient(); delete lPeer; return isRunning; } void DbusUpperInterface::emitLockState(bool val, bool isSessionTools) { qDebug() << "emitLockState state = " << val << isSessionTools; if (val != m_bBlockShortcutState && m_kglobalHelper) { m_kglobalHelper->blockShortcut(val); m_bBlockShortcutState = val; qDebug() << " block all shortcut " << "blockShortcut states = " << m_bBlockShortcutState; } if (!isSessionTools) { static bool s_isLocked = false; QDBusMessage message; if (val && !s_isLocked) { message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "lock"); QDBusConnection::sessionBus().send(message); s_isLocked = true; } else if (!val && s_isLocked) { message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "unlock"); QDBusConnection::sessionBus().send(message); s_isLocked = false; } } } QString DbusUpperInterface::GetInformation(QString strJson) { QJsonParseError jsonParseError; QJsonObject retObj; retObj["Ret"] = 0; retObj["Message"] = "Success"; qDebug() << "Json:" << strJson; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strJson.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { retObj["Ret"] = -1; retObj["Message"] = "Parse Request json failed!!"; qWarning() << "Parse request json failed!!"; } else { QJsonObject rootObj = jsonDoc.object(); if (rootObj.isEmpty()) { retObj["Ret"] = -1; retObj["Message"] = "Request Json is null!!"; qWarning() << "Request Json is null!!"; } else { int cmdId = rootObj.value("CmdId").toInt(); qInfo() << "CmdId: " << cmdId; switch (cmdId) { case LOCK_CMD_ID_GET_USERINFO_LIST: retObj["CmdId"] = cmdId; retObj["Content"] = GenerateUserInfoList(); break; case LOCK_CMD_ID_GET_DEFAULT_AUTH_USER: retObj["CmdId"] = cmdId; retObj["Content"] = GetDefaultAuthUser(); break; case LOCK_CMD_ID_GET_CURRENT_USER: retObj["CmdId"] = cmdId; retObj["Content"] = GetCurrentUser(); break; case LOCK_CMD_ID_GET_SESSIONS_LIST: retObj["CmdId"] = cmdId; retObj["Content"] = GenerateSessionInfoList(); break; case LOCK_CMD_ID_GET_CURRENT_SESSION: retObj["CmdId"] = cmdId; retObj["Content"] = GetCurrentSession(); break; case LOCK_CMD_ID_LOGIN1_IS_SESSION_ACTIVE: retObj["CmdId"] = cmdId; retObj["Content"] = m_login1Helper->isSessionActive(); break; case LOCK_CMD_ID_GET_AGREEMENT: retObj["CmdId"] = cmdId; retObj["Content"] = GenerateAgreementInfo(); break; case LOCK_CMD_ID_GSETTINGS_GET_LOCKSCREEN_CONF: retObj["CmdId"] = cmdId; GetLockScreenConf(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_SCREENSAVER_CONF: retObj["CmdId"] = cmdId; GetScreenSaverConf(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_DEFAULT_PHOTOPATH: retObj["CmdId"] = cmdId; retObj["Content"] = getSaverPhotoPath(KEY_PHOTO_DEFAULT_PATH); break; case LOCK_CMD_ID_GSETTINGS_GET_CUSTOM_PHOTOPATH: retObj["CmdId"] = cmdId; retObj["Content"] = getSaverPhotoPath(KEY_PHOTO_CUSTOM_PATH); break; case LOCK_CMD_ID_GSETTINGS_GET_POWERMANAGER_CONF: retObj["CmdId"] = cmdId; GetPowerManagerConf(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_MATEBG_CONF: retObj["CmdId"] = cmdId; GetMateBgConf(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_UKCCPLUGINS_CONF: retObj["CmdId"] = cmdId; GetUkccPluginsConf(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_THEMESTYLE_CONF: retObj["CmdId"] = cmdId; GetThemeStyleConf(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_SESSION_CONF: retObj["CmdId"] = cmdId; GetSessionConf(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_KEYBOARD_CONF: retObj["CmdId"] = cmdId; GetKeyboardConf(rootObj, retObj); break; case LOCK_CMD_ID_PAMAUTH_IS_AUTHENTICATED: retObj["CmdId"] = cmdId; IsPamAuthenticated(rootObj, retObj); break; case LOCK_CMD_ID_PAMAUTH_IS_INAUTHTICATION: retObj["CmdId"] = cmdId; IsPamInAuthentication(rootObj, retObj); break; case LOCK_CMD_ID_PAMAUTH_GET_AUTHUSER: retObj["CmdId"] = cmdId; PamAuthenticationUser(rootObj, retObj); break; case LOCK_CMD_ID_PAMAUTH_GET_AUTOLOGINUSERHINT: retObj["CmdId"] = cmdId; GetAutologinUserHint(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIAKEYS_CONF: retObj["CmdId"] = cmdId; GetUsdMediaKeys(rootObj, retObj); break; case LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIA_STATE_KEYS_CONF: retObj["CmdId"] = cmdId; GetUsdMediaStateKeys(rootObj, retObj); break; case LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANHIBERNATE: retObj["CmdId"] = cmdId; if (m_login1Helper) retObj["Content"] = m_login1Helper->isCanHibernate(); break; case LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANPOWEROFF: retObj["CmdId"] = cmdId; if (m_login1Helper) retObj["Content"] = m_login1Helper->isCanPowerOff(); break; case LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANREBOOT: retObj["CmdId"] = cmdId; if (m_login1Helper) retObj["Content"] = m_login1Helper->isCanReboot(); break; case LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSUSPEND: retObj["CmdId"] = cmdId; if (m_login1Helper) retObj["Content"] = m_login1Helper->isCanSuspend(); break; case LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOGOUT: retObj["CmdId"] = cmdId; if (m_sessionHelper) retObj["Content"] = m_sessionHelper->canAction(PowerLogout); break; case LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOCKSCREEN: retObj["CmdId"] = cmdId; if (m_sessionHelper) retObj["Content"] = m_sessionHelper->canAction(PowerMonitorOff); break; case LOCK_CMD_ID_SYSTEM_UPGRADE_CHECK: retObj["CmdId"] = cmdId; if (m_systemsUpgradeHelper) retObj["Content"] = m_systemsUpgradeHelper->checkSystemUpgrade(); break; case LOCK_CMD_ID_UPOWER_BATTERY_STATUS: retObj["CmdId"] = cmdId; retObj["Content"] = getBatteryIconName(); break; case LOCK_CMD_ID_UPOWER_IS_BATTERY: retObj["CmdId"] = cmdId; retObj["Content"] = getIsBattery(); break; case LOCK_CMD_ID_UPOWER_BATTERY: retObj["CmdId"] = cmdId; retObj["Content"] = GenerateBatteryArgsList(); break; case LOCK_CMD_ID_SESSION_GET_LOGOUT_LOCKCHECK: retObj["CmdId"] = cmdId; retObj["Content"] = getLogoutLockCheck(); break; case LOCK_CMD_ID_SESSION_GET_SLEEP_LOCKCHECK: retObj["CmdId"] = cmdId; retObj["Content"] = getSleepLockCheck(); break; case LOCK_CMD_ID_SESSION_GET_SHUTDOWN_LOCKCHECK: retObj["CmdId"] = cmdId; retObj["Content"] = getShutdownLockcheck(); break; case LOCK_CMD_ID_LOCK_SCREEN_GET_THEMES: retObj["CmdId"] = cmdId; retObj["Content"] = getSaverTheme(); break; case LOCK_CMD_ID_BIOAUTH_GET_AVAILABLE_DEVICES: retObj["CmdId"] = cmdId; BioGetAvailableDevices(rootObj, retObj); break; case LOCK_CMD_ID_BIOAUTH_GET_DISABLED_DEVICES: retObj["CmdId"] = cmdId; BioGetDisabledDevices(rootObj, retObj); break; case LOCK_CMD_ID_BIOAUTH_GET_STATE: retObj["CmdId"] = cmdId; BioGetBioAuthState(rootObj, retObj); break; case LOCK_CMD_ID_BIOAUTH_GET_CURDEVICE: retObj["CmdId"] = cmdId; BioGetCurBioInfo(rootObj, retObj); break; case LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_ID: retObj["CmdId"] = cmdId; BioFindDeviceById(rootObj, retObj); break; case LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_NAME: retObj["CmdId"] = cmdId; BioFindDeviceByName(rootObj, retObj); break; case LOCK_CMD_ID_BIOAUTH_GET_DEFAULT_DEVICE: retObj["CmdId"] = cmdId; BioGetDefaultDevice(rootObj, retObj); break; case LOCK_CMD_ID_GET_PUBLIC_KEY: retObj["CmdId"] = cmdId; retObj["Content"] = getPublicEncrypt(); break; case LOCK_CMD_ID_TABLET_MODE: retObj["CmdId"] = cmdId; retObj["Content"] = getCurTabletMode(); break; case LOCK_CMD_ID_NETWORK_CONNECT_STATE: retObj["CmdId"] = cmdId; retObj["Content"] = getNetworkConnectState(); break; case LOCK_CMD_ID_GET_WEATHER_DATA: retObj["CmdId"] = cmdId; GetWeatherKeys(rootObj, retObj); break; case LOCK_CMD_ID_GET_INIT_DATA: retObj["CmdId"] = cmdId; GetInitData(rootObj, retObj); break; case LOCK_CMD_ID_GET_SAVER_DATETIME_ENABLE_DATA: retObj["CmdId"] = cmdId; retObj["Content"] = GetSaverDatetimeEnable(); break; case LOCK_CMD_ID_GET_LOCK_DATETIME_ENABLE_DATA: retObj["CmdId"] = cmdId; retObj["Content"] = GetLockDatetimeEnable(); break; case LOCK_CMD_ID_CAN_SWITCH_USER: retObj["CmdId"] = cmdId; retObj["Content"] = GetCanSwitchUser(); break; default: qInfo() << "不支持的CmdId"; retObj["Ret"] = -2; retObj["Message"] = "Unsupported CmdId!!"; break; } } } return QString(QJsonDocument(retObj).toJson()); } int DbusUpperInterface::SetInformation(QString strJson) { QJsonParseError jsonParseError; int nRet = 0; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strJson.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qWarning() << "Parse info json failed!!"; nRet = -1; } else { QJsonObject rootObj = jsonDoc.object(); if (rootObj.isEmpty()) { qWarning() << "Info Json is null!!"; nRet = -1; } else { int cmdId = rootObj.value("CmdId").toInt(); qInfo() << "CmdId: " << cmdId; switch (cmdId) { case LOCK_CMD_ID_SET_USER: nRet = SetCurrentUser(rootObj); break; case LOCK_CMD_ID_SWITCH_TO_USER: nRet = switchToUser(rootObj); break; case LOCK_CMD_ID_SET_SESSION: nRet = SetCurrentSession(rootObj); break; case LOCK_CMD_ID_START_SESSION: nRet = StartSession(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_LOCKSCREEN_CONF: nRet = SetLockScreenConf(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_SCREENSAVER_CONF: nRet = SetScreenSaverConf(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_POWERMANAGER_CONF: nRet = SetPowerManagerConf(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_MATEBG_CONF: nRet = SetMateBgConf(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_UKCCPLUGINS_CONF: nRet = SetUkccPluginsConf(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_THEMESTYLE_CONF: nRet = SetThemeStyleConf(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_SESSION_CONF: nRet = SetSessionConf(rootObj); break; case LOCK_CMD_ID_PAMAUTH_AUTHENTICATE: nRet = PamAuthenticateUser(rootObj); break; case LOCK_CMD_ID_PAMAUTH_RESPOND: nRet = PamRespond(rootObj); break; case LOCK_CMD_ID_PAMAUTH_AUTHENTICATE_CANCEL: nRet = PamAuthenticateCancel(rootObj); break; case LOCK_CMD_ID_USD_MEDIAKEYS: nRet = usdExternalDoAction(rootObj); break; case LOCK_CMD_ID_LOGIN1_SET_POWER_MANAGER: nRet = SetPowerManager(rootObj); break; case LOCK_CMD_ID_LOCK_STATE_CHANGED: nRet = lockStateChanged(rootObj); break; case LOCK_CMD_ID_BIOAUTH_STARTAUTH: nRet = BioStartAuth(rootObj); break; case LOCK_CMD_ID_BIOAUTH_STOPAUTH: nRet = BioStopAuth(rootObj); break; case LOCK_CMD_ID_KWIN_BLOCK_SHORTCUT: nRet = blockShortcut(rootObj); break; case LOCK_CMD_ID_GSETTINGS_SET_USD_MEDIA_STATE_KEYS_CONF: nRet = SetUsdMediaStateKeys(rootObj); break; case LOCK_CMD_ID_SET_SAVER_DATETIME_ENABLE_DATA: SetSaverDatetimeEnable(rootObj.value("Content").toBool()); break; case LOCK_CMD_ID_SET_LOCK_DATETIME_ENABLE_DATA: SetLockDatetimeEnable(rootObj.value("Content").toBool()); break; default: qInfo() << "不支持的CmdId"; nRet = -2; break; } } } return nRet; } QJsonObject DbusUpperInterface::GenerateAgreementInfo() { QSettings settings(CONFIG_FILE, QSettings::IniFormat); QJsonObject objAgreementInfo; if (settings.contains("Greeter/showLoginPrompt")) { objAgreementInfo["showLoginPrompt"] = settings.value("Greeter/showLoginPrompt").toBool(); } if (settings.contains("Greeter/hideTitle")) { objAgreementInfo["hideTitle"] = settings.value("Greeter/hideTitle").toBool(); } if (settings.contains("Greeter/promptTitle")) { QString promptTitle = settings.value("Greeter/promptTitle").toString(); objAgreementInfo["promptTitle"] = promptTitle; } if (settings.contains("Greeter/promptText")) { QString promptText = settings.value("Greeter/promptText").toString(); objAgreementInfo["promptText"] = promptText; } if (settings.contains("Greeter/promptTextFilePath")) { QString promptTextFilePath = settings.value("Greeter/promptTextFilePath").toString(); objAgreementInfo["promptTextFilePath"] = promptTextFilePath; } return objAgreementInfo; } QJsonArray DbusUpperInterface::GenerateUserInfoList() { QJsonArray jsonArray; if (m_lightDmHelper) { QList usersInfo = m_lightDmHelper->getUsersInfo(); for (auto userInfo : usersInfo) { QJsonObject objUserInfo; objUserInfo["uid"] = (double)userInfo->uid(); objUserInfo["name"] = userInfo->name(); objUserInfo["fullName"] = userInfo->fullName(); objUserInfo["loggedIn"] = userInfo->isLoggedIn(); objUserInfo["backGround"] = userInfo->backGround(); objUserInfo["lang"] = userInfo->lang(); objUserInfo["headImg"] = userInfo->headImage(); objUserInfo["systemAccount"] = userInfo->isSystemAccount(); KylinUserDatePtr ptr = KYLINUSERDATAMNG::instance()->GetUserPtr(userInfo->name()); if (nullptr != ptr) { if (!ptr->backgroundPath().isEmpty()) { objUserInfo["greeterBackGround"] = ptr->backgroundPath(); } objUserInfo["dateType"] = ptr->dateType(); objUserInfo["fontSize"] = ptr->fontSize(); objUserInfo["timeType"] = ptr->timeType(); objUserInfo["color"] = ptr->color(); } if (isCurUserSelf(userInfo->name()) && m_gsettingsHelper) { QString strBackground = m_gsettingsHelper->GetLockScreenConf(KEY_BACKGROUND).toString(); if (!strBackground.isEmpty() && QFile(strBackground).exists()) { objUserInfo["greeterBackGround"] = strBackground; } } jsonArray.append(objUserInfo); } } return jsonArray; } int DbusUpperInterface::GetLoggedInUsersCount() { int loggedinUsersCount = 0; if (m_lightDmHelper) { QList usersInfo = m_lightDmHelper->getUsersInfo(); if (!usersInfo.isEmpty()) { for (auto user : usersInfo) { if (user->isLoggedIn()) { loggedinUsersCount++; } } } } return loggedinUsersCount; } QJsonArray DbusUpperInterface::GenerateBatteryArgsList() { QJsonArray jsonArray; if (m_upowerHelper) { QStringList batteryArgs = m_upowerHelper->getBatteryArgs(); for (auto battery : batteryArgs) { QJsonObject objBatteryInfo; objBatteryInfo["key"] = battery; jsonArray.append(objBatteryInfo); } } return jsonArray; } QJsonArray DbusUpperInterface::getSleepLockCheck() { QJsonArray jsonArray; if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("sleep"); for (auto lockcheck : lockCheckList) { QJsonObject objBatteryInfo; objBatteryInfo["key"] = lockcheck; jsonArray.append(objBatteryInfo); } } return jsonArray; } QJsonArray DbusUpperInterface::getLogoutLockCheck() { QJsonArray jsonArray; if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("logout"); for (auto lockcheck : lockCheckList) { QJsonObject objBatteryInfo; objBatteryInfo["key"] = lockcheck; jsonArray.append(objBatteryInfo); } } return jsonArray; } QJsonArray DbusUpperInterface::getShutdownLockcheck() { QJsonArray jsonArray; if (m_sessionHelper) { QStringList lockCheckList = m_sessionHelper->getLockCheckStatus("shutdown"); for (auto lockcheck : lockCheckList) { QJsonObject objBatteryInfo; objBatteryInfo["key"] = lockcheck; jsonArray.append(objBatteryInfo); } } return jsonArray; } QJsonArray DbusUpperInterface::getSaverTheme() { QJsonArray jsonArray; if (m_gsettingsHelper) { QStringList themeList = m_gsettingsHelper->GetLockScreenConf(KEY_THEMES).toStringList(); for (auto list : themeList) { QJsonObject objBatteryInfo; objBatteryInfo["key"] = list; jsonArray.append(objBatteryInfo); } } return jsonArray; } QString DbusUpperInterface::getBatteryIconName() { QString iconName; if (m_upowerHelper) { iconName = m_upowerHelper->getBatteryIconName(); } return iconName; } bool DbusUpperInterface::getIsBattery() { bool isBattery = false; if (m_upowerHelper) { isBattery = m_upowerHelper->getIsBattery(); } return isBattery; } QString DbusUpperInterface::GetDefaultAuthUser() { QString strUserName = ""; if (!isGreeterMode()) { strUserName = getenv("USER"); } else { // 获取lightdm传过来的被选中的用户 WillLoginUserInfo willSwitchUser = UniAuthService::instance()->getWillSwitchUser(); bool selectManualLogin = GreeterConfig::instance()->getSelectManualLoginDefault(); QString selectedUser = ""; if (willSwitchUser.strUserName.isEmpty() || !UniAuthService::instance()->isUserNameValid(willSwitchUser.strUserName)) { qDebug() << "WillSwitchUser is NULL or invalid:" << willSwitchUser.strUserName; selectedUser = m_lightDmHelper->selectUserHint(); } UniAuthService::instance()->SwitchToGreeterUser(""); if (!willSwitchUser.strUserName.isEmpty()) { strUserName = willSwitchUser.strUserName; qDebug() << "WillSwitchUser:" << strUserName; } else if (!selectedUser.isEmpty()) { qDebug() << "SelectUserHint: " << selectedUser << selectManualLogin; if (!selectManualLogin) { if (m_lightDmHelper->findUserByName(selectedUser)) { strUserName = selectedUser; } else { qDebug() << "SlelectUerHint invalid:" << selectedUser; } } } if (!strUserName.isEmpty()) { // do nothing } else if (selectManualLogin && m_lightDmHelper->findUserByName("*login")) { qDebug() << "Select Manual login"; strUserName = "*login"; } else if (m_lightDmHelper->selectGuestHint()) { qDebug() << "SelectGuest"; strUserName = "*guest"; } else if (m_lightDmHelper->getUsersInfo().count() == 1) { // 如果只有一个用户,默认选中 strUserName = m_lightDmHelper->getUsersInfo()[0]->name(); } else { QString lastLoginUser = UniAuthService::instance()->GetLastLoginUser(); if (m_lightDmHelper->findUserByName(lastLoginUser)) { strUserName = lastLoginUser; } else { if (m_lightDmHelper->getUsersInfo().count() > 0) { strUserName = m_lightDmHelper->getUsersInfo()[0]->name(); } else { qWarning() << "Have no user to select!!"; } } } } return strUserName; } int DbusUpperInterface::StartSession(const QJsonObject &objInfo) { Q_UNUSED(objInfo); m_lightDmHelper->startSession(); return 0; } QString DbusUpperInterface::GetCurrentSession() { return m_lightDmHelper->session(); } int DbusUpperInterface::SetCurrentSession(const QJsonObject &objInfo) { int nRet = -1; QString strSessionName = objInfo.value("Content").toString(); if (!strSessionName.isEmpty()) { if (m_lightDmHelper->setSession(strSessionName)) { nRet = 0; } } return nRet; } void DbusUpperInterface::onCurrentSessionChanged(QString strSession) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_CURRENT_SESSION_CHANGED; retObj["Content"] = strSession; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } QJsonArray DbusUpperInterface::GenerateSessionInfoList() { QJsonArray jsonArray; if (m_lightDmHelper) { QList sessionsInfo = m_lightDmHelper->getSessionsInfo(); for (auto session : sessionsInfo) { QJsonObject objSessionInfo; objSessionInfo["key"] = session; jsonArray.append(objSessionInfo); } } return jsonArray; } QString DbusUpperInterface::GetCurrentUser() { return m_lightDmHelper->getCurrentUser(); } int DbusUpperInterface::SetCurrentUser(const QJsonObject &objInfo) { int nRet = -1; QString strUserName = objInfo.value("Content").toString(); if (!strUserName.isEmpty()) { if (m_lightDmHelper->setCurrentUser(strUserName)) { nRet = 0; if (m_accountsHelper) { QString strSession = m_accountsHelper->getUserSessionByName(strUserName); if (!strSession.isEmpty()) { m_lightDmHelper->setSession(strSession); } } } } if (nRet >= 0) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_CURRENT_USER_CHANGED; retObj["Content"] = GetCurrentUser(); SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } return nRet; } bool DbusUpperInterface::getCurTabletMode() { bool tabletMode = false; #ifdef SUPPORT_TABLET_MODE if (m_libinputSwitchEvent) { tabletMode = m_libinputSwitchEvent->geInitDevicesStatus(); } #endif return tabletMode; } int DbusUpperInterface::getNetworkConnectState() { int state = -1; if (m_networkManagerHelper) { state = m_networkManagerHelper->getNetWorkConnectStatus(); } return state; } bool DbusUpperInterface::GetSaverDatetimeEnable() { QString currentUser = GetCurrentUser(); if (currentUser.isEmpty()) { currentUser = qgetenv("USER"); } return GreeterConfig::instance()->getScreensaverDatetimeEnable(currentUser); } void DbusUpperInterface::SetSaverDatetimeEnable(bool enable) { GreeterConfig::instance()->setScreensaverDatetimeEnable(enable); } bool DbusUpperInterface::GetLockDatetimeEnable() { QString currentUser = GetCurrentUser(); if (currentUser.isEmpty()) { currentUser = qgetenv("USER"); } return GreeterConfig::instance()->getLockScreenDatetimeEnable(currentUser); } void DbusUpperInterface::SetLockDatetimeEnable(bool enable) { GreeterConfig::instance()->setLockScreenDatetimeEnable(enable); } int DbusUpperInterface::switchToUser(const QJsonObject &objInfo) { int nRet = -1; QString strUserName = objInfo.value("Content").toString(); if (!strUserName.isEmpty()) { QString strSwitchSeatPath = ""; QString strDesUserName = strUserName; QString strCurUserName = SwitchUserUtils::GetCurUserName(); if (strCurUserName.isEmpty()) { qDebug() << "GetCurUserName failed!"; nRet = 1; } else { UserDisplayIfInfo curUdii = SwitchUserUtils::GetUserUDII(strCurUserName); UserDisplayIfInfo desUdii = { "" }; if (!strDesUserName.isEmpty()) { desUdii = SwitchUserUtils::GetUserUDII(strDesUserName); if (desUdii.strUserName.isEmpty()) { desUdii.strUserName = strDesUserName; } } if (!curUdii.strSeatPath.isEmpty()) { strSwitchSeatPath = curUdii.strSeatPath; } if (!desUdii.strSeatPath.isEmpty()) { strSwitchSeatPath = desUdii.strSeatPath; } if (strSwitchSeatPath.isEmpty()) { qWarning() << "switch user: switch seat path invalid!"; nRet = 1; } else { nRet = SwitchUserUtils::SwitchToUserSession(strSwitchSeatPath, desUdii); qDebug() << "Switch user result:" << nRet; } } } return nRet; } bool DbusUpperInterface::GetCanSwitchUser() { return SwitchUserUtils::CanSwitchUser(); } void DbusUpperInterface::onUsersInfoChanged() { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_USERINFO_CHANGED; retObj["Content"] = GenerateUserInfoList(); SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } bool DbusUpperInterface::SendUpdateInfoSig(QString strJson) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "UpdateInformation"); message << strJson; return QDBusConnection::sessionBus().send(message); } void DbusUpperInterface::onLogin1ReqLock() { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_LOGIN1_REQ_LOCK; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); // LockScreensaver(); // 调用屏保会导致在两个已登录用户切锁屏,会出现屏保界面,因此改为调用锁屏 Lock(); } void DbusUpperInterface::onLogin1ReqUnLock() { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_LOGIN1_REQ_UNLOCK; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::doPrepareForSleep(bool isSleep, QString strAction /*= ""*/) { qInfo() << "doPrepareForSleep begin:" << isSleep << strAction; m_strAction = strAction; if (isSleep) { if (!checkStatus(SESSION_STATUS_SLEEPED)) { uninhibit(); if (!strAction.isEmpty() && m_sessionHelper) { m_sessionHelper->doAction(strAction); } qInfo() << "doPrepareForSleep done:" << isSleep; return; } if (m_bBlankState) { qDebug() << "Is blank state"; qInfo() << "doPrepareForSleep done:" << isSleep; return; } stopDelayLockScreen(false); m_bBlankState = true; if (checkScreenDialogRunning() || GetLockState() || GetSlpState()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); message << "SleepLock"; QDBusConnection::sessionBus().send(message); } else { this->onShowBlankScreensaver(); } if (m_bLockState) { uninhibit(); if (!strAction.isEmpty() && m_sessionHelper) { m_sessionHelper->doAction(strAction); } qInfo() << "doPrepareForSleep done:" << isSleep; } else { if (!m_timer) { m_timer = new QTimer(this); qDebug() << "m_bLockState " << m_bLockState; auto connection = connect(m_timer, &QTimer::timeout, this, &DbusUpperInterface::handleTimeout); m_connections.append(connection); } m_timer->start(200); } } else { m_nStatus &= ~SESSION_STATUS_SLEEPED; m_bBlankState = false; inhibit(); qInfo() << "doPrepareForSleep done:" << isSleep; } } void DbusUpperInterface::handleTimeout() { m_timerCount += 1; if (m_bLockState || m_timerCount > 20) { m_timer->stop(); m_timerCount = 0; uninhibit(); if (!m_strAction.isEmpty() && m_sessionHelper) { m_sessionHelper->doAction(m_strAction); } qInfo() << "doPrepareForSleep done: true timeout"; } } void DbusUpperInterface::onLogin1PrepareForSleep(bool isSleep) { qInfo() << "onLogin1PrepareForSleep:" << isSleep; doPrepareForSleep(isSleep); QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_LOGIN1_PREPAREFORSLEEP; retObj["IsSleep"] = isSleep; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onLogin1SessionActiveChanged(bool isActive) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_LOGIN1_SESSION_ACTIVE_CHANGED; retObj["IsActive"] = isActive; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBlockLogoutInhibitedChanged() { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_LOGIN1_BLOCKLOGOUTINHIBITED_CHANGED; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBlockInhibitedChanged(QString blockInhibited) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_LOGIN1_BLOCKINHIBITED_CHANGED; retObj["BlockInhibited"] = blockInhibited; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBatteryStatusChanged(QString iconName) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_UPOWER_BATTERY_STATUS; retObj["IconName"] = iconName; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onSessionIdleReceived() { if (!checkStatus(SESSION_STATUS_SCREENSAVER)) { qDebug() << "m_nStatus:" << m_nStatus << " can't do screensaver!"; return; } if (m_procLockDialog.state() != QProcess::NotRunning) return; qDebug() << "emit SessionIdle"; m_bLockState = false; QString cmd = QString("/usr/bin/ukui-screensaver-dialog"); qDebug() << cmd; m_procLockDialog.start(cmd, QStringList() << "--session-idle"); emitLockState(true); } void DbusUpperInterface::onSessionIdleExit() { m_nStatus &= ~SESSION_STATUS_SCREENSAVER; m_nStatus &= ~SESSION_STATUS_SCREENCLOSE; stopDelayLockScreen(); } void DbusUpperInterface::delayLockScreen() { if (!m_timerLock) { m_timerLock = new QTimer(this); m_connections.append(connect(m_timerLock, &QTimer::timeout, this, &DbusUpperInterface::onLockScreenTimeout)); } int nLockTimeout = m_gsettingsHelper->GetLockScreenConf(KEY_LOCK_TIMEOUT).toInt(); qDebug() << "LockTimeout:" << nLockTimeout; if (nLockTimeout != -1) { stopDelayLockScreen(); if (m_procLockDialog.state() != QProcess::NotRunning || checkScreenDialogRunning()) { QDBusMessage message; message = QDBusMessage::createSignal(SS_DBUS_PATH, SS_DBUS_INTERFACE, "SecondRunParam"); QString strParam = QString("CloseLock&delay=%1&hasLock=1").arg(nLockTimeout * 1000); message << strParam; QDBusConnection::sessionBus().send(message); } else { QString cmd = QString("/usr/bin/ukui-screensaver-dialog --blank --delay %1 --has-lock 1").arg(nLockTimeout * 1000); qDebug() << cmd; m_procLockDialog.start(cmd); } emitLockState(true); m_timerLock->start(m_gsettingsHelper->GetLockScreenConf(KEY_LOCK_TIMEOUT).toInt() * 1000); } } void DbusUpperInterface::stopDelayLockScreen(bool withUnlock /*= true*/) { if (m_timerLock && m_timerLock->isActive()) { if (withUnlock) { UnLock(); } m_timerLock->stop(); } } void DbusUpperInterface::onLockScreenTimeout() { m_timerLock->stop(); } void DbusUpperInterface::onBatteryChanged(QStringList batteryArgs) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_UPOWER_BATTERY; QJsonArray jsonArray; for (auto args : batteryArgs) { QJsonObject objBatteryInfo; objBatteryInfo["key"] = args; jsonArray.append(objBatteryInfo); } retObj["Content"] = jsonArray; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onUPowerServiceReady() { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_UPOWER_SERVICE_READY; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onLidStateChanged(bool isClosed) { if (!isClosed) { if (m_gsettingsHelper->GetPowerManagerConf(KEY_LOCK_BLANKSCREEN).toBool()) { m_nStatus &= ~SESSION_STATUS_SCREENCLOSE; } stopDelayLockScreen(); } } void DbusUpperInterface::onLockScreenConfigChanged(QString strKey, QVariant value) { if (strKey == KEY_BACKGROUND) { onUsersInfoChanged(); } QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_LOCKSCREEN_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onScreenSaverConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_SCREENSAVER_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onPowerManagerConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_POWERMANAGER_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onMateBgConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_MATEBG_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onUkccPluginsConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_UKCCPLUGINS_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } if (strKey == KEY_DATE) { m_config->setShareConfigValue("dateType", value.toString()); } else if (strKey == KEY_HOUR_SYSTEM) { m_config->setShareConfigValue("timeType", value.toInt()); } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onThemeStyleConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_THEMESTYLE_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } if (strKey == KEY_SYSTEM_FONT_SIZE) { m_config->setShareConfigValue("fontSize", value.toDouble()); } else if (strKey == KEY_THEME_COLOR) { m_config->setShareConfigValue("themeColor", value.toString()); } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onSessionConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_SESSION_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onKeyboardConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_KEYBOARD_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onUsdMediaKeysConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_USD_MEDIAKEYS_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onUsdMediaStateKeysConfigChanged(QString strKey, QVariant value) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_GSETTINGS_USD_MEDIA_STATE_KEYS_CONF_CHANGED; retObj["Key"] = strKey; switch (value.type()) { case QVariant::Bool: retObj["Value"] = value.toBool(); break; case QVariant::Int: retObj["Value"] = value.toInt(); break; case QVariant::String: retObj["Value"] = value.toString(); break; case QVariant::UInt: retObj["Value"] = value.toDouble(); break; case QVariant::Double: retObj["Value"] = value.toDouble(); break; default: return; } SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::GetScreenSaverConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetScreenSaverConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } QJsonArray DbusUpperInterface::getSaverPhotoPath(QString key) { QJsonArray jsonArray; if (m_gsettingsHelper) { QStringList pathList = m_gsettingsHelper->GetScreenSaverConf(key).toStringList(); for (auto list : pathList) { QJsonObject objPathInfo; objPathInfo["key"] = list; jsonArray.append(objPathInfo); } } return jsonArray; } int DbusUpperInterface::SetScreenSaverConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetScreenSaverConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetLockScreenConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetLockScreenConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetLockScreenConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetLockScreenConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetPowerManagerConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetPowerManagerConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetPowerManagerConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetPowerManagerConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetMateBgConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetMateBgConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetMateBgConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetMateBgConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetUkccPluginsConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetUkccPluginsConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetUkccPluginsConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetUkccPluginsConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetThemeStyleConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetThemeStyleConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetThemeStyleConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetThemeStyleConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetSessionConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetSessionConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetSessionConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetSessionConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetKeyboardConf(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetKeyboardConf(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetKeyboardConf(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetKeyboardConf(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::onLockDialogProcExit(int exitCode, QProcess::ExitStatus exitStatus) { qInfo() << "ukui-screensaver-dialog is exited,exitCode is " << exitCode; if (exitStatus == QProcess::CrashExit) qWarning() << m_procLockDialog.errorString(); emitLockState(false); m_bLockState = false; } bool DbusUpperInterface::checkStatus(int nStatus) { if (!m_gsettingsHelper) { return false; } switch (nStatus) { case SESSION_STATUS_SLEEPED: { m_nStatus |= nStatus; if (!m_gsettingsHelper->GetLockScreenConf(KEY_SLEEP_ACTIVATION_ENABLED).toBool()) { return false; } } break; case SESSION_STATUS_SCREENCLOSE: { m_nStatus |= nStatus; if ((m_nStatus & SESSION_STATUS_SLEEPED) || !m_gsettingsHelper->GetLockScreenConf(KEY_CLOSE_ACTIVATION_ENABLED).toBool()) { return false; } if (((m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_COMPUTER_AC).toInt() == m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_DISPLAY_AC).toInt()) && m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_COMPUTER_AC).toInt() != -1) && !m_gsettingsHelper->GetLockScreenConf(KEY_SLEEP_ACTIVATION_ENABLED).toBool()) { qDebug() << "Sleep same with Close and Sleep disable!"; return false; } } break; case SESSION_STATUS_SCREENSAVER: { m_nStatus |= nStatus; if ((m_nStatus & SESSION_STATUS_SLEEPED) || (m_nStatus & SESSION_STATUS_SCREENCLOSE)) { return false; } if (((m_gsettingsHelper->GetLockScreenConf(KEY_IDLE_DELAY).toInt() * 60 == m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_COMPUTER_AC).toInt()) && m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_COMPUTER_AC).toInt() != -1) && !m_gsettingsHelper->GetLockScreenConf(KEY_SLEEP_ACTIVATION_ENABLED).toBool()) { qDebug() << "Sleep same with idle and Sleep disable!"; return false; } if (((m_gsettingsHelper->GetLockScreenConf(KEY_IDLE_DELAY).toInt() * 60 == m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_DISPLAY_AC).toInt()) && m_gsettingsHelper->GetPowerManagerConf(KEY_SLEEP_DISPLAY_AC).toInt() != -1) /* && !m_gsettingsHelper->GetLockScreenConf(KEY_CLOSE_ACTIVATION_ENABLED).toBool()*/) { qDebug() << "Close same with idle do nothing!"; return false; } } break; default: return false; } return true; } void DbusUpperInterface::inhibit() { if (m_inhibitFileDescriptor.isValid()) { return; } QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QStringLiteral("Inhibit")); message.setArguments(QVariantList({ QStringLiteral("sleep"), "Screen Locker Backend", "Ensuring that the screen gets locked before going to sleep", QStringLiteral("delay") })); QDBusPendingReply reply = QDBusConnection::systemBus().call(message); if (!reply.isValid()) { return; } reply.value().swap(m_inhibitFileDescriptor); } void DbusUpperInterface::uninhibit() { if (!m_inhibitFileDescriptor.isValid()) { return; } m_inhibitFileDescriptor = QDBusUnixFileDescriptor(); } void DbusUpperInterface::IsPamInAuthentication(QJsonObject &reqObj, QJsonObject &retObj) { Q_UNUSED(reqObj); if (m_pamAuth) { retObj["Content"] = m_pamAuth->inAuthentication(); } else { retObj["Content"] = false; } } void DbusUpperInterface::IsPamAuthenticated(QJsonObject &reqObj, QJsonObject &retObj) { Q_UNUSED(reqObj); if (m_pamAuth) { retObj["Content"] = m_pamAuth->isAuthenticated(); } else { retObj["Content"] = false; } } void DbusUpperInterface::PamAuthenticationUser(QJsonObject &reqObj, QJsonObject &retObj) { Q_UNUSED(reqObj); if (m_pamAuth) { retObj["Content"] = m_pamAuth->authenticationUser(); } else { retObj["Content"] = ""; } } int DbusUpperInterface::PamAuthenticateUser(const QJsonObject &objInfo) { int nRet = -1; QVariant varUserName = objInfo.value("UserName"); if (varUserName.isValid() && m_pamAuth) { m_pamAuth->authenticate(varUserName.toString()); nRet = 0; } return nRet; } void DbusUpperInterface::GetAutologinUserHint(QJsonObject &reqObj, QJsonObject &retObj) { Q_UNUSED(reqObj); if (m_pamAuth) { retObj["Content"] = m_pamAuth->autologinUserHint(); } else { retObj["Content"] = ""; } } int DbusUpperInterface::PamAuthenticateCancel(const QJsonObject &objInfo) { Q_UNUSED(objInfo); int nRet = -1; if (m_pamAuth) { m_pamAuth->cancelAuthentication(); nRet = 0; } return 0; } int DbusUpperInterface::PamRespond(const QJsonObject &objInfo) { int nRet = -1; QVariant varRespond = objInfo.value("Content"); if (varRespond.isValid() && m_pamAuth) { QString strDecrypt = varRespond.toString(); if (strDecrypt != BIOMETRIC_IGNORE && !priKey.isEmpty()) { QByteArray buffer; QByteArray byteEncrypt = QByteArray::fromBase64(strDecrypt.toLatin1()); if (rsac.decryptData(byteEncrypt, buffer, priKey)) { strDecrypt = buffer; } } m_pamAuth->respond(strDecrypt); nRet = 0; } return nRet; } void DbusUpperInterface::onPamShowMessage(QString strMsg, int type) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_PAMAUTH_SHOWMESSAGE; retObj["Message"] = strMsg; retObj["MsgType"] = type; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onPamShowPrompt(QString strPrompt, int type) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_PAMAUTH_SHOWPROMPT; retObj["Prompt"] = strPrompt; retObj["PromptType"] = type; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onPamShowBinaryPrompt(QVariant value, int type) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_PAMAUTH_SHOWPBINARYROMPT; retObj["BinaryPromptType"] = type; retObj["BinaryVal"] = value.toJsonValue(); SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onPamAuthCompleted() { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_PAMAUTH_AUTH_COMPLETED; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBioAuthShowMessage(QString strMsg) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_BIOAUTH_SHOW_MESSAGE; retObj["Message"] = strMsg; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBioAuthStateChanged(int nState) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_BIOAUTH_AUTHSTATE_CHANGED; retObj["State"] = nState; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBioServiceStatusChanged(bool bValid) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_BIOAUTH_SERVICE_STATUS_CHANGED; retObj["IsValid"] = bValid; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBioDeviceChanged() { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_BIOAUTH_DEVICE_CHANGED; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBioAuthFrameData(QString strData) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_BIOAUTH_FRAME_DATA; retObj["Data"] = strData; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onBioAuthCompleted(int nUid, bool isSuccess, int nError, int nMaxFailedTime, int nFailedTime) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_BIOAUTH_COMPLETE; retObj["UId"] = nUid; retObj["IsSuccess"] = isSuccess; retObj["ErrorNo"] = nError; retObj["MaxFailedTime"] = nMaxFailedTime; retObj["FailedTime"] = nFailedTime; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onTabletModeChanged(bool tabletMode) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_TABLET_MODE_CHANGED; retObj["mode"] = tabletMode; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onNetWorkConnectStateChanged(int state) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_NETWORK_CONNECT_STATE_CHANGED; retObj["state"] = state; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onScreensaverDatetimeChanged(bool isEnable, QString strUserName) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_SET_SAVER_DATETIME_ENABLE_DATA_CHANGED; retObj["enable"] = isEnable; retObj["username"] = strUserName; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::onScreenlockDatetimeChanged(bool isEnable, QString strUserName) { QJsonObject retObj; retObj["CmdId"] = LOCK_CMD_ID_SET_LOCK_DATETIME_ENABLE_DATA_CHANGED; retObj["enable"] = isEnable; retObj["username"] = strUserName; SendUpdateInfoSig(QString(QJsonDocument(retObj).toJson())); } void DbusUpperInterface::BioGetAvailableDevices(QJsonObject &reqObj, QJsonObject &retObj) { #ifdef ENABLE_BIO_BACKEND QVariant varUserId = reqObj.value("UserId"); if (varUserId.isValid() && m_bioAuth) { DeviceMap bioDevInfos = m_bioAuth->getAvailableDevices(varUserId.toInt()); QJsonArray objArray; DeviceMap::iterator itValidDev = bioDevInfos.begin(); for (; itValidDev != bioDevInfos.end(); itValidDev++) { QJsonObject objDevInfo; objDevInfo["id"] = itValidDev.value()->id; objDevInfo["ShortName"] = itValidDev.value()->shortName; objDevInfo["FullName"] = itValidDev.value()->fullName; objDevInfo["DriverEnable"] = itValidDev.value()->driverEnable; objDevInfo["DeviceNum"] = itValidDev.value()->deviceNum; objDevInfo["DeviceType"] = itValidDev.value()->deviceType; objDevInfo["StorageType"] = itValidDev.value()->storageType; objDevInfo["EigType"] = itValidDev.value()->eigType; objDevInfo["VerifyType"] = itValidDev.value()->verifyType; objDevInfo["IdentifyType"] = itValidDev.value()->identifyType; objDevInfo["BusType"] = itValidDev.value()->busType; objDevInfo["DeviceStatus"] = itValidDev.value()->deviceStatus; objDevInfo["OpsStatus"] = itValidDev.value()->OpsStatus; objArray.append(objDevInfo); } retObj["Content"] = objArray; } else { retObj["Content"] = QJsonArray(); } #endif } void DbusUpperInterface::BioGetDisabledDevices(QJsonObject &reqObj, QJsonObject &retObj) { #ifdef ENABLE_BIO_BACKEND QVariant varUserId = reqObj.value("UserId"); if (varUserId.isValid() && m_bioAuth) { QList disabledInfos = m_bioAuth->getDisabledDevices(varUserId.toInt()); QJsonArray objArray; for (auto devType : disabledInfos) { objArray.append(devType); } retObj["Content"] = objArray; } else { retObj["Content"] = QJsonArray(); } #endif } void DbusUpperInterface::BioGetBioAuthState(QJsonObject &reqObj, QJsonObject &retObj) { #ifdef ENABLE_BIO_BACKEND Q_UNUSED(reqObj); if (m_bioAuth) { retObj["Content"] = m_bioAuth->getBioAuthState(); } else { retObj["Content"] = BIOAUTH_IDLE; } #endif } void DbusUpperInterface::BioGetCurBioInfo(QJsonObject &reqObj, QJsonObject &retObj) { #ifdef ENABLE_BIO_BACKEND Q_UNUSED(reqObj); if (m_bioAuth) { DeviceInfo bioDevInfo = m_bioAuth->getCurBioInfo(); QJsonObject objDevInfo; objDevInfo["id"] = bioDevInfo.id; objDevInfo["ShortName"] = bioDevInfo.shortName; objDevInfo["FullName"] = bioDevInfo.fullName; objDevInfo["DriverEnable"] = bioDevInfo.driverEnable; objDevInfo["DeviceNum"] = bioDevInfo.deviceNum; objDevInfo["DeviceType"] = bioDevInfo.deviceType; objDevInfo["StorageType"] = bioDevInfo.storageType; objDevInfo["EigType"] = bioDevInfo.eigType; objDevInfo["VerifyType"] = bioDevInfo.verifyType; objDevInfo["IdentifyType"] = bioDevInfo.identifyType; objDevInfo["BusType"] = bioDevInfo.busType; objDevInfo["DeviceStatus"] = bioDevInfo.deviceStatus; objDevInfo["OpsStatus"] = bioDevInfo.OpsStatus; retObj["Content"] = objDevInfo; } else { retObj["Content"] = QJsonObject(); } #endif } void DbusUpperInterface::BioGetDefaultDevice(QJsonObject &reqObj, QJsonObject &retObj) { #ifdef ENABLE_BIO_BACKEND qDebug() << "BioGetDefaultDevice:" << reqObj; QVariant varUserId = reqObj.value("UserId"); QVariant varUserName = reqObj.value("UserName"); if (varUserId.isValid() && varUserName.isValid() && m_bioAuth) { if (reqObj.contains("DevType")) { QVariant varDevType = reqObj.value("DevType"); retObj["Content"] = m_bioAuth->getDefaultDevice(varUserId.toInt(), varUserName.toString(), varDevType.toInt()); } else { retObj["Content"] = m_bioAuth->getDefaultDevice(varUserId.toInt(), varUserName.toString()); } } else { retObj["Content"] = ""; } #endif } void DbusUpperInterface::BioFindDeviceById(QJsonObject &reqObj, QJsonObject &retObj) { #ifdef ENABLE_BIO_BACKEND QVariant varUserId = reqObj.value("UserId"); QVariant varDevId = reqObj.value("DevId"); if (varUserId.isValid() && varDevId.isValid() && m_bioAuth) { DeviceInfoPtr ptrDevInfo = m_bioAuth->findDeviceById(varUserId.toInt(), varDevId.toInt()); QJsonObject objDevInfo; if (ptrDevInfo) { objDevInfo["id"] = ptrDevInfo->id; objDevInfo["ShortName"] = ptrDevInfo->shortName; objDevInfo["FullName"] = ptrDevInfo->fullName; objDevInfo["DriverEnable"] = ptrDevInfo->driverEnable; objDevInfo["DeviceNum"] = ptrDevInfo->deviceNum; objDevInfo["DeviceType"] = ptrDevInfo->deviceType; objDevInfo["StorageType"] = ptrDevInfo->storageType; objDevInfo["EigType"] = ptrDevInfo->eigType; objDevInfo["VerifyType"] = ptrDevInfo->verifyType; objDevInfo["IdentifyType"] = ptrDevInfo->identifyType; objDevInfo["BusType"] = ptrDevInfo->busType; objDevInfo["DeviceStatus"] = ptrDevInfo->deviceStatus; objDevInfo["OpsStatus"] = ptrDevInfo->OpsStatus; } retObj["Content"] = objDevInfo; } else { retObj["Content"] = QJsonObject(); } #endif } void DbusUpperInterface::initPublicEncrypt() { m_initEncrypt = QtConcurrent::run([=]() { if (pubKey.isEmpty()) { rsac.genKeyPair(priKey, pubKey, 2048); } }); } QString DbusUpperInterface::getPublicEncrypt() { if (pubKey.isEmpty()) { initPublicEncrypt(); m_initEncrypt.waitForFinished(); } if (pubKey.isEmpty()) { rsac.genKeyPair(priKey, pubKey, 2048); } return QString(pubKey); } bool DbusUpperInterface::sendPassword(const QString username, QByteArray password) { if (priKey.isEmpty()) return false; QByteArray decryptText; rsac.decryptData(password, decryptText, priKey); // 解密 if (GetLockState()) { if (m_pamAuth && m_pamAuth->inAuthentication() && (m_pamAuth->authenticationUser() == username)) { m_pamAuth->respond(QString(decryptText)); } else if (m_pamAuth) { m_pamAuth->authenticate(username); QTimer::singleShot(100, this, [=]() { m_pamAuth->respond(QString(decryptText)); }); } } return true; } void DbusUpperInterface::BioFindDeviceByName(QJsonObject &reqObj, QJsonObject &retObj) { QVariant varUserId = reqObj.value("UserId"); QVariant varDevName = reqObj.value("DevName"); if (varUserId.isValid() && varDevName.isValid() && m_bioAuth) { DeviceInfoPtr ptrDevInfo = m_bioAuth->findDeviceByName(varUserId.toInt(), varDevName.toString()); QJsonObject objDevInfo; if (ptrDevInfo) { objDevInfo["id"] = ptrDevInfo->id; objDevInfo["ShortName"] = ptrDevInfo->shortName; objDevInfo["FullName"] = ptrDevInfo->fullName; objDevInfo["DriverEnable"] = ptrDevInfo->driverEnable; objDevInfo["DeviceNum"] = ptrDevInfo->deviceNum; objDevInfo["DeviceType"] = ptrDevInfo->deviceType; objDevInfo["StorageType"] = ptrDevInfo->storageType; objDevInfo["EigType"] = ptrDevInfo->eigType; objDevInfo["VerifyType"] = ptrDevInfo->verifyType; objDevInfo["IdentifyType"] = ptrDevInfo->identifyType; objDevInfo["BusType"] = ptrDevInfo->busType; objDevInfo["DeviceStatus"] = ptrDevInfo->deviceStatus; objDevInfo["OpsStatus"] = ptrDevInfo->OpsStatus; } retObj["Content"] = objDevInfo; } else { retObj["Content"] = QJsonObject(); } } int DbusUpperInterface::BioStartAuth(const QJsonObject &objInfo) { int nRet = -1; QVariant varUserId = objInfo.value("UserId"); QVariant varDevId = objInfo.value("DevId"); if (varUserId.isValid() && varDevId.isValid() && m_bioAuth) { m_bioAuth->startAuth(varUserId.toInt(), varDevId.toInt()); nRet = 0; } return nRet; } int DbusUpperInterface::BioStopAuth(const QJsonObject &objInfo) { Q_UNUSED(objInfo); int nRet = -1; if (m_bioAuth) { m_bioAuth->stopAuth(); nRet = 0; } return nRet; } void DbusUpperInterface::GetUsdMediaKeys(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetUsdMediaKeys(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } void DbusUpperInterface::GetUsdMediaStateKeys(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetUsdMediaStateKeys(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } int DbusUpperInterface::SetUsdMediaStateKeys(const QJsonObject &objInfo) { int nRet = -1; QString strKey = objInfo.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = objInfo.value("Value").toVariant(); if (value.isValid()) { if (m_gsettingsHelper->SetUsdMediaStateKeys(strKey, value)) { nRet = 0; } } } return nRet; } void DbusUpperInterface::GetWeatherKeys(QJsonObject &reqObj, QJsonObject &retObj) { QString strKey = reqObj.value("Key").toString(); if (!strKey.isEmpty()) { QVariant value = m_gsettingsHelper->GetWeatherKeys(strKey); switch (value.type()) { case QVariant::Bool: retObj[strKey] = value.toBool(); return; case QVariant::Int: retObj[strKey] = value.toInt(); return; case QVariant::String: retObj[strKey] = value.toString(); qDebug() << retObj[strKey]; return; case QVariant::UInt: retObj[strKey] = value.toDouble(); return; case QVariant::Double: retObj[strKey] = value.toDouble(); return; default: break; } } retObj["Ret"] = -2; retObj["Message"] = "Key is invalid!!"; } bool DbusUpperInterface::usdExternalDoAction(const QJsonObject &objInfo) { int nRet = -1; int actionType = objInfo.value("Content").toInt(); if (m_usdHelper->usdExternalDoAction(actionType)) { nRet = 0; } return nRet; } bool DbusUpperInterface::blockShortcut(const QJsonObject &objInfo) { int nRet = -1; bool isBlock = objInfo.value("Content").toBool(); if (m_kglobalHelper->blockShortcut(isBlock)) { nRet = 0; } return nRet; } bool DbusUpperInterface::lockStateChanged(const QJsonObject &objInfo) { bool state = objInfo.value("Content").toBool(); bool isSessionTools = objInfo.value("SessionTools").toBool(); if (m_bLockState != state && !isSessionTools) { m_bLockState = state; } emitLockState(state, isSessionTools); return m_bLockState; } int DbusUpperInterface::SetPowerManager(const QJsonObject &objInfo) { int nRet = -1; QString strFuncName = objInfo.value("Content").toString(); bool isForceExecute = objInfo.value("Force").toBool(); bool isSessionTools = objInfo.value("IsSessionTools").toBool(false); if (!strFuncName.isEmpty()) { qWarning() << __LINE__ << __FUNCTION__ << strFuncName << isForceExecute; if (strFuncName != "UpgradeThenRestart" && strFuncName != "UpgradeThenShutdown" && m_sessionHelper) { if (!isGreeterMode()) { if (!isForceExecute && !isSessionTools) { if (strFuncName == "Hibernate" || strFuncName == "Suspend") { QStringList sleepLockcheck = m_sessionHelper->getLockCheckStatus("sleep"); if (!sleepLockcheck.isEmpty()) { AppBlockWindow(strFuncName); return -1; } } if (strFuncName == "Reboot" || strFuncName == "PowerOff" || strFuncName == "Logout") { QStringList shutdownLockcheck = m_sessionHelper->getLockCheckStatus("shutdown"); if (shutdownLockcheck.count() > 0) { AppBlockWindow(strFuncName); return -1; } QStringList logoutLockcheck = m_sessionHelper->getLockCheckStatus("logout"); if (logoutLockcheck.count() > 0) { qDebug() << "logoutlockcheck"; AppBlockWindow(strFuncName, false); return -1; } if (GetLoggedInUsersCount() > 1 && strFuncName != "Logout") { qDebug() << "multiuserblockwindow"; if (strFuncName == "Reboot") { MultiUserBlockWindow("Reboot"); } else if (strFuncName == "PowerOff") { MultiUserBlockWindow("PowerOff"); } return -1; } } } // 当存在多用户且存在软阻塞的情况下,且用户选择的是关机或重启,并且用户在软阻塞场景下点击了确认按钮 if (strFuncName == "RebootSoftBlock" || strFuncName == "PoweroffSoftBlock") { if (strFuncName == "RebootSoftBlock") { strFuncName = "Reboot"; } else if (strFuncName == "PoweroffSoftBlock") { strFuncName = "PowerOff"; } if (GetLoggedInUsersCount() > 1) { qDebug() << "multiuserblockwindow"; if (strFuncName == "Reboot") { MultiUserBlockWindow("Reboot"); } else if (strFuncName == "PowerOff") { MultiUserBlockWindow("PowerOff"); } return -1; } } qDebug() << "doaction test" << strFuncName; if (!isForceExecute) { m_sessionHelper->doAction(strFuncName); } else { if (m_login1Helper) { m_login1Helper->setPowerManager(strFuncName); } else { m_sessionHelper->doAction(strFuncName); } } } else if (m_login1Helper) { m_login1Helper->setPowerManager(strFuncName); } } else if (strFuncName == "UpgradeThenRestart" && m_systemsUpgradeHelper) { m_systemsUpgradeHelper->doUpgradeThenRboot(); } else if (strFuncName == "UpgradeThenShutdown" && m_systemsUpgradeHelper) { m_systemsUpgradeHelper->doUpgradeThenShutdown(); } nRet = 0; } return nRet; } bool DbusUpperInterface::ShowFolder(QStringList paths) { int nRet = -1; if (m_fileManagerHelper->showFolders(paths)) { nRet = 0; } return nRet; } QSharedPointer DbusUpperInterface::getGSettingsInstance() { return m_gsettingsHelper; } void DbusUpperInterface::CheckTurnOnScreen() { if (m_sessionWatcher && m_sessionWatcher->isSessionIdle()) { QProcess *procTurnOn = new QProcess(this); auto finishedSignal = static_cast(&QProcess::finished); connect(procTurnOn, finishedSignal, this, [=](int exitCode, QProcess::ExitStatus exitStatus) { qDebug() << procTurnOn->arguments() << "finished:" << exitCode << exitStatus; procTurnOn->deleteLater(); }); if ("wayland" == qgetenv("XDG_SESSION_TYPE")) { QProcessEnvironment procEnv = QProcessEnvironment::systemEnvironment(); procEnv.insert("QT_QPA_PLATFORM", "wayland"); procTurnOn->setProcessEnvironment(procEnv); procTurnOn->start("kscreen-doctor", { "--dpms", "on" }); } else { procTurnOn->start("xset", { "dpms", "force", "on" }); } m_sessionWatcher->simulateSessionActive(); qDebug() << "The display is turned on" << qgetenv("XDG_SESSION_TYPE"); } else { qDebug() << "session not in idle"; } } ukui-screensaver/src/lock-backend/dbusupperinterface.h0000664000175000017500000003360615172041106022147 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef DBUSUPPERINTERFACE_H #define DBUSUPPERINTERFACE_H #include #include #include #include #include #include #include #include #include #include #include #include "../../src/common/utils.h" #include "../../src/common/kyrsac.h" #include "../../src/common/definetypes.h" class LightDMHelper; class Login1Helper; class GSettingsHelper; class Configuration; class QJsonObject; class PamAuthenticate; class UsdHelper; class UpowerHelper; class AccountsHelper; class SessionHelper; class SystemUpgradeHelper; class SessionWatcher; class BioAuthenticate; class KglobalAccelHelper; #ifdef SUPPORT_TABLET_MODE class LibinputSwitchEvent; #endif class NetWorkManagerHelper; class FileManagerHelper; /** * @brief dbus服务接口实现类 * */ class DbusUpperInterface : public QObject, protected QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", SS_DBUS_SERVICE) public: enum { SESSION_STATUS_SCREENSAVER = 1, // 屏保 SESSION_STATUS_SCREENCLOSE = 2, // 关屏 SESSION_STATUS_SLEEPED = 4, // 休眠/睡眠 }; /** * @brief 构造 * * @param parent 父指针 */ explicit DbusUpperInterface(QObject *parent = nullptr); virtual ~DbusUpperInterface(); void init(); public: bool checkScreenDialogRunning(); void emitLockState(bool val, bool isSessionTools = false); void LockStartupMode(); /** * @brief 获取黑色屏保状态(沿用) * * @return bool true 是,false 否 */ bool GetBlankState(); /** * @brief 获取锁屏状态 * * @return bool true 锁定,false 未锁定 */ bool GetLockState(); /** * @brief 锁屏 * */ void Lock(); /** * @brief SwitchUser * */ void SwitchUser(); /** * @brief SwitchToUser * */ void SwitchToUser(QString strUserName); /** * @brief 解锁 * */ void UnLock(); /** * @brief 以黑色屏保方式锁屏 * * @param int 锁定方式 0 延时关屏、1 合盖、2 低电量 * @return bool 是否执行 true 已执行,false 未执行 */ bool LockByBlank(int); /** * @brief 设置锁定状态 * */ void SetLockState(); /** * @brief 清除锁定状态 * */ void ClearLockState(); /** * @brief 显示屏保 * */ void ShowScreensaver(); /** * @brief 显示锁屏和屏保 * */ void LockScreensaver(); /** * @brief session tools * */ bool CheckAppVersion(); void SessionTools(); void AppBlockWindow(QString actionType, bool isHardInhibit = true); void MultiUserBlockWindow(QString actionType); void Suspend(); void Logout(); void Reboot(); void PowerOff(); void Hibernate(); /** * @brief 获取信息 * * @param strJson 需要获取的信息json * @return QString 获取到的信息json */ QString GetInformation(QString strJson); /** * @brief 设置信息 * * @param strJson 需要设置的信息json * @return int 设置结果,0 成功,否则 失败 */ int SetInformation(QString strJson); QString getPublicEncrypt(); bool sendPassword(const QString username, QByteArray password); QSharedPointer getGSettingsInstance(); bool ShowFolder(QStringList paths); /** * @brief GetInitData * @param reqObj * @param retObj */ void GetInitData(QJsonObject &reqObj, QJsonObject &retObj); bool GetSaverDatetimeEnable(); void SetSaverDatetimeEnable(bool enable); bool GetLockDatetimeEnable(); void SetLockDatetimeEnable(bool enable); public Q_SLOTS: /** * @brief 服务退出 * * @param serviceName 服务名称 */ void onNameLost(const QString &serviceName); /** * @brief 用户信息改变 * */ void onUsersInfoChanged(); /** * @brief 请求锁定 * */ void onLogin1ReqLock(); /** * @brief 请求解锁 * */ void onLogin1ReqUnLock(); /** * @brief 系统准备休眠/唤醒 * * @param isSleep true 休眠,否则唤醒 */ void onLogin1PrepareForSleep(bool isSleep); /** * @brief 用户会话活跃状态改变 * * @param isActive true 活跃,否则不活跃 */ void onLogin1SessionActiveChanged(bool isActive); /** * @brief 程序阻塞关机/睡眠状态改变 * * @param blockInhibited 阻塞类型 */ void onBlockInhibitedChanged(QString blockInhibited); /** * @brief 程序阻塞关机/重启/注销 * */ void onBlockLogoutInhibitedChanged(); void onBatteryStatusChanged(QString iconName); void onBatteryChanged(QStringList batteryArgs); void onUPowerServiceReady(); void onLidStateChanged(bool isClosed); void onLockScreenConfigChanged(QString strKey, QVariant value); void onScreenSaverConfigChanged(QString strKey, QVariant value); void onPowerManagerConfigChanged(QString strKey, QVariant value); void onMateBgConfigChanged(QString strKey, QVariant value); void onUkccPluginsConfigChanged(QString strKey, QVariant value); void onThemeStyleConfigChanged(QString strKey, QVariant value); void onSessionConfigChanged(QString strKey, QVariant value); void onKeyboardConfigChanged(QString strKey, QVariant value); void onCurrentSessionChanged(QString strSession); void onPamShowMessage(QString strMsg, int type); void onPamShowPrompt(QString strPrompt, int type); void onPamShowBinaryPrompt(QVariant value,int type); void onPamAuthCompleted(); void onUsdMediaKeysConfigChanged(QString strKey, QVariant value); void onUsdMediaStateKeysConfigChanged(QString strKey, QVariant value); // 空闲锁屏 void onSessionIdleReceived(); void onSessionIdleExit(); void stopDelayLockScreen(bool withUnlock = true); void onBioAuthShowMessage(QString strMsg); void onBioAuthStateChanged(int nState); void onBioServiceStatusChanged(bool bValid); void onBioDeviceChanged(); void onBioAuthFrameData(QString strData); void onBioAuthCompleted(int nUid, bool isSuccess, int nError, int nMaxFailedTime, int nFailedTime); // 平板模式切换 void onTabletModeChanged(bool tabletMode); void onNetWorkConnectStateChanged(int state); void onScreensaverDatetimeChanged(bool isEnable, QString strUserName); void onScreenlockDatetimeChanged(bool isEnable, QString strUserName); /** * @brief CheckTurnOnScreen 检查并开屏 */ void CheckTurnOnScreen(); private Q_SLOTS: void onLockDialogProcExit(int exitCode, QProcess::ExitStatus exitStatus); void handleTimeout(); Q_SIGNALS: /** * @brief 信息更新 * * @param info 信息内容json */ void UpdateInformation(const QString &info); private: /** * @brief 生成用户信息json数组 * * @return QJsonArray 信息数组 */ QJsonArray GenerateUserInfoList(); int GetLoggedInUsersCount(); QString GetDefaultAuthUser(); QString GetCurrentUser(); int SetCurrentUser(const QJsonObject &objInfo); int switchToUser(const QJsonObject &objInfo); bool GetCanSwitchUser(); QJsonArray GenerateSessionInfoList(); QString GetCurrentSession(); int SetCurrentSession(const QJsonObject &objInfo); int StartSession(const QJsonObject &objInfo); /** * @brief 生成开机特别提示json字段 * * @return QJsonObject 信息数组 */ QJsonObject GenerateAgreementInfo(); void GetScreenSaverConf(QJsonObject &reqObj, QJsonObject &retObj); QJsonArray getSaverPhotoPath(QString key); int SetScreenSaverConf(const QJsonObject &objInfo); void GetLockScreenConf(QJsonObject &reqObj, QJsonObject &retObj); int SetLockScreenConf(const QJsonObject &objInfo); void GetPowerManagerConf(QJsonObject &reqObj, QJsonObject &retObj); int SetPowerManagerConf(const QJsonObject &objInfo); void GetMateBgConf(QJsonObject &reqObj, QJsonObject &retObj); int SetMateBgConf(const QJsonObject &objInfo); void GetUkccPluginsConf(QJsonObject &reqObj, QJsonObject &retObj); int SetUkccPluginsConf(const QJsonObject &objInfo); void GetThemeStyleConf(QJsonObject &reqObj, QJsonObject &retObj); int SetThemeStyleConf(const QJsonObject &objInfo); void GetSessionConf(QJsonObject &reqObj, QJsonObject &retObj); int SetSessionConf(const QJsonObject &objInfo); void GetKeyboardConf(QJsonObject &reqObj, QJsonObject &retObj); int SetKeyboardConf(const QJsonObject &objInfo); void inhibit(); void uninhibit(); bool checkStatus(int nStatus); bool GetSlpState(); void onShowBlankScreensaver(); void IsPamInAuthentication(QJsonObject &reqObj, QJsonObject &retObj); void IsPamAuthenticated(QJsonObject &reqObj, QJsonObject &retObj); void PamAuthenticationUser(QJsonObject &reqObj, QJsonObject &retObj); int PamAuthenticateUser(const QJsonObject &objInfo); /** * @brief GetAutologinUserHint get auto login user name * @param reqObj request json * @param retObj response json */ void GetAutologinUserHint(QJsonObject &reqObj, QJsonObject &retObj); int PamAuthenticateCancel(const QJsonObject &objInfo); int PamRespond(const QJsonObject &objInfo); void GetUsdMediaKeys(QJsonObject &reqObj, QJsonObject &retObj); void GetUsdMediaStateKeys(QJsonObject &reqObj, QJsonObject &retObj); int SetUsdMediaStateKeys(const QJsonObject &objInfo); void GetWeatherKeys(QJsonObject &reqObj, QJsonObject &retObj); bool usdExternalDoAction(const QJsonObject &objInfo); bool blockShortcut(const QJsonObject &objInfo); int SetPowerManager(const QJsonObject &objInfo); bool lockStateChanged(const QJsonObject &objInfo); void GetCanPowerManager(QJsonObject &reqObj, QJsonObject &retObj); QJsonArray GenerateBatteryArgsList(); QString getBatteryIconName(); bool getIsBattery(); QJsonArray getSleepLockCheck(); QJsonArray getLogoutLockCheck(); QJsonArray getShutdownLockcheck(); QJsonArray getSaverTheme(); /** * @brief delayLockScreen 延迟锁屏 */ void delayLockScreen(); /** * @brief onLockScreenTimeout 延迟锁屏处理过程 */ void onLockScreenTimeout(); void BioGetAvailableDevices(QJsonObject &reqObj, QJsonObject &retObj); void BioGetDisabledDevices(QJsonObject &reqObj, QJsonObject &retObj); void BioGetBioAuthState(QJsonObject &reqObj, QJsonObject &retObj); void BioGetCurBioInfo(QJsonObject &reqObj, QJsonObject &retObj); void BioGetDefaultDevice(QJsonObject &reqObj, QJsonObject &retObj); void BioFindDeviceById(QJsonObject &reqObj, QJsonObject &retObj); void BioFindDeviceByName(QJsonObject &reqObj, QJsonObject &retObj); int BioStartAuth(const QJsonObject &objInfo); int BioStopAuth(const QJsonObject &objInfo); bool getCurTabletMode(); int getNetworkConnectState(); void doPrepareForSleep(bool isSleep, QString strAction = ""); private: /** * @brief 初始化数据 * */ void initData(); /** * @brief 初始化连接 * */ void initConnections(); /** * @brief SendUpdateInfoSig 发送更新信息信号 * @param strJson 信息内容json * @return true 发送成功,否则失败 */ bool SendUpdateInfoSig(QString strJson); void initPublicEncrypt(); private: QSharedPointer m_lightDmHelper = nullptr; /**< lightdm工具实例 */ QSharedPointer m_login1Helper = nullptr; /**< login1工具实例 */ Configuration *m_config = nullptr; QSharedPointer m_gsettingsHelper = nullptr; QProcess m_procLockDialog; bool m_bLockState; bool m_bBlankState = false; bool m_bSlpState; int m_nStatus = 0; // 当前状态 QTimer *m_timer = nullptr; // check lockdialog is show int m_timerCount = 0; QTimer *m_timerLock = nullptr; QDBusUnixFileDescriptor m_inhibitFileDescriptor; QSharedPointer m_pamAuth = nullptr; QSharedPointer m_usdHelper = nullptr; QSharedPointer m_upowerHelper = nullptr; QSharedPointer m_accountsHelper = nullptr; bool lockState = false; QSharedPointer m_sessionHelper = nullptr; QSharedPointer m_systemsUpgradeHelper = nullptr; QSharedPointer m_sessionWatcher = nullptr; QSharedPointer m_bioAuth = nullptr; KyRSAC rsac; QByteArray pubKey, priKey; QSharedPointer m_kglobalHelper = nullptr; #ifdef SUPPORT_TABLET_MODE QSharedPointer m_libinputSwitchEvent = nullptr; #endif bool m_bBlockShortcutState = false; QSharedPointer m_networkManagerHelper = nullptr; QSharedPointer m_fileManagerHelper = nullptr; QVector m_connections; QFuture m_initEncrypt; QString m_strAction = ""; }; #endif // DBUSUPPERINTERFACE_H ukui-screensaver/src/lock-backend/sessionwatcher.cpp0000664000175000017500000001761315172041106021651 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "sessionwatcher.h" #include #include #include #include #include "../../src/common/definetypes.h" #include "../../src/common/utils.h" #include "../../src/dbusifs/freedesktophelper.h" #define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" SessionWatcher::SessionWatcher(QSharedPointer helper, QObject *parent) : QObject(parent), m_gsettingsHelper(helper) { sessionPath = qgetenv("XDG_SESSION_PATH"); // defaultFontSize = getDefaultFontSize(); defaultFontSize = 10; if (FreedesktopHelper::getInstance()->NameHasOwner(SM_DBUS_SERVICE)) { qDebug() << "Init session watcher!"; QDBusInterface *interface = new QDBusInterface( SM_DBUS_SERVICE, SM_DBUS_PATH, SM_DBUS_INTERFACE, QDBusConnection::sessionBus(), this); connect(interface, SIGNAL(StatusChanged(unsigned int)), this, SLOT(onStatusChanged(unsigned int))); } else { connect( FreedesktopHelper::getInstance(), &FreedesktopHelper::serviceStatusChanged, this, &SessionWatcher::onSessionDbusActiveChanged); } QDBusInterface *displayManagerInterface = new QDBusInterface(DM_DBUS_SERVICE, DM_DBUS_PATH, DM_DBUS_INTERFACE, QDBusConnection::systemBus(), this); connect( displayManagerInterface, SIGNAL(SessionRemoved(QDBusObjectPath)), this, SLOT(onSessionRemoved(QDBusObjectPath))); connect( m_gsettingsHelper.get(), &GSettingsHelper::lockScreenConfigChanged, this, &SessionWatcher::onLockScreenConfigChanged); // 重置屏保从不字段 bool isEnable = m_gsettingsHelper->GetLockScreenConf(KEY_IDLE_ACTIVATION_ENABLED).toBool(); if (!isEnable) { m_gsettingsHelper->SetLockScreenConf(KEY_IDLE_ACTIVATION_ENABLED, true); m_gsettingsHelper->SetLockScreenConf(KEY_IDLE_DELAY, -1); } // 重置锁屏时间(不处理超时锁屏220620) m_gsettingsHelper->SetLockScreenConf(KEY_IDLE_LOCK, -1); idleDelay = m_gsettingsHelper->GetLockScreenConf(KEY_IDLE_DELAY).toInt(); m_idleLock = m_gsettingsHelper->GetLockScreenConf(KEY_IDLE_LOCK).toInt(); QVariant lockTimeout = m_gsettingsHelper->GetSessionConf(KEY_LOCK_TIMEOUT); if (!lockTimeout.isNull()) { m_nLockTimeout = lockTimeout.toInt(); } if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_SCREENSAVER_DEFAULT)) { // 重置屏保休息时间字段 QVariant varIsShow = m_gsettingsHelper->GetScreenSaverConf(KEY_SHOW_REST_TIME); if (!varIsShow.isNull() && !varIsShow.toBool()) { m_gsettingsHelper->SetScreenSaverConf(KEY_SHOW_REST_TIME, true); m_gsettingsHelper->SetScreenSaverConf(KEY_SHOW_CUSTOM_REST_TIME, varIsShow.toBool()); m_gsettingsHelper->SetScreenSaverConf(KEY_SHOW_UKUI_REST_TIME, varIsShow.toBool()); } } // for PowerManager // 同步旧的电源管理唤醒是否需密码配置 FILE *fp = popen("xset s 0 0", "r"); if (fp) { fclose(fp); fp = NULL; } if (m_gsettingsHelper) { bool ret = m_gsettingsHelper->GetPowerManagerConf(KEY_LOCK_SUSPEND).toBool(); if (ret) { m_gsettingsHelper->SetPowerManagerConf(KEY_LOCK_SUSPEND, false); } ret = m_gsettingsHelper->GetPowerManagerConf(KEY_LOCK_HIBERNATE).toBool(); if (ret) { m_gsettingsHelper->SetPowerManagerConf(KEY_LOCK_HIBERNATE, false); } bool oldValue = m_gsettingsHelper->GetPowerManagerConf(KEY_LOCK_BLANKSCREEN).toBool(); if (!oldValue) { m_gsettingsHelper->SetLockScreenConf(KEY_CLOSE_ACTIVATION_ENABLED, oldValue); m_gsettingsHelper->SetPowerManagerConf(KEY_LOCK_BLANKSCREEN, true); } } } void SessionWatcher::onStatusChanged(unsigned int status) { if (status == SESSION_IDLE) { qDebug() << idleDelay; if (idleDelay != -1) { if (!m_timer) { m_timer = new QTimer(this); connect(m_timer, &QTimer::timeout, this, [&] { Q_EMIT sessionIdle(); m_timer->stop(); }); } int time = (idleDelay - 1) * 60000; if (time < 0) time = 0; m_timer->start(time); } if (m_idleLock != -1) { if (!m_timer2) { m_timer2 = new QTimer(this); connect(m_timer2, &QTimer::timeout, this, [&] { Q_EMIT sessionLockIdle(); m_timer2->stop(); }); } int lockTime = (m_idleLock - 1) * 60000; if (lockTime < 0) lockTime = 0; m_timer2->start(lockTime); } m_isSessionIdle = true; } else if (status == SESSION_AVAILABLE) { m_isSessionIdle = false; if (m_timer && m_timer->isActive()) { m_timer->stop(); } if (m_timer2 && m_timer2->isActive()) { m_timer2->stop(); } Q_EMIT sessionIdleExit(); } } void SessionWatcher::onSessionRemoved(const QDBusObjectPath &objectPath) { // 如果session注销了,则结束进程 if (objectPath.path() == sessionPath) exit(0); } void SessionWatcher::onLockScreenConfigChanged(QString strKey, QVariant value) { if (strKey == KEY_IDLE_DELAY) { idleDelay = value.toInt(); } else if (strKey == KEY_IDLE_LOCK) { m_idleLock = value.toInt(); } else if (strKey == KEY_LOCK_TIMEOUT) { m_nLockTimeout = value.toInt(); } } void SessionWatcher::onSessionDbusActiveChanged(const QString &strServerName, bool isActive) { if (strServerName == SM_DBUS_SERVICE && isActive) { qDebug() << "Init session watcher!"; QDBusInterface *interface = new QDBusInterface( SM_DBUS_SERVICE, SM_DBUS_PATH, SM_DBUS_INTERFACE, QDBusConnection::sessionBus(), this); connect(interface, SIGNAL(StatusChanged(unsigned int)), this, SLOT(onStatusChanged(unsigned int))); disconnect( FreedesktopHelper::getInstance(), &FreedesktopHelper::serviceStatusChanged, this, &SessionWatcher::onSessionDbusActiveChanged); } } bool SessionWatcher::isSessionIdle() { return m_isSessionIdle; } void SessionWatcher::simulateSessionActive() { QDBusInterface *sessionInterface = new QDBusInterface( SM_DBUS_SERVICE, QStringLiteral("/org/gnome/SessionManager"), SM_DBUS_SERVICE, QDBusConnection::sessionBus(), this); if (sessionInterface->isValid()) { // 异步调用,防止阻塞主进程 QDBusPendingCall call = sessionInterface->asyncCall("simulateUserActivity"); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (!reply.isValid()) { qWarning() << "session simulateUserActivity error: " << reply.error().message(); } else { qDebug() << "session simulateUserActivity done"; } watcher->deleteLater(); }); } } ukui-screensaver/src/lock-backend/bioauthenticate.h0000664000175000017500000000624015172041035021420 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BIOAUTHENTICATE_H #define BIOAUTHENTICATE_H #include #include #include #include "../../src/common/biodefines.h" class BiometricHelper; class FreedesktopHelper; class UniAuthService; class BioAuthenticate : public QObject { Q_OBJECT public: explicit BioAuthenticate(QObject *parent = nullptr); DeviceMap getAvailableDevices(int nUid); QList getDisabledDevices(int nUid); inline BIOAUTH_STATE getBioAuthState() { return m_bioauthState; } inline DeviceInfo getCurBioInfo() { return m_deviceInfo; } DeviceInfoPtr findDeviceById(int nUid, int nDevId); DeviceInfoPtr findDeviceByName(int nUid, QString strDevName); void startAuth(int uid, int nDevId); void stopAuth(); QString getDefaultDevice(int nUid, QString strUserName); QString getDefaultDevice(int nUid, QString strUserName, int bioType); public Q_SLOTS: void onServiceStatusChanged(const QString &strService, bool bActive); void onBioDeviceChanged(int drvid, int action, int devNum); void onStatusChanged(int drvid, int status); void onFrameWritten(int drvid); void onIdentifyComplete(QDBusPendingCallWatcher *watcher); void onBioAuthComplete(bool isSuccess, int nError); void onPamAuthComplete(); void onBioAuthTimer(); Q_SIGNALS: void bioServiceStatusChanged(bool bValid); void bioDeviceChanged(); void bioAuthShowMessage(QString strMsg); void bioAuthStateChanged(int nState); void bioAuthFrameData(QString strData); void bioAuthCompleted(int nUid, bool isSuccess, int nError, int nMaxFailedTime, int nFailedTime); private: void initBioService(); void initConncetions(); bool getBioAuthEnable(QString strUserName, int nType); void checkAvailableBioInfo(int nUid); void clearBioData(); void SetExtraInfo(QString extra_info, QString info_type); void startBioAuth(unsigned uTimeout = 1000); void _startAuth(); private: BiometricHelper *m_biometricHelper = nullptr; FreedesktopHelper *m_systemFdHelper = nullptr; QMap m_mapDevices; // 用户可用的设备 DeviceInfo m_deviceInfo; BIOAUTH_STATE m_bioauthState; int m_fdFrame = -1; int m_nUid = -1; bool m_isStopped = false; // 是否被强制终止 int m_maxFailedTimes; QMap> m_mapDisableDev; QMap> m_failedTimes; QTimer *m_bioTimer; int m_fTimeoutTimes = 0; QList m_listPriority; }; #endif // BIOAUTHENTICATE_H ukui-screensaver/src/lock-backend/gsettingshelper.cpp0000664000175000017500000013721615172041106022021 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "gsettingshelper.h" #include #include #include #include "../common/definetypes.h" GSettingsHelper::GSettingsHelper(QObject *parent) : QObject(parent) {} GSettingsHelper::~GSettingsHelper() {} bool GSettingsHelper::initLockScreen() { if (!m_gsLockScreen) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_SCREENSAVER)) { m_gsLockScreen = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); connect(m_gsLockScreen, &QGSettings::changed, this, &GSettingsHelper::onLockScreenConfigChanged); QStringList keys = m_gsLockScreen->keys(); if (keys.contains(KEY_IDLE_DELAY)) { m_nIdleDelay = m_gsLockScreen->get(KEY_IDLE_DELAY).toInt(); } if (keys.contains(KEY_IDLE_LOCK)) { m_nIdleLock = m_gsLockScreen->get(KEY_IDLE_LOCK).toInt(); } if (keys.contains(KEY_IDLE_ACTIVATION_ENABLED)) { m_bIdleActivationEnable = m_gsLockScreen->get(KEY_IDLE_ACTIVATION_ENABLED).toBool(); } if (keys.contains(KEY_IDLE_LOCK_ENABLED)) { m_bIdleLockEnable = m_gsLockScreen->get(KEY_IDLE_LOCK_ENABLED).toBool(); } if (keys.contains(KEY_LOCK_TIMEOUT)) { m_nLockTimeout = m_gsLockScreen->get(KEY_LOCK_TIMEOUT).toInt(); } if (keys.contains(KEY_CLOSE_ACTIVATION_ENABLED)) { m_bCloseActivationEnable = m_gsLockScreen->get(KEY_CLOSE_ACTIVATION_ENABLED).toBool(); } if (keys.contains(KEY_SLEEP_ACTIVATION_ENABLED)) { m_bSleepActivationEnable = m_gsLockScreen->get(KEY_SLEEP_ACTIVATION_ENABLED).toBool(); } if (keys.contains(KEY_LOCK_ENABLED)) { m_bLockEnable = m_gsLockScreen->get(KEY_LOCK_ENABLED).toBool(); } if (keys.contains(KEY_BACKGROUND)) { m_strBackground = m_gsLockScreen->get(KEY_BACKGROUND).toString(); } if (keys.contains(KEY_MODE)) { m_saverMode = m_gsLockScreen->get(KEY_MODE).toString(); } if (keys.contains(KEY_THEMES)) { m_saverTheme = m_gsLockScreen->get(KEY_THEMES).toStringList(); } if (keys.contains(KEY_IMAGE_TRANSITION_EFFECT)) { m_imageTSEffect = m_gsLockScreen->get(KEY_IMAGE_TRANSITION_EFFECT).toInt(); } if (keys.contains(KEY_IMAGE_SWITCH_INTERVAL)) { m_imageSwitchInterval = m_gsLockScreen->get(KEY_IMAGE_SWITCH_INTERVAL).toInt(); } } } return (bool)(m_gsLockScreen); } bool GSettingsHelper::initScreenSaver() { if (!m_gsScreenSaver) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_SCREENSAVER_DEFAULT)) { m_gsScreenSaver = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER_DEFAULT, "", this); connect(m_gsScreenSaver, &QGSettings::changed, this, &GSettingsHelper::onScreenSaverConfigChanged); QStringList keys = m_gsScreenSaver->keys(); if (keys.contains(KEY_SHOW_REST_TIME)) { m_bShowRestTime = m_gsScreenSaver->get(KEY_SHOW_REST_TIME).toBool(); } if (keys.contains(KEY_SHOW_CUSTOM_REST_TIME)) { m_bShowCustomRestTime = m_gsScreenSaver->get(KEY_SHOW_CUSTOM_REST_TIME).toBool(); } if (keys.contains(KEY_SHOW_UKUI_REST_TIME)) { m_bShowUkuiRestTime = m_gsScreenSaver->get(KEY_SHOW_UKUI_REST_TIME).toBool(); } if (keys.contains(KEY_CYCLE_TIME)) { m_nCycleTime = m_gsScreenSaver->get(KEY_CYCLE_TIME).toInt(); } if (keys.contains(KEY_AUTOMATIC_SWITCHING_ENABLE)) { m_bAutomaticSwitchingEnable = m_gsScreenSaver->get(KEY_AUTOMATIC_SWITCHING_ENABLE).toBool(); } if (keys.contains(KEY_BACKGROUND_PATH)) { if (keys.contains(KEY_VIDEO_PATH)) { m_strVideoPath = m_gsScreenSaver->get(KEY_VIDEO_PATH).toString(); } if (keys.contains(KEY_URL_ADDRESSS)) { m_sUrlAddress = m_gsScreenSaver->get(KEY_URL_ADDRESSS).toString(); } if (m_strVideoPath != "") { m_strBackgroundPath = m_strVideoPath; m_gsScreenSaver->set("background-path", m_strVideoPath); m_gsScreenSaver->set("video-path", ""); } else if (m_sUrlAddress != "") { m_strBackgroundPath = m_sUrlAddress; m_gsScreenSaver->set("background-path", m_sUrlAddress); m_gsScreenSaver->set("url-address", ""); } else { m_strBackgroundPath = m_gsScreenSaver->get(KEY_BACKGROUND_PATH).toString(); } } if (keys.contains(KEY_MYTEXT)) { m_strMyText = m_gsScreenSaver->get(KEY_MYTEXT).toString(); } if (keys.contains(KEY_TEXT_IS_CENTER)) { m_bTextIsCenter = m_gsScreenSaver->get(KEY_TEXT_IS_CENTER).toBool(); } if (keys.contains(KEY_SHOW_MESSAGE_ENABLED)) { m_bShowMessageEnable = m_gsScreenSaver->get(KEY_SHOW_MESSAGE_ENABLED).toBool(); } if (keys.contains(KEY_MESSAGE_NUMBER)) { m_nMessageNum = m_gsScreenSaver->get(KEY_MESSAGE_NUMBER).toInt(); } if (keys.contains(KEY_VIDEO_FORMAT)) { m_strVideoFormat = m_gsScreenSaver->get(KEY_VIDEO_FORMAT).toString(); } if (keys.contains(KEY_VIDEO_PATH)) { m_strVideoPath = m_gsScreenSaver->get(KEY_VIDEO_PATH).toString(); } if (keys.contains(KEY_VIDEO_SIZE)) { m_nVideoSize = m_gsScreenSaver->get(KEY_VIDEO_SIZE).toInt(); } if (keys.contains(KEY_VIDEO_WIDTH)) { m_nVideoWidth = m_gsScreenSaver->get(KEY_VIDEO_WIDTH).toInt(); } if (keys.contains(KEY_VIDEO_HEIGHT)) { m_nVideoHeight = m_gsScreenSaver->get(KEY_VIDEO_HEIGHT).toInt(); } if (keys.contains(KEY_SUB_MODE)) { m_nSubMode = m_gsScreenSaver->get(KEY_SUB_MODE).toInt(); } if (keys.contains(KEY_WEATHER_POSITION)) { m_nWeatherPosition = m_gsScreenSaver->get(KEY_WEATHER_POSITION).toInt(); } if (keys.contains(KEY_MUSIC_PATH)) { m_sMusicPath = m_gsScreenSaver->get(KEY_MUSIC_PATH).toString(); } if (keys.contains(KEY_PHOTO_CYCLE_MODE)) { m_nPhotoCycleMode = m_gsScreenSaver->get(KEY_PHOTO_CYCLE_MODE).toInt(); } if (keys.contains(KEY_PHOTO_CYCLE_INTERVAL)) { m_nPhotoCycleInterval = m_gsScreenSaver->get(KEY_PHOTO_CYCLE_INTERVAL).toInt(); } if (keys.contains(KEY_PHOTO_DEFAULT_PATH)) { m_sDefaultPhotoPath = m_gsScreenSaver->get(KEY_PHOTO_DEFAULT_PATH).toStringList(); } if (keys.contains(KEY_PHOTO_CUSTOM_PATH)) { m_sCustomPhptoPath = m_gsScreenSaver->get(KEY_PHOTO_CUSTOM_PATH).toStringList(); } if (keys.contains(KEY_PHOTO_SELECT_PATH)) { m_sSelectPhotoPath = m_gsScreenSaver->get(KEY_PHOTO_SELECT_PATH).toString(); } if (keys.contains(KEY_MUSIC_FOLLOW_SYSTEM)) { m_bMusicFollowSystem = m_gsScreenSaver->get(KEY_MUSIC_FOLLOW_SYSTEM).toBool(); } } } return (bool)(m_gsScreenSaver); } bool GSettingsHelper::initPowerManager() { if (!m_gsPowerManager) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_POWER_MANAGER)) { m_gsPowerManager = new QGSettings(GSETTINGS_SCHEMA_POWER_MANAGER, "", this); connect(m_gsPowerManager, &QGSettings::changed, this, &GSettingsHelper::onPowerManagerConfigChanged); QStringList keys = m_gsPowerManager->keys(); if (keys.contains(KEY_LOCK_SUSPEND)) { m_bLockSuspend = m_gsPowerManager->get(KEY_LOCK_SUSPEND).toBool(); } if (keys.contains(KEY_LOCK_HIBERNATE)) { m_bLockHibernate = m_gsPowerManager->get(KEY_LOCK_HIBERNATE).toBool(); } if (keys.contains(KEY_LOCK_BLANKSCREEN)) { m_bLockBlankScreen = m_gsPowerManager->get(KEY_LOCK_BLANKSCREEN).toBool(); } if (keys.contains(KEY_SLEEP_COMPUTER_AC)) { m_nSleepComputeAc = m_gsPowerManager->get(KEY_SLEEP_COMPUTER_AC).toInt(); } if (keys.contains(KEY_SLEEP_DISPLAY_AC)) { m_nSleepDisplayAc = m_gsPowerManager->get(KEY_SLEEP_DISPLAY_AC).toInt(); } if (keys.contains(KEY_BUTTON_LID_AC)) { m_strButtonLidAc = m_gsPowerManager->get(KEY_BUTTON_LID_AC).toString(); } if (keys.contains(KEY_BUTTON_POWER)) { m_strButtonPower = m_gsPowerManager->get(KEY_BUTTON_POWER).toString(); } } } return (bool)(m_gsPowerManager); } bool GSettingsHelper::initMateBg() { if (!m_gsMateBg) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_MATE_BG)) { m_gsMateBg = new QGSettings(GSETTINGS_SCHEMA_MATE_BG, "", this); connect(m_gsMateBg, &QGSettings::changed, this, &GSettingsHelper::onMateBgConfigChanged); QStringList keys = m_gsMateBg->keys(); if (keys.contains(KEY_PICTURE_FILENAME)) { m_strPicFileName = m_gsMateBg->get(KEY_PICTURE_FILENAME).toString(); } if (keys.contains(KEY_PICTURE_OPTIONS)) { m_strPicOptions = m_gsMateBg->get(KEY_PICTURE_OPTIONS).toString(); } if (keys.contains(KEY_PRIMARY_COLOR)) { m_strPrimaryColor = m_gsMateBg->get(KEY_PRIMARY_COLOR).toString(); } } } return (bool)(m_gsMateBg); } bool GSettingsHelper::initUkccPlugins() { if (!m_gsUkccPlugins) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_TIME_TYPE)) { m_gsUkccPlugins = new QGSettings(GSETTINGS_SCHEMA_TIME_TYPE, "", this); connect(m_gsUkccPlugins, &QGSettings::changed, this, &GSettingsHelper::onUkccPluginsConfigChanged); QStringList keys = m_gsUkccPlugins->keys(); if (keys.contains(KEY_HOUR_SYSTEM)) { m_nHourSystem = m_gsUkccPlugins->get(KEY_HOUR_SYSTEM).toInt(); } if (keys.contains(KEY_DATE)) { m_strDateType = m_gsUkccPlugins->get(KEY_DATE).toString(); } } } return (bool)(m_gsUkccPlugins); } bool GSettingsHelper::initThemeStyle() { if (!m_gsThemeStyle) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_STYLE)) { m_gsThemeStyle = new QGSettings(GSETTINGS_SCHEMA_STYLE, "", this); connect(m_gsThemeStyle, &QGSettings::changed, this, &GSettingsHelper::onThemeStyleConfigChanged); QStringList keys = m_gsThemeStyle->keys(); if (keys.contains(KEY_SYSTEM_FONT_SIZE)) { m_lfFontSize = m_gsThemeStyle->get(KEY_SYSTEM_FONT_SIZE).toDouble(); } if (keys.contains(KEY_THEME_COLOR)) { m_strThemeColor = m_gsThemeStyle->get(KEY_THEME_COLOR).toString(); } if (keys.contains(KEY_MENU_TRANSPARENCY)) { m_menuTransparency = m_gsThemeStyle->get(KEY_MENU_TRANSPARENCY).toInt(); } if (keys.contains(KEY_STYLE_NAME)) { m_styleName = m_gsThemeStyle->get(KEY_STYLE_NAME).toString(); } if (keys.contains(KEY_SYSTEM_FONT)) { m_font = m_gsThemeStyle->get(KEY_SYSTEM_FONT).toString(); } } } return (bool)(m_gsThemeStyle); } bool GSettingsHelper::initSession() { if (!m_gsSession) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_SESSION)) { m_gsSession = new QGSettings(GSETTINGS_SCHEMA_SESSION, "", this); connect(m_gsSession, &QGSettings::changed, this, &GSettingsHelper::onSessionConfigChanged); QStringList keys = m_gsSession->keys(); if (keys.contains(KEY_SESSION_IDLE)) { m_nSessionIdle = m_gsSession->get(KEY_SESSION_IDLE).toInt(); } if (keys.contains(KEY_SESSION_LOGOUT_MUSIC)) { m_nSessionLogout = m_gsSession->get(KEY_SESSION_LOGOUT_MUSIC).toBool(); } if (keys.contains(KEY_SESSION_POWEROFF_MUSIC)) { m_nSessionPoweroff = m_gsSession->get(KEY_SESSION_POWEROFF_MUSIC).toBool(); } } } return (bool)(m_gsSession); } bool GSettingsHelper::initKeyboard() { if (!m_gsKeyboard) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_PERIPHERALS_KEYBOARD)) { m_gsKeyboard = new QGSettings(GSETTINGS_SCHEMA_PERIPHERALS_KEYBOARD, "", this); connect(m_gsKeyboard, &QGSettings::changed, this, &GSettingsHelper::onKeyboardConfigChanged); QStringList keys = m_gsKeyboard->keys(); if (keys.contains(KEY_CAPSLOCK_STATUS)) { m_capsState = m_gsKeyboard->get(KEY_CAPSLOCK_STATUS).toBool(); } } } return (bool)(m_gsKeyboard); } bool GSettingsHelper::initUsdMediaKeys() { if (!m_gsUsdMediaKeys) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_MEDIAKEY_SCHEMA)) { m_gsUsdMediaKeys = new QGSettings(GSETTINGS_SCHEMA_MEDIAKEY_SCHEMA, "", this); connect(m_gsUsdMediaKeys, &QGSettings::changed, this, &GSettingsHelper::onUsdMediaKeysConfigChanged); QStringList keys = m_gsUsdMediaKeys->keys(); if (keys.contains(KEY_AREA_SCREENSHOT)) { m_areaScreenShot = m_gsUsdMediaKeys->get(KEY_AREA_SCREENSHOT).toString(); } if (keys.contains(KEY_AREA_SCREENSHOT2)) { m_areaScreenShot2 = m_gsUsdMediaKeys->get(KEY_AREA_SCREENSHOT2).toString(); } if (keys.contains(KEY_SCREEN_SHOT)) { m_screenShot = m_gsUsdMediaKeys->get(KEY_SCREEN_SHOT).toString(); } if (keys.contains(KEY_SCREEN_SHOT2)) { m_screenShot2 = m_gsUsdMediaKeys->get(KEY_SCREEN_SHOT2).toString(); } if (keys.contains(KEY_WINDOW_SCREENSHOT)) { m_windowScreenshot = m_gsUsdMediaKeys->get(KEY_WINDOW_SCREENSHOT).toString(); } } } return (bool)(m_gsUsdMediaKeys); } bool GSettingsHelper::initUsdMediaStateKeys() { if (!m_gsUsdMediaStateKeys) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_MEDIAKEY_STATE_SCHEMA)) { m_gsUsdMediaStateKeys = new QGSettings(GSETTINGS_SCHEMA_MEDIAKEY_STATE_SCHEMA, "", this); connect( m_gsUsdMediaStateKeys, &QGSettings::changed, this, &GSettingsHelper::onUsdMediaStateKeysConfigChanged); QStringList keys = m_gsUsdMediaStateKeys->keys(); if (keys.contains(KEY_RFKILL_STATE)) { m_rfkillState = m_gsUsdMediaStateKeys->get(KEY_RFKILL_STATE).toInt(); } } } return (bool)(m_gsUsdMediaKeys); } bool GSettingsHelper::initWeatherKeys() { if (!m_gsWeatherKeys) { if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_KYLIN_WEATHER_SCHEMA)) { m_gsWeatherKeys = new QGSettings(GSETTINGS_SCHEMA_KYLIN_WEATHER_SCHEMA, "", this); connect( m_gsWeatherKeys, &QGSettings::changed, this, &GSettingsHelper::onWeatherKeysConfigChanged); QStringList keys = m_gsWeatherKeys->keys(); if (keys.contains(KEY_WEATHER)) { m_sWeatherData = m_gsWeatherKeys->get(KEY_WEATHER).toString(); } } } return (bool)(m_gsWeatherKeys); } void GSettingsHelper::onLockScreenConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_IDLE_DELAY) { varValue = m_nIdleDelay = m_gsLockScreen->get(KEY_IDLE_DELAY).toInt(); Q_EMIT idleLockConfigChanged(m_nIdleDelay); } else if (strKey == KEY_IDLE_LOCK) { varValue = m_nIdleLock = m_gsLockScreen->get(KEY_IDLE_LOCK).toInt(); } else if (strKey == KEY_IDLE_ACTIVATION_ENABLED) { varValue = m_bIdleActivationEnable = m_gsLockScreen->get(KEY_IDLE_ACTIVATION_ENABLED).toBool(); } else if (strKey == KEY_IDLE_LOCK_ENABLED) { varValue = m_bIdleLockEnable = m_gsLockScreen->get(KEY_IDLE_LOCK_ENABLED).toBool(); } else if (strKey == KEY_LOCK_TIMEOUT) { varValue = m_nLockTimeout = m_gsLockScreen->get(KEY_LOCK_TIMEOUT).toInt(); } else if (strKey == KEY_CLOSE_ACTIVATION_ENABLED) { varValue = m_bCloseActivationEnable = m_gsLockScreen->get(KEY_CLOSE_ACTIVATION_ENABLED).toBool(); } else if (strKey == KEY_SLEEP_ACTIVATION_ENABLED) { varValue = m_bSleepActivationEnable = m_gsLockScreen->get(KEY_SLEEP_ACTIVATION_ENABLED).toBool(); } else if (strKey == KEY_LOCK_ENABLED) { varValue = m_bLockEnable = m_gsLockScreen->get(KEY_LOCK_ENABLED).toBool(); } else if (strKey == KEY_BACKGROUND) { varValue = m_strBackground = m_gsLockScreen->get(KEY_BACKGROUND).toString(); } else if (strKey == KEY_MODE) { varValue = m_saverMode = m_gsLockScreen->get(KEY_MODE).toString(); } else if (strKey == KEY_THEMES) { varValue = m_saverTheme = m_gsLockScreen->get(KEY_THEMES).toStringList(); } else if (strKey == KEY_IMAGE_TRANSITION_EFFECT) { varValue = m_imageTSEffect = m_gsLockScreen->get(KEY_IMAGE_TRANSITION_EFFECT).toInt(); } else if (strKey == KEY_IMAGE_SWITCH_INTERVAL) { varValue = m_imageSwitchInterval = m_gsLockScreen->get(KEY_IMAGE_SWITCH_INTERVAL).toInt(); } Q_EMIT lockScreenConfigChanged(strKey, varValue); } void GSettingsHelper::onScreenSaverConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_SHOW_REST_TIME) { varValue = m_bShowRestTime = m_gsScreenSaver->get(KEY_SHOW_REST_TIME).toBool(); } else if (strKey == KEY_SHOW_CUSTOM_REST_TIME) { varValue = m_bShowCustomRestTime = m_gsScreenSaver->get(KEY_SHOW_CUSTOM_REST_TIME).toBool(); } else if (strKey == KEY_SHOW_UKUI_REST_TIME) { varValue = m_bShowUkuiRestTime = m_gsScreenSaver->get(KEY_SHOW_UKUI_REST_TIME).toBool(); } else if (strKey == KEY_CYCLE_TIME) { varValue = m_nCycleTime = m_gsScreenSaver->get(KEY_CYCLE_TIME).toInt(); } else if (strKey == KEY_AUTOMATIC_SWITCHING_ENABLE) { varValue = m_bAutomaticSwitchingEnable = m_gsScreenSaver->get(KEY_AUTOMATIC_SWITCHING_ENABLE).toBool(); } else if (strKey == KEY_BACKGROUND_PATH) { varValue = m_strBackgroundPath = m_gsScreenSaver->get(KEY_BACKGROUND_PATH).toString(); // 适配下发静态屏保策略 qDebug() << "backgroundPath changed!"; m_gsScreenSaver->set("video-path", ""); m_gsScreenSaver->set("url-address", ""); } else if (strKey == KEY_MYTEXT) { varValue = m_strMyText = m_gsScreenSaver->get(KEY_MYTEXT).toString(); } else if (strKey == KEY_TEXT_IS_CENTER) { varValue = m_bTextIsCenter = m_gsScreenSaver->get(KEY_TEXT_IS_CENTER).toBool(); } else if (strKey == KEY_SHOW_MESSAGE_ENABLED) { varValue = m_bShowMessageEnable = m_gsScreenSaver->get(KEY_SHOW_MESSAGE_ENABLED).toBool(); } else if (strKey == KEY_MESSAGE_NUMBER) { varValue = m_nMessageNum = m_gsScreenSaver->get(KEY_MESSAGE_NUMBER).toInt(); } else if (strKey == KEY_VIDEO_FORMAT) { varValue = m_strVideoFormat = m_gsScreenSaver->get(KEY_VIDEO_FORMAT).toString(); } else if (strKey == KEY_VIDEO_PATH) { varValue = m_sUrlAddress = m_gsScreenSaver->get(KEY_VIDEO_PATH).toString(); if (varValue != "") { qDebug() << "videoPath changed : " << varValue; m_gsScreenSaver->set("background-path", varValue); } } else if (strKey == KEY_VIDEO_SIZE) { varValue = m_nVideoSize = m_gsScreenSaver->get(KEY_VIDEO_SIZE).toInt(); } else if (strKey == KEY_VIDEO_WIDTH) { varValue = m_nVideoWidth = m_gsScreenSaver->get(KEY_VIDEO_WIDTH).toInt(); } else if (strKey == KEY_VIDEO_HEIGHT) { varValue = m_nVideoHeight = m_gsScreenSaver->get(KEY_VIDEO_HEIGHT).toInt(); } else if (strKey == KEY_SUB_MODE) { varValue = m_nSubMode = m_gsScreenSaver->get(KEY_SUB_MODE).toInt(); } else if (strKey == KEY_WEATHER_POSITION) { varValue = m_nWeatherPosition = m_gsScreenSaver->get(KEY_WEATHER_POSITION).toInt(); } else if (strKey == KEY_MUSIC_PATH) { m_sMusicPath = m_gsScreenSaver->get(KEY_MUSIC_PATH).toString(); if (m_sMusicPath.isEmpty()) { m_sMusicPath = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); } varValue = m_sMusicPath; } else if (strKey == KEY_PHOTO_CYCLE_MODE) { varValue = m_nPhotoCycleMode = m_gsScreenSaver->get(KEY_PHOTO_CYCLE_MODE).toInt(); } else if (strKey == KEY_PHOTO_CYCLE_INTERVAL) { varValue = m_nPhotoCycleInterval = m_gsScreenSaver->get(KEY_PHOTO_CYCLE_INTERVAL).toInt(); } else if (strKey == KEY_PHOTO_DEFAULT_PATH) { varValue = m_sDefaultPhotoPath = m_gsScreenSaver->get(KEY_PHOTO_DEFAULT_PATH).toStringList(); } else if (strKey == KEY_PHOTO_CUSTOM_PATH) { varValue = m_sCustomPhptoPath = m_gsScreenSaver->get(KEY_PHOTO_CUSTOM_PATH).toStringList(); } else if (strKey == KEY_PHOTO_SELECT_PATH) { varValue = m_sSelectPhotoPath = m_gsScreenSaver->get(KEY_PHOTO_SELECT_PATH).toString(); } else if (strKey == KEY_MUSIC_FOLLOW_SYSTEM) { varValue = m_bMusicFollowSystem = m_gsScreenSaver->get(KEY_MUSIC_FOLLOW_SYSTEM).toBool(); } else if (strKey == KEY_URL_ADDRESSS) { varValue = m_sUrlAddress = m_gsScreenSaver->get(KEY_URL_ADDRESSS).toString(); if (varValue != "") { qDebug() << "urlAddress changed : " << varValue; m_gsScreenSaver->set("background-path", varValue); } } Q_EMIT screenSaverConfigChanged(strKey, varValue); } void GSettingsHelper::onPowerManagerConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_LOCK_SUSPEND) { varValue = m_bLockSuspend = m_gsPowerManager->get(KEY_LOCK_SUSPEND).toInt(); } else if (strKey == KEY_LOCK_HIBERNATE) { varValue = m_bLockHibernate = m_gsPowerManager->get(KEY_LOCK_HIBERNATE).toInt(); } else if (strKey == KEY_LOCK_BLANKSCREEN) { varValue = m_bLockBlankScreen = m_gsPowerManager->get(KEY_LOCK_BLANKSCREEN).toBool(); } else if (strKey == KEY_SLEEP_COMPUTER_AC) { varValue = m_nSleepComputeAc = m_gsPowerManager->get(KEY_SLEEP_COMPUTER_AC).toBool(); } else if (strKey == KEY_SLEEP_DISPLAY_AC) { varValue = m_nSleepDisplayAc = m_gsPowerManager->get(KEY_SLEEP_DISPLAY_AC).toInt(); } else if (strKey == KEY_BUTTON_LID_AC) { varValue = m_strButtonLidAc = m_gsPowerManager->get(KEY_BUTTON_LID_AC).toString(); } else if (strKey == KEY_BUTTON_POWER) { varValue = m_strButtonPower = m_gsPowerManager->get(KEY_BUTTON_POWER).toString(); } Q_EMIT powerManagerConfigChanged(strKey, varValue); } void GSettingsHelper::onMateBgConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_PICTURE_FILENAME) { varValue = m_strPicFileName = m_gsPowerManager->get(KEY_PICTURE_FILENAME).toString(); } else if (strKey == KEY_PICTURE_OPTIONS) { varValue = m_strPicOptions = m_gsPowerManager->get(KEY_PICTURE_OPTIONS).toString(); } else if (strKey == KEY_PRIMARY_COLOR) { varValue = m_strPrimaryColor = m_gsPowerManager->get(KEY_PRIMARY_COLOR).toString(); } Q_EMIT mateBgConfigChanged(strKey, varValue); } void GSettingsHelper::onUkccPluginsConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_HOUR_SYSTEM) { varValue = m_nHourSystem = m_gsUkccPlugins->get(KEY_HOUR_SYSTEM).toInt(); } else if (strKey == KEY_DATE) { varValue = m_strDateType = m_gsUkccPlugins->get(KEY_DATE).toString(); } Q_EMIT ukccPluginsConfigChanged(strKey, varValue); } void GSettingsHelper::onThemeStyleConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_SYSTEM_FONT_SIZE) { varValue = m_lfFontSize = m_gsThemeStyle->get(KEY_SYSTEM_FONT_SIZE).toDouble(); } else if (strKey == KEY_THEME_COLOR) { varValue = m_strThemeColor = m_gsThemeStyle->get(KEY_THEME_COLOR).toString(); } else if (strKey == KEY_SYSTEM_FONT) { varValue = m_font = m_gsThemeStyle->get(KEY_SYSTEM_FONT).toString(); } else if (strKey == KEY_MENU_TRANSPARENCY) { varValue = m_menuTransparency = m_gsThemeStyle->get(KEY_MENU_TRANSPARENCY).toInt(); } else if (strKey == KEY_STYLE_NAME) { varValue = m_styleName = m_gsThemeStyle->get(KEY_STYLE_NAME).toString(); } Q_EMIT themeStyleConfigChanged(strKey, varValue); } void GSettingsHelper::onSessionConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_SESSION_IDLE) { varValue = m_nSessionIdle = m_gsSession->get(KEY_SESSION_IDLE).toInt(); } else if (strKey == KEY_SESSION_LOGOUT_MUSIC) { m_nSessionLogout = m_gsSession->get(KEY_SESSION_LOGOUT_MUSIC).toBool(); } else if (strKey == KEY_SESSION_POWEROFF_MUSIC) { m_nSessionPoweroff = m_gsSession->get(KEY_SESSION_POWEROFF_MUSIC).toBool(); } Q_EMIT sessionConfigChanged(strKey, varValue); } void GSettingsHelper::onKeyboardConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_CAPSLOCK_STATUS) { varValue = m_capsState = m_gsKeyboard->get(KEY_CAPSLOCK_STATUS).toBool(); } Q_EMIT keyboardConfigChanged(strKey, varValue); } void GSettingsHelper::onUsdMediaKeysConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_AREA_SCREENSHOT) { varValue = m_areaScreenShot = m_gsUsdMediaKeys->get(KEY_AREA_SCREENSHOT).toString(); } else if (strKey == KEY_AREA_SCREENSHOT2) { varValue = m_areaScreenShot2 = m_gsUsdMediaKeys->get(KEY_AREA_SCREENSHOT2).toString(); } else if (strKey == KEY_SCREEN_SHOT) { varValue = m_screenShot = m_gsUsdMediaKeys->get(KEY_SCREEN_SHOT).toString(); } else if (strKey == KEY_SCREEN_SHOT2) { varValue = m_screenShot2 = m_gsUsdMediaKeys->get(KEY_SCREEN_SHOT2).toString(); } else if (strKey == KEY_WINDOW_SCREENSHOT) { varValue = m_windowScreenshot = m_gsUsdMediaKeys->get(KEY_WINDOW_SCREENSHOT).toString(); } Q_EMIT usdMediaKeysConfigChanged(strKey, varValue); } void GSettingsHelper::onUsdMediaStateKeysConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_RFKILL_STATE) { varValue = m_rfkillState = m_gsUsdMediaStateKeys->get(KEY_RFKILL_STATE).toInt(); } Q_EMIT usdMediaStateKeysConfigChanged(strKey, varValue); } void GSettingsHelper::onWeatherKeysConfigChanged(QString strKey) { QVariant varValue; if (strKey == KEY_WEATHER) { varValue = m_sWeatherData = m_gsWeatherKeys->get(KEY_WEATHER).toString(); } Q_EMIT weatherKeysConfigChanged(strKey, varValue); } QVariant GSettingsHelper::GetScreenSaverConf(QString strKey) { QVariant varValue; if (initScreenSaver()) { if (strKey == KEY_SHOW_REST_TIME) { varValue = m_bShowRestTime; } else if (strKey == KEY_SHOW_CUSTOM_REST_TIME) { varValue = m_bShowCustomRestTime; } else if (strKey == KEY_SHOW_UKUI_REST_TIME) { varValue = m_bShowUkuiRestTime; } else if (strKey == KEY_CYCLE_TIME) { varValue = m_nCycleTime; } else if (strKey == KEY_AUTOMATIC_SWITCHING_ENABLE) { varValue = m_bAutomaticSwitchingEnable; } else if (strKey == KEY_BACKGROUND_PATH) { if (m_strVideoPath != "") { varValue = m_strVideoPath; m_gsScreenSaver->set("background-path", m_strVideoPath); m_gsScreenSaver->set("video-path", ""); } else if (m_sUrlAddress != "") { varValue = m_sUrlAddress; m_gsScreenSaver->set("background-path", m_sUrlAddress); m_gsScreenSaver->set("url-address", ""); } else { varValue = m_strBackgroundPath; } } else if (strKey == KEY_MYTEXT) { varValue = m_strMyText; } else if (strKey == KEY_TEXT_IS_CENTER) { varValue = m_bTextIsCenter; } else if (strKey == KEY_SHOW_MESSAGE_ENABLED) { varValue = m_bShowMessageEnable; } else if (strKey == KEY_MESSAGE_NUMBER) { varValue = m_nMessageNum; } else if (strKey == KEY_VIDEO_FORMAT) { varValue = m_strVideoFormat; } else if (strKey == KEY_VIDEO_PATH) { varValue = m_strVideoPath; } else if (strKey == KEY_VIDEO_SIZE) { varValue = m_nVideoSize; } else if (strKey == KEY_VIDEO_WIDTH) { varValue = m_nVideoWidth; } else if (strKey == KEY_VIDEO_HEIGHT) { varValue = m_nVideoHeight; } else if (strKey == KEY_SUB_MODE) { varValue = m_nSubMode; } else if (strKey == KEY_WEATHER_POSITION) { varValue = m_nWeatherPosition; } else if (strKey == KEY_MUSIC_PATH) { if (m_sMusicPath.isEmpty()) { m_sMusicPath = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); } varValue = m_sMusicPath; } else if (strKey == KEY_PHOTO_CYCLE_MODE) { varValue = m_nPhotoCycleMode; } else if (strKey == KEY_PHOTO_CYCLE_INTERVAL) { varValue = m_nPhotoCycleInterval; } else if (strKey == KEY_PHOTO_DEFAULT_PATH) { varValue = m_sDefaultPhotoPath; } else if (strKey == KEY_PHOTO_CUSTOM_PATH) { varValue = m_sCustomPhptoPath; } else if (strKey == KEY_PHOTO_SELECT_PATH) { varValue = m_sSelectPhotoPath; } else if (strKey == KEY_MUSIC_FOLLOW_SYSTEM) { varValue = m_bMusicFollowSystem; } else if (strKey == KEY_URL_ADDRESSS) { varValue = m_sUrlAddress; } } return varValue; } bool GSettingsHelper::SetScreenSaverConf(QString strKey, QVariant value) { if (initScreenSaver()) { if (value.isValid()) { m_gsScreenSaver->set(strKey, value); if (strKey == KEY_SHOW_REST_TIME) { m_bShowRestTime = value.toBool(); } else if (strKey == KEY_SHOW_CUSTOM_REST_TIME) { m_bShowCustomRestTime = value.toBool(); } else if (strKey == KEY_SHOW_UKUI_REST_TIME) { m_bShowUkuiRestTime = value.toBool(); } else if (strKey == KEY_CYCLE_TIME) { m_nCycleTime = value.toInt(); } else if (strKey == KEY_AUTOMATIC_SWITCHING_ENABLE) { m_bAutomaticSwitchingEnable = value.toBool(); } else if (strKey == KEY_BACKGROUND_PATH) { m_strBackgroundPath = value.toString(); } else if (strKey == KEY_MYTEXT) { m_strMyText = value.toString(); } else if (strKey == KEY_TEXT_IS_CENTER) { m_bTextIsCenter = value.toBool(); } else if (strKey == KEY_SHOW_MESSAGE_ENABLED) { m_bShowMessageEnable = value.toBool(); } else if (strKey == KEY_MESSAGE_NUMBER) { m_nMessageNum = value.toInt(); } else if (strKey == KEY_VIDEO_FORMAT) { m_strVideoFormat = value.toString(); } else if (strKey == KEY_VIDEO_PATH) { m_strVideoPath = value.toString(); } else if (strKey == KEY_VIDEO_SIZE) { m_nVideoSize = value.toInt(); } else if (strKey == KEY_VIDEO_WIDTH) { m_nVideoWidth = value.toInt(); } else if (strKey == KEY_VIDEO_HEIGHT) { m_nVideoHeight = value.toInt(); }else if (strKey == KEY_SUB_MODE) { m_nSubMode = value.toInt(); } else if (strKey == KEY_WEATHER_POSITION) { m_nWeatherPosition = value.toInt(); } else if (strKey == KEY_MUSIC_PATH) { m_sMusicPath = value.toString(); } else if (strKey == KEY_PHOTO_CYCLE_MODE) { m_nPhotoCycleMode = value.toInt(); } else if (strKey == KEY_PHOTO_CYCLE_INTERVAL) { m_nPhotoCycleInterval = value.toInt(); } else if (strKey == KEY_PHOTO_DEFAULT_PATH) { m_sDefaultPhotoPath = value.toStringList(); } else if (strKey == KEY_PHOTO_CUSTOM_PATH) { m_sCustomPhptoPath = value.toStringList(); } else if (strKey == KEY_PHOTO_SELECT_PATH) { m_sSelectPhotoPath = value.toString(); } else if (strKey == KEY_MUSIC_FOLLOW_SYSTEM) { m_bMusicFollowSystem = value.toBool(); } else if (strKey == KEY_URL_ADDRESSS) { m_sUrlAddress = value.toString(); } return true; } } return false; } QVariant GSettingsHelper::ResetScreenSaverConf(QString strKey) { QVariant defValue; if (initLockScreen()) { if (m_gsLockScreen->keys().contains(strKey)) { m_gsLockScreen->reset(strKey); defValue = m_gsLockScreen->get(strKey); if (strKey == KEY_SHOW_REST_TIME) { m_bShowRestTime = defValue.toBool(); } else if (strKey == KEY_SHOW_CUSTOM_REST_TIME) { m_bShowCustomRestTime = defValue.toBool(); } else if (strKey == KEY_SHOW_UKUI_REST_TIME) { m_bShowUkuiRestTime = defValue.toBool(); } else if (strKey == KEY_CYCLE_TIME) { m_nCycleTime = defValue.toInt(); } else if (strKey == KEY_AUTOMATIC_SWITCHING_ENABLE) { m_bAutomaticSwitchingEnable = defValue.toBool(); } else if (strKey == KEY_BACKGROUND_PATH) { m_strBackgroundPath = defValue.toString(); } else if (strKey == KEY_MYTEXT) { m_strMyText = defValue.toString(); } else if (strKey == KEY_TEXT_IS_CENTER) { m_bTextIsCenter = defValue.toBool(); } else if (strKey == KEY_SHOW_MESSAGE_ENABLED) { m_bShowMessageEnable = defValue.toBool(); } else if (strKey == KEY_MESSAGE_NUMBER) { m_nMessageNum = defValue.toInt(); } else if (strKey == KEY_VIDEO_FORMAT) { m_strVideoFormat = defValue.toString(); } else if (strKey == KEY_VIDEO_PATH) { m_strVideoPath = defValue.toString(); } else if (strKey == KEY_VIDEO_SIZE) { m_nVideoSize = defValue.toInt(); } else if (strKey == KEY_VIDEO_WIDTH) { m_nVideoWidth = defValue.toInt(); } else if (strKey == KEY_VIDEO_HEIGHT) { m_nVideoHeight = defValue.toInt(); } else if (strKey == KEY_URL_ADDRESSS) { m_sUrlAddress = defValue.toString(); } } } return defValue; } QVariant GSettingsHelper::GetLockScreenConf(QString strKey) { QVariant varValue; if (initLockScreen()) { if (strKey == KEY_IDLE_DELAY) { varValue = m_nIdleDelay; } else if (strKey == KEY_IDLE_LOCK) { varValue = m_nIdleLock; } else if (strKey == KEY_IDLE_ACTIVATION_ENABLED) { varValue = m_bIdleActivationEnable; } else if (strKey == KEY_IDLE_LOCK_ENABLED) { varValue = m_bIdleLockEnable; } else if (strKey == KEY_LOCK_TIMEOUT) { varValue = m_nLockTimeout; } else if (strKey == KEY_CLOSE_ACTIVATION_ENABLED) { varValue = m_bCloseActivationEnable; } else if (strKey == KEY_SLEEP_ACTIVATION_ENABLED) { varValue = m_bSleepActivationEnable; } else if (strKey == KEY_LOCK_ENABLED) { varValue = m_bLockEnable; } else if (strKey == KEY_BACKGROUND) { varValue = m_strBackground; } else if (strKey == KEY_MODE) { varValue = m_saverMode; } else if (strKey == KEY_THEMES) { varValue = m_saverTheme; } else if (strKey == KEY_IMAGE_TRANSITION_EFFECT) { varValue = m_imageTSEffect; } else if (strKey == KEY_IMAGE_SWITCH_INTERVAL) { varValue = m_imageSwitchInterval; } } return varValue; } bool GSettingsHelper::SetLockScreenConf(QString strKey, QVariant value) { if (initLockScreen()) { if (value.isValid()) { m_gsLockScreen->set(strKey, value); if (strKey == KEY_IDLE_DELAY) { m_nIdleDelay = value.toInt(); } else if (strKey == KEY_IDLE_LOCK) { m_nIdleLock = value.toInt(); } else if (strKey == KEY_IDLE_ACTIVATION_ENABLED) { m_bIdleActivationEnable = value.toBool(); } else if (strKey == KEY_IDLE_LOCK_ENABLED) { m_bIdleLockEnable = value.toBool(); } else if (strKey == KEY_LOCK_TIMEOUT) { m_nLockTimeout = value.toInt(); } else if (strKey == KEY_CLOSE_ACTIVATION_ENABLED) { m_bCloseActivationEnable = value.toBool(); } else if (strKey == KEY_SLEEP_ACTIVATION_ENABLED) { m_bSleepActivationEnable = value.toBool(); } else if (strKey == KEY_LOCK_ENABLED) { m_bLockEnable = value.toBool(); } else if (strKey == KEY_BACKGROUND) { m_strBackground = value.toString(); } else if (strKey == KEY_MODE) { m_saverMode = value.toString(); } else if (strKey == KEY_THEMES) { m_saverTheme = value.toStringList(); } else if (strKey == KEY_IMAGE_TRANSITION_EFFECT) { m_imageTSEffect = value.toInt(); } else if (strKey == KEY_IMAGE_SWITCH_INTERVAL) { m_imageSwitchInterval = value.toInt(); } return true; } } return false; } QVariant GSettingsHelper::ResetLockScreenConf(QString strKey) { QVariant defValue; if (initLockScreen()) { if (m_gsLockScreen->keys().contains(strKey)) { m_gsLockScreen->reset(strKey); defValue = m_gsLockScreen->get(strKey); if (strKey == KEY_IDLE_DELAY) { m_nIdleDelay = defValue.toInt(); } else if (strKey == KEY_IDLE_LOCK) { m_nIdleLock = defValue.toInt(); } else if (strKey == KEY_IDLE_ACTIVATION_ENABLED) { m_bIdleActivationEnable = defValue.toBool(); } else if (strKey == KEY_IDLE_LOCK_ENABLED) { m_bIdleLockEnable = defValue.toBool(); } else if (strKey == KEY_LOCK_TIMEOUT) { m_nLockTimeout = defValue.toInt(); } else if (strKey == KEY_CLOSE_ACTIVATION_ENABLED) { m_bCloseActivationEnable = defValue.toBool(); } else if (strKey == KEY_SLEEP_ACTIVATION_ENABLED) { m_bSleepActivationEnable = defValue.toBool(); } else if (strKey == KEY_LOCK_ENABLED) { m_bLockEnable = defValue.toBool(); } else if (strKey == KEY_BACKGROUND) { m_strBackground = defValue.toString(); } else if (strKey == KEY_MODE) { m_saverMode = defValue.toString(); } else if (strKey == KEY_THEMES) { m_saverTheme = defValue.toStringList(); } else if (strKey == KEY_IMAGE_TRANSITION_EFFECT) { m_imageTSEffect = defValue.toInt(); } else if (strKey == KEY_IMAGE_SWITCH_INTERVAL) { m_imageSwitchInterval = defValue.toInt(); } } } return defValue; } QVariant GSettingsHelper::GetPowerManagerConf(QString strKey) { QVariant varValue; if (initPowerManager()) { if (strKey == KEY_LOCK_SUSPEND) { varValue = m_bLockSuspend; } else if (strKey == KEY_LOCK_HIBERNATE) { varValue = m_bLockHibernate; } else if (strKey == KEY_LOCK_BLANKSCREEN) { varValue = m_bLockBlankScreen; } else if (strKey == KEY_SLEEP_COMPUTER_AC) { varValue = m_nSleepComputeAc; } else if (strKey == KEY_SLEEP_DISPLAY_AC) { varValue = m_nSleepDisplayAc; } else if (strKey == KEY_BUTTON_LID_AC) { varValue = m_strButtonLidAc; } else if (strKey == KEY_BUTTON_POWER) { varValue = m_strButtonPower; } } return varValue; } bool GSettingsHelper::SetPowerManagerConf(QString strKey, QVariant value) { if (initPowerManager()) { if (value.isValid()) { m_gsPowerManager->set(strKey, value); if (strKey == KEY_LOCK_SUSPEND) { m_bLockSuspend = value.toBool(); } else if (strKey == KEY_LOCK_HIBERNATE) { m_bLockHibernate = value.toBool(); } else if (strKey == KEY_LOCK_BLANKSCREEN) { m_bLockBlankScreen = value.toBool(); } else if (strKey == KEY_SLEEP_COMPUTER_AC) { m_nSleepComputeAc = value.toInt(); } else if (strKey == KEY_SLEEP_DISPLAY_AC) { m_nSleepDisplayAc = value.toInt(); } else if (strKey == KEY_BUTTON_LID_AC) { m_strButtonLidAc = value.toString(); } return true; } } return false; } QVariant GSettingsHelper::GetMateBgConf(QString strKey) { QVariant varValue; if (initMateBg()) { if (strKey == KEY_PICTURE_FILENAME) { varValue = m_strPicFileName; } else if (strKey == KEY_PICTURE_OPTIONS) { varValue = m_strPicOptions; } else if (strKey == KEY_PRIMARY_COLOR) { varValue = m_strPrimaryColor; } } return varValue; } bool GSettingsHelper::SetMateBgConf(QString strKey, QVariant value) { if (initMateBg()) { if (value.isValid()) { m_gsMateBg->set(strKey, value); if (strKey == KEY_PICTURE_FILENAME) { m_strPicFileName = value.toString(); } else if (strKey == KEY_PICTURE_OPTIONS) { m_strPicOptions = value.toString(); } else if (strKey == KEY_PRIMARY_COLOR) { m_strPrimaryColor = value.toString(); } return true; } } return false; } QVariant GSettingsHelper::GetUkccPluginsConf(QString strKey) { QVariant varValue; if (initUkccPlugins()) { if (strKey == KEY_HOUR_SYSTEM) { varValue = m_nHourSystem; } else if (strKey == KEY_DATE) { varValue = m_strDateType; } } return varValue; } bool GSettingsHelper::SetUkccPluginsConf(QString strKey, QVariant value) { if (initUkccPlugins()) { if (value.isValid()) { m_gsUkccPlugins->set(strKey, value); if (strKey == KEY_HOUR_SYSTEM) { m_nHourSystem = value.toInt(); } else if (strKey == KEY_DATE) { m_strDateType = value.toString(); } return true; } } return false; } QVariant GSettingsHelper::GetThemeStyleConf(QString strKey) { QVariant varValue; if (initThemeStyle()) { if (strKey == KEY_SYSTEM_FONT_SIZE) { varValue = m_lfFontSize; } else if (strKey == KEY_THEME_COLOR) { varValue = m_strThemeColor; } else if (strKey == KEY_MENU_TRANSPARENCY) { varValue = m_menuTransparency; } else if (strKey == KEY_STYLE_NAME) { varValue = m_styleName; } else if (strKey == KEY_SYSTEM_FONT) { varValue = m_font; } } return varValue; } bool GSettingsHelper::SetThemeStyleConf(QString strKey, QVariant value) { if (initThemeStyle()) { if (value.isValid()) { m_gsThemeStyle->set(strKey, value); if (strKey == KEY_SYSTEM_FONT_SIZE) { m_lfFontSize = value.toDouble(); } else if (strKey == KEY_THEME_COLOR) { m_strThemeColor = value.toString(); } else if (strKey == KEY_MENU_TRANSPARENCY) { m_menuTransparency = value.toInt(); } else if (strKey == KEY_STYLE_NAME) { m_styleName = value.toString(); } else if (strKey == KEY_SYSTEM_FONT) { m_font = value.toString(); } return true; } } return false; } QVariant GSettingsHelper::GetSessionConf(QString strKey) { QVariant varValue; if (initSession()) { if (strKey == KEY_SESSION_IDLE) { varValue = m_nSessionIdle; } else if (strKey == KEY_SESSION_LOGOUT_MUSIC) { varValue = m_nSessionLogout; } else if (strKey == KEY_SESSION_POWEROFF_MUSIC) { varValue = m_nSessionPoweroff; } } return varValue; } bool GSettingsHelper::SetSessionConf(QString strKey, QVariant value) { if (initSession()) { if (value.isValid()) { m_gsSession->set(strKey, value); if (strKey == KEY_SESSION_IDLE) { m_nSessionIdle = value.toInt(); } else if (strKey == KEY_SESSION_LOGOUT_MUSIC) { m_nSessionLogout = value.toBool(); } else if (strKey == KEY_SESSION_POWEROFF_MUSIC) { m_nSessionPoweroff = value.toBool(); } return true; } } return false; } QVariant GSettingsHelper::GetKeyboardConf(QString strKey) { QVariant varValue; if (initKeyboard()) { if (strKey == KEY_CAPSLOCK_STATUS) { varValue = m_capsState; } } return varValue; } bool GSettingsHelper::SetKeyboardConf(QString strKey, QVariant value) { if (initKeyboard()) { if (value.isValid()) { m_gsKeyboard->set(strKey, value); if (strKey == KEY_CAPSLOCK_STATUS) { m_capsState = value.toBool(); } return true; } } return false; } QVariant GSettingsHelper::GetUsdMediaKeys(QString strKey) { QVariant varValue; if (initUsdMediaKeys()) { if (strKey == KEY_AREA_SCREENSHOT) { varValue = m_areaScreenShot; } else if (strKey == KEY_AREA_SCREENSHOT2) { varValue = m_areaScreenShot2; } else if (strKey == KEY_SCREEN_SHOT) { varValue = m_screenShot; } else if (strKey == KEY_SCREEN_SHOT2) { varValue = m_screenShot2; } else if (strKey == KEY_WINDOW_SCREENSHOT) { varValue = m_windowScreenshot; } } return varValue; } QVariant GSettingsHelper::GetUsdMediaStateKeys(QString strKey) { QVariant varValue; if (initUsdMediaStateKeys()) { if (strKey == KEY_RFKILL_STATE) { varValue = m_rfkillState; } } return varValue; } bool GSettingsHelper::SetUsdMediaStateKeys(QString strKey, QVariant value) { if (initUsdMediaStateKeys()) { if (value.isValid()) { m_gsUsdMediaStateKeys->set(strKey, value); if (strKey == KEY_RFKILL_STATE) { m_rfkillState = value.toInt(); } return true; } } return false; } QVariant GSettingsHelper::GetWeatherKeys(QString strKey) { QVariant varValue; if (initWeatherKeys()) { if (strKey == KEY_WEATHER) { varValue = m_sWeatherData; } } return varValue; } ukui-screensaver/src/lock-backend/pamauthenticate.h0000664000175000017500000000465315172041106021431 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef PAMAUTHENTICATE_H #define PAMAUTHENTICATE_H #include #include #include #include "lightdmhelper.h" #include typedef struct pam_message PAM_MESSAGE; typedef struct pam_response PAM_RESPONSE; class AuthPamThread; class QSocketNotifier; class PamAuthenticate : public QObject { Q_OBJECT public: explicit PamAuthenticate(QSharedPointer helper, QObject *parent = nullptr); public: bool inAuthentication() const; bool isAuthenticated() const; QString authenticationUser() const; QString autologinUserHint() const; public Q_SLOTS: void authenticate(const QString &username = QString()); void respond(const QString &response); void cancelAuthentication(); Q_SIGNALS: void showMessage(QString text, int type); void showPrompt(QString text, int type); void showBinaryPrompt(QVariant value,int type); void authenticationComplete(); private Q_SLOTS: void onSockRead(); void onLDMShowMessage(QString strMsg, QLightDM::Greeter::MessageType type); void onLDMShowPrompt(QString strPrompt, QLightDM::Greeter::PromptType type); private: void _respond(const struct pam_response *response); bool isJsonString(const QString &str); private: QSharedPointer m_lightdmHelper = nullptr; bool m_isInAuthentication = false; bool m_isAuthenticated = false; bool m_isOtherUser = false; AuthPamThread *m_threadAuthPam = nullptr; QSocketNotifier *m_socketNotifier = nullptr; int m_nPrompts = 0; QStringList m_responseList; QList m_messageList; int m_fdToParent[2]; int m_fdToChild[2]; QString m_strUserName; bool hasSendBiometricPam = false; }; #endif // PAMAUTHENTICATE_H ukui-screensaver/src/lock-backend/authpamthread.cpp0000664000175000017500000001720515172041106021434 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "authpamthread.h" #include #include #include #include #include #include #include #include #define PAM_SERVICE_NAME "ukui-screensaver-qt" static void writeData(int fd, const void *buf, ssize_t count); static void writeString(int fd, const char *data); static int readData(int fd, void *buf, size_t count); static char *readString(int fd); static int pam_conversation(int msgLength, const struct pam_message **msg, struct pam_response **resp, void *appData); int is_user_local(const char *username) { FILE *fp; fp = fopen("/etc/passwd", "r"); if (fp == NULL) { return 0; } char buf[1024], name[128]; while (!feof(fp)) { if (fgets(buf, sizeof(buf), fp) == NULL) { break; } sscanf(buf, "%[^:]", name); if (strcmp(name, username) == 0) { fclose(fp); return 1; } } fclose(fp); return 0; } AuthPamThread::AuthPamThread(QObject *parent) : QThread(parent) {} AuthPamThread::~AuthPamThread() {} void AuthPamThread::writeData(int fd, const void *buf, ssize_t count) { if (!m_isAuthenticating) { return; } if (write(fd, buf, count) != count) qDebug() << "write to parent failed: " << strerror(errno); } void AuthPamThread::writeString(int fd, const char *data) { int length = data ? strnlen(data, 1024) : -1; writeData(fd, &length, sizeof(length)); if (data) writeData(fd, data, sizeof(char) * length); } int AuthPamThread::readData(int fd, void *buf, size_t count) { ssize_t nRead = 0; while (true) { nRead = read(fd, buf, count); if (!m_isAuthenticating) { break; } if (nRead < 0) { if (errno == EAGAIN) { usleep(100 * 1000); continue; } else { qDebug() << "read data failed: " << strerror(errno) << errno; } } break; } return nRead; } char *AuthPamThread::readString(int fd) { int length; if (readData(fd, &length, sizeof(length)) <= 0) return NULL; if (length <= 0) length = 0; char *value = (char *)malloc(sizeof(char) * (length + 1)); readData(fd, value, length); value[length] = '\0'; return value; } static int pam_conversation(int msgLength, const struct pam_message **msg, struct pam_response **resp, void *appData) { struct pam_response *response = (struct pam_response *)calloc(msgLength, sizeof(struct pam_response)); AuthPamThread *pData = (AuthPamThread *)appData; if (!pData || pData->m_fdRead < 0 || pData->m_fdWrite < 0) { return PAM_CONV_ERR; } int authComplete = 0; pData->writeData(pData->m_fdWrite, (const void *)&authComplete, sizeof(authComplete)); pData->writeData(pData->m_fdWrite, (const void *)&msgLength, sizeof(msgLength)); // 发送pam消息 for (int i = 0; i < msgLength; i++) { const struct pam_message *m = msg[i]; // 二进制类型,适配服务器多因子认证 if (m->msg_style == PAM_BINARY_PROMPT) { pamc_bp_t binary_prompt = NULL; if (!m->msg) { qDebug() << "msg is null!"; } binary_prompt = (pamc_bp_t)m->msg; pData->writeData(pData->m_fdWrite, (const void *)&m->msg_style, sizeof(m->msg_style)); int length = sizeof(binary_prompt); pData->writeData(pData->m_fdWrite, (const void *)&length, sizeof(length)); pData->writeData(pData->m_fdWrite, (const void *)&binary_prompt, length); continue; } pData->writeData(pData->m_fdWrite, (const void *)&m->msg_style, sizeof(m->msg_style)); pData->writeString(pData->m_fdWrite, m->msg); } // 读取响应 for (int i = 0; i < msgLength; i++) { struct pam_response *r = &response[i]; if (pData->readData(pData->m_fdRead, &r->resp_retcode, sizeof(r->resp_retcode)) < 0) { break; } r->resp = pData->readString(pData->m_fdRead); } *resp = response; return PAM_SUCCESS; } void AuthPamThread::_authenticate(const char *userName) { qDebug() << "authenticate " << userName; pam_handle_t *pamh = NULL; char *newUser = NULL; int ret; int authRet; struct pam_conv conv; conv.conv = pam_conversation; conv.appdata_ptr = this; ret = pam_start(PAM_SERVICE_NAME, userName, &conv, &pamh); if (ret != PAM_SUCCESS) { qDebug() << "failed to start PAM: " << pam_strerror(NULL, ret); } authRet = pam_authenticate(pamh, 0); if (authRet == PAM_SUCCESS) { /*检测账户有效性,即使密码认证通过,如果账户锁定或无效,也无法解锁*/ int ret = pam_acct_mgmt(pamh, 0); /*如果密码过期,pam_acct_mgmt会返回请求修改口令,由于锁屏不支持修改 * 密码,会导致无法解锁,因此这里要判断返回值是否为修改口令*/ if (ret != PAM_NEW_AUTHTOK_REQD) authRet = ret; /*对于域用户,因为密码不是保持在shadow文件里,所以可以修改密码,因此 * 这里发起修改密码的请求*/ if (userName && !is_user_local(userName) && ret == PAM_NEW_AUTHTOK_REQD) { authRet = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); } } ret = pam_get_item(pamh, PAM_USER, (const void **)&newUser); if (ret != PAM_SUCCESS) { pam_end(pamh, 0); qDebug() << "failed to get username"; } if (authRet != PAM_SUCCESS) { qDebug() << "failed to acct mgmt " << pam_strerror(NULL, authRet); } if (newUser) { free(newUser); newUser = NULL; } fprintf(stderr, "authentication result: %d\n", authRet); // 发送认证结果 int authComplete = 1; writeData(m_fdWrite, (const void *)&authComplete, sizeof(authComplete)); writeData(m_fdWrite, (const void *)&authRet, sizeof(authRet)); qDebug() << "--- 认证完成"; } void AuthPamThread::startAuthPam(int fdRead, int fdWrite, QString strUserName) { if (!isRunning()) { qDebug() << "startAuthPam ----"; m_isAuthenticating = true; int nFlags = fcntl(fdRead, F_GETFL); nFlags = nFlags | O_NONBLOCK; fcntl(fdRead, F_SETFL, nFlags); m_fdRead = fdRead; m_fdWrite = fdWrite; m_strUserName = strUserName; start(); } else { qDebug() << "AuthPamThread is running!!"; } } void AuthPamThread::run() { if (m_fdRead >= 0 && m_fdWrite >= 0 && !m_strUserName.isEmpty()) { _authenticate(m_strUserName.toLocal8Bit().data()); } else { qDebug() << "AuthPamThread param error:" << m_fdRead << m_fdWrite << m_strUserName; } } void AuthPamThread::stopAuthPam() { qDebug() << "stopAuthPam begin!"; m_isAuthenticating = false; if (isRunning()) { quit(); wait(); } qDebug() << "stopAuthPam end"; } ukui-screensaver/src/lock-backend/switchuserutils.h0000664000175000017500000000476315172041106021540 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SWITCHUSERUTILS_H #define SWITCHUSERUTILS_H #include /** * @brief * 用户显示接口信息结构体 */ typedef struct UserDisplayIfInfo_s { QString strSessionPath; /**< 用户显示会话路径 */ QString strSeatPath; /**< 用户Seat路径 */ QString strUserName; /**< 用户名 */ } UserDisplayIfInfo; /** * @brief * 切换用户单元 */ class SwitchUserUtils { public: /** * @brief * 构造函数 */ SwitchUserUtils(); /** * @brief * 静态接口:获取当前进程执行所在用户名 * @return QString 用户名 */ static QString GetCurUserName(); /** * @brief * 静态接口:通过用户名获取用户id * @param strUserName * @return int */ static int GetUidByName(QString strUserName); /** * @brief * 静态接口:获取用户显示接口信息 * @param strUserName 用户名 * @return UserDisplayIfInfo 显示接口信息 */ static UserDisplayIfInfo GetUserUDII(QString strUserName); /** * @brief * 静态接口:切换用户会话 * @param seatPath 用户会话所在的lightdm seat路径 * @param toUDII 即将切换的用户lightdm显示信息 * @return int 0:激活其他会话;1:切换到登录指定用户;2:切换到登录并选择用户;<0:切换异常 */ static int SwitchToUserSession(QString seatPath, UserDisplayIfInfo &toUDII); /** * @brief * 静态接口:切换到用户的锁屏切换用户界面 * @return bool true: 切换成功;false:失败 */ static bool SwitchToUserLock(); /** * @brief CanSwitchUser 当前坐席是否允许切换用户 * @return bool true:允许,否则不允许 */ static bool CanSwitchUser(); }; #endif // SWITCHUSERUTILS_H ukui-screensaver/src/lock-backend/securityuser.h0000664000175000017500000000243115172041035021014 0ustar fengfeng/* securityuser.h * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifndef SECURITYUSER_H #define SECURITYUSER_H #include #include enum SECURITYTYPE { NORMAL, WHITELIST, BLACKLIST }; class SecurityUser { public: SecurityUser(); int getMode(); QStringList getBlackList(); QStringList getWhiteList(); bool isSecrityUser(QString username); static SecurityUser *instance(); private: int mode; QStringList blacklist; QStringList whitelist; void loadJsonFile(); static SecurityUser *instance_; }; #endif // SECURITYUSER_H ukui-screensaver/src/lock-backend/proxymodel.cpp0000664000175000017500000000647315172041035021015 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "proxymodel.h" #include KyUsersModel::KyUsersModel(QObject *parent) : QAbstractListModel(parent), m_abstractListModel(nullptr) {} QVariant KyUsersModel::data(const QModelIndex &index, int role) const { return m_abstractListModel->index(index.row(), 0).data(role); } int KyUsersModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return sourceRowCount(); } QHash KyUsersModel::roleNames() const { return m_abstractListModel == nullptr ? QHash() : m_abstractListModel->roleNames(); } void KyUsersModel::setSourceModel(QAbstractListModel *sourceModel) { if (m_abstractListModel) { disconnect( m_abstractListModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(onSourceRowsInserted(const QModelIndex &, int, int))); disconnect( m_abstractListModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(onSourceRowsRemoved(const QModelIndex &, int, int))); disconnect( m_abstractListModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(onSourceDataChanged(const QModelIndex &, const QModelIndex &))); } m_abstractListModel = sourceModel; beginResetModel(); endResetModel(); connect( m_abstractListModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(onSourceRowsInserted(const QModelIndex &, int, int))); connect( m_abstractListModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(onSourceRowsRemoved(const QModelIndex &, int, int))); connect( m_abstractListModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(onSourceDataChanged(const QModelIndex &, const QModelIndex &))); } int KyUsersModel::sourceRowCount() const { return m_abstractListModel == nullptr ? 0 : m_abstractListModel->rowCount(); } void KyUsersModel::onSourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { dataChanged(createIndex(topLeft.row(), 0), createIndex(bottomRight.row(), 0)); Q_EMIT userInfoChanged(); } void KyUsersModel::onSourceRowsInserted(const QModelIndex &parent, int start, int end) { Q_UNUSED(parent); beginInsertRows(parent, start, end); endInsertRows(); Q_EMIT userInfoChanged(); } void KyUsersModel::onSourceRowsRemoved(const QModelIndex &parent, int start, int end) { Q_UNUSED(parent); beginRemoveRows(parent, start, end); endRemoveRows(); Q_EMIT userInfoChanged(); } ukui-screensaver/src/lock-backend/pamauthenticate.cpp0000664000175000017500000003773715172041106021775 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "pamauthenticate.h" #include "../../src/common/global_utils.h" #include "authpamthread.h" #include #include #include #include #include #include #include #include #include #include #include #include PamAuthenticate::PamAuthenticate(QSharedPointer helper, QObject *parent) : QObject(parent), m_lightdmHelper(helper) { m_fdToParent[0] = -1; m_fdToParent[1] = -1; m_fdToChild[0] = -1; m_fdToChild[1] = -1; connect( m_lightdmHelper.get(), SIGNAL(showMessage(QString, QLightDM::Greeter::MessageType)), this, SLOT(onLDMShowMessage(QString, QLightDM::Greeter::MessageType))); connect( m_lightdmHelper.get(), SIGNAL(showPrompt(QString, QLightDM::Greeter::PromptType)), this, SLOT(onLDMShowPrompt(QString, QLightDM::Greeter::PromptType))); connect(m_lightdmHelper.get(), SIGNAL(authenticationComplete()), this, SIGNAL(authenticationComplete())); } bool PamAuthenticate::inAuthentication() const { if (m_isOtherUser) return m_lightdmHelper->inAuthentication(); else { return m_isInAuthentication; } } bool PamAuthenticate::isAuthenticated() const { if (m_isOtherUser) return m_lightdmHelper->isAuthenticated(); else { return m_isAuthenticated; } } QString PamAuthenticate::authenticationUser() const { if (m_isOtherUser) return m_lightdmHelper->authenticationUser(); else { return m_strUserName; } } QString PamAuthenticate::autologinUserHint() const { return m_lightdmHelper->autologinUserHint(); } void PamAuthenticate::authenticate(const QString &username) { m_isOtherUser = (getenv("USER") != username); if (username == "*guest") { m_lightdmHelper->authenticateAsGuest(); hasSendBiometricPam = false; } else if (m_isOtherUser) { m_lightdmHelper->authenticate(username); hasSendBiometricPam = false; } else { cancelAuthentication(); m_strUserName = username; if (pipe(m_fdToParent) || pipe(m_fdToChild)) qDebug() << "create pipe failed: " << strerror(errno); m_threadAuthPam = new AuthPamThread(); m_threadAuthPam->startAuthPam(m_fdToChild[0], m_fdToParent[1], username); m_isInAuthentication = true; m_socketNotifier = new QSocketNotifier(m_fdToParent[0], QSocketNotifier::Read); connect(m_socketNotifier, &QSocketNotifier::activated, this, &PamAuthenticate::onSockRead); } } void PamAuthenticate::respond(const QString &response) { if (m_isOtherUser) { if (response == BIOMETRIC_IGNORE && !hasSendBiometricPam) { return; } m_lightdmHelper->respond(response); } else { m_nPrompts--; m_responseList.push_back(response); if (m_nPrompts == 0) { // 发送响应到子进程 int j = 0; PAM_RESPONSE *resp = (PAM_RESPONSE *)calloc(m_messageList.size(), sizeof(PAM_RESPONSE)); // 响应的数量和消息的数量一致,如果消息类型不是PROMPT,则响应是空的 for (int i = 0; i < m_messageList.size(); i++) { struct pam_message message = m_messageList[i]; PAM_RESPONSE *r = &resp[i]; if (message.msg_style == PAM_PROMPT_ECHO_OFF || message.msg_style == PAM_PROMPT_ECHO_ON || (message.msg_style == PAM_BINARY_PROMPT && j < m_responseList.count())) { int respLength = m_responseList[j].length() + 1; r->resp = (char *)malloc(sizeof(char) * respLength); memcpy(r->resp, m_responseList[j].toLocal8Bit().data(), respLength); j++; } } _respond(resp); free(resp); m_messageList.clear(); m_responseList.clear(); } else if (m_nPrompts < 0) { // 当m_nPrompts小于0时,需重置为0。否则在下一次收到pam消息时,会马上发送一个空响应,导致密码为空错误提示 m_nPrompts = 0; m_responseList.clear(); } } } void PamAuthenticate::cancelAuthentication() { if (m_isOtherUser) m_lightdmHelper->cancelAuthentication(); else { if (m_threadAuthPam) { m_messageList.clear(); m_responseList.clear(); m_isAuthenticated = false; m_isInAuthentication = false; m_nPrompts = 0; if (m_socketNotifier) { disconnect(m_socketNotifier, &QSocketNotifier::activated, this, &PamAuthenticate::onSockRead); delete m_socketNotifier; m_socketNotifier = nullptr; } if (m_fdToParent[1] >= 0) { close(m_fdToParent[1]); m_fdToParent[1] = -1; } if (m_fdToChild[1] >= 0) { close(m_fdToChild[1]); m_fdToChild[1] = -1; } m_threadAuthPam->stopAuthPam(); delete m_threadAuthPam; m_threadAuthPam = nullptr; if (m_fdToParent[0] >= 0) { close(m_fdToParent[0]); m_fdToParent[0] = -1; } if (m_fdToChild[0] >= 0) { close(m_fdToChild[0]); m_fdToChild[0] = -1; } } } } static void writeData(int fd, const void *buf, ssize_t count) { if (write(fd, buf, count) != count) qDebug() << "write to parent failed: " << strerror(errno); } static void writeString(int fd, const char *data) { int length = data ? strnlen(data, 1024) : -1; writeData(fd, &length, sizeof(length)); if (data) writeData(fd, data, sizeof(char) * length); } static int readData(int fd, void *buf, size_t count) { ssize_t nRead = read(fd, buf, count); if (nRead < 0) qDebug() << "read data failed: " << strerror(errno); return nRead; } static char *readString(int fd) { int length; if (readData(fd, &length, sizeof(length)) <= 0) return NULL; if (length <= 0) length = 0; char *value = (char *)malloc(sizeof(char) * (length + 1)); readData(fd, value, length); value[length] = '\0'; return value; } void PamAuthenticate::onSockRead() { // qDebug() << "has message"; int msgLength; int authComplete = 0; readData(m_fdToParent[0], &authComplete, sizeof(authComplete)); if (authComplete) { int authRet = -1; if (readData(m_fdToParent[0], (void *)&authRet, sizeof(authRet)) <= 0) qDebug() << "get authentication result failed: " << strerror(errno); qDebug() << "result: " << authRet; m_isAuthenticated = (authRet == PAM_SUCCESS); m_isInAuthentication = false; if (m_socketNotifier) { m_socketNotifier->deleteLater(); m_socketNotifier = nullptr; } Q_EMIT authenticationComplete(); } else { readData(m_fdToParent[0], &msgLength, sizeof(msgLength)); // qDebug() << "message length: " << msgLength; for (int i = 0; i < msgLength; i++) { // 读取message struct pam_message message; pamc_bp_t binary_prompt; const char *auth_mode; int idata; int pic_len; const unsigned char *pic_buf; const char *str_data; int msgType = -1; QString binaryString; int binaryNum; QByteArray binaryArray; QPair pair; readData(m_fdToParent[0], &message.msg_style, sizeof(message.msg_style)); if (message.msg_style == PAM_BINARY_PROMPT) { int length; if (readData(m_fdToParent[0], &length, sizeof(length)) <= 0) { continue; } if (length <= 0) length = 0; readData(m_fdToParent[0], &binary_prompt, length); switch (binary_prompt->control) { case 0x10: { msgType = BinaryPromptAuthType; auth_mode = (const char *)PAM_BP_RDATA(binary_prompt); binaryString = QString::fromUtf8(auth_mode); pair.first = msgType; pair.second = binaryString; break; } case 0x11: { msgType = BinaryPromptNumber; idata = *(int *)PAM_BP_RDATA(binary_prompt); binaryNum = idata; pair.first = msgType; pair.second = binaryNum; break; } case 0x12: { msgType = BinaryPromptMessage; str_data = (const char *)PAM_BP_RDATA(binary_prompt); binaryString = QString::fromUtf8(str_data); pair.first = msgType; pair.second = binaryString; break; } case 0x13: { msgType = BinaryPromptImage; pic_len = binary_prompt->length - PAM_BP_MIN_SIZE; pic_buf = PAM_BP_RDATA(binary_prompt); binaryArray = QByteArray(reinterpret_cast(pic_buf), pic_len); pair.first = msgType; pair.second = (QString)binaryArray.toBase64(); break; } case 0x14: { msgType = BinaryPromptQrcodeStr; str_data = (const char *)PAM_BP_RDATA(binary_prompt); binaryString = QString::fromUtf8(str_data); pair.first = msgType; pair.second = binaryString; break; } case 0x15: { msgType = BinaryPromptPromptMessage; str_data = (const char *)PAM_BP_RDATA(binary_prompt); binaryString = QString::fromUtf8(str_data); pair.first = msgType; pair.second = binaryString; break; } case 0x16: { msgType = BinaryPromptErrorMessage; str_data = (const char *)PAM_BP_RDATA(binary_prompt); binaryString = QString::fromUtf8(str_data); pair.first = msgType; pair.second = binaryString; break; } case 0x17: { msgType = BinaryPromptEmergMsg; str_data = (const char *)PAM_BP_RDATA(binary_prompt); binaryString = QString::fromUtf8(str_data); pair.first = msgType; pair.second = binaryString; break; } default: { break; } } } else { message.msg = readString(m_fdToParent[0]); qDebug() << message.msg; m_messageList.push_back(message); } switch (message.msg_style) { case PAM_PROMPT_ECHO_OFF: m_nPrompts++; Q_EMIT showPrompt(message.msg, PamAuth::PromptTypeSecret); break; case PAM_PROMPT_ECHO_ON: m_nPrompts++; Q_EMIT showPrompt(message.msg, PamAuth::PromptTypeQuestion); break; case PAM_ERROR_MSG: Q_EMIT showMessage(message.msg, PamAuth::MessageTypeError); break; case PAM_TEXT_INFO: Q_EMIT showMessage(message.msg, PamAuth::MessageTypeInfo); break; case PAM_BINARY_PROMPT: { m_nPrompts++; m_messageList.push_back(message); Q_EMIT showBinaryPrompt(pair.second, pair.first); break; } } } if (m_nPrompts == 0) { // 不需要响应,发送一个空的 PAM_RESPONSE *response = (PAM_RESPONSE *)calloc(m_messageList.size(), sizeof(PAM_RESPONSE)); _respond(response); free(response); m_messageList.clear(); } } } void PamAuthenticate::_respond(const PAM_RESPONSE *response) { for (int i = 0; i < m_messageList.size(); i++) { const PAM_RESPONSE *resp = &response[i]; writeData(m_fdToChild[1], (const void *)&resp->resp_retcode, sizeof(resp->resp_retcode)); writeString(m_fdToChild[1], resp->resp); } } void PamAuthenticate::onLDMShowMessage(QString strMsg, QLightDM::Greeter::MessageType type) { Q_EMIT showMessage(strMsg, type); } void PamAuthenticate::onLDMShowPrompt(QString strPrompt, QLightDM::Greeter::PromptType type) { if (strPrompt == BIOMETRIC_PAM) { hasSendBiometricPam = true; } else { hasSendBiometricPam = false; } if (isJsonString(strPrompt)) { QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(strPrompt.toUtf8(), &error); if (error.error == QJsonParseError::NoError && jsonDoc.isObject()) { QJsonObject jsonObj = jsonDoc.object(); if (jsonObj.contains("type")) { int type = jsonObj["type"].toInt(); switch (type) { case BinaryPromptNumber: { if (jsonObj.contains("binaryInt")) { int number = jsonObj["binaryInt"].toInt(); Q_EMIT showBinaryPrompt(number, type); return; } break; } case BinaryPromptAuthType: case BinaryPromptMessage: case BinaryPromptQrcodeStr: case BinaryPromptErrorMessage: case BinaryPromptEmergMsg: case BinaryPromptPromptMessage: { if (jsonObj.contains("binaryStr")) { QString str = jsonObj["binaryStr"].toString(); Q_EMIT showBinaryPrompt(str, type); return; } break; } case BinaryPromptImage: { qDebug() << "binaryPrompt image"; Q_EMIT showBinaryPrompt("", type); return; } default: break; } } } } Q_EMIT showPrompt(strPrompt, type); } bool PamAuthenticate::isJsonString(const QString &str) { QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(str.toUtf8(), &error); return (error.error == QJsonParseError::NoError) && !doc.isNull(); } ukui-screensaver/src/lock-backend/msysdbus.h0000664000175000017500000000243315172041035020121 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef MSYSDBUS_H #define MSYSDBUS_H #include #include #include #include "kyrsac.h" class DbusUpperInterface; class MSysDbus : public QObject { Q_OBJECT public: explicit MSysDbus(DbusUpperInterface *interface, QObject *parent = nullptr); public Q_SLOTS: Q_SCRIPTABLE bool sendPassword(const QString username, QByteArray password); Q_SCRIPTABLE QString getPublicEncrypt(); Q_SIGNALS: void closed(); private: void registerUniauth(); void sendSignalLoginFinished(QString username, bool res); DbusUpperInterface *m_interface = nullptr; }; #endif ukui-screensaver/src/lock-backend/org.ukui.ScreenSaver.xml0000664000175000017500000000376215172041106022610 0ustar fengfeng ukui-screensaver/src/lock-backend/lightdmhelper.h0000664000175000017500000001277015172041035021105 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LIGHTDMHELPER_H #define LIGHTDMHELPER_H #include #include #include #include #include #include #include #include "../userinfo.h" #include "../../src/common/configuration.h" #include class SecurityUser; class KyUsersModel; class AccountsHelper; class LightDMSessionInfo; /** * @brief lightdm工具类 * */ class LightDMHelper : public QLightDM::Greeter { Q_OBJECT public: /** * @brief 构造 * * @param parent 父指针 */ explicit LightDMHelper(QSharedPointer accountHelper, Configuration *config, QObject *parent = nullptr); public: /** * @brief 设置会话 * * @param session 会话名称 * @return bool true 设置成功,其他失败 */ bool setSession(const QString &session); /** * @brief 会话 * * @return QString 会话名称 */ QString session(); /** * @brief 根据用户id查找用户信息 * * @param id 用户id * @return UserInfoPtr 用户信息指针 */ UserInfoPtr findUserByUid(uid_t id); /** * @brief 根据用户名查找用户信息 * * @param strName 用户名 * @return UserInfoPtr 用户信息指针 */ UserInfoPtr findUserByName(QString strName); /** * @brief 是否存在相同用户信息 * * @param userInfoPtr 用户信息 * @return bool true 有,false 无 */ bool hasSameUser(const UserInfoPtr userInfoPtr); /** * @brief 获取用户信息列表 * * @return QList 信息列表 */ QList getUsersInfo(); QString getCurrentUser(); bool setCurrentUser(QString strUserName); QList getSessionsInfo(); int getLoginUserCount(); public Q_SLOTS: /** * @brief 启动会话 * */ void startSession(); /** * @brief 用户更新 */ void onUsersChanged(); void onLDMSessionAdded(QDBusObjectPath objPath); void onLDMSessionRemoved(QDBusObjectPath objPath); void onUserAdded(QDBusObjectPath objPath); void onUserRemoved(QDBusObjectPath objPath); Q_SIGNALS: /** * @brief 认证成功 * * @param userName 用户名 */ void authenticationSucess(QString userName); /** * @brief 启动会话失败 * */ void startSessionFailed(); /** * @brief 用户信息更新 * */ void usersInfoChanged(); void currentUserChanged(QString strUserName); void currentSessionChanged(QString strSession); private: /** * @brief 初始化数据 * */ void initData(); /** * @brief 是否有有效用户 * * @return bool true 有,false 无 */ bool hasValidUsers(); /** * @brief 是否为同一个用户信息 * * @param UserInfoPtr 用户A * @param UserInfoPtr 用户B * @return bool true 相同,false 不同 */ bool isSameUser(UserInfoPtr, UserInfoPtr); /** * @brief 更新用户信息 * */ void updateUsersInfo(); void updateSessionsInfo(); void initLDMSessionsInfo(); bool isUserLoggined(const QString &strUserName); void initAccountsUsersInfo(); private: bool m_isShowManualLogin; /**< 是否显示手动登录 */ QString m_strSession; /**< 当前会话 */ QString m_strCurUserName; /**< 当前选择用户 */ SecurityUser *m_secUser = nullptr; /**< 安全用户信息指针 */ QLightDM::SessionsModel *m_sessionsModel; /**< lightdm 会话数据指针 */ QList m_listSessions; /**< 会话列表 */ QMap *m_mapUsers; /**< accounts 用户信息列表指针 */ QSharedPointer m_accountServiceHelper = nullptr; QMap> *m_ldmSessions; QDBusInterface *m_dbusIfsLDM; Configuration *m_configuration = nullptr; }; class LightDMSessionInfo : public QObject { Q_OBJECT public: explicit LightDMSessionInfo(const QString &strPath, QObject *parent = nullptr); virtual ~LightDMSessionInfo(); inline QString path() const { return m_strPath; } inline QString userName() const { return m_strUserName; } inline QString seatPath() const { return m_strSeatPath; } private: void initData(); void initConnections(); private Q_SLOTS: void onPropertiesChanged(const QString &, const QVariantMap &, const QStringList &); private: QString m_strPath; bool m_propertiesChangedConnected = false; QString m_strSeatPath; QString m_strUserName; }; QDebug operator<<(QDebug stream, const LightDMSessionInfo &userInfo); #endif // LIGHTDMHELPER_H ukui-screensaver/src/lock-backend/authpamthread.h0000664000175000017500000000261615172041035021102 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef AUTHPAMTHREAD_H #define AUTHPAMTHREAD_H #include class AuthPamThread : public QThread { Q_OBJECT public: AuthPamThread(QObject *parent = nullptr); virtual ~AuthPamThread(); void startAuthPam(int fdRead, int fdWrite, QString strUserName); void stopAuthPam(); void writeData(int fd, const void *buf, ssize_t count); void writeString(int fd, const char *data); int readData(int fd, void *buf, size_t count); char *readString(int fd); protected: void run(); private: void _authenticate(const char *userName); public: int m_fdRead = -1; int m_fdWrite = -1; QString m_strUserName = ""; private: bool m_isAuthenticating = false; }; #endif // AUTHPAMTHREAD_H ukui-screensaver/src/lock-backend/personalizeddata.cpp0000664000175000017500000001721215172041035022135 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "personalizeddata.h" #include #include #include #include #include #include #include #include #include #include enum enum_operation { enum_operation_update = 0, enum_operation_add, enum_operation_del, }; static const char *static_dbus_service_name = "org.ukui.UniauthBackend"; static const char *static_dbus_path = "/org/ukui/UniauthBackend"; static const char *static_dbus_interface = "org.ukui.UniauthBackend"; PersonalizedData::PersonalizedData(QString user) { KyInfo() << user; m_user = user; } PersonalizedData::~PersonalizedData() { KyInfo() << m_user; } void PersonalizedData::getJsonData(QJsonObject &json) { json.insert("user", m_user); { QJsonObject json_1; json_1["dateType"] = m_dateType; json_1["fontSize"] = m_fontSize; json_1["timeType"] = m_timeType; json_1["backgroundPath"] = m_backgroundPath; json_1["color"] = m_color; json["greeter"] = json_1; } /* { QJsonObject json_1; json_1["cursor-size"] = m_cursor_size; json_1["cursor-theme"] = m_cursor_theme; json_1["scaling-factor"] = m_scaling_factor; json["usd"] = json_1; } */ // KyInfo() << json; } void PersonalizedData::setJson(const QJsonObject &json) { if (json.contains("user") && json.value("user").toString() != m_user) { KyWarning() << m_user; return; } if (json.contains("greeter") && json.value("greeter").isObject()) { QJsonObject json_1 = json.value("greeter").toObject(); if (json_1.contains("dateType")) m_dateType = json_1["dateType"].toString(); if (json_1.contains("fontSize")) m_fontSize = json_1["fontSize"].toInt(); if (json_1.contains("timeType")) m_timeType = json_1["timeType"].toInt(); if (json_1.contains("backgroundPath")) m_backgroundPath = json_1["backgroundPath"].toString(); if (json_1.contains("color")) m_color = json_1["color"].toString(); } } /////////////////////////PersonalizedDataMng////////////////////////////////////// PersonalizedDataMng::PersonalizedDataMng(void) { //后续移动到公共接口 this->__getUserInfomation(); //后续移动到公共监控dbus接口 if (QDBusConnection::systemBus().connect( static_dbus_service_name, static_dbus_path, static_dbus_interface, "updateUserInformation", this, SLOT(updateUserInformation(QString)))) { KyDebug() << "updateUserInformation signal connected successfully to slot"; } else { KyDebug() << "updateUserInformation signal connection was not successful"; } } PersonalizedDataMng::~PersonalizedDataMng() {} QString PersonalizedDataMng::GetConfInformation(QString name) { QJsonObject json; if (m_userPersonalizedData.contains(name)) { QJsonObject json1; m_userPersonalizedData[name]->getJsonData(json1); json[name] = json1; } else if (name.isEmpty()) { for (auto &it : m_userPersonalizedData.toStdMap()) { QJsonObject json1; it.second->getJsonData(json1); json[it.first] = json1; } } QJsonDocument document; document.setObject(json); QString strJson(document.toJson(QJsonDocument::Compact)); KyInfo() << strJson; return strJson; } KylinUserDatePtr PersonalizedDataMng::GetUserPtr(QString user) { if (m_userPersonalizedData.contains(user)) { return m_userPersonalizedData[user]; } return nullptr; } void PersonalizedDataMng::updateUserInformation(QString jsonstring) { KyDebug() << jsonstring; QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonstring.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { KyInfo() << "getAgreementInfo Parse json failed!!"; } else { QJsonObject objJson = jsonDoc.object(); if (objJson.isEmpty()) { KyInfo() << "getAgreementInfo Json is null!!"; } else { if (objJson.contains("user") && objJson["user"].isString() && objJson.contains("operation")) { QString key = objJson["user"].toString(); int opt = objJson["operation"].toInt(); if (enum_operation_update == opt || enum_operation_add == opt) { if (m_userPersonalizedData.contains(key)) { m_userPersonalizedData[key]->setJson(objJson); } else { m_userPersonalizedData[key] = KylinUserDatePtr(new PersonalizedData(key)); m_userPersonalizedData[key]->setJson(objJson); } } else if (enum_operation_del) { m_userPersonalizedData.remove(key); } } } } } void PersonalizedDataMng::__getUserInfomation() { QDBusInterface iface( static_dbus_service_name, static_dbus_path, static_dbus_interface, QDBusConnection::systemBus()); QString param1 = ""; QDBusPendingCall pcall = iface.asyncCall("GetUserInformation", param1); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); this->wait_for_finish(watcher); QDBusPendingReply reply = pcall; if (reply.isError()) { KyWarning() << "getAgreementInfo error: " << reply.error().message(); } else { KyInfo() << reply.value(); QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { KyInfo() << "getAgreementInfo Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { KyInfo() << "getAgreementInfo Json is null!!"; } else { for (auto &key : objRes.keys()) { QJsonValue value = objRes.value(key); if (value.isObject()) { KyInfo() << key; if (m_userPersonalizedData.contains(key)) { m_userPersonalizedData[key]->setJson(value.toObject()); } else { m_userPersonalizedData[key] = KylinUserDatePtr(new PersonalizedData(key)); m_userPersonalizedData[key]->setJson(value.toObject()); } } } } } } } void PersonalizedDataMng::wait_for_finish(QDBusPendingCallWatcher *call) { QEventLoop eventloop; connect(call, &QDBusPendingCallWatcher::finished, this, [&](QDBusPendingCallWatcher *) { eventloop.exit(); }); eventloop.exec(); } ukui-screensaver/src/lock-backend/proxymodel.h0000664000175000017500000000310115172041035020443 0ustar fengfeng/* proxymodel.h * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifndef PROXYMODEL_H #define PROXYMODEL_H #include class KyUsersModel : public QAbstractListModel { Q_OBJECT public: KyUsersModel(QObject *parent = 0); QVariant data(const QModelIndex &index, int role) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; QHash roleNames() const; void setSourceModel(QAbstractListModel *sourceModel); public Q_SLOTS: void onSourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void onSourceRowsInserted(const QModelIndex &parent, int start, int end); void onSourceRowsRemoved(const QModelIndex &parent, int start, int end); Q_SIGNALS: void userInfoChanged(); private: QAbstractListModel *m_abstractListModel; int sourceRowCount() const; }; #endif // PROXYMODEL_H ukui-screensaver/src/lock-backend/CSingleton.h0000664000175000017500000000266415172041035020323 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef KYSINGLETON_H #define KYSINGLETON_H template class KySingleTon { public: // 创建单例实例 template static T *instance(Args &&... args) { if (m_pSingleInstance == nullptr) { m_pSingleInstance = new T(std::forward(args)...); } return m_pSingleInstance; } // 获取单例 static T *getInstance() { return m_pSingleInstance; } // 删除单例 static void destroyInstance() { delete m_pSingleInstance; m_pSingleInstance = nullptr; } private: KySingleTon(); virtual ~KySingleTon(); private: static T *m_pSingleInstance; }; template T *KySingleTon::m_pSingleInstance = nullptr; #endif ukui-screensaver/src/lock-backend/bioauthenticate.cpp0000664000175000017500000004773415172041035021770 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "bioauthenticate.h" #include "../../src/dbusifs/freedesktophelper.h" #include "../../src/dbusifs/biometrichelper.h" #include "../../src/dbusifs/uniauthservice.h" #include #include #include "../../src/dbusifs/giodbus.h" BioAuthenticate::BioAuthenticate(QObject *parent) : QObject(parent), m_bioTimer(nullptr) { m_bioauthState = BIOAUTH_IDLE; m_deviceInfo.id = -1; m_maxFailedTimes = UniAuthService::instance()->getMaxFailedTimes(); m_listPriority.clear(); m_listPriority.push_back(BioT_Face); m_listPriority.push_back(BioT_FingerPrint); m_listPriority.push_back(BioT_Iris); m_listPriority.push_back(BioT_VoicePrint); m_listPriority.push_back(BioT_FingerVein); m_listPriority.push_back(UniT_Remote); m_listPriority.push_back(UniT_General_Ukey); initBioService(); initConncetions(); } void BioAuthenticate::initConncetions() { connect(FreedesktopHelper::getInstance(false), &FreedesktopHelper::serviceStatusChanged, this, &BioAuthenticate::onServiceStatusChanged); } void BioAuthenticate::initBioService() { if (!m_biometricHelper) { if (FreedesktopHelper::getInstance(false)->NameHasOwner(BIOMETRIC_DBUS_SERVICE)) { m_biometricHelper = new BiometricHelper(this); connect(m_biometricHelper, &BiometricHelper::USBDeviceHotPlug, this, &BioAuthenticate::onBioDeviceChanged); connect(m_biometricHelper, &BiometricHelper::StatusChanged, this, &BioAuthenticate::onStatusChanged); connect(m_biometricHelper, &BiometricHelper::FrameWritten, this, &BioAuthenticate::onFrameWritten); } } } DeviceMap BioAuthenticate::getAvailableDevices(int nUid) { DeviceMap mapBioInfo; checkAvailableBioInfo(nUid); if (m_mapDevices.contains(nUid)) { mapBioInfo = m_mapDevices[nUid]; } return mapBioInfo; } QList BioAuthenticate::getDisabledDevices(int nUid) { QList listDisabled; if (m_mapDisableDev.contains(nUid)) { QMap::iterator devDisable = m_mapDisableDev[nUid].begin(); for (; devDisable != m_mapDisableDev[nUid].end(); devDisable++) { if (devDisable.value()) { listDisabled.append(devDisable.key()); } } } return listDisabled; } DeviceInfoPtr BioAuthenticate::findDeviceById(int nUid, int nDevId) { DeviceInfoPtr devInfo; if (m_mapDevices.contains(nUid)) { DeviceMap::iterator itDevInfo = m_mapDevices[nUid].begin(); for (; itDevInfo != m_mapDevices[nUid].end(); itDevInfo++) { if (itDevInfo.value()->id == nDevId) { devInfo = itDevInfo.value(); break; } } } return devInfo; } DeviceInfoPtr BioAuthenticate::findDeviceByName(int nUid, QString strDevName) { DeviceInfoPtr devInfo; if (m_mapDevices.contains(nUid)) { DeviceMap::iterator itDevInfo = m_mapDevices[nUid].begin(); for (; itDevInfo != m_mapDevices[nUid].end(); itDevInfo++) { if (itDevInfo.value()->shortName == strDevName) { devInfo = itDevInfo.value(); break; } } } return devInfo; } void BioAuthenticate::onServiceStatusChanged(const QString &strService, bool bActive) { if (strService == BIOMETRIC_DBUS_SERVICE) { if (bActive) { initBioService(); } else { clearBioData(); } Q_EMIT bioServiceStatusChanged(bActive); } } bool BioAuthenticate::getBioAuthEnable(QString strUserName, int nType) { bool isEnable = false; isEnable = UniAuthService::instance()->getBioAuthStatus(strUserName, ENABLETYPE_BIO); if (isEnable) { isEnable = UniAuthService::instance()->getBioAuthStatus(strUserName, nType); } return isEnable; } void BioAuthenticate::clearBioData() { QMap::iterator itDeviceMap = m_mapDevices.begin(); for (; itDeviceMap != m_mapDevices.end(); itDeviceMap++) { itDeviceMap.value().clear(); } m_mapDevices.clear(); } void BioAuthenticate::checkAvailableBioInfo(int nUid) { if (!m_biometricHelper) return; if (m_mapDevices.contains(nUid)) { m_mapDevices[nUid].clear(); } struct passwd *pwdInfo = getpwuid(nUid); if (pwdInfo) { int nAuthType = (pwdInfo->pw_name == getenv("USER")) ? ENABLETYPE_SAVER : ENABLETYPE_GREETER; bool isAuthEnable = getBioAuthEnable(pwdInfo->pw_name, nAuthType); bool isQRCodeEnable = UniAuthService::instance()->getQRCodeEnable(); DeviceList deviceList = m_biometricHelper->GetDevList(); QStringList listDefDevices = UniAuthService::instance()->getAllDefaultDevice(pwdInfo->pw_name); qDebug() << "BeginGetFeature------!"; FeatureMap mapFeatures = m_biometricHelper->GetUserFeatures(nUid); qDebug() << nUid << ",count:" << mapFeatures.size(); for (auto pDeviceInfo : deviceList) { if (!isAuthEnable && pDeviceInfo->deviceType <= BioT_VoicePrint) continue; if (!isQRCodeEnable && pDeviceInfo->deviceType == UniT_Remote) continue; int nFeatureCount = 0; if (mapFeatures.contains(pDeviceInfo->shortName) && listDefDevices.contains(pDeviceInfo->shortName)) { nFeatureCount = mapFeatures[pDeviceInfo->shortName].size(); qDebug() << *pDeviceInfo << ",count:" << nFeatureCount; if (nFeatureCount > 0) { if (m_mapDevices.contains(nUid)) { if (!m_mapDevices[nUid].contains(pDeviceInfo->deviceType)) m_mapDevices[nUid][pDeviceInfo->deviceType] = pDeviceInfo; } else { DeviceMap mapBioInfo; mapBioInfo[pDeviceInfo->deviceType] = pDeviceInfo; m_mapDevices[nUid] = mapBioInfo; } } } } FeatureMap::iterator itFeature = mapFeatures.begin(); for (; itFeature != mapFeatures.end(); itFeature++) { for (auto feature : itFeature.value()) { if (feature->biotype == UniT_General_Ukey) { DeviceInfoPtr pDeviceInfo = std::make_shared(); pDeviceInfo->id = -1; pDeviceInfo->shortName = "GeneralUKey"; pDeviceInfo->deviceType = feature->biotype; if (m_mapDevices.contains(nUid)) { if (!m_mapDevices[nUid].contains(pDeviceInfo->deviceType)) m_mapDevices[nUid][pDeviceInfo->deviceType] = pDeviceInfo; } else { DeviceMap mapBioInfo; mapBioInfo[pDeviceInfo->deviceType] = pDeviceInfo; m_mapDevices[nUid] = mapBioInfo; } break; } } } } } void BioAuthenticate::onBioDeviceChanged(int drvid, int action, int devNum) { Q_UNUSED(devNum); switch (action) { case ACTION_ATTACHED: { // 插入设备后,需要更新设备列表 clearBioData(); QMap::iterator itUserDev = m_mapDevices.begin(); for (; itUserDev != m_mapDevices.end(); itUserDev++) { if (itUserDev.key() < 0) continue; checkAvailableBioInfo(itUserDev.key()); } break; } case ACTION_DETACHED: { if (m_deviceInfo.id != -1 && m_deviceInfo.id == drvid) { m_bioauthState = BIOAUTH_DEVICEREMOVED; Q_EMIT bioAuthStateChanged(BIOAUTH_DEVICEREMOVED); } clearBioData(); QMap::iterator itUserDev = m_mapDevices.begin(); for (; itUserDev != m_mapDevices.end(); itUserDev++) { if (itUserDev.key() < 0) continue; checkAvailableBioInfo(itUserDev.key()); } break; } } Q_EMIT bioDeviceChanged(); } void BioAuthenticate::onStatusChanged(int drvid, int status) { if (m_bioauthState != BIOAUTH_START || m_deviceInfo.id == -1) { return; } if (drvid != m_deviceInfo.id) { return; } // // 显示来自服务的提示信息 // if(status == STATUS_NOTIFY) { // QString notifyMsg = m_biometricHelper->GetNotifyMesg(drvid); // Q_EMIT bioAuthShowMessage(notifyMsg); // } } void BioAuthenticate::onFrameWritten(int drvid) { if (m_deviceInfo.id == -1 || m_deviceInfo.id != drvid || m_bioauthState != BIOAUTH_START) { return; } if (m_fdFrame == -1) { m_fdFrame = get_server_gvariant_stdout(drvid); } if (m_fdFrame <= 0) return; lseek(m_fdFrame, 0, SEEK_SET); char base64_bufferData[1024 * 1024] = { 0 }; int rc = read(m_fdFrame, base64_bufferData, 1024 * 1024); QByteArray byteData(base64_bufferData); QString strData = byteData.toBase64(); Q_EMIT bioAuthFrameData(strData); } /** * @brief 进行生物识别认证 * @param deviceInfo 使用的设备 * @param uid 待认证的用户id */ void BioAuthenticate::startAuth(int uid, int nDevId) { if (!m_biometricHelper) { qWarning() << "BiometricProxy doesn't exist."; return; } if (nDevId < 0) { qWarning() << "Biometric Auth device invalid."; return; } qDebug() << "deviceInfo:" << nDevId; DeviceInfoPtr ptrDeviceInfo = findDeviceById(uid, nDevId); if (!ptrDeviceInfo || ptrDeviceInfo->id < -1) { qDebug() << "Could not find device info!"; return; } if (m_failedTimes.contains(uid) && m_failedTimes[uid].contains(ptrDeviceInfo->id) && m_failedTimes[uid][ptrDeviceInfo->id] >= m_maxFailedTimes) { qDebug() << "Failed MAX:" << uid << ptrDeviceInfo->id; Q_EMIT bioAuthCompleted(uid, false, -1, m_maxFailedTimes, m_failedTimes[uid][ptrDeviceInfo->id]); return; } if (m_bioauthState != BIOAUTH_IDLE) { qDebug() << "Identification is currently under way, stop it"; stopAuth(); } else { if (m_bioTimer && m_bioTimer->isActive()) m_bioTimer->stop(); } m_deviceInfo = *ptrDeviceInfo; m_nUid = uid; m_isStopped = false; m_fTimeoutTimes = 0; m_bioauthState = BIOAUTH_START; _startAuth(); Q_EMIT bioAuthStateChanged(m_bioauthState); } void BioAuthenticate::_startAuth() { if (m_nUid < 0 || m_deviceInfo.id < 0) { return; } if (m_deviceInfo.deviceType == UniT_General_Ukey) { qDebug() << QString("Identify:[drvid: %1, uid: %2]").arg(m_deviceInfo.id).arg(m_nUid); m_fdFrame = -1; m_biometricHelper->StopOps(m_deviceInfo.id); QDBusPendingCall call = m_biometricHelper->UkeyIdentify(m_deviceInfo.id, 2, m_nUid); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &BioAuthenticate::onIdentifyComplete); } else if ( (m_deviceInfo.deviceType >= BioT_FingerPrint && m_deviceInfo.deviceType <= BioT_VoicePrint) || m_deviceInfo.deviceType == UniT_Remote) { qDebug() << QString("Identify:[drvid: %1, uid: %2]").arg(m_deviceInfo.id).arg(m_nUid); m_fdFrame = -1; m_biometricHelper->StopOps(m_deviceInfo.id); QDBusPendingCall call = m_biometricHelper->Identify(m_deviceInfo.id, m_nUid); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &BioAuthenticate::onIdentifyComplete); } } void BioAuthenticate::SetExtraInfo(QString extra_info, QString info_type) { if (!m_biometricHelper) { qWarning() << "m_biometricHelper doesn't exist."; return; } m_biometricHelper->SetExtraInfo(info_type, extra_info); } void BioAuthenticate::onIdentifyComplete(QDBusPendingCallWatcher *watcher) { if (!m_biometricHelper || m_isStopped) return; QDBusPendingReply reply = *watcher; if (reply.isError()) { qWarning() << "Identify error: " << reply.error().message(); onBioAuthComplete(false, -1); return; } int result = reply.argumentAt(0).toInt(); int authUid = reply.argumentAt(1).toInt(); qDebug() << result << authUid << m_nUid; // 特征识别成功,而且用户id匹配 if (result == DBUS_RESULT_SUCCESS && authUid == m_nUid) { qDebug() << "Identify success"; onBioAuthComplete(true, 0); } else if (result == DBUS_RESULT_NOTMATCH) { // 特征识别不匹配 qDebug() << "Identify failed"; onBioAuthComplete(false, 2); } else if (result == DBUS_RESULT_ERROR) { // 识别发生错误 StatusReslut ret = m_biometricHelper->UpdateStatus(m_deviceInfo.id); qDebug() << "StatusReslut:" << ret.result << "," << ret.enable << "," << ret.devNum << "," << ret.devStatus << "," << ret.opsStatus << "," << ret.notifyMessageId; // 识别操作超时 304/404 认证超时;8 网络错误; if (ret.result == 0) { if (ret.opsStatus == OPS_IDENTIFY_TIMEOUT || ret.opsStatus == OPS_VERIFY_TIMEOUT || ret.opsStatus == 8) { // 304认证超时, 8网络异常 m_bioauthState = BIOAUTH_TIMEOUT; Q_EMIT bioAuthStateChanged(m_bioauthState); onBioAuthComplete(false, 1); } else if (ret.opsStatus == OPS_IDENTIFY_STOP_BY_USER || ret.opsStatus == OPS_VERIFY_STOP_BY_USER) { onBioAuthComplete(false, -2); // 主动停止,直接重试 } else if ( ret.opsStatus == OPS_OPEN_FAIL || ret.opsStatus == OPS_OPEN_ERROR) { // 无法打开设备(设备是坏的/被占用),直接停止 onBioAuthComplete(false, 5); } else if (ret.opsStatus >= OPS_GET_FLIST_SUCCESS && ret.opsStatus <= OPS_GET_FLIST_MAX) { onBioAuthComplete(false, -3); // 主动停止,直接重试 } else { onBioAuthComplete(false, 2); } } else { onBioAuthComplete(false, 2); } } else { onBioAuthComplete(false, 2); } } /** * @brief 终止生物识别认证 */ void BioAuthenticate::stopAuth() { if (!m_biometricHelper) { return; } m_isStopped = true; if (m_bioTimer && m_bioTimer->isActive()) m_bioTimer->stop(); if (m_bioauthState == BIOAUTH_IDLE || m_deviceInfo.id < 0) { return; } m_biometricHelper->StopOps(m_deviceInfo.id); m_bioauthState = BIOAUTH_IDLE; Q_EMIT bioAuthStateChanged(m_bioauthState); } void BioAuthenticate::onBioAuthComplete(bool isSuccess, int nError) { m_bioauthState = BIOAUTH_COMPLETE; Q_EMIT bioAuthStateChanged(m_bioauthState); if (!isSuccess) { if (nError == 5 && m_deviceInfo.id > -1) { Q_EMIT bioAuthCompleted(m_nUid, false, nError, m_maxFailedTimes, 0); return; } else if (nError >= 2 && nError != 5) if (m_deviceInfo.id > -1) { if (m_failedTimes.contains(m_nUid)) { if (m_failedTimes[m_nUid].contains(m_deviceInfo.id)) { m_failedTimes[m_nUid][m_deviceInfo.id] = m_failedTimes[m_nUid][m_deviceInfo.id] + 1; } else { m_failedTimes[m_nUid][m_deviceInfo.id] = 1; } } else { m_failedTimes[m_nUid][m_deviceInfo.id] = 1; } qDebug() << "Failed count:" << m_failedTimes[m_nUid][m_deviceInfo.id] << ",Max:" << m_maxFailedTimes; if (m_failedTimes[m_nUid][m_deviceInfo.id] >= m_maxFailedTimes) { m_mapDisableDev[m_nUid][m_deviceInfo.id] = true; Q_EMIT bioAuthCompleted( m_nUid, false, nError, m_maxFailedTimes, m_failedTimes[m_nUid][m_deviceInfo.id]); return; } Q_EMIT bioAuthCompleted( m_nUid, false, nError, m_maxFailedTimes, m_failedTimes[m_nUid][m_deviceInfo.id]); } if (nError <= 0) { qDebug() << "Biometric dbus error:" << nError; } if (m_deviceInfo.deviceType == UniT_Remote && nError == 1) { startBioAuth(10000); } else if (m_deviceInfo.deviceType == LOGINOPT_TYPE_FACE && nError == 1) { m_fTimeoutTimes += 1; if (m_fTimeoutTimes == UniAuthService::instance()->getFTimeoutTimes()) { m_fTimeoutTimes = 0; nError = 6; } else { startBioAuth(); } } else { startBioAuth(); } Q_EMIT bioAuthCompleted(m_nUid, false, nError, m_maxFailedTimes, m_failedTimes[m_nUid][m_deviceInfo.id]); } else { Q_EMIT bioAuthCompleted(m_nUid, true, 0, m_maxFailedTimes, 0); } m_bioauthState = BIOAUTH_IDLE; Q_EMIT bioAuthStateChanged(m_bioauthState); } void BioAuthenticate::onPamAuthComplete() { QMap>::iterator itFaildTimes = m_failedTimes.begin(); for (; itFaildTimes != m_failedTimes.end(); itFaildTimes++) { itFaildTimes.value().clear(); } QMap>::iterator itDisableDev = m_mapDisableDev.begin(); for (; itDisableDev != m_mapDisableDev.end(); itDisableDev++) { itDisableDev.value().clear(); } } void BioAuthenticate::onBioAuthTimer() { m_bioTimer->stop(); if (m_deviceInfo.id < 0) { return; } if (m_deviceInfo.id > -1 && m_deviceInfo.deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { // ukey时不调用ukey认证 } else { _startAuth(); } } void BioAuthenticate::startBioAuth(unsigned uTimeout) { stopAuth(); if (m_deviceInfo.id > -1 && m_deviceInfo.deviceType == UniT_General_Ukey) { // ukey时不调用ukey认证 m_bioauthState = BIOAUTH_START; Q_EMIT bioAuthStateChanged(m_bioauthState); return; } if (!m_bioTimer) { m_bioTimer = new QTimer(this); connect(m_bioTimer, SIGNAL(timeout()), this, SLOT(onBioAuthTimer())); } m_bioTimer->start(uTimeout); } QString BioAuthenticate::getDefaultDevice(int nUid, QString strUserName, int bioType) { QString defaultDeviceName = ""; if (UniAuthService::instance()->isActivatable()) { QString strDeviceName = UniAuthService::instance()->getDefaultDevice(strUserName, bioType); if (!strDeviceName.isEmpty()) { DeviceInfoPtr pDeviceInfo = findDeviceByName(nUid, strDeviceName); if (pDeviceInfo) { defaultDeviceName = strDeviceName; } } } return QString(defaultDeviceName); } QString BioAuthenticate::getDefaultDevice(int nUid, QString strUserName) { QString defaultDeviceName = ""; if (UniAuthService::instance()->isActivatable()) { for (auto bioType : m_listPriority) { QString strDeviceName = UniAuthService::instance()->getDefaultDevice(strUserName, bioType); if (!strDeviceName.isEmpty()) { DeviceInfoPtr pDeviceInfo = findDeviceByName(nUid, strDeviceName); if (pDeviceInfo) { defaultDeviceName = strDeviceName; break; } } } } return QString(defaultDeviceName); } ukui-screensaver/src/lock-backend/gsettingshelper.h0000664000175000017500000001437015172041106021461 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef GSETTINGSHELPER_H #define GSETTINGSHELPER_H #include #include #include class GSettingsHelper : public QObject { Q_OBJECT public: explicit GSettingsHelper(QObject *parent = nullptr); virtual ~GSettingsHelper(); public: QVariant GetLockScreenConf(QString strKey); bool SetLockScreenConf(QString strKey, QVariant value); QVariant ResetLockScreenConf(QString strKey); QVariant GetScreenSaverConf(QString strKey); bool SetScreenSaverConf(QString strKey, QVariant value); QVariant ResetScreenSaverConf(QString strKey); QVariant GetPowerManagerConf(QString strKey); bool SetPowerManagerConf(QString strKey, QVariant value); QVariant GetMateBgConf(QString strKey); bool SetMateBgConf(QString strKey, QVariant value); QVariant GetUkccPluginsConf(QString strKey); bool SetUkccPluginsConf(QString strKey, QVariant value); QVariant GetThemeStyleConf(QString strKey); bool SetThemeStyleConf(QString strKey, QVariant value); QVariant GetSessionConf(QString strKey); bool SetSessionConf(QString strKey, QVariant value); QVariant GetKeyboardConf(QString strKey); bool SetKeyboardConf(QString strKey, QVariant value); QVariant GetUsdMediaKeys(QString strKey); QVariant GetUsdMediaStateKeys(QString strKey); bool SetUsdMediaStateKeys(QString strKey, QVariant value); QVariant GetWeatherKeys(QString strKey); public Q_SLOTS: void onLockScreenConfigChanged(QString strKey); void onScreenSaverConfigChanged(QString strKey); void onPowerManagerConfigChanged(QString strKey); void onMateBgConfigChanged(QString strKey); void onUkccPluginsConfigChanged(QString strKey); void onThemeStyleConfigChanged(QString strKey); void onSessionConfigChanged(QString strKey); void onKeyboardConfigChanged(QString strKey); void onUsdMediaKeysConfigChanged(QString strKey); void onUsdMediaStateKeysConfigChanged(QString strKey); void onWeatherKeysConfigChanged(QString strKey); Q_SIGNALS: void lockScreenConfigChanged(QString strKey, QVariant value); void screenSaverConfigChanged(QString strKey, QVariant value); void powerManagerConfigChanged(QString strKey, QVariant value); void mateBgConfigChanged(QString strKey, QVariant value); void ukccPluginsConfigChanged(QString strKey, QVariant value); void themeStyleConfigChanged(QString strKey, QVariant value); void sessionConfigChanged(QString strKey, QVariant value); void keyboardConfigChanged(QString strKey, QVariant value); void usdMediaKeysConfigChanged(QString strKey, QVariant value); void usdMediaStateKeysConfigChanged(QString strKey, QVariant value); void idleLockConfigChanged(int idleLock); void weatherKeysConfigChanged(QString strKey, QVariant value); private: bool initLockScreen(); bool initScreenSaver(); bool initPowerManager(); bool initMateBg(); bool initUkccPlugins(); bool initThemeStyle(); bool initSession(); bool initKeyboard(); bool initUsdMediaKeys(); bool initUsdMediaStateKeys(); bool initWeatherKeys(); private: QGSettings *m_gsLockScreen = nullptr; QGSettings *m_gsScreenSaver = nullptr; QGSettings *m_gsPowerManager = nullptr; QGSettings *m_gsMateBg = nullptr; QGSettings *m_gsUkccPlugins = nullptr; QGSettings *m_gsThemeStyle = nullptr; QGSettings *m_gsSession = nullptr; QGSettings *m_gsKeyboard = nullptr; QGSettings *m_gsUsdMediaKeys = nullptr; QGSettings *m_gsUsdMediaStateKeys = nullptr; QGSettings *m_gsWeatherKeys = nullptr; bool m_bShowRestTime = false; bool m_bShowCustomRestTime = false; bool m_bShowUkuiRestTime = false; int m_nCycleTime = 1; bool m_bAutomaticSwitchingEnable = false; QString m_strBackgroundPath; QString m_strMyText; bool m_bTextIsCenter = true; bool m_bShowMessageEnable = false; int m_nMessageNum = 0; QString m_strVideoFormat; QString m_strVideoPath; int m_nVideoSize = 0; int m_nVideoWidth = 0; int m_nVideoHeight = 0; int m_nSubMode = -1; int m_nWeatherPosition = 0; QString m_sMusicPath; int m_nPhotoCycleMode = -1; int m_nPhotoCycleInterval = -1; QStringList m_sDefaultPhotoPath; QStringList m_sCustomPhptoPath; QString m_sSelectPhotoPath; bool m_bMusicFollowSystem; QString m_sUrlAddress; int m_nIdleDelay = -1; int m_nIdleLock = -1; bool m_bIdleActivationEnable = false; bool m_bIdleLockEnable = false; int m_nLockTimeout = -1; bool m_bCloseActivationEnable = false; bool m_bSleepActivationEnable = false; bool m_bLockEnable = false; QString m_strBackground; bool m_bLockSuspend = false; bool m_bLockHibernate = false; bool m_bLockBlankScreen = false; int m_nSleepComputeAc = -1; int m_nSleepDisplayAc = -1; QString m_strButtonLidAc; QString m_strButtonPower; QString m_strPicFileName; QString m_strPicOptions; QString m_strPrimaryColor; int m_nHourSystem = 0; QString m_strDateType; double m_lfFontSize = 0.0; QString m_strThemeColor; QString m_font; int m_menuTransparency = 0; QString m_styleName; int m_nSessionIdle = -1; bool m_nSessionLogout = false; bool m_nSessionPoweroff = false; bool m_capsState = false; QString m_areaScreenShot; QString m_areaScreenShot2; QString m_screenShot; QString m_screenShot2; QString m_windowScreenshot; QString m_saverMode; QStringList m_saverTheme; int m_imageTSEffect; int m_imageSwitchInterval; int m_rfkillState = -1; QString m_sWeatherData; }; #endif // GSETTINGSHELPER_H ukui-screensaver/src/lock-backend/main.cpp0000664000175000017500000001357515172041035017540 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "qtsinglecoreapplication.h" #include "utils.h" #include "dbusupperinterface.h" #include "screensaveradaptor.h" #include "personalizeddata.h" #include "msysdbus.h" #include "../ukccplugins/sessiondbus/screenlockinterface.h" #include "../ukccplugins/sessiondbus/screensaverinterface.h" #include int main(int argc, char *argv[]) { syslog(LOG_INFO, "[ukui-screensaver-backend] startup!!"); initUkuiLog4qt("ukui-screensaver-backend"); // 重启或关机时不被session关掉 qunsetenv("SESSION_MANAGER"); QString strDisplay = ""; if (QString(qgetenv("XDG_SESSION_TYPE")) == "wayland") { strDisplay = QLatin1String(getenv("WAYLAND_DISPLAY")); } else { strDisplay = QLatin1String(getenv("DISPLAY")); } QString singleId = QString("ukui-screensaver-backend" + strDisplay); QtSingleCoreApplication a(singleId, argc, argv); if (a.isRunning()) { qDebug() << "There is an instance running"; return 0; } qInfo() << "ukui screensaver backend Started!!"; // 会话管理器退出时,锁屏后端也退出 prctl(PR_SET_PDEATHSIG, SIGKILL); // 命令行参数解析 QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", "Backend for the ukui ScreenSaver.")); parser.addHelpOption(); parser.addVersionOption(); parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); QCommandLineOption lockStartupOption( QStringLiteral("lock-startup"), QCoreApplication::translate("main", "lock the screen by startup")); parser.addOptions({ lockStartupOption }); parser.process(a.arguments()); // 检查该程序是否已经有实例在运行 QDBusInterface *checkInterface = new QDBusInterface( "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", QDBusConnection::sessionBus()); QDBusReply ret = checkInterface->call("NameHasOwner", "cn.kylinos.ScreenSaver"); if (ret.value()) { qDebug() << "There is an instance running"; return EXIT_FAILURE; } // 注册DBus syslog(LOG_INFO, "[ukui-screensaver-backend] DbusUpperInterface initing!!"); DbusUpperInterface interface; syslog(LOG_INFO, "[ukui-screensaver-backend] DbusUpperInterface inited!!"); ScreenSaverAdaptor adaptor(&interface); QDBusConnection service = QDBusConnection::sessionBus(); QString sessionDbus = SS_DBUS_SERVICE; if (!service.registerService(sessionDbus)) { QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); sessionDbus = QString("%1%2").arg(QString(SS_DBUS_SERVICE)).arg(displayNum); if (!service.registerService(sessionDbus)) { qDebug() << service.lastError().message(); return EXIT_FAILURE; } } if (!service.registerObject( SS_DBUS_PATH, SS_DBUS_SERVICE, &adaptor, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) { qDebug() << service.lastError().message(); return EXIT_FAILURE; } // 注册控制面板插件接口服务 if (!isGreeterMode()) { ScreenlockInterface *lockInterface = new ScreenlockInterface(&interface, &interface); if (!service.registerObject( SL_SESSION_DBUS_PATH, lockInterface, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllProperties | QDBusConnection::ExportAllSignals)) { qDebug() << service.lastError().message(); return EXIT_FAILURE; } ScreensaverInterface *saverInterface = new ScreensaverInterface(&interface, &interface); if (!service.registerObject( SS_SESSION_DBUS_PATH, saverInterface, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllProperties | QDBusConnection::ExportAllSignals)) { qDebug() << service.lastError().message(); return EXIT_FAILURE; } } qDebug() << service.baseService(); QObject::connect(checkInterface, SIGNAL(NameLost(QString)), &interface, SLOT(onNameLost(QString))); if (parser.isSet(lockStartupOption)) { syslog(LOG_INFO, "[ukui-screensaver-backend] lock startup mode begin!!"); interface.LockStartupMode(); syslog(LOG_INFO, "[ukui-screensaver-backend] lock startup mode end!!"); } interface.init(); MSysDbus m_sysDbus(&interface); QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); QString sysService = "org.ukui.screensaver._" + displayNum; if (!QDBusConnection::systemBus().registerService("org.ukui.screensaver")) { qDebug() << "registerService failed"; } QDBusConnection::systemBus().registerObject( "/", "org.ukui.screensaver", &m_sysDbus, QDBusConnection::ExportAllSlots); syslog(LOG_INFO, "[ukui-screensaver-backend] application event loop begin!!"); return a.exec(); } ukui-screensaver/src/lock-backend/lightdmhelper.cpp0000664000175000017500000003502715172041106021437 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "lightdmhelper.h" #include #include #include #include "securityuser.h" #include "../../src/common/global_utils.h" #include #include "proxymodel.h" #include "../../src/dbusifs/accountshelper.h" #include "../../src/common/definetypes.h" #include "../../src/dbusifs/uniauthservice.h" #include "switchuserutils.h" #include #include #include #ifdef WITHKYSEC #include #include #endif LightDMHelper::LightDMHelper(QSharedPointer accountHelper, Configuration *config, QObject *parent) : QLightDM::Greeter(parent) , m_sessionsModel(nullptr) , m_secUser(SecurityUser::instance()) , m_strCurUserName("") , m_accountServiceHelper(accountHelper) , m_configuration(config) , m_ldmSessions(new QMap>()) , m_mapUsers(new QMap()) , m_dbusIfsLDM(nullptr) { // 连接到lightdm if (isGreeterMode() && !connectToDaemonSync()) { qDebug() << "connect to Daemon failed"; exit(1); } initData(); } void LightDMHelper::initData() { // 获取会话信息 m_sessionsModel = new QLightDM::SessionsModel(QLightDM::SessionsModel::LocalSessions, this); updateSessionsInfo(); setSession(defaultSessionHint()); // 获取用户信息 initLDMSessionsInfo(); m_isShowManualLogin = showManualLoginHint(); if (!hideUsersHint()) { initAccountsUsersInfo(); if (hasValidUsers() == 0) { m_isShowManualLogin = true; } } else { qDebug() << "hide users, show manual"; m_isShowManualLogin = true; } updateUsersInfo(); if (isGreeterMode() && UniAuthService::instance()) { UniAuthService::instance()->AsyncQuickLoginUser(""); } } bool LightDMHelper::setSession(const QString &session) { bool bRet = false; if (!m_listSessions.contains(session)) return bRet; if (!session.isEmpty() && session != m_strSession) { m_strSession = session; bRet = true; } else if (!session.isEmpty() && session == m_strSession) { bRet = true; } if (bRet) Q_EMIT currentSessionChanged(m_strSession); return bRet; } QString LightDMHelper::session() { return m_strSession; } QList LightDMHelper::getSessionsInfo() { return m_listSessions; } int LightDMHelper::getLoginUserCount() { QList userInfos = m_mapUsers->values(); int loginUserCount = 0; for (auto user : userInfos) { if (isUserLoggined(user->name())) { loginUserCount += 1; } } return loginUserCount; } void LightDMHelper::startSession() { if (isAuthenticated()) { if (UniAuthService::instance()) { UniAuthService::instance()->SaveLastLoginUser(m_strCurUserName); // 三权分立不保存秒级登录用户信息 #ifdef WITHKYSEC if (kysec_is_disabled() || !kysec_get_3adm_status()) { #endif UniAuthService::instance()->SaveQuickLoginUser(m_strCurUserName); #ifdef WITHKYSEC } #endif } Q_EMIT authenticationSucess(m_strCurUserName); } if (!startSessionSync(m_strSession)) { Q_EMIT startSessionFailed(); Q_EMIT showMessage(tr("failed to start session."), QLightDM::Greeter::MessageTypeError); } } QString LightDMHelper::getCurrentUser() { return m_strCurUserName; } bool LightDMHelper::setCurrentUser(QString strUserName) { bool bRet = false; if (!strUserName.isEmpty() && strUserName != m_strCurUserName) { if (findUserByName(strUserName)) { m_strCurUserName = strUserName; bRet = true; } else if (struct passwd *pwinfo = getpwnam(strUserName.toLatin1().data())) { m_strCurUserName = strUserName; bRet = true; } } else if (!strUserName.isEmpty() && strUserName == m_strCurUserName) { bRet = true; } if (bRet) { Q_EMIT currentUserChanged(m_strCurUserName); } return bRet; } UserInfoPtr LightDMHelper::findUserByUid(uid_t id) { for (auto userInfoPtr : m_mapUsers->values()) { if (userInfoPtr->uid() == id) { return userInfoPtr; } } return nullptr; } UserInfoPtr LightDMHelper::findUserByName(QString strName) { for (auto userInfoPtr : m_mapUsers->values()) { if (userInfoPtr->name() == strName) { return userInfoPtr; } } return nullptr; } bool LightDMHelper::hasSameUser(const UserInfoPtr userInfoPtr) { for (auto user : m_mapUsers->values()) { if (user->uid() == userInfoPtr->uid() && user->name() == userInfoPtr->name()) { return true; } } return false; } bool LightDMHelper::hasValidUsers() { int count = 0; for (auto user : m_mapUsers->values()) { if (m_secUser->isSecrityUser(user->name())) count++; } return (bool)(count > 0); } bool LightDMHelper::isSameUser(UserInfoPtr userA, UserInfoPtr userB) { return userA->uid() == userB->uid() && userA->fullName() == userB->fullName() && userA->name() == userB->name() && userA->lang() == userB->lang() && userA->isLoggedIn() == userB->isLoggedIn() && userA->headImage() == userB->headImage() && userA->backGround() == userB->backGround(); } void LightDMHelper::onUsersChanged() { updateUsersInfo(); Q_EMIT usersInfoChanged(); } void LightDMHelper::updateUsersInfo() { QList userInfos = m_mapUsers->values(); for (auto user : userInfos) { if (isUserLoggined(user->name())) { user->updateLoggedIn(true); } else { user->updateLoggedIn(false); } } if (m_isShowManualLogin && (isGreeterMode() || (SwitchUserUtils::CanSwitchUser()))) { std::shared_ptr userInfoPtr = std::make_shared(); userInfoPtr->updateFullName(tr("Login")); userInfoPtr->updateName("*login"); if (!hasSameUser(userInfoPtr)) { const QString path = userInfoPtr->path().isEmpty() ? userInfoPtr->name() : userInfoPtr->path(); m_mapUsers->insert(path, userInfoPtr); } } if (hasGuestAccountHint()) { std::shared_ptr userInfoPtr = std::make_shared(); userInfoPtr->updateFullName(tr("Guest")); userInfoPtr->updateName("*guest"); if (!hasSameUser(userInfoPtr)) { const QString path = userInfoPtr->path().isEmpty() ? userInfoPtr->name() : userInfoPtr->path(); m_mapUsers->insert(path, userInfoPtr); } } } void LightDMHelper::updateSessionsInfo() { if (m_sessionsModel) { for (int i = 0; i < m_sessionsModel->rowCount(QModelIndex()); i++) { QString name = m_sessionsModel->index(i).data(QLightDM::SessionsModel::KeyRole).toString(); if (!name.isEmpty()) { m_listSessions.append(name); } } } } QList LightDMHelper::getUsersInfo() { return m_mapUsers->values(); } bool LightDMHelper::isUserLoggined(const QString &strUserName) { QList> sessionList = m_ldmSessions->values(); auto it = std::find_if( sessionList.begin(), sessionList.end(), [strUserName](std::shared_ptr &session) { return session->userName() == strUserName; }); if (it != sessionList.end()) { return true; } return false; } void LightDMHelper::initLDMSessionsInfo() { if (!m_dbusIfsLDM) { m_dbusIfsLDM = new QDBusInterface(DM_DBUS_SERVICE, DM_DBUS_PATH, DM_DBUS_INTERFACE, QDBusConnection::systemBus(), this); connect(m_dbusIfsLDM, SIGNAL(SessionAdded(QDBusObjectPath)), this, SLOT(onLDMSessionAdded(QDBusObjectPath))); connect( m_dbusIfsLDM, SIGNAL(SessionRemoved(QDBusObjectPath)), this, SLOT(onLDMSessionRemoved(QDBusObjectPath))); QStringList listSessions; QDBusInterface ifLDMProp(DM_DBUS_SERVICE, DM_DBUS_PATH, FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusMessage ret = ifLDMProp.call("GetAll", DM_DBUS_INTERFACE); QList outArgs = ret.arguments(); QVariant first = outArgs.at(0); const QDBusArgument &dbusArgs = first.value(); dbusArgs.beginMap(); while (!dbusArgs.atEnd()) { QString key; QVariant value; dbusArgs.beginMapEntry(); dbusArgs >> key >> value; if (key == "Sessions") { const QDBusArgument &dbusObjPaths = value.value(); QDBusObjectPath path; dbusObjPaths.beginArray(); while (!dbusObjPaths.atEnd()) { dbusObjPaths >> path; listSessions << path.path(); } dbusObjPaths.endArray(); } dbusArgs.endMapEntry(); } dbusArgs.endMap(); for (auto session : listSessions) { std::shared_ptr ldmSessionInfo(new LightDMSessionInfo(session, this)); m_ldmSessions->insert(session, ldmSessionInfo); } } } void LightDMHelper::onLDMSessionAdded(QDBusObjectPath objPath) { qDebug() << "Add LDM Session, path:" << objPath.path(); if (m_ldmSessions->contains(objPath.path())) { return; } std::shared_ptr ldmSessionInfo(new LightDMSessionInfo(objPath.path(), this)); m_ldmSessions->insert(objPath.path(), ldmSessionInfo); onUsersChanged(); } void LightDMHelper::onLDMSessionRemoved(QDBusObjectPath objPath) { if (!m_ldmSessions->contains(objPath.path())) { return; } const std::shared_ptr session = m_ldmSessions->value(objPath.path()); m_ldmSessions->remove(objPath.path()); onUsersChanged(); } void LightDMHelper::initAccountsUsersInfo() { if (m_accountServiceHelper) { QStringList listUsers = m_accountServiceHelper->getUserList(); for (auto user : listUsers) { LocalUserInfoPtr userInfo(new LocalUserInfo(user, this)); connect(userInfo.get(), &UserInfo::userPropChanged, this, &LightDMHelper::onUsersChanged); m_mapUsers->insert(user, userInfo); } connect( m_accountServiceHelper.get(), SIGNAL(UserAdded(QDBusObjectPath)), this, SLOT(onUserAdded(QDBusObjectPath))); connect( m_accountServiceHelper.get(), SIGNAL(UserRemoved(QDBusObjectPath)), this, SLOT(onUserRemoved(QDBusObjectPath))); } } void LightDMHelper::onUserAdded(QDBusObjectPath objPath) { qDebug() << "Add Accounts User, path:" << objPath.path(); if (m_mapUsers->contains(objPath.path())) { return; } LocalUserInfoPtr userInfo(new LocalUserInfo(objPath.path(), this)); connect(userInfo.get(), &UserInfo::userPropChanged, this, &LightDMHelper::onUsersChanged); m_mapUsers->insert(objPath.path(), userInfo); onUsersChanged(); } void LightDMHelper::onUserRemoved(QDBusObjectPath objPath) { if (!m_mapUsers->contains(objPath.path())) { return; } const UserInfoPtr userInfo = m_mapUsers->value(objPath.path()); qDebug() << "Remove Accounts User, name:" << userInfo->name(); m_mapUsers->remove(objPath.path()); onUsersChanged(); } LightDMSessionInfo::LightDMSessionInfo(const QString &strPath, QObject *parent) : QObject(parent), m_strPath(strPath) { initData(); initConnections(); } LightDMSessionInfo::~LightDMSessionInfo() { if (m_propertiesChangedConnected) { QDBusConnection::systemBus().disconnect( DM_DBUS_SERVICE, m_strPath, FD_PROPERTIES_INTERFACE, "PropertiesChanged", this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); m_propertiesChangedConnected = false; } } void LightDMSessionInfo::initData() { QDBusInterface ifaceExtra(DM_DBUS_SERVICE, m_strPath, FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusMessage ret = ifaceExtra.call("GetAll", DM_SESSION_INTERFACE); QList outArgs = ret.arguments(); QVariant first = outArgs.at(0); const QDBusArgument &dbusArgs = first.value(); dbusArgs.beginMap(); while (!dbusArgs.atEnd()) { QString key; QVariant value; dbusArgs.beginMapEntry(); dbusArgs >> key >> value; if (key == "Seat") { m_strSeatPath = value.toString(); } else if (key == "UserName") { m_strUserName = value.toString(); } dbusArgs.endMapEntry(); } dbusArgs.endMap(); } void LightDMSessionInfo::initConnections() { if (!m_propertiesChangedConnected) { QDBusConnection::systemBus().connect( DM_DBUS_SERVICE, m_strPath, FD_PROPERTIES_INTERFACE, "PropertiesChanged", this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); m_propertiesChangedConnected = true; } } void LightDMSessionInfo::onPropertiesChanged( const QString &interfaceName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties) { Q_UNUSED(invalidatedProperties); if (interfaceName == DM_SESSION_INTERFACE) { QVariantMap::const_iterator itVar = changedProperties.constBegin(); for (; itVar != changedProperties.constEnd(); itVar++) { QVariant varValue = itVar.value(); if (itVar.key() == "Seat") { m_strSeatPath = varValue.toString(); } else if (itVar.key() == "UserName") { m_strUserName = varValue.toString(); } } } } QDebug operator<<(QDebug stream, const LightDMSessionInfo &sessionInfo) { stream << "[" << sessionInfo.path() << sessionInfo.userName() << sessionInfo.seatPath() << "]"; return stream; } ukui-screensaver/src/lock-backend/securityuser.cpp0000664000175000017500000001012515172041035021346 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "securityuser.h" #define GREETER_SECURITY_CONFIG_PATH "/etc/ukui-greeter/ukui-greeter-security-config.json" SecurityUser *SecurityUser::instance_ = nullptr; SecurityUser::SecurityUser() : mode(NORMAL) { loadJsonFile(); } SecurityUser *SecurityUser::instance() { if (instance_ == nullptr) instance_ = new SecurityUser(); return instance_; } void SecurityUser::loadJsonFile() { QFile file(GREETER_SECURITY_CONFIG_PATH); if (file.exists() == false) { qDebug() << "file is not exists"; return; } qDebug() << "/etc/ukui-greeter/ukui-greeter-security-config.json is find!"; file.open(QIODevice::ReadOnly); QString value = file.readAll(); file.close(); QJsonParseError parseJsonErr; QJsonDocument document = QJsonDocument::fromJson(value.toUtf8(), &parseJsonErr); if (!(parseJsonErr.error == QJsonParseError::NoError)) { qWarning() << "parser json file failed"; qWarning() << parseJsonErr.errorString(); return; } QJsonObject rootObj = document.object(); if (rootObj.contains(QStringLiteral("ukui-greeter"))) { QJsonObject greeterObj = rootObj.value(QStringLiteral("ukui-greeter")).toObject(); if (greeterObj.contains(QStringLiteral("mode"))) { QString tmp; tmp = greeterObj.value(QStringLiteral("mode")).toString(); if (tmp == "normal") { mode = NORMAL; } else if (tmp == "blacklist") { mode = BLACKLIST; } else if (tmp == "whitelist") { mode = WHITELIST; } } else { qWarning() << "file can not find mode!"; } qDebug() << "user security mode is" << mode; if (mode == NORMAL) return; if (greeterObj.contains(QStringLiteral("blacklist"))) { QJsonArray tmpblacklist = greeterObj.value(QStringLiteral("blacklist")).toArray(); for (int i = 0; i < tmpblacklist.size(); i++) { blacklist.append(tmpblacklist.at(i).toString()); qDebug() << "blacklist add " << blacklist.at(i); } } if (greeterObj.contains(QStringLiteral("whitelist"))) { QJsonArray tmpwhitelist = greeterObj.value(QStringLiteral("whitelist")).toArray(); for (int i = 0; i < tmpwhitelist.size(); i++) { whitelist.append(tmpwhitelist.at(i).toString()); qDebug() << "whitelist add " << whitelist.at(i); } } } } bool SecurityUser::isSecrityUser(QString username) { if (username == "*login") return true; if (username == "*guest") return true; switch (mode) { case NORMAL: return true; break; case BLACKLIST: if (blacklist.contains(username)) return false; else return true; case WHITELIST: if (whitelist.contains(username)) return true; else return false; default: break; } return true; } int SecurityUser::getMode() { return mode; } QStringList SecurityUser::getBlackList() { return blacklist; } QStringList SecurityUser::getWhiteList() { return whitelist; } ukui-screensaver/src/lock-backend/personalizeddata.h0000664000175000017500000000751515172041035021607 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef PERSONALIZEDDATA_H #define PERSONALIZEDDATA_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ukui-log4qt.h" #include "CSingleton.h" class QDBusPendingCallWatcher; class PersonalizedData : public QObject { Q_OBJECT public: explicit PersonalizedData(QString user); virtual ~PersonalizedData(); ////////////////////ukui-greeter-conf////////////////////////////// QString dateType(void) { return m_dateType; } void dateType(QString val) { m_dateType = val; } int fontSize(void) { return m_fontSize; } void fontSize(int val) { m_fontSize = val; } int timeType(void) { return m_timeType; } void timeType(int val) { m_timeType = val; } QString backgroundPath(void) { return m_backgroundPath; } void backgroundPath(QString val) { m_backgroundPath = val; } QString color(void) { return m_color; } void color(QString val) { m_color = val; } /////////////////////usd-ukui-settings-daemon//////////////////////////////// int cursor_size(void) { return m_cursor_size; } void cursor_size(int val) { m_cursor_size = val; } QString cursor_theme(void) { return m_cursor_theme; } void cursor_theme(QString val) { m_cursor_theme = val; } int scaling_factor(void) { return m_scaling_factor; } void scaling_factor(int val) { m_scaling_factor = val; } void getJsonData(QJsonObject &json); void setJson(const QJsonObject &json); signals: void conf_changed(QString user); protected: // ukui-greeter-conf QString m_dateType = "cn"; int m_fontSize = 5; int m_timeType = 24; QString m_backgroundPath; QString m_color; // usd-ukui-settings-daemon int m_cursor_size = 48; QString m_cursor_theme; int m_scaling_factor = 1; //用户名 QString m_user; }; typedef QSharedPointer KylinUserDatePtr; ///////////////////////////////////////////////// /// \brief The PersonalizedDataMng class /// class PersonalizedDataMng : public QObject { Q_OBJECT protected: explicit PersonalizedDataMng(void); virtual ~PersonalizedDataMng(); public: QString GetConfInformation(QString); KylinUserDatePtr GetUserPtr(QString user); protected slots: void updateUserInformation(QString jsonstring); protected: void __getUserInfomation(void); void wait_for_finish(QDBusPendingCallWatcher *call); protected: QMap m_userPersonalizedData; friend class KySingleTon; }; typedef KySingleTon KYLINUSERDATAMNG; #endif // PERSONALIZEDDATA_H ukui-screensaver/src/lock-backend/msysdbus.cpp0000664000175000017500000000360415172041035020455 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "dbusupperinterface.h" #include "msysdbus.h" #include "kyrsac.h" #include #include #include MSysDbus::MSysDbus(DbusUpperInterface *interface, QObject *parent) : QObject(parent), m_interface(interface) { registerUniauth(); } bool MSysDbus::sendPassword(const QString username, QByteArray password) { if (m_interface) return m_interface->sendPassword(username, password); qWarning() << "m_interface is nullptr"; return false; } QString MSysDbus::getPublicEncrypt() { if (m_interface) return m_interface->getPublicEncrypt(); qWarning() << "m_interface is nullptr"; return ""; } void MSysDbus::registerUniauth() { QDBusInterface iface( "org.ukui.UniauthBackend", "/org/ukui/UniauthBackend", "org.ukui.UniauthBackend", QDBusConnection::systemBus()); iface.call("registerLoginApp"); } void MSysDbus::sendSignalLoginFinished(QString username, bool res) { QDBusInterface iface( "org.ukui.UniauthBackend", "/org/ukui/UniauthBackend", "org.ukui.UniauthBackend", QDBusConnection::systemBus()); iface.call("sendSignalLoginFinished", username, res); } ukui-screensaver/src/lock-backend/switchuserutils.cpp0000664000175000017500000003357215172041106022073 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "switchuserutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../src/common/global_utils.h" SwitchUserUtils::SwitchUserUtils() {} QString SwitchUserUtils::GetCurUserName() { QString strUserName = ""; struct passwd *pw = NULL; pw = getpwuid(getuid()); if (pw != NULL) { strUserName = QString(pw->pw_name); } else { qWarning() << "SwitchUserUtils:get user name failed"; } return strUserName; } int SwitchUserUtils::GetUidByName(QString strUserName) { if (strUserName.isEmpty()) { return (unsigned int)-1; } struct passwd *pwbufp = getpwnam(strUserName.toLatin1().data()); return pwbufp ? pwbufp->pw_uid : (unsigned int)-1; } UserDisplayIfInfo SwitchUserUtils::GetUserUDII(QString strUserName) { UserDisplayIfInfo userDisplayIfInfo; // 获取所有的session路径 QList listObjPath; QDBusInterface iface( "org.freedesktop.DisplayManager", "/org/freedesktop/DisplayManager", "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); QDBusMessage result = iface.call("Get", "org.freedesktop.DisplayManager", "Sessions"); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "Get Sessions:" << result.errorMessage(); return userDisplayIfInfo; } QList variantList = result.arguments(); if (variantList.size() <= 0) { qDebug() << "Get Sessions List is null!"; return userDisplayIfInfo; } QDBusVariant dbusVar = variantList[0].value(); const QDBusArgument &dbusArg = dbusVar.variant().value(); if (dbusArg.currentType() == QDBusArgument::ArrayType) { QDBusObjectPath objPath; dbusArg.beginArray(); while (!dbusArg.atEnd()) { dbusArg >> objPath; listObjPath.append(objPath); } dbusArg.endArray(); } // 获取各session的用户名,过滤指定用户的信息 for (auto objPath : listObjPath) { QDBusInterface iface( "org.freedesktop.DisplayManager", objPath.path(), "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); QDBusMessage result = iface.call("Get", "org.freedesktop.DisplayManager.Session", "UserName"); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "Get Session username :" << result.errorMessage(); continue; } QList variantList = result.arguments(); if (variantList.size() <= 0) { qDebug() << "Get Session user info is null!"; continue; } QDBusVariant dbusUNameVar = variantList[0].value(); QString userName = dbusUNameVar.variant().value(); if (strUserName == userName) { result = iface.call("Get", "org.freedesktop.DisplayManager.Session", "Seat"); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "Get Session seat :" << result.errorMessage(); continue; } QList variantList = result.arguments(); if (variantList.size() <= 0) { qDebug() << "Get Session seatis null!"; continue; } QDBusVariant dbusSeatVar = variantList[0].value(); QDBusObjectPath strSeat = dbusSeatVar.variant().value(); if (!strSeat.path().isEmpty()) { userDisplayIfInfo.strSeatPath = strSeat.path(); userDisplayIfInfo.strUserName = strUserName; userDisplayIfInfo.strSessionPath = objPath.path(); } } else { continue; } } return userDisplayIfInfo; } int SwitchUserUtils::SwitchToUserSession(QString seatPath, UserDisplayIfInfo &toUDII) { // 检查Seat路径是否合规 if (QDBusObjectPath(seatPath).path().isEmpty()) { qWarning() << "SwitchToUserSession failed, invalid seatpath:" << seatPath; return 1; } QDBusInterface ifaceDM( "org.freedesktop.DisplayManager", seatPath, "org.freedesktop.DisplayManager.Seat", QDBusConnection::systemBus()); if (!ifaceDM.isValid()) { qWarning() << "SwitchUser seat interface invalid!"; return 2; } // 截取lightdm seat路径中最后字段与systemd中Seat匹配(systemd接口状态异常时,可在/run/systemd/目录下查看实际状态) QString strSeatID = seatPath.split("/").last(); qDebug() << "SeatId:" << strSeatID << ",UName:" << toUDII.strUserName << ",USessionPath:" << toUDII.strSessionPath << ",USeatPath:" << toUDII.strSeatPath; if (!toUDII.strUserName.isEmpty() && !toUDII.strSessionPath.isEmpty()) { // 指定了切换的用户名,且其有显示会话 char **sessions = NULL; char *user_display = NULL; QString strUserDisplay = "", strSessionDisplay = ""; QStringList listSessions; QString strNewUserSession = ""; // 获取用户的所有session id sd_uid_get_sessions(GetUidByName(toUDII.strUserName), 0, &sessions); // 获取用户的主display id sd_uid_get_display(GetUidByName(toUDII.strUserName), &user_display); // 释放systemd接口调用占用的资源 if (sessions) { char **k = sessions; while (*k) { listSessions.append(QString("%1").arg(*k)); k++; } for (k = sessions; *k; k++) free(*k); free(sessions); sessions = NULL; } if (user_display) { strUserDisplay = QString("%1").arg(user_display); free(user_display); user_display = NULL; } qDebug() << "user display: " << strUserDisplay; qDebug() << "user sessions: " << listSessions; /** * 遍历systemd中用户所有的session,过滤掉Display为空的session * (wayland环境可能Dispaly为空,需再确认Type是否为wayland) **/ for (int n = 0; n < listSessions.size(); n++) { char *session_display = NULL; sd_session_get_display(listSessions.at(n).toLatin1().data(), &session_display); if (session_display) { strSessionDisplay = QString("%1").arg(session_display); free(session_display); session_display = NULL; } // 支持wayland的环境 if (!qgetenv("WAYLAND_DISPLAY").isEmpty()) { QString strSession = ""; bool isNumber = true; listSessions.at(n).toDouble(&isNumber); // 会话id是否为纯数字,是则增加前缀_3,否则不加前缀 if (isNumber) { strSession = QString("/org/freedesktop/login1/session/_3%1").arg(listSessions.at(n)); } else { strSession = QString("/org/freedesktop/login1/session/%1").arg(listSessions.at(n)); } // 检查session type 是否匹配 QDBusInterface iface( "org.freedesktop.login1", strSession, "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); QDBusMessage result = iface.call("Get", "org.freedesktop.login1.Session", "Type"); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "Get Session Type failed:" << result.errorMessage(); return 2; } QList variantList = result.arguments(); if (variantList.size() <= 0) { qDebug() << "Get Session type info is null!"; return 2; } QDBusVariant dbusTypeVar = variantList[0].value(); QString sessionType = dbusTypeVar.variant().value(); if (strSessionDisplay.isEmpty() && sessionType != "wayland") { continue; } } else { if (strSessionDisplay.isEmpty()) { /** * 最后一个会话id时,如果该用户的Display不为空,则也认为最后一个会话有效 **/ if ((n + 1) < listSessions.size() || strUserDisplay.isEmpty()) { continue; } strSessionDisplay = strUserDisplay; } } QString strUserSeatID = ""; char *seat = NULL; sd_session_get_seat(listSessions.at(n).toLatin1().data(), &seat); if (seat) { strUserSeatID = QString("%1").arg(seat); free(seat); seat = NULL; } qDebug() << "SrcSeat:" << strSeatID << ",DesSeat:" << strUserSeatID << ",Session:" << listSessions.at(n) << ",Display:" << strSessionDisplay; // 有效会话的SeatId与lightdm中的SeatId匹配则此会话为即将激活的会话 if (!strUserSeatID.isEmpty() && (QString::compare(strUserSeatID, strSeatID, Qt::CaseInsensitive) == 0)) { strNewUserSession = listSessions.at(n); qDebug() << "SwitchUser: find sessions path:" << strNewUserSession << "," << strUserSeatID; break; } } if (strNewUserSession.isEmpty()) { qWarning() << "SwitchUser: can not find user session" << toUDII.strUserName; return 3; } // 激活用户的会话 QTimer::singleShot(30, [strNewUserSession]() { QDBusInterface iface( "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QDBusConnection::systemBus()); QDBusMessage result = iface.call("ActivateSession", strNewUserSession); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "SwitchUser ActivateSession failed:" << result.errorMessage(); } else { qDebug() << "ActivateSession completed successfully"; } }); return 0; } else if (!toUDII.strUserName.isEmpty()) { if (!isGreeterMode()) { // 切换到免密登录用户会直接登录桌面,需要先展示“登录”按钮 #if 0 QDBusMessage result = ifaceDM.call("SwitchToUser", toUDII.strUserName, toUDII.strSessionPath); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "SwitchUser SwitchToUser failed:" << result.errorMessage(); return 2; } #else QDBusMessage result = ifaceDM.call("SwitchToGreeter"); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "SwitchUser SwitchToGreeter failed:" << result.errorMessage(); return 2; } #endif } else { return 4; } } else { if (!isGreeterMode()) { QDBusMessage result = ifaceDM.call("SwitchToGreeter"); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "SwitchUser SwitchToGreeter failed:" << result.errorMessage(); return 2; } } else { return 5; } } return 0; } bool SwitchUserUtils::SwitchToUserLock() { QString strLockCommand = QStandardPaths::findExecutable("ukui-screensaver-command"); if (!strLockCommand.isEmpty()) { QProcess::execute(strLockCommand, QStringList() << "-S"); return true; } return false; } bool SwitchUserUtils::CanSwitchUser() { static int nCanSwitch = -1; if (nCanSwitch != -1) { return (bool)(nCanSwitch); } QString strSeatPath = qgetenv("XDG_SEAT_PATH"); if (!strSeatPath.isEmpty()) { qDebug() << "SeatPath:" << strSeatPath; QDBusInterface dmService(DM_DBUS_SERVICE, strSeatPath, FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusMessage ret = dmService.call("GetAll", DM_SEAT_INTERFACE); if (ret.type() == QDBusMessage::ErrorMessage) { qWarning() << ret.errorMessage(); } else { const QDBusArgument &arg = ret.arguments().at(0).value(); qDebug() << arg.currentType(); arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; arg.endMapEntry(); if (key == "CanSwitch") { nCanSwitch = value.toBool() ? 1 : 0; break; } } arg.endMap(); if (nCanSwitch != -1) { return (bool)(nCanSwitch); } } } return false; } ukui-screensaver/src/lock-backend/sessionwatcher.h0000664000175000017500000000443315172041106021312 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SESSIONWATCHER_H #define SESSIONWATCHER_H #include #include #include #include #include #include #include #include #include "gsettingshelper.h" class SessionWatcher : public QObject { Q_OBJECT public: enum { SESSION_STATUS_SCREENSAVER = 1, // 屏保 SESSION_STATUS_SCREENCLOSE = 2, // 关屏 SESSION_STATUS_SLEEPED = 4, // 休眠/睡眠 }; explicit SessionWatcher(QSharedPointer m_gsettingsHelper, QObject *parent = nullptr); /** * @brief isSessionIdle会话是否空闲 * @return true 空闲 */ bool isSessionIdle(); /** * @brief simulateSessionActive 模拟会话激活 */ void simulateSessionActive(); Q_SIGNALS: void sessionIdle(); void sessionLockIdle(); void sessionIdleExit(); public Q_SLOTS: void onStatusChanged(unsigned int status); void onSessionRemoved(const QDBusObjectPath &objectPath); void onLockScreenConfigChanged(QString strKey, QVariant value); void onSessionDbusActiveChanged(const QString &strServerName, bool isActive); private: QString sessionPath; int idleDelay = -1; int m_idleLock = -1; int m_nLockTimeout = -1; QTimer *m_timer = nullptr; QTimer *m_timer2 = nullptr; bool m_lidState = false; double defaultFontSize; bool lockState; int m_nStatus = 0; // 当前状态 QTimer *m_timerLock = nullptr; QSharedPointer m_gsettingsHelper = nullptr; bool m_isSessionIdle = false; }; #endif // SESSIONWATCHER_H ukui-screensaver/src/agreementinfo.cpp0000664000175000017500000000474615172041035017122 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "agreementinfo.h" AgreementInfo::AgreementInfo(QObject *parent) : QObject(parent) , m_showLoginPrompt(false) , m_hideTitle(false) , m_promptTitle("") , m_promptText("") , m_promptTextFilePath("") { } AgreementInfo::AgreementInfo(const AgreementInfo &agreementInfo) : QObject(agreementInfo.parent()) , m_showLoginPrompt(agreementInfo.m_showLoginPrompt) , m_hideTitle(agreementInfo.m_hideTitle) , m_promptTitle(agreementInfo.m_promptTitle) , m_promptText(agreementInfo.m_promptText) , m_promptTextFilePath(agreementInfo.m_promptTextFilePath) { } AgreementInfo::~AgreementInfo() {} bool AgreementInfo::operator==(const AgreementInfo &agreementInfo) const { return ( agreementInfo.showLoginPrompt() == m_showLoginPrompt && agreementInfo.hideTitle() == m_hideTitle && agreementInfo.promptTitle() == m_promptTitle && agreementInfo.promptText() == m_promptText && agreementInfo.promptTextFilePath() == m_promptTextFilePath); } void AgreementInfo::updateShowLoginPrompt(const bool &showLoginPrompt) { m_showLoginPrompt = showLoginPrompt; } void AgreementInfo::updateHideTitle(const bool &hideTitle) { m_hideTitle = hideTitle; } void AgreementInfo::updatePromptTitle(const QString &promptTitle) { m_promptTitle = promptTitle; } void AgreementInfo::updatePromptText(const QString &promptText) { m_promptText = promptText; } void AgreementInfo::updatePromptTextFilePath(const QString &promptTextFilePath) { m_promptTextFilePath = promptTextFilePath; } QDebug operator<<(QDebug stream, const AgreementInfo &agreementInfo) { stream << "[" << agreementInfo.showLoginPrompt() << agreementInfo.hideTitle() << agreementInfo.promptTitle() << agreementInfo.promptText() << agreementInfo.promptTextFilePath() << "]"; return stream; } ukui-screensaver/src/lock-focushelper/0000775000175000017500000000000015172041106017024 5ustar fengfengukui-screensaver/src/lock-focushelper/blackwindow.cpp0000664000175000017500000000362415172041106022041 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "blackwindow.h" #include #include #include #include #include #include #include #include #include #include BlackWindow::BlackWindow(QWidget *parent) : QWidget(parent) { setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); setAttribute(Qt::WA_TranslucentBackground); setCursor(Qt::BlankCursor); //qApp->installNativeEventFilter(this); //installEventFilter(this); } void BlackWindow::paintEvent(QPaintEvent *event) { for(auto screen : QGuiApplication::screens()) { QPainter painter(this); QColor cor = "#000000"; painter.setBrush(cor); painter.drawRect(screen->geometry()); } return QWidget::paintEvent(event); } void BlackWindow::laterActivate() { raise(); } bool BlackWindow::eventFilter(QObject *obj, QEvent *event) { if(event->type() == QEvent::WindowDeactivate){ QTimer::singleShot(50,this,SLOT(laterActivate())); } return false; } bool BlackWindow::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) { return false; } ukui-screensaver/src/lock-focushelper/blackwindow.h0000664000175000017500000000235615172041106021507 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BLACKWINDOW_H #define BLACKWINDOW_H #include #include class BlackWindow : public QWidget, public QAbstractNativeEventFilter { Q_OBJECT public: explicit BlackWindow(QWidget *parent = nullptr); virtual bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; bool eventFilter(QObject *obj, QEvent *event); signals: private Q_SLOTS: void laterActivate(); protected: void paintEvent(QPaintEvent *event); bool screensaverIsShow = false; }; #endif // BLACKWINDOW_H ukui-screensaver/src/lock-focushelper/main.cpp0000664000175000017500000000215415172041106020456 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "blackwindow.h" #include "../widgets/screen-manager.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); BlackWindow w; w.setGeometry(SCREEN_MANAGER->virtualDesktopGeometry()); //w.activateWindow(); w.show(); a.exec(); return 0; } ukui-screensaver/src/widgets/0000775000175000017500000000000015172041106015225 5ustar fengfengukui-screensaver/src/widgets/authdialog.h0000775000175000017500000002635615172041106017536 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef AUTHDIALOG_H #define AUTHDIALOG_H #include "mylistwidget.h" #include #include "userinfo.h" #include "../dbusifs/biometrichelper.h" #include "definetypes.h" #include "biodefines.h" #include "loginoptionswidget.h" #include "pluginsloader.h" #include #include #include "kmessagedialog.h" class QLabel; class CustomLabel; class QPushButton; class IconEdit; class LockDialogModel; class BioButtonListWidget; class StatusButton; class QTimer; class SecurityQuestionAnswer; class CircleLabel; extern float scale; class AuthDialog : public QWidget { Q_OBJECT public: explicit AuthDialog(LockDialogModel *model, UserInfoPtr userInfo, QWidget *parent = nullptr); void initUI(); void switchLoginOptType(unsigned uLoginOptType, bool faceBtnClicked = false); public: void startAuth(); void stopAuth(); void onCurUserChanged(UserInfoPtr userInfo); void onCurUserInfoChanged(UserInfoPtr userInfo); void updateUI(); void updateAuthSize(); void setLoginTypeTip(QString strLoginTypeTip); void setUkeyTypeTip(QString text); void setDirLogin(); void startLoadingUkey(); void stopLoadingUkey(); void updateLoadingPixmap(); void updatePamLoadingPixmap(); void setWillLoginUser(QString strUserName, bool isOneKeyLogin); /** * @brief setCustomAuthDefault 设置是否默认第三方认证 * @param isDefault true 是,否则 否 */ void setCustomAuthDefault(bool isDefault); void updateAuthFont(QString fontFamily); void updateAuthFontSize(double fontSize); QString getAuthMethod(); void inhibitShow(bool isInhibit); QString getCurAuthRealName(); qint32 getCurAuthUserUid(); QPixmap getCurAuthUserFace(); void clearMessage(); int getUserAuthFailedTimes(); void hidePwdTipBtn(); private Q_SLOTS: void onPamShowMessage(QString strMsg, int nType); void onPamShowPrompt(QString strPrompt, int nType); void onPamShowBinaryPrompt(QVariant val, int nType); void onPamAuthCompleted(); void onRespond(const QString &strRes); void onMessageButtonClicked(); void onLoginOptsCount(unsigned uCount); void onLoginOptImage(QImage img); void setLoginMsg(QString strLoginMsg); void onFirstLoginIdentifyComplete(QDBusPendingCallWatcher *watcher); void onPamShowOtpDialog(int type,QVariant value); void onPamhideOtpDialog(); void onPamShowUkeyLoading(); void onPamStopUkeyLoading(); /** * @brief onCustomRequestAccount 第三方请求认证用户槽 * @param strName 用户名 */ void onCustomRequestAccount(QString strName); /** * @brief onCustomAuthResult 第三方认证结果 * @param nResult 结果值,0 成功,其他失败 * @param strMsg 结果消息 */ void onCustomAuthResult(int nResult, QString strMsg); /** * @brief onCustomRequest 第三方请求响应槽 * @param strReqJson 请求json * @return 请求结果 */ QString onCustomRequest(QString strReqJson); /** * @brief onCustomPlugEnv 插件所处环境 * @return 登录或锁屏等 */ int onCustomPlugEnv(); private Q_SLOTS: void onBiometricButtonClicked(); void onDeviceChanged(unsigned uCurLoginOptType, const DeviceInfoPtr &deviceInfo); void onBiometricAuthComplete(bool result, int nStatus); void onPrepareForSleep(bool sleep); void setQRCode(QImage &imgQRCode); void setFaceImg(QImage &imgFace, int nStatus = 0); void setQRCodeMsg(QString strMsg); void onLoadingImage(); void onBiometricDbusChanged(bool bActive); void onFRetryButtonClicked(); void pamBioSuccess(); void onRespondUkey(const QString &text); Q_SIGNALS: void authSucceed(QString strUserName); /** * @brief 手动输入用户名 * @param userName 用户名 */ void userChangedByManual(const QString &userName); /** * @brief customRequestAccount 第三方请求认证用户信号 * @param account 用户名 */ void customRequestAccount(QString account); void lineEditClicked(); void lineEditMousePressed(); void bottomPositionChanged(int nPosY); void showResetPasswdBtn(); protected: bool eventFilter(QObject *obj, QEvent *event); void resizeEvent(QResizeEvent *event); private: void initTipWidget(); void initHeaderWidget(); void initNameWidget(); void initEditWidget(); void initMessagerWidget(); void initUkeyPasswordWidget(); void initLoginoptionWidget(); void initCustomAuthWidget(); void initPamUkeyLoadingWidget(); void initBiometricUI(); void showAuthenticated(bool successful = true); void initConnections(); bool unlockUserTimer(); void initLoginOptsWidget(); void setLoginOptsWidgetGeometry(); void performBiometricAuth(); void skipBiometricAuth(); /** * @brief connectBioSlots connect or disconnect login options slots * @param isConnect true to Conenct */ void connectBioSlots(bool isConnect); void startBioAuth(unsigned uTimeout = 200); void restartBioAuth(); void waitBiometricServiceStatus(); QString getDeviceType_tr(int deviceType); QString getDeviceType_lowerTr(int deviceType); void setBiometricAuthDisabledStatus(bool locked); void updatePixmap(); //一键开机认证函数 void startFirstLogin(); void showAuthMethodTip(); void startBioWaiting(unsigned uCurLoginOptType); void stopBioWaiting(bool isDisable = false); void showPwdTipBtn(); private: LockDialogModel *m_modelLockDialog = nullptr; QWidget *m_tipWidget = nullptr; QWidget *m_headerWidget = nullptr; QWidget *m_nameWidget = nullptr; CircleLabel *m_labelHeadImg = nullptr; //头像 CustomLabel *m_nameLabel = nullptr; //用户名 QWidget *m_editWidget = nullptr; IconEdit *m_passwordEdit = nullptr; //密码输入框 StatusButton *m_pPwdTipMessage = nullptr; QHBoxLayout *m_pEditLayout; CustomLabel *m_messageLabel = nullptr; // PAM消息显示 QPushButton *m_messageButton = nullptr; //免密登录按钮 QWidget *m_ukeyPasswdWidget = nullptr; //放置密码输入框和信息列表 IconEdit *m_ukeyPasswordEdit = nullptr; // ukey密码输入框 CustomLabel *m_ukeyMessageLabel = nullptr; // PAM消息显示 QWidget *m_ukeyLoadingWidget = nullptr; // ukey loading窗口 QPushButton *m_loadingButton = nullptr; CustomLabel *m_ukeyLoadingText = nullptr; QTimer *m_loadingTimer = nullptr; QPixmap m_ukeyLoadingPixmap; QLabel *m_labelFace = nullptr; CustomLabel *m_labelLoginTypeTip = nullptr; // 登录类型提示 QLabel *m_labelQRCode = nullptr; // 二维码图标 CustomLabel *m_labelQRCodeMsg = nullptr; // 二维码状态消息提示 bool m_isNetworkErr = false; QLabel *m_labelQRCodeTip = nullptr; QPushButton *m_fRetryButton = nullptr; //人脸识别重试按钮 // 指纹、指静脉、虹膜、声纹识别动图 QLabel *m_labelBioWaiting = nullptr; // 指纹、指静脉、虹膜、声纹识别动图提示 QTimer *m_timerBioWaiting = nullptr; // 指纹、指静脉、虹膜、声纹识别动图切换定时器 int m_nBioWaitingFrame = 0; // 当前显示的动图帧 int m_nCurBioWaitingType = -1; // 当前显示的动图类型 QWidget *m_widgetPlaceholder = nullptr; // 占位控件 QWidget *m_widgetLoginOpts = nullptr; // 登录选项 CustomLabel *m_optionTip = nullptr; BioButtonListWidget *bottomListWidget = nullptr; UserInfoPtr m_curUserInfo; bool m_bRecvPrompt = false; bool m_bHasUnacknowledgedMsg = false; bool m_bDirectLogin = false; bool m_bNeedRetry = false; QString m_preStrMessage = ""; int m_preStrMessageType = -1; bool m_isNameLogin = false; bool m_isAutoSwitch = false; bool m_isPassWdInput = false; //手动输入用户标记,设置该标记的原因是判断是不是手动输入用户, //如果输入了无法登录的用户,就会一直输出密码错误信息 bool m_isManual = false; QString m_strManualLoginName; bool m_isInputPasswd; enum AuthMode { PASSWORD, BIOMETRIC, UNKNOWN }; AuthMode authMode = UNKNOWN; // 生物识别认证 QString m_deviceName; DeviceInfoPtr m_deviceInfo = nullptr; BiometricHelper *m_biometricProxy = nullptr; LoginOptionsWidget *m_loginOpts = nullptr; QWidget *m_customWidget; /** 第三方认证界面窗口 */ QHBoxLayout *m_layoutCustom; /** 第三方认证界面主布局 */ bool isBioSuccess = false; // 生物认证是否成功 bool manualStopBio = false; bool m_isCustomDefault = false; /** 是否默认使用第三方认证 */ QMap> m_failMap; int maxFailedTimes; QString m_face; QTimer *m_bioTimer = nullptr; QImage m_imgQRCode; unsigned m_uCurLoginOptType = LOGINOPT_TYPE_PASSWORD; // 当前登录验证方式 QString m_strLoginTypeTip = ""; int m_nLastDeviceId = -1; QString m_strDefaultFace; int m_fTimeoutTimes = 0; QPixmap m_waitingPixmap; QTimer *w_timer = nullptr; bool isLoadingUkey = false; bool isPowerup = false; QDBusInterface *bioService = nullptr; int drvid = -1; bool isFirstAuth = false; QString m_lastUserName = ""; // 将登录认证的用户名 QString m_strServiceUser = ""; // 是否为一键登录 bool m_isOneKeyLogin = false; PluginsLoader *m_pluginsLoader = nullptr; /** 插件加载器 */ bool m_isLockingFlg; //判断当前是否正在锁定倒计时 int m_nCurLockMin; //当前锁定的分钟数 QTimer *m_timerUserUnlock = nullptr; double m_curFontSize; double m_ptToPx = 1.0; QString m_strLastPamAuthName = ""; QDateTime m_lastPamAuthTime; bool m_isInhibitShow = false; SecurityQuestionAnswer *m_pSecurityQuestionAnswer = nullptr; QMap m_userFailedTimesMap; KMessageDialog *m_msgDialog = nullptr; KMessageDialog *m_bindOtpDialog = nullptr; QString factorMode = ""; bool sureBind = false; QWidget *m_pamUkeyLoadingWidget = nullptr; // ukey loading窗口 QPushButton *m_pamUkeyLoadingButton = nullptr; CustomLabel *m_pamUkeyLoadingText = nullptr; QTimer *m_pamUkeyLoadingTimer = nullptr; QPixmap m_pamUkeyLoadingPixmap; bool hasSendPassword = false; QString authMethod = ""; CustomLabel *m_authMethodLabel = nullptr; // PAM消息显示 int n_prompts = 0; QEventLoop *m_bioAuthLoop = nullptr; bool m_isBioSlotsConnected = false; QSize m_widgetSize; // current widget last size }; #endif // AUTHDIALOG_H ukui-screensaver/src/widgets/timewidget.h0000664000175000017500000000331515172041106017542 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef TIMEWIDGET_H #define TIMEWIDGET_H #include #include #include #include #include #include #include #include "customlabel.h" enum dateinfo { DATE, TIME, }; class LockDialogModel; extern float scale; class TimeWidget : public QWidget { Q_OBJECT public: explicit TimeWidget(LockDialogModel *model, QWidget *parent = nullptr); ~TimeWidget(); void updateUI(); Q_SIGNALS: public Q_SLOTS: void update_datatime(); void timerStart(); void timerStop(); void updateTimeFont(QString fontFamily); void updateTimeFontSize(double fontSize); private Q_SLOTS: void hideEvent(QHideEvent *event); void showEvent(QShowEvent *event); private: QString getLongFormatDate(int type); private: LockDialogModel *m_modelLockDialog = nullptr; QTimer *m_timer = nullptr; CustomLabel *m_t_label = nullptr; CustomLabel *m_d_label = nullptr; double m_curFontSize; double m_ptToPx = 1.0; }; #endif // TIMEWIDGET_H ukui-screensaver/src/widgets/screensavermode.h0000664000175000017500000000355615172041106020574 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVER_MODE_H #define SCREENSAVER_MODE_H #include #include #include enum SaverMode { SAVER_BLANK_ONLY = 0, SAVER_RANDOM, SAVER_SINGLE, SAVER_IMAGE, SAVER_DEFAULT, SAVER_DEFAULT_CUSTOM }; enum TransitionEffect { TRANSITION_NONE, TRANSITION_FADE_IN_OUT }; class ScreenSaver : public QObject { Q_OBJECT public: SaverMode mode; // path is a directory or a file path if mode is SAVER_IMAGE QString path = ""; QString lastPath; // for images saver TransitionEffect effect; int interval; private: int imageIndex; QTimer *timer; QStringList imagePaths; Q_SIGNALS: void imagePathChanged(const QString &path); public: explicit ScreenSaver(QObject *parent = nullptr); explicit ScreenSaver(const ScreenSaver &screensaver); ScreenSaver(ScreenSaver &&screensaver) noexcept; bool exists(); void startSwitchImages(); void stopSwitchImages(); bool timerStatus(); friend QDebug &operator<<(QDebug stream, const ScreenSaver &screensaver); }; Q_DECLARE_METATYPE(ScreenSaver) QDebug& operator<<(QDebug& stream, const ScreenSaver &screensaver); #endif // SCREENSAVER_MODE_H ukui-screensaver/src/widgets/loginauthinterface.h0000664000175000017500000000436015172041035021255 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LOGIN_AUTH_INTERFACE_H #define LOGIN_AUTH_INTERFACE_H #include "loginplugininterface.h" #define LOGINAUTH_CUSTOM_ID (0xFFFF) /** * @brief 登录认证接口 * */ class LoginAuthInterface : public LoginPluginInterface { public: /** * @brief 构造函数 * */ LoginAuthInterface() {} /** * @brief 析构函数 * */ virtual ~LoginAuthInterface() {} /** * @brief 获取插件类型 * @return 插件类型 */ int getPluginType() { return MODULETYPE_AUTH; } /** * @brief 返回通用认证窗口 * @return 无 */ virtual void returnCommAuth() {} /** * @brief 请求认证账户 * @param strName 用户名 * @return 无 */ virtual void requestAuthAccount(QString strName) = 0; /** * @brief 开始认证 * @param nUid 用户id * @param strName 认证用户名 * @return 无 */ virtual void startAuthenticate(int nUid, QString strName) = 0; /** * @brief 停止认证 * @param nUid 用户id * @param strName 认证用户名 * @return 无 */ virtual void stopAuthenticate(int nUid, QString strName) = 0; /** * @brief authenticateResult 认证结果 * @param nResult 结果值 0,成功,其他失败 * @param strMsg 结果描述 */ virtual void authenticateResult(int nResult, QString strMsg) = 0; }; #define LoginAuthInterfaceIID "org.ukui.LoginAuthInterface" Q_DECLARE_INTERFACE(LoginAuthInterface, LoginAuthInterfaceIID) #endif // LOGIN_AUTH_INTERFACE_H ukui-screensaver/src/widgets/screensaverwidget.h0000664000175000017500000000324715172041106021130 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVERWIDGET_H #define SCREENSAVERWIDGET_H #include #include #include #include "screensavermode.h" #include "screensaver/screensaverplugin.h" class ScreenSaverWidget : public QWidget { Q_OBJECT public: ScreenSaverWidget(ScreenSaver *screensaver, QPixmap *background, QWidget *parent = nullptr); ~ScreenSaverWidget(); protected: void closeEvent(QCloseEvent *); void paintEvent(QPaintEvent *event); bool eventFilter(QObject *obj, QEvent *event); void resizeEvent(QResizeEvent *event); private: void embedXScreensaver(const QString &path); void initScreenSaverPlugin(); private Q_SLOTS: void onBackgroundChanged(const QString &path); private: QTimer *timer; ScreenSaver *screensaver; bool closing; float opacity; QProcess process; static QSharedPointer m_ssPluginPtr; QWidget *m_wdgScreenSaver = nullptr; QPixmap *m_background = nullptr; }; #endif // SCREENSAVERWIDGET_H ukui-screensaver/src/widgets/kymprisdbusclient.h0000664000175000017500000001077615172041035021165 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef MPRISDBUSCLIENT_H #define MPRISDBUSCLIENT_H #include #include #include #include #include #include #include #include #include /* * Common mpris interface,reference:https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html */ class KyMPRISDBusClient : public QDBusAbstractInterface { Q_OBJECT public: KyMPRISDBusClient( const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); ~KyMPRISDBusClient(); Q_PROPERTY(QString PlaybackStatus READ getPlaybackStatus NOTIFY signalPlaybackStatusChanged) QString getPlaybackStatus() const; Q_PROPERTY(QString LoopStatus READ getLoopStatus WRITE setLoopStatus NOTIFY signalLoopStatusChanged) QString getLoopStatus() const; void setLoopStatus(const QString &value); Q_PROPERTY(double Rate READ getRate WRITE setRate NOTIFY signalRateChanged) double getRate() const; void setRate(double value); Q_PROPERTY(bool Shuffle READ getShuffle WRITE setShuffle NOTIFY signalShuffleChanged) bool getShuffle() const; void setShuffle(bool value); Q_PROPERTY(QVariantMap Metadata READ getMetadata NOTIFY signalMetadataChanged) QVariantMap getMetadata() const; Q_PROPERTY(double Volume READ getVolume WRITE setVolume NOTIFY signalVolumeChanged) double getVolume() const; void setVolume(double value); Q_PROPERTY(qlonglong Position READ getPosition NOTIFY signalPositionChanged) qlonglong getPosition() const; Q_PROPERTY(double MinimumRate READ getMinimumRate NOTIFY signalMinimumRateChanged) double getMinimumRate() const; Q_PROPERTY(double MaximumRate READ getMaximumRate NOTIFY signalMaximumRateChanged) double getMaximumRate() const; Q_PROPERTY(bool CanGoNext READ getCanGoNext NOTIFY signalCanGoNextChanged) bool getCanGoNext() const; Q_PROPERTY(bool CanGoPrevious READ getCanGoPrevious NOTIFY signalCanGoPreviousChanged) bool getCanGoPrevious() const; Q_PROPERTY(bool CanPlay READ getCanPlay NOTIFY signalCanPlayChanged) bool getCanPlay() const; Q_PROPERTY(bool CanPause READ getCanPause NOTIFY signalCanPauseChanged) bool getCanPause() const; Q_PROPERTY(bool CanSeek READ getCanSeek NOTIFY signalCanSeekChanged) bool getCanSeek() const; Q_PROPERTY(bool CanControl READ getCanControl NOTIFY signalCanControlChanged) bool getCanControl() const; public Q_SLOTS: // METHODS void onPropertiesChanged(const QDBusMessage &msg); QDBusPendingReply<> doNext(); QDBusPendingReply<> doPrevious(); QDBusPendingReply<> doPause(); QDBusPendingReply<> doPlayPause(); QDBusPendingReply<> doStop(); QDBusPendingReply<> doPlay(); QDBusPendingReply<> doSeek(qlonglong Offset); QDBusPendingReply<> doSetPosition(const QDBusObjectPath &TrackId, qlonglong Position); QDBusPendingReply<> doOpenUri(const QString &Uri); Q_SIGNALS: // SIGNALS void Seeked(qlonglong Position); // begin property changed signals void signalRateChanged(double value); void signalShuffleChanged(bool value); void signalVolumeChanged(double value); void signalCanControlChanged(bool value); void signalCanGoNextChanged(bool value); void signalCanGoPreviousChanged(bool value); void signalCanPauseChanged(bool value); void signalCanPlayChanged(bool value); void signalCanSeekChanged(bool value); void signalLoopStatusChanged(const QString &value); void signalMaximumRateChanged(double value); void signalMetadataChanged(const QVariantMap &value); void signalMinimumRateChanged(double value); void signalPlaybackStatusChanged(const QString &value); void signalPositionChanged(qlonglong value); }; #endif // MPRISDBUSCLIENT_H ukui-screensaver/src/widgets/batterywidget.h0000664000175000017500000000363115172041106020257 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BATTERYWIDGET_H #define BATTERYWIDGET_H #include #include #include #include #include #include #include "lock-dialog/lockdialogmodel.h" typedef enum { battery_state_unknown = 0, battery_state_charging, battery_state_discharging, battery_state_empty, battery_state_fully, battery_state_pending_charge, battery_state_pending_discharge, battery_state_last } BatteryState; class BatteryWidget : public QWidget { Q_OBJECT public: BatteryWidget(LockDialogModel *model, QWidget *parent = nullptr); void initUi(); void setupComponent(); void setPoint(QPoint point); protected: void paintEvent(QPaintEvent *event); void changeEvent(QEvent *event); private: LockDialogModel *m_modelLockDialog = nullptr; QLabel *mModeLabel; QPushButton *mIconBtn; QLabel *mValueLabel; QLabel *mStatusLabel; QPoint mPoint; double m_nCurBatteryValue; int m_nCurBatteryState; bool m_hasBattery = true; bool m_isAcOnlineState = false; private Q_SLOTS: void onBatteryChanged(QStringList args); void onBatteryStatusChanged(QString iconName); void refreshTranslate(); }; #endif // BATTERYWIDGET_H ukui-screensaver/src/widgets/kmessagedialog.cpp0000664000175000017500000000537115172041106020716 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "kmessagedialog.h" KMessageDialog::KMessageDialog(int mod, QWidget *parent) : mode(mod), QWidget(parent) { init(); initConnections(); } KMessageDialog::~KMessageDialog() {} void KMessageDialog::init() { checkParent(parentWidget()); if (!m_dialog) { m_dialog = new BindDialog(mode, this); connect(m_dialog, SIGNAL(accepted(QString)), this, SIGNAL(accepted(QString))); connect(m_dialog, SIGNAL(rejected()), this, SIGNAL(rejected())); connect(m_dialog, &BindDialog::finished, this, [=]() { this->hide(); Q_EMIT finished(); }); } } void KMessageDialog::initConnections() {} void KMessageDialog::resizeEvent(QResizeEvent *event) { if (parentWidget()) { setFixedSize(parentWidget()->size()); move(0, 0); } QWidget::resizeEvent(event); } void KMessageDialog::showEvent(QShowEvent *event) { this->raise(); QWidget::showEvent(event); } void KMessageDialog::checkParent(QWidget *parent) { if (!parent) { return; } QWidget *tempParent = parent; bool isFoundSuitableParent = false; do { QString widgetClassName = tempParent->metaObject()->className(); if (widgetClassName == "GreeterWindow" || widgetClassName == "LockWidget") { isFoundSuitableParent = true; break; } tempParent = tempParent->parentWidget(); } while (tempParent); if (tempParent && isFoundSuitableParent) { setParent(tempParent); } } void KMessageDialog::setOtpPromptText(QString text) { if (m_dialog) m_dialog->setOtpPromptText(text); } void KMessageDialog::setOtpQrcodeText(QString text) { if (m_dialog) m_dialog->setOtpQrcodeText(text); } void KMessageDialog::setOtpPrpmptPromptText(QString text) { if (m_dialog) m_dialog->setOtpPrpmptPromptText(text); } void KMessageDialog::setOtpMessageText(QString text) { if (m_dialog) m_dialog->setOtpMessageText(text); } void KMessageDialog::setOtpEmergMsg(QString text) { if (m_dialog) m_dialog->setOtpEmergMsg(text); } ukui-screensaver/src/widgets/userlistwidget.h0000664000175000017500000000274215172041035020462 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef USERLISTWIDGET_H #define USERLISTWIDGET_H #include #include "lock-dialog/lockdialogmodel.h" #include "mylistwidget.h" class QListWidgetItem; extern float scale; class UserListWidget : public MyListWidget { Q_OBJECT public: explicit UserListWidget(LockDialogModel *model, QWidget *parent = nullptr); void updateWidgetSize(); public Q_SLOTS: void onUpdateListInfo(); void onListItemClicked(QListWidgetItem *item); Q_SIGNALS: void userSelected(QString strUserName); void widgetSizeChanged(); protected: void changeEvent(QEvent *event); private: void initUI(); void initConnections(); void refreshTranslate(); private: LockDialogModel *m_modelLockDialog = nullptr; double m_curFontSize; double m_ptToPx = 1.0; }; #endif // USERLISTWIDGET_H ukui-screensaver/src/widgets/screen-manager.cpp0000664000175000017500000003043215172041106020622 0ustar fengfeng#include "screen-manager.h" #include #include #include ScreenManager *ScreenManager::m_instance = nullptr; ScreenManager *ScreenManager::getInstance() { if (!m_instance) { m_instance = new ScreenManager(); qAddPostRoutine([]() { delete m_instance; m_instance = nullptr; }); } return m_instance; } ScreenManager::ScreenManager(QObject *parent) : QObject(parent), m_displayMode(Unknown), m_lastScreenId(0) { connect(qApp, &QGuiApplication::screenAdded, this, &ScreenManager::handleScreenAdded); connect(qApp, &QGuiApplication::screenRemoved, this, &ScreenManager::handleScreenRemoved); connect(qApp, &QGuiApplication::primaryScreenChanged, this, &ScreenManager::handlePrimaryScreenChanged); initScreens(); } ScreenManager::~ScreenManager() {} void ScreenManager::initScreens() { m_screenIds.clear(); m_screens.clear(); for (QScreen *screen : QGuiApplication::screens()) { if (screen == QGuiApplication::primaryScreen()) { // 确保主屏幕ID为0 m_screenIds[screen] = 0; m_screens[0] = screen; } else { // 分配自增ID int id = generateScreenId(); m_screenIds[screen] = id; m_screens[id] = screen; } connect(screen, &QScreen::geometryChanged, this, [=](const QRect &geometry) { handleScreenGeometryChanged(screen, geometry); }); qDebug() << "init screen " << screen->name() << screen->geometry() << m_screenIds[screen] << "primaryScreen:" << QGuiApplication::primaryScreen()->name(); } updateDisplayMode(); updateVirtualDesktopGeometry(); } int ScreenManager::generateScreenId() { // 找到一个未使用的ID m_lastScreenId = 0; while (m_screens.contains(m_lastScreenId)) { m_lastScreenId++; } return m_lastScreenId; } bool ScreenManager::updateDisplayMode() { DisplayMode oldMode = m_displayMode; if (screenCount() == 1) { m_displayMode = Single; } else { // 检查所有屏幕是否有相同的几何形状(镜像) bool allSameGeometry = true; if (m_screens.count() > 0) { QRect firstGeometry = m_screens.value(0)->geometry(); for (auto it = m_screens.begin(); it != m_screens.end(); ++it) { qDebug() << "DisplayMode get screen Geometry" << it.value()->geometry(); if (it.value()->geometry() != firstGeometry) { allSameGeometry = false; break; } } } m_displayMode = allSameGeometry ? Mirror : Extend; } qDebug() << "update mode: oldmode" << oldMode << "display mode" << m_displayMode; if (oldMode != m_displayMode) { Q_EMIT displayModeChanged(); // 根据模式切换进行处理 if (m_displayMode == Single || m_displayMode == Mirror) { switchToSingleScreenMode(); } else if (m_displayMode == Extend) { switchToMultiScreenMode(); } } return oldMode == m_displayMode; } void ScreenManager::updateVirtualDesktopGeometry() { QRect oldGeometry = m_virtualDesktopGeometry; // 计算所有屏幕的边界矩形 QRegion region; for (QScreen *screen : QGuiApplication::screens()) { region += screen->geometry(); } QRect virtualDesktopGeometry = region.boundingRect(); if (virtualDesktopGeometry.height() > 0 && virtualDesktopGeometry.width() > 0) { m_virtualDesktopGeometry = virtualDesktopGeometry; if (oldGeometry != m_virtualDesktopGeometry) { Q_EMIT virtualDesktopGeometryChanged(); } } } QScreen *ScreenManager::primaryScreen() const { return QGuiApplication::primaryScreen(); } bool ScreenManager::isPrimaryScreen(QScreen *screen) const { return primaryScreen() == screen; } QList ScreenManager::allScreens() const { return QGuiApplication::screens(); } int ScreenManager::screenCount() const { return QGuiApplication::screens().count(); } int ScreenManager::displayMode() const { return m_displayMode; } QRect ScreenManager::virtualDesktopGeometry() const { return m_virtualDesktopGeometry; } QVariantMap ScreenManager::getScreenInfo(int screenId) const { QVariantMap info; QScreen *screen = getScreenById(screenId); if (!screen) return info; info["id"] = screenId; info["name"] = screen->name(); info["geometry"] = screen->geometry(); info["availableGeometry"] = screen->availableGeometry(); info["size"] = screen->size(); info["physicalSize"] = screen->physicalSize(); info["logicalDotsPerInch"] = screen->logicalDotsPerInch(); info["physicalDotsPerInch"] = screen->physicalDotsPerInch(); info["refreshRate"] = screen->refreshRate(); info["isPrimary"] = (screen == primaryScreen()); return info; } QScreen *ScreenManager::getScreenById(int screenId) const { return m_screens.value(screenId, nullptr); } int ScreenManager::getScreenId(QScreen *screen) const { return m_screenIds.value(screen, -1); } int ScreenManager::getPrimaryScreenId() const { return m_screenIds.value(primaryScreen(), 0); } QRect ScreenManager::getScreenGeometry(int screenId) const { QScreen *screen = getScreenById(screenId); return screen ? screen->geometry() : QRect(); } QPoint ScreenManager::getScreenPosition(int screenId) const { QScreen *screen = getScreenById(screenId); return screen ? screen->geometry().topLeft() : QPoint(); } QSize ScreenManager::getScreenSize(int screenId) const { QScreen *screen = getScreenById(screenId); return screen ? screen->size() : QSize(); } qreal ScreenManager::getScreenScaleFactor(int screenId) const { QScreen *screen = getScreenById(screenId); return screen ? screen->devicePixelRatio() : 1.0; } int ScreenManager::getScreenIdAt(const QPoint &pos) const { if (m_displayMode == Mirror) return 0; for (auto it = m_screens.begin(); it != m_screens.end(); ++it) { if (it.value()->geometry().contains(pos)) { return it.key(); } } return -1; } QPoint ScreenManager::mapToGlobal(int screenId, const QPoint &pos) const { QScreen *screen = getScreenById(screenId); if (!screen) return pos; return screen->geometry().topLeft() + pos; } QPoint ScreenManager::mapFromGlobal(int screenId, const QPoint &pos) const { QScreen *screen = getScreenById(screenId); if (!screen) return pos; return pos - screen->geometry().topLeft(); } QList ScreenManager::getBackgroundWindows() const { return m_backgroundWindows.values(); } void ScreenManager::relocateWindows() { Q_EMIT windowsRelocateRequired(); } void ScreenManager::switchToSingleScreenMode() { Q_EMIT singleScreenModeActivated(); } void ScreenManager::switchToMultiScreenMode() { Q_EMIT multiScreenModeActivated(); } int ScreenManager::checkScreenMode(const QRect &geometry) { if (screenCount() == 1) { return Single; } for (QScreen *screen : QGuiApplication::screens()) { if (screen->geometry() != geometry) { return Extend; } } return Mirror; } void ScreenManager::handleScreenAdded(QScreen *screen) { if (!screen) return; if (screen->name().isEmpty()) return; qDebug() << "Screen added:" << screen->name() << m_screens; // screen的信号不及时,暂时先校验screen大小是否与桌面大小一致 for (auto it = m_backgroundWindows.begin(); it != m_backgroundWindows.end(); ++it) { QWidget *view = it.value(); int screenId = it.key(); QScreen *screen = m_screens.value(screenId); if (view && screen && view->geometry() != screen->geometry()) { view->setGeometry(screen->geometry()); } } // 为新屏幕分配ID int id = generateScreenId(); m_screenIds[screen] = id; m_screens[id] = screen; // 连接屏幕几何变化信号 connect(screen, &QScreen::geometryChanged, this, [=](const QRect &geometry) { handleScreenGeometryChanged(screen, geometry); }); // 更新显示模式和虚拟桌面几何形状 updateDisplayMode(); updateVirtualDesktopGeometry(); qDebug() << "screenCountChanged"; Q_EMIT screenAdded(screen, id); Q_EMIT screenCountChanged(); } void ScreenManager::handleScreenRemoved(QScreen *screen) { if (!screen) return; qDebug() << "Screen removed:" << screen->name(); // 获取屏幕ID,并从映射中删除 int id = m_screenIds.value(screen, -1); if (id >= 0) { m_screenIds.remove(screen); m_screens.remove(id); if (m_backgroundWindows.contains(id)) { QWidget *window = m_backgroundWindows.take(id); Q_EMIT windowRemoved(window, id); } } // 更新显示模式和虚拟桌面几何形状 updateDisplayMode(); updateVirtualDesktopGeometry(); Q_EMIT screenRemoved(screen, id); Q_EMIT screenCountChanged(); } void ScreenManager::handlePrimaryScreenChanged(QScreen *screen) { if (!screen) return; if (screen->name().isEmpty()) return; qDebug() << "Primary screen changed to:" << screen->name(); // 确保主屏幕ID为0 int newPrimaryId = m_screenIds.value(screen, -1); if (newPrimaryId < 0) { // 新主屏幕还没有ID,分配ID 0 m_screenIds[screen] = 0; m_screens[0] = screen; } else if (newPrimaryId != 0) { // 交换ID QScreen *oldPrimary = m_screens.value(0); if (oldPrimary) { m_screenIds[oldPrimary] = newPrimaryId; m_screens[newPrimaryId] = oldPrimary; } m_screenIds[screen] = 0; m_screens[0] = screen; // 交换窗口 QWidget *oldPrimaryWindow = m_backgroundWindows.value(0); QWidget *newPrimaryWindow = m_backgroundWindows.value(newPrimaryId); if (oldPrimaryWindow && newPrimaryWindow) { m_backgroundWindows[0] = newPrimaryWindow; QRect newPrimaryGeo = screen->geometry(); qDebug() << "new primay:" << screen->name() << newPrimaryGeo << newPrimaryWindow->geometry(); m_backgroundWindows[newPrimaryId] = oldPrimaryWindow; QRect oldPrimaryGeo = oldPrimary->geometry(); qDebug() << "old primay:" << oldPrimary->name() << oldPrimaryGeo << oldPrimaryWindow->geometry(); } } Q_EMIT primaryScreenChanged(); } void ScreenManager::handleScreenGeometryChanged(QScreen *screen, const QRect &geometry) { qDebug() << "Screen geometry changed:" << geometry; if (!screen) return; int id = m_screenIds.value(screen); QWidget *view = m_backgroundWindows.value(id); bool geometryChanged = false; if (view && view->geometry() != geometry) { view->setGeometry(geometry); geometryChanged = true; } bool modeChanged = updateDisplayMode(); if (modeChanged && geometryChanged) { windowsRelocateRequired(); } updateVirtualDesktopGeometry(); } void ScreenManager::addBackgroundWindow(QWidget *window, QScreen *screen, int id) { if (!window || !screen) return; int screenId = (id >= 0) ? id : getScreenId(screen); if (screenId < 0) { screenId = generateScreenId(); m_screenIds[screen] = screenId; m_screens[screenId] = screen; } m_backgroundWindows[screenId] = window; Q_EMIT windowAdded(window, screenId); } QWidget *ScreenManager::getWindowAt(const QPoint &pos) const { int id = m_displayMode == Mirror ? 0 : getScreenIdAt(pos); QWidget *window = m_backgroundWindows.value(id, nullptr); if (!window) { window = m_backgroundWindows.value(0); } return window; } QWidget *ScreenManager::getWindowById(int screenId) const { QWidget *window = m_backgroundWindows.value(screenId, nullptr); if (!window) { window = m_backgroundWindows.value(0); } return window; } void ScreenManager::removeBackgroundWindow(QWidget *window) { for (auto it = m_backgroundWindows.begin(); it != m_backgroundWindows.end(); ++it) { if (it.value() == window) { int screenId = it.key(); m_backgroundWindows.remove(screenId); Q_EMIT windowRemoved(window, screenId); break; } } } ukui-screensaver/src/widgets/mylistwidget.h0000664000175000017500000000320415172041106020122 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef MYLISTWIDGET_H #define MYLISTWIDGET_H #include #include class MyListWidget : public QListWidget { Q_OBJECT public: MyListWidget(QWidget *parent = nullptr); void setUserListWidget(bool isUserListWidget); void setIsLoginOptionWidget(bool isLoginOption); void setItemHidden(QListWidgetItem *item, bool hidden); inline void setMaxHeight(int nMaxHeight) { m_nMaxHeight = nMaxHeight; } inline int maxHeight() { return m_nMaxHeight; } protected: bool eventFilter(QObject *obj, QEvent *event); void changeEvent(QEvent *event); private: void refreshTranslate(); void updateItemState(QListWidgetItem *item, bool hidden); void selectNextVisibleItemAfter(int fromRow); private: // 标志位,记录前一个选中的item int m_lastRow = -1; bool m_isUserListWidget = false; bool m_isLoginOption = false; int m_nMaxHeight = -1; }; #endif // MYLISTWIDGET_H ukui-screensaver/src/widgets/screen-manager.h0000664000175000017500000000737115172041106020275 0ustar fengfeng#ifndef SCREENMANAGER_H #define SCREENMANAGER_H #include #include #include #include #include #include #include #include #include #include class ScreenManager : public QObject { Q_OBJECT Q_PROPERTY(QScreen *primaryScreen READ primaryScreen NOTIFY primaryScreenChanged) Q_PROPERTY(int screenCount READ screenCount NOTIFY screenCountChanged) Q_PROPERTY(int displayMode READ displayMode NOTIFY displayModeChanged) Q_PROPERTY(QRect virtualDesktopGeometry READ virtualDesktopGeometry NOTIFY virtualDesktopGeometryChanged) public: // 显示模式 enum DisplayMode { Unknown = 0, // 未知模式 Single = 1, // 单屏模式 Mirror = 2, // 镜像模式 Extend = 3 // 扩展模式 }; Q_ENUM(DisplayMode) static ScreenManager *getInstance(); // 屏幕信息方法 QScreen *primaryScreen() const; bool isPrimaryScreen(QScreen *screen) const; QList allScreens() const; int screenCount() const; int displayMode() const; QRect virtualDesktopGeometry() const; // 根据ID获取屏幕信息 Q_INVOKABLE QVariantMap getScreenInfo(int screenId) const; Q_INVOKABLE QScreen *getScreenById(int screenId) const; Q_INVOKABLE int getScreenId(QScreen *screen) const; Q_INVOKABLE int getPrimaryScreenId() const; // 屏幕位置和几何信息 Q_INVOKABLE QRect getScreenGeometry(int screenId) const; Q_INVOKABLE QPoint getScreenPosition(int screenId) const; Q_INVOKABLE QSize getScreenSize(int screenId) const; Q_INVOKABLE qreal getScreenScaleFactor(int screenId) const; // 判断一个点在哪个屏幕上 Q_INVOKABLE int getScreenIdAt(const QPoint &pos) const; Q_INVOKABLE QPoint mapToGlobal(int screenId, const QPoint &pos) const; Q_INVOKABLE QPoint mapFromGlobal(int screenId, const QPoint &pos) const; // 窗口管理 Q_INVOKABLE void relocateWindows(); // 屏幕模式管理 Q_INVOKABLE void switchToSingleScreenMode(); Q_INVOKABLE void switchToMultiScreenMode(); Q_INVOKABLE int checkScreenMode(const QRect &geometry); void initScreens(); bool updateDisplayMode(); void updateVirtualDesktopGeometry(); int generateScreenId(); void addBackgroundWindow(QWidget *window, QScreen *screen, int id = -1); void removeBackgroundWindow(QWidget *window); QList getBackgroundWindows() const; QWidget *getWindowAt(const QPoint &pos) const; QWidget *getWindowById(int screenId) const; public Q_SLOTS: void handleScreenAdded(QScreen *screen); void handleScreenRemoved(QScreen *screen); void handlePrimaryScreenChanged(QScreen *screen); void handleScreenGeometryChanged(QScreen *screen, const QRect &geometry); Q_SIGNALS: void primaryScreenChanged(); void screenAdded(QScreen *screen, int id); void screenRemoved(QScreen *screen, int id); void screenCountChanged(); void displayModeChanged(); void virtualDesktopGeometryChanged(); void windowsRelocateRequired(); // 模式切换信号 void switchDisplayModeActivated(); void singleScreenModeActivated(); void multiScreenModeActivated(); void windowAdded(QObject *window, int screenId); void windowRemoved(QObject *window, int screenId); private: explicit ScreenManager(QObject *parent = nullptr); ~ScreenManager(); static ScreenManager *m_instance; QHash m_screenIds; QHash m_screens; QHash m_backgroundWindows; DisplayMode m_displayMode; QRect m_virtualDesktopGeometry; // 上次分配的ID int m_lastScreenId; }; #define SCREEN_MANAGER ScreenManager::getInstance() #endif // SCREENMANAGER_H ukui-screensaver/src/widgets/kyiconbutton.h0000664000175000017500000000242515172041106020131 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef ICONBUTTON_H #define ICONBUTTON_H #include #include #include enum ICONBUTTONTYPE { EDITBUTTON, NORMALBUTTON, CAPCHABUTTON, }; class KyIconButton:public QPushButton { Q_OBJECT public: KyIconButton(int buttonType, QWidget* parent = nullptr); ~KyIconButton(); protected: void paintEvent(QPaintEvent * event); private: QString getElidedText(QFont font,int width,QString strInfo); static QColor mixColor(const QColor &c1, const QColor &c2, qreal bias = 0.5); private: int m_buttonType = NORMALBUTTON; }; #endif // ICONBUTTON_H ukui-screensaver/src/widgets/useritemwidget.h0000664000175000017500000000435515172041106020446 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef USERITEMWIDGET_H #define USERITEMWIDGET_H #include #include #include "customlabel.h" class userLabel; class QLabel; class QPixmap; class UserItemWidget : public QWidget { Q_OBJECT public: UserItemWidget(QWidget *parent = 0); ~UserItemWidget(); /** * @brief 设置用户名 * @param name 用户名 * @return */ void setUserName(QString name); /** * @brief 设置用户头像 * @param userIcon 用户头像 * @return */ void setUserPixmap(QPixmap userIcon); /** * @brief 设置用户登录状态 * @param status 登录状态 * @return */ void setUserStatus(bool status); /** * @brief 获取用户名 * @return QString 用户名 */ QString getUserName(); void setUserNickName(QString strNickName); inline QString userNickName() { return m_strNickName; } void setFontFamily(QString fontFamily); void setFontSize(double fontSize); protected: bool eventFilter(QObject *obj, QEvent *event); virtual void paintEvent(QPaintEvent *event); private: void init(); void initIconLabel(); Q_SIGNALS: void enterWidget(); void leaveWidget(); /** * @brief 响应点击事件 * @param userName 用户名 * @return */ void clicked(QString m_strUserName); private: QLabel *m_labelHeadImg = nullptr; CustomLabel *m_labelNickName = nullptr; QLabel *m_labelLoggedIn = nullptr; QString m_strUserName; QString m_strNickName; }; #endif // USERITEMWIDGET_H ukui-screensaver/src/widgets/pluginsloader.h0000664000175000017500000000513315172041035020251 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef PLUGINSLOADER_H #define PLUGINSLOADER_H #include #include #include class LoginPluginInterface; /** * @brief 插件加载器 * */ class PluginsLoader : public QThread { Q_OBJECT public: /** * @brief 实例 * * @return PluginsLoader 插件加载器 */ static PluginsLoader &instance(); /** * @brief 获取插件实例列表 * * @return QMap > 插件实例列表 */ inline QMap> moduleList() { return m_plugins; } /** * @brief 通过名称查找插件实例 * * @param name 插件名 * @return LoginPluginInterface 插件实例 */ LoginPluginInterface *findModuleByName(const QString &name) const; /** * @brief 通过类型查找插件 * * @param type 类型 * @return QMap 插件列表 */ QMap findModulesByType(const int type) const; /** * @brief 移除插件 * * @param moduleName 插件名 */ void removeModule(const QString &moduleName); Q_SIGNALS: /** * @brief 插件被加载信号 * * @param 插件实例 */ void moduleFound(LoginPluginInterface *); protected: /** * @brief 加载器运行过程 * */ void run() override; private: /** * @brief 加载器构造 * * @param parent 父对象指针 */ explicit PluginsLoader(QObject *parent = nullptr); /** * @brief 构造器析构 * */ virtual ~PluginsLoader(); /** * @brief 通过路径查找插件 * * @param path 插件路径 */ void findModule(const QString &path); private: QMap> m_plugins; /** 插件实例列表 */ }; #endif // PLUGINSLOADER_H ukui-screensaver/src/widgets/batterywidget.cpp0000664000175000017500000001430515172041106020612 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "batterywidget.h" #include #include #include #include #include #include BatteryWidget::BatteryWidget(LockDialogModel *model, QWidget *parent) : QWidget(parent), m_modelLockDialog(model) { // setWindowOpacity(0); setAttribute(Qt::WA_TranslucentBackground); initUi(); setupComponent(); } void BatteryWidget::initUi() { QVBoxLayout *Lyt = new QVBoxLayout(this); Lyt->setContentsMargins(16, 16, 16, 16); Lyt->setSpacing(8); mModeLabel = new QLabel(this); mModeLabel->setFixedHeight(24); mModeLabel->setStyleSheet("QLabel{font-size: 16px;font-family: NotoSansCJKsc-Bold, NotoSansCJKsc;" "font-weight: bold;}"); QHBoxLayout *Lyt_1 = new QHBoxLayout(); Lyt_1->setSpacing(4); mIconBtn = new QPushButton(this); mIconBtn->setFixedSize(48, 32); mIconBtn->setStyleSheet("QPushButton{\ color: rgba(255, 255, 255, 255);\ border: none;\ border-radius: 4px;\ outline: none;\ }"); mValueLabel = new QLabel(this); mValueLabel->setFixedHeight(48); mValueLabel->setMidLineWidth(48); mStatusLabel = new QLabel(this); mStatusLabel->setFixedHeight(36); mStatusLabel->setAlignment(Qt::AlignRight); Lyt_1->addWidget(mIconBtn); Lyt_1->addWidget(mValueLabel); Lyt_1->addStretch(); Lyt_1->addWidget(mStatusLabel); Lyt->addWidget(mModeLabel); Lyt->addLayout(Lyt_1); Lyt->addStretch(); } void BatteryWidget::setupComponent() { mIconBtn->setIconSize(QSize(48, 32)); mIconBtn->setFocusPolicy(Qt::NoFocus); mIconBtn->setIcon(QIcon::fromTheme(m_modelLockDialog->getBatteryIconName())); onBatteryChanged(m_modelLockDialog->getBatteryArgs()); connect(m_modelLockDialog, &LockDialogModel::batteryStatusChanged, this, &BatteryWidget::onBatteryStatusChanged); connect(m_modelLockDialog, &LockDialogModel::batteryChanged, this, &BatteryWidget::onBatteryChanged); } void BatteryWidget::setPoint(QPoint point) { mPoint = point; } void BatteryWidget::paintEvent(QPaintEvent *event) { QStyleOption opt; opt.initFrom(this); QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); p.save(); p.setBrush(opt.palette.color(QPalette::Base)); p.setPen(Qt::transparent); p.setOpacity(0.75); p.drawRoundedRect(this->rect(), 16, 16); p.restore(); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void BatteryWidget::onBatteryChanged(QStringList args) { if (args.count() > 0) { m_nCurBatteryValue = args.at(0).toDouble(); } if (args.count() > 1) { m_nCurBatteryState = args.at(1).toInt(); } if (args.count() > 3) { m_hasBattery = QVariant(args.at(3)).toBool(); } if (args.count() > 4) { m_isAcOnlineState = QVariant(args.at(4)).toBool(); } // 添加无电池情况的处理 if (!m_hasBattery) { mStatusLabel->setText(tr("No battery detected")); mStatusLabel->setVisible(true); mModeLabel->setText(tr("PowerMode")); mValueLabel->setText(""); return; } if (m_nCurBatteryState == battery_state_unknown || m_nCurBatteryState == battery_state_charging || m_nCurBatteryState == battery_state_fully || m_nCurBatteryState == battery_state_pending_charge) { if (m_nCurBatteryValue < 100) { mStatusLabel->setText(tr("Charging...")); } else { mStatusLabel->setText(tr("fully charged")); } } if (m_nCurBatteryState == battery_state_unknown || m_nCurBatteryState == battery_state_charging || m_nCurBatteryState == battery_state_fully || m_nCurBatteryState == battery_state_pending_charge) { mStatusLabel->setVisible(true); mModeLabel->setText(tr("PowerMode")); } else { mStatusLabel->setVisible(false); mModeLabel->setText(tr("BatteryMode")); } mValueLabel->setText(QString("%1%").arg(m_nCurBatteryValue)); } void BatteryWidget::onBatteryStatusChanged(QString iconName) { mIconBtn->setIcon(QIcon::fromTheme(iconName)); } void BatteryWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { refreshTranslate(); } } void BatteryWidget::refreshTranslate() { // 添加无电池情况的处理 if (!m_hasBattery) { mStatusLabel->setText(tr("No battery detected")); mStatusLabel->setVisible(true); mModeLabel->setText(tr("PowerMode")); mValueLabel->setText(""); return; } if (m_nCurBatteryState == battery_state_unknown || m_nCurBatteryState == battery_state_charging || m_nCurBatteryState == battery_state_fully || m_nCurBatteryState == battery_state_pending_charge) { if (m_nCurBatteryValue < 100) { mStatusLabel->setText(tr("Charging...")); } else { mStatusLabel->setText(tr("fully charged")); } } if (m_nCurBatteryState == battery_state_unknown || m_nCurBatteryState == battery_state_charging || m_nCurBatteryState == battery_state_fully || m_nCurBatteryState == battery_state_pending_charge) { mStatusLabel->setVisible(true); mModeLabel->setText(tr("PowerMode")); } else { mStatusLabel->setVisible(false); mModeLabel->setText(tr("BatteryMode")); } mValueLabel->setText(QString("%1%").arg(m_nCurBatteryValue)); } ukui-screensaver/src/widgets/grab-x11.cpp0000664000175000017500000002045515172041106017261 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "grab-x11.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #define MAX_PROPERTY_VALUE_LEN 4096 class XServerGraber { public: XServerGraber() { xcb_grab_server((xcb_connection_t*)getX11Connection()); } ~XServerGraber() { xcb_ungrab_server((xcb_connection_t*)getX11Connection()); xcb_flush((xcb_connection_t*)getX11Connection()); } }; static bool grabKeyboard() { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return false; } int rv = XGrabKeyboard(display, DefaultRootWindow(display), True, GrabModeAsync, GrabModeAsync, CurrentTime); return (rv == GrabSuccess); } static bool grabMouse() { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return false; } #define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask int rv = XGrabPointer( display, DefaultRootWindow(display), True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); #undef GRABEVENTS return (rv == GrabSuccess); } bool establishGrab() { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return false; } XSync(display, False); XServerGraber xserverGraber; Q_UNUSED(xserverGraber); if (!grabKeyboard()) return false; /*抓取鼠标会导致触摸无效,这里暂时取消掉*/ /* if(!grabMouse()) { XUngrabKeyboard(display, CurrentTime); XFlush(display); return false; } */ return true; } bool closeGrab() { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return false; } // XSync(display, False); XServerGraber xserverGraber; Q_UNUSED(xserverGraber); XUngrabKeyboard(display, CurrentTime); // XUngrabPointer(display, CurrentTime); XFlush(display); return true; } static Bool Window_Has_Property(Display *dpy, Window win, Atom atom) { Atom type_ret; int format_ret; unsigned char *prop_ret; unsigned long bytes_after, num_ret; type_ret = None; prop_ret = NULL; XGetWindowProperty( dpy, win, atom, 0, 0, False, AnyPropertyType, &type_ret, &format_ret, &num_ret, &bytes_after, &prop_ret); if (prop_ret) XFree(prop_ret); return (type_ret != None) ? True : False; } int getWindowByPid(Window win) { int ret = -1; Atom xa_ret_type; int ret_format; unsigned long ret_nitems; unsigned long ret_bytes_after; unsigned long tmp_size; unsigned char *ret_prop; int id = 0; Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return -1; } Atom xa_prop_name = XInternAtom(display, "_NET_WM_PID", False); // 取对应字串的AtomID if (XGetWindowProperty( display, win, xa_prop_name, 0, // 获取窗口属性 MAX_PROPERTY_VALUE_LEN / 4, False, XA_CARDINAL, &xa_ret_type, // XA_CARDINAL为数值类型 &ret_format, &ret_nitems, &ret_bytes_after, &ret_prop) != Success) // 后五个参数是返回值 { printf("Cannot get %s property.\n", "_NET_WM_PID"); return -1; } else { if (ret_prop) memcpy(&id, ret_prop, 4); // 类型传换 qDebug() << "window pid: " << id; } return 0; } static Bool Window_Is_Viewable(Display *dpy, Window win) { Bool ok; XWindowAttributes xwa; XGetWindowAttributes(dpy, win, &xwa); ok = (xwa.c_class == InputOutput) && (xwa.map_state == IsViewable); return ok; } static Bool getIsPopupMenu(Display *disp, Window win, Atom xa_prop_type, const char *prop_name, ulong *size) { Atom xa_prop_name; Atom xa_ret_type; int ret_format; ulong ret_nitems; ulong ret_bytes_after; ulong tmp_size; unsigned char *ret_prop; char *ret; xa_prop_name = XInternAtom(disp, prop_name, False); qDebug() << "xa_prop_name = " << xa_prop_name; if (XGetWindowProperty( disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, False, xa_prop_type, &xa_ret_type, &ret_format, &ret_nitems, &ret_bytes_after, &ret_prop) != Success) { printf("Cannot get %s property.\n", prop_name); return false; } qDebug() << "xa_ret_type = " << xa_ret_type << "ret_format = " << ret_format << "ret_nitems = " << ret_nitems << "xa_prop_type = " << xa_prop_type; if (xa_ret_type != xa_prop_type) { printf("Invalid type of %s property.\n", prop_name); XFree(ret_prop); return false; } Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return false; } Atom type, *adata; adata = (Atom *)ret_prop; int i = 0; while (i < (int)ret_nitems) { QString windowType = XGetAtomName(display, adata[i]); if (windowType == "_NET_WM_WINDOW_TYPE_POPUP_MENU") return true; i++; } qDebug() << "ret = " << ret; if (size) { *size = tmp_size; } XFree(ret_prop); return false; } QString getWindowNameByWid(Window window) { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return QString(""); } XClassHint ch; ch.res_name = NULL; ch.res_class = NULL; XGetClassHint(display, window, &ch); QString res(ch.res_name); if (ch.res_name) XFree(ch.res_name); if (ch.res_class) XFree(ch.res_class); qDebug() << "CurFocusWnd:" << res; return res; } bool checkHasPopupMenu() { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return false; } Window focus = 0; int rev = 0; XGetInputFocus(display, &focus, &rev); if (focus != None && focus != PointerRoot) { Window root, parent; Window *children; unsigned int n_children; int i; Window root_window = DefaultRootWindow(display); if (XQueryTree(display, root_window, &root, &parent, &children, &n_children)) { for (i = (int)n_children - 1; i >= 0; i--) { if (!Window_Is_Viewable(display, children[i])) { children[i] = None; /* Don't bother descending into this one */ continue; } if (!Window_Has_Property( display, children[i], XInternAtom(display, "_NET_WM_WINDOW_TYPE", False))) continue; if (getIsPopupMenu(display, children[i], XA_ATOM, "_NET_WM_WINDOW_TYPE", NULL)) { return true; } if (getWindowNameByWid(children[i]) == "ukui-session-tools") { return true; } continue; } } } return false; } ukui-screensaver/src/widgets/userlistwidget.cpp0000664000175000017500000001050615172041035021012 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "userlistwidget.h" #include #include "useritemwidget.h" UserListWidget::UserListWidget(LockDialogModel *model, QWidget *parent) : MyListWidget(parent), m_modelLockDialog(model) { initUI(); initConnections(); setUserListWidget(true); } void UserListWidget::initUI() { QString userListStyle = "QListWidget{ background-color: rgba(255,255,255,15%); border-radius: 8px; padding: 5px 5px 5px 5px;}" "QListWidget::item{background:rgba(255,255,255,0%);height:40px; border-radius:4px}" "QListWidget::item:hover{color:#333333; background-color:rgba(255,255,255,20%)}" "QListWidget::item::selected{border-radius: 4px;background-color:rgba(255,255,255,40%);}"; setStyleSheet(userListStyle); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); verticalScrollBar()->setProperty("drawScrollBarGroove", false); m_curFontSize = m_modelLockDialog->getCurFontSize(); m_ptToPx = m_modelLockDialog->getPtToPx(); onUpdateListInfo(); } void UserListWidget::initConnections() { // 响应点击事件 connect(this, &UserListWidget::itemClicked, this, &UserListWidget::onListItemClicked); connect(m_modelLockDialog, &LockDialogModel::usersInfoChanged, this, &UserListWidget::onUpdateListInfo); } void UserListWidget::onUpdateListInfo() { this->clear(); for (auto user : m_modelLockDialog->usersInfo()) { if (user->isSystemAccount()) { continue; } QListWidgetItem *userItem = new QListWidgetItem(); userItem->setSizeHint(QSize(420, 40)); insertItem(count(), userItem); UserItemWidget *itemWidget = new UserItemWidget(this); QPixmap p(user->headImage()); itemWidget->setUserPixmap(p); if (user->name() == "*login") { itemWidget->setUserNickName(tr("Login")); } else if (user->name() == "*guest") { itemWidget->setUserNickName(tr("Guest")); } else { itemWidget->setUserNickName(user->fullName()); } itemWidget->setUserStatus(user->isLoggedIn()); if (user->fullName() == m_modelLockDialog->currentUserName()) { setCurrentItem(userItem, QItemSelectionModel::SelectionFlag::SelectCurrent); } itemWidget->setUserName(user->name()); itemWidget->setFontSize((14 + m_curFontSize) * m_ptToPx); setItemWidget(userItem, itemWidget); } updateWidgetSize(); } void UserListWidget::onListItemClicked(QListWidgetItem *item) { QWidget *widget = itemWidget(item); UserItemWidget *currentItem = qobject_cast(widget); if (currentItem) { Q_EMIT userSelected(currentItem->getUserName()); } } void UserListWidget::updateWidgetSize() { setFixedWidth(420); if (maxHeight() >= 0 && (count() * 40 + 10) > maxHeight()) { setFixedHeight(maxHeight()); } else { setFixedHeight(count() * 40 + 10); } adjustSize(); Q_EMIT widgetSizeChanged(); } void UserListWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { refreshTranslate(); } } void UserListWidget::refreshTranslate() { for (int n = 0; n < count(); n++) { QListWidgetItem *widgetItem = item(n); if (widgetItem) { QWidget *widget = itemWidget(widgetItem); UserItemWidget *item = qobject_cast(widget); if (item) { if (item->getUserName() == "*login") { item->setUserNickName(tr("Login")); } else if (item->getUserName() == "*guest") { item->setUserNickName(tr("Guest")); } } } } } ukui-screensaver/src/widgets/statusbutton.cpp0000664000175000017500000002320215172041106020507 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "statusbutton.h" #include #include #include StatusButton::StatusButton(QWidget *parent, int type) : QPushButton(parent) { buttonType = type; if (buttonType == BIOBUTTON || type == BOTBUTTON) { setFixedSize(48, 48); setIconSize(QSize(24, 24)); } else if (buttonType == POWERBUTTON) { setFixedSize(130, 130); setIconSize(QSize(48, 48)); } else if (buttonType == NORMALBTN) { setFixedHeight(48); setFocusPolicy(Qt::TabFocus); } else if (buttonType == PWDTIPBTN) { setFixedSize(48, 48); setFocusPolicy(Qt::TabFocus); } if (buttonType != NORMALBTN) this->setFocusPolicy(Qt::NoFocus); } void StatusButton::setClickedStatus(int status) { curStatus = status; update(); } void StatusButton::setDrivedId(int drivedId) { m_drivedId = drivedId; } void StatusButton::paintEvent(QPaintEvent *e) { QStylePainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QStyleOptionButton option; initStyleOption(&option); if (curStatus == NORMAL) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); switch (buttonType) { case BOTBUTTON: case PWDTIPBTN: painter.setOpacity(0); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; case BIOBUTTON: painter.setOpacity(0.1); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; case POWERBUTTON: painter.setOpacity(0.1); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 65, 65); break; case NORMALBTN: painter.setOpacity(0); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 24, 24); break; case SIDEBARBTN: painter.setOpacity(0); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 0, 0); break; default: painter.setOpacity(0); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; } painter.restore(); } if (option.state & QStyle::State_MouseOver) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.15); switch (buttonType) { case BOTBUTTON: case BIOBUTTON: case PWDTIPBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; case POWERBUTTON: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 65, 65); break; case NORMALBTN: painter.setOpacity(0.15); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 24, 24); break; case SIDEBARBTN: painter.setOpacity(0); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 0, 0); break; default: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; } painter.restore(); } if (curStatus == CLICKED) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.21); switch (buttonType) { case BOTBUTTON: case BIOBUTTON: case PWDTIPBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; case POWERBUTTON: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 65, 65); break; case NORMALBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 24, 24); break; case SIDEBARBTN: painter.setOpacity(0); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 0, 0); break; default: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; } painter.restore(); } if (curStatus == SELECT) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.25); switch (buttonType) { case BOTBUTTON: case BIOBUTTON: case PWDTIPBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; case POWERBUTTON: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 65, 65); break; case NORMALBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 24, 24); break; case SIDEBARBTN: painter.setOpacity(0); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 0, 0); break; default: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; } painter.restore(); painter.save(); QStyleOption opt; QColor color = opt.palette.color(QPalette::Highlight); painter.setPen(QPen(color, 2)); painter.setBrush(Qt::NoBrush); switch (buttonType) { case BOTBUTTON: case BIOBUTTON: case PWDTIPBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; case POWERBUTTON: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 65, 65); break; case NORMALBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 24, 24); break; case SIDEBARBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 0, 0); break; default: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; } painter.restore(); } if (option.state & QStyle::State_HasFocus) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.25); switch (buttonType) { case NORMALBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 24, 24); break; default: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; } painter.restore(); painter.save(); QStyleOption opt; QColor color = opt.palette.color(QPalette::Highlight); painter.setPen(QPen(color, 2)); painter.setBrush(Qt::NoBrush); switch (buttonType) { case NORMALBTN: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 24, 24); break; default: painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); break; } painter.restore(); } // 绘制文字 if (buttonType == NORMALBTN && !text().isEmpty() && text() != "") { painter.setPen(QColor(255, 255, 255, 255)); painter.setFont(font()); painter.drawText(this->rect(), Qt::AlignHCenter | Qt::AlignVCenter, this->text()); } // 绘制图片 int buttonWidget = this->width(); int buttonHeight = this->height(); QRect buttonRect(0, 0, buttonWidget, buttonHeight); QPixmap pixmap = option.icon.pixmap(option.iconSize, QIcon::Active); int pixmapWidth = static_cast(pixmap.width() / qApp->devicePixelRatio()); int pixmapHeight = static_cast(pixmap.height() / qApp->devicePixelRatio()); QRect pixmapRect(0, 0, pixmapWidth, pixmapHeight); int deltaX = 0; int deltaY = 0; if (pixmapRect.width() < buttonRect.width()) deltaX = buttonRect.width() - pixmapRect.width(); else deltaX = pixmapRect.width() - buttonRect.width(); if (pixmapRect.height() < buttonRect.height()) deltaY = buttonRect.height() - pixmapRect.height(); else deltaY = pixmapRect.height() - buttonRect.height(); painter.save(); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); painter.translate(deltaX / 2, deltaY / 2); painter.drawPixmap(pixmapRect, pixmap); painter.restore(); } void StatusButton::mouseReleaseEvent(QMouseEvent *e) { if (buttonType == NORMALBTN || buttonType == SIDEBARBTN) { Q_EMIT clicked(); } else if (buttonType == BIOBUTTON) { } e->ignore(); } void StatusButton::mousePressEvent(QMouseEvent *e) { if (buttonType == NORMALBTN || buttonType == SIDEBARBTN) { Q_EMIT clicked(); } e->ignore(); } ukui-screensaver/src/widgets/kymprisdbusclient.cpp0000664000175000017500000001453015172041035021510 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "kymprisdbusclient.h" #define MPRIS_DBUS_INTERFACE "org.mpris.MediaPlayer2.Player" KyMPRISDBusClient::KyMPRISDBusClient( const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) : QDBusAbstractInterface(service, path, MPRIS_DBUS_INTERFACE, connection, parent) { QDBusConnection::sessionBus().connect( this->service(), this->path(), "org.freedesktop.DBus.Properties", "PropertiesChanged", "sa{sv}as", this, SLOT(onPropertiesChanged(QDBusMessage))); } KyMPRISDBusClient::~KyMPRISDBusClient() { QDBusConnection::sessionBus().disconnect( service(), path(), "org.freedesktop.DBus.Properties", "PropertiesChanged", "sa{sv}as", this, SLOT(onPropertiesChanged(QDBusMessage))); } QString KyMPRISDBusClient::getPlaybackStatus() const { return qvariant_cast(property("PlaybackStatus")); } QString KyMPRISDBusClient::getLoopStatus() const { return qvariant_cast(property("LoopStatus")); } void KyMPRISDBusClient::setLoopStatus(const QString &value) { KyMPRISDBusClient::setProperty("LoopStatus", QVariant::fromValue(value)); } double KyMPRISDBusClient::getRate() const { return qvariant_cast(property("Rate")); } void KyMPRISDBusClient::setRate(double value) { KyMPRISDBusClient::setProperty("Rate", QVariant::fromValue(value)); } bool KyMPRISDBusClient::getShuffle() const { return qvariant_cast(property("Shuffle")); } void KyMPRISDBusClient::setShuffle(bool value) { KyMPRISDBusClient::setProperty("Shuffle", QVariant::fromValue(value)); } QVariantMap KyMPRISDBusClient::getMetadata() const { return qvariant_cast(property("Metadata")); } double KyMPRISDBusClient::getVolume() const { return qvariant_cast(property("Volume")); } void KyMPRISDBusClient::setVolume(double value) { KyMPRISDBusClient::setProperty("Volume", QVariant::fromValue(value)); } qlonglong KyMPRISDBusClient::getPosition() const { return qvariant_cast(property("Position")); } double KyMPRISDBusClient::getMinimumRate() const { return qvariant_cast(property("MinimumRate")); } double KyMPRISDBusClient::getMaximumRate() const { return qvariant_cast(property("MaximumRate")); } bool KyMPRISDBusClient::getCanGoNext() const { return qvariant_cast(property("CanGoNext")); } bool KyMPRISDBusClient::getCanGoPrevious() const { return qvariant_cast(property("CanGoPrevious")); } bool KyMPRISDBusClient::getCanPlay() const { return qvariant_cast(property("CanPlay")); } bool KyMPRISDBusClient::getCanPause() const { return qvariant_cast(property("CanPause")); } bool KyMPRISDBusClient::getCanSeek() const { return qvariant_cast(property("CanSeek")); } bool KyMPRISDBusClient::getCanControl() const { return qvariant_cast(property("CanControl")); } void KyMPRISDBusClient::onPropertiesChanged(const QDBusMessage &msg) { QList arguments = msg.arguments(); if (3 != arguments.count()) return; QString interfaceName = msg.arguments().at(0).toString(); if (interfaceName != MPRIS_DBUS_INTERFACE) return; QVariantMap changedProperties = qdbus_cast(arguments.at(1).value()); for (const QString &prop : changedProperties.keys()) { const QMetaObject *self = metaObject(); for (int i = self->propertyOffset(); i < self->propertyCount(); ++i) { QMetaProperty p = self->property(i); QGenericArgument value(QMetaType::typeName(p.type()), const_cast(changedProperties[prop].constData())); if (p.name() == prop) { Q_EMIT p.notifySignal().invoke(this, value); } } } } QDBusPendingReply<> KyMPRISDBusClient::doNext() { QList argumentList; qDebug()<<"media Next!!"; return asyncCallWithArgumentList(QStringLiteral("Next"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doPrevious() { QList argumentList; qDebug()<<"media Previous!!"; return asyncCallWithArgumentList(QStringLiteral("Previous"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doPause() { QList argumentList; qDebug()<<"media Pause!!"; return asyncCallWithArgumentList(QStringLiteral("Pause"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doPlayPause() { QList argumentList; qDebug()<<"media PlayPause!!"; return asyncCallWithArgumentList(QStringLiteral("PlayPause"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doStop() { QList argumentList; return asyncCallWithArgumentList(QStringLiteral("Stop"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doPlay() { QList argumentList; qDebug()<<"media Play!!"; return asyncCallWithArgumentList(QStringLiteral("Play"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doSeek(qlonglong Offset) { QList argumentList; argumentList << QVariant::fromValue(Offset); return asyncCallWithArgumentList(QStringLiteral("Seek"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doSetPosition(const QDBusObjectPath &TrackId, qlonglong Position) { QList argumentList; argumentList << QVariant::fromValue(TrackId) << QVariant::fromValue(Position); return asyncCallWithArgumentList(QStringLiteral("SetPosition"), argumentList); } QDBusPendingReply<> KyMPRISDBusClient::doOpenUri(const QString &Uri) { QList argumentList; argumentList << QVariant::fromValue(Uri); return asyncCallWithArgumentList(QStringLiteral("OpenUri"), argumentList); } ukui-screensaver/src/widgets/screensavermode.cpp0000664000175000017500000001036215172041106021120 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screensavermode.h" #include #include #include ScreenSaver::ScreenSaver(QObject *parent) : QObject(parent), imageIndex(0), timer(nullptr) {} ScreenSaver::ScreenSaver(const ScreenSaver &screensaver) : mode(screensaver.mode) , path(screensaver.path) , effect(screensaver.effect) , interval(screensaver.interval) , imageIndex(screensaver.imageIndex) , timer(screensaver.timer) , imagePaths(screensaver.imagePaths) { } ScreenSaver::ScreenSaver(ScreenSaver &&screensaver) noexcept : mode(screensaver.mode) , path(screensaver.path) , effect(screensaver.effect) , interval(screensaver.interval) , imageIndex(screensaver.imageIndex) , timer(screensaver.timer) , imagePaths(screensaver.imagePaths) { } bool ScreenSaver::exists() { switch (mode) { case SAVER_BLANK_ONLY: return true; case SAVER_RANDOM: case SAVER_SINGLE: return QFile(path).exists(); case SAVER_IMAGE: return QDir(path).exists(); case SAVER_DEFAULT: return QFile(path).exists(); case SAVER_DEFAULT_CUSTOM: return QFile(path).exists(); default: return false; } return false; } void ScreenSaver::startSwitchImages() { qDebug() << "ScreenSaver::startSwitchImages"; if (mode != SAVER_IMAGE) return; QFileInfo fileInfo(path); if (fileInfo.isFile()) return; QList formats = QImageReader::supportedImageFormats(); if (fileInfo.isDir()) { QDir dir(path); QStringList files = dir.entryList(QDir::Files | QDir::Readable); for (QString file : files) { fileInfo.setFile(file); QString suffix = fileInfo.suffix(); if (formats.contains(suffix.toUtf8())) imagePaths.push_back(path + "/" + file); } if (!imagePaths.empty()) { path = imagePaths[0]; timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [&] { imageIndex = (imageIndex + 1) % imagePaths.size(); path = imagePaths[imageIndex]; lastPath = imagePaths[imageIndex - 1 < 0 ? imagePaths.size() - 1 : imageIndex - 1]; Q_EMIT imagePathChanged(path); }); timer->start(interval * 1000); Q_EMIT imagePathChanged(path); } } } void ScreenSaver::stopSwitchImages() { if (timer && timer->isActive()) timer->stop(); } bool ScreenSaver::timerStatus() { return timer->isActive(); } QDebug& operator<<(QDebug& debug, const ScreenSaver &screensaver) { QString modes[] = { "blank-only", "random", "single", "image", "default-ukui", "default-ukui-custom" }; QString effects[] = { "none", "fade-in-out" }; debug.nospace() << "screensaver: " << modes[screensaver.mode]; switch (screensaver.mode) { case SAVER_BLANK_ONLY: break; case SAVER_RANDOM: case SAVER_SINGLE: debug.nospace() << screensaver.path; break; case SAVER_IMAGE: debug.nospace() << screensaver.path << effects[screensaver.effect] << screensaver.interval; case SAVER_DEFAULT: debug.nospace() << screensaver.path << effects[screensaver.effect] << screensaver.interval; case SAVER_DEFAULT_CUSTOM: debug.nospace() << screensaver.path << effects[screensaver.effect] << screensaver.interval; default: debug.nospace() << "screensaver path not exists"; } return debug.maybeSpace(); } ukui-screensaver/src/widgets/kmessagedialog.h0000664000175000017500000000277215172041106020365 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef KMESSAGEDIALOG_H #define KMESSAGEDIALOG_H #include #include #include "binddialog.h" class KMessageDialog : public QWidget { Q_OBJECT public: explicit KMessageDialog(int mode,QWidget *parent = nullptr); virtual ~KMessageDialog(); void setOtpPromptText(QString text); void setOtpQrcodeText(QString text); void setOtpPrpmptPromptText(QString text); void setOtpMessageText(QString text); void setOtpEmergMsg(QString text); protected: void resizeEvent(QResizeEvent *); void showEvent(QShowEvent *); Q_SIGNALS: void accepted(QString text); void rejected(); void finished(); private: void init(); void initConnections(); void checkParent(QWidget *parent); private: BindDialog *m_dialog = nullptr; int mode = -1; }; #endif // KMESSAGEDIALOG_H ukui-screensaver/src/widgets/mynetworkwidget.cpp0000664000175000017500000001753115172041106021203 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "mynetworkwidget.h" #include #include #include #include #include #include #include #include #include #include "mytabwidget.h" MyNetworkWidget::MyNetworkWidget(QWidget *parent) : QWidget(parent) { setObjectName("m_networkWidget"); // setStyleSheet("#m_networkWidget{background-color: white; border-radius:12px;}"); } void MyNetworkWidget::loadNetPlugin() { if (netloader.isLoaded() || wlanloader.isLoaded()) return; if (!tabWidget) { tabWidget = new MyTabWidget(this); tabWidget->setObjectName("tabwidget"); //网络弹窗的高度仍保持528不变,这里将网络窗口里的TabWidget减小一个圆角的高度 //解决切换有线网络和无线网络时显示黑边的问题 tabWidget->setFixedSize(420, 528 - 12); tabWidget->tabBar()->setFixedHeight(40); tabWidget->installEventFilter(this); QHBoxLayout *m_tabBarLayout = new QHBoxLayout(tabWidget); m_tabBarLayout->setContentsMargins(0, 0, 0, 0); QLabel *m_lanLabel = new QLabel(tr("LAN")); m_lanLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); QLabel *m_wlanLabel = new QLabel(tr("WLAN")); m_wlanLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_tabBarLayout->addWidget(m_lanLabel); m_tabBarLayout->addWidget(m_wlanLabel); tabWidget->tabBar()->setLayout(m_tabBarLayout); tabWidget->tabBar()->setFixedWidth(420); } netloader.setFileName("/usr/lib/kylin-nm/libnetconnect.so"); //有线 wlanloader.setFileName("/usr/lib/kylin-nm/libwlanconnect.so"); //无线 //如果要修改插件加载顺序,涉及mkylinNM控件index的都需要修改 if (netloader.load()) { QObject *plugin = netloader.instance(); if (plugin) { netInterface = qobject_cast(plugin); // 插件是否启用 if (!netInterface) { return; } netInterface->setPluginType(SIMPLE); QWidget *widget = netInterface->pluginUi(); tabWidget->addTab(widget, ""); // connect(plugin, SIGNAL(updatePluginHidden(bool)), this, SLOT(onUpdatePluginHidden(bool))); } else { qDebug() << "Load Failed: " << netloader.errorString() << "\n"; return; } } else { qDebug() << "Load Failed: " << netloader.errorString() << "\n"; return; } if (wlanloader.load()) { QObject *plugin = wlanloader.instance(); if (plugin) { wlanInterface = qobject_cast(plugin); // 插件是否启用 if (!wlanInterface) { return; } wlanInterface->setPluginType(SIMPLE); /*这里需要先调用setParentWidget,否则会出现网络连接弹窗无法弹出来的问题*/ wlanInterface->setParentWidget(this); QWidget *widget = wlanInterface->pluginUi(); tabWidget->addTab(widget, ""); // connect(plugin, SIGNAL(updatePluginHidden(bool)), this, SLOT(onUpdatePluginHidden(bool))); // 平板模式输入状态下自动调出虚拟键盘 connect(wlanInterface, SIGNAL(needShowVirtualKeyboard()), this, SLOT(onNetInPutStatus())); } else { qDebug() << "Load Failed: " << wlanloader.errorString() << "\n"; return; } } else { qDebug() << "Load Failed: " << wlanloader.errorString() << "\n"; return; } QPalette pal = qApp->palette(); pal.setBrush(QPalette::Window, Qt::white); // m_kylinNM->setPalette(pal); tabWidget->widget(0)->setPalette(pal); tabWidget->widget(1)->setPalette(pal); setGeometry( this->width() - tabWidget->width() - 20, this->height() - tabWidget->height() - 72 - 8, tabWidget->width(), tabWidget->height() + 12); connect(tabWidget, &MyTabWidget::currentChanged, this, &MyNetworkWidget::onNetTabWidgetChanged); onUpdatePluginHidden(); } void MyNetworkWidget::unloadNetPlugin() { if (wlanloader.isLoaded()) { tabWidget->removeTab(1); wlanloader.unload(); } if (netloader.isLoaded()) { tabWidget->removeTab(0); netloader.unload(); } if (tabWidget) { tabWidget->hide(); tabWidget->deleteLater(); tabWidget = nullptr; } if (tabWidget) { tabWidget->hide(); tabWidget->deleteLater(); tabWidget = nullptr; } } void MyNetworkWidget::onNetTabWidgetChanged(int index) { /* if(index == 0){ if(netInterface) netInterface->setWidgetVisable(true); if(wlanInterface){ wlanInterface->setWidgetVisable(false); } }else if(index == 1){ if(netInterface) netInterface->setWidgetVisable(false); if(wlanInterface) wlanInterface->setWidgetVisable(true); } */ } void MyNetworkWidget::onUpdatePluginHidden() { /* if (netInterface && m_netTabShow != netInterface->checkPluginIsHidden()) { if (m_netTabShow) { tabWidget->removeTab(0); } else { tabWidget->insertTab(0, netWidget, ""); } m_netTabShow = !m_netTabShow; } if (wlanInterface && m_wlanTabShow != wlanInterface->checkPluginIsHidden()) { if (m_wlanTabShow) { if (m_netTabShow) tabWidget->removeTab(1); else tabWidget->removeTab(0); } else { if (m_netTabShow) tabWidget->insertTab(1, wlanWidget, ""); else tabWidget->insertTab(0, wlanWidget, ""); } m_wlanTabShow = !m_wlanTabShow; } if (m_netTabShow && m_wlanTabShow) { tabWidget->tabBar()->show(); } else { tabWidget->tabBar()->hide(); } */ } void MyNetworkWidget::sendNetPluginVisible(bool visible) { /* if(!tabWidget) return; if(tabWidget->currentIndex() == 0 && netInterface){ netInterface->setWidgetVisable(visible); }else if(tabWidget->currentIndex() == 1 && wlanInterface){ wlanInterface->setWidgetVisable(visible); } */ } void MyNetworkWidget::onNetInPutStatus() { Q_EMIT showVirtualKeyboard(); } void MyNetworkWidget::showEvent(QShowEvent *event) { sendNetPluginVisible(true); } void MyNetworkWidget::hideEvent(QHideEvent *event) { sendNetPluginVisible(false); } void MyNetworkWidget::paintEvent(QPaintEvent *event) { QPalette pal = qApp->palette(); QColor colorPal = pal.color(QPalette::Base); //设置窗体为圆角 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 反锯齿; painter.setBrush(colorPal); painter.setPen(Qt::transparent); auto rect = this->rect(); painter.drawRoundedRect(rect, 12, 12); //窗口圆角 return QWidget::paintEvent(event); } ukui-screensaver/src/widgets/passwdcheckutil.h0000664000175000017500000000324115172041106020573 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef PASSWDCHECKUTIL_H #define PASSWDCHECKUTIL_H #include #define PWCHK_ERROR_SUCCESS 0 #define PWCHK_ERROR_MIN_LENGTH -1 #define PWCHK_ERROR_MIN_CLASSES -2 #define PWCHK_ERROR_SAME_PASSWORD -3 #define PWCHK_ERROR_USER_CHECK -4 #define PWCHK_ERROR_CASE_CHANGES_ONLY -5 #define PWCHK_ERROR_TOO_SIMILAR -6 #define PWCHK_ERROR_PALINDROME -7 #define PWCHK_DEFAULT_MIN_LENGTH 8 #define PWCHK_DEFAULT_MIN_CLASS 2 class PasswdCheckUtil : public QObject { Q_OBJECT public: explicit PasswdCheckUtil(QObject *parent = nullptr); static bool getCurrentPamState(); static int checkPassword(QString newPw, QString oldPw, QString userName); static QString checkPasswordStrerr(int nResult); }; #endif // PASSWDCHECKUTIL_H ukui-screensaver/src/widgets/fullbackgroundwidget.cpp0000775000175000017500000016141215172041106022147 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "fullbackgroundwidget.h" #include "../lock-dialog/lockdialogmodel.h" #include "utils.h" #include "lockwidget.h" #include "agreementwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include "grab-x11.h" #include #include #include #include #include "commonfunc.h" #include "screensavermode.h" #include "screensaverwidget.h" #include "displayservice.h" #include "configuration.h" #include "screen-manager.h" #include #include #include #include #include #include #ifdef WITHKYSEC #include #include #endif FullBackgroundWidget::FullBackgroundWidget(LockDialogModel *model, bool isStartup, QWidget *parent) : QWidget(parent), m_modelLockDialog(model), m_isStartupMode(isStartup), m_background(nullptr) { updateScreenRects(); initCurrentBackground(); initUI(); initConnections(); } FullBackgroundWidget::~FullBackgroundWidget() { if (m_backgrondGradationTimer) { if (m_backgrondGradationTimer->isActive()) m_backgrondGradationTimer->stop(); m_backgrondGradationTimer->disconnect(); m_backgrondGradationTimer->deleteLater(); } } void FullBackgroundWidget::initUI() { if (!m_windowHelper) { m_windowHelper = new UkuiWindowHelper(this); m_windowHelper->setWindowRole(UkuiWindowHelper::WindowRole::ScreenLock); m_windowHelper->grabKeyboard(); } if (QApplication::platformName() == "xcb") { /*捕获键盘,如果捕获失败,则可能是由于弹出菜单项已经捕获,那么模拟一次esc按键来退出菜单,如果仍捕获失败,则放弃锁屏,避免密码无法输入*/ if (establishGrab()) qDebug() << "establishGrab : true"; else { qDebug() << "establishGrab : false"; /*检测当前是否存在弹出菜单,只有存在弹出菜单时,才模拟按键esc*/ if (checkHasPopupMenu()) { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; exit(0); } qDebug() << "XTestFakeKeyEvent Esc!!"; XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Escape), True, 1); XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Escape), False, 1); XFlush(display); sleep(1); } if (!establishGrab()) { exit(1); } } } setMouseTracking(true); m_defaultGeometry = SCREEN_MANAGER->virtualDesktopGeometry(); setGeometry(SCREEN_MANAGER->virtualDesktopGeometry()); if (!m_lockWidget) { m_lockWidget = new LockWidget(m_modelLockDialog, this); connect(m_lockWidget, &LockWidget::authSucceed, this, &FullBackgroundWidget::onAuthSucceed); connect(m_lockWidget, &LockWidget::sessionToolsExit, this, &FullBackgroundWidget::onCloseScreensaver); connect(m_lockWidget, &LockWidget::showBlankScreensaver, this, &FullBackgroundWidget::onShowBlankScreensaver); connect(m_lockWidget, &LockWidget::startFcitx, this, &FullBackgroundWidget::onStartFcitx); connect(m_lockWidget, &LockWidget::moveToCurrentScreen, this, &FullBackgroundWidget::moveToCurrentScreen); connect(m_lockWidget, &LockWidget::moveToPrimaryScreen, this, &FullBackgroundWidget::moveToPrimaryScreen); connect(m_lockWidget, &LockWidget::setCurrentBackground, [&]() { setCurrentBackground("LockBackground"); }); moveToPrimaryScreen(); } if (m_modelLockDialog->getAgreementWindowShowLoginPrompt() && (IsStartupMode() || isGreeterMode())) { bool hideTitle = m_modelLockDialog->getAgreementWindowHideTitle(); QString title = m_modelLockDialog->getAgreementWindowPromptTitle(); QString text = m_modelLockDialog->getAgreementWindowText(); // 文本内容不为空 且 如果配置了标题,则标题不为空 if (!text.isEmpty() && (hideTitle || (!hideTitle && !text.isEmpty()))) { m_agreementWindow = new AgreementWindow(hideTitle, title, text, this); m_lockWidget->hide(); m_agreementWindow->setGeometry(m_lockWidget->geometry()); m_agreementWindow->show(); m_lockWidget->clearFocus(); m_agreementWindow->setFocusPolicy(Qt::StrongFocus); setFocusProxy(m_agreementWindow); m_agreementWindow->setFocus(); // 特别提示窗口抓取键盘,避免焦点在密码框上时输入事件跑到密码框,但会导致我已知晓按钮无法响应回车 m_agreementWindow->grabKeyboard(); connect(m_agreementWindow, &AgreementWindow::switchToGreeter, this, [this]() { QString strAutologinUser = (Q_EMIT m_modelLockDialog->getAutologinUserHint()); QString strAuthUser = (Q_EMIT m_modelLockDialog->pamAuthenticateUser()); bool isAuthenticated = (Q_EMIT m_modelLockDialog->pamIsAuthenticated()); qDebug() << "switchToGreeter:" << isAuthenticated << strAutologinUser << strAuthUser; if (isAuthenticated && !strAutologinUser.isEmpty() && !strAuthUser.isEmpty()) { onAuthSucceed(strAuthUser); return; } m_agreementWindow->hide(); m_agreementWindow->releaseKeyboard(); m_agreementWindow->clearFocus(); m_lockWidget->show(); m_lockWidget->setFocus(); update(); }); } } if (m_modelLockDialog->isUseWayland()) { setWindowFlags(Qt::FramelessWindowHint); } else { setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); qApp->installNativeEventFilter(this); } // 登录模式下监听屏幕插拔 if (isGreeterMode() && !m_modelLockDialog->isUseWayland()) { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; } else { XRRQueryExtension(display, &m_RREventBase, &m_RRErrorBase); XRRSelectInput(display, DefaultRootWindow(display), RRScreenChangeNotifyMask); QtConcurrent::run([=]() { RRScreenChangeEvent(true); }); } } installEventFilter(this); // 初始化队列处理定时器 m_queueProcessTimer = new QTimer(this); m_queueProcessTimer->setSingleShot(true); connect(m_queueProcessTimer, &QTimer::timeout, this, &FullBackgroundWidget::processEventQueue); } void FullBackgroundWidget::setCurrentBackground(QString cmd) { if (cmd == "LockBackground") { onCurrentUserBackground(m_currentUserName); } else if (cmd == "ToolsBackground") { QString strBackground = getDesktopBackground(); qDebug() << "picture path = " << strBackground; QSharedPointer background(new Background); background->type = BACKGROUND_IMAGE; background->image = strBackground; setBackground(background); } } QString FullBackgroundWidget::getDesktopBackground() { UserInfoPtr userInfo = m_modelLockDialog->findUserByName(m_currentUserName); QString strBackground = ""; if (userInfo) { strBackground = userInfo->backGround(); if (userInfo->backGround().isEmpty() || !QFileInfo(userInfo->backGround()).exists()) { strBackground = "/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg"; QFileInfo fileimage(strBackground); if (fileimage.isFile() && fileimage.exists()) { strBackground = "/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg"; } else { strBackground = "/usr/share/ukui/ukui-session-manager/images/background-ukui.png"; QFileInfo image(strBackground); if (image.isFile() && image.exists()) { strBackground = "/usr/share/ukui/ukui-session-manager/images/background-ukui.png"; } else { qWarning() << "Can not get the background when login the user:" << m_currentUserName; } } } } return strBackground; } bool FullBackgroundWidget::eventFilter(QObject *obj, QEvent *event) { static bool isFirstActive = true; if (QApplication::platformName() != "xcb") { if (event->type() == QEvent::MouseButtonPress) { if (screenStatus & SCREEN_SAVER && !m_isBlank) { enqueueEvent(EC_ClearScreenSaver); } } } if (event->type() == QEvent::WindowDeactivate) { QTimer::singleShot(50, this, SLOT(laterActivate())); } else if (event->type() == QEvent::WindowActivate) { if (isFirstActive) { isFirstActive = false; syslog(LOG_INFO, "[ukui-screensaver-dialog] window active event!!"); QTimer::singleShot(50, this, [&]() { if (m_lockWidget) { m_lockWidget->doDelayInit(); } sendStartedFinished(); }); } if (!m_modelLockDialog->isSessionTools()) { qDebug() << "Delay setLockState"; QTimer::singleShot(10, this, SLOT(setLockState())); } // QTimer::singleShot(200,this,SLOT(killWindow())); } return QWidget::eventFilter(obj, event); } void FullBackgroundWidget::sendStartedFinished() { // 使用QDBusInterface调用org.gnome.SessionManager的startupfinished方法 QDBusInterface sessionManager( "org.gnome.SessionManager", "/org/gnome/SessionManager", "org.gnome.SessionManager", QDBusConnection::sessionBus()); if (!sessionManager.isValid()) { qWarning() << "QDBusInterface org.gnome.SessionManager is not valid!"; return; } // 参数与gdbus命令保持一致 QDBusMessage reply = sessionManager.call( QStringLiteral("startupfinished"), QStringLiteral("ukui-screensaver-dialog"), QString("")); if (reply.type() == QDBusMessage::ErrorMessage) { qWarning() << "Failed to call startupfinished:" << reply.errorMessage(); } } void FullBackgroundWidget::initConnections() { // 监听全屏窗口变化 connect(SCREEN_MANAGER, &ScreenManager::virtualDesktopGeometryChanged, this, [=]() { qDebug() << "virtualDesktopGeometryChanged:" << SCREEN_MANAGER->virtualDesktopGeometry(); this->onDesktopResized(); }); connect(SCREEN_MANAGER, &ScreenManager::screenCountChanged, this, [=]() { qDebug() << "screenCountChanged:" << SCREEN_MANAGER->virtualDesktopGeometry(); this->onDesktopResized(); }); connect(SCREEN_MANAGER, &ScreenManager::primaryScreenChanged, this, [=]() { qDebug() << "primaryScreenChanged:" << SCREEN_MANAGER->virtualDesktopGeometry(); this->onDesktopResized(); }); connect(SCREEN_MANAGER, &ScreenManager::displayModeChanged, this, [=]() { qDebug() << "displayModeChanged:" << SCREEN_MANAGER->virtualDesktopGeometry(); this->onDesktopResized(); }); // 监听休眠、睡眠、唤醒 connect(m_modelLockDialog, SIGNAL(prepareForSleep(bool)), this, SLOT(onPrepareForSleep(bool))); connect(m_modelLockDialog, &LockDialogModel::currentUserChanged, this, &FullBackgroundWidget::onCurUserChanged); connect(m_modelLockDialog, &LockDialogModel::showLock, this, &FullBackgroundWidget::onShowLock); connect( m_modelLockDialog, &LockDialogModel::showBlankScreensaver, this, &FullBackgroundWidget::onShowBlankScreensaver); connect( m_modelLockDialog, &LockDialogModel::showLockScreensaver, this, &FullBackgroundWidget::onShowLockScreensaver); connect(m_modelLockDialog, &LockDialogModel::showScreensaver, this, &FullBackgroundWidget::onShowScreensaver); connect(m_modelLockDialog, &LockDialogModel::showSessionIdle, this, &FullBackgroundWidget::onShowSessionIdle); connect(m_modelLockDialog, &LockDialogModel::SecondRunParam, this, &FullBackgroundWidget::onSecondRunParam); connect(m_modelLockDialog, &LockDialogModel::showSessionTools, this, &FullBackgroundWidget::onShowSessionTools); connect(m_modelLockDialog, &LockDialogModel::showAppBlockWindow, this, &FullBackgroundWidget::onShowAppBlockWindow); connect( m_modelLockDialog, &LockDialogModel::showMultiUsersBlockWindow, this, &FullBackgroundWidget::onShowMultiUsersBlockWindows); connect(m_modelLockDialog, &LockDialogModel::showSwitchUserLock, this, &FullBackgroundWidget::onShowSwitchUserLock); connect(m_modelLockDialog, &LockDialogModel::showSwitchToUser, this, &FullBackgroundWidget::onShowSwitchToUser); } void FullBackgroundWidget::setLockState() { if (m_bIsLockState) return; m_bIsLockState = true; Q_EMIT m_modelLockDialog->lockStateChanged(true, false); } void FullBackgroundWidget::clearLockState() { if (!m_bIsLockState) return; m_bIsLockState = false; Q_EMIT m_modelLockDialog->clearLockState(); } void FullBackgroundWidget::onSecondRunParam(const QString &str) { qDebug() << "onSecondRunParam" << str; if (str == "SleepLock") { onShowBlankScreensaver(0, true); } else if (str == "ScreensaverLock") { onShowLockScreensaver(); } else if (str == "StartupLock") { setIsStartupMode(true); onShowLock(true); } else if (str == "CmdLock") { onShowLock(false); } else if (str.startsWith("CloseLock")) { int nDelayTime = 1000; bool needLock = false; QStringList listParam = str.split('&'); for (const QString ¶m : listParam) { if (param.startsWith("delay")) { QStringList listDelay = param.split('='); if (listDelay.size() >= 2) { nDelayTime = listDelay[1].toInt(); } } else if (param.startsWith("hasLock")) { QStringList listLock = param.split('='); if (listLock.size() >= 2) { needLock = (listLock[1].toInt() ? true : false); } } } onShowBlankScreensaver(nDelayTime, needLock); } else if (str == "SessionIdle") { onShowSessionIdle(); } else if (str == "Screensaver") { onShowScreensaver(); } else if (str == "CmdSessionTools") { onShowSessionTools(); } else if (str == "CmdSwitchUser") { onShowSwitchUserLock(); } else if (str.startsWith("CmdSwitchToUser")) { QStringList listParam = str.split('&'); if (listParam.size() > 1) { onShowSwitchToUser(listParam[1]); } } else if (str == "CmdRestartAppBlock") { onShowAppBlockWindow(0); } else if (str == "CmdPowerOffAppBlock") { onShowAppBlockWindow(1); } else if (str == "CmdSuspendAppBlock") { onShowAppBlockWindow(2); } else if (str == "CmdHibernateAppBlock") { onShowAppBlockWindow(3); } else if (str == "CmdLogoutAppBlock") { onShowAppBlockWindow(4); } else if (str == "CmdRestartAppSoftBlock") { onShowAppBlockWindow(5); } else if (str == "CmdPowerOffAppSoftBlock") { onShowAppBlockWindow(6); } else if (str == "CmdLogoutAppSoftBlock") { qDebug() << "CmdLogoutAppSoftBlock show"; onShowAppBlockWindow(7); } else if (str == "CmdRestartMulTiUserBlock") { onShowMultiUsersBlockWindows(0); } else if (str == "CmdPowerOffMulTiUserBlock") { onShowMultiUsersBlockWindows(1); } } void FullBackgroundWidget::onShowBlankScreensaver(int nDelay, bool isHasLock) { if (screenStatus & SCREEN_SAVER) { return; } qDebug() << "onShowBlankScreensaver:" << nDelay << "," << isHasLock << m_isBlank << m_modelLockDialog->isSessionTools(); bool isFromSessionTools = false; if (m_modelLockDialog->isSessionTools()) { isFromSessionTools = true; if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->exitSubWidget(true); } m_modelLockDialog->setIsSessionTools(false); setLockState(); } if (isHasLock) { screenStatus = (ScreenStatus)(screenStatus | SCREEN_SAVER | SCREEN_LOCK); } else { screenStatus = (ScreenStatus)(screenStatus | SCREEN_SAVER); } if (!m_isBlank) { for (auto screen : m_screenRects) { ScreenSaver *saver = m_modelLockDialog->getScreensaver(); saver->mode = SaverMode(SAVER_BLANK_ONLY); ScreenSaverWidget *saverWidget = new ScreenSaverWidget(saver, nullptr, this); widgetXScreensaverList.push_back(saverWidget); saverWidget->setGeometry(screen); } setCursor(Qt::BlankCursor); m_isBlank = true; if (0 == nDelay) { /*在进行压力测试时,可能会出现锁屏界面启动极慢,导致在睡眠之前调用了锁屏,但 * 锁屏没来得及绑定睡眠唤醒信号,导致唤醒后,锁屏界面没有收到信号从而一直显示黑屏的问题。 * 因此这里在进入黑色屏保时,通过后台接口,获取一次当前是否应该显示黑色屏保状态*/ if (!isFromSessionTools) { bool ret = Q_EMIT m_modelLockDialog->GetBlankState(); if (!ret) { m_isBlank = false; onClearScreensaver(); } } } else { QTimer::singleShot(nDelay, this, [=]() { if (m_isBlank) { m_isBlank = false; onClearScreensaver(); } }); } } show(); } void FullBackgroundWidget::onShowLock(bool isStartup) { Q_UNUSED(isStartup); if (m_modelLockDialog->isSessionTools()) { if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->exitSubWidget(); } m_modelLockDialog->setIsSessionTools(false); qDebug() << "onShowLock:" << isStartup; setLockState(); } else { onCurrentUserBackground("LockBackground"); moveToPrimaryScreen(); } screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); show(); syslog(LOG_INFO, "[ukui-screensaver-dialog] set current user!!"); Q_EMIT m_modelLockDialog->setCurrentUser(m_modelLockDialog->defaultUserName()); Q_EMIT m_modelLockDialog->lockStateChanged(true, false); if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->show(); } } void FullBackgroundWidget::onShowSwitchUserLock() { if (m_modelLockDialog->isSessionTools()) { if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->exitSubWidget(); } m_modelLockDialog->setIsSessionTools(false); setLockState(); } moveToPrimaryScreen(); screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); show(); Q_EMIT m_modelLockDialog->setCurrentUser(m_modelLockDialog->defaultUserName()); Q_EMIT m_modelLockDialog->lockStateChanged(true, false); if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->show(); m_lockWidget->onShowUserListWidget(true); } } void FullBackgroundWidget::onShowSwitchToUser(QString strUserName) { if (strUserName.isEmpty() || !m_modelLockDialog->findUserByName(strUserName)) { qInfo() << "Switch to user failed, name invalid or not exist:" << strUserName; return; } if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->exitSubWidget(); } if (m_modelLockDialog->isSessionTools()) { m_modelLockDialog->setIsSessionTools(false); setLockState(); } screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); Q_EMIT m_modelLockDialog->lockStateChanged(true, false); show(); QTimer::singleShot(0, this, [=]() { if (m_lockWidget) { m_lockWidget->SwitchToUser(strUserName); } }); } void FullBackgroundWidget::onShowSessionTools() { if (m_modelLockDialog->isSessionTools()) { return; } screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); m_modelLockDialog->setIsSessionTools(true); show(); Q_EMIT m_modelLockDialog->setCurrentUser(m_modelLockDialog->defaultUserName()); Q_EMIT m_modelLockDialog->lockStateChanged(true, true); if (m_modelLockDialog->isSessionTools()) { setCurrentBackground("ToolsBackground"); } if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->onShowPowerListWidget(); } } void FullBackgroundWidget::onShowAppBlockWindow(int actionType) { QStringList lockcheck; if (actionType == 0 || actionType == 1 || actionType == 4) { lockcheck = m_modelLockDialog->getShutdownLockcheck(); } else if (actionType == 2 || actionType == 3) { lockcheck = m_modelLockDialog->getSleepLockcheck(); } else if (actionType == 5 || actionType == 6 || actionType == 7) { lockcheck = m_modelLockDialog->getLogoutLockCheck(); } if (!lockcheck.isEmpty()) { show(); if (m_lockWidget && !m_lockWidget->isHidden()) screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); Q_EMIT m_modelLockDialog->setCurrentUser(m_modelLockDialog->defaultUserName()); Q_EMIT m_modelLockDialog->lockStateChanged(true, true); if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->onShowInhibitWarning(lockcheck, actionType); } if (m_modelLockDialog->isSessionTools()) { setCurrentBackground("ToolsBackground"); } } } void FullBackgroundWidget::onShowMultiUsersBlockWindows(int actionType) { screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); show(); if (m_lockWidget && !m_lockWidget->isHidden()) Q_EMIT m_modelLockDialog->setCurrentUser(m_modelLockDialog->defaultUserName()); Q_EMIT m_modelLockDialog->lockStateChanged(true, true); if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->onMulUsersLogined(actionType); } if (m_modelLockDialog->isSessionTools()) { setCurrentBackground("ToolsBackground"); } } void FullBackgroundWidget::onShowSessionIdle() { // if screensaver is never do nothing if (m_modelLockDialog->getIdleDelay() == -1) { return; } onShowScreensaver(); delayLockScreen(); Q_EMIT m_modelLockDialog->lockStateChanged(true, true); } void FullBackgroundWidget::onShowLockScreensaver() { onShowLock(false); onShowScreensaver(); } void FullBackgroundWidget::onShowScreensaver() { if (screenStatus & SCREEN_SAVER) { return; } if (m_modelLockDialog->isSessionTools()) { if (m_lockWidget && !m_lockWidget->isHidden()) { m_lockWidget->exitSubWidget(true); } m_modelLockDialog->setIsSessionTools(false); setLockState(); } screenStatus = (ScreenStatus)(screenStatus | SCREEN_SAVER); for (auto screen : m_screenRects) { ScreenSaver *saver = m_modelLockDialog->getScreensaver(); /*锁屏设置的Qt::WA_TranslucentBackground属性会导致第三方屏保变得透明,因此在使用第三方屏保时 * 取消该属性,清除屏保时再设置回来*/ if (saver->path != "/usr/lib/ukui-screensaver/ukui-screensaver-default") { // setAttribute(Qt::WA_TranslucentBackground,false); } ScreenSaverWidget *saverWidget = new ScreenSaverWidget(saver, getBackground(m_background->image, screen), this); qDebug() << " new ScreenSaverWidget"; widgetXScreensaverList.push_back(saverWidget); // 深色模式有一像素的白边,所以主屏幕向左,向右移一个像素点;这种操作后,外显上方仍旧会有一个像素的白边,暂时不对外显做偏移处理 if (screen == m_primaryScreenRect) { saverWidget->setGeometry(screen.x() - 1, screen.y() - 1, screen.width() + 1, screen.height() + 1); } else { saverWidget->setGeometry(screen); } } show(); Q_EMIT m_modelLockDialog->lockStateChanged(true, false); if (m_lockWidget) { m_lockWidget->stopAuth(); } } void FullBackgroundWidget::onClearScreensaver() { screenStatus = (ScreenStatus)(screenStatus & ~SCREEN_SAVER); bool isStartAuth = false; if (screenStatus & SCREEN_LOCK) { if (m_lockWidget) { m_lockWidget->startAuth(); isStartAuth = true; } } for (auto widget : widgetXScreensaverList) { widget->close(); // 这里使用deleteLater会导致在锁屏界面快速按esc和空格引发锁屏内存占用达到几个g的内存问题,原因未知 // widget->deleteLater(); delete widget; widget = nullptr; } widgetXScreensaverList.clear(); unsetCursor(); if (screenStatus == UNDEFINED) { onCloseScreensaver(); } else { onShowLock(false); if (m_lockWidget) { if (!isStartAuth) { m_lockWidget->startAuth(); } } } } void FullBackgroundWidget::delayLockScreen() { qDebug() << "delayLockScreen" << m_modelLockDialog->getLockTimeout() << m_modelLockDialog->getLockEnabled(); if (!m_timerLock) { m_timerLock = new QTimer(this); connect(m_timerLock, &QTimer::timeout, this, &FullBackgroundWidget::onLockScreenTimeout); } if (m_modelLockDialog->getLockTimeout() != -1 && m_modelLockDialog->getLockEnabled()) { stopDelayLockScreen(); m_timerLock->start(m_modelLockDialog->getLockTimeout() * 1000); } } void FullBackgroundWidget::stopDelayLockScreen() { if (m_timerLock && m_timerLock->isActive()) { m_timerLock->stop(); } } void FullBackgroundWidget::onLockScreenTimeout() { qDebug() << "onLockScreenTimeout:" << m_modelLockDialog->getLockEnabled(); if (m_modelLockDialog->getLockEnabled()) { screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); } m_timerLock->stop(); } void FullBackgroundWidget::onCloseScreensaver() { hide(); if (m_lockWidget) { m_lockWidget->exitSubWidget(false, false); } Q_EMIT m_modelLockDialog->lockStateChanged(false, false); stopDelayLockScreen(); screenStatus = UNDEFINED; m_isBlank = false; } void FullBackgroundWidget::onDesktopResized() { updateScreenRects(); qInfo() << "[FullBackgroundWidget] [onDesktopResized]:" << SCREEN_MANAGER->virtualDesktopGeometry(); if (SCREEN_MANAGER->virtualDesktopGeometry().width() > m_defaultGeometry.width() || SCREEN_MANAGER->virtualDesktopGeometry().height() > m_defaultGeometry.height()) { setGeometry(SCREEN_MANAGER->virtualDesktopGeometry()); } else { setGeometry(m_defaultGeometry); } if (m_lockWidget) { if (m_modelLockDialog->isSessionTools()) { moveToCurrentScreen(); } else { moveToPrimaryScreen(); } m_lockWidget->reloadRootBackground(); } repaint(); } void FullBackgroundWidget::onPrepareForSleep(bool sleep) { /// 系统休眠时,会关闭总线,导致设备不可用,发生错误 /// 在系统休眠之前停止认证,在系统唤醒后重新开始认证 qInfo() << "onPrepareForSleep begin:" << sleep; if (!isVisible()) { qInfo() << "onPrepareForSleep done:" << sleep; return; } if (sleep) { if (m_lockWidget) m_lockWidget->stopAuth(); // uninhibit(); } else { if (screenStatus & SCREEN_SAVER) { m_isBlank = false; onClearScreensaver(); } else { repaint(); if (m_lockWidget) { if (m_lockWidget->isHidden()) { m_lockWidget->show(); m_lockWidget->activateWindow(); } m_lockWidget->startAuth(); } // inhibit(); } } qInfo() << "onPrepareForSleep done:" << sleep; } void FullBackgroundWidget::mousePressEvent(QMouseEvent *e) { // close(); } QString FullBackgroundWidget::getFocusWindowName() { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return QString(""); } Window focus = 0; int rev = 0; XGetInputFocus(display, &focus, &rev); return getWindowNameFromWid(focus); } QString FullBackgroundWidget::getWindowNameFromWid(WId window) { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return QString(""); } XClassHint ch; ch.res_name = NULL; ch.res_class = NULL; XGetClassHint(display, window, &ch); QString nameStr = QString(ch.res_name); if (ch.res_name) XFree(ch.res_name); if (ch.res_class) XFree(ch.res_class); return nameStr; } int FullBackgroundWidget::RegisteSubWnd(quint64 uWndId) { if (!m_listWndIds.contains(uWndId) && m_listWndIds.size() < SUBWND_COUNT_MAX) { m_listWndIds.append(uWndId); qDebug() << "RegisterSubWnd:" << uWndId; // QTimer::singleShot(50,this,SLOT(laterActivate())); return m_listWndIds.size(); } else { return -1; } } int FullBackgroundWidget::UnRegisteSubWnd(quint64 uWndId) { if (m_listWndIds.contains(uWndId)) { m_listWndIds.removeAll(uWndId); qDebug() << "UnRegisterSubWnd:" << uWndId; // QTimer::singleShot(50,this,SLOT(laterActivate())); return m_listWndIds.size(); } else { return -1; } } QList FullBackgroundWidget::GetSubWndIds() { return m_listWndIds; } void FullBackgroundWidget::moveToCurrentScreen() { if (m_windowHelper) { qDebug() << "move to current screen"; QScreen *screen = m_windowHelper->getCurrentScreenBlocking(); if (m_lockWidget && screen) { QRect rect = screen->geometry(); qDebug() << "movecurrent" << m_lockWidget->geometry() << screen->geometry(); if (m_lockWidget->geometry() == screen->geometry()) { return; } m_lockWidget->setGeometry(screen->geometry()); } } } void FullBackgroundWidget::moveToPrimaryScreen() { if (!m_lockWidget) { return; } bool chkInOneScreen = false; for (auto screen : m_screenRects) { if (screen == m_primaryScreenRect) { qInfo() << "LockWidget:" << screen << m_lockWidget->geometry() << geometry(); if (m_agreementWindow && m_agreementWindow->isVisible()) { m_agreementWindow->setGeometry(screen); } if (m_lockWidget->geometry() != screen) { if (!m_lockWidget->isHidden()) { m_lockWidget->hide(); m_lockWidget->setGeometry(screen); m_lockWidget->show(); } else { m_lockWidget->setGeometry(screen); } } chkInOneScreen = true; break; } } if (!chkInOneScreen) { for (auto screen : m_screenRects) { /*避免切换时闪烁*/ qInfo() << "LockWidget:" << screen << m_lockWidget->geometry() << geometry(); if (m_agreementWindow && m_agreementWindow->isVisible()) { m_agreementWindow->setGeometry(screen); } if (m_lockWidget->geometry() != screen) { if (!m_lockWidget->isHidden()) { m_lockWidget->hide(); m_lockWidget->setGeometry(screen); m_lockWidget->show(); } else { m_lockWidget->setGeometry(screen); } } chkInOneScreen = true; break; } } } void FullBackgroundWidget::fakeFocusIn(WId window) { // We have keyboard grab, so this application will // get keyboard events even without having focus. // Fake FocusIn to make Qt realize it has the active // window, so that it will correctly show cursor in the dialog. Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return; } XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.display = display; ev.xfocus.type = FocusIn; ev.xfocus.window = window; ev.xfocus.mode = NotifyNormal; ev.xfocus.detail = NotifyAncestor; XSendEvent(display, window, False, NoEventMask, &ev); XFlush(display); } void FullBackgroundWidget::laterActivate() { if (QApplication::platformName() != "xcb") { activateWindow(); return; } Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return; } XSetInputFocus(display, this->winId(), RevertToParent, CurrentTime); activateWindow(); } void FullBackgroundWidget::showEvent(QShowEvent *event) { if (QApplication::platformName() == "xcb") tryGrabKeyboard(); QTimer::singleShot(100, this, [=]() { m_lockWidget->updateFont(); m_lockWidget->updateFontSize(); }); } void FullBackgroundWidget::keyReleaseEvent(QKeyEvent *e) { if (QApplication::platformName() != "xcb") { if (e->key() == Qt::Key_Escape && screenStatus == SCREEN_LOCK) { // "escape" enqueueEvent(EC_ShowScreenSaver); } else if ( screenStatus & SCREEN_SAVER && (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space)) { enqueueEvent(EC_ClearScreenSaver); } } } void FullBackgroundWidget::tryGrabKeyboard() { if (window()->windowHandle() && window()->windowHandle()->setKeyboardGrabEnabled(true)) { m_tryGrabTimes = 0; return; } m_tryGrabTimes++; if (m_tryGrabTimes == 15) { qWarning() << "setKeyboardGrabEnable failed! focus Window is" << getFocusWindowName(); m_tryGrabTimes = 0; return; } QTimer::singleShot(100, this, &FullBackgroundWidget::tryGrabKeyboard); } bool FullBackgroundWidget::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) { if (qstrcmp(eventType, "xcb_generic_event_t") != 0) { return false; } xcb_generic_event_t *event = reinterpret_cast(message); const uint8_t responseType = event->response_type & ~0x80; if (responseType == XCB_FOCUS_OUT) { // 丢焦点时,检测抢焦点的窗口是否为锁屏的子窗口,比如QMenu,QCombobox等, // 如果不是子窗口,则重新给锁屏设置一次焦点 QString focusWindow = getFocusWindowName(); if (focusWindow == "ukui-screensaver-dialog") { return false; } else { qDebug() << "focus out. focus window is" << focusWindow; } laterActivate(); qDebug() << "XCB_FOCUS_OUT !"; } else if (responseType == XCB_FOCUS_IN) { // 因为弹出菜单会释放键盘抓取,所以在锁屏窗口重新获取到焦点时,重新抓取一次键盘焦点 qDebug() << "XCB_FOCUS_IN !"; } else if (responseType == XCB_GE_GENERIC) { // 因为锁屏抓取了键盘,所以锁屏窗口不管有没有焦点,都能够输入,但仍然需要发送一个 // 虚拟的focusin事件,来激活窗口焦点,同时点击锁屏界面,也能触发锁屏键盘抓取 xcb_ge_generic_event_t *xc = reinterpret_cast(event); if (xc->event_type == XCB_BUTTON_PRESS) { if (this->windowHandle()) { bool grabed = this->windowHandle()->setKeyboardGrabEnabled(true); if (!grabed) { qDebug() << "setKeyboardGrabEnable failed! focus Window is" << getFocusWindowName(); } } onGlobalButtonPressed(QCursor::pos().x(), QCursor::pos().y()); } else if (xc->event_type == XCB_BUTTON_RELEASE) { } else if (xc->event_type == XCB_MOTION_NOTIFY) { onGlobalButtonDrag(QCursor::pos().x(), QCursor::pos().y()); } } else if (responseType == XCB_BUTTON_PRESS) { xcb_button_press_event_t *xc = reinterpret_cast(event); int x = xc->root_x; int y = xc->root_y; onGlobalButtonPressed(x, y); qDebug() << "XCB_BUTTON_PRESS:" << x << "," << y; } else if (responseType == XCB_BUTTON_RELEASE) { } else if (responseType == XCB_MOTION_NOTIFY) { xcb_motion_notify_event_t *xc = reinterpret_cast(event); int x = xc->root_x; int y = xc->root_y; onGlobalButtonDrag(x, y); qDebug() << "XCB_MOTION_NOTIFY:" << x << "," << y; } else if (responseType == XCB_KEY_PRESS) { xcb_key_press_event_t *xc = reinterpret_cast(event); // qDebug()<<"XCB_KEY_PRESS:"<detail; onGlobalKeyPress(xc->detail); } else if (responseType == XCB_KEY_RELEASE) { xcb_key_release_event_t *xc = reinterpret_cast(event); qDebug() << "XCB_KEY_RELEASE:" << xc->detail; onGlobalKeyRelease(xc->detail); } else if (responseType == m_RREventBase + RRScreenChangeNotify) { if (isGreeterMode() && !m_modelLockDialog->isUseWayland()) { RRScreenChangeEvent(false); } } return false; } void FullBackgroundWidget::onCurUserChanged(const QString &strUserName) { if (isGreeterMode() && !m_modelLockDialog->isUseWayland()) { DisplayService::instance(m_modelLockDialog)->setCurUserName(strUserName); } } void FullBackgroundWidget::onAuthSucceed(QString strUserName) { if (IsStartupMode()) { setIsStartupMode(false); } if (getenv("USER") == strUserName) { if (UniAuthService::instance()) { UniAuthService::instance()->SaveLastLoginUser(strUserName); // 三权分立不保存秒级登录用户信息 #ifdef WITHKYSEC if (kysec_is_disabled() || !kysec_get_3adm_status()) { #endif UniAuthService::instance()->SaveQuickLoginUser(strUserName); #ifdef WITHKYSEC } #endif } onCloseScreensaver(); clearLockState(); Q_EMIT m_modelLockDialog->CheckTurnOnScreen(); } else { clearLockState(); if (m_lockWidget) { m_lockWidget->drawRootBackground(); } if (m_modelLockDialog) { Q_EMIT m_modelLockDialog->startSession(); } } } void FullBackgroundWidget::onStartFcitx() { if (isGreeterMode()) { startFcitx(); qInfo() << " QT_IM_MODULE = " << getenv("QT_IM_MODULE"); } } void FullBackgroundWidget::onGlobalKeyPress(const quint8 &key) {} void FullBackgroundWidget::onGlobalKeyRelease(const quint8 &key) { // if (m_lockWidget && m_lockWidget->isVisible()) { // m_lockWidget->onGlobalkeyRelease(key); // } if (key == 9 && screenStatus == SCREEN_LOCK) { // "escape" enqueueEvent(EC_ShowScreenSaver); } else if (screenStatus & SCREEN_SAVER /* && !isBlank*/) { qDebug() << "clearclear"; enqueueEvent(EC_ClearScreenSaver); } } void FullBackgroundWidget::onGlobalButtonDrag(int xPos, int yPos) { if (screenStatus & SCREEN_SAVER && !m_isBlank) { qDebug() << "clearclear"; enqueueEvent(EC_ClearScreenSaver); } } void FullBackgroundWidget::onGlobalButtonPressed(int xPos, int yPos) { if (screenStatus & SCREEN_SAVER && !m_isBlank) { enqueueEvent(EC_ClearScreenSaver); } } ////////////////////////////////// void FullBackgroundWidget::initCurrentBackground() { m_background = QSharedPointer(new Background); UserInfoPtr userInfo = m_modelLockDialog->findUserByName(m_modelLockDialog->defaultUserName()); if (userInfo) { if (!userInfo->backGroundColor().isEmpty()) { if (!m_modelLockDialog->isSessionTools()) { QString strBackground = getDesktopBackground(); m_background->type = BACKGROUND_IMAGE; m_background->image = strBackground; } else { m_background->type = BACKGROUND_COLOR; m_background->color = userInfo->backGroundColor(); } } else { m_background->type = BACKGROUND_IMAGE; m_background->image = getUserBackgroundPath(m_modelLockDialog->defaultUserName()); if (m_background->image.isEmpty() || !QFileInfo(m_background->image).isReadable()) { m_background->image = getDefaultBackgroundPath(); } } m_currentUserName = userInfo->name(); } m_transition.started = false; m_loadingOneBackgroundFuture = QtConcurrent::run([=]() { QString strBackground = getUserBackgroundPath(m_modelLockDialog->defaultUserName()); addBackgroundData(strBackground, m_screenRects); }); connect(m_modelLockDialog, &LockDialogModel::setCurrentUser, this, &FullBackgroundWidget::onCurrentUserBackground); connect( m_modelLockDialog, &LockDialogModel::currentUserChanged, this, &FullBackgroundWidget::onUpdateUserBackground); } QString FullBackgroundWidget::getDefaultBackgroundPath() { QString default_background_path = "/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg"; QFile file(default_background_path); if (!file.exists()) { default_background_path = "/usr/share/backgrounds/1-openkylin.jpg"; QFile file(default_background_path); if (!file.exists()) { return ""; } } return default_background_path; } void FullBackgroundWidget::paintEvent(QPaintEvent *event) { // 全屏黑色打底 QPainter paintGlobal(this); QColor color = "#000000"; paintGlobal.setBrush(color); paintGlobal.drawRect(geometry()); for (auto screen : m_screenRects) { // 在每个屏幕上绘制背景 QRect rect = screen; if (m_transition.started) drawBackground(m_transition.to, m_transition.from, rect, m_transition.stage); else { drawBackground(m_background, m_background, rect); } } return QWidget::paintEvent(event); } bool FullBackgroundWidget::isOpenGradation() { bool isOpenGradation = false; // return true;//test 一直触发渐变效果 if (m_currentUserName.isEmpty() || m_oldUserName.isEmpty()) return isOpenGradation; if (m_currentUserName == m_oldUserName) return isOpenGradation; if (getUserBackgroundPath(m_oldUserName) != getUserBackgroundPath(m_currentUserName)) { isOpenGradation = true; } return isOpenGradation; } void FullBackgroundWidget::onUpdateUserBackground(const QString &strUserName) { QString strBackground = getUserBackgroundPath(strUserName); qDebug() << "UpdateUserBackground:" << strUserName << strBackground; if (m_currentUserName == strUserName && m_background->image == strBackground) return; m_oldUserName = m_currentUserName; m_currentUserName = strUserName; if (m_modelLockDialog->isSessionTools()) return; QSharedPointer background(new Background); background->type = BACKGROUND_IMAGE; background->image = strBackground; setBackground(background); } void FullBackgroundWidget::onTransition() { m_transition.stage += 0.05; //= (1 - cos(M_PI * m_transition.stage)) / 2; if (m_transition.stage >= 1.0) stopTransition(); // repaint(); update(); } void FullBackgroundWidget::startTransition(QSharedPointer &from, QSharedPointer &to) { if (!m_backgrondGradationTimer) { m_backgrondGradationTimer = new QTimer(this); connect(m_backgrondGradationTimer, &QTimer::timeout, this, &FullBackgroundWidget::onTransition); } stopTransition(); m_transition.from = from; m_transition.to = to; m_transition.stage = 0.0; m_transition.started = true; m_backgrondGradationTimer->start(10); } void FullBackgroundWidget::stopTransition() { if (m_backgrondGradationTimer && m_backgrondGradationTimer->isActive()) { m_backgrondGradationTimer->stop(); } m_transition.stage = 1.0; m_transition.started = false; } void FullBackgroundWidget::setBackground(QSharedPointer &background) { if (background) { qDebug() << "FullBackgroundWidget::setBackground" << background->image; } stopTransition(); if (background) { if (m_modelLockDialog->isSessionTools()) { m_background = background; } else { if (background->image.isEmpty() || !QFileInfo(background->image).isReadable()) { background->image = getDefaultBackgroundPath(); } } } if (m_background && background && m_background->image == background->image) { repaint(); return; } // 如果是第一次绘制背景,则不需要渐变 // 现在由于绘制模糊背景,不需要渐变了 if (!m_background.isNull()) { startTransition(m_background, background); } m_background = background; update(); } void FullBackgroundWidget::drawBackground( QSharedPointer &backgroundBack, QSharedPointer &backgroundFront, const QRect &rect, float alpha) { if (backgroundBack == nullptr) return; QPainter painter(this); switch (backgroundBack->type) { case BACKGROUND_IMAGE: { QPixmap *pixmapFront = getBackground(backgroundFront->image, rect); QPixmap *pixmapBack = getBackground(backgroundBack->image, rect); if (!pixmapFront || pixmapFront->isNull() || !pixmapBack || pixmapBack->isNull()) { painter.setOpacity(alpha); QColor cor = "#035290"; painter.setBrush(cor); painter.drawRect(rect); } else { painter.drawPixmap(rect, *pixmapFront); painter.setOpacity(alpha); painter.drawPixmap(rect, *pixmapBack); QPainterPath path; QPainter painter1(this); painter1.setOpacity(0.25); painter1.setRenderHint(QPainter::Antialiasing); // 反锯齿; painter1.setClipping(true); painter1.setPen(Qt::transparent); path.addRect(this->rect()); path.setFillRule(Qt::WindingFill); painter1.setBrush(QColor("#000000")); painter1.setPen(Qt::transparent); painter1.drawPath(path); } break; } case BACKGROUND_COLOR: { painter.setBrush(backgroundBack->color); painter.setOpacity(alpha); painter.setBrush(backgroundFront->color); painter.drawRect(rect); break; } } } void FullBackgroundWidget::onRemoveUserBackground(const QString &strUserName) { if (m_allBackgroundsMap.contains(strUserName)) m_allBackgroundsMap.remove(strUserName); } QString FullBackgroundWidget::getUserBackgroundPath(const QString &strUserName) { for (UserInfoPtr userInfo : m_modelLockDialog->usersInfo()) { if (strUserName == userInfo->name()) { if (!userInfo->greeterBackGround().isEmpty() && QFile(userInfo->greeterBackGround()).exists()) { return userInfo->greeterBackGround(); } else { return userInfo->backGround(); } } } return QString(""); } void FullBackgroundWidget::addBackgroundData(const QString &bgPath, const QList &screensRect) { if (bgPath.isEmpty()) { qDebug() << __LINE__ << __FUNCTION__ << bgPath << ":Path is Null"; return; } QFile file(bgPath); if (!file.exists()) { qDebug() << __LINE__ << __FUNCTION__ << "Add background file isn't exists"; return; } for (QRect screen : screensRect) { int width = screen.width(); int height = screen.height(); QString resolution = QString("%1x%2").arg(width).arg(height); QPair key(bgPath, resolution); if (!m_allBackgroundsDataMap.contains(key)) { syslog(LOG_DEBUG, "[ukui-screensaver-dialog] load background begin"); QPixmap *pixmap = nullptr; QCryptographicHash pixmapHash(QCryptographicHash::Md5); QFile pixmapFile(bgPath); if (pixmapFile.open(QIODevice::ReadOnly) && pixmapHash.addData(&pixmapFile)) { QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); QString bgThumbPath = QString("%1/bg_thumbnail/").arg(cachePath); QString bgThumbFileName = QString("%1-%2.jpg").arg(QString(pixmapHash.result().toHex())).arg(resolution); QString bgThumbFile = QString("%1%2").arg(bgThumbPath).arg(bgThumbFileName); if (bgThumbFileName == "27ae8eb54648e4335a8cb3fb13fe4f7c-1920x1080.jpg") { pixmap = new QPixmap(":/image/assets/defaultbackground.jpg"); } else { pixmap = new QPixmap(bgThumbFile); if (pixmap->isNull()) { delete pixmap; pixmap = nullptr; pixmap = new QPixmap(scaleBlurPixmap(width, height, bgPath)); QDir bgThumbDir(bgThumbPath); if (!bgThumbDir.exists()) { bgThumbDir.mkpath("."); } if (pixmap->save(bgThumbFile, "JPEG", 90)) { qInfo() << "save bg thumb file:" << bgThumbFile; } } } } else { pixmap = new QPixmap(scaleBlurPixmap(width, height, bgPath)); } syslog(LOG_DEBUG, "[ukui-screensaver-dialog] load background end"); m_allBackgroundsDataMap[key] = pixmap; } } } void FullBackgroundWidget::onAddUserBackground(const QString &strUserName) { QString strBackground = getUserBackgroundPath(strUserName); addBackgroundData(strBackground, m_screenRects); } void FullBackgroundWidget::onUserBackgroundChanged(const QString &strUserName) {} void FullBackgroundWidget::onCurrentUserBackground(const QString &strUserName) { QString strBackground = getUserBackgroundPath(strUserName); qDebug() << "CurrentUserBackground:" << strUserName << strBackground; if (m_currentUserName == strUserName && m_background->image == strBackground) return; m_oldUserName = m_currentUserName; m_currentUserName = strUserName; if (m_modelLockDialog->isSessionTools()) return; QSharedPointer background(new Background); background->type = BACKGROUND_IMAGE; background->image = strBackground; stopTransition(); if (background) { if (background->image.isEmpty() || !QFileInfo(background->image).isReadable()) { background->image = getDefaultBackgroundPath(); } } m_background = background; repaint(); } QPixmap *FullBackgroundWidget::getBackground(const QString &path, const QRect &rect) { if (path.isEmpty() || !QFile(path).exists()) return nullptr; QString resolution = QString("%1x%2").arg(rect.width()).arg(rect.height()); QPair key(path, resolution); if (m_allBackgroundsDataMap.isEmpty() && m_loadingOneBackgroundFuture.isRunning()) { m_loadingOneBackgroundFuture.waitForFinished(); } else { if (!m_loadingOneBackgroundFuture.isFinished() && m_loadingOneBackgroundFuture.isStarted()) { m_loadingOneBackgroundFuture.waitForFinished(); } } if (!m_allBackgroundsDataMap.contains(key)) { QPixmap *pixmap = new QPixmap(scaleBlurPixmap(width(), height(), path)); m_allBackgroundsDataMap[key] = pixmap; } return m_allBackgroundsDataMap[key]; } void FullBackgroundWidget::RRScreenChangeEvent(bool isFirst) { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return; } XRRScreenResources *screen; screen = XRRGetScreenResources(display, DefaultRootWindow(display)); XRROutputInfo *info; QList listMonitors; for (int i = 0; i < screen->noutput; i++) { info = XRRGetOutputInfo(display, screen, screen->outputs[i]); if (info->connection == RR_Connected) { listMonitors.push_back(info->name); } XRRFreeOutputInfo(info); } qDebug() << "monitors = " << listMonitors; if (isFirst && DisplayService::instance()->isSaveParamInUsed()) { m_listMonitors = listMonitors; } else { onScreensChanged(listMonitors); } XRRFreeScreenResources(screen); } void FullBackgroundWidget::onScreensChanged(QList listMonitors) { qDebug() << "newList = " << listMonitors << " listMonitors = " << m_listMonitors << DisplayService::instance()->isJJW7200(); if (DisplayService::instance()->isJJW7200() == 0 && m_listMonitors.size() == listMonitors.size()) { bool isAllFound = true; for (auto monitor : listMonitors) { if (!m_listMonitors.contains(monitor)) { isAllFound = false; break; } } if (isAllFound) { return; } } m_listMonitors = listMonitors; if (m_listMonitors.size() < 2) { // 默认设置显示最大分辨率 DisplayService::instance()->setOneDisplayMode(); } else { int mode = Configuration::instance()->getValue("display-mode").toInt(); DisplayService::instance()->switchDisplayMode((DisplayMode)mode); } // 在调用xrandr打开显示器以后,不能马上设置窗口大小,会设置不正确的 // 分辨率,延时1000ms正常。 QTimer::singleShot(1000, this, SLOT(onDesktopResized())); } void FullBackgroundWidget::setIsStartupMode(bool isStartup) { m_isStartupMode = isStartup; Q_EMIT StartupModeChanged(m_isStartupMode); } void FullBackgroundWidget::enqueueEvent(quint32 eventCode, QString param /* = ""*/) { // 将按键按下事件异步存入队列 QMutexLocker locker(&m_queueMutex); m_keyEventQueue.enqueue(EventInfo(eventCode, param, QDateTime::currentMSecsSinceEpoch())); // 启动队列处理定时器(如果还没启动) if (!m_queueProcessTimer->isActive()) { m_queueProcessTimer->start(0); } } void FullBackgroundWidget::processEventQueue() { QMutexLocker locker(&m_queueMutex); if (m_keyEventQueue.isEmpty()) { return; } // 取出队列中的第一个事件进行处理 EventInfo eventInfo = m_keyEventQueue.dequeue(); locker.unlock(); // 释放锁,避免在处理事件时阻塞队列操作 // 处理事件 switch (eventInfo.eventCode) { case EC_ShowScreenSaver: { bool canShow = true; if (m_lockWidget && !m_lockWidget->exitSubWidget()) canShow = false; if (canShow && !isGreeterMode() && m_modelLockDialog->getIdleDelay() != -1 && !m_modelLockDialog->isSessionTools()) { onShowScreensaver(); } } break; case EC_ClearScreenSaver: { onClearScreensaver(); } default: break; } // 处理完一个事件后,清除队列中的相同事件 locker.relock(); QQueue filteredQueue; while (!m_keyEventQueue.isEmpty()) { EventInfo remainingEvent = m_keyEventQueue.dequeue(); // 只保留与当前处理事件不同的事件 if (remainingEvent.eventCode != eventInfo.eventCode || remainingEvent.strParam != eventInfo.strParam) { filteredQueue.enqueue(remainingEvent); } } m_keyEventQueue = filteredQueue; // 如果队列中还有事件,继续处理 if (!m_keyEventQueue.isEmpty()) { m_queueProcessTimer->start(0); } } void FullBackgroundWidget::updateScreenRects() { m_screenRects.clear(); for (QScreen *screen : SCREEN_MANAGER->allScreens()) { if (screen) { m_screenRects.append(screen->geometry()); } } if (SCREEN_MANAGER->primaryScreen()) { m_primaryScreenRect = SCREEN_MANAGER->primaryScreen()->geometry(); } } ukui-screensaver/src/widgets/statusbutton.h0000664000175000017500000000354515172041106020164 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef STATUSBUTTON_H #define STATUSBUTTON_H #include #include #include #include #include #include enum BUTTONSTATUS { NORMAL, HOVER, SELECT, CLICKED, }; enum BUTTONTYPE { BOTBUTTON, BIOBUTTON, POWERBUTTON, NORMALBTN, PWDTIPBTN, SIDEBARBTN, }; class StatusButton : public QPushButton { Q_OBJECT public: explicit StatusButton(QWidget *parent = nullptr, int type = BOTBUTTON); /** * @brief 设置按钮当前状态 * @param selected 是否选中 * @return */ void setClickedStatus(int status); void setDrivedId(int drivedId); inline int getDrivedId() { return m_drivedId; } inline int getButtonType() { return buttonType; } protected: void paintEvent(QPaintEvent *e) override; void mouseReleaseEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); Q_SIGNALS: /** * @brief 响应点击事件 * @return */ void clicked(); void buttonClicked(int drivedId); private: int curStatus = NORMAL; int buttonType; int m_drivedId; }; #endif // STATUSBUTTON_H ukui-screensaver/src/widgets/timewidget.cpp0000664000175000017500000001043215172041106020073 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "timewidget.h" #include "../lock-dialog/lockdialogmodel.h" #include using namespace kdk; #define TIME_FONT_SIZE 36 #define TIME_DATE_SIZE 18 #define TIMEWIDGET_SIZE 1000, 100 TimeWidget::TimeWidget(LockDialogModel *model, QWidget *parent) : QWidget(parent), m_modelLockDialog(model) { this->setMinimumSize(TIMEWIDGET_SIZE); m_curFontSize = m_modelLockDialog->getCurFontSize(); m_ptToPx = m_modelLockDialog->getPtToPx(); QVBoxLayout *m_VBLayout = new QVBoxLayout(this); m_t_label = new CustomLabel(this); KDK_EXTEND_ALL_INFO_FORMAT(m_t_label, "CustomLabel", "initialLabel", "add time label of tools"); m_t_label->setAlignment(Qt::AlignCenter); m_t_label->setFontSize(TIME_FONT_SIZE); m_t_label->setStyleSheet("QLabel{color: white;}" "QToolTip{border-radius:4px; background-color:#FFFFFF; color:black;}"); m_t_label->setText(getLongFormatDate(TIME)); m_t_label->adjustSize(); m_d_label = new CustomLabel(this); KDK_EXTEND_ALL_INFO_FORMAT(m_d_label, "CustomLabel", "initialLabel", "add date label of tools"); m_d_label->setAlignment(Qt::AlignCenter); m_d_label->setStyleSheet("QLabel{color: white;}" "QToolTip{border-radius:4px; background-color:#FFFFFF; color:black;}"); m_d_label->setText(getLongFormatDate(DATE)); m_d_label->setFontSize(TIME_DATE_SIZE); m_d_label->adjustSize(); m_VBLayout->addWidget(m_t_label); m_VBLayout->addWidget(m_d_label); this->setLayout(m_VBLayout); this->adjustSize(); m_timer = new QTimer(this); connect(m_timer, SIGNAL(timeout()), this, SLOT(update_datatime())); this->timerStart(); } TimeWidget::~TimeWidget() { m_timer->deleteLater(); } void TimeWidget::updateUI() { this->setFixedSize(this->width(), this->height()); m_t_label->setFontSize((TIME_FONT_SIZE + m_curFontSize) * m_ptToPx); m_d_label->setFontSize((TIME_DATE_SIZE + m_curFontSize) * m_ptToPx); } void TimeWidget::timerStart() { m_timer->start(1000); } void TimeWidget::timerStop() { m_timer->stop(); } void TimeWidget::updateTimeFont(QString fontFamily) { if (m_t_label) { m_t_label->setFontFamily(fontFamily); } if (m_d_label) { m_d_label->setFontFamily(fontFamily); } update_datatime(); } void TimeWidget::updateTimeFontSize(double fontSize) { if (m_t_label) { m_t_label->setFontSize((TIME_FONT_SIZE + fontSize) * m_ptToPx); } if (m_d_label) { m_d_label->setFontSize((TIME_DATE_SIZE + fontSize) * m_ptToPx); } update_datatime(); } QString TimeWidget::getLongFormatDate(int type) { char *userName; QByteArray ba = m_modelLockDialog->currentUserName().toLatin1(); userName = ba.data(); kdk_logn_dateinfo *dateInfo = kdk_system_login_lock_dateinfo(userName); if (type == DATE) { QString date = dateInfo->date; QString week = dateInfo->week; if (dateInfo) { kdk_free_logn_dateinfo(dateInfo); } return date + " " + week; } else if (type == TIME) { QString time = dateInfo->time; if (dateInfo) { kdk_free_logn_dateinfo(dateInfo); } return time; } } void TimeWidget::update_datatime() { if (m_t_label) { m_t_label->setText(getLongFormatDate(TIME)); } if (m_d_label) { m_d_label->setText(getLongFormatDate(DATE)); } } void TimeWidget::showEvent(QShowEvent *event) { update_datatime(); if(m_timer && !m_timer->isActive()) { timerStart(); } } void TimeWidget::hideEvent(QHideEvent *event) { timerStop(); } ukui-screensaver/src/widgets/mylistwidget.cpp0000664000175000017500000002654215172041106020467 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "mylistwidget.h" #include #include MyListWidget::MyListWidget(QWidget *parent) : QListWidget(parent) { QLocale local; QString systemLang = local.name(); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { setLayoutDirection(Qt::RightToLeft); } else { setLayoutDirection(Qt::LeftToRight); } installEventFilter(this); setFocusPolicy(Qt::TabFocus); } void MyListWidget::setUserListWidget(bool isUserListWidget) { m_isUserListWidget = isUserListWidget; } void MyListWidget::setIsLoginOptionWidget(bool isLoginOption) { m_isLoginOption = isLoginOption; } bool MyListWidget::eventFilter(QObject *obj, QEvent *event) { if (this->flow() == Flow::TopToBottom) { /*在收到快捷键按下后,首先会收到keypress事件,随后会收到一个ShortcutOverride的快捷键响应事件, * 收到这个事件后会把listWidget的currentRow往后移一个,随后才会收到keyrelease事件。*/ if (event->type() == QEvent::KeyPress) { //先记录按键按下时的currentRow m_lastRow = this->currentRow(); } if (event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast(event); switch (keyEvent->key()) { case Qt::Key_Up: case Qt::Key_PageUp: if (this->currentRow() == 0 && m_lastRow == 0) /*收到按键释放信号时,判断当前currentRow与按键按下时记录的currentRow是否一致*/ this->setCurrentRow(this->count() - 1); break; case Qt::Key_Down: case Qt::Key_PageDown: if (this->currentRow() == this->count() - 1 && m_lastRow == this->count() - 1) this->setCurrentRow(0); break; case Qt::Key_HomePage: this->setCurrentRow(0); break; case Qt::Key_End: this->setCurrentRow(this->count() - 1); break; case Qt::Key_Enter: case Qt::Key_Return: Q_EMIT itemClicked(currentItem()); default: break; } } } else { /*在收到快捷键按下后,首先会收到keypress事件,随后会收到一个ShortcutOverride的快捷键响应事件, * 收到这个事件后会把listWidget的currentRow往后移一个,随后才会收到keyrelease事件。*/ if (event->type() == QEvent::KeyPress) { // 自定义按键选中item QKeyEvent *keyEvent = static_cast(event); if (keyEvent && (keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Right || keyEvent->key() == Qt::Key_Home || keyEvent->key() == Qt::Key_End)) { return true; } } else if (event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast(event); switch (keyEvent->key()) { case Qt::Key_Left: { if (layoutDirection() == Qt::RightToLeft) { for (int i = 0; i < count() - 1; i ++) { m_lastRow = (m_lastRow == -1) ? (count() - 1) : (m_lastRow + 1); m_lastRow = (m_lastRow > (count() - 1)) ? 0 : m_lastRow; if (!isRowHidden(m_lastRow)) { setCurrentRow(m_lastRow); break; } } } else { for (int i = 0; i < count() - 1; i ++) { m_lastRow = (m_lastRow == -1) ? (count() - 1) : (m_lastRow - 1); m_lastRow = (m_lastRow < 0) ? (count() - 1) : m_lastRow; if (!isRowHidden(m_lastRow)) { setCurrentRow(m_lastRow); break; } } } } return true; case Qt::Key_Right:{ if (layoutDirection() == Qt::RightToLeft) { for (int i = 0; i < count() - 1; i ++) { m_lastRow = (m_lastRow == -1) ? (count() - 1) : (m_lastRow - 1); m_lastRow = (m_lastRow < 0) ? (count() - 1) : m_lastRow; if (!isRowHidden(m_lastRow)) { setCurrentRow(m_lastRow); break; } } } else { for (int i = 0; i < count() - 1; i ++) { m_lastRow = (m_lastRow == -1) ? (count() - 1) : (m_lastRow + 1); m_lastRow = (m_lastRow > (count() - 1)) ? 0 : m_lastRow; if (!isRowHidden(m_lastRow)) { setCurrentRow(m_lastRow); break; } } } } return true; case Qt::Key_Home: { if (layoutDirection() == Qt::RightToLeft) { for (int i = count() - 1; i >= 0; i --) { if (!isRowHidden(i)) { setCurrentRow(i); m_lastRow = i; break; } } } else { for (int i = 0; i < count() - 1; i ++) { if (!isRowHidden(i)) { setCurrentRow(i); m_lastRow = i; break; } } } } return true; case Qt::Key_End: { if (layoutDirection() == Qt::RightToLeft) { for (int i = 0; i < count() - 1; i ++) { if (!isRowHidden(i)) { setCurrentRow(i); m_lastRow = i; break; } } } else { for (int i = count() - 1; i >= 0; i --) { if (!isRowHidden(i)) { setCurrentRow(i); m_lastRow = i; break; } } } } break; case Qt::Key_Enter: case Qt::Key_Return: Q_EMIT itemClicked(currentItem()); break; case Qt::Key_Tab: { if (m_lastRow != -1) { setCurrentRow(m_lastRow); } } break; default: break; } } } if (event->type() == QEvent::FocusOut) { //焦点丢失,记录当前焦点所在item,清空选中状态 if (!m_isUserListWidget) { for (int i = 0; i < this->count(); i++) { if (!this->isRowHidden(i)) { this->setCurrentRow(i, QItemSelectionModel::SelectionFlag::Clear); //焦点丢失时不会主动发送当前item改变的信号,需要手动发送 Q_EMIT currentItemChanged(this->currentItem(), this->item(i)); break; } } } else { this->setCurrentRow(this->currentRow(), QItemSelectionModel::SelectionFlag::Clear); //焦点丢失时不会主动发送当前item改变的信号,需要手动发送 Q_EMIT currentItemChanged(this->currentItem(), this->currentItem()); } } else if (event->type() == QEvent::MouseButtonRelease) { //鼠标点击设置选中状态 } else if (event->type() == QEvent::FocusIn) { //焦点进入设置选中状态 if (layoutDirection() == Qt::RightToLeft) { for (int i = count() - 1; i >= 0; i --) { if (!isRowHidden(i)) { setCurrentRow(i, QItemSelectionModel::SelectionFlag::Clear); break; } } } else { for (int i = 0; i <= count() - 1; i ++) { if (!isRowHidden(i)) { setCurrentRow(i, QItemSelectionModel::SelectionFlag::Clear); break; } } } m_lastRow = currentRow(); return true; } return QListWidget::eventFilter(obj, event); } void MyListWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { refreshTranslate(); } } void MyListWidget::refreshTranslate() { QLocale local; QString systemLang = local.name(); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { setLayoutDirection(Qt::RightToLeft); } else { setLayoutDirection(Qt::LeftToRight); } } void MyListWidget::setItemHidden(QListWidgetItem *item, bool hidden) { if (!item) { return; } item->setHidden(hidden); updateItemState(item, hidden); } void MyListWidget::updateItemState(QListWidgetItem *item, bool hidden) { if (hidden) { if (selectedItems().contains(item)) { item->setSelected(false); } if (currentItem() == item) { selectNextVisibleItemAfter(row(item)); } } } void MyListWidget::selectNextVisibleItemAfter(int fromRow) { for (int i = fromRow + 1; i < count(); ++i) { QListWidgetItem *item = this->item(i); if (!item->isHidden()) { setCurrentItem(item); return; } } for (int i = fromRow - 1; i >= 0; --i) { QListWidgetItem *item = this->item(i); if (!item->isHidden()) { setCurrentItem(item); return; } } setCurrentItem(nullptr); } ukui-screensaver/src/widgets/sessionlistwidget.h0000664000175000017500000000247515172041035021172 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SESSIONLISTWIDGET_H #define SESSIONLISTWIDGET_H #include #include "lock-dialog/lockdialogmodel.h" #include "mylistwidget.h" class QListWidgetItem; class SessionListWidget : public MyListWidget { Q_OBJECT public: explicit SessionListWidget(LockDialogModel *model, QWidget *parent = nullptr); void updateWidgetSize(); public Q_SLOTS: void onUpdateListInfo(); void onListItemClicked(QListWidgetItem *item); Q_SIGNALS: void sessionSelected(QString strSession); private: void initUI(); void initConnections(); private: LockDialogModel *m_modelLockDialog = nullptr; }; #endif // SESSIONLISTWIDGET_H ukui-screensaver/src/widgets/iconedit.h0000775000175000017500000000571715172041106017211 0ustar fengfeng/* iconedit.h * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifndef ICONEDIT_H #define ICONEDIT_H #include "kyiconbutton.h" #include "statusbutton.h" #include "lock-dialog/lockdialogmodel.h" #include "capslockstatehelper.h" #include #include #include #include #include #include enum EDITTYPE { NORMALPWDEDIT, //密码框 CONFIDENTIALEDIT, //密保输入框 RESETPWDEDIT, //重置密码输入框 KYLINIDEDIT, //麒麟ID输入框 }; extern float scale; class IconEdit : public QWidget { Q_OBJECT public: IconEdit(LockDialogModel *model, QWidget *parent = 0, int editType = NORMALPWDEDIT); void setIcon(const QIcon &icon, const QSize &size); void clear(); void clearText(); void setPrompt(const QString &); const QString text(); void setType(QLineEdit::EchoMode type = QLineEdit::Password); void readOnly(bool enabled); void setLocked(bool lock); void setModeBtnVisible(bool visible); void startWaiting(); void stopWaiting(); void updateUI(); void setFontSize(double fontSize); protected: void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; bool eventFilter(QObject *obj, QEvent *event); void changeEvent(QEvent *event); private: void updatePixmap(); void resetFocus(); void refreshTranslate(); void measurePlaceholderText(); void setKToolTip(const QString &text); void hideKToolTip(); Q_SIGNALS: void clicked(const QString &); void focusOut(); void clickedPassword(); void textChanged(const QString &); void mousePressed(); public Q_SLOTS: void clicked_cb(); void onCapsChanged(bool state); private: LockDialogModel *m_modelLockDialog = nullptr; QLineEdit *m_edit = nullptr; QPushButton *m_iconButton = nullptr; QLabel *m_capsIcon = nullptr; KyIconButton *m_modeButton = nullptr; QString m_iconText; //文字作为图标 QIcon m_icon; QTimer *m_timer = nullptr; QPixmap m_waitingPixmap; bool m_capsState = false; QSize m_sizeIconBtn; QString systemLang; int m_editType; bool m_isShowEditToolTip = false; QSize m_widgetSize; // current widget last size CapslockStateHelper *m_capslockStateHelper; }; #endif // ICONEDIT_H ukui-screensaver/src/widgets/powerlistwidget.cpp0000664000175000017500000003545515172041106021201 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "powerlistwidget.h" #include "commonfunc.h" #include using namespace kdk; PowerListWidget::PowerListWidget(LockDialogModel *model, QWidget *parent) : m_modelLockDialog(model), MyListWidget(parent) { // updateBtnShowFlag(); initUI(); initConnections(); } PowerListWidget::~PowerListWidget() {} bool PowerListWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyRelease) { MyListWidget *listWidget = qobject_cast(obj); if (listWidget) { if (listWidget && listWidget->hasFocus()) { // Tab键切入时,设置焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::SelectCurrent); listWidget->currentItemChanged(listWidget->currentItem(), nullptr); } else { // Tab键切出时,清空焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::Clear); } } } else if (event->type() == QEvent::MouseButtonRelease) { // 电源管理界面点击后清空选中状态 MyListWidget *listWidget = qobject_cast(obj); if (listWidget) this->setCurrentRow(0, QItemSelectionModel::SelectionFlag::Clear); } return MyListWidget::eventFilter(obj, event); } void PowerListWidget::mouseReleaseEvent(QMouseEvent *event) { QPoint mousePos = event->pos(); QList itemList = this->findChildren(); for (PowerItemWidget *item : itemList) { if (item->geometry().contains(mousePos)) { // 如果是点击的功能按钮,不发信号,防止点击锁屏按钮时不进入锁屏 return QListWidget::mouseReleaseEvent(event); } } // 只有点击空白处,才需要发送信号 qDebug() << "Mouse press on list widget"; Q_EMIT powerWidgetClicked(); return QListWidget::mouseReleaseEvent(event); } void PowerListWidget::setPowerType(int type) { m_powerType = type; qDebug() << "m_powerType = " << m_powerType; updateBtnShowFlag(); onUpdateListInfo(); updateWidgetSize(); } void PowerListWidget::initUI() { setFlow(QListWidget::LeftToRight); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setProperty("isWrapping", QVariant(true)); setResizeMode(QListView::Adjust); QString powerListStyle = "QListWidget{background:rgba(255, 255, 255, 0%); border: 0px;}" "QListWidget::item{background:rgba(255, 255, 255, 0%);}"; setStyleSheet(powerListStyle); this->verticalScrollBar()->setStyleSheet( "QScrollBar{ background: transparent; margin-top:3px;margin-bottom:3px ; }" "QScrollBar:vertical{width: 6px;background: transparent;border-radius:3px;}" "QScrollBar::handle:vertical{width: 6px; background: rgba(255,255,255, 40); border-radius:3px;}" "QScrollBar::handle:vertical:hover{width: 6px; background: rgba(255,255,255, 60); border-radius:3px;}" "QScrollBar::add-line:vertical{width:0px;height:0px}" "QScrollBar::sub-line:vertical{width:0px;height:0px}"); // powerBtnList[0].func = &PowerListWidget::setSystemSuspend1(); } void PowerListWidget::initConnections() { connect(this, &PowerListWidget::itemClicked, this, &PowerListWidget::onListItemClicked); } void PowerListWidget::updateBtnShowFlag() { m_powerBtnNum = 0; for (int i = 0; i < sizeof(powerBtnList) / sizeof(powerBtnList[0]); i++) { if (powerBtnList[i].canFuncName == QString("CanSwitchUser")) { powerBtnList[i].m_show_flag = (m_powerType == SESSION && m_modelLockDialog->normalUserCount() > 1 && m_modelLockDialog->getLockCanSwitchUser()); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } bool canDoAction = false; if (powerBtnList[i].canFuncName == QString("CanLockScreen")) { canDoAction = Q_EMIT m_modelLockDialog->getCanLockScreen(); powerBtnList[i].m_show_flag = (m_powerType == SESSION && canDoAction); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } if (powerBtnList[i].canFuncName == QString("CanLogout")) { canDoAction = Q_EMIT m_modelLockDialog->getCanLogout(); powerBtnList[i].m_show_flag = (m_powerType == SESSION && canDoAction); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } if (powerBtnList[i].canFuncName == QString("CanPowerOff")) { canDoAction = Q_EMIT m_modelLockDialog->getCanPowerOff(); powerBtnList[i].m_show_flag = canDoAction; if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } if (powerBtnList[i].canFuncName == QString("CanSuspend")) { powerBtnList[i].m_show_flag = Q_EMIT m_modelLockDialog->getCanSuspend(); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } if (powerBtnList[i].canFuncName == QString("CheckSystemUpgradeReboot")) { canDoAction = Q_EMIT m_modelLockDialog->checkSystemUpgrade(); powerBtnList[i].m_show_flag = (m_powerType == SESSION && canDoAction); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } if (powerBtnList[i].canFuncName == QString("CheckSystemUpgradeShutdown")) { canDoAction = Q_EMIT m_modelLockDialog->checkSystemUpgrade(); powerBtnList[i].m_show_flag = (m_powerType == SESSION && canDoAction); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } if (powerBtnList[i].canFuncName == QString("CanReboot")) { powerBtnList[i].m_show_flag = Q_EMIT m_modelLockDialog->getCanReboot(); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } if (powerBtnList[i].canFuncName == QString("CanHibernate")) { powerBtnList[i].m_show_flag = Q_EMIT m_modelLockDialog->getCanHibernate(); if (powerBtnList[i].m_show_flag) m_powerBtnNum += 1; } } qInfo() << "m_powerBtnNum = " << m_powerBtnNum; } void PowerListWidget::onUpdateListInfo() { this->clear(); for (int i = 0; i < sizeof(powerBtnList) / sizeof(powerBtnList[0]); i++) { // qDebug() << powerBtnList[i].m_strName << powerBtnList[i].setFuncName << powerBtnList[i].m_show_flag; if (!powerBtnList[i].m_show_flag) continue; PowerItemWidget *btnWdg = new PowerItemWidget(this); btnWdg->setObjectName(powerBtnList[i].setFuncName); KDK_EXTEND_ALL_INFO_FORMAT(btnWdg, "PowerBtnWdg", QString("%1_InitialPowerItem").arg(i), "some power button of tools"); QListWidgetItem *btnItem = new QListWidgetItem(); btnItem->setSizeHint(QSize(powerBtnList[i].m_item_width, powerBtnList[i].m_item_height)); this->insertItem(this->count(), btnItem); this->setItemWidget(btnItem, btnWdg); btnWdg->setText(powerBtnList[i].m_strName); btnWdg->setToolTip(powerBtnList[i].m_strToolTip); QString strIconTheme = ""; if (powerBtnList[i].setFuncName == "Reboot") { strIconTheme = "ukui-system-restart-symbolic"; } else if (powerBtnList[i].setFuncName == "PowerOff") { strIconTheme = "system-shutdown-symbolic"; } else if (powerBtnList[i].setFuncName == "Logout") { strIconTheme = "system-logout-symbolic"; } else if (powerBtnList[i].setFuncName == "Hibernate") { strIconTheme = "ukui-hibernate-symbolic"; } else if (powerBtnList[i].setFuncName == "Suspend") { strIconTheme = "ukui-sleep-symbolic"; } else if (powerBtnList[i].setFuncName == "SwitchUser") { strIconTheme = "ukui-stock-people-symbolic"; } else if (powerBtnList[i].setFuncName == "LockScreen") { strIconTheme = "system-lock-screen-symbolic"; }; btnWdg->setIcon(drawSymbolicColoredPixmap( QIcon::fromTheme(strIconTheme, QIcon(powerBtnList[i].m_strIcon)) .pixmap(powerBtnList[i].m_icon_width, powerBtnList[i].m_icon_width), "white")); btnWdg->setFontSize((16 + m_modelLockDialog->getCurFontSize()) * m_modelLockDialog->getPtToPx()); } } void PowerListWidget::onListItemClicked(QListWidgetItem *item) { QWidget *widget = itemWidget(item); m_modelLockDialog->updateLoggedInUsersCount(); PowerItemWidget *currentItem = qobject_cast(widget); if (currentItem) { for (int i = 0; i < sizeof(powerBtnList) / sizeof(powerBtnList[0]); i++) { if (powerBtnList[i].setFuncName == currentItem->objectName()) { if (m_powerType == SAVER && m_modelLockDialog->getLoggedInUsersCount() >= 1 && (powerBtnList[i].setFuncName == "Reboot" || powerBtnList[i].setFuncName == "PowerOff") && m_modelLockDialog->getShutdownLockcheck().isEmpty()) { Q_EMIT sureShutDown(powerBtnList[i].m_inhibitType); break; } if (powerBtnList[i].setFuncName == "LockScreen") { Q_EMIT lockScreenClicked(); } else if (powerBtnList[i].setFuncName == "Suspend" || powerBtnList[i].setFuncName == "Hibernate") { Q_EMIT suspendClicked(powerBtnList[i].setFuncName); } else if (powerBtnList[i].setFuncName == "SwitchUser") { Q_EMIT switchuserClicked(); } else { Q_EMIT doPowerAction(powerBtnList[i].setFuncName); } break; } } } } void PowerListWidget::updateWidgetSize() { QScreen *screen = QApplication::primaryScreen(); if (!screen) { return; } QRect rect = screen->geometry(); if (BTN_ITEM_SIZE_WIDTH * m_powerBtnNum < rect.width()) { if (BTN_ITEM_SIZE_HEIGHT < this->maxHeight()) { setFixedSize(QSize((BTN_ITEM_SIZE_WIDTH + 3) * m_powerBtnNum, BTN_ITEM_SIZE_HEIGHT)); m_scrollIsShow = false; setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { setFixedSize(QSize((BTN_ITEM_SIZE_WIDTH + 3) * m_powerBtnNum, this->maxHeight())); m_scrollIsShow = true; setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } else { // 宽度超过屏幕宽度,需要换行显示,取item宽度的倍数设为总宽度,防止显示不居中 if (rect.width() > 0) { int pHeight = BTN_ITEM_SIZE_HEIGHT * (BTN_ITEM_SIZE_WIDTH * m_powerBtnNum / rect.width() + 1); if (pHeight < this->maxHeight()) { setFixedSize(QSize( rect.width() / BTN_ITEM_SIZE_WIDTH * (BTN_ITEM_SIZE_WIDTH + 3), BTN_ITEM_SIZE_HEIGHT * (BTN_ITEM_SIZE_WIDTH * m_powerBtnNum / rect.width() + 1))); m_scrollIsShow = false; setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else if ( (rect.width() / BTN_ITEM_SIZE_WIDTH * BTN_ITEM_SIZE_WIDTH + 10) > rect.width()) { // 需要显示滚动条 setFixedSize( QSize((rect.width() / BTN_ITEM_SIZE_WIDTH - 1) * BTN_ITEM_SIZE_WIDTH + 10, this->maxHeight())); m_scrollIsShow = true; setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } else { setFixedSize(QSize(rect.width() / BTN_ITEM_SIZE_WIDTH * BTN_ITEM_SIZE_WIDTH + 10, this->maxHeight())); m_scrollIsShow = true; setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } } } void PowerListWidget::setSystemPowerStatus(QString statusName) { qWarning() << __LINE__ << __FUNCTION__ << statusName; } void PowerListWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { refreshTranslate(); } return QListWidget::changeEvent(event); } void PowerListWidget::refreshTranslate() { for (int i = 0; i < sizeof(powerBtnList) / sizeof(powerBtnList[0]); i++) { if (powerBtnList[i].setFuncName == "Hibernate") { powerBtnList[i].m_strName = tr("Hibernate"); powerBtnList[i].m_strToolTip = tr("Turn off your computer, but the app stays open, When the computer is " "turned on, it can be restored to the state you left"); } else if (powerBtnList[i].setFuncName == "Suspend") { powerBtnList[i].m_strName = tr("Suspend"); powerBtnList[i].m_strToolTip = tr("The computer stays on, but consumes less power, The app stays open and " "can quickly wake up and revert to where you left off"); } else if (powerBtnList[i].setFuncName == "Reboot") { powerBtnList[i].m_strName = tr("Restart"); powerBtnList[i].m_strToolTip = tr("Close all apps, and then restart your computer"); } else if (powerBtnList[i].setFuncName == "PowerOff") { powerBtnList[i].m_strName = tr("Shut Down"); powerBtnList[i].m_strToolTip = tr("Close all apps, and then shut down your computer"); } else if (powerBtnList[i].setFuncName == "Logout") { powerBtnList[i].m_strName = tr("Log Out"); powerBtnList[i].m_strToolTip = tr("The current user logs out of the system, terminates the session, and returns to the login page"); } else if (powerBtnList[i].setFuncName == "SwitchUser") { powerBtnList[i].m_strName = tr("SwitchUser"); } else if (powerBtnList[i].setFuncName == "LockScreen") { powerBtnList[i].m_strName = tr("LockScreen"); } else if (powerBtnList[i].setFuncName == "UpgradeThenRestart") { powerBtnList[i].m_strName = tr("UpgradeThenRestart"); } else if (powerBtnList[i].setFuncName == "UpgradeThenShutdown") { powerBtnList[i].m_strName = tr("UpgradeThenShutdown"); } } onUpdateListInfo(); } ukui-screensaver/src/widgets/iconedit.cpp0000775000175000017500000003550015172041106017535 0ustar fengfeng/* iconedit.cpp * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #include "iconedit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "commonfunc.h" #include "utils.h" IconEdit::IconEdit(LockDialogModel *model, QWidget *parent, int editType) : QWidget(parent), m_modelLockDialog(model) { this->setFixedSize(240, 40); m_editType = editType; m_edit = new QLineEdit(this); m_edit->setProperty("needTranslucent", false); if (m_modelLockDialog->isUseWayland()) { m_capslockStateHelper = CapslockStateHelper::instance(this); connect(m_capslockStateHelper, &CapslockStateHelper::capslockChanged, this, [=](bool state) { qDebug() << " CapsLock changed !!! " << " state = " << state; m_capsState = state; onCapsChanged(m_capsState); }); } m_capsIcon = new QLabel(this); m_capsIcon->setPixmap(QIcon::fromTheme("ukui-capslock-symbolic").pixmap(QSize(16, 16))); m_capsIcon->setVisible(m_capsState); m_modeButton = new KyIconButton(EDITBUTTON, this); m_modeButton->setIcon(QIcon::fromTheme("ukui-eye-display-symbolic")); m_modeButton->setFlat(true); m_modeButton->setFixedSize(24, 14); m_modeButton->setIconSize(QSize(16, 16)); m_modeButton->setFocusPolicy(Qt::NoFocus); m_modeButton->installEventFilter(this); connect(m_modeButton, &QPushButton::clicked, this, [=]() { if (m_edit->echoMode() == QLineEdit::Password) { setType(QLineEdit::Normal); } else { setType(QLineEdit::Password); } }); m_iconButton = new QPushButton(this); m_iconButton->setFixedSize(24, 24); m_iconButton->setProperty("isRoundButton", true); m_iconButton->setProperty("isImportant", true); QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(8, 1, 8, 1); layout->setSpacing(2); layout->addStretch(); layout->addWidget(m_capsIcon); layout->addWidget(m_modeButton); layout->addWidget(m_iconButton); m_edit->setAttribute(Qt::WA_InputMethodEnabled, false); // 禁用输入法 QRegularExpression inputRegExp("^[A-Za-z0-9`~!@#$%^&*()_-+=<>,.\\/?:;\"'|\{} ]*$"); QRegularExpressionValidator *inputLimits = new QRegularExpressionValidator(inputRegExp, this); if (m_editType == CONFIDENTIALEDIT) { m_iconButton->hide(); m_modeButton->hide(); setType(QLineEdit::Normal); m_edit->setAttribute(Qt::WA_InputMethodEnabled, true); } else if (m_editType == RESETPWDEDIT) { m_iconButton->hide(); setType(QLineEdit::Password); m_edit->setAttribute(Qt::WA_InputMethodEnabled, false); // 禁用输入法 //m_edit->setValidator(inputLimits); } else if (m_editType == KYLINIDEDIT) { m_iconButton->hide(); m_modeButton->hide(); setType(QLineEdit::Normal); m_edit->setAttribute(Qt::WA_InputMethodEnabled, false); } else { connect(m_edit, &QLineEdit::returnPressed, this, &IconEdit::clicked_cb); m_edit->setAttribute(Qt::WA_InputMethodEnabled, false); // 禁用输入法 //m_edit->setValidator(inputLimits); } connect(m_iconButton, &QPushButton::clicked, this, &IconEdit::clicked_cb); connect(m_edit, &QLineEdit::textChanged, this, [=](const QString &text) { Q_EMIT textChanged(text); }); // connect(m_edit, &QLineEdit::textChanged, this, [=](QString text){ // if (!text.isEmpty()) { // setLayoutDirection(Qt::LeftToRight); // } else if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { // setLayoutDirection(Qt::RightToLeft); // } // }); m_edit->setContextMenuPolicy(Qt::NoContextMenu); // 禁用右键菜单 m_edit->installEventFilter(this); m_iconButton->setFocusPolicy(Qt::TabFocus); m_iconButton->installEventFilter(this); m_iconButton->setToolTip(tr("OK")); this->setFocusProxy(m_edit); QLocale local; systemLang = local.name(); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { setLayoutDirection(Qt::RightToLeft); } else { setLayoutDirection(Qt::LeftToRight); } } void IconEdit::resizeEvent(QResizeEvent *event) { if (m_widgetSize == event->size()) { return; } m_widgetSize = event->size(); // 设置输入框中文件输入区,不让输入的文字在被隐藏在按钮下 int marginsWidth = 0; if (m_editType == CONFIDENTIALEDIT || m_editType == KYLINIDEDIT) { marginsWidth = 0; } else if (m_editType == RESETPWDEDIT) { marginsWidth = m_modeButton->width(); } else { marginsWidth = m_iconButton->width() + m_modeButton->width(); } if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { m_edit->setAlignment(Qt::AlignRight); m_edit->setTextMargins(m_capsState ? marginsWidth + m_capsIcon->width() + 10 : marginsWidth + 6, 1, 1, 1); } else { m_edit->setAlignment(Qt::AlignLeft); m_edit->setTextMargins(1, 1, m_capsState ? marginsWidth + m_capsIcon->width() + 10 : marginsWidth + 6, 1); } m_edit->setFixedSize(size()); } bool IconEdit::eventFilter(QObject *obj, QEvent *event) { if (obj == m_edit) { if (event->type() == 6) { // 禁止复制粘贴功能。 QKeyEvent *keyEvent = static_cast(event); if (keyEvent->matches(QKeySequence::Copy) || keyEvent->matches(QKeySequence::Cut) || keyEvent->matches(QKeySequence::Paste)) { event->ignore(); return true; } else if ( (keyEvent->modifiers() & Qt::MetaModifier) || (keyEvent->modifiers() & Qt::AltModifier)) { // 当meta或者alt键被按下时,忽略按键事件 event->ignore(); return true; } } if (event->type() == QEvent::MouseButtonPress) { Q_EMIT clickedPassword(); } else if (event->type() == QEvent::ToolTip) { m_isShowEditToolTip = true; measurePlaceholderText(); } else if (event->type() == QEvent::Leave) { hideKToolTip(); m_isShowEditToolTip = false; } if (event->type() == 23) { update(); } else if (event->type() == QEvent::MouseButtonPress) { update(); } else if (event->type() == QEvent::MouseButtonRelease) { // 禁用鼠标中键 QMouseEvent *mouseevent = static_cast(event); if (mouseevent->button() == Qt::MiddleButton) { event->ignore(); return true; } } } if (obj == m_modeButton) { if (event->type() == 2) { } } if (event->type() == QEvent::MouseButtonPress) { Q_EMIT mousePressed(); } // 导致出现按回车发送多次密码问题,因此注释 /* if (obj == m_iconButton && event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { clicked_cb(); } } */ return false; } void IconEdit::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { refreshTranslate(); } } void IconEdit::refreshTranslate() { QLocale local; if (systemLang == local.name()) { return; } systemLang = local.name(); int w = m_iconButton->width() + m_modeButton->width(); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { setLayoutDirection(Qt::RightToLeft); m_edit->setAlignment(Qt::AlignRight); m_edit->setTextMargins(m_capsState ? w + m_capsIcon->width() + 10 : w + 6, 1, 1, 1); } else { setLayoutDirection(Qt::LeftToRight); m_edit->setAlignment(Qt::AlignLeft); m_edit->setTextMargins(1, 1, m_capsState ? w + m_capsIcon->width() + 10 : w + 6, 1); } if (m_iconButton) { m_iconButton->setToolTip(tr("OK")); } } void IconEdit::setIcon(const QIcon &icon, const QSize &size) { QPixmap pixmap = icon.pixmap(size.width(), size.height()); m_iconButton->setIcon(pixmap); m_iconButton->setIconSize(QSize(size.width(), size.height())); m_iconButton->setText(""); m_sizeIconBtn = size; m_icon = icon; m_iconText = ""; } void IconEdit::clear() { m_edit->setText(""); setPrompt(""); } void IconEdit::clearText() { m_edit->setText(""); } void IconEdit::setPrompt(const QString &prompt) { m_edit->setPlaceholderText(prompt); } const QString IconEdit::text() { return m_edit->text(); } void IconEdit::setType(QLineEdit::EchoMode type) { m_edit->setEchoMode(type); if (type == 0) { m_modeButton->setIcon(QIcon::fromTheme("ukui-eye-display-symbolic")); } else { m_modeButton->setIcon(QIcon::fromTheme("ukui-eye-hidden-symbolic")); } } void IconEdit::readOnly(bool enabled) { m_edit->setEnabled(!enabled); m_edit->setReadOnly(enabled); } void IconEdit::setLocked(bool lock) { m_iconButton->blockSignals(lock); } void IconEdit::setModeBtnVisible(bool visible) { m_modeButton->setVisible(visible); } void IconEdit::startWaiting() { m_edit->setReadOnly(true); // m_iconButton->setEnabled(false); if (!m_timer) { m_timer = new QTimer(this); m_timer->setInterval(150); connect(m_timer, &QTimer::timeout, this, &IconEdit::updatePixmap); } // 这里将QIcon转QPixmap传入(24,24)时,实际得到的pixmap大小为(24,24)乘上缩放比例,导致计算pixmap位置错误,因此需要重新设置一次大小 // 更好的解决方案应该是重写IconButton的setIcon函数?直接处理图片大小与icon大小一致? // QPixmap icon = QIcon::fromTheme("ukui-loading-0-symbolic").pixmap(16,16).scaled(16,16, Qt::IgnoreAspectRatio, // Qt::SmoothTransformation); m_waitingPixmap = getLoadingIcon(16); m_iconButton->setIcon(m_waitingPixmap); m_iconButton->setIconSize(QSize(16, 16)); m_timer->start(); } void IconEdit::stopWaiting() { if (m_timer && m_timer->isActive()) { m_timer->stop(); } m_iconButton->setAttribute(Qt::WA_TransparentForMouseEvents, false); m_edit->setReadOnly(false); if (!m_icon.isNull()) { QPixmap pixmap = m_icon.pixmap(m_sizeIconBtn.width(), m_sizeIconBtn.height()); m_iconButton->setIcon(pixmap); m_iconButton->setIconSize(QSize(m_sizeIconBtn.width(), m_sizeIconBtn.height())); } else { m_iconButton->setText(m_iconText); } } void IconEdit::updateUI() { m_iconButton->setFixedSize(QSize(24, 24)); if (!m_icon.isNull()) { QPixmap pixmap = m_icon.pixmap(m_sizeIconBtn.width(), m_sizeIconBtn.height()); m_iconButton->setIcon(pixmap); m_iconButton->setIconSize(QSize(m_sizeIconBtn.width(), m_sizeIconBtn.height())); } else { m_iconButton->setText(m_iconText); } m_modeButton->setFixedSize(QSize(24, 24)); m_modeButton->setIconSize(QSize(16, 16)); m_capsIcon->setPixmap(QIcon::fromTheme("ukui-capslock-symbolic").pixmap(QSize(16, 16))); } void IconEdit::setFontSize(double fontSize) { QFont font = qApp->font(); font.setPointSize(fontSize); m_edit->setFont(font); } void IconEdit::updatePixmap() { m_iconButton->setEnabled(true); m_iconButton->setAttribute(Qt::WA_TransparentForMouseEvents, true); QTransform transform; transform.rotate(90.0); m_waitingPixmap = m_waitingPixmap.transformed(transform, Qt::FastTransformation); m_iconButton->setIcon(QIcon(m_waitingPixmap)); m_iconButton->setIconSize(QSize(16, 16)); } void IconEdit::clicked_cb() { startWaiting(); emit clicked(m_edit->text()); } void IconEdit::onCapsChanged(bool state) { if (QString(qgetenv("XDG_SESSION_TYPE")) == "wayland") { m_capsState = checkCapsLockState(); } else { m_capsState = state; } m_capsIcon->setVisible(m_capsState); int marginsWidth = 0; if (m_editType == CONFIDENTIALEDIT || m_editType == KYLINIDEDIT) { marginsWidth = 0; } else if (m_editType == RESETPWDEDIT) { marginsWidth = m_modeButton->width(); } else { marginsWidth = m_iconButton->width() + m_modeButton->width(); } if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { m_edit->setTextMargins(m_capsState ? marginsWidth + m_capsIcon->width() + 10 : marginsWidth + 6, 1, 1, 1); } else { m_edit->setTextMargins(1, 1, m_capsState ? marginsWidth + m_capsIcon->width() + 10 : marginsWidth + 6, 1); } } void IconEdit::measurePlaceholderText() { if (m_edit && m_isShowEditToolTip) { QString strPlaceholderText = m_edit->placeholderText(); if (!strPlaceholderText.isEmpty() && m_edit->text().isEmpty()) { int nAlignWidth = 16; if (m_editType == NORMALPWDEDIT) { // qss影响 nAlignWidth = 15; } int nTextWidth = m_edit->width() - m_edit->textMargins().left() - m_edit->textMargins().right() - nAlignWidth; QFontMetrics fontMetrics(m_edit->font()); // 如果当前字体下,字符串长度大于指定宽度 int nActualWidth = fontMetrics.horizontalAdvance(strPlaceholderText); // qDebug() << "edit text width:" << m_edit->width() << nTextWidth << nActualWidth << strPlaceholderText; if (nActualWidth > nTextWidth) { setKToolTip(strPlaceholderText); } else { hideKToolTip(); } } else { hideKToolTip(); } } } void IconEdit::setKToolTip(const QString &text) { if (!text.isEmpty()) { QPoint point = QCursor::pos(); QToolTip::showText(point, text); } } void IconEdit::hideKToolTip() { if (QToolTip::isVisible()) { QToolTip::hideText(); } } ukui-screensaver/src/widgets/agreementwindow.h0000664000175000017500000000266415172041035020606 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef AGREEMENTWINDOW_H #define AGREEMENTWINDOW_H #include #include #include #include #include class AgreementWindow : public QWidget { Q_OBJECT public: explicit AgreementWindow(bool hideTitle, QString title, QString text, QWidget *parent = nullptr); bool getShowLoginPrompt(); void initUI(); Q_SIGNALS: void switchToGreeter(); protected: void resizeEvent(QResizeEvent *event); protected slots: void switchPage(); private: void init(); QTextBrowser *browser = nullptr; QLabel *titleLbl = nullptr; QPushButton *ensureBtn = nullptr; bool m_hideTitle; QString m_title; QString m_text; QWidget *centerWidget = nullptr; }; #endif // AGREEMENTWINDOW_H ukui-screensaver/src/widgets/binddialog.h0000664000175000017500000000426515172041106017501 0ustar fengfeng/* * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BINDDIALOG_H #define BINDDIALOG_H #include #include #include #include enum DIALOGTYPE{ ENSUREDIALOG, BINDOTPDIALOG }; class BindDialog : public QWidget { Q_OBJECT public: explicit BindDialog(int mode,QWidget *parent = 0); ~BindDialog(); void setOtpPromptText(QString text); void setOtpQrcodeText(QString text); void setOtpPrpmptPromptText(QString text); void setOtpMessageText(QString text); void setOtpEmergMsg(QString text); protected: void keyPressEvent(QKeyEvent *); void resizeEvent(QResizeEvent *event) override; void paintEvent(QPaintEvent *event) override; public Q_SLOTS: void on_btnClose_clicked(); void on_btnOk_clicked(); void on_btnCancel_clicked(); void on_otpPromptEdit_textChanged(QString text); //void on_lineEdit_returnPressed(); Q_SIGNALS: void accepted(QString text); void rejected(); void finished(); private: void updateSize(); void initUI(); void showFinishedPrompt(QString text = ""); QLabel *otpPromptLabel = nullptr; QLabel *otpQrcodeLabel = nullptr; QLineEdit *otpPromptEdit = nullptr; QLabel *otpMessageLabel = nullptr; QPushButton *btnOk = nullptr; QPushButton *btnCancel = nullptr; QPushButton *btnClose = nullptr; QWidget *finishWidget = nullptr; QLabel *finishLabel = nullptr; QLabel *finishMsg = nullptr; QLabel *finishTip = nullptr; int mode = -1; }; #endif // BINDDIALOG_H ukui-screensaver/src/widgets/blockwidget.h0000664000175000017500000000431715172041106017701 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BLOCKWIDGET_H #define BLOCKWIDGET_H #include #include #include #include "customlabel.h" class QPushButton; class QListView; class LockDialogModel; class QTimer; class BlockWidget : public QWidget { Q_OBJECT public: explicit BlockWidget(LockDialogModel *model, QWidget *parent = nullptr); ~BlockWidget(); void setTips(const QString tips); void setWarning(QStringList list, int type); void updateFontFamily(QString fontFamily); void setConfirmButton(bool isHidden); /** * @brief 设置关机倒计时 * @param type 0关机,1重启 * @return */ void setMsgTips(int type); private: void initUi(); QString getElidedText(QFont font, int width, QString strInfo); void adjustButtonText(); private Q_SLOTS: void onFontSizeChanged(double fontSize); void hideEvent(QHideEvent *event); void keyPressEvent(QKeyEvent *); Q_SIGNALS: void cancelButtonclicked(); void confirmButtonclicked(); private: CustomLabel *m_tipLabel = nullptr; CustomLabel *m_msgTipLabel = nullptr; QListView *m_listView = nullptr; QPushButton *m_cancelButton = nullptr; QPushButton *m_confirmButton = nullptr; LockDialogModel *m_modelLockDialog = nullptr; int m_blockType = 0; bool m_multiUsers = false; double m_curFontSize; double m_ptToPx = 1.0; QFont sysFont; QTimer *shutdownTimer = nullptr; QDateTime lastDateTime; int msg_type; QString getHibited_tr_lowcase(int type); }; #endif // BLOCKWIDGET_H ukui-screensaver/src/widgets/poweritemwidget.cpp0000664000175000017500000000454515172041106021160 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "poweritemwidget.h" #include "statusbutton.h" #include using namespace kdk; PowerItemWidget::PowerItemWidget(QWidget *parent) : QFrame(parent) { itemFace = new StatusButton(this, POWERBUTTON); KDK_EXTEND_ALL_INFO_FORMAT(itemFace, "StatusButton", "initialButton", "add button of tools"); itemFace->setFixedSize(130, 130); textLabel = new CustomLabel(this); KDK_EXTEND_ALL_INFO_FORMAT(textLabel, "CustomLabel", "initialLabel", "add textLabel of tools"); textLabel->setAlignment(Qt::AlignCenter); textLabel->setStyleSheet("QLabel {color: white}; QToolTip{color:black;}"); textLabel->setMaximumWidth(130); QVBoxLayout *itemlayout = new QVBoxLayout(this); itemlayout->addWidget(itemFace); itemlayout->addWidget(textLabel); itemlayout->setAlignment(Qt::AlignHCenter); } PowerItemWidget::~PowerItemWidget() {} bool PowerItemWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::ToolTip) { } else if (event->type() == QEvent::Leave) { } return QWidget::eventFilter(obj, event); } void PowerItemWidget::setText(const QString text) { textLabel->setText(text); } void PowerItemWidget::setToolTip(const QString tip) { itemFace->setToolTip(tip); } void PowerItemWidget::setIcon(const QIcon &icon) { itemFace->setIcon(icon); } void PowerItemWidget::setSelected(int status) { itemFace->setClickedStatus(status); } void PowerItemWidget::setFontFamily(QString fontFamily) { textLabel->setFontFamily(fontFamily); } void PowerItemWidget::setFontSize(double fontSize) { textLabel->setFontSize(fontSize); } ukui-screensaver/src/widgets/sessionitemwidget.cpp0000664000175000017500000000562015172041106021502 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "sessionitemwidget.h" #include #include #include #include #include #include #include #include #include "commonfunc.h" #define PREFIX QString(RESOURCE_PATH) #define IMAGE_DIR PREFIX + "/images/" SessionItemWidget::SessionItemWidget(QWidget *parent) : QWidget(parent) { setAttribute(Qt::WA_DeleteOnClose); init(); initIconLabel(); } SessionItemWidget::~SessionItemWidget() {} bool SessionItemWidget::eventFilter(QObject *obj, QEvent *event) { return QWidget::eventFilter(obj, event); } void SessionItemWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QStyleOption opt; opt.initFrom(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void SessionItemWidget::init() { QHBoxLayout *layout = new QHBoxLayout(); layout->setAlignment(Qt::AlignCenter); layout->setContentsMargins(8, 4, 8, 4); layout->setSpacing(4); m_labelImg = new QLabel(this); m_labelName = new QLabel(this); m_labelName->setStyleSheet("color: white"); m_labelName->setFixedSize(364, 32); layout->addWidget(m_labelImg); layout->addWidget(m_labelName); setLayout(layout); } void SessionItemWidget::initIconLabel() { m_labelImg->setFixedSize(32, 32); } void SessionItemWidget::setSessionName(QString name) { QString sessionPrefix = name.left(name.indexOf('-')); if (name == "ukui-wayland") sessionPrefix = "ukui_wayland"; if (name == "kylin-wlcom") sessionPrefix = "kylin_wlcom"; m_strImgPath = IMAGE_DIR + QString("badges/%1_badge.svg").arg(sessionPrefix.toLower()); QFile iconFile(m_strImgPath); if (!iconFile.exists()) { m_strImgPath = IMAGE_DIR + QString("badges/unknown_badge.svg"); } qDebug() << "setSessionName icon:" << m_strImgPath; QPixmap ssIcon(m_strImgPath); ssIcon = scaledPixmap(ssIcon); ssIcon = ssIcon.scaled(32, 32, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); ssIcon = PixmapToRound(ssIcon, 16); m_labelImg->setPixmap(ssIcon); m_strSessionName = name; m_labelName->setText(m_strSessionName); } void SessionItemWidget::setSessionType(int nType) { m_nSessionType = nType; } ukui-screensaver/src/widgets/securityquestionanswer.cpp0000664000175000017500000001721415172041106022615 0ustar fengfeng/* * Copyright (C) 2022 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "securityquestionanswer.h" #include #include #define KPRBACKEND_DBUS_SERVICE "org.kylin.KprBackend" #define KPRBACKEND_DBUS_PATH "/SecurityQuestionAnswer" #define KPRBACKEND_DBUS_INTERFACE "org.kylin.KprBackend" QDBusArgument &operator<<(QDBusArgument &argument, const QuestionInfo &questionInfo) { argument.beginStructure(); argument << questionInfo.nQuestionId << questionInfo.strQuestionText; argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, QuestionInfo &questionInfo) { argument.beginStructure(); argument >> questionInfo.nQuestionId >> questionInfo.strQuestionText; argument.endStructure(); return argument; } QDBusArgument &operator<<(QDBusArgument &argument, const AnswerInfo &answerInfo) { argument.beginStructure(); argument << answerInfo.nQuestionId << answerInfo.strAnswerText; argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, AnswerInfo &answerInfo) { argument.beginStructure(); argument >> answerInfo.nQuestionId >> answerInfo.strAnswerText; argument.endStructure(); return argument; } SecurityQuestionAnswer::SecurityQuestionAnswer(QObject *parent) : QDBusAbstractInterface( KPRBACKEND_DBUS_SERVICE, KPRBACKEND_DBUS_PATH, KPRBACKEND_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) { setTimeout(2147483647); qDBusRegisterMetaType(); qDBusRegisterMetaType>(); qDBusRegisterMetaType(); qDBusRegisterMetaType>(); } bool SecurityQuestionAnswer::isActivatable() { } QList SecurityQuestionAnswer::GetPresetSecurityQuestions(QString language) { QList listQuestionInfo; QDBusReply> reply = call(QStringLiteral("GetPresetSecurityQuestions"), language); if (!reply.isValid()) { qWarning() << "GetPresetSecurityQuestions error: " << reply.error().message(); } else { listQuestionInfo = reply.value(); } return listQuestionInfo; } int SecurityQuestionAnswer::BindUserSecurityAnswers(int nUId, QList listAnswerInfo) { QDBusMessage result = call(QStringLiteral("BindUserSecurityAnswers"), nUId, QVariant::fromValue(listAnswerInfo)); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "BindUserSecurityAnswers error:" << result.errorMessage(); return -1; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toInt(); } else { return -1; } } bool SecurityQuestionAnswer::IsUserHasSecurityAnswers(int nUId) { QDBusMessage result = call(QStringLiteral("IsUserHasSecurityAnswers"), nUId); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "IsUserHasSecurityAnswers error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } QList SecurityQuestionAnswer::GetUserSecurityQuestions(int nUId, QString language) { QList listQuestionInfo; QDBusReply> reply = call(QStringLiteral("GetUserSecurityQuestions"), nUId, language); if (!reply.isValid()) { qWarning() << "GetUserSecurityQuestions error: " << reply.error().message(); } else { listQuestionInfo = reply.value(); } return listQuestionInfo; } QString SecurityQuestionAnswer::VerifyUserSecurityAnswers(int nUId, QList listAnswerInfo) { QDBusMessage result = call(QStringLiteral("VerifyUserSecurityAnswers"), nUId, QVariant::fromValue(listAnswerInfo)); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "VerifyUserSecurityAnswers error:" << result.errorMessage(); return ""; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toString(); } else { return ""; } } QString SecurityQuestionAnswer::EncodeSecurityInfo(const QString &strSource) { QDBusMessage result = call(QStringLiteral("EncodeSecurityInfo"), strSource); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "EncodeSecurityInfo error:" << result.errorMessage(); return QString(""); } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toString(); } else { return QString(""); } } int SecurityQuestionAnswer::ChangePassword(int nUId, const QString &strPasswd, QString strToken) { QDBusMessage result = call(QStringLiteral("ChangePassword"), nUId, strPasswd, strToken); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "ChangePassword error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); qDebug() << " result = " << varResult ; if (varResult.size() > 0) { return varResult.takeFirst().toInt(); } else { return -1; } } int SecurityQuestionAnswer::SetUserPasswdHint(int nUId, QString strPasswdHint) { QDBusMessage result = call(QStringLiteral("SetUserPasswdHint"), nUId, strPasswdHint); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "SetUserPasswdHint error:" << result.errorMessage(); return -1; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toInt(); } else { return -1; } } QString SecurityQuestionAnswer::GetUserPasswdHint(int nUId) { QString userPasswdHint; QDBusReply reply = call(QStringLiteral("GetUserPasswdHint"), nUId); if (!reply.isValid()) { qWarning() << "GetUserPasswdHint error: " << reply.error().message(); } else { userPasswdHint = reply.value(); } return userPasswdHint; } QStringList SecurityQuestionAnswer::VerifyAccountsInfo(int nUId, QString kyidAccounts, QString kyidpasswd, QString code) { QStringList resultList; QDBusReply reply = call(QStringLiteral("VerifyAccountsInfo"), nUId, kyidAccounts, kyidpasswd, code); if (!reply.isValid()) { qWarning() << "VerifyAccountsInfo error: " << reply.error().message(); } else { resultList = reply.value(); } return resultList; } int SecurityQuestionAnswer::ChangePassword2(int nUId, const QString &strPasswd, QString strToken) { QDBusMessage result = call(QStringLiteral("ChangePassword2"), nUId, strPasswd, strToken); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "ChangePassword2 error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toInt(); } else { return -1; } } ukui-screensaver/src/widgets/verifykyidwidget.cpp0000664000175000017500000001232715172041106021327 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "verifykyidwidget.h" #include #include #include "klabel.h" #include "kyiconbutton.h" #include "iconedit.h" VerifyKyidWidget::VerifyKyidWidget(LockDialogModel *model, QWidget *parent) : QWidget(parent), m_modelLockDialog(model) { initUi(); } void VerifyKyidWidget::initUi() { QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setAlignment(Qt::AlignCenter); mainLayout->setContentsMargins(0, 40, 0, 0); mainLayout->setSpacing(16); QHBoxLayout *kylinIdLayout = new QHBoxLayout(this); kylinIdLayout->setAlignment(Qt::AlignCenter); kylinIdLayout->setContentsMargins(0, 0, 0, 0); m_pKylinIdEdit = new IconEdit(m_modelLockDialog, this, KYLINIDEDIT); m_pKylinIdEdit->setFixedSize(240, 40); QPalette palett(palette()); palett.setColor(QPalette::Window, Qt::white); m_pKylinIdEdit->setPalette(palett); m_pKylinIdEdit->setPrompt(tr("Enter your Kylin ID account")); kylinIdLayout->addWidget(m_pKylinIdEdit); connect(m_pKylinIdEdit, &IconEdit::textChanged, this, [=](const QString &text) { Q_EMIT textChanged(text); if (text.isEmpty() || (m_pCountdown && m_pCountdown->isActive())) { m_pGetCaptchaBtn->setEnabled(false); } else { m_pGetCaptchaBtn->setEnabled(true); } }); setFocusProxy(m_pKylinIdEdit); QHBoxLayout *passwordLayout = new QHBoxLayout(this); passwordLayout->setAlignment(Qt::AlignCenter); passwordLayout->setContentsMargins(0, 0, 0, 0); m_pPasswordEdit = new IconEdit(m_modelLockDialog, this, RESETPWDEDIT); m_pPasswordEdit->setFixedSize(240, 40); m_pPasswordEdit->setAttribute(Qt::WA_InputMethodEnabled, false); m_pPasswordEdit->setPalette(palett); m_pPasswordEdit->setPrompt(tr("Input password")); passwordLayout->addWidget(m_pPasswordEdit); connect(m_pPasswordEdit, &IconEdit::textChanged, this, [=](const QString &text) { Q_EMIT textChanged(text); }); QHBoxLayout *captchaLayout = new QHBoxLayout(this); captchaLayout->setAlignment(Qt::AlignCenter); captchaLayout->setContentsMargins(0, 0, 0, 0); captchaLayout->setSpacing(16); captchaLayout->addStretch(); m_pCaptchaEdit = new IconEdit(m_modelLockDialog, this, KYLINIDEDIT); m_pCaptchaEdit->setFixedSize(112, 40); m_pCaptchaEdit->setAttribute(Qt::WA_InputMethodEnabled, true); m_pCaptchaEdit->setPalette(palett); m_pCaptchaEdit->setPrompt(tr("Enter the verification code")); captchaLayout->addWidget(m_pCaptchaEdit); connect(m_pCaptchaEdit, &IconEdit::textChanged, this, [=](const QString &text) { Q_EMIT textChanged(text); }); m_pGetCaptchaBtn = new KyIconButton(CAPCHABUTTON, this); m_pGetCaptchaBtn->setFocusPolicy(Qt::ClickFocus); m_pGetCaptchaBtn->installEventFilter(this); m_pGetCaptchaBtn->setText(tr("Get verification code")); m_pGetCaptchaBtn->setFixedSize(112, 40); m_pGetCaptchaBtn->setEnabled(false); connect(m_pGetCaptchaBtn, &QPushButton::clicked, this, &VerifyKyidWidget::captchaBtnclicked); captchaLayout->addWidget(m_pGetCaptchaBtn); captchaLayout->addStretch(); mainLayout->addLayout(kylinIdLayout); mainLayout->addLayout(passwordLayout); mainLayout->addLayout(captchaLayout); mainLayout->addSpacing(2); } void VerifyKyidWidget::onCaptchaBtnclicked(bool success) { if (success) { m_lastDateTime = QDateTime::currentDateTime(); if (!m_pCountdown) { m_pCountdown = new QTimer(this); m_pCountdown->setInterval(500); connect(m_pCountdown, &QTimer::timeout, this, [&] { int interval = (60 - m_lastDateTime.secsTo(QDateTime::currentDateTime())); if (interval <= 0) { m_pCountdown->stop(); m_pGetCaptchaBtn->setEnabled(true); m_pGetCaptchaBtn->setText(tr("Get verification code")); } else { m_pGetCaptchaBtn->setEnabled(false); m_pGetCaptchaBtn->setText(tr("Reacquire(%1)").arg(interval)); } }); } m_pCaptchaEdit->setFocus(); m_pCountdown->start(500); } else { m_pKylinIdEdit->setFocus(); } } QString VerifyKyidWidget::getKylinIdEditText() { return m_pKylinIdEdit->text(); } QString VerifyKyidWidget::getPwdEditText() { return m_pPasswordEdit->text(); } QString VerifyKyidWidget::getCaptchaEditText() { return m_pCaptchaEdit->text(); } void VerifyKyidWidget::clearText() { m_pKylinIdEdit->clearText(); m_pPasswordEdit->clearText(); m_pCaptchaEdit->clearText(); } ukui-screensaver/src/widgets/powerlistwidget.h0000664000175000017500000002167415172041106020644 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef POWERLISTWIDGET_H #define POWERLISTWIDGET_H #include #include #include #include #include #include #include #include "mylistwidget.h" #include "poweritemwidget.h" #include "lock-dialog/lockdialogmodel.h" class MyListWidget; class PowerItemWidget; class QListWidgetItem; class LockDialogModel; #define LISTSZIE(x) (sizeof(x) / sizeof(x[0])) //电源管理界面 #define BTN_ITEM_SIZE_WIDTH 204 #define BTN_ITEM_SIZE_HEIGHT 200 #define BTN_ICON_SIZE_WIDTH 48 #define BTN_ICON_SIZE_HEIGHT 48 enum powerType { SAVER, SESSION }; enum stateType { REBOOT, SHUTDOWN, SLEEP, HIBERNATE, LOGOUT, SWITCHUSER, LOCKSCREEN, UPGRADETHENREBOOT, UPGRADETHENSHUTDOWN, NOTHING }; class PowerListWidget : public MyListWidget { Q_OBJECT public: explicit PowerListWidget(LockDialogModel *model, QWidget *parent = nullptr); ~PowerListWidget(); LockDialogModel *m_modelLockDialog; void updateWidgetSize(); void setSystemSuspend1(); void setPowerType(int type); inline bool getScrollShowStatus() { return m_scrollIsShow; } struct Btn_Data_Struct { QString m_strName; QString m_strToolTip; QString m_strIcon; bool m_show_flag; int m_item_width; int m_item_height; int m_icon_width; int m_icon_height; QString canFuncName; QString setFuncName; int m_inhibitType; // void (*func)(); } powerBtnList[9] = { // 1.switchuser { .m_strName = tr("SwitchUser"), .m_strToolTip = tr(""), .m_strIcon = ":/image/assets/switchuser.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CanSwitchUser", .setFuncName = "SwitchUser", .m_inhibitType = SWITCHUSER }, // 2.Hibernate { .m_strName = tr("Hibernate"), .m_strToolTip = tr("Turn off your computer, but the app stays open, When the computer is turned on, it " "can be restored to the state you left"), .m_strIcon = ":/image/assets/hibernate.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CanHibernate", .setFuncName = "Hibernate", .m_inhibitType = HIBERNATE }, // 3.Suspend { .m_strName = tr("Suspend"), .m_strToolTip = tr("The computer stays on, but consumes less power, The app stays open and can quickly " "wake up and revert to where you left off"), .m_strIcon = ":/image/assets/suspend.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CanSuspend", .setFuncName = "Suspend", .m_inhibitType = SLEEP }, // 4.LockScreen { .m_strName = tr("LockScreen"), .m_strToolTip = tr(""), .m_strIcon = ":/image/assets/lockscreen.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CanLockScreen", .setFuncName = "LockScreen", .m_inhibitType = LOCKSCREEN }, // 5.Logout { .m_strName = tr("Log Out"), .m_strToolTip = tr("The current user logs out of the system, terminates the session, and returns to the login page"), .m_strIcon = ":/image/assets/logout.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CanLogout", .setFuncName = "Logout", .m_inhibitType = LOGOUT }, // 6.upgradethenreboot { .m_strName = tr("UpgradeThenRestart"), .m_strToolTip = tr(""), .m_strIcon = ":/image/assets/reboot.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CheckSystemUpgradeReboot", .setFuncName = "UpgradeThenRestart", .m_inhibitType = UPGRADETHENREBOOT }, // 7.reboot { .m_strName = tr("Restart"), .m_strToolTip = tr("Close all apps, turn off your computer, and then turn your computer back on"), .m_strIcon = ":/image/assets/reboot.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CanReboot", .setFuncName = "Reboot", .m_inhibitType = REBOOT }, // 8.upgradethenshutdown { .m_strName = tr("UpgradeThenShutdown"), .m_strToolTip = tr(""), .m_strIcon = ":/image/assets/shutdown.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CheckSystemUpgradeShutdown", .setFuncName = "UpgradeThenShutdown", .m_inhibitType = UPGRADETHENREBOOT }, // 9.Shut Down { .m_strName = tr("Shut Down"), .m_strToolTip = tr("Close all apps, and then shut down your computer"), .m_strIcon = ":/image/assets/shutdown.svg", .m_show_flag = true, .m_item_width = BTN_ITEM_SIZE_WIDTH, .m_item_height = BTN_ITEM_SIZE_HEIGHT, .m_icon_width = BTN_ICON_SIZE_WIDTH, .m_icon_height = BTN_ICON_SIZE_HEIGHT, .canFuncName = "CanPowerOff", .setFuncName = "PowerOff", .m_inhibitType = SHUTDOWN } }; void setSystemPowerStatus(QString); protected: bool eventFilter(QObject *obj, QEvent *event); void changeEvent(QEvent *event); void mouseReleaseEvent(QMouseEvent *event) override; Q_SIGNALS: void mulUsersLogined(int inhibitType); void showInhibitWarning(QStringList list, int type); void lockScreenClicked(); void suspendClicked(const QString &strType); void switchuserClicked(); void sureShutDown(int inhibitType); void powerWidgetClicked(); /*发送电源管理鼠标点击信号,在eventfilter中点击listwidget的空白处, 收不到鼠标点击事件,只有在mouseReleaseEvent中能过滤到*/ void doPowerAction(const QString &strType); public Q_SLOTS: void onUpdateListInfo(); void onListItemClicked(QListWidgetItem *item); private: // QDBusInterface *sessionInterface; // QDBusInterface *loginInterface; bool m_canSuspend; bool m_canHibernate; bool m_canReboot; bool m_canShutDown; bool m_canLockScreen = true; bool m_canSwitchUser = true; bool m_canLogout = true; int m_powerType = SAVER; int m_powerBtnNum = 0; bool m_scrollIsShow = false; void initUI(); void updateBtnShowFlag(); void initConnections(); void refreshTranslate(); // void initDBusInterface(); }; #endif // POWERLISTWIDGET_H ukui-screensaver/src/widgets/kyiconbutton.cpp0000664000175000017500000001142215172041106020461 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "kyiconbutton.h" #include #include #include #include #include #include #include static inline qreal mixQreal(qreal a, qreal b, qreal bias) { return a + (b - a) * bias; } KyIconButton::KyIconButton(int buttonType, QWidget *parent) : QPushButton(parent), m_buttonType(buttonType) {} KyIconButton::~KyIconButton() {} QString KyIconButton::getElidedText(QFont font, int width, QString strInfo) { QFontMetrics fontMetrics(font); // 如果当前字体下,字符串长度大于指定宽度 if (fontMetrics.horizontalAdvance(strInfo) > width) { strInfo = QFontMetrics(font).elidedText(strInfo, Qt::ElideRight, width); } return strInfo; } QColor KyIconButton::mixColor(const QColor &c1, const QColor &c2, qreal bias) { if (bias <= 0.0) { return c1; } if (bias >= 1.0) { return c2; } if (qIsNaN(bias)) { return c1; } qreal r = mixQreal(c1.redF(), c2.redF(), bias); qreal g = mixQreal(c1.greenF(), c2.greenF(), bias); qreal b = mixQreal(c1.blueF(), c2.blueF(), bias); qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias); return QColor::fromRgbF(r, g, b, a); } void KyIconButton::paintEvent(QPaintEvent *event) { Q_UNUSED(event) if (m_buttonType == EDITBUTTON) { QStylePainter p(this); QStyleOptionButton option; initStyleOption(&option); option.state = QStyle::State_Enabled; p.drawControl(QStyle::CE_PushButton, option); } else if (m_buttonType == NORMALBUTTON || m_buttonType == CAPCHABUTTON) { QStyleOptionButton option; initStyleOption(&option); QPainter p(this); p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::TextAntialiasing); p.setRenderHint(QPainter::SmoothPixmapTransform); QColor backgroundColor; QColor mix = option.palette.brightText().color(); QColor highlight = option.palette.highlight().color(); if (option.state.testFlag(QStyle::State_MouseOver)) /*鼠标在按钮上(hover状态)*/ { if (option.state.testFlag(QStyle::State_Sunken)) { /*按钮被选中(clicked)*/ backgroundColor = mixColor(highlight, mix, 0.2); } else { backgroundColor = mixColor(highlight, mix, 0.1); } } else { backgroundColor = mixColor(highlight, mix, 0.05); } /*绘制背景色和rect*/ p.save(); p.setBrush(backgroundColor); p.setPen(Qt::NoPen); QPoint point; QRect ir = option.rect; uint tf = Qt::AlignVCenter; if (m_buttonType == EDITBUTTON) { p.drawRoundedRect(option.rect.adjusted(0, 0, -1, -1), 12, 12); } else if (m_buttonType == NORMALBUTTON) { p.drawRoundedRect(option.rect.adjusted(0, 0, -1, -1), 6, 6); } else if (m_buttonType == CAPCHABUTTON) { if (!this->isEnabled()) { p.setOpacity(0.3); } p.drawRoundedRect(option.rect.adjusted(0, 0, -1, -1), 6, 6); } QPixmap pixmap = option.icon.pixmap(option.iconSize, QIcon::Active, QIcon::On); point = QPoint( ir.x() + ir.width() / 2 - (pixmap.width() + 1) / 2, ir.y() + ir.height() / 2 - (pixmap.height() + 2) / 2); p.drawPixmap(this->style()->visualPos(option.direction, option.rect, point), pixmap); p.restore(); if (!this->text().isEmpty() || this->text() != "") { // 绘制文字 QString strEText = getElidedText(font(), width(), this->text()); if (strEText != this->text()) { setToolTip(this->text()); setText(strEText); } // 绘制文字 if (this->isEnabled()) p.setPen(QColor(255, 255, 255, 255)); else p.setPen(this->palette().color(QPalette::Disabled, QPalette::ButtonText)); p.setFont(this->font()); p.drawText(this->rect(), Qt::AlignCenter, this->text()); } } } ukui-screensaver/src/widgets/biobuttonlistwidget.h0000664000175000017500000000300115172041035021476 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BIOBUTTONLISTWIDGET_H #define BIOBUTTONLISTWIDGET_H #include #include "mylistwidget.h" class QListWidgetItem; extern float scale; class BioButtonListWidget : public MyListWidget { Q_OBJECT public: explicit BioButtonListWidget(QWidget *parent = nullptr); void updateWidgetSize(); void addOptionButton(unsigned uLoginOptType, int nDrvId, QString strDrvName); void onOptionSelected(int nIndex); void updateUI(); public Q_SLOTS: void onListItemClicked(QListWidgetItem *item); void onListItemChanged(QListWidgetItem *current, QListWidgetItem *previous); Q_SIGNALS: void userSelected(QString strUserName); private: void initUI(); void initConnections(); protected: void resizeEvent(QResizeEvent *event); private: QList m_listDriveId; }; #endif // BIOBUTTONLISTWIDGET_H ukui-screensaver/src/widgets/blockwidget.cpp0000664000175000017500000003342515172041106020236 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "blockwidget.h" #include "customlabel.h" #include "statusbutton.h" #include "../lock-dialog/lockdialogmodel.h" #include "powerlistwidget.h" #include #include #include #include #include #include #include #include #include #define SHUTDOWN_MSG_TIME 60 BlockWidget::BlockWidget(LockDialogModel *model, QWidget *parent) : QWidget(parent), m_modelLockDialog(model) { initUi(); setFocusPolicy(Qt::NoFocus); } BlockWidget::~BlockWidget() {} void BlockWidget::initUi() { QVBoxLayout *mainLayout = new QVBoxLayout(this); this->setLayout(mainLayout); m_tipLabel = new CustomLabel(this); m_tipLabel->setAlignment(Qt::AlignCenter); m_tipLabel->setWordWrap(true); m_msgTipLabel = new CustomLabel(this); m_msgTipLabel->setAlignment(Qt::AlignCenter); QHBoxLayout *listLayout = new QHBoxLayout(); listLayout->setAlignment(Qt::AlignCenter); m_listView = new QListView(this); m_listView->setObjectName(QString::fromUtf8("applist")); listLayout->addWidget(m_listView); QHBoxLayout *buttonLayout = new QHBoxLayout(); m_cancelButton = new QPushButton(this); m_cancelButton->setText(tr("Cancel")); m_cancelButton->setFocusPolicy(Qt::TabFocus); buttonLayout->addWidget(m_cancelButton); m_confirmButton = new QPushButton(this); m_confirmButton->setText(tr("Confirm")); m_confirmButton->setFocusPolicy(Qt::TabFocus); buttonLayout->addWidget(m_confirmButton); buttonLayout->setAlignment(Qt::AlignCenter); mainLayout->setSpacing(46); mainLayout->addStretch(); mainLayout->addWidget(m_tipLabel); mainLayout->addWidget(m_msgTipLabel); mainLayout->addLayout(listLayout); mainLayout->addLayout(buttonLayout); mainLayout->addStretch(); m_curFontSize = m_modelLockDialog->getCurFontSize(); m_ptToPx = m_modelLockDialog->getPtToPx(); connect(m_modelLockDialog, &LockDialogModel::fontSizeChanged, this, &BlockWidget::onFontSizeChanged); m_tipLabel->setFontSize((18 + m_curFontSize) * m_ptToPx); connect(m_cancelButton, &QPushButton::clicked, this, [&]() { if (shutdownTimer) { shutdownTimer->stop(); } emit cancelButtonclicked(); }); connect(m_confirmButton, &QPushButton::clicked, this, [&]() { emit confirmButtonclicked(); }); } void BlockWidget::hideEvent(QHideEvent *event) { if (m_msgTipLabel) { m_msgTipLabel->hide(); } if (shutdownTimer) { shutdownTimer->stop(); } } void BlockWidget::onFontSizeChanged(double fontSize) { m_curFontSize = fontSize; m_tipLabel->setFontSize((18 + m_curFontSize) * m_ptToPx); sysFont.setPointSize((16 + m_curFontSize) * m_ptToPx); m_confirmButton->setFont(sysFont); m_cancelButton->setFont(sysFont); adjustButtonText(); } void BlockWidget::updateFontFamily(QString fontFamily) { if (m_tipLabel) { m_tipLabel->setFontFamily(fontFamily); } sysFont.setFamily(fontFamily); if (m_cancelButton) { m_cancelButton->setFont(sysFont); } if (m_confirmButton) { m_confirmButton->setFont(sysFont); } adjustButtonText(); } void BlockWidget::setConfirmButton(bool isHidden) { m_confirmButton->setVisible(!isHidden); } void BlockWidget::adjustButtonText() { if (m_cancelButton) { QString cancelStrEText = getElidedText( m_cancelButton->font(), m_cancelButton->width() - m_cancelButton->contentsMargins().left() - m_cancelButton->contentsMargins().right(), tr("Cancel")); if (cancelStrEText == tr("Cancel")) { m_cancelButton->setText(tr("Cancel")); } else if (cancelStrEText != tr("Cancel")) { m_cancelButton->setText(cancelStrEText); m_cancelButton->setToolTip(tr("Cancel")); } } if (m_confirmButton) { QString strEText = getElidedText( m_confirmButton->font(), m_confirmButton->width() - m_confirmButton->contentsMargins().left() - m_confirmButton->contentsMargins().right(), tr("Confirm")); if (strEText == tr("Confirm")) { m_confirmButton->setText(tr("Confirm")); } else if (strEText != tr("Confirm")) { m_confirmButton->setText(strEText); m_confirmButton->setToolTip(tr("Confirm")); } } } void BlockWidget::setTips(const QString tips) { if (m_msgTipLabel) { m_msgTipLabel->hide(); } m_cancelButton->setFixedSize(120, 48); m_confirmButton->setFixedSize(120, 48); m_tipLabel->setFixedWidth(this->width()); m_cancelButton->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0.2);border-radius: 8px;color: white;}" "QPushButton:hover{background: rgba(255, 255, 255, 0.4);border-radius: 8px;}" "QPushButton:pressed {background: rgba(255, 255, 255, 0.3);border-radius: 8px;}" "QPushButton:focus{background: rgba(255, 255, 255, 0.4);border-radius:8px;outline: none;}"); m_confirmButton->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0.2);border-radius: 8px;color: white;}" "QPushButton:hover{background: rgba(255, 255, 255, 0.4);border-radius: 8px;}" "QPushButton:pressed {background: rgba(255, 255, 255, 0.3);border-radius: 8px;}" "QPushButton:focus{background: rgba(255, 255, 255, 0.4);border-radius:8px;outline: none;}"); sysFont.setPointSize((20 + m_curFontSize) * m_ptToPx); m_confirmButton->setFont(sysFont); m_cancelButton->setFont(sysFont); m_confirmButton->show(); m_tipLabel->show(); m_listView->hide(); m_tipLabel->setText(tips); adjustButtonText(); } void BlockWidget::setMsgTips(int type) { msg_type = type; QString tips = tr("If you do not perform any operation, the system will automatically %1 after %2 seconds.") .arg(getHibited_tr_lowcase(msg_type)) .arg(SHUTDOWN_MSG_TIME); m_msgTipLabel->setText(tips); m_msgTipLabel->show(); lastDateTime = QDateTime::currentDateTime(); if (!shutdownTimer) { shutdownTimer = new QTimer(this); shutdownTimer->setInterval(500); connect(shutdownTimer, &QTimer::timeout, this, [&] { int interval = (60 - lastDateTime.secsTo(QDateTime::currentDateTime())); if (interval <= 0) { shutdownTimer->stop(); m_confirmButton->clicked(); m_msgTipLabel->setText(""); } else { m_msgTipLabel->setText( tr("If you do not perform any operation, the system will automatically %1 after %2 seconds.") .arg(getHibited_tr_lowcase(msg_type)) .arg(interval)); } }); } shutdownTimer->start(500); } void BlockWidget::setWarning(QStringList list, int type) { if (m_msgTipLabel) { m_msgTipLabel->hide(); } m_tipLabel->setFixedWidth(this->width()); switch (type) { case 0: m_tipLabel->setText(tr( "The following programs prevent restarting, you can click \"Cancel\" and then close these programs.")); break; case 1: m_tipLabel->setText(tr("The following programs prevent the shutdown, you can click \"Cancel\" and then " "close these programs.")); break; case 2: m_tipLabel->setText( tr("The following programs prevent suspend, you can click \"Cancel\" and then close these programs.")); break; case 3: m_tipLabel->setText(tr( "The following programs prevent hibernation, you can click \"Cancel\" and then close these programs.")); break; case 4: m_tipLabel->setText(tr( "The following programs prevent you from logging out, you can click \"Cancel\" and then close them.")); break; case 5: m_tipLabel->setText( tr("The following programs prevent you restarting, you can click \"Cancel\" and then close them, \ or click 'OK' to force close and proceed.")); break; case 6: m_tipLabel->setText( tr("The following programs prevent you shutdown, you can click \"Cancel\" and then close them, \ or click 'OK' to force close and proceed.")); break; case 7: m_tipLabel->setText( tr("The following programs prevent you from logging out, you can click \"Cancel\" and then close them, \ or click 'OK' to force close and proceed.")); break; default: break; } m_listView->show(); QStandardItemModel *model = new QStandardItemModel(this); QIcon icon; QString iconName = list.at(0); QString appName = list.at(1); if (!iconName.isEmpty() && QIcon::hasThemeIcon(iconName)) { icon = QIcon::fromTheme(iconName); } else if (QIcon::hasThemeIcon("application-x-desktop")) { icon = QIcon::fromTheme("application-x-desktop"); } model->appendRow(new QStandardItem(icon, appName)); m_listView->verticalScrollMode(); m_listView->setStyleSheet( "QListView#applist{font:10pt;color:white;background-color: rgba(255,255,255,80);border-style: outset;border-width: 0px;border-radius: 6px;}\ QListView#applist::item{height:48px;margin-top:2px;border-radius: 6px;}\ QListView#applist::item::selected {background-color: rgba(255,255,255,80);border: 1px solid #296CD9;\ height:48px;margin-top:2px;border-radius: 6px;}\ QListView#applist::item::hover {background-color: rgba(255,255,255,80);height:48px;margin-top:2px;border-radius: 6px;}"); sysFont.setPointSize((10 + m_curFontSize) * m_ptToPx); m_listView->setFont(sysFont); m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_listView->setIconSize(QSize(32, 32)); m_listView->setModel(model); m_listView->setFixedSize(520, 320); m_cancelButton->setFixedSize(120, 48); m_confirmButton->setFixedSize(120, 48); m_confirmButton->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0.2);border-radius: 24px;color: white;}" "QPushButton:hover{background: rgba(255, 255, 255, 0.4);border-radius:24px;}" "QPushButton:pressed {background: rgba(255, 255, 255, 0.3);border-radius: 24px;}" "QPushButton:focus{background: rgba(255, 255, 255, 0.4);border-radius:24px;outline: none;}"); m_cancelButton->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0.2);border-radius: 24px;color: white;}" "QPushButton:hover{background: rgba(255, 255, 255, 0.4);border-radius:24px;}" "QPushButton:pressed {background: rgba(255, 255, 255, 0.3);border-radius: 24px;}" "QPushButton:focus{background: rgba(255, 255, 255, 0.4);border-radius:24px;outline: none;}"); sysFont.setPointSize((16 + m_curFontSize) * m_ptToPx); m_confirmButton->setFont(sysFont); m_cancelButton->setFont(sysFont); adjustButtonText(); } QString BlockWidget::getHibited_tr_lowcase(int type) { switch (type) { case SHUTDOWN: return tr("shut down"); break; case REBOOT: return tr("restart"); break; default: break; } return ""; } QString BlockWidget::getElidedText(QFont font, int width, QString strInfo) { QFontMetrics fontMetrics(font); // 如果当前字体下,字符串长度大于指定宽度 if (fontMetrics.horizontalAdvance(strInfo) > width) { strInfo = QFontMetrics(font).elidedText(strInfo, Qt::ElideRight, width); } return strInfo; } void BlockWidget::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Escape: break; case Qt::Key_Enter: if (m_confirmButton && m_confirmButton->isEnabled() && !m_confirmButton->isHidden() && m_confirmButton->hasFocus()) { m_confirmButton->clicked(); m_confirmButton->setFocus(); } else if (m_cancelButton && m_cancelButton->isEnabled() && !m_cancelButton->isHidden() && m_cancelButton->hasFocus()) { m_cancelButton->clicked(); } break; case Qt::Key_Return: if (m_confirmButton && m_confirmButton->isEnabled() && !m_confirmButton->isHidden() && m_confirmButton->hasFocus()) { m_confirmButton->clicked(); m_confirmButton->setFocus(); } else if (m_cancelButton && m_cancelButton->isEnabled() && !m_cancelButton->isHidden() && m_cancelButton->hasFocus()) { m_cancelButton->clicked(); } break; default: QWidget::keyPressEvent(event); } } ukui-screensaver/src/widgets/fullbackgroundwidget.h0000664000175000017500000001511215172041106021604 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef FULLBACKGROUNDWIDGET_H #define FULLBACKGROUNDWIDGET_H #include #include #include #include #include #include #include #include enum ScreenStatus { UNDEFINED = 0x00, SCREEN_SAVER = 0x01, SCREEN_LOCK = 0x02, SCREEN_LOCK_AND_SAVER = 0x03, SCREEN_BLACK = 0x04, }; enum BackgroundType { BACKGROUND_IMAGE, BACKGROUND_COLOR }; struct Background { BackgroundType type; QColor color; QString image; }; struct Transition { QSharedPointer from; QSharedPointer to; float stage; bool started; }; enum EventCode { EC_ShowScreenSaver, EC_ClearScreenSaver }; // 事件信息结构体 struct EventInfo { quint32 eventCode; QString strParam; // 事件参数 qint64 timeStamp; EventInfo(quint32 code, QString param, qint64 time) : eventCode(code), strParam(param), timeStamp(time) {} }; class LockDialogModel; class LockWidget; class AgreementWindow; typedef QPair bgPath_Size; class FullBackgroundWidget : public QWidget, public QAbstractNativeEventFilter { Q_OBJECT public: explicit FullBackgroundWidget(LockDialogModel *model, bool isStartup = false, QWidget *parent = nullptr); ~FullBackgroundWidget(); virtual bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; QMap m_allBackgroundsMap; QMap m_allBackgroundsDataMap; // QPixmap * m_defaultPixmap ; QString m_currentUserName; QString m_oldUserName; QFuture m_loadingOneBackgroundFuture; public: int RegisteSubWnd(quint64 uWndId); int UnRegisteSubWnd(quint64 uWndId); QList GetSubWndIds(); inline bool IsStartupMode() { return m_isStartupMode; } public Q_SLOTS: void onShowBlankScreensaver(int nDelay = 0, bool isHasLock = true); void onShowLock(bool isStartup); void onShowSessionIdle(); void onShowLockScreensaver(); void onShowScreensaver(); void onCloseScreensaver(); void onDesktopResized(); void onPrepareForSleep(bool sleep); void laterActivate(); void setLockState(); void clearLockState(); void onClearScreensaver(); void onShowSessionTools(); void onShowAppBlockWindow(int actionType); void onShowMultiUsersBlockWindows(int actionType); void onShowSwitchUserLock(); void onShowSwitchToUser(QString strUserName); void onCurUserChanged(const QString &strUserName); void onUpdateUserBackground(const QString &strUserName); void onTransition(); void onAuthSucceed(QString strUserName); void onRemoveUserBackground(const QString &); void onAddUserBackground(const QString &); void onUserBackgroundChanged(const QString &); void onCurrentUserBackground(const QString &); void onSecondRunParam(const QString &); void onStartFcitx(); void enqueueEvent(quint32 eventCode, QString param = ""); Q_SIGNALS: void StartupModeChanged(bool isStartup); protected: void mousePressEvent(QMouseEvent *e); void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *event); void keyReleaseEvent(QKeyEvent *e); private: void initUI(); void initConnections(); bool eventFilter(QObject *obj, QEvent *event); void fakeFocusIn(WId window); QString getWindowNameFromWid(WId window); QString getFocusWindowName(); void tryGrabKeyboard(); void moveToPrimaryScreen(); void moveToCurrentScreen(); void setCurrentBackground(QString cmd); QString getDesktopBackground(); void initCurrentBackground(); QString getDefaultBackgroundPath(); void loadingAllUserBackground(); QString getUserBackgroundPath(const QString &); void addBackgroundData(const QString &, const QList &screensRect); bool isOpenGradation(); void startTransition(QSharedPointer &from, QSharedPointer &to); void stopTransition(); void drawBackground(QSharedPointer &backgroundBack, QSharedPointer &backgroundFront, const QRect &rect, float alpha = 1.0); QPixmap *getBackground(const QString &path, const QRect &rect); void setBackground(QSharedPointer &background); void delayLockScreen(); void onLockScreenTimeout(); void stopDelayLockScreen(); void setIsStartupMode(bool isStartup); void sendStartedFinished(); void updateScreenRects(); private Q_SLOTS: void onGlobalKeyPress(const quint8 &key); void onGlobalKeyRelease(const quint8 &key); void onGlobalButtonDrag(int xPos, int yPos); void onGlobalButtonPressed(int xPos, int yPos); void RRScreenChangeEvent(bool isFirst); void onScreensChanged(QList listMonitors); /** * @brief processEventQueue 处理事件队列 */ void processEventQueue(); private: LockDialogModel *m_modelLockDialog = nullptr; bool m_isStartupMode = false; // 上层窗口管理 QList m_listWndIds; // 锁屏 LockWidget *m_lockWidget = nullptr; AgreementWindow *m_agreementWindow = nullptr; int m_tryGrabTimes = 0; bool m_bIsLockState = false; QList widgetXScreensaverList; QTimer *m_backgrondGradationTimer = nullptr; QSharedPointer m_background; Transition m_transition; ScreenStatus screenStatus = UNDEFINED; bool m_isBlank = false; QTimer *m_timerLock = nullptr; int m_RREventBase; int m_RRErrorBase; QList m_listMonitors; QRect m_defaultGeometry; QList m_screenRects; QRect m_primaryScreenRect; UkuiWindowHelper *m_windowHelper = nullptr; // 按键事件队列相关 QQueue m_keyEventQueue; QMutex m_queueMutex; QTimer *m_queueProcessTimer = nullptr; }; #endif // FULLBACKGROUNDWIDGET_H ukui-screensaver/src/widgets/lockwidget.h0000775000175000017500000002265715172041106017551 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LOCKWIDGET_H #define LOCKWIDGET_H #include #include #include #include "userinfo.h" #include "securityquestionanswer.h" #include "uniauthservice.h" class QListWidgetItem; class MyListWidget; class QPushButton; class QPixmap; class LockDialogModel; class UserListWidget; class SessionListWidget; class AuthDialog; class MyNetworkWidget; class PowerListWidget; class TimeWidget; class BatteryWidget; class VirtualKeyboardWidget; class BlockWidget; class StatusButton; #if SUPPORT_NET_ICON class NetWorkButton; #endif // SUPPORT_NET_ICON class ResetPwdWidget; class TipLabel; enum { rootWinPicture, rootWinColor }; class LockWidget : public QWidget { Q_OBJECT public: explicit LockWidget(LockDialogModel *model, QWidget *parent = nullptr); bool exitSubWidget(bool isForScreensaver = false, bool hideVirkeyboard = true, bool hideCenterWidget = true); void stopAuth(); void startAuth(); void reloadRootBackground(); void drawRootBackground(); void updateFont(); void updateFontSize(); void onShowPowerListWidget(); void onShowInhibitWarning(QStringList list, int type); void onMulUsersLogined(int inhibitType); void onSureShutDown(int inhibitType); void onLockScreenClicked(); void onSuspendClicked(const QString &strType); void onDoPowerAction(const QString &strType); void onSwitchuserClicked(); void onShowUserListWidget(bool show = false); /** * @brief SwitchToUser * @param strUserName * @param isSwitchInCurPage */ void SwitchToUser(QString strUserName, bool isSwitchInCurPage = false); void doDelayInit(); protected: void paintEvent(QPaintEvent *event); bool eventFilter(QObject *obj, QEvent *event); void resizeEvent(QResizeEvent *event); void keyReleaseEvent(QKeyEvent *e); void changeEvent(QEvent *event); bool event(QEvent *event); private: void initUI(); void initConnections(); void initUserWidget(); void initSessionWidget(); void initPowerWidget(); void initButtonWidget(); void initTimeWidget(); void initBlockWidget(); void initSystemMonitorBtn(); void sendNetPluginVisible(bool visible); /** * @brief 获取usd快捷键键值:QString * */ void initUsdMediaKeys(); /** * @brief 快捷键转化:QString - QKeySequence * @param shortcuts 快捷键键值 * @return QKeySequence */ static const QKeySequence listFromString(QString shortcuts); void initUsdMediaStateKeys(); void initPowerManagerKey(); void updateBottomButton(); void refreshTranslate(); void setUserListWidgetLocate(); void setNetWorkWidgetLocate(); void setSessionListWidgetLocate(); void setBatteryWidgetLocate(); void setRfkillState(); void setRfkillBtnIcon(); private Q_SLOTS: /** * @brief item选中状态改变 * @param current 前一个选中item * previous 当前选中item * @return */ void onPowerItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void onButtonItemChanged(QListWidgetItem *current, QListWidgetItem *previous); /** * @brief item点击响应 * @param item * @return */ void onButtonItemClicked(QListWidgetItem *item); void onPowerItemClicked(QListWidgetItem *item); void onUsersInfoChanged(); void onCurUserChanged(const QString &strUserName); void onSessionChanged(const QString &strSession); void onUserChangedByManual(const QString &userName); void onUsdMediaKeysChanged(const QString &keys, const QString &value); void onUsdMediaStateKeysChanged(const QString &keys, const int &value); void onShowSessionListWidget(); void onShowBatteryWidget(); void onShowNetworkWidget(); void onShowVirtualKeyboard(); void onShowVirtualKeyboard(bool tabletMode); void onLineEditClicked(); void onLineEditMousePressed(); void doSystemMonitor(); void onNetWorkResetLocation(); void onSetVirkeyboardPos(); void onHideVirkeyboard(); void onConfirmBtnClicked(); void onCancelBtnClicked(); void onUPowerServiceReady(); /** * @brief onCustomRequestAccount 请求切换认证用户 * @param account 用户名 */ void onCustomRequestAccount(QString account); /** * @brief onGetCustomPluginMsg 获取插件消息槽 * @param strMsg 消息json */ void onGetCustomPluginMsg(QString strMsg); void onSessionActiveChanged(bool isActive); void onLanguageChanged(bool isCompleted); void onTabletModeChanged(bool tabletMode); void onLockDatetimeEnableChanged(bool enable, QString username); void onFontChanged(QString font); void onFontSizeChanged(double fontSize); /** * @brief onServiceUserChanged 登录服务用户切换 * @param willLoginUserInfo 新用户信息 */ void onServiceUserChanged(WillLoginUserInfo willLoginUserInfo); void onShowResetPasswdBtn(); void onShowResetPwdWidget(); void onBackToAuthDialog(bool isSuccess = false); /** * @brief onShowKylinIDCodesTips show kylinid codestips * @param message */ void onShowKylinIDCodesTips(QString message); /** * @brief isUserBindKylinID * @return */ bool isUserBindKylinID(); Q_SIGNALS: void authSucceed(QString strUserName); void sessionToolsExit(); void showBlankScreensaver(int nDelay, bool isHasLock); void startFcitx(); void moveToCurrentScreen(); void moveToPrimaryScreen(); void setCurrentBackground(); private: void setrootWindowBackground(int type, unsigned int color, QString filename); void setRootWindowBgOptions(int nOption); bool getdrawBackgroundIsStarted(); void setrootWindow(QList> screenRectList); private: // 日期时间 TimeWidget *m_timeWidget = nullptr; // 用户列表 UserListWidget *m_userListWidget = nullptr; // 会话列表 SessionListWidget *m_sessionListWidget = nullptr; // 电源管理列表 PowerListWidget *m_powerListWidget = nullptr; // 底部button列表 MyListWidget *buttonListWidget = nullptr; // 底部button布局 QWidget *m_pBottomWidget = nullptr; QPixmap background; LockDialogModel *m_modelLockDialog = nullptr; UserInfoPtr m_curUserInfo; AuthDialog *authDialog = nullptr; QTimer *m_timerChkActive = nullptr; // usd快捷键键值 QString m_areaScreenShot; QString m_areaScreenShot2; QString m_screenShot; QString m_screenShot2; QString m_windowScreenshot; int m_rfkillState = -1; QString m_strPowerButtonKey; MyNetworkWidget *m_networkWidget = nullptr; BatteryWidget *batteryWidget = nullptr; VirtualKeyboardWidget *m_virtualKeyboardWidget = nullptr; // 当前是否处于阻塞状态 bool m_isInhibitStatus = false; BlockWidget *m_blockWidget = nullptr; QString m_inhibitType; bool m_isHardInhibit = true; // 底部button StatusButton *m_sessionButton = nullptr; QListWidgetItem *m_btnItemSession = nullptr; StatusButton *m_batteryonButton = nullptr; QListWidgetItem *m_btnItemBattery = nullptr; StatusButton *m_userButton = nullptr; QListWidgetItem *m_btnItemUser = nullptr; #if SUPPORT_NET_ICON NetWorkButton *m_networkButton = nullptr; #endif // SUPPORT_NET_ICON QListWidgetItem *m_btnItemNetwork = nullptr; StatusButton *m_virKbButton = nullptr; QListWidgetItem *m_btnItemVirKb = nullptr; StatusButton *m_powerManagerButton = nullptr; QListWidgetItem *m_btnItemPowerManager = nullptr; StatusButton *m_pRfkillStatusButton = nullptr; // 底部系统监视器按钮 StatusButton *m_systemMonitorBtn = nullptr; bool m_isCustomDefault = false; /** 是否默认使用第三方认证 */ bool m_isShowNetwork = true; /** 是否显示网络插件 */ bool m_isShowUserSwitch = true; /** 是否显示用户切换 */ bool m_isShowDateTime = true; /** 是否显示日期时间 */ QString m_rootWindowBackground; int m_nPicOptions = 0; int drawBackgroundType = 0; unsigned int drawBackgroundColor = 0x0; bool drawBackgroundIsStarted = false; QFuture m_futureDrawBg; QList> m_screenRectList; bool m_isTabletMode = false; QString m_curFont; double m_curFontSize; double m_ptToPx = 1.0; int m_powerBtnNum = 0; QString systemLang; bool m_isManualPower = false; SecurityQuestionAnswer *m_pSecurityQuestionAnswer = nullptr; QPushButton *m_pResetPwdBtn = nullptr; ResetPwdWidget *m_pResetPwdWidget = nullptr; bool m_isShowResetPasswdBtn = false; int m_nUserHasBindIdentity = -1; TipLabel *m_pKylinIDCodeTip = nullptr; QSize m_widgetSize; // current widget last size }; #endif // LOCKWIDGET_H ukui-screensaver/src/widgets/loginnotifyinterface.h0000664000175000017500000000255415172041035021627 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LOGIN_NOTIFY_INTERFACE_H #define LOGIN_NOTIFY_INTERFACE_H #include "loginplugininterface.h" /** * @brief 通知类窗口接口 * */ class LoginNotifyInterface : public LoginPluginInterface { public: /** * @brief 构造函数 * */ LoginNotifyInterface() {} /** * @brief 析构函数 * */ virtual ~LoginNotifyInterface() {} /** * @brief 获取插件类型 * @return 插件类型 */ int getPluginType() { return MODULETYPE_NOTIFY; } }; #define LoginNotifyInterfaceIID "org.ukui.LoginNotifyInterface" Q_DECLARE_INTERFACE(LoginNotifyInterface, LoginNotifyInterfaceIID) #endif // LOGIN_NOTIFY_INTERFACE_H ukui-screensaver/src/widgets/talkswidget.h0000664000175000017500000000154615172041106017726 0ustar fengfeng#ifndef TALKSWIDGET_H #define TALKSWIDGET_H #include #include "iconedit.h" class CustomLabel; class QLineEdit; class TalksWidget : public QWidget { Q_OBJECT public: explicit TalksWidget(LockDialogModel *model, QWidget *parent = nullptr, int editType = CONFIDENTIALEDIT); void setQuestion(QString question); QString getEditText(); void setPrompt(QString prompt); void clearText(); int getTalksWdgHeight(); void setEchoMode(QLineEdit::EchoMode type); void updateFontSize(double lfFontSize); Q_SIGNALS: void textChanged(const QString &text); void talkEditClicked(); private: void initUI(); private: LockDialogModel *m_modelLockDialog = nullptr; CustomLabel *m_pQuestionLabel = nullptr; IconEdit *m_pAnswerEdit = nullptr; int m_editType; }; #endif // TALKSWIDGET_H ukui-screensaver/src/widgets/securityquestionanswer.h0000664000175000017500000000761415172041106022265 0ustar fengfeng#ifndef SECURITYQUESTIONANSWER_H #define SECURITYQUESTIONANSWER_H #include #include #include #include struct QuestionInfo { int nQuestionId; QString strQuestionText; }; struct AnswerInfo { int nQuestionId; QString strAnswerText; }; Q_DECLARE_METATYPE(QuestionInfo) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(AnswerInfo) Q_DECLARE_METATYPE(QList) QDBusArgument &operator<<(QDBusArgument &argument, const QuestionInfo &questionInfo); const QDBusArgument &operator>>(const QDBusArgument &argument, QuestionInfo &questionInfo); QDBusArgument &operator<<(QDBusArgument &argument, const AnswerInfo &answerInfo); const QDBusArgument &operator>>(const QDBusArgument &argument, AnswerInfo &answerInfo); class SecurityQuestionAnswer : public QDBusAbstractInterface { Q_OBJECT public: explicit SecurityQuestionAnswer(QObject *parent = nullptr); public Q_SLOTS: /** * @brief GetPresetSecurityQuestions 获取预设安全问题列表 * @param language 语言 * @return 问题列表 */ QList GetPresetSecurityQuestions(QString language); /** * @brief BindUserSecurityAnswers 绑定用户安全问题 * @param nUId 用户id * @param listAnswerInfo 安全问题答案信息 * @return 0 成功,其他失败 */ int BindUserSecurityAnswers(int nUId, QList listAnswerInfo); /** * @brief IsUserHasSecurityAnswers 用户是否绑定安全问题 * @param nUId 用户id * @return true 已绑定,其他未绑定 */ bool IsUserHasSecurityAnswers(int nUId); /** * @brief GetUserSecurityQuestions 获取用户安全问题列表 * @param nUId 用户id * @param language 语言 * @return 安全问题列表 */ QList GetUserSecurityQuestions(int nUId, QString language); /** * @brief VerifyUserSecurityAnswers 校验用户安全问题答案 * @param nUId 用户id * @param listAnswerInfo 安全问题答案列表 * @return 非空 校验token,空则校验失败 */ QString VerifyUserSecurityAnswers(int nUId, QList listAnswerInfo); /** * @brief EncodeSecurityInfo 编码安全问题答案接口 * @param strSource 原始安全问题答案 * @return 编码后的安全问题答案 */ QString EncodeSecurityInfo(const QString &strSource); /** * @brief ChangePassword 修改用户密码 * @param nUId 用户id * @param strPasswd 新密码 * @param nTimeout 超时 * @param strToken 校验返回的token * @return 0 修改成功,其他失败 */ int ChangePassword(int nUId, const QString &strPasswd, QString strToken); /** * @brief SetUserPasswdHint 绑定用户密码提示 * @param nUId 用户id * @param strPasswdHint 密码提示信息 * @return 0 成功,其他失败 */ int SetUserPasswdHint(int nUId, QString strPasswdHint); /** * @brief GetUserSecurityQuestions 获取用户密码提示 * @param nUId 用户id * @return 用户密码提示 */ QString GetUserPasswdHint(int nUId); /** * @brief VerifyAccountsInfo 校验用户安全问题答案 * @param nUId 用户id * @param kyidAccounts 麒麟ID账户名 * @param kyidpasswd 麒麟ID密码 * @param code 麒麟ID验证码 * @return 错误码 错误信息 */ QStringList VerifyAccountsInfo(int nUId, QString kyidAccounts, QString kyidpasswd, QString code); /** * @brief ChangePassword2 麒麟ID修改用户密码 * @param nUId 用户id * @param strPasswd 新密码 * @param strToken 校验返回的token * @return 0 修改成功,其他失败 */ int ChangePassword2(int nUId, const QString &strPasswd, QString strToken); public: bool isActivatable(); }; #endif // SECURITYQUESTIONANSWER_H ukui-screensaver/src/widgets/verifykyidwidget.h0000664000175000017500000000324715172041106020775 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef VERIFYKYIDWIDGET_H #define VERIFYKYIDWIDGET_H #include #include #include #include #include "lock-dialog/lockdialogmodel.h" class IconEdit; class KyIconButton; class KLabel; class VerifyKyidWidget : public QWidget { Q_OBJECT public: explicit VerifyKyidWidget(LockDialogModel *model, QWidget *parent = nullptr); QString getKylinIdEditText(); QString getPwdEditText(); QString getCaptchaEditText(); void clearText(); void onCaptchaBtnclicked(bool success); private: void initUi(); Q_SIGNALS: void textChanged(const QString &text); void captchaBtnclicked(); private: LockDialogModel *m_modelLockDialog = nullptr; IconEdit *m_pKylinIdEdit = nullptr; IconEdit *m_pPasswordEdit = nullptr; IconEdit *m_pCaptchaEdit = nullptr; KyIconButton *m_pGetCaptchaBtn = nullptr; KLabel *m_pMessageLabel = nullptr; QTimer *m_pCountdown = nullptr; QDateTime m_lastDateTime; }; #endif // VERIFYKYIDWIDGET_H ukui-screensaver/src/widgets/talkswidget.cpp0000664000175000017500000000540515172041106020257 0ustar fengfeng#include "talkswidget.h" #include #include #include #include "customlabel.h" #include "kyiconbutton.h" #define LABEL_MINIMUM_HEIGHT 12 #define LABEL_MAXIMUM_HEIGHT 30 #define WIDGET_FIXED_WIDTH 240 #define WIDGET_HEIGHT_SPACING 10 TalksWidget::TalksWidget(LockDialogModel *model, QWidget *parent, int editType) : QWidget(parent), m_modelLockDialog(model) { m_editType = editType; initUI(); } void TalksWidget::initUI() { // setFixedWidth(WIDGET_FIXED_WIDTH); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setAlignment(Qt::AlignCenter); mainLayout->setContentsMargins(0, 12, 0, 0); mainLayout->setSpacing(0); QVBoxLayout *quesTionLayout = new QVBoxLayout(); quesTionLayout->setAlignment(Qt::AlignHCenter); quesTionLayout->setContentsMargins(0, 0, 0, 0); m_pQuestionLabel = new CustomLabel(this); m_pQuestionLabel->setFontSize(14); quesTionLayout->addWidget(m_pQuestionLabel); mainLayout->addLayout(quesTionLayout); mainLayout->addSpacing(4); QVBoxLayout *answerLayout = new QVBoxLayout(); answerLayout->setAlignment(Qt::AlignHCenter); answerLayout->setContentsMargins(0, 0, 0, 0); m_pAnswerEdit = new IconEdit(m_modelLockDialog, this, m_editType); m_pAnswerEdit->setFixedSize(240, 40); m_pAnswerEdit->setAttribute(Qt::WA_InputMethodEnabled, true); QPalette pal(palette()); pal.setColor(QPalette::Window, Qt::white); m_pAnswerEdit->setPalette(pal); connect(m_pAnswerEdit, &IconEdit::textChanged, this, [=](const QString &text){ Q_EMIT textChanged(text); }); connect(m_pAnswerEdit, &IconEdit::clickedPassword, this, [=]{ Q_EMIT talkEditClicked(); }); mainLayout->addWidget(m_pAnswerEdit); answerLayout->addWidget(m_pAnswerEdit); mainLayout->addLayout(answerLayout); mainLayout->addSpacing(2); setPrompt(tr("Enter your answer")); setFocusProxy(m_pAnswerEdit); } void TalksWidget::setPrompt(QString prompt) { m_pAnswerEdit->setPrompt(prompt); } QString TalksWidget::getEditText() { return m_pAnswerEdit->text(); } void TalksWidget::setEchoMode(QLineEdit::EchoMode type) { m_pAnswerEdit->setType(type); if (type == QLineEdit::Password) { m_pAnswerEdit->setAttribute(Qt::WA_InputMethodEnabled, false); //禁用输入法 } } void TalksWidget::setQuestion(QString question) { if (question == "") { m_pQuestionLabel->hide(); } else { m_pQuestionLabel->show(); } m_pQuestionLabel->setText(question); } void TalksWidget::clearText() { m_pAnswerEdit->clearText(); } int TalksWidget::getTalksWdgHeight() { return m_pAnswerEdit->height() + m_pQuestionLabel->height() + 18; } void TalksWidget::updateFontSize(double lfFontSize) { } ukui-screensaver/src/widgets/mynetworkwidget.h0000664000175000017500000000346515172041035020652 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef MYNETWORKWIDGET_H #define MYNETWORKWIDGET_H #include #include class Interface; class MyTabWidget; class MyNetworkWidget : public QWidget { Q_OBJECT public: explicit MyNetworkWidget(QWidget *parent = nullptr); void loadNetPlugin(); void unloadNetPlugin(); private Q_SLOTS: /** * @brief 网络窗口有线和无线弹窗切换事件,用于通知网络插件窗口切换 * @param index 当前页面索引 * @return */ void onNetTabWidgetChanged(int index); void onUpdatePluginHidden(); void onNetInPutStatus(); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event); void paintEvent(QPaintEvent *p1); private: void sendNetPluginVisible(bool visible); Q_SIGNALS: void showVirtualKeyboard(); private: QPluginLoader netloader; QPluginLoader wlanloader; Interface *netInterface = nullptr; Interface *wlanInterface = nullptr; MyTabWidget *tabWidget = nullptr; QWidget *netWidget = nullptr; QWidget *wlanWidget = nullptr; bool m_netTabShow = true; bool m_wlanTabShow = true; }; #endif // WIDGET_H ukui-screensaver/src/widgets/customtabbar.h0000664000175000017500000000167715172041106020077 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef CUSTOMTABBAR_H #define CUSTOMTABBAR_H #include class CustomTabBar : public QTabBar { Q_OBJECT public: explicit CustomTabBar(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; }; #endif // CUSTOMTABBAR_H ukui-screensaver/src/widgets/binddialog.cpp0000664000175000017500000002330715172041106020032 0ustar fengfeng/* * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "binddialog.h" #include #include #include #include #include #include #include #include #include #include // 二维码生成 QImage encodeImage(const QString &s, int bulk) { QImage ret; QRcode *qr = QRcode_encodeString(s.toUtf8(), 1, QR_ECLEVEL_Q, QR_MODE_8, 1); if (qr != nullptr) { int allBulk = (qr->width) * bulk; ret = QImage(allBulk, allBulk, QImage::Format_Mono); QPainter painter(&ret); QColor fg("black"); QColor bg("white"); painter.setBrush(bg); painter.setPen(Qt::NoPen); painter.drawRect(0, 0, allBulk, allBulk); painter.setBrush(fg); for (int y = 0; y < qr->width; y++) { for (int x = 0; x < qr->width; x++) { if (qr->data[y * qr->width + x] & 1) { QRectF r(x * bulk, y * bulk, bulk, bulk); painter.drawRects(&r, 1); } } } QRcode_free(qr); } return ret; } BindDialog::BindDialog(int mod, QWidget *parent) : mode(mod), QWidget(parent) { initUI(); updateSize(); QMetaObject::connectSlotsByName(this); // 自动绑定信号槽 } BindDialog::~BindDialog() {} void BindDialog::initUI() { QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); QWidget *titleWidget = new QWidget(this); titleWidget->setObjectName(QString::fromUtf8("titleWidget")); titleWidget->setMinimumSize(QSize(0, 36)); titleWidget->setMaximumSize(QSize(16777215, 36)); QHBoxLayout *titleLayout = new QHBoxLayout(titleWidget); titleLayout->setSpacing(0); QSpacerItem *titleSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); btnClose = new QPushButton(titleWidget); titleLayout->addItem(titleSpacer); titleLayout->addWidget(btnClose, 0, Qt::AlignVCenter); btnClose->setProperty("isWindowButton", 0x2); btnClose->setObjectName("btnClose"); btnClose->setProperty("useIconHighlightEffect", 0x8); btnClose->setFixedSize(30, 30); btnClose->setIconSize(QSize(16, 16)); btnClose->setIcon(QIcon::fromTheme("window-close-symbolic")); btnClose->setFlat(true); btnClose->setToolTip(tr("Close")); layout->addWidget(titleWidget); QHBoxLayout *buttonLayout = new QHBoxLayout(this); buttonLayout->setSpacing(16); buttonLayout->setContentsMargins(24, 0, 24, 24); QSpacerItem *buttonSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); buttonLayout->addItem(buttonSpacer); btnOk = new QPushButton(this); btnOk->setObjectName("btnOk"); btnOk->setText(tr("Ok")); btnOk->setMinimumSize(QSize(88, 36)); btnCancel = new QPushButton(this); btnCancel->setObjectName("btnCancel"); btnCancel->setText(tr("Cancel")); btnCancel->setMinimumSize(QSize(88, 36)); buttonLayout->addWidget(btnCancel); buttonLayout->addWidget(btnOk); if (mode == ENSUREDIALOG) { QHBoxLayout *promptLayout = new QHBoxLayout(this); QLabel *warningLabel = new QLabel(this); warningLabel->setPixmap(QIcon::fromTheme("dialog-warning").pixmap((QSize(16, 16)))); warningLabel->setAlignment(Qt::AlignCenter); warningLabel->setFixedSize(16, 16); QLabel *promptLabel = new QLabel(this); promptLabel->setText(tr("Mfa authentication is on, please bind OTP code")); promptLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); promptLayout->addWidget(warningLabel); promptLayout->addWidget(promptLabel, 1); // 添加拉伸因子 promptLayout->setContentsMargins(24, 0, 24, 0); layout->addLayout(promptLayout, 1); btnOk->setText(tr("Go to Bind")); } else if (mode == BINDOTPDIALOG) { QVBoxLayout *promptLayout = new QVBoxLayout(this); otpPromptLabel = new QLabel(this); otpPromptLabel->setWordWrap(true); QHBoxLayout *qrcodeLayout = new QHBoxLayout(this); qrcodeLayout->setContentsMargins(0, 32, 0, 32); otpQrcodeLabel = new QLabel(this); otpQrcodeLabel->setFixedSize(150, 150); qrcodeLayout->setAlignment(Qt::AlignHCenter); qrcodeLayout->addWidget(otpQrcodeLabel); otpPromptEdit = new QLineEdit(this); otpPromptEdit->setObjectName("otpPromptEdit"); otpMessageLabel = new QLabel(this); QPalette pe; pe.setColor(QPalette::WindowText, Qt::red); otpMessageLabel->setPalette(pe); finishWidget = new QWidget(this); QVBoxLayout *finishLayout = new QVBoxLayout(finishWidget); finishLayout->addStretch(); finishLabel = new QLabel(this); finishLabel->setFixedSize(64, 64); QHBoxLayout *finishHLayout = new QHBoxLayout(finishWidget); finishHLayout->setContentsMargins(0, 0, 0, 0); finishHLayout->setAlignment(Qt::AlignHCenter); finishHLayout->addWidget(finishLabel); finishMsg = new QLabel(this); finishMsg->setWordWrap(true); QFont finishMsgFont = finishMsg->font(); finishMsgFont.setPointSize(16); finishMsg->setFont(finishMsgFont); finishTip = new QLabel(this); finishTip->setWordWrap(true); finishLayout->addLayout(finishHLayout); finishLayout->addWidget(finishMsg); finishLayout->addWidget(finishTip); finishLayout->addStretch(); finishMsg->setAlignment(Qt::AlignHCenter); promptLayout->addWidget(otpPromptLabel); promptLayout->addLayout(qrcodeLayout); promptLayout->addWidget(otpPromptEdit); promptLayout->addWidget(otpMessageLabel); promptLayout->addWidget(finishWidget); promptLayout->setContentsMargins(24, 0, 24, 0); layout->addLayout(promptLayout, 1); finishWidget->hide(); btnOk->setText(tr("Bind")); } layout->addLayout(buttonLayout); if (mode == BINDOTPDIALOG) { btnOk->setEnabled(false); } } void BindDialog::updateSize() { if (mode == ENSUREDIALOG) setFixedSize(452, 172); else setFixedSize(380, 456); } void BindDialog::resizeEvent(QResizeEvent *event) { QWidget *parentWidget = qobject_cast(parent()); if (parentWidget) { move((parentWidget->width() - width()) / 2, (parentWidget->height() - height()) / 2); } return QWidget::resizeEvent(event); } void BindDialog::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Escape: break; default: QWidget::keyPressEvent(event); } } void BindDialog::on_btnClose_clicked() { if (otpPromptEdit) { otpPromptEdit->setText(""); } Q_EMIT rejected(); Q_EMIT finished(); } void BindDialog::on_btnOk_clicked() { if (otpPromptEdit) { Q_EMIT accepted(otpPromptEdit->text()); otpPromptEdit->setText(""); } else { Q_EMIT accepted(""); } } void BindDialog::on_btnCancel_clicked() { if (otpPromptEdit) { otpPromptEdit->setText(""); } Q_EMIT rejected(); Q_EMIT finished(); } void BindDialog::paintEvent(QPaintEvent *event) { QPalette pal = qApp->palette(); QColor colorPal = pal.color(QPalette::Base); // 设置窗体为圆角 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 反锯齿; painter.setBrush(colorPal); painter.setPen(Qt::transparent); auto rect = this->rect(); painter.drawRoundedRect(rect, 12, 12); // 窗口圆角 return QWidget::paintEvent(event); } void BindDialog::setOtpPromptText(QString text) { if (otpPromptLabel) otpPromptLabel->setText(text); } void BindDialog::setOtpQrcodeText(QString text) { if (otpQrcodeLabel) { QImage img = encodeImage(text, 5); otpQrcodeLabel->setPixmap(QPixmap::fromImage(img).scaled(150, 150)); } } void BindDialog::setOtpPrpmptPromptText(QString text) { if (otpPromptEdit) otpPromptEdit->setPlaceholderText(text); } void BindDialog::setOtpMessageText(QString text) { if (otpMessageLabel) otpMessageLabel->setText(text); } void BindDialog::setOtpEmergMsg(QString text) { showFinishedPrompt(text); } void BindDialog::showFinishedPrompt(QString text) { if (otpMessageLabel) otpMessageLabel->hide(); if (otpPromptEdit) otpPromptEdit->hide(); if (otpQrcodeLabel) otpQrcodeLabel->hide(); if (otpPromptLabel) otpPromptLabel->hide(); if (btnCancel) btnCancel->hide(); if (btnClose) btnClose->hide(); if (finishWidget) finishWidget->show(); btnOk->setText(tr("Ok")); btnOk->setEnabled(true); if (finishLabel) { finishLabel->setPixmap(QIcon::fromTheme("ukui-dialog-success").pixmap(QSize(64, 64))); finishMsg->setText(tr("OTP added successfully")); finishTip->setText(text); } } void BindDialog::on_otpPromptEdit_textChanged(QString text) { if (!text.isEmpty()) { btnOk->setEnabled(true); } else { btnOk->setEnabled(false); } } ukui-screensaver/src/widgets/loginoptionswidget.cpp0000664000175000017500000011275215172041106021671 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "loginoptionswidget.h" #include #include #include "../dbusifs/giodbus.h" #include "../dbusifs/biometrichelper.h" #include "customlabel.h" #include "common/configuration.h" #include "common/global_utils.h" #include "pluginsloader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include LoginOptionsWidget::LoginOptionsWidget(BiometricHelper *bioHelper, int uid, QWidget *parent) : QWidget(parent), m_biomericProxy(bioHelper), m_uid(uid) { m_listPriority.clear(); m_listPriority.push_back(BioT_Face); m_listPriority.push_back(BioT_FingerPrint); m_listPriority.push_back(BioT_Iris); m_listPriority.push_back(BioT_VoicePrint); m_listPriority.push_back(BioT_FingerVein); m_listPriority.push_back(UniT_Remote); m_listPriority.push_back(UniT_General_Ukey); initUI(); initConnections(); m_mapDisableDev.clear(); } LoginOptionsWidget::~LoginOptionsWidget() {} void LoginOptionsWidget::initUI() { this->setFixedHeight(110); // 初始化UI m_layoutMain = new QVBoxLayout(); m_layoutMain->setContentsMargins(0, 0, 0, 0); m_layoutMain->setSpacing(16); m_layoutOptBtns = new QHBoxLayout(); m_buttonListWidget = new MyListWidget(this); m_buttonListWidget->installEventFilter(this); m_buttonListWidget->setFlow(QListWidget::LeftToRight); m_buttonListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_buttonListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QString buttonListStyle = "QListWidget{ background:rgba(255, 255, 255, 0%); border: 0px;}" "QListWidget::item{border-radius: 4px; background:rgba(255, 255, 255, 0%);}"; m_buttonListWidget->setStyleSheet(buttonListStyle); m_buttonListWidget->setSpacing(8); m_buttonListWidget->setIsLoginOptionWidget(true); connect(m_buttonListWidget, &MyListWidget::currentItemChanged, this, &LoginOptionsWidget::onButtonItemChanged); connect(m_buttonListWidget, &MyListWidget::itemClicked, this, &LoginOptionsWidget::onButtonItemClicked); m_labelOptTitle = new CustomLabel(); curFontSize = 11; m_labelOptTitle->setFontSize(16); m_labelOptTitle->setAlignment(Qt::AlignCenter); m_labelOptTitle->setText(tr("Login Options")); m_labelOptTitle->setFixedHeight(30); m_layoutMain->addWidget(m_labelOptTitle); m_layoutOptBtns->addWidget(m_buttonListWidget, Qt::AlignHCenter); m_layoutMain->addLayout(m_layoutOptBtns); m_layoutMain->addStretch(); this->setLayout(m_layoutMain); } void LoginOptionsWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { refreshTranslate(); } } void LoginOptionsWidget::refreshTranslate() { m_labelOptTitle->setTipText(tr("Login Options")); QLocale local; QString systemLang = local.name(); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { setLayoutDirection(Qt::RightToLeft); } else { setLayoutDirection(Qt::LeftToRight); } } void LoginOptionsWidget::initConnections() { if (m_biomericProxy && m_biomericProxy->isValid()) { connect(m_biomericProxy, &BiometricHelper::StatusChanged, this, &LoginOptionsWidget::onStatusChanged); connect(m_biomericProxy, &BiometricHelper::FrameWritten, this, &LoginOptionsWidget::onFrameWritten); connect(m_biomericProxy, &BiometricHelper::USBDeviceHotPlug, this, &LoginOptionsWidget::onUSBDeviceHotPlug); } } bool LoginOptionsWidget::getCurLoginOpt(int &nLoginOptType, int &nDrvId) { if (m_curDevInfo) { nLoginOptType = convertDeviceType(m_curDevInfo->deviceType); nDrvId = m_curDevInfo->id; return true; } return false; } unsigned LoginOptionsWidget::getLoginOptCount() { return m_mapDevices.size(); } DeviceInfoPtr LoginOptionsWidget::getWechatDevice() { DeviceInfoPtr devInfo = nullptr; DeviceListMap::iterator itDevInfo = m_mapDevices.begin(); for (; itDevInfo != m_mapDevices.end(); itDevInfo++) { for (auto devinfo : itDevInfo.value()) { if (devinfo && devinfo->deviceType == UniT_Remote) { if (!isDeviceDisable(devinfo->id)) { devInfo = devinfo; break; } } } if (devInfo) { break; } } return DeviceInfoPtr(devInfo); } void LoginOptionsWidget::addOptionButton(unsigned uLoginOptType, int nDrvId, QString strDrvName) { if (m_mapOptBtns.contains(nDrvId)) { return; } StatusButton *newButton = new StatusButton(this, BIOBUTTON); QListWidgetItem *powerButtonItem = new QListWidgetItem(); powerButtonItem->setSizeHint(QSize(48, 48)); m_buttonListWidget->insertItem(m_buttonListWidget->count(), powerButtonItem); m_buttonListWidget->setItemWidget(powerButtonItem, newButton); m_buttonListWidget->addItem(powerButtonItem); newButton->setCheckable(true); newButton->setChecked(false); newButton->setDrivedId(nDrvId); connect(newButton, &StatusButton::buttonClicked, this, &LoginOptionsWidget::onOptionSelected); m_listDriveId.append(nDrvId); QPixmap iconPixmap; switch (uLoginOptType) { case LOGINOPT_TYPE_PASSWORD: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-password.svg", "white", 16); break; case LOGINOPT_TYPE_GENERAL_UKEY: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-ukey.svg", "white", 16); break; case LOGINOPT_TYPE_FACE: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-face.svg", "white", 16); break; case LOGINOPT_TYPE_FINGERPRINT: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-finger.svg", "white", 16); break; case LOGINOPT_TYPE_IRIS: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-iris.svg", "white", 16); break; case LOGINOPT_TYPE_VOICEPRINT: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-voice.svg", "white", 16); break; case LOGINOPT_TYPE_FINGERVEIN: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-fingervein.svg", "white", 16); break; case LOGINOPT_TYPE_QRCODE: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-qrcode.svg", "white", 16); break; case LOGINOPT_TYPE_CUSTOM: { LoginAuthInterface *plugin = dynamic_cast( PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).values().first()); QString strIcon = plugin->icon(); if (strIcon.startsWith("/")) { if (strIcon.endsWith(".svg")) { iconPixmap = loadSvg(strIcon, "white", 16); } else { iconPixmap.load(strIcon); iconPixmap.scaled(40, 40); } } else { iconPixmap = QIcon::fromTheme(strIcon).pixmap(48, 48).scaled(40, 40); } if (iconPixmap.isNull()) { iconPixmap = loadSvg(":/image/assets/ukui-loginopt-custom.svg", "white", 16); } } break; } newButton->setIcon(iconPixmap); newButton->setToolTip(strDrvName); newButton->installEventFilter(this); if (isDeviceDisable(nDrvId)) { newButton->setDisabled(true); } else { newButton->setDisabled(false); } m_mapOptBtns[nDrvId] = newButton; m_mapOptItems[nDrvId] = powerButtonItem; } bool LoginOptionsWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyRelease) { MyListWidget *listWidget = qobject_cast(obj); if (listWidget) { if (listWidget && listWidget->hasFocus()) { // Tab键切入时,设置焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::SelectCurrent); listWidget->currentItemChanged(listWidget->currentItem(), nullptr); } else { // Tab键切出时,清空焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::Clear); } } } return QWidget::eventFilter(obj, event); } bool LoginOptionsWidget::getHasUkeyOptions() { return isShowUkey; } void LoginOptionsWidget::clearOptionButtons() { QMap::iterator itMapItem = m_mapOptItems.begin(); for (; itMapItem != m_mapOptItems.end(); itMapItem++) { if (itMapItem.value()) { m_buttonListWidget->removeItemWidget(itMapItem.value()); delete itMapItem.value(); } } m_mapOptBtns.clear(); m_mapOptItems.clear(); m_listDriveId.clear(); } void LoginOptionsWidget::updateOptionButtons() { isShowUkey = false; bool hasCustom = false; clearOptionButtons(); addOptionButton(LOGINOPT_TYPE_PASSWORD, -1, tr("Password")); DeviceListMap::iterator itMapDev = m_mapDevices.begin(); for (; itMapDev != m_mapDevices.end(); itMapDev++) { for (DeviceInfoPtr devPtr : itMapDev.value()) { if (devPtr) { if (devPtr->deviceType == UniT_General_Ukey) { // ukey 设备类型排在二维码前,但实际上应该显示在二维码之后,因此暂时不添加 isShowUkey = true; continue; } else if (devPtr->deviceType == UniT_Custom) { hasCustom = true; continue; } addOptionButton(itMapDev.key(), devPtr->id, getDeviceType_tr(devPtr->deviceType)); } } } itMapDev = m_mapDevices.begin(); if (isShowUkey || hasCustom) { for (; itMapDev != m_mapDevices.end(); itMapDev++) { for (DeviceInfoPtr devPtr : itMapDev.value()) { if (devPtr && devPtr->deviceType == UniT_General_Ukey) { // 此处才添加ukey addOptionButton(itMapDev.key(), devPtr->id, getDeviceType_tr(devPtr->deviceType)); } else if (devPtr && devPtr->deviceType == UniT_Custom) { addOptionButton(itMapDev.key(), devPtr->id, tr("Other")); } } } } // 存在特征但没有插入ukey if (!isShowUkey && m_biomericProxy->GetHasUkeyFeature(m_uid)) { addOptionButton(LOGINOPT_TYPE_GENERAL_UKEY, -2, getDeviceType_tr(LOGINOPT_TYPE_GENERAL_UKEY)); isShowUkey = true; } m_labelOptTitle->show(); m_buttonListWidget->setFixedSize(QSize((48 + m_buttonListWidget->spacing() * 2) * m_buttonListWidget->count(), 64)); } void LoginOptionsWidget::setUser(int uid) { m_uid = uid; readDevicesInfo(); } void LoginOptionsWidget::readDevicesInfo() { m_mapDevices.clear(); bool isAuthEnable; if (isGreeterMode()) { isAuthEnable = getBioAuthEnable(ENABLETYPE_GREETER); } else { isAuthEnable = getBioAuthEnable(ENABLETYPE_SAVER); } bool isQRCodeEnable = getQRCodeEnable(); DeviceList deviceList = m_biomericProxy->GetDevList(); QStringList listDefDevices = getAllDefDevices(); qDebug() << "BeginGetFeature------!"; FeatureMap mapFeatures = m_biomericProxy->GetUserFeatures(m_uid); qDebug() << m_uid << ",count:" << mapFeatures.size(); for (auto pDeviceInfo : deviceList) { if (!isAuthEnable && pDeviceInfo->deviceType <= BioT_VoicePrint) continue; if (!isQRCodeEnable && pDeviceInfo->deviceType == UniT_Remote) continue; int nFeatureCount = 0; if (mapFeatures.contains(pDeviceInfo->shortName)) { nFeatureCount = mapFeatures[pDeviceInfo->shortName].size(); } qDebug() << *pDeviceInfo << ",count:" << nFeatureCount; if (nFeatureCount > 0) { int nDevType = LOGINOPT_TYPE_OTHERS; nDevType = convertDeviceType(pDeviceInfo->deviceType); if ((pDeviceInfo->deviceType == UniT_Remote || listDefDevices.contains(pDeviceInfo->shortName)) && !m_mapDevices.contains(nDevType)) { m_mapDevices[nDevType].push_back(pDeviceInfo); } } } // 检查是否有第三方认证插件(非root用户) if (m_uid > 0 && PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).size() > 0) { LoginAuthInterface *plugin = dynamic_cast( PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).values().first()); DeviceInfoPtr deviceInfoPtr = std::make_shared(); deviceInfoPtr->id = LOGINAUTH_CUSTOM_ID; deviceInfoPtr->shortName = CUSTOM_PLUGIN_DEV_PREFIX + plugin->name(); deviceInfoPtr->deviceType = UniT_Custom; m_mapDevices[LOGINOPT_TYPE_CUSTOM].push_back(deviceInfoPtr); } updateOptionButtons(); } QString LoginOptionsWidget::getCustomDevName() { if (m_mapDevices.contains(LOGINOPT_TYPE_CUSTOM)) { return m_mapDevices[LOGINOPT_TYPE_CUSTOM].first()->shortName; } return ""; } void LoginOptionsWidget::SetExtraInfo(QString extra_info, QString info_type) { if (!m_biomericProxy) { qWarning() << "BiometricProxy doesn't exist."; return; } m_biomericProxy->SetExtraInfo(info_type, extra_info); } void LoginOptionsWidget::startAuth(DeviceInfoPtr device, int uid) { if (!m_biomericProxy) { qWarning() << "BiometricProxy doesn't exist."; return; } if (!device) { qWarning() << "Biometric Auth device invalid."; return; } if (m_isInAuth) { qDebug() << "Identification is currently under way, stop it"; stopAuth(); } qDebug() << "deviceInfo:" << device->id; this->m_curDevInfo = device; this->m_uid = uid; struct passwd *pwdInfo = getpwuid(uid); if (pwdInfo) { this->m_strUserName = pwdInfo->pw_name; } else { this->m_strUserName = QString::number(uid); } this->m_isStopped = false; this->m_curLoginOptType = convertDeviceType(this->m_curDevInfo->deviceType); updateUIStatus(); if (this->m_curLoginOptType == LOGINOPT_TYPE_GENERAL_UKEY) startUkeyAuth(); else startAuth_(); } void LoginOptionsWidget::startUkeyAuth() { if (!m_curDevInfo) return; qDebug().noquote() << QString("Identify:[drvid: %1, uid: %2]").arg(m_curDevInfo->id).arg(m_uid); m_isInAuth = true; m_dupFD = -1; Q_EMIT setLoadingImage(); m_biomericProxy->StopOps(m_curDevInfo->id); QDBusPendingCall call = m_biomericProxy->UkeyIdentify(m_curDevInfo->id, 2, m_uid); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &LoginOptionsWidget::onIdentifyComplete); } void LoginOptionsWidget::startAuth_() { if (!m_curDevInfo) return; qDebug().noquote() << QString("Identify:[drvid: %1, uid: %2]").arg(m_curDevInfo->id).arg(m_uid); m_isInAuth = true; m_dupFD = -1; if (m_curDevInfo->deviceType == UniT_Custom) { LoginAuthInterface *authPlugin = getCustomLoginAuth(); if (authPlugin) { struct passwd *pwdInfo = getpwuid(m_uid); if (pwdInfo) { authPlugin->startAuthenticate(m_uid, pwdInfo->pw_name); } } } else { Q_EMIT setLoadingImage(); m_biomericProxy->StopOps(m_curDevInfo->id); QDBusPendingCall call = m_biomericProxy->Identify(m_curDevInfo->id, m_uid); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &LoginOptionsWidget::onIdentifyComplete); } } void LoginOptionsWidget::stopAuth() { m_isStopped = true; if (!m_isInAuth || !m_curDevInfo) { return; } if (m_curDevInfo->deviceType == UniT_Custom) { LoginAuthInterface *authPlugin = getCustomLoginAuth(); if (authPlugin) { struct passwd *pwdInfo = getpwuid(m_uid); if (pwdInfo) { authPlugin->stopAuthenticate(m_uid, pwdInfo->pw_name); } } } else { m_biomericProxy->StopOps(m_curDevInfo->id); } if (m_retrytimer && m_retrytimer->isActive()) { m_retrytimer->stop(); delete m_retrytimer; m_retrytimer = nullptr; } m_isInAuth = false; if (m_curLoginOptType != LOGINOPT_TYPE_QRCODE) { Q_EMIT updateImage(QImage()); } } void LoginOptionsWidget::onUkeyIdentifyComplete(QDBusPendingCallWatcher *watcher) { if (m_isStopped == true) return; QDBusPendingReply reply = *watcher; if (reply.isError()) { qWarning() << "Identify error: " << reply.error().message(); Q_EMIT authComplete(false, -1); return; } int result = reply.argumentAt(0).toInt(); int authUid = reply.argumentAt(1).toInt(); qDebug() << result << authUid << m_uid; if (result == DBUS_RESULT_SUCCESS) { qDebug() << "Identify success"; Q_EMIT authComplete(true, 0); } else { qDebug() << "Identify failed"; Q_EMIT authComplete(false, 2); } } void LoginOptionsWidget::onIdentifyComplete(QDBusPendingCallWatcher *watcher) { if (m_isStopped == true) return; QDBusPendingReply reply = *watcher; if (reply.isError()) { qWarning() << "Identify error: " << reply.error().message(); Q_EMIT authComplete(false, -1); return; } int result = reply.argumentAt(0).toInt(); int authUid = reply.argumentAt(1).toInt(); qDebug() << result << authUid << m_uid; // 特征识别成功,而且用户id匹配 if (result == DBUS_RESULT_SUCCESS && authUid == m_uid) { qDebug() << "Identify success"; Q_EMIT authComplete(true, 0); } else if (result == DBUS_RESULT_NOTMATCH) { // 特征识别不匹配 qDebug() << "Identify failed"; Q_EMIT authComplete(false, 2); } else if (result == DBUS_RESULT_ERROR && m_curDevInfo) { // 识别发生错误 StatusReslut ret = m_biomericProxy->UpdateStatus(m_curDevInfo->id); qDebug() << "StatusReslut:" << ret.result << "," << ret.enable << "," << ret.devNum << "," << ret.devStatus << "," << ret.opsStatus << "," << ret.notifyMessageId; // 识别操作超时 304/404 认证超时;8 网络错误; if (ret.result == 0) { if (ret.opsStatus == 10) { // 10 当前时间错误导致网络错误 Q_EMIT authComplete(false, -4); } else if ( ret.opsStatus == OPS_IDENTIFY_TIMEOUT || ret.opsStatus == OPS_VERIFY_TIMEOUT || ret.opsStatus == 8) { // 304认证超时, 8网络异常 Q_EMIT authComplete(false, 1); } else if (ret.opsStatus == OPS_IDENTIFY_STOP_BY_USER || ret.opsStatus == OPS_VERIFY_STOP_BY_USER) { Q_EMIT authComplete(false, -2); // 主动停止,直接重试 } else if ( ret.opsStatus == OPS_OPEN_FAIL || ret.opsStatus == OPS_OPEN_ERROR) { // 无法打开设备(设备是坏的/被占用),直接停止 Q_EMIT authComplete(false, 5); } else if (ret.opsStatus >= OPS_GET_FLIST_SUCCESS && ret.opsStatus <= OPS_GET_FLIST_MAX) { Q_EMIT authComplete(false, -3); // 主动停止,直接重试 } else { Q_EMIT authComplete(false, 2); } } else { Q_EMIT authComplete(false, 2); } } else { Q_EMIT authComplete(false, 2); } } void LoginOptionsWidget::onFrameWritten(int drvid) { if (!m_curDevInfo || m_curDevInfo->id != drvid || !m_isInAuth) { return; } if (m_dupFD == -1) { m_dupFD = get_server_gvariant_stdout(drvid); } if (m_dupFD <= 0) return; lseek(m_dupFD, 0, SEEK_SET); char base64_bufferData[1024 * 1024]; int rc = read(m_dupFD, base64_bufferData, 1024 * 1024); if (rc > 0) { QByteArray imageData(base64_bufferData, rc); QBuffer imgBuffer(&imageData); imgBuffer.open(QIODevice::ReadOnly); QImageReader imgReader(&imgBuffer); QImage image = imgReader.read(); if (image.isNull()) { return; } image = image.convertToFormat(QImage::Format_RGB888); Q_EMIT updateImage(image); } } void LoginOptionsWidget::onStatusChanged(int drvid, int status) { if (!m_isInAuth || !m_curDevInfo) { return; } if (drvid != m_curDevInfo->id) { return; } // // 显示来自服务的提示信息 // if(status == STATUS_NOTIFY) // { // QString notifyMsg = m_biomericProxy->GetNotifyMesg(drvid); // Q_EMIT updateAuthMsg(notifyMsg); // } } void LoginOptionsWidget::setCurrentDevice(int drvid) { DeviceInfoPtr pDeviceInfo = findDeviceById(drvid); if (pDeviceInfo) { setCurrentDevice(pDeviceInfo); } } void LoginOptionsWidget::setCurrentDevice(const QString &deviceName) { DeviceInfoPtr pDeviceInfo = findDeviceByName(deviceName); if (pDeviceInfo) { setCurrentDevice(pDeviceInfo); } } void LoginOptionsWidget::setCurrentDevice(const DeviceInfoPtr &pDeviceInfo) { this->m_curDevInfo = pDeviceInfo; } DeviceInfoPtr LoginOptionsWidget::findDeviceById(int drvid) { for (int type : m_mapDevices.keys()) { DeviceList &deviceList = m_mapDevices[type]; auto iter = std::find_if(deviceList.begin(), deviceList.end(), [&](DeviceInfoPtr ptr) { return ptr->id == drvid; }); if (iter != deviceList.end()) { return *iter; } } return DeviceInfoPtr(); } DeviceInfoPtr LoginOptionsWidget::findDeviceByName(const QString &name) { for (int type : m_mapDevices.keys()) { DeviceList &deviceList = m_mapDevices[type]; auto iter = std::find_if( deviceList.begin(), deviceList.end(), [&](DeviceInfoPtr ptr) { return ptr->shortName == name; }); if (iter != deviceList.end()) { return *iter; } } return DeviceInfoPtr(); } DeviceList LoginOptionsWidget::findDeviceByType(int nBioType) { if (m_mapDevices.contains(nBioType)) { return m_mapDevices[nBioType]; } return DeviceList(); } void LoginOptionsWidget::onUSBDeviceHotPlug(int drvid, int action, int /*devNum*/) { int savedDeviceId = (m_curDevInfo ? m_curDevInfo->id : -1); int savedCount = 0; for (int type : m_mapDevices.keys()) savedCount += m_mapDevices.value(type).count(); switch (action) { case ACTION_ATTACHED: { // 插入设备后,需要更新设备列表 readDevicesInfo(); break; } case ACTION_DETACHED: { DeviceInfoPtr pDeviceInfo = findDeviceById(drvid); if (pDeviceInfo) { int nDevType = LOGINOPT_TYPE_PASSWORD; nDevType = convertDeviceType(pDeviceInfo->deviceType); if (savedDeviceId == drvid) { if (m_isInAuth) { Q_EMIT updateAuthMsg(tr("Identify device removed!")); } } m_mapDevices[nDevType].removeOne(pDeviceInfo); if (m_mapDevices[nDevType].isEmpty()) { m_mapDevices.remove(nDevType); } } break; } } int count = 0; for (int type : m_mapDevices.keys()) count += m_mapDevices.value(type).count(); // 设备数量发生了变化 if (count != savedCount) { updateOptionButtons(); Q_EMIT notifyOptionsChange(count); updateUIStatus(); } } bool LoginOptionsWidget::getAuthDouble() { QSettings settings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat); bool distribId = settings.value("DoubleAuth").toBool(); return distribId; } void LoginOptionsWidget::setSelectedPassword() { if (m_mapOptBtns.contains(-1)) { QPushButton *btn = m_mapOptBtns[-1]; if (btn && btn->isVisible()) { btn->setChecked(true); } m_curDevInfo = nullptr; } } void LoginOptionsWidget::updateUIStatus() { if (!m_curDevInfo && m_mapOptBtns.contains(-1)) { StatusButton *btn = m_mapOptBtns[-1]; if (btn && btn->isVisible()) { btn->setClickedStatus(CLICKED); } } if (m_curDevInfo) { if (m_mapOptBtns.contains(m_curDevInfo->id)) { StatusButton *btn = m_mapOptBtns[m_curDevInfo->id]; if (btn) { btn->setClickedStatus(CLICKED); } } } } void LoginOptionsWidget::onOptionSelected(int drvid) { m_nCurDrvid = drvid; DeviceInfoPtr info = findDeviceById(drvid); if (info && !isDeviceDisable(info->id)) { Q_EMIT optionSelected(convertDeviceType(info->deviceType), info); } else if (drvid == -1) { stopAuth(); m_curDevInfo = nullptr; Q_EMIT optionSelected(LOGINOPT_TYPE_PASSWORD, nullptr); } else if (drvid == -2) { // 存在ukey特征,但未插入ukey stopAuth(); m_curDevInfo = nullptr; Q_EMIT optionSelected(LOGINOPT_TYPE_GENERAL_UKEY, nullptr); } QMap::iterator itMapBtn = m_mapOptBtns.begin(); for (; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { if (itMapBtn.key() == drvid && itMapBtn.value()) { itMapBtn.value()->setClickedStatus(CLICKED); } else if (itMapBtn.key() != drvid && itMapBtn.value()) { itMapBtn.value()->setClickedStatus(NORMAL); } } } int LoginOptionsWidget::convertDeviceType(int nDevType) { int nLoginOptType = LOGINOPT_TYPE_OTHERS; switch (nDevType) { case BioT_FingerPrint: nLoginOptType = LOGINOPT_TYPE_FINGERPRINT; break; case BioT_FingerVein: nLoginOptType = LOGINOPT_TYPE_FINGERVEIN; break; case BioT_Iris: nLoginOptType = LOGINOPT_TYPE_IRIS; break; case BioT_Face: nLoginOptType = LOGINOPT_TYPE_FACE; break; case BioT_VoicePrint: nLoginOptType = LOGINOPT_TYPE_VOICEPRINT; break; case UniT_General_Ukey: nLoginOptType = LOGINOPT_TYPE_GENERAL_UKEY; break; case UniT_Advanced_Ukey: nLoginOptType = LOGINOPT_TYPE_ADVANCED_UKEY; break; case UniT_Remote: nLoginOptType = LOGINOPT_TYPE_QRCODE; break; case UniT_Custom: nLoginOptType = LOGINOPT_TYPE_CUSTOM; break; default: nLoginOptType = LOGINOPT_TYPE_OTHERS; break; } return nLoginOptType; } void LoginOptionsWidget::setDeviceDisable(int nDevId, bool bDisable) { if (bDisable) { m_mapDisableDev[m_uid][nDevId] = true; QMap::iterator itMapBtn = m_mapOptBtns.begin(); for (; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { if (itMapBtn.key() == nDevId && itMapBtn.value()) { itMapBtn.value()->setDisabled(true); break; } } } else { m_mapDisableDev[m_uid][nDevId] = false; QMap::iterator itMapBtn = m_mapOptBtns.begin(); for (; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { if (itMapBtn.key() == nDevId && itMapBtn.value()) { itMapBtn.value()->setDisabled(false); break; } } } } void LoginOptionsWidget::setAllDeviceDisable(bool bDisable) { if (bDisable) { QMap::iterator itMapBtn = m_mapOptBtns.begin(); for (; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { m_mapDisableDev[m_uid][itMapBtn.key()] = true; if (itMapBtn.value()) { QPixmap pixmap = itMapBtn.value()->icon().pixmap(QSize(16, 16)); QPixmap grayPixmap = drawSymbolicColoredPixmap(pixmap, "gray"); itMapBtn.value()->setIcon(grayPixmap); itMapBtn.value()->setDisabled(true); } } } else { QMap::iterator itMapBtn = m_mapOptBtns.begin(); for (; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { m_mapDisableDev[m_uid][itMapBtn.key()] = false; if (itMapBtn.value()) { QPixmap pixmap = itMapBtn.value()->icon().pixmap(QSize(16, 16)); QPixmap whitePixmap = drawSymbolicColoredPixmap(pixmap, "white"); itMapBtn.value()->setIcon(whitePixmap); itMapBtn.value()->setDisabled(false); } } } } bool LoginOptionsWidget::isDeviceDisable(int nDevId) { if (m_mapDisableDev[m_uid].contains(nDevId)) { return m_mapDisableDev[m_uid][nDevId]; } return false; } void LoginOptionsWidget::clearDeviceDisable(int uid) { if (m_mapDisableDev.contains(uid)) { return m_mapDisableDev[uid].clear(); } } QPixmap LoginOptionsWidget::loadSvg(QString path, QString color, int size) { int origSize = size; const auto ratio = qApp->devicePixelRatio(); if (2 == ratio) { size += origSize; } else if (3 == ratio) { size += origSize; } QPixmap pixmap(size, size); QSvgRenderer renderer(path); pixmap.fill(Qt::transparent); QPainter painter; painter.begin(&pixmap); renderer.render(&painter); painter.end(); pixmap.setDevicePixelRatio(ratio); return drawSymbolicColoredPixmap(pixmap, color); } QPixmap LoginOptionsWidget::drawSymbolicColoredPixmap(QPixmap &source, QString cgColor) { QImage img = source.toImage(); for (int x = 0; x < img.width(); x++) { for (int y = 0; y < img.height(); y++) { auto color = img.pixelColor(x, y); if (color.alpha() > 0) { if ("white" == cgColor) { color.setRed(255); color.setGreen(255); color.setBlue(255); img.setPixelColor(x, y, color); } else if ("black" == cgColor) { color.setRed(0); color.setGreen(0); color.setBlue(0); img.setPixelColor(x, y, color); } else if ("gray" == cgColor) { color.setRed(152); color.setGreen(163); color.setBlue(164); img.setPixelColor(x, y, color); } else if ("blue" == cgColor) { color.setRed(61); color.setGreen(107); color.setBlue(229); img.setPixelColor(x, y, color); } else { return source; } } } } return QPixmap::fromImage(img); } bool LoginOptionsWidget::getBioAuthEnable(int nType) { bool isEnable = false; if (UniAuthService::instance()->isActivatable()) { struct passwd *pwInfo = getpwuid(m_uid); if (pwInfo) { isEnable = UniAuthService::instance()->getBioAuthStatus(pwInfo->pw_name, ENABLETYPE_BIO); if (isEnable) { isEnable = UniAuthService::instance()->getBioAuthStatus(pwInfo->pw_name, nType); } return isEnable; } else { return false; } } else { return true; } } bool LoginOptionsWidget::getQRCodeEnable() { if (UniAuthService::instance()->isActivatable()) { return UniAuthService::instance()->getQRCodeEnable(); } else { return true; } } QString LoginOptionsWidget::getDefaultDevice(QString strUserName, int bioType) { if (UniAuthService::instance()->isActivatable()) { QString defaultDeviceName = ""; QString strDeviceName = UniAuthService::instance()->getDefaultDevice(strUserName, bioType); if (!strDeviceName.isEmpty()) { DeviceInfoPtr pDeviceInfo = findDeviceByName(strDeviceName); if (pDeviceInfo) { defaultDeviceName = strDeviceName; } } return defaultDeviceName; } else { return ""; } } QString LoginOptionsWidget::getDefaultDevice(QString strUserName) { if (UniAuthService::instance()->isActivatable()) { QString defaultDeviceName = ""; for (auto bioType : m_listPriority) { if (bioType == UniT_Remote) { DeviceList listDevice = findDeviceByType(bioType); for (auto device : listDevice) { defaultDeviceName = device->shortName; break; } if (!defaultDeviceName.isEmpty()) { break; } } else { QString strDeviceName = UniAuthService::instance()->getDefaultDevice(strUserName, bioType); if (!strDeviceName.isEmpty()) { DeviceInfoPtr pDeviceInfo = findDeviceByName(strDeviceName); if (pDeviceInfo) { defaultDeviceName = strDeviceName; break; } } } } return QString(defaultDeviceName); } else { return ""; } } QStringList LoginOptionsWidget::getAllDefDevices() { QStringList listDefDevices; if (UniAuthService::instance()->isActivatable()) { struct passwd *pwdInfo = getpwuid(m_uid); if (pwdInfo) { listDefDevices = UniAuthService::instance()->getAllDefaultDevice(pwdInfo->pw_name); } } else { QString defaultDeviceName; struct passwd *pwd = getpwuid(m_uid); if (pwd) { QString userConfigFile = QString(pwd->pw_dir) + "/.biometric_auth/ukui_biometric.conf"; QSettings userConfig(userConfigFile, QSettings::IniFormat); qDebug() << userConfig.fileName(); defaultDeviceName = userConfig.value("DefaultDevice").toString(); qDebug() << defaultDeviceName; } if (defaultDeviceName.isEmpty()) { QSettings sysConfig("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat); defaultDeviceName = sysConfig.value("DefaultDevice").toString(); } qDebug() << "default device: " << defaultDeviceName; if (!defaultDeviceName.isEmpty()) { listDefDevices.push_back(defaultDeviceName); } } return QStringList(listDefDevices); } LoginAuthInterface *LoginOptionsWidget::getCustomLoginAuth() { if (PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).values().size() <= 0) { return nullptr; } return dynamic_cast( PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).values().first()); } void LoginOptionsWidget::updateLoginOptionFont(QString fontFamily) { if (m_labelOptTitle) { m_labelOptTitle->setFontFamily(fontFamily); } } void LoginOptionsWidget::updateLoginOptionFontSize(double fontSize) { if (m_labelOptTitle) { m_labelOptTitle->setFontSize(fontSize); } } void LoginOptionsWidget::onButtonItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QWidget *currentWidget = m_buttonListWidget->itemWidget(current); StatusButton *currentItem = qobject_cast(currentWidget); if (currentItem) { currentItem->setClickedStatus(SELECT); } if (previous) { QWidget *previousWidget = m_buttonListWidget->itemWidget(previous); StatusButton *previousItem = qobject_cast(previousWidget); if (previousItem && previousItem->getDrivedId() == m_nCurDrvid) { previousItem->setClickedStatus(CLICKED); } else if (previousItem) { previousItem->setClickedStatus(NORMAL); } } } void LoginOptionsWidget::onButtonItemClicked(QListWidgetItem *item) { QWidget *widget = m_buttonListWidget->itemWidget(item); StatusButton *currentItem = dynamic_cast(widget); if (currentItem) { currentItem->setClickedStatus(CLICKED); currentItem->buttonClicked(currentItem->getDrivedId()); } } QString LoginOptionsWidget::getDeviceType_tr(int deviceType) { switch (deviceType) { case BioT_FingerPrint: return tr("FingerPrint"); case BioT_FingerVein: return tr("FingerVein"); case BioT_Iris: return tr("Iris"); case BioT_Face: return tr("Face"); case BioT_VoicePrint: return tr("VoicePrint"); case UniT_General_Ukey: return tr("Ukey"); case UniT_Remote: return tr("QRCode"); default: return ""; } } ukui-screensaver/src/widgets/lockwidget.cpp0000775000175000017500000027002615172041106020077 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "lockwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "poweritemwidget.h" #include "useritemwidget.h" #include "mylistwidget.h" #include "userlistwidget.h" #include "sessionlistwidget.h" #include "statusbutton.h" #include "../lock-dialog/lockdialogmodel.h" #include "iconedit.h" #include "commonfunc.h" #include "authdialog.h" #include "powerlistwidget.h" #include "timewidget.h" #include "networkbutton.h" #include "definetypes.h" #include "mynetworkwidget.h" #include "batterywidget.h" #include "VirtualKeyboard/src/virtualkeyboardwidget.h" #include "blockwidget.h" #include "pluginsloader.h" #include "global_utils.h" #include "../lock-dialog/languagesetting.h" #include "../lock-dialog/rootWindowBackground.h" #include "configuration.h" #include "utils.h" #include "resetpwdwidget.h" #include "tiplabel.h" #include #include using namespace kdk; #define BLUR_RADIUS 300 #define RIGHT_MARGIN 24 #define BOTTOM_MARGIN 24 #define PREFIX QString(RESOURCE_PATH) #define IMAGE_DIR PREFIX + "/images/" float scale; LockWidget::LockWidget(LockDialogModel *model, QWidget *parent) : QWidget(parent), m_modelLockDialog(model) { scale = 1.0; m_curUserInfo = m_modelLockDialog->findUserByName(m_modelLockDialog->defaultUserName()); initUI(); initConnections(); initUsdMediaKeys(); initPowerManagerKey(); installEventFilter(this); } bool LockWidget::event(QEvent *event) { bool bReturn = QWidget::event(event); if (event && event->type() == QEvent::LanguageChange) { QLocale local; systemLang = local.name(); // 加载网络插件时会安装语言文件,会触发语言改变信号,qt内部会根据语言名称设置默认布局方向, // 因此收到语言变化事件后需重新设置布局方向 if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { buttonListWidget->setLayoutDirection(Qt::LeftToRight); } else { buttonListWidget->setLayoutDirection(Qt::RightToLeft); } } return bReturn; } bool LockWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyRelease) { MyListWidget *listWidget = qobject_cast(obj); if (listWidget && !listWidget->isHidden()) { if (listWidget && listWidget->hasFocus()) { // Tab键切入时,设置焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::SelectCurrent); listWidget->currentItemChanged(listWidget->currentItem(), nullptr); } else { // Tab键切出时,清空焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::Clear); } } } else if (event->type() == QEvent::MouseButtonPress) { /// TODO if ((m_networkWidget && m_networkWidget == obj) || (m_userListWidget && m_userListWidget == obj) || (m_sessionListWidget && m_sessionListWidget == obj) || (m_virtualKeyboardWidget && m_virtualKeyboardWidget == obj)) { return true; } else if (buttonListWidget && obj != buttonListWidget) { exitSubWidget(false, false, false); } } else if (event->type() == QEvent::Show) { if (obj == this) { if (authDialog && m_powerListWidget && m_powerListWidget->isHidden()) { authDialog->inhibitShow(false); } } } else if (event->type() == QEvent::Hide) { if (obj == this) { if (authDialog && m_powerListWidget && !m_powerListWidget->isHidden()) { authDialog->inhibitShow(true); } } } return false; } bool LockWidget::exitSubWidget(bool isForScreensaver, bool hideVirkeyboard, bool hideCenterWidget) { m_isManualPower = false; qDebug() << "exitSubWidget:" << isForScreensaver << hideVirkeyboard << m_modelLockDialog->isSessionTools(); if (m_modelLockDialog->isSessionTools()) { authDialog->setFocus(); authDialog->inhibitShow(false); buttonListWidget->show(); m_timeWidget->setVisible(m_isShowDateTime); if (m_powerListWidget) { m_powerListWidget->hide(); } if (m_systemMonitorBtn && !m_systemMonitorBtn->isHidden()) { m_systemMonitorBtn->hide(); } if (m_blockWidget) { m_blockWidget->hide(); } if (m_powerManagerButton) { m_powerManagerButton->setClickedStatus(NORMAL); } m_modelLockDialog->setIsSessionTools(false); if (!isForScreensaver) { Q_EMIT sessionToolsExit(); } return false; } bool allExited = true; if (m_userListWidget && !m_userListWidget->isHidden()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } allExited = false; } if (hideCenterWidget && m_blockWidget && !m_blockWidget->isHidden()) { m_blockWidget->hide(); m_timeWidget->show(); if (m_powerListWidget && m_powerListWidget->isHidden()) { m_powerListWidget->show(); } buttonListWidget->show(); allExited = false; } if (m_powerListWidget && !m_powerListWidget->isHidden()) { if (m_isShowResetPasswdBtn && m_pResetPwdBtn) { m_pResetPwdBtn->show(); } m_powerListWidget->hide(); buttonListWidget->show(); if (m_systemMonitorBtn && !m_systemMonitorBtn->isHidden()) { m_systemMonitorBtn->hide(); } if (m_powerManagerButton) { m_powerManagerButton->setClickedStatus(NORMAL); } authDialog->inhibitShow(false); m_timeWidget->setVisible(m_isShowDateTime); allExited = false; } if (m_networkWidget && !m_networkWidget->isHidden()) { m_networkWidget->hide(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON allExited = false; } if (hideVirkeyboard && m_virtualKeyboardWidget && !m_virtualKeyboardWidget->isHidden()) { m_virtualKeyboardWidget->hide(); if (m_virKbButton) { m_virKbButton->setClickedStatus(NORMAL); } allExited = false; } if (batteryWidget && !batteryWidget->isHidden()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(CLICKED); } allExited = false; } if (m_sessionListWidget && !m_sessionListWidget->isHidden()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } allExited = false; } if (hideCenterWidget && m_pResetPwdWidget && !m_pResetPwdWidget->isHidden()) { m_pResetPwdWidget->hide(); m_pResetPwdWidget->switchToConfidentialDig(); m_timeWidget->setVisible(m_isShowDateTime); buttonListWidget->show(); authDialog->inhibitShow(false); if (m_pResetPwdBtn) { m_pResetPwdBtn->show(); } allExited = false; } authDialog->setFocus(); return allExited; } void LockWidget::paintEvent(QPaintEvent *event) { return QWidget::paintEvent(event); } void LockWidget::resizeEvent(QResizeEvent *event) { QSize size = event->size(); if (m_widgetSize == size) { return; } m_widgetSize = size; // 重新计算缩放比例 scale = QString::number(size.width() / 1920.0, 'f', 1).toFloat(); authDialog->setGeometry( (width() - this->geometry().width()) / 2, 148 * scale, this->width(), (height() - 150 * scale - (24 + 48 + 16) * scale)); authDialog->updateAuthSize(); m_timeWidget->updateUI(); m_timeWidget->move((width() - m_timeWidget->width()) / 2, 48 * scale); if (m_userListWidget) { m_userListWidget->setMaxHeight(height() / 2); m_userListWidget->updateWidgetSize(); qDebug() << "m_userListWidget:" << m_userListWidget->geometry(); } if (m_powerManagerButton) { m_powerManagerButton->setIcon(drawSymbolicColoredPixmap( QIcon::fromTheme("system-shutdown-symbolic", QIcon(":/image/assets/shutdown.svg")).pixmap(24, 24), "white")); } if (m_userButton) { m_userButton->setIcon(drawSymbolicColoredPixmap( QIcon::fromTheme("ukui-stock-people-symbolic", QIcon(":/image/assets/switchuser.svg")).pixmap(24, 24), "white")); } if (m_powerListWidget) { if (m_systemMonitorBtn) { m_powerListWidget->setMaxHeight(height() - (80 + m_timeWidget->height() + m_systemMonitorBtn->height())); } else { m_powerListWidget->setMaxHeight(height() - (80 + m_timeWidget->height())); } m_powerListWidget->updateWidgetSize(); if (m_powerListWidget->getScrollShowStatus()) { // 如果显示Scroll,直接从时间窗口底部开始显示 m_powerListWidget->move((width() - m_powerListWidget->width()) / 2, m_timeWidget->height() + 30); } else { // 居中显示 m_powerListWidget->move( (width() - m_powerListWidget->width()) / 2, (height() - m_powerListWidget->height()) / 2); } } updateBottomButton(); buttonListWidget->setFixedSize(QSize((48 + buttonListWidget->spacing() * 2) * buttonListWidget->count(), 64)); m_pBottomWidget->setGeometry(0, height() - buttonListWidget->height() - BOTTOM_MARGIN * scale, this->width(), 64); if (m_pResetPwdBtn) { m_pResetPwdBtn->setGeometry( (this->width() - m_pResetPwdBtn->width()) / 2, height() - m_pResetPwdBtn->height() - BOTTOM_MARGIN * scale + 8, m_pResetPwdBtn->width(), m_pResetPwdBtn->height()); } if (m_pKylinIDCodeTip) { m_pKylinIDCodeTip->move((this->width() - m_pKylinIDCodeTip->width()) / 2, height() - 72); } if (m_pResetPwdWidget) { m_pResetPwdWidget->updateResetPwdSize(); m_pResetPwdWidget->setFixedWidth(width()); m_pResetPwdWidget->setGeometry(this->geometry()); } if (m_sessionListWidget) { m_sessionListWidget->setMaxHeight(height() / 2); m_sessionListWidget->updateWidgetSize(); setSessionListWidgetLocate(); } if (m_networkWidget) { setNetWorkWidgetLocate(); } setBatteryWidgetLocate(); if (m_userListWidget) { setUserListWidgetLocate(); } if (m_systemMonitorBtn) { m_systemMonitorBtn->move( (width() - m_systemMonitorBtn->width()) / 2, height() - m_systemMonitorBtn->height() - 40); } onSetVirkeyboardPos(); } void LockWidget::keyReleaseEvent(QKeyEvent *e) { if (e->key() == Qt::Key_K) { if (e->modifiers() & Qt::ControlModifier) onShowVirtualKeyboard(); } if (e->key() == Qt::Key_Return && (m_systemMonitorBtn && m_systemMonitorBtn->hasFocus())) { doSystemMonitor(); exitSubWidget(); } // 监听按键事件实现快捷键功能 switch (e->key()) { case Qt::Key_VolumeMute: Q_EMIT m_modelLockDialog->usdExternalDoAction(MUTE_KEY); break; case Qt::Key_VolumeDown: Q_EMIT m_modelLockDialog->usdExternalDoAction(VOLUME_DOWN_KEY); break; case Qt::Key_VolumeUp: Q_EMIT m_modelLockDialog->usdExternalDoAction(VOLUME_UP_KEY); break; case Qt::Key_MonBrightnessUp: Q_EMIT m_modelLockDialog->usdExternalDoAction(BRIGHT_UP_KEY); break; case Qt::Key_MonBrightnessDown: Q_EMIT m_modelLockDialog->usdExternalDoAction(BRIGHT_DOWN_KEY); break; case Qt::Key_TouchpadOn: Q_EMIT m_modelLockDialog->usdExternalDoAction(TOUCHPAD_ON_KEY); break; case Qt::Key_TouchpadOff: Q_EMIT m_modelLockDialog->usdExternalDoAction(TOUCHPAD_OFF_KEY); break; default: break; } // 实现锁屏截图功能 QKeySequence keySequence = QKeySequence(e->keyCombination()); if (keySequence == listFromString(m_areaScreenShot)) { Q_EMIT m_modelLockDialog->usdExternalDoAction(AREA_SCREENSHOT_KEY); } else if (keySequence == listFromString(m_areaScreenShot2)) { Q_EMIT m_modelLockDialog->usdExternalDoAction(AREA_SCREENSHOT_KEY); } else if (keySequence == listFromString(m_screenShot)) { Q_EMIT m_modelLockDialog->usdExternalDoAction(SCREENSHOT_KEY); } else if (keySequence == listFromString(m_screenShot2)) { Q_EMIT m_modelLockDialog->usdExternalDoAction(SCREENSHOT_KEY); } else if (keySequence == listFromString(m_windowScreenshot)) { Q_EMIT m_modelLockDialog->usdExternalDoAction(WINDOW_SCREENSHOT_KEY); } if (e->key() == Qt::Key_PowerOff) { if (m_strPowerButtonKey == "interactive") { if (!m_modelLockDialog->isSessionTools()) { m_isManualPower = true; onShowPowerListWidget(); } } else if (m_strPowerButtonKey == "shutdown") { if (!m_modelLockDialog->isSessionTools() && m_modelLockDialog->getLoggedInUsersCount() >= 1 && m_modelLockDialog->getShutdownLockcheck().isEmpty()) { onSureShutDown(SHUTDOWN); } else { m_modelLockDialog->setPowerManager(false, "PowerOff"); } } else if (m_strPowerButtonKey == "hibernate") { m_modelLockDialog->setPowerManager(false, "Hibernate"); } else if (m_strPowerButtonKey == "suspend") { onSuspendClicked("Suspend"); } } QWidget::keyReleaseEvent(e); } void LockWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { refreshTranslate(); } } void LockWidget::initUI() { // 检查并获取第三方认证插件配置信息 if (PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).size() > 0) { LoginAuthInterface *plugin = dynamic_cast( PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).values().first()); QJsonObject rootObj; rootObj["MsgType"] = "GetInformations"; rootObj["Key"] = "Configures"; QString strMsg = plugin->onMessage(QJsonDocument(rootObj).toJson()); onGetCustomPluginMsg(strMsg); } authDialog = new AuthDialog(m_modelLockDialog, m_curUserInfo, this); authDialog->installEventFilter(this); authDialog->hide(); m_curFont = m_modelLockDialog->getCurFont(); m_ptToPx = m_modelLockDialog->getPtToPx(); m_curFontSize = m_modelLockDialog->getCurFontSize(); m_isShowDateTime = m_modelLockDialog->getLockDatetimeEnable(); QLocale local; systemLang = local.name(); initTimeWidget(); initButtonWidget(); initSystemMonitorBtn(); // authDialog->setFocus(); // Q_EMIT m_modelLockDialog->setCurrentUser(m_modelLockDialog->defaultUserName()); } void LockWidget::initConnections() { connect(UniAuthService::instance(), &UniAuthService::userChanged, this, &LockWidget::onServiceUserChanged); connect(m_modelLockDialog, &LockDialogModel::usersInfoChanged, this, &LockWidget::onUsersInfoChanged); connect(m_modelLockDialog, &LockDialogModel::currentUserChanged, this, &LockWidget::onCurUserChanged); connect(m_modelLockDialog, &LockDialogModel::currentSessionChanged, this, &LockWidget::onSessionChanged); connect(m_modelLockDialog, &LockDialogModel::sessionActiveChanged, this, &LockWidget::onSessionActiveChanged); connect(m_modelLockDialog, &LockDialogModel::tabletModeChanged, this, &LockWidget::onTabletModeChanged); connect(m_modelLockDialog, &LockDialogModel::fontChanged, this, &LockWidget::onFontChanged); connect(m_modelLockDialog, &LockDialogModel::fontSizeChanged, this, &LockWidget::onFontSizeChanged); connect(m_modelLockDialog, &LockDialogModel::upowerServiceReady, this, &LockWidget::onUPowerServiceReady); connect( m_modelLockDialog, &LockDialogModel::lockDatetimeEnableChanged, this, &LockWidget::onLockDatetimeEnableChanged); if (authDialog) { connect(authDialog, SIGNAL(authSucceed(QString)), this, SIGNAL(authSucceed(QString))); connect(authDialog, &AuthDialog::authSucceed, this, [=] { if (m_isShowResetPasswdBtn && m_pResetPwdBtn) { m_pResetPwdBtn->hide(); m_isShowResetPasswdBtn = false; } }); connect(authDialog, &AuthDialog::userChangedByManual, this, &LockWidget::onUserChangedByManual); connect(authDialog, &AuthDialog::customRequestAccount, this, &LockWidget::onCustomRequestAccount); connect(authDialog, &AuthDialog::lineEditClicked, this, [=] { onLineEditClicked(); }); connect(authDialog, &AuthDialog::lineEditMousePressed, this, [=] { onLineEditMousePressed(); }); connect(authDialog, &AuthDialog::showResetPasswdBtn, this, &LockWidget::onShowResetPasswdBtn); } connect(LanguageSetting::instance(this), &LanguageSetting::languageChanged, this, &LockWidget::onLanguageChanged); } void LockWidget::initTimeWidget() { m_timeWidget = new TimeWidget(m_modelLockDialog, this); KDK_EXTEND_ALL_INFO_FORMAT(m_timeWidget, "TimeWidget", "initialWidget", "add time widget of tools"); m_timeWidget->move((width() - m_timeWidget->width()) / 2, 0); m_timeWidget->setVisible(m_isShowDateTime); } void LockWidget::initUserWidget() { if (!m_userListWidget) { m_userListWidget = new UserListWidget(m_modelLockDialog, this); m_userListWidget->installEventFilter(this); m_userListWidget->setMaxHeight(height() / 2); m_userListWidget->updateWidgetSize(); m_userListWidget->hide(); connect(m_userListWidget, &UserListWidget::userSelected, m_modelLockDialog, [=](QString strUserName) { exitSubWidget(false, false); SwitchToUser(strUserName); }); connect(m_userListWidget, &UserListWidget::widgetSizeChanged, m_modelLockDialog, [=]() { setUserListWidgetLocate(); }); m_userListWidget->clearFocus(); setUserListWidgetLocate(); } } void LockWidget::initSessionWidget() { if (!m_sessionListWidget) { m_sessionListWidget = new SessionListWidget(m_modelLockDialog, this); m_sessionListWidget->installEventFilter(this); m_sessionListWidget->setMaxHeight(height() / 2); m_sessionListWidget->updateWidgetSize(); m_sessionListWidget->hide(); connect( m_sessionListWidget, &SessionListWidget::sessionSelected, m_modelLockDialog, &LockDialogModel::setCurrentSession); m_sessionListWidget->clearFocus(); setSessionListWidgetLocate(); } } void LockWidget::initPowerWidget() { if (m_powerListWidget) { if (m_systemMonitorBtn) { m_powerListWidget->setMaxHeight(height() - (80 + m_timeWidget->height() + m_systemMonitorBtn->height())); } else { m_powerListWidget->setMaxHeight(height() - (80 + m_timeWidget->height())); } m_powerListWidget->updateWidgetSize(); if (m_powerListWidget->getScrollShowStatus()) { // 如果显示Scroll,直接从时间窗口底部开始显示 m_powerListWidget->move((width() - m_powerListWidget->width()) / 2, m_timeWidget->height() + 30); } else { // 居中显示 m_powerListWidget->move( (width() - m_powerListWidget->width()) / 2, (height() - m_powerListWidget->height()) / 2); } return; } m_powerListWidget = new PowerListWidget(m_modelLockDialog, this); if (m_systemMonitorBtn) { m_powerListWidget->setMaxHeight(height() - (80 + m_timeWidget->height() + m_systemMonitorBtn->height())); } else { m_powerListWidget->setMaxHeight(height() - (80 + m_timeWidget->height())); } m_powerListWidget->hide(); connect(m_powerListWidget, &MyListWidget::currentItemChanged, this, &LockWidget::onPowerItemChanged); connect(m_powerListWidget, &PowerListWidget::showInhibitWarning, this, &LockWidget::onShowInhibitWarning); connect(m_powerListWidget, &PowerListWidget::mulUsersLogined, this, &LockWidget::onMulUsersLogined); connect(m_powerListWidget, &PowerListWidget::sureShutDown, this, &LockWidget::onSureShutDown); connect(m_powerListWidget, &PowerListWidget::lockScreenClicked, this, &LockWidget::onLockScreenClicked); connect(m_powerListWidget, &PowerListWidget::suspendClicked, this, &LockWidget::onSuspendClicked); connect(m_powerListWidget, &PowerListWidget::itemClicked, this, &LockWidget::onPowerItemClicked); connect(m_powerListWidget, &PowerListWidget::switchuserClicked, this, &LockWidget::onSwitchuserClicked); connect(m_powerListWidget, &PowerListWidget::powerWidgetClicked, this, [=] { Q_EMIT sessionToolsExit(); }); connect(m_powerListWidget, &PowerListWidget::doPowerAction, this, &LockWidget::onDoPowerAction); m_powerListWidget->updateWidgetSize(); if (m_powerListWidget->getScrollShowStatus()) { // 如果显示Scroll,直接从时间窗口底部开始显示 m_powerListWidget->move((width() - m_powerListWidget->width()) / 2, m_timeWidget->height() + 30); } else { // 居中显示 m_powerListWidget->move( (width() - m_powerListWidget->width()) / 2, (height() - m_powerListWidget->height()) / 2); } } void LockWidget::initButtonWidget() { #ifdef USDFIXED initUsdMediaStateKeys(); #endif m_pBottomWidget = new QWidget(this); QHBoxLayout *bottomWidgetLayout = new QHBoxLayout(m_pBottomWidget); bottomWidgetLayout->setContentsMargins(RIGHT_MARGIN * scale - 16, 0, RIGHT_MARGIN * scale - 16, 0); buttonListWidget = new MyListWidget(m_pBottomWidget); buttonListWidget->installEventFilter(this); buttonListWidget->setFlow(QListWidget::LeftToRight); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { buttonListWidget->setLayoutDirection(Qt::LeftToRight); } else { buttonListWidget->setLayoutDirection(Qt::RightToLeft); } buttonListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); buttonListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QString buttonListStyle = "QListWidget{ background:rgba(255, 255, 255, 0%); border: 0px;}" "QListWidget::item{border-radius: 4px; background:rgba(255, 255, 255, 0%);}"; buttonListWidget->setStyleSheet(buttonListStyle); buttonListWidget->setSpacing(8); // 电源管理 m_btnItemPowerManager = new QListWidgetItem(); m_btnItemPowerManager->setSizeHint(QSize(48, 48)); buttonListWidget->insertItem(buttonListWidget->count(), m_btnItemPowerManager); buttonListWidget->addItem(m_btnItemPowerManager); // 虚拟键盘 m_btnItemVirKb = new QListWidgetItem(); m_btnItemVirKb->setSizeHint(QSize(48, 48)); buttonListWidget->insertItem(buttonListWidget->count(), m_btnItemVirKb); buttonListWidget->addItem(m_btnItemVirKb); #if SUPPORT_NET_ICON // 网络设置 syslog(LOG_INFO, "[ukui-screensaver-dialog] init net icon begin!!"); m_btnItemNetwork = new QListWidgetItem(); m_btnItemNetwork->setSizeHint(QSize(48, 48)); buttonListWidget->insertItem(buttonListWidget->count(), m_btnItemNetwork); buttonListWidget->addItem(m_btnItemNetwork); syslog(LOG_INFO, "[ukui-screensaver-dialog] init net icon end!!"); #endif /* connect(buttonWidget, &KyNetworkIcon::updateIconHidden ,this, [buttonWidget,this](bool status){ if (status) { buttonWidget->hide(); } else { buttonWidget->show(); } }); if (buttonWidget->checkIconIsHidden()) { buttonWidget->hide(); } */ #ifdef USDFIXED if (m_rfkillState != -1) { m_pRfkillStatusButton = new StatusButton(this, BOTBUTTON); QListWidgetItem *rekillButtonItem = new QListWidgetItem(); rekillButtonItem->setSizeHint(QSize(48, 48)); buttonListWidget->insertItem(buttonListWidget->count(), rekillButtonItem); buttonListWidget->setItemWidget(rekillButtonItem, m_pRfkillStatusButton); setRfkillBtnIcon(); buttonListWidget->addItem(rekillButtonItem); connect(m_pRfkillStatusButton, &StatusButton::clicked, this, [this]() { setRfkillState(); // setRfkillBtnIcon(); }); } #endif // 用户选择 m_btnItemUser = new QListWidgetItem(); m_btnItemUser->setSizeHint(QSize(48, 48)); buttonListWidget->insertItem(buttonListWidget->count(), m_btnItemUser); buttonListWidget->addItem(m_btnItemUser); // 电池信息 m_btnItemBattery = new QListWidgetItem(); m_btnItemBattery->setSizeHint(QSize(48, 48)); buttonListWidget->insertItem(buttonListWidget->count(), m_btnItemBattery); buttonListWidget->addItem(m_btnItemBattery); // 会话选择 m_btnItemSession = new QListWidgetItem(); m_btnItemSession->setSizeHint(QSize(48, 48)); buttonListWidget->insertItem(buttonListWidget->count(), m_btnItemSession); buttonListWidget->addItem(m_btnItemSession); buttonListWidget->setFixedSize(QSize((48 + buttonListWidget->spacing() * 2) * buttonListWidget->count(), 64)); bottomWidgetLayout->addStretch(); bottomWidgetLayout->addWidget(buttonListWidget); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { if (m_pBottomWidget) m_pBottomWidget->setLayoutDirection(Qt::RightToLeft); } else { if (m_pBottomWidget) m_pBottomWidget->setLayoutDirection(Qt::LeftToRight); } connect(buttonListWidget, &MyListWidget::currentItemChanged, this, &LockWidget::onButtonItemChanged); connect(buttonListWidget, &MyListWidget::itemClicked, this, &LockWidget::onButtonItemClicked); } void LockWidget::initBlockWidget() { if (!m_blockWidget) { m_blockWidget = new BlockWidget(m_modelLockDialog, this); m_blockWidget->setGeometry(this->geometry()); m_blockWidget->hide(); connect(m_blockWidget, SIGNAL(cancelButtonclicked()), this, SLOT(onCancelBtnClicked())); connect(m_blockWidget, SIGNAL(confirmButtonclicked()), this, SLOT(onConfirmBtnClicked())); } } void LockWidget::initSystemMonitorBtn() { if (!m_systemMonitorBtn) { m_systemMonitorBtn = new StatusButton(this, NORMALBTN); KDK_EXTEND_ALL_INFO_FORMAT(m_systemMonitorBtn, "StatusButton", "initialBtn", "add system-monitor button of tools"); QFont sysBtnFont; sysBtnFont.setFamily(m_curFont); sysBtnFont.setPointSize((16 + m_curFontSize) * m_ptToPx); m_systemMonitorBtn->setFont(sysBtnFont); m_systemMonitorBtn->hide(); m_systemMonitorBtn->setFixedHeight(48); m_systemMonitorBtn->setText(tr("system-monitor")); m_systemMonitorBtn->setContentsMargins(5, 0, 5, 0); m_systemMonitorBtn->adjustSize(); connect(m_systemMonitorBtn, &StatusButton::clicked, this, &LockWidget::doSystemMonitor); } if (m_systemMonitorBtn) { m_systemMonitorBtn->move( (width() - m_systemMonitorBtn->width()) / 2, height() - m_systemMonitorBtn->height() - 40); } } void LockWidget::initUsdMediaKeys() { m_areaScreenShot = m_modelLockDialog->getUsdAreaScreenShotKey(); m_areaScreenShot2 = m_modelLockDialog->getUsdAreaScreenShot2Key(); m_screenShot = m_modelLockDialog->getUsdScreenShotKey(); m_screenShot2 = m_modelLockDialog->getUsdScreenShot2Key(); m_windowScreenshot = m_modelLockDialog->getUsdwindowScreenshotKey(); connect(m_modelLockDialog, &LockDialogModel::usdMediaKeysChanged, this, &LockWidget::onUsdMediaKeysChanged); } void LockWidget::initUsdMediaStateKeys() { m_rfkillState = m_modelLockDialog->getUsdMediaRfkillState(); connect( m_modelLockDialog, &LockDialogModel::usdMediaStateKeysChanged, this, &LockWidget::onUsdMediaStateKeysChanged); } void LockWidget::initPowerManagerKey() { m_strPowerButtonKey = m_modelLockDialog->getPowerButtonKey(); connect(m_modelLockDialog, &LockDialogModel::powerButoonKeyChanged, this, [=](const QString &value) { m_strPowerButtonKey = value; }); } const QKeySequence LockWidget::listFromString(QString keys) { keys.replace("<", ""); keys.replace(">", "+"); keys.replace("Win", "Meta", Qt::CaseInsensitive); keys.replace("Start", "Meta", Qt::CaseInsensitive); keys.replace("PrtSc", "Print", Qt::CaseInsensitive); return QKeySequence(keys); } void LockWidget::updateBottomButton() { QList statusButton = this->findChildren(); for (auto button : statusButton) { if (button->getButtonType() == POWERBUTTON) { button->setFixedSize(130, 130); button->setIconSize(QSize(130, 130)); } else if (button->getButtonType() == PWDTIPBTN) { button->setFixedSize(40, 40); button->setIconSize(QSize(24, 24)); } else if (button->getButtonType() != NORMALBTN) { button->setFixedSize(48, 48); button->setIconSize(QSize(24, 24)); } } #if SUPPORT_NET_ICON QList networkButton = this->findChildren(); for (auto button : networkButton) { button->setFixedSize(48, 48); button->setIconSize(QSize(24, 24)); } #endif } void LockWidget::onPowerItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { if (!m_powerListWidget) { return; } QWidget *currentWidget = m_powerListWidget->itemWidget(current); PowerItemWidget *currentItem = dynamic_cast(currentWidget); if (currentItem) { currentItem->setSelected(SELECT); } if (previous) { QWidget *previousWidget = m_powerListWidget->itemWidget(previous); PowerItemWidget *previousItem = dynamic_cast(previousWidget); if (previousItem) { previousItem->setSelected(NORMAL); } } } void LockWidget::onButtonItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QWidget *currentWidget = buttonListWidget->itemWidget(current); StatusButton *currentItem = qobject_cast(currentWidget); #if SUPPORT_NET_ICON NetWorkButton *netItem = qobject_cast(currentWidget); #endif // SUPPORT_NET_ICON if (currentItem) { currentItem->setClickedStatus(SELECT); } #if SUPPORT_NET_ICON if (netItem) { netItem->setClickedStatus(SELECT); } #endif // SUPPORT_NET_ICON if (previous) { QWidget *previousWidget = buttonListWidget->itemWidget(previous); StatusButton *previousItem = qobject_cast(previousWidget); #if SUPPORT_NET_ICON NetWorkButton *previousNetItem = qobject_cast(previousWidget); #endif // SUPPORT_NET_ICON if (previousItem) { previousItem->setClickedStatus(NORMAL); } #if SUPPORT_NET_ICON if (previousNetItem) { previousNetItem->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON } } void LockWidget::onButtonItemClicked(QListWidgetItem *item) { if (buttonListWidget->isHidden()) { return; } QWidget *widget = buttonListWidget->itemWidget(item); StatusButton *currentItem = dynamic_cast(widget); if (currentItem && currentItem->isVisible()) { currentItem->setClickedStatus(CLICKED); currentItem->clicked(); } if (currentItem == m_powerManagerButton) { for (int i = 0; i < buttonListWidget->count(); i++) { if (!buttonListWidget->isRowHidden(i)) { buttonListWidget->setCurrentRow(i, QItemSelectionModel::SelectionFlag::Clear); buttonListWidget->currentItemChanged(buttonListWidget->item(i), buttonListWidget->item(i)); break; } } } #if SUPPORT_NET_ICON NetWorkButton *netItem = dynamic_cast(widget); if (netItem) { netItem->setClickedStatus(CLICKED); netItem->clicked(); } #endif // SUPPORT_NET_ICON } void LockWidget::onPowerItemClicked(QListWidgetItem *item) { if (!m_powerListWidget) { return; } QWidget *widget = m_powerListWidget->itemWidget(item); PowerItemWidget *currentItem = dynamic_cast(widget); if (currentItem) { currentItem->setSelected(CLICKED); currentItem->clicked(); } if (m_modelLockDialog->isSessionTools()) { if (m_systemMonitorBtn && m_systemMonitorBtn->isVisible()) { m_systemMonitorBtn->hide(); } } /* else if (m_blockWidget->isHidden()) { exitSubWidget(); }*/ } void LockWidget::onUsersInfoChanged() { if (buttonListWidget && m_btnItemUser) { if (m_modelLockDialog->normalUserCount() > 1 || m_modelLockDialog->findUserByName("*login")) { buttonListWidget->setItemHidden(m_btnItemUser, false); } else { buttonListWidget->setItemHidden(m_btnItemUser, true); if (m_userListWidget && !m_userListWidget->isHidden()) m_userListWidget->hide(); } if (m_userListWidget) { setUserListWidgetLocate(); } } UserInfoPtr ptrUser = m_modelLockDialog->findUserByName(m_curUserInfo->name()); if (ptrUser) { m_curUserInfo = ptrUser; if (buttonListWidget && m_btnItemSession) { if (m_curUserInfo->isLoggedIn() || m_modelLockDialog->sessionsInfo().count() <= 1) { buttonListWidget->setItemHidden(m_btnItemSession, true); if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); } } else { buttonListWidget->setItemHidden(m_btnItemSession, false); } } if (authDialog) { authDialog->onCurUserInfoChanged(m_curUserInfo); } } } void LockWidget::onCurUserChanged(const QString &strUserName) { UserInfoPtr ptrUser = m_modelLockDialog->findUserByName(strUserName); if (ptrUser) { bool isUserChanged = (m_curUserInfo->name() != ptrUser->name() || m_curUserInfo->uid() != ptrUser->uid()); m_curUserInfo = ptrUser; qDebug() << "CurUserInfo:" << *m_curUserInfo; if (m_curUserInfo->name() != "*login" && m_curUserInfo->name() != "*guest") { LanguageSetting::instance()->onLanguageChanged(m_curUserInfo->lang()); } if (authDialog && !m_modelLockDialog->isSessionTools()) { authDialog->onCurUserChanged(m_curUserInfo); if (isUserChanged) { if (authDialog->getUserAuthFailedTimes() >= 1) { onShowResetPasswdBtn(); } else if (m_pResetPwdBtn) { m_pResetPwdBtn->hide(); m_isShowResetPasswdBtn = false; } if (m_pResetPwdWidget && m_pResetPwdWidget->isVisible()) { m_pResetPwdWidget->switchToConfidentialDig(); m_pResetPwdWidget->hide(); if (m_isShowDateTime) { m_timeWidget->show(); } buttonListWidget->show(); authDialog->inhibitShow(false); } } } m_curFontSize = getUserFontSize(strUserName); updateFontSize(); QVariant curUserThemeColor = getUserThemeColor(strUserName); Q_EMIT m_modelLockDialog->setUserThemeColor("themeColor", curUserThemeColor); // 登录后绘制桌面背景而不是登录背景 QString backgroundPath = m_curUserInfo->backGround(); QFile backgroundPathFile(backgroundPath); if (backgroundPath.isEmpty() || !backgroundPathFile.exists()) { backgroundPath = Configuration::instance(this)->getDefaultBackgroundName(); } setRootWindowBgOptions(Configuration::instance(this)->getRootBackgroundOption(strUserName)); setrootWindowBackground(rootWinPicture, 0, backgroundPath); QString drawBackgroundColor = m_curUserInfo->backGroundColor(); if (drawBackgroundColor.length() == 7 && drawBackgroundColor.startsWith("#")) { drawBackgroundColor = drawBackgroundColor.remove(0, 1); bool ok; int color = drawBackgroundColor.toUInt(&ok, 16); setrootWindowBackground(rootWinColor, color, NULL); } reloadRootBackground(); } if (buttonListWidget && m_btnItemSession) { if (m_curUserInfo->isLoggedIn() || m_modelLockDialog->sessionsInfo().count() <= 1) { buttonListWidget->setItemHidden(m_btnItemSession, true); if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); } } else { buttonListWidget->setItemHidden(m_btnItemSession, false); } } } void LockWidget::onUserChangedByManual(const QString &userName) { if (m_modelLockDialog && m_modelLockDialog->findUserByName(userName)) { if (isCurUserSelf(userName)) { Q_EMIT m_modelLockDialog->setCurrentUser(userName); } else { SwitchToUser(userName); } } else { } } void LockWidget::onSessionChanged(const QString &strSession) { if (buttonListWidget) { for (int n = 0; n < buttonListWidget->count(); n++) { QListWidgetItem *item = buttonListWidget->item(n); QWidget *widget = buttonListWidget->itemWidget(item); if (widget && widget->objectName() == "BtnSession") { StatusButton *sButton = qobject_cast(widget); if (sButton) { QString strCurSession = strSession; QString sessionPrefix = strCurSession.left(strCurSession.indexOf('-')); if (strCurSession == "ukui-wayland") sessionPrefix = "ukui_wayland"; if (strCurSession == "kylin-wlcom") sessionPrefix = "kylin_wlcom"; strCurSession = IMAGE_DIR + QString("badges/%1_badge.svg").arg(sessionPrefix.toLower()); QFile iconFile(strCurSession); if (!iconFile.exists()) { strCurSession = IMAGE_DIR + QString("badges/unknown_badge.svg"); } QPixmap ssIcon(strCurSession); ssIcon = scaledPixmap(ssIcon); ssIcon = ssIcon.scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); ssIcon = PixmapToRound(ssIcon, 24); sButton->setIcon(ssIcon); } } } } } void LockWidget::onUsdMediaKeysChanged(const QString &keys, const QString &value) { if (keys == KEY_AREA_SCREENSHOT) { m_areaScreenShot = value; } else if (keys == KEY_AREA_SCREENSHOT2) { m_areaScreenShot2 = value; } else if (keys == KEY_SCREEN_SHOT) { m_screenShot = value; } else if (keys == KEY_SCREEN_SHOT2) { m_screenShot2 = value; } else if (keys == KEY_WINDOW_SCREENSHOT) { m_windowScreenshot = value; } } void LockWidget::onUsdMediaStateKeysChanged(const QString &keys, const int &value) { if (keys == KEY_RFKILL_STATE) { m_rfkillState = value; setRfkillBtnIcon(); } } void LockWidget::onShowSessionListWidget() { initSessionWidget(); if (m_sessionListWidget && m_sessionListWidget->isHidden()) { m_sessionListWidget->show(); m_sessionListWidget->setFocus(); if (m_sessionButton) { m_sessionButton->setClickedStatus(CLICKED); } if (m_networkWidget && m_networkWidget->isVisible()) { m_networkWidget->hide(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON } if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } } else { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } } void LockWidget::onShowBatteryWidget() { if (!batteryWidget) { batteryWidget = new BatteryWidget(m_modelLockDialog, this); batteryWidget->setFixedSize(420, 112); setBatteryWidgetLocate(); } if (batteryWidget && batteryWidget->isHidden()) { batteryWidget->show(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(CLICKED); } if (m_networkWidget && m_networkWidget->isVisible()) { m_networkWidget->hide(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } } else { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } } void LockWidget::onShowUserListWidget(bool show) { initUserWidget(); if (m_userListWidget && m_userListWidget->isHidden() && m_userButton && m_userButton->isVisible()) { m_userListWidget->show(); if (m_userButton) { m_userButton->setClickedStatus(CLICKED); } if (m_networkWidget && m_networkWidget->isVisible()) { m_networkWidget->hide(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON } if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } m_userListWidget->onUpdateListInfo(); m_userListWidget->setFocus(); } else if (show && m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->onUpdateListInfo(); m_userListWidget->setFocus(); } else { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } } void LockWidget::onShowNetworkWidget() { if (!m_networkWidget) { m_networkWidget = new MyNetworkWidget(this); m_networkWidget->installEventFilter(this); connect(m_networkWidget, &MyNetworkWidget::showVirtualKeyboard, this, [=] { if (m_isTabletMode) { onShowVirtualKeyboard(m_isTabletMode); } }); } m_networkWidget->loadNetPlugin(); setNetWorkWidgetLocate(); if (m_networkWidget && m_networkWidget->isVisible()) { m_networkWidget->hide(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON } else { m_networkWidget->show(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(CLICKED); } #endif // SUPPORT_NET_ICON if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } } } void LockWidget::onShowResetPasswdBtn() { if (!m_pSecurityQuestionAnswer) { m_pSecurityQuestionAnswer = new SecurityQuestionAnswer(this); } bool hasSecurityQustion = m_pSecurityQuestionAnswer->IsUserHasSecurityAnswers(authDialog->getCurAuthUserUid()); bool hasBindKylinID = isUserBindKylinID(); qDebug() << " hasBindKylinID = " << hasBindKylinID << authDialog->getCurAuthUserUid(); if (hasSecurityQustion && hasBindKylinID) { m_nUserHasBindIdentity = ALLDEFINED; } else if (hasSecurityQustion) { m_nUserHasBindIdentity = SECURITYQUESTION; } else if (hasBindKylinID) { m_nUserHasBindIdentity = KYLINID; } else { m_nUserHasBindIdentity = NOTHINGBIND; } qDebug() << " IsUserHasSecurityAnswers = " << m_nUserHasBindIdentity; if (m_nUserHasBindIdentity > 0) { if (!m_pResetPwdBtn) { m_pResetPwdBtn = new QPushButton(this); m_pResetPwdBtn->setFocusPolicy(Qt::NoFocus); m_pResetPwdBtn->installEventFilter(this); m_pResetPwdBtn->setFixedHeight(48); m_pResetPwdBtn->setMinimumWidth(120); m_pResetPwdBtn->setText(tr("Reset Pwd")); m_pResetPwdBtn->adjustSize(); m_pResetPwdBtn->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0);border-radius: 24px;color: " "white;padding-left: 8px; padding-right: 8px;}" "QPushButton:hover{background: rgba(255, 255, 255, 0.15);border-radius: " "24px;padding-left: 8px; padding-right: 8px;}" "QPushButton:pressed {background: rgba(255, 255, 255, 0.4);border-radius: " "24px;padding-left: 8px; padding-right: 8px;}"); connect(m_pResetPwdBtn, &QPushButton::clicked, this, &LockWidget::onShowResetPwdWidget); m_pResetPwdBtn->setGeometry( (this->width() - m_pResetPwdBtn->width()) / 2, height() - m_pResetPwdBtn->height() - BOTTOM_MARGIN * scale - 8, m_pResetPwdBtn->width(), m_pResetPwdBtn->height()); } m_pResetPwdBtn->show(); m_isShowResetPasswdBtn = true; } } bool LockWidget::isUserBindKylinID() { QDBusInterface interface( "org.kylinID.service", "/org/kylinID/path", "org.kylinID.interface", QDBusConnection::systemBus()); QDBusMessage result = interface.call("IsUserBindKyidAccounts", authDialog->getCurAuthUserUid()); QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } void LockWidget::onShowResetPwdWidget() { if (!m_pResetPwdWidget) { if (!m_pSecurityQuestionAnswer) { m_pSecurityQuestionAnswer = new SecurityQuestionAnswer(this); } m_pResetPwdWidget = new ResetPwdWidget( m_modelLockDialog, authDialog->getCurAuthRealName(), authDialog->getCurAuthUserUid(), m_pSecurityQuestionAnswer, this); Q_EMIT startFcitx(); connect(m_pResetPwdWidget, &ResetPwdWidget::backToAuthDialog, this, &LockWidget::onBackToAuthDialog); connect(m_pResetPwdWidget, &ResetPwdWidget::resetEditClicked, this, [=]() { if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } if (m_networkWidget && m_networkWidget->isVisible()) { m_networkWidget->hide(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON } }); connect(m_pResetPwdWidget, &ResetPwdWidget::getCodeByKyidAccountsed, this, &LockWidget::onShowKylinIDCodesTips); connect(m_pResetPwdWidget, &ResetPwdWidget::hideKylinIDCodesTips, this, [=]() { if (authDialog) { authDialog->hidePwdTipBtn(); } }); m_pResetPwdWidget->setFixedWidth(width()); m_pResetPwdWidget->updateResetPwdSize(); m_pResetPwdWidget->setGeometry(this->geometry()); m_pResetPwdWidget->hide(); } m_pResetPwdWidget->setBindIdentity(m_nUserHasBindIdentity); m_pResetPwdWidget->updateFontSize(m_curFontSize); m_pResetPwdWidget->show(); m_pResetPwdWidget->setFocusToEdit(); m_pResetPwdWidget->raise(); if (m_pResetPwdBtn) { m_pResetPwdBtn->hide(); } buttonListWidget->hide(); m_timeWidget->hide(); authDialog->inhibitShow(true); } void LockWidget::onBackToAuthDialog(bool isSuccess) { if (m_isShowDateTime) { m_timeWidget->show(); } buttonListWidget->show(); authDialog->inhibitShow(false); authDialog->setFocus(); if (m_pResetPwdWidget) { m_pResetPwdWidget->hide(); } if (m_pResetPwdBtn) { m_pResetPwdBtn->show(); } if (m_pKylinIDCodeTip) { m_pKylinIDCodeTip->hide(); } if (isSuccess) authDialog->clearMessage(); } void LockWidget::onShowKylinIDCodesTips(QString message) { if (!m_pKylinIDCodeTip) { m_pKylinIDCodeTip = new TipLabel(this); } m_pKylinIDCodeTip->setText(message); QTimer::singleShot(0, this, [&, this]() { // 不加延时计算的m_pKylinIDCodeTip长度会有问题 m_pKylinIDCodeTip->move((this->width() - m_pKylinIDCodeTip->width()) / 2, height() - 72); }); m_pKylinIDCodeTip->show(); } void LockWidget::onLineEditClicked() { if (!m_virtualKeyboardWidget) { m_virtualKeyboardWidget = new VirtualKeyboardWidget(this); m_virtualKeyboardWidget->installEventFilter(this); m_virtualKeyboardWidget->hide(); connect(m_virtualKeyboardWidget, &VirtualKeyboardWidget::aboutToClose, this, &LockWidget::onHideVirkeyboard); connect( m_virtualKeyboardWidget, &VirtualKeyboardWidget::aboutToFloat, this, &LockWidget::onNetWorkResetLocation); } if (m_isTabletMode) { if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } m_virtualKeyboardWidget->show(); } } void LockWidget::onLineEditMousePressed() { exitSubWidget(false, false); } void LockWidget::onShowVirtualKeyboard() { if (!m_virtualKeyboardWidget) { m_virtualKeyboardWidget = new VirtualKeyboardWidget(this); m_virtualKeyboardWidget->installEventFilter(this); m_virtualKeyboardWidget->hide(); connect(m_virtualKeyboardWidget, &VirtualKeyboardWidget::aboutToClose, this, &LockWidget::onHideVirkeyboard); connect( m_virtualKeyboardWidget, &VirtualKeyboardWidget::aboutToFloat, this, &LockWidget::onNetWorkResetLocation); } if (m_virtualKeyboardWidget->isHidden()) { if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } m_virtualKeyboardWidget->show(); } else { m_virtualKeyboardWidget->hide(); } if (!m_virtualKeyboardWidget->isHidden()) { setNetWorkWidgetLocate(); m_virtualKeyboardWidget->raise(); } if (m_virKbButton) { m_virKbButton->setClickedStatus(NORMAL); } } void LockWidget::onShowVirtualKeyboard(bool tabletMode) { m_isTabletMode = tabletMode; if (!m_virtualKeyboardWidget) { m_virtualKeyboardWidget = new VirtualKeyboardWidget(this); m_virtualKeyboardWidget->installEventFilter(this); m_virtualKeyboardWidget->hide(); connect(m_virtualKeyboardWidget, &VirtualKeyboardWidget::aboutToClose, this, &LockWidget::onHideVirkeyboard); connect( m_virtualKeyboardWidget, &VirtualKeyboardWidget::aboutToFloat, this, &LockWidget::onNetWorkResetLocation); } if (tabletMode) { if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } m_virtualKeyboardWidget->show(); } else { m_virtualKeyboardWidget->hide(); } if (!m_virtualKeyboardWidget->isHidden()) { setNetWorkWidgetLocate(); m_virtualKeyboardWidget->raise(); } if (m_virKbButton) { m_virKbButton->setClickedStatus(NORMAL); } } void LockWidget::onShowPowerListWidget() { if (!m_powerListWidget) { initPowerWidget(); } if (m_modelLockDialog->isSessionTools()) { Q_EMIT moveToCurrentScreen(); m_powerListWidget->setPowerType(SESSION); } else { m_powerListWidget->setPowerType(SAVER); } if (m_powerListWidget->isHidden()) { if (m_isShowResetPasswdBtn) { m_pResetPwdBtn->hide(); } if (m_pResetPwdWidget && m_pResetPwdWidget->isVisible()) { m_pResetPwdWidget->switchToConfidentialDig(); m_pResetPwdWidget->hide(); } if (m_modelLockDialog->isSessionTools()) { m_timeWidget->show(); } else { m_timeWidget->setVisible(m_isShowDateTime); } buttonListWidget->show(); authDialog->inhibitShow(true); if (m_powerListWidget->getScrollShowStatus()) { // 如果显示Scroll,直接从时间窗口底部开始显示 m_powerListWidget->move((width() - m_powerListWidget->width()) / 2, m_timeWidget->height() + 30); } else { // 居中显示 m_powerListWidget->move( (width() - m_powerListWidget->width()) / 2, (height() - m_powerListWidget->height()) / 2); } if (m_modelLockDialog->isSessionTools()) { initSystemMonitorBtn(); if (m_systemMonitorBtn) { m_systemMonitorBtn->show(); } buttonListWidget->hide(); } m_powerListWidget->show(); m_powerListWidget->setFocus(); } else { if (m_powerListWidget) { m_powerListWidget->hide(); } authDialog->inhibitShow(false); // bug#292889 头像加载缓慢问题,需要调用一次update或repaint来刷新 authDialog->update(); authDialog->setFocus(); if (m_isShowResetPasswdBtn) { m_pResetPwdBtn->show(); } if (m_systemMonitorBtn) { m_systemMonitorBtn->hide(); } buttonListWidget->show(); } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } if (m_virtualKeyboardWidget && m_virtualKeyboardWidget->isVisible()) { m_virtualKeyboardWidget->hide(); if (m_virKbButton) { m_virKbButton->setClickedStatus(NORMAL); } } if (batteryWidget && batteryWidget->isVisible()) { batteryWidget->hide(); if (m_batteryonButton) { m_batteryonButton->setClickedStatus(NORMAL); } } if (m_networkWidget && m_networkWidget->isVisible()) { m_networkWidget->hide(); #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setClickedStatus(NORMAL); } #endif // SUPPORT_NET_ICON } if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); if (m_sessionButton) { m_sessionButton->setClickedStatus(NORMAL); } } if (m_powerManagerButton) { m_powerManagerButton->setClickedStatus(NORMAL); } } void LockWidget::doSystemMonitor() { qDebug() << "doSystemMonitor"; QProcess::startDetached("ukui-system-monitor", QStringList()); } void LockWidget::onSetVirkeyboardPos() { if (m_virtualKeyboardWidget) { m_virtualKeyboardWidget->adjustGeometry(); } } void LockWidget::onNetWorkResetLocation() { if (m_networkWidget && m_networkWidget->isVisible()) { setNetWorkWidgetLocate(); } if (m_virtualKeyboardWidget && m_virtualKeyboardWidget->isVisible()) { m_virtualKeyboardWidget->raise(); } } void LockWidget::onHideVirkeyboard() { if (m_virtualKeyboardWidget && m_virtualKeyboardWidget->isVisible()) { m_virtualKeyboardWidget->hide(); } setNetWorkWidgetLocate(); } void LockWidget::onShowInhibitWarning(QStringList list, int type) { if (type == 0 || type == 1 || type == 4) { m_isHardInhibit = true; } else if (type == 5 || type == 6 || type == 7) { m_isHardInhibit = false; } if (type == 5) { m_inhibitType = "RebootSoftBlock"; } else if (type == 6) { m_inhibitType = "PoweroffSoftBlock"; } else if (type == 7) { m_inhibitType = "Logout"; } if (!m_isManualPower) { m_modelLockDialog->setIsSessionTools(true); } authDialog->inhibitShow(true); buttonListWidget->hide(); m_timeWidget->hide(); if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } if (m_powerListWidget) { m_powerListWidget->hide(); } initBlockWidget(); if (m_blockWidget) { m_blockWidget->setGeometry(this->geometry()); m_blockWidget->setConfirmButton(m_isHardInhibit); m_blockWidget->setWarning(list, type); m_blockWidget->show(); m_blockWidget->setFocus(); } m_isInhibitStatus = true; } void LockWidget::onSureShutDown(int inhibitType) { // if (!m_isManualPower) { m_modelLockDialog->setIsSessionTools(true); } authDialog->inhibitShow(true); buttonListWidget->hide(); m_timeWidget->hide(); if (m_powerListWidget) { m_powerListWidget->hide(); } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } initBlockWidget(); if (m_blockWidget) { m_blockWidget->setGeometry(this->geometry()); if (inhibitType == 0) { m_inhibitType = "Reboot"; m_blockWidget->setTips( tr("%1 may cause users who have logged in to this computer to lose content that has not yet " "been stored,To still perform please click \"Confirm\".") .arg(tr("Restart"))); } else if (inhibitType == 1) { m_inhibitType = "PowerOff"; m_blockWidget->setTips( tr("%1 may cause users who have logged in to this computer to lose content that has not yet " "been stored,To still perform please click \"Confirm\".") .arg(tr("Shut Down"))); } m_blockWidget->setMsgTips(inhibitType); m_blockWidget->show(); m_blockWidget->setFocus(); } m_isInhibitStatus = true; } void LockWidget::onMulUsersLogined(int inhibitType) { // if (!m_isManualPower) { m_modelLockDialog->setIsSessionTools(true); } authDialog->inhibitShow(true); buttonListWidget->hide(); m_timeWidget->hide(); if (m_powerListWidget) { m_powerListWidget->hide(); } if (m_userListWidget && m_userListWidget->isVisible()) { m_userListWidget->hide(); if (m_userButton) { m_userButton->setClickedStatus(NORMAL); } } initBlockWidget(); if (m_blockWidget) { m_blockWidget->setGeometry(this->geometry()); if (inhibitType == 0) { m_inhibitType = "Reboot"; m_blockWidget->setTips( tr("Multiple users are logged in at the same time.Are you sure you want to %1 this system?") .arg(tr("Restart"))); } else if (inhibitType == 1) { m_inhibitType = "PowerOff"; m_blockWidget->setTips( tr("Multiple users are logged in at the same time.Are you sure you want to %1 this system?") .arg(tr("Shut Down"))); } m_blockWidget->show(); m_blockWidget->setFocus(); } m_isInhibitStatus = true; } void LockWidget::onConfirmBtnClicked() { if (m_blockWidget) { m_blockWidget->hide(); } if (m_powerListWidget) { m_powerListWidget->hide(); } authDialog->inhibitShow(false); if (m_isShowDateTime) { m_timeWidget->show(); } buttonListWidget->show(); m_isInhibitStatus = false; if (m_modelLockDialog->isSessionTools()) { Q_EMIT m_modelLockDialog->setPowerManager(false, m_inhibitType, true); exitSubWidget(); Q_EMIT sessionToolsExit(); } else { Q_EMIT m_modelLockDialog->setPowerManager(true, m_inhibitType); } } void LockWidget::onCancelBtnClicked() { m_isInhibitStatus = false; if (m_blockWidget) { m_blockWidget->hide(); } if (m_powerListWidget) { m_powerListWidget->show(); } buttonListWidget->show(); m_timeWidget->show(); if (m_systemMonitorBtn) { m_systemMonitorBtn->hide(); } if (m_modelLockDialog->isSessionTools()) { Q_EMIT sessionToolsExit(); } exitSubWidget(); } void LockWidget::onLockScreenClicked() { Q_EMIT m_modelLockDialog->lockStateChanged(true, false); m_modelLockDialog->setIsSessionTools(false); Q_EMIT moveToPrimaryScreen(); Q_EMIT setCurrentBackground(); onCurUserChanged(m_curUserInfo->name()); exitSubWidget(); } void LockWidget::onSuspendClicked(const QString &strType) { QStringList lockcheck = m_modelLockDialog->getSleepLockcheck(); bool canContinue = true; if (!isGreeterMode() && m_modelLockDialog->isSessionTools() && m_modelLockDialog->getSleepActivationEnabled()) { Q_EMIT m_modelLockDialog->lockStateChanged(true, false); if (lockcheck.isEmpty()) { Q_EMIT showBlankScreensaver(0, true); } else { if (strType == "Hibernate") { Q_EMIT m_modelLockDialog->showAppBlockWindow(3); } else { Q_EMIT m_modelLockDialog->showAppBlockWindow(2); } canContinue = false; } } else { if (lockcheck.isEmpty()) { exitSubWidget(); } else { if (strType == "Hibernate") { Q_EMIT m_modelLockDialog->showAppBlockWindow(3); } else { Q_EMIT m_modelLockDialog->showAppBlockWindow(2); } canContinue = false; } } if (canContinue) { QTimer::singleShot(100, this, [&, strType]() { Q_EMIT m_modelLockDialog->setPowerManager(false, strType); }); } } void LockWidget::onDoPowerAction(const QString &strType) { QString strRealType = strType; if (!strRealType.isEmpty()) { if (strRealType != "UpgradeThenRestart" && strRealType != "UpgradeThenShutdown") { if (!isGreeterMode()) { if (strRealType == "Hibernate" || strRealType == "Suspend") { QStringList sleepLockcheck = m_modelLockDialog->getSleepLockcheck(); if (!sleepLockcheck.isEmpty()) { if (strRealType == "Hibernate") { Q_EMIT m_modelLockDialog->showAppBlockWindow(3); } else { Q_EMIT m_modelLockDialog->showAppBlockWindow(2); } return; } } if (strRealType == "Reboot" || strRealType == "PowerOff" || strRealType == "Logout") { QStringList shutdownLockcheck = m_modelLockDialog->getShutdownLockcheck(); if (shutdownLockcheck.count() > 0) { if (strRealType == "Reboot") { Q_EMIT m_modelLockDialog->showAppBlockWindow(0); } else if (strRealType == "PowerOff") { Q_EMIT m_modelLockDialog->showAppBlockWindow(1); } else { Q_EMIT m_modelLockDialog->showAppBlockWindow(4); } return; } QStringList logoutLockcheck = m_modelLockDialog->getLogoutLockCheck(); if (logoutLockcheck.count() > 0) { qDebug() << "logoutlockcheck"; Q_EMIT m_modelLockDialog->showAppBlockWindow(4); return; } if (m_modelLockDialog->getLoggedInUsersCount() > 1 && strRealType != "Logout") { qDebug() << "multiuserblockwindow"; if (strRealType == "Reboot") { Q_EMIT m_modelLockDialog->showMultiUsersBlockWindow(0); } else if (strRealType == "PowerOff") { Q_EMIT m_modelLockDialog->showMultiUsersBlockWindow(1); } return; } } // 当存在多用户且存在软阻塞的情况下,且用户选择的是关机或重启,并且用户在软阻塞场景下点击了确认按钮 if (strRealType == "RebootSoftBlock" || strRealType == "PoweroffSoftBlock") { if (strRealType == "RebootSoftBlock") { strRealType = "Reboot"; } else if (strRealType == "PoweroffSoftBlock") { strRealType = "PowerOff"; } if (m_modelLockDialog->getLoggedInUsersCount() > 1) { qDebug() << "multiuserblockwindow"; if (strRealType == "Reboot") { Q_EMIT m_modelLockDialog->showMultiUsersBlockWindow(0); } else if (strRealType == "PowerOff") { Q_EMIT m_modelLockDialog->showMultiUsersBlockWindow(1); } return; } } } } qDebug() << "doPowerAction:" << strRealType << m_modelLockDialog->isSessionTools(); Q_EMIT m_modelLockDialog->setPowerManager(false, strRealType); if (m_modelLockDialog->isSessionTools()) { Q_EMIT sessionToolsExit(); } } } void LockWidget::onSwitchuserClicked() { onLockScreenClicked(); qDebug() << "onSwitchuserClicked"; // fix flash headimage QTimer::singleShot(0, this, [=]() { onShowUserListWidget(); }); } void LockWidget::onCustomRequestAccount(QString account) { if (m_modelLockDialog) { SwitchToUser(account); } } void LockWidget::onGetCustomPluginMsg(QString strMsg) { QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strMsg.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qWarning() << "Parse message json failed!!"; return; } else { QJsonObject rootObj = jsonDoc.object(); if (rootObj.isEmpty()) { qWarning() << "Message Json is null!!"; return; } else { QJsonObject contentObj = rootObj.value("Content").toObject(); if (!contentObj.isEmpty()) { QJsonObject configObj = contentObj.value("Configures").toObject(); if (!configObj.isEmpty()) { m_isCustomDefault = configObj.value("DefaultAuth").toBool(false); m_isShowNetwork = configObj.value("ShowNetwork").toBool(true); m_isShowUserSwitch = configObj.value("ShowUserSwitch").toBool(true); } } } } } void LockWidget::stopAuth() { if (authDialog) { authDialog->stopAuth(); } } void LockWidget::startAuth() { if (authDialog) { authDialog->startAuth(); } } void LockWidget::onSessionActiveChanged(bool isActive) { if (isActive) { if (m_timerChkActive && m_timerChkActive->isActive()) { m_timerChkActive->stop(); } if (m_modelLockDialog && m_modelLockDialog->isUseWayland()) { QTimer::singleShot(500, this, [&, this]() { if (this->isHidden()) { this->show(); update(); } startAuth(); }); } else { if (this->isHidden()) { this->show(); update(); } startAuth(); } } } void LockWidget::onTabletModeChanged(bool tabletMode) { onShowVirtualKeyboard(tabletMode); } void LockWidget::onLockDatetimeEnableChanged(bool enable, QString username) { if (m_curUserInfo->name() == username) { m_isShowDateTime = enable; m_timeWidget->setVisible(m_isShowDateTime); } } void LockWidget::onFontChanged(QString font) { m_curFont = font; } void LockWidget::onFontSizeChanged(double fontSize) { m_curFontSize = fontSize; updateFontSize(); } void LockWidget::onServiceUserChanged(WillLoginUserInfo willSwitchUser) { if (!isGreeterMode() || willSwitchUser.strUserName.isEmpty()) { if (isCurUserSelf(willSwitchUser.strUserName)) { // 锁屏且是当前用户则清空不处理 UniAuthService::instance()->SwitchToGreeterUser(""); } return; } static QString strLastUserName = ""; static quint64 llLastChangeTick = 0; // 清空 willSwitchUser UniAuthService::instance()->SwitchToGreeterUser(""); quint64 curChangeTick = QDateTime::currentDateTime().currentMSecsSinceEpoch(); if (willSwitchUser.strUserName == strLastUserName && curChangeTick - llLastChangeTick <= 2000) { qDebug() << "onServiceUserChanged same name and time short:" << willSwitchUser.strUserName << "," << curChangeTick - llLastChangeTick; return; } strLastUserName = willSwitchUser.strUserName; llLastChangeTick = curChangeTick; if (willSwitchUser.strUserName.isEmpty() || !UniAuthService::instance()->isUserNameValid(willSwitchUser.strUserName)) { qDebug() << "WillSwitchUser is NULL or invalid:" << willSwitchUser.strUserName; return; } qDebug() << "willSwitchUser:" << willSwitchUser; SwitchToUser(willSwitchUser.strUserName, true); } void LockWidget::SwitchToUser(QString strUserName, bool isSwitchInCurPage /* = false*/) { if (m_modelLockDialog) { if (isSwitchInCurPage) { Q_EMIT m_modelLockDialog->setCurrentUser(strUserName); } else { UniAuthService::instance()->SwitchToGreeterUser(strUserName); int nSwitchRet = Q_EMIT m_modelLockDialog->switchToUser(strUserName); if (nSwitchRet != 0) { UniAuthService::instance()->SwitchToGreeterUser(""); Q_EMIT m_modelLockDialog->setCurrentUser(strUserName); } else { // if(authDialog) { // authDialog->stopAuth(); // } if (!isCurUserSelf(strUserName) && !isHidden()) { this->hide(); update(); if (!m_timerChkActive) { m_timerChkActive = new QTimer(this); m_timerChkActive->setInterval(10 * 1000); connect(m_timerChkActive, &QTimer::timeout, this, [&, this]() { if (this->isHidden() && m_modelLockDialog->sessionActive()) { this->show(); } m_timerChkActive->stop(); }); } else { if (m_timerChkActive->isActive()) { m_timerChkActive->stop(); } } m_timerChkActive->start(); stopAuth(); } else { UniAuthService::instance()->SwitchToGreeterUser(""); Q_EMIT m_modelLockDialog->setCurrentUser(strUserName); } } } } } void LockWidget::setRfkillState() { if (m_rfkillState == 0) { Q_EMIT m_modelLockDialog->setRfkillState(KEY_RFKILL_STATE, 1); } else if (m_rfkillState == 1) { Q_EMIT m_modelLockDialog->setRfkillState(KEY_RFKILL_STATE, 0); } m_pRfkillStatusButton->setClickedStatus(NORMAL); } void LockWidget::setRfkillBtnIcon() { if (m_rfkillState == 0) { QPixmap rfkillOffIcon = drawSymbolicColoredPixmap( QIcon::fromTheme("ukui-airplane-mode-on-symbolic").pixmap(QSize(24, 24)), "gray"); m_pRfkillStatusButton->setIcon(rfkillOffIcon); } else if (m_rfkillState == 1) { QPixmap rfkillOnIcon = drawSymbolicColoredPixmap( QIcon::fromTheme("ukui-airplane-mode-on-symbolic").pixmap(QSize(24, 24)), "white"); m_pRfkillStatusButton->setIcon(QIcon(rfkillOnIcon)); } } void LockWidget::refreshTranslate() { QLocale local; if (systemLang == local.name()) { return; } systemLang = local.name(); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { if (m_pBottomWidget) { m_pBottomWidget->setLayoutDirection(Qt::RightToLeft); } if (m_userListWidget) { m_userListWidget->setLayoutDirection(Qt::RightToLeft); } if (m_powerListWidget) { m_powerListWidget->setLayoutDirection(Qt::RightToLeft); } } else { if (m_pBottomWidget) { m_pBottomWidget->setLayoutDirection(Qt::LeftToRight); } if (m_userListWidget) { m_userListWidget->setLayoutDirection(Qt::LeftToRight); } if (m_powerListWidget) { m_powerListWidget->setLayoutDirection(Qt::LeftToRight); } } setUserListWidgetLocate(); if (m_powerManagerButton) { m_powerManagerButton->setToolTip(tr("Power")); } if (m_virKbButton) { m_virKbButton->setToolTip(tr("VirtualKeyboard")); } #if SUPPORT_NET_ICON if (m_networkButton) { m_networkButton->setToolTip(tr("Network")); } #endif // SUPPORT_NET_ICON if (m_userButton) { m_userButton->setToolTip(tr("SwitchUser")); } if (m_batteryonButton) { m_batteryonButton->setToolTip(tr("Power Information")); } if (m_sessionButton) { m_sessionButton->setToolTip(tr("SwitchSession")); } if (m_systemMonitorBtn) { m_systemMonitorBtn->setText(tr("system-monitor")); } if (m_pResetPwdBtn) { m_pResetPwdBtn->setText(tr("Reset Pwd")); } } void LockWidget::setUserListWidgetLocate() { if (!m_userListWidget || !buttonListWidget) return; if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { m_userListWidget->move( RIGHT_MARGIN, height() - BOTTOM_MARGIN * scale - buttonListWidget->height() - buttonListWidget->spacing() * scale - m_userListWidget->height()); } else { m_userListWidget->move( width() - m_userListWidget->width() - RIGHT_MARGIN, height() - BOTTOM_MARGIN * scale - buttonListWidget->height() - buttonListWidget->spacing() * scale - m_userListWidget->height()); } } void LockWidget::setNetWorkWidgetLocate() { if (!m_networkWidget || !buttonListWidget) return; if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { if (m_networkWidget->isVisible() && m_virtualKeyboardWidget && !m_virtualKeyboardWidget->getFloatStatus() && m_virtualKeyboardWidget->isVisible()) { m_networkWidget->move( RIGHT_MARGIN * scale, this->height() - m_networkWidget->height() - m_virtualKeyboardWidget->height()); m_networkWidget->raise(); } else { m_networkWidget->move( RIGHT_MARGIN * scale, this->height() - m_networkWidget->height() - BOTTOM_MARGIN * scale - buttonListWidget->spacing() * scale - buttonListWidget->height()); } } else { if (m_networkWidget->isVisible() && m_virtualKeyboardWidget && !m_virtualKeyboardWidget->getFloatStatus() && m_virtualKeyboardWidget->isVisible()) { m_networkWidget->move( this->width() - m_networkWidget->width() - RIGHT_MARGIN * scale, this->height() - m_networkWidget->height() - m_virtualKeyboardWidget->height()); m_networkWidget->raise(); } else { m_networkWidget->move( this->width() - m_networkWidget->width() - RIGHT_MARGIN * scale, this->height() - m_networkWidget->height() - BOTTOM_MARGIN * scale - buttonListWidget->spacing() * scale - buttonListWidget->height()); } } } void LockWidget::setSessionListWidgetLocate() { if (!m_sessionListWidget) return; if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { if (buttonListWidget) { m_sessionListWidget->move( RIGHT_MARGIN * scale, height() - BOTTOM_MARGIN * scale - buttonListWidget->height() - buttonListWidget->spacing() * scale * 2 - m_sessionListWidget->height()); } } else { if (buttonListWidget) { m_sessionListWidget->move( this->width() - m_sessionListWidget->width() - RIGHT_MARGIN * scale, height() - BOTTOM_MARGIN * scale - buttonListWidget->height() - buttonListWidget->spacing() * scale * 2 - m_sessionListWidget->height()); } } } void LockWidget::setBatteryWidgetLocate() { if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { if (batteryWidget && buttonListWidget) { batteryWidget->move( RIGHT_MARGIN * scale, height() - BOTTOM_MARGIN * scale - buttonListWidget->height() - buttonListWidget->spacing() * scale - batteryWidget->height()); } } else { if (batteryWidget && buttonListWidget) { batteryWidget->move( this->width() - batteryWidget->width() - RIGHT_MARGIN * scale, height() - BOTTOM_MARGIN * scale - buttonListWidget->height() - buttonListWidget->spacing() * scale - batteryWidget->height()); } } } void LockWidget::onLanguageChanged(bool isCompleted) { if (isCompleted) { if (m_networkWidget) { m_networkWidget->unloadNetPlugin(); } } } void LockWidget::setrootWindowBackground(int type, unsigned int color, QString filename) { drawBackgroundType = type; if (drawBackgroundType == rootWinPicture) m_rootWindowBackground = filename; else if (drawBackgroundType == rootWinColor) drawBackgroundColor = color; } void LockWidget::setRootWindowBgOptions(int nOption) { m_nPicOptions = nOption; } bool LockWidget::getdrawBackgroundIsStarted() { return drawBackgroundIsStarted; } void LockWidget::setrootWindow(QList> screenRectList) { drawBackgroundIsStarted = true; QString m_defaultBackgroundPath = DEFAULT_BACKGROUND_PATH; if (m_rootWindowBackground.isEmpty()) m_rootWindowBackground = m_defaultBackgroundPath; if (m_rootWindowBackground.isEmpty()) return; char *path; QByteArray ba = m_rootWindowBackground.toLatin1(); // must path = ba.data(); #ifdef SUPPORT_X11_BACKGROUND if (drawBackgroundType == 0) setRootWindowBackground(0, 0, path, screenRectList, m_nPicOptions); else setRootWindowBackground(1, drawBackgroundColor, NULL, screenRectList, m_nPicOptions); #endif drawBackgroundIsStarted = false; } void LockWidget::reloadRootBackground() { if (!isGreeterMode() || m_modelLockDialog->isUseWayland()) { return; } // 记录当前的屏幕信息,写入QList保存,防止线程中在遍历屏幕信息时,因 // 屏幕插拔而崩溃 m_screenRectList.clear(); for (QScreen *screen : QApplication::screens()) { QRect rect1 = screen->geometry(); QRect rect2 = screen->virtualGeometry(); m_screenRectList.append(qMakePair(rect1, rect2)); } if (getdrawBackgroundIsStarted()) { if (m_futureDrawBg.isStarted() && m_futureDrawBg.isRunning()) { m_futureDrawBg.waitForFinished(); m_futureDrawBg = QtConcurrent::run([=]() { setrootWindow(m_screenRectList); }); } else { m_futureDrawBg = QtConcurrent::run([=]() { setrootWindow(m_screenRectList); }); } } else { m_futureDrawBg = QtConcurrent::run([=]() { setrootWindow(m_screenRectList); }); } } void LockWidget::drawRootBackground() { if (m_modelLockDialog->isUseWayland()) { return; } #ifdef SUPPORT_X11_BACKGROUND drawBackground(true); #endif } void LockWidget::updateFont() { if (authDialog) { authDialog->updateAuthFont(m_curFont); } if (m_timeWidget) { m_timeWidget->updateTimeFont(m_curFont); } if (m_powerListWidget) { QList itemList = this->findChildren(); for (int i = 0; i < itemList.count(); i++) { itemList.at(i)->setFontFamily(m_curFont); } } if (m_userListWidget) { QList itemList = this->findChildren(); for (int i = 0; i < itemList.count(); i++) { itemList.at(i)->setFontFamily(m_curFont); } } if (m_blockWidget) { m_blockWidget->updateFontFamily(m_curFont); } if (m_systemMonitorBtn) { QFont sysBtnFont; sysBtnFont.setFamily(m_curFont); sysBtnFont.setPointSize((16 + m_curFontSize) * m_ptToPx); m_systemMonitorBtn->setFont(sysBtnFont); m_systemMonitorBtn->setText(tr("system-monitor")); m_systemMonitorBtn->adjustSize(); } } void LockWidget::updateFontSize() { if (authDialog) { authDialog->updateAuthFontSize(m_curFontSize); } if (m_timeWidget) { m_timeWidget->updateTimeFontSize(m_curFontSize); } if (m_powerListWidget) { QList itemList = this->findChildren(); for (int i = 0; i < itemList.count(); i++) { itemList.at(i)->setFontSize((16 + m_curFontSize) * m_ptToPx); } } if (m_userListWidget) { QList itemList = this->findChildren(); for (int i = 0; i < itemList.count(); i++) { itemList.at(i)->setFontSize((14 + m_curFontSize) * m_ptToPx); } } } void LockWidget::onUPowerServiceReady() { qDebug() << "onUPowerServiceReady delay!"; if (buttonListWidget && m_btnItemBattery && m_batteryonButton) { QPixmap pixmap = QIcon::fromTheme(m_modelLockDialog->getBatteryIconName()).pixmap(QSize(24, 24)); QStyleOption opt; QPixmap batteryIcon = filledSymbolicColoredPix(pixmap, opt.palette.highlightedText().color()); m_batteryonButton->setIcon(batteryIcon); if (!m_modelLockDialog->getIsBattery()) { buttonListWidget->setItemHidden(m_btnItemBattery, true); } else { buttonListWidget->setItemHidden(m_btnItemBattery, false); } } } void LockWidget::doDelayInit() { // 电源管理 if (!m_powerManagerButton) { m_powerManagerButton = new StatusButton(this, BOTBUTTON); if (buttonListWidget && m_btnItemPowerManager) { buttonListWidget->setItemWidget(m_btnItemPowerManager, m_powerManagerButton); } m_powerManagerButton->setToolTip(tr("Power")); connect(m_powerManagerButton, &StatusButton::clicked, this, [this]() { m_isManualPower = true; onShowPowerListWidget(); }); m_powerManagerButton->setIcon(drawSymbolicColoredPixmap( QIcon::fromTheme("system-shutdown-symbolic", QIcon(":/image/assets/shutdown.svg")).pixmap(24, 24), "white")); } // 虚拟键盘 if (!m_virKbButton) { m_virKbButton = new StatusButton(this, BOTBUTTON); if (buttonListWidget && m_btnItemVirKb) { buttonListWidget->setItemWidget(m_btnItemVirKb, m_virKbButton); } m_virKbButton->setIcon(QIcon(":/image/assets/keyboard.svg")); m_virKbButton->setToolTip(tr("VirtualKeyboard")); connect(m_virKbButton, &StatusButton::clicked, this, [this]() { onShowVirtualKeyboard(); }); if (m_modelLockDialog->getTabletMode()) { onShowVirtualKeyboard(m_modelLockDialog->getTabletMode()); } } #if SUPPORT_NET_ICON if (!m_networkButton) { m_networkButton = new NetWorkButton(this); if (buttonListWidget && m_btnItemNetwork) { buttonListWidget->setItemWidget(m_btnItemNetwork, m_networkButton); } m_networkButton->setToolTip(tr("Network")); connect(m_networkButton, &NetWorkButton::clicked, this, [this]() { onShowNetworkWidget(); }); } #endif // 用户选择 if (!m_userButton) { m_userButton = new StatusButton(this, BOTBUTTON); if (buttonListWidget && m_btnItemUser) { buttonListWidget->setItemWidget(m_btnItemUser, m_userButton); } m_userButton->setToolTip(tr("SwitchUser")); connect(m_userButton, &StatusButton::clicked, this, [this]() { onShowUserListWidget(); }); if (!isGreeterMode() && !m_modelLockDialog->getLockCanSwitchUser()) { buttonListWidget->setItemHidden(m_btnItemUser, true); } else if (m_modelLockDialog->normalUserCount() > 1 || m_modelLockDialog->findUserByName("*login")) { buttonListWidget->setItemHidden(m_btnItemUser, false); } else { buttonListWidget->setItemHidden(m_btnItemUser, true); } m_userButton->setIcon(drawSymbolicColoredPixmap( QIcon::fromTheme("ukui-stock-people-symbolic", QIcon(":/image/assets/switchuser.svg")).pixmap(24, 24), "white")); } // 电池信息 if (!m_batteryonButton) { m_batteryonButton = new StatusButton(this, BOTBUTTON); if (buttonListWidget && m_btnItemBattery) { buttonListWidget->setItemWidget(m_btnItemBattery, m_batteryonButton); } QPixmap pixmap = QIcon::fromTheme(m_modelLockDialog->getBatteryIconName()).pixmap(QSize(24, 24)); QStyleOption opt; QPixmap batteryIcon = filledSymbolicColoredPix(pixmap, opt.palette.highlightedText().color()); m_batteryonButton->setIcon(batteryIcon); connect(m_modelLockDialog, &LockDialogModel::batteryStatusChanged, this, [=](const QString &iconName) { QPixmap pixmap = QIcon::fromTheme(iconName).pixmap(QSize(24, 24)); QPixmap batteryIcon = filledSymbolicColoredPix(pixmap, opt.palette.highlightedText().color()); m_batteryonButton->setIcon(batteryIcon); }); m_batteryonButton->setToolTip(tr("Power Information")); connect(m_batteryonButton, &StatusButton::clicked, this, [this]() { onShowBatteryWidget(); }); if (!m_modelLockDialog->getIsBattery()) { buttonListWidget->setItemHidden(m_btnItemBattery, true); } else { buttonListWidget->setItemHidden(m_btnItemBattery, false); } } // 会话选择 if (!m_sessionButton) { m_sessionButton = new StatusButton(this, BOTBUTTON); if (buttonListWidget && m_btnItemSession) { buttonListWidget->setItemWidget(m_btnItemSession, m_sessionButton); } m_sessionButton->setObjectName("BtnSession"); if (m_curUserInfo->isLoggedIn() || m_modelLockDialog->sessionsInfo().count() <= 1) { buttonListWidget->setItemHidden(m_btnItemSession, true); if (m_sessionListWidget && m_sessionListWidget->isVisible()) { m_sessionListWidget->hide(); } } else { buttonListWidget->setItemHidden(m_btnItemSession, false); } QString strCurSession = m_modelLockDialog->currentSession(); QString sessionPrefix = strCurSession.left(strCurSession.indexOf('-')); if (strCurSession == "ukui-wayland") sessionPrefix = "ukui_wayland"; if (strCurSession == "kylin-wlcom") sessionPrefix = "kylin_wlcom"; strCurSession = IMAGE_DIR + QString("badges/%1_badge.svg").arg(sessionPrefix.toLower()); QFile iconFile(strCurSession); if (!iconFile.exists()) { strCurSession = IMAGE_DIR + QString("badges/unknown_badge.svg"); } QPixmap ssIcon(strCurSession); ssIcon = scaledPixmap(ssIcon); ssIcon = ssIcon.scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); ssIcon = PixmapToRound(ssIcon, 24); m_sessionButton->setIcon(ssIcon); m_sessionButton->setToolTip(tr("SwitchSession")); connect(m_sessionButton, &StatusButton::clicked, this, [this]() { onShowSessionListWidget(); }); } } ukui-screensaver/src/widgets/grab-x11.h0000664000175000017500000000145215172041035016723 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef GRABX11_H #define GRABX11_H bool establishGrab(); bool closeGrab(); bool checkHasPopupMenu(); #endif // GRABX11_H ukui-screensaver/src/widgets/networkbutton.h0000664000175000017500000000247015172041106020326 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef NETWORKBUTTON_H #define NETWORKBUTTON_H #if SUPPORT_NET_ICON #include #include #include "statusbutton.h" class NetWorkButton : public KyNetworkIcon { Q_OBJECT public: explicit NetWorkButton(QWidget *parent = 0); /** * @brief 设置按钮当前状态 * @param selected 是否选中 * @return */ void setClickedStatus(int status); protected: void paintEvent(QPaintEvent *e) override; void mouseReleaseEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); private: int curStatus = NORMAL; }; #endif // SUPPORT_NET_ICON #endif // NETWORKBUTTON_H ukui-screensaver/src/widgets/agreementwindow.cpp0000664000175000017500000000746515172041106021144 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "agreementwindow.h" #include #include #include #include #define CONFIG_FILE "/usr/share/ukui-greeter/ukui-greeter.conf" #define CENTER_WIDTH 960 #define CENTER_HEIGHT 722 #define ENSURE_BUTTON_HEIGHT 48 #define BORDER_WIDTH 40 AgreementWindow::AgreementWindow(bool hideTitle, QString title, QString text, QWidget *parent) : m_hideTitle(hideTitle), m_title(title), m_text(text), QWidget(parent) { initUI(); } void AgreementWindow::initUI() { centerWidget = new QWidget(this); centerWidget->setObjectName("centerWidget"); // centerWidget->setFixedSize(960,744); centerWidget->setStyleSheet("#centerWidget{background-color: rgba(0,0,0,15%);border-radius: 12px;}"); QVBoxLayout *layout = new QVBoxLayout(centerWidget); ensureBtn = new QPushButton(this); ensureBtn->setText(tr("I know")); ensureBtn->setObjectName("ensureBtn"); ensureBtn->setFixedSize(240, 48); ensureBtn->setDefault(true); ensureBtn->setFocusPolicy(Qt::StrongFocus); ensureBtn->setProperty("isImportant", true); // ensureBtn->setStyleSheet("#ensureBtn{background-color:rgba(55, 144, 250, 1);border-radius: 6px;}"); // ensureBtn->setStyleSheet("QPushButton{text-align:center;background-color:#3790FA;border-radius: 6px;color:white} // QPushButton::hover{background-color:#3489EE;} // QPushButton::pressed {background-color:#2C73C8;}"); connect(ensureBtn, &QPushButton::clicked, this, &AgreementWindow::switchPage); titleLbl = new QLabel(centerWidget); titleLbl->setText(m_title); titleLbl->setObjectName("titleLbl"); titleLbl->setStyleSheet("#titleLbl{color: white;font-size: 24px;}"); browser = new QTextBrowser(centerWidget); browser->setObjectName("browser"); browser->setAttribute(Qt::WA_TranslucentBackground); browser->setStyleSheet("#browser{background-color:transparent;border-radius: none;color: white;font-size: 16px;}"); browser->verticalScrollBar()->setProperty("drawScrollBarGroove", false); browser->setContextMenuPolicy(Qt::NoContextMenu); browser->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); setFocusProxy(ensureBtn); browser->setText(m_text); layout->addSpacing(24); layout->addWidget(titleLbl); layout->addWidget(browser); layout->setAlignment(ensureBtn, Qt::AlignCenter); layout->setAlignment(titleLbl, Qt::AlignCenter); if (m_hideTitle) titleLbl->hide(); } void AgreementWindow::switchPage() { emit switchToGreeter(); } void AgreementWindow::resizeEvent(QResizeEvent *event) { int m_width = CENTER_WIDTH, m_height = CENTER_HEIGHT - ENSURE_BUTTON_HEIGHT - BORDER_WIDTH; if (this->width() < (CENTER_WIDTH + 2 * BORDER_WIDTH)) m_width = (width() - BORDER_WIDTH) / 2; if (this->height() < (CENTER_HEIGHT + 2 * BORDER_WIDTH)) m_height = (height() - BORDER_WIDTH) / 2; if (centerWidget) { centerWidget->setGeometry((width() - m_width) / 2, (height() - m_height) / 2, m_width, m_height); if (ensureBtn) ensureBtn->move((width() - ensureBtn->width()) / 2, centerWidget->geometry().bottom() + 40); } } ukui-screensaver/src/widgets/customlabel.h0000664000175000017500000000334115172041106017711 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef CUSTOMKLABEL_H #define CUSTOMKLABEL_H #include #include #include class CustomLabel : public QLabel { Q_OBJECT public: CustomLabel(QWidget *parent = nullptr); void setFontSize(int fontSize); QString getElidedText(QFont font, int width, QString strInfo); static QWidget *m_parentWidget; void setTipText(const QString &); void clear(); QString text() const; void setFontFamily(QString &family); void setFontWeight(QFont::Weight weight); void setMarqueueMode(bool isMarqueue); public Q_SLOTS: void setText(const QString &); protected: void paintEvent(QPaintEvent *event); private: void startMarqueueTimer(); void stopMarqueueTimer(); private Q_SLOTS: void onMarqueueTimer(); private: QFont sysFont; double m_ptToPx = 1.0; QString m_strText; bool m_isMarqueeMode = false; int m_speed; // 滚动速度,可以根据需要调整 int m_scrollOffset; // 文本滚动的偏移量 QTimer *m_timerMarqueue = nullptr; }; #endif // CUSTOMKLABEL_H ukui-screensaver/src/widgets/resetpwdwidget.h0000664000175000017500000001454615172041106020451 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef RESETPWDWIDGET_H #define RESETPWDWIDGET_H #include #include #include "securityquestionanswer.h" #include "iconedit.h" #include "passwdcheckutil.h" #include using namespace kdk; #ifdef ENABLEPQ extern "C" { #include } #define PWCONF "/etc/security/pwquality.conf" #define RFLAG 0x1 #define CFLAG 0x2 #endif enum bindIdentity { UNDDEFINED = -1, NOTHINGBIND, SECURITYQUESTION, KYLINID, ALLDEFINED, }; enum SwitchPage{ SwitchToPin = 0, SwitchToWechat, SwitchToCheck, SwitchToResetPWD, SwitchToConfigPWD }; // 和后台规定好的 错误码固定值 enum DBusMsgCode{ Error_QR_Get_Timeout = -8, // 二维码获取时间超时 Error_NetWork = -7, // 网络错误 未能如期获得对应的数据 Error_VerifyCode_Timeout = -6, // 验证码失效 Error_RepeatedRequests = -5, // 重复请求二维码服务 Error_NoReply = -4, // DBus接口连接失败导致的无回复错误 Error_UnknownReason = -3, // 未知原因 Error_ArgCnt = -2, // 参数个数错误 Error_SetPsw_SameAsOriPsw = -1, // 设置新密码时 与原密码相同 No_Error = 0, // 获取消息无错误 Error_TelHaveBinded = 9009, // 手机号已绑定 -> 更换手机号后再试 Error_VerifyCodeDiff = 9000, // 验证码错误 -> 请填写正确的验证码 Error_AccessTokenInvalid = 9007,// Access Token 失效 -> 登录状态已过期,请重新扫码登录 Error_ErrTimesOverMax = 9014, // 手机号登录时验证码错误次数超限 // 账号密码登录时密码错误次数超限 // 1小时内连续错10次触发 -> 1小时后再试 Error_UserInfo = 9017, // 用户微信信息不存在 ->老用户,联系管理员处理 Error_WechatHaveBinded = 9022, // 微信号已被其他账号绑定 -> 更换微信号再试 Error_DeviceHaveBinded = 9027, // 当前设备已被绑定 -> 请联系管理员或班主任解绑 Error_HaveBindOtherDevice = 9028, // 用户已绑定其他设备 -> 请联系管理员或班主任解绑 //以下错误码保留 Error_NWUnused = 6, // 网络异常,请检查网络链接哦~ Error_TencentUnused = 7, // 腾讯服务异常,请稍后再试~ Error_NWDelay = 28, // 网络延迟大,请更换良好网络~ Error_ResMalloc = 301, // 资源分配异常,请重启尝试 Error_DBOpen = 101, // 数据库打开异常,请重试,或重启再试 Error_DBGetKey = 102, // 获取数据库密钥失败,请重试 Error_DBRetrieve = 103, // 数据库检索数据失败,请重试 Error_DBDecryption = 105, // 数据库解密过程失败,请重试 }; /*! * \brief The QRCodeSwepState enum * WaitingSwep 在DBus、SSOBackend、1.617 版本中未启用 */ enum QRCodeSwepState{ WaitingSwep = 0, //等待用户扫码 HaveSwep = 2, // 用户扫码、等待用户确认 CancelSwep = 3, // 用户扫码后、取消确认 ConfirmSuccess = 4, // 扫码确认成功 QRCodeInvalid = 5, // 二维码失效 QRCodeTimeout = 6, //二维码超时 }; // 麒麟ID错误码 enum KylinIDErrorCode { ACCOUNTNITEXIT = 634, ACCOUNTLOCKED = 531, ERRORTOOTIMES = 656, CODEERROR = 632, IDORPWDERROR = 511, DECRYPTIONERROR = 631, PICTURECODEERROR = 614, }; class TalksWidget; class QPushButton; class QLabel; class CustomLabel; class QLineEdit; class KyIconButton; class VerifyKyidWidget; extern float scale; class ResetPwdWidget : public QWidget { Q_OBJECT public: explicit ResetPwdWidget(LockDialogModel *model, QString userName, int uid, SecurityQuestionAnswer *securityQuestionAnswer, QWidget *parent = nullptr); void updateResetPwdSize(); void setFocusToEdit(); void switchToConfidentialDig(); void updateFontSize(double lfFontSize); void setBindIdentity(int type); protected: void keyPressEvent(QKeyEvent *); bool eventFilter(QObject *obj, QEvent *event); private: void initUI(); void switchToResetPwdWdg(); void verifyFailed(); void refreshConfirmBtnStatus(); void makeSurePwqualityEnabled(); void checkPwdLegality(); bool isContainLegitimacyChar(QString word); void clearLineEditText(); Q_SIGNALS: void backToAuthDialog(bool success); void confirmBtnclicked(); void resetEditClicked(); void getCodeByKyidAccountsed(QString message); void hideKylinIDCodesTips(); private Q_SLOTS: void onConfirmBtnclicked(); void onCaptchaBtnclicked(); private: LockDialogModel *m_modelLockDialog = nullptr; QList m_listTalkWidget; QWidget *m_pQustionMainWidget = nullptr; TalksWidget *m_pResetPwdWidget = nullptr; KyIconButton *m_pConfirmBtn = nullptr; QPushButton *m_pCancelBtn = nullptr; QString m_nameText; int m_uid; CustomLabel *m_pMessageLabel = nullptr; QWidget *m_pSureWidget = nullptr; TalksWidget *m_pNewPwdWidget = nullptr; CustomLabel *m_pNewPwdTipLabel = nullptr; IconEdit *m_pSureEdit = nullptr; CustomLabel *m_pSureTipLabel = nullptr; SecurityQuestionAnswer *m_pSecurityQuestionAnswer = nullptr; QList m_listQuestionInfo; QString m_newPwdTip; QString m_surePwdTip; QString m_verifyToken; bool m_enablePwdQuality; KTabBar *m_pTabBar = nullptr; CustomLabel *m_pTitleLabel = nullptr; VerifyKyidWidget *m_pVerifyKyidWidget = nullptr; int m_nBindIdentity = UNDDEFINED; int m_nCurrentState = UNDDEFINED; #ifdef ENABLEPQ pwquality_settings_t *m_pSettings; #endif }; #endif // RESETPWDWIDGET_H ukui-screensaver/src/widgets/pluginsloader.cpp0000664000175000017500000000574315172041035020613 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "pluginsloader.h" #include "loginplugininterface.h" #include #include #include #include #include const QString gPluginDir = "/usr/lib/ukui-greeter/plugins"; PluginsLoader::PluginsLoader(QObject *parent) : QThread(parent) {} PluginsLoader::~PluginsLoader() {} PluginsLoader &PluginsLoader::instance() { static PluginsLoader pluginsLoader; return pluginsLoader; } LoginPluginInterface *PluginsLoader::findModuleByName(const QString &name) const { return m_plugins.value(name, nullptr).data(); } QMap PluginsLoader::findModulesByType(const int type) const { QMap modules; for (QSharedPointer module : m_plugins.values()) { if (module.isNull()) { continue; } if (module->getPluginType() == type) { modules.insert(module->name(), module.data()); } } return modules; } void PluginsLoader::run() { findModule(gPluginDir); } void PluginsLoader::findModule(const QString &path) { QDir dir(path); if (!dir.exists()) { qInfo() << path << "is not exists."; return; } const QFileInfoList modules = dir.entryInfoList(); for (QFileInfo module : modules) { const QString path = module.absoluteFilePath(); if (!QLibrary::isLibrary(path)) { continue; } qInfo() << module << "is found"; QPluginLoader loader(path); LoginPluginInterface *moduleInstance = dynamic_cast(loader.instance()); if (!moduleInstance) { qWarning() << loader.errorString(); continue; } if (!moduleInstance->needLoad()) { continue; } if (m_plugins.contains(moduleInstance->name())) { continue; } QObject *obj = dynamic_cast(moduleInstance); if (obj) obj->moveToThread(qApp->thread()); m_plugins.insert(moduleInstance->name(), QSharedPointer(moduleInstance)); emit moduleFound(moduleInstance); } } void PluginsLoader::removeModule(const QString &moduleKey) { if (!m_plugins.contains(moduleKey)) return; m_plugins.remove(moduleKey); } ukui-screensaver/src/widgets/customlabel.cpp0000664000175000017500000001113115172041106020240 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "customlabel.h" #include #include #include #include #include CustomLabel::CustomLabel(QWidget *parent) : QLabel(parent), m_speed(2), m_scrollOffset(0) { setWindowFlags(Qt::FramelessWindowHint); // m_strText = ""; sysFont = qApp->font(); QPalette pe = palette(); pe.setColor(QPalette::WindowText, Qt::white); pe.setColor(QPalette::Text, Qt::white); this->setPalette(pe); } void CustomLabel::setFontSize(int fontSize) { sysFont.setPointSize(fontSize); this->setFont(sysFont); } void CustomLabel::setFontFamily(QString &family) { sysFont.setFamily(family); this->setFont(sysFont); } void CustomLabel::setFontWeight(QFont::Weight weight) { sysFont.setWeight(weight); this->setFont(sysFont); } void CustomLabel::setTipText(const QString &strText) { m_scrollOffset = 0; m_strText = strText; QLabel::setText(strText); } void CustomLabel::setText(const QString &strText) { setTipText(strText); } void CustomLabel::clear() { m_strText.clear(); QLabel::clear(); } QString CustomLabel::text() const { if (m_strText.isEmpty()) return QLabel::text(); else return m_strText; } QString CustomLabel::getElidedText(QFont font, int width, QString strInfo) { QFontMetrics fontMetrics(font); // 如果当前字体下,字符串长度大于指定宽度 if (fontMetrics.horizontalAdvance(strInfo) > width) { strInfo = QFontMetrics(font).elidedText(strInfo, Qt::ElideRight, width); } return strInfo; } void CustomLabel::paintEvent(QPaintEvent *event) { if (m_strText.isEmpty() && !QLabel::text().isEmpty()) m_strText = QLabel::text(); QString strEText = getElidedText(font(), width() - contentsMargins().left() - contentsMargins().right(), m_strText); if (strEText == m_strText) { QLabel::setText(m_strText); } else if (strEText != m_strText) { if (m_isMarqueeMode) { QLabel::setText(""); QPainter painter(this); painter.drawText( m_scrollOffset + contentsMargins().left(), rect().top(), width() - contentsMargins().left() - contentsMargins().right() - m_scrollOffset, height(), Qt::AlignLeft | Qt::TextSingleLine, m_strText); // qDebug()<<"MarqueueText:"<isActive()) { m_timerMarqueue->stop(); } m_timerMarqueue->start(100); } void CustomLabel::stopMarqueueTimer() { if (m_timerMarqueue->isActive()) { m_timerMarqueue->stop(); } } void CustomLabel::onMarqueueTimer() { if (!isVisible()) return; int textWidth = fontMetrics().horizontalAdvance(text()); if (m_scrollOffset - m_speed < -textWidth) { m_scrollOffset = width() - contentsMargins().left() - contentsMargins().right(); // 重置位置 } else { m_scrollOffset -= m_speed; // 更新位置 } update(); // 更新显示 } ukui-screensaver/src/widgets/passwdcheckutil.cpp0000664000175000017500000001227015172041106021130 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "passwdcheckutil.h" #include #include #include #include #include #include #define PAM_CONF_FILE "/etc/pam.d/common-password" PasswdCheckUtil::PasswdCheckUtil(QObject *parent) : QObject(parent) { } bool PasswdCheckUtil::getCurrentPamState(){ QFile * readFile = new QFile(PAM_CONF_FILE); if (!readFile->open(QIODevice::ReadOnly | QIODevice::Text)){ readFile->close(); qDebug() << QString("Open conf file %1 failed!").arg(PAM_CONF_FILE); return false; } else { QTextStream stream(readFile); while(!stream.atEnd()){ QString line = stream.readLine(); if (line.contains("pam_pwquality.so")) return true; } return false; } } static int palindrome(const char *newPw) { int i = 0, j = 0; i = strnlen(newPw, 1024); /* NOT consider one character as palindrome */ if (i == 1) return 0; for (j = 0; j < i; j++) { if (newPw[i - j - 1] != newPw[j]) { return 0; } } return 1; } int PasswdCheckUtil::checkPassword(QString newPw, QString oldPw, QString userName) { int digits = 0, digitExist = 0; int uppers = 0, upperExist = 0; int lowers = 0, lowerExist = 0; int others = 0, otherExist = 0; int totalClass = 0; int size; int i; enum { NONE, DIGIT, UCASE, LCASE, OTHER } prevclass = NONE; int sameclass = 0; QByteArray ba = newPw.toLatin1(); const char* newPwd = ba.data(); if (!newPw.isEmpty() && !oldPw.isEmpty() && newPw == oldPw) { return PWCHK_ERROR_SAME_PASSWORD; } for (i = 0; newPwd[i]; i++) { if (isdigit(newPwd[i])) { digits++; digitExist = 1; if (prevclass != DIGIT) { prevclass = DIGIT; sameclass = 1; } else sameclass++; } else if (isupper(newPwd[i])) { uppers++; upperExist = 1; if (prevclass != UCASE) { prevclass = UCASE; sameclass = 1; } else sameclass++; } else if (islower(newPwd[i])) { lowers++; lowerExist = 1; if (prevclass != LCASE) { prevclass = LCASE; sameclass = 1; } else sameclass++; } else { others++; otherExist = 1; if (prevclass != OTHER) { prevclass = OTHER; sameclass = 1; } else sameclass++; } } size = PWCHK_DEFAULT_MIN_LENGTH; if (size > i) return PWCHK_ERROR_MIN_LENGTH; totalClass = digitExist + upperExist + lowerExist + otherExist; if (totalClass < PWCHK_DEFAULT_MIN_CLASS) return PWCHK_ERROR_MIN_CLASSES; // 回文 if (palindrome(newPwd)) { return PWCHK_ERROR_PALINDROME; } if (!newPw.isEmpty() && !oldPw.isEmpty()) { if (newPw.compare(oldPw, Qt::CaseInsensitive) == 0) { return PWCHK_ERROR_CASE_CHANGES_ONLY; } else if (oldPw.toLower().contains(newPw.toLower())) { return PWCHK_ERROR_TOO_SIMILAR; } } return PWCHK_ERROR_SUCCESS; } QString PasswdCheckUtil::checkPasswordStrerr(int nResult) { QString strResult = ""; switch (nResult){ case PWCHK_ERROR_MIN_LENGTH: strResult = QString(tr("The password is shorter than %1 characters")).arg(PWCHK_DEFAULT_MIN_LENGTH); break; case PWCHK_ERROR_MIN_CLASSES: strResult = QString(tr("The password contains less than %1 character classes")).arg(PWCHK_DEFAULT_MIN_CLASS); break; case PWCHK_ERROR_SAME_PASSWORD: strResult = tr("The password is the same as the old one"); break; case PWCHK_ERROR_USER_CHECK: strResult = tr("The password contains the user name in some form"); break; case PWCHK_ERROR_CASE_CHANGES_ONLY: strResult = tr("The password differs with case changes only"); break; case PWCHK_ERROR_TOO_SIMILAR: strResult = tr("The password is too similar to the old one"); break; case PWCHK_ERROR_PALINDROME: strResult = tr("The password is a palindrome"); break; default: break; } return strResult; } ukui-screensaver/src/widgets/resetpwdwidget.cpp0000664000175000017500000006061415172041106021001 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "resetpwdwidget.h" #include #include #include #include #include #include #include #include #include #include "commonfunc.h" #include "talkswidget.h" #include "customlabel.h" #include "kyiconbutton.h" #include "verifykyidwidget.h" #include "kyrsac.h" #define RESETPWD_WIDGET_HEIGHT_SPACING (12 * scale) #define FACELABEL_HEIGHT (154 * scale) #define NAMELABEL_HEIGHT (40) // 用正则表达式在一段字符串中提取出数字 static QString extractContinuousNumbers(const QString &input) { QRegularExpression re("\\d+"); QRegularExpressionMatchIterator i = re.globalMatch(input); QString result; while (i.hasNext()) { QRegularExpressionMatch match = i.next(); result += match.captured() + " "; } return result.trimmed(); } ResetPwdWidget::ResetPwdWidget( LockDialogModel *model, QString userName, int uid, SecurityQuestionAnswer *securityQuestionAnswer, QWidget *parent) : QWidget(parent) , m_modelLockDialog(model) , m_pSecurityQuestionAnswer(securityQuestionAnswer) , m_nameText(userName) , m_uid(uid) { m_pSecurityQuestionAnswer = new SecurityQuestionAnswer(); m_listQuestionInfo = m_pSecurityQuestionAnswer->GetUserSecurityQuestions(m_uid, getenv("LANGUAGE")); initUI(); } void ResetPwdWidget::initUI() { QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setAlignment(Qt::AlignCenter); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); mainLayout->addStretch(); QVBoxLayout *titleLayout = new QVBoxLayout(); titleLayout->setAlignment(Qt::AlignHCenter); titleLayout->setContentsMargins(0, 0, 0, 0); m_pTabBar = new KTabBar(KTabBarStyle::SegmentLight, this); QPalette tabBarpal(palette()); tabBarpal.setColor(QPalette::ButtonText, Qt::white); m_pTabBar->setPalette(tabBarpal); m_pTabBar->addTab(tr("Security Question")); m_pTabBar->addTab(tr("Kylin ID")); m_pTabBar->setFocusPolicy(Qt::NoFocus); m_pTabBar->setFixedSize(200, 36); m_pTitleLabel = new CustomLabel(this); m_pTitleLabel->setFontSize(18); m_pTitleLabel->setMinimumHeight(30); m_pTitleLabel->setAlignment(Qt::AlignTop); titleLayout->addWidget(m_pTabBar); titleLayout->addWidget(m_pTitleLabel); mainLayout->addLayout(titleLayout); QPalette pal(palette()); pal.setColor(QPalette::Window, Qt::transparent); m_pQustionMainWidget = new QWidget(this); m_pQustionMainWidget->setPalette(pal); QVBoxLayout *questionLayout = new QVBoxLayout(m_pQustionMainWidget); questionLayout->setAlignment(Qt::AlignHCenter); questionLayout->setContentsMargins(0, 0, 0, 0); questionLayout->setSpacing(0); for (int i = 0; i < m_listQuestionInfo.count(); ++i) { TalksWidget *questionWidget = new TalksWidget(m_modelLockDialog, m_pQustionMainWidget, CONFIDENTIALEDIT); QuestionInfo question = m_listQuestionInfo.at(i); questionWidget->setQuestion(question.strQuestionText); connect(questionWidget, &TalksWidget::textChanged, [=](QString str) { refreshConfirmBtnStatus(); }); connect(questionWidget, &TalksWidget::talkEditClicked, [=] { Q_EMIT resetEditClicked(); }); questionLayout->addWidget(questionWidget); m_listTalkWidget.append(questionWidget); } mainLayout->addWidget(m_pQustionMainWidget); // 麒麟ID QHBoxLayout *verifyKyidLayout = new QHBoxLayout(); verifyKyidLayout->setAlignment(Qt::AlignHCenter); verifyKyidLayout->setContentsMargins(0, 0, 0, 0); m_pVerifyKyidWidget = new VerifyKyidWidget(m_modelLockDialog, this); verifyKyidLayout->addWidget(m_pVerifyKyidWidget); mainLayout->addLayout(verifyKyidLayout); m_pVerifyKyidWidget->hide(); connect(m_pVerifyKyidWidget, &VerifyKyidWidget::textChanged, [=](QString str) { refreshConfirmBtnStatus(); }); connect(m_pVerifyKyidWidget, &VerifyKyidWidget::captchaBtnclicked, this, &ResetPwdWidget::onCaptchaBtnclicked); connect(m_pTabBar, &KTabBar::tabBarClicked, this, [=](int index) { if (index == 0) { m_pQustionMainWidget->show(); m_pVerifyKyidWidget->hide(); m_pMessageLabel->clear(); } else if (index == 1) { m_pQustionMainWidget->hide(); m_pVerifyKyidWidget->show(); m_pVerifyKyidWidget->setFocus(); m_pMessageLabel->clear(); } }); // 错误答案提示 QVBoxLayout *messageLayout = new QVBoxLayout(); messageLayout->setAlignment(Qt::AlignHCenter); messageLayout->setContentsMargins(0, 2, 0, 4); m_pMessageLabel = new CustomLabel(this); m_pMessageLabel->setFontSize(14); m_pMessageLabel->setMinimumHeight(30); m_pMessageLabel->setAlignment(Qt::AlignTop); messageLayout->addWidget(m_pMessageLabel); mainLayout->addLayout(messageLayout); // 确认密码 m_pSureWidget = new QWidget(this); m_pSureWidget->setObjectName("SureWidget"); m_pSureWidget->setStyleSheet("#SureWidget{background-color:transparent; border:none}"); QVBoxLayout *sureLayout = new QVBoxLayout(m_pSureWidget); sureLayout->setContentsMargins(0, 4, 0, 8); sureLayout->setAlignment(Qt::AlignHCenter); sureLayout->setSpacing(0); m_pNewPwdWidget = new TalksWidget(m_modelLockDialog, m_pSureWidget, RESETPWDEDIT); m_pNewPwdWidget->setQuestion(""); m_pNewPwdWidget->setPrompt(tr("Enter new password")); connect(m_pNewPwdWidget, &TalksWidget::textChanged, [=](QString str) { checkPwdLegality(); refreshConfirmBtnStatus(); }); connect(m_pNewPwdWidget, &TalksWidget::talkEditClicked, [=] { Q_EMIT resetEditClicked(); }); sureLayout->addWidget(m_pNewPwdWidget); sureLayout->addSpacing(3); m_pNewPwdTipLabel = new CustomLabel(m_pSureWidget); m_pNewPwdTipLabel->setFontSize(12); m_pNewPwdTipLabel->setMinimumHeight(25); m_pNewPwdTipLabel->setAlignment(Qt::AlignHCenter); sureLayout->addWidget(m_pNewPwdTipLabel); sureLayout->addSpacing(7); QVBoxLayout *sureEditLayout = new QVBoxLayout(); sureEditLayout->setAlignment(Qt::AlignHCenter); sureEditLayout->setContentsMargins(0, 0, 0, 0); m_pSureEdit = new IconEdit(m_modelLockDialog, m_pSureWidget, RESETPWDEDIT); m_pSureEdit->setFixedSize(240, 40); QPalette palett(palette()); palett.setColor(QPalette::Window, Qt::white); m_pSureEdit->setPalette(palett); m_pSureEdit->setPrompt(tr("Confirm your password")); connect(m_pSureEdit, &IconEdit::textChanged, [=](QString str) { if (!str.isEmpty() && str != m_pNewPwdWidget->getEditText()) { m_surePwdTip = tr("Inconsistency with pwd"); } else { m_surePwdTip = ""; } m_pSureTipLabel->setText(m_surePwdTip); checkPwdLegality(); refreshConfirmBtnStatus(); }); connect(m_pSureEdit, &IconEdit::clickedPassword, this, [=] { Q_EMIT resetEditClicked(); }); sureEditLayout->addWidget(m_pSureEdit); sureLayout->addLayout(sureEditLayout); sureLayout->addSpacing(3); m_pSureTipLabel = new CustomLabel(m_pSureWidget); m_pSureTipLabel->setFontSize(12); m_pSureTipLabel->setMinimumHeight(25); m_pSureTipLabel->setAlignment(Qt::AlignHCenter); sureLayout->addWidget(m_pSureTipLabel); mainLayout->addWidget(m_pSureWidget); m_pSureWidget->hide(); QVBoxLayout *buttonLayout = new QVBoxLayout(); buttonLayout->setAlignment(Qt::AlignHCenter); buttonLayout->setContentsMargins(0, 0, 0, 0); buttonLayout->setSpacing(8); m_pConfirmBtn = new KyIconButton(NORMALBUTTON, this); m_pConfirmBtn->setFocusPolicy(Qt::ClickFocus); m_pConfirmBtn->installEventFilter(this); m_pConfirmBtn->setText(tr("Confirm")); m_pConfirmBtn->setFixedSize(240, 40); connect(m_pConfirmBtn, &QPushButton::clicked, this, &ResetPwdWidget::onConfirmBtnclicked); m_pCancelBtn = new QPushButton(this); m_pCancelBtn->setFocusPolicy(Qt::NoFocus); m_pCancelBtn->installEventFilter(this); m_pCancelBtn->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0.15);border-radius: 6px;color: white;}" "QPushButton:hover{background: rgba(255, 255, 255, 0.20);border-radius: 6px;}" "QPushButton:pressed {background: rgba(255, 255, 255, 0.4);border-radius: 6px;}"); m_pCancelBtn->setText(tr("Cancel")); m_pCancelBtn->setFixedSize(240, 40); connect(m_pCancelBtn, &QPushButton::clicked, this, [&]() { setBindIdentity(m_nBindIdentity); clearLineEditText(); m_pSureWidget->hide(); Q_EMIT backToAuthDialog(false); if (m_nBindIdentity == ALLDEFINED) { m_pTabBar->setCurrentIndex(0); } }); buttonLayout->addWidget(m_pConfirmBtn); buttonLayout->addWidget(m_pCancelBtn); mainLayout->addLayout(buttonLayout); mainLayout->addStretch(); } void ResetPwdWidget::makeSurePwqualityEnabled() { #ifdef ENABLEPQ int ret; void *auxerror; char buf[255]; m_pSettings = pwquality_default_settings(); if (m_pSettings == NULL) { m_enablePwdQuality = false; qDebug() << "init pwquality settings failed"; } else { m_enablePwdQuality = true; } ret = pwquality_read_config(m_pSettings, PWCONF, &auxerror); if (ret != 0) { m_enablePwdQuality = false; qDebug() << "Reading pwquality configuration file failed: " << pwquality_strerror(buf, sizeof(buf), ret, auxerror); } else { m_enablePwdQuality = true; } if (PasswdCheckUtil::getCurrentPamState()) m_enablePwdQuality = true; else m_enablePwdQuality = false; #else m_enablePwdQuality = false; #endif } bool ResetPwdWidget::isContainLegitimacyChar(QString word) { foreach (QChar ch, word) { if (int(ch.toLatin1() <= 0 || int(ch.toLatin1()) > 127)) { return false; } } return true; } void ResetPwdWidget::checkPwdLegality() { // 判断用户是否输入了非法字符 if (!isContainLegitimacyChar(m_pNewPwdWidget->getEditText())) { m_newPwdTip = tr("Contains illegal characters!"); } else { if (m_enablePwdQuality) { #ifdef ENABLEPQ void *auxerror; int ret; const char *msg; char buf[256]; QByteArray ba = m_pNewPwdWidget->getEditText().toLatin1(); ret = pwquality_check(m_pSettings, ba.data(), NULL, m_nameText.toLatin1().data(), &auxerror); if (ret == PWQ_ERROR_SAME_PASSWORD) { ret = pwquality_check(m_pSettings, ba.data(), NULL, m_nameText.toLatin1().data(), &auxerror); } if (ret < 0 && m_pNewPwdWidget->getEditText().length() > 0) { msg = pwquality_strerror(buf, sizeof(buf), ret, auxerror); m_newPwdTip = QString(msg); } else { m_newPwdTip = ""; } #endif } else { // 系统未开启pwdquality模块 m_newPwdTip = ""; } } // 防止先输入确认密码,再输入密码后tipLabel无法刷新 if (!m_pSureEdit->text().isEmpty()) { if (QString::compare(m_pNewPwdWidget->getEditText(), m_pSureEdit->text()) == 0) { m_surePwdTip = ""; } else { m_surePwdTip = tr("Inconsistency with pwd"); } } // 设置新密码的提示 if (!m_pNewPwdWidget->getEditText().isEmpty() || m_pSureEdit->hasFocus()) { m_pNewPwdTipLabel->setText(m_newPwdTip); } else if (m_pNewPwdWidget->getEditText().isEmpty()) { m_pNewPwdTipLabel->setText(m_newPwdTip); } m_pSureTipLabel->setText(m_surePwdTip); } void ResetPwdWidget::refreshConfirmBtnStatus() { if (m_pTabBar->currentIndex() == 0 && m_pQustionMainWidget->isVisible()) { bool editTextIsEmpty = false; for (int i = 0; i < m_listQuestionInfo.count(); ++i) { if (m_listTalkWidget.at(i)->getEditText().isEmpty()) { editTextIsEmpty = true; break; } } if (!editTextIsEmpty) { m_pConfirmBtn->setEnabled(true); } else { m_pConfirmBtn->setEnabled(false); } } else if ((m_pTabBar->currentIndex() == 1 || m_pTabBar->isHidden()) && m_pVerifyKyidWidget->isVisible()) { if (m_pVerifyKyidWidget->getKylinIdEditText().isEmpty() || m_pVerifyKyidWidget->getPwdEditText().isEmpty() || m_pVerifyKyidWidget->getCaptchaEditText().isEmpty()) { m_pConfirmBtn->setEnabled(false); } else { m_pConfirmBtn->setEnabled(true); } } else if (m_pSureWidget->isVisible()) { if (m_pNewPwdWidget->getEditText().isEmpty() || m_pNewPwdWidget->getEditText() == tr("Enter new password") || m_pSureEdit->text().isEmpty() || m_pSureEdit->text() == tr("Confirm your password") || !m_newPwdTip.isEmpty() || !m_surePwdTip.isEmpty()) { m_pConfirmBtn->setEnabled(false); } else { m_pConfirmBtn->setEnabled(true); } } } void ResetPwdWidget::onCaptchaBtnclicked() { QDBusInterface interface( "org.kylinID.service", "/org/kylinID/path", "org.kylinID.interface", QDBusConnection::systemBus()); QDBusMessage result = interface.call("checkKyidAccounts", m_uid, m_pVerifyKyidWidget->getKylinIdEditText()); QList varResult = result.arguments(); if (varResult.size() > 0 && varResult.takeFirst().toBool()) { // 验证麒麟ID账户是否输入正确 // 异步调用,防止阻塞主进程 QDBusPendingCall call = interface.asyncCall( QStringLiteral("getCodeByKyidAccounts"), m_pVerifyKyidWidget->getKylinIdEditText()); // 获取验证码 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (!reply.isValid()) { qWarning() << "getCodeByKyidAccounts error: " << reply.error().message(); m_pMessageLabel->setText(tr("The service is abnormal")); } else { QStringList resultList = reply.value(); qDebug() << " getCodeByKyidAccounts reply: " << resultList; if (resultList.count() > 1) { QString code = resultList.at(0); if (code.toInt() == 200) { // 只有返回值为200时为认证成功 m_pVerifyKyidWidget->onCaptchaBtnclicked(true); QString message = tr("The SMS verification code has been sent to the mobile phone with the " "tail number %1 (Valid for 5 minutes)") .arg(extractContinuousNumbers(resultList.at(1))); Q_EMIT getCodeByKyidAccountsed(message); m_pMessageLabel->clear(); } else { qWarning() << "getCodeByKyidAccounts error: " << resultList; m_pVerifyKyidWidget->onCaptchaBtnclicked(false); m_pMessageLabel->setText(tr("Failed to get the verification code")); } } else { qWarning() << "getCodeByKyidAccounts error: " << resultList; m_pVerifyKyidWidget->onCaptchaBtnclicked(false); m_pMessageLabel->setText(tr("Failed to get the verification code")); } } watcher->deleteLater(); }); } else { qWarning() << " checkKyidAccounts error!!! "; m_pVerifyKyidWidget->onCaptchaBtnclicked(false); m_pMessageLabel->setText(tr("The account does not exist, please re-enter it")); } } void ResetPwdWidget::onConfirmBtnclicked() { if (m_pTabBar->currentIndex() == 0 && m_pQustionMainWidget->isVisible()) { m_verifyToken = ""; QList listAnswerInfo; for (int i = m_listQuestionInfo.count() - 1; i >= 0; i--) { AnswerInfo answerInfo; answerInfo.nQuestionId = m_listQuestionInfo.at(i).nQuestionId; answerInfo.strAnswerText = m_pSecurityQuestionAnswer->EncodeSecurityInfo(m_listTalkWidget.at(i)->getEditText()); listAnswerInfo.append(answerInfo); } m_verifyToken = m_pSecurityQuestionAnswer->VerifyUserSecurityAnswers(m_uid, listAnswerInfo); qDebug() << "Verify ret:" << m_verifyToken; if (!m_verifyToken.isEmpty()) { makeSurePwqualityEnabled(); switchToResetPwdWdg(); m_nCurrentState = SECURITYQUESTION; } else { verifyFailed(); } } else if ((m_pTabBar->currentIndex() == 1 || m_pTabBar->isHidden()) && m_pVerifyKyidWidget->isVisible()) { m_verifyToken = ""; QByteArray pwdByteArray = m_pVerifyKyidWidget->getPwdEditText().toUtf8(); QByteArray pwd; QByteArray publickeyByteQrray = ("-----BEGIN PUBLIC KEY-----\n" "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJlLIik423nKkNY/TN4BCsDqfuyR1+Wx\n" "yzpHZJrP4R2XChrzHluHkJXvQaEA8jgCp74Q1NY/5TIJQlyiXktEHm0CAwEAAQ==\n" "-----END PUBLIC KEY-----\n"); KyRSAC rsac; rsac.encryptData(pwdByteArray, pwd, publickeyByteQrray); QStringList result = m_pSecurityQuestionAnswer->VerifyAccountsInfo( m_uid, m_pVerifyKyidWidget->getKylinIdEditText(), pwd.toBase64(), m_pVerifyKyidWidget->getCaptchaEditText()); qDebug() << " VerifyAccountsInfo result = " << result; if (result.isEmpty()) { m_pMessageLabel->setText(tr("The service is abnormal")); } else if (result.count() == 2) { qDebug() << " error code = " << result.at(0) << " error = " << result.at(1); // 麒麟ID服务端修改,直接返回已翻译错误 m_pMessageLabel->setText(result.at(1)); } else if (result.count() == 3) { m_verifyToken = result.at(2); } if (!m_verifyToken.isEmpty()) { makeSurePwqualityEnabled(); switchToResetPwdWdg(); m_nCurrentState = KYLINID; } } else { if (!m_verifyToken.isEmpty()) { if (m_nCurrentState == SECURITYQUESTION) { int nRet = m_pSecurityQuestionAnswer->ChangePassword(m_uid, m_pSureEdit->text(), m_verifyToken); qDebug() << "SecurityQuestionAnswer ChangePwd ret:" << nRet; if (!nRet) { m_pQustionMainWidget->show(); m_pMessageLabel->show(); m_pSureWidget->hide(); clearLineEditText(); if (m_nBindIdentity == ALLDEFINED) { m_pTabBar->setCurrentIndex(0); } Q_EMIT backToAuthDialog(true); m_pSecurityQuestionAnswer->SetUserPasswdHint(m_uid, ""); Q_EMIT hideKylinIDCodesTips(); } else { qInfo() << "SecurityQuestionAnswer ChangePwd failed!!!"; } } else if (m_nCurrentState == KYLINID) { int nRet = m_pSecurityQuestionAnswer->ChangePassword2(m_uid, m_pSureEdit->text(), m_verifyToken); qDebug() << "KylinID ChangePwd ret:" << nRet; if (!nRet) { m_pQustionMainWidget->show(); m_pMessageLabel->show(); m_pSureWidget->hide(); clearLineEditText(); if (m_nBindIdentity == ALLDEFINED) { m_pTabBar->setCurrentIndex(0); } Q_EMIT backToAuthDialog(true); m_pSecurityQuestionAnswer->SetUserPasswdHint(m_uid, ""); Q_EMIT hideKylinIDCodesTips(); } else { qInfo() << "KylinID ChangePwd failed!!!"; } } } } } void ResetPwdWidget::updateResetPwdSize() { m_pSureWidget->setFixedWidth(width()); int talkWidgetHeight = 0; for (int i = 0; i < m_listTalkWidget.count(); ++i) { m_listTalkWidget.at(i)->setFixedWidth(width()); talkWidgetHeight += m_listTalkWidget.at(i)->getTalksWdgHeight(); } m_pQustionMainWidget->setFixedSize(width(), talkWidgetHeight + 8 * 2); } void ResetPwdWidget::switchToResetPwdWdg() { m_pQustionMainWidget->hide(); m_pSureWidget->show(); m_pNewPwdWidget->setFocus(); m_pVerifyKyidWidget->hide(); m_pTabBar->hide(); if (m_nBindIdentity == ALLDEFINED && m_pTabBar->currentIndex() == 1) { m_pTitleLabel->setText(tr("Verify Kylin ID Reset Password")); Q_EMIT hideKylinIDCodesTips(); } else if (m_nBindIdentity == ALLDEFINED && m_pTabBar->currentIndex() == 0) { m_pTitleLabel->setText(tr("Answer the security questions and reset your password")); } m_pTitleLabel->show(); clearLineEditText(); refreshConfirmBtnStatus(); } void ResetPwdWidget::switchToConfidentialDig() { m_pQustionMainWidget->show(); m_pMessageLabel->show(); m_pSureWidget->hide(); clearLineEditText(); } void ResetPwdWidget::setBindIdentity(int type) { m_nBindIdentity = type; if (m_nBindIdentity == SECURITYQUESTION) { m_pTitleLabel->setText(tr("Answer the security questions and reset your password")); m_pTitleLabel->show(); m_pQustionMainWidget->show(); m_pVerifyKyidWidget->hide(); m_pTabBar->hide(); } else if (m_nBindIdentity == KYLINID) { m_pTitleLabel->setText(tr("Verify Kylin ID Reset Password")); m_pTitleLabel->show(); m_pQustionMainWidget->hide(); m_pVerifyKyidWidget->show(); m_pVerifyKyidWidget->setFocus(); m_pTabBar->hide(); } else if (m_nBindIdentity == ALLDEFINED) { m_pTitleLabel->setText(tr("")); m_pTitleLabel->hide(); m_pQustionMainWidget->show(); m_pVerifyKyidWidget->hide(); m_pTabBar->show(); m_pTabBar->setCurrentIndex(0); } } void ResetPwdWidget::updateFontSize(double lfFontSize) { if (m_pNewPwdWidget) { m_pNewPwdWidget->updateFontSize(lfFontSize); } for (int i = 0; i < m_listTalkWidget.count(); ++i) { m_listTalkWidget.at(i)->updateFontSize(lfFontSize); } } void ResetPwdWidget::verifyFailed() { m_pMessageLabel->setText(tr("One or more wrong answers")); } void ResetPwdWidget::setFocusToEdit() { if (m_listTalkWidget.count() > 0) { m_listTalkWidget.at(0)->setFocus(); } refreshConfirmBtnStatus(); } void ResetPwdWidget::clearLineEditText() { for (int i = 0; i < m_listTalkWidget.count(); ++i) { m_listTalkWidget.at(i)->clearText(); } m_pNewPwdWidget->clearText(); m_pSureEdit->clearText(); m_pMessageLabel->clear(); m_pSureTipLabel->clear(); m_pNewPwdTipLabel->clear(); m_pVerifyKyidWidget->clearText(); } void ResetPwdWidget::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Escape: break; case Qt::Key_Enter: if (m_pConfirmBtn->isEnabled()) { m_pConfirmBtn->clicked(); m_pConfirmBtn->setFocus(); } break; case Qt::Key_Return: if (m_pConfirmBtn->isEnabled()) { m_pConfirmBtn->clicked(); m_pConfirmBtn->setFocus(); } break; default: QWidget::keyPressEvent(event); } } bool ResetPwdWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::MouseButtonPress && (obj == m_pConfirmBtn || obj == m_pCancelBtn)) { Q_EMIT resetEditClicked(); } return QWidget::eventFilter(obj, event); } ukui-screensaver/src/widgets/loginplugininterface.h0000664000175000017500000000653615172041035021621 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LOGINPLUGININTERFACE_H #define LOGINPLUGININTERFACE_H #include #include #include #define LOGINPLUGIN_VERSION "1.0.0" /** * @brief 登录插件接口类 * */ class LoginPluginInterface { public: /** * @brief 插件模块类型枚举:认证、工具、通知 * */ enum ModuleType { MODULETYPE_AUTH, MODULETYPE_TOOL, MODULETYPE_WINDOW, MODULETYPE_NOTIFY, MODULETYPE_MAX }; /** * @brief 登录插件环境:登录、锁屏 * */ enum LoginPluginEnv { LOGINPLUGINENV_LOGIN, // 登录 LOGINPLUGINENV_LOCKSCREEN, // 锁屏 }; /** * @brief 构造函数 * */ LoginPluginInterface() {} /** * @brief 析构函数 * */ virtual ~LoginPluginInterface() {} /** * @brief 获取插件展示名称 * @return 插件展示名称 */ virtual QString getPluginName() = 0; /** * @brief 获取插件唯一名称 * @return 插件唯一名称 */ virtual QString name() = 0; /** * @brief 获取插件类型 * @return 插件类型 */ virtual int getPluginType() = 0; /** * @brief 获取插件图标 * @return 插件图标路径(绝对路径或主题图标名称) */ virtual QString icon() = 0; /** * @brief 获取插件主窗口句柄 * @param 父窗口句柄 * @return 插件主窗口句柄 */ virtual QWidget *getPluginMainWnd(QWidget *parent = nullptr) = 0; /** * @brief 插件是否需要加载 * @return true 加载;false 不加载 */ virtual bool needLoad() = 0; /** * @brief 设置主窗口背景 * @param 背景图路径 * @return 无 */ virtual void setBackground(QString strPicPath) {} /** * @brief 获取登录插件环境 * @return LoginPluginEnv */ virtual int getLoginPluginEnv() = 0; /** * @brief 获取插件接口版本信息 * @return version */ QString getVersion() { return LOGINPLUGIN_VERSION; } /** * @brief onMessage 主程序向插件传递消息数据 * @param strMsgJson json格式数据 * @return json格式数据 */ virtual QString onMessage(QString strMsgJson) = 0; /** * @brief onRequest 插件向主程序请求数据 * @param strReqJson json格式数据 * @return json格式数据 */ virtual QString onRequest(QString strReqJson) = 0; }; #define LoginPluginInterfaceIID "org.ukui.LoginPluginInterface" Q_DECLARE_INTERFACE(LoginPluginInterface, LoginPluginInterfaceIID) #endif // LOGINPLUGININTERFACE_H ukui-screensaver/src/widgets/tiplabel.cpp0000664000175000017500000000512715172041106017532 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "tiplabel.h" #include "customlabel.h" #include #include #include #include #include #include TipLabel::TipLabel(QWidget *parent) : QWidget(parent) { initUI(); } void TipLabel::initUI() { QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(8, 8, 8, 8); layout->setAlignment(Qt::AlignCenter); layout->setSpacing(10); m_pIconLabel = new QLabel(this); m_pIconLabel->setPixmap(QIcon::fromTheme("dialog-info").pixmap(16, 16)); m_pTextLabel = new CustomLabel(this); m_pTextLabel->setFontSize(14); layout->addWidget(m_pIconLabel); layout->addWidget(m_pTextLabel); } void TipLabel::setText(QString tip) { m_pTextLabel->setText(tip); // Ensure that we get correct sizeHints by placing this window on the right screen. QFontMetrics fm(m_pTextLabel->font()); QSize extra(1, 0); // Make it look good with the default ToolTip font on Mac, which has a small descent. if (fm.descent() == 2 && fm.ascent() >= 11) ++extra.rheight(); m_pTextLabel->setWordWrap(Qt::mightBeRichText(m_pTextLabel->text())); QSize sh = sizeHint(); if (parentWidget()) { const qreal screenWidth = parentWidget()->geometry().width(); if (!m_pTextLabel->wordWrap() && sh.width() > screenWidth) { m_pTextLabel->setWordWrap(true); sh = m_pTextLabel->sizeHint(); } } m_pTextLabel->resize(sh + extra); } void TipLabel::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QStylePainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(Qt::NoBrush); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.25); QRect rect = this->rect(); painter.drawRoundedRect(rect.adjusted(1, 1, -1, -1), 6, 6); painter.restore(); return QWidget::paintEvent(event); } ukui-screensaver/src/widgets/poweritemwidget.h0000664000175000017500000000302715172041106020617 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef POWERITEMWIDGET_H #define POWERITEMWIDGET_H #include #include "customlabel.h" class StatusButton; class PowerItemWidget : public QFrame { Q_OBJECT public: explicit PowerItemWidget(QWidget *parent = 0); ~PowerItemWidget(); void setText(const QString text); void setToolTip(const QString tip); void setIcon(const QIcon &icon); /** * @brief 设置按钮当前状态 * @param selected 是否选中 * @return */ void setSelected(int status); void setFontFamily(QString fontFamily); void setFontSize(double fontSize); protected: bool eventFilter(QObject *obj, QEvent *event); Q_SIGNALS: /** * @brief 响应点击事件 * @return */ void clicked(); private: StatusButton *itemFace = nullptr; CustomLabel *textLabel = nullptr; }; #endif // POWERITEMWIDGET_H ukui-screensaver/src/widgets/sessionlistwidget.cpp0000664000175000017500000000530315172041035021516 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "sessionlistwidget.h" #include #include "sessionitemwidget.h" SessionListWidget::SessionListWidget(LockDialogModel *model, QWidget *parent) : MyListWidget(parent), m_modelLockDialog(model) { initUI(); initConnections(); } void SessionListWidget::initUI() { QString userListStyle = "QListWidget{ background-color: rgba(255,255,255,15%); border-radius: 8px; padding: 5px 5px 5px 5px;}" "QListWidget::item{background:rgba(255,255,255,0%);height:40px; border-radius:4px}" "QListWidget::item:hover{color:#333333; background-color:rgba(255,255,255,20%)}" "QListWidget::item::selected{border-radius: 4px;background-color:rgba(255,255,255,40%);}"; setStyleSheet(userListStyle); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); verticalScrollBar()->setProperty("drawScrollBarGroove", false); onUpdateListInfo(); } void SessionListWidget::initConnections() { // 响应点击事件 connect(this, &SessionListWidget::itemClicked, this, &SessionListWidget::onListItemClicked); } void SessionListWidget::onUpdateListInfo() { this->clear(); for (auto session : m_modelLockDialog->sessionsInfo()) { QListWidgetItem *userItem = new QListWidgetItem(); userItem->setSizeHint(QSize(400, 40)); insertItem(count(), userItem); SessionItemWidget *itemWidget = new SessionItemWidget(this); itemWidget->setSessionName(session); setItemWidget(userItem, itemWidget); } } void SessionListWidget::onListItemClicked(QListWidgetItem *item) { QWidget *widget = itemWidget(item); SessionItemWidget *currentItem = qobject_cast(widget); if (currentItem) { this->hide(); Q_EMIT sessionSelected(currentItem->sessionName()); } } void SessionListWidget::updateWidgetSize() { setFixedWidth(420); if (maxHeight() >= 0 && (count() * 40 + 10) > maxHeight()) { setFixedHeight(maxHeight()); } else { setFixedHeight(count() * 40 + 10); } adjustSize(); } ukui-screensaver/src/widgets/mytabwidget.h0000664000175000017500000000167015172041035017723 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef MYTABWIDGET_H #define MYTABWIDGET_H #include #include class MyTabWidget : public QTabWidget { Q_OBJECT public: explicit MyTabWidget(QWidget *parent = nullptr); void paintEvent(QPaintEvent *event); }; #endif // WIDGET_H ukui-screensaver/src/widgets/tiplabel.h0000664000175000017500000000213115172041106017167 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef TIPLABEL_H #define TIPLABEL_H #include #include class CustomLabel; class TipLabel : public QWidget { Q_OBJECT public: explicit TipLabel(QWidget *parent = nullptr); void setText(QString tip); protected: void paintEvent(QPaintEvent *event); private: void initUI(); private: QLabel *m_pIconLabel = nullptr; CustomLabel *m_pTextLabel = nullptr; }; #endif // TIPLABEL_H ukui-screensaver/src/widgets/useritemwidget.cpp0000664000175000017500000000636315172041106021002 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "useritemwidget.h" #include #include #include #include #include #include #include #include "commonfunc.h" #include "customlabel.h" UserItemWidget::UserItemWidget(QWidget *parent) : QWidget(parent) { setAttribute(Qt::WA_DeleteOnClose); init(); initIconLabel(); } UserItemWidget::~UserItemWidget() {} bool UserItemWidget::eventFilter(QObject *obj, QEvent *event) { return QWidget::eventFilter(obj, event); } void UserItemWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QStyleOption opt; opt.initFrom(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void UserItemWidget::init() { QHBoxLayout *layout = new QHBoxLayout(); layout->setAlignment(Qt::AlignCenter); layout->setContentsMargins(8, 4, 8, 4); layout->setSpacing(4); m_labelHeadImg = new QLabel(this); m_labelNickName = new CustomLabel(this); m_labelNickName->setStyleSheet("color: white"); m_labelNickName->setFixedSize(364, 32); layout->addWidget(m_labelHeadImg); layout->addWidget(m_labelNickName); setLayout(layout); } void UserItemWidget::initIconLabel() { m_labelHeadImg->setFixedSize(32, 32); QVBoxLayout *layoutImg = new QVBoxLayout(m_labelHeadImg); layoutImg->setContentsMargins(18, 0, 0, 18); layoutImg->setAlignment(Qt::AlignCenter); m_labelLoggedIn = new QLabel(this); layoutImg->addWidget(m_labelLoggedIn); } void UserItemWidget::setUserName(QString name) { m_strUserName = name; } QString UserItemWidget::getUserName() { return m_strUserName; } void UserItemWidget::setUserNickName(QString strNickName) { m_strNickName = strNickName; m_labelNickName->setText(strNickName); } void UserItemWidget::setUserPixmap(QPixmap userIcon) { userIcon = scaledPixmap(userIcon); userIcon = userIcon.scaled(32, 32, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); userIcon = PixmapToRound(userIcon, 16); m_labelHeadImg->setPixmap(userIcon); } void UserItemWidget::setUserStatus(bool status) { if (status) { QPixmap status(":/image/assets/selected.svg"); status = scaledPixmap(status); status = status.scaled(14, 14, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); status = PixmapToRound(status, 7); m_labelLoggedIn->setPixmap(status); } } void UserItemWidget::setFontFamily(QString fontFamily) { m_labelNickName->setFontFamily(fontFamily); } void UserItemWidget::setFontSize(double fontSize) { m_labelNickName->setFontSize(fontSize); } ukui-screensaver/src/widgets/screensaverwidget.cpp0000664000175000017500000001532315172041106021461 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screensaverwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" QSharedPointer ScreenSaverWidget::m_ssPluginPtr = nullptr; ScreenSaverWidget::ScreenSaverWidget(ScreenSaver *screensaver, QPixmap *background, QWidget *parent) : QWidget(parent), timer(nullptr), screensaver(screensaver), closing(false), m_background(background) { qDebug() << "ScreenSaverWidget::ScreenSaverWidget"; // setAttribute(Qt::WA_DeleteOnClose); setMouseTracking(true); setFocus(); this->installEventFilter(this); if (screensaver) { screensaver->setParent(this); } QPalette pal(palette()); pal.setColor(QPalette::Window, Qt::black); // 设置背景黑色 setAutoFillBackground(true); setPalette(pal); switch (screensaver->mode) { case SAVER_RANDOM: case SAVER_SINGLE: embedXScreensaver(screensaver->path); break; case SAVER_BLANK_ONLY: break; case SAVER_IMAGE: { setAutoFillBackground(true); screensaver->startSwitchImages(); QPalette plt; plt.setBrush(QPalette::Window, Qt::black); setPalette(plt); connect(screensaver, &ScreenSaver::imagePathChanged, this, &ScreenSaverWidget::onBackgroundChanged); break; } case SAVER_DEFAULT: embedXScreensaver(screensaver->path); break; case SAVER_DEFAULT_CUSTOM: embedXScreensaver(screensaver->path); break; default: break; } show(); } ScreenSaverWidget::~ScreenSaverWidget() {} void ScreenSaverWidget::closeEvent(QCloseEvent *event) { qDebug() << "ScreenSaverWidget::closeEvent---beginStop"; if (process.state() != QProcess::NotRunning) { process.kill(); process.waitForFinished(200); } if (!closing) { closing = true; screensaver->stopSwitchImages(); delete screensaver; if (timer && timer->isActive()) timer->stop(); } qDebug() << "ScreenSaverWidget::closeEvent---endStop"; return QWidget::closeEvent(event); } void ScreenSaverWidget::paintEvent(QPaintEvent *event) { if (!screensaver->exists()) { QPainter painter(this); painter.fillRect(0, 0, this->width(), this->height(), Qt::black); } if (screensaver->mode == SAVER_IMAGE) { switch (screensaver->effect) { case TRANSITION_NONE: { QPixmap pixmap(screensaver->path); pixmap.scaled(size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QPainter painter(this); painter.drawPixmap(0, 0, this->width(), this->height(), pixmap); break; } case TRANSITION_FADE_IN_OUT: { QPainter painter(this); QPixmap pixmap1(screensaver->lastPath); pixmap1.scaled(size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter.setOpacity(opacity); painter.drawPixmap(0, 0, this->width(), this->height(), pixmap1); QPixmap pixmap(screensaver->path); pixmap.scaled(size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter.setOpacity(1 - opacity); painter.drawPixmap(0, 0, this->width(), this->height(), pixmap); break; } } } return QWidget::paintEvent(event); } bool ScreenSaverWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == 23) { if (QApplication::platformName() == "xcb") { Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; return false; } XSetInputFocus(display, this->winId(), RevertToParent, CurrentTime); } } return false; } /* Embed xscreensavers */ void ScreenSaverWidget::embedXScreensaver(const QString &path) { qDebug() << "embedXScreensaver path = " << path; /*屏保模式为ukui或者自定义时,直接加载对象,为其他屏保时,使用调用命令方式实现*/ if (screensaver->mode == SAVER_SINGLE) { QString cmd = path + " -window-id " + QString::number(winId()); if (process.state() == QProcess::NotRunning) process.start(cmd); } else if (screensaver->mode == SAVER_DEFAULT || screensaver->mode == SAVER_DEFAULT_CUSTOM) { initScreenSaverPlugin(); } } void ScreenSaverWidget::initScreenSaverPlugin() { if (!m_ssPluginPtr) { QPluginLoader pluginLoader("/usr/lib/ukui-screensaver/libscreensaver-default.so"); pluginLoader.load(); QObject *plugin = pluginLoader.instance(); if (plugin) { m_ssPluginPtr.reset(qobject_cast(plugin)); } else { qWarning() << "pluginLoader '/usr/lib/ukui-screensaver/libscreensaver-default.so' failed"; } } if (m_ssPluginPtr && !m_wdgScreenSaver) { m_wdgScreenSaver = m_ssPluginPtr->createWidget(true, m_background, this); m_wdgScreenSaver->show(); } } void ScreenSaverWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); if (m_wdgScreenSaver) { m_wdgScreenSaver->setGeometry(0, 0, this->width(), this->height()); } } void ScreenSaverWidget::onBackgroundChanged(const QString & /*path*/) { switch (screensaver->effect) { case TRANSITION_NONE: repaint(); break; case TRANSITION_FADE_IN_OUT: opacity = 1.0; timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [&] { opacity -= 0.1; if (opacity <= 0) timer->stop(); else repaint(); }); timer->start(50); break; } } ukui-screensaver/src/widgets/circlelabel.h0000664000175000017500000000212415172041106017636 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef CIRCLELABEL_H #define CIRCLELABEL_H #include #include class CircleLabel : public QLabel { Q_OBJECT public: CircleLabel(QWidget *parent = nullptr); CircleLabel(QPixmap pixmap, QWidget *parent = nullptr); ~CircleLabel(); void setPixmap(QPixmap pixmap); protected: void paintEvent(QPaintEvent *event) override; private: QPixmap mPixmap; }; #endif // CIRCLELABEL_H ukui-screensaver/src/widgets/customtabbar.cpp0000664000175000017500000000566715172041106020435 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "customtabbar.h" #include #include #include #include #include #include CustomTabBar::CustomTabBar(QWidget *parent) : QTabBar(parent) { } void CustomTabBar::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QStylePainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); int radius = 12; for (int i = 0; i < count(); ++i) { QStyleOptionTab opt; initStyleOption(&opt, i); QRect r = this->tabRect(i); QPainterPath path; if (count() == 1) { // 只有一个tab,左上和右上都画圆角 path.moveTo(r.left() + radius, r.top()); path.lineTo(r.right() - radius, r.top()); path.arcTo(r.right() - radius * 2, r.top(), radius * 2, radius * 2, 90, -90); // 右上圆角 path.lineTo(r.right(), r.bottom()); path.lineTo(r.left(), r.bottom()); path.lineTo(r.left(), r.top() + radius); path.arcTo(r.left(), r.top(), radius * 2, radius * 2, 180, -90); // 左上圆角 path.closeSubpath(); } else if (i == 0) { // 左侧tab:左上圆角,其他直角 path.moveTo(r.left() + radius, r.top()); path.lineTo(r.right(), r.top()); path.lineTo(r.right(), r.bottom()); path.lineTo(r.left(), r.bottom()); path.lineTo(r.left(), r.top() + radius); path.arcTo(r.left(), r.top(), radius * 2, radius * 2, 180, -90); path.closeSubpath(); } else if (i == count() - 1) { // 右侧tab:右上圆角,其他直角 path.moveTo(r.left(), r.top()); path.lineTo(r.right() - radius, r.top()); path.arcTo(r.right() - radius * 2, r.top(), radius * 2, radius * 2, 90, -90); // 右上圆角 path.lineTo(r.right(), r.bottom()); path.lineTo(r.left(), r.bottom()); path.closeSubpath(); } else { // 其他tab:全直角 path.addRect(r); } painter.save(); painter.setClipPath(path); style()->drawControl(QStyle::CE_TabBarTab, &opt, &painter, this); painter.restore(); } } ukui-screensaver/src/widgets/sessionitemwidget.h0000664000175000017500000000341315172041035021146 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SESSIONITEMWIDGET_H #define SESSIONITEMWIDGET_H #include #include class userLabel; class QLabel; class QPixmap; class SessionItemWidget : public QWidget { Q_OBJECT public: SessionItemWidget(QWidget *parent = 0); ~SessionItemWidget(); /** * @brief 设置会话名 * @param name 会话名 * @return */ void setSessionName(QString name); /** * @brief 设置会话类型 * @param nType 会话类型 * @return */ void setSessionType(int nType); inline QString sessionName() { return m_strSessionName; } inline QString sessionImgPath() { return m_strImgPath; } protected: bool eventFilter(QObject *obj, QEvent *event); virtual void paintEvent(QPaintEvent *event); private: void init(); void initIconLabel(); Q_SIGNALS: void enterWidget(); void leaveWidget(); private: QLabel *m_labelImg = nullptr; QLabel *m_labelName = nullptr; QString m_strSessionName; int m_nSessionType; QString m_strImgPath; }; #endif // SESSIONITEMWIDGET_H ukui-screensaver/src/widgets/biobuttonlistwidget.cpp0000664000175000017500000001334115172041035022041 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "biobuttonlistwidget.h" #include "statusbutton.h" #include "commonfunc.h" #include "definetypes.h" #include "loginauthinterface.h" #include "pluginsloader.h" BioButtonListWidget::BioButtonListWidget(QWidget *parent) : MyListWidget(parent) { // installEventFilter(this); initUI(); initConnections(); } void BioButtonListWidget::initUI() { this->setFlow(QListWidget::LeftToRight); this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QString buttonListStyle = "QListWidget{ background:rgba(255, 255, 255, 0%); border: 0px;}" "QListWidget::item{border-radius: 4px; background:rgba(255, 255, 255, 0%);}"; this->setStyleSheet(buttonListStyle); this->setSpacing(8); } void BioButtonListWidget::initConnections() { // 响应点击事件 connect(this, &BioButtonListWidget::itemClicked, this, &BioButtonListWidget::onListItemClicked); connect(this, &BioButtonListWidget::currentItemChanged, this, &BioButtonListWidget::onListItemChanged); } void BioButtonListWidget::resizeEvent(QResizeEvent *event) { updateUI(); } void BioButtonListWidget::addOptionButton(unsigned uLoginOptType, int nDrvId, QString strDrvName) { m_listDriveId.append(nDrvId); StatusButton *buttonWidget = new StatusButton(this, BOTBUTTON); QListWidgetItem *buttonItem = new QListWidgetItem(); buttonItem->setSizeHint(QSize(48, 48)); this->insertItem(this->count(), buttonItem); this->setItemWidget(buttonItem, buttonWidget); buttonWidget->installEventFilter(this); this->addItem(buttonItem); QPixmap iconPixmap; switch (uLoginOptType) { case LOGINOPT_TYPE_PASSWORD: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-password.svg", "white", 16); break; case LOGINOPT_TYPE_GENERAL_UKEY: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-ukey.svg", "white", 16); break; case LOGINOPT_TYPE_FACE: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-face.svg", "white", 16); break; case LOGINOPT_TYPE_FINGERPRINT: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-finger.svg", "white", 16); break; case LOGINOPT_TYPE_IRIS: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-iris.svg", "white", 16); break; case LOGINOPT_TYPE_VOICEPRINT: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-voice.svg", "white", 16); break; case LOGINOPT_TYPE_FINGERVEIN: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-fingervein.svg", "white", 16); break; case LOGINOPT_TYPE_QRCODE: iconPixmap = loadSvg(":/image/assets/ukui-loginopt-qrcode.svg", "white", 16); break; case LOGINOPT_TYPE_CUSTOM: LoginAuthInterface *plugin = dynamic_cast( PluginsLoader::instance().findModulesByType(LoginPluginInterface::MODULETYPE_AUTH).values().first()); QString strIcon = plugin->icon(); if (strIcon.startsWith("/")) { if (strIcon.endsWith(".svg")) { iconPixmap = loadSvg(strIcon, "white", 16); } else { iconPixmap.load(strIcon); iconPixmap.scaled(40, 40); } } else { iconPixmap = QIcon::fromTheme(strIcon).pixmap(48, 48).scaled(40, 40); } if (iconPixmap.isNull()) { iconPixmap = loadSvg(":/images/ukui-loginopt-custom.svg", "white", 16); } break; } buttonWidget->setIcon(iconPixmap); buttonWidget->setToolTip(strDrvName); connect(buttonWidget, SIGNAL(buttonClicked(int)), this, SLOT(onOptionSelected(int))); } void BioButtonListWidget::onOptionSelected(int nIndex) { if (nIndex < 0) return; } void BioButtonListWidget::updateUI() { this->setSpacing(8 * scale); QList statusButton = this->findChildren(); for (auto button : statusButton) { button->setFixedSize(48 * scale, 48 * scale); button->setIconSize(QSize(24 * scale, 24 * scale)); } } void BioButtonListWidget::onListItemClicked(QListWidgetItem *item) { QWidget *widget = itemWidget(item); StatusButton *currentItem = qobject_cast(widget); if (currentItem) { currentItem->setClickedStatus(CLICKED); currentItem->buttonClicked(currentItem->getDrivedId()); } } void BioButtonListWidget::onListItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QWidget *currentWidget = itemWidget(current); StatusButton *currentItem = static_cast(currentWidget); if (currentItem) currentItem->setClickedStatus(SELECT); if (previous) { QWidget *previousWidget = itemWidget(previous); StatusButton *previousItem = static_cast(previousWidget); if (previousItem) previousItem->setClickedStatus(NORMAL); } } ukui-screensaver/src/widgets/mytabwidget.cpp0000664000175000017500000000262515172041106020256 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "mytabwidget.h" #include #include #include #include #include "customtabbar.h" MyTabWidget::MyTabWidget(QWidget *parent) : QTabWidget(parent) { CustomTabBar *tabBar = new CustomTabBar(this); setTabBar(tabBar); } void MyTabWidget::paintEvent(QPaintEvent *event) { QPalette pal = qApp->palette(); QColor colorPal = pal.color(QPalette::Base); //设置窗体为圆角 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 反锯齿; painter.setBrush(colorPal); painter.setPen(Qt::transparent); auto rect = this->rect(); painter.drawRoundedRect(rect, 12, 12); //窗口圆角 return QWidget::paintEvent(event); } ukui-screensaver/src/widgets/networkbutton.cpp0000664000175000017500000001165415172041106020665 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #if SUPPORT_NET_ICON #include "networkbutton.h" #include #include #include #include #include "commonfunc.h" NetWorkButton::NetWorkButton(QWidget *parent) : KyNetworkIcon(parent) { setFixedSize(48, 48); setIconSize(QSize(24, 24)); setFocusPolicy(Qt::NoFocus); } void NetWorkButton::setClickedStatus(int status) { curStatus = status; update(); } void NetWorkButton::paintEvent(QPaintEvent *e) { Q_UNUSED(e); QStylePainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QStyleOptionButton option; initStyleOption(&option); // if (curStatus == NORMAL) { // painter.save(); // painter.setPen(Qt::NoPen); // painter.setBrush(Qt::white); // painter.setOpacity(0); // painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); // painter.restore(); // } // if (option.state & QStyle::State_MouseOver) { // painter.save(); // painter.setPen(Qt::NoPen); // painter.setBrush(Qt::white); // painter.setOpacity(0.25); // painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); // painter.restore(); // } if (option.state & QStyle::State_MouseOver) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.15); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); painter.restore(); } if (curStatus == CLICKED) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.35); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); painter.restore(); } if (curStatus == SELECT) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.25); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); painter.restore(); painter.save(); QStyleOption opt; QColor color = opt.palette.color(QPalette::Highlight); painter.setPen(QPen(color, 2)); painter.setBrush(Qt::NoBrush); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); painter.restore(); } if (option.state & QStyle::State_HasFocus) { painter.save(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); painter.setOpacity(0.25); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); painter.restore(); painter.save(); QStyleOption opt; QColor color = opt.palette.color(QPalette::Highlight); painter.setPen(QPen(color, 2)); painter.setBrush(Qt::NoBrush); painter.drawRoundedRect(option.rect.adjusted(1, 1, -1, -1), 6, 6); painter.restore(); } // 绘制图片 int buttonWidget = this->width(); int buttonHeight = this->height(); QRect buttonRect(0, 0, buttonWidget, buttonHeight); QPixmap pixmap = option.icon.pixmap(option.iconSize, QIcon::Active); QStyleOption opt; pixmap = filledSymbolicColoredPix(pixmap, opt.palette.highlightedText().color()); int pixmapWidth = static_cast(pixmap.width() / qApp->devicePixelRatio()); int pixmapHeight = static_cast(pixmap.height() / qApp->devicePixelRatio()); QRect pixmapRect(0, 0, pixmapWidth, pixmapHeight); int deltaX = 0; int deltaY = 0; if (pixmapRect.width() < buttonRect.width()) deltaX = buttonRect.width() - pixmapRect.width(); else deltaX = pixmapRect.width() - buttonRect.width(); if (pixmapRect.height() < buttonRect.height()) deltaY = buttonRect.height() - pixmapRect.height(); else deltaY = pixmapRect.height() - buttonRect.height(); painter.save(); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); painter.translate(deltaX / 2, deltaY / 2); painter.drawPixmap(pixmapRect, pixmap); painter.restore(); } void NetWorkButton::mouseReleaseEvent(QMouseEvent *e) { e->ignore(); } void NetWorkButton::mousePressEvent(QMouseEvent *e) { e->ignore(); } #endif // SUPPORT_NET_ICON ukui-screensaver/src/widgets/circlelabel.cpp0000664000175000017500000000307115172041106020173 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "circlelabel.h" #include #include CircleLabel::CircleLabel(QWidget *parent) : QLabel(parent) { } CircleLabel::CircleLabel(QPixmap pixmap, QWidget *parent) : QLabel(parent) { setPixmap(pixmap); } CircleLabel::~CircleLabel() { } void CircleLabel::setPixmap(QPixmap pixmap) { mPixmap = pixmap; update(); } void CircleLabel::paintEvent(QPaintEvent *event) { Q_UNUSED(event); int radius = qMin(width(), height()) / 2; QPixmap pixmapa(mPixmap); QPainter painter(this); painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform); painter.setBrush(Qt::transparent); painter.setPen(QPen(Qt::transparent, -1, Qt::SolidLine)); QPainterPath path; path.addEllipse(0, 0, radius * 2, radius * 2); painter.setClipPath(path); painter.drawPixmap(0, 0, radius * 2, radius * 2, pixmapa); } ukui-screensaver/src/widgets/loginoptionswidget.h0000664000175000017500000001346015172041106021332 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LOGINOPTIONSWIDGET_H #define LOGINOPTIONSWIDGET_H #include #include "../dbusifs/biometrichelper.h" #include "uniauthservice.h" #include "loginauthinterface.h" #include "definetypes.h" #include "mylistwidget.h" #include "statusbutton.h" class QLabel; class QButtonGroup; class QHBoxLayout; class QVBoxLayout; class QPushButton; class QTimer; class QPixmap; class CustomLabel; #define CUSTOM_PLUGIN_DEV_PREFIX ("Custom:") typedef QMap DeviceListMap; class LoginOptionsWidget : public QWidget { Q_OBJECT public: explicit LoginOptionsWidget(BiometricHelper *bioHelper, int uid, QWidget *parent = nullptr); virtual ~LoginOptionsWidget(); bool getCurLoginOpt(int &nLoginOptType, int &nDrvId); unsigned getLoginOptCount(); DeviceInfoPtr getWechatDevice(); int convertDeviceType(int nDevType); void setUser(int uid); void setCurrentDevice(int drvid); void setCurrentDevice(const QString &deviceName); void setCurrentDevice(const DeviceInfoPtr &pDeviceInfo); DeviceInfoPtr findDeviceById(int drvid); DeviceInfoPtr findDeviceByName(const QString &name); DeviceList findDeviceByType(int nBioType); void setDeviceDisable(int nDevId, bool bDisable = true); bool isDeviceDisable(int nDevId); void clearDeviceDisable(int uid); bool getBioAuthEnable(int nType); bool getQRCodeEnable(); QString getDefaultDevice(QString strUserName, int biotype); QString getDefaultDevice(QString strUserName); QStringList getAllDefDevices(); void SetExtraInfo(QString extra_info, QString info_type); bool getHasUkeyOptions(); void setSelectedPassword(); void updateUIStatus(); inline DeviceListMap getUserDevices() { return m_mapDevices; } QString getCustomDevName(); /** * @brief 进行生物识别认证 * @param deviceInfo 使用的设备 * @param uid 待认证的用户id */ void startAuth(DeviceInfoPtr device, int uid); /** * @brief 终止生物识别认证 */ void stopAuth(); /** * @brief 是否正在认证 * @return */ bool isAuthenticating() { return m_isInAuth; } QPixmap loadSvg(QString path, QString color, int size); LoginAuthInterface *getCustomLoginAuth(); void updateLoginOptionFont(QString fontFamily); void updateLoginOptionFontSize(double fontSize); // 设置所有生物识别的禁用状态 void setAllDeviceDisable(bool bDisable = true); public slots: void readDevicesInfo(); void onIdentifyComplete(QDBusPendingCallWatcher *watcher); void onUkeyIdentifyComplete(QDBusPendingCallWatcher *watcher); void onStatusChanged(int drvid, int status); void onFrameWritten(int drvid); void onUSBDeviceHotPlug(int drvid, int action, int devNum); void onOptionSelected(int drvid); private Q_SLOTS: /** * @brief item选中状态改变 * @param current 前一个选中item * previous 当前选中item * @return */ void onButtonItemChanged(QListWidgetItem *current, QListWidgetItem *previous); /** * @brief item点击响应 * @param item * @return */ void onButtonItemClicked(QListWidgetItem *item); protected: bool eventFilter(QObject *obj, QEvent *event); Q_SIGNALS: void notifyOptionsChange(unsigned uOptionsCount); void optionSelected(unsigned uLoginOptType, const DeviceInfoPtr &deviceInfo); void updateImage(QImage img); void authComplete(bool bResult, int nStatus); void updateAuthMsg(QString strMsg); void showKToolTipClicked(const QString text); void hideKTooltipClicked(); void setLoadingImage(); void customPluginMsg(QString strMsg); private: void initUI(); void initConnections(); void addOptionButton(unsigned uLoginOptType, int nDrvId, QString strDrvName); void clearOptionButtons(); void updateOptionButtons(); void startAuth_(); void startUkeyAuth(); bool getAuthDouble(); QPixmap drawSymbolicColoredPixmap(QPixmap &source, QString cgColor); QString getDeviceType_tr(int deviceType); void changeEvent(QEvent *event); void refreshTranslate(); private: BiometricHelper *m_biomericProxy = nullptr; DeviceListMap m_mapDevices; unsigned m_curLoginOptType = LOGINOPT_TYPE_PASSWORD; int m_uid; QString m_strUserName; DeviceInfoPtr m_curDevInfo = nullptr; // 当前选择的设备信息 int m_dupFD = -1; // 透传的图像文件句柄 bool m_isInAuth = false; // 是否正在验证 bool m_isStopped = false; // 是否被强制终止 QTimer *m_retrytimer = nullptr; // 重试定时器 double curFontSize; // UI QVBoxLayout *m_layoutMain = nullptr; QHBoxLayout *m_layoutOptBtns = nullptr; CustomLabel *m_labelOptTitle = nullptr; QList m_listDriveId; QMap m_mapOptBtns; QMap m_mapOptItems; QMap> m_mapDisableDev; MyListWidget *m_buttonListWidget = nullptr; QList m_listPriority; bool isShowUkey = false; int m_nCurDrvid; }; #endif // LOGINOPTIONSWIDGET_H ukui-screensaver/src/widgets/authdialog.cpp0000775000175000017500000032044715172041106020067 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "authdialog.h" #include "iconedit.h" #include "customlabel.h" #include "../lock-dialog/lockdialogmodel.h" #include "statusbutton.h" #include "commonfunc.h" #include "biobuttonlistwidget.h" #include "definetypes.h" #include #include #include #include #include #include #include #include #include #include "definetypes.h" #include #include #include #include #include "../lock-dialog/pam-tally.h" #include "../../src/dbusifs/freedesktophelper.h" #include "global_utils.h" #include "binddialog.h" #include "securityquestionanswer.h" #include "circlelabel.h" #include #define WIDGET_HEIGHT_HEADTIP (40) #define WIDGET_HEIGHT_HEADIMAGE ((int)(154 * scale)) #define WIDGET_HEIGHT_USERNAME (40) #define WIDGET_HEIGHT_AUTHMETHOD (40) #define WIDGET_HEIGHT_PASSWDEDIT (80) #define WIDGET_HEIGHT_PAMUKEYLOADINGWDEDIT (100) #define WIDGET_HEIGHT_SPACING ((int)(16 * scale)) #define WIDGET_HEIGHT_CUSTOM \ (WIDGET_HEIGHT_HEADTIP + WIDGET_HEIGHT_HEADIMAGE + WIDGET_HEIGHT_USERNAME + WIDGET_HEIGHT_PASSWDEDIT \ + WIDGET_HEIGHT_SPACING * 3) #define WIDGET_HEIGHT_LOGINOPTS (110) AuthDialog::AuthDialog(LockDialogModel *model, UserInfoPtr userInfo, QWidget *parent) : QWidget(parent), m_modelLockDialog(model), m_isLockingFlg(false), m_isInputPasswd(false), m_curUserInfo(userInfo) { pam_tally_init(); initUI(); } void AuthDialog::initUI() { installEventFilter(this); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setAlignment(Qt::AlignCenter); mainLayout->setSpacing(WIDGET_HEIGHT_SPACING); mainLayout->setContentsMargins(0, 0, 0, 0); m_curFontSize = m_modelLockDialog->getCurFontSize(); m_ptToPx = m_modelLockDialog->getPtToPx(); // 生物认证空占位 m_widgetPlaceholder = new QWidget(this); m_widgetPlaceholder->hide(); // 生物认证提示 m_tipWidget = new QWidget(this); // 头像 m_headerWidget = new QWidget(this); // 用户名 m_nameWidget = new QWidget(this); // 密码输入框 m_editWidget = new QWidget(this); // ukey m_ukeyPasswdWidget = new QWidget(this); // custom widget m_customWidget = new QWidget(this); // login option m_widgetLoginOpts = new QWidget(this); // pam ukey loading widget m_pamUkeyLoadingWidget = new QWidget(this); m_pamUkeyLoadingWidget->hide(); mainLayout->addStretch(); mainLayout->addWidget(m_widgetPlaceholder); mainLayout->addWidget(m_tipWidget); mainLayout->addWidget(m_headerWidget); mainLayout->addWidget(m_nameWidget); mainLayout->addWidget(m_editWidget); mainLayout->addWidget(m_pamUkeyLoadingWidget); mainLayout->addWidget(m_ukeyPasswdWidget); mainLayout->addWidget(m_customWidget); mainLayout->addWidget(m_widgetLoginOpts); mainLayout->addStretch(); initTipWidget(); initHeaderWidget(); initNameWidget(); initEditWidget(); initCustomAuthWidget(); initLoginoptionWidget(); initConnections(); } void AuthDialog::initConnections() { connect(m_modelLockDialog, &LockDialogModel::pamShowMessage, this, &AuthDialog::onPamShowMessage); connect(m_modelLockDialog, &LockDialogModel::pamShowPrompt, this, &AuthDialog::onPamShowPrompt); connect(m_modelLockDialog, &LockDialogModel::pamShowBinaryPrompt, this, &AuthDialog::onPamShowBinaryPrompt); connect(m_modelLockDialog, &LockDialogModel::pamAuthCompleted, this, &AuthDialog::onPamAuthCompleted); connect(m_passwordEdit, SIGNAL(clicked(const QString &)), this, SLOT(onRespond(const QString &))); connect(m_messageButton, &QPushButton::clicked, this, &AuthDialog::onMessageButtonClicked); } bool AuthDialog::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyRelease) { MyListWidget *listWidget = qobject_cast(obj); if (listWidget) { if (listWidget && listWidget->hasFocus()) { // Tab键切入时,设置焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::SelectCurrent); listWidget->currentItemChanged(listWidget->currentItem(), nullptr); } else { // Tab键切出时,清空焦点状态 listWidget->setCurrentRow(listWidget->currentRow(), QItemSelectionModel::SelectionFlag::Clear); } } } else if (event->type() == QEvent::Show || event->type() == QEvent::Hide) { if (m_loginOpts == obj) { if (m_widgetLoginOpts && event->type() == QEvent::Show) { Q_EMIT bottomPositionChanged( mapToParent(m_widgetLoginOpts->geometry().topLeft()).y() + m_widgetLoginOpts->height()); } else { Q_EMIT bottomPositionChanged(mapToParent(m_widgetLoginOpts->geometry().topLeft()).y()); } } else if (this == obj && event->type() == QEvent::Show) { pam_tally_init(); } } return QWidget::eventFilter(obj, event); } void AuthDialog::resizeEvent(QResizeEvent *event) { if (m_widgetSize == event->size()) { return; } m_widgetSize = event->size(); updateAuthSize(); } void AuthDialog::updateAuthSize() { this->layout()->setSpacing(WIDGET_HEIGHT_SPACING); if (m_tipWidget) m_tipWidget->setFixedSize(this->width(), WIDGET_HEIGHT_HEADTIP); if (m_headerWidget) m_headerWidget->setFixedSize(this->width(), WIDGET_HEIGHT_HEADIMAGE); if (m_nameWidget) m_nameWidget->setFixedSize(this->width(), WIDGET_HEIGHT_USERNAME); if (m_editWidget && m_authMethodLabel && m_authMethodLabel->isVisible()) m_editWidget->setFixedSize(this->width(), WIDGET_HEIGHT_PASSWDEDIT + WIDGET_HEIGHT_AUTHMETHOD); else m_editWidget->setFixedSize(this->width(), WIDGET_HEIGHT_PASSWDEDIT); if (m_widgetPlaceholder) { m_widgetPlaceholder->setFixedSize(this->width(), WIDGET_HEIGHT_PASSWDEDIT); } if (m_ukeyPasswdWidget) m_ukeyPasswdWidget->setFixedSize(this->width(), WIDGET_HEIGHT_PASSWDEDIT); if (m_pamUkeyLoadingWidget) m_pamUkeyLoadingWidget->setFixedSize(this->width(), WIDGET_HEIGHT_PAMUKEYLOADINGWDEDIT); if (m_widgetLoginOpts) { m_widgetLoginOpts->setFixedSize(this->width(), WIDGET_HEIGHT_LOGINOPTS); if (m_loginOpts && m_loginOpts->isVisible()) { Q_EMIT bottomPositionChanged( mapToParent(m_widgetLoginOpts->geometry().topLeft()).y() + m_widgetLoginOpts->height()); } else { Q_EMIT bottomPositionChanged(mapToParent(m_widgetLoginOpts->geometry().topLeft()).y()); } } if (m_labelLoginTypeTip) { m_labelLoginTypeTip->setFixedSize(m_tipWidget->size()); m_labelLoginTypeTip->setFontSize((26 + m_curFontSize) * m_ptToPx); m_labelLoginTypeTip->setFontWeight(QFont::Medium); } if (m_labelHeadImg) m_labelHeadImg->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); if (m_labelFace) { m_labelFace->setStyleSheet( QString("border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,20%);") .arg((int)(WIDGET_HEIGHT_HEADIMAGE / 2))); m_labelFace->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); QImage faceImg; setFaceImg(faceImg); } if (m_labelQRCode) { m_labelQRCode->setStyleSheet( QString("border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,100%);") .arg((int)(6 * scale))); m_labelQRCode->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); setQRCode(m_imgQRCode); } if (m_labelQRCodeTip) { m_labelQRCodeTip->setFixedSize(22 * scale, 22 * scale); } if (m_labelQRCodeMsg) { m_labelQRCodeMsg->setFixedHeight(30 * scale); m_labelQRCodeMsg->setFontSize((14 + m_curFontSize) * m_ptToPx); } if (m_labelBioWaiting) { m_labelBioWaiting->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); } if (m_nameLabel) { m_nameLabel->setFixedSize(this->width(), WIDGET_HEIGHT_USERNAME); m_nameLabel->setFontSize((24 + m_curFontSize) * m_ptToPx); } if (m_passwordEdit) { m_passwordEdit->setFixedSize(240, 40); m_passwordEdit->updateUI(); } if (m_messageLabel) { m_messageLabel->setFixedSize(this->width(), 40); m_messageLabel->setFontSize((14 + m_curFontSize) * m_ptToPx); } if (m_messageButton) { m_messageButton->setFixedSize(200, 40); QFont font = m_messageButton->font(); font.setPointSize((14 + m_curFontSize) * m_ptToPx); m_messageButton->setFont(font); } if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->setFixedSize(240, 40); m_ukeyPasswordEdit->updateUI(); } if (m_ukeyMessageLabel) { m_ukeyMessageLabel->setFixedSize(this->width(), 40); m_ukeyMessageLabel->setFontSize((14 + m_curFontSize) * m_ptToPx); } if (bottomListWidget) { bottomListWidget->setFixedSize( QSize(48 * bottomListWidget->count() + bottomListWidget->spacing() * 2 * bottomListWidget->count(), 64)); bottomListWidget->updateUI(); } if (m_tipWidget && bottomListWidget) m_optionTip->setFixedSize(bottomListWidget->width(), 32); if (m_customWidget) m_customWidget->setFixedSize(width(), WIDGET_HEIGHT_CUSTOM); updateUI(); } void AuthDialog::updateAuthFont(QString fontFamily) { if (m_labelLoginTypeTip) { m_labelLoginTypeTip->setFontFamily(fontFamily); } if (m_ukeyMessageLabel) { m_ukeyMessageLabel->setFontFamily(fontFamily); } if (m_nameLabel) { m_nameLabel->setFontFamily(fontFamily); } if (m_messageLabel) { m_messageLabel->setFontFamily(fontFamily); } if (m_loginOpts) { m_loginOpts->updateLoginOptionFont(fontFamily); } if (m_labelQRCodeMsg) { m_labelQRCodeMsg->setFontFamily(fontFamily); } } void AuthDialog::updateAuthFontSize(double fontSize) { if (m_labelLoginTypeTip) { m_labelLoginTypeTip->setFontSize((26 + fontSize) * m_ptToPx); } if (m_ukeyMessageLabel) { m_ukeyMessageLabel->setFontSize((14 + fontSize) * m_ptToPx); } if (m_nameLabel) { m_nameLabel->setFontSize((24 + fontSize) * m_ptToPx); } if (m_messageLabel) { m_messageLabel->setFontSize((14 + fontSize) * m_ptToPx); } if (m_loginOpts) { m_loginOpts->updateLoginOptionFontSize((16 + fontSize) * m_ptToPx); } if (m_labelQRCodeMsg) { m_labelQRCodeMsg->setFontSize((14 + fontSize) * m_ptToPx); } if (m_passwordEdit) { m_passwordEdit->setFontSize((16 + fontSize) * m_ptToPx); } } qint32 AuthDialog::getCurAuthUserUid() { return (double)(m_curUserInfo->uid()); } QString AuthDialog::getCurAuthRealName() { return m_curUserInfo->fullName(); } QPixmap AuthDialog::getCurAuthUserFace() { return m_curUserInfo->headImage(); } int AuthDialog::getUserAuthFailedTimes() { return m_userFailedTimesMap[m_curUserInfo->uid()]; } void AuthDialog::initTipWidget() { QVBoxLayout *tipLayout = new QVBoxLayout(m_tipWidget); tipLayout->setAlignment(Qt::AlignCenter); m_labelLoginTypeTip = new CustomLabel(m_tipWidget); m_labelLoginTypeTip->setText(""); m_labelLoginTypeTip->setAlignment(Qt::AlignCenter); m_labelLoginTypeTip->setFixedSize(m_tipWidget->size()); } void AuthDialog::initHeaderWidget() { // 用户头像 QVBoxLayout *headerLayout = new QVBoxLayout(m_headerWidget); headerLayout->setAlignment(Qt::AlignCenter); headerLayout->setContentsMargins(0, 0, 0, 0); m_labelHeadImg = new CircleLabel(m_headerWidget); headerLayout->addWidget(m_labelHeadImg); } void AuthDialog::initNameWidget() { QVBoxLayout *nameLayout = new QVBoxLayout(m_nameWidget); nameLayout->setAlignment(Qt::AlignHCenter); nameLayout->setContentsMargins(0, 0, 0, 0); m_nameLabel = new CustomLabel(m_nameWidget); m_nameLabel->setAlignment(Qt::AlignCenter); } void AuthDialog::initEditWidget() { QVBoxLayout *editWidgetLayout = new QVBoxLayout(m_editWidget); editWidgetLayout->setContentsMargins(0, 0, 0, 0); editWidgetLayout->setAlignment(Qt::AlignHCenter); m_pEditLayout = new QHBoxLayout(); m_pEditLayout->setContentsMargins(0, 0, 0, 0); m_authMethodLabel = new CustomLabel(m_editWidget); m_authMethodLabel->setAlignment(Qt::AlignHCenter); editWidgetLayout->addWidget(m_authMethodLabel); m_authMethodLabel->hide(); syslog(LOG_INFO, "[ukui-screensaver-dialog] init edit contol"); m_pEditLayout->setAlignment(Qt::AlignHCenter); m_passwordEdit = new IconEdit(m_modelLockDialog, m_editWidget); m_passwordEdit->setType(QLineEdit::Password); syslog(LOG_INFO, "[ukui-screensaver-dialog] init edit contol end"); // m_passwordEdit->setPrompt("password"); m_pEditLayout->addWidget(m_passwordEdit); connect(m_passwordEdit, &IconEdit::clickedPassword, this, [=] { Q_EMIT lineEditClicked(); }); connect(m_passwordEdit, &IconEdit::mousePressed, this, [=] { Q_EMIT lineEditMousePressed(); }); editWidgetLayout->addLayout(m_pEditLayout); m_messageButton = new QPushButton(m_editWidget); m_messageButton->setObjectName(QStringLiteral("messageButton")); QHBoxLayout *msgBtnLayout = new QHBoxLayout(); msgBtnLayout->setContentsMargins(0, 0, 0, 0); m_messageButton->setStyleSheet( "QPushButton{text-align:center;color:white;background-color: rgba(255,255,255,15%);border: none;border-radius: 4px;outline: none;} \ QPushButton::hover{background-color: rgba(255,255,255,40%);} \ QPushButton::pressed {background-color: rgba(255,255,255,30%);}"); m_messageButton->hide(); msgBtnLayout->addWidget(m_messageButton); editWidgetLayout->addLayout(msgBtnLayout); m_messageLabel = new CustomLabel(m_editWidget); m_messageLabel->setAlignment(Qt::AlignHCenter); editWidgetLayout->addWidget(m_messageLabel); } void AuthDialog::initUkeyPasswordWidget() { if (!m_ukeyPasswordEdit) { QVBoxLayout *ukeyWidgetLayout = new QVBoxLayout(m_ukeyPasswdWidget); ukeyWidgetLayout->setContentsMargins(0, 0, 0, 0); ukeyWidgetLayout->setAlignment(Qt::AlignHCenter); QVBoxLayout *editLayout = new QVBoxLayout(); editLayout->setContentsMargins(0, 0, 0, 0); editLayout->setAlignment(Qt::AlignHCenter); m_ukeyPasswordEdit = new IconEdit(m_modelLockDialog); m_ukeyPasswordEdit->setPrompt(tr("")); editLayout->addWidget(m_ukeyPasswordEdit); ukeyWidgetLayout->addLayout(editLayout); connect(m_ukeyPasswordEdit, &IconEdit::clicked, this, [=] { Q_EMIT lineEditClicked(); }); connect(m_ukeyPasswordEdit, &IconEdit::mousePressed, this, [=] { Q_EMIT lineEditMousePressed(); }); m_ukeyMessageLabel = new CustomLabel(m_ukeyPasswdWidget); m_ukeyMessageLabel->setText(""); m_ukeyMessageLabel->setAlignment(Qt::AlignHCenter); ukeyWidgetLayout->addWidget(m_ukeyMessageLabel); m_ukeyPasswdWidget->hide(); } } void AuthDialog::initLoginoptionWidget() { // QVBoxLayout *loginoptionLayout = new QVBoxLayout(m_widgetLoginOpts); // loginoptionLayout->setContentsMargins(0, 0, 0, 0); // loginoptionLayout->setSpacing(0); // loginoptionLayout->setContentsMargins(0, 0, 0, 0); // loginoptionLayout->setAlignment(Qt::AlignHCenter); // QHBoxLayout *tipsLayout = new QHBoxLayout(); // tipsLayout->setContentsMargins(0,0,0,0); // m_optionTip = new CustomLabel(); // m_optionTip->setText("Login Option"); // m_optionTip->setAlignment(Qt::AlignHCenter); // tipsLayout->addWidget(m_optionTip); // loginoptionLayout->addLayout(tipsLayout); // QHBoxLayout *btnsLayout = new QHBoxLayout(); // bottomListWidget = new BioButtonListWidget(m_widgetLoginOpts); // bottomListWidget->installEventFilter(this); // btnsLayout->addWidget(bottomListWidget); // loginoptionLayout->addLayout(btnsLayout); } void AuthDialog::initCustomAuthWidget() { if (m_customWidget) { m_layoutCustom = new QHBoxLayout(); m_layoutCustom->setSpacing(0); m_layoutCustom->setContentsMargins(0, 0, 0, 0); m_layoutCustom->setAlignment(Qt::AlignCenter); m_customWidget->setLayout(m_layoutCustom); m_customWidget->hide(); } } void AuthDialog::updateUI() { m_labelHeadImg->setPixmap(m_curUserInfo->headImage()); if (m_curUserInfo->name() == "*login") { m_nameLabel->setText(tr("Enter username")); } else if (m_curUserInfo->name() == "*guest") { m_nameLabel->setText(tr("Guest")); } else { m_nameLabel->setText(m_curUserInfo->fullName()); } if (m_fRetryButton) { m_fRetryButton->setStyleSheet( QString("QPushButton{border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,10%);}" "QPushButton::hover{background-color: rgba(255,255,255,30%);}" "QPushButton::pressed {background-color: rgba(255,255,255,40%);}") .arg((int)(77 * scale))); m_fRetryButton->setFixedSize(154 * scale, 154 * scale); m_fRetryButton->setIconSize(QSize(48 * scale, 48 * scale)); } qDebug() << "AuthDialog updateUI:" << isGreeterMode() << "," << m_curUserInfo->isLoggedIn(); if (!isGreeterMode() && m_curUserInfo->isLoggedIn()) { QPixmap iconLogin = QIcon::fromTheme("system-lock-screen-symbolic").pixmap(12, 12); iconLogin = drawSymbolicColoredPixmap(iconLogin, "white"); if (m_passwordEdit) { m_passwordEdit->setIcon(iconLogin, QSize(12, 12)); } if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->setIcon(iconLogin, QSize(12, 12)); } } else { QPixmap iconLogin = QIcon::fromTheme("go-next-symbolic").pixmap(20, 20); iconLogin = drawSymbolicColoredPixmap(iconLogin, "white"); if (m_passwordEdit) { m_passwordEdit->setIcon(iconLogin, QSize(20, 20)); } if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->setIcon(iconLogin, QSize(20, 20)); } } showPwdTipBtn(); } void AuthDialog::startLoadingUkey() { // isLoadingUkey = true; if (m_ukeyLoadingWidget) m_ukeyLoadingWidget->show(); if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->hide(); } if (m_ukeyMessageLabel) { m_ukeyMessageLabel->hide(); } if (!m_loadingTimer) { m_loadingTimer = new QTimer(this); m_loadingTimer->setInterval(150); connect(m_loadingTimer, &QTimer::timeout, this, &AuthDialog::updateLoadingPixmap); } QPixmap icon = QIcon::fromTheme("ukui-loading-0-symbolic").pixmap(27, 27); m_ukeyLoadingPixmap = drawSymbolicColoredPixmap(icon, "white"); m_loadingButton->setIcon(m_ukeyLoadingPixmap); m_loadingButton->setIconSize(QSize(27, 27)); m_loadingTimer->start(); } void AuthDialog::stopLoadingUkey() { isLoadingUkey = false; if (m_ukeyLoadingWidget) m_ukeyLoadingWidget->hide(); if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->show(); } if (m_ukeyMessageLabel) { m_ukeyMessageLabel->show(); } if (m_loadingTimer) { m_loadingTimer->stop(); } } void AuthDialog::updateLoadingPixmap() { QTransform matrix; matrix.rotate(90.0); m_ukeyLoadingPixmap = m_ukeyLoadingPixmap.transformed(matrix, Qt::FastTransformation); m_loadingButton->setIcon(QIcon(m_ukeyLoadingPixmap)); } void AuthDialog::switchLoginOptType(unsigned uLoginOptType, bool faceBtnClicked) { switch (uLoginOptType) { case LOGINOPT_TYPE_PASSWORD: { m_tipWidget->show(); m_editWidget->show(); m_widgetPlaceholder->hide(); m_headerWidget->show(); m_nameWidget->show(); m_labelHeadImg->show(); if (m_labelQRCode) { m_labelQRCode->hide(); } if (m_labelFace) { m_labelFace->hide(); } if (m_labelBioWaiting) { m_labelBioWaiting->hide(); } stopBioWaiting(); if (m_fRetryButton) { m_fRetryButton->hide(); } m_ukeyPasswdWidget->hide(); if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->clearFocus(); } m_customWidget->hide(); setFocusProxy(m_passwordEdit); } break; case LOGINOPT_TYPE_FACE: { m_tipWidget->show(); m_editWidget->hide(); m_widgetPlaceholder->show(); m_headerWidget->show(); m_nameWidget->show(); m_labelHeadImg->hide(); if (m_labelQRCode) { m_labelQRCode->hide(); } if (m_labelFace) { m_labelFace->show(); } if (m_labelBioWaiting) { m_labelBioWaiting->hide(); } stopBioWaiting(); if (m_fRetryButton) { m_fRetryButton->hide(); } m_ukeyPasswdWidget->hide(); if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->clearFocus(); } m_customWidget->hide(); setFocusProxy(m_passwordEdit); if (faceBtnClicked) { m_fTimeoutTimes = 0; } } break; case LOGINOPT_TYPE_FINGERPRINT: case LOGINOPT_TYPE_VOICEPRINT: case LOGINOPT_TYPE_FINGERVEIN: case LOGINOPT_TYPE_IRIS: { m_tipWidget->show(); m_editWidget->hide(); m_widgetPlaceholder->show(); m_headerWidget->show(); m_nameWidget->show(); m_labelHeadImg->hide(); if (m_labelQRCode) { m_labelQRCode->hide(); } if (m_labelFace) { m_labelFace->hide(); } if (m_labelBioWaiting) { m_labelBioWaiting->show(); } startBioWaiting(uLoginOptType); if (m_fRetryButton) { m_fRetryButton->hide(); } m_ukeyPasswdWidget->hide(); if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->clearFocus(); } m_customWidget->hide(); setFocusProxy(m_passwordEdit); } break; case LOGINOPT_TYPE_QRCODE: { m_tipWidget->show(); m_editWidget->hide(); m_widgetPlaceholder->show(); m_headerWidget->show(); m_nameWidget->show(); m_labelHeadImg->hide(); // 延迟检查错误状态 m_isNetworkErr = false; QTimer::singleShot(500, this, [&, this]() { if (!this->m_isNetworkErr) { setQRCodeMsg(""); } }); if (m_labelQRCode) { m_labelQRCode->show(); } if (m_labelFace) { m_labelFace->hide(); } if (m_labelBioWaiting) { m_labelBioWaiting->hide(); } stopBioWaiting(); if (m_fRetryButton) { m_fRetryButton->hide(); } m_ukeyPasswdWidget->hide(); if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->clearFocus(); } m_customWidget->hide(); setFocusProxy(m_passwordEdit); } break; case LOGINOPT_TYPE_GENERAL_UKEY: { initUkeyPasswordWidget(); m_tipWidget->show(); m_editWidget->show(); m_widgetPlaceholder->hide(); m_headerWidget->show(); m_nameWidget->show(); m_labelHeadImg->show(); if (m_labelQRCode) { m_labelQRCode->hide(); } if (m_labelFace) { m_labelFace->hide(); } if (m_labelBioWaiting) { m_labelBioWaiting->hide(); } stopBioWaiting(); if (m_fRetryButton) { m_fRetryButton->hide(); } m_customWidget->hide(); m_ukeyPasswdWidget->show(); if (m_ukeyPasswordEdit) { setFocusProxy(m_ukeyPasswordEdit); m_ukeyPasswordEdit->setFocusPolicy(Qt::StrongFocus); if (m_ukeyPasswordEdit->isVisible()) { m_ukeyPasswordEdit->setFocus(); } } } break; case LOGINOPT_TYPE_CUSTOM: { m_tipWidget->hide(); m_editWidget->hide(); m_widgetPlaceholder->hide(); m_headerWidget->hide(); m_nameWidget->hide(); stopBioWaiting(); setQRCodeMsg(""); m_ukeyPasswdWidget->hide(); if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->clearFocus(); } LoginAuthInterface *plugin = m_loginOpts->getCustomLoginAuth(); if (plugin) { QWidget *authWidget = plugin->getPluginMainWnd(); if (authWidget) { if (m_customWidget != authWidget->parent()) { m_layoutCustom->addWidget(authWidget); qDebug() << "CustomWidget Size:" << authWidget->size(); QObject *obj = dynamic_cast(plugin); connect(obj, SIGNAL(requestAuthAccount(QString)), this, SLOT(onCustomRequestAccount(QString))); connect( obj, SIGNAL(authenticateResult(int, QString)), this, SLOT(onCustomAuthResult(int, QString))); connect(obj, SIGNAL(onRequest(QString)), this, SLOT(onCustomRequest(QString))); connect(obj, SIGNAL(getLoginPluginEnv()), this, SLOT(onCustomPlugEnv())); } setFocusProxy(authWidget); authWidget->setFixedSize(m_customWidget->size()); authWidget->setFocus(); authWidget->show(); } } m_customWidget->show(); } break; default: return; } if (uLoginOptType != m_uCurLoginOptType || (m_deviceInfo && m_deviceInfo->id != m_nLastDeviceId) || m_curUserInfo->name() != m_lastUserName) { clearMessage(); switch (uLoginOptType) { case LOGINOPT_TYPE_PASSWORD: { setLoginTypeTip(""); } break; case LOGINOPT_TYPE_FACE: { setLoginTypeTip(tr("Facial recognition to unlock")); } break; case LOGINOPT_TYPE_FINGERPRINT: { setLoginTypeTip(tr("Fingerprint recognition to unlock")); } break; case LOGINOPT_TYPE_VOICEPRINT: { setLoginTypeTip(tr("Voiceprint recognition to unlock")); } break; case LOGINOPT_TYPE_FINGERVEIN: { setLoginTypeTip(tr("Finger vein recognition to unlock")); } break; case LOGINOPT_TYPE_IRIS: { setLoginTypeTip(tr("Iris recognition to unlock")); } break; case LOGINOPT_TYPE_QRCODE: { QString strDrivName = tr("wechat"); if (m_deviceInfo && m_deviceInfo->shortName != "wechat_driver") { strDrivName = m_deviceInfo->shortName; } setLoginTypeTip(tr("Use the bound %1 scanning code to unlock").arg(strDrivName)); } break; case LOGINOPT_TYPE_GENERAL_UKEY: { setLoginTypeTip(""); } break; case LOGINOPT_TYPE_CUSTOM: { setLoginTypeTip(""); } break; default: return; } } else if (uLoginOptType == m_uCurLoginOptType && uLoginOptType == LOGINOPT_TYPE_FACE && !faceBtnClicked) { clearMessage(); setLoginTypeTip(tr("Facial recognition to unlock")); } if (m_deviceInfo) { m_nLastDeviceId = m_deviceInfo->id; } else { m_nLastDeviceId = -1; } m_lastUserName = m_curUserInfo->name(); m_uCurLoginOptType = uLoginOptType; } void AuthDialog::setQRCode(QImage &imgQRCode) { if (imgQRCode.isNull()) { m_imgQRCode.load(":/image/assets/ukui-qrcode-null.svg"); } else { m_imgQRCode = imgQRCode; if (m_labelQRCodeTip) { m_labelQRCodeTip->hide(); } } m_imgQRCode = m_imgQRCode.scaled(QSize(148 * scale, 148 * scale)); if (m_labelQRCode) { m_labelQRCode->setAlignment(Qt::AlignCenter); m_labelQRCode->setPixmap(QPixmap::fromImage(m_imgQRCode)); } } void AuthDialog::setQRCodeMsg(QString strMsg) { if (strMsg.isEmpty()) { if (m_labelQRCodeMsg) { m_labelQRCodeMsg->hide(); } // m_labelQRCodeTip->hide(); } else { // 一开始认证时就没有网,直接停止加载状态 if (w_timer && w_timer->isActive()) { w_timer->stop(); } if (m_labelQRCodeMsg) { m_labelQRCodeMsg->setTipText(strMsg); m_labelQRCodeMsg->show(); } if (m_labelQRCodeTip) { m_labelQRCodeTip->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(22 * scale, 22 * scale))); m_labelQRCodeTip->show(); } } } void AuthDialog::setFaceImg(QImage &imgFace, int nStatus) { if (m_labelFace) { QPixmap faceImage; m_labelFace->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); // 如果头像文件不存在,则使用默认头像 if (!imgFace.isNull()) { faceImage = PixmapToRound(QPixmap::fromImage(imgFace), WIDGET_HEIGHT_HEADIMAGE / 2); } else { switch (nStatus) { case 1: faceImage = QPixmap(":/image/assets/ukui-loginopt-lose.svg"); break; case 2: faceImage = m_loginOpts->loadSvg(":/image/assets/ukui-loginopt-smile.svg", "gray", 48); break; default: faceImage = QPixmap(":/image/assets/ukui-loginopt-smile.svg"); break; } } m_labelFace->setAlignment(Qt::AlignCenter); m_labelFace->setPixmap(faceImage); m_labelFace->setStyleSheet( QString("border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,20%);") .arg((int)(WIDGET_HEIGHT_HEADIMAGE / 2))); } } void AuthDialog::updatePixmap() { QTransform matrix; matrix.rotate(90.0); m_waitingPixmap = m_waitingPixmap.transformed(matrix, Qt::FastTransformation); if (m_uCurLoginOptType == LOGINOPT_TYPE_QRCODE) { if (m_labelQRCodeTip) { m_labelQRCodeTip->setPixmap(m_waitingPixmap); } } else if (m_uCurLoginOptType == LOGINOPT_TYPE_FACE) { if (m_labelFace) { m_labelFace->setPixmap(m_waitingPixmap); } } } void AuthDialog::onRespondUkey(const QString &text) { if (m_loginOpts && m_deviceInfo) { m_loginOpts->SetExtraInfo(text, "pincode"); m_loginOpts->startAuth(m_deviceInfo, m_curUserInfo->uid()); } } void AuthDialog::onPamShowMessage(QString strMsg, int nType) { qDebug() << "onPamShowMessage:" << strMsg; // 设置label最小高度 m_messageLabel->setMinimumHeight(36); if (strMsg.indexOf("account locked") != -1 || strMsg.indexOf("账户已锁定") != -1 || strMsg.indexOf("Account locked") != -1 || strMsg.indexOf("永久锁定") != -1) { if (unlockUserTimer()) { if (!m_timerUserUnlock) { m_timerUserUnlock = new QTimer(this); m_timerUserUnlock->setInterval(500); connect(m_timerUserUnlock, &QTimer::timeout, this, &AuthDialog::unlockUserTimer); } else { if (m_timerUserUnlock->isActive()) { m_timerUserUnlock->stop(); } } m_timerUserUnlock->start(); } return; } else if (strMsg.indexOf("No password received, please input password") != -1) { m_messageLabel->setTipText(tr("Password cannot be empty")); } else { QString strText = strMsg; if (!m_preStrMessage.isEmpty() && m_preStrMessageType != nType && m_preStrMessageType != -1) { if (m_preStrMessage.back().isPunct()) { strText = m_preStrMessage + strText; } else { strText = m_preStrMessage + "," + strText; } } m_messageLabel->setTipText(strText); m_preStrMessage = strMsg; m_preStrMessageType = nType; } m_bHasUnacknowledgedMsg = true; } void AuthDialog::showPwdTipBtn() { bool isNeedShowPwdTip = false; if (m_userFailedTimesMap[m_curUserInfo->uid()] >= 1) { if (!m_pSecurityQuestionAnswer) { m_pSecurityQuestionAnswer = new SecurityQuestionAnswer(this); } QString pwdHint = m_pSecurityQuestionAnswer->GetUserPasswdHint(m_curUserInfo->uid()); qDebug() << "User tip:" << m_curUserInfo->name() << pwdHint; if (!pwdHint.isEmpty()) { if (!m_pPwdTipMessage) { m_pPwdTipMessage = new StatusButton(m_editWidget, PWDTIPBTN); m_pEditLayout->addWidget(m_pPwdTipMessage); m_pPwdTipMessage->setFixedSize(40, 40); m_pPwdTipMessage->setFocusPolicy(Qt::NoFocus); QPixmap tipIcon = QIcon::fromTheme("help-about-symbolic").pixmap(16, 16); m_pPwdTipMessage->setIcon(drawSymbolicColoredPixmap(tipIcon, "white")); } m_pPwdTipMessage->show(); m_pEditLayout->setContentsMargins(48, 0, 0, 0); m_pPwdTipMessage->setToolTip(tr("Password Prompt:%1").arg(pwdHint)); isNeedShowPwdTip = true; } } if (!isNeedShowPwdTip) { if (m_pPwdTipMessage) { m_pPwdTipMessage->hide(); } m_pEditLayout->setContentsMargins(0, 0, 0, 0); } } void AuthDialog::hidePwdTipBtn() { if (m_pPwdTipMessage) { m_pPwdTipMessage->hide(); } m_pEditLayout->setContentsMargins(0, 0, 0, 0); } void AuthDialog::startFirstLogin() { bioService = new QDBusInterface( "org.ukui.Biometric", "/org/ukui/Biometric", "org.ukui.Biometric", QDBusConnection::systemBus(), this); if (!bioService->isValid()) return; QDBusMessage result = bioService->call(QStringLiteral("GetDevList")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevList error:" << result.errorMessage(); return; } auto dbusArg = result.arguments().at(1).value(); QList variantList; dbusArg >> variantList; if (variantList.count() == 0) return; DeviceInfoPtr pDeviceInfo = std::make_shared(); auto arg = variantList.at(0).value(); arg >> *pDeviceInfo; qDebug() << "pDeviceInfo->id = " << pDeviceInfo->id; if (pDeviceInfo->id <= 0) return; QList argList; argList << pDeviceInfo->id << -1 << 0 << -1; drvid = pDeviceInfo->id; QDBusPendingCall call = bioService->asyncCallWithArgumentList(QStringLiteral("Identify"), argList); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &AuthDialog::onFirstLoginIdentifyComplete); isFirstAuth = true; QTimer::singleShot(3000, [&] { if (isFirstAuth) { bioService->call(QStringLiteral("StopOps"), drvid, 3000); } }); } void AuthDialog::onFirstLoginIdentifyComplete(QDBusPendingCallWatcher *watcher) { isFirstAuth = false; QDBusPendingReply reply = *watcher; if (reply.isError()) { qWarning() << "Identify error: " << reply.error().message(); return; } int result = reply.argumentAt(0).toInt(); int authUid = reply.argumentAt(1).toInt(); qDebug() << result << authUid; if (result == 0 && authUid > 0) { struct passwd *pwd; pwd = getpwuid(authUid); if (pwd && pwd->pw_name) { if (UniAuthService::instance()->isActivatable()) { bool isEnable = UniAuthService::instance()->getBioAuthStatus(pwd->pw_name, ENABLETYPE_BIO); if (isEnable == false) return; } setDirLogin(); isBioSuccess = true; authMode = BIOMETRIC; startAuth(); } } } void AuthDialog::onPamhideOtpDialog() { if (m_msgDialog) { m_msgDialog->hide(); m_msgDialog->close(); m_msgDialog->deleteLater(); m_msgDialog = nullptr; } if (m_bindOtpDialog) { m_bindOtpDialog->hide(); m_bindOtpDialog->setOtpMessageText(""); m_bindOtpDialog->close(); m_bindOtpDialog->deleteLater(); m_bindOtpDialog = nullptr; } sureBind = false; } void AuthDialog::onPamShowOtpDialog(int type, QVariant value) { if (!m_msgDialog) { m_msgDialog = new KMessageDialog(ENSUREDIALOG, this); connect(m_msgDialog, &KMessageDialog::accepted, [=](QString text) { m_msgDialog->hide(); sureBind = true; if (m_bindOtpDialog) { m_bindOtpDialog->show(); } }); connect(m_msgDialog, &KMessageDialog::rejected, [=]() { onPamShowMessage(tr("The authentication has been cancelled!"), PamAuth::MessageTypeError); startAuth(); qDebug() << "MsgBox rejected"; }); } if (!m_bindOtpDialog) { m_bindOtpDialog = new KMessageDialog(BINDOTPDIALOG, this); connect(m_bindOtpDialog, &KMessageDialog::accepted, [=](QString text) { m_bRecvPrompt = true; m_bHasUnacknowledgedMsg = false; m_preStrMessage = ""; onRespond(text); n_prompts--; if (n_prompts > 0) { while (n_prompts > 0) { n_prompts--; onRespond(""); } } }); connect(m_bindOtpDialog, &KMessageDialog::rejected, [=]() { sureBind = false; onPamShowMessage(tr("The authentication has been cancelled!"), PamAuth::MessageTypeError); startAuth(); qDebug() << "MsgBox rejected"; }); } switch (type) { case BinaryPromptAuthType: break; case BinaryPromptMessage: if (m_bindOtpDialog) { m_bindOtpDialog->setOtpPromptText(value.toString()); } break; case BinaryPromptPromptMessage: if (m_bindOtpDialog) { m_bindOtpDialog->setOtpPrpmptPromptText(value.toString()); } break; case BinaryPromptQrcodeStr: if (m_bindOtpDialog) { m_bindOtpDialog->setOtpQrcodeText(value.toString()); } break; case BinaryPromptErrorMessage: if (m_bindOtpDialog) { m_bindOtpDialog->setOtpMessageText(value.toString()); } break; case BinaryPromptNumber: // 暂不不处理 break; case BinaryPromptImage: // 暂不处理 break; case BinaryPromptEmergMsg: if (m_bindOtpDialog) { m_bindOtpDialog->setOtpEmergMsg(value.toString()); } break; default: break; } if (sureBind) { m_msgDialog->hide(); m_bindOtpDialog->show(); } else { m_msgDialog->show(); } } void AuthDialog::onPamShowBinaryPrompt(QVariant val, int nType) { QString binaryPromptString; int binaryInt; QByteArray binaryByteArray; switch (nType) { case BinaryPromptAuthType: factorMode = val.toString(); case BinaryPromptMessage: case BinaryPromptErrorMessage: case BinaryPromptPromptMessage: case BinaryPromptEmergMsg: case BinaryPromptQrcodeStr: binaryPromptString = val.toString(); break; case BinaryPromptNumber: binaryInt = val.toInt(); break; case BinaryPromptImage: binaryByteArray = QByteArray::fromBase64(val.toString().toUtf8()); default: break; } //消息类型为BinaryPromptEmergMsg时,不打印日志,避免将紧急口令打印到日志里 if (nType != BinaryPromptEmergMsg) { qDebug() << "onPamShowBinaryPrompt " << val << nType; } if (factorMode == "2fa_otp") { // 目前otp场景下,只有字符串类型数据, onPamShowOtpDialog(nType, val); n_prompts++; } else if (factorMode == "2fa_ukey") { onPamShowUkeyLoading(); onRespond(""); } } void AuthDialog::updatePamLoadingPixmap() { QTransform matrix; matrix.rotate(90.0); m_pamUkeyLoadingPixmap = m_pamUkeyLoadingPixmap.transformed(matrix, Qt::FastTransformation); if (m_pamUkeyLoadingButton) { m_pamUkeyLoadingButton->setIcon(QIcon(m_pamUkeyLoadingPixmap)); } } void AuthDialog::onPamShowUkeyLoading() { initPamUkeyLoadingWidget(); if (!m_pamUkeyLoadingWidget) return; if (m_pamUkeyLoadingTimer && m_pamUkeyLoadingTimer->isActive()) { return; } if (m_editWidget) m_editWidget->hide(); m_pamUkeyLoadingWidget->show(); if (!m_pamUkeyLoadingTimer) { m_pamUkeyLoadingTimer = new QTimer(this); m_pamUkeyLoadingTimer->setInterval(150); connect(m_pamUkeyLoadingTimer, &QTimer::timeout, this, &AuthDialog::updatePamLoadingPixmap); } QPixmap icon = QIcon::fromTheme("ukui-loading-0-symbolic").pixmap(27, 27); m_pamUkeyLoadingPixmap = drawSymbolicColoredPixmap(icon, "white"); if (m_pamUkeyLoadingButton) { m_pamUkeyLoadingButton->setIcon(m_pamUkeyLoadingPixmap); m_pamUkeyLoadingButton->setIconSize(QSize(27, 27)); } m_pamUkeyLoadingTimer->start(); } void AuthDialog::onPamStopUkeyLoading() { if (m_pamUkeyLoadingWidget) m_pamUkeyLoadingWidget->hide(); if (m_pamUkeyLoadingTimer) m_pamUkeyLoadingTimer->stop(); } void AuthDialog::onPamShowPrompt(QString strPrompt, int nType) { qDebug() << "prompt: " << strPrompt << authMode << nType; // if(isPowerup && m_configuration->getIs990()){ // waitBiometricServiceStatus(); // startFirstLogin(); // } factorMode = ""; n_prompts = 0; if (m_pamUkeyLoadingWidget) { onPamStopUkeyLoading(); } if (m_messageButton) { m_messageButton->hide(); } onPamhideOtpDialog(); sureBind = false; if (m_authMethodLabel && !m_authMethodLabel->isHidden()) { m_authMethodLabel->hide(); updateAuthSize(); } if (strPrompt == BIOMETRIC_PAM || strPrompt == BIOMETRIC_PAM_DOUBLE || strPrompt == BIOMETRIC_PAM_QRCODE) { if (m_bDirectLogin) { onRespond(BIOMETRIC_SUCCESS); return; } if (authMode == PASSWORD) { skipBiometricAuth(); if (m_loginOpts) { if (m_loginOpts->getLoginOptCount() < 1) { m_loginOpts->hide(); } else { m_loginOpts->show(); } if (m_loginOpts->getHasUkeyOptions() && m_loginOpts->getLoginOptCount() < 1) { m_loginOpts->show(); } } } else { skipBiometricAuth(); // if(isPowerup && m_configuration->getIs990()) { // QTimer::singleShot(1500, [&]{ // performBiometricAuth(); // }); // isPowerup = false; // } else { syslog(LOG_INFO, "[ukui-screensaver-dialog] check bio auth begin!!"); performBiometricAuth(); syslog(LOG_INFO, "[ukui-screensaver-dialog] check bio auth done!!"); // } } } else { syslog(LOG_INFO, "[ukui-screensaver-dialog] auth alraedy!!"); m_isNameLogin = false; qDebug() << "m_isNameLogin = false"; m_passwordEdit->stopWaiting(); if (!strPrompt.isEmpty()) { m_passwordEdit->readOnly(false); m_passwordEdit->show(); } if (m_passwordEdit->isVisible()) { m_passwordEdit->setFocus(); this->setFocusProxy(m_passwordEdit); } if (nType != PamAuth::PromptTypeSecret) { m_isAutoSwitch = true; m_passwordEdit->setType(QLineEdit::Normal); } else if (m_isAutoSwitch) { m_passwordEdit->setType(QLineEdit::Password); m_isAutoSwitch = false; } m_bRecvPrompt = true; m_bHasUnacknowledgedMsg = false; m_preStrMessage = ""; if (!m_biometricProxy || !m_biometricProxy->isValid() || !m_deviceInfo) { switchLoginOptType(LOGINOPT_TYPE_PASSWORD); if (m_loginOpts) m_loginOpts->setSelectedPassword(); } if (strPrompt == "Password: " || strPrompt == "密码:" || strPrompt == "密码: ") { strPrompt = tr("Password:"); m_isPassWdInput = true; } else if (strPrompt == "Input password" || strPrompt == "Input Password" || strPrompt == "输入密码") { strPrompt = tr("Input Password"); m_isPassWdInput = true; } if (strPrompt == "login:") { strPrompt = tr("Username"); /// TODO // m_name = "*login"; m_isNameLogin = true; m_isPassWdInput = false; switchLoginOptType(LOGINOPT_TYPE_PASSWORD); qDebug() << "m_isNameLogin = true"; } m_passwordEdit->clear(); m_passwordEdit->setPrompt(strPrompt); m_passwordEdit->show(); if (unlockUserTimer()) { if (!m_timerUserUnlock) { m_timerUserUnlock = new QTimer(this); m_timerUserUnlock->setInterval(500); connect(m_timerUserUnlock, &QTimer::timeout, this, &AuthDialog::unlockUserTimer); } else { if (m_timerUserUnlock->isActive()) { m_timerUserUnlock->stop(); } } m_timerUserUnlock->start(); } if (hasSendPassword) { showAuthMethodTip(); } } if (!m_isInhibitShow) { show(); } } void AuthDialog::onPamAuthCompleted() { if (m_curUserInfo->name().isEmpty()) return; m_passwordEdit->stopWaiting(); stopBioWaiting(); hasSendPassword = false; onPamhideOtpDialog(); n_prompts = 0; bool isAuthenticated = (Q_EMIT m_modelLockDialog->pamIsAuthenticated()); qDebug() << "onPamAuthCompleted:" << isAuthenticated << m_curUserInfo->name(); if (isAuthenticated) { if (m_loginOpts) { m_loginOpts->stopAuth(); } connectBioSlots(false); if ((m_bRecvPrompt && !m_bHasUnacknowledgedMsg) || m_bDirectLogin) { m_bDirectLogin = false; m_failMap.remove(m_curUserInfo->uid()); setLoginTypeTip(""); setUkeyTypeTip(""); authMode = UNKNOWN; m_uCurLoginOptType = LOGINOPT_TYPE_PASSWORD; if (m_loginOpts) { m_loginOpts->clearDeviceDisable(m_curUserInfo->uid()); } Q_EMIT authSucceed(m_curUserInfo->name()); m_userFailedTimesMap[m_curUserInfo->uid()] = 0; // 认证完成后清空密码框 m_passwordEdit->clear(); clearMessage(); showPwdTipBtn(); } else { qDebug() << "m_bRecvPrompt = " << m_bRecvPrompt << " m_bHasUnacknowledgedMsg = " << m_bHasUnacknowledgedMsg; m_bRecvPrompt = true; showAuthenticated(); } } else { // 因为有个概率性会认证失败的问题,导致重试按钮显示出来,因此这里暂时屏蔽 if (m_bRecvPrompt) { m_passwordEdit->clear(); // 如果用户输入了不存在的用户名导致的认证失败,让用户重新输入用户名 if (m_isManual) { if (m_isNameLogin) { m_isNameLogin = false; /// TODO // m_name = "*login"; } if (!m_isPassWdInput) { if (!m_bHasUnacknowledgedMsg && !m_isLockingFlg) { onPamShowMessage(tr("Authentication failure, Please try again"), PamAuth::MessageTypeError); } } else { if (!m_bHasUnacknowledgedMsg && !m_isLockingFlg) onPamShowMessage(tr("Authentication failure, Please try again"), PamAuth::MessageTypeError); } } else { if (!m_bHasUnacknowledgedMsg && !m_isLockingFlg) { onPamShowMessage(tr("Authentication failure, Please try again"), PamAuth::MessageTypeError); } } m_isInputPasswd = false; m_isManual = false; if (unlockUserTimer()) { if (!m_timerUserUnlock) { m_timerUserUnlock = new QTimer(this); m_timerUserUnlock->setInterval(500); connect(m_timerUserUnlock, &QTimer::timeout, this, &AuthDialog::unlockUserTimer); } else { if (m_timerUserUnlock->isActive()) { m_timerUserUnlock->stop(); } } m_timerUserUnlock->start(); } startAuth(); if (++m_userFailedTimesMap[m_curUserInfo->uid()] >= 1) { qDebug() << "m_authFailedTimes = " << m_userFailedTimesMap[m_curUserInfo->uid()]; showPwdTipBtn(); Q_EMIT showResetPasswdBtn(); } } else { showAuthenticated(false); } } if (!m_isInhibitShow) { show(); } } void AuthDialog::onRespond(const QString &strRes) { if (!m_bRecvPrompt && strRes != BIOMETRIC_SUCCESS) { qInfo() << "Wait for input passwd!"; return; } if (m_curUserInfo->name().isEmpty()) { qInfo() << "No user be selected!!"; return; } m_bHasUnacknowledgedMsg = false; clearMessage(); qDebug() << "onRespond:" << m_curUserInfo->name(); if (m_curUserInfo->name() == "*guest") { Q_EMIT m_modelLockDialog->pamAuthenticate(m_curUserInfo->name()); } else if (m_curUserInfo->name() == "*login") { // 用户输入用户名 m_isManual = true; m_isNameLogin = true; Q_EMIT m_modelLockDialog->pamRespond(strRes); m_strManualLoginName = strRes; if (!m_isInputPasswd) { Q_EMIT userChangedByManual(strRes); m_isInputPasswd = true; } } else { // 当密码长度超过1000多时,lightm会出异常,这里暂时先截断 if (strRes.length() > 1000) Q_EMIT m_modelLockDialog->pamRespond(strRes.mid(0, 1000)); else Q_EMIT m_modelLockDialog->pamRespond(strRes); m_passwordEdit->readOnly(true); if (strRes != BIOMETRIC_SUCCESS || strRes != BIOMETRIC_IGNORE) { hasSendPassword = true; } } } void AuthDialog::startAuth() { syslog(LOG_INFO, "[ukui-screensaver-dialog] auth start!!"); if (!m_curUserInfo->name().isEmpty()) { m_bRecvPrompt = false; n_prompts = 0; m_bHasUnacknowledgedMsg = false; // 用户认证 if (m_curUserInfo->name() == "*guest") { // 游客登录 qDebug() << "guest login"; m_passwordEdit->show(); m_passwordEdit->setPrompt(tr("login")); m_strLastPamAuthName = "*guest"; } else if (m_curUserInfo->name() == "*login") { // 手动输入用户名 Q_EMIT m_modelLockDialog->pamAuthenticate(""); m_strLastPamAuthName = "*login"; } else { qDebug() << "login: " << m_curUserInfo->name(); Q_EMIT m_modelLockDialog->pamAuthenticate(m_curUserInfo->name()); m_passwordEdit->clear(); m_passwordEdit->readOnly(true); m_strLastPamAuthName = m_curUserInfo->name(); } } hasSendPassword = false; authMethod = getAuthMethod(); } void AuthDialog::stopAuth() { if (m_loginOpts) { if (m_bioTimer && m_bioTimer->isActive()) m_bioTimer->stop(); m_loginOpts->stopAuth(); m_loginOpts->hide(); } clearMessage(); } void AuthDialog::onCurUserInfoChanged(UserInfoPtr userInfo) { if (!(m_curUserInfo->uid() < 0) && userInfo->uid() == m_curUserInfo->uid()) { m_curUserInfo = userInfo; updateUI(); } } void AuthDialog::onCurUserChanged(UserInfoPtr userInfo) { if (m_loginOpts) { m_loginOpts->stopAuth(); if (m_bioTimer && m_bioTimer->isActive()) m_bioTimer->stop(); } m_curUserInfo = userInfo; m_isInputPasswd = false; updateUI(); startAuth(); if (m_passwordEdit) { m_passwordEdit->clearText(); m_passwordEdit->setType(QLineEdit::Password); } if (m_messageButton) m_messageButton->hide(); clearMessage(); m_bDirectLogin = false; m_isLockingFlg = false; authMode = UNKNOWN; m_deviceInfo = DeviceInfoPtr(); m_deviceName = ""; isBioSuccess = false; if (m_loginOpts) { m_loginOpts->hide(); } } void AuthDialog::showAuthenticated(bool successful /* = true*/) { m_editWidget->show(); m_widgetPlaceholder->hide(); m_passwordEdit->hide(); m_messageButton->show(); m_messageButton->setDefault(true); if (successful) { m_bNeedRetry = false; m_messageButton->setText(tr("Login")); m_messageButton->setFocus(); QString strMsgTip = m_messageLabel->text(); switchLoginOptType(LOGINOPT_TYPE_PASSWORD); if (!strMsgTip.isEmpty()) { m_messageLabel->setText(strMsgTip); } } else { m_bNeedRetry = true; m_messageButton->setText(tr("Retry")); } } void AuthDialog::onMessageButtonClicked() { m_messageButton->setDefault(false); if (!m_bNeedRetry) { // 认证完成后清空密码框 m_passwordEdit->readOnly(false); m_passwordEdit->clear(); clearMessage(); m_failMap.remove(m_curUserInfo->uid()); setLoginTypeTip(""); setUkeyTypeTip(""); authMode = UNKNOWN; m_uCurLoginOptType = LOGINOPT_TYPE_PASSWORD; Q_EMIT authSucceed(m_curUserInfo->name()); } else { m_messageButton->hide(); m_messageLabel->setText(""); authMode = PASSWORD; startAuth(); } } void AuthDialog::clearMessage() { m_messageLabel->clear(); m_preStrMessage = ""; m_preStrMessageType = -1; } bool AuthDialog::unlockUserTimer() { if (m_curUserInfo->name().isEmpty()) return false; int failed_count = 0; int time_left = 0; int deny = 0; int fail_time = 0; int unlock_time = 0; QString curUserName = m_curUserInfo->name(); if (NULL == curUserName.toLatin1().data()) { qDebug() << "get user info failed."; return false; } struct passwd *user = NULL; user = getpwnam(curUserName.toLatin1().data()); if (user) pam_tally_unlock_time_left(user->pw_uid, &failed_count, &time_left, &deny, &fail_time, &unlock_time); else return false; if (time_left >= 60) { // 请多少分钟后重试 int nMinuteleft = time_left / 60; if (m_isLockingFlg == false) { m_nCurLockMin = unlock_time / 60; // 获取当前需要锁定的分钟数 } // 如果当前设置的不是1min钟锁定,那么1min显示成2min,由2min直接跳到59s || // 剩余分钟数小于当前设置的锁定时间,并且大于1min,自增+1 if ((nMinuteleft == 1 && m_nCurLockMin != 1) || (nMinuteleft > 1 && nMinuteleft < m_nCurLockMin)) { nMinuteleft = nMinuteleft + 1; } m_messageLabel->setTipText(tr("Please try again in %1 minutes.").arg(nMinuteleft)); m_passwordEdit->clearText(); m_passwordEdit->readOnly(true); m_passwordEdit->setLocked(true); m_isLockingFlg = true; setBiometricAuthDisabledStatus(m_isLockingFlg); return true; } else if (time_left > 0 && time_left < 60) { // 请多少秒后重试 m_messageLabel->setTipText(tr("Please try again in %1 seconds.").arg(time_left % 60)); m_passwordEdit->clearText(); m_passwordEdit->readOnly(true); m_passwordEdit->setLocked(true); m_isLockingFlg = true; setBiometricAuthDisabledStatus(m_isLockingFlg); return true; } else if (failed_count == 0xFFFF) { // 账号被永久锁定 m_messageLabel->setTipText(tr("Account locked permanently.")); m_passwordEdit->clearText(); m_passwordEdit->readOnly(true); m_passwordEdit->setLocked(true); m_isLockingFlg = true; setBiometricAuthDisabledStatus(m_isLockingFlg); return true; } else { if (m_passwordEdit) { m_passwordEdit->readOnly(false); m_passwordEdit->setLocked(false); if (m_passwordEdit->isVisible()) { m_passwordEdit->setFocus(); this->setFocusProxy(m_passwordEdit); } } // qDebug() << "清理 1 clearMessage"; if (m_isLockingFlg) { onPamShowMessage("", PamAuth::MessageTypeError); // onPamShowMessage(tr("Authentication failure, Please try again"), PamAuth::MessageTypeInfo); m_isLockingFlg = false; setBiometricAuthDisabledStatus(m_isLockingFlg); } if (m_timerUserUnlock) { m_timerUserUnlock->stop(); } } return false; } void AuthDialog::restartBioAuth() { startBioAuth(); } void AuthDialog::onPrepareForSleep(bool sleep) { /// 系统休眠时,会关闭总线,导致设备不可用,发生错误 /// 在系统休眠之前停止认证,在系统唤醒后重新开始认证 if (sleep) { if (m_loginOpts) { manualStopBio = true; m_loginOpts->stopAuth(); if (m_bioTimer && m_bioTimer->isActive()) m_bioTimer->stop(); } } else { manualStopBio = false; restartBioAuth(); } } void AuthDialog::setDirLogin() { m_bDirectLogin = true; } void AuthDialog::pamBioSuccess() { m_bioTimer->stop(); if (!m_loginOpts) { switchLoginOptType(LOGINOPT_TYPE_PASSWORD); return; } if (m_deviceInfo && m_deviceInfo->deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { // ukey时不调用ukey认证 } else { m_loginOpts->startAuth(m_deviceInfo, m_curUserInfo->uid()); } if (m_deviceInfo) { switchLoginOptType(m_loginOpts->convertDeviceType(m_deviceInfo->deviceType)); } else { switchLoginOptType(LOGINOPT_TYPE_PASSWORD); } } void AuthDialog::startBioAuth(unsigned uTimeout) { if (m_loginOpts) m_loginOpts->stopAuth(); if (m_deviceInfo && m_deviceInfo->deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { // ukey时不调用ukey认证 switchLoginOptType(m_loginOpts->convertDeviceType(m_deviceInfo->deviceType)); return; } if (m_bioAuthLoop && m_bioAuthLoop->isRunning()) { qDebug() << "Already to start bio auth!"; return; } if (!m_modelLockDialog->sessionActive()) { if (!m_bioAuthLoop) { m_bioAuthLoop = new QEventLoop(qApp); connect(m_modelLockDialog, &LockDialogModel::sessionActiveChanged, [this](bool isActive) { qDebug() << "sessionActiveChanged:" << isActive; if (isActive && this->m_bioAuthLoop->isRunning()) { this->m_bioAuthLoop->quit(); } }); } qDebug() << "Session inactive blocked!"; m_bioAuthLoop->exec(); qDebug() << "Session inactive blocked end!"; } if (!m_bioTimer) { m_bioTimer = new QTimer(this); connect(m_bioTimer, SIGNAL(timeout()), this, SLOT(pamBioSuccess())); } m_bioTimer->start(uTimeout); } void AuthDialog::waitBiometricServiceStatus() { qDebug() << "get biometric status"; QDBusInterface iface( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", QDBusConnection::systemBus()); QDBusReply bioPath = iface.call("GetUnit", "biometric-authentication.service"); if (!bioPath.isValid()) { return; } QDBusInterface bioface( "org.freedesktop.systemd1", bioPath.value().path(), "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); QDBusReply sessionReply = bioface.call("Get", "org.freedesktop.systemd1.Unit", "UnitFileState"); if (!sessionReply.isValid()) qWarning() << sessionReply.error(); else { QString res = sessionReply.value().variant().toString(); if (res == "disable") return; } qDebug() << "get activeState"; int times = 0; while (times < 20) { QDBusReply sessionReply = bioface.call("Get", "org.freedesktop.systemd1.Unit", "ActiveState"); if (!sessionReply.isValid()) { qWarning() << sessionReply.error(); return; } else { QString res = sessionReply.value().variant().toString(); if (res == "activating") { times++; usleep(100000); } else { break; } } } qDebug() << "times = " << times; } void AuthDialog::performBiometricAuth() { if (!m_biometricProxy) { waitBiometricServiceStatus(); m_biometricProxy = new BiometricHelper(this); maxFailedTimes = UniAuthService::instance()->getMaxFailedTimes(); } // 服务没启动,或者打开DBus连接出错 if (!m_biometricProxy->isValid()) { qWarning() << "An error occurs when connect to the biometric DBus"; if (m_deviceInfo) { if (!m_loginOpts || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_deviceInfo = DeviceInfoPtr(); } } switchLoginOptType(LOGINOPT_TYPE_PASSWORD); return; } // 初始化生物识别认证UI initLoginOptsWidget(); // 没有可用设备,不启用生物识别认证 if (m_loginOpts->getLoginOptCount() < 1) { qWarning() << "No available devices"; if (m_deviceInfo) { if (!m_loginOpts || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_deviceInfo = DeviceInfoPtr(); } } return; } // 获取默认设备 if (m_loginOpts) { if (m_curUserInfo->name() == "*login") m_deviceName = m_loginOpts->getDefaultDevice(m_strManualLoginName); else m_deviceName = m_loginOpts->getDefaultDevice(m_curUserInfo->name()); if (m_isCustomDefault) { // 认证插件配置默认插件认证 if (m_loginOpts) { m_deviceName = m_loginOpts->getCustomDevName(); } } } qDebug() << m_deviceName; if (m_deviceInfo) { if (!m_loginOpts || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_deviceInfo = DeviceInfoPtr(); } } // 如果默认设备为空的话,第一次不启动生物识别认证 if (m_deviceName.isEmpty() && !m_deviceInfo) { qDebug() << "No default device"; return; } // 第一次,获取默认设备的设备信息,之后使用的则是从设备选择窗口传出的设备信息 if (!m_deviceInfo) { m_deviceInfo = m_loginOpts->findDeviceByName(m_deviceName); if (!m_deviceInfo || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { qDebug() << "Device Not found or disable: " << m_deviceName; m_deviceInfo = DeviceInfoPtr(); return; } } initBiometricUI(); // 已经错误超过3次 if (m_failMap.contains(m_curUserInfo->uid()) && m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] >= maxFailedTimes) { if (m_deviceInfo->deviceType == UniT_Remote) { setLoginTypeTip(tr("Failed to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); } else { setLoginTypeTip(tr("Unable to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); if (m_deviceInfo->deviceType == BioT_Face) { QImage imgFailed; setFaceImg(imgFailed, 1); } stopBioWaiting(true); } m_loginOpts->setDeviceDisable(m_deviceInfo->id, true); return; } if (m_deviceInfo->deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { switchLoginOptType(LOGINOPT_TYPE_PASSWORD); m_loginOpts->setSelectedPassword(); return; } switchLoginOptType(m_loginOpts->convertDeviceType(m_deviceInfo->deviceType)); // 自行校验生物设备,pam greeter校验密码 startBioAuth(); } void AuthDialog::skipBiometricAuth() { Q_EMIT m_modelLockDialog->pamRespond(BIOMETRIC_IGNORE); } void AuthDialog::connectBioSlots(bool isConnect) { if (m_isBioSlotsConnected != isConnect) { m_isBioSlotsConnected = isConnect; if (m_isBioSlotsConnected) { connect( FreedesktopHelper::getInstance(false), &FreedesktopHelper::serviceStatusChanged, this, [=](const QString &strService, bool bActive) { if (strService == BIOMETRIC_DBUS_SERVICE) { this->onBiometricDbusChanged(bActive); } }); if (m_loginOpts) { connect(m_loginOpts, &LoginOptionsWidget::authComplete, this, &AuthDialog::onBiometricAuthComplete); connect(m_loginOpts, &LoginOptionsWidget::optionSelected, this, &AuthDialog::onDeviceChanged); connect(m_loginOpts, &LoginOptionsWidget::updateImage, this, &AuthDialog::onLoginOptImage); connect(m_loginOpts, &LoginOptionsWidget::setLoadingImage, this, &AuthDialog::onLoadingImage); connect(m_loginOpts, &LoginOptionsWidget::notifyOptionsChange, this, &AuthDialog::onLoginOptsCount); connect(m_loginOpts, &LoginOptionsWidget::updateAuthMsg, this, &AuthDialog::setLoginMsg); } } else { disconnect(FreedesktopHelper::getInstance(false), nullptr, this, nullptr); if (m_loginOpts) { disconnect(m_loginOpts, nullptr, this, nullptr); } } } } void AuthDialog::initBiometricUI() { QVBoxLayout *headerLayout = qobject_cast(m_headerWidget->layout()); if (headerLayout) { // 人脸识别头像框 if (!m_labelFace) { m_labelFace = new QLabel(m_headerWidget); headerLayout->addWidget(m_labelFace); m_labelFace->setStyleSheet( QString("border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,20%);") .arg((int)(WIDGET_HEIGHT_HEADIMAGE / 2))); m_labelFace->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); QImage faceImg; setFaceImg(faceImg); m_labelFace->hide(); } // 生物特征等待验证动画 if (!m_labelBioWaiting) { m_labelBioWaiting = new QLabel(m_headerWidget); m_labelBioWaiting->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); headerLayout->addWidget(m_labelBioWaiting); m_labelBioWaiting->hide(); } // 人脸识别重试 if (!m_fRetryButton) { m_fRetryButton = new QPushButton(m_headerWidget); m_fRetryButton->setFocusPolicy(Qt::NoFocus); m_fRetryButton->setStyleSheet(QString("QPushButton{border-radius: %1px; border:0px solid " "white;background-color: rgba(255,255,255,10%);}" "QPushButton::hover{background-color: rgba(255,255,255,30%);}" "QPushButton::pressed {background-color: rgba(255,255,255,40%);}") .arg((int)(77 * scale))); QPixmap retryIcon = QIcon::fromTheme("view-refresh-symbolic").pixmap(48, 48); m_fRetryButton->setFixedSize(154 * scale, 154 * scale); m_fRetryButton->setIcon(drawSymbolicColoredPixmap(retryIcon, "white")); m_fRetryButton->setIconSize(QSize(48, 48)); connect(m_fRetryButton, &QPushButton::clicked, this, &AuthDialog::onFRetryButtonClicked); m_fRetryButton->hide(); headerLayout->addWidget(m_fRetryButton); } // 微信认证窗口 if (!m_labelQRCode) { m_labelQRCode = new QLabel(m_headerWidget); m_labelQRCode->setAlignment(Qt::AlignCenter); m_labelQRCode->setStyleSheet( QString("border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,100%);") .arg((int)(6 * scale))); m_labelQRCode->setFixedSize(WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE); setQRCode(m_imgQRCode); m_labelQRCode->hide(); QVBoxLayout *layoutQRCode = new QVBoxLayout(); layoutQRCode->setAlignment(Qt::AlignCenter); layoutQRCode->setSpacing(10); m_labelQRCode->setLayout(layoutQRCode); m_labelQRCodeTip = new QLabel(); m_labelQRCodeTip->setFixedSize(22 * scale, 22 * scale); layoutQRCode->addWidget(m_labelQRCodeTip, 0, Qt::AlignHCenter); m_labelQRCodeTip->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(22 * scale, 22 * scale))); m_labelQRCodeTip->show(); m_labelQRCodeMsg = new CustomLabel(); m_labelQRCodeMsg->setFixedHeight(30 * scale); m_labelQRCodeMsg->setFontSize((14 + m_curFontSize) * m_ptToPx); m_labelQRCodeMsg->setStyleSheet("QLabel{background-color:rgba(255,255,255,0);color:rgba(255,0,0)}"); m_labelQRCodeMsg->hide(); layoutQRCode->addWidget(m_labelQRCodeMsg, 0, Qt::AlignHCenter); headerLayout->addWidget(m_labelQRCode); } } } void AuthDialog::initLoginOptsWidget() { if (m_loginOpts) { m_loginOpts->setUser(m_curUserInfo->uid()); } else { QHBoxLayout *optsLayout = new QHBoxLayout(m_widgetLoginOpts); optsLayout->setContentsMargins(0, 0, 0, 0); m_loginOpts = new LoginOptionsWidget(m_biometricProxy, m_curUserInfo->uid(), m_widgetLoginOpts); optsLayout->addWidget(m_loginOpts); m_loginOpts->setUser(m_curUserInfo->uid()); m_loginOpts->installEventFilter(this); } connectBioSlots(true); qDebug() << "OptsCount:" << m_loginOpts->getLoginOptCount(); if (m_loginOpts->getLoginOptCount() < 1) { switchLoginOptType(LOGINOPT_TYPE_PASSWORD); m_loginOpts->hide(); } else { m_loginOpts->show(); } if (m_loginOpts->getHasUkeyOptions() && m_loginOpts->getLoginOptCount() < 1) { m_loginOpts->show(); m_loginOpts->setSelectedPassword(); } m_loginOpts->setEnabled(true); setLoginOptsWidgetGeometry(); } void AuthDialog::setLoginOptsWidgetGeometry() { // 生物识别 if (m_loginOpts) { m_widgetLoginOpts->setFixedSize(width(), WIDGET_HEIGHT_LOGINOPTS); qDebug() << "LoginOptGeometry:" << m_widgetLoginOpts->geometry() << "," << geometry() << ",isHidden:" << m_widgetLoginOpts->isHidden(); } } void AuthDialog::onLoginOptsCount(unsigned uCount) { qDebug() << "onLoginOptsCount Count:" << uCount; if (!m_loginOpts) return; if (uCount > 0) { setLoginOptsWidgetGeometry(); m_loginOpts->show(); qDebug() << "LoginOptsShow!!"; } else { switchLoginOptType(LOGINOPT_TYPE_PASSWORD); m_loginOpts->hide(); } if (!m_deviceInfo || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_loginOpts->stopAuth(); authMode = BIOMETRIC; // 初始化生物识别认证UI initLoginOptsWidget(); // 没有可用设备,不启用生物识别认证 if (m_loginOpts->getLoginOptCount() < 1) { qWarning() << "No available devices"; if (m_deviceInfo) { m_deviceInfo = DeviceInfoPtr(); } return; } // 获取默认设备 if (m_curUserInfo->name() == "*login") { if (isLoadingUkey) { m_deviceName = m_loginOpts->getDefaultDevice(m_strManualLoginName, UniT_General_Ukey); } else { m_deviceName = m_loginOpts->getDefaultDevice(m_strManualLoginName); } } else { if (isLoadingUkey) { m_deviceName = m_loginOpts->getDefaultDevice(m_curUserInfo->name(), UniT_General_Ukey); } else { m_deviceName = m_loginOpts->getDefaultDevice(m_curUserInfo->name()); } } qDebug() << m_deviceName; if (m_deviceInfo) { if (!m_loginOpts || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_deviceInfo = DeviceInfoPtr(); } } // 如果默认设备为空的话,第一次不启动生物识别认证 if (m_deviceName.isEmpty() && !m_deviceInfo) { qDebug() << "No default device"; return; } // 第一次,获取默认设备的设备信息,之后使用的则是从设备选择窗口传出的设备信息 if (!m_deviceInfo) { m_deviceInfo = m_loginOpts->findDeviceByName(m_deviceName); if (!m_deviceInfo || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { qDebug() << "Device Not found or Disable: " << m_deviceName; m_deviceInfo = DeviceInfoPtr(); m_loginOpts->setSelectedPassword(); return; } } initBiometricUI(); // 已经错误超过3次 if (m_failMap.contains(m_curUserInfo->uid()) && m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] >= maxFailedTimes) { if (m_deviceInfo->deviceType == UniT_Remote) { setLoginTypeTip(tr("Failed to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); } else { setLoginTypeTip(tr("Unable to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); if (m_deviceInfo->deviceType == BioT_Face) { QImage imgFailed; setFaceImg(imgFailed, 1); } stopBioWaiting(true); } m_loginOpts->setDeviceDisable(m_deviceInfo->id, true); return; } if (m_deviceInfo->deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { stopLoadingUkey(); } if (m_deviceInfo) { m_loginOpts->setCurrentDevice(m_deviceInfo); m_loginOpts->updateUIStatus(); } switchLoginOptType(m_loginOpts->convertDeviceType(m_deviceInfo->deviceType)); // 自行校验生物设备,pam greeter校验密码 startBioAuth(); } } void AuthDialog::onLoginOptImage(QImage img) { if (m_uCurLoginOptType == LOGINOPT_TYPE_FACE) { setFaceImg(img); } else if (m_uCurLoginOptType == LOGINOPT_TYPE_QRCODE) { setQRCode(img); } if (w_timer && w_timer->isActive()) { w_timer->stop(); } } void AuthDialog::onLoadingImage() { if (!w_timer) { w_timer = new QTimer(this); w_timer->setInterval(150); connect(w_timer, &QTimer::timeout, this, &AuthDialog::updatePixmap); } m_waitingPixmap = QIcon::fromTheme("ukui-loading-0-symbolic").pixmap(22 * scale, 22 * scale); if (m_labelFace) { m_labelFace->setAlignment(Qt::AlignCenter); } if (m_uCurLoginOptType == LOGINOPT_TYPE_FACE) { if (m_labelFace) { m_labelFace->setPixmap(m_waitingPixmap); } if (m_labelQRCodeTip) { m_labelQRCodeTip->show(); } } else if (m_uCurLoginOptType == LOGINOPT_TYPE_QRCODE) { if (m_labelQRCodeTip) { m_labelQRCodeTip->setPixmap(m_waitingPixmap); m_labelQRCodeTip->show(); } } w_timer->start(); } void AuthDialog::onDeviceChanged(unsigned uCurLoginOptType, const DeviceInfoPtr &deviceInfo) { isLoadingUkey = false; if (uCurLoginOptType == LOGINOPT_TYPE_PASSWORD) { switchLoginOptType(uCurLoginOptType); if (m_loginOpts) { m_loginOpts->stopAuth(); } m_deviceInfo = nullptr; authMode = PASSWORD; startAuth(); return; } if (uCurLoginOptType != LOGINOPT_TYPE_GENERAL_UKEY && !deviceInfo) return; if (deviceInfo) qDebug() << "device changed: " << *deviceInfo; if (deviceInfo && m_failMap[m_curUserInfo->uid()][deviceInfo->id] >= maxFailedTimes) { qDebug() << "Failed MAX!!"; return; } if (uCurLoginOptType == LOGINOPT_TYPE_GENERAL_UKEY && !deviceInfo) { isLoadingUkey = true; startLoadingUkey(); } else if (uCurLoginOptType == LOGINOPT_TYPE_GENERAL_UKEY && deviceInfo) { stopLoadingUkey(); } if (uCurLoginOptType != LOGINOPT_TYPE_GENERAL_UKEY && deviceInfo == m_deviceInfo) { return; } if (m_bioTimer && m_bioTimer->isActive()) m_bioTimer->stop(); authMode = BIOMETRIC; m_deviceInfo = deviceInfo; if (uCurLoginOptType == LOGINOPT_TYPE_FACE) switchLoginOptType(uCurLoginOptType, true); else switchLoginOptType(uCurLoginOptType); if (!isBioSuccess && deviceInfo) startBioAuth(); } void AuthDialog::onBiometricAuthComplete(bool result, int nStatus) { if (!result) { if (m_deviceInfo && m_deviceInfo->deviceType == UniT_General_Ukey && m_ukeyPasswdWidget->isVisible()) { if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->stopWaiting(); m_ukeyPasswordEdit->clearText(); } } if (manualStopBio) { manualStopBio = false; return; } if (nStatus == 5 && m_deviceInfo) { if (w_timer && w_timer->isActive()) w_timer->stop(); QImage imgFailed; setFaceImg(imgFailed, 2); return; } else if (nStatus >= 2 && nStatus != 5) { if (m_deviceInfo) { if (m_failMap.contains(m_curUserInfo->uid()) && m_failMap[m_curUserInfo->uid()].contains(m_deviceInfo->id)) { m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] = m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] + 1; } else { m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] = 1; } qDebug() << "Failed count:" << m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] << ",Max:" << maxFailedTimes; if (m_deviceInfo->deviceType == BioT_Face) { QImage imgFailed; setFaceImg(imgFailed, 1); m_fTimeoutTimes = 0; } if (m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] >= maxFailedTimes) { if (m_deviceInfo->deviceType == UniT_Remote) { setLoginTypeTip(tr("Failed to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); QImage nullImage; setQRCode(nullImage); } else if (m_deviceInfo->deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { setUkeyTypeTip(tr("Unable to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); } else { setLoginTypeTip(tr("Unable to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); stopBioWaiting(true); } if (m_loginOpts) m_loginOpts->setDeviceDisable(m_deviceInfo->id, true); if (m_ukeyPasswdWidget && m_ukeyPasswdWidget->isVisible()) { if (m_ukeyPasswordEdit) { m_ukeyPasswordEdit->readOnly(true); } } return; } if (m_deviceInfo->deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { setUkeyTypeTip(tr("Failed to verify %1, you still have %2 verification opportunities") .arg(getDeviceType_tr(m_deviceInfo->deviceType)) .arg(maxFailedTimes - m_failMap[m_curUserInfo->uid()][m_deviceInfo->id])); } else { setLoginTypeTip(tr("Failed to verify %1, you still have %2 verification opportunities") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType)) .arg(maxFailedTimes - m_failMap[m_curUserInfo->uid()][m_deviceInfo->id])); } } } if (!isBioSuccess) { if (nStatus <= 0) { qDebug() << "Biometric dbus error:" << nStatus; } if (m_uCurLoginOptType == LOGINOPT_TYPE_QRCODE && nStatus == 1) { m_isNetworkErr = true; setQRCodeMsg(tr("Abnormal network")); startBioAuth(10000); } else if (m_uCurLoginOptType == LOGINOPT_TYPE_QRCODE && nStatus == -4) { m_isNetworkErr = true; setQRCodeMsg(tr("Acquisition failure")); startBioAuth(10000); } else if (m_uCurLoginOptType == LOGINOPT_TYPE_FACE && nStatus == 1) { m_fTimeoutTimes += 1; if (m_fTimeoutTimes == UniAuthService::instance()->getFTimeoutTimes()) { if (m_fRetryButton) { m_fRetryButton->show(); } if (m_labelFace) { m_labelFace->hide(); } setLoginTypeTip(tr("Face recognition waiting time out, please click refresh or enter the " "password to unlock.")); m_fTimeoutTimes = 0; } else { startBioAuth(); } } else { startBioAuth(); } if (nStatus >= 2 && m_deviceInfo) { if (m_deviceInfo->deviceType == BioT_Face) { QImage imgFailed; setFaceImg(imgFailed, 1); } } } } else { // onShowMessage("验证成功!", QLightDM::Greeter::MessageTypeInfo); setDirLogin(); isBioSuccess = true; onBiometricButtonClicked(); } } void AuthDialog::onBiometricButtonClicked() { // 当前没有设备 if (!m_deviceInfo) { qWarning() << "未选择生物设备--"; return; } authMode = BIOMETRIC; startAuth(); } void AuthDialog::setUkeyTypeTip(QString text) { if (m_ukeyMessageLabel) { QString textTip = text; if (!textTip.isEmpty()) { // QFontMetrics font(m_ukeyMessageLabel->font()); // QString textTip = font.elidedText(textTip, Qt::ElideRight, m_messageLabel->width()-8); m_ukeyMessageLabel->setTipText(textTip); m_ukeyMessageLabel->show(); } else { m_ukeyMessageLabel->hide(); } } } void AuthDialog::setLoginTypeTip(QString strLoginTypeTip) { m_strLoginTypeTip = strLoginTypeTip; if (!m_strLoginTypeTip.isEmpty()) { m_labelLoginTypeTip->setTipText(m_strLoginTypeTip); m_labelLoginTypeTip->show(); } else { m_labelLoginTypeTip->hide(); } } void AuthDialog::setLoginMsg(QString strLoginMsg) { setLoginTypeTip(strLoginMsg); } void AuthDialog::onBiometricDbusChanged(bool bActive) { qDebug() << "BiometricDbus:" << bActive; if (bActive) { QTimer::singleShot(1000, this, [=]() { qDebug() << "OnDelay init biometric!!"; if (!m_biometricProxy) { m_biometricProxy = new BiometricHelper(this); maxFailedTimes = UniAuthService::instance()->getMaxFailedTimes(); } // 服务没启动,或者打开DBus连接出错 if (!m_biometricProxy->isValid()) { qWarning() << "An error occurs when connect to the biometric DBus"; if (m_deviceInfo) { if (!m_loginOpts || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_deviceInfo = DeviceInfoPtr(); } } switchLoginOptType(LOGINOPT_TYPE_PASSWORD); return; } // 初始化生物识别认证UI initLoginOptsWidget(); // 没有可用设备,不启用生物识别认证 if (m_loginOpts->getLoginOptCount() < 1) { qWarning() << "No available devices"; if (m_deviceInfo) { if (!m_loginOpts || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_deviceInfo = DeviceInfoPtr(); } } return; } // 获取默认设备 if (m_loginOpts) { if (m_curUserInfo->name() == "*login") m_deviceName = m_loginOpts->getDefaultDevice(m_strManualLoginName); else m_deviceName = m_loginOpts->getDefaultDevice(m_curUserInfo->name()); } qDebug() << m_deviceName; if (m_deviceInfo) { if (!m_loginOpts || !m_loginOpts->findDeviceById(m_deviceInfo->id) || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { m_deviceInfo = DeviceInfoPtr(); } } // 如果默认设备为空的话,第一次不启动生物识别认证 if (m_deviceName.isEmpty() && !m_deviceInfo) { qDebug() << "No default device"; return; } // 第一次,获取默认设备的设备信息,之后使用的则是从设备选择窗口传出的设备信息 if (!m_deviceInfo) { m_deviceInfo = m_loginOpts->findDeviceByName(m_deviceName); if (!m_deviceInfo || m_loginOpts->isDeviceDisable(m_deviceInfo->id)) { qDebug() << "Device Not found or disable: " << m_deviceName; m_deviceInfo = DeviceInfoPtr(); return; } } initBiometricUI(); // 已经错误超过3次 if (m_failMap.contains(m_curUserInfo->uid()) && m_failMap[m_curUserInfo->uid()][m_deviceInfo->id] >= maxFailedTimes) { if (m_deviceInfo->deviceType == UniT_Remote) { setLoginTypeTip(tr("Failed to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); } else { setLoginTypeTip(tr("Unable to verify %1, please enter password to unlock") .arg(getDeviceType_lowerTr(m_deviceInfo->deviceType))); if (m_deviceInfo->deviceType == BioT_Face) { QImage imgFailed; setFaceImg(imgFailed, 1); } } m_loginOpts->setDeviceDisable(m_deviceInfo->id, true); return; } if (m_deviceInfo->deviceType == LOGINOPT_TYPE_GENERAL_UKEY) { switchLoginOptType(LOGINOPT_TYPE_PASSWORD); m_loginOpts->setSelectedPassword(); return; } switchLoginOptType(m_loginOpts->convertDeviceType(m_deviceInfo->deviceType)); // 自行校验生物设备,pam greeter校验密码 startBioAuth(); }); } } void AuthDialog::onFRetryButtonClicked() { if (m_fRetryButton) { m_fRetryButton->hide(); } switchLoginOptType(LOGINOPT_TYPE_FACE); startBioAuth(); } void AuthDialog::setWillLoginUser(QString strUserName, bool isOneKeyLogin) { m_strServiceUser = strUserName; m_isOneKeyLogin = isOneKeyLogin; } void AuthDialog::onCustomRequestAccount(QString strName) { qDebug() << "onCustomRequestAccount:" << strName; Q_EMIT customRequestAccount(strName); } void AuthDialog::onCustomAuthResult(int nResult, QString strMsg) { if (m_uCurLoginOptType != LOGINOPT_TYPE_CUSTOM) { qDebug() << "onCustomAuthResult error Current Auth type is not costom!!"; return; } if (nResult) { if (!isBioSuccess) { qDebug() << "onCustomAuthResult:" << nResult << strMsg; startBioAuth(); } } else { qDebug() << "onCustomAuthResult success!!"; setDirLogin(); isBioSuccess = true; onBiometricButtonClicked(); } } QString AuthDialog::onCustomRequest(QString strReqJson) { qDebug() << "onCustomRequest:" << strReqJson; QJsonParseError jsonParseError; QJsonObject retObj; retObj["Ret"] = 0; retObj["Message"] = "Success"; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strReqJson.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { retObj["Ret"] = -1; retObj["Message"] = "Parse request json failed!!"; qWarning() << "Parse request json failed!!"; return QString(QJsonDocument(retObj).toJson()); } else { QJsonObject rootObj = jsonDoc.object(); if (rootObj.isEmpty()) { qWarning() << "JSON串为空"; retObj["Ret"] = -1; retObj["Message"] = "Request Json is null!!"; qWarning() << "Request Json is null!!"; return QString(QJsonDocument(retObj).toJson()); } else { QJsonObject contentObj; QString reqType = rootObj.value("ReqType").toString(); qInfo() << "Req type: " << reqType; if (reqType == "GetInformations") { // 请求信息 QString strType = rootObj.value("Type").toString(); if (strType.contains("CurrentUser")) { // 当前用户信息 if (m_curUserInfo->uid() >= 0 && !m_curUserInfo->name().isEmpty() && !m_curUserInfo->name().startsWith("*")) { QJsonObject user; user["Name"] = m_curUserInfo->name(); user["Id"] = (double)(m_curUserInfo->uid()); user["HeadImg"] = m_curUserInfo->headImage(); user["RealName"] = m_curUserInfo->fullName(); contentObj["CurrentUser"] = user; } else { retObj["Ret"] = -1; retObj["Message"] = "Current user is invalid!"; } } } retObj["Content"] = contentObj; return QString(QJsonDocument(retObj).toJson()); } } } int AuthDialog::onCustomPlugEnv() { return LoginPluginInterface::LOGINPLUGINENV_LOGIN; } void AuthDialog::setCustomAuthDefault(bool isDefault) { m_isCustomDefault = isDefault; } QString AuthDialog::getDeviceType_tr(int deviceType) { switch (deviceType) { case BioT_FingerPrint: return tr("FingerPrint"); case BioT_FingerVein: return tr("FingerVein"); case BioT_Iris: return tr("Iris"); case BioT_Face: return tr("Face"); case BioT_VoicePrint: return tr("VoicePrint"); case UniT_General_Ukey: return tr("Ukey"); case UniT_Remote: return tr("QRCode"); default: return ""; } } QString AuthDialog::getDeviceType_lowerTr(int deviceType) { switch (deviceType) { case BioT_FingerPrint: return tr("fingerprint"); case BioT_FingerVein: return tr("fingervein"); case BioT_Iris: return tr("iris"); case BioT_Face: return tr("face"); case BioT_VoicePrint: return tr("voiceprint"); case UniT_General_Ukey: return tr("Ukey"); case UniT_Remote: return tr("QRCode"); default: return ""; } } void AuthDialog::startBioWaiting(unsigned uCurLoginOptType) { if (uCurLoginOptType == m_nCurBioWaitingType || (uCurLoginOptType != LOGINOPT_TYPE_FINGERPRINT && uCurLoginOptType != LOGINOPT_TYPE_FINGERVEIN && uCurLoginOptType != LOGINOPT_TYPE_IRIS && uCurLoginOptType != LOGINOPT_TYPE_VOICEPRINT)) { return; } m_nCurBioWaitingType = uCurLoginOptType; if (!m_timerBioWaiting) { m_timerBioWaiting = new QTimer(this); m_timerBioWaiting->setInterval(100); } if (m_timerBioWaiting) { if (m_timerBioWaiting->isActive()) { m_timerBioWaiting->stop(); } // 重新连接信号槽 disconnect(m_timerBioWaiting, nullptr, this, nullptr); connect(m_timerBioWaiting, &QTimer::timeout, this, [&, this, uCurLoginOptType]() { m_nBioWaitingFrame += 1; QString strPicName = ""; switch (uCurLoginOptType) { case LOGINOPT_TYPE_FINGERPRINT: { if (m_nBioWaitingFrame >= 15) { m_nBioWaitingFrame = 0; } strPicName = QString(":/image/assets/biowaiting/fingerprint%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_FINGERVEIN: { if (m_nBioWaitingFrame >= 12) { m_nBioWaitingFrame = 0; } strPicName = QString(":/image/assets/biowaiting/fingervein%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_IRIS: { if (m_nBioWaitingFrame >= 12) { m_nBioWaitingFrame = 0; } strPicName = QString(":/image/assets/biowaiting/iris%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_VOICEPRINT: { if (m_nBioWaitingFrame >= 8) { m_nBioWaitingFrame = 0; } strPicName = QString(":/image/assets/biowaiting/voiceprint%1.svg").arg(m_nBioWaitingFrame); } break; default: break; } if (!strPicName.isEmpty()) { QPixmap pixmap; if (QPixmapCache::find(strPicName, &pixmap)) { if (m_labelBioWaiting) { m_labelBioWaiting->setPixmap( scaledSmoothPixmap(pixmap, WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE)); } } else { pixmap = QPixmap(strPicName); if (!pixmap.isNull()) { QPixmapCache::insert(strPicName, pixmap); if (m_labelBioWaiting) { m_labelBioWaiting->setPixmap( scaledSmoothPixmap(pixmap, WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE)); } } } } }); m_timerBioWaiting->start(); m_nBioWaitingFrame = 0; QString strPicName = ""; switch (uCurLoginOptType) { case LOGINOPT_TYPE_FINGERPRINT: { strPicName = QString(":/image/assets/biowaiting/fingerprint%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_FINGERVEIN: { strPicName = QString(":/image/assets/biowaiting/fingervein%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_IRIS: { strPicName = QString(":/image/assets/biowaiting/iris%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_VOICEPRINT: { strPicName = QString(":/image/assets/biowaiting/voiceprint%1.svg").arg(m_nBioWaitingFrame); } break; default: break; } if (!strPicName.isEmpty()) { QPixmap pixmap; if (QPixmapCache::find(strPicName, &pixmap)) { if (m_labelBioWaiting) { m_labelBioWaiting->setPixmap( scaledSmoothPixmap(pixmap, WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE)); } } else { pixmap = QPixmap(strPicName); if (!pixmap.isNull()) { QPixmapCache::insert(strPicName, pixmap); if (m_labelBioWaiting) { m_labelBioWaiting->setPixmap( scaledSmoothPixmap(pixmap, WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE)); } } } } } } void AuthDialog::stopBioWaiting(bool isDisable /* = false*/) { if (m_timerBioWaiting) { if (m_timerBioWaiting->isActive()) { m_timerBioWaiting->stop(); } } m_nBioWaitingFrame = 0; if (isDisable) { QString strPicName = ""; switch (m_nCurBioWaitingType) { case LOGINOPT_TYPE_FINGERPRINT: { strPicName = QString(":/image/assets/biowaiting/fingerprint%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_FINGERVEIN: { strPicName = QString(":/image/assets/biowaiting/fingervein%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_IRIS: { strPicName = QString(":/image/assets/biowaiting/iris%1.svg").arg(m_nBioWaitingFrame); } break; case LOGINOPT_TYPE_VOICEPRINT: { strPicName = QString(":/image/assets/biowaiting/voiceprint%1.svg").arg(m_nBioWaitingFrame); } break; default: break; } if (!strPicName.isEmpty()) { QPixmap pixmap; if (QPixmapCache::find(strPicName, &pixmap)) { if (m_labelBioWaiting) { m_labelBioWaiting->setPixmap( scaledSmoothPixmap(pixmap, WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE)); } } else { pixmap = QPixmap(strPicName); if (!pixmap.isNull()) { QPixmapCache::insert(strPicName, pixmap); if (m_labelBioWaiting) { m_labelBioWaiting->setPixmap( scaledSmoothPixmap(pixmap, WIDGET_HEIGHT_HEADIMAGE, WIDGET_HEIGHT_HEADIMAGE)); } } } } } m_nCurBioWaitingType = -1; } void AuthDialog::inhibitShow(bool isInhibit) { if (m_isInhibitShow != isInhibit) { if (!isInhibit) { if (isHidden() || !isVisible()) { show(); } } else { if (!isHidden() || isVisible()) { hide(); } } m_isInhibitShow = isInhibit; } } void AuthDialog::initPamUkeyLoadingWidget() { if (!m_pamUkeyLoadingWidget) return; if (!m_pamUkeyLoadingButton) { m_pamUkeyLoadingButton = new QPushButton(m_pamUkeyLoadingWidget); m_pamUkeyLoadingButton->setAttribute(Qt::WA_TransparentForMouseEvents, true); m_pamUkeyLoadingButton->setFlat(true); m_pamUkeyLoadingButton->installEventFilter(this); m_pamUkeyLoadingButton->setStyleSheet("QPushButton{text-align:center;color: rgba(255, 255, 255, 255);border: " "none;border-radius: 4px;outline: none;}"); m_pamUkeyLoadingText = new CustomLabel(m_pamUkeyLoadingWidget); m_pamUkeyLoadingText->setText(tr("Insert the ukey into the USB port")); QVBoxLayout *m_pamukeyLoadingLayout = new QVBoxLayout(m_pamUkeyLoadingWidget); m_pamukeyLoadingLayout->addWidget(m_pamUkeyLoadingButton); m_pamukeyLoadingLayout->addWidget(m_pamUkeyLoadingText); m_pamukeyLoadingLayout->setAlignment(Qt::AlignCenter); } } // 这里临时用读取配置文件的方式替代原本调用dbus接口的方式,避免kylin-mfa的dbus接口 // 阻塞导致锁屏界面卡住的问题,后续kylin-mfa提供库函数的方式来获取双因子认证方法。 QString AuthDialog::getAuthMethod() { QString configPath = "/etc/pwdless/default.conf"; QFile file(configPath); if (!file.exists()) { return ""; } QSettings settings(configPath, QSettings::IniFormat); // 检查[global]区块下mfaAuth QVariant mfaAuthVar = settings.value("global/mfaAuth", "false"); if (mfaAuthVar.toString().toLower() != "true") { return ""; } // 检查[mfa]区块下method QVariant methodVar = settings.value("mfa/method", ""); QString method = methodVar.toString().trimmed(); if (method.isEmpty()) { return ""; } return method; } void AuthDialog::showAuthMethodTip() { if (m_authMethodLabel) { if (authMethod == "otp") { m_authMethodLabel->setText(tr("Two-Factor OTP Dynamic Password Authentication")); m_authMethodLabel->show(); } else if (authMethod == "ukey") { m_authMethodLabel->setText(tr("Two-Factor Security Key Authentication")); m_authMethodLabel->show(); } } updateAuthSize(); } void AuthDialog::setBiometricAuthDisabledStatus(bool locked) { if (locked) { if (m_loginOpts) { m_loginOpts->stopAuth(); m_loginOpts->setAllDeviceDisable(true); if (m_bioTimer && m_bioTimer->isActive()) { m_bioTimer->stop(); } } if (m_deviceInfo && m_deviceInfo->deviceType == LOGINOPT_TYPE_FACE) { QImage imgFailed; setFaceImg(imgFailed, 1); } else if (m_deviceInfo && m_deviceInfo->deviceType == LOGINOPT_TYPE_QRCODE) { QImage nullImage; setQRCode(nullImage); } } else { if (m_loginOpts) { m_loginOpts->setAllDeviceDisable(false); } if (m_deviceInfo) { m_loginOpts->startAuth(m_deviceInfo, m_curUserInfo->uid()); } } } ukui-screensaver/src/common/0000775000175000017500000000000015172041106015047 5ustar fengfengukui-screensaver/src/common/utils.h0000664000175000017500000000237715172041106016371 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef UTILS_H #define UTILS_H #include "global_utils.h" /** * @brief checkIslivecd 检查是否为试用模式,试用模式直接退出进程 */ void checkIslivecd(); /** * @brief KillFocusOfKydroid 通知Kydroid组件取消焦点 */ void KillFocusOfKydroid(); /** * @brief 判断大写键状态 * @return true: 大写锁定 */ bool checkCapsLockState(); bool ispicture(QString filepath); double getDefaultFontSize(); double getUserFontSize(QString userName); QString getUserThemeColor(QString userName); void* getX11Display(); void* getX11Connection(); #endif // UTILS_H ukui-screensaver/src/common/commonfunc.cpp0000664000175000017500000003331615172041106017725 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "commonfunc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE extern void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); QT_END_NAMESPACE #define BLUR_RADIUS 300 // 设置一下鼠标的居于主屏中央 void setCursorCenter() { auto setCursorPos = [=](QPoint p) { QCursor::setPos(p); }; if (!qApp->primaryScreen()) { QObject::connect(qApp, &QGuiApplication::primaryScreenChanged, [=] { static bool first = true; if (first) { setCursorPos(qApp->primaryScreen()->geometry().center()); first = false; } }); } else { setCursorPos(qApp->primaryScreen()->geometry().center()); } } /*修改图片缩放机制,图片长宽不一致时,先取图片中央的部分*/ QPixmap scaledPixmap(QPixmap src) { QPixmap rectPixmap; if (src.width() > src.height()) { QPixmap iconPixmap = src.copy((src.width() - src.height()) / 2, 0, src.height(), src.height()); // 根据label高度等比例缩放图片 rectPixmap = iconPixmap.scaledToHeight(src.height()); } else { QPixmap iconPixmap = src.copy(0, (src.height() - src.width()) / 2, src.width(), src.width()); // 根据label宽度等比例缩放图片 rectPixmap = iconPixmap.scaledToWidth(src.width()); } return rectPixmap; } QPixmap scaledSmoothPixmap(const QPixmap &src, int nWidth, int nHeight) { if (src.isNull()) { return QPixmap(); } QPixmap pixmapa(src); QPixmap pixmap(nWidth, nHeight); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); painter.drawPixmap(0, 0, nWidth, nHeight, pixmapa); return pixmap; } QPixmap PixmapToRound(const QPixmap &src, int radius) { if (src.isNull()) { return QPixmap(); } QPixmap pixmapa(src); QPixmap pixmap(radius * 2, radius * 2); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); QPainterPath path; path.addEllipse(0, 0, radius * 2, radius * 2); painter.setClipPath(path); painter.drawPixmap(0, 0, radius * 2, radius * 2, pixmapa); return pixmap; } QPixmap PixmapToRound(const QPixmap &src, int leftTop, int rightTop, int leftBottom, int rightBottom) { if (src.isNull()) { return QPixmap(); } QPixmap roundedPixmap(src.size()); roundedPixmap.fill(Qt::transparent); QPainter painter(&roundedPixmap); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); QPainterPath path; path.moveTo(leftTop, 0); path.arcTo(QRect(0, 0, 2 * leftTop, 2 * leftTop), 90, 90); path.lineTo(0, src.height() - leftBottom); path.arcTo(QRect(0, src.height() - 2 * leftBottom, 2 * leftBottom, 2 * leftBottom), 180, 90); path.lineTo(src.width() - rightBottom, src.height()); path.arcTo( QRect(src.width() - 2 * rightBottom, src.height() - 2 * rightBottom, 2 * rightBottom, 2 * rightBottom), 270, 90); path.lineTo(src.width(), rightTop); path.arcTo(QRect(src.width() - 2 * rightTop, 0, 2 * rightTop, 2 * rightTop), 0, 90); path.closeSubpath(); painter.setClipPath(path); painter.drawPixmap(0, 0, src); painter.end(); return roundedPixmap; } const QPixmap loadSvg(const QString &path, const QString color, int size) { int origSize = size; const auto ratio = qApp->devicePixelRatio(); if (2 == ratio) { size += origSize; } else if (3 == ratio) { size += origSize; } QPixmap pixmap(size, size); QSvgRenderer renderer(path); pixmap.fill(Qt::transparent); QPainter painter; painter.begin(&pixmap); renderer.render(&painter); painter.end(); pixmap.setDevicePixelRatio(ratio); return drawSymbolicColoredPixmap(pixmap, color); } QPixmap drawSymbolicColoredPixmap(const QPixmap &source, QString cgColor) { QImage img = source.toImage(); for (int x = 0; x < img.width(); x++) { for (int y = 0; y < img.height(); y++) { auto color = img.pixelColor(x, y); if (color.alpha() > 0) { if ("white" == cgColor) { color.setRed(255); color.setGreen(255); color.setBlue(255); img.setPixelColor(x, y, color); } else if ("black" == cgColor) { color.setRed(0); color.setGreen(0); color.setBlue(0); img.setPixelColor(x, y, color); } else if ("gray" == cgColor) { color.setRed(152); color.setGreen(163); color.setBlue(164); img.setPixelColor(x, y, color); } else if ("blue" == cgColor) { color.setRed(61); color.setGreen(107); color.setBlue(229); img.setPixelColor(x, y, color); } else { return source; } } } } return QPixmap::fromImage(img); } QPixmap filledSymbolicColoredPix( const QPixmap &source, const QColor &baseColor, const QColor &symbolicColor, int colorDifference) { if (source.isNull()) return source; QImage img = source.toImage(); for (int x = 0; x < img.width(); x++) { for (int y = 0; y < img.height(); y++) { QColor color = img.pixelColor(x, y); if (color.alpha() > 0.3) { if (qAbs(color.red() - symbolicColor.red()) < colorDifference && qAbs(color.green() - symbolicColor.green()) < colorDifference && qAbs(color.blue() - symbolicColor.blue()) < colorDifference) { color.setRed(baseColor.red()); color.setGreen(baseColor.green()); color.setBlue(baseColor.blue()); img.setPixelColor(x, y, color); } } } } return QPixmap::fromImage(img); } QPixmap getLoadingIcon(int size) { QPixmap icon = QIcon::fromTheme("ukui-loading-0-symbolic").pixmap(size, size); return drawSymbolicColoredPixmap(icon, "white"); } QPixmap loadFromFile(QString strPath) { QImageReader reader; reader.setFileName(strPath); reader.setAutoTransform(true); reader.setDecideFormatFromContent(true); return QPixmap::fromImageReader(&reader); } QPixmap scaleBlurPixmap(int width, int height, QString url) { QFile imgFile(url); if (!imgFile.exists()) { // qDebug()<< "pixmap file not exist!"; return QPixmap(); } QImageReader imgReader; imgReader.setFileName(url); imgReader.setAutoTransform(true); imgReader.setDecideFormatFromContent(true); QPixmap pixmap = blurPixmap(QPixmap::fromImageReader(&imgReader)); return pixmap.scaled(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); } //QPixmap blurPixmap(QPixmap pixmap) //{ // QPainter painter(&pixmap); // QImage srcImg = pixmap.toImage(); // qt_blurImage(&painter, srcImg, BLUR_RADIUS, false, false); // // 在设置Qt::WA_TranslucentBackground属性后,模糊图片会导致锁屏界面透明 // // 因此这里修改image图形的alpha值为255. // for (int y = 0; y < srcImg.height(); ++y) { // QRgb *row = (QRgb *)srcImg.scanLine(y); // for (int x = 0; x < srcImg.width(); ++x) { // ((unsigned char *)&row[x])[3] = 255; // } // } // painter.end(); // return QPixmap::fromImage(srcImg); //} // 模糊算法优化,之前算法得到的图片会有黑边,解决传入图片特别小时,显示异常像素点的问题 QPixmap blurPixmap(QPixmap pixmap) { int blurRadius; if (pixmap.width() > 500) { blurRadius = BLUR_RADIUS; } else { blurRadius = 30; } const int expandSize = blurRadius * 2; // 处理无效或空图像 if (pixmap.isNull() || pixmap.width() == 0 || pixmap.height() == 0) { return pixmap; } // 创建扩展画布(增加边缘区域) QPixmap expanded(pixmap.width() + expandSize*2, pixmap.height() + expandSize*2); expanded.fill(Qt::transparent); // 使用边缘像素填充扩展区域 { QPainter expandPainter(&expanded); // 绘制原始图像到中心位置 expandPainter.drawPixmap(expandSize, expandSize, pixmap); // 生成边缘平铺图案 const QPixmap leftEdge = pixmap.copy(0, 0, 1, pixmap.height()); const QPixmap rightEdge = pixmap.copy(pixmap.width()-1, 0, 1, pixmap.height()); const QPixmap topEdge = pixmap.copy(0, 0, pixmap.width(), 1); const QPixmap bottomEdge = pixmap.copy(0, pixmap.height()-1, pixmap.width(), 1); // 平铺边缘像素到扩展区域 expandPainter.drawTiledPixmap(0, expandSize, expandSize, pixmap.height(), leftEdge); // 左侧 expandPainter.drawTiledPixmap(expandSize + pixmap.width(), expandSize, expandSize, pixmap.height(), rightEdge); // 右侧 expandPainter.drawTiledPixmap(expandSize, 0, pixmap.width(), expandSize, topEdge); // 顶部 expandPainter.drawTiledPixmap(expandSize, expandSize + pixmap.height(), pixmap.width(), expandSize, bottomEdge); // 底部 // 处理四个角落 const QPixmap topLeft = pixmap.copy(0, 0, 1, 1); const QPixmap topRight = pixmap.copy(pixmap.width()-1, 0, 1, 1); const QPixmap bottomLeft = pixmap.copy(0, pixmap.height()-1, 1, 1); const QPixmap bottomRight = pixmap.copy(pixmap.width()-1, pixmap.height()-1, 1, 1); expandPainter.drawTiledPixmap(0, 0, expandSize, expandSize, topLeft); // 左上角 expandPainter.drawTiledPixmap(expandSize + pixmap.width(), 0, expandSize, expandSize, topRight); // 右上角 expandPainter.drawTiledPixmap(0, expandSize + pixmap.height(), expandSize, expandSize, bottomLeft); // 左下角 expandPainter.drawTiledPixmap(expandSize + pixmap.width(), expandSize + pixmap.height(), expandSize, expandSize, bottomRight); // 右下角 } // 执行模糊操作 QImage blurredImg = expanded.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); QImage resultImg(blurredImg.size(), QImage::Format_ARGB32_Premultiplied); { QPainter painter(&resultImg); qt_blurImage(&painter, blurredImg, blurRadius, true, false); } // 裁剪有效区域 resultImg = resultImg.copy(expandSize, expandSize, pixmap.width(), pixmap.height()); // 保留原始透明度信息(移除强制设置alpha) return QPixmap::fromImage(resultImg.convertToFormat(QImage::Format_ARGB32)); } QStringList getAudioFiles(const QString &directoryPath) { QStringList audioFiles; QStringList audioFilters; audioFilters << "*.mp3" << "*.wav" << "*.ogg" << "*.flac" << "*.aac" << "*.m4a" << "*.wma" << "*.opus" << "*.aiff" << "*.ape"; QDir directory(directoryPath); if (!directory.exists()) { qWarning() << "Directory does not exist:" << directoryPath; return audioFiles; } directory.setNameFilters(audioFilters); directory.setFilter(QDir::Files | QDir::NoDotAndDotDot); QFileInfoList fileList = directory.entryInfoList(); foreach (const QFileInfo &fileInfo, fileList) { audioFiles.append(fileInfo.absoluteFilePath()); } return audioFiles; } QString getFirstImageFileIterator(const QString &dirPath) { QStringList imageFilters; imageFilters << "*.png" << "*.jpg" << "*.jpeg" << "*.bmp" << "*.gif" << "*.tiff"; QDirIterator it(dirPath, imageFilters, QDir::Files); if (it.hasNext()) { return it.next(); } return QString(); } QString mapToThumbnailPath(const QString& originalPath) { if (!QFileInfo::exists(originalPath)) { qWarning() << "原始壁纸文件不存在:" << originalPath; return QString(); } QFileInfo originalFileInfo(originalPath); QString originalFileName = originalFileInfo.fileName(); QString thumbnailDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/ukui-control-center/wallpaperData/"; QString pattern = QString("\\d+-usr-share-backgrounds-%1").arg(originalFileName); QRegularExpression regex(pattern, QRegularExpression::CaseInsensitiveOption); QDir dir(thumbnailDir); QFileInfoList files = dir.entryInfoList(QDir::Files); foreach (const QFileInfo &file, files) { if (regex.match(file.fileName()).hasMatch()) { return file.absoluteFilePath(); } } qWarning() << "未找到对应的缩略图文件,原始文件:" << originalFileName; return originalPath; } ukui-screensaver/src/common/configuration.h0000664000175000017500000000356515172041106020100 0ustar fengfeng/* configuration.h * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifndef CONFIGURATION_H #define CONFIGURATION_H #include #include #include class Configuration : public QObject { Q_OBJECT private: explicit Configuration(QObject *parent = nullptr); public: static Configuration *instance(QObject *parent = nullptr); QVariant getValue(const QString &); QVariant getUserConfig(const QString &key); void setValue(const QString &, const QVariant &); bool hasValue(const QString &); void getCurrentUser(const QString userName); bool getLastNumLock(); void saveLastNumLock(bool value); int getRootBackgroundOption(QString userName); bool getIs990(); void initShareConfig(); void setShareConfigValue(const QString &key, const QVariant &value); QString getDefaultBackgroundName(); private: QSettings *configSettings; QSettings *recordSettings; QSettings *userSetting; QString configPath; QGSettings *stylesettings = nullptr; bool hasCheck990 = false; bool is990 = false; QSettings *m_shareSettings = nullptr; static Configuration *m_instance; }; #endif // CONFIGURATION_H ukui-screensaver/src/common/kyrsac.cpp0000664000175000017500000001446215172041035017057 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "kyrsac.h" #include #include static RSA *privateKey2RSA(const QByteArray &priKey) { BIO *pBio = BIO_new_mem_buf(priKey.data(), priKey.size()); // 创建内存 RSA *rsa = PEM_read_bio_RSAPrivateKey(pBio, nullptr, nullptr, nullptr); BIO_free_all(pBio); // 释放内存 return rsa; } static RSA *publicKey2RSA(const QByteArray &pubKey) { BIO *pBio = BIO_new_mem_buf(pubKey.data(), pubKey.size()); // 创建内存 RSA *rsa = PEM_read_bio_RSA_PUBKEY(pBio, nullptr, nullptr, nullptr); BIO_free_all(pBio); // 释放内存 return rsa; } /** * @brief RSAC::signData * 使用私钥对摘要数据进行签名 * @param digest 摘要数据 * @param sign 签名后的数据 * @param priKey 私钥 * @return 执行结果 */ bool KyRSAC::signData(const QByteArray &digest, QByteArray &sign, const QByteArray &priKey) { // 私钥数据转RSA RSA *rsa = privateKey2RSA(priKey); if (rsa == nullptr) { return false; } // 对digest进行签名 unsigned int siglen = 0; QByteArray temp(RSA_size(rsa), 0); RSA_sign(NID_sha1, (const unsigned char *)digest.data(), digest.size(), (unsigned char *)temp.data(), &siglen, rsa); sign.clear(); sign.append(temp.data(), siglen); RSA_free(rsa); return true; } /** * @brief RSAC::verifyData * 使用公钥对摘要数据进行验签 * @param digest 摘要数据 * @param sign 签名后的数据 * @param pubKey 公钥 * @return 执行结果 */ bool KyRSAC::verifyData(const QByteArray &digest, const QByteArray &sign, const QByteArray &pubKey) { // 公钥数据转RSA RSA *rsa = publicKey2RSA(pubKey); if (rsa == nullptr) { return false; } // 对digest、sign进行验签 int ret = RSA_verify( NID_sha1, (const unsigned char *)digest.data(), digest.size(), (const unsigned char *)sign.data(), sign.size(), rsa); RSA_free(rsa); return (ret == 1); } /** * @brief RSAC::encryptData * RSA加密函数,使用公钥对输入数据,进行加密 * @param in 输入数据(明文) * @param out 输出数据(密文) * @param pubKey 公钥 * @return 执行结果 */ bool KyRSAC::encryptData(const QByteArray &in, QByteArray &out, const QByteArray &pubKey) { // 公钥数据转RSA RSA *rsa = publicKey2RSA(pubKey); if (rsa == nullptr) { return false; } // 对任意长度数据进行加密,超长时,进行分段加密 int keySize = RSA_size(rsa); int dataLen = in.size(); const unsigned char *from = (const unsigned char *)in.data(); QByteArray to(keySize, 0); int readLen = 0; do { int select = (keySize - 11) > dataLen ? dataLen : (keySize - 11); RSA_public_encrypt(select, (from + readLen), (unsigned char *)to.data(), rsa, RSA_PKCS1_OAEP_PADDING); dataLen -= select; readLen += select; out.append(to); } while (dataLen > 0); RSA_free(rsa); return true; } /** * @brief RSAC::decryptData * RSA解密函数,使用私钥对输入数据,进行解密 * @param in 输入数据(密文) * @param out 输出数据(解密后的内容) * @param priKey 私钥 * @return 执行结果 */ bool KyRSAC::decryptData(const QByteArray &in, QByteArray &out, const QByteArray &priKey) { // 私钥数据转RSA RSA *rsa = privateKey2RSA(priKey); if (rsa == nullptr) { return false; } // 对任意长度数据进行解密,超长时,进行分段解密 int keySize = RSA_size(rsa); int dataLen = in.size(); const unsigned char *from = (const unsigned char *)in.data(); QByteArray to(keySize, 0); int readLen = 0; do { int size = RSA_private_decrypt(keySize, (from + readLen), (unsigned char *)to.data(), rsa, RSA_PKCS1_OAEP_PADDING); dataLen -= keySize; readLen += keySize; out.append(to.data(), size); } while (dataLen > 0); RSA_free(rsa); return true; } /** * @brief RSAC::genKeyPair * 生成密钥对,并分别保存为文件 * @param priKeyFile 私钥文件名 * @param pubKeyFile 公钥文件名 * @param bits 秘钥长度,一般建议1024及以上 */ void KyRSAC::genKeyPair(const QString &priKeyFile, const QString &pubKeyFile, int bits) { // 生成公钥 RSA *rsa = RSA_generate_key(bits, RSA_F4, nullptr, nullptr); BIO *bp = BIO_new(BIO_s_file()); BIO_write_filename(bp, (void *)pubKeyFile.toStdString().c_str()); PEM_write_bio_RSAPublicKey(bp, rsa); BIO_free_all(bp); // 生成私钥 bp = BIO_new(BIO_s_file()); BIO_write_filename(bp, (void *)priKeyFile.toStdString().c_str()); PEM_write_bio_RSAPrivateKey(bp, rsa, nullptr, nullptr, 0, nullptr, nullptr); CRYPTO_cleanup_all_ex_data(); BIO_free_all(bp); RSA_free(rsa); } /** * @brief RSAC::genKeyPair * 生成密钥对数据 * @param privateKey 私钥数据 * @param publicKey 公钥数据 * @param bits 秘钥长度,一般建议1024及以上 */ void KyRSAC::genKeyPair(QByteArray &privateKey, QByteArray &pubKey, int bits) { // 生成密钥对 RSA *keyPair = RSA_generate_key(bits, RSA_F4, nullptr, nullptr); BIO *pri = BIO_new(BIO_s_mem()); BIO *pub = BIO_new(BIO_s_mem()); PEM_write_bio_RSAPrivateKey(pri, keyPair, nullptr, nullptr, 0, nullptr, nullptr); PEM_write_bio_RSA_PUBKEY(pub, keyPair); // 获取长度 int pri_len = BIO_pending(pri); int pub_len = BIO_pending(pub); privateKey.resize(pri_len); pubKey.resize(pub_len); BIO_read(pri, privateKey.data(), pri_len); BIO_read(pub, pubKey.data(), pub_len); // 内存释放 RSA_free(keyPair); BIO_free_all(pub); BIO_free_all(pri); } ukui-screensaver/src/common/global_utils.cpp0000664000175000017500000002333615172041106020242 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "global_utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #define FACEPATH "/usr/share/ukui/faces/" #ifndef STRNLEN_MAX #define STRNLEN_MAX 1024 * 1024 * 1024 #endif #define ASSERT_NOT_NULL(x, ret) \ if (!x) { \ return ret; \ } /** * @brief 对给定的字符串进行strip操作,删减字符串前后的指定字符;注意该操作会修改原字符串 * * @param str 需要进行strip的字符串指针 * @param ch 需要删除的字符 */ static void strstrip(char *str, char ch) { if (strnlen(str, STRNLEN_MAX) == 0) return; char *startPos = str; while (*startPos != '\0' && *startPos == ch) startPos++; if (*startPos == '\0') { str[0] = 0; return; } char *endPos = str + strnlen(str, STRNLEN_MAX) - 1; while (endPos != str && *endPos == ch) endPos--; memmove(str, startPos, endPos - startPos + 1); *(str + (endPos - startPos) + 1) = 0; } /** * @brief 删除给定字符串前后的空格、制表符、换行符,注意该操作会修改原字符串 * * @param str 需要进行strip操作的字符串指针 */ static void strstripspace(char *str) { if (strnlen(str, STRNLEN_MAX) == 0) return; char *startPos = str; while (*startPos != '\0' && isspace(*startPos)) startPos++; if (*startPos == '\0') { str[0] = 0; return; } char *endPos = str + strnlen(str, STRNLEN_MAX) - 1; while (endPos != str && isspace(*endPos)) endPos--; memmove(str, startPos, endPos - startPos + 1); *(str + (endPos - startPos) + 1) = 0; } static char *get_val_from_file(FILE *fp, const char *key) { if (!fp) return NULL; char *val = NULL; char buf[1024] = { 0 }; while (fgets(buf, 1024, fp)) { if (strncmp(buf, key, strnlen(key, STRNLEN_MAX)) == 0) { val = strdup((char *)buf + strnlen(key, STRNLEN_MAX) + 1); break; } } return val; } bool isGreeterMode() { static int nIsGreeter = -1; if (nIsGreeter < 0) { QString username = getenv("USER"); if (username == "lightdm") { nIsGreeter = 1; } else { nIsGreeter = 0; } } return (bool)(nIsGreeter); } bool isCurUserSelf(QString strUserName) { QString username = getenv("USER"); if (username == strUserName) { return true; } else { return false; } } static char *SystemGetHostCloudPlatform() { char *cloudplat = (char *)malloc(sizeof(char) * 65); ASSERT_NOT_NULL(cloudplat, NULL); #ifdef __linux__ cloudplat[0] = 0; char buf[256] = { 0 }; bool res_flag = false; char *type = NULL; if (!access("/usr/local/ctyun/clink/Mirror/Registry/Default", F_OK)) { snprintf(cloudplat, 65, "ctyun"); return cloudplat; } FILE *fp = fopen("/etc/hw-cloud.conf", "rt"); if (fp) { type = get_val_from_file(fp, "platform"); fclose(fp); } if (type) { strstripspace(type); free(cloudplat); return type; } if (geteuid() == 0) // root 用户,可以用dmidecode { FILE *pipeLine = popen("dmidecode -s chassis-manufacturer", "r"); if (__glibc_likely(pipeLine != NULL)) { fgets(buf, 255 * sizeof(char), pipeLine); strstripspace(buf); if (strcmp(buf, "Huawei Inc.") == 0) // 华为云 { snprintf(cloudplat, 65, "huawei"); res_flag = true; } pclose(pipeLine); } if (!res_flag) { pipeLine = popen("dmidecode -s chassis-asset-tag", "r"); if (__glibc_likely(pipeLine != NULL)) { fgets(buf, 255 * sizeof(char), pipeLine); strstripspace(buf); if (strcmp(buf, "HUAWEICLOUD") == 0) // 华为云 { snprintf(cloudplat, 65, "huawei"); } pclose(pipeLine); } } } else // 普通用户,只能读取文件 { FILE *fp = fopen("/sys/devices/virtual/dmi/id/chassis_vendor", "rt"); if (__glibc_likely(fp != NULL)) { fgets(buf, 255 * sizeof(char), fp); strstripspace(buf); if (strcmp(buf, "Huawei Inc.") == 0) // 华为云 { snprintf(cloudplat, 65, "huawei"); res_flag = true; } fclose(fp); } if (!res_flag) { fp = fopen("/sys/devices/virtual/dmi/id/chassis_asset_tag", "r"); if (__glibc_likely(fp != NULL)) { fgets(buf, 255 * sizeof(char), fp); strstripspace(buf); if (strcmp(buf, "HUAWEICLOUD") == 0) // 华为云 { snprintf(cloudplat, 65, "huawei"); } fclose(fp); } } } if (strnlen(cloudplat, STRNLEN_MAX) == 0) { snprintf(cloudplat, 65, "none"); } #endif return cloudplat; } QString getHostCloudPlatform() { static QString strPlatform = ""; if (strPlatform.isEmpty()) { char *platForm = SystemGetHostCloudPlatform(); if (platForm) { strPlatform = platForm; free(platForm); platForm = NULL; } } return strPlatform; } bool isCommunity() { static int sIsCommunity = -1; if (sIsCommunity == -1) { QString filename = "/etc/os-release"; QSettings osSettings(filename, QSettings::IniFormat); QString versionID = osSettings.value("VERSION_ID").toString(); if (versionID.compare("22.04", Qt::CaseSensitive)) { sIsCommunity = 0; } else { sIsCommunity = 1; } } return (bool)(sIsCommunity); } static char *SystemGetSystemName() { char *sysname = NULL; #ifdef __linux__ FILE *fp = fopen("/etc/os-release", "r"); if (!fp) return NULL; sysname = get_val_from_file(fp, "NAME"); if (!sysname) return NULL; strstrip(sysname, '\n'); strstrip(sysname, '\"'); fclose(fp); #endif return sysname; } bool isOpenkylin() { char *systemNamePtr = SystemGetSystemName(); QString systemName; // 使用完后释放内存 if (systemNamePtr) { systemName = QString::fromLatin1(systemNamePtr); free(systemNamePtr); } if (systemName.compare("openkylin", Qt::CaseInsensitive) == 0) { return true; } return false; } QString getDefaultFace() { // 遍历头像目录 QDir facesDir = QDir(FACEPATH); foreach (QString filename, facesDir.entryList(QDir::Files)) { QString fullface = QString("%1%2").arg(FACEPATH).arg(filename); // 社区版不加载商业默认头像 if ((isCommunity() || isOpenkylin()) && fullface.endsWith("commercial.png")) { continue; } // 商业版不加载社区默认头像 if ((!isCommunity() && !isOpenkylin()) && fullface.endsWith("community.png")) { continue; } if (fullface.endsWith("default.png")) { continue; } if (!fullface.contains("-")) { continue; } return fullface; } return "/usr/share/ukui/faces/default.png"; } void startFcitx() { QString fcitxCommand = "fcitx5 -d &"; runCommand(fcitxCommand); QString vkdcommand = "/usr/bin/kylin-virtual-keyboard-xwayland &"; runCommand(vkdcommand); QString sogouEngineCommand = "/opt/apps/com.cpis/bin/cpis-engine-service --ini /opt/apps/com.cpis/etc/ise.ini --type dbus &"; runCommand(sogouEngineCommand); QString sogouPanelCommand = "/opt/apps/com.cpis/bin/cpis-panel-service --ini /opt/apps/com.cpis/etc/isp.ini --type dbus &"; runCommand(sogouPanelCommand); } void runCommand(const QString &command) { QProcess *process = new QProcess(); process->start(command); QObject::connect(process, &QProcess::readyReadStandardOutput, [process]() { qDebug() << "Standard Output:" << process->readAllStandardOutput(); }); QObject::connect(process, &QProcess::readyReadStandardError, [process]() { qDebug() << "Standard Error:" << process->readAllStandardError(); }); } bool isProcessRunning(const QString &procName) { QDir dir("/proc"); QStringList pids = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); foreach (const QString &pid, pids) { QFile cmdline("/proc/" + pid + "/cmdline"); if (cmdline.open(QIODevice::ReadOnly)) { if (cmdline.readAll().contains(procName.toLatin1())) { return true; } } } return false; } ukui-screensaver/src/common/displayservice.h0000664000175000017500000000316615172041035020255 0ustar fengfeng/* displayservice.h * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifndef DISPLAYSERVICE_H #define DISPLAYSERVICE_H #include #include #include enum DisplayMode { DISPLAY_MODE_ORI = 1, DISPLAY_MODE_CLONE = 2, DISPLAY_MODE_EXTEND = 3, DISPLAY_MODE_ONLY_OUT = 4 }; class DisplayService : public QObject { Q_OBJECT private: explicit DisplayService(QObject *parent = nullptr); public: static DisplayService *instance(QObject *parent = nullptr); bool switchDisplayMode(DisplayMode targetMode); void setOneDisplayMode(); //单屏模式设置 void setCurUserName(QString strUserName); bool isSaveParamInUsed(); int isJJW7200(); private: void getMonitors(); QMap> monitors; QStringList monitorNames; QString m_strSaveParamPath = ""; QString m_strUserName = ""; static DisplayService *m_instance; }; #endif // DISPLAYSERVICE_H ukui-screensaver/src/common/biodefines.h0000664000175000017500000002117415172041035017335 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifndef BIODEFINES_H #define BIODEFINES_H #include #include #define BIOMETRIC_DBUS_SERVICE "org.ukui.Biometric" #define BIOMETRIC_DBUS_PATH "/org/ukui/Biometric" #define BIOMETRIC_DBUS_INTERFACE "org.ukui.Biometric" #define UKUI_BIOMETRIC_IMAGES_PATH "/usr/share/ukui-biometric/images/" #define UKUI_BIOMETRIC_CONFIG_PATH ".biometric_auth/ukui_biometric.conf" #define UKUI_BIOMETRIC_SYS_CONFIG_PATH "/etc/biometric-auth/ukui-biometric.conf" #define SHARE_BIOMETRIC_CONFIG_PATH \ "/var/lib/lightdm-data/%1/ukui-biometric.conf" // greeter、screensaver、polkit share conf /** * @brief StatusChanged D-Bus 信号触发时的状态变化类型 */ enum StatusType { STATUS_DEVICE, STATUS_OPERATION, STATUS_NOTIFY }; /** * @brief 识别、终止操作等DBus调用的结果,即返回值里的 result */ enum DBusResult { DBUS_RESULT_SUCCESS = 0, DBUS_RESULT_NOTMATCH = -1, DBUS_RESULT_ERROR = -2, DBUS_RESULT_DEVICEBUSY = -3, DBUS_RESULT_NOSUCHDEVICE = -4, DBUS_RESULT_PERMISSIONDENIED = -5 }; /** * @brief 识别操作(Identify)的ops状态 */ /* 定义操作类型 */ typedef enum { OPS_TYPE_COMM = 0, OPS_TYPE_OPEN, OPS_TYPE_ENROLL, OPS_TYPE_VERIFY, OPS_TYPE_IDENTIFY, OPS_TYPE_CAPTURE, OPS_TYPE_SEARCH, OPS_TYPE_CLEAN, OPS_TYPE_GET_FLIST, OPS_TYPE_RENAME, OPS_TYPE_CLOSE, } BioOpsType; /* * 定义各种操作结果 */ typedef enum { OPS_COMM_SUCCESS = OPS_TYPE_COMM * 100, /** 空闲状态 **/ OPS_COMM_FAIL, /** 操作失败 **/ OPS_COMM_NO_MATCH = OPS_COMM_FAIL, /** 不匹配 **/ OPS_COMM_ERROR, /** 通用操作错误 **/ OPS_COMM_STOP_BY_USER, /** 用户取消 **/ OPS_COMM_TIMEOUT, /** 操作超时 **/ OPS_COMM_OUT_OF_MEM, /** 无法分配内存 **/ OPS_COMM_MAX, OPS_OPEN_SUCCESS = OPS_TYPE_OPEN * 100, /** 打开设备完成 **/ OPS_OPEN_FAIL, /** 打开设备失败 **/ OPS_OPEN_ERROR, /** 打开设备遇到错误 **/ OPS_OPEN_MAX, OPS_ENROLL_SUCCESS = OPS_TYPE_ENROLL * 100, /** 录入信息成功 **/ OPS_ENROLL_FAIL, /** 录入失败 **/ OPS_ENROLL_ERROR, /** 录入过程中遇到错误 **/ OPS_ENROLL_STOP_BY_USER, /** 录入被用户中断 **/ OPS_ENROLL_TIMEOUT, /** 操作超时 **/ OPS_ENROLL_MAX, OPS_VERIFY_MATCH = OPS_TYPE_VERIFY * 100, /** 认证匹配 **/ OPS_VERIFY_NO_MATCH, /** 认证不匹配 **/ OPS_VERIFY_ERROR, /** 认证过程中遇到错误 **/ OPS_VERIFY_STOP_BY_USER, /** 认证被用户中断 **/ OPS_VERIFY_TIMEOUT, /** 操作超时 **/ OPS_VERIFY_MAX, OPS_IDENTIFY_MATCH = OPS_TYPE_IDENTIFY * 100, /** 识别到指定特征 **/ OPS_IDENTIFY_NO_MATCH, /** 未识别出指定特征 **/ OPS_IDENTIFY_ERROR, /** 识别过程中遇到错误 **/ OPS_IDENTIFY_STOP_BY_USER, /** 识别被用户中断 **/ OPS_IDENTIFY_TIMEOUT, /** 操作超时 **/ OPS_IDENTIFY_MAX, OPS_CAPTURE_SUCCESS = OPS_TYPE_CAPTURE * 100, /** 捕获成功 **/ OPS_CAPTURE_FAIL, /** 捕获失败 **/ OPS_CAPTURE_ERROR, /** 捕获过程中遇到错误 **/ OPS_CAPTURE_STOP_BY_USER, /** 捕获被用户中断 **/ OPS_CAPTURE_TIMEOUT, /** 操作超时 **/ OPS_CAPTURE_MAX, OPS_SEARCH_MATCH = OPS_TYPE_SEARCH * 100, /** 搜索到指定特征 **/ OPS_SEARCH_NO_MATCH, /** 未搜索到指定特征 **/ OPS_SEARCH_ERROR, /** 搜索过程中遇到错误 **/ OPS_SEARCH_STOP_BY_USER, /** 搜索被用户中断 **/ OPS_SEARCH_TIMEOUT, /** 操作超时 **/ OPS_SEARCH_MAX, OPS_CLEAN_SUCCESS = OPS_TYPE_CLEAN * 100, /** 清理特征成功 **/ OPS_CLEAN_FAIL, /** 清理失败 **/ OPS_CLEAN_ERROR, /** 清理过程中遇到错误 **/ OPS_CLEAN_STOP_BY_USER, /** 清理被用户中断 **/ OPS_CLEAN_TIMEOUT, /** 操作超时 **/ OPS_CLEAN_MAX, OPS_GET_FLIST_SUCCESS = OPS_TYPE_GET_FLIST * 100, /** 获取特征列表完成 **/ OPS_GET_FLIST_FAIL, /** 获取特征列表失败 **/ OPS_GET_FLIST_ERROR, /** 获取特征列表过程中遇到错误 **/ OPS_GET_FLIST_STOP_BY_USER, /** 获取特征列表被用户中断 **/ OPS_GET_FLIST_TIMEOUT, /** 获取特征列表超时 **/ OPS_GET_FLIST_MAX, OPS_RENAME_SUCCESS = OPS_TYPE_RENAME * 100, /** 重命名特征完成 **/ OPS_RENAME_FAIL, /** 重命名特征失败 **/ OPS_RENAME_ERROR, /** 重命名特征过程中遇到错误 **/ OPS_RENAME_STOP_BY_USER, /** 重命名特征被用户中断 **/ OPS_RENAME_TIMEOUT, /** 重命名特征超时 **/ OPS_RENAME_MAX, OPS_CLOSE_SUCCESS = OPS_TYPE_CLOSE * 100, /** 关闭设备完成 **/ OPS_CLOSE_FAIL, /** 关闭设备失败 **/ OPS_CLOSE_ERROR, /** 关闭设备过程中遇到错误 **/ OPS_CLOSE_MAX, } OpsResult; /** * @brief UpdateStauts调用返回的结果 */ struct StatusReslut { int result; int enable; int devNum; int devStatus; int opsStatus; int notifyMessageId; }; /** * @brief USB设备插拔动作 */ enum USBDeviceAction { ACTION_ATTACHED = 1, ACTION_DETACHED = -1 }; /** * @brief 特征的信息 */ struct FeatureInfo { int uid; int biotype; QString device_shortname; int index; QString index_name; }; /** * @brief 设备的信息 */ struct DeviceInfo { int id = -1; QString shortName; QString fullName; int driverEnable; int deviceNum; int deviceType; int storageType; int eigType; int verifyType; int identifyType; int busType; int deviceStatus; int OpsStatus; }; QDBusArgument &operator<<(QDBusArgument &arg, const DeviceInfo &deviceInfo); const QDBusArgument &operator>>(const QDBusArgument &arg, DeviceInfo &deviceInfo); QDBusArgument &operator<<(QDBusArgument &argument, const FeatureInfo &featureInfo); const QDBusArgument &operator>>(const QDBusArgument &argument, FeatureInfo &featureInfo); typedef std::shared_ptr DeviceInfoPtr; typedef QList DeviceList; typedef QMap DeviceMap; typedef std::shared_ptr FeatureInfoPtr; typedef QList FeatureList; typedef QMap FeatureMap; QDebug operator<<(QDebug stream, const FeatureInfo &featureInfo); QDebug operator<<(QDebug stream, const DeviceInfo &deviceInfo); Q_DECLARE_METATYPE(DeviceInfo) Q_DECLARE_METATYPE(FeatureInfo) QString getDeviceTypeTr(int deviceType); typedef enum { BioT_FingerPrint, /** 指纹 **/ BioT_FingerVein, /** 指静脉 **/ BioT_Iris, /** 虹膜 **/ BioT_Face, /** 人脸 **/ BioT_VoicePrint, /** 声纹 **/ UniT_KCM, /** 安全管控 **/ UniT_General_Ukey, /** 普通的Ukey **/ UniT_Advanced_Ukey, /** 高阶的Ukey **/ UniT_Remote, /** 远程账户 **/ UniT_Custom = 0xFFFF /** 第三方 **/ } BioType; enum BIOAUTH_STATE { BIOAUTH_IDLE = 0, BIOAUTH_START, BIOAUTH_TIMEOUT, BIOAUTH_DEVICEREMOVED, BIOAUTH_COMPLETE }; #endif // BIODEFINES_H ukui-screensaver/src/common/capslockstatehelper.h0000664000175000017500000000073215172041106021262 0ustar fengfeng#ifndef CAPSLOCKSTATEHELPER_H #define CAPSLOCKSTATEHELPER_H #include #include class CapslockStateHelper : public QObject { Q_OBJECT public: static CapslockStateHelper *instance(QObject *parent = nullptr); explicit CapslockStateHelper(QObject *parent = nullptr); Q_SIGNALS: void capslockChanged(bool state); private: UkuiKeyState* m_keyState; static CapslockStateHelper *m_instance; }; #endif // CAPSLOCKSTATEHELPER_H ukui-screensaver/src/common/utils.cpp0000664000175000017500000001442615172041106016722 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "utils.h" #include "definetypes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRM_DIR "/sys/class/leds/" static QString readFileSafely(const QString& filePath) { QString strContent = ""; if (!QFile::exists(filePath)) { qWarning() << "file not exists:" << filePath; return strContent; } QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "file open failed " << filePath << file.error(); return strContent; } QTextStream testStream(&file); return testStream.readAll(); } void checkIslivecd() { QString strResult = readFileSafely("/proc/cmdline"); if (strResult.contains("boot=casper")) { printf("is livecd\n"); exit(0); } QString filepath = QDir::homePath() + "/Desktop" + "/kylin-os-installer.desktop"; QFileInfo file(filepath); if (!file.exists()) return; if (getuid() != 999) return; exit(0); } void KillFocusOfKydroid() { if (!isGreeterMode()) { QString username = getenv("USER"); int uid = getuid(); QDBusInterface *interface = new QDBusInterface( "cn.kylinos.Kydroid2", "/cn/kylinos/Kydroid2", "cn.kylinos.Kydroid2", QDBusConnection::systemBus()); if (interface) { QDBusPendingCall pendingCall = interface->asyncCall(QStringLiteral("SetPropOfContainer"), username, uid, "is_kydroid_on_focus", "0"); Q_UNUSED(pendingCall); interface->deleteLater(); interface = nullptr; } } } /** * @brief 判断大写键状态 * @return true: 大写锁定 */ bool checkCapsLockState() { QDir ledDir(DRM_DIR); QStringList leds = ledDir.entryList(QDir::Dirs); QString capsFile; for (int i = 0; i < leds.count(); i++) { if (leds.at(i).contains("capslock")) capsFile = leds.at(i); } QFile drmStatusFile(DRM_DIR + capsFile + "/brightness"); if (drmStatusFile.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&drmStatusFile); QString status = in.readLine(); if (status == "0") { return false; } else { return true; } } if (QString(qgetenv("XDG_SESSION_TYPE")) == "wayland") { return false; } bool capsState = false; unsigned int n; XkbGetIndicatorState((Display*)getX11Display(), XkbUseCoreKbd, &n); capsState = (n & 0x01) == 1; return capsState; } bool ispicture(QString filepath) { QFileInfo file(filepath); if (file.exists() == false) return false; QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(filepath); return mime.name().startsWith("image/"); } double getDefaultFontSize() { GSettingsSchemaSource *schema_source = NULL; GSettingsSchema *schema = NULL; double defaultFontSize = DEFAULT_FONT_SIZE; schema_source = g_settings_schema_source_get_default(); if (schema_source) { schema = g_settings_schema_source_lookup(schema_source, GSETTINGS_SCHEMA_STYLE, TRUE); if (schema) { GSettings *gs = g_settings_new(GSETTINGS_SCHEMA_STYLE); if (gs) { GVariant *size = g_settings_get_default_value(gs, GLIB_KEY_SYSTEM_FONT_SIZE); QString fontsize(g_variant_get_string(size, NULL)); g_variant_unref(size); g_object_unref(gs); defaultFontSize = fontsize.toDouble(); } g_settings_schema_unref(schema); } } return defaultFontSize; } double getUserFontSize(QString userName) { getDefaultFontSize(); QString configPath; configPath = QString("/var/lib/lightdm-data/%1/ukui-greeter.conf").arg(userName); QFile configFile(configPath); double fontSize; if (configFile.exists()) { QSettings settings(configPath, QSettings::IniFormat); settings.beginGroup("Greeter"); fontSize = settings.value("fontSize").toDouble(); } else { fontSize = getDefaultFontSize(); } return fontSize - getDefaultFontSize(); } QString getUserThemeColor(QString userName) { QString configPath; configPath = QString("/var/lib/lightdm-data/%1/ukui-greeter.conf").arg(userName); QFile configFile(configPath); QString themeColor = "daybreakBlue"; if (configFile.exists()) { QSettings settings(configPath, QSettings::IniFormat); settings.beginGroup("Greeter"); themeColor = settings.value("themeColor").toString(); } return themeColor; } void* getX11Display() { if (QApplication::platformName() != "xcb") { qInfo() << "This is not a xcb platform"; return nullptr; } if (QNativeInterface::QX11Application *x11Application = qApp->nativeInterface()) { return x11Application->display(); } return nullptr; } void* getX11Connection() { if (QApplication::platformName() != "xcb") { qInfo() << "This is not a xcb platform"; return nullptr; } if (QNativeInterface::QX11Application *x11Application = qApp->nativeInterface()) { return x11Application->connection(); } return nullptr; } ukui-screensaver/src/common/capslockstatehelper.cpp0000664000175000017500000000131615172041106021614 0ustar fengfeng#include "capslockstatehelper.h" #include CapslockStateHelper *CapslockStateHelper::m_instance = nullptr; CapslockStateHelper::CapslockStateHelper(QObject *parent) : QObject{parent} { m_keyState = ukuiKeyState(); m_keyState->fetchStates(); m_keyState->on_state_changed() = [=](UkuiKeyState::Key key, UkuiKeyState::State state) { if (key == UkuiKeyState::CapsLock) { qDebug() << " CapsLock changed !!! " << " state = " << state; Q_EMIT capslockChanged(state); } }; } CapslockStateHelper *CapslockStateHelper::instance(QObject *parent) { if (m_instance == nullptr) m_instance = new CapslockStateHelper(parent); return m_instance; } ukui-screensaver/src/common/definetypes.h0000664000175000017500000003450215172041106017543 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef DEFINETYPES_H #define DEFINETYPES_H typedef enum _LOCK_CMD_ID_e { LOCK_CMD_ID_GET_USERINFO_LIST = 0, LOCK_CMD_ID_USERINFO_CHANGED, LOCK_CMD_ID_SET_USER, LOCK_CMD_ID_SWITCH_TO_USER, LOCK_CMD_ID_GET_DEFAULT_AUTH_USER, LOCK_CMD_ID_GET_CURRENT_USER, LOCK_CMD_ID_CURRENT_USER_CHANGED, LOCK_CMD_ID_GET_SESSIONS_LIST, LOCK_CMD_ID_SESSIONS_CHANGED, LOCK_CMD_ID_SET_SESSION, LOCK_CMD_ID_GET_CURRENT_SESSION, LOCK_CMD_ID_CURRENT_SESSION_CHANGED, LOCK_CMD_ID_START_SESSION, LOCK_CMD_ID_GET_INIT_DATA, LOCK_CMD_ID_LOGIN1_REQ_LOCK = 100, LOCK_CMD_ID_LOGIN1_REQ_UNLOCK, LOCK_CMD_ID_LOGIN1_PREPAREFORSLEEP, LOCK_CMD_ID_LOGIN1_SESSION_ACTIVE_CHANGED, LOCK_CMD_ID_LOGIN1_IS_SESSION_ACTIVE, LOCK_CMD_ID_LOGIN1_BLOCKINHIBITED_CHANGED, LOCK_CMD_ID_LOGIN1_SET_POWER_MANAGER, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSWITCHUSER, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANHIBERNATE, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANPOWEROFF, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANREBOOT, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSUSPEND, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOGOUT, LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOCKSCREEN, LOCK_CMD_ID_SYSTEM_UPGRADE_CHECK, LOCK_CMD_ID_LOGIN1_BLOCKLOGOUTINHIBITED_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_LOCKSCREEN_CONF = 200, LOCK_CMD_ID_GSETTINGS_SET_LOCKSCREEN_CONF, LOCK_CMD_ID_GSETTINGS_LOCKSCREEN_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_SCREENSAVER_CONF, LOCK_CMD_ID_GSETTINGS_SET_SCREENSAVER_CONF, LOCK_CMD_ID_GSETTINGS_SCREENSAVER_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_POWERMANAGER_CONF, LOCK_CMD_ID_GSETTINGS_SET_POWERMANAGER_CONF, LOCK_CMD_ID_GSETTINGS_POWERMANAGER_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_MATEBG_CONF, LOCK_CMD_ID_GSETTINGS_SET_MATEBG_CONF, LOCK_CMD_ID_GSETTINGS_MATEBG_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_UKCCPLUGINS_CONF, LOCK_CMD_ID_GSETTINGS_SET_UKCCPLUGINS_CONF, LOCK_CMD_ID_GSETTINGS_UKCCPLUGINS_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_THEMESTYLE_CONF, LOCK_CMD_ID_GSETTINGS_SET_THEMESTYLE_CONF, LOCK_CMD_ID_GSETTINGS_THEMESTYLE_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_SESSION_CONF, LOCK_CMD_ID_GSETTINGS_SET_SESSION_CONF, LOCK_CMD_ID_GSETTINGS_SESSION_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_KEYBOARD_CONF, LOCK_CMD_ID_GSETTINGS_SET_KEYBOARD_CONF, LOCK_CMD_ID_GSETTINGS_KEYBOARD_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIAKEYS_CONF, LOCK_CMD_ID_GSETTINGS_SET_USD_MEDIAKEYS_CONF, LOCK_CMD_ID_GSETTINGS_USD_MEDIAKEYS_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIA_STATE_KEYS_CONF, LOCK_CMD_ID_GSETTINGS_SET_USD_MEDIA_STATE_KEYS_CONF, LOCK_CMD_ID_GSETTINGS_USD_MEDIA_STATE_KEYS_CONF_CHANGED, LOCK_CMD_ID_GSETTINGS_GET_DEFAULT_PHOTOPATH, LOCK_CMD_ID_GSETTINGS_SET_DEFAULT_PHOTOPATH, LOCK_CMD_ID_GSETTINGS_GET_CUSTOM_PHOTOPATH, LOCK_CMD_ID_GSETTINGS_SET_CUSTOM_PHOTOPATH, LOCK_CMD_ID_GET_AGREEMENT = 300, LOCK_CMD_ID_LOCK_STATE_CHANGED, LOCK_CMD_ID_GET_PUBLIC_KEY, LOCK_CMD_ID_SEND_USER_PASSWORD, LOCK_CMD_ID_LIDSTATE_CHANGED, LOCK_CMD_ID_PAMAUTH_IS_INAUTHTICATION = 400, LOCK_CMD_ID_PAMAUTH_IS_AUTHENTICATED, LOCK_CMD_ID_PAMAUTH_GET_AUTHUSER, LOCK_CMD_ID_PAMAUTH_AUTHENTICATE, LOCK_CMD_ID_PAMAUTH_RESPOND, LOCK_CMD_ID_PAMAUTH_AUTHENTICATE_CANCEL, LOCK_CMD_ID_PAMAUTH_SHOWMESSAGE, LOCK_CMD_ID_PAMAUTH_SHOWPROMPT, LOCK_CMD_ID_PAMAUTH_AUTH_COMPLETED, LOCK_CMD_ID_PAMAUTH_SHOWPBINARYROMPT, LOCK_CMD_ID_PAMAUTH_GET_AUTOLOGINUSERHINT, LOCK_CMD_ID_USD_MEDIAKEYS = 500, LOCK_CMD_ID_UPOWER_BATTERY_STATUS, LOCK_CMD_ID_UPOWER_IS_BATTERY, LOCK_CMD_ID_UPOWER_BATTERY, LOCK_CMD_ID_SESSION_GET_SLEEP_LOCKCHECK, LOCK_CMD_ID_SESSION_GET_SHUTDOWN_LOCKCHECK, LOCK_CMD_ID_LOCK_SCREEN_GET_THEMES, LOCK_CMD_ID_KWIN_BLOCK_SHORTCUT, LOCK_CMD_ID_UPOWER_SERVICE_READY, LOCK_CMD_ID_SESSION_GET_LOGOUT_LOCKCHECK, LOCK_CMD_ID_BIOAUTH_GET_AVAILABLE_DEVICES = 600, LOCK_CMD_ID_BIOAUTH_GET_DISABLED_DEVICES, LOCK_CMD_ID_BIOAUTH_GET_STATE, LOCK_CMD_ID_BIOAUTH_GET_CURDEVICE, LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_ID, LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_NAME, LOCK_CMD_ID_BIOAUTH_STARTAUTH, LOCK_CMD_ID_BIOAUTH_STOPAUTH, LOCK_CMD_ID_BIOAUTH_GET_DEFAULT_DEVICE, LOCK_CMD_ID_BIOAUTH_SERVICE_STATUS_CHANGED, LOCK_CMD_ID_BIOAUTH_DEVICE_CHANGED, LOCK_CMD_ID_BIOAUTH_SHOW_MESSAGE, LOCK_CMD_ID_BIOAUTH_AUTHSTATE_CHANGED, LOCK_CMD_ID_BIOAUTH_FRAME_DATA, LOCK_CMD_ID_BIOAUTH_COMPLETE, LOCK_CMD_ID_TABLET_MODE_CHANGED = 700, LOCK_CMD_ID_TABLET_MODE, LOCK_CMD_ID_NETWORK_CONNECT_STATE_CHANGED = 800, LOCK_CMD_ID_NETWORK_CONNECT_STATE, LOCK_CMD_ID_GET_WEATHER_DATA, LOCK_CMD_ID_GET_SAVER_DATETIME_ENABLE_DATA, LOCK_CMD_ID_SET_SAVER_DATETIME_ENABLE_DATA, LOCK_CMD_ID_SET_SAVER_DATETIME_ENABLE_DATA_CHANGED, LOCK_CMD_ID_GET_LOCK_DATETIME_ENABLE_DATA, LOCK_CMD_ID_SET_LOCK_DATETIME_ENABLE_DATA, LOCK_CMD_ID_SET_LOCK_DATETIME_ENABLE_DATA_CHANGED, LOCK_CMD_ID_COUNT, LOCK_CMD_ID_CAN_SWITCH_USER } LOCK_CMD_ID; #define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" #define KEY_IDLE_DELAY "idleDelay" #define KEY_IDLE_LOCK "idleLock" #define KEY_IDLE_ACTIVATION_ENABLED "idleActivationEnabled" #define KEY_IDLE_LOCK_ENABLED "idleLockEnabled" #define KEY_LOCK_TIMEOUT "lockTimeout" #define KEY_CLOSE_ACTIVATION_ENABLED "closeActivationEnabled" #define KEY_SLEEP_ACTIVATION_ENABLED "sleepActivationEnabled" #define KEY_LOCK_ENABLED "lockEnabled" #define KEY_BACKGROUND "background" #define KEY_MODE "mode" #define KEY_THEMES "themes" #define KEY_IMAGE_TRANSITION_EFFECT "imageTransitionEffect" #define KEY_IMAGE_SWITCH_INTERVAL "imageSwitchInterval" #define GSETTINGS_SCHEMA_SCREENSAVER_DEFAULT "org.ukui.screensaver-default" #define KEY_SHOW_REST_TIME "showRestTime" // old #define KEY_SHOW_CUSTOM_REST_TIME "showCustomRestTime" #define KEY_SHOW_UKUI_REST_TIME "showUkuiRestTime" #define KEY_CYCLE_TIME "cycleTime" #define KEY_AUTOMATIC_SWITCHING_ENABLE "automaticSwitchingEnabled" #define KEY_BACKGROUND_PATH "backgroundPath" #define KEY_MYTEXT "mytext" #define KEY_TEXT_IS_CENTER "textIsCenter" #define KEY_SHOW_MESSAGE_ENABLED "showMessageEnabled" #define KEY_MESSAGE_NUMBER "messageNumber" #define KEY_VIDEO_FORMAT "videoFormat" #define KEY_VIDEO_HEIGHT "videoHeight" #define KEY_VIDEO_WIDTH "videoWidth" #define KEY_VIDEO_PATH "videoPath" #define KEY_VIDEO_SIZE "videoSize" #define KEY_SUB_MODE "subMode" #define KEY_MUSIC_FOLLOW_SYSTEM "musicFollowSystem" #define KEY_WEATHER_POSITION "weatherPosition" #define KEY_MUSIC_PATH "musicPath" #define KEY_PHOTO_CYCLE_MODE "photoCycleMode" #define KEY_PHOTO_CYCLE_INTERVAL "photoCycleInterval" #define KEY_PHOTO_DEFAULT_PATH "photoDefaultPath" #define KEY_PHOTO_CUSTOM_PATH "photoCustomPath" #define KEY_PHOTO_SELECT_PATH "photoSelectPath" #define KEY_URL_ADDRESSS "urlAddress" #define DEFAULT_PHOTO_PATH "/usr/share/backgrounds" #define LOCAL_WALLPAPER_PATH QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)\ %QStringLiteral("/ukui-control-center/wallpaperData/") #define KEY_SAVER_DATETIME_ENADLE "saverDatetimeEnable" #define GSETTINGS_SCHEMA_POWER_MANAGER "org.ukui.power-manager" #define KEY_LOCK_SUSPEND "lockSuspend" #define KEY_LOCK_HIBERNATE "lockHibernate" #define KEY_LOCK_BLANKSCREEN "lockBlankScreen" #define KEY_SLEEP_COMPUTER_AC "sleepComputerAc" #define KEY_SLEEP_DISPLAY_AC "sleepDisplayAc" #define KEY_BUTTON_LID_AC "buttonLidAc" #define KEY_BUTTON_POWER "buttonPower" #define GSETTINGS_SCHEMA_MATE_BG "org.mate.background" #define KEY_PICTURE_FILENAME "pictureFilename" #define KEY_PICTURE_OPTIONS "pictureOptions" #define KEY_PRIMARY_COLOR "primaryColor" #define GSETTINGS_SCHEMA_TIME_TYPE "org.ukui.control-center.panel.plugins" #define KEY_HOUR_SYSTEM "hoursystem" #define KEY_DATE "date" #define GSETTINGS_SCHEMA_STYLE "org.ukui.style" #define KEY_SYSTEM_FONT_SIZE "systemFontSize" #define GLIB_KEY_SYSTEM_FONT_SIZE "system-font-size" #define KEY_THEME_COLOR "themeColor" #define KEY_SYSTEM_FONT "systemFont" #define KEY_MENU_TRANSPARENCY "menuTransparency" #define KEY_STYLE_NAME "styleName" #define GSETTINGS_SCHEMA_SESSION "org.ukui.session" #define KEY_SESSION_IDLE "idleDelay" #define KEY_SESSION_LOGOUT_MUSIC "logoutMusic" #define KEY_SESSION_POWEROFF_MUSIC "poweroffMusic" #define GSETTINGS_SCHEMA_PERIPHERALS_KEYBOARD "org.ukui.peripherals-keyboard" #define KEY_CAPSLOCK_STATUS "capslockState" #define GSETTINGS_SCHEMA_MEDIAKEY_SCHEMA "org.ukui.SettingsDaemon.plugins.media-keys" #define KEY_AREA_SCREENSHOT "areaScreenshot" #define KEY_AREA_SCREENSHOT2 "areaScreenshot2" #define KEY_SCREEN_SHOT "screenshot" #define KEY_SCREEN_SHOT2 "screenshot2" #define KEY_WINDOW_SCREENSHOT "windowScreenshot" #define GSETTINGS_SCHEMA_KYLIN_WEATHER_SCHEMA "org.kylin-weather.settings" #define KEY_WEATHER "weather" #define GSETTINGS_SCHEMA_MEDIAKEY_STATE_SCHEMA "org.ukui.SettingsDaemon.plugins.media-keys-state" #define KEY_RFKILL_STATE "rfkillState" #define WORKING_DIRECTORY "/usr/share/ukui-screensaver" #define SUBWND_COUNT_MAX 1024 #define SS_DBUS_SERVICE "org.ukui.ScreenSaver" #define SS_DBUS_PATH "/" #define SS_DBUS_INTERFACE "org.ukui.ScreenSaver" #define SL_SESSION_DBUS_SERVICE "org.ukui.ScreenSaver.session.Screenlock" #define SL_SESSION_DBUS_PATH "/Screenlock" #define SS_SESSION_DBUS_SERVICE "org.ukui.ScreenSaver.session.Screensaver" #define SS_SESSION_DBUS_PATH "/Screensaver" enum SessionStatus { SESSION_AVAILABLE = 0, SESSION_INVISIBLE = 1, SESSION_BUSY = 2, SESSION_IDLE = 3 }; #define SM_DBUS_SERVICE "org.gnome.SessionManager" #define SM_DBUS_PATH "/org/gnome/SessionManager/Presence" #define SM_DBUS_INTERFACE "org.gnome.SessionManager.Presence" #define DM_DBUS_SERVICE "org.freedesktop.DisplayManager" #define DM_DBUS_PATH "/org/freedesktop/DisplayManager" #define DM_DBUS_INTERFACE "org.freedesktop.DisplayManager" #define DM_SEAT_INTERFACE "org.freedesktop.DisplayManager.Seat" #define DM_SESSION_INTERFACE "org.freedesktop.DisplayManager.Session" #define SSWND_DBUS_SERVICE "org.ukui.ScreenSaverWnd" #define SSWND_DBUS_PATH "/" #define SSWND_DBUS_INTERFACE "org.ukui.ScreenSaverWnd" #define DEFAULT_BACKGROUND_PATH "/usr/share/backgrounds/1-openkylin.jpg" #define G_FONT_SIZE (11.0) #define DEFAULT_FONT_SIZE (10.0) #define XSCREENSAVER_DIRNAME "/usr/lib/xscreensaver" enum LOGINOPT_TYPE { LOGINOPT_TYPE_PASSWORD = 0, // 密码 LOGINOPT_TYPE_FACE, // 人脸 LOGINOPT_TYPE_FINGERPRINT, // 指纹 LOGINOPT_TYPE_IRIS, // 虹膜 LOGINOPT_TYPE_VOICEPRINT, // 声纹 LOGINOPT_TYPE_FINGERVEIN, // 指静脉 LOGINOPT_TYPE_GENERAL_UKEY, // 普通的ukey LOGINOPT_TYPE_ADVANCED_UKEY, // 高阶的ukey LOGINOPT_TYPE_QRCODE, // 二维码 LOGINOPT_TYPE_CUSTOM, // 第三方 LOGINOPT_TYPE_OTHERS, // 其他 LOGINOPT_TYPE_COUNT }; #define BIOMETRIC_PAM_DOUBLE "BIOMETRIC_PAM_DOUBLE" #define BIOMETRIC_PAM "BIOMETRIC_PAM" #define BIOMETRIC_PAM_QRCODE "BIOMETRIC_PAM_QRCODE" #define BIOMETRIC_IGNORE "BIOMETRIC_IGNORE" #define BIOMETRIC_SUCCESS "BIOMETRIC_SUCCESS" namespace PamAuth { enum PromptType { PromptTypeQuestion, PromptTypeSecret }; enum MessageType { MessageTypeInfo, MessageTypeError }; } // namespace PamAuth // usd功能枚举(dbus接口参数) typedef enum { TOUCHPAD_KEY, MUTE_KEY, VOLUME_DOWN_KEY, VOLUME_UP_KEY, MIC_MUTE_KEY, BRIGHT_UP_KEY, BRIGHT_DOWN_KEY, POWER_DOWN_KEY, POWER_OFF_KEY, EJECT_KEY, HOME_KEY, MEDIA_KEY, CALCULATOR_KEY, EMAIL_KEY, SCREENSAVER_KEY, HELP_KEY, WWW_KEY, PLAY_KEY, PAUSE_KEY, STOP_KEY, PREVIOUS_KEY, NEXT_KEY, REWIND_KEY, FORWARD_KEY, REPEAT_KEY, CURSOR_PROMPT_KEY, RANDOM_KEY, SETTINGS_KEY, FILE_MANAGER_KEY, SHUTDOWN_MANAGEMENT_KEY, TERMINAL_KEY, SCREENSHOT_KEY, WINDOW_SCREENSHOT_KEY, AREA_SCREENSHOT_KEY, WINDOWSWITCH_KEY, SYSTEM_MONITOR_KEY, CONNECTION_EDITOR_KEY, GLOBAL_SEARCH_KEY, KDS_KEY, WLAN_KEY, WEBCAM_KEY, HANDLED_KEYS, UKUI_SIDEBAR, UKUI_EYECARE_CENTER, TOUCHPAD_ON_KEY, TOUCHPAD_OFF_KEY, RFKILL_KEY, BLUETOOTH_KEY, ASRASSISTANT, PERFORMANCE_KEY, } ActionType; #define UNIAUTH_DBUS_SERVICE "org.ukui.UniauthBackend" #define UNIAUTH_DBUS_PATH "/org/ukui/UniauthBackend" #define UNIAUTH_DBUS_INTERFACE "org.ukui.UniauthBackend" #define UNIAUTH_GREETERCONFIG_DBUS_PATH "/org/ukui/GreeterConfig" #define FD_DBUS_SERVICE "org.freedesktop.DBus" #define FD_DBUS_PATH "/org/freedesktop/DBus" #define FD_DBUS_INTERFACE "org.freedesktop.DBus" #define FD_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" #define ACCOUNTS_DBUS_SERVICE "org.freedesktop.Accounts" #define ACCOUNTS_DBUS_PATH "/org/freedesktop/Accounts" #define ACCOUNTS_DBUS_INTERFACE "org.freedesktop.Accounts" #define ACCOUNTS_USER_DBUS_INTERFACE "org.freedesktop.Accounts.User" #define ACCOUNTS_USER_LDM_SERVICE_INTERFACE "org.freedesktop.DisplayManager.AccountsService" enum Action { PowerSwitchUser, PowerHibernate, PowerSuspend, PowerMonitorOff, PowerLogout, PowerUpgradeThenRboot, PowerReboot, PowerUpgradeThenShutdown, PowerShutdown, TestAction, }; enum BinaryPromptType { BinaryPromptAuthType, //认证类型 BinaryPromptNumber, //数字类型消息,暂不使用 BinaryPromptMessage, //消息类型 BinaryPromptImage, //图片类型,暂不使用 BinaryPromptQrcodeStr, //二维码字符串 BinaryPromptPromptMessage, //绑定密码框中的消息 BinaryPromptErrorMessage, //绑定密码框下方的错误消息 BinaryPromptEmergMsg //OTP紧急口令 }; #endif // DEFINETYPES_H ukui-screensaver/src/common/kyrsac.h0000664000175000017500000000300015172041035016506 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef _KYRSAC_H #define _KYRSAC_H #include /** * @brief The RSAC class * RSA算法相关实现,包括密钥对生成,加密与解密,签名与验签。 */ class KyRSAC { public: // 对摘要进行签名和验签 bool signData(const QByteArray &digest, QByteArray &sign, const QByteArray &priKey); bool verifyData(const QByteArray &digest, const QByteArray &sign, const QByteArray &pubKey); // 生成秘钥对 void genKeyPair(const QString &priKeyFileName, const QString &pubKeyFile, int bits = 1024); void genKeyPair(QByteArray &priKey, QByteArray &pubKey, int bits = 1024); // 对数据进行加解密 bool encryptData(const QByteArray &in, QByteArray &out, const QByteArray &pubKey); bool decryptData(const QByteArray &in, QByteArray &out, const QByteArray &priKey); }; #endif // RSAC_H ukui-screensaver/src/common/global_utils.h0000664000175000017500000000303015172041106017674 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef GLOBAL_UTILS_H #define GLOBAL_UTILS_H #include #include "definetypes.h" /** * @brief isGreeterMode 是否为欢迎界面模式,区分桌面锁屏 * @return true 是,false 否 */ bool isGreeterMode(); /** * @brief isCurUserSelf 是否为当前用户自己 * @param QString 用户名 * @return true 是,false 否 */ bool isCurUserSelf(QString strUserName); /** * @brief getHostCloudPlatform 获取当前云平台环境 * @return 云环境标识 */ QString getHostCloudPlatform(); bool isCommunity(); /** * @brief isOpenkylin 是否为ok环境 * @return true 是,否则不是 */ bool isOpenkylin(); QString getDefaultFace(); void setFcitxExport(); void startFcitx(); void runCommand(const QString &command); // 检查某进程名的进程是否在运行 bool isProcessRunning(const QString &procName); #endif // GLOBAL_UTILS_H ukui-screensaver/src/common/biodefines.cpp0000664000175000017500000000724215172041106017667 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "biodefines.h" #include #include QString getDeviceTypeTr(int deviceType) { switch(deviceType) { case BioT_FingerPrint: return QObject::tr("FingerPrint"); case BioT_FingerVein: return QObject::tr("FingerVein"); case BioT_Iris: return QObject::tr("Iris"); case BioT_Face: return QObject::tr("Face"); case BioT_VoicePrint: return QObject::tr("VoicePrint"); case UniT_General_Ukey: return QObject::tr("Ukey"); case UniT_Remote: return QObject::tr("QRCode"); default: return ""; } } QDebug operator<<(QDebug stream, const DeviceInfo &deviceInfo) { stream << "[" << deviceInfo.id << deviceInfo.shortName << deviceInfo.fullName << deviceInfo.deviceType << deviceInfo.driverEnable << deviceInfo.deviceNum << "]"; return stream; } QDBusArgument &operator<<(QDBusArgument &arg, const DeviceInfo &deviceInfo) { arg.beginStructure(); arg << deviceInfo.id << deviceInfo.shortName << deviceInfo.fullName << deviceInfo.driverEnable << deviceInfo.deviceNum << deviceInfo.deviceType << deviceInfo.storageType << deviceInfo.eigType << deviceInfo.verifyType << deviceInfo.identifyType << deviceInfo.busType << deviceInfo.deviceStatus << deviceInfo.OpsStatus; arg.endStructure(); return arg; } const QDBusArgument &operator >>(const QDBusArgument &arg, DeviceInfo &deviceInfo) { arg.beginStructure(); arg >> deviceInfo.id >> deviceInfo.shortName >> deviceInfo.fullName >> deviceInfo.driverEnable >> deviceInfo.deviceNum >> deviceInfo.deviceType >> deviceInfo.storageType >> deviceInfo.eigType >> deviceInfo.verifyType >> deviceInfo.identifyType >> deviceInfo.busType >> deviceInfo.deviceStatus >> deviceInfo.OpsStatus; arg.endStructure(); return arg; } QDebug operator <<(QDebug stream, const FeatureInfo &featureInfo) { stream << "[" << featureInfo.uid << featureInfo.biotype << featureInfo.device_shortname << featureInfo.index << featureInfo.index_name << "]"; return stream; } /* For the type FeatureInfo */ QDBusArgument &operator<<(QDBusArgument &argument, const FeatureInfo &featureInfo) { argument.beginStructure(); argument << featureInfo.uid << featureInfo.biotype << featureInfo.device_shortname << featureInfo.index << featureInfo.index_name; argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, FeatureInfo &featureInfo) { argument.beginStructure(); argument >> featureInfo.uid >> featureInfo.biotype >> featureInfo.device_shortname >> featureInfo.index >> featureInfo.index_name; argument.endStructure(); return argument; } ukui-screensaver/src/common/displayservice.cpp0000664000175000017500000001777115172041106020616 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "displayservice.h" #include #include #include #include DisplayService *DisplayService::m_instance = nullptr; DisplayService::DisplayService(QObject *parent) : QObject(parent) { // 使用usd设置显示参数 m_strSaveParamPath = QStandardPaths::findExecutable("save-param"); } DisplayService *DisplayService::instance(QObject *parent) { if (m_instance == nullptr) m_instance = new DisplayService(parent); return m_instance; } bool DisplayService::switchDisplayMode(DisplayMode targetMode) { if (m_strSaveParamPath.isEmpty() || m_strUserName.isEmpty()) { //获取显示器列表 getMonitors(); //如果当前只有一个显示器就什么也不做 if (monitors.keys().size() < 2) { qDebug() << "only one monitor, doNothing"; return false; } QProcess subProcess; //模式切换 try { switch (targetMode) { case DISPLAY_MODE_ORI: { QString XRANDR_ORIONLY = "xrandr --output " + monitorNames[0] + " --auto"; for (int i = 1; i < monitorNames.size(); i++) XRANDR_ORIONLY = XRANDR_ORIONLY + " --output " + monitorNames[i] + " --off"; qDebug() << "XRANDR_ORIONLY: " << XRANDR_ORIONLY; subProcess.start(XRANDR_ORIONLY, QStringList(), QIODevice::NotOpen); break; } case DISPLAY_MODE_CLONE: { //查找最佳克隆分辨率 QString BEST_CLONE_MODE; bool find = false; for (auto &mode_0 : monitors[monitorNames[0]]) { for (auto &mode_1 : monitors[monitorNames[1]]) { if (mode_0 == mode_1) { BEST_CLONE_MODE = mode_0; find = true; break; } } if (find) break; } QString XRANDR_CLONE = "xrandr --output " + monitorNames[0] + " --mode " + BEST_CLONE_MODE; for (int i = 1; i < monitorNames.size(); i++) XRANDR_CLONE = XRANDR_CLONE + " --output " + monitorNames[i] + " --mode " + BEST_CLONE_MODE + " --same-as " + monitorNames[0]; qDebug() << "XRANDR_CLONE: " << XRANDR_CLONE; subProcess.start(XRANDR_CLONE, QStringList(), QIODevice::NotOpen); break; } case DISPLAY_MODE_EXTEND: { QString XRANDR_EXTEND = "xrandr --output " + monitorNames[0] + " --auto"; for (int i = 1; i < monitorNames.size(); i++) XRANDR_EXTEND = XRANDR_EXTEND + " --output " + monitorNames[i] + " --right-of " + monitorNames[i - 1] + " --auto"; qDebug() << "XRANDR_EXTEND: " << XRANDR_EXTEND; subProcess.start(XRANDR_EXTEND, QStringList(), QIODevice::NotOpen); break; } case DISPLAY_MODE_ONLY_OUT: { QString XRANDR_OUTONLY = "xrandr --output " + monitorNames[0] + " --off --output " + monitorNames[1] + " --auto"; qDebug() << "XRANDR_OUTONLY: " << XRANDR_OUTONLY; subProcess.start(XRANDR_OUTONLY, QStringList(), QIODevice::NotOpen); break; } } subProcess.waitForFinished(); return true; } catch (const QException &e) { qWarning() << e.what(); return false; } } else { QProcess subProcess; try { QString usdSaveParam = m_strSaveParamPath + " -u " + m_strUserName; qDebug() << "usdSaveParam: " << usdSaveParam; subProcess.start(usdSaveParam, QStringList(), QIODevice::NotOpen); subProcess.waitForFinished(); return true; } catch (const QException &e) { qWarning() << e.what(); return false; } } } void DisplayService::setOneDisplayMode() { //组装第一个设备名显示命令 QProcess subProcess; try { QString oneXrandrCmd = ""; if (m_strSaveParamPath.isEmpty() || m_strUserName.isEmpty()) { //获取显示器列表 getMonitors(); if (monitorNames.size() <= 0) { return; } oneXrandrCmd = "xrandr --output " + monitorNames[0] + " --auto"; } else { oneXrandrCmd = m_strSaveParamPath + " -u " + m_strUserName; } qDebug() << "setOneDisplayMode: " << oneXrandrCmd; subProcess.start(oneXrandrCmd); subProcess.waitForFinished(); return; } catch (const QException &e) { qWarning() << e.what(); return; } } void DisplayService::setCurUserName(QString strUserName) { m_strUserName = strUserName; if (!m_strSaveParamPath.isEmpty()) { QProcess subProcess; try { QString usdSaveParam = m_strSaveParamPath + " -u " + m_strUserName; qDebug() << "setCurUserName usdSaveParam: " << usdSaveParam; subProcess.start(usdSaveParam); subProcess.waitForFinished(); } catch (const QException &e) { qWarning() << e.what(); } } } bool DisplayService::isSaveParamInUsed() { return !(m_strSaveParamPath.isEmpty()); } void DisplayService::getMonitors() { QProcess subProcess; subProcess.setProgram(QStandardPaths::findExecutable("xrandr")); subProcess.setArguments({ "-q" }); subProcess.start(QIODevice::ReadOnly); subProcess.waitForFinished(); QString outputs = subProcess.readAll(); QStringList lines = outputs.split('\n'); QString name; QVector modes; bool find = false; QString lastName; monitorNames.clear(); monitors.clear(); for (auto &line : lines) { if (line.indexOf(" connected") != -1) { //找显示器名称 name = line.left(line.indexOf(' ')); monitorNames.push_back(name); if (find) //又找到了一个显示器,将上一个显示器的信息存到map monitors[lastName] = modes; find = true; lastName = name; modes.clear(); } else { if (line.startsWith(' ')) { //获取分辨率 QString mode = line.trimmed().split(' ').at(0); modes.push_back(mode); } } } monitors[name] = modes; //将最后一个显示器的信息存储到map qDebug() << "find motinors: " << monitorNames; } int DisplayService::isJJW7200() { static int ret = -1; char *pAck = NULL; char CmdAck[256] = ""; FILE *pPipe = NULL; if (ret != -1) { return ret; } pPipe = popen("lspci | grep -i VGA |grep 7200", "r"); if (pPipe) { pAck = fgets(CmdAck, sizeof(CmdAck) - 1, pPipe); if (strnlen(CmdAck, 256) > 3) { ret = 1; } else { ret = 0; } pclose(pPipe); } else { ret = 0; } return ret; } ukui-screensaver/src/common/commonfunc.h0000664000175000017500000000337515172041106017374 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef COMMONFUNC_H #define COMMONFUNC_H #include #include // 设置一下鼠标的居于主屏中央 void setCursorCenter(); QPixmap scaledPixmap(QPixmap src); QPixmap PixmapToRound(const QPixmap &src, int radius); QPixmap PixmapToRound(const QPixmap &src, int leftTop, int rightTop, int leftBottom, int rightBottom); const QPixmap loadSvg(const QString &path, const QString color, int size); QPixmap drawSymbolicColoredPixmap(const QPixmap &source, QString cgColor); QPixmap getLoadingIcon(int size); QPixmap loadFromFile(QString strPath); QPixmap blurPixmap(QPixmap pixmap); QPixmap scaleBlurPixmap(int width, int height, QString url); QPixmap scaledSmoothPixmap(const QPixmap &src, int nWidth, int nHeight); QStringList getAudioFiles(const QString &directoryPath); QString getFirstImageFileIterator(const QString &dirPath); QString mapToThumbnailPath(const QString& originalPath); QPixmap filledSymbolicColoredPix(const QPixmap &source, const QColor &baseColor, const QColor &symbolicColor = QColor(26, 26, 26), int colorDifference = 15); #endif // COMMONFUNC_H ukui-screensaver/src/common/configuration.cpp0000664000175000017500000001272415172041106020430 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "configuration.h" #include #include #include #include #include #include "definetypes.h" #define CONFIG_FILE "/etc/lightdm/ukui-greeter.conf" #define PROC_CPUINFO "/proc/cpuinfo" Configuration *Configuration::m_instance = nullptr; Configuration::Configuration(QObject *parent) : QObject(parent) { QString recodfile = QDir::homePath() + "/.cache/ukui-greeter.conf"; qDebug() << recodfile; configSettings = new QSettings(CONFIG_FILE, QSettings::IniFormat, this); recordSettings = new QSettings(recodfile, QSettings::IniFormat, this); userSetting = new QSettings(configPath, QSettings::IniFormat, this); } Configuration *Configuration::instance(QObject *parent) { if (m_instance == nullptr) m_instance = new Configuration(parent); return m_instance; } QVariant Configuration::getValue(const QString &key) { configSettings->beginGroup("Greeter"); QVariant value = configSettings->value(key); configSettings->endGroup(); return value; } void Configuration::getCurrentUser(const QString userName) { configPath = QString("/var/lib/lightdm-data/%1/ukui-greeter.conf").arg(userName); userSetting = new QSettings(configPath, QSettings::IniFormat, this); } QVariant Configuration::getUserConfig(const QString &key) { userSetting->beginGroup("Greeter"); QVariant value = userSetting->value(key); userSetting->endGroup(); qDebug() << " value = " << value; return value; } void Configuration::setValue(const QString &key, const QVariant &value) { configSettings->beginGroup("Greeter"); configSettings->setValue(key, value); configSettings->endGroup(); } bool Configuration::hasValue(const QString &key) { configSettings->beginGroup("Greeter"); bool value = configSettings->contains(key); configSettings->endGroup(); return value; } bool Configuration::getLastNumLock() { recordSettings->beginGroup("Greeter"); if (recordSettings->contains("numlock") == false) { recordSettings->setValue("numlock", true); recordSettings->sync(); recordSettings->endGroup(); return true; } bool lastNumLock = recordSettings->value("numlock").toBool(); recordSettings->endGroup(); return lastNumLock; } void Configuration::saveLastNumLock(bool value) { recordSettings->beginGroup("Greeter"); recordSettings->setValue("numlock", value); recordSettings->endGroup(); recordSettings->sync(); } bool Configuration::getIs990() { if (hasCheck990) { return is990; } hasCheck990 = true; QRegularExpression r1("kirin.*9.0", QRegularExpression::CaseInsensitiveOption); QRegularExpression r2("pangu.*m900", QRegularExpression::CaseInsensitiveOption); QFile file(PROC_CPUINFO); if (!file.exists()) { is990 = false; return is990; } file.open(QFile::ReadOnly); QString str(file.readAll()); is990 = (str.contains(r1) || str.contains(r2)); file.close(); if (is990) qDebug() << "is 990"; return is990; } void Configuration::initShareConfig() { QString userName = getenv("USER"); QString configPath; configPath = QString("/var/lib/lightdm-data/%1/ukui-greeter.conf").arg(userName); QFile fileConf(configPath); if (!fileConf.exists()) { QFile file(configPath); file.setPermissions(QFile::WriteUser | QFile::ReadUser | QFile::WriteOther | QFile::ReadOther); } m_shareSettings = new QSettings(configPath, QSettings::IniFormat, this); } void Configuration::setShareConfigValue(const QString &key, const QVariant &value) { if (!m_shareSettings) return; m_shareSettings->beginGroup("Greeter"); m_shareSettings->setValue(key, value); m_shareSettings->endGroup(); m_shareSettings->sync(); } QString Configuration::getDefaultBackgroundName() { return DEFAULT_BACKGROUND_PATH; } int Configuration::getRootBackgroundOption(QString userName) { static QMap mapPicOptions = { { "scaled", 0 }, { "stretched", 1 }, { "centered", 2 }, { "wallpaper", 3 }, { "zoom", 4 }, { "spanned", 5 } }; QString userConfigurePath = QString("/var/lib/lightdm-data/%1/ukui-greeter.conf").arg(userName); QFile backgroundFile(userConfigurePath); if (backgroundFile.exists()) { QSettings settings(userConfigurePath, QSettings::IniFormat); settings.beginGroup("greeter"); if (settings.contains("picture-options")) { QString picOptions = settings.value("picture-options").toString(); settings.endGroup(); if (!picOptions.isEmpty() && mapPicOptions.contains(picOptions)) { return mapPicOptions[picOptions]; } } else { settings.endGroup(); } } return 0; } ukui-screensaver/src/lock-command/0000775000175000017500000000000015172041035016124 5ustar fengfengukui-screensaver/src/lock-command/main.cpp0000664000175000017500000001676715172041035017575 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "utils.h" #include #include #define WORKING_DIRECTORY "/usr/share/ukui-screensaver" int main(int argc, char **argv) { initUkuiLog4qt("ukui-screensaver-command"); QCoreApplication a(argc, argv); QString locale = QLocale::system().name(); QTranslator translator; QString qmFile = QString(WORKING_DIRECTORY "/i18n_qm/%1.qm").arg(locale); translator.load(qmFile); a.installTranslator(&translator); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", "Start command for the ukui ScreenSaver.")); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption lockOption( { "l", QStringLiteral("lock") }, QCoreApplication::translate("main", "lock the screen immediately")); QCommandLineOption queryOption( { "q", QStringLiteral("query") }, QCoreApplication::translate("main", "query the status of the screen saver")); QCommandLineOption unlockOption( { "u", QStringLiteral("unlock") }, QCoreApplication::translate("main", "unlock the screen saver")); QCommandLineOption screensaverOption( { "s", QStringLiteral("screensaver") }, QCoreApplication::translate("main", "show the screensaver")); QCommandLineOption blankOption( { "b", QStringLiteral("blank") }, QCoreApplication::translate("main", "show blank and delay to lock,param:idle/lid/lowpower"), "lid"); QCommandLineOption sessiontoolsOption( { "t", QStringLiteral("session-tools") }, QCoreApplication::translate("main", "show the session tools")); QCommandLineOption switchuserOption( QStringLiteral("switchuser"), QCoreApplication::translate("main", "show the switchuser window")); QCommandLineOption appBlockOption( { "a", QStringLiteral("app-block") }, QCoreApplication::translate("main", "show the app block window"), QGuiApplication::translate("action", "which block type,param:Suspend/Hibernate/Restart/Shutdown/Logout"), "type"); QCommandLineOption multiUsersOption( { "m", QStringLiteral("multiusers-block") }, QCoreApplication::translate("main", "show the multiUsers block window"), QGuiApplication::translate("action", "which block type,param:Shutdown/Restart"), "type"); parser.addOption(lockOption); parser.addOption(queryOption); parser.addOption(unlockOption); parser.addOption(screensaverOption); parser.addOption(blankOption); parser.addOption(sessiontoolsOption); parser.addOption(switchuserOption); parser.addOption(appBlockOption); parser.addOption(multiUsersOption); parser.process(a); if (!parser.isSet(lockOption) && !parser.isSet(queryOption) && !parser.isSet(unlockOption) && !parser.isSet(screensaverOption) && !parser.isSet(blankOption) && !parser.isSet(sessiontoolsOption) && !parser.isSet(appBlockOption) && !parser.isSet(multiUsersOption) && !parser.isSet(switchuserOption)) return -1; QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); QString sessionDbus = QString("%1%2").arg(QString(SS_DBUS_SERVICE)).arg(displayNum); QDBusInterface *interface = new QDBusInterface(sessionDbus, SS_DBUS_PATH, SS_DBUS_INTERFACE); if (!interface->isValid()) { delete interface; interface = new QDBusInterface(SS_DBUS_SERVICE, SS_DBUS_PATH, SS_DBUS_INTERFACE); } QDBusReply stateReply = interface->call("GetLockState"); if (!stateReply.isValid()) { qWarning() << "Get state error:" << stateReply.error(); return 0; } qInfo() << "Command:" << parser.optionNames(); if (parser.isSet(queryOption)) { if (stateReply) qDebug() << qPrintable(QObject::tr("The screensaver is active.")); else qDebug() << qPrintable(QObject::tr("The screensaver is inactive.")); } else if (parser.isSet(lockOption) && !stateReply) { QDBusMessage msg = interface->call("Lock"); if (msg.type() == QDBusMessage::ErrorMessage) qDebug() << msg.errorMessage(); } else if (parser.isSet(unlockOption)) { QDBusMessage msg = interface->call("UnLock"); if (msg.type() == QDBusMessage::ErrorMessage) qDebug() << msg.errorMessage(); } else if (parser.isSet(screensaverOption)) { QDBusMessage msg = interface->call("ShowScreensaver"); if (msg.type() == QDBusMessage::ErrorMessage) qDebug() << msg.errorMessage(); } else if (parser.isSet(blankOption) && !stateReply) { int nLockType = 0; qDebug() << "Param:" << parser.optionNames(); QString opValue = parser.value(blankOption); if (opValue == "idle") { nLockType = 0; } else if (opValue == "lid") { nLockType = 1; } else if (opValue == "lowpower") { nLockType = 2; } else { return 0; } QDBusReply bLockSuccess = interface->call("LockByBlank", nLockType); if (bLockSuccess.isValid() && bLockSuccess.value()) { bool isLocked = false; for (int n = 0; n < 20; n++) { QDBusReply reply = interface->call("GetLockState"); if (reply.isValid()) { isLocked = reply.value(); } if (isLocked) { qDebug() << "LockState:" << n; break; } QThread::msleep(200); } } else { qDebug() << "LockByBlank:" << bLockSuccess.error().message() << bLockSuccess.value(); } } else if (parser.isSet(sessiontoolsOption) && !stateReply) { QDBusMessage msg = interface->call("SessionTools"); if (msg.type() == QDBusMessage::ErrorMessage) qDebug() << msg.errorMessage(); } else if (parser.isSet(switchuserOption) && !stateReply) { QDBusMessage msg = interface->call("SwitchUser"); if (msg.type() == QDBusMessage::ErrorMessage) qDebug() << msg.errorMessage(); } else if (parser.isSet(appBlockOption) && !stateReply) { QString opValue = parser.value(appBlockOption); QDBusMessage msg = interface->call("AppBlockWindow", opValue); if (msg.type() == QDBusMessage::ErrorMessage) qDebug() << msg.errorMessage(); } else if (parser.isSet(multiUsersOption) && !stateReply) { QString opValue = parser.value(multiUsersOption); qDebug() << opValue; QDBusMessage msg = interface->call("MultiUserBlockWindow", opValue); if (msg.type() == QDBusMessage::ErrorMessage) qDebug() << msg.errorMessage(); } return 0; } ukui-screensaver/src/userinfo.cpp0000664000175000017500000002251415172041106016121 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "userinfo.h" #include "common/global_utils.h" #include "common/definetypes.h" #include #include #include #include #include UserInfo::UserInfo(QObject *parent) : QObject(parent) , m_isLoggedIn(false) , m_uId(-1) , m_strBackground(DEFAULT_BACKGROUND_PATH) , m_strLang(qgetenv("LANG")) , m_strName("") , m_strGreeterBackground("") , m_isSystemAccount(false) { m_strHeadImage = getDefaultFace(); } UserInfo::UserInfo(const UserInfo &userInfo) : QObject(userInfo.parent()) , m_isLoggedIn(userInfo.m_isLoggedIn) , m_uId(userInfo.m_uId) , m_strHeadImage(userInfo.m_strHeadImage) , m_strFullName(userInfo.m_strFullName) , m_strBackground(userInfo.m_strBackground) , m_strLang(userInfo.m_strLang) , m_strName(userInfo.m_strName) , m_strGreeterBackground(userInfo.m_strGreeterBackground) , m_isSystemAccount(userInfo.m_isSystemAccount) { } UserInfo::~UserInfo() {} bool UserInfo::operator==(const UserInfo &userInfo) const { return userInfo.uid() == m_uId && userInfo.name() == m_strName; } void UserInfo::updateLoggedIn(const bool &isLoggedIn) { if (isLoggedIn == m_isLoggedIn) { return; } m_isLoggedIn = isLoggedIn; } void UserInfo::updateUid(const uid_t &id) { m_uId = id; } void UserInfo::updateHeadImage(const QString &path) { m_strHeadImage = path; } void UserInfo::updateFullName(const QString &fullName) { m_strFullName = fullName; } void UserInfo::updateBackground(const QString &backGround) { m_strBackground = backGround; } void UserInfo::updateLang(const QString &lang) { m_strLang = lang; } void UserInfo::updateName(const QString &name) { m_strName = name; } void UserInfo::updateGreeterBackground(const QString &background) { m_strGreeterBackground = background; } void UserInfo::updateBackgroundColor(const QString &color) { m_strBackgroundColor = color; } void UserInfo::updateSystemAccount(const bool &isSystemAccount) { if (isSystemAccount == m_isSystemAccount) { return; } m_isSystemAccount = isSystemAccount; } QDebug operator<<(QDebug stream, const UserInfo &userInfo) { stream << "[" << userInfo.uid() << userInfo.name() << userInfo.fullName() << userInfo.headImage() << userInfo.backGround() << userInfo.isLoggedIn() << userInfo.lang() << "]"; return stream; } LocalUserInfo::LocalUserInfo(const uid_t &uId, QObject *parent) : UserInfo(parent), m_strPath("/org/freedesktop/Accounts/User" + QString::number(uId)) { initData(); initConnections(); } LocalUserInfo::LocalUserInfo(const QString &strPath, QObject *parent) : UserInfo(parent), m_strPath(strPath) { initData(); initConnections(); } LocalUserInfo::LocalUserInfo(const LocalUserInfo &localUser) : UserInfo(localUser), m_strPath(localUser.path()) { if (m_strPath.isEmpty()) { m_strPath = QString("/org/freedesktop/Accounts/User" + QString::number(localUser.uid())); } initConnections(); } LocalUserInfo::~LocalUserInfo() { if (m_propertiesChangedConnected) { QDBusConnection::systemBus().disconnect( ACCOUNTS_DBUS_SERVICE, m_strPath, FD_PROPERTIES_INTERFACE, "PropertiesChanged", this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); m_propertiesChangedConnected = false; } } void LocalUserInfo::initData() { QDBusInterface ifaceExtra(ACCOUNTS_DBUS_SERVICE, m_strPath, FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusMessage ret = ifaceExtra.call("GetAll", ACCOUNTS_USER_LDM_SERVICE_INTERFACE); QList outArgs = ret.arguments(); QVariant first = outArgs.at(0); const QDBusArgument &dbusArgsExtra = first.value(); dbusArgsExtra.beginMap(); while (!dbusArgsExtra.atEnd()) { QString key; QVariant value; dbusArgsExtra.beginMapEntry(); dbusArgsExtra >> key >> value; if (key == "BackgroundFile") { QString strBackground = value.toString(); if (strBackground.isEmpty() || !QFile(strBackground).exists() || !QFile(strBackground).size()) { m_strBackground = DEFAULT_BACKGROUND_PATH; } else { m_strBackground = strBackground; } } dbusArgsExtra.endMapEntry(); } dbusArgsExtra.endMap(); QDBusInterface iface(ACCOUNTS_DBUS_SERVICE, m_strPath, FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); ret = iface.call("GetAll", ACCOUNTS_USER_DBUS_INTERFACE); outArgs = ret.arguments(); first = outArgs.at(0); const QDBusArgument &dbusArgs = first.value(); dbusArgs.beginMap(); while (!dbusArgs.atEnd()) { QString key; QVariant value; dbusArgs.beginMapEntry(); dbusArgs >> key >> value; if (key == "UserName") { m_strName = value.toString(); } else if (key == "RealName") { m_strFullName = value.toString(); } else if (key == "IconFile") { QString strHeadImage = value.toString(); if (strHeadImage.isEmpty() || !QFile(strHeadImage).exists() || !QFile(strHeadImage).size()) { m_strHeadImage = getDefaultFace(); } else { m_strHeadImage = strHeadImage; } } else if (key == "Uid") { m_uId = value.toUInt(); } else if (key == "Language") { if (isGreeterMode()) { m_strLang = value.toString(); } } else if (key == "BackgroundFile") { QString strBackground = value.toString(); if (!strBackground.isEmpty() && QFile(strBackground).exists() && QFile(strBackground).size()) { m_strBackground = strBackground; } } else if (key == "SystemAccount") { m_isSystemAccount = value.toBool(); } dbusArgs.endMapEntry(); } dbusArgs.endMap(); if (m_strFullName.isEmpty()) { m_strFullName = m_strName; } } void LocalUserInfo::initConnections() { if (!m_propertiesChangedConnected) { QDBusConnection::systemBus().connect( ACCOUNTS_DBUS_SERVICE, m_strPath, FD_PROPERTIES_INTERFACE, "PropertiesChanged", this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); m_propertiesChangedConnected = true; } } void LocalUserInfo::onPropertiesChanged( const QString &interfaceName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties) { Q_UNUSED(invalidatedProperties); if (interfaceName == ACCOUNTS_USER_DBUS_INTERFACE) { QVariantMap::const_iterator itVar = changedProperties.constBegin(); bool isChanged = false; for (; itVar != changedProperties.constEnd(); itVar++) { QVariant varValue = itVar.value(); if (itVar.key() == "UserName") { m_strName = varValue.toString(); isChanged = true; } else if (itVar.key() == "RealName") { m_strFullName = varValue.toString(); isChanged = true; } else if (itVar.key() == "IconFile") { QString strHeadImage = varValue.toString(); if (strHeadImage.isEmpty() || !QFile(strHeadImage).exists() || !QFile(strHeadImage).size()) { m_strHeadImage = getDefaultFace(); } else { m_strHeadImage = strHeadImage; } isChanged = true; } else if (itVar.key() == "Uid") { m_uId = varValue.toUInt(); isChanged = true; } else if (itVar.key() == "Language") { if (isGreeterMode()) { m_strLang = varValue.toString(); isChanged = true; } } else if (itVar.key() == "BackgroundFile") { QString strBackground = varValue.toString(); if (!strBackground.isEmpty() && QFile(strBackground).exists() && QFile(strBackground).size()) { m_strBackground = strBackground; isChanged = true; } } } if (m_strFullName.isEmpty()) { m_strFullName = m_strName; } if (isChanged) Q_EMIT userPropChanged(m_strName); } } QDebug operator<<(QDebug stream, const LocalUserInfo &userInfo) { stream << "[" << userInfo.path() << userInfo.uid() << userInfo.name() << userInfo.fullName() << userInfo.headImage() << userInfo.backGround() << userInfo.isLoggedIn() << userInfo.lang() << "]"; return stream; } ukui-screensaver/src/QtSingleApplication/0000775000175000017500000000000015172041106017471 5ustar fengfengukui-screensaver/src/QtSingleApplication/qtlocalpeer.cpp0000664000175000017500000001550515172041106022516 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlocalpeer.h" #include #include #include #include #if defined(Q_OS_WIN) #include #include typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); static PProcessIdToSessionId pProcessIdToSessionId = 0; #endif #if defined(Q_OS_UNIX) #include #include #include #endif namespace QtLP_Private { #include "qtlockedfile.cpp" #if defined(Q_OS_WIN) #include "qtlockedfile_win.cpp" #else #include "qtlockedfile_unix.cpp" #endif } const char* QtLocalPeer::ack = "ack"; QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) : QObject(parent), id(appId) { QString prefix = id; if (id.isEmpty()) { id = QCoreApplication::applicationFilePath(); #if defined(Q_OS_WIN) id = id.toLower(); #endif prefix = id.section(QLatin1Char('/'), -1); } prefix.remove(QRegularExpression("[^a-zA-Z]")); prefix.truncate(6); QByteArray idc = id.toUtf8(); quint16 idNum = qChecksum(idc.constData(), idc.size()); socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16); #if defined(Q_OS_WIN) if (!pProcessIdToSessionId) { QLibrary lib("kernel32"); pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); } if (pProcessIdToSessionId) { DWORD sessionId = 0; pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); socketName += QLatin1Char('-') + QString::number(sessionId, 16); } #else socketName += QLatin1Char('-') + QString::number(::getuid(), 16); #endif server = new QLocalServer(this); QString lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + socketName + QLatin1String("-lockfile"); lockFile.setFileName(lockName); lockFile.open(QIODevice::ReadWrite); } bool QtLocalPeer::isClient() { if (lockFile.isLocked()) return false; if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) return true; bool res = server->listen(socketName); #if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) // ### Workaround if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); res = server->listen(socketName); } #endif if (!res) qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); return false; } bool QtLocalPeer::sendMessage(const QString &message, int timeout) { if (!isClient()) return false; QLocalSocket socket; bool connOk = false; for(int i = 0; i < 2; i++) { // Try twice, in case the other instance is just starting up socket.connectToServer(socketName); connOk = socket.waitForConnected(timeout/2); if (connOk || i) break; int ms = 250; #if defined(Q_OS_WIN) Sleep(DWORD(ms)); #else struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; nanosleep(&ts, NULL); #endif } if (!connOk) return false; QByteArray uMsg(message.toUtf8()); QDataStream ds(&socket); ds.writeBytes(uMsg.constData(), uMsg.size()); bool res = socket.waitForBytesWritten(timeout); if (res) { res &= socket.waitForReadyRead(timeout); // wait for ack if (res) res &= (socket.read(qstrlen(ack)) == ack); } return res; } void QtLocalPeer::receiveConnection() { QLocalSocket* socket = server->nextPendingConnection(); if (!socket) return; while (true) { if (socket->state() == QLocalSocket::UnconnectedState) { qWarning("QtLocalPeer: Peer disconnected"); delete socket; return; } if (socket->bytesAvailable() >= qint64(sizeof(quint32))) break; socket->waitForReadyRead(); } QDataStream ds(socket); QByteArray uMsg; quint32 remaining; ds >> remaining; uMsg.resize(remaining); int got = 0; char* uMsgBuf = uMsg.data(); do { got = ds.readRawData(uMsgBuf, remaining); remaining -= got; uMsgBuf += got; } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); if (got < 0) { qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); delete socket; return; } QString message(QString::fromUtf8(uMsg)); socket->write(ack, qstrlen(ack)); socket->waitForBytesWritten(1000); socket->waitForDisconnected(1000); // make sure client reads ack delete socket; emit messageReceived(message); //### (might take a long time to return) } ukui-screensaver/src/QtSingleApplication/qtlockedfile.h0000664000175000017500000000630715172041035022317 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTLOCKEDFILE_H #define QTLOCKEDFILE_H #include #ifdef Q_OS_WIN #include #endif #if defined(Q_OS_WIN) # if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) # define QT_QTLOCKEDFILE_EXPORT # elif defined(QT_QTLOCKEDFILE_IMPORT) # if defined(QT_QTLOCKEDFILE_EXPORT) # undef QT_QTLOCKEDFILE_EXPORT # endif # define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) # elif defined(QT_QTLOCKEDFILE_EXPORT) # undef QT_QTLOCKEDFILE_EXPORT # define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) # endif #else # define QT_QTLOCKEDFILE_EXPORT #endif namespace QtLP_Private { class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile { public: enum LockMode { NoLock = 0, ReadLock, WriteLock }; QtLockedFile(); QtLockedFile(const QString &name); ~QtLockedFile(); bool open(OpenMode mode); bool lock(LockMode mode, bool block = true); bool unlock(); bool isLocked() const; LockMode lockMode() const; private: #ifdef Q_OS_WIN Qt::HANDLE wmutex; Qt::HANDLE rmutex; QVector rmutexes; QString mutexname; Qt::HANDLE getMutexHandle(int idx, bool doCreate); bool waitMutex(Qt::HANDLE mutex, bool doBlock); #endif LockMode m_lock_mode; }; } #endif ukui-screensaver/src/QtSingleApplication/qtlocalpeer.h0000664000175000017500000000520515172041035022160 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTLOCALPEER_H #define QTLOCALPEER_H #include #include #include #include "qtlockedfile.h" class QtLocalPeer : public QObject { Q_OBJECT public: QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); bool isClient(); bool sendMessage(const QString &message, int timeout); QString applicationId() const { return id; } Q_SIGNALS: void messageReceived(const QString &message); protected Q_SLOTS: void receiveConnection(); protected: QString id; QString socketName; QLocalServer* server; QtLP_Private::QtLockedFile lockFile; private: static const char* ack; }; #endif // QTLOCALPEER_H ukui-screensaver/src/QtSingleApplication/QtSingleApplication0000664000175000017500000000374715172041035023342 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtsingleapplication.h" ukui-screensaver/src/QtSingleApplication/qtlockedfile_win.cpp0000664000175000017500000001466115172041035023531 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlockedfile.h" #include #include #define MUTEX_PREFIX "QtLockedFile mutex " // Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS #define MAX_READERS MAXIMUM_WAIT_OBJECTS #if QT_VERSION >= 0x050000 #define QT_WA(unicode, ansi) unicode #endif Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) { if (mutexname.isEmpty()) { QFileInfo fi(*this); mutexname = QString::fromLatin1(MUTEX_PREFIX) + fi.absoluteFilePath().toLower(); } QString mname(mutexname); if (idx >= 0) mname += QString::number(idx); Qt::HANDLE mutex; if (doCreate) { QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); if (!mutex) { qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); return 0; } } else { QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); if (!mutex) { if (GetLastError() != ERROR_FILE_NOT_FOUND) qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); return 0; } } return mutex; } bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) { Q_ASSERT(mutex); DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); switch (res) { case WAIT_OBJECT_0: case WAIT_ABANDONED: return true; break; case WAIT_TIMEOUT: break; default: qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); } return false; } bool QtLockedFile::lock(LockMode mode, bool block) { if (!isOpen()) { qWarning("QtLockedFile::lock(): file is not opened"); return false; } if (mode == NoLock) return unlock(); if (mode == m_lock_mode) return true; if (m_lock_mode != NoLock) unlock(); if (!wmutex && !(wmutex = getMutexHandle(-1, true))) return false; if (!waitMutex(wmutex, block)) return false; if (mode == ReadLock) { int idx = 0; for (; idx < MAX_READERS; idx++) { rmutex = getMutexHandle(idx, false); if (!rmutex || waitMutex(rmutex, false)) break; CloseHandle(rmutex); } bool ok = true; if (idx >= MAX_READERS) { qWarning("QtLockedFile::lock(): too many readers"); rmutex = 0; ok = false; } else if (!rmutex) { rmutex = getMutexHandle(idx, true); if (!rmutex || !waitMutex(rmutex, false)) ok = false; } if (!ok && rmutex) { CloseHandle(rmutex); rmutex = 0; } ReleaseMutex(wmutex); if (!ok) return false; } else { Q_ASSERT(rmutexes.isEmpty()); for (int i = 0; i < MAX_READERS; i++) { Qt::HANDLE mutex = getMutexHandle(i, false); if (mutex) rmutexes.append(mutex); } if (rmutexes.size()) { DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), TRUE, block ? INFINITE : 0); if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { if (res != WAIT_TIMEOUT) qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky unlock(); return false; } } } m_lock_mode = mode; return true; } bool QtLockedFile::unlock() { if (!isOpen()) { qWarning("QtLockedFile::unlock(): file is not opened"); return false; } if (!isLocked()) return true; if (m_lock_mode == ReadLock) { ReleaseMutex(rmutex); CloseHandle(rmutex); rmutex = 0; } else { foreach(Qt::HANDLE mutex, rmutexes) { ReleaseMutex(mutex); CloseHandle(mutex); } rmutexes.clear(); ReleaseMutex(wmutex); } m_lock_mode = QtLockedFile::NoLock; return true; } QtLockedFile::~QtLockedFile() { if (isOpen()) unlock(); if (wmutex) CloseHandle(wmutex); } ukui-screensaver/src/QtSingleApplication/qtsinglecoreapplication.h0000664000175000017500000000502515172041035024570 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTSINGLECOREAPPLICATION_H #define QTSINGLECOREAPPLICATION_H #include class QtLocalPeer; class QtSingleCoreApplication : public QCoreApplication { Q_OBJECT public: QtSingleCoreApplication(int &argc, char **argv); QtSingleCoreApplication(const QString &id, int &argc, char **argv); bool isRunning(); QString id() const; public Q_SLOTS: bool sendMessage(const QString &message, int timeout = 5000); Q_SIGNALS: void messageReceived(const QString &message); private: QtLocalPeer* peer; }; #endif // QTSINGLECOREAPPLICATION_H ukui-screensaver/src/QtSingleApplication/QtLockedFile0000664000175000017500000000374015172041035021727 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlockedfile.h" ukui-screensaver/src/QtSingleApplication/qtlockedfile_unix.cpp0000664000175000017500000000661415172041035023716 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include "qtlockedfile.h" bool QtLockedFile::lock(LockMode mode, bool block) { if (!isOpen()) { qWarning("QtLockedFile::lock(): file is not opened"); return false; } if (mode == NoLock) return unlock(); if (mode == m_lock_mode) return true; if (m_lock_mode != NoLock) unlock(); struct flock fl; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; int cmd = block ? F_SETLKW : F_SETLK; int ret = fcntl(handle(), cmd, &fl); if (ret == -1) { if (errno != EINTR && errno != EAGAIN) qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); return false; } m_lock_mode = mode; return true; } bool QtLockedFile::unlock() { if (!isOpen()) { qWarning("QtLockedFile::unlock(): file is not opened"); return false; } if (!isLocked()) return true; struct flock fl; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fl.l_type = F_UNLCK; int ret = fcntl(handle(), F_SETLKW, &fl); if (ret == -1) { qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); return false; } m_lock_mode = NoLock; return true; } QtLockedFile::~QtLockedFile() { if (isOpen()) unlock(); } ukui-screensaver/src/QtSingleApplication/qtsinglecoreapplication.cpp0000664000175000017500000001235515172041035025127 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtsinglecoreapplication.h" #include "qtlocalpeer.h" /*! \class QtSingleCoreApplication qtsinglecoreapplication.h \brief A variant of the QtSingleApplication class for non-GUI applications. This class is a variant of QtSingleApplication suited for use in console (non-GUI) applications. It is an extension of QCoreApplication (instead of QApplication). It does not require the QtGui library. The API and usage is identical to QtSingleApplication, except that functions relating to the "activation window" are not present, for obvious reasons. Please refer to the QtSingleApplication documentation for explanation of the usage. A QtSingleCoreApplication instance can communicate to a QtSingleApplication instance if they share the same application id. Hence, this class can be used to create a light-weight command-line tool that sends commands to a GUI application. \sa QtSingleApplication */ /*! Creates a QtSingleCoreApplication object. The application identifier will be QCoreApplication::applicationFilePath(). \a argc and \a argv are passed on to the QCoreAppliation constructor. */ QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) : QCoreApplication(argc, argv) { peer = new QtLocalPeer(this); connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); } /*! Creates a QtSingleCoreApplication object with the application identifier \a appId. \a argc and \a argv are passed on to the QCoreAppliation constructor. */ QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) : QCoreApplication(argc, argv) { peer = new QtLocalPeer(this, appId); connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); } /*! Returns true if another instance of this application is running; otherwise false. This function does not find instances of this application that are being run by a different user (on Windows: that are running in another session). \sa sendMessage() */ bool QtSingleCoreApplication::isRunning() { return peer->isClient(); } /*! Tries to send the text \a message to the currently running instance. The QtSingleCoreApplication object in the running instance will emit the messageReceived() signal when it receives the message. This function returns true if the message has been sent to, and processed by, the current instance. If there is no instance currently running, or if the running instance fails to process the message within \a timeout milliseconds, this function return false. \sa isRunning(), messageReceived() */ bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) { return peer->sendMessage(message, timeout); } /*! Returns the application identifier. Two processes with the same identifier will be regarded as instances of the same application. */ QString QtSingleCoreApplication::id() const { return peer->applicationId(); } /*! \fn void QtSingleCoreApplication::messageReceived(const QString& message) This signal is emitted when the current instance receives a \a message from another instance of this application. \sa sendMessage() */ ukui-screensaver/src/QtSingleApplication/qtsinglecoreapplication.pri0000664000175000017500000000050415172041035025130 0ustar fengfengINCLUDEPATH += $$PWD DEPENDPATH += $$PWD HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp QT *= network win32:contains(TEMPLATE, lib):contains(CONFIG, shared) { DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport) } ukui-screensaver/src/QtSingleApplication/qtlockedfile.cpp0000664000175000017500000001374215172041035022653 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlockedfile.h" /*! \class QtLockedFile \brief The QtLockedFile class extends QFile with advisory locking functions. A file may be locked in read or write mode. Multiple instances of \e QtLockedFile, created in multiple processes running on the same machine, may have a file locked in read mode. Exactly one instance may have it locked in write mode. A read and a write lock cannot exist simultaneously on the same file. The file locks are advisory. This means that nothing prevents another process from manipulating a locked file using QFile or file system functions offered by the OS. Serialization is only guaranteed if all processes that access the file use QLockedFile. Also, while holding a lock on a file, a process must not open the same file again (through any API), or locks can be unexpectedly lost. The lock provided by an instance of \e QtLockedFile is released whenever the program terminates. This is true even when the program crashes and no destructors are called. */ /*! \enum QtLockedFile::LockMode This enum describes the available lock modes. \value ReadLock A read lock. \value WriteLock A write lock. \value NoLock Neither a read lock nor a write lock. */ /*! Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way as \e QFile::QFile(). \sa QFile::QFile() */ QtLockedFile::QtLockedFile() : QFile() { #ifdef Q_OS_WIN wmutex = 0; rmutex = 0; #endif m_lock_mode = NoLock; } /*! Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in the same way as \e QFile::QFile(const QString&). \sa QFile::QFile() */ QtLockedFile::QtLockedFile(const QString &name) : QFile(name) { #ifdef Q_OS_WIN wmutex = 0; rmutex = 0; #endif m_lock_mode = NoLock; } /*! Opens the file in OpenMode \a mode. This is identical to QFile::open(), with the one exception that the Truncate mode flag is disallowed. Truncation would conflict with the advisory file locking, since the file would be modified before the write lock is obtained. If truncation is required, use resize(0) after obtaining the write lock. Returns true if successful; otherwise false. \sa QFile::open(), QFile::resize() */ bool QtLockedFile::open(OpenMode mode) { if (mode & QIODevice::Truncate) { qWarning("QtLockedFile::open(): Truncate mode not allowed."); return false; } return QFile::open(mode); } /*! Returns \e true if this object has a in read or write lock; otherwise returns \e false. \sa lockMode() */ bool QtLockedFile::isLocked() const { return m_lock_mode != NoLock; } /*! Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock. \sa isLocked() */ QtLockedFile::LockMode QtLockedFile::lockMode() const { return m_lock_mode; } /*! \fn bool QtLockedFile::lock(LockMode mode, bool block = true) Obtains a lock of type \a mode. The file must be opened before it can be locked. If \a block is true, this function will block until the lock is aquired. If \a block is false, this function returns \e false immediately if the lock cannot be aquired. If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock is first released and then a new lock is obtained. This function returns \e true if, after it executes, the file is locked by this object, and \e false otherwise. \sa unlock(), isLocked(), lockMode() */ /*! \fn bool QtLockedFile::unlock() Releases a lock. If the object has no lock, this function returns immediately. This function returns \e true if, after it executes, the file is not locked by this object, and \e false otherwise. \sa lock(), isLocked(), lockMode() */ /*! \fn QtLockedFile::~QtLockedFile() Destroys the \e QtLockedFile object. If any locks were held, they are released. */ ukui-screensaver/src/QtSingleApplication/qtsingleapplication.cpp0000664000175000017500000002704615172041035024261 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtsingleapplication.h" #include "qtlocalpeer.h" #include /*! \class QtSingleApplication qtsingleapplication.h \brief The QtSingleApplication class provides an API to detect and communicate with running instances of an application. This class allows you to create applications where only one instance should be running at a time. I.e., if the user tries to launch another instance, the already running instance will be activated instead. Another usecase is a client-server system, where the first started instance will assume the role of server, and the later instances will act as clients of that server. By default, the full path of the executable file is used to determine whether two processes are instances of the same application. You can also provide an explicit identifier string that will be compared instead. The application should create the QtSingleApplication object early in the startup phase, and call isRunning() to find out if another instance of this application is already running. If isRunning() returns false, it means that no other instance is running, and this instance has assumed the role as the running instance. In this case, the application should continue with the initialization of the application user interface before entering the event loop with exec(), as normal. The messageReceived() signal will be emitted when the running application receives messages from another instance of the same application. When a message is received it might be helpful to the user to raise the application so that it becomes visible. To facilitate this, QtSingleApplication provides the setActivationWindow() function and the activateWindow() slot. If isRunning() returns true, another instance is already running. It may be alerted to the fact that another instance has started by using the sendMessage() function. Also data such as startup parameters (e.g. the name of the file the user wanted this new instance to open) can be passed to the running instance with this function. Then, the application should terminate (or enter client mode). If isRunning() returns true, but sendMessage() fails, that is an indication that the running instance is frozen. Here's an example that shows how to convert an existing application to use QtSingleApplication. It is very simple and does not make use of all QtSingleApplication's functionality (see the examples for that). \code // Original int main(int argc, char **argv) { QApplication app(argc, argv); MyMainWidget mmw; mmw.show(); return app.exec(); } // Single instance int main(int argc, char **argv) { QtSingleApplication app(argc, argv); if (app.isRunning()) return !app.sendMessage(someDataString); MyMainWidget mmw; app.setActivationWindow(&mmw); mmw.show(); return app.exec(); } \endcode Once this QtSingleApplication instance is destroyed (normally when the process exits or crashes), when the user next attempts to run the application this instance will not, of course, be encountered. The next instance to call isRunning() or sendMessage() will assume the role as the new running instance. For console (non-GUI) applications, QtSingleCoreApplication may be used instead of this class, to avoid the dependency on the QtGui library. \sa QtSingleCoreApplication */ void QtSingleApplication::sysInit(const QString &appId) { actWin = 0; peer = new QtLocalPeer(this, appId); connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); } /*! Creates a QtSingleApplication object. The application identifier will be QCoreApplication::applicationFilePath(). \a argc, \a argv, and \a GUIenabled are passed on to the QAppliation constructor. If you are creating a console application (i.e. setting \a GUIenabled to false), you may consider using QtSingleCoreApplication instead. */ QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) : QApplication(argc, argv, GUIenabled) { sysInit(); } /*! Creates a QtSingleApplication object with the application identifier \a appId. \a argc and \a argv are passed on to the QAppliation constructor. */ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) : QApplication(argc, argv) { sysInit(appId); } #if QT_VERSION < 0x050000 /*! Creates a QtSingleApplication object. The application identifier will be QCoreApplication::applicationFilePath(). \a argc, \a argv, and \a type are passed on to the QAppliation constructor. */ QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) : QApplication(argc, argv, type) { sysInit(); } # if defined(Q_WS_X11) /*! Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, and \a cmap are passed on to the QApplication constructor. */ QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) : QApplication(dpy, visual, cmap) { sysInit(); } /*! Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a argv, \a visual, and \a cmap are passed on to the QApplication constructor. */ QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) : QApplication(dpy, argc, argv, visual, cmap) { sysInit(); } /*! Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be \a appId. \a dpy, \a argc, \a argv, \a visual, and \a cmap are passed on to the QApplication constructor. */ QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) : QApplication(dpy, argc, argv, visual, cmap) { sysInit(appId); } # endif // Q_WS_X11 #endif // QT_VERSION < 0x050000 /*! Returns true if another instance of this application is running; otherwise false. This function does not find instances of this application that are being run by a different user (on Windows: that are running in another session). \sa sendMessage() */ bool QtSingleApplication::isRunning() { return peer->isClient(); } /*! Tries to send the text \a message to the currently running instance. The QtSingleApplication object in the running instance will emit the messageReceived() signal when it receives the message. This function returns true if the message has been sent to, and processed by, the current instance. If there is no instance currently running, or if the running instance fails to process the message within \a timeout milliseconds, this function return false. \sa isRunning(), messageReceived() */ bool QtSingleApplication::sendMessage(const QString &message, int timeout) { return peer->sendMessage(message, timeout); } /*! Returns the application identifier. Two processes with the same identifier will be regarded as instances of the same application. */ QString QtSingleApplication::id() const { return peer->applicationId(); } /*! Sets the activation window of this application to \a aw. The activation window is the widget that will be activated by activateWindow(). This is typically the application's main window. If \a activateOnMessage is true (the default), the window will be activated automatically every time a message is received, just prior to the messageReceived() signal being emitted. \sa activateWindow(), messageReceived() */ void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) { actWin = aw; if (activateOnMessage) connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); else disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); } /*! Returns the applications activation window if one has been set by calling setActivationWindow(), otherwise returns 0. \sa setActivationWindow() */ QWidget* QtSingleApplication::activationWindow() const { return actWin; } /*! De-minimizes, raises, and activates this application's activation window. This function does nothing if no activation window has been set. This is a convenience function to show the user that this application instance has been activated when he has tried to start another instance. This function should typically be called in response to the messageReceived() signal. By default, that will happen automatically, if an activation window has been set. \sa setActivationWindow(), messageReceived(), initialize() */ void QtSingleApplication::activateWindow() { if (actWin) { actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); actWin->raise(); actWin->showNormal(); actWin->activateWindow(); } } /*! \fn void QtSingleApplication::messageReceived(const QString& message) This signal is emitted when the current instance receives a \a message from another instance of this application. \sa sendMessage(), setActivationWindow(), activateWindow() */ /*! \fn void QtSingleApplication::initialize(bool dummy = true) \obsolete */ ukui-screensaver/src/QtSingleApplication/qtsingleapplication.pri0000664000175000017500000000106115172041035024256 0ustar fengfengINCLUDEPATH += $$PWD DEPENDPATH += $$PWD QT *= network greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets qtsingleapplication-uselib:!qtsingleapplication-buildlib { LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME } else { SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h } win32 { contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT } ukui-screensaver/src/QtSingleApplication/qtsingleapplication.h0000664000175000017500000000761715172041035023730 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "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 Digia Plc and its Subsidiary(-ies) 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTSINGLEAPPLICATION_H #define QTSINGLEAPPLICATION_H #include class QtLocalPeer; #if defined(Q_OS_WIN) # if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) # define QT_QTSINGLEAPPLICATION_EXPORT # elif defined(QT_QTSINGLEAPPLICATION_IMPORT) # if defined(QT_QTSINGLEAPPLICATION_EXPORT) # undef QT_QTSINGLEAPPLICATION_EXPORT # endif # define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) # elif defined(QT_QTSINGLEAPPLICATION_EXPORT) # undef QT_QTSINGLEAPPLICATION_EXPORT # define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) # endif #else # define QT_QTSINGLEAPPLICATION_EXPORT #endif class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication { Q_OBJECT public: QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); QtSingleApplication(const QString &id, int &argc, char **argv); #if QT_VERSION < 0x050000 QtSingleApplication(int &argc, char **argv, Type type); # if defined(Q_WS_X11) QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); # endif // Q_WS_X11 #endif // QT_VERSION < 0x050000 bool isRunning(); QString id() const; void setActivationWindow(QWidget* aw, bool activateOnMessage = true); QWidget* activationWindow() const; // Obsolete: void initialize(bool dummy = true) { isRunning(); Q_UNUSED(dummy) } public Q_SLOTS: bool sendMessage(const QString &message, int timeout = 5000); void activateWindow(); Q_SIGNALS: void messageReceived(const QString &message); private: void sysInit(const QString &appId = QString()); QtLocalPeer *peer; QWidget *actWin; }; #endif // QTSINGLEAPPLICATION_H ukui-screensaver/src/agreementinfo.h0000664000175000017500000000427615172041035016565 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef AGREEMENTINFO_H #define AGREEMENTINFO_H #include #include #include class AgreementInfo : public QObject { Q_OBJECT public: explicit AgreementInfo(QObject *parent = nullptr); explicit AgreementInfo(const AgreementInfo &agreementInfo); virtual ~AgreementInfo(); bool operator==(const AgreementInfo &agreementInfo) const; public: inline bool showLoginPrompt() const { return m_showLoginPrompt; } inline bool hideTitle() const { return m_hideTitle; } inline QString promptTitle() const { return m_promptTitle; } inline QString promptText() const { return m_promptText; } inline QString promptTextFilePath() const { return m_promptTextFilePath; } void updateShowLoginPrompt(const bool &showLoginPrompt); void updateHideTitle(const bool &hideTitle); void updatePromptTitle(const QString &promptTitle); void updatePromptText(const QString &promptText); void updatePromptTextFilePath(const QString &promptTextFilePath); protected: bool m_showLoginPrompt; //是否显示特别提示界面 bool m_hideTitle; //是否隐藏标题 QString m_promptTitle; //特别提示标题 QString m_promptText; //特别提示文本 QString m_promptTextFilePath; //特别提示文本路径 }; typedef std::shared_ptr AgreementInfoPtr; QDebug operator<<(QDebug stream, const AgreementInfo &agreementInfo); #endif // USERINFO_H ukui-screensaver/src/assets/0000775000175000017500000000000015172041106015061 5ustar fengfengukui-screensaver/src/assets/ukui-loginopt-iris.svg0000664000175000017500000000177215172041035021364 0ustar fengfengukui-screensaver/src/assets/ukui-loginopt-face.svg0000664000175000017500000000205315172041035021305 0ustar fengfengukui-screensaver/src/assets/capslock.png0000664000175000017500000000044415172041035017371 0ustar fengfengPNG  IHDRĴl;IDAT8q@ D_%@ na[0-mB q a!|1_d|j%G%I ׼V  pyYdvAl(i qlkW#hKcl T^RTg%]Yh:%:$,6 q䂄[!ql_SbCUcUIPZ e>),LjxVт~ڮBI9`ouE'ݕ2IENDB`ukui-screensaver/src/assets/switchGreeter.png0000664000175000017500000000204615172041035020411 0ustar fengfengPNG  IHDR::J(IDAThYKTQ=a RlZ)EAm °u;mD~`آڕnlѮVPDJEF6]3/g\9{{J(X#Ah@%_z@ fx `=#u6œ8''HnQk\s2f43rShw醏RmQ1zH2I 1By",o5Q ` >8QqlKPQ&PG0F uc5= rXt:6a2=Bq"IcU;m=yWN-In]!P9qǬI%kZVV#ZI6yMLݎ8b\4.0|@Q;f:[DJt60"} %P(V&udJIENDB`ukui-screensaver/src/assets/scrollbar_down_clicked.png0000664000175000017500000000017715172041035022265 0ustar fengfengPNG  IHDR;֕JFIDAT(c 2p(05DRYfl`ՈK3604ӌFBj$F3^0D5 NIENDB`ukui-screensaver/src/assets/scrollbar_down_hover.png0000664000175000017500000000017615172041035022011 0ustar fengfengPNG  IHDR;֕JEIDAT(c-\HscrkDǪX5Ҍ 4#kfь Q ٪IENDB`ukui-screensaver/src/assets/01-default-community.png0000664000175000017500000017427515172041035021474 0ustar fengfengPNG  IHDR+usRGBDeXIfMM*igI'IDATxwG/=}5Fi-'ɲ  ombeYvvcþm?Y 8a# VH0&UGUwW{Gf#vW:u"?Oe~J&+ȹCC/ndV0@ ̜C&^XޓE{VgEV8`aD<008cCTOefW XSv+ xe X>%(lf˘fwQJ^)dD E3dA7 1\Oଉ95n#omIJ-})O$܎zW=\O3 PqEy; 9`4g 2~/P})@o{F}^H!,#AϟUSD Lm`YږKOnxϷ=(>dR `]G81`ˠf:!Dl Yp'`[dańP @3c};CǬed HY@)v1 Cx z?LLZKZj" usb1*F<;0N;8`}8!OۯţOn(+20TLǟWGK9 gKM !QюnƎ]G1aM1u{ѯj%  !{ocS/>[f|9رSaƝ'Hl9` {B~e&OnE6G8 &dp0z5K N|FE}٠'NZ|\< wxGOs}$.$d>5CakJلo2ڧj㦿 8@k6)/R@({>=I0}Zml76?=r޻a!ruޅ/neH*]2~f ! |0TČAEbԚ⮺a@NF?\Նz6cˡ~6=d55c5 |c:k IA*x>wxǂTe!`B Jz8}xqlRJycthpIT&C, JG4z>&f vi_E=tR%.!c_`R"dL3f1| GȞW"h?K;s((F !ǡH"-LgϢ?9~f)-]I@yy]qgK?_> Fy\8%՗5>w(APez) ?9 {%#ƈ/`Q4BI3&)]ѝ?mb@XҖ@Y3- +!$Tl|ZmQW6b*ZZE2hчmYH%h/*6rFe=Yz Kۚ01%F\d(,",3E`x1.lRs< pކq QL ܸ5|yO `b+P֯jvOVNI<]s^C1J[?*p@İw8޶ }u#?+A( $ʮ5sp,Z}UMp"A(A6D&!|.vhCe?ʐ\ ,p #>5 7bGni0:[MXzc=cC;`B\`j;Vo#b Wvv'^?>U鯑2pRT) j[J3;J\sh!~[9zDҶ( W81Zġc#wBdVE}HqnO]9lXX7̝9 r;Z6Yjj;J/x s'b~C"|PW]X4I- ^3۞X:d cZa01KO̘0 1T)+-Yл>hNR,6s1*C4&}^ǟv  4]æ]gU|)A\}ӹkX8}x3m}A7`IB!7҂X@C8q6,4e{w*qǏ2Df}1@GW8f`!^=?:^seE( X8d25=/m:"e7T!_J]-u.GS{JB0:ށO| tp`ێ@jgGg)UKV%BTNZIwcP-QA6,"m~w{T"D^%PאG.-:`MHpR״Q:|eDGg{n3c>ز,njZ{BRb_^UeE:醻M)}:rd Dg/m f8n;Efq^ U`'6pZGT;\u>˜Pֵ݆Ek?|%/w|σoJJ|,}xDrZ>l5>p:? ,EOBEHZLFCƉ}Ăk/5-odx!R2|f&+HFzӕc#do%@ ӕ@ї( /!YݾQ} _kH+N&J XEwY[`m0cbS '('!7@pu*i{V[d1_Id#" 5'LUx]WH b:?@H9&aa %]~&l ͚3J`̓$\ 01Wb(\"xp ӓxJz(lf:RB &"qIoVG8 uC1 𡮝W2>%_“@c"2ed#9{Hp|+Aa21—̐W0Tm9Z ϑ%3> d+Q ^h"a,piީp8MMi%1= g03HgW0c T'dvnNϲN?Q{TEN<ڳVvq P 'iY,;bᖌt:K{s!!$ :%AL$/!$czKMit.>)]axZ{6x}dmܲj1PFaY6r  b$l’IDeθP.- )TAčj|orbz{Hbv .iv>z;'ҵBqX≍e pb"rȥS K4dh"rW.NGP X$l*k~e2F /! &f*\V&#\Q-^3 0u*<# A& ˗KYp8h (hta<-BzI璥 ,B0fud@~b>'F!nˁaNR*L^22i\8dRe&mrQK4 A( x+/N(0tLjF 2 D̘فN&*GOwzlځ/?hjb )U0)/ۛ+Y!Q!+bHcڻ8~{U2T29p[eZgcoA:g9&FCu64|7ۋQ0nPEw  :#o[޾8ÌDgNZgX_˛-S۟Ʒ ߗkCG, ?2ŧNkԩi8kUT+rh$y7GOw6ęXي_-E"[̀/ݴ =uX1>= ޶&,2] al]2;vn] ٶy=3)[` 6ԣmBf-S`C='!@҃Xd޸??T)K>wR$J%fa;b6AIRPGk8G>aYMHJ5A!`u;BNe2hjʽ:, C>펟Ɏq Or< ]6m;8sƎiGx G~;:Gkp_{޼' )ؗJіWt_یsۄ?se/^vT|ؕل}}JǶ'\]わbjj˟r렽>;N/hj-qI0dBBRaqUTO :Jd%$+!0=:MY? |g1KژݒR@YHxBU[4X' KoF.@#gxrFπ|d^꿅vn)ŽDC0kDI0< 2 BQ(x+TX]Hv cW "E)8 YSrtjs?(؍s;K`|FAH8&Z)n| -¤H 锷MAR3{2( ӔL_rDZ:`s:K$DQHV&[b+!"$g\080kVP>41Ht6eZFm̡>$DS8hv%b,@QbFsB;w@@^6Эl*Q"D- ( ƨ(x^q5DN> <+zx@ BYm A//3r˧*l.A/M%6Tx49I,r)(@zZ^EQe057$C1OQG~!8>VpiϜ1&,GR谽ǽ ڄI q,E۲^<\3q%4nk@g ' qdžPܼ;oPy~uI$lBsEa*f_*44^PhTZ@E&u PaH;e'0 #.pTO% B߄GgVg i +JUU̜G-cT4!2fp4$~10Ȑ*ĨWcr')%g-A2k g zv̚܁gC&B&ml0Κ2lTz\$C5/@*?4킊m+֣) *|Ta$O[[ÃCeӨ&808WMLdk:qU=K )ژ]˵jk"M =܀gahc(>q G2a.\ IMqFb$EZ_WRipʁSp,9mWWocsWݪ^}-#Ձfl #2 )nnyQfL7 TauK˔\NW JPInޔYEZJk9]- |iŒR#ce z^0W8-,3s\$4䘝}Bjx/T{ YVo-Wյbժ,"a^wTޮA?y!cbXH:g! 4Ιʊ?yBD$CigLaFco~!,eڬLZOdwGuL}vc2|4-+tA .jrI, nre{Dޫ!˜Psc ,΢o0Ԇ)`צ'n"E91ڍPWu€/YČ_<9vj#=Nª!gPja 'd J}@K=E[dSuBdӦT`rO2pؖ2*b>?WK{4kNN(JHF[.|B =-f\ӆM@>$$+%*urjȀ*RqQ*M״8˘GW:d'imCϼ0i6`8׷F1LDVH)+!iY7~ ~s_.ۍP&l5ñ<뽑 }W8c#!f$̌| fhv [|e61jo?[^"!Īk!WݴH7#}^RQce/d@%MbKdh7LjKyytSz%W⩍Xm,$г!{35, dXgUB]kg^G;\I _ba^ƁAkmķU#54X;$sYר]IFpyw o6K)Sc(Ӓ; >Ci??+^$eu{j7t]' 8RNeYaa"B`ؐ~vɭc?yK{?7+,%  !r֊#tG@],$d=Win6ԣe685g4ՏW^X~'R (fo<a}5AAދ*B*{egXvv."F0k-O݈Ȯq@'ý;lAd.Sʴj7QGUbz &%!YAyH݁t"PX\T?yAɢ_wWLJ #[  䓉mo6&}n++ 6&"NUtH15CP $;W2Cg,>yS}pL0k+A"nab ٺ,,ۊaLP XִN;tV]/[uuJGBnrmĶǔKkȣT>'-#I)ya)1<4Ogx;c?)KL˜t:qd`dHgSp'^.N|}`/[۶a;6l=B 1[{<,5dBkEYjd ɺI Wl!\Mk΢xD>'3X;޹p'_4Nb9֒ jՕBԐ!\QRW_? MZ+cUt}QP{g3r+)N_&YFꉀE5c~ZSco~tPL'eS5RĎgn=ˈWwגV P-b V4Omv?ho+ΘzI &N_&ha6^q/\\PFQ-_㌫dZ& AO5s|!uILE`FcxX } )=LP]2!j"upf'Pd4SJ7žҲZ2W}6|uU6rΜ?xt:|'/"N@lku0eM7A2↟ZPP\\)NvV~ï`?3s¡|5uʮ7%R< fiU˄@*AyV,\:6|l-(7gc8oӕ?^ǤYLAH1?$l˖@0e]J16GTjJNt7`; ",oYT2r& ~-Q;A$S-h> '挍ZݨѦY26 ?[èj F` a6U"T _t -v u L5ǧ_?EH6W{Э͢Р ?:Ft/$dMŽ6 6N&Ag_tWJo)6 ՚IcF]6* jGWu+FUbk3ZŮjۆ fP'Wuf+B;PTYoF%9z/JDL"$ٹXlWh][__8 `yj1^Wt2 cYƍ!04<VkYdtlZғǡ1/\i\X% '2]X% ]A,# 9Y#Mit1g7ׄ;<1Z5=&^r\]MW9#LJjGgݴxUIM)`3Chqto㗔J9WJ1&8H69jv 8vC}ƅ1[ʲ,zSrmCR#Vl~fQs+uF\2|&]G&&#p=? #o.q(W0*τT$#%]Gݘبiu`0])4?؍#W꒑{`D%gO{73)@w6±,(F<ƨ+f7LDQg|qO} X<)3zt0T 5\/A !J VTA\E4C"214 .Tk8F.+brѳH9K ݇T ߙ*)5t`--U۵y 4 2  Y (BL03\q$kӐVnCC({R+~!VA Cl-nO}/c1Zw̏%;&af)8,x,W"mEj(w.  _}:]?Y?_(}h{:N:a-#n;< mpv%J!D6㌉p UcXhY峈\WJ7'G-aE}nN]O:C +C2fv1 B^rZ?Yk+wg;nھQy ~/yS;ce%Hi-$ $)lRA]f0o, imu(iTG` -+NvгkӃyʎP Z)[I>,eS2!;a(s;&GײOt^}%CP(({~He bqowlᤐ844 ,B: J!H::BȄ 4ftz=҉9yp,"e}NhOxu/}ԏ%df^/!\4#zW\֛i1Wow(BT皝 OK?ۜÆ=9u/Qr]]eCp]wfE -Q(  B6$`3%صKŽ Ƌw=G̫dNrghǑA?D0F2tYg!O^4#]i“keo3 1v:~vGyGO@.%<_ut=(==u_\$Z &24S3¸[ƣzh42:#ao o4 "BE?Y(ĺGbc᜞lx=~}ww ni+#~'d+aQ{7jp(hك'TB|d(bՊUS3K:-:q8a\%_Ybsv5{|}dϋ>X-~,¤zABINvMg0)fj_[BsbB]ZK4/rnqnUY>_#|l1sR;Yl{qg X68#g֕#ג\,T%N]} Gg(fVL9Hت[.b^̖pWu, |`JL&Tn뼆rQz^ݑ[N^1*xEI6ۧXظH2-6ͪ.Tg2Y!#CGecDCDC3.pjL cϦ($lԖO`sx=.ZЕ>Aݎf ( | sڬWH2#'S( QאA$`EaM,@ɺbi]֌`o:A 7b@X EX~B!nB2P.ѳ$YCP,njK) @05XO}y&4A1 H;4^˲`M:!-UOù'] MqPTYSc;3Fn`fp^ٌ4.=.|wX.+ 0A'&رӟ̀|t̵a9t\8^A_(M:'0;λz'&C}]-*lmotף'݂mgrNt+%H8/qh bN~̧`;v' bԸhm%iFC=Egҡ9ZؓBOEo)&JBU}d PKb1kv uܱ汫ziY3T!$|1>APhbH11EAE N444C74 MCUuխ{~{sA }|k2wvIxXr/8'ĒK."b8-#xKSa5^8iLs톧i S V)Ujt\]*8b?lSᎋsm85;Q﨡T)3ylfVV9jr `IhN$88`x$Ɓ &B|'.}h01dy W8`X<0vq`|l 虢c[p٪GN9[ɿs߮(sLC:8ګM/"(3lûKz|}t5FRGRJy98ԖL diZ%I+fru Y ec$ɀŐ$10FDs.Z9[wيYkMu ;y&gn.uc8>:{&fc1z> (8ۆCzaUD} 3%ݘgxD8WFdekI:{.5mE[{>}##d(Fe?JsltcKеA[)G &a_q3p1^sî g(+ 0#pFvݧ>aPҜv \˯`C9z6t^sîQA fJqz13l8eEͦcAbԚBB#\.cZ!T T ){Wͽ뙬e  pb}.57FȎrb(:^lƴu̜}hW Y#fϊ^7 K|%d#Qbw0M\CQ^|xբcApp?'nBa1˝fcQaNnYmqf&:F Bm`>/^(9mr 1^zɂ='1DĮaWc +%U Viay !yO(QȰF&J??agyE>{(繍vn`a8leQxKCIk[(Nְ}ĥk}}h\-s>+_y#MqPu9 ]C@1`>W'B/x@cyFo'/)n +^ky$cncZ( 汜;R%9o眵`M!= HWisX|}^4'g<~5oa@N.Z2rv+̈́͠m4Iyŗpnl=4AL„'W%y;x@{8G%`<|ʅGk٥??<˷n"bf(B LF2a"}U׉صh⡽ZFHEHܾ]G 66^Zp$[ g\wXłw5XU(r^A`< VNi80{6u 5yJoB`1%O8}T`"b$L 9@v;ho[v֘cіщ.,w};n.*ɦH$XSec Am0ӆ@Yc rؒ!ڇXN+p URMCI`"G:m^G _w7XW,cR("%%Ӳrc9МJr6Quư{[DNaE)1l@c.@gX<"OKx|6jXC"$[djMz| W3r^0㑬dаk4s˦ejJ _x /_`îҗ/limw c\0'9$cMGĒDhGBo%`1cG}7ʷ2,ASJ$KD&+ahiH pZ5RW 紳1p+ԣFH(2w~cN{hUq!iXu v \&PQ5w008̘4:<_fjFf}eV5 1mĕ~Q6Į[]EwkX?E 9L@EHw~(ݟ!Br-\dFX`K=GkӲQfTtT=,Q*j4>g AC2洗R@,0zGFt/ >?x5o|Aao\7X.RdndAFx 'ίI'[m_ٸ#Hv;۫5}vG"=„&P2@ D@3d4rd3σ;Y^>ocFoz 4)0J#Lf2B&vvUd6]ec3/? 8˝Tj܉:p?g{4cPz(1lQKA181E$[V;*X&訸DDja̲sDwnC lj02bw՜8ahKD~0!L$J& cJ=.-~g~RGQ֬╖ t(!YkֲuۚK~0#n_ڰs1Y*g:5z l@H^0&4J=I wgof]%L$<(Ң:6A!Hs5@2$0$53pSv9/NA u -cHw|j*Y|e A+3kA 1K{KhKi;-Ƕ4OP1A, AZE(,AB4~9}a&xdwJ_x?ox^XV?>h^( 0e@$P* @)Uzc2R}6<+t;mfFQ㫮f:quhD8q^ ]fvG:1k7<$o&w@΍ RA`, 6wAč'HHhjKbHJpI`kgVQK>N5l栈nN+ΌOy'JWODڊ3*c#Ȁn?236/nxj7#>yac8y[@:7X-q711HFeU}*܎|b\fUQCx)pfl߯JJߙ6?̋Ihuku+2.tt( !oq4GŪ'P϶<i?ܿ[{͇Z@d팓b&z2gV'tTHo#gxnܵc̡\a6[@zF20-{M7Kin@%(3Izy)#8uA=Uf`2XT%gEӋQ>^ە} x>32n}Gc",sg x}( ?J`k:!`羃:=Ө.9}䱃Vpt=|3LЈNf]~aj"L(Wrv3 ige"C Ȥr[YD|ʙYE<,O~.dq:'/ 0.XsH %Nr"LkǞ6J ;NGd+ yogW31c%f^@gM O'.(0"ߖ}J33kIݭ|gh?=@PL!әzp]8EfǬ^NVxI(8s  83+@qÔP`a,1 _T#!L@W)! u8iN9 EM%6޻<GQkzR'E#aHl #yxp:\F1 Qm謥d<{5lְj9㺤l "#`u &B-"18T0HRVɀ0+&lܥu(%:"]ɕ >b{9nMFQPo7]} b8F'@$Iq0IxZT[{yXd}qj;Ifh邧<jjDfsAbP+31]M,( *,H.\fMzG}C#ÇC^fkb@RuڐqV*n:yw1Ǩ J0ys"P8[R^`<*|p7=qSoJ@-.+RQpI<뮚9.;lV[B#c 2PVsO{1a|_A FIWl4〥 OHD"Yfd8%TU]QŢY;e| "Jlσxi_># ΀ tI͎jr)ь05$q$sTYMaT{;aU@$"y uğbU3y1vfE zq h6ϸﵧ30C#LwJyK(߃=(y *a,.X4$FI+1%8&70sp&mtS*Kف%8gQSɅpս"I[&w6{&wC/c7a"hƘ.{vDQ #"|G\Bgi=Rt~,18% X NQaRιLM)N6nk:}H Tnjл+|}ԪUuv*l8w>ffa"1ьG @\%bWD7:F$I{ʁZZGq%Rm>fbno>p*?I$H ciW)~Pc҆?I$G Em+O4 5=7(Pn#۪xj"qa(Fa"hFB4!'c_"8D C4#Hn1 lpE'B@$8h)m6/]+C$P]0O3X4X) ~t#>/}y1,bN~Al:T{FF (D?fFb|"y'3z/sL@)53U>6Iz8##c96N 9tGH_9Y 2^`񩵃?ۍČՌ݌e[aD3#pAgm!Da0ь_f 3V^yuB>aB/b[sfx!P-"-ĥ&nGKu2!K1\- wm6yJ!gtH#DiDQ~a M&矰]&N`t*422'8p ,86n8NZ^BOinmk PTzH@WKfV^{?qƄq`n Hpo\6% pp4@#B ĉ@'Q)b"a3jѐ֦7U=[:s8 yp _6k g)OZ)nmXLe#EdeqD"S c=[C^Ѷ~UK9Ҙ!.,Kl75Yw4Qhh3F,>$IH JRObf`" qKP-yOpvSNZᣧOv˯&,b N- L 챭P9bjjId0!3讦m+rn|?H\˹z;w8nYģO-`ȾHEjm=~ۿCZnG秷gvcqwRA)Ti4[n] bA(3l{~{mEk]ŝܳ!\B2GT& "uX.2B1"aao>m5ͩ|T֧?Żkcġ.y c1%j%H7aߟii3r;v1o8$NGTRE=Cc,}<0Ǎxmg xX<)p`$Qˌ 8" H*>e^֎Ϝ5alSf.ы~D!!D.>iWH "l_:] 5!ݯn=&k7Uǩ~cK Z"Ϗ=y('Sg~\. D 'kӣs :,x B(׷=Y}XSLȊ"=-, Pn,sCIZK*9 c xƊ: b'=<*z95{_GFB9, zٱ@KgTQ+G9 z뎟czq^ݻn9lJ"Nw(olSqH +l/s`|3qKY( SJAֹ)𲗶NǞ-81!2 (1q;B[F>o~LPg^cu]tl7J.떘n޸_-Spl_?b"4s}ՒMh3] miݬgp|bU5Sjhg뛟ת W` ɑl#J)9)(4,>M 3лd|٭n29}v=7qv2[_Z]?c >V/",pgR_5x ngQJVO xE o:(8LxV^JW'9b>~ZJvMcKl?$M*qY=6wvx'c0ͨelp $Ȍ@m<#pG-۩Skig|O5>)PfDԱm8HXkQnUC 8Ce"c{Ʈ_)߳jvܙ[7~Gd`fG':UI"4 \J #pe=%cV׭L14V8<gH[=/&jUWC EI vj/qOj}ovޣVPg2"<h6yp}mV,-c@ ߷2Lh~@Xׁ+wPBL RYiyoUÿXO:w*kMǣ| 2"# h@Ѳ!0Υ0 ! n(Y8gɦԜ h 1 c`*(k8 `yТ WUeV^庤ْ\3qʙuFeWr=1I՛z ~Ng0VCX B1 sf/TЋ %۩,c2M~Ym"Z聓 TEU &= 9jbJvPt*.V{C5/+~`9/HO,nD/dogPAY`l6|J7z$}T|xC٩0-߱ҭfeM{e 2qL }QqI@ `Ul|cЃdܘ4ck`;XOf%mNt}k/p=rՙQ=R&xT 9ua>̅vxiԑZit#"l |﶑X@rÄ WrȭxQkckq^OՀcOqYC=tvuN+&qZH;+,g~`C(7ZA ^yv8|ȅg0IR!oBz)pm#8BM,\Ss|j93,23@g%@3ٟ>MoC2tU0b<_^Zkdʗ-/ʁAnW]F V('u?k<;P (8\H|:c@(= #c(WZղYeL ȝiGn/{s˓Yn}.]6L4SU˭ʵç3W Ƿ'HN 5ed{\E0bXAY&seH'e3.C=@A ةq}5x( DO@3{+k׮$bS؆c!bu^lM ^&!V<<'4nF-2jS~90FHI`Ѧm[1\0P?!ˍB Ɂ[✅ R(ǎQ$x! qZujYo=\D;)][yUk>8iM☰zŸevr!>w5_O$Q3ĜN-r`dR'RĒP(Hzs9tl9d}07EΘ7m,b73xyPAžsrb{V{e,[pa@T²+qWcxO;200ј6Ù354\ʍVsʅ:J80]>x=T8<]0AZ-C!BWC3#&: SM:KhSۆsʗuv8^t n_c7˩#malpNbV0T`ذ;d1IkYZVx tAZ࡯={~‹9bU=t lM|?@SEX0;-Ymȷls ;Bm~qF˵?W S!pꉽxb{_+:f&9z\:f@ZRVgALe=;2ݳ ̸aT3+1kǓ=eü5̘P!U˻hGSAy\ 9cq5/!\?7RY hb\7b`niaǕ F8r"\V]B`ivQV9}{Doc92`w_@ \E@'c%49tv38Cwph@]ٸO/>~3~?WT;! " 2tId1TR=ysm0"h}x!yrHyv!ߜ MGBM~zS <

#53Sh&μ7Y' 8Y+lGx\wڬ 7CU ۚ.7iO߃lQ:k#P˹!v^K loٍz-(9w޴ 60}\0g\x֝ {FP(%8~Q*>D[[3]>>:on-lW){v=lNΕ!b\1{2}øw4~z A$|芵~r4jklBw`hZ=W,@1IصS2*%?7w`ecn߼;7%8OlrLdۏ<YAuLf|j\yc>"7ϛ?`;}\G_C@a}Hp篟¯oێ$tH%On\NC['M}0AC\܇7i=3ѷUy%/a@ mUoJ b[wxbgw6-_oxkfl1Z,(o+5 4iD+7цW-c}ނT9{NGX0ۺ?Oه}Ǧ#=w\},@dޒf˰ri@ARz<_Čmoc/1Nt9:k˛['_=`-v80v9]SGHm:K|;UWV 2ӿsۋqrT $8V|1}XhF(ku6sU)޿16m%ofı0Z*dSFzhsG\Y=g;Ep?0W#/ y̑[_|?yd Ř5'Olكή:}#3vӤ? HLSpžTp͚`2evD = ~xS9;hc"a$( M ۅ:,um$D`V /J(:_e#tjӸN rO~o0cF߼ۺ<"Վ+%3wj}+UeF0"f{2$܇/Nj( g&F/_q/8p n04Th1VlUիRE2259,٤p7w|T>m~oQz7Af" $uSMr]-x ׇ( uwuT-z~N\yLH|@#qGQڛ'Yhp5DQz >4~KEG֧fy<*+ٱ J\|L;eW]GDt9H9Bʝ腕"s릋0 :澗đ{@[GjysTE1P(ʐC>he1PFǻ N_4J{7Ie;3#ȝ RF`]Nն*;Qo>*e^Pp}4z `|k'˵g:3_䍦د )!c`h먣QCZof[ -E;h.=Uvb'ʁ6픝^PR#3amm8*]WZIsO_sk2qiqA`ʎ??2& ym5F}/djSg=ZtUL Uq]``剽 \ם& 1zZ VPL '#dR'v?m@!B׺'59Cz[jA"p} Z8WPi|sl~5[sEݞY2k'[) w\# *y#6N0=#2,e Brz[ ZRI.>l{s1=s򹌂YA2U\izW}si~\`T#_iL88A&JZZJ̥hQ3ds"As"DD;\Qf 1ݸ{q%75Nfv 8:[MF``|d^\)RR)!(y{o, 9\Z1P5S,Hϖ$a3q ]د[B 3qيYO_"z'P/:o@yD2{>4c~)@T)J<)l~T'e,5lI!+XvsIϩ%{{:-Jw~3BclCzٹC7`Y9Gt@h˓ @O'@qr#3|L2#j𤒸|9$bPX' 9B3xA";`"a#l!Ԗ^oc7ڠgO/[9僚e_`hsbNAzF\3P:8AFdJ,ZE<d9"sdaL,6z VNΠrT0tUL2@Xe \5{a֮˅Ϛ \ttH8F 'p)XGB>gUt@v2Ǡ|rj*]e{kFCP 0(wж Vu깇Ov>A0 vf"ұ>4šʓ,6=}AgqYX]m5v3`Y\(|{9F@9NݺC6gPHc gPKLc؟su7Vvxj7?Q"Y nC/sYǧm_*{j'pg8ݞ9|F&w̭/ߑ,s.ck/x&k>?w8f}X8A. *,;0QLڏt>val*#`Gdd2;8tK?;"m|k[i$wa'+ Eq~I`jYetmSx̲IЂeq"}x|~$}ۻ2 rc"dDG'Ȏ)@)|1t]ПsEl]N7r@7Bݟ @D=n[#ͷ( onRvo'pQsx -2LNV$ޥEQo2ʧx^y\#;aYl8rQ[;8@r8T8M.ې7 H/۝#hH73;x8w䌠l sGu˻ wދyŭܺ1s=n?[%Z=+0N`N/&Hi -q\ou:;8i< ?_ KŒp65Nxۥ9#q-flyVP?[W<+D|Ƣ0wA[59D- MU?3 ΩM_D!/)O2]JofLP0@ mfAZJ]^!c|׫9Í?ᘋuy @7'!q3 Ж1OX<c<ɲ:Ǽ?qɺr=sP=Ql6NP h0 KEUJ] I[I頒[?y.wb:@~5aw?NPY~lxN`&o廭.fBĒ%#5}h rYBN@Nav?BR0{B淈 c԰{x'O\^7|O'нll*r>N3ҵ!:gM85~nL^e_\I al$9؈Ar;W _dVXu.~|KІpÎRON@bWP 抪 /܋$*ǷWt=gljg~ꔅym:kHˮpN㠐@Xn=n4a/z4O)|省 bϞµ}A0޸gf܌blPL ,g̰?Ot?k|AE>;>V z }v]$Tc}bk]p?ݏIsZYd z+?Dx-%3@`W_0;q㍏К_'/zv+x2\dhex3ŒKnV'9u M`-$X0 zonitTގ:7秗}vzPݜ,-m^epd lXFCFKlܜ0HW-a{ZQR"^S+ژ1C"H)cAABTB1&)`[LlFL(0c8C{<{(찌*k1j- v7YeOsepM{K$  0ukO]yZJ~oFD' ;N03N!a)0sfIg!L~;2EWE%Z(C\Db1\;;aP߼`ڎ:m( $^cA Q t/BFB] b58+&f)@cꧺfrE_(`4d3&ҡoXf=(hà4Jߪyf[63^FFC^P)hZF&=Vyc@0kچatW8@!Έ% g7Ϝ Q=֍>WKkt DvڰBu JT=&DBiPEȴtiw3(x:tugpIVPhLj|YfksbzZb6C`)cL焫DQ' :J\oHuj2͇)$#`R)6πvclF!L2 T@j}f&GyF4 JW47| [= bR*ݧ69ƴGvj _E$ܜL4r@F mE 7k -)Kt3"WXQU\qD7h® (u dQ1J 6dM9 Ϙ"7͈t PW%x v*.ݸ{]r3 HFt#StYmt&B^O9 9mi-J^ f)iFyl4:SZӌe 9,Pw;|kw9 Q,=CM&naje-;u㊭d mG:W@Zv'J]=鍏|'|LґBL@BBfQ2+L77aP ܸu.^Ċǰv {BdGZ&i(1]t^1=Ť Ud%A X!+"K*h+M"#!߼]p|8/?Pr3s +rAcLOO n`flyfM>6NXdUX旉հdO@d?'DEp)Rx'۲\kEdX#<ۗ D !1Yv6NG]mMg~BYnX2=0 ,f3ʒ,}ŽLO(q@,'l#la816`X\XŕcspM0mccKh5#it;TT{f]ZZvz>J`n &*jt7y>agVO?tvtO} ~yv٘gX`Zf}.% o0D%@JRjg>G@D9@8GZ@R##ժg8Df<ZzVnSu%xr@-CmQ8 fSyv$mҬēZLj櫭B nZ2|)rw_<M(7O&iy,DЈ ;}%=߉o<$$ D{ () 20mIY,?J4D<E J7H 2$70*#gP?őZS0{@յ~K-&[`k7 d0$jqTP2E2;[TlNQ1i@d%e K5 ` %&}c>?-~Qt8;\Am}|HcstD[M7X򱩫WRܑSvxiOY/BqsI@B"~Hz,(y*ePaҦih?K%L~I`Yɏn&<!H䎓i%5D@Q-@P0X TRY}hvg&ZheT]-r2,kfHYrޒF"a#FqbL@[!u7dpCǂ0W|QAw[tcF/zf2@;.{'eةl$?]禯yIj({m~,Pay;%Ze,g5xS5guoq_yrӓ)Fbg Mٛy.y+O@[|2[ä1 9@ DZW~|J#|t g)V1fE7{C7jrN>[}$MT FI F3sDBێ$k PqnHLLejK.[_F5ut܎MgbF[P^ϭXdcX&q&гJ,RX1+Q |LM^s#5;94{ë#x=qD)g_op !g){#Dɗe=H#,I%}ybMr0Ɲ"tCU:PsXr1TpA G詸VI-X<fL.E7hކ#c䈤fC2R&18HH3h<̬8S Xp\mp?qw/>9߿"M6z9 QڴXۥr$O)u=xYOO :\H>4`+J~AQy N'/ފ()SQ >{NEA{/i%CRtg-f$hƙCNN H" Yt.glf5aRݰѳ3,8v$.O_Ǐ}~OBhm7@ue/mщˋDʿGBaK^qXkm? ,7ZjSYfzԵCp1g8*0xS DO+GOŅ* hB)%Ѕ2-Gi(?EddLA]d<#"L;ẋ7u ]/>}㏒uWZ}̳-/8c?8( >C~A0aɘApniha~ih}o}(/}|;:jCwEWAsP9ʮeXJiaT!]Ǡ'Ay>af9bf@fFAAwjmɻQ% GȈKH|*J-36U[]9aGo|ǐ_6}~fVd1lta}|bC(E?Oڂ #SA L.H˸]= PFBfrN,XtkэW< ™3:fd4Ipeъk 9$$#Y2؜H*U~[H?8~pw<]]n_!7*[j[cegsRx@;a5CB,Ū: *,d?. b` O\F<㢯,ϞFWSeW_RO߁@$PVBmJ4&?\i *%z@!PHd ,I8~; FDs d=0l_w:Eu{ޚ"V""ðÓ7%Y/z2 $l*bkZZBC`f)B;"84p"l WfxLAJg$tY} KVr_ m]/fK>}Og:ˤtl-+i*:'5T (uePD5y|( J$ dK_p:\2FiL * \<'Jy՚C 4[mQT_IH~J6 -`7Q35`5Uv W2֔r`<pҒ!KrJ4A1U-U T cßf3G/)_fJ ZaEeʤmW{W[xqUSy=R\tJh\Y``C\6s8 Ń,l@#iZ@\yk>t^F;@66<ӖZP& } Z.:) fc]Ο[j LXikBdJRx aJ)NEL2AFM5n*aﰧ Mwh㯺 _%7;赛FS#ͳka%fNpC Gv L,DcD@2Uz9y<@9WfL.'jŒ258B;hCDr,IaUu\ED?Fu88p]ω$gqrR߹Cϡ֪'M3|QBF.:rkarvYn nKG)O M&Q1@wyڶk`l\9o**}|拜b9yvu_L:}|X/NpQL-ӱGQo) a9H{ clWj5Ah(%p<^b K[n; Q)*6 28m9_ `5L|##H ],랰~겏Cq)@{‘-!E} [Ffʱ#JAeta!r\<SX)'cjyÇD?iJQ jj')Ya, L߀`nZG&@DQ_FAR*ttn o8r΂- ,,7j"E;؞9Ds7?c11,pc {62\9o|u`q>@EJ4s< x<}tՒ,<pt TDusқa!--pT4͈Uy^9I'X]/H| >0 hHpT,cg@sCUsn.61>KvGBII " qa!DE>#\wV`('nWek socP88_@"Rg䆟 eYKl""&93e˞L@F"E$bnY*'RY ց(@~%M|m냇,{c XICb2A-:̳<;,|sphk*H+>N.w"bPEߣ$AƑ*0go}]UK5P4Y4珙Oسc:^4ꝦU,VS2>7{Vsl\s MUǎ%1H?Y@P/EHHe\0\h;Uͷ|?y8dpUCߊQT,P]:ITS4[czAL-6FteW$b9H@D"H2 0u %܅jpN2,B2pI>'{U/1\AQGrW]lr9Id Vs/Oζ1PMMS41ҮCphUG "Mh>97t~gu,*+hkcg0p꾾b]z&>)Cwqh/,Qnߤ_!-}[f[/ ֜oW(WܶN>Ͻ('j$/ڔNS-F#`xR!{Hd%CH$N -ﲿѲ18$[vN| o2lFs.'4P@MZ R5eRFҦw J1C EEHHAD8jEoZ7W%Z H~L]%Hf.98sۊS! s][yS2f ^D œ,D%U 3C%SU܁LD_7Rz7zfѷ% ^2kғJ 4ϯ9Y㊑=]$Gre2\hoZ 6>V[w (s]KN#OkjMZ7Yzv3B_考8s iJdtH5/d)7 C]uؚjߙ6fjD%˛DX(Lס;R1-NDؿڽud5ougG>8.{BŽOG\HSn&xHp.\8KQ^@/*\_zȳ+"좏S>{(Ќ!Ѵqbm>߳u5Ub9h)le13v CmjY KqWg!#713GkiK~W\\2 \~"!pd!JaTO3#Tj<]8 I8 1|FBS%kAMH$)B]dR+oNI+ʮ8J"RrSH(c{*䊋K89b|JNPehm둣rv2]pu2_5Qة[ _M*K>RJz)>ZHՉ[ gط)w\7_g8_{($"Fq5P T<zk[`/jMoCMQ9[(#',YdK=5Qm4bioj\HPCi1 h"V:"E[X*CwAj%[=lZZ~B ?tXR#Ic;BdY)2MlRPo.dR=eƍo|>OޕQWj=:5W eHuƺ/j}Gj-V8Vַ7[yQA =&G|Hӣ!hZa"™3,yIFoDo?ܵH!]dJ=7:̱cK)ާτxp dª< |P:Li`G(.L֯sq񅕎]م6̴ ӆje/>D"̜B)!GK SK_\7VywvM~m@%f20\8؍4\xϘuuI'=}z] Eng1QB PAjNɐ΁x\f&r瘞a,dZ/EvE,/:X 9 h©S;}oZ`YNl`)ib -d#{9_Q3 p "UBE%{}Lal@Mv\P^$SH+ݯ AH~3c!Oԏ ߘl;$7ҋk7 JQWdh9X1Wk(LJYjuR}:M BRw%9B)hm}xTwP&0*BHODI3hhѯ J 0f+p}s%;vNa<c0@O Az[/6zػ8tdĠ9x4:NY5"Ĉkt%J@#/i-;7_/_~FppZ3w3dM [=;B{V Z7^hRaЈ8HLpKk9G>^C&`D;KrɪP-xVy[e]`Vh!|+Aqصx˔/<sU:_Fv#r}AYl`%GܸN:(ou{CެFd'@YׁCJjiFs=- Hi\AtLg>NX\8rE$t`Ŧ[whè঵JW_V36DO60Hk6)w6<f>V`FJ[8vIn<:<dʽHRuTfX2DK{*.+#Q=;lV}x` WcW_M۸+E"? *jwhMc*uӖhƼ*HjG97 ?6#aҝ8rC͔dLZsc̊B6S=&zZqr7NKJ#g[LV[,`$Y<abf3"$j<" O!N,/߫ U#naɟ&!̦Bc_Ӕ0 u.k/ᑯK*؀EEwx"c"I$SEaI k8t .-"5 (yApvd :ަ @N.b 9JN@7zs8t"(ATT\gvJXy F<-qu@WL%8/hXEjH-6U8 v P30lWq3up|>|/ؽ?.+ȯg=]1 ZJNdX3|||1S֡ax>X  D07Xv֘:TA5iI~+K *K&@kt!ԁǀG S|_EA`1)w_GcR"[j"ψRg RrB\=c%3N.F_?}0.V;zk;PG5#d!d*'6[yI x :_eב7  sp p=VT֨ig%zumdXs%)يӝ >ᦫjl =tR;fv:@IF_86ɰ`·H<8uCep,la=~f"C΀D>)z_r^WE0}};$^zO3t߇%Vwn̔|nAˮJh>i;eebr!sʵ2ʵ2R #n_wO;;hƴkӅgC~_n Piـ(\LcEfdze;V`~6=cz0.(B#B9s|K]i/nii̙ yr0Tk l,I<.,GGj]oP. dfF{LٍB͕nXݧj^W*Yg /讔Wz>Y(Sfp5_R%C`6elXgk"<2k`7f쑜IBvߢ.b~Ih2,4S6!lUp CB~"WRVB @8c-^q'/s`1kM`}@ǀ+G{2KNv:mQ .GA֌Lm-*s3;ƊulCKњ+ UIX@U߆ ~dŕTcr&dqfG}%SSm{`Оɰmd D"Kk{£Ym[(4tab&i(7P!C]k^h=B9vU밄1}:}G3Z*}HL d;㓜}b0j< 'SΉ 8J :XR?kBԛf5'9fv8Sotf-&ڸes2O(uw+FyW`TTS .?Ȥe˶ 'e'30)9+ڎ_ *%]_p5`0Uk){Zh}Mňz P{roJ$PKط~=2,f*lyO9fڱ>*Pw*tk^2tU|_) @/l?z 4L /`b:Tz6%eJ^ D}FKx6-K=cP c0IF9Lj|T)8`~&h#p"c&VP8.ꫪ ,g&YG'1d}xש&c6&s׌aV̈́DO!G+Q[5|[v<AgzLtxp႑a5+`⛽Va:e JhE8J g͕z'r@D! º.y>`!v3I$dʇIٯȶ J.X_9V &Gy0n w 3KB5y{k]x-l",I=;>\>.#Gdm iӫ/ йNrMss5y}W ~a7Ȁo}.KV3!،/ ACx`ý2H DdghU-3b()&;#%%v̆ xHrwJf z}w :bg=`Ɩ>Ŷnߌ^:FaSo7vRkZ cn!Y=F k:07 ' eհr:SHtP6|]BL!LW^܍cx(/:Vg:J_BƶqضM֌`f,Y9*aMV2CӔv/MAދ!g($}y`ʹYLOlN/sa>r?%{m %n=,sҐcGhaۃܜc#~Db>lGTH-O tMi2Vqi[JoOB<); l!`!H)yW׀Et:Warv|ϧqםmZ-cݸ~zϴq׃=E Jk(->^ V Sɾئxb-ىdTӌQ`åUo+ j{1$k'b[2[(±sM|Β&0gC"!ލuhG0e*MHXېGr@pȎs #41!é#EѼ b> ! `(}}A᭬?fi*ߟ:` U H$uN@s:_rp5qK/܊^a8}anR`&jfuQL&I㼐u^D[oF#eˮ|}k ?_çOS7F{~7+(UK ";MJk+s!X@(4@,+eUC$^eĮ1pֹÊYggrPs-}(Me:T3 =bN~{| dW;\atTbk>0; o ?ESI>O9+=MQ *`jra>cٻ}g׸>??,d|pvƷUJuADVvFkKu/113Jj'="(uҹ| 81*.* 9o%ߴ< 5CߪbIa ^f^\ϧiC+*M̒va6]eNw/!ivvoW"<:  +'PiDyN 'ZAk6c>5 G2ڨ `U4X/` g0YSª RhHTCX}v@6"eם;yӓx;v o`}?UFU=î7\F(^ڻ *ޅE<eX̣1/'OWo`%s.<7n)ϭs_gxJ B?3O裭]G'XpEat#8LH3jU^.(diC`Wgۏmz#MCl!gQ{5pOux7ͺV hTw|u4`mc9; KmhA-hyTCV׍P{EY$Vb,x19hu9f~ h&3z,׶b X)UT-`t[p`rMa[8k' M+chB90ow\79_o/ ^ sXZi |@pqPvCbF!@jA{1rFrhok8@}7@Wq=]l\H2#p v`+޺r0y}~A㯵l U fQ޸ BK}`R,^FuӹfzR lSua;݇+Ulӫ:ߤcmPCCG{ Z33 Mp\` xPޮ'v` ;1_)1̜iDpCwrg!۴y0^}\|QJhKht +&#Ko :)nc GwJK?gӊ/O;3_jbh2eXגc֕^y+ ֒?qJ,%2W$z vp…cmFr#]ڎ[~膗 Iӑ)6Y&?gé`/#8N*3T0c_}A;qf ߷?peovIr]_3֏Vq?13*8ٹHW`0=w1>Rchk+>ö\}/o&w>k~P+i@\%!r}(HH7W: B]g==7oeMq=$c9guۯ>yvVJ6$ 5 T(@Qԙ6`]/ 7h*^`aIhqp9jyߞXܲPnVX_Y8 uL؎::)ڌ0͝[C(kEӍLRxGc3ͦ{?|O_ޏ?kߞFZ< 98FQ(le]w1_.Uźu><2yG7Aˈ[Wjꃚi [+g@9VZ[-Yɯ>F),}cW MaziY}dl 1 l+cUme< 6}[_A 05}%U3 OԱ&N9S gc پ#yݎ^6^x؄>yZg⅛}mc(1:eLa%p-ǟ_A^;5Py y Bww}} V+uBƏ٧nz{[$O0"+%8a~뮁O%;2C: 'pd~3R-6zbxPtHsQNOBA;lEU^[3XB\~\6a ߎس jm3W6XVtWPR.[lݔÉ6NM/ \YY**eC3T|:,sܲTXZmF+֍?x K3-}Q S-LAFXodGLL:orƔ"X\~;93 C޺{+yqxs*yu(h/DFJ$Ook7 cWgp߉)C2{Z붭7fJTQVʢϾb͵p~3Mic|ct݋ v%}ъH@'Z dtN2 qBɹ!&fCL}tcoq>Bu&_A<8zj*NGARCLY !YWϣ޾z{$|ņ, 2XbH>Yc,5y?gs(dj?,,Hn7؄.[ xlr=5x9k6 U_xvwW<7c 7nޮ=:NN"H*5sp 'EtQB3\} I(T&.6=ajEklU*6YG|^x3 p>h1~zC#ݨ~a3Ci$JoH" _/x";o*Y[_P""~T&m 1vl쪭=H;/xGB3'Vx E'gqJzjx CزTIGMrD'"4g]BTЫ5NBAVP[\@m{)S_$ww?ZU3 DuV3mO$M9UH*2QyTBXTQVPK:3P ۺc6`B@$0߹?vrʠl`+F/s^cm.4kx~BOOXYiab<'*e3!z0uYlAa0w_e@af;ҹ$zJ.[5_$ީ8;93ƓObXYH@` H 0ݜU rI EP(W(KKewWutOl (31=(ـLLqP {~u^op f!jO!pUЬ~ ۚ_"DC&Md_0I=XiRْa*;:谴@A9,C][Ɛk2 AT)\)=x I'2ȠmKX?Gml苞A AYJ=ڣޜ,phc %u7 p~|ַ[=g6QQƬ)Sy4d!Vc|wcڱn%q/M\i9/" X4{,NCaɚq1#SE9\A/P%O2/_# q1:]} NB ۦ:;Խ3g? aZȭ"AT72E#?݀hACt} 8pfɶN"id 7ϔR V<,4{ckgUbo Dldtooǩs)uWj傡1Z Ǫ vL'(Ot@Sx~aEkjY1P%9 Q@#"˳^v /(6Ba{kH)#rt#K bqRGPz.<ϋr +yp)H,KNmz 07kS':k0&;5b`Sy1ecE@ble)I!{"?Gdl)Cs,[' wus|]xC5)ne,jv[#6bD{ S2Q̣7(%H#PM4R/Ol!HqxRɃWQ*p\XЁs):BTq'̂C0]ޢ'(Ol%PBE{ L+kR/c֗9\$7`Z&U ڢQw:`x_<;>6맾4:_{Oa.\Qk=zeU>4FyߕZHcjh-c]ǁ{K\߅{p<;䑏d0sߕؕ٦L*3@oť/Vq"6-CyS 3 MОFҌp/FH|ŘFJKP8yƹy,ncr 02x]YW uOmlwB.T#$p` k5 =䬂I8$6H^\"s.eYDN>r_gfvYւ-r{ kSV@ AVk=Z6/kEcx²!J6{:}IV3ݍFpaU{OkAu_+@N-i(m%^haY#юo6ps\\^8n|ŘA &jW`7utbh{ lQ^Cb0e/z 6 s~?+P{:>:Z'JeKʴ" ;@}NЉG`<@wzxr@k q9\DžŘp〻qb=<Ù2laHШ(=%hIߚ,j.+t, s ~#@SJivv;@^40+0WuؕAH;?pk^*}}qD)t:EYşKZ Djӆ=(n vֹE|ᨫ!e>҆9:p<u+p1Y.;+ oT"t0uF)Qȥv%3(Ƣ@;t#@&?"Av VcyyZbh_En{)eW&/1x UK/ex.@އ_CYG`aQUx9@AYhv!6ry59y-NVLE@hڂu(KfuB.JF2a2DSXm58ڤY_s#5q)+wDaH DD a{K9'b,@ m80ή 7VE.]^ 0k oߋf58KVTa7~M 7<Ns *]yv'"4P@,΄,ܲxMi)m |Q VYE:u82Fq}ez}zK|x=' 4#R[)XQb/F `qX\!F Ǐ\ͯ]iZ"!\Y4U@GukŪ|_7 of3Y,B+Argi,؁W_6%c>+ ڳ<= W[Q9x/eL؂.EZmq4zCo܀e]2v+exb05xu x&ء! ,&GI)s2,@w1oɳ,X_ag+=ŝSQA3}vsET(fDPZ6ǣw /%W_5}cga!@%3e80MkD< 0QT?Y-#PpEQTwZ*:v.b" l'{}]?wȬSUe&=Lͨ-ɆLo6ST`W3:Xw*~?Nl{,yjgomVj$!'uJgsw| * >V[ D϶YŘ8Pƙ<`@Ǯ.]9[d@C3 q["u *Y9HAIJ >I3r=W'9,w' /g#q^:Vd+ѨE~L"{!މ'۳>'п3v;QY3kIw?:a1FV Cm"8>)Z7+[H&2ԙPlmCn}^Q}623@p؛CڬS!"D/^ z0.Πiz;:_R|^4ī "C|m<03'E~ ` >5`놯ޕ$oTED7F2zxφǮ7g'y0f́˻?ʄxeX rkr-梨nYfcxc>;:6auMP-pv̜{_A.5!^gVqURK?j%h !جDԂdC+ILBlo8}ylUW+l$poereJqνߩ^&hD80,mY?"N[+-xPZȶ*m0X갱(؛ bdD+"x9QZ#Tm<z*>? >~RWa` (.8|O)(phqC5@ir'j0t92X4N̶gB@r[i" :u{ffKG"Ы̒VH~]ԂO-GlafO0jXA5LM YKE#`G _|yMћhnf±.WLvx`oj $ȍ܃ & ǟF :CG`=x1ieoWep(jbP" ߀??p$> iop]S:1L^Ae盍FI2x{}Wr8, aIRy@GxkKOAx[_i>}PCS/* qx/O"Aܿ By*(f_<3g1;.ko821^`Kv N6fh!ΟN'Ѩc/AܯpsvM!jadJg;)f8p+9cdD8FIzlsnB)i&{;έ|81BEp( M#HWRހZ$ٙE;OiO@sLxo%Lop{C{{{gfh%:bq#9E'1eknv:{Caݺ<y\~ 0σh0,pD݅SϿN0qi{->3ͿB!-~IENDB`ukui-screensaver/src/assets/show-password.png0000664000175000017500000000110615172041035020406 0ustar fengfengPNG  IHDRĴl; IDAT8ˋq#a( Q.)?@԰U{\"epye1&e!. Pr f=Nӹ|y~K]Zֈh4Zj Ef"bBD\?z]l~l׸V;xTIhF<Wф \F<8TG1X4h6Eb̓0'U?3_nC\́~ޣRx ډYE~38x<]q3Vefoŷ#b"F|O2/GمKW籪X?-⫑* "b/7Ef.̥ލ؇򽃲3QHJX/J.X.sqg3TujjeDOf +2 .a&VgAߐ4=؁~ ba8_Âk}#+Pf满j/ gIENDB`ukui-screensaver/src/assets/logout.png0000664000175000017500000000106215172041035017100 0ustar fengfengPNG  IHDR00W pHYs  ~IDAThZq0 }M7#AG`Fvlܑ3ϲ,04#q}hQh"QO虆Vvt;!:]cvTEh̵m+fٔлǬV6HR |"Wy^2T;tyWB[sI/f4@ь}/-+OS,XS;l?Xt3P7a'}Ǐ)<,;f@Q3# {܉]wv=RĿ?6|24zGG 6p gl 6g%T=ކ$|Œ`Y=W#3]hu_ mtqc !vS XHA㻤Kh{c Kh-{s7u{]|C{$= &~cW (bqmmb5Z?]nEIÔ WIENDB`ukui-screensaver/src/assets/unlock-button.png0000664000175000017500000000103215172041035020370 0ustar fengfengPNG  IHDRJLIDATH?LQ?m*t"2i\k8H;1`h\& {FJ ʟjHUPk~w޻~ݽ~R7"t n7/--<ܨjnL9SVDkm z4+@! feM JpG.^8ck_ Y)sx|o6>hvM}m KivFm aP9$!yVŠ]94(- l<9nCii 3gg"qhMFW D?:0D &yJ{ k=HҊw-^UBzMU6խ-H>m,TL&p?4;<Wue Q x7t\~ ?_;0fvN5IENDB`ukui-screensaver/src/assets/iconFace.png0000664000175000017500000002657315172041035017314 0ustar fengfengPNG  IHDRx pHYs%%IR$ IDATxOl׹sL0vb% T\v"H\)"R,Ra RVmz+!7)TfU4VFj+ed N`Ǯ\s xΟG&ywyR)%KJ]vqB6u!D{LfEuݒhZY(JŘ@HH)Kv I+OJIA$ KJ ғ{J bY%aE) "P̟F򦊰Rku$@ Ô呌R`YPJ{~$@ ɾ '|>VJJF."S̓~ l &2z Xl#_//΁t2p  "-t`-H:9_ P@Hg(cUi؀RADH)Mgmn$FX2?J, "g^gCh^3Mx1"EȘ~sv@tJg'e~ Q$ f6sH4X%o*$h FF"`SfpNnG!@UlfAD 9?LGޢ6 ZA0 ٟBt>@@ BN  Pf_A[0D )e?0ˀ /@W@8H@@X 礔}fw?~*sH<%0{YV3e|C H<&?1bgH<`KG NsfǑ8~)GTF v'EƗl3K;a8tO]kM5. p2c?خhfn$cAr$bAb$R !(L+LXFJ{CޙWJdZA` '%K9%` v;)@$N @h P4J @Jll` HRz?<$̜z?<+/ Y$ 2w17H)M)fČ~06& F&f4`Ą &fl @ @LH"ƛ rQ0$Ċc"Rza^3J9u$yM5S5h @ 4# h{d]N]8+/k`cH@k_@`u`@'4ABP'hs9?8vv{8"X @'  *H!wH* (c^Kx$ײ>x>sCP /@JϋXp)d `wYzl3m"w Hk1 A~\ !7 g&,@Je,zMlJP 9A#z} &0=9fGH'Hf61{'4 !Ev)e?;M51[&Rn!דU%^208vZp)uS6wARJɦ?@:LlW T''@"z}M`7B@"|E`6`6.؋XKg_;H)3>{&R^9?p\.kL&a۶m{]...N-,,LݺukjjjEXSB\UJ:' ]\ 95,--Mܽ{O?K/tGJo\&G3~SN冇z'=cg/w/~W}v) . HʬRjv200Xp)@t?z3O<đ[+_|'$^ (QKǏ nt6z/ą~ RGt1Ȳ>OǏc=vؖ~T27.;w]ԜRjƥT`^˸˂K"7󍌌fCՙ yZtc9ҿ^G/gΜyep lO.vگrOMMM~׮]vs HU[?KR>?\wQtX1cgB0:L,^ s/㡗yh X`[Y*cuѽ8:K.w 1 J]u gǾ8Ⱥ6&+ߠ_:tWCpҊRjҦ j]JO+c 1SM5 G8o8:+TȲ. ۅ$1Κ*lc?GIkb^lyZp @Mz?^zn>p1/@JK?N稟 }`Rj`^kծH`=&?s,qM LM^ /=g;K[jL泗~ Mgg䞥V *qi?l7ݥM\H%Fo=Ħ?9s8ji5J p?63=zNCX- &/077>?n߾ǾCXmR)Qؑ¿.甥X.@&aWN^L[`L-]R&\rrppɳ{}+qJo%V6McsL X+*@ba3 >VbUD*ւRz{'Jq9獁X&I,9섉b&X{`wX?ev=Cca񶶶&*\b-[>VQ(SJ8s?B@Vm&"kXý˹H){y/D lLL\\AѣGavP{%F70O拇pOrqh L 9h'X"&톇bP ,y{+$k{4F<?Too/Qi,lFT Ez$0ʀ?O󄇚Gbm$ ٘? K?Ow{j3`T6z===&8"U@?6=GDs[Npl"pr%ԃ{h9ؾ};m^Q8΀VpB&Gu^CZ&QG]W4H);x/0)R)(eVǼJ/(KJ 1iR[Y`t5J(`fPMml6j p .9uǺ4| pNA hI?vi867+5| @˯h(F7PN AةW7RκwEf^$#@J] pzpq]&fo @7w\:½M(inGu^&ᴩk 7},;}'* >J=ᅥ fp#zݻw/3{4vS@0fggqGM#Hk)ew 駟 5q#m&@)e;X]]okkaB^Xe˖ <2q6{󄇊7࡚1|fpW_} J7࡚1j`XW>cFQ !D;󣝝CL,JQJUX9/}2(=U>^<{kl @J.䎀߶m!&ݻd^~ IW?611q^@*j>)h@)fjN ƍ<{ӫ%Ν;.6bL߰`@f\x5q@0* R {vΞ=Vcl*lDP8!@6VA9s e0І^]^а #PE?Ct$#8Ag6x B@\rrppɳ{%@)K~>W^y~s8q@G-'=zCnۏ>过>>}^}R sرWf1z.>@%P2G}Ӣ'\R+8<ڵkr?aX555]v:q<8 PYF V>bHܳ458(ĺ=4y_f?{ } ]/p?K/t9q*xp<ëǏ8pu~.]? }* Jb]ٟTPMA_Kл';a?R/ ~,I}x/А_Ju" hC؁4e-4$ ]5qǜ4m-SZwc2Xh])c 4O=h=z9l-^ H}FKhh1cK{_ k1rc DW GWSJ}&d 1^`Iyz&@ &###>n߾GƸ1z1tHs`gttг>[d[^^Z]xdrr,]=F [h/(w۶mΜ9GD`I/HЩSr?ضmۡ޽{Ϝ9ӧ, 4: !z ϟ?22r,a ٳg" n!DKW?{p[[[Oӡݸq¹s%Vȓ?244t]柘@^:$tUѣ<G:;;\,ݶ믿>}Z$+t20<<ԓO>Loo![ ty~~W_}g} k /ܣ7>ӇOB޽{yvv~zNZ /<;r]۷oe2\wwwNF{ԭ[Ѥ e@ D@H " @$ D@H " @$ &9A'K ,1eI'EY @TZK)x¡c?TJx# aX7*%l k1 t3FGGr]۷o յYD܊|>Yk/޺ukjjjEXR)%B]lsԩS?CLl4q/}v)& ]f 6Ol6֭[w2)pO.]"SJMJAZΟ?;BЇJ_zL4ҢR`AǏ|?Fi!K_;xL<VPJ]N$axxʕ+'WWW8:w@w y+B~fq?ro={c>-}ի+34ѬRjVKYFq|}$D/|k1LɌRjN["w ? MGwEgwB 7"2?==3cC@K~ryh^裏^P:(*$uWUFͺvگrO@ ZSSSߵkׯV hnٛG @$ÆNukNL@ f#ݣ쥿l D'J)TNҗna]fW1WLR+p~@w ֊T )Ȅ>r!mA)u>r!NVC' MP֭[w>owttTȅD%~mZXf4@ &} }8R3o,>zx7>w\י`Ԍ% ^ lÏ] 1&)[G]r ٽ{t{M'VWW)(wlr)Z%<c ,;L6T1zM+{6y f8, } lo '`]M`۷s68XO500@68l0brS qM /i XpoP t,n%)j:7CSeвBb@P 5-V8W~WZR)`H)Ƒ R_>*R+CV. @Z-'JyZ-a8U?B !IDATHD$17vDP[$17@)k]ܖEU,bmd U$J&uf@i40h @Wu@P rԢ",8*,w*%ƒp$HDz\\m.6!-ƝP9E'T#4mXYliXӴ xl>?]ThX3d0=x"򶿕$QT["-IL CԖo%IUU6XL, @M=+*U%#MPQO" v3'&T%Ok!~򻜞ػUF@ =o&EF%5TyP?;nݺ5b.b@6:F>coC*iVĥR*O.Bv* ںu}zspGGǿ1UJY!8w! X1105&JT? } |߄T̚TJ{_H@VWWzB tb-[> +(}i/qqƅ)"Y \J0Ν;nc߂,$+ev!ޛ~#}P\x 1zjZ׳&ؚIuGdhΞ=Vc߀ĥk*%RA!DW?@xO܊RjҦ  ! @}*/_>o߾mmm=` _Stƥ vծ.X{wF?aA.$?U:W4ƩSr'N8I y:7>}zO q24N?{0K@|tƍΝ;.7@Ov !,}yGںuNh;w|>R߸z'6b20<: HZ@!E$8)e"Ň{VhDHJ7]n>D$,@diHb B& ME && )h@V +~H}cF3 56o 1"HR @R3_H1Ac~ "H'`v RvsB́BW0;WB́f4LJ/z)#, /L -B`) x &?KX Ls[b`?-8H刟H ̘jKlW4O ̔HaN $+` w chR?七Al`69Ql`69q!C kH<@5@x GO!qR@9B)N ;=F9)e@A*u9F_$0]BLc`S G6 M~!i'DzSoxHŲ!ļY e,:?`ל`?5biK3<@ L"0 eti?#@Uf`?$6&l$pXF> fN Vʛ'~v.$h`?BDQ Х~4-c (6!$LY"y'#2$ !@h1 4 A.lCbH(O@'<ѥ;&𳾏Đ 5fy PH RGUi ULU ^xF/J*1ͅ:%8`,zRd8N˭yTɂH m)8KJ1' i'RjчH S0C(_| f^L] P@-+eAg$Y*6@7  $ txh/>"ʬtdy!j0 ;LBɦB@d,qhA*J%g{1$@LA;ՂM)C'{u$@Rt=P#tObD(\t?DizY KAXNJ]v J D8@^d({r_ZO`!jH{{IENDB`ukui-screensaver/src/assets/fengmian.png0000664000175000017500000001472315172041035017363 0ustar fengfengPNG  IHDRPPIDATx]vYu Z5?Oe;3u"胠!@-fo"!YvZgd朳97/|7H' [֌%S-!X_6-%5#+h޲A?_ó^*ľ[# kw4&Ѵ { 0 ?ӴWoÒ}w ~Ò׋wOf)[4>3رgKg3y:رeіƆI׆I?MGKp Ԛk ^#}׾G~Dfo>-<l$oi:?+{ ަ9 2҄'?BDik[pW]:r+'n7hZñ ol{¸'0E;شTIy9xԿg0%aeHtֿ/h`vɏSJ-J,4-{%0fl 1PW8#t$+۾ o^#ZDrzܶCYC~"[AI!5?k|߾GL 2/Hu> ҝ/Hv5>!VAtp )bܾ%CKJ*5Jxolyl5 t ||9&)_—i ^^oDИw3QmkV·f Dװ=|~gΛ7oĮʷm۸y@obcqyw tDi"\}ؒzE0rG`W-#B9IXcĖ߼鍆-:_—Z^X |!|!1 D+E g#mlSpr}6n,o+9 _o~B-`v Z8>I^+w &3N aN@waUaϝOVnі) X/FFKBk,w]IGpe//!Xox%~xgdm#O _FXg7_I'BBhfpp.~;Xp`Lf#_ _E Z͎!XK/5|59Oە%7u&}^R+nCCwҸEz5ǖ%_3u2Qz@'sE/_=UDh!6IhIkK{ulikع;p/c˚a4Us$M2ėT 'gAo_EL>~;;wKb'$aӒ[]A 6n6n.W)tt^ʝs+#]G:.&Nn)hHT2 !Slw;tư x bӚ {"H0 4H@w^ eAE۲]WTH\K63aI(ך l@6}o`Rcʼn{j m8U apQ%gϰcωp/,afg\9_Ž# )Wh_8S&`YeɯhWmW!ebrpw2]8wGp蓃hv^nNO[S虒S+Ɣ )H=::)eMcӜB"&-/)) c=d ^ Gcch~=J,Òp'- &zL؈q(G0'FkY828TDxc)J][]VV}9 $W JFE5$ϝ텧 S+d$9@_{2q !X,R*[39ShȪ?G{v:! HXH3iwړ:`c nSwg)d raB2$=<7qb []pBYbݔ}8/@2"* ;_`%́tZJ"ĕmr:T%5#O ,BDQ5}~-K~Ӛ7I- FƢkpylrr.r,0#nPM"Τ\Ƭ9lҰg06f< NHcVpoe|g$SNa{Z..cYp~<6jc{25V!|NCK2I*Ҁ9~2ɍ""Fp,:68Udmx0+{`%q l"1H""HsݶMTT!n-e_Dp$O'ٓd:b'Sv^4)2҇3LJd HTn0$B CQh+LUD޲ۅti5 gJD7)Y~|,LQiuo7%ў Մ $k;?5{RLdc=J;?;trbSS FN@2Ғ5#'L X1TZ?zRԻb6LݶgdaROa UAF8Aj #u,y;6``-{hd I45 ྻ$;m65+zW9-HFf,Yޒ0> %"B5PqQrQeU{y6+{!a,0ٲf΍aԧe)p )DH9܉n_i#y6}w%gGz˰$fBZm>tW^ơ;ocJaV/őks} >_ -$wJyp>Zv y$׉.vkf:kϙy2y42R=8;/zqE#*v2TWoÜx1%? bݔ';>U=lh K,hJr30kX̝ufpJLm4wKh۶6 4FI۰d`w)(PviA:~“T~eyp$&W*ޓl&2vN*7W0JŪ]: 2\)ֆU†55R%`˖ *Rǁ7C̈#_tD5tO~͉veK"ݥ ύYXuӜq@@B&ڹ/5p'&Ö-dzźΙYy1]/zj X)|tGEty,Yk+\|fЙI@9/<H5:wAgUY!Ѧކ9 On{q1Շ^:Ƒ4yἜٲT |\ɤGἙbͩ{eu[yhwUv@ qh6q;?1H,@)H)o32ӻ0daɝ˝K UIKk{ J~י85PC'ҍ +aF8aI{0E0\U8شa`˖]]hb4pG{{lZR]YʅeIW9D'Iz ԧʌ$ȣٸ6bݕ.Gw` V@Ej0E0ǚ0"5 }'sܢ1UuC8+;8/]%y)BMhO֒I͙y=+K*|oi{!Pnj u˿{UBJs̪%YG޲`rлN΋qoi&H[챍ɏz/`|F P-!2'[2Ekb(GJӪey'71{JEjΎa״5_Wδv7Xzrc8ծ%Ħ),e*2E|wmI~m'{2 g*H7$Z΍AE:زޕ=q))H[p{%t guckl[ȌɔBgWKpf :S] @R|~& lY䍆1-NNUN -OӮ ^ w$7IENDB`ukui-screensaver/src/assets/lockscreen.svg0000775000175000017500000000073415172041035017742 0ustar fengfengukui-screensaver/src/assets/ukui-loginopt-qrcode.svg0000664000175000017500000000203415172041035021663 0ustar fengfengukui-screensaver/src/assets/selected.svg0000664000175000017500000000046615172041035017401 0ustar fengfengukui-screensaver/src/assets/ukui-loginopt-lose.svg0000664000175000017500000000232615172041035021354 0ustar fengfengukui-screensaver/src/assets/password-login.png0000664000175000017500000000027415172041035020543 0ustar fengfengPNG  IHDRw=IDATHA 0 'VO(N~1>4$6)?@ j=3PYq%8SVz%Kr;uEzE߯yC]g{ 0wkjkr/mcU;IENDB`ukui-screensaver/src/assets/waiting.png0000664000175000017500000000064115172041035017233 0ustar fengfengPNG  IHDR((mhIDATXѕ E)(,b v`t~H`P xwN~+B$H@d9rgcs !&8C =6X{)pdhJύ盀07_|1-s5.?598N{bq XV@`21J/@ Hjx~kBId͘mKsɻ3a*fi{|m}.M{{&8R3!)Un. 5-CmƎ(||zyu~]%xܘs9e]35x cͳOPVHn]IENDB`ukui-screensaver/src/assets/iconFace.svg0000664000175000017500000000147615172041035017322 0ustar fengfengavatar_128ukui-screensaver/src/assets/ukui-loginopt-fingervein.svg0000664000175000017500000000255515172041035022552 0ustar fengfengukui-screensaver/src/assets/ukui-qrcode-null.svg0000664000175000017500000000344315172041035021007 0ustar fengfengukui-screensaver/src/assets/intel/0000775000175000017500000000000015172041035016175 5ustar fengfengukui-screensaver/src/assets/intel/more.png0000664000175000017500000000112415172041035017643 0ustar fengfengPNG  IHDR@(NIDAThC*Q巈D) \)$ xw.< wpAQD񷴴G9ftFgf}5u7kZ[PN ڀhȤo3<w"rT@xyIU[AZ% 8{N4Pf`/H C.SyMدT B,"ו>'#tp 4ʎHjНsomdCo`n"r݆7^301~y u39~/A (CZ \7$'׎o`20T-9s&"'XmqFs}[>`)o7`h&`Xtu5BvVsӚh櫔elkZe57-Pca%\UVsx/&X'B7vaCaks[q^oI֗EQ~ŃSy)Tn8/:1hh)W Q+2e $Uh+5 kIENDB`ukui-screensaver/src/assets/intel/shutdown.png0000664000175000017500000000205115172041035020554 0ustar fengfengPNG  IHDR((mIDATXGK]S蘁xDBI*a]600QhsHڒ"Q BhCxK{~"޳{u<_|8 cuZ/YGHzFI_F*a3'JI`D 86AFeyX)k"`DRX\;'zK$E {ϴ"X#)k67f=\ҕI7 C$}5؊u%GX8x?56KZ=VNDܐ6J ,E:ǔ>򙏟{##d'E/#2&L?c%}R`DܕgvHZ7-`m.HwK37Elw `Dk=D;%FIkSJ]d=tրJzmN_&9vzwϿ:F.":frHgK%0ZpkJݾn [-;S+_'4 D`DvG@5 }-n~K'"%-L{ԺA_nX·VV7*3֤mϝĖ`Dܚg959f?[;4fZ}5|Y}cgbqrC<Wz8Wgfplx+^܅l#½97 ܥ~^LgpSlƮ[_瑱d!yo7r̲tmJmU"8ԇJcb݀<ڞj, s^9_ef 8!:O8-IENDB`ukui-screensaver/src/assets/intel/slide_unlock.png0000664000175000017500000000063015172041035021355 0ustar fengfengPNG  IHDR szz_IDATXG픽+aB,LQY RMd"' N2Yٌ۱ٰEu{szߓ:=}&KM'b1F ]eIE`rY7u ^c`[Uf6L@/Бw Z0Ufk:0.?Guh-Hr_0V`i];n.!dfcd!M 86%y/%[KyB髙K20skߵ>`3OrؓtǴF"mlHe< fx J̩Fkhf ҬH\@EF@L &D]!μ+IENDB`ukui-screensaver/src/assets/intel/keyboard.png0000775000175000017500000000045515172041035020512 0ustar fengfengPNG  IHDRw=IDATHKmB1 ν0G&&ݠZ(AO<[?;vDP}f|Ѐ )3AwA~:KI50ڒfvj2`&iSxLr2` O|/𞦓 kI> $Ӛ)x9 >IF `J{F'Iإ0x0m^QQDZO9I.=Abfwr.i:Z}A|ÌwxݱSLR񛇌wYe#dOvުͲ>84h IENDB`ukui-screensaver/src/assets/intel/message.png0000664000175000017500000000107515172041035020332 0ustar fengfengPNG  IHDRw=IDATHKMhAӤɶ&545ЃX*4O*͓ୠQPx&zQ]K JC!UJHlҏmMv99of[naࡀI)S`[kqMK\BuؗEWHcf-7e)VXM뢷eN\b='eH`9S [Hp6Z~edTͥ/Ըv))`9[Mt~FBn\;6d픦r336`G-ޤLWu՘:s6/KMS:"}b jp}-q|fOaNw/u.e=h==xPw3+̮,LUj\?7:v̶}e-NdIENDB`ukui-screensaver/src/assets/intel/qr-reload.png0000664000175000017500000004353715172041035020605 0ustar fengfengPNG  IHDR dsBITOtEXtSoftwaregnome-screenshot> IDATx}6Umlcxc1a@qJR?"U E* p & PƐxg]K|ӺjIVU;UI]c=Qs/1&59ƨWB"ISPJJ4DJW.ՊqxJ{9M/!TW]W ̟bPPIZOlJ)2 ";lQmg̺|zއue)}Q:5PZ\u"sՕf]̸ mA+Zkx!Zl-b.9?==eE"BRzzzZ`0ķ)~'ҳ3t%$ڇ Y+4E! /Ψ"5 ^C0>PJ?|Ok)a4+蜪YE!eb\aձ}G.xgw*"Q' 2}Z(/f .0s]9ڋ{ݾf7kuB CqJ \]wx8ιecܷ+FGvԄu4|Dن`M/Hۇܺu+Cd-rrΕy߿}v)^ /|4Fcn6F؇u]Ӫr6uX-VvՇpkqdqtUjZW<ոvmqdSSTwawMFѻqM\1d1Wq槈d4kJPU (e6B2Ϲ8S_Duj??p8"=Eq[Vv.97s Cs\.c QF逳3{> v{0k89&w' Vkuzna!4wE i8nJ(V?G'e0&d[.QTc]@+D#ER@ K=z]BY!ܔ%Ow)bP2geͮ7pI6C#}Mk|Wd.t3\: 7Uy]vMgd#؇ʓlNik`+W*nY:PGM] FCZzMjp]]ԡ2ԎF( *DIR~ަbؒd^oQZᥑ%bL{vU9_,z]+įPA:@6ݻZhTp$ٞ>ҝky4MLv167Պmkv]~W9*o5j1w?T,دeUk먞z7܄V֮d~e X*UȤ1w2B[ˮ)fuUgp VEKsl}q6Ԏx= fჃC <$#.u&7z86#Iis?W}[D.Z*K뙟k\pA~l+ϠRkvϿɕvinIOeem=0lR5:r*a-Кo.L\VB?r^fL)v)[)d>W_i&6pCr9{ctYjjkF)?V+|ޟޭ:6+j_y m3uf*E\gm7ْltqT4͖e'NJ/Z|L!P+vzzjKJcx|5E~xxjY.qQAKO9gPu|>c:}o"? 0nLMR&p19 y1s9$BKKӄqD|khnSTӻaR_աI1n)]QzNڝvUPJXcw\4$|B"9ij@t1&)ιٟJ^7$I|NL3BA&@Ƀ:>VoD(L,cڥDQ]qZ(n:VibnՈo6Rg|f3̏V,Ij?Vt{]L<JW`M|&&7~_0VѰ٬KeaxJH> [u>eI^Zug~ ]1Vd lZp:%Tf`9T7~'chbG/~,WSYG%gJء{ѯ0,/B0 (fbb :^|nbTIYմO=WK6=*Фy-0Gځ)NNSj*UNPj#$Is&Rw :\2 DV3m$ٮVX-]-!`fi8)ږt<."njWu]@Vt: "NcyѺ9NΘ87]&:wjp'fRRڷ^/4>׫2[$d9k7DvLRgggY<|L>aO>4Mͱhݾ]oi[jB1`}Zd_rzWNOPp ۄڜ E Y"eܕVb|S6h\ɟl(*eu#]gq6E\>+T7K%~JK؋X;?y7DTjUJa8cdpRӏQ*uQDcg@Pp75 tVEćjWgsQ(!Vϗn]f$!osK$(#s',ns) Q D167 !@=y1'.Ca%ߒ3(R `)}/+0Te^/K"xPsCoݾM.^\:0!"GJwVX!5\:.;`Xwn!4Dԋ$ܥ&8>99Dt]`@^=?Im Ay#7s@k4e&:YZ4(&c(W`f>U#2zWbjz .$Df$-RN9+fΰ$5tmXw2J>6f<:4pH&y@r)Ł:קR*(\ւ1VbJ! p)e;ks:6uUJ_T}`w*y+U,vV]1JYf0J)1bXd“Uh<Ӕ LDbbBC&rGM5P8,΀aQbo s!R!6CsąJ8nX(Km !mfn6rˋBY14D.0Ɗ2Wvlltm|ҥh._i' 6b?yly@Zl6[,s!bW%|qQɩ۾z v KKj&.10u{8j5r3H)!7MUVc"J1wQp8l8&Kl4M !Tn6:|h/JcdWbj*O\ dHmJS$]gkݻW`0dX3"I\LU׳VA}6՟C5)FeQdʄ{Ho⧞z'V뉰fsqDe |%g RF"pfd+; Ce5کr%ݖc,MS*d4=WnMqݻ$NBGQ$p9J ??{1` ]ȚfђJcFcXHD-b]AG%Ut:Ѣ=햙aPK9a[ۢ]hF2D`0#35흟!|>` dv^+a5qzze$z\]@ynb{Z#/-x?q)eT1IM>t=z9;@9PIJ < \˃V0n: " Jg=*kAt7M\V}rr"&4ME&fOy@a\EPLvT)jn9 @><Ii"$\5u +C~S,9TGá~W<~0T_Qx:L&PW;wIJnA,:?-p>z6jŹErڈmU.ҩUdWX(;f G҉cr("iK<>)8$ _o~>s?c^V"b!"(yib1&L7vY6kυyu*\\9hv{猽//~K͒QP\dDTzX:Dތ|fqwMV减Nl6_e;W?p4MR*(" ='ʌ^[z[$L4E7W\ʟw8, _g} _RűO};>0 X)}Ł"o^\ޕL.S0j$[ջEtb\\Q0H Ŧvah>:j,泟?=Ԟv5Sw}[՚Nvu)ÃlvPSqkzi0l#ƙzMi)(F#!^o'sI۔C`XG˗U+׿o V_s.`8!9Sۍm=Q!gN+%$r L9a) 'OkSʤ F+d2A(===PBGGg޽+b4oee`*`"T Ŋ&adsE:5]3Bh8k~_DXD<~=B~'onyPt+B@48M%TXCmŢ 5-B.ZJ8V)2HsnՌytz|{~ѿ~׷9H}ů}y'>xj 4XdB;UeϞ/z]#e]x@QX?0Q DIqmыjw.z[ !C?c/:\@"ZO>(X񖃇$E^UoǭVaa!IREqvi|ƚ~gwC !?ӿ˿2Oo|_tNXQ"Ld^G8nf-9z[eFlo?<8㖧FCiNZvcu IDATu[rIi#JeW3LEK9E?;?{BozSnO<< gqTUk\:_*:^,HP;/ 0i]$I8;Mŗ^B=76H.ܳo翿/};@!HJSa-pEHDH e9xf+<Pخ ]nL.ĦvK{p_?.rxxMӳL*Dq|-Ep)看S2 t鞘f{wQpjN'6!$r߾} !}rIf٢bC#+gӍPg_M{vgR}wsls^v*s0蟞{oKpd&7K{vWµ k~2@b4"f@%Ʋ]WZKS=PsWC\EYIHЄ]PHL%Ѽ7o<<4߽27 8;)y1t>C lBFSaN]@tkn > hʉ( 1dzYka8BHuX{7@X2ߨNn|t7ÇS.1Iq{^7@[rd2.0`L=TD{ Ҕjq$_~9RL&~̷s~p0e!8==+~_-ZSJJ<$$&!`ע4܈ᔤ X*S󙵘fmEKҔ0Bt݄gy3ܗjU3.g?ےx 4"pfIyi!37 W]qnc\ă?r7rMZ˘ܗR뢸!g>Lf~ )"FX7*6ج\ik"xSW|d)?b= ǧXaxgߟ}͞_6}3~JSyyUu˝.Ã+;XRnVq(AJ|UjmD=S`:&>O `"I!;:ʎSptL+[-׿HS곖Tv>|SxF c qD\\)xC}h>{ۣ>W/u;564wp FrJT`7ܨăyLw3D l6M@b0^~&mGRb0bX,Nt;z}_Y_yGBqx0j\.ŵv58owW$ٮ{hd\,ZUNplun7~Ƀh]vŭ|@|K~yidL\'š#4o^x_xW~C~3o??U'Qd2D]rOU$Ij{'Z]|Gv޵gq!dKr:OSɢ٬|~'!ZvF_泿+\.B'(<{q|+ SZX!Vp1W[D~<;;VJ2c,"Ҕ"6v!?~Z??y%9b?O_繁rH$3MP,}ݴ}|QHٌn& c![ZzdRd3^A#jkzh*8vs^$Z!k^<9Z#iY6t]-W5川N[;;E4M)MT\FE+:v[{ӛ3oxC'Ew:qB"R,Sv]t [kIɣ\҄+:PTb0]:u(r'T,zWM8&y7 @S#o_m6$~}ozx<8<8lDѪs.`jÈKr]ǺDJ >=ۿ^w7E\d ]AV^KpSswj,jKTR=7^ gө=|ts(,NI׫twƮ æҽucdr96̓}˜3$sܺuKLjCҴ(m `@+WH<C~G^БԤ(mGCtz~~VWl٬pGB"9 "\hLLy0Zj h-zK7 9B,b&W_o@LSqQ*Fe GWSp˃OWj4m2N)guN]ŋ/:-[RLϪ cUP]ijU^j5Yey#ȓW7t_." "`B.Hļ 8bX|+_A. qtb\Nʔ2Ȯi!ں@jʲfט)Hf0H$ 4闿eQ0?,\ԼP(b6D0 GIYxRqlOOO'!v!uhMw T !4 |*&K+b8Q1F3NO9[,NOO_~d\3 ~As:ɅmN"5/Z`wK`pX.fZ$Ufy-H#5&Y:勆{5:jVVPhE +c l /pttxWcٹjM}r-= 59bʷ z OnoDt Ə_rBQ1Lf_L&;w騅Jf H,Lk$dPIQ_sd3"\-ePg&>'CH@1Ra <ڹ 8猱W_}Çբt:yW(\hܩ[BCc ,5"̅rɎ{&MgUprrg:һ-$ZJOcM?)onX,EUrř"tCQz=Q18fr{^S6f6OSJ4M3^4DXY6Rj1ikfЄҤ{f9r=|^"D}v 1fʮ; vJX3Ą猱['҉.ob>GQt:~`L,l6M ]J-\_ &ҥmdWD֗<]ɮombfnS7WBDRnc~qܯ~k7g~܀,5;8BE8ORbĉ4Mtsjq?KR9KS*($vZ/W+m(WHx R@Χ+-]*LX[ sέ. P6Ɋ;䟑-}%*r8𪔾<;5B0c\K).1ƔR#@i$!'i*0?Q)B %Z8=)^ҷZsܺ}4z,qln=H‡K%|jzwk2$qёUAH-cX7TDS@ar;Xz9c𦪐M6;l: ; v ]}%mSm 1G ;`7cRdüef߂2՜3kJLggՁЧJ4:MnU(`_E-\ϩl91&q5/*n5Q@ T:ոZ6{^\}g)u:!*jɹ!?;pH~{xql#uj*:~/ ֩z ϴƕ]KӔX)Bv*DIqB)vj{/մkJCY"[RR#8:l2+3Yv8ƑE_}; mNQCj58^q#@6iIG,/KH}h9fN'UKQsQ|xtE1-X,f&9H+IG 9><0pqq!lP@,.qL (5A' If?׍ځt9{&aC^"Æ1JpJSrr$($sz/bڱ)+y U3c~] ** rii|2fs d/Q?ũ cSiET9tUv}w1ނ0 XŽȎw<)կdEx!~li8/Axجg@C%kK; ?nʒ_dc[j4_C5_JG|]+Vr+.״®{:#)܍$温7a?Y^yU+5Ty4JpwVj1a*DF3?_c4>?%es~qܛ $οѧaf\j!";ݮŒ$Y.Z~i?pIvcOk1Gc"_aO~SxgOaN #TT~j/BH ιOdL0n:EҔjX,fpqKˎEĔ]8BN74H,fb|KhRt{i%ιqVLd݉\p5?PXr5 9s-3,S<*sΙ8F:W[# -(_qsX:f1لj?r5Z=ϮC×E)yF~v]>@w-U|H@Y䋪$ɉIֳpU$gEp(Hsu-6)}Qi3E<YC[ChSʦ# \4, 15Z4iOR[/~fֿXfE8hFfxhl܃E±nN7w̶mi1N$IrY#lvuvnq[AZaYkQT5_4nw+4"f6' hD>6zYx`Bn߾]G*hg%Or^rN8"`=Ti1T ZNfyīF9f ;Fe' ӽ#Um!5q+Xjɉ'EE@wxHT$6'і2t}gĹgjiR<֒יY2(%RUTZyzW:F:﷮ʕ捺 +`znuCs4N8n|PcÒU^E 7?өV~KJmӀؿѲ&o]22]%da R-f,o~dʺ֊|*ێQO #/Ez]o J[n.H4H0iNEjlޢ?Ag~"< 6$[󠎉vV<Ԟ\Ql(U$VbNet ddfEAԜ:Vxw&E:z7'zNJ 9ZnL?-mz*'Kiv%e_˸x6Mv511yn9"06Wȣ+cVA#@Z/vTK$RHqιkUDqHK9'ĭ-n<'<2G$ʴx8wF}2NbQhn3'*DobjªrYD陧qstǎII4t:&_P AQ6@Ѵ8+3M ̀1f s&ǯwp ;m|fOG!![\IDATE¬.Xi_im1KHVS@ڒ-vh2Rj{dXgZJʳTadՍz ]_PwMV_c/rgZ쪳!*֩U֪(x&.(8m],]W}f'O@[OͥwoFp8Rl6O8#<=*3M -Bgk%ff,5c7"L&QX_&RR>B9ZfZr0Np-7BhMs{)~P|0t{Ȋ^GޤijD]fl6+`1Vn&XAp& Uq+6v/gͿ-̏?ܠ=amħ-5=4?s%jtjnv#ܠ=az&رIz=ywg j,*b%zF n4Y@ْJ-Dy*l%ePEj #ZFܺ6)ASnRV"VKYrjS{^@r_jMq~'R5bYk 0nHQj(*!@ou!fH6|x7?Joi&]W< KGg7(m[TCP]bg],4o_ UcU/zfE˴|=pS֛kW*kL%lin`FٜRVp\z),27W\5!Y.hJMit|cbHai ukui-screensaver/src/assets/intel/cancel.png0000664000175000017500000000045315172041035020132 0ustar fengfengPNG  IHDRaIDAT8O1jA`+x6z)SDb 9D! S 4x`̆'uYQXfv \HZՀEp7J7}@X ρ+$08R%)z̮ZP-p |}vz_ҳuY{^&­1\uV@$D(F`N_|0GgWi 1rele=c<̊[@)OPW;o3fAا Tn{),KFC_@~W]IWN.VbقUA84IENDB`ukui-screensaver/src/assets/intel/pin.png0000664000175000017500000000044515172041035017474 0ustar fengfengPNG  IHDRaIDAT8O1+Fa+IIٔɦL g)ɠl2 6o tqS'ss?#=&I`X+@}LwX`U}Bg6^I6K\i$ހ u*Lr lSg G5" \mY8Ph :paHW5WҌ_}IENDB`ukui-screensaver/src/assets/intel/icon-backspace.png0000664000175000017500000000046315172041035021550 0ustar fengfengPNG  IHDR;0IDATH?JC1NU'&MA.((-#t**M^_Z! jk>Bf".<$gF8k>>n*sƎ:b7dC:I DF ^wr8{|i;\}X:E: D`tV H^V`{1X,π;IJE`{=p XZ..'I׊wg(`6 L.&HwM|8 Koc 3#;Ԍe{3E X_Y ༤ǶGEBXJ:m{p\<^ n -̍lQ=0"A }}ޅdWۀyy%2YR['^~JS'm!kI t%ofNPJkf^іA*0~WJ uR>JlgYTD$5ڎ%}+郮9Ȫ*Us(=IUI$zZ .fVK*Ov˒ZmLj']%SwBh]^\'W`{eQiٮ &'sbτ;c&k$*^ReL523V74L5JIENDB`ukui-screensaver/src/assets/intel/phone.png0000775000175000017500000000024515172041035020020 0ustar fengfengPNG  IHDRalIDAT8Ocdi =d``3gJ/̊g0}#"4Ô?.LC7?Q 3#`ԀG|:!1@l݅ؒ2̄, dJ]IENDB`ukui-screensaver/src/assets/intel/wifi.png0000775000175000017500000000122115172041035017640 0ustar fengfengPNG  IHDRw=XIDATHK͕ۋq?Ln%k"R%cB")$E˃!5Hm xp(M K{Ggƙ9g4So绿e}D?3>D`tV H^V`{1X,π;IJE`{=p XZ..'I׊wg(`6 L.&HwM|8 Koc 3#;Ԍe{3E X_Y ༤ǶGEBXJ:m{p\<^ n -̍lQ=0"A }}ޅdWۀyy%2YR['^~JS'm!kI t%ofNPJkf^іA*0~WJ uR>JlgYTD$5ڎ%}+郮9Ȫ*Us(=IUI$zZ .fVK*Ov˒ZmLj']%SwBh]^\'W`{eQiٮ &'sbτ;c&k$*^ReL523V74L5JIENDB`ukui-screensaver/src/assets/intel/back.png0000664000175000017500000000045315172041035017605 0ustar fengfengPNG  IHDRaIDAT8Oҽ.DQMBF&h$4:RO))t@4*TJT*$"P}r䐙)]{={l3?<ԏA? pL3@EhISH{O8W~c?-$9&+^q`G(w $ VZ@Yp.p6 u6d8.I怍r;:ZjrI#U_ zor%DYIENDB`ukui-screensaver/src/assets/intel/icon-wechat-noqrcode.png0000664000175000017500000000643615172041035022725 0ustar fengfengPNG  IHDRF IDATxyE'Hm+PR@LRF "ȍV@[,A@P*(DQ | ك$aIMlT}Lz."H$D"H$D"H$D"H$D"H$D"[5 `=8EU/H>#"P"~"":IpMC)"DTU%I.|d"rzޑ&q΍N+;Yꑈw[-O"upMUuI 4+QT. tttG%/9V Qv G2ƘZ)Je2?f F,/ڧ8U Q=Cm)'F"'u[[idU" }{J҆9^$"[ I~X<>t,rGT'9 "1 Ƙ˒Dб<"rB+ú t2 f H~3t2-IC'" cHЁɓH.2l:v@Dn Eܸ )9MQD3\,"CW$|NUE Y`i c]:@gȩCG򾔓RƯT*y~kt#QDfWUR<[{ec;V3ЁʚDRϊr΍A׍%g6R8t"Y@rA|7ZmSrY'eTZI!~qέJ$/w\_%x:S-Uչ;IǑ̀}CC?D c T퐖$Wl"GeL$S9AE@1W}[-9s`kge%Ri6vH$E䊖ZɷXKrn=۸e dIfTuGJKSK()ei۶-ɺfuEv`-+lCO8E_eYU/"VfH$XIX,j֋ιkxKl %ݽYj [|ODvI3B۔3 ǤJIz1꼒ZC۔(|#<_(t΍WB۔%.J9VON{Ot$د:'UcRՉ:sy\p&ݝsk'; `1ު]z*BlgO1'o3}!pTp1zzz6FT_Lm+D?Gsdɒ V6n ?_ŽșO!"N&duݛS wOM52|$ox^Ƙ$OkݡQvY,"w#iPf_X<#3$O :97PX~A\FJė*n y(tb4Tp^xts1K匪Nܫ0jnytqέgxIZ9aH:9pBZ97d1T:|u=%y}T,/WNafVkv)%2U={ι1z"PG MD'iOHpxȬJ2\.("<9Yl!"ȕiȂTPG2j3@L= B!q)zMڞ$ BADv rɫA5 x`Q$'KBa3Z<*xPX"^}jGҫ0x@rCNP(1r@ulgW) ޯ6'vFuK)8V y{Շm+f ( @ $ճ#ϡ'uvvn2 $]ga.ɩs# j,cS"J) T"[xx z>c"r5n%y=8RufxmS- V5qFDfzZu5wH+6ˍ"HD#9>qx.24@ckHU4AUꝡS yoo[z!FiKl40Ɇ 2'sKBǥP-#Ƙ2<.3 ^px#vRX%Kl@ 1H.3:FUC31[P. {W@{[`<6jRׅw[#+Jn>=(mwzzzhBDVFR9HeM$*^;x48zuG-$'G˨꾡INHzZhEZ?'3 êeFZ+^Oapέ `f򮚗G\S1oOs⡪zy^Z!aǕ2dǿ~\:"?SfC F=zy fl@$IENDB`ukui-screensaver/src/assets/intel/powerManager.png0000664000175000017500000000107615172041035021336 0ustar fengfengPNG  IHDRw=IDATHK aZJòdل @! D%(hb%ȊPwFQȑܙa;̜{eOO X nz>Erf}U@Y>0d;5sZX ,S? >_%U@&] sXHDd|SV 7hV}'` xK`0^p8\WSx%XnMA#"4<\4W4 :KqM-pm*@"b#𠰊lU4^DT .?;<`zUdi -;*7 ^l9yOT‰lWVd'{>ܖ4,6A[  pm !z2  ? ǸIENDB`ukui-screensaver/src/assets/intel/qr-code.png0000664000175000017500000000154115172041035020236 0ustar fengfengPNG  IHDR<ڝgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs&?IDATxݑ! kӅp)]$a3 n *u? W~ HA Rq>>rXe"]lofĒ,p8 W}"zCoi![lZkmʭ֒I_r @ITRXR  A[JaIAD #w-N9}Si[;jy"z3$[ fRni|,RXR *ݻ΁u),)R]fmLA4\r),)R-\3] pg+ZrKaIAD4Nhw}EL}LRM?έ4d)HUsW_6!b>]n[ K $r]d)9.θ‰`fI,)RsK;ߙөn K $rGPƔKPrj%%Y 2EI4Rh":%Y r9=ڏ](r#.jNE7+%Y -;Ɉy-{&iZ&D5xl KAr`fqܰJJHޙT3ƽےI_\ֱ^=7N),)Rb6bTr+[n) o-7sc;r#487(%)$HAf3TZ'IENDB`ukui-screensaver/src/assets/intel/icon-no-signal.png0000664000175000017500000000074315172041035021524 0ustar fengfengPNG  IHDRw=IDATHK͋Pg# +al,(_Q6F(X ?`A)LE6ʆXQ槫Nc2޺ns{~sq|H2Rz.0OIV/OUfSwd {{Vg 8 \5Q?XJu<| pF4 HП'.S=4pLu4ɖDKAHݕdyd1pJ2$pV?'y;@9 ZY:d>pxPA;$cz.q*0Ɓ+`C}0pxS#$ہkuc[:|deU*Iupo5/rk֮UT@fW!u4pzOd_@T(f%IENDB`ukui-screensaver/src/assets/hide-password.png0000664000175000017500000000051715172041035020344 0ustar fengfengPNG  IHDRĴl;IDAT8ұJQ߷X|@|-TLl%4V2[[JHa'hHk%#R.; cƌMDD\۶^{"b>" 3kf ؃m[9|FUݼpKUi{NmAU=c{|Uqf.V 1ՎqQU3s 3X:.[U=XjU R|yf'`xUkQ݌G{+"6~+bϏ>G`GՎ?(-IENDB`ukui-screensaver/src/assets/scrollbar_up_hover.png0000664000175000017500000000016315172041035021462 0ustar fengfengPNG  IHDR;֕J:IDAT(c-\Xsh543ft8 @׌bh # OW$WIENDB`ukui-screensaver/src/assets/reboot.svg0000664000175000017500000000042315172041035017074 0ustar fengfengukui-screensaver/src/assets/bio-login.png0000664000175000017500000000026715172041035017454 0ustar fengfengPNG  IHDRw=~IDATHA@@{}m_AɄ沝Ld5sdt'GzUS$%yBa dOi/( 1j-Jy}pARe` 0tIENDB`ukui-screensaver/src/assets/ukui-loginopt-ukey.svg0000664000175000017500000000367115172041035021373 0ustar fengfeng ukui-screensaver/src/assets/keyboard.svg0000664000175000017500000000202415172041035017401 0ustar fengfengukui-screensaver/src/assets/biowaiting/0000775000175000017500000000000015172041106017215 5ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint4.svg0000664000175000017500000002347015172041106022357 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris9.svg0000664000175000017500000000267215172041106021004 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein5.svg0000664000175000017500000000613715172041106022166 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint3.svg0000664000175000017500000002214415172041106022353 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein9.svg0000664000175000017500000000614215172041106022166 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint6.svg0000664000175000017500000001317515172041106022215 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein3.svg0000664000175000017500000000613715172041106022164 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris0.svg0000664000175000017500000000202615172041106020764 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint8.svg0000664000175000017500000003274315172041106022366 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint1.svg0000664000175000017500000001317515172041106022210 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris8.svg0000664000175000017500000000267115172041106021002 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein1.svg0000664000175000017500000000613715172041106022162 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint6.svg0000664000175000017500000002556115172041106022364 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris3.svg0000664000175000017500000000266715172041106021002 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein0.svg0000664000175000017500000000527615172041106022164 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris1.svg0000664000175000017500000000266715172041106021000 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein8.svg0000664000175000017500000000614115172041106022164 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint7.svg0000664000175000017500000003057115172041106022362 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint14.svg0000664000175000017500000001762615172041106022446 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint13.svg0000664000175000017500000003754415172041106022446 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint0.svg0000664000175000017500000001763415172041106022360 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris11.svg0000664000175000017500000000267215172041106021055 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint2.svg0000664000175000017500000002115315172041106022351 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris4.svg0000664000175000017500000000266715172041106021003 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein10.svg0000664000175000017500000000614215172041106022236 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint11.svg0000664000175000017500000003574115172041106022441 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein2.svg0000664000175000017500000000613715172041106022163 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein6.svg0000664000175000017500000000613715172041106022167 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint4.svg0000664000175000017500000001317515172041106022213 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris10.svg0000664000175000017500000000267215172041106021054 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris2.svg0000664000175000017500000000266715172041106021001 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint9.svg0000664000175000017500000003374615172041106022373 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris6.svg0000664000175000017500000000266715172041106021005 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris5.svg0000664000175000017500000000266715172041106021004 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint0.svg0000664000175000017500000001317515172041106022207 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint2.svg0000664000175000017500000001317515172041106022211 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint12.svg0000664000175000017500000003647015172041106022442 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint7.svg0000664000175000017500000001317515172041106022216 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint5.svg0000664000175000017500000001317515172041106022214 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint10.svg0000664000175000017500000003471015172041106022433 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint5.svg0000664000175000017500000002420715172041106022357 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein4.svg0000664000175000017500000000613715172041106022165 0ustar fengfengukui-screensaver/src/assets/biowaiting/iris7.svg0000664000175000017500000000266715172041106021006 0ustar fengfengukui-screensaver/src/assets/biowaiting/voiceprint3.svg0000664000175000017500000001317515172041106022212 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingerprint1.svg0000664000175000017500000002071315172041106022351 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein7.svg0000664000175000017500000000613715172041106022170 0ustar fengfengukui-screensaver/src/assets/biowaiting/fingervein11.svg0000664000175000017500000000614215172041106022237 0ustar fengfengukui-screensaver/src/assets/unlock-button-pressed.png0000664000175000017500000000104215172041035022034 0ustar fengfengPNG  IHDRJLIDATHka? AB:@ɟ !D%ВJ]3. TtRt? .vH,I -AA]"<x/|{{߻sJrlM`4e!p1NT>eZ%@;mko2Z-yW1m 2 R.És6ztsiV{zd{&O^}bDwV@0j<vI@g~e%~G+6-N5Us$y45 Mco_%҅  :~eQA.#_?5g uvQC@} p"}C+046B:u'*Mz-|gՁz4'XOF&a%=[QLAm~ZHI\f K"p4a:+~TIENDB`ukui-screensaver/src/assets/data/0000775000175000017500000000000015172041035015773 5ustar fengfengukui-screensaver/src/assets/data/conf.ini0000664000175000017500000000060615172041035017423 0ustar fengfeng[MachineType] PANDING LTDtablet = SLATE #Phytium12345 = SLATE #key 值机器的sys_vendor + product_family #cat /sys/class/dmi/id/product_family #cat /sys/class/dmi/id/product_name #cat /sys/class/dmi/id/sys_vendor #value 有以下四类 #[SLATE]平板 #[LAPTOP]计算机(有永久附加键盘) #[CONVERTIBLE]平板/计算机(键盘可以分离、翻转或旋转) #[ALLINONE]台式机 ukui-screensaver/src/assets/ukui-loginopt-smile.svg0000664000175000017500000000232415172041035021521 0ustar fengfengukui-screensaver/src/assets/defaultbackground.jpg0000664000175000017500000026040415172041106021255 0ustar fengfengJFIF``C     C   8" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(Ȃ(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((.J)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ)vѶ]mmh(FJ(h0h0h0h]Rm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E.6QKRm%h@ E8-hSѶE;mhSѶE;mhSѶE;mhSѶE;mhSѶE;mhSѶE;mhSѶE;mhSѶE;mhSѶE;mhSѶE;mhSѶE;mE6v)`Ph`Q@ F6v)m)E)EE6v)`Ph`QPhmhNhNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFm`Ph`Q@ F6v)`Ph`Q@ F6v)`PhmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNFmmNF.3KKR4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF :n 4(4`ӨуNF)P1F)P1F)P1F)P1F)S((4`ӨphQ@ E7u0iPphQ@ E7u0iPphQLуNF :n 4(4`Ө)b))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))Q~(3b1@ P1F)S(S(S(OQOQOQOQOQOQOQOQOQOQOQOQOQOF;m.3 ~ 0)`P0(Q@ F3 ~ 0)`P0(Q@ F3 ~ 0)`P0(Q@  0( 0( 0( 0( 0( 0( 0( 0( 0(F3 ~QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? fQ@ F3 ~ 0)`P0(Q@  0( 0( 0( 0( 0( 0(`S(bS(`QOF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`QO(`SѶF;mh`SѶF? f 0(`S(`QOF? f 0(`S(`QOF? f 0(`S(`Q~Q~Q~R❊1@ bPqF)أQv(7b1@ bPqF)أQv(7b1@ bPqF)أQv(7b1@ bPqF)أQv(7b1@ bPqF)أQv(7b1@ bPqF)أQv(7b1@ P0(Q@ F3 ~ 0)bF? n(;b1NSF(Qn(;b1NSF(Qn)0)F? f 0(`S(`QOF? f 0(`S(`QO 0) 0) 0) 0) 0) 0) 0) 0) 0) 0) 0) 0) 0)1NR`S(`QOF? f 0(Qn(;b1N v(3 ~(3Qn(;b1NSF(Qn(;b1NSF(Qn(;b1NSF(Qn(;b1NSF(Qn(;b1NSF(`SF(`SF(`SF3 ~ 0)`P0(Q@ F3 ~ 0)`P0(Q@ F3 ~ 0)`P0(Q@ F3 ~ 0)`P0)p)`PqF)أQv(7b1@ bPqF)أQv(7b1@ bPqF)أQv(7b1@=)pi f(?3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF 3bF G1R`у@T4`x& 4)qO#0hbShbShbShbShbShbShbShbShb& &(Qf(?b1ObSF(Qf(?b1ObSF(Qf(?b1ObSF(Qf(?b1ObSF(Qf(?bP1F)Q~(ShDx& &(Q~(3b1@bSF 7bF 3bF 3bF 30hF) f(?b1ObSF(Qf(?b1ObR"f(=m1F==m1F=6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6Ԙ6(mm? f6((hOFf6Ѷmmm?mhhOFf6Ѷ`P1h*LP{R)\mm?mi f6ԛ)vj6Tivl Rlf N Y(u*ǚc%3Xĉb:"L{W<uե!CUkEZjcCMTTM+E^*%=hPWLds%Bµiə4@WړmLVVL=jL 6CQ"mm?hmmmm?mhhOFf6Ѷmmm?mE3mi`P6ѶF3mi`P6ѶF3mjLQmjLQmjLQmjLQmjLQmjLQmjLQmjLQmjLQmjLQjmjL{QjmjL{QjmjL{QjmjL{QjmjL{QjmjL{QjmjL{QmjLQjmjLQ`G&(G&(G 0(hOhmm?mhhOI`Ѷ f 4m`ъmh Th@\bFxFbj6Th@\o}Mm.Gڍ&6#Fjmh Fb#F`3im?miiip)؛mIF+6ԘiXwmaFړ @G&6Ԙ`P{hR`Q@Gm?PQ6Ѷj1@\f6F=`QOǵbSF=SF=XO2~(F ?b F 3~ 4ݴmmm7im3~60hm F I 5&6x4`ԛh@уRmmGF I 5&6x4`ԛh@уRmmGF I 5&6x4`ԛh@уRmmGF I 5&6x4`ԛh@уRmmGF I 5&M0ih@ m4Rcڤ#0h(0h(0h(0h(0hGQj1@ F?cڐ`RڟFCKʹmӂҸ҅D{}vԁ)vTimBS{VnF$A)*@dhDcU)sfL➱HUjG"ub8U#XA XHQ sBR0miy4ƆjO @VUP u$U]j9d*NJһ##Q* µf-jbhC"+I{RI=6уLDx`T4`& (ڌ{P0(1F GFIF GFIF G1R`у@ ǵѴ1F)h@ 0ih@ mGF I 5&6x4`ԛh@уRmmGF I 5&6x4`ԛh@уRmmGF I 5&6x4`ԛh@уRmmGF I 4m`уOh1E?2~ 4(?cژ {Qjfiʹmڌ{P6Ѷj1@ F~=ǵ{Qjf6F= F~=ǵq`T b#x`T####QR`уL(0hxcڤmiEj#F~6vRm h Fڒ n(`QڕqSE+QOXcph`уHуOFf M?mhhOFf6Ѷmmm?mhhOF3mih@\f6Ѷ\f6Ѷf6Ѷmmm?mhhOF@3mih@ F~66Ѷʹmmmm;mhhNFn6ӶѶ vK@ F3 ~(3 ~(3hm?b F?cڀF?cڀF?cڀFOǵmm?bQ@ F~6Ԙ`P{hR`Q@mIFG&666Ѷʹmm3mihHc6Ѷ3m.v6iiiqI NN O Pv# N Ruj$!):GNnF$!)Du8#ͣ8%L#*Tu*rʡ XdJUJP*qc\U'PGLS=uJu*{U!PNkUڵ*$5 ,Ud努KzT}HO@ⴤIzP8*&J.ERҵ`4nE}jVZMdmmHVG&Gmmm?mhhOFf6P{hR`QLD{hR`Q@mIFG&] 0(hO 0(hOmm?bQf6F(hOm1@ 1@ 1@ F0FOݴih@ F~66Ѷf6Ѷmmm?mhmm?mhhOFf6ѶmC6Ѷʹmm3mihLmm?mhhOFf6Ѷm6Ѷʹmm3mih@ F~66Ѷʹmm0m6Ѷf6P0)}ǵQ{EjL{QG&(ѶF7S(1S)iXuݴ`ӱ] f 4m0ih@\f]mqhNFV XwF;X.7 xb`QRQ@QRQ  f(?@3cڟF 3cڟF j1Oj1Oǵу@ у@ у@ ǵу@ ǵу@ ǵу@ ǵу@ ǵу@cڌ{ShcڌSivx&6x&6x&6x&6x&6x&6(I(I(ǵI=ǵI=ǵI=ǵI=ǵIj1Oj1O1O(Tfi`R͢-8-;mKcC@-!iLԪŎT<+ L@cOW<a(yT^+U+lJRIT:cL&ib`W$12 jT5)1b1VcոsJgDbZj/JQ3:JA$Ux9*XV{UIb^*RDbkKRH׫NgR\iIVxфĠɊaJLsI Ւ\֩ jRj#ǵIF)4b#b{SF 3cڟKx&6Ӹ\`уOFf 4m!4`ѶF ?mh4`ѶF ?mh4`Ѷ 0ih@ (I(IcڤFcڤFcڤF@ڌ{Shcڌ{ShC1F=4`1F=4`1F=4`!0h0h0h(0h(0h(0h(0h(0hx`T4`x`T4`x`T4`1F)4`1F)4`0h0h0h(1F(< 0*LQ f(?f(?`3cڟF 0mjLQL=mGF ?mhbSh1F)4`!0h0hQ~ 4 f 4m0ivӶѶ v6p(`Q@ (=m(Ѷ`Qv.) e. ;43mihHc6m3mih@ F~f6(mm? @3mi`P6ѶF3mimʹmmGm?mhhOFfM&6{hRmmGm?mhhOF3mih@\f6сHc6ѶF3mi`P6ѶF3mi`P6ѶF3mi`P6ѶFmmm?mhhOFf6ѶmIm3mih@ F~60h8QR(?i NK6)@(fEJͣSR"Ոҹ%P#NӣU*\i,TYX/jS4ʞU0NJb+UN)2UJa*Bh)`lHCUHY9\MT#jsXj΅MX3T]QUjvy*##)DY"iuWNgHU%lKT.Ҩy cV*֖,^SN[PfQ3 ~(`QNF; n&~hhOIIHbBfC>z6PQGJͲ QMJ+R7$Z*3-F*KUȍy*n%Z*&Eڼ;L#t*P+UN]=ZDs6P3Z"VJ_].`+ytyuge&ʮarDε~DH2)8Z"1UbHɥ M=i4,TUS#⥢4ᖯ-b/5v #hj)jrDSUv5RJ^$.njSV00BSSXWm9sNDִZ*f:s<2厩ʘ9RʵBg8VUWRQ](|TUg50ƘZ0Gnnnu$ N P>RPi@LR>BAڤQLZECJ @p!M)V6SJљWM+Săm40+t̚#ǵ&IL{Uga`SF*3QLmm? b 0(=mm3mih@ F ?m& )4b Qf(>n 4Qn 4Qn 4Q@ F 7v 4Q~ 4f(?f=ǵ?f=?P!Qf(?b1ObSF(Qf(?b1O4R614mm3mih@ F~66Ѷʹmm3mih@ F~66Ѷʹmm m;mhT!JRREV2VV)LeX+L+Zd_mTݵi=6b=m0h+mKѶ]&6pj6ThEahRmo+mOJb=(B#jp J!u?K9A`GKTWFʳRTUKT]O0 (V<9Uϥ/jוJ">USUhCNԹUSUlCNV~е)$tESS!@84bz[Q:##^ :V+ 1Z0I\҉pIW!HԎJ>EPJVv43t\SfƩ"+HZEUR!Ud3lاB\bLThG5ZqY+-O՛j99sOVRjƎzJ9 7L{JOS\4LU|zcOZ%b$UI?Qna$2y:tc5,u뉺LY$G4jjM%LTdjw dlDjZZFUJR4Q.F٫QըZ#14"5e*$UamJi)k 2@W4µ`0kCp+ ˜B8ZB1ZaZ댎iD&*]ҵf OdXf(UqXe)1F)Q~(3`SF(`SF) 0) 0)`S`S(C6ѶFahOʹmQ@Xf6(V 0( IR`Q@X x4`Ԙm,GF Ih6Ѷ 5&6#F66(v 0( F~mm? 6ѶFahOʹ 0(`S)q@ 1@ Qf(?bf(@ Q@COxw=RJfdJZQ2F%RҵaRLŢM)JMȱj6TivqXm.ڐ/8%+[(Syt:W)]>]@#RW)JxJ%=clHԊ"dl1c*RX HDGRuX\򙺁XCOU#\͔ p5T~MM^Q{V~ХLQU*+ڧ~G(ڮy^Ԣ.*]B2ա"VxuhV=|j"V~пfR0{SLʤ1SU0Z&/jaڭT#ٙ &Tm hf[P<5Uy"9Lx "9"E](@Z/Wx29%DV:bdJj2ikt I& h >S$fiiʹm`уEn6Ӱhp0i4`Ҹ7v 4\,3bF  Q(; ahOFiF)(4(OnJu4pԇ8iqqQ15DƜ KcHFlT]޳lޡwy*zɲj43ujųTB4ikj79J}0djIҝM"h1ž8D H bj:UE5*> `mPWn)Q:##r zUf(gW,tFSqVRZƎzu\MA/J.=vhQ"UjiykU7!$j#S-jf$UJ$)2b蒥Ik4KR,֗)\ƲMVlwެ$k?XKȎj{rk:Y 5J)HE1*=7>G[ީMqZKz5{դK=ǽgOqךl<3c9K>j9M36joU yd4]ԉ%QY3S#զCEzf66.TQVlJ25TVU 3Xo\:&Bٮ9ӁUtMqJ%RQS(8̙@n܊iJ Bu9+S Ւ/zT|V+aZReL i1S쌎YD&*\flDx1M.MbSсL 0)bS(pQ~(bSF( S Q@Xf(? 1F)`P1Oa 0(Xf(? .1ObS(3bF 0( QEf(? .1F)`Qp1OQ~Q~,3b1@Xf(?b1F)1F)Q}bS,3bEaQ@Xm(N)PE:QڐSm+_;slWd1fmY*&OjQ2b0J J3 }m+KEeK%)JpJGNԹBӄue.}.=\.fqOOc-׽<\>sT{M?PO1j s.I?Voz]Hr,88jUrXy#7Ty37+6hYX5NJMcRBlIMY1VozsUf9&?b/'Q^zr̓)=gM MDU)eKa,YI&jIVI?Jf+TIuXDA~6QPoTIuV2K)U=J\6r65gr)Է9"=+.(JآuaEWUsA\ԁsN]0sΙJc%Y)MdFSϩLQ⭲T,ӨyVdJaaeB8gjR)k29F`cڟEnC(>g]Ȱ͹Fj~1F)V#FRPi,GFKoҁX oҍJx`T~mP#oҀ&6{S< \S{Qڝf(?g= &Iڍ\(o`SQڋfm3 ~j6QpF?o} ڍ\`QOFj.0(o`SQڋ 0)}EngQOEtThEx&]ZW cڍ.6Qp 5&j6Qq؏.j6Qpj6T`Qpj\ .#h0(b<0jL 0(X .:R@4+} Ħ:OHBQǥ01ev\S0h&ʗw&a)*@JM!3R,tJ-fhF#PdhB# Vt)seY)L)U.R mJqUqXn1OQINZLhzԫPR)4LSUVy#xu5 9\5H7R'jՋ5(j1'E"L2)G)|(݊urp QnK|UST8Y N Un%nCx+p=ru!zizlFaO&ZI8-YsP]P9dSx%\qUWt4RurAVs]gQ֣"8DºI"ZaWf-␭?kD#+F߭IW6ԔQp\ ~(F;n&~6qIRmF)4N0(NIJaRSa֝M=h jibPM(jB7Ѿ|aPir'P/O\Ji/H^@2RzaQ!s>*&l3S [BƜQ[##ucu41`bm!E&vF Rdf уN؏0)V#F~6Ӹ3mi4`vѶE0ڌ{S()fhRS2BO"i\vƧ1٩1Y'jW5 9ګ $z#Ӥz#6Z@P;<IYRBT.Ԭ5Ce z5;Tl*@P歲e*J*V|C!S]]DQN JCXԬNjF5H\ڡf;T,ԬZM!N PJù(jxzuQ`0zxzE);fDNjMLf=BMCFM AL5Fɓu٪.DjFEW!M ]@*ub'Zu" oޠqVj'J,)j%BNr!]*%Ba\PU玠trZLbda\hOSқpS*ZEze"=YGz\7 gXJж~;Ҷst9:WX) HbZIҵ`zfJӉ qVQ'J@h'̉@꼕;!BC !5VFfC˫LcVj{TMH ZviCW |H -0G8 ANR1h1J(dXn ; vUamNj6{T4m{(6ѶXm&ʗmh leKI&6ӸGEI\,GEI\,GF=Mma}OFw F~6qXf6SѶmQOF.F IZWhvQځ؏m*L]#ێԻiivTUӂT[=BT)BR*; *q8%KJ%q(SW)_ˣ5gS)VSJSR)X!Jc&mm{Sv\mKуRmm;x4`Ѷ0hm ҁv kLR`SѶm!hm v] m8p()i2Rur2:RATؤԔR)9M2[2E6"NhjJF(S.i)vثhhwE4u+fE+p*l;,&ԔR;}.e6738G(soR<40M-N$.}i2+-Qa\4GZ$搚nsT#Rw&⍴R(ݴ`Ӏ.) BJD*&mQWrl2}#Sъ(MPHM'1MjRihlԬ5+j&jVzޥzz#Wdz#ԶRC%z#ԒT) wYzn;B3MJ !)@Eyu8Z]XW+B}qSHcJ=1OR$z#zIjFNj@ %PR(R( iW< pJxJ,HEM;ʬ*mthʌ*iңd4+MLW+ƨPSUjdZͣd⫰]RH!D3U!r.բDR*eVMƕ*zB+Y*62l+:Ү:.(R*WdZAErD§sJq⛚ an P}H>pjJb7TUxߊ瑼M^۽dwnIҶ\嬝+f^=ZGKi'JնskizWPiqIVzˆNr)+LHN-UѩT5Ugl+b)Hej#ԓ>*^uXQJ^VN$8I^9lZV+Ԫ RaMH5 J$S5Nj@+dZ Ӏ(Va1]ahOFwʹmQNahOm F~\V&b=QM0Qj~)phXcڤhN3b\C6Qڟ\mo?.6QڟF.6QOF.60jMm\c6ѶڗoMb=}MEƑp pL9(8%L#Q! N?jxs4P+*"k72 /jp}*4P)tGK(*ajB\ &:aUGZ8 SJUcTJ:M9OjMjDmSeUɱM><.+mmOyt\,IKmM ]ԀmjMmp#F~\V .; E!(. !N(KHaERjm> F))1bmh KKmChm`RfF)jFOqE;RNE;m!([!8'44!41٩Rzk5D@ L)zBzO2&FP}+w&}-;ޜ D|Ni>Q@-QQJqITCSH)&R3qҊ 4֩4.BsIRn6ij3Uq'wSKS8S -L/EVjw+=DMy*sBSJ䩸;%5^JW^Gޠw$Uj27j#T /I/IjzVR+4ڠVU, \ jx9!6B)Ԇ0LƠsJ"sU5,# UjF܊FԬjg2#MSQa\EZRVT`*iʕ*N#SCڐXHRVZ4u%+ 2Q4u}BVmIf*ih,EtJɣtFj!Q"Ո+rBFjgT7-Tj^MHM3}!j Q1-QP+v^Fj#Sz# :GQY6z~7%7Բc}<=T)J͔]GzTXH&RsW`ɎJ ᨎlܶlZKҹiyk7J#ԤΚ^G5m/JfkMҼJ~j2V4$:G|MDzwN9*`V75UezFrZ1 )dSNFgJ FZI*S<ʐ- jE5@KR,ZC͜ GԖ%z08:5R5wFG$ZZM@S/j錎gUO5ԣjA3N2lGg.jb< 0)}N 6ӶѶm;mhXnmm ;]F@7mihN3miG&=ǵ;0(ڌ{S3 ~=phX{T4mqڗm?m.lv%H# N O R*VnFu Jc\THTLԫa)qHUڥXXJVXjEڬ5*XJTSpCR+TR(ZM!ڏ;TM j45 ZFf<8*xqP~̢Mh4uZơ @VPaXj\b\,GуOi0hXn 4\{QpL`S@6ѶͿJBIx`T~cڕ1FE+6Sr)DeQԹ2r-Dm.~(Y93hCE<{Qmk?h_!Ik ={2j6uXU*8@3jB95^G$j+U2I*RRI1VC/BrIP4\Xejޏ6ű%-^{z}֡kzcG>ߴi/o54Tm1Xe*%4@XޢgeYC6zk5FRD<ʄ4C,)-R)JE 5J{jRIhv;&K.-TiZaJJB&2Ry_̤(zzSSJR%j)gJ6Z&2S|ʀ&͖"Jd橇4E喧ZY*ho^Bj†Zo/=kϨgGk?Jڴrֳt9WU6uvstYslVkǨN:KikB'KYzVRq\2GdY|7LWRd?CWnpzT $Q29T$85qSwR;T{k ,UTS5 քthdWᓥwӑ80WbzȆ^v)kЄԍV#zΊZWtdq%j^(R0h}V) 1,걗ޚfV,)Jf)ọ̪~m(0KJxjE0KR,JDf VY*Ezh)=HLrp5W&éxqX)phi\, +BӂӕiTRCBӂӂӂ6hйx O Xj*ʴT6tUNLUW9QMUTW9VQMQR(\gLPJ`:W3gBCH)Ś(iAgr /J\fD%i3SR!Įc4VvmC7SLUse!U 2b:O.Ut&jO&]he?&]UhQRTUJ^y*:xX:j_!Ȏˣڇ*yt[1S:PLlqeC7 Xu]kEP5#O&i (? f]\ҖN O9GQI|E&E(M.e(&sI)XJ`?4S7QSx\{\ҪtF(4;CWQN (5zpjs4Fj=Ի:Ulh-QTqjv?kHteoz3TRc*%yNjg޺\֨LbwY]qNu5KT5 8 @ы5ޥ[z[zn}EͥVޱޥ[z\6׽;CֱVg)5)&ޚn~wK|]{7YYfޣ7YWsj&sq]1)Ƹ&zi޺ȸM?T3m=kc;z1ީ @]kz=\{/sR.IsUd\{i.=-"ܗ8Uު=ǽ@m$[y3?RijW(yD@& &h%!-8IUqyLINi ^%/Q Ա/L/L/L-R%@z#,*E˂JT% %@d$%0Q )H5JKNi+4.$3ZY*[EYZ)Ljw@+5F["UjQ,H҆J 5n9Wl$rMPW"#k8gn9s,wa.=28efzKz\B'3Yii347U]{\kzs)DOi{ҋzqB5"{"wML zKz.}E37aeZɎެG?hCT䚧IkDK"RI*erG֞HOV&Q+T(`4>QS"r*4(HV-9EJΘDd2 ; PST\rgTG(=kGLGSjep'.!z̦k72 Ѿ=phcT}޲@z7#W>}Dmc)&ޚg>w4[$bxQjޫ<P٢EޠiUjl"KQ\L2T\ y[}! y[}JY2S|ʮdhZie!zw?ITe&z.eoKGޓu6jB٤0'h&MC()[ҚMC- )wTe7VL}ԡ DYNsV#&uٷjҹ%ZןQgOg7Jݳrst)חQ6uv9ky޹IsZOӚz9in:\sԾgcKd45I[E&6WTnjYHsIfv]19咫I]1eՖI0KguE6#aL>rIuVްޥ[wNnAyX yOФ`nzMC{LkzryMZޱޠ{zNc6^ޣkz{zޡ̵{֔^=zp"ޏZ/=[z/}骂p:x=W\wn+c37=w>yWazLz~\fN&M%x}wIS,Մ\JTRZ$>R⚑MVWQyXZOJ$iE<"IL f٢DR-D HL(*d5\5JdtĝMLTkH&Vy#t)i新h2@qKL4k&4KY2ig4$w.4Iw@^R2}6̠ U<ʤIc}KVYFRHY$YVc޵lHG٬)-diTURJISU ٩ 1T.T#!ZI+U37)MTydjfA+UI9d̜Dw%B֊d8)<ڪS բ{ͥ{J'%ͬQ"Khza@h|Vyz2bh;> P 7/Yzw^|Ц_JRջqǺ5ґՉqZy<).:^9MD\7zCqZh gy1?h9g?H{դC47g,5jf~w5 /=Ui'I$?W|\{M$XޫYOY6ljKVI5fzaު4/f٢E=}S()\v. \/2Ze+%HUrZ/*TUzC/ *A-RW,QUp'H^Fz}PԡP% PoH ӷwU"IwRnQM-M&-M&CҐ Zw0MԆIF&C%NPK'N P恖TX5HLEz^(BJ_6/+"MQYlTha^zT-.j4HTM-Wij6'ij3%@d*J'2Ro7ѾOz]D7[SqQR.7}&.'F}e X=Hh2`j]FȨwV-MIHd-2R%BTm-E`M2ViXe5'TL=; 6:=hG<liD'޴HZpިH|w5j;\wj;Vpw֬q\wj;zH[z\*^{ԟjfikQnMkz?Z$Uդ&2MU%ުKsZ6fީ75N[zM$Uds[$dy1ޢ7YuQ5D4ǽ'}!)޴HcgC֏!zshޱZޣ7Io=k3}Iz:nzp{R߽y$z4Ѹ.ⱖ'J9^eI"jFEf}ޗuLM?޳G\ґҢ_DgV.F&S$Yi7NVfk-J%ԚIkR.(/Uy7*5^Jh%j+ԒIU%z,ɢ ۭg\?%f\Ek:օԝk^Mzt6y-bɫKz׵D.&PnM>^k>Yy5<53-Gףy4zgeԫ5vŜrFϑ֤[zI_;޺S9!=ugT̚/wPoz/*KYZ%D3Ee.{ӄաE70ULƟޝđa6j5Th4Y&d5dٲDMUjIl"fisPl"c%7̨Kn(Rp44,ZVԊjd*WSRD2ʵ<5W O TIaZޠVDnQPj?u.E6C@4H 7Q4gޤ],7SDnuEޗ& 7Si4P!IR '4Җ(AH 6!pjӷPw-M/JHM2TLTm%KeN/-BO%CfKPIP٢D%FdZJaNdU̔oCp\ {CSU7Rn-ACSKS SKRX400 Lz+JO2)JW^eeT()\,Z}2UO2̢bיIUo2̢bziz/R$-LglfD7%L(Y*H\gLQ зx]JznkZ~ȭ8&\3Gd洠nhCq\ҙEuVRް")uYrBރ=e :V-KTzi޺"dM-0VG=k(ŲrMѓd7TeZ68) Q%2S TlfmqL3T ?W1,4 { &*'0rj5.bNmsUjlsYoz<zcUn}Ta?Hcq.w^>b;z":(1ޮ{\w^n+zH/zs\7~ cL^޹o7C}WaFz W8z Nkޯ}Ӛ=5r+\7zz\:hYnw^ÔIeަ[z|k NXzw})',>c]gާKzHY}UަI FRT=CEh)J{ԋ7dѢe-?̪"ZQ6;.&|C-Sǭ!ޱq5R.i5S3m?dhq{'$+֡{zϒޫ߽.P4n%z1zӅϽc}ޜ~Isin=UޱVަ[%agVjKznsT-mZ޹8wJƩEVB=C5j=Cް#5N/Q~=kMCަ]CްtSw!pjCޣMޱP^ޚْzY/}"Kz%hT-T޶T̝CN[z%e}Ud3'PՒޠ{zȒޫj_{m}X}Q5Z7MX&ޘo}9_{e(FF|ց{\5Mt湔Y޼i3ާKz碻QqּL[zw}%n3ޞ6w@?ޔO\͝ }9f:Vl?XI'Y2ѯe&x1YDiioPYuIEijMQ=F{3=F5U䚫Y(5Vy5 Y6lLTM%@m%f٢DJizIV&J O @*jE94Й*Rj*UTI*ԫQ-J!OZp8$5H$p9$tQ`AAQHzTJJ)48)5-0R KLC^4JLSF(()Jøb֟jLT0)cpi2) SKSJCLS6zTO-FT-%CeHm%Dbi+6%i*6i*2-"S%4FZ}"KލP2pjxj`NuCvi\d7{iwR1KS Tl=5gl2SL=FdV'/Fdqس[g Cpḳ}V(2TRD%F^2SLfLP,o\gDQz7Yhn)+GLMx%+F sXqKWagzϽ`?Zްq6R:jwUsVޣcynjz[z]gRH zщfޔO.}E[$f٥SVzϞ*͞Fm%m8K@z.=0KOYܓ^;zϽb$b9%z+z߽s\cZޯW}9^޹w^cޮ{Ӛ墻z>artNkޯ}G0v0_ q_{/ 7v+zj|±G{V޹XzW0tzӅscQ CީH -sCPUqXRެGy\zQӸ;zϽs1_{ȯ}Kzn=;z. Ÿޱ=ޥ6E)Eǽd=~Yhjaz7XL7~> {k]N&FuZKY^{Io}9J4_{TRKz9C{z{fގQs޹R; ެ%߽s1{ո}F^:]{=NiIz}jtunsR3Cެ&\{XFѪu {˨{,(]#eTƣA}ot'PP}Q57ޫX{P=ZKlI}Ue/=꼗fri/}2KUޭS3u 9/W{z{z 4ޣ7~^\֟(V5uL7^JR5Zޣ7>]{r=Cޭ2N/}ezP(zw}27=azzL{z%c{i/65s_{TU%9ozU%o꬗5ޣ7מϵ֕sx^{ԩy\#JXw:h=wG{V.t{Մj;zo}sǽ=nHY~u(')uR-׽c x ͡vGzx#bw Phm}{7hWG!\湼5ez9i߽B~5P9~{^⫔\׺'^ގQsޠkT{z,;ޘ5Fzn*6e\{mqTZzi0/zǽfV~kΪzTx*9]nGHiA7sVUGH܆^*RVLqWz&zp4`N6q\>P4f*VSR)cEzdJ |RpK_6yZi UOUbc)q֨Ms棚ިOq]P9e\9n:]y$6c]\.:M&ZQ<ʒ#޳'&q>I泦)̨YjsM^MWy+(+ILQo'/F[#<ʀ&2lX(*Igͥ]+?RJ$.RϪ>a̪)pL3Uc-0G0rZZ-0KZa/Ib2}AѸp`==^LV-+TQ^FLh:5SFZ3hCUTjѓ,TjPkB .i;piP2@yښ2*%RL*A֣AR2Ei Ѷ#(IdQyZLV N3NHK@i UH))ؐt&B1N'j'4 YD5ۚއI%d͒JcPI-bٺCy)%DY6hzQl!婥*.X)41۩ S T~7TE7҇EbϙKyy\,Z(*KQp9P4-ĭ%BLgDT6hi*6f-!7*Ļ7T[4;-J6]z7{ҸM^AjH&u!zɳDw҆wR4HR+U` jɚҵJU ˫%H%$aܼ& ꈒ%._ӄ&yEh zx58OE搸= uV#EާKzcNjW5ǽ[ސѻYI+z\]g1'VjWZcIUL\,JQ3KZaYlSfjb9jgRA!Բ!65<ӚVhL⌊C֒Sժ֞D+T ԋژdj*ZWUꪚ5,"US䚧I4Ie#Q.}tzSBfcN.9O[;}N.ޗxT#|_zglj֤MG޹{R-Pp{VzcR_R.z;թé{ص/za}ZZ6;H/z5z}t}.NrԃPpCޣmC޹ޓc7^ޫ ԶR5^{[{w#4wYu\{9޳!bޥ[zԋ=&4mGsZ׽a?XzĻzn'DyLI3wΥꇛGR4?ޝYu(GJù|L7Dώ?+rOQLQ4X.\y?BmzqRH{GzͳDqqPkz{m$hzizCqYh^i6ޚfΘ"韚UsNYkϨB*Ջo-y5OZ[KZtvJٵ~ExOfnajǶ~i+ʙЈՅ5VV\l%SNi֜:w4$Zz׭<R"85E]&H^yi&*VIlU9g5Q|w攇qYc6{f.1k81n.:YW7i5s?^kЧϩ2;s7Z|͸&:q<ȎyӦ5NY+Y;LTNIHZF,}.Z&f6QIoQoN&7;Ebo2̪NbǙGUѾJBIKR(V%FuX>gޣFw3pj58&25UVLV,V#jU52h5R3Vc51i L*ץhf9ihQO2n2}sNpQM(HDS T*ЮJQLAR7 ѶE;qbafLm P)ha(b\P)"#HM4SYY6zk=DT2UIj{M,Y$Z$,wޚT,fgDMgT6j٦PP҃%Hb搜McRP3TL-801ZXx}Cޓw֕b2IPEb}y7ҸX5/QԶRD%BϚk>j6|T6ZC6lKS T6Pԅg57*KJ ~7Sr('!۩7SwLԶRCRL&Ce*dEyVr;z׽W0t_{ը똎ެyWKGOXK\wa/=cKzoKzo=osޱ߽/ڽv޲ɦEƛ\7Y=<z."45RhoͥjJ$q"TD*>MCehG&jrVtoVczHGU=LKv#V5fSE#T6ZEz^+TԮ;ӃT 4&@0TҘEevZ5h0he&JaJ u3hgˣ˪$GOXaHU\V!XUXU H" ->aKznOc$:MG0rsOT_z9ꇚ}iDq/y $W1-i|ʪɱhIJ$ZWLTTzH՘޳zl#B6joVczbyiޘejafސK|:;ީyp|;ޚf^w!0KjcOTJ%ƟޡiOPF%yP<՛e7@w޲r5Q'yޡiisX(oza&DQ&*J$r-"ؗ4J5-2?5IZiPF/h[JɅ v\Bhf+nkrɺWT {ųt+FfWP]Pt-% ڗ0rD4U_R>s 1Sj0{S f4UEZmoQ>byLƋڣhIOS5+E^*|3j&Z{T/>arTL:շh;fqS2TeqJR;4(4\v&WU4\,\Y*U+T\v/:KT6*nW)ydJR.a򖄴%U NER.%&*VfZY)ꪵ<>)ܛP+ԊP=HR/Fn9+67Q>*4H҉rVlrU䨹G46޾NR٘ LL3R+fꁟޓ>Rޚf>w4G0Kiꡟޣi\Q-OUZlM-fhYyi}6r4Q&yji*2՛#IQ-"B-QrJ4-2)54HSUMMy/օV\-_jv6ܲn7#۳nyUQRgEfճj nj *#֦͛sW5l Ѐ}*UPjeX HԴTdoޫISj9srzքf\Mz4ytՓrz֝Y7&Zq<3n fzօYΜ3Fj7z7L-T:ineWUQ7z&h▚zB"E1KLCriwSɤwi)@M NQVHS-=E4HZ]mĤ#4+yE)jE5O^'SVcjFkDg"fIJVɘ5NUdj3h 5]ZSȝ[4j5*I:uҫkDC&^:T S'Zz*jU5d5<I j)҃bE&E&RiM4 kj#VlZGY^l!TLSz+<2I*R%UL $IDTgB@ϊQ3V-<&SrL<5+ST,Mޚ^ijWCTL3Tlj4H2iXMAHi9J{jJM%)HzTu6Cf);")\ci:uR(bpSG&ր 1!zlicPCDӘ Ce4i9i54n\⚽)hKmĀ F:S=j ih*p54sL (p4aMKLfNML TYDոun*ųdQոBGXj2TF I(>Qn@8jPjvK*2=RWK*DzG8EpZxJr Ȟ"ԋ{UڦHRjt)XKj\(RާKj֧RTj[Zm}yg j>+LZRڗ8rF=mZd6Ҏp1Zڣkc[ОƳ>skcPoB~}IiUeR.ZHkv[R;UImq1^/ji-괖jD8oDZB}*ڪEV4uGEb1EHRVu&pph"(ڧSTš{gc S∓#UYֻ 9Y-֯Nk2ԥֲͪZй~pk֧ɨ>~3W< 5A|JQ֩J3Wf 9dr-Vj䂫H+D[)*ԂqTIYD¬2L) 3RL" #3M ((JZAҖO3T&fJxD04(QO KO A0)Tޘ =)ҚN) RsIHGZz)V#5]O5) :R8 @E8 P0H:R8X3TR5Vja9e5 +a8lM:jFQ@QO2qMå9zGJPq@֙NdQKp8"4њee!8T`OSP2*:ɚ"un.R*d͑r*GbQ8ڷŗaseQ=tڣ{j=eTjeNkjPϯFkOj'{VfN+-S֞ղIVv}բ$^]y`CH9FVE+]i1Uq4H HPӁrlNiCTAᩦKDԡKEz^+ԊɱqY*JHGILf~Q%0LfrHd*-obMGJa V񙋁mTet=;VFN'?,]mMoT|F*XSEajEij[) SԖRRZJUA6 <4bJPj\(jv785Rd4LWCՐ[Wꢽ<=٪&jVj}%Ϝ &eSZu;p9^QP)iуOR6Ѷo"h6DE%HE1 BқTSNSPZ$SR)TOSYDZCVjUjۿJԶb@5m'J^i/Je[t6OJ^jː+5o?NkΜOF7bެՓefwHed֑$`4ٮ=}85$,|^8mI ^ M6{9ez0G6$UedN) v]]j1ifD&5#1 jF5ct59@ n3Nn(=hA(4(NQL)R5H9EH;SSZX) R,Ԭj6jW*M14RV4)*èRZ׭=i=j,zT+ҦFlLjhث/Eb J]7*[i )qE$bԤf#0HzTKuH[CuEH͓PoV,*zpzO)cQ5F;a Fi( :ө:LrH< HpS҅`#Fڗm!ZV")TSRTҢzPҙO=)(Ahzj@j*x5 VcjFd^'Jʉ/\G\o.1ZֳtykNnR'}99KyW9o7y SՅƆz\'RUzIHR$z4fQozr4FYI4jWyb7^jMZWtQ6:I9'5=tDbLzFLs5FƑ-TC5s5FƝƣcNcQ0M0sS4)4(& N)qA#iӀ4p9DZZm4aԄ搚))N&Ni\L=iOJm+!IN#44%8 \RT&Tb< SGQ(LҦN TִFL:WG[#Jju]5f1[#6M1Vc1d:-25jت2% :%Y DJ#$u:GVcR:cu2TI<%JNيdj-DHRUYGzEQ0Tf2TfZ:Tj"f"BW]*#s6tu%]1mdٲE&JUGSrSdնh\v+I)L+Ef;m4QE֜:8Ӹh]ҍqEC֣zy5jHcwۭ=DƥOZiJOqRH 4A搜TZ)3BaI7&Bj RM&-Hig!NhQEK#4P1RҁpKH($Zi9cM!4N*YH !jJLԔ;&fhL(J 2@ǃPsL JSOSHe5*jd-jU#5f3H2:Tآu5":T(EjkED&jqR(HqCK5r}H 8CȢ=B+6hA hȦA hCұfȞoAUobbYh^ X3tY8~U^^k xF*D+j^n$bZ,Fn((•-EJ>cWኲl"=[TҮG K`T[ojwٽAa>7!}նUyakYMbӃex߯qoZ)rw6}xNW_qoY7Vxc#'d]Y+^VMͦstFf,ڻ .VUŧ^+30p99zTe]=ŧ^+>kOjS1q9  VܶتoZe}<{TfbyJ]*ъcbJaJc2STdUJi)NPDivۥZ2&S 欑wY-V&JFbQzq4k>zOJm8qNҭ2GLi֬LzԊ)*U8 VW52X)iiiD{ijmҴEF¬0\b@¢qS0߭C,RL=j ֤jJZ:SJhJ:Բ* WjTnk&jQ*OҨ#t1sf2c5~ +h7m+J  }JjN3r)VRozȊjW+ҙ|U嚢iPQB 3KOJ4q͌Z,dJbI%@SdzrHs?5zc?49nj6jFjhfb-L&XVja4 Vh&N( M4i4óFinD Qn585CN 85B' KPN UqXqMȥ'\mP1zSѶF~Pd4-< p_jrQAHRբ2b-LZ"$ua*$ZZ&HF*4Z֨ɓ Vc^j(֬Ɲ+dbɣZQDn4QSƔF1Z#6 Nƕa*=RTqX%.ڔ%(XPզZlTZБjXɛE-R+JTrMp*Uw^:qɝb4Uajiڹe#13ƇHQT/i5^Hist+BHE6ZYdh@V+4ҸXa!"HE;a(qXpKMɣuSZ2ip1Lj[)!j6<ӘmҢXBiiܫi⎴RL`'4h&N)Siy4()@!94P)i)1OqEH)f) C֒JCBLN=)%M6)2QE (aO)fIR%8 EZ{STSRS%FEHOC1"MN-XED~T4Z(zU!CJЂ*hAhCJlUbY3D>kFTPEWXdM }+B)үV,!J UxV½+lv!Un1X3hbr!Ҫ:UV r%v+WJW-NUHh)EEؖbt.ET.*֋%A"R&hk>xkjd@wun!kz:͸4cVmk.<9Z-Ͷ{V]ͧ^+U U#'eۋL̸o8Ş3fi]uŧ^+6ϯֽxrZix̜N}-}{Vf|;Ak=BUc1:x}H玡txD3*Z$uYҵL͢ TL*ӥBZ#6@zTgL ̈JHn4S &NkݹNh&4aRZhj5RZ$ELAVU" jUZEZ$Ҩb M""E@qVT*HqSB*dG1Ja F’M=j:R Fye!*.VlUxީQЉ2b&1\GT+j3{m7JD3UN6y*А6,U9$RY+4z+ԓ=U=jz$=/M-Qs[B544h05Z6ZM!jajwL&4Ri搚i4\MԄҸX~]z]\,J N N0lӃT Ӄӹ6' N POPJhpjbQN^& .Q8 `4-.~P4%<-8-<-Y O O R*U1RT*TH"'5*9X,DJRGY:0DFOu3cJ|RGZ:bJS#n(DUҙuj8+ SP肟0rU}j\"HS4Zr ZuPԹZ*ȵkT3Z2&j\Fb_hUqGd Q}*K,vFyڣh+PQ<$VXkbHj!,XGZÊ,tq3*$U\r*6JёQ2SJk~VIm%ڨOk׊Rׯ'1=fg]\־՟5jfrUڨMe[8~RK?jS2q9-qګtrUIm=U37{V얾VKojHH=ܶN[|v1䊪k T*Hţ)+FXU:3&.ZOjEFZZe*I\i)j+^sOn^4nёNp9EOSTdjeWLh̞:ՔDu*MH h%4`7zRsLc@jW5QT'JƢ=*dg4֥ 0ֳesIҊnZiwTdnep"5X58=du$1YԥDϑ*ZGUڳuKTuvP2{U'\ҙea;?jp)QTQUxQu$^RXkZHꤱR!4UJXfX{UdI WxVHjTCT-i>WffUEZ-DcR%FUsS+M+VT>arJՃ!ڗ0rJU*.bJe)3W TM)4W ^MS)L+V~R_jB`46Ѷ@Ҹ K:pʱ_ˣ˫;)|W(V tݘ%-mv)6U"ѶHV$Xb"EB˜FjV"ңn!L#5 01HR(hZp 0-8-<-8%F N @ OT:#NӒ*TĊGK5f8}8qRE[ ,R!U +X @Q{UG Z*V("JS#D#D%]jҭĵ 0z!ҪD*CF⫱U(jFh5n#T5n3X4lzV5F&k #hFDx1s\F闣5j#Ҩjm\6F-]VlMҮՄ UeB*2tZ4FZ?JdžN~:V,Wb!-fjG%NYVJ4[M'5}"q*Ն5P MiȵVT bCZGR2CU䂵jcIoU%n<Z[zw~k_j-trTVKG5-Sںim=~բ'3-S˯IgUeT̜NV[/j-urU)U37کMi]L~FkOjLS[{WM=^+>{^zV񑋉oTT'82U%,NX+21q2:9 ldўM*Q4ufm{SJUc6ծ{Uy uCPS50=PRn:Td*6ڡkoj9h*&ڵ^ڡx)ܞS0M1VCQV)ߗIp_ʥǗNrViJrJVVhVVeTʹS$c P"n Tl*IL"#M%& h(-8-Ha)*@𔀈%=cDu"@,u2GOXtZXqEN(jPSኮC 0#ڮEoRE \8=tG N)2AS5:ES$TXd XHEHX#"R"T6֬Ƶ-NW]qeY}x3>{_j.,Y}xQ9Y}i׊Ϟںc3ڪoKkN[j31q1 -UڶR3q2*Z-D֊D4x#u{S+ϛPE1zze*2MWSRD25JUVZ3ej=UVܛ7^Az.zijCja4L-Vjmi11&ƥCi8lԬj6jhi RL'5 InfuC4D᪾zK-%T OWfѢeՒYjHh2O0%M-y%ARrBSKU!jazijajCZ04Z!-fBj!7S7RU&EȦ7U\9E;a JN)qXBiM4Ӹ!4aIE`4dR @8-TkNi,Ji☬NPO N8jx8&ŐjUqrUUjRU\R5NUՈ>ar5f3T5j3U.RUn *Ur9/V(!ԥHlqՈYj :TqCXʩ#b5j8CVㆹT"$X}AS*Li"<[Ve|ڶ F>ԯdb=Wڷ^ګmSG-VVKoj^~{j-RȚ1i<SW!AuBZ"fu].RǚFcR!Ġb0U*$\ښmOީHS%'j"\LWڶzToTCͿW1Oϥf5|QX}ڵizTd}zPmF:VF:RaKS }׶^Kj`o^H+rH1UeڭHxjڵ$Z&fP1RyUm6+Fʘ4F1Q4"Tj&5h1DzՐɤ=('4ɰҘқ֐Xa)ZB#i\ԻsKD!)*PD#U*E,u"Ru"LDtN#=cu"LdiOt􊧎*cEҒ(1CJlQأ被qIuj4`*%LIՎT*ԪԊZxZUiT*YCT)֤Z4L:]ML͖HjtjS#Lc~joY5i4#z loV~kFҍ-Y=\F4cneUjf.esS,z䒦I+&n)9jsVM#j)S$Sո-3n9s,s՘=T5e$2Ϛ!5;58MPE2^yy#D酪2#%'4MKIf+RьԌc<a=Hb+kzڵ>T61d[{VUک2l`MkTf[j5Z-~՛qe]d־B{{.Vuŏ^+˯=^+xn,:{/jn,zYx&Qںq+>{>WDfbrUY-کKg[{T-o[Z{Tk[)/52QJE%1(IP9$xR)ZiY2N*45hfW u-Ri3HvZu3u&zjQG[:hE\*8ƴ5Yj8XEqE}*eHUZE5G*+J+ Z ij1;"TԊ42e^WaUzɚ=ZJG$Qn9r9ѫ_YQIW~kF5"zT"zW<]F5LfR,P=8=fEI5dV_zͣCZ9wUfwf;ĎެGqYRfw:\V$wa.=Z(YA5dǽL桡DMNVxޞT4Q|IJԡP4[WUdjS1ҥQP0 O JR*԰#`프V1:R@^*$5P:ӸCUe*ⱍ-j[AUR%kLgt[gTڵR3q9K/jΞ˯iYuS3q8}:{Wc=gOe׊31q8^OcF[WBIeUںl}VfNĭL""W2PN"* SSD4 S樖:) !jCEqjMԮ;-Iw+43u&M+aKSI1vff٪Yi M1j- 'i SKTԙ34-!8Tt⥲Hi`ךR%N(PJz2(O^Tܫi*ӶҹD`RT)U&&ژ# admM&E1Uzz)E&1XvpLH,:P3J"VwELFijCD41RNhmi0jR@ZZ`@1KNuS@P@8.NL; x5JW T+i,XCSjTi\v.DjFFn&r4anoֳ 8h[⡳UV;qҲ1Z8JZ0'JoZv`n\3Ђ>Vg.f40Q+3uVゖq.f#ڬ$8:#\$^b8Q1S.fZ:dkVc䎦XcZY90UJwYg4 V5CK|qrR[_j襵)sZTں)m}jd8Y~K_j%Z[lBբ`ɭژajHĠaWu&+DJ,Wd\UyjER@ T5WfHcJ*INĖUaUVJxz,;3HNJ?4Jøj5#W&%5TV T2Sћ+jVd5]ڤR$k5DJQMfjq9bHM-+ ^ڋSS{TJk=b:cq؅b*bV*. XUXE+ JJSWXjE,U a HՁ=cpS$u*RtUґEqX51NĩVTQXAJXAQJ*ĊHF*Q֕afiSq()\,PN)ҸSb)wf&R+`՛fXGQOV,z +66p5ѩ zWG5; RTiZHC!Y Q$2sۭFjFL4 g6HVѴԛ (0 SӄtÔhZV+RHT$TW) C Tv+,*VjabR,^hCR,TUXUU"O\uXR)TEOU`!TQ):RVEUQh ESjtZ2-"LSr*=P;N i)(GHV)M++*R1JZ3SҒ.re=F*-"d5:: ųT1 qc&jVt5~#&&zV|=<⹤tDZLjZu5E:em4i\vsN H(HU֤SHv,ՄzCP\I*tz!e"=Xfj˱Z}jGf/jFWaE؏JSET1cS#SJ@#9FƀcOcQ112CmiiP22ҙlV*49 1:.#*Kpjղfh=&Y=}ڢk_j"9Zں&=E"?*MHK)BK5D4iV55+5DR#5DJ͚&Z0+a婅\v^^IMH~ijijajaũnRCRg4sJZ!sMqRZCQ@4 4j qNhZSPOSRE5:Ff*FDk6j*UZ N\Md!U-[efmdZZqPH+D+7ZRBɰ3HM7" 5"T ✭RR,)JT-5\,-!j21Rg#4SȦa(}iVu`QQN`E 0h4-8 \{S\,4-< JpEaSJbӁqؕMJZhV,jGX)"MWjˉ Yj;w[w;wY3xjo'JözԷ`mIҵ-XVr+RN#&+F+^ #&2p*rVD2W#fI:UR_z%f$VJԋ-df9+.9sVlIW!zȊj Ջ5FOVjˊ^r)k6Z/J5V9*tz.]ԊX*Fi\,Q*$UUZwaJXzִTZhɚ*,u2LkDh̚:2u9ZϟdTfӵgNkxH;pjό՛tu+'ZϚN5Ľk6^ uDM|溢sIwZDQ`͚LI։\&%V7/T6.j&2S9ޫRNԂr!hZJ%WqJYDVj2;RlAFߥMfb *qi\v OXq HҸBՈJUq؊8*p{TVゥHcڬ$b8*[TbR*Tڗ0r]>arj`koS%) گ%J)s qZS!{Qց1T'i/jR+,u*GS,U*O\iNӒ:#; HdJzGS$Ufi Y&jBsIF5 *Bri:Ԗ@¢eY)LeILtZ1y~ԮUUUZ|_*U8EJX"Ȇ!qج"*T +Þj(*XQCPj8=X[T6hAe Ք+T {ToKv(=_[zzRToWRڦKj\)%Ly ڧS8[{R֠=d|v=\TmoTK`{S|HĠ!^oɧ HdڥXE`9RTqjRtZRa:_/ڦT*\1Lts FbFc)0Jvj\r{R) .RMcm!Z` Fڛh) Sl NⰂ:\,*1QTܤQqUcj*H W5Bj͚#J֌-Yp ٪5`~ +"XdkC&*RxqKY3T͈1YVjţdtޥY%g޳h3TOO[zqYZfTV2TsFc]=럊=WF7>v+za]{M&tq\ո}랊5n+zͣD΁.}e8u-3d\cⲅ=iMjl]&ޢyꉹF>XW-ZYY.3j%YP^\{)f!ֳn$SO7g/Z(Lq';7Vtֺby2k2ۭf\5uDJeuӚ͜)jVT*yҎjVc0+S h(5iXWU\ BjA 4,c4g Znʟm&چ GVt**q8E"nUx:b":x]b*b*WC"CiWaD\j|0ء-"( X {T9TX=U=ARM(JymUڧ|ڮ,*K|Dڭ,5*SrSҔ Uc56ڵ 5TCF[CQ+I5fn&@y4y>w"1=c>HiV TU*QH⋅*UԋJҸF#e#W*\|0W^Q1{Qճ'JXGNXq=cCeF9RT4HDJU4H3VcZj-NY6h֭FZf*U"ɖ˲\{In=i.3Z$f,)?Toz(űI;jI$SJ(L;uW&j1LNiΚ&jÓ]9PU)hJ*tDe:Z"s[&bU3o5;/4Z4BF(IVsMZwҤqE$4qQ\TS)٢B J9Cg8%<-<-C,%<%<-<-IV O RRDB:xJ%=cV#HQRua$:(1J T6Z': U WbZɳDYH#Z6ia*b"/jaR:RXaV RX\v!XQJO Sq؈GA5`%..)TƊL1UCF{Chjh0QVI{U܋1n&FwXVc޳q5R7ޥ~xRzĵ#p^{ޱ=~=jyK6~Hntk>c]'ϴZ' {^ިϽB9V-$5WyHɲY%&iM孒1l%z+u$UhɐjjįU%j2ZAzUqP:Pj*dUZD̚!aL"#֮XeRwJFW)"`"VOVlj jOY*-TjLPZ,j)(j4QSrЪ*eZ1RTSҹB*T5VEcD-\Uh[fEȇr:qfjqՔ5R6ѫ6Z.GVRf6eAUѪt5 LQQT(x 5"xഊjE@Ҕ^j`3FhW1RyUkfhEM"V)y](*Eڮ*RQ[zm[{TmSqڥ[oj[ojmyQ3E)Mj jSj=)s;[T/o[mj=*1j-ҟ0dThP^\,SqS^Ԣ*W*eZP"K(jdZfD0*UqP&7S7SK 7"^%Fz}.ꑒPN .5.$-LgJV&*&yjE M樚zv& (zEǽezn}\TsҋͥVްޥ[S;wS%߽`-׽L^B@~2]>~2]Ή.w~%Yޕsެu\w~f;zsެu\w~f;zsޭGs\W~n+ztQ\{ը=럊ޮEsQaݎ| -cEqVUY+9&j@^K%XED4 r Q@fJ'z@@FOZMҁ =)NcQ@ƞy=)Ԁ )4iNiOZa(p(搜RI椱(M=iք4%QH)2R7ZZ1 1Jh43ABRʴ *UZEZVUZbOԲ" 5QP)POC1M"i "Y]en %R3eWZTLDYj2h0تReY&Fк; ?XPZz>Xޜ.yYl}Y?i>6+QzA8ùn3ޘn+&5C-5n3TqDZCRP%J\ZTh%SS)VZNCB2uڦAPj(`NEcVc#"%X)Yj[(|q՘ƕr() ,ո᥊:X-"45:@*hYHj*Eڝqү,4'ڦ㱘m'F0{WA%^*\,s9mۯN[ojblaI@VĖ] m0h<8Pt\,R+L+V:֋j'8Uy:@Sj#Ei*&JӰFjO>z,4DqYhF)X.jyhqKqPSk X.Yz-A$VI!Q4U̹.y ꗘi|N¹{ΣΪ>m/JùxOJ':4XW4Eǽ=n=0OOҰj2{JRԬ;qSc:OSaKzϽba'hIsV$ެqYRfWXWbzbfi$7^zzḫ\gMCsӚ NkޯqӚɢΆ 9ҳeqV⛧5n)j@IjrVdr՘iV(X?9eIEVqEHWqUފ)Dj&E u߭T"=)R(iSIRe!IEQE&7JmP(Lh)1Eai֊() J:EJRE2 (Q*JE EH(OS(eVQE"SQE$1枴QT!iP!i6QE*Ehbhe_* CLLh(V"x*QLD *J(Dei PzќQE&48=<=TE;NEX N)5SD3TLQL )"R$QH ҭFQRR-r'Z/Dr&h[f)(jt%!=DNjQLBEDފ(&LuS j(hH{QRhKNEbQ"OEd͐m!*F4M3QERfi<(@%7̢&4F^*"n E-PKBҊ*,iQERd=@QT#~QE(N4Q@OZ(Uz(-zE kVZ((R* CcQE&ZM<(e"EjQEfEȍ]Edv#W#4QRe*Qڊ*@}=zEXVZ(DS%S: (f>j1TZEC)!Jt͖\:(e T2:RiR J,x.+SAEI,RH=bi"b+V1EB+H*QMNSTj(Dz$QTd5BgD3֏QE OK{EH i'(QVlh@(8EXC|<(Zx)Xi,*MErd5:KE%$:ME,dެ$QPHn)袱e=VLOZOӚ(Y4 hC7J(H *2QE@ˑIV㒊(ukui-screensaver/src/assets/lock.png0000664000175000017500000000147615172041035016530 0ustar fengfengPNG  IHDR::J(IDAThMk@jֺV*[7**~TPW](v%__Ѻ֕J *ڂmm.*]Jy%rrIbs`yL#j؝TY|Mp 8ց ЋQ013[XRK;i4%*L3g` W*p&|Q5zTfvzѵ*s\ʲF4:lfM }lf)8UulFOن>t|)R[ 63d^qo: .r5lj2tw $0 lZrNlJӤ4ƢS=Ep XML5l$$4 M8gf;vF'D`f%2ؚ|m_u"Y) Hlg4#]ߩb/M5Z"zzjUCm4 OjxE/πk-ԖD^4L@uCTh+_. Amjofյ'vhx z<1ڢ}|]c(itx{E0T Ѫ6Z5V> Ef:lI팴֨Q)oPWXIENDB`ukui-screensaver/src/assets/capslock.svg0000664000175000017500000000066315172041035017407 0ustar fengfeng画板 5ukui-screensaver/src/assets/suspend.svg0000664000175000017500000000057215172041035017270 0ustar fengfengukui-screensaver/src/assets/logout.svg0000775000175000017500000000067315172041035017125 0ustar fengfengukui-screensaver/src/assets/login-button-hover.svg0000664000175000017500000000076315172041035021353 0ustar fengfeng画板 3ukui-screensaver/src/assets/shutdown.svg0000664000175000017500000000064615172041035017464 0ustar fengfengukui-screensaver/src/assets/badges/0000775000175000017500000000000015172041035016307 5ustar fengfengukui-screensaver/src/assets/badges/ubuntu_badge.svg0000664000175000017500000000222515172041035021475 0ustar fengfengubuntu_badgeukui-screensaver/src/assets/badges/gnome_badge.svg0000664000175000017500000000200715172041035021256 0ustar fengfenggnome_badgeukui-screensaver/src/assets/badges/unknown_badge.svg0000664000175000017500000000070315172041035021651 0ustar fengfeng 画板 1 ukui-screensaver/src/assets/badges/lubuntu_badge.svg0000664000175000017500000000135015172041035021647 0ustar fengfenglubuntu_badgeukui-screensaver/src/assets/badges/pantheon_badge.svg0000664000175000017500000000077015172041035021772 0ustar fengfengpantheon_badgeukui-screensaver/src/assets/badges/ukui_wayland_badge.svg0000664000175000017500000000124515172041035022650 0ustar fengfengukui on waylandukui-screensaver/src/assets/badges/plasma_badge.svg0000664000175000017500000000235715172041035021436 0ustar fengfeng 画板 1 ukui-screensaver/src/assets/badges/xfce_badge.svg0000664000175000017500000000112315172041035021074 0ustar fengfengxfce_badgeukui-screensaver/src/assets/badges/mate_badge.svg0000664000175000017500000000072515172041035021104 0ustar fengfengmate_badgeukui-screensaver/src/assets/badges/budgie_badge.svg0000664000175000017500000000065315172041035021415 0ustar fengfengbudgie_badgeukui-screensaver/src/assets/badges/kylin_wlcom_badge.svg0000664000175000017500000000107315172041035022502 0ustar fengfengukui-screensaver/src/assets/badges/ukui_badge.svg0000664000175000017500000000072615172041035021134 0ustar fengfengukui_badgeukui-screensaver/src/assets/badges/xubuntu_badge.svg0000664000175000017500000000147315172041035021671 0ustar fengfengxubuntu_badgeukui-screensaver/src/assets/badges/kde_badge.svg0000664000175000017500000000177015172041035020722 0ustar fengfengkde_badgeukui-screensaver/src/assets/switchuser.svg0000664000175000017500000000151615172041035020006 0ustar fengfengukui-screensaver/src/assets/ukui-loginopt-password.svg0000664000175000017500000000064115172041035022252 0ustar fengfengukui-screensaver/src/assets/authdialog.qss0000664000175000017500000001217615172041035017742 0ustar fengfengQPushButton{ text-align:center; color: rgba(255, 255, 255, 255); border: none; border-radius: 4px; outline: none; } QPushButton::hover{ background-color: rgba(255,255,255,15%); } QPushButton::pressed { background-color: rgba(255,255,255,40%); } QPushButton::checked { background-color: rgba(255, 255, 255, 40%); } QToolButton{ text-align:center; border: none; border-radius: 6px; outline: none; background-color: rgba(255, 255, 255, 10%); } QToolButton::hover{ background-color: rgba(255, 255, 255, 15%); } QToolButton::pressed { background-color: rgba(255, 255, 255, 40%); } QToolButton::checked { background-color: rgba(255, 255, 255, 40%); } QLabel { color: white; } QToolTip{ border-radius:4px; background-color:#FFFFFF; color:black; } #userWidget{ /* background: rgba(255, 0, 255, 30%);*/ } /* 头像 */ /* #faceLabel { border:2px solid white; } */ /* 用户名 */ #login_nameLabel{ } /* 密码输入框 */ QLineEdit { background: #FFFFFF; border: 2px solid #FFFFFF; border-radius: 6px; color:black; lineedit-password-character:9679; } QLineEdit::hover { border: 2px solid #FFFFFF; } QLineEdit::focus{ border: 2px solid #2C73C8; } /* 大写提示 */ #capsIconLabel{ background-repeat: no-repeat; background-position: center; max-width: 16px; max-height: 16px; min-width: 16px; min-height: 16px; } /* echo mode */ #echoModeButton { background: transparent; max-width: 30px; min-width: 30px; max-height: 14px; min-height: 14px; icon-size: 16px; } #echoModeButton::checked { } /* 登录按钮 */ #loginButton{ background:#3D6BE5; border-radius:12px; } #loginButton::hover, #loginButton::pressed{ background:#9B3D6BE5; } /* PAM message提示*/ #messageLabel { color: white; } #messageButton{ text-align:center; color: white; } /* 切换用户 */ QMenu{ background-color: rgba(255,255,255,15%); color: white; border-radius: 4px; width:250px; padding: 5px 5px 5px 5px; } QMenu::icon{ padding: 2px 5px 2px 5px; } QMenu::item { width:225px; border-radius: 4px; height:36px; padding: 2px 10px 2px 10px; } QMenu::item:selected { border-radius: 4px; background-color:rgba(255,255,255,40%); } QMenu::item:pressed { border-radius: 4px; background-color: rgba(255,255,255,40%); } /* 虚拟键盘开启按钮、用户切换按钮 */ #btnSwitchUser, #btnKeyboard,#btnPowerManager { border-radius: 4px; } #btnKeyboard::hover, #btnSwitchUser::hover,#btnPowerManager::hover { background-color: rgba(255, 255, 255, 15%); } #btnKeyboard::pressed, #btnSwitchUser::pressed,#btnPowerManager::pressed{ background: rgba(255,255,255,40%); } /********************** 下拉选项 *************************/ QComboBox{ background: rgba(255, 255, 255, 20); border: 1px solid rgba(255, 255, 255, 30); font-size:18px; color: white; combobox-popup: 0; /* 配合setMaxVisibleItems设置下拉框显示的条数,超过的滚动显示 */ } QComboBox::down-arrow{ image:url(:/image/assets/combobox_down.png); } QComboBox::drop-down{ width: 30px; border: none; } QComboBox QListView{ border: 1px solid #5187bd; /* background: #4682B4;*/ background: rgba(255, 255, 255, 30%); font-size: 18px; color: white; } QComboBox QListView::item{ background: rgba(255, 255, 255, 0); } QComboBox QListView::item:hover{ background: rgba(255, 255, 255, 20%); } QComboBox QListView::item:selected{ background-color: rgba(255, 255, 255, 30%); } /********************** 生物识别切换按钮 **************************/ #biometricButton, #passwordButton, #otherDeviceButton, #retryButton, #OKButton { background: rgba(255, 255, 255, 0); font-size: 16px; color: white; } #biometricButton::hover, #passwordButton::hover, #otherDeviceButton::hover, #retryButton::hover, #OKButton::hover { background: rgba(255, 255, 255, 20%); border: 0px; } #biometricButton::pressed, #passwordButton::pressed, #otherDeviceButton::pressed, #retryButton::pressed, #OKButton::pressed { background: rgba(255,255,255,40%); border: 0px; } /********************** 生物识别设备选择界面 ************************/ #lblBioetricDevicesPrompt { font-size: 30px; color: white; } #lblDeviceType, #lblDeviceName, #cmbDeviceType, #cmbDeviceName{ font-size: 18px; } /***********************关机界面**********************************/ QListWidget{ background:rgba(255, 255, 255, 0%); border: 0px; } QListWidget::item{ background:rgba(255, 255, 255, 0%); border: 0px; min-width: 128px; max-width: 128px; min-height: 128px; max-height: 128px; spacing: 0px; } #switchFace,#logoutFace,#rebootFace,#shutdownFace, #hibernateFace,#suspendFace{ min-width: 128px; max-width: 128px; min-height: 128px; max-height: 128px; border-radius: 64px; icon-size: 40px; background:rgba(255, 255, 255, 15%); } #switchFace::hover,#logoutFace::hover,#rebootFace::hover, #shutdownFace::hover,#hibernateFace::hover,#suspendFace::hover{ background:rgba(255, 255, 255, 40%); } ukui-screensaver/src/assets/scrollbar_down.png0000664000175000017500000000017615172041035020606 0ustar fengfengPNG  IHDR;֕JEIDAT(c\HM(05#)D׈,U36jĥiFրU#!x5/ AK.IENDB`ukui-screensaver/src/assets/login-button.svg0000664000175000017500000000076115172041035020230 0ustar fengfeng画板 2ukui-screensaver/src/assets/ukui-loginopt-finger.svg0000664000175000017500000000567715172041035021700 0ustar fengfengukui-screensaver/src/assets/unlock-button-hover.png0000664000175000017500000000104415172041035021514 0ustar fengfengPNG  IHDRJLIDATH?hSQ !@l]J?)4c fp02XRS](TC %f"nb*XQ儓yr|%wodv!:K@ .~61sXO"4U๩8j} &gC=|AGpޚzA dCʪW*as3qmzdK6c,]=b +e'yQ( wZ@ b4଄ TX6;*;+twy20tpw&< sQdRcT^i '|@d?{ cXPR~KǯST}QC@G m !D[nEy=[ȾNhh:XA2m^ 2{|Jzz92݂$2Hj d_ rF N.*D,eAIENDB`ukui-screensaver/src/assets/ukui-loginopt-voice.svg0000664000175000017500000000330415172041035021514 0ustar fengfengukui-screensaver/src/assets/weather/0000775000175000017500000000000015172041035016521 5ustar fengfengukui-screensaver/src/assets/weather/17.png0000664000175000017500000004645515172041035017474 0ustar fengfengPNG  IHDR>a IDATxy|?>gfO a&,ʢH]VXWV },bˣP**AJ@%la K͝s~ܙ0$}^y]f9s>r>s$@2P @T_$m7FqmwYvuu巷B1X 7746~j0eo_|`DӴ|Ν79rr]\.x<\.Ȳ A8!QJt](u-''g٨Qvmzp»Ted 0B Y!"$I"BL皦AUUh]9QEY5a„]z饫6·v K,eee2YprzJ<N'DQXTT$nt:ijj*/--%^1PJaigAUU #%UU潈K01ϊ$F4PJcN>33 LjJ+mPdڴiFx4+n^ZZJҠiAH0z:ԿTMڊ`0hU "YGAp7b13D4AUUaapΕ1c+**j6+p6@s9ӧv5'8N\3tPv `g>2mmmEWWTUE4MxA "!>@$I `DQ(­i UUP(@ Ji9ĉ_,++54M{{II<s9Z>DGGb("bt]?$%HHMM4oaZB0:i<~H$PVV{tAl`?k,kiiiBJJ F8ITV`~hmmmF0@~(e#ldeeAD@ɤ̍Y[.DQb1Ť6Jۍx(@(\.SYYӧ-]tZtH< e<''gtaaXY$gffS]*2%lg7sh2_eB pM0nj5XN̡iBͤF(&Ij[[[>K7"OKK;dp眘C0םL t`ɲ  ӉZ=3D5&s ;0!8^;N)=== Goδ^$Isu]G{{{BmZ=lS][Ur20؇79㜛EŽt3S9F?]KYb,1\.S#(v}ׇ~k} N @!H!RUU%~ߚҕ fGdk88*)))k̡Ձ14?[d-|f=Ƙ'ڵkСC[-g" 4\n0jp_[%b8?xiK2N,'&qaT]cs.D4iizsΝ[@rss/_v^/qp8e9nU5@)MdivɱkdetwX.eo7c@Gbt/I&_zYJJx5.vg>\`T&v|[NE対Jnzt^PP|ϗNB&aV_*'+9&Y&NTUEVVપWcX4pZt xMMMW^WHII %%%$'''Gbf~7͂9\3mrw t؇oVbߓ]dOj5Bb(=%%]UUU+Ztࠔ.u}bNNNr(++À8yUV*YZcnx;OU{L6ڟՖXb:1!DE᠀T `Oj(B1CdvH]*dD%DսGw&5hf!˕tkI}7/RG#&Ϛv8| Km۶ᐲ0j(@9&N~&xvwHdJF~2jfR(BBf$+BdP((^޼ |„ kaY?3:j(I8s1:f?BrA2k‰D{9 fҜ6WEኢUUy,cHu BFp8ۋ^tE/**2I`ĉM2:Jz!Cy޽7óU8fֲLOX'*'{=v-LXV5Mfp]׹1Z"@4m0` ~"8CD@}я~^eeemw)1޼H޹}`gzw?{=+cW}=Ƒ;lnnF8~%3 N4L𲲲SRR,˪ ۝8Sa7N}Xeu>̙%S=OŇ9x`Y҂+W\`oq 8Q0BHȑ#1ބtA$uzbzdd5ӨxBޝ9꩝|b2hy$h$IxHMMszKN}Ȑ!}>_,˚233c}O69Oj'KI֩N47ɪ^CBUCYMmnz Mp8 СCc555X`vm=8/z0w\̩O&eiyKJC;Ⱥ!Tz1tDQ ,D$.Iꒊc;w|߮_)8'js.r"fgw !bh&V X>35njN' LHO@IՉ RJ:;;šC۷oiÆ ߜ;p@Ñg80t)bqo}V &(>x"9G؞dͬ)i Xb\ycfzf̘QR O@)9J[[[`9)dNlځ& 4H b5nv`&p2I2NT~oH0$Is$ID._r :TAxIڣ$*vUnJ}Y5j?9@axћfI&vMc>1RȲ̇ v~|=z! Sn7u+B<qg2&IiR+f-;ZFlw=&~]OfZGw%n:9;;`Wt8p`WSSS4##a$1 y i'v[;*f<@H̲$`Wf' ӓ|[{kLp}A)%xxmxhPUUUUIIɨh4c qn } "'VrMӈu9ۿ[fY&j{n3dm־05i;KFis?'tlr󶶶# =7OQ:8)]]]h4l!f =|Qvošl(k{g\vd?"Fz,˖-[Y__ c,Ez Lq߿t3hРlEQtX 3ߪ O7; .7o7ۓMw|wdY쎡vؙQ6a+b6oU yq=A0 @%rQ,V]׉nw8 )V2jc7II`G ;d$V3LݐH$cdG.]W1Jr\ї88:A۷o~$h4ʌ4%TU%֝-ܓdΞOnefo Z' +{Rɘo.uCF{fj(<P(*Kify@:O$ZD) VUUUq8iiib4x.GQ27dN] A`{Ow}ak$DF']yݕim)@HA#[n .X]׃Ji5"G6nܸ]uJeBi& #D"(z1%SvOxaBaURoU4u $@7F}=D"¼y;pWF!BHsi8FݜB(B0A!hmm=PYYeƍkkkLQ1`QTu1:D"x} u%@nRo]i$ g2o-;O$AOf!,4p"{>裿"> SJÆ88I J)Ut]" !Nnݺu )ii>xܢ(JZVVVܷo_BaqBG Z=pcN=]ZEQ4̲TUEٝY+ivvfaJI k׮ ,Xoc6sĐM t]!UJhB8A02 G7XC&knUUt]8ǖDy2ޔ|b^Xݻ~f3ngy\UUbPaÆ>H$rpbTBpsVbFDQ@@ Άs.qzv0{MBM6}5t@  Hj3תRڃL8{0cXmsHu]2f^DUUs/L g+(  Gf09u]XbEӼy^j`L^=Bפ 5 vU߰u͌u@IIxHQKZo&'"ָPMΤ @3iҤk.±>Rf, :&`=XcUaSbdnm2]j0I㍦pp\p\̪-$`n 5kzu333sfeee^p$QMh4vtt몫k"H3'v:(XqGp2 #|x^C[׺k+Nw;钹<!f?tҤIKKKx^̘oHAmI)4kBV4yMkp\,syI{O\.Wbk;pMѬ0!s?^jժb(S#)cXL5hFDBhxQaĥr"8'W_ M-Tt2{8L)U9"j3 x(:cL7:Cqb X ֬YSjժ:::!cFwpe#'*Q߇BB7I1QD\ !Q IDAT\=I 缷jKlpJ}}K+.8\4$@ f0,RJ=1n!@7!)s@|>_AJ<_~).+ښ) KpԱ45H/%++kC=￿v[ !ys̐ !⎠8RQ}ٹYiv$ RcXLD"J0 uĩO% .!P-uvҽ^ouI$麮uuu@g{{{(~ĥSj3GG&SUS3&G[5%?{Np(wdӽ}cpHC2+z{Wvwy#;t]ojkf"O<4556558:0 >IMLP+q D9 pn:!p~ C6C60*@q%v 9)<sʛmc/'ee¢jnkV&:% TWW?F*\f̘Xeeӧeɒ%oy7u! 0̈QFѪ1q"X3 n0݌Uh!J {M8eddZ Ѝ f(;8(o4,v Gq7m(-bt}3o+[^{;H׺HX x:8gQKM򶷷z1D"?ݴi\1.''-[TF"PJ6QaN!ڢl2c8!<@H1֋& Oؽ{Q URUM+Q~ě2ẩ=p?WQ JQ8QO <)̛HP ib HմȎo6'EzB$}yͩ"77wfuul}?*˲1A)us Gcc IN24O0_yVMy`8 o(V ;zi M;?g}#p{/ k rp+Q6!$F)qƘ ^'|z*@~h!@XT@k]+撒qhtW_}k7pù]tW\q؊nQF]c=2v17o-psͨZGr]w^?$ MGV~6`z)z–s @V7='ImAb#Ua(1BHs *J) nsH鿜2;="9{uI.$;󖊊?O׷Huuu§~gҤIp̘C-^r&LpݺuxTTTfY iEv]=':`N8% ܊xG 81 p moͥSFD#AvG o^N(ء"a" R\~qiS"t+K6mz#\hmmmp8;tP .ȫwuuiǵjժ}o?`ӓO>??OxС:y<ܚؓlmՖ?o * \tJD 65gQq]'Kv!D Vr=E1= ,KU׬Y3j̙o.IOOϽ?+..8~Dgձ@x&MoĈi󈪪/Kwq~ii\SS綴cǎ??/ 80{Ƀkkkq(]2"Bg@qw9eRyssǜ?ڝ.^̦)S6k; u \R8:!D#pMZ_z2bh:KWggg?vQ_$閛nnh]]7g33Jgۿ-[~&OMXQQm'xp8!C_|^qeO:{ǫW?y\oJJE)++sUTTt_x^[g͚5q̘1۵kW ]w8oJɀRZzc)J!ԧOŇ/c}Nlݺ?c]9998?(>!) F`2۷!TAAAF~~~bqcڦMj:kiii8qPM;m-  طoozݏ='_}sȱX_}/7557oW\Leeg(jVR n`w#B˞BT^~!9%%媝;wNt:#TUI))'ۍ춚VWW7x׮]gggHOO' =zt{cccgkk묔<uu;UVhuvdᄏ+##{Ι3vYqaC !t]g)))Œ3>MIIAyyz?rܑ}l>^6S;^~jjjؓO>7"ph_-FXLC;pKnNⳍ}FgٷH{<A&I@ gϞM (̤%D[#F 455@wss>s.OMMeYؿѣGw:Xnݮoq^)??wyZZZԁ:ͬ#A$Ip:.ϟ_3cl-Тz-Z7xoc_1_( 4TJG36~p锲[䧩JSӝ [Y@y<Oqq1;rH.IY~;,4hĬqae_~?ի')#yhw͝;wc,ww@˅Νtƍ[u]7?Diի^{U[ls=:|Ammm^aԤn;x^G!A)3"fv?9q΃+<F)u}7cZh4ڠ3o:&:e_,[3gLUt:?]ƍ+&IDI(~敕eegg|ͺlQs 2gΜ?l޼jхRMMMۃ>#GoW/yR \={﫪^o .nٲ%m۶oVsApȲ,ހ+8Ã|7;(͒$UfQc8.ĝYo^ yioA[] d͚5X,n71A {nذ!y-5 ǐin;%k/¬ ߋ/7XAI7=QsD3(5Gѩ]|7>!##֥N3roFFFιkkp}2I2&]Y>Prܺjb/*u]7'---k׮]wmɓ'g466{ ]tEi< Yf-YxWU1V +WDQLg aC)\fz@ ՜Xz"A(iT{9iq[48d[?'B'vttd| [nQqq1 ͢(FC6k`8e9NaR1y_[ nB<"~(NgYfffZYY766>=(׿U%%%dCAh!ii'b81BHPP2!B)mB\xT Rش@ &D^|_dddpÇ-##Ɖ NA.TP"sB2G]c-esYvv6/s9眓SYYׯ?녅۷7b1.Cd}&"11+Hi4ǯt] x} @u]w!6 41۽{vY7%%xСƍgMpXݿNAR92駟~߼4)'ΑwTۀH |c`k۶myww>y䑻.K/IM PJ2Wɞ߃n(Yƺ04W9'J0'B.4}v1*//OmkkS6lذGDjSm):!l]׋W90nLNEhA]p}?2BBψ98yҥK_x߿iSO=ƍ>={_h6N,(];̷JTύ|8cϞ=_(--}>DLӴ(~ιs3ҍ̕^GhVUՏ8d&833۶mLCgN%I)9/朧 :z!uuuu0ӹ4 c$@]]]i]]SCܽUUUzI{6;;;2eȑpb~梔f22gJQ)!añuK5 |޽W)2ry[ZZ?C) u}؈# -pv.X|{xt]|7g G%f,iib|(B^g"ڪ-eee E9#ZWW~H?:euh#tc*Z3Ql&#33󎦦شiHo޼˗p8dT"] 6J:HiJA ޞSQQ1q,] 07nƘ1Q 0aBahټ2&ć{l l\pO>b:;;]]]Æ ^^^>)33Sg=u:VAb)whsGxuuu%%%7ڵkG]UZZ:wީ,)) w5jTyydYnΝm۶Ғ{#f fzc>hG߾}|嗏 4={hiiiwmڴLII~u+V>qƟ (TTUmN:QkA.]vժ(߿ҦM~L 8`ܸqw9rȑ#3 .|F1>G)pνbŊ[<ϢAFg"Mnj{Ś5L_vۈzg-Z^yy˗/?PRRoBG4MKKKܸq?|IZZڋjN]u]4mt]ߏLXN !۷oSO=5kҤI?tѢEJ~Wy睜6 IDATwܹPVV-)))80uԻnjs3!$ƌ?D"+ $ 1ՄI??3Nwg{Lc]@@饗*EEEkG}?x@JJ6_B֕'r\w߳+W<9p5j2Y/~g̘n~~MMMM|ժU/i7 Q= .lʵs"X|/`0kkkÇcCi7oVvikk~i;S#?p'e7L4=rh/eP@ }GTWW-\pٳ3O?uo*--M3fеk.Aqqׯq[}:*w~kG@H;̹u~5G xIF>#?XL2eА!Cٳ'#lذ/llnn\1cƔ?;@?e]QVnj}Bgf[޽{/6mgG170133fs5Mӫk׮G"9{U|y˖-ܬO6q}o+;;[ڱcǾk3g|]Qo.=? :ҳHARpzyu9p7+***׬Y󳚚ƍGW?~|n~~o߾'|pcǎUF)22(ǥ+tglх1J^q0#BW>&e˖U>N6s.䒬niT~~~Q(KNSk)lݺ?ڽ{7UUUq p?m%-Kq$;KW,Ei_^x%EEECo޶bŊ /pՊ(455չs΀(nxaŕgϾtZjKxeti/C?ڃvq}۷.D;v566}>ݩp]b޲EO'&,# sbR I|KȩE-_'K9Bu޷UuM;^pcxL<= ,̅˲l[u]/4MӸx♃\~}dfgg+`0{'&&|$89>핕'nƬW%2 A!& !sV8d-IRP$ d`)K$YY13 qqMbVVݻ>}P(LZZZcLeuJ+$mZB*rg2a 2vqBHs9g1%H:ԉ׽KG@x0=tPJ=?z-TUui6m|ҥ+**.u[CR tl8#XF%zثieY5 ca IDATx}yŹ[է>2  2EQ0vQ14Q4qň 7FcD`4F#WMe`}֧]33~TU~k}]-$@`ІA}|d[nojjڽ!r`29nFK^^^] ˫.,,o?^NH8K.=r޽{ B}5MFq\.B@)Mc RZx*k>/((8(Mm@d?h֭?F>@>n$1BA)u2 0 Iu(H$P(]sss?2dȬ#G~MpΝ{ ~a)))HMM5$I2!D$*In7\.dY&B3MӠ*4M0("555@`#~.· bŊUU=#''@p݆p F^/\.a0 UU( (0 BD"PifhF[ZZhmm- ~u=XXXX# 쥗^ƍ%!nn"77deeAg0L?AQB!455&ajhFjkk(cǎɹ瞻c|+O*++o7^/|fiii'999x<  k]߫p8Z@bTUU᰼o߾u]7.zS|G}tn8qn[|RXXכ`H|k$Akk+0"(SXqMUUرc/ ? ޱ}n [>⤦o /_޵kWv^/ O>$///Tg(1D"477# !CUī(LQinihhhvޝdff&~''_KK gEI8{\.x<x^Ȳ 333я$IR I7.++2~?RRRH  . T2ϧBr׭[w-ܒ,xRIܡgnzI `YYYK%~+w^},'ѭ[7n0NsqUez F|V^^~?<ŸݻIhN$ZII`ʲɲJKKNv-r<?7JaW333ѭ[o鴏G#k 0 444QJi(byyy񺺺ӟo߾>}IfN@r,ːeG0;'[,7\dڵk],iͷ'C,N!p߂PJ---L^z飚ݺu;'#$QM<t݌$vۿ#g!$[HߏsxAL8CnBƩ0==lx0S$,==`03c`vI $c;  ׋܄gaQo)o? $Az匌x(>mڴq)֩5  ].a\!6E[յ`ڄ~1^QdyakFO'k)q,vb|>b˹񲲲}EW_}8 B)Mw\(LUU$t%@':Eb:8$9<4d{!/Il_; 1:1 C,X̶m߿߲?ЙpQJI,F-0- Jp%1 ۝Xa|'n'sDOvY@ 1M$MӴ`0;}'܅o9eY ;k=hРK~*AxvZ`=tQ:v`c.79"DHv'KhmD$iСCZn]$ !cƌytԨQt޽  )))R  bY]Δ=\#H&qΩ-%cb|[NE322K,q,) nOwN222}&D,'s}4VNJ։e˖qX@t677_A)%%}܄x<fk&'l `Db:`mI${Q+w -lRԴkR#rss>ϝRs&GV98DibEƈE3X%=8~0 Bq\M6Ƒ᠄o B8H'xbn8,77Wz6dBa!k9H9JzGսG2@8*$8+H3gwƌ1r0p1cƌ{7nLGƐ!CHjj*!_7)g3"0'p9Q?{/JUppg"(aR/ n֭OFCGy睽EУG#tȐ!,++7q91/uH\#pj ۳?kx/_EQ(DUU A} Èbx<;-x8޹ˮj`կ_?ֳgOkӻ'x=C6Ķ8zlbG%hcN@,Z4 7`3+Z(illD,-..74(:Itڵo ς ҥ JKKI϶'KD2Dd?{?clUѴq gDamm-"ȦѣG=r%MT0JKKNIIn[RAAAs|'_pd 8 :s7'V*X|C{c;hFmAE!n|>- \hٳgj„ 8AQ_Aͺ7)##effrul-gXi,ԾQ[t98yȁ@GŘ;Ȳ,k@TTT<#oAG wAjjj<EﴼKj;8M8|Nz쿵DSgxmphC:D\P?^QQ߳g&L0&/K0GxM_wJ88av%ũdB[}wm!8\.躞Hn"2e}oݺ7|sq^185rvv@|>dx<~~[V{/AxitG LH[@kwru"IfWM6XjWgye=dfddtz wʘzJ޵&1 "v1(VmtIVM3---Z޽= .\/О7FPJiV H<>٤r)t#&IE-` =;:ƔLӉ`H\sov3/g=쳷”(c,r<ib(ì ow{_ve?-'<9999^W驪vXff*b@<'^0Ɖi'{;%ECHx[Lc^xbAG'I%_\nOppĹ9ڭ6n 1X,Vy-}X<H䰼=/aB9z~[&޾S]նe쪿=TBa{^o0E@ /bѣJh4,@ d3I2m@T⅋/~'ڃ-~בHv ڙ;bu'Y1B4UU)0[3FRv޽f͚5/袢P(dȲ, ǯ8PޮSK?o˞̀N Ѯ d-NMCHvcɘ. BP 6@[0y޼_u.sL4"ގeG}{g1mqf5JiRs7:I+ؙ'̉4u'tl20c`Hu )(Ƙ@Jkkk],.---MB:O\A{6^,x{귫b!QĶ۳Ng.=iZ#X,q.\"IdF mr pߵkN{Z^rE 99av*p\>h1.nnn-ndA'@%s1;(Oo( D"㤾ommm, ӄ;3mk1x[lvzëXu]'0L0/D/2_4A%xL 23m;CNE3AnH4eak^* 0\8G7-aӦMBPe L UUs7A¾wrxܩ]dfo ' m}'۵K;s4dKEaha]UU_S72bHRJ% yvܹe˖-=ONzzz!X3.|X,&+!v5l`L{*NuN6)唬dmFI(2 6l͞=/`n=n ۠Z@"x^z}EEE~ϗv)!hfFY4eVB;X|礞xaBb(J(w,v3L8Ev&P|ܱXYXLzީZb5&Dch(!Z!B@]]]պu֯^lΝM (a0(:5 Au]hXo!3=GO };lzMnN'/L|'e@p)D"E'y^D(KS@*GqphM {w嗒$I)iii)))@ pۈ>]vuB!a0BG 8>)"s&PGEI 4x[',hޟݙ.5c)%---:׊+ZfϞ=23gc) hYzfVk$fuS.B̵뱆 `Ȟ={0aUU5]%ܒ'[InΝ=|@0'~f3˳_UU*L@ZjU駟~988D29Ӊ7 0d  CAB{͚5K?`РA[ZZt  S`Wvj.mτCj=G0~3h{yi:'f^DUU~/J<,$(#K . /B; !Rq`\v=}5Npa2^p CɣwygNJJ魭:cL&|O;s 35F"$I9,'GN biaY=GppSٳg͛￿@5:@!- "?+\ pMw8?UUu۶mrrrzfggqRD`.qJ''ɄaB!nHb1&.@Ykv{/³%Il}饗>.//oRUC)ID5|%ĺ3;07 HTTT(֗^z/>U< ڠMݢ$$)0|0%g uKxK5j#F8հn \ -uL {FTBؔfќ$QɆ,˰h GfI<ǰ@K/_^kiunݺu-((螗 Gejb1|wyyyE4+C7hRbF6hS+C97!/`="HcԒTRb@?r  P 9s kȾ!qڒ8Rim?i1I;v|l,ˆU{O|>_bk; p¸~Cuu{XxBq+Jb.̥R5viU+!ey1(-wQtn?bY$ck5TuL `I***sg +))镝ጁYx-BlV)WױX uuu۷_~Muuu$I={-)))ٳgϜ7f,3I@N{˗/駟.mhh೜4Ϲ_jB4} N><,2v. ϭ7CD7cu.C`8cL1 S >mQg:҇~8?0РgffJ<;_ii`/~{ɺU!"$?D v-q=ҸŴ#|a tMwȲ{Wr11.:E}/Y\__%\3f̘;u)i\1Ǚ,D4rss#k&0K4vLi H8f2=wyg/ǣ4M[4pyBw13++kbuuOKKK_{ߝ8qUq^ |8 O9s|zy m2YF8,t֬Y_3(gʔ)/3}>_ /Pةab)ZB%[C󱉅*uC'fDTuQ5ax?ɓ'1%%Žo>ddd Nްa-`pM]](8pɓٱcG,;aYDȃs… ͛7ǯ/_R]]mTUUl߾}(++ceeegO@O"w2HًYZ-YSjYYYsyys+**&EXee%kjjb165ڳg G;ux?ژ4i/n_͕222Lu_^[[SPHkjjrs=#Nz_^ΰ;tĸ)3Ƴc<5*@99|\❲k Hnn^z;nr\ -ZV=jԨM0 /G.]\ƍhcY{L {w677WX7|ף>ziMMb&Jf͚eIIIV$aŊt⯨Ս.Q2Obb׿رwRSS7xcڐ!Ck׮wxo \믿~(tw:kNB~cƌC]֣GZ]]*ׇ8޽{狏90%frda (((O>{=2?e˖ʉ'^3f̘̤{={3g^={`PڻwUUg%M@k捲2cܹ;?6lΙ3`>}^p\:u͛7 !BM) Q@mc̰Y8f1[Huuz V3ؾ}H$~-:t+\hZVQQ ʤ g'O^o>ֽ{yw,;v < c$sN^NN=?iiiQ׭[WsW;w &̼7n\Qss3+,,̿/MKKì񌌌]wm=У\sV2%q.+C4!$1@SR6 @!^:M8cX [?Dׯ~X ! 0 gՖ+0hmm͛ujjw݄߷ QYY~Yf=Eie)8b Nc&0+XyhɑMMMy6lU߾}uҥ',&Pp}ٗ>-_u 7|[nm۷oJFFb:ysy Ç5jTرcg._|U8eY1 \jܝ \ޒ$$Bȥ0DWe$]@)C$e˦8p\r[}urIǰf͚'ǖ.]TC㎅1(~;xhf<#k?ͬFWE{Woܸ1G '=$`,d!@|]p\gQJ/r\^oc%5557nxUUU_{ PQQ񡪪/+ٳg[zf͚Z~M6_g1qF={}=`w 6>V3ĉ(755pcl$|?0r] S 8=$ٺuCvbk֬^\M[hx:m`Z( ɓW?766{MzW\1g?G7nڵkWϭ[nݺ] sM!C~]׍ٳg@!b+?s3[&zHt%^PJ/$i0,X,Ny)S|޽{Ν;h4n喅{gРFt`gd0+ 4nРAEoʔ)w-?yz̘1[R駟4hm{i3fxQei!0zqƍX끙8V"I7̍]l0!#F6gΜr0?|}JJJ믿~󫯾h4#˅h4 ×;mTI*++{BEVVBq5\t'---6|G緿oB(lʕFJ (..~H$$iUڵkZ s@:0!~Xdʕ_.z𫯾> 7V}daaa 7OUVJ_z#P3{fXBeX 6}wqԩS?WUU {XL3 CWU;GL0nݺ] }ذakfΜ93+(Rz!5_^~UUU(l $IzR:fd8tPmٲ幪iǏ۳gn{s΋vq3fL9Nݾ}{駟,; _`kii1~_|6.L\/[re c}U{Bx#rJpJ np6x p1@ȑ#߿AUUңG,0D r7o^v}_}m߾=x>ܿUH*?>1 q֬Y_sR:t7'$_n? CZG0UPJ#uʉNctPr."tc-ZhϺuclΜ9  /0 cĉŏ566uDD,:nܸc$IJlj|00ٔsp; 0r9B&PJov\WH)t*8n(y֊ܯ_EEE۶mK.^y{.h4 0ʕ+[/>3x㍃b?+V;vnjs6n8ݑy!(Hn; 0I7mYs;rz^ #J[&M.n ܬkD"e={hk׮mbE-..@^2eʔ `zhf޽sNyQ00npIs8T=N}D믿>,㏿*Ps7"cm۶3dY~n[FX,fDQ#wtM3MMM=Kc 4A}3c} w?sMÇSkk sϝd!dg?{7ؕ{̝DRz?̲ׯWYYx|Z0$˿v\Y1*AUU躾ToҰ$I8O~=z{[׭[7:ÇzoY lP233i:c̳p55Mz~t~IGBI3 #rMn;ϸ[>h\si_OgϞ9s̤6b\.Q$P]`ޣؠ~oYN- B E4={.V?^:ӥe˖͚5kѣ/0 Cҽ]K,󧍍u3 UۮWRJYFinD^hccc!}%B2c^a3f̰kϔ)SjllgYXvS=UJe9zY\,BHoJi1LX`b322Bc־k]X˲`$%Zoc8i0p ߝ-:LY'G߁<@!c;`n7X8wgQtJЩHFy$"~o_ru/g2`' @Z !~,{PIENDB`ukui-screensaver/src/assets/weather/31.png0000664000175000017500000006604415172041035017464 0ustar fengfengPNG  IHDR>a IDATxwŕyrF9$D0`km]:c#dL4 d$I43t熾UuΕ66\_IuK?vFD5`0D@.썃(~uJM[^N>l#}z& [5Kמ. X >03z0`6tx_< cP!| S|6^pee)k@-wMvAb=T*{x^V_P;j{Eo}꺧mϝr^# r"  ~4%B)GHdT|'AP"Xyw @g}v(g2y~rؖh?Dp;/<󪨲U\Vȱcɑ6+ 03k36Fþi Z`@q@H}c7F3> +gþa#6J) O[, "|92\aEm˲XI} eBQԢ|c_iD !@ k ˒ X>|e'b Az8Xkmp$"eI`Yima1l1J1JѾbBzv$vuYWs㺞q w}2L}_+ˉΘF;1xf6Ckac`lf+L|x̜LJJ%F OcH$0|4lTp˒عc+ld2==T0 u `ƕJEMgDz@)L0ӓơ wVZQR`kf eF+EB Ғfk#O{rz#۔6ۯ-^9-됐(+WymV*ZDmy̘LQ"!4!f&)DLc Ah-u8DGau1j|PAgnù6ojxH@vxϮ4ib#t8&"C[D8x0p{x2B,2ljjO`_x#O/ɺ5λ\ejSkbʑCPP3߬ ;㇟p|eK57hq3X)Mt7R #{vw%/^_K,yrgh k[V _|g|.s^ϥsRDekUGg> Ep/_]oڷl=1QSiRuRFop/%h́ǰm[,K²$FjF}AHU~j H8=HX @"Ac4 uBb)%$- /'fE-K2 Y'' KVWPWvyoVVg-F=۶X)EΨtlXј%R'N&H#\]kO4zGN4nؖ^wdGl-yk{㩻6mM8MDܶ{w_}Ï\k:…׾"J5 h?BL%BCGe? !$Qрkw0Fh @Ahۻ#Fԁckv`"aqWW7b3kg*k됬1BaL7llu }?#N;}"3~ok$;5$ 99ANDQabfԥ)ǎxuhұxFlZ/4}gsr+<~v{X캾hiC\ys]7$#eO>\>-[ќ9 ];QӸa(Pi*e  }wdͺ "$_];CXu`~ $ĉMwROw7 ݻ1xP,Kh;{Pp=yܗw…wfGoں @1jyy}#_yTw>Mذ!"4*!KrH"!,KAf}C:3OcF@μV%7O7YdO}ܧom ^Ѩm\'cܸhݽ Q[-P[[cR Q_Ş;ȲvfFOO˗wv%/o{K bfIDdq,r].x: 䢇NuX*ʆ5}1T"0CJ>.fvBDQYV˦jL)IUex?]YJǍ/~$Ӝy箻^l@4j[>Ü_=輢HW\qy,D\=i{E$ G'8HGMV@pH$`{Mx8`fZ݃c3c4vԩC 0<ɁXF /2Q<5,H )eA))÷,s$d3w*G"{;}E -B 'ׁAL0FӄgO5v+cXˣo[ʀxafy(-Suͦa#}u+gqIo\!6׿ 6λ.g "GO|gA+ |JX*l PѷVohSGѵQAt.5 Mؼ%\(b8Vvb@{߼yyfm-[BER"W1i1 <% oΆ`Y/h$<̙G^%<=vډʸ1FR!w諗(pkr튀dfQ6d +>4yy7:vƄ7Q۸oJafO% GYcG0t?nBXPG3qɀˀrf+"Lk,S5k`DGY;| CiVM׾@\8LY~5Ĩa#BAL"G|2JF<}^x\Σ٧MSO|VxOU;6mcBܔA6P`"Pg91{3+lےR1;z@|>:_~{s^iYJ!;p啿1[Z֎;{%IZ}~f >@R (VT7DzeJ؄Z|۷w;cxU%3'c/ZI̓quSk-AiÀd}:ԟ3Ϭ)ēᙧo=BMwˉkD mRRD`#$03I)D]! "&_g?yyKdY!ӓa2KմMV{h~kPY']*ő%o:LI2  ka9d񦆤,*Ll"dRv:z^xn-13k6 +Ḻ U-$L`q3@(PxJ`h#xwM7a@DxрǞyvXb3`&x%g[Fde|">}Ldiw\~J=xL:y%ա<~$MRLHXڂ-GlG-{$뗮2#Blڼ]OF RXD2ʡ/-2#F ^0=PH) T˹l~e =8/h |473gNo܎ŋ4aؾm7}tt,>?Wpө+$qQTR㚈2`Iy0 NO{Wdu/=k\܋+m~m䨲[XBc; (1c 0DE1K)4mG~nfBmT ?O OE @wP C4p<{Btu:BXxɷ0fl*#uxq!%PЈD,zѕ (~xcG-r]B·$ "ެ8byi>9 whZ M2QYEss3׬չ`XRPeʑG ?Xp?}_'Ϸ-xU e9H0Dh0*2$)̈́ C}9<e ʸ1! @k`Y!0 ß_Ο*g<S ĻvdusOݐ]ZHP>;>NQcY&%B(>%Q6j_ D0 @YU ,ư''߂۶{X=+`ฑ}MFGV0m};y0hd}Hض%[A!7"Ròdp_PQ_aar٬wԳOK_u|ED,&"4J:@ImڶŶ-9pHF9r"hOv {{^a Bæz3t`@r*4NjDP A4̥ hJCHʁxg.ԥ𻻗@nr**Mg>>>p}i["?DZs`[hjoi@9hS>޽i$+|6+?)'l^{{e>LeW9lM4IKRbfF:bN,Y>ٴq;R0AWDBLK{ ؽQԫC9%4QmBpE0EHZ+x Q)DH5Xu-)H,l QS',U[F 2ƐaUu ~}{ A$`6 &0LVyXWrO zHXf30 V4ڷ3J\ᾯH2aOv,Ҥ V"#oeVL x#DCI0%0"@"e[$"DyW@ıaN WTd |xu3Ky0?}ca$|g"iѴuѯeY/nUaYBRKq R~1R2%+v+̈́McNy:4``4%:IX\ƎBb e4zBF%"2&)`b"_vx9e+, g`lX\ &.yS5̦` F a +K{\$2 2Eo)\Q`V>u .XQ""H@ B( #;\s43k6F XZLFy'LvWf[l^`kNMzeɆgN-3>E²e(& ņkDIvW|.D LT R .)_pVSb9PiHsϥM\\ao& #(0Tء)'LZJ'! l 1 ]sRJSUQe$>seqӕ#jjr#m̶]rXS詷JTѧHtc!Аg˞5}sbpJp}<p0:l@}+DVV-ҀY"4 kC|n e9RB6'V}}mɳ}mܱiP/V$v[MI[W@~TcBQt8#? D@@iw3DI_\ߎ(`K_ҬwVP0| (*CdJ(>(!V֧>՟v4ca!}WZ${'m֬dԎŢ #>yҕW?osږ&,~s& iuvDӗ?\Cۉg]h 5e||]fQ ^sQ|ؗ:SQGyW|Mk7筚F4&l.kq-ՋiC &6!KD@@c\$GH8rWeJyTG|P潑lkŌG]xOs u_M?d iؼ;v;e2]]qxو|%^=HJ#́́˪ /d_sD6g\p9[~f YP3>$Di+U)<϶ 3wD@` ϊC9P+[cơ߹qc8k3gyրϼct5uף>)DeBGZU'"B2C h$@P:|KL4-E( 8s SׯHAы.xډ'ԟծ}qjLªEm7ًG'0>βr(u߃8q{7>""n IDAT۫*}ʉ=:cʽݑI\N 'CByN B`5eKM =U'U% @V$zz|1lXr% :>pϽ~~5 "뷠7ӍH6 H@JMm9ԶlWr?G8]<DzY:;Ӟg7w7\=-iѦMR=jLeֱM[]]f0! D+IG[@pߗׯtԲ(5g΀O]vq~;6yڋ,:FT%{QiwPp(1H+43Kt wYb7;h/C Ǣ)Mm]i`p(kNĈHw끿zkJ'c5|xS=:5b˖֭e,Ax] :YmP_A12&*͙ud1_?]y\NUUvM_ڱ[7'g;p=l]wLO:i=;㑨E{ Wyo!#}DڋnmgON$BɉjOV{Xݻ/кtڞѨ- dxx%֎<Ӟ_\G Fs975n֗#8Ib(3kF0h"''o6zѢھ*V]BY_2=XbSp珿mQk 1TX42bc!I/Oo@~'IK/]D"\aaWDF{~v䘯ޭޒ֯B2)O!Kx5K>둗ߕcF}Öۺ%6vTeWp^7}o(QJAg[\MĀ06-[Q u7`~ ŶX/_j۶`kͶ8U#7VpQ%JI|ŀ~N>ϙDBd͛$uz]KƏ[s.тƱe~$_HNv.t#%B"" EK<)%I" Š1c6D0j;`֚1l`|?u!'&IIBJBdHHE01]zwU/J9K8_zyްcj.k{޼qf}uNz37kir1 _L"N+x#*ض2HXDŠ28"BID fyxJ}cy})H,`o +8kck,ab"VQa'jjHeuFcYBXfhHKREE̴Y HzcD/r*~  mY^uըpџw&(1PFv@}wy }/_wt7/|CĆ?HUT~3‚S'' 8W_,&E}D,*7 __wWj J12(e @ Ad:JZ3d ll[0 m,!$`YDBS@m ê޺={L[{pjH<䌨wPH+ॅ/%twm2hI _E_O^_\}7y-fά0wsyo^g}"!_?r@/<:JY5&T1"9Ү nPƘ`IǑR1Z)bkm'V!!dR! -dYCBi-JIR2[&B¶l[AY/۷gOa_GgC{:g;"w[ u)C̪Iq<бӥ'ܲ?~:Ž|~ @uo;sNG>SG]w]zh]]^We'"QYJ V-k]絓HX5&U;bx*eU(֚u`T6^fU>QB=XiJk.*s&Mm}XGwV,%l[zqDZ,&DzȒmaS²Uvc#Ȳ A9Kxk{:{L%gKvsj`2KM03 ml"4INq޸1roBA= &{~KO&川imsOZԸ_Yّ+4-چhZ(s^p3VAAd1靺#m^׹~j۶TmM#0qN?~xw_33n뿾e{V$d&[--~'xɡZ3Z[ݎ͛{6nimu;]xR*B3M{`r;/^ߖJhgd=g]VZjN{c̭瓟 1lʿ6{XyXK04Gۣz|O?yT,JQڭ ^޳i1g۶GN)i:IBڒ6#U*LSŚvԦ.Zq Kz~sEgYcc?]ByA/Wn'Q7rÿ?/^?bg;{??{޼e cUDO8kVdRmYu;zzٶaw=GaNCdߢp}bKK>C+v xO?w +oܚy34,?%Extyi2S9iy ȨfoS-, +D R) -qЙW|󺍛;S <27k[ι3?G 'sf%L]H]T\};_=}v?a-g=$bT#]<9sZfqDn?s{lY{~:* qǔq\06/j].zK`ӎ}MˎIap7z;~G}a"F8 gⴵҘ}6 ʦ&-(K>\j#`1 o#f.[!nSYa{ L ކm[l2l-}=7]F+tHDFu[X;a=nVg p'/Oe280ZN0 6x͚gΝXLT.‹?T! ^t# w?{l=-("=|"FZGP:o0 w0͙99h盟m=/G*"U&Wh]BhuN9 TTFX@#;p^=?o[\ܦ/$ޒW.YLJj=QU. G̪ ,\${}!޼&C|k}9k^{oCw^YQﲥ]oKz->mʶQMk8Wd9OC>ub]e}!'-ݷTBމ!K{~p$v|me*; K&M^Knhj[MϻN#qD@rƌ)rv[oԂSz͙rG GJr!X¾* N[ٙ3(F͗=fĿ?ȅ._SeÚ1kvѸ/{NZ\"mt~Ͼ\顽ڎ jڅ&߸~sQH|*k]L5n\s[ ]oRH"-zéGL sj͜ \x c1j֗-j#mEVq3q*[1֩ay(ŚidT.Sy53* a~ƾXR3^?uȑݐGr\{8 V k}ܒ.y=9>0i׽3/lTDxkV==0tH,atY.a+/֘'>ȏ <8TO v⑈袁8VY3{_Ť -}}=:5h zf~6۽tkVN|0(Dw?hTMpl-F 0Y w)hc !vRrff5FS0J m Lο}Ϛj/ށ^qWiV]ɝ2~S76ۍ?Ӈx&D/6G{RW dDQcK߭ر"{ cƿy/b r˓c\2b#[[]?98H|@l޼eLFE >nСdOٹ3߾ysm׮|G:2ư+%'n0inA40%Qi "aF`1r6{ޣ={-&|J+ˢʹBh2QnEl;s=߽XKSNx Zp y6Ϙޑ P6໅1g^GO[_n؆^> }nEۣO߬yӟ<^؛HXnEh\w݊D>l)rISLprvv[޾~}B1pm;J*Tyz'f$VŜDҟ쮺~WQװ*tE2 ydf3ӹ+~T4, {9Unw?"CץHpwWV;e i'&~mW OrIQ6(aUJH|ܮcGڭk~bIv2V?7]FC%&y S!IuIip)fAUW}H9nhH=bڵ'+,XP$ԩ}Zሟsܸ W,fΝe\nq.hݵ}{:2ŒaRiOV$XE"|%ۿD?{[GZ2FLCwP\@aq/8ꁉ/Tu~Z4@<7!~X,d Jpk )X7u "DY \Y^KoxyO  }}4$M⣏-ڰ!c>VAcZ?B4(ϠA)кkw!U%c8~'ylӯtGfw/~*X~QK~Q_?05u~!QJXU4{^Iھ4?ߙ*O?|VyEŝV|Pu&dH>lTFb*mQosw6מORgfRG%I@qXCW:q1Pex"y.niѣN'ӧA4n\;,+e]{m3w۷7\ɒ{2PЧM필#XOٸ [o]oOqc &!2~3g@6cFxi;Um5n,B[4/QF}2B!|WVQ>-lξ5g3@ ГvŲ_.[O"uIGo2 \w'4kuOk,WOpfeB# Q XE IDATSU p2N" Nv|,{;㎍E"X1[9-/w Vren#o՗u-t7vG.gH=&&W^(Gxu !Y}DHDimqH%R5`j4+q]%mg\={7o.EE֒ߊxsb+ު*2Lŗrs@ dz=ԣ~swA7|v:oosݮ]}UU:.h덼iq3/gwq]i6.}sj\%EjHy-8X7GZ;~(^lJ05&k O'(px~=;wn7Tr5B@_x`Ŧ3z/R8Ff{Vo_wOzC 퉠TV{cg]`_'?Q<Ntn͉uzᵣ'ӛOyDund2f&O,f} ׆CF읹571l٣Fج'|[PU)jQݩn_/]/_:"}P\._|tEMyi̊''޾g'Ӌ}^|03_7_板um;ǖ0W2xpZ0pOv}QL5. A!aBƅ4u]p]cCRLC1LSJ)&H2D]zWK|q'kpMqUD3)z9g=$ uH065%[ZP8FZΆ=(eZi@^+8 _#ЎEu1i<-THN~u?_Y<`ԶQE{t8J eܸ o4j!Cλ뚚ƙ3ϯשnM[N+_]U6;g$ ln1}Bc]s]*Wke$cQ񸙌ǹHd2uaHTsP)%H,$B`"PE !Q`F0R#}]6peu `CP$NbRrX5Qmuz$"Ѩ4cd"䬙}{y(" OBvZxI]hKTum3镟,|hOۯLtՇ?{Yk_tpA55j1o7GPP&L zy96#THԼ3{- :g>յp+jk=4"Bv {{kنw9kntö]ش5haSSBX-vJcqfSSt }5{svƿ8 Q[ctOp-nZ͂sh1H3&s@ٯ2rѡ:*WIp%$(Uʞ[w BkZ=Fۥ6Nzm뙵vma\1Leoo]JuZg/ӾPt?L@%GcgC{v8}djۑͱd*5'qduZ(n7fe)Yyyjn~#?7ב$sxMzK\]ܳ'2Y3MPUS$qpO]kj>^T-yrOoB63y{E]zGiVl.T8q6eʙIH3i]2NZ~pQ*bV1* VJBcB$I%UnƙaHNj_$(:69: !-!\0,QQJySB"%31cDCLQ.z~(Ydzr+þG/!)B q3А߷/xu$b!:c 4e:ℌ8tȲ֭ Xfy,f:F8]v?m-kzz4p{ {W45%[=9״)Sٺٹ[@R)""N'3I!PӴ]HX74  ɑ4MM)1BV#Ƭ?cX* F @2\Giu0 &Lr޾UbF1LAU =vanxNRU@@DJ&r$X}}0D)E1lx (A.ҥKxwݵr1ECES>m;s}u7>g5 V4 ^/Ӝ0튖|jC}( ʨ2K0?|Gra 4KB!SL$燛>CB9έX$"p:)J%AҶj )`0cP)!A(80 Jb cEDU)BɤhkhHjkꒇA=i<2)Ec0 A[oZ/OV{ I v Y#CQw|Ü ^_xpɒ% N AW^FMym-VV"#{v/pPsAyv%G&\K&MS]00M)us]<aJJ1)#BPj aL00kRb6H$;,  !DF#ujfuE#L*&ѨiiуdCs aÄB Bʹ4MSgHG1{8;$a08Gp;LwtkWoyreFSFៗM]utM"B YTzڻ9.ǡ-s^u:Bɓ4Mu]"%$S$)HsoxjIAYPFfApa "p)H s10#aB`L1#h' B sD$bFB!=j57kFk,fĴ :ȤeDLVMS3f hkp\߾qwӼ]}+7x ;,?46-3/I ?+C @;­1|x={lܺj*Ӧuirѭi5|\mqmG s|KN|_=}:2'b_jzSν/~??j d.~uI8U *K2&| {ߛ.qc@C|G*,tw=0, @ƍpG#\-D˖5ص+fʻvfWxq䨹7Ң"gњ5[,h"PW^mSK="DO%,N96 /XfmEMolp8Tޛ/\rn m}$Dᗙ1C;tڷW8*-B};e+4^~g@#6PRJLBv "M}_T,+)q8򜅁r~gxz`p`{MnX7ߴn7᯽'G*lܸ O<Εgѭ<P-kXbEccHs:iLhxj d %?;Tj:cmCA}O"tzn|y\8ViwPgG8'_ ~{1ÁE L&e$/"7}I DI]ƈ* qz<(9GgWrE1cܬaAtBP4Ioܝ-6Wik職u@V`ƈRo{Kۆ,RJgǍߐ\zin{1# ݉jF"Tm1$*wm~Y߇ovک'*;_h:q y\ARVa@ko|7A!p0B@ATU&ތ 99j~NZ{<,1D(B@(:ؘ1yމ+32+éqv`ʾrɒΟ;["dMxIVG*.7W[qͧ֯v8DN{"<teĈ Jt@JR c"G \"]z  ĘPz>L(4eiʈkwqG0mY=J5p\C9꽙$X IDATljNFC, Ëw*-O+KJC n7I ~TJ P%cY.1*%B)%A)&" juuuɽU[x}xJ/4GnRcz)\&R>F*Q\.>˾vW:֮m޸dI:ԩ~rJ9r(Xp:1Ǽ.q1UKb(DZj %%B R *!əlqG&=Z+uvg9]]<׌:ve<<-R{3KPِ dICR1~D*d!-<lc0խyyݯdggtGV q3ʮybU.EB BDDv)c*a u]$B!IMT8ITof s&#=hs#fd i^u/^>(.G}b~!1XV`45bEckaJV0"U_F&.kTyZqF %}GuusWW@JR"B"i@JU4 `PolhH:8pP&Q3l2pBq}iOW^.Nyر$w VrE>YboΨ&+g#pӍ#^{}VHeXʹteg+9|g`alI83MaKaHi)lPpM6 (S3Pbv`c3;Ds.[*6BʰIq:%L+HFm/k ƀ1>ces}2܁3%;0@"afa7 '"1YР55immzK<Σ)ٍ. CJϚ5 } UR#l^tQޠC/J$ mƬ֙$El؆P`i>˝ź{RpBHaWaK_YA#$BHΥ4n$\O&LrCׅLI$V(o)yD-gOLC*Q09||a|E!gv@wHPƘ Smi A` 8Xd5un6R40d0&SF9OPFO0@sd"Ѩni[A= V%1CuR!iJSJi I`N| Wxc1[ݠ0liV/zf$Avzu:7s57W߹]@Rt]dᰙhkcF,24<}Mq BX`)CTU-˗Sc UF '! Pl3sӈ\l@,0]`ނv9,<V'X4jDQ h$bb13aiJ=Ujpe$YJ'B18u],%/𡬯/++>=2{MS0ے}[v)>6q3&%9g$XZ&RShimÚ&dWv[/Hs!N D&W:(CA`(Xq8HJ7h)E̞SK6eL 0iBKuߐk! Y,S48B|6' LLSQʮ)WZ\lIR8R%u1)YVsyc̟?[NxeɴΗW88(;@&@Lѩ\u6vne"E)}TSqrS9][(S B3AKR>rۦ^X%_T1 jt}'}liH,MEӖlXr0uZ68.v*$WSWW^ŗ8eo~td? SOq Sǎkȥvh[ڹM4-95EOkǏ&qUz65D[>08ZߟLS'{6aw^v>}J&eW67kN'O' iԯS'JhԄNW\Q8R,׮m^}{hgJ nEOaxk̘&<LJزmI4ѩi@]dR  ͘Cek 8q[޵kΛo2qa IDATxy%Y'"r_իzh-Kzdnzifiz04i4Li n`0`aIm%B%[JU*JケE7d}*[>(ɓ%2߷x^iW+J{5W+ 8u'|{[Nw|_*bNvnM%{7}]SG>==Re߾$Nw<WowE[dk{v ;Ҿ[JpcC+|d+AK`*&#&NOL09 0<>ď}~(mϾ:X%ֹ6k9!B˸m͉k%d՗}oqmn_Ī쌞Dqd7>x{q \"JF, /Nukgd8wxto3Q/?;mSԁ=୯%UWu~+McTkj GS9vuwQ\Ԁa8ps<;f~h˽ Pb4IF? :s/}n\~$B Mk_o)巕w<;~(Wxoo[_1v= |?_ҡKMA*ٱcy/1~9wκT% Э+‘/_?{Z(Pw$3h&cj켝W&/R0ezƯ}v4 @Rmfb_񟷾o۟u^5Ҋ6#7#aՕ+9Gu'N25f`zw_ G) ,}WlzC|eE0ñcJIf/^h/gwI9!#H!1yMA{YSZ2վ4r>m( Vɿl_k ±Lǻ~g P0`4ͭjӆ׌qwL4 }{ӱ+;`)-ۊ!c]RNU wAxarYӄ$R 6^CvN^(M؋SOFK^;N"7i̋Kէv`|Yo'l| d~D-|%qDXzV*T0ޭIJ ޘx W[x ] 1uhIJ/|%o'ljA `7y "4FF$f@/p ~ϲ^>&uA"<56+O86A12[ D0QCajL 9o5M{w& cA21b:H)|DT mi.xgm]u1w_eݞ/R)P;_'"C^׎B${ O 37S_^~AJ4frDWN4@AhLprӛ9aI]#׀'`A' [!D%E$1 12ǿsk?.jv@0?h~~=J31Oɫ8cgo%њq  EPA~ׁ Z=x;VA @,&А Gw@\[:w`p6q!c_GSbk0ܜ-GJo{A{g?׎KN};@G[?{w|ws[>=_Gsכf6>~ {ARŹ @ @Ѿh4HTQD`d y=l IKJj 7-c-b9(ce{ww'.>.;>O߆l=y-0܃hm<Z|_]T8{ً~C_TAPgFmmmLcS& `N@G3 V ƪ١ss-#00evp.{H->,AzJ.ٟ7o~Cܒ>L،x wHsAl@~UqÉ<\ԉj%k"f1\mW^/e( ỳ .E4V0>l*Ϋ sE }!"`z x£D1א:dkc ?4<@^K BtC$*t}{WݲOpW%Q_ l`@Ҁ+J<@R'bE@av\dE1H}R)6xdu@WvX)Wǰ: |04.!^]2rR\n*JC/c& 0H bcplHVT_R۪$[pHB&-zb`.4%3[Zpq`kE38cpAǿhfL"$/*r,w8] 64vw 9F ,8nY Uye`I{64Mu?xAi𰂑7W~ipU`,@]<n0[46y F/#`!@BX˵88ۻ][c;v9u 0]T>`2蕃:3`cSap 6$E`sMދl>@zn/~K9KV갆٩q~pS-s|tp F[^31oYH@b(b%.asTUsE@/z0˵v!ļPB0G?~~鏂 cPB3F2TB"-O x=іRs^$̨`k%38z %ml%Nts*)t0}ɪc P%ՊI>NLF(2)$9FXsoߊoP_|Ư `LK^"ot+ 'vBl , pPХm$b\*@bG^E@q$ rݽK:y4xM tRJL 6)X ԊDP} `2QT"X:_umr"4W /_%)0DG2~(( |H^8uRO)Z0&@2)}fOh5cf}n0>| LaJQscVg%!@q^9"rui:P?p];ߧ;A=ksvMRzjEHvfea /),, (# }@BRm2?}L1v\⺫:L%CȲ%fe[@~IX}3W6 hˁ 6ppUt"GPB }NT>v߸4Eb7Atn8_`cN$b6[ۛ[ubt87^!@Yly#׈5A+U:꠴ L SP+D$9_(:;ŧ_>2L ]9|%$X7i~1[f,͡܃ýF13BRZ-mZ-a!ƛBH~n^a 1 g+75L c I u$>E=-CWe)BuFI(@ ]$(3t J/>ZHNtlH@D N {ښ6NmbT5fMcGbޖ[;{8lNL8&&''199v ?1fFcJI9.._`y%_޽{CtFvx{V|G;n l6133M6lVJp%ͪh4}FǏ'<~B`Be2DYWٺUU0x=e!=gM`ʬ.H|>@O]]HIO ݃ݿ)T1+.Gɰѭ-Foec~d/s͓:Mh4EEe]rKzsAk c RzXZZBEвbb,C>?O~\M`; D-LkƐa=Wz PSCPLb6kb ˂Z3cL. b.Cz r/ЏS" 3~PgFG޵F iӦM+!DJDw?[gfiy sE,ˠZ$>y|CaGhĄ8H@42ԯK+d!%la _1HJ T#$@T)@+t{CDЫ&w;G@wQI2* TAKJ[?Hs _}`.gPq9IC|Q|T6?rdGc<^&_0پAɉ rFCĿe,,, \4MBf!t5(cRRPZ#M)>s_kwx-6lKJ p Pq|cQA 6^Ss4F~W~h}BEJr'.l{(Z>n'Q☚&.}Z5bq$I( MF)2T{?nzvvHN(exk}A yvy{ @+4>P[$Ԓ )O1`f5`y[h˓tӏ/ L_s+_۝Nm6\ou s DYџ9R0DƖ-[011B5e&agjth4*A=FC >NƘ] PoVLSJ}icccE;0fAT$x! ޓas=\suakha BV%h e!CZb"b#$s47RnV8wmT75 ohfhVK̐;胋k⿾5֔R|GEشi uV4b\x,K| fffNp{JZ>c0JU 27vJp& IDATÙXKuvq]UeU1QQ[l$$ l3:Kʷ/_K{ù@po?~s{Xdx-q!z\@)58f k}kY5 Hڰ$}1Z50;0`O qrZtQ;1Qy0R4 A!)d -=k./˟w~$8g {TisyOÃSݦ(Zyiz}FkMLLl-kE%UcZ>oh!vBS3ą3 qY#ItVm` D@։Lɢ,&'=r=ן5jKM;3pn1n9lASb Jn'V=IŲlAbɗ){Zۓ;.ݥUi6R8T E> n‡7lx۶n]nhű i 4q,q7;hqẉRJi:p "l^ ,(ߺǶKb  4aX&J3t&b")_&s88N{Ĥ08E"KHu=q,ګ#7LLĝf4Mn1::F1:q0{~n3L RA1h44tcy%4Pk`aQSjW9_8)'XINOZ/Ƙc3 ղZkDp9|Ӎ?;۷,Z^\n=zLbXa_Q Ybbqxq*.;Y=_)A&U狒'G}NýO,z@LXvKsQ{׻_S>֟K$FWTEy Gek[SS{ӛ"n[u]a 2gǮPƥ}?gj5cyY)4QK72$ rJjhv$!hI&S\_k)׾Itn#[46lHW_}5A@6:WwÆ ;`n>rs%\VՀ=fA HEe9Ҭ9{1ǗI195Wx4TE+ï0b%3<4wl_ҼuSjݻ]ms7;vl<茌;vF1nTs} L.(TD X\Uꭨ&(t{9G9^5tTCT JCR^Y%ѵ*- FYa-qmaRbM f3?#?'F#hZbضmaYsuq\q3g;^'=`mF8(*k v 6\(* 4123Ia &j620O_MHH+ V(vø3.$7៍DqFFFM|X~ɘs9%qqbs&l݀mn i$IE}th?_|n{[6 ($ؘ$CL2f-`LaO{ { /W5=er9嵅g\@xttt_[m޼###TO9%}.\/QsB9zr?&%g6*(( GB\> Zq\ .a` pA̚0FY_ScHwdN][)|J @MNN^;66hT3|wP<7zd0ca%z~ϯfsu [i@k zp?t\uL<@ 1o B S,y+uQU8Kgvǯ㘃 0Q0 f8&}UYxDgS?f?136@F7&S߼7G&1@ 2`{_~v̀s '^4>>.RQ^^z=N߽+ \Ѷ3սg"$ںPְ ( \C?SG'hyRx@ <A@. <_A/?rĉ)rڙQ[fns ^u4綺dagw[Y{p sRUGuYр+F%+sh]њ}a2 ! ㏿0==}D*s2EQ%lkg[cLķaəanKx7iഢa:\pY\!r;|H ,>(2ZA(./@Pv/gsj.B2A[W^MLLq,R)'I2X-IXeY]?lA3NvAgmņNQw5`$zR`7^nX]r|'=HJlzEg %!s"nǙ^#bfO nvP1}|G]XX#aFRJbf,  sRJ1\%H;|='F0\uu.`mlRk~~3H3 v\xJ7 >{/88dg383QJQ]#0Ig}/x' yz"2( QyRda!iV~}_'0c%]?]N-+ad=ㆫg{,8MSz|jy|=aqӈ-`aX~O1\@`mE(1oפ!EQtؾ} R6ZVl8( zm'$1FCL5әV iX+z2gLp]:ebwmk.+<,˨qR{TNl<ƆIoL3DRx ^V/ r"J+)`ÊjKQ@Ddiit ʊڼuc X%:Rb߱;ork`VTS1 h`aO(뉻{{-8 /v[57?xӲyvϷ73ՑeiPKLT19ADDB4O4mn-yf v@!I^0U%ubAU6}dAdY,#ʵ4CHKNIpZĽ^~;B\Ø?_as `%~Ƿ@\m!VPIҀdb_⽾IFkcj9"eR+uzRzn~~,#UB,89l:[OG9J'#U0a'80Bb)Jɶ*HfyN֏w{otAd$si@B;V ?\Z{켗=AQh]9i!J8^%J ŏ+K853w]VEa*C:CAP})HVO+ge(IyΆ )iiRLGQ$ gi Ō?)$ R*^ GIooEފ9Eas~}3<׽/|ɓxhoqq!81=@/ p2FT py2HT,BƘ%Gg4BmhyD͂Y۳׽ͷ'''%<)_ɑ=#!K)UJ!KS$iIis ?4S>m[# c8)c K^WuMPSx=8Oœv`D@$dH+@aRZ4yn4M*[V1bB4? ^v5>VvslѨ/ 1np(BZ=+oie\uzFh@ Q, !1P<^ly?PcO{X1e۷O]v;[. hԈB߃{Hd0ҴOϟ#'daæl2)d" b}e |}_w}`Q9 @s!,6-@e+%_ "_ bѨ Q#:(2`@ľߙ޶mfK7mܾ}jtzl0Hh2 Z)yY 'M6wDĞ{qJ# Hl|) hdG1ғ fRy<{3J>JF$37zۼc`Pqѕԛzլ]*Ț@`K@w5ʩ9eU|G$&r@H-`$( ?.m1`&hS^qlvZN40}c!6üe #]ZYf%"K(~&"*TSldxhF4A$,ƒI59\?s"dzxD4h,.Й#:P!`Օ4w _{w)ߩપ@*J$1udw(@3LA`7K 8w @"D!@jE\ (ʓD2J̯$ɼ8z %yπ../Gh +I^ዹ9>I~+b.0\`{7OQ8JYh.tJ}~8>KfUk嗛É>p-;X*f0R$!0 L H| dMP'?$-PN@ `Chߙڅ B&PV0(T9 1Bk@RgJIK"+1@BGgOK4%ij% J'N_o7G[hdIA0 +'{cd(B@@1s/veRqKP%4h 1@~ .:@6^J4FQ?R1{ *# |Hm٧!B0Y yѓKWD/<NR2% 0GGGR0ƣ''<'_s$|yPA  T#9~խmdV)폾Zݺ(mHF@Z`de^^3xv'ƫY4jK WT$H @aPJ >;-M $P= ֎q# )0KS)ucۿ 09_.)GU Q@*"YyyM?=k= ˁn ,D=ÜG$o6jE4w ٍ.JbD*)Ӝ7&*Lo\v(0q.HqYU%n,y C)BY!EB B"%ؙâfL`HҸ<׹uցs030``j!_.:* B,q㇋oݼSra{g/_E!33D]RT}zqpQ'>T}≫Um˳ miFIDAT+Y{.io@[zI*]Lg0b:A&LoB m$EdecW?~p?7v]8d@;>@!VR $ih0,[ىQ#\֘B}6lE7^\C,ޗgc=BpM]SzDԁ ƁTaj8{h:d:@3l@MywCcۓoqXs )5j`l:/!^x7HMH 3L`d!S߁>`Mn_5J Hyv_# h3tY3G窱*h+h]L5t f}DԂ ^#`Eiܫ NPQɋ@)e&܅S1 aICB[G/6Ӵ,*p2PWPװD[8sܷT&H$\}HV;+;QV`@ζG%Sq@%C=`kbzYurL%iAXglɥdօ\]rZm% hzY߀]2rnn@̳xD7k^κӳ Ü ?:g;;3., ^G!&SrK!ɟ,@u)ٸe~^⡍k~߿%cawf,qTUϹiWk EqX3pA ]Zw (%@ HT3ߤM_SJBIξK#W݊vY[3Kߖ:jWVn331̠*iV6B*eٍNB jvRT,@4rYߵNz.`mjsORlݻ,_ wq57A0J 8omHZ6};/8O)ۤhӶ .ߴ~eJer kR.R..!zBIENDB`ukui-screensaver/src/assets/weather/33.png0000664000175000017500000003376415172041035017471 0ustar fengfengPNG  IHDR>a IDATx}yys("R؎/6kVi1H4qm PmVFil+N؈;孲,kDI!E̝qw7K#E<}޹}o}97ƍqc7ƍqc7ƍqc7ƍqc_ j4.U?`@ct}  :??4LckRV)e ,v rAu݌x^G*?RZC[kVJ)(5|k-୵j֚4uJ fݟFq]|=c?sݞR ɲLc4**jjA8@ "MSi80 `0@jyV)q[w}q~ g~tyyߜ9s[kjYYcyjhhZjVRbLx5] ݵky]q'J)Zok.Fcq/y/cX܁RKܟzSJ4˲`jj8pj0ș^^ޮ^ ]y+ͿA^+kPJyY%o|կKp@pHw}]z_z,2˖-;T8V${ǿ8Uq ྒ$9Kˆ5QJH+B#9ZJ|GEzJ}>uԉ~]U\K}N c]v}) 8-92?O@S`M@JwZ(Fk-"a*z6I/ݻ߷՗^z$%Vz/t]}eg{n%2aewi 8v/K e$2nF ] q쎝`0ZZۼ[{鿙?H] ~Skm,nV{-`P04r ?P0Ӄ~i{x`Tj2yGq>a0d,y^N~wΓ'O޿UJv7N#9 Ͳ߳gݳgt'$n2Ma%n y2#'AQN)e E}Faż*6ݮ j%&>O pMPȃOLL2$i۷oW*,Q-5fQ`ZdM5-PΝ;}DQ`,K`0@C+qk[QJ*c 8i4lٲek!8?~Xk 6~] cl\9/ #sqe/1 I,//caaΝҒT7yh3;"cXkپ<~?ݸq!k'N|/kF]-;y{zFku}]w!/!t\|)]s?F2rM9$AqtyHDW\_ KN"~1SH);*gm~^x`yyyMf~~鯔RհڻmPԌ2{7RqY>7W.t:={gϞ-A\e,O"݋Rz=t](\#9kLt@c˖-~b\q<PC޾}Oqiرn޼Yi"bieGF(A`eSrN-y~e7Pҽ-&&RV.#8FEEQ4Z[<;}o]Z`gϞCy{[c1ợ˛!m\)Wn(^_%XZZr?rѯrLqYto߾}?~7 ,cc*_Ѕȝ Er{`qy.֎`!!ժNnDZu.h^8ͬ앃I[jd<*Q lV4۶m{7'O>˴W ~;,ll7:ɛmQVgb0 8o1VV#s%(\xJ c%Lo(R>w޴P(z]@;ɓ' ^`\ Tdݺuoݺy7l`k*q%1CJ-p9ܜVcjFSi~EKX]]u'VVV <'Y!,sg9Z/3Ɍ,ˬy*"k׮7v0j(h+_ym ޿{kZA7lؠɖ8ʂ.s ś2;77(y}@!" nap~!5XiZ;)g80 udabff333v֢hlx$QtY޺k׮0 /IRշmh4,j˖-`Tb5(?e0;2;M锑ъrͲ \6ͦD.7tJ)_%0 dIf1GeYٓO>:.\@/}orle Oj~)ٳgQTP\/Q2WOӴ'p9,//'1 |hү2-͒) @ϻ@ʩL4`es7@5su$RVsE*nwl},..:pzBQ1X&yP*JĄaذaí Gr5c|> 3 C* z\_&vkhjo#F 7Rf QV_F A&[z@\F35T?0~!٬=rʲ lJx9jbcJ |Ka˒tA`eei9?ij|Aks,l6ZT!O  ;z>HqnOpINT9E Xn-9HSKBKvUUJTMMMmPpR^*F1ST&Vk0,;S2,S@J㴍朂nE6a|h4M](RXA8`Gb08@0 KT+0ƨ jևa {i"p)PT*ua61Fky2lL%oEV+kx%5+}^nCN&Sӵnj9 j5e9xF|i?^}l_GVsg" e(jSjur0 l6'RɈ2%OyKvRʜFKp{Y*1IK2Z4ٝL` DW}a8m6ߚb*Xk:OZkmeJF"j2-dFJ~2Q,,x${^$L9z, {x2a-SVf\f32ЪѲB0\{Wev, X`|ReVk1?Ԝ)&@!Xb#5Z^XeEQD +ӗ§EJ.JVrv% mq̰yE\R. ;Fڿ&<.oiii9ce,tKV>1I8HƌJ!Wa@E7"swI&gNh jrs$`d-D eRlzPQEN^$kmy^ Tej8CCG@'e2gUV]??Ei82fU biH$1J&AZ;TK3^31:jE]5s:bESSS3i4MS2 A`@P(D edNPPXLŘ"y dI r&UjYF_2.ð,2+++ t$/InEƘiKLP,@Mr5MΠ)kvJckHZEuE^ZlpLB2KH_/ 5p) -*tIZ--//q|] ;7&a)4JEFeO8\YA|,J"%L)"4M,,V-6$+2!HkH}IЕ,{^C@ɓ$*XwfΞ={\-JKF3\xCwٲ Gĉ\HكdL-,,IүFBB$UDLvXL)*9&P^;3[ IwEi$Q9=rM@Zk =77LJ)16MSŋ%M#Q 3e3v; Nǔ¤O&z\P111F 6}HO%YW FOaw FYHZkuǏ6IDATScزeRP@x̙c^WTZ|]&=yђs'VQ{@r ?lt:.|Ҳ$}qh!т!^yxV{Z8j9),l׳Zk}ܹΜ9b.25ON ,AaI /ܞS 3^oj\ŃSKg9t]aeextG\UTҼVފI*MS}玦iQX*Gwر/c]،N%A@RCW@H:\@a_~QsŐ)z=}yy<ʪ"&5T xL^;3|EZTI#G|ÞPB^c >v򚳦eˋ+D7ѳ\n%2סS-?cy7us㲬Q0;;RIm\,1<D`4)d`$BIV eL+ZO?8?2.8.W {ܹs|L2J'^ ȅNgTǏƟ̲(| \4iR& AˆœVAOL&&& .DԔ1Xp;dC"Jv1J,2,K"Hk핔 5_ԓO> kZk/5O2j-t]墒\X]]u ͣ\ TV%i:\Iq!Գ,0<fv<ڛqh1|&-#=ϳY,˼̰԰.0Z,GY Ɖ'?}s7oޕZ{@q5BH 0RA l4쥗Ţ(촙@F///?(s9YBD3Z1z>jآ:˛C(CVy䑯aV*ft @Sz&H7CJ 3t5YX㮬sI4Mv|EUSCҋF*"յQY k_aN[.+g*>?,---aXL(@`R2H^\-kZ nnMsԔÛKLYvL9W dT1 VB2&bA$nDZ P?8O|@ˡ_qY%uRjј޿mI+/9f 4=de| '''155v/HeW$cz^X@JZ3!Y>ZDZ@Z9#xt&Ib}}}ɓO~Tz pE]\2ZkcÇ4??yVI yCFƒ4lbaUh4\cNOO;ƻy$jGƎM)DI0 ð'{ܽa_ʲj5}ȑ|౔:E.{90y!_RJw:Sr>gRc-h \KnǛG!! (7!vh\/AkVm(h,Pl\A/OX 2e'9;n0VkmY_Ƙ{ch_A<_|mVn5iސrM:YHaM0sY'RkYZ;`4MLOOüMfX~= 1% .LUʉ%2u WaS~(zȧ?0KOpU 44Mi|`9vKDc[o}uu5j*nE@$e/,Yְ&:* fgg+rIXSf$jչҺ8 ᗵ_솃ܞ˲iZc?:qSyH.j\)xI xJډ'ݸqٍVج!jȌAڳb(9oELh*{i!Xxjp&qDݹ5L/ߏ;wq6\v:ťj6n]MONo>7G7jN$ &&&0==cݺu1,im3M/4I8UER~~0MK|bȕ@N>g4`0ɹw]ZmmVTT\+ҧˬ(b6M3 6I5 yީS?яN3|I\@xN!---}w0ZMU*!44,(IKKK8{l/o8Y=CjcǨ똜t'$0I?N5 x8RJEQگ>VCj?a$Kz\ pgd`DjNzZ߿o$Yȱ2X}<;gFx-*Pe[V!t:XYYq{vIS/WrЅa܄f-)k}f4UQPc؟~~OE\XGcgy &w޽3}yRb) fMimʬL6,sB֦n;k5uBS9YLI }1qA}_W־1B—X٨ "O?Xј޹s8"F%B+ 0tTK_. E$P9 (.#S>iJ` +|*"y;x1w> .k$Rȑ#V޽{w&Ib1J c̯%aD?+)Vjnrm2$j099 Z&hQ~$g/ig ¿eZ(-<?/M.|KPB@SO=1ݹ,t6r-FA^TJ[ijPpC.^AUVH~lNJ(28}c~EktW9'sɦc-CrRg}޽{{F j i&3j+l ҷ,}${at> W#ȗyK7(2Z͛ʯ}/S?isc- RJ'O|_ؽ{1&SD3_h[O.1E eLm?5oۮi]lIk枿 6^ no~'~SG}: ʚϔ/:.<ƁJܣ>7|ͳYkT*6g-+Cdt<DhfL j8SOٖU6 .45 >j>;\f?Ig*c$m4މ'V~~|3c2)FB,Wq,+! >vӏ?3SSS6mZ3cY(;`o0`R%] 0F}%b9euDQd 1>OO"+5_ _}Wl9+orEah`xjBT'5ozӛy;oy*z}A"(#E4\\ER 1v6yja迴a o'?/?Cǐ> ReG/MP8U APfoo>455UK&|@-88(K`'Q5y晅~[Y㼡C p>ٳ&~,=Ɓ(b0gɍo}[tС7_[+ +3L 8 L B c٬Aժ~>o_NEagS.)k1 | P(Rb=kZwm߾&>J6I:.8`BN 8BHk(myajZGy؃>$I15H,|W]̵J(A@k@T;(BTLkسgϮ~߾}ggg'H5b M(rP(Z1*z;vO_ѣ17R|;Z$ˀBCG.\ A#EPȧW]vmݻwdž ֵ@#GcR:RY]\\?>#<?wȑ1KAJ^jU>pcF%%Fnì"PJU?9fgggnݺi۶m7o<;;;njjYVCJG\,LߏWO8q?}S=FB6I%M:}{yS4ڿx5Q8 Rzq{WJJ)P$J|jf111lZVUo45J1YYqW:d{԰5ߦv4U.R &$ŌW< [ļxvyeR(5pUNkm N^5ƫ `,#i^R%ð-Mwx|.zm7Җ\G" ?5>) >!FfK`/ ߛ~YˠPwy(k"޹Es5)8 l*˳ ("02 [BdIaJ_>N˂_3Nrsi/-J@ ,_ [7WpH p HW%x s\ʚ=K^rkZ9&<1=6b]@y\ PJeZ4e, q9` Ҽa=3p^¸i$~p.6'#xu/t9^c\i)bc(kLr0`8E-==| Ƹ1nƸ1CCҲ}IENDB`ukui-screensaver/src/assets/weather/14.png0000664000175000017500000002666415172041035017471 0ustar fengfengPNG  IHDR>a IDATx}y}WM,II D`-lqc?;CC` <A`d0)I h_FsztםRߙH(9}]]~_}060G(`RvWW>cǎƞt:Uzs9H`mmvz꿎A x/:pG^df2 !a R B81uA4 -Hܼfo655ʦBIF2W_}u?9uHX pa@uNa TsN8( d2GX쬮^}EZFkO=-۶m!dn2D"@(b1J)R0:cusq8lۆ뺜yBۋ.B5ԧ泟@-hu-\zc"10iTuI$eYu9p]X,"!L&l6|>aо>ގ?455ud%mǎ?'T1MXEMD"@uu5IR47 W8u."4z{{yOO,"m8q/~/0}#G|Y#fYO&'UUU0 /dlWp]82 N8I>GX٬qСoXdK' ?~X,P,$˲J VEU x>"NC\. <) 8ڑ#Gk%Kնah1-tSNhǹ p8Lb9s&!O3iOOhܧz[6lxhڴieYz8&DϜ9$ 0J}Ѿ38ׇbXr avpK81FT*s^t]7_YYcT*ubO}}}̙3pߙl3)gW_z)ShhaH$YH4缔ؑc{!A_}۶ՅL&۶OR8' +B8RumR,kee妦5g^3gΜKUfL _;\rɥX̉bz45M+)_PR_(P(J t]w/#p?0Ȥ0ƈ833 cd2g~/~nҤI*l2ƴIPl+*9DBD":u*뺠JvWB"|>|>_R>)[4D*}DJYFqo9 N#Ţ`mz&Aoo/onn~b…{L_9sLF,iuuu(ׯ:}Ӄb(?( b\x4KIH$jqPJO*0RNNp*zOOtܹs7pd(|w-rRL&1}tbYws]|r"|>_B"h&PYY ]O8ʅrQN0 vmb-W\q.]c$hß^7b1DQZWWG***;C:.sP(y`,*Q|,CCC***JBӴa*؃R 0DDzBjmM&l޼K+VH˯c$)@K PD"^QQAdj/gDeˀ0HQPa Lis>)Px DQH$4٬Ia'N{m6 '0M™;wD"A pLԓ$L,)JOtmۥϢ Z0PQQ)_~_.!)XE5Mxmmmc߿f_1c!1sp&ĉuø<`s' <# 0 dV Ài/j(i#OHQ}MHww7aVVVs{>|xqCC ! H$8a„95 ;8*JT5=| @D"L0aT~r&Rd`l ORx%qΉpbCaB!BA-|I)bL(s:A9 fb1FI,PȨ,rOs=a)u9 pc0cdh8eC4f6^nM&b,3ET$C~V1D"i555]v}^x}1n Ҕr۶I__LӔu/t`ɥX,V"a$W+Y=_d2MW>aquZ]]zwڹہQN'tiJiqϾS+ u!%i`, ` `qu]'O{-rt%V[[뮻n^h4&A "+G(VӠ\z" ":=ZDuSD43 /|fӦM$@^}}Wĉ'b1H$h4J(" WrՆ!;m7aT{Qtz^9KŢ[YYi]V?rop@خ]wod[dHq744$Iq&f]n(B.CڃU4:+1ŶmL߶m?ǒ (H$I3Huu5D"a%&UeB*>> jrrbIm۶lC},q`麾sp„ H$bVVVbܹjjj!$g`{~8+nU##Q,ÁRHPzYF"HѣG/[,O~<2BgNu>~cǎPȨI"@Ԑ2 Ƚ$((fe =R>GTS^\MaL&3hz>'1tww#0Jh4)Sn…0=d<t۶?lnn60I& .UUU/Dr9 c0r>@IYyP d d`I&=O_&M$ )3s^{PJx<Ϝ9O<,QRVhrr,DͧC宓Je ei 8\.GzzzO4op` x]]ŷzDQimm-;R/(yNH6dΠg҃r Á3? PA ۶m9 ~1魋/Ei` >o޼oqjDPl9gOn,`1jcʊT*!rW.[) C]WqF L cP(|>O<N:>o͚5kV\+V< BjϟL4DT*+**ai_4rZ"ZrUS!Fha$TMExTV,tS-LCP B^ ]ם9s搶W\YbŊ1@0hYf]J*Lt,*++r-nɍ+(!U 9# ܸ:8$/ʗA`Y"9l6i>k֬={~rJgŊwӥlmX (yq4eq?):b}zqǎO 1 03 I0$"fww6Sfb{+#iT8} RyPJ9{e˖7lذ~q@˲*+++w$RCeDWnhrv^Q ljJ` IAQe sgƌ믿.)K(*&%yӧ6PN9p)Ej%D 3tߑ\5 -WvwåADH$HDK$v*^|Ñ@gt]䊔Bd/GҰAzwr-"?zDAD gKa 4 I4%aɓ' S`B &! A;rêxG*:!T(7#Y{9 u,7T۩(<h1v7Κ5FQT`$oc3 ~Em# ?/7^POWclr@A9ɿ7CLP;`$+,X,/^F@[3L䠘\/ 9B0O`9HaTl/pmrRcYABV k PlUyAJ.瓔sTzzh>{C)]D:{d2_ u]^(H$w/j' ,ףe!##]i9e&rDCjC/DLg{ ;󕕕VX6 BΙh hS","C)A(_>qUpȢC}q:rv FN)%Fk ;x(p+\|ȶmv̚5k~>brҖbK .sdjh Z'+N]a$ q;9{2 AJCe J#BP,0 0u^+/4 \>K{ 5x Q{ՙ2:ղUQ40'RzYQS|2*!m^?R8/$o߾7lذꫯNa!BJ@r6P 圽l([4Q}A*Dêə9YAre #ȿ %Ѓ*냜r?8/@CP`\B9{^|g͛:>KQO ~q 0] e_B4 z| ӗR=HI38wvv`R 8_c *++BV:cV~ EQJ2W*S5~ *S*.'$ϻb^ic"@ud`B"{cYִiӦEy;p#(7XR ū=!q1  1H*PNr4-@ I~<τ+=s^c;MӜ4yqՊX/ޟ W{<$3=#8p- !$9c89%P <.E  :::lڴiƍgϞ.V( Ƙ뺔1Fcu]HP u_^`/ `x;zш}r=>( Pf!rrLrH__Kџ{UV=/RJ>|<\a0h/0 !!Pgg{O4-L&ST*ǣh4N:B,"1N%a(T-7ڀ鑷~_`r`IA8FPn2ecI%}}}.}ݺu}+WGBͽ;fbH;eP4rxfnpWWA2Ŀ>{Сe+Vmێ眨&@jW DEQ ߗ1GPW%(!@쥠>m7.mÆ \.;~E6!f9c0`ts4l Ph5Bs=wμy\%SR\^:}St] ;sK9u!ކ 'zaDfGB8u]^kGV=}Xt]w-iOe@ ]x/ËD(!~Qs=d<FSSSk~J,:00+z k. .!=+-B=Y˔<@!@a5sJ9СCxg׿KoC!E_gCTO`ŀ1<>jvΝ;w4WWW'"BDM^r/hXL0t:F l.9nc8Hr8)55MӶoߞ{G_miimۢ5M>qĈ2X8ńyGl6K /G}͛7-}@|0  ;i J 3A.w5M^q.\x<O}&tL]GdL"tJ4Jfa 00[T-ݧك惖Gӳ u'NlNbeYaq7}<Ҳ-?C Rc! 1`20@%3 RrY666hѢ+̙3% 2i䠵r$SJrG:C9fhmm='N{ΐp8 ˲' S9<֬Y%q>0JmiRJM0 MӨ8nX}%|]zt0zы M,ι) @fC`&>iҤ]tųgϞZ]]m '@DܧU*:ϣ#{͛7M晳g>&,v9=un4K#>=ᄏ7xk?1ԯl^w~ٜy xGIP0_(tbKc ߩ~#P~r(yl!PyI$5SN9}MMMUUUp8|VAb>8q{Zךd:KBeFMMMSL2"Jhdze  ?e˖][n{^b *`H9UE"D*bX,6 p]vuuu xӐO")# 6B'-ls89OB^@?XA8: <=qS".iH+Ӯ%@刲Tj|PG@LF:6R Spk?}F,gQgC(Rj$#3\o\QwSK=R=s*1̈L!H)-r 1N %0 N,ɡ2Ԇ(TV @PJN4oȱ,8玢p٩X2 D$ĒOe 0lrr.`T_QrM촞K MLιżY*9OC@VJ]-@2}RZ`d32 mt?f78纟P {8]Ns[ԡ@,Qι#t`Hrι GSn)^XRX]O@ɠq8TdG,]P2˾X./|- 8_0.Ar(Q褅N ;OE(r5Ծk[p "~[gy}m$uL˘ ~o5M{M01 ru~۶'ރQ~(Hd ǏQ__WmU/5660Bptq\>}yvѣ$LfbX1Fww1iȄطoߵǎ;f`o8p 3cƌb'>R(W* /2嬠Q \.nnݺusNѣ/ٔRk„ \.Wlmm}q{B֝;wÇ_ yvvuub_x]tYzf,s6l۷ *9t6%wifӳ,O6>J7nܘ={vaɍw}jjjÇ?&5d,9dXp(>矿 /D43NgfΜYo;vXouqSLo~γ>/ׯ7KGSD$}F;?-.ڸq*uIP(F]Ce,XP{ܳo߾w)$dNضmWep7n|9gF @iL4>JuRF b:'v<k֭K[ZZ_T!쥗^zޑ#Gݻw7|޽{oݺu%Kέ~.8t.02GseϞ=3ׯ_m۶mk{͛7wmݺui[[9"6A &L/{[lɬY渮~o۶mǏK>( QnH ޽|R{oj$]xqqݍk׮p {)c]4 GͿڴi]v׎=\vmmL4u]L:fܹ5t:s~޴О$p8\bX _W/_/_wĉڵ+nصkx(dB w]w mPo~@;aq)'=PO>t.k׮cYV/9|>UUU3 ?6՞2 !_!~~)Yɑ']]](tvv>:QJo߾cBs޽ Ø;wu1*5˲GӴz]׫㜿9MkVTc\{ښ.\s]D ,xxϞ=ovsm555nڴ)M)]|oAx@FSL|V4s,xQWa7|Okkkosq˗/o>뮻ѣG~?2GI4x VY``!˘O dz>o~DܴiӃԩS{ޟd]]]3f$8N24^vx́7~4D]R&;+A>l}4L{(O=Ԫ~?z饗Z*++D"{O/X`?{,2/}iY2Lpk)}S0. n喋vi766^|~7 `2Vc=w g͚]ϙ3fn5k֜0 cpV @>.b^z饷<#_~v*n޼9} />oY֗\'p{iOӽ]]]}}}Փ'OUQQ/\pM:C8g7`zhfoV'RB\puկ^]x|X,+W}w\3u]w.]zɎ;2 g˿'6mԵaÆ|ۭO34oue˖=9۷oOwD"14BGϜ2jiK,޶CeU{?fUu]wg34Mcƍm[li7^*8hy6h" @!s^ /v Q 3lxՃP(cwhN 'xﶙ`2ƎpΏc ކH9BH?cp>?S o\h.V9y7ow iZ49}gM}hV۪dt7Z߀sn2N8q Na3f2.2.2.2.2.2.2.2.2.2.2.2.2.2.2b1fOIENDB`ukui-screensaver/src/assets/weather/60.png0000664000175000017500000004332715172041035017465 0ustar fengfengPNG  IHDR>a IDATxwՕ6{h4J(# ˮ]kwiql &gH&+!$HUuVM{4`穧u{ %& 轸 w"zPJR5k'ض$PZ &bm---[OuM #zW_M6Nf2IJ@kajjj61mڴ͛ٳ7Km`Xx /p͚5a !,H)&"BkMmP( @fO}ѷ}ُToo%'@xѢE\Kx<4emm-{qʱ=3?L$>prdF8^dPaXkM43 uaYVmmp}}}o,ܼS.?Znf /\Ҳ LHH&|Rcc#lۆv^}T*.Ų37)Am*&. ,lqF| !t}}W9'ϟɓi$uQGבGh4q#裏]`TVZkd2lݺmuRBaa$`?b J)ömyضX,c̙p ;餓^?Db'?d2L&4MqAa(v+1?l mۆ|>qP,avD!0 \O}}=ڐH$ ,l4xffT(d&mۘ6mSgu5'|3࣊} @wq7{W !x祔?̐R"byD$"+'>q /@; _pA'գ$Ւ=5l[|R34M$ ̝;"1 F1MRJX&L:Z[[)L"h---ڵk/+^蘂ؗ}dòq/:O$I`v,\-;A''YX fB2#8,z;B8H$I}}}P ̿꫟8 `_බ,)+$,K;0+%l3:0ʪٗV41sL$ ?/3ޏs(q'v%0"$Rj8>|_ʯtN bfZ 2MsRjێ;^ε +Z`OO:_Դq²,  v6 mem#8'̯95y>h4zիoG|cz/ 5kLZ?Z__Rb1Dss3Ϙ1mmm܌z(`%I 'm*1+`owGk65JjM{UCCCSGGǪL&C׎zٲe\s5j444ؑHlhhyi(`bBbXV R}5FUl}ЖWag{0Y5 L?\u֭[}衇Hgpq ~7f?~JJ91 Ú2e qૼ D |b|mpxQI]λ7XA秐#auE>ǔ)SN4 u%>&pwwwW_) a9scX,FZO8D4- ƜpTl~5UgO}0-?*~k0L)b'x"~뭷Q S̤{ɯ&@P_WޱcG)9{l># @l"3 umӱ4IaFa΅r4G3y셉d2a=drC[[3f3| ɩ~Zao O?s-2LN8| KO\>/OݮĀp^ ߞNbt8Rٶd21}?[;ATm.Fɓ'7NBMM f<ySª vr pU|ݝ_&i^㺮f,b(4l۶Nzٳgo{d>蠃{X,f.Xg͚EJ3@{ 2P(j_^|jl_0䛅J[=p@kڶm#JP( s1?}@ Pw嗯Fnnn'p$N!'P(t<(,aA0g#~0 ^!`2UP0zzz0a„;o]{ zΜ9ga(˲ĤIF˒]fJ&U;UsKD >JRayKٸP(ȪmW]uG #1cyap$xZZZ^ކ3N#J94;+,]Ntj|JaAݲ,Xh4xIc|%W S*E4e,A%FB%-|Ȳ,D"R)'pG=@4NlvHQy}!'A.T#*|atoT *)dw;mv }A8-2wwD4@&d2g۶96j'܁jIRnJ8TAJuV Ga ߻ 0Q !ز,={ nT "2R鞞"b5;C2*hL(Y8p% >i˫䄎೎ET|y;f @D$-[PJuJ&έRIR*I}L_^8^fժiJeVsT\ u}$DDBbf$ɉi=5B6nRJ uf9ˍya0TcPCc#>[~9 ;!Jj 0SXM=?AFW*Ͽ&pbd$0*ƚbׯ_nSSӼbmۖeJüaϻZ UG E<T\}ವJ>@t}0]$>F% 4&"5yמ<$"DQ6 XT~%543>+2+]͞Pwt5t.r^ e)X4&%b^`p,`n޼yWs12l4^0:5,aOJh_Xò`'V(}&%AoT&8'wA*WJRʈR*7sǪ2 @?looVkkd4l6YFyP ; GNK~:*wNQD89;XnX"Rʊ9pU0UL'g;XR3GPzSX,wؑ3g"bQΨLJ Om)XDUZp>>PMX,=s.eX B  n-L>@""۶.q'[ѕ_~?ⓠ6OA2sTsA=J#XF .0]]===?7:3DD3;(@im "Z|>?s/0aBq9eYP `'U;h~ Y Œ]Bi4 T6{\NO?o}(I}6W%ȝ4@ɤ֮];֭[{3vj 3?|PT*'T2i9l~\((jXz= T@@rnC'H]TI;5 P6X4yNWXѽbŊW H556666d2,+ -'NlhF0ژ0 vQ\/#8PɎæ%|xn|X,R&QRww?V۶PNݬ!& 9>Fq<1s;#J&߿ƍ}ũ_,Kz(e)I>󽶳8K1RJ~{zzVERn&%$F[}w[ ۽4{`1<-aBDmۘ>}BA1?YjG!1%|Ue$7Z۶MbC߃G,~yuooݺ ""fAg_5&t7?w9*tnDmf =ԋ< r& 8GK(l%ѷz/uuu ,8*+W;̃# -iպZAw7 Kh_Z/ L4{ဿ hѢ .|,u{B<zq`w'_D7ra]c x!2#uމba̰Q+HX 2qLj$^'S5z4g,AIC.C>|>oʠٹ|>Rqz'+Ks`樔2" ! ]!]TzzzK,Ys}=tgɕ(:`TԤ9} 4R 75Ȃy%?r4 f0xg8i0/^PwwOVpطba)}'VX2~[U8nƍ76a̬mv2pOOOoggggGG\.׋ ̜Ci ,Qio[nzްAEKi 2bP# b  3: 7뙾q=hH$\U6Q͛7/<9䐣k|* 4rtEc 2Lcu}}}ۈR(f$a˲(`FQ6z` <~wy? m%8͟luwwo^,/s9t5?}{LOs}SW3Fuִas>)2/tDLkeGNݯ W̘"/>ZܩY(av^{wY2k֬s̙?a„Ix\6X6 R6,wض===6lذbݺuo"M9s&M:45"؟;>hR^v%Ky敥PsR\zV;z-bԧ#{ ҄ǘkJH$vm!-}@CF2֪SliuyG̓K}  %3,"˟_|gN8qZKKKk"D"wFK{{]]];;;nGy(z֭kniɖImmo IDATmS[[['5555b14MCFqL&ݼyׯ1Di>EN@k84?$O\rpw8s(1;c'w+e>JM9O^mV- Mज़?~q?M aZYu~32xS r7}QIoy?t`z1hW0DmmmC}}}C2lL&H$R^ʚ]׵b.ɤ`*r]7u$YD%3H!D,%bX0JՑQJeDdPJ`xb3v tَ*#@{JN/9(iJiO55|e; 5;|qţ-fD1>]\U#q&f;wK{/1GmEx5x>dUTV񙿋 po jWXQeK fMqgE`IwA"@JF!6ac/aDC̈Nfӗ"-vf0ZK1ss b 0a*־uwt-똕(d޲jisH %z8Y ^NeP#ZvSj/n]ʽE@g|) :Z(چ (28΃n;@PB. }_8tu,BH"s&~\ta2ko_K\UE Ojk=^iopᯢ1şK=.h!K@pVC?j6 \/ 7?0D=ßKepY%$3ifs#757^ wpdL0"вUsa-s-͜1ԍ?<DF(%Z jG˅gThk6Z% ܂!$㖵b7KxfbW_Ɍaz/j5bAE.% \6?w:cXѬN<Ȓ2gڌ7̌fP (9g`*FwMNUF$m* HC[vK!#QU\fB̒d*C= Vǰj R'IC(F E/FЌ @.9{ aeO:ˤ qyC+M 0 ! }}X6;\Q9E iZwAr&>n#:H%]o_ 4Jt^A)&J1!yK"ao?% {񄣘iB>de`,̱'D"%e?2σ A fa@QN,ȥi~R#{6 C[@/@&9iW݈E(e5]åDžK5YȏlӲ̠ i } ` 8 ~ԁG@Aq mDNP}=8O֢^ djor/`f" }DpDJ)5QO{$bďqe_?``e⮇[kC1('13WͅZC'ZX4Lf5̐Bih|>Mř"`_2Z) i % -UhT W%"Б #`du$`\ 0}0D%.,B)y3D$R(iҨOɔ>" ɨFB&q1(pXFfp?kw/[xti0-oJJ1GBOCBA4dz_}u0 w0~enxe5K1z aj1dUY ZaFJ`w8mv!T׃+X8!bDV#6| A N@ wF|Uo>Hetle<6ih$e+ Q&ѻk~W`h !# x{^8B?vpSm1g;n}x) gۧOUۭD K{)?^OzэR')1뷯|n9P"F㽹+ _pz|f˄MML   e$ } _YP,53\q/\ɓ? ">P`YA(e1tB`ժ ]}nB#?'bX2~Vr;m\mǣ/b=XjDzp56gfDQ A0 *'~EM$)rOO&cw)xvaģfvz&|?ԕ٫:2}*`[:8)qKֵ6v|hO8>59٬#<&HLva xO>Ab)BP4I .nNe"2R eFsimS0E1k0eaփ-LqDZTo|%vS7sGwXHV\ݛ,8de.CjMV ߿calT#NgY"N=9=ZFOb_?io@#v Kj)L@3-!%'isKG$zҽ(eF%KFԚN*FpXLEe/J4b͸ӑ lzdỳϙs瞻i(Xfĉ9";w/%R&gC@ mw+.n,ؗRy1k>tkJZfCuJqˁųIHBͪ֋>"`2zm 6\Y|kn!FɜG;):82):TCSd󄓭y)vv>Xb~>bAHu/;aRԨa4+YeWo󺇞 }`7Kìt7uS9un1ݼt,"cZNP]Bg;ԍ`A4co~p?1`jfvH:Ȁ)͠!H1bsy\w# ED9a1@c*ÜNp+icM+2̔P?/c/ӏť *\_C@wFiߝ^R`ߝ[{),z:6{WB D۽z/Xon]FDxR O0 ;d@H{49gG~a6l's-d-4,5[{13ίt:[Q.zv?[ Ar[zXҵ@4{0Z;j|[ԽnoqL.-+SJ} ?$'y8=:eej4H3s?JkޫSz6_qʌ(g?#t [lq1Z?L .e؛ק5Js{nSq.p—&qI !zR؏ۊ62)u!i90&MQܨ`oP8a_sTpBuFiKkg|mL`f=_~R"Z6CJܹG;voگh^\ԷPD/JS-?no<ϗ5b[ Pq7J4Nefun-ngҷסCl'ƒ!C8+Q|C_{Sȁa)e]?@51M8{r8rdW}oO[êaXթ)~05u:GڙBG7e[5<^POɜEWO1K%4ZBDֻ]> Oopr=RʽgKp[ w?6&Qך|({|W1 id{:wa槹C D~$w =ŴC}Tڗ3Լ/sRʈ]kruR5˟$̢DʭMصQ(uڥ;@fysWJr!jC57VpOz]?gF_jZ7oǛhErfslؐD45G`XjE| %%:te;nEjxhu60"IZ 1)ٴb;sB *a.U7fՕ/" 'پB@iFHf9zt_qDE[M"Nf Yt]@2<4#|dC( 1BG^VX&2v+?>_ؽv&`6ڽyaءQdzLsMgHWuh<(2"̱)hS!3-q ][8aDžךt Z\V^fo}7ŋ9dFڅ?z֘PD.en|u3bPܷ_4K){R]´lCNL.(̤f f2@ŕL y !~xf) 1q0TNh>}֎h}&!+cڔl|c"{\55W@3fT^ W!DRN3MZ&y-dmZ'@)Rc7qk݋RS5Oc أRPJ<8SpQ;qLEIENDB`ukui-screensaver/src/assets/weather/30.png0000664000175000017500000005021515172041035017454 0ustar fengfengPNG  IHDR>a`tEXtSoftwareStardock IconDeveloper - Professional ver 1.0.0, Build 839 - http://www.skinstudio.net0lt IDATxydWY/{Z{p>=wt@”QQO*z s/|WD\AP@` D dN:NX=~s짪v{z*bfGmG݁h? o aEרKw[QYO o}S7DZ11 "1`Si7N$F^0 f8-sBqadܲ-H Ps{^:}V6n=ƤןtdHH`ε?c>! C$YA)zqW! uQ~X IGiv%`cRMs, C$$NBX01Xk")|-CgbꬒfB){/a\/]`d 7uh[40@kܽ5kB,rʘKk׃+}~z{?=sqVnxo͝CH*ȞwF<}5,,`[8#1vmauqXAkã;JAPp8=&b~=D$L~7_D0I0IG'ٞQ)ըPB|=|?߹$4ERN=Ǐ?$G8HyZ)pJ,Tեu/%v稽?HJ)Ẻoתǯo!:,wl1P q z&q&h !%~xoHdb]>QZ9v i \xutߣMfy+uO3ziU^=!D* g50ĝS/)tulv|aIݥ̤4x@%o@*}jh9o'f !a$~T]= 3n=w@ HDV\*%x#z~g._hIp!\ck=2,N:vE/0cH`DJ|wl v_#\X.1J3ԛF.>U\ܺ|_@`^q$4e8 )T$":U kC6yʋG?{_RD)N^'n-2nI T8)ZRA:W ubfDݮ(B)9w掗2(tϬۡm?AZ"yJ#},<)33q,0, /1LYLnEKo81H[_$~s&9w> (;,?fyqūvJMz5[\SaE=7%(:yGwxՕMW~˭,hH"ɬ1F'V0F$ZS]1>3*@ƀBk>F+`&)O4 Ť /BDr73_͏vOQ>q % _8F\ 'W[s9zE2dGIhrO19W]?ՅTNT(w! EBXIFY$M{7gެ3 LNe Hyr hX8`]r'. U_nŢV%nuU]׿u(_ƼnG33D 4`v%% 6BTn\hPyCzMy]qjʨB**k?ut +)RMj}S 2 ẽA> C1!+BW𠅓U[·38z\8eIOzA"DIxC msxMѭ_G/1bؽtW7p|AH-%:+HnG^Z!碰` 3E[nL0F@&)M?0Q:w*nq2;Yk !W@eoy )?& [ %}>wWŠ&1rشz&΄9 x'!CF *!* ;` Ð% $c (3wȐǏab<DBj`k6wᏕx6&{;?a{o~Slр,'*pY*Y!95$Jc 3+!&z p^ H }IL` 0`:#//4dV[ !4ށQpuQ!Gb[q/ "fpeb )00M*͗L|SSM &|ڵ kΚzЫ~ڿ;/GV=2We/ҳs;:d9swDjƮS)D?AAd@ |10,s pjZ3aH(E=ȑ ! t*/[n*2ZNA*9DNEqmwO v $ dJCG~;nܨml|*1iw9⩶gfp[< 0kHUW'M0Ƕ7gAo{Ϻ_uj }: 3 $# 3 m.Nh4DTr"x0F*a1 YR@DEI@ 09 TU[ Uqi)JxW58D?Dx_Ew]}t؄N HUpF7›;HA0s0IU} ɢ& iiAJ IKpX$H(A&vLkn.n3Y#c&Z2X(D#ќM&ڭj!K!L@L ,@@'X]FV71PS@6BR* ET*CBXy)(BAZGTtxpOi%ωp & `0J8!PEfւ9!a7Dt!LE44'BzOԈs+OX89kaԉ=a3=^ZZjdi.$38< S 1 @8&஻8_؃y:J< cbbzA-1W\ׅ(}0Z-bvv%j)| shE|%=$ QALL$ ~9ξeXKNw6MQDdsOg. J'$%n8iB&bm'uU@^(F 1 @EGu +c!x022uaxxJ)&Ud~ Z 8Ǐ,<`lG,\BeHBwDi*t!!JCNǔF+`Y"3 AGp:1C{y[@ IRԈw'm$PiG م`& ! w_~*j\GP vZ858^"3 0ȘeaaFV aBq4W1/o-#(+ Fof F3X\@!ܤWnj #( i$ d/1)IO{ HM PAwe`PƄSĺː2O'qX^z(ؾ}; ̜|fqZCJ&fggj18 $kT OKp!# @(M@D0 2 bc8p1I I;9E@)RrZ~(Hn28H0L @YpS ı_XQ* v؁J8:0:sc033Eq#I~ܝak|:O`*/0R!N0L0*eH&"IDQ AI' :]J.m#/~9߈=Hy^R*LUH0XHR $$  d nw P*{>&''y^_bv~Ik;x''R( Xr%*Js&'{' @VC4fgt:.ZM[b|6onF?=>z8}GwnDs_>ȗ!| *1mL x`щ#dy#[˗ƂzuVQtJ2Sl@Caf="T+#p\2z_3DEolG}8rիW#Yȃn2 h,@G516M|7uW^Tm^͡txjsWx6F9#"f5Ǒ环lIg*68'pZ)Mv 䀹2wNZ0Ӑ>Cv T+rBΊg, PXVu~f/_RTa<"pJW8}`?#iA@~/j13#ABb G?p^?O|Qm-R!пLwZ87ŦJYϣW1ӥuXMRggN׽:g =R.@2F8!"-pFV@wl4?сPJqJ~&/+IBuݾRT*aժU}I>;>0s(3B5&}}Ml_{ssApc?IHrL<zDʕ^y"F(uzsfo;1!YX0OJݿ~9CZb, !T,%RՕpF7AU]![fQ=ʑ|]㍎j&i]x!6]g]v>o3њ /AU%t"h_QBfD0 i IS3a/D[P1 T evL?ODHxyva9{kG vH L}q"&|nK{ϻAL(7됐3}o,<ѸNC1E͆`Ct?Y$|&@J)"-d 1L})_l=}SrBLRٴzWfTDJ68Il#FKvqa8(>ώL}M5slI(}況/@v|_J5˨_7l>}.Q G@F  5Gӳ>eFg]f}BBbBRчppDLIL 9)ͽ+eͷmi9!dsy Q;=@cqÑq$E¦y ipZ@A0@NO>|Ӏ(RM=ܻ8gK|Ol 3l ,8>  ͼvb+gluԊv :+)%YC7ܞ\ M8Ǜ_S$'ѭ9Wʛ  YtqH1X99bb1 "/cjdg9n9ɛAx ;]_|"`ʲ 'Ue jl~AFD.hӽC)0xA6zSP v.\WXU3W _H¯@o^Zax^زe FFFIcvf]aˈl;Lgd痒h5 G<8$Tu6G@ !K$t1@LOh2h| @mtnG%IKn^Nڐ_fU~[n-[yg2`d) 2;r5Cvv.\%>R 1A(a63IC׏f1!2(!f:Og@@DܛUǀ"ZyzB+ y ~aXAP;iӦf=; j<,lIK= IDATs9\}$]$ |&aᘿzL050=43+)t5H J/g vV(""ʊ2_z^ōp4nA4 > o _xgVOJضm;<$IҟϞ9Yiv89q|~,'O|80/i޵u}% 0n~"ط*I@RBDZ2t;YóbG*6:aJv zkl %[ f(h UG{{AȱcpС>Q2eT*VaxxZ n|*K}&Ϩ[g[I[9q@Qc8 C\,xllmwֵ'^7=pm}tՃ}&qjzksHum`^45;_ZL}Rh@.}T4LxjeWb;ꮬ~nN nJ9]8,rೃQ*0>>IE6mA80-k]jܯccN8. C=vW_{o0IbM*xUJq "]T>P\gF(^T݊⇢+(P\H%dKF (jj̘1<<0 VB||ok 0Afl ,Ţg!c1Qk#G oݟk>x`flrV Tm@R |furNDbdhcoF"]oGVm5: Kq]J% T* 12&gǒ$`Vf_t0??c n݊;vi&=3g3N!?Yk06 io_kvt:n0 Z}WWОb#ۗ>WwOCqpeUMa8%K%TQrAA\U $Inۯ/윙^/*$Av%Pfc+`38{3X @vMf(J8o;RJ9z{go/ԍ=&B.b?4KiEMX, UPTӣ uYv4n!QVWAً@əK`C{|lh iEn۴a{u7l׿ Y$1'I : 1p{Ggm?I%8eP"h1^B+j^PF\ƺkbŊ%qdϳt^b3&o:.(qWciHs`Q r x2}.rcg، =vvif޽`}J4 fy^B)'s/8wUЬJF"#(uAqV +Ar7o"DA?c~\gj/{u03:֬YkBkg}&җi{1\<;|bˁ6&%InfnI'Iv@ `*$}?.^0 rRAQ bEPB\֭[QT3e@[g_XXLFfɟ|bѼo;PVQ(34 t0Ki|.b9INVZon$jj0#,}Oia~REҺ;JkkJQ0[(o T+g-˪v~,Zk4 ^a{y v(7DbUVaÆ K%Uj_ zֹ|tIU: Cqx޷{^/k3`${[R:Q`v8&f qe}կin~\Q)P*ƍ1>>~Dg޾1f8t:ʴ}v]Lk tn!Ě5kp`bbbl̶==sm)s⹹9nW|!ę졺ل#B Yt_Ht+u\ɂ^P1s!D^5??(=iZz=j&P1[l6E6<bӦMKiV׼d. Y?ɨ4pΞ̊M\%u]@/>O}(Za\I h͜ 3/zU.JbŊg8@l#jc;xRy^`||by1X,Zl(hصk<+[lANjl3ORw4[K5n&CjiÆ wgOONmoKC{Xro >U۶=,l9r~9cП t(֬YRrȀ u^ׯNJ+PVQV1117}{f#U#eԖclz/ ˑn*qU#:uoVYПpd[\Q3APVq.e68CrV/111џE[^pP1hV~R`ʕXj:PQpw_iOfl ?BDd"-pK":TCf?ts|e }[&e!=MMaxx:?/pZmQahv? c d,.s O{H LDBTOܹe7 9B9 )EyQk[YjZ44 D-̤czg1} 1qYqlBey m.Bv*yll wu*.t݁Gc shH;Mid>031[V/Y@=׏D8bixqNhPEgg‹Ed}Hju}?;Usy1sQa~Xrey3@KAav|c 13%IF1ҫ{j %zː1c9i҅޷'O;'o;+yF ?hؠgc3(63f5;|;X}ݶР~.g^l [ejQ9ݑ:iBk#dbapÆ_|;}_xGYxTɫ뼊|%@0 ŕͤALZPXTQq!۷֭C$lq-ARSٽkmM`B?~|ɓO81oП`?]w99[(^첷( >Kvim,nm鷿#,# x6_پY8K#7MvgX\ם .{_2u.#fީ5o? I &/)  @VGH?8ȯ3=_O ɠmMsv9ILOO@wmydyn]w Arxx8j_###DDvXNsTk< ]LRZo̜elf Bl@\FTB^]ͲaF_;"8Kc/ӈ|QqciL1_Bq4+&G>(.*{E@'/^y/lX-Y2gЖ\}yǰ{rJ(M{[qZkZs?$`5{eb:㘓$Q_Wg>)#̬֦u]ʕ+׆ Ij*@y/Ep{Ե' xAI?<gq*[.+JqulL1 &4&7MPqC){?W*ccc[RT(hhhy<ԥZ^jDa;O`_Ey&/'K1l`sS2MߊNOOOĉh/LcjwȝGډ",QDR8( c (5E"70w󽣁 fժ~[f2]Dž1LLLW.}qL!RS? -T$I׌!"(077o~ @Ig o<ϔ'C|٫ \e2"Oc 2yZd3miH%V_|Xp]-:Ş}TDkfg*gk3ag_M/W'!F^ug-^e 40@ZF^ & @DEYlJn$UO|}%4 tIeQ*yk # ȫ=NJ)A.'9|0vH&>]RDl*j+!+M;axF D d gMj8aъ+o|-[ED[:(t$D u'N>gt:ls1Hb|vM>̳5B~?ۀNo y8FtP^ff3}L(QgJw" QVǀQ=8IX h,q?yK:}-K!AјFbr9?}5_-d{)lH :A9=v`=ǁW^Cwښ?v 5q D28@GH@: @aLT,w^{y{)zI):A/@Zxg$hKF'CN{yifjz)O-;H !,G#1e9 ZL@p QbCdCi+pކ }V!N3{ر#6mژ$ GQ$l ([Ϥ9˚+|#yˈmcfɚ|̶:lg]K HfʞQJA*E QT(z2 VtBpH@k̆Ft05hoGys21s/(n$!~MrlUo3?4:giY@fb?g  "5A X@`AL+BXpIcty6h{)WaodAC\p$J^eݼ9MHy} {{뺢#'t35Py˼8 m'lgfP4WA %3@C @9.Sڟ ^>6A],⍯ҦM(wuk*P?<זf4ѣ]/{67MyRrP {K!Vv(gAfL&v4Ϡ5(si-| {\6SLy'5YtpQUHr;n B(D.X]wB6ðsZƟ9&2dFCD~7]veGDyƽ% γ9|Hg)ik[rm[k;_Y?m*68 ]91 #U'6o.\YPz[6OMPs0zU( 3'o߾{n;7?wvv6tz ,S ug |ؗ1*c|~IR6lQ-fj?a), DԞi=6}|8Qrh}RJG.yo'^l%dm۶ = DD$o/vmG8VӺWKV;M3۞mdc_^r7{Pege{ymfWZ(oJ kI' !xf߾c1YVjn~"M ѿi`=ev3/x F#df$IQSN)377GF&g5f[dջ-pvM}Z-H)MxIDATg<`9W7-<>fYxo1QUu|sVCk4=EĚ9uKZe_v3 g](o7[n̈́-uL {7֭[뮻OV^bҥQ\0j٩\iOTEt3>ڙsuiH+q:5gڝa!}&G^x^"[$dGxo;֭[3fN#X'$N uww/Yr+W\U P)T*"!dg0d>E5^:} mwu:)ƘձZf4ř3gΟ,ϧ Qjl5O rXk%q5衡ę\\4PHD%T&GGG?vB 0T*,GQ޻`+cSVْfkb1P,/gŽf*z2gA%ueMNM?<}c}D:cjЀ1dfZpƷ՚R ۶mo9cXk]<lwi11YJc@Nj=3y35 %!&A`e_ҿic/zq mIVfwH4+/U~ {8s^FALjJ08lr$FF|}Ϟ??bpKo@+RɖRyи1=9Ɖ_= .1&S`2& 0S8e>EsA0;lO? fΞ}Y`Es4@E1 @/>q _WW^~f,LB\nqsz Ih4h1if$l6jQbۨ1868{w?ߟdY|#!,^ܽѢK+U@89]?vÇ;{db˵\S$LdBT  l l򜍏n0.>(z!q R1%f.Æ!YگDͬ 믻]|RBh>Lj4,jpw{G!W-EjQ -ݦ䎥o c5`"IC>/~12[GC@J!$D}r&h:0Y & Q+uDf.NWM#QZ ` B "JBx{n!=G@F%>QJYfu\je}}}RtN ^̵F`':oǏ1DW_vKzz/JbBH()lebzbB;;⋇πVHfcBR)I\3 k[Ih3-מ0Ɣck/,m8pf)誫PZPsA 1 CCCعs'|IqIz@x " :q{5)\./X|K.袋z{{0 R ZkZi}j9uLw'ONN?>1uD=Mg{ D.YK.YquѢp钞%:3Y?ޚNμĩcNNOO@ F,<#:rW@3j-`*m;u!DvMCțGM0q5O?ыq)s=oFDT1tJ/Ř5 %:$|73@! ]O\eHmʍ @ ˬHAH"e%%o̙fN5Ɗ:u4]QB@I, c[27 h@mJ\AD:GyIw!I ̟fK444 )eo z`5B9pmF$7Y爜%`=sR (˴H?n~ a&e,;3^S-!|^ ݼy޲e/}bGN} SO=7JAknP,O!eI>-sgJ!P jT(!ۅ1 zfi LА@!9 g ]ʎ4siS0)Pl^:߾};oذ3j.y RJiӦdbbRTkk '%me*YXN[Զ.g;@IUtd Z 0LT@,-QO@9d "5/mj lV]owߍjzf!2A#GĎ;ZR3a P\>;k4I4S!ZIl+t^i|BxݻGGG1o 0u9P!+w AH⚵8zTI!V:۹vܚ+3cgW|fc?dhrL;7]Zf٠ _"ǟ >%s2ՙOPt:{##$k- 1Fˮw8Wb6ac~Ř }: l,iN̜Lf$Zخvr€A$,5W|fm{5h~?gf>>8T lQlKgi Io<C "QIENDB`ukui-screensaver/src/assets/weather/4.png0000664000175000017500000004330615172041035017400 0ustar fengfengPNG  IHDR>a IDATxwř?y{]iJ(d&8all33w|wNl8 M09h@$$V͓g1ݣhf~tUycU-& @`@ (KU7׭[1444ɶ!D@Z'M4DO0a̙3NRnGmܸ-[dg)J@gaĖ &:cƌg,Xܹsn믖 tK.`ݺu/ 3 @$A(eYR2MD֚lFXD>ԩS?n~{svZ@Eysx\ H$YLӤp8상 "h.۶Rq.k53m2 ˡN;]݇Fz;`p׮];n-[>؈D"e)"BhiiAkk+544 BJ ! 5Rmm#!N(2 p+ Ap kaaYB B###E>8m=SA(uN_|>;jP|bg8M8Ba"GMSN02A;A X,V48!Ql6k_z?ZC(+1y%Kq5,kmm5(LӔ | 'PKK ǩAfz-ۋl6sE 5k ff.,2 455G"ߘ>} Oh`@0?&LXP(dq>(L¶m `O zJۋRTq}{ Mj'ȿoqvB覦M/q/\ɩSso  Hcc9{p؉FF8qG{0ד~_gYl۶ muݤ!R "aoF|R>( ۶Q(`6"ٳg?xߜr){ X,6cتx<d80w\J%Tvk13sEEl߾BT* 8BH)aF&ttt AJY;|gsg2d2̚X,l6 ۶1cƌ>+O='G>餓~q_*pѨwLKtX,V_k]ai0M0m4L8B PKdYro"ɈB3fsC];78&`^: 5444 GMp]wLW?22A8BbXaaY À'N *u="R9JGy9Ck>쳿q{wdY{Κ5냡PHa9i$1cF5q5lȈW5_ @(iØ={6:;;!4 = e p |8mmm y'8744P0M;_ VȠϬH$9s uݤZmwpP&Li488OUd9/.~|$B644">#Vζm0s%3 }i5MgF,c Ï&)j""444@ALD ljjrFFFf?SwwAC@p (4P"M}; JKϠS7uT ΞyjF}[p"4M466".gl@ af)%K###{HdB{mTohh@2RabTM U;(PI)%!kd2iO~/; 28 aX, m۶4MQaGy~`v Tkkk ~HlWu.CZ2[ZZ~+~^xxAp `1seYI!K)t*iRѾ4F"b KR0UhU'---;9A21c٧z/[[[ab1FvVK`n>`޹Zqh1:: q|摒cPv| UP(P** Zzo}wc9ڨ6hr(8캮( :uɆat\!5?01 Ü;w.xFIk]:+US}Z*o6>A5-?*׆aRJ?po,K̤?ɯ&@P]v};w`2tܹsc!ײv냟۶kckiÌfSMc>֞c/LD6u :::Κ5gus(j8ϴѶ,˜6m/^LUKOA2P(TJk1:~+PݾPRt8Rٶx<=sN:_uQ<۩}wyIکSMMMSNA"< U3 jZKf T_S {;_ &AMu]U,R$2 l۶O~۹sn[d>þsg|H$b.Z̙C~޽:Ydo( Jv=Oۿ_#w{`)`ojz`umF*BX9: ?K.Y'EQ*/^LeIDr*Fo_Z‚`&| 8X~j;zVbǤIn7q K(yF',SLA8Hv-O@"B.C*k~[KD{TT>Z+D =x]{W[b0MSa}{ַ> Y1 CFVmtfYRqf´AwwRN\Oo˲`Y0(88544pCCD"rʔ)v>?=( ~Et L&md"x<^7?~]}pFnl=ml=#z  NU$ Uf7cyeuD"V{{{)Ͽ? 97N&4Y W{\ ak9նVVXWU@mݟ[_}/(L o)hͅ@TCjI0,fի{SQ>Y#8T YҠFMC8F$h4Ji)S%Kk2mƫ$64YJ 0, >SQJ!jzRQşFFn^?rnq'sv~̬s̉E{<4^ oՐa\/<UA M7-PAQgWF&(s$iv̙3D9e"TKO?d X/ ?)F4o&! ! Rӧso{@lBR)&""!, uioZ}u)Y&l;x2A-FB--|Ȳ,BR)ŋ/ 63LʶmU@b,uzQ+䫖Z[+պ/Zg %|!GT;> oАAyƊ4儎೎Gz8y;n @D$[RJuJ6M[O5V?H-I%2}`V&uzNcꝫ6{ZMu''"B3#O6Mo`yWRJ u8fCSAi3(:Վap V"^B)gM;8>u?ADP o"}l޸q--- Jm[T$w kMV{jhYu|c9~so|i׆.x}wek|ZS`ڻI`J) e*1K8hLD/?rJDl)Lio*5`]ϞPt=t.Zjȧ`;{,+6(L!x"jZbŊ5l6d9(zZ-՞z-Rc9:, blZgbT;yk=Л'v ]JR2ʏjED.3Џ?;;;neY @03۶MiVPTPDzA mV|9 F!VKd3x~,{]fzi:@3IuiL)eC()^*2;wϛ7oRHp`nLZ.k)XDZp}B C=mXMT~ iJ>SO=u oB1BJii-`QbdddGqهٶtƝoNb~#Wⓠ6e$|j&{TS5H 뺢?ZqFac 3D4ih4*J8{`nWK{-W\P5c`_-A_m A>BqߛSh~ Yͱ X@I? _PN}x7Ȱ6W^۷r98rL*<Q(3 8E_A\͠ cH5[5O۩fjyEX\.bڵwqϔRYyMkҾT("vilhB#N\oʕ/kllL666&d2&x̲477OI~ e\PBZ1!DG'O{EO%R@0FٯP^'{ɓg23}03{L<걜 & |B i[24Gt*Rg VJ qGY /,Rʐ|'?tyrtyS)/K-[8sZ= 2ﮯ~1-)!t4ȁy^HwNFGF3+ZY]Xt}}}[O>sښ"\ՎT /U8Zc25CTR ,xMP(Jm0XQc 1)xWZ<^rR˲dĉ'ML&͉D!a̬mvl+`2"3QE$ GDEflLn =O[7mFRZ HTpD΀k&][$ *" յk…'q%Ʉ/YbQI>|{ `A33fMMM&NADBT҅B0B!, { V\O3ϺԴ<Hx:_]3H0C$ܮ_ R'&_Ŕy-_o0C-ņ}p c /Zա4 #Ǔx< RJKYvTgL:Itzuݜ7S fQD H$D"Q0B(;hFV)g2KR@)"VFN*,+aWW@ҟFYQƸlO6֧MNthGS,V=pV/%AU>o[ !4]F8.J|OMZ=_03(b{xxwxxx[Z%`ID2YV(j6Z\.7冱A9YDd1 -bƐ!?# ?M\YvW | A伣'SVm?y6~4% \qi6x)^1hA dM:K`j)cUSH=H {'x=!UM6fPfLM 4hb-;"c*N( U̙d0C 2鑲 :o~N(l CnA׃a9DУ.fӗ"o)Kr.pY5٘3}s"ob-~ҕ] dm2l-hفE0#xλ96n + ߁_y6 )3A /b,Mfs/'բ,PX@5J_y cO25yug}?땥s~k/d[jz+zڒJ2Dp2W(@eh3qCx{Bf= r]ifz›} eLLwBzӀ xN:wa9Wnṭܿ%$(@|fQ "*}B=Fx(  h&ɣWOhQeo: /נ IRL(!OdiX$MT2Y /pfRU>}F7 ZE s[qӁ|uƒSBH4ko!vKC(h k"p]Y>}A4;nCJ;!IͬP0[Vf4Y6 BӉ\]f#7♭}P,j&vK L 6fA#ӽ9(;/2.g';j&< L`&0A7]G@I h`|I2oׄBn 2:ߏs'm위C"wˆ d€( [@ۛUˡ#{6ˍq)D2~"w{H d`"k嬦 ]l0xɜd~11<ɝEq-~@=o|b%LS&A &hgJc;0'y̡ǚ@YdSdU>͝RZ ФN =X6雀3`)Ykxo "RJ1FG@t?q 9Fe S fV.X!n;#Rqbm>hZl0tlL2\Ȏ* p R(4ʔ LaНce* ʁ0DTC+uR\oad%;WՎS1lTSr}@ JNmXrfHP&cQ;S(~b. r +YI"V`!L?5@`r93ޝrA\[jlZ:$4r[Gq`MuH@B4d^|qC0 j;826I?Jk󀛂4`BOea0LM*k!ܠCH kwύ5"_^g5w3sq$rB! P?/(0  =}ͿŮtla"û\m_3ՄARҀBE"1`;^gNwPΒ[D(PU?hmjD"L=B CG,\pղCy ;V?(Oс;#:{¤4M-L(   e | k_XP,ҀL^z普_S?) v7= de}kJ:P&ͭ#Bk6XF0'I,CMujH?3?R]W:LFSe%N\ԖT %@K{%{9/(,w8¯tiꗎل#Ƨdaa&ܯf"vu/n+H0Y" Ӕۄ!.k{ sGڴQf[UC1l% TLIla k*52*O /?|Mcu^uf^6uꌇPٍNE0!~`K0K/ac/a\z<+[N]rKXGxN89_NB` bfn³$P0,bQ֯[cbjX)GNJk9 J d]K}KI\C6KN#Lp \dЅa /r}(za ('`UΑ5fI wx9)eN)CcǜQIWmQU †[&/:PK DXHS9`#inhXktyʬ=\7_@'8 w#^ΓI)|:A1IL\$4)A`U4c`t4͛}>dyghJlhG uyeOf%ACyMq|!0Mdy> .׮E%A_)5Uzl +stѧ{V_t9.W}ӴF w4aG[0YGY9,ei &wnnGfpy*yf0+ еwaYƨ<nD斩DmA'L_u͝@ W"F*O1YRfFZ<_,o&zwfЈ@7˒#Glҟ7FԩLdEl,'?Ey-0E縔ZB$ 1=8Zc*ځ#krBw/*U[uIZ[v2ml+J`dzM,W1(D4rF ۯW k!_E/G>LYax#IB)`*L".Ngɜ8)>( Xŷѧo2`"C1g]%-3&Lj"L̽ B#p59D be݀G&8/z8(b L *ka %2J# il m}37ʉCRvS~]ZtaG2.: Kn\In1I ܈:́_[w%/zGѾj#\`΄@)ckln0f!Q"6ĢI ;h;x\Rk=y0& CrI0HsT_Gx/ܮS٬aReU; 3. ?إve UF3Tt88~ؙd4$s99+GDG) mRϦo}tfSC71 K6 r~K_Bpo7^|N)XK#j)6/y|cx=q&"Y2t`Ef S ^^M|_ wdHw$Qha2Gd !f #6P4':oz$yJ==}އU1?p @g~p'D8MF\X*|U|G=Wt~ Yfezlh~cXkK6߰ihoq& "zuk*-Ѱz㏝w>+,~Ͻ:i# SA"kri֙͡2{յgQ^9 Sq"c:ÜI3psia kQZF8Ĭ̥M̅u$z4{9F{>ip]-~ Ghm`Z-sk}H*š ?HD{ݫ J_`=fDxdLI;bpO墍x,y'ǝ~y6mGd4-m~i{ݙ&;T-7*imKܶV!0Kr/TO(DŽP@:mX1>D_Jwޚxt0lPLm۞ !fBy{~ß=(oڝ8w `b9isA\\ϰׯY3Js6MQIWC8K!@\քB~bǾNl4DdcUԖ/o>Ԉ, #lBo`"f!`򾮷0vNM5y˭_ e՟E9+mT:X`0)IY.$hHk+#|O!ޛ'ɜ vIi IۉhG!8ejBqR 6Si/Opʥ4oFyKՅژ͞իcAi]pN)es>q-:E)<נ@ (KLO'3y8+Bi8rC=kJ3w{Zb3:B7߱c [3h($j>By&i%JK7tP y7/{~&!vg~yg~ G|F)r'&LgD]{ W8/"L'R;!x#~xC9DFCZkAv{AX-QjWăM!CD`R@22zq3`a 0Ui'v5(ӷ0]?xFaF]{vb(200ve~alf --"m^Ɩʽ McMQ>}7[M f/(ji(n|Onˆ%2dXR(7d]rF\nXaBG;Z/zm*-nXJ9uet_+޼B}wZIlv֔>SY J)e21t0֠>dO :jRVۗ>rmsM)fӅ^٦i~0=u:GA뇟`X'S_}~y  K|Ș4GM=ޔUv*{oyW6Ş>Ct*̓D47ImAi;'GvJ EB'/"k[R,۷pZNk!)吗2~֜Kg23)(_mXuő>%uIDATWv~O#vѠk;2W `xnY_-*}8y>T i(OAxJTeFkTTv(Ъ L[h&i M8;7go> Lc@Nd]9گMJRQftZ[W;TV @U,^H+8G?~SMjIӊgF1B]:0>^}MPB)!wg$> |#OdPBN ?]PNB]O |ktDhd|Vf8i \Hv(]eeys:/Ue'kmko;WTF1kt~R35R"ʵA;yo{^KU.Pς (F@К|D{et3բǞ?gڙ?{m}2ٶ?Y i2d5"`%g2ki*:ѽ/W^1ߵV)fWSx@H)O3fxe:5"6׬]v}zdDDCf7WG7ڲJ 8&\mԜN#UD4fRY6Mn2g^0ȇL5Jѐ[xKŕ*bl+kpw5v[j^~3nm ycGއTa+A`@Iڶ!r躪3Zf.b>t?3q9сF=˨ʦ7aqcw>] k}[RέO"m.ʎıGJqn,8"U=Q0-qW\WƐaH{ms 7~E@ȏGg[<߽ųH{F;HucKy.M F?BྫྷH%0+Q\c#K ?=y kՃ+BSLDPZJb{R%>P\,s{d(yB@1YtX:˶ڢhINH&<ˣoHN'ْOD9PQ}=kg8`#Rq?OLBVTtc52޲jWg^䙣_qK ]J! 0ݰ3dY--_$;vṭpڴ%0b¤,TV^r-- \5iV]"I6uBV,\yZHr@Z6S cY8Nf?0IPQqUv630y@6os:9&? IT3q왚KXց i~g 45wG-x ~r9+6i-4&fFG`k̺ݻw"jgض0J~fas lA<OmZkmc1~ Boڽ$3U&n*U)cLލqa IDATxyU7}Ω^,=! !Pq0:.踼#qW}ygUP@E&P%D@ $$dY{ᄋo[y~TMuNDϧ>ު:ϾS?A4w{#rOXNF|nii)(p31DT#(^zi޽{[?Ϸ%Id2銈HJIRJ!HZCkJ),BCCCH&]˖-5k;k֬}A?aip ԭ]_{IDZd2²,Fmm-jjjNɲ,H Zk(8" !#ömv]l۶0V^]vٷ#i8`x≋~_\444 NB)uuuڊVeYf9~R X,"rOOȶmbŴi~s݆?Q"8Y 7|kkkEmmk!;w.!J9p''}_~L2MR!Z[[sΡL&uA+8ȏr>3cppk!CBVJ)۶eCCŋ?}=WJu644:ue˖ WṀ?R0Y@ݻ 4R)6 CKe<!ʈp+CGGG9G|Jh!@2쬯} _hSӦM<!9p[zۅn*2ydYR}zc3tuuE0AHHG&<WJ)d29sϞ}O7O MLFR)cŊhnn.8f]na{2D{{;m'JѾqwy^P,u]yrm}2h#9- s9W$IJs%|c9>ѕ?Ru]sB"?LBJZ,ZBJQD14MTWWS: ED$ PBϾGyny' zҥ7oŖed2)f͚Euuue$0f  8ʜoprX9np=/$ fiH$ DT*ڵk%\ro_r$ >nllgYg2~ '<esD)eYWWWc,( 6EhHD{40¾uC@ZkJW^yeλ߭)"'mNRMKIT*ʼnD<ݯhQ,kg&H5Mgφi{CPN!;SDs*5:ZUU50ٚ:uji˖-+~/}8EF'J2ee˖eY4M444sT֚AKKˊM6kxRx:4eYF*ҩTJVWWsCCjjj(N#L4aFT1 ~P}T:/VJy:{'866o|旿_13LTd2rܹ<{l)WG*y~w4>$]̱`] -*zD"A8Zc;v')aj @Л6m:=׻i|gpss3UprLVۃ0(RH ǓF)\~7M3\žI766hhhX,KIA|5*}f_HJiΟ?;D p LDDo4S5DCwڵkٲesŢ,KRQ=V@xHs=x Xo#_t1kVN#>Ţ`[S*x@&w}Νyff˲uuuTIGWJHP%3lpҢZajJqDVqDm?J(U}G*(=Jm;8NIZzqK!DDT=00iYVۜ9s]U2 =X"9.pE%APѵ*qm)Q"8zJJ1>G_tjc.J{~aZPJ)(~衇nimmdɒlV)dXhYEU+AMhQx$rH.U\W|`x1G<8;=Eۏpk߿e.; bE֚m۶cƌKjjjK"L0T)3W Q[iEq1'KJG!*m#q#U,R$v !ֺQNdePTH&y֭[_2eƠ])(88oޕ8"#N#2$'ĸ$QN qǟ#~TµR)|ػw{:fv:|ګ 3o"HnڴicPPMMM3.9J0Ej#(?>щIExqőUIE'>(ĥGSm( @u]8p<"U9 ,H7 ۷l"'I  >9"8J{tB(!Ĺ$Nho*Q-djFۏ@T(4m۶CwqՁVR'(J!!äm]v/xСRB$OG@@m@Zkfߥ1 6GW?]sa#ەPDa*ቮ 8D$# fhAD)8N{>+OD /=3y/J'ȏq=!Rs~rAqbZ#qc6nbɶmm6ȸ;{&3xr H?#!Li1!(!9 @_7mmm󪪪2CCCJk]8)$򠃝FisD\Hz9Nі@d.$^ʽf9:vqI۶qw޷~{ >G)b 8 "¤P{4_R]h((a4ef<8o޼lxHn$RVAT|\GK`oqR"+6ǶmmP0b׮]7t^xq"̜0  ODyf.'c!$|[V# C/yvC0(utt9s"˲a 4.UM~sd8bBb lۦbȎp( d~ԶA`Ţ6MS>zqa)˲L!D0D0&BT*ў={n_'"G0BuCqUSZ'W$$ !!ulž4i!\ql:/KN$H&IR4]HT$nnԀR^Rl[EdH$31^A%駟|wQ*$tmkkkkKKK[ssssCCC}uuuU2L !DЮ* C8s]{ `ID*@~@—^-G\A0 D$V4m% ݇><`Hy a2ADŋ\rZZZ\MӤGb%N]dt:tynkkk{Z)n)aX u]Q||7OY# _Ri4M""KR1^0f@6"|b@BXǃv!0,f֖ N* ugbc76S 7>ǗͻUJŌ$|CH)ϟdҥ+3iLD!4b6GD5y'jappt]۷oݻ16mZ9s͚5ka}}}C*[BYd?HmE_`÷ƅ&Y|w(ӀT Z H/`շu=P4?!!4M~ʔ)555S,J& KuqǶ[-}xIDATl6ߝREk)R5 +i骪t:]Ji)۶|>fYPJc'-X#{[/ =+>qZ>1 BC$h0xOXsWIpIHBBq`&lٸ|۞y DaOD$rBDK  Z"6r#"m" Iv 4zNoqs8KzWqcUBΉ 5$892LvX0mol'3g#%|h("feS`&&?`@GFgIaZøB^bDp/\ ^Srۖt` Qt< T`lᖳO3֜e;ӐzeBEV\xC RJdRl4 `w:_Zj /S зߏ_0E3_ΫAP ȇh!bf%p8ZkWk ~'oy7-NG=I` 4CK"ׁV3긐*!D Cq^eXuj\.8T@&d&dijObo }Dp1ϨFUS=je0~]p#}6l>R ]FfOt(uBB'٭3e&V$!fV@L`F[nT`^a2RP<{){}О.eFv "aHXk{B'y]!+j.'SFR~߰ .nP9q& D!u,^6a4zp8: L``@&@BmxN H)ap  |fbzh6C Ѐ%>D Ȍ6zM1(`jMf6AVrqI@牋5_@H)y#H=i/`륹i` t_{JVA<v> ьn]#\ZC%siyTݪ<1 VAZHwaJ] S+&Ҫ{{Q٨o+̬Y{Hs#~Տ~Apo3pp?0 6zgXi,XSSt˽ظz!ӛcnoLǒD=lDY =$5XG!V=wB`&eVj!+ Ҥ=h݊oų+p q?0 ʎPX Q7e .Zr?VlNjU` p7ҏ2JEtشuf_,1}?%9yP\ $lh?_$@~0D))$sg&`6"nIC e?ҷ4>"ӏ4y΂5pG>3X|q/3˘5K0҂0XX& _|~z^$Y!1t8PA QXSY$jho:iØRR" ⭾` Ľc˳[Gfkzޛ.+!}$ 0k0\CEDw*r?pb HY&M&baMVD`ԫtmc-Wbb#UjYWwB ;?œ𫔳R\d!h'5Pν>r2uJ5)$_OC *tbkZ2ۉ,U6t2j ωԪVz<!Ob#N4RQ7~J -p@&{R0  xux|I){OUN 0n}|sw@ ֹ&FXؚKH6U;[. O aK@V`I{C ,6R@EGtt ,-aS'^f>e\ 4^w`UU󊮆tXT&2L%p/33Cc$a8}V@3bH!Z$@[ǡu4zʈK{'%ZA U``6aY"^Nh/4Iu?O}:c}CLb!!d.&]<'`>mL G`x~ .Y㨛ۚ|='S`$Ȩ&BkW?[SB֪RhMZQPs{_uՎJ*g*f=(qC!|M`1ә|O# ,MW^BBӌX VAdrirsLӈ2h}A=3$z'ֽB}AA/|co!E']:8H8p2y5/ kN$&+Rm~x^tХ5)q}|Y;hpZjB+OW3"YN;Ӗ=[o >`2Rn׌fI2@>f[VdHJc| f4NfLPvF "-~=p .:#"W?>̄-u4p"FM054]s~iŧP.P=`2R"s[Dod s mbKq:wX{?o ph8CCHLQdQWz{'~a^jyD$,9s\!~ÞR"u E"\N<`dcOG{dsz357dҤ3D>d짟x/(z00o6J.R585999ΔA2(R7we]o޵e-siO%lX_{[{B7C*2ɸ_˟1s2`'نڼdӓ?Om}GqdhP^D!Ofor]f&K*{eoP;>NL&7U3 JѮ/E'<"ZcO-{{G+bnYʒ(z M J.#3,2l-Q:(qeferYv(|Y/ kA+zևHEdZ[3f* EMFq|1,"-?H%-Xٱ}`(#U%!3wRR5@ߺ}Ηl f]rv }?{ 3B4?%}c91@^蟾u+bGJ}*Ua @45ԵS9!P!OdZ>0 ~kVi32ir*)n_@OT<Woy`w_n~N4c@u%n ;T2f#8v!' @.)>" o<')\B&{Wkה`2[?DEkFQ+xڹ+ `u@;@GLLE>ܳu>t%=0J(0R0.@EԷfmN_^f 6&5Rv{IC fn!DgP|,4i0`vfeE[@Ld @Vke$.hT 爛={ze`=Ax P CW.4 qlZ͟|k4rzT!L ZkO3 @.fW5狅n*-=] a{C63z}\͹=8ucgבe !Bcqx *n~r0]sc_I [ @WKzm4Ќ>)e_P=p `{%j̗ΟfQ0!L0nn_=@dODT27UOGf3N|VrQ7ܴ_KRuB.&VN:Lf6ܛ >ؗà't,ϵ0}Lh{fa9WpD8@ <Y`y "kB ɴ䫮$^&t,= \3^]xU(O_pۅ!'y$G\?%>#MS?ӷ Hvß+5,loaf=mc X3 &ҧw↓2j?OۀB)tQg'ah9m=3O朿"\`5aPKoع.:3RB`|U̧L 0|`Af19醙SH;uɍEdZQ|'RHC:]T!L&ZR .ǕO ."`'^ק=OK߉WLjAdFyn 4`VtmoCz )yRP @/maL*L&xJ>(yz׫Ќn!D1}P$BRڄODC?ŧ4L (o{̉[v7X R|N !R#!L79⬅0{KDaլϓRH$SRuY~{?,D}A5~DT$2y^g,P7zչZc@Jya IDATxwř?ӓvvgV+r$$[p s>1ωg|` M!@JBZVff'O1ֶfWpyٝz U-o `>5_1D=7o<1O&UKT9sVl&òx$F{kjj:&Ly}@rhmm]gϞ]]]bl#\EpyQcmsض ˲9c NTUc 1LaY DlˢU$I̶m9#mj1't3ӦMr0*?C688# Be))ɲ̈ PU*RdLӄafP(P(@uXƘ ۶dYi|ޙ3g>|gwǷ9]+?D1s~eݺufA4i,rD" IpslKt$vk-˂id2<ɠP(00i6뺮iض5kw ,Vd?4?syW3JDL۶%IX(BUUX(,.AyD/2i"<]KH۶]( H;UmC$z0dx<G:fmsιiH$(?.i<#W?UU.i2ι\QQF(KP`jRnY Կxx6ra&$O$d2Lu1fY aٿ替 ?IDȿ닕eٴm[ 8q"eEŠ*|{jDHBsX~]CǕq+R)>00d2XsJ$UU߿+:37O #ٟիWUt:}v4I3X4et8?ӵ}_I I(u;|̶m^($]י(ik׮k_ތ#, P"{+'|w/P(d)B!i̙4޼R/kQ}U9to71 iѝ4~: 9|>oǎ~s^ x;%@r0xCDZ W՗WXX4 ɲlZbΜ9D"L$"(^7Pd̉ ˲.b(q\ ,˂(L4fYu]m~申>{ ^p31Ҿ}&rYBD|EEEBiӦu0~ 9\$yĉ|ҤIL%K߉Ċ"&0 %E m$P(2yt]$I( .#D"c3M|>Su5<\ @Ds0(o۶mΝ;O>p)dra4yc~~ݻwlIi4i$D-qQUY8")$2i^Ta,7AP( 0 4dx,ca`0h|/׿0jd޽qƍooo$J-TUU)++C P-I^P9lۖu]䊝L&0ӧOgމ/yվؼ-HEIS(FK1=8M01@:F,C&A.H&$ fr@ЂT句 0& qz0s ¨1G(,˂ $(|>reҲ{:\&~ۦM~8i$=(I&Lsah€H|a D\a8aQDFJ1ȐԷWҞf]s$jb&K.7M%>1IjD+iUTT;;;Hq!ۑ ׾M^lnnv§NHO3 b٬;1A8@;4 :s K\.'mRȟF"dh@ ѝ%f[xLϧܹ?4G9ϟ_䖖>e&NR4Dab1>Ef$Nɲ MQ^^EQshd{1t ԉf(MGL|+M,f0 feY|b߾}Goڴy<)xM`r;ϟ_(i[ZZ%+&6/Z$JoVTD|H꺎>!eeeuLD|)D/ƛ("f!s,p(e1\e4M* p0'y晗rέ`0(WUUS+/' ")+VYF] 5M ffGi]ב UUQ^^۶]#b!e"*C<CFfͶm#ϣx8̉FʆaXsνX,G|R /xi< `0ZZZ(Ti%LtGORO#YDPWWSNiɤSã\L˲PVV`0L&3*'FEw(UC0ts+!DHI>bU$˲:;ny饗NH0vcc[ZZN4z^^^>J+ "EBiWDI;|>աڝo.!_]]\.D"xSn={`0hF"yLD`&Fa>w+>h_L(Ar|B(++.kt'&k dYa@]GĤA35EńdYfm3ι9guuuvWXU*4MF<O=jJA K4|> ġ~&JXH*! bppe$٬;|̩eYHR DO(eYCmmmOa h^WW@ Ƙ@ pP;߼ I0766$؇1ͫD-~;il624>J@Q?$H䢦!35 T$$0xiS <:I䪪y4A>; 5\ jb(W꣦Ub-aD{Qc^( "A&4NL&uPUp1Ljcbtι+0B1'aR@}$I ϙ3͛7%\jMqƉ`0(w$K)xB!DQWC#y%M8?Mz>(s@I/1I:}&E#( n9ISfxNiڹs^]((.4MHg2hvP Y}.D DH$>e0'=G/AhRJ(Q;<1RT^`F@D,# tbbzn\Ѿ ^I 8`m[,rܮ ٥#1 .++;K/}ӟѨP(>0IHLB"P$FSBc|/A[)#"=/i,ᙱ\W:,aznWPq[QzI.0o'?4J4e <3x}L N{ĉDO:=s׎~7DX0 Qu}BTf۶k󁢇@($baGA%dfA*rur1&"ȱ$5ض#ϡ@`>7\FiӦf GSޠH ԈBu'KC\:\ ]BRl4  2[T F@:FPpu}Sj<1(8);WYŃ`cMMͬ.x ;Ŀ^ &@@WϟkkkWY(%`DzczJ1K)F@ SA Ld3P}o߾O?=*˲YYYuQ<"~)&N$M*1VZࠉ|[-% *V (jGM IKdZ`-Pڶl6j2,G,*е^姟r)epX>}:F iBD`ex 6Í ",HzC/ HR@:FMM{?nͅP4B@ re15Mf.%M^]l„ \b#'u%w½i"t{{gEI(+ Fj;#!hll9%s \ \O,hT HG4 |2IiAf-2`|PHnjj>у"5Q&^bp<7>RHs爫o1f@`xݨAee%1G$44`F1N1j23p g! `U"=F19sHD4͈D"jee%E|ƘkƲcOpEo<`<"׊cϧ})35kH"< m ߏ.0PQQJ-HeyԊ"1OE`I#i ̲,cAPN4i+//~E,U.5c&;yux;Rۇ"QF LRI9:H@0D8v5y,_UUפP!#J&B B!Z?pƘ1Y2"Xk1c5ČG/EFBފcpEGͩiԩSGͣ񁸿a@ I{мQL AS=\P(4JUU%\pvI%H$>=R848PyAM\#;ahhh3F@ mp=m D!a8/dGiS&E}>#t,'V>֤xD-&cJuI3_cil744-:- i^Rm$a:O,p2䪪Rͤ+SŠpEQ0*pR!b.PLb$/×zVK AB!TWW# #jW`$'MOaezG#b.2$qM%KP|R Zea^Q:kD)M@FU@RTXc"&.2Xt<Ѩ9b@Y SjqdYf\. @ŗ[ șiI$K΂ХRvӋ D nNq_/K1xׄDQ>zGD"HR(++sK|f`DRb9]KX4M bYLN3t:-IRsέӒ6n-|oK(3)!OA;J ++q0,K2M (&06cL6 Æ ̙sv6٬( 0u.N8QCz^~*J?1[$.R x5[^.u",A p5#I4^`L)B9R.K t0$ιg }}}>h4*;c $p-w8&b yXJ_n<HX!/5s( tHBRL7A5ma|>-bm7o~Zes`ӾC|t:^`۶-IP\k?jDHtvnT,NH|Za]\R*^hx@vJC+.Ӓ IDAT=+Hs@HIE8*D5a\.g3Ƥ͛7ܿ:Y ۶Q|'H wvv3g̘Q[(,㜻 KI:FxQ꽫yE/MU޿cEټ륌@"iMA.C.C&AOO`0J=QBCv_hHzIu['vIrdgA`86x 9& Z|^ ,pNc1#"aTt{#|c!q^<*D RJ]tS["S2D<҄=/$!#{D3 B,˲c`OOO.\wzE@`(8P(J?b?smL R zUEQ^jDyDt,r鳗𥀚7N=_S/y̢eY0z?l@}È9cسg&˲)IA 9a@жO?~|3Ϙ14MdY[UUX L'PXz'5;$1-Ds#R${%V^<5O)$e2D50jzbh  HB;t][[[ף (%̭$I9P>}o===9I"~?d63   x>w38Rɜ*KDχ"7$,&." 5*&"N.9Jaq_V,rZ;v@ `0Ү4wM&hy-R9g{W^$/뺮)9P$4%]יb*GJK|W*]!eN'{&ϳ<0сݻw*HÊ1zvu;UȜ1&kneys> V 4茱<\eg۶9m.HTn۶յkǛoȲ*++HYYYc~9s,cIx0QiDJO 2sHD.2,bޥ~R/ZX'އL DPb^q|4_4~rB`zzzb۷o<{{˲Ι3gmہ;wcPX1˲ D/Ht;E\4i3fT'I˶mTU`2J1NFμ@dRHXIy75;`R.؆sO(.$IJ0c#ģCKP( /$F#~w>W_}uiyM Sc*cL$9/8p{ dY#K̉*O^ʯ/%'fD[ʏՏ7D-졵ΡC<z={w߅eYݧŚ]TH#XE3]m۶3ڰw^tuu-p.I}:F<Goo/۱c84 eeen{[)Kyfi޽{qγH]OP u͛[[[[ߟa`lkks '|JӴp@o__~X1sb ~۶e555u,s,XtP$U2J9GUU*ض~C>c * D G{,gF*rs ^ fMdl'=PQVkp)Sb$I$Ib`!RU|ڴi8ꨣP[[j̚5 ۶ms{r9TVV '3#&t]t:m{zzl Eoc,hc 0BAιeY|s>܀?nN 6㏿2mڴ-nݺ~7͋.]{V8FȌ`hhhYYYҶm;c9% e]6555uU$䒉VUU\wuؽ{) PU---xW188JAeaƍ8QQQQy~LEW"O=N?'d(y+|j .\s?8p8O;e̙Q7o^}WWW_( $0 8>00F+kkk矏͛7B̘1ͰsXaLMܮŲ,TWW}X"ի]0(ȗ'i1ydߔn$G]]_Xǝ@d2$I=󀃛,y!$I ۶8v͢Im08\l\p'N1cF5k^pSZ[[wzWr) Q[[œ '¶ 袋.mܸ. hnnf@fزe ordo%Ir_~z+D"1>L<}}}`)_O8#ɸ&&heYEee3(u]gBg2){t3Ɔm΢HЇuر+w0ɓN2e?Xr厚G/첓O?<p](ƭ3 F ~|nҥ J2pB̞=luuuAuL0Guz{{]NTwwoχs9< ^}U$Ipeee8^fYTTT`x1qD׼DQtvv عs'0Ny"i}D"$E¿%_Y;v,-pLm۶9s̛7 Soڴ 'OvCTe$;GV(l4z͏=M:j$@\UՔacFjiDbRwwo%/xGmjjjy}ݭiU{{{z]'tߝzǨOc=쩧={_J&Hq'(I΋N>o޼Zkkkc)>}:Z[[b |_Eyy9=XȲF%z,.s5|=EBE"b1u]8ꨣd'{H=2 O,_mbL$x`v7;cƌa淾[s8UNµDBYwvvEO:97n~;1`;uٶ-~[А\sI'5>{yvgΜ)GQ_xh"^SSFҫbvk*%Ĕm*ªU~\r%G__T gy&dYFee:, |;I%l]יݻ}:::0T'?,HiduӦMUVm}@7zzƌUׯ߽vۮ纺F1}Ce^eY4zɒ%c{ b헍]ck׮};ECCCIoY[lɲ`'N-['I/ƪUx{{;[p!wk7A}"8أ$E2bɔ~Cu`֬Yxgyf{. T]ĝ\sҹ(k_~4+ޥJ߲M|t2hT=Sp*@ R/-?/Th2XIFLCumۃ<(J3cYEQR!C<&˲/ U˲xuLӴ CCCoq+O~rӥnmcuuu y:`FP(barok|4~zڵˍx'֧R),/gu֬xVrBTGH1C5 @Ν;eY6~x֬Y;D":k}zM7={}fl eYW B۶|>Ϩ T*\.?3gD{{;9lݺ|-sh;6KpR(bIt:{mg/_dS9sx Ipcx P$\Z4}L&F#]v٧>kϾ{os~8>4 @^tg^Q]]9sfN]| gq[ne(!˲x<޵nݺ}7n\5iҤ)ӦM<\QV(pB466rY]edY477cԩ;òe1W\H$©# J@Q5O2,|>;vsrs(F>Αe9eY aymYIzH:{>SotM v,R2x-iZ\uͫSNsWݽW_]Ͼ{a0rN510 }Ϟ=ٳ}ihh2qĖoͱXL/G4mO>ݻ0[4y N5ٹw}1gTƘ>"Α0,IR@In#Cy%\D1v0/˲)IRaz=ז{~BS 05k 0:ں4F%Q %>mڴ-z>wP6p47rΑu :dBɌJYeYN͙CZ(>˥xz۶m_<餓nPvڎ>}ݝ^1eʔ58|m QI"$缏s^,$ݒoqK1~'p¢˗[o^y}_җ/^|׮]y/M4]w]e]O}*k׮r뭷W[sYQ$IYI2iR1TIDATT@OD(rX`D`۷ᛏ>W\qŜ.{ӧO?6?-m>o޼fwF0H(mضM>AEQ&~׿p@ RgoyGuuu 7p>_]|r/78暚o3NRߢPIgm&g#<R xK/w{}޽;2o>}w-Zh/>mmh2)RqwTp|g?,~۶s~4d]]݉K,kpk׮Lggg_?B{kjjqox)'|rӵ^t:ztdY.psNHXc"7o~nZj|0gϞŋ/{wr9cǎg}vN:7xC۶>$/muQi*42&TVVC9<s9뮻vx[8shhZ|yw{{{s-[ WXq@|Kt,˳eY*IDڗc'|u]v5 ~ĦMy2;v޷o_>oٳg>wP\,Q'"|8%j&(5 p \hp o eo޼99睝ٳپ}O>_ӓ?dY*I4QP+~]hCwկz{<y:惃vWW͛b袋\vXIl5k<{k?ɘo馓lW\qQ$K,  >s̻W^}7;7y9+V8} 9ȠFVLնkUW]7n%\rk/Ydi$Iryy9bJMM ׮]?s@?+++_ß}޽U]]] &Lx9p&www} uE|'wO6|ƍ ͛76444MBl*dɒ/ݻ7u֤w\}wɓ;::pԟA̟g]v0Lo544Dgkjj$IFGGGۏ;MLʁbDݱcLJ^{mS-[{ws%,x^c(W88@/ gfň9z_zmݺn_}$Ie7n0k֬םȕ|>w҉s#`q{%MMM~_۶mX|Oti++(f0X- FW_}fϞ=q֭ݫW~ϲ{"!)>V&PȌ5h۷oA^~568t|{EۑT[aL4 ]]]W^yώ=czꩫo׷㏟$I~%1\{hH$UUU  s׮]X]]݇񙃣x_mgz7Q c>UU(F IRifgWڑ ( _s5vtttK~m6 (>Ī%IE1wV$uYՅb"H$&>X[[{lsߡrU>o>$I~)'Gq˛s>Uf*ۑd seYc5ZU$\Q h(_{WFϝn)])HSHɃiJ HK(B% !- ybiy(5BgUh46ۇ4Yfoz&(iE e  YBx03$euuuuHʲ\Cu]BT*{QAw}9"s)ndžaaKm,>w^D"sΝaǖ’Duu)Yws)8''紴ؙ6dCN>rRK޳%yGӴg˓TUmbXcdH"@cc7'Ν;D)5HdSJ9rҥxb#(.Dsuuuҕ+W'UUwOLLx<fgg?~ʬEni?~. 2&EQT !!dBG. l/ ^X̝k 9 z|+WY0zPX%}zG<-S~ !L'VٺqnR<(̐o'^D!A[a%-#EuVmQ%*,QK[5Dob/C*^IIENDB`ukui-screensaver/src/assets/weather/20.png0000664000175000017500000003546415172041035017464 0ustar fengfengPNG  IHDR>a IDATx}wřIAD6B$ه|&q |> c,`$Q" @BVJMٝ݉?kT٠y鮪 $@sQ@V| ~:[6D"Q[j9&H$f_ccc~~~ (P0k֬9uH&Smۮ#說Jib98pX h)--0q3g|eر)>B>lP/_~֭[qP(H$P(]ס( '0JI 1qt:d2 qbΝ_o>LW/{73N***Bqq1 SB(TQUUi!"$۶ ˲`6,G0 %Hdy~zv7 gݺu.]zeYVUU!0]יp FQRRB" TUcpe0 d2R)L&NamٶMhww7W~1C} g_ ?͵k;a+ @3f0THˇ,@xi¶C E q9g1d˷qMqq]磌k:uj+C?: /8[Ԥa iII 9s&F`R:2i3`YbǁiòAB܋iF?0mRj֊MMMgΜz̙R3[GJ+kP+|mܹgF";pϜ9q(g);~_0նm Lj>@a*283ƸdfmۊeYHHeeetIO~455uŪi%(6iҤkG]\\AL:Ԉm^{53De! 3^Q E 7BDiZB0\m5,KMR{ҤIs9Κ5ki8 B_W7O:u|8fHDäIp}}}0MlLiqA4-DD"BII ( 2ZN$H&\ٶͳ٬Ǒdf͚?0@X`&LnzXuT)**Œ3H WMW,b٬`l;秩 ] Bee%E'27n[S"@6iܲ,4M-@yK/YOH a+FB~?'` ;{@ `0MPTTcǢ<_(R EQ0濋k5MC8FQQD"DUU RRCE޸q_} /9- cP&O;v`0H" BT(->@ 4-?vPZZ: ? \p\5MC$!%%%bZ |/_4{K0+..V( u= Cխ?Un8O #IB|{݌1(,>Sܸqc>$ё /]tm{Cc(+**R"u{%Ly59Re|NwACF{.R `Yʬh4SMfAk&l d F`q$wRp ]l7"Cw-p$PJ93 Bzyy9f͚Ǔ@ p7CSCױ2c߲{?`}V#UU7o^CA!B8(x**>w\TWWBȠ޵P$LH%}[hQ3b B5]]]/첒{,x}eq0S(̴hPeӦM7VYYsbBMSB?@GΏt51)0 R)R)a15J1JiG4QWWf+͛QGߘl{ƍC)..sXypq^UH#+C\[#yWosDR _+ap0eY4M:]T*l6@ O}S IJA59g.)v4UM'L@8Czry/ҳTQ\ @Mz=^- ImsiA8w%ɐ>dsu F ֞|p8̣(3f f͚E eDyB" 1^\w F^y`d`'ʦMA_=N7/XGA4LYf}naeرykN `)Apa y<@v~}TC9ވ fwmHڂAt]PN&'ZjEwލ#H!5s̹)eee:P/_gkF ~/dj!(B@D?UU8N>I:4kF6es֭={ꪇpHMVYYy;B! B4Ne Z@OL `ʄ  NER߯N>ټy}ׯNۂQf@@yYYY`0H7T̲C$_~HV0Pe0Qwq$9G ء<,0jJrs:00`O<9rQycVD"1G̷/ԩB u$Կ!Dϑc6tr?x*w8Dߋ.pabI(sUU5 b}O>D&O*~R~|74\;F QL (J/>&tܸql?8o}h8(sBHb=U:6z. NUX^={j1c c͛7aڴi׌^D۫7sd~*P~`Ѩ[ن(}^?nwHU~<!y  PD"`/0y,3J%_R_C%:C?qyq_u4~M~Y-UH㧟~\BL @s#p9;dBR%wߐ܁^2&^Mr݅`?I%?? "vN4(ٳg/ =1DP*՛5Y\HwYr&?g߅4{5xv1Rg̘q|DCED:={lK80 D8?IxyTfr 1P t=޺&{PfOu~9UUU0^2wm]]];i`08qtzI>ey|@Y{( @ ^u,&xA0RfNS^iΡ4RH @9-ͶoٲeԩSdYn&MӃ޼|){y3~^oYo~jXN^U; }Ci/ 39G8h6`a@anu֭:s*L&]H$BL0O2=3f@V˒/~?C-IVN&?LtyJN ,r!feY0~ȥqΙ[Hў={6lذaυ^ؔL&i{΋ ߽*;}JV^ey#ہ^0z5Ehɮ\xӥ@2LuB !8<̛,YYf}o̘1PU\+mD^E {o9e f5JiA_k S̓Gi9L:h69csy,Eǡy*49(J$=lr֬Y!`8/Ox;H{տwcy"Qr~f. a8r#l6똦I-[UEQXCL:cLG:!${]`pĉ pc@~ݛe 勎srsvCtoX^{Lj>fa3;{")iWV@`QJ q2/ !A$b=o(JQIIIiYYYyiiiiQQQ$D6bt֬YSjkkuB 1 >3WELjNɛ{(ԁ?bF> 9!fYEά pyWy=~yw6t "X1G0 s L'w@1@Jn9\^#.`@7lgΞ=~``qA7^eR:;{1d+r~[9-"eUN+qPU(0 9'(+W%]{9tg-iGOEn9gcۭʑF s.G8{DKJǶeYȩ8 q8A-Jl۶m[UUՄ4W]/qrS2X8ń6 tK,{ɒ%V^FIbw~)cm;iZ. W*!Du,4r' FZx{s8i̩L  PV_~zZ?=d{d:ޏj^[g_S&OεW!?@9sB4A8RUU+**j****J#H45MS(1LӴ2L&bX߁u>9Mip)*pqiiiY4-F!M4qD"GɚmU5:Zy @kCr+I(GH~:~i~ 5556J淮K77۶z̙=k_o"?v-{) g{/M%i< '!T79q0#rz_[(+]y?u7|kW䓊6t.z_w߷ @[Ty8rٽ4rN!r`5b:聀޻w~-.)9D"a  Es+V_lfҧDџHju֩s˗Ϙ`7J p\&YFBTBmW_]-^o 8\wUc_-@|j-K.Z"gºjjjֵz:z{~WZQYN(n+++ 3tt46o3^wMhjsh+$t&= WmՓhq14 ƏI*nҶewO_rv]}]y2Lwvl|UO=Lݚ"$ƞ.L4H)X2jK\"]]~=onI#Yb3|eP7e\SYIIS,Kʕ &9!oB5`SJi*Uju}=Z˲PzI&{Vk,s?MykW}'OflS)OWǟ~ogNUxgg.<}DZo sw,CH)(q,B)UUUd2XviѢ%\RG qc˖-: յS__~?9;HPoPVaÝOi ][3o>9, mxWӗ]t.^0<;oƞ2Y3hk[ۦdY{?g$r1+ȍjߠ|>\:@Ң" $D4RQQɳٌ#ՕU8<c^:}붭7s޾8ql6TW:)gޚ`ukoo׌#{QM e+7pCk…0Ě:Q]&M F% Sz ն?H$5cjX(m9Rm4PhOqu/Ҳ2G{wL`[.( `'TҝM ۶w466nܻwP/\ "/8MYbי` ȷo6 2"4sN4%fL R,鴶q{3rCKqx`:'ljb&۵w Aap,FM$'0z&OnssPal:_iso~셛tRiWE_<_A*?n?ɪ_uWgϛ]\\y/^${IX>{y_k6ϙ1H$$ ҟH}GwX"h 6777J\|'N7H4MUܙӱrݛ7?zRYS_;39mg2gsg9tp㊢\Ҧ7׭!pGJ;"hnLuտqck~k x${@$)ɤொ"/o޾:e 6M`OZA$p^gvʜgH&1NIQ4WxJh$uL8 ~H(9?ZSc7rt]M܏O"Frڵ_l)Iuax/{ĶꪪXwOj4|c̩SqG+ 7>4K,\0#glk#)4w}q%/.4LՖoF[]wU~wOoQ4Z `Y*>`(dv I B?YёfGS6i4iDBHwΝhkxk +W=ftk~~AnhU|NSimP]#inpH%s׾<;kIfmm ֡q{{윻bϽnjF8t$<<͞ޮ޽px6M{IhMuv<1K/,])o8b3g*Cn;qg;Uٹ}f|^ieUu2{;:_}z1ͱXl$Q=O,YdLEl6F֗oo)oVS,߯iF%tz޿M{l o|5߅3(%|tw74[73xx\S{q ?=Wzk|+Bҙ4ּS[7]z $T*H$1`Dꁠ3?djKJ];7ߵs#BŠp0\/we/]4drtUϞ ;w6)[vljB9slbaûș[.?c漢TUTUREQqغukaDt,5cOU%I-@ @ҩU]Yzzx.zw kz"A0_y-cF+mˊ`3>cnәxüD#pNEdݽpfyG]-w\}ƙs+J::6[YfiMY.e`=F6tX*TSc&t.}j R;[_Zp_NR16wC?pM@[߼ciYMc;/|'Op #8? |70ۨ(Je1:8Fʿ:wlݶ|˪8eĻ:9gkklA1;E{= $I%Ro_G$rőᯒm;N!$M+kۻ%T}pad"gfgYRjzIV*⮽1r3 cއǒyw9_n;N7eކa4mڵkFQ Bz?)MMFbZX_?R[zsFC"SLJH1w]qڶ=u]o_|zWϞ6UUUm9uon>c@%Rֶ|8f&}[^Yui3]61ʆwmw=z×zh4ӥc yݣ.g.< D^z_GpfhcO?+_,SUUMź)!iEӴ5'vePpǰ,5s?~Ï'pꚚ'~UUmj9??e.ulrFiii)@jX u]5՝G|}ݴ٧ 95̦qSUVTDJI"Aw{XW3T7#|QWS^z NPE ðLjVP^ZRQ9{Ͽpnyyy(Lf^^b^Fv҉1a`i]USU]J R0_UpAT[eo`(ͤS 3M*/(щpN}ev]>\ܱ lV1MI&N,{z|E֯ nUUd{cf6 !qv8O>ZtBi78f˯_Vo{}sisfoÑ%Zrwn̄4м{衇-@۶pr+l6>*Ds?%ͭ(eر{ϥO>tSpٶ`0T}OjBx[KD<v: /psZ|y;lˇJ'(>Nϭ2 qNՍ1[*+־?o1~B3ӽ=JGlŊ?vHt"i@%S) hQQlD:3Upr8/Bg#{&m모-t:s Uh<!_{1 ,˚,Ȫ# RPgNZ$cМ~?=Rd2;&~$G&cȲ|bvtm{zd ƈf>p@f{CORj&60 3}v P0Xef4AH2L&?ri֏0`j3Ls_J&#cpW~zy&:͛7U1}Lt% IENDB`ukui-screensaver/src/assets/weather/7.png0000664000175000017500000002417315172041035017404 0ustar fengfengPNG  IHDR>a IDATx}y%Wy{f_43h$ !cb!1IaC`\! qLX.@L$$ȣmFHh4[w>_'iy3ܯsη} h@Ѐ4 h@Ѐ4 h@Ѐ4=eɖ+lM)w7O Lh 18lzΊw+,'l+Vp"wO$-璹)f(3?|8zb8[0?AY]TR+Y8!HOȴ*YG_g3UzLTw=6"ׯi '<_ł2m/vw2]8x}_m ya7PU!\vi2F515z :FmPPT߃}x$qwg|@ -;t(f&r 'sw7iUCZj`?ڗbe,1iw"[ 9#PXB"S4& ([!HZ` @dDqf1(@OCw8G!"0W,?WynR,+X@KPd #1.@ hjZe6j  ۊH<0EHQ82y( FO FЛ\5 ´Qf 4C\'uJ俙 d(>4VXZiЏsǔM5r62- \~8 !.%AaI jwhB=| ˡeGWpRkN&`5(k)]}}q `eW@ӭHb\`A(R`օIܺ;7EO;-(O2_WEO0觇645iǹ/xxPi<`0zP@h\{7G pt @+a51||Eac;(í] ]ҴM4ftsT"Xd"@9f3E2E]8 f3 fF\a)S-J5=0 c6R;)]D9 cCqz{i%h(D{bfdPٿt*Li'U_P(( 4N!Z\("vbL4(;y A(OrC],`IT@Ҫjyj)$c8!"wjF<7c)ʩ^C8@a 8"$7TigRYS@AV7vҴPJ\^[tZZ߈/hOcA'Nx@95#sh6SOf4 Eиph.HYZjCy.OV? G!(XF@B@a B :Z 5* y4vPX8KPwJ@ZJX ! Pbr=*܇V*М_]Y,ipZKڦpچ3Q*x℈4BG\]WJy-y6*ʠXx(ꚪGc˿sګ aZ2uE*GВ1ƦVMQ2٧([Xl|}"GC7+׀IM0H݅h V 㘩PUN/&p\\Y{@JipIUFQ))NLx\! dM3#EP ȵ+Ui)Wrd,+zzd{;yO9T?K+|? f&^WA>5e}]Sy΀N @f[-AwiSMKBY9hm`9p3@Oԉ),`0M4XՒ fSP QQ+.猠L+SYf^ S4Q+ώ4e(\i9TZPi(AbAFsB9p F5yZE.i(311MMfµ8g O^3 ͦh(^>w.7'H S5G5:ҧ)XjyT*nd1i(b|X:Kw$Mv@3<>*Y\OZ1 ]L^FVQIc5lOb5;P9u9AP[WI0ҀS{' \N>1FUъ(9mΣ*4J<έ>Ԣ!EY2(iғy>dI+AOސѾbLj0H꧈O]b75(R5 aqU7Zasyyq|/6P0dl~ (bR&n-f N޿VMg4}R3v >GM03shM%T5UQF* BQ5VaK@Y_Ne&"F|QY 'RK9 ZO[<֢34۔d` S'5Pm蛓'{B,xe,ǮjZ(;MK!c%SL)Rt|f/j ԧ)B`Sp$;%HqErB2K1j _DqAbk,Ѹ#TsPkM,Y>Q{vP` w[a;iOvQN]DrUv'gy>Lҵt;(F|f qZ ~'Y+~(6AKf[5 "[OLR06`k ~_IS49 J.QIi,- FLI¬ҞJ2*&XRr&Lf]sS5dAFfP>1\¯}+kRtfSj5ب~Ĩ/М˚ D1@!ʄQP> cZE$\9V-2)8c2_ٮP$j,P/:b@(3Y@ >A |N8DP˫l 0`/kNs%RMZvFX6RwfBv _k^t)vufB@#J2%lW!Ѻ[c>Z-SDz5&Ё"$kǬ6jfW3d1殼 W=wSSvtBz~j6@iD K(R`r66gV0kla!E|d$!l֏Pk9jRzc[ 4MͩZm&be(*'QfwI:226;֎Hh,i ߼y턙EjX l[Q6N>?:APr(?D,2kV k2N >.x՘uҟY*0 ʕVj8ՠ'J ܷ?9Ժ Ww?k Rfǝ֕sf63I_vsشi݁vtl[s晛c3# p!R{?$2 zf6#VS3F8651\vǤ;L̷c襩s 6ϣHe΢HYufkeJXX_n˟4Fy.~^ࢬrn I:v8W箻8{a3644?j?@-EkuGyQ ޯcs0Koѣf=E/>o#N,]M~pyzñ<Jkx&ʏSyc,/*;/ ` gEE,n'QWWE!}UQB)R亻c۶۶mAa|YSuY+3jh3eHἸUPRG琷guG9t[ٹs[=p{E} .qck{jMb_eK|!͡j. !h̷vnmO* |,#Lqǝ΁7;>$B9=; &ɣ|UHFvC1[ff8̝wk.o_\m"@aRu "Zq/ܽ{-N>e 3f֝$Mټ@(,!F49>1q.cf  ;v%4{6d'?Ͽn[t^YT&WTn:qMϴ>VNkwzpÆU;]1B+bwtC@E8vgvkW]BܘeV"o]{ Ki !nM7נHUmGFbvГl}(|.T&z?I| 'ӄqeb]U(7iZţu%eiq# ؊Wނtrm. ֣g쥛!O%@Jn>66U׮]j޳4_q++(Grؼuizk37{^^\09qc/H?-?*- iXDKM;QCu O4~`F}<8ZȯnV(vrL u\?0 i<۰aM7˃o|:qD?WiX<iQ!2o5hL<-~h<=cެi#(?R?8Q*-nȅ!ǘ(j`@\uk/sb N6IDAT=,h ?Q"rn޾iEv A!" N2*ۏjcaG-ׁCk=-cO8 R\qlнmox0baV={q]_$(FHzPMߙhzHW{7 `?=!? L4ʹCq56lh̀gP~d(|i"Q(uaECpGg~f: @+( BČ}f8L{y?aum_xvz%lhYx^6 Cc٣&_syRt%jRNh Ϗyu{[[Vo?Ow-=O"c^s/_%aqt7 7_ᯟ{N6;aZ.Z:wBz󾽷z0 (^OgO%GugZ6w!|v+N4XVj~E%s}~n,Kq~әٲe۽-]0t?poE4n۶q2?I͓2 %_6/[t|ƞ={$N4uRg)htO69vWD?ƛ֮B{O}/^W,GA[QI"Wj\ivw:8u~S>͕ɞpq8 wurwnВ+J>/Xb$u[.1|.gɳb  0lVǦsz#^9HRr!'VDR`i[bѦs3_N΂u̐O9~[⿺ܝV8qchU7gkw>\{?wMK?ȱe8+:r[_v =|`'o: 'ok~ߵ"7ԥK+Ez( vٿvZR>ZtNKO<{biÆf?cթvкI1hlXt.i/}y:{/|KoWt5M؎"(\Z)@\n%l=6;w͑%h[_Ms: 78l:Msh va`v܍,A1o~‹}Q$:,AUٺ p9΄k;Q_|x/7qh _sd׽e&O;o/N%Eю3mk6;}d{$i/뚌,KAfWgRd}7m~F<˭݌ك?~Lb9Tgnb3!Ξtм|]'n\k7䇻F1N+% <o_vl^kW_3/9S2;Ə~#_[.p#Ȓ) S=W H=t`1Chם{֞;|#3  / k4jCp;zU?vT]^mv{5ʲi}੦kZͩBznyh7aOe֝[2kY7C{.t?ո.{63GGkkT'՛?o?:Ϻ֭`;l@2s?;_Q?;)پ\K5SY͆pOƿ|3`"dU㇗g`:trl@f h@Ѐ4 h@Ѐ4 ?R"BIENDB`ukui-screensaver/src/assets/weather/21.png0000664000175000017500000004626515172041035017466 0ustar fengfengPNG  IHDR>a IDATxYmQ.Zcιo%BwCHA4:59:rBB OHHvPb8 i\ 8Aذ9FU݇Qs66tϐV71jԨ꫚W@/^ff`mbן79svOun|zZxM7u=qS6}GwF3C*o6 7?'F{6']{ұk;OɎ=7osۓO}xaTqO6>)L'5 IO- ؆@'뼟$z={0>!@}Co۶EUq`E8^'7ƶjfwl}㞨uw( zwDܾlʱsnV6εmgQŷټ>/431"3`۶BUDؘn((>8 bnfm*+.q-9efqķUUQDTD""RB1k=(URU`{ofs_dfEDNnH@EUDoxK_  9K53CJB&"s6еR, tsY8al1FsFFlmmeU|N}b0lgU-9""YDJ9jV\J3ÔW9ٽ~m.;ǖ6sW_LDlO3Uն-/"D/| ]a=?@oϝ;g.<<4ϱm[Woa Tccg?vgg %m_m[r 3@1k#(X=V+._=}_>w뵻5 VoKbf=<<~cm a pV:gUu=!s#sox^f~VJCx衇/~WhT23],==>я>9U"2U5D4U#, |K_?gxG~sҹs0C=Ї~GkBޗk!{g>֯ޞ mۂkMbxcSU "?o~zի^}G_6mޏ޶Ϥ3D\03 5mm`2M7d'u{"b~`6 "ZJrii>˫_93?e׽NUzo| ~'~⫾뿞Fugԙ3g$OO'𸰜Ѷ^Dd=_}.]fp);D^rDfR ٯEDWV+],: .KmF:dc lˀ˗/MUa˿KX,vgk^١oo5Uz衇S13kkpgg/??h$*03<̥vIodЯ7OYq0Q8ƛ@D;?YvyٳXJC ^335y ?4MSFї5 Y]t:E"a~GEfDV~loobQ?n3˗c o@wٳg!缾" 1Fmqmp#j|sgSl,,gJ/zы)R 6M<\@nRJxCDY.ܶҸf4h'wA0<O 4#"#"[.4 {ー\.x!@)>;No~~F"ʛƤqx;TjϚp= <YVJYE0r|B=cd]d2/nBOh;t$ ?ya\^7|7-Bo_WҦ> }c>w}^8nYYh |[oN:fl}J.\gΜY˃8<<[n0xgokh}+__7j۳ 9~730ڃ>x曾雾Ox{vΗfjj>shQtkkT?OگDz/| p%'m];f:HB0+UW;`s\p ' q3#<?#?roŢ=g6govF8F5Fhk_5|7|575 3CU??77a|cSUgYPXJrXJYڸtb46Un47vm?O|w~^h۶{3A#.8;pCUѷ}Zz#?*:YN|2Au5j{wwΜ9׳0M7ߌ+v圑ŋxy;U7bş9s#H:… 7y] GGG׾w(??Ύ}K_ځ=\Ӥ-!j"dBqT$ I<^ϴMPU888D71om"13v]?zEo[LD0!MɟI TxJ F߇Rzif裏b4l]`)9q52lm||zKō B#27b1wH)ٕ+Wg~g(?^hJ o'?ɷ~: Rz]gmj_??).] gΜU:u? /~G U â_mNjpB0hM p\pJ)OwX)ࡇ_N>*zV+TU{;ߩ뿎vmmmSETawu|͸Urt)y_?g~g^1!}B33>¯ZuwDD cT… x53888FMi>9~2;4tڸq 0U^//;ja\./׽umm: Rp{{[/ӯt:5"^*}¶/~qU)K)7oo7Ν'C9L&>w~W~êʰvNJ)_o۵m[23XV?9>s#Fɑ^9@DxJmw _p5D{0YX>Mӌ8RU+0 OL:\. sbp޽IRʕ+kv5b+8E|$](C Z]?3oRK]%t^C RmB*xM=Nk]Ϝ9祔)3_җGyޱ\s'mlI|8~WfǏn{D&)ɮ6zҬ}p>y O[+{ !bNxY8儎Xoo !bP7Q|,*Ǿ0 $|l?!i`\^NOcv\hp>/| #>O7nf?g8O>ƙs7f~_<4.OUi÷;.I6fJ)EK_5yu9$y\;^5^zI{y~G 7O??j#:3}>s ~+ȥ dBPJmFf1Ʀ{\VZ6=sQGx޶@4mڶڶn4͎N`t,V)S ""fUBDa .#⡈,EDqD#!1SRRTuYA\a򖷌fx^`po{{;&<%61Ʃ&!z[f63DD@ϙD#C}ӈؖRRa4bfkٺk(jm:^VE)Ec˜sC4[RrOHI?kSJ[؆ҔP])$f "jp;s\+pT55qjknRj9!bRRJNwSI)eKUTJnv>03|kiBhC-3oQ4͞P>l ÀRRJRNdUxof[ccSBdf+D,)s"Q4f53M)%fRJj.R? !лn;NўO@ڶmC[f037MZDlTuw(4fmf' RU !rST3[lmmM1wC f֖RZX{?o49DEe "LDxww9eoXR1C[Xf6q$Ш23}#f3f:{^!]\+R̼KD!ڶ=Bgn4 @:%"-[DtO6vc[1ƛDdO1YD!lgQzz9h H)il:5!VU;" 9g"c(Z1@fDԨꑗ›!"*q0 їvgff D" N> xR Tly+3mv}",$}w=ycb] L[t)OӦmqfRʖ'8A+WL}faТ]8kC4)%܀+#: Dg!yV5s Sf.0y{[ׂF'q5ûc,^A D4U՞Tu`f %dVP40 ɘ%݊Ȁ7R$01þTD̂i"0`4b+E{0 YUEUo}&csڞk$p3 ={v6NO1&3aU望lv(8$\QTuBR!̡ڤHD3pv"ts{yf67˪]!̎<qq!" "RU~P< 5#FxhOm!3l6fnJtaܸ:]qbf{jw"FI=:Y:KXk5n42s4RJ3K)&IPg8"zU=rySEU{ d#z23gD<D"x4sDasGj__HϺ<0*fMJ!4TUySJ)x''6MӦ({Xw&̼ahU9w}߷031ڶ`i[̸9ga-"/2pЦ@לsdAg "H#ۥRgf="f9AfvBXF䒈\AD0 b8ry}1;5Eg=0Z}oL&d2b!-Umi9Z3.TxKD3[g0Q՛ͬ+suz,5 b !w D wK=Rzշf]{D"tQaH-%|v IDATeZQ9HUq"Z0 ͌s*"}y_U/~)@DŢ!l1')My̶hMH]"WS'ةjSJD V4 Cdf"DTQU5jB0 i "Di:p(WDXWYyu[U3q*ضgdLFUPu@^[‡u-C 37!Bą ÖWEi=8xĪDĆNيkBD}JIEdW f)1,ݻ3uC]!3clB½JJM~|o٤߰qliӘY}CD3"9' ?LB_8rFeB}9X""bjcftK+z聜;u׋Atk[{gPDB9RXO!QblfKHB-;J)+f"A\@(1(";Bi3H1~%ϧ-oPuij&~䃗d!ô8up!3QιGYN)%fJ)YNDSG8"">KF c.@}cyAK)˔""pdf~5s{.]u$rxÞ!b˵M9_rAi-r"rj%Mؽ;,\J;cN  u]3DnfS1=3;B t)+Tb ޅq4@po`u]DW[2ƘawC$VBhp0 ӷIQUwlWnu> a%0 ,%!eTuNDi ւg.󜳨ꬔ᱔RgfR.13.˹)yja4,}0J)jʪZ18A J%h}V\9ZI< #mf6GDqaM4x>_K3"3_H)5,"#SJI("n0#- XqSJ೻)4f̬&$ *Dl9?#bQ'"-Շ_*S9 `fPԣu9khifCw=Hq| R8n)egA< p M2amFd2z(hc-aXu]xw{ {ZeV43f\fq^7313o}  !H4l*OTuԧp,9Vv *—},V+3K)GfB6V.uT_bFf$X[(̡jp%3RJBij6Z.pSQK-D X.C%?츍RTԸ2d[J9p-glf{'v] N)tTMqvciIa1LJu">c\Ddz|;rB| 3ߒs^B}*$3XUvG5 >bǰr)e|43"3DJkL=@`[a )Gf̬.HbfADlkA!Rʾ(%(Rj 0,rWqsZe_JɈ\'ڶm Mڶ3KmM!4s.E""Da8e3G:Yd=78jf)K)!Ti#sgZ4E{ttxawYJ)R<){tQ L23a7&`v`Z ̄*m}je9PM90EgO`),i@jÝw޹fݨlBocf̼e6BS[+''DE$%816"r4McDDR!$ͅOYUID&hf%:԰,"R)eRJo1Άa8p߾sm=l u}.AfxY*qbP9S|_[)d_J)Ed7M"NcJ$;CPŅ;QƐu{キmvB谲ivKstvP ѲiN1Ƹ +Dtc !lDR}̝̍50i-M0"6k!DD;zU"%"Ú7`;SGn|QU[U%ѿNĊQ?l( ~΅[SJ{c#}cTD\&5EUţ7>Jl7 {d2I!RHWJ;Ǖ3R,@E4MyRjb_u޵3'UrKD!r4 np#p "]"Zt"~= a T>6K3v/~lce,C-B"pFݴX'֨咈BE]DDV7k45M.!عfnM\H|$ pcKZׇ?cl]MOBSDt jPdBi̍R9q% T?5̼jsB@'"=ֈb? PJ9dt:=B,"U5SKZҵ.$"RZ-j*'Tf>ڻskgybB!d;sQ ZefF5 Ts'5EUtZ\w P|o=w} X1I#v9g !3gx<жm:mi)3c}4VT)e5 0L&q_QmjB1r+|xSYOW|F*AE M3)9 7hf;R ^$qHV:^OcC`$PF1N1z 8cUwAb[,~>RՐs^AzmPꞙBIU۾R+H)enfW s΍P{ !HQUay -3UuQ3f朏fVjZBUuR9L&#U"µ*W9)V;Hy*XWu܆fNܨ{$xf4HP0T%U~!V+9)̬rlc\j%l:wZ~FJXq,#lf 1FJ)vtl}Z),hOUiZ@]wNs[3;(ZAjZ΍.!8{9TsqꚆl)e0"B,"hkf 03Y‘Unzv \RMi3b23r߯gG7+0` =6s4 I%Mk(yY,\0-Lp۶ 9ƘRJLDּ("invIO̼I<]uDɜƝs.IZ6 6qc {Q>+YjpttEj J(ZYADDqju~~v$?߬ҋt\.RJ3(M43fRY@\JrΧDdUBFUڍPaRW2geC3[@qf0~71ٮ3YΙR GZ9 E9/b}+1%QRczT8gRҦi &!h^`IQ@DcN`C\ %pD4Ra+|Uh F3A<Bj5Yp4RɈmvX̪ YU}AJ]Wz5@E u9DUeoHA+V} WT`D-~[RSe{D< 5RMPVr(+-(򣙍ݝpkp;'P%0 5B&"bf6+fALJD튈jdT0T/f=%"s'Ṫx%p )jDt1p8 z6 &|M&Ib-iyLlDZBZmODLkvD!ъDξ2g0rc.2Pj:\y5 C X1暥UUoTg/XJ}<> #{fvsפUAM/fjA[+)l^>TO_RʑV;ON\F Ʒ29`ٖsRc~F+勪= .V4,Zû"NTuj5GJ):ɭm t0,RJ])eY#mǵًˎ5>s+@& XYc %@3^8tuCw!!LD17\DzR@7Bmr5QU-\v-ZYPsGcEdpf tqy`B4-"j`Mgf[>gB1܏<~*׸uj%\diᦪ ETI#C)%vK)byRy<HZa-Y̪Ji6BF^;F*l>Sr/J2aɣ+9|7C/r69N&AjQ%R(Rv"l;<[]Mk1}h4Vs,u=k>4ZgL EDE$ReLK)LDaVC>H y5ָ@fE`&eJsTc c/Z Y?ڸ\%U6FA+cfqY->+@j2mR\R %>Xl' e5e\EGyay̮hE,祔]-2qs/ᮻ "0LTM)u9=%Bݤj)̼_Z)TG;.*+GEݢ5k!UgǕч -r2RՀ5`2@5@{qRV2g'_ khwn %!bQ2xfol偧c pW/5m#pjk1ƜcnEfRJdRkd}s̆mef/Y-VۮfT뷠>3G!yӣg0VpjנrgЫ꿵mbeݎQ f6Պa ;`/ѣVZuȵ<ظ0#3@-a#T;Êѣ,$q#bI4!")G0:,UEnSJ "o)%4D4n}G9U)eUJ<;h]cV]&<8LNwm;r5"/U=C = _DIԏ7Xky[D!NR]etDSL232ĉ5z﹔qQD Tj{W^ICbz1XY23Bs=ѿ k|O@n|8眢ұep}E&`4zJ<-MgWS>"¿{׳|{9pE"ٰ:"RzR+9d M,)5Tr.gxLs5QL))^Z4mJ){;?MV} Î}FjٝFzn! ɸFvSC8U}B -"̳ZAf¢2-!*,HS+㻡xT}x{;'5殔~9Zf~u&s9콟3T6[׵jN)ɺھTyyf5>} Lݺxw"e#ͣg8X-+Tm/+ōل֚~5gf! dIDAT2fU5Zo09##wQorHц[ffgh"+])]snBw]bV"2t8l\Jids>6S7[((윣Rvc)*"֚DJzE1EMOD} bqj&3ߔRNn`H Y9[,d1!afqqոN J"2;vĆAsܭ61sEy]~vΝ꭭"m8 >ιmQHөSJ)RV5U ` EClR5"}v`ϊO?nG x%P`g833)%P}Q&fΈ'pRMoר90H7^dAb~^ ovb:Mp΅s/M1"-`f7MS8""1 ^BD6]7;^x7T~Z[K)71ƛRZ0Oߨ[>t""[(W=Y9j O@D_Tv@G'zRbt,yJsC!I(ʭ-$WG7㼍[uTݦ ȸ{`۟|+*3S!2 D)Ã7:;>F=N"4Mc,jXWTk=j9{g~ƒ4M1|.vni R$zԆ>>ᅲF4U;#T 0MS,<,@wj"N4ޏZinj30'6ꜻELqGg4LD⍱k~1N""K7u@1 dYpM#}_?L4{@+׏XEzaѱF*y km5aG\#.Y7Sxn7Kx[Wѱ)9cƕW2rS+=3cj)L'Vd(%DƝC8!`aAy4ly/DtnyU]j33kkS5ElPu[k"r+ZZ Z+Rb#jF ^J`3=7C,r<nZ ^ٷ [5KBkZ2gBxZ;7[W64a;2b@o;sQCA6:mDg#6@봙F@UU/XVغhƬ75sx1λaXM!AZ:D9\ZkSyJD+`Kq|Bׯ]{w  ٘< iιsgIYMh d|C3q6C%zxĬl%|P+)?gSĘI<)ZUo{l,Fجd\tz3֌E+{ C Sl z`8r|\k4X 4fRk= 喴IGl@%(c )KtOGP݈;2EeݑQMj!U_ G83sW+SV2ncڱcCG(vl:A RMYԱ.bZ}?Vrpʶ'2sxCDV95(9=c7fbljìN#cFZ5Ͻ=31?y=l[g x<V%YQ(Tݼ'륟izjxݝpH6_{Ϫo(|TULb7Dl(!!ADi:*RD< YDe@ZJWU=W u'[p9v HTS*]ٙp8eބ ι4؛ cM1FKn=!d/Ԝ %.$l`֎DTZkC3wbQ7]{pGjWsoY 9":kFHK"ժ,Zk m̟r0O٥FD̝I"59c!Ľ&뺶/rחZ|o7~k޼y3[qv"=k $\NüMyA`Srx#*"9_our~,;3sy"yVR먻֚RW_})Xod~OBȿyf}aoݖ#. 8ض1{"*d@8^ D5K||,#8rVwDLBEZ}l?ODl(@D~;˒}<>.3m=s!Ӎ-q$ miѶ ](_^Ϳmi`kŹ\˿3^Wծv]jWծv]jWծv]jWծv]jWծvߙ}M2lSIENDB`ukui-screensaver/src/assets/weather/53.png0000664000175000017500000005427415172041035017472 0ustar fengfengPNG  IHDR>a IDATx{mWU'c̵{K$ !<"D(ER;X֧g*Ֆ]R)W*Z EB!HBͽs^k1~\k}O$<{~9{{91p@?p'?cB6F/6Fn#70MōQwf$]=]#Ѕ^99y{꺟ȓI B)!#d)$}>"nCE""و \3%{;#293I'QDP6' 9S>jeP /w~_Z F >'þ"KÅfVANN<`C"gfPb"Qi2vCODS:!&qN$8J)BjGE$F\k:G }asLPe(Q3Ȕ[ gaЙ#b<,kW+K=#YS8fuT$#H`)qW#@=fpI6=Pkȸ8V`{JοB!H=M{oAg)#q6Kԉё惔 6c'InG<Ց /ު(̌$edL.Ve%Jy Yq=]}ϵ\{?~b<{dg|=Dd "p6+/iοoKݏ}֑슪HNZ&Fs;GQ&QUjPx}fmb !bHeQ{KR6 x@PN׈%~r`K>shSǏp]pdED^U.8~ԉa?JkmWo}w[6mwH # 8~ \z94ɦi;*-5~n8rߏjRUk`d@2aĈ 2jլ@xh@`>&Iv$as$d Y-Z5 k*`$Q dxdQDŚr%'v\싟NFY#>/ʝa{z;^c zz!"aUp9>N T{;~y&@v$k(ȚHA-#W "1RnBQ<2#'G"$wR&3gBދP^=1Qp0dDiAP%(B,a$ZtI[GO.~s |?4׾/z£+ *瞳yQy(R y3QT|=&Ї]{X-#WS~_\v)y}R5 F I-m,ZeVYHDt4:)H`H<Pު$ńH5dܽSoÉ~=a]Q2B{;_[^o^YW1?r)"\xOu QUz~OKܯt3~'_Qѧ|cgqqX+~ؙZvjIaMU+9d1Ni"ә(O#̍}v/#_#%X[1ik0d\s͍Ѻ9Yɀax{;wo!|##/3!#G]g ^㶕O_y3?y5.~ܣ "xֳ|ӄ :?~}te?f ժ*ʉʼnT03/8CdX}S/}Ig 8qD#"eo)l{āI[Fnҍ +'aw =QJ>D.ixEO1NK.m;Ԭa%M+t`f9q 7RI>^ G+7J@{~[GT`zB5sB7[wR6s.z-\˾Yl+@8!{@?G?p/Sݡo>ۉ(ED"Oyַ~t׾c.~r" ?>{ו7[,/EvO|ܮ;']um*j4%\_olJ?LlX'u61`dR r=!ׇ' #=" Cj<㶛߄3§[;xUNvOjMHSr ;}?ў>_< y IJJM</W‹scAmbww/kW?H<]3nc˾it7L.Xc_vR]SMڶfad0٠i{sOktӄب# ~{^Z3i8vL$9+%Biłoyß9Kъ7; 0K.OoG*p~gusڋQ^55?Ol ^Gx̭;ѪODT-q>! r:z Uɻ;f hjRw92oauGn~{:X$3-k^0!ܤӃYgŹA)"ZCg̱P~TM+2(8qۭl:$t૿q~Jk_bGwʳ`pE~%M7WʷCp|^wLW }Ľ>kݴ\$}-(s*cSK^ٯVD'b='G ~:Ɇu0Ak5"‘wnDai a)

^X]vOCԀSs,9S/v|_}>gXJkZ!'=x/qČ&_O|//j]S&ÕG=gbF`Tq>w߿虏7>~ylrM&YR87t$lo A!4ugRjP5*D`~UDi(_^3/'=!*|?Je e/a9zi/x~R>@6.ʑcg1ǏlQ5R8`;nq,˘|$g:w:&RL(2~ darݷB`-2-Q;oV;P8ΰ6TP;uRv$'obч^L9vs-rGjl/{<< Q"Anm+_g|o#㑗^ (rõ/FT];M:M`IAf۹Cvn%dQkN-@-'nhTMN֑^I5\+-@gAjdt "µU/8/?*<`]I txp9-G򍯨pD^6?z K^qoq`ώ}0SC(O/)RJ/|WC-?]ׯ\;d~!C U&ʦԋ OPU=Zpp`l#o.pXu=mQ=T9.R}7dUymm2';E o}dO|h۪nm'^zdlS M3=KNk~z@x?ݽiDQ)q}믿ܮoȈDJ6ANw57|7j?Jlg&!P0"4m Gg#jtSb1 HMFh0xӿO'օV$ S(AR\,Nif1V sȣ}?3DUEE'3f >!(\QY,FLeVVV5WE)%NF7M}񺽽~v Ƽ@ZlzPy0v#@jF)5u)tJ r02ȁkq/~_#}`Ppc ^j>soDp0udDȥ0|(;lU~ojxqhL|^q7YA2~mϣlz6)zfs\=M'|si :lH2< z)0wex lh4ݿ%w!AЀlI RG` #ٍop `?W^**5Xy}C_U`G<<$O>I{wDS[T@ MBgBcrQ h|}/w;/̽,moGsZU"j 1"1<6t4 Qxs18B(#$yLm4*ڈJ,eդUiNMgjը.$3oxxX_'ؔym)ys/e^*YBR̖"J )ҒlvBtPmHKH5<ƓU,0RKdjJq,^j7ZP}/M@?9zcƿCv}t^V)yK.9^#+^zgDUDR@DCUkjM~ܢ$HUF$U TDFD "f"4Zs;i iJf*"e/{||Ӿ^؍]ٮw'Sl[uln!b6X$cީ 4 ѣD0"\DUN15-"QHKbVbEUĂ1p{jU; "jK[6y ?k-kJ[:iTxɍҖ/ZTzj_+,!AMӾ'7 IDATfDN3GG%F&L`qڊ!Si7HBpڃlfQ  W/|d?[pOЮOhJp#%^ üa;Hdtv>gČj%ًj/@hQp/3YTrUս#d,HCtY i Q]"9Zjz {O"Z٢W3I]܌S$l ar~н z+&ܛWv6^p,o-`3F⨕:Tx̪q`,DdA<"j+bI `FrA^ LT:UŒ`5l%7$=Ae2&>|;a? 86{g xݕj,ǀH"E0)c2l((#,K9^*Z "d "Z&p[ci?LU# ] (AԲ^aPq4lI#+ѦQFD:^Z/K2BX;UMK剗=gS(}p_#iּvXw^[;DxˠZJyM Ft`tL"TQUK^1'-ZK`0 #ԨIeH *`aXM5e]+0T[tTDQf""c>˝BMH>Z[!TuWszls'} T]y%oE3K%"" k%axɍv*֒@Qmp!"-jχUioXSKnP#x R"ڊB%$i$a@ŚvE'وhԲ.5ߚ$:KjKT!(CLՊ틈FLJ(ZVaK"I$dD+Tuda&1S> л32DfD/f_·<Y)y^SЈv6Xj(]5WQtWB̚&D1V\@#R)CMM74l5ۖ,a܏fJ 5 (eUYdY{Y%׫ {$O<}z Wd](SoK%aE+ w͹|2< /OEԫj4S;BDy s/ԄjZȺ @KU@Z P@`%[fFH٠55m_=@SDkF]wTTT4lɫHD ^*ۄ`p& ky@JC}Д[pR0MW=ż'@BEjQJYj<ܕF QywJmi]- 5Q5MɯR," 4E p|聪BD=v ^J HDlK@I= Iܥ@ZrUk+@${ F*#j c+4+cKF0a}h_ooKi=ʼ o4/ /f)hdJ()Mz)sF4bRi[+ՄuD-2ڪZ4ݼ' 7ғ b"mE2Q[ @2[fOM-XƬ<:}U߽֜#3%2%2g{pK"jAHp w#KI,޳]{c׷袔6hҝ޹dEU^jhTSC4%͙缰.,%2G S ͇ zQs/Mlގ9hIHdzruig4 R wX4!Rnn`CFuiݕ\:(ѐ}tkD ^rm  3+5t5Ҡ on{NW QHV"B0F64 D T $iJQ0:$wo R (dȘلKK}-1"H (5#c|8\DKVőj mtfMHETy%JHɥd2=DCa+  @/;8D!pƘ:>#p@@d%d$]GU^jy +~w~^򰝇~K^L`EX+NehsE R5h7ZyLDZm[-dj^c+5bCjQeF(#č.[j.[F /3E   "Ny-)[2TPbb}Vj館ZJTm_Uz٬9%"UeZ n$KO UԀZiD|?1h(VMC%7q1c|hDBSd̬++зQr+jdž գ0imEH*  DYeD?Heh, ́B2PJ+ra\p%5m6KR~"nz5n\TgTt^<[%w^.<"poJ(TA5cxӕ~<#\d <%MD!#EZLR 0@B@0o+Wu@JBD 01Ƶ1tIiMS hFH#"*bTVƈ6JpO5kIJD)m oFOI b@U<' ԬZSQ& D=QuR%e[yȨ',*@R-#<r^--ܳZBFSZIhDK7F(݅,Ed4C03ZtU/{)i 8eQ# E Z31"ԇ5RTQF4 \t0րquQEdLնc4KW Ynj0g2rh hUKBRlH}EF܀A4>FIf.;Jn"*ִKKMV9"rɽ4m7["Hn{s <@Du%U~:Z#QK-MoV$mb70«䌌H[KfU 2K^j}Q-$6%Z5碆Exi+"ҤFPu5ϤFD"5ʚvR,Rִ6j#ZT0jNJT:Ō 4yR_UX@GF$QnlzETd1-"O!# ЉFB(Z4>z&-Jj_k0E<*i}.0h^YJo2pbVB&IU]z)8 ZmTDu"*H,Q ;֧T"rtdLҭ2Zjd 5Qj5L^. ja4 ځfU@ބNj :!ыjiLlU-jεϽW"`Ccǎ@@_bQ~HlAH1D Eq<u!Bc$ 8}kY5P;w-ݖ ggXkΚU%`+rX jvKӅ V/a0LۗOl 0k[b] B 0 MR Im?5Psb_ԕnf!`9=$W܁ޞ,%oo]m=ȴ(!3FTMC Si,Jˬiӛ$)iJiA)['i)I23Vnq\9h1^Y3#ff[(w.ZewAI8@jpD,>AꫳH;{5-ohW_{든2Nq3oY_5P@2k2 `U/Шd],y h fEZ3p! b^&bUAZw.z| Ȑ>8xgo=^[Q$9Gff 'H%vPQk TJg5C.T9h6Bpv<2fi5ӵvr1Gbo} k}xU 1-#vsY.h.ZRLfZҴH@FA)/G#@McM$2 N^o'ܲTՊ+Z;,!FI8޶i#svCJ %k>UCoRq[FtVL]9.(T=z8l\TνN Kq5^ZzA*3 CmVrFLb=\ҫJ˱ZD~onf9O9 LUːn])/,d dgGsv)dh6&6&* R&)[*[?ŪeɶT=s (<Rc&>`i~ PLP~@+'Q<م6Y~)-QAL=c qYXK~< ' UQ2ɞHr@E g̱-52Nv`ڨ*ZƳhf]pWvEnՒN6$ƛy1b}II*=3ZA#qW%gkgfqUOo[+I0SRT|4sfO1ȣ$X_؎|OO7?6eJLZ.Jě;ur1e& S4"g5IvߒoffuA3Qn( wJtE[RҦE%S>crH b(#LBR%O̘3|ZpoGļvfK{_WZ4 ^Y4H>YV?jWv׽_[`m MfhfD[%h Q賈rUMKe,_&e_!kITʉkRɰ̫tet3rnʸxi>bb$ }H üc6撚KAS:2T[so7)s68O2PʘNRex%'HE;7xg̘w^|(p嘃.oXp% # )5)EnBVCByYN\ݕP>yxc:m[,Vr9K TXA23堒`4~ 2rbd%*Nv-OB$ w̜BOeZIy#@nhOL@QZ0&)d|5@38=Tl}۱In{3~ЇPT[oݬXisq0vT,S3^J L1vnn|1cDeY&gνad){ Lo#o@eFGrݽ߼~l{KiPطT%#.cNJ=xffMۍFA`a#ro60@g>/`}RʜN7;͟W@i%V-V0h] W+MzlD|kJekIDATmy2(m$h,JלЋ1?PC]8L͜oOz:ai+J%P%.`VZ" ՆefZf,c ,#hl,#œ%b59WD*3ȼ]یmfҦ>\@>cR/O#@g"'74H*AZx<B҄a̅JB>I $_2rRIڜERLDݏ) .eLU1F͛<nH-t*cIBҞs*3YY336%]KsTahNoV ~CF^V,;H!8@ByUK!|z;Ol]_jзK>mxD5c֠:UR`y?n4m{2o)}6=4_37,x9bW9sWQ -FIJ0{vU/cEb2۹[<~Θc_`Mq\:!tD 3gΔtd7]po 0Mhj ȻX81Ry5[)y}:>8O~k/-f>CwF53"2R c c2c"c*cH}{_n}{1A޶X>z}[b%af,<$(])& 7ydDJ ! ׊nʫa1cd3wTn ۝s[fnb r48-E-$+Uˢz9HHKzpYYBQ6Ngʌ@l*˚2H?yB_|zC_R<)!iIO$ߘG$ټ] f}M:޷3c"194&csг2f٥{ nHϵ%\C$%dDb>Y -i0oٷ(7y̹KYSC1Q"PAj^W9j0%=3ߋx,*WfVa HsX3*\AxPu @}<2Z3%]̜z(hˈW11[z ܽH@tA4fFJupU4U6*sW]YUַ'oXӇy@ *]*&96LqE ' m 4z}@/SC*AE@t2IYo*u|8b[|79Nï~3'd$ם/no4w*sJ¼X><y.W2weF9u Y,wym2|" 3_[d4;a6H"CCWLAL,ncv`$V9(6* S52Y$s$e]7$Y$5'm(ug< p[LLJj+w(L9YpC܋mrJy}neO U+I ,7oHUט,yQ4󛔷E4IfXO`Ep>5)KvgTz*ʰ`JY6A-J9GLe"b6D/s-6}?4+YqA4# =W 3Mț*pcm 77p9J ̘0k˛9g8dZF|\) ½փXttyKr P I`1Q/PE"n៘pL9h޼uwo|z!)#RbմQ޼೔iZWz}9Zf#szE$~QЪ2sFAC@@)Q2`Y$s*Lpˬ'|ZzLJ*CUߺ$H( !ۑLe,ڽ( @!i :W\TTB81sn/!{se^׷}l;ͺ5Һ>oV]Siؤl `7(ٹ:{*|ouj3Ȋ㽌4Guۭc/$gxcCxUtU:hb @1' A:\EYI\ WoJ*ff5m)*e))ᣌ(3|/ȟ p'Aw~5Vv|`%% +r+Jm Sk \R} b{Μy;E*(̤L]S02( xDVl"ٜ )*]ڜ( Vwt33pBK zPbWĨJk_]!!Dm"sȄekZFO! [S(ss"ZK "{l$b̘g*Wc{D\X=1O&٪=O"E ܑw`4+ hl#bB%ILd 9J9 @-k"Z%*WJ?-% ͈{A O>3[2w1=\/??Sq1İ:s,fL } } 3%e骁9bⴈ;YT̖ D+Y!V\Xeu%a^3ȧ~- Bq:@OgL~="]g?Lb#*f^ +$.3^0f*wJn3+7 D:ĕfWBgIORA%`7WI~Vb`9S>OjsnMȫ2{+7jz=yC O+vS<%ʶ>馌m|4Z\@{f>q.5WJ@&S 8j=i,TFLR,]\ɛ+ )P'J ڭ"fFqM-ey1 {Fx*e' "e*̈YςuNIwו2_dk v4ߖiy7/0+s_fAlKD e`1y{!cn4*y/Om֏­0fw]@Sƶ8Fp|gi+To=8 :neLYєwss_Zs_Q K&C+2f*VQg)_+uFi,h}4;mE$^$\H6Y22|]H_9 e1ǜcRRV2ѷw?pp/1Zy0s9-h*G,yXFH13< :ySƘs_ʏu iꝩi?|{U|4v-ICս=+8mgýeȲt`9w@I/_Mˤk:/|Mky9.4v:߭wzޭwzޭwzޭwzޭwzޭwzޭw_h~UNSIENDB`ukui-screensaver/src/assets/weather/2.png0000664000175000017500000003121415172041035017371 0ustar fengfengPNG  IHDR>a IDATx}iuutuU`AR]AAy%%:%%CX{Wڰ ٻpZ]CiRä, "%"%QA@$`fz:st9=e͋ꪬ|;,`hhhhhhhh~+H\ Xj`k7Zs7 @G:cǎ -..꺞BhAw:˝Pnx72Z ĉ'z;vѣG=}UJe# :h5IJ(im/JϬ_[>|f2s~ .x>7MOORfY8l6L&MӤ"B$j=HHxFZu|>?tMw<ވ ^G 6EyX !ifd20MS.5ME }a(!ZbvvRʗvGy -~cb!,088(J,˂"a6hRȹ9T*naP2v}cs;b~+Ǐuj###Al ۶122۷ 6B]ۘHAE0q ٬r4M麮}:#;w;y@ \j Ǐr->Y, bQnٲE B4a?#'sObqqh4h6u]l6eElP(g>н~ RP/i^gL&عsXn]PH0DR"cffJf}A b'QAeYnnnݻ{X(o ]J ?OZppf0&''aB&\:Μ9Z0 }0 ERhqqQm6lhð8ΩWsCCCǶm2ZNZ ?TDv_g?Ąf 41::IA ' \1== Qg<]GhI`;s\.jtt';vW_ ,16=cό[|^,K_~4 i21@@.uDZV.@ !B_a{fF0 fLNN{UW}㪫:¥ 뾹gϞlB f!lc>9}D*AӧOu -@fE40(d 0P,faf0 3[]EQERJ) Лͦ\s-y{~~{ ]lh"qO~\.EͶml۶M " &qR=~mO6Өp]ffA$ui4MA&```>?b j Zdyۻ}{߾-o)+%|o e|2N? CAxzx0MLeA4ضa0\tA&~>4 c~~^j5r2 ‚VTm۶oweYzX4rضm(JXqΜ9uh4-@L5Me!@u`dde-q"prl61??YIv]7 HDLZ@`&ߟMjz:Չ'Buζm133#u]GV3( ð;o÷j g{kqL>T8#~mu-ƯzݘωԾ|]{OOz{{sL|P8Әݩtr[RJEQa=_m\Euu] !RД.1ׯ~'K l66iH_܃RD~NR4MyDQ$(qo}[_߷o+.| 7 !l6ԩSmR4u":ҭ[.I!U#ql="?Q%N #e..l`ڵO'LӔBJJvzI \ҹ'ٶM\ ~CTjcudi؍7G?z-( @d١b84MS 蠡9m$9, t0k*/0 Oh$PUtP}(a6r\NZ-ܲ-?h.HoLMM}_8Z.|LKЌm'3t3y+I;*< r?aرc"i  |Ǐ={\. BT(4۶mfm63h<X!!9cXZv &(0a e>Vw"h L}_k׮]S\NsG/rxxbQd26PiO @N9Yi]LJnD HQnA6`hhȶm#;ZtAc[h'|r?R/ fX6m$m&DPm M?-˂aLV@;hnJ*IHgR'D"wD\/~eV+]P-p>5=3o>w8====A65;000 HT;)9҆\m46"|=4\ cHp6AhQE("=oZ|iGrpOOfQyUW#r[tXXX@ZERB2CQtoރ拉҈i$kHu [^vE{24:_E}Sjl6k]v ˲ QӧOԩSEEsZ"l6\.$n4ͶI .촨^9 ͠i*xF%fhF!ͦ\ro۶ׇkFض 4PVq1=z}?a,`Exwa\.c(Jp]-w.:Ek%pJL܅f u(A [) l遁ڵK^{+KiސVƍ7޸0lBAӛ$8ؿ?^x qP(a c爐;E}ߧt,!qF\qpf3sYc:|n'-,[YA 2j(.8޽{T-YxD;w~C sbbBNMM 4s'DE>OfQ,8Nbyrul6QPVT P0;; 0pWc֭p]w`5ҼK3i Pf2‚ky6m?G?jmp>4@ϟrQTH?8z(Q. Ć;LRTfZl煞'ORx޾}Ig6nL&d2}ddi.* ƒ FGGߏl642mf/#t]GPHRN>JqiyTUٳ'-hcT:WMVq-^*8^1lz]gΜs뭷>t߹s X^\yכ)-B>bPV122x011\.0T%& i!O < hcnOON8|z׻(w1O5?jߣ֓-˂,KX%-BV3u]ewye# 7oJ%=L&i!Rrȋ/JE>Ouh`flӧ~ ahh(A6MZ}}7MC~@<.*JxСB L4@. ݻw_?44r9+hlZfLd pO>%e$'H+ТQ[&ASO=~_vl_ҘnQ'-}&(@0, o :GF5rb\.7Ǐ#$ Sԡl6?8CӀ~_Icp&r>66%U0D`:?5dߜonI󣯯i53WuRL?G  rJ&F[oW'w+ #U_0-&iҶ< Bb@n>j>~7"'@98N⩓k1Qڜuꫯ?^1,Y|4_WGQ_ir%b 0(|۶*XZO ?䘤%j*ms|>O14 @s 7|>/c,|X7ZU}i _E33Wi>>@Q6۶eRCcJ\f^T;i PJ;XLE !z.*i6/\-WWZx8 +iW ϝ<i 9,JF&vS2`Yg+O߻I=ˡaJ!v̙Sh Y@9򧃃a6E\6֯_/7l KM{+9hTʴa)-Tn'=(EȄ/Y/8uN< @O#vvMYe"|}Vu; :b3t>8p}}}brrR R-%Uʝ(U:͌QmB&GQko_زe f2MbK&<8jK">u7^xO󇇇Ů]$S#eݼW>W6>ЮP\5>7uJ% HEM]SJ)h0 Çχai+l}G߼k||ܳ,W^y, iܬ2^;$D` {@"zdfۋ #' (i}Su]yػw #[X O>HumݺuΝ;ePBWKSy̴Xy=*u;:ZjHqF)j"fN‰':e $Lt~2u]QC!o߾^ !FD7y/e]&{zzyjRmҤ3%TuV[v'V4)jqu]T*<Î;P.ۦIä @̧y uez]z'gggŭzyB! !Zt@`_ !rАn eNnAltGd:Vbt'3M"UgAL L:Q,h4077Gy$IB()-FZVFu+_/BKJVt*45\sSoo@2}tt4|DKumv86!\ɮÙiWk&TN&G89x{HbZ Gř3gpe%m[iDe'c"zl6׾{o !L)e</@LNNޔfe<*2hT;i&DgA!\sW ">HU0̻^'W_}DR.I"H4}ffz{վV. {Q6 7g2a۶#3L2SU0o0Mtͼ3W=B@՚4J3C%SYR$_ ^ΕL0 |'NG٦Ru^OSЌZQX\O ˲|!D"cd($݉Eg4Uhjju]_P`iaĚ69vM=)e-W,ğ5!D#6 @BdI!BYiHN;[TSOPgJV=iR{vnnO"Z ׏v EЈ6R4ҽ-!Ix:$-ۑ&Y@M~l!iiJ}vz.aJ2 0x;ޱ@(њ1CAO.I1rۼ4 Ja*3|> (i@H2rJs6ӮM3? ݀Sc "J۶ix4s5C9bx=f0gw@Uj:gHQuԙ:tugVoܭ.T8oWzOh ˲h47ڛ7o9t!D^JI[Gh-hk{U ӧ_m6B^n$)]S'{64iNS\bTЮyRxÙ(yRJ)Ҥ7j窝 5ʡQ\44TmmNs&;[O-8. Qa|||=7v(^KbIPi _&Q">2zK/t|۶m}ߏ<Ӄ im_M'uVu֐;Qi8 Q/?OsIyyj?t(i&@˹DO 3X%Zњ4M^/sI7}٧w~g G!`>쳇LӜؼysu]`L=N߫~[ T)杛n>BǞffz>j}_;qDxw|lF_/ t]7&Z0dh8߰a4irڀTp{XՎU7jDji񉮴$@NBG=ǵ/:jRݻw?~{\:Z&:&訣p޽w=k׮]bB"X Ԏ,vU8Z~jZ@M/tl0 1==- KQ<wj[&s!ϟСCeF!BAے/2@ٔ;ndRA (J&1;-x0w2 ,hPa'?ws+ZqStl@(r$KT+iUՙveYv. MӴ?whhl6pG'e ri8Μ4gO|2-9=Q1#hQېKu ggyJ9O@YmQFlա=ϓ'ʎD/y O?]替233h%vT.]-&6K`iVd(2%4I&!'x 6LoTT@2,l۞3K{h-yhz$g.5$(moX-HuaZ3q;%?F 6HOc|}$հݛdbii~`uD+8djA+N !(8Dx}}}۷oܺu릱,R177+G#G嗏ŒA >{xxxtӦM[7mڴqlll/W, p]gΜi⋯==36Sqx$Ӵ:W4Pԕ`i5^pv,^% @C>> hEK]qr__`@oobXv&15MӥAVkVՅ陙S huִK$Z%um/=R\(iQEz>???̙3V{ QI\r3hgP"'_Exh-߲iR("Z`p8xW$!+w&| Y-=VCH]QETO^jpſ &D͡&+%&6KJIΤV[`dkK, "{3i  - #f5 kZsGOK 0\G()h&h}?\ a#X\F'O*CK쳨J"i%L hj;wP/ ( ÐO -Ϊ`XNqݦ ",HB%@J3,Z8O !B)eiȓRJZEDI`H4l\Dk>irV$j5@U594Ջ 6ѳXg%@u݊ȒRyMG%%gakha:t/1|s?t1)'hfjRf(2e+%mE)4͏ի!944`FQd!(:S4͗Rz&_tu}!R{4Mt4Qr՚N'>אҳN/]jz#WɌ5Z5Z5Z5Z5Z5Z5Z5Z5Z5Z5Z5Z5ZJ ;IENDB`ukui-screensaver/src/assets/weather/68.png0000664000175000017500000005423115172041035017471 0ustar fengfengPNG  IHDR>a IDATxwdY'{ϹstO(˒`d|10b2]%.>:8aKB%Œ& +xqY_|yjֹ9˿{~7,^.hh0?ˋA6]yRv ?7wO+xsm: sO Ӹl Y,Oc@-2llk~G222OW @frW@̥fLD(> (u D%r3pӗ@ʇA1}| mP2v_;J;h{wscvsG>~B԰x?o{D66]Nzb$J]%؂H\a4,8qv> c_v~@]-B[3|Z IyN3X߰2 _( h?y|1:Oĺ1/=X 4DW+]gr+v6]?{\ .aXSBg`R)̗*p5Zs#=w*Tc8'U`޿=jU|#:ƈ3|] x(f/~ r֠42x0#<ĒjZ[fK" cl~ W<8A c]Zc?@@iZ҄z ~m7 B8&y9Ͻ+v;Ň?uŏ|ǭ͹Xvd{>11 v1 J <)i{Y m_㿒+W:.Ksc 6~e5u Ux^J`1IAD€ Fj͎} e| ]qK_;G}QfP x;B'V-S0"*}8sJ[ 29V} p߂?qG# Ҕa4;wKw |@z <`f"XAPIf0cZHv'?޿7S;\\ ,tI PaDE:QVv/G,2؋&̘C8P:S' ₀ )B5x0Q .'U< L]H*?| A} x)%bbNl/E9L^{W7"}g[D'1ag%Xà[gWt?^(m0?]p`vY }x@X0`DLKOyQ} 秌q#D㱣ȵ6[3cb뜺e D1(XfH!Ѝs?ym 7E^KJq-3)@x&9\^-@81d %A\ǎ+ vh0A@c⧿v?OVD3}92{8XN]-s?ڽc@;-&>&Xۿ|7_\S;:,|)2}&| 09[b&bO:?Q(Ñ;͖-w݈s d_iX` @ 4["~E~_Zt" OeL ޓ%oim}YG p~!3>x#T+/^qb4F|"0\d?zJ8@݆ h;10'W=Ƿ_yb QHmww>w׭7ބ8}%@鷔J13B\.}? *韛Xo'8օv|LyH443 e@3$ c,N΂'!Av塈wK u}E~A9(wt=ίF?xmXPEdK--7#nm鞮LDmk(-ygpY2DY[t >. րܤE6Чqi= H3[xI| Q8Sk3ݷ2i ID@XJR0<Ѝs?r@f} Ӄк'q( !ąG”.DO9l%@/.߼ T={$+xc!^ƛ+?t'rea˹.̢IF2c>'HC]wL|!!HV e_hlxc;]@< (`fܹ$.>*~~`hȩm$:cК. }C!Vzޠ?/渴'xrzKM!˜4" =LR<f9Vzxk?_+߽/W^'?v>ym͟x1B+Ɣ]/.c(-¡|"7wh8{(߱`gc<9D1U[Tܜ.@2/s05ЉU$^a}8Iޟ})^a mAq1a uJW.dw%_),3*l/. &x+w_F2ؑZ:PA,Uxtkq-^C{ poMoM낾`]Qt)\xR;|^f;Eeoz.ۿNsiPKPz ^*=߀n#/b"_ LNƫ“NǎmL0;C'bW(+*/bF JvP 0zImlх *Jɥ-'XHCE/[·~oy7L᣿:?NOrKKQ˂P9#.4ԙYB2jf7D'/4ר.G@/G%;>5?R $ eF}y9jaa#$oŽH! 8u\0 ``T(kye-@“8 H!,v2 3G}1h or4,H5V| x;nƱvGﻀ4sĶa Zl|?RҔ1=yWM ڽS/13VGSI xp4DG'ܧI~]1#GgY[эKlA#fIbU_$~r?[kCJ_䐩Ǖ~g HAR qdrmpQ|ފGcH3zþ*~Кc: 2N.PDł`1~;]+Eq,j%Dspq-nǎcfō&эRzahIHs_GgO`6M!˝%t=F.xra?tl@dʠ)\Xp| m,^5WRŅvAqҸjg)Kd-!I,}c v.9*u/= If -exRbi𒛦{3٠E3}xˮ~Q<{~QŚ"SҋgwǗ+v@tz D~SoyMoqq^`zjܥ}}[|p&gP vC),!e߆Z!ɹ\dZ:KM]y E/ Lqb HHXcM $,yqS g2(O~ қf@ju>9 ߱oljs[|WS];(uM$cO?:l6cHOI*Q \ ŷ}ult~Max;~gӣUzLhvlIH&KRv5~/#ƞR9WĻ8k}ObbD16"Kq*e1xjg 8e?vcz >t%\ MKra(l,P jeT8qR׫TZQ쪕5{˦I^7.@9ㇾ{`D,@.Y0q`JmVwHLy. Ój+=}}?V/4 HOt66Z[&ˌ'egCK;[YZ.BKXPہ$sц-E96JyÌ.`-ì7gc,>X VMyAv*.9q[kpvm^fku_nnlwӬIr)6>3d[ºH@(m=v0ES[JbOSe)2M0eh5= e7*DKOA"lqCIX~ I2næ֭iC@l_hJpgECHXGYR1ʀCsC58f1@rH \2T>1R̥P!–bֲjr,Zkv~dig gK?j˚~_7[oϳ2)|@i 8e(\vմ.l2, Ԫ~+Xn,CGF)0Z \sh(#2m䫽s})@ 1KkV*ULiy* r4K`a"KكRV'|"y^\VBą^WeeAaP9!-36˕җm?!Ҧio"Q¬293zFm#^9u>;nBa/WNz=Ǭ#sAE c4X E6J/ʸ΄iӊsGO̳3ӤDM,eHaXNQZ%G` <ˠMNIц'>󉏞7ܟeBXvֆmRM6&G $Hl --S V{McXL^s3I٢8B1\n!eM`P:e\eV#+++a pH80lm6zc$Tj] aȨNԲA&ŗ9^odCio .cL“ӂVW:01>VHFEj[ ̃N׌C *ׁeHirlPgi/~|E |_p\4!W-]3n_ (|aWț(` PͶ 3A2Tklo=A)G/\̔I SH:G12 .7DRH)3W9d$|Vt`\ %z]r+A;r0zhum?Bߞ}׏7DQL{LҥrZ##ΒHiju%֫{>t=s3)g9e `vΛ-Bri.e75&>$W`(\N| E$EiR>D髮f߸h[\Ȳb6Bk@";®16`ڀmaƤԱ`>v3c4hiŹa|0Ơy,3Pb~,G╮imtxQC:voĸR]h8se?dԔ*U{]?}&*33Rzm@RZCjT82?q? \Z>ox֦33$Z-ҢLm&eGliы\{ai~q|j:˞V&MMyE"y{~aP6(Y̲X1 32g@f-'zDG?~dm-H8ue{̀k IDAT]LGJ Q<@f9׎BkFs2 y m \SNwӔew&Ѱ=Z(Ȳn[ڊzccfsF23Zf5'IjT}|޿R0_?;XGv [{P;=lmyM!8QXs1u Xb2C >pbp1Ev)xL-"j.3bi!]0Ň *%ص,v9AR0oKve#FE$!d$c ivNT!I XS167]1jb ܂FKw@cn. ǭ C ^imX+..} vG/,=(/,=eJ9ZKmZGI!e!Yl~}Fb(&H0FBkž! |Ǘm")t fc iF*{bf ~*(_YPjkjऊ g"yhE1,9XDZk5WV*+ F>FjU03c_fZ娄AQU֖@>#Sa`~4|A/|ΜYZYVAV9okr@- e\n~@legΝ\XZ\3NZYOWKs'ĕ[.vZUyQY4U6|IJssXf4ϲW'};51QRD/dX[cK,!|CQPm><ɍzVV;ͭhclZ/\Npi!5rt!02\21$&|˿D&6 Z@R?c r/$5K.&I)5";nt kN㣜$)1 jAg.yb 5eZ^^nv:y\[`nz}cLW5{ dc;H"W c t5`VAJQ3 C R-?bGxC[ܚq(Ck͝n[mVV^g޷^]DJA2}M!bZn'''F eڕ:I?v@%{R2 ALD䄵wJ*qblN7p5zH)VF \-y@dn/g/5] <| n Zom/J yBT+$$͐.?6{XZݍ;Lic˶TDB""nRj|K瞰ebcc }8lom3kyR1aPu"q֠؄yycO@zuZ NDm,@jjY^YťeZ]n0FU(*}0vg^&ā:")t,zH0(>* ! D'Ƣhwr o w*2m uj 9B}ɇ 3S쎩q;R+Z0i]I'y(ŅB\Yp8Ni^//_hv4{{Vڢ6R  =g EO!ՁV*(A$?lS B$7pݵ:xؿRCk-\5s3lTA)íVN$Jڲ6X8qHQl>>Y1MNa.̄JҊ/[ ]J>C:2R9canZ yaO&y@~-ay<獍$ީS XJW gRJI;`i"(SPj~;Ap>X0TV$IAxwz)##訳 J)t#Z\<# Zh@Gd(aF$`,sBD1ҥ'Q0%ifyB+mV6"p";}ȅ5HSW\CGkLطu:57WwYcx U)+z֝ByӧIZ9s|. CŤ&"}™o<V84L /PZ,N'JdhK.3dH)X^:H]V*UihNfڥԩEgAIQ(4Z! EeLNnP0zcٴ}X @!73jn 0 / (kV܋:pig)E4doKpa|Uu z=|œA TYVrU XF JkE :1=zlԩ|r`p,̙mlll;BHӼ x=zd9I50=9n KY@$Hkbn]e němZ:wRx c@:33[72(P"bht5P0D;Ei%9Zz/\S*bc2g[X_[[.V cǎ^)bX oVֳHkn8F7-1eZhkփ2"a VBB7S\_LyU733&ܕ˂O a18:?kS&Znw.:~1IzcuNnq$DJGF! YB8=3w߾) Sʲ,K9th;riaaatwnll4{= ω,^/()ԩ+QdR9ccn X !%ͭ,}PP...n[\ ARJPYt F W%3D%4_n]Ck482gY;sj[ zQu. sAY,3V ⚲Ie49lsqG1ID>촡R5 !@a6r 2vw#k D;vRYN ;,#.O*\ J|ߣZBZk\su7HU SRyYzg8-:])0_ʅv 8tX乢n/4|ʛDH4ˮ&l-ceeU @4N̛2(D^̠&/Y !.է~,3iyQR.ZX4J_)^!(e q܉66Y][-jD@Pڠ>R%5lA ى}=ϗED21ey)'9 _`llܮ~ws61yikMO=y$\*# @$Y `R^/Fdj2CFZv3?m$k-qn]X:wXC.-WEOP'Ok(K;3]  ϝa@V`sc[Qŭʺ1om TÇeXd}vvb+Zkhml ,;@NM(""j6Յpt p 6ƤeBRe)<⨄^] z0733Slw陣ǚ˗_s ooү Rl666 ̔ȔR @eYN++nd<ϓ$9$szh(%G{LKOQn޸l0|wܾ \,%tp0H}~ CpTRv}pG`ℋ<*Y, Hvv` lVmZ`McDZkz":! Wvd$iV`eY&uL]*:CDPucۙ8M+j?qRDUY9K&"XhD{`2ϛ42S|ij6X"QEQU򺇩68Ir/2Zfsp|ڵM"]`,]r)4e?wN3Y rȲ&+GfڮzcO)l6 B|tt8Ͳda?[1ݿ{;f#f]WI)+ZNZ̜3s8P.No>]/%-Gu~˸DEpC; ۶? ʲK$_[YN4]s0T0h96;;nf{Ed4T7 ;wn(1{ 6| ) ."`fV3wv#g^{&>h8Q4[vxV ]}Z1cM"-㮭cyNB$R] 㪢irABu07= \}e;=8(UQnݺ/@o!:,+Y+%{T,(=Ӵ<Ol67("5F_%UktHV׮}bKˤ-[`)r'O$ kMA/9 `=-'5 yYʖTQ5-666zbb8$Q4mnJpxxluZPYo}aZl{ibk-iR j[0 #*˚X=m3urr4uxJP,+Sl@z]-] A0N&iTDQP]G f8Npx&`Nf7v2e̡뙦~1 SR5 -J+̀ bTQ ޶]ea0rZ^ `2AA-+z=+W(]ceZdrg4. -du-a\ \%,p`x~/$ 4W6@m@u1E:0^'&D. ft8ʒ8 8IbG^~5;7w~z0<$(6YJI]'ѵMVg\ i> ?k90MdI {;LDN"rSL-hlS˅5NC=G2SεDwH+Myҗ{I !uLI.m}_7MO>`]ۦ[>>iQţ @ !,˶WڶCLgR} @OQX1\7X ;辮0b::$WJ+Xց2 ,[,d8 0D"i4 @n㞃 ]U#e@U Bo:I`[̘vc6IgNLk,kL&pm. eg.5lnhb.(jGRٴ%N;4Kl!7X,-t1\m@pbk? hq$QlhgJ@j7n{pJ\>5=.};(5ڽ{S!|6sI 9(fU1o:^(7WӐhX8v6)*^UiEȶ{cL*rmY\QskRʖTe{95,ˋ8pp۶vx]]5)[$~RN"&sX-JGaE2Yk a W\@k9 ].zy)e;B;LIS"mx}y/W԰JFQڞj@TUѴlz=SR9 A]0L͚\0tA5t~@E$\H6u Kڮu+%pqNq1L6OѼER*k áM#paO =Qp ,tvU@Qdl $S̬Yuͳ0, |QRJ-0Mc˔e,~']d6s !@y6w>|J h`<2Mrqʠ[K$uyfV,REVJ }!Ó .(H:99l>XR]KuR^oU8kdߧ(LFc<ČDg8I|>3ivaf߹u]ƍ#/J\FYE$qe-+U <>4.Q"~0@G"t@.s.[,]6lmLvU$`gz}Nydz+f u0qp狰[Ү,ΐRq*&%3 fh ASX nr2NSc6Mt_#0i#^@+Idx< Wku4/'tP苪%̓0R -߿@+z]D蘈uv%0VqH p8 f2n)E 3\X K0뚪R~뷈^}G1m[ھpG`Y|rAۂ!%mZ<xgJTy^y^y^.(PVPv*mn2ml0$(Ҩȹ`X,g-Fיi9]'=O&JIװh2DhlÉ#P X~@;;LiF{{NV泫WMaeٱ{Ǔ1L/ue𧪇e,:>% ɭ-/ ͛wY-wsB'8"*%#Mmajt<Ν%@LGh (EP?/s!'E+k]gr[[}\l?7AX-q2ܾuJa}0?P$΂ br˵fH  d2tÚ =׿5߿7g֮s߫Ţ<:7ß=O3 On8r2j_t)l\w~/y;R'yQ莎[[f-g4 9)Ulݟ>>b֕R?\X,E4 z=e*ˆn޼qEM~Q` y^Okm;Ⱥp^Sys9?0lRfRj9aTQY E ;@@kA{{x0 h/h|G#3^=eR$|4cj[?=RJiԴ NJ^C@`ss/]׶DB@v+Dw`a2x(XN?N#ϝ.b,DUU¦pzZK9-_\`ok`^/|VED.?5M~e*lM_olk},_' _CBu[_7})lqYoɟΛm'Z/Г/yW״W~(p &3G0R^@y Xm[K"0Csy93a IDATxyGy'{s=MҕdI1@6 aK +0!$IvlobY%ݷRUGwm+ɲ2WStuuzJؓw|Zvw"_uV /(dz ;X\3 O|o϶RpZcx]}[~)䳼o 5O `޳)(Hlw*'[F_GWu*ÃWkwSMI\k@MNUȡwY hGB@?<>|k$ 8?t$1Zk03* 6}5|e/{D @q_\yz9Եm{D5q'Z;Mn_~qm#8_&YF 3_p')Z4##7 ,}F٩rV32Ī cO hဳ/.7RoZ-;=oG゚^XX.V}RJ+0BH)!$!(C1l31F&IBvvZ_??'{@W+_4׈kE;p z[CD<TMExGtK vM%QAobYFD-@X0H=O~…A:DHNݠ wXh鶄p7dwKIZ|9>OyB矐872/k볟k+&''YJ+ippPuA@@T=0 IDQvV t:u]Sש^rw//K_ҷDz>IE`s!wIFM 𶼈W@?OO-ush l,iEDbF-@d𶾜?? [#SoerƯx 8]*ۥ(Y<ɺ TTF ـ1 9MTL~%Dm w&'>˝#mx./+㴯e[&!o7~ݷo}C>rȞIyRaޱc." Ƙ,ւ̽ccL:ufggvÆ Wf%_^-jpdc B$0A r˫^xK@Nb=B $` 9* Q 6]0",IRU0\;*f-GS Vvko~|ᮏs 7߿RRtݻZ"Ii%>'|e[Jnecx8^ Dfk$fτed絞 " 0`Nml R Aj6Lpk>_|Q^lG6;Lv藿gc8*ڴ5w݉7'Ix>u oR^'8ZoF@":㋬1==0 {&aZggf&0[CB•12T*ԫ F}L`b̃( 6 VιM+g5aq~>uy0[k0'`A p:r(ل$ "p!}VH .7H@fϞ=kZ⺮ oFڰa.g /ryLOOq7`L*&RRqZGׂd" a6K`؈mA\qI lz? <=P`@&9_&oe0+X&H$r4ETwN<Ӽ˺t x>@Ȗ]Zx'ǡGGG!tF8g )Z+DCB@k )%y`6I Nbhhc-6XZ8#Co܀X@i6md*s`P+:ȀɃ 2!& :UYAQC֓K~׀!d.k4R\Py_~;FFFjRJ۷o.(!>3fYn6(zA)իD)%0119z$PEBDD0FCn7Ɨ{1/+oK@aL] kCiL( TQ8U8 6r@2؂^XH Dpsjkǘ/xΝr9Z(077#\LjZ!=q` 188ػW  1P FģWG߿  ֐"d'^HM8$1 B 3LH`f@%}U"Hft@|ow]}_nʮ"I—l 8(xrw1<σ8PJaӦMزe uBZVFFFj0??H=t!/bS "s"%wT*h4 Ppťx/rn:Ӝ >lH8 ({>SSSV0􀒳˜1 z3,?c * {el`;O`_5 Cy,?#TO L0b9LgH Ŕk] >sEd]B@jZK뺔Y9; V,tȯAр1gi^?8)sdQLmʫ0 !dۀm6`#sOa&m#[ I*LE02tmo?t?87b;'pLZ @A0~ZBj8xۼhHԚY"0\#|Pe1h-0޽ej*8\*ئĦSߚ[6 2";iz2n0[߬n~Yְl=~My/Ѯݍ6_cWEMn9MB CWRyϚAch.C|psQ$J>F!"t:^|!'xr6E@.J}XvS54NNO$-"[N aR !zтB`-k+Ed[7P=8a+*Xy#6o7xIw׮]?xWwq<۫m;'h@9(ٚ0'@N9q *vڼ,H8'F5TԖԛw1v(r0)]@T`R2P`X,tGX3Ⰽ}klah0f:gE@P۳g^{pppkܣ)S̅_;N`-Y<3øRB}HY!:Q#He—?hM))! HrNɒBٹ78tD`z1fldd$}ߙݻyƍ[E-=g8W򶊿m) EEq h?5m( BȒRO,.Z[9N b  %14MK5KCo{ө_6q~Ǯ|{E)Hs={h4R:|>"Au{(2l~׏{Nc ,l۵xJG@H3/x*ƽz^BpHtt[nIzi[$+!""fk >BL ={_NBp,^oC}A?\&t?2ˊ` s;f ff qL\BHLJdA@+"35j#pb`-(^6Ypr/rY3g-oM6mV_ϗ\r c>k(k+/?_^r@-7<D^res2*>Cy2 C^j?2~5ع0@fH*)gVC%abX^-P]}b ,4Z.3###ٳA h֭'gAϥ%t5gy ^t U+Xr_aE{*b0,I.u"8`b@PjR/.6B$ Y \LTWN-'!2q2k׮\z饯gM)`.򟞚㺮SSSp Y\+++ð5YՏy<\r>К~1{G D GG\E|-Cj1S2]a&HLooˆnbJ/nȓ*TmJ)r <:'Rjz3L[U)兡ŔX(L?_K6FJ_H1|^, 0+d@$T`ȩ[G6$쌇xoj<Ib8ʻbI"?(( Rg{~3.<l`sȠ 4䀄bɩ cDVM?|' ұP}2kr0czq"zܠ o."SH2H<~B: |`OH8` \U5P5KΠaw ʟ~EH:]M?ྦྷzbV31eEQ&"w pRf@=qcEx|Y˥<eQ,sܜrE5h`vQ@2R&pHa=K&V 6uFةT]=s{30\;;xsЬ)J?^f+rAN5{,!z3X>E.dE.|/`!m`bJ}`Yj`aC%(i VM%yuǏl||m `uuuc(B(z7em8g-r2^/wE SJe6_g?]d5IB*OxIEz }r+ưCweJhIr$ˮ;c{gi/`a)s@1[q~a㸯=[YO/Ё; IDAT (2:[.1ʳx~r6D?!$|Oaq5 cx0sfP`&;zURwA=cS{l~c_6 eޓF{2733snJ"n100@ys~P>W&zx;ŝ@me !l:q-gmK^l;z4"DP=e֦ cIJn+J%:OK__X>eX%@MJNcR`OzKKKN:u|ttt*It:bl7eX13kbEs?-\=S'y_K=W+ΌAKPXiFk@r 8]db#薠h^sJ/>jl'v. lcF+ [JM/J `-`r_0s{߾}_˶FQqRg˛Wʄ/*{yG?y)/&-ާx\m lWϕD˜ ah \2``Ӆ0MK?'1~h hXF4/ Xlx=ؗ$IDtԛew\s'Iիel7JRP?W8:bZ~~^2_k<[XHc9`"pBHV%E>'yhH!(-cM&נ @@$M!Ć]vZ)iV њ</&^E@H""[/{ʚyOכ+{=V_D{>>Q! #DQn0X@l &KJ~% ,!rVWhQGJݛ+3̈g`[)+eΙ  h6Bix*'CL0 tG n7<?|E ,ђb̭{mebdn"DFVĉ3;v<$t:OleͿ8sʳ0 v6@ G2ʚ}y|OtDV;C`a;1|f $0]A&!!SOuXf`r]֮|9OIP @H"v{رc ۷oq'"mZ2p_O> ޏey_Nȿ+<~P^ЭT'@;? CQ.FkObb GZn ߉ 9$0` #XN4>}F楔4&O ;cM@3皣"CmYjk))vTd~~I@sq*_~L'p. ]Qvs[ǞD-0Qq@6 l$ Jba?dd|oD/wA}A)֪ 9v{y޽ߊDql(yETܭ 2;.΋qG?S\e=ܿ)E(.VVcxx -U/B ;a 8&Xj"ÒMk䝌&c3r=0Ng$J"رc۷`L\׭[R2@P0J`.2:=L*`,nr3lQUWњ)se0y?(BA7XY| X^!pd¶' `@&a4ъ>>5β*x=d}Y9z[ZayG:ttfffl(D$#1 lgv2kfz}fbcEl ϗq2Y.'QktU7Dv`Z]-a&&P+NX*oy㛼}_YBb@8HM*e>y>YSBj~ZDdǷtM7Xk<}%~?]>PTŠATx#O7,hﲒYU]DQֽ;F 0\ 1T`BE`;ISAtW ؐ`buGpozo1p撰[RJiAn8]tQln6fsvnn.w7<:99u|||C2ZLl,ϲ~H\7,')+^Yo3HkYEpK! 7)8\@q e9Yq4Dl:&#A!(Krp(??:?Ooo=0$ɜ=y}^f1(Sfg km}kŔr"H$ev(߃ 38yB(+I4!o)9 0Zc,:KπG0T]ڬ]Eql2 c:jRzP/#/[0"at U?2UC }2^l[,:ɱr|'bmOjߗzZΞe I)}c y 8O?1AdY.h\t뭷g@Pe0/l.Eq$1pMP (q!FyCI m<[E5@HR:=jX݅+`= yxN G ʠk4 vZˠ9־*ZO~ʞ8tsɹ艶62g KU `̂0o&y ,M#BhkmfhbÁ3^G>0,%cz$X^^>uwW\qu]ⱱFVVRP^$O5BDa(Eq`ۅ<7Q\=_r.ӕIbF tg&+<78cD!@ WhH$ 4*Ab1P1 ^ ᰅ5`.ٯ<9&h"fĂ(ۖ!g(FrgζQJoA3кjG V\c#/|h WdcJ4;;{5[A0 =_BE!(0 rIknCn.u:];1Z0a2:!1# D"J!r [InA$Qh.:ŇPqAنς܋ Ld %#HC !V !(ѨAcbDy]NNİM%%:ѬE*e( %/Bg[Ĝw@c$Z8C#Vsk܀" Ï\{vw~ @@ (C \` HkӨL"@k Jp]8D+,aF"rK-)P,SwMd ts(҄0`Wzxɧql  xam**5;V.ffzgM|.lU/6YG X* f`(X Br(Ô40o_@9?IH 8RsNߓb 8JpDmc,ᒗA0H(# z *^`6b"[UU;/@$ h4l4Odao|4`؎؀"ғ C 1SWboaӒ Y ̡etJRL^d2͆ vk}fRHP `!BعEƩ%JY*,X°t er\ @R q>j-#=~#wUm(b-`=5HJD -x*F3—\7y"l&\РOa)XHXkg"?kX6b%m { k%y$%s|xYˀC̒ҤFEr j00 A>u FS|h@ $Ʀ+ aL )WTp-c6|#+p^X`jH(XjxRCŀ0hoZJ'$4:lW@V4s5&=V_|.^1  ej)ª*gn= B2V< ,IG1x'О=j%%z<C#LT?0! ر,1%Bmjr{A-pa hT:.P& Жbb 6qX} 1CH܅@űD160Dl([;=B2<4N ڌ0wy3UDR#_ 3XP TU,G*؂m(#lCl dGz1`$*$p,X0 `+[&+ߋ[H|yo 9=J9w q'"$as !"rafT6 6*7:ض)8 !{"}(}sΦWrs[3O@33bsc \{5P}H#`=Y03?C8j& J5\H L 1M%O>x9 V`zcPmR!9 @,-6nmw#u/@} Ê =8@3h9bY&*T 440EBzDa]MK`@[dpash;v-C Ch˜ڤoWT_@4@@ ca*^\HQĠxnL$6LWDh̴#.0`y5P?͓Iu`] ~Fi- qv$XIb>r cDX^DJGt&R_AUcfv #SY bHKx&7JtٓZͦ0XHpr"X_Sw;Gc }]}"Ѐ2j!E4*H ^($D"$Mɦ)8T+DX] I}탾.rl vA99L")ĩ'a/+\,!b+yAnҘ^#/@ֹ8fI雸 ć@H*_cuArplKae.C'2A-O@@5< E7r2/:ވ to'LGrmh*st{fU \y`xmFӭN1q 6v Ώm(# !z HA ! Xf}˶z"%${%ekd ex(uW@E  $]PŁwQP냀@ qʗW{T%ӪΪ)Ӿ}cjъx0a5`2IϲlA&"LI ml .džb pŗ21m *.Nn,a+ȀItYFK˕poضIOPx'!֖ؒ\]`cV O(DdM(t%-#1|{;g~O=bX&h=-Omf;Ixڻ 92{{fzC$lEcŃZ]p]JD]YBVtaCDɒ!$@$923=}v%L2ͯfys + KXnٸ3Hg=s3:r0y"HUMT3@1]5pxfh@ KeIՌT2"ٙ[!;]G6 tqEK"'>CZ(}n e2y! Xk fM8.Tl &lwx  Hr `wieaw3YF E'5\Kb̟;#`5U&C0nAw<-bL8{ 3!XDs@fvS6UȞQqb1[L_^*xgbSTLJ V!UHtHpb ي_5O>Y?*THTR)\Qie&{،wOTWJx BI`cAs0JQ3 Ѩ ]*tabX;,,>{$Vi}[Aj7X 8L!^d2DWh@~U}U|Y ";U`esP觏T㖤x9껀sC̶\ Vd[XY~^SBL~U$jy&tr lv3 Fω*!ZGB7/l/&IS^~B+ 4U=_)} ,=TRl1'IDAT0d~ k\JշspRs] CRlMA!| :ZZ$8|VuWHƅ^0 w, < 1?|H:+|fWi!SH~CΘ;Xa0#dkSb z'm"*x;|[D|T5ىa9P|*d2% 0)ԶU 8xֳ7?W^-Khp#q0XV1%~Ť=Jm<_3HBV|UME ,H.ZQ{|WSjMZuh>D\JJqF^ʇ+zBg?TaL[ H(f0B|wͲ"=Fa4n5y%`ӎݥ5#'f @@Pbӳ@Ƕm/"!xupYȉ |1c4D==vde/t~XbsHJK;N}=o@ tLV;w2 9 !¿ap%: Oס5H)tjmOJJЋĜ0ȶ3"!³0kwEϾh 1FvYM\D|$H +CFqp}$a^ֺa7J(;&- 1˒̎n/%TfwaɃMUDBs`:^ $] j@`Y}#*Jh%WBX #/a;\mc]TC,Nf͢`~sθg cdM{f᝚OsmI8  A<K@M'NJ9=q@ dGGpt~6Ms5Wv.6BQ@ME+K a6BvwvdB'5<:Bg2L $ S?o 7s{OQOג;Y't'F;\gae4pKvhCQ9+@@nsi{l*=DTFc^w+0i CNJ~9 ՙ3ZL*CeF[Syd@vjFԲsfa>!a+)XmPMjkGn19 F>uPV_|Yd΢P@s)`#bj;m犠z\ݝ{,֢ryDU/ӪܠuF挔2@X"* IA#0BfV}T[{s pS#_WPBȤ:WS&1ysdsd.SS7i9Ek5]_)\ӲJ?LKKd cMƋg]󉕽;+t~c`1(eHrߓ==0)B!D?^[^2a3٥F `~lf4RLA8^7޻kcjFU{~K ?bmAn"ZH|i 8E"A R iǷ][¿?Q`DkRP*U W !+{MŽV&qqf%vm`2j%!DFꓸ\TIHcC{P$74Ri[;S]M(3·T>BWތG>Cri POej:м YƈBiRWCߥfaZ4S)̘)(sد7i\Y+8H*왃՛'O* jwf#:@/lYiLZH܈|r }3OQvM, 0RL"$@<ZA{RPJRKcyt/jKi*h&cφ{6`d1f 2_R.&FмqTknwcH`s@Q3Y#tÙMBE RbW`{>hQםcۮ~+.-uv2bf3~j=ki 7e(X僕\1r,˧pFIr}~7o>eZ% x@~ZWCC?hٰĨ?*@uH !"*ibm3\w^{|:giAʽpI! H/uR ;H_fB T}V@^Wo8ԿCCyA-hm kHܷ25S@FODҕ:+&":@0T*kvN-[Rɚ87eX5Kmo߿/%lO!i23*dDCoJ4`?oNg]d/y#VmNz9 E03Bȟ>]Zf D^[kۙLΘ_?4fD|[loxa۝5B H[mZ_?&C@Mp'qZ6ѻ{cDpjڟ,a#Z>˶z |tnO ʽlĺEAgirL<*L{G]>;Dw{4XF 75G2o[Q 2Lpx%B'nEb1y{IUa8 טpqf-czB[ٍ!AR&soE? -ho? w$(E{iǝtbw2O^9/`3# T]^ =k]wVLZB$ 8R/[ ϦC?{$tP$J4v+\SO:jJ}Mu&`1L\Q{]#pn|5fDޕj%% Aup3^>zM&1qU1k#ܴdO%CBƴ L\x;0]b\ fdĥ uo`DC{Mqm*o~S@3}v~x˽MПZqLSJEk|[:Fzvb?]1y?Y@[ϲ[6]P 1^6|i?݃&6?!LL[54,x%?Am:W<Y; e@b0գ2f>z|8Eka\dc[ M! '3+Ny2ؼo螕j&oRh溅_6tfh9!D>[ fFԾƕr) ٧!BqA;) 3|&aĄ@MG0uoXDCA(흍v~Кbn}t-vA1uǠ0GWFjmJ)Jw鯣ZZZB*U49k~6oa3ǘh>ÒR+@$_G#+ˍ.mڏQq7< DXMuZ4# P&431u44.?>3 [LDIfN>@2C9E$hQF @ݼ3>VWJ9ZIq ˲|۶ p8fhhhMO:+b0ɀ AP*aIX~צ}Oy4Nm 1Cۭ-=D˦($λh0$6->p_K?iMJvIENDB`ukui-screensaver/src/assets/weather/19.png0000664000175000017500000004332215172041035017464 0ustar fengfengPNG  IHDR>a IDATxwՕ>{㤞i4JHB!0Y `ll w55fNlvYmd06` @PHUuUVh$#~U޺=xo@>o; zPJRuֵNmY֩I& D"&L6sm,K-+rƍOڲe tL&3E)Bvo>30d]]ݖ &>cƌϟܹs믖 dɒEK,`ݺu3 @$A(eYR2MD֚lFP@.ԩS:co>PzZ@Ys~X PWWp8Bd&a $Ak uٶmE8uYklۖtlz}65һ vڙwyU[lhCCز,EDBAxhiizDQH)!3R mömdYR)A:F.RJ+d:::g>sBQ -H]w)esss BR 4҂8q"hYҕRZ A:"Nmu]6\袋>sYg=Rԍ7g}D"x>@@P_? ², xΜ97oB2d|P5<122\.q`6{Q"Jɋ/³>*m&$C]?w'NCa|QGԩSˌ̯@```CCC( e8N`qs9ؗ^z>5Zk QWc\/>k]bDQ)㏧f8S_j'ˡLfsEe 5k ff.,0 H766E"---ޚ>} Oh@@0?%&LXP(dq>)Hm `wH&AX,;>=&b vgԧB766njoolL:u0П~W@@Ps}? N450=Xjkkۍvj?`۶mm뎒z>B(@D0 !`ߌmPJm#ömD"g~xѢE|/y= X,6퓟x<d8C1w\ETv13sY l߾|X,¶28BH)aFF#AJY;|g,ii̚P(L&۶1cƌ':+O9唧G>;.B8hlkkc=|%A[ۋT*qP(ү.34M b6m&N!D(c%2 yxxl7 u]NE>nj3⋿y!=0K/] D訣;&?c`` ϣP(wX0 ĉ1}tB@DcF|Bd2N&Q^Z:o_|ſ=rl~Yf}$ p8,'MD3f(3S6.mKaB0MpgFGGLBRcfH)FQ__OHfD(r7xC'yPh=Cbϗ31yA/dgvIzdM6떢1@L7MX |0Eb>|b&, ͘>}:Q<G$10&L_/| /vuuMCI>k2cٺu~ccBD<z icuݲt|D0gq;J+%?X_O\|Qq 0aIPH$/gN{/_0mmmE"KJ !d}}=I)R3j ?۶0j4M̞=X̏ˌ#?CFϧ h*P__!31O]]]:::#@uuu$߷_\/;eS"tT2:l`ۂQixܫ^gdq bf)%KûIdB{uG"RabX*jVo`8)D9dH$~ve]oxD4 #ZN#m6 0ʌ6`6.jii)ğ$4AUZVSW;!Uh-VJs= / 0dѲw`hk4fh_#bXb Z̪/ ֢Ys=pF*pz'~/3fu)e^8F,h4JJIvu*x<###PJWWyЗvn ,RiNRJm߹skصv;?a-\'|/ڣ(EQaYfbvέ/}ծmemSYGpao_KsTsC׮]{#^;@[n:H$"ZZZx֬Yhoo466|FIZVY _ XtTosF ^[+boںjjjjZdV]c^|W^yZiMMMv(2hA%-?*~k0L)b{,z7Q S̤ȯ&@P]vك;wH"pܹs裏&eWFOumUӱ4Ia`ʞU2lOTXfRgak&"8aMKf͚3ϗnWc@eN@-?2=>"{S8VJ (?d6x̙3<sG fty%kNl&766ʓO>uuu0R% ßRUZ`'Wm\@55k?> < k\ތeQ,E:mlC5:Hˠ9% j4aD"Fd2eZxO(1`@@8n5M0 òQ?R tfURԒZG-p05RecsC0?Q :؁Y)%]u̙x9v{1h߰1!0(qRc1?dɞ`T&Z OPe"`y՜б|hZ<xv=:Nގ[Ij֭RodF9sk&)դZ/2^fjijerT\)S9~۫YyOe''"B3#O6M`ySRJ uf9ˍya!kƠ4V Jn!̱fcHPdJki'=jH$B>xA`xxxƍnnn_,mrxx,ycXmV+E̪<T\j}ವj>@v}0]$>%4&"5yW?!"a6 CCCH$zؕ'_MM͌*vjeײA Tc@]K#K\?W-CYgOE{M1J/p0FH80i;;;Xbq7?(Y0J^S+%SR?W*ò`'V(}&V>KZRxdrP)J)CJXPD2.SOmmmV&3mdfA5X~,J Xg`Q,R"cUsUK0 =+ jZ)X}ZN;ȸW2QI .0]]}}}?O;3BD3ص;(@im "Y|>?ϛ4iRq9eYPN vfy:1hW:_YwЙ 'XN]VNЯT4@ˎ8Ja^\. ! 0f(6PW^#ojjj3 EmOl߻]iռZߊvY jfPKګ1fٓď+߃PP`q022Bsul+I@ADP"m6lذrƍ]ZHɲ,y ۶[Q\(ysᛎdTcH%*'` /2{J(j fO#i򩧞z7߼% Ȱ62W^۷gY8RL*<Q(K)8T(?d~5s%T!n)<ʯWST3ic9l~\((jXvw T@@rjnC7H]TI4 P6XE4{JW\ٻrW È544$D"Fx̲455MI>󽶳8K1RJzoVER&%$F|~;4M"%L"BpXmf:(+.-HTmSjG!c1%|Ue%j7V۶MbC߃G-~y Rzom:3dPT@>r%?os˼@ "6"-4|?744$.\xf.;Q_Wj%ݬRBuZbkO0gjO2Q*ŋW=r^O("Z9!8*qƹ:7Dʕ~uf05G@BeF0g^b) aW(յ @h3\>HNZXXp\.|>|ߴ-9 ˣ:As|^+8/{嗗r9a)eHAB@4C.]{}hٲe8+ >QZu )iR촐hs{,P,Ak 1o}Icha wcɒ%n=餓immm* p]W;S%rj/"w(`f!$l&B!_ugU5mXdqXJ)~UVW\IJD":qIDS]]]}86 0Y۶d2T___wwwwWWWg.Ǯe`fȡH@ ؘ/Pwnx:Xo$ (`1ہz7L߼H1WE@sEOOO N8ÏM$ud m&L9PT/*Z= 666N8qb;R b BlY(FD>ʕ+~4226m;ۻw}@|Hzυyטz=1= 9zG ^vϜ~j(Ie3eLy)C$"Z+P@+!;w|a4$hrf%mW_}zk9s̛7oIDQXff"bQڎfWۆb7mڴjÆ +tg̚5방SH$L,Otu]_T,QAL&7-]եPs!R\zVz-b'#{ ҄ǘhZ;$ ][bf)*,ѲLgɖ n:͗u'u%3," ̊+[b+ӦM5y&LhbuPHNX`o>JR;{zz:7@y(z۶mmi &Lioor8) IDAT6.Xi2:f2l___gg71Di>EN@k84?$¥(pӴi/Kq3Eݚ_䫙ҏ-57}N%f9AwIҩc6rܱSR}{k3:WCGr8"ld " y}I;XQcVDJZ y16*͎d!bP9ENDYl y1hHOx_Ak./d~ ޑGI})TxټeV?"VDV4b4#c&!ra0M**$&p=!UEVfQbLM 4]2#`'Y>7ap<~BPi|l Dʆ #쏿}\t18dq13Deb|AѾ<"J uuN*XHV#Œ1;&Tz"f٨I,HeA}*[ގN!`0DH[JL?aO0"m @.9<m)B`u`0{9d"C]3 Y7hzO>^$A@溮*zeg?f{}kW7bz6g0S `A̮fÀppCj##>ɛ~ S\%_A2>x&nIpZۅ# dm 뙓ki挑^q< 5DTD)TfE]ixY8L`7MQFSQr7DzPp"nPԴy3ftvv/"@9S1*Ok"r2ھ$lfVY@g0DnD[p@Akƈ74w-f, S)Gphm7;UgшP9ɸi:V)b$4 x;Ĉ@Eely t ,ϝw[!Ĥ&V(&[G46 BӉ0 \k]f#;^(AhBfvDr%N~2@ 0IXRV*E6?АUZ0D `\$;+Mn 'a =05V@S1#) ro.mkE7>sj RfpR"+An!. %_CH确/(0̠4ݡ}7uWͿŮtli"X0ՄARҀD"1 `;ƾO\_\#E+WU?nilD"Lͥ=B CG_p§ԲE ;V?( ܮ;=:{¤4ML   e yk_^P,RLʏ\q]/]S?YNv+<s}kI_&ͮ#Bk6XF0'I,CujH}0?R]W:HFce%Nr\YaT g)ޥ){9/ wl?¯r쓎و#*daa&of"vuAuw5H imB Yʵ=#eZuQf;UC!dAh;ؚ0;@Tbk2dE 1'4t_o>IͶ=l4 f͝ aCBǗ)aKɥXGW33)ܢ) ~쉗N$XX[B&  !y:dRʑSbq뚣>r@m_j|;Yi5|'^[뒯.t1אMӈ0\8KF$'t~9C/~ J&_`J {s<,2@z f'H?^aFVJUJuѧsTU[trUa-K.%:"dbV)TAlZ[›D0F?!"Z]u;?Z+w?K ~O3g}b)F;O.&e1DgX$EC0qS $q <1yխno7sd;DVREW ZX+0qŗM>QcH@  v&~]QAK,VP߼OlV\~V29`&}iEŧx~Py9|_;ztm6ܑBaAldAdJaJO.ϡ=@1cvS4K P30Yd 6F@vG5 k"<-ivɔJDP! 7CPÌH +5ݠ}غG_/'"MK X+(3Rf7#!b%';(ս5{z>t?J1pSqe 'T޸M nvZCY7\NF)yGFhFCRŏ~x?/ݯhlqF<d vfA}u(]DbRx#ki[ 9-Ov:ҳ H6V&{~ AC{hЫ= 4L|L9ġLdĦʝ[&/=O==?ز #V._;HQBHYeװ g/Kìt7 mӏ;X"\t=1 mDpH'I]{hA4c=O%+ ?uN0;E|HCdfȘ\`se\}= ED9a0@EG̀1a$iU42~h+ I%R{rgӾ ӿ_扻^H4Z7hkNfvX[;6.{  1!yfD)/:w|oŎS{8Ste6Ζ~f1ZuJ)m?oCw9%4R&bY'a=lʿͤ J#I)3u2P"ÌIB S3Ǻ;HDN w+]W( au?J6Cmg>w7m[ɫZP#A\J(Bٽ}凿LHٶS% `yb&G]ФӤX-qO=Ջ ԗV3!.~O>&WכtJ:eӧϮ:RjZV -7ճ +oN-x/ЮRf(Hȟc ꩇ_^2RAfc.nbί# +;1IN.)gb :MkoQk.- qwy0(ziuF:;w )xn4'byW 6R _3{Yشe.hLmc8`ǘ-F%r3Q*f n I%ap1($ߤ +_G 0k@b@);o᠎F m3=̫hdAּj>fJ9ĩ@V;#piؘ$ z۬umϚQ 4w} ?pua=&D%M8 SJ N[:P)-_H#|2gP?RBp ϐ0MpۄDdFw[C';Ha6-G{zn9oVi(z; N^Ii!oc{JYR>ﹲ{Y$ur}=rLB?Ǐ,=9~̚@2mCiֺϐ֝t$ S)N(؛!f*uUYNi*8dr;HUsup6&z{]e?.!KN)vܫ;4PFh#^Xt(%Lw$sT^͢Vng}_)s\dsO̬έ0ЭwXr֌A" 0h<1StnJ3D 3I){ߧ _ݓ[ C[O8Cn fgU49΢# ӉNHٟTxI"BA ;=b▏+Fh6=Kx[DF }:(O ioxy^ q~`[Mu4fLg w5ʪ$IRN®̜V,Å2\+oW>aޣ^%!NKSC"DIm" ͏mٰX K ̷# +?QX||h8֋uۆ~~JoRi]'׋/P_S-iCv0tc2W$)e7pO 5hN bDr;Tj,*%醏_rRyS T~k)w2lKΡmfơYfɦ3fsq_]pkR-5&fӝO.R`U?DD;R~g@}[>MxWJp鍤cc[FNĀ҂BPg]iC.B1D||oz@-'ƠrK|`kV}} |u=)(ӆIDAT_[mXu7q~O_{WVs8!%hEYZA$(hu@hX* H3*PI2bB4ISHq؉~̏n!4$Ky~~sY"=-#~&@zV>W(UJE9Qjm]oL;SY)UTx"ٯ8[kyifhRLV<03Q"l(o5J2%$}Ϯ['o#= Z{ p} qUg8eCyra(o)dgYm~΋UZnj9ݹQṷҵIn9k; QZoj}GŞ=sRgA@)9Q0'P)&⮭z4ҰnqZy!&ڙ6sۆdm~ 8\*t-k3"`%g2ki*:ѽ̕K/ owZXB)< c')3k->52e "_׊Tm>(D۷st*ÂR**0s4sYPs*QTu!@*IeD~(L薇pW*cp |tC(  A vuRecQsqnecmNݿnur{pCo؂kyHٶ0p_7v',?8Dg=\ LHLHҶ% Dj^.lzq}p.tg; zbdolhI;z "ڼ3tgV_$пFE"?tޜY&cly{m *}3Du[yn(nu[8 "˗ǬDq&?)@*4j w'}'#/ 8sY@)k} i J@Ehlr8:sϝ!!;+G `Z j hHE@{NzwG*̫߷4G{fGr>ȖTN'ʁzЎ}܃APJ>3LI"# T)ܫ;;c S-/oOw*Dtþ?v%WgYVHlߎ=43Ӗ  SҰPYyхô$p9uHo$ڴG t<"j+W,gSN$9 hczi+ǫ,%"Tz*;3c@ Ml7)' R8J})1?jcMMsOO<?9ۥd Sٳkh씱1jؚ=c׮B !Bzm`y-tmZ$@㩲m[k-c&nP9Pc ӻ$"cƧj$1pG*`{pG p8F&'E0#IENDB`ukui-screensaver/src/assets/weather/43.png0000664000175000017500000004431715172041035017466 0ustar fengfengPNG  IHDR>a IDATx}w|Uoz3j0HPbņ{/k]We-,VD-(ui H0JBBBzS; Mu綹gΜ3~D)aN7;#Nyt[8t:S[ZZbx ,ˮfVTQ?U^?tkߏTSS3vE1byPJ;Nf1I@)uLmvv&MK@ ~80~$I6 F1BL) g,˜$Izpݐ$ɑȑ#ߝ4i0?M$TV\yKaa},@dd$fqL!Q`4< !J>E @EH@ 9Nf};qĿ\x*}?_ iǎ׮]wAF'$$fFQVN, v;flǩY!IA@ an^@(H].mllDFFƿG3228_[tkbb"M&1h4"22$..uyd9$WQvt: (/ &a"W^yִ4l63F4PPIU$u|/<YKK Ax<:W\1okgSOx<פFbIFFfs~2_ W׋vx<x^|>I0A/w^x@^X?62N-3/;999`4fv6p@T|3zۍ`0A:^ DEIev뭷1bġ^ Eh+q&V\yѯ_?l6&DEEC(ȲxRC+$A DQ<hIDc,5d%I:FcUtttmlllyJJJA* B?7nݺ/rOff&gZlQQQ,;;vȲ JqR8 ,C8C$C4_$@7-`DQ4h6+3336;;TM ?7(rWl9r8&vjlIq\Z/W*"vjAD'dLeEcf( zz`bbbv61cƬlT 䬬7xVUf3 Dmk=tH,mnnF{{;Aǁyp\h  UmOE {<\.PJ>8St7a~N  4jm6~wk}ֆVA~|>AHt CGf!!!QQQ- SSpLs YEy Y|w@ ;sh :L&0J&@/*~?~!@C~00L HHH@||<8H(ԕQj~` AҩS>:k֬5qUSN_fbbbnb$66KTr @ p!y& fHOOGlllu8RpqVfPJ9J)X,!$1??o1믿ވP_?CKLL>h4Ll6#t멫ď:y^}C CAtt4RRR`4;t*T`0f(8v䴴4VZZ?>ڄ_I9:nS  >** F#W#ZF)=NrՙXtN}}Tj Y &t2J)u,)))_:_~j v{_ cLKժ֪p`ЇW1ԊPB^Z)tܜV#hpRھ5,˰X,ڤ$ɐb*GFFrvXVL&-Oh=t կ %H&G &-xmtuhӿ*,D#d9|ԨQ7L.:DcƌOM{О@EL&90˻n6dȐ`YYٓ+Vϟ'x_#Էo)VL&fXZSN G@/)j *B']w.b<I:dF f0H{{a<'T̛7oN1 0ǟvQ, Q+OA0uU= vrK{uj%VYV.22Rk݂ް' @1;&5i5N*z~t:pA=d2BTFߓrJ=1&O>`%O؎B 1#!($k]s2ҦT=õÅċ^ sj}NcMsw]m6PeY8qb%o{< W(tSWޞpjվ '4*U])uwNNL&bXdfcSLYW@Oc&A]`WwIpٕ׆~Cf=@ߧp3/z]z+@r,1cƌ$dkxx8J1p8M cl+D)cL3Pm#\1J^5]5Iz {@C; q>|J/ ! VȲ̔|=НJg IV\~nmUp` LsWLFO%5zǜQJa4СC톯QwQ\EEA;jIX  6SD$Gx4AӶ0%;]A&K^wfO{ ꜫ YeW`4;v`CC?66֤1  |ҩ'zkO+j>\@F$j[:Wje:T^k{sTpvcA)%`XmX:0 Y0^>/eÚeҩ^vg'B{2j5f@H;vv#9c fʼp=Zqkm8hzmwiIztJ5ݕϢ!~_"w'Ȍ1Yi$b?X1}L- NP:/l^SZ~-}rDkP~`kpL]NC(vU z`ttv{kэ2"2o/77wUNNguxg$K&".#Y?p]{gm2[ _Rڥp8g`'M&uM&2c1Q@8(IE7S)&Ƙ @D{{{+`F!Dxd=|SzB[n@^(v{\vpԎ?"R0|ʭq,MHOx7ʲlDFBѣf9 @@eX 3_ WZ .7o7۝M|WpY슡z39u;nE0$~'joȯ J (``(..>h43铠VJD$=,p3]y]@k5Au'K03ZMqzWv8sw Anc,mAJit·89A"m0 u  l~8gÓ+E2\ogݩpkƺːQ߮ZZ#GVVV~f>Ƙ@k&[D)cL! +///...7L QtLVwT7\Z{nvKwփ@w7L}Uk^oF AUꪽ_ Gn,2),,tXuIm;m AU4'Ng޽{ Z$IZ,hH !DEL)of>|>$pe?w©gck!mب~kx8͢7s]ipZj~?S~?dɒǎ۪4!c^\QB2C* ! {",&Y)*IeʲL%I>(o!$1(^ 6U gv%|p ,q%BիW/F(RJ8_A)hIVxb` 8v%K<|G+cr>` F !q!Cr$@ժmv>;R9Pchmsu]8Rg^ Du/T +, #v#$q7n_d ]N"%IqKړ'PDț=͜9>`;v2====555ѣ U2TիWq{FFFt{{㔁ej>Ai(5JAzY}vq0L$ԽQG8h S 2*"Q52YS*zN;1PMcVaڵ_hS׋B Ϻn'|/~I&M={ت4I2MTY=$堂 >>>n[M&g0(Z[[]UUUe> ! @ VR,Of]&<۽1CCCC]bbb}h *jԲ !Ue$)iӦ>ҤIS<>JV&``ϙے)EimYQ)c~9rbX !p,Qz>RJ!Lu(^?eqUUUbnnov# !$ Vw8U)FCh(AAab!a !fƘ0߫!-eYն=OFXxv믿w~9nHNN ,x~͚5(љ/LE9lcLgff=zC7@P]e4)jWRRRZPPcUUU)q~ :thI^ IDATN~%$$XVvkf0qLs=BP__/mݺuw}EqduRLi:PŁf,K4~N$ V-̙3gDDDeff&RJerqq궽{n^x !QeԬ|+r !f$ 222 ?###-...bPI[>rq(1#d/f۷oIII1Faz ZZZ ;s-)))F^SBQPr#РzrCA"!(7iLJ;΍͢(ju: uU3B,BO4QC%54scLz-^'>5J*++k}n7o~ʕ+RJ9A`mcNwzG]'Kvbc,Gz7hX|W %cuJ" 8Asνw~ʦ={ _RiK`-cܓJIe@IC{ZTC)Ÿ[o]WQQr\qqqk׮?bĈ{)tڡ.5 tn۸qVUUUԔ*!BsʷǸ_O'0(^/_\XXx!8Ȳڤ:Y 6~ܿ*$99־}}(zp6U}6Sj8H RJ1fXDm.f"...r_}UC=tl67Oddu##KKK Ahj:N`)S?/oqGJJYk׮ 9~7!DI|,ph")jc,hUl6fϞ}ksIOOO{뮻.v駻^|ſsw}^5贕.&'cNjbfBRچ7ݢ9ZÉNg˥E) 8p ϪnbX ݻw=Ss9}c56Ji+B)u?u-[z_|y]vƍ7jԨ>HBWzϧaT/81f8N$IdDQTU$~:vI_a˖-[`wݻ{ĈgΜ97###~ԨQi<7Kd7h͂փ#iӦM%cQ(Fehw6<쳷~fdd;\rɄ˗{(383ɲ;wǎܱcǧ(ݻw`tt4_WWW1~|N6 P"d{u@AzM_eiiiknڴ `1Qinx<." h~'׬Yg}G}x֭;ce˖-1cƬ^{mu0Ks?~˗?/}=L0555 EJLL]6[nReىFRC/^zo_m6l2>>>&22$|uuu.rrr4xওZ:gͨj%Bg׿z駗 4hrzzzoc+u]N6ɪU6\.וW^y!B[;!NNKK1cd۷nO׿8--L)u0c aPٌ1AA{_999I>l,--6lx%"jx@L}чR ~Nw477f3`ଳκ1㢺?oh{R~B\v_{433s;Ɣ[ZZť|voX&oQQёdɒNc6OJ!_?1cƌNJJ)S&]|/[~ I ?+>ꫯ~^wu#Tp P&&2ݵkׁ~> &Å~O@~rm]yWHIIv\RFF/((h.%B4ɳN #t^z: QE&/ FnѴqO?o͛ gp8Z[ Fj@@mm?/‘ &XhկX,?[l曟+.kjjx≫{ッh{{{ܹs-_|{tȢH|F #Rڤ j(***nztwBLV 9⍈,YuΝ@MM͏,,zξ}egg..//g{۲eKmAAA?mQSSӿ̄L !{6lȞ}?~K~a8 pD`„ geeMrEo^+rt_y~Φ&;|} `J88n8qL&SBݵapd9s|R]]c}]ull\tEGtN`nٲ% %$$ĘL&ӨQݲeKDCCCWa1yB<_͛w9sMLL8sF?ӯA!DE.0eʔ˾O:fY|ɫgΜyhD0fΜٿzEEҥK?]pt3AmRq\5I%Iro}%hhjj`0Y}* !o^ZTTTk׮9Nڰ9''opmm-۵kW[׾q.^|7R `!@(ŔY8.TL~zyUUUaK,ٶsZzlʔ)/{ws} }^sy)+ OhӷQJ)Y&Ϝ9rȑ#zhK|Yg9oҩ8!<@cbbAV|bn޶m[s=?jÆ Żw.^zcTX~|ˌHROyzb,F[Eaݺuk,YСCo9$___R*Եw؎ q^zǏO>NΎ$ ѣgqEftQf)IR,&?9#PJ' &^dɲQF]|Wεcƌw 7{7~G>rGG};/~Ŋ/IԆ^l[:tд}qqqM)sv,BHoP܏̟ە4M<8RSSN6mb1l6kLLn"""L .\VUUkfGYE9ritfO=)Ҿ!e e bF#oISny>@,Q_?_?c„ JCUT)Ng#gbb7xog}vVff)gWii&L),,YvBZ6\0<ˇ eggOm8(F!ʲEHv<˩Щ@&DQleYV^ŕW^9a{`0lٲU˖-kP9 GvIvZ3<}}=sss6k֬ێ=ZPYYp:{Z[[]N-tE@v>}Smc˲Z[nrzz|.JiZ{{;Vk :sŝ@Y_|l p-*.y2e~JL&g!TtC){#G B B. 8n`q\O_‹!F&PJB&, B(B8B衿Pֿ91v? @G}̙Ȏ;_ڻwoc566z>KcǎJ>LI" $Cq7!ώp8p\8ǥ" Χ HQJ t,Z2ōO{矂 n[d1IJs{hmm M6-"FDDw=&&&:++k( $.^>}T\wuYUUU(?U@MxmFK]A-5Դ1L2fȐ!q~ƪ*ĉ'v֭nyܹXYYY(I#F~y#<$:( F}[PPиe˖cǎy >D%_z?U9فQ]S ^cǎCo4??~>Cv@iii(Gydt25D`ʕo^y|%{q~V[[,))VWW>O޾}{lcǎ7n|[l)*:ƜN\WW'׋{m:|nӦM_S`xС;vXVXXxÇg@ 775+eee]Pv\TTǎ1wo}MQQQS}}c7nx7v644x tL0'IfEN>fk>7tӫ@Ff0dD>t c̜kۛ2@m۶?FFFҶ644jސ q\B~mݶp͏ =z駟rW޽6))644#G̼{ֺ)`qULLtݺu?CJJJŮ]|%kkkkjjģG<|~h_xsG۷|޽5;{!+Wiޏ1%%e䫯Z~17|Sv~d,wߝ ҥKZ㕲xAΌIݻwo>|yFxaÆEBn',Be---c>Q]]]7qytvܹsĉsZZZn:uꟳl۶m_|Oɞ Es]|׮]%>+vj~g\pY`?5*ܵp/?-={\.mFəp4Zj-r  fTTv`0e`b4ɸq&''G 9TvVl3d2Erg2dHƅ^8z7mEuuu;WZ C!$`rkkk7o^{?e?q˯꪿ZjYg8dȐxQŢ?JL@ȑ#&$$ `)S_={ׯoZ~}ömfV~}͚55!TJE*'T f)h40dĈZjM7}mpW^;S"Bȹ4D|*LכLL 8B%zOS!_fZ/p6B&S"T2 BH?f"8nZdc7 &A*S}[|_~}RRRZƘL)mP;ltE6"$I&} RQC F g577X/߿H8.UEuaR**5~3J/"%Iwƫ|DL8Ji,c,A8Ll63߯yu:Nt'xKB:R  Bii^OV(IVu={RnwWΘ1cݞ@`8.QXJizb ke!;aHV*ɬ<v#<€(9IGhV>}OJ(!?cI$7n̙3RJތcZΝ;w^K/KqF*Iq%Apszlo8(:(!لALJi q&)V9I (Ătʔ)?///wnVWWJJJ| ;w,ݿ}J{=iMBk&Bg1v~L5y͵1ӳV}O$!`0SK/ɓ'|&B`2]s5M:uUW]uab;Xz щT$S2a@^^>}l[---8ObbbUuEKIIIy?>|xNkk-;;,"""СCb~o|_5ӵK@Oz@0h4^xݻw1c\{֯_Ə6B޽%-^xi N6mG}$+++.??%&&l۩j54hԬY.Q;}WktD FUWWhjjj%$O>ɤ}lYcȨQf}C.ə~_:thdcc?##þf͚¤$5k*dBɚߌDv>|ԡCfGFFf{fϞ}رc3j;vX'|O6lؐ[^^ݽ{wb\n]y睗a4… ˲]r%⋏&_ XdʴʖHHHJIIݾ}e˖-g d2 7o >y VXzȑ)޶zkl&й _& 8Y2^|ڤ<5KOII1=zGd'zezqYc/9=|YG}>066]YYt@(_Wk'#Ç_4{p;!CA$`~t>uLe٥I8uEQ"7F0C % 0@ga`UwJ q 9䛸]< F,4NIENDB`ukui-screensaver/src/assets/weather/47.png0000664000175000017500000004745115172041035017474 0ustar fengfengPNG  IHDR>a IDATxyG}/m9s-G6[ly`yqya AnK/7y cf1x7dْ,hߎ~fzLԴh1vHr_}>szz}KU`0ȗ_;T/c/kcGEC,:ijjjyZ_,L,H)m9W-!T2<<{?*Yx_G :sΓlrѾ}.;T*p]w^x-80MJR s?HzvٲeOvi-[6v> pd?kx7ܹbxI۶8,˂i` GL1}!8SUy_}Ϛ5kj3{d Cݻxk7nީRcBi>5|yfYVD"05"$k8tw~{:Zfffl6l7yU*f+|&r3IH?ܹ|ms߯-Z\.t::6s0 atx $?D:y**Jh4}#Fh{_'?_8y=\rw9?5 v|^-^}}}AMWV"<ADO }JEMLL^3BJ2rO|^I4fu 7ٓO>|>o۶H)Mqxb`fYuջ=] Èbfљ"PT011J}_뚞M]{zx5`#fmOtU+V@6e$qݰy ӧmU?Mx@\ׅyjjrrzI)Yq|3?edؗ"0DdPڑ'>Ӊ _JK,Qr K$,ubϑBg]IrI)P"ŵIDCJ 48L<3R"H_u5x10!-C<80555T.1822g$Qw ~\:A1a,+ @V574M!"Ljl6{X,BR)l|7p;WtMu]ڵbl&A:m0MSr%cLNRJ)yp]רVJ^600t:I(u k(Ql9)]OB^뺑Q cT*W*Ư߁ ^I gvۇ{a( fm[pUhx"9猈<SU*A~ 1VAc~n6J&{j:GNG05˲H$"G#HDѽ9\ ((DTZ8Ԕui}K!R ~wqyD& LdiL&BB4,}ri&j22FtT*m,[ mGY'K.IoSI]9BD lFg'3H?!j#(uYRqs}sK `wz H$ ۶Y>ǂ 000$>w ӁMLXjZ* \Q(jC:RJP8#[t⒔Ai*XqIݲ,RqD"wUTb TX <~_\/hs;x;R===A"2 .]SN91lg}"<σQnav ~NԮA"ӘtMyFjF$EKy8$|Nfbq!L$;^5駟vO`/WO~};T&Q㘅BA^-Z#, u#MZ (Vj+M4ȴs`6g {"7d*Jt6~O:OϦ30OoTJ1՞@C u ܅(D}L ?\T9zɤrPWft:znٴn_affJCB/ҥ&HtfTH$`fD(]͛dFji2DP]uaY,&He!LOJafi g \? Om[$ _~qMڍqFPJl"LD"8@ItJH727RJ4 Jki:g @ tɏ3@ ȴ}OUYuIPC87_"UVk^TSO2:q7 LNNF>@D@',Q,199 ˲a&jbtCqCI yA5Fٌ3Sa ǿ8Sr002d&1RV\ɒd4X]jtB7}}D*\dYtYlF..Jbx@^XtEt BfS7=csX}gyر[bq `W^y_ d28c.\P ^ !P. >I48N$dȽw;* rt!3AN'65]ѽ9HRQ+.sדOjߞ^xEߕD:<38J<ϫnt յ.rիv{{{188tDꙀY2DPZP0\׍9 ݻa= LRJM0!RA ^#8 ]@@uYyի^u}*L/Zt<4=H_" @q~".\G*Ƞ'wg.·@gB2,)< ۶t`0Ix5ݯjE#/I)RJ*?Q8rkɤJ$HRu t~}rɍ j H ;"gnL #HRL^T:< !Jd}4bnP2w!y4,ːR{zz.,J`-R5rI_~RCoo/DvQω>Lv?.:#}\q3"\..:"q\@cyCM7LR3 C!t:mٳJɎ/U3{zzzH0X2d}7MdH$>C'sy%t:A)Iq~'-#Г=HiG܇b qIYex8R im+1fR'Zn@>DӉPig<ZqE(ԯTUr9RT}7 t:zA'Z3*r9$APDϥI>s`Fիݼy.fग़$cժUXj9et:c'5R %0 8hM62ntWT?BO5V"=i M$ۮ{/zƑ&"Sq:2ܤ LD"QKNs ⓋKi9!)sFRTu#T3I7w4/.'m;y(X,qaR)Ad2UJJQJOмh(p ǩR\|>T*hlFiBU;}}}L&3 K$Qp|tVA`0fz :SuN乤>yqFczK&KH}CZ#Q.(E=J]tQH))* ðg'887__z[BY% F8qFXR:ㄣ!/~?:h DO}aR*#K#T$4=LtA"sE!1pj…oݺ%W7%{O; ,… U__f\'tki2hㆸK»?gxXgEiF !גs!q9L A +! [ڶmOp?VXa˗/WK.UlX8}&'_Wu:a*xx%]E <T*JLQ̟>dÉյc2Z Ey^hzn,T*Zw(4-|]wuǼyx*3:,tR8Ѹ*%ﶡK~..sy%G;gd1t.r]DL&\.ͧaWQ"W2}t9 L47LMM=]G7q~[-Z$Sϟo}٪&esEL@ťQ}\Dqw[G"=GEM%T8SI-2914h-i(4Q8pifR*ϯFۃi H$\rd2)s=7">=HljN&դWt^;^*v^lqF3G69RT#P"?biD zK7Iu  &SI @ׄR*l6w;$vϞJ)B`q*1Gغ-nD/sį7ůqϑ-&\#3bb\^A t9MQ^N3Fi-bNBD 1ٌS暿RL\b A'q.׍HP\D?Q>=LD! @^GRARA__ Bm=i*N=} V+JS@R1<۶ f6ƿf ۶x6hU~h5TG󱈥3 } P#bڸNt5M3B"zr*B. OZN:館?Ҩ,ˊ2z1>m(2LĈHRo0 8Nʲ\4bn իW_oYJ… U2dP %J6hD'ct:sǒۍsQg|<9T'H$N1>>={`ɒ%0duYs4GPV$a()%d"JU*p~ bŊm[dY$4d2 'Εp&qƊ98\7lO'Bom$u]RV;Q۷CCC7hiyU[hlYP)d&43R4dɒSrGy#JuH@7L*).R}m=I"ѾKi s_@bQ~E-[od8EFLӤ֨jLE:j*dGa `3¨fʲ,|IcǒĹ&N?NZ7Msѳ[=OâEpL&2R(bHuH˶mx 3MsgeYq*/:_OuMF81&l(=pu#h4v>u[bL) bJa=Mr*BVCpdJ(7 fht&qL& ӓ֙kŽ6q:1uK<\xH}&tE#DϮkBqd"O[[քA#E0 z`22eR츊ヸڞ l:SG(6=}⸧{)2Hs$)@ehү% Sa00^W3":5#únꜸW/mO\MW׺ L-1ԍ/Uuǧ7d< TUA%t1~0b `Vl6,Q5b;fiFߍúIH7?x@H*?uuHeJX\qn<ژ1gThhRF|>Nxz7EUh`!WT~*L$0&'D%"n%M0t_um.ωt@BV/q@JI 2BY=4JF)F{xLs-!766v@)2 ds[6SR>NҸי[۱s׉|<"rZg,u;jV!}O=TG=lp8p`R*ў> 93\%ѴS7mh.;8q&!cm?==Qx=s O2.$@m猽k׮* qh}5On;WD~}befqc ⦡sƥZ{ wƉMA>b ͙y,sTJpfffnݺuŋW+ͦAnKZ :g?g=@RJqXJ)%?3Ŋ+V\tɒ%r\Ήn=TYʶmF8uO!h">-Ơ6t}ncz F6t Xh^c֭uR(7s(fJhi[no4c-yKD1^K_>O^z͛~ RBH0ƒxT۰aá 6< H$l6L&St:iO&뮻 \swtBd魛Aj]➌P: {/ey/"~ƍQ,{赃::y:t(Z)D kCVi6*&Ps! cSJ5.b:ο]Sc'oi(%$RRlc]n?CXhЩ:\. Ex.:tn&I>$Q/1fSB\f Y%;ΰ:x0==N:IS<ޏ 0Ƃ s>666q# թHbvitQQaZRrUd2tD%“VUI)뺂1f ݸqSc@z0UcN͊R[3fBmN $ R}=Nroa~pxx/Vo&ժj4*̲,E4$NG|!f3խV 0 B D,x}bÆ 8x` ڱceFK)J) @*o8П^po^3 W}1n)]v߽{ի8/8r z'"_v;ku08|0,XI:vԪǎ;wzR2y^ ?i.}lji.=Kt l6YBmկ=pfƘTJ4:#Կ xcϝ*v&6rK~~ 4&f[1 2̔2 .xG/8?㋥Z =&P0ƒJ`֭nݺuK__I+W\bŊzzzh%+ef:^633 J%4(1̠T*!J!ϣP( #Fuz%N^ FFFm!΂1bYaY'O oA|_Z-j$ػw؃>xO{T@0OMGzü5MWEMc%F䟽O-_Պen^oai*v3Z3._rBJ$Y(@6#I |߫LL&&Kc˖+$8{zz#/"ڹfA4жyDHE.GNJ1ԧfL@ } 1A|u_|K&ͦ2MӸ;~m@)UP5 *h(@=1>@4D?`CW$('iZRV%dBA[]H"ۛ| 9",LgYJnIx~SP} A:+gB.ku#A<4ҩ86Lӈ@]{< -'C)g `Iض1n{Tmz&`|W_[o?1n?쏺3#% ~xO@=Drnq> PJ7=@V;_?מcfs .Kι,+眑]KDZ{x]ϝG[G>UEߒ2483Paeoc!"8NB Rݞ'l>7{e$0d”-+UBQ%YSIU  @V,{Npռg <%$\MTz̀)$m6;q\ac 4H[W7T*<+N P n l qi#ka>Tq%[^਻[؏jB*eGI{PA I0(jA}{Q~@ @1VB49>jk8ةg[୸*y3?s`G|T 0XjhH!ł5y5d˗IӲr  57om\:MT$9mlq|^2 p{1 z-Rzh2a; z/CWo@qv/ JFdɡd]f{NOA* ٱl&(ͩ~S (RY_Vu4FDe̗]OD™ Pa0UB4 èVw=smgL Ѿ Y^ ,Ɉ@ (4o_MNk,Qoj~Ϯk1YU2A-Ho`d7l.'xpKӻ]՞|?RJ1ɸ >un97Q,D_a$Dшb ZJI09ö iN00 08J*(T10M|zTl6̲,c˖-{o喻10C?6+A1GX_łj W  azGG3Wk{F^Pqse\&*0SR}k/\eZl۶mҜ70\j\fmГٳ/Ēgo0 S@6mw (20 ՚6R4N;m@J)Ňj@˗ QEUEa[au]+ww>t뭷]J1FH|˲~''=+vm􋉱u_۞?ݐk/umd*+ R &eЂvLTRJ fUVs[(l-溁,,Yl/C,]XLݘˠb m b~̟I'b޼q#J X)Fǚ0[XftTTJ-hu <9A~z7͛7vI]ft [|MKHvfEi:ʕCcG'.5l{}&糟PettGk;AMB\.9 oSW]/OG>SGGGo:餕g^ŋr Tuu*vFJB5c-Nx2"v~c٬tڄPՐL&;JפX0c| !$c޼,* _ RJMA`ӦM| `sKJY~df >iL`ŕ@$0 !M%^6pR~>0xVcݯ,cW)o4x=v"mh)%@m۶߶mzw%LΗsn%nm33%L90 (ߜKXt&8ؐho iAwdm˖-[׭[܁m찊j-e!^y% wxډ:V_{q rmwco‘_]}w+| ݙ@~Rnx6R kT@1#F>Zzi_&O f`<\ 87$-oRKc{Y>=e}uWm.0f\YUӌ 333ݴiӋûz1|~+!h_ܼN s<;X fǵs#{;bs:呍#΂_]n}k1(0<dƠО,R",`}v_Ύf郧n;u0vN{2}Tݻ':tpϞ={whТ+ RbʯG9,A»'^ m Wղㆋޠq="Fz'|=&U瞎Y8U ϓRR FfS ^u.aT\e~ᇶo`gB'NsʙoҥΖjDG8,}?-s.p[AШU*Vm333ӵZ6HWV2-W'*cI#s01ۏW.*Ov}SzhWz@9k}e,Öm[te4wRM.CD6(]-RѐR6B"=jhmvr)VgJ)Zʡy+>ܫlȆRJq?X) dRk;-{Ͳ{望J)":]Dg1VׅM m#W-sn/~I:׊%̜c?_NaO}d]jBVldN?}ob}=ɶ<ۏY> !J"/) 8 ^h[5t\n,]p;FAa\q|ܦyM\|Xx_72}Nۋ0B0?8iN(eQR1\_l'YnWLg*01>z _n ~K]\)Q< [ETϩ~Sn]Ɖ{&E/hAjc !JJvB%KI;JV"S^TAޅ7l&vowIDAT[}=d%םMXO5ŘЖv%p$-X0!u{K-ډ2?ռ[~_Rj3OAS3WJJ(i'T̫OU ^k;Y3B) i%%Mۧ6V˪' `oBrq^#180.MBB|4 /B}p\G9wd}_ںK5=)ئŽ7<ԳGjw(|鋟w߽3\}VT? LGN?ʏye>]'N=W~RI·U1WJBZP{~HōdNLE ;d;7s]WY'ݹAKf)1Vv f 13yhzqޓ1+rgT_3i ( 濿v&@cWbAagפ݀ٲ2L$yZS۷xD+_HQ yil~)˗nOG ‹L;d9{e)ҏ=$齠-&Dy+im)K; h@sdߓ129}?"1`o[,LR*xjR c^+زGCCCwy⋁_t/.Z F(١ws'I5 euڎ| <ށ6`ӷ/6O 5QY?3b5 ꖭ_9r%'gF}? )PK\][ |vf{1SR| ~w  P'ЙJ7r}.l ITuuȔPSܧ~3FRpj*p g΢EC󀭛| :Tb`480X9$ ~xpU@Z b4sѓi0.Fn?rۙM+f9Cg-+WŽhuMќɱI"i=@,ͥ)Oghp3rZ) PSڗ_^eczY5K(E>3Sm !FϫsF&T ί<;f=@3&3&r"3i`qB 2uWC$EǜEI@K'scQ9yv\C՟{Z2 3*cac;/U3x`h*\Ÿm5"u9R^՜#|r ^i$X6TC2qNzTֻS]6ρ&4~^ctOJlU-uksUӴK-%#+W.n|Dmim=jăv0G=K9|#L:UCfǓn_\ +hRB&0Y&zD3fe(i‘`$t׺wI3iFң2ū%>6<.4Fgn깹\İ)D&:8j7Í"V{+u2ۍ00tH )YSoM=^Uo{uٽPT:bR񥍎otrJxʚq-㮔+@5,?nI洩wIP4{=U]SؑcU)1z>2 -*p@I( ;!R ~kKR'tqW8Pwpm +d0<;Hǁi6@2@A~_³g/mX4ɓQrT0ZceF|+g}}%Fa31#wAY¤IENDB`ukui-screensaver/src/assets/weather/24.png0000664000175000017500000005133315172041035017461 0ustar fengfengPNG  IHDR>a IDATximWU&cf}{$4 H }y (P~'JFR>1)5`" $ M01 hIm=n9ǜksnH}ws^k5;17@jx㍇EMR@u~6{> mν|vo%wt~ww1}}~70կ~uMo|߷wEY,A7ܢN@$4{?7޲7^o:V~8O4{g11kl;pn[x˭y0/{>{ֽ>G ;oLǤ"\0,OSVc VsCn}Egj{"*SV8d6=ǡMB{U.{MQ7ܪ쎥͊|xcf#|˟7ťvWvˢ{7M$N@JN#^I͟m~Vl)_Lt-V>;C[>;3gw4㕏AL U btyd1fyj9ZPQBg϶y@!m3(>C5OP5% JDetId 3IYYu]{]93)6,I>=1@K2Y$ݣ$t"蒂YBh=Ʀi@+!Hj=Ʃ{l%DQ-FI@QPKURctoBZ%AH sRqI=S;HruH UUфy<}@*K1j4Si$,t,zLiCf!6SxtdWK.QWO,p$ɂ*K.WetWYP+߶6md:Ljc^e[61mt2V®]{c3N HӥŅmx<ժmGd A \R'H{^UM6[I&%Cr; Kcxo;PH^<( m۠* [q]YU5hFuS{#ƣ9w6 r oNn[Z5;hiড়8LH&Qw%WYUﯫVannvN3$_])>~Yp4~rqNMr eQ7ʵQ;?׫y%/Mua^/{{u2;bL&?/|yp3 ny'lwyG.]k{ӟnqG>}#=O'zgo>Q޻o~:=M3Erw½_:ǟk{ӟrgqmLSHS?_R`i@܉q;8|QG{>gŽB]W1BZ|~#n$ *x~8ٳW]u;?.㝿꫿ؿ3=5gkB] յ w޻pжm/~?|_=.=ψlee/}KW\asŲ,(T\#m`-^I f@ +Ҳ ,%rdm! w}7v܉(ffڶšC9Gyywя|fFfs=oң>$k<WcI'x̺㗯&??\L;o[nQN|0 'tg>[?;vZx3ض$eM\z饼B1o%v),IK?χ 瑃2,O{hc|l밶x;wj2b瞽zU9pB/ɗ.>kmk>/|IdfFRE(@U]uqyy0x{mn!.//@kÎ:b -^ }^7}|S_:N;4㘟ys?SN'wDoy[pߟo6,blWݓ*!X l!(]hvvf(p̑; 9$%3ٻp _oe h~Geٛ~f8g^p'ݻTUU1AO3Ai$eiyz`]wN?t7iUU68>G`~!IK˜olۖHXtZŦmyns" O~mgA*@26I,TѴO!F GﯽD \KHw~p6AxK^/~\ZZ]?^>vN[\r _ann.AKA#{m_xoӜG^eۋ}GK*|Qn"Lf4__SO\s5x_wu 9g~cN8[kAe8X__PiږF6GiӚ4ɴfCzйn.}0 d_dNhc$<O}"<`fߎmxq갅[+iuWr~~fo:'Cw_:Pd30'`uܿY4 xWN@4?3)#M7݄kO|Gp1G|c2iJS@l76v`, @>' rYgstG]UN'ػw/-/cqq{,wWSN~r&۷㪫§>)/O__'R_)|ަ;eYv*4Pff p!CA۶\__g#wyOYx[ނ`%^wh)eB1FJ _}xހOMSt҉ꪫg?9/Oٻ4m,F'j*w@ cC]Ӣ(, 7УGuW۹lN6dYe_|e/;pڋN{a8!i0p6/1vJ>~V{ ~o+?}2p2o;>v< =lr=$M){wm!+Wmִ1,+_IWw$d'syy4-OXM/Y?g~Ǟ6ܗ2r")3z!8c_uTfGsu`M61@hd'<;tq}}mя~ԛ;L_syYs^Ž{UɖsH{wq_蛜S9m1>UgͶiC |$ <unbX[ۍضm vt:Eߡm#7Lu]! \ՍvKre<Oi_w~7Ӟ3{o(6o }ݶo?Iio_z@Q r~,sN<#v}拯}γO?m~~}Օ~ڋN?,gκ23Ϳ@t̰O, X!1Gp(RUUinnN ZXXGj@]U1DUUH GaGg>}/|M&73z=^ƓN}کG3[Q^iL y\/-.9h;w=E>r9;hSw2>+ëg M4n&ʯ%ّGPŽ{ᄏ;+@fxO>^U8/ovVWWARIc=]Wu[ft5cp8ԧ>)}CbQ_\;clov%*‡q}}/s~ǟ}{%ig ;yםwzӶpmL)amup*)r k6,~InAJ]Bs_Ry{ 9#',*ꓞ!~ܱޯm#-Ncw C:p4ƶmtǭ 'Hb<JF9Ӗ?W>!Z!o;$a4OycvvAtjˋ +GX! Ws9x Kss;,u-xǝx w:o#i:0[A HT!6 h<;o{k!Lcǣgm9<׾J?[Y3cp}ʲYHHQmqĎ{ vvH|᳟>G"4-o[nu~PFmq2ǢxӞӿִY?M3mGu'mYm XYFޡ73GqݣᰝLhݳ]\[??Ź]z;'pmh8ؖb…!{(Xn6UO[k_3Gxf:90Ѵ-V ڠ ̌ *H$ C wi0* 5w9 IdPh2*4hGwյGw_ٿʶmD 4czuثfaa~4 6ۈQz\~7Ub3*'Mbz- Rj%zpej-WcUk~"wHh!o% G \[om&blF?_^NRo8\*eQ  2e@Bb.rmu\ͨ^@UShLf xZ4׫}%%:x4X潲t)Hpis )G# v0:N~V8g;w -.I(hr4jun FOTowx( (!Bg  Ky~Ï<拀vE87o}B: 㞉6i#u&MEcFPNfbI7uhC ;"  @H)P,c 81 heLk}*c8VN۩8[[9IaBQޝڿN-8m>` .D`Ad 4 p38 4fV,H+`VG =#C%0:؂l$,fVк%KIHJSHǶ2>я@Uu{2{H? }>mQl @ _('@z%8-8Ӄ.X C\9m$q Rp{~VH7߫ ӹ+>{N+`rErP:DrJS '@>`}ˢ-ʬ'")-r)Zd#ppM*"'WE 0F^gۃ@'['N]r*U4 2FB,ĖD4SE hme , seѶ rX) l+#p r$`>$+0919Y*@g d_z%t =J$10&(s&O@rX,0̖ۢ]ξ̂(XL>?[5 18O>610%ќsιSn~Pե]P*,V wXn t&=3<&aPe˄A,@X?l^ XM`10 b'UP#kW@luSs[m?ku3K* A "|gBRd9ڜƈhr40lE!rA`!d,)H&\(A[H=ASu&N ($3ћ\|_4 f¿PSeEks1 ȁ@i 0FJ-niz*IK@l`}h&08,DVpqfﶹ*,$"Ąj&* 6UX&}?{ׂ̈́z@l=f};lIa v:ҀPEh4X)CR(PR2JAdA!0yooJ@uEV-0Zd G)c/dL44f B`P)28- U 08"r] &T0 mDj夨UBM]s)AWo+u\E|< bKh}|J'{ue`90 ӵN$8l bSJcBYz6ݿ j b^(.i"+R8i>-A8$+D`An 3RL fQ]dR(M 0͠ɬwdF:Ft"#1&p` .J{4"„Ј1S@SO*a[?oDS(D ::pXI2%BsPk(DnlY9բE) Ę4+g SPPD0J=ƒ&?q2$H,3 ~]8՝A(Zh/5CS%jjEƹ!:v쩇L j~ 9rwX&X0OpఁyAt>4iCv J!$lNr2 0D D!*J6K!0 wNhbbR#*B(D+Ƴ"v4|Ji@ykRhFJ6z984\4ʇȅD&INlik%S4qXkuo=i_x~DB0p/؂̖%[Q ppY8H2ULRD4|Bip jWXeۤY:)FYi%1 5qM}G\L&5tsL<~YMbZs"r"(M_frNah UԥUP)T]tA_@ U@ Dm5k- 9h\2"Kɐ1> )_iR!/9 Y ibf.j LRm`E$eGlpVӔ@ɄIO.x\@@Z&(DUT"IH,RT©D; udhBzcR# \7x?]p55aU#aT!- dh *ghLQBbPQ ΔU 80 H-)/9X{PC}1/L`B)AʇWs%`JL3$q`Fp8I7P=B9 Rq~i톩HH9=5u@kzT kRj%a+#IZD T&4"EHh\y48B+ҳBQT8XnzȊHߩ^ eb&W2i[_"ΰX;l!CSR>f\iRiɩBNߜPT/\~"Ah2w0_@>XZHDK d&N3k.FdRT0F"2$NRo!1dK x \?,HfPLVJD+a-ǂyMReԂ"*X 1jV s0T5?û c*82,tC0=GEiBr0~Jؒ$NL%R6cR)!-RmQj9^ Elȵ*q:g=D8,fjv^xAY:9 )σ@Z0(T9(dNUHA 1EʹeL~TC*RfXG*("2\ )S-TtO@`˩ZD"T ZVQ"VN Pu;)F%.̓)0F?A`"iVQŃK /!$# gF.ȒB@i98(pB5%`Mp `T#TS^K#a%E8YQ1毧/D! 'i b4&Tيyj#Sl8~ڦU!z"ɂLr8%drN!5{pݛl.T^tA敢U0\`a_>5"6I/E Ex9v%"6(͋X,çBd̕Z"Euhۖ&HHFl#) 0M1KEҺt("L JV'd1K}=uդZ 1m^Yf`~W` RW80 9_fTg'œ#cPIxI`1tXH< hs7E.l>hbO6BF:H@cL1Lz޵7W_x)N00E[A a (27SA 9HShQ ٤wˀ׊;ޑB\>{=UܓK- TsZ/UV *ǔ#@})BMqAJOIHR5Iɋ=EB֕pK)4G @% 5#MnEC*@5JS7wr"%1怰HEVA9)5_i:ȠF@T+h2'Y>M# @**xSezT ]me}Bs v1ӮEs؀2z&L>W!dLtYD$BC_2bi*@LZH ݃!]KY(J( W|5 OtMq"&P%mMq"C)03vZK> Xsp]F=sZ)|fHX~ r0yᰞhK-8lQ!rN}|R+0dK]8p)F nj$n@ё%2OGdlRR,x+0h>Sa#BF֑ʃ}91#mb~10-դUn9jBFQ!( &:ߗsnN߸ lz./\8@/YPK\XQJXƯ >` L~z2+%Ihd *Q*dU֕q '%+ 5 4&Dd=挠O"LdΏ؈KfDZi| c P$.Pb?.q CT6)եU $08Mk)/y >O`WXB*T*t:d!\6M)2 6QP7FȠMgp*HțM3?~x[M!*IVd!G1%|H8]$bZ[4mf-dA`cF-g6U1 !D l*Nx3:ZņH&&!Tn));QGKj0,g@)@M>;Ҍ觛gaeJrE=~ڠP@Eu҂ h TH&I"+R0 !SgoKQdBEHxJe"4%R}[c@H8A<@҄ \$T攜\bTfJٽQJ Rt_8HNuPCvk^x~GKz"3>ai4PD] S;aM$(E_Hf1dbDԍ(/%DLRV @V`EJuV+giNx u#r%&ߗMBi]CI-QDC9OEQ$S> εEȠ.d+BO(EBlF* L#8U8 |eoeP0yP ,rC%%f_'Ӵ46i\E3 WJMI)JCRb"d9xG&֏3%"D f@=||bnZi jL08Y)ER}W&X"0@DOKm)^g%(XL;,ݹ H䩸JT:$ٵuu~3 ͙@z(sUSN@ɥݺiGR\&V<&k13y`D76&L)a`1q\f `ai%&!~9C3&JĊb;SY 4[Oi\Dp%pd^[I ś6Hᒃ=J$ZRCȣdVQᛪxEjV'>2.)!Mُ\$UeP파8q#!ДVųL B 9#4A:g&Y[A"~i-os#WVX+Ap% !i0(L vD{ .3j5ho+8Zf`i{V \ i$ 4sՓYyuqLFsyE5щT":iNT trgF`hNS@G)^+8Rʢb~6xz}O /ȼ~A y%+@ . g1P#Q8FTJAD@);y 1͹BtKx=9=C )0d2mUҞ1G1 7op2{FKHjKc+"JcALk鉠p%}0ݰHQ!Am@ϟЊ RHeiM*DL-ĤF!nhD88*VSl?;z KD@48AE3c(J$Fd" t+.2B'@J96-ko_icl PQHLlU\5 F,=iR܀ (TA , @̠&iKM[a @:v%yMY@ f@,*&k)P ha;Q2&qKYi7;J 8֫sEYQu"-@( /R*[ JġeaM@7JNA5A8HKpI39r63WJ!FRWN/xŋK}L8#G?e_gG$/U м o<9S9U{Q$.x5q@ ehp+JuRb~-7z m5+]EH ے@Gs T6w}(H@z4I1纻{p<[I0mC:nwhr19>i`Vي6R'SߒD dCqWoDBE`MjUP-&Nb+^n`-Ѝ[Nm\|&ĭ`T󦰗dϸP4L&PryP9yji9r c'j>z[^T̑pftK"7R,FjpSoK)}4[u\ Wvn^ WH%ke:25U=Ԓ1GO ]n|t"kȀ]Y }dH}I$!#L['C| g0yɠ٬/www 9t*߬ 6ieiFQƐd&ې2Xn3;a%[WpGYBX)SQ+kIT0Բ> /{w} <HWg8tl Bb; 0C}?QBycN76PB?Qe 8p]RڛeB Ua=GYG3`RW0], 5;8qJt6@Y=J?9(MrW$D ה{Ǖl) dV:Fk?i.q ADqVF0EPĀDrbMeGT(n BJ1l[q* T7 ɭ|^[v ׹/ϛ!\wEGA0g&p8V:2JK{w\ GYyJ(/'rLT+I>gUyv3`MA i}a+i_Fi#&[ 0JȕbHhkKX6^@l(݀j'p-i1  4.C UFZy,~r,|H֬cII&3J f8BЪjr {n1$Pg 6:H$_)8 Kį'ȮVݾ&CdH;v!5&"g \#u2߾UUu83h:] w0t\N"x@͵v;hx`S* UH{&`qCF;pNnB(5P aډJ|P$}"lK V8\H\ܛঊPghN| xs| ̙u/^xgKoț@AV9vLB GBWhV^t!/ᰅ1H\:RY-LZnȼ&s OQJ?Jm*opX ʄ2_Jȡ~w(IU3+ Eq{*bj9ƕkmNJgψ7e޵,r+㗟|<ZGM"ek11Qad(mEn !\b ϑ^)`OމFc EɣcMVNǿP$ ϞJ+J\"e ^wR&Jk-/tזP݀W ")84h=.b|h[ 1xp/oJ\@?'&V bՃ{5+ԮM)|ж .r® f+HS=)&L%+\AXҶˢ*m ͭ|j{ꄞg1ɌT"mְBdں1e_Wc CX3.弄RRPAS <! P/pyDɇ5>)h8.FS )eCӊ,g7)Eh&:l1@DD 1A$b[q" &0X~}CأD/pK2X5CXCs@7>Eϥ5U7u]',x|g`JX8[i9!v5G4:nXf2,wʄ f9Zfάo˖pI3_?r_^ 0d4J1‘'@@=pDG k@7A]Q̢$rژkȸP([μQKSXP nX(aѳFb\pvBIjN#&,`sb:VBY$(-򲃷NmJ➐e=wƏ?Y@[:JBJKBp,Q# 9L-nB [*Ԭ TFe TW"Q -WߍF0ßhģ%:Q:SOY?$(RD$(@KvB gaOm7trۻҾj<=GO0I' {gWp+_k2wd)ȚLpkJ^a9Ťo7 (G ։"o:I"Qp$ے!70²$eRNQ4eƭ狧GJ'H'`vn^\ &G&C.9;G ufʂWJT^=߈= %׹x | ЃrxMYWL3~/?ꖴ)̍ 2*^FfW\Q?<g,vq /("pm;.dKW̙ 'jIa&¢tA2-@Nf7gOsbbGQ]53juʴwBMx~x\?CJ ƘhWh!:օZe+d\W 5KĄ[88Ǖ*M da H&f LT8FԲ9v1Mw[p67mMͦ|bK+6PHdu A>}|IN=]"e~:઒ĖPI~TDKh=АEZ!kF'w%( Ĩ$AEkJ#=>Cy^o1s'щ^Y&Ȗ1bKHt%"j-d[ܼ‘ 9!W&(gTbC)NuOb8y{hNlIlh5K}h::|jA2s"ppz3Ļ X~lb4X\zB@")9@B2r X]EZ53F9vn-]_w|Or5_~gĕV"WJbCdd9\;zt$?_6&:*w.-MfE(=Cyj&6(Bʰ{$2Nq*>/7rr+{Rs@:r@5TT|FΛyq{/?+MLN8%d9}tlG ?y<^pXEC,Ne?'1ŋ~yO/Oɑh+t4O\kƋ8//}jlB-R =Yb}^tL6&p?{o|+.8-ykB|/2).'gkw[5ߜow:hOi<4xOi<4xOi<4xOi<4xOi< x@{IENDB`ukui-screensaver/src/assets/weather/67.png0000664000175000017500000003616215172041035017473 0ustar fengfengPNG  IHDR>a IDATx]iXW~oUu1%5c5h411,F2j4ѸDb( ;4{W~THL@Qq<4tWWݮ:u9= ?~!-Xy#C@rss^|1{uDm S걮͛˲ddd@N!cv=&P硶~srrfi4{r{ذw^;<nʼ@zڃBCCg-\pl@@@ &D>Ɵ&(o "̙3YH^^g}?vA>`EV$&&湻+f%66oO^x鵵BpƟ\pt?G///Œ{"""<#ϟoU6X c6O>իW>GÇw.++8%q M)UO.žȠ .|%222/--y&R,6nx866֪Na/\]]t͐JKKW_}56&&&''kv111Q?{-:+J?wp% :uСRg̘1ٙ>iҤ==={ュ+Vk Ywb a/FӘ`,++>|O>pرӧOptt***!+j@ e;~1G{7 :s~\L555:T9Z,Tܞah0(n xhpСbcc̙9&bmEEMEQJCkA6 r''G 6,((#MMM3+W홁Θ1cʕ+'0aɓ'o+// bC)(6u:]Caa>;;[ B4MiCCC(-#ho.YyBt͛&bNΜ9sd2L2hڵ^4o69pPI$\}$,Gb ./^)##c $ ?~ÇKOO[.@}wbY$)T*=X8˲Ym/lƹ>(66}NGu./xⓗ.]:R ##c҅a!T?|pٺuXq ׍N.**7nL(///?tR*0J9H9ӎsɡzXիףׯk3g~(//W]v644=B^EQ`ƁeYGRֹsg:}HHH///Ŵivcz6--(...ɓƸlٲN:usxxxc=ƍ% 7bYYر$HZ9¿MBVVVj& 82 @g=_,7OZ:~:H{ YYY^ piqsNNN>},;1tԩ;;tLTN[pzVWWSJ}Wm6bojTmM[WYY闒&%%"f4 q9q/qބwH ؋/V7}/44t(? ...B}ZZZ}ffnnY 4y]͛cQQqҤIxs+ Rh4Rj4illlEmmի׫o92fi jRWT)**[nݯV… Cڔ4%bj "**8%%%O>#z{cGO>@/\6sE'N*Pcc⪫Źsxԩ2K(L&l6[L&e̘1;ӧϻ7oެ[~},>>>Ĕ=i \rt:ZYYI,999\sLLLܻwuJz={}k]xjJ)]|J !^^^}/^kn >OMM9::1p QQQř/(Ҩ޽{0 ,ww !p IC5vw>&%%455jՇKKK 5TƑ#G \\\LNVUUunr}6>rIÆ lٲvu~H+))RVKiAA mlluJ)dgg7$%%(JW4o e!t;qSJekG-5LTQDg̘3JJJGVTTT{}Ϟ=i~LJt@4̏pׯ)&&&nڴ))11Q_QJi|||UnB(~6 tذacy`!͏K/m3TVӚriNN`XKSRRRSSnzU ͟wm ^^^SSL/xbbb4>cbbҪ*nݺso߾Sxqw Gƍ{xeMaav֭7O>]I)ׯ_o4^AyRJU*o0^K.&@5pvzB:88}>ڴiStII ty /0BH+EFFҿw===,ۓ҉n΍ 0~&5] lUV6GDDT_rr|sppH,fwy=~RzĉիW_JNN: ĒZXXhNJJY,zСe˖,((О={-~{e!Q؃8ɨ|QTBzz6l 鄐y?<i$R8ZGH,es"ÔRvׯsss yyy7nr)\RRBy`$!d0$faͭ+jihhF#_RR"TUUE^VWWR'$x孷::~ҥ_{`,JHm.ctmB M6]1bĦ;v\/xRP<O>1 3$ ܏aAmll:͛7ocaaիɺׯ, X,R^^n㏓=<}(z=x0Ɔz1.]*NIMM-9y?APP(Kf8,k# 0(]v^+W_|WVVV qZ)]Ҍ10 ,#C?Ntuuv֬Yžt{N vqqqJOO $$k׮q1:~("06G~ټysrm۶UVE+JZUUE+++d"8;;Ӑ˖-{7Ѿ}{[! X:`)_@QdDQ43 Bjjjzeeey ðR^5;`0N3Ph4zZ-޽˗wn{:>##}PJ-L,!so `/kΝ;NIIU<8q_edJjԩE&Ig4zl6m۶VVʉ!fmj I C2+ f9D+/Ve:A@v øI)uHKKx{MIIIY8iҤc'O o߾#oXB!~[ju: $aY Go˗/~Y06cǎ.))1y{{~ĉzb2駟AhhX5`U-#n qvR{Q( 3 $b@ !&e8F#n/QT*,$c'V? ,AjBA0~lX9t:>>>544G<}te֭[&Mٳ@S^^wy޼m۶h4CRZB^hU##˲fBa!B(&Q( <[p{9 `Ei?>9==]%o$:JRH)m3BcwoQ4dL~Gsf͚թB\he˖ 9sߢE.feee'&&fqqqsgGaY(ZBA7h{z VnAWS+jlrmȟ[ ׬qo*?aH. (>O?=]SSS\ZZZ1ɒȲeO>K)B̲ 䧩54(~+m}Mk M^9BEQ˩_yB(H) ~`KqB P9ЕeYAݻw'6f̘JҡK.>I.@eYҵV s+o5BxJf Q3ZS!Mo*{Nq'pEEti6%%%qb&TY$xS@[eYA!JHהg(BSZYBߪԏaFM3k]@V #OO<#HE m VXUZ 4++kղ*j˲ڵkW٩ST@۝ =V:ejO03 cE!{/[ yyysrrvܹ3000plv寉N>X12 gZ֥ҺQJ+( 2mE[2lBrܹs};u٫WN/7nܒ}>T*ɷ~g}o=գG˗/%9܍UUфg߼(bN@ҧO=([n]Gt:w<`ذa#Μ9s/ZJ|:vX|ã޾]vM=)]",,,gϞnG=3{zzvtwwlj'ϟ" ֢ Va.MxP8q"STԦ[nIuogW  !!!ѣGC g͚PLsXXX\ddd /ydd*$$d i>|yZ?Edddwyln A|WV3}}}݃{;w.C*ڵk9&f8pd2O?=3`'DQL4)T#չ(N8ӷzXeee]G м|:)hȑ/]Tp dϝ;-IDAT_>y5c=ذa#"dNAXfs݁v<'**ꊻ{[>}S3f̘ݻwoJUyʕMk5nZҪaHM7v>\ `ٳ#sssi||.,,`ƍ7D_ƩݫUH:tS zmݻ&NEmm-X,… UIII.\ٹsg˗K|}}{ݍ.**t7}a G۷o?+00p+WST氰lR&&&  xy8LHHk׮ݛiJHH3gΔM:uW_} #~Hg䈤sνt)Zw]LJJJ`Yևi{Ο?2666m̘1}:u*믿NOOO8r ϙL& wHP7|3YbżZN:mᇬ7nls_U֘9v״ˇzj HIIIJJJJдzL(8Bl|||z888˟w{:N#$$Ә1czeJXjqj444g}ӳڵk7ju'SRRҿ7o^W__piܮҶ</BXAIII)uyakƽ"ZRjƍܒ*V)==aΝ';nڴi_uRt0:S~=zz100pٳ_ܹwzzҥKIm2*==={[۞xك *DuT(eYB%n)B QHOV hĽTV/_|9swww~jkkM۶m˲(jq9o޼Fرcgz=}*Jz?Ү] ·0eCBB2bĈq5j , r7.C oc۷f͚u>244]:,KXuo :q+HGi`YVòl=˲jeYeFQEkĽ)Viba0 #BLn|#J$@W^կzݽGtGjwnP(,~=zegg)_eX 6B ްfѸ@Av m) 51Hm\@qnh~B,e@@@gUm۶-+WJJJL,˾Ν;3\]]999ܰaрA45Go-u*.{*qKp+"@1<;SʨFY]]mVTUV튏;p@z\h4cTTTFQмAnzIuĐ{8"0 c; ! 4PJJBdXhTwܹs7x5N2eɓ͛+99YL&]v]j߾jZQVt?XAEQH#Rj)yգP(4s(;v||բk @+ê f;qQ ؈XQQaL *nȐ!qqqE111?nݺ 6ѣGo̙3g~QQQ:0 fMԮpP(=`|@@Ȼ~:t4!!vl%QQQ˗_رBhB: uqM/ ^E0"r'fddX⇓'OVUU;]P(rssk.\[ZZM}Q|ffŋڄ}vvо}KTzA-~5ZPYY雟o `|M^͛PJK/AeNIK#0cccc /]TWTzbvܙQPP`~g$wmIjZիJOKK&$$,lRJ]v3jԨAsѲNwwmF`2f̘93gΜWSSC?&11qȐ!J%vuŋY+Vxȑ#DQ4e @sy1 ëT aH޽;^!_Ν;YE?>a?o !DEEB"Thbl? i;uT3{`A2` 8Ya^o^tI#Ґ!C>}{lA{.]tBDDD.@Bȣ<.]Ri̙nŊ)t߾}L?Ojҟã<𪨨pEoܸA?z`$B&ԩSeǽ˲B>@eвyc@@zZSSc7nzHt/WW'7|sϩSr嶭1O0PJÇz7z866vni*iz-333())i͛7KСCGIpԸ/$ AWy[ua,,0!Ѳ͎eY?e^z֮a'NiMNv.rر|%W\)iRR<˗/:{z͚5;U*={*//O666nhy:{a:A{Y&RJNFƍs7ҥKjF#k׮krrr,&ɒ`gg7.4t2;![l9[\\L+++Z}ŋ5۶m,..a_~%5::ڈZvy_{HVcKmFZZZh,Xp$""BE)_z_r _PP $$$hM&5TӉyyy/>0`ۯڮ .RJpD:'v^}կirr)##ðybZ^^Nϝ;W_XX(|}aaatڴiŠ -oB忭Ō`l1>333kyVoX_~yW^ܹs:Nt:X^^.?~xϞ=Eh4RJ)Udyzܹ*ww nu;~)nذ#G j5ץf3=~erS3k֬sgm/;}6 f7}00mڴqTΚ5Ǔ'OWTTXZh0e :RJɓ'۷"׬YTQQah4;9pgC 2#^y5jao9Cʏ9Rwފ7j4.dff=zx@{a84 <nHEQ\`Hœfx,5BEEdɒAAA$%%JKK-gϞRJݻ W$?fbddd$~b2 8LfСDGGݼyӘ"F/hKKK`.]j())㵟~iJyy9}w#lll0 q~?%>}rQiTTT?3f=3Ffj2 D}iX??sU3f155ȑ#%R;ɲ= s ܥN,pke˖4{|AtQQe]xxxu~~p]BBB#Gvy  n3)cĈ !ݻwM6mڹs~~~,X0”1b~d^^_DQb~F#5 bcc#5 l@@c޽e 4ȅ+!,wA*ef2\X6o޼a֭ʪ|b~zYf,Z ,g[[.<==RJs׮]kܸcǎl"fQRFCvd*JM6̘1_RJO>]/SJirrYRYzX\\l>x`74 `u%SL9XUUeQtО8oHs Hq cOq]v W_:7!! ':OlF@a`*qs!X]{{{/SJ5kҺvvRf[lI;uV`?N`J`` :t'e7!oxH@a:>1mEX1Jaaݺuvvvv<_6`AzU}~ @?|wF5jILLjtRJmEQt_5-bT;NԐ_ܓlhKF5~Zh)ԝSuvvvFlly50(ܔW^pBh#vرcfy)Ѻ!< M[ʱ@!f D5yQ}JiڊǙ冈rsb„ !4...a(wĪU׮]{PF۷yWRJa4a2H[k^qؚcZbZ,~kzU(a1Le!Ӿ"(dggPJO:?w3fX,$"""UEBp9r*JekkkeYƴH+;DEyH7$BȲ,/71@%^̧Cj$iKTӆR aӧOo5jwXXXʄ ze0a ` Ŷ>q"_Si+y^E[ FMQX+ޗ`mIhdBSJ#&EDDZ|WgnzҴa7 0e =|Nk[ YB:@Rbw/TZKiW `|[<8$0 R'_ n~ش휵S=wڊGdKiqBEQogYV*d;ڈsMhM<([ z%^! Nɕ~ ܪ`UVWOE,<!J*5nc[ pk~TihF@VkeI7C4bXmbV7[؂Ow p+Ǿ).Ca IDATxwՕ6{h4J(# ˮ]kwiql &gH&+!$HUuVM{4`穧u{ %& 轸 w"zPJR5k'ض$PZ &bm---[OuM #zW_M6Nf2IJ@kajjj61mڴ͛ٳ7Km`Xx /p͚5a !,H)&"BkMmP( @fO}ѷ}ُToo%'@xѢE\Kx<4emm-{qʱ=3?L$>prdF8^dPaXkM43 uaYVmmp}}}o,ܼS.?Znf /\Ҳ LHH&|Rcc#lۆv^}T*.Ų37)Am*&. ,lqF| !t}}W9'ϟɓi$uQGבGh4q#裏]`TVZkd2lݺmuRBaa$`?b J)ömyضX,c̙p ;餓^?Db'?d2L&4MqAa(v+1?l mۆ|>qP,avD!0 \O}}=ڐH$ ,l4xffT(d&mۘ6mSgu5'|3࣊} @wq7{W !x祔?̐R"byD$"+'>q /@; _pA'գ$Ւ=5l[|R34M$ ̝;"1 F1MRJX&L:Z[[)L"h---ڵk/+^蘂ؗ}dòq/:O$I`v,\-;A''YX fB2#8,z;B8H$I}}}P ̿꫟8 `_බ,)+$,K;0+%l3:0ʪٗV41sL$ ?/3ޏs(q'v%0"$Rj8>|_ʯtN bfZ 2MsRjێ;^ε +Z`OO:_Դq²,  v6 mem#8'̯95y>h4zիoG|cz/ 5kLZ?Z__Rb1Dss3Ϙ1mmm܌z(`%I 'm*1+`owGk65JjM{UCCCSGGǪL&C׎zٲe\s5j444ؑHlhhyi(`bBbXV R}5FUl}ЖWag{0Y5 L?\u֭[}衇Hgpq ~7f?~JJ91 Ú2e qૼ D |b|mpxQI]λ7XA秐#auE>ǔ)SN4 u%>&pwwwW_) a9scX,FZO8D4- ƜpTl~5UgO}0-?*~k0L)b'x"~뭷Q S̤{ɯ&@P_WޱcG)9{l># @l"3 umӱ4IaFa΅r4G3y셉d2a=drC[[3f3| ɩ~Zao O?s-2LN8| KO\>/OݮĀp^ ߞNbt8Rٶd21}?[;ATm.Fɓ'7NBMM f<ySª vr pU|ݝ_&i^㺮f,b(4l۶Nzٳgo{d>蠃{X,f.Xg͚EJ3@{ 2P(j_^|jl_0䛅J[=p@kڶm#JP( s1?}@ Pw嗯Fnnn'p$N!'P(t<(,aA0g#~0 ^!`2UP0zzz0a„;o]{ zΜ9ga(˲ĤIF˒]fJ&U;UsKD >JRayKٸP(ȪmW]uG #1cyap$xZZZ^ކ3N#J94;+,]Ntj|JaAݲ,Xh4xIc|%W S*E4e,A%FB%-|Ȳ,D"R)'pG=@4NlvHQy}!'A.T#*|atoT *)dw;mv }A8-2wwD4@&d2g۶96j'܁jIRnJ8TAJuV Ga ߻ 0Q !ز,={ nT "2R鞞"b5;C2*hL(Y8p% >i˫䄎೎ET|y;f @D$-[PJuJ&έRIR*I}L_^8^fժiJeVsT\ u}$DDBbf$ɉi=5B6nRJ uf9ˍya0TcPCc#>[~9 ;!Jj 0SXM=?AFW*Ͽ&pbd$0*ƚbׯ_nSSӼbmۖeJüaϻZ UG E<T\}ವJ>@t}0]$>F% 4&"5yמ<$"DQ6 XT~%543>+2+]͞Pwt5t.r^ e)X4&%b^`p,`n޼yWs12l4^0:5,aOJh_Xò`'V(}&%AoT&8'wA*WJRʈR*7sǪ2 @?looVkkd4l6YFyP ; GNK~:*wNQD89;XnX"Rʊ9pU0UL'g;XR3GPzSX,wؑ3g"bQΨLJ Om)XDUZp>>PMX,=s.eX B  n-L>@""۶.q'[ѕ_~?ⓠ6OA2sTsA=J#XF .0]]===?7:3DD3;(@im "Z|>?s/0aBq9eYP `'U;h~ Y Œ]Bi4 T6{\NO?o}(I}6W%ȝ4@ɤ֮];֭[{3vj 3?|PT*'T2i9l~\((jXz= T@@rnC'H]TI;5 P6X4yNWXѽbŊW H556666d2,+ -'NlhF0ژ0 vQ\/#8PɎæ%|xn|X,R&QRww?V۶PNݬ!& 9>Fq<1s;#J&߿ƍ}ũ_,Kz(e)I>󽶳8K1RJ~{zzVERn&%$F[}w[ ۽4{`1<-aBDmۘ>}BA1?YjG!1%|Ue$7Z۶MbC߃G,~yuooݺ ""fAg_5&t7?w9*tnDmf =ԋ< r& 8GK(l%ѷz/uuu ,8*+W;̃# -iպZAw7 Kh_Z/ L4{ဿ hѢ .|,u{B<zq`w'_D7ra]c x!2#uމba̰Q+HX 2qLj$^'S5z4g,AIC.C>|>oʠٹ|>Rqz'+Ks`樔2" ! ]!]TzzzK,Ys}=tgɕ(:`TԤ9} 4R 75Ȃy%?r4 f0xg8i0/^PwwOVpطba)}'VX2~[U8nƍ76a̬mv2pOOOoggggGG\.׋ ̜Ci ,Qio[nzްAEKi 2bP# b  3: 7뙾q=hH$\U6Q͛7/<9䐣k|* 4rtEc 2Lcu}}}ۈR(f$a˲(`FQ6z` <~wy? m%8͟luwwo^,/s9t5?}{LOs}SW3Fuִas>)2/tDLkeGNݯ W̘"/>ZܩY(av^{wY2k֬s̙?a„Ix\6X6 R6,wض===6lذbݺuo"M9s&M:45"؟;>hR^v%Ky敥PsR\zV;z-bԧ#{ ҄ǘkJH$vm!-}@CF2֪SliuyG̓K}  %3,"˟_|gN8qZKKKk"D"wFK{{]]];;;nGy(z֭kniɖImmo IDATmS[[['5555b14MCFqL&ݼyׯ1Di>EN@k84?$O\rpw8s(1;c'w+e>JM9O^mV- Mज़?~q?M aZYu~32xS r7}QIoy?t`z1hW0DmmmC}}}C2lL&H$R^ʚ]׵b.ɤ`*r]7u$YD%3H!D,%bX0JՑQJeDdPJ`xb3v tَ*#@{JN/9(iJiO55|e; 5;|qţ-fD1>]\U#q&f;wK{/1GmEx5x>dUTV񙿋 po jWXQeK fMqgE`IwA"@JF!6ac/aDC̈Nfӗ"-vf0ZK1ss b 0a*־uwt-똕(d޲jisH %z8Y ^NeP#ZvSj/n]ʽE@g|) :Z(چ (28΃n;@PB. }_8tu,BH"s&~\ta2ko_K\UE Ojk=^iopᯢ1şK=.h!K@pVC?j6 \/ 7?0D=ßKepY%$3ifs#757^ wpdL0"вUsa-s-͜1ԍ?<DF(%Z jG˅gThk6Z% ܂!$㖵b7KxfbW_Ɍaz/j5bAE.% \6?w:cXѬN<Ȓ2gڌ7̌fP (9g`*FwMNUF$m* HC[vK!#QU\fB̒d*C= Vǰj R'IC(F E/FЌ @.9{ aeO:ˤ qyC+M 0 ! }}X6;\Q9E iZwAr&>n#:H%]o_ 4Jt^A)&J1!yK"ao?% {񄣘iB>de`,̱'D"%e?2σ A fa@QN,ȥi~R#{6 C[@/@&9iW݈E(e5]åDžK5YȏlӲ̠ i } ` 8 ~ԁG@Aq mDNP}=8O֢^ djor/`f" }DpDJ)5QO{$bďqe_?``e⮇[kC1('13WͅZC'ZX4Lf5̐Bih|>Mř"`_2Z) i % -UhT W%"Б #`du$`\ 0}0D%.,B)y3D$R(iҨOɔ>" ɨFB&q1(pXFfp?kw/[xti0-oJJ1GBOCBA4dz_}u0 w0~enxe5K1z aj1dUY ZaFJ`w8mv!T׃+X8!bDV#6| A N@ wF|Uo>Hetle<6ih$e+ Q&ѻk~W`h !# x{^8B?vpSm1g;n}x) gۧOUۭD K{)?^OzэR')1뷯|n9P"F㽹+ _pz|f˄MML   e$ } _YP,53\q/\ɓ? ">P`YA(e1tB`ժ ]}nB#?'bX2~Vr;m\mǣ/b=XjDzp56gfDQ A0 *'~EM$)rOO&cw)xvaģfvz&|?ԕ٫:2}*`[:8)qKֵ6v|hO8>59٬#<&HLva xO>Ab)BP4I .nNe"2R eFsimS0E1k0eaփ-LqDZTo|%vS7sGwXHV\ݛ,8de.CjMV ߿calT#NgY"N=9=ZFOb_?io@#v Kj)L@3-!%'isKG$zҽ(eF%KFԚN*FpXLEe/J4b͸ӑ lzdỳϙs瞻i(Xfĉ9";w/%R&gC@ mw+.n,ؗRy1k>tkJZfCuJqˁųIHBͪ֋>"`2zm 6\Y|kn!FɜG;):82):TCSd󄓭y)vv>Xb~>bAHu/;aRԨa4+YeWo󺇞 }`7Kìt7uS9un1ݼt,"cZNP]Bg;ԍ`A4co~p?1`jfvH:Ȁ)͠!H1bsy\w# ED9a1@c*ÜNp+icM+2̔P?/c/ӏť *\_C@wFiߝ^R`ߝ[{),z:6{WB D۽z/Xon]FDxR O0 ;d@H{49gG~a6l's-d-4,5[{13ίt:[Q.zv?[ Ar[zXҵ@4{0Z;j|[ԽnoqL.-+SJ} ?$'y8=:eej4H3s?JkޫSz6_qʌ(g?#t [lq1Z?L .e؛ק5Js{nSq.p—&qI !zR؏ۊ62)u!i90&MQܨ`oP8a_sTpBuFiKkg|mL`f=_~R"Z6CJܹG;voگh^\ԷPD/JS-?no<ϗ5b[ Pq7J4Nefun-ngҷסCl'ƒ!C8+Q|C_{Sȁa)e]?@51M8{r8rdW}oO[êaXթ)~05u:GڙBG7e[5<^POɜEWO1K%4ZBDֻ]> Oopr=RʽgKp[ w?6&Qך|({|W1 id{:wa槹C D~$w =ŴC}Tڗ3Լ/sRʈ]kruR5˟$̢DʭMصQ(uڥ;@fysWJr!jC57VpOz]?gF_jZ7oǛhErfslؐD45G`XjE| %%:te;nEjxhu60"IZ 1)ٴb;sB *a.U7fՕ/" 'پB@iFHf9zt_qDE[M"Nf Yt]@2<4#|dC( 1BG^VX&2v+?>_ؽv&`6ڽyaءQdzLsMgHWuh<(2"̱)hS!3-q ][8aDžךt Z\V^fo}7ŋ9dFڅ?z֘PD.en|u3bPܷ_4K){R]´lCNL.(̤f f2@ŕL y !~xf) 1q0TNh>}֎h}&!+cڔl|c"{\55W@3fT^ W!DRN3MZ&y-dmZ'@)Rc7qk݋RS5Oc أRPJ<8SpQ;qLEIENDB`ukui-screensaver/src/assets/weather/13.png0000664000175000017500000004431715172041035017463 0ustar fengfengPNG  IHDR>a IDATx}w|Uoz3j0HPbņ{/k]We-,VD-(ui H0JBBBzS; Mu綹gΜ3~D)aN7;#Nyt[8t:S[ZZbx ,ˮfVTQ?U^?tkߏTSS3vE1byPJ;Nf1I@)uLmvv&MK@ ~80~$I6 F1BL) g,˜$Izpݐ$ɑȑ#ߝ4i0?M$TV\yKaa},@dd$fqL!Q`4< !J>E @EH@ 9Nf};qĿ\x*}?_ iǎ׮]wAF'$$fFQVN, v;flǩY!IA@ an^@(H].mllDFFƿG3228_[tkbb"M&1h4"22$..uyd9$WQvt: (/ &a"W^yִ4l63F4PPIU$u|/<YKK Ax<:W\1okgSOx<פFbIFFfs~2_ W׋vx<x^|>I0A/w^x@^X?62N-3/;999`4fv6p@T|3zۍ`0A:^ DEIev뭷1bġ^ Eh+q&V\yѯ_?l6&DEEC(ȲxRC+$A DQ<hIDc,5d%I:FcUtttmlllyJJJA* B?7nݺ/rOff&gZlQQQ,;;vȲ JqR8 ,C8C$C4_$@7-`DQ4h6+3336;;TM ?7(rWl9r8&vjlIq\Z/W*"vjAD'dLeEcf( zz`bbbv61cƬlT 䬬7xVUf3 Dmk=tH,mnnF{{;Aǁyp\h  UmOE {<\.PJ>8St7a~N  4jm6~wk}ֆVA~|>AHt CGf!!!QQQ- SSpLs YEy Y|w@ ;sh :L&0J&@/*~?~!@C~00L HHH@||<8H(ԕQj~` AҩS>:k֬5qUSN_fbbbnb$66KTr @ p!y& fHOOGlllu8RpqVfPJ9J)X,!$1??o1믿ވP_?CKLL>h4Ll6#t멫ď:y^}C CAtt4RRR`4;t*T`0f(8v䴴4VZZ?>ڄ_I9:nS  >** F#W#ZF)=NrՙXtN}}Tj Y &t2J)u,)))_:_~j v{_ cLKժ֪p`ЇW1ԊPB^Z)tܜV#hpRھ5,˰X,ڤ$ɐb*GFFrvXVL&-Oh=t կ %H&G &-xmtuhӿ*,D#d9|ԨQ7L.:DcƌOM{О@EL&90˻n6dȐ`YYٓ+Vϟ'x_#Էo)VL&fXZSN G@/)j *B']w.b<I:dF f0H{{a<'T̛7oN1 0ǟvQ, Q+OA0uU= vrK{uj%VYV.22Rk݂ް' @1;&5i5N*z~t:pA=d2BTFߓrJ=1&O>`%O؎B 1#!($k]s2ҦT=õÅċ^ sj}NcMsw]m6PeY8qb%o{< W(tSWޞpjվ '4*U])uwNNL&bXdfcSLYW@Oc&A]`WwIpٕ׆~Cf=@ߧp3/z]z+@r,1cƌ$dkxx8J1p8M cl+D)cL3Pm#\1J^5]5Iz {@C; q>|J/ ! VȲ̔|=НJg IV\~nmUp` LsWLFO%5zǜQJa4СC톯QwQ\EEA;jIX  6SD$Gx4AӶ0%;]A&K^wfO{ ꜫ YeW`4;v`CC?66֤1  |ҩ'zkO+j>\@F$j[:Wje:T^k{sTpvcA)%`XmX:0 Y0^>/eÚeҩ^vg'B{2j5f@H;vv#9c fʼp=Zqkm8hzmwiIztJ5ݕϢ!~_"w'Ȍ1Yi$b?X1}L- NP:/l^SZ~-}rDkP~`kpL]NC(vU z`ttv{kэ2"2o/77wUNNguxg$K&".#Y?p]{gm2[ _Rڥp8g`'M&uM&2c1Q@8(IE7S)&Ƙ @D{{{+`F!Dxd=|SzB[n@^(v{\vpԎ?"R0|ʭq,MHOx7ʲlDFBѣf9 @@eX 3_ WZ .7o7۝M|WpY슡z39u;nE0$~'joȯ J (``(..>h43铠VJD$=,p3]y]@k5Au'K03ZMqzWv8sw Anc,mAJit·89A"m0 u  l~8gÓ+E2\ogݩpkƺːQ߮ZZ#GVVV~f>Ƙ@k&[D)cL! +///...7L QtLVwT7\Z{nvKwփ@w7L}Uk^oF AUꪽ_ Gn,2),,tXuIm;m AU4'Ng޽{ Z$IZ,hH !DEL)of>|>$pe?w©gck!mب~kx8͢7s]ipZj~?S~?dɒǎ۪4!c^\QB2C* ! {",&Y)*IeʲL%I>(o!$1(^ 6U gv%|p ,q%BիW/F(RJ8_A)hIVxb` 8v%K<|G+cr>` F !q!Cr$@ժmv>;R9Pchmsu]8Rg^ Du/T +, #v#$q7n_d ]N"%IqKړ'PDț=͜9>`;v2====555ѣ U2TիWq{FFFt{{㔁ej>Ai(5JAzY}vq0L$ԽQG8h S 2*"Q52YS*zN;1PMcVaڵ_hS׋B Ϻn'|/~I&M={ت4I2MTY=$堂 >>>n[M&g0(Z[[]UUUe> ! @ VR,Of]&<۽1CCCC]bbb}h *jԲ !Ue$)iӦ>ҤIS<>JV&``ϙے)EimYQ)c~9rbX !p,Qz>RJ!Lu(^?eqUUUbnnov# !$ Vw8U)FCh(AAab!a !fƘ0߫!-eYն=OFXxv믿w~9nHNN ,x~͚5(љ/LE9lcLgff=zC7@P]e4)jWRRRZPPcUUU)q~ :thI^ IDATN~%$$XVvkf0qLs=BP__/mݺuw}EqduRLi:PŁf,K4~N$ V-̙3gDDDeff&RJerqq궽{n^x !QeԬ|+r !f$ 222 ?###-...bPI[>rq(1#d/f۷oIII1Faz ZZZ ;s-)))F^SBQPr#РzrCA"!(7iLJ;΍͢(ju: uU3B,BO4QC%54scLz-^'>5J*++k}n7o~ʕ+RJ9A`mcNwzG]'Kvbc,Gz7hX|W %cuJ" 8Asνw~ʦ={ _RiK`-cܓJIe@IC{ZTC)Ÿ[o]WQQr\qqqk׮?bĈ{)tڡ.5 tn۸qVUUUԔ*!BsʷǸ_O'0(^/_\XXx!8Ȳڤ:Y 6~ܿ*$99־}}(zp6U}6Sj8H RJ1fXDm.f"...r_}UC=tl67Oddu##KKK Ahj:N`)S?/oqGJJYk׮ 9~7!DI|,ph")jc,hUl6fϞ}ksIOOO{뮻.v駻^|ſsw}^5贕.&'cNjbfBRچ7ݢ9ZÉNg˥E) 8p ϪnbX ݻw=Ss9}c56Ji+B)u?u-[z_|y]vƍ7jԨ>HBWzϧaT/81f8N$IdDQTU$~:vI_a˖-[`wݻ{ĈgΜ97###~ԨQi<7Kd7h͂փ#iӦM%cQ(Fehw6<쳷~fdd;\rɄ˗{(383ɲ;wǎܱcǧ(ݻw`tt4_WWW1~|N6 P"d{u@AzM_eiiiknڴ `1Qinx<." h~'׬Yg}G}x֭;ce˖-1cƬ^{mu0Ks?~˗?/}=L0555 EJLL]6[nReىFRC/^zo_m6l2>>>&22$|uuu.rrr4xওZ:gͨj%Bg׿z駗 4hrzzzoc+u]N6ɪU6\.וW^y!B[;!NNKK1cd۷nO׿8--L)u0c aPٌ1AA{_999I>l,--6lx%"jx@L}чR ~Nw477f3`ଳκ1㢺?oh{R~B\v_{433s;Ɣ[ZZť|voX&oQQёdɒNc6OJ!_?1cƌNJJ)S&]|/[~ I ?+>ꫯ~^wu#Tp P&&2ݵkׁ~> &Å~O@~rm]yWHIIv\RFF/((h.%B4ɳN #t^z: QE&/ FnѴqO?o͛ gp8Z[ Fj@@mm?/‘ &XhկX,?[l曟+.kjjx≫{ッh{{{ܹs-_|{tȢH|F #Rڤ j(***nztwBLV 9⍈,YuΝ@MM͏,,zξ}egg..//g{۲eKmAAA?mQSSӿ̄L !{6lȞ}?~K~a8 pD`„ geeMrEo^+rt_y~Φ&;|} `J88n8qL&SBݵapd9s|R]]c}]ull\tEGtN`nٲ% %$$ĘL&ӨQݲeKDCCCWa1yB<_͛w9sMLL8sF?ӯA!DE.0eʔ˾O:fY|ɫgΜyhD0fΜٿzEEҥK?]pt3AmRq\5I%Iro}%hhjj`0Y}* !o^ZTTTk׮9Nڰ9''opmm-۵kW[׾q.^|7R `!@(ŔY8.TL~zyUUUaK,ٶsZzlʔ)/{ws} }^sy)+ OhӷQJ)Y&Ϝ9rȑ#zhK|Yg9oҩ8!<@cbbAV|bn޶m[s=?jÆ Żw.^zcTX~|ˌHROyzb,F[Eaݺuk,YСCo9$___R*Եw؎ q^zǏO>NΎ$ ѣgqEftQf)IR,&?9#PJ' &^dɲQF]|Wεcƌw 7{7~G>rGG};/~Ŋ/IԆ^l[:tд}qqqM)sv,BHoP܏̟ە4M<8RSSN6mb1l6kLLn"""L .\VUUkfGYE9ritfO=)Ҿ!e e bF#oISny>@,Q_?_?c„ JCUT)Ng#gbb7xog}vVff)gWii&L),,YvBZ6\0<ˇ eggOm8(F!ʲEHv<˩Щ@&DQleYV^ŕW^9a{`0lٲU˖-kP9 GvIvZ3<}}=sss6k֬ێ=ZPYYp:{Z[[]N-tE@v>}Smc˲Z[nrzz|.JiZ{{;Vk :sŝ@Y_|l p-*.y2e~JL&g!TtC){#G B B. 8n`q\O_‹!F&PJB&, B(B8B衿Pֿ91v? @G}̙Ȏ;_ڻwoc566z>KcǎJ>LI" $Cq7!ώp8p\8ǥ" Χ HQJ t,Z2ōO{矂 n[d1IJs{hmm M6-"FDDw=&&&:++k( $.^>}T\wuYUUU(?U@MxmFK]A-5Դ1L2fȐ!q~ƪ*ĉ'v֭nyܹXYYY(I#F~y#<$:( F}[PPиe˖cǎy >D%_z?U9فQ]S ^cǎCo4??~>Cv@iii(Gydt25D`ʕo^y|%{q~V[[,))VWW>O޾}{lcǎ7n|[l)*:ƜN\WW'׋{m:|nӦM_S`xС;vXVXXxÇg@ 775+eee]Pv\TTǎ1wo}MQQQS}}c7nx7v644x tL0'IfEN>fk>7tӫ@Ff0dD>t c̜kۛ2@m۶?FFFҶ644jސ q\B~mݶp͏ =z駟rW޽6))644#G̼{ֺ)`qULLtݺu?CJJJŮ]|%kkkkjjģG<|~h_xsG۷|޽5;{!+Wiޏ1%%e䫯Z~17|Sv~d,wߝ ҥKZ㕲xAΌIݻwo>|yFxaÆEBn',Be---c>Q]]]7qytvܹsĉsZZZn:uꟳl۶m_|Oɞ Es]|׮]%>+vj~g\pY`?5*ܵp/?-={\.mFəp4Zj-r  fTTv`0e`b4ɸq&''G 9TvVl3d2Erg2dHƅ^8z7mEuuu;WZ C!$`rkkk7o^{?e?q˯꪿ZjYg8dȐxQŢ?JL@ȑ#&$$ `)S_={ׯoZ~}ömfV~}͚55!TJE*'T f)h40dĈZjM7}mpW^;S"Bȹ4D|*LכLL 8B%zOS!_fZ/p6B&S"T2 BH?f"8nZdc7 &A*S}[|_~}RRRZƘL)mP;ltE6"$I&} RQC F g577X/߿H8.UEuaR**5~3J/"%Iwƫ|DL8Ji,c,A8Ll63߯yu:Nt'xKB:R  Bii^OV(IVu={RnwWΘ1cݞ@`8.QXJizb ke!;aHV*ɬ<v#<€(9IGhV>}OJ(!?cI$7n̙3RJތcZΝ;w^K/KqF*Iq%Apszlo8(:(!لALJi q&)V9I (Ătʔ)?///wnVWWJJJ| ;w,ݿ}J{=iMBk&Bg1v~L5y͵1ӳV}O$!`0SK/ɓ'|&B`2]s5M:uUW]uab;Xz щT$S2a@^^>}l[---8ObbbUuEKIIIy?>|xNkk-;;,"""СCb~o|_5ӵK@Oz@0h4^xݻw1c\{֯_Ə6B޽%-^xi N6mG}$+++.??%&&l۩j54hԬY.Q;}WktD FUWWhjjj%$O>ɤ}lYcȨQf}C.ə~_:thdcc?##þf͚¤$5k*dBɚߌDv>|ԡCfGFFf{fϞ}رc3j;vX'|O6lؐ[^^ݽ{wb\n]y睗a4… ˲]r%⋏&_ XdʴʖHHHJIIݾ}e˖-g d2 7o >y VXzȑ)޶zkl&й _& 8Y2^|ڤ<5KOII1=zGd'zezqYc/9=|YG}>066]YYt@(_Wk'#Ç_4{p;!CA$`~t>uLe٥I8uEQ"7F0C % 0@ga`UwJ q 9䛸]< F,4NIENDB`ukui-screensaver/src/assets/weather/18.png0000664000175000017500000003121415172041035017460 0ustar fengfengPNG  IHDR>a IDATx}iuutuU`AR]AAy%%:%%CX{Wڰ ٻpZ]CiRä, "%"%QA@$`fz:st9=e͋ꪬ|;,`hhhhhhhh~+H\ Xj`k7Zs7 @G:cǎ -..꺞BhAw:˝Pnx72Z ĉ'z;vѣG=}UJe# :h5IJ(im/JϬ_[>|f2s~ .x>7MOORfY8l6L&MӤ"B$j=HHxFZu|>?tMw<ވ ^G 6EyX !ifd20MS.5ME }a(!ZbvvRʗvGy -~cb!,088(J,˂"a6hRȹ9T*naP2v}cs;b~+Ǐuj###Al ۶122۷ 6B]ۘHAE0q ٬r4M麮}:#;w;y@ \j Ǐr->Y, bQnٲE B4a?#'sObqqh4h6u]l6eElP(g>н~ RP/i^gL&عsXn]PH0DR"cffJf}A b'QAeYnnnݻ{X(o ]J ?OZppf0&''aB&\:Μ9Z0 }0 ERhqqQm6lhð8ΩWsCCCǶm2ZNZ ?TDv_g?Ąf 41::IA ' \1== Qg<]GhI`;s\.jtt';vW_ ,16=cό[|^,K_~4 i21@@.uDZV.@ !B_a{fF0 fLNN{UW}㪫:¥ 뾹gϞlB f!lc>9}D*AӧOu -@fE40(d 0P,faf0 3[]EQERJ) Лͦ\s-y{~~{ ]lh"qO~\.EͶml۶M " &qR=~mO6Өp]ffA$ui4MA&```>?b j Zdyۻ}{߾-o)+%|o e|2N? CAxzx0MLeA4ضa0\tA&~>4 c~~^j5r2 ‚VTm۶oweYzX4rضm(JXqΜ9uh4-@L5Me!@u`dde-q"prl61??YIv]7 HDLZ@`&ߟMjz:Չ'Buζm133#u]GV3( ð;o÷j g{kqL>T8#~mu-ƯzݘωԾ|]{OOz{{sL|P8Әݩtr[RJEQa=_m\Euu] !RД.1ׯ~'K l66iH_܃RD~NR4MyDQ$(qo}[_߷o+.| 7 !l6ԩSmR4u":ҭ[.I!U#ql="?Q%N #e..l`ڵO'LӔBJJvzI \ҹ'ٶM\ ~CTjcudi؍7G?z-( @d١b84MS 蠡9m$9, t0k*/0 Oh$PUtP}(a6r\NZ-ܲ-?h.HoLMM}_8Z.|LKЌm'3t3y+I;*< r?aرc"i  |Ǐ={\. BT(4۶mfm63h<X!!9cXZv &(0a e>Vw"h L}_k׮]S\NsG/rxxbQd26PiO @N9Yi]LJnD HQnA6`hhȶm#;ZtAc[h'|r?R/ fX6m$m&DPm M?-˂aLV@;hnJ*IHgR'D"wD\/~eV+]P-p>5=3o>w8====A65;000 HT;)9҆\m46"|=4\ cHp6AhQE("=oZ|iGrpOOfQyUW#r[tXXX@ZERB2CQtoރ拉҈i$kHu [^vE{24:_E}Sjl6k]v ˲ QӧOԩSEEsZ"l6\.$n4ͶI .촨^9 ͠i*xF%fhF!ͦ\ro۶ׇkFض 4PVq1=z}?a,`Exwa\.c(Jp]-w.:Ek%pJL܅f u(A [) l遁ڵK^{+KiސVƍ7޸0lBAӛ$8ؿ?^x qP(a c爐;E}ߧt,!qF\qpf3sYc:|n'-,[YA 2j(.8޽{T-YxD;w~C sbbBNMM 4s'DE>OfQ,8Nbyrul6QPVT P0;; 0pWc֭p]w`5ҼK3i Pf2‚ky6m?G?jmp>4@ϟrQTH?8z(Q. Ć;LRTfZl煞'ORx޾}Ig6nL&d2}ddi.* ƒ FGGߏl642mf/#t]GPHRN>JqiyTUٳ'-hcT:WMVq-^*8^1lz]gΜs뭷>t߹s X^\yכ)-B>bPV122x011\.0T%& i!O < hcnOON8|z׻(w1O5?jߣ֓-˂,KX%-BV3u]ewye# 7oJ%=L&i!Rrȋ/JE>Ouh`flӧ~ ahh(A6MZ}}7MC~@<.*JxСB L4@. ݻw_?44r9+hlZfLd pO>%e$'H+ТQ[&ASO=~_vl_ҘnQ'-}&(@0, o :GF5rb\.7Ǐ#$ Sԡl6?8CӀ~_Icp&r>66%U0D`:?5dߜonI󣯯i53WuRL?G  rJ&F[oW'w+ #U_0-&iҶ< Bb@n>j>~7"'@98N⩓k1Qڜuꫯ?^1,Y|4_WGQ_ir%b 0(|۶*XZO ?䘤%j*ms|>O14 @s 7|>/c,|X7ZU}i _E33Wi>>@Q6۶eRCcJ\f^T;i PJ;XLE !z.*i6/\-WWZx8 +iW ϝ<i 9,JF&vS2`Yg+O߻I=ˡaJ!v̙Sh Y@9򧃃a6E\6֯_/7l KM{+9hTʴa)-Tn'=(EȄ/Y/8uN< @O#vvMYe"|}Vu; :b3t>8p}}}brrR R-%Uʝ(U:͌QmB&GQko_زe f2MbK&<8jK">u7^xO󇇇Ů]$S#eݼW>W6>ЮP\5>7uJ% HEM]SJ)h0 Çχai+l}G߼k||ܳ,W^y, iܬ2^;$D` {@"zdfۋ #' (i}Su]yػw #[X O>HumݺuΝ;ePBWKSy̴Xy=*u;:ZjHqF)j"fN‰':e $Lt~2u]QC!o߾^ !FD7y/e]&{zzyjRmҤ3%TuV[v'V4)jqu]T*<Î;P.ۦIä @̧y uez]z'gggŭzyB! !Zt@`_ !rАn eNnAltGd:Vbt'3M"UgAL L:Q,h4077Gy$IB()-FZVFu+_/BKJVt*45\sSoo@2}tt4|DKumv86!\ɮÙiWk&TN&G89x{HbZ Gř3gpe%m[iDe'c"zl6׾{o !L)e</@LNNޔfe<*2hT;i&DgA!\sW ">HU0̻^'W_}DR.I"H4}ffz{վV. {Q6 7g2a۶#3L2SU0o0Mtͼ3W=B@՚4J3C%SYR$_ ^ΕL0 |'NG٦Ru^OSЌZQX\O ˲|!D"cd($݉Eg4Uhjju]_P`iaĚ69vM=)e-W,ğ5!D#6 @BdI!BYiHN;[TSOPgJV=iR{vnnO"Z ׏v EЈ6R4ҽ-!Ix:$-ۑ&Y@M~l!iiJ}vz.aJ2 0x;ޱ@(њ1CAO.I1rۼ4 Ja*3|> (i@H2rJs6ӮM3? ݀Sc "J۶ix4s5C9bx=f0gw@Uj:gHQuԙ:tugVoܭ.T8oWzOh ˲h47ڛ7o9t!D^JI[Gh-hk{U ӧ_m6B^n$)]S'{64iNS\bTЮyRxÙ(yRJ)Ҥ7j窝 5ʡQ\44TmmNs&;[O-8. Qa|||=7v(^KbIPi _&Q">2zK/t|۶m}ߏ<Ӄ im_M'uVu֐;Qi8 Q/?OsIyyj?t(i&@˹DO 3X%Zњ4M^/sI7}٧w~g G!`>쳇LӜؼysu]`L=N߫~[ T)杛n>BǞffz>j}_;qDxw|lF_/ t]7&Z0dh8߰a4irڀTp{XՎU7jDji񉮴$@NBG=ǵ/:jRݻw?~{\:Z&:&訣p޽w=k׮]bB"X Ԏ,vU8Z~jZ@M/tl0 1==- KQ<wj[&s!ϟСCeF!BAے/2@ٔ;ndRA (J&1;-x0w2 ,hPa'?ws+ZqStl@(r$KT+iUՙveYv. MӴ?whhl6pG'e ri8Μ4gO|2-9=Q1#hQېKu ggyJ9O@YmQFlա=ϓ'ʎD/y O?]替233h%vT.]-&6K`iVd(2%4I&!'x 6LoTT@2,l۞3K{h-yhz$g.5$(moX-HuaZ3q;%?F 6HOc|}$հݛdbii~`uD+8djA+N !(8Dx}}}۷oܺu릱,R177+G#G嗏ŒA >{xxxtӦM[7mڴqlll/W, p]gΜi⋯==36Sqx$Ӵ:W4Pԕ`i5^pv,^% @C>> hEK]qr__`@oobXv&15MӥAVkVՅ陙S huִK$Z%um/=R\(iQEz>???̙3V{ QI\r3hgP"'_Exh-߲iR("Z`p8xW$!+w&| Y-=VCH]QETO^jpſ &D͡&+%&6KJIΤV[`dkK, "{3i  - #f5 kZsGOK 0\G()h&h}?\ a#X\F'O*CK쳨J"i%L hj;wP/ ( ÐO -Ϊ`XNqݦ ",HB%@J3,Z8O !B)eiȓRJZEDI`H4l\Dk>irV$j5@U594Ջ 6ѳXg%@u݊ȒRyMG%%gakha:t/1|s?t1)'hfjRf(2e+%mE)4͏ի!944`FQd!(:S4͗Rz&_tu}!R{4Mt4Qr՚N'>אҳN/]jz#WɌ5Z5Z5Z5Z5Z5Z5Z5Z5Z5Z5Z5Z5ZJ ;IENDB`ukui-screensaver/src/assets/weather/41.png0000664000175000017500000003440315172041035017457 0ustar fengfengPNG  IHDR>a IDATx}yWg==Y d e0!@%PaL R0LdȐJTPS\d  ٖek6Kz{wIO,Dw_߾m}4pkƭqkƭqkƭqkƭqk1ī}XuW,nר/1~MHXXx8c[k-!B}o]R4AgϞY\\ܹkiiuNgnOa8tZJEVB@)58h|R9W, =3::zGԍs(CtC:תtB/'?9pw,,,j p, RJ! d{zq#"k۶Wէo~yXH5*^k Wr{m=ϝ;}l6lۆeYBhu$Z BJ)"|5ߩZK)Fa)H׃8G&''vnn7~(DԊZQOzɓvsJ)8˲"qE"c @ض y ( p]mg,\k0 !z: A CGK)cB^' eՖ-[޽{׿t޹lfWUP|+oŋXkyTEX)DP@ZERJB@)d:Ih#Ntt݆JXq, Pna.j{:=ME OV*?qh0 BJ%LLL`||\e8!8hJ \1h?Fvh}z= qVGNz/2f7V|dVUJ q ܤy @1> FJ$Jqv ڇw]hn+(BHain;<; rP ܤ?878BA`׮]b֭\.>hհrdME:JZ&\a"")DEy?Z/:u WU no}G>>==}_Ղ:H9͛7c֭P(dͿ%|??y۶i&!DESܦ<\><#SW uQTRJRqw:hxxxAp̙3j7| @.|/?\YYDZkZu{m&R9P3b 0Hf6@Ǡ+z #:^\ ta Bk{͛#W3X۷}R^B(۷oRJz\4Nsp P|?$!2bJB}F/\4,D~_WU+_o_xq78$Z8p p.v]Wyz۶m" C@g : Ѝp` -H$(;|,fH1G )E뺢X,\BvCqv}… 4ޟRdz5}Ct:B0"{xxX}A 3Ing1) C)L}Fc,if(Bk.*eYSwuׇΟ?"nhz׻>wN*(͛7 ˲DZ[bZ+yJ@oNgia@jJH^<H pGXVBeY]v}/q T7!J##+NG}ev*}hg { @xv۲,KGQvءeL7/) z^ aqB rME"%}m{P B zB@keYֺ<;;^zo_+HDw[[uݲRGQvܩgggE\/Y pAfnz7LP(d/3M§~ #+)QA)Rm۶pٯ7^N%1555O}Q)aE֎;;M&WlYր1|eYEm2x\)̔g\|ώ`YJ(u]7cbVi)nu"v^w!I {+RO~Z8a333z݂%> <YLQ`=?Pmm`aaKKKv}Ad"t:t:Pvjk"^z^OP^_*f7o<3|^Ip>ݻykIqu^/&g)vx\\Js}8FG^Y* i^Y4))pyRn^Zkj_J)vé{/>}^7R@o}{NK)UXwy'n6 )3/fQ9}~V+VN(!%N"!J/!Ro$t2D$[̙3߬ו(z%Z+'%I˵ڻw.J,d(U h?=&̂N<0mkYxxUJNv;Bˈ9HLd1͛w*V$qя~?̼aZv޸q0CܕBG0k۶ `e@<'+'a { PML%y(#z}{0 ZvhdddV)ɓ' 8;v nWq,{nq&o nmy\x~|8saee%s='q Q:xٚ<=R +ٖ x;vv۾Pp=@"Mc_ qGBk޽(%y4YQfoOb[^z%,//gs Sw8@AG.vS(dt/X t) BRT: {YSs=~533/i68. \JQō.nX\\WULOOgu}d7[a$R`ii KKKY׏m8qZ,JK*(~ b*0e. * *J`Me@RJQV= `!cٲ{yj@x7lY.qyހ%hܟ^p7 D+jHy§ Raaaa… myDg Tej |HJLރ2~ک~9󨒥k bZEj2=p]w*=o " X@-ذaC&|NJ{d.^Gyo}[3+Nм?NP `&Hv:9$^RZ˲tzϬR4b3R,ऐ8BJɗs\/1ȕɌ&5dN 6g%+})J4::'}7P0:>'[1&ХJM)><6%\.I8r&''{ 5tb1>uL \.t ROg쥔6)R"/mnB%<2[ۜ<ή N@VVTcksrO""TU<䓘C\ΊK&.Jf5QC~VHOKǺk,j*Rar׽)۶mۂ--GsrT*e<:[^&51%''p|IhZ ES lC(Bd. 1?O˲yRt44\ Xa֫η-tw<],]ILaM0F@9OxӶKz= b USA1P*@w27)#c!A(ҿ @sϞ=-oyްapTҥRIeTUJM+^˽ ! HdAY9~!WR'ŕ GpΝ}{)ekӦMzͨVSdwjaaS/]sA籏ky ]8\xB2Z .\2*J?ފFހ{@N򀼪jxFZR7vmJaP}sĉbvT*䤵sN=55%ϻ\x1[0 | 渜RsohIPz }ǩS[cB xoICZ6@(џcǎdX,Z7nwqj"śSDp~s׭[=S7nھL ])<^~%T NJ FpGGG'\ ժڴiS&t74X޾s:5LgygynkEOR`~~>(85ϐ7Y @J}^/|O~ť<صkׇ=4.QT ~K%_KWtyk~ \P ɏM#P3 Fuo11170"tR.,,x9$2 p @lbQh۶͛Nqp-4O7'/*g\<L湭Mhй>}<[%<wdF!VVFiaj۶Ǐaj?ɬ(ؾ8QPJ'_K] "Hz>k)?]NܘUQ}ݗU Aǣ2fӿÇ'$x!+RI۶EEխ|)r_ OA׺Nny0MiHZ\\*D417DbQCi}M6]` P(JAS{H)ֺ\z#p(B1{L 8Z1ЌyB_ 󘟷<,yxl7z'z,mVT0J)y??|]5 6?>u5/[Qn6 /k׮}qǂDZ`]@ Mb#y BT>|xq֭;ժN`6S>Ԍs ȱ-&q"(V"F 'k8HCB'xq˲j&Eo3qǜ FD2M㜂YsZu*wXӧOZZJ)ֱy֣>zsܗF@@G5OuڔA:3Ϝ P(QD"2>'Ww]N~_GQ$87SOjsAH%2<@yɏINEF8}4jl֖e !D\,Ջ/X?jSs!@'!hQZ^YYp?=zti~~>t:V߷(iHY+vEa Hfy7-I,k'bUȣJC厛 n=zZ i˲R*. "38uTY}&z\ 0:i)b?Zk-z~'x'ZTjHZŊ뺞Ron|ߧJVd ٜSmڸ§59ƠlÜoGA4A,cQQ\(O?vxgD8c)%gzСRJ+8Y=|WHKO0$t2B828Fqhг LŋO|oZGQDBK)LHS^'<ʧJty\~lt̵¡wcddD#-jyϾ#XJiqZCI@CL1b֡Nc ¦MJ+Fm6tTH:Z`LVE1'g ײtE:HM/Y;mϹspAr]eŮV~衇R8H=zJpձ= 'p 76S##ǎ;-[{NRotcRigz͚f8q.^8{R:tGxmR~wυ*G_BzZk"%$&VW'݇Rߓ AI}3]*Sy*n3`H+sJRֆ0\0DƱcDz'qiZ*O'On64P~n<66&=ϋ/|'?#R0&6(71+j(W<੣SN_f===߷wޱrL `D ɜvEVJl6 {_݃+'h'O̪tTPH+zsChz Z)%\ՎsJJq\7N<Uy9+HRJ1 {l$ RPPK_BPz=ڵklttTA)%RX一xPh4v [],Ji8L}KKKx/y&1([L"Ze ۶yzjjJ;v—oBZ9gJAYY)EI. 0D JPH M͍oٲ6::T6'@?F1R=K)շkZVt!}&hցͩ]]m z0-9qRJ?< pIJ^k}^W=:/dֺU0 `$j*B*d[UݲwJEI)cqD(Q 5 Ak-D6Q#wٴQohz8z` CR(teYVryy<_Hth:vZ¿Gů%q ssEu 0D#xB'tXٵkΩԊ+I$epl`02*\$lOyd@)%R,.JJ{|fBI)曀7|\q=`:RbU za$᠊U%( ͛7oR)&e >$Pd7SC838W5҅BA{nCBRH{Jˊ^!JBE$'EWEfТ-*n۶mۖR;m[ӔLe '9@|Y))~z-&NMwo=_7]˲ a&"PMОUfo>344VJ'd>9\ LA1P,"% P@gu MxBS)\pĉ~A }F'zu> amx' v]}o}vÆ #b1Gǔ>RO#DP-."ϴV/ѼcM˲8ͦ>x0l Cp-E""|( b ZkR,"/&)Y$p6mڴaϞ=۷o߾ybbBZPJŴb1D}ADZ?c- D" )N8~G}~бcN#J;Y5W.Nk7RU.b (EP"8;w~[FժMKQu2һ8 u8HOZ^^O{&8YɕgfC_Q7^7It#eWS (Ц<E!Y9Fr4o|||x˖-fff'Gju]?"Erny " F!tRڸU!I)/nFO07~4w4,Zkꃠٸ|Vm yV`Z2 5>Tj:}B)GěRR]Y+WY0|:9-Hq*|PٓqiWR$tz_?o!{.|B\Aw\9n&7P}Īs¥J@Y@+pimàDyn q:nF.8rܿL%qe@7qo0n>oܬ @+]% <<7i @fZv^?-^MᯅLhf7x($@.`Rq\ XwBDR0JJo:kE [8X}n7C% Uc)e58ozZW>py_kL|:)@0->xf}kƭqkl?3F\IENDB`ukui-screensaver/src/assets/weather/58.png0000664000175000017500000003546415172041035017477 0ustar fengfengPNG  IHDR>a IDATxwŕ6VuqMЌr` b kp5^wl/w5g#$Q$$F8A3pgnTqjZ}'h$!֜禾uT=E:;VH|޶m[}<I$ucD"444C'  fzlݺu۷3t۶k ! R?s9'l?}WM$'x▦ 8%!! 2EQ!(BEPUB!Bm۰, mqrD P8D֜{?׸}aÆs-[veYWTT 2]יp FQ\\L" TUcpe0 d2R)L&NamٶMhWW~w5|G* @z[lϪ*RZZjNt]#!CQ眃ʟW۶a$8H0MٶMa-W_}97W Ds=ݽ{-, P(΋Q__O*++  /ī俷, T dYXeR)#}5\{%,_)N$(T*u}mm H0Ǐ' y$>̗ .i$ R)id2n,1MӶ,K촯꿹W"qa0]>⤦s}rߪ5t]׃ F|ڴi:Tg,si#L4MX5 ir0mcr-7oގ EnWD@<\~&MHqq15k)..c,xAԾ|8o&l>R!sLl;t'[VVu@III{YYӧ` BO}jllT0 Ϟ=DQ0@)|4lc, ===H$pi˲i_$W0ɀmR3 +//w@"__q(MDH,Coo:V__ϛ?nںC8 M5k֕hjfkC|/"Y-97J 3eeeNo> mkAۋx<)4Lj{/~5;w|ڴi8(5Mi8~b  ($[/))AMMM]{~6ߛj 8"| B)R eeeVww)=_/I+++gsΉ(Tu8HHxe;> BH޷Ppy\p3„MpaII 2%}]wNO)xIIɔH$R9gs"B0 1ᝮBK,_&5vB B '1`0dgSӱ*'N4p8ͫMZV~`B m"B?ι(u(祋JAsFO?j)/^C(Eܱc->~Ik BH)ܲ,咮<(@Wh@NVVTT?GW1>7z 1yپ}3gv%TIJiq}}\EQh"Դe!#~6$s z~G}^ƋTΑA$"ضmVl۶h;|pX%V]]ɫjܹs/,)ha躞_JiȶA&ǫY(tyJhy#B(N;͛7wLyѸKt"%+^gɑp$z4 1m|B'!a8ڵk!];sq4f,C8fhT)--#b`! _ԇ2e_BJ}!Gw~}$M.dXMMM3M39>oii)BJKKh4щ'ӧM|fAk&l d F`CI&oQD9EEEᦦx<9k @)]8yUUUf(0gL8#392*!;T~+3F,KG+Ư{G0!غu"*8@'RUUUF0 TUU磪Bx~v  g*F*#UGB&qUH$ Uwvv]yŏ?xpP0iP}˖-ՙ@@I,!dPp" < /#>~4/xDQaHRHR" 5J1JiG4Y[[nĉ+= OoL5 &Pb1:|^^^NEG8x~ 1 #H2|ޚעx0 n,\Jf666>O~raccX~2y睷+RjGQuƌ|ҤIs>w/"={}#ڐ葳#ў/ǫ4\j6p7ZP2 C68q}7x]28F` xMM_W^ <qaΜ9PvOdۼ'$y "oez: l`pl0#_wՅt:u>h >gΜoQ]׭p8?>{m׉$n 82:@oX%H9z*|4>PΡ7Yl&a]y(䩫W^hѢp b )!z޼yWuoJii)/++#|=}n({ZDҨP>} s PO+z 9$rw4M#iiiG1`$P83f̸0n<6ozs/+Tyd*{ ߽y{YCɦ >ې m@up GjS. x("b ~BL*޵ &9 "1G(z;TCwfMIgNSVZ0s0J 07FPJiy$)<޾Р )[7QHRT~#P}*q/p$On]0 <+X*))9{{i @GUU X/ +!Կ eM ׏dBTFޓB9 ? ׻0~Z<`~4>Ϥf38Eǡy*49(J$lbΜ9!p6^.x;H{տwco(y`R_?y30_Oy I6uLӤ˗/_o߾(,(:cLG:!$gϞ`pɓ+ pc@~ݛe 勁srsvCtX^xG|b3l0@ C|%9g3 ĝ AZSSvM&LP)X!f @f,2es j(`feyMq^Wn .? nwC2 g~ /&+9Wq!:OۚL&Í l6beyn@»sDܯ]ff{ Ծe.2zaL&CRcYc-۷oKog9l*s 91@ٽ{wSSS@ PYRRRL!nǘSlfe{%6dݫ =6Pe_z#a AjPre2L&cE8NA)͌YmP]MHP`<?[o8N8 N !Ķm7L&3 wJD<={dr7!D4vKhp8A[eMCz#9C%K<^TTD"pw`'N@@/^xl:@^( {u@2.$2Զm"4 2=GpS9immŒ%KXtpL:{Y0!˳!0-b˼tie){} >-.ߺw[e v 00 G,+}핕***-r s_:y8Y^/'JL&+uif<:G!xNJ"CWEijjcB  [4U~g?uq%C6ž]xl~_DZrI#Du( .s9X,J)sB!:&d7Y#)lʇNbhtEQdi'"# ! `^ׯot<ofuh4iԶm'Z}}}jnnܒdfG)`Cj@˿O?+/f #9d.[Mɡ SrYͼ .={h4* oKLm\mSsOݵkׁP(?~|~^4ޓP(!p8^?ҥK7YfӍ8?L4 kE 6{Mk֬Y׷@H0D xc,kFsB 1s.Lk9՟&d92)tj8p82Jov(:uy/n.@J#pw}۷oʕ++'׌IDATO<}ԩˋB$G СC}oݵkW޽{wR/AqF"'NPW]]]ZRRG"rZ0 ܲe[nmp&LC A#+pI(Za'!I ?b&ঢ়y9\|隫.DWUUCKksK/O6Tނ94mԹmmm׬^IP 9 !rpҒH$ AMJ)e14L&c$ɁC9ݮ}rH;SUXIIIi4-F!M4qD"1 #5IUUM+mpfu)1wLe9($vgpvrߏƆ9  sO=pɼsnx_n{kͯ zq<fS,r "(I #MNq2 $+.T.0XRkҕ:8 gsWvN=%5 lܴGUg-8 y=0BG"F)@BFLG={{WXq)I%q,*M Es+\ffҧEџHjuV̚`7~J p\&YFBtMJ rpm0W5 rpܶ/~﫟Bld2((Onӝ}zMqyq?&\|(-b B>K  @ bFի;ӼRB  gܲדT"A8pDK)49M %OQ0ʯBb:S sU$@3*r$kmjp]NN=aXh c͙5h٧o{? gYmYN:7Э[lx}2!j \YjΝ;4 xtu___Xζ9gE0Ӷntط@sރ Ԛ0E T(6A|9P4>Ze5<u+Dee4MCWW4+*.%繤t/_lZcLBgwY~q,a0m ۶x뮫/^7nڃj_%O.ްwK+r*/)MYP0<r בR:Xduέ۫ōW\qNחCY/lqyi4lB!( Ҵu߾7SEQ][O]_^S{ V*iB/³ׯ[[ۗQd!Xx(9ؾ_Et2ΤS4A)5X549Lq?1X @=[O?pǶy0Ӧ^8^^! &w7q '[EE{!|}wvmY"I>Br'BX,Ɠm|o~Q?Fp$4L:]IE}AϙgoH3(Yy媒@/ -:shiy 'L EQ\tSiܲm1ƪbQ οWk$DP8T;BPJMX4ӿ{Z.4uD۶u7f4-J&8o?]X3UTVPŲQt,0 Z_^^4Ms8ymZ~!:#Tucvs%/Tq ?qv9kkSO?t6r89O۶*-)YIJBȡ.RpH3.\hA!,Ƙ9sYi4ZWWZKLi'-omW5%S)ӫ{m>Dr ;0xOO;#{ B&7U }o篼ܱLӲ,ej8tX 's&R]*&\O{\x9t sGqSJ&i7n܎o$G_LX7LZi*im=?Db|C9#xd 9n\esjm6㚮Scs^ IzǪǍ;; k0i~,~񕰭^ e_CO>lfș ---CܷN$:-)(#Ҟ.+-- 3 t,x3^wOjlsh+$be#iuH4øj鄉EJ6iӲzl.?,H־흛z|O?=[z4 9\ܞF(mJҌeYЗAtU/rU8wvv]q۽Gbrg *oڄb3d3Y2mULrBm߷g~(j;T*504e:XL&\ ~穯]Ϝ:eYN?X ~xG9oWWQXQQ.wmɴ(E@y0Y!d230DZTUU5ɼ `9re>§NJ_~y-!$9Y|yl|, ʵ맿q/sv< G\:4ͲҲ2^]UE,dPBQ\g)"Sh޽4m^CUxvygf9N&3;BymsyzRjem:tf>q5P8ொ"|yigisxҲ߈'ɹUP˖y;cdRW^EC|qկOE"ؐdS=Bi!]{vAYim} _q4jnbKYEՕU7LШ[W^}m3Oܬ_ e]X[Uɺ@ PUU]YZZYO{'P L`7|{0c,pݞSonn G,4Qd2USVVS _=F~&Ԛl>X ˶478"kϳ>Ϟ{YSSvhx{w_bEvqH *yz؛=){{{T?m>+.S&kkx"1K/WWlԿ͞=`gg)reqVcg׎FsK** qPyqWenIzX6ɥnkIUi陙l6F|7N\ꔿ)_]ɲX5MN_{W?OSSS/%7|Cg9PJ7pb좇z\Ϳ+' SW< 8kҚƭ^V|3 yqI F"(JSɤn= (9ye;::ްn#4reMY-e`#F6txk.PSc&:.{z& R{iw}:!RJP~ปl޹̴ 'Mh sα>SgLos Asӏ0۬(J1:8f tm﬙3O۶}ETVUs,ˈwv0=e[mPwʋ]pH&xmKۿ}FMp:"o!W WIih *>Xǰ Pķ?v65)駞bR/#7#{] i1;cx{~K.8C<֢KrV( hL5i/>xTUU5E I+Qh=?  u>gW6n1r +hIEVUW8H,ਪm{zhT%Z ̱ ,V{W}!N'x˞=,[^Ғհ, k;:j>Pyͭ555r`'m `F&ywx\#xc3_7k/vw,Ο/%'N[ǃ+//'奥\u5ޚnqR8L&ȅrhȘO_|n֤{6qӼ{Y7u>Pf0'LqviQQ(%in;H뮧{:;CS' F^uYe;]B-2Mf XI!nxi*EUeeeD2yyMQxIK'cD$  a<Jn|u`(ͤS 3M*9c~d8g߁]>.vwL6$ 'ڻ˻zE>ׯ UUd{{:l6 C .>VtRiP:@g_L:=H$49P;l(_[xĔN @w=(mpV\hq2̶gLH(EEE0YZVZtܣ{zB6s;vN:UQ[:vkQVVƋ9'T񞞝c`0eYTwg6'WUc RPgO^$cߜ~?+e2e?3xǒN=k}lntzt *ƈf>p@f#τRj&&0?f.`ǎ `̤i$5M d$~=L~Ҭ:i40 *c,9)Lxhtt^qeLf#>p#NIENDB`ukui-screensaver/src/assets/weather/36.png0000664000175000017500000004577015172041035017474 0ustar fengfengPNG  IHDR>a IDATxymUۻ w7K==ͣ [`xlVb05@ҋƴ@L33,,W`0&(ϖ-ɒ w:CGU{g,VZ{{oxz<=Oxz]OܘC{e|wؿ [?_x`otZUםW}$}r gWcyVy|Us|:IO)}7Ɨ_d2-z-;z{`]p8Ɠ>* p7~;>#)0ԯ«3x~qb.8_^*xǕ䡟_gxJ\$b\='1]UԔ ̠ĚӯaPh@sU?q͟㩢 owGO> ~9,nXs[_mJvoꟹ[?ͦOꩢ  __5Cubq}akAvPk6j.lo[\?7/ۯܴ XѿTQrww q\~{rmX 7b6l0 (;s)(? &_|/]wz|șramai}iqOx(w= l'.}W 7kޗ l\l/֯|#S;X?_,{t|۳F׍]Q[Sx&_TP4k,(7-;~U7+"~駿~u.p&hx+ȬOx|5[|7~ >K>xXח6[ksex-;`=^g~_  :DG&GOLqƳ]qgk~_w=?uF37|/:Ak|m}]}}/ @qw}KO}s7CkfplcDl=(*8CAi?gBpӛ˿eo)5RnnY//?홻 MFI;7ZT W}α \93^C`4O5]2:?c*';\WdW ĎDmQS4/;}Zm9Y~M [v<Jxl ֲdU.TL˓C=ȝ4m;(dTlVg/ 6:<&:887QC kP%5xu-ގ25d]qكll"lwbf< reb[Ⱦɷ gNuM 3aq#CvmB֞/P6\c NN<<tI@ԂvfWDv(A5_Dv`SnKvUuRv=޵O^IO =Oo^xW}`{5ruC;hАmlY; B pa`NsIv }'ʭ{Q? {Nj~bN"P@{?!*.ϓ~hx#hxȌ~` ZOr31z wgX)so 'ybǓ n?_/~ڳ?^ {ؗ`m?HV&; rs0́;UA6Ԭ2A! j 0ǾAHd* ư ^&xO @O­wᳶ񻯞c}oo(<n7;iQχ7|wozݣn:Go[, f|#+_ ]3ܱa(Gm)*(}U)((@%.>*RUJ nAT#T~~?&~s\~?>ry}wx޷9wMpQoSxdiOV@CKϽFfp ٍۘ+Nr@~嵗ޞ>u;5nF'(|<3ޫ A掕WGA (H_@ў]>]:w^8Ԝun m'{dIeur/x˭ O/'r0_mm6-DaU=֞}@l21<=֮:OѪWMU !)kM=]BV@ >f'4* r-oҀɃr4*GDnMo}W{W)-' ~헲MTua;`=GsU"@%H#(&eF .I 0;KĤlhne()xjdh`J@m m"23V*m/' < 0|ßՅ>׮P3X>f *Ll9 YAgQ[]Օ%\} ~E1P@#]o\E+amjH]C* 9@#P>sRO%q+>Ru֔?|{\}5hSEyWCY@$30U;Mq8(eP2Q)es\:DDݛ%StH6 uPU(ɭ(G%ui%|-9'O 8w~Y3VGD d WC]_;hs D*`W]ny-.ΗXY:, ` ˹ ѱŠV so R(X$Y(Y;b5EM.*A<8p"NYc $ CS$J09@-$PA "d6swMw U5 L*'n>sB#%궡nTL@‽ h2Q@ %A^A0jC @b*ǍH #z$I Tc0]Ơ<$P L Pg"NFZXhSd%fP:.~Jj@DJX2x sdDzq: v{S%4uAaSp;rTzUvp$Òmo@`[ }Tj)S DD!|j{E/(S 9D LD\Iʽ'S3±=E Y \Ԅ+ .v{Jۧf@ d3p 0 QUa}' x20kE h)L4ND 6 l=(NRp|pgP|NU֒ {)bbR}OG@?ϽkBNO'{))dR!8YeĢz(P4NS&)O=a\Ɏ 'f\`Fe9'|,^Sj' +0Zq'ZZt.Oa;n!LT ǔSLBA ܀C !i<5S=Ox< .^rw؁uSAGފ̃(BR^Vn>d S>Uܬʢp{KY8V14аg1S|# YB9jo|C=Tւ~G?>(|X(#|Y?7ԟЋ'v-z}ʳJ!f,qIq[G@c0VHXXy$l R'؅X*PԀ@%h;)ԌaWmPquw_}ky_o֞Lǒb~=w;_m(MS(w,k^!Kqpe a2s'ZlёW< ' ~zͱp;k4DAgb#ѹ3$5 @dn&XBxIJBc03np;P 3b~wvfQoCکn*%X?vߑfy luevֆ7<ߙdd(|;{ Нcq3)a'(ۑ7xY @}m)LҺ&լ᧪aLѵor%!@#Qe|NYy+$H~/ ]CfK;ffv&*RYcאy_'P0Kdo7ttm& U='/MX[(79)@<`'صb3^nt e{O8]w^ f$ˏ8 ֺr1Qx&=_}7]!ePm_fP_lN~psQh~頵SJT2nt`6x%V eJ'e=g|j5l }Wlݸf8x06ahȋ.yclpAt-M J.Հ\<:ƵuɊ5!g+%X)Bl|&(.'~HP./ntK[6Z7KQ 7oj%/+"Is|4#=cwŔW]}3q9\8mLr@u]xDj7^okY}N2fXԮsA)K#br4]g"H"]P?JZW*~O cW>W%3dg<| _nCEjimÀA\B)XQ]l.IT8)x%c`fZh+w gx=#]_L3L qD)匜B gԌ@v<Gia\f [B5o,ڨ4ls('d̏uB|-0x0xA79x\?D?^xva6N @M,qy {D'.J4m@ӣ7EZrf-;Fkg&X)C p  :U'BSS*e 7wfv=OU560`ٿ ̘ȑ`<:6}dd9S57q2N+X{h;+3 HY,Jx#}- 5K:VX}*@AP ulgJ픽@]8AWWCDOD=Mx4DP ,}]8Far[luBD]VCS:T*k,oc?!n\.}N="D@'ȅR9G\q]z,$J84axHE=ܲzGڏ~M!_vzXS p=ouٳkb!m<_/ y6˱~+Y t٘%@HKIG.MhBN8BvUtۏ 4zm©G-Hr5:q5؞b (*lc탔 Rkcsɥ?JQb <)@j˼3}6;s8~|ll-'7\U#Me6ɢxR: = 9z^u.Hd}h$A[f? e(e'MO4zHX`UhCGBz5 q~Iv63. MzY6W`GDQ T(!!:PDl0٘\_/4k ײhQm63@A - ,)]2ty_UU'JX@T6lOLE! -1sIJ=BR +IFԝw^B,Thhcd͚=jw>@G<@ؐ-k2Rir$5(ޥ$U+ʤȗaT32*CtrlQ:BRXƸYG o4$mbhI!#e13W&.d:BȎҴ5 ^<.W7?qLzq))AOy| 012[=uM{{Yɇ'b %Sdj&ƒ$yZRx#.QS&q 'ujbh徵JY~2mMۈ ҹSBO1ElkEȡTD4%p Tu&u۫(~vp7[F}SR4C6UuBD&D1Mwէ>}/D3MQbj#*]pZhP `SʖEFtX5]1RNH  :Te>*V!h7&A @ ~:TpUw}{5s /}o! fLT1D%GD#V۹t9 ~z#S!+DxfA<<LwZSgOc{1Y*SaAEإzI i^) إ N IDATu%(5*XB&' 1#l.d>_k"7pZ%gbgx sknsrp>)H 0Fp)TuJ,rР僚}yn3ʔg8kՎ]2O4ڹJgIq,?*V~ R4e_O]K IkO$=)E+K)LHBCH-M hg~ϗJnf^ 3nv'~eto.YTd T0#*!x1L/Ļ*r6>q3&L6aOS8@BWiB(;)zWRiw@K{ Jw%vHG(!YPT4JKJ8Nqs __,/vv[sn~8v]YKfv>M9˖f.$YM9  BUD^BɈ٪!"ynlQdeT#CMX@d"I˶̀;ȹWxĴ+GD/t5 @7IB&:~@ѐ6j:KQgOxJ]  ubٵ? bO*jm]\s)V & z-ũ燐 :i cUeL}@88 0%3x劦ojc䣑ڲT[jܰ93"c*2fTDTjCD goedR`KP6Fx!}afgrbze X̓f=+OxBjaW՚:. `Ew(:i z*WԹ VEr7_086tsCڪ:Or.'"g&RTnb%v{(Kn5CH#W_\94ك[ e8Οư :3IIH[%ۣ;'Iq)Dh**mk@T=뿿lIA]GlBSOQ†ŊqX2[{o[.oëZҡmnIpaa9fma`*MbtI)ܯtMrA5VT;6N|!,b`]'Pʐ8cUFNalu]pQ@*9;֫4ijhNdGpm/dzͪRvEu{4I&-_4Adgӯ݀- <ȊP06y)dW@yhA]jH""ndaE:~WL @W^^am!J)3؅zz@ JFo,#taU[ƍRyHՒ6 | Ϙb?lR&_X;:n7>H_4xDkm]K y|6hgCmElKn@!k61/jjC!By/n94ʕ,{)vr̛(gEaUXr1s *Ŕczt=@iMtу h+Poj 'jEvm3ohIZͣ.ك\?8~{Tāhnsё?}I-{o|ꮻwA}5 õk͍od%ekGv9j? -5k{{RT,WGLu֍=yqcrÔXm;t-\K*P\˗ –~wSx JuWmƭxo:OK'4vd(x+& kf4"^%FUwhl{V%byP#V7bs?k]Uemxfllpr`+G7;jnӵ`k 2 >Aqj"aaw$$^?gGҔQPw)d!" $N N0@ tր,AgVݔ+"bcכr*&[VOL }]圠K[Sv)7Z\E G;YvomhT-qm6l/e/?1NYV:1Asn?I4%+ΕS_~$YpbRHdɭ'Ԟj6sTC%ɷPqCnX,$l12eIds"B9 "&cj4(`b S |,m{?jj%_C UEmٴuf -zV7w 5S@΢[N ݊HȀХ#X5C]}Q62 t%/Ie*p;.92xi[Yf&c>U9s7,Bh@M,cUz3 3?aPD -HGB\5F%N?@AV]=1l IjNM"bCOS6*`Ԁ_B@=,]D@7uTaX:[.[;XS(- F庮(UmAђ̼`湈"؋[}FY7yZӊsD֒5e b⯺%w6K UC8[I6w "G,/WtqFM@ѭWݭ؛MChJkJ mYJ\ 3h,ڰeܒ_f˗˼s[kw28 d{H9砺DlgTh:%c yŜAaUQ Soase %?Ca(Qu洆zbQ2!.ɵ3XkIl*HP="'f6asdQ nKvjdVV9]>ĐZRA\p դ| 1:ywhԌ:ڿdB('@dY G~"rR'O]$ƾj50SVi 'H(\uu /+[*6l@5Hk ͕ib[\ʈRXAD(3C=$4h/ˇH:Z fOKۭS' 6}KzBOr4?~db ֺa 䘤!4PWZW NxCT컂dzۀ q)P5Pm@RCقz2l@DJji2~HQ<$]%6ػ GP nGPc,>nú}@Qn͖JD#v9-6'!jʒE@}rBT]ѹa*vZy[Qx…k&5: - Ģ®Q<\*}Y:#9EA1怔I"HITg*e:|9j]SFABV}Cb\(j%Zx8rv[lZI5g]Z.ޯᲣ=OP`Ll J e1ܲ8q%q]* Z^ҩ_檶BJR2i؁aM#*V !.TDь4!clΰ*WG(5 nm' Q Hɰ-6cZiW,lLb$wиAw$ fҩ?; u $$0V$kʒDTeRr"# 3U)K=` ".'_h+?4rfUI]#Edj7a*e:bӜyn|ia3QQZ3f.2+PdY(nܳ`I F>"&Ƿw2&ifjJSh;"k3Al/sNbqUc+o_񻏙ƞ Q,PY|Ո]Md/|vؗ?%+gDibI&U-]rrʒfͣB?u!qxa (@\UDDe!XCRETw"~d[{m L{nL(_~7fw޶(& GbUP5R#y\+hq G@2}O[-j1h8n t֞]R6ƪˏD:Bj8kU͝&L|uv\Q"U+kpG) FQ6$A1 4z{ JF%oҮ=nĢnhjۉ9FNa.}m'Ɓ 3KOmC%ۋVYEB\R`@L fA2xGá{F,u*0*R"_Cb bH9]yWqyX]XOacĻ_i~0%*l1Hsřk 0% a Eش/~>*":r,3W"̱1&H"Pӷ[VW s=+"p\ԼX%3bm4 _*~? +6J,7PD18GtpzQL՛{/zW4&2X6E-_Ŗo}gR N1rpK@ij3f΅ k-`+3sq%V+lm/l)z Xтܸ:C\[  Vz +QLn| X'1?dMVYElVk8oփE+XBq\ylwjwK\v %16FF E{uP2@ẼB5}g:m>|/"j(`G(}vmRa`nƗ&_Xf[Od3*[Akb\i[a -rP5/<0! z<9/(N?`LDj#x tuxoevX]+tA0QB 3ګIs_x`W~`@k(Ȁ$Vkj>kmد @c./D@ra.=j 5|eANۈ>\%?CTFі"!@F0Ǿ C=&R`QoD \~uJ:3s ~jƔM'urĝ<e'^eC )Ơ.3\.HLjqyC2Qe, Z ~/k?q~ _"C~1U08JyW}Q;xx|Sg#tF ~-_XdnPn" X)&=>C6vcgN7_dʚ&vݤ NEk;A<1nQ$¸-ɤ6J 0ǝG#T6[ f#'?x=Fq>!RT^qup+4^[xg㉼Hu\Q_GQ>)~>FF=oLpo!hvߟ*&giI,N!qIENDB`ukui-screensaver/src/assets/weather/63.png0000664000175000017500000005327015172041035017466 0ustar fengfengPNG  IHDR>a IDATxw%E?}:t;3wrN0 bZӮaװ]}u"A0 wxs8CU=?ܾgΝ;0o}tWWzOh"D76N&Ka)c:F aQ.&AlKi}}-Y3g36h'Lxb cB3%3 yuSƀbO |$#S ̈h`3!z4"\11#“"Jh0%00fk8"P3 !r AE' hm!" Ar$r36Fah Zs`@qD~ $̡1& ! +&dáa#6JmW MwD "|y34gŠр&2߲,BRUA0A`68U+* &D2$"Cl&`˲@( #ga@kDEb3:D00!q %+M=ڐlXfQQh+2]"UJR Ce30Z)&~ R=Y1v6c)c 9llt,1Z)V*Qy 92PX0wu!c *м+8%wABFc&Ma@RR8[_ A#E*4Ogar  e\L> J+ֵMMYh1R&?f9,ۆR NH_H2L֥0[.60ʳ#;s.=BhF +elds]s-S:~E{1LDt+w+<&.d MQ,hm೟]0䓛pgqI Ų Аq>NZ9~dg)?wГ{~/ğ^Ɔ7}O olR{mA #dYD dljh wPp.r.gsggs.x13|y0_΋f-}Xlvl-Ƭ4ٺcf&F+ͻ^^;?iZZ :u6}E1oƋF 0W j)~|_ !$@V;յ?X`  }`,fRB/w qCtNk%fiϞ>jjBS3bQ,ز 6m+ᵭ>xcWzJ-k7__p2,oG& =;2y W}KJ" )nO~*!H$ , ]B䁈P(ل{vAXtigT4Ɋ3QAaά&, "† {2 ضs}#%lt>7cOB`bΨm[OٸgW[>dhFN&Z. Q g%_ggmZqE_g3nX Px9aA~;cWMOn1BqιKB>K/?AJ+J'43Je iwTߨ o@ $LArؼ@-BBww/Z[̐R`Lւ MMT%S14T~0#6+GjaI%FcB\Sq^n"[@EL4b$0Έ8Bdގ)hnhi@O=ػwϝo<;C^t?}E&cKp98XnټF"lh٧ +$i QW%A{o5ID<[Wgx4h}`N?̌kW?V!c5ogQ^BpG݄g]IǡA% S, ~˵hj+GʫVu-|ڛbva8*OqR@R1w&u!}h0J_?(}efgm _Тw,yyu_'?&Rʈt{*-gZM;7k{;~6eѬu~(,"? P[K5@[Iul֎)ض}X 47g1sV ^u=J?3ޮ`.^҉~_kPP(d@DXe V,uN F3,Kkǟ؂|ENo(c ؋|Sxn)?!˶ -1DMX7 ϡBCSssmG#Ы4 4OcD="G NH/ (5L]]{jbSWvXh;?}7̟? ҮW?-6>bP`Iboh B\x_G.\p˱O;F61Q4\C kCTq%pۖ$ڀR% Ur]t̖]?,KB)MaW^ٍn9NYAbU=1j c@@lR Hf+Z:a]U.9P‚]ӟ>}`ꗪe`~ɭ݃eJ !r> `c*.ת x׹Kq=Ƌ<QcJ`9VTnDBDR@XҒрŀ"LF}۶8L|'Vmw<χCCꪛL8$ =]rƍBF4-Ě?lRpK}[I--9KB_q,z-#;<; +u._qݯ qY5e tLtn!qs6hoWEDkS&8$E-mrJW˒-a .=|s~P&r8j%@U `OQ` D`4ظ-Chmo_y&p]/qeco-o}2$}| VL_ڄV<[$H WK{뮗E6I+λ:0U2.FUg' -`,[r$Ɩ<xWq'T%=Gsk d2 iY nشl Ft̟cԍvbֈҡb]Xb)q'`Hoփz;N\];ᮻᤓq,̛7 coFoL{Z;;W* oC0N%A(K83`9կ W_}|?ą.BP DuǪ CG*.! aTJ>`ݣq9 pNF*ض{Y[dd.,[RZKz!XX°6 "N@E*\KՈXP"Aߺ  D`O0X"!qǟz<ɣAsKq,WX\,z1x;PCJke N8vƆ@V<8/E%J"GJ8=B\_NE틸;֛x^fFSSSNŦ'_Ƨ,ӛW_s20= ً&`HKBZlUFΜ̤&h2]Pd3!^hZ93487IP8.yhw5T;\G f/'xṭ8m xz`DlɪTPuys3AR 8%,K0GN+8LVDARړѐK)9! <𗟻{Ƥ)1wi'?0lۂm[Jy8bS01xZvb-SaY@bI ƄmvFG, J%8~{я?p]Nd1@b`B!, 1DZ02㓟7Q.V] @iEKפ%`Ydݎ# u+2LHY"bA\vb7"IxH270vBD9p){uRn9h=8 MM( !9u>n{Ƃw,^xhtaJK(}}yFDvR*c9+`Q* 1UC5C;w׿^[v8qձ8ELGF#I $a}O)aJBL\Id b5p*(ZB& Xख़1iz-Ċ_m@c!,B|L_0/4v`/q( jŬo Aiis _ApaFs[Oy_ xR'n'$* ]$[_-P*`z[@R K_]hWpZ@@² x+p5+hQ Qd09?crz (m267*{[S&X#03i-"z8"9IF 8:&n:g3a5An#&L[<fNV"th:reOCTVt(W<=@q\$.ܸp KXwhҶ m ! iJ;J2*B /+07뢥-iy'>quF%RAN~2T"@5Y$v&Ӥ$HĔ{P|k+xR-Dn[׷"#ּ%S\I|j T0C2)YgLdVJ˵d3&<7Rm-2/Wb2h89-&YA,$Et6NFeՀL&`ztR%5B(ZF(?) dBR_U*0Z# ^ _# a3+f Z Vl2CM(R^ | D|d\a“QL&hiq h D,3 ER GX@a/Vg&V 0M"r%[lX*R@V522,KYP~DQuq< \T0DCχ͠)\*Z 0FA6|_PTr)p>:e`Xh%3+K™Y̬lذ1Dgm kcB?;EsgAFس^-0!ј035H!TDQk".L8P%]bT A"oT&rbRP-QdCJlZ%`I{+$H}QW*eJ\ю)v>uIEH8tQj]UA!oIX'^2VN=o4\#" a68 gtCxra?!n̵ )Q'רY&k #JKG.Rķb$߂k&L8h"}T V54 baQ.P.\p/ohmm_bI&? 7 C% Pei.>")Pu lǝ `ZSP?)JԖU .ܶ#rNl. xb T%J<85I4a J"* * GAX{!7 vv>5gΜ/>zuppl]3$c&F7ƄLcd2¶mv]WL:lEmw25-UCr(1d10ߑJK1,D 9 t2`I?4SF;AaRJaH*(<묟5k/<̟ϛ7@<8G(Ӭ" `3Ik/Yc3fX*Ԯvnm{'^;*ďD[߇4.ܔrj)eI1ĕsn8>zԗVEdߵ=?iVKt R}qq\bao3eʔo^wu_CļjI ,qzܹlnnl6k)SP(iJuN?0Vu貯Q5Hzy}JVWz6MmJnYְmqmmۦ{… z>?x |ؓ&MZn69#2 g2@$+#@:ob\pz=ԻUUD&*i,!kɒ%z?Zv 'tkx@u֖N)%lۦL&C㌫g#!~2H'c ]VҖf"b?`NP^mJVgV ,pz衟8 oR#x.m|ɲ,ضL&˲E;Us=M&~ɴsrH)׵FW>ԂZUpj/]x꽓N>QTp4!8ꥻk׮<赶ar2¥6?=xiNMiG#IP%B8NkApHȯt$t"IΓp*="ްC_G0p<յhѢ< D\H:Z[x^P"=w&>1_JyHp; 7?N""JDrL&S< "ϽC?3ZRJT*O|:鳄x83~N鎧9?$-=$*N }%j= ԪZ2]g,6!Y(2eY1Ar۷oߖdϛ7}m!dzFX-"Ibm@k4N58AkASKzi1_ms<\.#~lB R |xCP< ]]];3k*V5i9Tobf<7o<4(= 0F U5peSI}V#$hT*TTC/ҁ{&DLBdM1 "_~}X͚5kmy a&ӔÐAHV&i/{I )Hk͈?(p-'~U%g8ZPX=8a$e&"k[߄+( !ʱxKqj̅}vv}6\" dDBsI)eCSSSsKKKkSSSsccc>,˲H,_|ԩS""uEKu\20]FI=7i{j< 1z9-j4j%ZDZ*a zgo喟03uH͝|cymMZz'laݽXDd'RAkߝj !.ٳ뮻a*h/!dkzI}iz1LJxF-lۮko$AaRB5v*ʁ DcGes/sҔO6thWܤv`q#s.E!mfad49֭{bɒ%VX1mxxX Z^KܴخÈϪAyޘ-kfMbz@Lh18 C 0D%\o(jG\ 3Z>Co_mD$bq 2a aDL>_*# lٙmKctOa.qɁ^zm 9sfȈff$6TvN8@VT*(U$ Ju$2 ǩ0RcBB)EDAfb p#܂iϞ=Xzk֬x|ˈD-@\?<3w@b]`MQO*v\HJ"R_W\qu-,JZkMƘj`) CGjh2JBHN?>s"0d"88NBD l Dt`&ZuGڶmU뮻:uj13I U 813<Þ={g}vC=tOOVA2oTT#$2dz׼s0=# B *<7*X+f8bzU.>Nvbb2 @`MZܦR RJaA\6m>o޼E ,7smmm l$)8$c?<<]vٺu;vl-J}+޲e[ly%ϷΜ9sΜ9s͜9sɓ[|>_(Fc``߹sW_}u뻆C Љ}y%Qkt"YWX'6AÈ?qRRl#NYv\s>^>p KߏSfbIDH>4Q!ʓuN ÔJun]Ϟ~ #F05q1eYmmm퓧Mjoiijs\.m[ !1AV*X, D`1&;gu,ADB0dz&5677 BmZP&"+(D{~Ii> Qί "HH צ B~Η?Z@ W$ pf`H&F?0˧^vπ GvV L ќhQ<:"e1VPMIiQb]%"BCM\/˽%`:VLXT0OL8$aXIdT$1 XQǛJD(lvi8&4pdhY۶m[{0'+z_ Ps9~(X e#Cb4`%$$)ΌWK5dca*8xC@*@XFn奵krf&ďactS+xKu~b(OdPA׺̥T'x*E̯|jp42{e 2xjj@d*̧L{Ꮬ9O/\|u@8Ih#F%8\$|tC~|̝^#@ F%IRwy<"s%W*J:ZWCgᨤ#nXyښJ٫Q;E"Jܿl.l~Ɵ*tWK==IV֊I ["A$t`>Tp`V .tw^{Uy̼%=Ȕy^J)O@0e]|>q΅ھq3ևdyuS!@!eKϧN=׈==R)YR { "p'_~{ˏ\x]ߺD!fNNo3qGM7}@8 [<ռڵ ]uͼtJZTxe9ET,o$\E5@oO jh }C(:qs^fBrBWB/P-5j0*a4No֩EV*PBifѪ>C&҆yyYuvw6:EG2S3sN?{S=&O?ƚ/kͷsbk>{7= /Wz~ Hđ$" =.]lAY؜$MI `Ӵ|gV<˒&K4̞`d,Cm @0E`Y˩Lmv씢d4;(4Ns\ QqsÁ =IDH;sL{Xyp=`z[қxw簴{N+e7lxudA ۞-֞5;8+Wc[R/!_1߷L{ʤC[2$m@z)cEضs:h޾]6v@d[(q$0`}q^|g4H͑K@oDҒ@]|ǻ0z6)V.'cv7 $/fK&r[%ݺ61$ }k ȲRMbZk$:a1xtKk@6Bk1CDF)KDS PKlV.K̚Rhh_u|a1&1 ܿ9((atrTnGe^-R6 rsRQit0 x0;vATjum23lټW^mYs)528uڲ~b>cfb ӴVCBXa"(1z4S^E$&:Mˌ1Fp!"|`a7 >ADt_k~+aN7"Č0S -ïCFUgj>bV` :Xsb{ bffP(LE (ˍqj㻺n^[҃F(UyaҤ~z4g?k B76Zgiݱ TFޝ=@iM?3f0ѐi:acK#CBte<]֙4}W7k޸<(ʗb/;C۞θ'~yuO/CI;_t@ٓfn4NenJ]%L=t;O'6hJbht)⯷w*~ߋ6Ϳ\ ؎uw@o b7l?ջ/\y:H!6w.3|% .+_?5'5O;طo_> *%T^PͿ.Ml{۳{nzۿE`\ض עl{_X_O#Xl&:6#rÇ}ԥՐ5xYe9Y-[W׿̜f! lbn~;QK [/9eӷRy<lȼMn=;c`!{6޽va^MG4yw-sVf܌a@9W,pwYNO:/#vmwg۷oJ@GdA}DK;+oFtɗ1{ˀE-$'5O9~ȇ}RXNLOKK'`|pvI-> ~mR(:X?ѽ`#Yfٹjݧ(x1vﺫ}GhVgIDATԤޕGU]Y2[2 aъu)ZVS~c+jJKԥV)VjAqb+ZDP)dL2wdtH}Lιsϣn!ѿ*,RB^wy:-QCd­bڒcScQ5:V+wm>a;wV`8c`v7f 9v?p >mbx`V;^$U˯ak# /-\ށ'LTNY2T/CFsSL͝I,*+[|eN2NOx=Ӧw(0i;(fyT@Wɫf" fLei`ǧv?JyVYL,# 2ͦX]g-c DO _y|[+xݥTc&4;"DBmwE|G˂Y]F&LÕw&, +!Ti,VCBVwYS67߹)Z {!hsa# hSq'$)i7܂νznJ"`W%Ϟ:&:-Ld45x{ᡲu_lPٜ+UDZ(#.{ΟAw^8)8%}*G2 w›x'`dd=xp|:w ˃^g1}K6/%6@xmы-ŀoJ%wyԙ3NKu-W<PSs]Ƃ_fš S4go٣אb0%wJn׮Ӧ!`Q0lou5eP%1K"@Nܯl 0,^"L+ɑBoZʍϙxiʁJ6,x0ʘXI6{ cPO`?-;wK}XiϼB%bF %'y^osvǣd8t?s$2v5'&u9Ē #@$2f%Y4S9dԐ;ILnѓv .efYX5 (с⍳g?V x]v&`+)'a1 T AO-):d-oĝwdע4,@x5Vn寰WefYcM0ÙGB%^q"023ߔl.$TOaV:l6JTU)DK#G;<"o<܃ŋmnwgʌ,XDhɎLMgā]7P[Gض\PCQ54*qs|NNh_l1ۜR0ƃgI|syʵ}_$'}}AWm^چgZͩ*-媲mE@I#{n;8JpeO,S;fCPWo9]u r:8lG9sE9evh PRaoiLt0vڗL6ʺl>+DgH3f 2|D̕MlBEٶ,)XUaac N4$q .2sXdG1]ﲤ egd.R&2yyKXq=6ynl45{G1@^]֖GLhY)@.B;tpMiC,<#B#É&0 y%e,Z :w9K[\Ջ4%Զ(lp›nu2GK TUm"?ޗz0%5ӞZ\fД N2g=. X餴 rFsfʼnLCJXW$zS˭\9<֯;'h=>[S_<PnWBm #Fx+*)=D8=x;f=ܢV~jQ Feŧk֙x~9dpf"7Ȑ!U*:}q8C2nvFQfnf;oʚ7okK.w$b_ C1.7]QҬRUTж!ÉzZ qY:C[.] ym8*G?>:siB-UesW?w/~*|@ dD7Ef}{KvNXRn,wZpɍUI eG+3>n")tnrE#1fMp 9NXXx}+9n Z€fX-Z?Kx%`p/п sdJ#ӞbGu^}3Y >Qaqó;;NZ.ikVUOS0ϹڵTq9D]1';xۗ 78P-eiZP`bp~?sqYS\h(th hSNe5(}$sGJ=ة~۶pQ.R-6t6-Z&!씝iLz| &~fkUx?=b*pRpnuj0c q|C^ h2A tW<]EJX̙dr8 DW*n@N=NPBz4吲ro.1I`mQ~9g_4XKol"[\|WAQJyr,(+@s0mksG>qJ_U?YT.>ln`(9Plqhv;7hUkX^0 jgR4w',@(4,* Fvᅑ*Mf#Ăiv&'T9֭|`c3{h#S,}/?ZB[z8+ŧd0Q!b.>WO;"ho`D92g.9R'L9d0t(C%yxxEH D,W˟ԑzIUU Y^ vzL3dfaܒ- M'h3 _NkѽN-o|E]6ժم,Z6Etվ[`6=Wg]Dff(cd ГS7t1m! ݃@L_<, GkT5Tt"*yj#P`Cx:?~ٕ zkXQ M[5{,+f\زz}E@zp{/~mԴ/̣op ǯ[owɒƲ7Im6;X ^7xp9RQFZ$:v(ZrCbx܎@طNܛ| \&~2 am XWw  Koߚ̶8dX1@08}H]\5hCH,V[4l)0j=Bo9,$Qʺ J6cMV}X̑#%*RW_K>dL"..V v0]\z_0@~p&zy&41/sLug"Zq_bEНBjm-Ҭ'C5ϘMrua8YI3Xp,}>}x /Y̮%;3YB@wADFԦ+)0T* X`vgwHgc@UG[J>_AEd2gUQjvϿ#Mw&~60IaUEeG%)}ukoY8zU;QASKXPM{:z/w:Ž0WTCQpl .n> ($%7O,XW`O0ˆv* R >2+]Vfԃ$f!X]ɼY0 PY~T6t ̵w0\-`[0)$x(Lj>0!a!*`9- ڥ h( #6Ҫ@ PUK0l}p2.!Z&4 `vղ /P?_0L$isIENDB`ukui-screensaver/src/assets/weather/51.png0000664000175000017500000005327015172041035017463 0ustar fengfengPNG  IHDR>a IDATxw%E?}:t;3wrN0 bZӮaװ]}u"A0 wxs8CU=?ܾgΝ;0o}tWWzOh"D76N&Ka)c:F aQ.&AlKi}}-Y3g36h'Lxb cB3%3 yuSƀbO |$#S ̈h`3!z4"\11#“"Jh0%00fk8"P3 !r AE' hm!" Ar$r36Fah Zs`@qD~ $̡1& ! +&dáa#6JmW MwD "|y34gŠр&2߲,BRUA0A`68U+* &D2$"Cl&`˲@( #ga@kDEb3:D00!q %+M=ڐlXfQQh+2]"UJR Ce30Z)&~ R=Y1v6c)c 9llt,1Z)V*Qy 92PX0wu!c *м+8%wABFc&Ma@RR8[_ A#E*4Ogar  e\L> J+ֵMMYh1R&?f9,ۆR NH_H2L֥0[.60ʳ#;s.=BhF +elds]s-S:~E{1LDt+w+<&.d MQ,hm೟]0䓛pgqI Ų Аq>NZ9~dg)?wГ{~/ğ^Ɔ7}O olR{mA #dYD dljh wPp.r.gsggs.x13|y0_΋f-}Xlvl-Ƭ4ٺcf&F+ͻ^^;?iZZ :u6}E1oƋF 0W j)~|_ !$@V;յ?X`  }`,fRB/w qCtNk%fiϞ>jjBS3bQ,ز 6m+ᵭ>xcWzJ-k7__p2,oG& =;2y W}KJ" )nO~*!H$ , ]B䁈P(ل{vAXtigT4Ɋ3QAaά&, "† {2 ضs}#%lt>7cOB`bΨm[OٸgW[>dhFN&Z. Q g%_ggmZqE_g3nX Px9aA~;cWMOn1BqιKB>K/?AJ+J'43Je iwTߨ o@ $LArؼ@-BBww/Z[̐R`Lւ MMT%S14T~0#6+GjaI%FcB\Sq^n"[@EL4b$0Έ8Bdގ)hnhi@O=ػwϝo<;C^t?}E&cKp98XnټF"lh٧ +$i QW%A{o5ID<[Wgx4h}`N?̌kW?V!c5ogQ^BpG݄g]IǡA% S, ~˵hj+GʫVu-|ڛbva8*OqR@R1w&u!}h0J_?(}efgm _Тw,yyu_'?&Rʈt{*-gZM;7k{;~6eѬu~(,"? P[K5@[Iul֎)ض}X 47g1sV ^u=J?3ޮ`.^҉~_kPP(d@DXe V,uN F3,Kkǟ؂|ENo(c ؋|Sxn)?!˶ -1DMX7 ϡBCSssmG#Ы4 4OcD="G NH/ (5L]]{jbSWvXh;?}7̟? ҮW?-6>bP`Iboh B\x_G.\p˱O;F61Q4\C kCTq%pۖ$ڀR% Ur]t̖]?,KB)MaW^ٍn9NYAbU=1j c@@lR Hf+Z:a]U.9P‚]ӟ>}`ꗪe`~ɭ݃eJ !r> `c*.ת x׹Kq=Ƌ<QcJ`9VTnDBDR@XҒрŀ"LF}۶8L|'Vmw<χCCꪛL8$ =]rƍBF4-Ě?lRpK}[I--9KB_q,z-#;<; +u._qݯ qY5e tLtn!qs6hoWEDkS&8$E-mrJW˒-a .=|s~P&r8j%@U `OQ` D`4ظ-Chmo_y&p]/qeco-o}2$}| VL_ڄV<[$H WK{뮗E6I+λ:0U2.FUg' -`,[r$Ɩ<xWq'T%=Gsk d2 iY nشl Ft̟cԍvbֈҡb]Xb)q'`Hoփz;N\];ᮻᤓq,̛7 coFoL{Z;;W* oC0N%A(K83`9կ W_}|?ą.BP DuǪ CG*.! aTJ>`ݣq9 pNF*ض{Y[dd.,[RZKz!XX°6 "N@E*\KՈXP"Aߺ  D`O0X"!qǟz<ɣAsKq,WX\,z1x;PCJke N8vƆ@V<8/E%J"GJ8=B\_NE틸;֛x^fFSSSNŦ'_Ƨ,ӛW_s20= ً&`HKBZlUFΜ̤&h2]Pd3!^hZ93487IP8.yhw5T;\G f/'xṭ8m xz`DlɪTPuys3AR 8%,K0GN+8LVDARړѐK)9! <𗟻{Ƥ)1wi'?0lۂm[Jy8bS01xZvb-SaY@bI ƄmvFG, J%8~{я?p]Nd1@b`B!, 1DZ02㓟7Q.V] @iEKפ%`Ydݎ# u+2LHY"bA\vb7"IxH270vBD9p){uRn9h=8 MM( !9u>n{Ƃw,^xhtaJK(}}yFDvR*c9+`Q* 1UC5C;w׿^[v8qձ8ELGF#I $a}O)aJBL\Id b5p*(ZB& Xख़1iz-Ċ_m@c!,B|L_0/4v`/q( jŬo Aiis _ApaFs[Oy_ xR'n'$* ]$[_-P*`z[@R K_]hWpZ@@² x+p5+hQ Qd09?crz (m267*{[S&X#03i-"z8"9IF 8:&n:g3a5An#&L[<fNV"th:reOCTVt(W<=@q\$.ܸp KXwhҶ m ! iJ;J2*B /+07뢥-iy'>quF%RAN~2T"@5Y$v&Ӥ$HĔ{P|k+xR-Dn[׷"#ּ%S\I|j T0C2)YgLdVJ˵d3&<7Rm-2/Wb2h89-&YA,$Et6NFeՀL&`ztR%5B(ZF(?) dBR_U*0Z# ^ _# a3+f Z Vl2CM(R^ | D|d\a“QL&hiq h D,3 ER GX@a/Vg&V 0M"r%[lX*R@V522,KYP~DQuq< \T0DCχ͠)\*Z 0FA6|_PTr)p>:e`Xh%3+K™Y̬lذ1Dgm kcB?;EsgAFس^-0!ј035H!TDQk".L8P%]bT A"oT&rbRP-QdCJlZ%`I{+$H}QW*eJ\ю)v>uIEH8tQj]UA!oIX'^2VN=o4\#" a68 gtCxra?!n̵ )Q'רY&k #JKG.Rķb$߂k&L8h"}T V54 baQ.P.\p/ohmm_bI&? 7 C% Pei.>")Pu lǝ `ZSP?)JԖU .ܶ#rNl. xb T%J<85I4a J"* * GAX{!7 vv>5gΜ/>zuppl]3$c&F7ƄLcd2¶mv]WL:lEmw25-UCr(1d10ߑJK1,D 9 t2`I?4SF;AaRJaH*(<묟5k/<̟ϛ7@<8G(Ӭ" `3Ik/Yc3fX*Ԯvnm{'^;*ďD[߇4.ܔrj)eI1ĕsn8>zԗVEdߵ=?iVKt R}qq\bao3eʔo^wu_CļjI ,qzܹlnnl6k)SP(iJuN?0Vu貯Q5Hzy}JVWz6MmJnYְmqmmۦ{… z>?x |ؓ&MZn69#2 g2@$+#@:ob\pz=ԻUUD&*i,!kɒ%z?Zv 'tkx@u֖N)%lۦL&C㌫g#!~2H'c ]VҖf"b?`NP^mJVgV ,pz衟8 oR#x.m|ɲ,ضL&˲E;Us=M&~ɴsrH)׵FW>ԂZUpj/]x꽓N>QTp4!8ꥻk׮<赶ar2¥6?=xiNMiG#IP%B8NkApHȯt$t"IΓp*="ްC_G0p<յhѢ< D\H:Z[x^P"=w&>1_JyHp; 7?N""JDrL&S< "ϽC?3ZRJT*O|:鳄x83~N鎧9?$-=$*N }%j= ԪZ2]g,6!Y(2eY1Ar۷oߖdϛ7}m!dzFX-"Ibm@k4N58AkASKzi1_ms<\.#~lB R |xCP< ]]];3k*V5i9Tobf<7o<4(= 0F U5peSI}V#$hT*TTC/ҁ{&DLBdM1 "_~}X͚5kmy a&ӔÐAHV&i/{I )Hk͈?(p-'~U%g8ZPX=8a$e&"k[߄+( !ʱxKqj̅}vv}6\" dDBsI)eCSSSsKKKkSSSsccc>,˲H,_|ԩS""uEKu\20]FI=7i{j< 1z9-j4j%ZDZ*a zgo喟03uH͝|cymMZz'laݽXDd'RAkߝj !.ٳ뮻a*h/!dkzI}iz1LJxF-lۮko$AaRB5v*ʁ DcGes/sҔO6thWܤv`q#s.E!mfad49֭{bɒ%VX1mxxX Z^KܴخÈϪAyޘ-kfMbz@Lh18 C 0D%\o(jG\ 3Z>Co_mD$bq 2a aDL>_*# lٙmKctOa.qɁ^zm 9sfȈff$6TvN8@VT*(U$ Ju$2 ǩ0RcBB)EDAfb p#܂iϞ=Xzk֬x|ˈD-@\?<3w@b]`MQO*v\HJ"R_W\qu-,JZkMƘj`) CGjh2JBHN?>s"0d"88NBD l Dt`&ZuGڶmU뮻:uj13I U 813<Þ={g}vC=tOOVA2oTT#$2dz׼s0=# B *<7*X+f8bzU.>Nvbb2 @`MZܦR RJaA\6m>o޼E ,7smmm l$)8$c?<<]vٺu;vl-J}+޲e[ly%ϷΜ9sΜ9s͜9sɓ[|>_(Fc``߹sW_}u뻆C Љ}y%Qkt"YWX'6AÈ?qRRl#NYv\s>^>p KߏSfbIDH>4Q!ʓuN ÔJun]Ϟ~ #F05q1eYmmm퓧Mjoiijs\.m[ !1AV*X, D`1&;gu,ADB0dz&5677 BmZP&"+(D{~Ii> Qί "HH צ B~Η?Z@ W$ pf`H&F?0˧^vπ GvV L ќhQ<:"e1VPMIiQb]%"BCM\/˽%`:VLXT0OL8$aXIdT$1 XQǛJD(lvi8&4pdhY۶m[{0'+z_ Ps9~(X e#Cb4`%$$)ΌWK5dca*8xC@*@XFn奵krf&ďactS+xKu~b(OdPA׺̥T'x*E̯|jp42{e 2xjj@d*̧L{Ꮬ9O/\|u@8Ih#F%8\$|tC~|̝^#@ F%IRwy<"s%W*J:ZWCgᨤ#nXyښJ٫Q;E"Jܿl.l~Ɵ*tWK==IV֊I ["A$t`>Tp`V .tw^{Uy̼%=Ȕy^J)O@0e]|>q΅ھq3ևdyuS!@!eKϧN=׈==R)YR { "p'_~{ˏ\x]ߺD!fNNo3qGM7}@8 [<ռڵ ]uͼtJZTxe9ET,o$\E5@oO jh }C(:qs^fBrBWB/P-5j0*a4No֩EV*PBifѪ>C&҆yyYuvw6:EG2S3sN?{S=&O?ƚ/kͷsbk>{7= /Wz~ Hđ$" =.]lAY؜$MI `Ӵ|gV<˒&K4̞`d,Cm @0E`Y˩Lmv씢d4;(4Ns\ QqsÁ =IDH;sL{Xyp=`z[қxw簴{N+e7lxudA ۞-֞5;8+Wc[R/!_1߷L{ʤC[2$m@z)cEضs:h޾]6v@d[(q$0`}q^|g4H͑K@oDҒ@]|ǻ0z6)V.'cv7 $/fK&r[%ݺ61$ }k ȲRMbZk$:a1xtKk@6Bk1CDF)KDS PKlV.K̚Rhh_u|a1&1 ܿ9((atrTnGe^-R6 rsRQit0 x0;vATjum23lټW^mYs)528uڲ~b>cfb ӴVCBXa"(1z4S^E$&:Mˌ1Fp!"|`a7 >ADt_k~+aN7"Č0S -ïCFUgj>bV` :Xsb{ bffP(LE (ˍqj㻺n^[҃F(UyaҤ~z4g?k B76Zgiݱ TFޝ=@iM?3f0ѐi:acK#CBte<]֙4}W7k޸<(ʗb/;C۞θ'~yuO/CI;_t@ٓfn4NenJ]%L=t;O'6hJbht)⯷w*~ߋ6Ϳ\ ؎uw@o b7l?ջ/\y:H!6w.3|% .+_?5'5O;طo_> *%T^PͿ.Ml{۳{nzۿE`\ض עl{_X_O#Xl&:6#rÇ}ԥՐ5xYe9Y-[W׿̜f! lbn~;QK [/9eӷRy<lȼMn=;c`!{6޽va^MG4yw-sVf܌a@9W,pwYNO:/#vmwg۷oJ@GdA}DK;+oFtɗ1{ˀE-$'5O9~ȇ}RXNLOKK'`|pvI-> ~mR(:X?ѽ`#Yfٹjݧ(x1vﺫ}GhVgIDATԤޕGU]Y2[2 aъu)ZVS~c+jJKԥV)VjAqb+ZDP)dL2wdtH}Lιsϣn!ѿ*,RB^wy:-QCd­bڒcScQ5:V+wm>a;wV`8c`v7f 9v?p >mbx`V;^$U˯ak# /-\ށ'LTNY2T/CFsSL͝I,*+[|eN2NOx=Ӧw(0i;(fyT@Wɫf" fLei`ǧv?JyVYL,# 2ͦX]g-c DO _y|[+xݥTc&4;"DBmwE|G˂Y]F&LÕw&, +!Ti,VCBVwYS67߹)Z {!hsa# hSq'$)i7܂νznJ"`W%Ϟ:&:-Ld45x{ᡲu_lPٜ+UDZ(#.{ΟAw^8)8%}*G2 w›x'`dd=xp|:w ˃^g1}K6/%6@xmы-ŀoJ%wyԙ3NKu-W<PSs]Ƃ_fš S4go٣אb0%wJn׮Ӧ!`Q0lou5eP%1K"@Nܯl 0,^"L+ɑBoZʍϙxiʁJ6,x0ʘXI6{ cPO`?-;wK}XiϼB%bF %'y^osvǣd8t?s$2v5'&u9Ē #@$2f%Y4S9dԐ;ILnѓv .efYX5 (с⍳g?V x]v&`+)'a1 T AO-):d-oĝwdע4,@x5Vn寰WefYcM0ÙGB%^q"023ߔl.$TOaV:l6JTU)DK#G;<"o<܃ŋmnwgʌ,XDhɎLMgā]7P[Gض\PCQ54*qs|NNh_l1ۜR0ƃgI|syʵ}_$'}}AWm^چgZͩ*-媲mE@I#{n;8JpeO,S;fCPWo9]u r:8lG9sE9evh PRaoiLt0vڗL6ʺl>+DgH3f 2|D̕MlBEٶ,)XUaac N4$q .2sXdG1]ﲤ egd.R&2yyKXq=6ynl45{G1@^]֖GLhY)@.B;tpMiC,<#B#É&0 y%e,Z :w9K[\Ջ4%Զ(lp›nu2GK TUm"?ޗz0%5ӞZ\fД N2g=. X餴 rFsfʼnLCJXW$zS˭\9<֯;'h=>[S_<PnWBm #Fx+*)=D8=x;f=ܢV~jQ Feŧk֙x~9dpf"7Ȑ!U*:}q8C2nvFQfnf;oʚ7okK.w$b_ C1.7]QҬRUTж!ÉzZ qY:C[.] ym8*G?>:siB-UesW?w/~*|@ dD7Ef}{KvNXRn,wZpɍUI eG+3>n")tnrE#1fMp 9NXXx}+9n Z€fX-Z?Kx%`p/п sdJ#ӞbGu^}3Y >Qaqó;;NZ.ikVUOS0ϹڵTq9D]1';xۗ 78P-eiZP`bp~?sqYS\h(th hSNe5(}$sGJ=ة~۶pQ.R-6t6-Z&!씝iLz| &~fkUx?=b*pRpnuj0c q|C^ h2A tW<]EJX̙dr8 DW*n@N=NPBz4吲ro.1I`mQ~9g_4XKol"[\|WAQJyr,(+@s0mksG>qJ_U?YT.>ln`(9Plqhv;7hUkX^0 jgR4w',@(4,* Fvᅑ*Mf#Ăiv&'T9֭|`c3{h#S,}/?ZB[z8+ŧd0Q!b.>WO;"ho`D92g.9R'L9d0t(C%yxxEH D,W˟ԑzIUU Y^ vzL3dfaܒ- M'h3 _NkѽN-o|E]6ժم,Z6Etվ[`6=Wg]Dff(cd ГS7t1m! ݃@L_<, GkT5Tt"*yj#P`Cx:?~ٕ zkXQ M[5{,+f\زz}E@zp{/~mԴ/̣op ǯ[owɒƲ7Im6;X ^7xp9RQFZ$:v(ZrCbx܎@طNܛ| \&~2 am XWw  Koߚ̶8dX1@08}H]\5hCH,V[4l)0j=Bo9,$Qʺ J6cMV}X̑#%*RW_K>dL"..V v0]\z_0@~p&zy&41/sLug"Zq_bEНBjm-Ҭ'C5ϘMrua8YI3Xp,}>}x /Y̮%;3YB@wADFԦ+)0T* X`vgwHgc@UG[J>_AEd2gUQjvϿ#Mw&~60IaUEeG%)}ukoY8zU;QASKXPM{:z/w:Ž0WTCQpl .n> ($%7O,XW`O0ˆv* R >2+]Vfԃ$f!X]ɼY0 PY~T6t ̵w0\-`[0)$x(Lj>0!a!*`9- ڥ h( #6Ҫ@ PUK0l}p2.!Z&4 `vղ /P?_0L$isIENDB`ukui-screensaver/src/assets/weather/0.png0000664000175000017500000004047715172041035017402 0ustar fengfengPNG  IHDR>a IDATxy%Gu';'rkRTUZ@b!L23v6^n@nnh<4"  VTUg̼yUTԟeyFD$|{=ߞoϷ|{=ߞoϷ|{zLv?:y?`fjޗvܷ<xK xxN;pMLC-_/|v>{@σ3W?`RI.y 뜵 }O;|N5GkoIϪ|z"3SԵ*ޞD*?3NX@m-pfīYxϧW̡}ekg{{`mn_?j?P_Wlx zo7E!/ϷV~ N q/?n3Gϩ1}tV`;fTEqFԺ~F썿-l#OX {^5p*H%#x:߬>;ob/~c@{>?s G|`&lԈ>]</+tw.Ѝ[gf.Rz 7mpj=VD.l4Kw|+5.Ӫ±Zu{G>+׮ş:K` Rzϐ?N%_o|?QomS$<*Txl؞#&}yVP"f"XϯoLs75oȧ+;18k_OGw} Lh7TMMn߄אP8Uzҽ+wnzvzCz7i\ ~0C@C<۳p\ȁObO?ޤʌ/AsQU6|_yO|k>W8bcWh* T~D]u:TϿBf*O߽wKO~K4~nTeBRJ*o߳>Q7&2ܖ5^z~+$!L\?Wco+bÉ'5!H#zhi1:OU=?xy0iQRa J-w]C  X73?k8IӷsL5|&UĪP<\5_K;1lmbpХC}ʺ.3U .5z\)(X}N_`HC`{_޼j5v=7o~y/5TEH%95DXSw]u۱˟^x $"2!)po&Em .yqc+]GL1@AAjb)yLD*@eS]**;]`dk`Mv8^fxb)<#c*YoJ>[x3߄F&PNx]gڲn&UImqD-.+!֫H]z-Ae:~(_:֬ 6Ugb `H_j\O6mPy.O֭ . 黟fL26߼joy g4߽k5ڟ+xcK`4"k <]y ^5R L  PX6jkm|<$'?A, [uϢ>~77A HƠ+'7%Ut} խÓ߇ TѢ}߹ҾUSo( 1C瑍Ώ zjj߫_߫~mK/[wD ϋt}c˯]e+RWInjb;ҿ E,b"V1{,Dĝb|}zGk T/YyGNtHp#)gX17@i ũ5a>$D,/@:PgINZ <(NKJA%_.ؓH.Dv0*T~,iԓH GN@)6ր;/G}^d`@"Cg;(\>6u}|T0a @ ,hГN?uQn>%Hh,7-%D$`[5SKqWP9IC3}M#.ݞ8̄}^w<~[ʒ23'((q,I4-- Pf0&YN @'FKhN+&!@J 9)SAF@b96F6@̤ٝCJrA򨣈MI %`{j;?NpsQb?$̜0DIoA VEOCdJ2"]+ F^dHWFT8{Bby HY(1L0?g}0 X,@B,{p9Y+`(I d Q>(J9͂E)S1:hB}GTX'`X0o^oQC#v&40{yzwl;/xebšĀl~(JܖftB"Y"AJZdF/C/_DO7kə@DT6|+{V^kP=/C #lx(?F_h#@*ܷ!#%rmf]wN ʎ3̧d!6I)PuW囃x7+~ڛEYR S,yJM.H 5LgqG|zn=z}C޸Uc͘q x1y fzs.] 7YIWXjR#;CY̜#1Ht^t ]@/$JeEт'IY>鶠wQcj;6-՝/hqr3ܶSyX DmGػf˃`G j 5Cxu6$V1 ` [JHu7 i>s?^"(2}>b` .R"g3r 8f7MEFyPgmT8);FA Aw тdI!i+>t iVzP?t'[ȁÝ12U{Ǐ#Wն*"Y"T7V? /7BpmC*9#B8~u?f"Vg} GW@d} Ɓl+HZ IJLa1l"D {[Aw\is6km@#{~ߑd%Ml z"~AjbMv@MU_<ΠJ#|˝.gTՒ/?H }ЇM%O1 :>ۗ9| Ab}azQ}0LD1CHt)^8 >WW6/anfl6orū yְoK%!TAD`¥_b 햙7^#r4$>(l+{#)gFV3NS2o #,(u  U@ &"@w>mzyŷ]`0:mu1@@O\֖=~ =B|8g.2s}!Ri9%0䭬 ʜ_Š ]G ΑJ-~8? ?#y ǫ. TdT<"lԀ*-%gց{y%@EuZ V*GT}-a\7C1[W_e^'D{&U_︟SnO#2ϭF|) <ď9A* 2^?@\s=cB):8'a wyNQOR@{i8EZ\@ 3P=(:};$6f1$yDG5pJ^Bڥq z(}/lȈ[N=QD7v3`6~M)Ul1*mG+Ce$A3ܶpDZl"GAb#)p8Ju\1h m9̹+!^F2\^jRώi&gmY׾E4D- u)׿C%%&$R%}r2#9=gG9V#$AY ?/zp!2O%zpe:s57],T{YtKb[0pSϬn3F,D`,%[JH|j%7WOr}[j[ 6tyYy«0D:U G,GX5Y;Sd2-zӯ}g/2Ycd+ 7MHF`VrI W| FKS9= cU1˜\O?V @AF@C`  Ē@\L+ʥM.3ZٟxEC_=yz n(ᅿH\HbG *jyF G'ߥo'Ju>E}% sr*bK# `kM*tN,tK!"X39T?szhn }5t"7_-pyuOMux~`E^+ @p=t`6P%̸7)'GV )#K/m﹚N'v`ulڰqym͒6i{6KhΠvk~-k~曧KS@˷7?{ojq.~WKl؈ʮ7?s`_(͉_YDL;n!et {Зသ|H|m703j2%->,ECE% @=opk9$b#TJ>"iN!Q2Œ,P0"2R2߽z|l`yl+kBe1l.C E9fԟW{2 ǾnYMG3yn,` h@!'}x@&${n$e!T;:`bݜ @t 3P|e@$ gRa50#wC_bIܤ"nLFjSo wS#v0 Q&v?J"X > ARA1_4PIL +F }O5'p#/T(5xb-zpxm.Rj=zL?p ,JQ#[cz*T) l wLW^|o_SBB>H \4S\$0 $p9˜] + 3\"E "1-s/W#wA%W ँ(kBQ]M*^D@b}"`%D2GڈH_OM%  Ii /bV.g>P܌3B : )1˰[+4z>#E1?F]CK^Bk[<2Ϯ.EDݏ`m5 NX8[{-"Ko޴sO 7wA SXA3QAu1* saW$Nvb ? َd ٿՀP(ŭF>?DL@ī6Ѧw6*XbDZpvW580#[Wrᆘ)~sy/'ol=HE`E08/n>=ny{g%EWE F]8ŭd|$XN5.m2dc&pkhؘa":U/M[X#ᳶV Pn jLM5*7U @2I@Cl ʠg,$3Ez|䪨(RXQ$jQߖgYp2}'!%qJS@"fo_}'M$fN=;~OOoV5fC&("ֶIrtydά^hn"YIl b_L.r-mR>Οv1n2$ɡbɫx Q41m_JGL%u.cxuTh[FD 1DRd( lǥEWv. ì+q}=ʞb-m`rN|񃟯7&Q6f}&gbb ۛqb_@>`v0KYe|<%._Ê:j-@1M;f.BJ:t I@ٶ!Q~CrE qzŦH"E01tuwzhr\ r,~o_ObBW>PBZV`r+ QX(h0nX}\M?Z2=L=6alr"o2gݶGĊ'0L]IMB}ep9 @딪c^r4#]FŽMB/KI &G@@6KyNX$AEi]v5~`TpL`L`$N7]@Wic CL襥J<|d%O>gk3f @[HwCkc|rTU#'0`| YͰ p(N@9eLzgG!D,&rQP#(߫,J , ]̐٬ڿzH y 쐻qޯг" po] $X6:&L%p` נ*r{K 5 ,AZ3 \'7Aϻ;<ի<=a@9 `dKqI˽ưI2IM+BD,nlӉq ]9QYF3I;Ci > PTU :[Tl-ḭ̄ATX\_@nݿ 'bhwQLd4r Ղj>TV\eU 1"ggF53Y,ڊP3'y%ChV1p*ćXrf J  l$","*{c(fVQujED1A^ 3]05g#x6$bF = mew8]oؾi ֩0#@IXlNfkt[ NL!"+q  H`@V `w/6PF^2#ٷziZ@7F,,E|V  T% 3 CTn1 _W|M`߁!x˙,Ȭ6}dY>\Iw҂ei $ nr+Bnh!b7iD,R ZXk,EZE ?9=HԂ9Ud Ub f‹> ``TizO(xVQ@O4axX DgI'S%׬C7v ] @,\I%Ag{- TY%%|.mO1%.Rb`ۂ[ac&Erk3Һ#٩)^GL+]Ŗ8i3lBb=,xGZڦ)^5U^3޸!Nd]\5*Ȃ_YHQkPI9zE:ɒGт_T~I?+botsܟ8S{῝{rM7U6ɓD 3i& :"đ@z:DAz{ +WݴVRýp%~(UT[+0Lv"],| (@]|~/xc>-GB48(*T BWq`!J l(1jնMk099jo*1CZ'擃s vr7QlF$@Ȼu3Uۨe РŞŌ[xY;@ā[N/l>ˤdɜQ-)GX[s[;`šqZQnj23o WPeM׿8c0 ~<ě7/sСc_X"q_/UAZ ٴ!bGSTP.BAȹ|yj%>]IR<[Η%6*`H*CE-6OX;_؁ݞH;=@Tju6W^nw\!| fOùgA\I ޽o<HH:@5F <XQYw*LC(rA3k Np1 $B(1$I$})yؔg8L{nb:J>uYAq ܼb/96w yΌ3h\s0;9@Ym@,t;Ly!al)pHPfͱ6oK3VU 5."&DI۽9p&b;alf X[uB`"@R{c,W<_c $2 9S "[c-4Xj;W>[  .Kf^~'xwl6޳W٬{dM<h.S"LH:l%夊M#j} "@H#u6̀%i=C MW;tmaqoNOF=ؾc+.+tHLDëBc.KK;,BֳxE} yvoË-zM#~MX,Z!1qIR:c׹d]@)d׈gC$xv@XJ1,* b{׵Kc 3 Y'HQ%I:lhAa螇-ݼK[_ŕ=cjf!$_2f.Tng ?4ҝv/74ԅU}D3 z2 1vEzxz3bmvm1s%G-C%XK"R#@n.$ʼn3{ǃwx1zj@|=_f޵E[X K`AY{^ $ud/=~ ~|ʶA3aį &+GI]"nBGea!]^xz&ipisLDZMH2+) ql٘luKYzX~HK'.'VX5w~[8㭤(^V6Y9[Jly@H:w>£&Җ},Qo`jK,Z3Ɛ{&b/ %dcMySm pvbbm<6QY ~?"H(3Yt}?ɭw-՞>P Ł>r_W͉w@Y!@fDK,f1Pw޳=}42#MIT?3u-I%N;@Gv3vy zR*!imBDZ9HHKu _:֎U\N]dS4k Ᾱ[\zxEE=Qؠ}:"iH2m*@rƛ>L -ŊHC+J*7KkM=\zk3WTtbL1%d(hd[wBC8n88芜;i<@Xƭ[]8J%tGQ4OGZ7DRÍ\m/nl?:]Vvktጿڬi'~df@ZSm}mmT "g@fT]z^vb=Pۯj\o?MVZ|VfXW)-˝ oWtJϊs 86jE*U7
文件名网络图标本地图标
ukui-screensaver/src/assets/weather/22.png0000664000175000017500000003303315172041035017454 0ustar fengfengPNG  IHDR>a IDATx}ieU޷9^UWuwhmmA(d<p1XbQA!?,D8 `I !A9# =Wի7{{=V׫(oN{ϰkZ8i_5wG2x>\fߕt{zm Hk{[>}aZ뚳5/'I wο\W8=+jv>߇ر&|/_zK1ke]aOhP fO=v'%ر<3;FGu<Pq |5>wc XycrjT>i8\oD݇t][wfHWF1 [X؇p g}䃏5C9+Yc)?Wi~a6!I1,[RhJa^f,cT#%0O<9='GQRSࡱ-9RJw*Fbb!DG̼!Sѽ1 )N^4$`pap >@S=Ę0{-U63ג'7w:B2^` CjQ4Ӳ<۟ @pCfk,EG7k0wn, 总66!4sj+5#b'bbkEXN36^s顛ucc!PC0 U>zC8d(59 EwZbic*BS)%{LɗC7 @Qqbt\4u~ࣸqŘ=(TuR7߳.\8WkS?fHu[!eg>՝yF\féY_3,`V|SJr 1a/=.Oym/3}RSB1 mlƐM }(F0f3'7.,+&ueɿ͂ ؄/V '1K_z;Voe;}~Okx܀mе#qw/ x߱pa[[~#{[e3hpd$xV)|o_zϾuwi s` waV E' IG='Q&~69 )9B0m^ˆ!y7 g ϲ~SSJBc,w߈W}Տo}7ߵx֝Nm[ފbL?Vp 6loܹS?+ 3F-bēz-[{I@Sk٧~>-@n4eP{s3Xfm|S\8`u{almjO?&dykYgf{{+j>$3{3g/w?tq칳~0fffpK99~=cǩ< ܿZ=Ð}&Aӟ~B0DbN_[:0x"Nv0$}|niƛw0 6T?ffCre&vww[oZ[]lۆ f}h~7{Ч{ҥ~/y'Ƅmo?B𔀐sIHɔ6G1<6F!)(96FrYϾ9pDʧk^|뷿֧r~C~z (>p$@ f94Cc &Hl-(@u /2`]fOnX_ ?d}拧1a`з)I`H2+} 8L nH=h f)&O4ܬ?F Jȃ}]D۶޶ vwm.|][88|GN|c??G~uv-gJ.P0`٢`̿><. 5Hl؆`E,8`)"V.}ۑDP0@!9u M?xfԬOvWʺ0y*mmxێt#l!:㦈ܬ8 +t3Xnuo~6`":zV5@!xδ‘S Mv4 ,%94Ab.W74%:|bڑDhbxwHן-={6ð7N^p{?gƗTm\ŢlX X,zxa~+W=-`RL[(֠p !X`XBRpGtG_@bb4'G BP B vT\-q}Jֲ ?;qb`52Y'f,"Б:[n9<)yh{ 9T\붭rV/5 5r1b2q_/RKJ ~ùn53; M@ji49 d0wXSʷ}SQlr[BBh, RZq ,C&?9NJ1d) d<,\uO~Rdkcm dE/,4bEټ72TdmbkCnmG  ̚4A < tGJů8ycBfr fBRtGM+0y"b.B\]׬x08bL&`mGloo_x˗ |ȯ9s'wk0|3>7qmO|!_Bn9C`r@R$BV0c `H@CB4dɞxH9 x`߿ \Α7ЍlB¾}?ΝW|ggfǩ;nsgkv—E-?7o:{(j#o~obq`gkR0 _i sff1C2C2 `ÇĐW1YKyy44 m軝i_dzT񍿈7X[_`bg=_~7w| 7M+|ψʙ^%/Xḻ{a^?Ѕ׾O^0hE(eQhJrP`Oүhm¼  >隍JxYZ67C `hm m¬mBۆ0B3B3kCvM3kCy0 5 !b,h+8|o 3a5M4Mcֵ!`mkCm,4yi٪b..{/.]_:tZCH)!y\0"$"oOC)R{k0k!}12zX!YD3XBa~ފp4̺6جklޅ+uMuiC7a1 +6nJ^k C0w`)*nڀ,l4MYl>6mе}@Ր|~iob;wW᲏+>> UqX,A~ #O"/O_?r͛[r98񳙡)ilyy5fMc`!XfMcmiġyU9?40RNlpWd9Cc캰5amB͂m  yR!g=OABJ~c:X i}ɟ%e1> U7}>~%[Bp>K0s9-s]ٚrN[~o8,6k\jOIe!/ۢl=UTMYa*%hWys^V?/],A.ړq/f[ï4Dp<4y5ʽUgfV> i 6F19!3s]1/jE*) crNLU飑01 rPƶU{/t~`C;!mR}FRc>ۍ6tȓ!ZFAu05u=FMFFS(FHBJʽwdZe@=BFBy@E0HOI75{Sy @44FڧbPC3>h8j0Sb}0 )u>` R\i0&>}T<Ȧ.1CSLٯ'ء02B@TcV!ttO54 (`0.ҏZTChT?x'HQ`ԢR%dfd%)4dF7F-b JQkhP= A $O J2ݓXyƔz035P)%!Fc/&ST CuNĨCu_F YQ@a' Wz^S8BLDHfj MQ)dig0wNPT>:+FA&RH\b`j0 41$9{ #(UVc0d~iw0z4˲Z]3\gփ?Q5Mpy s.ޟ.ZЗ{iւBĨhk .jHdfܮVȜ L63<;Qa {mLCC?iiE2#'3O@PHA#=u|?QrL"2S hiPa3`33ofNJ ¡=a@a!HNB4ieY2Hbj=C<^ú}5ex9[:҂}v a5c]b;h 2(%2R^b7+a4@ϺYt,[ҿޛWkFO9_CZIN\ĬoYh1ՍuʙIԕBp\099? rOKqr-/@bВA>I2c#L5D1D#j2^Q6MW: !"NIsjRVZdW|W +juSA ڔ#qBi柚O SVTkvMͽ~%8e_As"Mԉ#~+Rp\;!dM `twk!6Ѳn\Q[{$Y1Nò_K,H3 3'1rn2jI {(S@D:n`ꖀQɃg44byDѱ]1= ,әN`ԨB=Fƫ 1 xoLUFe7W B".0bBL#^x6LqZMMڱ:2-ej^-k)!4ʨi99i^M;sj"~P#Fr.].h1-hQ GM5h^'cMvͪz]! >-q&jQ5qK(|Kv5Pg)dLOZR8`\W0p4SS.mJ8)dɓ65(Hfk@MZAr.vp4DSBOfXgiJ)׬ZM(P#rP.qbiF&WVf98GMK2jW0U*` q}:MP#8uz?i$кT[jP}3jKF1TTn*`TB\(U!>[hj,@4+ꨙ$TH59VPHiZAރ}kI1 &j&^d^ SdYjJk>ZV΂QFSpw74,YRJ^ԼV4p̣p(>5ꠐuP&QQKk[j<ʤN5~F$\UEr.Ը9~vjuO-3 T9 ~gEd*$k3Y#MXlrB GIW@]pLI21ړkehMO;W81MUR;OTp>g_,Fї%qjRRPtsK rsQ~4LMls(Ii]I[b[`?|eY@Q( 89Ԅ'_D P_hj9ҾդFsyo.<3&"ju)mjWJu5YMQ(;m.…֠N t̀3.4L84Td7lS-5Z&?evHqu;u$Akجɛr Q+oS휫4[`(S>:n MǢCCWIN?)|긛cefb(3limYu#+ƅ`)pkPmL}x~z SŁhX SQ ԂU*#A+Ф_8_/e 8jJc$)\#B$XRGAK5PڻEcr +x&pcB0P ;5U+yCҗ}23aT%R r ǨW҄;ZJKpH˥Z3אOo@KaXa|3&'F7D EwAJfk95@)&h%Wu/ Z9b}!:=K>oǘXʦ74#`?.JPA `P~_GB2LW)Lֵ#( 5RmQOBG~1 2KQA(顥\Ǿ~_I|*'Z2Tc`EĜ("͐seL<ԄkHC.M|Ic\Ck05IVRtuxOn|-FR4¤e}cvʦF+8 hS\d#6ۘZ&5:~LjOVk9B ʲ)iFޘФ-3tw,pN(Ld>]l+12% p6?$ ¥O)7~>IcQW tXZWyu5Uӯ x'>.GH>Q(髶B8|)+A8Yզ9'YL>9WkVMl*X4]u=M# -hU--j *d/}+JΡI-R/P0#W yW(o|\ASDԬU_Ma SQ xh55#>Аa20wu .ٯ Ɨ1"_R_=fU;FC頋g(ӽ0ͭZc̚J'zP|Ҵ0mSf5@Ui> B~W0뱄di-gteZ+Fs(\ xL5uMb# JWMir}ҟ ;"Pj4R&jpGSB{M&Q5- Mb)t5 >=F,A @dR+O.@%UOV0P%ZK"qru…&Y`8tlV}>`| ]) +FP`.NӚ)EprM(edo #m<0R 4M $b?:_ωq>[GQ/o"hA.^ |ZIC4QoP@xLJHkuf'py1ioJSWKŢ1Z#>u$F)o1>֙=Pj2Ќz"x8uQZndAG_qTkNQS G-e[<kPosH<2F@ӵnMr"rZ O Id||iY5V\dgktqjݢ!*h/]BlNM~IM5a }[}RAZ!T[V*TWfct7&v0.aAuJ#[r~hXJ%]RwҲk|RQTTDE{~i2HK$i99N^&J&c)BHlwA'51^2ȇ9>8M Ֆ0]8 偌]VZ/l  ;wǒ9uD3Pr5wY%" AC ,lgz}V#QuZa趈ʕ$jVpkL L`*{hCV:_]`wiӌƜiɫy| K&t .+MԱckƤ*HW6΁M5)-)d8ojΙtJnC ukK @ZV/m). kP+uEy,pM}FmjVt%/ĮݑFzMIU?ZsX(x7):I2t]O|-4oZF:$gd%+P FCqM>蜄Sw]) Mnbg?)b //!?@++:QX(ZQrݷ O<.F A$rh! Y KJ@;H_K@#bj]WFWn0L7A ?*&R_^'Bܱ&G^MQI&UD?ޏ _ Mӧ@,@@P%dͿ"r}:.DQiRI!>MseboX>0Xj)'sp.oi:YbF($" @ Lߏ-u*/d}ڕw4Qu.F9UU626iUV/M vrQjʷ6>݋IWNoSb4L 8Jטek{2 q+lO-SןY0Hu5jmbY1}M`j63(8@4f #Qcq ͨj12qWMD_1Wb&|Rc6 Stj쓮}muVCcؤ=ǽ~*(UkI;i'vNI;i'vNI;i'vNI;i'vNg_Bҏ IENDB`ukui-screensaver/src/assets/weather/52.png0000664000175000017500000004035415172041035017463 0ustar fengfengPNG  IHDR>a IDATxyGU';.$= $@XeSP4"Ό#* "QYA @Y: !$ݝN{ݞ:Uso:It&<}}z,UB B@_] ^* ~QL} "H =*ȸj<;Wx~S J }h_PrC1@/7ɍh|!9- iOuʾ@(>(+'bd^R_@ *r3TQq"rMbIL' Z 2BV#)"έ|; $0@D@"!JQ۵g@Q7AB$4a/<0DA(EB23[-ݹ8gǰvmgb BqP"! 5 b}J 2H#4@SYs5UݷR֭ 8Bm; [23kbcD+mi7lִsc9*/XxB_"=w>6Q014Aܹ`sǯ彋C89@>ѭ^}c:JÅ!"b(vUkWn;}=w,*\Vޱq˪C8 1 P){@! (L@BtjB+k#CP0'眶n|V|mtҹbka0YHDPVu]g^|ạ>˶+tEY1 *rD G>!HvDQ{ $"|#&5nE MfI!Qx(h|! MBMRI! H }iN'zCyQbĚU-?ozZ7?گ8lCxHM~!-NK1"YbENtB4Kr'ɹGˆ(Hdǘ =hmmVY~L@9yz Nd*e7}Se2d$!Đ||'ל|ϏuȇG~ӷ3OٷW>E'}k#|Ug^uAN^7,%۔'N, ?z)"D0u!oߺ-VIMDzm{_ړ/xGVO_*[6̈|tWx|7?g\ppP|[/|3O:M*c9Dap O}gFx腈IR,Iw@MAFA>T$!` 1`фKp (0 QMaa@J'v]+2rZE y!- .6︢QfKD&t.B@UJq㛞t.yS^c?3IHu|fۏ{[Ib+'zz1u#g|J3#C0 By$/O#HnD ` ! T! ( $ 1G(&by4(MȑAJ\\,DD4 "04?O2"-H QڭB޼7Ϳ}W]Wh.v^NDӨjPAI~5gKw/|O~}d|/7ΗN~™?x G(+'MEG|W7n_Z¹DTsC ڥ h*e2!⏀b2 !F{t}DGDJj7XD"BExED 7Xc+9xU5tKU9BBD V%JNg`$ [O~ΖWm7>n;PtA!Fb@k>R]9 Q~Fx%2n| /;ce\Z^nfcSJf"16 r3eQ(Ոa;w_ގ9̟KF &0EҐ/]p Z//M&)-( "1{ݹ]YSv NffKj]PNQD5*Er- Sk/;;o?OgJ(bb`kۥK~?KB̃Ԧ|pIh R E@  A1, y^  Ĝ-K]5*k}?璉exI` )=ݻ/Nk ǟvi_U!H1bhF(JSG~>77~Krtڅ13x3mq[C gNx↯{ & b9sSLmI(_s_ lP!| }Q9U-&tXII(jF9]EASIm@SI33Q54}HUQȮ= 瞊}۾ _]臃KxmY:M $lh(rr9+bӏ_ڳo䯷=ԙG#c,4uǂL],S.[{f& 9Ӝt!K#'41m'#'EF0ݴ<:}R`Oh3aHYIF$M8M*L!BH|DbP!ǟF~Oqu0[.n2 1-KQ [Akd}dU DŽVdqe+ןpL}(`M(SBOw'h{,tFpDL Z!; =8y(Ʃ%gITTHb]bQ$ 3). oO[o1ED'|jGwl_`؇BJIU̷W<?&/ 0a $A`*9 ppa/D)}̎c]#淌M$<J !aa!1œ#T9`2s4O#v !}>AdGާikN`6~Ip5m|{5Zޠ0!p.P,wn2B^D$Dщ;i:&"۔Ӕܩ)oŴ@",' ٩UMzi YG& y5"" JfJ$8ޕ0˰W 0hk]kUy Ћ.آ}%Q"ʢ[w, P` !elzHCKn麗 !11'1Ҝ.8o,9GE8%0ZIr6 }Ɖ4W5 :)[üd*L+79pQ0\Tɞ|U3 4#4S}z~O_'^u鎻 B%' Jc (,1q"fLfBNL7QXRxB("(Dp!|" cjb ku1)=252q>$"Ѝ)瞂NZgu- &!c%!OI0%t QAąֽtfi wg <(YvW;?sBMHOv>􄘲?+9Xc Pb:y'~;E' ɩM  %pB!BKf$QT.(7}xD&i@i7 _7wK(±^PKxs D80-y0v FBRϺЇ^w@a@g-SY+Jr$.ӰN1g=kOrwh?YܴןnSL B|.&"yE' %C!q^$ GK'R/DAB1}>%a>a HzUUܔ"@u)ABY_&k] ȉɫ31lk3&|rYKrdĞ! =GW iqt!)"1͍c"ΉH 7V+\z: 9RHw}A +?ZsFx:d&e#XƓ"-HD!AMen$y2&AI,r2.ii@&@DQc1]4/}KT GB@ Ȍ蘅 sQdG`UTQU:bb&D +1bA&ai,[W?s\+ҕeYڭU꺮˺ժ뺮T(p31chbb_$6"2df[$[CD! S Hj9:P%sRוtfʪ\Y윋u]sYVeUU\N\QRUJ*'"eEDC B~i8 ~6GaL*ԇ@ v3g&l3sFw WpY#@( Ǻ5UbQRĹ * fv &,<3ApX+1rn^ ™h$FaGB$B!$عjFYU]IJb*@eA\q @ qN @)&LBeUTErXV[E}ŕWUV;t:,q`+B5q$.8gf+˲tEQp\Y9ǔE!9)u]KUURU|.%;ϴfhFECb拈3sSn Z-nP׵EZVUUUsΉseY:#"X#Ibf"̄٦UWE؇3; G# aY"/7)t3SYTUkZkj]]e]ץR}YS!;fypy*\_3sGlMԁ*pf1F <g( e>&h)wN2]n1sP""*yC`"R 9,::GL#i~/^W-o֯__^577ש]׵/"eW8 Z##yEsDTQUQɮ e!#8PGAHJ# dꔆG"c2(yQZ |]AD A~cš=d_A吏 Ƶ8g1d*X6%> 84]l2}͈.物4[a2-fWM_ΠQ6U `1@VDg_ODNU #Gp@Ὧb5tH?1T uz<&i0|\˜z! -|:hhnmccp _߭P?Ne5eE PPׂ4DD7X|A\ =sZ>&_Հ*0M1",acmkTcUɹ&5ZC7͏9$;lG*lgl?-JX[ wdCMt(cmɪ 4k݌d(G0dQ@(9$>7uj~T~&JO09" /?. IDAT|mn9w6lQWjk_AƂcmb1(HØ l]筘zk߶e@C4^;(( ZPy\~*p5=@P*K B]M#[2+@Pm.1TӦ!eJҺ?~iU*>~^a5s 1f1(L#i(HDTЪꍷ0"z(ujsW0`B݂cqxc٥4r}z}Ip)hTZ0~dcz*|$Z;ț}LzeGMG@TfMo!ium+cZ[>maLJJl.V{rTm  @acm ߴ/6!N CZjaZpP#(=b 3ySIZE*$ЩplC)yz{zӁ^5Khġ gUi|,(LZWZe$T% *5g)U&y~&, Mxڠ&@@<0&hޫ͌)e:6RA5MӂWZlۧ@ӁY`l5PPNQg VYQű"oh~+mk@0L _@5M5:LlLiSA6`Ckf-0iUJ6:QM7[AN=bșP)}1@gQm5"d&`y[ϵ1Ζk}HLn꘶S볶SfUSmn#cWW/:cCV{I߄2L,aS[&Љ s)P}VlLdtMmz?NlccA7f~EV Hvzp˩ߪ>Yv`6[O3(,e ;\UЍtY볹fXYG#JLuD>̀ w6L}]6nB; #j]Aԫa5ۚP1{ \.Siڦ,* qom c3j`aTF2*DDyy:{0W+MLOj|eiMH`Q`6hoL]5 ]`q^N;z^̝ KaBKf ?il}H&6&[@[Ȧ#=]ftE3uB!]tI}Ҍ|Nsmxu2t )mgESeSkt51v"2I"- Ў[aс ZHFj5l^jnpݯɧ@ W-3BeYoYkgiD6*QSLz~.#>^k͊34ű &CG1LЁ`ӮvƳmg&Z:6f٬6IgM{ ]ݪ6 eBK|AAdm}iPHm/`LvUtF$sPS)t 00G4v.PX$֝ReHKֿp0 Y!뼂24ժo´~jYgTVN|\go kΟn%IaL:0j-,Y:@#SmT^n4،d4mz Ky֌WĘWXj2^f1G@{10)hN7w@gm"ܙuUK,+ _""1G /h"cFu Ǝ̝T.ݧ&S3t9K֜ŭmԅ;¤")5TTe ڴH* zt|Vk,%Z'(EaltD&B%o]WX8j s |Ċ6q͑!ֻ#2q㫹;kfQ?uۦM2jr̫ڭt9E@*8i>\Q̀*b (>Ib'VFHCj2Wa \Lm͕Ś[ЬaE;gX46=22YcLoOn?c ^,OtzچlEHSc%5`q6 IX\͋uJ>86r ڷ`&$S'sq,md/U -XlYڵDP:_wxptށz: ch46QGMJW +8Z(0^\ԕk`'}4~tHu6g#$wt0)Г""kx#!Q۱p@d4x[;o=[cr_O^ p+^Vًke$'Z5 .!zr(; +nEձ~ؐTvtbͲĽ`ojɲ@;ǙKX|W^zkz>=b$ha$LCm'w"2+(>s9x Ârb> nD1;s/ XjMSJ6Kx(GM):8A ?>[_Gigm2l"L>tfE\ӂy2+ q<_BK "Zt"XAZaVa,IixIE/ HkGzŻ >@U ky At+*0-WEW9TYqyWV&IȈY,l5 rm4llWoL %?4L`P(*(h']mg>G}D{;nHcFE_{h:r+"*Dxs!HpUMZY:PkDB%P]LRNUu"LgAjZtoN$ SpY-kW3 0WoUZuH6_0Mq*%|P!M;#4?~A{_*g'}t,Rd/zd #р@妎h 5SĐNWdj}'^8ibsj)3^!]ԯ@ǜo"zr;9gL1 ZDYDdUUNgv!='ٽ(x?QQlذzuTԮk6˦?vTCm*WEMތH`⒬X B%d)ʰ_/:PD$fGəVˮt|މ8tx>~ V嵟 lC_vcq'q֘D,"EQ<9Oc8p`re[l6ܓ|#">Ȫ-T?Z ڐ H MO'!(R_KD#2 R˟'oHq(!!]92']Qԝ뿰UOΝHCԘ9im׽n=u]z&T 90!yANh$(po(̴$1vqWXʴozW(Xa1xY /^]1NٕX: "s-n/ݔ`eb>BA>6&)<0zQr%Oc;qo06.Rz5dBg '&aSF男Nk|Iⰼ [[nE97#>&YNi%` H 2>00F<(f $:vn8q͕Wo!Ώ;rx6Y3u39W>gM"x"jA*)'BsX?|E?HeHe5Ɣj _P^WϮ}[zq07]8ثN\yus;Gl0ZDĈy֭~ CYD.u'?-G!\}W`H w "!>Yf(f=crwdVѰhӦMNDbe궫S  ~+~ʯxz7,vwzU/gWԺ *("(bD_5vϞs>_ f=6?#-z{ 0IߙlK36L⩶5yI9~z3? ~⪫GWP%ѼIvlev/腗^=wW߼n{ #t9m>u>*zOFʳ/Ls.;vׯON%3یr$P_(Hr'PHC!cpۭ|.|.PЯX.4/g].73(濠֭[_׼p}(ַ}WV3y/ &"7۞2;wciߍ@&-82!OXQ WҞ?I11f_`&]F`dEADDv A\?zo_m{s7 s }3o-qu}lL7(ƫ8pˏ>Iv$yaW_ WO09!u"i\=wUu_$[ќwtMj4 VfV[?/|k_Dn=|(4oZ^g|'>m7c&`zfaL mw\u7Vt9s>}8K뺏o =˺<7 ~Z[ {/-%_G N:i7oU|xpiӱsϑ+M s~>;6BfO8K>ַq)Hth9x뭷>(6qP5_HH~|?LsQ)G`^z?oox-yǮa*8ʋ:o2|~6oK%/}r ehMfk~_S@LU/ X^y}]ZOcV*u~K[keݮukg#t|$ _5yڙ߇8wn%gyӛ߽z] nPw=L>]7핟|܅;f4Ъx~n/w^i`[}m[]-|q@ݬϜ?]x՛E~ }wu:k/#U! v߹aÉWQn6@͊s7q%bh-sGt69yDffʻ>?zTo"G݋"o[~6o~> <w3bq;pG63zuU[ߏ?]8aIDATBԪW7cWĀsQY=wףɠ#Qw+:sDEQ3޽U]<*no3.BwCX/sKv+l7{=V+cXya IDATx}w|Uoz3PB &,XaGwUY +Ql A]AB( &77O9?L2 7P}39sSspm`P`І@:H8U}o=p@LnkkKmnnyނ,l6[eRRRe^t~+p<#nznEEńn{()# R~2c 1HJd2FGGԯ_ﳲ67jڦ@h_Zf֭z9uuu$IJX,lX,08BdJi8@dY$IB $IF 6i5J=bŊ[ eyhDD"##a6edB8ra0!U((B$&t6墋.^y ZH;vf͚ pnBBl6l4ebn#**l6f<e$A|>x<vz((rц_W^yczrD~ lɒ%O۷﯉$::Z4Lh4RшH$&&8X,pncj?k_EQD dN Ȣ(JƍAh^{d, g4YTTHBBL&S;CC%=WUH xv eMMMhkk 28< M7B``Za0ځj@ @ 퉢AxJiCffG\p?˔.L/ 2o޼ZfRRRy z-Z[[҂`0χ`0Ic`hO"l6$$$ ** Ұdjj ni!~?t:\ً\d)Pr߾}o뮻F1::!Cdc,_kү2paD1<ǃJڪo&  R048(@NKKc%%%>9 _qȐ!3v;5 `࣢`4}z+Ҫ`0xo$W9EJJJGGe8 b2Hss3N'R͒MMM^{/0`@~cpiiiygEDD'0>wlT2@FUHNNnoWe>\?6_k mq !D-QJrBSS%KfRJJJ#~C % [ִ,8Fb2 IH$Qeh!ݷ8VIIIQs ڄ:sp`SEatt40X!Cllp8.\$~#ݟrR`љv=1&3ƈpZ3&}mxg4a6aX`6a6.ZUf|>qQn8P!8'n3l ,Xt8: ><Ƙ$ jSkUuUUuj5cEtR9FІH}kYaXI+&I!111xСV^K/@R>QQѿ4pZt Xiii4Z[[v}a$d2lg,j;ls?@i5>d Ý/jUBDI֢RәuƵD)N񉉉AbEvv6CL& k391CPX-cgJ~w G KTGȲ,B81p"S@~Vx<lJHH`#F@bb"!ⅳ@xZU*H3={}'LmQB"%z̙Q#S2'[@o{=555h2 1b!EY[Z`DW8~A_x<x<5QY=L))))[~;С~d@{6Z,IGu ^8kc.HN#׆zng@IOߞfA@jJ `@8TYT / ##C]eO\p3f\F)v;?h ַo_һjz چ-=lbO%dGimѪ(LU&IS󑖖>}y7.Ϥ z %''wZVfYglYvOͶO\mH9YOSūZӦAA h;lhh-4iҽ&L؂A4L{#""hV+mމSH1q ԇU*iΜ[8U*T|C}ģwEh h42"7n;w8M2!$i3ia@XO?+k׿gkU&j+:'k?P*iE=4#L Al?0oz4hРɑ1FQ4L\\\\{Wc A vUҪWО@EL&90˻n6x`iiS˗/Ν,B/[z @ӧ$L&X,D'eWއK#p5zu!tu+pu1tx$II2H 3 0`̙VJbaݙ'  0ߕ՝kzS,2!]U)W'1J)imm,gϞ;"d;d2Ĥ( 1Duʘ pko'Y`FYĞ0TE=`jg'$3.K߿iÆ (J)lQj5Zo٠Rn_WGg3*\gOU:tqпH;jʬV+)DGG~7nEH{\o؝xƘy 'I^B:SQFɠq2p!^#I^g9pxeڻwoyݺuXa;pcH! "ZhW霌%Upp!>ݩꮜp߅Xzܕk_[ iudYǏ6hРk{nO"kf! (`T'n6\;Z~I7 {kOU~gy o]?&X,f2`ؤI)/8ƘIi;X3U(@vf絡zhåYO/)ܹ̋,i-vu F8(,lԨQ#!Y2(RjdG`c)pLnX;* TW 6zvg} pe©ΐ0.Ωp'Æ v{AH555,3 j>_OztRYpd*W_[r?9@xӷѝf 'zM޻1gRF6dȐqU@4W^^~ζZD$f;ATƄ3|@82^;Me~дv@?LINW{Ѓ@-ҟו^G:kBBB&zX-X.Mǎ;X__5)E l68tIJ2P=v&ձZxAOz8|i4]i(XPJ lvVwc ~~ =./wڰF:s{3~zoY5éatjAW]1ί+mL*!0`Zf}fn[BdҎ;6N8qq|La>lЙ=SN2u@7|P'Y9-5Up@Л.S;Y} P.w1&+DO?O:5vS'&=~K8׫|}V_mKњ5T@= Eڋ]v1]s0p^ZF~t @"H,[YYg H˱H\g^YGm@נv)kN+虧X8NhCIfɲcp93JDyJ1f`㳳H!BQ.~xmnT^7п3J;]o83; \?cH~ w}b q,~tQ(8獲,b=zhlׯ_B dY&׽sתx=ՁµveYv0:s;cLG>az ᠟}ٿC>!!d3ke2 ƌ޽{'U$Iy~,$Lga^g 2E+ZkpfP]I?L dzgն]kFiPe,$77o}@R8]1ƣ!FtLv[322z ee:M Avnu~ p6?Õkz3 zB~8K7ciȨoW-# }Gk***~P|3c 5{-R1hՙL(BQ:&x.~R7d^ AO>P5]'LgÅ*u^gmj #n[e/_$InB۠*BNn޽͒$Y-Kh" 73|>SJD} ;xԳ^1^{赐6lJVjN8te8BSz!U|"yx)^ER@%@) HC0(JyJ1G(ߵkq\DTTTtLLLlttttDDf6b4;;{@rrBL&e^}\:(Z:@?iméfVwfז5Wc)%.Kرõ| ! lD"cLD7!=- %I "UR5,lEI`"T IR[UU5sܹA%I Duy8^|,wܽ3k}>Cvyz @*^p>NqB$+ ?1`0PAv}ŋjii >裋r1N90Q#PrӄO?eYÆ Ku\vSWzjն^JPv1W9ۺ.s3/L"x\UОG`j@@fI 6-^x.#PEr$u%("M|7O~G}v;Vq"iHc*xwҪU>W^mmmcSSuԴ*zf^ekd2PG2L.(D( dL9P@7e*\5k|U_/B @ *<'E zꩿ|[&L0e̙+++222$IKKKK5GP9g v }㣂Ka. N*N֮W'xvWzg (1=aAoYCD9㊊|K,YWRRD)pG9|"J2Xu8x^RZZ9dɒU[o,ڠKۢAQ)S<3cAem& {-E1z;QBM>R#!x{8ĉg?~ݫ, *AdVk¦I,Ze:qTal0<ViU:O0W#+۷oYlN\_k\\\bJJJrzzzjRRR||||nL&`0PQ%/*++JJJKJJJ}>_(ZB+f-Z(.Y=i"""*999Uyyc}POU<eB,IRpƍ_>|x &geey^}L43 % 1S&"~(R8rHbBvۓYZ}B8Q~*++ŜmABHp8*Gyaa!1RJ;EQ BB̌1QaW@BZ '˲mM? hѢuuu7pjjj}ZDDD\FFF"T jݻw>uJ)NUA*̷)bFhMB"##7zaX-QrRQQQUZZZR^^~8fիO>}+5)))&::hM~0Dsss~߾}wܙ+I# +b5V[M Y9~tH!)yszLd``=}|p޽{l_4J ˲ y@e|$BE1ZdYVG|d\\\BBBBR\\\|LLLf[V`(Te9 >/v]1DNI@(<5!) *Vkdtttn`$It\ކ٢0 0jԨc(Y8Q9TFkCب4(ޢPH!MZ"##333ssscD(zz(CtU  MPI ME\<^om‰O 233SJKK[^|̙3䥗^ZbŊ5RNB!@i2=9B[0ƘS:@|Ir>X/YoEQ4,[B\ZCZ C ٳg;p@REEE={oY)@Ѵ 1jI[2OFh$͡OdM|w///ov\5k>|x} :vPwRCSaP:u̘1+++KË{ !~1mܯ`+*((8RE|ydYVmRWQ,˵C???߿cee3\lommm۾}>Q= Z8gѡT5$)~Ƙj3U RUnU39lذo>l6ƌ;22:v቉%%% x5@0ZéwO[wf͚/wBԾm@?'rFBR1V.r5fJiO{a+]9LNX,UM'!oYs(ΖKmR}v^G|$I|,h=hIQ?2eʔ>_1~1PBMak `\vo /pGlli^ ǎ FEEK/tܲe}ǙdYv̞=cǎTرsQݻ `0::k;IuJ'DF:O ]L&Ҵ45Y7nX @7oc(tʔ)7y<BDw4=SsW>O>Ye˖1ҥKL6mop9kw;vҲe˞{W{'GDD"%&&Z֬Y{뭷^M) ʲDH#S+|0hРni 4Nur3tA Бog}٥[o{уʡw}ESL9yrM.uUW]֫Bd2M6d2c-o}ܹ -JKK3SJZX}U6c_WvvvRSStm6KKK:묳.y[e OUSô[瑟D rrr>޼y /𢌌߿9駟~n|ѣGq!kF|Wm6y{?cmۊsss/\c=6t2q 8SBY9J˗/po_ g͚5xƍMgO:'0={4l6KbbK.O kkk[SSSc?IyZ9w\U\\|~9..zSknnڵkbG8ŋ;Nlsrr>C(1"h5gس>QMJJI&\r%/]GyII .+E\/!Tp P&&2ݵkׁ{>(^z_~&zO@~衇r_~}5LKIIv\R^,|~~~7x5\3~+JOetZp81NrXDffUUU)_',0~Ag(5p; 6|=/qV׻7mT߿/rߦTYh5V jjj?)y嗏7n… ϯz7bOũ۷o8pׯ_?Oiǧ:sDQc>wqqq޽{+8#GTSJ`/{W̚5krddɵzjСC}F`P>}z5QK,K.sNA3G8 FBH5qU%Ij$BFWcc' iWA!r˒º]v-8NjhhCH;i3N싾555l׮]-۷oo޷o_ۆ +_`[} BRn0  pJ xsE[*Q?|[]]jժJŋo۹sg cy^6iҤFs=:0O>Wggg_5!d a;mrxJi&\OJYYxȑ?en9묳b{ط.T@ 4hҥKW}WE0x*rrW]]{r8 3~!:kgFʲ압$O@.@ИDAx*;3##'qe[e<,bz ZV. ~YoCCC岲ԗM#^}eY"}J;TT B(0,Vꪫw K\ti#L0To0< ITk׮u?s@3f~ "pj+zh3лwosnnnjxڵӯVTT(immmZ dtw]gQ52"RBfz7uҫ9sʔ>D9j2bJPi6R1&\`2| 5 L0s/N|xՔ)Sƍ;ꝙ޽{r p8ލn;Aj1$Ct-6Z'D14 ۶m+N`0vu,22>lذ>e@_|6mZ &j㴊rS Ԅi&ʻRSuuu+c~ҤIont?~Q޽clgϞ=k h0|}y7>ON𿋂`tii 7o8v옷>ۑ:^t\rIIIu ~ecǎO|\WW4s\ Ͽvǎ?m}aaawy^^1&~}>RVQe>r<'#d6k+tMkغuk={~UUU瓷o^i6ر TVVJo֝J[@8s#GSMMM}ݷGY]VV$y<k׮F1rss[bccgϞ7OZc?IIIWD) e8 v͛&2)uuu޽{>ϯݸqcWjWDX:th;|i?-PyJ!ıc|1v=|ۿ;X]XXXWW'1؆ z뭝^0XSB(YҼ=3@SN뮻?|7z  F d2x0Ƹ>;>==3 P2t۶m{O?o輚'$0s7bЫaÆ]}ߴiӣ Gm{&XbayyywIJJˆ#21555.e pp@h)--]vmُ?XlٲbQQZjkk}>vZ[[jѣ:?-ZYiѣGg+ܻwouNN|+/++{cLII믯Rg1wߕ^q|>Y}/˲OЧO瞜`0Ȗ,YjUq83&@w޽yذa9r㑇I!>vȲL!Le477GLDDDDUUU-rqQVVfڹsjnnnwCCgm۶_}>?%{2?ٶf͚]v?CڵGvfAꫯ8rȔ2 7kSr䧟~zzNNζmE#gtLp8W\[oMYcSS?**va0o01d̘1^~/Gw*u9+6qľ{e2"9={ >IÇOK(,,,(+++ideYf<(S,!ӟth4_ȲeݺueW_}_rss W\J>׷\r%C¦+.B?HEhkiӦYeOYb>+? d4!T4U: !әJ9l^7:ӧr8XMR&N8h…ʲH+"tUSMAu˼^V(IVu={RnWM6m@`8.QXJizb3ka";aHV*ɬ<#<€(9IGhV>}OJ(!?} $7lӷPJގeZΝ;~sKqF*Iq%Apczm8(:( !Y Ji q&)V9=I (ĂtҤI;w+++snV[[ˊ};w,ٿJ{iMBk&BG1ov~L5y͵1óV}K$jkk!`0SKį'|FB`2]{Odqfff\^^^sLLnSj8p3f\VQQ#BɣNw=o6G9BI:uSIIIźu3#G8_tE322~4dȐȆ^W.HJJ^zHv Hxw[aÆM2dHVdddF[[g̙=:#77ͱcǎ[nM,..ފO֯_SVVݽ{wb\vmh4/eK|W*I6wC1(--=xiii ֭;}CK.]$]d07mڴ}ĉ._|OQQQՈ#R>m|Lrrr֢cNpd ,XVO>?Su߾}SRRGn*..މoq\A$`~ta IDATxy|?>gfO a&,ʢH]VXWV },bˣP**AJ@%la K͝s~ܙ0$}^y]f9s>r>s$@2P @T_$m7FqmwYvuu巷B1X 7746~j0eo_|`DӴ|Ν79rr]\.x<\.Ȳ A8!QJt](u-''g٨Qvmzp»Ted 0B Y!"$I"BL皦AUUh]9QEY5a„]z饫6·v K,eee2YprzJ<N'DQXTT$nt:ijj*/--%^1PJaigAUU #%UU潈K01ϊ$F4PJcN>33 LjJ+mPdڴiFx4+n^ZZJҠiAH0z:ԿTMڊ`0hU "YGAp7b13D4AUUaapΕ1c+**j6+p6@s9ӧv5'8N\3tPv `g>2mmmEWWTUE4MxA "!>@$I `DQ(­i UUP(@ Ji9ĉ_,++54M{{II<s9Z>DGGb("bt]?$%HHMM4oaZB0:i<~H$PVV{tAl`?k,kiiiBJJ F8ITV`~hmmmF0@~(e#ldeeAD@ɤ̍Y[.DQb1Ť6Jۍx(@(\.SYYӧ-]tZtH< e<''gtaaXY$gffS]*2%lg7sh2_eB pM0nj5XN̡iBͤF(&Ij[[[>K7"OKK;dp眘C0םL t`ɲ  ӉZ=3D5&s ;0!8^;N)=== Goδ^$Isu]G{{{BmZ=lS][Ur20؇79㜛EŽt3S9F?]KYb,1\.S#(v}ׇ~k} N @!H!RUU%~ߚҕ fGdk88*)))k̡Ձ14?[d-|f=Ƙ'ڵkСC[-g" 4\n0jp_[%b8?xiK2N,'&qaT]cs.D4iizsΝ[@rss/_v^/qp8e9nU5@)MdivɱkdetwX.eo7c@Gbt/I&_zYJJx5.vg>\`T&v|[NE対Jnzt^PP|ϗNB&aV_*'+9&Y&NTUEVVપWcX4pZt xMMMW^WHII %%%$'''Gbf~7͂9\3mrw t؇oVbߓ]dOj5Bb(=%%]UUU+Ztࠔ.u}bNNNr(++À8yUV*YZcnx;OU{L6ڟՖXb:1!DE᠀T `Oj(B1CdvH]*dD%DսGw&5hf!˕tkI}7/RG#&Ϛv8| Km۶ᐲ0j(@9&N~&xvwHdJF~2jfR(BBf$+BdP((^޼ |„ kaY?3:j(I8s1:f?BrA2k‰D{9 fҜ6WEኢUUy,cHu BFp8ۋ^tE/**2I`ĉM2:Jz!Cy޽7óU8fֲLOX'*'{=v-LXV5Mfp]׹1Z"@4m0` ~"8CD@}я~^eeemw)1޼H޹}`gzw?{=+cW}=Ƒ;lnnF8~%3 N4L𲲲SRR,˪ ۝8Sa7N}Xeu>̙%S=OŇ9x`Y҂+W\`oq 8Q0BHȑ#1ބtA$uzbzdd5ӨxBޝ9꩝|b2hy$h$IxHMMszKN}Ȑ!}>_,˚233c}O69Oj'KI֩N47ɪ^CBUCYMmnz Mp8 СCc555X`vm=8/z0w\̩O&eiyKJC;Ⱥ!Tz1tDQ ,D$.Iꒊc;w|߮_)8'js.r"fgw !bh&V X>35njN' LHO@IՉ RJ:;;šC۷oiÆ ߜ;p@Ñg80t)bqo}V &(>x"9G؞dͬ)i Xb\ycfzf̘QR O@)9J[[[`9)dNlځ& 4H b5nv`&p2I2NT~oH0$Is$ID._r :TAxIڣ$*vUnJ}Y5j?9@axћfI&vMc>1RȲ̇ v~|=z! Sn7u+B<qg2&IiR+f-;ZFlw=&~]OfZGw%n:9;;`Wt8p`WSSS4##a$1 y i'v[;*f<@H̲$`Wf' ӓ|[{kLp}A)%xxmxhPUUUUIIɨh4c qn } "'VrMӈu9ۿ[fY&j{n3dm־05i;KFis?'tlr󶶶# =7OQ:8)]]]h4l!f =|Qvošl(k{g\vd?"Fz,˖-[Y__ c,Ez Lq߿t3hРlEQtX 3ߪ O7; .7o7ۓMw|wdY쎡vؙQ6a+b6oU yq=A0 @%rQ,V]׉nw8 )V2jc7II`G ;d$V3LݐH$cdG.]W1Jr\ї88:A۷o~$h4ʌ4%TU%֝-ܓdΞOnefo Z' +{Rɘo.uCF{fj(<P(*Kify@:O$ZD) VUUUq8iiib4x.GQ27dN] A`{Ow}ak$DF']yݕim)@HA#[n .X]׃Ji5"G6nܸ]uJeBi& #D"(z1%SvOxaBaURoU4u $@7F}=D"¼y;pWF!BHsi8FݜB(B0A!hmm=PYYeƍkkkLQ1`QTu1:D"x} u%@nRo]i$ g2o-;O$AOf!,4p"{>裿"> SJÆ88I J)Ut]" !Nnݺu )ii>xܢ(JZVVVܷo_BaqBG Z=pcN=]ZEQ4̲TUEٝY+ivvfaJI k׮ ,Xoc6sĐM t]!UJhB8A02 G7XC&knUUt]8ǖDy2ޔ|b^Xݻ~f3ngy\UUbPaÆ>H$rpbTBpsVbFDQ@@ Άs.qzv0{MBM6}5t@  Hj3תRڃL8{0cXmsHu]2f^DUUs/L g+(  Gf09u]XbEӼy^j`L^=Bפ 5 vU߰u͌u@IIxHQKZo&'"ָPMΤ @3iҤk.±>Rf, :&`=XcUaSbdnm2]j0I㍦pp\p\̪-$`n 5kzu333sfeee^p$QMh4vtt몫k"H3'v:(XqGp2 #|x^C[׺k+Nw;钹<!f?tҤIKKKx^̘oHAmI)4kBV4yMkp\,syI{O\.Wbk;pMѬ0!s?^jժb(S#)cXL5hFDBhxQaĥr"8'W_ M-Tt2{8L)U9"j3 x(:cL7:Cqb X ֬YSjժ:::!cFwpe#'*Q߇BB7I1QD\ !Q IDAT\=I 缷jKlpJ}}K+.8\4$@ f0,RJ=1n!@7!)s@|>_AJ<_~).+ښ) KpԱ45H/%++kC=￿v[ !ys̐ !⎠8RQ}ٹYiv$ RcXLD"J0 uĩO% .!P-uvҽ^ouI$麮uuu@g{{{(~ĥSj3GG&SUS3&G[5%?{Np(wdӽ}cpHC2+z{Wvwy#;t]ojkf"O<4556558:0 >IMLP+q D9 pn:!p~ C6C60*@q%v 9)<sʛmc/'ee¢jnkV&:% TWW?F*\f̘Xeeӧeɒ%oy7u! 0̈QFѪ1q"X3 n0݌Uh!J {M8eddZ Ѝ f(;8(o4,v Gq7m(-bt}3o+[^{;H׺HX x:8gQKM򶷷z1D"?ݴi\1.''-[TF"PJ6QaN!ڢl2c8!<@H1֋& Oؽ{Q URUM+Q~ě2ẩ=p?WQ JQ8QO <)̛HP ib HմȎo6'EzB$}yͩ"77wfuul}?*˲1A)us Gcc IN24O0_yVMy`8 o(V ;zi M;?g}#p{/ k rp+Q6!$F)qƘ ^'|z*@~h!@XT@k]+撒qhtW_}k7pù]tW\q؊nQF]c=2v17o-psͨZGr]w^?$ MGV~6`z)z–s @V7='ImAb#Ua(1BHs *J) nsH鿜2;="9{uI.$;󖊊?O׷Huuu§~gҤIp̘C-^r&LpݺuxTTTfY iEv]=':`N8% ܊xG 81 p moͥSFD#AvG o^N(ء"a" R\~qiS"t+K6mz#\hmmmp8;tP .ȫwuuiǵjժ}o?`ӓO>??OxС:y<ܚؓlmՖ?o * \tJD 65gQq]'Kv!D Vr=E1= ,KU׬Y3j̙o.IOOϽ?+..8~Dgձ@x&MoĈi󈪪/Kwq~ii\SS綴cǎ??/ 80{Ƀkkkq(]2"Bg@qw9eRyssǜ?ڝ.^̦)S6k; u \R8:!D#pMZ_z2bh:KWggg?vQ_$閛nnh]]7g33Jgۿ-[~&OMXQQm'xp8!C_|^qeO:{ǫW?y\oJJE)++sUTTt_x^[g͚5q̘1۵kW ]w8oJɀRZzc)J!ԧOŇ/c}Nlݺ?c]9998?(>!) F`2۷!TAAAF~~~bqcڦMj:kiii8qPM;m-  طoozݏ='_}sȱX_}/7557oW\Leeg(jVR n`w#B˞BT^~!9%%媝;wNt:#TUI))'ۍ춚VWW7x׮]gggHOO' =zt{cccgkk묔<uu;UVhuvdᄏ+##{Ι3vYqaC !t]g)))Œ3>MIIAyyz?rܑ}l>^6S;^~jjjؓO>7"ph_-FXLC;pKnNⳍ}FgٷH{<A&I@ gϞM (̤%D[#F 455@wss>s.OMMeYؿѣGw:Xnݮoq^)??wyZZZԁ:ͬ#A$Ip:.ϟ_3cl-Тz-Z7xoc_1_( 4TJG36~p锲[䧩JSӝ [Y@y<Oqq1;rH.IY~;,4hĬqae_~?ի')#yhw͝;wc,ww@˅Νtƍ[u]7?Diի^{U[ls=:|Ammm^aԤn;x^G!A)3"fv?9q΃+<F)u}7cZh4ڠ3o:&:e_,[3gLUt:?]ƍ+&IDI(~敕eegg|ͺlQs 2gΜ?l޼jхRMMMۃ>#GoW/yR \={﫪^o .nٲ%m۶oVsApȲ,ހ+8Ã|7;(͒$UfQc8.ĝYo^ yioA[] d͚5X,n71A {nذ!y-5 ǐin;%k/¬ ߋ/7XAI7=QsD3(5Gѩ]|7>!##֥N3roFFFιkkp}2I2&]Y>Prܺjb/*u]7'---k׮]wmɓ'g466{ ]tEi< Yf-YxWU1V +WDQLg aC)\fz@ ՜Xz"A(iT{9iq[48d[?'B'vttd| [nQqq1 ͢(FC6k`8e9NaR1y_[ nB<"~(NgYfffZYY766>=(׿U%%%dCAh!ii'b81BHPP2!B)mB\xT Rش@ &D^|_dddpÇ-##Ɖ NA.TP"sB2G]c-esYvv6/s9眓SYYׯ?녅۷7b1.Cd}&"11+Hi4ǯt] x} @u]w!6 41۽{vY7%%xСƍgMpXݿNAR92駟~߼4)'ΑwTۀH |c`k۶myww>y䑻.K/IM PJ2Wɞ߃n(Yƺ04W9'J0'B.4}v1*//OmkkS6lذGDjSm):!l]׋W90nLNEhA]p}?2BBψ98yҥK_x߿iSO=ƍ>={_h6N,(];̷JTύ|8cϞ=_(--}>DLӴ(~ιs3ҍ̕^GhVUՏ8d&833۶mLCgN%I)9/朧 :z!uuuu0ӹ4 c$@]]]i]]SCܽUUUzI{6;;;2eȑpb~梔f22gJQ)!añuK5 |޽W)2ry[ZZ?C) u}؈# -pv.X|{xt]|7g G%f,iib|(B^g"ڪ-eee E9#ZWW~H?:euh#tc*Z3Ql&#33󎦦شiHo޼˗p8dT"] 6J:HiJA ޞSQQ1q,] 07nƘ1Q 0aBahټ2&ć{l l\pO>b:;;]]]Æ ^^^>)33Sg=u:VAb)whsGxuuu%%%7ڵkG]UZZ:wީ,)) w5jTyydYnΝm۶Ғ{#f fzc>hG߾}|嗏 4={hiiiwmڴLII~u+V>qƟ (TTUmN:QkA.]vժ(߿ҦM~L 8`ܸqw9rȑ#3 .|F1>G)pνbŊ[<ϢAFg"Mnj{Ś5L_vۈzg-Z^yy˗/?PRRoBG4MKKKܸq?|IZZڋjN]u]4mt]ߏLXN !۷oSO=5kҤI?tѢEJ~Wy睜6 IDATwܹPVV-)))80uԻnjs3!$ƌ?D"+ $ 1ՄI??3Nwg{Lc]@@饗*EEEkG}?x@JJ6_B֕'r\w߳+W<9p5j2Y/~g̘n~~MMMM|ժU/i7 Q= .lʵs"X|/`0kkkÇcCi7oVvikk~i;S#?p'e7L4=rh/eP@ }GTWW-\pٳ3O?uo*--M3fеk.Aqqׯq[}:*w~kG@H;̹u~5G xIF>#?XL2eА!Cٳ'#lذ/llnn\1cƔ?;@?e]QVnj}Bgf[޽{/6mgG170133fs5Mӫk׮G"9{U|y˖-ܬO6q}o+;;[ڱcǾk3g|]Qo.=? :ҳHARpzyu9p7+***׬Y󳚚ƍGW?~|n~~o߾'|pcǎUF)22(ǥ+tglх1J^q0#BW>&e˖U>N6s.䒬niT~~~Q(KNSk)lݺ?ڽ{7UUUq p?m%-Kq$;KW,Ei_^x%EEECo޶bŊ /pՊ(455չs΀(nxaŕgϾtZjKxeti/C?ڃvq}۷.D;v566}>ݩp]b޲EO'&,# sbR I|KȩE-_'K9Bu޷UuM;^pcxL<= ,̅˲l[u]/4MӸx♃\~}dfgg+`0{'&&|$89>핕'nƬW%2 A!& !sV8d-IRP$ d`)K$YY13 qqMbVVݻ>}P(LZZZcLeuJ+$mZB*rg2a 2vqBHs9g1%H:ԉ׽KG@x0=tPJ=?z-TUui6m|ҥ+**.u[CR tl8#XF%zثieY5 ca IDATxyU?9{g&3I2 D4DE("/(&!HBBȾdf2Kf鵺]J̄$K,Y C`}w_OH""gϞz ðm-`Nr~mH&hooGoo/yAeYLF.s=d7oۯ||ޑT.⏜{PC6Û|/ﭭ !H$".\SL!^&!~*Bww7$H'Y^^{„ /s1O.X`ĉ{ux-7… hѢt]7ѨzꩧbܸqC3&pL&!DQmD !$m{\hU \3oP @ѫ@83gݧrmGjjj&}ٛDXu:Ĵi&>0<|}7ضCJ 0`̕R (PUurb1EQ@DC 9"˱# mҶmrJ2i:u3K,i\qHo{H3 -Z/Ҷms֬YϖeR3;{{{3-!!|1 "B<G}}=*++T6z 4J088Ad١cѸ-dRdYL:o_|ہm o ?Yx"JhY F̌L&zp]1qDL8 3R{I& `V0M6M)%K}ZmrVF/rHuq3g/g͚L4,:@ܻa$} 3L B@ EQPVVYfv G%c8j[FUUa!BQbfr(XB__s_x0[xۀ @={Gc&N8dxyg_hl6 0r!iPU՘?>lhǩ7C8L*8&Mz$ F `0jsǎg>󯦦IEI4Gn<|3ZD"!'zKlF.63]?4!"> =/8O/!ݍRƭVQQa-ojj:sMxGv]]݉`0 f.Gsq{f(BQUUӧqUߐ0tWt]GUUEAK @+//7f?\Դxx0eD"qu#yw>$iFR!⻉D0uԡzL 8 J#1[V^^}7?(-wh4:Ӷm  ;K?)`hvsOz(2<0D^Uf&J-oDRJ@ p8 .d(YJWTT䛛O[n <#@SU5ZZZ1TUN دߎ^0rt0Lf(F~N]c ]׽-K)*s_[lgyxH0 eʆaP{{;04M ?g&yLu!mPUCMRfCfR= I)ٶmm[q ]8XLN;q DIt}Ct"k0[ 8̛Fd2@.z{c~@~f*\.W[WWײk׮ؿwM%1-XE}EAPu]6vZO.Mߏl6;$];0pP$!~ =(8@D⣊p(PHaBˏ Ew5>7WSOc)%a7is*ڄ(Jx ȵk~l&9&H$QG3gΤ:A"@<힍~O{ &c#w~Fφ(e :Hpَ;75MPTQ7x㳊///7~QGܹs9H0npvn1&ZWf7qK- T麎@ alӦM;EQ:۶[ٜ<#F%ny)xڮ,]Ed2WU#H쮯_5s/~ @օÚF@`_xᅟ br3@" ۶pyhhI0_xد>7DwwGU縸ዩ0MSڶ-FqömDɓ'?r'ANOӉ'Nu8 eߍ8OD,k(ʲ 1? E?]ޫG3м .uߏIx&nI0eYxbHr9J)SϿa֬YM8Lăe@Κ5s9z0̅ jG}4[E xOTۦid28 "^EΡkXCawܶ{{w(-%5ز,b`N%\rJx# OzuN:1~!' 3t$(e?ubL6>RRV.:;;Q[[{w`@@N>h4:AJiϜ9S>don "R)$ɡg#=Ws#ב*~21RʔWEuTЏi\aq*s.4M` \sQ` O:oVVVH$+_h WΧ;鎌{Ŵ_RUy"G-Ydu|"XAVVV.""֊`0F$

G?z;P O @ hPO 0 cXⅻw;u`n7˛M#"`ft~1Y˽XUB#t]G0D0Ҫ뺘0ajժ/_U( ǩ`P꺮z^!$rܰR"V9PJ{ E'KǛK+pʍ /^9aq:ff۶˲'naU4"c( 9:=ukwΛF%hR@àr"FD(.,Q0`0ns1-\X<* bR4 R7X7¼)^۫ 2Cl-%RV +""9a% ~EQa, P0ŕ,b(d2vq~"oF^O8ϻv+LP*7@I) HueY|ꩧ^ cFdi3 @!+ˍ-S\γ~֑Dw]{#ݞ_g,W)|w HUUB؉Dbԩ)65*}*@:KӽmiEa%CcA*R 1G0dիW/7oNq`f2Msҽq`4'ִCp=x^wݥwupC)%=]$F>(1%?XƲz$(X0YYYytMMMi2)%r9v= Y%{Gh ~Oܖy7~"}~r^8w\L0{֭[wB2s#JbE6goʔ)*D.cUUIJ99-{n);[{Er378Nwtɘle/JIpggg[^Ē0cMd\n37gΜ|Md)m/ދ #{YANc}\Rۏ 7 XXc!MT/_pWW!I)R+(hEBGqaǜ1ckZY<ׯ~/und!{@nCo?pnvu?;Iܹز,i,]i9` ,Vg7mڴ4o/R_O2Jv?,l%绛E㏜l衇-N$A%R!z.BawZccc2+4͡≞ P,wfK|#[xzgdd? %|J~>;d2Ϯ\~(t28 6@0 ;̠*j۷y-mmm=L-ҋn$q8*BE@a&K 7"nsKKYvތJ+]}\.GtZJ)Ŷm|[lNHQͦp0@-K2b" 6lhݰaKEDeYYYEEEEe8F""W]ׁ0ZwG[y?$pL*`$qh׆{#SmYuuux.0M9{I8@(R6$v`,(x *if;;;:;;_jǯ4M !DޠN3 @Q\FR &RjϮ7z-e\.g۶twwwuן_'" `J)-2V 3Wwww 8[;PT"҅"r\IEr|^L6mF6 t!&p_rd$lrv$;+PFrc*2U/~P\}cŭlА뮻ijjZCD̃R(TQ狴H†W[s[xۖq`g?@6 BA P^̧"b @hϗW{d26_t1 kznB,nڭpUUCӴf gz_Lٲ, R\9e"⢪a'#e%}Q:ƪU,]T*V7y۶&Jh @>ŦU㧲I"\Ձ1 b'_DH|F`ߴ| wqolY9'9PM>}G0iuݙdSr zqw Ð\He˖l5k֜p0$oe  v֭[w\ressԤRiF00؞~w%J`$;B(Yfp$@ -XoM/@eeeY.eY4M8b1Dn鄂CW a,E!۶fY4ʾuG} 5v1Ɋ'\aÆ׿ *++kjjƍWSQQQ@@4Mefd2niii޳gOc*DAj fN94Qζ eӗ`Z/j5Y ;xlbM_2UU-)e^Jin fD@mmm ,8y Dѧ\N_$=#\4H !Yt:Ҳ-WWWW'"mal6`)b©%\.7\l&" d.KnuBBB[3ǫD,^)f6ό<HbwJ2$z#r:kRYgf @* l"*P(2Vzrƍf͚`ԅB!!$ "e l&) w޴cǎ BIM>'NO$eEOΧ,`!M6ٶd2;wlXjʆ~Fl%`b,4Ξ`,"6w l # 揯1 Py5e59\-y˲r(u!FD* ARmpBᇐvvqbB:N[uֽ\SS3~ &7n|8N0 tIWO&]]]mmm{ݻk߾}` "MMM[6xuu &L?~X, ÚQ IDAT[RtWWW={wܹYb cF!(Brq5dEf?? '#OpLmLI恖*YψgDAz0 ekKK&(p8 BUUuEQiaT*4M3]DR\0#8^XG;R#1}2T/A DׁK-:n Ğ/_`6 N?*+\H2QP GNDs-{a{ٰ"‰(@Dfl X#t@m;L&d2)FD#k8sy!xt3@EEɸҝ׮xMUyL<lymWHc`>qwJEW퐠Hٱ3I gK`fg ,Pr+3ň,H73;1 0JQGTQzu9,֋mb78/%.;(5 D8bZkEE"JeO~0B ;@G,'C= { @: mfi`DjNqp(n]궰b#Y|>OZC ;Ϝpźl̕?:|;O׷򔫮M^<N8cC+sp-3_"Hظ x_Y*Wu`ּ%`m"˶^)e3#>}Bo}KKi'U?d[^$#J7BKA["$[7H10ͷv&9WPhv:.b5{ל:%2.j ;i_ȕw%{3AvR dakuwA(qX(\C(78n`povPzSVK*ك[]vqIjP*j+&{aCa}B{٨X+, 3xP7oKQJ?+k]]U{tp}loߦDdˮiK6RX24ՌibRVl!sW\b ~p/zv̜";tmߺa/ۤh 򢪲x ^\V 2D@sC~o`S?RD ffsφ-/(GmB lS"w?6}x@U WW,AyO{8w՗+PD TU\'p|yGmwr jR=m}=},^E%_nxeDZTTFo4lvv@  H+&R@ 0 mj@!zWJSFq_rM_tgtxu^jb;8@OSF_ز+꧌h߷O/j9}[nyƧ~#EY(Q`om(@VPfAO?}ۯ_z?l5lݷ}2)᷆ 2`mgëNwNiZf0ko \7VX({zsǶ7nj.=@YQc!in:4TU-gaDg> '>TsW DHڶ ([q6-Mm׊ܻkr :@1l?<{{tcGݩl!U/-[y&A Hg]ݒښĸB6`z↔ SMsw m@(@֔d#VXжw}(J,jYfF8eVGwkW_}yo[)/K?ʷX&"\*b7W_n %䅗-I&A%*>z t.Q&۶=oӧ_0H+c6v^Ӏ7҇!ko#(s dY{L>X!\/C7ϴdgN "0%>貉''jC:c_GsH'od6{[wzhݏnśFcN;(/>ZV},GmϞG:Fx İ?c4P.\^8W#1KQՕm, ,aQgqe<3>)^E"Qǜ%ȶ; W9//V2~p*CѼ F,K<ďݹ>TT.eG?w4J@~K|>UnE&l+V w"#6QǒM @yK0_z16ù䗔W3xڛhҴ8_Y8P,; N_\\xi0t *D,|ż shPg(}o[EH"=]>k'E_:,2IW=Rŗ@羆 M+m.0|V yx|q\=nV) ?y~G )`giD6`!{k;`;FK eJ۵+N|Yb֍wu7e@ n} ipzv|Q8b%S_8R,F KQVߴeN73؏_>4ʡ,{>~k.gUTVM\6TV.KڲKwVcCkV\qǺ/m= dF t Ur|<0D?|o~[V79wG x3s=ݐm(ĤUvn)~*:ӺvbͬoxhoW;(cY=f]m@F7$fi9#m<+[ǎ_DWdO'?9S*'ojMVWHY@=@&X揝v𥧜Be3MV nWt;79ϑ?Mwő?N\IhX2!Q "ll<[Ƨ1Y$BQf^&wz} D_]_VE".DR2r8 ~ƍBhn 6`>Ƌ*D L lwr'nė0yDޔk/Y/VD:pTuU|O-Du4jl/TT &;Zƨl)UK1g`P mi(DJ$w'5a"?|Cxom_oÁw@Sl;QoD=H q 3ߍK*aufTb%¡e˞כ"|~4cDlW Om\H$ɑJMk`W[L@]B;qCPT-*m[٧}ŏR.׽9dF$av= =uǟw]=UݪU[9i+si@RD5^zSMY8*@ǼK0eq)-Op'\9u]ܾ^d W95ӿϷ.;7? lsq@klmD?=߷:svowwcs=h-Da#w 8m7[q9VM+mڼg߶߿|A7=d(Sao ?USa6%HnivREս{~_/`\'3DnY-b!L}kvx¾ĜE"VD7`wu=O`v^ ?# Az8=,D4fy64 g  8G9m;w(0-6T^P;[MCU 6n)8x-۶U5Jgxm/ެat %e~hZ4@ XU6sL 8daT8 ; &WL{_jZaš⹉#<VM4=izETr552eDT?˲QgCcGK9pwk4'U ]$,l*$9x;KIǘ5 U/$}M{;m[')͐l[Wg B%|Y4k|͵o~#xsV1!;"eB=?_,#YiΑIWLs޲O>sk]'Hֵt+ӵc\_瞣ᓼ֯%}GT/oDq"m B@ %>Bp(6x5զW=7Fu˗.O~;|.ɐiU*z;ǣܾ?j۳&aDSLzFCD ݶlN{!}\3n?h;yw,KRY.DERlmh[Զ#bL'cӫZ7LeFΘ1w]+J fuO}|uuE+wEpW'Y*Ѧzp^]Df׮ܸL)|FU?bkwܾUVrݣ20(X6Qx @w{,6e fWJu` hq8dU'nxtv&$s@WЬ@:o8|`*l,Sѿ i*&;TB4e-^c_W|p[oVwt5RݫvHk>U-s; p/M3Qf:9 ɗ8(wH!GM3,DV5_ՒHc?>/ȫ滋/N?րɚr=Q8@жVe26|sLJ3>Fɱ|U91RL^uۛ:QuuY p=>8=2 |W-?Xw %)p w-Rf۲e7^mj`xn5sКR@ aVe?u3xSݽBHJ4ˮes_T=B2@4ZmJEB(eHٳ{=rGj5Lkq]}/׌PVaZ ܲ3>۔DWck N> uYP9T[Xn{ P;dUIus믯({K>bvHd:=8PyvE!w(iUC3P+,@&v6E]a[~ۻQ8f$)5޺N]!Q+*mf`VڃL{G~qgz\r cfVI$Uq{t9^a&V_ Q-ÎHؖ0)wķnp3Je9΃%UXre!ɮnAL*jD%up]/3S4DjG~d_ ?m+C͛tEJ;o6xb5u'e[,+ ` .`k_[|Dgh5Oyҧ>I{0볟a>˨r,@Pg_3 lAKIDATG%|IY=8۰өs#]=y{O,XZDUk{߻O/З%z׬=o#uY#EjzP  ݹ2Ho~5rևat(}X=fV$4]+j PQƜoy-uF!jwN%nvJK ~f˓\ό7m;M T4=hD+gh_wH2x}C;$΋'ŗf~ kѲuܸ/az(_?s,H >a-{G z!ot,3-f3oVY9Yxw#շ/N"޾JF%T7{%/5hU#lmaQgFwR!zvfәc` ( Hl?vp@/g>bH5+aY r #Q ~n_z%Dd@7l dQщ,kՅXٜlWh:xB)`SgƖW;v("1Qʚ,VҲhÛ[BD!Tۺimb"`t&Ѳ^FFṞ-{I=zJJMMpp8ʇ3.;mɪbKw5r&C[-U ۄ賀AT:YQHJ]'/iPZ+7rkٌH @>z+XJR:!Bjk䭷tGY_(l@IO?}g"XXd7DLl ofTx!%l$Hw0@b op%#W)~=h`ÇXX^0\1a&DvNNK_ec 9pqG>'W IENDB`ukui-screensaver/src/assets/weather/45.png0000664000175000017500000005527115172041035017471 0ustar fengfengPNG  IHDR>a IDATxyչ{ϩmf@WW`W͍&1hL7Fcnx .QQ %#2/]]y?}Pյ9I@?(BA2pg" 9Q5@`f(\)&ASΡ--e1R9L4aV," hD,Zد٫C9.3LS5pNɔ;& 6.P9(a"fswz2+Hņ 17:k D9dmJe;M)S]@ ymw?7%G>y׼r2zy\\=|Cyew@ }tYWSӈ Bb^i <ޝ4UXN& R! & % z% cw͝r>6€'/@ =VNJ P= ƸУMp~\Svl(՝,Mq3/ fVC1Xb? (fQ7u%t)64 I$D^#H$@k;f@Am;hRv).{ DMH "?{e@sCu9Q)Wùw+r?DŽ ͧk (e;`E4SYP2FH(D@GpAmP`fVp|紃OL$|nW>B*L; ̊]Uڥ:8u:7 !,p %N+r]Ŭ 8 8ۖ庮bufa>qRd} 9l+lR. ÊfŶb%DZQQoZzB@D#RNSш{|MXIk eA) @J0(۶v\'IH bvDi=OĠXu]ȳ1ڰ,U̬HD %)NRp\jreYJ9R(qk;찐nL3c!2E&miEͬf2( Juڶ8n>2SrJ)=E&.r} zJ)V RJ:;=s9GQQR`N׀D"Rd@FOHPhā{c%I袊qTǶ% Tw\GE@:)YZ BW>H 覲A#1v8F&PRJFOpNH_H7nD4fm8t4ͣO x{ē ۄe9,v;b*qn'x#41}w*"AJ8KND76=◑1#$CwK:׽j<28:hLW4OxvY-R."f&Lۼ@>QYܹF?;wmŘq`[ c+/;.ݻɿGRF*=‡ӧT%DD>7n身O)] /܀hd1SYGo]0lËK&lf.$^ՉSq+{O5864"MG:EHLF9 qcb .+Ou/V+WHSOy.>iǝy, =BJ.*gYYwnܹgX4*ьam<4mۦY%XwgmfdQSu$D#竅||!$@$p1f`a$ t;lg.\\trq?֯MdY3$NټzY_j]Z4mK&}iE\KR,uw~^gڶ+2W]@4H [ꎫ)O#zt8q/ P h,'8iҤ8rE8ze`fH)0-CuU:0aB 13::2x7ʫᣃI8|# DFT bz4M Ӵ/מzGA, 1S?M 4BO8K$"!DUU(ҪA(j$KrG%, fw{ Zf%y/[̮X]D:-Y#m9~~5H7ܰP_ w'^<것eń$#89IPɦe ϣl06nl駟f C:GygWqH'kԩ# pꩵX,ðe2zmx5rUBDAa|/_%%q\uHک0 }q!.T[۶{|=R@TEW!Y \(B9= ~633/˺~yܙַM[~* )!Qg˩+ =-ZCuΙ5,_{ QR?kUo&4] "#YD(U~Xc7?ƒSUU170b6 *NW9?, LUUUwM2q룦M^jbKS/QgK/6`sjଳFb[Cj5TWsx۷=cGӾh޼`fjmMl CGZp,I0$[EaY6f%^}+i ͮǒ;؏X"h(帆G4D Bm4+;$躄 "2v %G%Z$X;4Mq\mGAظq?n PZچɟе$ RVszC>zF7H9/ (5kSkƌׂ?a%lr>7#8wh؉F p Ŕ zO@UtW`Ųe[r X]ZBT-yO ^!<) 5!& 5 H/ɤ\vցk"03ߘJO-zzMoGGQ!cEM9Oa"z^g*/脬T}]Hb1eK7#nAqX,Gpp%+RYYDD_X)BE|Ve~{dpƧŔ/7^|q,.pfFGg6 84`ހ֜-wψhFUE"'-R) M):EhԨ"Ciݼ}!hFQi`A4vDH(c眑9)?Of=l&CGmf*w\U "ϯ`Łf7?X} @娝0!4I;,0+ݻrV ;k =gOHDèQ`YMyʇT#ɀdǿ- P 8!NaChO߆yF6kcI9@ʹ ro8 62L,bdRYiw6O< @5,]@5D# h3<9B Єb$8XI(r-Pp/"`A=y @c О"dmB$B8}xo eId6" / ݌ S/C3'g ) ٬ 8sdQY3)~/*YPx@!ݕٟ | mߊ4yM<\/3$l|M(JV[0 Bvvfwq0&!5=#:(v`&]&y=5 =v譕SAIO! #\W_7=g =t=tX)Kz'f7.ё_ 2 -X5  O~&H] JYSi_0lō7lA$"+QhP97}8. !R Ȳw,rp-FN+kCH\]i'xVN "&$IG7|QMa0N.6 yBc:} FAu ۶!^\a¶E0x8^]455f(Tz`.CaY᷃@|I zmvzٗ\rλ3~GYrxFr._u .HDLFQTC(ڋh曟? Es Q 5o4M ;nS es$!@  @Y^LJ P^Y Wu1 QR]Tg7,SS{[`:z}.Ɯ9 z\m9)hhox$6qp'ۿ̤Ҩ7?t&/̛q BPnpY)45y:a.L׊իwg`玽κT5sM &=#RJΠ݌ `R{Nh%v)`JO\Ad9 |5UrcɃˌ"T)S~ #VTw@㦝EZ_\c#41|ʸ+Ep\CG?wܵPe (VP좴"0j 8bO@;S/ {?_!T %`*yb%ޢ0X$4-s._ yK&2ԕϋ>Q?TLq bm}&]@_y/H`:m%_5H,uSxѰmPpqW悌bt-H0thTױ@A+sGqM-R4Ҧ GclH9yHgLM}GLsR)L<".HABfգdpR42Yi3 31eU>n,9btBUOxj;uλl2f73뒦K#BFL%qT#X3_Ø['"xj/~gDT:_-ڨ ) |j dM8,3!R.,Lg7\ǁR #3{`(fW Pւh2!=ZȦ3>V\D4]U:XH "Q l6& A23\!`HM04QUp6 HI9i f&庰- Y3î lSkoO)f3rX[ vX)Gl+W*u㘶me,{37l^">`q9 XM 磅ő/jXhhp6ϛe&([AJ pT3Ngq{ &-V4 Y64 #3f@ CH@{}E O6pͬ}Hfpɟ\iHēPʁcfSiNli[[ۚAV2I83;2`VX)JYm. YQBzb;ٛW.01ޘ035H!3x\Xs@wcO@1,A\#"ǹҏk‹KAD )u/s!&@ )ymd[ ضfȤ2iQ;OٽSt"6m{WƐBtTZY:ُ@K!{fOBX*M'(=0r@Ys/zr\<ZAq_s,@H֋A&=D!k>oA5R@ \׆"cf`id2eCáVK@\ 8-} PN'3(vr"c0 ÈzU]]=0"1mۢapQq;w H!|$>Çr]B[|8X|){qlΰ0.sR s-Bkg{AK-d,Z[ӯ=}\?eʑ}m;{>0ߞJK>4D {9 p2=` 4m{;LD&ifٲdY6۶mS6ifJa& #ySryԨ8NDJ],`Jfc &|n5 CVUUqy;Jq&>q{ᦔ=\9b" =*'|-mضl6 ˲8۶ q^qv]]ed2417_2$<]\ADU\BD3 q2eee;?qA%%%6lXϦ_8&p!. mp]7w-| mq6MS;z(3gE2> IDAT~ /\$ F@3K۶O~o}}Hĉ)c8?:0Û: -$SHzgn A)[8ėf)ɰmNwww^w̘1> AO @@?ٱXl^8WTTИ1cHuK3)@~_ |0 "^b!Ez9L;9 p9"<,ɦi"NS&q,ˢ]vɺ_\?'A!*OǏ/|Od-**ÇÇÛNlf"|!'UQ,;ViD PqdYxơtٶm{׮];c^gK ,~ȑ#)--X,JKKL&a R6-x?@o=֙=˾z@X"zp+NX] 4)軦ip]oI$A$!]Yuǎk}w={p|蕕S$h( A}| `` 2 lS4H$ҧ r@`?O'DŽA-"@`P^~ V`tƌcp.Nq_!*Diuhv"qg_(:4lP ]'k@ <3aua "S<x<.i=ЍYq\i h̜44 Aw⊁/CX(^ Xv UBcHh_@ q]ǁeYSNQO@` "HgH D  lsj!9kaIЗݟ.޾З*dם/ " @,X,&RΫ?~>D\({)7 pdmþ!"+i @ߏ2-bD"3gμɧq@2a۶J)MDcР.+e<^ M-^ E'eB R)SN=&hx"ٖ&_/ D!?=9>p _LBx_@K!ÜD"$ OBB6D$~ W34̓ 7t4ٲ,N{^:җ,ϝRS~!10 E@vE{I|@H13x4&M`@DouE!D&aH$rЗ> W3 axúA#;|xr/|B7*uݗn4MǶmύ@Tm(ڳg>`ϧ?JuLLD,C|yKHX S1_"RmI&UPDe_Wަ>@y;Sߙ%KP__iL^+q 81\_>w؏*/,Bi?;<ھB\X4 #Lu2MSY۶iJضM~ y B^et>?2Bϗ.u.c~?P*rmۖO>ҽ{fflQ0OH!${Dwnhhh8lFUiii 0ef5XWŎbE~,X~路pi_r5}a~}CB: pd2ݭRq'x7v !2X zV kA&h{{knصkWX,VD8 _̙L}qlYdG&o/OBaa1^H䫹$A!O"attZ),Xx߾}+"RDa4>͂Kť 0y7]ݻwl6+ \J)L&Cl!u/  ``= fٜJ prB6N! 8a$dQggKDŋwˏsRB/N*$Bd]͠G(5XBĘ[ZZ[ZZ^ZJ)JJJJKKKKxضmU @Yx4 ? MDO '}V ۶ۓ foJfaSϨ4E>&L4eʔnN|v>#>si1º79Gu^ضmm;@\z(j Gl6\וo <ݽ/-xn'+6wЬY.y'_;v}:th5wO}cذa]]].3K`9''P՜fdNs$@aIDQD"`2K2MSP Q|)9 `fjll /ϥK @?ix%"˧Y8E;ﭷZq^|WLۿk]׵ Tk׮]!s93av֭[VUU,,%K]T*)ktMo3V4M ,t)lhh<#رݶmCR \o79C~\%Ktvڕ]dɇ< 6=J@k h0/ /-3[X$L& ڵk;MQ">PI-tJ)]tw޴"!.15arrDR Jub17` !aD"Duף|ЊUV\ps{+** RSWW7tРAd2nu]8㚦iu߿;رcW&9 ~6 @S)' e*PTT4/4̬9rPuuuM8BD徘'lUQu7|m۶m ?5iҤ!,O)x g(daݟd8qd&vܹ?*ݹ`V,-6 #cPo fw,YzED%zKK=7o !DD؅8eYO,$~(i;`D$oURJu/˗lذ#F5lذ *+--$ ʷ, ={ٴiӶ{o-^3._pߊ{a0vxgk  QVVvF}}oVZnhڵG=ZQQҗtVJu'na/PDDI1_u@SJ#iV\QQQUUU54H$xTu)J)eYdݝ---mM^:GsOuBA1V NjKKK˒dI2麮tvv=z'@vԩ֭[q 9 $[&I@x,D~izDl (kkkkkll<1sI~۷Zx""" wu_Y""ѣG׭[# 8Nȑ#9#o eF&7IR:s*Nf|1HrC+**Jvv=\uM{__T!} X@@\p-CׂqMA*t-JȉI)e3SJ~k_8… w0Ḋ0 zms֖w=ݻi͚5٬lٲif%TVa*x OB90g|Ia׏={vvvvVTT/]t駟>]jGB V¹ӧO=4ϝpWZ?9B@Ji(ZΝ{} ˅ھ}ݿWϘ2кu6UTT y뭷6^zK,MMM͛7`-h4,Y,H'gWSN=kРAEEE9s慳gūnֶ_>t6]/}^x aU@ 55u=WO>{èQѺm۶+@}{߻_͛w!CJ;;;aÆ6oެmذy?_'YODhii)kooF!vzq5l. ---MoE=o|#Gy#cƌ)}~|k #Ha)8x඿;~_+ַ~e˖sTqqq^JM<*ND?h4Z@{wtCjȜ>Y۶*++QTTy7>^d~{{K/=/޳gOm֕W^yݭ:^`͚5 MMMm/Ɋ+6}߾Q)uq(%{jjjjjj7pW_}MMM;󲒒I͛7gӦM&M4oؽ{7]nذ?oV\OYQ"`,]yכ=?O3f򩧞D.0NӴJx]({W=<C.nv5-[8޽\h{MMM?Ek5BLpS# o2J.3 c~9szѣGÓNaW^vA^zu۪UZ7moiii᫮?z`"]׫p:~-~s)+HhѢV/nؿJx+{ctgΜy?iz>L1bĵW PG8Ix g'w)BB\4k֬_޽ٹsgo0xTVV^idx1 ?~c=_8 466555ɽ{f8yWr-_gIk 3wQQ\YzifUEE "5:f\ͦ!jqK'L&'$Qcc&2s|V'EdnlQ݁EEtzwhWw}:cnao-+y FT9\.KJJ ,--wM:xO.*:rFN&??/8@QYYauKEf\uy.HZN'@rrǎJKKfY֛8kCCC0/,>:f͚FcVGEEpGbxԨQӛ.,q 8I$o|x0{pA@'L0kǎ{bcc?\B:f̘=KG]v @aÆeeeG5444|qgB-uS/@'2ڽ!.{{[!xZ1ޭ wܩ7rsssCCCO:uST* B%MJJSSS_p\A &3]V^+ /IѴ:ulaaa5*ZPx Z/Rk׮%'Ojeff:3c ]ꫯ }wqu{(a%8)#:1BZ!eݒ+?Osqf~ɺO˹/9R\6,Ydl~~~ŋdɒi&L_0u[.Rh4mݺ/Çׯfa˖-/// u„ # *_xWy'"4 d2VTTX~}ĈtO 6PWW*q8_v-O֮Kw BlN@)8rȱO5jTիWem{9gϞFu*[8yfffʖ-[y6l6srr_fΜReee~uuh4f fB&+lQ]ApYnnn\.ߗSN]nfl\.oB G;7( 0'd4E ,`Y H(KٯMG{ T*UB0@:0 7n#G. `xT$GM vgY@{}p8hSSmnn6ͱk׮aC I__"VgfB02eXܟe;ߑxq7'0 L0L?!XB8 !!J=% 8p6.Y=A8mXfϞ>==ZSS'(''RJu:] ׯ_YSS.CF & &7TTTDqO!d˲Q wIa\㺮 0XT+..n҂|pBMRR!^o7LӧSSS+Z[[CYv>UtgMqn;pk ϝ;#SNHdDDD`|}}M{%^RJ[9c!l]` wMG% C1bD vy[4cƌFGGOl6̘1cSjjݻw˿zqW$;=1'uJۧةSzl://5+VR􍈈t:A 4۶m;^x√G]wW'ptZ!eo@w#h;R FcjL2&22߾}5eĉΟ?FKK ?w܉ .@+**LNS;9&&湯jH$_-.vߊΝ;W}uKAAADVlnnM*`ܴiӾdW3wacƽ饮?M˗/.//H)VCC^z٭N'X,tD!l ߿ҥK_OpBYvvjҺ:{YY_xF&MLOO|ќMrrrƍ#oZ=.# gqVRJS*zꩃxy7ocXren_~\W?3&dee;6l7!AP !<&رcU[[/gYZRR.Z-Ș8q즦&{KKKNkMHHx?""bPZZZαc..;lfᇴ'O333֮]IffuM4iRn'ZݜtTUUtf~ӓjkk FzgS@>|^xpCr bDBƍ7w>|!^[RRj^᳥R7˲Ⱦ?xdYYbϞ={6,==Ϧ@BHFg577]6L~>>~ .{nn}hnnv;(JR 2Yf2iii'4Mnh¦j/_>=>>~^wdffF\.666wܹO,ߝpDZ xB<!'wTD"G}}w򜜜_~YF֫W,,,9z P@G )Sӧ?[dvݲ} G>Ӄ<7B0 &L+**||||/^xٳ)"DƩb~+}nr3^vd\.[rߎ?4B 2rế+ >?ȘՇYl^Kv}%//`y_AMaDD*LU(KR!cY @W}yycc!$j_f\>pd).(c!P&! qܭYNRáx@`޽۷RTZRj$`F뢃w̰q3x,q?0XW>10-D Mt=Wq1H4V$nGEϲltx2 tq)f ø"2q|= gB<ahdjż;yB=N:};"Hq i[bqpuϳX,gCZZZ~ݻiӦuhv@l~ B-&aRAwpadОH|:Ͻ̅PfZ[]Ym8 u뾷~n\tB1~W dgΜ>} 0ەJ\N(222$ǏoB-fDpDZ,BG3Ͽg'X:0 3EB0L˲RϥayB8x13eʔy>ZBiYYY[AA6##ʕ+ڻԔBv2}-3E횵^1!;b O>?{F'~f/**j$>>>҅ .NHH`5Æ Sk+cݿ@ʼY1 )zRSSeeWt𠠠_f hHHHK$+WȚ5k:4{wE#~ 'Dg₂J$[[[$39///[RѸ н n۶K&:u3ៗפT*e !CΜ9s^uu%ƣfxqMu:]Ullz}cc;11A?RtڶFؙÇ|fZCzt:k߾}Ǐ/V?~$; k~;@tttСCYf=6v~٬OOOOpBPYYt+=}tZdeexxxxy?D"ay3gθcǎtka (TTTرc>}T*Uq``OHHŋKٳpXpkOagHbq@jjjE||o6vȑ!{M۵kWL&#)hO} >ۜb?dJJJ|O*++6lXZVVLaY6ߜ9sf,,l_KuueX ѵwH(?jժCzXhdd;iiiA=B!zBhu뎏73ϲ,"BCq{p |ZmOG}tԵk2L~¬˗/gD"*Q8ztE"IOOϫm$yorDW߻y?AXb8p\T BuW:;qp]qROWVe,bjZq"w%hQ a/`3 cyDqW*ͮZ}޺V筮ڻqnC51(6 Y^uy;N @\pƃ.RNYWn8un}{{7Ã4R.2r+q=t/?D lHIENDB`ukui-screensaver/src/assets/weather/3.png0000664000175000017500000005312315172041035017375 0ustar fengfengPNG  IHDR>a IDATxwWy7{ך٧XՖ-K H$|$4n.po$$@ b7ɒ,YnSZ3#x]3gښYZ)΁'St_/4ڙ[?.= 7|G~bt=yV~齳Gu˜_o=FH$y>3w}x/!t/';^ݸ޲KoPS'߽zuL -ؿS/3O\tfg~Ǎer3t%?yw飗ftyS3Z>[?U ?wy"v,̖G7xH{K9ީ6c}衪t,.-8w}fgHX\%k/WlKvjK]!+1++x߇s.ֿ'WegF˦yH?( _ڦոW~$*hsn>U+]r䭹jWRϳ^z/?_H[돇K1 _y?s vǿ{? V@Bo̮+vttћ-!uw~uHԊGVmW/JM񬡴 ?#) M]VyU9ĿOW"`k?!G_PZNmN7j!kđ/Lھg/1 )o43c\Ur0}Ɵ -~"Ye+ ޼ysmʚ%5t{"yFGlo {c>oycUx1Rć/R87[ {co~3{'SZ'VVUaס=/A= K;o6:H$O+ny`rOj?k5kҎ\o'{|3GH9ղ*Dݯ8_!=E5[ oYV@hjΊ+ӞHЀOvMvN PMI%lvּw):k\j: $@ѕ`X"o?]S%A VM%M U"`N ma6]0$58vwpp;?|61g >E݋ukf_s¥4i'۷~]~hla>\ׅ8RBf1Q 6";ob?VqkV|o٦Kg܇*.9 XaŶs:\\6& $0'61Z |F#6`h'1+I#CNAf`ZZݷuk={km%jr%H4?߾z`?Id/l۶m?:vد! \.q-4BضMeeY603kb 8"AoIsxV{݁#Qi^w0ydL{ ]꼲rNWWW%o l: H1S $cJg 8KHI `Z":]n={ģѝ{_^kҨek5&<5p(;..@OJD}wja,KT*j5j5J%rR.ŴZ#"AvfɭV A8VlfYS֭8OAc=@zkA>$p=ΦOo۶+V@\VmK0Q^8H'BXqP*000(ncnnD&Y&Z~=O?de4 N]:bQLe<+J@@k*E61DeSs̆``bDBٖԁAܲ(8vAl@0;vX}rUVŮJa^j 2)7cLO=pcHkVeo_۲Z$5HJt)=#ДF 8#IMNf@`$&i0F7Q dm.clJ,AxCPG޻Oq9M60FCCJO[ufe +: A@abHw\MDvqc)8#~ddLs` N'6 4HhlY*`(B@gT[l@0㯼n].sE^8Od(099v8a35"?AKI:U#J)hƾ#1GV; 6o<4xaFybSV*aRSpJ|Ď i@"b8btupw>m<;`k^/J<֮]CCCuXw@?ѯFǘFEv={ eAJ f8Yov 0DEPZA) H{fq3xcW]>152I@p\T&D uFzR@$5t b& R]33`.7^lqrll.]JZ8R FvG|˲`vжmcxx###"S V "6`m$@ )~F1lrLJVXL (`nvG(N"p]RJTURπ/m_F|`6FFF066z@3d۶>1??4 ض w0;'>w/8 /+c"h0 $2 &`Z0 P3"&F 61بԱ AM_?𞣇+/\9[~w8}H3`=V*}ߔe9< ,gH ;ȼ0!&!<ʈF4g9Ϋ_vZmzG5ojOxge=$c?~Yg]mY9#*{GlF qzw]mc||CCC=`(>2OBa8T3}|~] aH7扐 P6gϼvΰHIR[8NksʈA ٌj쏯nᕭFY0(24 =gdɒRrL-WyR+Ȭ%K`pp' *яZ^oj~\6ZM eŝE~˲-s2/W$ga15L(tMBY0*)RIt@d!'eL@!b KS㮸g=Pa@S{=e {lG?ѯ^~ !Rd-]T])^^wfDȌcݏXO/*8<QT*!B YrJc+йX! J0S\QD# td9w 0O$!D"}n 0۶m[}vV^;6lkײyqb"(B3*~^ ,TbF`>@OXu#Bj'"%y睃Ї^VWu500`_xᅼrJ./G.E[^ ? l ֜Ԣ%]i_uco~=C/囇wJk6B_qr]W{_4'Q~ȒC}glg"h執XzX>ٷܿ ^ NR'@&g_$k0F8k MaChӴ$;ΉCcG}^ uGj=qbsV[l?LdŁ?uyEu}E !>GaQgyZ'v&`[I5Hl4D4k:rW~Xf) ̜Lzz@ |E?^`@sH􁁁H“h6jΠBXH49!|TfhQ Pкj8~'S\zv2//"XDad,!y'{HPgb=Z߳ߧQTLl Ub5eFW:յK>r5&) igID-AԹɘ.5$;={NAn yB-8(}2dʸ8(obzt7bAx-9iYBB+RJhڃ첡yW?q Rޫu |)efK/䒍B8$"m(_t33}N Dt_1U48?_<?~EkKa\%khH)tA:H3] &"`A~sOV4yOget!OLad GvvdZ uc,-)Y5%T[@V3]߿]o?N bd9k.g13nѣGr((ؙYYS|A:% Rߢɫ47e1;_Lgci(=x?GeV5_!ys԰7dR6$ Ij"ݔ*h{+ٿ/| H(dFHd`(NUm۶mꪫ+/XjUR L'"ā.]~"7/VwBS1H93B3FGϘqc9ɗEbwx&rp+Z8o#psx@Jhl\~7v˖-jy8f! 4 9@ hV j&uarl bLlZ^Ś~DRnnbj][UC14 0X 0X`4gAdӲH5,4N 8q\~93c/ $a$7;رcjZJJRJT*z88|ϤIljlE-[R6_qZyv~j%**%o\8?;&[1=6ؖBX閬 @J0qٽ}Vg EJ!R`ὃP@oM,2=W~79Q̭y%'^Hc$nl6M~\.%\jcLfR"w~ܟD“T@f-0/P4ԲJFn<iQ=Aqan>߃ގ%"82#"E]pab$K~n:և]\=kÈ  tYR(VW%liõb8"D%0q񘘅pʂ! dK T 9xM$Kl|2H#]x9cK9a@`3!R .H Ufe?onaϽ֚\f1שSvNضS !;oA8EGT*ɂ-a%!BIyRf GѻAT $A$"Y%Ƙ| @_˷~z0jf)rO1:iGq刟UI2zonկ~gnCk8QfTa#!`XIɄJK*:b [ARM| dQ&!$,C۞x^ϣ>І1Ol(u lߛRJٰEqvt ^AIj!CܰtQ"R}tLPU_6ADTz/үTgsl6Sm|05s'=n y͚5z7}[9r{8 HYLA$# խb>g2,2Nlفa,Y @Òl[²}p|r=v7>uK{nx Ai*6=]X6vF۰e GpdՁg:-G] !- 逄  Ic _YH7Y"3#;^Zsv-|?!`ny4ؠ+Ƙy @"|Wj3QkM*!ѹtн2 {ga.Pq+5%L;׌_tH̳3Rx5iR29Ljäj%+v |b&OL~lR&@T2w mSCs~c* L$@H][N\ '9Iaf!{ǡlVO#O: o? #tf'8p㷼HmiO~.N :6S$_~ n|MOڒTJ@9f4Gw r#e I@F3sѪ-DQEaFè$ii#`C'eoA!w%s6ТװwIN@S`Pa3 K%Yށ+ρr t3Q2+KN_%scǢeN! ɻ86azgM%Cy{y#W,Y(egwړDGRT?0@vj2HM!΀1`~KFf0sV6`ljJR]j :1=2 $-DWe,f;!:?Qnu=tcd~|R{̷҇F'W&+N\$tXQCsAu=9JTdZd楀[%xN@(cjKRzXrNVq)yiN*fIu0Ա0{ s՚U<&خ]LaC#inCL1m~p{H Xk"PWd{ )'P[ .{Ru Mm6W`&' -Md[|]?e 4}cl:Oع}ߡ౳FJ翼Rs7wKcTw|yֈG`"3 >oLPU`lM`mt`>Bs=#˂opMD̑6ϷuLtRg/F>gh*Rm[gDd,$@N~Ӛ#KYÔPzt\̬<`+j߉ A24JU'QIn:Vhy26UM^Z1z\02-^%@xÇCe^)f ic~q[w^HGAwGu>P }'rތ&8F$LSʒ\׊X k@pC\  D `Fg!09 58qCUE( Z'|1 RLd[[ Ud _E2~> P]y<=HX9NJg?CgI(R PQ8@XI " X?vm,w$mVwCgltiTRǶRrsΚgi8l)#"{'U7?lk{7b`s?;,M#_~̗4`˫H|`/~DqULg5dDs`rc!2ƛp$`8:{lB<W8[w]5쮗絛Xp;;mt>HMx"}BX//^|1|ap2_ǻϝ^6LӔ]sݥ荷z6/\|J|YӯIBes*:/ų^fL TEVC+!i-JloYFc+*(ˉTTްѬl".}ʸ[^z.R:?k/m{Xm[ic3s.ID)/ '2{!O-? WPȖ$=* 5K#x^Ge6@jeDy굋dM9JcԜ {4 ḦPR$?ce{3*OٛH/ҫQE!8! "*ՁKb3_{[* i'vD(J/Hjq/E;AUא(dT@Uu%]~ ?%O]Jq~n{ J;zf  Qϻ:ꗑɀ+|C=)I0 8\_{{# s`$0ff"9@%ͿpsLӌhN1,m Te%0lS:*g_#G򶭛BdIrT[ ֝, xΡ'hX&*9(e ȑJV6&tV42\Nb~$Laǜ[=d!@T&1UZ26K'de2~57Tvjło?8.M 5Y!{S 2G]KxUxp ^^8nEꜟJ!^A:E/Z"طD8$̟Ovq]_*fRת״v 0v^杦c8Ga"06|Cpɒ[ct._:||.S037@'ڲ4aՑ_ժh>x)IgWO p3LA&2)| y\xox»i?[ 7pY޾h nXX쳷UgL 5n{v  3va:cƵPNke-YcӦdy/z -k_W*V.1?ǛTǚпӧ? vrB>&y|`w,mP`ׯ_MSC<7;ۚvmڡ ! ,d߯/]g'cWeʰK)h̝1@Fa*`>k(̝RKسwKyvEQ4- UD.wսuetvD^PZ8DDd YqXK}R岨W=gCEY9/~-OEiI~ ZAPDR zN/˷׬mWE8Ac5f78ǣ?4zx9"DJ䠟Ğ7YɷGzÇn@2̕" z7!@MKciY,:j[m3Akަ{\2z5/kR';(EÎOݘhu 'ND%`Sd]V2s*?"FQAj|S98P+Lޔ9=oc^f Trg'`;.FP6J2 d"H{ˌ>ipv8[on !n aAԝt}?<҆NUx/_y9 c3y)69aBX!ؙ,z 8&Q1ޒZvP[yF7.KO!ɢEkh!HQXwgtz~y#^V'{_YO# 2K.;S&~5&vD9B7d")6X/֕M(w,|eCVu9 'gY-ߌk{Y  ~4z6Yf4B^wM}U1pG|~} +|A6ypb s?-IАngrK5{(UrA>/'M+[0x;õ{mdg^˚P`x3P1Shn&5bҬ~hQf-RE_ :'P m; CIF5UU}0\(~k?~sڹ;ۥvyOM8QHRY(LơdIQz~4&LZ@wa3ۚA"~"M|@iiXղlN&bMUU/w]܆sdaU>J2h"wiC'24ͻjZC؞:uNێvE@^i "eaj'nX 'fp\:mwXV{>>MK˕x3Vv4ɜ,{wxs7tJ톮]).V:dv]vK;vH8hnq0k|4yՙy 0s$!N_ٝ|8`P⡇~9@@Y~ǼQIHA.NxlFIgo?v7P eͿ:yϋV~4`k?IŪVݟ:݊RѶ`WTfO!l|l.hf݇U<;V[䫇 inŔs3VWwz%|:v}!a;2AqH2̈Uˑ*tǁED)zmVMKAȍ 8Z2boCÁq$ KY%D> #@rН̦Դ܍R>&4r]w'"9x_6|^UV^4㉌2d~{o?N8dGNž"_ҸmKBIENDB`ukui-screensaver/src/assets/weather/55.png0000664000175000017500000004000715172041035017461 0ustar fengfengPNG  IHDR>a IDATxi%Gy }ݭnݥVZXc!/<`u0f㧣pl/6d#DI))ED|?2jTIvツ3@e@ԘMK>ypńrMʘR Pwf{>J~AIB!fDA$B!-6 EXfʼnTg8~2JZ,8fŝSOctma"g[n:T}%|޴Ty!a˖UǏ<9r𩙉R9J/XQ!6@J1J)(7"|ct-Gq"ھ}7G~DBG 6:DD b~ذ9E)|Kc<ۯOJ&p߃َjwH$"q؊${[jLSI?yUerLI js~;FtN!_ޅDG1A)¯};SFe\kH}Te[($; z3Z4|Hsz}]3tw! S 9C ٫G(S#Aɧi/GSX~g^f(7?er%=|R/:"E'_|G}x"1xcGK~TX@D$P2]P3+(ÀbK* qx87cwOzʋ؊V"R e~򦫙pG&DWLN]0ƌq"Bq[o]2ď|."6TQ\KR*x_O_o|)0N1"A~qPq@|e`{!(I\z|߇n/y1TK(ʅz\mCƃOO{7Z:p4~{ȇWqWRzV|q0,WK뉈pu2奞6F ®]~#93GRPO~;!HPqY~h`CF2}Oo+p_g>.83s@?"j{p97@6Ml/" d E˜LgRf8o=y5}:x-o~O@&wU?}J3&0"?{'SD)<*Q'LH{>D@1^/8JG,eʭ?wCRQb"{z/< ]ӑњj:J")ň, RJa˧8`ک$)J75*=$x:5k7?O[zՖˮ9bdZ訫R2h"H9@D"%1$}>9 l?H $

=,:߾>ayw^ꗤq"cL "6 ݯbGT؉.}q82qJ$$4^0%)Q%tl= `@@e* %5bfL?po_3LSI,D)%vo""{Q_|em c $SB Aqk[G 1Mcˈ$Ƀ{~?*_> N/ 7oo~-onJBCGD Pg)R q9?XMF c@+r|҂q8u1և+#x~gZG&@h!Dʗ{V6>Qk^zeb#eȢQÇN*Zۯ%4я 3 AF?k9drj5Co~9 x=}wl;vhߞ.%1gLd7HR ) sB`ɹ csK=礁1HNS0? Jen2KrCKZ-?Woz=f̔}^ 1 IR "HT  pFsT];w\^ T*)!SqzFI'UdYD8z}n dfvfC&ĭLJ'"9HD1~;:mJ`Y65ٳ$TJ3Stqƀ1$Uv{)zKIq9t;=uk^{ˊlD.;kvtp:\;w8 C)6{8G m۷v[=G! sƅfֳYkgԿJ)<'FN:DRHH@)Ū)u ql6jT8q|n>`g1kr֡jʡKk'Ky wM( [ Vfw!0ܛm%D u0/E}}qJVAeI Ln,3z$IAVk3"v&%qJΩ /78H =/dbW$%%YFg9϶6QLg%StMγsIgIP9+RR!i "v; 'ℐgFQ$(JHOq Zu˾.zv)x]Vuq-Y* "l6ݻwx=p1t:iM(JӴO6/:#)O,=Wԧ"+#ݔꯕi*$v9!l !tb]a6q ?; w5'R^򒋈0IT)eRl9)O Bճrz\o~})kQ}C8̽$IpzNb sND$$IpWqAb{kI ysEeEosa#LT(d6tq/B$bڳ#f)mm}i/@NLL\wu/O~< %=HyMvG?򑏌?~|lJ%*JYx4H%nN9`4\ۇ#,wY]snELqCףnn.z=z=4M(M$띱1ADc"IVN}h/D$qesKu]yr]W:#\ET*LLL(J%,B +-ϠlDETBW7vr `9QJyA@P.! CYV&IiJ)+qoi6@8v;O}SG6M7۫j>Hڑ/R@^Q "d=l q b#$s ?ow x̾gl[qCۅ8)"H44L])%qLvm6s=Ty_ʚ@kFcC 0-[$رw؁X~.3E[뼅^^1[˶oA^F$U39Gqu>Sxt:wiiiwp y䑳/yK2J(q`xx_֭[ZBYyx[[ѽi k\â>*aDD)%iJaRv}V\?_@,dRiM2Ƹ$`dd@@èص%zmF&IrNElQH|$MFeqLqF>k駟~|ffƅ96/0X=ΆrdddIxH\.zhzbޮcĽٳ6a{woV](2x͵R 4$I(" Ð:v:~Cwyvw< !CΞ={ahtkZZ4xHkſc0hIDAT7:?Fۈ,)JBH8(BqN8ugll F?O3zx`;3c9BZV!BP٤fI6"Q7b=n.*۬8_͢ߢS48d[d5xw@i"xKv}0BVck. m1ZAu7nlՀHdUW]U{Gi|>m/x ^>11uhhHu,8>>wr Att\Q/znϋV@yо}.y_/m.>Oرf󪉉^jR '''.RN2$v6*aտbȗW6h6 `u@_IEDU㸭Vk۾}_W;kcj0vm=ДrWՄVp饗ҶmdMFzh_aϮ1ۨ4(z}37|40 A:^8^,..4M篹#}{`o pbttԫT*u]*J>Ν;add͜85PD:e!Ǟ+7QiAyy(O|De'sH03%1DQQQE$ z=j4M'kfO>yi^q$d=s1T*PV#ihDlPl?+03dQyrPlٲ #*o>1}F:&QČ;"8rhhȡC&?7M~#{el/J%ue\VRZ H_ W'w.3EQ?س4Yް^O %ͯMȶx7E*/F%(0 1AEQtooRCo}[wT*?SVѥ^ yު<!)DQtgƵa#:ߜl |cZ.4r Ð̙3'?>s}WZfϸ xEA ƪ{q [n+zq귓kAQ٠k~|-{$p|uM{Rlt/KfI7*qx+^YIk?݇q⍥if$ILnFQDi7Yh\7Ez?`S6u>̼ף^g:tRFQ4eRJ.uݭ7pn]p!0k׮J%t:IN'f4M1ILӔ>|I`$ $lX#X"qm X8lAc3z^c@ZRbӱ&aȺ.;ȥdyy9vQ]RݮMo|[m uemV/ (c/JHehhȯ~\fAPT"qؾ};x&>67 F<^g#ii $ar7r/MӨv~J` ^hfa,"l6;DCD y^nZ۶m[mhhȫj411s:@_˽K<Y) C`E^'8N5濦eA{ !@/6<"?,9vV]Ț YA;`c{1fD IfpCyw(<(fqFuoWISS\c2?Ϗz0{7m]6qF dT r]!fχAHňD!#Zyg!< dI3CrǦBTyh; u]edF'0e1b a-o䯵ZDJ;1BZ:_b#X ΗLgLGV@<0":30üj 94ZkZ}\Q?JD_k&uU e*.|;nj4)\)%jPT>|ٵk+}1V;8؄ADgea6!_@E"=k^CyTU2=G .{ѣxVu Hpyy?w`KlYAJ5E2 ԓMyooSk5unbb// V sP[Lcccu+;[nGDplBRfDZj۽3g,={6\ZZ{ "EDh@ԩSK;vRp]c}IW6ҖEmOL)w(;zvp+"$IV=z8Di֭C7x%wu\0999ygΜ {s4G 9m144GX^ޔe鈹|[Z*haa!\XXX:|q.ܾ}|6 ^ XT SDI)ιsј͑10I966V [w&YCoN&,//dYG ,|>cZ^^ƳgRxm]Wzvlnn.:t y*FRJ.//wӾ7N:uS%D@oߋ,MSjzq8&u16_LLn@?zr2_\\\i*Z~;g7,""sǛz(b$bQ(ӦzSFW#$wH&IBӫEv9RX*޽{tΝzA @q6&Iz^R86qVM[cBv7srϞ=vf*OMMb)2>"Q8|sssm"Bq;9" 2v:?ydG=DZr]4l<~xk޽[o^KDA@AR)P!R >lܱP6o\ ų rA/*>|(H)UKn!,8pLEB"9KT='+nƘSv@IN5Md:EAgΜ6ͣSSSc{R՘BqQezK "J{ٸV,~kfbh*8_k>[t=QJaEDZܑ#GڊI;Ds\ZZ\.2.IT5_zO/,,2|t+g%FFF|%촸<*<ŻXq\m[돲FqbbbxbbbXF#}G8qbsuLDK2ןsLHc) C~رO>yZGi"///G JA,..v?>?33ހO0Eٸt{C蹾jUq _XX~aN4vvp3ILKǏRNReapFذ?Wz{=L2'$\Ik 9NӉfff9Hqcǎ-.,,t^|r9tӞa(y䑣29w.4pYñH YMLJ#%q16l"A !h`&)F hZH0$ٳ;vl}DQDǏoBoc] Hj?ultt4?EhZ9ChyGQ$zEJIɆg \RV"hve#혽 |X1iサY8D++~}~PG 0XC6҆x3fͺn}z 0ȷ1{jySm5M666oSOر ώ ZzWZn&+]TQ6]7X=sQ1Ob<ѳU!GK<_l]$Ïc1ʡ6[IENDB`ukui-screensaver/src/assets/weather/29.png0000664000175000017500000003511715172041035017470 0ustar fengfengPNG  IHDR>a IDATxyy'{yTe7>p A@ ѤHlNZK+Gx:bcv?GlMwgN(uѳ&,Q#J2%[$M qq7FQUY_WY 4@/"#32{ l7vl7vl7vl7vlh~]M @e>*:!K_6V#sҥ&R!PB 0::Jj"yֵ~`ۏ<{Kn=jGQ4y^A)U؊B !R !.Baf``{.bEl{Ofy[E# eY0 BPF^H)1h`J7mo{ ] B_4ɞĮӟ#G7]Vg pXnD.K62zn8SC6  ?'LAvX^^Vv[qRJjy=o}[M|_@k$&ţ>?ݻߛ*RizbjjJJ%@"sNׁr;?fEr(˨V"ϫ(DZa8100+.:uj? Hu;vs֯_f TDZ011)nYz[s.q2y m4ϒ&@XXX@^O%Bm۶iyySxUAW<;;_3MsyQ⮙6lݻƍa(% h!t'A}(`ğc gyeaJ HSJ)=ϋk{sA:sA0{o/; VJYJ~;6o,LL 5~Z'(^$D@D{zFtR$@y'(2 |߯vm(߿2gTh֭۾/~4u:Pa*-[[ 0t2qw.(AD's<WX{%@@B}euE$j!PU*+ͿX%_D{fJP)e;]vu pzp": Hvn{80 QPeY)(^cWYUFvϫe!TQEB,3?M\08Ag e.-˂izsssG@ ֍\.BjALd\ Fa"pF`$FQyu%H)u]u ?z^{6~>O~kv6M3؜P;wBN\|F'N3g !<ǡp -Ҙ?:N*-jشiqʽo]H)~ :X)0Le5??8maPMӜ+O4^7 %lC=oofir9%4l٢m&tG8(033(P,Q,ӐeYrD4ASTaӦMp]RJXf BI@(yh3<󎊢H(Y.=3n$x`7;V˶,KI);v["X뺸p F(JV,188OrKp."avf%,--PJaO;۶S`&A4n@F!:ZtzK.$83<433s70|/@ @sΝ=wv0 DZy--[ z888QE8lڴ ۷odž PT3=|oF*)22r\A8< eCATt% 皦i&͛ٳg ro4 B8ck)s̭y2\oW_}Z Z X," cll e|FGGQ.100o,'El=D\"K.aqq2^^CJ aZ@7GtRUCBn,Kt:t:aX211Q;pǻ\x#g?';wv;TJYccc;w8 #qz9N?22ݻwZ^ዢ CJ8"D("rلy)+"(^GRG)E0Mh=JO>z2=p`s?I0B`|CBM\.N'rwqR<^:`nn))%nqR!!PJQ]CF&yf@RC}DR`bDQ$'&&vkOc%|6u7К@WDrnGq[[nUZMDQ۶/3H/\sΥ/ رcG뢟GxHEKKKis>_0==jc5O{ڈi#P(\.T*tiZhHJzNr5Re }Jw f?48U>7o6U\{ӧOZR6nxY1FVg+R+06$GFF011)tY@IPJ23yT4yP݉-U.G|o:@2][K {#4×p'd(q}J6"UV{"|֭:N9ݜ rFGGaÆD|P: ɵm#ϧ֯_T*ҡhP80 Om߾~>ջ0irf;N0㈱1aQ[<8fLDX쩮V{tN7I}B`VS[%Rexq2-@duw̏1\sz@GFF>AtM6)۶ !=1CleYt< Z|O8OMzl9& c6*J82|߇8iLmu]3"5557nٳg[`Mz@ܳgϿKT*4Ae)W^(egP,ݗKkT3y^?[K%⦍NvX^^F㳔I n#ݻ?Wэ DX[x=]Ht166\qʥX"cDx66vD" IK)AmL; t͟Dž<ϻ ܤT!4MSnhX?[V+uW-bsG\-ڶmb * S(O8N0:ǟ\drWZ?C/%j'DRI@aDQW*ɭ[>pF[Z bUlpJJ)**ωEv,//cppB!iӹ= C!GQ+W#8'8D| m,dh4)1-J'l߾0 SJY ٝ7Z-fрRyؔ`^Ǿ}!FFF@SKRZùH z6}7\ iBs1SC|*?$=>6l+ryhJx rvݎYTD<4Νþ}4oX6mڄѴ z:@\U_-]KzD \)BY$%P(LLLuСJ⫊^ Sa(J)B (U+HkoL۶qY?GX"lق۷2 5Ł8t]? Dni)IKΨހLCNIjcZ`R*ϯE`e%ףZawoJBy`׮]شieܯ'ۂG݆8^ISacl ) aHcinyDZ֭[0"ZW4b8#*j̕LƛobjR}CPb<fw=]>Y뒈S,XݲqLqUJ|>/8FV2000YԲ˲(;,BmY"upa!044rݻwcjj*u(&9QЉ_$E`%Hi=m|MnuzHBPbVL) t>Wm_6PkY.^jhO[ocT?88 4ʹ"rgySkq~u}4|0|ic*h48y$p]ؼy3FGGS>DнSKZ1g fy"!JvI~A"rYTUjuO_"+t,JyT*XkZ-t!??+P‹F0LtqaC.e8rUAΙW;C- <:tS+4Tujf>{T*=OۅWѝG^kUXrJ)eϞ=zBҶm4'hЈA3k&%C>|z+.H'f#vZ\«N7By\_7 nvm4#ȎjܗGΟ? \.7RJi{O88{&tPijX,nh4XXX,BoWWQ(c\Bqbġg0`+qc|ݏK^8pGr9h;v?~_>.3&0@N*066vΝ;Q[6<<,r0ĉ'`f- ͛111JҳC?F)Nt;M6P(V+Ȫ=j+eVE1*LTeQEj2::z+eRrݱcCRJiY9888q\0 BZ]ee :qsz,..udYf~;vȬjE+݋OQw>e*NٸqFQ:~ߣϚkR"Y PJƣ-N:־QBg֭K %"D? fOR[nl6~۷oOc "ft/2\GS^ŋ8x 2&''m6^ݳ,c.bzq{9#}V%hR`-6mF###wn߾(aP(055^xx2@ħw4{@R 688ZJt=I|> pY[G\FVK- :u,b^uޤBJ):NlYr|J#W+mL+8R0 Y<}?=۶100t ?/ 54 Z-Jt%ӧOɓؾ};>яb``AZ"Koͺz$OOt"FNx7>$  :;  %\3vmrIABKeI0\|+sj5PđF P?OH)Ӱ5FJ1.>OE(DNz^ϯ~ nеH@JRTTJI!D 7Y.~gJDqCCC)@xgX IDATresFGGchhw}7vOzV_YS']+ݛo>V%`UňfqS׭Z!zj)*JDo7n>Y9atl,BcFJj~~~ݲq08}4 ڸ0 PJp9G)EOg3H~xGg>sD,N縤Q︚$gd&ﮔR}q…1_Zxmq# }4> %뺩 Dwi09g oH Qj 3p9r;wMKI Sjz_n:Pus'!0:O~ j`hr>c޽544bh2/]'sT}[Ӊ Xz>(8U*o>lٲGqBbeq~-.|GZIz> DݎM4^y3)Rj̦[ ~ꩧw-lQVܹSMMM)۶E=G/lY"3K\nTOqioVåKTqnz21.uf`G<E׾(r! 6m~4Z-٣r|:ީ~﫟Tw  V8 Q󘝝\d?dyE+B:|BrlMNN|>/Ρ4j_4YqKȯ[MͫJ*KWz"CR㘝Ů]y=_\\o 4%)c+IU!-AܹsK/tO!RGqL*}vPDCCC`_UWx]mzV{ZuH='X$z?LZM]gI{vngggq!nܹsXZZ[BdyyK%DJ)e?쁄nB(")e@roR8UT oI0FR<ۏ1lmdI+^?efj5[ H33d̑$9vF:+g J)88{i0T.,&еz? @<>>RضmDžBp]7ΓػZL "f3t1F}v'}333pw- لR-T.H,~Kq]#0 2bq2i*1h,ߣ_`c5k?}] wT7#~z,--^LDJf|>/;vܹsh]4%K~99O^ekqm ˲D.[l>1 #]c8K{ql:OꄇQ+LLLlŮzQ)0Ti"cꫯ]Nq9E>H0ҹa^-+>Y@O{j־~^tZ9HtOXMhBZau{hʶmAJ}oᮽX~ hJ)tDOB|Lt^ ղsnsB1t}f:g#'ׯzt)'o&# tAZVPl_!:, Ӌ?EF}a4dwWX*kZKQC|,1>o{tbgaW#N,ďgW{Fq\|iXZnSFf)LT\NI)3<\gE7C&#ꕸXw/]thcccRJq,\]b53`4м~4hm+jڈ^G8iXja멧ѡC1ye9oc  6F1?;;;@DQ:Zmp:^]t(D*'>Iy 4c| WEY6j}%h%s"q̿LDZf?HYOFW߯A\4M;ѣG}caJ}j} -䃔"N(j|Vzgs5T.\-xVFQ$0mΝ[?EQSBcſa(y3پ (*NS{?8"DA"`RGո?k AϒD1d1Z=,i4 hmj5rJ!}??'O:u4/'>ykI$ ĉ}x~0,)2Ϲ_% 8aJR*n6]wݵ[!oi sYYOOE\wwe˖ZӑiRJ\{չS{8x㺖`rP,esEd~^Kk:z_|<.]$ ÐX,w>w2f{]\yQmF^)(0=zaǎ{s^2duh@1|.9^Ǘ~᳋8j}e,-+H[sYvW_}uf~~>6 \4Ra(:}_xGP+8WX_2+R 5O<-Xm]@/G90Tƕ%[SlfSnD&@ȷcǎO~oo?~|ivv6Jp6=3  EQq e/ [VH5@?Y?ƞ^u:ˈ͚;LԈF)"<ϋ:}яzK) +5=F':'ΧR\艹@.Y8YY^KV"J~} @,--O<ı':MY=f_˚'% t'/|R_ !D^]wݵyii)ݏ=,"X!SIZO`v8ȹks,T!PpdlVS)""q__:pQ!DOĿn[ bE Œ-FWR(T|ȑbq݆ F %>ӭ)c>jh[٤[J8nQ9ݐ$ E"~ ğG6n]GeYytm[)RfE7PL%ѭ7P={~6n8X*RL2 CixAncqqOn8qbYJYv$Jjr"\URORBQJ 󢅅ƅ .\P_RYԔBn^Vf蒱WGX!>8Py|F!UBEd([xb'\um\?:::XVb1O Of?yԩ3g,58P(j\V+RX* R)W.J"[* fhZDRR`qqИ_ YoZoH-Jm`e%t@ӷ<\ۿO9+%Br.Xqmr \-;SH""jENWZL9Ca۶mr|wm۶-2 #kL2qa}t:|r$hFϢN|Jz@fpDs肠zH DJ)SuH$Nfi:V \Sp&$J{>{BT,'K]А=I'"_oi1b R*DyP+)ĽD+y^B~1Dw=vosuм}ۍBlSvX`% Ms>W1 8;G ,'"D@-j}>U]_KaD:;_b *xB'8tH: *Hp{^~FYϱئiNǮVqrP3Hե6{g8X'.x|ӍN߽o>WK iľ"={C+]ddG-Ҷu᩽%еm֜끮q'[%U@)l.&r< :bJ /@qOM$ed{WqoL.u@, Èn2=}PЯes 3A*'^S !i3sWjtqrK. b\.)_0Ɖo5@/!UƦ_sl7vlޡB[zTIENDB`ukui-screensaver/src/assets/weather/38.png0000664000175000017500000004670415172041035017474 0ustar fengfengPNG  IHDR>a IDATx{mW]͹~=>sܼ#!$$hwVڶVr5FZ:(lҒ!e@K(-)AyA $!{{Zs_1Z{}M#Ocz޿9LT?95~3I Ik߬'[kSӽ0nΙ̽^ }'no]}֠Ͷ#{uspn`r}:9ǝk>{J=,;Okgz.@3eXyۿ~_am-M[t eEA%3nJ#S\Ui;& đ~mk~ܭtʞ3%̀u[Bs}\zVҨ|iϫ3t],=Ycu[S)gqF 7D!^ ] ".,/GRUAuj?HA04U4Ո4wFA5fG AQUЂ)0=-G$J+ɀ"U""AD96.މk.HmZ44)7Gvu˱q1^'_tʣZO&❧c괸Ղ!8%x!Dswyʲ|XnS3VlT "Iv2q[ߊ }}. ߁ mpp+_ ?នpJ|"5DUUv\[/xwPuˮڲKFEdx|7EQ!V{L@-YgdpwƵj\͖$A'@Ht$ mŌ|;QO@k#h""F륹$"u=o9#ফvPt@;/>C"4^;{,dv[ Ə q E5jkfqI~f2Ml[Ǥhvϯ3QE?K@*ZT@:-ChDWULFht!P UBs5" !hIugN@&cw'!prZмf!|PtYpmF*tL-bcXuau!2ZYYYfԅE4^L`kkwѠ)d`{ \@< PⳀM6@[U@wu>}Bl~a^#X>wsQ"M@mG%(&dFUγ}2AQ |@b !rhFEwٗ\Y5rt;{> tTys]ϯc'1bD,:=Ll>ácG s, 0ꖌ73ÌWs_m lT@*eaUno}{6J(c7P{E1hP/kHzr=U!XAF') Q#$Q T0 nW:K~>Y wwkM i&~Uu7O=(Esys;r?o >n`#hT@rE!ܔ~oPT}F`K6G~$]>epM]ٛ`ڎ VQ)۩D!JMQ7JGYP5Ԫh2apEt 9F`!;+R]m7V}Y}q:ưXmzT|sӖL愞i IBAk) 㪬?(Ʉ'hRcZ#ji$ƞTM׼1bނQ ?EQ]h՜Ж5 (RԪTXهÌQ-0 jעۥ4ݨݫ_9{xp 8 m&9kSkCQA/!n.(Uf]}0L\dQ&7.=Clz/_x46E1vTBIhPU, b58gĠXAc̢YB1( S"=w7P, 5'0Rʢ`KD`ԠEixbCoƜ5:o/cb>Y~ubG*AS;*i)% ;]Sh7*+ʌsNPkBc,ZEqz n|` }ۛW˺lOK z""֖%E-=9(ET3j)K(*(W1>UD )A RA Fc㪊zQ_ٕ}wz@:@#(HW06,Zd5]cBc]XԴڊ=`qpD+ĻypM˺g>ϟ)[nBoJ-ŨHaTK~WAe~[D5IE[*R` D`Ljְ%l>)"Ctz}NR0&*,zG5flt r.L4حw_yl<,8O8" "}Sj"_D5M/ `/ϼ.-Imx0~&'N+*eXEŠF(x>óg$)EؠjKEXE:] V) L8xX>1Ob{#10eciRO~Q0IS 6f ?緾یJP$(53G?o#mt͂Jڢq{h7߿/r Lm :u5hjT@)uFPy˘O-/oSkAlKS%{@{) T~ݝ!ϻӯx,BH}zPxD-Lkk*FaJXS$< hyWnǂ:htH0=0kow!䙶(/-/)E<:+ALG@wY(X#1ccQ#/ ##Ų]]9$"9C E-HPvU^tK)ȅ0a.WZ)T ` X 2cnUjAՠ!ՠ*hHu*}5#ΫA*jz<$y?Mn]ʚ "uɠˌq˟kP1p ތEppf)fL~VA01w(t±#,c:]T,*17̈0vc,"`C+[СQb\pcC]l`2 ϗ̰cr5pBְ ĴVb6ڽR׬ٯ hQc@L{f"b*-^+9Hܞf`#wF=ng"#lOm}#k1mNA<! H1%ԖɆ3)=j!~kܕު/oYZ˜+19\1={EkQ-" Ao=fS.b.C'5H@LL̋ `)c{X5Xk0`m5qڸ#fmmx^KXCa}ւ5+>@BsŽfF h H)*~1OLt|={~ S9R4 R"q6iQU #jďƃ0\db{WL?01hT`cl~,lMf6"FФB`hXEL6&8,be#:P -"]bxV5ه 5Z溻8o#g"@`6?=L%IRXcܱ"]ӗYaH-lP0wmnTC X5&rml!SiIUt_Q-F$+ILy,٥#>w먟tT LzD/K }e4(UHtv_$-Ez=X) A5  "C %dśV!x@muDm 3i `1oTłcbx̦:ԏ v'X86\B|[^W9^OTCUTWaԔU8yc.zWk yDdI"Nr݌D7&ݫ=&1?=:+_GXI7}"4CwQAE|%:MEbհ 7FWpxJ;P,إrSr" s/ԬBp1e M알c3q`X u ma:L=4Zg߮sI(.~Q cOQdRoY0HS݀'LBm01/o- _}Ͽzjcʠb1E%<łX;DyR{vF(Sg`J2i8uٗ {y\HwN<_7%toH c4LP?FtNޱC~ǂBe4F58Net]V \?~فގvh͏%[tztxS,x-׎]9Bvuh%\Ao'vIBh IDAT0dhZ\ܟ!]Gh#am0F.aq5Q"D+~Hm1n{5n[~έ6.lzW~_u䇞W*kg:-t޳tz[cKҌܔ83l\'9.S_@A)0`09sij#ND,t)LmM)PR|ucc %>V_&BB&ܮQ702ٴ!lt?W' /3ry!B!"wj{.U]j_UtϜxͱWPڅBMxؕ7XԕψɆҾ}k-gZuObJ͗dR!s}`#w_PITwG?RoPf^Y ;H pS B]SzV[1ӧͣb7䖾2!o'=}"IKd.-M=f*ɔ}UP''g>.L 8ЗX8o]!Qd6Xq+=O5nW~$G΍[d~H+7{O4焟SyϏ^9P2iP]Na=rOwʰFtxzg ]?, _k q;u,:e۶Ή߼X}>ZXFC  lݏɖU7Z;`FK -Ivl,O>}|?מKs1$y@Li_qCCim{aD"Ro(lZcDP\Rȥdk`d5f[⑃+W zP~sbMs-: H^c8>#fRdG{jY Lh*:jܮPXH\8$f6 BCKjwQٗ>gǟέ.^MX:W<oNt뷏$$icXWz"v_)0=hc;=^ D "aZ!JN/e`GD6ު:i3pE+ s/8K7RZ\6Wz?6^:̹ao/3AsF~saOh_CJ_c0vj±K9=Zy&+K0C;^w\Ln.MeH[uRoa>icp Da'>!OBI@n xADDkW7\ܿc{LCvsPkoNGj?΁ķR9%͑2yKN4⚆9a牜=Ü'۞qa}B 0ԠAǙ,D0űk|e9խp@P/©wΫ[>v 7/\ IQ,O{k[ƔL\5\..sݗd}r)046y~bKgkšI]_}^9'1M࢘y^HW;ww'}5Ѣ.XHȪoR}&DWDbIޑv.!n=ݽ8#^͋@Ѯ4M\|OnA}܌:_HBn>.w o7 N"V#ՖzǺ/ǏWtN<Gc]bS_jU|e5wl'Bm堽Ϣ{˥XZEQHSzy_$)A~+1p@:k#9g3s4sŞ-"@'rHBS*?Du0 L&V »R'OFrB Vuk>;n/CygbO? M,K +%8ĠGUybZՀ*2Ş+Q΂AM'B l$|u3ܝ\ě9o{ﶊ#Kl1*i:J807B]hie|ߎß{-,hBK1wj1;? /7tUuX8"Z.hn ˖n*  c×XY]p]3kTAӜxIo@fsk~\Ds |$|3f!ZUԛN2:UT ,ݹ{pJX!u#U w3t@{{ ,eEKĠ~cՃJW Ctā Y)%CG,-`f,}Q}“3=A3DoOņHrq[RvOzkֿ\ ΍w<>|OX'.A((njK?E/x̖YS>:MA)Ĭ>ypr>u32Gd3{o'Ảʏ=4~w~:B.t 2xFw̽v}w^d6]YC> _L#3ƒ 0 + H@!yGHI#$ g m~ NfKw̼7kNIKXtbN)?=rtJ}=JK3'F{'mE N`<&V4m0 ʤݫ=8tY bVnBdSXY#Q|s~n$"LyGEubhAoHVA'0f!dI,Κ\\~sBl; T(\ܹ:E5K7B1M4G/;o\ l6"4'ӎF7=߅yͻY804FLR.TnZp8\ gͷE Tuw1uPu@}׬|_-ڵ`qn~I3alqG45a9cm,9OfnΟ{dkeƂЎd c9G7hh8R`P* ʘPK=Fd0H, .M *3?x^gVZE휸M qtp5]ވL1QN9BFZF\m^&Z:ؓ s@s@h5@ʎ5 f 1Ux:ZTH]t;'&[zv;#wy T@]{^r跋S.{R1ܠyt~ka#Sm,I$Gd>_͛ls~\i=RA#H0LH(„!"ꌨ#[^GF}_dDv6|kp饷W,ĔGn 8ă :ƠV 8[$ CfFTrؼn9W\&cf?ğ[NHKhf0| ~7V;F@Z.S,:kw-˿{Ϭ2#:/]0~_w[uW AҪWX. rhA\~0FG[HF01uG= MQl3gWnz/o4zQļ! oB=-Ueau굕C+RYr]Rxzkpqw$+G;~VgK9Hms@7*mmv}RCeU`Cg7*<8 uFn::x፰`aT0Yɭ#z)-?ܽẫ\vEɊqGX 5+dOB=^`@$h19͈߆ ZORǧJܶS'CΔ`3LD%s>f'ӘhJм`#MuP 6V4)*uh6UT/jgIҠt~;\Ib̬s!@JL2ৱ~ 8`X}@K!"Vu|nn˿Z sK1kUHIJX;=ӱ  %Z+H'*DMXö`ҭ /{f*5d9 SJ#yo*|#$Lƨ#@-IcJhQbS蘁> z f.]d ^fÔKToomTP-7` :.ؼhɀ&u`z19=QEf%Ō HۭҺ~1MO¤'M'D eWtyX:]mHmh{# F@O.cL!YU!X3@GJA,L*=B R֘[ڍ.r]0K5oEm^)#@#6-]p[FK:L;#&zwCŨ"R[DVN?kP!\.pXa ֘R/,S("**%b{.:Li1e련?q $LӻsBy^pY Z`7z/~֛HG}:VJe*("XJkU6v+lc&"R*Rɔ~u Ϭzv1h:q`CP޵ueյ6۱dE 2I`fÌF#/|H $⇗@B$ #y8q^NbwlUU}{[UneLm![jW\uo}cub^,,#jjBHBp!( a%#0P  'nYxiz-^b\<)wR>mM}myx2')\O? 5 هJ*]X rquU.7{n`{A B QWYbW;uDL@q iƳg 0P(X}i `\I1Q15 nz*oSFƦ\T _h'?DMH^ *=Zo˂oيP:{tU9P\#ڨ A_WҩP8P| Yv뵂L 0n٢|Ze4ӻ^Q-Sx⧒7%]\:UCC+!)S.ʾQ;0.nɀpn!`) ԨjxxQ gs{WzhkՕZ^IFjQ %~_*Vy1*B8p'mq ~ !%#(fnC&^A^ʁOǹ6J)\ehaFW@@G<<3 .̇ n#$28=j[ G.vHR/_o]^.\^+7/fG ~p9;FK&5TNM vOAma"X@@ɻbLjG$uxrpsMf .: `åUȀ&K+[[{9p&;w>p@xtnݿSX[1˳٥AQg1=Uo謍압e;dؔt 8+k7ųX@]cIܦ^H+@u -uBz%(6m<TunPgzd&,f]LF+IU +2=w|޻/m\d@Hg꠴6;K!mn"UdWʳG:r:"P>{sM%4vbꄵ ,u~T+1PWB*C |ɡ*I &H{9.5z-?' NɆksh-Ei鮾t!w*|\u)m%TEO}.7]Û?IZS&3_J AI"P$ ï]Ge Sx!p%*"ʗC] pVtT9RQ@an9䆋^-6TrYiО1L@_TU|rs "cz\WL6_`lƽzr{i4<.[$$Q׀D3:5i@z N\aP6a*ͮ(torpi8#̱* ҩV@i^/4iLdEw00Æ{o,8 j-y4՘=۬!:h5=JESV3\NTXUƥK N\A#'0fg&l9wc^}!fuP]|Zf 甃rKJRX$q+Z1Gԡy VJF8dsllcDFD& O}Y"Cs- ԕ.&d;LIj9 i _[*߬lT1| |GQy-`M*D$"jsphRŠ2&VCf*/:+ȗ^]^oWQ!#P'FPfSgߋ.{7$֊7<[>3s8sy%ᜒ&UKs `DUE_xc iH Y Vaڶ8a'UG&<[EEƤĜB55rQ05Ά9? ['_k]iIg#{f `"vhk!.R^D]|QH_K]݋pZ$ w}r (UjTK CT1mQFOU'}cO* ,Lds qd1"R՘8+5-`H?_Ixu1!A'Ky [~io k[Q7Zg_Z~ d◵I xiV~V)UDt@FE60qT5p.} 28j_ǝiDnN4 cU:.2GT$UVr42\޾x&2IMէ^S V?p qFL짠f4It-0@{&z푯xoSuNiU6SI-kJ$z(`">>% >"ӿ(. `ld%WFZ0 X6Hsoc/MP|߮uuikϼ3a]UNa)\އw sW$ rdk\g^?̄A=j8vjV7_j4;Cxz/.mg*:BuM6WFm2o~$iUR: sɁtژzuB@@)̼?(*T* Xb~0QLOmI;ܪ/(DK՛]`4Qi2Rc;輪e~b7Pw:Z1m\3ŗɦiҼxF@wt6eV-O#]ox2ЉGE=n57~}fX2(R;tsZ^˞Q!!}ӳiFB{d3޶=ߪ*B'qYH 4+iwQ>cjecA\2:ut5G5̀YwCUH&8F @aکDs[يtPTo<$yƘwk5oV.//UC= 0 @B$<\HX6umy">m/5̛b&Pk?9p=P1NN-1t~o"*+S~ǽn q #9-PSɫqoNW)[D=~Ш?l dLD%-mgi!T}DZEWufvN@MA۸㶐y |ҎB  s2?.mxԷ' AC=G_5{J= >#hɷg.+NW`a&xya}GM>֓=<9fhbMFI6?*}si@%hwfWc僘X;MZkJnK~Mlb`{2|<))1w*O XQ-xo 429D*||]q`n4ve+1cչf4@ATPm.}8olw-`;d+_=\1Q % [蹿{?lE@7*~kmvo NԿo3L><4SsIENDB`ukui-screensaver/src/assets/weather/11.png0000664000175000017500000004213015172041035017450 0ustar fengfengPNG  IHDR>a IDATxyU?9{g&3I2 D4DE("/(&!HBBȾdf2Kf鵺]J̄$K,Y C`}w_OH""gϞz ðm-`Nr~mH&hooGoo/yAeYLF.s=d7oۯ||ޑT.⏜{PC6Û|/ﭭ !H$".\SL!^&!~*Bww7$H'Y^^{„ /s1O.X`ĉ{ux-7… hѢt]7ѨzꩧbܸqC3&pL&!DQmD !$m{\hU \3oP @ѫ@83gݧrmGjjj&}ٛDXu:Ĵi&>0<|}7ضCJ 0`̕R (PUurb1EQ@DC 9"˱# mҶmrJ2i:u3K,i\qHo{H3 -Z/Ҷms֬YϖeR3;{{{3-!!|1 "B<G}}=*++T6z 4J088Ad١cѸ-dRdYL:o_|ہm o ?Yx"JhY F̌L&zp]1qDL8 3R{I& `V0M6M)%K}ZmrVF/rHuq3g/g͚L4,:@ܻa$} 3L B@ EQPVVYfv G%c8j[FUUa!BQbfr(XB__s_x0[xۀ @={Gc&N8dxyg_hl6 0r!iPU՘?>lhǩ7C8L*8&Mz$ F `0jsǎg>󯦦IEI4Gn<|3ZD"!'zKlF.63]?4!"> =/8O/!ݍRƭVQQa-ojj:sMxGv]]݉`0 f.Gsq{f(BQUUӧqUߐ0tWt]GUUEAK @+//7f?\Դxx0eD"qu#yw>$iFR!⻉D0uԡzL 8 J#1[V^^}7?(-wh4:Ӷm  ;K?)`hvsOz(2<0D^Uf&J-oDRJ@ p8 .d(YJWTT䛛O[n <#@SU5ZZZ1TUN دߎ^0rt0Lf(F~N]c ]׽-K)*s_[lgyxH0 eʆaP{{;04M ?g&yLu!mPUCMRfCfR= I)ٶmm[q ]8XLN;q DIt}Ct"k0[ 8̛Fd2@.z{c~@~f*\.W[WWײk׮ؿwM%1-XE}EAPu]6vZO.Mߏl6;$];0pP$!~ =(8@D⣊p(PHaBˏ Ew5>7WSOc)%a7is*ڄ(Jx ȵk~l&9&H$QG3gΤ:A"@<힍~O{ &c#w~Fφ(e :Hpَ;75MPTQ7x㳊///7~QGܹs9H0npvn1&ZWf7qK- T麎@ alӦM;EQ:۶[ٜ<#F%ny)xڮ,]Ed2WU#H쮯_5s/~ @օÚF@`_xᅟ br3@" ۶pyhhI0_xد>7DwwGU縸ዩ0MSڶ-FqömDɓ'?r'ANOӉ'Nu8 eߍ8OD,k(ʲ 1? E?]ޫG3м .uߏIx&nI0eYxbHr9J)SϿa֬YM8Lăe@Κ5s9z0̅ jG}4[E xOTۦid28 "^EΡkXCawܶ{{w(-%5ز,b`N%\rJx# OzuN:1~!' 3t$(e?ubL6>RRV.:;;Q[[{w`@@N>h4:AJiϜ9S>don "R)$ɡg#=Ws#ב*~21RʔWEuTЏi\aq*s.4M` \sQ` O:oVVVH$+_h WΧ;鎌{Ŵ_RUy"G-Ydu|"XAVVV.""֊`0F$

G?z;P O @ hPO 0 cXⅻw;u`n7˛M#"`ft~1Y˽XUB#t]G0D0Ҫ뺘0ajժ/_U( ǩ`P꺮z^!$rܰR"V9PJ{ E'KǛK+pʍ /^9aq:ff۶˲'naU4"c( 9:=ukwΛF%hR@àr"FD(.,Q0`0ns1-\X<* bR4 R7X7¼)^۫ 2Cl-%RV +""9a% ~EQa, P0ŕ,b(d2vq~"oF^O8ϻv+LP*7@I) HueY|ꩧ^ cFdi3 @!+ˍ-S\γ~֑Dw]{#ݞ_g,W)|w HUUB؉Dbԩ)65*}*@:KӽmiEa%CcA*R 1G0dիW/7oNq`f2Msҽq`4'ִCp=x^wݥwupC)%=]$F>(1%?XƲz$(X0YYYytMMMi2)%r9v= Y%{Gh ~Oܖy7~"}~r^8w\L0{֭[wB2s#JbE6goʔ)*D.cUUIJ99-{n);[{Er378Nwtɘle/JIpggg[^Ē0cMd\n37gΜ|Md)m/ދ #{YANc}\Rۏ 7 XXc!MT/_pWW!I)R+(hEBGqaǜ1ckZY<ׯ~/und!{@nCo?pnvu?;Iܹز,i,]i9` ,Vg7mڴ4o/R_O2Jv?,l%绛E㏜l衇-N$A%R!z.BawZccc2+4͡≞ P,wfK|#[xzgdd? %|J~>;d2Ϯ\~(t28 6@0 ;̠*j۷y-mmm=L-ҋn$q8*BE@a&K 7"nsKKYvތJ+]}\.GtZJ)Ŷm|[lNHQͦp0@-K2b" 6lhݰaKEDeYYYEEEEe8F""W]ׁ0ZwG[y?$pL*`$qh׆{#SmYuuux.0M9{I8@(R6$v`,(x *if;;;:;;_jǯ4M !DޠN3 @Q\FR &RjϮ7z-e\.g۶twwwuן_'" `J)-2V 3Wwww 8[;PT"҅"r\IEr|^L6mF6 t!&p_rd$lrv$;+PFrc*2U/~P\}cŭlА뮻ijjZCD̃R(TQ狴H†W[s[xۖq`g?@6 BA P^̧"b @hϗW{d26_t1 kznB,nڭpUUCӴf gz_Lٲ, R\9e"⢪a'#e%}Q:ƪU,]T*V7y۶&Jh @>ŦU㧲I"\Ձ1 b'_DH|F`ߴ| wqolY9'9PM>}G0iuݙdSr zqw Ð\He˖l5k֜p0$oe  v֭[w\ressԤRiF00؞~w%J`$;B(Yfp$@ -XoM/@eeeY.eY4M8b1Dn鄂CW a,E!۶fY4ʾuG} 5v1Ɋ'\aÆ׿ *++kjjƍWSQQQ@@4Mefd2niii޳gOc*DAj fN94Qζ eӗ`Z/j5Y ;xlbM_2UU-)e^Jin fD@mmm ,8y Dѧ\N_$=#\4H !Yt:Ҳ-WWWW'"mal6`)b©%\.7\l&" d.KnuBBB[3ǫD,^)f6ό<HbwJ2$z#r:kRYgf @* l"*P(2Vzrƍf͚`ԅB!!$ "e l&) w޴cǎ BIM>'NO$eEOΧ,`!M6ٶd2;wlXjʆ~Fl%`b,4Ξ`,"6w l # 揯1 Py5e59\-y˲r(u!FD* ARmpBᇐvvqbB:N[uֽ\SS3~ &7n|8N0 tIWO&]]]mmm{ݻk߾}` "MMM[6xuu &L?~X, ÚQ IDAT[RtWWW={wܹYb cF!(Brq5dEf?? '#OpLmLI恖*YψgDAz0 ekKK&(p8 BUUuEQiaT*4M3]DR\0#8^XG;R#1}2T/A DׁK-:n Ğ/_`6 N?*+\H2QP GNDs-{a{ٰ"‰(@Dfl X#t@m;L&d2)FD#k8sy!xt3@EEɸҝ׮xMUyL<lymWHc`>qwJEW퐠Hٱ3I gK`fg ,Pr+3ň,H73;1 0JQGTQzu9,֋mb78/%.;(5 D8bZkEE"JeO~0B ;@G,'C= { @: mfi`DjNqp(n]궰b#Y|>OZC ;Ϝpźl̕?:|;O׷򔫮M^<N8cC+sp-3_"Hظ x_Y*Wu`ּ%`m"˶^)e3#>}Bo}KKi'U?d[^$#J7BKA["$[7H10ͷv&9WPhv:.b5{ל:%2.j ;i_ȕw%{3AvR dakuwA(qX(\C(78n`povPzSVK*ك[]vqIjP*j+&{aCa}B{٨X+, 3xP7oKQJ?+k]]U{tp}loߦDdˮiK6RX24ՌibRVl!sW\b ~p/zv̜";tmߺa/ۤh 򢪲x ^\V 2D@sC~o`S?RD ffsφ-/(GmB lS"w?6}x@U WW,AyO{8w՗+PD TU\'p|yGmwr jR=m}=},^E%_nxeDZTTFo4lvv@  H+&R@ 0 mj@!zWJSFq_rM_tgtxu^jb;8@OSF_ز+꧌h߷O/j9}[nyƧ~#EY(Q`om(@VPfAO?}ۯ_z?l5lݷ}2)᷆ 2`mgëNwNiZf0ko \7VX({zsǶ7nj.=@YQc!in:4TU-gaDg> '>TsW DHڶ ([q6-Mm׊ܻkr :@1l?<{{tcGݩl!U/-[y&A Hg]ݒښĸB6`z↔ SMsw m@(@֔d#VXжw}(J,jYfF8eVGwkW_}yo[)/K?ʷX&"\*b7W_n %䅗-I&A%*>z t.Q&۶=oӧ_0H+c6v^Ӏ7҇!ko#(s dY{L>X!\/C7ϴdgN "0%>貉''jC:c_GsH'od6{[wzhݏnśFcN;(/>ZV},GmϞG:Fx İ?c4P.\^8W#1KQՕm, ,aQgqe<3>)^E"Qǜ%ȶ; W9//V2~p*CѼ F,K<ďݹ>TT.eG?w4J@~K|>UnE&l+V w"#6QǒM @yK0_z16ù䗔W3xڛhҴ8_Y8P,; N_\\xi0t *D,|ż shPg(}o[EH"=]>k'E_:,2IW=Rŗ@羆 M+m.0|V yx|q\=nV) ?y~G )`giD6`!{k;`;FK eJ۵+N|Yb֍wu7e@ n} ipzv|Q8b%S_8R,F KQVߴeN73؏_>4ʡ,{>~k.gUTVM\6TV.KڲKwVcCkV\qǺ/m= dF t Ur|<0D?|o~[V79wG x3s=ݐm(ĤUvn)~*:ӺvbͬoxhoW;(cY=f]m@F7$fi9#m<+[ǎ_DWdO'?9S*'ojMVWHY@=@&X揝v𥧜Be3MV nWt;79ϑ?Mwő?N\IhX2!Q "ll<[Ƨ1Y$BQf^&wz} D_]_VE".DR2r8 ~ƍBhn 6`>Ƌ*D L lwr'nė0yDޔk/Y/VD:pTuU|O-Du4jl/TT &;Zƨl)UK1g`P mi(DJ$w'5a"?|Cxom_oÁw@Sl;QoD=H q 3ߍK*aufTb%¡e˞כ"|~4cDlW Om\H$ɑJMk`W[L@]B;qCPT-*m[٧}ŏR.׽9dF$av= =uǟw]=UݪU[9i+si@RD5^zSMY8*@ǼK0eq)-Op'\9u]ܾ^d W95ӿϷ.;7? lsq@klmD?=߷:svowwcs=h-Da#w 8m7[q9VM+mڼg߶߿|A7=d(Sao ?USa6%HnivREս{~_/`\'3DnY-b!L}kvx¾ĜE"VD7`wu=O`v^ ?# Az8=,D4fy64 g  8G9m;w(0-6T^P;[MCU 6n)8x-۶U5Jgxm/ެat %e~hZ4@ XU6sL 8daT8 ; &WL{_jZaš⹉#<VM4=izETr552eDT?˲QgCcGK9pwk4'U ]$,l*$9x;KIǘ5 U/$}M{;m[')͐l[Wg B%|Y4k|͵o~#xsV1!;"eB=?_,#YiΑIWLs޲O>sk]'Hֵt+ӵc\_瞣ᓼ֯%}GT/oDq"m B@ %>Bp(6x5զW=7Fu˗.O~;|.ɐiU*z;ǣܾ?j۳&aDSLzFCD ݶlN{!}\3n?h;yw,KRY.DERlmh[Զ#bL'cӫZ7LeFΘ1w]+J fuO}|uuE+wEpW'Y*Ѧzp^]Df׮ܸL)|FU?bkwܾUVrݣ20(X6Qx @w{,6e fWJu` hq8dU'nxtv&$s@WЬ@:o8|`*l,Sѿ i*&;TB4e-^c_W|p[oVwt5RݫvHk>U-s; p/M3Qf:9 ɗ8(wH!GM3,DV5_ՒHc?>/ȫ滋/N?րɚr=Q8@жVe26|sLJ3>Fɱ|U91RL^uۛ:QuuY p=>8=2 |W-?Xw %)p w-Rf۲e7^mj`xn5sКR@ aVe?u3xSݽBHJ4ˮes_T=B2@4ZmJEB(eHٳ{=rGj5Lkq]}/׌PVaZ ܲ3>۔DWck N> uYP9T[Xn{ P;dUIus믯({K>bvHd:=8PyvE!w(iUC3P+,@&v6E]a[~ۻQ8f$)5޺N]!Q+*mf`VڃL{G~qgz\r cfVI$Uq{t9^a&V_ Q-ÎHؖ0)wķnp3Je9΃%UXre!ɮnAL*jD%up]/3S4DjG~d_ ?m+C͛tEJ;o6xb5u'e[,+ ` .`k_[|Dgh5Oyҧ>I{0볟a>˨r,@Pg_3 lAKIDATG%|IY=8۰өs#]=y{O,XZDUk{߻O/З%z׬=o#uY#EjzP  ݹ2Ho~5rևat(}X=fV$4]+j PQƜoy-uF!jwN%nvJK ~f˓\ό7m;M T4=hD+gh_wH2x}C;$΋'ŗf~ kѲuܸ/az(_?s,H >a-{G z!ot,3-f3oVY9Yxw#շ/N"޾JF%T7{%/5hU#lmaQgFwR!zvfәc` ( Hl?vp@/g>bH5+aY r #Q ~n_z%Dd@7l dQщ,kՅXٜlWh:xB)`SgƖW;v("1Qʚ,VҲhÛ[BD!Tۺimb"`t&Ѳ^FFṞ-{I=zJJMMpp8ʇ3.;mɪbKw5r&C[-U ۄ賀AT:YQHJ]'/iPZ+7rkٌH @>z+XJR:!Bjk䭷tGY_(l@IO?}g"XXd7DLl ofTx!%l$Hw0@b op%#W)~=h`ÇXX^0\1a&DvNNK_ec 9pqG>'W IENDB`ukui-screensaver/src/assets/weather/15.png0000664000175000017500000003705415172041035017465 0ustar fengfengPNG  IHDR>a IDATx}yŹ[է>2  2EQ0vQ14Q4qň 7FcD`4F#WMe`}֧]33~TU~k}]-$@`ІA}|d[nojjڽ!r`29nFK^^^] ˫.,,o?^NH8K.=r޽{ B}5MFq\.B@)Mc RZx*k>/((8(Mm@d?h֭?F>@>n$1BA)u2 0 Iu(H$P(]sss?2dȬ#G~MpΝ{ ~a)))HMM5$I2!D$*In7\.dY&B3MӠ*4M0("555@`#~.· bŊUU=#''@p݆p F^/\.a0 UU( (0 BD"PifhF[ZZhmm- ~u=XXXX# 쥗^ƍ%!nn"77deeAg0L?AQB!455&ajhFjkk(cǎɹ瞻c|+O*++o7^/|fiii'999x<  k]߫p8Z@bTUU᰼o߾u]7.zS|G}tn8qn[|RXXכ`H|k$Akk+0"(SXqMUUرc/ ? ޱ}n [>⤦o /_޵kWv^/ O>$///Tg(1D"477# !CUī(LQinihhhvޝdff&~''_KK gEI8{\.x<x^Ȳ 333я$IR I7.++2~?RRRH  . T2ϧBr׭[w-ܒ,xRIܡgnzI `YYYK%~+w^},'ѭ[7n0NsqUez F|V^^~?<ŸݻIhN$ZII`ʲɲJKKNv-r<?7JaW333ѭ[o鴏G#k 0 444QJi(byyy񺺺ӟo߾>}IfN@r,ːeG0;'[,7\dڵk],iͷ'C,N!p߂PJ---L^z飚ݺu;'#$QM<t݌$vۿ#g!$[HߏsxAL8CnBƩ0==lx0S$,==`03c`vI $c;  ׋܄gaQo)o? $Az匌x(>mڴq)֩5  ].a\!6E[յ`ڄ~1^QdyakFO'k)q,vb|>b˹񲲲}EW_}8 B)Mw\(LUU$t%@':Eb:8$9<4d{!/Il_; 1:1 C,X̶m߿߲?ЙpQJI,F-0- Jp%1 ۝Xa|'n'sDOvY@ 1M$MӴ`0;}'܅o9eY ;k=hРK~*AxvZ`=tQ:v`c.79"DHv'KhmD$iСCZn]$ !cƌytԨQt޽  )))R  bY]Δ=\#H&qΩ-%cb|[NE322K,q,) nOwN222}&D,'s}4VNJ։e˖qX@t677_A)%%}܄x<fk&'l `Db:`mI${Q+w -lRԴkR#rss>ϝRs&GV98DibEƈE3X%=8~0 Bq\M6Ƒ᠄o B8H'xbn8,77Wz6dBa!k9H9JzGսG2@8*$8+H3gwƌ1r0p1cƌ{7nLGƐ!CHjj*!_7)g3"0'p9Q?{/JUppg"(aR/ n֭OFCGy睽EУG#tȐ!,++7q91/uH\#pj ۳?kx/_EQ(DUU A} Èbx<;-x8޹ˮj`կ_?ֳgOkӻ'x=C6Ķ8zlbG%hcN@,Z4 7`3+Z(illD,-..74(:Itڵo ς ҥ JKKI϶'KD2Dd?{?clUѴq gDamm-"ȦѣG=r%MT0JKKNIIn[RAAAs|'_pd 8 :s7'V*X|C{c;hFmAE!n|>- \hٳgj„ 8AQ_Aͺ7)##effrul-gXi,ԾQ[t98yȁ@GŘ;Ȳ,k@TTT<#oAG wAjjj<EﴼKj;8M8|Nz쿵DSgxmphC:D\P?^QQ߳g&L0&/K0GxM_wJ88av%ũdB[}wm!8\.躞Hn"2e}oݺ7|sq^185rvv@|>dx<~~[V{/AxitG LH[@kwru"IfWM6XjWgye=dfddtz wʘzJ޵&1 "v1(VmtIVM3---Z޽= .\/О7FPJiV H<>٤r)t#&IE-` =;:ƔLӉ`H\sov3/g=쳷”(c,r<ib(ì ow{_ve?-'<9999^W驪vXff*b@<'^0Ɖi'{;%ECHx[Lc^xbAG'I%_\nOppĹ9ڭ6n 1X,Vy-}X<H䰼=/aB9z~[&޾S]նe쪿=TBa{^o0E@ /bѣJh4,@ d3I2m@T⅋/~'ڃ-~בHv ڙ;bu'Y1B4UU)0[3FRv޽f͚5/袢P(dȲ, ǯ8PޮSK?o˞̀N Ѯ d-NMCHvcɘ. BP 6@[0y޼_u.sL4"ގeG}{g1mqf5JiRs7:I+ؙ'̉4u'tl20c`Hu )(Ƙ@Jkkk],.---MB:O\A{6^,x{귫b!QĶ۳Ng.=iZ#X,q.\"IdF mr pߵkN{Z^rE 99av*p\>h1.nnn-ndA'@%s1;(Oo( D"㤾ommm, ӄ;3mk1x[lvzëXu]'0L0/D/2_4A%xL 23m;CNE3AnH4eak^* 0\8G7-aӦMBPe L UUs7A¾wrxܩ]dfo ' m}'۵K;s4dKEaha]UU_S72bHRJ% yvܹe˖-=ONzzz!X3.|X,&+!v5l`L{*NuN6)唬dmFI(2 6l͞=/`n=n ۠Z@"x^z}EEE~ϗv)!hfFY4eVB;X|礞xaBb(J(w,v3L8Ev&P|ܱXYXLzީZb5&Dch(!Z!B@]]]պu֯^lΝM (a0(:5 Au]hXo!3=GO };lzMnN'/L|'e@p)D"E'y^D(KS@*GqphM {w嗒$I)iii)))@ pۈ>]vuB!a0BG 8>)"s&PGEI 4x[',hޟݙ.5c)%---:׊+ZfϞ=23gc) hYzfVk$fuS.B̵뱆 `Ȟ={0aUU5]%ܒ'[InΝ=|@0'~f3˳_UU*L@ZjU駟~988D29Ӊ7 0d  CAB{͚5K?`РA[ZZt  S`Wvj.mτCj=G0~3h{yi:'f^DUU~/J<,$(#K . /B; !Rq`\v=}5Npa2^p CɣwygNJJ魭:cL&|O;s 35F"$I9,'GN biaY=GppSٳg͛￿@5:@!- "?+\ pMw8?UUu۶mrrrzfggqRD`.qJ''ɄaB!nHb1&.@Ykv{/³%Il}饗>.//oRUC)ID5|%ĺ3;07 HTTT(֗^z/>U< ڠMݢ$$)0|0%g uKxK5j#F8հn \ -uL {FTBؔfќ$QɆ,˰h GfI<ǰ@K/_^kiunݺu-((螗 Gejb1|wyyyE4+C7hRbF6hS+C97!/`="HcԒTRb@?r  P 9s kȾ!qڒ8Rim?i1I;v|l,ˆU{O|>_bk; p¸~Cuu{XxBq+Jb.̥R5viU+!ey1(-wQtn?bY$ck5TuL `I***sg +))镝ጁYx-BlV)WױX uuu۷_~Muuu$I={-)))ٳgϜ7f,3I@N{˗/駟.mhh೜4Ϲ_jB4} N><,2v. ϭ7CD7cu.C`8cL1 S >mQg:҇~8?0РgffJ<;_ii`/~{ɺU!"$?D v-q=ҸŴ#|a tMwȲ{Wr11.:E}/Y\__%\3f̘;u)i\1Ǚ,D4rss#k&0K4vLi H8f2=wyg/ǣ4M[4pyBw13++kbuuOKKK_{ߝ8qUq^ |8 O9s|zy m2YF8,t֬Y_3(gʔ)/3}>_ /Pةab)ZB%[C󱉅*uC'fDTuQ5ax?ɓ'1%%Žo>ddd Nްa-`pM]](8pɓٱcG,;aYDȃs… ͛7ǯ/_R]]mTUUl߾}(++ceeegO@O"w2HًYZ-YSjYYYsyys+**&EXee%kjjb165ڳg G;ux?ژ4i/n_͕222Lu_^[[SPHkjjrs=#Nz_^ΰ;tĸ)3Ƴc<5*@99|\❲k Hnn^z;nr\ -ZV=jԨM0 /G.]\ƍhcY{L {w677WX7|ף>ziMMb&Jf͚eIIIV$aŊt⯨Ս.Q2Obb׿رwRSS7xcڐ!Ck׮wxo \믿~(tw:kNB~cƌC]֣GZ]]*ׇ8޽{狏90%frda (((O>{=2?e˖ʉ'^3f̘̤{={3g^={`PڻwUUg%M@k捲2cܹ;?6lΙ3`>}^p\:u͛7 !BM) Q@mc̰Y8f1[Huuz V3ؾ}H$~-:t+\hZVQQ ʤ g'O^o>ֽ{yw,;v < c$sN^NN=?iiiQ׭[WsW;w &̼7n\Qss3+,,̿/MKKì񌌌]wm=У\sV2%q.+C4!$1@SR6 @!^:M8cX [?Dׯ~X ! 0 gՖ+0hmm͛ujjw݄߷ QYY~Yf=Eie)8b Nc&0+XyhɑMMMy6lU߾}uҥ',&Pp}ٗ>-_u 7|[nm۷oJFFb:ysy Ç5jTرcg._|U8eY1 \jܝ \ޒ$$Bȥ0DWe$]@)C$e˦8p\r[}urIǰf͚'ǖ.]TC㎅1(~;xhf<#k?ͬFWE{Woܸ1G '=$`,d!@|]p\gQJ/r\^oc%5557nxUUU_{ PQQ񡪪/+ٳg[zf͚Z~M6_g1qF={}=`w 6>V3ĉ(755pcl$|?0r] S 8=$ٺuCvbk֬^\M[hx:m`Z( ɓW?766{MzW\1g?G7nڵkWϭ[nݺ] sM!C~]׍ٳg@!b+?s3[&zHt%^PJ/$i0,X,Ny)S|޽{Ν;h4n喅{gРFt`gd0+ 4nРAEoʔ)w-?yz̘1[R駟4hm{i3fxQei!0zqƍX끙8V"I7̍]l0!#F6gΜr0?|}JJJ믿~󫯾h4#˅h4 ×;mTI*++{BEVVBq5\t'---6|G緿oB(lʕFJ (..~H$$iUڵkZ s@:0!~Xdʕ_.z𫯾> 7V}daaa 7OUVJ_z#P3{fXBeX 6}wqԩS?WUU {XL3 CWU;GL0nݺ] }ذakfΜ93+(Rz!5_^~UUU(l $IzR:fd8tPmٲ幪iǏ۳gn{s΋vq3fL9Nݾ}{駟,; _`kii1~_|6.L\/[re c}U{Bx#rJpJ np6x p1@ȑ#߿AUUңG,0D r7o^v}_}m߾=x>ܿUH*?>1 q֬Y_sR:t7'$_n? CZG0UPJ#uʉNctPr."tc-ZhϺuclΜ9  /0 cĉŏ566uDD,:nܸc$IJlj|00ٔsp; 0r9B&PJov\WH)t*8n(y֊ܯ_EEE۶mK.^y{.h4 0ʕ+[/>3x㍃b?+V;vnjs6n8ݑy!(Hn; 0I7mYs;rz^ #J[&M.n ܬkD"e={hk׮mbE-..@^2eʔ `zhf޽sNyQ00npIs8T=N}D믿>,㏿*Ps7"cm۶3dY~n[FX,fDQ#wtM3MMM=Kc 4A}3c} w?sMÇSkk sϝd!dg?{7ؕ{̝DRz?̲ׯWYYx|Z0$˿v\Y1*AUU躾ToҰ$I8O~=z{[׭[7:ÇzoY lP233i:c̳p55Mz~t~IGBI3 #rMn;ϸ[>h\si_OgϞ9s̤6b\.Q$P]`ޣؠ~oYN- B E4={.V?^:ӥe˖͚5kѣ/0 Cҽ]K,󧍍u3 UۮWRJYFinD^hccc!}%B2c^a3f̰kϔ)SjllgYXvS=UJe9zY\,BHoJi1LX`b322Bc־k]X˲`$%Zoc8i0p ߝ-:LY'G߁<@!c;`n7X8wgQtJЩHFy$"~o_ru/g2`' @Z !~,{PIENDB`ukui-screensaver/src/assets/weather/66.png0000664000175000017500000006604415172041035017474 0ustar fengfengPNG  IHDR>a IDATxwŕyrF9$D0`km]:c#dL4 d$I43t熾UuΕ66\_IuK?vFD5`0D@.썃(~uJM[^N>l#}z& [5Kמ. X >03z0`6tx_< cP!| S|6^pee)k@-wMvAb=T*{x^V_P;j{Eo}꺧mϝr^# r"  ~4%B)GHdT|'AP"Xyw @g}v(g2y~rؖh?Dp;/<󪨲U\Vȱcɑ6+ 03k36Fþi Z`@q@H}c7F3> +gþa#6J) O[, "|92\aEm˲XI} eBQԢ|c_iD !@ k ˒ X>|e'b Az8Xkmp$"eI`Yima1l1J1JѾbBzv$vuYWs㺞q w}2L}_+ˉΘF;1xf6Ckac`lf+L|x̜LJJ%F OcH$0|4lTp˒عc+ld2==T0 u `ƕJEMgDjz;S"`0'CUCucAFi7הּ*+,q<˶V &~ %ƷƕkG}G)m _[^ݷ*s6[)xkʰRa(&W'rJ>lqwk?uJ]khB-M.T}'y!!Q021V,K|"^۬T*2f;;D1 <EBhfCL"0S+ (0 / " 0[8qF.M0b8 |ΎtsSSm=&5ҁ6 ]iF:qLD[p4aB `Ske4QX2e2D?=SG&M^ )9ukp3wɹ(#§bͳŔ#r0fYv?V˖/knKgR,)%n(TGJ,^{Yڭ3:uѨ淬>12Ͼ]YGK!2Z[xū|%A,f#_W޴ozbj{!Y&zE8_JBs/(<јߏa۶XeIՌ; @gg'@:q{v,|Eƌif)% m=0,RJH)IZ^>OI5[dijN˳OB;"/ܩ{ѡX~/6lM[:R{mRݝQaذ1KR1N$"LF9 #;9G&cyמh/9{9}иa[zzs+*b5ڴm@4VV4qvߵ~%?rNX, _>3h*0tr 0 %|@G?3 DE֮>1 ׁc"$'D6I=, {wAU, "A(j[{s_wK .Mi*p&Sƨm[y]; n|Rݝd4cÆ@`Шd, !`"b*޳,I<͚wP:f>};&Zj[$S<><>d͒C?sW3-xEDq]_x8;Շ=7e_ycqPۭ<D:bb/m+~pYiqضL5:)u-'_sڈ_2.\r;ﺜ6DlGYd>}M#,d)aɂ@EZM]EGM44a ra[ۉq}}x晵%O<oy Id2 "_"`Ĥ l2l JȾ9fe _ТEHD83g?USWydjЩ a&Bi~%}'.G~V@@P3q]$&O֬AsJyd%0bX5m_]pT03uvf`c<AE0PF*%x)??yqs9f6O=Y?Uش͎cl qS4':CLL( dCĤTmKJUĐrsͽ?{oeIVJ+!oWlUkY;Ѿ$AjIC)~8 J`H%+8[Q˖m+)bkF olށ;\ݻwV3ϜȎch޷o'5M Xb{D 6SN<OgN 5ݭ{,'bv!#$>II]yD$Awփ6G,|.=/o-/eI#@OOdzd,U6 8%–i(|V@鄂GhЂ-3 Q--^\n*jG?RpIcsRUUDD= °DmYqA@2d @OY{O=[! tBet G|3%ˀ0HNǛ\d2t² H1Jىdz豓_zṵ̐Rp{{ٰ~/H*33.HT%zV0%TB O/)}@:@0K->^j7t G{YصO`Պѣpg]Cjn=,@U" 0gwq)=JN0u~/T`4?KNr2 -bi VDL2_nȜs` ѳi>ttus<-@P@H c2b(lh1xYðB!!0 Pɦ/Nu æ((ӌn#F߄[Ϝ9Cis;/^;҄S&bx8p ÆՁʫ?tj5r!#aE9y;.M;6sY1ұ3C_ϟ…O:AGQI ,k""RʀG67'h+8=]- qs/ʾȶ-z ʆna 퀠`#1I,lE" ƏQ'TK@<*P ?EvvYB'8aҌ ً$ c'߂Ø`‹ǍBA#GW2hu ߒpw"{3Ғ nNzK82ܡi 24utfFee^~TjaIA)G1R`sT}MD<߶bT=~7" D"Z`@ː"4"kZ)( |$(=eN`FÀt~~ ;D=J6FN924z eK7#a==y~5DTǍ2>%$.o`#h}u$ BMg4yi_v/S+gp (M\%(XRb.xR Dcb'@ڵ=uCwi"8B\8ADEe@>\L< ˢ@hDxi%6`GadV'| Bl{bٲF=R7GYDB,ZaP;!mbX `ۖZlY"l܈XHQ˒}AE}Eeb S>-}U!\(&Ͷk۶DA"d*ʉd1g?q,k{y >H !J@v:}/zKA.0?7`O + *"+O; ?㝵PS^1rqlD64~D<=mDl4FLvawyl f ;o LK{wtd`&"2R٬s_{\0=_.b`^ڻ7m$-QT0J鴋;df<fH9`ˆM^y#$ D^M{w7W,s'*Jiڄ8`.  @i/V9ARt.j43ZR:'~X L@N X%j7*x䈯É$䈨Nx$, 2 6nx;O }"d!Ú6HBk&m !!L`bi =, |垪8faXA0+iPo=f?$p}_C)$e :j',7ZYХIkDmSEG $ AF0rD`J`{E0D˶HD굁cH#œ@Ifds6qM`8~8 I0E*y6Qøn`#,ba \1Ӥ[8!jVX Xi՚#Θ.g],-Eq\=E&ߛ#gs;=@e]K˖$<1v}\6\׹W:9O]_kJ: 1IsՉp+hБ}&=?8i렩_f^ݜ ²w )O )>hXy;bc4 2T˚lH-e堆h;0y|ܶזWhOEd{%_|t65*Xi0hz/K(Yu,̅%łhtwKDd6LR0lDv9sH WY" %fٰ11L\(jM&۫BABV&Hdd Y6R80|(@\"03$8A1ED> <PZiSO OB oO"fc礔ʮI-}/>E-+GLGژmv[ I㛷﹧#E*USoO(B!=k2 >*"yC `t؀"VQ **^+-m+[t/ E h@xe }.slN$+8g1c¡^.H:)ܷZ;]Ix@!P1 FpF~"261f<.;"A+>%PT,lY-*ԡ`).C8(\QTȔQ|PBH|O}?iB"2I^NڬY+ɨEG4}z+ކ;n禕-MM‰YM&R*/M Ϻ@@j 0t?s̢Z#GI/}iu; * =*n[5hMPaٺ]hצ+[_uӚRQLlo${#֋ӫAn_TS4DM~@Ҷyk+vf9vd2ջc?0&KĽ{6Jhm'F?U\_{lV'7θ!sx;95GABAJgQ_'}}"Ic?VgRymf "  ؟هrY>WǾwC#s5p<g4:ykUZ+j;oG}Rʄ}9vANDd@H&x/ܡtl0h4*B[4>PLp`&#+ #_p]]4O?a]/e/9u;\a+UJoQ+|!Nea|Pe!P2*79@q`C)oo7 }nE# IDATWUUvOJu׭HbذK.tv}}{꫇1jf[WyJ šE+oAoUl0*rm9خ`&pnyB)euѵw^˧=xo;toz[ҢM$z՘ʬc=3$E `PC:V jj6 m+C@3/ _QeQjΜ/6w++lMYuJ&ԙa(mFQpclVy֠Q/@?O@]sx:aÒ:IC&-ZԾ䩧v/t][Ov#&Ww,; έذm'* ?Qᒤ +|WO6z-[̉O}i'6Lg',x+*nw^u}EES(ںҰOQ֜͓֨O6/gz{}kd)4=ztjĖ-?[;R),X0%@V,i!~u 1bdM I)T23u9못!b"}/n7"-bkMV;1ѿcՋ/.uo"aOw +%l{3/ٺ~M-tzw09[u1Z#Q+߂C0+Gx=TzZ_}H'6cw[_xuڵ=Q[ LQKxkyZ;=< +8T^2Do1sn:kܬ/-G >q!P*f6da#FE&ON$jmE+^}}U:R)[ez1LI8kvbh=heB_5oy>N"d^ԉFE&nf;1_yC[%?ŋ;V_߻=dRϟC0k$n}Z#/+nj{-YuKl֯n`[QQ$ζ.dak!R2XVZq@ iYŽDN8uuj;JDzЌ"ϥiu+qT>Y_Tk@$(۲Q?y</Q]\,X0 Y{꫗EaXLV64D"Ne4*lY(eR 2lflPDPDe[q$"ɶ %2؄2>"&H;N,&#Ѩ8R"iN{Ec|Ra?MQb" }wɕV_Uo 7^~ +Zgl O,O@pzYLXUo2$ѿ>R8" (4 b(eX)6ZQZ)1t"gbض`@&iYBHI$ 0U{[!u{n홶^gԐyQ:챡ڑVK 5^K:eдË۟p]eo6Z̜Y7a߼vmDBXQV8?r_xuu$j&L:bDrpe]p1#!0Jc R+J)J mOC2BɰXICp#Al[`30,ZJk!d,2M,m ض0 ^nkoϞ¾BtvD.5/vR;U ycO9{3KOeBu2vV}f㧎>˻V='JOE ?(У߭Z^;4"oS#6YK8ٶcYMe%%lۂ2eGe  1es:2=tZBAK(ϖq.d\ۻy?2 Gn~ҧ{{٬fJEh SI#=Wߘr#?~z¶%RD`rGܼyAO,EOA@"^<&ʌ U#:fbmSs.ݮ.w^k^s߾Bo&2g \DA}<`f"=/Dh4=r?qcyƊTŸM6y ۑҶɑvy1^:||=Ӆ|x{>M}|q3x#:fMo;cUӤ.]qϿ#WhZ ѴQ"fЃ1c!^%81b;uAGۼsԶmٝnGa1~/yCgf}'H3`LƷZZbN<ÓCf7glܘv Ufv<_d-{ܻ+؝jǘ['?Sq+̓c8m>$`h)!eFG\}?jd3Xյ[Fg|cZ?m+S%"t҅G%#mF>U5zM+^]ܵT)&.~깻^ʳW_h;q/5孯rOn_$>~|3wn<y L^7q֬ɤLmۖ۳re{֥wi)ɳmÞ{4+ɾE}Ħnj}W37V$59gkiX)Xwe+~J`.=c҆eڃsX_%OOo>ZXH4-V RZ3W-u7wqǑydn`׺^Gs g~pOK6!v"@Lwz O;Î[>{88H'Fny"s:I~uٲz{t4*U"ar9)jalֱ ~y˗_28] .-^3tg †|ow94&ÈDpL%ik1lmzECMMQ[P|0>F :b A߬7FF\rCm퉷ݴ,A K =۶zp#eD[{ڻ}oV絝;mT] DOC@4y?fG~[E=zۯ[PD{VE.#tVY/2la~1,`3s%+?9r7?_~{^UDLRѺ\PE^CrFv?{rKKoٷMy_H%\]S{]>U pYIBd5ysM66F>tNs֌3nI >xꎃu'!"_ ҥ3[$3eQp?SƊ*^X_qs'UykG75=nEQLr蟶6M:Z=wv@0˖XD;'upGw-P~6jYt×>]h3IH&'4XKbКwDa w|ޢYeKޖ'e[}ڴm<:p\-ɂ]Isx ydZV3l ׯHӾ5uj9sZ6x]/,]v,&=!(smS> XQ.[A`I)l)aIAIA#-uvt+\VVƒ1ɖcv%2ؾCeW]딧aL$AXhsMۍT%}nk0.JnG3QZ1B lRqu ||]źBO[ǻo3CxOIJt5Tv~M ;Cն5>i#wm^mG5㈀"uS=BA>ɷYWQa o3D\q0!B4xJ&2YQ!`0)GxG5=OvNU0<%*N6Gvܹ ƻm^n*e LIݗE'E䪫E@DkHIJ>RS]T%VBJh yA{5EKC6mUޢ2ϞgG*ziތz5?#/U3T6wxE.jQK9$,%%HA$%I)IXI)CU2Vq_D+m\7׎2mڮ4ء;8v}j}UN3gxVM>刺: _W\FԒ6v%;b'_7{?qzS*1KvQFֺԬ3kEps[G>g΀0ydo]i#`Z 2oRaD* K& fXab֚aL=;jgdu纪IKӦ*N]rYj׷ua 7)A;ۖm)KIlY`Y) ȉ;{~ujQmy2 p yyڻ&~ ya>Kz񶟕|1J}P!/{̈ $?;o-T{]v˿2P[W5cy|q__Rk+#!}Eh_3 }*C{, MԿqI梐@9TX^EG[<1S'XWJ)"y~0?oDGd`#>;傈wYx|_\rWgߞ};8uUl±ꀝGZBHrmA%qAH3]g$N[;VUT[=VúVb]z9rQuY!-߯Qro~wS @+Ը.wxO>[{lt_[wVMg)Έ)ruxw NJbxN\ [K1i| 9k/>($ mi bV…/-ZF*en{>z5gU;bhmS Q53sϥ*ɨ\.k gTȡ1*!}f}y+c!d{#Gʺ=jBR # #*˩|&~뜯#!d 4qto=~ჭN%_].zrJe};aVd{gnye_DٖT߭z{{yaX &\V^>Q0ʉ~Ш"Z,A2a 7R:""-оB!횥 6c`AJk(6a(6G(/?5~ M[_\;8 Ӭ㻻;9eإ?+nl2hM3⍟_m4'5lt[ Ɩ[ɱceEE|_u}ƌ=9'_p"@k']W.dF|3ZsqDqEq6ؼy♌ ?}z݄CȞ?sg}Lۮ]tZeaWJ)OJ`fu}RqitatK,*gAD¶ 7(7"bc8D>ltG{b[>0M:VEsK#&e£܊Z2w{{yQMF{ =\=Mb&,m2=MQ1#$lBw c%d!;ꏞ/zG |8܊DwGtYV18?y76%|^RRML8[.}]nc6?}wU}LO0%)H69+?u]uaՕU %dA 0gsWx.hX\w9szU~5(EK-; Aw">*ANM$=ڮl咀U{ǣFmPê]ǎ[7Ē@݁eec]6Uo>һ1d+JZM+@=Buc~MD+\ҢGN"Ok:hܸ w|CM5d C|y|ZN}Kkhә~u˛g4'5Oe[k\ 01DYsxh-2|#dh9Nh<XpEmʄF&APd IDAT  *=dm D(SOXvEb;sZ^.6\Gު/*>[Z n\ΐ{LLb~Q\vxa4>|=C1J> d7IJkHipWJ|jzXo\ %-jWU/~ UdMݙ/7W 檁@@zGMnRt`]"u\6yg_^sUm\x}DCsոJj8=[pcowQ"IٔaJSkMۧא6( Nt PH@M{6vl'3IޙnjM3fj_q3$v?vo߾x FڠP(BR)Eb1P#!$6M &0\"JM:!IB` 25#p7 i0TYU:S dih JYO:6O&x<輱)ܚkGO7ljV8$e̘MX矟 ;skçocG^YO:SոSj ^_x4煗7CQ0FR;n81F !adaXJa:K!$!4QR(o(|q[{s !{פ-,3Ls{eszbS Dr.a(`33,F\vnJO>}zt.D\ ]=덛>xO=ҘON}O`"f&mbnx;w-y~TOnrk'?ڞ'WO VUhȐsDp֜Y+Vƛ[/mi^WX1<.݇*LF+r'4Yi4~E,G- q:KIu] H&߆!iJ<%J!S%dl1"cۯ)EaDC1<-UcgBKs̘MI)'JRPeUξG{byƎC `_ϝƅ6"z, wþv{a~Èj6  5_@t^/KK]Փ˻ ?%l/3С]yeU ZÌ*q^Vx--L5/ZzO˶z[SptOh|`d4h`pv^k~!1 .u]"]sHB00 )4 [ i.1(0cxRL0d<ɯ E<9BN!A)DfRsz}I2B!`PomjJp {jQU'-ߵҀWV_qAH;6F`s6co]yZ%47.xAm q@ʸqh$CwE55i3g_[S*ᵛLV ,=juky-lvH^ܲc0--/W )wU׶,7IƢ(U 4q3s=d!LÐ 0RJ%XIDB01E`LF,Km&ʔFH"'nƥ?jܣ4HDQ3i": DY34>K)QD(A";𠓺җ<5zMfw+?Y ϟDk__U{j=jc,zo Q3I Lr^}ummF?:yg [u{|>kVziD?݅wײ kr݋͗32m6?iak¦xB CBȴ" %b"V{Ba p :aNE2<}[p dTwa0J.`k㸿DC)!0>{ˋ;$ C"Nf)Fǯ Uύ:If{Lc4| !!Y`Ba14 ff{k WF2읆ck%H*K.$69{.U7?qUAP]1^?⑏g8H-Z-%u&GKzֿ[ջw$e_{:tS0K]<.*Z967w`y7],?G?,}][L$jۍq:RxK* /|N?8VŇnw+u)W5%هso"r;z_{x*;6|7oWLQSO@k lxB7a&Z\Fm;'_5q _l B sB3i>wvsZ 8eo|ɏ%i ro->/ߴ9=Oydy< Ct&z5}(9#o ط#[G:䗸އ ,K]e@HH}/_R70rYPkj= [1@Ed~mW/8K8oy`__Uiǭɳ?/7NSUqpkӎُv2$.rq. HzY\^X^\R,"DN'Q׭ Λ6Eư,A?q]aXv#> y/hZwu+#ɣ>ۿ=Ⱥmylm[}3.O 7]蕯%Y %Ocxaf[NqWᱯ 6{c+-vݼ+ b,24c4iE]Qwpt'_mХ 0{-zÌ_۳:j&P`l[H1n7QJK]eEELC)vJ)1pѯj^3{vtJ 6gBk.}eҤ- ^xCxòt q3ocܷ%W |Ik>sKOʹӟ^m?x@tl?)=׾ú_}2 JSow$8{2\)5 :q׶;vE{KGmL03kn1"Dbʽ$>(^}/8I~@GD4MK)^!ƈϚ'pk~'o#=cUkN 6 8QT;nR|G~n#/+KuI!HVTi !:^gOdweelosVg2X'SI x o-ӟ>Ԥ}操[@7mJ}gzdKGՏ>lӂw]Zq8 mʔ3gӺd&#5z#T?cHU* V &pDŽH pCK܌3Ð"SվTI#PJulr.u%BZ*`&BaXNS)?&8,U!LEJfb ƈ0`E]^/,#P5;3Sz"KW},_"BS@f!YW]߿o_tHlCtJ1it^ Kq3q{ȑe[\paXT7g:uph;oۘ[:Ci>ΑűnHi$#iR c:?FY5ưTAee4BuaM<ۙۃ}Čc0ÁU.v{Ai $ C^&H7}d+!s[8H8(EtRJH%m?gG!dRa0SB(! jQ@pna1"ƊR$4IА%&%zPx!dR`꧃BI_8`@ G: `9>z{%KV;)>[>>8EF^]-_t;SN1Jj L.582L.aaR:.xÔ bJ)R0F 1˜`` lH2vX)=BRˍ$E!G̀1> :QFRUL05'Q3Ң듍Ɇf%Æ 1AՅsigbpv;H8$)`pvxS׮ޮ|ƍ.7S?/;L7fyD@fw's\!C[\8u 2L' i0u.EKHHR)V>ԃxH"(E&))RK cHa G\Ä cdG,N@J :2HČBzhknZ[[ẌiIStI)5(ˈQʻa#ܾ.tۘ8:7u0pӒh)q6dwľdZ7(#华dE m&5!l`f %kK{ ao>|v~{$XhlvC3Zf^rÓ0~W:vl[ׅc{ؼu.SU̧M7Ӷ3f[ *wkF>#]+ϟ?:n;3{Ju,e3OO,C=ZV{_۩>xotgѴMfY?j !AHnKI^E<^eDzdd}9gvm^y7q8v2Kڇ근&? 싂?!5n(_}{58 ;lܸ \W]=Oq,W}e-5ji`gSJI<6-[7@!/is^Njhtudd?Cާ/{񙉟f}5C{t6w3 @e@GeHIj'̜6@CοQ(>[4` /bܰE9G~Ts_͛v͓|~>~>\ fqTd"MId7]f)ƈ!)# )0T >UX{`sYcWF ڣGF//Z-k\kWdK͔wXƏ\QsoݥEE΢5k,YR0Dԗ诼ګۦd{=DJNY)/?sl`o^8hRMC2ڊxsp7_[/[+`@n9oI/3cv.Ro_( qTZvʔ5WiN?#Fl@@(E%YVR*/)q9 4e݋/s=4 5oi[n_{O,Uظqx+΢[omwy d/[ְ~Ŋ Ɛt+;x򝱕f߱s;|Ѱ_Dns5j1Ux?PrEvv99j߯:؋b) ưY=y{{b' zs%7=ͭk;A(d4SӦl̘ q5?Q8jTٵJ֒%_}y&脠h n@.s̟23@ dJDz\.c ;1UJLS K!) RL$E${-:46QW!~ _|qi>>ݤF-RƹL|Tt]8}>}u]ۼqɒuS>rUQ*%(t8c>y].b @94-Q d2JJ@8U B!D3VMzVsN|-*nysGunP/xxZg8!K#~!4Lɒm'bRU.CZxBa[_!3/6萏­fg] m"<] " -S0SU@HBfcSVW[>p VYSji QM*Gzfywc#y18>61noذ+];vڵ)SQUPQyy 9t]`](H躥JY!-T0 !0RH$7|F,,+Ҽ?-{_B3}(P΋\2Ki1ӗBbf ijx )%=R Ô"(Xa 3JA!DLT]Ιר5@Kڏt箮4yD DV 1&"() >i< mА;t(qСdM05Ff4e! 1#&H7K]%c+2I6>}ߜQ7MV|G85gGlyNr˰zHsVrEEؒ~q0 Kg–~IÐR(mP7b3g #7 %"9*Ndgv\Ull h`baL0rFU&!` MD'Q#Ңz0ԃzK$b 32!4B[.%ӔHē^ @Əb٭ͷAa'߭^˵ͅI6~#u1n\#jn׫si$7a3֦Z[X(dq3iB3My<3&vVSJ1EUA[/,8TNB0%f7-&X(aٽ6sYx?.$RO$t-hԈF<H bf,qIӔz-ʢI.%fE=Nbp`YYJu_CY_oE_VV|H{d`UKsdK[z}Ƙ?2z˒i/7Sqq2&QvkM Sq96lΩ\wVE3MS Ns@-PpfQ|MWKҩ&c~S?%0OyvG߽;sчTia[rj-_M$r/эlj|/9GaJq,?nק?(N&=lv&9~}dLկlnN2OҨ_'NѨ :yKp0X]ۼnΔ&83Pט1M$x}eKNhiS8Q)fMӵɤ` \1p*KDc۽k770dc-N y @H-d[8% HTSq*Nũ8TgqQKIENDB`ukui-screensaver/src/assets/weather/9.png0000664000175000017500000004240715172041035017406 0ustar fengfengPNG  IHDR>a IDATx}yU{nmξ$d%@ j ,"!j@DQ?WTD \Y%,l&aM$!$!{f2I&gꪺGwu* I@?y[޺=uK'-w:t y:ܧ;Z~X,6zpppLoooAdRvڶݾEG.P7K^ycZ[[ܹx<>ն:"UUA/`f8!DaMeee+'M.Oyzesϝ~w}8 0 t](LDRQ$Rqi"L"qgϞyXjz/Jr}]z+GQ HEQ$(BQUUiȕ|mem82Kdߏp8xܹ??S_ gҥs|YuLuu5u]N`H^_J q`YLD*B"x@T b$R !۶˲L&L&fyy?9s<2nܸ\*?J) ,B:xEQ*%;@DB4- W 4[mۜ3,KM$sp 7Μ99p(@_N:uB(pXɓRC^?dNJd8^4-D èFii)} / 8{ڐms:VcR̙3/Wi}z7c`@B;HEQ8hrt!K x% %z~ wR~*T ? n+mmVl۶#Ho~k|q`%=s]pG~8 ɒ%P(au= Uu )\xb{RJHQŎ<\jU7$с /~U'tƌSDd4U0yu%L5o9W41mo|_!4r_VP+|5=|PHF"ǎ1cPyy9JJJk>Ih2Kx%X\\ܒu, UUU[L&?>Snjj*tV$QѨ0aO:jjj0|v͂6`,m$ v*?_:9Dкub*d}w]  !ӎ̭A3gĄ 0}M:ToN%8^} K\GHUUcڵ o8] k/HYSScgϞ"BBv(]*\!S1RI׿?(fx3*$ ;:{7pM7'S2i] țnk֬1c2ahUUU={6Z|1ܠx{x+F&  Qs(4M$ $ 7ɦi5HH!ĮH$ &{A4Lx̙_FBu+ )yoN;@Pj\x9B}T;!r^mh a&`ЎG,Zh 5?A0RH"5kY"M)//犊 P/4lDJbB:_ B> D7tXH+q)Nsw4M0555﮽ HpMvJIIIaJeee>Bӫ̜_]h!HA[%z wޫ}7-mAuL&x<ӧO455]`{?Fֆ@p&Lpr(b08 ?]{_(S_R 1X0Tsn(p 1{q|Lu45MAmʔ)_;p Gj#rKD0`0H`SDL&PpW xsMH@ʄ wAP`:)BWO]˗~Ǯ~f 0*r r2fe#d ޵L(?8ƼGءBYd֔;0c…/07&!De8.uW +(ITzC{z ;.PBB!*++;J#n_(n&T '~ ) QFɠ?B!_#__E!Sn1~xs};^CBD3DD9D>?T?ý ċ_ rj +&{(oۯ i?H)ܹsN6vB^ΒRu}o '4#Ux à`0Ȇa 'F  ˲2Db`bk$Q,f罡{xҬTl!7KjoQOEJs̙MDcBgf+`ffOF1D)cL3Эbu&koZ$|?R ϐ]ܝS5M#EQdyyzG<[TN{{{s|?0Jg IWܕ~cޔB`*Lw1c8RHƽwcB3f8vECE9 dcPa4)#q.c >x&2?hz1л,wU 7YrC=o:guudpװ]۷oё0rd(8t{|y%͇{ވĭ @ ~u.<`pK{w{8M-s(  !@80jvht}ݺuN:+Ns&dr?/_bύs{3~~o5 atzAWC1ί_76[~8JDB@ I) 370,H:,]tI'4[QR)1p~3y$ӷbv/xտƽns#yɯ~tJo.x@DHӶeY0~Gчdf$Ҳrʕ-vڸx<.5MS܉ w_RȆ*߿|ʫOx̀ W;F&($oQt|@~[_`Q鞃 'smB  !"yK'x3g֨Q*Sl6y#?p7vrWBݔB. igRJff @*;#0U!@tpp;NW͜9sp6޻^߯Cx3;Co!3ŀ0_P1Htd2駟^(2! EUU]J# B۶mk'MTm#$ 9a~{Ur@{se_,;XPŜb BTDnm&'Id2<-YSȚ  Bumu}U㐓{ M {5ޛA %J03^Mq~[w!s)AsP*b)%曻zd̬bKu ڵkиq4Mt:-s@Ӕ,;wHp/d 9{nRt/6xf|߿2(_~2X KMT*ED±,K{lmm}!盥9 S͑dBaf PjyݺuvQ]VVVJDtwt7\N^{n\pKw5Lkjo$L/d .XJ(K)%^:`?:Gvk *34"V1@v vX⭦^qB`LuADd۶ -oT*ũTs %r_OgB/=Z6z+^5^H\1MP(kvw:2N+7|#۷o9WER̜8Y!\}\%ҳ"2{UZbXoo4MSRRJ@8#BJ)Tro!q٨0KM$yg8 }? SCB .)'IpH}G>!%$ɜxGy|d) E>gy(7JA~}l2ؚ(0 c?(vt:-۶(98g#7.3ӎ;'|_sD6`QYSp>|pirb {g*K8IL48F@%<  D"ywry͟:uj]<w>:ĒeY3il~'W\xUOv5eYLDut]wHL&Wxob&46Mr;8)֭K=ËG]}O3fLeII0 zgNcsٲe.\k+0ö FpAb\9Bv 2r( -U2=HI vlfx{N:kܹx<.sj{@d?YU):Eim)%ɤ4 tQArw5jO;7qrKy뮇bX ' UVVՍS[[[UUUUDBa( ۶t:m unٲe˖-MTȩdH߅:P_,kgLI ڶHZ))t\7ȢU@p Rve}f|kq̢Eڴi;qdn4McLL~m ft:[`}}}!Y`0}]m' fV'xŋ/!RzOOO[OOOڵk]k9va۶d,pvm]bD[Q&&c%c >d'c(̺{x1B*u3NnmnWWt;vl{ƍ7c3cƌIUUU7c"bw-Bl6s.=-r͛[+ڶ(O8q3fN8qbuuuPQ IDAT|h&kƊ=ʁP&Ν;%K{^mIc$ND"Ϝi4S9ƹ" `)&^:1 IDif ,x`]ʌA5"@`sϭʹ q2kSQ@*''Wl(,e. Z[[7n|'M402 9\Gp? "6LD$d:UAZֺkP090>0"9";#(ѱc bo3C>S# +*d*+l" d7Lv[Ej9U-,@;gGsP62vVc { ,]B@RfsgJ"5?~¸#mXWgL.E($`aR`hYM` gF@9ӣ8W:c(DnD*8 c'6T[31&WNn PlR/_>,xs9wh2<Lү|K|wwdPOq̰8>ĒW7(\̤(88qX V%$MؽԻ;HsǞ.>q[d&~ t7O{4@)& ;|}\|' -zcأI:sH-Ci"?[y¼芺5lYpJl?N?I$j9z;أDnF66/bXr{ߥjՌzd*H):s.F&;(&X` \Wz}) 4dy/.=@u֧S>l6sϜZNl](.|;>5MIbJKmPRq Et$級(.>{?~.O>! #3B 3qkonq/"xÏ#FN m׶k8{*zTPou!(pGb+P`s?PTZ=8 )G}𶯟zvǐ p|v]:n 參ؓ(=sG_.gh`٤ƒmcB#r$̪y~1ںS96R U.6r{]3tz+W^ h3O|Z_[sKXW]{ͧv̚T3V5~}=XY; dymA@vr?(hZIYM@Ow$^c~}G.TN"@t|T{a49? 58U 鰮2^kyS%5fG>VZ!-pxzY(A@[p+(~櫳m^rقNz[D M J)3&ҹRғǧbGN BSMR4 B 3E6qcϼoۦ#HD= ׊h!Q:}izGlUT#DzzdMZ:w=:YƴYj֬(Lc@0b"1c; "XX H@gOTUHUN{_iR .D0^/9,@qY#ޗ}ؓ2uC'4f"˲h,Au~ﻻniݹzs}s7{_7/ VZum#]pha2VUY/dP2t4$'0|_sNK,P6ܷsPx~`ȩa`ӟn|oh]/E"8Rm ^ﯼC AE6rѺP'?QѮNz[]pl٘1,^nI4fޝҼlg7Y]}t %V;R@3n֏L "}ӟ{߿)QV?g2)@HV!Abz캷omo|vNEݻb~M9}w|j>3BQ@=޻PH.X_?MaO3t5/ٱ酟?$8>Ұ׽ؼqn)@ܑvZׁ?ɍ5\^ .pJeVu7>V颴4=00SWhPZ;wo75UUF[ӎ\{Y%GmbUWoilрa&Du7ۺtB ͝B H 4X+>Qר=Բn}ؼ8H']䍆Q?G(R`!Lojk[sK]A36QnTT0H X[1фFzHKj> B ۷Du}{#iU⦔4jCdD֚ aN7SI*egW/unߏl {( LyNE iF*竟NoR<{ *2S핗}_h+?ģ>"ZG3$BLv~ƍo~(M(uZ}耝@f[UiegS(J4bV;쿇R^+hocI@`/rq&ۯȕW^d#%'?\Pz,>Â"ȱEmA? Cݨiz<2iiVSfbhjXˬ@(lms׌o]ů>#|︡wvӎ^Z>wpx.Y>±Ǔ Vѱ{{0wMRMMnغך݀wA'F^)-5?#ii!q QdgFBE ~خ1=wgGUW49KoLzPK&3T(c.M$6= ^?>ycֺ?zkȲg=?fr&xB*`4?TO^kq;wCbW:Vu+)ph/s19 hPI=l-o%O8cȑ7\"g2rÿ^?%Os}ղ?yw>9$Xtd'TX!SAЀDƮrCօ/xAE DTs"6 wJA _} k@I`2 L$2!*Ջ];;W>XO=ͦueuεv7ߺP~ /ٞ/f¨Ddڋ30uH`j'_=s>N@$KQZz{\0ȶ~;AY '.cUTVK.*HSwVC+^z_tSǦ$3'ZOB=6U%^|"tC~1k3({P T?$1i-M2Oq̙pv,:fkFyWF;/۹su,k&;l[Bl{}k;ڏOGJkh2G)K.< OaHpjmggc$]h=c -rO :2>賩fV Uyr[G7+HHr þc+yS;WS5Oo~闏v3\AYʶz ,tX|:m'H0D N0R~r-!F?-MbRh*Y&t$;9zW@M͸[nfJi`zpqnU-3A7%!ڱG2]K٬aM5=`258}DB׎ Gu)@aMUWˣ¥cHAM(,.]4vk #AQEKnh^Q +zI(n{͊ 3jQg5♃N#zP"kV=~Zuk-ĆW:*YiGR2Gj U]uu_T`ڴqt J'Ŏ;;7{ qdæQ?Yi5k$WS?;bcAvB.V/z_ċQXP:;YSIG&c[֪5zkS'3'%Y1Eytj?LW#B$ym>cSV˪ s9}$OoR!3 ׬yh<ٯ~oHG yy@-~nK'7!yW@ycՏ^ :~oM-׸!;]N32^)5>t&@7J2Olxe`0 t%6t:,FWL:vŚ=cMиDоfT`Y2el sTJ+k@Q͢v_K7%n!TSSӆ <5Ԁ+̆c*C id*/ .iu 0vxM"ql[ؖI}[YYؿ1hq`aQe OޔMK@h;SNƄ|E(yXoXrRQ.RMu>o|>;Mp'rZ>!tfׯ=; 4DsGAQ<U:̶ ) Od^ʖHʹ1\-2=eq"̵X |9$M±%[CA{EP@Cg*kmYZ\9vD5 )E $VL7:^C.jE|GƳϖG?~SPUDH oہɃ]=̻͡ b<01:4o͒%6jhA!Q]k~M'W*z~[<^}/;;(PrE(ul0kkS糟Oj߿5OMWep 1~tX']R׶mc6cIDATP^VΔ#+Y/ǞU [xm|>A:yoϬ||U6em" Z ]*|зw_r1`ח[nSw~~Y1SA?J<:(yOW,:Sϖ6[TV֮y#%a[S-?|%l l".- Ni,@Ҕw\l ~nz5Cҭ~qU"шGºQ]ܿ3[cP@>}d>*{3` ]Q@XqGo4ZnleSV/emǽuU›#?̢%MW +h۶c'$/e Lw̘;WWVySPy]3S7mT Ap.8>3|n3{]][G:Bb4.8әyTPO`e:] O^BvS @g!ݵݤC@.YçO0EAVB6K+6SD)^H{CQrGoY%(Dd@Hݒ*@L),F3dspj`WLU3r ňЗBkVެRNY5@sޝ?@dS;^[vh<d;0Vzc)HYN;V\__x#UX]c<ѡ@vsU?\Y]4i86yl;?ZhKXhJ (l7 gOᅤ*$.׋µF^sǬ PDw"t^UPB[P#3ϥŻ*z#}PyCėRUs{UAr7y `.ȏ?(Jt̬#a'whhxQ_| ,dA@P5@j#z4^q~1&1BT(M}\=/ QjpHLeJ׃K'<B9׮IENDB`ukui-screensaver/src/assets/weather/16.png0000664000175000017500000004645515172041035017473 0ustar fengfengPNG  IHDR>a IDATxy|?>gfO a&,ʢH]VXWV },bˣP**AJ@%la K͝s~ܙ0$}^y]f9s>r>s$@2P @T_$m7FqmwYvuu巷B1X 7746~j0eo_|`DӴ|Ν79rr]\.x<\.Ȳ A8!QJt](u-''g٨Qvmzp»Ted 0B Y!"$I"BL皦AUUh]9QEY5a„]z饫6·v K,eee2YprzJ<N'DQXTT$nt:ijj*/--%^1PJaigAUU #%UU潈K01ϊ$F4PJcN>33 LjJ+mPdڴiFx4+n^ZZJҠiAH0z:ԿTMڊ`0hU "YGAp7b13D4AUUaapΕ1c+**j6+p6@s9ӧv5'8N\3tPv `g>2mmmEWWTUE4MxA "!>@$I `DQ(­i UUP(@ Ji9ĉ_,++54M{{II<s9Z>DGGb("bt]?$%HHMM4oaZB0:i<~H$PVV{tAl`?k,kiiiBJJ F8ITV`~hmmmF0@~(e#ldeeAD@ɤ̍Y[.DQb1Ť6Jۍx(@(\.SYYӧ-]tZtH< e<''gtaaXY$gffS]*2%lg7sh2_eB pM0nj5XN̡iBͤF(&Ij[[[>K7"OKK;dp眘C0םL t`ɲ  ӉZ=3D5&s ;0!8^;N)=== Goδ^$Isu]G{{{BmZ=lS][Ur20؇79㜛EŽt3S9F?]KYb,1\.S#(v}ׇ~k} N @!H!RUU%~ߚҕ fGdk88*)))k̡Ձ14?[d-|f=Ƙ'ڵkСC[-g" 4\n0jp_[%b8?xiK2N,'&qaT]cs.D4iizsΝ[@rss/_v^/qp8e9nU5@)MdivɱkdetwX.eo7c@Gbt/I&_zYJJx5.vg>\`T&v|[NE対Jnzt^PP|ϗNB&aV_*'+9&Y&NTUEVVપWcX4pZt xMMMW^WHII %%%$'''Gbf~7͂9\3mrw t؇oVbߓ]dOj5Bb(=%%]UUU+Ztࠔ.u}bNNNr(++À8yUV*YZcnx;OU{L6ڟՖXb:1!DE᠀T `Oj(B1CdvH]*dD%DսGw&5hf!˕tkI}7/RG#&Ϛv8| Km۶ᐲ0j(@9&N~&xvwHdJF~2jfR(BBf$+BdP((^޼ |„ kaY?3:j(I8s1:f?BrA2k‰D{9 fҜ6WEኢUUy,cHu BFp8ۋ^tE/**2I`ĉM2:Jz!Cy޽7óU8fֲLOX'*'{=v-LXV5Mfp]׹1Z"@4m0` ~"8CD@}я~^eeemw)1޼H޹}`gzw?{=+cW}=Ƒ;lnnF8~%3 N4L𲲲SRR,˪ ۝8Sa7N}Xeu>̙%S=OŇ9x`Y҂+W\`oq 8Q0BHȑ#1ބtA$uzbzdd5ӨxBޝ9꩝|b2hy$h$IxHMMszKN}Ȑ!}>_,˚233c}O69Oj'KI֩N47ɪ^CBUCYMmnz Mp8 СCc555X`vm=8/z0w\̩O&eiyKJC;Ⱥ!Tz1tDQ ,D$.Iꒊc;w|߮_)8'js.r"fgw !bh&V X>35njN' LHO@IՉ RJ:;;šC۷oiÆ ߜ;p@Ñg80t)bqo}V &(>x"9G؞dͬ)i Xb\ycfzf̘QR O@)9J[[[`9)dNlځ& 4H b5nv`&p2I2NT~oH0$Is$ID._r :TAxIڣ$*vUnJ}Y5j?9@axћfI&vMc>1RȲ̇ v~|=z! Sn7u+B<qg2&IiR+f-;ZFlw=&~]OfZGw%n:9;;`Wt8p`WSSS4##a$1 y i'v[;*f<@H̲$`Wf' ӓ|[{kLp}A)%xxmxhPUUUUIIɨh4c qn } "'VrMӈu9ۿ[fY&j{n3dm־05i;KFis?'tlr󶶶# =7OQ:8)]]]h4l!f =|Qvošl(k{g\vd?"Fz,˖-[Y__ c,Ez Lq߿t3hРlEQtX 3ߪ O7; .7o7ۓMw|wdY쎡vؙQ6a+b6oU yq=A0 @%rQ,V]׉nw8 )V2jc7II`G ;d$V3LݐH$cdG.]W1Jr\ї88:A۷o~$h4ʌ4%TU%֝-ܓdΞOnefo Z' +{Rɘo.uCF{fj(<P(*Kify@:O$ZD) VUUUq8iiib4x.GQ27dN] A`{Ow}ak$DF']yݕim)@HA#[n .X]׃Ji5"G6nܸ]uJeBi& #D"(z1%SvOxaBaURoU4u $@7F}=D"¼y;pWF!BHsi8FݜB(B0A!hmm=PYYeƍkkkLQ1`QTu1:D"x} u%@nRo]i$ g2o-;O$AOf!,4p"{>裿"> SJÆ88I J)Ut]" !Nnݺu )ii>xܢ(JZVVVܷo_BaqBG Z=pcN=]ZEQ4̲TUEٝY+ivvfaJI k׮ ,Xoc6sĐM t]!UJhB8A02 G7XC&knUUt]8ǖDy2ޔ|b^Xݻ~f3ngy\UUbPaÆ>H$rpbTBpsVbFDQ@@ Άs.qzv0{MBM6}5t@  Hj3תRڃL8{0cXmsHu]2f^DUUs/L g+(  Gf09u]XbEӼy^j`L^=Bפ 5 vU߰u͌u@IIxHQKZo&'"ָPMΤ @3iҤk.±>Rf, :&`=XcUaSbdnm2]j0I㍦pp\p\̪-$`n 5kzu333sfeee^p$QMh4vtt몫k"H3'v:(XqGp2 #|x^C[׺k+Nw;钹<!f?tҤIKKKx^̘oHAmI)4kBV4yMkp\,syI{O\.Wbk;pMѬ0!s?^jժb(S#)cXL5hFDBhxQaĥr"8'W_ M-Tt2{8L)U9"j3 x(:cL7:Cqb X ֬YSjժ:::!cFwpe#'*Q߇BB7I1QD\ !Q IDAT\=I 缷jKlpJ}}K+.8\4$@ f0,RJ=1n!@7!)s@|>_AJ<_~).+ښ) KpԱ45H/%++kC=￿v[ !ys̐ !⎠8RQ}ٹYiv$ RcXLD"J0 uĩO% .!P-uvҽ^ouI$麮uuu@g{{{(~ĥSj3GG&SUS3&G[5%?{Np(wdӽ}cpHC2+z{Wvwy#;t]ojkf"O<4556558:0 >IMLP+q D9 pn:!p~ C6C60*@q%v 9)<sʛmc/'ee¢jnkV&:% TWW?F*\f̘Xeeӧeɒ%oy7u! 0̈QFѪ1q"X3 n0݌Uh!J {M8eddZ Ѝ f(;8(o4,v Gq7m(-bt}3o+[^{;H׺HX x:8gQKM򶷷z1D"?ݴi\1.''-[TF"PJ6QaN!ڢl2c8!<@H1֋& Oؽ{Q URUM+Q~ě2ẩ=p?WQ JQ8QO <)̛HP ib HմȎo6'EzB$}yͩ"77wfuul}?*˲1A)us Gcc IN24O0_yVMy`8 o(V ;zi M;?g}#p{/ k rp+Q6!$F)qƘ ^'|z*@~h!@XT@k]+撒qhtW_}k7pù]tW\q؊nQF]c=2v17o-psͨZGr]w^?$ MGV~6`z)z–s @V7='ImAb#Ua(1BHs *J) nsH鿜2;="9{uI.$;󖊊?O׷Huuu§~gҤIp̘C-^r&LpݺuxTTTfY iEv]=':`N8% ܊xG 81 p moͥSFD#AvG o^N(ء"a" R\~qiS"t+K6mz#\hmmmp8;tP .ȫwuuiǵjժ}o?`ӓO>??OxС:y<ܚؓlmՖ?o * \tJD 65gQq]'Kv!D Vr=E1= ,KU׬Y3j̙o.IOOϽ?+..8~Dgձ@x&MoĈi󈪪/Kwq~ii\SS綴cǎ??/ 80{Ƀkkkq(]2"Bg@qw9eRyssǜ?ڝ.^̦)S6k; u \R8:!D#pMZ_z2bh:KWggg?vQ_$閛nnh]]7g33Jgۿ-[~&OMXQQm'xp8!C_|^qeO:{ǫW?y\oJJE)++sUTTt_x^[g͚5q̘1۵kW ]w8oJɀRZzc)J!ԧOŇ/c}Nlݺ?c]9998?(>!) F`2۷!TAAAF~~~bqcڦMj:kiii8qPM;m-  طoozݏ='_}sȱX_}/7557oW\Leeg(jVR n`w#B˞BT^~!9%%媝;wNt:#TUI))'ۍ춚VWW7x׮]gggHOO' =zt{cccgkk묔<uu;UVhuvdᄏ+##{Ι3vYqaC !t]g)))Œ3>MIIAyyz?rܑ}l>^6S;^~jjjؓO>7"ph_-FXLC;pKnNⳍ}FgٷH{<A&I@ gϞM (̤%D[#F 455@wss>s.OMMeYؿѣGw:Xnݮoq^)??wyZZZԁ:ͬ#A$Ip:.ϟ_3cl-Тz-Z7xoc_1_( 4TJG36~p锲[䧩JSӝ [Y@y<Oqq1;rH.IY~;,4hĬqae_~?ի')#yhw͝;wc,ww@˅Νtƍ[u]7?Diի^{U[ls=:|Ammm^aԤn;x^G!A)3"fv?9q΃+<F)u}7cZh4ڠ3o:&:e_,[3gLUt:?]ƍ+&IDI(~敕eegg|ͺlQs 2gΜ?l޼jхRMMMۃ>#GoW/yR \={﫪^o .nٲ%m۶oVsApȲ,ހ+8Ã|7;(͒$UfQc8.ĝYo^ yioA[] d͚5X,n71A {nذ!y-5 ǐin;%k/¬ ߋ/7XAI7=QsD3(5Gѩ]|7>!##֥N3roFFFιkkp}2I2&]Y>Prܺjb/*u]7'---k׮]wmɓ'g466{ ]tEi< Yf-YxWU1V +WDQLg aC)\fz@ ՜Xz"A(iT{9iq[48d[?'B'vttd| [nQqq1 ͢(FC6k`8e9NaR1y_[ nB<"~(NgYfffZYY766>=(׿U%%%dCAh!ii'b81BHPP2!B)mB\xT Rش@ &D^|_dddpÇ-##Ɖ NA.TP"sB2G]c-esYvv6/s9眓SYYׯ?녅۷7b1.Cd}&"11+Hi4ǯt] x} @u]w!6 41۽{vY7%%xСƍgMpXݿNAR92駟~߼4)'ΑwTۀH |c`k۶myww>y䑻.K/IM PJ2Wɞ߃n(Yƺ04W9'J0'B.4}v1*//OmkkS6lذGDjSm):!l]׋W90nLNEhA]p}?2BBψ98yҥK_x߿iSO=ƍ>={_h6N,(];̷JTύ|8cϞ=_(--}>DLӴ(~ιs3ҍ̕^GhVUՏ8d&833۶mLCgN%I)9/朧 :z!uuuu0ӹ4 c$@]]]i]]SCܽUUUzI{6;;;2eȑpb~梔f22gJQ)!añuK5 |޽W)2ry[ZZ?C) u}؈# -pv.X|{xt]|7g G%f,iib|(B^g"ڪ-eee E9#ZWW~H?:euh#tc*Z3Ql&#33󎦦شiHo޼˗p8dT"] 6J:HiJA ޞSQQ1q,] 07nƘ1Q 0aBahټ2&ć{l l\pO>b:;;]]]Æ ^^^>)33Sg=u:VAb)whsGxuuu%%%7ڵkG]UZZ:wީ,)) w5jTyydYnΝm۶Ғ{#f fzc>hG߾}|嗏 4={hiiiwmڴLII~u+V>qƟ (TTUmN:QkA.]vժ(߿ҦM~L 8`ܸqw9rȑ#3 .|F1>G)pνbŊ[<ϢAFg"Mnj{Ś5L_vۈzg-Z^yy˗/?PRRoBG4MKKKܸq?|IZZڋjN]u]4mt]ߏLXN !۷oSO=5kҤI?tѢEJ~Wy睜6 IDATwܹPVV-)))80uԻnjs3!$ƌ?D"+ $ 1ՄI??3Nwg{Lc]@@饗*EEEkG}?x@JJ6_B֕'r\w߳+W<9p5j2Y/~g̘n~~MMMM|ժU/i7 Q= .lʵs"X|/`0kkkÇcCi7oVvikk~i;S#?p'e7L4=rh/eP@ }GTWW-\pٳ3O?uo*--M3fеk.Aqqׯq[}:*w~kG@H;̹u~5G xIF>#?XL2eА!Cٳ'#lذ/llnn\1cƔ?;@?e]QVnj}Bgf[޽{/6mgG170133fs5Mӫk׮G"9{U|y˖-ܬO6q}o+;;[ڱcǾk3g|]Qo.=? :ҳHARpzyu9p7+***׬Y󳚚ƍGW?~|n~~o߾'|pcǎUF)22(ǥ+tglх1J^q0#BW>&e˖U>N6s.䒬niT~~~Q(KNSk)lݺ?ڽ{7UUUq p?m%-Kq$;KW,Ei_^x%EEECo޶bŊ /pՊ(455չs΀(nxaŕgϾtZjKxeti/C?ڃvq}۷.D;v566}>ݩp]b޲EO'&,# sbR I|KȩE-_'K9Bu޷UuM;^pcxL<= ,̅˲l[u]/4MӸx♃\~}dfgg+`0{'&&|$89>핕'nƬW%2 A!& !sV8d-IRP$ d`)K$YY13 qqMbVVݻ>}P(LZZZcLeuJ+$mZB*rg2a 2vqBHs9g1%H:ԉ׽KG@x0=tPJ=?z-TUui6m|ҥ+**.u[CR tl8#XF%zثieY5 ca IDATxwՕ6{h4J(# ˮ]kwiql &gH&+!$HUuVM{4`穧u{ %& 轸 w"zPJR5k'ض$PZ &bm---[OuM #zW_M6Nf2IJ@kajjj61mڴ͛ٳ7Km`Xx /p͚5a !,H)&"BkMmP( @fO}ѷ}ُToo%'@xѢE\Kx<4emm-{qʱ=3?L$>prdF8^dPaXkM43 uaYVmmp}}}o,ܼS.?Znf /\Ҳ LHH&|Rcc#lۆv^}T*.Ų37)Am*&. ,lqF| !t}}W9'ϟɓi$uQGבGh4q#裏]`TVZkd2lݺmuRBaa$`?b J)ömyضX,c̙p ;餓^?Db'?d2L&4MqAa(v+1?l mۆ|>qP,avD!0 \O}}=ڐH$ ,l4xffT(d&mۘ6mSgu5'|3࣊} @wq7{W !x祔?̐R"byD$"+'>q /@; _pA'գ$Ւ=5l[|R34M$ ̝;"1 F1MRJX&L:Z[[)L"h---ڵk/+^蘂ؗ}dòq/:O$I`v,\-;A''YX fB2#8,z;B8H$I}}}P ̿꫟8 `_බ,)+$,K;0+%l3:0ʪٗV41sL$ ?/3ޏs(q'v%0"$Rj8>|_ʯtN bfZ 2MsRjێ;^ε +Z`OO:_Դq²,  v6 mem#8'̯95y>h4zիoG|cz/ 5kLZ?Z__Rb1Dss3Ϙ1mmm܌z(`%I 'm*1+`owGk65JjM{UCCCSGGǪL&C׎zٲe\s5j444ؑHlhhyi(`bBbXV R}5FUl}ЖWag{0Y5 L?\u֭[}衇Hgpq ~7f?~JJ91 Ú2e qૼ D |b|mpxQI]λ7XA秐#auE>ǔ)SN4 u%>&pwwwW_) a9scX,FZO8D4- ƜpTl~5UgO}0-?*~k0L)b'x"~뭷Q S̤{ɯ&@P_WޱcG)9{l># @l"3 umӱ4IaFa΅r4G3y셉d2a=drC[[3f3| ɩ~Zao O?s-2LN8| KO\>/OݮĀp^ ߞNbt8Rٶd21}?[;ATm.Fɓ'7NBMM f<ySª vr pU|ݝ_&i^㺮f,b(4l۶Nzٳgo{d>蠃{X,f.Xg͚EJ3@{ 2P(j_^|jl_0䛅J[=p@kڶm#JP( s1?}@ Pw嗯Fnnn'p$N!'P(t<(,aA0g#~0 ^!`2UP0zzz0a„;o]{ zΜ9ga(˲ĤIF˒]fJ&U;UsKD >JRayKٸP(ȪmW]uG #1cyap$xZZZ^ކ3N#J94;+,]Ntj|JaAݲ,Xh4xIc|%W S*E4e,A%FB%-|Ȳ,D"R)'pG=@4NlvHQy}!'A.T#*|atoT *)dw;mv }A8-2wwD4@&d2g۶96j'܁jIRnJ8TAJuV Ga ߻ 0Q !ز,={ nT "2R鞞"b5;C2*hL(Y8p% >i˫䄎೎ET|y;f @D$-[PJuJ&έRIR*I}L_^8^fժiJeVsT\ u}$DDBbf$ɉi=5B6nRJ uf9ˍya0TcPCc#>[~9 ;!Jj 0SXM=?AFW*Ͽ&pbd$0*ƚbׯ_nSSӼbmۖeJüaϻZ UG E<T\}ವJ>@t}0]$>F% 4&"5yמ<$"DQ6 XT~%543>+2+]͞Pwt5t.r^ e)X4&%b^`p,`n޼yWs12l4^0:5,aOJh_Xò`'V(}&%AoT&8'wA*WJRʈR*7sǪ2 @?looVkkd4l6YFyP ; GNK~:*wNQD89;XnX"Rʊ9pU0UL'g;XR3GPzSX,wؑ3g"bQΨLJ Om)XDUZp>>PMX,=s.eX B  n-L>@""۶.q'[ѕ_~?ⓠ6OA2sTsA=J#XF .0]]===?7:3DD3;(@im "Z|>?s/0aBq9eYP `'U;h~ Y Œ]Bi4 T6{\NO?o}(I}6W%ȝ4@ɤ֮];֭[{3vj 3?|PT*'T2i9l~\((jXz= T@@rnC'H]TI;5 P6X4yNWXѽbŊW H556666d2,+ -'NlhF0ژ0 vQ\/#8PɎæ%|xn|X,R&QRww?V۶PNݬ!& 9>Fq<1s;#J&߿ƍ}ũ_,Kz(e)I>󽶳8K1RJ~{zzVERn&%$F[}w[ ۽4{`1<-aBDmۘ>}BA1?YjG!1%|Ue$7Z۶MbC߃G,~yuooݺ ""fAg_5&t7?w9*tnDmf =ԋ< r& 8GK(l%ѷz/uuu ,8*+W;̃# -iպZAw7 Kh_Z/ L4{ဿ hѢ .|,u{B<zq`w'_D7ra]c x!2#uމba̰Q+HX 2qLj$^'S5z4g,AIC.C>|>oʠٹ|>Rqz'+Ks`樔2" ! ]!]TzzzK,Ys}=tgɕ(:`TԤ9} 4R 75Ȃy%?r4 f0xg8i0/^PwwOVpطba)}'VX2~[U8nƍ76a̬mv2pOOOoggggGG\.׋ ̜Ci ,Qio[nzްAEKi 2bP# b  3: 7뙾q=hH$\U6Q͛7/<9䐣k|* 4rtEc 2Lcu}}}ۈR(f$a˲(`FQ6z` <~wy? m%8͟luwwo^,/s9t5?}{LOs}SW3Fuִas>)2/tDLkeGNݯ W̘"/>ZܩY(av^{wY2k֬s̙?a„Ix\6X6 R6,wض===6lذbݺuo"M9s&M:45"؟;>hR^v%Ky敥PsR\zV;z-bԧ#{ ҄ǘkJH$vm!-}@CF2֪SliuyG̓K}  %3,"˟_|gN8qZKKKk"D"wFK{{]]];;;nGy(z֭kniɖImmo IDATmS[[['5555b14MCFqL&ݼyׯ1Di>EN@k84?$O\rpw8s(1;c'w+e>JM9O^mV- Mज़?~q?M aZYu~32xS r7}QIoy?t`z1hW0DmmmC}}}C2lL&H$R^ʚ]׵b.ɤ`*r]7u$YD%3H!D,%bX0JՑQJeDdPJ`xb3v tَ*#@{JN/9(iJiO55|e; 5;|qţ-fD1>]\U#q&f;wK{/1GmEx5x>dUTV񙿋 po jWXQeK fMqgE`IwA"@JF!6ac/aDC̈Nfӗ"-vf0ZK1ss b 0a*־uwt-똕(d޲jisH %z8Y ^NeP#ZvSj/n]ʽE@g|) :Z(چ (28΃n;@PB. }_8tu,BH"s&~\ta2ko_K\UE Ojk=^iopᯢ1şK=.h!K@pVC?j6 \/ 7?0D=ßKepY%$3ifs#757^ wpdL0"вUsa-s-͜1ԍ?<DF(%Z jG˅gThk6Z% ܂!$㖵b7KxfbW_Ɍaz/j5bAE.% \6?w:cXѬN<Ȓ2gڌ7̌fP (9g`*FwMNUF$m* HC[vK!#QU\fB̒d*C= Vǰj R'IC(F E/FЌ @.9{ aeO:ˤ qyC+M 0 ! }}X6;\Q9E iZwAr&>n#:H%]o_ 4Jt^A)&J1!yK"ao?% {񄣘iB>de`,̱'D"%e?2σ A fa@QN,ȥi~R#{6 C[@/@&9iW݈E(e5]åDžK5YȏlӲ̠ i } ` 8 ~ԁG@Aq mDNP}=8O֢^ djor/`f" }DpDJ)5QO{$bďqe_?``e⮇[kC1('13WͅZC'ZX4Lf5̐Bih|>Mř"`_2Z) i % -UhT W%"Б #`du$`\ 0}0D%.,B)y3D$R(iҨOɔ>" ɨFB&q1(pXFfp?kw/[xti0-oJJ1GBOCBA4dz_}u0 w0~enxe5K1z aj1dUY ZaFJ`w8mv!T׃+X8!bDV#6| A N@ wF|Uo>Hetle<6ih$e+ Q&ѻk~W`h !# x{^8B?vpSm1g;n}x) gۧOUۭD K{)?^OzэR')1뷯|n9P"F㽹+ _pz|f˄MML   e$ } _YP,53\q/\ɓ? ">P`YA(e1tB`ժ ]}nB#?'bX2~Vr;m\mǣ/b=XjDzp56gfDQ A0 *'~EM$)rOO&cw)xvaģfvz&|?ԕ٫:2}*`[:8)qKֵ6v|hO8>59٬#<&HLva xO>Ab)BP4I .nNe"2R eFsimS0E1k0eaփ-LqDZTo|%vS7sGwXHV\ݛ,8de.CjMV ߿calT#NgY"N=9=ZFOb_?io@#v Kj)L@3-!%'isKG$zҽ(eF%KFԚN*FpXLEe/J4b͸ӑ lzdỳϙs瞻i(Xfĉ9";w/%R&gC@ mw+.n,ؗRy1k>tkJZfCuJqˁųIHBͪ֋>"`2zm 6\Y|kn!FɜG;):82):TCSd󄓭y)vv>Xb~>bAHu/;aRԨa4+YeWo󺇞 }`7Kìt7uS9un1ݼt,"cZNP]Bg;ԍ`A4co~p?1`jfvH:Ȁ)͠!H1bsy\w# ED9a1@c*ÜNp+icM+2̔P?/c/ӏť *\_C@wFiߝ^R`ߝ[{),z:6{WB D۽z/Xon]FDxR O0 ;d@H{49gG~a6l's-d-4,5[{13ίt:[Q.zv?[ Ar[zXҵ@4{0Z;j|[ԽnoqL.-+SJ} ?$'y8=:eej4H3s?JkޫSz6_qʌ(g?#t [lq1Z?L .e؛ק5Js{nSq.p—&qI !zR؏ۊ62)u!i90&MQܨ`oP8a_sTpBuFiKkg|mL`f=_~R"Z6CJܹG;voگh^\ԷPD/JS-?no<ϗ5b[ Pq7J4Nefun-ngҷסCl'ƒ!C8+Q|C_{Sȁa)e]?@51M8{r8rdW}oO[êaXթ)~05u:GڙBG7e[5<^POɜEWO1K%4ZBDֻ]> Oopr=RʽgKp[ w?6&Qך|({|W1 id{:wa槹C D~$w =ŴC}Tڗ3Լ/sRʈ]kruR5˟$̢DʭMصQ(uڥ;@fysWJr!jC57VpOz]?gF_jZ7oǛhErfslؐD45G`XjE| %%:te;nEjxhu60"IZ 1)ٴb;sB *a.U7fՕ/" 'پB@iFHf9zt_qDE[M"Nf Yt]@2<4#|dC( 1BG^VX&2v+?>_ؽv&`6ڽyaءQdzLsMgHWuh<(2"̱)hS!3-q ][8aDžךt Z\V^fo}7ŋ9dFڅ?z֘PD.en|u3bPܷ_4K){R]´lCNL.(̤f f2@ŕL y !~xf) 1q0TNh>}֎h}&!+cڔl|c"{\55W@3fT^ W!DRN3MZ&y-dmZ'@)Rc7qk݋RS5Oc أRPJ<8SpQ;qLEIENDB`ukui-screensaver/src/assets/weather/35.png0000664000175000017500000003327015172041035017463 0ustar fengfengPNG  IHDR>a IDATx}]$q^9s?W{wErER #)cҢ-H$[$~!   =yH ~1`{e˲@e+ҎHJL2Hrww ֥>}z[v""!"a撈?&y^.KuTǎqԩϜ9s˙3g늢5RYpٺVD9SKKK'vzdϞ=?ܿ>! +~R`C ==z?ԩS?w7ESDZLAHpαPUU:(zEϭ|߾}_{x{oQPW)n+_?wcǎx<~]Zpn+kCDda) 31DVUUUQYT%%I7|A qU'3#Fk-qLas9RE(c P]T5EAySQREg 13آ(Le9%IM7x;o?>oW[Mcǎs.A%"✳Z44M9MSd*Qsn YQe2N)sr3;"r"b,Ʉ8xw|}{w.J \Ԓ~=z?QFF#˲fN&L&?gh67񚶫 뮻~]"Zjs6Iڵkٳ448}kkmUDY.kkk4NYD<σtz;?j@?qį.,,6 á-ш"D$ܤv ʩz#Gt:}ke]ז^:Y]]0 97#X|LD33E n}Qg=+!"4ܹsԺ.˲ I~cWvYzAZ {s_z=qu]I 7 wfL&d~ Z`cL]F|A[>%eI"BΞ=+ш"D$̲챏~zCzykKDɟ{ ~? ð:XZZ7 )k"Ah3jZ}am zd|w,K2̀l2P]6( pႬS]LDed2y~~~WDT}ݿr}`0Z'"v޽r3L-Wk `C57w^%tT[ k')MS꺦*V^/8;W>5KD#Gcȍ70ZM4L x鬟&`Rx!EQԍ)Rף$I(W)pRUUw޽r/~&WZ{GG3 Bk=t萬244QZ E܉^DA@DTm[CհtȑLӴg=t8peY,XpAt:0h>W 5iXD!z= "K-..RVDp뮻FWDY`"GKԧrν! 9eYRe%ǒe;tVU/| wR./|+q~YUUxu 7D4\Fɞ^{h>׏  (A `˲<.*OApY""<ϯ_ַJ}meUUΝ; ox>BM4͘u}`Ҵم;.PUDƘkVtuC54@(T,"Z[~xv]eiW LDO}}{4~onf ÐkZ@8JSh+Z:GkuT%EэA %i\5z=yꩧG?w]F}`(޵k_/--1Ç`0`hX`*Aph[S *ǔeY{4tܯ[S[(ϲ, D*o 0eY`O|.D~?ܹG~ٻw/aHD5DMb`v]G[mʲxLɄs]QN{~ M_{TMӮnGCV<4MgN8+!ҪIPE _ 6Vm-’&~+ VA. ^ר0&6ٳ-Y͹s瞡W. v8㜓(ÇͱvH M<&'0Hh⧐hj|vC:WuS~1hKǥ&;瘚{lO8q rKD_y<WDd'qӋ/KF^h Ck"ғؙd]/0"? qM[< n\AABH_4EQtM~kkk+pywa9G~߼gmĉ6ڤ̼ND&/m7ѝ`i Ws95E.I,"cSO=yzdrXCDnΝ޵k۳,ٳG$aF_`/׼x_ObC-k0m4yze.c֮Hsqm{]15a֊E!|_$x:~^l;offviC1&Pgȴ$jBkxD~Z˵ibtNÏk ~д4POh.JBsSA۹ẮkfO?zyaZ{xuu!ٽ{ I!jm4@WD^(Τ{h&{bfNk.x}A:}JdfaH;h+_Z1ImMyAyn@p"%IrQzKץ8(;t?2J^)ÇJ]CYXX`B@g%]\74h$A" " (OMV=!t׀[Ĺ$I:kC|s}?p=ʍ17V?%W .//EYZZ6b(fXW<hN '$fuNS14D/Ej; Nk``cvNkj]'^_ o]2sDZ]\\dA$JO٢Mm4(0y$=ڪh%i9Ƣ$@6PMp8LEopma.qرA./V/27ncz=t5&P  /4QUC^Aں!u[c|-GvOmH׿~&(NyFʢD,͠duu]-^R{10ys.`ѣo-رC$atTʆѓ gMڴ@B ~.żM'`0\<¨Řfrssm6WɈxee6"拐i.Dsu.L;4(xqqt/l'.Ldp8_y$p.4O>Vگ# $l\2#ZAJ3DAp]ג Gѷ^'"="(3(9Wu8): Mә2+L4VО5k`%OOC2Pz}7J.XX/=.m((hL PA q_TNU%aD䬵={nm𢳂> #"J(&>3'sjMᐒ$4MΊi0̄Bp_u Q>ihsgTN!`:݅J4(,ЏT}Kr9cYYYyhp'"V:"cV!q @ELܼ8Z:}hz }:^i[g_JYkk+&:JD %I2$\w>rUUś[ y> ڿ}"9uEu]/A2 Z+:M>O@&~YZhK~[G A_i!tm-4)oD280ScDQeYA"Z  %ju}" e'I"@o11Z0:{VW`b'}@=83P @'z4k@5EU6I~X =0 )I9z,"wرcemmDYYd澈sI$4ƈM;`rH־yo70i:/g\kd63wh)- ,$IT1c"`О{Բ~fZ"gZ<1ƀ؏_ӵ:l}5ӱ6d-,_k@<}^>lZ~"\ xLAB&""----r|QDE$Fj?DXD"2s?~Lgn7y@_]H&Ng&I'  >K׼C Juﵐ} ڏyѹ24M/roKk1Lq-?k q>F8礪*njYұ6Akïeq' uLtQkvQz #AS4'Ajk]g*57 =~18tJKKKf0 7jWt8އVrcCͪ`eh<ciK r"|yp&7w4Q:v5h7zUh䇒A )$ZǼvܹcǎ4.+9SH)yVBH!$"#"%yRuZ urůo'я5VNx7Z}@`2s=OVע>:$!= m86\3siqDDq]׹"KwWRcCN AF959fATDT; 3KQ SIBL[ R h˲)B`A->a#MuAݜoY?uI1 ? _M{^$ %I4̜Sc͉ŞI+a&Wez5Ӛ5\ӡvbI}q|mN:I C}Wɮ>K ÷H>`0OZ1tΩ@iJ`!><ιjI^TXQZOh;7 ̬?72 ñs.afaf oZӵ ~])+E1z bԣjy1}|=:$n(d4ݺ_뚃 j-Z/5Jq aC@ BM*km-,rg)U}/h[!Æ u MBZڇk>kD4C~E[@@ 1ۤDQE 0F,AZ+]dm~^Zdz& h4aaĘ 훱'm]bӺ@"r]>H8\/@<Ҫ>'>з^MsvwuK(i.$IRDQRă;u%k &4 SUU5RUթI\Ue@*.V5CaQc5С mBTU1|Yu@PVZ#fZ ]m}`CtjA `"(4M(0Lq p @MGED3s{Dt(#u]w!dO{YO_m~uӓoWV@cZ0/Z~Pk|C`h^\A̜(J @1OZourj$o ڮ"y3&AYgX=A4W\AN U>>.c)`˂.$C }M/,8|7}5p:B 1[aVAj:ZxdOPO6ʪLӪDGD*uNWBzR43ד mk)>ւzuf.HkZd4Ȥ i786 sIDAT4[ʉ1FD@h{H?"gfnՎMDZcʲJs̷IS<Mg]8ZXm˶AtqXmιE !ת-)d=&%\hZTiEQ89Su̅1f3"RH)h;7@a -YyK,;̛UU 1Z,@Whc2< ,f6>ᡮ~Z'|"=|7_A}7 +q$yoXk9 "c&̜) 4?Vvg n7" ".˲7Ls=c~gHF&y62qmUD4S}R}"Rz}]`0#$M7}S:RKG>DgL5(d:<ϟzEQu][k'\8*"*y@u ""Ƙ: z<kkk@Do߾qα1FQ;p-At tdP~)^18`eY(:Xի GhOM ҂8*c̩:Aڑl2.^x⊚z( 777o'4ND1IBY4Im6u)8L. QkL&&TƍO&di-־][ (c,kn \zyCan͙.y-@@D)ICtٳgq-|\!jL2*4 BZ 'Xki8v Za' mZAaqhg5|vQeYJ*gZSk1&ky[-"u m!V!"M~׻ޕsطo{9'i]m1t)ຮme`ޮeTE u]S{CG`q4&Wߍ!$ }qBmʣ0 8˲Z[DQ$QUƘJDJ15?A3/eH^>w={nllϟDQp뽇tٗ&XDԙ~OQu5Djw@!Orצ\Gim4["NDmazYp8i:6"SDp$JFSk-"{-3I5kF si_}&2p0Zpf"jBEQtQ-ÂBWehcp- Z1eq|6^a(*k&Y}V0sFt:=#ok-2ࢴo֏bgg DSO8I%ȧOucl:4 zYk4Eⷦ)M&TS뚝s'0ZhG "b[0@gJ^^M"jV kkkk_?q6 ?ۅ 5PY8@0Ngfֹ8W@>OЦa=ZsRUO&qQ?N8GIlvKD2f3Qc5c&u]h~+y{e]u 1Ym>  f=LD]@3xԦ_kB1>k ev9rktƓAA0fMczƘ-ј9]/^kkkgv}Ν;wu0L\ Ws- = H"X ھ_o<--'fzsK2q-4mϬh!Q:S \@UUf}}TQc& Q]Ht=M0 f Q 5 / VȔ"2&[Q٪Zi3~~:v[֡( L&i4u]w+"oA! (ft%4FY[wD8;sw$4ƌ$0Ƭa:3sc,OhM69n\@vI,ۨzt:u^ۉb}v}\fzFxܙtn rE4?o.Y`Ѡ,[ EQǏ}eLjhZ$ qoшq̴ּ~=܏wqxyyy'91HBDnqcW G D?XyYdBL&{:_>oڱJ[>SO>}!"czsٕ Al`{Q }SNEQLDeN2Af|#3wu^20aD7n:lȡ,q^nzI NMe!`e|ֽt:d"u]'NwWeYneVUթ N:W RS&|yWg.\ܹsN>}ѣGשasۮ]Ko- 9ygzK?Pe&bUJWD",CI{At+Zs5G1v 3*2脟ecf{ȑ{omZUU犢0NG$x^v% .WQ, ޏ{]YlS#u]3G4[!SB+K׾+)`?o4 (" 3i^h!HL  <l,6Dۻj;wLbIt`0vE =2`u׫hZWLSN2N( L&( C=t.P+|fh3ҕQ}kދ3<8p&cQ[qdc # -_`! ,Î#. -Q+-G f'd2t*ys{/ wuy䑯sSED}M2}ݮ4 3gΜӧOڿ~?v͎ئ"Bxރm@D C/3GQDItnBgt8Zx<һ^G=O~GىuDD }=fI}ݮ4fAqz|iii]v]DĄa(`CL.ЋFR`}X}&BpD]CD !ھ2J "{wn4n5?G/P#)%\ MR<'?w{ EQᢴaB@POD]↙>=˲i[ _oǢ^w0!iIe gΜ~~_vܔݏ1_jhvB (8yǏ?6w////5u+xԾB.+{}^֋53$߾wDd2_~vG9FkͿl^x+۰HF=hC)*fn֟}]v pҚnJ%.@8h"ak u-m03=cg/裏~mX-m|J h@Jo{~[o}BN lizQ~D  E__–I6L>|fӮpE}^+5 HZa25=Z ' {7/--m`q~  +yP*,mJ9:gYo|yQȔ' Ƙ ܘQAbcL9HJI$ͭNU;o喷tMoٻwnuNG #4+c錠*Bl҆F٘ꩧzw,ujXDrj4{L]cZ;jjsޥk  WUR#xaH#zlfYc7|ͷ޸<Щ[kC8&eKnFEQb:Qu<}G}}{<ǨdMnh·ADAlUU}UbkmjU X 6DDQ-߿quu~o}/<# ~VHF@='|O<'~\ڽ¾Q{lR[m^?+/ծ&4$"5,$3<($" @ ///ؽ{u{ٿs+ z= ~>QL>Μ9sϝyg=y󴽱vM=d ~B[̼ծ&Z"kmb 7lcj@۾4&I`!IA'IZkCk״r:'d<FdҒOjjVP#x-1Ƙ*KD[-RIi yBm59eL61[-5)k7Z-`'1f1f[v5 摵6sN  efC-3MK6Wm |Sc̘W~=ڞ*>pQc. ڶp' jɌ1"wzS$ 'ZkcjIS ´ͽ@̹1 .\ݒn? 1&2ye "4}E`9W:f6bx@~ @t1@_ZPSsӋvzÅKys&MxT Uf<_{S#kZ֮kZ֮kZ֮}mdIENDB`ukui-screensaver/src/assets/weather/49.png0000664000175000017500000005001015172041035017457 0ustar fengfengPNG  IHDR>a IDATxyWu/[::HdIX'#'@y $/k]!${3 71sB3ƃ !YeK,Yu>Ǫ{?Wiwlc^rN]Zƽݿ6QG:Ϯ?=ztرckͭj++ʘC]Z\.w|ʕ ?W0jkC zwuvСC8qA \3'1`fhJ(˝755ݲe˃ׯ?\cQi߱cǹwuO?oT*is#<8"AD=: `3܋1Q! Ch~uxx͛7t%?۶m{ۏ#3uO?ws<577w 3S.C68Zkǁ8<}r]7!\?"nۈZ'|fRJiUh6 `ʕ߻ mo{ۍ?*$o;޽gϞa8fff""y,r~v0::J)6Ƹ|YgaddEz:~ib ،6@b{פsfdčtĞ{챷ooy1=1?_nqvZ>3)P^]ߏi/fKHH}7zE-&q' FQc \ץl6KZk1l6k9rfkN^TQ2 {S8+_uU7aR)Z{M6U8Ih9la9%6ݗmqc񻮛Hi= ߌ1M!"qJkMB!/C}/QoBl8wxuX1^X:GFFȶD+\Z!lBR 6m`k^ 8=u]r]6mU(Fqޥ^z"QRpر-[fffV3lF)qKR;W 'owAB!""T*MP(Ȗ.k[61iw϶HE"[ڣ4s(:1fV+AV|\Vr}sWE?l#Y۷ݻ7MW)@D_ऄd`1QL&s(f"ba%-Ml~6H6f${\v>I~Ad%@Dr Șc4Ma$ FAJ)VJަM>]$W~'?ZIxuy>|߇8$:VkQ%~rEJk8v hrrSSSpZDFQ@ǁ^H0̌ n@iކliDm8nEI5&A)l6n `̌w9g7B? H!"D&R@ԝ)10 V^s! @P͛1>>HNJ|@ص!oZ*H{v~&HdR%{y|26G6M'vnyqq04qE޲ek?܋6~=z>/9rʱ1AZv-&''Q*Ȟ|я6tZ-,..bnnJV zabݺu83ɒfr:L vlqꦘ 61<L&a@A3m?(8j6ڙ>q ?O0K(駟vWo?orr2|xwǙRƍ.B.v}J<==2)iѣGQViA&u],..&uZtFVEz"}ORAV;EƠr/cO<\A/QkMS//_9~xCſ Z~ۼy3_pk_xDm#KIudPհavvӘ,jbeY7T+F^ tɘ..nl<@q6Q C3"DRR=r7n۶=ϣv}n|./,,`ii Ƙ@"X,bXbEO Fa0 h4Jz0V±c@DĊ+ۆI HEqB=a3Bwlbr 11*c=11o Jɷ-y^&њ5k~zcܛTIbt/M|R=99͛7cxx\.\.'_zEdY$xcK -]Ah Bm_u]a<K*E`1j|||jϞ=p:" .ઍ7l&bٲe |ߧ>>+6c$AD: ,6n܈5k$4CچT-͢\.X,&.vD\EPH!8NbkJMGt͠*Kl"s"#y{>m~Ǯ*f6rylZA2@^zGFFFd2իW'_Z i70tˣK@Bccc=Rd.I:Z*~ҧannZkJ~P"ж - AVJQ.#q)JK)) @M###w15223p;et`C%8bӦM(I'EhQҩ\[jq u]7??5{r@?WJ%o "y;Bix$vTVf{ֳyN|_n#xbb\.WE+d(v[%fI,|qn:J)ó"vyF S6hroaaJ)hu]J%8%HoF#2lٶ (Ja}G.Kr6Smcql;w_c 000.%is=iW'Xv~~||81D"PL_z{tJqbd(]\FF.K R.bϳI\"-$6sILBn' .H ifǓ1׭[;w~}~(tOhۼo>ؖ2`9∴-.9<$!+m%_oCbulIf%Mk1c8qp]'NH+ s]|>AtPف$aaHn۶3u]^*., #/ . J)vDrd`vqd^BZZ0CCCd2I@Gb v=tX7Z\\DR00L\R}032L*1}K*s!>0S-0jۗv_~kGR/ PL.[}eƍ:˹Br$.-vvm8 vqJ$/m #fZVBty3LigiIOVyLm I(i :{됊 >_P̅^kʕ+'l6K\.4 h A 䯝 =OM F[MEQRgH"h)]$Dȉa& ҿ}j1 ˿w=99f2Uzm޼֔e(Ձm祉Av:bYni^l 0??FrYȶ"ݒpfw5wPlaήHQ֚FEMy~^෿I<9Ԗ-[XJgA:\{z=q$(b=y prYGWtHy@R<*()yK3Q{ Z dz'?!LU* `޽1s N.֮]lٲ1OSV]דB'LY~yB>G&߶:?1%rJ i`1$شj%ޅ̜ qX}jsp&좋.)tnʎpr7(rD6Y\S}EEbUM4 <3իy)FBIHGQ Pj')vP8dZߗg2^z5 IzhZXZZb#rıC k3yZ-TU۷k׮E.;]l I #HD'c$v:q]08\.7D.O3}bժU?qT'@yp!j@y97v?d,'#~nvZ~MKjR+._ZŁpg&^r?4KRH5Nv )!"ʣC|[B0躮Pd.M4zb,'~7` @?Kۏi¦گ?ۅ^$tqqĆ J?v}HN K!lS2]u9\.7.u!=lbjdRODJ¦9_s|!~o&cCr%pD+gub bS=)9"C9f *Ö~hя=F(.54A -ҒF%B d$ ų,}ٞg9GZEVK;l6х.].9sl{oRJQa٬𘹇t_pj*,=&{"$kN7f*-~fD_O7ӵ~s{ HR`ʕ=*n?[* /I+A)r|<R M"&=A+v]bف$!,cH6ƨFQѹzd3Fwvv֚PvMl6ގC'{_ԧL~t:HBc43C~ْlo%'gA;*(ܯD;u8n=s @O(ϻ,O2vA=$ ,viO2Wb/ =A|F#mrݯߴ^tJr'yvN x*ae63tnaT tѣQZFRuj՚%{Igۙ~E29if6~ރsI|?c}N66$j!j;`;$cB愈(n7==}'[60 >zW]΋lOpzMrPoSZelL/a9&H/ K3B?GI3U밥5N6D :B{ u]ߵXwW1RAa@'ѣǺuZ;R\.+J39l9W"]0IlU emصǗGZͤs<$_wgLa+r[ˤ~KCяtx> IDATE~߹˩D9DmbR1,hϋ̫Rfw盃 `"Ra|i~n$pE!VcÇkRծ F c&B믿9rū_׎d(cõ-Y2OY='|_>&}:/e[v&x~lf(J@a^;VbL'(ޟ633ݻAu"Z&nccL%|'Bw}ݾ{]|+/9+ u>-buÎJ)Juv5FL`vG];/r:G =،VjA`hhf)edrDZj'|xBfn `A)fNBDSJ傠8p;w|СCǫjEUNVQQ8^-wghDٻJ#T?I؇}mNvöe740'Tj%[(jMX@V޽{IDRj]z hwQ%OĊ[hVU߽{Cw~@T* JRX,;cVXꬳ: d2NN @{wiuB|@A$f=v@C~4ʲV'N  '=Y "#̷k8F"c ;Q!ڰHWkR1}0SOzziCDٮěZVj8f* `hhEaf#T>픬-cSX@[ZҒ`6؆gZ;~8000%#׊d-`ϙ]s(^VEnRJ8pwvcBtWI4t& 6w'>+pWNٛ' ErdR9cL0===qsMgKBNi4tKb3Y|aGv CZ&/wP&UZlZkuM7]7;;Kcp +7_~Na{V.>z7n=(X),X3jsk֬٢rМ$3 4>퀓rj#Gvp+lk^cffGA.`B!!t >a2:p[nY=*~rchD[_Z;HpB4*c(+Bpΐ2hԮ⊟NDS&a@jzU&^$R&+qeN[ZeQR~$سgjZ"X,&ɵtAEŤWo/-}Q[R!ęhlHh0sKֺi`k>{k.1jQOF+gE k ڣȼ @W|":5f(ĉϜ{ʭV>r]a`Qi)bKKKIlV\.Yca3fƁy  "fffzl ۀxu)ٖ# J0000y׭1FUDvHE huz艧zA"2 32՟6~/ԗcnijGfue^.)0Uo>0(0338x;?gu_|bbb*+c:[ww'[ .e\ qeiBFCCC(~v2?Ƴ>fLvQu + ;+0 Q(zDP.Q*Vy^ ʹqsn1jZx7@ MTSkώ}.. Z31{9ОV^1QEK;K >GU7pW6>yOVcǎر+VjժSSSkFGGGb>:]HB.vFU՞&RR$].Q. +0Lh44ku[48J%RBle)yDE}udR hDwSl6<[om~~i" l;{`5P}Ww?{@w*]sYk^ ߹^sa3Dt2RLJD4R(FJ ^^,ݚveauE]ׅ"CzrXbIEvܐ4Q%kZ!|l[* hhNj֮BB|RA6T幤F>6 jRyGۿO14qX!C?+w+{8Nc솊(gw.]dL;|sx c0 {]gfnJ^o,,,͛7R5` el;"9D"ht"8.".TpkE9]bfK  , x&8H0ƻn_QJy_ 55cL@P‡ .@Qgrۀ!q #': bFP|Bg W/יɎy=x@` PP9<"ʆa8}ĉ7bGQ7݉#Z!:U19og" OPۚ(@)a Poc /bP˶> LZŸ[P,L3,OnyEXLBfŒb S(n9 C nWz}R%5uqpw e-i*>i=$j%ib=KdjjKL>p aaLYk@T ni̞Z2gPe9d2.@&qɖ#xi|BE. Jh:VӠ2!aD05ډWl>ވ\L8ڀHXt8y)IBʹ_:YQA'{K}}߽n'WJ1f@52 i;ɟ|0Ic~ts`0 PO +֟QMkw%h~qJ@jE'xZ1ѿlgJ.䲌b#7π+ fq0GFZ&ҘԔn{'H}.NW80 jl6n߾۷HDdYBGǩuӾLEzgL/3PÃX8+՛@Ql89Vރw?&f Wm0h: 6#~9ҌFx[u434!iD6WF6ӂvbʾbfYaAŖ{K?3/*P0h?x\Cqhq 8:fd2Yd3;x5.B+.C *  4o;Ĉ RqL[/~?F&glzy3ݪr t :Z)4Z !*_Wn71;?ﴠ~l&Tg{\#׽|LgN3ʧACE[@` }2W]}t2\ V…i?&dar7õZxܹgpoX":w[y2r>]ïc(|P"&0*:~ _O(;8o{x@A،)HDTeF%/ 8LN)ɤ &Xf<6t1B!l"oW0`^y1 H|XNg_vઃp P,<arl=`fQ]R#xNC;shG섏 7\y]s55.+*J1洆\=w#V芡$L``ʂz3v֚萙6^_:mizΜ唆_X0jjXM!2 4nնg=28^sg\'}(_š;HKx D |ZN `(r0_M98dT]BM; A0ζ^D%kGYTnZjll6u]>n71+,Q='$ i gbX-Q^@ob'68<n~76㌒V5dۯ".SLChBF\ysh C9=uH0DG6C6y_W*2-~'PăO P(02y`5sػ*Mc0_yYL) (sB,sf[)3Z@ Gg<,+Yr()`R l4b`j |TkO0qGM5}e(\ `NQxWѵX<( BtI6BVEݚ~MDNѵ^{ӽΟ]#9tA`4@dA"piu]ҊLvbg]gn=O9^xA\'DWʁ|;Fs{&1,:dm] $-'@Sڧ; ߹g O*#,:RZuM4t"|h ]D=}R"A:thꫯ{^7bIɟEGu ޷Mc Xe@J`EV' f4,0n}{uoraww_ЏI`-kq!( n!Pl0X9@c_}kǂ!wgf |l1: ]pÈ"ύ3PNt?b(Ϲ{gUm('wҗ'*{E18`׷(wBy'C΢$(\qLqsn ð͝&~}RϧP&Hv9?ϼ#yf4W>7 U**&M7^Uk4n}]wW|mdx62̭{ExEr.\6V.ACx3?O;pmo{^K^wTQxҸ;Vߛc;3;nE< [#3.FbI,APJ%7#y7A^O HDk/ CZ"r\u*JqwUTE'!Lgg ؾzK]x `Y!Բ~]OqIM2RghYw{O47pjO'򺻔 (L_;'໋[R},=gH9nI\VLcL>}d2N4M}gݻo׮]?~ OD>:)pɄ_"R iw:UŸLma7/nkP`F^qˁ}ۿ8zKppΦWФ |,:t]o[ɜuTTE/reǎS=nQ g Yt,ʊ?q6<7zHgxP]w_`E&6"Xw3'%A3y|#?̊W._NJ;®O}w]CTxqظ1jpqS%]E~coW+7T b&:N8V UժjuqaaaR,4NO\T#fnZ.nBit_2O4(mb4Zs!7"2E%t݁0mxeE[;wGaFJك-N`l@av "eZy/V<ּƟh9@8?t B_ݭ~RT'HafGXN^X)F6HJ[ 'u(o51m܃ApWIDATn6/{o(x7zT ](44oIr]&qڗ1nUvu'9#ܾCsqֆ%"?B'+;ŷ !t&?͹vvqy":({/kzV*WkLA8@ #J8GgyтQ#K2-f^bt,t l!qR3Z녮<<ȅjq;63NHL\[7+0r`LLt(zrDZ܃BcXB3wa nWcC#r#ĎPR!10O4W_ZqN9fZGgB T tm߽-#+s9vrAqqǜx GD`ffL1:H)/G@D-TKk6No pdǟ+o|{׌eh\r~}k FKZ=B_z(Fuh)f+i _{ߍeWhaUA R`)9j:﷟o}_ZEݣc"܀uP^{k §1p?~0B"4!6TJDhfF[_=k5OzTkv"z֗yݦϗz&nŮ:,_"#k}U1 -1&3Ł0pa~YqTu8ak7<1GQ]k=rMw#8ేÏe?yq \”;X`ʟ욽uöO]8MKv K(2o{Uuwfy䡄U(Wu|MjmkmvJuԱjZZkU|RD" <ݼ6d{=􏻫7!J|~f2=^s\3L4l:B"TE{oU?Ό4d kg -N5Hj-)eM\WǤٰg"1iNu55,%1 ",X_*ىߘ5>6H&JӀ0^8P3;a?\Jt#|)ۯ TTg +Y&&~8&43:ky䛙1FJL0m\thiVL\be;U1Z8NPp '=c $< ӭԭiO0RRUbeƻu~UmZ{L 񪧖7 ѩD|'Gν/;v'{ZROjQDy&Vdց ww< _*̉qֈn|CIX.xOw=L G*E)Մ}W9-aIyZttgS;Q "Я_ޛ*}hθ"0LC_x}ꔲ+].Q 3 d_F/\P [42Zh5F{RژN#"љ1|ܛ햍^A~8(\ގg^]٩`VbJJFn6ӒU/Iz6&hJJ4T޷@>)e Mn)#c5m<-+NOc37J)}zs@P_S8)xNv`'^gSTҐ-$EzNSHV&!RK,*񅭛>1WiɲYp|9o:(2kxⲭE`$*.fnTJ5bxu3"0>ߓG}kuτe 1Ts!1ʙ Nkqwtܔ|iC ʎln!@q+73GE/o@[F3{!mcW@ϛ #ǹF@m+dm%z;H7%j{ɹ3tdHdX݋n"H[>Fi֣7ܨOՔ|ts@/5;KwBmb)bWԳgF{wT5=nfaZL$f.Q(k^\}L-Zv8eTcډo^3ڐ4h˥+ŧ(P mB/g9d9P0u(RcT+kKVTnYBDɋj;rGgw,$Ku  F4JZ';B⼉d&~ A_s55aߚ,D/kҼ*&F}>p*SMs[W7֨mH)1&fҸ5kWZ5 ] wMe֝< D m^WW(u_(7ݦ{=ha74$m{2k#̞}w hRb?B{(2a {)VwL:~v)'b\.MOOX3%+@8G{ Y~oֈ0QR N'\̨"jVZQ׮۞?`M .^u E+֛%4:kv#3c$@0!YZSƂqOOشXu]rki'uwo׷~YoƵ"y8=bU}ɆOPXD,_y]jdfYSIheckj|rG0)R/CcDؐ A~yW|_0uΜY8z-W,UdT61 ֢ )ϝ=ٖc&HWH)iJ; $iGi< PR K)W\{ iwCYm>:>:6?020$c%JkeBHkjZ!m=W_QoY Q޶*{ͭ`1s?^ Bc^!mDc9#,-%@ރϟ.Vt0ʶ:kW X9Mu2"@DqWzrYz%c2Z+K9W;{#nѬ@|Y~5M޹s|9KўXy71FJdzzhE7nnrye[-]vǓDIYw`kЩ nVR [. Cz?`o|]t3,3۲qqǾRRa$ZKi0tn-`pӨ2 _[w'+i{Ϸ+̑f2R1kRBa:FDG9!Fz  2dm~? 1[HBSJ;([|-/2xE[wJgʹIENDB`ukui-screensaver/src/assets/weather/62.png0000664000175000017500000005216615172041035017470 0ustar fengfengPNG  IHDR>a IDATxy%U.s"ƽys2kbFfAfjETDpBG[mOVvnmgyH)L" EQSAU5eM9gޛ7s#"nFF̬Z7'D$0RPvdžB@p3G0i) $6+ɥi͆!`Zbnϔih!}"@0$6fuh#`qQfM:~,f',/@u &jĈGЩEE t|JдB ܆\8,˅'%b!Il[9H*2]sUZfavɳ$oAֈ{qD}l70s찀@YX顒/$1 II[ZVc>NT`%-9& 4ADRBkA`7>k"͒UR!HPaW7 Bk@:=}(5hLa>xsi趌DŽ׽&B,4sȀAY b@M &i7R ŝ)X +pr:GD,(@ H0E=9 `1 f*$ }A9\׺\,xM f)5lG璢Y984~" Y(,QVPL2@@Jb(.jc*لňĊL#M<Π22 IR`C&BdPR2ƃ փkM QaHb8Eu{P6\3Tr("p f&f*jx2!iA:giD 4TJkYi Fb#5P!S3s !*s ABD2(`A!3B )΁f C Ќ0|!#^aYsXJ  h02b5e Z `H",iEÃ.yURQ2B,@}C'!P1aj5R +XFY֮]Â@ VPœeNz'L(A$83B\W̕vz(ji) ]4i[3kfJH)f Zۊ Qg  `H2$hqD@C0 -B׎>ԗ+VRL>oBӲ\hCxD`vo:H{%op}ޠ! b#qMfFCn.B)̀0U^Pi? eas[ȶyg.D=C3 J.}&O)Va_P fhPKcª7<78H2*-E 9y̦}2ّĂIH3xQߥZ1b"Ŝã`&( @l|$@jzd,:܆+e2qBc+"003̜[! .OS'@c.%7V V KБ1 fhE?'J]ysgfM)vO۪.f -h) yPD$C03\?m9{]υEm*]ןſl|-u S_ C,$A1^e+XGhB 9bqG@`hpJ3P#TJ3@ǙvRJL9&!X{ʜ*Ʌݟ-;o 9#fuZio3CT7_6w'!4?Yw޹3'o9STM+yˏܦE gXƙW Vi[?-ߚ9[=F04 "u{e4#4"yǸbID۸&G36A Z3!nW9 k".{rTRr$jۈDNdh-%[2j0*M g73w޶!wյǿ<0wryh^tX";gx?>B7tid +^r?,:g#ARLD &f&Z#Al5O#DfQlx#ߝ@`Y)@ifHDW:K7\xyǢ֗" A0E Ȋ1XDq\: ut1G%` h֓("ckpQ@Qp\(/ e(# Pᆏ0#=`Mݸwf|5ر]S Po!TKAHQ aF Mnz.?_:{ dH-xąmB$so=0l?m\Y!L :V8f_֊4 GLJHiQO_jx֑;ÖՍ33Xѽb ޘToO^*@NRD[ ) iDĤ IC0aqfbz|= /GRg1v@i|;^aјc)9iQ˦eͻ᠎pYk0 JpTYeydCVs T}+wCգaNN QБn%Ę)"O-G`R~ĩ: J1kYP1+(%u&&sTBBD y%]a`(MO26os14diX3 @bSX4Y ΙbyW@KQ$yZ1ErX"o#c~n˜ h>00f iVq'I4<"$ E !Nn5̤cK3^ #Fq'$X! '|z 6`ZZm46 !H0XG&d;zH@HB§xn:?aխoC] xǫ@y\X@B{*58c fKu`8-| HH b {J(UTeӆ- $)tq bߟ- `JtI*1 K s!\ oua!w}7 َr)@UmO9LAp| W~u0@`2D1FI]ԄV`x`F{gNv/D q֓0.q5@M4 @mIU&yA?N LWMM" AJ#gDY#,\2'$aF|'TPJ[ſ윁Y[ '#ۜbʤbtf Н"A\}ZGHغƀ 1Oaؖɓ 1>o)U UU.>2%I1͌0#$Hd42-NK[<'aւDN8J% NPAdmb%Hd aXjd¢4*0ИT23O.+@-z SarhPJ\ C._#X&4&FYm[iKÄe ̚TV9Uiܿ{}_ a $ g,@U0z(M Sh|I1PJEE0ō0*訓LUl"3U/@1!JmT̋ΘΘv*lj9w qf-`@ioE#Y#tJc!5#- @HH5YV.,8ټZj <z J EVT>& E( I NM@]v΀eJRNC&i\ 8(Q-h*Է`5_)EΣ-0}w? LH@(J ?RV٤hNƒ>H ) RΪ) )! 0HҐlUbCiPiᇚ}ODlna-A%Ux@ 3`h ^dn3@Eׂ7@YNH+xۻ֟<6t7zhLf8`͆n6 iE`Q:F?(1+ѱJ%c;1}Z=B!@wu"@IQ54d(Ei)ο퓍xf+GQ@/vI 3 C\ŨpD@ȄJS7wm/ٓ%S 9v͈Ƴu*fF\kĵ< ?xu`ƙ#-@Ek; 0OB3a$k񬕘%bq hb8 >ۉSQ7fIhKHi@j&mh*$eX+^mXc mi:vPc20/>AcXifBi8ĹԙMWdJx &"!SEEMbFsxT1a }\[ `pUu,$$UguFf<lJPHYpx4Fio\@!#EBJ֐ iF(iIN_mJ&fQ< U#lQ'z5kSć- ۿ_N1#P1KSҠ.M2oZ@OE<WRD cys:_A %LyH0,!/r4+ %Sa_4m)E!"  @P g xQ1idaPkdEq0XC0AXb3©fTsu"t4,L SKC&Ă8f%"VX8U&*Mɾ%W0f(gb) dIH(;K=nKܳQ†L򫆂xÊe_cQ<(89S( }{o6K"To9~"~Yǝyl`^ZE~qoz!,tW)I] $S9%tdZUMK>#ŠF3e~ E@/2uڮu-EN )"/sٗ€VZA~  <ӬʹuQLM1 !#@+nDZi0}eY9,9ۦvr6@jG|6 #!⑉<$92h .Rռ$ܒ5,GXd2:OXBBHAq]R($I?'W+h}ҥZsESSSDR>8Ir͆Cb1^i59ݞZ֮Ȃ k[.9AJܱc:::nG?#/JR@_WW7k}tʔ)uB'MD , 0`ƨNO:* R Bq?]'֞$K':ݾZ׺.[GcY.,HAđˮqA={$}s%tj.YCy{~QWW644s̙3AD0Mfyhx濖ՐJi0f5{aκ֧gJ@)Uyp]+ e}?عseYoq`If͚unX|>b[[[ɲjgMn3<σaCDDTՒZ9qinX9/ݶx4Ld4T*wꫯ~[oyG,ȵ-1M,|mk}f ˗&ڑ"Zv 4ֽkYέe@s<@`)WKBB9o޼pZjg}38BN0lnkiiH|,˪_A!j=xO| VQ?^|jZ=],)FD, G%xcިFhE2Ӓ륔0MeBXfU_-ӘBZ!ۦ#jizZ'R*I>#SP຺:.X~gLtDD ȥMxrNc!$afzk Hڛ'}}ZصΫv~Y0dUiض ۶P(ibڴiX,~nhD@a1ED ) .kbZQB{ IDAT1ϚrKQ#R eYyɒ%K,:)ݱ0ރ'13'f>`Y`ԲَȚZbZxiӘԕ0{H}HGZ|d,7 ֽ#adYI)Ucc#r)&!ۑ:9Fwc5ApGUY Enj!֓Ǫk<]oۜ-c] ,Ku~&GBrX`iuT/@DpcǎP(R=#T5ђjx#fZ_7d;:+]7[Oj OuZ.3dϊp,---sv„D@\Is޽{˭usBP_5;~:kMg5?=|˴'&Dz#@} gX|(]oص,Tڒh>bA)54|x3'pٮj qy49-c=X DbZ N8|-a$L'-A @͑tǺ_VsYl}Y9`mffض3MN)5h"eBf>[z_/sNJiRR}}}57GO iAsi8^c!oM?S-ג~@-HD km#}M-K&"8y(cW0r2a߾}/?3ַ_.i2 T]A$eMjR&"{i_kPqH6H kE Z2zOHM?Gv_c}0ffk8 @\.ǛX,"TNҬYP*""4G æ&k ?ͲӾ= 2G)4H"CV Y~ +ج09mjl}: Yc(0ra q1CJZ'3 '|"^U"Q51m?,KD1QY=ovlz=RW6sYˍZڞJqrG{'?ٳgBhc@Š`wޝM]h RAd/ԊՓNNDH h\{ZhiiL4jWδfAp$LR:eqO]]]b?T*Dph&.ZQCkӵu]](D>g۶aY!.!`Y?Q' Z<@MRޟ/Fiԓ$Y%۵PϺXJs$JZ!6lذ{+<㦃'f~]]]]W_}[f̘a+ l&۶9x! ejiLZk"z҂OHB5cZf@kJ>Au'x @$#4 !lMj ID_}a:9ˑR. yMYyx?Wv]5/_pD.p@BV1 BADyq6l/sppP`Y%x x2Y;îyaJ)Jtּ,j &Ydޏ 7 |iޗXtqT*8{m]]]/" ᇉa -EǿS{;= TDdY*z6o޼~կlܸsϞ=kA`i) CRJdq]\e?(C40hx9/QGZJOdD kk%x?}teT*T*3vۋO<>]0,`̙‡;+ؙ1C|xg!ob׎Uo:x[*c#" ;e˖l4ͺƆBPW,iDDgyͫwG۶-tfYwIZ!V"%~$Pˏ{ڬKKr-e}}R)`_Q YDko,3+XuLG>䙳Yx \"1#YC((((? KmF p ZksΌ^HLi0$pڙ5Yԓ -uy|sreIf[È!|#ol8r=SZk-Xk=QoܙAȜWL*;0{- _)cfR 7o~:s]T*B;j]v?=—iB,'qN$,*4j>3Gi3gA+s Iqw߽;M=q׸ R*x=n8 9Trh IƵBD33^ f.Y X^WWK(Jk-8z)㎥MӬ60듓sړ MH$Xu4m\jkE` @AP~~ǻOJ)Jbx `t&~`d,ў'_#$ `"i!Rb(K_s 4r] Іaȧ~;ԩ.]:Μ9mmmzrJ!u'|UV<+0/^&6/y",M7d;~&agoXAurWX'|iӦ͚7oqs̙>!o۶$B6F___y޽wرsΝ8p3H ===+Wܹr&O1k֬93gΜ1rar}:02W<wٹaŊ9۶[[ZE۶iRJҞ9 JqIqGGT uյks=' BP(-˲-˒ajr\vg( XB[kqaoƈLOM@di?HKv%akx``?HχG@!'>|NKpˎ5g={P?@Bh|F <'b;r8AX=z&8-Mbҍ9{\8ej$mIr*vt&hO'rD:犷odsϠxgKT$U?hSzSsIϮ֡Ԟ4c$,@h.nR]CF>/ %ffee $BBgdi8ѓ<9a .釮u8PB̜ݦzD}>Rطw PH&-(MYN=~Ug Ҕf[)Һ^"̣AVC 0ϻt%[Ƽoj'H1X&5w?:Sÿ{bkZŢ!L36Z얗 qQ9\R !׃v74w]w\iYPoQCcS+3'禗ZSuvNsGzL:4mKrhhl `Ǻvy"Oz8d!m Diǘ4\C@/;S oY拗'O9x1?&O# ǦWǕ GOLCbuũgMc^J!8xUvѴ\v%W_={~޷_Js>'`TB |!Z S钿}gڻ_PTP/i? fb}-OzM#o)Se xhpk\~'z_䮃K9W^;oE ͜-<~Ҭ_E_?a)۷絻jGave[3mR:_(M=]g^xRS9Cah[ !P.Y0= )kBpv!8رũsl`ullveQBoT|d/DdXw>"Mr4-Omn`0P,кR|\4Ru/ݺE/D˗T˒$ۄ>۽v-t vk}wnټ^zϝ7ƫ硝¿:8+JP L~U zw}b޼KFy r }O@夙z&y:_ضOƖ8YКa+%% Qg~=vv VQ.IkڊxtӾ|gϝT8].,e`˳vvž5k~5l߾k ݷ%'M~}/61rc-bb{k *(:n؃p`^熗J[Kuuz\'^<(o.tϴ~W-3\vuXNMRZZ| wy\Dwwȴ sܵs8 ܷopW׺]]J%{ar_]ΝϽzK}2l$ 3߷omVW9<@0Pz/V/ 8{@eRW(o?ŋIV۽gV's| ?:?>+a7!D}@{Έ=}LNbu?G2vTX K:@BL;m:GPog,ӚaHO?Zt7~so@א!kpFI{{GSyg 1_|ʱ 0վWn!ʷ1RRk罺0o};o0Ob|4p~ݛu6ڳgZ|Dv|<]fVPAY#*qZy5_nefo30w_Q% X9a%N>qhd|=lOd Ww +J+WkC_*bDJ:C.921iJgeUT* =bf?j'>qۅ~'}'MԕW >``!Y\[xj@{v<7.xyբ^׹c=w<`'| Ja  4`;>?sҌw6V.G78L?On>o{g_Eu93s]rsaS( ER./Z}}[WK}ؾ nViՂX@Iea Y !!ܛ3sNH^BRPڷr3gf99YʕmI⑭H(>8xdw'&JvX\ŷ]H=Ōt6lRBhjSWs^/oy" ]!;5Pn4/g} &0;ai '-s;`1;To_o[i]^=?xѦT]d (Fm!h +}7=$s-E LWήs6˲JUՂCIEjR%QtF|:I@F9Z@iت~lFl.:$A&lyW#%yb-iCʶ\[iq GR ͘9^sqӿŹ7@p/*X4+;9 Yj R|+p.oTr20F8⁀'1D#Rp9< 68ps:{q"@c9軋N4%I%©brȩ0]O 37a$b*`ee'F SnqYW<>(޹`ې9ƇD~ CCҐfIc'iH^r}zzҺӓ67vMC3 =yU_}$bQf禰Av>4?)&nIIǭOF1M$a) ߵ@^%[vطgv9iKǎO:X饥Ǵo-oāmݱe3 t3@7yנ,+/?]5 Óh #47o-ܩ9k`Ly%ouM [MFr i!Z[XS֝U>0[a й.+%G:Oz{7r 6W,&1hn.s΍HJH{po[)@(Q;mxoFrc-CP_]W'U<<{OMf^O)iҘeu*^[T]?t5;qF~XN0%*4>p993i&#&A*oUs?3hIV^fQ pE@QLٱL+}. D"MU_߱`D.wC($ d%w̚gOl@S-\d#ADYieXW yyChI @XYQ_$\PzU]ߘ:\( `j 3f w׶dey7Hѩԩ\ίԮ,d0456$QX1ӟ?avɋa4x9kK9vvb#6TH8]-u ~8;9úu/8NC~җ/suK7%y6DZz,V9GhDc@"85FJhM.WX7wg\_Qj1I׎n[2 dĢ Oܻv4T@$QZKK޷uy"5#eU+|V@Yԗ-۞{m/܏(8^O5mO+Cڙ83Zd2 E!z-{\.k.UPhO 1ӡX'L?k(I2Y%ɢ ]CᤤtoVr뒇QZ\S1X3l$wn<.uLe @GVL}3/ԏ>ꦱe1lvdɩFT7;klHޫi[T٠.w,B[}jXG n1^=wtN}XLyCxvn*6"3E<.YJN $S|,?跮iz42*Jh +qIOZX٘?x9]zLS}1Рq-Y2d>f{|vٳ撃oV}~͐aVgܽHj4yP =9#g_cVnƪs$޽$%4iµpY?|md{)>͝GkFDqEo+NAmQ*wi,p@!IXA,+_{lDYDJm6@<<`d_,i/sk/yik~qshUQ{fEH=IȲ$/ssPB :ƅs /{Z(0ӞM(>*j&D9ߚ"hӫ% ) |BUOqH-լ."I4$n[X&oZ&4M§/z؟˚VNu5bP,2QdD#v;t@ES2IGUpa:cj)jq5l(:tC\1c&l.-=\PUU^_/0fQ̸^u15d4*k"]Up a[t|'JB4WM&7Pk]{9ˑoIENDB`ukui-screensaver/src/assets/weather/59.png0000664000175000017500000005522315172041035017473 0ustar fengfengPNG  IHDR>a IDATxy^E>{ߵ}3{H5,DD}DFt3eDMDQP"I0@VN: ~T޾yU>{߻Tխ:U6PN?(~`j!Bpco`& RIhm{{z[RjƎ]Uh7Ƿy-K$VVP>;F*?Vso|~G6˂ :QQ0||L+$S^ZxNw]) A`3D2UpHE`"?%`e3Wh<HРc;.2~1@T?7`Q_Ki4"PRéךњWN_MB%,$ ,) IkI`C5d@>Q2{2ݐ tQ瘏 Kjc`v1wιfM/&4jbRhD L&DkLA`ac&k5I奥A @h>s#́l$O|V#mv`9^ACTdrx-. )_؎ AKLPl{ RP|~Hq'KEBX|"Jr A.1|&A>3|5ig bXxLz7h@G14%i~WE7LM!%(*xY, ["ݐ38h\90J(7P$'+Jl, /oL=຾dIA,@ #*WDy?4@&IH, o xغ0c&rUYqˉ 03E;@SN㚫 q?pz|;/a' |"fhX b֚)Β.i?$N׬EO*sWĥ7n=5%f?XǢ&( /[;e yYHQZIPR0P@w*cp+{߄dhm͢aL յ BчFA`Jn-KvHPGUmʩ<2i+pAyAXi~N-s(D>#p :}A72dA C@& IN9-c(FȯH BF0|b$P&& F&dxc Gڡt@yL/8[{}6}M^=\A'3'Ҋܐ1"2 e/Wöqijె-rvtJ ŵhn;Z{j'?dpCV z$@ e&ö)`#w D L#Ip=3VoFKZ 렲?!-.K(4L1S^hQhF0i >fN-ןX*#_qnD:)5*loiB~Gd܅EpU$!I0 RR s/&tJUOޝvgT2v[`|^fh@]N FU}XQJ IbhiI ]# u ]׃ش'uDfZ#h; Ȥ,L_}X6\sIY\2h%'.C"Lf9HIAHXe+RIr M ;_?㧾LAkBr!Z"lSJ>"l ~ЕrP A`_cU P- Q|U%"P!=)k@ CC2&3ϟY:a2I\zXHP0PldڂklQ52i) ʉ˜l$6b f-N75~֝,JH PqPP BP3@h&-ݦt{7syM(蠓}*m4yx\ RI+D93&3&èP5Ұ- ;:`<-soh:z o@k`kLfRBX6 5[JKN_7^bm0~@# ˌJ[Axi8!I$hEġD) B Y*@Jt:pt`}m•eGŀ@3j2~F|'CcMRd-@ۀ Ap=?\fAj BFPlٴ! ؂4iJ:vگ/rxzkOvB̂'ϰA 20# 5 Hj43Ʀx}G/OAQȹpuy%<7+O~vNä,6}(㣫W7w߆yU*8;qky2 aXܘB+:1qB5|aIΞ|>)$d!- YҭC5\+ւ9XP x‘ ~q'z #@! (&Nӌ>?~tfR+FA1 |CKh<i ƴKAЂ{HX8s0| U*eEȱ!]CmM(@cJ0UL!&k.bE`* D I aY,,Jn)\U-L,…04Y. )!tOu-+*NJ$,6e:tp(ib* 3|)iE*kaks\GoVCiZPI"8b914k>/7t%!E"Vi *f@j* \<#X3cS{՝*? fK'`i52{ _V f{` "h 3.aRK[1ۼpNه.ch[PYh,(pRKdžy$@ !P>&YD)aHA}M3|怖VPW9(zr<2{:M]NdE"㒴U4_R4bq!`.@[3Rյ%[J.Q  B_=L;2ĜBe%$$RI&z"x#ASV^)S(eh )*:@BsFDXioJA)sQ,P,q|zz mGrb ڗ]o=-y$G\c da's`"1Buq͜9 ;@2LcǍt:h;iQJ)/VkN3a( I [aDRX @ QJ Ƚ&a`J2%㳕T|_=  ;C\BB9uرcWL6ϳ׸`5$&<eRT ?J%m'8Lc[Ng=f! STZ+h]GaNj6Pb!#* =HF˖eA[0! H o'`R J+ qy{OY)҂Y+rC(H&'Oss/Or0,V)h DN)*`Ffa[3gΜOL8qR%Icƌ!f.k~IQBi*>qD# ߜ7 GY( ;R6UB kv RZR#Rb, ׇbxhԩ?P m{ 37Q3Q9xB}}>Ldjkk1i$Rd#(* o:/.qqiyP03m+o =C\,C!oo#;ko6$R2 e2d2ƫ;?hr-1`1seYI˲`h 7y,U\\C*(|?b>dTjg8#ğ)^aH@2(}+L?}/*6B:R$̜ " Iu̎GZixX *ѨR{GHU1G뎳A"@2D:t:-Ϟ0{쫎QG S$'lWm܆#iqY X)(+ G{~GR(ڌ $iN&f|21I1R IDAT\.7N%mDчԙ:u4;FA2_oSk+Y2uT*F%[N;$"[  fvzzzB33~E,*#Os^I}? {Vp%M ?*QPH9$۶IJ뭅  =k9y^śR?Q*9@=xq4fq1qYD"s=uK(e{{|>E&!;Cl3d"J\kvr[ofLRNWg ^WdůEJ97ۦwQS&ݻ8f̘y.\ץT*5LpQ팧T 0) yP4t* (?h$hLƀs4fTEF0H7oD?Q͏J;zXrg6.J@S3#,6Ţy@00|G?(}͚5k/CCCڶmi&&RSlُS~?\:5LMYH D08LPIHqb΋1aWQƬ M044J(b ""fy'|l7²,>M4}xǍd㿣hSVt9RB^]Pb+ąXt4>G|$d1 (8h !̜D:zppX,6.X` `FDDa'8.xɨLE'FL4ɦ;~`%@,$8=q!WJ#|D|>uG<#?l|B. 0 .S6 P +JCPF*o27 T(hhhHkiÆ C|> 뮻߻wKa9"*0so fADѥ2D0 H@wwu֭_z]vjqluRk-Z PJB@ⰼ?(֛NtJ dR%-w$ThfMYvs>ED? rB|o)` !T1H (zzzZJJ)kkkdlƲ,Ă f;6ADL&Ikq91ѷLF?8N)`hLÔDQ6ŝ(84400X/Ru]@8T,59MRi߾}t}!u~0` xHJ;ZxPo“?y j-{{xHP0ټ~&<ܲe˖iQfPDT)<89m8D.Аr.CP@>b 1=b!nI#bHCR͛7?ر$"',&)R֤|>OmmmΓO>wu떣dCKYkm`T਋+\ٚ_eN>/ׯ˝W1 @ 0! %̞wyuY@!(Jax&JaSiT(RPڶmi _@:F:d2D"1#\XA+V\y{=|LCCCqN8q|KKKccccMUUU&LJ۶X,z}}}vѾcǎBpm@/>}Bu6 8wJG~hׇL*%?@6+F9 1[wy͛7W@Ry2!2FBǁsΎt:8qbc"mt:d2d2iGDq4&D03:::'|~7%1؍#$ ;c+uP&gzE2@xcx[XR_/^(; ~SJiPb )` &$BiU)Y"J߿C=6y䙧risΝؘ`}LDl"5*B&q]ؾ}ׯ!LL6mܹsL6mZSSSZJY&:öm#Q@bʕ+7?/k+n% 8HZk]tCD$(~rB@Q?ODEfkt\&|oEu&FʖwO؄`-`K)-_&[BTcdBMӧϚ1cI&MhhhNL.1 o޽vܹ}g. oI!fL4iԩSO4i|KKK}]]]"͖L8so۸qÇZǿΈF&kL.}FQ@+8x OI ]~YN(7'Ϸ픗xz3ʎ$Q68 _?}yڵh3$FQXUX___f2Lʶm)ZkW( uw6h!@$D0RL*m۶{{{{Gv#fFGFшaR,xh~G:~Q=h:V0a|v_8TUlꫲz]cOgGk *-2}n3fHr)nPJEoM ((`QsWgg现hxt <0cO6u(P#њM6D3|>ߛQFqlRKaۄ p . Jqද_N]PK3'ٍc*:죒f_رbqyt:9s<_o[nݪ믿'O=7x{'R)aE;E&$QHb8{aqID2z]gp(t" Ll̝{9@TbM²CaQ>1}#l%N 4|(#1}|]GCW|~~G`d)0D0s1\o˃<%% @l۪¦MpBkruֽrˎ;455iӦrg݄ D"Z !2<`h؈ -9򇖏S s?|˧|3Oھ w3qAz*Ѵpg"[?{ƢzmBJ bi&@20>+pZugt@_к8:@.:npYf-*ϼK[/^|%3[ZZj9看/WX{WI'{m}O?mڵk!l5Qh.Fr!aEtd@$Yڿu+\Ho+V,馛6ٳnoo>;oF>wÉ3g{ٳ>UV=dɒϯXb1-+\pK.?J2)R6F>}0z.\rbu0'3)Jg%e5<^-a"̞={CGT`[fC@}Ň&l V&a/2=E_~b;KOtR$]TUm mF ;&kk/m{>yE+W<[no.O3g{+-%c_<>{'1s?鏦Mִxs׮]0sŒ0(+8qh"B(N;L}ª&Ab2y簔R2-̬'"}k ?ĉ(jGWgdu6.:H^p˛/}ieYwڶ}UW]+ժioo5ͮEGڸqX~oŋ6cƌg:k_+'K,9W^we}VtKʌL@Nz@,L+30NSa„c\qu?n{j*L3;yɭSj_(+^9=5lr3< \nݺ'tҕǏ߷~6m [N heݻv͗]vمf>0_&k]7NW_}uwܹd,  #h \,"G F&ط{z?xjN=}2ƶTcŲz55 5:_@x=̋YZIfSBtuvC:::rܛN/dK.gfX,fͪ]z?c˲ =ww޾p3~?N?~׿uϙg|嗏]n]Nkhhgy[kN;38c]0s& t٤`jMJwxw:P5k~̚GnXmcXQS8Jǯ_>{>daYc/n/;?#,f2?ɓ''\^{go~3Ͽ0{lqw0o+Sg/_{ [ZZ]]]΂ +V8-k}f֭3O2ytR]mn_'͛7W tl m߯ڶ-;::ҥKNo5 ̜PJԯ}}W]t+bĉ-zge˶=cs/nkk[ ҇47z/_r~_{*GDfWڵ$o'JyNYs~ `cQsglٲMMM{0Oݷo[wxhoYguvDwnBdcmw1c}ҥKK$x7uss/e*tuuluu5.\8 _?ϙ39Ng D񧿕 Hh gI{mmm[ֲ|/Dd+D#!ӼzϼY[O'bŊ/e%Rm۾b``z۶mkNA,Oo.oڴi)HJImc``TZkuwo{[Wkão)L K) .^>o'N?Z;J)Ä !jP^YDѼkZy'jSkme}:Rf̙3ץR'OtZrs=1qQttt>/`566N\hQֺW^}7g gT(RڿHT|ee:\.9\ڬRk9\rfoI:2ҤD"a8 Hm`xM!\ 2 5UR5iM vܸqY'p+V7~RUUUS'֜J/έXOT 0|||[n_Z[[iC+Wl߰aCUkkkeYսc+ҁFK2HL_tW\GJiڵkSOmz„ ˗Y>WJm۶8ps=:y;h%Y3BIDATW״ !RItQf,mw0o|S_8u{J'lJ_}XJ$w drڵkH$t 3f̘v '$D8_gΫ3<W_}o}ֺ6m!.\(z?z J)h}?|o~/ϟӣ_xSZҥKoIRT7E$"m۲m[:k܂ W_}u޽{78ÒeYK.ڵk7|춶'?ɃD4@n\fv}wUUUwlllD.+i~믿kkʣ{R"bTPq cl]GccYM4Ǥ3㒐$f6LҚqƎ$Ak$(wDbP޻wx"!s;ݺサ}6nܸP5jZ`g&zH---cF|#XT*U+<[A0SJ/p @-J_>##Xx }@6o޼Pv^"=gR:::8((?駟p?qk1p8<e>裓׭[w_1zha}6mtL$Jzvp䍂8Ʉˍٳg?[z!dR>jt&E{{)aݝfn8u*6[fJ~d2矏x ߁+S!@z?WVVv,_H>쩮1cFje}}}dժU{sss7k4xj)t ұ`[:|-cD)meAF yTcEWIMc,.+?q?76>uTT$بǏr===A}}}SJ;A1\0!p8GI|=8 ڵ/Zvh `#tj@6].ƘWۋx^ c=1 c̪O1fS>Fly?sLŋVRKHHt:unՋG=5ܶ1 q\ !$DQP(] N9!h INĹ{i#j!۲}_~KCCCSLLL([om---u:!11Qn$c}uB{8&1y`8SRu) of MmYYkYfN.+8`Ϟ="##ȑ#1f̘Սb@@@3glyĉMO~c8x]|~~0 Eݒ$5KT+b$Iu$5@*)F#[RSSfwU*ѣz'+**.‹礤G8LOO(0a\%HI)k_o~BY|~UWUUuFRJkkk{===G}T5jϼy֬^={lh4=f ܽ[|le~:uȼcǎlf###nkkc*}* <̖~f79ذaWs{nz޴iS݆ jcٳg]N_7o۶ڵwx{q;ݟ{SwjD\z쌌줤$I'xbv@@48;448qg[ @WRyE4z hrnIMMr6l8v9s?~|xuu;44?uT#G-C,YeV Q6t?*I.r+1jjjf=;511qoq*Eb1Q .؋m1XMMk͚5YyyyEJKK#6 m]yCtdظy SNe9RRRmnn;::_|ٳg]JKKvHUUU#~7QT +nu hRSVX5w8U6pmʖ-[/F$?> }֬YO/8N:qDŋlݺ2$$DU^^^7o޼E|m۶bŊuw@H=1#!0؀`ǫ\tI7$I&bZXXXRTT8}޻;<<\)9F{」'Bca B(h?$ne?&\~5Qv98ŋW|s>|1mڴx^^^ /,--mj팊?S###GUZKϗ_~ynt UUUf_p( 4_OB ܉Vw0NQ< RѣyyyM6fskyiҤ;v۷@I0ѤcbϲT1R]ar -Z-BMX)IcΩSZhy V*ڽ{*sddA$v-lmp;`0N12[{󁯏R#}GZ$I.N+,O:1Lyy!/_vk>ǟ{=&M2o=ѣO\xIlzr<⚢IQ]vP-ϻy)B{W?-**Z5kݺugm Fқ)^aK6J1zlW9,U{{tϮ+//085c -z=%%]vO>Oz衿ϝ;wJBFFÇgBHDhQ r8ܹs '$"1䭟'-a>qx;ΊUe{qXa*jktm HJJ_t9s 9sV(8 99yΝ;+++kj!w.!I,ÂYT7~C74Xso Do<YDJ" 5dA̱c QTB&goS ƹnSyyyϜ9sٳLիWX,=;vx/X$ R~~~ɓ'!<.#9 L`P+d-qM*)UR$I&Q<O"㸥H;^\s]p+\}Bb(aZ3NV'qnڴi%K2kjj)0@^; n-&Ji nQ#w`a$Iь1 (^ u`;[Ec1}x^pmZ^@F$b %7!$R 9|f.2Ƽ۩; RxnA/ߍNŢ?ȖrHȪ@^Xj=@~?‘ŏʱ0+! EѫQQ?u:PPC?8~3IENDB`ukui-screensaver/src/assets/weather/8.png0000664000175000017500000003126615172041035017406 0ustar fengfengPNG  IHDR>a`tEXtSoftwareStardock IconDeveloper - Professional ver 1.0.0, Build 839 - http://www.skinstudio.net0lt IDATx}y]Uo3֜RI2$'"- ݡ[inۧ>DmҶ!Thb#DM3B%Cus^{]'֐"|w:w}p B,>2l9Yz# _۷kd2===uiPVr󼁳:+onnv$῞C@NO?}}.=|l6PJ9l˲@DB`fH)y۲t:ܜ9s\dɯڎke BEN5te?/Mǎ{ x3\.l\B]ו늁qL>;x;>֍ ַ۷өֵm"mtT[[X,(JCJ ry1)"gƌh4jb1J&pBjooeYz|T3`fytuu pRJRdfl<>Pf^PjR䵸 |xӦMߜ7oDX,Ft.\HtRJ|&aRJ T*UCu8S9\e󈙥Rd2;y^ϲvO>}… ;a7 82 ׯ_iF"X,&4/Zc{%a?H㠧l㜠xuO\ 33JJ"Hg}} mmmO.YɥKҪ)Pfוjt7fժU#LdL$za0򕲂/(z7 d@9A9a&EJI*fRkjj~dɒW\YU61d2׼d2i#cܹ ٽ^u(  BE@Yنa$Rx<^2krQ*1,؜9s_z7/_ۯ4L%@KN$2L---hoo?`aӧ(}``}}}(J*b*mDhllD* #UٙLLS'P(}}}d2˖--exd 3gǾDZϧH$R>r`P)y z{{UBUѶm444iqT 3`TxRy.O^s5?4 觜oӧ&I$ BuuuRz !D&sXx`Y"H$Z[[QWWWap(SBXxOG?zӌ344 Slٲk鴰,˵m۬mEt)ѕvYH$˲PWW)__-)D"Eoo/!psss;W^y if`̜924d2IpNma?(ReYbY è|AdR>a5zk}^z2Ϙ6mRv-˂ DW 8M6LIAG~z&R3`wpp` \[[;/J3dfRHyPl$P4h4h4Zy8o'0@ :A1,d2ID$GQĐSxe%ٖe`dj8eC5f6^n0N&D" (&G0 :Ϫx\wI)<]9J:砇b}5ΠSK?CѾ C7A~ Ð}}}+; ]8IrW]uխW\qſϜ9sf2tH$H$LJ1==ذ"tM3Lޟ WM4F`T*y~+СC)J~߯pMM LRF]]jjjJ@ k`vр"2^{~| 6t!'Vj"8hjjuJ0!9YBIRF:f Pcc#JJֲ We* %*A}$ 8tbbIVKR[vD1Ms=36mZ)Xlɶ".NiEFj@#6k`c~d dfdkqb8N)(Ny?篙6mZ1EsESS ^5dh6WAEJ%aT4͇>x֬[.zw0)Tʟ2v|@}};[[[KhjhhKtBm|XC,L~n0C)#sT9au9YPմFfæ RlV !'WZZZ={W^C0!x@C|q^3gŨFs9Pr0Z>L՘ㄛEcPuR@A& y .O&~@)hl|mo5k_|wg͚OZqKnBT\p!ʂ<ԕ"z* Qd(t @ em k Zy.!穯|جYv7 IbpKKʿm"T*%qgW2}awsƢ`33JaVGu:_}qVVHuX,GE&rK/} cM^bGSmۉƌ3*C՜=^ F6ȠCDOece0aUcQB}3cZR,EPBXd2g?O]k֬ X sϽ0 Fmm-Qi_5jZ!ZqB 0YAQQY_4E*OzLt.]Jw]y͚5 `,0x-ζm7\[zckz1Ancc՜ }_m\"88ADP*ȟ\d2hѢݻnڵ5k>.F@̙sy"H$¾B%Wk  :YaLh_5 GnmeYlY Z/m߾|pM78 p&jll<۲,m[b1bfVWޡLu~06l]ZaNcI5G ЇIB7/^m޼M6 /܆qfGH$R___?ݟGX)ۦBj}TTAUAq넱z?lRvkTaYRJ "/ "m^  DFQm'8}Vʩ0TS( fGpgXun0 VvwA)s<7S[[ꮻ {2ifDH5Dyar, A?k뻅珵cQ`0 aTDm#"Q" ˲D{{, " MDWv X0/;0P} @WH%v߃ )袋f,Z= dzRJRp0ۦ7F Ao^o2! Քj V02aJ& _)mH&|ݘX`4q]fu%zH Q ,)R5L:Xv^Pz0^SAm6<+WGDK5(#@063 Vb$֨#j~PTO1#A/LFhyj~ٶMaȺ:sŊk:QFe"2x-SNh+v>8k Hl”\'樆68z?yɒ%t]Fv{ّfbyE9p#hJiFr0hd((uRnNz צylQ;۷oǑ#G R6EPL5Pa*QCީA)_XKAW Z;;V#uB$&I6 +P(ںuE[(T*|>_UxxUj=Gf}u}+Ta4[@dLBF3aPo13x4& B "~% 6+80 y'}VSkX@)jPeNT=00fa'x^PiV_Sg0"8"BPp(#.E _Hj޽mڴiUW]5+H˲ "b"&l`7TsFlpHPjyzfNW^o]a#oJeacÜj?2Lο X`$Hy̬?#X---0M>yGjb/Ha T9z3d‚Qu10  <})iYJGH0lFh)0s@@jpp+7._|"T`VY]3fװ]5b*#}qVu :zaj>@ FUh~AXoד]Lc* ^Tׯ)0 )e#L iF6ٳ;̛7o^STdynR9j5Aו`%-< $jv<st Q PMFr4- uww|;{>!Q6 z"Qֶmvض=u]ZQz _3#5x3f l1,lԯ+.{w݌f19YJI?{>%!Dq3zF[l288okkk,2Wf:C~OLFJ)饗^ʬ]kePު'?a@>L|IDѾ#>=cXmۂu]RH- \(8ϳ.K-^B0z0Ň9 &ǀ^*a&EY5&$k8!B`s=۷),9 8YR "tuu{^|g_޽{w_OO,2"yBJ)ï?/ ' pRy%#(zp004^`Quww0 #USSS[WWW_[[[JD"aIDb ZZZl"H$BRJ&"5QQn0E6nzܯ@1C50Τh}#,ndǯ&< ֮]m"rqb!czWB9@)!l, zzz*Csv͚5oq<`f _5x;SAZ J)CQAf gP{) ^T8&`lڴ)swܗ_ CDt~J $Aو0 $EC@"{/^tŊ)R{P:m{YqLEL6̕Cp!P zeY3P#*X,Jf&'x=ܳ6&"} eyި[Ҏg@   pQ :xo<{ץR[j=f6X аAb8`*2 KDa,e*CBA>6[E/5T038p=}oe.(Qو1 xUss(<>8;v455ill@ٿPʥ`"$%yUf+d26br?gi "zPP al۶->k׮>q"Ba'+SLqr9(>#އ_|ŧ1 AR*6u`( skGQ#]p\ Ҁs|u( H\ve׮^z<O}& }:ணdN*tRQJ7 CJe!X Xw:Ukשl_G<̡}{?oa[fΜؘN&H$bX%\ ;cvڻk׮|> me@^B)ee6N4̏{W;wD}/fx*{0ZS&oƌ/+.]:;L*Jm䰵rAIѧ2eGȺ+\X,9sf,?vb"sP^`f߿}G6 FD?D#\ϭ"b?OB3XJa"^PGsDT`f\#x@W\|p{fsGi~~txץ ?2 `N=UUTʏ)e(OG>z.(S"p&*TR#f\'u!1 F{U1<ڡ 3u-k?O޴> gPAekq`V1/uuݻ^f_ 9~CLz֮022DJzOQQA31!AKuL @8k_/ۡ^37Gn,ݔ-bv"5ۿ闀/!|dM@/n·0?&ܳ1b*U2#K,a_(T0 zj4W'LIT竲TW^cd8o[?]x+IBvYK (OR :n7noSNd>qjί W|)WSDb0Sg0EieS @0? G*(?Yh 3T. FYJt]&in%2+$%Gr?Է^5Kd>YBP>" `E' R9c BT#B` @"H^ֲ7/I=B8s=E< ,`&$"59ƽd0IX8S.9~cf(bb[&嘍m\޲瀜wFV_ßm A++.R͖A<}DeO<_)QYv:T%t (0sDJixC,5I5P'"* !*0ʏ}5= ctv.ۿTm^Ѹ臰Feu]vߚt\u0cQ ̆~II>5l糫V_(`:ɗ^z`O2(PEI)gjy?:C )p%{!QT !* ̎af2Jf0fiU%b[vmUDs˙8^ƪ(-ۗ66[ϵW*;Lc~G(n(>rF"oKڽ zޯ&x^ybČ.j`jϯ/S*W~eh>T6mSQ=h}>bAOx%lj8'2m99';^G!)q<KX ˊ 3cF&& eoN2Bˢb) =̙@,ndHm IV}|KNq\O5L'SOWλl'Enm Lɜ9 QNZKN!d0 Ơ7aٻ#g*[@dgy 2|lJ`¡1JծLWck\eu|0)'}nz܊LֳTvsvk歌œMDkO3ppW~،ݹ??W1wo{ ?6~y*)ӝvO v]ֺ}#>}﯁Lӳ(QM9@J/:@ a=&MHJ"]Ckﳉ+g %8!Qfd4sRdH;Lx߹GFi+SLzLc D55%e'SD&bW'h>Rx'Ê3St6<-^[yTy%|a,=G~ xwyEqYt;G8?8 +h%W0udڟQ?8Qb%#|ǽ o Pd@}= I  }3_5lܶ|.95c_ǵk 52Q wf# \%;/2 + >|c=$R$호Kssgᕈ1lz*d + x Jr^O_m]F\swcn;P#`-SOVO|Z[EO'H\u=YNv!dis~wJ 8iY ycB-y5y'[oyΥ|o?1bex_s;hc N).׉&ZR7QH\dULI5-7y/\"]sR6NA594Q(LpzXW_Lб|-cQi㞵uQ}'s@9^-x_^ moyvݿ f1_1}?hEa玧;.M;/Ձ1f#sfg$}JdQ@iw/~wciuk:YGX?)wQɬx~Z /=~uw|iw~E ݢ':`.Ox26x~GκQZ&u2I0,d04󴒉 &K?=;pϳ?ݴL#۽a'Wݝҷߜ6(l^ x#s&.aDR[n`ӴDC?ޓ(xБ6I2lO}|[5DÒtm{`3ZBإٳ;4*S !_c:0a 'k_x$'la QS'ed-(BH]M?yfmnov|#6kRykI'Urn>nS)̥~IwֶEw`B[6OīiGۓj^pt穮NnY5hwIiPʞ9et7$[?oZޝe812Q#i3"U$Dͻ|Oo"Q@ϱX;{E] 4IOJaI=. ^;y q37.(5Ͳ:%=(O8#opI7k8x,^sנT{xDx͚gw| 4$H `VbRCr#yIENDB`ukui-screensaver/src/assets/ukui-loginopt-custom.svg0000664000175000017500000000200315172041035021714 0ustar fengfeng ukui-screensaver/src/assets/scrollbar_up_clicked.png0000664000175000017500000000016315172041035021735 0ustar fengfengPNG  IHDR;֕J:IDAT(c 2pr5DkiXl54]3F4>)4 ý΄IENDB`ukui-screensaver/src/assets/hibernate.svg0000664000175000017500000000131315172041035017542 0ustar fengfengukui-screensaver/src/assets/01-default-commercial.png0000664000175000017500000020537315172041035021555 0ustar fengfengPNG  IHDR+usRGBDeXIfMM*igI eIDATxyeWYyZ{:ӝ5WjJ* IUeL2hm&Gl}AQTPA@ۡ՟m+Z4"C9UIw:Z{}=@pSuzw}U~9cwKnǽ{y<|b欵`ᡵ(gX֙F Y]kn#{q"Ac)㼦X,:2^3Lb}WL8͸!0Vr"2GYҞYO`lϭ:'GqYK8 JSMhS8S׵t{eB /b>w)+5R/>(U.|e4st;jE[ǫ7w\jFk3ṖP|TʭkXws}9H{ZF{-܅|t9%_ARt0TLdž0=R\3`G]xDpF SpK-vJq"ܺd=XD0Vı&5uSBW(f3ΙJV ǥ\xGzy'Vn'1Ȝ2E Of?RfZj;c|Lϋ,9x:NOc:aSK&D"yx珱e}Jix\8G:VWSOD:_ߝ;OHƧ ::NoE(_\I]#aTRUk9ZZʮ\k2 Y C1-FŘ$$JE7S@A2|%>+GktRG~gըMm$5kDIDXz4@0R<|DƁtkwoRTΊ^0]3Ȗ-YZԫ&I&=kAǚHzDc2cEinjd=T ج$ZHkNZ'CO}ÇT_5$Qk j"z_T0R\-dw=@PS#)KofgMJ*z"U?QM _D}[" D)r(?=nE3Nx,wHSKcڕ+F5QB<9zԦ7)PkFa)x{B!S b@ e@,_djR may˼֟3.H(B!m} $5ƦPuyK8x#$4*f N!FXj[qg/v]4>.Ä1m0&juۍ53:r.;[;(2J5"<3DpӞ9|xG:Ik,F)!F7NP|$ON ~NX313I\Ljn-P|fb܀c-ϒO||hٽ Cgxß Njv|6[Yqv&'AN9C%甃oῡ65_ Y~Oo<= 9Kvʑ>%^)wLT{O{tPqajMGĿ)~uّ*eq.䕻~9H4dn04OPZDƟ+.jMuIHBCPb>gJ@%}_*QbxӘPq`ssLCeO "; 3aT@N6!ޒ;CsCAD??}.SҌ+h˥OمntT{]Z'ex4y5≹ʍea{! ĉ[ϏHwv!B7e(i/)0 S8!jN6'M+ UKZ E_繁df+V'=.V$rNpNC rNX͈kV3DϢpGͽ Nʟp:Jm2M:%b Gά,/;8j3r>?v{ay֞< :\`,w?L{]0 Yir˷<‹}CSD|77UmO@ i 0L$rxXnq3% sڄFQ4%\߅eH]ZFۖK% -4+(s!|e9㲖do\uO7v0"Yfa1 ,i2!_\1(#qՅ<98:޾ZjLv/Bϵ]O)vW,6Kp*B3wJ"xκұi4g16k'5_x ; )>ݱ5G;bo1Eg467y擧2W a)Wȓ?A/\[2K模#m \ UwZnȇn_?r Ph-h{mTs=Vܰ+p_:C^͛>urQ-/dRB\Ly [B>FP&@(es|E1Z F֊4[&Cޤ.$_:")JD$plYgE BXȬ #a2˞a?,ze?$]4A6!:|:if[>!Qu ."/uۏq~  <و=s ǖz߳e< sӟ] ʄD1TL2F1!AA9xs&x-gRPl2z]JD4F8ub ۷֩Q!ʠD\=)3jH"%!ɨ,uDljE$&OPvg:P Z1xl:z?'BH`B=q7}pܤ t,Γh <%@1c?>q9T[ dg̃lW;b,t: J6hOg>D^@!8F9}lw quA=”8>@CW [[c2;iFn ͣU-W< ~CUYԒe}:BOWK1 R @Q a.ϼr^JrN{~52OnA]1-'\KD{OĶIfKݰ&tNYir`3˖_f̈́I^R"@8C&KQXL="ƻ(shPfKnsҒMcq4D Y>ɏ|$?X -96HrmjkMzͩʙ0Q~.U03_[ߨpJP"ZyӂRg n˨@ZA]vSZ(xfZs zufN&@AMԜA!6x|]>{L|=FrfőqD($A6T,Cgy%xSm #I*Ѫu2]2rޡ9x P"(҆dz$YlU<3''M 8e ꊮ8(E=4A+rZp V  'VxɯgY5 9Rij퉔1C9L8ZoJM, $3[%Sذ(#ؼtJ'9X^Oa1N),0 ([^%o}|糯㼙;R}'z?ZЏ;y}&7I-Vxn`jvF,u3]:K |Q@{Wjp7PgvliI}0w"#ly3 9˗sNy]a1DuYeã^ÇbzahIlk&ds!Q1⑈6ZMm=ԿJlڟ[CpbWtTJO]زJ,us$p V"ה YjDeIN?㭝SJn7Hl[zrَ%"5Z:Sg-CZRyK9l4 UlVpqxWBv፭3uSמ_yPrNGbZFN$ :wRf|Ȟkۿxqo` =Ĺm5ZyICD1߄0U{ovLux#3kY( 1brS7}Q(a/~s}_w s=#Z5F uLa3yǍ88sZ42B0`MJ=Q7,۵cm7ะDU%i8I Q$5q\@H/FW}±,/y;&  hn;1B34~G*p*(k BxE1@{#;pmm@^z5wLP[.gd뫈9e6_s,L4bkUbe6[p4u{Z!v*1MddX[ xYMruhf<6^'k2HtXE {)+Z,ihlLq,$3cs\;iW2@U)P`*:"M}~K Xp?scpzjbKY$S4S-}`~k&;ꚿ8)`jjt*k8 .FiRb܅|?I^Y=jy|.m+vkV3zfܺ8^/q"b=#4$0KC~gV}B '5@ ӊ_X,16tLU:V[4r\;ӻdDnj߾/ݷqd]%(] ~ZihL)_$PcOܼ|V'/$a+fqujjV-|m'֐+^E ^CRql\΂WA@xJ<1&hS„fDo޻4gu~wJ= ).W 3|tQYZ'c(]31ciG=>םֆ9hKcⱩ bhtД!&oxxۇA"~n#K-PZ6Y#G!Pxրu@o6Eu2BP3";/>J[ t6M8a P`C`# .?no>{YB'ȱC8Cn c«1FH3J31u-"cO{ۧbmrLP Ʀ*pؾ2,6}vN';Oe}gp֡btB`JB2@yígh9gZ/(`ۦ"FD%lPoMb$衢p6 _8r=QLBqS%3=|ږ U69}13-gknY(߽+vDPIdwz o8N(`GF!@cOZajaٟ}^ëێgcM j.3˘7ϋy_Y0 =ֲu<:秹}46?K@y]] msdα0!& lN"Zä" O!& jR5%QW7ͺ"=NZ8ZWNyûm=Q#u,i"|t.Ack+v=2 *P:s1.#e]8Uc|OΞCEck)]v#8S2ӆzFflHΏHvJh ecWh6 "Σt{(N6ݥ5justGq@2ذ.mh51*$ u=gIz}Ad%ߗG}cf6(aT)2+':wf8tBq<6ŖR mH|\)TeβˏϡhG`M;rQ&3!GY# 1~:ʀp6Ӿ^Zc .T }-9.1 !N"@e"q.a{7PDc8x y uϤmNi>ADa>[)s8{Nw]YJ~~'S536ZP,Y Zx=8*:ѾI9IZI}8h.o/XMV;,-f$" A$*9rXygJ+VO.3jaɔzMsꙠ R;zI aL)O..Z`K$hg ל^f=G"RU2SǪ<(]01ӎ&n~m[sd)B`o+hz6OwH!Z( p. ZR hfFZI2ÏX4WS?NGԳ7hX#aR+0ymRd[f{gG?ץcd0$Gtֲ_Ha1l-By-3j^pQHJ{kC#⃯w]!hN#.OmEG*4Xf˝.#s񪧎0r4:$˛6;scŌDz yH!o^W~ >MyNP%VH^1<{"|Dr")#rz(ҳaF'=S~UuiƸyȤŠth |de7YBA!gYC7{ /7Ny]?,ォ'3utC>f1Yo~r0 jYk+;vÛh YxfE[iUQmgq'Y2'<㭀(V φU.spY,Bj>կ_v1hj;㠔p%10~V~j7]ݔ_o~CyXa95i eVGUª"En~=Nt3\G Q¦]} 33i65"}]K}|}{斳 6H)~3⧴;%Fld6&_|}7"m! JZ9M#icQtOƿgN*#b٭q "~M_ע= Zrݿ_@G-qDjqFMs78!j~n͋A$-p*` 6^TSEV,Pӳ^h@We7ީWYi9 ]djqlJ0y,GeLsEdw'7YO\xܙޫ65_5~-;wQ;=dY13UreW{VNcwS-i L%U•`f[FgR e gݳLK3Ks9ϡ9Ǫ򑧅 M=cS\ /4R)EDO K^7\8c!PTqe{|= D_-_\u;R? 48--,:ҡ5X8"aG"S > =qSKC9F2/W\+fefj#~r%{首+ sk8g=WVdu 8uR8)z'y#s_ZgWXK?6B5w{*e`CmTuS<*y:v%B栃@5[ wrMhʜQCZ}uX}k,V!/LbCs(|FYGFIߤ|zս9~y lGڹ 0,0fӺbm'Z[O5~Pcecڳ)5ِl^++WrN>Z=1C>،;G;djlQ^c995S"a)Ze~()TYaKv~Tٸaerx ."A)t'9j(܅Gi:8fsN>Q ]?k8TMÉ5[QRZlMuC"XdL8wy-Ŏs?Qkp~3 ٴAa(5C. h=V`_6#u.fgar'^㟾GzݥxPvPy*av=TMqpG( 08m*&a95|DʹMaOC,]$Uxm3o`t=AF4DaZ>5#,<7ܺ:UnVJX@ۤ #X LgPy[Rs\ UTPΚg6O9>uڱ5rn>}Kc~ Ԇ}FʏEܛōG*͇)>H i" C-A4(c|}=eoN[?ɩ|i(Q(T.eVln„I^RX"/?_+.L%}&jǹML(zrdղ9Nj8踟[9LMEe[l_h`9m=?rw=pN)g~kzjPkTLW9xFSJprʫ|dk?m,CDxT{@pMdH# -!Li3[je} I?G|c{dk- |L;|y3b$$# sh!a(ϋJ %T\2kTj?-x<+Fr5I4-z=|~}T_|rٕŰ 0L>kTgb\艕G=Y`L/TCZJR" Ԃ>~+Ch881+|wÉ/B[ugt[<:] B9M8{FO%ZhX'K>ԯ|ˆGy܁EшZƈ(ZWx♅e}p?,+o]U>eñ#\J!P l(oe c}ZHЏ}ܝP/dX{T:6e97i)H6@;PrQGN7m 1 \8,ӜQL4㧍ws(D'7xw̚:vaW5ATy82˽_1\ڄ[i|dpi&O((atD,#߿ӌ#!R}J/-FuLH4\|t!hf93;8-k>]yf5}˦Vj?@RڜRQ툠?&L0Itk #BS(c#n24s7K{e鬻.2G]kG8OW{JCUjTtEU..KS20A\cf" u8xGW ͓2ۧujV8m /O|> dRdNAR zS+q(~\6\;❟kst p6K'JWhžIP}hյŠ +-gDAu6赻YB*O3Cs?8*~Dao^GE #W{i(4BL̎uov@bb"Zs]_P(}/ }|*yvo9Ι6ӫ]WmԲTWktK!hV.7Ytz$S  thk5wzVWŚTXY5elEnu? J+ ,_ԘTiշssPU\\kq5朐sg k=ǟ#3ex lkNua3T"Cؗr}p>toY2|EcG;3A ) ҡV!0U]P6 @MRl^`<`igsKM(m+}oa 3 lIb hpE.7%_,QkuX;\59ln-t6 yo S"9s\ĘjjTljby6[g&$*C)85e/\H XJM"}`or9OB$ds-CCc3TPo~GoP<>a l\:^@q&G1 , K*rjL#t L qaj LqL20_l2sz7yn] p8/4b,p}ˑ|oxO 0RXqs/ټ ]QݢŽk<)E^I0J2$C!"}} ҈$m>qi6ذlR~OY:3)TENI3"7 ʙռOvcIXѮ(+'刭D@'U!&Q"r`ɢy 2ɛl V~x*Dh,쩅],F $ }K1,$ 4$5BȵG~ ƫoON&q̈\rjTHxEڛ>1]9{Zk Nof14ς$bj35 fzJƦ 3:ڢײ~%P@{|k Ag, Pj@YnWq(%|ו\nwB{54!S iaiL $Cpug ahvZ|?ح":wDxKS=z&gNOr ^,qU!a3_yD4m/! {7 i8^Jogl/B2wo}Yj9(J/jsffqBI4mPe@6FL/T8yuX,ȕMl>S&L!4ey5rJ}Fؖ[>}+>V c4nM`d^A6Qƹ@XJD^h*=hиz̫tM7͇lq5m'eǩMO2I(T'KՕ;:x1F}ivJZv3@fL-hDo`Ν'Gr*<׾B&\SYhX>y jʽDY4/1`҇ rLXkT³,C$ːd2q^u. F#4۵Ϛq4^v4ov}NJTŝ1νsbdJCP0K%o2"DEƝrCu_0#O q`5œW8/{_uF r8們_"j3>uzQT>}=jddJs*m\ )I7ٍ ~i:r6,}]ųV\ sL؍M:%j䧞$}EbEozAFf`w|g%{|eo#K/k-•ȴ>K&XY9 K+\/Mn#,a uɦC61d_y5+?brw+b"YPu88q0D-0´k|kްC7 x9T-kt{qkGǶ߱7s7J ;IkCA'pќHZ,tXu1Xp[]A/VC'ZS/ę}I}<}{lip! 3 ӼW U/FƐE #a8Vc$LJr]^%^_)_I//+w=`wsX,>@JKٖvw{{Ηo纓Mf (K7rhIZ[#3jEF\1)A 2__A_:tvGߩ͡{~~s]ܟo!P%̯D\:%LÄhGonbkqLx\3;oߟ_Qzgm/=ϭ1( f, q틻F! >Xf~U]zr{k~~=+Ű!\8yjʫ͈:{S `}%[~}Ŭ/<Ƥ&r 0bύ򽊁5{p~%V_=6Dvg@tLY FG:@c]kJ֏I3It_!~%>_1P2ߦbt%0V4̡*Si?>J_*`ͷ'XY:p#Rh@cw$!1)i i ~8I~!_KDW KHoh< o5%U-…F30:>bg>Ic@ [a Go TЫ˚_5/퉡!Lxi$.]ox HazGp =9*-}P|`-  _5/Di;n-lͱ1e~u^["8CtO*`f' ٵE‡jyZGX9Oޤ+ :7m'd0oGZcTzoHAvtp -K:OV-v$ڱ,| w7}oƙV@Jc)ИŪ7RMɘ|$ d%g\; 1$G`KN mfߺ#$`d]_ ,5]Lq <D-³BWbj;%x|oy.=AZ ~zc|^Kxt+7D#Di5$+FWkaTWt-hMf>b5m 5 O?O%5].C 81ִf6nRRP3+"#gաMϬ0@XJPZS>C{IVq啄J{*ſ0T?6OAXdEDO "&Jց&y>Ak'50;R1X*Q4t)!u]e_;o+46#ƪk.NI~EI$"20?k`SF~]R(^W:0QǺ%1&'fK@$}Η6:^=EQQј x~-}~MAu1uMcne#<3P: jLXO183NwJ4gʽ'g {cm© !1]6JC (h  ˝WZJA@]>P@M' i0M\#"kg$AfL3>*#O)43U,{Yi1"YF_H#xA cz5­n}|{]W>#I4!BoAX jj1;dPoJB; hxpOk`iX ,q W'ֳ2V1p6F+y hAHm}ƙD!㎮mejr *%|k,Ii``npo/4ppp]Wʡ/H6s\aG/¾Hhpg[;ee-P k%'0rJn* Y0' 6?Le[͒A !˹]g=z)N 6c! gToz Nof"zp g \ d+Y..V]zw!AYCrPp }Aা#UЖ1]=MOMpj=éK F4X"V`cei' Mָ62 i)+񇟾=m2oxz pM8.NrER7F^%sіYV VNGv4R" V @kM U8LJ*_Rz+[.?:FL<$j12mMe?r:E[jH~묀G@,pw;%5[xFTy̲(dn \(`F~G3o_Ķ/_T]Ba+eve\]"}="t2d켂A8fxO%ՑxN8f2~u! A \mjHugʢ* dc;Ğ+"^|tߪ5iěs#XFcjwjTYn7^`_MfF !pM~A9Y)޵Pw~%!|Y 0zSF>rvn[)hd̲xtlD_@'w`*U)@ P U5 '5}@ M@⼸FK x Ro ||?~h휇 TaĮkG7(*V8C8y)dYbVj/8a>d ?hjw# ,,SYfGx4狠w>[yg4MQXC'p+V^u"|U{ۄK VeN`WLq*VTkAc`!^1dvJ9@BXFwD⪃4s6^) c f#u/= Y=n3Jou O 4C ~Mܜh30'*a+%E2бRE)S*7kV*El3\>u7ط]Eeq2S< <{Dh"n Vӑ9bET"4f~pWw74zj?]f},6UOL \Lt 1mVhl_O񾿹?(+n1;w0@Dș2p s (e 8gl1zxxD%JWU7\*)t2_uE~-TX 4nWB]=Y8@5/ R_>+} +&FTVRHm:p}y'݋TT ve߹(o/F`A,=^KO|<^y\*to8?3\up%"`^~TEU6/f*SH3+oC^BzgL%7h?mrxV;x??Px%QN \N# Q×A1O +3ɠ->8 4D5=wK!|]uK0 g(67BurG ʏo'8%ܼ?Ǹ/P.V8AL -D)Y DдYqxG ՛g{hÉw~CJaԲ^ S]!̤-kٶr~k~|tj[dM}V5!M3Lьv _޽3 C~őF½;0M- 18#bK﹫kVBC3M-_E&wx~pNjopxѼ(~q }`/C1$dZCg]eʛF-y.qV,өZJk Wރs^_M[BY؛쭒+:)@,EҳүX<m#OMHcPj@:ؘC%7$m-ƉA}1N#P:p JFZpL5a.\/2w]R)k..]PEBD9Hr\[l>pք41"{&^vu_{?}d<9)ú5Hf<<^IO>ܝ[my4i~y8AGZGa,`vqV8O@Ο]_o =hA"(s4T2Q|iK>2/e`b ]t]c|u >d:ǬdEz` kJuBD}^`o N8p ܽE*'pO6BDUݒj@,:my_F6A=:h*6ΗJ>.]Y@ָI Ըo޶77^?>2A d!_ܩAACs Y<X@k`}3n\ 6Oڞ@w^i3 ܮ`v8@X@R`YU5!AYeWGPg6 0DQSO5snPYe[dc*[a d‘ELw.(]oErH޸V:4TSJCARôdWs_ƟJ D؄JSV3xl|mH8! R` ICD zMM\#/}p.3bzMx<>Hp AV:M iljJZ]eHi2N wH1 6_pԈaypFf/B8,s6g?8QJ`m|v a' t9bd VlX(l>km0^/2y4ߕ \@D GޠVBՔ*k*n|Lux (*4paPF揖ehk.D`CFj Uayains^2G|uK1z<<[0n$xf¿~Io81S9D^y?!*+nyw-U)e vĄ1n <5Px׆,p'z- \'Sy0֘X s@}t}0'" !++)/R`aX̞ {`x%Q X;;Âqppo|xo,ufXy߱WPid8 B؎!CA8&Xˀ?8yr`6pSDŽeҗW ʜطƩB7_,enp0!VtS q[i9iV` ) Bi~m ՏnNqL©Da2hot}9,[5HK!oiVȒ qAaEs)mH6f76]RB.4o! $+~gzmYt/OD \ %]拢 %x&f!?ƙƯ 6.-9$H $X%1+|~-MlMX.0݃6/lb6D܍\hFCmK>7fy=vǞ:,W 1`\t$u^sX,ٿSaxgܹ6˚Uj1 FL{jc*2`BrAThnЕڶySK'Z)6%pۀ9`<nbJrjh$N,.$݆C/lo=(6{ slT a4lWҁ9cNj|p4 4\&>N&[gpnK S^aK]@f^ |  %_cYڵ;J]sù&ѤA3|?"j ylo~a_M]bԯ0w @Cr̗LѨ?=.R9X-,=^)5R֥̅Akc\|,1Z]vksAhۧ:1tD*8k)2MES Y9L%9^2' >u=(rc`b*HJ֠,5_&A4rc=|&A=L[xvy9̀ { $#,F#D/ba{@3nn{7PjBkPRHS"~n=^v]J T.nQ}( &/H+p$m^Pwbb.aϜWxjXGB-f[Ǹ\-2L\ ϠdM-=|X`cg?aZ qⵙꁄJ&0š@P(Ji(mOpÉBp( ZY_b!3,= |zhZ~>J5Ɖh SDqrN jؾ\G2P  0\Î+funأЈ QȈCqٟO,1|qS@W d |_,X5 ^2/![V(,a@3H5NS%?󃱞UA! *e\2*.0=P$3p4Dibx4{7#42A^QTj gBCqB M^6M  #Aғ ZiD~E{OԅuOOe\NG )pЎ:@ ?цl`Lc)LSfsT= ̂Kr}%04(dHC $$CA#@ؿ#(| xJQk3k:=„[{uňe}8R&ܸ@yZdȨyOi+2$dhѮu<@XHmxpK5PVPPZ}+0~@s*$ Qm;ds ͕ T( D#<a)76#F'/^`\b!0cB;BNH2ZEju6,qXV &Ȗ+]bpª8 ip/{R-ܞ_uDm)Ły8G#ppRRu; yIN8@HrbIR7& F A;!BLA xK>1#,,[kpyKI &${+5I3xEZʷG"ֳ5awr\jMu p䕟[ aSXi5:.BT#,4 OW! q 5L#5g B6^GD L"Hr UC.Q\3ϛDˬU4:BhΜAdBHa;+Hjw (1| Pi6/ q%$S E!;3Ơlj`c}Ƌ$Τ4Bpu?B8dFDԸoįa7ià5o@6`u*~YU ^k kS5LeWDDs(MO$P@ Ϲ0 X@kFS~p#li4  l='1'R>Ƞ3  +ҁQyBLh68{dd& Ȓ s-Eht"LR m/" $>`Gbw-୮wsVLc/ t!`xfϰ  ;g$ #7Q0rIC#;vLS FIj%l@I0ټd0uJp`3 ?@B.+aA `di+ 8@_}qw4ø/p+1|G w}$Qo:ߙPy+h"e`كuF6"Gq(b/e~?ӏy:R0eI$80IT.o昀&pJfJ>F2UGt:y a86lIL g7c6@I.g `oq9' ]NCp%qi\ۥ4nC3']Wҽv׹{!vQ%'e`4.a@xf94u\MvDЬ1d ID[Ll_g@Ev.M҄ aa4N" ="4ls{տ >/_plAGEa!S$DphY%brTЕD:C9D;<7e#q-we"m80L2 T!I3H 5̌[d۝ztu T}6BΓx#9$ C aWI*ɐ Z;`,~/88ˏK4ҩ3_F0 X`oc:sn Ew>K4vp8q+\u>UB~!-h%Ɖ80U@)d0Ce-g#b=$z;<5hE>kXDah6 fdhS : a򎀝7ptxk?g&36Q@0bdj'<*_D,ZǟB&AVFlO7΋|bx64%ݐj8eE\[Cg @kVŷݧ]C=pv1@<dj bFW\ra@cao%~)^EHbjL+*"Vs$0V i찤¡d|m]h<5D A`mv| 2uVṑbOΤWS”ph}[Ƿݧ H9TT a+.V2hoGOiϰ3B(qORe6%A'u@#@Eӟ7NSD SyE",ŖW!]'p ŖиL?xSH6 d[ SMvt)veH )Y/sm_o<3H "ӧ癚=vs.nۗ6[Df[%g)k=pY{kC6@|>k{y@1,tl`*imrFO Qb BžZ^壑 I$$ˌx?TR BiI.Bo~U$:"fV_i?6 kB`W[`b!r#hWd·Ky}< %!q$ZŜh^NA Ž1mw2m&$6Hk !5ۈL#P 6^3gf.֪ 0e[~2dCE+ˮ-@b嬮l \/Ɖ|Oе<`s;m(  v&V5#`k罣 x$By9vϧk2m]<}6@!ڒY _݁OI{z>76x?x~$ K |b]-5"sA7\'~",0`_E =,IEs;Kߎ` ]nJ+3(WP)`2TeHZ ;ѕ:x1X [kԤ5#"epjcpiHu"Xm1v[D,XMI"ʮ~?M3ehq%O0FY,QN4fϖؽ5:}ͮކt %)dH\ h?v3rĄ^sK |~ ͛>6^43gG.beFXi  -ro#LԱ]nڏMCFѶ_q{T?ū/P5Ρ+{"4%d?5R9 eJ. #D75Y3@]J./G[ֵʞQs=ޛKv\ɼKo F&(Q5Lif$plLȦ11a ǣIkfšC-Z,Q4qI\  htvof潷^5D?[U*90Zh)]dX)eNK-/m`'sqK\@<7U=gD5oP:7&Fް;ۯP@lYPnV l Rxo[8aGO h ״x"yY~pKc{R!JoA)ev59_?x]6SI۹RWǿlo\ƒCI$ъ+Dۇ P4Uy|;F+b/'h6o`4[\@&ԣmm;ȥkq\҅]E\7/߂ Qy;\1rͶ!+Zx8r)Њ QT9oDsqW71vIkG 5~k?{c~,7yE;4{A*n(״/r4W:#ߣE %|R䳡PP*|Þo@C Qp93> >yqpw3w;;@7#lI([{7Uخht7=g>y c VIW-HRD04ϐxUYb".%i, vo/)\+v7loGPй@b-jqY.UwNF5"A3p]/ܡ7wzA `ON{P:0";!t %E.Pe>O le"EN0y{$zm G`^tgA#7:N+:tB Z/S70pj"݈D$+y ;7> GU$\OaFlD߿uq4$Yk~|]ܿipe68+t_7I,dAng: T%l݅+{ZõuS0r9@%P EPg]hW .':,. r[TZ{@{;}WȡJ,"$m׷1 [H"*f.o:g Dz383:}d I'؁HDxmՕI[e/\-`TCaVq-{|1]Z&0_B6b$jExi;4HI.gp_o_ݡ7>/Xk ib*jx0}+>|}M8「  r7KWdԍ ,D8 #3TMf2G}h/3 1Z/w.""0& qk |BfףHXb%Q9lm񼪴0G?;d'dUhYu;P|( 2%.v o ,%CȜv:$M\l@[zYVMɘYW] *]1wZIXXe۴uG_lr lj:s(?g9"]/Rtvxw3HbBt)}>-)=xb@)1V|B/&/5ȇo0[8 )ٽv'T)z 8b9XmLÉLߥ.K%s{yIc?-8j3kSl3{#lҌCʼn_;#[/%Q 4ݤױudT6``! ̀$Hbtc'5ٚ?U/\v)\1?##JkIk1X[¹< jX xjJ3R,H 86 $*lrp6a𦐀<;*/.Gaπ@e(GRx<8Myљ1pjVSbztXHAXY.:ax%9 r6/N3D5e913K _M830Ol^Z31w(j@0%zB/[J;cDnoۣ:Be@:ZG[ʺؑ #`&@'?Q26/her< )jaʡPriHI$I %<1ܹc.C"RVP{gNdBf.W`%sK]o)„uP ;7ѹP@e(;# R HuYhY{yɍ2w8ṿ{s MX<{Z[=utjaג%I#ta"f9V~ǞV|Čd+;ZN45K@Пt_{N-@q7A$N2oca#+´Nx}{ ‰r.!_/+uȸHc`` KgDX k5w%mV1*߫$`!v$RL)XLSf)`tkrgNS:X_ApجF Z{ǢR Kp$aay>mQΟ:;}o] _N1ߜerLc3*׵r#ARӫ3BOR0\GLڃGlz"Ŏ''\lK awJIM"PX;ρSJ."Nvr'i';ܞٕ+ۑE'K %P VM `u>A;Vi=g7wYL^bǙ7-DPC^mQw2 !'Xo@ݛƪ۱.3*N-=w>OKk agJHpd$$%Pn8ZRMvU qqB;7js$};KklteƮZ<~K ݸQކ̀ tRx I@̌Z\>fClLvYڢ %0'JM}ƾWNy![~J'262F- *ZbAI#2LqQ(lPe@v@QZ\'J/.i%arv+_W{] /ۃ[|KN0t8 5' YRLz'"ldOsv3 {R[tK1@`+׹X]Ҫ˄9k"GxPI P(m:{"v(@zCעT6*6}^h~t`P v}_@8|l.(sksc \*`ה!Jh˔V?1 o{E`33ِ 58PNi'$S?cyΌc9Ҕ8Q0maTpki"8E{g\+S jJmjƬCH? 4̘HB_1k~`>:Ƨ#$/q`O 'H8 B?bn j!mʴl}MV@MP(TI۽;xܔˍlQvtЊ.ی"OOR=%(Y7 Y~4*6\ԡ}C!TD4梈Ik-zat gv fDՔpy_a5сs 9+Ă01v t٪7M|Cw+ <e@E *7(<dX(Q7J>#_i><;Pw.w忼w`:[ TQ rԦ M]O [YSp[b:XJuyг'lfl!YCbvo% 7yn"S Ў>j)Yx a6Qf(k*,ȨDK*x`!B1Da~dC8B;S7O2E{_§\: >uO<Z_خlh O7. @oBxa%cBDZ) 3P#!w $QB嘰CXNs<6VR*GأY] v!z~4ﯡ,M'R^o0 E%l ,Pw-XhG-#'_x14|yJ`e3h5Lohz,i``z<eG7*,$JLXJ-5C [x&H0~I`f#o8|gDB.KC-`9?LِSIMMWMP!e >#1;]E֒NB@%Nk1B'2౧NkG[RxC5#XT4Q/&?ԕubS{1 d'cSWk0.S@RoʕD*AnfsxnMG\7,I J) ÜgtqO \':CaԷG+ӐSZD0Mz.Mfc֣#PEy`f;PW[hQ{,}!x{¯6-"Wc^aitb7dM e&9)FJo*lHi`+"$ 1@/ B[hJuDC.(Jdfqpߠ>v1 `o \@xwSZ-*eX@3f_,]h=!@@@ ZM!l;>/%|i+>64s99:GxCUlM ^6\Luؒ3˺ "{}-piKlED`"`9!$X;Mؘv"%P8 `%A`-!d |=$N v#9eP؝/=3؍a) kN%h@7Sge\y w6x"8E`;E'Z, l(DЉEɀǏϥ 9 ſ~"qmH—Z=H/;av /.7pn60·^-^#wuכr`^Yޡݖ uHۆ b\|k րCV ov{`_VJ&C\0A F%)u0X fu*;{nA#\ǥ)T.T9C=>SN0Ӂk)&KK"0s-|ne:cu|38\ `C1Tuep;`9EߨE.#HBqAB^7w6(’}nynLxI;UoNmX<*pLXg a e6=G@Wj &<9dGd[Cd13ҕ>nrD݈tF>2f,?+(ǽFM['Oc8RfcGiiLFwZ+{ΊFv9/-/RyH tűrL'jyN ׶[YWJ )-3Spdp|xr81r"`WK@G G :_Q/؀dv)U^][24y u#rT%I ȢLKU" !\#}]+8 On-|~|}cO~(>8{~>/I^'A׍1K0׍0׍ĘkGu"V4f2b'`01cPasXd8Xf8?PUn|n9(5q=3\r(Bz]DI']_`Q) NDvY*oN,t;EI„g&`-XKu<=b) c)lV@G>ef ܐsVbP[Ji[y`9UYr؀ V[Hflgw% wmd`}uʽ礏\CXag3Wfp°[ !nvZ~_ԝ6; rOk^*A;ѥ#4h2"",6a_G9po jYP>,-b"e&+%BݺyV u~wk_b|ZxVE}ϺB7t1OFs* y^&;֘0Ƅ+%@[#si/H`f:1݊qk 8*s!7jFetB^|R4yr}g='O5 K{D"QEB g|M>F83ms֓}sB5T$-:~9S-LY+kAR ;qFy$y&BNlGgLRʎKl@Wh 8ubzYO^YTUɉėN?9OOgEl7" d:A-t>h B3(Z{ h r}U BN>}k)ZKN7Xی piiAEb-6@[`g[O_'|}*-mSЗzN@bI}o\:!mC >:䪌.)Z5|'Cz_~vl'E$=sksG< DbjSZQT-7 AYZ,5]g4#;|󜎆N/̶sBFD܏q:ƫK!͔݇nyyXMe7fsr0f0md~_}LS)~fSG7Dxe],vde3 {ڄT_ΨAS`G2irѮ3: h K'#}Ir:]-167ʌGKčY:՟uXuuV)|큧ן{|lݤgr\G6DheB`Ą]k&>^y»'jcZ@-7 {mz1g-l޿~Ǹ`DB#$[X 3:'Y}φG&/l;cӂ@C+qZ)PyXxA4*\.y S|E'$B+>}#}K ̌6g67xYMlX=QڂL[Ml|ooƲN~약{77j6c}^tS|`*@*X3#5=vQ"BkH V8vle+5@D@:-`4'Hv&dµT&bZʯgV_aMƾXH9+.33+s~po?-h|RUx| AAH t!ɡmwK-H.Dؿ_XCOO*m8g_׃n冨k%B,4Oc8Z|{Ɂmk\6[jJ޶|>zNbxX<;]*\)i|`!.u!lcN8Ji5?mǨ 28mhj8/>u~3>U'`0>}e2\~wD1}u7h/L) hެr]sqtF:IBÚj)b3^+벋s'8qtDݻطer1_el(,Ěq9)lY0i\-˂,`&xo\Z rlȬ)2zk6[f%ؽJ{:=j8|yiNC}(2NU6IͅDқz)}8@}l-M 5dTOZ7^Gop~/n3n>JtM]p֠a ڇU0Lp KIس=d`$B+ v˩8G8S#$ ˱]A lCOPPcgN!( @e&`uXq*/cbv,Z3\djy]pycO1*P(H"ݔI"Կd7psN@HYl~!|ϮZ@pj0XHc4ėkORCP/DJӮ{Laƿ* H[Wq:'ީ(]m`Pu1"4ÈI;jUggߙG@;b0DESxl O Ǖb{A$.Cs|Zk9?osK5)pZsI8Dq Bq6r5!vZkPwDfaP(Ga{aFrBb;V!@}[jg^or "+@3 9qy OD8wfG69{THΕb=QUPzяں4"R;wqP"`G,-:oedM4݊}îaP.&`DP>}B, VZx'cxRKg_x\G(5HDҮ^8ftNd6VP̺0E/@nJ/, fxŴaPӐ5r.(D碣kE9FOcUzjsg` _z7?8+ja:@SB-9'< -KA> 3p!XDi rΦh@*Lda`Rqk5 j{.o׵G5Y@hi jY}SܯKjYY5BqYMPZNSJss1v;c1 tuy 3:<J 3f6CJx@eCw y=wk| AnB\s>F7d 29?҉~huO>t||,8ΦbT^], %/9T>EǏ;sE/; 4@뒂X!fd0o0v./p%^{0^.Gbߨ^{s{1`u-Kvm8,Ɣ{M҂T 8 .uܑٛX?55,~߯Ǡ E"dW")!2J̆pzN @k\Ⱡ!)t ȫ_(a̢-}USêM&x:#ݽ2E5#\b_LJagr 4AAJ%GI"'Q*ђ_x0fs,,П}W^6ˌDXo'H>#A,Ece=3%MO>+d!d*%S$"O+?xYcX~oP>TRJȤSl2&2)*’GeaSYq=g+) C@nSJXZsj|SP}WK4#kn܈h؆Dѿ~>SCY թ ~7\["M"0+8%5Ai.XЏ- a3\WgaR5#vؓa7!OJL 1k!Tbv<-XOush1_<ߢUD1DS}ٹPB;{a.i0 [O:s =|GQwz}K[UY @ьuWtqhI36r|-67%g5иΝb2Ν"`u.E@ktVE׻^Tavrrn҂͛NdT®%X3 Ox&ARશl( Gϰ0q-&UqYbL'ADarZirR*nwhx'+Sh[0IiFGU)`Ŏ(:zˠ}W i%@6 Rt܌l!+TybJ 6c Eܼbe A"0Cq#".ԉL--Џa<8 tIpev3KfZ:}0-Db\UJAf9h؍0߉*/wy`{⟂NO=ϴt J/IBܝzͦgB,KQ v7хvYy!J ,n91r7 ڗnn#1>_=2KFtlkc396$ H'K-t{sJpS/ }Ce*az+\"$ʓ8Z Fk)!Cߜ5Xԏݦ+;zr|kưH c .#v3\ r%hrj|"(;OMw~*swoR+OeatNV^AI(mBBmdsxpR7ߠf'y0XBTtp P8 M{?p][/|O蛟I# 6&Q#T,xjDWڈ逆&]/6L aq"&*l7h˵(ʆ,~'B XcG*cPDH"qݚ-za5.JiRI81>~qDI+ \X@z40jEN]~sPyCIR%`ۂ9c4GD[.>XŜSגsS+K%gY|v"u_FNZN0J i ,%/Bf"r`a@;_|n=C+s@xa20PCPt$-ƧO4H" +#2՘4Ў>B1"Ý+`3 挍1#' 8FARla9D>@q"jňiXF H@;"h.7X>~F&XGjEuA`Z04ٓ][0r6b,Y= -[J+OO>wڲî\zpm={ /e` {SOE@S8A/F}D&mS}C=>X6ٙ 2oJPq &hLnNAJNL,qi a?D,9c}pr#DZu9ѹd аB@g gm1`2!H)N8|1iΕ~LBL5 xk'Uqf#w6 0l.;#qPXщ% gw$GІL{L}uPw{R jj"6ʀVJA'qT0WG93AA! #YVް))6.4-Sǿ=sj]ûO mG_OTzcA`l]f ʀ~apN 놪`D|a9hc*Sq/Eo 4$300iE`KӄKQ⮩yf:pFI}3Q!Srv4Q%ҍ7;JKdcQ5-.Ǐ.j Zגxj:_ 0e5ފ+4_0ft$%AD K|)-G!39:<(G3?ؐӍCmBC@~?XLkP>. ps#NLp7묔RNHw'q01K7?Dd"Bu'g. v 5`cƈZ{T.3GjY.8Г/-˞2DK2z۹p4=BZSQoҐ=pLXEHP}/aA+26 DA淧/mX Nw@=Es jhG6A禳GM0"3<#Y4N)FO $#Z)H;lϓ0 I'1@ԓFG%!uGXJ#K-On MeGSĆf ډ^ ?uDAgHPppxߑU@i:u4  `k@1ڲG2g_ΈH'&\b  '#WTV\~cT>^m6 ؀<țJ^A |uYFJi ^87'>7c:9!U\*U{3W=\ 5о>ag)ckQޓ͑UQA@mUKP$ c  ıDMI,)X7Rʰ PA3-)J@ 0:ݻOA{D5~.nvb`=43ـS!^XY%@@q:|'*kԾ헷@hX6Ee܂HRlr]}J`*]j/¹B1C\_7g%=Y|CY3bscFtS Pbc#R4sD_ `*+NK,(\HL`V^(M\ Jh$dAFG|,vm[#$pۅwTy!n%H{-Xjhс6#3F+(!I:SZM!vjw-sob HcMvLVr|#6'jAmiO{o]BT0,gz%DUm_E/J, K W) {" ŹVJ2@BA rs듘gEcz@g]X&%);9Eݔl&_0&de$!%D, N;bn-cWega9QtNr|A\ӥ (*\8b1[QUyRuY:.9}`bⰀwte9mw~ -YjCA>܄'Hz ӆCd_taP $$\ї}tTx`86Ü6 ^r|0cB^}]+!Tup(^llfguI%C $DXZI1ߗHT8?b`M}ܨrcC@!-{me])Axr"49u6BPa+~ ~\<(oS_,^W}Jpt}_g=œQ@E0K58Af"HD "f Sxu㪭<~ƱWMtX̀( 0UiBb (]u{џOp|VhI e͝[@ѫ IW/Fm Y+VPB>VL2Qg\QaaR*qGZ~KAK "x?#0n4~OToĆT3Tĝ T,QV\Yk3Zoѻ+VZ1?-?J;s 7ޏ bY0k5T;bFw Y PԎL`;_Μ_k5J\}M`m-Ʃ3IE rƅ$/4O?'#a(\q\ *\|hЫ0db=raqVi03rIP eٴ$\/K@p]j*z`7f.AH &|~= _ `R`WjMFjk.d)L;QZ髭Dp>A[LH!z#FoWjF'#pKpNEO_ȍ::<_΁ ;d'_uQI'RƲD"VVR(&<}|X i*D RuKBG <+@9'hjn"7\!M3LF$7W8.fr`+&0r^4A4m Ȉ q9*m\0҆ܟH|;*$CuunSRtWH؀zZ!eG[@>)b@/O"2$DdP\$ؗ $pL3nP~p@ fĩ¼DƄoZ۶5z>e܉u0^ɐdi0- Rp2w {'"M_(O%酊X%-Hň41& HjH{_T㒳pF" pgiV '{VNlӝ]]Bg9İSZyK}V¼}g~gy뷾׾R  (o4ā&YqH=&f}qKq;\Sx@ }e pި_SR_/4 Lwρ):K@e(BPjڏ H* #~cx7+EXXͷ@?NQ%&wOJ!f l}4C8W RC.Óv; / Ax-}E \9jІ0+o+`EUO4b^ +(ٵJ( m/~| "n?)ɱab6ęW;L7W%:pr)$́D~Y#Pɟ bZn*gW?{H~(x7w&QR!⇘A=' ]yB8A$)J>ZI=y.3hDG:X+4D8M:(Zv}Lꯉr\̛ӓ((ELT . UzO0TU 8#Z=8ĺ v1"qn~>̾IHuc@00dRy SR:fӔB3=D`]ea*ku0gԗގ4@P% ) W8MZt%A(&^C= A?6 !4)wތo^_KFY7 ʽ Z$$jNEʿ}Co$z[ʲ#(BMLq/p*:3R鹿dJP@Qd(9RL؟(L'hOXЖa6+0GTyY@"BiϷiݔYc\ʷut)JrJLPE F}Hc񺆅fP+{1pbA=6޶~cvKBPJʁfntws O9t 0Y7d8-?28xA>)tv]BI߽-zͫ{Z(HI$ c6f`H%7Έ'sv|J;-x'B|?;Y{R,@M5t31'J`ODMWy ']ĩ-ɠ7~FPE)v)I #O~jHO*w]>'>:rH=$)rDdy9K77= rl^HȦ1z @lM51dpp^_rT U7o (2 Dw%N$b-(~ Gu)DG/|+в GA= 堨yƷw\P8[bR"Fo=tQop՟0l<^ K9pg;‘e z?{(f; J=s%E~Of|` ]܂8ፋZļ7 $u=N1ya " 99*j9B_$c{$N$]?6[S `B&((GLB$*s" 3wjgvtfm *}@Aխ nx =gvㆷ6F{8+o8gVrdN JaMoȶ\x0#R-M&]@R.Lk5TM@J3%"l(qxm-Xmf(' l"DGn}W}C܂35+Eo_9R`S(b1/; ۏЙ_zbZQX/t~B )pYhA0%Svb &!F" :e *"l$)G;%b\Z) W\@aw_3ߌ& :q>2N}e/A@ǐi7 `T5$E6ޘ Т#Hqm7D\EzYvͯt I,.0M{Y(67R)mmn Hc.8@*QtOL +aHHHh+/؄Z[dd;8MILP8mBUP_2׃VPP*] \$q/ #SLg&^.1[VXȢF @>^ηѣII"t3+tWs=wܻQ*qU "\q둃O9&_Hrֻb };D`Yvqez z]fxB/ytٸWFñN(%Jec@Y*LMA(RE4{s-.U;7pV#-~FPS)HQI#x bCn#%uABPm~]LK6AhTQG=+F'^Bxzx0A&N?maQpkn]^qZ`T@JR<šEx7f!Z, d9&Z<.ގY|da5ӄ},`3`KܪGoỈ%3Zth%UrƏS X_0 t‰w br(_I"797o7tE0+tJUC)}Rּ$|v?=%^ Ilvu#%z9eZ[ ^f5{ux_<D^_o 3 ok={G5-5PdjR,p-`nALFJF5 zMܗ޳B<߮!w`hG dedFi:B)WN\y!nG{k7 \ߘ`'!-XI2sHrg>YE^d\ JkTK#V$ZpJCݒ$m G&&P L W:s tOF u,䲿 UY9E{Ucې%y&񱈐Gp,YjEqý"̩ PmmTo!#Nc v+_JPv)=*2ل>*,t:)n?ŕk)Μ/pSh&<2@٤nYoccü Bk11BO"vPKQ*F<%1 ;XҐb\qǠL&Hzw*W޷^APi T;0pֻ-m:['MAi1G\ktᤒsx(U(Ys 1gau'7֣:2sN0HR7Tb3BXi6yOy,`hPƮaV,WŴT8'| zY"!֩ KdAf=1)2}\7i')8r吞$ӛJ*>tơ.6>'1[y /%9jk..4N: "]]j-#AlSC[Gj_?ZIi .tE ֊pK7"(TGRDSճ?L$HFMKA6'sƀ"|[q"o^5GFmtRKprUbo21A $/!Ze)x^6 Rl>P7T})_~&WEw7Jhh`TcY ԽX]Ck$v 7G[{BIg1WZsHN ?dԱHTɺ@՗Չs0 Vp H]l %zb'1Y~vw4ZunlzVФM4HQ3Vu`dO?6-Gsd'Η RtDK Ux)89X>_:'h UkleIP EEy|7,hȺyـD^9_4܀6= [;'h7Uy8hKoV~;.'96rJpk fBrj ōSl+C& ~tb$~ #Wzvߜ7؃7b#8A0DtC륟Qp~ß=n&0H3Ad=k]D7*Uiw6nj^yvxy)JЏ0x1 t|۾EXҷm.@Ad]ǜ6,W&HUz- Dg_zqZƌ;0#ƺ AEI3}; R9ʣL>~+&~R-%Hg04aj j{x{R _]*t@O_:@b4Mc3 SE!>plYhl|ѐC'UBUwVB(1+n_^p@*LMnQf?X9;CrX8Ht5Z#~z=íG2x|r).pB-nҾh ,5Dsv6^@?HAĨ/3=E*<г E:{$=\s.FϨ[ bak5%"d_J2 ov3҇i8 ojB^aY:a@^y7b.[! W>Ta`+owS©`!<aΟ mj=,@ &xf}x;+./K+3,u֖c`ϚsIOz8us=x#>'@}+08'a!x@A*p`Ō;aջp:4# AwHqR 텽w1Lrl P"MƉ0ɧWHq&Z 'fEk9mnƦx oy2[heɘ 6N$[^ߟ}:@%Gb5oa2f4u1wqĒi1_ Bj04[^ 8reI+u,1%(,4z AX `7 0餄 n>a |L,'v`$њ ?=" B3AI[ xǟu';9l0gh ))ᄆGᅯ[SФ#v|mo-f"# 2c3]3>mD:?Z_#܁fPǯV켆~;a3['en};.<&qӑWRL S=';GU @s:UY3k$mڃ؆°#Zs1D1T+hL!q>ߑ`~!j6⾆M0yI>~-kc[>hWo9pHG@ +{r}wi5`%uPQm>\dE2")Jq`( `5Ixj ^!CUfI][qmAPDy x -VC![zwSg;Z`tP%> n`Z3 5 ;CȬXKtzVvEGt73o#LB|G7-E 4'_k JϴX !*O)6Jpd97.V:YG zy؅g^vg}me05Zn,?a]X־NJ,C??^}S><`{3荁PHF/F $! "m_Dy7V$f6e .8psYLqj3ř%J6YB HJG+ ᛓWc[/\!bzga B0>O?|"/dR $\RT!?צ֋p+?# lq#+:?P[s(fVIDI:Eʴ嚳YQZٵe06  =B1p>!lg:)ᆵCmܦ !h/4C Au&`Z4`!_W*iIoėuo@.ڍ^М|3/$M6@ݏ~.Ք:Ⱥ1A2]2*]EC+;B(ޞR#eX 2 _fkNm!E8DoqZgEϙm]0y`q'tcHd9N&ع.񟱇|F7q ҁHY<`)#_@J+?ʦxvUɄaj#Us `HjVa2D3@Ey1Kl{CB匮z^9xE%}z>q6yz!mۀFLx7-I72 Fixe.^aU1M_`0ps󏵁}di Pz)㹷c!0[d^ꫵĵ|rr `_$F Fy ]U+A*>/t֪q}VPF ^򕆋 3m`C™yctY ^] }?g.zUqG]4-yKB8pSuޯ<޲(I AeMC\%s &wmeHGW(C)Ƌ/k&gυ@'~໵{>`7#_sRO:0W?ܤpu ?HV`dԊ=K@խ Kͺ\%<;\r[' 8`sD(KxJ@( 0Q9gu13bZL6D F/c }|& xtEhT&a-LƁAb`\h.ToID<`AsCRh4LyfR`},qxI%cD@(`%;ì zyY! R9O/=QǛ~;PÄcΉnDcHeD8 s]9#(-ekDQaڏԓ84Jax0%־A9 pQ rF :r&VԵr"\C-^{XhX4}hA/&ۣWH;2yN٧ Ɉ.7`W9Xp=E`sshzzRy,3p m!-w5_^=#>㑟p䫏vp%XȐm#NdfkR6\Ry\0@0T!$ؼ]&6* :9<=?GfVK1@;Qwջ(ǏTP G1\j8ۺ%E(Q0hʍ*§6gf-Q&)t](\݅&"(( xB~5w/,o{!zc3гysW` F(f.Ah[pZ7b 鳪UhSp<!KK=RWu4}¢՘#gmĀê|$a/}<<C,/2@c@ 4QL^\wY,M1cƣ4(e"ah'`ԕ‰HF &ʎ.dVW6 ;+!ދuew<Ϗ7G׷ "s]z<' @A @ޖK̼ˆ{ DH{IGpn1IaS 13nUTXPΓ-πA*HUx`$ݔY ]~]./Oͽޛ@x 35"h_\b,.eKo7 8CA7aHրcMF=,Q!ʔP0PL_oMDćU q}=Wחp~Ӏ>$a@#I^ȢV|T1.(}%n'M/K.npw7o+Y t#C] .`V.L+y}cl 䙂\zf>+ &< D%>fSW߻.ʗ %zroSN WURX/'~L 8c)2b40& D(58KDg@C!O&=|?^~^AIENDB`ukui-screensaver/src/assets/scrollbar_up.png0000664000175000017500000000016315172041035020257 0ustar fengfengPNG  IHDR;֕J:IDAT(c\XMr5D Ӡl54]3F4>)4rNGIENDB`ukui-screensaver/src/assets.qrc0000664000175000017500000002042315172041106015571 0ustar fengfeng assets/authdialog.qss assets/show-password.png assets/hide-password.png assets/switchGreeter.png assets/waiting.png assets/capslock.png assets/combobox_down.png assets/scrollbar_down.png assets/scrollbar_down_clicked.png assets/scrollbar_down_hover.png assets/scrollbar_up.png assets/scrollbar_up_clicked.png assets/scrollbar_up_hover.png assets/unlock-button.png assets/login-button-hover.svg assets/capslock.svg assets/login-button.svg assets/unlock-button-hover.png assets/unlock-button-pressed.png assets/iconFace.png assets/iconFace.svg assets/lock.png assets/logout.png assets/bio-login.png assets/password-login.png assets/weather/68.png assets/weather/67.png assets/weather/66.png assets/weather/63.png assets/weather/62.png assets/weather/60.png assets/weather/59.png assets/weather/58.png assets/weather/56.png assets/weather/55.png assets/weather/53.png assets/weather/52.png assets/weather/51.png assets/weather/49.png assets/weather/48.png assets/weather/47.png assets/weather/45.png assets/weather/43.png assets/weather/41.png assets/weather/40.png assets/weather/39.png assets/weather/38.png assets/weather/36.png assets/weather/35.png assets/weather/33.png assets/weather/31.png assets/weather/30.png assets/weather/29.png assets/weather/28.png assets/weather/27.png assets/weather/24.png assets/weather/22.png assets/weather/21.png assets/weather/20.png assets/weather/19.png assets/weather/18.png assets/weather/17.png assets/weather/16.png assets/weather/15.png assets/weather/14.png assets/weather/13.png assets/weather/11.png assets/weather/10.png assets/weather/9.png assets/weather/8.png assets/weather/7.png assets/weather/6.png assets/weather/5.png assets/weather/4.png assets/weather/3.png assets/weather/2.png assets/weather/1.png assets/weather/0.png assets/intel/message.png assets/intel/more.png assets/intel/slide_unlock.png assets/intel/qr-code.png assets/intel/wifi.png assets/intel/delete.svg assets/intel/pin.png assets/intel/wechat.png assets/intel/back.png assets/intel/cancel.png assets/intel/qr-reload.png assets/intel/icon-wechat-noqrcode.png assets/intel/icon-no-signal.png assets/intel/icon-wired.png assets/intel/icon-wifi.png assets/intel/sms.png assets/intel/phone.png assets/intel/icon-backspace.png assets/intel/lock.png assets/intel/logout.png assets/intel/shutdown.png assets/intel/reboot.png assets/intel/powerManager.png assets/intel/keyboard.png assets/ukui-qrcode-null.svg assets/ukui-loginopt-voice.svg assets/ukui-loginopt-qrcode.svg assets/ukui-loginopt-iris.svg assets/ukui-loginopt-fingervein.svg assets/ukui-loginopt-finger.svg assets/ukui-loginopt-face.svg assets/ukui-loginopt-password.svg assets/ukui-loginopt-ukey.svg assets/ukui-loginopt-custom.svg assets/selected.svg assets/keyboard.svg assets/ukui-loginopt-smile.svg assets/ukui-loginopt-lose.svg assets/01-default-commercial.png assets/01-default-community.png assets/suspend.svg assets/shutdown.svg assets/reboot.svg assets/hibernate.svg assets/switchuser.svg assets/logout.svg assets/lockscreen.svg assets/fengmian.png assets/biowaiting/iris0.svg assets/biowaiting/iris1.svg assets/biowaiting/iris2.svg assets/biowaiting/iris3.svg assets/biowaiting/iris4.svg assets/biowaiting/iris5.svg assets/biowaiting/iris6.svg assets/biowaiting/iris7.svg assets/biowaiting/iris8.svg assets/biowaiting/iris9.svg assets/biowaiting/iris10.svg assets/biowaiting/iris11.svg assets/biowaiting/fingervein0.svg assets/biowaiting/fingervein1.svg assets/biowaiting/fingervein2.svg assets/biowaiting/fingervein3.svg assets/biowaiting/fingervein4.svg assets/biowaiting/fingervein5.svg assets/biowaiting/fingervein6.svg assets/biowaiting/fingervein7.svg assets/biowaiting/fingervein8.svg assets/biowaiting/fingervein9.svg assets/biowaiting/fingervein10.svg assets/biowaiting/fingervein11.svg assets/biowaiting/voiceprint0.svg assets/biowaiting/voiceprint1.svg assets/biowaiting/voiceprint2.svg assets/biowaiting/voiceprint3.svg assets/biowaiting/fingerprint0.svg assets/biowaiting/fingerprint1.svg assets/biowaiting/fingerprint2.svg assets/biowaiting/fingerprint3.svg assets/biowaiting/fingerprint4.svg assets/biowaiting/fingerprint5.svg assets/biowaiting/fingerprint6.svg assets/biowaiting/fingerprint7.svg assets/biowaiting/fingerprint8.svg assets/biowaiting/fingerprint9.svg assets/biowaiting/fingerprint10.svg assets/biowaiting/fingerprint11.svg assets/biowaiting/fingerprint12.svg assets/biowaiting/fingerprint13.svg assets/biowaiting/fingerprint14.svg assets/biowaiting/voiceprint4.svg assets/biowaiting/voiceprint5.svg assets/biowaiting/voiceprint6.svg assets/biowaiting/voiceprint7.svg assets/defaultbackground.jpg assets/data/conf.ini ukui-screensaver/src/screensaver/0000775000175000017500000000000015172041106016077 5ustar fengfengukui-screensaver/src/screensaver/language/0000775000175000017500000000000015172041106017662 5ustar fengfengukui-screensaver/src/screensaver/language/screensaver-en_US.ini0000664000175000017500000000561015172041106023714 0ustar fengfeng[1] FL="May there be enough clouds in your life " SL="to make a beautiful sunset." author= [2] OL="And forever has no end." author= [3] FL="Just because someone doesn't love you the way you want them to," SL="doesn't mean they don't love you with all they have." author= [4] OL="Happiness is good health and a bad memory." author= [5] FL="The best and most beautiful things in the world can not be" SL="seen or even touched,they must be felt with heart." author= [6] FL="I am not afraid of tomorrow for I have seen" SL="yesterday and love today." author= [7] OL="Enjoy when you can,and endure when you must." author= [8] FL="A friend is one of the nicest things you can have," SL="and one of the best things you can be." author= [9] OL="Only you can control your future." author= [10] OL="Don't let what you cannot do interfere with what you can do." author= [11] OL="Seeing is believing.But is it a truth?" author= [12] FL="You don't have to see the whole staircase," SL="just take the first step." author= [13] FL="Start where you are.Use what you have." SL="Do what you can." author= [14] FL="If I had eight hours to chop down a tree," SL="I'd spend six hours sharpening my ax." author= [15] OL="Actions is the foundational key to all success." author= [16] OL="Refrain from excess." author= [17] FL="The longest way must have its close;" SL="the gloomiest night will wear on to a morning." author= [18] OL="Sometimes ever,sometimes never." author= [19] OL="My angel,flung out of space." author= [20] FL="Three thousand,see light is a cloud;" SL="Troubles are many,would like to open is a sunny day." author= [21] OL="Everything comes full circle." author= [22] FL="In delay there lies no plenty,Then come kiss me," SL="sweet and twenty,Youth's a stuff that will not endure." author= [23] OL="There is always a better way." author= [24] OL="No act of kindness,no matter how small,is ever wasted." author= [25] FL="Laugh loudly,laugh often,and most important," SL="laugh at yourself." author= [26] FL="When you want something,at the universe conspires" SL="in helping you to achieve it." author= [27] FL="It's the possibility of having a dream come true" SL="that makes life interesting." author= [28] FL="Some people don't belong to you," SL="but it's good to meet them." author= [29] OL="Miracles happen every day." author= [30] OL="You can't change the past." author= [31] OL="To make each day count." author= [32] FL="If you reveal your secrets to the wind you should not" SL="blame the wind revealing them to the trees." author= [33] FL="I love three things in the world:the sun,the moon and you." SL="The sun for day,the moon for night and you forever." author= [34] OL="In me the tiger sniffs the rose." author= [35] OL="The longest day has an end." author= [36] FL="Sometimes accompanied,sometimes alone," SL="stay awesome all the time." author= [37] FL="There are no easy answers," SL="there's only living through the questions." author= ukui-screensaver/src/screensaver/language/screensaver-zh_CN-ok.ini0000664000175000017500000000720415172041035024315 0ustar fengfeng[1] FL=世上的事,只要肯用心去学, SL=没有一件是太晚的。 author=三毛《送你一匹马》 [2] OL=如果你是大河,何必在乎别人把你当成小溪。 author=汪国真 《如果》 [3] OL=世界上有不绝的风景,我有不老的心情。 author=汪国真《我喜欢出发》 [4] OL=人生如逆旅,我亦是行人。 author=苏轼《临江仙·送钱穆父》 [5] OL=行乐直须年少,尊前看取衰翁。 author=欧阳修《朝中措·送刘仲原甫出守维扬》 [6] OL=行到水穷处,坐看云起时。 author=王维《终南别业》 [7] OL=仰天大笑出门去,我辈岂是蓬蒿人。 author=李白《南陵别儿童入京》 [8] OL=天生我材必有用,千金散尽还复来。 author=李白《将进酒》 [9] OL=希君生羽翼,一化北溟鱼。 author=李白《江夏使君叔席上赠史郎中》 [10] OL=惟沉默是最高的轻蔑。 author=鲁迅《且介亭杂文附集》 [11] OL=虽偶有轻风细雨,但总归晴天朗照。 author=史铁生《记忆与印象》 [12] OL=不能走远路却有辽阔的心。 author=史铁生《我与地坛》 [13] OL=从卖气球的人那里,每个孩子牵走一个心愿。 author=北岛《代课》 [14] OL=要有最朴素的生活和最遥远的梦想 author=海子《枫》 [15] OL=雾打湿了我的双翼,可风却不容我再迟疑。 author=舒婷《双桅船》 [16] OL=拯救地球好累,虽然有些疲惫但我还是会。 author=《超人不会飞》 [17] OL=宁可一思进,莫在一思停。 author=电影《一代宗师》 [18] OL=让人类永远保持理智,确实是一件奢侈的事。 author=电影《流浪地球》 [19] FL=无论结果如何,人类的勇气和坚毅, SL=都被镌刻在星空下。 author=电影《流浪地球》 [20] OL=生活就像一盒巧克力,你不知道你的下一块口味是什么。 author=电影《阿甘正传》 [21] FL=也许我们这些聪明人,脑袋里能装的目标太多, SL=所以忘了执着。 author=电影《阿甘正传》 [22] FL=我并不是每次吃完饭就看电视,有时我边吃边看电视, SL=生活中有些改变会增加乐趣。 author=电影《加菲猫》 [23] OL=做人如果没梦想,那跟咸鱼有什么分别? author=电影《少林足球》 [24] FL=世界上有一种鸟是关不住的,因为它们的每一片羽毛都沾满了太阳的光辉。 SL=当它们飞走的时候,你会觉得把它关起来是一种罪恶。 author=电影《肖申克的救赎》 [25] FL=你每天都在做很多看起来毫无意义的决定, SL=但某天你的某个决定就能改变你一生。 author=电影《西雅图未眠夜》 [26] FL=把人类看做虫子的三体人似乎忘记了一个事实: SL=虫子从来没有被战胜过。 author=刘慈欣《三体》 [27] FL=在宇宙中,你再快都有比你更快的, SL=你再慢也有比你更慢的。 author=刘慈欣《三体》 [28] OL=人间风雨各处有,何处不是浪浪山? author=国漫《小妖怪的夏天》 [29] FL=我看到那些岁月如何奔驰, SL=挨过了冬季,便迎来了春天。 author=《瓦尔登湖》 [30] OL=保持热爱,奔赴山海。 author=佚名 [31] OL=别慌,月亮也正在大海某处迷茫。 author=佚名 [32] OL=当太阳升到最高点的时候,影子就不见了。 author=佚名 [33] OL=大直若屈,大巧若拙,大辩若讷。 author=《老子》 [34] OL=博学之,审问之,慎思之,明辨之,笃行之。 author=《礼记》 [35] OL=你要批评指点四周的风景,你首先要爬上屋顶。 author=歌德 [36] OL=只有流过血的手指,才能弹出世间的绝唱。 author=泰戈尔 [37] OL=他强任他强,清风拂山岗。 author=佚名 ukui-screensaver/src/screensaver/language/screensaver-bo_CN.ini0000664000175000017500000002341615172041035023670 0ustar fengfeng[1] FL=སྐྱེ་བོ་ཡོན་ཏན་ཡོད་མེད་པའི།། བླང་དོར་བློ་གྲོས་ལྡན་པ་མཁས།། SL=རྡུལ་དང་འདྲེས་པའི་ལྕགས་ཕྱེ་རྣམས།། ཁབ་ལེན་རྡོ་ཡིས་ལེན་པར་ཤེས།། [2] FL=ལེགས་བཤད་མཁས་པའི་བློ་གྲོས་ཀྱིས།། གོ་ཡི་བླུན་པོས་དེ་ལྟ་མིན།། SL=ཉི་མའི་འོད་ཟེར་ཤར་བ་ན།། འབྱུང་པོའི་བྱ་རྣམས་ལོང་བར་འགྱུར།། [3] FL=བློ་ཆུང་གྲོས་གཉིས་འཁྲུགས་པའི་ཚེ།། བློ་ལྡན་ཐབས་ཀྱིས་བདེ་བར་གསོ།། SL=ཆུ་ཀླུང་རྙོག་པས་ཆུད་གཟན་པ།། ཆུ་དྭངས་ནོར་བུས་དྭངས་པར་བྱེད།། [4] FL=ཤེས་རབ་ལྡན་པ་མགོ་བསྐོར་ཡང་།། བྱ་བའི་ཆ་ལ་རྨོངས་མི་འགྱུར།། SL=སྲོག་ཆགས་གྲོག་མ་མིག་མེད་ཀྱང་།། མིག་ལྡན་གཞན་ལས་ལྷག་པར་མགྱོགས།། [5] FL=བླུན་པོས་ཡོན་ཏན་ཁར་འབྱིན་ཏེ།། མཁས་པས་ཡོན་ཏན་ཁོང་དུ་སྦེད།། SL=སོག་མ་ཆུ་ཡི་སྟེང་དུ་འཕྱོ།། ནོར་བུ་སྟེང་དུ་བཞག་ཀྱང་འབྱིང་།། [6] FL=མཁས་པ་ཡོན་ཏན་དཔག་མེད་ཀྱང་།། གཞན་གྱི་ཡོན་ཏན་ཆུང་ངུའང་ལེན།། SL=དེ་ལྟར་རྒྱུན་དུ་སྤྱད་པ་ཡིས།། མྱུར་དུ་ཐམས་ཅད་མཁྱེན་པར་འགྲོ།། [7] FL=མཁས་པ་སློབ་པའི་དུས་ན་སྡུག། བདེ་བར་སྡོད་ལ་མཁས་མི་སྲིད།། SL=བདེ་བ་ཆུང་ལ་ཆགས་པ་དེས།། ཆེན་པོའི་བདེ་བ་ཐོབ་མི་སྲིད།། [8] FL=དམ་པའི་ཡོན་ཏན་སྦས་གྱུར་ཀྱང་།། འཇིག་རྟེན་ཀུན་ལ་ཁྱབ་པར་གསལ།། SL=སྣ་མའི་མེ་ཏོག་ལེགས་བཀབ་ཀྱང་།། དྲི་ཞིམ་ཀུན་ཏུ་ཁྱབ་པར་འགྱུར།། [9] FL=བློ་དང་ལྡན་ན་ཉམ་ཆུང་ཡང་།། སྟོབས་ལྡན་དགྲ་བོས་ཅི་བྱར་ཡོད།། SL=རི་དྭགས་རྒྱལ་པོ་སྟོབས་ལྡན་ཡང་།། རི་བོང་བློ་དང་ལྡན་པས་བསད།། [10] FL=བླུན་པོ་བྱ་བ་ལ་སྦྱར་ན།། དོན་ཉམས་དེ་ཡང་ཉམས་པར་འགྱུར།། SL=ཝ་སྐྱེས་རྒྱལ་པོར་བསྐོས་པ་ཡིས།། འཁོར་སྡིག་རང་ཡང་བསད་ཅེས་གྲགས།། [11] FL=ཕན་དང་མི་ཕན་མི་དཔྱོད་ཅིང་།། བློ་དང་ཐོས་པ་མི་སྒྲུབ་པར།། SL=ལྟོ་འགྲངས་འབའ་ཞིག་དོན་གཉེར་བ།། སྤུ་མེད་པ་ཡི་ཕག་པ་ཡིན།། [12] FL=ཡོན་ཏན་ཆུང་རྣམས་ང་རྒྱལ་ཆེ།། མཁས་པར་གྱུར་ན་དུལ་བར་གནས།། SL=ཆུ་ཕྲན་རྟག་ཏུ་ཀུ་ཅོ་ཆེ།། རྒྱ་མཚོས་ཅ་ཅོ་ག་ལ་སྒྲོག། [13] FL=དམ་པ་དུལ་བས་རང་གཞན་སྐྱོང་།།ངན་པ་རེངས་པས་རང་གཞན་སྡུག། SL=འབྲས་ལྡན་ཤིང་གིས་རང་གཞན་སྲུང་།། ཤིང་སྐམ་རེངས་པས་རང་གཞན་སྲེག། [14] FL=ཧ་ཅང་གཡོ་སྒྱུ་མང་དྲགས་ན།། རེ་ཞིག་གྲུབ་ཀྱང་མཐའ་མར་བརླགས།། SL=གཟིག་ལྤགས་བཀབ་པའི་བོང་བུ་ཡིས།། ལོ་ཏོག་ཟོས་མཐར་གཞན་གྱི་བསད།། [15] FL=གཡོ་ཅན་བཟང་བོའི་ཚུལ་བཟུང་ནས།། ཕྱི་ནས་དོན་ལ་བསླུ་བ་ཡོད།།རི་དྭགས་རྔ་མ་བསྟན་ནས་ནི།། ཁྲེལ་འདས་བོང་བུའི་ཤ་དག་འཚོང་།། SL=མང་བོ་གཅིག་ཏུ་བློ་མཐུན་ན།། ཉམ་ཆུང་གིས་ཀྱང་དོན་ཆེན་འགྲུབ།།སྲོག་ཆགས་གྲོག་མའི་ཚོགས་འདུས་པས།།སེང་གེའི་ཕྲུ་གུ་བསད་ཅེས་གྲགས།། [16] FL=བདག་ཉིད་དཔོན་དུ་བསྐོས་གྱུར་ན།། དེ་ཡི་བྱ་བ་ཤེས་པ་དཀོན།། SL=གཞན་ལ་ལྟ་བའི་མིག་ཡོད་ཀྱང་།། རང་ཉིད་བལྟ་ན་མེ་ལོང་དགོས།། [17] FL=རང་གིས་ངན་སྤྱོད་མ་བྱས་ན།། བརྒྱ་བྱིན་གྱིས་ཀྱང་སྨན་མི་ནུས།། SL=ཆུ་མིག་རང་ཉིད་མ་བསྐམས་ན།། ས་ཡིས་མནན་པས་ག་ལ་ཐུབ།། [18] FL=བློ་ལྡན་བྱ་བ་ཅུང་ཟད་ཀྱང་།། རྒྱུན་དུ་གྲོས་ཀྱིས་བསྒྲུབ་པར་བྱ།། SL=གྲུབ་པར་གྱུར་ན་ལྟ་ཅི་སྨྲོས།། མ་གྲུབ་ན་ཡང་མཛེས་པའི་རྒྱུ།། [19] FL=རང་ལ་དགོས་པའི་བསྟན་བཅོས་རྣམས།། ཉི་མ་རེ་ལ་ཚིག་རེ་ཟུངས།། SL=གྲོག་མཁར་དང་ནི་སྦྲང་རྩི་ལྟར།། རིང་བོར་མི་ཐོགས་མཁས་པར་འགྱུར།། [20] FL=མཁས་པ་ཡོན་ཏན་མཛོད་འཛིན་པ།། དེ་དག་ལེགས་བཤད་རིན་ཆེན་སྡུད།། SL=རྒྱ་མཚོ་ཆེན་པོ་ཆུ་བོའི་གཏེར།། ཡིན་ཕྱིར་ཆུ་ཕྲན་ཐམས་ཅད་འབབ།། [21] FL=ཡོན་ཏན་ལྡན་ན་སྐྱེ་བོ་ཀུན།། མ་བསྡུས་པར་ཡང་རང་ཉིད་འདུ།། SL=དྲི་ལྡན་མེ་ཏོག་རྒྱང་རིང་ཡང་།། བུང་བ་སྤྲིན་གྱི་ཚོགས་བཞིན་འཁོར།། [22] FL=བློ་གྲོས་ལྡན་པ་གཉིས་བགྲོས་ན།། བློ་གྲོས་ལེགས་པ་གཞན་འབྱུང་སྲིད།། SL=ཡུང་བ་དང་ནི་ཚ་ལེ་ལས།། ཁ་དོག་གཞན་ཞིག་སྐྱེ་བར་འགྱུར།། [23] FL=ཐབས་ལ་མཁས་ན་ཆེན་པོ་ཡང་།། བྲན་དུ་བཀོལ་བར་ག་ལ་དཀའ།། SL=མཁའ་ལྡིང་མཐུ་རྩལ་ཆེ་ན་ཡང་།། གོས་སེར་ཅན་གྱི་བཞོན་པར་གྱུར།། [24] FL=བློ་དང་ལྡན་ན་མ་སྨྲས་ཀྱང་།། རྣམ་འགྱུར་ཉིད་ལས་བསམ་པ་གོ། SL=བལ་བོའི་སེའུ་མ་ཟོས་ཀྱང་།། ཁ་དོག་ཉིད་ལས་བྲོ་བ་ཤེས།། [25] FL=དམ་པ་རྒྱང་ན་གནས་ན་ཡང་།། འཁོར་འདབས་ཕན་པས་རིང་ནས་སྐྱོང་།། SL=མཁའ་ལ་སྤྲིན་ཆེན་འཁྲིགས་པ་ཡིས།། ས་ཡི་ལོ་ཏོག་ཁྱད་པར་འཕེལ།། [26] FL=དམ་པ་སྡིག་པ་ཆུང་ཡང་སྤོང་།། དམན་རྣམས་ཆེན་པོའང་དེ་ལྟ་མིན།། SL=ཞོ་ལ་རྡུལ་ཕྲན་འབྱར་བ་སེལ།།ཆང་ལ་ཕབ་ཀྱང་ལྷག་པར་འདེབས།། [27] FL=རྒྱལ་པོ་རང་ཡུལ་ཆེ་བ་ཙམ།། དམ་པ་གང་དུ་ཕྱིན་པར་བཀུར།། SL=མེ་ཏོག་ཕལ་ཆེར་ཉིན་རེའི་རྒྱན།། གཙུག་གི་ནོར་བུ་གང་དུའང་མཆོད།། [28] FL=རིག་པ་ནངས་པར་འཆི་ཡང་བསླབ།། ཚེ་འདིར་མཁས་པར་མ་གྱུར་ཀྱང་།། SL=སྐྱེ་བ་ཕྱི་མར་བཅོལ་བ་ཡི།། ནོར་ལ་རང་ཉིད་ལེན་དང་མཚུངས།། ukui-screensaver/src/screensaver/language/screensaver-jd.ini0000664000175000017500000001053615172041035023304 0ustar fengfeng[1] OL=没有共产党,就没有新中国! author= [2] OL=解放思想,实事求是! author= [3] OL=中国产生了共产党,这是开天辟地的大事变! author= [4] FL=一百年来,中国共产党团结带领中国人民 SL=书写了中华民族几千年历史上最恢宏的史诗! author= [5] FL=一百年来,中国共产党团结带领中国人民进行的一切奋斗、一切牺牲、 SL=一切创造,归结起来就是一个主题:实现中华民族伟大复兴! author= [6] OL=初心易得,始终难守! author= [7] FL=我们要用历史映照现实,远观未来。看清楚过去为什么能够成功, SL=弄明白未来怎样才能继续成功! author= [8] FL=中国共产党领导是党和国家的根本所在,命脉所在; SL=是全国各族人民的利益所系、命运所系! author= [9] OL=中国共产党根基在人民,血脉在人民、力量在人民! author= [10] FL=中国共产党始终代表最广大人民根本利益, SL=没有任何自己特殊的利益! author= [11] FL=用马克思主义观察时代、把握时代、引领时代, SL=继续发展当代中国马克思主义、21世纪马克思主义! author= [12] FL=走自己的路是党的全部理论和实践立足点, SL=更是党百年奋斗得出的历史结论! author= [13] OL=强国必须强军,军强才能国安! author= [14] OL=敢于斗争,敢于胜利,是中国共产党不可战胜的强大精神力量! author= [15] FL=新时代的中国青年,要以实现中华民族伟大复兴为己任, SL=增强做中国人的志气、骨气、底气! author= [16] FL=伟大、光荣、正确的中国共产党万岁! SL=伟大、光荣、英雄的中国人民万岁! author= [17] OL=发展才是硬道理! author= [18] OL=聚精会神搞建设,一心一意谋发展! author= [19] OL=看准了,就大胆地试,大胆地闯! author= [20] OL=人固有一死,或重于泰山,或轻于鸿毛! SL= author= [21] FL=理论联系实际,是马克思主义“活的灵魂”, SL=是共产党人从斗争中创造新局面的思想路线! author= [22] OL=用中国理论阐释中国实践,用中国实践升华中国理论! author= [23] FL=理论联系实际、和人民群众紧密联系在一起、 SL=批评与自我批评,是中国共产党的三大优良作风! author= [24] FL=坚持中国特色社会主义道路自信、 SL=理论自信、制度自信、文化自信! author= [25] OL=让初心薪火相传,把使命永担在肩! author= [26] OL=心有所信,方能行远! author= [27] FL=面向未来,走好新时代的长征路, SL=我们更需要坚定理想信念、矢志拼搏奋斗! author= [28] FL=牢记初心使命,坚定必胜信念, SL=发扬斗争精神,增强斗争本领! author= [29] OL=发扬优良传统,承担历史使命! author= [30] FL=实事求是、坚持真理,科学应变、主动求变,咬定目标、 SL=勇往直前,走好新时代的长征路! author= [31] FL=缅怀革命先烈,赓续共产党人精神血脉, SL=坚定理想信念,砥砺革命意志! author= [32] FL=革命理想高于天,理想信念之火 SL=一经点燃就会产生巨大的精神力量! author= [33] OL=信仰、信念、信心是最好的防腐剂! author= [34] OL=学史明理、学史增信、学史崇德、学史力行! author= [35] OL=时代是思想之母,实践是理论之源! author= [36] FL=实践发展永无止境,我们认识真理、 SL=进行理论创新就永无止境! author= [37] OL=理想因其远大而为理想,信念因其执着而为信念! author= [38] FL=在胜利和顺境时不骄傲不急躁, SL=在困难和逆境时不消沉不动摇! author= [39] OL=从群众中来、到群众中去! author= [40] FL=中国特色社会主义道路是实现社会主义现代化的必由之路, SL=是创造人民美好生活的必由之路! author= [41] FL=人民立场是中国共产党的根本政治立场, SL=是马克思主义政党区别于其他政党的显著标志! author= [42] OL=方向决定道路,道路决定命运! author= [43] OL=全党要坚定道路自信、理论自信、制度自信、文化自信! author= [44] OL=得众则得国,失众则失国! author= [45] OL=自信人生二百年,会当水击三千里! author= [46] FL=发展是党执政兴国的第一要务, SL=是解决中国所有问题的关键! author= ukui-screensaver/src/screensaver/language/screensaver-zh_CN.ini0000664000175000017500000000523615172041035023711 0ustar fengfeng[1] OL=天生我材必有用,千金散尽还复来。 author=李白《将进酒》 [2] OL=莫愁前路无知己,天下谁人不识君。 author=高适《别董大二首》 [3] OL=莫听穿林打叶声,何妨吟啸且徐行。 author=苏轼《定风波》 [4] OL=巧言令色,鲜矣仁! author=《论语》 [5] FL=与君初相识,犹如故人归。 SL=天涯明月新,朝暮最相思。 author=杜牧《会友》 [6] OL=见贤思齐焉,见不贤而内自省也。 author=《论语》 [7] OL=为政以德,譬如北辰,居其所而众星共之。 author=《论语》 [8] OL=知彼知己,百战不殆。 author=《孙子兵法》 [9] OL=人患不知其过,既知之,不能改,是无勇也。 author=韩愈《五箴》 [10] OL=万物并育而不相害,道并行而不相悖 author=《礼记·中庸》 [11] OL=自信人生二百年,会当水击三千里 author=毛泽东《七古·残句》 [12] OL=青春虚度无所成,白首衔悲亦何及。 author=权德舆《放歌行》 [13] OL=知者行之始。行者知之成。 author=王阳明《传习录·卷上·门人陆澄录》 [14] OL=此心不动,随机而动。 author=王阳明 [15] OL=你未看此花时,此花与汝心同归于寂。 author=《传习录·卷下·门人黄省曾录》 [16] OL=省察是有事时存养,存养是无事时省察。 author=王阳明《传习录》 [17] OL=路漫漫其修远兮,吾将上下而求索。 author=《离骚》 [18] OL=悲莫悲兮生别离,乐莫乐兮新相知。 author=《九歌·少司命》 [19] OL=风飒飒兮木萧萧,思公子兮徒离忧。 author=《九歌·山鬼》 [20] OL=善守者,藏于九地之下,善攻者,动于九天之上。 author=《孙子兵法》 [21] OL=善战者,致人而不致于人。 author=《孙子兵法·虚实第六》 [22] OL=乱生于治,怯生于勇,弱生于强。 author=《孙子兵法·兵势第五》 [23] OL=不战而屈人之兵,善之善者也。 author=《孙子兵法·谋攻第三》 [24] OL=天行健,君子以自强不息。 author=《周易.乾卦》 [25] OL=积善之家,必有余庆;积不善之家,必有余殃。 author=《周易.坤卦》 [26] OL=穷则变,变则通,通则久。 author=《周易.系辞下》 [27] OL=天若有情天亦老,人间正道是沧桑 author=毛泽东《七律·人民解放军占领南京》 [28] OL=仰天大笑出门去,我辈岂是蓬蒿人。 author=李白《南陵别儿童入京》 [29] OL=秋风生渭水,落叶满长安。 author=贾岛《忆江上吴处士》 [30] OL=飞流直下三千尺,疑是银河落九天。 author=李白《望庐山瀑布》 [31] OL=长风破浪会有时,直挂云帆济沧海。 author=李白《行路难·其一》 ukui-screensaver/src/screensaver/graphicsview.h0000664000175000017500000000221315172041035020742 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef GRAPHICSVIEW_H #define GRAPHICSVIEW_H #include #include #include class GraphicsView : public QGraphicsView { Q_OBJECT public: explicit GraphicsView(QWidget *parent = nullptr); protected: void mouseMoveEvent(QMouseEvent *event); }; #endif // GRAPHICSVIEW_H ukui-screensaver/src/screensaver/timethumbnailwidget.h0000664000175000017500000000146015172041106022317 0ustar fengfeng#ifndef TIMETHUMBNAILWIDGET_H #define TIMETHUMBNAILWIDGET_H #include #include #include #include #include #include #include enum DateInfo { DATETYPE, TIMETYPE, }; class TimeThumbnailWidget : public QWidget { Q_OBJECT public: explicit TimeThumbnailWidget(QWidget *parent = nullptr, bool isControlSaver = false); ~TimeThumbnailWidget(); public Q_SLOTS: void update_datatime(); void timerStart(); void timerStop(); private: void initUI(); QString getLongFormatDate(int type); private: QTimer *m_timer = nullptr; QLabel* m_timeLabel = nullptr; QLabel* m_dayLabel = nullptr; QVBoxLayout* m_mainLayout = nullptr; bool m_bControlSaver = false; }; #endif // TIMETHUMBNAILWIDGET_H ukui-screensaver/src/screensaver/albumthumbnailwidget.cpp0000664000175000017500000001165215172041106023020 0ustar fengfeng#include "albumthumbnailwidget.h" #include #include #include AlbumThumbnailWidget::AlbumThumbnailWidget(QWidget *parent, bool isControlSaver) : QWidget{ parent }, m_bControlSaver(isControlSaver) { initUI(); installEventFilter(this); } void AlbumThumbnailWidget::setBackground(QPixmap background) { m_background = background; repaint(); } void AlbumThumbnailWidget::setDefaultPhoto(QPixmap photo) { m_customAlbumViewer->setDefaultPixmap(photo); } void AlbumThumbnailWidget::setDefaultPath(QString defaultPath) { m_customAlbumViewer->loadImages(defaultPath); if (m_bControlSaver) { m_customAlbumViewer->updateViewerSize(0.1); } else { m_customAlbumViewer->updateViewerSize(0.15); } m_customAlbumViewer->setUpdateInterval(); m_customAlbumViewer->stopDisplayImages(); } void AlbumThumbnailWidget::initUI() { m_mainLayout = new QHBoxLayout(this); if (m_bControlSaver) { setFixedSize(201, 113); } else { setFixedSize(292, 164); } m_timeThumbnailWidget = new TimeThumbnailWidget(this, m_bControlSaver); m_mainLayout->addWidget(m_timeThumbnailWidget); m_customAlbumViewer = new CustomAlbumViewer(this); connect(m_customAlbumViewer, &CustomAlbumViewer::pixmapChanged, this, [=]() { update(); }); if (!m_bControlSaver) { m_customAlbumViewer->setFixedSize(850 * 0.15, 790 * 0.15); } else { m_customAlbumViewer->setFixedSize(850 * 0.10, 790 * 0.10); } m_mainLayout->addWidget(m_customAlbumViewer); m_maskLabel = new MaskLable(this, m_bControlSaver); m_maskLabel->setGeometry(0, 0, this->width(), this->height()); m_maskLabel->hide(); if (!m_bControlSaver) { m_animation = new QVariantAnimation(this); m_animation->setDuration(300); m_animation->setEasingCurve(QEasingCurve::OutQuad); connect(m_animation, &QVariantAnimation::valueChanged, [this](const QVariant &value) { QSize size = value.toSize(); setFixedSize(size); m_maskLabel->setFixedSize(size); }); } } void AlbumThumbnailWidget::animateSize(const QSize &targetSize) { m_animation->stop(); m_animation->setStartValue(size()); m_animation->setEndValue(targetSize); m_animation->start(); } void AlbumThumbnailWidget::setSelectStatus(bool status) { if (!status && m_bSelectStatus) { m_maskLabel->hide(); m_bSelectStatus = false; } else if (status && !m_bSelectStatus) { m_maskLabel->show(); m_bSelectStatus = true; } } bool AlbumThumbnailWidget::eventFilter(QObject *watched, QEvent *event) { if (parentWidget() && !parentWidget()->isEnabled()) { event->ignore(); return QWidget::eventFilter(watched, event); } if (event->type() == QEvent::Enter) { if (!m_bControlSaver) { animateSize(QSize(320, 180)); m_bHoverStatus = true; } if (m_bControlSaver || m_bSelectStatus) { m_maskLabel->show(); } } else if (event->type() == QEvent::Leave) { if (!m_bControlSaver) { animateSize(QSize(292, 164)); m_bHoverStatus = false; } if (!m_bSelectStatus) { m_maskLabel->hide(); } } if (event->type() == QEvent::MouseButtonRelease) { if (!m_bControlSaver) { m_maskLabel->show(); m_bSelectStatus = true; } m_maskLabel->show(); Q_EMIT albumModeClicked(); } return QWidget::eventFilter(watched, event); } void AlbumThumbnailWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); if (!m_bControlSaver) { QPainterPath backgroundPath; backgroundPath.addRoundedRect(rect(), 8, 8); painter.setClipPath(backgroundPath); painter.drawPixmap(this->rect(), m_background, m_background.rect()); painter.setClipping(false); if (m_bHoverStatus) { QPen pen(QColor(255, 255, 255, 153)); pen.setWidth(2); painter.setPen(pen); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 8, 8); } else { painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } else { painter.fillRect(rect(), palette().color(QPalette::Base)); const QRect bgRect(4, 4, 193, 105); QPainterPath path; path.addRoundedRect(bgRect, 8, 8); painter.setClipPath(path); painter.drawPixmap(bgRect, m_background, m_background.rect()); painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } ukui-screensaver/src/screensaver/weatherthumbnailwidget.h0000664000175000017500000000235315172041106023022 0ustar fengfeng#ifndef WEATHERTHUMBNAILWIDGET_H #define WEATHERTHUMBNAILWIDGET_H #include "masklable.h" #include "timethumbnailwidget.h" #include "weatherwidget.h" #include #include #include class WeatherThumbnailWidget : public QWidget { Q_OBJECT public: explicit WeatherThumbnailWidget(QWidget *parent = nullptr, bool isControlSaver = false); void setBackground(QPixmap background); void setSelectStatus(bool status); void setCurrentWeather(QString temperature, QString weather); void setErrorTip(QString tip); void setWeatherIcon(QString weather, QPixmap weatherStateIcon); private: void initUI(); void animateSize(const QSize &targetSize); protected: void paintEvent(QPaintEvent *event); bool eventFilter(QObject *watched, QEvent *event); Q_SIGNALS: void weatherModeClicked(); private: TimeThumbnailWidget* m_timeThumbnailWidget = nullptr; WeatherWidget *m_weatherWidget = nullptr; QHBoxLayout* m_mainLayout = nullptr; QPixmap m_background; QVariantAnimation* m_animation; MaskLable* m_maskLabel = nullptr; bool m_bHoverStatus = false; bool m_bSelectStatus = false; bool m_bControlSaver = false; }; #endif // WEATHERTHUMBNAILWIDGET_H ukui-screensaver/src/screensaver/scconfiguration.h0000664000175000017500000000773115172041106021455 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCCONFIGURATION_H #define SCCONFIGURATION_H #ifndef QT_NO_KEYWORDS #define QT_NO_KEYWORDS #endif #include #include #include class QGSettings; class BackendDbusHelper; class SCConfiguration : public QObject { Q_OBJECT public: explicit SCConfiguration(QObject *parent = nullptr); static SCConfiguration *instance(QObject *parent = nullptr); QString getDefaultBackground(); //获取屏保默认背景 int getTimeType(); //获取显示时间格式 QString getDateType(); //获取日期格式 int getFontSize(); //获取当前字体大小 double getPtToPx(); bool getAutoSwitch(); //获取是否自动切换 bool getCShowRestTime(); //自定义是否显示休息时间 bool getUShowRestTime(); // UKUI是否显示休息时间 bool getIsCustom(); //屏保是否自定义 int getCycleTime(); //背景图片切换间隔时间 QString getBackgroundPath(); //背景图片来源 bool getTextIsCenter(); //文字是否居中 QString getMyText(); //获取文字 bool getMessageShowEnable(); //获取是否显示消息 int getMessageNumber(); //获取消息数量 int getBlurNumber(); QString getcurStyle(); QString getVideoPath(); QString getVideoFormat(); int getVideoSize(); int getVideoWidth(); int getVideoHeight(); // 智慧屏保 int getSubMode(); void setSubMode(int mode); int getWeatherPosition(); QString getMusicPath(); bool getMusicFollowSystem(); int getPhotoCycleMode(); int getPhotoCycleInterval(); QStringList getDefaultPhotoPath(); QStringList getCustomPhotoPath(); QString getSelectPhotoPath(); int getNetworkConnectState(); QString getWeatherData(); bool getSaverDatetimeEnable(); public: public Q_SLOTS: void onConfigurationChanged(QString key, QVariant value); void onNetworkConnectStateChanged(int state); void onSaverDatetimeEnableChanged(bool isEnable, QString username); Q_SIGNALS: void isCustomModeChanged(bool custom); void cycleTimeChanged(int cycleTime); bool autoSwitchChanged(bool changed); QString backgroundPathChanged(QString path); QString backgroundChanged(QString path); QString myTextChanged(QString text); bool showCRestTimeChanged(bool isShow); bool showURestTimeChanged(bool isShow); bool textIsCenterChanged(bool isCenter); int messageNumberChanged(int number); bool messageShowEnableChanged(bool enabled); int timeTypeChanged(int time); QString dateTypeChanged(QString type); int blurChanged(int num); QString styleChanged(QString type); int fontSizeChanged(int fontSize); int subModeChanged(int mode); int weatherPositionChanged(int position); QString musicPathChanged(QString path); int photoCycleModeChanged(int mode); int photoCycleIntervalChanged(int interval); QString photoPathChanged(QString path); QString photoSelectPathChanged(QString path); bool musicFollowSystemChanged(bool value); int networkConnectStateChanged(int state); int saverDatetimeEnableChanged(bool isEnable, QString username); private: BackendDbusHelper *m_helperBackendDbus = nullptr; static SCConfiguration *instance_; }; #endif // CONFIGURATION_H ukui-screensaver/src/screensaver/masklable.h0000664000175000017500000000052015172041106020200 0ustar fengfeng#ifndef MASKLABLE_H #define MASKLABLE_H #include #include class MaskLable : public QLabel { Q_OBJECT public: explicit MaskLable(QWidget *parent = nullptr, bool isControlSaver = false); protected: void paintEvent(QPaintEvent *event); private: bool m_bControlSaver = false; }; #endif // MASKLABLE_H ukui-screensaver/src/screensaver/checkbutton.cpp0000664000175000017500000001016415172041035021117 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "checkbutton.h" checkButton::checkButton(QWidget *parent) : QFrame(parent) { this->setFixedSize(QSize(52, 24)); checked = false; borderColorOff = QColor("#cccccc"); bgColorOff = QColor("#ffffff"); bgColorOn = QColor("#0078d7"); sliderColorOff = QColor("#cccccc"); sliderColorOn = QColor("#ffffff"); space = 4; step = width() / 50; startX = 0; endX = 0; timer = new QTimer(this); timer->setInterval(5); connect(timer, SIGNAL(timeout()), this, SLOT(updatevalue())); setWindowFlags(Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground, true); } void checkButton::paintEvent(QPaintEvent *) { //启用反锯齿 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); drawBg(&painter); drawSlider(&painter); } void checkButton::drawBg(QPainter *painter) { painter->save(); if (!checked) { painter->setPen(QColor(255, 255, 255, 30)); painter->setBrush(QColor(255, 255, 255, 30)); } else { painter->setPen(Qt::NoPen); painter->setBrush(bgColorOn); } // circle in QRect rect(0, 0, width(), height()); //半径为高度的一半 int radius = rect.height() / 2; //圆的宽度为高度 int circleWidth = rect.height(); QPainterPath path; path.moveTo(radius, rect.left()); path.arcTo(QRectF(rect.left(), rect.top(), circleWidth, circleWidth), 90, 180); path.lineTo(rect.width() - radius, rect.height()); path.arcTo(QRectF(rect.width() - rect.height(), rect.top(), circleWidth, circleWidth), 270, 180); path.lineTo(radius, rect.top()); painter->drawPath(path); painter->restore(); } void checkButton::drawSlider(QPainter *painter) { painter->save(); painter->setPen(Qt::NoPen); if (!checked) { painter->setBrush(sliderColorOff); } else painter->setBrush(sliderColorOn); // circle in QRect rect(0, 0, width(), height()); int sliderWidth = rect.height() - space * 2; QRect sliderRect(startX + space, space, sliderWidth, sliderWidth); painter->drawEllipse(sliderRect); painter->restore(); } void checkButton::mousePressEvent(QMouseEvent *) { checked = !checked; emit checkedChanged(checked); step = width() / 50; if (checked) { endX = width() - height(); } else { endX = 0; } timer->start(); } void checkButton::resizeEvent(QResizeEvent *) { step = width() / 50; if (checked) { startX = width() - height(); } else startX = 0; update(); } void checkButton::updatevalue() { if (checked) if (startX < endX) { startX = startX + step; } else { startX = endX; timer->stop(); } else { if (startX > endX) { startX = startX - step; } else { startX = endX; timer->stop(); } } update(); } void checkButton::setChecked(bool checked) { if (this->checked != checked) { this->checked = checked; emit checkedChanged(checked); update(); } step = width() / 50; if (checked) { endX = width() - height(); } else { endX = 0; } timer->start(); } bool checkButton::isChecked() { return this->checked; } ukui-screensaver/src/screensaver/mprissaverwidget.cpp0000664000175000017500000004741115172041106022211 0ustar fengfeng#include "mprissaverwidget.h" #include "musicfileinformation.h" #include #include #include "commonfunc.h" #include "../lock-dialog/lockdialogmodel.h" #include "playcontroller.h" MPRISSaverWidget::MPRISSaverWidget(QWidget *parent, bool saverMedia) : QFrame(parent), m_bSaverMedia(saverMedia) { initUI(); initConnections(); if (m_bSaverMedia) { // m_adapter = new DbusAdapter(this); } } MPRISSaverWidget::~MPRISSaverWidget() { if (m_adapter) { delete m_adapter; m_adapter = nullptr; } } void MPRISSaverWidget::setSaverMediaType(bool saverMedia) { if (!m_bSaverMedia && saverMedia && m_mprisMonitor) { disconnect(m_mprisMonitor, nullptr, this, nullptr); if (m_mprisDbusClient) { disconnect(m_mprisDbusClient, nullptr, this, nullptr); } connect( &playController::getInstance(), &playController::playerStateChange, this, &MPRISSaverWidget::onPlayerStateChanged); connect( &playController::getInstance(), &playController::mateDataChanged, this, &MPRISSaverWidget::onMetadataChanged); connect( &playController::getInstance(), &playController::singalChangePath, this, &MPRISSaverWidget::slotSongInfo); } else if (m_bSaverMedia && !saverMedia) { disconnect(&playController::getInstance()); } m_bSaverMedia = saverMedia; } void MPRISSaverWidget::updateMediaSize(float scale) { if (m_allLabels.isEmpty()) { QList labels = this->findChildren(); foreach (QLabel *label, labels) { m_allLabels[label] = label->rect(); QPixmap pixmap = label->pixmap(); if (!pixmap.isNull()) { if (!m_originalPixmaps.contains(label)) { m_originalPixmaps[label] = pixmap; } } const QFont font = label->font(); if (!m_originalFonts.contains(label)) { m_originalFonts[label] = font; } } QList buttons = this->findChildren(); foreach (QPushButton *button, buttons) { m_allButtons[button] = button->rect(); } } QList labels = this->findChildren(); foreach (QLabel *label, labels) { QPixmap pixmap = label->pixmap(); QSize newSize = m_allLabels[label].size() * scale; if (!pixmap.isNull()) { QPixmap scaledPixmap = m_originalPixmaps[label].scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); label->setPixmap(scaledPixmap); } label->setFixedSize(newSize); QFont font = label->font(); font.setPointSizeF(m_originalFonts[label].pointSize() * scale); label->setFont(font); } QList buttons = this->findChildren(); foreach (QPushButton *button, buttons) { if (!button->icon().isNull()) { button->setIconSize(m_allButtons[button].size() * scale); button->setFixedSize(m_allButtons[button].size() * scale); } } m_labelName->setFixedWidth(this->width()); m_labelArtist->setFixedWidth(this->width()); m_buttonLayout->setSpacing(40 * scale); m_albumLayout->setContentsMargins(146 * scale, 20 * scale, 45 * scale, 80 * scale); } void MPRISSaverWidget::initUI() { setAttribute(Qt::WA_TranslucentBackground); m_mainLayout = new QVBoxLayout(); m_mainLayout->setContentsMargins(0, 0, 0, 0); m_mainLayout->setSpacing(0); m_labelName = new CustomLabel(this); m_labelName->setMarqueueMode(true); m_labelName->setFontSize(24); m_labelName->setFixedSize(this->width(), 50); m_labelName->setStyleSheet("QLabel{ color: white;}"); m_labelName->setAlignment(Qt::AlignHCenter); m_mainLayout->addWidget(m_labelName, 0, Qt::AlignCenter); m_labelArtist = new CustomLabel(this); m_labelArtist->setMarqueueMode(true); m_labelArtist->setFontSize(16); m_labelArtist->setFixedSize(this->width(), 42); m_labelArtist->setAlignment(Qt::AlignHCenter); m_labelArtist->setStyleSheet("QLabel{ color: white;}"); m_mainLayout->addSpacing(6); m_mainLayout->addWidget(m_labelArtist, 0, Qt::AlignCenter); m_pLabelDitu = new QLabel(this); m_pixmapAlbumDefault = QPixmap(":/assets/ditu.png").scaled(521, 521, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); m_pLabelDitu->setPixmap(m_pixmapAlbumDefault); m_pLabelDitu->setFixedSize(521, 521); m_mainLayout->addWidget(m_pLabelDitu); m_albumLayout = new QHBoxLayout(m_pLabelDitu); m_albumLayout->setSpacing(0); m_albumLayout->setContentsMargins(146, 20, 45, 80); m_labelAlbum = new RotatingImageLabel(this); m_labelAlbum->setFixedSize(230, 230); m_albumLayout->addWidget(m_labelAlbum, 0, Qt::AlignCenter); m_labelBopian = new CornerRotatingLabel(this); m_labelBopian->setFixedSize(115, 192); QPixmap pixmapBopian = QPixmap(":/assets/bogan.png").scaled(115, 186, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); m_labelBopian->setAlignment(Qt::AlignLeft | Qt::AlignTop); m_labelBopian->setPixmap(pixmapBopian); m_labelBopian->rotateCounterClockwise(); // m_labelBopian->setImage(pixmapBopian); m_albumLayout->addWidget(m_labelBopian, 0, Qt::AlignRight | Qt::AlignTop); m_buttonLayout = new QHBoxLayout(); m_buttonLayout->setContentsMargins(0, 0, 0, 0); m_buttonLayout->setSpacing(40); m_buttonLayout->addStretch(); m_btnPrev = new QPushButton(this); m_btnPrev->setFlat(true); m_btnPrev->setFixedSize(64, 64); QPixmap prevIcon = drawSymbolicColoredPixmap(QIcon::fromTheme("media-skip-backward-symbolic").pixmap(QSize(48, 48)), "white"); m_btnPrev->setIcon(prevIcon); m_btnPrev->setIconSize(QSize(48, 48)); m_btnPrev->setStyleSheet( QString("QPushButton{border-radius: 6px; border:0px solid white; background-color: rgba(0,0,0,0);}" "QPushButton::hover{background-color: rgba(0,0,0,15%);}" "QPushButton::pressed {background-color: rgba(0,0,0,21%);}")); m_buttonLayout->addWidget(m_btnPrev); m_btnPlayPause = new QPushButton(this); m_btnPlayPause->setFlat(true); m_btnPlayPause->setFixedSize(64, 64); QPixmap playPauseIcon = drawSymbolicColoredPixmap(QIcon::fromTheme("ukui-play-full-symbolic").pixmap(QSize(64, 64)), "white"); m_btnPlayPause->setIcon(playPauseIcon); m_btnPlayPause->setIconSize(QSize(64, 64)); m_btnPlayPause->setStyleSheet( QString("QPushButton{border-radius: 6px; border:0px solid white; background-color: rgba(0,0,0,0);}" "QPushButton::hover{background-color: rgba(0,0,0,15%);}" "QPushButton::pressed {background-color: rgba(0,0,0,21%);}")); m_buttonLayout->addWidget(m_btnPlayPause); m_btnNext = new QPushButton(this); m_btnNext->setFlat(true); m_btnNext->setFixedSize(64, 64); QPixmap nextIcon = drawSymbolicColoredPixmap(QIcon::fromTheme("media-skip-forward-symbolic").pixmap(QSize(48, 48)), "white"); m_btnNext->setIcon(nextIcon); m_btnNext->setIconSize(QSize(48, 48)); m_btnNext->setStyleSheet( QString("QPushButton{border-radius: 6px; border:0px solid white; background-color: rgba(0,0,0,0);}" "QPushButton::hover{background-color: rgba(0,0,0,15%);}" "QPushButton::pressed {background-color: rgba(0,0,0,21%);}")); m_buttonLayout->addWidget(m_btnNext); m_buttonLayout->addStretch(); m_mainLayout->addLayout(m_buttonLayout); setLayout(m_mainLayout); } void MPRISSaverWidget::initConnections() { if (m_btnNext) { connect(m_btnNext, &QPushButton::clicked, this, &MPRISSaverWidget::onBtnNext); } if (m_btnPlayPause) { connect(m_btnPlayPause, &QPushButton::clicked, this, &MPRISSaverWidget::onBtnPlayPause); } if (m_btnPrev) { connect(m_btnPrev, &QPushButton::clicked, this, &MPRISSaverWidget::onBtnPreview); } if (m_bSaverMedia) { connect( &playController::getInstance(), &playController::playerStateChange, this, &MPRISSaverWidget::onPlayerStateChanged); connect( &playController::getInstance(), &playController::mateDataChanged, this, &MPRISSaverWidget::onMetadataChanged); connect( &playController::getInstance(), &playController::singalChangePath, this, &MPRISSaverWidget::slotSongInfo); } } void MPRISSaverWidget::init(QString strMediaPlayerPath) { qDebug() << " strMediaPlayerPath = " << strMediaPlayerPath; if (strMediaPlayerPath.isEmpty()) { m_labelName->setText(tr("No music detected")); m_btnPrev->setEnabled(false); m_btnPlayPause->setEnabled(false); m_btnNext->setEnabled(false); m_labelArtist->setText(""); m_btnPlayPause->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("ukui-play-full-symbolic").pixmap(QSize(64, 64)), "white")); } if (!m_mprisMonitor) { m_mprisMonitor = QDBusConnection::sessionBus().interface(); connect( m_mprisMonitor, &QDBusConnectionInterface::serviceOwnerChanged, this, [=](const QString &name, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner) if (name.startsWith("org.mpris.MediaPlayer2.")) { onServiceStatusChanged(name, !newOwner.isEmpty()); } }); } if (!strMediaPlayerPath.isEmpty()) { onLoadMediaPath(strMediaPlayerPath); } } void MPRISSaverWidget::onServiceStatusChanged(QString strName, bool isActive) { qDebug() << "-------------------" << strName << isActive; if (strName == m_strMediaPlayerPath) { if (isActive) { onLoadMediaPath(strName); } else { onLostMediaPath(strName); } } } void MPRISSaverWidget::onLoadMediaPath(const QString &strPath) { qDebug() << "onLoadMediaPath:" << strPath; MPRISDBusClient *newMprisClient = new MPRISDBusClient(strPath, "/org/mpris/MediaPlayer2", QDBusConnection::sessionBus(), this); if (m_mprisDbusClient) m_mprisDbusClient->deleteLater(); m_mprisDbusClient = newMprisClient; connect(m_mprisDbusClient, SIGNAL(MetadataChanged(QVariantMap)), this, SLOT(onMetadataChanged())); connect(m_mprisDbusClient, SIGNAL(PlaybackStatusChanged(QString)), this, SLOT(onPlaybackStatusChanged())); connect(m_mprisDbusClient, SIGNAL(CanControlChanged(bool)), this, SLOT(onCanControlChanged())); onPlaybackStatusChanged(); onMetadataChanged(); onCanControlChanged(); m_strMediaPlayerPath = strPath; Q_EMIT statusChanged(true); } void MPRISSaverWidget::onLostMediaPath(const QString &strPath) { if (!m_strMediaPlayerPath.isEmpty() && m_strMediaPlayerPath == strPath) { if (m_mprisDbusClient) { m_mprisDbusClient->deleteLater(); m_mprisDbusClient = nullptr; } m_strMediaPlayerPath.clear(); Q_EMIT statusChanged(false); } } void MPRISSaverWidget::onBtnPreview() { m_labelBopian->startRotationAnimation(); if (m_bSaverMedia) { playController::getInstance().previousSong(); return; } if (!m_mprisDbusClient) { return; } m_btnPrev->setEnabled(false); m_btnPlayPause->setEnabled(false); m_btnNext->setEnabled(false); m_mprisDbusClient->Previous(); QTimer::singleShot(300, this, [=]() { m_btnPrev->setEnabled(true); m_btnPlayPause->setEnabled(true); m_btnNext->setEnabled(true); }); } void MPRISSaverWidget::onBtnNext() { m_labelBopian->startRotationAnimation(); if (m_bSaverMedia) { playController::getInstance().nextSong(); return; } if (!m_mprisDbusClient) { return; } m_btnPrev->setEnabled(false); m_btnPlayPause->setEnabled(false); m_btnNext->setEnabled(false); m_mprisDbusClient->Next(); QTimer::singleShot(300, this, [=]() { m_btnPrev->setEnabled(true); m_btnPlayPause->setEnabled(true); m_btnNext->setEnabled(true); }); } void MPRISSaverWidget::onBtnPlayPause() { if (m_isPlayBtnClicked) { return; } m_isPlayBtnClicked = true; QTimer::singleShot(300, this, [=]() { m_isPlayBtnClicked = false; if (m_bSaverMedia) { if (m_nPlayState != 1) { playController::getInstance().play(); return; } else { playController::getInstance().pause(); return; } } if (!m_mprisDbusClient) { return; } m_btnPrev->setEnabled(false); m_btnPlayPause->setEnabled(false); m_btnNext->setEnabled(false); if (m_nPlayState != 1) { m_mprisDbusClient->Play(); } else { m_mprisDbusClient->Pause(); } QTimer::singleShot(300, this, [=]() { m_btnPrev->setEnabled(true); m_btnPlayPause->setEnabled(true); m_btnNext->setEnabled(true); }); }); } void MPRISSaverWidget::onMetadataChanged() { qDebug() << "m_bSaverMedia = " << m_bSaverMedia; if (m_bSaverMedia) { delayMetadataUpdate(); } if (!m_mprisDbusClient) return; delayMetadataUpdate(); } void MPRISSaverWidget::slotSongInfo(QString path) { QPixmap pix = MusicFileInformation::getInstance().getCoverPhotoPixmap(path.remove("file://")); if (pix.isNull()) { m_labelAlbum->hide(); } else { m_labelAlbum->setPixmap(PixmapToRound(pix, 115 * m_scale, 115 * m_scale, 115 * m_scale, 115 * m_scale)); m_labelAlbum->setImage(PixmapToRound(pix, 115 * m_scale, 115 * m_scale, 115 * m_scale, 115 * m_scale)); m_labelAlbum->show(); } } void MPRISSaverWidget::delayMetadataUpdate(int nTimeout /* = 1000*/) { if (!m_timerDelayUpdate) { m_timerDelayUpdate = new QTimer(this); m_timerDelayUpdate->setSingleShot(true); connect(m_timerDelayUpdate, &QTimer::timeout, this, &MPRISSaverWidget::onDelayMetadataUpdate); } m_timerDelayUpdate->setInterval(nTimeout); m_timerDelayUpdate->start(); } void MPRISSaverWidget::onDelayMetadataUpdate() { if (m_nPlayState == -1 && !m_bSaverMedia) { // 停止播放状态 m_labelName->setText(tr("No music detected")); m_labelAlbum->hide(); m_btnPrev->setEnabled(false); m_btnPlayPause->setEnabled(false); m_btnNext->setEnabled(false); m_labelArtist->setText(""); m_btnPlayPause->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("ukui-play-full-symbolic").pixmap(QSize(64, 64)), "white")); } else { QVariantMap meta; if (m_bSaverMedia) { meta = playController::getInstance().getMetadata(); qDebug() << "data = " << meta; } else { meta = m_mprisDbusClient->metadata(); } qDebug() << "Metadata:" << meta; const QString &title = meta.value("xesam:title").toString(); const QString &album = meta.value("xesam:album").toString(); const QString &artist = meta.value("xesam:artist").toString(); const QString &lyrics = meta.value("xesam:asText").toString(); const QUrl &pictureUrl = meta.value("mpris:artUrl").toString(); if (title.isEmpty()) { if (album.isEmpty()) { m_labelName->setText(tr("Unknown")); } else { m_labelName->setText(album); } } else { m_labelName->setText(title); } if (artist.isEmpty()) { if (lyrics.isEmpty()) { m_labelArtist->setText(tr("Unknown")); } else { m_labelArtist->setText(lyrics); } } else { if (lyrics.isEmpty()) { m_labelArtist->setText(artist); } else { m_labelArtist->setText(artist + "/" + lyrics); } } if (!m_bSaverMedia) { if (!pictureUrl.isEmpty()) { const QSize &pictureSize = m_labelAlbum->size(); const QPixmap &picture = QPixmap(pictureUrl.toLocalFile()).scaled(pictureSize, Qt::IgnoreAspectRatio); if (!picture.isNull()) { m_labelAlbum->setImage( PixmapToRound(picture, 115 * m_scale, 115 * m_scale, 115 * m_scale, 115 * m_scale)); m_labelAlbum->show(); const QString status = m_mprisDbusClient->playbackStatus(); if (status == "Playing") { m_labelAlbum->startRotation(20000); } } else { m_labelAlbum->hide(); } } else { m_labelAlbum->hide(); } } m_btnPrev->setEnabled(true); m_btnPlayPause->setEnabled(true); m_btnNext->setEnabled(true); } } void MPRISSaverWidget::onPlayerStateChanged(int status) { if (status == 1) { m_nPlayState = 1; m_btnPlayPause->setIcon(drawSymbolicColoredPixmap( QIcon::fromTheme("media-playback-pause-symbolic").pixmap(QSize(64, 64)), "white")); m_labelBopian->resetRotation(); if (m_labelAlbum->isVisible()) { m_labelAlbum->startRotation(20000); } } else { if (status == 2) { m_nPlayState = 0; } else { m_nPlayState = -1; } m_btnPlayPause->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("ukui-play-full-symbolic").pixmap(QSize(64, 64)), "white")); m_labelBopian->rotateCounterClockwise(); if (m_labelAlbum->isVisible()) { m_labelAlbum->pauseRotation(); } } } void MPRISSaverWidget::onPlaybackStatusChanged() { if (!m_mprisDbusClient) { return; } const QString status = m_mprisDbusClient->playbackStatus(); qDebug() << "PlayStatus:" << status; if (status == "Playing") { m_nPlayState = 1; m_btnPlayPause->setIcon(drawSymbolicColoredPixmap( QIcon::fromTheme("media-playback-pause-symbolic").pixmap(QSize(64, 64)), "white")); m_labelBopian->resetRotation(); if (m_labelAlbum->isVisible()) { m_labelAlbum->startRotation(20000); } } else { if (status == "Paused" || status == "Stopped") { m_nPlayState = 0; } else { m_nPlayState = -1; } m_btnPlayPause->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("ukui-play-full-symbolic").pixmap(QSize(64, 64)), "white")); m_labelBopian->rotateCounterClockwise(); if (m_labelAlbum->isVisible()) { m_labelAlbum->pauseRotation(); } } } void MPRISSaverWidget::onCanControlChanged() { if (!m_mprisDbusClient) { return; } // m_wdgControl->setVisible(m_mprisDbusClient->canControl()); } void MPRISSaverWidget::resizeEvent(QResizeEvent *event) { if (m_labelLine) { m_labelLine->setFixedSize(width(), 1); } } void MPRISSaverWidget::updateFont(QString strFont, double fontSizeF) { // if (m_labelName) { // QFont font = m_labelName->font(); // if (!strFont.isEmpty()) { // font.setFamily(strFont); // } // font.setPointSizeF((14 + fontSizeF) * LockDialogModel::getPtToPx()); // m_labelName->setFont(font); // } // if (m_labelArtist) { // QFont font = m_labelArtist->font(); // if (!strFont.isEmpty()) { // font.setFamily(strFont); // } // font.setPointSizeF((12 + fontSizeF) * LockDialogModel::getPtToPx()); // m_labelArtist->setFont(font); // } } ukui-screensaver/src/screensaver/customalbumviewer.cpp0000664000175000017500000001505415172041106022365 0ustar fengfeng// customalbumviewer.cpp #include "customalbumviewer.h" #include "../common/commonfunc.h" #include #include #include #include #include CustomAlbumViewer::CustomAlbumViewer(QWidget *parent) : QWidget(parent) { m_mainLayout = new QVBoxLayout(this); m_mainLayout->setContentsMargins(0, 0, 0, 0); m_mainLayout->setSpacing(32); m_mainLayout->addStretch(); m_tipLabel = new CustomLabel(this); m_tipLabel->setText(tr("No photos detected, please re-select them")); m_tipLabel->setAlignment(Qt::AlignCenter); m_mainLayout->addWidget(m_tipLabel, Qt::AlignHCenter); m_topLabel = new AdaptiveLabel(this); m_topLabel->setAlignment(Qt::AlignCenter); m_topLabel->setFixedHeight(372); m_mainLayout->addWidget(m_topLabel, Qt::AlignHCenter); m_bottomLayout = new QHBoxLayout(); m_bottomLayout->setContentsMargins(0, 0, 0, 0); m_bottomLayout->setSpacing(32); m_mainLayout->addLayout(m_bottomLayout); // m_mainLayout->addStretch(); m_leftBottomLabel = new AdaptiveLabel(this); m_rightBottomLabel = new AdaptiveLabel(this); m_bottomLayout->addStretch(); m_bottomLayout->addWidget(m_leftBottomLabel); m_bottomLayout->addWidget(m_rightBottomLabel); m_bottomLayout->addStretch(); m_timer = new QTimer(this); connect(m_timer, &QTimer::timeout, this, &CustomAlbumViewer::nextSlide); } CustomAlbumViewer::~CustomAlbumViewer() { m_timer->stop(); } void CustomAlbumViewer::updateViewerSize(float scale) { if (scale == 0) { scale = m_scale; } else if (scale < 0.1) { scale = 1; } m_scale = scale; if (m_imagePaths.isEmpty()) { setFixedSize(847 * scale, 580 * scale); m_topLabel->setFixedSize(804 * scale, 512 * scale); m_tipLabel->setFixedWidth(m_topLabel->width()); m_tipLabel->setFontSize(m_scale * 22); } else if (m_imagePaths.size() < 3) { setFixedSize(847 * scale, 790 * scale); m_topLabel->setFixedSize(804 * scale, 788 * scale); } else { setFixedSize(847 * scale, 790 * scale); m_topLabel->setFixedSize(847 * scale, 372 * scale); m_leftBottomLabel->setFixedSize(406 * scale, 372 * scale); m_rightBottomLabel->setFixedSize(406 * scale, 372 * scale); } m_topLabel->setScale(scale); m_leftBottomLabel->setScale(scale); m_rightBottomLabel->setScale(scale); m_mainLayout->setSpacing(32 * scale); m_bottomLayout->setSpacing(32 * scale); } void CustomAlbumViewer::setDefaultPixmap(QPixmap pixmap) { m_defaultImage = pixmap; loadImages(m_sSelectPhotoPath); if (m_imagePaths.isEmpty()) { QPixmap defaultPix(m_defaultImage); m_topLabel->setPixmap(defaultPix); m_topLabel->show(); m_tipLabel->show(); } } QPixmap CustomAlbumViewer::scalePixmap(const QPixmap &original, const QSize &size) { return original.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } void CustomAlbumViewer::loadImages(const QString &directoryPath) { m_imagePaths.clear(); QDir directory(directoryPath); if (!directory.exists()) { qWarning() << "Directory does not exist:" << directoryPath; } QStringList filters; filters << "*.jpg" << "*.jpeg" << "*.png" << "*.bmp" << "*.gif"; QFileInfoList fileList = directory.entryInfoList(filters, QDir::Files); foreach (const QFileInfo &fileInfo, fileList) { m_imagePaths.append(fileInfo.absoluteFilePath()); } m_sSelectPhotoPath = directoryPath; } void CustomAlbumViewer::setUpdateInterval(int interval) { if (m_timer->isActive()) { m_timer->stop(); } else { nextSlide(); } m_timer->start(interval); } void CustomAlbumViewer::setImagesPath(const QString &strPath) { m_currentIndex = 0; // 重置索引 loadImages(strPath); int nInterval = -1; if (m_timer->isActive()) { nInterval = m_timer->interval(); m_timer->stop(); } if (nInterval <= 0) { setUpdateInterval(); } else { setUpdateInterval(nInterval); } } void CustomAlbumViewer::nextSlide() { loadImages(m_sSelectPhotoPath); if (m_imagePaths.isEmpty()) { QPixmap defaultPix(m_defaultImage); m_topLabel->setPixmap(defaultPix); m_topLabel->show(); m_tipLabel->show(); m_leftBottomLabel->hide(); m_rightBottomLabel->hide(); return; } m_tipLabel->hide(); displayImages(); } void CustomAlbumViewer::displayImages() { if (m_imagePaths.size() < 3) { m_leftBottomLabel->hide(); m_rightBottomLabel->hide(); int index = m_isSequential ? m_currentIndex % m_imagePaths.size() : QRandomGenerator::global()->bounded(m_imagePaths.size()); QPixmap pixmap(m_imagePaths[index]); m_topLabel->setPixmap(pixmap); m_topLabel->show(); if (m_isSequential) { m_currentIndex = (m_currentIndex + 1) % m_imagePaths.size(); } } else { m_topLabel->show(); m_leftBottomLabel->show(); m_rightBottomLabel->show(); QList indices; if (m_isSequential) { // 顺序模式 for (int i = 0; i < 3; ++i) { indices.append((m_currentIndex + i) % m_imagePaths.size()); } m_currentIndex = (m_currentIndex + 3) % m_imagePaths.size(); } else { // 随机模式 while (indices.size() < 3) { int randomIndex = QRandomGenerator::global()->bounded(m_imagePaths.size()); if (!indices.contains(randomIndex)) { indices.append(randomIndex); } } } QPixmap topPixmap(m_imagePaths[indices[0]]); m_topLabel->setPixmap(topPixmap); QPixmap leftBottompixmap(m_imagePaths[indices[1]]); m_leftBottomLabel->setPixmap(leftBottompixmap); QPixmap rightBottompixmap(m_imagePaths[indices[2]]); m_rightBottomLabel->setPixmap(rightBottompixmap); } Q_EMIT pixmapChanged(); } void CustomAlbumViewer::stopDisplayImages() { if (m_timer->isActive()) { m_timer->stop(); } } void CustomAlbumViewer::setSequentialMode(bool sequential) { m_isSequential = sequential; m_currentIndex = 0; // 重置索引 int nInterval = -1; if (m_timer->isActive()) { nInterval = m_timer->interval(); m_timer->stop(); } if (nInterval <= 0) { setUpdateInterval(); } else { setUpdateInterval(nInterval); } } ukui-screensaver/src/screensaver/networkwatcher.h0000664000175000017500000000267615172041106021332 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef NETWORKWATCHER_H #define NETWORKWATCHER_H #include #include #include enum NMState { NM_STATE_UNKNOWN = 0, NM_STATE_ASLEEP = 10, NM_STATE_DISCONNECTED = 20, NM_STATE_DISCONNECTING = 30, NM_STATE_CONNECTING = 40, NM_STATE_CONNECTED_LOCAL = 50, NM_STATE_CONNECTED_SITE = 60, NM_STATE_CONNECTED_GLOBAL = 70, NM_STATE_CONNECTED_LIMIT = 80 }; class NetWorkWatcher : public QObject { Q_OBJECT public: NetWorkWatcher(QObject *parent = nullptr); Q_SIGNALS: void NetworkStateChanged(uint state); private Q_SLOTS: void onNetworkStateChanged(uint state); void getIp(QHostInfo info); public: void checkOnline(); bool isConnect(); private: QDBusInterface *network; }; #endif ukui-screensaver/src/screensaver/mprisdbusclient.cpp0000664000175000017500000000324615172041106022017 0ustar fengfeng #include "mprisdbusclient.h" #define MPRIS_DBUS_INTERFACE "org.mpris.MediaPlayer2.Player" MPRISDBusClient::MPRISDBusClient( const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) : QDBusAbstractInterface(service, path, MPRIS_DBUS_INTERFACE, connection, parent) { QDBusConnection::sessionBus().connect( this->service(), this->path(), "org.freedesktop.DBus.Properties", "PropertiesChanged", "sa{sv}as", this, SLOT(onPropertyChanged(QDBusMessage))); } MPRISDBusClient::~MPRISDBusClient() { QDBusConnection::sessionBus().disconnect( service(), path(), "org.freedesktop.DBus.Properties", "PropertiesChanged", "sa{sv}as", this, SLOT(onPropertyChanged(QDBusMessage))); } void MPRISDBusClient::onPropertyChanged(const QDBusMessage &msg) { QList arguments = msg.arguments(); if (3 != arguments.count()) return; QString interfaceName = msg.arguments().at(0).toString(); if (interfaceName != MPRIS_DBUS_INTERFACE) return; QVariantMap changedProps = qdbus_cast(arguments.at(1).value()); for (const QString &prop : changedProps.keys()) { const QMetaObject *self = metaObject(); for (int i = self->propertyOffset(); i < self->propertyCount(); ++i) { QMetaProperty p = self->property(i); QGenericArgument value(QMetaType::typeName(p.type()), const_cast(changedProps[prop].constData())); if (p.name() == prop) { Q_EMIT p.notifySignal().invoke(this, value); } } } } ukui-screensaver/src/screensaver/customplugin.h0000664000175000017500000000245615172041106021010 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef CUSTOMPLUGIN_H #define CUSTOMPLUGIN_H #include "screensaverplugin.h" #include class CustomPlugin : public QObject, ScreensaverPlugin { Q_OBJECT //声明QT识别的唯一标识符 Q_PLUGIN_METADATA(IID "org.ukui.screensaver.screensaver-default1.0.0") //声明实现的插件接口 Q_INTERFACES(ScreensaverPlugin) public: CustomPlugin(QObject *parent = 0); QString name() const override; QWidget *createWidget(bool isScreensaver, QPixmap *background, QWidget *parent) override; QString displayName() const override; }; #endif // CUSTOMPLUGIN_H ukui-screensaver/src/screensaver/screensaverplugin.h0000664000175000017500000000306215172041106022010 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVER_PLUGIN_H #define SCREENSAVER_PLUGIN_H #include class ScreensaverPlugin { public: virtual ~ScreensaverPlugin() {} //插件实例的名称 virtual QString name() const = 0; //创建UI的实例 virtual QWidget *createWidget(bool isScreensaver, QPixmap *background, QWidget *parent) = 0; //获得插件的展示名称 virtual QString displayName() const = 0; }; //定义了在QT系统中该接口的全局唯一的ID //实现该SDK的插件也要定义相同的ID //接口的ID中包含了版本信息,通过该ID我们可以区别不同版本的SDK和插件 // Q_DECLARE_INTERFACE宏将类型和ID关联起来,这样QT就可以验证加载的插件是否可以转换成MyPluginInterface类型 #define interface_iid "org.ukui.screensaver.screensaver-default1.0.0" Q_DECLARE_INTERFACE(ScreensaverPlugin, interface_iid) #endif // FILTER_H ukui-screensaver/src/screensaver/checkbutton.h0000664000175000017500000000341115172041035020561 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef CHECKBUTTON_H #define CHECKBUTTON_H #include #include #include #include #include #include class checkButton : public QFrame { Q_OBJECT public: checkButton(QWidget *parent = 0); void setChecked(bool checked); bool isChecked(); protected: void mousePressEvent(QMouseEvent *); void resizeEvent(QResizeEvent *); void paintEvent(QPaintEvent *); private: bool checked; QColor borderColorOff; QColor bgColorOff; QColor bgColorOn; QColor sliderColorOff; QColor sliderColorOn; int space; //滑块离背景间隔 int rectRadius; //圆角角度 int step; //移动步长 int startX; int endX; QTimer *timer; void drawBg(QPainter *painter); void drawSlider(QPainter *painter); private Q_SLOTS: void updatevalue(); Q_SIGNALS: void checkedChanged(bool checked); }; #endif // CHECKBUTTON_H ukui-screensaver/src/screensaver/saverthumbnailwidget.h0000664000175000017500000000232715172041106022504 0ustar fengfeng#ifndef SAVERTHUMBNAILWIDGET_H #define SAVERTHUMBNAILWIDGET_H #include "timethumbnailwidget.h" #include "masklable.h" #include #include #include class SaverThumbnailWidget : public QWidget { Q_OBJECT public: explicit SaverThumbnailWidget(QWidget *parent = nullptr, bool isControlSaver = false); void setBackground(QPixmap background); void setSelectStatus(bool status); void setDictumText(QString topText, QString bottomText, QString authorText); private: void initUI(); void animateSize(const QSize &targetSize); protected: void paintEvent(QPaintEvent *event); bool eventFilter(QObject *watched, QEvent *event); Q_SIGNALS: void defaultSaverModeClicked(); private: TimeThumbnailWidget* m_timeThumbnailWidget = nullptr; QWidget *m_dictumWidget = nullptr; QLabel *m_topLabel = nullptr; QLabel *m_bottomLabel = nullptr; QLabel *m_authorLabel = nullptr; QVBoxLayout* m_mainLayout = nullptr; QPixmap m_background; QVariantAnimation* m_animation; MaskLable* m_maskLabel = nullptr; bool m_bHoverStatus = false; bool m_bSelectStatus = false; bool m_bControlSaver = false; }; #endif // SAVERTHUMBNAILWIDGET_H ukui-screensaver/src/screensaver/screensaver.h0000664000175000017500000002065715172041106020602 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVER_H #define SCREENSAVER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef ARCHITECTUREISSW64 #include #include #endif #include "sleeptime.h" #include "chinesedate.h" #include "mbackground.h" #include "checkbutton.h" #include "scconfiguration.h" #include "cyclelabel.h" #include "videoplayer.h" #include "graphicsview.h" #include "mediasaverwidget.h" #include "playcontroller.h" #include "weatherwidget.h" #include "customalbumviewer.h" #include "musicthumbnailwidget.h" #include "albumthumbnailwidget.h" #include "saverthumbnailwidget.h" #include "weatherthumbnailwidget.h" #include "statusbutton.h" class WeatherManager; enum dateinfo { DATE, TIME, }; enum SAVERERRORTYPE { VIDEOTOOLARGE, VIDEOFORMATEERROR, RESOLUTIONERROR, URLADDRESSERROR, PLAYERSHOWERROR, NORMALSAVER }; class Screensaver : public QWidget { Q_OBJECT public: explicit Screensaver(bool isScreensaver, QPixmap *originalBackground = nullptr, QWidget *parent = 0); ~Screensaver(); void addClickedEvent(); private: void initUI(); void setDatelayout(); void setWeatherLayout(); void setSleeptime(bool Isshow); void setCenterWidget(); void initDynamic(); void updateDate(); void setUpdateCenterWidget(); void startSwitchImages(); void stopSwitchImages(); void setRandomText(); void setPreviewText(bool bVisible); //设置预览样式 void setRandomPos(); void getConfiguration(); void connectSingles(); void updateBackgroundPath(); void enterEvent(QEvent *); void leaveEvent(QEvent *); void isMovie(); void getVideoFormat(QString fileName); //获取视频格式(视频格式、编码格式、分辨率) QString getLongFormatDate(int type); /** * @brief 获取动态屏保视频是否存在 * @param * @return */ void getVideoExistence(); bool getIsValidUrl(const QString &urlStr); //网页是否合法 #ifndef ARCHITECTUREISSW64 void initUrlWidget(); #endif void initDynamicSaver(QString path); void initOtherWidget(); // 缩略选择窗口 void initThumBnalListWidget(); // 天气 void initWeatherWidget(); // 音乐播放器 void initMediaControlWidget(); // 相册 void initCustomAlbumViewer(); QTimer *switchTimer; QTimer *fadeTimer; QStringList imagePaths; QString backgroundPath; int cycleTime; float opacity; bool isCustom; bool isCShowRestTime; bool isUShowRestTime; bool textIsCenter; QString myText; QLabel *myTextLabel; QLabel *myPreviewLabel; QLabel *cycleLabel; SCConfiguration *configuration; QWidget *myTextWidget; QLabel *dateOfLocaltime; QLabel *dateOfWeek; QLabel *dateOfLocaltimeHour; QLabel *dateofLocaltimeColon; QLabel *dateOfLocaltimeMinute; QLabel *dateOfDay; QLabel *dateOfLunar; QWidget *m_centerWidget; QWidget *timeLayout = nullptr; QWidget *m_widgetDatetime = nullptr; SleepTime *sleepTime; QTimer *timer; QPixmap background; QPixmap m_scaleBackground; QPushButton *settingsButton; QPushButton *WallpaperButton; QWidget *buttonWidget; QSettings *qsettings; MBackground *m_background; QLabel *centerlabel1 = nullptr; QLabel *centerlabel2 = nullptr; QLabel *authorlabel = nullptr; checkButton *checkSwitch; QLabel *autoSwitchLabel; QFrame *autoSwitch; QFrame *vboxFrame; bool isAutoSwitch; int flag; bool hasChanged; int timeType; QString dateType; QProcess *process; QLabel *screenLabel; bool respondClick; static QTime m_currentTime; int blur_Num; QString curStyle; double curFontSize; double m_ptToPx = 1.0; QFont sysFont; WeatherManager *m_weatherManager; QWidget *m_weatherLaout; QLabel *m_weatherIcon; QLabel *m_weatherArea; QLabel *m_weatherCond; QLabel *m_weatherTemperature; QWidget *m_widgetNotice; QLabel *m_labelNoticeIcon; QLabel *m_labelNoticeMessage; int currentIndex = 0; QString currentPath; bool is_gif = false; QList currentPixmap; int delayTime; QTimer *movieTimer = nullptr; int currentCount = 0; QDateTime m_lastDateTime; bool isScreensaver = false; bool m_isShowDateTime = true; /** 是否显示日期时间 */ bool m_isInitUICompleted = false; //动态屏保 bool m_isDynamicSaver = false; KyVideoPlayer *m_pVideoPlayer = nullptr; QGraphicsScene *m_playerScene = nullptr; GraphicsView *m_playerView = nullptr; QGraphicsVideoItem *m_playerItem = nullptr; int video_Duration = 0; QString cur_video_format; QString video_Codetype; int cur_video_Width; int cur_video_Height; QString m_videoPath; QString m_videoFormat; int m_videoSize; int m_videoWidth; int m_videoHeight; int m_saverState = SAVERERRORTYPE::NORMALSAVER; QString m_sPlayerShowErrorTip; QString m_strPreViewTrans; //网页显示 #ifndef ARCHITECTUREISSW64 QWebEngineView *m_pWebEngineView = nullptr; QWidget *m_pCenterUrlWidget = nullptr; QLabel *m_pErrorUrlLabel = nullptr; #endif bool m_isUrlSaver = false; // 智慧屏保 int m_nSubMode = -1; int m_nWeatherPosition = 0; bool m_bFollowSystem = false; bool m_bSaverMusic = false; QString m_sMusicPath; int m_nPhotoCycleMode = -1; int m_nPhotoCycleInterval = -1; QStringList m_sDefaultPhotoPath; QStringList m_sCustomPhptoPath; QStringList m_sAllPhotoPath; QString m_sSelectPhotoPath; int m_iPhotoSwitchMode = 0; int m_iPhotoSwitchTime = 0; WeatherWidget *m_weatherWidget = nullptr; QWidget *m_widgetMediaControl = nullptr; MediaSaverWidget *m_mediaControlWidget = nullptr; CustomAlbumViewer *m_customAlbumViewer = nullptr; QLabel *m_smartSaverLabel = nullptr; QListWidget *m_thumBnalListWidget = nullptr; MusicThumbnailWidget *m_musicWidget = nullptr; AlbumThumbnailWidget *m_albunWidget = nullptr; SaverThumbnailWidget *m_defaulSaverWidget = nullptr; WeatherThumbnailWidget *m_weatherThumbnailWidget = nullptr; StatusButton *m_sidebarBtn = nullptr; QTimer *m_hideThumBnalWidgetTimer = nullptr; protected: void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *event); bool eventFilter(QObject *obj, QEvent *event); private Q_SLOTS: void updateTime(); void onIsCustomModeChanged(bool custom); void autoSwitchChanged(bool iswitch); void backgroundPathChanged(QString path); void backgroundChanged(QString path); void cycleTimeChanged(int cTime); void myTextChanged(QString text); void showCRestTimeChanged(bool isShow); void showURestTimeChanged(bool isShow); void textIsCenterChanged(bool isCenter); void getWeatherFinish(QString city, QString cond, QString tmp); void onTimeFormatChanged(int type); void onDateFormatChanged(QString type); void onBlurNumChanged(int num); void onStyleChanged(QString style); void onFontSizeChanged(int fontSize); QPixmap getPaddingPixmap(); void onScreensaverDatetimeEnableChanged(bool isEnable, const QString &strUserName); void onSubModeChanged(int mode); void onMusicFollowSystemChanged(bool b); void onWeatherPositionChanged(int position); void onMusicPathChanged(QString path); void onPhotoCycleModeChanged(int mode); void onPhotoCycleIntervalChanged(int interval); void onPhotoPathChanged(QString path); void onPhotoSelectPathChanged(QString path); }; #endif // MAINWINDOW_H ukui-screensaver/src/screensaver/albumthumbnailwidget.h0000664000175000017500000000230215172041106022455 0ustar fengfeng#ifndef ALBUMTHUMBNAILWIDGET_H #define ALBUMTHUMBNAILWIDGET_H #include "customalbumviewer.h" #include "timethumbnailwidget.h" #include "masklable.h" #include #include #include #include #include class AlbumThumbnailWidget : public QWidget { Q_OBJECT public: explicit AlbumThumbnailWidget(QWidget *parent = nullptr, bool isControlSaver = false); void setBackground(QPixmap background); void setDefaultPath(QString defaultPath); void setDefaultPhoto(QPixmap photo); void setSelectStatus(bool status); private: void initUI(); void animateSize(const QSize &targetSize); protected: void paintEvent(QPaintEvent *event); bool eventFilter(QObject *watched, QEvent *event); Q_SIGNALS: void albumModeClicked(); private: TimeThumbnailWidget *m_timeThumbnailWidget = nullptr; CustomAlbumViewer *m_customAlbumViewer = nullptr; QHBoxLayout *m_mainLayout = nullptr; QPixmap m_background; MaskLable *m_maskLabel = nullptr; QVariantAnimation *m_animation; bool m_bHoverStatus = false; bool m_bSelectStatus = false; bool m_bControlSaver = false; }; #endif // ALBUMTHUMBNAILWIDGET_H ukui-screensaver/src/screensaver/playcontroller.h0000664000175000017500000000327115172041106021324 0ustar fengfeng#ifndef PLAYCONTROLLER_H #define PLAYCONTROLLER_H #include #include #include #include #include "mmediaplayer.h" #include "mmediaplaylist.h" class playController : public QObject { Q_OBJECT public: enum PlayState { STOP_STATE = 0, PLAY_STATE = 1, PAUSED_STATE = 2 }; public: static playController& getInstance() { static playController instance; return instance; } void cleanup() { if (m_playlist != nullptr) { delete m_playlist; m_playlist = nullptr; } if (m_player != nullptr) { m_player->stop(); delete m_player; m_player = nullptr; } } explicit playController(QObject *parent = nullptr); ~playController(); bool play(QString playlist, int index); bool play(); bool pause(); bool pauseOnly(); bool stop(); void nextSong(); void previousSong(); void setSongIndex(int index); void setCurPlaylist(QStringList songPaths); QVariantMap getMetadata() const; void addMusicToDatebase(QStringList fileNames); Q_SIGNALS: void playerStateChange(playController::PlayState newState); void mateDataChanged(); //媒体切换信号 void singalChangePath(QString path); // void curIndexChanged(int index); private Q_SLOTS: //状态改变 void slotStateChanged(MMediaPlayer::State newState); void slotIndexChange(int index); private: //当前播放列表名 QString m_curList; //当前播放的索引 int m_curIndex; MMediaPlayer* m_player = nullptr; MMediaPlaylist* m_playlist = nullptr; }; #endif // PLAYCONTROLLER_H ukui-screensaver/src/screensaver/musicthumbnailwidget.cpp0000664000175000017500000002101215172041106023027 0ustar fengfeng#include "musicthumbnailwidget.h" #include "commonfunc.h" #include #include #include #include MusicThumbnailWidget::MusicThumbnailWidget(QWidget *parent, bool isControlSaver) : QWidget{parent}, m_bControlSaver(isControlSaver) { initUI(); installEventFilter(this); } void MusicThumbnailWidget::setBackground(QPixmap background) { m_background = background; repaint(); } void MusicThumbnailWidget::initUI() { m_mainLayout = new QHBoxLayout(this); if (m_bControlSaver ) { setFixedSize(201, 113); } else { setFixedSize(292, 164); } m_timeThumbnailWidget = new TimeThumbnailWidget(this, m_bControlSaver); m_mainLayout->addWidget(m_timeThumbnailWidget); m_mprisSaverWidget = new QWidget(this); QVBoxLayout* mainLayout = new QVBoxLayout(); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); QLabel* m_pLabelDitu = new QLabel(this); if (!m_bControlSaver ) { QPixmap m_pixmapAlbumDefault = QPixmap(":/assets/ditu.png").scaled(78, 78, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); m_pLabelDitu->setPixmap(m_pixmapAlbumDefault); m_pLabelDitu->setFixedSize(78, 78); } else { QPixmap m_pixmapAlbumDefault = QPixmap(":/assets/ditu.png").scaled(52, 52, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); m_pLabelDitu->setPixmap(m_pixmapAlbumDefault); m_pLabelDitu->setFixedSize(104, 104); } mainLayout->addWidget(m_pLabelDitu); QHBoxLayout* m_albumLayout = new QHBoxLayout(m_pLabelDitu); m_albumLayout->setSpacing(0); if (!m_bControlSaver ) { m_albumLayout->setContentsMargins(22, 3, 7, 12); } else { m_albumLayout->setContentsMargins(10, 2, 5, 8); } QLabel* m_labelBopian = new QLabel(this); QPixmap pixmapBopian; if (!m_bControlSaver ) { m_labelBopian->setFixedSize(17, 29); pixmapBopian = QPixmap(":/assets/bogan.png").scaled(17, 29, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } else { m_labelBopian->setFixedSize(11, 19); pixmapBopian = QPixmap(":/assets/bogan.png").scaled(11, 19, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } m_labelBopian->setAlignment(Qt::AlignLeft | Qt::AlignTop); m_labelBopian->setPixmap(pixmapBopian); m_albumLayout->addWidget(m_labelBopian, 0, Qt::AlignRight | Qt::AlignTop); QHBoxLayout* m_buttonLayout = new QHBoxLayout(); m_buttonLayout->setContentsMargins(0, 0, 0, 0); m_buttonLayout->setSpacing(8); m_buttonLayout->addStretch(); QPushButton* m_btnPrev = new QPushButton(this); m_btnPrev->setFlat(true); QPixmap prevIcon; m_btnPrev->setFixedSize(9, 9); prevIcon = drawSymbolicColoredPixmap( QIcon::fromTheme("media-skip-backward-symbolic").pixmap(QSize(7, 7)), "white"); m_btnPrev->setIconSize(QSize(7, 7)); m_btnPrev->setIcon(prevIcon); m_btnPrev->setStyleSheet( QString("QPushButton{border-radius: 6px; border:0px solid white; background-color: rgba(0,0,0,0);}" "QPushButton::hover{background-color: rgba(0,0,0,15%);}" "QPushButton::pressed {background-color: rgba(0,0,0,21%);}")); m_buttonLayout->addWidget(m_btnPrev); QPushButton* m_btnPlayPause = new QPushButton(this); m_btnPlayPause->setFlat(true); m_btnPlayPause->setFixedSize(9, 9); QPixmap playPauseIcon = drawSymbolicColoredPixmap( QIcon::fromTheme("ukui-play-full-symbolic").pixmap(QSize(9, 9)), "white"); m_btnPlayPause->setIcon(playPauseIcon); m_btnPlayPause->setIconSize(QSize(9, 9)); m_btnPlayPause->setStyleSheet( QString("QPushButton{border-radius: 6px; border:0px solid white; background-color: rgba(0,0,0,0);}" "QPushButton::hover{background-color: rgba(0,0,0,15%);}" "QPushButton::pressed {background-color: rgba(0,0,0,21%);}")); m_buttonLayout->addWidget(m_btnPlayPause); QPushButton* m_btnNext = new QPushButton(this); m_btnNext->setFlat(true); m_btnNext->setFixedSize(9, 9); QPixmap nextIcon = drawSymbolicColoredPixmap( QIcon::fromTheme("media-skip-forward-symbolic").pixmap(QSize(9, 9)), "white"); m_btnNext->setIcon(nextIcon); m_btnNext->setIconSize(QSize(7, 7)); m_btnNext->setStyleSheet( QString("QPushButton{border-radius: 6px; border:0px solid white; background-color: rgba(0,0,0,0);}" "QPushButton::hover{background-color: rgba(0,0,0,15%);}" "QPushButton::pressed {background-color: rgba(0,0,0,21%);}")); m_buttonLayout->addWidget(m_btnNext); m_buttonLayout->addStretch(); mainLayout->addLayout(m_buttonLayout); m_mprisSaverWidget->setLayout(mainLayout); if (!m_bControlSaver ) { m_mprisSaverWidget->setFixedSize(78, 93); } else { m_mprisSaverWidget->setFixedSize(52, 80); } m_mainLayout->addWidget(m_mprisSaverWidget, Qt::AlignVCenter); m_maskLabel = new MaskLable(this, m_bControlSaver); m_maskLabel->setGeometry(0, 0, this->width(), this->height()); m_maskLabel->hide(); if (!m_bControlSaver) { m_animation = new QVariantAnimation(this); m_animation->setDuration(300); m_animation->setEasingCurve(QEasingCurve::OutQuad); connect(m_animation, &QVariantAnimation::valueChanged, [this](const QVariant &value) { QSize size = value.toSize(); setFixedSize(size); m_maskLabel->setFixedSize(size); }); } } void MusicThumbnailWidget::animateSize(const QSize &targetSize) { m_animation->stop(); m_animation->setStartValue(size()); m_animation->setEndValue(targetSize); m_animation->start(); } void MusicThumbnailWidget::setSelectStatus(bool status) { if (!status && m_bSelectStatus) { m_maskLabel->hide(); m_bSelectStatus = false; } else if (status && !m_bSelectStatus) { m_maskLabel->show(); m_bSelectStatus = true; } } bool MusicThumbnailWidget::eventFilter(QObject *watched, QEvent *event) { if (parentWidget() && !parentWidget()->isEnabled()) { event->ignore(); return QWidget::eventFilter(watched, event); } if (event->type() == QEvent::Enter) { if (!m_bControlSaver) { animateSize(QSize(320, 180)); m_bHoverStatus = true; } if (m_bControlSaver || m_bSelectStatus) { m_maskLabel->show(); } } else if (event->type() == QEvent::Leave) { if (!m_bControlSaver) { animateSize(QSize(292, 164)); m_bHoverStatus = false; } if (!m_bSelectStatus) { m_maskLabel->hide(); } } if (event->type() == QEvent::MouseButtonRelease) { if (!m_bControlSaver) { m_maskLabel->show(); m_bSelectStatus = true; } m_maskLabel->show(); Q_EMIT musicModeClicked(); } return QWidget::eventFilter(watched, event); } void MusicThumbnailWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); if (!m_bControlSaver) { QPainterPath backgroundPath; backgroundPath.addRoundedRect(rect(), 8, 8); painter.setClipPath(backgroundPath); painter.drawPixmap(this->rect(), m_background, m_background.rect()); painter.setClipping(false); if (m_bHoverStatus) { QPen pen(QColor(255, 255, 255, 153)); pen.setWidth(2); painter.setPen(pen); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 8, 8); } else { painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } else { painter.fillRect(rect(), palette().color(QPalette::Base)); const QRect bgRect(4, 4, 193, 105); QPainterPath path; path.addRoundedRect(bgRect, 8, 8); painter.setClipPath(path); painter.drawPixmap(bgRect, m_background, m_background.rect()); painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } ukui-screensaver/src/screensaver/musicfileinformation.cpp0000664000175000017500000003400415172041106023032 0ustar fengfengextern "C" { #include #include } #include #include #include #include "musicfileinformation.h" #include "ukui-log4qt.h" MusicFileInformation::MusicFileInformation(QObject *parent) : QObject(parent) { m_musicType << "*.voc" << "*.aiff" << "*.au" << "*.dts" << "*.flv" << "*.m4r" << "*.mka" << "*.mmf" << "*.mp2" << "*.mp4" << "*.mpa" << "*.wv" << "*.mp3" << "*.ogg" << "*.wma" << "*.amr" << "*.flac" << "*.wav" << "*.ape" << "*.m4a" << "*.ac3" << "*.aac"; } QStringList MusicFileInformation::getMusicType() { return m_musicType; } void MusicFileInformation::addFile(const QStringList &addFile) { qDebug() << "addFile"; int count = 0; int failCount = 0; resList.clear(); for(auto &filepath : addFile) { if(filepath.isEmpty()) { continue; } //过滤掉CD和远程目录 // if(filepath.startsWith("/run/user/")) // { // continue; // } //过滤U盘路径 // if(filepath.startsWith("/media/")) // { // continue; // } QFileInfo fileInfo(filepath); if(fileInfo.isDir()) { //适合用于大目录 QDirIterator iter(filepath,m_musicType,QDir::Files,QDirIterator::Subdirectories); while (iter.hasNext()) { count++; QString strpath = iter.next(); musicdataStruct.filepath = strpath; fileInfo.setFile(musicdataStruct.filepath); fileType(fileInfo); //文件类型 fileSize(fileInfo); //文件大小 if(m_musicType.contains("*." + musicdataStruct.filetype)) { fileInformation(musicdataStruct.filepath);//获取歌曲文件信息 if(musicdataStruct.time != "") { resList.append(musicdataStruct); } else { failCount++; } } else { failCount++; } } } else if(fileInfo.isFile()) { count++; musicdataStruct.filepath = filepath; fileInfo.setFile(musicdataStruct.filepath); fileType(fileInfo); //文件类型 fileSize(fileInfo); //文件大小 if(m_musicType.contains("*." + musicdataStruct.filetype)) { fileInformation(musicdataStruct.filepath);//获取歌曲文件信息 if(musicdataStruct.time != "") { resList.append(musicdataStruct); } else { failCount++; } } else { failCount++; } } } musicCount = count; m_failCount = failCount; } int MusicFileInformation::getCount() { return musicCount; } int MusicFileInformation::getFailCount() { return m_failCount; } QString MusicFileInformation::filterTextCode(QString str) { QByteArray strdata =str.toLocal8Bit(); int len = strdata.length(); int num = 0; int i = 0; while (i < len) { if ((strdata[i] & 0x80) == 0x00) { // 0XXX_XXXX i++; continue; } else if ((num = preNum(strdata[i])) > 2) { // 110X_XXXX 10XX_XXXX // 1110_XXXX 10XX_XXXX 10XX_XXXX // 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX // 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX // 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX // preNUm() 返回首个字节8个bits中首�?0bit前面1bit的个数,该数量也是该字符所使用的字节数 i++; for(int j = 0; j < num - 1; j++) { //判断后面num - 1 个字节是不是都是10开 if ((strdata[i] & 0xc0) != 0x80) { return ""; } i++; } } else { //其他情况说明不是utf-8 return ""; } } return str; } inline int MusicFileInformation::preNum(unsigned char byte) { unsigned char mask = 0x80; int num = 0; for (int i = 0; i < 8; i++) { if ((byte & mask) == mask) { mask = mask >> 1; num++; } else { break; } } return num; } QStringList MusicFileInformation::fileInformation(QString filepath) { QFileInfo fileInfo(filepath); QStringList information; QByteArray byteArray = filepath.toLocal8Bit(); TagLib::FileRef f(byteArray.data()); if(f.isNull()) { musicdataStruct.time = ""; AVFormatContext *pFormatCtx = NULL; avformat_open_input(&pFormatCtx, filepath.toStdString().c_str(), nullptr, nullptr); if(pFormatCtx) { avformat_find_stream_info(pFormatCtx, nullptr); qint64 duration = pFormatCtx->duration / 1000; if(duration > 0) { duration = duration / 1000; // QTime me(0,(duration/60000) % 60,(duration / 1000) % 60); // QString length = me.toString("mm:ss"); int h = duration / 3600; QString hour = QString("%1").arg(duration / 3600, 2, 10, QChar('0')); QString minutes = QString("%1").arg(duration % 3600 / 60, 2, 10, QChar('0')); QString seconds = QString("%1").arg(duration % 60, 2, 10, QChar('0')); if(h > 0) { musicdataStruct.time = QString("%1:%2:%3").arg(hour).arg(minutes).arg(seconds); } else { musicdataStruct.time = QString("%1:%2").arg(minutes).arg(seconds); } musicdataStruct.title = fileInfo.completeBaseName(); musicdataStruct.singer = tr("Unknown singer"); musicdataStruct.album = tr("Unknown album"); information << musicdataStruct.title << musicdataStruct.singer << musicdataStruct.album << musicdataStruct.time; return information; } } else { QStringList str; musicdataStruct.singer = ""; musicdataStruct.album = ""; musicdataStruct.title = ""; musicdataStruct.time = ""; str << musicdataStruct.title << musicdataStruct.singer << musicdataStruct.album << musicdataStruct.time; return str; } avformat_close_input(&pFormatCtx); avformat_free_context(pFormatCtx); } if (f.file() == nullptr) { KyInfo() << "f.file == nullptr"; QStringList str; musicdataStruct.singer = ""; musicdataStruct.album = ""; musicdataStruct.title = ""; musicdataStruct.time = ""; str << musicdataStruct.title << musicdataStruct.singer << musicdataStruct.album << musicdataStruct.time; return str; } TagLib::PropertyMap propertyMap = f.file() ->properties(); QString musicName = propertyMap["TITLE"].toString().toCString(true); if(filterTextCode(musicName).isEmpty()) musicName = fileInfo.completeBaseName(); QString musicSinger = propertyMap["ARTIST"].toString().toCString(true); if(filterTextCode(musicSinger).isEmpty()) musicSinger = tr("Unknown singer"); QString musicAlbum = propertyMap["ALBUM"].toString().toCString(true); if(filterTextCode(musicAlbum).isEmpty()) musicAlbum = tr("Unknown album"); TagLib::AudioProperties *properties = f.audioProperties(); if(properties == nullptr) { QStringList str; musicdataStruct.singer = ""; musicdataStruct.album = ""; musicdataStruct.title = ""; musicdataStruct.time = ""; str << musicdataStruct.title << musicdataStruct.singer << musicdataStruct.album << musicdataStruct.time; return str; } int h = properties->length() / 3600; if( h <= 0) { musicdataStruct.time = ""; AVFormatContext *pFormatCtx = NULL; avformat_open_input(&pFormatCtx, filepath.toStdString().c_str(), nullptr, nullptr); if(pFormatCtx) { avformat_find_stream_info(pFormatCtx, nullptr); qint64 duration = pFormatCtx->duration / 1000; if(duration > 0) { duration = duration / 1000; int h = duration / 3600; QString hour = QString("%1").arg(duration / 3600, 2, 10, QChar('0')); QString minutes = QString("%1").arg(duration % 3600 / 60, 2, 10, QChar('0')); QString seconds = QString("%1").arg(duration % 60, 2, 10, QChar('0')); if(h > 0) { musicdataStruct.time = QString("%1:%2:%3").arg(hour).arg(minutes).arg(seconds); } else { musicdataStruct.time = QString("%1:%2").arg(minutes).arg(seconds); } musicdataStruct.title = musicName; musicdataStruct.singer = musicSinger; musicdataStruct.album = musicAlbum; information << musicdataStruct.title << musicdataStruct.singer << musicdataStruct.album << musicdataStruct.time; avformat_close_input(&pFormatCtx); avformat_free_context(pFormatCtx); return information; } } else { QStringList str; musicdataStruct.singer = ""; musicdataStruct.album = ""; musicdataStruct.title = ""; musicdataStruct.time = ""; str << musicdataStruct.title << musicdataStruct.singer << musicdataStruct.album << musicdataStruct.time; return str; } avformat_close_input(&pFormatCtx); avformat_free_context(pFormatCtx); } QString hour = QString("%1").arg(properties->length() / 3600, 2, 10, QChar('0')); QString minutes = QString("%1").arg(properties->length() % 3600 / 60, 2, 10, QChar('0')); QString seconds = QString("%1").arg(properties->length() % 60, 2, 10, QChar('0')); if(h > 0) { musicdataStruct.time = QString("%1:%2:%3").arg(hour).arg(minutes).arg(seconds); } else { musicdataStruct.time = QString("%1:%2").arg(minutes).arg(seconds); } // int seconds = properties->length() % 60; // int minutes = (properties->length() - seconds) / 60; // QTime time(0,minutes,seconds); //// QString musicTime = QString::number(minutes)+":"+QString("%1").arg(seconds, 2, 10, QChar('0')); // QString musicTime = time.toString("mm:ss"); musicdataStruct.title = musicName; musicdataStruct.singer = musicSinger; musicdataStruct.album = musicAlbum; QStringList audioFileInformation; audioFileInformation << musicdataStruct.title << musicdataStruct.singer << musicdataStruct.album << musicdataStruct.time; return audioFileInformation; } QPixmap MusicFileInformation::getCoverPhotoPixmap(QString filepath) { AVFormatContext *pFormatCtx = NULL; int nRet = avformat_open_input(&pFormatCtx, filepath.toUtf8().data(), nullptr, nullptr); if (nRet < 0) { qWarning() << "avformat_open_input failed:" << nRet; return QPixmap(); } QPixmap pixmap; QImage image; if(pFormatCtx) { if(pFormatCtx->iformat != nullptr) { for(unsigned int i = 0; i < pFormatCtx->nb_streams; i++) { if(pFormatCtx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) { AVPacket pkt = pFormatCtx->streams[i]->attached_pic; image = QImage::fromData(static_cast(pkt.data), pkt.size); break; } } } } avformat_close_input(&pFormatCtx); avformat_free_context(pFormatCtx); pixmap = QPixmap::fromImage(image); return pixmap; } QString MusicFileInformation::fileSize(QFileInfo fileInfo) { QString musicSize; qint64 size = fileInfo.size(); //文件大小 if(size/1024) { if(size/1024/1024) { if(size/1024/1024/1024) { musicSize = QString::number(size/1024/1024/1024,10)+"GB"; } else musicSize = QString::number(size/1024/1024,10)+"MB"; } else musicSize = QString::number(size/1024,10)+"KB"; } else musicSize = QString::number(size,10)+"B"; musicdataStruct.size = musicSize; return musicdataStruct.size; } bool MusicFileInformation::checkFileIsDamaged(QString filepath) { QProcess *ffmpegCheckProcess = new QProcess(this); QString cmd = "/usr/bin/ffmpeg"; QStringList args; args.append("-i"); args.append(filepath); ffmpegCheckProcess->start(cmd, args); ffmpegCheckProcess->waitForFinished(); ffmpegCheckProcess->waitForReadyRead(); QString results = QString::fromLocal8Bit(ffmpegCheckProcess->readAllStandardOutput()); KyInfo() << "ffmpeg info: "<< results; return true; } QString MusicFileInformation::fileType(QFileInfo fileInfo) { QString musicType = fileInfo.suffix().toLower(); //文件类型 musicdataStruct.filetype = musicType; return musicdataStruct.filetype; } void MusicFileInformation::durationChange(qint64 duration) { if(duration <= 0) { return; } dur = duration; success = true; Q_EMIT durations(); } int MusicFileInformation::findIndexFromPlayList(QString listname,QString filepath) { int index = -1; int ret; QList musicDateList; ret = g_db->getSongInfoListFromDB(musicDateList,listname); if(ret == DB_OP_SUCC) { for(int i = 0; i < musicDateList.size(); i++) { if(musicDateList[i].filepath == filepath) { index = i; break; } } } return index; } ukui-screensaver/src/screensaver/timethumbnailwidget.cpp0000664000175000017500000000453415172041106022657 0ustar fengfeng#include "timethumbnailwidget.h" TimeThumbnailWidget::TimeThumbnailWidget(QWidget *parent, bool isControlSaver) : QWidget{parent}, m_bControlSaver(isControlSaver) { initUI(); } TimeThumbnailWidget::~TimeThumbnailWidget() { m_timer->deleteLater(); } void TimeThumbnailWidget::initUI() { if (!m_bControlSaver ) { setFixedSize(100, 40); } else { setFixedSize(100, 30); } m_mainLayout = new QVBoxLayout(this); m_mainLayout->setContentsMargins(0, 0, 0, 0); m_mainLayout->setSpacing(2); m_timeLabel = new QLabel(this); m_timeLabel->setAlignment(Qt::AlignCenter); m_timeLabel->setStyleSheet("QLabel{color: white;}"); QFont timeFont = m_timeLabel->font(); if (!m_bControlSaver ) { timeFont.setPointSize(14); } else { timeFont.setPointSize(12); } m_timeLabel->setFont(timeFont); m_timeLabel->setText(getLongFormatDate(TIMETYPE)); m_mainLayout->addWidget(m_timeLabel); m_dayLabel = new QLabel(this); m_dayLabel->setAlignment(Qt::AlignCenter); m_dayLabel->setStyleSheet("QLabel{color: white;}"); QFont dayFont = m_dayLabel->font(); if (!m_bControlSaver ) { dayFont.setPointSize(6); } else { dayFont.setPointSize(5); } m_dayLabel->setFont(dayFont); m_dayLabel->setText(getLongFormatDate(TIMETYPE)); m_mainLayout->addWidget(m_dayLabel); setLayout(m_mainLayout); m_timer = new QTimer(this); connect(m_timer, SIGNAL(timeout()), this, SLOT(update_datatime())); this->timerStart(); } void TimeThumbnailWidget::timerStart() { m_timer->start(1000); } void TimeThumbnailWidget::timerStop() { m_timer->stop(); } QString TimeThumbnailWidget::getLongFormatDate(int type) { kdk_logn_dateinfo *dateInfo = kdk_system_login_lock_dateinfo(getenv("USER")); if (type == DATETYPE) { QString date = dateInfo->date; QString week = dateInfo->week; if (dateInfo) { kdk_free_logn_dateinfo(dateInfo); } return date + " " + week; } else if (type == TIMETYPE) { QString time = dateInfo->time; if (dateInfo) { kdk_free_logn_dateinfo(dateInfo); } return time; } } void TimeThumbnailWidget::update_datatime() { m_timeLabel->setText(getLongFormatDate(TIMETYPE)); m_dayLabel->setText(getLongFormatDate(DATETYPE)); } ukui-screensaver/src/screensaver/weatherwidget.h0000664000175000017500000000217515172041106021120 0ustar fengfeng#ifndef WEATHERWIDGET_H #define WEATHERWIDGET_H #include #include #include #include #include #include "customlabel.h" class WeatherWidget : public QWidget { Q_OBJECT public: explicit WeatherWidget(QWidget *parent = nullptr); void setCurrentWeather(QString temperature, QString weather); void setErrorTip(QString tip); void setWeatherIcon(QString weather, QPixmap weatherStateIcon); void updateWeatherSize(float scale); private: void initUI(); private: QVBoxLayout *m_mainLayout = nullptr; QHBoxLayout *m_textLayout = nullptr; QLabel *m_temperatureTextLabel = nullptr; QLabel *m_weatherTextLabel = nullptr; CustomLabel *m_netWorkTipLabel = nullptr; QHBoxLayout *m_iconLayout = nullptr; QLabel *m_outerringIconLabel = nullptr; QLabel *m_innerringIconLabel = nullptr; QHBoxLayout *m_weatherIconLayout = nullptr; QLabel *m_weatherIconLabel = nullptr; float m_scale = 0; QMap m_allLabels; QMap m_originalPixmaps; QMap m_originalFonts; }; #endif // WEATHERWIDGET_H ukui-screensaver/src/screensaver/mmediaplaylist.cpp0000664000175000017500000001345415172041106021630 0ustar fengfeng#include "mmediaplaylist.h" #include MMediaPlaylist::MMediaPlaylist(QObject *parent) : QObject(parent) { } QString MMediaPlaylist::getPlayFileName() { //异常情况:播放列表为空 if (m_playerList.isEmpty()) { return ""; } //异常情况:当前播放的媒体在列表中的位置超过列表中媒体总数量 if (m_index >= m_playerList.length()) { m_index = m_playerList.length(); return m_playerList.last().toString(); } return m_playerList.at(m_index).toString(); } int MMediaPlaylist::currentIndex() const { return m_index; } bool MMediaPlaylist::addMedia(const QUrl &items) { bool isExists = QFileInfo::exists(items.toLocalFile()); if (isExists) { // 单修改此处,会导致界面点击和实际播放不一致问题 // m_playerList.append(items); notExistFileNumber++; } m_playerList.append(items); return true; } void MMediaPlaylist::next() { //异常情况:播放列表为空 if (m_playerList.isEmpty()) { return; } //异常情况:无当前播放媒体 if (m_index < 0) { return; } switch (m_playbackMode) { case Random: m_index=randomIndex(); //异常情况:无当前播放媒体 if (m_index < 0) { return; } break; case Sequential: m_index++; if (m_index >= m_playerList.length()) { m_index = m_playerList.length() - 1; } break; default: m_index++; if (m_index >= m_playerList.length()) { m_index = 0; } break; } Q_EMIT currentIndexChanged(m_index); Q_EMIT stop(); } void MMediaPlaylist::previous() { //异常情况:播放列表为空 if (m_playerList.isEmpty()) { return; } switch (m_playbackMode) { case Random: m_index=randomIndex(); //异常情况:无当前播放媒体,或当前播放列表为空 if (m_index < 0) { return; } break; case Sequential: m_index--; if (m_index < 0) { m_index = 0; } break; default: m_index--; if (m_index < 0) { m_index = m_playerList.length() - 1; } break; } Q_EMIT currentIndexChanged(m_index); Q_EMIT stop(); } void MMediaPlaylist::setCurrentIndex(int index) { //待设置的数量和设置之前一致则不处理,默认播放第一首除外 // if (index == m_index && index != 0) { // return; // } //异常情况:要设置的媒体位置超过列表总长度 // if (index >= m_playerList.length()) { // qDebug()<<"指定位置超过列表元素数量"; // return; // } //统一所有非正常情况 if (index < 0) { index = -1; } m_index = index; Q_EMIT currentIndexChanged(m_index); } void MMediaPlaylist::setPlaybackMode(MMediaPlaylist::PlaybackMode mode) { //待设置的循环模式和设置之前一致则不处理 if (mode == m_playbackMode) { return; } m_playbackMode = mode; Q_EMIT playbackModeChanged(mode); } int MMediaPlaylist::mediaCount() const { return m_playerList.length(); } MMediaContent MMediaPlaylist::media(int index) const { //异常情况:要设置的媒体位置在列表中不存在 if (index >= m_playerList.length() || index < 0) { return MMediaContent(QUrl("")); } return MMediaContent(m_playerList.at(index)); } bool MMediaPlaylist::clear() { m_playerList.clear(); return true; } bool MMediaPlaylist::removeMedia(int pos) { //异常情况:要移出的媒体位置在列表中不存在 if (pos >= m_playerList.length() || pos < 0) { return false; } m_playerList.removeAt(pos); return true; } void MMediaPlaylist::playError() { //当前仅在存在播放列表中的媒体本地文件被删除时触发 //播放异常时,轮询所有列表中的媒体文件是否存在 for (auto url : m_playerList) { //如果发现列表中有媒体文件没被删除 if (QFileInfo::exists(url.toLocalFile())) { //如果是单曲循环则切换下一首 if (m_playbackMode == CurrentItemInLoop) { next(); } //按播放完成处理 palyFinish(); return; } } //列表中所有媒体的本地文件全部被删除了 Q_EMIT currentIndexChanged(-1); } void MMediaPlaylist::playErrorMsg(int Damage) { if (Damage == -2) { if (mediaCount() == 0) { return; } if (m_playerList.isEmpty()) { return; } if (m_index < 0) { return; } if (m_index >= m_playerList.length()) { m_index = 0; } } } void MMediaPlaylist::palyFinish() { //如果没有待播放的媒体则不处理 if (m_index < 0) { return; } //如果循环模式不是单曲循环则切换下一首 if (m_playbackMode != CurrentItemInLoop) { next(); Q_EMIT currentIndexChanged(m_index); } if(m_playbackMode == CurrentItemInLoop){ if(!QFileInfo::exists(QUrl(getPlayFileName()).toLocalFile())) next(); } Q_EMIT autoPlay(m_playbackMode); } MMediaPlaylist::PlaybackMode MMediaPlaylist::playbackMode() const { return m_playbackMode; } int MMediaPlaylist::randomIndex() { int lenPlaylist = m_playerList.length(); if (lenPlaylist == 0) { // 播放列表沒有歌曲,此时不应有可用值 return -1; } return QRandomGenerator::global()->bounded(m_playerList.length()); } MMediaContent::MMediaContent(QUrl url) { m_url = url; } QUrl MMediaContent::canonicalUrl() const { return m_url; } ukui-screensaver/src/screensaver/saverthumbnailwidget.cpp0000664000175000017500000001274715172041106023046 0ustar fengfeng#include "saverthumbnailwidget.h" #include #include #include SaverThumbnailWidget::SaverThumbnailWidget(QWidget *parent, bool isControlSaver) : QWidget{ parent }, m_bControlSaver(isControlSaver) { initUI(); installEventFilter(this); } void SaverThumbnailWidget::setBackground(QPixmap background) { m_background = background; repaint(); } void SaverThumbnailWidget::initUI() { if (m_bControlSaver) { setFixedSize(201, 113); } else { setFixedSize(292, 164); } m_mainLayout = new QVBoxLayout(this); m_mainLayout->setContentsMargins(0, 8, 0, 8); m_timeThumbnailWidget = new TimeThumbnailWidget(this, m_bControlSaver); QHBoxLayout *timeLayout = new QHBoxLayout(); timeLayout->addWidget(m_timeThumbnailWidget, 0, Qt::AlignHCenter); m_mainLayout->addLayout(timeLayout, 0); m_dictumWidget = new QWidget(this); m_dictumWidget->setFixedHeight(15); QFont dictumFont = font(); dictumFont.setPointSize(4); QVBoxLayout *dictumLayout = new QVBoxLayout(m_dictumWidget); dictumLayout->setContentsMargins(0, 0, 0, 0); dictumLayout->setSpacing(2); m_topLabel = new QLabel(m_dictumWidget); m_topLabel->setAlignment(Qt::AlignCenter); m_topLabel->setStyleSheet("color: white"); m_topLabel->setFont(dictumFont); dictumLayout->addWidget(m_topLabel); m_bottomLabel = new QLabel(m_dictumWidget); m_bottomLabel->setAlignment(Qt::AlignCenter); m_bottomLabel->setStyleSheet("color: white"); m_bottomLabel->setFont(dictumFont); dictumLayout->addWidget(m_bottomLabel); m_authorLabel = new QLabel(m_dictumWidget); m_authorLabel->setAlignment(Qt::AlignCenter); m_authorLabel->setStyleSheet("color: white"); m_authorLabel->setFont(dictumFont); dictumLayout->addWidget(m_authorLabel); m_mainLayout->addWidget(m_dictumWidget, 1, Qt::AlignCenter); m_maskLabel = new MaskLable(this, m_bControlSaver); m_maskLabel->setGeometry(0, 0, this->width(), this->height()); m_maskLabel->hide(); if (!m_bControlSaver) { m_animation = new QVariantAnimation(this); m_animation->setDuration(300); m_animation->setEasingCurve(QEasingCurve::OutQuad); connect(m_animation, &QVariantAnimation::valueChanged, [this](const QVariant &value) { QSize size = value.toSize(); setFixedSize(size); m_maskLabel->setFixedSize(size); }); } } void SaverThumbnailWidget::animateSize(const QSize &targetSize) { m_animation->stop(); m_animation->setStartValue(size()); m_animation->setEndValue(targetSize); m_animation->start(); } void SaverThumbnailWidget::setSelectStatus(bool status) { if (!status && m_bSelectStatus) { m_maskLabel->hide(); m_bSelectStatus = false; } else if (status && !m_bSelectStatus) { m_maskLabel->show(); m_bSelectStatus = true; } } void SaverThumbnailWidget::setDictumText(QString topText, QString bottomText, QString authorText) { m_topLabel->setText(topText); m_bottomLabel->setText(bottomText); m_authorLabel->setText(authorText); } bool SaverThumbnailWidget::eventFilter(QObject *watched, QEvent *event) { if (parentWidget() && !parentWidget()->isEnabled()) { event->ignore(); return QWidget::eventFilter(watched, event); } if (event->type() == QEvent::Enter) { if (!m_bControlSaver) { animateSize(QSize(320, 180)); m_bHoverStatus = true; } if (m_bControlSaver || m_bSelectStatus) { m_maskLabel->show(); } } else if (event->type() == QEvent::Leave) { if (!m_bControlSaver) { animateSize(QSize(292, 164)); m_bHoverStatus = false; } if (!m_bSelectStatus) { m_maskLabel->hide(); } } if (event->type() == QEvent::MouseButtonRelease) { if (!m_bControlSaver) { m_maskLabel->show(); m_bSelectStatus = true; } m_maskLabel->show(); Q_EMIT defaultSaverModeClicked(); } return QWidget::eventFilter(watched, event); } void SaverThumbnailWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); if (!m_bControlSaver) { QPainterPath backgroundPath; backgroundPath.addRoundedRect(rect(), 8, 8); painter.setClipPath(backgroundPath); painter.drawPixmap(this->rect(), m_background, m_background.rect()); painter.setClipping(false); if (m_bHoverStatus) { QPen pen(QColor(255, 255, 255, 153)); pen.setWidth(2); painter.setPen(pen); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 8, 8); } else { painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } else { painter.fillRect(rect(), palette().color(QPalette::Base)); const QRect bgRect(4, 4, 193, 105); QPainterPath path; path.addRoundedRect(bgRect, 8, 8); painter.setClipPath(path); painter.drawPixmap(bgRect, m_background, m_background.rect()); painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } ukui-screensaver/src/screensaver/cyclelabel.cpp0000664000175000017500000000521315172041035020704 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "cyclelabel.h" CycleLabel::CycleLabel(QWidget *parent) : QFrame(parent), m_width(20), m_height(20) { this->setFixedSize(QSize(m_width, m_height)); setWindowFlags(Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground, true); } void CycleLabel::paintEvent(QPaintEvent *) { //启用反锯齿 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); drawBg(&painter); drawSlider(&painter); } void CycleLabel::setSize(QSize size) { m_width = size.width(); m_height = size.height(); setFixedSize(size); repaint(); } void CycleLabel::drawBg(QPainter *painter) { painter->save(); if (!checked) { painter->setPen(QColor(0, 0, 0, 66)); painter->setBrush(QColor(0, 0, 0, 66)); } else { painter->setPen(Qt::NoPen); painter->setBrush(bgColorOn); } // circle in QRect rect(0, 0, width(), height()); //半径为高度的一半 int radius = rect.height() / 2; //圆的宽度为高度 int circleWidth = rect.height(); QPainterPath path; path.moveTo(radius, rect.left()); path.arcTo(QRectF(rect.left(), rect.top(), circleWidth, circleWidth), 90, 180); path.lineTo(rect.width() - radius, rect.height()); path.arcTo(QRectF(rect.width() - rect.height(), rect.top(), circleWidth, circleWidth), 270, 180); path.lineTo(radius, rect.top()); painter->drawPath(path); painter->restore(); } void CycleLabel::drawSlider(QPainter *painter) { painter->save(); painter->setPen(Qt::NoPen); painter->setBrush(QColor("#ffffff")); // circle in QRect rect(0, 0, m_width, m_height); int sliderWidth = m_width / 2; QRect sliderRect(sliderWidth / 2, sliderWidth / 2, sliderWidth, sliderWidth); painter->drawEllipse(sliderRect); painter->restore(); } ukui-screensaver/src/screensaver/mprissaverwidget.h0000664000175000017500000000506015172041106021650 0ustar fengfeng#ifndef MPRISWIDGET_H #define MPRISWIDGET_H #include #include #include "freedesktophelper.h" #include "mprisdbusclient.h" #include #include #include #include #include #include #include #include "customlabel.h" #include "dbusadapter.h" #include "rotatablelabel.h" #include "cornerrotatinglabel.h" class MPRISSaverWidget : public QFrame { Q_OBJECT public: explicit MPRISSaverWidget(QWidget *parent = nullptr, bool saverMedia = false); ~MPRISSaverWidget(); void init(QString strMediaPlayerPath); void setSaverMediaType(bool saverMedia); public Q_SLOTS: void onServiceStatusChanged(QString strName, bool isActive); void onLoadMediaPath(const QString &strPath); void onLostMediaPath(const QString &strPath); void onBtnPreview(); void onBtnNext(); void onBtnPlayPause(); void onMetadataChanged(); void slotSongInfo(QString path); void onPlaybackStatusChanged(); void onCanControlChanged(); void updateFont(QString strFont, double fontSizeF); void updateMediaSize(float scale); void onDelayMetadataUpdate(); void onPlayerStateChanged(int status); Q_SIGNALS: void statusChanged(bool isValid); protected: void resizeEvent(QResizeEvent *event); private: void initUI(); void initConnections(); void delayMetadataUpdate(int nTimeout = 1000); private: QDBusConnectionInterface *m_mprisMonitor = nullptr; MPRISDBusClient *m_mprisDbusClient = nullptr; QString m_strMediaPlayerPath; int m_nPlayState = -1; bool m_isPlayBtnClicked = false; QPixmap m_pixmapAlbumDefault; // UI QVBoxLayout *m_mainLayout = nullptr; QLabel *m_pLabelDitu = nullptr; // 默认底图 RotatingImageLabel *m_labelAlbum = nullptr; // 专辑图片 CornerRotatingLabel *m_labelBopian = nullptr; // 拨片图片 QHBoxLayout *m_albumLayout = nullptr; CustomLabel *m_labelName = nullptr; CustomLabel *m_labelArtist = nullptr; QLabel *m_labelLine = nullptr; QHBoxLayout *m_buttonLayout = nullptr; QPushButton *m_btnPrev = nullptr; QPushButton *m_btnPlayPause = nullptr; QPushButton *m_btnNext = nullptr; QTimer *m_timerDelayUpdate = nullptr; bool m_bSaverMedia = false; DbusAdapter *m_adapter = nullptr; float m_scale = 0; QMap m_allLabels; QMap m_originalPixmaps; QMap m_originalFonts; QMap m_allButtons; }; #endif // MPRISWIDGET_H ukui-screensaver/src/screensaver/cornerrotatinglabel.h0000664000175000017500000000177315172041106022320 0ustar fengfeng#ifndef CORNERROTATINGLABEL_H #define CORNERROTATINGLABEL_H #include #include #include class CornerRotatingLabel : public QLabel { Q_OBJECT Q_PROPERTY(float rotationAngle READ rotationAngle WRITE setRotationAngle) public: explicit CornerRotatingLabel(QWidget *parent = nullptr); // 旋转控制接口 void startRotationAnimation(); // 开始右上角旋转动画 void rotateCounterClockwise(); void resetRotation(); signals: void rotationAnimationFinished(); // 动画完成信号 protected: void paintEvent(QPaintEvent *event) override; private: float rotationAngle() const; void setRotationAngle(float angle); QSequentialAnimationGroup *m_startRotationAnimationGroup; QSequentialAnimationGroup *m_rotateCounterAnimationGroup; QSequentialAnimationGroup *m_resetAnimationGroup; float m_rotationAngle; QPointF m_rotationOrigin; // 旋转原点(右上角) }; #endif // CORNERROTATINGLABEL_H ukui-screensaver/src/screensaver/sleeptime.h0000664000175000017500000000314015172041035020236 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SLEEPTIME_H #define SLEEPTIME_H #include "scconfiguration.h" #include #include #include #include #include #include class SleepTime : public QWidget { Q_OBJECT public: explicit SleepTime(QWidget *parent = nullptr); ~SleepTime(); int setTime(QDateTime time); void setSmallMode(); protected: void changeEvent(QEvent *event); private: SCConfiguration *configuration; QLabel *restTime; QList list; QHBoxLayout *layout; long long sleepTime; long long m_nLastSleepLeave; long long m_nLastSleepTimeSecs; QDateTime initTime; QDateTime m_lastTime; double curFontSize; double m_ptToPx = 1.0; QFont sysFont; void init(); void setHour(int hour); void setSeconds(int seconds); void setMinute(int minutes); bool QLabelSetText(QLabel *label, QString string); }; #endif // SLEEPTIME_H ukui-screensaver/src/screensaver/mbackground.h0000664000175000017500000000212615172041035020546 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef MBACKGROUND_H #define MBACKGROUND_H #include #include #include #include #include #include "scconfiguration.h" class MBackground { public: MBackground(); QString getCurrent(); QString getNext(); QString getPrev(); QString getRand(); private: void getAllPixmap(); QStringList list; int currentIndex; }; #endif // MBACKGROUND_H ukui-screensaver/src/screensaver/weatherwidget.cpp0000664000175000017500000001374715172041106021462 0ustar fengfeng#include "weatherwidget.h" #include #include WeatherWidget::WeatherWidget(QWidget *parent) : QWidget{parent} { initUI(); } void WeatherWidget::initUI() { m_mainLayout = new QVBoxLayout(this); m_mainLayout->setContentsMargins(0, 0, 0, 0); m_mainLayout->setSpacing(8); m_textLayout = new QHBoxLayout(); m_textLayout->setSpacing(16); m_netWorkTipLabel = new CustomLabel(this); m_netWorkTipLabel->setStyleSheet("color: white"); m_netWorkTipLabel->setFontSize(24); m_netWorkTipLabel->setAlignment(Qt::AlignHCenter); // m_netWorkTipLabel->setText(tr("Not connected to a network")); m_netWorkTipLabel->setFixedHeight(55); m_netWorkTipLabel->hide(); m_temperatureTextLabel = new QLabel(this); m_temperatureTextLabel->setStyleSheet("color: white"); m_temperatureTextLabel->setMinimumWidth(120); m_temperatureTextLabel->setFixedHeight(55); QFont temperatureFont = m_temperatureTextLabel->font(); temperatureFont.setPointSizeF(42); m_temperatureTextLabel->setFont(temperatureFont); m_weatherTextLabel = new QLabel(this); m_weatherTextLabel->setStyleSheet("color: white"); m_weatherTextLabel->setMinimumWidth(140); m_weatherTextLabel->setFixedHeight(55); m_weatherTextLabel->setContentsMargins(0, 5, 0, 0); QFont weatherFont = m_weatherTextLabel->font(); weatherFont.setPointSizeF(24); m_weatherTextLabel->setFont(weatherFont); m_textLayout->addStretch(); m_textLayout->addWidget(m_netWorkTipLabel); m_textLayout->addWidget(m_temperatureTextLabel); m_textLayout->addWidget(m_weatherTextLabel); m_textLayout->addStretch(); m_mainLayout->addLayout(m_textLayout); m_iconLayout = new QHBoxLayout(); m_iconLayout->setContentsMargins(0, 0, 100, 0); m_iconLayout->setSpacing(0); m_weatherIconLabel = new QLabel(this); m_weatherIconLabel->setFixedSize(200, 160); m_outerringIconLabel = new QLabel(this); m_outerringIconLabel->setFixedSize(534, 706); QPixmap m_pixmapOuterring = QPixmap(":/assets/outerring.png").scaled(534, 706, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); m_outerringIconLabel->setPixmap(m_pixmapOuterring); m_weatherIconLayout = new QHBoxLayout(m_outerringIconLabel); m_weatherIconLayout->setContentsMargins(0, 114, 297, 192); m_innerringIconLabel = new QLabel(m_outerringIconLabel); m_innerringIconLabel->setFixedSize(286, 420); QPixmap m_innerringDefault = QPixmap(":/assets/underlying.png").scaled(286, 420, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); m_innerringIconLabel->setPixmap(m_innerringDefault); m_weatherIconLayout->addWidget(m_weatherIconLabel, Qt::AlignLeft); m_weatherIconLayout->addWidget(m_innerringIconLabel, Qt::AlignCenter); m_netWorkTipLabel->setFixedWidth(this->width()); m_iconLayout->addWidget(m_outerringIconLabel, Qt::AlignCenter); m_mainLayout->addLayout(m_iconLayout); } void WeatherWidget::updateWeatherSize(float scale) { m_mainLayout->setSpacing(8 * scale); m_textLayout->setSpacing(16 * scale); m_iconLayout->setContentsMargins(0, 0, 100 * scale, 0); m_weatherTextLabel->setContentsMargins(0, 10 * scale, 0, 0); if (scale < 0.11) { m_weatherIconLayout->setContentsMargins(0, 120 * scale, 350 * scale, 198 * scale); } else if (scale < 0.2) { m_weatherIconLayout->setContentsMargins(0, 114 * scale, 340 * scale, 192 * scale); } else { m_weatherIconLayout->setContentsMargins(0, 114 * scale, 297 * scale, 192 * scale); } if (m_allLabels.isEmpty()) { QList labels = this->findChildren(); foreach (QLabel* label, labels) { m_allLabels[label] = label->rect(); QPixmap pixmap = label->pixmap(); if (!pixmap.isNull()) { if (!m_originalPixmaps.contains(label)) { m_originalPixmaps[label] = pixmap; } } const QFont font = label->font(); if (!m_originalFonts.contains(label)) { m_originalFonts[label] = font; } } } QList labels = this->findChildren(); foreach (QLabel* label, labels) { QPixmap pixmap = label->pixmap(); QSize newSize = m_allLabels[label].size() * scale; if (!pixmap.isNull()) { QPixmap scaledPixmap = m_originalPixmaps[label].scaled( newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); label->setPixmap(scaledPixmap); } label->setFixedSize(newSize); QFont font = label->font(); font.setPointSizeF(m_originalFonts[label].pointSize() * scale); label->setFont(font); } m_scale = scale; } void WeatherWidget::setCurrentWeather(QString temperature, QString weather) { m_temperatureTextLabel->setText(temperature); m_weatherTextLabel->setText(weather); m_temperatureTextLabel->adjustSize(); m_weatherTextLabel->adjustSize(); m_temperatureTextLabel->show(); m_weatherTextLabel->show(); m_netWorkTipLabel->hide(); m_innerringIconLabel->lower(); } void WeatherWidget::setWeatherIcon(QString weather, QPixmap weatherStateIcon) { m_weatherIconLabel->setPixmap(QIcon::fromTheme(weather).pixmap(150 * m_scale, 160 * m_scale)); m_innerringIconLabel->setPixmap(weatherStateIcon.scaled(286 * m_scale, 420 * m_scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } void WeatherWidget::setErrorTip(QString tip) { m_temperatureTextLabel->hide(); m_weatherTextLabel->hide(); m_netWorkTipLabel->show(); m_netWorkTipLabel->setText(tip); m_netWorkTipLabel->setFixedWidth(this->width()); QPixmap m_innerringDefault = QPixmap(":/assets/underlying.png").scaled(286 * m_scale, 420 * m_scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); m_innerringIconLabel->setPixmap(m_innerringDefault); m_weatherIconLabel->setPixmap(QPixmap()); m_innerringIconLabel->lower(); } ukui-screensaver/src/screensaver/cornerrotatinglabel.cpp0000664000175000017500000000673015172041106022651 0ustar fengfeng#include "cornerrotatinglabel.h" #include CornerRotatingLabel::CornerRotatingLabel(QWidget *parent) : QLabel(parent), m_rotationAngle(0), m_rotationOrigin(1.0f, 0.0f) // 右上角为原点(相对坐标) { // 将动画分拆开来,不放在同一个类中去实现 防止动画状态记录混乱(切换音乐的时候会同时收到暂停/播放两个信号) m_startRotationAnimationGroup = new QSequentialAnimationGroup(this); m_rotateCounterAnimationGroup = new QSequentialAnimationGroup(this); m_resetAnimationGroup = new QSequentialAnimationGroup(this); } void CornerRotatingLabel::startRotationAnimation() { if (m_startRotationAnimationGroup->state() == QAbstractAnimation::Running) { m_startRotationAnimationGroup->stop() ; } m_startRotationAnimationGroup->clear(); QPropertyAnimation *rotateAnim = new QPropertyAnimation(this, "rotationAngle", this); rotateAnim->setDuration(500); rotateAnim->setEasingCurve(QEasingCurve::OutBack); rotateAnim->setStartValue(0); rotateAnim->setEndValue(-15); QPropertyAnimation *resetAnim = new QPropertyAnimation(this, "rotationAngle", this); resetAnim->setDuration(500); resetAnim->setEasingCurve(QEasingCurve::OutBounce); resetAnim->setStartValue(-15); resetAnim->setEndValue(0); m_startRotationAnimationGroup->addAnimation(rotateAnim); m_startRotationAnimationGroup->addAnimation(resetAnim); m_startRotationAnimationGroup->start(); } float CornerRotatingLabel::rotationAngle() const { return m_rotationAngle; } void CornerRotatingLabel::setRotationAngle(float angle) { m_rotationAngle = angle; update(); // 触发重绘 } void CornerRotatingLabel::rotateCounterClockwise() { if (m_rotateCounterAnimationGroup->state() == QAbstractAnimation::Running || m_startRotationAnimationGroup->state() == QAbstractAnimation::Running) { return ; } m_rotateCounterAnimationGroup->clear(); QPropertyAnimation *rotateAnim = new QPropertyAnimation(this, "rotationAngle", this); rotateAnim->setDuration(500); rotateAnim->setEasingCurve(QEasingCurve::OutBack); rotateAnim->setStartValue(m_rotationAngle); rotateAnim->setEndValue(m_rotationAngle - 15); m_rotateCounterAnimationGroup->addAnimation(rotateAnim); m_rotateCounterAnimationGroup->start(); } void CornerRotatingLabel::resetRotation() { if (m_resetAnimationGroup->state() == QAbstractAnimation::Running || m_startRotationAnimationGroup->state() == QAbstractAnimation::Running) { return ; } m_resetAnimationGroup->clear(); QPropertyAnimation *resetAnim = new QPropertyAnimation(this, "rotationAngle", this); resetAnim->setDuration(500); resetAnim->setEasingCurve(QEasingCurve::OutBounce); resetAnim->setStartValue(m_rotationAngle); resetAnim->setEndValue(0); m_resetAnimationGroup->addAnimation(resetAnim); m_resetAnimationGroup->start(); } void CornerRotatingLabel::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::SmoothPixmapTransform, true); QPointF absOrigin(width(), 0); painter.save(); painter.translate(absOrigin); painter.rotate(m_rotationAngle); if (!pixmap().isNull()) { QRectF pixmapRect(-pixmap().width(), 0, pixmap().width() - 10, pixmap().height() - 10); painter.drawPixmap(pixmapRect, pixmap(), pixmap().rect()); } painter.restore(); } ukui-screensaver/src/screensaver/sleeptime.cpp0000664000175000017500000001220415172041106020571 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "sleeptime.h" #include #include #include #include SleepTime::SleepTime(QWidget *parent) : QWidget(parent) , sleepTime(0) , m_nLastSleepLeave(0) , m_nLastSleepTimeSecs(0) , configuration(SCConfiguration::instance()) { init(); } SleepTime::~SleepTime() {} void SleepTime::init() { layout = new QHBoxLayout(this); QLocale local; QString systemLang = local.name(); if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") { layout->setDirection(QBoxLayout::LeftToRight); } else { layout->setDirection(QBoxLayout::RightToLeft); } // layout->setDirection(QBoxLayout::RightToLeft); layout->setSpacing(8); curFontSize = configuration->getFontSize(); sysFont = qApp->font(); sysFont.setPointSize((20 + curFontSize) * m_ptToPx); for (int i = 0; i < 3; i++) { QLabel *label = new QLabel(this); label->setText("0"); label->setFixedSize(40, 40); label->setObjectName("clockTime"); label->setFont(sysFont); list.append(label); if (i < 2) { QLabel *colon = new QLabel(this); colon->setText(":"); colon->setObjectName("colon"); colon->setFont(sysFont); list.append(colon); } } for (int i = 0; i < list.count(); i++) { layout->addWidget(list.at(i)); } restTime = new QLabel(this); sysFont.setPointSize((20 + curFontSize) * m_ptToPx); restTime->setFont(sysFont); restTime->setText(tr("You have rested")); restTime->setObjectName("restTime"); restTime->setAlignment(Qt::AlignBottom); restTime->adjustSize(); layout->addSpacing(8); layout->addWidget(restTime); initTime = QDateTime::currentDateTime(); m_lastTime = initTime; } int SleepTime::setTime(QDateTime time) { // 与上一次取时间的时间差 long nNewSleepTime = qAbs(m_lastTime.msecsTo(time)); sleepTime = qAbs(initTime.msecsTo(time)); // 时间差大于1s,则认为时间异常变化,保存已过去的时间 if (nNewSleepTime > 1000) { m_nLastSleepLeave += qAbs(m_lastTime.msecsTo(initTime)); sleepTime = 0; initTime = time; } m_lastTime = time; // 当前时间差+异常情况过去的时间 sleepTime += m_nLastSleepLeave; sleepTime = sleepTime / 1000; if (m_nLastSleepTimeSecs == 0 || qAbs(sleepTime - m_nLastSleepTimeSecs) >= 1) { int hour = sleepTime / 3600; int sec = sleepTime % 3600 % 60; int min = sleepTime % 3600 / 60; setHour(hour); setSeconds(sec); setMinute(min); m_nLastSleepTimeSecs = sleepTime; } return true; } void SleepTime::setHour(int hour) { QString time; if (hour >= 100) { QLabelSetText(list.at(4), QString::number(hour)); } else if (hour < 10) { time = "0" + QString::number(hour); list.at(4)->setText(time); } else { time = QString::number(hour); list.at(4)->setText(time); } } void SleepTime::setSeconds(int seconds) { QString time; if (seconds < 10) { time = "0" + QString::number(seconds); } else { time = QString::number(seconds); } list.at(0)->setText(time); } void SleepTime::setMinute(int minutes) { QString time; if (minutes < 10) { time = "0" + QString::number(minutes); } else { time = QString::number(minutes); } list.at(2)->setText(time); } void SleepTime::setSmallMode() { for (int i = 0; i < 5; i++) list.at(i)->setFixedSize(10, 10); adjustSize(); } bool SleepTime::QLabelSetText(QLabel *label, QString string) { bool is_over_length = false; QFontMetrics fontMetrics(label->font()); int fontSize = fontMetrics.horizontalAdvance(string); QString str = string; if (fontSize > (label->width() - 5)) { str = fontMetrics.elidedText(string, Qt::ElideRight, label->width()); is_over_length = true; } label->setText(str); return is_over_length; } void SleepTime::changeEvent(QEvent *event) { // if (event->type() == QEvent::LanguageChange) { // if (restTime) { // s_strRestTimeTranslation = tr("You have rested"); // restTime->setText(s_strRestTimeTranslation); // restTime->adjustSize(); // qDebug() << "RestTime trans1:" << s_strRestTimeTranslation; // } // adjustSize(); // } } ukui-screensaver/src/screensaver/mediasaverwidget.cpp0000664000175000017500000002547715172041106022146 0ustar fengfeng#include "mediasaverwidget.h" #include #include #include #include #include #include #include #include #include #include "mprisdbusclient.h" #define AUDIOTYPE "audio/x-vorbis+ogg" #define DESKTOPPATH "/usr/share/applications/" #define LOCAL_CONFIG_DIR "/.config/" #define LOCAL_APP_DIR "/.local/share/applications/" #define SYSTEM_CONFIG_DIR "/usr/share/applications/" MediaSaverWidget::MediaSaverWidget(QWidget *parent, bool saverMedia) : QWidget(parent) , m_bSaverMedia(saverMedia) { if (m_bSaverMedia) { initSaverMediaWidget(); } } MediaSaverWidget::~MediaSaverWidget() {} void MediaSaverWidget::setSaverMediaType(bool saverMedia) { if (m_bSaverMedia != saverMedia && m_pMPRISWidget) { m_pMPRISWidget->setSaverMediaType(saverMedia); if (!saverMedia) { m_strMediaPath = ""; initMultimediaControl(); } } m_bSaverMedia = saverMedia; } void MediaSaverWidget::initMultimediaControl() { qDebug() << "initMultimediaControl---"; if (!m_dbusListNameWatcher) { QDBusInterface dbusInter( "org.freedesktop.DBus", "/", "org.freedesktop.DBus", QDBusConnection::sessionBus(), this); QDBusPendingCall call = dbusInter.asyncCall("ListNames"); m_dbusListNameWatcher = new QDBusPendingCallWatcher(call, this); connect(m_dbusListNameWatcher, &QDBusPendingCallWatcher::finished, [=] { if (!call.isError()) { QDBusReply reply = call.reply(); const QStringList &serviceList = reply.value(); QString service = QString(); // check is default audio app QString defaultAudioExeInfo = getDefaultAudioAppExecInfo(); for (const QString &serv : serviceList) { if (!serv.startsWith("org.mpris.MediaPlayer2.") || serv.endsWith("KylinVideo")) { continue; } // 优先默认音频应用,其次按dbus列表第一个 if (!serv.isEmpty()) { QString strServicePath = getServiceCmd(serv); if (!strServicePath.isEmpty() && !defaultAudioExeInfo.isEmpty()) { if (defaultAudioExeInfo.contains(strServicePath)) { service = serv; break; } } } } if (service.isEmpty()) { // check is playing app for (const QString &serv : serviceList) { if (!serv.startsWith("org.mpris.MediaPlayer2.") || serv.endsWith("KylinVideo")) { continue; } if (service.isEmpty()) { service = serv; } // 优先播放状态应用,其次按dbus列表第一个 if (!serv.isEmpty()) { MPRISDBusClient *client = new MPRISDBusClient( serv, "/org/mpris/MediaPlayer2", QDBusConnection::sessionBus(), this); if (client->playbackStatus() == "Playing") { service = serv; break; } client->deleteLater(); client = nullptr; } } } // 未检测到支持mpris的mediaplayer服务 if (service.isEmpty()) { if (!m_dbusDaemonInterface) { m_dbusDaemonInterface = QDBusConnection::sessionBus().interface(); } else { disconnect(m_dbusDaemonInterface); } connect( m_dbusDaemonInterface, &QDBusConnectionInterface::serviceOwnerChanged, this, [=](const QString &name, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner) if (name.startsWith("org.mpris.MediaPlayer2.") && !newOwner.isEmpty()) { initMultimediaControl(); disconnect(m_dbusDaemonInterface); } }); init(""); m_dbusListNameWatcher->deleteLater(); m_dbusListNameWatcher = nullptr; Q_EMIT loadDbusListNameFinished(false); return; } qDebug() << "Found MultimediaControl DBus service:" << service; init(service); Q_EMIT loadDbusListNameFinished(true); } else { qInfo() << "init MultimediaControl error: " << call.error().message(); Q_EMIT loadDbusListNameFinished(false); } m_dbusListNameWatcher->deleteLater(); m_dbusListNameWatcher = nullptr; }); } } void MediaSaverWidget::init(const QString &strMediaPath) { if (m_strMediaPath == strMediaPath && !strMediaPath.isEmpty()) { return; } if (!m_pMPRISWidget) { m_pMPRISWidget = new MPRISSaverWidget(this); m_pMPRISWidget->updateFont(m_strCurFont, m_lfFontSizeF); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(m_pMPRISWidget); setLayout(mainLayout); initConnections(); } m_strMediaPath = strMediaPath; m_pMPRISWidget->init(m_strMediaPath); m_pMPRISWidget->setFixedSize(this->size()); m_pMPRISWidget->updateMediaSize(m_scale); } void MediaSaverWidget::initSaverMediaWidget() { if (!m_pMPRISWidget) { m_pMPRISWidget = new MPRISSaverWidget(this, m_bSaverMedia); // m_pMPRISWidget->onDelayMetadataUpdate(); m_pMPRISWidget->updateFont(m_strCurFont, m_lfFontSizeF); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(m_pMPRISWidget); setLayout(mainLayout); } } void MediaSaverWidget::initConnections() { if (m_pMPRISWidget) { connect(m_pMPRISWidget, &MPRISSaverWidget::statusChanged, this, &MediaSaverWidget::onStatusChanged); } } void MediaSaverWidget::onStatusChanged(bool isActive) { if (!isActive) { m_strMediaPath.clear(); QTimer::singleShot(10, this, [=]() { initMultimediaControl(); }); } } //void MediaSaverWidget::paintEvent(QPaintEvent *event) //{ // // 设置窗体为圆角 // QPainter painter(this); // painter.setRenderHint(QPainter::Antialiasing); // 反锯齿; // painter.setBrush(QColor(245, 245, 245, 166)); // painter.setPen(Qt::transparent); // auto rect = this->rect(); // painter.drawRoundedRect(rect, 12, 12); // 窗口圆角 // return QWidget::paintEvent(event); //} QString MediaSaverWidget::getServiceCmd(const QString &strMediaPath) { QString execPath = ""; if (strMediaPath.isEmpty()) { return execPath; } QDBusInterface dbusInter("org.freedesktop.DBus", "/", "org.freedesktop.DBus", QDBusConnection::sessionBus(), this); QDBusReply call = dbusInter.call("GetConnectionUnixProcessID", strMediaPath); if (!call.isValid()) { qDebug() << "getServicePID error:" << call.error(); return execPath; } /*check caller white list*/ QFileInfo file(QString("/proc/%1/exe").arg(call.value())); if (file.exists()) { execPath = file.canonicalFilePath(); } return execPath; } /* 获取默认应用的应用名 */ QString MediaSaverWidget::getDefaultAppId(const char *contentType) { QString localfile = QDir::homePath() + LOCAL_CONFIG_DIR + "mimeapps.list"; QString systemfile = SYSTEM_CONFIG_DIR + QString("ukui-mimeapps.list"); if (QFile(localfile).exists()) { QSettings *mimeappFile = new QSettings(localfile, QSettings::IniFormat); QString str = mimeappFile->value(QString("Default Applications/%1").arg(contentType)).toString(); delete mimeappFile; mimeappFile = nullptr; if (!str.isEmpty()) { if (QFile(SYSTEM_CONFIG_DIR + str).exists() || QFile(QDir::homePath() + LOCAL_APP_DIR + str).exists()) { return str; } } } if (QFile(systemfile).exists()) { QSettings *mimeappFile = new QSettings(systemfile, QSettings::IniFormat); QString str = mimeappFile->value(QString("Default Applications/%1").arg(contentType)).toString(); delete mimeappFile; mimeappFile = nullptr; if (!str.isEmpty()) { if (QFile(SYSTEM_CONFIG_DIR + str).exists()) { return str; } else { return QString(""); } } } return QString(""); } QString MediaSaverWidget::getDefaultAudioAppExecInfo() { QString current(getDefaultAppId(AUDIOTYPE)); QString defaultAppInfo; if (!current.isEmpty()) { QByteArray ba = QString(DESKTOPPATH + current).toUtf8(); if (!QFile(DESKTOPPATH + current).exists()) { ba = QString(QDir::homePath() + LOCAL_APP_DIR + current).toUtf8(); } GDesktopAppInfo *info = g_desktop_app_info_new_from_filename(ba.constData()); QString execName = g_app_info_get_executable(G_APP_INFO(info)); QString commandName = g_app_info_get_commandline(G_APP_INFO(info)); if (!execName.isEmpty()) { defaultAppInfo.append(execName + " "); if (!execName.contains("/")) { QString strFullName = QStandardPaths::findExecutable(execName); if (!strFullName.isEmpty()) { defaultAppInfo.append(strFullName + " "); } } } if (!commandName.isEmpty()) { defaultAppInfo.append(commandName); } if (g_app_info_can_delete(G_APP_INFO(info))) { g_app_info_delete(G_APP_INFO(info)); } } qDebug() << "defalt app info:" << defaultAppInfo; return defaultAppInfo; } void MediaSaverWidget::updateFont(QString strFont, double fontSizeF) { m_strCurFont = strFont; m_lfFontSizeF = fontSizeF; if (m_pMPRISWidget) { m_pMPRISWidget->updateFont(strFont, fontSizeF); } } void MediaSaverWidget::updateMediaSize(float scale) { m_scale = scale; if (m_pMPRISWidget) { m_pMPRISWidget->setFixedSize(this->size()); m_pMPRISWidget->updateMediaSize(scale); } } ukui-screensaver/src/screensaver/assets/0000775000175000017500000000000015172041106017401 5ustar fengfengukui-screensaver/src/screensaver/assets/ying.png0000664000175000017500000122037515172041106021067 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATxy-Guw;'I2rVVbYcf ,$ Ј&&'Id9 otNvUWwWgow鮮k޻v!4h`[9tι[[sG:GH%ٙ%mIhFLwdg`BMPZD %$v[-6 !7bV[R%iA8'8%O<~[/?9kРA3РAs?q'Ek;mwy[[#֞ͭM!jO^(`̔lm힜Zl-LLNLNMhj7u]o 4 Ah`;='+S-;?ٺhccܵd? 3Ӵgfz}fzja;b ߰x zFC4^umE):Ώon]qxiu}氳8BD$$jC*vOO]{fNLN}gbџo:]) ! nO^zy ˫{67'A#UZ}"ATK)ؿwS?[l?;{_ʿT Fsj`D~C_nly K3[N4|C _l 80w}vggZ'O{~o`b4G Fw|?/&^x9OE8{^D7!!mCsv?kK׿?); l;֠A_0??/x ńxf_o\xrayаnBGALeGA";o}vGg{_=ÿLe#?5hOxvfzry}g*{Pe-}WEQ f|aZ ԜO@]X6|׾ ;`Gw~O\pe_ZZYӋ+{2I 3 J[TDʁvnEoǪ!bX*0LNȁ}{wo5ה F4#n_WoV׽v_7bL BQMMU0n37]3#.{Fؠ! K+?ubay] % `t=o2ە pZ 8Y=1тZطg/@vCCl \zM?#^)ڿ'fRm'>A~;dռx6h1$v$N~om)% 2`lq7.xV% +?~ziyx$YA$U)BBA?e8<;oLNL|Ͽ`\c3??#ϼ-;/[\Y'@YI8A? OZ\*799*>)#! Oi6JT^"!־l8Tm7So„4!4XCw}W[/j:ϲ ?HR^ǁ p}&}U_*LA@C,<?2w/Y\tɟLʜjD$z&2Cbqev=$sA#"ȳB@t5QHIB>߻qԠ!;bgxymKb! 9z9'mE1΄ֱdv !8όD-ֵ1ۑ \pʾGJ}䳗ʎO/\XD !K;>u""idiKK_7S& AmyA^)`! 8@)Mp Ά~P7Fh5G=prq_\*=Pm愨m]PZmLm˒Ik Œ$Aqb?1g?rO{]rP9c z\Y_թծS ܨ G\U5!nyG׼Wu$ Aya N->d*Q?(]z1T# a@d J; rJ(;+qx9u8>ӏ$[YEC1{ WEݯ!E/=l} xC$푇ڣO|jqV76*Ϸ B翺-։ϒM&H[u so=>|kCGl׶䙅CbC@K% p_!a1A08->xjfjՋH(c{V$eޣǏzϭntuqOH2)*#8a@/zş Ȝ'aa+pr/pS {wMù}7nSIm0h˜㎻?wz',W4ZM|o$O.UQ!A3S{i{4[2GnuIm0h˜>GN.`J "$QGC~F)M<3ņ pB_}gƣ=[NtBC# [Zs:P9?m4U86rw'yh0T *=G%׿0fwvyUgW&LGnTpҰ  U#U@* ڷ}hO]Q)3 Ix>vѹ}T;dh4Ab'CCOb~QPLm-'HW >O]f"A +:}$Q*u~ Z-~w;h0,Hۀᶻ?v}'.Ϯ0:(d<Ұs hshVMDMl-z DʃQ'WN;kz]{}Zn :6ys>{fekNHvEʇg_=K>A "]b[ׁbN|=覶,Ai3uBaewC S]ڍP4ّ0w7ٷO~~}~~^G jΛ]ox}ϼ9<;ZuWa ux,M @b% ӎ?>~7oZs5]yܾ?oA  R 1o'O9GҕEڠ64}S+\^]1&/|SWn=>=p B1j>9.$E u׮!$DiXFMGeK*A_1"-wG!9;+!}@C<4ïZb ˆȝxρc3 ?ӑa lr(w, ,7'p}3x B{٪Ny A1+#E X/~b+rXd=M0Q@րr?VKG=^g 76}9撃 49 &}Kӑ'Nwu|  A >t^rp酧)=k} a˴5~v0*1-ѧN?_{I7ECk?yv2,=`Ko9ƿɳc_~89yV p}_eK1 lPŶAhjЭ!X @x`d U|&r̻6g;ٷ{~K_py5cغ ybq[mGA@LԵH'/!vnA^{ >!P.lzE'oԺ?3H&!B@s2X0ˑtFU_Spׯ/HAu4Oj};8K / U}Us:Ֆ;^Of3]FC"B%ڷN ϸ)MeDw#=vsxY3P#s#?{dv  FujP;; (P!RU% A$A\i_& ?u¹C3|ݿ]E fT 둗>~bai_.ig*FH"RױnPwy B/H1 G./ Ƽ3Hj|՛R~:>FZ٠v4a"G~P-ƉK6;['bjq۽fysSK++h5\C14vU.*{'Py~KӋ+7_v4F>n/z;+kwyW"yz>pvek/ x+&=@A=>k YMTP\lFē”!SW{/EGd*k8J!!AHE sbW80k+/};jx T-MgWDtԠh)ᇚHgW_iy74no:vFq+~#kSǀpF*QuŨmn (a&A`H)Gqp lymN-\{P:n 8rFO"AS4Cw~ʍ 5pQ1'2ĥ"՘kQEu˭k?9A8M%DeHW1$Ő.jXI׍2W!M 䔲}ƽf޽7^yuC Bn=uНvI C4bw*X%E2 3fym.O|1\Lm剸\ŵu` "[HLR;0YTq&QDk^WEUI]u̓"]IT|bvpkN~OIog,u[b_Wµ_*y+*= % ]H _g]5[ x#׬߼ȷ2jSU')EIT ^L0 F4pPp|U8dUj "e$Q t@)A`(V`L FL1$@D0gwg߮/ )CnC^]_Z+4G8Eh異hm(emx0,f';.77>-X IDATs.vj+/{n}%mI>+s[, -`$)WPZ#ekq tw0 XQZ*A.TE IJj 5E%g}\eq49ʑ ^dږJc02x\3 }{+性!o. Cp3j7y䵯vt(1\h}z~z#{gڛ>1;켌,9MO;| CU ԗ+=Q1h0 euSœz H=9ou\E7Ttph VuI,WC ?u=3ϰ%7UܐeA"Ůk.tR:CHc$#=׍TU 㟼ܡkuf5kU_&C̎2x FX=#Sͭ6]Z쮏}JFؑ=>𒕍wQ;E6h%qiyth{;>cLW}}w?g߮]|[T 1+DԮb(D-G";1I~=[V/+4*v?Kbpk%;a/zؘ:a0@ߞ2 WUB]1D=+ةJNj̶p5 `TԟXUa}?{3o{ǰ$}=3K+"υO2^4h*٥o2Hm3g=yQ}ՔkAV H{#0˺I?CcQO~eQ\]#\U6|QFj_҃2 7ⷍ`NZt|VUU5I愋^ˏE+'O/<|en ;fDßy詅u$=<&ŦzR,yL}W1px+Y KkF+mqܽxeb!$E*^#JPRZR%D놝A`?sŵFP| @Xc:ֱ\56{='^.h2PpuSتl us=Vy~H$)"sI^_s(?c Gtk !НxQ7^v0Nb|?uC'͆B#ϤqT4h0(UpvyuZO㆏SvGHٽ_}jqY9 @Iڌ!wUkaz=*=Z.%8FfKS7CZ6fn,}O6O֒(HE@݆کfizt1k.̸09V[U1t=d#{7h#%I2|Qߓ vba9G}~4FcnN.'#f";4j_BA*ڡ3k{=˅A0n rb̹/=9qpܽl7'6ocN6W!LMiD6|-ٍqTk$=S:ͨHA2!‘>W rvic땙(@FMtǯ Fɞb^2"ޯiРA*Kk/܎݈r͍ƾ6WΑ ͸ a&ت*G-J˿_ɌLFo%a\ J7njZokO݋AGWh,:m(AH_#A?s44+X # p`'W^J@DxhWA&;`0,2r̀o]lРAgW2Uu1VF7ϞZZ- 1~ڝֿ6L$S+?\q%^zJ'uT#"ԶF| R7ښ.gU? ՀAa02r 12&cTkK`kS ?~'YE>\lQnï[ZFSAFPAZ݆w%ġ~+'#bHoQCYi+ez{$ARe ^{embp7Wn,.T ,Jo EB!/537⻻N`-T .lw޿9.c1O^آ 4; П3M7?xѰR؞/ZY~.10Jo>]EɦKq;a<0F]`d;<TiA/B ${c\rRy˾ƑLBW! !IM6ڟ[dc I)'%RO)tLb +~irGc/Ax߭wfeA |K&i(|x!NZnTuPWV~ΏeH1Ou5e=ERFA1cʱ_Ҩ@gW= B&7|'A~QwneK=$K2Q7LWb#A=(Eu6dږ^zɨc.OZ$:REԽ6H2PLGn{F66ev{'vKΗGs6g.f왞iONM͗] &Ɩ 7g.Թvw\e {^+]v^ BpAA]oz{ (vOZcՕWoYCk_kVU:fd.񽧟iy޽vַb%-ت ̙ -`V]vpF 4mg*bH JȶhY^p /ca,G嫛mծګ |1#*%cqF͍sH2y !4@mzXK|:oҕr lSN*hICF5Yf)m#E˛VY?)jRHf B''aW_{l; @}[C͇䙈8(^e?J89 |4h0hCɒ1ݵat@o;/U1vy~?m!>>"ljR3FĜl#ԫ"K<5θa8"cJ/F+%M,ZZ7al`Ɏ5Y-a ʑ#sX5`$7~ڬp͜YKvF`8+$L еȇ6W=Ǖn~K꿶Oڧ06(>$^[ .nܝMG Ax w;7*RਪP8a\EL(g CuvzAյ!g6vڒ;>slyIب9ЉC淚>bHCDxv lj]3Y٦%k{$kYPl` .)u2j9DCɱ䯕8QRг7DV"i|]w‘R;G0m5C}~ac!A+WQ'="mРA-0̧^VWІ.̨B?C=3UL~P3.{sǎL}zF,Y׼po `$_A|$jZ<6>wx1v-zX!4zMvBsT^v}ϭx]W6KRq.wZtc1O%FBWGIeL#*zr_҅ZF^p'V_ՊEN:tƊ9Pͳ"׌w ib8V3~tcs4!P[O) cmDId󇬏F`$!,&s9 ܴ)3x)T6vX8a*Hy"~]j`i dk[ pb`7,oV:Ɨm 1 >,>Ɖ@L0Z]=G ,]M @KAVσ&8{SJ^s?Ly{+{ES{'c%qHi$\fRsIxvMMȩo՛0[V62 z4+2>HFBu2றg q;$<: w64X.D Wn2/1ebI'FXSxFtN:)ƺ;q%S75Kj'9 ~2+nS9:0}J} zvp fjY0J={ngWV1|;lbo`B|0î!D^\!AvA&B^ $8[E$HmHL0@D@S*mX]Y}=;<}}o,olAT nT  DS1p&=F" u(C{Yw&e ? [5R̹-C=B(X;PO,~ 6xjϙe`[ەN:}X;.5N)3uݐM?YŴk3>W6A/9`v ܦ'T[tDJZfV6]W;dIáٙOZZm&7Dq.S1_on!L=L)^~J#0'Ŧ"%v $=[ M 74.XfP_}Ez%TJUKs$ v;ȌS ~ݲM&,RL @&'Gtҫwٹꚠ*+Z= `$S,o?g&Adn -ѵ8A_"RB aSn6 -A9E/ AEX! ohD e/X . ZBΈQD"44m|#8ԿKG&_-~ 򏢥/mC!.n&bukP2z%ɒDZPL{IG|"-{U;o.olݏ#/HhH\D?xwOܠ b(dio8Te,zZJڌ0 dU_A:F yw_e^ H1O̍ hNq"z69kC3:*Hnb1}+7!̵g|j p=+Y>2 5 1}A,FЖ7HAIQ1D %Y޵7#b84룧W @h#.8Y͏W b|+쩛YxVĔ0r>A  _RApPޢ^&׽+H(A}2it AUj|Q! 2 ?9&=.bEӠd[l^cu}d'٠ IW_p|ު> $(Hs K5FsH Qsyh (تr 1  Vaayuϱsd7=mͭ2 B q!qDժq(hv2WbIÑz:^füHtisGQNdb?9e _7nv"RvVj%w !,R%HU`#pX(J0-J Q&s-ʯ&* <=KWθ3 Hyn;aZF)62[ab$.NGT]YLə_0)}HH&ն1ܪpƼ.r&uS!FSWiSG9K HFՎf dXAk.#1&ެ!{y38fɣW$ZfV}n!Ef,' t-@l Zj\'RJv@T|+oIL6h}e(\ @nWU\wdW3s3!f Foux0AIW ?`t$HQPy(DY5O, ~jbF)Ӥ̢Y-?K:jHRkT$Ell$D`v~ =~dĊՆеp1ԡvo<:RC,mֿͳެԻW1$'Qq\,^1s-E1Fĸ]VЍhY{!4 >@i'aFM^*=|Xc` @Om 8$AкJUo!>FzІyը2KmVlCBpΘbD .!`)b1B]2+6I<~샯t]Rfo=3[\?TG|UlCzqffX1 IDAT;j R-꜐z{}*d[E %k3=(B9K<.(!PR ͔! z<h!L "Ky$$ )ӑ2A+djRyVó"A]> I2Sd_:Mۆ F#N-?𴷾+ICS1x, zgEi`+7I _V Nߚ@/(3 GN\gHc@׉O[qK4fIn&bVRK] .A^`-GQ^mX뇭c-p[1 MJbb꘭"Q8yv'q#=~?"F3-0?O5hdxT:`W!v!Rص @)+tH fڦ[J-Yqr?euQA`J)e<3b_c>&P! aa+Ÿd&uolKhoakk $I`4&IHJ[hT_lyHk 4g"l|1@KٽK=T5f1]dw;${{1=vh'5ͯR!z@%V76(~ˆ7s?$)b@Iۃ_LGKi"{(>N MA+(o3Q"ŞN< '|6`qR''lw _dv_$EC-l^)`,!lҾʡx'JlfM}ͯ CSC<.^14B'vat1@'O/N3ο Z00WWx;z'h '[; L3|AYNTK0?u;yxcHChu:_}QhM-Abt :wI(xCQp)2Zw-ƥ4tA$'6cA.:@fn}}@pFǔڈd2nU`핓^kfn\@hZ01!%wDtuR|-SgaHBׂIff?1Њu‹=~q weYMȻ^jzQEf oU3du+rMkys 7>fWÀ^,<xAd9=ٝCob9"`̄(|;ULɪb@x_# ߂Pb쳊$ "6;&FDuEf$Q"Ҏ=v;)5wb]|~« LOmusc:V!Stढ9+cG3"(~&(+8oNWCdW0ydFUZZ ` ؾØYDL^YrULIPFKO&284`D4HmسQ+MPS\`GT+Ot20a'CŒ$%/8[2+L݊l&UBRl4ŇY O3L ɯ`8qjqO;~B$3қop|{+> )c"38dkeo4>U3ly<_aVJ&X!(/5 AEС0Ndj 7%6F<Dc2-khiA#ҍ-W?]k[a&pRAp& hˎ!a+WU*4 ka#k<g;]K~`tQ><{OU%fښc67K7c芽 V @?pOyƉW<[ށ!K5c6)#NDvmMRgw?s>vI̮f:o7b,yz8/-—[v r`MQbYѲ"yVbm1#j|9ե5NlzTR|pԇ6ΜAh2M;;17KN"`uRVdT~?TFrAmmBe(ԡ9 hbbK"oP7.;t$Ն"IeO!;HiFrw4LNh=|"$\0 .6R $ v0s:L?|"1 lZ(.k,dz679CPBMj?zpaᱥsGUG߳ޜg F=d@9`e!Q  3 c&"dJ]?d @)?`M4lt5j+Y !l~|%Q_)b/*vZ"A ' P8ÈeM&ߣٓW, ascUBEIo%@$mbU s~yg H輬8s«qvGVRFz(!Vb !{^I1ҁSP.yCT7,Ӈh&@ǐ0۳^ARt ~ R#Dcz;XeriBnVyɡکg$%@o@ Bo}N\Ck+ zv5D}zʗ2}dC<|X8cƎ90cqeW5@h/-тVK}ZD.'8[`bp .5G|I#*d'Vz%wVopx[&mm^Y=<Ⱥ2(GV]5Ju$ۙ>)td}K><NQ2haU (\iwHf`D%Kbq gBW&& v>YvcO 7Jlj2T7$Z8Y(2g=6>i2'Dp{A;A!49⤦`FV +pL;:bFJtOFEFsN m%o}' Sڷ[Aot+ay1;3-~bk '֟ @ / !JV w[( ip"6BUFBϠB{ NYr%j/5>RL;+z3?#9|ɔkԥv_S"3@ / P(Ϥb%<] m L)L hWx1_wl Jekx|aHeħY'`@u£:%kBG01Iwxj7f4gRбuP蝫*_ ZbDKLZJT+TxV$IB `"{ŎԒ! R$]. תN u:Bc$~tw^]0<[E2<\Aq U  IhiR'-n-'o\1M_~S0警 _"ҰE JC2g!_]{_9*V@I$vȜ dl PXE`}TbD6 \=S&PhjE &fEn@Vjk(y|dXiv<0 ,0zN99P-+u}!?ل` rkBO1Lt{e)Ŧ:36hZva;&̼V Yu3_Mjy{1$؉Jd ]H-ݧQۏ kؘX$8e6[N"]g%P#Aәf#nS!7ͯ' ;ca%7^wwq&mVO0D'Whԁ!(goJfi'-j>lMF 64%j8#<:j+ԣɣnv6r~WceXRF/Av۝D0ީɁ*H}1Bc`̖mFn_[me$ :=oΜ [MA%Z); AIn7A6uݐ% f{'"ljv9IS55DH]3 #ir!;J-VBa]G,6 :1NH D|䌯nO$Zhsr.3DOkpEi.F-e?ylyѬ5:JWyWu B\T 0^OLb*X|"wpϪ&djIw4MF K)fÁB"dړZ7`А0lIJ{6O78[xy%z4xkuN,sHHu!ހO<*+hrATUWԠ3}Uo1ask 6EH0Ie&9d9Ɉ Kv@7tgHVzvV$&RIW F`VN;-৏16ۜT!1p;22 CaT*ʼ[ML%'-l;vpvi]~[iG-޴r+CնZh!dūP{}Ƞq呭b~NFDO3?'K@p }9[>e(YTl'6i4G>"@ Ԏ׸DL$'b>[E&-D™M*B6IcthE-E8}Y}Hsn&! 1,jg`gEX"-bR*l@ A>Z  i`69H'EzG0jR ^e= RBK1ivPK$@_4M13y&i>$05E'o2]ܵf IDATčH9J-x||q(f7E[qy.xতL.kAwMF*, d39 aF9:ˠe$ Y%+Be˂@.^iYTIs;wX@'q' ^A'W5 L.SP =6 Hs=U;2xDw$&Rjt_JtH95)ί!rfrm HT 䗹#Mm A@m'9K [U c@N]QY16Fh 'z%?x~#,XLB$ ߭]F&zjzP5 &Z 8>& ?ηq dX*Hڶ{3K6Ԓ/@Imjs L^7G` zO3&;:$8Q'.Evq쩌a[f AҾ1r[J}MM|@ZO=MNN@ Z-Mɼ>ߐ 6NΤyaC7rqה![DcR8~pфГaWȍʬgE cl+?#1"1ٗʄ7OlNj`Vj(0۩L줿H ` `"5FT~Vy Pu} [eV Z-`ke$hHCAyG!H!+5_R %T%; [ۭ~IУ;Re,+g %md =N7@$D;`9АLLdHL2Ja;?VUwծZZZ`<dẍዐYiTV\h{O4@Zl,i+A޲Wȯ2VI㾏Smvz+V ?hؘ{_*|9?O9{S@7SBz\!d@ӲG.x< ӆFߞ3}auCpI%{ecĖ6h)YK`~(@33NԳLŧ̈ ɳc&T< ]a_?m~f[fo16hfG~fԮeqA&$6Dlv1N3FP|T$C! s "2s[;&D=#jham ۶mmQBmgGϿ'G#9(2zgzŜgn֊E/akL8m2J*)6mVKJ#iʝovw@# 1%Y, A4qAoy:`pj+b' }f]&泽>jڿVkP)qw$1ȍ-q`ዘAW\XcAb6~eT,[lra&uřpnn>7,bA$Cv4L?ċg[L3t}"NGL$[BTjKpj@ ޞ͂J`vބS9h4ł]9π:\gc40 VGP>H.֭L8,4/ l}1=)D "Y]`hD#}zp@ղک~ńHByPT[-QR^xO6V6NTҮ&rAhq+0F'5 Uv1|N\ 3pIoC*<l r,6u+C% . F:gƐuy೟?Wy͛,?Oow_ cx7A/tr02c1J0֎q5"F[>Rɟhl}_Vq,$U` ;vPJKyk P7́Y(R|;uNHq ʊ3p>[=W# Dd99kۺ@`n+Bt_e5grvc߹?>01r1^Y#|$Or)H< h<[$ސ7" |??7?ZMB-uI0;E?knب wb%eBk3ryG%1ϫ,R[?qТMain'#W@/ۦYZPkm)mGЦ׬'[#Yס$f&Hx7t#ㅀBL!:@tO/I>{Gw!r)pӊ&%*evy?.6Mz5,lMhMc$ c~b_YX3bOC;FupjqYO4eq 7ds/ H̤r2b&ˉd"jʙ~)u 8]j1Ћb_∷͏.(*&cp<3lcyqڭ|ua 0DmfQf}>ܺSfbqn13ƑLm19b9&I!H0I=;:in?Ft^qT@*UW&VĂjX4;ZΈ٪0s1XzD࠰.k"PgPwXOX&:s4ڀ|Os@ aZܩ勨\ JlQXGA Si4 Zq5S4%6(,41Np,:sz\UO~Ooѫn/~o?=|ZMGcBYkǷ{/zp&8K@*3&_nsZ}#q{[Ǚ)6U:4AK/\xa-+C`Bh~"c]rN q, e7[I##D&g/ddsc94z[x.*ZG2{V3!gh08BS}R)ׇ /R楮YǕ|lc} \.q>_?Q92l`63#>c@>g~x+U@(q{$.r'`=$V#͡1 l+~SKaH@$hUTL{LdC"99Y[e $ 4`>}y](&.ѵi oiL#HPC2/ Xu{B'R Zh QRm4A%$OeDZjzqI\/:~5FAƪg.hy+Z ~賟?߾>G,?pB:Lp(?P(j̹X= $fHe'##f})@,Du+= 4x<0nϪgrA5/<9zqYK9vײQ^i!Y%9&97rjl|lid? XjF\2<"',&!)>XrC&` U+w2~_w.كvUZOm[}/ۆP1i6Ht"-(*  "tucHok!ں@SDILsCX3-dGnqa\w>zgff^J6EDܫL-MN79c' q }Ql=؉ ѐe$bbۻPKqmJ[A/ J04rD[;9ytHg| %@ ./Q')@ e;X |;+U/ /߿ϝ|T_c o6Oƚ C0 3 }DsVN&$&YylUvIZXp m!/_lI:RP&B[sޘ*Q3`xwA)6lYĭ`c 9~&>oѣuwc l @+:AlJc0N#ܝk2JUڶf)|agMR^ r|1UM8#'/ `&H*pyE"a"dS rE\Mn78vEM̃"NdM%ro#.&Dك ^_.`#KѥAtbkdx{OoVpY>$i91kq^{K/%!ʓɎ)&xl?k>Fg,f*رft 3h9vA_r#:[ݴ}NPr2/z8=x_y]c@NX(^&{so]3CcD]za\%)VХ:N{M, G;-=YVne]`ʼL|B; * \LrrDINn bl-vRRkFJ6~V Sf05_9 fv G+׮;IC~8\vL6t̩| }7p\K?Ene-*JkE]|9pv!uoݸՉC#Q@3ƥڊΒ4"c==H 1f'Vk,6rNΔ~c51#ArI. ԺQ\ï@6,'8ܔ'VfɋL2AiA9aWY?Rsq> f$DpD'o :˾ xw fhЍ=0D%M:ߥD{-&O1ZM$PE m|KK`I@׿#yܿ~-֢ `(׀Y̊x}b[OES5%|$~/m0GMZKdçi1JL) $H=&'J"& IDAT!v:LJz5H䀤NE_K@ H-̘ĂP7`WµPo !~.~fbs#~gdll[ŐckKwBc''}7ǀc0X`B +7x*yK Hr@f'Xp―9q HP$nIhXo0#PeJb[䬈"P`@KԻ8#% zYJ?@P ;_[wWK¿?Ʒ13~m%F>4/D(ޓ^h4$ _#!Pɼ)Z^U=sią`K -)U_F#wA/?xD89Y@(e?t,GFBv4F;n}7)'%|[n]5j6h Y2R }v_} d)b 2!5cN٧@9 Jx ;,;Ԭ+gcf'0I1ǤG  Yչ{SSs|YDr-8S7y r`9؝dFo|C ~ڀ%B2JT"I;y`<;;mfeY7MZTcVϦ^hG U55/._J-`EtWd4_(ojk5tZ ʆ8 !,}O4*πX6a{?&n5`kKM(f% zB$Cu*( `yF,F6xnDoMcW#pX\ůP1@6A l.C|m^KbWϼG-7N6mMȑL#$3"X! 'n)USO~/emu^%FP9K$`)>QR @^ RRbFpf(LJWr٩ {}3}mK4Tm = ,Ĺx Aɂ 4mMp/U_Qj:g/, U;I3' ލ"[!T'HE%)1{>v,| PIER(9嶵㞥<Ęin}/ 2OD(`pt0g-^rؖڴU8OT~o^(]?[1~ [^ *CDAʃYêV[rPYj^돠.8Pҍ@9]0~z|Z^ abv-HWgEF۶j|lm/;P&h"_sNm!jP ߤ @:́ Ѭ@Ow V6ʦwEl[+4a'[RLf&0 =}aǢΈg^ Re@5>QHo2"M⬓)q&D]*%P\MZ6bK0&#h |? p4mJt-Ѡ4OdbfΧ@l /RT}3Y!_ i K).q? ;,V!"Bj7-y' TE;l˛Xh##D]b%Xizkɲ%LղAINK'!u+\rxIp9 DrBOմ7}:D[ 7EݭY)`G@ U+v0lQJ& _Ngs ( oI+0R(j)&jul !MuZ:ڪUW\i? sśs#U T {ҵGh`F„<򷔱H/{^Wz3 Mr#%Nڤ&%HAp5 tE9HZ> zlO9ߗ7@Poy\p0jBvuRelL؁^(X4q0E!nfg Nǟ;k+/~;~X?a>`[$:#8ǑY2R' {7e~݋f #!5k㬡vTIj[ l=lpѢ Ui` UX <<< |Xise9b{Pvc`ZB"7H `> oɍ$0MO1N Խv1vcG4OKjٸtavшC!) \w'`, 4Za_zǨ,AQ|znw݀Kɼ,ͪWj~BkugdHb}-{)Q0Ef\."~oGD +n(vU6N oQz4h "kEoJaQq(c?M}wtM@._9[2:ڵ$ڝۓ\ ȍi "L,P@bڝ ̸S/?2?7ԁнI3w&+[!j-Sܧɭo9g#?bBÞ3`nccha:R@noIY${m'k=w*Pj> 7r=f4U"(PswYV.)6İnD-%AaLtX{Lg RsLf^|T OIyA3tApZ N@Ie vl=ݴ!)JԍDv?RD,άNT,C3<15Hkpe.NӺQ<*2W(Lq;rzA/(#{1XsZ( ̞!~9acYvq|1˱ OMjkye 4EO+BJ(` o=CRx@Ҕ7e\|Og?s4.›А8nUn?;?:=\=cN3Ձ%Aόitm.v#EΦ'Y>i!z pLFm,"u`)vCkolGP똬U,}9-?wne,OwNϵyҍfAzǁwā)A[Wc&~M1~2%cai~ɜ !Dw*)gó /|qԂPwZMdRD/, ,:Sݳ#F%$o$\ŗns3,ڵ˚ 3!WOED7_Y 7 &DޘH~HyVf}7 ࿡jK?+ޠ.ei{hXc6\id׌gjǖmZOP8Z/7O":ٹ幠gD3#mD{}dVQIFa.Pb! ԕ jI0ʈ q84a+pM@~!HtÜz_%XۤKz6B*31&Z@(c~4cg\I4Fm/X^ܩ$ /ؠeq= qQ^LӤ'3 ofM?`A\$9{Wa{ iGLܽXS/Mm:z~j^@wV=Bޜ?#~t-TqHu^No_u#{ Z dT yZ'E +^,;[MdtI+#+W@OD"G|1G7#W˘h9Dfn͓O1"[ș23@㔿$SqU:i|ᵕMMGxϸ'v7zljׇR;'5v* ^ngarVJ-OU.6KEw kfj.pEo#﯂P£:1͵$(@}6|@RU]+?:s5:I$Rfu144?G{Y=o?g={\еsC7cP VgIuY?1/ ]j6wurl/4Yrh:C0߮7ʹoI7c+OeaƼQp&o+\W^8hOZ?MePii]Q5 El5]oSwU~VIr|nK ϬRuinkXƵB? a  Wޝպ΁ 01d3iXV  M&ty9ȩ:! H.vB»e9ãX*v+|+BIY6Y2b{44J9W w^naSPF7)/jy'ϟWB5Y:NOjvU6KQkw"VW X(^JWڈkY?U3Ei: "v6iȄ"J n׼JKgWPɝt+𖄮+U%Xgz0*k>Fē3TF4r6f ۱dR"HE1'W  }G!v $WRy"L7|=#:_Qn\hi Ph[}1PhGǪ)r!>f4< Q/;:By&{+ _t}< \MGT kNHT*9rCA@4[s`zV+q]30ox]ӏU/s:! ^V3ZJ]Y?ZG غr.9z^C $+[4w= 5(O4Y|D`Sm,LfBi43kF3A S,41xB}_R֮7OJ./ouE$)j⼥]5t> n5*qd밝\؁`'lGw99DAhamoǣ?\픈THBgCX.8i.7vjl=@]& "촗y\f+S қz|YL=󡙠5Q.Y:MWfVeNt#;ƞL7!u\.` !3{6D81]?Z 鱧)FAd視j,@hnNБ q@(%0dLBK Z?EP.XL¬;1Kuֽ1t?k56Xj= Gڧ 7?EpTa  9_A~Ἔ6BEn:oHQ[# &T<<4v5z0ގ?PY0 IDAT~DKLUww۲z.A]#{kZ畄;3H@¼k3~Y |n_^SVM)MϮ QBKaݘixg){W=ϩi:ɫƼԇ}wڸ).a5!YQĀ 8JTM *Z\޵r>[U1 1{ Raf3=l!֊i3٩CBTZ)+v}z>xJ۫֗3Xl 0 S[ALxIP9-cDP@q7 }u' AF13kv%JHBh01/8(mtkzSxZd$ h 7lBl^80MuP ;c~O{\]"h Ј0\ѭ}~go#W-`k"Phĺ_P+AE%?bkAZI?r^ l[Kh?Ju4CAY e_Ϟ3i~4j'_V3$>Eh+KC c,Fޮ%ycb~bKǭݲ:WC= $Bgeb\yr*ҟuC% )񩛧N׍=aԸ2K@nĩ%Nj?Q } pȹ@βg;'ޠ%L5P #5lyk,!֎CR[Ά>N5 'D BwXH.FCjA5zH>g"* ՂT~,TE  ivUoyU/A;&vF8gU3:̖a.iƗPÏ4IS%"lkh!YC;%*$1- As'1*Z $>H~[ق]>{0|LuFR R`6OWmB8oɰ3ѐkEUGX'8;isZEP7Q8I3}WEPB;O56vC2a˟XY`6uiʄ(qQh V|Enۃft*5ñ֫qCnYH11]1 WYƅ')Y,jt2Z Yή[ڭw3}T( $~eAo)+FoFG !(Q [u´L<"qS߇o Č>uUE.{Ծ!^skWWjw^ u'Z]Z{Kgp)wMçD heyB 7ijMռ85k~{-sFA65L 'z6O4r}ׂE2^d05k'={lz\]%%ڄ#Kw q%9Aʉ)FS\wt¸tm"t6fsjR`r}S J1U@HaKE:w$tD"N^{/Y>|͢' {킳,P0` (Ci)A\rZ~Z-y O^rOD&E쥓mթxȘ1$#MǤm9xYx :j!Ϟ c?jȧSNW|"l@[QTp_f1 fNc@>=ލ`P&8@!pˍ'Klh\xw8q hkQQP'~/!K'vYe(7)D\,RET^sBHnMb&yګ=N.`ZPxW? O$` ΍A4Fơ{}oRp*}ٴ'{0PA#p8TbU~ Efm0e{B鏯tqU@8.u?8ǝ~D>HYDMF3+7R,PUφ` zDa"dئ6coK+Z Y.^$~h,7q6IM@k'>9*F 2N$3 CNA`}#[/M Q^-Ub0v܀j wG>VK_pO~~2UHt#m,4<\4az}}z??e/!YrƧuDKTRw f].07WٝOK\x^ 'fcwLFN#LXj4dkOL?jGЭO> t/vvXH.'{y;}} o|L"nAKw3o ^"5ծ@pܼQ3&}UO]xԝ% u9I1UN jP7jVٙ JCR\ D~1 */6^<|~zI|s+e8B‹3$QAw3(~y[ |Pׂlk@%X{|֓蘩 Y n #]֌س|LuIN} QD ̦Ix"Ofy'W0ehIŐݿDnN85" ՘ "L8z{bX]s $jvZH go)D-*Lh MiȮ"@ 6wQ$]{0nD[qО󹱞&V3k^Fʆi"9?hB'4HRgj s9鄅^Pgp *;F@%cu;7аS{Ky "9i2t+lƔ;]`hzV|K g8§' ,F3C]I9gB#ʍGIq] 6䌟:8(-9Uo- , MXoN Bb֡DJ)F1|wyT_n,̴͑ׄ(jTkWHU &ĻJn ] t1$ x| UkPv@=9vkƂSM7<(`߰P]w}HpGn%U2BÉ--G@y[h NrM퇣6Y?wڟx@^B uqZnR Ĭ12P#(GP]Tx7mN}%wj?v_\SzݎX9Ad @ƂiZo3y|Wj}@-d8qѻ1\ a5>BW2d<7-RDlXфn &Aǭ:o0N;bB G56m&#iq J0?SeóZミ Fh" t~~ _}#uӬj CŽŁ072cKA-sۭdžLؼ`;P&eķ*dYA͔oHƣ#{; ';," QVDI[?\"-I=GX UE$Fł!iPl>m^jz8q]W`g]Y1 -un?~Z{NB^uoM3=Řpl׾?u+CIҴG >< ~+!K09ڣvm⧭ U :f 厪XI}Tugs4@Q :͋O%<ϴv}Tu$44a-ՇeKђ;@ķ`bOvf5$X!|}큦0 \(9Cl ٽ;Z$|)y/8_ @b|"=gEmj -{rƥ1 ]3Ų[}S*֬'q).q t@T%AiЙfQ> +([5~#,"gDY>ŏH;_?O3Jֵ9EGg+ Qcㄞꯉpźm8Qm=h()nq=iѥ5ғYAPH_aUbղm8`=i?)%'Y04TQ~ 0$($qH:GYEK4nq|~h=ʁA1a WDHUIiuX%gB#zva(Ѣ>GOڷ;xc&z)$YvLpx.B R MڹbSƧ,P2z0~-.Fbb*Sbs#?ד$oH>wvx4CQ{ EMY.bi!1[5QVaG^خ4qs Ķo S rЄ/*=.g&l\Mr5]6;4{,Zi4~>GxY% P`"c-ac |rY c{8{<8tDPU媻J:$g%K9=geKh!+Vpa>ٔU~}}e2Fʆ=V6JqL 9;b>DȒi .OU)>/[*؈aHY9AVKE%<[)Bݩ %ȃOd2u#M.O u9Mhd^Wzs@,"րDS yօJh.k2&Ό,,)U* "xk,dsOrN"l;L3ߋ\TpSZcΩ(*g0!n&TQ[{ G x~T>xܨVw t_p4Z"vu]O{0'ibi^w 6JtzO.e̞U kF)m˭8`Տ-Dg:B' ]|Q BVĵMqקl,CXGs.ԕ كZ A;O.o SEA >['6E(fՂn_yx tѭEybAx/럥}VXh:r3^{9Ո65/G82O*daqz'B *l5 %VXI t#kVOڻx3h[|B]?y+ àpen؉ɅȖ@*rGtꉔ @i8F55ocD@G@i+^!-K}y"NtOݪP)ؙ-_( Ldj%o' RMm<%ر@;l}O1xx|ԟJqfHǐwWP>'9Zz1,ήzY(B44kbG=>OrKTRս0>"x5nBH#Ϻ7s$1®nGJ /.Ñ'H7Ď9BGXU1$Rs_t{*GV"}U7k϶8ӶExp/oM[<ڹH-"v` ퟹduF{1NHDyv'K䜕"GC쬩6$˪%):YnoS[ \َ9>'ڎ'Uc[:{* v'3aX,3M7<ҹ%$_B`ŗ3?qS~B= ",mΝضMW3 es=YȫWAc.@\+,a@oDx& VV|%}7Nk Dp BMػRaF; ~i4߱z;﬍ҸKG~{GmW3I)ÑP'bd?͈4%Zb#W5Z&_UZZE@KHa jB,yJ{ `AZGA,GԴ￘5f[OaTWhX-”5F6֫UVZud:*V8 ΛUǹ^X׮meke G;a);[n^ZN}N_9RgUwh»Qv-=Ȥ|ȶPJn,A5ugѦKi̯3r9w gx#Ф^r[JA6}y[+19fe Pɍ8+cYƁqƷޙcW,٤z>4 >싽+0sY#z0 >4qφIkmPДrr8χ!4 h^F(@/cV7kv$Gp&}s/`~wdEUPYT=~\Ƀ ^𠯓/@+{ݏ$adpA67GKt ym2fj_cKqά 8);b/AcQv*<v%(GIfMG5y;6̅P嚯fgivә`܊]дN9dT+c`.{F `y'*,vfhAs|1Ve܊n7%SM1̬ESȂ `B uS]p3'XZ2ҵbŅJ] }rw߅]^fF;:'<#a_`Ǖ)4jtYF|2unnc@  CnrlB./9ӕ4t =Q,K+Ik)5C/}H.aln{8 ߊ'kK\eR> {/uox{` >Tq fy<V >:P{Jڝ1O"4,gT.&B[MQsAe jb}N':MPԱC"uKgMs{sOri!)zEdo%Y8 rkN,1YUZOڰneE$_^6j!N[ϙQ^Ӣyat9sOXTsvG}OxL,EvOUiL\Y;J8aFwG0d?f xS 2Qm`W(ᔚ{ejW¿oܮksY?q|4Ʒ﭅ c$[y te:gN On|2 c^CPNl9Bϰ l|"6}R BٽZ %E۠>C!=fxLN8xx78LM3[^iS/A. eR蕉8zy8op(P)uKS ҉'* S^u{ +롟3Y?~MbIx6=n~o Y ; 6>%4*w[9ns7R{!*'B# lP0"} IPLjW1zƅ׷Ȧ#|p. p Mq /{Lа+ ٰ w2]/C[n ~[%7=zV\GϏgP?+WcHCp&B|(u NN ^!gVM͂cؕNYÙ,S=5[ `rkkΜv}F,M@(d_,DaE] c6P\P.e}=X4XD+Bݞ&):&me[! zxqe%߿%8)Kd7 >% K WS&N$DAFW9l &[_#Bm욭^ԄYZffu%*_ Nė54 " rc >לdÃ`M?Gn7%V ]%|C01Kp&4r"sTH⺆2V~~XQbT+xd>OtRˊ _Vz__n' YIPoYKA%7eNS)Pfw/uԍ<) +Cg  ~'ψg!{* &x\( ?oi.-Y3+? {rׄ@. cwm-4 "0~wᄯ/Ws͈*gJ/.BN%ٽ ǽ@I/dNW\lDj) \3Đ%gp8x- T9YvC0_gi|,u9Giu%qE#֥0A޿[=/OD9Wo;3u1W<؃KyiVE7x*@biWo[d M1w34o"0GGyBbAs|&bi~_-NvKKП "BAQ}P0ѱX\ٚ<:ː ,b(-{"]3PM P479oZdheV0S~v,{/ Z wEA|ZpZ\]I#4qɳ:,͔|Q\xm GL8M|%j 0)I#}%14~d{g:I2gWb5]j*_$7%9ȗ4Gl/.eŨ!6+horVne,R߽Y}yoYDd\P#[n #x{ɲ8f@Hj JA(iqp1T4S<HhZ#DA:mU'=4 euVYKj=S+b_΂rxI i\Ş/ \D/ ;$\eKp$ h WeQ{[qzrȵ0X'iZ:0 d%&X;nr+x +L2 Aa[b J5 v7`',0+R˘5\.~.ѵVUA"Ϳ"%Yc|ݚ_]Gg:b0 >  du]X2k(&RTDD(]sGf,DxVS۰EP+\f9p쮣%&+4P'Dz$ku)#/r@O;S"Z-S܉`g>rEMbj:٦k:ël{3ѹ}W3v"l_}f|GY5Bqk" xL!JtXfK95 b3 gv,;r]ؚ]uk{>4NtM-)0Kݏ6H@iRw.7T7Yfۮ 7@5=.QƏvXMs\~`wǕxTݬ?gy%b!GMO:&w#}'rrA%ݠt=7Wa› ޟ/Pg2eMp)DqvWp H xvE; F| `ن3LI8f~̈=U_ n!9ݾ T@}aξ 3z( bjJvtCo=i Ǧygw0&gjfyT1׼D2[+BօsSf42GVԇk`$E(*[D<0o|@<[7tPbJ>eroTާvhTbTurKs#s~ wOh3o`>59<?.Kz,UP'&tаo6Pa>lfy39L=#I *OҘؽG$k<*7a(K;jϐȇ̕ƌ B}dٴHc}_ %OCxOB9춿gzfj$pˑ`buR̎Q$^I&Y|c$L:0iSFdd 1u171nE:z h=av h7i"׌v^\C/:4#=y@LKe;{d~gFiޢV?t}VpFsI2>h}4e΋Bh彂$zY7n?V.j6bݯ(Fx-LZlT#x{LQlB \ސ㽎 o,-?6Lh/t8mEM?{ֶF4%&F'w0;ՆM6n VeFxx3-䇽0 8G' 'fOkjx[Km1lR5MGh">50z#`_Ǿ]p~vff11YI刵Y€JGP#J&aPb; Y;r\M)we<W:AY.R'ayш IDATyAL`䥀J^ ޻5TC`= xܽ9>K $z} }Qj^;s,َ$5z'% N˂I0BoHD_ͨLi/d¬kk;ǡZWhTNvKOos&f &/ćA;.c+VUs]n>)ֳ'cvPD!fNff(ٟ&J- zV9jeYL n m4,A#ֶAoJ%$=1Ea<^")VU=O>qYWJA &Wv6\Jo ߣԻ6^Q n_CGe4B_;{mpi8سV6/l|Y;.jOSE&( 4buRl)ia&`{nyK MY9 \قN>&/:G!ySkh԰f^BxT^oD_сlL"I4nLczDI7aѰzPIp5[B!3F  -Ĵ&H'8>ǝ>N8VsxTsZԞnɻ >L4LKg'﹘WWgsqЭ3?! T 쌇;K> ya'Wk Ĝ̙ w;n"y '&pv5,y6;wn6# `|4tG2T@+Y:bA|W(xB;8mkEd9fҭY_<-s y?dTYvҚ&cuUjr ܵK.htֶ'2IlZq 6}g U˱]i.?j\?&0g)ĩ=[|JCx y& G"Izs4JJ$)өm R0$Н}eylz'R>ٖOR\L9"H DVTѲVQ,hC|AQij‰LS\`]~.Q(HԮ^#͎>:AWUgڟ/R fisbQ 𤅕ݔ,,Cf\;T(bGc <82:[^N9QNoFj>s+LTs3e'a>8S^ztML"S:_HLI˺^sC7K"?|h^]uYóg7ͽa4 BJ`i[#,K+aBBg0A`f~$!lH)a6J.*4۬]9Fk'9'Cztf3Te[>H/t͝byT} |Tf9ˏcb\ )9boN1M}i곃ܟ <0,{bfq/3͡rЍ2$I¢h2(N.8M&@vr;$mVLqyx{L#تbsEv`q”w{wg,0IV_FQö{j)&d ^HGQ DL5/8-U\p0[M ĿF17Z X [CkKznGC'=d`f)<0ɽ/)9CuvtĤ=s sE{eedSb[uf~$r )A/;U&x 8yc橀#g捡Ų6oЖQ-G ㆟c2]+z`Wh#,s*FZ5SqQs̓U}= %}g98UWƟW3{:"k-bgܟu.Kʂ?;` ?,gE:X? 颴*]9=qj7zȘծ{ǸPF1M=(XtZ]H ?Q7TRg0 q˙=I,c{\9/ލwǗ|][?-JGn,\VyIۤ̄P)ʼ¤o"NdΈ jx(,yZ Ft(د}XI%69;Ǔc}g(-1EəL9A>y'{D lkELش&\yWDa.Iãc{ U@s]MD?YqszquwjG"44mj"hC ΌXm^1ݔ| j7ib\tV+ӜϦ~;8L˼~BUŒdRM:J$gf10g+? 03wn+,[悒hlNbv9 .(;8s'}97H$~vp^g_A'ryԐbdLsS%\}^SdU4\%^}4oG V6 S=\N|bbFak6/@%! (P}~p.IbpJNt%H3g :䪿%u`!d鳾\,+ Q0æd"aW@%{cOcH[1{o>9|I1/dx>4 LĆ̻p[*mͬɳ`bGH);~:/|eenJYr}&UσyV#51LmCAĹ"lLjx|-=sv:{ UN}9Jn<55߇k>'7k|ouY)O39ԯf F#oh 4TLت€ t mD;z97s9ӗL{+K')hXoZ5a;pVbkߺs&WeWB!;"9,0;ױwwfDbv^in@9{ r|\L7*$'t";"fQuc>ʞxt`zĭVPzr"; v:oNA̱EJXBЁ8:ׯ3׉rkVZ.q߰I?°7CE0J E>+`NwQgɀr-d{}f: 8 =RDV}Fۂ-FnĔZ&Qܙ$O4y͇ ߀U3ZY~{v>wQI~*XfKzyaѹF4m1iF:5e_1U~_ۧپp7Q<ޘ)aۗcA-A7a!:%zo{48TKecce_. 0QxLw0:;`>3ysⰗտ{UQ[ݰ[o [wM =q~:/olM]s' P<9+#tVlKy?[2$%>+f'I ]oi*ufM="<0\WF;!oCe/r[59R+5P_x(F9gRlv5(QBțb? RHc4AK^`ޤ{&J^clH'N|uKd!2 YY8v]2$9Sb:)XioБON gGcU &Fw_M<,qs swGdku~G~$&N@`uӛZwR.Ͽ,S<7 XáKeڮf0e(5>d,]~n҉L޴-Kr~lb{\9~m{3|(hЫqLՊwgB] Ty>%"k2T"]9eৗm5AIt`jy՘yܽ:gA_{?L]M9~-6QK ~Į pBĿe A.&;EQ{r#l;B qᮜ/F'N^ a$u`􏁰>*3~9cI*I;;swCΤ*7}C̷D?ijtb_, RA ˖CݞKpxo *K'5I@z03<3B@V`p%1 鈈=ky3Jm?_;2K7 ;ƛ n5|[V=/5ϐ91, fݼdHұEf}´1Hc5Ml1OHcG!j}i ɦ8 Pwb$Qԕ߳gPx`ș*ڻF*#=I} 9(ʘֳ#y#%~$N;1\IԙԮuMS`5)Pלc838T͐_d^=gBH&ʉw dDwYkR?NQMig$hdb _|?|WR%*qpdOq)}+$qμ{5gŵږp)Ǥr<ƾ)prpS'B=|̻0A;)/8~L:L41%j?5ϳpD,tvwpcptG3ڳLy(s"tż@ahMĂu~mJ/ 4ۜ1o=Y)!ЗV63b:1+ys^ NF잓BTCũ?t#n%Kai|V_Mُ!’O'ۘifL)L=LC(ln]Aߡ[CykVq;3fJ#h+Is:SiXSᴻzfVH|=0z$lD?dmx>~$wH'w_U$}y.O30uFtlz˽Ga>*{LFjvh6XE>1xSZI}U?#&o۸?p9/lޯQBj/dImi{IX=0>3$`o#vzDc%a\MM|ڈPͪDC[+w&qIƈވ"X &8m1F+22:!0Mcw0AKw\& ge|˚va즓36f匆x+Y0ž{ e@ϵ?ǔ  t>zՇѫ#| }[Ų_f3? ?4Ys{OǼlM`3xۘ3.٧\W&<.&+Ӧ36t_:Q2b g4 l&x#qݝ{ի#>F?(JK>N``̀1/Yun|JKI\fr譭=U;ۻɪL4?T$N>P .l,${Ÿ! 3ncu㐳VEͱu =Wq_1glL{Ao!F쟟['+<9w߱3 ~DqKsI%{,Q_.'r$|. T߲XǙgBV̶.hԂhX۰L9Gڍ|P=Qc e3\$N`x䷣x(k}FK@]R3.̌P:cVh:>L곘;-i4]&y'敶nйS .}r+&Lٵw]b'Eri &#p;>Lxw̓A$DN/vN݇Y9vvύ!9ZM,45p`a$ 3uN;]% Oo2ͯ&vM: 8&}>Dh7s| 3b$}u3_k)if)zP9//kWs"5QfQem )*,LѓҁȚ/ Dx?7䎌O F>y l@L E%$zP5S<,1x۷ww[tfl&*a?[*PCD+⪿ 8qWT>Z|kbL_ov/^'=6k{v/=35?D/n9ߵI>9u !&"iG_h0%  p 3 IDAT "(/9S5ً|Yci>=Wݑ  M>'ߛ?_p}۟u _<&}a]dkt8FpVa dSÎ埀uM״3$gҫ\j_`#J%C Wh' 1UGRNv׵s 9?"~4\K}ϟi#6c8m΁`iP$0+SW;|( %lA̞/C{}01vbv~1/3b NTAfx)^x' ZƾMm4`w13߃}ӽz 띔#tjH"v7K+%Hă5bz[QjjW/`R*L#M%|v> &B/&w+,ERa_-mZMQ 8%FsMH q)=en{gbJj93H15gAd_v sn]?L!`X V']ߴ$Ü ;L҇qV 53MP눞J?UE[uX`q'ׁ$|/F&(Ο:XY/9Oͥ"ѓ[:JVAָ9a$aq&AZfJ&&@# s"Z>+qFV*Py%(dx@{ c"suۭF8$$WW7TQ^]Z3<|ug i%q~;ۜs5/@ KsN-^8kM+mi7+H(r8~#BY3TsfM>A*-qX녲]oQq蚇t w5g{~g9|-|.`\ O{>cC|M YOdezO51\AgC7>+2L_]s]`P u=Pߥw}K2AL)χ$֪琾gcC萎͞u^+Bg_wSAr-dWtXk{B~YV/#eC7g(7ՌJ ?w:bא.^n|C#'Uu@W"cI\Hb"]Q)EӋ`ڿ]zrffqz0=Mm̊5uQƙԳ$㚦bW,qJ$K $2ӷCW!; #ϟ׳H<Lx+cWBGؼ~AՈ8jz&g\PWyO ih-pY왵bdmZfy#0^ic0f"rej2hxa䍐GpMt*yR+&;d|5x^ \ğ=We*G2_ ±Kk`2]Y3q{n|y.O1ZE?Cf423ㅦ!{Ǹc2P>|4mk_9M_l)ekl~]12,\11g9i D82uldr7sW6 " !暵"0TvsxT~~ zM2lu`bUNxVù=` V+}JIp +zsGmZ#HY^__F e(s4{fފH ^ <'^,7#^ { ~E=a3$H3y I~5F-h;.NFsBBe\bv6]4\-CSgähWBmCQ3^( TWmY)AvFޕ6I@~y 8:3(JTº,hˢfk)ayk̹z YpL,0i*:/7\pQ욎0qc>DCvs].Q (4Q*Co.}\ ?,֠[x_3zZ yӰɳN?S9Ç&]GdfvA}t8:9'QΘڼwl "劔Fjgl cJ[kGaD١L?I~u9/oe ǴlSrg4ozX/%,2@01ЈFfJLBf#QI*7BP}â[kߘʷyRh BUEgqP>Eڛk`YV,95ź6+ZqKF!&y);7bm7f|QOPnkXBnxS(g"pHU5X΍;;~~&`HSQ3\#9nW`LF,}|~z?db1Zd 6-zSsqfc1/ޗI%s~MوMN0GgDR7H=v,X~+ϯ<=Aq*v;2,+c`sihO2uM8^ook1̖| VAz3{8eJ4 to,u(%>ly<7&y&C-Rp^ >(1"B (߿($mdXO]S8}N=zaff(,*;>SiHYOt؋n$1`̗0_v?'V0)ΔQ6矼DOcffwp ak12ΐ9fv[B@ͮVJR&"J7+!VGÆ&eFJg/$ɒº&@׊W#o`@nP`jhx].+'r(ydJ .ΊL2069%IvR(qg1s8NsЖ7)5e)k°鐳b 7^]uƺ Zv+Y)mi3|88]<2s+/ aY~9{oqmt=,,YC'版̿wCe6u25dA*sl B z\!COKx^#C}7sڝ A75PDoS*r|KAaM@Ba&uKRE0bqY@̜ >К̓{,4mk-H&ϣ|?W[`Zf9n]p~l ¬cwT?z 2WXwA`J-҈x\,iaƶma`[Ah˶Cufw{Y?gNuD|2o 17$=1qJVe&8}+)eC~oa\t@=DCj޶w1TwHoa>wƐ41 :Z(j\]" +5zci{rdJ[B4M-Թ/)G}(w;q ;N#_N<+LՔvߥ+)e]/T&sU.PU22w@?v׶3fVQw.0zQzCJ?` xTNTjM\Ɯ&=sU5:Z̻h$l(F37輁ys)!jE"s=Z~C&VB.^=b' OGSs~$Xmq=Q \mI<KA區wj3^P-M3Uvp,WXvOU@X4Bׯ_ J#^l"C{|Fѝ9- g&MIG?7;kOGZ~P?s2^O-eGQΑ%X ω( W$- ;,#r{!NB$˃"YiL&Z] ,k!\3`M T$ߨa]eź(iZgdVIo75>)3!n},~EϩaC^纵p 컩2W ]`"j} lL1~g7R" 4A{%;?.bMOO B ,KsB 0SVs`9N8Y4ɓA5>`&y4uޣZc i3G~fE۱ǢUa0>5O҄"+w){۠t1Iuý5c8f\^jDT9jtC)m>xm=ۖPRP3;S0 { 8*$PGS)eTZŸe3CU'Hs( b&"+,)9;Q0^wE蘋oDЩ-"KSBLäJ`pG UN79e2>@Crqnea*8W"0,?\ЋZ˦k,fbh!ܩ~ OOK41fI=BH2DۂM ߴ qNlj*wFVasHҀE}D]b)ι'_Vp$WCtydg]Rg8\#(׎sEn*O h/2V_J۞8 ]MFVdjG@ڐsFv8?6y. Ch Ӷ(s`΅*57Kj(R\X6%kbI6 9| dGx{xCGKiirA[Dbm "$H˲$MB@%loAets: wZl&Xh(a]߰,'6X}G8i. :3Z91XIP?VI"Y4ݾ$Z L!jjai 8[kRvKcB۶YQZOLSE#;"XA@/sX5'/"VԦ>%_dnXDZͣaՈkHNdf3#dun؍#N3BF:ai5qCόcް.ű5i^XYAy[f8I}IX CY\cdi xnY7f"Xu&E$ -؈Eۡ+lUOe$6 ٛ c/~Tଝ 讁SEҋqhq08Ut>)"LmG`+pOFM%r)A:{Dݓȓ3#7“;Oσ;=;!دR_Dm.M];ZfCfWMJuMMk bve}~Z4ݏ_FfưE\Z?aTZ bwm>HD9X{* tCL\lhmMQ!\3aL I< 5¯_/)#!;7 F$R.iDi誎*?wfxy|}oB"2Ϟ\̩M9N߮+?~\ e[`'I9j%nz=ΉG,E.;bTfy5u#O2Kh.L]c-j$m|ڍqݳbE"-SѢ;ֳ7%M[(k`\F̵^WrvdNIRU: }S$mI髗hȄ fCNM8E/6Ыkb͹UEUnŵC/2G"K|,;fk>4#AʏY2|1 5 An o|iswDQ BwĸH]3$gxFbתΉ-l½"*ߩv~!Jp]`&2Dz0yaĹ&Sg1;ʥ\SΤZ:OT^(e( & *+ PB˶z "|5L8m]<ō#"N)-,2\H)5(rѹl`:G1p5=aйOCD aKnc[b2juhuK?QZ'mf}<6buAo 0;真yAF;Jzϴcm*‘x`fnbxŤXwB*(6ȄxMopdS+B=Koz8#6_d*=l 9O#8a նGuJ>12*bߡC$J;2 )ҙ#t$WM)S3r{}K&89S[z&Ћ8zLܴL-9iHj KYd4S}KeL4<9ÌšFō*JI=Ε,ʆ{%oE@ kVIr*]x=oaݠɜ0G!6IUHQi1Y43KA.K(-S>Y+5yHVtRtZejWQ n;fcz9h$.scƂy/b$=掾5e4k}CŴA2uKܒ]C&q}s'2D80o`_yD XBTRf` 3q0+Bw {S+*t B3s%_EL@ N2N[47w 3FyO ;ʻLӨ\q!1?#rŘz-ײxzG[ް~P)jqkb<o9=ͬA*g42H3>|fis%k2a6}#`fg o,]<.rZAry^\=9L-x0e}WcHTzZ; -w;n;cy C=I;vcXWcX%H幛'R}ɟ7'afB<acC Dݮ5B|JM2Nz$ 3֕kl֚2(J7Wn+, Pe򻩮LCIT^0ާ3ҌKu3.6'MM vi4D!w*g8 49bDxu1LKY  )X=0ҀfSWq#71ȾP0}ۡ}m:K 0$B*r:kc\I{wAo V=(!?C33R?|J٤E ЮͥGO60[MC'.TL.%*}s{%! 1 C"E ]uRIIS*fJ|e3VNVPP] .\^'x6E6í:SE8;QN]Jsuc #WAjcon3v>yt6u/e!"ndsǶ ɍh95UJg| Pi*o~f[GPvt'Q:08S8TE߆ȕP/EN䍮WSĨtp7Ajxmfd.r *޸f45fGeo&9u n11)ў$Kk8zk[g;j˲ROЦ1)3-]LU+&tf_P =\$3 Ud%e,U *۬j~$Pb?ZAp`:1YK F\rD6~",)2˸XI/D"/4lߤv0Л%IRԙ+wP7qqQ"5?Ebua#חuc.c ~ f <ܼuV3R'r+CZއ ~LիA)C"s87كdܙoSՖ. !cq1h^ 3U6s:ܵʱ-;"hQ2w~gC1S/_oΓQPhƭ}$Q0)9^Ԍfj %bC o;RMzOZ]͓8b`9Sp-)cPc[o"Co"1`M¢5.-%M4 ԐٿfMčgYg\lD}ol\$mض ‡4%uD*yjoQY!$!ǝc<ا1ޙoB|-hN!ٻ1#ξVgE~f|Mڏ5Y0~CT MD!Vg4y4i:3?!@W_}|UMX| ^Aƀ2uo r&Iǩg5gࣳ-[0io8 C{y?ߘ ٚyvZr]  $<)xr -s.T&l`Z4d%' ;Z#L[M=z.hݨRr&Iዔ9$F [NMK+aRw50/j!>%&/?2-ǝ&d݆d"p_{g+;lu&-ہ&8_LԑiaMHa\8f8ML*\ ٶ$Bqk~sg0$djyȌЪSklʠŝF{ɍbWOn<S4t&J(sm$T# +Q5 @02|O4fF͢h8 ir)&IֵRS裞ixI$c]D/2 ~YuudjVg'log!K,)b\0Jwh8dZ#صċDžJ{}g nmc;!VőĤb&g/oc.9kFgsM%&9QxƦÀ}? Ȧ"hreۊeа4vG$r6ghIM~$=˒5<l@:xjd審IɝsvyNi쎉K`=w,̰P-Qs~ hiνҧ< rQD8XĦ 4)!̔BX#C{9Yښ.9 2{ERg:$'$J,h&TQ"jݑ?\RVPlbPNƶIyk[J- lt,.#W|~fCs3l+z{6my@vN@_şKʆ䮜j۰EEu0_wI #gY:GG.ܫFvH F4{7&U"LP \Yö;n<'?5[ly> (KU8r sƖR FP3ãQ4lFtLCǔ5 &0Iew,6F_a)~1-᝕Ib"1|C7MȤUˮ6 1rWyF.βi =$XCu  -3Da[JYd.'s_=e%l/3ϟ`fJR5ܜ4N[RaY(h\0~DB#C)]e,X( A<˺;DȸKF_5*C%h’  uiŢ$Zy <_@qA `By||Fcugs `&3kִ53 h C{ L}c޺``!LW]FyhO˰XmI w=Ida!aʼn ˝lde.jY=-(s uNm4eJ6φ߶pJq8ԶorIWn";͈Cn81<3*?gmbê.eKlI@4zaBH#84=e)#] [3}_(8 * D i' XoˊFo@@[%Em*ɵL1gV^z,&xј:lb*SE~ڵN~R>vȒ>ZE;M IDATj>l^(י|3JjS9q\q^9 2$LKsVSޘ652h: C֍Pu ׏ْoMGLLՓ CMz"璙4ՒVr(H} 1sZR Fjs ?y7!y;a&# A!v$] PۤA{sQ>K8u^8f&+gdd >YUIPo,(Ni}>\Q9ɪk;~&Qf pduLF|}9B- 'I0 5vnˎY}Is"vGw6 fiFX{FUhG>̭mfFpL#|'hnxOjVכ% uC0%"qr@4.SW 4V@$fzco%)#[oJ4ѰKoCkF`kfZe9Pk:M~:#|ɿQV.IVo(gxD]eH5IR1ґ5yܩyI9K|7Te~ `VG ¿ݏ$KZwu\  woԱysY ?~rz׽Nȩos&`s{;XJиX|,7kzE0cahXd[:r5ʃp21Mq4'i zhD5&NRc1 DR?gB)d~*B1}QDfڌ4~EfY(?FXFk~950HRvM·-d6509&KU!~yI`eCwRa)ϑy;>A3"$%.*ɈE\ X p<{q¥uA~䋙5 {۝?NgHQ3BTщf}XnK22ʤk$Q)Uw71SJ>=8} 9\ kSRQr=+ lf"ݦ$Y;"c hmiLĤR$7E(}_\cΔYx8.18J>9汚'0~`kNݻDr IV=IpOo#$9|OR@P 4i ڦfkf #& ]tSps +h Ih 'DKM&AOp* "WW #Eo.NTW:0)adWgb4)`,6 '̟jXTڵrH1Yb|~,vcK`keێ㶢"f詪: zum[83ubάL8Lsh,5F즰L@F(o.8r5Ca8AeN:լR T+T댲L“o /:4C`k2op8a⵾_!k ,nRI͈!.tI#w恘0EbGb,-eCz#c.1Aw21wNT顰pye8h2Sÿ~'dG]1j+)3 *zٖ]5c7l䗨d>.@6:q<.*jF`1"$D#2~B԰UQV1[qv_exY@rpKށM"Nu-nG-3'lkٖMmιA]a s)i 2>% Npvf\In!j)PEvNEDSTx6U}WaO5srs e$-HRmñ/Suz:PDǝi@0WUBZwEay`\UX 缊?sײxoVNͥ=%vuWHfka JwNÃr3'.ܦx7bSDkY3&ix9.iUq+!o(\3u5biO1 UCn,dfdxIVjxp)/"Ȧ _LMO*9\m”ǫ5\{nVhf_\+c(Bl9(*m#r-np~ ;c)Ss>gFOSeLwl |_}VNcm~g9&.ejaպ໣@M 0v 9AFLAÊ1(VQ8@ @3b r$go g-8KsiF|El''rg4lrM$&/7THoœ )k-UBUה@(5lBt\6@4d=HB<N${?ܜz6A;pZ"zF&%7 3flV٨BR: _Cᴺ$IARCkrkrY͊ ãq$3}ܑ38 ֏C~tfĈ ,أ?8׿XU.bphS`){7*1 UT45c8TrҤs W4Xy;,R] 3Gj r_8P1OBLqN3 %(HcC1 D_,&ѐ>yݦ5=~J۠CHZ[kBxvBeǑWRU5ͭ?9Tuxծ BNkQA,cc{E>莘$?'G-,)93">FL\6pپs;Dyϧp\un] ?ڑ竽lyJ&?MUy95>#IlkJ։Ẻ6~/5  d6K*$$88b׬: MX4<<'tL9QREݹZp!{19^ lJ*/ɛՎ-lɓ(jq h@MgkQ&@7T 8&)ӄw*h : R'#y2H{87 !S&L{}%U~N̘-hGlexmdgW!%R;x!CiuW߭58)\KdK 2;AA9=X; T Rn+]BP% Ȋ> S.HQ qA) |8i3FRF1Gu;ÙӚ1IϋZ5"|z㬎u+8:9\}N$L>(z+xFT:F+NEVnOhE5u9׮~Ρ/C] Xl) ƪ` A}߮ HmWS"r[7Ck / (6'x@tKܠ%QnPA&,d?b "|<3߰&]JA1:`{El$4G^?'FnXF%Y4"6I zB(ofNkӜ?vaZ*-ɌF1@7,\}0X ?#m"4qEuu~N7e.o#'Yr汊QuV)JqS 4(廢vp=밄g4}E;]̝.C&cD$>:.THM.*lW] ">$Qd`z<8s(jvHI:>zxd*d6ȑ> :!gxpPiĊ $3r7ajl (n~Ucj?# 3g- yJ_fdnz DQ3&=fSMSqRo`=wn"U8Q+лDzM_[_ PD#Q> zλ Z yлJsȢ1 6.n>@d渇H֚1 ;P ׏?5m/C^>A {erp C(fDҘ HuC| SީyʵL"sϘJ͢);IH ] SvʿaEl`^fP|̦Q~UkcCy]6>9 p3jڃB*ߎYz;5ÙI4Bh!ُ O@wk?Pm*QAs3Mc s?qE.54;io OA+ 8I3Tgyx 螆8ljSGhl9`59(nbA zu~Vz%Р})!jm"孫Hz'0v|e ru$ |,EVP.JB5P3 C*Eڲ&|Ǜټw+AO U}FAvXAAl+OCt"~mM$Q 1sÔ>%A['WTqALC54|3Ns$_[P)4x/\^« UBa#B}wdݛ=zo%6V}f nl,?T P;T*jqF:t;q|܌N<)= ":N qw/Y6@+@*3ՖFhMk3-C >l.Idž&@οlj }9uHpvm"@ ŷQN41>,YZu|;CpI1G;*3 ~0= ]x99܀ݧTYu,ʅ%퐯j%*s^l~f )3f ' ] jSC悝Yި(ΜlGF\nXBO2ZLC޵!׻ ¼+xtfH5s"!y@W7 ǵM"LB|הwjʜ3t[7= 8 yem4 L|4X!6;5F'+=C=#߉REVH]K_iւwvi.фs˴^Q0ݜlv6Ĕ yVbWaPihR>"ul@9c$Vti7>[Fڷmg)S>SԌBy1IYa[՗Se ͱ@jm8Ƙ =/BdxS ١AdIa'&?4Qq6F@[smJ#D;үm|8Da=OGڵ.u/(gT\*6(T,QU~9huC勞Mȧ2jԁF 6i_?3Cn9C2##30~JR` WPBBT;FQDta zW ·saډt`JP1XwԜ1k݄ jM\㽘;j%8 M=ȁYeW.9AkƢP֚11H"GLkBcd}`0̌×u_'1%@> ]L 4G@(c)K$ڸ("rzˑ*@ՊUVqT|Lܯnc!,^3&u cCHʼp'ŨcjL$qIGxh5o"cp2{&2O9[?گ򁻃u?JDkK RT{ ĈšԧvN6(@CUsAK$SS;jRNUж((M# 0\vߋh]NAZж rf2ՁUpϜӮ/4@):*)51A?s΀D`Z{"֖ KlR7; v: +40ܧAob|ah\ nfOJ?aNMssP=!G /Ƨ'A"l׺JK5A7ْ5FO~Έt$6T(咰/VhH rZПQ'ÆRsǑϸJ5b?!5+-(ãB]N'sdI)_O -H/_wiXs*oaz"،q$o^S%[%B6d҆`[<T xmVQ.%:u HD Y*` ̻\f*nli~U32(5MX12JXclO)୘i_ɳYeRuIWt놭1PvG{oVZJRitgQqFs LŠS,-Ĝŝ z'Myո(s?u6Jzt {/%t.xp6j>~>Zk-wʚ7N2Gps~8!*͸IBdNI IDAT.uٵ|ia5v)8(:a@ߙr?hcF~]57XP-/M f3g*;VAt%[nj/kPHH[3$jAiQ$o$Rb"dAvgpd@;Mc}4D411ȯS:B_j";by(¡Hʚ/'u$Mx=zN]g˶BezsNPf67 xDݿǂi% c" [V}CNl_Ka{UyM jw"].>0I9쐑.Oy1&2wK=O8pQ".[vJ{i=B43*G;PFo~Bpߓ?>+̷/TӨ=zvElƢ;#t;yprBj')]8T_NRv<<^,oY~x8{uHA?zJ'q%K!I(hGNp Q~uxAMޒCPpVB2[^I).r/鄸`dy*Gq"%esCVpi>c4޶,n8]`&B(fwеqy7E+3%Z0/qk LU,T@ó@!EܼgW9ۻ~q{x1| sH6<#%!:rdR%FH^H"1秹5 J\l,( >C$ EIUtWEIVY AȷM◯S<zDN3DRHaQ\c5|uW% גkj"2U:&XzY,$O}$@ Ѳsdwr| y1xYˊ8Bcb Ig[[|-W\!mUHcHrN~Y\`bLohPp^ j6#᠞xu?ڌ([kXh4DE05?MBO}}J6_3<ud,[dUCV1tvo*'÷Hn5=;Io9KI_=D_=_uAԲU#d+~-D4C|%'DZ4{!y[BF浫+_Dwܾ5J6_S U޳Ƿm\bIEDӊAŰ[P3sYRD1 nxS5LGUB\5wUC2Xc>U3FU 5GQzZ2$~b/(7_d#V鲉gh4"=|Qe[͍k?qͥH7l`` pZI&.9X:OW[< yX\_?4)T DwЅ\l{C/`9D R@vp\@C v\ dLf|?OaCF;H7/YXUԬNVBϜ%UU pi wDX̸l̖MX˜`#S"&rM"#N8 <՞L :` Izk>CU1TkpGDpRݿ5:У¯ ~MFQeѻdݱ+85ʿ-o(:)Y9dm:) s\r%9p_91 ΖJ$+ _"1Kv޼~  6=IfuЫ8z"sp"([tՉ(8vS|7Z54>_q=ʵq^_=_'}B%!|H ssX}#N]Ƹȳ pPL *\c ځd_&pE$2xKwipgϐRg:Bsl^ ч#}$0}#$>߯OtJFˆ'[19"&&hLfcО;tCÄZ8+gav$ר~<;m\ԍ.GEӓhp`~ G,´D`>LO>3k[~/D3ԯ+}Ds{.`$\׍v\+_E J*ݚgrFeP]/Ptך?I\ƱJ@3Ubk\}PoMPT/twFP,.4U/^ ڴe/R/ tfZJoa׀$Z\ߩnнle$N#BPַؽ?ni|~>Ձ}VbvM>ܴAÇUil&Ou+3M/^Hnrri1F*^?od5rix;t& 0ca`}t>6yvd^qˎ}L&UB!6g@1j=/ezBsaŘϮij^\P'`gA2MV=sTn-i `Z2EP;GhZRmmd-K&J I,yC[sb&N{{u#&ԉ)# Fߔ(4R`4!Hމ-*xT1@96KGA|>T; 0 zZ՗خꡥkI8܆ ] JMXMFpx4L<vnt핃.TA*|픷 5)##j}1+a;q#.dkָ&ִ€ܽ::4aC883ퟳSgՅS+_w.RB]A8\6EÁZCwe|j/HD#8sv(_@8qDVhS&5%_}(삄!oFQ.%͖ Z,8dP yPPM\oģy3 #_o%Zh_KeZvxZiD^䣥y1(Iٯ|4Ԣ2#qf.mUjʼn"4t;SrYZnT> }t]a?H%]PCԂirWϖr"-Ķ3w""OGڍ=ow:-` Nd"V!:yՊdJaU24&{ewfVfU-=t^vnLf+? zthC Ff"y+ rQ L(pwRs LĒ *VdC=Ta#1c?r=x bwi@t >q|bHܨI^xބ7őӕy&e$Ƒ~uݯjH\"+^n.+&%ڒci3אuQz:uL/.;@=_ʛl[:7pA~o>8wODVƦ͂`7T!Q@͞@ -3jy>Z۔K|\eK.L9BN1] E{˛*'ZʨT[*M).z:&& ILfȮCEYԓ@=Ufaԫ1̵B5|1\ eeplްv !N#w%h'00KWAAB=!Nrڍ]'Ū]w}_3'`#t䢣$C$6Y5Gcz*i:n9d=,v"$Ac9=s#ٽ ۚ7mJop0eϳ~d?ރrr-y料!i,;W(D@砺~l[<&h͘oaј]SN,ޝpCP.C J&sWq0]hDʿs=3贕(̮GDI`ERM`HƅW ҆7Cc10 C#}17܁l~gt1FFIͩ ]T19SA -2jpjXK%骝RP;.CRO.}-J5C_WaFYv"{£N6;0n]ҸlԒC{ݨm Q;"-oB42;!yպJݩ-~6uEV UywX b>س 0j7(L*#V' !^gH 'Mk^Mp 35[v`J]Ϫ.w^j>W.k]CK2<;{#:4"=w|UN0CXI˴@XԳ艒s!z HUm-ҖbL^Z  P_*'W%ZsWOਐĭnZ5o>vm/3rDC6 =90o m`Q5JWP3Xi\W;ẍ́ GRY}5jw٠]F2*S9ޚ빱1B7;M.]u6 ?[벋4ChCy&F%NUq–{p0I$[\FD߻ys\e^p}jޅ $QW7jf/p $W_n+2AQADeHzFƿ.'&WvӴ5= &bàVܯePZClnUWMحwqLxW5H(ޅ'0:;0C7@t}S0I q\0lr`, \UE2C~0bpT+m_~5)У:eDǂ: IA"1!ZӅ7,zxTO\6GEw7B fLwlR!Y<9H @/Y:Z6S3{,oH4}+ Ap'QaM͙щJUBcIW;_kLӧx $1\ 'N \! M\ Gggi?{~DCv]"ȳSdsZk 䎀yi.ssJ0\]çQDJ7a3iUp}kG X&3ҽZżiױ}$PnoEL/׌R<DιywgF3hn  z1 }tÞ7o΁F {\26U9EǃqLsVAKB'kzj`4p~A-?rtuRJH뛹;1Fm2v1B8v7lG u:y(8r"6mm{d0ixDEtD$-WJ68rzj_e[c!jD|+XY@NsDϗy/T}enC'c&z.G,%bED?&pc}pfė >>x~DN%G"$!(ùIֽ!aAe'cUgzw Ԩ;c4yC(OkWZX⼼AA92r[z88N|?t JN1:y ZSJl֯KKlݳȒ=6lV+/E*.^NY6DڜIX%:&"FL!}'9"Rqϫ `q9hȷ3 L|Ov|%pkS0!٫??+? MGS ߶LXDW|G/v0g*sw8WMY*"A$tz{'֣K8lPf2A@#mAÚL]T/+_{B7#^ zG| :Sp `+@Fy24c!/meUSXt&=/ƲN}f$sIe?S (Ǟ&:o4v]1$UGr1vN|@I,9%--y1ϗ(yFe";}]cqfL̚NK_ˤ+=#*5ݲEP-)3,}DjW1'Urhū5C@=f竫I5J<6˥;hEǓ$o<} M NsLw [OZ,:GCLaD|cCj14)bAJTlӘ_ `*kuXiwRi禟].b;VCv;0F6 ׬*!Isd DwuoJQ3ca>Yb)Tr* ~M$J*5-ccTVhѓIj\﫵v[lzGA('$L_ -V-$Jrr]s0Ү3uCviaU.6}^f{2usyKAX@Tjw$O.< g\?$H-\sm%c PcqB e. IL;!.;UDdѲYKk h,l$~ZHq I*'`&ϧDmU710 Q8/G0a ehy⺜ggzX+֛XG|S @oz{|?%04>2CSB2JxX&qU,szc~)`Pj?jG$}*0oCI{ 7Ae'.쇢*mP7 7kaLzpF88 BGy=(jzkY7{ys08F6Tn S:U^dL]✆C{ykSxw69z@FqzV/Auuv,Cppb IDAT3t3?f/M/3'ꡠi@unG8Bר`nõ P] #UʾR Hx@nS>~AAw ONO;ONߢA(ݨ(6]v`RK7_ìTޭmApS_#AIUO fV!llK"YF vU*`ɢPNY Chsҍ0WfY 9kIa!T9)3M?[eHtN=GuV_^\԰6le2zW*9D;~zz:~1НzwWτDSA~mf7^l~p "?l$u\gfY_@|g/ hܛ<;l m Gwa߭~%"r]}bDhW DGX%Ud%LM#Ly.M.i箍 0D A~b5yg39]-J~$x@_뾫El  3~4 JCZmfxD ; {x./nI8iI,j|aD gƹ6~s9uay#sT(XAXp:՜=ΚH2eALi({Anwjv~kŸ)P(<9r^{<m JꡗX}Ӻr6ԊuڬvX1" Pb&{k\隣k pȺH>9Ǡ"'] -A8BoPz'x6f V.Kx`hMUc1+U^h'l񶟐5 ΀_% `ͨl3U {ș' ˚6% P{ﲗ+©;0: v hSHkBVGLThѢNx`cmFQ0h^`,Xݶ<8#M1>AJlipoPg-o 15Ϫ$? @/0>!{όkG">P }jvJu49eG=':;6R)ƴGIBoF+ouԈquko9[Z(Sy}#J5ׄ\3+fgǮ|DiOf:Bn:2 ILg{rDW=xh57fa-DQQkKJdػ r3d@ne}qʍl츮Dy5& F ˌ~N7@ AI­B\'6;0ފÿO6|G-D>'+G娩^h]acymqL WcM-mrx_KBZnH4y& &5~jtx-he/OC,4=ȷ6Y%c;K-> Y0H9aL X#[^ UxF 2LwgƢHc@;;SiZRBz|; $>]cҜZ~ײQ1_d>9L- uTQK962;'EL ûmTSuM Ja}#!nOB}en9ҭi9Qpɕ' d[=+f822U? &v>/kp})T޳ζ-*|sKQ, `^zx/lQ˲s$31dr]mǓ'!yD9ZwQKĻUJؗ~fz>D{}&滂q@ # &'.kgt]-mE}Ii{)'m]o4Il c4wcKkwEFf97B+s3@@ˊjH^ WPWYb6/ gC_#B'Rp-ˤtp\u|V:PwW37EI%ȿ6 '?pOQS}_O&P6#5|zIJIp4I]f9;h[Q[J?7v^Y$5#ze.a QUzg9]wA;ca>tmk˾mgFAL Ò $yhf[vɼ#4ut"ɭD猤O' G2j޻JrG5Rܫ4%_.d/2)x?GKv45Hh]XIE'fy9*鱋 '56;ոNAhLohX9Hwڂ9+ wJf?RɐoCqfOl8iΆwp%3X)( T<B<db>GZS?02%;0 =ƴn#Lqպ&WkEBUݶedOM7 UJ%Jl6f *A2!B;Tlm&"hL+8!i }d}5hd>1  31Cߏ#gz0gzAF8nk)=e3~{*A}G9H.Q*Q寡DPt9@ "EBSH|Of1&^Taq @3~H{nBGU+WIદ'bFSI,>1<sqN: ϊ*,6o(*Cl$ Ox;J2#;iSkqكU?r"@Qsvǻ?/s<}Rixcq] *[ yÆk?OV{4b!46 Ĭx~(L,_Ѽ.D mѾǎBIaC#J^'S%и 9]^FFxIN gj_14A$^[&矽I%<*nG^4wN>UrV=80, ш( 6HO h jdӸ)KoR>K_l^T?>n_x9`kE,;nq<.Gl~O$- 8zBZ1Bbj "G R ~ﰼy^H*~j(0J j{9らA@y{W^ٸ֠~J8H%#$ "<׼ I2$}aW:#"XLJ(kAd(MjfPT W:FWMM%9pZ%j[{w됂™zr)|Y?'[{ qF&&ݑ㐊<9P,csewW5ejVھE3o邆& +TRebW3_:^hf4_G }A@#ap0"T{;Ic;‹3FPB'`y"Jh@LƞJp ( Jv!Ӎd\o%[^7kZBw_ U$91P(K/IdâtwQJ;(Y =;^u֏R1ɟU9:g qW݉KoP&b$~W:֝%VbDDՙ)Z`i*o\9l1:D ό }{wD0M2'{e}1D p1A:bϽ95ETt6])_W"=]kf @_~qQ[LIa#MjEDWWS?lV֩!6~Y+FB˂vhiiV4iIຶ!J%aMR&&DLB@݂zp]ub|ʶO:խA׹Hs>ݎ$Bzki Fp</֌))jXV-|^ PN|WkpyF z/1#ciH 9*WP\# {:oqZ\Gc&Ο%>1 bɞe$ʄ=$OTdD#{4&1)44õ=C=GPy(9RoX8Ʃ6FK{sW'׎ÂaCPMm0i<^i}u\#ќJ56x_h{3fq2D;Ln@:Fy% (~L8) f]vkBոys3$JXOf*i$v}>]?2#K5C5}b8taA%_,/ ZH 'Z$)yEA; E`Ϊ5T 6g.\{6Vj3YZ9}j{Zs0)4mႅ|.931(_h H_\}&s^}/#!j1~{Us|{uDsleAӢjK(jux:=Kc_Ϝd7l#Q43˛_;Hs.3"S1q.cc4b1?`x'T5m@1jԼƮ|R<[=zV0­& s$\~. '\;&C3 Sṻe߱4bpcY(;3ƜQ[j$cz =fe,0D$=(Lqzg#Na<3xhلUtar u^-2xz[OB33 ]ym, ՛^a7Dɵ`nP? wJ揫 ]5 Gkd%3=_8 IDATw[SFߣi"M&"ӬΈ |޹%2-3O6mUkRѶi[3#53:l>y|V '"d]~Auu(tTkIY1ӄ68)_sIٸŧ4<G,Nf{L@Uaj745V'wHԩ>~i],zi%Gi] PP[rf( |ׄ3YAv s^T`=ڀ,4țhls/I:^._n#k1'}Մ7Lb_C5w8S1l?*e0I@ǼWB0ac>_W5&+P>vAW39$Y.<Ȼ=/(Cw̭=I*eIxA(c//[H&C-p~ %nX 3) NA쭶Cg&\  W607RqE6Y-kBt iO|OtީL-gdg#a_l10)= gĽ5,"юITAswgZ*l+IGsTV$gqS9ήbI9ˍ4 xq"8@Mqlb[{\6`d 2k͂ YDvR?wU&qS;}LR<"G4jɴN nIW'я )PLY tb{U14^ >Cc() r%S2p/^lϖutq0G mx ȘFpϑ; jZQ|pZ}/c@w>^OA0%;JUPELXi鸻qa[FAroLU̐bB[g'ˮ7;kI|:ֹbyptBk t<{ݬ#Y޹{q#7v&jSS$ШMY P 9:.{'nǶ#ځwj 3f.+Y 9TJ'ʹ[l\Atbʨ y}׎ @t MMG輦ꕸd=ϐР Zwn>Aǀ U"6CLZN8!~/w~o)Fa}&(Mxi58ØJ 5&~:q^fMJa*U9t#<3sUc;62ZԠ2!z@l]pi):Ѭg'$4ۙ"Ej>X s5zrAh %sraf>זB a0?;f3P_kXDy(*c=S\ }¨һbB+ߍQ%QR?)tIe 9IbYkcIj q0^-AFLcJÕiY4WPhҩNI{Yd&ܗPxk "L=Bu]3@N/VQ3H1Q귱DkR•obDjoPg[BN<]&kOԳA4Xud@``‹$Dw$!Xf>]X v1a @*߄R6Q_7bqj~3 6 וqAF 7М])S[]pts_)ň^o?Gf/Kˈ:Q6I?U[y< ء #./኷| $Ih0ƇB]eAؼ&C>nʸG1upꁥ1T1p#\W;;m3*-z79T,C26=MGTSAj& HD!Ħ')a.fF(mDb\;~vy&A GH b|_ĕuU_8뿀?׮7 's<45~5d%"î3/l`6LHlqnD햟lM4֨vL#ݠ!b\ 1Y7 cշk 0( ϧht95B8iR<+CFOIrGhL2:bpթ|iѨGn_{OYڌs? RǙLzsP"?J\\3gޖ] ̔ jCEkFPr02;$SZd0Ǖr0&_V3O<}C h0b -cD~il\-jR^%ok$a 4IvE^;Cҭ n?ԟ*-̼Ԫ~:P?>l;g$c2%F] *챻@eZ_#o%cšX:U\FQķ "kM[nK^oL )2[7;q_GV8{H ㌷˹C)a<@yPg VGD=1fIo@%wEecܿ|§'Ѫ{]g ??d'i# kp* {]1Q*?4,} 4Q".2n5np :Rl8 'f -8[Uc`1&1V A@.8ۑ754mubhCZ\x!KnLSG&c ~3/yo.4$`|QQJuiR"D D F(k(8sb>C|տI~lАo5<2NĽ{zs)8>2t[CzIÒ[xի}n+u#gy>%cM}9,hz|̇lgwΘx,weIEhde85,W:ߚK_Ӏ6吙8;@Q 9++d!=F/=P1OTq1H$R xgBDei&$o8ai;)7fS8W7,!2 AG;b"Y ʥCxUZ? >i w z~982Nf7KHbk"tȨKzj&G97#㤞QnSeeu,ɭOUiVq D&!@;Ć-ŝmJ0Q. B+k"{| }1MF:yng IW& gqH_ !:NT{.;4zgYUH @$-p#BҎk@h/h)@ZjZ @φ eʌw=<.Y]]~w?T`L)*:X4<Ƣ=QfԬV5eq'y5nAZ"`RJ*!@rCk!MY68Y[_2yL|&qƋ'FRݎ6r#(#}W 2Tj,'CrbJesLhC0kclW+-Cڑj[AY#/C!:,1ϯ)T/7w-f8D.-"'2%^p!iEBLBF` DEB{$in:G- T +)L'br$ǂFx>`:4!U3X f]"*7(O+0\`ÖAv8J`25H1^ ,Hƥ~21l&sitJ0[@_Z 8%٭Z֬WRV{KkqǶ*=(}+g[ګ۠,hvRI)[Ԑ*|GƗ1ѩ7T>S0 Yh?VeI1I%`ahPɝ#c2C5ੜ׼w2缾 2ΤX&TQ= 9+^̲|I3U*ieޱtNƜ& kYl??:jJjh{ԅFQ 5mӡj s}bb:+*e$JZSSz-4DE)j*IY*g}pɵޓTMjfBp꜅T1b8!΂-zj2S%2(ysD2}/&qa?%'$CWsWt<0kfMł?m4U{|g 1N(]8EI(˅?T1rSVҩ\l FY2 Hx+ B6T.Z.։9%Х<#:E$(vZ{nL1r9ZZ>M`.GgR{qAпilN ~Fj^nf$0-T?:teңw_~s*U"^]n& aXS+EHs% \G8wz0{ jfqT]Ǣ%%B~ _;j,k|Yw@Ni")W-Hlra&J¸{_rU]0rɌ]!-Nj6XJ/Q!C@edtTHzшc(9:0QE@ ޶(^2C>{o0Ro e](8UL۳j5S}/rdͿ WTpne=!K=n_cagHb._.M|܎soDrZNY$Ss⑶FiLe˞o_xDPEFqhv0^uIz֟,jmRtf)?~a8$AWɑ!aeDutdO\"d w\A4W /#&D{ 8%Y\WDI!c͆LF;caɱ:wePCqBN $Ink?՜={۾sJKP}T^߬DoXUռri7PSU̕pa/aGkCI%u 臺 e IDAT+$X~O:l|nYy̪f*k_ʿ_uBF^j>o2Qã,/N2ΐEILR A.r X-T#3\F2P²jP,"Zj3a{,c46s#\yT£Ya+a**VV/7sYQ xyle5bQoFC^\Eӵ=, -Ym)i5:ps6Wgԝ/<ɦX*Y:W~A(7u]0Y" LB|}ؾIk mĀer|pT\3#Ykij)Hg{HF)Ωar!r#|jlIcҽQ r>㠿H:ۡX8:gE6,P\KD]\cL,LguCHRSsNQY4 eRRL}Ȼ.ڇ\g0H \=}gaBp׾Fľ򩯷797͎iU~&[I֧+fM& #=2oN,b-ӿ=xEG[Q~psgskKdQT#֛r>{ m- h#pS;1E(C]el!HXUsuܜ6UK sdfZ eϺ8 Pr@d 䅹 v uTUHBM&)a,C0+[3¨ \(KTy+JPrE>Rt;tWb])4VR^cT^Yњ,0(]?8%1au"$()s5չKi]-Cn]yB]1qFn1=-e_gÜ#Zk~ldž)mEr|t̒pT9x)pSCHRh{,6jrQ+eB%6%:)F^qf`)Z[yaC[:i Lv#-rŻ6S=Uvay_i|huOAu&6+AvfK cp!A2 ?.T7')'PLx!2S.9v_X?T|t8Ol?Dp&EC?Hӟ  imr갶>zɊ3g7o:>kkѼj~K)tW]qdSsAf&4oIf ڔb%Se*J^4RiKKje9mm~ݫMc~;=4{P/ɫ)߳pW\;Plskn!^WyYYa]Yل)*iTCp-Մ4We6-fhFgk5q~t]8ɡhZkD /47^jt9a/{*J6Ng3`Ͷ"Oo 譇%UiPI;ZG$+@1Fayh}nV_xJRD荸mSSf±>t ԎHOQ+ _;"ٔӀaPޭݷZmj c(@xS0T祱kR0ܔQoctILF8k-a{HoI>a53-9aUu9Κt2ɃRma 띸s sܖ/B 9VvEooTQ*.cLA2BƋj\J|%,L[/80[75ƽ,@$z/wԼ&=A:#&pD)^Xl }cB q8Al8qnK*õ9룎8ļd ,)}q1?2au/L[-w G48o^wz[ej&V~{@*.͗9HA26-㻣ͭ; -}c'hظr2ӛN[~@3Wܙ90\|F(*Iف/]2Gc޹{v sIY5FyZV(Ԕ7īJW^F-q\Ե+ ǖF~. 48ڕJ9w;RkPѽ;x2'TtFݣ"6ǵ bgٻ /*lUTqs9o9&7o"sj8wrC(\4Nd}UܘA^D\j7Vlr]J+4~W4KBe"*9073dﲒPS&MtAzfVr o%o:01eU=͒KݼLU.G$SC/^Y>q[t|jށ4Ĉ.@K5 >ؒAni:lE!%P AS' gma^? %+ !/(04i4:M7ߺ}L5Pf3/UFAg;GW$VI~U;>ь#2NQ ̌/D.mp dڑ*Z5/USCK\tluJX-lOؙ_wfolؿVSA7\A %ZXp<!@BkeUbyoCi!\zdYh썅Dޞ 3}S@vң-K+(Z1|Vq z5x|t׿suz3W% n_vΌ s[kԮ7"X8j3.-XܾQ.sa^| tR .߀7=^>J}2Xsthh[28X +y"kԇ=sr1$0hc]!M-lmdAqB[2h=Dy픫0GLko mȻ"*Pu 4{ f4׋k;1X?w}ojlwyeqqcJh9>6wl;BkFka /`HLdkI"%mDnP tϢҋbƦVBBʭĵ\~ R&}`\5|ý{ZOCf9b<:u0:CD473چZ!؉nB!DFѷ Ҟ2 >7U緀WH!JɄYxqc$ǛV>>h1>=z}[;һڼVa\+(LvkUTA pk# l/^&(򌬢QeYXat]\2>y3DnBF>"pj:Uw)6L7!`S9nHTj:4SʚiO+Vp/a\0W `ꚩhiHi缪N*@V``1z-lwxe]҈E9RhZ;-LscUkz( 3'`:%?2&CDUu= dQdn~ZJdۜ7ᇌ hWRKihm;:LK G`ub~e8~2|=Y4 kr׬Y}ro,3TpDfA,nYugMM l֙ĐQ6S/2rIZC8-6ڰ}xU?;P;@=eMYGN&Eds׍~'/֠P;KhR%",BiߙUɪU-˗K 7U9ϣi&l.՜-Ih$6oT{QOu#&9=ڕ!鍤r%Lm6,׏2`PuENNL= CIP4;wk)V]HkP†5Xp쮢==mȭg$~_X5GܔD[vY?ʽ}l~sY{*t`H`_& $mY!X[@8iCV}u-|N>8Ca> 4F&ݺ[r!?ˊI"tSdSyvKrH$b^}hɂKT }l8ef` ^r-w#dNMaQwew;&Z/OJ 3p`c ``?iV>XnUjcܑn%@]5T.mL W 8*N/@]0P}6s1Q>Ul-.E4{P#]KTBݲتxpJalIO^RZA:+sАgloI\gaHPNj!Oۏ; ў(2ejKGl|\]'cSk\wlzG`^ߐ{df}g%S&PfδGZߑz XڶM F{*g J :jG}Z12A\f@0tRs,{Sbs<\ˆٷ`y;B(Kn?aծ5'3O]{58(OyGhȉ ize:W\.`-`R5<:"T&v5+'j/RN?XSaL0 jw~928%h-'~Z6πqjI܃ܴ:~3\wnxװ WMvg\11oRuᣓCnJ!ꨜ ]0)3KwlR‹neGDH_} s`n -;i.;Gs8ưzwo{Ǵp/zFAC%+DIҗ<Zr l. .z_2 YJjJ;6o=>M$B|&&#} kcS앖ޮY "2k'kV7-uV#[Ȝ}}s>3w%6[G,朷֓I+5)[v(ָ%\ IDAT=m}A&wPl╌>J{TZ{CYa$sgE$3ᗛ.E$P*̢APb.K`BC7DeN FqJ>ms<؎e;+qs t;<ϑ)=Ԡe}p2Ts?pU :.ľVގJP9K%u۲9nm[]˼97ke5N3YV)l1vu7H19x}pJ^ f5;Rz74f%|T^mߣi aѻzsj=H:j9]s֏]'rWZ+%# J,ƆD|Ex?@'>K&"2jlGđ(*#S5fZ N(ْRpiT8ZU6¾_И̩٢^r"\Z{Ki-qK2_lRGCI='nd@bRzof\S+sv:=12 =}|T~ָ,Ӣwc䑳k$/::f˲ڃe5N,4!Z>\ϣR/L2ekRN=<#Ѽg5MwA]Xu>[;g,U DK(QJ[n]C%4pqh~8Rա35B ZԆ)N^ROLeפ9տv.zwh:EGQm$Ҷ/F 47npkw%U>WhJ=R s5|}F/k> #9֞cc &]ɒ&T (ٳFwgi@ppe4ڎWВ=ۑ } p$l$,;QAN.AC~T_$_0e&44>a]| $T^dOE xŸˮz{#BozD_D`uYrH m41JҋrJ/q1![RY2S=,Cg!P3& 'Z`:,eS8,"duoLޟ齝oEWY 5gv&h02Z-sڄ9A/{>@̂ |BOC1e :tD]="P~f"*#)diUw£Đխ+N5"Ѳ6f(9=$my8|4%?9H K"Y 2"r_С5j9c{o&aGMg $ !KovP7G} hZ.mPR( 7gߍw4iEްO~"fM2u}3'V#gԣխ7T-(w?ީ]8m|V~8~>7ܲa4 CW QF?4Ѩ`>y ,UnϩFoTd_߂i0.g5l0 UL4 (\.bϨi.Mq󪷉}s{Tu&$pXg[(i㪌Ɔo? 9QU ql`ӐR=p# D(*f XyC[sOxT7ԭ=[3ofGXҒ@k{qL\R2BuC=o*ǞqӺ!%{zN?cbB,jDJk.TGDdI1}(ۮ8Q'[PNUsbrqPAٌv**RMa 甧2w`U$剞MK JrE$F GȀG19tjuTr?s \mmZ CʿXRc4ڳ8z|M^d u(4|(^\YGvT|BN .',]VXQҝpHKq%/nk#[*("0|*%t\YHV[Tָ7g1.Fj;SM1 ۚqw\Hż UgR]e5D<@~ņhۉGi#Wv)}`]߈Dgʃ4ե_E,IOش.8aIYKELkt(Hr"Iu=r<9 l91c>cySTZ-,f׵:m}G׮ܩjAN[:s: f<m`ad5=*n5/UZ[puR^^,$Ju}T#,B& at~e\[e"B{hW,0-K8SUjIYCig$l$tK<2 .6U@ H{ Hv̡Zhj'H =loHzvQ;% t9J4#_l;]uOCUܭ僐_7,Zu9A{ƣ,*aoھ GƠx-w3!gzWU罉.NV:x]׵P(7?OF]cAs7ZՎ|J ]E?iݿR^"n} 歴0גһH%nI;`|*#XOmU"F_(J+ YW:Ï- *sУ([ԓU S[r\]3ejz\I)浺JaM-PgW#Jsfs:0#ǧ/b\!W"ӽw?#GIngvh69}?[ nb0 O*O;nTAG6|I5R%~QI:(0O.Kǒ$m9=C2H;JpGRlbXa냰-Y(2w9 vC禪C/QJ{АEB ңM IK{qP6a$OI3-wvkbu<=Gyԙ\GrMeّ|R:T( 6nbedp*gH@$9_J~1Um2|YuHJ[>i_[SŁg2?1A)+!Cݹ {eJ6 UVUw7r7DZ7 1eU&˛(-{U_#r闙WP;KHDm}bvnbW> XlEX#;ąhݞ+- Zm冿?:k\ь㲯7[iΌD֡R%Ĵwya==Ȟ$w>\0>gt+wp7b}䅒>NJ}UoƵ[rM[km$ZrʝFXI8%H[{VP1ȑ"Pյ/$~VXNlj}ӼTM4Γ6aF=sjnVU1u=W-{rmi:G |PK}$^! 3:Un `JV4aƔYіbU|b-5 4 Eɽ YP`͇@wVr nMȝYzb`Bx8'dZA}n F1`h:cW;|f~?6Û%~ԫ-V{{ĒهwGTczLRwYwޱPʭZhLT6g>^i%Oڃ@Y.*׆4*jN^%;wض%|]n͛4Ns&3١B W&o5w>Tު%#a9D]雥Pavj^&?'?cnsG^Dcl%3ZغZqPr-wlAB$zmX:Co<&>巽1z#kQɝ\> ܪԃ{3rw ͽ,y^LWb9gU㣑&͎y!nZ90 Ue*D6&c9sq2 Gɗ~l PsgZk(ǐ֠./_ ';z5P^l= JĘvCUȵǜ5Dzjz y)U`u{ePا@1Z+"bڋÆ#b+C蕬:+QX;ڴG.hxfzXpGsAHYgx¿i:lo.9*`]g;^$j:-8F+[0F/,ޱ{mIҸEniTQ+!O `>yj$ U}[!0o (iRnh:޺0޸ӎ=vu\uw1.~VEoAxo9:,{3ećZ6P2ޗ#ƷF{7GΣ0iw%$='˲[}GOOj1M<_eg˘[|rNZVμf{ڷ9l;[ V=kolw֌ƶ&<5q0&"AZv$=K9WII:\*EI>(YK&M⸞GE)n)TsDCH̓BفX6`!qNsfy\ zq7s!ӽzfoDo7Z&B(=YCaN~wR4jܳWnYC2-B̐< W)~&+ Hn< i.Z ōgwj%Faf46OwFOI <~|+ȶ[z8iYL\ 5öuwzB˺m2N%^ue0Jmߝ-ru.k]~' zׅwn]3WwC Ow͇2c_7)/5+7VvYѿẇ>ssm-Zkt-BcYֿn܎. |·is2~ ⟆7wov `= hHᇅT»axo8C?߽n0s_(yɞ(ʱL)je_C_B +N ˯=<[]сCF8zlfpA8n81|{Xh~fOOOm@ _~yzpxFGVtd wg-0Y@Lq! w]A6ƂZ8R(,NԨ6]/pXfYxFIG̒sy_i~wOvV6롴7EMZƇ=_;1R ۀ wu,{҃Էo"}$C:gu[sGO`uw~ J{Ex Ǟ+Tjj\KBv4Wch|+6&>w}8͚y{uO~^X[ ?w>z 4K{|bYB @_a&,b?>>{2 .Yt;ub#9~+g'['q{K .km1K :YeHZ:#3VWe`^xڛ{qj(ټN1~Cfsyy̯;?o Wu8H z[sǡM|wo\r`) # 1%o͹ŝ'|'|̸Qӷ^[ IDAT1[5\ /7ЛTnhtQz07v;/&Z{@,o:;_ߞP^tGqoTyN]0%j;_q$|8h8w@ΘY4A4:Njxbtb{٦-oMQgwz7/oƽB IM7g/ z5?=>~b 4I Nއ8wiϟ^׺/ӝL$3kv{pd[vu{c߯/_G<?꽠un|@P9VeǿZFq$(oi_(fwRo-rpchU1ɮc$ޖ$vtEt;d?84[Y_̇)G} dLbҭzpo~2'7fD=0vZtbL+˵wk{-_J=?߾?$ \tOaŎg"QD=u7(ك,p}T>^Ax}/ݮ$$p:[w0?ڤ#O_ w3f;,ᆴVm3Pw-f$%ؚ >93/ fL/wzs pA8z]*a8?Z'A<-s-`»>^.30^ГgZ j"=^_DN)y[q8oie@`뀩M.dyjZܓ(d-B[Nm6No/B=.&W)oގP[8 ;9V78#1AMMG#nOW"vwυA_G?v(U~w*s!PgY"XϫIN}?sզ=oᶙȶ++ŨyF?ϩҲ9^6htX"wL5ccܢ:"2K n*:zGgmc-j}e:o6|zRBiPQbT_ s2˺װZ9Hu̐>ݎ ׶Zu5kX{덕`K;c6zx?RUq`lW^[)ӿAE"\YC2WVwF Mjnvpb{I(ug/3@uޝΥq:0GoqR4rR1.=uLϪku4[5Q1 3'NDzUUcNi[ k$Tr'kL.;ΙӶs42Q̈s+ )h+2G.WM(Ir4fJڍ< :!\{|d @g;z؞9,SA0b׭ܙ=囔gFL3g5%:U Wc&~3ޛչ>ܤ$1( QH2'\Y<5gxs8S0< q{+pnLo "8Τn=.s@BӔ8e=&Oi}Phf ]˙j)ٕ}!~@Bw˯8y_$x2QYUW&ˈ1=1Hߝ'B\9zdpe#iw.,ND1ȁ| %F.LfgØt!%@L2sLc/COgZDH Ga$"Dq b51A~&gw1Fȹ.XƛT?~Tdl1e͜wi&2ʺu)XM=SafoC {iۥ^lM<x懅"bWJZmeZ\Z+ Qn,q'f+d>}c:c*O0y%ͳh?ҹas>#0Q?+y£Då`xuq{e *{;!uG9p#t\Ǯ#@$+D9HL% Q|$Dx9] $\??3 ?Ced BKdλsLrIID$Ra8!2#{tDw5ty!GLBJƹ@>Ӆ{y9t:|3a tPСd8gQ p:9Y9pS1F0̍1N0x x8D`D~@a&xO&e!͏Kt'a8qF$/!?o1#t'$g TZG0B!mVz͇KPʝdLӌ^ !'d10B9$Qa>6*#h8'%IRW0 B|/}Pcpr8mB<<fS,i"4͘gA;DB  H"2gy`c.BdPa"0`'L ahA)  " 0ωYU,(ƴ/2Ji 9eyy13'aD03b 2wsHRs0c8 BHM1M %! g4!v99 ^BHya2#?`F"NÀ!gbA \Ӄ8"Dtf<ψi I'ϊ  ya6%e)7g?,; lxja9ᎄ4s@C0 x{ٳT$`xin#$t6İrI8Rb kbN !(3+lJ s#b"ӌ9 4aN!e!Xc < :(>fN[0 ì0x;81{\/Xa8BDA%I;a^x<0].YPVC8¥ '[P 9b'N~8itIKd88!@qD'ﲐ6GO9gqIrC}'8"7\^.7ґCG%d &Z2O9L$ڽ8 gL抒qG/0?'0:& p~p"Mπsp$#0c+b"u9œ$0 qq>ZW$dU!4 lH{&Ng(7/g_$ap##Nkcp~'LIJSp~8 9?`3O1C8N`&<><` cgx?9?ǧ')9i 4C|Dfff! |:!,es|bb3!d)aP_<\;rg?x\]?r 9à Aʔ4xOӄy0'<:^*G3y@ ǧG\1DyPAfD<|=30)Ecy`?!3ӈ1IM89OL8G_O14ci rI ~yzJk`B4ß~E "DYS4'88AL!$I1NhB^^DU=.1D2O(ta#,LO'u0% ݻ ?cJBe~8Ì0G1`|ys&/#"""/#DY#'b3BFfLӜMpƜ#rNWSf^^^ #! ދ//%s: 7#l<@8S2&ꓽy3a9`r3#@,W_=̳?N!00_/Àt'Q^.<*c4JQZGYN`ٵ zichv{{ qVgQ(Fae;5B !aNJ$NYŷ1[ SPτ6@f)ŋ\E hZ:/fjE1GwT+.X4 o4c6VA j5I`!ƶFwwA-ڎ64묠Fcc2A`o;|>ֻ,@h&6FƜ3wmRiC;GI lkfo/=1ry!KT2fyKm ۩M?PJ#l}LBrSZc,qa4I)-lc@W)tq&Z(Q T#Mҧ:"oY Y#A}'T1!QTJF=ߤՆjK-9;ysf!(A[ӆ'Xج4vu6zxg =,#裚g,6ϲ>e')Fl!(@to9SS&]~*;Z FJ^2aug0m3hp/8mBF)+ж1(/Zu]ܷ IQF}@{#g,_YOpB=jK䙩9,m[EL/o8=5B-eA[Ce쫰M.={hҙL2(%1457"25& dZmqCAɅ8fbkgЯ岊Vm(%tm \Zzep6jDs^`t<90,PSG'Į&j,>0zD+5K|9xBXB\NB `Z~6X4.ʵ|tƢN[»1'.񕶴.R5ٜt)tZ 7k*:\ˎVg:٢_:Aք0:MojVھDksLq>16sA!Dr!eZ~;~݉qd&)k,Ip0@c P%_E\i#yRz(#.(Йn3L(ʲF7 IDATGe֠fvQjFo}QS ׼&6JJ'/nzHWTSfrN`HqއF`t,6R {UI%%I)S<^R.@}vA>h(k_7ZKК2|N;/> tҚ SIq|n B"Set o8OĘRo鰊mh) \:)gBje8yֽuAj@ 녗_ך' ]%NІyfhkMa5tXt~G;YfPEkƜVQr6D?]Leb҇iKkY\WhAApzX ?o/qCg18V3>h/ȫYQ Jժ+A=t{${a<(Q[erTHQ6w`7_"x<2]MsdԶ"NJ5,:AǍ.V)r#-38QKF+ o?Y)_x߯3;yC o?ltf3v}F*iY|Vۏ,LŕZ7wcPjO>:V)ƐKE[G: Ke6'XRNʝ>e~;RU1/7GP %\7|SJe4"49Nqw)V=6jktd#g;(^J˶ab-s[ց-:jbQ`:Qw3(eE2˵\UtVL-$}QxYc8Cܢ(щ޿짵 gC1z~J A8i7rxew\9^j{c Қ!>&EUqkLct鬜s!N s|ZDtF}_.H'5'ەD/ Zަh+@CޖF]R!0Z}u%o=L(q3'ϗkt*Ƚ>?хlA$Tb1Qz#lfuHI>'hiZXR`ښcb HąSm:&0)m9Ʋ)Tة_ƩCkro ZF!>sDk-( meQs8ض Uf AiLH_1SaDD%tSM~4|bhM4|A4f rwЦ% kws^rizda)_oh+mY?& hE[џhjrWj}n j'x'Ze:)5K-!f")Bcc0ƓK`jb!xyFfqh}FX먵Ӯb|&&BG['?BW*Ec4V1i4Y5wLpAq3^*{&)àPc (w>G45Psxܭoo|[6I-}^$POB@)SXk\EX'JqVPww9ty*5ᯬsv4,mK1V*ڪEapZxpS|׵z k)\EͣWQM2w3uv]4YF[z/X~]6uq> ԄRN{XiPn%̉Vجc hzqD]ZAҘ %:ECWg?=*ntL9f5> Ui]lf4%|uRXa9la0bEKE+jj+7ʙlQNhJ.:C'ms\FYa`:MK\lV ((A-pOj'"nPFT~LpF~ZbEp;y$К\sG#XiRP1`mVzKߺ[ji m8K#MٮaOQcj1 ]T "3'դBg 8{&flj(KemT$)Qt#̘ ,6:-h.5Iq*.ZZ\ֈ^[DAo#}NVQV 'o?Qk%XO8^qbc u͐_N ̡nb&QK0Zbv*B"7@/;j )d^vq Qs hWT3Jbl=IFմQQ2B;q ʋl꠴FD B0Q>wA×uρ R1οZoR{@7mc`4`½EgRJ83o4snQ1;Ҕn ]P Lg?W (?ݷw?p r$?lE6'..JZKp|޹H)=qvW75ږonzesn;gB0i־~$WzQrɩRs2g(͙2ni(Fm8E/{Z[':'"*x{5"̵kXJne8 ԪY>ok)dޚ}Y%cI)K|6:GUFùTRtiF,\_)-0nQk9PkAzuѽ%,<1V/!upsΉS8+]m.O kZ+7X ᄉ">xgAaco;OR*(g(tf ÙjZU(g],^N U4 >5{@Q'bpD%A)s)>8[P;' z >Ն@> ?3Su#Yj.lF֜Vy0jq1-'Tz-hS١Fp6Atk'u ʼ\A@'NrίZc"zѤL*jmP@9/T4neLZޡ8YOq-Co!ԢsqRN2̽+yJhDZƬ\Zxk3H쥖>N͂Šur=m͙ѽSFb5SCUXM.muVS[A>5ƊSХ$s!.9D9Ԣ{.Q8Is)KkK.Ko"6Z P<UVbnfe]4+LM c=U3 `| AJ)cEW>RBO F(љL`B Z58-%IW#Hpz $у">^NXORL6i6?s3aj8$zGkO)~gD[Ck⾑Kx}k=vľ`$tȚ@^:yTrIgqcXLJLq]r#̕,w<8#<֊w cy\%%C}KF__ a[n>^p>.r,l.8Igt9s}Җ0/h(ݽ$<Ǔg)9t\_Ok5NBY_!XXRTăo%5q0BK(q"Bt>mZ{+J =44~Eɥqf.:%gHM!l*|QnuI7iQX>7i'Ĝl܏Qꎷ z%썳R耋tL6Fa,QSq(/ $EPR 8dPƀ^$+%N8JKfQgg-hv,\LDqd.n#U"-?c4ǑleU<}6B҇8.DD%K/I>Dzu ga!Ό)XIF)1Wcc-Siq8/hP0d;$mpu y' CXIAXYH9e^̜g_{_ABp&URXJ;[}Pt N$$3q֮\e'R_ɺ 55 E*'䠙mrd o l|KwMn[b[A&LmU+ui}#5q0Sx<u^BRC-)K3~zٹCҥ gcdCn$keDB 46g۰P.&3mV/jK&tV{VmHxi}ʃyRJ N/kMkh$VRjƲIJ}zƘ`şOA&?[??ƿ k 1]9/+tqs*hh}׷+/˘+YU߈10frGҚ\ ׫/ 6QjEBLD:9C$.qy~pFvq'\P}dwQ1_v"D=!D5("1p^zqBZYKK3qHa$˭uJc}n5Z,:瑘jRFoM(O{/kvbe%ƬNIZ?D/2wrNg4N$^Qr&_kw84\3}vkPly\!FN9œRs=V'g!$m9e&LmKLi?xǾ_^]!xlB/jcl\q- kb͕1`vqRJ`Vi<{E6eއV(Dۛhmr:)n}׿́B }Кcc  ɼi'$pFqh9ox>Jdc[tAhrXdn48gygɗmsſ@8S9 *d RN>rZQ \vv-ndH_o(~yuPRa @L?SKm4o$zkBt?$1!?>FQHbQtkcg|22Şcl9^9܏_kG_$ԣ$}w8^2SIR g%Z)sLqX#yRIo ]$9_+ISG#:KS1>Z1A,N|꣣XTbj)-pHFR2شJ~fD$QkxryVA[*J=+J#4ƑNzai?xpw| o7.ۆ<%5r^v"s Ჱ_RVDm?Ězǃ)_].A^D[ϛw׌듑 FZv;KZ@$2L&׷/(@OIAn1ytx@ɉ?7=d(Ujd9j^(g*Დvᑞhr$@Gt 2MґP+I$V;DHW_dfDm(51E_["NmJc2{p=61jy J;7Z2"J#"c ?8FkPrNMTfgl_B0^GcTP IDAT XP0Ėf;cJC@͉൧gv[7l@![V6&E4 {Z;q>]Xg"Ԫ5tRS|6]]Dpi[ć4m`5O _Hv;'ZC쮊N̢#Z9$l4 SF/7BjH5F=46%L ,K 8-ؚ`}db*=:ʿAg^Huz$ZJZpxkck8 b_2+ۃdTJab|2|owjU'l[Xݚ7wbW q8$Kݢ78Rcj;'j/tH9&9'-2d;oW1t,EZP% +9NV,9 SFkY {qJ Z^S׌~.ЩV\.;;.oo\ $(ǐj RPbKkNYFzm$jq ^^^9Z:ZP؅Y`vHgC)?0BN>ӢAĶw'5I%*Ғ?-XY MɶE`]4X[P$eR-#TgɹJXuyͣ0*(2?\sJ'>C!@ZpJb7ux+YE E\)XK܉—){jylˮz}mUjسʥ1cwHωV޻˅+7 [ؙ̈e*S&xM<鵒k&8\sb%FmEb@ )ᢵ}/n.72-ZaIq"Hq>;f#IlF2h\20j%jca5@l5]b阬&I4jk > $-e[s.8d2Z4yb(_w?@8nY>?(cx!^W_q <%8&ƜMfhHZyrqB|rLHFk~W~7hehOQ8Be,}5 =d*]C1~.6%-5.S$j6yHHSJs9␋ R"xTRBcBǴ_ag4` =o| >x>k[7kQ ^O6zRQ1#%HKǙ Z@"%Wd\)wV<:y.&VeSC/}"gfʔNj԰Z*mYd;o2!5x✙~ygHecx<ZH%rʉV"!Fb`,e :ritzq&8}k3a,D;[uA6wXQ?~™3!}H2c'_csIYV9w*FJCOêhmf xfk xrgL/~l:dRsffPlWBJZ< ɹ.[tf9'T,L-c^y N2[FG)J-R ߾c!ڔ JKY*p>}w 8Xҙ^ ߉%ejDs}8GlKXsڛ)qtE3D+a|.JZk_졲0Zٶŧ]Ř5F(CV&5{1ڭdWgT^ D )ֶ^&d(*̾ ^qYtZIfȘa.%ιR9&#P1j~YC(a0&ђ!F˚&"'dDmZwT o ݻ"2Y ޢդDmwT9db @d'قpN.B #D!/uI| K:ܓՆ[j(Rc4['߬U 3$8XygAw6{2xtjǃV Ӂo?^D띥V)Ԃjwp`%X8蜼+ZJlo7Q}_ [_׻ d b9$iЇr8_|;q2&l'HIebZ }Pjɾ(1l"B@(ƶ\.2BI\.||p^]Jt-"An<΃li4XBw@.w{vz|ɤS[aRZ !Jtg^"J$3%qhZޥ42Z*byj֊cDPQvJv>,Zi x%]Ĺ*¬5?cHPvǍ8IYhqc,D$,-sןG$ &4ʲȹ(#ۥhCI!S-P۵7 Aj[2h8C )΄ }Ph)$Pb4.S8-cAf [ι9/9 *ݖ(Yj2H{W[u?i{cε>{D X h@9cbɒeɩRH)B0XS DQ"o'g_֚kqyQwef9Z{y6MKgVW}mp:5YQÐ3 Ste4eo?, ?{yChֲ\V :쏃rԦ5B+l۝y8;&2_^xSs<1y?'E?(x#}焅Pb̉Qx7}"Ō˫Yhބp?R3=AB+dkQ&3/W|. -53ᔧ׈z5N.ejm}&ouMp/0 qr1y.WA*I4&n_޸+rNKo,-طC!!v&?c,žR\Ot*⑙h?#[u`RGV3zPbAcNo~Hk-2w4;^[J̓CU7>?wf1հQcj2{n2:vphѬrYV;/w MXZ::S*#&eLo?pfx(øU> u4DJXrJZ6!"uj. k\L:M\@FY K59,_=325+r*  .c4Ik,]CʍZVOeAbM UAۋL[7lm<L^}ܴ]{ibFュu]Jkt$P~9R~rr88#KMsF 숄|er渋w GQc*,^ޱl$Tǃ0v!xŴL%q;5 -xژ#.RߙcByxp%Rj;4Tg;!9*:qBi#JhLj3/cuE*h+..P@ϟ?aYfyfwʐ-an̼yo^Wj"Q"mV vűVl%%Zҏ ]=KD镆 Y͍\̀*eEud&r%)K38Op'~FPU Xq;R>jLWJW)HEn-JK.WyRju"y.&Q kEM{Y'g늰,.1Y<-ֳҗ$#9\wۼb(s;s?IXiU~bG)M."5z5frP`?")w 2&-W[ <cdy)*Rt4@kpCFFZeGg&*WIy{g\ ooX]ng˞"O( WmXsLR,.h9E.i 8g\ .3^gY~gc'C_ @2]1[E&&I_;Z07`ru,/9L"?R g~bu@5Xofze}evLtXrLfQ9ADlJ X#[sDTq{'d/Un`L( $xւk7 *3rímvMi0ק'q,h%)ȺNPL6(mPrcȩT H.6V9ɅTxK#YsI hԇG㱉}Y2B=6bLar-\/+w.+綡P;":?&u K<}'vzB5 @N<#X 7bL8:Ceg:Sp^-;vΓw|#_^m0qw2W+8]V .c#o{y n߶o;v_7YEn9rY' 4E-bZJIxXm{|rLˌ`ee^f$™$P)K ۿƬ8QzWе|Z޽ CΓ"a^7yi~mQCONLIkSfyhs>bp!~pY.LΣT'J32PL>/?TG[큒LrY&"V%Sz{ƙ6.ERES]TfR"́8Vґ%hbf:`Ya%' %,81A=۝Jm4j, ! }-ARJ%)%uPk [tǠ4We}q[~'sNZˈSqL/%grqTW0@Պ8y9ws襓Cvpv-\|~{zp\CliӏXyza"])ᗈ2Z,Ύ&ΉVu#%3Ӛr*4O#l P/_ް֢ꥣeZViwKĻ[hcc{lAWFrHe!!S2ƈw*V;j1%\k}Ȉi8ma 9x1PHRG"LR3"D*JF֨4z/x^&)HXy4b32h'W9(e"ws<w˂1KӔ9\U=>Oob(2Eֱh%=OT0ȉƊSxh{ vTD1 F v]{w~I͎irτo~/om$ ָ^VbOihm7?g]gL\֕xXPbX9S'>aZ)I.x&LWAiI866QºL::}{%Do2ђ/8R12V 3&/t=Y* ţ*TzIQuؚBIJ!~ބzCjylf,hvx8g=xA9!S*SJ>Oeq]x[ŗDUI%Bh'hD1!%F^BÅ&r|{ m>63E~_@%n0qRcH9><~ӄ21}߄ا% 5Ig_WlV{UuE}ܹ^WZgpvDjH IDATgtqbf jJ+X$'Ҷ98MzJ; }g]ܒ~dpNvNrkJɍ3NO+OۆmZJ'*OO; ?q?4e=1a?>Wyf(+^a^L"-I)}|ycm` ˴J<~_Çgr>XdKc}z|PZyr>au),8ɹ0+1%^Ei;Rݗ -J3{ya:' >ZIZ#ڪ흮5VNKBhkQV2>Mò8…gf/8Lc'6KV iY%8#^})IMch_ySi,y$㵘2hyд`Q&m+Uo;X=22i2.t uN$T< (FWlX:#:nm]gǃز&E1]ו+ooc*Fiz-LQt։(HKNVZjBL%Rj)OaKz2OX)ńWwrR$BZ!'ZFo[l']1c⦆Օ[ nR ȿs =8㉶&ÁaA)U)HP)r'Z?;|phTu)`w-iFZguh,o䯜S陒~C@Gh]GT)9%A PralWiB×//aڎHW= >A2gdj:az8 Ie?RR-TE(7[b7C'BJQ%-DBKQ,Fw.^셨&[+/8aaSk4DL'ɚm.؀ T@nOM_',^`F>PZ,?oy?DbYˉ0C~i ? n:OccͨpjeK[X[CA [>R A+~y[~|<wc)rzmϓH]|ix ԺY'SǾ YA<4 P|Rrd0Γ#\yOׅfZ10OL[< S2:!,kMaD $\)I+L3`p>61ґZ%:qwTD!L<ݞe&Ǔe0 FJ'@kE5 @bY9"j7x81v3CHoZ< }9}۱NnrYAh]!De\bn5o,+~,mRfG?F 4><&XܯtK-v;m47eYY'nƙ"YJeOǙe/X7LA:oi$yp,LGJe>R6Z0RGwt2$>gve4ME͵l!1֡GvBc !1eTAyݶ]~&@o 5w&mdu2kjc Fmi &m0OlIKϕ%,(嘗x JP׾o)Qj>zi1A5,pvRŇs.[V1K_|!x14T,JGལtYF)y`0@RD(lo-Vi2 @~9X$i7Q쪴|z%}*% cQp{z_8)uVkI)T7'ife*f&y1v$3J@o`2koj4v Ƶ.mYڸ.WYٱj o*&N#&R^~RdBhdY {VX-߯\Ũk̙L(70Az6yy}5B9ҔA[C kEap^%AJ8_svSL!h7`oE|f=rF" c 7m]mr2)v_^i0pY1NÏ(|`Z:'&ЈQ2L'Py{}#ȇ'Z)P2)ʻH$$)xk젂϶@T,cq^_xpl.!BXM|Ұ&\pm Bӊ:ccX*~ю`-ea,#O~ lw01YT ʙ#3o㋺'mnY<n늳] &$'+6=kq8AZU&VѬ8 Gd;v۝rKrLˊq_ZbRt6Hr:@1ʌ[X3xQjǃ:Vk&+-g%x;etpJ6E֒Im 99!y!$]1Q^)p zEV9rIxPR&< Ukintu`~iM„svVVjnjBk29NYCnkHSvW;zH:G1>b<6z/0$7B?wii৙}8 3e VJp ݯkye^Mi႕1^Iەj`R9^ZyGK5%&zn3IC3ہ7BƱ=\%oJdgeFǟ>Sh܏#FJx^^~/{o2jȵH T~_ g]j6BNCD^qR+T8F5a԰!N4^;;~ b(3l;Z+63Qp]V>< /8"ooPsUIg\/֤ & ˲2+ p2ɵ"CSCIQT({o+c l./nk ueQ*gdt9uhQk+T:*RD֊ഛ Jo"`rHOYFΉʲNh%+$܀xJSUCTJJno3PgH7o}&&yt20LRghT3('ӏ$+RCYڈ[EipBU T[Aǟ?ߞǎr9wr|uv]=-xgPV0B>OdHD j7?~&C/~ίW7}w2\O>~5oxGzKǙQg?Э2& x1I;LW3eS旿NS{Ee>svH(qGyteʰĚȽsBCBYV+,o5g.4"x堠&TjE,iƁnef}3FNdT+ܙT~K#XK-gyBiLEg,*7 g,W1>OۗW ?)j9F9dTIŶ"!=EdqYZ3;J(b1_A=3۝b/\Gj<މoG}ദM2~A[pW_ ;|23M@K Z]HljU|ԖHY$VgYE IDAT)1QW$R<zZ)5q&[V(NC41Nl,<džo-)ol DKNDJ!1^YAxfsԔ9mj"(h||poj7v0 ό[T*1a 1Y5B/2N7xLmt QLnŹ1S^+ގ+A ,Kf x^ 1}-y9KD mEa03ۖP.]eEp=k,~ oEK { əe9PEnN)<+Vb1lM.e13|c;O+cEF: 7iaSW (n vq yY-HZׅ_cx.$~2YZmJd5HXfẔ+t.-,3K `RaeZ|8D+^5lGDm,A\o3o7u(Nc}xҨJKNJҜ)1>KU nQgR7J6,ӆR(ۆ!ӡ!ZZ0+e`N3%9s_Gao}$GXfN5Wa LD ? |'cX#ٖ[KU1_he&̺i5ȴe=>޹e7*V} r}8htaӞάmmc&ʤ^n$En1*}gWx)023D*^`HuO;(̃&2Ie[5\gR,ۊ(hS(#%% 8S7X+/igjN݅Ǖ>p<2F 6`*q$Lu ]۰l]Ƒ#wְmR/}E3uf)'}+iu8 2+þ)5B_i_N7RV{",3^[RVB*ʞ :2][eTB•ր.yOY3O V;7lRyg:yOsvN9S5c':m-#jF]DBo&*ZW OHͫhUF#] L`'HVx|c`1c6N-vi(FyXTAm4. qs3Bީ;%rf&·3jZP@eMmOHZ| ; ij_4NBq 4p8+ emexd (Zɪ :t)pRqrŨ.oQ>RVK2%Tr6hzCnԕ{4

hk 1f,{a/\g*SKxqI$Lê5ӲaJfg~I:%'\EvqNIUN) aGaPdKA+é;n~Ń3oQ?ߞϽo"o_N%+ON ~O/TZ3]oV~"i}t&$橘نC;'|y!,@ XXO.HUi9zќe^7B̬{D)Vg 8 ^~\%tR E0JgM7{,OJT{x,t뷯_s9?32B6~#{La1jAKLYu+yISAޕLR7.u-7xg^u%n3Kh'%\{!m BW7]o E)m4MZé=^e˅10s>h*3___Y&rfϙ- oogdKMK+R4Jki$gQr.j/3Ӻp5mbP]B=ahўi^P|ȪZ\NgQ-|M9ӷ=AIhc AWT,</tؾ<|>PH״TO?$I@<̈́MC˱o98'iڢYP=!4u%Yr2%$y&#u^QqZ'~EΑ@pض=C.$TНǻc'aۄ!m+ C*~?8NMK,aAۂ : E󅏷WPJፕ$rEGoe'{HN*n7`w4uͺ]oj>(|Ǯ;K';OuD߶{JΜ'r:s:*_QUh!z41z%Z{f)YQF)Eed{.a>4ap0p^߿,ȴ,`S*קw=O˲9%6Dd#ն- #mEF>) @!=X"a]$Bx#q*s-uݰ.(:s*ՀF+#W 1hˍLtP\0(²A['YTJoqYf!wNwB HjYVad(J[ryFʾ ~ %|W4(=Rh%5=34U<ɥd7o^?2?mC2Z^9۰qT";%@m&iuD"D m[HPBoN,iǕappIUko ˴0JŪr}.-"t&S䁺,yIljLQ::R É=D@Vl4*":KP|h˟pb3x+7nWӜO'1o|wRgF=p]\iv׏ߑYQp8Iej)2UEo;aY2LL з5V [Y  4Ru.9%1ioDUZ" {>D %mk%~cW k*>}]p_qmktU4* .H +e;[|?1$+ 4-lc v׵$|kmê" f\z-Xjێ67R\aJ^z/_+Hk-0γrWqShkNjp*$#}s!~m-qs{Ϥu' 0q;Pb*u5Lk•)&4T(Zp,|pSmX,ya7H)Q929(iӬa%c2CG6 WB.4' WR O'2dyj(/ [p9BvRX‚֎awr6ÊY9*/t2qXQR xDЬh9^%iLii+q[ҎR .j%i(jtoĺJ 4a mts-q_,A$oO`+'&[k i-0hcyNNCҦT8SF!/Auca^UxP#ӁBBuV2,G+8X;}ۊfGc@.|z:{+oդ~ueAĖ00phOMw ?=Eg u;!|4mCuǕ}[`2L%uSyº=7>ޮi ǝ^21|2ɹ/#طy\H)㌦mj %촇L8J.g:^rl!4Zh(8[bMֶ!~ ۶rR[|""eCn4m2.uzFq<Ä"{N)ҷ1,lζba)!wx\*:c!Lr9諊z8L:3 u%xڣ}g&-pzz&&+%7m>ъlӱ7?HB&iD]7 FiXcQqra e|~~&( ?lc;Ǵvx9O1rWrxKb_ lF]5cZF0W-%FHVq\ kʲYm 1kqY;[97P#Vг番1h%LFE_~EMEVl 7>1N ).݉uZt'ǛD8DBKX[13c\E3Mp=x11(MMWጓ*])붕g/ma-'+h jӊ-J[6ܥ.4EXwa!+5;i[JN&N]@*5p,x !8/\N(/ _y#d'5h_oRgE_լBB4J hRfxz୥}ȴ?O$ἡM~% x ъXkN'-b:I0c1[,a'쑰J o:0eKJhAyw_0-{hT/ma u1Ƒc\.G.(2 }qZ1%s'։4陧'*Ss:bĺ/?6B[¯~>_ ftU\PbU qӉ1%,Cװ}14 VĜQ&ar ]Ik/7=vO>sfZ&?ducZwiˑp_䁭5: 5Ʊ:ovD)kel 8⬧rJDe ;[y# g]]SdlgeòGnÌ6 4 B\B>NN;t4ڳwh?7,!0 P8Gh#/MϖPGb9//ur&aRrx;a} xP 6XftJO'⬥k6ݏgr$F5v.z@[G.4L8eKqVE 󺢌 )Ґ5,˄8i)e$C],U(EG45vFAP+m]aaG%v)ENi9 ZjF31&oM~yYA+>J?jނT[5J,ܙ}Miٽgf9)'h(ZK"B{jv5[ ƈ{;-r0b'ڦcO [L[WJGޯB.+^6itfKc%iIzZ>4O9y;mzeYGN,.*gY"cmC IDAT9hG-qLaW>=NkJ|>-/OIpy`]VcݡcCT۲Pbx8̂ϭ*Mej|Th523, o\7xu3o+g]##+F{,%XF~ѫ?6uQ{?43/ #m1bbvp6Op`gy!msF@IfƾG.iZJ@Q/oWWyҊKSYvG;OمlWc nخfQÀ)<_|AwJ-+~þXC=a0Fql 6N̍^jyFeeWτRIU-ymagWbh~(M NuTֲ+ // 9J^ n1*FQF&=JbOĘeN3hHŖiy{ mO~PuOmPH4Dي=*9t=JbJTuCu$_1ϋ5%`9&l]gyb)K_WUmXyNºu-[,(0,- 4kXT24=Ÿ%!*X TN1 8.u2mcF;Og<i0=) vx^OU`]zX *gP=kq^%&0 } L;|mADLG&S!(]ؖH['a HP)r:D6*֓N @*J&V/ ۞hbK U2붱8Bʉ>̚{BD׎T4a/4?C[_O;O8 a;V'm>NK:|%]">w޾*u἗1- 8ݙ߾P5o@"_:N<]MV60BTe-h*_6mtxyjZ*}s~~:@ն8$4(di\5zucgꮥeZ(܇;4 E1k ucp XWn!gp^JDFur8躞ާī&re$@-ފY,kPF˶NM>P;JX9!p<a -?u4ct"9^ԝS۶ǟ׎~#Jr>r{rƷbG~9}\慮*Ƀx-yǞ!#4NTd SX1VVkڃXcږWQi IdQ;G 󲱄h#1TuIV3"5ۙwT,XtGr1J&9ER{~$yput$C5 U兰W t8M,sYM5_Ts5-F|QдuK]YUU3F?>uݠR`XȼFi!IqvL[ +E큒xて2bݴ4m//#_uJ6{ HLŒ(1g%k^oJeYOBq8$D3ZLa}jΗ' 0UyԧKc͏?@=_,ۆk=p_f#ؓ25<֯+)X7]pEᛎqXK=Kk`yӧ'L-пjC LVXRA$$87cqcNke0q֐b",1Vr;(^6g+UDtzطļQ9霣{( g\UK).hCz$S!Q BRA mC`Kx©s/8'T.8#񺩄%Pn*^I _v=Z@!bI1bgiH9]u%; )-:-ӵ˳j1$Ez+A-.g/޿nˑoz;JSmMTqM Fk(Ǹ18> V >.T7T4U&k+^Lu8=lډȬȶ#?-DyE4-˲/gҊ`ֿRNAsӴ-Ӽ0;aF_7,늵mLa]d䌫ucétMp`3Ck{N#pEҚ\vuh,a ģgRlϼW 1pJY pEJM?h噧U$0cWoۃ4c8^4% 7nZTLTP79\>m˲laC:ƝDN+[;H j \誖^q9}xpj9}GL+}wh÷oWM-zrNX+R ##@RPUö\-PZm;LeXg}+U DYVL2R(5(~o)yxy?dյ@;e9OOϠDdriZӉ!Lxﱥ0P }xyzoj cjMp1E̷o4mŲ|{B.е-oh0_gr=Sr֊.:rx> z_Fm[v U9gwDưwotAQ2 Y!ɍ{_1JcNr<@"' 5uq !&RLPc%|!ѩJ;50/Xӌ2e](}%,-*hrq=hWGXNk\(!3Mlaj:VǾ!1fawf i /SK mZ xGR""cbLV\gΤطٗqkGS׿j$e-^+*25|ʘ7ҺR{Ő@R -(H ]y4C1. ,Ӵ."˼z}<:ҥF,s,[ةoSӢ+ 7*tUa!E!cjZi#Nq65鉦iXVu"M>q9^hZ^>Jt:PJO= _Ϳ?CUM|exdhѽWg*uKNk{%*i{s۲6OY91!.J] 6i ( -2̶ph rphg1RN:̸q^}*tH B.}[IahAm-O<Z-x֒W|Ȥx\Ǎ }\7gΗ )6my?=iCs[*Àmj޽{r!"9szx`w8kwwX#ϟ?q~yW4Ew@UY|;31z`DL ??gj-h۾{•G"LXvYE(jM /-F)rʿSBSJU[.b|yH5e:uRDHM0/ 4xjl Lݔt0`8 =L0W|ܩ[ $á5}?Ȣhqdw̽Vy`bc,ˊw W)RB"}nkv_5ۺou bMPʃ#ƈ jmh)!> ~Q/#)$7QIJľu( -Rδ!VJ3-\n$K[YFkYQ 2膞8GHw8\/ou[W<=t¹><>=/7r* a|}rZC8be_gw-H%?ArR2m0mCV -nw%/KFz daX*( A9C{DvtMUDZZʵ[&+\=kpVPMݖ` 8,X@bъǜ@2^)u *\@[K3UYiCbĐ$臞D`d498mbkԪl)5¡J1z$eاm[~_C%F-jbJM@raԣ%͝Jl-y+GqebVRqxzƣTĚJP[M5ciڎ@X@^ !Wm/o=4_= ?GN̻G}b]Vq­+m3Hז@n wUEM1Ņ2?'֭T>o2%o oo| 46gY6ec^Wyfv9mgWEՖMEbbjp"m V{´ijV#[(mI!b*?/|#HeZ.t8m\ePv1ii[b;\2rry6 V0ǷhA}Q Q xyy}c-Ǣmۆ-^XcX CAރo;.No$`6!)n[ Mz6/\궕rٽCHO?ʐrrJ"߳*3~s\7*[KTew{1yOHu,yy+qfMsq^.3ˈo߾i^Ip:\Pw`Rغi8v14UuvEqp3|[6غnbv9A&Ĝ:i]JBd݊y6u+o߾ D!9VH({)wk_( ⽊ش=.smRP11zr̲-]O5%O!!$s<xRr$ϧJ-r'6D`_m3.8u'6w#ąW/wTs :z5R+V}?+0??4b\L0()XvRJU ^%U8$˙J>@8I맙uÇL8[_S7%lM5S1}K)Dj4!et#}[?۶ҙ VP > nov=- Ɩ 06OX)޿{x8\iФXwl‚ˇ;xuEm+/'C+ v/`a^v۔}d%0vʒ}i MYS$ Mq9}SYM e+#-%}[41ڞJ7)p?0nem ;VWn;&8lhebc^gPm_ƑwϏe+uSrHPJHt>CAR c,˲JSGp!i~QD؋m(A1a߳m,NI-q3Q N+k4a L۵Oˮio?#lEU皦v󲕺ce#F9xn>A\!L]3kA&KE$4?CUO_ӝ.j4ƚ+&ײ wmn]Wuˡ;B.BtGtr;(b,ԽiU ȼƍQ<]b DkBQۅ5Ҧa Km QDN Bi{45ֿu*[ʔ6c4h놺mJ(SJ&95)O8"fZfqk[Bvi.ӛz2}>3ub5HVكæH7tPTAIIXt @zuEľy23!q&\+ܮgu& qmb(x;eXx_.;DBkߋfRcԚMM̙Zx_|&n;:3"kK$uUB}mZ;uU>[9PK%blMLB">طV&2 Ԋ4[Ec(%J a;)A)E kmT1kn}2v( H8Ya;q]xȻ: ĠO_6* +^{!wVg vcb[flaf[6yjm[+}af$ oۼ|Z!s;_1b\gsƖVna8" B38G6x<r:](iuSC^.}QJx:c ǟ~$7OUU#,X2)ZahZ >DrN7jSӷ@3+Č4MQ=mFiׯ޳lBth'aY8aH}(mh8$xa IDATS yL?p%EeT][S 5mf͜_<~(UC?8iBS}|x:7\B(n]/m׎yZ~/_ X󞺪R 0Ɛ5!ǧ23ȱ ;²{É+ω=y_5@SȻ^uAY6mGRcFJ\k )r7l[a ^ҦÁ*ki-}sy=}|:ٗLGfbBEHG >RU-*&ZkGbT (*YcJ>j Gma ;2WҐB ! %ݤ1mݸ\LkZ)$e]JV u]6Ss(|~`8p1VA(oMKuQutV+˶|U(s{YTu/BJ@y-f-ARm,(l1牪.q%L=2fMKp;yyml*;9 b()e/RO#ʲ8Z[s:!{GMy.X+.F(Y6BRTٖm(Q͋s8c #q2}Y'ֺMBywŔQb%2!h 12DNer{rxG޿^i-}S1:Tٗx٠5UӰajB(unJݶX$۴ 2TqM6Tei-24簊sS;?V nԕiu݉IQwp8UϏ_95|bVo"a`5|HYcҒJ]gHooiH9q⪿wnk{u~o| "<IBw xNN_}gv^WLU!f]{¾]~oh?k^(}x5o+?|`BD|-Umi߷_-ViV1fZÁ=dE뺀S`,2ƞ#NJ֝zڶ??x2w,ǹPus{ ˍXKJTۄR|>;~.%A)-a'EWZMϼ츰 m2or6_ymE ) q9,}cAyrƨZWnL5ʲ,Ÿ~x0 I_YׅHb5zah~Qki8蚎 (Id*`_yAZV9*%VܖS7'leXW-FȒ z#s DzAUjKVi.Ҝ19N#mߠ*ɶ{šTU d6-9&r*̷WˌcvΓcƨb\צ/aTG2%#CoXkiflEB SzZblNʥ9tC[cmV,Xv}8xwF#sb`p+(a YY\mͶMp1px$+m؉d]~B&.UvP!B`Ѱ:T:MŶ-\ a[˴FU\21drEP=jڦb`7V,t Aa^S D¹b8o[b;Rp^!!>_fe!g[7>pʭ[JGs};>s\#Cu-eؗ)1RRreu >(!qv=/a@2CBvrfYKߵ% Vc69:^gGPRp<JL*58t]GN)%x/iknQ|;~_IADUR!P.jU<>\¾L|XeՌQ@5ORE͝S"ᎌ~vyZxp<1/Z*NHʺxs {Re/// l.B»RmJ\MsV[Ցb1Y`l9F!bD/o̷3VFHΡ@iHuf&SHy]0 i5PiB3N3!yD@3&Wڰo{rWHx.3`g@| cBqG\LHi4"gZvwmz&Hwad]V+6pa;ȫ; 3 3JBL`u рQԶmw(ma|{@a[$D@q~Es^K;¾d'yA[w/Uee.mFk_f۹~¾S;jwD Ov\^_qn'nJn߈1< n͙ynvm6Gtt]^!7.Oeo[YOHY~K}748϶DgE<J:m,{f"c9%PiSߡԫ1 <]nwkgbF?4_o=?hiHyl񲠴f[:)qvSՖ34uKV}KK@</g^_\ 71Z"}OSϬ驥?&et ^,{yE ŷ/_h۾2#}sCbu;NB"gRP4Qc r#ԶBZ)O]axmu5?~@7|"xzƻ'DMǠ-6!`VHusg *hvSZJhmxLN uBPnQ(R\F]o į MOkzK(21Ђp{ǟp4ZI;]ϾM42 ndwcO,2#SX9|x|;JAE| o;$K ~8Ry Vxl&,2ί>bm!-$,[U佀^zH3+BT!B`d}QUMvoI TՐH50UMJ#9;c $*Iy w"4m>smքJ!xhݯi VB1^Fۆj=˹4"$0Z+۲r.c`^Jm'j 6TL! vw)0e I`n;tCB@ƙ$2]z) )x:uAw?eu\$Xu+ih+$ܶeBǧGmZI#Y7w#hf#J5LCSf y mŲ3Rmh{p'B&(! .B[ݶ5 (k9(y"~ h-˪uː'.yi=y<Rl]@RTR&GnLNӕ1  %*wNǡL kQ=BLTXcJ] hՅThc*"'YaF)lU3SnH MWXe0qm!DW)8_r>H/˳_Cե'?bWV6pwnkBʜ/Fh7]F (u4 ,FFW ;gC]˼۹Op<=0}0eu+BQ,Z\:j[&Jd2 B*m_vӈ xGĄi95hM}gB0"fל/o/{cFז6\^)|dOǟ?@0- 1Au;1\316 W}G4L-%mβ:5 i2UUzW¾G?=.3_ڲn$ݡ#F8+ˍ}蚖??-gt|ؼo}1oԕ%ȡ+)6!l:2|B)).$vG1 ZC;{ |w ̾9^FFŅHJ*nyer+) RTl{ܺj5J*>oFӴ,hf^R/[4P2%jP(!#a2q*A1q8O02M0"j3Z}4m  0Ґ"jΗ3y8(mJ2n<۷=* bTXAD jP$⺲c-ۑ{&wOm#1I6_QMӐCֆr+,1J}7-͹aD4xI.Q)Sr,ZIZc94 ؂'+[K6jmEbˍRv\65GݷউQUMFB̉q<5}`sId+=ku^9eXc1sC۲R KW!3Mp8<{pFN+FE"r`7tݒBJKu 2??O:_rFV0UCߟ D}_p<JJ|̉ YuLB,!@8 I$R1V:y4ǁeQ#iXCtzbh6. Kr Μ#5m04˸0.@ !T51AcRC; ;9{з=4ѩ'VVuzOLיK8GSw*']1&TU֝Hfc& i)87(yCݽEx{oz͑m&%אsb6ny>=p<>+LJUݲmvRp ]*{zbbg #+3{9 GN!7|Y"ɳL,VZ5m9~|ǒ㺐DuVÇw{<橚ux8ƴoȠ_~e\{9 GHtOԦC&8?! }{ ˼hJ6뾰%uͧoo rqRVKbeR!kjє(i`]6leI +;Ā˂WB3mc9N'D$* w;vx|8B¾/o b_Aj.Wܶp<1ж-)gƢf=r֕#۶SB; ޗ7hH)5%"Ā4l3xzxr\DZ"EhCt{tXk F00+ֈ,q{a~'PSkH{|#Z?ħϟFVz|>TL)rUoÁ1-#9R|Pm#:'7 Ҏ {kn~Ͳ֌`+. (SD:!P KZΩ?p|d&Á,q"d{R<{6O| 1dDOG=TUEp@)sƯs[:-,0ޮ }GweR ee~[9։ zǷ/_16K.eEXPu-L5ueHI) Oi*S1f^g" 5>XmXuy^n#IhzABCY9f)YC(V BDZhܺC$M,mؼg\ 2D!J{*{FR[bm̼lei-1!d)eNCO˄7 (6B^g` &'UHk,{=Ȝ;M356Ʃia*b,nG7 >iz C6,`bV}K4J6,CԚ %Ae9_ԺdEy]Ych,kmټ6 d>_&I4}eyS ,2n^hj8c2271b-f.]b]amO 0u}?++hu6o% ɏOm@Dqdٗmi8]Ge+_QR6hYt}SSJ297BN4u_fuV/WB,Mom)b* 2buE" yAJ)gQ㵜꟟>kBhTZs<4 -gmBi˷3BbF5/߿SWCq:9)/վ;/5rz>l3>~*zժ-RU-)mښTPVX55=GlK@%:Cx"GR{@i˲LNGV"`mt[)4_|)!+ۍؤHi65ሶ}Z 2%G.ėϿ4UΉ0@vϧߘV[??;4SK҆l TFRKy xY4 OOܦq:ž{  _^_HY{^۲p IDAT5=s5Ws"NL'&(mD4,3hA4q踅C Ar":u:팸rUnD78Rk1}v 4C'K 9E5$YiR&*#J+S9y[(ֱ ӰbtE~|=kKm[[Čϙm@iTKrʢ;&|!g(kf\ص]Vgk~`ueMw^iDže B7\.J}`0Ma; [#˹h++1yNBJd2/oT֖%nju <9 rr<YԺ"Ks׶駑 5\Bqtd-p9"k`z\1'Zn9D I20Mf]g0BT YK yK,Hz-%!0ݳ. ^u%Dt,~wR jW4nC W(qu Cl%/b痿i\E]WL';Z%~;D&Čnj_gΧOOO|??tb?0]gtb $Frj5 !,+þ%nχqyosNlچ*&p?¦۠b^/H)m |׸Mߦ`]FgpV!R*;KD!%#~ߒB+5!ƑyYJ2M#)Gw'g6sry]n` EB@9 Un-m D#gv# .FYWgMvEfEJA~_WW0%8LJG7}IK\eY֞yItuZ#dHU^m [WyaPKNB[5tݎq~就ʱNnvzS~F,$y1 dz<~xo>1'wMKl-rfmx|n]yz{}=0v\Og!q͆qUB("jsJi"V*adZr,0Rq>y}#YSB2O BUU29Tr8=ݎkeB0O*I`]T{V(ya+O/Iˌ_H=8SeHdO=)S=o/h#B{}#cr>q=n4%B7 CѿPTl;F)\dMV$L̇ZC)~aYglw,HBP9G)aX)"%q SZəYTN䰲*h`Ԯ见ޢ8!yBV Tupne]|XPZuـ.`MUS_3(QBQu4-srbZbETRDh,d}5455˕qeysz)2N)t._}@hYP8m i" d 6V[jmq6 R e"C[&'=Z9_[Qi4uUœ!#Q8~G}mzӿ^sZ {vd65Ͼ:vhh*/! x/WІӹfKӋDВ%Ȯqo;ĦP9S 1`\ޯt-+?>`\MXu߰mr{t./yciAʊxzy33I$*yl;>ܡߐFjwb@?.Hex= =ۮ*\mYօk2m,֙"Li<)TY,o7#, //ĵ}Ĝ d.{ӷת;䋛!b4[VBD`qǏR,qrgDP~z~&8L v1qJXcV]AJMU-A*=ul\rᇧ/H!98A̼sRn)FuaknJ;o/oPfRuJTZ)\۲;/wD];%NJҹ- !IL:\"ݡa9_,k4ت2+.3a->{aYfr7ȲrY -bS("օyF*B JUgZ)%QۮSv %B =,cdF 75xUEF[52uSuK͆zӕ_m[k^|a{1$EET<=5 Mw HqCeB#k}!ZIa(PfM1l İR[K~÷+yE*Y(NhjjNGydG[ZX\fZ ZMC:hWEfSoeAi JOL-z[<. Tle>bAK{{⺰kkϸs4,nP4%9I-=q`۶2 iM(.+J9vlWiyx*LvH$9̞Z*[Mare4G,~ e`a{5t R%fm1 1~u(Y7,J]UT^GՍc0O3f ZCewwm٧oY28Etx{/lLJGfE zxxD(] M3q0N!LӧGX*w ~DT1_x(?"AFͮƧo~#2m]3 k q:"S75S_vҚݦqY|,VX ŬhnіwebaRawwOE@cB7i56^irN DnY|(D(93;P#Zi-J1l*S\ tSyE42psLxCQneF$*װ,laϾqor<12 I]SKZ\PǛݶ(֍AgE=(P%c2-\doҕ׊T)D`n*Re޵C<6A!+ۙسJ](eexy{@aG eX8_GvҚZ\8.QV# ~AXfMJVRȘnf]ys(*D~$MU:qBl\sBD28!ea{Wn)1_™ u]S@Jk3)z׵8.}47$Nu(YD&ޣpwGʙӑ~KnZs>_pM.Gcw5%trIZr6s8 _Co~J*v4!9dׯj>>~MӀs )s8@tecvc "+(q)_H$v|Tsz<]*8)sp_TPTZ3, o3-Z\Ǒfa8s?!#tćvi$V;Kŧzk5brSӉp@2MYuB|>Rl6u =9fš8_J:4{Ȋ"v~Qgڮ_nitQ&2lY"jۖn+ 1OW3. & SˆٳݷTuCFKY8-Oۯ"`5%J4R!nORbZ/$vmKjP/W^NHX3hF!b(u-βva,q,#r TB!/PWvT"1gqiף5 PU$~㸗RyN31xIͼ¥7LHI uX̮+%EfK/84vh"03,%f8#stq<+9 dG"Xi,ZlUҲ6%dHi8tkBA*i\"~= B<<<2OϟYg0A#]~_c25 4s^vҎ}pGLv!(0_Dv/X?3yl7Uq ėԥ|O`_^;vuHv;O'N+4mŦx~zc gÌ݆30Mq', ]SR[[kB\}*ԾG*0ݐS`Ln[*iZG3^O}Ou]LLSY7]@"8^Te(I/ a-!)󒩪$yɬDipa^+{^kJ5M Ot4*Kk\Ƒ+#uawrETyr)~(x}%fzEnW˺0/Њ 4jB˲t:ιII1 3BeN 1B ?Ҷ->% bTJ1N3>eޏG7&B\B=ەm>xz~A(G ֆyM`5R[(q J3Fy&pWU (HHB4fOk !3^O4Q+zrvE_W4PvZs9^s4ۊ>A֙ xd5(XbBq J DL@pVL M4LP Mm /$]_.CA[˸z䐑72T]}nLnK6#3@uS׶$abgL;ib<;izdB"fG)F)TJ"bX&ubY!*Y(}!n;| d<}.+_w-MȞ"Rb,4c$+1uUL>d+Wqˌ3mrGg"Ƈ@wSm+)&3K]b&d\XA/1%$y(#<۶e^1&f4-l,HL'\5q73Var$!EuftGgGkh 4#~8TC*T_ٵ F+*8o~~>}?OOߏHJ2Z2%S@mB1+ݮȎyg9ϸ2We*O<>>p9nxi.kijίf5E6݆iq0pf;~EY@FM*b4Z4.R~<1>zNw*S3~4ux`izdUOg4MMLgp3 q!oi LF"r5( X>"B?\xߣoJo~ߴJ+.+U\0.8홦 JiNgĔX|Jue!f+:cM{*[s9C&hۆKyF2p:]gy~{d^/ ) v*MKik?v-~DW=>}_=<й"E+̵y~}3&د%*$}?4OEpǕgRX^,Rh%"ԍ9G&;)ntpcFڶO?,ZlAg3<B"$8bu9H. .W6mMN#s4ݞ5 yegB"4,~ub]$(sude VN,s$S?Ҷ:B4{?,.C?__)ӴR3mapG?.󂏅pW (@NDRTVc Ibmĭj\nuᛏ<v.~gapcop(iwXg/.W?OiiZb͆zTe^]jEXW^_Bm>#1D,~w`E[S!*! d;0 0s:~~w(rf ~XCq8!ED<,0K8ĐvײuԮ.c0h#JCH3ϾTb ?LqRq9]y\g@+>Ee9!%v]ib&Ԇ.kkpc ض4-5!Pm #R*Lz V5#f\#feZXk慪j0ѵPH%8-82O#/V HtEjKHzd75Zk6U 7f@R `%0+!BL0s jH""ra>hì 3TR7 X PհR?--6AS iGAdQזfS5-3/HZT2?"Ң>e&+VT4Myj IDATIT,-hDm9B V^g c .䔀!]@EV֕ wH,&ĔZȴuK[5lԮB)y &ƐC$*7B=&AJNL˄q%C}d+)(Zi.9|(>&%A ˥^ 5jI_.< e,J,MΝ?׹]wK8lr𼿟m,zd{. SZlZYP\FI(%dudx+nG+æ.Բ󹧪Z8ײ߱nȒ~b>}$(\WT X t-~0F.3˲050d&#/9_zݞo?}R@*8%#(햘"}?\D7B¼mٔTxLJ`wز,yQBP;GePU"8;7L pg|ȷfWza#mݲ(H0 t3mMa@סdǑq^%zb,J7Xx5$yf:Y* `MA/eh2O  )dtM_$9=!*\բq-s\%RIѶL?crm%K#1 dAo_V2?7UY)<#|޼jjǻ{VnNNI%[M -V+N+?>vf]/tuk-ǂ]r2FBDp붸`4(~(i4k)r9JIy 9/OgueswԕOy)K(OHbZ>?}4M'ڦf-;q4pwؒ 1p:u<)%Mˏ?6 w6qw80$vR UЯ#ݶuLs`=9!r&xJ~IMʺV~۱JE6tcd׊} x}~BK0L#䲋\=ٯ\E|]Kд[iikq͖ǯH!n9C`P:C,S㙘"st8_Olw{qU-xeN+Îxy7Ǚ 2nq4^O 1U뼾V+ SpeNl~ lhH Q$.Nr8ez!0%%d"|YA3]`jǴqWyyz3hur+>02ےc(@i=V[3.%PETF`n{ڷsYdd詅=uWEjA$s gUYXr*uEbLHk@ĮklTxj[!&tS3M 0|DiAUUò,>zF?c@"2Z^*uIryax(t\]Lh FXamu'>3+'d(֒6Md*emIK@$6@O.GJ*N4pQNS5J["@}\.g,ˊOLm(kQFCQ>oaRNw;BejObg$,R@LG/KBɂ\q⛏TBZô, COΉ( Fq1ur+p -uf4\Z MU "YC`^*ְą.k=J)N3qlꖰMU9Qu=hq i4VWY~~'ǧߩdn!RlւTN͆xr S"\{nKc7xx9 a^Džtt`,>Ǹi*y[a3v]qiyZ>Q943W򁦲( JJRCݖuUEHnR0)N k2nXش ~믾*LR=gh>?Ѹ8"KEX=CO,󈳒H@xNlm F[Yd&#:qjD9]l{!k.!BiN5ͦaj%F_~Ke* ^^MG6ĐϷr^8kΧW Bj^M۲." cH%/iAѴd_y=y8 :5G*cG*axzzM~ ˄;H.灡_uv?}hr"~R3LBJ+2l]; Cϩ7[\ڀHYsf&q"\Clmx**6%m-H9q>2G5OgO'E*x~OW4uw ȂFie{xr6X6-눟=׾Gk\Aky ʊ5EW3NuA)TH}1xmm;G A*,*$1+yZ$Q,F# ͮil7(S1y,0#z#F[s?SIaKO(u{L4L!z\݀QO,ٸLU`*YǺ R8QM1.RUWRRC E@t\ЦL 1 DZRuWIˈ J[R0*Ira˴mM"CI3 W\JM4,$G[9:Nj( IKV@Nu L!1N3Y눋p.ӌUe w'9!c: ڈZWPb4uFIJ5.o/k@QیdǏ~+?hJˇq*=Cۛ궥Yfkݝsם,Ab dAHȑ$HH-KD !ـ]{oթs{5_7/}v<<ia)J4FݑBkHm|1iu:U[* H\<>$qs;tԟNT\o'|Ӊq˒k+o(=Riճuٕav)L9eIpOG!6UV Da%$O]hUbxeV-ȊZe2{]@8>(JHJy͖HW7Hrۤ҉O{>}|GUeR涭;ڶJ|Tg3 z\F.4,DaUhJC/~ei `K/R!U60MabGۀo]1#mYtܱrpJi^9.u;Bd'b,6u]}»s#""phYMנH 㕔%vDV;SMS5x;7j(д *88!l'AVTUvy쎔Mחyܲb8_ϸQUAy7E8/)e`;fRfrMǦ9\K )A˒$ o-2q*FvSG> *"LL~)MgmX09 =4CTeEL2N=+[&%<#\ ĺ2Z˼,tҜp:e)gaX9To8gjg|\ ~E #s#t1??kwawG8\g}U]p@(AQv .\`;1ZaT%H+8ӂ;r/ˀ)z1E+xz(!mYT^W)í1$* )xx|" kYf_>s:߸F^.+bWKUB !ń[=/_N#FI-?cJrb{.2r(+|MKH"3]d=I1aiyebX~_;|b2qIo/_P + y(H4y\Ge+|\Rkx{=zt篼|}fYR;tLḺ[ÅkpNg*xUY2z6eEU`ln mˇv6DbF.vk!+Wܺ2M3˅~=Q,Ļw ;.vC"{%d HD CUh/gRuz1M1 ]\\ RewG04%P 6ݖh>2#6wߞtQp0dݣEDFOu2|!qO6hS"Me%JUMO/ذҕ Eĭh!֚*Wxh-ZL =RVN;TW2݈> ]nÅ҆5#O 28Ǘ/τچ"Rq]1vi뎦 lQʼzW`'ub^fXn fA$) bz+!d]xoj(yTU5%m[L䕔1saʩeD(Ӑ)F821HP)f˱dchچJcN/HI43 4vyŦB`{b;mD!EvRJ+wɫ!+wI`WRLĔIIgi|Iw12l+}MSij>ȶ*-LEEU,kl.l"*ew "*F?yG oU,m)4Ji;fK?]x8{EG ض]:Byu,LU89%ZJEm4~g.n)2=$P$ j+fCDSe!|~ݮh-8#u]0O7%^0v$!Ihy!ZpmZkO=4em;ʣI yEI>#vCTw'--"x6ޮӈ x6)4e'~Gro+ѮLswl:W$L"Q6Mtnn%U P|B.*J myHDc9D7N=0NyG_T]2(llj?f^,*4%QJH[w v;ʪk mr8gIibOtXWӄ(`+!DIO!hʊ<],cg߽e2XC)J__#v,KFdaH4- l=2v p_Y{H CJ2lQ݆q2\\O/53Be2 |<<1ˀjlh-+R,.(4g#y !r|{}JTbb,/? aJB:_y9⃣7{Vw|~N|2.8r.K6ņ`W$yo_EI|faFd*YUi)ڎf]g)ij-'S@Qln;v˙醚Vyr!Dc*5 =I`BBl g+J(6(p)vô8G (]ި o3Qi>px`Fl{M`BWt]GR´(Up4-m|*5ېAVZO"4uK"v ]!܈:KQ/_ upxx]/~ND;b QPiXi(nHUhVQ%yY^TQ9<`C+kE(t'|ga#BJ3) ں$OYTuVB") cGu5Ӳ佷:m=a 1.*+uAjͰNZlʒ۳. y 4%hږhL`gzj0BrhZH#iښfDaj/_?O mD)(@(GI8 ÐdEu)+fe1 r KD@ 2ĝ߲5[2(nYQ,;pu"rW]"- HYeƺUjU P&ғ\d&L}햺М"]QqZ:u";KMhM5TMWmUC!3ҩ 6&/Oe]Do[oSxxx+/7BLux|Ǐ,Da-p&Ǧni gJ]d$ʀ3QLxCSBte[y'[qw`fNmYO)$KSܮgh3[`o~%3ڎг7e˼^ma]-c킐P[v1S&º!S vY(kH"+*^_3裮EISwaAKH2Ç@n]oH2 D%~`&; Lːc>i:j 2v-3=󙮭)E}˴Z~z=}6#e[-۶EhﲑЧv@0eQϿ|Po6M˟ɟ?#.h(K7&ɿd^V=)P2i}J#g{y94) . E@ݶm3 K]ވ)1xHIUI@v=S7%fP)QR7E2\Į p6܆YVLlhîkxJ m Ӵ|}}cZ=Ml-Z*KYUf] C+h[~Om۠UӇ'D _lwS3,efUuM@7+:-eRpt9¶9OwL֒iT݆40J!B DY"nK,#ްFK+Qly]Dt1t Ca42<)J3L]. Ry2hX93mqӰ7`a)LI!K93e* ;.RIZSЙk=?>sqEi86- LiJ%ኖ 0p)#5 $?6#-29GSbN!P&s%6jWڦTe<<'Rjtu>$UMٱ3nYQs]˥`SѶ[S,㺍HwO48.)B4:DU~%JlŽ90{'R5")XhKV\gϒA_Wտ%S:~K? /Η _|! ݑ(I#0olیO:-MUq{\ ٟ^h LUH mc‡Di\Eيx}˧G^[ rEЬ 6F:rDp6ޡ%933E;]x~~E]DHHmd,BJ }!Ft痯tqh!dJ1˲?3ooL~Ra`RW F̫@{C$jgiB{|Djgs82, R S|WnEȚd]lQm ){=͆ &^)*2&k2.3G»VKMxd6MXjWRR c- JS B LY@IM 9rJ(R­0gFJHg\p(rض;ʮFw-6e-oف pna3Z2~$p.v*W]]5Eg;v2M=EDd/)p˔Ƀe,b',mT6msFݮ X))e~;߮`4}?5".w-Ex9_A4  4uEK|B$1D-6D0-+d?Hx~qk{n#8₧j+R h%r.7\rvM{ YvaȷTeönua2jX)VsȻeY M]YMSV%Re2cȮnfG"lakcnlI&o;e!Y#26;n+6x܆]TeI R aVew!%D|~&. .1H]=>m[_p;[q"e$q]]3=<6JJk JbdfUAcDHA%2uH`9̶ v9v  ͆q_%~]g6wOD3,XC)MVJt P*6%Fd:ur$YuIt(*-7t2Q g3*=3nZd_O>7A8/1ulWJîu4&ޓ-!x|xdWYGmJх?cr-fai(([BRc߮45fo*6n'XHmxzbJ]5-i(й)2ECej|(v[WsT5AI^.vm ײ*h7 Dne|Kd'sqv`Jşůg*{s1#$] $wdia#i">}x %ǞەBHnOYt>gS]!ϴ.e }~9leDaԒu6Z4(7h!`L]b*LşL5qC O__?s>h+(D2,c(fZ>|@Vg- !15 b<~`+^$-Kn(hʆZkdp6y!jI2jy_iLw[D|P&I U]Cĺo7ܚ0=]]xq+)$f4MJu165Ѽ<3+?~KjjB~{Ϯ1?mz;S UVXm@2RX Qhrb遨BD0DU52u'{Z2aj痟)ቦP 3R$R zrcwS^<+Ǯp8Z-ZJTEQI(yaX,iH!ee)=\/ (TfL<..!PlwOOΌOU 2%:(x3h癭Eʂl 'DX-kϦr=U&v[.#6m-.ˀ$`ꪦ.kTL+fU3y*6=J,Oԅa{ڪ4xRU@TvzP( !D%+ v\Y HӼi ˍn9<Q:_6ĈT >xOytQ2l75!.knd||yЦnGLi+vO@ m(mSJm*5¯3JcnQҳoRWߩ6.k׿?[ZR~1&6l횖aP͓8] fûH.RʊƵ9/38@ ~O |:n1@-:fpޓbbSDP45ն_hY?l Brfnz>(E Y JNÀ PW !œ*<|={{J,ɭLvejx{;YIm_$.ДMGzqBD[ٕ-][ {54GTR(*)JiA{B]dsaZ!D̡M[7ڟN~T&#SDŻ/(- XC@hE]2h%!AheW/x'i7%M],3B+uBFOWWZʂMY%ӭ:En- ?7(f8=qIveb<2OϯPowy7[@YC9]o\7TU^E]ngRծ&*j!sEg@G~nz|lwUˇYQ!$u~);ʮbg6mK&v; XۍF*Db I9H)ezoz0-EjcX9%%eQPɆ/ogeOߠbB E+d`2 4JMU )ZFFLYnqn㷿tc:ϬvCOtu4v/dV$ Zʪ+?p98|e-AJƾ7ۻ\vxC%MQV8/RsFVDoi"2'"P:øw{Q.^{z|ّFεqY.(SF&Ov}2:D;n2e׺Pu ҒM) ˌ)Zc1DgިG!Rͦ0BN#cb1,+m]`* 2JC)~` /$}?Åc=eYTF93:=R-nAj낤Ԭӑ/1e_o?<-c6ĺ.̗3t-n8_oTeI)5ZnÌ,jnCX 'augt>ܶl g8 ,D H;@H !!H ",P*Wսƺ!u-n|>Ov(v'LJot9f_? m43 s\p.y,}`?]=Us`cVl:ݖB$++^_n]tf:KǦQ-,ѳ:+1"gt+}?vfѡrDg5U^|r}Y<;OQ#a ͎za\g#h m HIXVDŮ\3ۉeAd³. d(ص[q!db&tLQw4eå2q I;GLsGn P"hN*ewwU|^^9=VH7Jj]4Uͻ?rev]TXP@iڷt7δf_ۊ>2EbJR`Zf X<)˜EFN}G3րfq*JaF)ݰL*$DVdMCY]N<7|~e)BC26p:XCg y9@*t1=FHrm(S-:zOn6L)*Cg(r҅`4dFpR%RH#toM7H /׎cfu^噧 oѹ!u<lwxڧ F+rSmE ەe^R`˚rZuE1YLnl-weQߵw ;w7Nza\V&Xlx>va4oCnZٔ%O)6-!&sid)itHLㄵdxp,26mb[w MKYmE^jL z=  IDATˊ 룄,+M*r33/'"De]hR=z&`ocqdLO7udG8PךWu 40O#Z*fm|Y e vt:1/ %jߒ%nFif\-4T _nc'η'/yu(2邍0HqF:'*&U 2#T\jp![J)/#f'B*̋u>`^Ŧb(Ƒy1CpDZIJَ/1u=˕˭{ NP4}&W5ee]mBQd=܂ Znagl;H e%r!8LƜK*&^O'N3ZR6?we_eKܸJhGTi~|>߲ٶHYmۍЃPe g1"˦FK6MY_t$DwzIe&t@<2C 6yW˥R#@]yqv"\Em(9lx%SbZ(/t p6%{l_f=YǏdUEVxYf9.dt+aDL`k#,+e>' =P9M+fXfsd&c 441#Lj2=:ϰ1"aur p*Q&|Ύddq\rˊ)JLibfC4%AI|:Casuݴh2` KHw158<%n7 %IHZB#PMi(a !tݻtr! &+x=]ɋL:/Z %EJCS7DM#Ď۷lPfp@,HmuvUͧuu3YJMM=MSQ6\%gWX&K7 #MEf4H\,mU`t@ '|Gqض:w A,((e~2u{n P4~)f{68u7\]U*MFbLsyieu*"Y` Q(ð7wt3Ļ"+9Dw ֮y>:L%"˒<: D$jj=~$@7qz=p2Oh*5Yn@^D|{͆P5ud9㰠0\qnI=u^T6^OK| +"rx|z>bי@@2. O<>>":vat輢]eWQ6/2Xú2r<^RH5 q8S9q`Y<8mZU Q$Dl`fqFм5WU{F%[O=3&a-3WI_<ixb`sس-3dT-ەH[ԙCZe7k,2MEODR^Ͷa]GedX9`Mh* +˺ DX$/'LYT1{G5֥vep+E$ZMt^, ։e#"(ș֙uF蜢RߔHUimODMEpL ڪ9,ӭ#גڡ]_^Υ(+*sg&TVbUqc( LYoQ 4mc^-[eaPR ͥ\tnh>qf[7i[we41*~/\nue/~Y"1lsyQu/G eU0MJzη_`+ϧmh#"_O;`gT w/5n R2RI{l°.#3T3̲ ww[] ^(TLBYԥP+㰢A ]W ˼{lxW:Q5Mvk7쪒r,7dy,gSW(QR|Ϭ'w; @fFR58 | <^y9 =ŭD)5u=\H)ߴ슚|:ÁCEQHNh qڀP#|>-+RgH/M&JtX;Qۦeny¯ Ez۷¦izq|~~RB |.LsCL缾^ #Rgin]O$R%J)AJ! .R6'OoBb$xESmӈrîɲe(JV՞}:iG6.Қ'22%845 X=(0I -| *Fat )BüZΗ &Y VzAThe)`:ʢ:<=nmw먴A,}]<4l(|zu|;4M#vBB$ /hlIQVvzV@t+k* kFΧ+EQvLc4)[6-|>QFQ 9F=Uݤ2O-Dt瑺moAE { Wܰ+d釅"nD&౨)d\'p~L\(!`nB"REDAɦ#/ g˺ʢb8,44,@?{5/<2x8X;ɕ.3g&q"1xi͡%48)a8Ҥiǹ 3uUrw%Rfary1L'$1Tu??&UO|/f?s?EAU's+JBpÍ`5B\29W>}WyJaYa<2K5E!%B|, vJ$§3l2 /%W ĹX׀-X9Xn`+m7E0 +@tya8+Jw_*ʼH7**qD]zuĠ8^^1.CLވA#r{w9+ {ǣYΗO4M׏Eɲ㗿ڶ, J Eq.\/W(Jm[r<2%:]>-Y #ژ+Yc8$|/~3~d]-kpǁ㗑Eq2EE%^<D+F.f\^)(!JPP%QYF}^(e, 4 MC ȤG AgG"c5ayNy^\G X v#ˍq\YvQ987axljqY+R 6-]?r:H)WǰF!3ʬn`dA\|g~fiY~=(y٥=GFf JHڪ- mø 2UbwLB uSnzatδp^,srZPo62#D~ qH+gΘօ[8:G6o4A(ˊ/҈ov vGf24MIU"Jhڪputش5/#_^^ DT'? ^?~YG㑻nO9˴,6F3o;I~[ϻmDm4JRifdfAa 6qXrb =M]\ˉv6hm@ȼTM0LXXal>~_ft]ж -y\N\O!.}^_B _xPIADYy!HfZDb0b$3󳟒eOt1$3Q8dJs'.aITӢpg^V-2gYWQ-ݦv(2YmϼZi*8LJ"dLݞdtg?ozD$vJçch;ivM YndQ"B1, .ȴX^.,%/+< pÍ+X,K=6 iXk! 2MYL 0$YYb raN Rz-ɔ,J >*<诗Tv- U̺8#|stn}BDK( 6=4S9D1mtTLǧ#֋u]˅R)bDY)=> 1eK0/uf$ep+n1:.1 i[V;q=nҔ%s O[ *&DCST%bȼ&f ,lUNB_opVeCV|#Z)jnwyzza0e4v ;tj-[YV0L҆z%?EDHUhMIx aR{]Fv-Y,vg !Ν2x8^bdzٶL|IfGo-hQ,4L <`DxCQ iX煶mJR1BF6 gneYi6.r`-!~NHOݯe~EԜN'RF:=,B[oVM`z:6 ..efXg[zAII,v茺nX:PHj1ai3v|qmjr-L$׆۵'jŸ,"єYZ*>08G7LsOt+Z%8\24x;Xgۙ~rfh~i 'D[h=ݐB|;^grb&y)e^ovɴj#IBj l .FDfx)4Dӂ58g4- qBP~ x2`4Yu#JEYDB"ULkȔ&3nò`%Ve 1yI?MiK3I֠XHБKA]׌΁2d , Ta4>w?<' l ##Q^=NoT˒%y}L@Tl?=cW$FUo8P%Bv{s!*򂵞yi(ȵB++'|= _>s9 RE*=s sczY72J@SyM(k̫a#(ɲ"E,'i26"hEU$G6 D"J? t̵x}=gy")V%FAi^|aܺŮ:7(^;0 IDATL>m[8] =簮p֡P2Phrl0R12Tkee2]"U@`0tg+a9_Xxәf{څO)oT%*_=E.+C j[Sh23n^N(>=S%.䅡RMU2O#$dSd2s^__E??wٵ5n00OrzMvg~h(AEKQJD%uVctF7uDDʜu>Þ0r]Quv'޳r>}ka)> QgNǘy 8y2 *ODMaڔli$F Aq(!)+t4y_-!FFg9/˿]Lq>Rw ަ,膅>=qBpn"r<_*!O#֮l;ʲB :[ι.Dȫ VʪH Ux/?8u*3šE ѳڰ̉pdJH)vL@2uHv&c׉"'/x=^mzJ/ޜWmMϺF;[sva!dEF Ȁ I N>kV6#a?`WKsZ_A B!]` ü)an*1صH^ }HoL!09& R"H))&#UU'xTuu$PB%h-Zpz5H!QlAtti21;DJX5(ҶaShR Fx 4UE3B0RDʂ \Ԕ2<` ˾|6pY7ڻ{:k2ZJkBĸ~GZ, mDk+D 6a5˺a#UYngWTm= ̫vIfINWuVCwdBX/e"`Jn啮,!Te&e]}e}~^(li$ #mi0F.SSn{FvHU2xFn+ޭDyf_FԊEY:牏˚jT!RR K-IUSam}{zBI KTHJ ?sg; k̿9d~Yu<\ @G>~%?o sRJn7&yEYrpsiZDHѳ?We|%IYuHUqOuIJis=_)#?|-J^5p*Ư?ZE2!,k-/ehͪUwuzzڦcfF q5&;.yRuADSigHnYѶ7e(lE[TX+)?c@JЙ5B)TQ"LA `'ҒS%uSs;d>?4)x.aYַU2.iʖ$@w|933EQ@򁇻=]WR[KShu]8Oc?Ĉ>R*%KӾFϵ] !6,(,EY0-K^I7)EHB(]B563Edk23>"!vX Q(uMZ7laeqfg ˀR 1#QRLJr. DZ)Aer+0Xt`Z<"k@qX׍OXWy\Ym8ZSڂeQ0Y[.lŶz-ly&a9(Q1CCؖR ҷgiR*rtk[hsk`RհhIr^qQZw>=xw2/D5U(qRȇw4nVw)Q"TC o0. ` 5H1LW*"zEa<)"ƍ>L,2p6mYZlO<=]|e]6.0ڲ+]! eKQ2(iX_~+z㇏ULp\R<ȶlheh)/>&E24VB6u}%8Ç<i\qbgqhoPp:Lϼաt%[v),'s aEiAi ֆL׵o{^ئ<\6xwwD$~ĭ3أD?#Ӂw"fB[ F$0=t]CݔtmՊ~_STerF oN4MA$ ɖVgYsHpy8^FoYԒx#rWasqara ˙)KĺmTMtmvn\./F Ñᧁac{k(#0Db&Q˴,D\ ٱA żL󂏂~ =J4#ttEsmmUAkQzpIQYR6u^pI9x}atA5ZTeC]V.9eDkI.nln5 U!UnVDQUEYWTe{?>r:ҽaR{HcEU"ĈTG k)hI>R@IVl.yVhb PT5(3K?~Qʠ낐cB!YƑ]bL-w'PTu8( 0o BvږݡŅ6r%uYa݈RXQm`W( OTauH)Keag–<]\nMT9( ѭ꒻SDz<}}o@r;.m0&ınh8/+IYԊ(a*k$xln k:ͱHOXY>PUIп Q m9Q涉14|p"ZQl~eF?Bۺv; ¶7eDH )²,q~|d X\@6H ?oܕ;)9HZRxZ8ToLն|̷WB(1ڒ\ y4mEo"a@t m}> R`[R lU"JlGeRYhi60zPdJm6#¢PRx-I*yáiHZq'a-s{IvuV@q_`ߔb0 I?,Vԭ*dpl@SUe!i2uZre댌(KV\^Y[?6'ExgLYULs#Sb״T&0/+1%JO4?/݇%sϷ శ,+cM]yb@H{+?mK5$i޷"isD 2 kBo߽ӧPF%?V) m K~CKi O0Z/+jkj 7|G,˂4ErhS-j;mCwb<.&4OmKLwW/O}eۮ$!z⇧ NwY&V/7^^RzDuy$T -fKfQD68ރH\ {H[Uфq Rh͈*DzR eEY6^/ZŽOo/߾~eRjn8ffu֟u-+wǕ(N=!xBh+1)ʭ1hw5Re)ԼEV|fX.1E-{.ۀ 7҄XRXi1U6^֌޼c|y|u51̔aXbJod݆MUXl,; B |򘪠+4q"Emr,kز|&zcYFg1˕jiN3R %9{꘧a?2#-1n9<:جݦH%2[L 9\G&ueEHxasAaBYmdz6lƗ %47{i6 L(AN?sܸG6a={ N% D.~H6G xXb,*$-mMjCQ7L)qf23q5ݎ#JɇaY$Mw).QKb2 G PDL$ʢfy&O=`ɨISV,[*é=byH >~LdyRtG_\?_?ݟ/gy20Ύ@i ZI k(l|ӑq4D$>& _~NHX.  ků~ʲ@+<9!:g[V"s?kBߵh2A[$";֭I*\L//HWVH}?1 #MSpڷXQ"09Di$$|D>2-ug5j^.nmQ'SL0 2VmIfWŭH )&bҖX%"")27.)-Rcb +"%> 1n+)hJ[duADnÜA6ޱRcbG^ox L3u]̎OmL r}J]jB,rT<=?5[ WFQTXdlYl넑 8q8ϯ-6PYp;.s޳vUMQVcO CJ61) B/$u˵DUHuY)ֺiZ 1}kiʖ3)(mgcY7G$bk59b :l$oÕ0EAYV u^n(JCu]hۖ0-+Η]Pܟkv5tMǡ0Fe?+f4DFȂ2Sd6N{eͭ[Rc:7]o@Uܦ|U w͎X\ؘ [hmH ӐR*bUѨDE÷[ q4:ky%Z*~jJ`lPwSTeu)sX<ȘC{*g h#Ô?ߘƙpc*$~SW60:T]ux^vxq~~"xBrgbHH IDATB ubl~5m|(4c?sxuq yj񑮩(jv&|n*R[`t(/"8@e4RwX9-n[(TQ˷W!E dxmKLÄM ) RC()P(PFrgx yAOt( RF·6 a@QwHD[1&7 b(=OduGy͜_#MS2#ѯ .9_<_k*]Ǻ(3uFey9rk:PeI[V(m=}+Bp(kyA)zm,ӄz;'mɵVD_@SJ¶9Bd&8wTQv4Qy_RS֕!Fx@[4MD"A=RR<컌)T|Hn[ 5m2GRHD( !yy^3O!VvVvizBxY1̀ZA>>gm#ʘ=!FWܵO߾Ô5:g]K}($. JkemVwȰL\ةlQN5˲2+MF.ӌ_C9|'xA44, s #jef!;mcZ6eGxmCo>e&03*U%4Z nQpjZL|%5UUc[ZZr,Le'!5v{;[s+D cnkeqC[؊8)%%//3}؁ ӈ hDSp'2olJ}"Iuqʲ,meg4)4y<(ㇿg<}c`XfLQm9ܳtNwwDMUP(QӲ95ﶉu|(#y]/3P(F&6Umag(օLJ{ίHʰNsƛ,A(7wA !O@-%m\ϷWx#D "td 9]htPҲo e0-_9ZBSus.0gJͮaWdV1%4;ۙC]rl|㕞#0fm<8.bߵe D6n퀄T#RJFdv\/75@CݔX-)cN )vX [ز-cK׿w]v%)FbXepӵ{HΡEZ +˒W@h(Ίw6!ւB ӰfRGdvuEhEͯCh[;AnhP I ͟q@+w>A΋O_) qLQr兢̈~yvE:\KƠzad< v{o<ח3B6W3*Dp}1r:)tEy9xӤ. cBKŮj8wyf*|^B}M.DU7$y{:\R= t8!cyŰB-:S9,iAi8,;sv<}ۆH0 ˺Ѷ+`^;^}a~`Yf`6tSw-*1EA nPDxDd1 y |`QTt:7EIrc'H-10# QzvY\8kɾ(aZfF?q,ɩA&G!I펗WmZB>~B`sv^X}4%)-1Zi):q=YXط-.:u' A<) ( ]6f55ێRhmvGiǞm 5۸ Umy=RU%IpH'uADɱ;^Ja,-9dTl~f$!?!~o %cBM7L6 u/)cs}@8PRQ%glabuh-Dž:ව[6*뱵*m<e&i:yx4$ VW~{" H 01n3hѨd %F¶҆P%eTĭ IKTO2EO2_r08kRsG֘'G1&<$hƁsI11uPV7",<gݞE1 % >%v%~Y~umcwwZۈ~Ɩ ܣl I(-Jit#iʓwD'Uy^gv=ߞdf陋1f]-3|[Ukx|=_ph#OO7$B]whF)P ĮQ?9wݿ􂖹O!%B)aQ2Dȼ\qJLYPT0pPU:3ZLy) .a]˶#hm :afx<o=c[W~OTMg=?彫X(Mt:/38,j^4N,".tЬC'_mo땦sTzC~Pe{BJ% m75i5yBaMw=J@WϨPXm!*^^Yوd@II%-N@w=IvG#L-4!$aW1uIUHҠrO"5.Q)s8u}6 ҙ38lͿjjc_8x;q=?S^qQ;J+.Eq! du!E+ZRk?P؂#H T%w'RhBȶ–:Sú2 &,RԂ=!fuYnP,F¡Cǀ.K&q\QҖÇB釁a熂s`s+"b$<~KؖVuYU 3g<ؖ>$\ <{$H_(펻v 2ͤmx[,ӆ@aTDb䷟|Ъ y(+Abs`\7?|]7-a <ϽyvUmaklQsb*L,)L.JpihڒH|g u 2%QMU J*˞q@*:easfaIָ-Ci-Y\Iלך]e`LIC8 $ӑm^Y_VQhc}`Ys jjve)5eia,,>x%I/hUEH!Ҕu+<[_Isv!ocvf(CR3i! R&ALc5_!ZRf+*Mm9`&\H.Rß@iJS6;-.@@D֤Xa-ISUֶl0FroLnc,dQ0 SO˺L7-m8UCpaFU7TJ#2\̰͹n.`d Qx%ӊj>,:cdh28VX gB3L3K8 @2ݷY<,7 ix˒\ޚag {=}SU{v58CBQ#! 2 $#D,i7 }g=` -o'8g}E낰~ZSeb3|h[k 1 T09:\Xb@4~/2 k#h8\z)ɋ Ds>|ժB1ہ5z(瞇G|{^ Ȑ8?S%f8[/E}`Ma]s9!U#N)f`ʜ02,PC;8]M]YZeN`ILvM~LnhTD%W5[bpHS0wKbW[!ç/h$%rqf=ZjEP dgFP*IA$zCL`Q N81J0aOST%y1Ɖ8y:dy\p) J(y^"EdլAsjl|4K%lj%j ;8<(tƪ9N\ ,~7{D\s"BH".PS` +E='"#21otcrRpjO3|F%Yiժ4$BsYܞyNo2!22 H]rȁ39y0;d3aOs/xП9svd<zp@DJg򎶻PW%5{q[r#R6r>> V[p,k\ey~B7Ӓ޼%#ڐ׆it1FlJıgx^m&Coxsgab ) ~ޖ-*#Hw)]џ;3(gBT/T5I(nj0urRe˗u~pn0vqh|!s;뷔// gܿFD?TȊ7M9 Wny{sEqvX(7/G a3>}ÂlvLe1%~虦\j <3 0pr¬7$[~; hYDB'xǫ; )*a#)|extsGY5߼!:KD(sOhm(kCI,𮴸\ly0\-~/$+զ斦(K#`Q7ee0X~l$.BljO޽ۉyE "IȪpA+/JbLY!|y~eh#pG~1&cUhHP!*$H& rT9"LH\(!5$4m7"QX%!wMR *-1Z pʊr>`牛lgtm˪y}G ")|%9>&fIBA(xD <<YWni= (b{?[K(&kC3bDf!Z*hn* IDATDZpƙ1D) g)2[ P?G_$vy'H Y #ѓLmJfTesM `Źz˘GbGs˫WHa}dmў.D-x2j]S4N|>#RKH;L#e"R"Hl{ 60D;N<===n`r4ڀ.K)A2#,  LaShM5q\dNͦ{df0Ur<͟vE!51%R6kMEZ|iv(sMSUerWR7DF"B$r!EJUi/'\Nl;YOJKX6 2ff, C@e92Wutb@;Ir%ȄD&<[|p`f 4uK?SYU9Ctb=nXU9MQY)q얫f C5J E[2~4L0Kɻ-ӡGI@B4 i8lˆ0E :B{]- 81̬0uE?O%`'JA Qd-L_hG; ~Ѱzxx:I,Ss8<"R49(ww_BbnjBLտ Q3,_ׄet ;EDTM|zΗ2]$θޮ(% )JVyNQ|]@7:#&$ptFi jDKJC$4pseGrSp9wj] $5ZZ`ax^p9׫l4x[rqs{2 <˅=vK; !.U\PkJ#'%R$2BRUrg. -ajnȺ^Z9S'MEם1y;["[" #yԀ{ ۉ@D]W|s#s(G0;,b gtQ2#Ή %9woI"0-.8peHԦb/Q%N4u"RT*˰q71On4dXڡÏR.;6EB.oof: @ 2EoR(4 :Pc݌$AKD}l_9{~_7g2O_%l[Li(ʒqn"9F>~9Q77$7Jy,L9,' F33~Ƈ ^ȥ-WWt9ZΧ=&S-uzS"e Rdz|~`,78w3ϧvnr-10,CA8=}f*<= wج]G=ҟ()9X+n_+&bkEWWoS v"snk4݌%LܽTz'͊DBD˫vXIq9( cXZ.!$,B"۲-_G( uE _~B茺 ~ʙb]yUEEa^ Sk[a AQ!r{uM5d$K0E&YFH @x9ciDV.i AFAwsLew! S -31(-KC+T2xiIIFV UlPGVͲEq&AU$9tv#f15yvE3)B+44uYyZc -#EU sd+R 8K}-QhSж#ҳZ7&)o_S8s<R< ׷AgSnfGg" ZS^>qfɓd\QVYY! ~F9m?$gΗEQfK̎ip$sBS H'QjͺvwEft) o^|㉢o܋vyе8v3> ch~X~vCQeݙO S6kt.:ko)ڭqUh^xSuߟ9[?ps#xBM]ܿzb3- oQ%7 :9r Ζ$n톷ov@ hy3# !e}YԼGE|PVRQ| ǙRx*[|RQ^mYcOS*vͼg` >ʜ02OKk&n^sx>&DYj,uVYh1Gl7?}q=R7W\ݾn42Kbl 8RW ) FƱZ8g 41gv1 (>qOn TS7;ꢢrTܿ[4֡f,楐ZR4 4Hja=$Γٮk*3/2ՆUb'R(%Jê*1p9HV5>:Ȕȋ`&n`sM4Lv櫛[0 KY1en6?Ӹ]LΓR.k !vKa4e"E92NCkԌ3!0t-羧\V%?q\nHQ"agʢ2޽&P/O{,YRh~K bzuQR Lq&ZTnaxƗ RSdJ`9.|!Sf˱kyȹ@g!dBDnw2P2~H)2-c;\%xn^Mӌmκ/IO=/)W5~'\ky3>?ٟ;yެh+vWdyHIt u:я3Jknv\ Sy֥av'XTJJ˚k {>>0tQj>=|)AgomTՊ$r$/W)%V ;lpa{s|Ag9}lk޼~jxȋa$P0^ؕʱfH~A%4N\Eu=Ly \mnJv+ 4$υL5o_#hBLt"9_¡ww`>ob˗EUk4 UO`=U]ssc7 bT/_8 EQp{uC!UQ3åZ7$΁ Pضw|y͎ғ9i!U HS{8cJn\\'x9?}fásTo\9f-pana]V](?1<N#uU.5y4\ceU׬5qK\iEGC *2"myssC?dUE(0"ZUIK-2F/]UIi\n)J#zWSe96풀-y\`^y ,+ڶgBd9T)2Lpj;dSmMSJyLZS\)D??aۅ]2,+qQq"̉ް]_cim "D)f81 =P/Xɀ\1[ϺQe83>:c5HvX%ˁ: bŧ'Onwᆙ@O_b`pfbݬq㌊hgb09UgPvX 6Y-Lw?3?[^ œhO=};,<,k*0dU< ɩʆbcƥэ28]Z/ ރ*udU+(i Q IDATx &D78Y *'/%!"Z&6[%#!xʺ\BsB+J#wݛjeyZFܼy3EWQA *$hVX-@@B`H Ȫ;pozwO20Tdo'0wZ<4e#t91fBybFiC0eS^۪e) AH"$d.go O/yFe ZJ՚yv@&L!%4U]a@F 32R|iiZ)ɪ0#9 "$^6GVk)O`^㦅Q2db`zfUڎ=hfDslz 8mve2ȋ9M<{wdF{a8J(#몢2rU| ]$)C*Mg6=Vj膑OtL OV@znJ/BCד4Xvtg{_pۼOߞ^o~Ͼ |Glw+ӱahF<'-s?OdYF]Vˇo&?2ҞF|`gr%( ) .-S׳+,g:v4;ɰU5nC2yV Zw8NvOL V5!O|wu03t={Lt0ҧi\6Ӝi!QM?"XȌk;5|qCqC2ard9u^%69&|VkHGJm$e6x?iCYV|ȷ~5,gYSkRLԥGk "PZC/yC;1t3Ȅ( vU"~iڎ~(2eq vF|>2GeTEȵFHD,呤z].|٬a*zG cduIm5J 2)rꊡ[ jS?q|(3grX'O纥㗘8sin$K#mӒ:gE.{L3vA%aRzt:Ɉ>RfY~ 2X}"8HU+qdg.'x*>}GL^}wW s/r]^em\3v"uuj~v(Hr{FnwE,ѹW7wtg~Hݱ6T:6g%& @Ʉ^Ȏg)6e UY|7o23?=>92 x4ql{-j D=)dZ_gܠo?^GN3J&rN34-? tVӢnZ7W<40MzCBP%"Џ! a"YEߏ:#*NJz[PY4ө%!W|-,2n*û5w/"8\7W86EF e`[Ҽ`/ҍIRഢ0 Mf*j\k~ݏ I"2B&V#(Q(bytU([fVE\q#evʐ*摮FO?,B$Brx? ~JHVUEFڔh1CmrMD \Ji2u Tn^tZI6yx~EMQ֬ꚶ" Z^Uh4=21-VDDK0*cI GЂ/(ֹAO {1j21 ]p9]c"yb&(*Ғ8ezCJC$OiD\s (Xb^.8Gk5X1E.G%5.xf~@*JBvXZ- R@Br D7uNQ<GƧ_?o|~ O핹* Z.8b|G4~FK[T)g_td'IՆa7W*||z|hm1M MefRQG| H^f$h-n:HY '. UsNBJ8 aqS1YeRW9eUs>H(m CafX%&i m%7zEf-E=)09T#1t01zGR8:JӜ;N avKUTtMt&q~OuU>$)ѦX&Gnq}}}GeYk Q4-B#OUV].i1#Jg*3Nc[YVyN3e4\d%9N~(I$x`h:r- rjHMYFvCYdĥi8NObp vX^'wo߹ƿZ$Kwb1Z e,.|Фd0L&Y#E gTֲJ ӌ'\fM7pBEf?+a|0Mk @g7*qpHi(o޾-67>!mA\|=Wk^j/4 0aM|ŀ=ZHb ÄrlQbLjܶ(8(e:x=|ж`aٖ+raԫ5Yc3ՂURJ|k9O}O  <3!BeEf1_9^&&pn"FD}sfFkIL0`O=$qXv^~<ԝX6 sZ˫ B$?5 B uAH=eal$ k 2Ga&/J9,#wW]8\(>zqEA7N2&rҶL)"Y21f HDӒ<'.`^$s{ڮ#QjfY2%V4T*c#KW A]ULZe{5?/Sbp>I>Pd9qjX3jc4:DLnFZM7ޡA3kDi3ㅕ1E;#I0N#(ynȳ|)|9  3YVx>cp3M7 f]|vwC& Ύ-HgG7L2,J.U$ ,¥ɢ^v<5DXD"KzD+AaxXez ܘ;Y [)vG !s!rj:"~N=w>ߡT:Uŗ_ժ ^~$ ÷u=s$Ah{Ŷ*gOH - yev(^^Gf V<(zrEB!>2GGG]פ 97h/ o>fվfzlFc%DH0E6{xrh|@Y~.-0c<>ŢN}zzbׯ^Z]-B4K{yֻm4Dz%j'3Vi ;v5ܢj0NY~G[s;;pjۙz}M|ݷUO~U#cב[W_CkZ%~Dؔ>?3 L:zŮW$y+| m21eǠ8eg ԫ~Ggڱ'!Ru]aA&I b$)c.)~H/KϞ3@f,uEY1|8" eS1"!ךu3 nMU'?OQ[D)&l*q-_9 i$LB y0zgDخ+T LϿ?ep~g ?G_JFՎԢsgHBBbY*Hb cn(3 l7M2ć#L>^ $R&0%u~w~HBps}jYIPM]b\b.黎# h#ȴz&OHo+TZ^XR@L?(ztnn:Ne2˹#~i[Xm(G}N^g$̾ 4ސ]Ogvn#}Je(:51tUc  ϧ%)QR h`ʨjCd2LS`Qh0 .p8𫯿ZnO2z'7Њy:].i$7N97 r ZSki0űFψZW5^- y|$2*ʬirC^X:i6% DA3 87[|~wK87RU9J$2#h=ׯ ݙiҴݼeZE.K\>TTe]K'l+**W+t3 vE.Z(6!&6yۛ+iG.%qzU1~lBb<$(؃UQ|!zRjsaY2"ZI!2OX" V#mב9Ϗ&g'DaDPqsFIAhmiI1QHp$M/{ @~C$83̞yqRݖS7c،a聀@0 =2-fY[Z ^aԷx-V%(-HYT<K $Aэ\31IGĀ$BX<)YnGf,6|z`EUk,>SrhGHg?a0L-՚_ٯƑfMVjnow n0F. fAr|ul6{Bc˩8\ZW;n^q5H!+1 M7 ׻Ͽan"X?Zw,OLSC4 NDL3 ɀ h?4us=]wa]^op$ ӇHUo躉2&(=&ϸ//˒( a~|jm7굥*p<̩܀(+H7C*Y E ۪JLaIgD{rkخK'8<<>-,#c-l-wǖ.m #Qi.=mRG~$E9&/ȴe;D ˘G-(&CDԓD(˷%Uc3>]1!݌2# *HKr!r.mAH;;N#77$p|iHڰ&ПZp7_7?'1E\m9<yQiBX^)N.͟S5EbZΨ IDAT)jN"Q -]7ɰ24-Cw$$5H2a2jg ms{~`Gs[;ybOZ)qZC3MM]yݫ=y0,Z% $1&`hEf35Kq^B+ln@ hf}wB/o)38p)& L^mO[ e btQ eH9?}B7SBM.3aG|{e˜%0zcJJ(" *ooȥt~_CIMpi"u-K@f65mך˱!k&%54'ǾiĥqNc|BŒ2 o&@z24HPǶGeJE1{VuE] m $͊,8O ]0 \S岥/ q򜻎 lS$/ >YBT($~i@YȬKJ'Dž9L$" ,M3@?yWWrΓ\P c-wαk-zW`FsH!T5eQs<e؍Lg4c- HrSZ$U\0b^JJ+h /F,cg b :O n8.œK*12Y1R9ɹp.޳ ʢ@kɥYm#EQ0Ն23 ]C6'0 -%F+%Cp)'<'RT| h/UQ3Gόg3"!&0RS(OGuM;'NX~ 㦉.B09Yo~G=[͛nes]"|kʪn0Ny<͖<_9C՞<خ7:cꗘ$H (IQU<9$GY~Q^oW(ʒ<_ӷF vdN9$nn!E6 4A8ILvC]儘R"˘- 3}?K4TUL<{:Z2J!CFʺ<Òxۛ-܄Պ햡9'||Ap3J0Oy՛7dYaLQpgiB%eݜVl7˲FDؔ9}~9B^5QY{-p(|VK˂*$"ȅf,#g9;&(M"+ eQ3mlj&9()%<= 2, t`W ?f*2|qwK-cr:|7pb}5>^h'O$٭k9_zvo_`2,1BG3y:7:ϙǎifG=1Ʈ篱y"5ZtpnBb-%1; ҆ @j59%nb`93 _qw{˷|5n/~$9Mxy^5= 70 Ĉ,<`^{i>abiRc31<_,@o ~x zۼq" /տonwӶYU<D9i3禡 eYJ%"ȍi(ȩ8u-9_H)YUzǏ>MU(}OGtQW &?O0Q׋jVIO4Ds`{rkbAQBp}} 1ϑ1D Sd{2mXu("QZ*B8"t<.!~ b}uXc@Q&[ēX" &cSnkGOnx&⁇V@)"N$6ݮv=B5-?9\jJ/#1x#jyz!ObuS (~f=Y3Nh(vVkRy~F(zŦ*>[561?sws~Zx>{Lz 5߽g&FXэ+@iYv4mOe)sv4M72(? nY1Tc̲Nx8آRXCW6Az)-434L@a"S a#A0QhfWڄ*HYnn\/|t úxqwW^mwD'i8OrWmDq (reI`,hF0/5^iqiMl6+h] %jn(kIP">==]deH)5M;0͑Guu yx#(B(Id6e~1&"`PmZѴXBB i]M?henK6d)p_D^kfB,FK}&z[*h뎅l,9 ݌l"N#͆_-Z%Hr@ ?M 0IO,M &"spnR·<#Ӳ5y'%"몄4%+s>!7OrM_ƯbID)͇ K<ΈvAHYq ??aI'Hqn"ӆ,膑f"Vdflr4H)rssKem͑!FŵqQ!z <+nU2s}e,mfG fCUH0y.׎`f{ir$oO'~+$/Ȋ49.LS\!%͞&(ԣz,9ͥm4Ͽ+ޣL"|AhfG<.LA^IC{esS߰_el+i2>>uv+TUFӵlvIp&C{ݦ$x0 ~8séR)(5FlgG]_(5~^Y6#eG_cB19w0#8p< xonښ̰F1-ibb#Pr&%ј'|$,$*tiMPBƻHSU18abfʰPvia$8-*mx7}0adg$g 3|GÌ nodINpstDǙLhN4zޕknWk GDR0v#EZp84521Z)LiFEþ =\N5NجW()!z*ˮLfyf8b"~Jsiچn򜚎+ؑ)oXm(v"]&d}Bq&3i)A)7?a,Ww7Fq9Gf,ELjN$hچ"';O~g}en1~}_>}{m7px"YUDp4\~nSh4P#r=2|'^sssK{i!zSiBc蚅<,جR1,90#8T|^I4[c %7\*yv.j֊9V|ڍF}d 5W XlP*RJ*6NKwHtcOf M c~b]NpE&Ռ.-M&DK$'DI^A'K=n8̩Bɔ"]"Sl3 P_&NHa3Ѵ]~t~BeZ"L[RS*-Ϫ?uqH}"رgz/ o'*,UF'臘f9f'2#~GSLw$!OSi*AK͜eiO'ȄeU-xXưZ $@kKnr6INj4Y Ag)sE*Qhbxs{稽dU-SGy< D[ 2k<3,PlE$ ֤ mTMVI\S)B"DX9Phc(b01;.ٮ.8dt'3Uۿt o)~ͻGI69K eFD&3_hS^<~[xp<29t.,g;ӏ=e|"E č#R &?$4cB} 1 iݮ@<|hNC˛??F =_y( K.[[2 ~J߇e/["yv5BIq *DԐg .,`kD/ 4#,QVg*ŘiIN7 g Z*GI8!釁vGp[#| UrްRM<_}5RhܸK6IkkpJY`]=B*xwzھ%- iB*Edb"O$Z(#x4 *Jri:.@B*+HE '"wHc0RP *R+)ͮ\ѵ5'}K*Ma QuVb$3[Bu>B \WOzeMRx)8 ZO ǧ8ŋg<t:s<>-9mA2#l7+lfɫoyõv*͐z)HքA>i>4t9b)novdyvișH*5$."oǶDJ[vE IHN݀w&D2G7C`nrC7q< CUXBx"`D =RH?{dY2G3ma$w :z(٬7J44L:|ǯK??wo_on<vC\|x:rNHs'/ɶkڡoZjLt-'| "0-ijHϞ IDATfYQbrxp9jv7dyFU$I4:<MtH4繧ew;J~OG$apxh-_~=;HūZQ*~`UY)L<<>=_1"BD& 2bs}bZ+':7i M[6F6iJT=#eiGHe p[bXȆ2J' u~d %2͉ <-7xyO\<ϤEI4#9a&"H f.LQl0Z"RmKn69t4'TPxzzVL77a憈& AR7bm6T:-ta HqDÒJ^VYjad: l˻glʊywxVG;?oys!8Bf4Dc-#Vܬrݭ鑷3HA ;֫5?яm-iKM;o YƤOWL8xpmgcCU:n0ϙ匰97/\ojQzJs26 ՚[tbAP ~Ubݻnnw s,fnN<3#}ش$~Gu.#y"Ɉ: D<%y v=u2O=! ,'`BD;utCG$Uٯ*^~SJ MU{^yó a۟`ng_Hs1xF(W*Yk15ۛ{v#]?0y0yUX5zHmv)8gqbrmh E>_TUf)u-JݧqGI*$cj{BAJfX,]Dnor46 Qv$I-2W 16%XrM\_10#$Bbӹx^jsXF*F?o JsFuBPe)wgh]0fh7yBlVx6ctmCSw[PoilDnv87"`MzrB:wu8-]bӌyT+$Y濳C?w4,/O?o6h%45IDS qށ 5ocx~@Sw vObSel<6W"3լKKb Z4mrn{b }LRd( Z&1)mC"(̊Ǯ3nWyBb Vʴ (tC1 QJ&?3{O׏L!7B d ž0)xD+ N*ՊUQ I<DIj͎*K91FTNyya\EU2"F#I.ٳ*7#2ƾ )(s ǧG"MDVeBj,MhkܼBD0;4EecYFf)?B,GPN36eJK'w06~1ry,5|/1Jb#ŒP9GjrWfG?{ɹΞK7RkT\2gQ)VZT KABo$s&#?Ӓy8~[?Ï~_.ogB'KHmh%[?( hgbdF-VY8P" gG%O'|$L.iQD7ttE |d^Ct͙J›7oxxa&W߼.-"ZZM]SU_nim'G._| &"&2fig7v5z#ۛ;Btwy<.ugTiNf4݆h5EEZ=Njpԗ#}?3-Hm4θ (6)0#]QHzeH㵣'qdvw-cWs=>i8"-7XҔd]##5 igwEi ) E^d^^8|Q2[2gٮ ޼Vx"w©FN7MLSZgjE MخTdC;N BtÈb3,Vi 4ƨ%g$Qk2>d(q38 1 7Yi4ZU U&JMiǁk܌bUޮQv$rrs}-(|pM8 {vjûG;dBDOD!+f]"BdZ{2mhzmxhd $!KJL8D$&a :Ʃgt.эIp׆[?/~GB+ff !@K.4ql;UU "*+@dE9OIʵ>э:J!@&O q ^q8GnEafeyUmwy}G^y ( CzCsR͖2/eG(Β%1y 7K]iB(pm?BK- &2r=H2"95xݱ*R<,r´^P.c$Ean)52dFD7 ]C?uyL^122{="GJ҄uYLcvS$IR>>=OYQhK@D<'_}5/のƉ=2-X.zPݞ,7:eg|i1IFt$9j$mWLOgzNf}e)0ZPKblWWo=Ӂ 1 "n9|x<+L&E˻Z0_QJ\ڎ{a@XjU@W_s N]49IG/=6vDC"ae(YyiX<()ipSG?ۯm+J$MÄ?c^FF<*Mۖ-'cy:' 2R2yU(,!"M@ zb ㈑r'$%8'rp O*ӕyI-1z"[<1<\ U.M&$y9HY Xii+Y>>Yը0i5*:zra8&j8LSbʚ4Mw;<>>8]F\+<'ƀmpĪFM`y|cXLtEASmIK\G|GXѯ_2A扟}'4>>q8\(늘1ƥa#6 4˗TFsJ@UUC%}.h|Dh-1%,_B٥[/ӝ;Fx@R9-9H7#B0HLP7J@  MόS4CLHVB%L'/+t?,x_țf龏mbPt4u[GeJSjv"@7deb{()@kĩ !MQ"E&!*-jOv^2A!9V V5^xvvh?A+9# Y]F Y nq|:?$rEUdB:Z1Ym8y$)9ĸyBgfYi6"m[ S `'yw/+a=eQ $O/}yiH^\oV2R犐@o=1E3Z`m:2L0-k$,h52];}YB _7ʿM^6C?,7"E i2V튺*ȴ$ErʪB 0QߑiA[Z,A3`mT%)$ꬦGi2l56z;pɍaU֋&B3#}m*xuG5QFV/KtR+ "#%Ls{aU kl) Ӟ^󓯾pav %ASW4r4UI-v,J>|#Ef5?zwo-84k՚n( ͫyeǑ2bt "oߒBd0Ydl#Y ayz$VmuSQK)%Geq<y)>8>.A*ߕPk-;>m*"CKX7R =!$8[^,:8&5mQ%h(RT4Uze3ˉfR"aePi$E^}f)PWlT"Cwf6iF箧núݒX\]_6-1`lGd|󌈑ft|A%$1!eb9512+J] EOє4b. vsӂN32S$lW-2$vdY|! ]lJUB!xRZD5m"A N %%B,kCwH $(HL0O }+yt9!R+MKÉ8stFHi"M7W[ 7//^aP*4mMD/RD &%KMI #hAVOwP5wI$2czS(uNݬ0U\ݖ]"Z4 XkBSW5RHl-|)E1@SWL1Dr!%ɺY "'P0=Z'n 0GȨzՒ<[eGdHڦAXTљ$D?D74tF쟎Ĩجw~"DYVUMۮI,ĮksXkfG7,ٕj@򬛂MuN* c9v=tEϦ.!E縸;[V5fO>}S~okqY6"39!Edy6GDL6?u<N.g Lq\<"C 3s9ٶ vղn2LqB)20x0he/Ը,2K}qcӔnYD)2V8Z`.aF7RM緯heC$K2UAKbX*N&ù<>.4b`9uOOjǪi#ç{nRs{~p>.6@V4EEet3y4i)-,ҠX6L"EszﹹH?\NgLSGVUCR! <* JR+Ȼ{:;%|̟']/οo+7 f뙝cǮ\c]sy [vWHBBxQc8:Pv U3æj LZOK q؏-K DL%Η0b'Ȧ]4eF ! /2Z| ܄тL ʲ$YU|$ "F_VB&L1M#¹w$$vv2"6 6 4_}od g/ԹKPBrE_x~`?1M,kFKUOHR <_ I27Rq0eFY7W7 xz:x:th4( i&SS7VW Q8;sLR+H2pD·_e"[$&CzhgN]86kBq٭z'D 咙1yZ3N3SpgNZ 5~9O4uM]yFYTK4D7valNGsR$!- !ogXRh-xM>sM]gRG*2gK@;֚2˩r0Z~3ْd>'JН9iZ+k4pz>Әz@! #3sۅ/Cx8RAm ggLDdiHCPeU^msm4_}MH9is #3MCcw!@d\_mڐ Mf /<I1KpdBj 5fG$WmI&=)Lh#QF:|ZrE%1JSw3 )zG""n&:t$ i[Tc?)J-9fpQsXTE5QBe+.Q%FH4]\_8Og84flYg;.ϫ?_h&`I$|p|[N'-东NhvrѝϬë:LSՌu!Zg$7p^Hᦁ.ȍf,x>w|zxd՚ $QH?zC[7Z[L 81~tlՖUS 㞲thm~Қ;޼IDKçgiꂺ|v#9ts1FnjbѲĨiChFgxy>e/^EX41,0w &\]_S@%LY!#Be<NOgi(>KB ̜??{)(iɲܣ2\R8؟F2]bYD:/I*C"hʊS H1=UUC/R`r9Pf7E. fTdUU -cwƛ"D2$& RH~,s|H J&9KUG;z@/EFgsĒ֍}Ӏ/?Ée5%El pwb3|d3<u*ET"'0(膙`=ZJ겠N\oY%Q \-O{KhmXbrCa2Z|pp6ME/4ZKIJhG(F꒦cgfA?yagKLGY6E-( ljihQ%YfHUUH "H1cNF(svJ 6xVLY/P)2º~wEfҝ=m۠.K))A]F۬aF ~q|4w3U +Eze4)F&E[6QJ#La.;9:ǎ +3v1C:73gZ?>aU!WQY4eر,dphFy࣠zZͯ}- 4˙~#"hy͆}.gLh6=!גHL|RV9ǁ?LPR+y\mv~7wzЍ5Eaxxx@sʼ𲶔TR+bN ]00O#뺡ֆ +rV G~!*!`d@%OST䉪4<<<-aO$W[\j k90Ng~~@ՔHSV'ʊ=6! sul;V5}7?L3yS//Xc^VD+ -$46: TSd@KuQC?p<0zVdR"Ęt=ˉcwOarʢb(UAq"/Iޣ +sg>٬y3DUl;i, 28V[!$!90 zu˦8=O5 g zϿl6gpusKU7OgHD;g j@) ÄuBlWh)/dLPƑdFDvW2O49ev!X-rudT٢?,2,_~?zݝJ6?_o;cO ֻUͶi&Rdڍymzͺ-l6;n_p'HUXѲj*^vF\mˊo?}`rK޾պ)3\H/Feնxx3H?{ƞȸ[nVh9μ&/L<]F:;Sk .Ά"HYt"&Jr{u4cKC1(% .EK(^EMfrHDtਪrSDRi\ZdR ԛ >E8+VU-B/y\z El@IlY5FHْʂq^Z!ԋ e|2ڗBˣ5 f[91\zlj)xC`=as3ǡzVRT:i?٘uXHi2P9UU/8tD "!;L&׻W u#,Ji3Yq/DFc9\X\!٢FJff[gӌ'PBH rqFgyeGqXluN]B팛-Ib$3Y1ysV 1q|>g%>susЊ\Ϳ?nןUBAnu!xtVe/g>{}G|e x{wE)F&;|>3>YMY98By@2Pe͆9XL.) TeN^6b۟/ Du$glU"%*xy|9S?l[ooY*6 I%>t<"ap3҄Ӵ+o_cC!.{27l-[KO-o>{EUd" ^(abE&VeE5;RH J%@'gO#6g0IQMxQd"1,Lf ~ć% A$PBRBg ę,>,68tIpy>ʼn˂Ɣ$I22M7 H)kufɄК(P`GnV;@#?[ Gw4!=rf| /|`:Ȩ8=Xo[򴛚,+) '$8*r#$J RDl^/ʐ>*<蔖k2ǻ,Ae׷׬˜BJ$?->GFj"Z{f1A[~Wm.`*-8#' ad" Ѵj)󒧇5a'R/۪ba{떢n^.y [04Mg2d-F0@,yr06[0tx$)fz0kVdz8ul+1o{Gf??~__~͛ȗ{go)ʜz, CJEծw3KO`U,=x\@kN/~Ft$.T)PДuζɵYE0<>K8$*ynow ?-<4+-#a$E{՚,~ǻW7|K?D"-WͦBD U`y? &6:L0 w/JCe "`F$z'I2vM+aY%{ȴDQ"rG>$qf9DLoA kPTZsjMd(p2'lrYp /r8ܥUzŮmhfUelW $Gz ᜧ(*(-љY(xnqOg*S\^ M^#fN/$Xm`-!8DrXoV{[/͜i!FѢH)Q岡3?\o 1%ב\5Z q 묢l*VME N^ KB˂<+Έށ)&HPitt2+id%ǒW "{gǩ ĸ ۲DEKD)HHRu3>DPyNUz!S*1i 39s.mzֆJ,#cGkz˜˙"ZW^g8gyk7~?o;= => ͿӶUw9S 5>=pxu} ?U &g߼g UZUUFBrh!:+B8Hm"]7TYw'Ntvs[1@u]x:ef0H)vdP(#?c駄wo 3ˑ ug3 x{B v]˪ - uK#$Q20L U]|aR#n+LԽZgS(@P\ $46.s) ԕA+M aa3#rtydbY*ry.8_ D1xy ]APưm^H4Cp*ںƺ+@ʴBjz %F(XG$t9!z7-i&| 름qYT$A&3-y$X:EV0 PbzD1-_uӍ#WN#c4y2`rÞ,gǥT 6h$M]aGgl7+6n Hs/,F;O#-ȼ.0u4tr:) 2%1) ^֌3;e&mi^޳vuffeUBAإAXvr Fx``,`FA`OͻO6 Sʹ,#kN #4b/5UY3x!TH`sO?,}! pl(sKfdL;DԶOi-i`g1 {v+V*u$%8M30Ah$Vgh0F`dqRIq xG]o_"Aᖅ7Sy09DT4DrkyCS`ӕӥؕv=1%a`gb MM*u|%ُ}ۯAI~JUWuͶ˟_S} c_`F͚ ӊ(e!`*)iaa[+ 1@0]n[#]; |Y<^[/nm;q,,ʬBg6MEQ"䊧w_areԶ(ڐH'RIp$)0sdL.<|:#1FRug8. fc2(<)&~BϡiXG5QBDIzk;ԔF#Cd^"|<]xʇ}MYdTuǏG1Vb%uV\d iajul6'+֛ӹg"C|ZJJ7c&LmMs%K?xCHm^f~W|pi{Q{yoFR5ݎXi )zxlϔyɦl8=]8#]XM6ž*Pvgie0In92EЏ")i4h_:ֆ0r:wr #T R"01J,&3QeݞB(Z?iKnKQ ӌNș׏a!Hb0цȰ,ϻsI!ẄRXn^s'Ն;ʴ02-s6NSH1Z $g^f`=uY햏>R2/($103rYMZdf֤] n^0#pSBUD}8no$~2YƁZʼ"4FbRz"j~Oetn_8q6"#%B2JOKGόf9Ѓd֮g(W^cLL2\u $oXƁ*4?/__:?cݿշG{YA+29,i{<=^s#ʂO|3=UJr1w\=1!<4rcx}%yƻqb-:#<ɨ~eú-"I1HbRh^FA7?$áf[״]GVomwTyoַr_zꁝ?HMY#i uEJ)+@D/[HT" ėo~AFyxš |݀O ȑ²6sKKK&uaGwG[Xf?e1tJQ4-QEkcB"Pn &7$'.0YA#/ lMF|xxBI6/),i<=b*_>ޓW;_G]v#s;`6Ȼ Sǘj2׳E -m/ 7Uq9S7^xOOﰙa&ؼkv{> &3|(AFHz5Xfk-IlJɥBifSg̘UN|WL46"%fcV<2O3ڲt> i9X~aKz$pR|@Hrmk6d DžWwϸp;JZ%!Lj~6t|~iiapzĥPmUUݐkõ=#u91i 5L/+PSVkKQՔҰߑgF +u< gRno{T gL7y"\ND/"(-8+C3die)|(E$&R3~}-aaБ B1Gi G;s6E0{G7 ($?'АLn`$Y42s\v&i2ǨVk uWVħepbiq땻sifLI.1me(*BF hMov[◈"]aBDM3EG2V[8/3Ft>Q5ôov[\v-dD=nv\+jn%/{7V&Lf馁%xqiE[w޽c^zr%y$8(ɉ~BDH%xBB(9H7ul`p.&O3@ "/w6{0D Zәazƾ/8`D˙vKn2޼}C? yn8]{H4U@b@ )% 8M:*B A7t$V4UEH!F9qۖͨˀc=HŰH@ m$:SdVfO1Qek je\]0 \*ϨdzNC$%Vy еL8Z%fE]6ux*ϊLPe9QF8q!bv2r7uNSd,DyHѓ鄵,qu$Rg:'PWa! ޺)IxHK V@@TE )ňEiCG)&o2;"R $H%*Θ_o~P?7Ɵ#z@U%!Dž8.,Kv[9RO#>#5kǏKz͛wtvG]DD%wnK2QZneYf~;z#MQs^g.+10hfH()F‚8ȭaoyD`Uy4=cUirNWq#/ܿq<-H w{ }2RLSÓq>"SϦ̱a r˩H!r==+-F+<89t]|k;͎.fJ ²$.Cyea>|Ǿ]|5@ێ4k;of,?rwH657{v-K5#swQWz=b"7 ˪W~4OL׎yxsZޛaIpvE"rb5kNetk*3[cd' !llpmYJn5H-<)&XJdVm-)FK4Τ$#׮gy6!i VUXR;)#42O6(eniOGa{s5:RJXؗu^ 5h%q`叾"/rJkyz|ԵizdYa1u-nI)f% Iۖ*+yqȫMQ0ѝN0yNYT! ׾C8EBSV% Qlq9Y 4#%u]اiZlMe+ٱٯXi`:vՁp>"UBkI {@3)D`FRZ=!8].G+VK* v7 >RdY&ʪ$9~)MOQ?ب5m!V 0;tfrMCd^&HyZҊj=?^WHi:vD븭"=2ᖙö!9Ov\HR?lݵus; k`ɓdYFf4 3%u#e ۆg["4;Χ||:!,)1KJfI(%9ESLnIRd 7 )&5ZdTρi..y:$%0P%Zh~EX+5Dm{¹nk#FXi/Ti)gӿw`=c7=,.ըN#Ӓa~SJ":Rm Ouh8e7o߰3??ʚG-P:C}$%, #^H2)iȤFI y$aʚcSe~qac󌤠:~-~u :ޘO(=maKf2>?b@aTs(%Ƕӑ}s;2ƈ)6d&ub3UXΟ0*iEn j\O^˂}Sk**x KM<<>qPb}Y`޽}zdv[nw;l]T1(% c9_Zy* M * lrďMJ|dWo (o/f9/Hܺ門Tqob_Rww|x82 6$iYfÀQ̄(J疲x%[Z E),{~f# e^<<4UO~5Jkvzq@a-!z LamNuWֆr#uUiٹ LAn u%:2_Wږ)0,3UYnaC./; (ʌ|d:tȳ  ďm,qGÕ_﷌CnRd|7!eFJgxpsvn09\ Lf\-߮kw%uU=Zh4kqqxb;FFe /}P4o@ !yꦢ(KQ݀nU!̹HYW|hmϻwT͆³[hɬJCiXqXW햡⧑mg7+@$2Ʈ)2iAD^{1Z0K7CԟVN|=ַ4Jl.WB$7s>#"LPf*f:ڮo|fHAn4:JAc36uiM\/=B$^ܬρܠDsr~dfj 2 ١Usb""WUd ޭshecYVB ?[ ہ_s&l,2n 67H-Ρ, \ҮrOHqKB w~YS`Vxe^xw|$)Et"%|\C|Ox2 uYSf!.%4lʜnji6*DR\ #3~#V*UAQd\.ksT[LFr=hg ,CJ|ZcF^ܐELMSe{J2 k++)U n2rk$d".&$EQQ6q2pgqaR9) x:gI B873WBd֢Q!YB"DȋY(%y)^ !h #2 nD)r35ػoL?NH$ny[PB++S+l! )7:g{YFYWc's 2#{~$i~;_O޾egl7,D]<{vQifGU7$ixkד[3Þ$`!E ~ә]PRI91 $Йa&0#C߱mqntC\W-fo~e6E6͎k&$Gӑ~ɇ7lw%WpcYGx:"}֚,JtKG5~,6LJ#iي־N #uJg$lE׭&Ky\yfe:.D](˂aq\_ob@F ! a'0s ۪@M#źb8/ 0M$c |zDɒ`٢۷yN],gZXnFJwqYA-%(UFS( X_EBٔ%&[CqdQtbnGwTۆ)LFCTHpX.<%U0-3u]sN~i~MkOOUw^FyEJkZ֊~O2OOGaO㑼4+љsw<~: lQXx2Gqb =-iXsO'0l]Kð RrW3mKdYR,ipàmno*nnsl&X\ΌKdgkGnEszAfm%DSL2N<>HU4̜NWqd7ۚ]eɳ<nGH]HISmHOG$5! 퉶sv{7,K ?E4 uYRhN2GDBd)8ļ`Bi.H k ۦA5<>>QU%e^b 2"e:Nhx֔okF?ХH@Xze|x 2K(4{nmͦ($"!FPl>q2MYA\ZjD F(B8fS2LU }K f̀Aes(!JՄ1ZV hlwgeaX 1O3VU7 ¯~g?)WxS+1>ҷM&'GWmYH 3 LWLr՞z0c{톒aPy<tzb^ c0"qr =,-,@e @oe 5\7>/ NbXrJw9t Kd &¶"7< r' y]bsGuHU3dJvW?GZ4$ě?ųWܑD/o?MxvА[޽Gt! ]T8 aKJ22Q6:8Dw=ŔLcMQTL2U9/TҐ|`LJq gCH/ln5۲YYbʂ4L<~x /AvME 6xy5L!x97U2޾OLno6U/W̘CB)J$@JX[23iy(r3(MƦiC@EY8D^G)g7wlMSg&b h!mdW>oM P4 /~|շgG_ˊ`N.De/0y$PXCn 4-*qY u%I^ ./}C͆W=-*ha"#Gab:2*eg]^P gqDxmU-2$S8`n*\J|:[~#UY1O bYhJt]Oډ -VIvUEpׯ_B@DUX2%ɄD+=}pA%ndV[a5W>QYr`[!xGDt¬lmŋ;Eǧaxvw˾.ӥcp0M^P疡ܼJʂ)-V $2!R8͌}O$~Vjt~ YS0 Q ȦnxxxKns\>A ~'w#$3O!7(ɊI׫Ge 80 bWϭaTh(I=2IM&x"/0͎ŭ+<˨EK!лĥ[MZ8nE({jE^yp)*++d'Nm2$$AQDB-Αb`SE}`yvVckh) D*J:/ljܭXTje48Gg`@k׉~ʜ(tF&4⶘ȲZDD_ ߬2៱~{,+_WMy#v[DƑ~u ȕƍ7G22si x9yВ+˧ӑ_+w[.78hYyA$Pq:#9pf@1E.o?C} P?;evm˶3*v;)qG2D$VHJq7y #T.l!%NG)v{ճc'\szk1aߋͪFĹ;я#crteAP }km?qxBzC74D Z)_=8ZH2M .fR%7/@J-Rk١p{7 OO4UVq:6|;ȋ/Zm y֗[ڦk{wTUƺdF`3CQ@)97,*BSsJOn3!8L?>xqugf/*/y<8]XY)CuUdFrA&=@$rcyzcج L5,s’RD(D ø|WL3ȠmsOdV[`bpV@jKb 94 0J#\!3/^_38r>) e--BZeUXw$==1U)Muc ]*̒, 5))[AX+|JhR"I()rDBD6H41Jx"97 )DpfTh'nbqw6) >EfҐ5U6tmDb:O^k}Cw:rPHcL<{&麆=бڭx!3EQR%U߬3͹;ᦁfqu[BD C(̗ٙɔH󼼸KB('d99Ǝd4*IBUkSPZ y"84 dJ] gIxŘ `:lc Z)B@j͞mR3͋Hf .z< l@=Kp<#)xg^4N7~7/QK妉Mb&v-]s>(Lo? 882 zgCǑs}#P؜{Tqt<̬~w'Yn;L]h1;Os:#G˩hc2*{874,j6mx<>,BQUڲ-5_ٮ+BL*Ţ6g3w_c~S ]&em[2 mmgm7UB`|. Ŏ1MUX #c|t= Q<1=ya c<#9OK#͞6 )L IT my󊗗;Q Ļ/|<\ٌYo7^u7w>gNʏϏ?+.V<~ HrU#fܵT2/Tf9}s@ O?vLcSG^ tȌa ㉟ xO5Zg|88?!MBHI64V$°tV4 =2eLcbp,8-;4\\wUΪ.VA@hMVdD|Ǐ.rw82UV`&1vO ]rZ8rߓIDjKYœ) aZvkZR\A5o7 |DH8?bfFD4lp#%yzw[eUjagp)r{OQTuMYD軑i2`@w eŋ+n`Zh6d01x0q:˞C"BHKG%]HAt8#0C)Gsfz]! ;z4Ra3p?P4o7fG)l~z- DD3ıhqDjZw-y Ä- Rb\%^_n)(唶(e%`fCuEUoyŖ:21$Ԙ|902=4F%JK>#~ږ/lkh E`rmɋȐ24MC]gXhO=}d-AElfQ(@f T<)0L}+U.x/0Y!%:w('(d ƺG5%-XU5IAUFRL0F+Գ㙦ÙgEūoO}EQxfl[)1:y1Yv#DZ`UVhAm=yq~[n5{4Q9h Q QX)Y%/v[V4cp@.n$sXUe$+rHܶP'3h/!Hi"(n6[~bJ0$2 e]֬YC<&hȴeW(l mY)fD<Ȥ$zA?ziF(PvqD:~`9DkIUNI"Ʉ 7 .KLb8?u~~lso?GW;G,  ?~UU# ʪ$)#QX)5ǛcaK.?'%<#UNf4hry_03~G("ݎݞvKs@݀A)ɺT,܏x0(19Ȕ&e%/._4tf7-dž1H}Xq!p{$'N]ǖ}/EN? Wxh"MwnJv|D ra* eA]}OjUq8 6$Q)[\ s%o-A ɩL?+*J)ʬ&nXe/?zMv2C4G$y^V<>%^3tH*/QeW|;p@wjY2Gj_v;$p} SfyX6Lcpn(l@\]oǑ$B9GrC#Gf%M7:lU"0 d$'M$9G7xS*jEE4yU̾]&,>9]hLJ'~n, @5\ Uhky:p!b $$Fi<<0@VyX)E]q(? /Y} ~pw^tjgS]ORι9Q6<O<>Qȅxמ9$ZQ%]Q*SjC܌pܜ =̤x7_tnUdaYgZ.LөmzIB3v#bU=IɓgͪƇ*ab3Rơ!3ŮK~&%Ŧ-!rcɳwO|;|Ep%#UIUY^oSk÷|~։{Ng֐g9s>⽣0|'%uE-1)B0Y2#Q$paBhx<>>z >: sdvFb3n>CktVCd&9PRo}-m?1#q4.Y DPryjpP!!S/=R*@0MnAO= ݒ)KBJje3/,"ڂԏ4rŶМ"я J&vE{.{Duj[ IDAT0aff(*RmCe)@Ŧd]Za3E? uVB" ]t8-~DRh55*7!7¼O,˨.D LCQa㻯KY4A׿\m?ȍۛ;y؎lw;Ԕupl' NRa03avXQ 6U~{**x7N0yyA6CO]4mn*4Cg~L[|z֖w73+Rə0лM׉ ޽go[oyw~V#enخ1"Qnilj?ѷ W-o>zCVlk^^_Мğ?AW/B/n=ĦydUV+DUԼՋk^SB~]-C^ps<ɤd]ָ;3d]T|+C. i{:R(4 V]892e1E..^\t-).6;L\DGxFt]=\_\SVkX0Zp:Նbg"H .ԜNgnD"(ʜ$DQ)fN3"82%>aԴTYbAW<141:13jl]mXU+Dq"Cױnɳe @{eQ!@YV8ZЍÙ*bZS+2-z HA]XL@UV. A'xZ,~b ȫyHf@b)$J$\֋Ms(HݞhB\yffsCsBZzU7oir3"@"R"8O- *GBZD!R:0`i1| xJi-kLbfhTՊ2y=WrF1H;dZieΤyX(I)!cAvIڇw?|o{뿺l>-e]дө!P*ЏqSUQp> ab$bVn06tb aٮJn>TUEY-6&ca]_lI^- )(x}Wlwnج1qz{>Zo臑YਭՋ`S 6fS(0p\]nHvS KӴҳP䒇'mYQ^լl|&rP$_MK|bjCmt<#'O_p:/mDzC<=t:s$KO>Ŏ3OPbO>'i3f1D¹iHn"/^MK3&̬v;0I!*Jw7|y<]?1 S$ӆo2;.v|>2zTd5C3aHѱ*3|>=f&g]PJ,O5Ȳ wH%"Ϩ6xNS18COHv徦T\Yfxp/cM\SHjݪU(Y #/Ȋ0DnnOM|`"s3#ZjŮ(嫇bTv%)߯20=R+^3 >#SzNeXTeMLcw}O@LOJ:7p@ \?LF؜8Hv#R4U+<<o+B)BH(Ơe?8j%bi& ¹)K=V$jm.NVb0ђ#S'^ e"x7%Ya瞩8u-Mw >˗Tp:ta>`~=Zy<fJޮۧ3IJV^^]۷?7/{} ֿ߾BOdž 䢄6.R|)$Ef){˫=Uf|KnsV 3st݈L٭+.yq&&醉~麁fE&=ۚݺg.sS1x}xdft0 #Jf5XBZSKn8~-=L1[U$fq˱:`FH4E~{vkwDxruQk{cq\Df j8rC"' ǖ%kúh [|a4L?o: MSB?;f|@=n ΑdD͡.+NOGUE)EqBP$RЍy7B%V,"{Zy yQrӕzA> ^H2Q Qh@^TX-ɳ. $^-麥AKO@ HEe DG]<6-D](Xcr>DaZTfiS<'2zU`I wH5@#86 ) Ȗ ^JL(+0HXR ʲ؈Ts7t'G?NL@Iɦ䙥{]⤙"D6+˶TYA7H+C"/ony8rn/ɭ|&# -JUQ1g7e\2K]hOYuE[Ly*æHX-臞 Q:^BFxxX(! SO3BԝwIG%7 /󿺼|)et=O4͂+r8ܼ(Ͷw>!O#c2޾agVE$Z)\_mYu{F-8[l>52lwy&bR1H~4 TFVe3<<#}St)MPQp˫Wz]|-2 Ùyv\_~x⧿j`Dqsgb<1OgT`-mJE؞! 24^p_#t"`t (OkW}@ }Ǧ.Q9x#>Cڌ#  5e^kJ"YJ AH7dYPdH2)JD0̀P$zE4s⦞b1JZ/'bX Jke XZkI!H Rъk5H#̂yU64Ū)܌4 "QD\ gk+3qD,"2Oi)%Z. OdbsQZ[\#cUԫ4dRL!a" KRDHmj9UrC#nt<d͈G6\J(44*J2'G3L<5-Nd֐R،ki#.D#0}7A%?߻v=9rK9Z(:E],몠{|sd"s\6X!IK>Ty !cU%n8X9޿ū7xMwijQlO1\"#hE7&s("c\2x}_ޓ!*p3=8rzo) buu>Ep6'?twMٮ7cq!pn})r~Goߞ_WiѿnV|_)HD!YFh2UdqqBa k]r9nwk|9Q(mq3ͪbۧG3M9/\]]h#KyY۪8ul%E97-oOH!0={P%v52g[i+L]?Hlk]x>qx0=O+4uquA)"#)&'K H<4gPO{Rխo7y̧LNgy4PW%R|xF`*80EiCӝi3)-{MB fs86g~{o[wVw]Uue5r9!!"H! ! 1d13&`7~{0 "Er盜TgPUgZ~C?/<T5yQpuv|6 c57wtʒqQFkw~-SsRkHlJj 3G6%EY"Ts:ĺPRkkyWT<},1{̀ӥaP 'uEOTEJQf\- q]Gd?0ZSu=L2mRkq!2Yd R iQ*4D@ aG7,X\c,q+Y^H5srDuM1ׯRU E:LÏ>ݱFηV:Y H#<-& Q~P5!0 sG$Y577\N-H˩IBo;T&e G+\ =ytʠIXܐZ95E[XT 턟(K?~xW> ?_))>JZ# BqLRbtmH4E87pS-*HYa#y8fM\vOG϶q2,1*I&(˂250B?_QYjVa$Id IDAT>ؙb!B#4^~ | Gȧ~4MdYcK-"e$^Nta$ SX*ϙgѪ*Y-G.}OZ:O#f0J3t-$؂2/b e-HՁ~w`$VH2a(t}w|4+Ÿ|ItOxIb馁,0:E x%)fQ.Q"ݬCj5usdz$,OnI2#zs5='zò(RMpDI{/<^j*CIh3sM fy1ϣ\ʢBH3s""L·>xeɹ!N,,65FHvV+\p#\#ء)iGzqGʊiW0Q4ϱokK\YIdEnċ'|~y_ )_g0#.#rơy]nr% DX)J ,wH\ԨH$K-NH9NMcLZ);ͅѣM"Xf)Ua Cb"6ӷ# "MP7-CӰ,3<9s/DDќk@2P/UqY\U\ꎱue2eY&43UU!c}Z J?>8!Ш\csEju{ >Y7Oe$(eh>@5 }Mf(P敺ilVidQL1 ʢ@ӷZx)i5hRy>:*EB&\R0 a h &QQEwkx9՚D}\JE&?2904Xo DǦ(M&C "O3Tu}D&,4B؄Woz覉h5^D(lFRh22%ITBVLL)mBa-d x s4'12/Ʊ)JfMn=],+DOkF(;J7'n6K>xT&~/EvK6LxSK" "]e09")ZqZÈ42Go|Sy_ )̟_3 rԂLEnVE @)ѣ8,g$駁ky}2| sKߏ(!H`]U˒vj(JC.5GΗnH2Kb>%0eS?! 1NHamJXUNfh[K!%m7$c/w<Nt@`<ج3>|Oa.R}~Fͪ"xF2/8,Dp~!JD?1Lyk~|ej^!:i|Xs;5r˳)%oI%͆/ ,y\y=C-_tGtL+Ղ4MV3n97FF6 %=вs\yJ/{$*KLQ- r8G6$Bqsu,)PJC넱m""dDٌ( qbK7N((2 $!F3Nza{n;=yVC,KX74 1BY1VP,RtwÓͶb^0L'T`擔GNl=Eۓ[$( iG8 Ihs[O?'z2Ma@#ڌpW+k}A/xv%S( Փkxo#F l!wG v`U-:c*sJ@J1]K="a SHig԰٬/s>9c3p{K ޛw|?Ÿ|y뷶~~|c =g !д=u#"UV5iqa J.uCU%Iھ'*L4)5F[y^ ob/'ׂpQ_Zeix hy?7>wo^*\.OWhlq>ڒ (0&c'vV.UJf柏3bAUL瞩Y-2$y4Kӵy/ CǟÑrBz&h0yRXeg9EA8(%߱R (Gqt8w+zGLI<WV?9N?`""?]LᣠMG6YCD3jKtgpafSjYY- "aIDK:GІkIme&""Q5OiXoEĎ#UipQD|H76-ss)yFMd&Or֣<;("QD9 yqu#hnx 2k=$J'ё90;yxGgQz@!J͜> LP"A$IbX]]1@ Lu NN4!DV- !P.cXVt.e ^@;RM3f~Cdzu8_W_>;;l矙Ÿ~oVy]ff6C@蒈ap8火ӏiMUXmo鬅}/>`1YtHjVm۷o F֫c4("ry8:իdֲnUQyy/=7~Gx:"&3Pσ\uIVdLޓa my;s͚ mc!"l $iJ?y޼gɓgsK!MJibK)ȋb1?Pj]ugUf ђƑa[crD㜣;nOX,i3 ʔ>˅L7 ?gY. wX9ϟܰZU(k}jֳk"-8. ӄM5Ţ@Ji,Kr,A:ZQbJϥ>\D|$ր<(ӌ׷ȴ@kLJ!M9 {O/~_~ )>̼/~ ;ǟ0t~, CײnHaɭk| tÀ 0bkZs%t$іjsi\$zKt8Y7O.^}˳%7^FƾA ,5Y$0˼0I K4+/guM} qjLǑj9l7sYK آHG{:t㈑">MaR)蛆n5~riNX- L|<b`r= |c rě#ͥA" Jr9i뚠Z,YWK ÞP,3ri1ڲ3ϗ!4;hkуKF >xv؞i.F?`)̦$I+zG者$X-!${m,M &N{s_I钲bZcdq8 QPUJqO ,(7'2Ho9k7<{/^|/$i.JiNhၑ4l*lr<]P2x}4g^4Z$\lҒL?R#&x9js_jx< zwD癢C XW QX# CGXVIR$'?i~/>i2;7T4%QDQXXO7kܽ}R&Oy8Ȍ4Y,+ق(%U , b7+aK: F?ôD L@t!1v_sjq PwSM>?#ާ"03!Ǒ4O"aP YdeP/,6Ƒir&EN#ըB# | OlXRcEӎH2 QJ.MG;dEFj,icyn@Ԛ0H<gpSϢHV1Y`J53oۗDEjsC=GV;'%aB&lLxƒ),RMw`#j;\_4}?i?e>Q)vZ'Or{=FJVEEU.y{Ñ0#q0Y64K黎,Ihڞ~%>xyۻ{h-u?e)o_&J0$6cj2GfaϩnҐ%9lV4] H5Zi1HF Y8i/~ W>7/("߾v'$I|uOn2r&nw:c?Ѿ4K5X4-eque6/ҌX+bAn^W)"Ed(Y9Ռ,OnõfBU|iDafDK,idGgO>~s|Y%?Hx|ܑ$%c7ҌKS蹿I7W(\Z*x&Ha"?T )$CߓjCt54H%D!) ("^<ë~G?f?yνc/a /2TM8LH)6-׷y%aXo7T@e -UU%&O\֨yx{18%1I2_Qd9B YNYж5Ȁ҆_p|8am~]:l A L"2 C3x$IP7#_|)sgQTh1ZGG՘L):|"1 :Gi`*Ҍ!PicY$y.玶W*4 Cȳ톢\|z|&8UE\dpY=mkl2|ۆ:w{'XB<웖˥mDzH| @@JAg\G!z5vKZ踽ojaDZKDz=6L؄Ֆu['N4?w]̇o}/۷"$~sGʠZU@Ih5se Gpn4&F>y|V=C,Vk6%&DJ*c;VU䫗;|idUdSrKY4g3t)*2=MotmOf,7dY©9- \k:b(<<>I1Z"#K%Yfڔ؝Z 1r{{wӛkD(A?N ~-JYve-.t=2Lx?`mJf'O𣛅Di"M9D5~n6dƑ7o_0_"a`Zƞ錈}@+MxaIvb(RG43G"#(J-mp-*F`$˼Dt=2Њ{vMCߍ(Pf)e^b(ٛ`أڄ,KI;.=R(`#.p}`Ӵ55r8 ؟j$i%֌@wM8$e5)-Co{#Cw"<|Ɔ/K ˑ#Kg4xJYp΁bZ=+ӇcU-x|8Ly%ׯߠL$%C?b")G-84gEA$Y"}p eU!D%nOkچF dBV; IDAT1^HЊja\]o臖(#!dZl̗dyケw}f??/ʉo?z4'ц=2IN=zr} ?503a%8cpD?x1b$Ӛ̤L0ߒrFqќ.s^};w~ߝYTjvE 9 r!Ͳ3IF=1#3AkA;Xr<4n7M`S.)fz])ww\]]=߼~69o:aY yAbvT)m?0 ,+>AsgӎTM ⫯I5,o<ܽryb]2/nќz|yϮpʒ%eRf9V8[cBEQ$lbKbk(Jid/a%$.!x N+&If#i0@{ؒHYd]ti\qN4,cf{=zB76>8 ѸY^3 pS5z0)6 |?{ܱ?QEFcg¦XcI,BU0=FU&׀tcRPgtx72Y\zJtaD9^XK˅ZRc躞J^]ӍJͭooqLR0U V'=2L0fRKF5 >e.3Qkʲ ˙<˸ڮ0z$l Ͽ׷wD)^!YeG&I*a8 wwy>ϿX{+o}OE"EF q5,c&aJ.@7 D10A(ϋnX%48# ӑd[jpH$\.J ϟ=fJؖ Thm{RLsH(YN,a(ӄ-H">NwDdيa NTZplI qd(xGiځq2Y%J 4֦Ώ3JP@AL QM}FYrm!FDn8=>0 .: (&I0vH|}-:IY/ 2k隆,IYVK2h06agfs!瞱5|`hRc1bHd(٤)E0@=\Dq4gڶǦ)ewC0@e4M)Ҟ8wlRpDOr!ji~iB78f@JKD!bFb>SW-D[tH4ۆQF$PA shn}zG)zf笪pEuŤ㖖Kӳ\8gы| V E &ڮa?0:fz; yRMT< E#Pq 4$9!܄$75E)e-w;vwwʰ^T,7\_Io=.ri4]C|'||ۿWᄄ}@(?i3AR1П;1ppFXccOX-jKÏ>;>l msNk>{^o)z1z>~ʧoXW8UKiƙHc.Adۻ\jc9Ƹ+\]` GBΤY\7ۆE1.+Vu%tC~a%jBC7p{sKPei1RRU%JD]4n`=R'6tb>*(hj)ru #~}k)lXyxxiAВVyFlbQsv UpqN}x^@Y7F,O/k{w>g^,TV໎2׏G !RL Նa!3a*S jixpǮ!rXQUÁy!H"ŇԍgJhe$*lW-~0BCqGi$JP~Ԋm8A0Ҭ~QɌ{6vf "Ȥ s{z?t\7C?+իb߾e! Cn+/黁j "i4=M~要c?%= U- N݄6%5{8eZ6k>th !1X\,Q!}s`Trs^~KSɓ Ͳ^͒X))?9 ܝBw,m`UW't9ugJex!z͛”Vq?^l{)(s1cJ]k.q9hc GA 4ZY-1Jp<yq1rNcy~qK)PU+%:wD̓Rk] 3ȾUC7q"IA4eIe &̡8{OI"'.7+x<2Mz˪HX^_7|?/?3Jkta҄ >zb*_gAS$в*0 hښjs#-8OS3z9$wjv H-hj:&74U I?<6\^\r>醉s#ёE$5jCS'8y2:3@JRX. % ivŘJkR2g{$31*ݔ/C)CC޼y[-Do-yrd}bϐJ,y4<~)fM:rśoq}yM]5| ?Bْ7w~sGeϯ_P-c$!jի;~0UKhVEљ8E.I[pp@e JHJkg:49#!.'_k c!g-},(UPV|(E44mn*.bw> ,MH!0}|)0*IURX=!F;s7$EM̒qr\m,-踹mk6\_l8NQi}yֆ,y/Bєxsi8~ w~B ܔ%͢80QmMK44͒jGjn(*;br ~{YUY1 -9;sx*kXT馉v{tbXb KYWT O@+Q %hĜ}&G!"?&aHe)$%Ow:#Ϟ=4D!!qn ]Zb$Q,"ܟpo|7?K{w'.6iR)jrD*KzBbzUQdI)S ۶>C/V É~p)s{c\OQfL4@Ą<#,S$LE@V3N6xOQd2up9qZ FRۗN=E+&Y|3mM]x5>F?jg^Vݑ7{GGĠB$H ,)$}?|@CW{8O3 xZ6BۻGDb889_/Jb2f֒|Bn!5RE4G=@IxaGʉ,5zU8x<Ѷf'ӉjRN2'n EݠSbY7Ku"g{ʦ@ħ|w#9$3MyPs8vb`#=Fk6y?8gҐft#!&vR EMp/e !qBB%I US%ٝTMJ >"BZӕac(-Z݈BFUC26x>ѝΜL(MK]pZLLYh@"!)^5#{p@؆MLV"824!Zn f#4k6UQZR gc*Ji W|>H7w{%ǁ<ҏ=Ol/k..y [O-D̉?3 e1=J1 / sDXAU$GOyG;^=?`K-0eE۴h%Y jNhg,XJn*S-wT]5L)0$8FRnw VJe S4Ri*5Bd 3sxPEI̼E4%#19ӧp}qA QDRr smC&XDeH">!ds^a aM ".~~Ѷ$K-+<8A di I03ˊ k(meatJqYܬn.7(~3''+)-YIj[Έ!zJiL) RF)5%uY"S@qz`.xOvBXM ,9h 'T,lE />~fuHSWheg=d9 TѥEd` 3z:'Gr)Bɂn\Lޑ D4ʠM. ڪ@&Rʠ.zq0q'no?'}Ax/}K>}O)"$Ð}<0FuS y#g2?;#|)>pg/^^\2cJя\4 k!$-5MYѴ-)4{x 9GV<8քS[CY(`B9'̌1҅0E.1RQHK?<x k! b2{)k$";ȉ3F,+>x۷wx1֒fӫ'N}{8E-Ee%Bbtc !8uEstҊ{N}Rs IDATJkIfލ$(5 kUuY&Oя$H qRRZ'Lb(Ȉ3cnj%y~%#gpJCe-)>FK0QZK]T4@9&&K7[^޼a?RV+ar{ $7oy3Tec{})K=)'4,ʒ0)2Eq`YH!gDQb!O3RurY3ĹwNcs.Ogϟr!%<7IH(bH%B)bΜ04 Ҳjۆf\ 2Z)pe G =Cc"z!' b ,ly&GhpXb|"Ӝڝp#}B(Ŕ#> \.֔Ɓ,gbQbq($s`j!f~$zDzX.dџ//̿}֝ {Wد0kݻDU.h;`#!4X|,6+)ѹVj~0.(+ tU2#UUabU!tחT5!%TY"#cgGtXu,nIV2dk VPhIa??|ߵ~{}^ūvU}g&~5B"&D Zi>x%!Û{ϴ͊^ ˚)墥) rq$f@)M+ٶ Mng#SLޣFh-nB+>Nߝ~<:\Jz1Hx@yE?zә#Rjr<.P-}с]>$'|xx8nۻd)d 險Zc45za51r{`ό yUe-h68L!Ǒ? w'Ng8&ѳQ³Y<@fMNzĉ嚧<[[)f/y:6F)鄟aȢ]PZQق"{ΓctL* \(ޅ5U=?O?|B%O\ͩ>X|tyF`Hq)`Qdxs&QEz&cp4HI\BLt],kX٬HfqyAmr'zuK%MUQ9aٶY5*DkQ(B]VH))"t0LSOEMN%\D HM#0Td 9IR8C^~/ۯP_盏s?bKr#VaTeRfJP%4"&SnO\lZ>?<,<{BBd)&\LzsD| lxHI`*_" 0 eN"m[5U!(fլIon"g\L.2=mj$P5*&Sw` ݑX^^q<8(I. QXdJb.\__p<>ch!%˺iTn*<<GQ7t S$ڊ I2g$J\Sj!iX.e-e ͢E(M˺F̓ktjg $SX~ /_b<ۆ/sF??w<,j~z{o|SѺeɢYP(?zVE f(Oˏ}8b%'ty<In@) S4 ۋUcpS:ݝQ5-?O؟uCPmeydϮ#o-qL0"l[W .W .Jņ'N}Җir)cxؑ,,f 7 FjZϨR*2zMEz>d:~ԫ%߃QW߽74?)gH|8!5qyAdE7^~KIQV]vbY^!h(iV غ~gs1"DNʂၷhaY,$+Г'D,O Uaȯ[)ޡ#ޏh4r7 9%s@p&lX+M5PjMLA`;:q!";Kgf'pXP:\~ķ>{ {ғOVlr/>X5aKlo_puyEaQoϭY~Y39uj议;4qb:' BEY!$DK!.@pB2DQ/E8!rlꪮ3YQ.?~Z~W{_tbkReN%y.?nv:erb ,˺2fCCPAŧ~{a PV0yX=|1 J+HuNaV!1"0ؔϽwavpw:"Ĺ"ϹjwEt*bS0 >Jzǁ.rwjOǑ,1*2MJ>~vKPbD$H$RX `]D9LnjCa5O_ܬVB3#Ǿ'Þwx5,mkOe)#Rg}G]g")%vK1f2l|7[ ݎSavI8Nؤ.2Qefź.SM0"lTw<2/ԬW9UΪPRL1Џ6פ>OE7r#&pI2$#"J*ZZH.%DzW3O=C7q:8݆_0͞=S\b@ )Ǘx?Q2+dM3"O%ӑ9-'aFn"J[u S\ªYU9F n#ƙ27/Ebwt-%c:=n_fҋ~OCZpg~Myz??UGPByk0G>- z6ɳgd:tuM+{|∏@ W@YmV<%8cɋ+ndyf !Hq/] 52C (bJIN!+82 EJ\+]$FIVVxpqQjKڞ_/n_ঁ#)Eծ& %"bfd#I/E(Ȑȳk zI[(SK7(悹 E-ilAKNl5KdLpSdͨVkܼ0xhɫe-m-?Pj51&Դ}&U0B xVU8ɃHC( aέƨ "I BE{`fbZd<~L c7En2bBF69z\VHkc$s.;sY֊u(Zš0;n$2 b~)-8c2.3~|/qg~]x5J%cww2c,/ngG3r[pw@7 ]G$bZ-=v]w?p)˂cӒ6dpf׷56p^=ah˂ݶسox)VŚ\*e͖qtS HrkX9YX2vO%siԒ/< 39}nhS;֗e믭حWhz',Ai{s$ ~DQBs0 rp<H.2/B1M,/ 1;G ayMK^<!@H_#%!EUM=Ֆd0֢F˥ipD c{֫D\f"< xy<2\k|Mh#๹ɓkHҺZ+2=EeɫM;`Xԥaȳa$Rhñq;nv>}xÁ)I6'S%k~iY3{™_wgg/l}0v/~eI~jIr}MrQ*q]#J /  ^{|3[@8tx:,$Wkjij` ˉn!A&R c5jSc%*&t(9bGA)L?|/yV+^y/뚧W[*r"Wuq&w6㡡j.wWO CP22LV3$< Of bT$4 ժ."@Dh~ O M^P2gAJHfrkUMBP(ff uMUDkZpCO !&I(TbΨjo96ٚqp|xv:D dW;vuǏc`id:ώ v釁n$ѝN- )qpXL7v˺Xk^Zs:yNLY?3UAAnHuA."P q.!FvW֛RyZ-!R 6~ )M fqj!@"23ZPgUULiPZ*-u y.2aZ̷>okJ.v()m[>z8olYoVdNL}G&TvGZ$"cw9ρZ D@0;0 EnPZ._PJ_kdL"… "~lj6̈ܢf"qQhd-6(cɲXdJ$?1N=Ғ 'HqZWe RJL#<3L )&s)' ZIWS4R Ԛ xt}f]IXapC 0t,Fk2%)H!2Ga=}DUh%x%ÉjEق86 (/6=ᣟӟ?g ,|w~O}CN|qs =9c7+冪 FAYb[Cf[IIQ0qyz"O +!y>}m5U]b(*88-Cf:'KUU0&^d]l꜋OU%s MOedR 'Y"'rnLHa K0N873͎c;p|#RZ,qF[CYrJ%F7rXqlzOj(i&8'i˄`E,Y5QXcR!b":(šn*9#S"ZJkt!%؂87ruuSגU% (H.b4&ڠ{(QW+@q2wK)UJː'#JJErc0J $CFi㑼,xL]deir"LcOf 861 ȫ"?/?+@Lw߶™{'fQ8f  I"̒ڸ-טFʼn< ț2jYԓ;)GPfK SᄠOd}s;2řF6 +4s2c3R3KQV<(醁 yabf&Eɩhf.YYAGX جK6,ӌD(ќdž40 3Y^Hʒmѻ8Ij&@H$y~hM 2VIĔ(Q&ù-@Ɩ"[FD2a\%8u Zn2WÁ~Ũ0.sJ+b9YO1mA,V"Hi 4!&`خ+JN cHNc5nqg%Hi5U BHۖ=펔8O̓X˪QxRyÁDT s׿ޠaSmڞMdpyqR &ȊKrk0{1)1M#M(|DO|izN݀Oks# MMmiƥQ &3=+;|QKpQ;>2;OaYFfLZFǁB/R65:>FyPGX H nf 5VLcќ:iZn)$k )T@VR) k(TbUW!N8UQ / b<Ѵ=b%ۇV)2k6khڎ&4G|,EFɄn# @S0xNoN@i$pŦEJ=qU1C ӈ՚\Zw/!m)UɒHӏ8ZRmW(:Zp>C' 𧈄6cvnIT)xz7~gyΜ,jG^WOM P11/i8#GbL9'zVV5hLQ"ԥ%MVp|l./~U}?#Nbh*q@J1y 4SpੳHPF1#~vHCBsENUym;X&IJNӇUSXMa58=]1EI.8u=ڌ:UFӁ-Ynh(y jJ"fX NRrzlsus'yXJdFikT1yFpw#$HdD0h.V5\G! . >}qKRI.VVEF]ֲnI)23\\l@L$.֍?!o5N9sgp{?O_M+k(at0'/9>8r HB dMVAUe[ٔT"KDL!9<"v[⤃ HP6ٴ<4CbU/͖Fj6܂by ~"~"3Śbٻc Z34N$(1GRR cxu8u<#`UZn[T+`Riv(q1qz|x՟(s*7Z S%>yuawHAV7503y#.mXsc\JIbib 4e悋 h!AtͱiG(ms"HYCd&~"H !qhyLKhج laIL5Ǟ>{PceoHr<;BHJ!(fP-ɵa{i"2bzg/`P9\_mPbdyv|Hqsuz~1͉Qɘk-7i#͚ v]=f<4'6 fa5-ST%zͱR30L߷l7sSz?{*9M>zoo{gϜ,|?Gj_yLm.vdv뻞$l )m3 $*IL 7Oy6ۻ>=bPK8sߜ]tjcw!ICX!(DF,EMa3G; E@Ȉp{>9FeDۜcC;x`*rMHc8zRZ%$ZSvka$!ICgy,CI3M=J.vk7x'7w~CG2xV,EQ231"bdY!p yzjM%28v=A DӜn)YR2ac3 ggϜ,⭷'`G&ySrG$)9=ydi&HU& ط!I,'S?,J!HIRrzih0-,׸iQPHU!8l;.67<)J=|(mjSq)J0n?(OH#*ص }EU $Ѥ0h,|r`!yԬ,&cC;,g]VX 1LLLtqd@"%Z'7և#Q`ra 4:RZ'&ժbɌ3Smw"ƀ1,[eH\#@x8AHאc ~O~?3Q™\\76WWXERۇvKv|8bm ؍$+~ԏ<{q-24MMɦVlR[H1ZnI11w2&|k֫ Wm>}@^V֓K=є\򊲮dLi9y8ez"fAia;ڮ'EDA,ɊZ~L0'sgr[͎] B2zO7EڮGX4sD?ȳr JI/n2MfぇD*R,bA[E U^`"eYM<$7<~K /3g>3̙ȏfYZ?mC 4]aUyeD UEf ֯Z%cb{& Yԟ%)L51),nfqcG83Û{;džc7%@Ir5o?~Z8at<~ %#~1,yeeMi b;6G6uTuF]fJ79э mcvMJ<Ze;yԵ3M\VUI?O,gh{Qk7鐬N__Μ̙O~ߟ9=\]] E4fy¥%@(4HU_RaYNn* d:VU8ql[mCY(mմ{5yk$unX#?H" 2s۲*j|w>.BHKLÚ =՚*~9آv ZhOjpa$JOU0NeY18˻;v5`[ZtQKTOL.,6~/RiK$S094.-(Ґ9ȄAޏS_^cgf,Μ_哷5?V.>xRN{ƿf~}}IYKOVc92!LYhQR!F ش>4=wwX,G(I?|xKvU灪J(AgL}Gs&fsE$h)y1ݚj?' v󹷞2g,Cz5%z! zC%5ef[3Ǒ5({vG3h-xG| ahЪ͂8aL.`!&2m)1x1lq2~G|?~}^CgV,Μ _l:/ٛTuVl DYWKTYƦYU6x84R>_$`h.1#~ZoCKRf$.wh`'n͞#Гg#xq{De9 %J$51 V$)#Ӓqh94LR5L>,T!ӧ\륃 :'.I"憺.XU&~"*N@yCж 3 94 U>mc?ϙ|o}k̙ʜ™3𿠳⇋:u\KPԆ>~{$ <#@>cÉIR7\>~L? ̲^mr;FޢRr].s6y=f˻[ȓK6R]ZMxƓqvj?;?pl3g~pg&wȺ??r}}̘诲\Y[ev}"ahc7( ƍ#a\쮨ʒHIxLRȌÁ09!DYd<ߑE-wnf2H߽{`"̔FMGvHyUq'pH;F_N_Hxo̙b|7?r&/Z=!p5OnCSyvZ䒺wbH^7 v)3"e] RdkZ\I.)1K2Nak윤Oض]!ɔI}=g;s?lFmIENDB`ukui-screensaver/src/screensaver/assets/bogan.png0000664000175000017500000003676015172041106021211 0ustar fengfengPNG  IHDRt7EGsRGBsBIT|d IDATx}U￧zz2o=dy  1z ⮻""7&ADֻ, ByQނ e H+/1BP.!Ly뙞:>ԩ}:u_      6{ 5{!+^őeexK3z^a1&aC3maC&l{&ںV*ƌSc`LnRm0Oy(1!6%0ZЗL_&TںVY,c*X9E\"p+lH<3O5I4^X`¤*x:Vd|aUKKmXj a ״ j +;;~`C,ؐ0Ov<Ƥ$L-k5;׷ULԊ DkIrVKby6f7#a4.a`ձb CXon[Ɔ$L.$s1NZ, jML越NkxR4RX 3ec~$L"ZzEs2l^X:_S7`ll_mㆺ[O&driɂZjX1|,8ƻJ0Zրnf16e,d !qH3#qw7K۶I$Bcaf̀C`Z篜H8/q_06G.?yzMID(V~XNb ەtVUS2ê#V? [W1㌱ޱbmbF+$VDzvx!--mXYR% (}$H+fj,tFv&\Y7d=O:ⵄ+֫=(V\+ ǗPƻ3ޓҙD֒0 _$70f|qtc/w}~IhH8b\qpmZ/q o?$빧I6-k;2VMCZ^acXNŗ~Gf:']/ߚ?-mm`ǵ0?VtXvW{3ʮD-nGmX &aޤxy5?TUԕfA_Ku~x%I-[;4SkDHHZ'neepBā>v/TOtN>eMrrsd{jZ VeuC=,uv~~:Z.,&X[,~~ߝ,&!%NcRI>mh(S&JXEI!IK9i1yufr+դ,!'ZVfAC׹ߝuyf:WB;8<|}1&-^YSK,!$ٹ{m(l J$a}'qyեXwVf ȭlɕ%$qj+f= SV0ܢ*=!²O* ˥%I֕^MLX&N0VuYe.LZv{0ܺQjR;QcXp:bqպP&v;-Dww Kѱ^μѱq[؜nwiSyZ'&mta]k[8=ȇ>\TWtٱ1m}ϽG<1;iEiDWRcN}njGnxO߁I8LϿ`Ix k%pѹg<~|&upj;eRY l㜃{yITI`iWǝ g9[}oC ͏{[$D6A%,Y)osVxIDCY=.PXΚiU,'XN> ~rtwuEZ#|}o8a00Q~5W\9u~|#x/z"tcj[+s97RI4OnehDR@M`0 |/|{4oW5BlacLL=ݿOUc$asO.6ūZTQ&æݧ~ww`l|;^-'c {Dl|TC,Nx KyJ|/6;_wuI(tWb9*}VղJTc#aXUk9E#>ݩnjsg.,BrW~L[n~j\$L43+Uo]P+H3_>3uɾa!ѓ> NeYGwB坲̭R5.&V ""}VQ[O4=8'-tv8Շ ,ȥr{-g['.' ű zŊ*wT|ݳEI6E?݋"͆:ygLbD1Scr˂U*CE 8][ *Nh޽{Q(jbɒ%}ޓ_-eu#yL;W> ,&Fhnѹfj*}MΝ;dBL&L&]8wۂV%jsƘ[=0 \dԓ 3`hHo%ȃ_}3N9AS 3q<Ƅŭl,lOz.W+EYw.WH !bEkzec!I1#]+XQm᪋udfZQvSb`߾}YႤknS`1k,ŬǬA$BªUvRƖfaf5>> _Y1G/'ym9YleMG7V2wvϻy+Y^_s>Rc tcǗxR}cג$|ZV+Lsvo;%0g9mm*&\VX~<$hnZCޛH*;:t=51a9,DŽreg9mۆxt45P/3}~i Im݇,&$:[ۜ9sގq_9:]OV9pgd IlH6Jcy.\{-^WKUHLԺj $L%2 *lbj{pQU =O[d/Yu=-<%&\0[;-M6&#2.=d3=%KhsXdk<ۭ(ƴ[GyOYiI˙($LƄ2<,'=0ˡ0,YT*7|SxWŲΙ3 ;:|~#._9fYE$`$0? vaT T ibbbBD=vja&tTc2@j1ɕ%&.XQφ=i"t:(j';:;6Ԉ1IQ) %HD$־jas,o\q߯hW02u1&1]5q[?fdc"a`}ºX{p[q쯴cz3:0[ ?sY5>i4je-iK SILb$fY jO5K7ڇs/ڔpc:nrYzc:N C.0 @ҧx^V5SI$\Ӕ^=o[ǘ=hM&xk>{ɕ9aO>J!yMyL S+GGT$WH<3NkLaqפqWԸ(>uƟy&RDp{u7pQ]]\pmxy\Y?;vaıaNe`9X(٣"xO_=BK=$@9(J(X [5.tUU؀1&8e"75vre '|H%HR#fyw\mg4;:}?WP(P*&\|1kELLL`bbbji5]5cLEŎ{Ⱦ۔Y!_@$lxO0 X1o9sl2N<5W;EJXfsVYpR BU~Pi5Zꪈ6*vIwŘX?@. !k?*I-֜ sv"5'Y77WGs|J;YYۦǴ}\xjs*hYKIغqC ^k9+zlt[G;5t ݪScIfؙjK|]̇H2z2se=8~VՅrS*[H2]!PV\lTw\1&5ԂHl0Xo ul9քKxR6Yh jԵyJ?q6W'ƴ~%@$<}׀33[dr~&cZ ,UL WVlD(vm1a 0jayy"a c̚SXT/]紘k--{fibm{oLh6`kE+ZGRs w^TP1bLkW GIx0T,'s Tǘʖr?$aq{Ę~}cC] om=# .p*?J}l%k.+[Ę{[lవwKb9b-yaKТ2b٭=ՓĘ.Z4+"ƕXq#׼ 6=bLXCC0%ao#|4QSnDnmY.ë.YYK+hcmY#@>  Em֊36&JT]ːZz] b^QƗ-XvC2 @߆2fekiMǺ8󁵂 YI#q 45"Iɟ2[Ҩ1E!*I\-9vEt3~U,3#Ǭ~T wzJ)t,j9GhE"LTa]f"jk(&õ1ZƘX2U2%HbL{,{\,1?W宎X pQ5$S3VX(c|k;~p.a a(厷,뉚fy˅#^(p۬ʊ?fN+r&{ss˴ZELgC{%tr"iQ[Bn%CYQ&97rt<.k)#tewC#1翄Y|g!ϫ' z4^*-srS8iOEƖ +l9-n~a"7^:8ID셾G~?8nVNR]=69u~,t`vò.+rwx@","h۹!|[1\Ĺv,YNm:BҲl+En7kџ h^xaw\}` WofoGwŎmT]J^69e-ş,nͅhtC$ ػsx`[ܚ^(>`Iݩp,n>mY[E!T>*h%ԅE+dw&7MW/?:?=o޼P+/K-zA [ߢ'I##G0.Y'2ʠS՘HDнPo\A$=~nΜ9eY#]c#T&g"JXfl aQiC$ŏzZ|!&a'L.m X51I4RIe b *9a $L"8DH$ÜlttuͮqkICHDĹȆC$b\:$n. 5˗/_ 0 "0fѰX^&7-X.َ$L",_Q) \r%Qu1 b 5]]]K#$L"քiQ CID=[y^d+UHl%VYX7$L4̒iW\0#Bi1cm!ݐ08ڲX=V0FЄPUEk($L花Q}, 5^9%a3B'+I$I b#8]HDQ51'&&fe"j&kDŸ{n @$baQ]H Z$LӦė+6$a&H.te" u>&W"q2HD\:#{-IyOXW&CE`.` $Lb2]HDimm ʺV/ ID՝3gN(a*V/] ?}:C$0P´$$L"nD&l$Lh7I7݊0zr9Q9^RI4dn$LѨ,iV|W 0kCk)4= ^K0;}DZ7 } wPyYm>$wMD ~K_wq[8+ $L-裏%:0HD6Eqi"aq-]4Tq[b'X&,=+E$L"dxSTX0%Č )9s愪1M-(VkHIDXiaBu( %mmm|~:0 o QQ|f2 PSSad% &B/}Ks.+0 I4/_^2Ƥ 3CO?SX=v08lFTIC a^cτ]}bbZl$L"vqIDfc–zn  Kd"}4ApHDhmm uge+ciYIDY6c,ܺu޽ HKDsǎe-E}QĴ$ԂaWsL[HDW9'C$A|,¥+ZZhǠa&&&S%2Qsz$0zZY:϶֏u 7J#r~~bW\0 D]SCr<ɒ"AK򇘖Es9& _~zЍĴÏ@=8ٳGu/f3NA$$H|ao%\.`o# h?ʍu=zϝa6 aAzAu; g&n&5eDt:b~?Tcp ₖP|i&CYrqXP ~t[w*+;88fV6rHb曗},k储cSjݟSHL; mPس>{ƻۃ4͑15MbٶclC7d1zŋO yJŏi5 0hYm]g-^݂pUbIw^R6U"%l>L7:s$L",$qn_tEs*izv5($L"(Ms9% 5 z4-_ILBM,^x/_IDATyU-#뷰@[U&Q55Ox*oD+r!S,Obm؟ jdujcel۪P$t-JT7o|b;JAs{QI1R' [i'ؚ"P&!#k -LUDJ$BY=Li͛7Zj~]؆ċAʻ׫hl```C{{{4_: Q1۪АŜfce}VK.VLխ[:bݲ#$L ǫ_W/b`;$, H5v%̝?4?dӦM&h^7Dfs6S[܅R_|X|B@g-Qۢ 9; cTꫯ^(```@ZiV9ŠbKÒvmK.\x(=xW܅"YAKPm۪;M†7 c] ;jd+Ŝ=RZ۷o5Q6-$فQ>>:S$,\}aAx饗dnF0#9 "JY?$~.\xP1Ms"_QIE sf'*K);w?q}-o]w-1.pS a\Dz᪫{Yg=^K7iYȭ:ySW!SVvfG}w&V3W]uUk_/[ZZB歷޺#gxebֽ\ں ''BXQ5k̽+J${AKD"b4ey5k֤_&c]н/zRjBowoC=(Ɯ94Bs2/^4Msg? ]Q|j6* 93Zwi_|M%@¬7LTޕzj ɛ/^Y+;ݽ-ڟ֐+[K#D$M2Qۢ}7N:DQN$?~guZʮlʹ%@NDٴ$ϙg?(GeHbNROQzZ˽{^Ԩ$OPqM7]h{zs0Am1mE 0AX\eJG0V2Ǝ09}6ݼ}ߟ|özZF&ym!?Jmi-J)mmmX L><<|MOOf5I?֏/ݻZj؃Ok&=6m1P󯴵=v%eY;H%M6/~FT*{>!0/[3^쵘5߻l%o* YO>PNXFݮ"ts3cD 0RD"qt=.9n.cJHI?{?0eT*cF asd1MbyW>cnw5eqzwo8Q;Ƅi7@SeP( #!!ò,% $ɠT*? 1;]mbr<^ rǥ} 3m['eV2zmdnf('̊(W4fގT*D"W*P*|[FS!}”OtMdnf(@YRtVkkHձX,b``BEJrZZZ"y0 X΃Ly饗Z|u~Y$09+1e s}*$zzzn0d٧E!D]5$Ai,'y(u*k*]R'kZI-[b i ڪ\f͏gɢmU1B$g<188](H$:"~a\xHxG$0V'Lbҥ-$}Ͼwնh 2"MdY {\G Kp]w}%dۢ}7$iŌ"ɣKkk+9~<Cv``qM]k%uݐ(#!1fTI] ٬g9眃~X;T*.e˲^W|i*ƨIDn1NkeJ򌌌hYqݺ[˲^/~q֧?!_k+F4¬W===uLx%,zħ>)(~Ũ#İn" u[|d6p|>$yt1RI:[#zXI>D"Ld%$ŧ}ы>OKλj[/D"f<~H&X`Vߨ<fG[JQ Z}$+[N< $OTŲ,X噔%c CGGg߱RPM+L?m}$0I(V>7V%҅ƪ[s{׮]hѢ;5ء$4%0ѥP( LJoX6 t4|BBN?B =Jz.#KzϻkZ{UtrBB!fB9uQPUF5i/$|6.5TkB!fB9<*BorԒ=bg/M8vB)B6=qW5fݓ?l$5pJmM(S@BBL!GC M6FZ&ErwT E τr h&RsʵJ .X8KBm%Mp*7!l̈́/Z"ESsM8tw+_ʙ}>~CZpsJ[3dhz !A!DdHHZ*ҍ̴YUJDz 5[<&A!t=LdH[45[ݰ[M_oh*O6ThZ彤&Bڥ# sʵ5{ӪO0Gٯt֋uЦwJTU@l=lBV)|Bڨ@'b܍lW=+V!z*C%el=p&h&Qpk$oBq\OW\Ly\ bs+ X܄B ]Gi"Uhgj$&:uGbE.rR@KLL! ࠁB)O:C#a.ܤOv]\w;ɔo'} }ӥ! gBbtgFXZdSю6V {)7?/uwbvTG>ȷ.TguSDBpBHb9R[Qebъ!z]Jet':-*a{&ONBP!#CvGӮMMU@(MŲ]Y&BFzخ/;lL+u*;ai&A !~eS3UE:ʀXM$=gY$W\oc.\]ihBQ BVXnY(][<wg"]põEtQU.8 hgBE3!>vjE(w5:cU"uZnפ[iz=Dt+{Ci/Q<B4̈́zIJn,ˣɪ7*]dMfm!zzrVUBXWfUh}sf6h4or&h&vz%sɞMߔ xU^bvSw ܕnwW"0 N<֌Q<B 'GVɜ-ˡH;#w?OnW$:^c R[]-KJJDa,Ј3!TA!dSYXaϪzg%w3]\wJ5Swڷ]#:۶:訬RdHsr6iv(oBn#VsNr;"@ C޹R\va*͚xD4#Ѐv/+=nF9}pB!+-)-!]Zt݋dx!GXmrdbEL'ױ&pэwFYEggBH?!#,)QxXvm-~\pV ?ݞm+^Gd_ [6Ub!ߍŚ"cd R<B6fBH_Z5l0YQlbyeӭEr~tT3)_jT="ͧp؄tjb4HL=̈́!Pzojq<a bP^H"w%avT!: ^{:uBz"=, Mx&l̈́س"UV,By<݉uŕ6{P!jvܩ*Е.iELw7Ot@o37 #fBȺx4d+='7l_olǻ-w[ '){osx?ߟ}2&wrw %5זtWلt3/9x6x& fBȺXX,\ZˉPP&?FlȻ$MY R-UlZeF!ceh'/tG\]]W<3#c1O( !j,|Ei.v6B,7?'{oSDrhjCjv"K #&1Jlw>O0 V 3!dP4BV^Ehl,CUD(n]$2"w 1?q7ydo&cIy5#6otӹ ҬzK tTVٸgBZh&=E+`J힭܆X d{jB([_lJ/W7z[ ?Ĵ{+g^ww@'hڒS"W%t{ڷ\egBJٸ>!N,G^`w4ݭM~*-Zq/Q?FvҏrwyyU{Kt Bȋ饈Ψ/B[R I˄"]3*TQUΡh&4>bX9;*IjgrF]+]ڳ4VIޭo}P\zoC7x!OT`5DgX(Wva 鼈n;e"=m@V^HppT@LfBH!ښeK,GYsB{$Ԫ$!U%g5 bu浮'7 d;h"p]^-HO/$BLY ̈́iz$5,jz]7 P}$*W{{pY+ol`x3.SD7D+&wyҷ]*gBȆ~BZ(̑=byBYݺPU.i T_Rqܭ0qs615n݅ `KHh*D[@ϨxaVh&Rr|T$5̹h)[lXP.=jZV|V'"9x2< q5.CȋOL_~ߺeqx8hY/\'^黊WML8;jiCwD# ]g.YɌB ( !h&R"ڂ9*Vѳle%ySH Ek둁* dڲ@>:|CK\?10?Ⱦp.|z˼0OCC $򿢖Rʂmt~ s1 I*~ =w4"VR_~IYKD+G\ѵτcuN3PХY)GCτZP4BF,UPV Xn[(߿?*ɀPnqC`uV^c8ޞl_~ Kke0I)oIjr.oOo9ė[S|TH鍮.Z+lѓZb-g]]t+S<Bh&@crgS&g+6I$ rFޛ>Ӯ+D9W dL${g s왋Ot0@g2KSoBz@sy(=soշ:z;H#c GM't7Pt#nGzZ!€23! fBN/zr5GX|+xg.N-@:"YNj6r"]o~  8a(y9nۗ*2.utNԻ mmt31_].}Dы KO^ [FU 43!dP4B2NXlePND2ЦP޽T>gr}lNj6"|*߼ OCƀ\c_-.247{%K|.!茺bZ 4[];hqޙτ>ALN,kգw|v{[(r~Ye(.~#o:[c1^u5擵}t2\Cmە=E]=6Dtos+sd.fE3!gE`bbʫ늍v$Byr7"Yci,x\Z EkM鱔{io]SD; ;6j>}v⹱d9gXV]̈́2\,Pq./* /GgMNGki4| 3Ã_mmus3!gfB݋eLTR]:b9:*-]@Q$gs}So} ״J-^{ mۖ}C\vlq2%3. @i]<[3H͌|{e3DL2n+~+9zeO|t7lUOl$E2hs § 瀖G G9mN'.4ysK}mmZ軍w^LVm"Z)\2mWsgF>=*3DLӍo_,JIJn}-bm.kmeeBO(d|>5@ kZe5*ðV؞h?TmWүҫzsYDRxwp5~<^/_rY T,B h@mMtkU3\t)č#RR !fBN+ˮVW*[^l˫u'ӮlO뱌` /xHGBf󤱹wQfE~a0I҃]{p#{crm&:D@3P"3І^xJ$n™SE3!z RrrSSvʾ;^x |OomOP[`Jz*mzR!0KLI0/Jw(=A(Ka 0X.=H+m}*Ev`o ro&w>[Y\&t'ܖpV!#=™SE3!`n(5Qˀw]|t7wUʾ'7zd΅ #- Bf3 LUU`RR'*P )^hN6o(wt[:u{]9#/F#΄( 9 n%IF|2P.Wyd]CR\-`_POgُCYh@$gX΁\A 2l! C~Á@/7@k|LZP 孇'"q,ZyMN7 u?=UU(Ns!Ḿͧ=A,kF.2er~].t3/ϴk ʿ򑧞 ɕ xBr10ZlpK$,T7یg(ON<>_eJU@.k)ۍ33!fB6/[0GihZV˛#jWټuPSφxKxhSصyA̜c)#DB @6^§z헿GUq{}ng*7xdʌQ8g>BBLb̀h6 lֺ ]X޵zW9.BK @sUzLg&}6k:u; 4=*#2 ,X+P8P4y-L* f# k{")urGz.L(2[Jɚ@;U@ 3\OQLb8HGC)ךhxW4^xHDtaJs3sF^@uυR !E3!\|TX;k94{bzbG#O= r[x|s.:[\yX"/]z4ob\Xx,ko . R<c( /eZ}*pwruFX'X>:xc9ϖMq^5Hir Փ̐9[Cb:D;ͤ_‹߼}QK;phv1cyA?ijpȨ]x&dh&5joEj2|X]a˕;arlGp\P*+Ӹ7%Hd}t.׭iZRNkEu9-P GD,3߈IGE~Y-3]oUzX ΅מdj܈ IDAT f)NEi(*٥`nݲ;.w%w%Xnѫ~dFP.aT{T%lt,2Z!PAz B@򠲚])->B<' &}#@FMLHh&dt&QGbYͻ\o*vb>^g?10^9VwЮȷ<˧g,&r d}@K˹u x]u. -˄3pC_\)gBNYF&aQj55 2].ˈwybڽoyy\Gُz^Z%E$C $Գ;`W uFB295rFZ]aí>`0狾ORn=|n]:mx 9MBBτ fBVC R=sdIL,nwyr6|`*rOlLqzfr54{댫dS_{08 9\=xNI^T٘h(*!fBGXtlKU=E,{%1R.`Sx0ګ8t}=?Y+C-XE`)ecVYÃ+% (]/le(+ gG<ߴ4nPQZ !-q&@Xؖd`n[,/#>GFUNrt@sHIPB %p<ɋn~۰?{{I?: gՋ @H<й/P%_B;8yK3UݻC\unO<;ySυJrMiQ]RaFлg-Ң&.bp][^XeWb5I t.{Q>Krm{`E/J$܃A<ݭR[_~>xF)y 4̈́sfPIZ !-yk&8",)ϴH03%]{ۢ*X~E9b8ccXd*1:KY.{G^Ƽ<~ V\ a~*PUNc)o_w\re \.OnKLHyAiD0Gh{:͚Y0]6e=L0bk^u=aMUZך@`kgK畉;IzszbFk^gp:4UkBHCehpZ]Y[{?Kt?}:ئ̻\]BG0V]пJX2_‹߼]ysK<4C sYܧl7ibl mRein-DVv#ܦwy)J5(eX~=0˺谉,TR:Oxk&]cFU9هPc3Ӿ6gMտF@`-qa//׆xw.U~Z ɭwwYU}pژ[kB0_GJr vi(n`/?Qv=sYrˀE0_:ُ:zGJiL\NkWB˭v^XʭӪ[4V~a]si1KFk6suumu׭ey#S!\ai&8x%h-LFHԪ,v@wrTn2e@'ՍGu!Kb.[_mMhkxtӾh׭#V +A@ۿ?L*sÍ´,amk#@BaGz%;zrg]0W X-{Ns޹ z<&U0盆rVuK;![&!DAxDix}sS2/]wRF&S;OGy2Ȉx&Na(p2kuV|1~j U wKZ^IZ;5+Z]w~T<DZ5v@ [~P,|͗G%^F{esg;M`Z9RVU)Qby#T`B gN7s@&K-_[M]XW> x`9}rJ0$SE5e1|)W-ۯΗ^tоVg] U"_9jv]1waUgu3b}' T|}-MΑc$( qIQ0Gpd>{rLŶe|re48؉Sώ1A8l.q4/>oJ]/\|][,˗򥭾45Džgy{\x}0z->% ZhPo2?@3΀z avxLs&Fz2( qj,ZüB\WqWl`6e`{o2 L,%-QV آ[ CYHbmrWfz_oUvYe%_Pű^5~Ik [Y3-S4 ysKfkR[&W]b|`aOI+еgyԳ,nk=ү~X~:[Y#Xs]ada}5ba7>vynos"R󒼔4Q:M g@Y{B2 9T93]s+GHUMdY"$DrtTJ;]5Y5FK5F~P}5`ٵDwߟ5dҾå/vE|Ѱ/Lv{}MWij ʐnbd@x.;=`D<_~I$ۚv4U .T ~","QQ];ҢZwhrr]Nˀ.!e.+Z< nHYj=ix0fm¡mܯHE*F楕͙N9ktI]-aJѝV=ZlU]niV;=:&y^>cm3`<:>׹E M-L8/*ӌ.BȩE#%zv]jULv.2 fb9;BjP6`dMcЦ}ҭ~^~/c룬~W'¯J}a_NUlG< g򅩾p/8?sO l%CJq䡷ib3sMθiH/`B'f*niZD:R7kGڥVd36]vu*_>(>Ѷz>d>>u7 ;ןjW)Tscn^٘Sі5ݩ[s#҇*ˬS5$Q(4[Zavw~K,fBkB/Ƨs⷟?:PSme|{p΄l,\&}sm2̻Xw9+`x W>1fGB,X(w-+qe Z dF k `: YY-^e>>,W#4On;.-vW|K+1~~ڭc},~)GA/Y 1jqq9 =#0ŋCxmD`|K”.ڳlo./ K˻^i}o M[P>X[{{ǿy;-^|08ųu~EN]vvqWBN+! ;)7kFʥVTj:6P5%[+| $f_.bt:vnw'!I?sk,U~A`jߺ?>{ަ~U$Wg i LDj^a08Eppaw;} Sq'EWˡgd߷>픅U9/B;/?8#,-QJyL0IcߥvR8\xs[.Zv)(X0&5dhD p8|Sb28jd|[$}r88P6p(@8+iNFNecpi, B }.- g~Oϊy{@ w!3jbT֚n!`grh&gQh}'7һؼˮb0xSe0RܲcD 8Tʢux=j~4Tr,X-ص8y Bͻ3L3Ca~2uA%G GuBAˋP`z$SP"Gy: ~`PzFx:[D)_~e*m6s9R"CEaվ*nSl<y'dSuܺ`nap\Ͻx!8ә4ɗ/ڗ+=Rm_Jf_,f?׾^5g/B>?_G!`|Cr8E8q1`kGʏ07ti"/ɋ腖(&SX //o[3.3fS|s_;l'T(w쮽 (뜣L )!.BFb7kGix\j7mIW0ۻcW0O/ O@2x"-[ibika,gWA;ڗۛL6i}` dzYc*ƕerk=r8ֹv~4օ!]BV4n6[T3jo=4kVO4v0m{k/觼~ l%q/|p.MM׎ Wn*P8SE39mוmI\̫ f2P3vώgs|y2ΤP8˦"u[n5аPl/K=_#Kch{o`7$fac31^ħ8QMN^,/%?X"#xڎg폰5߄pkG&kdh 5.Ӿ}ھoh߳كxZ>Ņ?{cC#C`%?ZgTiR=\E+ODkFH)ajyRUK4tl'>x6W`C $.,i쀳U >ue!RT,Saǐ1ptj7>3؆ilՓ=['G;KusqnaX-$7d2fZƺ Á2=>|FIW]6 ai+Z NZҹWp&frZ/#CRM\%H2u{g8 xzAXx~!`N@nvd.x Q(HξilK{9 Dhg{o#qغa IDAT0֎T ,BMYXtM`j;rv~?U#<ģ{o)l^CfRsŹ>GSeIL<_20Цp2܂pl& hDuBzȇ f{2 EP/R/ׇ_v&Q?"#J~,-c2_‹v:OYܖ̂7 :`#'9]̑!I.lˀ`^t_C\ϏPSALL 2_ rf|A\_Ff&loVߘo`-B1GWu8Ś Ƙš_)"Hkq5_/__GhÞ4q\  -!p.5S;7'GC~/vYiVK #m/]Y>P[`PǖKax<0xxou xN׾> Yt΀Iqۮ%י}C=-[Dx>A=VL1'`Y"pfkqOg-w"zV<[sדW?}~gzGB|Lya8ŕ /3I\5X_֭WϜ_cPJsk3B*W P||9ٵK~g%x/q-m\,%{ ,fmB/]⋷8?lkYQw}hr"G-?8g?ZM@u)DO#{P{!}EC{l7I7%W0LǶ ftlW28/-T׆1Tqd"tz(xaSZ#eedE[b˜  p3M;[=L[~G>[_tm׹jy΀: g0ٺpZHHoYN/4G`q|t=Rz k"\0_H?]LGNGwz8$r! /C3ãű: BaIG +2X`n\ K5˶|B~:ڗ~J5}~ 1~W! Vcy0^:&a0HvCu2H+A'\>gYVOb='?޸; Pa!Q_-ҕ'[`1ŖeZNPOv_.jԏc)&S12"}2;7 zcïM''1ǒmMŁhk;vv0񖭋؇eOŏ<d_lm]p^\Hbg =H#R/x/x;'v˹_~Y=1 jW,jr~Y&<#TnZs ;d_[EՆ_eeE<4=6d O!?Yo͎t+oKeK}ƅ2[#?S>*i(eW׾=|dorSGm}1d{{l_OPnἼz W./YQxY~yWӿcbOi(/*dur!ӫ~ x83="џB |x(_ӯ+Yk%sKs}8/jL6> Y QzY""5z-wt쏿SIT@J<S3J)G8g2U,\Ecv]䱡. FIJ~=?*ed[؃"g$?tn9s~R¾9Zf=pn~ #e wOH<9ǥ*bME!p)-Q0ӧoTY\s,@BdF0_{뵧k ,M n+ڦx&S*w.h*?9OA\"dmL OdE&oA0K[,ԵXΧ'~C15q,듻&שׅ縏Ҷ׺WNU{+yvυ)xO4r.+)ZVn\8'&r:r۾OF6v_/Q{wmp|6;YKLz hK7)g0*ٰCk,e}:?^/@$rA (͖xrLA7Ǒ#Gy}niOoih_e0O Sq¹σpGS֎kEZO :mx[0A:B0Ji\GX-S<.uuWjtSeSI:(6Sl_{8[@ȹ0Ed!LKkK쬽w魆o:j \s3%f>qH#vr\vrtD0,}2TlכVQ"lEIJYJ,'1SR4US1LkM*B!=zj+lϲMN?V\z]{] CX'k:zگ:zbJ><Ъhoĕd7l4T]C"> Ŵobߚ)=syz #5x>KUs0?7E]g:4_8+WΑ3O6 2lX4X%DžsɟyWO2Gf(p$CvRFqq+ϫO0X]_fqKE>«/^"/whk_**|p.] \~d͈"t_GJDGpf v|p<]_B/Rqٙ)NƨFmIkĐlp'SӒ#jT`q:eoi&ixt6SݥZ럁:,>(0'kAKWeu1V^C{K}CYaJhˏ[{B^vk{g9g ;*O3`pdQ)%X6M0*6) 3?Wգ!jy!$&`H3Z+ jtzPzs)ɸ@..Aoo.ǘ1,|.>`Q(gskum'5Εӿ}.}>m 2\KvN˹X}E'bxD \ZQM_m鰧v=rnkpkم-y(e_^|J?7~Kwj0ڸ\dϛ.,AJj,jev Y_[B:D䯣FyYUm7~Y0x'}d?g)'!CMc ɋeEm ^.%bN,'=,gkSRѵ'>w#;k j\.I[Mw 1QDeY{-K_$ {ټcٚWv5FC1$0+Ÿ_+X P8/.&# p&k!\wșFl^V#ߋ޽"߲̻Q  qH"p/n_FRW0Lp dw3[Iվ%/_Oz:f{?s0? [kb}LI/ 4}),3a ,x3A[ڷŃt!x֟|V_woSw>WU_~g3[Lu{Yl bL NbrU{S#sIˋ}iyy/JycٲS^5ZN[Cd fC?4TK,!#@!|HbBdڶ\_Gd@Lܿ3_wao7?|i._O{X F~ᣴ}i'ʅ3CT g`)L8_]82,`<ؚ^MI`80TO{+/3`:<4M5=ڡh& g K}P+ҥ`.[ ,5ӱG7B6bU1~P`m_|p<]h"D4;3'2Nh$? LI@5].uEY|yl%5ؖ:V<9 $.hJ 1$> TGTv8)QDA/ȉ0\JUV%ĄdYU(iR 'H@<I]9oΑ渇skt5g;| ߟS:D*f!㉒y#}/w.KmS_&}]7׵9WA?lb!DBI|5c#IO|3ߛ&\9Y_SN\9[.\sټ ~ҴG[X%K[=Q}D[yy' Csstri  ±5[RS/c.Ϲ|7FbO3g9MNR?wȋ e[v efܸr?VJ4BB,'4RM |(K=Yǐ܉7I'&Y/=O|8^a=U'k|7exr=7Q]/ KkKTCA֞ټː/m±q80`DOZ;Bg16Ӕb/|Yno䚯|o?hdҷ˶\p@{1)Ϟ}{@ HWXWxS=3LRa<ˢxu)D[8$ !DC/=6oo{c\=>i}MWzdzoןrucRS{,fH s7lĐyڴ h5:獪UWlSiڳ"_?KZ҉B[ID5 yڴԺx*`~Թ`{*MS٥>Si)ģ}>-[Ѳs;\XMZ UrZR^i,Iꌥyq/32ϗ[,ho4z.mUFZńa P|z@3VsU RɮFlg>1ALQk_즿+t $Z΀$,0g^fM6b- [UoyI(-=K:IZfKyMsӡ0տM{t o C!G^+>ሶ{zy',F؏YFajYe\8z|^9r۵f9>/8ǖ~+oǛ>'oiy/sSs+gaJGy&FLa,œu9^MpOHxgoQ={zn= sNe$8FBen*Uce g ׁƯ l[ zZ%m&es0Zh8RÑ_ҒNKм"J!ٗ뭶΀30O>3B}kK)ƼO%k.bVЖv%b/b9K4aCvleˎCzwk}w/2DXGFuf.O1Q\GMr ڴ"UoOxW.ho JELiCa*̬.qmm\zs%:>t(>J޳=B8@JSo/#|=K;o_Gρvu֝yNy愰{= k w;r{|QQ_{~GIߣ}<ǨH΢8pG:k(ڂFyIKZyIEj &`~܄~9/8v0|&>~דOj & ƼO,^;0+w([r/<>M޲ca/ڮl/睯PynܵM+vopۥOYWl li}Qoy-\ǁ}py*g]`o;`=Y25oeqC\(:Q]PhQ&) lﮆBH=o~!խ` f8ޠ~RO-I5<` \8_Ʊg` tt!/K_3\|xp_[ysyMOErf!y0兠va?q'fN ՗' =x-מ]=g̩lYn\ (߽\E&9/RJ˯5FRz 4FXkDL =^PYJ=Yqguoy'2O㻎rMzes8Ǻ>616:o`s.bWD+^o?i*2*?MS Ҕ0: `u4cּVΰ㌫88iJ^^ll72hq~<}g@J.p+/iIPff*`70±?o CqυBvpv6S}~yKkpFΦ^a{8rnioUY9`mY+ Hx.P}G&("^e]l,lwCCŐ/K@oU>1bΛJ$DJ@M݉r-tW {omT"Vh  ~o:܌[kW²"A;#Bh|ӿ9Z[#Ο3P %p^[yIG Y0o`ˀ/7>?`;[ygsW6:[ܼ}KIͷ)Z3UmX/Ou3MnEdZ%؆QXNʑ+U/\jWn_fX6qu! wL96*@:&Wv_bfffl0exkg׾kϬg\66 8?]KZ:KZRA0涀ɿ?Jiw} ȍGUQogpOS+Poz{rel_=q](wקH fHipxv{=p2! ɗV,/5L)m "w M6x2F} Y#7tyE%O<_w]K8r8o@{1MVh$P<)<<~>,!U_obc_y9f[l1mI!"!S=Y":K!t4#gHJ&}[wޜi=cqsr=9ފZJ(#^gg7%96Oy#]1U-ᷰN}yjI2I|}|ƭ}E]q|ytWpx0lp7[[6pf\ҒZWYҒ"Df#`0 /5ؼ~dB6qpח?e Ύ@?w6ְ;;+zm{Q^zg}hh-i~jߒ ΑNzzL&" `{ry r{hstzw%k[嵞Sz(v cMˏ.vR p< ʻ7f/{8j8%p^RgZ%u@~-w0?sgV~@Wl 64`nOD\ [Aoö$dUjpC$V:貜zW S\}Qrz|U{fC0Q%s1Uh#DmȻ泥Qo_뾒Z|_,sS{w|rԻkcg9o T l`SW$6\m4J~^;g?jgg0T8/ͧ:KjKd崅K;[30ú#smw<3'U{9MzҼ*H>J={l_ "\ɶ5i;#6R1/Q$vX3̏gX^v#$w交Z>ឺza*<tt"C=x~> %PBQ R59P$/KzT/X?*O*)hh^2=HH(f@dPʺ~fD,`,b|3oo rnCLt" ')`Xip|00$S>i٩>l|_4}4 (|U"{u`k9i:8OoЅz`z g@LvlDcIYk4/mE5+k87Mk83CT(jŲ6X 0oY;%xXyc{f&Ţ~ TtFcl~Wq;XT6t2;c>yOĵ{_~"FH)%$ܜ[s/R ۜl#0x~板)O `u3LFaA@a"Gx| o{-y a}P}k({޾>7dt{i^L4!1n˯]$ >1Q*k+y o )+Ηi'!9F%^|g|d]nG+ @緞ey^l0BF D~=aI/t᫞geay@!{?Ff;3yj NJn}ΜЁJ㼾p6DDᗁRe|ml!N-=zKzKrl`6h@47ϔ} n}lf!>Jl/*`fbjxdB8 Aϱ7?ZA{w[-=DHv_wy\L:e @0K)9|ׅ6?՞XYq_A8Jd5%#ڇyxx=8,7s)<#Fl1rq4  ( 9fpz+ڴirZjGl z\'l}_ʩp6OO8[ŽXYfo3-b+إ=Rf4,~_mOϷTjO/)0/5Lfs;#z P`֓}MYKI smy9+v8'N->\*pnr4%w~498LukR[+q8d:2Tqg5Me#ujΨV8yM[z;dHWU[QL C/ ySG kɞ#rYJLQwjr2st mu-$oyŠ:A6t~0pCT+@q-y:^gJQkYParj0h(? xf,Oؘ_kͩXҒ z_/OYomnY!ào`'{=voXUӔ8h` nuor"Wo7 i[/c\gMkWakaeg_Omy)R~]KS;eiZ;Cr"7-'*.::vT݌6ykI[(P se(ά+7EEx:5yY۸A옕c5l IDAT ttGtE3z#IHxp6vUo߇MurV?ԮGͲ=ξ_^> " $&C{Y`TU~~s=re\lG8pp6P} ,?Dt?y%,f2^Yf`>iF=HfR ;WR.%]wqRSԠ),UkOβ!>}wfrs,2Ar)e7)qom93xuN+xVKHdqõUG?H9mIxuj6;=8uFS=LԬ Mȵ~"ǃrM]u亾}+6mv:+(|A%[ [D*khEקpMד8z3UyŀVmE+tS\I9m@f\ `vfu#Ӭ}H6W6Ziw;pl`lW<Oӫñd_IDM}x(r_Gmf_8 \+[9)M1[E1T;6&J(r0uq`\.BpA[q 1LdlG@ eO*0r@@iV∕Fvf9fE9Ra<lȟ\=;r6rnw. Ǟ+n_Rvٱۓ.[rDUOtX%@ g^#oG C 7ӓy6C2Nw~ `$";@jq q Jg+>qs-&KM @Z\b6ms' :ϜOy8tT΀!Yx|`QWUϳ=x=rn1bm P8pTzb4OE-7?m{_N-w8/򛁓qv+tRqIQxذemZ(~̟z 7{#<ƙ06E' s:RcZw!ٖ=R>~sl# )׿6A~=؄؋nzk /ͬT6~Z6,+Әտ"X+#Hi?N_kt|X] D٫Ar PSxask򚙄ogښ$92[XdF4u@` oyޯHw=w/psk}Ho+9~㻿sU̩uwhfg|bXɆu6kg|`ʋT.Nq&"|uyhz>1++ǚuow_Xr g%pvN l/>9%)N0fZe%޹ەy04[ |̂YA8!΄^& W"=;rScEvض/7VqwѲig*p:0ߔ\ǻ٦^v\e N,`U!#҆  |v<3w^^gG@o?~/ / 2j@*'79,S WZWW&;w徽]r3]ZהMr张9R^:HX9<(=6Iizٿ ^(3%b `"OQ̬\=bd:]FʘlL @{yrώt6YD KIgGDW2]˞d˅ N/x(C{C.gٴ_ { :'i'D{fًoPJ#XIBNܛ\xyde&vRsrYu82epBD O+-(r TvJwISmGQ}/LGM^UuW= DƘMNNm<eh?B0YƅQW^g4U\gdxAi8dhWnxr\EjGyGZF%d{rd^hV}8׮O>~Zo%H׿Pd l{P" ݟº;_Uy}}]9h_{ 0g/D:߹8ϩލWb#pshȫC+s0'V=:?*=o};_=q.̀qgFm.$у+vN8f788z2[]ͅZz !oeX˖hm~t/OK|5@ҩt")~"k_S'L@&L0'/cp6p[-MK5de=y.,9òq빏~l Џ'RP͚Xw9O@WWí3}m۞=>g~'A*0;}$!qJa-֋:kxݱ4CMr,jC`G{p hl^w|t~v\hus$zyuK뙭lOf] NPn+ vBӴ{.ai$y[6-!өIZ|]@0Y|^DNءBXٱB١ .CS<sm.U4l0wG~b0hyu(> J)G^zr.8vf8=Zb#-%QWx U;d*CߌM]Sx5P|9܈ 4Φ~_@jϨ xMM6վE}6P}aOJv1Io[ZzO|0]— 0_rú8KNscO}GeO~XR*/ yUF.f ,l<*BRϖB}뫳_ޫP:YXͻ|<# KʽB?xlע>ssfG9%D\eߛ,Cz7L(<ɤ@l&ryX̤t Dke{Zo1"*;\zj5vH:?Zل.g#XB$G, ? ͟ ,`Y %C(GfYe /7U\{VVQC/P#E (F{YioLH}^e87\/Je@›L, X1LJF;8ALz?$*u0ܯ>DFG[rS]ܔߴ˙8q a俚/y|(nIUr"*"-F,Rs.sѶޯwJlI<uZܶaЩt@ Xy' OrHw Ko˃̬ TԏCAf"#ƭ+;3;U \PN~&fHhZwc])h{zO3;Z{ѧk"|E}W:U9+@ ku}j6'q${qR;68O'`#p~E~eͨ̀ p~}9~%'<Y7VMKշ[~-f+,hc.sx⯃u`O6V17}GӫQvO`$wvלW vKog{ʒWx `J!tXOi  y.˻\u>Lm96.:za~MX$:#u%}륟VΏجz] Y/i;ֳZYPk7ڽ}qi6!)Pj[ {NvtHإ5̘ A !e3mY'.]2u8ui]C-n3^*%Dz?[)zQLQѯ" C%mq ׃&Vٸ 1 l0,W%Zd[&ڏ;a&n.#c%Z^χK)VsLct]E;ĆRɩs^OgN+i؇8./}'_ap#0U\5ͨ 8M3j0m~F0mjϟQiٲyi[~Z}BK|ul `s:9s%`>C8ӹ}\7Dzay۳1mؕ>0a%Zy'K@T5x[\w_ D~;;<+?<7{g_7^}ID3VHS((rf% (}vrlmlS'iOz-OX+J@IO?qseڨ^i Rjp^Sef>%6dV@tTJ Eɗ]ENQޮimD3Z1Ʌ4ҙl$$tg+gEZ~3RdwUߞ=׵oƒWM a;a]. m/;T@aR^--}umpnLՀsAs%W{`f-pnoprmBi 3Zg_D ت IDATX0a'jH68N 0`wJ,62t~p7;|6=SY9h뗚e;voMj?Q`=‚GjDur)wӛ_+_\oB2 ,+2@lr1+$ Ar@L t)okD%HY e #YOl& vu 3/ tib Idz"2;O:À2/`t>HkR_΅Z:C ^D,2哈I\L F@3wf1LK/t_J!NV!UYp>۠2I/C(4%h -lu^+A.`n3W0{)I4o-~oq(14pk ͐gM "j[=f؍Ƚ.@S #_=u y޸+hsYͶtiO=>P.K&ܛP,YrQL$ 23X3̔^2‹̔ϖdm 8,p\z)J\@l@p$" JETd 8'T$(۳]Oym 1 b uS2@3HD]xD o5XB42rAHC0sF "&!  1Dvn\^hfq]@Lh$"'pY[O.e@ %џ lە`9]-ffΞ}lB`@Q!R<N:$DN7̾{͓WꩦjuZjk`AW{⡏}c/Om}!w hxs;|~fds:D:N]IoWo_z;PcPEgH f%Pl{Ie"3]3Kv($"*íA H5sf-S!\cɌ^OA N`D0bb)ҿ)+@ DQ]ʁ8Xie?d` Y_`J&`#b)SuLyԴB:[&E+ DpBzg@+PHҷucW\gxW-:߷P"a k~{;Wq0j{ϛr&ƈaxdSV%d{xfN~E/~.e-Ӕ֘^OcVLV A>}&38Wô ӾlhmjU%h?ȺzlOͷefۻTau ˾W:Oms7 S^/4YA{@PQ~Yfo;Cዲ6YL˥+v\i'= Jd#X0{<+HD._n;%?2Ys+e.ld$w^n`DBe'W*KFy(3 P4P&rM^rEIA"+a«uq c5xv Ӭ mWG,ez`)S"ł#7\gEBp4łuhv2Z;Y/M \`VJ{@i# 呮Ѷ'ZOECɅ*B9ЬXʬ]?"Yfuk7Ƽ[yD޿#DCF) BǟdzչʃQ9f`*0b՟c :e`ף}C@I~!~7Cgnv\IAEY}i.rܫxtxU奾6{ZP+UHdgG/YCoss/W/~ۇx \eb('*+b&!WY+(OnbDJ;XTGX2^vkCG|Gg!Nך=9)KM7>Gu7O!{cT뿔1Z=_3,6<ϊm> i=o=?ɔN{/%';'qGR~C_+ K^~3.$9p7ô g 8 ôg,OFB[/vȢe~tNXv}V.傇f`5Δuonka0P2+2x]z7,Ї{Ndd nK9Z_C}Vfe_~j?ypxmtiat^N^&uЯ+cR)Z 1gJWH^?xޙxd tP3k(ګL׫ip-3* Pf3u›,)bThvۓ,ɒYQbdә%&A86c`,P`EL=`XJRBh8wYD ,cd~Nt϶o״eO8L{-2H0 !XOrl눣AĊ"=Pm8IDA)"W:Q(b72xE1d=PT ^hKt99"YN"/Yl,& U>ePD{I n^~;^6软j[(YN&XqD|φVUO ڟNew%9Þ.잽WdYKKQؒr  $HIlYPIdي@ʏ{8m 'QJ)ƴPIqIQh{?fwvaZԵ{zSUO j4FԜ`OS;jz5{6ܓMNwCESP՜7IpNA.}>yZl]NfEq6дƹq_ t#c0k [Ǐy;-;%iOc`e)eO L;TLlDu 0pֺa% SOa+'$CރYvm1M G4.nr=s˧r0%O~'!&gC'57po|˯;Fc+5Kxkc*񔉕X(0кțGEj& .@642.l:qX aǬD4@ҢzY_'rPu/n i(ׂza%B]l5^>돳!(K:y G6o~S?s yp~U>./^b8^b8!y,~s?v3pQq}taiM_5-{GneL19 . S?Ӆ cԺUo//t:R%KwS,/NOK>j/[,o:!_w배?e1 0M`axM'?tӱ>]nzڔ7:=dn~_|?z~Ι40r,*ʜzYoU z׭{Mdثi+7P9P3u"oIlc&݆՝XGlB щ՘E)r"kّ؊Z5B@sv(R,=L#-6H"(#|u BEx_}g-rDX-w:H;wj֢";j7"j%+v몣sw>O^K[[9ٞZ 4#p{ t/Ua`}VphSgjA fЙ)^~??u/_Uߩe8m+T8Xշ8'v ly6p4mJlcHtq1Ff%>?7hX@2CK'~g/])9]z+a}imv!߶]^b4"#rn:^Z/?~٠e({::Uw_XEy}a2Q/o%~,!T>JsO?f1$,op,¬ 2A$J,:2~0/¬PE-U[{ldAQɹ%YK`hIN} 7J,ƹxh~y?.Op;}Fnomjq&,ոg.2]6*ݷH Xű%:B@ӬN ŀ6h@͚5h lC>>CA[Ig}vB^'YbG }DR8|,(ж15o92nh.(nTz׿c>Bb*8SI6eaAޟ6=vEńӿOQw%#_W/WZKГ^Vs6|^k3 6ǴEz hKo `vCZfx?y/(tQYlαL@?]d_]C% kJ{{J?z#5ʷגD}\'ҽ/TG,SX'^v; 40 ,{ v㷢X@:eOb @:zOeO@*TlYҌЭ@R"jV]p]gJOS:s{TYk񖡾6 dw:mtD% Xe(כ@|yݻ@@JЁ1s@ ہ cj= 8)I n;,o;?uy87<OPX=׮,'p|JOz ,./Y=TKZ Q3vt\ Jgf 8;~~RG'3X~>E-<>W<{4-.;U1_0 zY.?>cJ_ˆ .PzM'W{>j0P IDATUf$m>Ü]zmo 8ťi5Sgi-YFSWOYa}>~|cA~ [1HT3#ZcPח?oůb,wet!b(XF ɱ 2;+9%Aa6lDRk%ZH,"l!֠PSJu5ƃP$o2G +Xw~$#ɭ4)Z\H!,֧XX\HRB%( ^`dq ,ʹ@K7VcB,l{fZĚF#nXC+qP@R L+[j2(@tg8Y( ֭/tCF\r I4JD9Щp ]$%@Is7X߳yh3bn_|z|O]zzj~? EZ:XcS+a~kS#&Ͽ<~=r !KRX.X\.: Ԫj'\T>R*e%xFA,xq/VahD+]*޸ʊ8"h1Bi`r W  N,,}+rgA ,8e ]Ӂr  7 8Vhv2(ѽN71<W׌ŭBåI`]q LioVCthf#<Н& *->k,Dtk}vVz(QwR `%JB!\`RZ86ڋwVX:X4GvFDju8?Ga}?~tW?^>?ZKT['5w z(<+[z-Q~f8ȪDݿ_? $ 7r fk{'^LaVK 8ODaڌ&a񛧭͛s* yGXˋ nMjӵp~j1i1ee~+i4J>̝)V+O)V8hcSr)e2r1sMs/,2_+sOopZ|GXh5<{fE!9s2~ Љ{PߎPMlʟ^^ѯ~WokLI$D ɁI`MW6 &Cle윣,rF@z Zd~프ɹEAP&a)Kr$w*Հ,-✴tC+ia3 ǀoQ G+8e'8q*V^܇.po׳܂ d:iX`<|VmЧvDG:&MW4c`@1Bf8wmD6CU!u"BhU" 0,y U[iSW6_R(vOD,:Ks:N>aBXlH`y PHuX6XH#S~l ot|POY1n>_"VFG&j>ESE@sfavi}W/*1%>I>vcһ~19^(;/W+ g٫aG΃!XvX!"XS$*kѯ95^ka(6Pve"nz:[\gkYWE\U sK1sٳ7UbDkRhp(_#^KĘ{1v':v]ނR }0p3tV-+ ef6MCmNi1UQ@!ӷi}>UJm9(~*9$(㈔<Egz+_Kb D 7N̟~b$\?9̟mnAG87\cWp[ / kRrԻn/'u,$ y"@?~a|/}0<c~r]}||k;k3Ѥ|4N5? &Csi뼀y-{S<1?3ЏǼ~R6V>rҏ9??50qvBD]/@u-:,a Іn/2jC3ˁϊp`Sd~J)w*ڹo-}$K~f*]ϡ/>SKMwU[+E|uz7~#bDv'eC,$ X윳2`Wr`)T(eU-(w:Ox,xYOrI3ArF~ ъqZ,XCf)n2P\`8K…k%KAN RR NN:J0 ׌J!q 13 P iUKh.ނY'NZw-HNTfaMݠ(T.hOVPIvϭ߳8gJ~l2i2$7k=\Rl'Ycc)o^өpXAYұbN7Nu!])z;g;a.=<Ȩ8y}Kӎ= `&| 3К}^?ǜ mc^d?럋/r%jJ'ocZ5Ëb[V"JzBGXK%kddYk[eoYҞ]=b\ el޽"cО?/e~4='Iw@i_?Z=_cʯ~敳k7πVp"׈e9eǢH:_YHN؋F`]_+j@6JZ\PVaSQ-JPV"vĚZdҭ-֙?N2wy`9*bY5(+q R2~9s:98]. u;?;7OCY5B"؂X"H@ug-4XmѰ@#5O\* aXm*7s-nUj:gT V<s ߳$Yg80֯||j{MA{Xѩ;O̧Wy[{̧Gݼ_N__Ƞ5*o}>$ 7<,q_wKO]~|?^_tWQؓ8̈́ɟQ\ 9y(g7r༫3&go¿y=% v?4 8UF4Vx4??^גS[_M>>?2~ho3_ښ~Q)8p?g9o\DOFJ*_)_admib<$Y^+c?߫o~c_yg}"=A"Xvel0 !iTثSĪls)RbW&Y@(Ɂ,h=ud/A64I dnF,ڃU +'A%{\z/N畠TR;u!'8䤠q%*S3G=ov\RKuB=o-fBR Zw l`jۂh'Ș8AFb>NHUQTZaԠGPPFݎaV `+x>8Ϲ`K30Utk͟Û__.5O:< ږYo~!qi|`ٰtɑE5*|2Nn5{ԋeVtݔIx[9x8TL,ZGDI۞?4=FFhAŽ1|eπ HXXKgvEiq#0%4JAQ`iT-iQ۳*ij*~ʊs]JiAݠPT l)@ "JF s KYP߿}/*YυI̝-ߦг~tڅg~i+hbtyת]ig&!3YSXMT!*'DdUm(T&B>*KZUD6T%m(`!ņ/u2 4͊Qh(Kpl9CLؒ }m֬8t 5 d Ѐu p q <[q2;G6D WaA+<5bI|q|+vZfX2lxOҭ @((2:mV\J)ͣ[ fo77כޟ/{%>%&/}| |~94 X,J]'#j{˗p页/&/+WMjxg~)~x?1yj(Y?m}!V2c)ZeEz K󃗨wzus]f୦ec~S?Ru$!%Z'fc}惻wMҳL$H촡>Cz$`x_]{r"/PuCkVAȠ(y :YX}|.m n VujَCK?~,!JXB5<2]c,k[mQ9(8'q_*XL)c)zJHjQN[uO%dk\+啬ȱdTkѬ '@-Z +h=^'4fƚ.1 Pҷ 7jX'8Yn*65=RR#-}3ek-Ёvܘ,۳z,VjzIezQ@t!-{IwJR1WnP3w =hB:dv4p_yB9A4,JAUygKb="PU8Q,, SK3{[7y^YF$ƿ1\'p:p~?{,ԏM48=c [7KXޱ?j_'}AT/inpX|OiYuI B|_]7?6yڿy<~ۓ 4stDװՏZvTҲʇVf.s1oeoc{k?/'ULbsWm-U~qoU'w]}!cǗ)XzIGn<&ƽ/P~Vlh#u SpFDI;WvmT-uؿSkE>^ݎ/y?s6qSwURp ~+O^?, -;f"_=ՑH=% pbd ,w!\TA1ב~tDk*P1=P^F|SS{4Karq3Rs'E0;M C)i.5źja1X4bcOs)i.h:vd P)A)b@wK^wTo%C%@Ou2=bci!VnpP !"}SD H1dU .Nw_tz}zo8n.򱞈=O7BӾ!Mϯ=Ihn?L瘨_p޾4?X󵐽:Z*Ѳs1C+}1@˞c/˟G3q&$'M"k׎X8V N2~^2sl߾ݖ^w1D8zqmd7=1'3T<.o4[O+ɆA./ov"N;WJ4Hn\ˉb/D >βIl]`>ClP`GLtκݭʙrrGpPidi:k.Kq\`$IN,A 9!G+rרhAqqj6~֭a*=!ߟLMƏל'pXw[0}ߵ)8U`VM3DJPxx{nXZXέIhXo1* ڋ3ںE)C>sz^󀹆_hx{fv̽| yJ>x6Oyhs=S>x4\03|̭/|^?" qFS&Q,_}YC[CPV A^dEXHIKQKdhUH'+(+RR\ գ]+)њ`niCG{u윃"vYJM͇n`CޠoL2w8I23&&U 9Vκ+|Ckܣ>'DZ8wM Ni9@`gd2[hϯǮĮM??/3`-q,,+1X (fq`oKbvJ `BE`Y"" DT VJgHQx/p^B"%XkZ/l ʋk)/բf&bB^ 0 "]h@ *#߃(>ux?1Q7HotvHwzגsӽ%Y=a?v}AP>TE}k<=*(~\|~86Iˠ}xQ:m/dd'ԱN#O| =r e8Vo+±yr3YITĎUQ9&԰ 0 ҂NFUSlk%㠭SQC $kK1Z\S Q:ZGEkflb&tN:WȸOcNQ4 |qǪ@pG|7K4>P:!էٜ=)JM&j%UD `)w{yy핺;nӬ&f3ո*d*ܵ4 6TԷi}JR6D Y@^q9"hs|AڶpR5YR+h;)m&U-yOL!ŮX#qfͷ32_yJ:9^ݏLi^:dG;wz)io7Ծ υ⺛֧zҦEWOK}`_W_Uأ]C카O7_/0n3ԴTQ{ZMp7i@8}5뭦} R?Ɖu"]=Hs=u&C D1c~+8-ӏ9k_'%J(E2af>NOggx,iצ5Re^ڇlF*ir} Ԥ^ %{|8?6xRz[,s̚ÝGLG?կu]]ZHl:e !~:DeDH)8+5dYvivŽblel΄q*H0"U .PZoJESAS֝r-*aB -S@ PGu5npu/j_HUT(-PZCh]GQ ٍ>]n>z[Lն& ?Hj_nO QE=!5[H(8"~G:G/h&SȘWb3%(@D_!BZзS ƀ\sN!E4! c={sJێj>399]RAf;U`G V?z~p~bO]~׮󛶁g#3s?iXοI>i}WXq[ѩ|` {[,X*|RѰ}.L޶Ѽ``qN B( wY{}gå4m`wu\~HӨԴsQM4|~}]z>KZH=~_07kkGG|םowϲ}ySr g-=ll\J#~pnr evA ,/>ä E9!^ 7 !-ȗ$>T#HM_" `92"²^+x//rtnjʭ5u\r)iM |yj${\' (@}*t@4Y[լC:ֳ`ص=pwQ&z]X3UzG ݁2U D,5E\5QT}VYAW"xau0W3`*Rfs? IgpʫlCQ(@P+ƱW'k/T@ñfplCàf@C8l13(ew]{`Ya8~m/h|m9(~^ 屝G(xDڂԫ2s#ϓxo~}XGb`4O#<MtM/Dpʻ4Oo!Mm$Ez !w-={0qܥ, {iӲ_~Hi|~Y{0S~'A5&9z /<9pI'7O܊Pj|/Z}7 (ojˇc9C[Ost!$!IhbcQ(]?s"^l}l~U({e}u-Z/QBNAkvx/Fb+'s" qo` TX_EPb@AXiaF j[;e *K ,^Ɖibt).z,ɊG?e}i4Bp(5 ޕ wb}0sxAkc{Ip\YD`ܐ^ (LCZ(V9Aki2TJhXfV9 +k;ba-Ӑ@98vZ3;X,%^(LcC @A0{O|G?z+w!HV(k6?S2?y=kGa}ܽEn/{׏댓|}˻BZ%QB>^>o+͇v}LM//O c\A>'wk*8+Gs' ~g/ Ǔꝸw%z:!L.n}y( R3/y惣9,K>I.+]гމW ZjR~ecFM0{ZlhS'w"SҸ(?;=w.䉱.2) w8L^3/+H7$\|zjjWyTA/OwhP gGshn~錏W$hebhCH@Nb-Sb[eED)e1.bĂe_[S vI3H諌]U)nCCEc@0JV4ЮO8HΩb} ^=,I#zRslVȉ(t V X0J #M q(,K7VY8oV+`Zq ـd=]{1.zlCI[Jw s'Tnֶ4W3Bmo1}mEJ QVՀ-) kkY]X@UbLCZs6Ol#,.r[7f]hy>Zji ]?!()pX? <[_؋_(_0{0R'J!0ZUl)y bhT],=Xn~԰rSX[Hmt!J9j([H](=R< [,p˙]wܢ\OQ뽬knZѫ;Zj/V*NJz}u9TnC*w7؞ `W4r*sWfQ),[mlC6кwm+%j*m`&gC6rl@ Y4`cx*pL%fMmIC]u^d7`Xtiz#"cK>O?1*E>iXN{u`5HwcP>IXOۿ ϋ|{F^L2˥'?R6-MdaP4mCF^q^ /9T1vJFt;RxomEz9ҋǜ(?_ hM>ZV4 /1aOrW;x_X !~},//'ez.Y呌r;tcdAMϒg|/OK_g/gk2c=eaDEs"c!4/c-FG9-m9eBGeW)n^A0lmUN {]  }wC$wY詞_\?v`oIىݻ1@= I^Mkr F`y@}s-[sIV' U VUGi( WTCVPU܆Rlm*4 ty*>΁=:;gI4g+'waVV|<:xiJoioLAh[/L~K/-MƗZMYDH=?$$L= 덏wo}w $ľꪙݒga#Ђh 0LB;hmEL&"L `ha~LUge#o>~#=abNg~ss"ʾnΓ7?&g-<:{mnA) j0;pPO~sU?lȾ uñ5KTxؖ諟{P*ݸf=JqYBl8 c^nh%[ʣ#ߨ#ME>xnWϹ MGx;y|tv`)q0%-Lǁt(7{mV\VV2p;vY{=ϭm"<+M6Q:6!Z$/d[ 뜒 EnЊ wGoy?^[Kk3Yy=#q}Xg\wr~c,VX}G ]g/ysrw6tNTlzN3al}cmx_X3a@ y*3Op0x㼽g4L{8YU{b!jx&o9o?!ʼnزTf3a7[OƮ@}OloE_Sc^kC? ëSҫ79k#^~NU8龎wШV#'`ǘ7G잏r2iUUwۓKseYJ6w8%dZBHVҰ_A8ئT]Hӡ[Vڀf dsIrRv`吏QKtId.۵v%+Or4y9lb9`xu5,8Gw>sM(wU)g cQ `<2ϝIN'4?ð PR-\P`-J:µTkޱÏgRϒ8L߂w;;ЗDkI w&VtOzO9FLSڔ2!b+px5_@ -Ppj6#hكe"{ĺC{tLw"CAf0|\3݆'nqڵ}'c[L{MAG7\U8<ĕsb&h@ DÑô´Cٿ30aڗa٬O O²S0eqSp{L}wA2z˼֜_ */߹A-/es8=JG=5y#zkqjpm.#(I]l,[e=]}fns3>je5F=>s 3# !ڞ[/218]` ٗ]em.їf]C Hpbgyn #w2s]gbÞc2^Ƴ<`bǎ_]Ӯ$*OQnr d\x?xa .^$pZϴ/%3r/<Z"V.(efvCgB,yV25۶T AxJ$+s l)PB(]R:N!ە9PB ,Uz5(A@^gD o}Wbk{(idvyol8Wvsd0N;2~C_@ʃU^,Kإ]mF͟@K縖?987&<8#Yw(8wfO|~lo3-K2f ^Ե0*^M緁y'K̞DsL',F&]Š LwV+GnwyΛ8ץ(#?+׈g;zk=z{vBԍQoQ Wt׻A,6oLގ'rm=*]jVqT}~0c0*6nQ)l\@z嵿d;ļˆHhAW]2RYKzf³/S^.y]NSJk֕wYifCRSr YY 5ʄgeI lr=@2"W.)sMꕍG#4*rf6O ?,[lʈyqB-Hnjq9 yM4 x;}u|1('*Xwzg3q+풐1` 3%sn!s6Gz&֪<f#F1()+hyCV}GS\##T7)҂() yT{ V@r!UR+JyLZYRIhi*IϘ i<)RIXs$ k]tV٤J^dsɗ}߷<r]>|eh;&Y۳h ؇vCMQ;>}D䝅U@l)Eqp(>Y/4Ev'/n5@">isl+JRU2w6i\EޚޥCxr;0/>xRr_:ƪt Hrme T-:y>%7(L.x]s\?87lXu_̱Z7 d.9'y* ڶ=Vɢy@7Ќ%g=W867{m^=]!h~殞/,ev@)²\N1@Ьms媼xYPgX6 hsan8GlIK$ehǎGbcrdc4EwY_f7 m\Ho^{1x=Da8,ٗfɤi}BǼa.VEتe[g9 ;+rWvZL^{ʘ!aYӭp wUW  _ٵwv~- !F 5L@&`v;;>X;|žmk?BgK6D' vR#hQa6Da!vB@.,@! võfKG.|Ӳ۩,1kOs' L73 C jwd-{ݼR;puQv<^W6bdqO|~8}-Ae5sx[ 4gm%gI'yP^_9`v!hdpr`2/GMxWƮlbs-jGۍcČf2Ğ>δ[d/вsȣA8rD`fmo_Fw07À6|n.ҏg4,~0V~.1yܓ;@h0d_A))!Le. ͌Dj]65wY锅drJM )(#|$X.Rm@s̈́ݑlY/*w);lJ:@|bq4*RMs} Au 1L@:]qtw"sYüq{KgP6vtKTԀ"HDÈP*: Q*HZE4+L,|k8v3iۏZOn:쉟̞ܴ9~wםݢV_j6@t/F[qǻZr戭iХv*^i"]_ m稷;wL]IϦ:cD6"=c^潽k?Ճ[T{/fIL[1(G]Z 6 ?,{b/,Zt-@oOGΧvUG_h?kdc}= hr`ˎ&懆:P,rW|9}~}u 5T ~#_cq6(Z9=m'_zFLrL$pleȾLJ IVJ,S%34X "g0Kb&CI)2] B .kK0 )T iK>K&[˔)%d9,J,j օ)ŢWK rvhE %Q`Z@`4<҄Lar£d")YJd1Ҫ<&AN<{2&EZ2T`y !#B((1I9@Q ZP2PHFg}@jEBP uNBC)KpR&7,( %FeYPHR(Hr)rZxd:р&pBP%AD2+O+X  0@K9%ֱC;+*Iy yُJ#G:-jͻne9dVu(h VP/ȳhUp{Z_O w=~c_#^?9[_!`૿v>1I94jҲpxX3KL1|#h'6u1x. ٝ ź/3<$ =}&o9#{K^g\^"S0 ˮO4KAy\,/xM74=VUT?6 ;}zU6M?^{S e&l9q,&UAT# Q "/_ou$#Z^ocїonh]H&ٗWdK„cS*%%x5.,ֻ,Y"g.#}iV*\1KxeUoلb&"RsUFR2#(iEj"*|'ِU(Ň&V{/. 0zUUv[Ui&_($^n.ʓu^eKKI9+nJOy{wppa `=ӯ׏!p2٥:j";,id}hw>icni s_., 1][_ݲ0H/<:isЮ2ܧ0O[_29W]7Vj}2 3wyT4ir>)wLRR8oyxfKGEs1M ɽb!\Lҡ@ Xص vAWBğ'aoW #۞r7a&+ρVs=RQG6S+qaX!Lȶ)U( VJPRU(7jfŠ|*ΚHsK̫S˓A]M3$b"=OºZ*!s&{ W9"nO$"vh0mO-{/45;D%WdUŒtӟB :u-} R0o>q}"hujWc3}vL|9͏F~2 _"T&3lHo9_![v]w6@agv'>0abXB\Oĭڶ2q4pmsbz^x0bj<oH"F>/[ v.2 Q+}uRDuOX+L8v_n^'"a!k%QHӡnr&4R&,4XU`9\&̘9$0o]gyX>BXEr~r^/CNA6/[[^<0^PDWc( lj)i`4`YHİws g.`kUQ,iued9;eN@CC  )b5IAP$b!ԹE1Hu:Ů IDAT0jt9oy.=RsL P{=b054qT۳5qz_5*6Л; 18vZ@6`\~s{||Gv3c\t긹A_X[W/0u𥠪ƥ#ݻ__xqnsM{eezIm6 t,g!h~4e/擳e{JAaҢ7uqq\S{uu48UЫfG{ 4IzEIu\]T?C=vz#^k3<{zAʃf1$_}/?koDBIdQ}Iu BKKI0Hzfi Ŏr20C$Xgæ`nMe ;د(w^ ~y{2?dyH;'-t @ٷ>qsȶ=F*q홎+Fmlt㺽ǧ` 'qෝ7ADž G9ƙ*9l-O~_ެ2T ٴ[f =RSyFx@#3hWKf @KoH#wcn wC sj9b=Ӟ>,m;^;W~ܮ{76|KM,ā@J/څn {"*۷ߠ xc(^@_>4·`B3܀g얫NV6C B-˶uVJP,K\˺sQ̄/ &"dR j眐b. /sc'bX&&g}}D" Eq>WgP٬ETUW^"SU}3Pcb},vhĝxIw7c.*Yôݼ~88wٺS 8,gُF\vO*0#3kvB=otOvSbj`&`>;|\Ŕ\/sZvXh7?04kb6I]ܥp|wPGx ۭgWϞzcZkL$K%Dwsn5F@+<~l6;yOÒm7yc^+wAcGbn~3BҤL2k8رT+ 4XkŢdfu8bxS$\3ck B0w9j!ʺ2 R] Teb˔9/J$9d\d:-\gÖ:E^hf{55OLβLf' Ä`,c\*ـ.6v̱u"Ufv 4*kAs.]hR1StE,}M=??kk;0_G L>}QG]ZԱsdmWcmm^?+U`<}ݝ:C[s|ùfBLf1q;a#vZ,cee8+cG(P@k<ΐ$5L6ߞQc&lZ2(V#Vm{>Ҙ4+C+ERPTLb "dT&5˶tXU`@K@E2EA P:f@J$ Td%3'AK@6HdLliq}?U.\znKHxz쁓؍Ev&nsW/8f>W4tT@f]Pg/1٢KKoY N3}oƬQ3_ ,=Xä+( ֛t~ZqϾdشvpTl.67I־?M{gJtis葥:C3ypB*c>`&Gವ D,_<nm5Їwܭгw޵)ML5kl*OUQ$'os.vVʨQ[1,v3_g9h*aNuuuvyOkß3wϯE5(e#`%e]}%EH,e @EpUP +v]9Mu͈miG~ekÂJwZrM6*BIì5)ҥg\g#ׯ6}WaU~wgE6&]}sUy(rszxNK;3D6d{8`>;4WYuV(M2' +TN;µ<B šzZ8|׷y]1 iu{C}qղOu|wǻ9UtxѥTWoeԦ6SXyѼ y\ߵWi{y w{GM|⫿ۍY#[_8K6㣎*zng 4?|!U&,Ψ>/sO˜pW'D.A.ò}lg0Sdؽ!ncH$1]z4is@%:HMܡ]0y̡nQY_ \f(_WNskngts(K ɴ&2-/Nn± N)2&3 Πy.k2`rsLUݟ+.\\=/o;퐱_ڡ= 7]pgm}F ֦ {1&7̀8H @ضD[?uG?FԡǗif@ \w˿>q͓u %ܠ+Д Z[{'uox.^|'!1Wcoؙ<49p;"{WÏ*</^s' 2omw|Fw!V=,kGhrk0{Qh<̕1n TRbC@{iST5g᧫:܇s?؍ MJw~Iˀ˭lI%azpʻiRHwyK ̠JsnPReNSI9d;4gEq`!uP]& w}q…N3 =@ovi;1Ąl[4NrYaN̹Dyey0\[5QBiV= L!k  qaʏdX\/y9'<~n{2j/SBaIЄ_;?;Tə/w6 :W($N$ů_|> )XWsL{m^>Lsp< g 5\ff|Q\#òu ?g@Sb`KL_ǗCc1-`*yPPc;tlFwy+io`Cv3C*5]s}sk?ڗ+2dk!JRh/e~%H2R6z `ZE4iV))׻.Hub3l0o9@;r5KQ6 ,ym=^( kI}/A`[ }N 㣄bmôms!vcLL}vWȶ-OuR4UFE>iA+B LP(9S %I *ByDPZ@J4[AKo_f6̀ cBdY{ Lq}s:kT[r=78ߦf7y,*g uz clk az ~i/m'+A>h\ܙیn _3 4?\ D{W3p/Y|ˑ&t}}r7RTHW&Y$׌m&6YWgwP)CcHmؗ[ʶoM47+LABeOuDܲ1Ԧ֚#msZ ;`dX5I@w By, vL86T`!If9 j/kSW9$iE.À0/]d@]nJH 7oxg^}  w뛶V˱\品8P[w#Bd[ǦG[-1<>"o"O9z.\ަ^ ^|ANn*ubyJ u.Jd0Ca$>Da ##2ZH|o|?1nsLs46b3.3O 웱vr@|˝> M3ޜv`S+ݨ> `}cG"'w> M$Y`ϼ<׿͚n>))X sp<,g ^0yrYaSO5862Ioʿ8c^thÑ4msc|f[];t9"#ќNؗ59/- NNἎsw]=Uvms6ۓ}_n h˲L `VY鄥2:$Z+[1ljn-]d+|px,[ˁwyW/v܈۹װW/P^~yD rװ?ܢ]@`'zqj@Kv1 ]&>lnҟm/22.?Hҽin~9}m86 4? iYoA'v v}?$R0ےR0 ͬ4 /s.aMeC_6edVLRRszeqY2ˀavsiSy6h2`.G,`4!N#e}xO^^$v~@> }= 3sϸ\i:xnzF۷KWun3PnsuPg/9],= 9Ts*KA9ILnT#:m0%eA,%UDZp0ϹgV^a a^/Η.<ϑ?#cYm?7)b6xm-~7. Pf. 3fAo~s ;F '<ߋҤ6[A4oo@8 ɽ@_Om΀C3p8 ˑu+=^+2/C~@e oosڮRfq7BnN71fV_R\m]}3ίy}A{{Aҫ/"M>=R|^roױvqWWr |i&mxO>v}?f6`~ ͌DjK4su,Y锅dΠu?^ȾBr,{ 'd أM}e,ex ])ʋq|Ym<m>@O+{ږ{cGAuD)׀WDy~HXaQW<ہ28sy H-|]-/GYTU.sKO~~N=Lj\c_FcUh9bGɧnke-Ȗj0WYkfJ5`  1E B 6dFc8GYcϓ9X1rjsr]w92ۺE\.5s1KbLe2 c;\uJNՀ. X^%{<$ 0$ |i6s~k9 m~gxܺ*լ%xrG%^Ax&!TN%\+_{N5Z;hdan`1W]κ6Rf`6k7Uy@%f^޺Qk7/S͹n^O?$6xEͫx^>/2_qN^6ϑ^bjk-?O.߼!i)7gOam s̀\Cchms۹P ˤ|q t?vj~M0kn?0uM8Mwr>&o_g^?Y]yf[RjylY@_c"jySi].R3O_ Ug.? ,^}^]c@莕Dqgϊ3 dWzo3ua[t=U=q؂gy~Ŷ,l>а얨jXcDaiAuNgvjµ>kڱK5mx|ÒCvfJFz~3 0 @ 0 .W+Y2:_vرcE:pjN!O2cۘx1)!bwz6>| X+IDr6`>ȏNB ]P O, ]> K.d"s,'2?W"^|1\P4o;8>i//Z0e 9Cz4%^欘e.Y5n">o{o,!\Pվs;yR9̋z/x8TߣQ9VV@ ˦Ca jFTG_Oի_^*fdK3ӗ:=%fqd.g R0+2/&ܔ*TY0WK$0yQb6d_EM:w9Qb1}:>o?=q\rW;܇ %"$@ Yذ;q(Fd#1Hd(a#%#!$ b#a$wRCLOO/ԩs9!ߠnSq=R砟*>LS+JFkrs?q˻bN{ ,:ǽC70|P׮x| K`^%^ZUw׮K;S7xqk/$?_KKװt o%^Ϝ .caa 3o<\ȗv܏+Vp?BhP)@1aH{>/ ''!d<:XF= uB@QC d8D9<"c17KHvާ?$'{@XB_nkתKK ecU{i{y^Q^Leqx~h~$1Et!gBv=ðB:}R^塦݄r;:;/V2bfMcq}ү0`bXYr0)lB2-~ν̘1.+;T(v@ λ|xt{R7t{R;3U<<ʏכ;ԇNWzjLt~gk鶓dZ #s<Q:/S/֠܎us*8&%^N d3s6Y*(H,й8BDMslQ Uп̕=ixU雭z*F!7\Jm7'J;3:p=t`;.P aZ[ "7ĞR}j'f_7$Q?l˴Cf`xSPm??T腷{ݜQ0f[r(+>c}r 'X_[s>.}ƟaWZ%MR/*j4:be;-\zzRYR }6[h*5 Qej/a5#pdDWʰlaNhҗOFd\Cw%㉈g,ff=G p2&Z*pY elBQ>|h"ך'c&ǢX@їH Q1"PڅJ &50"R̅,2@P"P92PqTDEP*GHir"X AIɈg?_*"2N\##~_Z)6[6llsCN%r9NVz"b䅲 ިd.% iGLJ,{u8;9Un_߾4+Ζ lg=1mٲ3Jc- [6*_~n>oƵe,Xj+[uÛo$QI0B fM6_f\fq~/Ǫ_.mvcn}eސYRIy} ])|@8vy` ׭y۽[ef1-P>_/荓W^y v-¶]-zew )uN*:3lsLtLY$ s" ];u3M@>D"yZY9P*.㜛g ungU; *ƽr6M=a~[t^ǯox 1~?ӾTi>\&nvmjkk mm,s`~柽6mK ϲfmv=#6_\&#2fϛbO^Ykal &{BJQ+p;7a&H R $W'dd.CK?Cv)xxwvTˢ캀^NF+kzZˆWL=)<~7oV9lfU.P^ q/RW짓ٱހN*)5X˱gȹܹ[Z94`έkd_M?hKS ? yV9? xt%:jhWS,:0l˵WND 5^ckWi*0"A}gHU:] `/k}:e[_.5(j[.mo |o~Cv!8fM??7\XSg] xT_`΍M 9/Mtfͧ2!<9:vǟk6JE1U +* 0ߓߓ%بYΤ1Ἥ֍0ym][DwNY%lWݴ.oVSGNS߬=bcU]qRN(R]_r 9YNB¯@!\R}d5~5`ncg.b8Ԁ9s9;:Ge71ZgLyV9 h@t+vU#_߰5yf0x\v#¥JUuxBZ]gg @Pa_!пjv{ -1WWK !"mc^ospf)>hQELyizn/s =`WzezXf'@$Ț5~J! I@@Hpj 4Bnspcjv:{u{?`1MxX@"jξZ*`La,yNDWZ:I+v uWC{Ve{sqp}.Om/A2̒ TA`.=̍R \0bȤ_T\( 劑 8ƨI15Nzj*IA!xFaG@ D_%\ _{.7l-fy |^qv l?g/[~3jx<عKkOC?y4ZgGGM Wd%7 ^=~K 9c]y)D?Ya@? |EmOh#=FKdXLT:&` 8S<>NE c cg'1L`ɑH@rA~_:NYNrer" pS($"r%SeY@ DŽH*pkB!,#J,8__?r?pˈ/!C\$箴s.yGst[z,n֠T5ۏx# ~D Wv`a_b' w^غ~x.[$^4B#~k(3<4cOg/W!agvbu׸{uz8NP8.Zm$geyszf[y?^t8~V](C("Y==o3n;7fvl;݀]gkz|ÖD!ڍZHK ZљJEB˧mӢ߼v_.2z<,x-{oq%{fMpQ`n'&T *TwY/y'eܼ˧[]b.>'CǷ ⑞}{n|lw/ٮ:'Lukr 9u>0vQdC\H̨(u.gIDi! #)rP`%r8 Q㬘 ŧ[/O_͂F>Dw]gG_KX)ixP,h+mTv`[~~#nz<Ěz|/=@ ^8K oE.<'<_jmqf[x5`:0= f SDBa3R|=jX3u@-m;Au;-d6|l_)v"DDJU)lJCua ͜ (UZ3?^^޿U\FZ1{g;ߝkywn. IDAT cD*$b) `:~9 T1z cO%m<-<»Q./͊e^)t,eS LZpsgȣHSWj"y.OxopͲ kl]( "*OZN<`8Y`%fZfuNZ+*Q?UhqMCNj|rrB NxG [gcAa&E&biG*$X"-R/L 4p(2 EO*@9d!U!SE*B㬲(bT{B  ;nÍKZ ;˵>4JW/_geـf+Xi`jHc.`Z(XUvľٳ:j6X7~ *{L?9 kTV Q_DDžxOÉ>Yoy7εeLڏFTp!&Wes=+xJ,ޞ6Жֹ6[ UtWAtU=UoK}| 3-j.- ,`KN0{xQDK9; ETfs!{=0ˈ[! +<5 M% ,,<\33Ybͧt@q䏢}ܖ=h[3p/%۶yF]㝻XO#'V^g;ֹ+ssW)'G1k2Մa%A,e B(+sz$yH= ymr8igՋ6oisI z윝9OcۜX栴ێ:z$&!xr}gkhO>`f 9\xG9?#c%yj3^K+ J=$tu=PAdDvj'hbX3FərnL8v›I6J]:?"1rqɚy7UQ,VY1&`v\~e2Cq`.T\`NE7,eJ)j2Irl+v!sc Io1IF/s/'14c/q.5`^9ʷWn#iիcdz `g,_ſG}hvmӁPyfL/<+v֞b\v:g"+8Yءrm0b Gwh2ci  [F/#ǔŋg AB`+M@C>r&9B(RPHd.2ED \΢H"CByU,TBR9GqVL AeDݟ֥h.}̗@B` },(>ԿmϪ&_/Nmڏ`’XmTDWT֌ڴݭm vt9:%+J_Nm:׷揂|3(7ѩͲl2{m?:cNeNZkӟz3=yKk `qpyj.9Zu6C+@u]J*{U^GD]QHDZ}dT@ґT%fnG#ߺO[NЪD? P̺>=Q'AdA PY戕ʂEfQq `r0? I)N,2;]أ3\n[n.Y޲l`C ? t;Ai5˶=K3v^ܡvu^/']8q#Tkb֎"JFD`]3 %Q !TA$+):D@!x$ Y ŻO͵ࡰyކ cNZOR~Ww$7+p]3},'Ui{CYv]GgۙdYɅnԤaA ^v*G#*Sc\<_w:O 5-oX@̀*mlyr:W^)|v&Qg1ڼF2mA\ |O^@~ꠕF=)9B LrvڤU>jp]HEbXWZt 9$Uۣ滝ͳr:ɼm1nm]{qWW[:-;nf~.:aytA%uyP8gM׹f8 ,*C 3k/ Fyb0q29RbLh\ T$@"P*J!!P(9Øb5A s2%)ρ @CB %艽_y~0Ю.2Ѓֿ-})Jլ& سe{>=,a"AHK}dF!60˾*dۛ"ۏWɲB@^%veW OiX|o/4_Ѣ}|wqbe̳˼'7퍶BN!Ua-vF/3<9`gxb+/=yTك_0 i=]{}vl fu *Q$@eV)Se;agƩ^֧͠k@;f=̊E`+2o߹;_*c R)`Ze2Jy9 DƀT9vsR*v3Yf73JXq S&rSˆdƻٱ'U:0ǃ)ؕwF)tx=r YlN6P5䔻8,ټϧFgm{<xmu~Ʈ=Ф/  cٵ2s6ά0&GBFDR9ӹ(r=IAADNB9Y)D,@ B!$`b_>o>KГXٴ_;0?創xfNޝ?{ls{{^>hom=>(ٳ%ݟʛ6/. 6W|:o㚝r 4M f̝T%Tٓheee.O- bJVzOB`!>4e5'[V<" 2SD-0<Ɔ=H3X6a4fkv X2V$TY>kw?'dE X0Eά̩s00gـ0;tR=`lr/h..㗛,0` =v|6 <,_hy@=ϕo~`# Wegm­[p `:9od[#+sͮ-hڀsSڽn|l.bWrr4/s%׭Om^fKed\zXӆz3E=22 \)VOWLF-s⎭v 0_4/BN{lLڤ\Ͼ8YeV\_նi*@l~UPo6\o;oEmw`|23-*}@-_Q-Hǐz3dـ\1b=Y<s=戈Y JqBs5Y,^ID"eV"cRzA(f*Cz * M qoeo\so]ۧO[S䲙*/Vp 7BcSY7ozzg;;:44/JwˬAe^@xMRf}Fw' D\e՘yWjovj3[rB:9xzm\B0r$(/wԃ (p}һOu6m+k_xΰ&<+f`HH@O f$Pq,U{JM0'@R6 a 4pFw ]цt8-k9mǴe[V&`ٓӁQus{^_Zݶd[/m-i˶$ZmU`:RS:kkJK% X8'4 ¦*jTD49yDC]rڲ R"BQ*D!(&B "TH\*&!,)jGQS+"J R|oKol.Z@?[gpoYPʵ+J=ݰɿA/2jE r^j^_L~POxSi>nEI 6_pdZ~fKAP{ K ~t[/6hןۗiO' VBbt&ྂ]~+fyyN/̀a^ytM#]^SY[N1\9,3X*߂v񄟴Z̐2iwsv6slwcgUf-Qu*V*Y{ކMS@ZD渥 %XMYg\ VGsuq. 'c@u]K;{/oM5(@aPR\f{I#gf0˱u:)쾅p:~ح+m`(b~zb?z\@8_|?b{nt*<~_iܶ%:޹t%l6a{UeWݷh}yD淾wq%Irv5q,|& baGI[Kπ2qǔ^8 {a,q6pRRUKLrBQ>|0RHHB^o˸)%S!&@i"PJHE`T(KDz y* AR2C(U(!pe疿FOKΐJ $h`wUY{z'a~P˳o3);m){_XB. a&;R$x/i x{{c)Ɏmv?6Sz-|8.싛LDGu*ĩ\)¤8Xf)|T7[z׫|T~K!U($cH?6!0IGJ)q`Ð[@Zu2I3 رm;Ju ߻uCah}DXk+1 ŕ=z)؇ۼlk#4oO-3 ~,mF%uv#9Dk=+ٵClvQW^\ɩ32.ӁF<ѱ.1O?K,CAХ@UAX-RiGKkk?^*9[Ǔ,LjPy=H,nj60)O Z8J9%GPJ,d%45:B_)f3PR&@Q0`nYi`_n0ˣ㌃rpwK<`B˱IM]2pr;04`yfX<1YAl_3Kkc :&"`g)]Ixj yt h]a༿|Eu(Wls+gz/i H`eDcDpB!;B9Gε~Q,%pͯgGߩ5=SrzG_t[SJpg}DG|^yW\?T;A{)DAƐ=낹l_]"2={AƖiz#_^mVWr |n|(l5{V0-/?eymeY*r̊`8>O2\H6j?Uu^:ku `7P֫nPfo|Jj zNA3򳟯9/T,sűj*՟%KV]וSigs-ٿ\*cwp4૜}` lp)D?Ya@?+JK%qt|L5uE/rMrqcDrH}T2)NCY*H BHe2"%U !CŹR΂$՛KO-~c/!)'uES"7M6 9[Od6qXz$G|5Y9uUvYg$bvM\nbXٕe~fam/z_ ̟*c4:ymwmv8!S C.eeЁ4&j Oq\ (Yr }z 23>bك|gbeRE9 `ej)GA4I0T,gŞj̊fÉb0Nug`e{yml`~ ,r^eeABoE9} GhvCm B4YuNTtĉssqNsqZM_C[sR7OU]jw;vz/'fvZik붷|6/ў-y2U7\|3k@le~9p#eR>g^mIvv'XSkif#?Q՝wƣ*+! &@TyEyY߾ N>Vѧ(H8ff) `_bV)9PȔeΐAMd Ȑe)4` `EG mү&`s0c2(̆Kjv \*|;2POձ[Ur07N- /r }_n`68opOk~}i q'6J{s8ccT;dogCf{g&`.@ JdT+ c>Q_,_/Ŭ0~??6`b\Zbw)/V\ZMH7\{6{v4eK ˽/!`\Qˬ1L|Վ,zxtv9Ԏڕ׮:WJO :PՌ#Q=\*MŶ:3c\':8;bF>xŚ_߽ûN(W^ bKT_wMC7㫽/ԪNLsQ’uCh_o }}toP+(J) +SvƀTPK 0`pL_Ty`$0A7=qJR0P0 &Q>W`'6iseX_[7+|cC{ ,{[#@C |!O ۟Ӄg;?Q=v:-U< ? s8?d+M6 8j8c`)RbgY%p]".dTg;aք`21Vo܍a6le8EC?WM|-?omgA=L׵w9Xx-:8ԿyRFBؓmoDث4os6{rx.nRJ\9fI6rgMeIFX[׭Oee>/((VPdͶP\n=*3`b]~R9U13ʫJ'VǎeSyA79X&jcOve25ۢ;ְo% n꺊oYAi/%S&3F/s +s*fP^20_(Rjz\qcYb `>}cΒRj%[-~e,tR[0hMHظ gv8Wbahn3I~:l~Mq&-U<|N"~=%6LTG?Ys+6iE1IT+,e'^!YjՔv_f<6w\ #4ݮ鰣|iWvֹk܌mʽEIBde_؟6Zvk#Ԇgme[j<^ ̱*!vܵ7\e>MY-Od奄fO27y:<9{vg2bf 9GB~R `Y`LB{;kUKCM5^g?{ޞ{ٸlwe=M]u`cBbA|#/yɔsejg֟]:HY0 sܓJ6dy3pY0Ks0+Fj̾ u#Ty1 hcs/N)y* n݂]IWQmoU+:k7q@\oMi >>]OK<k7t2;_FܴT"gCrp} 4SRg;UykjX+r0 8O*4pXI~O8Wp3zR 490'+l!4% ɚ,إVp #ZA3vYx,upA8Nvڞa>!365*$?:m!t}_~mO2i ^Z}{r.mvͳ.v?|jo\[Y.@=PeyyZf2ϱ#ˌjټ5[> ҡyJIĹ{-ig_Y*\N:1tj &̳AxjJ{dO5ڟPx^s(qq͔ (%-~ɔf)[H   ,儵f ̽ `6,ٳftぽ$[h-a6>`Y=\r ` qE]ϻmYn<=~9wtc &;M+ƍy;Wpvkx3c9c Z=jtdծs'@8mYN稧%vbԖ\dս/d;op^3VҗPy6NP~6DЎ29`i֣}) Xv%Xۛ!;TǿTXjBٽ y2!X֖ٗx8O |*,˖ļֳue޽le.ǟ Y{{">#ۜE /w%+&[8~a=ǪC#k9~=slsuzm3tDA |L);;;SE#_JLٚTf0e"b;Q6"vsL$A4`{y$ @Ԥ_m,f8R/n4IGm*!1t󙀲=, s:+pg87y usp s_%nVm!xyQc H$9f):)8ROz9z9MusQ1jbLKo7 fanѫ=ή~+]}xm{xA=<]ޥ\^ڝU,.hm߽KכC[+ 藻 )y69ׇ("e-Z 2rNk3B'.d&f@/9:“i'G]Z,lgSoa61vHl0!&E`<+%z&q` `60Êg -,gg930˒٤߲}Wq g_G>9X vbs;vjα~G*8GRkpIVMr"VȠ%%+Q[e,D__Z[KѪ7w0^a6@t,L]UMuyx#*a%__~;!͕m6$+%d!ay9, yn,CW{gk N%٬ٲ2Y~?5[t]pؤo=u;q~ K1v,6@WY% o<e%_"-vV~MbDfʎL ̣ 0O&DqXq A%q64Sls*7PٱG3uێ%m㰷OׁifXv~K`-grg3=@&fe&bt{e-N,5;O>#QNLB/|L`"$n>vcjfjw"*+ڵbRAzvWKŰbT"V0.}=z;)`☕bhc,T jcʶ *8/y' tj)q6``!O0`n0{yX8Y煣w.B!_pFܔ3y r/d <'gxE8nf0pei)˹ 8dǹ@3iUcـ\`d;s3eg8wrbC ֌oVyyg& hV0c-zUi}<~SJť=ApPSk궑:ym{E}uS$b.n_37*l}m鞱i8glvx!zP7˲ܴ.f/~3yy-^fO$ЈJe}Ue!P-#6mQ2SY:uqZXG^;s`<{]â$oSLQ\ o|z(/I8ff4= *VAڣ\ͼSa& })& LY5Rc. ۈMeb.$3B:Yar1k Rj&1dywoDٮ9ChK\Qd a,6la]c&`!D?lE^+r\wz^#32#sq#22zfv*Lވ̸߹@/ߪ` "a0KJס8ɐ 򗁮HU$mpz饫$>3ɉ ^,MydFl>i?GH﯇Rx+S=?›B0)Azx6;,ۜ&/6  {͟ W?tzkEܭ`,{eY] Q0<8G;~Oyܪse@#ǽ>#Pz"@*ַwc=R-}OspH<0,[Ɍ1P"@ ,"BL`C¬@l0MzE0뽯7Fv8<9ՎcSi?K!jK|#ޫ_?>?Tp n'/~k!sfzJ"wFf+ p!ګ}IvGxw{JshٽJfGAHIKʽBPR n% yG'X_ק(>B N+ ,Ĭ!!Nv<ҹ̢a[r14~_^(\Ay1tr! SUFT9Չ DSvT'̔}6Li3 # 7'xj0c2J\07c1|iRlJJ4Y IDAT[X!; W5kJjr/- 5 p^xNk gYxq]Ȭ|Tqv`8\6W0a`8`JQML$|rs0vL V&)S,aɦ0[qBz lfׁ*$ȮL АܴV1nn)T~OoP@ǩRnl\.#1{ۅhu;E;l]|֥7WO&}`vb/s84=Rf~{_xJ(P^L*4!I)C&d\x956CE&G'kkWa?'˼+yCאr!^9JS-J[ǡds"Sx0TsK%17@2皟MMiP|3,5ɤu:,WΖ[ f+^/;_^`~/$rP^^|V4|u d Zo:[K 3g8_ټ 6`$ G4sؔj\D5zP1vR9/-ͦ~30 PAdjۇr?zgֿh=!Uu8/Y!g|'r7 L#dPR~kW*U|ߊT3~[,KH_qֿƧ^_XTg3soh6e瓾OFs]*;-9.Eiڑ󔙺'om?V'/*'؝Y7ޢG|4"f{B N)nΉs%ӛb 5ز]z*lx'zG{_ pXv=c^\ spn.KUܹ3j9;ٽydI*6 frt8hbqDCUPù\_*&k~sT fw,֬a)Gu9j[=޸f6mYK8h)=[USgҶP-<=z)&_x,UYI-R(1HStf6ܝB`pu/xl=B@JB4%[t 4aSN^n1µY=9?.)W^LIògzcO4|6Wn\ Z ù̽PQ/ θZ#yƛt `4 89=_p,8B(U0jG*´!j@YFA~s&/Ӏx ZPAW(X߳h}#9`<-Y6X-Ny-h8N I|b}!Ma^-ٯ1>XEU:ùVϹc3P|O }0[KQe0I V7eӦ`]j*C%t*IzJUa lmr(mЪx۶{EQL#6oT8*B|/>هC\Zǧ>~*]qn,1>swycYfJ ^ix 7ef'z}&/s̻Iҩ6p^(4}\)iB4+!$A{u  :~fGa^#Gxiz3)L+\z b {ܶ27/͚);7zw>__7xa6Lg_i45aJes)/ycnc.s"\y0ؕ '4נl4 F/an6Ēm%ɐm}UR*.s4XV^>Sr>9u溆sY-4jCq692] g 1fvl8FsK[7#ϔs~)C%eƼQQR9~,Z`=̦h(#s_aa|b܆_fm ?@ ~]].wE4۵jzMqC|ߟ=~;OQ&Je S^ % eV؀ ݤ\r7 UBW3 @?O}03/y0=qTv 7*8ONzUv5|`FjFD/E곓&[no'm^ 6cfg]  K-m?np\2y!uwx0zB!e\eѠ'v+Ti xwٯHaيeKaٮTn˂cN1<`J `CKȒݐÌ`1^'/![ĝrr`gz/J4|9i# 쓃EEl3$ƙ#0k5ͮ U´e R;tкN^7s!7I;fl}sQLxc|x\ |hX~iIij8, '8:E !Fs6[yvXvV|7}>m;ɷm3֒ WqzD2.X0w|rer)@TPDL >?csQ宐dJ[g{sl!1y]{Tӫs}= )7R<3z"|Nwe^}{JPFBry)%K헗 òusEDC9c2ՔnjYGuM t)` f`eyCT'xgk=k0o-1#Kv;C}p/`nK/mr~yYe8sI*PFjFvVW~޺551~n|aV)ٮ%Uj LGAWʆif̀TJl ¦ 夾%w/Y .ۣM8m'cc>&|,C p*Vc7OLl{!_y;}o6";hJgpY7އdvR7eLER2w,3W^RJ}t HJ\H?&,3JݮmhH_M]w>yz)s%LMݱTszGx7Zǧ-9=Vs<SR,Hil$ò3ʑ)eKeRsq3Q1@q3y El0G _m`~ -͒ 5Xډi&Rs@_XN^z+ݤ Ƴ,3FyfI)ysp~p6[ gKQibp g8xBpͪ': o62Ti/uKe0m0팤XBۇ>ϫZIRV]y|3Lo7Ÿ{/L(ik5# Ply C9D=r[Xv]{+v⑍h2Ho4}8t>mЉ7okbd KQn~UUëNbFU)WK)t{Pa"!)^ NsRu59ϡ\O*Yg%]^k3e11k>W4y,"A)O4̏(nƒj9 7Zs/soz?W|r0 m8jG`Ml8?Ϩm+D>A͞\hNhN qCzi7ww@Fchv},A*R$@^1;v2>-7n}!:,BɌf(hj(şe}>Luqms7(E$dFJeл2\ a 3/,ەJegcTya٩z`1#cT1e[[Eìe/*b̽, yozV3E+zmm祑RTNl` @Ή͙oT^oRaڀ+C´y]zvr%3%ذ0m~/m!^g {akVhU@ jwD ˣQVn!uB=l#6c9!KlǤ `E{)fW#9d9dhvkBݮDHh &bgimâmuf׏k##ԓQJbHy6]Gj)l+}|/vѽ-5gSS઼Q6f;j)D>#EaـRI RI~esܲxS% 1/ ͇a=fu%5̻85# ò ڭǒR]Xt c/tOvkN9*" g7woޣҌ7sr`G7;-1l´h4TôI[*/LJiKU,AJe 8؛?~Wfl GW\ D8a%@+٨&dһ5HJ,urҧf/U+_ G!)Ix͸<Ţmdw;tvZVa4*w ?c5zP{>ֺ6IfO94$ԉ B敯X/Ì3tvSu2U zb= խO XAСW{z)9x^|c&ǖ0"VMvFٿxo²s,lraيʲ ,;s IDATfNe3P`+/5X躝){`hp#0`WUȔQCsAK`X`O| viڸ}U2p6if>lA#C 8"&xdFmbp͋leD*?L;#h٢!L0m]Yi )|{ߎQ}.nyUa}O׭7:t8Yj s}~W! 􊏩ϯzjx5 (A5co3Cl5o3[=? vV奔h]'.;+^ eh@FtmuY;fߏxNYuMlwҏDfN<TR]?n[T+|7_T;JȢ׿Yg69+⾈Ǚ\f7}su5?LMgxWJ& ˖RIa9FJJ"?,ڇaَ-;òɖ ²uy ,{1Bt1ͻg >X.?S` ^z|Ο6?Ql* k #̺~dgix :| l|K8ydgɶ޿{ԽIuBY:*?rٍxYvU ne,9ObmE'6s= ul; /GHs|0x7+{i}|ϧ͇O|<Bs/\ft3w9YpGÝ`d3,qk۾72foþzwѯj<2b/x_R^MzXٽg99d.//ec΂lsqAsEMyu`f:4.LZ.%M%;y`pXۍj;j7We+´ς2T&L۔*hdβb6J弎eYT {eTƳǧ|V%__l+X}x_ a# h,3H)5'ʿ'JWw3ܓSE V |\K[fF^Ir픽 юeKaFs$˷_v [Qfow8sa(_wC`[UEޫ"RPR\YެЬ^ >j~zꕧW E! Saze\zS=VO䍷iT0[v+327`²u=\Lc3컰&c\3{F_:w*ey1_UV7{$".uµG\|;0Cb(tA& g~^nxoazex)s늊ش!BRش5YXDH F<wf^7[s@79^?D?baN#*.W}a B͌cV6UB*!W:\E}7_خIsDڲDV; 3)!4f}ǥd_-kNiB3/CNGԏO-ɨi<\?8?3B`²`4$`Yun07bךKiY`0I/; @"r|55޼G[[w*1fN70C?>ômٴ9LAv3)% Xa,Ri򯖰A",њy ؽ)ĕcefkj"joVY@綾^-lݮ 纴?g{=L7`C13V Q;oV܄i^7{aڃ%a4TyFS/LȲ^׫͜SRldIdR CERfE /!eWR6B7@Q!Sf?RxAiSO8KQ_5O-?@|<vmvA̢]ZuvNJ5wֵn46%>;kfNf7I*4pT فDDw>c~;E %),Ydc@{{KDo֜|Y92X#LQ.7*غ"Wnuy%0SSU\bJɌP.xW=" %SɈ6aq~M!ƛ*/+/,{\ >9=² 1ayͽp[@]օ̝Y2k̽Ib :͒6BIS՛hk˅i~ fy7#a'i0mf&7UiMa\uul)XY@!KP)|k]o /-mV+θ»m?0l a럧q-R$TKR8ߺq%B[v o//=fv@oڎς\v\YBf̡ f̚-3RP (= 7~C>nO4a_{8U;kF'9bl4%wޝm@7,덲qeN>ٶf\KxpAWV ,r)Uַ/d^q\|Ng_8+_(fm)AW6?Os@Q"X{yqrfCʦ´Ɠʢ02'!Gn6`8Q)R S`&IB&7P ?5u\j=?.-ox/Հ||_'C媬R G/e~o@`8w#ܾ]׍ly=N"6_fy%i8\'ooޒԗf^{w6YA ZR{x/i,іb>RzRj<5FOVO,Ð MFӗ˺[ )^H:nG3fLs)eTk28k5]XxS_p6=e>KeoP1ܠᘽϲ²zMyM_N _[`<<7[`O^++Ϋ _ 紷Yz=ut'm8{a]ٴ7n! شٔôUMvɜׯLqv8kѤ`e )ArsUl7ҹHBBu~\ 8X"?.\1,xU+||eE43h׭LWRNָt%IP}cNsKfd>UZ&^t})͝E u뵅¡n/*CC/3HR TJ(EP%$\< v >-7i ^`e wXM;)fjx~NV{kǗf積Sn2f 5{M%2ʑ)bqkd!eCeO5([v X`~leձJ<]1c/4iX3˪4uyٌ V˽铆eG㹣 \~ HܙR~3Ώt/Ues<ͦ&aMi04vôy ɯݜe)Xe_JZosX ٠|6&m Ū-и}Dg6޿fO!q yTw<ёP۬"BYc]s\)L-fYջ BOOD}7$%Lmݎ4e5=z]J{h6}k3G=4;Ovls;0$B'.Y ) Au=7+ 3Sڙ?,A.S]d.YOy% c9D^I޿ar_GݎZ[r>-{>O?z3 \E)%a7E{z^s[*nY[y)7R.(}?4g߳ $;.(a'I$AϞvW_AQ%=9Jpe&1$TyB]/mzEv` 7ez_H]nx_)ByLl T9I?۾6`DA-[ e[lf>&0*2d򘝗 ,f0<1K7{3"Kqh8h[´WNzI%T&pxSz'T;rz=oCWi!~$ro>Lp}|Y=LHIR_^gn I`Y7D;?|BKe.q>A:i(͚,ah}t0@Z:A IR2[#Gr!^9$E/x==%''@vmnyϵgdwl_t.s*MqMy2m^|/3P J:d6 wȿNk5G0řN1Nh0d/cˎòm2?cEui2zOX^Y+oe&?(m:pb g]ٸExz9_ ٴy9&j7RS]̬m0%%\P:o3@T}bi7mryq~9T^t],]w(gSw]/b\+*Qғ]'DxTt<{"nUemFsaz:x'>Irkvjl6s'P)4e럿IhQVB)L2)Wx7.zբOϼ\0 r:2, C1h}WS5/Lť7dKb,+dF\d6^fflGe KGR-k0[ fc5{F^V)/*W4z^%=^@XpeTQ<';3TQۄif2T!6?0#k/vsH j7`@y)AUeR"m/o/6 /Ĺ͵{c' Bkc,UWǟ=ƟSa^ƹq3t3_r}z'T^s i; {mFry'cGx0-3DˬdFltIH<%ˈ0_(*,5ZM3l3{`زg:Dˑ4e{a|utn8i+-uCc0[}Lo0gG}^mhb~4Μ/Ep/ǂzхBզww xT$Qq3/ohol´cR@Q>YR0^ GR9|o3Ty6KmhY}̟3x/^MK9PV 7 "WȚ&|tM g_~'vu]?u~}l:,]KP {?UihwN\Ϫ\69` gngfp<.g[׀{6hU@܎EY U*Jazg[wDc}:>J)4>sRz;K~n]Oc"}~-ˬ|/ e~Fl@{=#&1k2;/3!F4| 6^< ,=e^`s/|wxH`8O gAv:=y7?uc8fM fIN )0(4 Ty#6T)6%EE㧩o6$C rקz]U"t.4 2қ\"ca#7vXޏV~oVg?G2KQe92J{CYS T,_b ɿ|/s&2;/^m O8/ef1ld=f'o0K/tp~۔´)xaڏ?P{s~#S`ͳRbqDuR0)9~ ,RQp3GIq *KPYorlF{e,eF\ Qwog[u|~0rE6xMr:H#bsH['4^=;GbiǏII)={Gp)W͒@&%]|t0ln!ځm#^?{rn$ߞCa{#6|LLnDLp5 r" ӡs9w}a[oL#[JU'קe;II٨-L*ȚZ AP-*Ы|!:F"w^+gBz˲D3e*'euYfD9.1UC 9,4_%CEs nšƋ`}t!jcaC[Ji)E̽^.xso!ͱsȨ==Ugn#3un0m̒MclSsTTV G< uf8o3Tyyg2&tfPA7Wm}߀[c|,>0IJ]W kj#XXkj IgtRR/ji%WǩBrIfǼH)d92ܬcFB/Bw\&g]ݔD̩MVs&Fc3~HHBR.i\@x?$;n{7M0SEi?u؏I[hYNrB#9u?ǬM_UTR,52C/׬t^,2SYan˜C6g@HJL \y4dne3WǼ:$0ˎ f'an~=ҥ$| s/r0FFs56 3w-)3sׂ U 8la3v3gϐC!U(9GM-6W ,ͼƹ *~pz+>hmFqC4kKܰhk}npY69ph7?|/qe= 6?#3h'qy(Ytke)N+42F:qw:2dߖϜiJ?=@=4(F`_3ﱁa)&Jayc~ȋ~)~x|1~]Y4눠Y#ÜuX([/gY> ˬKL_ńNNOy1>O5zⰽ8,:,;  [C/)[ĝ |/DgExOh|J$8߹:QG@Jې0z/\na>9EP_^8T{8PYۜesVv6sV,WM3z,Ϊi+^*Z˱r|׎B(d@ ("sRщ 纄!RM]Ж|d`V.l2͞<^28ߐp~(fߋKM2f1LhlE:ЭqDDgWS}||,bXy>Ѝ5[SkXw~.R|=8a"'g [GO/Ic/3%X٭^<2g"ِ̦0% FTbz'%X:[ χݽq愷9&T< a 6繢\aT;o3,mxBT%}4߄.Ot9 Mg`J0 mwyM-;Jn/ xϬ@J @lO{Y@Qdb2tz96GO_-UjJTk0g˞R\2 h[@$rT p玜3ȧoSBH;?6/K@fI {5iҋϚe>a;hΪ2Pd#eB/33ۺM^A9)%~XlJL=ev,m^45;}!_7{饗Na:`_݀46osL m~^j\cqnsUm615ybr0:͋oZngJ۵Ƀƹ3>3+*g&J\"xVoe*J>ZŰy tVf Ql372 &L ѽŭ́Bemߦ"7suڜ[gUWl̤ݶ6ZakZ~N s6I ;%|k3Ym Ru5uimny>$#sd &IY9C[:D /uv3 4#:ŖE0xysˣveֱ^,!s Zt\[/.ߛڵ.k~LBm| ɻe "PCE6+g<&8%b]t^YN^  #ai0zϼ\ [^+eqo{; RUC5CYWWٯ91!@DsČt^תC Nv{V+O˂~dǶ"h}AzDTgş v ̔H(kv̍2'H[J$2' FU|ñVf-5ʮe;ɿ=z%|Vgr|Gy;I`0aM>2Bpn *l,XR03۔O8y]=տ>lwyb5O.V}10iq޸ps>CF笔}2Se"ɿHP IC:V$!2YeVVSJ2Vf&ٲVAf&Ld5fcS`k3ܿ_SYcWQ;6+Yȵ67,Gu^ru~6Sa?w~{1sDoh7Խq}~W-J1zE|v'ΖM 6f.&Wv)K.\:_CGhGq.YK] O0@wN 9G~wLs^see>/'e.Jj2PYz'7Rkxؑ6Bz螯Ԙu7V.fm~μ>x0Z@qf2p+MlזXq+ycLqCj@[Ւk+,1tS1](.kZ+Z잸:Y>BݧpɿXg'6s'L{6'}&I4ݧXkE&'bfl$ͩ(6 :QeD43S{k5Bj`M6f"y,I@ֳҌ~>ztq]&Q^>^?bq^מO^\> J \8`6Zp8RTh~~\s(|v/$4ˏXh}fk.8.Nh|3,%k&sԵ`~]f[?. @3ib8n4z,@|]縣D|ATF7 %C9FUV_Cͫ]u !㮆1^^ yUhjt)@eUY6u9uJ&DuS洍Y-/ VfCT[G6q̣7'Lrc_-d=`m>8xdҖGY-6i.ϹNr+P ж6n)Y Ռ?:w@E׸(_cP-vph󄋦n,?kY[=  㚵wgY^1mk~j8{$ٰz͏o'-Y6w]h;yY/Akv Ѯ;\]př\IY% \wޓ:-UٴP3E LiD8Gkjт:}}]1o8f,Wnk< LjXpͽ5yvcR0OX$;޿VVq.2Se ie& O):YZ1TZIDr`#̻Vf2KZ)32 >{b> &Lxm̀ώ6k%h6?sͺn6_@y,C̹(OO]U@*W])UO Ïs39_#?x09Eq.%or˯H0>;V3 cɋ ժb֘z̲.3L]fYN/zaRt57icUG^ݎq!ls_LhTWUY9F̠s[3Q7DiMD=iev2RO+30 2VfT,6Vf_z`TMo7&L*9=ldx ]kvn[Z->Wy&g\TDn.k3DZ Qβ!2JxJTۙMOn7g]2ﰷ)~dI+25;x櫫?~sb\쀎8 !dYT{]+fqY7_3H^O?e "3!qfk+K3g?5zƓ`6~韐hXt)^ ]f*U2LW"IJj<+ŶVf@]'sVH82-x+ԐY<>ٷ}Sg'LuaLCz,'omZ=|(k>0!J\ԔO _\* xCh󔈥9<he<д^pu}.?sF[cm 4HϨΤmm^EX4X,9&i\mqr| ^tf}lϜ>}O^Eq8fF9ZFD+=HfN8'&f!pXfș%/Y[G3ϹorƜjܶk$fZ zθpgl.3ij˷u7x^g7 ؘ2SnY234#DʬL9%zܲ{38 &LMkuYmngm2Sf ?km6@iDͺ6*)nC姘S~!Vs|gO!t~KbZX7ko>vͤk9exuydwq=8Ae&9d:i/< j}Þ?ϞqkC5;yl脛"4=t5Qudȵŗl}fᖔ:vF+%3%gUf{͸gi2t%wӚ,Oj/ZDѱB%*&`q9 6%SWΝ~8;Oz^ݸ<ؘ~?\ZC_\[wQCҢUf Pe̔Wz*6C;; [.Sڛøf .af+U:i=GκC//+y}u2qw[ZZ\TfH RDC"!* :%Gnp=FxQͦMb:Sg!ǷqD0ڭ[8@P}১O Jn0.dI\[ LP"ihjm6HSAXO$kv4p3fZ_t]Z߀V'7 6y„ @׮GZp~9|xXtm6~&KH/((+AY,[֐6^VMSQIC$ݱ/T g?!d [m An <7<@9EyA70|(7"W$J)> z,9x Fk[ O$*4 v\ ,$/6 + &K^Xūk]oCӾ<+q^ |+,pfق֯55`5pU΅ZpY֒YɚX$ snX9H'@el0]f`iNIJZ(fe*8-3n0Jg:cS}. M{ ɿ&L0ulblpn6?6Չ)U&mpVXBki6*禶6')QK'ΔlB0H+Q`a'zE0qayk=x3g93OTc4H^COC0/ܞ^]$?t??m@P4)LoyJsz"8v{c[. 'x7~~3's1_*+0n.xY 0We(K(!Ja2A&8sy<&գV;f&l[ 6\) oZ4j\h WuFkMY7 UU[ JiiUU X"JVl9(0 5zN~0IF tͶhxE [6]„ :p:'?7n|<UB m.epͱ`@T`NVU„`"B9X_~;e ino?t z]Z~8=zU|e2?~ʤkSW a!wNk>xz(y65^OUy܇k^ &{//ZX-0G$fhfeŠ܏?;,}CgNdVFMhIΉ߹KEhۭgv5fT֘f7fiZ>pތ́ nZ;KO5­\#,IJ%_ Sfj22Si[ܖ%Zũ}8?k8+(+xZO&L0ᎃyHڬ.wA"~XIaf}sMfbU TZɔԹOɳ0Is sTAB0iiVpj6^7?:^pؖ%|@ 7u+Bǯ@B@jPQ9*?!_UfyGfmrA c'GkE;(4;Ml )[-SY̋ KVΝx_`v]=Ca&4Ϣ++̜=r@k|!2?!8)u.E[Yܣ54E5Z9Of ޯV7b]Ue-k633Et%#BY$KsB`LOVQfKXt0|8ҧ!+\]V'de0aqf] _ǭzfܗĞ3'̹\@%|OJve e$xB$ saj6vf!XS7gg]=cAvXUcoO\HĢ|Tf7] bc1x>G?Q_/ P AS9 4}7-/%K3oՀ`#p4} c~kz|?ޓh0y<+tfPX+L'2SGk)Q 6p-vsk vLY2;eٸ}ƬY2c̣o$0O08 OzYL\OYn]mT 4cҐΪufH-.XƩ\r:k2-)5%Lw993KfQ,.46?PWvt1pE{J@'{w05Yr,(TZoIlU(WuW|@ˬ}e+YYwOyuC.Cu*\Twzc>66.n1)ih%V\M`:}M]DGkf3̥SiIY!xKAuej3PK07\`6sS$7* 5{cLnGf͘ O6lq~= j-rXC[5'׏~_^mVkVKV敟j[/ql99ed`ݠpe*4;}{JdO3Leױzt`0\2nwfo:fU :0cՆOkdic?=Ʀg5_'9r:EƒҪQWRwq %5?B&貵B.damf pk3C'PDa0Ye$2S?1f K|8̒i+ &Lxak+`[C|24sE3OY疗,W Jk6h%ld_;eB05b~jg,Q'qÀ/\1OLn,TG>RYȦck_"Q~ /M? tOyg(P%ݝ9Cim\%,l֕A[ l{|A뎻(4X9+־[v`&q!P̮X±:̻@UrXo5YjΔY=\ 41ƒjr5ջbؠˣQGkbDO">햶͜$%uf6I)!LRP+3LCf!_fZO?X'0  IDAT&LxӰյ?M v~9`ϋv;<\<ߥY)<sN}:!Xfs)-@{WFyl!xRW=|/ 㒧wH&[&_V5ރ(kL,J勝˼Rk6Kg *cˋq/?ؖqpwQhvЯ8jˉg^~7Wr?l~׷*q_91veL 4Mžxn9?Vk@38ZQ-*LѠ byS,]suk|1Y->}k XRes]s MkNf`'\e(]lT/ OԵZ?ws ׶z~/|"`܈l:y')EgLؚ2) ie.awh'ey\gGQmC%χy}[]K几z d0>fBeVfdseYl pU(„ 2?v%n q͡\t2bCԛ&4;q+n/-OWOJ{Gsd>EMªҍcv3fؕMUlJITRU 4MFnΟMxW݆(Aui־)\A?og<'Q#-z/.M]@5[^e0Jrk3j3ّ} u?ieJ} w~7~`de0a›Wǟ}u% ڬk6kkWfDG| `afx58 LSR9fr*u !DM}4}Nz'c8a^7 C/uyf?oSCJ?@@L'8+V +7WCem^-O3Ofx6brE7n9K+#X7NFYWW-4J&ܘfiqֱrNf(?bPp1eAn|H5 a+3h\e6N8*Q,a׬!RX-S4N|wo+;#8%yN^'[Ϙ[|>T*2[Ti+3 ݆D$_] N~Ե`ŚpFhogF'oc~e1jO˩u+k4ìqC9f܏$iz>@=k}3^\^m]#tͦk6`ERlkvB]u KM* \ho]coeV w0Y'Lz6?68#@ۯ6nm~bT E[K6!9ل`lF IJ@&#f3`ݘ{n볼.J~vuyRfHO3]YP70-i?jhrBfF240* ߳>$!_yMŊE׼:7bq6*GƫH֋wwEh>8' Nh^Q)[_2 lVi._~TUkWd̐(4eAUɿ<}i|O΁zO \i-Qk:C) S-ZY^*QjWF]%[h隝v.;Cf]jMj+gH+ w +-b'LpWg6 s_vv2.ڀ=Ϥl^yk 6vі9^XOxh~OܔtE_1k]K2KpYĭ%q~O70?I- ReoXmqԼ])sv{#;;S-}^pܚu'wEh~gΖ3|`r\r˖YGa}1{v߂cM(iLfvU&b1.qI_ױ7BysK_3cw|3@H25*T+_ QCǂqaT۹f% M-(IsJ\I,VUZ eUsk3e -Z=a„ /6T>_S4|okS]/@6b-Ϳ ۤ#y-7UNu)4(i+:Èf>yY\d`|1Z,^nIh~ '[.}lA<'0fsɴK_F+0- 5hj|ߋ@pL2G0^ZE}fj2q0}yq9%JL:$x^xVbg '\&&(튤fӼtE2AZIJ o-tQ52Lbcev5fO0aD>g]{هMcj6]x tf`΢h'IڛE۸h b}Ƹ= K8t>/88z=]o3|sCޥ2~U>yc{xbn~uypעHMj9qN-D&݋Mw.?nbq[)Y[69۬2]vtZ-Ii:g)ePٲP- *ҚAI[VU78>#,7h0G# }p^qlR1K$TKBMQ}. "5WTxBFւ[_Vvֵu┖kce]e&kCf?z4TBl`&L Hk3Ӹ5 Emm]vFhphKADSJ&g,!] s~ ;]"5'Dx$_uQ}7l9hTf@\z[ 4NN!{P9bPb&}X]7mjqͷ#'2`tެ:nͼtJEq{V՚et*1sprkqΩX56yG}W DiH&(Pͧ4+M(dk $wo>Y' 0(7W*W?IVkw5бMRSj><qM-dԺfg\%`Y .msUr8Rm OglWV&L0anlv渵9^]) ,;uP/hg:Y(h,ډUdWUЩWU5j3=mXfLs00pΎ+7_pM~$X,98gf'*=o>^~~N4>)U_l9/c` ?\a6GMŬ+k3:KBA]P!]^訿:aܛP_(6J'wIH'F@T5B6`΍ FFSic40~39KHʦ)!/o&ќ;BCl ca8O5Ԩv.]KY6k6O2]eϜBYl)(Ҹf6Ǩ0 6vf?I2Y8?_ZA}„ HnQVz~~k'ql,-͒,5S=S8oK~yhDc2gKLNn3Ny<݇\zI, ּݜ;>knrN;cKB0N]Зl;=etج[w)f6f+=ڡ͡\xtlfsNஅYjd\3眪MFCR^8oj>Lp=ʎWPs87cz }>0>ϻǙn JY""!}Zmv`]5;Q1Lmf 㚭k$k3]tg]?vxѫc28 |g &LkM=Q.?=Z.ڟhhf/d(⢽wIg.ҕ*7.lG$NT"JmJm͒HHԋ˵؜IK+L*g wb9Pʠ ,5wE|Pl{˔-Y om#²L&U%$2ja& 8@ܩ+') ~ O rO6 ӑCdPzJLݣT׸r~W_tg+ >gҕB]UJ-*T\lZY~aHPihAhQlpL_񓱼'Qjvn3$wstCطH4Ȉd m4h*ErvcY-1`Rp~`);:\Y2ۏkp'H]+zӜkhVWYTّ[3ˋ{g]!U qJ!&` yS B( l~8Skȟ< SZ?ԭҀTxn V@,nZT qQt6K\^uNkvJZDi5[8Jjv\/]\a^)tQٟN+skͬY^w„ @DݲkXks{?w?~|n~D.=q|G6!뢭{Dh'iNi-NIzvEhKm`iysN$\EEeqoy 0:uK ">F ePV<' q^&d\5B' im.+8B>gRp~hg)O+BOك68yδfv{Ir졌aNH Z1.j\#' WA+Ʒki$7g=;wQCWyJ?N8uI<]W_,*8=;id) hTR0횭3g@d,PVq횭Ǻ\ TYKڦÌ3s=6s5{>qh0a„d||>IyYO:g:sE&qn9-}~˲S 9[@ҐɠgYz&"!j7JX]5/xAd|ʎu\RW#]^HnВg ]Ұ @p}Y*K踪i_R-AZWv)[=c?c7͠=awLh>?ÛA;W/.e懭5[v,y_}rb_>ly]QmqQJͥQZNh3zE5Z!Ⱦ^d'2moxB;YtYîW BK[*sS q/ \#XUΎ|y"5ah$SµVL[Gմ:Ȫߴ9:Z?jw ~,@whNl>:䬟 m4_b]Ts^FAm8([/5BJM)Qx kJke?_s)\Gk6Vl\u~|u~3{„ 6:Wi|2hs/vE[!^@  "*B; 4 3rVV/ 2*1a6YgԮi>,[P/T̳r׺fe~ᚍnlD}yk`$,O0ᛊqGy.vYyq~6E[hJe.,[PsvMu UzܶPV=[Yu 3V;dޒM/W,yBgEP7)[; NƘ3:oM㶼5w @Us4hG#!@@ g3nf9ge)9b2,8R'BGn#8svyȣpC.wAh 'x t|e/)0RS}E0P沶[Q]/̜b<:s'DH( I`1O1>rTlsz[{p)dlF88sVcx_ץv5ZhoFbsy8Os㪷gGS_<@[YK#%pF_F]߯ѣ0)Qxw &Lxqk}pE䈋<綠9dv<i|\s5sP 5,O%?F/q1@v0} |ph8xX?\0ΉsXX]cȢ]"vZpviXǿ^owptt\2ې?kh=?Vf-0 ( '[#jYli DžS͆7gsue֑hBJ@TUUɬM3}4oNL6Czjz΀2ح FclM+i}>3gL+^ S|(YʼyxH)Zytm{Ωkg \E &|SKJ>3Ӗ&!#E=s5C5˒SIBSr`ꗆ8q^?:N^3r˫i60\,e)8]!~9ϟ @# ע gԎi,,Ζ !\Uim~kvƛ.4o0x/؃)sݲ]yrHcVqt]떭̇,9Od( qRA+#f|kus=\0dC)Jp'͢Z>8o'+-zZXm WiZx挤z\+H-ag.4k̮v͖5>u]>Y#w3&ay„ |h_w~@a,!)mq y+\\Bn\s 4,̃zHY˫'pưHCZ0?KworyuyϐAvbz&- *9!'-[I[kVK/oQp#8k!f(e?}iBx;$4|moy'H ࡾЮϬyWyd_NF4hD'"Pе!d|z~ӰVK#|o)mT:-@(|h?. }Q7]y3o\@$Uں,W3[6K>,5;p]q`'&L0V{@!,3?WReC}>k}dsTt㚁fig"5j,._ȓU#.45^y5ZΘYv 4|q-EYQ,M `q, ue<[Ufm;BK.sӧ`LߍeҖ2VC`52tw÷jwl75$7e@?ZOumwVw1ՙulN,Fa{q>~'`ZK4DJȘž?xmò|~ *0WertW`tLs"k1Ke_BB4*$ٳgLX VfԾF\[5؈~KheLr7O2DP} ^)]rI"ZZxf]فĩϬKhx%卿V!,5e Q"جycݮ[`|י0a„hp{AoO]M\΢T"Z419J4d nf 'jDd`6[^ |l#eWmaG]Ӭ;$ϖåID Q52h w@ UfUYs6( ߀p*븂Q^OXm_E7_]_CBȔ@  .P)Pۥee]'Qj Ex\J36lm@53֓QN{ t $3;;r؊n^Ė(60ϑf`y~ha!c>ق="RUG##2&hFEƥ.ߊٱ/Ǜ^6>2w-g+,rO\ i,D ^(S맠7uELi.Ḧ́"133 0Kg77\Ϛjmn_B5A%f>>ȹ{HVQQQQqa55+Nظf-ϵ4NYV~n q́(rNٸfirqVM~>gr/{?afUP?ݥ]lʇRBr`$%ж-k躸Nl~{᚛ޜ/km~fc.F]:4sO VGkbh}a?x홫l vr`sR927ӜCkļZM?B?mq|JY<cXZ0L h ֙7zk9ڎs8_{ef@_HuT h6i/)XQ<Gw蹫Y89&)q~tsj8t異b{s_/r\4;k6㚅QdOkvgB͋8/ x|i`<]yj _Z:hdO壗osod:Y:o>Lr("ڌlmDsRksWZ>th/]&2~1 mvR ) nZ*\҄yI YQ1@ت \FR:aC:mڎeFpD֯?ua:@ "nfaB0!f4?8TQH<-(g9 poݲx[zo@.9W#CvқRQQQQ1=z N9b_k(5/7A\30+Jg5ϒf=G ggisD,OQ ~p# 6JkM,}5Q+\?@j j VJk?R ?Cd4@T~Y?ܞ.>C=51ο^=_ӗCGYFg||7 gnϘϼ?4m1ص|sRƺlzz,bOme)WZl&3jYf_ 3#>+c{|p|9?pkږZJ M,͎/ ϕ 7?5VNXARAL3s72|?׬͟s6Nqնqk>3h?^Wd5;tl^Z9v˾f>2Ff햽qkRR4RJ( Hж-J85eI=XM}3N#cigLwwzM- +؜h -t&v]I=$:iZ5&f.*7h $mڅl6?5puTj*ϼ1DAw32Qw6^y⊊w+|'}|d`@T5oza }^d㚅$ZjG6 I.z\WYb`ͳc|f_)5*󙈟+@e!_?\)j[i8Ԗe pI mW|v0=8kk']벛vZ>gf5A{Ox]><};}bXݞ'ZؕiYt`֘Cq-\*s!a3L׬9c?C 5`i̛\UTTT}= ^$Ko E/y5i\s6Qgq%H):0A{1O|&Njbi]YǞON|NJnr)v|oZݲ$`R1i0>=)ۺXx69@߿iR~}Bshi6vy^x{^/$tfmiVh R~R2UK3kmnΜǨ}M4Q2`km3hoqϙMڼ;RiCGh!/B]y(6?  5y 3|`򎙗}#xL>RQQQQdʤPr'i2Y囕wZq=tK|f1[vB2I20^20>#ʼnηKik5Z<_ʳ.ǀ%2]wgf*2Zd+afxpBɟm17(%[.٢i.zUtQus$3nK|&94{-^Z1f X{y~:q*yX>$6YIV5! etk懓 |Rh[ŘUkOa> u~;kgCѦ۸~UjxX[ZOi Jqrr&Nq2nXP'$r妅#LI:;ݞEgׯהZo3łK˽йڶeKNDƬٶf(=8;,zuoyܗ{=Gsh~:ipN_uиbg ImSTګe\v/R-sМsly$fEkh1}>>3ΰ3Ͱ1F;Q'r]}^op/ׯRDNv̖ͧK3+"Jv9>nxWQQQ~OCIrULCJ> 5_G7oBl"%fC[CeR?Ƶ`XژgR!^noKMořA7]5Fx>%[˓' NBTgIRHfs {zRks0݆!-\ %%`g+ XA|ƥpvgHcT83jŰqcTVp**<{}~^Q\*N]%S6\ \$`gv.b&s=51͙xf뚽1PV+$emZx\0d5 XEEEoL/d`FYf圕{I>l\s$O7@Z&s1ͭ jL! 3B^]x 2 mM~}ːw;)_ThN H :sv#[+wi&m_ $uy|[Uhm~k?594ie^NOvo;gaπ&:# v yZ+2@ 9 2ބ>nFSh7ZՔ5Y֤]4>zevPSIC|$`Bx @Ch>bWov.J6 ؕM@^TTTT|Pxߣ97d`^~d˽(!y{`$d` ;R?,Y1L43kiS4o\7;K9q>'y>cz횅qRo)Ŝs`s <_{9Uw:H[<}m6v v-)+ v:ci担"R p"ncI*@*,f3>v4$hitect_Z> *釚*?|8/AvECb0sjҰI/i/f0I IDAT6N I4BK{ObJ| r!.}{gE[;zگ۽vcq\u}Gme~ 9(ցy};@levMz`>Dn~ VRL_$p.dhq}x/`Z>cc,,y.X3ƭ97>Ϗ 8Ywa-o͗Q:l3nI@'&B (ssEwfd(L3&gOWz******Xkf-Č=zC|TKU l blv$j-5 :-дmm\aT޷v["B c!gP~SZ1A;iQ.h,$* k`[ۻgLv}ʎ\l;?3%؃B&]𑜓#|t]/oߊo2{`x6WB/Wj*jn_3t>Uq XzIHI]WNJX!)>vNӘ7jUz` vh41$ǍKsv>)DHYKDv{>l%;)+.i1'1#cG8sKv,gb, vai*****/\i/#™~l奕zH9y:^ g9iw3$e8TX8 tod ~}˛r&˧5CŌ6#2)k⧃'I tmÔhAuz*:8k~ԋm^l8kkռ#yc/o1}`\{to,0zOXl~,s_͔o VnucKq9 (,dPdb0i^zaLI8qLSZ~JhiFGyf4Dw0[z۾^+8s6s"%sSCm?Lq9@gS=RLzs'_O,筨x_N78r^<`d`Cr:i2h瓁 "A4yJtmo^_'a1[~ik=]K5 d/pD'}@j>%$΅'6t2D96gsGI|%`sOvFh{3; Ө\;0o"-Pl$M-Hxǵk6WI pp e pc r}]W>&ks}%|ci_'cm)d7#ψr,eC4k༁V[x mX*S#l#R$`' <+Q슊Y88'˧?#ø^mV#`S.>$Ƞݡ|m?G7߄[eƵC!9)D'SVI)e dE i~fwuݖ$`6i8ͺ ݲ7R]fhl>j{j?槽9|z#6p<[ߘ2kON3[rpv~:[+vYӜG 2S @n A\ ۬c9D[BZϣ<`ᴔ6k2[՚?Γu6V'FIC|GvzZiO3;=cF-v>L|ى{ o**3`5YsnxÂx .HpG b9l?;6<(¼4dxs=i(h<`if<ͷc;=K N9.:Ψ5V'm7Ϝ-fΎnf6 SZaQMVUTTTT|]n83sXʳVk#${vtzfhyҀzr[^ѧO0&7.mi7>=>.yh來/?OIq;9rf0s!HfhwNsNVJ2hKZ"5i6o榹`Lws2nq錽zS0꡹epj+[u}!4!YkVhJ*@ $ږ) ()iiةŎ'@ ($ZU[+Ƴ *և ۾OmJv4gNdF'ZrY$:3g\={*75)X,exKCrAR)Cy; $QZvJ[XGSLmWB`Y|x .o'm{B3/|4w7Jhe?gќE-]wV Hx ƿ|… u\ŜB˳r}~..Ao/9ci}?i^NkDtqZKc'o>f# M̔lksH Q+7eg7OOi=e |! aW`9`nR6mm= +؇Sǁyso?Xk(>|ԇt،}Y^fp?vi)h2׀[8f8KSi޵ Rj]PRm[&$I)@v=?ĚLȔ0rKs2ZL 4[SK]b3ܻe'2_ -7u2ggf8u妴[6G**wP\.w}EEE5| ONN x-'-B[-f SqMMDP?|'3j!J8Z$;g?4a8%"VAM%%7H{wRi%RZ'.y¶ќ9_~Jۆ09/f !w`[EfII$p g! Lڷ8q>4#b}YYL@j>3ciNyl,Xr{VNFʕ nfX8 j,ob3ZV :]sqEEE> @֜"'bfقvrPv)oq!S ?|&<=h~@fcqfizq%YTDCI8$ .4SDJ3usV Zqqg4~:crF}bo"9G?x<:w}K-g>V&˼-%mzmfz5[ ]'NA뤤4ۡ} Ʃ8I87ZKVĚQvUߏigGlo%\͙L`vmTTxˠ]D;˳H `XѢ egŦ [v*5:Nb~xy6qe-)9Te8M^o-'%$%\]f@[&`qKaw5km.W c}|QM6zh=д_F>׿3{`Hk>61|R[vJVq̀ԙY[a,ѮPXop80_dj%t<ss|rۆ5a't|8_{6kFnWp,h.adL>tW6s67=ߊ{sPN2hq *E(oJ)d"AqAsjp%a`;' ;M<攥[mK {ot\a=4ٟয়Σߒywe?I>]|l\Ym K])%NO@DDRۮeZQklR\HO=-~p_ 5*ӆh>jd $m5E \I6Tٺn2SB(M22 Tn`qDQ9TX(G.OOInhd;/#3h{%la 1P~6yV3Lڈdr|DM3{\Op wefa?4Hʖh:Y%NXv:vL"fOl)Bۃf_=ys2МãG?ydڋ{z+s-g4ZmW[uYk3 HS'е}f@*פńy߇2)R*ؘ@sZ$і8,h8׵Hv5Mݻ*A7j%(7e+N7&ee\#=94ph+***XYyʳ|fh1Zyy 䪅Vkl%׺y/;NI8c9agA ·I<2|r/ݡľ֕hb5úi[W;0I =479kFT KTaCg`/^C|  j-"-6nه+,XX.uA[S8Jж):זCICe_lVg~QV6ҌXY_~reRvkvT9($l2aȜpfu2+xkdXnHnwpnZ-Ѡ̣`l?|y}4~bcV%SX$8ΟޟhG2QaѬq CrB׮e<4Nv_RQQQQQp󊢼jOe4sVs <_pW%z>[K eLb?/8QsNIfps")cKrlm[̜\f{g K)z?pzsM6zh}sS,)웾iY/Ut۰62nqy tΞĦit)F|ƻO5~*qua?m12oz3~Qtݞ9u^x17]uPy 7R=͚X\3g(`:z~m{EEEF{ĝP??/eF(ӲNӲZ̀)|$oc:|!#SiQlm]e?OI)јRJHhú^㓅- _+EZCIsE3韓Sr*|5ʵ"q|8.5ߧeAi< 䁣3]7"V)tf]AP|۾\R2@@ 1#9y7| N[4\F{Љ3Jd<=]GUFGh~{y8$j/&D7ө}0 Xʬq}͈f 恄$ ?jFnJit?dijacdet-vhՎ \,חHt&% pT.zy']눰C슊kGA.X*Ry妖j5v, R@E0S;;<-= ?3K|gs}٭QB)E)%5s$V|C3f ̟ =d1 %5;sCv##3jB;`$Fϧ;>d<;{3myyS3>E-w澹X6|d $lvZ VdA5?M']XQQQQQr~\uS k5,AS\ Ze!O<);a3w $YJk2\@m!v0)XOkgg/z{-_&=/|cLso`}+s,2qʼ4)7%;:m땍 J*o 7Z㖮qq(_49 q?۪m6eV@^_ìC,fBWº:T8\*70q˕|sT7{2Z^fZKs}pqV١?:9NH p|By~Qwzm?{{ ]:g7ҭ=L:iYdVk %gTZj (O1R[`6[l 1'(ԀۘӲSA {!>r}Y)ꕒUTTTT߸d`}>|jrjl~@,ۡ\os3ӅK IDAT3SP,O _as$|/偈x%_ sAڢ}~p_>3[ AqF3uZoZͯSg鰫ŹŰv([hoJsX'>Ϙ>/M;2Xf(V;wW;_ۣL)q.p9<_2hk7ڬ-ڼ\ܶfmmRЛ7c__|y⇉l^ħK3k7WW9O ֶжᨷ!:o7G$#:I/$HJ (c -׏j'h^ *?R_χѴҦUQ\>bm^#ՈL;&e_~VK8:7ERQQQQnQ3gж-b^ 'w嵖Kv $.wJ}\+,WhYޙ/W$8'N8q2/%Hg[]e!Rk 3zWx y:is7|x`2:qPsl=e6V)2.!lJז!E8ך-R) &9 qƱ=mj]fnI-=:1Lf!l33oϡ d8JL8Ꝝk4FE"ol?30a yg9Zdyq8%'qd6g'A jb AB $ɆrS9aP~ )eϜ6oiB9yӟÇ;p ^{iL҅3CBBJN*МH4tq8ryw;hFQMd[52>g'M^|_1N֚'{z_9|QJkVӣDѓ9ܿ{<} 0 ')TƣT2ͿˣYRc*(!.=u gl7ks>y KXjm>Jr>Bq皽[f-͙,`9+͎yw[% Bs9 ]ٿ{Emv?LZOK4m:>L{qI>kŹ +5L5@~G'oûxΊo{I3?2/~=lE?`k5Ǒbќtb>`8M;=_xbwbʺ-xnJj5:%$>/K͝e~ȝn@ [ |/w>C#|;}{Wxh6x:yf>y/˟/,7^/@zlȕl/p,>8똆hV[6 ;mN6 8@'H HPHpA$B221Ν +[LP۟DkXt~ u}52>F4qHmL|(!X&hba:7>Nr}`jrdw˽\#[q?(j[c4x|񴐻8f?a"Ĥ'e7g$@DlmX4Geâ!$vZr*6% |%6%u x 8WɓiO6 Ovh^o˳᧙k%2o34,v|5IGj(a6|.Y7sA$8'&c1;zDƔx}>>_@c改k̬;mŜPPUg _ܳ'bɵTN,̀;~.yRLi>{HY>4;8j^9">!qr}|g_E|'4O{{e_$mgdp"g0Ob5>^Bjm9do73dbKF]]g OcO{V6gN؋ꡕYS[_F|Gv[f]u)˞tu )c@$ܘ154i&di$]qcifUn^ӰXjpxt7q#¬f yT9߭QDo];fG8w^1@\[9 )q^|qŏR7;VF˟o}:4/ɋME8iv;gM#2{k6|ߊgVݰ22SK M$qٵz!1Ղ8L܅n6T.;Li3~d}-LɅ!ly+~Çcv&a͚mY-s"@z(XZ5|>ޜf/TK6Kyrl~?lL3hŜuS!v0%:s|lIE&A[Ij47:ʊ{ɍl}%Zڄ`V;|\s"wwwxo~C?kȀ'1nZ['O9xV* í axtkXdmz D>Jyk͒F}9492fΎ_Nsʬm9+umg!etpq}&AO|1L}>FƧQz*WZF5RLr#h0(r0)GU ,3 6K|8[z6Ќn5svEEEǁ}.W&/#|,S(OJ{z_Y[㧅SR:rISs$ow eX9 e>0 [aUEiO==4LqiqC+sP@h+sx`N]5n˖˺f,.(?% Z 8@(D80sQ(0Mʢγwm']rruf\f\}b91,5rr_ '2-Yf^bN`p,;>Owc df`,!Lqfڼ\uIW''$x|m6ÇҒ,ڻ}e~([}.YJ1#$!l%j(Q&mL#ڜva8Ѱ:km~ŭi.ڻp;x_W6et`6o/=hlke6ZB6q2> ]k-͡[PY+~̘&k6t8 .@a>9q. FZskvch#K2ߩ.dmifF3[1DZ@m/K`oQsY6ߜ6?U ;]3[)sZ,>c?&/9e!V)8-pwe7,jnʹ7pV5;19O_v,4Ol݁~n|ЎߵsҘj4 3!h8 k 8qf U&Apٞ0Rg0$C!k3/W-}84_y=A+)rV]2MZž.LvuNa/%cX@k$wNL\^0" ٥]16n4V{ MKZ̻L 4tYm{?Xۜ;wmaV-2SYHZ~2`N3id@uhX "PXsl9Q ۡqKcd?t?vYab*y]eҪ>7j:m1LLfOHĈejީ+X.kRF<"?_v٥- r-G5i0/Ӑ*/sb, l)뻶%YAyM⛗+Hx/NRJN9u$@u1Lm>|nviDgE[]dm~/ɓ'C{vh~`VMʜsΡmíEH]gf3meXx 4ä>]53vC(G7]^RZ [kcLs?2gLg4m9'&qcdX*}YOɌO?77\QQQQ"O?65]Q MI]DliB>ott=^0_َy_"?o`s.ȦoV#HpN9 RJaUk([ ;J[C xG1hC~L/2ϛ4NIohu6놬qkb9$D'% .3~ږ12~VKݡ-̀ ^xwѽCKMď[wOjX}ro՟cڷC=WTTTE~,f!f v BBK3OD|?lֻ*쟮uM *?k12>9TM5>p9mmNŇeWZzX./X'%/kv<8Us5w /6X+uͿ~5oLw+7@IѲZ+)[&etl@)$_&N,jVOyv.~8qoi-Ρ1abmyd<\?4b{׃035,A"0$UdP ^vٸ;"oypx6r?L c >D1h87'?]^c~nW8s\ RsNBpZY )u)3hLڨ6%)78sůa޾}hO5;Vi{7GӌGvjenMB0ZWaYwfXd'Hg6ul|sG  w} Gfc\H鸚09q`bm(~.>PR|ozi_Xuz[ 55s1#s*i@;iY wz^xO׻}EEEEEN^..y `ctﭑ!:R*)񏘯8gLlAv{DU;iKt ?x>NzIp.Kjtfi1 4}룸?}KoGZˠ0%{6۵)ٳgrlmk>LWTTTTT*'x(s*g&{v>c Hpgxe}E~Yu_N&sҏAS @[.A]6FQn,xأg p;gGPA9{5{ z{onje`mfM,2향E=Hٲ`NZ2 80u3&o-?/&GOKjFZKh1ab~dD-({vt=4 %f4'lE@] s7˱G_^r=>0[NࢃUP75=S[A!+> KXM"i%gq: nF'~feڬ]c;6{Cajq~dopN>C/5{ q6^>`y~+ {f?H@-Qo~YJfaʼڧ L_IHۦ"E8>VsɌ(/M1Xoc"'a[Y$y|~t=4\sFӪ4_>$-GEEEEDž\'\FihX07{/{#?uo{l IDAT< lgB_\Ђs41:|) Ilsr~&%y8b{pNC`?t|P<0[+G;}OЇgmUoqge>rޡV>^dtTZ˲GڦaDxV5LGXi=_/v[1a=[8e~96A_@kB3WP(  r1JVsD @? X/)XQ>_!%X!//ߺ(ʯ+,P.o帖\q " _>_#W&G -C믣U")%:ÿd'H"@ʆY.*<9G\8!Q!9ȡt^[K}{pǸ?_}>{h#0T|2fgvNZ2gpq{l?<-e22K2 bAsd:i}8 Z־Xm枥yiX0%31Jl`fZՈ_K+PQQQQQӕV)`8TX\[˭)(Ysq|}~5diQRa1} !@\0~i77ܺe爦Y[v96ki0dq~ ?|/555;K^ǩۇlm E\aQL2ɿX: I:  !6eԲq1a5h:4 U֏525=&FH׎W******n.)'&COmfm|b"@a7來4OG3#_ǸRaADV63ޝ Se\m^.]F dmY $C- ?wY^=4/l_V76vb+rK{8l RJ+Dg2e A:pZDkGqcM'L Ak9v׃>g/5?%5tӖf2$+******n _km @a7$OliLpR 0_&gą $[}月 A:fi\smYEL8gq9k˗?g|Lq;qJ!+&˜sNa)(U:kLRcu::ؓRB6i3t?:$ qdƑ ;gk4gc~"Ko)qjJ_O4?[T'qTEEEEEEŇ1"(Dž0?z?Ƒ+ßQjiX|DEe%m o;ꗝ*hgݖɑk̪ܤ1Θq>Ol3?1?p}ɴd[nntUe%#89 ̬"W5D3w_?R/Rv[75SG?gw?.+}[k1gqnC{$qw8=O{gv#yX#ƨ_ޛP9nX^߳,&⼶^GAAy}R9NK}EP(}|'xUO3VNLf%})8)e&<YSׯv9SP(n][y~,x3)Ӽo=`U~Do/[Sכ9(֧,:އwnx7xO19j9r]67WUvsOup %E6!د5h*4#}rhvjBM]D a|%w_6wH~fB)q 7>x΅1f}T}dž!iKmNX<к?4֟z&(jYF?6m B|8'~\+/焏|!s'_RՐ?_Zj ɍg7GOⓅ.?Oޏ&8FGGr&x T)cLss \'s~w3K9!q.4_k~8~SV]w'Zjd̞jwc21#k.fcF2d|6MdTiQoÏ]3y7n^j Bx&}UW 8>x')7'iڗ=bh!!?[(#:/oƇq o@_[O'/1g큟%{_P(>H{s \dNvOܗd ?Z[oVɏd$=as,ˊ/Wjci=&ւ4#bGh`lQ>PD;DD`0ߓ Nφmk.eNvr:?OM{okwJSxf s2x|I?>C@!wJ7Zqn1hu.K^f<|h\EG]C3ecџdφ f3L׷͆LuH6缅GS6BP(W|WO01 sP5~W׳G>={qxy`'JPK/Ӳwă :`R:!ʑWh`y8# 7K.a֦?Skfɲc^2rctw<>P+}_d8;{ZX0,,bG|R~%AGw>,+-4L߬_~=Ikq'<#?$` Bixux<{۠?J^[d}{3Zt~[S a@v;0aI5"X!+OBuպf!hVJ藞eկr;MW7kMG ]/}uhD`,6!\9kQ4@o;?QU %5=kSle{?~//* %NnԔBP( c/sOY~T\@E;~9k-B~4A,`w9 Zl-Df8޽m͡nc{o[ozͷi>Ӎw3`&4+TqlXr0Q8eaJΆL;4ڄI+`2o[7oBk t~p;! T| os^64X/Rq6^(:Qa- brdq`Pt`Y~nGssE-Gk$dO{= 6މvmޣk~ w~o7 BP(.]6d>w4˘/Pˡ̧fO/55 d`>Vڍ}3H]C/PeY Fp``bh;~⵽xq>GG\[,: hi4L!L$B{xp$Y3ΥyK MOBP({wޓHd>VWkJNM3D]f7#.bJ#T6Mn<ݪdbd hOw|neLIi^ff3 n7nq|!HV]qSQ|!%(cul^&-3#ߜv%٠A|h#K׍3dl[aq]6Y$g_G\_nSZchN\@BP|󇾀gD du>̼&GZ['ЧM6ml4y]/^2, ǣIpJUwۂMv(:oh>99z."mٍm-eN=nbuLL k8E;Ѷ$DOsxKtJ gymEruҲ7?BP(gb3A6*#x[ou| O9u1vBAl@94Tlˍsamy{ওƦ??$`o҆f/?Y64LwLb ͖llKK﫳)]og/3^.0GE7kQC3yVˬ!in,$?.ބmCj'륧wP( Ba @oLs4g7uqq]ï`.EE#_.xSZmrbB*rTj)4[j%hcm~;!596O~?n8Ml_NtM /sy.~ڹ|̍&,\tCKO_]|4Ol̀g}>XHC]9n{Rܮ49hfdR( B!i"yS$AGooƹN;.xU~x"K@!DZg[g10S,Osހs6кfq|M4GgM?^ n24$`o{K(q|O퇽W{xD]f9CUB.GY _6Ԍ-1&gj?㍧y07MI,,QγiƊ~Ӝ7z rCɏ/@P( _?lGԼ[ ?Z/xEiHzˊYCqlk6jĤAD2 ':BTrGJ{#*p㸓~ǮS }o_V¦0LPq WmK9,)T[f {숀}h%/IjS0z5HNx+KF+zPџ]3]TXh-s}+ Bд>&So~p4kE>i/xK{udOY0Gosb'|e[|, fuk n^?eWäs|t͎jĎh4[ &=;Se99Qo,aI4GlJo3 M35Yњ`En[3?.,M3-1}w BxAWQswuK7/M~37nθiiO.9k c:MsńZ=,4;XX%'O3LZ?s\ mD셆(wޅziT( Bqx>e$_>_C4o7s;qgz|,5uB+Ms-l{{=gpQf;#+D4NiT}՜MU .SIj/Ar?^24?3{BGy+f!D1Ū+vYl2%kXpX(}lYdϖbdϞ4'霥uKiCA-Ɣz BP(/xxyO;f)kQ}Gqlk9Wȵ#p/ IDAT'[ta()9O}nfl ex׼?Zqlg' K>>< oB++E謹s# QKYp-f Ӽ5;ja<6RwnTg17d̞db=m}b:?N+㵦yJDjbBP(+;y5 fǒtiF;N:OC>/7@q%̻Cg .DNvM v6g`6?>^.D~/ݍоMsȜ־yƷRf:q| iIٯ'WgCÍԫ육=؍󓥵DN΋a1빚$7!,-~i4? Yq<֥n6_n,x66>imq$>8 %}Q9xqlʥZ^|gmp0UN-ӜN-9b:Í:d.j~: BP(wB'ZO21+[֛M? Q2[W_oJ bG+y&v)rv"D[zEv/cnh<_5ۖ-⧆h|˜}KIdCǡfR%A<: ٹ\v.Bf @Z*ONJ}>棞M>\هA#5?e.{:җim> ^6%\~EA7%a1k_סٽ8>gGCS5_i}H=5ƌ!%>S*G8 cޢ8.9 DO3_E%fNY"Mm84fV1Co R( Bڃ]r77#1&sgoēEL_Z0A13Fz9a7u>`k#KZo6l6׺ G׆+4$`·_K_dh6NMPgKlű}0hq!{vlS|'t2 Y,Zjλz-tzOAKWP( ` i|!-Zj_3$i8.뽽vKWxc9E3'.( w\f\d$ ^6wv/{i2BoiA7gM74 ?B  ٓSTkN9lVXk-1;(ե^Xw>OZ)A.op:)FTW( B`v='bT7i})Se~&i/7!]P)K6وfX[K)_I +CJ @u[ul]vJh?;3ɠ}i֛6dZ[&j&lhDrͮb2ىC@n7ʎc #l6J"%3%4{ abDk`G< +-=#s۶73!Ι3SKr`Y/[BP(3|Apzt$^n3).cAx'D}2N ^NCyi]BCo WiT=+/Sh8^~7 b͚]>TPJ@Q<%D2>wm{13ڗ,[t!(͑hz:q+5ryz嗢P( B!O_a=㉟I]v;y`:GD[R/cS\L-ROAkOǒE[h_mtڶ5h؝7kk׍4u{CcN\jj)4["\ؿ_Ѽr fMs1[-`L X OU,c  KᚋefDebNJFXEfI߬_n=%-4 ( B 8n3_Zf2u|d+?ziH'ڊ/e3`P"gf"I\ϞfN<87D["[desB?D'_ѭJ7/[k.c6|?lOwkpvawWqbD9Y^߶3_eE΋'}^=*%1ie BP|*|3lGO]_4͉U-E٭5>5]LYs9D3sfReLkmq|OmT5K| T|ZWiN;edUff\~Ⱦ:OQf)}ӱ&k6͎Y Tr>$ku+y~̗''O2X4yO__&Ũqп;QP( sB zr^lk|EkXQ"vXy{zshCOr.E{|!e3-CAQя~xj<`~vJwłٗ|h liy]RZt{q0`[t͕SǺÑ?[#*fˏNj]Yi󅦙'ithiۭ' Pf=Oxo* BP(./|𖧭|\mxbj^6jvJ99 HpA,Q% "d^M!ڲ}Rzþp׶$` 'efQ*WU6퉞A[f7VYcYiNfkb=* ̴|/3%omd9fE"" BP<7jސD^y.㉩yQZk,<p8o+<ͱ'*݄pvT5=RODŽjAsxxدy&حjbtWsIPBa}UfE Fٍm}N T,Mgj1pH̡g/bۼC}ɲY4Wיiy BP(-jk~𖧭 v3?yn9_v6frBVYx}C=9؀~" sy]'yFz. MsòӼlgƤӉ?n݆d D}3RSf>蝭50vBȶO>_i{axӜ=H32kiZIBP( j̍oGk͆S~4Ywa>y0V|;U ag(2^tsvnkn=(SrɆ]{eѾZͻ}cso&3?6=d'3=n!r16#;ACTȣ8LD9)DfEQӤ|t~/TFj,|<\P$Iu(@X[7VMw_,F񝬫״OBP(g6'yƱ#/m2ϼ/ǃ'&?m' 87[G&0`D` BP(Xg=xh+X_[4HΤeŖ:=sTy| foCG\m׍4.894)%ulJMa3|هz1aiQ9xW( B mo/[S~6Jz4͑ALs߁s3)kbHDcc'軦T]Sgi')h%DN:\'+R*wIԄ׌4\[k\?̚=R*3hjhTqN7#3,7oYzr60Bmmތ@vmyd|ւQ%p6o 'PyGsO kvڞY@~"Wi>V?SW霷b?>6  I]{6 <Ch,`4̈́`ҧzhQ[hf=_rx4'xHLYX-1rI3]^!_k8 BP(S@mem=OהyӱŠAd7Gfxz;G Η`ܑ 0,d |0׺fdmM!3/p&ޓ]o4wc_Xo ͮ#6&Q"N:SJ)ukJ{mGk]sŸ^yS_WgၒY~XЫ!$(D)!|ͧ9r363A;2ց2hk4Cz>x+0LW/>]+ukWoBP(oWm/[SG~zWlcd&id"fw02^G !ڽ$5KOa}/N5ouv~Ms.^毟|ún0:طNSSb&k-ȃ`D#]L?NۜS:Gf匤`ƷOXoϣ BP(^Nl/[-'=TA'/ݷ=6}l|58-l3k׸y+mmU5?Ǿqu\77߿eae|||K? o~^wg;m(4}9͔vl{X8X(pp΅DI9{=b<3֣3|ק=͖ͩdL϶b߾=d+*@P( ӜGNĉ e ?Z[Y4 3|Q__L+cGB2iClCTh!M3ηɾސa_౪ 79\avMs\ ͮefaԗqtz=P!!dUTzj$v#DD43Zq`cbbτh)\t8c3볧Eq:]CcJ BP( bZ &C?.3}x6|6MLĎ@Lѹ 7Љ'N hT ѮJ'Y)~g׼0W{ZC?_U֡8>%EzSyO]ez GJm DϧUm7gn}e,z emRyeGx˸BP( ODn_;lGO]g/qtqzqMvawbFu"S173l(?;iL|y~q歡Ϛb!{)4A'4;DK^fy8-BtS{y+ڒ *Lgg &OXH 8c=:ci}<VQnZzfegؚۼ BP<{O򣧮73̗?7k+/Xca``έ D!Pcv<;ZtJ;1so*|*q9;f**뙷)' EpMCbG5)7yQKivkrŽfmqzt1(Zó^uV5 BP(Z4iOю=&YG/WouVx80&_y,jS_Ob_ceG>#n7oޮg5 P,=!أӇU#.A&O 6 藋d`cK)_z%M3ݘ{}Laf~N,=u OUމ BP(SO3it9'F821`!;{mLsip>svsWRϠ IDATW&9_UCmiivQ>_k~hʼ,m7+m\2gAs48X믽F}eկdQe{qBZ BP(=N3o/[S~Tw$zsOY[9Zf ᾳ+dծ-Tӑu4^{1 Vr;jn59۹ЁٿzjC5`#}%\2w=R>X6<169)iq:I UfS,;Y K תo韶; BP(-LV_̉{͆&Ҝzɵgxޔ3'`b=fY9y̒Cr~Y[avkn1oIto>kN|7i~|;͗e~vl3/!d/5{Tjץds}p"e}3S:g0؅,04[yGy>u}rF6 |xPzn|KM* BP` -8:7=|;ϩm1'Oyf/D k8 KAη[]`}~יޢBP( &&yEl})c:,4QxzB"8?3YzkeS98ЂS.J\(?fuy_d`M٨nkq_{/˟ qnَ7:q)s_8,"& Zqo=<j[`GLLL #k-c+RJO,af-WmAkN7^ s*7)_ Qf)qNS6Ë́n=}}!)\_^3G&瀢;Isxԯ ðD-@-?ckݦjfBP( +[ZaxaS Z>x yJOsʞÑ*O>bԬD&nԛҾ#O);)i_ݦgxGwP3 gE;u&! 9a>F[n*YXuIjF -|BGDz C00=SlTxG| X0)yh-XZ)sګ BP<7_[ڗ4/C?Ą>xy*5\c#liiJ ^*c#Ͷd^"=SWSo/oϟ^†9}`e~0&-3"~ĞfdKk /9: BPt3o[#%xpf1ry_v8eyk#ZffffGD.g.0Wd`m.$`\kʼna58r,Ig&j/1{P>a\ xavs!{,[#j9hs'9zB-W2b\k,`2q^Gf~qCK"RPK:Gvz BPtPxC㐈XKf_L'f#?2|z*7Ǽimz4f1~>Hk =˶8H_3Z\#lSvr;f7qʥH9ڝ!Il6}|{{fpyMVP{ZϼƭSa6p7b3p \Ξ̓# n2Á8F۶8>z0sLԞXC6yy=#YXh˸o8/df`oF5 BP(0@|>s͔p'HsG!7ć=T\bg:mj7OCy)i{0kfBP(]4|a1͙s_$Ty~ ;냧=S)k6@Ld1Q3wyR~a=7Ϟk~Ykoi1M^1w5̭uݑd󄲡>3uRl`3*{[5sx8GKix_j iqkaiUGBP(.6yQDzy73IϤ;CL_s~~/ Ls"uϹM%cl;6 6as,QS~¸Msj1MXe%n%^??NOB):bhvmc}h NgG8EMǖy΃TkvGim1ޚ|Iu DLé?V( BIT#`xi{{zeS ơXpƎ9Y)ͼ.>gK,pr|re3q#[,tgigMs-[o9_uR]ENgZiӬP( Ase/jMF޳vy%iX5x` e `dG;JL~6pWdwwu20Hq|xaXᴜ lpu9lɜs5w7}T7t;-mSxj v=_D!6̒mkmzmjP E/mi,rxťvDBP(I=!+vA9o&7x^o1111YeNfwcS]nq?ƶr3tř[Ru̵իwMt$M3WBtLg Ѣ,tiy?rspLyIۜ߇WzsBP(L)J+|d?궐e+m<=6|h2|>s;GBH'YD ^j=*kIm߽adw3/^$bɫ\ekmu ,`5YkAhC YvyqLca!=QYBxx&?t2Ο|>ӌ f2oB?__BP( EEG~T(i7 ?2 |xqLy掋,xQ0Aihx}E{[k{ooh.6rV$yhbO Z/!S{ Ơ| 6=jʂZ9gc gpp=Z[xBCvú]GX7,̱>$OsP( BѠn)cNy4h3ߩQܙE n8D08fi=Ox{y7 l*3lȠ+4"?SnymL%ef墟-HIl)hurOE|#iE -=e{U jk{q31E9s3P,iE>lqBʄZxg-M~iCl82W BP\ Ck 0Qߜy-3|.óq37eԤ>(3z~VzX4.*L]%&JmoϏKEZJӌrR{#,y:ozEӜ>&S( BQ` aVH{y 2 /vc6ǹqP/o%SiM3#jޣH*󁈥_R:F#`PWX ^TE.3O 7i^3o=OF0Z&9|{Y6aUI| CMivo)X-Q4ɾ?:4=R1`Ld6 Kg>xgOיt< BP(*00D iy5Au>?÷,T9$|lq;eV]2g8f϶<ϻx~p[Z9t͟nr<_}%egv{'Z,%OT]׭S]<Ĕ56z`L N2.8,E\2gL 22Lx* BPTс3A?VOsmjCiG֟0cd E=oQLE>(#mmjz!2X6OrΕԽWՏ珙.if[*D>:wyT:``DUfSt$=Op̎TGi8{Qq/Z@M}fO}[MP( !>Rydik|w fdz5^>cKIBm3VF;Wnͳ1`B̜-&a̬"-}KӜ sBGKj\Nf[i4g?M3)3^w>BP( E k.\Be>b9'_|d-vކS9>iћaݹ-o jؒlRiؑhGծR=ȼ$|{wS!sw?Ц}orY3k`øsl3dxNc4sPZf&L~ <(g`Lόs+}/N%ף/>Kyy{@gyړûG)/+}ޗW;=BP( W #KZQH8ƨIǩkN}Տ,Rnr1>H_Xoi~;s`9.fy]b` γco`x$_7۽@< J g.܀5Kkk4^GMR\BP( E\CSG{) rQJll!9qJI2MٵdA{[JUIu[e9j~x4I\yy!S,1MepMb5 *f&fOWiG">{9\-yns BP<|7I3侟^w}͚fbpLbY,d_sιm"^-/g+VxS/O뙛0͘]c^:V0`cx21X,,v(Ռ0fKڳq:,NX0zl9[/S?Y syxvS4FKX*1?+ BPt7>7sGGo}AƩsTg^3Zl6ꚃ..H'xCT}<=̘J͊OrӼ!L1姂vȃ#FRm\9ipV-}.׌'#ΟT<8R 9oz߬f83Gf`JBP( @8%u%}1-~j&k8_KaOvZmXwnTȞS4'LC8)E=ƼiwyNxdоd`Wi^c`)kvqZןM7B4s}TLEHcs"f.aXUoki'=TYP/X6 u[,%5yv<[sֲQ|?"5YP( E!Ty k,O+YS֣[$5͉ 49>iћaݹDӜrKOs\cc,'Uj^n)D7Kg;4$uȜutn~MVInps8_fg|Os~>Z6kOsKUj jD_xzz_kJb9VOBP(94TqoSOq8O^4Gy|4ei^U<mz!? /n:Ҿw%i{R҄Sd`ϷpÛ%|̧d*_[7#탛n拙 ùg"\0b-h4}ύh깝pB42 /o\dnl GNzxk5rP( Bх4QRi^?.?zpdf_8՜P<<+]L6i61&-$"p6SS~o}{>:&:pc";{&G6;7  _[Ssc{/-. {'+홳#nW/i zt$@8q]tVMhUkm 73={u g) Ba=EE{g{[?=fug9|s|bw> %&7̦I& PdΠ %=d`kMy59 Xg}Yi)Mhي噩h+mY vؼ."椏A0Ƕ:[n<خo_Wx< BP(n<* IDAT -m?h ۰#y f+ 5ڧ\Hb*%حjó*mѭt* N6#$ -찇&3[)Y/ov\&4Baz}hjycϱAҹcSU?ӫW_<7igr5<ë-e{*G 3̏d><BuAJ3Ap#B#.ЖQjJnY49_i.4,EPmdk-btju){a)N[ޛ BP(=-\fIL?" g^cߌRX&}Ms4N iw}'ף>\ALD. ;Sp9=J {$kOޓ??^>>A9˛$k_ʜ9Wm`,,-9j+`L܌ѠYYhӑq]e8LN1νiW'3୨P( bx+QxE{|\yuS~/G8[g>8>h#NZfk4c?UI.C$=]AI| P}|2L޽o<ߒ9{^m4oeNSȕƂ\Iq>}lY_C31A,Ytflqﭟ{=q}>zx0{YP( E L޷D-n?wdn} ?ʺcBVi9>Ꮙ!<3%*!P߮s"P@rR-+v'?2zɈe2qtzP;Ga7Ze$ȥ9X\UvXgP/K' Blf'3ogd 1cn>DZf~xkdwd?7xM$3"<{=>)!'X˶)~4FE~N \ځ96DN1Mt<;i$JM?Ζ>.`aC8񨼥k౞u˖q263(h=j/n` zydJ(f۠Scn>a)VEsf#-sG\Su 3}aMnཏWOBP(?P=+iD~>X^/34KIuP;Mc.NkQa:ҘH&?c2h?K}?%MSO0[ju {xU;ݙ`i [!a ks=9"˱8]Ξ{%Ulvֺf:^)HNEptttzD*tCx2MxFJ0M1꡴edޗ[a5 VuK^iXr3)je?. lE~mw￞=o *њ2@n:(Aw<?0"xGJ=6gIUMEh]kd6u4cuMO-ԉb""`B)ngbo'`ũ<(IdE'ȻS'3{V鐷j/l)^^Gˠ: F{BsӻlxͶ}0(̋gz=l5â-&YJ\LpkvtvT;(yn慵Xv K#uWaA_$Aͥd!@ XBTAG(mY?kGIӖ{ Dɫ[1 ?玻Gy|rg'\4ZGNg~zͮ ]57,m${P1l75^\sƃZϏF)XYb{4(4ރie~ܻOWH2i/@02?|T9ruޓܧpHk-O4x*ՙfk K4F[v{Jb(t:7W%w=(>s1yoZ/]S˙^^*f=cy[ܱ~v]=iٮ\?Rߍ?*uUQnOB^y^^HOOv=Qs/!)'GVJ(s Nv EaN5&Ǿ^72\5R|nBBO?ySz3`|"LNl~M 3Y ,a 2 jNeڇuAcvCM"$qJ3YqcL)-s/:r;l7|%v?$A@ (aF5.6hy[mi1"Oc硕cnWEoqDZ>/2J:<Mx'd_#uٱ LlErܖvbi/t&g/ջ()~4ˌz _UvX1;yۿi'~ >Ԋαj@ 0C ""r2?UhY~^v/N̏ή;+NEv+Ğv%kjbm{?}Df]cʻ<%5χyI.9zLEe?.~>>٢y-'{Lx\zy}<ʞ4yﳙ "s 3l.8ج,ڵ^imG|?qW0`R9sr[EuK]zSrObuoslb%Fݯ>j~6,4"f@ JBɴDicz~47[ƗxO_Ӽ\.+h~|>7'0Ms&o0i! , `ӡͽ`iB57}pC|Ej6 ¥ޕ O^m؊nvɎ,J"B sî>ϲ:oiXk03;PJWF}WIw @S=(? #Gѥ+?*%\b]'b/g/n g3yɛ%HTN|"OĜN/ gk5ڿǑ?-Ą BlbtJ44=kbHa]]b8bSMHgQLs-1}vBiOyU}oߍ@ %䂱o ?2'Ԝ6 VޢY 1f߉ ̠Il*r[d KgqKiGI\Bs[?`-J_2-9C@ #pi1!hV6ݎJ({5uZP=ٳ]?+㮗 iN@R^=s3'`f7-g'ׁ@ #x3fw|V};/oҼ'y(VxMOSifL4 J݅R8L^lݺtz!lC|E8sv,3{1OGt>CS,o ;{.QJyn{fU:2im>ϵhk&_@ / ܎ooØWЖ_xlJG*_RH;j_h{S]/or3,uf̫s+=ZS*2 8{\ )  GҍǴΟAFg@ d;0<~?9AL-IJߵm& x &LSВ!Z<#0 Oj!Z_,.|?&>]L=|뙳qI5Z-C S@0)jN`bLx1͌r蟎]ώ@)r7׺4 9IfsP@ 궅(AaKOj?ǷZ,xъc91'eB/3S*7?<l1Frq⯼y§Ň8㮕揋<<[~e\ᤫ_fgؔrg8gߍb>RǑq?niN:*綻@yE@ :/;)iӜ#/բKaDZŝ|:x͗j[{̨ 3f{Τ8,{9@D@U{:l[YTv@  Ҭ|9gfQ*͛ݟ+?V~vVY|r/]o}b͠J[OikFmy%y}ʷ@^]6~{_3{^4]jqSWn M9_H@LDNiΓ6{J3u#fS'%*1==bG)bmbrM @ p6(_HKhf([+7.~^No\|PL- B41Ky;9M}X#pByDO[۴eR0s) 䏙`;j5&dP?8g<ɉl^mr̖IȨ;+7+me@ VfD9Q¯;gWwuJ440Ñ8>k- Tf~aO%vvZ`qkg5mg:9şN/t#M 9gHM_' kNVu{׿oN o^#QzsI{FA~vw4ey_|'W@ Fp X$z0(Rz 8R,+_lb=?ze̞&@$Li) p@[x&$D)>~tem|E_w|LLS]3#c__7%)G3OS*f"hFY+a=If}}//K՟wqU Ƌk\KgE,wW]јX6<]C_Jye<ʸx@ /xaٵ+16i1,;S<(N<Ӝϩqy˘sN׹-#yG['ily>ky7CB̳|xܶӄ%O c.+ ő؍a=رl&6~J6@HH _X8IV?u(vii8c7v>s2׬I9=#~kI{SBy{W:u0g4_m3Su׎z ީYgqUgj&xQmþyX9w>K9YʚUx>̓ m֔b_WY$]&rb5@ /|l#)LÝēc;߈ߖfFɖ*ͶX(͙wG+yc[O9])妣M{~.IA* $LME24D=3SL"yk|\> >Z 1Rb=/ J[o*q~]<}L3|}PBmگ<,1@ XlQ~tiʏ;yސw[0|fʡR HT ͷU[^M &h܅=,h.6_d/ 5RʞjANRnjC9/i~Z?2~Fߔ^`͌ja"b,ϩ)ͯzܲ'|f|E5boႹ!SdcͽRYĈ`>fcFa0NAiJSn~~NfΥ08@ +`fOHjgHV~R`+Yi:ؾW:Zب&O4#S>aWƧ]4ovMa)z* I#KL&TdwW:2Tesֺzڱ_zZSRkR<@`I3N(|*8GA~sATeɮTV 9/ooLݹ5\vvVoJ4ҬYYCQR˚VEwݲ꡵(@ %Z`ʮf–s د_Ivf Mppl\nsԷ?4]7ճuV g5>ɢ:z&<=m(enbE1ͫvZnTII=@ =lgZe:i4Hi+Ye dxu6jq{7MBns5Su[fcՔpATy-9aUOk" Ŏ\m{\ߝHDSeU@ Z:WO]pFˏ+ś5tYiMi+=۲O-4-52i\M{nGK= 5'&)weihH,ùIu17_\i@ _1 ,c>qܵۜrruM(P{(U˵Y;W +ߍ_:shzi8! sJ g==ٜ~Rq>fvɞYR/oJ:QIbY/$w@ a,K3M/hPSOw`vU~%%"ZyHwf҈J̿m3cނ6֔? W'E,+JSqDcf".f~D]@'aJS_ ]>gMqnp߲h&4j]f$ Qq\|)/dEU!áz[ʠdTX2@ Xp-=Eu;fnc؎g D9-WvfOpRBJ5{v9[mǰC;0٦%P}Ӛu%uJڮ]=-hJ@d@ !s; ?$jN˂`̏i_y~dP)37ec`ʪrRR='=Ʈ]|F<_"Z'Vrf[4þP6D`ܗܧ˸/AIۀgلMLTYژbK۹/1FNY.O-:ޙ@ #3oIR@Ui%EC?{WX94-yTc !bu2A KrW;(4\O d,0 :z'\fb|N诅'//6iü $?a=勁lXQ԰v\n_{|M`4fr%fӬQ: Ԙf{ٶ.@ H@Vj1Ap;z{V+i.= 1Z{zTm=;]Qi0>iH=uBe#RǏ{lip/h_,k΍3?/˷c/Jch+*ےJ]Jk*ާ(@ 8#lw=\cKk[2}šZۢ>R,g-qYÞ\COsxIx SiI`3F%qٓ&UoxҬ4e uo T nGiܞTso t@ -H|eGzRů*?=_UI*dZSdd`*D<#8ws?~?叻` zP!0c43DO}iD[4)/X']/XaA vtKkOkK9 S/eij!Hm쏟7WCft/l|s- 1T ߿Sƥ;:Z0{{n4{ImzWSyΪ[L3@4rL < ȝl{wzx?~߾};9 M })%0*ԟ+~:v,c0?'5)恥^<]]ۭy-S[O:LTI67Ca&dWyG#y+S#~ &&4LrZM4`i{S8p-|z=|~2Hv' Ωf)m~.7U'Y-S}{EUyJ?@a6+ l~`i߾)vE|4[G['T9+` {λ;LF`V~pW/"k`7KgMML,L^i%mJ Au%)1:-ZFwܦ {&nهwZ;[͛l@C>zNs! ?|ۏ-S+G-O#SͳK޷1hwr|*%g$0lL B|Xݍy>gП Bi>LK 8T4.Ncs=4Qx 3וf^k+/u(q@ %lS@\=1ڷ4o ClYk{;i64UT4'FL|'JRf>/ށ>w[7˔f*y ȃ]jo=h7L깤Ҍ^CϮ~y۹ӟW1+Wf-W򫖧hޚҼ1~} `zb|;gv^$cz?):O*EW_nGsqrrn* ON$D>tLϿdy~#[6\`0HgjL33:J3\b7}#y^n>FYErMȹ씺5kZy.e^ԗZ`LU=/.u7S @S*0!!" t2!x>?^oWس>< c*yia3]}d>Vw"q>9,n~Du8~*!N2SI8V+X8x gJz-ET\7.] ]vKWVou|4@ =o0>%׷Y=K{k柷As.㑗 (5fg9N )Sى{ψE!^m1˓ek`3u19k=~>ֶUjǢ?˱02%z KL3e$֞( %Z^y4?{V<{4^z|@ Z$;'?RǃJr TTziNrϠ#8pӥ ; @%Ϯ x/$!ly&LQ T)3 A`!0sQaj6jWm Oފ !KLM=cklRu??#O=XX=te޳4^iٲҝRή%ﻌG&y4di*ͷ|̿^k"h1^{ګZΘpQq#k-5BHy>c/'xG-yuxxOit;M<! iZ.?/8X{:N.quhLs` d9kJ[[6RǑ"m}݌WKv -69,\ K5&sx_EnV %@ XBZ yP~ {|q<4Wusck,L>Pӌ6(Ni'0n?߿R7`DRiV';??7ykD@Ą)Y؇kg D˘ft偘 Xq Wg'bRvR#XVx^5{6,YڗD77|;y븩,>Ҥ߅b 9{99ˢ9=7wQ7Q8@I<acSJSh6Ki=@ 23UVCs=%S;b ̕f1v&My?f~vIR7o/0 @0b'Jyo!=>޸E!V=<(f!<샲M%F&R^"Qׯ g*W/yUwWYTi@ (;JL3ל(̼>FLsy:f\g3QxD Isc IDAT̘Sa 14M _q)_wh>zoW)|]QEN=K=Fi/޿M̎rJ&9O"Y;rJs\S׭)#RnuIž4D[=}.@ >2erZ*@R~1RJi^={=Ui) αuu/<29~X?8{X3{e^5ևw}#ϸ3Wzw=ų͜ϏCUlb6-Ooq?F󲗝ҕƓ˦Xn}%`_;>9q /-Ps\}G!6Ql2ݨQDHk-wW 3t,G 6s1 EO54#̃JbSW3@"2!&$g4;]){wg8g{'j /-'θ,!cu%WRF)?"ה~4D H9td.R.y&&?H/?F_): M7{^Qٔ:ayjy#N"4e^rЂsfgGD~~~â4+F َGYkpȾ{C7sTfѷאS w> b|unwJ3Sٞ}s'ZcB`X_ouY'̨_R*y!r=,Dl^ֳUy:'̶(@` aUAIOK=rcvT"zRh@kuSwo^ϮD$cf) Js - ;3![(ZafJx"”(bWvYԗW<1)9s('Z|)@ dd^J.#DL\ f{4.{<۲7>n $`birD)=\L?s//h(J3Mu81Ҝ83yy\w/kJHi^cj%(G+wҌNiF4s1?eJ3|cxtm괄`,BoCiF=hD1\Jm<ƽ*͔?>yXx8xeUjJs>*>ֳL<'*)@`Yn#)Tsl{JL:YtJsJ fЄI"sSzy $_?_^P+g1`ܲ1臕!6ZD׶e~d]@ 27~޳Nɜ/&\Q[ԟ< -aw4NO{,^yɀkV5lo2nk)Ter5G*[ uڱߞ0$ aUW@ =gD#Hi♑Z=a:uec<^_ῶ[3oJL3|:iԡ^4~GY$7c4u%yKRlٛKEoL[q~_#oGִQiZL 쯳tQo {L%ȁ@ ʲh&պFǒT~47[߲dRd 狨fyYDsucW4.yN!ȏ=gxI5b|`2*\*c'x,a3wʁm}^o.^i44k=٢\=M9]i&uڹ̜PbQ@ EiVLiǒT~47[ {Ǐ3;iZ-ffܼ4@ XC VB-o{)dV#;-_$_WLSJ"@V&y)h53)题1LSb_[bp]Øfbl۸/JP5_+}R:,e^N^Pfж[?P@ ,dEQ"$5Jfп ͘ yxǏϏvr1כ:~k%ӌ(lV6߁OX4xy}ȼ|xOW7ЪO3ҼӺ}èP.ŏӫVҚu^ϑXM q ;Σ3D&U1j6P@  FAD Xt +2t!Rx4[ r1"n'uq _ dA 5Y7O rә ?E,?Ҝ fξ4a:ZUw\m{xuv: ڢE $K3z-o*R%JNi>؟l'@`‘zKG[!eKL#??2Jaɝ@MNgvΎjwebOY(&s#HNChGec\<5L֧ٴgUhE/6~^_La^oJ}=yBy~*58ڪ.IViW?ٟ\wS/#ו68f'X1<˛q=K_vӜMM0!aJYg͜;vymA\(d#*ڭz;eڌ]}h_9IdqwD@ x$'(OIV1?J?9~" ?=+_3dE4u Ly^O&|vh`=LN?Ei̧6}4 9N.pYrK@+ُ/+c"?|{"9`#+'47|FE{ rן~$obC_L3ۿs(c!̏1?acn=U5*ͨٳIcDG:pv H:Viej멷;\{Zq͞D@ Cޚ 9ޖLin *> cE[*G{_~E{緋97ŬzzkK\g߷Ӷ=p]7\?ZW^J=U@ x\qDICޚ mWW ]+$[s~9k=N؇)\$JI|l]d\/d9m}JfAHk1({8m^xsckBR؏ ^uVO4C8nwod "'N-_yRǙ,m_K~R?J5kvYSgN{~8~v \m <iF~OrBZO4d>~÷YN#b`J*&f[ĕ/adq9p&ōomX!y뼒v; /HpU_aup_@{7Cו|:R B @*^PAm%zeșUZ!UzF| qfu[j/!y~=˭_}Lbg)6~&Ģy;=$&(1c1{ӊ\pMeKGJs^Ӫ494ۗ[Nٙkc1@*J3eEUw l˸( J#G/+jZ@ G9+͞gTC, jKSg1擕).](L]1^\s9AHi&0 $m.43xL9?Xu0oo\aXKUM6ӓK}lJnA1Inu@` /|߼?#5߉u0+nH\ۜx/|ZoWdг ْ+;Ui6a=zuY[^Ɠxk4z }rNHEv IZ`RW=ԅ4g\<*mrA @ 0$RPDIw- -Kis q{ӠA T,yC~xnhrbOIws@B Ȩb ӓ췼.~}ۛ=iͯ w 0O19ͺX\p.vKWww1H3O#v`c.؉i-}LKs^V]V3_K*ςj ˖m<\HY4sڳ6u%>DӋ]Ls?Yk;k1͚x03愈iE±UxyN$4 2y.<þմ-}wڅy=׌e6V'5ilc{־mu3[/{O"@;+x>=ƼZ޳YRmgօIGr1̀SXdW/o҂_K6ߥffHҜd>}xxy};cO[b#9gugfSp*뿯/߷! %k edfuu[Q3]~gO zCQt>o^ߟYOq+;UAR׼A@%}//P}OT][bBeהP6/cZ{|%yY/r i>e.W&W4:Uxvv!4o'hl!.!{`xn6o~n@uH$Ӓ,b;AL9GD\v =:6iN*R[4cNf1S>thz YښsLa=p?E ^-+Kg @ذ,|Sp]V8lv=:Y@ l8,5ﰐ; 5{]]ux]=k1M߀U _d|sNm/fƜz7/#)qَO4MV&`<'n.y5 OaЯQ՞H}4Klаn) %{Fimz5 @ p/x˴5' *e(:g?@ۑ=َgeԷcϏi.4C@OdO/|lQ]`4̳43hfG츰(Wd {tzcf2]q@ ؎ѽoy]Y~d=gl^f9;E(w/`v1`RLVW>7-R1ײ^vcx{4=PJRiP~}ޞmTO7D@ T> М)L:WuYI1#W-VGH yl5OX4#f~k>͘ʢLH "[ڡx0i1zO"]4}-uYde_1X̷}sc13~G39P @ /sp)||9_gO iwЉ}MLv=?3f/@Hyʬi~:O?__^4N qPzgޓ|;[\jQjTg7a„)e=9jKif>QJ$@X4xx~/?gr\~#e>cѿɎʼFؓ?:د_t WWE A\r]ڟ~Ag_C8_|{j%|)="#g"$h̲R!aݒ=mdkKlڥ#C_ /.cjr~~.Cy^(UO-sW_槥7Z琪@ tjˆO܂r\xއa-̮+J3 Η(q uޭعy}سœkxk]޿zX])#@ nB#o{V7槬IT悋GC4~m5M\%{ "cAC5TRpSS,Z` . DX,LsA&I^ |=QNH,( @ @R|RVm3ѐFŏ#6^ #uAY~OEf{ \ ң|oA,{Ȫp>/X]WJu`^ e,}}uQ``jr2An7z_a\V|N?y@ =[Ž#*BsG7oK)KAVwzJMOiKT矫Ws1aXLDIniz;|rӯ~z/hG@ T@-|rTރО~r_1Ei.1͖=Hy];Ԭx搘"'jlmVVG<ͫ$rfl(綻Uy>ǖ;}@ 53߮g)?h9l. jNiWd13!;frE)aB>1`a%J<@h|y碞T^O@^&Zv"@ ߳˚;D|=#h?Gcg~ ޛny[VҜ{i~X^z[^YEj IDATYi\Ei&wfuoǎ4ɍY͸j_ֵin?1; {0Y}9Z|4Or_OxN?xwS @ p{^#΢)}+0OvNS#Gp+ >R(łT~DCF?xRyP*-+tg:Kڱv!uy'K \xw܉0MҬ_o0!JF[u )y=e4WsjG(Gp촩!q2pv\+3iDŽ얣y:k{͓+}ݲHw%fW.PY)n"eqV#!1O酦/+0O/42,v+ G|hQ?Ҽ׿4-^#^S^iU+09~*ħ)jLsr G,[T=m1^c#uK# '"ߜ/be>_JP@ aqSޛH$ibGDF^uwLp8삵}`'I߸ 4S]ggԏFTgVf|[Mp82PWU38L>Q!)eGʢ@uv{{[dio 8oQl 9˘H>X $=2ro2%|l|42)!8搾%{fU8tVU3T!zb^zخ.dp=P7PPPPPPpxs {=qLKE ?-AIC IXF|_~x8Cѓ\OrPx Zm|%0ob\ӬgNjiE-i,O }md,USq,H,Hzsf1^8&% BTz]}Ӽ+YZj-EĞ P$_:FpVar$d`!U0Y%=oZXcxj~|=4;{,J-cďp2..~s עA 7s|w}w>gQ,߆jl'p ?MGxiX2_ Cuo] &Y _)%*8.ʢ]*s ]JIrg[7lM1#7O, V؝8Ɔ15a A?.1u|Y&2V[َ.^BۻtAAAA]zvWj七<;tO4=M)NCӜ+ ٿ4ObPh\.o9_9pY1VN9`M]L,!\k!}8)HkӼv_ј ۽i9f ʪK(c 5 VGq5?-^'+kҗB!ӑm@:~u{7RPPPPXw /|BIv|G=Mǐ_I{%'A?ޠ\p8{S# `ʢx=Y$L҆[,jg=]e`5[o G:<zӲPt,gFiu+܂;)49C^{h \y{y'kQ&3gfۛh~ 1r wb8xk쓰Q26^ص.|S~|=UuNzX5n͛u6j ݘ~<&,X14 ^_uG0Ow&U%wGx宭",]ylnJ3ʢ:09CXG?q^\2:p|eI0| lFn/p ;a1J('X=M*4/ā5Hi|fǏ|g_~O7n|_*f$efn'?;i~$R$b Uɹ(4|hR1ȟ?xh`)^Ad0Y2# )}ZJpt ̻UdVZqs6#"]Dq2'Ǩ<!#y* 93N((O14S  _ܵM´L\A#j e;iHkM]4:6FbCN?hxk ĖaCy|8i}K>\kBtW'5z߾SQZ[FlWq4/{ D@ڏIL / /?1bߌ+Ϙ4 Tal9տZm3OyM'1l.hsIO@bmf0áe@s܌ۻ? #W?1KD;KÔ^ˡ" g|"onh>9>.8- pA[Pe|@KsMz?د,xkCn.2 S/$i4 dN@M[9c1YϛB+,s Di =\7} ; SC=v|du(- 1px>!z9_ oiz_ |ȥv-Sfq^@xWNf ta"-l fv"+2QH3" y eT /ZoRL+xx;h.Clj 7KڈGNt\e:V HPjlL4Flb+\c!*yfn @fxN =Ͱ6;3u8v-A?mw$z}̳Sph RMe 'Ls#̅Zzp%W WN{DW܈ۥc0n19ς!f֖XVKϜ4#x{5 h~ j#vb7駭MtHET*!*ܽ7_&֖Nǧ #} ' YxB;h^X~ﰍpjk/_Ռ?Nioq1#Sֲʅ)`ɜʸ;Ƈ}=KlC0cf b\J%X@GX({lj-~O׿ƲΣK`u{#7 Za(Ɍ]4 ;Q_/bBYR~S17acO34V:^n$[]/o`w 4,$IZ7b 3X σk+3Z+UQF׶VMlz}[{b|wPJ@I-[kҭU|f}h{i%#^/eqZPPPPpg<x>f g < 0fKGNn?L`ѴKjkL,>cztX9xi08fmmtcTu涽vsB͛?Ǘw#zjT`NozU:0@svGc3%_W;ػy\Y{0~8? < U!IQc+.p?'xy͜7N2G4ktz`cFxD'ˡT |z$g~͑(}}A) !<%mhǠ{гӁmB2q7! !%'LهU>8 JɩY\ax7J$xb"gG_;|#2Kn% : D-/LɃ|l^|̟S/%ꊹ+8.)/KA;) Vc~~/*^3_ @8E~秇10 %QٳwI^>բZ6/5JXm^Kɥ͡GT>OcL1''T;1&:"kas\e0&^~nK"/S]' cz<Ьz'(>cW~M;ynR"e} R ~~i YÓ|m^xomYqƷpo}Ii Muk|cK0`´,rg3}d|w㙭8=^ i ޿.NU nm$:w {d}lꫛ{n~l"}WǶfLH^7s?)<_a6Ӷ;%-G1|rxGkSc{-vb n'7|;`n"y'MRfCoa t' >FbH׏'ZրR:3K[7qn&'?kmhNrd{N+Poo74:?&en;3~۷V1O*P!: m nIz`m{?'Wϐ~;W$ '|&^6>n-߃{ߙ,c|)1xIqO·뉪ZQq:̿ ;;m>-Ll'{3*lwq@^V 4KcXBߧsrΗ: xļ%6JZhp\~>f>c#!,'&ۣe~Ŝ+U1g33Q#ް[zr¸<^nr^b _p^WFd;4W810v,EGV.w;wf/s?/^ɠ]PPPnc2?ǹ,#e*. IDATV?M6H)88s;4XWClxӪhAȋufzQA7orf2ݛ 8t3g azmN,onƽ[4ǘ4c^<g7"#޼ݮ;ƔiYF` 1"G29>G|VT7%S^k1Ue<ھ4'g'WPް9Xcg57CȞ+6]zWZ>A̝]![9Ե gIŋ༿/Y8e]PPPpXsOzٱ^zK] f<2c,7Ur^Okx|erSFAؓo.3EHٮkQUPIsؽ$M3G˭`$`;]4<8c9p cc)) !###T^j8:_7fC`sヾ\oz< ~sfg'<\ vlZ d`cX mrMtnf|z/z&~=eu>O<-t.}q2o(8΂1DžL?g`%ӿ*'ص&]s`&k*.Y0s|X^.ia£2H-% )icGb8}Lcl|p,01gӬci6K{Τo<:GA΍vt cLg8#q2k+;.Wp{>yŐ|ЬLj.((((x1/}Qj/>"ϼuN@';4}GBپB4=E4 ]&˄wG#iFW;^8,| _qob|>?,xHN(7Fmp/1f10׋irv4sxOsELŞfzeOLp1%''HXrADHA腱gZK8[Osl i&'}q1,?5u,z"Vp{NIM> [ZN󛄯#7^!4:2''(Kpc/sgϱkW9t/oíx1vtF368f%Yƾw`a*1ȉT6‡] d4Kb 6l1rLڶ y"~c~7:UU%.>_RC&44?DxRz;d/'24 ܁-&NB&Q{``AJ oZXbбRhHn]Nocjc1bC㚇?FdA3Vy| ߞ5iu EKSɳnen7.^N\sϒcvZO۵QwG|>?އ\@~)>r2W-N`vVϻVI:;]U!?Z`Yo@>>oX$Gf~ӍIi^.kw=;4wmMsK?3Xƒ]0k{6W -.F`}NxRm=Ÿ:su-&vmn[lqɇ*ƨ?i{ޗQ*(((x7k}ճpDϥzMK~=3z$#htsvCѪN-7pP1n9O~d /r]8GS&b8q[˖^ͩs+B\EY4=_ڵ `0_?ff`P,Ĭ,ȡϣIO Eqߵz3|Zby1ruU$369oݖSt݆~}`<4U'bfS_K*`.(((8Wc_?a;%~lY6?}!]v_3^f!|)#Bq"e|AJ|%7lv|WM«XѤI/#]ظudlQ̣m/]1u1_+v( ? f{0hC Ç _Ģn Xs晑!ҳx>a?GOZj*D.R"駺jM=ܼ^CѯPPPPP./1GճVZz9^{ 9p#~[Z($?f&IoUUvrJ1tC;rvxͦz13ٴ-j'[58_y`;6Y4,oS><Лnc]Q̶'zY1Z]&r!Xo4w󘱉!BS5? E熍!~(מc{}??MFWF3ªa]қ\<-EXxDsmflMm{I|#lWM>[Ƅe9ߎ^` n@ _.z/gdԆtFi*^ɟe,Pm]7L@!0og;ᇞ+y> |3mw姹>o/ c%qN<͹ٴ8n9|P F[`bDAgc =6X a—ZxxrC J[#g9ge%6???>>͢m-s44?~|_ c2Nfղm+)N8Qo,XY+m=,c#E YL[5|)!{ǏO?f"AĜx_~# VSՠrTY9/zw+EP$)4k[=;UW}\p EÛ!$;뵔:i6m2iBx _j[AXBA)l[c:2ˡ\2?~A+V.ݶɼ;mKrh6(Kt}KdNiLg,1{7)ijKdGzX O?xæxߕߤ<)jx߇;R/ͽ:iЮe!S^4.@{K2E^X/<~\ڌ5(i6z[ǁxqrdH+T.9|u(&P@ĠyYp%V 3]guVz1>66lls tAoXsnyPvQ0晐Ԩ(;27>zҖ:Sggvf{h}Q 6}\o@,#QY@ī1uG7~#u"~Q~[y9vkG3+0h/3s/9ĸ#KK3g Es]?V~1}?^yO"(mUSfb!ylrkaƤHwBB͹|*-8'1;U5r=OdpD @*w=$j^)ojlX.T\ O̻`XxPvJ#.;,Nq`jAAAqq磒$9׆Ǹܔӧz]^B-n`5Gͫ(ٵRkWN YoxUx~b~&ƃʐb.gR@_XeæuL-W# s<&ٛD;66;B\O.^4_8n{{z f.v6A3>2>r!8 6C4Ϗgn;ifBϣۦ* +PkVs/Z꣭g8E ||F>+et`!C v>zz5jgNKGvCgI0s߷\^ u%W6bysZ}'0؉o(DYY9PCo_vbbS <4Jo͞+7ϭt|Bq"6{(Ÿ9K|ۮCO J-{fmlqFc~~ǒ?Ϝ\* ڪi@[j{A,/g䇛utfJkѓX.EF%mdňNYYCA×%"wsoAAAF{<:6Sq* M67LtN*I/b w(cZVl]fsm?X>>_./Ӿcr;Ӟ|6?嚃Aa̳t;Ă^߾X&\L3gΜmu2_#[}~Qwދ&`B-W`ٳ!ZJv.cypj\Z`x Jj F7q6:=\1RM%}K?~B)!e g:NO"3۰ږ α4k;. nyc̴v^=>uz6P4{=S|y,_?LDgCҝc0>SҼ ePBr%#hcwT47 ]'?$[YBP̷m&w{&K`REsL65wI]4'!&epm1A+ݐ 9Mʵ ex--t>|~,O9c{3Ub5?LD7|Im5<ߏwuduAAAaq;磒/&OlrK5AHOZ99;ޗwUz1I(0!NtyPv7ӌbhz9i;&d)u31͵c8bx//|;*5̎i1{ k&cYfi2P]?/3#3&O7w~PN)-X akam7ZI]ZZ6X`[ɕBUOy]j'i#_bJCV@w-wu Jmcbmѫ =2g\٩ ~hW_}8y 7|3?CϜ2qTkѠFs̨]%K˂wnkv ^' C9j5qz4?~3ob!b~i/79H?k #0R]u8~18B>maRn*{hB NKkq]XS[<~|a e,a30ɁH@ҿ#73\j7flMd)ҵ̃qTnMSr_ot>Cp3gϫ2Ryt]oSBͮ~n! L,(((((8~fw{ ݓM:[I-UǼJj4^_5StF9~/99Ȭ =,yHE~  Mpbu4ɆnՋhj]vz[/nFny ڳ|CັL)#gI%I Ia)Bj}c>x<>M>+ 4Pk Jb؝\ 'xJ#G'kkTPs"4Vs\sA AqjḂ` pNfZtqd&֑T [n*١mP54j}f .V>\= E/?{\fzq3pVh;ӜÄ'?ORdR\@n|yծ% GZ3Cnj<)Hn/^f>BK}R]l5@R5so2c oHJ)tkcZJO6}Qضb/Olk5 qUOEKA _/99V`6t\T_tCAAAayz^, 3g[<=CXJƊ0 e]Ykf E]tJ!i ۓ$Ql/녀Rj0}Kվ~{G\fE&K4 vέ8;@`>b.w{|8p_PfE̊1c亮"`ӁCȱ mߛrȉQ~3 6>/;Uv @@ÓUU9Do=0|C%*c [D? 4k>9р#>Dnfyi'pam-:B_sUMsO0b~3ƒxRs̽1L4Y)0!䷡k yr|Z(U!T>1@ɏׁ mݺџZgOjOĕdЮN=0QY씍$18X 3\KO< ^i}ќ͜}-Z-i=4; ^^ 7cc13Yw1P:Ca'͗R|Όx9Lp3` M|ТY\_clx?ͷ}sHJoo~H Ha.$$zȌY ڮňؗJ,o,a 2UUJƖ0&KITA gcxmۚ`,g#X|\`ٲS'( (l 14бa#\Xsx>y_7e;|&嬱ot`dϜ <˜==ClѷJ3A%Q IDATg[pVg.~ Qzc ir%|HZ!<Sp/M$}!$x o4?^ ˼9CѼ ޏE-m޷4WԘ*hl>M[Fֲ\nY4eF*Nl;O3qB{߿7?hEQi>!M a!jXƒscPQy?;@\1Sqe[%-(49{l k |?4^p[nXZ,Lѹ?^'A5kRIZo ti1ڧyDqy9bc6WQ+om~~zW~Ϫ4|I^ um2hװWgA굪pX6v׮'{԰,bMaK׳{3 w9l/-eγoia(Y*4/pzMn{Vxwq󸷋KD:;6HP[zaf+vkj`"lgRnɂOi ~N|4U6n}f[v pJg1A ]cfރL20`zG p{qͻ}󼼆~xfXW-͜a՗@\t]nJ-2ҊatC nǜ/fӡŀ?0s`z*x;! _6Jff*(c24}wu-Ϝ O.F%wY?˷5RLI]Y”"Wo/+ӧ.}]## US^qcyUtQOQ*gl닋UPPO e,iA2}`tƍYȵf~<۲Sy =wnwϸ=Gg3ȗSQvV?^ MDDR"͜T꣧ =Bb , A/!r0_~506˶<-pW<Žl뷛?X6%!!v53һ8/T2#X]_!ޟEsޢKh.I2\58sgX_tDJqK}h~|ږ+RyfgC?駌T3Uv;dەNF{[#3[9:ozA}D>svlvv5wSLyAlFySŴۭ{8?1ec\\,/ZHׇF\B_[H =Ͷ 6 9 >Y۰+ ߣ4ZXgf`sٿF GFϤ c3Б= -|$dGTicgӶHpV4@JWl @3ٌ&%-1:kt$SSr| 1K *(((xqd2;&!HgkOZ$`ilOe2g51Έl ОfEJyO3tF-|\r LO'pY 5xSPPPPpPQsmX s*U^EM:i/i>޴zTԍַ+Nn/Y9 ll9yY9o =Ї4',a// :^ϜWQR0 拱wbTG.m׉?@†/*́Waxr{G[ n|zj"s5q1D/pˇ~|u1͐SRZ,B*l,v>Oy9yp3|+źQ 2 Rҝ[AepV%sy_TUMxWa\=W'GJ=@ l뺁fd` 4Xb &/Y_Z(#lWݼdQ\ɯ,8t?a^x>1*/^<3$`!Q$zRoZ_&մI]V0s6G)tU74@'5*g){O B/- 3'. ۮNe4#4 @2֨+!1!ۗ:nk~q@]xpS^v.Y$ΘbL64N~k@1wO{oSK-@xp u>mz:9c5'N>9=i(X3*NBCל&=y} MFlI=_.MM-+`?3{}ۓdՇqD}260 Ne#y`Dc4}uK̇Z,%)^~ڀxaomDdEbrYygh4r9/u\srP4>ش4X=gY$ :KoQ5V6C:..Lz<-ܻ5vgz[C}+fLHyf8dzl YsW~>gL@=P0O4vi?r9ӳ~s7ReWNRNQo3KSX@8d)7u^Tn8 ؟wwd<C]<^vhڙ'tYy8۸f ~\.}Ҍ|n99rd]/:k#XmUѓU4jM񸝗"g }'u*?t\G<" tܔۖld`'a28?IfO/ay׬Y y ;_5OC#o>㤽$$`H$`'a0LÜ..>>{J?yj}' /s,'SrfK| 3/[0z7 w8<U i WṄ7[q}Ýg)Es|FۜZ]q;!p DJc7KRJ5'TO]/cXUz§t"$kE$DګJ⚭o7[0۸af{k<Euϧ ȼ[pwTQ<Շ6Է3VV2`›$`Ba#-0F9 !oQT-y|Ij^bڶ |.>h/v)W±%zII*I=ARy9J.|u6N`JYna]4/w9K)6`JbK1[qcrdƓcbT B6n,7=ϲ%.@!ZK8sVuuʙ^4 "H xH-q]!$y|8urqqk"͂|0$/ݻ-7C a0Έ>{~s $<[ly8/w3E81nC!j1fV\t3c/s %Y;Nqkv{ϼWxkWceAv>.kA*I "%10(Xd۵PisDCv|B#B'gDBXH=;'m_ϳ|^^\@qyV'U?wzWa|= 4g$sYNqu &Krs5ɿ.((( Wqz5|&y֜b_~ۮLHݛ ˋ5l͞[#\{1j̉[p1:b*?v~zJ,vXH\x)%U-P.ra?Vt#_4l.^2dkkp579Nf-Oa'F҂_Y< n˞ez O^IBx!{C 9Ln/Ov|Ъ '''7 ]y'H=;T. ?Rzmzh|$rx`TC~LVbTKvJm$晱Jy׎Al{Jᵾ'd!.$c3 3}8Ԣwn>rJ@I`˧oǶ9 ,pc-ZNyN> `f+V|.މӃ_|Ƥ'9qM' qf(.8hl20`㽬>LƐk %kU4[wPϜ.^f'gIbwo6 C:sg>C̢x.Zz"3sNԨk'cO?~I# ?z/b|ͣ%VPHctγ~\t76/DОfu=:ǸxxP`ywz\OwE?qέyr.=˻c߾\))IJP/$0?zvw|L>7_~\f!B'捋9w綖TKlZXNy0^n#ÌD{k 8?;WMW΀>5t X5o3^M׌<;>sB~׬- ֿ{i,(((2Oe_VlxfL3%=a<=IёOWӪTtfjO ؙ4'd~]q8(+f_ŵNNswӒ-Ey8TOĘbОh%APrt-duw 3g/<q:.tS/5I =gZt njL<ڙ?pq͹Wݬ`N,,Xx(bX~1ÛKgif8yWĜRh|kFPԵk h7>q͟fzͷ5xSPPPPpPUx3(9{>s׭g y58\=gm[0[e_0rOtyKOjtGC!t'|zo_" &g8Ndvfrৗ$HouSݼztcy -c3#\R[]1BA+';v" b~a-D @5?dڒ-EG CB5 ёkN5Aחy8: #E{&EY xNA</"Zs9 4kyxf=g37t WLzu*k<$}?yzm[̖DA$!?G ¶kѶPY9lLprSJzǙB,()[DZ.噳"ye]o*-73C/3,oٖ=<nE@<`,yv@32MiDJ*K8#H9@?mCybٴt<2TXQO3g2q1,>sX,IڷW{Co<j԰k!h5U:> HJi㙸fŏ}< 0c,sAAAF<ym͎^L\0L»iVA =IDk%Gz&^M6Mΰ&)V#kڅ Z /l[6,Wg ޅ;hm`њpdSKչ%y9yΧQ'2"/'j@ c!!@^ H` #FS͍9F 5۫,x{մ[rۼqγ޽_f Męe#TQUU QU1dh2]z* k^,׭518[' p/+Z;O xzyA4 #4l秶xfԔڔuX[j!y,{2dI6][1c9b/>A=xťxÓf9{rc9j#y ߩ7jy^ s|<a?WΫ,XA[8szlw SKh\1eSBݸw%O-[Wev82!o6% DB4vTg] $:>ӦCڈS9VRJR lOf} ^)a?1rc煲$?xutW>{5E.=kvKOyq͋!`k ǡ?z8{' hEõ 0AsPxr͆`wzwp IDATn}3'd 0u3#x檒g(gfW7n֞hϗ+{?!uÎ 'C^9ץcvׄ1HeYqdp"pJT%~&X->}-]v|Uxq5[4Ke_|ɠ]64%sj%'K58_ "K4Lwů8~PrT8#ϑLvlA[,ڍ]Ǜ6~?7bk8.Ks/nt>Z7OG>qUITD,5[35놸f koum`,E|枏zs͎rk6kϬqÔ㙳rGZrUsZT*\l'!'|<OX`] _??RD'Gi6?@^(9A6&9jK9q0ȺnLsi9^=Jqo VcuqXqeԵX4_^ՠK2793цf&D5J֣8ⓦjGyȿUcJ3a1;1mtq LV}DxRfof?1^cA$kk&Ln\sbq͛Ubێ⚻\8><|ܙ;0`5:$fRS>|99󒞯k6/:ٝt<3Ŏv‹gCyɼx,;?(˂A{y,@`( =7u~BǍ)5|exaU%~!s̤ͤ+Qb/Я>smk0Z, ,$9Ur(kv7Y)@۩]OȇbqBȷl4ڒGqy~y[ǝ_ 8bu)Xm]z lK #8`0't̶禩]{/4hk| 8;Dvgoo _?3?s]?\{Z-72Uk\΃ fT n)\%%N̒y+[y)<據#u /8fөq@b=2m߂<c並xjLABS- X4~Ige2ȥ8~ 3wqH\AggqH U۲>L‰E9zO~ίz[BMxlau,Xxm^~xn׬nlg:kA֋yDhS2~k\ӀhMA.m[zJ#tѶԥG1W\BuEh ^0̸ ~?Р df[ykY\bKeW@RY2vA\3WIaY38AP  n_*,/2~cTfS򏔘xf!`7rG ێo;fߴ%tfV8!x9;,iZ Wg1M$;u]0v Qf0DUa$iu gB<9_ngis"چFx& >d?*Evq+k 6bƷy޾#W5mny5W y# %iW)"l&ypE۫|4pnˢ^wff[\u1|%>߉)tNE8ck.j]l_ IN@ki`Ĺytmq\F_FѧDwi̊5j6g >2= ˠ-ݸ,Fڢ'^C "-esIG9_-yQ*'-bs!smsP-[orVQӬ2jܾrc76" C额40eA)lۤ`n)]ࢍ|6\mMan } :`@sC tdzfS[wɼZ=:%5[Ss 9MRS0 xI@x|7[*{YxLWKPmzm>r5:ܱ|rJOlUHh'I*@$y$!-.~f|2g7Nn͜}pmͣw!XD#/HEsۖMdgΉs,kj= k*Oev/8 cmyrX_=m`byض%ǖ:Cj/8cdo&IKO驱KO`mlfAmB'O>%NӡXcp9uu  nyu?%F,2y5>~B/Gt~>&d6󎚇f[_zU*Ԕ{x:/F4 ;_Yv ] k7.|`wׄ28tۗP6'Nx]Os\,Jn4 OW Ѥ*WsIOq/͢ǓorG؇ %,kkb>;q2ʺ&$-l0As/.$cr~xMŐ9?&l\0x4r4h&ͤն[b9~ﮕݷnxS@[hHLH5C5t. F:Y+=5.6먄ɢM`|E.ڽ0 0`W2wz ՜`a0etR 5,䗚)MGNQgk UNlw{EnA|gCD%{Eɑ3u(Cd_0)U2 = 9gq髳|݊X<3H2z]g% pm b֜oK;vfVPIt2J.mK;Ĉh]cP1ȮJİ4~VFb([yٛc07f_(+_?-'*-:Y 8zݷRdj\/=)=zi@ ^mf5ۄ9pCp] S,\lϜ$DU5L3S؛7ҷOӹV[yQ&>`{=/L$qWBQsyV ?B]\>݃?3y  *A J3`Vi~|,"WkhnwooAc]$:>}(KD_~@ <∭1LeW Gq^=GwEU~ZUr=$*) \}r~+2()Ymu4kv~M]բ2;E[5kYS՗8>1Zʠd4Hv};>x!zފ 9sʇkg-ԖU4;}Vgi6.ڣFz)FESq;9Y]R]\r].|v9AWh0`vsQ`pLf3g>]A$uRnɈƯ|ꪪޖjS2`ƏKJ%GŅQpot׈~D+LyYr@5*3 \WⅤ˷hv |lבЉ %)1!4'GgN{\ӎœ9JI*n "Gw6*oybB+ qYW@ I~ut.U%k7[,ڲi\f@Qlșm]uOb,qQxWUzJ^0`kUf|fͶg b*kH-{fkfWUI +%-?Ny[~YS\7.Gf3˗:|QU*Žq-D"o.Mv9Z'&`V_mOLtLNzoʲ-5KVn _ؙC$is>UeԟzSIFYXתve`\cn{mt|-̹QVe=<lC3R[)}wfc vy.ڣT',YI]PwBn`\ fv{! /Lm0`:y`z~)uw+rf7f,+RWv͖bΝ;6o7ʅP yF*үw#v7454ٮPRb7He .8L-y2L0qHr.u,#S;&%HJ Px_A?Ll!Mr c\ĄbPb.{[t_l pXn "0ϧ'Kp8$ް@17EM|}v̍x?XNP:]1Yg]kW8{qk~߃_Yh*M1, p]=~t2q]_g|+&ӮE5[Xfi'$R?3R`o??yLC,M(QAvޘ7Ҍ_jD .@^pRb8_jI<._ԓ%I|Hwhrc1FM,`4rλɠЬ} ozr+緻ˎ~|b2Xe8]Q:EY=jv~3^k׃PMe>?}Sj͚fky5[ϋ\Q%$U'*SY[Ff:߻],<>`.`G O_ Rc1?U(Οkɯ\EHO<ב9gys_\$1RVb @nl=e|E_H`s kvSe٢[qǹO2TѸ/gGR8pIt`?3CW@POU#%5ْT8qMeNXٶe<29>߫?ދ1\.~ooQ$ ]5vYD v 溦i{/mh8h&V]mI֕0`q`ݱ̡;o k^+yHKǺf`[ɺfɚ y!5hCq ߐm@f fOǩ?l$+W/@,uc`I*Yپ& amxq¨L?EB8ʲkylto63dzZk!5Vfbo~o,fߝph9cIBT%DU庖㱲D'uц뢝i/먅uY7'|l.jwj ~γ-k7|gG6U]٧k 99j \gT*FU"CjLr;9,{wOƣ$ugwXV9f' $_`pX;8~Yz,QzY[!. V9O4iuYcܘ@q`eϥӇ@S5gPjܕ)֪Bμ/.wn|.]oJ㐸f_kkn} p"miZB)\PUQRW|,~~OM]ǣzO.VUԕZujmzQm.meU^^oWXv{?@2l"ӲPB3Ƙpiy\`M p|S6* ke00`>~{ՎvLk3'|$'mQ i/*@fxXpfWe.τ(-e/=rVPo^9}u{aWvwc]~74Ad?ىǏBtyv ޿Ġf Y=ǃ/ v c8}V}<xV^j‹k "֥JBu~YϟK64&k s2BY%.Tvu?Exrciÿ?>.6Xv}-MS߫Ox.fzD.mS'ߟ3.a*!J2im3$#'ii2&TW,XI|ة6YWm:\ || qs55l},XjVz03?& Qht͞]+S]%D#1On|Ѯ ٝ`)L:!UgByG֢]Q~l>Gg`?X0-..b6\fT!%Xm|նO75`yof~rbo2dYdeab'cH 0 $JZB0` f'_|o!V K- 0@i@T6z3e72+ ,\'ypURPf&ʲ&,{&~rfyQrtM8?Ҷ;7D%o!~'V~Y~?Hg$+He:?/ꑛJ{:$P\<}\/'ẕW[E;jF=n_>sKEc\m| 1կ7ͦXe<5efA+4K597[obV99?d?9ȹE(Gj,JUgYcyU1"?ux/Cy Vhb{-RZZ|K,Zڬ͌e:fz Ek vi|㕻yz3UAy^2E[.%'ʜ4 T"BLCm ΏQ]zg=f5#Eŀ\soӔ ~0WfFP~KLP5;ͬu0`,c;+7G'Tyyߣ( ȻP=;?Sx~A:ƏFc;~vyUix&>*F$k0¬gƠfO?}(=CA)"tNL ?3S B~qpc<~xLl|o,K/7eg ۇ`%`+$u/8ƵRY*9Ï6Wjz[̣P0N\,!Njw~Hx:ʚdZiMl)ʴT%bj3JWS6Mp!Y^=`8ϣq)CyYfʶ<&mP|Ҭ2è`R[vG*J&O-d>e;k>ZoXfI \S\yv q*:KS,ʇ;%-/WYﷱ-,-ï/ eI;_qt'^|kpg5;8{ߜ3_zj~.ץam]%tދ@8^S\3c~ٳu]Aiq#]Nt|wcRSl|c`U\`Jc6lm:n#Klq/*ns-ل`K݊eN)?WMʼnMzר+Ө6fe{!fn =D!ɿB}M}>iJ`nV_7AͷZe+%2)EUf'XYDK" ͬ%;7ifېON8_-kIOQb52N|2Q&pS#Gm5/l'r/&P}܃6'O>%gᗟ>QoĀ~>k~~]<sk*R9ܫ?T=h b|?_orf hbSURo_:Fmtq%B=2m6f8սE/+aa>_5\U R ŞfA)@x`sUf}Vmpf.O=8K I܅s;|wIz^^KvG 9#=秶̔ &@̹-}8 uβrG3P{r$kJe%%Ѝ앻?9WY5s2yd$Ç3v\v1y]C=M󿐜1_BY+/T?hKS Вێ{FϠn3F@wZs~aޔ7W=wdRoԹ! ڞe82w;lO֬^/sp~VݸCv?$3c܄`ԞʒQxf dFHͶQR [f|6f,$a;ns |[8sUj~~?P_|3pw,c`yu&Uf,״Iy1`EdrSet0t 7Je?jdޢ(02(4'vѦ5qyU^8 @}ӯO4]>8OFenw?q"$ݬ&(A}NhjEgqR: \*X^_JKE-mmcDm̩k %JjXZB`4Y+E:<<;2#NXuwF74F4'ل`iN{̖T٬^~j?ҳVLm@plm^u`jss?Ծy/srs9yf+y2/y%T;yeԼT2S݊%inj*saT;w<-Ve0 6#۽@Y6Q#~Ϲ|ߔrZsiAg.\pmOS$kFqs{.8]|n~ x-aZ9Rs8e9TcW2{ Ϳ=́L 26ڲl 1b#l۟Z1[dx2gu-Lv"A,:j6X?5qUoOɟ/NF(YU\Uz10BDmζdc6+5ۼZ=!&4Mhoe>> )8I0, p劙L=͟Q= G HyD=޼"qkOeN("*LNJ *IHT߿;3/cv}}vP4q ;ytM#@>/x?kud5XyoyXz4'#HwTKB#̌ "\ g5ĵ^4gIС6/K'\jhSucb t#WǮ'>sHs. ySi٫„ N?y"JK[p+2O5 ډ~ElnUmE]x.+kA^`Ȑ+4姿ltIHKUQ4rbڜ͌$a95lFSU$PqGn [IuNk7f؀a<`F\>{Kɬ@mMQݲUg2D/7|WeVevLʬLN"߲ɨq;_ՙoW̘x-??h<_}cgwO&^ذ?*03"㙹b.2Z%EtOvV$q"u"bkh=04t$I.z|1΂;"&L|18%(HQX ~{mӸ;=VœPd&fȏE&vzxUĢs蝓?_,RmJsDmk{6js 5ۉ=W?ɺ5x xѽ}k܉a<`Ȃ/E>M*sXY |"qnE*s/WN!,30|{Ld<ΘQc{z]F8~|I?^Ǐw@%lu8cö[<υy,Ol5BX5R9pzx"dU ,ȉ䗚ОxTf%X4g wnIS;!X~.R-R.<6_B>Ni!-ucBnWJ:@Ů6O)2C'Kmp޹b~Rߏp;"/-sagHR~oLӬ Vm.\Bͳڌ &^&_RcIim|js/:`z3%0 JDYr9 x%!㜫J*ƶPЮǾ7z/kcuWUu;!ӆmfjn fvktX@Atx"i&Ugjf8tߚl4zըFd\HX'5j3)0ePmkjRf_mqW tN ǏQ#`6=-{/}\M;0`їQc 泇Iay~,aUZ2ky# IDAT)T|GT[SeGegL(ٍd< )QT xztC|TfLa1?DUq)UW߮?r?\$DHVs!)OI]OzS" :v wel2WShGJ^?\EsEpEssȫfnxe~d͊8gDo1iYQvyp?t~'q^Wg\c]OxIK$!Mۋq"x1I޿PoMǯ^j摴K+{A>%iIͤfFY`jj.QX iK$т{L1>1yiS߯~(.9@8w_>1lQ_)uj:w'u!YŸ$ 79D|M疭R,7x/Mg?Ǻh f~ Ti9?*Ͳ `)Tܳ}<re[x" @يe2,7ErnOƂωȖK[Fx[~qn6'c轓?Mפ,]8ɨ2'6O>js6OyPI@&q2i;͡j_ÇtIC 70GJ$:{*s-{zBuJڠ1*-Re6 n`2Ti<c9#' 'ɭoQ17Gw5+)W8Ϸl\5" yL!"E6P)jvU[|_1"#91FtГ3ξZ-_RmUqeHX悽{Wj*Di[uuKh=.Z\/\mHݸHC2% z P1$LZ$Ҳ,͒XۆŽ~rWt~wImιQ=ܵ" _y֎׻X] U0NGӿv&IUePm.s6͙6M͓sr2iTotӾ&Q2 ^8|Mr Q̵_QlgUf cPeκUf7*Z,"pOR  27ݬ"G9Qpc<%eS3Y4{V嶔I:qK>J;V!smE؛3gW릚Be H$$LLR noOt?Sp*\OYH@xr+S $y0Ru[@sȚoa2N޽OON&w(#h,Ȩ @4yvlafpQmzjs=6KFos:n/-:'"e̖vyUx*&OUYk,R*Sf2b7f?gskd>e /\,k}UAϸhbT;/J?ko5~? P?ʪz ĉHf&"̀$Ig+N5NIv[Ee5imЖ'Bmhb}i4:fN qumax E~뺿Ʉ_5hZf\J3b>*q-v6T3:~FZU< PN-9* bYV:v2i T4S;=CQ{>}[!'ݷ4.vUJmj+ FűjVͯjs,^8fM较nڃ<`p:-U]$Ts:Vb ̘̋UBZeɿWe.|QS+2WA.u58;s;G7Oof8./6j?pi-d>[X>w*ʥ(?Q?| 8XkmW˯UR 93e)벙" XDTfe=k_R4/|oMLᎵCLx|*S@g2՛4lhh(ԧU嗛GKdJ❵ P%ėa _[[Uukñ`7z g~Vϓ,T?-TxFn@mVI2.=~~oڬbGJm{j5vd>%8YywTg'dF l)VjF,M7 dQ{y]89!MOYf_ qn\ewXOUf3iG,46O0V*ȉe<7y2F̛7<[ʥ$C^,s|}ڂb)'=>!w'˗pBu˼&Tq"'"$lGzͫMz]VSؖ?P.Orf\ Y5^EṡDEc\mYj6ש'#&V8I_:Y l cbbQ(c1.rխS}?OJX+&l}d'[5 fisv߷F Uxi]\q\Yyc9N|OOĭXlQ6E"gPeڨ8~llt *pGԐKbXCM>.}<[b>??,[/dsqUf *`1GXBIDeJ2[?3Ng;_JaαZ+ IM, p2`[ ei&n)F} .ARpA Zi֗g1闟< fs7nEKkv3jѽf~9:[4|\J5}惾c*/VyJTP/@W  Ue}:c`B׺cx!ҲNI6ouu=r&Ʈ-]+&yVOdq''6YX9 :ɏBDNloOQ5hm!+l馽Oٸ6 JP9g`n6|76ۗQ?qa qsAxyNیw=l2|^|W Gev2oYV2Ѭ2Oikɿ/\Zq³Xnqt!9`<`4cU\},ó%k] GdT:"|Ǩ}|!"j̻x]6[gfJiWۋPe!z͊CW0v|ዝtݤFoڡE=f7k=u^4F^X|CEozgCni6cL<~|HeZvVyU%֊ZacP>av_ӆ@$w=9jZjQmFG:bz4%;7}__?{R5Bۓ?]LǷifp MsEyΈ 8EڬI46f|&I Q.]8FeK|;U=@JL*92@[Qi;*sΌ emʘݤ2O-h2NoI*K͗ Xf8ca[ 8 2f8*w9 ?R ? X)VB>H?EREWPW\f48Dd$r ˋRpPdFM Į#hya,^j*i\xKP5Y}^/CǯkuhݼJV\ V'DM.[P5͌e5 -p 8e,_uֵvYlGO}K-*) 1X,n(2 1l[ZE`A>lˌZg %(c/( \W93cާ?x9LmjD͙r Ƥ`RMnnR0SWU&)Xa<`@'Lۨc=$}"KRep DU扜S|mTQ̳*ʘ]%eܽ={?|m.W 童w3c8Vmu2k3g+힫/EUM|G{_T&DL5g1iT~37ї˃yXë$`W-IwqTBw}ŵZ4phj oK w\Qӗ`^vw&<͞ gާ:vUVIūzVU۵-燷EЕB0mdזskۖiY6 zpVU|ƻxWnO@LTeҦU QU, J6)-,AXI N *ffjx3LRZ?z-;40, xo|VC:F>#X.jLeИ2߂ b%ybgw9:ͅw<`1?x V6;H<ƯƳYIK+ 껤/g_uycS jWӂ٨A=.xɿ^mGeASaYVeL&eHg M;ޡ?y:Ij^|./ 0xf.36#xDxq6xTf-y|?W#~wl]ġrCD|#J0Ya@US 0INv9!!̽]Txg=o{ \PjAlM־~l{ؘ^l'ϔ\@j-9dwߜ7N$YFX㓪\,MTͩT˔QXJu * $6úit[%jsY͈-Ʒp~3/Q{_0K]5C-{5ɿvnOxɿTJLKReNR2%\I?Hw^4/9~ԙW7Ϻ?ۗwC󓐷!e Y0uiaE{v_Wˢd3'!@zS A}|s5]pgL!їͨ 3d3gQ/RgD-A/ѢYaBs8mȚG,;΍|WVV$3!MczFZM` X IX K+avO/d,vhL[$EVef2##:ƍȪbw]q{N{ P(o6fPVS$YSv 4mk oF|XW8_KU5nGD:w9^XG,DZ&C,24ϳhߺVڙf.z袪T|L]mN{ n~6ߨ`VZmXS^X8j9:Zֲ&< _ s\f+G,Ocv r/ɿxXYw AөP^f@e0jV9!B;[KpU00-O1=<+0_K[QwmvK27Կs) 6Xd鿗F!L*"DH&I"Mb\:~"egHfy74,&ˇh|qDro*!"b9+KdoԵڒ'[ߴA lmRy?q!By;l06TnBLw;iדa~s+Έi_i0-P X X絬e-R*_BSfe=p#ɿ(!2aٜKe%0^H$-{$ѽN_\4(.FhN[] =\:cQ>L[776>2KS(l <=,)OOcWoye}07" TfITpU?U< ?l%¾l_V f4w:[ћf.ud`J]^s59JL- .r.0dIJ͜- z3I ZEſי$?[?cm؛, 6;ϏXa:!U~(7[s=yhR<VVeƹTTPG+:y͇6Lې|8m~Bv g.AOܘUPk༖L5z{}xυeG .9Nò{^^X#\X2+h%Z2(I';oݮۣi R27\߫M.^Q19}%x0=ʑw>GM;޶dNY( ȲgWa)u$#HG_z5:̺l_5/)v㑪I12WU^k|n9X}fqFw ђB䂵#R*{cֵgtɗMnm6mz,d-ک>9Y@w=(diBkBMۖ`bcŕ: X/5XO8u]SY_!`͍lLPv+S`9R2`aq)AZo}Jvi͡9(gô|ZpYsIy-ky}ls942Wdacaȿf +Ry[T@k17V|}\d,cv3{_1W\'F3Wv6O1=bb0/Я<ӧp!^_@O@y!4 YttMiD3f%ȎLK)V1y#S/PyƂfV,Z!,JUY؀JaHH>Q|" %i&C] /MM|*{%=VDrf o;FԚZFEl6̎)asc)ﻗۤ#`-({ qv` }^EƂnw7>?Bf IDAT1Y?db>p]CQcBb4s__@P% )+AO &;j7_Y)[?-pZY8(yCPs)U:2ΤxϾ]ʃo|S!KC#`@͋Cm`ӓH;PʜGTERS-tyѩM1Rk%hcz;r>iqu]I'cZvۗwonﷺ mR+'2,Q{)-L6Lvv*sW[ pVRQ.Zm> pbs5p^Z^=xn0^pޓ'}y25&,GHUM^zL^n{Dr_@^,3/sScSh lֵ_t 魟u+_9Mq{^h3d|p^fJhϟe_+S IHB{-Q=x4R?YԔ_-{11\|=gV3ǝt0![WZ.(hoQZǸ#].^$|.-#cJa"Y[$LKSȂoPN^N<=;J5`srl{Pq ,:!% HIF!3*f}<R9ݸ~sU'դ`2̻Sv*´Ӯ)C(Ew}v=Xdy~8˪AFKZZ햊g,X^l=z׏怙{qM</yse8]i²̚x5S:^E{k7~j )f 򺪼a?[/`}F_(~~ O ɂ^k_Bz@Aoeձ`.//g"{ "YHRdb뤤\Jݛ,kL]I)]]^X̃E>2[7W4_[KQ,(Ƣ(<`+ظE/D$ ړJr^r%ۆ0]~6Fן/$G'lAnזjks3oJ*('SHB$-D7ߦ:_'Dy'eILAKPIQb4 ʆioaڳi{זyܳ~򛁗] ײ|;l`n  11Of ز7ڈ2l)MuP^2ѠzTR+K)i+1O4_}z]=bٮ{lr`|4f맿^ul?_JslY.d<~seP?&y&ћ*$9_md E3L9&YSӓ/e HqHU`wzٹ³ X@Ƃ3I:*d". @oJ6yjVm#F$m~%^4!A2'rKfevL ^HߎKx9>J)3@j !udeuW @[cgjo?m^6Ox1+k2nm& {76mfTyIrG kREkF,L{,Lmo.iN`W]_C9e-4|~;f9`.e_0Ǽ̀(fe.1(elob_&,{U-Z3_& 2' Q >zs:g=`a T [ύnr~+>bJI';;dgqWT_y*5|P"Ϟ)$tU9$UBf?N'@H|"ax]Ot=Vs(Pmu 0͡uqi(Fis WŎ۟ߪf1m ɑrylU /\?`rO8) cl1v@ XҢ´S?Lۄu!pon|]C2q 8/ȺZFJ\^<`^PuywČy07`G߫ò;lTF^2"I;7vDzGbrxMȰSdf,u{]ďGnn1Te H[MkyЌH)@2+tvZ=9#waLb])l& S$@'3iWy5u -I]Z#ohjh33$w9l{}ȆhuPD!͗ב\g2$s- I)tosUkAd0kw6͑*^;.?9pcrySRfZ!h77Sc۬4ߝdE\mshc+QM ͚,k 2`a66Ti0=`Y Ӷ=-pV a/LO8{.ǹOK{y-kyw>)2`gv2`U8+kvI{2g7zU eis%\\@ZWXR"BH@2/EUeu+f)gtg fm5,-mێ[LVp,y.19:,| FmlnlYJ Rx, Y6Ri&AZvfPo b@)XH80*L{p:FDôq#}xCɃY(%t5qx\5p^Zy9sxn\/!`͋{{ŋN uX0㊮|Y²2a0/ґ^9mTX*1ɿ̪|pwnn#"I&!ϳ goDT|fjc ,Owc@;(*~Uu^! NBW49 _oyAsS1^#`>D[kQz#,\H- Ϲ 3z7̅$eAyYhԣ%xҌ%`* ;>7ԹoDg,Epe"xmE<.L6yޝ `U7M^Ƕ)lepZ絬囓anr>|Oﷀ.Q>ú<氼s84lX^:\Vنeȿwo~;;m+m6/0NjůzjoG0I%=4eBycFKh;Yd_B"kR雊?G惇/Һ |/suvxB`Miڎ(3#8jNtJ C|Gsaf~Aˈm1%$M^;:E0EK,D%H$RC2v-`6ug"!/Ib01%0IԻ @TP!H"ș) B 3M{Ut֌v{؆g6:ٵey[,$s#_~Qs -BBTHBy/B$<=Iǻώ]&عrXwT*#wʥE3Ճ^uyAImnBmz$XbLg 9M6Y^(9*FB)%HP=) [YPQi1.Є9--]3r\;\Vf/熨dڂjIX[uD irx$d]x)Uƺ~)!yn></%J.u޿}3ɥKM. )X#3aڋ Z±iO:6^zL0힉O [tt& =[@ 80m)~q$FvT*GZIIayOaXdam:9nE{; dزiȖ-hYXvW3HGK;uk7n׭gxqp|GT᭯ Fm~xzfN,M1=fz$Z}=(z;n\Oӿ^~i=$dX"LH_L:Rza^}IX/9(1ב-^ݝ3j'r2S9gFiZ)VK=<^׃ X3#_gkNXvԳ*^ R$R|((n]+_|2~*cB*Iʂ( dy6 F< l,PSl|92~v ˮ2^ۓciK@{~6K&XQ]rԶjx:EY{ H1w/߿/N'ô9)X-oNHiוB08-7t p4}o'}:εy ײ'5ؒW6lLM3 K=yr°ly'C0T$De>iKx9̪ڛˬ 0f 96?mZ_6yXxy\@"!4WM u$AIBRӓlT{ xUbB\.DזeͪS]\$͂g7Z^S|B+5Ir!\#* fՔ""!d!sz3f"&v/kIߢ`mϷb&3/cdY) %5vΟ\kI$afmH|6AM>a 0+B61صKP>6pvVm_.8˪k𼖵\T`-ٓk1/VmOgG:,[a15 5[2[uX1<+zXR[nV[c7zE1Qhv"Kj;\1E,w΄$=ztp> .\0DaCi9jeQ*}(ПB*!E)5PL,*ZO{)H: <=O= MA 64)3es&zc:F=WwKhlg%ˁsܼ/j6;YDETtXɩXEs,!ONI()DR|1E4:VI\n yrnE<~J o"19JBt{sc)UBB\e)ܨ-AT/u '.Jۭnwn:LOspSXZd3aڝԱi 1 Ӯ*CeY+PmK3ge*5x^Z"ggl7Q>h92LKz̦y_lU'Ae~X60BeN>,tXt^vkzxDyWW>jg/G*J@ $%:.67~D79VY%Z]2bO'HSYGAwiX6bv} „i+Ek+߬K2T8_o* хj+`ǀ󧞷h ˫kYKswKuae` IDATمdþ|yU Yy)?9$RmS^ʄewڂbamƃ^T<_uF:mtE۝_NADׯP뛷X΃i|5[:-Ǎ[Tlx>>GrHAtmf@E5&I"|~H!$?GU.3+]e3Yy/L)DҒZjaxm(9}X I(t`y/%m1L_rxZi;)c&Ux`m2{l!m,mΏѣ"LwX)1nase/D,vv+Ϲ]8ڳ 'Z̷Ri$޺|* DKxv+#2lUeѩ4eT~[i2TF78SRT6ǹlX絬啐o`j| #G߃L 0_QaUy}G0oR1*,{NY9 :`lAy^$W\r{Oko|]^ ~#mHt[=t:=녥@߀8α\Aӟ8E5GNorwwn3%ڦe|~4e.Hʂt90i=Ѕ$ID'ŁHT_b*3ď@F9\ k3+Yujq,V}42r4n\Ί^%y@ˑY]>zh oQ56h%06X />{19 $"(IԆ^@"e9hmۦ,~bT*xCJ<[eN._tBֶ[Wk9޿V[gL}IDƶw~hn[o^{՝*6m2T7kB oeU9¨}-E8+|4i ײj 3T/ s\,̪T 0Ǚ9`V'>cV◗ My)EKe"EC9,D#۝s흛 pptn# ~[A&h[{zHƚXd3Bq czUQq/Jm{y&c XHq\xDX)&j24uO5"&c 5q|!X aٞ?SZkVvzd C+n*v nTn- eq6 F%e)\[*?{⦤ވt͠OOF\$$Y$jͳ^DenQW_],<燹9Sma6_}08Ko;6%t~s,|lL`>k:x[?6*L(ueL~*CfGܨop.wU6PK5{{ioor0\uϠïZH"yaf=0}ql0h2z-[ MyBPRYh>;Rf}t{ǝv'>v]~b5Xϴ>cxEy~=l~/ `o\`N!ؕzչl"/5RQoQMb-~adX E(>;w-Lf3fk'pm±N!D5\Iݜa;v C=lTKS/"kH]_-!p̍_ S?2Wb[6+DFL, %B@*( Hk#.~K5T/*7gYlj+)Ke5 rme4$ݨ_[T }>=_ j S?5lھ۶I7twONRa]F2T4;Ma~枴m{r|RfqΟĀ3{P ҁsX)Kv}|ݺy !`\{̶rE]ƖPBsW^JKj а|p{7npc*'?l1w9ˌ-[-#[eOYvzml#/4ISևDy-H>|(PrrYţÇR (d1;cՌYDmR%E\iM]ayw`0Fba&SOs xu5h7rosL|fbL @=8y9CYt)ЉBt92杋gϿxd`DBʠž;],~=бiMf?~,6Poq8BD%Mh @@$!!'oKNYO9{mhm7 Ϸoôy*VK)FUeeL<;>pnFރ8OZA2-+Fg_?+_ZK<4|glyD'1Y88`(Gm:9nلes|DiEyR-" R^X6dʕnzikX.}Ln[^n Z]<^Ph|g o\戞[wQr|]/P!!!Ax>}v<>(|cҬ&"Q^Z`૬h7 NZ]42KMڃ^ok?#axMhDm6a Α~97v-ZBW6.[q&g RQIj9 Ȝ R Nq6m~sR?-=?U0=:ѝjؚ_Fv@9wYMS:7:[@ ^s&S[;;'[aʁ$^PH~*Cu70m;A6y@C 7_5G1n!ά3 E/U{eQ7kYK Q{k$̟S%#VY1I0JǼccl|Y :B\^_\{.uxᔭoBo|c'uլGeJXu_^OGݳ` z;8>YI?wHIS~Ԥ E~~RiEyjENݱoK֦6v|3xcÌ#7l>J($v%rIERo6CQMm;m\X?jn4BJ{)tܽ5?Q6dPf ۂ:mWys7}3850j/Pǿ{(r˿&ڲvDzVe-oDlF%L׃e`W3Z5S6| A)ټXczm/1[HǬ²S^jnK-/9o||wv]ld]2ֵO-~Qy?;@Fl@^H/U/V2y^֏m#}+byKZ_4z5Et|8xr9`:0&$:3RάIe]Ϛz27C˒X-`{෗+h:5W[dN6/a$"@V_ܧ\U;~Ғ}cl$IIBkg&&҅Hh'mo-狀Tsv؟C϶w4܆NDZ[ci6 r}[lYquvi:tyNCZwowߥn+[)CVr=]yagҔb?q\C2p6p>o3\ύ$Z^w+XVŔۻO!K=Y 0b!y0cyỹrL.cV奠òKԽs|7c<''Hӹ޺&6)CTյk@\v-6R<نl)]sBŲЏ@x(x]:uQ$(b$e:%hXk𼖵p9?X։&re 'f`gҥ&,[03Py}11ryNzoXkKi:_z|݃dܪnbsU?@J' F[)Q=+PKord#eAESXfy"_ݞ3'ǬuwMܛfNM J$!!Z.V֠e.߶,e7=sCXղֲ/2 ;$I┨q9ӓG<ւn34V,~@. צ%=)sKUo?Zt>hZU^dmeel?Ngp6S\~`>_;Ս;0auͽ^3[X.#|:laޝ0jcLP7 pWY|)UOQq~Εy-DYیJ3ƹwcKa 0܍ (e`VYMg}lgc6auyy.(Iwnޕzz)Sl=[WNk? ;|Y^I?A~+Q?5 <@AB H0h3<{z m;”HyO#K3ܴ_OFu,ju5(\^B{]yAs#3*$4΃$mܳavpZy,?R+/":旟?;q@J4R,OT ]#m6|yR)Ʒ2V=>]8-o;F-Hf'(lo?K[~Ϟb>gX淿sulw+3tV*oDuCFm 7qEpPcʪ&'5xOгjnGkf-ky$r@Cά~N2{CͻƙRJEd8Z^Seݩ.򘯤̘1~sX^+3sux\aծ\oT~y!m@R"K.Qaj(Q8/^,Hm] ɥXվJ)6r֣OjueTk;+XxVQ.C.RScR>C]Ӯ3GJG"IN"@jk6mo\哷a~sf6\7WǗQ)Жpu/Yb4 ]ꂵe}%amr"^Η=OqKiooxߌ;VvIϖQwN99k }C }Fmߌ&}<啕e3Y) ߧ(`0,ف9oa`%䗖ay1slDYk@^hT[צ~{-WO|圡_@[$sk( x1LsQΗ[Ģ-Y=<(gv =/X5XXa'ls0*ɊKeo%!&T]b*<3Oע:9+|gX=–ɄX)$I[mg=?`5(7'9#J-p%2vpSxp:: , B+'ZRqox_(`?áQ.$$f0N@?SznyL`n )1b^cNỹ~?uY_>驿u:_LP YgR.3_G-O/CA,+8SJ!OzP0^u"c~F?6u,__LrGilamDA2f\[n*iPjcm&+f7/&4Hc7oAWO/g/gbS÷&.LJG_ 2C<ȤW"~ɱmsgȍخlďWu>6 zhc[ېll٬!E+-akGͣ K[G<g 'fp[w{4ov`Q26@XV0y8?-p޻op4';p;!*ko܋+ʣrVl\ WȄd 0iS̚)_7;ݻ6GA^_ ưP孯‹!׷󭯤_Meuvz>;g8:Yz2i}L >Ijr08DBdql@bɮHӗqwBHPZXfB="e Hv\GˉQh5svAscyZ j{Uf.(?6cbf&2߫ aJ˅N2>>,9s`7ډM&9/rvc~i'j/(W9 Oǚ짋~e C펲L۽f7I9%ϚTD%쁊f"%?Z^T8+\yq(:I0m ?CcMj DHCck̍z Z866U/@o w\[Y[1{ho>{X IDAT^~ Z]c9X g?XZ& `y9`&9ixB0?̎){Se"Ǭ/c\<ͭM%MΓ,kp0HS:0r>rn瑔D W(C^s ~KB}IB`9G #nkW4wWvG'D3 -?rm~hCW(DH 93}I(@$, {DDR$XQ/yQ<N P}(<Ǜ`>7OXdOiv=}a6ONh6dϿ }Z}(giSi?Ogx1GV\;^kx+oo\'7Wc|;uٖ:C'~I*%Zf9j5a๱Z19?X66\d_@9`ɶB%ZQ^2`vYESHRE.+?7Ʃǫr\q'=k/syu9c٘̀i,߲zoݸkNun|~}q 7HVȚ ,0"RčbFURI"4+#!ϋTˤooɍG0?KnryNzqc,UjVLfHrmm֯B)M|>ǟ`ž}){7Vw[op^cSAi*L g=@8_[ǹS@ (D Kcet br ?e?%S|’^F02~ `^#1eSzZDyFcI!7w~nW&GFuy[s^.bt^ v}vS8D=^N17~e51}xV)̹YQ<05sl.W"Y;XG-!#E_ *́]+/sl)DKFA0΁V._dlYnC/A8|-XZiه5qj-u):"+?phEg&Jd!xsP]&1Z/¯p$Jr?=~Mml,D*vDI.a^Lf*skZ=_!@tPΊQӅXf-E lR!p> Z8PΤg >S3` kU9Xsve»}R^!'xOzIWd9`V2`Ng;UXf);Ř|A*!vƆ|é> _*?Ήhgy]FSG" E%razU3zP@"?PE SHHIJ::Y?9> K²SUs47 Te 3̞s͑e>ni@ܜ2,~v fή4x8ܿ[1~q'gzi {C&yـ` %@+H<,jR'm*|ϱyXZ~ G6Y?lk42ţؕ(GRl'&)Xn<} :F o~|?C`MvnssrYL|??`zxR>|Gm~kyӊ2jRTIUq>pR <[2pVY7c2\^g%VZoЊ?9%'˫xO3Jw>0X?'ɤn//a^JK%DTȔ @H {wGѝ? ,Sö/`ܺ6JntKvosdY>vK?:?Wwz ӏ!b #|졭򙕐R٤Bk%B@ȿ|Q`8F2NDbb_f* (x2B2O>rp쫕.h~I1my!qo]XqG|o\> !e2m_Ei%Ť%!s#a) "Z%$ј-#X몳XbE3/ֺv|>Zr=aX6kyƀٗfy2zB. W 0+Qyk+0.j1b>+-5RD_y:a/{.`l8<>`]yU뛷z};'y?6v/1?wޚ!Ps}zbO!GB1CGZlHJo"<}r@_D>ZllYUŘITrS9,AP\)se.fW96sSFM y%*47VͫӺ,?jkV*.R0k٥RN@uWdF&.3`<H:KYPZ_@0˦)ErL kqE$g?y Q?0A`@Szྋ˒E?~~yK~%ε?^"C_EuKobz-H[U{]ùgs[Ꮗ畢JT)*])Vk@sp8Oi&8+a sfAg ,N8x~|=A9 4 .@m7fTf9leSuClټknݼABl8xQɒ0o71"=N[xbnZ̭TcVy|t{|W.o y<}b!\:]*ֳsospBcHcj[׃.H xUY]&D"BePY 甩g$ekyc CPn?rdi~;qkeVHn4wvd7`- 0 z/Xۚ6,/0 } MlUY8="2U F3\DTn,.rrm%o(aiZɶw< je>.t'^ \1> &<,E KE (1Uŗ1Yʈ<9}2?7'm6&N׼g?Eg=&L+vB8s% ͽ0Px̯N$ Q):EDƶϲI/Y}ݢu]%*߻7x4D{B4$qD*XQӒvS]O8;?-1sa<^5W@bEu_Kv` ~fD#PH[Wݟ~'9MhI&(ɕ@SJ2)6=iA8OTm\L5 iw3Tg#j4E1J`R"X0蛣%K,-z]r#(1fiM֛Puq(@=`"ND-``F ڇb(n ɻWk~#9}-YDp "u߽Fڲmud Q?ȫ?*.q+_m_*_p~oh``$A#5DIq&.Y7t& PIc.E(?+ LDQ*I?J3maxRVPL$md^2γ Ldw6R0L` /X\|i}}ߙҕOM7˯ްBa7`3|^w($d0MYwX({ʿwt+X%J]҂yG'=?d>5W{k~3Us4M$.g%y6?gM[`%p.eCu1Iz}&|~N|gBz+OF }9ԇY})NǕߥY/W-_2 WY86j^Rr$L_=>}604S%MkEUdRN (1 "j}5@trXD٘GQiI 7T":xM+ϛv갦nA"h`.v^ЇY4_꟰WUYfE\R/<niQQKik"K@޽G11ڥ|I%*hŗ"GiXq:J+iuE9`T[S;aG\kvYAn ~/Lh,Uf?k@IvM(̅.TQdv #  3#"~$,} "4M\ݸQyJ䅉}>r䘳̿Z C2DB ꏤ&H`:.0's e.cf#rm)-皐صc, Y E qVU9Gʹs( pC',XeVJ? u8ns2.37XQߟtS+GIv!cКo;H8ʧNNo¶̚iO*ꃂ_sE+ߋYd(E ʕ#ۍm#&fHu2`JJB:/NS홁U!j+roSS3~=H{󑹟$_!{JhU~9y>~0WBnLO̓ILy:U],0S#<l63)RP\ IDATo~G3C %~"aRL o]Ygr5`Ѩycδ5V_;)|v٪ fե*e<]6xhRL- Uq `J>f@פ]U mRHe^e5;'fO,FW4_Nΰamv6[~h)Z.uQKChv] 8vhPȇBPPO${fGՊg}Z4Q$e!(/`ˠEed[LeyJ=m/d` tS[6+`4U"\]K3鏧R}#=?tW~^]N&}{?y/ǯ_۸79 pMG"\CS5՞]1 5"*pVZ RRs2p>>nf]J[`e%8??M;bukpAtݤ?s \9yx9m>V^;;쁙2ǾUcTGK)u.Ob†o${Ѭ|Y*ȥ~-t$ `s1|?|Ic~pt04 KU@`c'2;e=TQ ^? ٲ'_2yfGbx,/ *n @DJ ku bݽQ J&:NJTc (Vϡd`?5jB6**+2[52hIV)PcJK[2'm9utQeBhh<  ~S ,l6ev >-Vd:p{ hjT*'^pv+"  xp~P?3|x196&o~;Zd{[rG5]}^xv8wawAfR%o8;M4{uu$ǒvbiһ}Zp.V +>dMUp NQ4iY.gL9ZXu6w5= u6s]<gUP@.H /GK}ѻ^&X.fC#cCo :8H Հ %sZ q:-}`ټ祕2Al1`6)ҷnlw?эIj<]wq8<e]j]3fwﬣ^}N?;#ϋJv6u!rkq @y5?Gpe*K~xQ./`d{ąʶY eI+ekYD5h@0jRGUeͲy56h,?/rmzE뤯~}cʁ毃"}JNr}ˬai+/l@ަbΪuQ&`a^Qz|:pa)SHA^JxuI늲_G/z'U`7'q3=wx }"i d\_J_IAQWB/g'o p2Ztm)ڇ2Uqv:0lg&*-UaϾ:Kk]8:"XfY'BZR$-4,QH+S#fsFl3M|>Aϗ蛦W4\mIPZmc]S"BsH`8!YMKry)" /v#9`7}Zic-U>A4tDŽ]g`W1K:D58i ;|xR'ȧ9to>IdNpt28[:%sl|-u.>(#(Se_`x2>oW~F8Rs]:z,itDa#k^&.8?}Z:sI |Z:fK}˚\^v~W2s`^JfFȞ5ԜKcoB 0EVfDS0~r/޾ڏ` Gc<~+jwo;~wCise@O1V-c\bƇr<]a|~d?/ڧTo~s@zþy\Q,8ZUg%EF>er`*ڼ̋˴4,f>KпZfu"]F,/eٙP&Q0a73gM$p<=ᐲ:3mcC,l{{Y0+%X&,% ~xШZ nOz |w޿Y^z=>/ s$K4׾l|U'lYv/V6 ;ΤqNoVOE1U5Ε5y}BYSR, 4ˑ7j?!pOKe>,wEί2xozˤK7_^X^ sqy m7!Y5ei#d0O4Dq s~vJoXhg!_ Mq/\vE5qF>;+W$uryyXJ]~:V,(% YQjd}6\2`&fg -/ VMCRWJ&JvDӊ_<[u*r:\o*0PR9S`ssYCWH1Y>qۜ!Nڜgc]b+}a=HY0x(J(@"bF Cse@($ȡNEX9|Hܵ@jH*8bdi+d:U! no; Fj6s y.E/ZV`YOdx7'p s*U50@=idޛ6G`]5w2 !Z_e>o؟>Tvp[k[szg'O~1"-b:|:$N2l%2B2EuZٺc 4 ElȳT\pW59k 1ss  9i F8mIljw~ΣΟStu]cM<~8c<|G֮P?)pƇ|p~BKl}Oo90~aEPuЋdi5- x)iT f Hi:)s6*jpJXWD9GD"fH[Dk3ie5D̨G6deV1TeG.3[lz9M|͇>+ WQq]LQ)֒SU/D̐D񗈀P s18$@L)"D$Yk#eJC`!J# h4?ꮥLZs&'AAVhD`a}]vxѵ/2lƮa?{yFЍ|]x~4ֆb€^nYԼlO"}/,N8$IKm#^XH}BhkݶW7 UhDI,1H4[L&&Ox"ڝ9N1j8X߿K2`]^],ˀo(]> ~5Kf#B0KO-`t.M^dG"rsAqLTLG0Ox߿dsO'OT$AoDRDʬ6n{N=3=k@1HU'0>O,((S4zcfhœdgB!yVQ^!;0cL$u|$Eb /HCU٫ #2eH3h9gt5`^-!4s |^o/' Ou/8q<H04i83Yi3R'"p>t1ǽk=`cjpnκn; o ,@w&G ՚i 0sOP`r0F y<۔d3<̋֕3jGcO bg]΀s+gV\6׾9'Ze< )RH~~g0= LgJ嶺OEfҁx X(/53m4y̙Jc}Mߚ2eSt"<,OצɬYb L?~|9sNʆηX$`ERY+)Ak-4e= A fH1C@^K>爐Bʈu9mqp_!b(as冉a^Ei?O  [$-/9{יf̆^i|94л){ hr \&ّ1ˢ !b&" ^(bfA&͵{*ձQ>"!T߯@,P#ZJG*P}YYkmBg^6nbRD,,H0  _(P:! i'48`wz,z``> y5yx3Onno9M&:Vh9x!`r~u#4 2En|#/_ܛ/'G4E>y)<I Rb)5:99lRgh?g@Xkko<x8Zdާ6íe_tPw-l MT2?1BZZ]oXtIE[Jүz-Dq$@n3Sg{;#t.|i4VVRJH©3Z:HKH(bYkz)p2UWD !idKK15:GD)+r-d:/y> 6˞\9G #S*@ 2|3iHC8m<3Ύ 1I e$:OuC[ٽë;,rn b)1$DNβP(醊XW&SMpR?Epy.4z3`~gD!\ѷV3q3_s*@ oi{xܻϟk^]O(Dwu,T[WIɀɳgae?g \o>mguWk77f%<߁* 3D_zр(dC+|ypjs/ 9Nj=R7dž↓~OqV< Eb `."s|ezY4{ D_T X2p!]?&F{zp7D+>HTz?|R;N:j` &<&̐D:,3D,%<:~vo@YF "1{[P,s`]$0Jb79H4 rT!d׵( q )G UyY,ͬ\D縹Ze~Ur4м \{y<;\S5>4]gp?rz~9U4)b::"DCc!ȸCȈs\9:S@HCw,XRR?~ݦMsNDh=i5ë۝wYĴڐbf,w" w]N>[ɮ?h90{q} ( ,uX데7e{$"zO|K& IDATAB{p–+qgO1mkW箹vZGt_|g3yh-ASC"Klnt2h%jx2\8#dI'$5~66:͵!p͵|e{s6`)x \ `wI[i=bD6T7Af]}_h~YSiy0}۟- emebQeAo6`̱]%Gƈrۋݚv0_YңMJbA9)FOi:`Dy&Iv,\㽷t_b~ (۽8<*WA^gzf7c/Rä's^+%~}j{~w_ s.9J[*iD"D~dY9 *e*!NS/P Qm(oh4[{W*,#6+$B~rdڸ;J̯y]m [I)P,]v}ySkI/fO;$m6NVkWǨmigX%EQ7V$DBRH! H쪅Z H(+s=/TLHDLRBq %ɿi3oE֌ͻ3/DLupP#kWVj4:[+,igBg"o0Mg"o*DY&r TI i⧤RNbv8mɳc Op aR'GԞBsLe?g@k:T<۷kPρu>W 4 @_F:Z_R t (2ݢ|ˀ2j2ĊTRp ? ͱOGԜZ)%ʸ&SRy<'fh Ek76+W?;Kaa>`0<|Tp/OAw,U6lj]|gs(-hLkb]#6x}?xmASP;LMA;M+L$6Y Vm $0g{'D%ke  ,ԧ$!8"iZ D@Ią\(*dS$"F{=-0y99|,+߉aڷ8tSu:*G^+t3#Ic@s5(EQbJ"jg2"e)AQr!2R_9EQ"F0 P%"#@1-* twUs:ͻQ !w Qar`pͫwLm /@%"xVlUϪͰ%*C%;@Ĵ1(ذ̯]wjƹ2?((M;-b^Ey OQsZ$G/?'E6s&(lFț MN)My'| ڝJd)SHL-pRZS FOխwk'X;_g͍#T+O]ng>'P/6JѶ.j}2]=Ӎ 冗IMkj?elr.2XMb;F;& :vjtDb~Ji*hͱP 0M1M3D@9ED͟ofZ{;MK_iCJdwK',p +eR'&ַk.O3<|:A+E/ߋp(FR* \H͏P) I/42^D\J I#L.(w0@,Tlm75đ̵ W9p9a[2/)jTtm ',쫔z@/zD'HZߛЎ?caʤ.Ȯˌwz0*+W@6psqJ[Gك989HDlJt$fWb*@b8NO:+rtzJIZp jU7ܚJŔ(s|m &1G_kpy@u&xNQehl/JrmLס}l73 UѰ }d?4Xb/.WHͷA0k VU,/tRUstD)!U\_P5 !MMG07-`rzwoƏ;^gfHe?dZ?D{͗_r=ĂoX:D~>"Yf_|èލ!o>[cOUzyI}oH1׳ $D$I;|otD$:`f,^/jvr=AڷY(3mzc#rM0/!#u> $ sJ.5#FZ?ڵ;)'DKB] $Yq"jl Uelu})߲:)e_"Q2`~ep_mqxpTLG]Ogsr9Nj#kH@?MKu\cks5i1bB ?Hl@צR8|`gG:Y`7π3] v98kuPݢ*ӷa -2.J~X\*r i؀.:]vqm`ctR.5~XRFf9׀DEq7Z7:z2`ǣmp|k(,_*?Y.Uz ' F#mj_hT:~J|W'#/'Ŀ ì!(bsmrV0HcPDaHBC Ps+,Z@F,[ ¬C$s$ Lد{q!"F4"X_QQqܰ}w^yЬr5g9r8V06-D+ q ̀i#i;@a'!"T}s,bl"\bq! R'¨3 p Q sb Ȟ$b)e5*V%4ZGfboo;&3Cdf&ᙀ4WF_VL^4LDjQV&Q&3|p\Y ƹ| h3'qD23v Z>Ywn v`r")F֯ &Xgg7SUR>wiǧ4D֕~LQ/t,W-b:Q%mL'SRp 6 Iukcuiskk9Q WR睝EЊ M ehs?CX ܾ`[3B_ P^@ XPomr.{`XF |^V`tR?? (M23lA~F9g뛽7kuY49h#}tА?=߁B/aMh6 x dInB kG2QԦڣH,^ls3Es6vs-Is v-%GQ(;@L*_.jC̝w47[<ퟁiG·+/hQ,%GQ˜E%& a\LE9*" f(JQg r ]! TD(*rBB*-2$M5ł%2"jN*DALqHbd7Vwp\iv+v @W ClLr$XjlK'6rѭ4{ui|aA~ea.w'&*ͯY+|<h5OlF:c?1ʖ`{lYgP xqre^7nn'elF7z{\fYT0Tq lI0@X.c 3A` MKkO]nllsk;‹ , <+j?ԥ3;`' ,KW %t6o@.$K24w,=Xn. Dѱ5Ԝ֧e:Wɼ PYJ)ER@r[n\:XC:A{=u \ev|ϱ}R)C Gx0Cjͮ瘃1{6 3k$ /<{)dJAkCI0tl~uݽdI/KE, &Tٵڔ uD8+ЫR fGv7b@1Â]""}lI0Lg\5YpYʪw^wzn5٦%4gԅTg3&wa89=W=굏3|qi3I|o3>f|O_GoR봓ފAQf?GhRL]J*QIIDr0M0eipW[smPcT1ZS]PxHr;<ϟ(BUI/@/VeӋcC2kN"3G[ٹKF o`v*0zj|*hkKIi)RK/5E9mSJfsz֏oZjg0٤QӄVs{ASKYGR<α"ӖHtiD2_ӏXBޞkMn "]Hnb2>$pj]}"klvPL6$L$@0],Z grH3 WoǙ~^bC1\::ỉp|:Bb G3Hx;+k||$1k&E3Qi8qO&JH?xm=EEv)tdlJ$q,9H*g&- 5?VܘW ۀJM5XGd;!ˀ:Sl@.qX.Lh9.o.`GF̱'YPRՠNﴺiUd{;<:@Գi4iXafx3AnC3y.AD4+qT_=6rĂ1VZ>lx3 % (d(d a^.XɋRq!JVf*Jɞ5@RJ4ʂ6 Rʡ}90-[3lU"!.XQ5y29v ^;M(`O9`l^x[wM6OşQ>?ޝ pƌPSD {~MOƔ$H2cs-gMᮨk a~j*Z"m?΀ 5YE|"n;/[ P6^&. h&>< ʡr;5E¾(X09Z0L:vYcaA/]Fy,(:Sjiyn#dGRQG׷~v{k?u*Ͳa*BzT:ͯiQ<~rM Ҵ+5;S 1<>.W _~僚<y}< "OXwR0HX(9 >*2 ^* %`,R\h[H#q;J HH*GQROr=_ ݚU"fN"k:J-j.촣H*89ϒ$-Ne">0N|TCz@e6wRQFږ}y) d)K<5uUJY]E"bOJ:uX9 e&`Q% ?Dm~,$"(M6Y3 ~X2lnC.4X!Kq^~>7.t*icO8%T^g 9x Bk?ߟ}1iȼhr~ΐI,| iMt,9-̓lh3wM2Zd;:r;X, P@}|A_ο'Kp8a)4x~C6).0AÀ}UPxh25/d +>¢^xzOTuf_Vh{ :epT~d$&l'`_ 0sң'%cyZ↓~Ĺv7܌ozGڭxf٤ruҔ}س:GՂ@vYO+ȿajCA>a1Ó?'r驃vlb3"ƗH[U 500Ml%1oro<!bn=$yط"2՗9=3g.P,ڲ@[` ,%%%z/mM@.0 < cq.3gnj+v̙sftΌ_|k} *>fE4NTK^9$IYbK,;22Vhb=Ee]r.~)FJI)qh`ԄΕv 0=Y5_>Z`?ILsiu yOނgwwae4uAC 3s57͜0HkpANwJ5  SQY aJE,CK ښ&`f !n!YHZ"mHYJԷDh*F*"JR 5TA5ZY+KDV"RV+"V *dQ)d@HRL RDUd5R%"hiHiE+ 24)bJγw߹}wܒ/9o@l!|t?>VSm a6e`hd0K3,-̀` %rP֖1/3`1=9geÅ dh32r͓]~A(ɥKpX_ϛ<<z¼}Q(ۏz*k Y'C %m_=^ž:@c_Ձ:9gOm%[`YD/ _I\s/]Nձ&UWF4D_w F5z{}mWw`xm?,)^ 8Ax/yql?_8?~w>.w:]?%2JiW]/o| H ,3n!g"DĖD@蟽B,6B3['K, @v (km ee6YZsfUJ-3+ ULeifmNЖ*bچ5Zf7 Ph X0ˬ 3chZch.Gae0+͌cjpTX` ڲe`k7,0 Ee(f)B%G3PR>P Ə%1s (85 sdT$Ҙ~M^,\pO|˭_< M}'nŨT6_}CKuXCrQ9 Թ RPY:1[e]RrD a0/e0.'6Ak!V&'kd48A=(Uaפ?1p8)b^:{ H>\^@ώfPd5qq'vPpg>^83͠^W;6S<0ܰcȝ?=?ƢmQNo;(3S>4w^Ntn@bA(jF஍rbȨ[=i.!5{w1g@aZ%9bp`}-y6dh'Ba'PpL~S>E.6J@|Qmjidn9a\Neؘ=rO|b\Fmx"*;vsS(*xAe=Nܱkw^\HY73<=Ne?3HSJ@br퇠xǕsow0w5~<RUt5`n`ήq3|Z2/yn%3bڼgߋGLs]H,D˦`X+>T Iz%d4llui-D׀t_ZJ#ҋn>sQ mݸ&@Y- l´rB%^;0l&hb>)uk0ÚqW돧r6 b`go~j5_Pz3SX>UÝ] %YRM9I{Jaue[d ((4hr(d70I'k@jh Su,d X "ЊAZ+ &anrj6. u_L}ۻY[fvIa|:D0["R x!cux<<VIrLeBy~&u3Nذۗ³;01r;qx4btf>WW)wK r ߻nL;HK)kbc9Z/^K>8S1h ( E;-Ռvr:0(H Ϋ ,6ϰ) [<iPXr;O.<*(K 3Rg| /=2dA`a#vg^?Ltgxvc O!Ӝ?s>wIo:_3SZOL4JifXs#GkE+ljDF\9d cx{mRwcݰy(XV9yjk6A.@4B$F -PzĨZ$-yh Pٛ2&jkvM:|[;;˫o`Zl̜/gy)%yH lʀ&dܕ)4[C \ d3 %^ftH33pb30 II T9!mT{v [tY0i&^͏ݝ DP`7- 1T ۞+]vylk3iIh_7\_?pyx4c,DFr8gjacmnQ񾲋Dr?y9V.ϝgݭS>#@䕵q˶Dw ۏe}Q0h0b^ n (#ti&3usHM5y0XgٳQ(2p&3l،u^x^!M a ޻7<}Qp|z#NAޗtNT0}dve:0?\V]9_|z8/gKYe ɵJt2 B_Xg+-F*c+6:{vy2f>c ZkP0;ڻoQdx"]=xRߦNQ4:q=~[ΝrO sl^鋹K-~ӹP6kZyJZ߭OUZthl|1X.zO/>y~xh-q%6+b0eԂ8hbk:rU $8gXRS{qsh5\[ pT62% K-#b噋mڰ13=v>pc\t]} @ּ:;$oGI洋L1z-*Rڲ]PХeK/QcR5ʍKސ(W(5٥RA:KP/cL vdL Js;sXHn:Nh5 -tpYI@GD{M+iYfIcZ9Yܵ @<֊+)Z'/-Ldp X6ܷaOA=͵ӗ9/T3=Nc~d !ƍj6@1Ѥ=Eج>fua1+r/l~lsTuSc,$-Ӝ%-!&]B:_suA°@Yga1c!㱋M:=)feE]#ˉ|C(ZTmv9vY7M_i* ,ݹޛW޸7;[k0D"gcyy&Ǐ8~Pnc}kEUg0&j}7Z'?>UcY:l}>'ǗwADZ3R V w("~?:3GϒD(ta;h93RM)7+*ffCmYFqڦHua5æ@db\ &K\/I9 RuG1[v,xJ;;^Q@ *5njY- +7M/bE4p|>Z5Y+Pkېv)jxx?GoR%B j (Lj?FQn4S]R , xWIPְl H>lE^J)v"_FϏ=7z$vcVaM'f8maiuP ^NvP.\1)Kye?ǾVP O %޺>یd`Ngw%:c5Wa?Som}\Vx>/GYv-kAONbqΒԗ,F|y1Smwm3PUR:g[VJO%lvBw*-S~ N i]x)xb*o9ޣ8I.s0F:)~ k0Ghe`6rþ8"L4R8;k^Y`>~Ӌc5|E7r:ٸ`k;2ayUӧh?e6`vjpR־R9wھ{ܪrR*y㏦AL(QXz+X.CT[6>oK6ZΗ(f4)Z@֎)Qr2;WmXRɿ)E;׫ocd~iŐ kY rVk,Y"jk}+}\7\ l;]ggxr׈J[z@MJ ZcN,-1JP~V9-Ns2@ Xa%CHr/9` S'l&$lMHp ,q, `><キ'd%4 &8m ~AN-.q[br0|ZR+4\*~dL`i%K[^bgd7?]e¤q΅9ԄVMrce3Gܵ ʒtF f}ΠYܵF<[ll(0o1pnW<-m 9m8X9ç:@;?85ܫo~%@g]@Xrr@Y,W e$`^|+i_,9\F}JeɌJ*$ 5P6DxSjf.M+~PZl{nۛxRG_3s~^4mit-񂐌=ۭ0s?N̰roxd%d쫤fa^j[Olk~:g4-$Ι׊P)].&hE<:c8;P>=S [<PxOemKJa<8:Öo/@P] e}#,vu:2Pܶ+A2Ў~)`=\*׃`YRHu|VsFjbng׬VMPZ- Хft{o;pu>*Z:p ʼx7N X :8xp2~x΄"e0SR5? p?# UQݰ1@VȮ.壣ꥏUV!2H . k:cbf%<݇*9ū4Y1BE;b-pQg6= OLL\q;9 Z%0  f~lge7aX;g+Asc֮!iR9ܖ%mą[AM8P?|$5I.Ji%[ g gp9M\HD%ۥ{&&pmA 0Tw ܡyLY[)mMK΍Zn $Ǚrd!¡dF~ɩKvWMq'!4.s't$++Hv;1ӭ_9޺7rNK):q&J c?Jz\@`l9oG@`e;'g'l1 KIw:?}p{?N ]&ڞuV(AcGҖPY ێu^\p,@T<_(L΀;{ѕyJs<_qhG9 D= tA4Jmy:"X IDATs5KӰk?~)Ɠ:=U y(v 6NO,X`'Tݎ[4Rw*b\)Mkk2el.Q 5iF$r h_iڜ6U5VeBҿ~Ves\r1ҭ\KUb%8Pcp>z#?BS:̀ȷOy/oWZ&iGi<Q|/'rDU͚M?!&TT.U;[H2╭e@rc5[dHs X̀bX@a+t(Z>1Lmg-آ"_#l;_M Bo Ԫn}LOކMޥ-|CAx<,wj[*;.`ݟ{zr3ҕrv_s:ꜭ7y]y|3f;턇uyY9u,qlޛ{?}6~=.R9s9<(\k(kaNHOg<pUsyUS“!*n9О}NW]~TjC}127tjn0?|ޗ4v]sm/:@Q¾1XFvg焝-1r7_4ra>^'r_%s"9p 9?NLWp* ܴ)NLǭs;H2%MݷM𷴾_' ]wzd) m(R ~Mƀ"j+Ӝ2`新& OfI[zWm*/u&X3PC9Rs+'8t(^O414`.vB4mij8?m2chx_Vk'74ɳkAv/'~IfY[\誓 2 (C˕1_9J eEdD^D<ʌtQZ c+ ,֊eYc.D8Mc=bveXXGwI,yIb bN2˪(jp70n`![h;A[Xl -OG z p&3z˧~,=6#_-mi&㫗SBAs*> h4R?!>B1`!|bFg:%s9F8 فrO R<۶ǹ2y xԶ^)Gн{)kBt'Ux8%?ϷݥW_]_?L1Vt|sz @X |]O`yt@L!%r._,WFMM40"мVDc3^=ɸXG89b^dgB>U~!$s3\roL|te ۶7mW9|#{{|;U>m%RR2Ү57S o3ˑ v! igRL&8K cA*qҶVmY sЪ`ۇ=>sLaPB@m--UCΙKdizzU33^l,-wgC|3gO.V\,7QA{UӅ#ec@Z_1CzvQw^/`T AzP2jS.ZȒZ0@;k+EljAQ2 0*qAc`A&ڥ@RڭZְT:VQD6iZXI]EL&ÂH+j7)ş|r8]pO$ǭEezA,n%:Yg Hm$~8p | p~t: L$Y&])쭇>߷s=wNxs|vO_ck}'klw_v'77ϪmzXOQWNof>UHkYg(kjɥ WT:PX<xmZodu9g-|O{@Է>[daS^ çUj3~ o] ɺotje.jL ([!I娈أó" T4\e8vekw}c2[@ XwAQ')[ܨ/M18?|g6I`:FR@=b)0޷҅gW5sv?\Ӻ;GY1'/IDDG/f<``MoLb1\P vbe b\2r7ҢixrMSr`YybzP,J9S4R#FU TS (~aEŐaE@,vĖnTs9~֩U9no}CA*nb!.WNN-]>%0}ip`bKCu)hdxW4TJ|i m=Xfے S"ZU`c bdd(|"4R- #ۆ(sfRQ-ƤKpO"lDKqjݵ(ɸ&nk n3l95ϗ J`ݱ|@VtP ۤ1<ٴ%nmO ]8gf~hOʭA߭@mZİq~>ŠHfcTFOG:-X,!5Xg׹,(]zXj0f=0tBa.EcX`ycuJdw+Sy}6f.q!&KDޙORHx0_>h "v)Lp(~n Dm+* f(qkeLa׺/ KJؒrf8ѵVwюn۶ N5rq$RgtYrTS2I%GsQr@j McI\Q9EZԷ 0p 8e5Dh~WՀifк0\ N+C9&+Y&tEIoB#+a"VِX.1H)hD~B A|<3IM`,B[-[XoJ1ܥE  eZulgYcrdUO02*l÷_~{-e47 QM;iUci@r.i7gˌ¹5Ve1~?8b|ڧkʑ0}Kkj  uh'x>lF+k,y,&izldRT{\ك`w0ٻSes  'v[[ SMuY]<裬k覱l~]3--^u$7j5Y){MA2 x4C7ʷ/}4VY(@pHQ*5HaԀҚٳ@2-إ2hBg9vUxF+ Dka*uɵzE*u{,Y뒵RuBpJv*U24 rv?ѹ?@pWއ _N]gO^̀wH%Q,VS@Qh4t m*ZRJhJDn7DvA:Dv+ni>hlk\ryoig z9F+3h#:hO[ h9aŘ gߖ붐,d| RVT7v6P~ڧ=)0J\aN(i(?<|;/8,vWV>g>_zQ ݮZg0}E|N j]lVC7֪_sox?S~Xcv ۵ŀBtٮOhnz*9xΫ+s92`e/\Dm>h O<K\J@R6:hn}]۪u ;7WP^ $Q@9a`3B䫅`ʓ 91rsî6i} \uOV` ].**@dEe1Jow/ݥZ_^uQkƋ]#﷣8g߳_ooݵG_J 30ǧS<=ybv,[9Lu.DXe0Z]K?p$L%ه2#Zf83&;M,z@9g1se&̫z,t@@ fz$Tb7 m~Wy]m ohtb-v׮k^ї^(A:`{N^A]W{0Җ\\TD4*X0!+lѐg4УaR2@c1 JUh'[&gm(iFfKK;A:=0# |rGPԼEPvB^H)7*~S*bLLa]HNmFL@\o1C|l *I9{1'z JO?p=@~2_VMqsU 'w]Gy}9ks@.oMV=VͰ loΫ0:r@oܻέ?<sWm8m_뺍. UŞ$gw8ϻ{,qGEPA4+":+I{$l.X ${rq:$FYdomV9=*zlfSIZ)rL!uQq˥sŞk@O(UWl!t1&X)(;yoo>7'Xbgu? ]@BYzWƑv`f0NaqpdPb<[@)_Uץxޙoײ8? ^)l|}_燗͎k,`- ҉ 0 ]/o'"& a^%Y)q[Vaٹel*tA[wXpRD"Ka^<$^M[߬pNF7X{[Lۯvg&?:WZ|^g] id-6T:mh;`Aʁim)èt_zRU]]\ t N [[xw@ݹv sYlt~+ثb {1Pvj_.Pί \=;%(`eG_#@T `9I!Չ[ʆ1)%v#TTDf>4`g;\ss*g2I[S<cy;?~)\X7a>>yxu6~S8%>oza?:%[O (9H ]f_D8,b?̩ b8wqAJ%slt+ 3֥WUn0OR̶Qe[a9QE[g&n3}6X'rqz: ,sb3{ xDs9hyکTA;D pSqA1MrLNfǀW؉2JkfXChUAMA˙j ]6d3=CN"eBێ!^0d h,i,_1*~88Q%PXfbEV♁~- w(l3 ق [QY7zBvJnNhtg/~{ۻ[ V.ޒUyk:*l]0.|o>84v62gwp~rpnڳOC7J0+8=q铤Oc[t;o])Je0u~'x}{E;,5\ xscx'NO0LcA)Pv驚be9d/V#IsI<,9u۶{QmB0(0<5n]m<~Jyݷ ?tMўN d] ·+ͳɧM/W6:O 6uxS"X^ h4lOC(uvF67d4c T:\`*5(]׬>fFi]Ek}vnomYSTc]>9O?$ÄnYZzަ%[*1/*єxd0gjۧd~#xgߑ0@~l3xR %K4a/eyra@gbTւ85n IDAT[gEldglUxYd/Fsc@J3 ^(U;h4,4kvLtj+͎q欢17V%WwU>9(g9oKփAWV]2 ̙%1/9o l}gXмO98x6>O l5Ul%oڊy%Ȉ(Á{ ĴSnu[ԍ&ƒ$!€`mSVgL\h[X_mK8bQ||OUeB#,5DlsǛ[wEbu]%,2%>QDbX01vsVN0SS8~O:S/߾??+ȺCR`xkczc[ۥoUs~^0D)^>bwv$5ժ>7?}__~VwG/O( C" .7\xP+wHs6xbgP `6 |t˞}nnO)" WƐ(NikjWz?>.hFP~T{ga~;[?{hWeM~v ^żP^!,q˞QeYѼ x`y\K`XRHMpK▝+viƭ;߽׾`77)k1.{bEθCLz%wu8oIܮL$l9Pc&7iLb=|7qrԮSN=:^*ipgea\>:z8p8QfDZfM &VOokv%.XC^Ϛ0҉5f$q.-P+c@Q+1OF:.K0k'T];x'i%Q+@$[V&.,.YPG3q&J>6 T / NQN cSVyvt4`o%%2%,_vU#a//#GNaYğe/ /)G~h=W6akNp6n:l ݞ'{d k}zb4s,P.&NeC-\0LbgMt29sr}t7:h U$t |z X3Q'.ؗW统>`)0\?r @9gSF֋{r;,Wcõ5I!r DUl^0I)b<_3ۛd4^Y,E3ttwWyf/!{[k?]ւfk꼚XQqEQN- dcJ@JlƉ}y Z&jޔ*1"MC fWײP` ehPSXc!d=M-`AZ+kk]\J.M2A3ȗ!MNB1I&S&ڵN@~vmR .Xg xPP̻֭zFٯ.ۂUeۻ/~\HteYE8[L,d{z$K'QU{vXpg>^ WD/}Wo()wk%q˫z}NFku 3OFv/?|O/͸ y5P@x2ϩv7yPyzNgPH3΀pCq>#>㹨n/X&k? s KU Kp>l ZZ{w>Yr^_lrp6P8"\BX>a/`6* {@ZLI@|. ȘeX.WNrL!%`9[9iCܻ5_m{}mwmvLPUcPUio~o_W_SF_F~wcP%f7EFa+=y8N/vڒ}:/ȍI Upq?z| qڲ$1̠=ڢ㒭 kj 3 (#[DN9*0Dˉ1bg@A3[ˉ;o{Ցe9|䙋gW(g3/A7kn`pм<P_A+^F"^] fx,V$b`X\pA}(ŌC' vťleKr10I 3sOg p_։).{n%f:BFJPcx(8eȦc,{@M ZY"_裏N~{%FN"XYY -RFH>&"Pmw+Znv<]Arfƹr@fIbyUޕt;(FsG~E"]W[&(?Bշvm~?nt֛/f?a::/-0MԻ&v?S7^ǏrP@_30<;U`Ne9 >w]pp[b@@Ăw  8^i`I5d~ %Zr@0N ,snݮ_=PnASkXeX   ,a,'V,g 11DC(jM{kt{o_O~q&s#V?Rel~ZWS/nU^b}~w>/g۲ܹΌtݻI'm7_O%m Dl>SR NKx%mdUJ6x1>(-)N=,ڢrp} @:8 ZJgagf1/䊭b*O݊P@I[BUK- @KNl;U$I3̬ݝ٥NVt<a)E%;7tt8A8");=]YUYU;32tTFD?nf)pnPUe/j|ttw{~|Kf,]SD/ `ak8`s q Vk|fU&y]]dV0fBꓡ;iMX[)lLv %i뽰X#u @WpszgD0i{:#aZl%!/7W?|DHe}y<02$ 7I(xlår[[ˇ ̤LwW7ؤOVLoQ7*x)73lu>S飈Iϓ#~cثjOz8IPFA:bz0AQ$;v?={X|HO?k&7[My*՚ Z jE3ptP>Mx`zfx5H&6ĭ٭@oշ_Ps)!>2|]a,$^5_;zA &ׯX/#RLeI23ld&˭l)l\cPۚS݇'gB =>" ,X7kՎ?n8i:eg ]Q:,]15̉B7a)jatdd>M-t'DNP ~rB^87oV?ynu$$Ak*<lف,S7 "($?4@0HxD.~I`.JP8k.lmWHŶg 4hDB\B8 ;in,å ~֝9r ti7Øg w5d |KAhr*~gW2N|K }urmL(5cF5OzcNEɽZ2+jsm.956RȒj`gԠҸV5%m ȚX6T MP0 q0h(tׯuAt0pd 䕧YL#Aحt;ޙVՠ` 6NʹOrWi^gOfg/=jU1|uÜr޻LdO+.{.y^z%?uI J9ԷWX[NGVΎѤӓx޼|"v[Ӧ 1- @3(<7<Ӣ% Ly} @pz`S @ L#vT P֊&jfu}܏Yc#@Ch Xq!yfF82V  ֒e;qD!jEw $u|ND9lvyp:3 qgMzzk18KDNt nv̅+*O4C̀sҝs$C};λ_ӣл)of//c]dO[\8w?hw&v PR!|JaQCoR %>zGw' QU:97L/uk'A7Hڂ 4P,k* 'e1 )@ Kl;eu-J @`y <D+XJAҳpGS!-Dk9$! =$)O3@"eu}31 g[8Wb%p ɭ,~Nvj"\|ɈҸv8ƒ({3Ǧ>)@+ZԽQ &,&(["LL`YEΈ=6 !Uó;Lk._//=ׁٌT+Ǯ<ʼns";>8f9Gs̗ R_sHG&eÜ!ppzw`ꗴzz#<^#3"ZuКݣuf#T|%GH*j!zs\ݖ:k `= :53,׆5LYi5lq#gQScvn$`;#9[xa $FsǜdKqM$`*E1%iEm*{̯/Z poi6K.aT^C'W0hWl{e GR1:X0s`3o̘RK|(+Q rjݶ)tfj.lojYwڅ񡟡^G^xbFUUhh t펙3z>m\}^rG=_g|wn /߿|S%amd"XTWAb qy{|(ͱ5|fd)h H@~s}7vke+`/r~fv}IvZC. حIMg@Yh0N|G״ފ|U\+NVXLMٰ_k" d؁0NN}l~p<)GM0M-x7j1Cxx4>0V~ﻼkciގWrMO5󝞯oozd.DqfJt~O9FB́%HvϮwxג-t|`][|E)|9zA]oQbWrr #[8>x=.o lx3M닗XmNpvUUYiuv01ݧO~bGϧt;xNb$wezhdp<=NMS㫗K4p1>ÜS_c>oy2/F_:y9,m7-Q޺>^ ,HBxQJAXu,wW_\_ψ D*bro:8n IDAT5(BBFh oNM"o闾h])62ֈW@`cIJsmu҂ҩ[aʎ V`kD4@  #PfifKsQz*Uu(@ c\lL%֮;/DW5"|n;Q'+ !k)eMx/ VpTJ \K!뙴-J@\Bb ~i߱k`ɠ(j;$ DCNV+D9'g Q6";KǏ'Kbw`!D@cHtIr8MO$+\<(d%;ԍŗ?_೧51^*9?Βb0Î3ەoθ09hl竛e(Yq>O`D|'x< .&rXlij*-m%ԄcFBzN ӳFdӷ}rG5ݠr'w iCo7y<| e`{3`*+4|Xvky,e yA~@7q(9P)Ʒx{([k/`0S>l^,<`N%`G_c&t:nNA-z4sTwQD6jf_|y>&L"oTͱKH-ḵ,@B"ZG-\b(\'>gS:'b֔o[xS#̥C )"ҙhl ƻ0YI˨'zYK)E åy xT )f"3g/;&n]&@ŮB/^Zo  wk|5r2@6~ݵֵ.m)vp))ހͱ iθ`4M JR@jLX56V\n)$SM2"@$b}] :0ZXbΑ#UT`gA\\S<t K"CI[?L;YX+x|죏>Wg$o|V|7j $eR@*Jm$LVtI: vsul?T֟Գ]-Vϗ¸* Q5S3l" wvÊN0ǹwP%H.,o Ϋ>}^-Äa;L(P\"2n{=i ϼ 2PF[f3L)}BЛΤߡ^bf:]M$*0]ا 1 l ߻(Ԏ;$JftMmk %(PqUʲZe JV<k0}2CxM\\p@_8ro7Nv8g+c<:uQ9ci!/޴hoޝwސ͚u/bZ2f@Jɑː οzݟ4sDxO~}C.vg_y)~3"a(e^#>;Ed 8ܓsN\|XS86*r-,VmѱHP8rSr`E_7{5s!vn̅4hcE0S@̦n $`JF~,'rl敂ۢZٵT.3箛ɿK%=ms.oqQgb- P#I{N}MQ4:k%뎔J@!|T`Xiz)EC1AɽHBȮyūGQ9ԍq,@āe0V]` ֒%Bk9򛽎΅U #s!I. iB+MSvG? tx@d_}C } SAl5Q+b;10<ԥrG{vZO鏡ױzwgi- >~4*;fMuvsG]$Iى~ C\`F ϘzW_t5wy.7z߼5˗ !vÄal[4aXpo2h%Qfa ~WrPTGֵA\5x%j-y)8C GgeEP2HfsEc96mk,fU[U gEsG065k"ǁw<A#lEGpcD+E;_$]wnqw-7SO6mְ6~۲ le'[,TS jL%YRjr]w&MTUW~!ƵT:m[0bNlL,jXr[nZD,u޴ p~Qex΃ MH5_ |ж BkGEGh>y[ &HȔZ9LWU7 u?0ۧ@w=Ńxnx2;пO:nGg'O1_^b owoW#q049ˮ~%wIK;w:VeEVOF~|竿x\oדoݮ@e1Z}E#]:`}Ye}>HLp.t1G5k\P6#za :5}ܑP, d.PW5}? -Mj@Ի]> fp9瞠da_2ʴHµc7t FBEa wj'Zez0(cք1PDnH5*Ʈ|tprv|zgTDxK|4%+?n`k\ʴq!u7$/2NS]?ӓOOqxpzW7>RP:sˆu{zj o31\+e+a ț_#__L b,'+M^?E@kzXDZP[eXĶp0c8QSj8 #]X) wos瘃##8&XйE ueA3ͦ8u0u^lJ?TݣژJb9XS3ip?K7TyHL='YҮݜl\V4]Hh 3h 4S]iSa.mPQQ. LnИI*z9P(c򫶚f@7Ko uxVZ0 tF[o yӾٵx0@VZ;KB$֞KIŒ5eoYLd 5_}u@G)hq厳4Gji+BeE7+=Ͱ.Kle[mkzom+5nV~tԕ",k?X†z!A=~.t+95n~<^*wE-9jk2E%mQkF 0k@ kD#l(A sI?s j!,PU o?WFfL`4:L@Ԝ;%KL7c2vT<>@ }@v97 v*飣_â~O(\9}}oy0dB!S/qK'|LʓOg>@sx[G`l7k8'f%dkZG04N_~oa O[2g̬8Yop9A_5ddՅ UX -'7W_.3" @1("߰PfȷuLDM[yV.45dMI[gΪ4<,+\=@wFMnوhVICP5HEy.5$+FGnQ9`@`K\şy0O&m2|x=y>T}WQmsYUR3ڵR*ʃ扴wm$m>7ƘKq1{`k],g3n|h2v1CF0#!6P1[@=Ek.K;7ւ0pJt[W3 OEq>&894Mܧ9YP31G܎SlDKY~K1;PkM.-Z2T>"&OH@@H8$%̥׻@ 8Gꦁ  "ˆ@Q}<ʼn|={7G+{0(/KG>DQ?8UAO7.fxX,޽je#EF[L&+,db>|h:Xl wID C͹$܈n=a.@ j1Io@ggq.vξy5Qnr pzGYM5-2*QNր`Z1P"4cXWnXt:}+qp<G8?۾O60 ?y>q$Go>d~D8a<>渾iBO0>/wȐow0fX(Q?nJld*4'R}jQ yj/n)~ؤV̾\r[bU/tqM 90Q`HDbv6` 8c4ͤΎ ԡ|ۂY$Zsx `Sa+¥5)S(WQ)pJѓܟ(l6,X^>ͳw2wlo7-F3 _ۙ6XͩRrQ滈 4jmoan^5Rhm!Ɓ,ؔ4m%#F@2xޱl wD_H (DM 3r["J ٺA>_pNt>}+)vz&agi)JSLO_ >Ԃ@w;IF b8qSқv9p'W׿\ǖj'wCCOp\l|೧S*ߧqck \n:<>郈$;_4uA%ϧ_Et> g8nkz EU7}˗Wںb}@f><kB¼  V@4o8DL=p 5r}ѨkCVzR [z21Uk5"Ak` ?9ʘNQ+J`!L)zZe)^VаQȃ%W>MF:NG'>_iG} xqxe紬?dp~;U#NbT;Müt QSd3 s̗GEFݨa^1P;齑!j{^cJiQk&"u|&xy-˯!!}@ څnNT.y uQ/Z a]14E'T\I8kA5`Xu'Q-' CLh/;QEQTg6"h˅px#5%1S0v*bU,'eNdLs)^D>/Ί  qPFMV5~׍Ԭgp.DY,@LF`Bo$Km @9f82Fm"k$†}ewMDL2:S,i1Q_=~|~Yxe޻ c9'h$ޯ";drq՟Wޛw5?Ѵg/)0U.*rGOnR €m-oNYzz?^~PE>u+;F.H^.sx<{*GJt'dO/Gk˛wo/7Z7AćZfGmF[8)ˑύ3@I4:W @Y9)PVH : &:veޣr P d>ԉӃ_ܦ%>)q*} zi%{A2[$^ ДKjj)#+4R[@*#rQ ͂]Ge]v>9NpT;}2Ec>ߝ`l,ڗ[z:&tUe9?4^mG?`G3tһN[/#x9w-CvkJͬXلVR4ˤ_) āIH^^~aCL_{0Kh&98-@%µ-N ##bm1EjݫՂRٴ]Hg\[`L!_MӨB)k~oBPP[S^ϼKyȟ |AD[d4jd)]8 bX @E1T&l3-y_`usk;8h̶಄_@Yv~mĂp,Y`p/-)c7f`-4@&5cJ'5B f8QM47c>.3@I9쐒jպ$N ofY GG?K"Ǐ&Y[Fg2I=dжËlNez?)ҫBzwm+['Ok<8<6e ^1'5ɗj2ʇ_ϭ3X_dEl?~= ļV(>A4ϧGy/6P=:)Ύ~Ot}S.VǧtUyۓk,W`ͷ5|KLYoЃ (L yu sCt}n wi hw{뗽œ>\sd@ HͭS|MN\!^FU_54Lhz4L+"nI6&#L'G4x IDATA7MxEiFUiÎ%( H^:kzqow|OǷ򇇧ע{ƃGۗx۠mpa"36%;RǷ!ɬ@7Еm]MBP<*glfϿ"dlAB0 ZA}j,?(Ϫ鍬RE q)fbqҁ`VY{O,Z莨~"N׿Z -:W}D(fQS:!ˮo& p)X9=1U$\n lk*swqXFfgvPS&VDhD[MJ4M _#ARYaA L1օGYN/Qs5u.?+$hnD=W#'RGo5_Qfl?Q`]!5& ؇bD\UR ށ 4s#%% s(XpZ(O= ˟=??>,OǓ$ X8]08yY0Z+&sJBLKp k\?u 3PIzjbF=+׵Pm+xGnbjw/ѩkz#B!0$V:Az>zHB!DorWB /]_ԩ\WyTqz]QU>zp|r>{߼.͛7VA@#3&Y+@t#TUAJA@  t@@)rB4dͿU;eXG; Uq~ Hi@o#O2;OV T z@ Gk^@fzm̯1+6P.ښ ã|渜~~v"s8}xp4gk,| \F>>QTw:;[?OgO_N%"`6˷ lq|05gƗtW> C^0e鄙Q=1tc+\0uNkԥL pU_SRjH[tp]V'b!O|;b5Vب&aq@"̪r `N?ba6-X0afiZ*6aCIZ00bגp5 P53+0Dqbc*qXeꑀ&7kmP}}m o hO2d})iA[k$unkP/HЬ;LnKqEҧLY (-.8}vt>ˁ¢~` vȈ 1-:abQ`ͅϔqFl#`8dCO9[8"Ș‰813׎sӀ? r:9?@)bĪP'P' # u;%:M&yzܽu98NPdD,Y%Ր0wƹW ~ru'q~*xzy9W2@C,';_!Os/NA(O;._^9F;<t2'GUgO?{j/W77m(lo h@epDb>;@6z4L# h-sۑdc`a@F" d$]׬dgh5it3oz=5򆐘_1l,уã9;88x'8;-˓Scly0Y\5˂wJfӠ/2/⑶i ǽPz.OMOqtp^>R\7^]mZT)n#:XR|9zC#n>א^|µs:ΡaߋIЫ > g;>K4}m\sNXؙAŢwB,qQ ?KpEį^#fတq+wjq-*A+lõk1 2c43s!6@5@ؠJ-s*4{[:rQr)afw06/h>?wHUb4jd[<>*ԲH RQ?vu~\[_3x[)b^sd@)#ɗօc/QCS_h hA P uTo5Ŷ&B‰Շ3n>`-9pB@N эQV3+9Z5{ke}v"k8 /WW&k,Rj$$ ̫4r7X 0(IBRolz?Э3kGr1]:<׋Y؈vyzw{{ݞgE߳eӶWN nxs8^aYw.O,˷u;{wQ_-VuC!3@@ A3 J0ic*s4r|7ݓPߒlZ#7RPy_NAp_d4}@r a΀rȀ 6Qa˿lHُ {TD$x;>@ }Ǒ9}[]aซ;_7M6//x8ܘCJΓ]?OI槜Hu'Cyz}JWM$_Z!b;|]pAS,5~"֩$^t%+vP;%Co鐊jE ꡧ1$Mĉ80"zX)d Ǭ`UX'0l 8Cי-6D;o'F]nY_t`nu ?_aSmoL)@eJAmָFf͚<״vM i(| YYͯ5%;{FCR{dbr#vPq<FZ,PźRuP-$Mukgy{>RP@^+-q9`4\_?'< `#Hv?g3xU"ҿ.[$ϐ~INӅdMe[溜W75=%G@]ۻ'1])S'LEiݎX5xY}@z%oyh}eQãiyBjyW7j燳wbu:0ʾj4c@XR9h}_b'[h ;4zNakt^焏9NQz >0t[x|)M}L \{kLfdRr\^hIU>zx?(F=84[HָL-9!pN/ٟ߯Mg&@SΘ&q"DsS`ZK'=ݩuU\\>pW}f${8!K?]瞏]8FEZ+ >>@5acZc>f8zּIBx ]$I>GIzzCnVxs|SLFTU>U9z|Z~-V_zvͷN bx}HQdȌѓi- }m4i`7NeT1o^:s엪KsaxaHS c` En-˲or݁Aʳ^ OóNV::OJSEfc/g^\C o}v0zێr[|\^xe9/ְ6:rcxSm({""B"`? 0ADeqNard&+ 6FցL%+XZ0'8#b#¦@jQ0'\L l,`IܖF9ͧoIjYӿ~?ϯh1) mk[i( :rk-.Hbe9  QRMRқxMem]Qu`9>g(Ulv;2Uaur4CdrG(yp$0U&gd^zńʏ+PU1I[GGGdžlq0>{y7u*|T:=`!zv/w=ߴPI_rW//8qt rjM(s>)I(ᚈoL:H(r$ݏߡxs?}K7ms.trbmGӘH#yx6-tbag>q=Meb2]Ƕ=IBH* jkFѷ|;FX*LĪB[gzl X1K%8fq 2֙% qXx̦kFFk'`S8jt`%qk64u$`껬 ?d梘|[? XU bEng>5WǠ}D;y"rC+S]I"~Q{\ԄD7r@ƓpKB,1Kf;#-ma!kGFj X%1ĩɶJ2 o"n?&: @ΊNmof% `y2 bHgPu` ~͡."։1 Bz~ǟp£O&8~ ~z5&k#(I~v}A9dHnvNE%Qkۧ^v\Oih#_Ϲw{<_(/QQݏ(iMO0pԭ=_݌GgIqGM;ߟ^̮i7|vt`=nLܦedb ˠ.d#Ѥ̺#K7B5@@YM`E5T4X2`{m-. >A:j,)"^`侩50ȻL d@Ne 2$8ߩ~XNL?R0{hw3e/fqr[%IGs2*z=] \ A"B=Im骮\#`~8q2zv{Pq<7777;4T'%MG:t4J'1'Gw%~֡SIc_ʟ̕R*]t)^8߿v[ʏx)xG 坱x+a*ޛe \D_"(r6 ^o0jf؆q}#QѰ+1de@Bj2dʨ!Uc.h\e` pi;$0`إ~bm*J(` VfOS4v;H_KTh>; 4nW5C1춚ho3X;+C(_#$ DT !XWG"XWX3#Ƴo~ϫơ4$ m OT?hAF`*+6Pc.K:WtL:_}W̵T9xl9<kq|穤Gؐzv #ɹ PXxOM =ǶSCǧݟ~wbWo=Woכ[z2(i'LŐif2D͹42.'Fzq vcaE;de皕f롙sMFx Ð$Ga,p?@[00$~G50 zكE۞3sŧoۦy9>2?Ym3>:x7*}- pq4o4헟hozPq}ѱӑh\i8o^ıDƞc m%q!`,{P >͵Y L7}~_` $TNYIԒݯ>zB`k9(@ ;ZH(FjI Qmڝ **ʵ 4|*O*sf\a >2Ⰲu- ZbF8Ѿ>omJV*Gfy%L1 "` CHʟ]n7+UM3Wا8e:ۉE72}<% IDAT I-9d >N"ȼ6)%IbR| X`c8ǹN'8=4~ _agJ-܄}z}+X0yFʂw>UO3erGLQ꣜vq :~@$"vs(I:Ŷ`@k8gMK=_= zO~j'o׻u߼Ze$&鷒3@EDhCm4rh@5Xk0I bZDBj SV@Q '+aٌo` @65ȀJV*Z &ld偐 ٨Mv㣣ɽ-ѱON?i[,1=pq}BP\za߉L69?}>S:b*TQrc @ >>TȨ}Sz5 IMa]чNұ*Oͧ|n<ީ# ڹy#B;JK oĺD7@PzճgOudgr4쏈J"d:1 `Apq/@s XA !14 zAī˟T!; |p"gl$ ƨ !&a+[aEfx̮ =[A0Thfn$AU{%fAhcZ0{0m/.d?>s޼Dÿi 0?-2g 6R۞H^]] )k #zcZ#` 6sG؁\$ qAUԎ؋8~v0g+;20`U@ t0"D =Hgzރ G=!M/2sl`BF-$n\)kV9D6|[/-j+NTmUD@BfHV`SW f JFɱ -ɉ$A=VR+m־V0\>uZ[j/K {StKL{(_YCu>aoX$t;̽v>c)~ݡ8)q>U^}:>!x/{KX:xlN6V=_͹?|77rumtVvIuVbD#tgm4i;;D\7MؘzzO[=KC!k-InKjjnE-=^ ȕ50IF$*({$`%xgf{O?:>{''L¾}@]7LwBaKa^8n*_L_!Sc53<|X`nktGbSIZzϸȨ#F:'Uȿ]Ѱ*|HlNMg~y@,hv-ݳx̢>ׯ7zIdO|M2`8 *)FX9x!Ϣ/F6Hw^AX=T -`q"K'w%PTP\m?Oӆ&Fw0pP_ZI~d+WD3ms4'ĿƞJ\" c͑\]=s|nwlabimcyi͵#Ap$11 Cj*a;*pN|R:@\vc}=y!0>aժfCeJm3ǣqrs^o~T2=P~NߔF0Ke8Χ;M/W>;>1b8k[jo_៯65xw꒾tXu-^J *Dh0f@8=\µJuEmZZvZdtx=n;zCc10af d@$ 3c963:ŽKO==,h}ߦ}ޛ tnsrړ>r8|A4Z?拽++EwY\\o}2Ǧ˸,wWNOqEU;9i~mk{ 0W`Ȕ$ jEׄb^o/Q$jЫcogɀ-M 56L 5UI`,\1fBqE+4G/A,@2?5-V"K@t4qqWj°^|T@ ''1W!6`RgQi00k炮ސ] @\t^tQ37͉쇖*Cx?ufBKlO$@ncꮛInXl MM#}ĚNGGb=ي8_z8ذ'c\LF} ,i$FF^MՒZ-'&mx 艨5y+ Ax!2@% Hu"B $@.ǘ͕ȩH*Q5צ$//xqdKqܭz=ꔶ _;'ER?n$qAyɷ\⿪ECJu#%wM ^ zGx|6{9E\ݦQY c쵉wOɉ|??˟Jo 6P|P˳hm "ЃS{v_unWw׫mͻ?}=]Uynu}$h`MOֶR JYhR'PlQ~V6܎;(´nf([QԌwjc ~K?hQs W*  a>]{,G/?h~ɡOií&?Q$UFG|<8(1Ob:S߁ft|s̚ݞE^o|_a% s? HZר<: 8ɇY:_ - }%/\uF -MkRz._s fYzߌ ﲢ Eٗ9A0RXTVWYc#8 A,AD=ނ1cYfXA "՜!,^< ۅ _983`Hf!;6¡W 4cDѕxCZK^KcF}y-lߊ1:hOr ykCOٟ ͇Ԟn _ OnTݻkHWWL#{4ip_6κs/l>xҏR mE nMVsV/#9G!33GDx ',d"kT"61!te5O{A ò jcʁ/ DHn:X8~ҺX明!PD$dM6AQi/ŧ:y)3(DiihI0. zEbͶR8eOkD{ճ( g?tw+lv>dڡ,k4h׹)WAjxv$S>F`wyuW=U2.,F0M{TAOTWON GrarbLlii&O[] o`&eJ4tBlG@ʮU,j֘jvf|[귬إ$ ̢vILm';Vp,@Ԋ3E\ş9e#^D4bE9=<[=ca.tc(8[IMfrםU,DM`}}}*y^O|sKv~G+4,h6;!g+X )=QD}h|P-#uX&4 'ފb*ZNy.udgs(+R6O |G6.Ė9Dr4f@Pi^"f܁ x_4߆8SHTkdJPW E/j$\'dU/&e0 @_}BBO8gR}"D2>[R,mUq:/qϣjO-)k<8ҍdĒPMH, T>jcq _7ka^_q B6~rKʇt2.Ow?Jru9 n ^+Y ph ɩӹN>]X}omt ߟan%;^zt2BMZ7F]s,NB@#z'lkgd8/ǒk\*p?5Ab1GB=a^oxlqb͏mGX_mf1mx*$P4{>=O|7G?&No/xv Rc$Waȋ әkA#=7ڻ@?EP+~os~@,`kkA$3 Dԛ_}R4;uNkap|AZ4!a D%ME+W<3{9G~`E< 5CQ2syR"bAmB'Y=\PwAb#.lg`pI#AGh:9X١e+lDK~9b,NE%i}7`"fjQfrّgV98g''4LˡP8&{IBOCx<8~WXf!~Bjfn#xmL85lfG=ȹFzj@Y03!X m\"81Ա93LA Hw(;+Z=@f>!SxAфzל,HX(F Y Q22ю]++7Տ IVo_< 9DXR.@eN؆V,|N |>q|hs|jA&T)Wbq?yi1g_ѴX=>}8ìíǶ>\@o+FWѝK-wH2?ԿLS't]$K]i>txy?9üUz|5-#?s{vr_Jh__/g/߾Ĵj[]-L++fހxIu5P4SdD[3iTLFcONU! 5S(/lv2IGGGŬy0oNhnX(b<|chhj9[揩6_]X~~tuXC|g/qd |)]Eq+>Mԯ^U>\σv5u$.=-zѧV%Qˠ/viW4AWI㬂t*H_⠅uWf•НeśH$9 w5Oa 4&fA,Y8"H.cOc]s0HNMwu> +tX4<``\#bZn4($%: K`"هCMA s_XB x*D@q%n4[#ciD)v{Om侔a+;{b"ŸЎIzǞhf 0QqjQČ$!6$8j=(>F@Tk ?f `GC%a rBF*$ 锑4+&Q[`DP ߬> 2vy_~~ Rr*XZA Kbj@Bi~x)/kN\TrI hBS~s48A>.;6OwrP?Aoɀʳ3 J`˽ځblvlv (wy>rFѹ?i:{?Bj !`Y3o]X/f =ca㶗mc:Xp1kn=7a[=)cl2])^#-4TT\i=ȓGi\Q-@c~o T lWe!c,ftԣXtAVYMRh~`V d<F}jXUd%{`@MppQf5!ϊԭ(ZQ뀠! ѯ9$ZHk 1 ɯTT_c6kT2 bVJJד*8׽$JI0;>VW䬩}qOwS'#Ȩjbtcyi9.7>}@X襟 Gd~6xs \-9 ,f 1 Ьm>E8eA\m/C>A3mCNr4 bG;߾Ó@f$>lf"3xv_U 0(Pgo$x{r2 's;[[ۦ5߉x6Xo6z3| NĘLw>oD?>ഇT{9kouC㍿w"~˥y~g]*6RO IDATP}t844ig.]_LWKvaHhȚlOͺ^Z?=.RtCi+[",䣰 5f@5Î4F5NzǾ9%2 @X|{:?\4AT"*BSP12:b~mm=G`~Bu /'1r#H+v3PU^,Mw7ktj,iL4X|'igNk: 1TBODm{"]wNc4ͱ6Nls ׯ鐉LfzjHֶⰿXbnx]mNц(jկ{cƦsDJ 6pvvPc"M>JLz6 (#= L Wy *d !B%VTG|4^P!&j|%=G0&5GqP/* Ͽ_#ŗce\V/yʣnT(Jb2伴?q'S聏sKNcVJ0'-<]8J ]z\ǘ`,7s\;캭٪qyP4tߥw) ~nwGjwOUWa|A$1 4NviQB?/MGkZւ Ǐs?Pueȶw^&@W7Z2Iϳy]4+TFy\j·cwE͛7Ϟ_õC8+H`,<3ix5ViR#w6M̗KBNp!;she %sbxj!pHvӺjWj-A(?n@ŗ!hF&ȝc\g=<{o甾syR㺿q~R3w"8V ?{x6$8|l9=n/~KOr>O#˟h|i`}i܁NT\B{ͥi:/DXCZ}h43zm, D٣/ro/ +{'c]vNhUO!߯=Ls<&0̉M3[3ԶͬmLit@ێ DC{_>L{/:Ow>JRwn$i||~ċ=U/qvyʗϜ\Ti)<8Rߚn_>̇ic+da+Zpf/_m>)*/E R(b +>֥+̮D% % *> { 5,3%n_YT.|um2.@\lVl3 gf+]kEGAf+(3A_[ks#q@`gVkv40ATxf߂]nB?Iy Fa.g`se_sMj=k^a+n{9E۞n iECZg虁;X#{1V0S`+ :X =^]³̗Dx"0H_$,`$4:+PY]_y=A(dfX3CTeҶ0Q}$Ss@2)(>En<~<ؔdb'\}.}j m>KO&ʺW]~CQ`cΧAjP ңy:, Ua?i]wr _|vY{m3ǓGߣEty\.+cthQӘרM7?qcB&mThm (p4ZU{sLs/Sp}>E F%_ߟOߩ>F=ί kg:~q.s|7sr|wz_.xF}* _-d>oiε\_=r5" A\P\t4䯩~PS,gf:WϿ]=ͱq/$g$x(Y>5-DP* ћMjuTI)0*mf A5r`2"PͳN&S> 0 ''z OZz$k'`At&3[[Rq׈3cG5{aLХ2K+ !Gh?4{ߟbqgN#ogt)oZ/΍QNeZٸy7'fءNHA6d6t gDw= FB`<Bdيz #r-BO[U"6"F7\ ;m4h?Kd`27o'*Qd{f։Q$#D#喺ItDd" Hۜ!:@Ǐ_wbخAڅ ήe*Y:yr>h򫖆)*Zմ尟xSK8{3wk|0ξDw[\ޡwESrCUi\ÏY!|c'L>㔒#Pߟ/M;n/;@xu}< fmnmy6G!(RHIW0@}h~=Q8MwjoxT˰ݻ|tfYsA˗%^@Xg_P<"!淜O?r8A>qU6ӣ|9͉jDñ c)Mut\8%E`||O2p-f.}|&DꑢP tJH"j6"(R̤.V-_l5YH`Js@߫QFV XO{K#8^&Zp6L6wdi"c;Ùi^l;p@ +0r] lc ϻ٘lxjV?U>;`W@J?ByHn =70T۞Hd~M:%=熀`9^z?4cp1ʪA4xtdL+&;:6hF_SfB$x""=ܜ#$} xh(dk4E,s{11$'ҎnzDGcYA# "B̏@@1$İӕS5OjC$2`-q՚$b*S$%ŐUUϺi+yiwݷgyqb:!x!Еb5iL*] *i(?ʔ@蛜#>A2ϒWq$ƹvkwTYyθmB_oSf[HCDa]$;^EA9DsxO"v܎%.߳̕EYBܢ* `aqǰFDV 3 K`d-% ށMTb|&[ZJDF X/-*{A2r3*'0c8ʪ5G69wkMGް\A|1jĹiLo2OfO|j=?5g*^lv*]1hD{o-0Lwf[ b:uHf+ܯBkW68Z6.k8f2`5Y*m|u6sIwc"Hll<2Jf jBj "Lh~-guTa &aFPxRRGSM_Η|:X~NGZ?d h~؞]O^={/݇/GЃUϳ g"rs5'sX5HG]<E{,"XoFGOe{?d]~>ԟHOGUz4a{c7A?r-O^]DC~1?L ͣȣG }i>a: k8?jV^aL+BxzS 1$=$qUVapz$Q$gCjoh 6ATt&(jz$JC$lV*x@<Ȱ)[24'`a3P!jvdsF1mT\< v‘VkaVG˻, 2[A7b %-`Z04}+0ElL]UԟڙČS ~ٵ?sM٩\_ K*Rw4 _Fmh]TK)Zv2ֶ M+ާ%P5̊m9+7P I ko D@l4i,8x@^B6Î D hu~(A)Y; @4"=Ș h*1"[9PeE?i1% Xa H,qPQJ(pXl:: I {aF4Dn 0xx@n'զfp4=E3bٴ K;Z伯yj _qXʅ=+}ƄF9݃x+h#>x/x{q:ǧpvzvlyQ1Dɦ2?8y\#ϛ|`;]D@.|ӣİP4'^ :7 ]=n|y~Ԭ`6bEYjC^'lW7O&1)D[}\!uc*T $`FDsĬnu96,^P%`!HBy>kY\,2  QI b$ı% s C:-RLU&U[FfF3V@@kPYdG|Qhl}څXe4g[(fX;П8o;H!O)5-XƲ|`ʯ )d h/_n]A8e46Ѯ<:r"Glt`ʇ&'w$v=+8.at~шvԧ;#n扣2w 8\Q}P,D/ `]8(q"^MHbRf! OJèkb*ZG&I]!ocFg02T؎29 h J,z =>GIP4Y{@Ѧ<_6C]X[z+n8 Ad=C+5 3xX9$?yЊ2xrm{k}>Ͼ|ۛS'E9?8-F>]9函e|_A=Sp8~~?ߟi߾`зLvyDLϧlODéz_ov[xȿyDIGyx?{=~!~i 5gӛbpxC#  at?ρop10S%4JV㋭˄#%Gvf:U r= w&8cï^o})TIPy!az@ILGj‹ƔBRͨEc*$ b T`@DAiv`!Fcޕf؉3}y̪cgoϺ1v5undmem6DG@VBIE@+^vGx0!D<Y0x8Wh>0x_ á'0M@Z/L2f~&q{e#EkP42p!V`g] ILR IDATCd^iGYIR,hA ug&/^˯N9XiP'%lh5[E(I$oZ~DGyi4@U.&UI0Q)IZ7rm'QӇ fP㽣3juQ T?!az:\~FId7u&{%ދ~@EgٛL}[,1@eDb̡/fs궣M)>\zz{wn|>ͿkSQᇣ[huj}fY\u #5lYk@BǮKS%]ک4w;Wmt # o8Z`*%q A\KP3KwH86wWOVFH?{bA?E]FABD=wANDmdGq11Y;_3S! %cBQA3AݖM7Hզ$|3[i5`nd8g[vBˬT9*3h46GBcPONwяXhp:>~,<?\LUl6qְ#D$_(3'm؉MIzE&H2཈1 `'CZ)9Xk! F"6z6b#f#AHO QA1@2Bd>CI763+Ƀ*ŷzz7JXiY%ۚ|ω8ʼnsG9V 1bPfdb*QP"#!rbjCD U "aZj·m{چ8pZ=~: &~1(Q0'뽭nܱ19:.')O1m“bceાى~_]8H,^a"?-kpWePUnz<8lfÃxC?j[o1 5TܞvϟnP]p]:&P+ǟܪ͖@r?W?7~_6a=_t}3u𣎟wx?R~o{k{}WK^P6 'e3=|[ʴ^nV570)Ϭ?v=zNL~bzX/1HǴD9Ɏ/P'I5 xsruf×`c4qf&VkNyhB*PVҨɝ q{6fuZTY dTՄ(CDO l!a>;f[ r@*2*,\F`11wU>{1ib3!-F^h6m2 ţxt篜3|?y|Nm(MM.2͕'Wo.rQsh{" t<<\1 Mʚ3M?NN#"U"um2ҮJg+LW[\8f%wYab9 3 ZX*c cACZ)R("&w%1J 1V~̆>7DõLb4?<Gw^40hvc0ynXh]-'h `A+9ڍ6urAcP홁|}x|4lY*vvф1fDP5a1)YIIbtMJPx&$CS̚gX  af7Ds$ef-`p$M&r :^V*79T@ZԔnOo\~37Dsv)6UW1'd[y}~ <".RB &-{f'WO̽2r4A:Uv$RvZ`ONbn;eJ\t[JDDt&ٰpVPDS;( &Hmoy>a%v!y, ~oq i8-DoS1X$*nzAPUiq؝ @؉tURu>&[r5I~N"ζLpZ Ou|6S<f4Wew$v3\ִoto4WFgw_3sl|SjMz`Wҷ?ӊ[Υ6uF+]>^LUlW?}kop}+S^g|UY <->z`0[W:ūgCU۔R>֟j&e?)"rM[7cwr֬v~=l=iAt]i颹)]lh,r۞`BVޒh}I ]~GULj2Ҧ~jWjGJJ 5^M ]>;WLVWԌ 'D""H"ՀpN1 /[&S s@>أ!gޚ\)j}WKt_u͔ߗZ9bG|mss%}PB<$w7 MM\ϼgտdKu7t2a Ѿ:a5D;Z6:i/2$! bбE,L_?6A 'vl,  2+iwJI+6QN% hg<ハ@Dd6 eԒƀ{.lN1ʂrf2~hk|d _YYWZt-Z]cAwڭ%o֭r8u$,OAbv5euFeꔲN SN/])0_{o_i./Xv+$&ٴ?ncNߤ/lmvo˟7)wKׯޏ#)ˍO{W{<qm}#{QgƗ4LG3*|/~<\R~_cSe6ڬ VvS]Fysݿ7]2xv:Hy 3Mxc'?9>F3#a?43q_s;{_n )3eMjnRAH9ĺ(Ԓa&V6GsbIngM%b `!5\OjU}t{9ǒU9~4;SJ|t-D4gli,:q8ٌKi` )׮? mtӯOhw݁$I 6;uý{L>lА" fU DH,I &`2A͏Dh n6E(D+OQ$ۼF/DP~924om>jmZ&/n يdReф |yhlێv:PvACE[b6sʖkb 8q4i7x>qйkKWSζWWxw'p|^r,ך(-\׫ί-ȟOoʵfCeC DUzE=C. UWSfl*kMߩ5=SsuO~'sIM_ߏk|=?ӌw_23;g"kΗ=n/M֫]lC$˰m@-kpZ)ח7o6O_<{Ҷ톘`K_PM}0TD1Ca f7B_B:^0:Πۮv.hhlv:!4~sf{cD`$f߸ ~hvm#Ѓǩ6b]f7O5ʉnf3Qq=u[z}3hCk[N욧P0hA6.$o#] }NlH6#0.`*z*)y}R(]Ü41:@R NI.m& =mRYKWIjMNZ A(ûWBm!JB7YHY`&L˭o/}}6sR@U1'ՀWD# haO$j~8MiK4q(6 ~~SNS/$Lh=4IqQw~å@O\lN4ZKf*&o2|V dٹs9iq0~|-}o5u>HݿߗI.Wӟ&K45ҜLx4ǧ-!;P3ɯ*k'\k's0ηlsOŠB:5w|r-W "\B)O,E yyg$*%׀Cgٔh]\&WPeM$"1@ 'tj-PLnrdŽ54[mNâF,6c$hAkSbsP cQ4; og OXH1!S=ƮuUMvVR+/$9oj|S˞ ۧ'OGWCG#9:1E .?. ?v20m>F.HrfZ48aA4&hrTݯ!]3l1lS.c MXC脘5 kS?]]Êۈ MjcD7YLDH. \q.Bl!!W` $ ޼<'"6P.gm"lṴ O&l85\EtPX"8_-NgMNjvl)53)y /^e[l9Sk-j:3;y1ǥIr*w.oz{'>˯z^kރrD}*Q6LO/=ޜ|nTVԳ`akcjϤjC"5AMlj*&dKd߽i= 2\]B%_"*J~Fam1 .4i`TɅ`"QUbh9dhdg `Ƹz啜Ig 1A%ªpmPlvh7k,hl`.,cp)%Sudu/Ǭ@*:wϼxB[hv gamfuUMc3c;ڴ+jv妿 kBVmի\D>s]@WR a 7 m*WC ` #u"Э%`lHioٙplQ1ciXͺMf > XgEm  nc%VR`ڜMд YK {-*U]v$X:gTۼ1^1ٕ& 60\7 Y'xtɝ5g8:?ŷ?1Z4mv>fԮ&p=4 5U'T~ٕonjz{%}pBMmvM^޺#ݷ>Ѯv.ƌV< ɋv=ogIoND̦,Z\ #9r-q%W4@X3ob[\֔yNFeD+Txƹ6&5m1kw%5Gp"JܮŘecCAM㬊vVl4;)"k= CBhBw[T%T# ShvI4 4{|W3jv/Z~B_;f:fO-8VC9B IDATGc.vZne{uԆjUɵ#t{5DfRLgv2"J$9&m 8wrP-'[崳kEVW<KTif"m/rq@Ȳ'<=檵ςl!Jy\=\q쌗Rst1ciWeLЄ>BSR. LnT_wbP'c31}i6622kUNmQք;7/&ynGystDy3|>S&T=+}qf߯O&=9>Wb6ˏ}aqYVw[ 9G>?I\퍧Iyj&4kS}Xո)vݮ! y(nz< cZpZJ^!ק;9OT́o82룍2 @Fժs$x Ɠ|I7d24""<Έk fPoC _gJ̨.SArmN r[z9ν2 un8PyȞ4{84;y9c*-׋|s3~ݡ[ad{TfuK7=Jc} CUE:=eE`r"b@`9 .A|@$fU ehc7躵`<(F݈-+@L9ZPHԑ`TH/ CD1)"H5P`V1kQР e%Ur[X!̈Յ*ldG*m[(E,"l{䱡R[Mman2T^K"%$fY!H O}?}p}*0]FKa{sVgÖ6Kt N=&RgCu m%t~If'krlB®n2.0 +N. ~ʅk8_-X.68|~h7e[l;SUyWO3K ޙ﮿ο]= 9#Uscxtɭ懝ncL0d;cr<-buWGԖc/-yP-sΏ7=z**n nMTDDJ&op?]oFH ÐmsJleS8?{y8 %Z ˚v}ɿ Vhy x)nKM" U bl׫VV(`!EYGQU *Q!mHTUFn)#FtLjP ɐ,)d;Vb"Ł+hQ`oBXiX.& ˛ R #u+E[kק =S¡ٮl)-^KShr/<qN`M0׿~X?M3׿BNC3*: ^>q$q1C"t `2ǥv҈-*}HH5l-h/XG*ċ:=\9@X2 #G8PAbg6KEH`I(1b歑 BuDc 6 ҢdD}XɅg!Ju4ڗ-2F\lsEj1Ņ^4 oF힪)Xf|Se B9>ON~ۏmlX6B-X]tazS-ҤےURWIUR(3}EҸ馥v(Ypvi|BǮ#1LYxw-yL6w>èxs$89;G/kkXvk<~aӋ"<'R5Zhy6_^j[ܗ'^;ݗo{6=f~_u tiY0ѡwx.CLO &H[Qd z￙'9S9/_kA y]/֯$v0)r\nslw*B `(3Hr.7mI-4E 'kmI`U)4 , VԽbIL${8@PK퓅B OX -(,LML 6-tѸ jNyNE-XTb5<4(Lp٠ٗ d3}X5۔k.{kϼ?B?Z{$vO?Kxջ# >vuZC6ؘC qX/r{ [ijef bJK(k~fhkMhI 64:-/ ! dixJPBPEWva9p6C]Z6{&ўr(U(4"9(+)-L*HnxQWuqIR<1!_FN@i&Yl$A~l<\sIeG>%3rUO.u6qh?|O,&?Ӄ2JAkΫIy_},}kbu{E\Ogص\}mSj`p?N=;]"9]uelG<|9oLYMt7S(vM߽OЭ†mӽά),S ;,}sonlA !-n -If<`°W[![) ɛGj+_,bvZ9EM[UDUW?i]i!i *K"36?9ȶnu4+•(Y+ ;/vg%ێcP`K|".kIkHAy6y0S@wm70/,TN5zh2WLJSE7I15 gd/' D'x}B/.x/E ˼uK1RAIm<Hr "!tJ[nsЮ[AG"THDñU;DD tdbɌ#) уtFRcbJ! 8u9Y8 "MHd0s`Qa*5%R6DP'p#-XZͤ& wlk#6H$ e$ͩ;`a+([/f5-7"L^ 5([2e9+Rup(^~B6VƯP;2Sns jܤImɮ2HB5H>Άn kPTa 8`^,6Oøsnq?T37$'~#bоUןN6o)͕F:>5G&y&vt̍_ה\s^>XI@}y}\j6MUby}KiB2|ϠU7n{ wew#&Io%[wf;)cqUPb%*.[= FigffC.bqJ" NU4ynmazUXN?_*0zIm/(y4+gFȝ4={8\qeHVT@7&8wWfuh$5fɞgޟj8~Bpu5>~sw_n\>Ҿ?;!9Ϗ@φKzOL%pBefŮ,-8#@2KesS7*B⠊anCd"(1*,㏶of qi BYU B&EFD D j7($nҠC}"  ԸlpZ<| >q1nqv)eвMXיꟵPjm4}V'ƀ^x*bO9iN;sX`7)r\ B jQq%jS&}m!!.KTS`gʅhHd UTjfcəϢt.λj`p:&2D@J @/aT+bASq>u,nQ%?N!' uP5,1 6A(b4,A ݥPRvkRL+̽Ft0xC\ZdEBX9U./\.UAmZlrŃff Ͷ j-4ןif_曤9msII<ޢNinVlճ5ױ T 0 P(6FB6Ĵ拄h̪ۛ6֊67]p0_$vh7i3vAU$Y(9vEAȅ - >;"ʒ؞u#U2ݎ&t+Ti{ 9;DQ }4 A =60 d5֊> V5hDBSM\_4H'kv͖6/t4o%Yhd9m\$;] {@I!t lȧSs6󄲋vϮVeVuφ*`<&n/61B*;}Ѽh 1$/&FWk-#3}dÜbR̂Jp,T_\%mv]w} N1UY5Y.U+dGd^Tdc|'gOj(U57&v}쏴,T:0(rH[9V9P{i'74vǯKGwNGbjMx| GW j}ǿ2-U.:nݶ33uCnVt<Pή <\?3[~岖s[ )s]`)|r[{w7o=sbOmJ`ޘ6@5ו~A2~3]o=Suz]#]6P! :YL߼xSՈWro+نY+%쓊'-%UNvω1ɨ*̉'|ܼb?a!DX"@2~lCщv)Gh{v> {90ǰkj-ї6#Ds?sKxs2:-&%~L˜ Xj;nqM Ʒ]$fT@-oy{}/WMaJgi޵kY:8Glysn_UǞ6D{^cmS꺨}r 6_Rr TaXlo>)D%Fy"=eËީ{@ldPmuJ(F2pi1b KXߣk=9HuJ! %JbיWvpD$fRY;/@L DHla l;,pD)p,f_\=ćgR<դ O 4"UZMnTۜޙ <?{~'Ů:ޡR͔ă|8\?RHqju{4ku~R,:+Nzi]F):7~%7ח٘t4gȶEqrbX/W\k,?6gڸTX<)O3첮תeyR/_ʷ(_~r_zkw|_3rA|= b.JMwvn/(}k%ˡ>՜QzHtMǟOCN١pfoU-Bmy:@x䫳L{n[@͖9o^E H#cou8Q鄃È0eiIlAHNm4ʤb< n ""9.SGH{4ffaSqȞmFU^> IDATf l0+ 1q'`Z h8⒎nLKŸw̝+i \ ZpX&$QDa$lAL|%./l}2hv٠ٖZh]R眀4}H\h_z_lg/Lgݽiz-mwIb57i-p`֙B *b6́7s2x٩H$,ւN1D2 ,[Yآ%00$J B>$  *hd>"A+(#5m!᠜tFR%+f E A SQI-lYI#i&Ta좕(4km "3D\`2'_l(Jl(1 ;K aS+CXNƓ?<:|dk Ȑt^aZnh~~ ǚa3Nmu[cL>pfVpYwhTZṌT w:s_Pթ(zxslv?XusmEgg[&3Oi.'}۷(?v~)/u|=?Ų;rqΏp|*8 QsjmS=OTG&4:O\Z 4H+4󫡡b2I{ Tp.lfjIҘ箘J__|g hS&' x,*) & 2IXU 9b_$jWLweML QU%InL:,̔j%U0:C Lde@0bf 1CD ,]Ph"AAK)u#i4/QK~B4 pLPfQ&<#PoCi`#@XjX BHͲإ_+]hv:]'gM_χjx>?g߿5.=lȼģOQ_\RhE]٠o؅ho[=2͵;)oN 谒[ s]>qpT$,c5!UMhC #L65IN3: c;P&j\6tl ծ5uX܀(C@aaGY_ly! p=6t9sW=h~v.@䯧1RMzv‚\`VXZrpEBD:JGSdzTutqLO1մUǮji5WJGśNOЏ[q|XW}/>->Kr/=gu}[-n ҪOWdMS<:ʏ?r3;Yq4W \]?o;FU=3`4[vimiG+;Ni=͜W,/܋ 1i5wۿyyg`MڴK5ZH'_SKJվ."yz*Qd,4(rLġZg#Mɦ׈L~L6oBSױ&c[E~Z:JYw1Dž݆Z-pjwIFTMyAJ̪1@)  VIqbpy*6I 8q&h ahe0#@tWdU^ &V ~x@2vD@Iٌ! ]"X2h6fb3ʸq읔竡~*{ _ zn^!|_^֮@QSrv>C=3ZBv6^7躵ņnNaRnv}@a4x+&^HBGu DIGQ Zm!Dⱝ;UB!X)00RNđd)bI,ޱ(Dmu v0%h4J"* ܚU"tHX!DN`MlEz;  Y2AݲVR`R$+\@"BԇxvhGVF,{aJ}týeg} U tz-Ne*̫z|)MW=N[[X-Xty`_lF dbܖ"6]{]Vmb~RN y8W3_3?m_u{ڽ?<¢W<[ Zgռ/xv9ؠ*@B#evgiriYnJa M`Fx<+)>͏.7\'v6,fDjKmϟlrӊ3Mru]v _HSߦdڿy}wgɜoʂq)$6-21k+- pؚ*2L%"v^|1lFC"Mjο|7@U IH0ff0{46;PRLe iELHHEfi8 u+ hby k6 N|sE2{j*Ql7fOn\AK2ybkvUV.L<QoeO ̅fUOcsЄ3j5}=C|p$9Kmsh!Rt9Mp-\:L,m>6yQa- mrjNh5c5ɩ#pSAYGb!L9l2B bq-f'"9S 0)tcAmRWJ K^LF$H̙/0HrC`^+l3b 7mqW8ɶj<m}5Ƨ _ϟa'cP5sf Oi2yF`x`y!?ˍjmQU?gV4ovP?gKH zv3Y͗5V5>K|ї/\LUhLnKIQ5sJ\;吲2K<'2?;O+\n/3n UYfzj~Ȫ=TyԎQ^BVo~v_O|K;CFN>6Ec,6IS{~lNZ_g17Mqڳoqش7?[4z=nowÓvw*R%x3Ŕk۷c h@ñ|,&6:P0Dvma ga4ReÍ++2Ŝ'ۭu͕dͅt(/JB=kNL¶!PVSrBZٞ]Z3?k?wbi.@5)0SU`n9G1&K7È̝&[0kUeWNNU|i׵;jvCOd|[{ep%|Uj3m# __ -_Ç8ݤ{ _\z?wR:u qiSGhjأq`_{`v(0}K`vDn\<*!LdLUP2(Cɻ wE*'YPE9KaU$W@E4J(FIC)qIP*" DM!BdUThTٷ+E;矖rXҗ|7GNPDT&+QX`$PE&b"B70: ֢B+ܷS;J"7 !`!@Tݣ cQ0uЎ&~p!dm(ƪf mn8Xzh`# ߘ<70 |'dbnD?&Os"jyfZ<%oOkr6Z\~dvq B;E|sqם_[$R<֙.NurU}rn!Vjyr׿B/ ЫeͮY03om/ (SC0OM+mG\@'Wt3 !r꭪QiJ(#wI~q!1R@@չE\9նTPx$ BrDsfqV*6CY͠Z!Tņc"wIzVLIXF 0)ZQ+z"D #UeJ j V&LJՄAG\PT3T #=U3qfZB5-M6)-5ـ;ߺ*=߿ܽZ+ǫ<#9?Y93).sy,~ge /c ?Zm,9H9<}W3|66/}6{yl._ nQ.aQkW?Jڱ!%cm8E\NK;Z'&:;l*@TQ<·4j@dQT"6ډ*YPE5.Aͱ#*QWB)gC559'cGЭf$JŪn^YŹ"MTdF$ +%$SSP  !c%QQJ5'T $*MI\eJrH ̴(,ڄ3\d tRݣJ꼥`T䱌ϖ~}뿾_V"(gj'ű5)]ldWKu=XW._}]NgM3&ig6.O*Ei}_ݷb]/޶WԾ!g]TkX濐>(xu7aDRa?wq}n>:ݿzхi#۾e8п~¦oxnb|}ҷp}콾t}ywaLxb 8`]b'9*})ڇM3_H̯O Y {$& |$}.]rI^B`,+l˿@oUqZ^?'q74JʃZZ)$-ٛLwBcRh1 T4A0(,Ժ)J^Vf a!K[9( rq7'jBPٲiI<5@!a*1̦aZX5S1ofB+4SSZɭ6Nqz)](N.0Hf0`U5ەsW6ٮ%-6v ?- IDATfو%uޝj`4aYS`Uzʹ:8 ,U$7͉L+'Ѽg)䜍H0I9[O d$f&D5C%E 'ZB %$TD$9VlL(2HD`;`Y #B_ȶQ!h^עmQèIPFױJ7006R.ҡtA |%U-%-5v4!.HMDH|hMf?ȦmhDvA`ǡv,61Gyt.%/͘L&Y^^0A )1~ȷi!"[=.U_*_,j6xޜ8 )pg|WG`%nHi|n[C[ cƋTyaѬ6_,y6#fe|w^߄ |6l弲?-eymbiS{Z}XgOC87@u9-˗o~}Vk,m*&g!Uẑ\+hoIM[;ȹ@nq{p` '2 8 aPqu,tg󾹰'TN6͜i9Qޘ0nHۨ`a YHMչbF׺D@S,ZM((ӜTЄc9A]Y)DNu<@q+e B5VRSǔ*Oln([V?Nqn\BeXfޞjWgKn\5 fB#Sh4A6Ϳin1am~=yc}k>IO&fkr+a|Z܆]39X5 =8l6 vmwnϜ=A*fETdۜ"Uq,{d ĴJY9cA22l '&Bf2bzv$@OZ| E+K㠪,X)"I;(GUdD;tUP й@MC[gT?bHswmy!9 @D*⋷3*^(x7B3qmi[TXZKM`r-ͫgO<2" hbwFqj.oaxKn6GbYPV<şUK1`*8ˋ,87ŵ ?)Bm7HOC˻9bO貢g K{Mt '/\H?|nv{|ާ;l-n^W[+Q- Ehv.ұMo_jؔZ%?/dq6'!?2 ̛>\n.5\.CwTU {Yiق8}酴8yWtyW+ȸ|U%ja(Bˉ2=#rfPjF2B9a rOѰenĨ"b;uE]Z5U BuuSaٝԌV!x5$S 1@ \[ёJVVU@L‚@XʀzҔ9wf's0v7QN ղI0A\p (`TdˮD躃?DG$T(C.^kjFi2y.: (3eN/_)n9صΪq왟`>27쮛ݧO/ Kπge6T˿٦O ?,]mX/:Kyaw4ׯmÆ{^ڹnwm[= ( )r9]J0蛽G',j'etq(EsX-抪})*&KO8 50A.PjHD* jUPt- "$$%P}@*"nig8i a*srn: B4Ic߈(U&s$+`b-|HFX)4Ri*q4Gm&vs7ɿw!W?bq$uun@Bu7Cyzhm[M*>fc<{K?vLbf yKϲ(w^_+˻[*rG?vu=D> kvh-Pw?s/]<~r};I]GpeDǂW 3C](مz~=Q\o]֋MMmڃvneݏ /4>fe%^@gNf?l?& "Τhdxˣ[x(3 ŵUqWQi;iTFmA&&M*dc4&XAGؒ .VW8&% .5TVV$"\P@UzYodS6"r[O3R gg5CY^N]0OvÀ;Q դRfdٳf7S)qҞUܹj6 ڕ)g>G_LmQPU[ ]^ Ip^=f_n}.`[m}m_q*ڹ; lsN(AܶyF;š:[wWL=N+Q4mWh8UzUDfZWOT CU GfZ(6PsB  P DQZMuL Т$*F*0 T곉0?D@ʎ.2VMEQ3%3E pbψ1s(&{bqB0R 4R._#]e bx̳8٦6Fa6 ?6NDoI,h<vگӏ>}Ԉ!Ja-b{-Xȧ24vi\~#ߪ[,%6n#h϶xs1]rUgs;B /7HI"Gz,.C]7h`{0Wls#*oIW~?+g{r|)+8 ߼|X3jp{|Mƺ/2M\x,wY|\_:Xgp9yU ޾M1ц PA Pg8X٥oP* V8[6HcEZ0::M]XpTF%~ڸ\ԉu݃VrD8$et|B*W} PpzN0͑oa. Wvy3ِ5y2__-.M |.;wanm_c}h\=N/d 4WIE+m>r[@_|'($9P94:H;E#R!9u:HI=+YXvmUjͣk^jRXhT&S*1#sN0\ L*zC0hKPHLh%TO(LEG0JRqKG!Hw}L8k;5QH,fLL¿e6B0hbP( 5/f T![ && m& BDaJ wW]H@=t{[X D@7w_50oH-><gA06.rp7iZ٤睧/7+*$i#E=h%h,/Fd{zΆ{nWk/Z,z7t0$,DElFn|peeÇO lo6 Eն>`;?V^ޱ"gw?:r|n*nEAE5LI:p u_ᶽ8_ֻmjHv}(4Pq 0IJ_W qzW_=|`#nBP@2;aG2!C ~NvX0qOM 2 YL&5lIPIft/Qf3svHS9(_hɓR7*5]56jJ**eEu0&6f覚mV XЉRɚU#c\ ش"=h]js{ ,.C {>!PU*UED&Dǂ slgͦ(*R ꋘbHQ"åJLX JU?wP"MLp0S8 aj6鳕0œ[3YYb0Xs AYtn<΁z'6!U"RD7 )U_|ߚhc'f>܅u)6bO>?SͮGGi( dY#]eF^I-1KxQ,suA8O,~_bߞe?~?0cۯոո{wH`,wn!lA8[Y^\OLd-'Scm>qt? ,WU[cFjR%R)sysydŨI?xz|7hѯ([ T>Ua#їfzFF?24@ѻÌ\=oi)[ !@A=GW< H<C6g^^/D^ȏs6.L:﮻XRmKC]o^l IDATa,NJcёd}Qm}ځb{en?YYƦ]!CY>ߥzrٖ;{Eߩ<ݡOŌBJ_d$7l@82_/!bš\l‘Z%&͚B,~faՅ':RƬNgFTe您WG9bh$M\vbbdz &{#cй'fo/i6R] V%9Lߌt2$UI#φ@]ܨ@\jۚR8Fc4)_Y@ +4D [ IINnڔHa쾙)+.w-爾jj6`Ƣ<4l'N릪 ]wZW,]%p3͎264foQW̮jPP aaͤx v/?oЎ6m'l*p\px.s`)kRw,(Q noS#.\h#au?PPmƌ6W(V%%R]QSABj"bJ*2a mS*>'pΨ)@d@U )dj"L!@U`.UfjPqlQ # I:jCE03!-d@M UF.m(d pN"ģhmRD5SgΨn!6uuTؕ4Ng8T 4on^;!Fp)+믏_vEalۭy1jY,Ό>ׇn<ޝvHtz#ݑ5;]yƺn]ʿxjȅ%]o{n;!`b?l/sO͛ `Fq[ ">(Onv^u_@Vݟs.OOd(zC!E9LZ;fN((syOMoBY-n1I'tyz/GGؽ7ûft,c=uNef~!t78rmnKnR!өY8½ojAil7B]NRjBq&lOIo|4e2PPJ<>)HuS2ƪ&>R}+*$F9b` f:gs4,C^(R+̀l ,,V\El$eZ 5!ךpQش>!A%!4Cx0a&bvFLbq;(/1mҽ@/{r$-c4G6<_&G6g4 _1X|AXJ*K#dk^@:Dhl?0S&\$⮤pffԫEHq"rM©f-qƹ!LP@HUHXMPL4S(Qen*F=Zri"PQ/*#|O ,eQ@<}8O~:/e{'>a2z)8ھ}} ]|#x.r (ވ8*s1=^_HC/h7[7NSRE=\Ex UF ];9Y GlNKNȂ9Ue0O=X ҵ[]G!hv+ N mvlPm$T"33HɹVn[3J*JafGVMh3)/f4wM.,& MT&a~IQ]G6Bt:65}1T5\\LjIT}<#PA QvޱE@n'm 7]{}w'O?Hڨ*$18-U@nУӱ婰,9]R> HkLyY|y)K\ elgDckXwQQE?{Δ<toq7|{s qetɰ;<9tIyUߟRR}{x8?3ZSGFWlgy_}[#ATYX;E#>0?0 fk\ʿO~G|m;/"_:RIjs|lMsr4YbNz1Nѐb_kj[lMMLg=c7qCmin5g"7@!J4@2:[bBa}S$q"#Q eC!(U KʄłU]bP e eV%({6-0M/̉\6ձ e(*82/ Z.3gg]?]5Q晖U׶̡=O(3L(e ef7{;xe^ =g~7k{q[ͷfv@y?@գgԲ{| pR|)NR*$qJdE׷jF$GE{4mݔ$*RRNNjl%\EU'`-+)4 NZ-&ݥ( "DSUWJ;կZM{Vgʕl|7$C}0̔A!B*77*@@B4Bl:;aK*L >csE u݆i#sw{xv_~'dcB27wTY %f֜L blळe6`^-^>?7s>vTr\M_sO\l[l9{iOO9+]5I!1"B aO2}FRw f ñ|RJC;oz_y1Ktt9˷|/s)^L;Z,qzl.ŷrY~m\x;t2VC4|9]gg,gϟ×3E WX]I-Z6l):`I!dqg=I!{syV1u5 ffX[Aa JsUC[)~$#(4YJ p+)?Y+(bnɱI-% )DGUO}7R1dVd8"kO+ځut%H]ẖjvf$D)u|wЕ 82^|371mGNypz͚!k=-3-.f?f\{oGߗ5aކf`695O`'ϥ{<߆6 p?uʖECs|/~jR{<Nos|C[4lp/}7Z1Cc!BlcJY/ ez\(׺z9u/G rXhoH.g\2]䢂F~YυqR?A{8aఛ_Sqp7ODXsW{¥r`.ew6_+X/Ӂ+p;X|>:/a` ̥qw7qMMl!&,Nkӗ_ fg=4khB-{`$aMm&fְ-^AKlN * QZ . X/ -vȁ$И8HN>sJg6T({""ALKYn)uVQ1hr*P@+mvG+²kL=8 @@ <. @"MS /ZJǔ\WzWv/ǜWQ@Us3盋2Yzyfޢ{mR1wU'Q歰B gQT<_7yVt8)؛ *]wwn6{BhFTӪc!sj}DJlP`®&ET3G[c!Yfڬd[j"ӮZJ"xեMX(*=Q]U1$Gq RYDǯ V }Z~]EfiIEyveDDō(<1g$z R.jK  bh0AL&3d,մ/9m̼&tZ BO}oc eWϏ_/w~5[DmJqMWm0)@M%cnz2mZ`ct:첺gǭ9cLHr<0?0v 2~VKy8`_ao1 +~ơ /ӧ߯_0Ԣl-k2L7'B˻wB6JױaF}8?;ãJzW[mx?.?kmcY^ǬQGV\MϗG |(<].#Gz˥ۥ~$s4{ayuZ:f|}WnT:%¨s&R@ҧ@&Q`N/v#BMvؗ/3N*N VCX#5\{+I"?h-7r~<\U()*B*E+r6?墤YV@,`?{o$~ #"Jhd6fn.^gKYmJ]Reef s!Yz{K} e1H 0%5 4s2+3v(3Z` :M3& t0S_'5~v'<('sUES5n48bNF2t@Ni#axjvT_țWxW0edϼ,f}e >g|K}UlmzWz7 4e[mC5LӜpX}*L7 xzh m>DYmW=v0ytgp6jC}iH01dRR(`A2d0S&$E#'NBMB , IЙL:0L%ω Y SbU3(f8%L HlVM+"$0jpb@3sS)[ڼO QմF1F/Z(26ުԴ1[`hio޾y}0DྂD> w5O9u˼`(Gmεe//Z\׮zKVRϯ#a/[p^ǥ/ԍCS]ٴg]a:nܷX=\I(56GGoAVWz~ڱ?>NɼQG4aUzBw-1q<^߿.O[SVu^ߚ?ZZ=C+|ސ/G7Rk_iw~~΃Y|Zh~?yܧy`lkof&mO,nB]W}ugL B-Wf*oT=[q3ΝRG{Zv}fXkq5@8 VӾ6e 1ei-`T4׷zSVW#WtRAJ\E\cOV KF0ϬJ3,l '+ "YY2'_.饔 -Vgl2bZkk)C3,@rx(3x6GbAE:c̊,'MvdXüS$;K^[wQ檚mxwc֥Zcl֖eQ1(OU!U̯Od#y;Qkhsh˗'-C~cͶh۝RT F\]D5+6G2pm 4)A1a,q~Ĥ3fbfcAgd1d=ڌda *G|AZfU4(9  x0"Dda@U(\SD9nlh3\Hd3U@ ^> ƚbwX jum#CwFaU.X*{%u6sјpBQaSS+Đ38@oo7oN'G$$3k:(;,y82;2Sord9pe~/4`ƘJő^ZG}hPpjE=oz^Rvx~GTokm#V ] _w\2.gGqi:5T*G $/K~>7ȪWqꮟf>4"1|z8u`l/ccᆵOmsl{]s*wVr7i܌V{}~/(h;_ 7솙ʳY_c6*-\gZf2ԯQ&c;- F-MZ&ɡAQ*Ӗ"}j"ȴIR|fUg)d= UzE($#BM3\@ALx 7͵ծ P;lRy`̹@UY)fHFg9 @ȜIIyt *p5|349ʌ`c1C^g8ٱAcyN@x^̜9BJ9ʌ米[V}"alsww@dw=l=:; P旍FeJO|qOkV~YnOL+L3Ƨ~ ?+_ϾO>yoޜGJ/ IDATcTz@i6ohw׊CTws<};[ jjw>>[= l`ܙCF6*|a m>a!QJShQ20yAfܳڬ=gg4(!Ӏ9BN((AYTf K$'FB'`L@J 'B2jlRHh#1 d@>;2oڀf!fnfvfTmD̃d6b.>GMJƊmfE %Dv5m1%n+$sU2͖WPl4XHÜi_~{o>h_WiyeR9Ӄ j65y+n/Oo6{ jilLZd} s7o}Uz\f;ޅvi̙CcҨ'ҨC<,^.nF=M~sJT,~s9J]m>mCnyho"mҦ>t׺?/qY^%x}lfi_6Dqߥ}K,1f}jW~|lV;wSWykF{e΍Ϫ3y֛z/K]|q2"G0JX]߿:>vTUі@M20'aN Kl? ^߫ŝf!vPQ|v vfcg$!6d5=l!mSG` 0X'(J`AbcPe-(Civ;8L{.( "P!D5( W\5i6*s22wyġjJtnXz-㠗[cS6وeJS ]4C/N5˼Tc;ٗ `??/TϤwRexVU(D&5)Jݼzx? LE =M({Gw|ԡSO9TڝJ|;|:aIp 1Z ^C0BIyR$ $ l -3rDYY>ss̪P֜Qd,Z$nĪR.9) Q%: `<ZWR(%_D}DTTjbz0p+3t&U@\ mрx}0.&\%A$1B@dQ(ENkV-r|'&@$7o7_Ͼ~z~+T$b<@ tjW㔒l"/dBmopRJma3Ju%t=ޞ6IڄR/׶Ϗꠓ.TM{4k{ܸ5WƼu+紸u^|~}\3~t~g;&C;mZ6n*I݅Ֆ.󩴿g;Ots=)+U)R]}[EcWS 梽mպ^0a)ga6xfۡ$빪"β?Wi955,|}8Ë~*P޹E԰1]U`u$0LjK`(.D,E">3 tyG&է*$g3A!ԑ^YHY dۯ5TƳC eքQTYs6]̬̬ P Ɏ2f@ XTlC3 f*wb=bœ4*02t۔q2~墎m PfKvd}?U5"a+ަ_U.0|싾$Uyz` ρ6jF6x_c=U5 {ua8(non;]W9qw-+٥w'|h&,fih ̳3f=E1Ǧ( fruVl9i { Y`RjyTCCivf9<3Qդ쌸3~C7Prvp8l̃mRfb2+]% GFCeԐ;IT,\ =`^1 3ZF`̲1oA"ǵxK ǽ/|oca5(eY׻i5idi렶N &?#ʠAm>ף6 "[(P ̴r̨7cVqOjhË~5u)ܺ>~y 1YvY߿O<.hF]S ?:~s =x~ȻÐތ=QAth֎^>_٘"Ya`6Bj l{{w,~/=@="ky1{v2%NJNL;L*&,3Uv#WhR GI  TAH#fUf/X3̬(31֜F fsBf|<' eVTPr>ڻpKa`GE(2MС N=Qqw] |tq+tt[4ENn*l(+fj`cP濤j%>_<ّ~ALs0Ο|?3K.->EM5uwLohM6/0umwh3ynյi熙=-w)#TCWr4)a̓d@͹EYD)M@/g0WRflOI1AyV2n C$cvi-D qF )+KEa Ri6il ]JNwMV*fyCXĂТol03u Op)RM}oT*V0mgܛ *FEЃ*Z.JH/?7]H0'dGπi!RYq9 N+qo ᲆ4_K>^5hʘtmk?]_2\Q=E*ѶߍgqK} ?wY8??yӎ(hOIG!{J/kxMofV[~7ZWC'0C,2}ڭbJ'_?;/o >6˱]PUmT1Qm*cD lTU8< CP?7ǩf~*mz_f~ۼOt*?ڼL[w;:hm2ECUrmУ(n\ʖ6ZY^!f S6XXUY&5$3aؙfoq iGc9iΡ-@ 62fHPIUW( S@snCMjց6uN 66xW j$$Q7*5l!yt%Ο-?MMd4!6}s!2ZΥ0Ngvb9Z!6|Ww=qxopd K@QuSp #j׻y@ Az7Ъ-E@iYg~&@O/6k҂=E_AmQ&JlӞsYVv` ;_O!kϷe5k&P),4xQoEqV3!ߘnzϻ{gˢ>^_?Ϳ&!v>tPǴ#\~ݎE8?}ݳo0{]aId)~ylcur=e$IS6!}FQԲɴ ATP _R"02Ba^ؐiAAI=5y}ApI1"3B+iP&NY2j&U $-h`02W9hVq8:ngPBQqRNIeezrY &9%uy馷eml͗:9R2XΖԣhR2ׂejQ>m[(ET(sI[~ۈ2sB6K*گ^*/Ws[,UM Kc T>n2F-$_aC+)KiEd͖/c,6m>h j\mBoP©6-N̾FUBX m6jVI%0)j,.`pd&c]ZlSUK] uC+ o)@L(5mc D)S`sFj @T4o/Rz(9!HMm*_b,g gqv}qg+8[N8 ^Q.Zoz5=p [?@^dwa7']Yjxj 1ΖSkHisŹ|V~-OGR %ܺno_lj?~?6A[d2v~y?R3mv-Gc1˿K^͟ iOiossoӋO.;`~[ևv|c}?[[_^Q_;~}=οȘ$\?C6KE6G{U}~yVmq:ka&f0由8L75f߳MMXv-С]hK,qt8b a7J %6r=LHbjx)˕e&r]#6ip(mQfv?+ kVF H`ɒ!X*̜6Pd/ i'3e'cdd@G"8 z<>L5Tc{ZxF{ۯڿ1m돟̳Rye5m*;rUW__fKHNflrVmOl۳ -G@Ṟ HDلLGJA5XTdƲ-Dʭ)YC'ǷjI<:(n~_Ljμ4dyG+ʜg:سf.^(ksRNlKA?[梊=a 3Ql'MYJoLN~6`Me=]KM_-[ejAH2_b?]]k`_+Zm힪h/w9E} =ʇ=#G4–FY5AmI3`ʍ_l[0 m.y{e4湄l:3kl%ZPEQA66C2ټBsM6GLs6鮫jIBKCQ&̔mZ쇩0`io!Fu` PWK /&8Z<.I+\;o0P3NDm0 7g?׉b%XfZrO?:]?=R Lyh@39m6[ cVgg1oY>71=@?ѿwtm=m-_uoߟ6G1}sۉz?;Ϸo}RӲyg'o xٿ1l^)嚛f5N u[M3gB7u1~oҕF{j~G԰?'G"iIQI+\Ɗ6ݺ} ㋪B WnMl?}#vѳMPWv[-a}OU6aKbDj˲WEc̙hGUE+q |P\ ( 4Agd1'Z" ;huJ>a"Y222T!V {1<^e9D b˜3 ,1vC7N/:@[9Nh4%8)DPp8E;A%}ݘ3/LٵkBM5{ysU6nۦ?e>5?KI1|횷^o~{S@8AsjueӰ@ɵ(sЏ"@\&8-QfSϡ!&Ղ2:kag`wݰe6nEI6-㼅2Р_s/se~Ȭ4eQ7YZWJ0Lӻ"Η>/köqqyU 6_q逊6c62$0&r0%r^g?ӌa܋  ! Fc̹30VVC=VmA0M$X+:l # f[ahٜ̞9b9%TDd6YUGkQTT n6ʮdF8z;a pcNCKГ-2eC%<<98bSsX`g1F tM9*3Jo|/0BP* ) ڮOLD,O~uv!c:B(MP\~,hPmjU}ԇ>m(A+s!?e-i_\6Zro/Z~k|G屙glm=[~=^Ǵoyƨ /acΕS>3k8y46Ԋj_VBH|{Q@ͺ DUw/X2L17U Fb(4!=bm*e%b3̕.ft41bZem0T4<./S@gns3b(3ÞmhWL-]a3!fc_ʪ*@8]3$l nd \;mr1N Yg`.(330#LyBPeGSg\b3?2[Z[6ӷؚFZZ2sk?dܦ_ٜ ޒ<6gXhsF;,SxZԖtpСwɚ:̣6/ lq+c8MgsТR>G6(1Մ mF2z(sͪ8&d[ lUYRQDM +C琴jBa_^8Rwea-Dpc %s`6`#>2bekP 0d7ýt`MlH а' Nĉ 3ws.؇/9%^$z0POŋϏ'A8uWsDcg 5eUapO?wA -c1c</G]9l[r%1_wt@f9CBo)Wƅ#o\ܷݲ4nxiK/cgz,,vnܷ.ϗ&r|gD|w~GG0z_׫~o7|aQynC9g"l,Axzz|{|^f " +aF0:29xz-,l! WѶv2Eg]a_&0!4_rEeShkv lG-T93&7M M,ߙl>ZH0 9SPfXgvy2g s6Y2N$ʪ) r0Ue taVmQi2l[~ 9JdC(3Glt_(3LA>,ðנ[e-ʼͺiil|6g˼.3̟c߻ c//zX9C!yvSWnjۯ {5KOOBx mmnCQMEwN|޼g(,y{ӌad'6vhEq"g i'٥mq7) h3U6%5)lsix$TaY4HlDVHp2TIY%Lfr.6,U8I8&! ZëEm(@4J Q,̹( 4'h!g0gT9bl"1vq~6mCJZ}R t5a7-jk@M/爑]}1*#](Yy~FL6DM>j+qGj]xomS~y;۵ ֹ~ӟnXC>_|i-ҕG9Ku>>y59j?]x?*m{]?>O'qd`k T ]~Nr6יچanV4jfMabiff(C5[)E1)M lv`$07j(!]lfs)ޏb(wW;>Tbt2fU3jnxͿ 4h'ZEaʯմC"[͎7O>+Y6 myHii=?s0KƔ23J&hm 52GmF6k6c3H"왹gw3e9#l-|BA3#1Y2 ¹;" ghύM9pFHsP3ZU ó3E̶8`l@MuL(`ʚؕ W`aCj[ tؙC9WD)#™K8s+U#9 PfNLy6g#nn/|_O/T B#Y2Zl _#=\采yE @ 1ϖ7i}kYj"Ɲ#!m39;-^WSs\jgn$[2٘|{|l%jGݩ8  3@ rgN\ϛcyniۜre<jOqA:_/nQ[/cbWRb<o~{ kz0 a0Ǩs daG+ ۨTQZ]>b2%fA$hGx:ݱJ T{jp*ʥlLi14CMX/fcJ6[gpW IBK2-( 0+Q`l ;'2'e8F7a84OU[1iߜ`+w06EhmC) l) -U9ՙD2VBV53_w0so~OG~>݀f*gݟZ]+0?h۲s;L'~+_LMC=Ro-Op)aG;|"90in'` (Qo @';䤚2*8&L3aΆ6RFC`!2` ȤIUe9LId0(a gVT@D srALDD 8 d&&ՙ!h1+ Y*M"h ٦0Eԅ oP )4*9!*#f@*Bj"+[\ RD-_ C d'6f`rr%sA ĬdԏkBGZhM\~ dn'H.È*287۷O\ӢoX1Ara^=,4g \`X2q1J\)m\rZM.h RizSŠ.F|\S25wmê]z8.ͧtI]߶Cvl 4m xuo--_~,]c.z2<+Rbau9eTW;-G-{M\7M"D0CjffZWk0$¢^@2,TspH%yfFE@Avq'Bz25HY!TݩS EJĵ@Ī*P + Pm]㙋N(MD,!&Q,B!W2gN MY g U >f(RD#'$XTU@,%Yg `dz i+CU*2`$3!@Ai0=i40W0 moJzʁ2g 0&zi[tPdvWqgffc\>jfse.>UDs7_ ]Vю{\:hV:ZL%h&PWO>ndNEJb6Fֈm8V.n_ruNf՜W< IZ9e`|oƜlްYu3ZYȜʐjlv3#Ь,Y)6*%֜ ѐVM=1ֈ޵> -6pEM% *ηfA$:/&S֐EHdӐ+95=Σ6q1mVGIIlep&ڧ8q1,^:`B1U-'3Qkx{;ox|{ʕbkBO,_X_{q=֗ѳiQ_нqk%=gn4c8/ߟqggv,U=2gyRlk+c[{Y6~g:Oa?ȰKBoOuܹɼzuz7oHY5^%W0"e uaO_fBDZT{Rc.&GŎ aS680P`aq FN"c EN kZiWmVQΤH(#M GU\- ,2:MìaG]e PDg(ɜ3 656 †rZL0) 8|gltbK74|rQf8=en|ek?[c=Wֹo޾ :0!MF]z$KH9Ӽ%EEE{!~.m?/w#\__ -}^-/ JL=lp=AԴ×q+qœ*5߮Oa[rxDkLblj80yӠ&dJxŘ"`M:1kPd"t-J<6"Vmh- Qh6eF{dwSvF;VfLmLI't'"%8voڢ5ԕ 70AC"oJЛ͟/nF@:2 ~~ΤIčp aHzd?_=:CTl{6rq-w{Dؤn9yf<شLx-q\<x?_͛ /k_(oީ7{{ru]x;z1\F0H u ^l[XY^/noNlNa03 180Wg9#0׽KȆcJ߿qWC6|ʴ`52 E/O6sn;4;F-;%;kf3kr| ɣg$ÈuAp9SU(l9) j 05qN Fdt@9'e4 6bq6rkֆ%,bNj+ 8,#IJ0 t5-2fb!\7xL `&>UPU36c6%tG$FVH0پ}3l+׾QH *f W&X6"&웩:1E#[k1nGf3Q (2fE KƙzqnѤ+곛q6fވ-m{݋/_}qTD (7NAȉpwЭ3+,1+hF}4єo<ײMR7Ձi/Wk/-Sl59/{޲xL}R[nS_H}חBCPR IDAT9|56޿nC]zM羟eRW~=k -ڰџadraa¥mw3}RW_|_(7;#`"Ԍ`)PuamP,ػ,̔Jq8DŽvMURgL>0;}[],Sya~Uf()~Aܫj%g!n-!f4'Y?/BQ-A A{%#l,lcQ6%JRsdu;4ltGhHI H:##Pfq9^ ܐӢt euvBn4 e>5(3:-tnJ;mdts}[|vn|x D YKCs.^5`e4V}PoI<ժ0gZ@[7kZOm>ͺ/o|`6o7t@qtC@HA|{O^c8A4J N=`aۜ'p(M@ 'Qڼ8:S=#\t9<I3 s LL 4<LfMs&夤Lkp B=L&LH)1DbhH WW"NI$ip^("@|xfg2|"Qf Q(7aAa `J R&BqnFq$B, fQ3EfP1<}391&xi 9T?nhWk m&J0blDfL]lWJ>Pk m~ nۛ/\O?x?% S'?VӐjH+42Y=L Z>w"ڗ_mnKNs|ww9ږu机ES2_τE 7Ξz?x޽wק?yW@[j}ϋ&!=\<_Փ̳`JR9MQ:xzzB.:2| Όzzf"( L wj X@aq7**Ud\bm1t{a(1%>Y),L, )o32k$"% ̮i̩kc%& L3dRt-%SN̬yHNVUaSԐmaUeQ#LC'e$@9Ȣd;bt 9\]rq 6tRMsj<-sMhoe0\10~s\HnQf~ a~47 H;鏪<ڼ=!!EGnmҪPڸo^OmVB֢)mrͶHqMy@ |:ymP<9楄Ph3$bY5f.h*+EyX“$gffآ5Z;UY%OU6ϖe=Ø)ij GA"Hx%vRd=ԦշM 5AvEĮFpD=2ߝP%zR$$ Pc>3NFl@%x6Gf5DZ͟xw_緪*c;=$.vz7?aFwӠ56zuZ?-s:֖ϱ_/W>Ԟض~o?_Nzߙ_H)H/U9?q>YS\]9\/7l/JYk qP im4 ֤iwi?*,> lNN'0Z8[)/q Ƀrq8 wQ('mP0!y6/p0ٝT̀jX S6ujI*N ; ٤XBԜ2!`ԥ21{-q\ n3s)~њOs?7w}uJ_Т4 %Dq+-3mσy\2ԩ"(87w۾Ͷi1O4 ZȩQ˒vjbPh6a0QwLƖFe$ <)4E\vAnH5tܾb"l0)lS =Pqnp?}7>̗zsCUBr"‹xlSPX\_-U\TvYHĆm,bdmbykM)v1Чbuyz,O]Rf1O.k#x\>rGcKlX뿼ճxŰu=ӏܚ(_oOcyO__~ˌ il&5g1ޅ/3bf> 6IHL )̪Ų))R5TG y`:QQ`ODHs s)b̷E($vId ;*Pٜt5\DkJ֥SP]͑s9f\{Ʃ  Z] ]؇iN7YaJyN5ٮ4-_2YCk,sw2Oeb?|}i/TNj8<˼,T&,'O'Q~3`?-y5=zq~mKֿq`qLlC8<49 {m.!`7Wrm'">:_Cˀ>bKk3uF W YƊڢ;bUІ mXUR$5s2+9*͆sQ3 5Jb>6E:K0*xSmE#"F2mqt2A#6P6F(J-k> ΁z=4pӀI@&71gɶ °J 3pkHno/>/^~x٘UT%]OS3^lD6(֚gq*1bmۯf*pϏߖqE u~b/ڱDdoeYu[co\_>8=~q9^7Xg&F=b}8n[K'K-AM]^E~xً/ 䎝Y"0tuD!1@Af`nyc:ڣ*320 5 d6N+[sD&,j` 844)ځo> 8eem6 0Q.))r1@M=f,@:ӑ3pmUTU0/"bSzݩ *䈊2C}KG:gIaO˜@Y7H/Yn2HKl8zbw옘 ~c~Y/?.^~y=L'V6*WW %~pm>UmgγoBG`EJ1SLk͈}ls  UP0#N Q:_G֑٘#bG A@O|V}W!I+zT+,af:D8{\iNݤۀAȇ_VLsΆ' MFIY`XT}6# 7%+fu9Ɖ$;=\90Im6[ܥ4˽4_[{ͷ/߼|aoG/:svkUd_oәն~xEl;JcśR53OLX[3]ku~ӱ1=1|Ĭ]1S=g%ښYkr~fs/z_WKhF,sD}G>vڇ_ZmgmՄ,7_ݿlz"D!;pSjd^*&:6N:s 95Il6Dar<(1s7Bb]k_$MϰZ5 ϴt.!45&BLac5Qd:ؗRjs@Тf ׮BzCq]Лj8cW=.6w0jGFTg*2;I3}\Ǟee,X應eNbq\&yOasb`2o~{?"P8,<%߮k׊9 Lnq9g~i\/u;Yʔ ׹m2J9m>,f`pֈE)e4F126[{60NMf 쑢t+jCVqDlh9FH#s `Pv g{d VB} UDe\<9d>]Dϱ3#f¡JA*%!iD&p8K0spxb"ViX3ܽm @?ҕ/ +i`&n7wŗ_jshW[:q;P(.n?utk{W~S*=7mW3q12;Vt'n֑>H8,v [?U_9>G*2k>Տ/Owy~ q^?磝q}8󼾾O|V>r^n:XaĂ4$qPn/Tvë> L T6 ~yjFr~ӝL 6].Jq6F"bL~5`yЮv1c׀ʖAbmmu:uctcE;;LA$hM$pvWh# IƙX3:pf6,ސ瀳P)}U3PF4:'&'1g*zn)rI9p(#30[QF0V"w]ԮS jʏ:D|~|x}\21'bϝϹ,cv߻'uNǗXiLjǜϩ%B޿S,<8w]Bߏ3w]^m]}QJ:pEAS?{=ׇW_|~_?|I2D#θcGG K &n`c.-UD$O>̈CH/>LsE$R,̪A8SRM^d:cv15'2儎1 1R $nQY w8Wlh ;#d+Bp,J#2FI3JQ )Gi?ֲS4yC\i߅7iIkx% 7'ˌC؝8{:,w-zy2#nnf{Hdx泡̑fj)6yQN,'Ohy,6K>%G9l3m>v,+7xM*/dG3m>.~6{Ől0DL̡[`],bF7. VG:NmgB):tzTzC A6RϺ&Xf!l@<ўS{}.ϵw:O?Q>wv9^w|N⪷~[g#Z+XM-D1>]@Ţ13!o;_|o9| ̥NrN*ذOXfL9׼"lٽ& (I:$82LE4<ǟ 0CT 0kONc& = ~'Rmէ?6A1hg<SqA5ZRm-Ìmp75;್,یjʋwHo* +hl3* 鶢V.Xca6N[ű6MuܚR6ƌ0,PEhHocoqW4a<̅b%&brpCSQ"-k e@Klal53:\IWmhY¸Sq54Tmhb?OQlz -.Tq:/>Ë^KF*xCYZabg/٧i_M j[?xumc/c=hΖQ='9Ow|,&\;39;\s?u?VT{ϛHf*Gݟ'< _>7|RMbݑWvzh( IDAT^Y8lDP 9003c,[ Č\k!8foQ5=s>-H.qDZ{z2 +bP+ waPѮ"MؐQ+Ƙ̐Ar:˨dN. ߦZ+e{%AݘzAL(Awvј@vۀ,{"=s.;RiG f,sةM1{&=_{7M{eӆ7aggq&ybdzO]'㞿Ŀ/4@ur}}çǒfxDyfcٯmLf{tݖf g{`\pmI W5<`=~k=6;i 0_t7jcYifg +lKcؤZtiҰJrL9wӶj:@~"Ɋ jsh(+Xi$IJgfR܆-4 5Ҁ 'VNhjp(nxX7C-*XgIiҚgNjΖ %grza[<̍DM ]}uuw7<|o^ztJ4f}LS^Unu{ Hڷ3a1ekx r-Ih\lg`L-|Jgk__i}vK+SlJPNBnpVjejU|mZӖi*+gpTԑdpKXf4pqMQ"Fٽge.s :Uz&+󆗶w2lZ Չe~`c,-e^櫫_)yyau?22?(?|nYs< g]ύ!Bb,o0gۑcƹx23e>sVO6aW Cs7oזuÜm>,a* < S^3s7(D P-M[ŕm%Lͪl%T"RF*JAw.f5@*`e!ńuF4{\ BGԙ!!@%-Rp 8Xa:/D M[U87X4"49ܹ8KgLibn_,a{z3q|9C|* M^2΀hh^muw՛aI7MpOa=fT8;l#᨞x]z8ly[XO)Qw*4΍v-րsP8 1c'3Rx%XΓ(Oѯ\HGCsǷjFCD˜ڮHn翿/_ݿUtv bLqu9coFMʌfvm.6 ĨX﫥;xL4V8b,<_}O]?q[3XQ=Qw>gc8Ӿ1:ca1g~>%n:^\mlGus~'i84B$߳φy^yէ7\dk.ˊ] S -f8 f _vU\= 2f<!`NYf j"At-Ph@6I|  5'({XbE:Ukl Ee,RPĭ ,:xj^Sg-C0T@$I E "N Agx d- @=vٴ2;RuDa斝nwBv@Z6]8x+๱̗NevTX{ggnُ̈☯nٞfꚧĿfe|ev;[/FF+?Nk>x9y~ʅbqCRSY< I6;p96GYϲ=k˖lz⊄^mb@ZGo.̅\T%躪GΜ8S6Hkć́L1 c؍9pNTN4uv Eesm"aUakBL~0(-_Ap|w[o/_޽݋&i#` 36r>_,Hƒpkpf'%ZWm޳4u>jz,=%'ֿ>sO6ӮϏh=?~~Gwʼo>vi]g||Y޴/]?F-f^}:;"$w>}77/ ,gO`i sFoI٧MW JW'`#`0y7Q-~ Z5pi W'6'p2 0,FKrA@Xn?[4!SU0H )4jetk(.j"Q:ӈcm2n1".4j]כRb_PwŮ@ȵe85%H=nم*?8vۈ F@eq8 MYfa2jn{ݓq}K1Y_0[_EZ'ʚeB1m=ÏYq>kI//,Wtb7GoU~W_Ʊ~q+VZ|u ~?ۏ3_+~ Kl6x8=6Í%N -oqmwq;caQpr pu;xoC:]QRz@a;4wDRDU YkRu RT8˼W BQl/R:F麍a:ރ;u P:"*[*0(VQ)XLUQʡ1UYR ҁbt"jjP3Qg37S LIS4u@ZdV NDi"Z8k &;5s!8"5ɐ@ PkFQO8|'a@ v b?l:D C^3B5ls>t#M0?hvf4mAxUq Mp A:*m7v|qyYvl:lɛ4mD9]fۧ:/`<$^n* & 0d8٭C֟wS{}o;G'Xˏv~,O''jzcfFlzExrv^͖mlFnC QM#_N!E,CF$uB 30` ǜtN5{ŧfX朘\с 0K41&C5{>7U`OjhiT: hLU0G~f"oTPY+8[HPE=*j>B;r ZRH%T0NWS :VulitwlЅw'2;;LDǤ2_\S 1熟?,W}Dit>源w!3qXq9ݴs{wמ%/#}&@=lw6לeYQ Y5Wwnڶ/\a{QzqFOq8ʎ T kwT ,y))Mnڵit 惫#hpl2Q2WFzp8>т63`4-|2.FD)D&p4R>FasfD!iM2bU2sw&1yΧ0=IK i \kĴOvR]dSvw.»zswWo>ŗ_޽?Coπ@0gI0)pZm; #~']+Aw&]WbG[/3y=r:‚m:ޓ?|[{|~>]wyeo||Y?(Wl}y`{idvN3Ӏ_ًKqpd 0GO#wC \/ SI ԙRSi T '3H6f,Fb{mf.3Y4 P} |e H{b<#&C[$nw橪%*chsxU'~==p'NOg1ϰL|nJ7tFjs J KتQX3hN/gn1[y*DM0^֞%c8STlXzIZeJ,0s\懇KkI[TC1ϱ̧ʄ'0ON?[vc/iN{1?O͟ pqvwE6/mls_9r6f|c//ou;]bqvy^Xw.U#;^@B_Aq7C)j,e4&(̓ Vt`@vQT T q|ƀvDv~WTwFUL4!TKB-RD uPФ@ hdȅ饳ZGTDܑm5C Ne e(qj35h)Z] F.`ngY(-gE8cQ㶓VjxcLXKtX]]hz &Hj8Wq6O喜nlLFIIhМXg *:Ygn?PV6CI0~S?x+:n߄ 4Js l.NvoTl1áJȥ9K9=ۺqOmK~sti)g+w ͈xW˓J]})FG?pxy{3 ޷t_%XFٖ1+ ww Qq?hC6=`6@gө9&Ťhf#s_ZԙQz7YhY,fQ5R+9Y|pnۇ;efAHTzVϤ^g. V}(6@$n([B F"JBRkP * h($BCA3j} HlZ-Uܫ:j#8>@c3O8ewn,3e.t +ۇ%cS@{#8\6bC$@@Gop*T2'`Ny =xev|yyXyOiR,f)b`|V;Ey~bMA5geS8fΏսwgC7D7DN9U-ebL JMr6دgYd*մK9P#EG9FN1ryb#F~cct^.?9깞i)}L0-%k5t*NcJ= #sІLEʼnՕHmEYi)ޑpe AͅVPYgo$Y6b9/n-z#`scʂCl!Uȭ0sV~28ss) 1XVB%3A@XwKF^sF_8 3 17s9<;/FmfsOƢxݾ/~njK4Eߘ0q稜,c{q_uxywoo=W;,6f-<$dK= ø)+vYse>o5/}k!T=T~[WY 0cbHeE=땆.H&Z03g E%`Fxb eUH,>O kdjD`JBؔY DL24!sc*U4id`kD`&A2hO7WтVXgQD(?,Xl H`FHݸm7FF\301ayxAd̴8n639 L3badImsڅ=qPk^djd}XqH\̕(R[mg6d_Tg=F @GbQmFj-ab_]A8|G=<( 9t6a.?~&,@ɻ2T&ɦ_2`09y}6k2(2ӌ# Ls`_K4#Ud3\M9ۼr싮[]{lq~ccC8fr8L/`i]Qq̱ZK_7m!4m> R 1 tS,jmU+k:UP+-`a53Cj3%i ,H1:c 1 5r9S5&]X7,P0'TR,)גqdY_UWF "d IfLK,S +ӽ/Ic, -qAo{xgP$M =9ƑB+\b=PW2>cѰ9L}Kq@NL o}qŋ~p?QM99(tV ?9רYѢ6[pg]"{KJqYeNQ덨BD-Onщ`^]8Z 1~[?~y=xG_w8{}gc{f۱7v.v.?`^\v x^^~?_ݿ× 0'Yp3ˎ4m֟ydZl$_}mt}wL2rݳӿD?Ld 0GD]Ay ˜mQfZ+7Vbf@M8Dhfgݽ]qr,U≮e?j843RE Y:{#}3ҩ+g^h@T䖍*08`.fxxdF3|l&r_3}ZwwR偯0Syˌw`,T2Oa}?"tǞ? _} >Z?O-~l86x+?ϰ R1YgZ㟮/Q/_ vp}z!`檷83 9zN:E  8}b*݁ XD: c7b}Zî۰E"Vh_0T:PEF^gR cm )@RG-@E~qn8 ZUT*BvAO`"P!PݖUJ%fa5UřjV5d#+L(" B"EAg@iP8?+UAVN3ͰHQ&-4XuS* 4E%Šc9;5~6n8j%d1QUFd<05V<>Ќ8@(tDMZ@@XYgXzE0yuG@}NcYkڍRNҼ&ZB}[m-خhT }a;b ZO_cّ Ξ ]-["(EEv,}r~qYBR"۳$:_kԯuYS=+,~݇^g7۝O\\nڰu)JWeNWř=|'aan>7ww&CN}e0g6f$ZZlDZn 'v9½/lpٞUZ}>+lf5ۣwb-H2ȣYV9'lsWig8E5"^H.TD圸@'`"b4#cմH5BH6&2E ͌q#E)CܛbJPdTEJ#vUX9pТMUbK!zZ-Q`#pX۶}w| e}=2~[;IPٳn D狋}/Xa?0/3t%jkrN, l5A=ww\]mPJ)}3< `6J+t0%q>R". ~'. `*]tAƮA<;(a^z^GQ*MPlJ,"TR`ՠCUlW)T+RD "bZ!UŊBlTQA4xJzZ-TQnHP:Q*!>x&BqAV⃙@ :UBuR$\UD ݮ1Hdʂ:jHvc*́p{`X )i. f3?l6+(d̬~l} +)p09vC0} $iǘg s2z߽oo{y*;d0CD()??9{X"orv"T',"t]%܀} JҔ(=^V!ƛDգeYesO- > J4]fYg@H,> K茈OX[ ?b1u:͒YOjfC2{%w/>OO>g]y,':fqFKcM#nˇQ|ۮ8H]f+Cw/Ö NJ9+;lW*[gRʖ#ztMfwR(#tVշSMnBxұUaثUGztՂ=LG:"L׊>7df(RAKMAD ({EpJu'e9J9I  Kg! 1 U AJ8Lu2P؃@7$aӹG(9"GG17Q.  U4D0@Œ0PN3Ҷ4G.4ԝ5L]W:/mMMaܢ`!/KOW ! ЍDڶKh36@HbޖEyNIۻܕg}.bOdp4şRi=m(("ս (dj, 5-ݏ3Ͽ[~6Θ)Yj Ol(Ox7_^(>)۟Iub( e"ZWJa`Q(c|;(ք(30yW4gK#98,KYST+޸pef(j K~ܓNA̳NK18LSiʪf1FN UnY]q )`Ah <օΦpP9: vQ/*Ph6 :#A( Lb u}['na[FhV1uhFhY-eWT@{\ .vJ@qLq@@틫ew[8{=6e`(.s2s Z:ݲ+(咇zՎ -QƞccL|v6e"EjeH-2*ⷎZ)ԾrOݲ)'.#׿6n/OLsWW_^[߅mN{dY#KyĽo *)%Q|nM{P:DXȒ,d 1PUs& El>YsTSy{mi' mRD0[Nq]}?xz;@˄T{줰ZKm8ĦiWHA*h0q"Inl<~\ӃN06).D bKJE%B搗lzv'Lt ӷRUh%k/#nl^uzP+YQ9qyq@ BAQU(\&$:'T? P|B(v0,ELz*O0سv!FPVs:RylG( ,]]H=A͠zYY {(z]h *(utt aڡ0%N.XZwT%.Yfy\<2ùtyTOxc=`2G˲1f/s6ћX/`|4_O\̿{gGw[ͷL2B~ @~6]|ѻ/&O_᫯~>ddQ6/{y> +nٵqS}A<@?xDIp8PvKG)2imug <({2v[;D.2nm>C^p,h';Db} ųj5+P=DiZuE$jS&"®M JY v1kk+'d{FkÀ:{|&Nv j;4L:9hx!)vQD-E0`sL4,F/1 *ųRl.F ghk'YU]d4%YD>1c $P~5 YVlX[; Pd!YWS l[CF6vy0; aRm2G'MFOvЈ= 4yv[_'7nbegҮfXp~ʰӗ˟2j6O^6{.ESz@7V>`fr'x k2–W (S::֩lx=Ћ}>2-/c\$>鲒{ ^.^,84kI4vyB,w W`cU=/Qnɘ`e>2O-GcK2{[[da`? -`2|ɿɊ~<-{^f t 8o+ ]g_>7S/^[28{ m1ΐL1XLZvLQrd eg_g"<NrRuǩPfDU*R޿ORg̲;vw<19L]:RLIjfMU+4LթMwa7:2 \}8 =FȞ7xZ\JFUrf8 k*Sf<Lc$GQpk#ނg ؑLai&B,r)g`оK?bd$לplw|myq{[_&ɜuLU;B%ޥ5zew; <ݮܤ"?.sO){^i_g0jfizٛmz{|<ҫF\;\:ph/Y'(%X.Ha,/2,U":|yr3"[0'LIxk;dJÃvkA<Kͳhܤ3 Tpj ->r7l֥*fZfu iܥ!4V/dl Ք&Z 6.iPȪV:,3̄ X7˥uR*h"%fTu[3@T&(wL) 6@( )&Y[m(s$P58o&(Ӓy#a"Ҧ쳄k+< W9_4=xϗ=yќc*waim|͙e^%]_.O4?C>xlz~{[^$g`}tY }$6믧e;@,VlLk]ΟvnjYԬNPTpsxg+*8<,d9}Ne}ůĻqu^6Mw3]ېe{\g8}͚Zm,eG/+$ˌYi7puURj:=c" OY,իK |{~|YÓyJ0,9,_F4.kolhN+?^sb3zls5xWգ3jQ_G?zXeӾA_ gfZ6iM{2A<׏y1k;rSf]&L z;Ikn_q& CYTnW4peGWҡD3Dcnu&R#AhҨJ- KwR'O,VDk]jD ХPTQU+oԩLm)QD\HD]8@Kq̉Juj)E=,cuPyiWbXZdr1ªSax )rGH$BdnT&1ʫ%δ:7PJ&@!Sd 0⓱SJղ%T9M, ԑI,7 d˱sEݏVZ<䪊FAO5$ lWyS~á=|W_>;U>ϸt.8nOr9_'j".[ǵ-f=xq}3~Y/ϟ՗~D,orL\֖t,9~qkl4c|5-qI${9YE@BꬫXg c Hαf7qJH8 !;28 2q8bűr{gda =l j2+AN^ӪxpEKx~f%Af_LqdKW BlCQ11xlu\z,-Q^J ;HQHkyug^+}R-ZjdT-ԙDB n f8O`3blR'MΘ<3tJZ̋,skGB.V1VZC\Er49=T'hY^Y 2 R}Dpnnαi-m6;㙯̿/L 3ۇ˵ÿ:7b[IG__}>)Vxd/~߿ay>꨽k0Njq/rúx+m?ŋ;ô{Z!jM{<={j˴җ1Q+y8!#FT}٘WΜhx+Ϙ] OL?1#|,u'АI)l @U ɑg0Lag <#;:(zMB%D3Q!~$M[D g N̊74t`C@HR(ZmpݞVZĕyO-jnpEBg UBb)/!y ROansOj $jj5Q`VFd$ ˏG &  }aAza9\YEa"2쿸\~'k+B`e$qK62 TF@^zy8[پ 9p[s,)N=?G}g?~ GAKE\ D~_?~xmsk7CzWk}G9lx|}Ο~᣿wŗOrڸ]C;10K8/`9Ļ/9R 0 "K -_)fn@0o=stYکT,u` KQA+ S.˦`E./h'_ ?]ΆS5@oqVH ˱,e ycvY[tnD+Y6NܾLYv)RveN;v|݇w7,쯿EʲednNkx!qśdٿ-Y-վm╹~?m3[wL/o~tWxr7k\#1E(!IZ?[k#"CՋ؟!t3i*듥TjZ3@?RH4镍+[P1YGR&5+́R]=0^Jx^L1Ҭ^+d<֚{E@s Y]7.3|p&#x BhS p0KI P8{*w h7/ F9%lMA$ġ0àQI!QHN`ψ'0i|GzFڐƽJar e`X187Yhp懏?~?~O}OGG\~i`Q_/K`\w4>ʴ\\}zk//xzSswc<O>@W_͟~ӏp/<u%}?ƒZεdcwUb1 =n#}:lau l}ep#ѝ9憒mk@s59ƿU=~~ c%r8{HEW>04 ToE_fkQڌ[НۚFyvQ˳dFӝ34=5I:`Y(ZĈxqzw eۦw80b$ #IHic@ʳ㡙~PE eDJڃ)D.;p C;#Xy^#2y:lͲl ewWboD"Oe9}OA2NRWd(=2/vm6|?]ڔ:{\-gmd?2L?Ygv3j}v(&,2&K .n!K[k2m<iIGY;rJUd"444^P]vJ1:a@Iъ.uڱG6Eʴ[EVjdT/M#ATجI);S( RfZ uSxj&/ҵܺ^ ?^cl\aHٳ,`=0Bhg3s=WKQ<D#iYɊ#{F3RtHaL #Eũh eۡ~n# \ Yt:j;bJ)vaSj C>D\Ny4 , N rF2_HlLHаZ l<%di*IBƽ^7@FqyFB^θmm7󫻻/wna 3@K^?mel?>K/c1xקr?ir.?\=~>᾽:G<xhxIrR#AVHГܬLgRSj*C,Rҗt.`ٷ u 9dRfX?a$^BY7gǫs/_'ln~ćl]ʴmiOgO2m =yPkkeG/ PڎeQT #ݫ50?Q;$_y, g"gQ,8&'ǧcXq/Y~yy'{ҟޛ2}Ot_:~՗Ͼ7??uWOi|&u?M\cu(#!Ow'fhN'4m>& 3"A2˖Zqa,8f4/ır,Z^! }{91x -Qn{9GKݺE]D!y40wh^fRi֠EL[,،{.mP@s\wېrw9\kdpIU.3iq33-N%z! (<9BTI48`Ndژ6 8cfP އ64SY6pJG Xf=A}:r2Oglke/+\ye|~=gMyofm2̞}&>+y?oOne_~KP}.MpyW~>j7y:mi`ڽ|~uzAް-u 7<; ٴkY$Yd'2cg*L{e)gX߉4ڏgzo(dB]'iK5aAz! ŅDMC/~ P+0mP0vRH+Zd]:YoIT+a:ب)SB5ep*jֻj)WVƒ!=krZ+aMD'4eLz7e ` fht($ 1cSI:k8F{ D[;yO^j#zU`9 tӬH*[Y/YJWb\~~Q扢`T2Xu*`WӍIfMf,IxAy|(V@,q۾j'[֕U_’m 9.c{|w )/Myb0y4W).m3l,xr}1v+翾W͟L_2'?}>C{p:8MV;2y8xEygq,Pր9B`fQœ uG掎B%[Rx$[SOf RY]q [RQf@qCɬZ(C3gg+Tm#X#Jۏ̙Јc.RTYni*ΖݑٲyȲ'tB='i|-#vv\Kذ̗lpwǥ,sy^rv0'e v{~ZVZ]O?` IDAT}# e߼/4NfWRXj7_KPy7?\)A&|~fl߷MwKQΛ;Y^Kw֎ⵛ\R[)%NVoB xB8$'*| m82޺$ лHYpPJe&yƴ8{Mg%`"E+gk2[kU+[?I[/CejMTD3bUXuǽaRCo.3,*j lŠp,2LrZKň֛F%7gCQnPTtbO=7 *(&0Tciut ;(:.VMTÆ NDY*\ =L=1q/x/xڂT0{xNQ^  hӿGЉ͗B^́ 0J`i/x6r UÙ]{W#;2S]y(:ԿC|w WM6mûxҷn|~?xlw8F&³zKI\:$s!^$#$ӛ"Bie@2%,(紗]p nă9umOgMp ; Y2oB %@P2oJ23D_C❞uԊU ŒMc巘gXo]#QS&ȶaC'sQ͸V9J)ĢEh`CVko /E\Az2A뎆8[Jtv ٳ<9M)$7!*8Q J3*J3tx"zb7iWl>R*` 8{fb&E+:&u[Zoo eV+:L@YhAZ/HƮNfJm"ڨV!rH)1 TkGU0T)Db0&Z Y̙MBaQd 0g?+fsXv UtTTHhԸa+26Aw:v)v3rL n=T=߇$ ǖ D"y^o >xj[s*,[a8.t NU> wm^aS@Q+-Xh8kqHfD\vQ^]qvzɋ @p{S_\@͋.mWrny_qIz[{;{c~&H?v,[p6@ c7bCoo2#x&'f31x=c׾Uϐ(g9)W X,%X5!pHF`3 1i$fNT.0ϯBD@ 1D5+̾7 b)H0=#C3`|`v~/O CU &`?+ ff8`&0oH5]H2.SAm غ3Tvh(t~Bzu64h+0Z"M fVv#gЙHY;3L3]yٲ Jĭ8=Bⴰ Kّl{&\Ꞙ_˼ K8*Uvo`_?lC"?pWi} Yd߻}Asޓm VlfX36 ɴ# $~x67wE}c?6諕Zx@Q3:g;r+v5V\n9ʎL `ƽ($LJY5plzq_n0_4߰\.e4g9e$xlw ]Ono+#x,%1t>yt:5q& @9ԸVy7\1Xȯ)uAd* ~ut93\gYaՀIIկD XUQͫD]8ZK96$`\TmWC<qT01^A2oxEF%@S-`6+AҀ'T%r=Z;Uz `t[0}T5Pwlu4HB1XTVLmegX5`.%l%>{>X 5zf`74B=bBmW-ACd8_&hER @΢ViJL&iUYBUa>K):Ea PxO#PaCJD̿!gc,9@j%ٔLGz :u(PXfE)f(drs狍꤇xV hâü4/M)95!=$2͍="`Hr逜H^_0d*5R?t6zHND $^o7qa)=W<1h?V 7 eNK=_ t`=Rlvx@}8 wr[NP1֗m%߅uK޼yC`粧xt6+\kZ2t;v 8 suA#5HN7؆9Pբ. ]z [k,@98GL36U.c$0gIYm_Ց/e}y@̕5!D]~[%ɱEHq>f$E.ؒ~~X$tf,Y0T30+I w3#/_fZ48 hZ Ez{U)K[Ѕ} )ݙNgl1♁YI .3ÖQ/uR ̂JJI`yvYv+f|VҥS$uIviZwlnj#q#a8%p0;ˌ`v;v-Rb HYZI`oS' zsŨ7--_ h~OlU__CoJ v-y ?O6]V ۼ?'.eԩ4b?'p6,\AJ%GI<6⛽Mcuv1S?I=)~b+'^Ph#zC vl(]Yt=NR㛋L6A*D햪 j3LF6vsP'D$EybZҵhL&^ٴ4-("J)JX-T5br.@1m$p>ԁog3 R@[禐J;z`eT)@))Xd:a^ %v$f+R¬PF$x_S&<[؅hI, KQ+sŪ[DYY۰C'`BK@y=-"Xu9EBV`9>a%a.u=oy8'\ 0+1Lp09؈Q--r7qz# !jFs_'Fny`,\+ Y 1w.t@R)*݋t)jB;iX0h*¨ R{I99xPl訶)cJXڞ8n{MbtqgŞgnUjqy*3Ap98('Bl2Pˁ!>WqՈ <7Od٧Ֆ9MxSY6 ˼e'`^KE}_0oۛ1[ү0߽}gK/o~|v?e/ۧz 96> ~o}iwew/pԙtl <wl̷{N˴g8<UnnǦ2'vD`FA7#kYP:Y p>2C1^dڳ<s^Xuy5U 0Ԧ[Nq3L'uXk#*ؤZk]k- ɮM3 G8еX^uZJqH.4E.'˵'}} T N4++&E#9 &ihSBqF 2_=hy漁8rN5' `1m',6z9YWn.׺DVmXd?ʟU8}d&Bʥej}KV96-(t?; 6[ $S0{^L(&:kᠷў1=eS`w65~վ[FWOO}^]qM>ڛ-xWt)V xh\sۘ\8/.A+5AЎ3| g<@y#_˼}#ꑁm7@9_S>0+ yȴ-NbM29YN f:}x[Yg6Gp "fq|^@A'3A2Y0b(rR>@͈_`8# tR ٻ _۽0wk#Y*\ 1bo4P3S*j UwݺA٨ wv,+#bd6mTxqy̳=1:4*vP\7W -aV~>DF:sȲ{,,q_#1= @W8]kY10<ϐew~+9e0x_OamOYG/^[m~+7{h:ٮ[?|my5ŋ補ǫ۹wjb7~ _6Gf_ۍ?zLrڏmYe/Xj7gf^zGK1fQ<~^d3JLԨ ᥨ^\"4捺I+,7p7V p,7U5AX!Ιcm24~ "л.њAU٢sG7 -QC$T_v0:.3z/j9PB{ɬ-IW ^az#`Awu- /B|*7GY#=]7.ѯ0TY֢,1%k#M x@5:~Ć*2 ;7ۺLyGQ #2L3-oazPUHb~$48?v_qx fncAr\! f+1!ƳxE_/`.gu,yH>7qOƕk.y/dw?lTľێuٷهo]~mt7a\x#)_?T$}lLd<3HKLl&phRHx u<1= k*3ERY:DYFbAk+x(*1} ˢb2Уkftmp .'5eCc̀~4-RgW^J!Pw /0#Ũ5` +jRkWow Y5uG!XS5s;^Հ濥hQ!eh3,,;3$`usbTר|F&*eb$nÕfuq왲Woc} |ϖ0_k̫5/OlEZu4{2_~^]Y_h{4 3ꞮpOP]KvyCwe2޻-Ir$izGd LȒ+2͋%e}r BRR(N]U^GDV^S*3'sheka˝p-Ȁ#|e8hb)GG=HՁu$pn! ej D򳸟i:P!{ kcyVp-P+T+"qx &gΪdes+8{ja سEMI&8 Aդi 焉(DoepoCO+ٓ iFG{7*d)ṯ՚*Z(BUR)`n[n %!0dƮ1F;w'C|ЇCoqsz JVKh_/rCs@POE,$C8(e>Ⱦ$T,< 9Cke6shLX|N;Xoi(9 (|wwز4z@X/"A c& |_t%#;`& wE,:H,}ĺ外vK L! %`'l&.^0췺5vkÖ8ZR\ghL>7V J-]}bmD虲}nO0Ñü10[6eSO[6lc;}xsP_0|/_~|i_)>צ/OL {PnLWf%)@q=8 I IDATce[LΡ2{|rτSdQ,eSd`:`8L3g=!n +K$s@- f ,^uHB FϜ =VG|EI 7(r Zka4mTZl0qշATLd3 80hkCTR`c' b-+80isR1I ^2Ymjp(@ v"ċ='alalRO3}c{L xfȲ,kqV0 iM}nއi3UO*+ <ёC  Dͭ؁t= D 8Iey瀽b5O'̗~>`Dǔ0v}~r^p kCBr-_dr}v 6|6=Xn.(kR@WSu[I8,4 Xe8nKvq:d" ~IR%~%mKn?}x"IW(-l(x.B j$b{9k$7`6 +Aa jNI +0(U*30[` BTCa[` Cҁ --X r ;Rܮ3-1aR1}T㚇r˖$Zۏ%Ffv}:ȑe(90 ԶpQe)N?~0%?3߅׿OW旧?dQ/g馽g9;q%ޮ y @2˒榝n=өxd+MPa3gv`ѹ _(/nPsΨ^l%@gn.*,ŌXmhL 0CΤRj=o`QW9׌VpW)lƠD0J8Y$vIw96"]&WZ[cbX*5)LlK|sv=겝~37{8@FH&HdX:˶+$砇4{Xb aзd: J\㜹*pl|qi7@(}A Bp#OgIj1S}|7nOc3~g y9Uk_ϝgE@^k%b[D9Z=eP#H|MUƅl&}uᰜBя  j² ni|wk)ec{5b8w8亗L@'UFR. {boL0gϐ̸5.),)},JK`_}$0+"Ŧ3//̦k=Z=H ;K{!a ٱiMwb|shGLpfkn|,92*ԹimMoї1>ب泝?\VLקs\>s6$o oM (m}wUҌ~KZ.{yŊ 8X rid-18$b ]ԍ䊿Bp86;`f ` f0 nڷMmq2Mxw+, Kpn7:2fyW lq^fnY0F<8my8cWpv(X"e6pFxv/glVb^QE\zLFHs$7B8ФTriLBԺ$5P6q0fHN=3 W܎ȱ,5-je@ Z. l@ܻlc粝Н.hq+DZEl J1@)q<7Ed|1+|^Rnu-qNxu6 ^nӌxӦ"-֪ Lih텨AԚbb}UQ&TM$E+= .Mu?쿶|\:_M)翱^_r"_d% OsP3Pn1ǭr!㪪Qlg>D`9v!oS՚I\>}Q=,}+6LˁI[-pG@\x%ş}Eҙbp7$c0-0V`+0c0)>ـ OY++*$pҷ6GB f $0ۉBz\s)rTsϔf@Kac>Oq`>_ _C؄e9F>y 2φ]gz\ٝ[k칭 o.:mˮ_}}z<?!˴~F|͕/Ei8?ui]4epN+ߴ!wHp;x gݴǗL$88/=8τ%: Γ'Vpr=8rMVoFbrpΌ Dxqp+*P3 f*j=TѡL-8ǫڭI${f24];G#i\qa8|#=[$±V!+1≓mw eRDpS]J,*8vc.{U"{ؚdy\ ,KKg{h܁]h77mYݤB*c@.!E^oE~7nki+7dz:W)9tx‰ίsr{n7Su?BarݽwD??W y =(.U9C~MώY^wm/琰,"D+Pɔ-$VWl]:^.}/m>ܭbEKe uvĤMݠ&xCրTj()~Cmճcg 샪'`Vz̒ Y.s~$0Ӏ ѫh0g^>%Y,@eY;n d* -Rc^ٕeUwˮ0/K1\"فGhۅ*.>yoy:UE:*DZr nn閽6}g˞6vNni90(ߪ2P`G?Olg ?> 07mx_ybe;%Z8ݲs2FF{X3j!9ڏi<߁SdRKNzpVup1M1K$bv+ã6㛗 {!JP98j0 J'8/gVQ0Ugqph sܚ 3[8g(%|Ԟ+t m}07IӭL c.ޙ; :B4$2D2U=1 %"<×]xH^]]UKq/$`ځjSlhcGqm%?=r.a7|+s~tmW[!_9Cpv.6=5W cu?slJdkȐ|7,]߶@S=mGA~ξ'aL(ۺ.%*ɀhH`99$8 CgUb:܎F1K\ec u!<5xQv$d+:%JvH8PZ&}wUfV8N` 03yE`^Z)V| ـYvLYu "Dȩt`v{tƂ6%Z>PufRRU Vr ƈcH ݎ+0q-[6bSe8f< xyp ͏ wӱ%zK5i\asu//ܲS/2Yia_>冫*_O@3Ϳ=_t͏d>7k/i_xp7of>Um]|3˳]|ؑfnneց(1y@x³өuf ; J9O ]0-K1T+,:h˙Z7ꥧ~z9D)*]Q+Pe/4r!YX{,(8gLERfR E=83Jql_-4jR҇P#%gl#ى:nqW6 a >P׬ܡB Ґ fypnf0pe[gsҺifzml~ ʹhΕ ]` LۍN$nc lU4 0u ]RkmK#xI ^+x+G\qѠz |ma y}g}ٸ}yƿx{.aw. (Pݵg?w/?;8n+:[3 '{qBMgc+{&(Kud ѯy?<4XK< )~'3Wl WY]E(W.Lď [^DKECb5>Z&Ú;zWM{`Ϥ]If /jl=f{aR 0U@G|慦 En:҅ GwϞ[Ytq⹳ 7x>Wqd]_3;8fa Á[vɞ1Oiej'7R|V\env%w)nOɔ>xtIe/?u O㢟3ޥ/)Sqݴsͯ›OQ86n޼ MѽM x7OkBa8!9{nkF+8*siOí}\"10>J;G‹% 3!{F5g:8LpVa.-%Ҽha'=8h&B7PX^jg^Z2Î8atW0!HY\񆏤3 7^@襪lY<Ґ4&Ħ&X^77!ů ]TgippjJH5]-l4<ݾe  ϒZ1;8 YVZ×Zv}9l5\"Y)[D˺J*礵уtHP8mOm6+]rFe^vlfw;}@EU ٝsbgv# .m Yq̣e]c"!=mvcs˞8q-W9mPo^-n~??ٺvyo0`~|?~ I7¹|>|)<|SPhMp~ϸvŷ'۫=8glw76hl͓q ˠ/;sKOo6nڇ`F/0;L=c0g6 ppuf-DZ)΅f8wau[gΞU5ܡB=w(]YUS=T1+,lD+}ɨTHfh0j T jؔ>gV7J`Q׳*7Hȵϖj1RU [Hlnҋó<¡T0uh&o03x gANm6lKd&Qo;:,ҁڌ5o{Ya U8HYf,| `>ii(I Ř[P^]s@U@׶n+߆a3 槷o3"_-oϗPXfB]u[gӂ>Vy=8@_`}JQS㉭*=sӞBl@9OmxF(w)nCE:4۞vq \!381=f\OW:A ZvBik(ފ龋^4M3^)/?_ig7O ̿? 4i ?S/ߧ6O7ݼg{7D|-e0Ϧ}=硐j36nx6nwp%8g|sic:0]ie-_g8XKhcQ/3pf{pJfl_%8"XkEp8{fmR38׈+t3K׈b L*RƺQ~813k5pMQg20lҍѿ-_ *WU]i*,jf,h CStxk47VqXD!6nޱ{Z9sz³U6cDwdoRO 6N`A6 В 뀼>Lk%< 8y]ۮ|s}O䉨*We=?+z.U=~\m:=]׫>^ E>h' [z}z+=[@FE-c&37bSČܫC;h̘#@GmKXNhK, 1H4q@Xe6WrC2[[|PqC ~.퍨\^ZCl%o5 IDAT|Èbߒ=52[+'UT:TZ–ץq`i FvTe ' %52H&ܮO N%Tje#֦)ܲ3$g6ln.[1!JKuٲ=l٩0hWy?y 7>+u`ʗT5k/cSn}|>/~Wx3wl~%ߞ͛o} Y<<|'tؖq\ף|10C[faY;/@mӛ' uqCV<7RKԪRY  VU0ap`s[DuoV*7)CYa0 (ed2 GLQ P&,䀑&VT̨,2B92ʈIf*ZeK5ʈyeW, "Xp⚲ 7(@xЗA&TY2)T7{x3Qe lh"2p<(ⶈ*ET vЃAQZ gk >7u@S)nA4NKTVd{ѵ]"B@` A~‹hgd$cؑ i۷7BL.)˶ wf[nF|KBDQ.mFT,m e}4U b@w:،9;I .ɷO:e7-/)ۮmmҵfey-0 z>"7G=ǝYr+*¼^}-o\\xoAmX!yl㱭nŝ!qc :F<9:>|ߊrzev;*=Av}YOP6SAHϒ@{"%E N_y/`_~ ^=Ue~O?o^À~ <Q ^}&=498~j[e޽x|ewK <;2[>q>Ѓ2[pƄg[pDrCQlhUxڃ`XFXE L$@*0xG:2*Ed-&(P(X(@H)0* h=I⨡EG,uq8p^Np PpVa:RBڂ 3 "k%KL)3AHE׃>B8#+8 1jgjX( АE&F$}j@6.bQ)"=6 *< Jq @Hح@Qo|x8P~S/*}n☟|'`= ^}g\̀CsnϦ! $8sp[h fiŶey([Hy/t7˷R/%\εQ 6Vg>ǻ^p΄Eb`CɪXp4ZD΀a#ȲՄQ*PiO[.sM8R TH"C.0)%UgJRAPMh`ecpvY"N*&@U$tW@2UR3H,$|TɄWEC {RE]SyVPgn[S< lA/J .UA®Mx4v;xn˄gınj݈&7E?6h;(& ͰPA)ȱ~Ds/K4r҃oCUhY6VQ{OȽ{J0-W)YLgi>osggg;pL]v~qkǺrU'>}o,ppNMF@$1ңpfxA'}H璪r/OwA,9NXv7qN-~90F=yp-JA(Q 1N'>*tx KFy&*>jkP nTB0PwxvUx:0աQDep%@j pJQ.+K0(B,b؜Kt`N207`-+P#%X1Ҁߵ , Yj HDIe\Jb7J9rR+`Luc^lPp@RdgX|?0v{j*1堁rrSt{u?Ob gKܱ8lw.\_ov}Emam\Y]^ϺeJ` #@h k[" ^%nBbUƚ2E[Ud)0`0 f3U!T3 A0 `Nf I}@)Tzf*0cI΀\f$3ơk)>(uD3]kgXZNZ6adyGb, >p)k^|e[6WvpO o 2'w΄c*{`>*3fyL*>UeNe>/H¥ŋ'\*5׿~Efo*6z_|) ޽{~^u~(fۦ6cj3p\,J=L =MIJj$*B  ظi[}+}|lC7?;)` 8ῃ V~ KZyݴwg+m[8yƒc觛_ 󛾨ZWbsE0Vڏ\5؆,]K7?7 ]Q5Y2dޭ72D#Lbskn~g/{vo1DlP[kYYМI?OӅl^+Qy OTGݦ67L.WW#۫" 58IkK:z[hխDMB* ,ū.Q:^a8Z΀yŗ30 A+Pc͖s`4y(J`ԒH oI*ͫm@.=0{Z2EӌWy@10fe=TKfSE-#ney jP3;hRTpgꀍ\'̥xg4eo=0K3p!(]&%P׬1 ;WL \ rQCdji #EdΤ] TI%R ',eFJ ^C:K2 ՁYRd;`^XMf U7`f+0/Հs0ks9Xl=W%:k)V; Bc>ir`.PGok(΃CszǏ 9 71 ^9ynw \7l]%uٷ{`>}?_k*sNn1>`^nQs+O6 ͷ F9>-tN,KRq3xQ(P_7Ǵ,ܠ;Yn 0K)U ۷}wr(7#Cm0@UN,0 z$@Lp𑿙vD{1-EcAAY8PP,&7u: GE@U03lRt8pZf9 L6(R0e RMq,4[d(GjFļ*D)D"O%Pŕ"^Y=jZ, =@=ċ(ҳ 9U/EB"\Bvx8Uw@G=OGpL"Xy~TTgnUPv% ut^G2]d(.Gh\xV]^`JGrv~/snK~ō+q\ B˺\}W USh0Mn gָط_Nd^HKO~7rSu+J:{yCWɼZ]L٬ [fTX7nͰE7ڼ!>=Yܺ8ݦ% mF^wlvnwH&1{TMVQtsBSUxZvȲ@ utOZ#KkDqcIP&2"-]߃eI6T ՘`.GfvlmBϔ HF۠Dsӡz| }{1Qnf|+UTJ\b*T+4r^f5`^gxޡ| ö}g|d xzdFw8Wa݃ܭ>F"]q.k +'PO<1x:K!TH^y%b8g <5xeU^hrx=+s)j3*8{G<,+[7:nJ/I!fQCD20Ĉ2($f6V CQS &IY]@K%)VyU"CIZt~L90Cu67ԷBuv%Ht Qj1-W]:[2)<16T:56gtʎO[/;K4 b!A61WY̢,g*X6XFN5OD 0a*1LBBk lUp]ܰJͼb^augU7nW*}?qClӏݠX/^qokz؟TL (?29~=?QZws`,#"a"ROMQ[_>_յ-~Uc,{ade<sH?P{eY. Y>U_=͌mY@%yש"j>eyB4ut:M?KA`//PqO([BMvŷ2tQו;FqAuVDsnƶȨ"]H ̌3^d!0 < k6a~S IDATK1Gү22k2=: \#T̩2On/;t)!߸<8^~k]ܦ̯ 0oΎ_8 SywO/_>̕?S?C͟~j3pMqުc[-ݨ%qIs>C}L|/g1xawxR"Y +.̘p(L-+㛽+ROa)2I'V (G&}\e[ g`YfVr-8+Z9\P\q^lR ( (5 qg:lh"Bh0OJAgFHLi THY<$=7xVnbP}"7T"9(JP[aVuFTHе eksnYH?OMBlgsLCbhO+#e9GKk>C*-qX[jԾ-|US%ze3ɺDb]Շ btNN_'ﱳښY϶43vvfn#qn cto5WEO^S׉שPȪCAsJ?MY,t\*39> obYYk6Rn|>Ds, (SS[\y-MV;~m*Hϩt*ʦ6Cֲ<o_5QMb-EDqEJհ,*lkEwx3]c0`¨,oJ#^8:JBEVlrAX艾4Wjl=0+HhX*0U[3 Yx3s =c\i?pqd̅j2T߄q摪op8Ig<,P-Pq307B-ܲo!LPc)^jY]Q-~Ħ.̟Ͼ/#\Rg^U3<)~:~C384? itu=|8>+N= op~x~=8/ˈn"8 ?M{% X[$a"$]IfN O7 ":L/Ge(Gl72eR(3 B)`ily_<ڀ3l4V#])$lnp=PD&0U+-B1H0 0d+:D`4Z^*JVU!D״ j!Ъïx0]ݸ] ?}LA66#YigWȾaڒy%6'@Baz&Wǿ kZ--OX-wTv[g}z7h1Y-;2+m'A۹G*ɬW@~G>=o⦶֍$TxwS::$:V}r1ƶ?_vJ+(roMFHJZM(gs#^ةm|ܵ&b  -, [Xǣ:|<"F#v~0 F)KԊ;aa`̂b* 0/q,6 T0>iR\F;`nnéb컇 P_@c` 80-P-Ј-x {y(QZ ovsRlOpw|-pXk <^5{;}0MS&>>)1X7=?Kl:LwOw|  ,,KEgcj,q$YG#$̞nIE/dށYϼNu=Os?\#SLFYG@fgH!">ӣG1wj^)ziⲦ1==u6NJ9H(>ƥڀZ/|SJ˂f%`l]' XXWԋ@B&u ]]̦Ҭ }t)RvsM VqF}.WVj>nn*Mg-ae'`%$W[rm,e2UfI_EkM=NhnLcևIq7h.mxY Bm#/$R誺 Dǥѹ+II9](!v]ڨZjIUt(ٍӼ;A{K}\":٩qwz>lQv M&a2 W;.?}q}).I(!Yi[[ liq.kg3x&c|$2PR:Λ6ˆ[Y_|tҐf4QxMjr>[G Wk7A?>qϲ/yaHSk7R<{#$?0\C~Vl x5?v!1DKns "9Sg"Y>KFe^L3 vpu#"F W}i,Qঙw X j0%%dڈTZoy `.Uryڀz4`V[iZ”\k}:nHZ0Owm`N3 ݝz̯3p `o{zr2i>pHQye{{֟ǻ߻ `;_|__?9}\p;iGrO>u:8oo$8+gny}-· p@o8?bZ<93u)wv20as|^X,$VshN`f [tҾW(.W`q+:0Á#0?90:1/jg.KqkEg31C|<01hxx|i|2d)(c\݋3o|wsw_2ӻ:O{9Z}Q~΍ŵ2Tq~#8? |G49}{N ߺtGg8'/-NpҠpxQ1 g3˲ Gp.8/!2!QӵRX0Z%X&-p?MCXj̨+KEpU /wqO,:$Xt2&9Rd쯺Y8<0ӣ=0zDRQ(Btrz$A1"" y4ɎInw;藥'c'Nd;Y2t[4wwM$-ralAG;aas{ݬFV#2y(lvcv쎃ve_|}fֹs+87 |k>4 ܷ]~Y7mE!`?۲n؁2:(G?#l[5I޻"Jh5IrΗc!hȯ6ÍgFDpBQbt2ݺ`)`U&@(g9)BXڤ<"ymTZ5~;0[v`^kE溂L``^_aFb]A-0@oK`i\h|cTq80gW 8'K`~6Nc;=@2xe?[q=¼b/z w;eٷ7ޏ+O3{4g\h󫟼rqmnG"^w;8ͣL;͗lu<<< vpvGik?`t>_.8'v:8,'82^ I%kLH8k ¿8nog#8;.#87O,uvQuQj@M 0y2h1PZh} XgV I0=ԿgR 1:{ JR]J5nlzl瀗돫xִ;]M| m@~8coA:`W =,ßd? (hl6iRfe lEc߆ZAfYX.V3xF{R kS%0++).O`.6!`̼{`V-l#0c7L+b@ss1i^$0{*̅Ku93Ug"C>:0ί136 h0 ^BLJ+'aC6 A1du}H6!1Kx}f!T2ff3V*Bjay@:yV1{0cf80]]J1fLVBmX O h,uɰ< >07Ԁ8LxUby}+tqZZ0o_ka^Wy{ك:v >9Ք\4w?`?W__n?-򃌐 |4>}4C.a;L7m*~Or4hF |̰G{1y4G/4 5Ql|li?xܵ}NEG3@<=kSV );Xu}bW( e^c#te#E&  ٥ۢ]r+r4{q&I5qA8QcgXHԉ"DAF93Q8O y=sc6!'ҿS^ѶE#$}kO\0qٯgks~bT6;7~*Ҏdzu]DxZa_ ~ 6Ge/12^L;>_㽋M?30_G~zqm I $sw=n˴Ep998vtcf˴x@N= -.m0[cyX)]Lvn=8g0tevk>bVB8Mp^R3x µSQIcHY]5tpNN6I%:\QJ'ݐ]JuE1I6g'˶˸4ZZHϹAip# x;;l#> 隝ld !.;Z)[Nfts.jw5o@{v' DDJ p 8ĴI˝nm9Zc@cߦP_?b?io7;2Jm1~6_$||c(i N1 NF6Y72|oyx+˖0=8'0ڙ[^Ⱦ;_C{@ZRDĵ{%V~dw_C=* .@Խ$}$aHc'PJ\gs;u8r%wFx.e&1Z㔐hg R3d~ҘRBʱ5fXrlҰFGTS+$ȕƕu~!jhYXnyŊK`\bb 0/P1%0O k!;0Du&ίjwպDkz367`>;0 x ^h ̽e]3!=MWV: IDAT:w?Gi<{3D`/J걿F*_ vN~ -? ?^p"}ր~Qe+duJoߞKdڋaı˴mx*LV-3Iʃ2E,0 fo&`-P=zZ+TExrMDtڵt )t1MU[`˹ L Ve.wXD9Z!@3iX.і PA-VzʞNPf+(t0&@h]KJ7ZH[(0$ۂ-JUsT U\KhB(R\reFKQR<+.5-^"P]p.>j'Y5>칺"QQD9VrZ¤~QYC8 w `UvK}ckի^ˣj6X?wܿˤSC kD_Hwm.((y eݎkPtDm^b"jJ=at]η'ESz4R%ɪBvGsutt)3,ɸ r$٬gmjRWT?ʳ/1Js WϬsv[[E|\\|̬8`#WD} |?)1R۾sϏ1=a'a/0 7r[ B?p3M \@z]dH) (͇jIRA tE56I f_h*jbC]A+>PLw3fQ0?]q^ZZfZ;0K-0KKYdt|%GLKKhsǗ<9^rdjM`~?FU_۵(? ۧ0[__auj?8Q}kiL:bL!+<9fWy͍+L>~`L~l2M\yJNMs43k7g>8/-8:3KQ񃐥3ײ8UID 8:RFT˂8#M̈-#&8uGG[]5fcZi8gv 8{LAApa>Bi$B*he6~kf570G&j/50}Om&DV4"Y -l!#TI}_`5Z"dl0,5L׌Ѷ6ȶ ÜQy"#їۛhUњcͪpT?}־e1m<~;lgFwu՜lR_Hnp{my؆w#av-\!};ϔ~N5#ʢxGύaUf>7r+a$$=Sxv}W ,ƹӼ{;pSMBD]QP 65Za@hW5MƖ%4)iu#džx\ϷɦZf晴L`F$orf3\*0N4;jdrvg:W y 0kuZ̈Zҁ){::0oL{?deW^s 쳿OǦjLWy/5ݒeS=" W/&e~nEW ϯ6|9}s96~+WGpy"70ʴZ~3-8}zepԞ"Fi'ރ1k3uVp9cgwXk%==3GzO̜67Juuf|EJюiela߮=B"Rm\}4k8_IW[gwCjO5V:ory#fz.jy=-N>ȓjV`*FHzɨzL:J:0ș/#0Re|=]ϸ ̸0e~4!~˱x O`Yn+n0>٧on˲?}??]]p`~u?N|?p>Wy8|%sCt'\˴/_{>=mߥ޾&\}:q<\*؆VDeV]Jwviړy" 0bvL:JJTp]rHZ]ZaVIwL[L8 ̘EȎRm (']J d#8)LZQ.R@D4`f*ӡ8z(}1RVJhpU(E;w*PN8BmJ=O@ u u}@j V߶fKB˪[TpviX i otTV?RAqWtVFQ\WIJ.E)@V~!fB@ 6'\?M @ "Rܹ9j&&3G<BqjcMHF-9)9ojֺH[h)ot7ǥv;quTw#u΂Q~}V,fWG9Ĝ6a?ۆq!Xj8-|q$.F]cw5~7AK4;vy.W ظu5HqF!qNc'Jh:\ u ?ao.y7E' B2n{d +%%Z֏dkqo B)-1ؠ-?@8ap`>|eY&Wu*\Ё PPVcqnЀxiq:WL||#ׇôeog=<$:riz|Vc>n3Nǣvs?7JpO>BCx/p[h?~]˷ߋ#%4p8;8/;x\o, ?sym<7?xQYe~>Z~3>c Gv*{p*\iZA> yRT:jOp(W\j2Rt3<4;pV肺g/j2MQmC~f&g& E&]h(mΤ"(^Ɂ˦Q"VMDUCPRE8}" g:8_"DQ>.5] Co1z>XH[s{x7Hai7Urudcv4zFn; y Z/@#V'䁎y%Hd4Ϟ/.-x?Q ߀fΡL 3nBq5g}泟3ǽx3AWc\n+6CHr-z/T-vWDٶ){rw!6OSv!>( zs(nr{󚙥 *MR0ZhZ+Ҏ#,T/5|妸AX؃Pu2!p{zjrDqRb̈́JH oGxD^>KXk{BFYKJB.$J:I5nSYF>4hQu.WYo8850יZZQP-XGN|Z\ʑxzi <,>5`2œ Rw)f4ӛ!xeהeU7Sy)v_}qWV:^fc.󻧏7?{9JO7rsT{O~Żfbm^_9a%7?˿\m񠽣G<`ZX⁽NGb(ˑOTެ0Ճ/9]+y808P 8o€3+9T/ lH-eqm8 8虞Ŕm-T-6,vEi:D۔݂fTe2u?Ef_5 (C:yVdUShv6 <8;7EuviwJE,%hRH5Uuߔ楼0%,je-]& dۮceJy;!̲S.!-#eǕP?+,#H}+KɼCv m$s8cy2@L8z< =2g;˽ȈevYvY3֟K)p;t~K~;\j!Ϗ\;w;qY+5g>!p܂z}>N3ym2bY{3S-/c_YB/*+<fׁ9z/|8SV}r-/pkd56rzXCӍ@{~܎~^Yr mdM$&f4X8>_o? eHʫ;w'[;wn 1FrAiQQ" HUo3%I˹7Qlr<_t rp'&6gsADy_:=&*\BY& (F@8@{ *2DV7>]qIV1 en3$zM ">p\aq.gdZ!Ac1 \I6)QbYX&bV9{`VkU4Q ,u"q(V>: yf)[`>^sxxt:wӯ$=0ro 9U ğܰz^yoisk/7῿?0ף__oo3>O3}ӇB3,8 88zoq/_6=_gfY,Ac]_zy+|M~s)ԇ7r:b g*8{'XQVP%8M^-*8ag$ D03QiIMj̵\KbSa&Z ͒` x BH&(%l+@Jw|3FA\*1Q6wIZSO0)Xz7A]({[&7mM9#.;=ʴNb?k!rG=]9j4*euq<`]Iǩ$fW;Fnur6n owpoEI*>4^JF0mK-7hR]&"Y{u ?iz~}|Ôn0d>)rDak~aK8}a}_"W-VV}*sQV2eN&H˽=dWHĸZNs.F! =`'Y )G|H`#(5 \ll} -ۣh( +dJx qzEU' $U]^YA=,"B'17Ryr6 &F|H{/#렕ZQ-/ Ξj!,b y̤\F`yB zTK9ƽ$Ÿ;r3C%W9J1o9Ԉ[IʇR +koi? |wkz.QE }-Yh) |fQ}s _f'L/r)~ɩ˴|3I_(G]lvgB#KHK@H/W2y'b__(?pYȵ6ٖzɵ GZQ&72p*x@).Vqi`ʵ ۨE !ז$rEĬV%u<udlBTn4Vѓ2 ey^3u)ˠxS?FI9g:C!p#Bh1yip vXo{n!dV])Ci&]y̐RO8{ vٝur.7{5ٝ7Ի&Zl;w鯟 9Y:߳rXalo«F9wMmoԯ<9+ *1l) IDATR bRZu (UDceeg1~2B1I\+EB]}\+RJS268dTbf->B#¼bfSÞf,p`F[| ` 02G`S/)mx~C%<Vr:W>5`}|7Emӷݙcyov'+|_}k:恟?}21g6v|X<Ϲi?=Q\Ded^!<OO1tq~zSQ#BLUv Xy-3DL((v X9c mQj.8 UdVG <\tqs|W0Fa,rIx(Rd&u'(*riQa 1⬺#"XW(&2 S =f *:ͬYH#"03=*i*0J)Q@QLT5*@ 5jJy%#0 +> [eH$UGltF{SN(Ĉ /&4S &&e9Rt(%V?Ndx3y̑\v@- ([ڱe,I>m=4@G[TCA${jz݆3✫5%܁ֹW(wiO{nlw鿊}\tC$"n%f>y8юSJ4Λ"d<)n#Bׄ`~UwKAZkˈw{k$[?і)Ll?⿱-cl-"Dt92R V 5ůpƹl6Ck#>b<"bh^ƅ&~D3=*evUj]Gc!%h)+&sB2.ˆp9v%Rg|C@ͤJORJ 4fBU:0+$}Hp]+k$6 _|ίדx2|OX+&깃y wun| p1NkY=wSy]΋Pi±lXMdLg2 %KOUp6غ@u];8`I p&g$8TYTmEm&euR$W`J E(FwvlϷUumR,dY*TWk&=:;ni2|u`ڽfS2,(RHĆ>pPHxb -WP0@àP" Y:z7_։w /[ՔCc-D{ }}HC=N)?S $G4{ F^ yJ2I(\8Z~| `>Ok3P,- Ք(yqV-̘΅#0g#x?^nݝ;ԡ)ӾmނsFgz؁3+ 8 'Q[`Dִ봂vg-@gEf.٭>8u588pϲ*UV]D0u,N 5J=KrmtXUQŒtpkE3R-\EXE`!sGKD(-Y3HaΠ{)t KwU)4H"R ULh +/"`Q,%?%|f%==pva@gwDqsǴ6%D;DA47DuPHނt6IP@F07\CkޣsS\xmͫӿuHL7TyZk@栍߆a6v/Ufu;`=#"hpdfy 9$)dhrD@Z>[+cޛ̍h;Dց$:,G2L hL}VZl*!**Nȶ(Ch2ͼa].Vڣa:&pqu"xvufE&eUYk(i4"|?tlu/3v0Bav"VI)0]X8Xu2RmbPu!y dh"f0/ =,u0?r"3 `;`>޽_WL8!`} =otbN,eS8?;ffzp`g0g0o_;cޖ,{K w31>B{Mh3 8{__}1}9oJs२>ﰮ.F;8!3P*8ܕX' UNyӴbIpV8Sr0a; W! 8jC&""ǥ(<@ub)V vXM&%*f pT'yΥhyu$PU9)yRiGFA+QgiQg xf@w$#,NIwewU*Oh xff~_&4HgjƾzֹIp 39;6 Ͷ' 2; l8 Cm&Ԏk v@ "o40tPԱ=P 2lҶ2юY njm'.|k}ZwK1|=X}k6;~9 ;8 K8q` q? q\}4ys)qFp}Ry!OfVkDKax$ҟq^N!wx.jtX֐+3ݨ+VR,U'r::TRMl%$]+ M pD7O->Eͫ)T$k]˼r]OlHc*TQ/1Ѥ**Vz0 ,BS &&0%ou`M`Zg[? '0gݒ$94U3$KI4fd8&am7Mu@ρy l]af W ;|uN_U/[`yM ⷿ\IQek1?>o.5k_})_am??i8w/|%>xzկ~Io埮_ ?t/q< ׇixZOW[YuXfK~;*7xgxZ a=y W%˟@r>J Yd;ԬM*3xri~3V |]mխE<`[Hўn0;~cmlek|@V7TYHF{ܙp׳mϋnڹzhI9[@Űlmk/5#,c}xp7t'#i'pח}1`5dYU&о3O?OSτv۔mlkԸ[z]jۋx_QT 3h84GCw/a"k4ke bT%j>yhn l7T"AOՀԬsQaj}W OlN'@T&-[D3FDV*M᪉Р3UZNL=gs H=5֦(%\z֌ځ,Sӑ̞y݀ٗ_#0ϻ%D3f^}b#0/h~׼ÿy[<3LFlLs弋2|׭/'| pB̗Ui3Tm?|A.@AZyW`2K4?aʗgWW|~m|_ ΧCjo{/3m+P+L{\q>`"38ҲG,w>y3}¦8O`=g3WZ]qPi1`(fGQKXTDYEI5RB"(dΠib-6d +:3Äp RDh0VYHnfh3sm+P "J7E#g IhB3vLdV+⪲Ae;dЪ%RDIiVE=P SuH񥺥)d ?f"M-qX%lԠV3ަRwlRĕdf6XBpJ @"=@"x`c}Sz' 4{5wZ_ J4mm|̴ޓHdin zc_7٪-4Ynr՜}uK4K7$?Os F>JXwh77fnk_;k?zO9M5H`n7*ɫ@k'ܞᶁ`@!':u?^~S|V)\gOPywřϟP%x'jJtޡ;Zr$edzx1@e21z*f 5)>i$@R6U37ՀVJԺ,ᕝ:xCMEI4Qm`8i=%?f;s$fvge̻'Va4]qy0 [4e?p,80@>'o @Sax P|̟E'"OtR=LmgjzV js+u//\mޤ8>\ul6's`Jg{PY_oR!B}vm}qNVfݼ#%iU_I uod.,P2q>;$>{j=k7>H9%h#Cmbz}HO\sm9ɍ+(Nʺ@wvbvzal%%S\>꿅r.?\(6͏DB=i[[a6td_o?NҖHh&Tq)- 5:ԉRଢ଼-_ĺ6 rPWU;#[N NS+ˮ *٬)zAYA0sbSU3J,Ń 0z-~A(bPÜۨl&VdLVj BUn0Z).H1YQ0J/+(_ȿ4 $K-$ò`Yib˲`98I'#>I#th _=/v鷺f~bg gG5V`67ON^?tPO2_ϟI6/7|/4TIcy[yZvK++q#NuoTþI>9>}tYy(sxC]MX *jҮǦ>wk| >ݹ_k*7WCk;@n!uiH; +i&DZ5{?zZ-Y!ʚeG*uB(ߤd}Ѭdj(dM?LdU=nw%Ѱ}Wz03vKkNon_hܒeU-P &t9P6:O@XkEJbb?E*KeO[7fM2%af3RR”LXiLT͒s52gE`v)Z̻;T>{`üd(okp45_(0?%Ѣ~0ys`#8n>!^xh|})0<_1`J)<{S` l;#4}˂3p ="8||'p>g[81N$u(@^6pv{wn{gS.0Ϳq&DԷgع>Z t&o(.8 cp,U&iw͊L I!*(\~KURk] g(E&dH4\੕ (M|,,J@<% 9B<QiH!HMay禄PV7ЃTB(=3Hhm)kZC*inMՑ +޹8Ӛ~*y*~'R09S6in=/`Wop?_g|bw %_z_RO]{[n#4}'B38v[<۫wp}˽=<pƃ Sap8+v8 f ༗R `*peY< 8 (Vh@Y8g I`uN $2iB9R KDN35rf)UtչJ\IJȎ{E7%jT(HL馤zjPUIPHDSUg(JCWpWu` ɕe:|ÐL@ WUѐ[@VYԩkUvtet#i*i;gCa&G XkBBwa%^cqoo ){]BZohUƔfzΞrSƘXL׾; -9hw{^Z;:##W-kFik[~Ӽ(v=29{kuˁEW+}g^@78svT‰~3[Hz&O$s44sfN[ffU1B5!0Do^;}KEi$n ?{\h,0*qVhHZj7kϽHU$l2r2\G܌: ډ˺ˀE:*7A B9Ilj%*]d(vsl hrA)3 M*-GXIW^2X(V`&5e:̵,FEf_J=; HT{>9D;t隗y*@#i4WHia],T{滦0_f߼U9_fٗ~۳ > @[`~ GCn 8ͧG6[p>= %p~|GpˊsS9/;(M՚9ܸͨvJIuk3]cq8܋3' Vp6t43Ur>< -)Yi ,2 g`@sSK-2%R҄Z 4S͵5.k*yʬX͟y,HՠTP R1j\T1X:: ȶеdTÄ} j_KR49;2TThJ ̬_.+p>t KH!i CŠ>vI!9,pq7ㆴFg>7Qii)L)mS-I͙%^)ΌI!`"6 :7V^WzمޟmQ89i|mnW?r~ `K3~0M؇'{Uͤ\az'$CDN!šjt{ h"v떖kYڌ@_a>Uۑ7τ8:t[P_Y{.rLF/a9qɶòRP@sەx7687hei겄l`ˇJI!Vc&ՠ hVvD&+. 4STq3$4&1[fr߻/T*;xqX} S6P󾥔iٱJtEy%X:0:0f'7ݕ*7VɁy `~AVݥ_c1T+g>) W<fꝀmTcT>B󻕓+/_ k>{gc|LRߠ\-n9kzu/~>ԿΥ 4p^208ypc$+qHr{|笯=g폲 p帚g/{!1&T&e],yGڞ&0R(I "W,e<%TIvGSQ ,(ƌY HbBTP)Au"zd9rxv3Uj'⊬G&6Z |C`sӺ ˙t"X%Gv\<M5& ꩺ&1I0gn  ohw׵겝Xq 2@6mBemcwM l`Ǭ tH!k&*( z_ ώ45/ YxM`0;[`m_qlaC {.@r#,Bw@1H܎jh>xWy:Te?Qj1cXo=z)kTz9IJ3XF{h"Z5(簼[Vz\F④g| j&r w#\].?aS\XbJ{-4K$D s,,䉉Xd2K)Pf&su %aL[ Rg 0MWւt$AܗyGjdՉ% XraJKq5Iv+  Ҳ 0`W'/^X뛟v`~^5s6?ll/ğeG`뚗/_Fʏ?f K__a?P>B󻗿8 9g>g76^wŲ^{ ~΃5^-\yVpnn>A}+pLDKbyDz?98#Wg9SXk\- +TQ6;$L +0L~nEX2LDa2fSf)B;UGZE5PM]uNTU>):C!XiJR}ycWb(`5H;i& UVF`k x03{ɢFLq,|QhwuSF F]D0#7^aU6 X]Gs< 2QΫtv:M)pVs֛@ѱǽ|a{-PYDpXe!j9Ѭ{~ik K|Q S>F`F).+ LYhp_BTOLYIh+ jd- aJRTFD&#Uw0DDlC3v$ȦR0=14Qԉ)+9r0o0~b ΁[%Q5p`F5L4p?#Bs!,{yJƏp߭˗/ئfٿmF#0/~>0_Ue^Ͽ◿yS cyo`?˿ 8-~m=/o8/_ٳ_]?W\_/tml"w-u=`g^ի?:&R  L 0? =l{#gg{쮞lLi3ƀ##R y<Ӝ W } T+yB%#k0^:wVJAvFjH4}V"3Dc1J.XP %$j$eIa> ɈfF#VR}$+RiMـ$+}=R6A*V Gn ܦEu#h[}٣{OP{_/ټ>TDQ.)(d>ǫdREk-h>ut|Hrڮ3sÌZ}3̪|{3OX{9_~dH;W:2̭\J~_s'yϯExy#JS F.Y58015E*JIjVPI+; }iodadV*EzHGCuVuBF9EZtvDZgJ$䞮 h&xGpUE t&j'J']!*&0!>nhQaPDkGAؒ5?h3#fͮ\zݖЫ)t%h̳MnlMLD5Ӏ<ォT:*X5 c2l60&MlIӮ;jfX)O4/A(M!~@t^`'Ztr7\L) )a kfq[*aMR"3[zx q=8WvߌV!Ok}]JD^娟\'s_ߨ3 l `JДe6T7qeHQT^h7d*SFRQ$)U]@WR+b(t4h:Jl"w4(t\1Ä8n2ހy)8i5DJ-C3dv-nY}~-vk+QeM580)2SWn݀y0Giܼ &׾ˀzUG`{u߮x?mρp7/l,|Y7xZy`eC#4_yOhjWo`4~ 8_6NydLxdEf' l{7 |1x>/2feg˴L8 3pFƃ<ﮈR=p<4+FjR"rvkfVMn] eN̵U(PJgT&8j0SzXɉ(&L p67ݵɚhUg9EjJ='')4DERdGDHmѮM1E=LnZ6N dvkaz mIL1uƆF#Ͷc&$rQN"#@kTva]fhҹ0b d_Dz=̘%w ְ\dx{Т:&m~k'i=kn=>5nOzY3ϓpLר[뭿r"8mMi$I @:h) CxZР>E+X'@qpvG^ +CE<\PlXevXw~eݶ#JcD& MV~Êu9BL*ޜ9]<>Guczm`Ŭy2̈́SI&THVC]`"TkU@遵&_)M,4u7$dK943S9I,eAJ)O$Xpĵy#T;|04S5V`80O;b9;iüp{i)@nрgz0*'3v/1|}]m.ST̿-||z\dd < O8秨O~x]m~Of s4pFsip77;uΟli_F,gyB0"8STgRm29Պi JEM-&ΆJH&Td\2by *R.$a,"h_j 0X!',YY} D)PI@.S@^6 -ϲUg+76/*ѿAU`m[("I>x\LQZ  @\`W>whjY>D{@.-odom4i #hm EVn<>ɁM`/71PUGot IDAT`񒊪@{,=iObww~}o/z~ۋq]mKlASQknmlm[!rFWdojcvBbΝ{@?y}/&@|G=lmۘdqhKZlNR19n+70/FT2ZrR31m"`YȤ٣ll: *5)* +DQ HyZJXC4&WS.SvcnDORL%}Y Bi6LZLf(-0Os6Al9{kO [0f'KQÇ9/;fl20=Jvdr`sW8.Yݴ[I3cGa`~Ӎ| 7+i*3pcX>Bfi|poo&pn < c맂 88'nI 8iBv.ys*#8LHApzh[TJKeJE)<:zM\3\:H{1F$  ̏Ͳ3Ǭ̗,Rj0 dowf-Ôa(wl i~l_6O>9o t'daIGz8 =#nwYW`·Fpi `i/AN4a aR8D̘o&Z=H+8ךb扂RͲ%5Vsnڕ&ny2)BSXH 1Ґ{UIѣQA[Dz(IO4tY$jsx&(t02fMKDG-7% 7mf,q\W SV4JXo7^!IϷ cݛhn@&|p} ^׏n-u7nZfWNgkޯl*A6q@/Oc'?|ysD/>O6Kѳ)Jgż;]]ta5 i3'-?Y  81 !M@ =${0 Jmj=aAKbAU(ɯp?vjgzri` l=ͪ+CX& J&i,̴+ *H\]'A-qI\VeYBk(`bb9@+3-\E/UqWu93\]&z)٬}ٺ_z 7_ |?u7ؿ<_W'ups /]>~XyW?^.Kew׷}9ڣnP^npe n r2To}q e*GՏ%}vjQYM_^ uTE0E0Dj-iB.5*2M@"  r WZEȔ'ɸBA 9R5gL9AHP( grԌ* 3_yԄL jULL:y{DCHs )b`ZE, 3\1A fbY,L7 wEh"}hHj$$*$MDSA!+"4l& iEx)=̗'(6a"U=Иym{5!)MFUPLtl'//21 }wNF;m#>y exC%fmAܶv[^uywE;GkZ]mʱu~<кUUQGZv3ضO;Je o׶ $oBA %}0JHI#iR(۞*"VQTZ_ʲ@ȘB4QDTj_T Vh;ʒ\tjRWd ϗl@T<B+J@W )+ 2Mjd؉ eq슞BYI,@Jk:G||͙n `> aK s.7`O3 I+`)0O?3/.Awg>0bi]Uf7#47#\>ޥ< ϿS| _{:?g|d9> wEp28_Y!r;ѧ8F9T9mV:w;uwn֐r[ޖe8E3``MQUPN!9|ۡTsf"nV/\!?_e6wyvPF76n]QHaAUـEXnZ EWg!n܏rEHܖ%QAu;8@<puY lF%.@4WȂjُ*dg겧L,w2_:Ot _|[\ahn7)8;4J_Fp~9! 8 0ή:?θJQy 8 vxx3݌k9`=:.w .~.HU0]*rEr(*0A::Wxĕ攡1uՙR ;<;<'1[)lU("ʀk"9 Oe`גTaTRq5Y]Y6 : ׬zS}]9ี=l*v%M!eP 4A0BtXZ|2*s /Az( ƘLxg8nJU~'Eg?Vw#.zfA<9Wo4Tk=/V(ldquLZ G< ܪ $ܴ!H\H}+ݧTWD90@0GiplGEYaAUFkSћrʼnUuCU,W:#;$ˉ*Rbò':{4+,xo`4G(UQ]+]iVt8yg2`JeR7֚r2[e /ywHyb).o9Ҳ^Eך+sSnoנ[,G7#Jc|u_AV.A35R(Öͭfh>8 O ?wEiڧ|D@e+$ `ztpgjλP\"̻uZ{T6.PEvYE5չ$9ΪՏ@Z< DL찃Y=giZivŵE P~OyɆ-x1x2dvm.?cj-taB`h_'96 6srCWk]e|x͢>9*<[MV8Ӏi3^÷!puXH2uX6zKlV kBJcަb9vh*uWg.jŒT%SUe˃9lOWW`ͪG^<ͱ'4p8B/OU#Z8? \o>= Gw7?lp?p$p^$e^Sd8xygAn ˗yyڵ7I$"sm[[4Hfgd| os8Դ[f҃riLG$$c? ckoqOvt7uu9Q/*'FUz[a~׻<edϯT G7f%h[m 7H]xhOjW1A!5 - (K2 x03KSP78Q?8X.u-_l:pS u& CV՞g`~PNțQ:}-|jc}tzCh>k9^*Igb~}9Wl/(tuM=.>c_K.+evrUqu_.y ,Pڳ\KW+݂r kFgUcyr5| 1"^O4XZ/X];6y~,hRݗr2EX:γfރv~ $G9HZ\`tV}c!qk_fnZu95[0O'urFm~H#l~a>9Wy'msk kXU[ >'lgչ 3$:À63PyTu~t)-L! 뚁8lAa@9&sm}pXKd[;w^Ϯg:lyH+xO mh_hfb-(ŷxsűt_Z}B#AWY,'g롺;VHҶ5[couͭOKd $7+L_`3L*UٺzJjTdTFQ 4l Օ>Q?ۺEZP֤jl)iw A^ O"lR+`3 V<ag$(0DrRvHLLxY;;Ir.52,~ـy2./kJ9`~n[!7HƷso/x O;GC3 p~p^$k?wۂ3j V<9=n35}y'7_Uaǖe;ΓD?2Yuf$UAhGhT )`W0FUi`5 0G;X XPK ڎj g³|ia5O>fw j_epɏ'9< IDATV:їZ'0ЅѠ`C))'}v mk4*q*u}=k*ޞSkΗ`OkGm=kxlei.ACe} ۰L_Ү{5x99:ͺ)6f^5.`@yN68aVe9V wVf+ҴZmqH O:Wn Ö"8/1 lq]3EzXZ\<\u+=, sVJ*,Ca9_ز}؀sefpD Uɾ "JU'&f/eb ux<i;0CiQ70Z ɎŒ}0n?w70__OEBw` ,O濕WA0~}[^pnxqݸޟɾ%9o<>=cg755x<7 sM{n#'w}wٞXZ;%bT8xAɅ0r'a4L.'w:nw"ؑvc3q1ML#܌/dNH᚛1|2 sq0^ða=E讆s&k4E)9I;8(@81:F^x;k\p^bf7'xS@dv$q%N.WR w$#F)9$$|dDadPaCU}%EΣL0q2KtIIU):ˆ8fDTwO#ΉDI8: yIEH!0HB$pxED#$%8D35Z㷓M>}E9Do=ssޤ9Ӿ N MDxZzK9sa/Cp^|=#!} 887zyV D,.kyAubNseuZ1|~5/O;ĽYz__nu^3 Ȟ[i,#`wu'Rɩ@@& /9Fv| .NRn!Ki-kY-:q uHGk+^[4JwwI EAlmgP»2j B|#.CKDHO^$D?ID\p">϶8DT@:}wqI$ ) ÐR$U)I$i+$>T5 JQ M0 )0'N`Oi4gϾ~0jAR܌s3I D.HD. ZF윗Ĕr^;Ȭjm9(56ȶE)9B~'ȝ>sbiH7/3P;HS[QP_AZ0c zs ]=rŒޞhsfk[?h9Xw@"vNʲ-R.>CXj-pUV.EjN=S:3o2 k7M‘CICIͷsBrR;|?oPծ/Im'4+ܓKw q!ل$sOVC^$Tk6PTvUe'S5oAb?c>R>jh)_>$~d&c$ȃ`كD$:6eR)EA }5 )Fk$' 0RҌ$.|" G$*6wE}B28ر'|DS>B}A$FM$A1du9[<ZU1kuz,ls9AaX+{/z$7ځb "fwaGmme54i{KvӱxyF|0l6K˶}!X˨Y~^!5 |'B-&F\< 128.3Վj>1x}c ?* d`_P{>KxZ p#20Ɛph]JWfcVsU/^?E<̖ Kh~e?'h@}ܷz8>p~a5З%W 5Y0M^S<+$C׿8-Y X@wm(Al᪂3PjwDiVnNW a#st[q`֤-@/YS x0Kѥ{%%jWx.-а@ߺo@$ZK&sv $Q'mHrηBӼ O0MWIUui~2&]ݼ>Wu{^ֵкW~,~0m{lO=pM[z90l{ lq+r xQ- a]I&z1 mݪYm;YsVm+Pv ~ة|xNVs/dXE -[ǂrnYR "ڊ KnTa= gX) iAW5T&,>v`PB/8L dQnIAWq Ta +?KY]̎ iJ2d;kytKy ~ ͏f0h~O4y<8Uytݏ58/y׮π8O[ݝN%ݡsgkm&]nt퓃ψiYuw(yYwVGRs\R]M%7l3Adi.[]t+x!F]ScowU]`jtV X-i;eh['ε!5ۆRg m RaYXVS!y>_ws#|q|˶tzذpK ێ%6wJna Dz3,`s~%:lo bES:?PtɦVYț !Aϩ봠σ<!ebln{ЯۉW[[ ˒L^ Xs|\\ etϨ<ZE򽰜`+c&ƹU^A P!06ZKKqfެ{n .m0֓>}Tz?.-Mr'U3bj}IܹAA-vTYA޲gKM/,+n /w2)I4t/vz-nl[}>wݩ>tc -.k؜3ZbNݰS[o;;hz;8n,;浴iꚕSN Xjb j=ew1؇ 1bxY&j;{orQ`0,'/9=xdnjEm~{KV5 .m-UlAf Ȥ 0²> 2M^ A-38WEyNriby:pfF#q}ae8Ҫa%``~e6T`θ<ƣZ |̟.0?3`}Op?x:'8O޼o33dxn iwv7 naǃ'߁s|y]<:aVuԭ' pv'nu\cTKyHMMstNCFFF[sUgg˜ ) n]xl·4쫂4>bVV'SsܽMV>+[.g,]J\*D*+kѩvOie{Uͱ/a_}YC k܎ +n.4"merl>!z e] ׋s/ͭ}I&cAsX"k@WbJo7BsaaEMv:K/ks-1<#-j6X7xZ啪xhNXV,֞lr^{> } 3 ,EXV+Usba%A>f@V;vV5sU%0_RX&0!˪.;d|0GbemgώZ?'0=,0'hgPx^3 Yj9w\qƹ %8ΐ ywİ Yk0Ow}s-8];UYUgXOu%<#^9WֲsTgȖm³4{٫B 4 ޏV |>2=Xrr!0uτu[ת>gӉKuN|^t>+phf3XX>m5jMA<=i VlaΖfq`ߚ@l3M9v.Ik_^08p+ykpzVND [8npյQ-`\ꓱuzwAbRpdW 5(WZ=*M.9!KPέ/l֩=ϙ"^aUނeds.d ,,+`9 2ܫ.G?X9[BeC]^'cW@̇AWvx%S޶} u^3*0pWVPBggIyVJ`/{{幁gY* ${6U)Zg^wo!ǐ&k Вrt}>aOιwu70?u^H+;/[ҫLc)-|k^ykkn$h/K n/{Fɖ  ;Ukי&qFîp܀eN qq dqAgneٻ 9lg$$Ԓ8Kn? 6WmP~8Z|cTTDp;{juͬ ,Jl׋~vڦMtmW7ϣUM۵\;yoTdR[ý24S:&kc:I&=kH*ʹޘ*\^(۹,Ue=U- 9 XY[XA%H6`&$rNna |IFJWOGٖ4vد !. '^[aYz pn';-p͛7epR=7s50k̈́lxk`~4}2<198^g(p/8 gWyx86d-<9e6woZ)|ɮ Ze𰫫+UFu~vsnOu]sn|F ˪'}o\R%?<ךgb<0sKxHU0 fLC>ga0[vɜr@cIMha,kZSj:GR.A`+H5Njt *u ezs4,sӼ:oaK nnvo-|X*g.ǂYt*MՊ٦ IDAT웤{f9umLNVlTt`|tY5H2 %g@ ɤ S5j^b:ƨjrSՂr(bP-{bf0r QI`g͊ 6gKY6,kM^&lgay"ĥӄO@VCtzStbƾ+^Xl{ۊ}I]N8dHz`ΰ̛jZ^[K ̯t u7~)_592|j ?WyqxokUp%<l8,84pOkp Na1uOm+8ꬪ9Yɵe1,< z`< $,2|d~ 9Xwkc5yu; dC}F}M:e ڊ*tRxmg#Pja p}ehOJs(o'w0+vfO㚈[rdwN,_/5j,o®_YTg}6l_ ^u $Jmqo^;C-C+`\!Ϊp{^oi \XgA:pc&uʾ J6{tlxW8jkR/|f2(g[9 D8}?gyMKx^T7=w;5a[%3Ugsٲ\UgXs:dxΪsl3;ϩG38Tc!zY5-yY4q tCV5H 2< GOι-xlݶa%PL_ Ynvl#wB2>S s -=T:䴲OWZ\B!K^$B}:_Y1T8vcg]Ub5^GB̠j0!a\VPIa(Y0qdp*^쭷a|>aYeLݲ[ngXeP} !䇣k ˭|8@.ھa(yku^`˻ _5*0opWWokX nz0{`~_x|pk<k;O US_?|s>Ns 7UggAZ_Ty8Zy n3<ΓƼ[xVS۵vmc{~S\A5@jfoE36GwKueg+$*tInB4T@imIU5lة!tm ߶v:\"^+n)8/~t~WʹE޺@c!_'׼q~kdm]0l`M[N5kK^V?\뫷{ Ȓmٍ󐚔]](-rY =$k `/=NhnX3,GNxK*{$%j=\SCH1ef W V{`y"&e{dQi(rNQ4-۠/ey兺|ـ2(06˼y/S ː}`5̟"20k[.* oVw'`4߁qf} 8oWE^ppVvk9wT sgP+78Pss c[&nWxލ#є[l+4o;>`:)i=Ze'VPƒ ܳ20lʲ7ey^rl>L  Fg[6Se떣(<'[?i ,W@^j' pN]n.S;`ˎ} U]-u9nsp|cOdK`mh.\xx4㣇f7۽/u?+p;gw{(80x8ϟ3 7:gpU y[uW\G^ a>بa\ZpPxΪ3<ɵRtnXJg7֊JyJ9xVui{dm`U`4̳9Ot e?wTe@<%U;C`t~@CzZhpC *Svrk:ίH%8ռf[0o/I7k{jN7ƃ֧JoҰ +,k<gpn{,ۂcZmKv@Zz6:[ 1 seP9Ab4P6p3KuVf pUqnayifFS RAR?ܒ| :} ܨ.Y$6QbȘ.y'A]٧BP@fU+01L50OWyא~Yn&Yի yy0nH`'h~?ukp~4}?sw^4pV=0Ź6 w?l3lZ\mөڱkHX9Y{ _Nax]´j|RC8hЪ:yՙ]Vmd.| Sܹ"Z\yv;7mS1|Uk ³Zgsr7Lg@콩/ٸ%Y\?]ab.f-akI-hjtUσ~kknH&j36zo[¶O>a}{?3X9w!ciюj7(VlU^HzljMpڄ% g%;vVIڶҾӚ^})x52 ҹ 6_$*7Xcf2=$Yà3zXAi'iaĖ>(,l}Oܯ.CcGEB]$w1 Jy]ir} ]ln/l7ur_ȟ/{YSŋ;' Cc[yu7{px'/l*wW}?5pp[=%_rw}w Nuκ?9xƴ+s}ɍoQ{6(<ϳw ,Sk_=av+}:23trY=T5TV:ۚCQn ]m[SKuSsMOd^2L̛,RtZOU0ĔAQ$FmN竺lƛrYVRsl }<̦$o2-㤏$ ̯N}:6+`[m)P2,RWw a 0ӇfÜw8h~oՄ~4vly8g`p p:r%,;| No=oa77J?Euny0]{ny̦CugN.C4M6=+.%L^^7۽lUE9zVz8^~z`TLRڀ[2.pw7j_7$p6jõZkU]/qfZ湛7 U f/&z qVTUu&[u^/?DjîuvsMrlsC*7u"%}z :' k@N(0:T%YU]=(5?yfeyz|| Ƞ47(@ex v"GWY-l}[u!Yι\KhCνSn)uʗ;+ DiuUmRuVm-Kf"XmX9 ,6uZ;lneG<PVV@۔e}B Ć{ji"*^u[Pux~qqNUpY{R\b_PE&(zN ٥n'guU͞A|n e Uy|<6,h=`Y r i{Fvkb ,*!֤(O,a0DBq.g`na/w.=Vaw|ԖR32TYW}kKr 4+,C{/b.k6~>y< \~O ?4/wsʎxw/` m{fV`KygKU8 ((Lmfs)CsnUaپ6_35 u;ïs!d5uW D󰰯nwM8V^ѵ޸_t;QFеږZ]V=O.&/w;zr.DgL^']xk]cv:y;ΜR[ js, [9xĺ@ ȹS=HSǜGj$wO4ŹBr'c a|[TebgU9F.j}rUuTʨ%|1a'}d{'?AnH`.O8Sp#{u9o?#< IGz+ u`2;hrp%.Xs;M>ay.C̫eoa ~ f&3,O c9{g2Ufx.1X>1NqGڮ>xVuyv #i0+x aKitKYWR>jXy>R,9@]Ze`RPjNXga +z(鴄b{PynzM}_[F_X;7ŕu^K~~H~ܲG&!UdVM]nbƋ pgpUy3u^$۵SU;/RnSi?DȨqVdύu3 .@9kk >TyZ*'[gnTo *O2=Cd*`i7a9ce'a,r?0]>ay'8jO]Tr0\-8^ a wye_qMam&d-on?*[a~ '`WHޜ'`4ck~΀=N[3׼y{mEٱ3n[u068/zp>yWgΨ;չe35^<Nn'sݞO=  ,os0γs-<Ɯ @oǕmƮ5yx_&] .[PR}Vm siKq`ɬK4S *Ѯ +YT j۪otwLAii}*p.5LUV<6(oݛ$es˕,kֽk-wYypT).!7:pgq}Mqbw(6ۀ補Zh]~%{Qy;&gXk*?5 Ш( v6qX繨-,1-,'Yshmj`ꛣԣT>T}nayJ 91D壵 ڭh%eN8~!e [q_SK򵌻5k;`vR?* ̻s__nϨogkn`l2` f0V~{`^R`~pjͷO 'hPМn3˅~*8wWp ;x z/~_:7n]΍sInZSeY>,)Hfnk[ՙ=dvg3t|oL;K;vR+~kЖUsVZ+ [;,y պ렗sƾX^Ebm}bZ_y>*@HsWT!OV5޺RrH.m`{[UF?=6ÇbgշFGݫ,ϻs>ٱ:S|S&7LJZsjSyV8^7Yң7 _5"ޗp<7qq,^j;ʭ`|32M(Ѐ2H,!CTefHXU xQeukXdA~U٪pUՊ=^p`Z{z.?JN;U`XK.C^]o_Vyڱ-2l3[fh͵i4G@3|" Mν˵=8Bu~n=<3@|$lyo~ιyms N<;a{v[ܩ+x69D}}[x~ONUg؄LGhaa[ ;vI{?gx1ry|VmmyȬ}Zlg꡵kOWk*05;oǃkhP9 ]tnm9fYXB7:ikaw6%/΅^.is}ذrvO] IDAT]KPUA ΞkuH4v {1v|k۴:ODrzZ [Z E8R<ץ sW+288幀%^ne kZ FmUUe16^{Uy~e= %i /@|*, r:suĸV,CGzXUNmzuLmݲqRue[bkyeg:7]6hÍmơU ]L24/|\=;gӱᭀo ?J@#28 sKs ܣ:G]28+8ގ/tSG盻a ` _}:_4e;6a{o 󴻠:?a7t_0N7o/ѺfflAamMyn-j<% l<:KxnGt]gH9ozǠykCUj7ZG }[97 U|O}u^D(7v Uz{V A:Hgѐ-7FY-oyV)%wU \ ͹nҵ?=F_4jmOZ7gu!g¿+ v %mjVf\KC24b(J9 3Rv|ffiqd\UeVUYGUTUz*oòr,5OXj[+lbz`-랢.IkV<W] MeeS+X򸻖ׯ_Xxˤo qWQJf(0Wu[s| I×I鮋 c<~ۯ.z\[W_ 4<Oi3_:OEp_; nKuxCpMgSu*S<_t3|AvJog [sVUX kr<م\'k#}Xa{m[h\h ^m׵9bcW @ tJyKpkK$R %He2$ ҡYiۗ =d/1yUͺ <;;&Jc1O/"Jꇌ$cr<0:.(7rz@\9_3ݦMA=u1P=7[oejos82Tpna?CrbT@4㡁lFMKn يPc6UyKr[ IjWoεP s$% ;w|F9͓c6,;[gg~Z<?,ұm|7geW7fžRy3?|vR͟xOg|xpq}!xΰ}UYW7 /5?lխ;YPX[3$Eє*OմP( ipNUNMTs1G.{ ޳ Wٲ,̌Ȍ9XZVDd2)R0+O$%K|~?/8vTmpҒp WOfO 48_Agŝv>Uso3?lnZ:rjArLDl3f3"a1-lxs͸)IKW K5$qϻQC [N8ȥncg [pZy`# ;~$s0cdҘY[;& pYlcpl>pl-l7"(EVp~0݉n^S}}U1/Aż* ~K†*cine#x5VW"{A9l-PXi0X]E-f:~ESnvNwڌñ0y.M(6ERZ?Ā9.A M`!V%[3 h+ꋯs>(4iǧȯ6ûz(8.nٝ)d{.p 0 ³:=<ƅfp 1x]%Knoa jgЮ}6u1L,8bmk3U<UZW[d[eL]ofl{>˒*ߎέwۭnY}7c=Q`,BQs`VDq| dY^Z7HuMV+7L~Zo '[ ;7P \<2x[r1-4.ۆʟ=m’npJK,3ŭ\ĺ^P sA62Y?kMwuq8-v?±_;zFd,|k,崢(L[@VR(Lk]. Ay+`9ߩjk,OveSjؓmT"*~Xq['`%kX.13\b~YScx׺e9o*8R[ .˅Es؃/楹~ 0_wëGf]Q{kǩwfh8wC3t3D g\!'%Hp4(]sJ˥'tKiX/_Wgu_qʅxxUMfqKC4b~o˜f uF;m@+@ZkEd@ٜkhtU[)s~#0zt񏲊=̮8 #m6E./U0{oOpo'ΧTLvVQ'l6wY=59K3҄k+r02Ε .dI"l7_&D[3h]\^.p B]ΉVYBv>@h3SD=DW;DntPDLdmpd:M4Cp0/y5vUmpssCn==cpʯZmÆYpp&lE%VNrtak-]c , eaH0fg bq޼z00޵ ~i`rp`!ưW#4?n=Jpx/6簺);!ϪsHXl֓ƹΕy6٪`:,u}1\|g E1;mut0O>i38h/#0z`~޿YqZCӍ7^ z<\דLV62:لm<yA[xVIx&tճgPZY@7]eXU5i wuq@Yx͑m6p [D)H H,dN{9͑}=X\ϭ *r$}  *MX:R[~wDrtzNeo+#L"=ɛ|[\L$;@&+gۼb]Q;๶E9T􁲂l=Eʵ`ɤhvc ۢq\:9r yQ`˦z;6Mr+8N2#Me$,e.0/`r գ*N`,. ܆ei< ?OC4? O_h3:+p9K=wM3µa͛'8sJ5U@}jۛ͝%(PUYƒ d[tLU"cWX5ץ}gj܅opuEAɮvJQ< XD@Z^XwsYihtpqH8mPt|!=Xǯ`꾝vM0nqXxbLC=N1@ 9 jgչY $ɖk=Y+9ʵ3l*u *Ǔr8|[]Mdl`YX-U9oչżN|mo\f:;ile/y 2y{h#eX\6''ϛ2$8_auoaԹguup~px}^ N pCOy LH^0YW>9Πy4E(+lz ;/ϛ PN]e`˕j$0&.qvuduXqS},B_`r ,弹 0. fהE#ue3]aY=rnoeP|N2(`HeP}O1Ņpl0|0,p珿#4`QϯC?r8Af-8ټ๊ir g?sy1YkwΛM:߹ܸV\2 Ui{׳R,LlUe*3ͫh癀`>OD@k Цṱ8tm*YBh@]`u>j2mIgAW{z疋"^^lf/˜iJ0mnx*:pyֈl#"+gGnr?P `Twg;ȭD w:^!!= 'ϣm}OP}Z,D w dVOQŢwPy\ 6o^{$DHXU&9Jm9h|UAnSۯ.mr:nCw Bm>u vÖ\wn}ڹl⽵ ?sQl,ok1܊psEOn-w2T|ݙyN# \H*vAYAhX`=eaY2@.uO8 IDAT*GBQC+]kŬneN2 >,_YCoRfkLLyKM5Lb_Y%`<[ˋœf?|^5ן4C`"<n^| +Gey!̣X#4zGQAٽ&5f|8j:,9ZO)/X="d@f38 mx8lOx% 3l}u\0 p w\ƚgo[ êjjot% M-s[s[DKZJ]4Uש]rTͽr{ź,;PƸ~ l.-,@YSmwP{n {^v1mzڠ=Wh\ e wΞY3 wpK`6b/ehUE;\ҖN|ϑ{gxB4~2l}1 |(0{S>F`5Bw GhKWgCZ@wx63ΰ P+=wџ܂g`63(Wyf6{6\ ېh1It9js7UfU۪6\*0 !3utga1s"SLl+gwYv[_HPjdv]V]=D6CpJa{ŲMܙ4 wæ-XcCVR?yVUM1b]P}5F¬ͳmۉk'\f*pƅ][Wmrw9UnXh'8/mᖍ4luuk*;ya {Xv:Wn:5y?Xβ>XN{e3cp'g}`0 ggiqi0ûj'%5G?l[p_:Π!s< 7v>;ӕ>:ٸ`L=TJ] 9ϦdzNϋ]xg:y>lYox:PlC-'ZTjUV6JJ?@@+Ղid8,2jw?tM`.sBg6{$mZebc[_dLPDX X/k3?j}}= a(0m_%,ûrjFh5_7  ^~A{ ~iS:}EҞ;!OY<{k3gO3XPWsQոn[u7lE@&N38ηY`40X&:h():=kjV;$oX1 c4:Xe?HNVZYx+E?%e ӽ`t`/yfay#4$׻waoppBp<zupu>4Qmۄh7Ipv||g' ''D< Pիs5$+9 a0>2ZEX@gYlBve-8ԍm l'J17ڭ"JʂD5b[x ~~0<DM*9NiUчW={:pglDsyN :I;oNOF{Y }vOԊrZP=WmP U.$}z|ms%ArczO* wv\:e1!Ymthnn@5 jm]-;XVRXAU7T/ON7|- ̗w3 0WVFyCN}Xa;3t3(x>a?xV5<mxuO)ZIpϛ nlk^s,tT' f7Uuۇ乲MG ߞY~60)S -z:>(ymZw@Fl=\mDz0bwR@ ԚvNLކ&+z,}k63S܊9E ,j|*QUm=)$Z^ Z s͍ Tt.P]66jWZ_kȎ8Ow@0ˊmXVm°o8Jaم`+X.Kfv(6tm$,.CXsN`~hS  ̗];ẹ__ȡa_oV׆8C{3yYd]un38x>g3={v]l!+x*[FkWe!Y=9qxVs:-t,E`:ͦ= /P/&AgmBl`XnC4len (wkCK>2~/!r~;~?]HHTG/CAk8y OEn1dvvH%k3n zq5 BQ]ZVFC_ċ`NOY@P֛ zFԚr*Nj2;FA[n)6A( P=a.L50KX80;X6~XY2/^|f#^\Ȭ²}L~<G?.}8Rbs As_;)˰?0_y;ңԽ %8C7<ܼ~&g_ manx6|UDs³^x>NIVm <*+2/:+s.z>>+@j)xzkylvkXA U/lS =a VJyI0m1uǪ vb"Z2ﷷ)yǪohzw GsznoVv!. my>Ҷdȱ0k l,w y1m6ky@y6?ݭrvu庘5Wv뻬\a.Ե;1_<ю]*D+XyShwY2(`.]sÍw/ j ]2jx,/4 a.|a eyw #0z թwrRU3}pmo<_\z1 ͑| CŒNOg[tZe<<jAUR3B³9 Mq63&:d(Ǟ! p V.iHVE7j-Y'gkF0ϋiAlk(`6?ݱ֨Mʀ(C*Q3*l?}9kJ(ߢ卭 <(1~<5nˑ~?/7'ːg~eׯr,CA(U `_xy?UG`կGxV):[^޿!ΐ ܭmsxNe6+-Z wsmy:ɪM7=h-*ux3ǽ.<<'e 8 b r*uNEƌBP:RV0o#JfP"{z r̻vL%1*|b `7}J..w`,nj$,U:v8@v VEYZ@rnz% ȧMQΚ ({…Aa9eh2s8s wayhMպEY6w.wͷX>o^o⽆W*_ZPl|OKC`븻2ۀe0h -̣Fh~<3x~,_c3 sP=SFSmf?Q=$!anׄAΣQ/s&y8`| p̄Xo: zd.f<N@2h7Y+ʬ`e޺jFIֶr e!²r²euX]Nòs%,?+ hV؛o1XÁ YO|,A,<ͣFpK`/E!8px~B}w,{;[Y yyWfp>amxϪU}V!S \j֙Kgmsm# >$s_ !tg`hi5 ڌA)0)46w3l~cks۰oQBuҲcp?G4\!;c!!Qρ|mg'qMt<1YSaj$[N761yt#* BX8F<X H.*$a% < Yݝehw!طs| ˓NqaYebexB9,NgYJ> PX a /{F`hQR8 qp9g)3lT;?u|&ٺx^u/~. p?Z@?͛rZ ɉ΅>(- qs]} DMk*QhwgB[|N8Ѷ?U:?/5kH'= -a1 WgԙCv+1xVb+Ka_ԗmad\pkuK]^A Vkd/{_^ P^w.*l@nY\츹QB^^p-`;j>=t_@W;)X|-f8knu/a9Y喾,C?0H/0a_%yz?:p`p g3?^G]g%+ ϟ<+xU R F @ -l>z޵A[4@p^;çea.MBdmu9@u)>u|Yھ4p{ Y'"!@YEkVv}ヲ`> kPòg/[X2X7|GZf Y~`ٳA@}a 0w2W?\<~h~(-8q^Y %8pxfwO=</yO?$:p/|79?O=M?<x}zgаl*kky*j3dmЫr! ]hmz*{\T m#ފnۡrZ@l&dNY2E.oWkXw!n̚eٺ7xG=:wSul a->ߴ]ޤ\cM\aHu!~ۧFK@ic Y.LC d({1H.Y;\A u97FG}OJS IDAT,_4,.X.!ؐj,y"W2w2 }ByԇGuۀflxar8à6UyMO?]s w]zg6<.@a:Y / gu@)aW]feYШ'{ELhvAXc%$duɟnP8k(UV)?j8W"S0mPyOLPos;mQyyy !y-rAXC6U3N 涟PЙet ( >(;z*jbkX. 2M,C\Y6<`1eU[3rPOv~8,<k-3}lG̾G`5H#4 !Mxm:lkE0> !`9 `>yhSrOj8YپBM% 3 *r+u.ta68Qo7=bHǶjR þZ>DA44W{}]DKi~XIޥaZA C>y+ވs74Yj<Ǡ( qx/{/9p{[d^og#w!uهg6S!Y*%)@sY2y4@a\i Y`:8@;^2js|VgA|ʦ8 2^ <#|:}~aNc`w# Uaah˺/ @70w2ā!1X6Ca _z ?@v24Bz}Lpp|q!! Ϯsno_Ev/y ƣ\gn~Y]o6o2!Ϝf >ݺ#0[#4G08Px~(p6,. Kj|ߞ2ā[z)%>2eyǥG(Tg|xvghók<y̴znó:C !t$ @sm*K*,}ixwڌ]}gTדlaY EbG/j*+-Ɖ9z@w>w@Z0ykv[eUmht wJDHqk 0 8H8yWs+%WaAHl eiB͘ P:P61W,wfߘ7YZ vP{b2`5o:߾+`q .[Sr!,Sp\`exa:~LayԽ4BC. pW]lه͏ Z9FS 1`|gg 2`x~2<5B]kGv_ ڧW=A˸e`>\g3ɉK>³g_=Yg:>6@w 4{SL p6D .ȸ .9 Z;kٽqn=`4:FC+u:=Nue.ս2ϡ ⅸulYkRTzvH í@s}Hz_&ځr&C˩r)Og:z(N6`ov*j~8,=,iwÆb],-`~D ><zG{Y!/_~ڻPx~j+}1gyѽj:[}O+n? tjZG : UPDA6HнBίWcaIKZjk λõ#-lEe 05.UQAra,,F_`Tr/,:;MsyCJ߽n~rWo]~?G`uT?<:> p> 5/';_tSzjC#,=tg4@9󯸽)ϼQ899 Ϫﳟ^\}6XJU"DPsB7^8wv+>DEt꠰< ݉Zҡv0VufARyɪ/>ؽf?.oWCo4`eK^v#:26HA[ j)[|ٹ> rI [B`׍t$|Pt1/nUM6(m+²*{&g,Ǫb7m~CF|0T#4z(}y?pww>^o{ڀ3 g~{doܡ.y)"MX |`ڞG*y]D_;rs&}* 啺OR)"f3[yƞVT4=ñ.˰rɂ]b@Q+`5)H. ƺMɐvg{Bcʦev~̹~` C2Dy2 >0e_׃~2 (,ûG=Fh6tg8| [>lh FCb FΉóqbv֑W:3Р <2:3.Nƹ''AZqjjJ|6]>zWa8>P`΍@"’uumw|ltq4϶Ń·N{]OJaeۆb;[9!JHżidӹ+/> ͍-%x+%ʳ*OljzOׯ_Y: uc $e1'9Թv1ߤf*IP64?0O>{#GUre=dɲ^i0 ah\ټá:68ouVU特+ف;g~(}yNfy*ز(M,}0X6JCsy?>GXNA_p8< FC:/^DG۱mg3g-|>N_C}0Zt6S?(-څu dq HQMj2_ƁmL[k#9KsvkkB`^y E&9p!7wkE9kK`%©{ [H΋>nTl+X[Y)ySr#,D8$yڐ ,w2Z }XhP_f?WЫKOO/療 ]±eBR 0G=n<]=pbauٴ:_x_Z7%Ds*a0gga0Df,3<6W^g d{4$@ٹp,+P& RuTeTXe6,\ؗn <QۿGz~y4nt H_X8~u]hnPE/TXG K}v 3Bi_e8 QChf7:&C6rEZ=­A2ݝ KIXʧͰB^J''Sa= _U}/i}懀eyǤG=lm3˗o~Π_!g^굿#ݝϟˁ62. 3\g\nj\tؘjo?AϛML b0ݧjapxT{8Gʣ>ipNvgp^5 @ rc_Hv{k'9Xs|[;oY ?iQ^,!1@V:r b_b|?0?(<ͣ_pn罜BC:Z)x@9 ʪA@wQ  @FF}6VL w )ThN>5v`Jg̣>ҭt?<p仪iAbjԝ>\~xtʾ w?s/ηlY7۞ ªCxIP|P! )H9 @ZCA 'ް ?=O#04BǦ?isrX~xVիkx@M?tw*{J1zz~B?KַWJSfXY mCY500aa`c~P4,|Hq߱ak`KŽ|nk?(dP^,T?xA2IeP6 a3_,K _>yԨͣ8^|pv}٨| " sax@ު|ڄ{ @h#vvA48<YV~.k͇m͢A~^,H2C.@6$˫6,`TLˋ(AˇF~ 囃a,Ǵ0XGz`Ёw24 B34C78C7<\S=Cyx6r=gWPl<`\f(k,}2 grއ;תgv(S= ̌e Ƽr>@/cf35\ tZ|;d0ѫ|6 }|ʹŦo}ЇҀsσV^vߖV_4Hw>j'ڟS Xr~Ś+{}47^Oxf6H~|2.P~U>8ze^_l 2_\,:, xXf6) hΧ=],Y(5̘~xsnot-ԁ4lFts]Ԓw;{`\3us)c GhOɼ[ ouTc{}^bb'Ω/$?;]S-_eG$˯5IW=6/%Ray=uתl,rIt܄`2P*el%ө3[@@2m,GcP\\j7}ԧ7ߌ0:=<@ḛ 7Wm o"u{=V}Э$] _sd\ e{ ]$±|}-E2)j0cY̍D1,3uNgϾpY.]v3`m@5>Rmj\w6#.m]J,@r X6X#hA:HbYLeO](Xv2|<( 2xR6Fm:?|85{3̀Q85>|x9ZWgHiVvBnNfnw{ݕ~u1bqĖ$羵F" ]rvX~;wQe{~P#,Cy6{z3OYXf%l* Vu y8X<' ~<\tπHiE> 4 ݽ+nh ޭ2m1^.LNٮ#ZQw[1b]ܭzHū~i|mw~՛U?_~'CDz S3co￷v6PX~VPy>}#Ư.UH׹`?G .u,h9Qq$,׶ۨ6=mZd+f®b]z-Z.U_,?*ex"XFgtpZ΄i1mVv܏@ h.xng j2W/~ig H³ݧ<ħ%uW-4&q;ݥDN dH@^ȱ*(F׹@hcٌd@@"tݗObbJ#3c)#[}ep#sA΀ЀV~񧛛˹уO@?h}±'OMhK-FO[c42s1`9td$h7[uQR7|XϦc냲k [ey̡Ptj0 ;ia̘"1u^)wj<Ǽٮhe8FW~%nn}ѝGE>a|5u#/~}_WY'k{>YLkgu*5bdG%1vų%YӞR1r҆bP`rҎ*/6R]B6)_(?{t?f:$Bgn 0,?lXsLcXf20fWk%{RU5/k= h<,jύ,R!@dcxΏh/H#?{$k4<~;ny.TX6!YƲ@˦d0c9?`\c $O' s'#U,&g5+֘g<Y_fk|-ՅP{!vwU/MeEbBxXKnx,CY䳞r]~-' 3@43N>+b\-w/(),?nn/_Mt0xFޚQ0hQM(Ȳ]akMr6Xq2Xer`sʥL,(3c̘[303L{N=mQK¯KF(0F(jf,XffUen)9l#M<$ m|w)ЮzZKhM['4bCE%FK_ Ō&^Nfmk(==2/fԈfTavIx#ՏNh>L~:><(mKGqKC :=2Is u $]z<49cchf,o_sCg.$*(Z9=EgwrGcG436,_G~8I. "Q=fr_mpw8ƥ]Qf`Y8>$e{1hfl^DžXL8#[,MrNTgDXɸv`6/Ul Q~1dJ.N=f.l:`X,Y~򮐞@YTGo6;΀.F ٬9 7)kOm(ojS 촚l'9c-t0IϩA~ƝZ^o~ynt̮yO1_CicQLz^YacB2Y.ݺOz6:;;\ c,W[|ݏ=spq-m) g@5F\#a||,7o ,{bvYaxV/;c,V{gE/?z๜ G⺾ObylάۘvOp1QCCor"cr?J90zAGfDAӄtQȩqlZ (Ҏ0 eB1VZD3c/ch=Yr^efŗc$X .eQ0eՏŽ1VjC53FNgCclI'%Dͩ6 ]C:Ea Y|)"iw󿧦'&?C?SV4Ve6!cc,CAfY|x)rYnr57q%4[Oe&cyH3c,C0L&St,(quϖ6`L\"1VFBkAaYb7=8qCz 54.Dv̶}e-NdIENDB`ukui-screensaver/src/screensaver/assets/logo-kylin.svg0000664000175000017500000001210615172041035022207 0ustar fengfeng ukui-screensaver/src/screensaver/assets/feng.png0000664000175000017500000174665215172041106021053 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATx{%U';y%+!F~a#ےab ^eG,,f"/ ,0 yL1;/1ba clc[~nZRKVK0;;'dVfV}|u:Veefee9IeY(;/|+wu؎5SɜXVZkNXZivO0Uo5 ؚecD+kbv-;ZaooX.ށ5ڽKם{..ug'μoo.w2,.wfe(Ef^_\>,{_l;~OY/dk -3Ɛ$=iZ"rޥxQۥՙ0sC8O{DS{?[>ič}ZpYfٖaYY<%K veo`kc_*u'P߈ZW᷅!7OW$/ P*:֦`!쯎& ߙ29"|Ґ!L xI^<~J2,[ 2ˎ kq#ü_fm) `qX)΍a-g2 x{DO~Cr˲zӳ2K"3@e~/'[ e2txD(Uy{L֖'$(ŝ1(Aatѝϱ!3~s-8(b=+B1`g>F |h|Ƨ~2KQf0,J1&WyydY.9,Qf0U-<;_WwK<;zT`~{"e i9BТ(R P𵔎P!Sp΋k͑hw9\I%m(iTA8+&IQD_ )L$D12N+H[4-l %g]hp||@nI٣SDžw/zc2 r#ᙼ_vx%/,_(bBZ^%fG0,*6Ah5<(˫ .z)l] N1~ڢVcƲik'hG9a^,N``]z4XnJf]#ܽ'0>gv{{-/|+9,Cf0˱|ZrzwRa d 'pYq&B c@5JW*`HW%E5 k35 A`leRTȽQ1K: XcBLD`B^  d }6 G`1g 7>󣃍4,Wac#g=5Ko~B(l9 ϷEЛx]o)\?>Գ4+c츖A D A@}~q$62a-e2EXkX+ZOKSY-FH#u·}nϴ(oT z=O~Y,W>+߬t+Ȕy$ >ԳdoGB'2 Y(9 I.H5ȝ,Fkd]F^_1 zED kS埰V+%@B'e5E ICڏBqƃ;41Bib]V "o}- A2`f\ѻ 3$fh fCq{׭߼ZgP:}O]zabOt'tP4u]P*keR' S?$,bE|Ƹ&Dz n !U> dF , }CHܔKcC'hwoz2ѓ rEȹsnD;WoB m(OY!PǂQ+=NI(h"w`e3^2.Ny,0xim NC*(h@\]YA{ԉ[}@T`vE]ڋ}1 o5,1,GVgpwYX- sIYN0…3L#*xL>x I06{@vRֈv D H2Y^9R=tO }N1O@4)J34dS. Y/{A3 R@$!";Pb pZ~}K^UZB+3_˱֌$q& F`"sA>b$K+W}!;X |>Ql9F; 33~{o~2gd yߩ?q͵߻Ҿ֥V^9hx YS80"C渄QuH$8N T[[R]z,])z$7DZgkVw < Kn k7Uj H+ āG6*Saө}c~cwre\V9wl߳+^ ~זAakQa BΧ|%X%׽*žΕSp1 B[ t~)?S%~Lb@b꺐go J3 *QnRH0 %}>P8Or_! U" oO_fPd\9c )rfP>sx/k @) ]$Z!'$eqTIDEzau!q3x/V΃2~!H#CIY Cj rS0^@paL`*;H,>nVytgcsI8v3X#kPbq/5geK2YM~/r{Ba dk@3P LX.з@d 8$#?] T=6_?@F$JYjJ4XaZKBrsbiSrhKp~&ƳLmJsѮXQ~4i=n0һ=p `T QA`"{x떕`4n~ވ̲aə?"n_L#(a VeP {,0Z@HV@ 0r+3/Ԥا}rLQ <|&.Ŝl41wɝ! @ "/=KS0~[j7Vo (. AqgܽGPPh'޾ ^wsY֗ ̲u9ׂuRq#jOCB ZM!: yPA6fsSe}qy_N(%i!_&R{rj`$ZZ븦 *1Gz((pV &`A3|'>S;N I.)ЌC`+ϲ kX=6,dlMN? ;<1#Kw-T؂0{RAO$!Q Ps3_v2Mǘj셵eW!뭏zߐ ( pե'D(͈7")G`FEV묚&13UW`gΙ=~-7>֚e2Y6:ЅwKfIaIaHY?!@P8a-zTR ;|z  PPr/RƖ1>C.= :B(g a(]5HPʚzwKJү%}@B*@!.)bB$$T`b{$˷`rc+3@e]#:wxZS7ŠB t;L)˕ gyʤӾs0cu@ʱ._!Ro xc}M'J+% e`@)DR *VAbqG}޻أf[_t[T2Jf0d9w+neRbI|A`5ibzCq]X[AO+"n82Eq%s a-7>F5,x,Ͽ }X`v<\0y~.ORiǕMy,h,dpUWp/+%?2;t̀ƵRէ(,#Lz!V1.@(]뮀1ll&b\+ 3 q7-?X̷MnYJ,r ߵZGW+SW'@ 4(7yQ@E՜,@ 'gj4K]Po CPJKij q]d!{fK2ȭ(5YQ]כ=16Vn4[`O-?{K'7,WaZ}R 4Y @0aB-lOA$} @bd*e^&(؊gڰ*!ѮTC `LJeH{ ,XH &}$ ^K 0)YyclrK8?8AܧOt{rrU fϻv^Zl\}A麔(cXo)f!iư(XʖTܔ],RFXQǛrA{z_,< A BaDV% [󴖱O|?lY:,AN?/I4o~1NW;'+gbnC؂X) H>Pʌ4K0-]HtBL zHXJY1&0 2KdJs5%RR^>ҽ$eRG=G 5*ǹs7Rk)uE6@r4SRvm\xuنW.ŔD / &] EiA 7޺ `3 `zJȬ v\wyHH0kޅtϻ٨K] WЙsK׈/־`v%ˇA HS vUST r!LmMOoS=Nu! 1!ăʔ鿡2kʾƔ+5ŮPU.DEb|<'5TwLw= u"㫃k:?71_Er߲zdHY%F6+aAuH !_Bz cOsQpH$3aܢJ$>Y(w@ sט?z71˱ \y_oY^꾝⬂_j D|%B&b̀Vi^@_aĴBXժ}RC0 ,g=Y BJЫ)MDK8=Z=kJh @} }_ jtjmxh-$.z 5A>, 5=Gk#gaؿs7Oz+]fpsv|i 8ma)MvGJ9 o cX[vO0&o'Y~~~'J]ޫHVt?P9Т$53"׏u{G0s}F!(H7ERy  L@F ,ѭ/~?lYX13g[//u_ Rkx,}> (zJkք0S cb= 'x"BTa5o%89f"dP{艟X-W?i;Yj ?_SjaL8kFf0g`RMTSiKaDr =X.ůݥ LGm+^KoA5t-Lmc'>yA!ƆV= "l7 V`D dDЙv{'oE'4,WəUR/wGtD& Ewp[dE]\|8%9UQSVYJY5KYSJ.o-|gIY4."P¿@Tl <]`Yh o{ɳ~rNfdWytpG5PY(Z%)9`1Z@彮A_WSu#-L7ӂLB Gx;\++aL=aMS1Me!@^M}%2 eYQ$fN]Nr%%:xpM_|re^( ^xAwruwa)RȭǮq#SkK†-K10ܲ{altNefXk{0t큈Xy' yVO% }v: <)V븉:߁ug{|B蒜1$~E;&±K$u$8` /G 1%aOY\y#,xxƒ_dꨨk@@Z(\OpNjō[ѵS]V\CVmM7~ Pe hԏ?.ԲC,yT <$򳞁ЀĖ,-i1>h8ŕO `{1I<].QhML`p R8 Jl}zX`d~%/ӣnn##3p}=սW19pX@5FX{JP'uJkwA^?,)ܖ[ǚN~Zъa!k"H::.[W3.)nN-' RvNZ[wǴ!|s*=Ǹc@PK)@䦶X6 :6,[ArN=[V冗=5HbGr-ehQ-."Ah`LTajaJZ_ڱ! ?V*[O%Y,Q^ BI{u3 ZYcczXU6`m'DX۹>h4ǰC$&XfBqؿvMA`hi 9}3_V|{;qJ k@*f|GCFek%2':]%Wa(HqA nn?&`A1?:5ھg4I,p0*r*3.!ŲߖH\Ȋ@()DHk_rA˟`&읠bnY.}UG[Yꯆe Xx#GFJd `<B))eO2CfJY=5D|jۣ H5昴Cr/{UVyI`Σ_Y.ʙsuՁR0aO\x>ȳ6*c/7Xڔl +cTW&umj$,R1["˸[e;NĊ"Y6ZT\ʦn|*mQڽԞnHckem 5&v~gFs-$1 < M  #|\>Mou 19.xߝ#Y&D]G b(3)pg$F7JP,G]/%ׂ*4Kyh1$+BkVu=&"d_1<T/;j{faqD lA'CZcD@>|_&e#$=x.]~8"?UI"Bh𿍄(J|A6Xk J b>j˨45]AdSoK Ykw:k@'. Sd0A˶BJ)&k[&PdOқIl+1` c5LE޷M'M`' 88ݮP3nOGkoJGDfps~[W] @I Š5pi JPY `BKkzwWSCX5O_SPCfJh)%sb[Įo;3:~ܟAѺ4!<ִG"^{ K䚱iK1{! RT#H ;oKv)AX\IM{tw|ӞuLfeϝVWPv)xe//p@Es -u &|s*xLQSt}FxS&Pl- Q'+AkԶ$&g-31RAJQj%fD0*34X+w}-0&2(|ӭ$ؐȁBXB0e3K^<֠;;X, cگ.))-)BJ&7B+L_nR_ wֳ6>v A[lC?1(?9 .#IoqW C+',Sc*=tg/=HPA&*R&P"َVtC.Lu'lC(ݖ HoX$o@,_H2jaaI-Pj!)vbNZ)+Wƀa3jb4S`Q|V~Zˣ\8a@!:,O3 . n `C܊[uĵz~XYv*3pr睼w^zŷ oCXA\A:k@^)tPktw!XTP(@ߏ\wdMnʪmcm6A%Q=FfCH`\?rݏwȗYC.vԩ ϵ{3E riB9k k>7v? !ʷ Z2 4VڥZ Aѻ,0 |ΈW֔ٮnHg12AwbBME, r=}`KS.KncL ^[oGKm7efIN= ] 8J_VE v)G`k{j"KͲݖLj@\Nk 04Z\5n{~䷩ όFےpMfP/xɚ n|0 ^m+<,AfCO|rcA{ 0mg!8!\ ]JwC 3iZ-KZ[;5m25G%mSU~K!deʀF$Xt>c[XL<8ۥOqZwr̉$%(M1r[Jx#z 3+& NΊs-`@*6!XCv̂yX=if(3c7\M۱О.+x \|Gw7}q MECpٶKa o׌C pzyqoj]tّ`s _Iٴ\fH6%Խ)ֳM3Fsb5HV`F $pkp0~a:|#Z-G߰Qc2(3@ء~-/ٷ8 Va VSAAåDmYyH=M:? I-uPK_;j!YG $[TAD)mVɑAH6 CiDZeID  WRdbe >kA(mF0zE~O>-49Fxhȝw^twWǙ [:{b "ʍ+C5K)u,kv(֠,[~4Cyib JXn?.y{7Y>6 7 vi}?0S@痗']9}N 2Yyݚ#3ߴ^*;s3bBH*0].x!5nV} d/0'~g׿ma (gS>oAwG@gB9 `AFG^%ʶq{ (, 202e9j5' h$'/yFPC8%vx 3'`6ɣg Mqagwu|6[d@n0`?aGK|_k7,.-ɽ>G˥Ða<%'xp ̩u"5nba*yI탗MKLwNJq#ɽZ= 5>U6aRr|@rR2",y!>e,7$X"|_Gwsa rc7=Ҿ8|'Og*8IS bb%Y %&Ji0+)0^ WH57(A0)b侳nWߦFQKewq98-snW:/X#y!ԧ@~!98 $$/8$H}ԟ}d/;BӁX-n#7nƮb] ʽ=v{w?--(He aP  2.-cԒqwΏqֱnɵCM"D^T܅Ru) C]=>^zM iL]]קtϵHE.(1*Wp!SHu j׵) qb.lc72昖TQ죹lpMʐ)HHו!ց)}ʼ#1 !|V,K#Ľh0 A%1ReBϿ˾`Y.W Bq=Zh1VrXEu!.5CViّ1j6ʻ07zDbAB`f|@pSoઑ_arZ]ĻV+|zxt QiE Z}]nV%c)գȭjq͂R])iw3 S|_T-+4TZ:YSKkpoIK풧-d޻!.??z|>@GtenB`#)Hb\1o ғA yU'Vn)8p} e"@( a6}cO)TSP[ 7%J~NK1L~J]vWke܊]k.-Vj׮ς@5 13G?NT0#qF2ż[rɿwG^=2rǿauq.ka >2 b ("8в`X\mQS|5*|7afr7\j#(-? m﹠nSd(aʽJDغuoҎc d$@}(4.ݛW<{]gӽQ\2 VOn d,/"yiS2+0Y+C6KV&=e3>5*;dՀTV{՜v?JI³A!`OI[:_j!!2Eb69F S\InYg]~}9g_+} 4X"CPEDajtus̺ oamY%zv75zWtkMZA%QjV;M}nzپ6i~ȶ]2&o /x;"q^*wYPp71+71 Tn>8S`! ӘzIjR:m7P^LGnf[`L^ Ș#1haLB pnVx]w}>2%wɋ[ 2FEw_?l̩>aԃtNs^繩.yZ]P~%XKia2o]ؽJiavR3v)GQzƄj !!'`ds H "txo+MfKo|wW+#[c$- 2ep2"C :VtM؊Mfl_4Nka5w +45 ,CֲהJj7޵΍[1&RxTS CBћ \X$N^zk'1Ap~#P5Bb=9`˰KmHXuwS֮d*en=s̐{ˊJuM? IDAT+%Lֺ[~^uJUKAfY}8x \;,c!m+j5?~l ^?m-@FD~,o&V+$U& yF5Z|џRzpo8xrF=_Brie  0du0 :.!m02QP- U\5%_ƘMt蜢) cjC횻s+&0'ےc6S@Tfv1UJ`nu(BR?20X^?ɕ A&5 oG?kWnG#,'~ux&C,0dFRI:[ت$^>1"~ů*%֣wU%?y-2DZC)mw}vX]V]4m1{}'`מK۬,E(M'AS:\<ϥuԲ6}qύ);uyGegyr@w:+;_&-Xqչ$U :g/~c#C` S s0$8E-|(]No9J""w9AKk)=FD\KQp$s՘ReݖS:ek&!d,v!Y, @D~Qc2~#y蹓+x wɋΜn>)11eC[ape,=EJYS޵bAPS벭z(Y%+snw >_M\+VmI@\L .HFqGau7Xp?`Eu5p/ou+{AJGCq.m?Ҧ/BIaouY )yfOW>T6^.1N 7d 9YDJ*z%g]V;q88MoaEw+@*i V>oM.Nj/S\K?PFٕ"ɠ4FLiXe65zݦP+vv}/I$3QԾ>^n!rE(G)rkku>WREw UՙScOҩ}H H&=-F@q*$/ ֒ui}kytZ)_zsظ3ć:pXDik#e^s'rÏnzW\5Ǿoy`eDBH.hQ?jl-G JZge:~쀤%, d8ZjU `h]= 6Jz"@`rJ?ϻ@|&5Yͧ&6KMؾԋ@,́"-"A>t *cnL8mzfweqIM'{Ī@u`ng 's`ܵjW2@w ^[Su8g\!zkQZ< C3/;tR~.^' 1 @\\Q|IJ,(nnPB?$/v[cl9fwX@Հg `KKȈكa03W|muJcp{ﰝ t0Pk-pӸA9lp5{%|rc;Ad@٦R~-jYQ5r4րKc>I`wlt jY*8n)X1ADhaܔoGYuN*nfΰ} v/] ZgmI^0KA"S(tY~r YFmJWd%6otFo%A G=OȊp(w@2d=^Mg] cp-a%CZ9ӻfd޵OѕPp1:eku{=do:L18ưnc @2#z]Di`׉5H0>3-|`>6ct nWvsM@G28()E(IR|:T5S]'fLSҷkm4{R&`&YkAJA^\:/}ܜ2;+3sq]p-Z;Yf#^ZIߏ"]pG4\ `_ҩ,V)9[n!f@Q֌dV!5!|; Ɩֻ3Uq^x5TRຐ=Y*@p}Ϙ1F#+}ǡ{R82O,o9)ǎAϼ[X6ZrHѥ) !426bX() kWCZNeDGU$g8jV:!kW6<ۺ:Q:G]v̟C]w Hv;~m?;W?}S09IlfCYpz[c?k̬; u\I 6_2,WoSjɮ]Gt;RKQ`(g,f\עAx7m8E1.%pX`-alƼw 9N@+Xa75!8LI EWg~Lj#.Aw$>Ү[~vBbeޝ8d Ş͏ѷ*Vۢ$Ǯ] scYekl5k7{Cu1bC3C/kfx 9.m3(ӏ}vc{ڊ(] +C: 9뀒V߯>|Jަ C@ >b4ZE؃8R(O=ckUqA ?V?jso\g (5EADT-6xIӷ:I>y,YCex%K ϬN|*ؤZ9݀ˋJ ֺu hc,4Cy؆?y~ISlpi啧o!8R 6mԵg>PlC;ȅlum1+Wឳ޺\[6r,*$1^`N+e5~ۋ?IJWE1MulJtL.&\D-&wL#&W4@xx&HsXˠau>_חe;~g. +} sڝZNtǂV8Lr *5k pGI^F cYd5\Ʋ-zcqKj.u(MU?} 1-j(u볎L[3~"˘gЈKB@׮_=Q7tp-ə3] H E삨!Kֲ G#T%[2o <zLsPJ/+:>F)} @fxQ  R+1oLseX cQCa|,y>Pc _+c,6%v-8gµް ](9ۿn{=n \*nN <8>€N +CBkNP.<lbAe)d-ڲ1%v8zǜj.$F۪w.k:H;LC2#cزm4&AiXHo\$>Ak!N=xMwS,!LX )B%@w\r)6t}`C|P{Ų{jL,sB>}uٜ.1 ۹f dM12.i b.K˥YyJ!&UK{LDŽOb_YRû5UKu;ڮm7e^ҽjϥn@ d[KKo4/?w\7 ђ+̽cՇu>"2kAAK|Tg>rp0UjNb%ƤP C2^t- Z+:٤է.җx[V<+}N.ʯxKu#%jo]bzOMfZ9 E]&WZ?yqq1Dj*wMT҆zOwuJ{k*-:}6:˂Jq`vlEg-!K 3,'/{˿!WV*+*B<!CFB{Mw]>ٴn%fqT5))]tonEl'f`A*Uu{Qkik dYQzֵi6ulZOU؛}nSLL]VП{f5~')f y闆BPGX~ik7yrS|'\<{ TAN8D[}؃!Kp**ײARfn.-E+@HHf~sU7]!^>Ҁ6>UJtjMzg nO*y3\x\_&)~-&)u hF|I4C݃0>a?. eTefA1{bTJ+ mU] y% a   [~b7C٭ڒslwpw\ ƻ s ub qzE"xI :k;))]> mVZiwEd/nqǠbcHh+Zj5#\^["l$i y`{ꦮ=gETfLUP=7V@pޤ)H[wE@Af Kbb|4pvY?>_?9.\ Ճqo|$h:^`|riD6E%:&JLȦ Ddўu_{إ䁋"*!V[T:f{2#MP_`)ؗHrz P?O5A6Űt@d@ rD Nv_]<@8yg~[kk!0>B[F۔{wZufƔ1t|WVQUj)u*[k٭k էTTR]Ͳu1M֒zJC3!3P'l#G0KIo"g/$wNW)}m|;#\?_^|:p-̵mׂozAJkTROybp˨խ$C3T_})Vu|` PbjTk-5V=*rm>ϣ[!x 5 o)Һ s`Zr1b8\ me5/6ehX@7_Wm;ɛν9uܬ,/ Q?A%avh{|m=nH3=nA@"p >P1(3WݴPTjsj1ֆ%c :TK\me#ceSk_zzfn]R&aن>nYR]q:)SDu඄x@ F) \3}f>ȧ;֭vntrd½;假Z#: 0]C۔\qf"%_̒2C~_v`2KeLrvӘz|D1;-\SaK>S혞's:1S<8<*&CX-s2G*@<# nH&>PB\YbNiim`}$|4%WJAa3z-W-D_?Eϕ[GkG[=1}z 屍R[vjn ^8(Iҟ@U@'ИeDwu?Hg?S%?!Rp_#X./Ř[/Z1SS0=T}_e0ԥxn] ]]X -@ 5ϣjψEPAR`"'8TD'A=jsؕbJpKg>AR @s?Pj?ʜ5I= C&7T!U7FrP;7d*iC]]+8˻r< /,$uUz]cL}5&>l 1Mʕ~8 2=`Bj ?޻Zqu&Yh2BYWRcIԵC3toI ` orx=d¥cn#.l_Ui7[9r ʼnF8L⋕/V&twKJ.y!k,=ugj9mH.#Nw F$_ 5+c*" IDATT9pl3]@"p` $tZqȱG Xv)Ghٖ"g64pWmSӺALi2 >d Kru=* )S ZG%3D+K'\۵K5;y['erW@K$|c3;Ae6ע?VT B/C_&q!F|"N|h;JבڒPZ%JgPʿdmiCR:M۴ TTv|N X5vnL;*Y)o\Z-&}`Y~V>$D^ 6j3jթuN\}C ,/K3|OPĵ-U|DP%.''@`C%~3ߩ6!ˑa=+ :k8+a ]CSׂCudߕ{S\sk4Qf^UV>Ԩ۞o;4 <oP :&YLy-(Z wS+0Ǽ^{^%gmYqZV:_+|_if{h.@}uGcv/@&G>/u咵:ލ=:D@E ` 19ٞWKkPmY%Sd K;PMkXoӷ:tAIs1禴a)B"a7}i=s_I1.$_qs+˱Fmq>RZ ϲ\pڰBs9|kqʷH[s˞{aZ+l_|l;\~*q b*Gз^EAťB[uJczoY`y:Fγv/^ҢI_:}Kg dNa˦^rŭ#y(V:?$C 6z 3)rj1:f"Ap[s ac\ JρS'R\rDnM fga`aֿu leg~U''xac.c @@^:CBul߭I} ojRt jeX{<.2qS|,RߤBKyN6cLo缏(b֍}lzOML!u.Tpi\TCOω8!vzH[P){a"i8plDz.2,۟IE'e'?ի@~;FH= bGKJ$Sh%Q/ DƂj TN9kүڳr,8sӳCU*z LOxK&3gN\0 BiϐZ~n-8]hu]3cd CwKu "6>&@G?>R;w\ZZnm4! LDo* eXanKX2k-QX:MސtsH0JuԿe`RR%p@jHw#uZy-@[Kt~^V~_ SIWW׉9fPz2sBgu޴Wz mV~~y}I>#T-ֵlҺfYꙀX;~`lʡ6uX^raLYpAx~ǶD̶< `PQnSY2N׏.GmX-?Ѓ.ד^5aZ# 0pWLdq 0o]r  H0sPS2dNxTs^䌀Nb:h1|0De_C'c fbC陬ӿbڢ&5|wRapO\߻!=cw u&{(;L{kZ,( Z)Jrlyn>}/:3/433>%Kly$K2%\Ta ޛKFFFDF͌|g펲ʹC\6;5Ϭ]\ bIʠ-"cP;G!PmJTv:1pFf8+n̠ NY A@ƶ8vP%T^[x1i+`Y\dnӮ^E?hJ|]$<8_"3g8@  w32<U'˔Zq2o]Tsh1l]{lq t^(5ι"0IDzBҏ?~Gӝ )Q{8&}Բs{MNh5K w{>=G̎lk yE>]gj;Q\ sB3^5O bݿx. ˏBGo^WY6Gʇ+c^8udͷ/*b)s8f( nxUʄECd'r+Sz06(@ձRlb\$"Qg[|3!oU xv*v^@v y\mU-[κq_M6**.sҵ&O(׻*/5LXp{`[dխ/.~%UH* ,CnP h#p `O/]3U(@)Tm$Nxd(f]h_G2;Y]n+ W>wu; MTxd\-?\JF 2\ϥM;뻝n/+` <g> :]A=w**T3bi[1ȬPP Y`X]Ď9\ x*NԂa$;_y\}\^V'fز1o)Edo2_ޱ㲊\2ir=as2\ YUr80)6ؓYyR\|%.d2=ױ*] "b=P{aN& >nֲ.A`2i 8 Q{; 1iP~^E.M޶^i{V>^P ۿb($zQU=BT!&_fWy8wMw ujmimk#r 2E&(:¾ץ,u(Q!q*}k͋Nv YzoZvUH뺬M?1Cad4n꤭x w_)O'.OHH:Ӏ_eն!y?KT'<*+Su!j?}E3:`V=04\I+Lt urY\œbA+ޝBgyRfeg͈.@`^LKMU@GVF.e2bǵ/mmy{K_JP㥗'e+t_U}(l<ͼ)iXnvZ5@.,MwyԨQ@ֈeOr cPcp8Q'(=ReLex3UI.% %" y83l~|aU2i05u;-Vm:F;~S {2TPr&tbNU@TrJIJe9/Q"cǃ=\#X$073dX ;b~sG̻% B|$R8^tdHeEdk\C)6di>+Jػlk$Wۘ2J5Y412~X*EwτܾU,3f<qu_GuӶYJޡq!߱T%12f\gz^y0*.hIc)Rj"CwsR>Q3qʐ|,fa+s'g儞d[ȳcuy(2%zH}7Ӫër%)½/*qVQHֺ(Gud*}6֯𘫓|U~3b_@<-)@09/B@I OfIYv2=ŗ%o;L_@ `:NRu"'Du 9CB.GA@|yyaûM;J 뷫fc.3?;&o|q6r6n8zӤz߹n#jW0o2^QhW^C}ԎcbqdoU/eZy~- Xk3jA#Ww;Fx|pdNO")2f]?;"Tp].إB֢ʷP%|Ib)\˫L,cu |:# c-X,{'L/-/u8zA(#܋}J̃Th $UNy!1Ko)]3 0A0 >潊!+CEO9(2!-rNE%HxyՀq{9 +J+C9(1LJUF6*w=7^H팠 oCxwճɥy< GX;BI!bS/g$-S=]u%m)>17n{uX^}}@ëߊSW޹` B^(#×y^^H/LC 𔃰+1IHkBk @T.DG!v quĤҦDݵ]oy>?{%Wk"ur:ԙ9zi!->mPzz@3V@]Lφ iĻ ۟$2jy8سp !z˷iʌw<V!Y5? /PYBPNϙ+zr_U8@ *|. wm{(|2¦XY6Vu^!+kTI9sal3_ tA= ;|Js\ϩ(A¥eM |#chMѦr͎յ[+*KnnU-2 u 4vqE%meu3y*nG w:1pQ 8n(ޓ*:dn{8(o;V^23/Zwv4tcE RLwe,OWDy)pY%6Qɪ-bӌGu˪Gr1=1UMPݾR|)9ֳ^#o5ӹcbVz4$=Gu>|'5k=:z酴͞|V ѸIh:Š۔U*`>u[\Br0 #4P8ZDd檊{d\š^V:?f|R*POᝯesL% ϹX{8攂O(z߼ٱ5]ӝP6+Q>*V^[(*(E^֕Kxs.ojz*|B7b$A+nȀ4PK!_cLY9 뙇y " .X|y }g4ALwriqպ<7wC=pƾV^xӿ29B4ƥLw4g-R1vg(sן-fvJ$L Ũ(*yDoMi̓ ir>CPdH(^Y\zo 4Rv1<'qu)Xmg6T˂sӍ; !Vˮu!̲Cnwlp Y-@{g!W¨mRB m1^n2@`)ĥU?D136^Ⳛmi**cDQoERo? Uu^LdL={x ʟ`D(Վ9Xʒٳ=)c~)P<%0fzBQl!Ps`|rqъilQl3V>+-a.?P֑-XGœL,g՞kHZ[+ QաH 1ǃb) A sX3Y!㘁O..1FASB!%dϐ(Oi$V@2U]g[慍T3gp9B>܈x0i.#FWB IDAT HϻXMB@+z:>"Ї0@4[-{g@v2jB* @\Z> _'1HD@BJ7T}oR#[H9y!o _:J%l˦MY*»Z+ϻw+[iSt~ҫS/ŷbp\H4Kԑ:ԡ@* ip:["M~~CEiҡ\j(.ST&Maj+.ٝAʂwz/ۺyj~%c-)YUvIEm/nܘAwEsdl$Q- b<1~7.*h/u/@5\N1f3POB?X֦͐Ȗ%O2 g"MHJqV8'sΊ  _sY!C8.mI!ʀ ĸCuo)r]Fc+6 HnͿ>>\V+3Quv[Yx;0PNAD, s:1$sa3P6v :Hf}ֹ+/1BjkWeaAyKBLx'ۖ>DDbEJ"p_vU(b K*p9}"gL,!g sv:MHXv[OAѫoL!] @NȩM1@Du]e}(T\}u"rvۼyg,}/CJi9Uv(LY[*  YP'M&''iӽdˀXc.$=͠o$hAr;oV.A]b`m|T(#4+7 #Aci u͌!$lΩNI4)*]U02ˁ[К{}cާ0ՀO^*ey(|Z+Q-3Cd/|,^]yʡ1 k?>xni+ B+'r r{+~`wOהg y_\u ՒD6WN_^Bq(>cWfq+R?,ۆ_USy\t k׮ua W>e+w_k|̋thf`ȷdEӣ H|^?TIxCBB4fXL6+. a"[XXKIeĴfօzYf:.`+@}i1iZ33< vۨWW[A]GBnWڞ!ͽF yAfpm D!٧MӯTfJR}a ڭ)Gr Pɠ_w+g%I" ؍1QyԱcJG3˴(zE1 ȇy*MW2Ur@61y*Si\6CӦue%(ҬRJy͝8e9CyPJ$ItqCe^3_y t~rЮroy YBc]? ($4!S ?!01*jyY[FVETs';oM+N2 ** uC#]Ba2(NReQy~]ι6w=sy8ܟg"U?2_Q.pT,/uIN_6e$_>@C1Wr3M.{{wUo] w1<ʀj7F5)Wv*VZc{,ŗU-I4I#4eA p&.C$H}=?(jzXC/*>CSJ#= L#L{̸6LYKU8z(zA&uX쾁Y\.EATUs)_,o)n(q|﫳ov~Q*㪮_,2*c,ν ιܳg0Uf~!0-Ŕ)e}1fI,[,,_Hcl;-IWyT\d$3&mG1cQ]EP h21^U^dB֒̅m!B![w+`)kl}Gvj̴s|Deupp1Nt!Ŀ!SUo@ZgCkSL_VdbU0U{>Z-appP35nbR#_Y>O9SZ`!w_~csR~Ioe$e' 8By~5[6ĹN!;IutYe0Y4 Sbe/MMS*>eh[(fhV.`e3ݎ @e"Q,Mu:оLW]TL@Hv;څH E MSYUΕ#!hsfU>";WfGf=2ޕ>oA)`;TV-ϥߛW@;4fY]1nV[f|dMgKD\F#vhffFÛ|md/U'9bɖ3@Zu#\Tz%(_B+C} 9@ȣ5ga6-rJXpswS99WƔ%0md [Vmն4*mZu,*nN˲] l|GKuWƏH(ecȼo(t:Z,,,`ʕoȭ _YaF4ʕ!2k_AEzC@ !l?@ByO)26 (c?W@l3 cbmmkr뻀7WOv>Ľ![L mT$Ƥ峂p.CF+Z~Nuq[E _AR6(<-[8=1]U[]\!_/O橝B@!=^%JI.T1¡*A$ʝ,BfSLz^4M }^|Wz @= j8Wei'MaS֟;Eq=f<6UEPUv|]VQ\Sl^m^l!RJvX;:4Ӆ 8)]x,{Wf]Y*rEMOo(ӗobs=/n5yV[-|c]nXNՑ_wLBq!rvw SSGlωȎ&\h 6 zrAUmͨS{*ۥL̼UF>`Tw"~7iPw+yHpBv߈ $̲:Kh\BY.1B2`)]61RTfeB1~CVO)'` +6 t սMe,k|"E@/Jct:\r333$s<#Xb:&I,cNӯ2 CrSiHHY r4 /A2W% ~HHzBJq)w-++/;? ։t"WTw%s:+"E_WZe$)dx$EމE' 4:}ǡ96C ՞<#N]inEץ("SŌnLۤ &v/a=XpNp5V\+غnйjǓ!v?QB&BFؼggR8H#Yb^  @Peqb;VP(6^Y2 X"(@/x̕ő+/p a =wk׋̻`ԑI.=>e:{ c7gٸs;Ig/Řq)2KU[xrTo1m\z>,fqEx=eQgYܢ[ƻ,ph4p…J2DU=v]Sh%SƤ#qdmIP$IJ)&&&CN|;vcnnwk 4+ehL#W]:af0v6(-@yx/#5 ~*X LBJ*$Den2=&q W^НճcwM˵B,=CuPr2k8ƀ.ʯ r)PB'{?L4::Z۔kưYG!Ev;Ev9>v\`>{$MSa1[ضm+ZEܼygΜA֭[99'I돶W\|)ɖ7^E(#bhbN ,TLQS ,$PpJzJ\Ū=biٝ Ơm={c:w5nnNN1tñ, |-L-8@v%7˛w8HkO~{ lܸv[ug)/SʋrĩOL!j7 1<<˗w? B~ f} 녽jk(\/m0,v]Y`DйTg@Zwx4Ci:q}Skf"'D= e@y(IQQ?ow3;3ie39 :Wz4lQ- ~s豃IMk•UOI ȚZצ&S-r V|fmBўbySg)-8&<)X*IgϞŮ]u5$NzQUvH1Jo@0&iqU:.h$J=z ۶ݏ;v[nEᅬv#i)Rj%7\ %B.C.o, 3NHTn_ęB/<Ai7{?!f3'K`Vk.2+*h"]k̓mkw^TWsD]RrX g}E1¢ՎA#_6聯Kq5/غuk: تҬB傼ȋGw3W)oE͸8wp?v;w@qR#G`x0ya m[wwk7\ >(k*F Eq:ZW}A;^)a*;(0g- p-`K5CyvKe\ɎT4nhZ@R掓o—Y:^jh([#?\7‚ᘝ :/X(ORf::wQu|\yBXla|F7oB N7bff'o> .@2/6PSQsZL[]Q̩/ӏolJ=i^ԨC#pPsCa㎓X+**d-WdCB2E?!H‡Ks`[iG3ۛX'RvYZf^XVJ=_AĘ|2FGG i :BcS;V՛Iŷ–ɥX0d,J(Μ>~[cǎ`tt3._<妚yL|sG3^,yb}STMΕ9yDATewLB/uڎ)J9@<@$D;Ћ9eū{aCݸOxA}%w`$HPm 5?KM @]UOo < ;/ל gs+75gۗ+K!=挣+WX~!6L/,=;  l;msKF+\2&O_Pom!"C~mBpcŊ3g`x <)ǟ1Oo*̐<[ x@U7u@$r;uD. Ě 8;?ӾJ?DXZ)/C6Kk -i.,ZY7LgLXBj&8c.o[.p`ܲZ>oZu ..=BkФq8FFF*ok QWy2 o)eG\5;T\ ":BĢYpαgX!9 IDATOĥK|rLLL'D r4'`*{tav(/{ &ËO5xױsNnZ]1:Wo.{ VIBûJDr>@͛8~8;~سg8V(ͶYz3ЕUM~\<@P%d#,>wq6*#w9@icxx<ڵkSc%sb*]zM7DjzF\1qy9#LP:G^›]l#v(B X@ H|qQˀmRlG)u8 ,.^3㗡|x&k֬ٳ)[hZQ(իh6=  \|²#L&غqq*/,,`zz*N^YQTT>+>$xb/; uZ^Z@HA bq 00_8< vgfKܐ~i 896b_h i0;3܇~9rsssHh\9b+Uw#]mW-",.! 'rX ?h6SO_E_ROWf^P:u֍ We2z3}ݍyUBug$ʕex= s-(*SuB(@ AAl4y32eOe9]t.Ȉ.;U+׿9瘛Ǻux6 yW"ڏMeue!xRry*W*XrQHyͣ" ,_7n\_I~io4c=bp$uqQ]ԾZ˯lKoYe`A}WU oNL4y |r 渑R6fz<{!f.\OK J'hx󍃸z*pUlvvމf$T3RS,vuhʬ=5km:#D.M.y+ \\J3oJz:?9:dz2Юbqeݻ7׆ | ֮],cȗL^ιtBy:2GcB*VWP=Rv) |0z5ƜX!VQqDIINhx {7a@k"B" H2oY 2OPTP.#c :X p/\/!%dhU w<%2-$7^QbHUo`U!_ӇY!Dzz* !eNl߾}֭Cw:$ȹBC%M.Y-Z\ Vҡ8v\ZJЖ?Sy Ae9k@iF `l0^~4A( ̫|mEcxO{$څqqtx_+Wч`OHg/DࢌlpIĪUĥG\L;qp0{|2Z-ܾ}###?Au;tufx5BuR1y;sV-(O[AA's5pHqO~ k֬0`֔ T^Aug.T:BN̈+CCx7?s\z *.ʢu˥m曘+tl6q-LL~|;?ͣx󟿆/}KxObqqoߖ<bB0A lBrǎX ߾ΤJE*}K,M\eq}q]|N0Ő2z*M\lePI a ; ,Gэۭk WqƺIr-0T\hPLMMaÆbIEb͛7c5Ǔ깯MrK̰6eTB&51+W& i%ԃrׇx;IA-*i'駆ȶybffq<gb1] X*0>pPBp-]; @r4Dy ڎMEBpu{NxW ֍xp%,_B TETU$bCa6YVn/-#NU_Xf)< W֨ocff׮Usg1v8`߾}8x Ν; 4ŋ122hs,kyfSdE$04BhVYĸJG hGr6pu=Ʒ>KȂJ)4v tCpv|1:((.[r]Kԋݶ+#4 \R Jq5lڴI$*.QK(M>̼t%E#\|~0C305545 W@Nի1;;~<N'leDž d;Pڵk޲x x^yc{ٵ &.Sc՘veM20WOi*?PM@۷X68bfz -݇ݻ166i|_`VhsssXbEn 2G䐼*"p+\qEYٺ Szp>y=)[D9@-PDtr#]aBeCcVG.wj͛صk8LP+! W4Ν;qtFY d}V+>}Y_Y\XDψ+rl6[~i ΁ c2,-LiNg01[PSk׮all NC 199BX=U(eJqUi.?qPF&Xz8~8x ={ׯ'> l߾1lx/y`3 u {n B޺P.x\@6̩x0~%_>f[kPQ& {vݦȏN^'VXG0=5ccٰV^ntdˌS 61;`۶mE½6e^iISŋn:R}{}[03=۷ocm<~1_ql6~z :ɗD64E?XCF:q/a&Xuf c+q%,..b{58Q9 $XaLXHIٳxWO?4]B>/XURU1uVUz0U uMxUҎh""'HUpdkDt0Nk ȩqZ77R楻! Tu@wc\v޽)Ⱌys^^lij4tkRBTWk*1Rl6Ջ#?U۔&z[y'=’;4|Jܼq?cڵ8>"~ P$h& mo g jw0a#Y픡Zw/4 ҴJU;Snݚ<-[FV垅w*m`\;b<ٮOk}[Vy;/= >At=&/)8lk4U*\ǚCeqXe [Q;MSWbC&g04kz%.- `ժrv|4,Uop; _vR\ &@"Vk2ʪUp\h# cqq uh I].d:u N?}shPI|rNAyMq̈ðRΑ&(Mp ɓx^\''FS΂W>lq+y,,,`v~DZi駟~E4է$44B&x`0fI.N$<>11o}[xuV9^9vލaLOMaŲ0)Y 7܋3gNc{z[F_uyd K0rM#7$ IDATCx8u}|w~)S`! m-.̩xǐ \Y}αG1s-X? MAdXUpy>v䑫ϸ(a3K'{Nt:" %£.:9`Cw~C$ Y6Z|kb@AYL급-7@cn\100f3|p2P*܏Ü#2 fZ9@-IBj-syziLđ@bl D)nݼ7x+W?MxgqauDbAo e(Ο?m۷c=KbH&Xjqk&Μ9z0,SmqY*\/K֬Yg}h6h!뻋nhR9E!T\J01q _◰}v̢n87o(^?v؁hp4`hj\z'~VZFv[w 8I7˗+HS =z/Tepu}(͛kn"R%_}3 O#DSZ|:2a-uڎB8!0" qSmK(RH4x"bK+!I͉ LOMa? W9ʔN>S (9 =ςHMnXeq4}O~kq#r[,:fU0tA0??SN 6V~ן m4|EM:N:o&xI-Sa"破#Gc_{!mӧ+ॗ^>o<'zE. ;Z$i̙r}IZb:.ְ]_}r.NmtV\!5e )c׿gySO=J~:~VoOkkH;)nܸ@?{9l޼oĚ5kOU+W`ffO|r,(R xXj~~ R}Xy=z6lƍ 'Ul1ƶ"˯xAUVP)#jJEqGw9d<@h. k*\tlW *gs!Bϗo7N(#,nVfDذa.xl8VK$'v Bm%k~T\܏"D3]W^!2;p45pR{R?)n!MW`u8r>CbJBvzŃ{ )42֡?q$Az[˘œO=|&` 8n()$uZI;fggo~'OSOa޽0cLܳh:'3HN[‰'s>طoFFF77XlmۆG}077^{h4;v2}%߿}Xl-{!]~[bnn?0<_Knu4}HSf7Sk֬bB!%j4T$tP[溼}.onoQ13 CO=qΆ@.+R*HY2䌃1 s ;t@)!GuP?F굷usuˋHȦiZ-LNNbhhp3 5k.;k٢>o:+O( 'N={r:"o*:_X<@9P-.䉣8w,V^,_S œO>%$Hqӽh6GhyH*.'<.tBn޺>/wv܉.wu}8G*FaϞ=Xv-^{5ᓟz +Wc v5BEN0=5ӧObXv>QUQ6qs[8]*qM~\FRU[<O Z\.( UAwmb` Ġ^: KEPd!Cy]6_xywJ(\>qз``)å+Wp Kw$}h3DN$H%Q,˲ya{g7ϲMJD1a8L20`39wUաэ0gp]]M|x"hD{X1iUl0\Ntd"NKk3{Djgn~BU?&W`hdkde(!Ai%ID*ɓ'Hykd"bl2d4b ;>h8J|\.FSNJ4vF2dpp).PЯN- O@6 (I111ɞ=]bqs\x177t*TP(fjjK/pp |MXY^gbH$plH@$cvf` ױY]p9s4uuJ"G3@вP1wq-虃Vgrnf>dnkd%ˀge0gWUR?Ϫ*Tkbf2bU!jҪNH9 oz;-[]HLږ~^ $`0 I[sBdCANֶȶvc;ei%<f)0G_JG, mXY]6Ưp1L&YPǭשT[_CUanvX_`iyzfgfx+Fmmmy/@AE!{!Qha_Sž{l%HRDc1$q8} ЀnϥϠMs q8$I |'Tzhk령 pQӏt !w=OVd F#Fd2lbj&cӧ$Ǐ*iTDIi f5AQI$H.Յnp 0%L&36aY^Yg 5G''>>{,9}֝rK߭g7mN) ' 57[31jYQ : ҒS *_3]e r߳ H6v(X\T3SJU\}o{95Ѧ}NC~~_54 It"P$Bꖆ0 _Wl>p8_ׂn(YV3DɐtY&'xsXlfRRKwaxxZ"0ݼ[$)s8ϭ[t"^EZ;USCI)2kꗿ`eiom:sv&`.&~1폾danDQ #p^y$IBEqTWW(J&PP&q q =J0U3?SS֩AUU~mf37DV̭n'GiKQL&hh4b޶NBF <[rv[TU 4>ǎh|a>.&.[AD4GI 4+*ZD"qTf  &*'QIx =R1CAOtUABvljZ3hrAF511J- @*hB YL2Aȷ rNUNV9nTWvWݖR D)ۙr%N6V= áW l!!}[`t =߭$T<&A` f,dFwJY++[U%N30O{{'omݹˉXYYbF ~~'#rw*ۜ9~}7; g2)ÔiLA NɇLfB0CCvٻw/FX,WEUIűh9btt?:3lxwyg045PGI9 Y49*NiA2{Hl#8n[Ee](bREK}}B-mha׾f٠$I>ՄaF#+;{I^yFFFZB0dii Ix@8 /դ $IȌEQTE%J322BuYVA>sΟ?"JfJv# V vo FzޮNџKme{zf:Klr'e;: ff!䄖t2x2>`h$']ɫ[tnt[ So 2cRH~?m'1 2rRY!M vjbɩ\rbJI!Y$Ns nݾI$AT7eB0+~n\@F`em`0ȕPWn^ UU|L&L&iE#ʲ"ND#f3@B 9{Ŝc$ Jrm^DGE  Ê*}avյyU6w2DQd2C$~Ljkj2v }+BQ,.$}).,,022·- ٸt{ p,9pn~g~ɄTVVD~:f7n7^ɓ $ bfZѥXbR)VVWDEV+!ݻKoo/gϞF ]e5ufc3촔c<ᛒVf)}Ś-aWMy`ldj +Jcn1r=P,S b(ڊ`|혝Giq]- {dz5@U0GhAJ)illbrr*h)A_=}D5(s^Ě7$zQ-煠s' 1??˭7P4F m}OUDH4B4w6ݻ H֐Nʈ?ࣦp&=Bmm9`qiD2h߱;Jt{{'Pdjb6!H_ (j6@aT`0(Xunܸn5ȭ70Eb(H$Y_ந>!3RUؼvZTUiDp,...]ӈhD"Ν;}qAF#0??ט̙g(Kܿ?NϮBGGU^DSLNM124۷o$fHAuu5/un(i%õIB@$( .Hz ۍ[PZμ8Iz9!Q^d.Od^̽шrA@5N<;%a;V|[ģ_(T`)m(z |6x "0_\jM5+GfXL fΆ!lYv$ IDAT^gs.g)D_D.^*.7 fEHR,s | /mm( ffp~&?6lpի8SOL%C)zzzN6x*H2hp8BUUTH%9DMm-ˋsn׼5d_Xw\vmwxDQf 96IEth0(xkDc1fffvN8A+DQ>x=ZZZy`N9EcS#2Ds0vAz}EammI[k OFhT34r:eIu|Dطo_d勪ы[+յ5cx"( D"Q±({b2w.>_cOD0K&N<~ʏ#K$%QWWÇYm:3ONRT0L 0~8CGG+>yEHD iX]Y矢*Zb8wTA$}}8v\.W^e~~K.2{֊ngddGT}U$A(٥9DHhDNE@KffNLNsԐVd|8妽A0<2<&|H]]-'O'R__G։  p{PUEY]]eeeNYYYiwښOU9,J3KWNgghhh"w5k*W4>vA'&&XYYJbX޽]_<!:f4 5Vָbg*gb4?~g'|[;mL+ %_6w+QUՈ|uu^o5|oM 6NeqA21 Sjv~+'j&琤y^ l/Q`zfzݽC2b-TU`PT?Ou5Gcaazo~TVzH&HhgsOÄatR__,(r> rAZt!124fAلdaqqFZ[۰Ty0F7@0HfWٿ UucSSSúoiFFF0[TUULI FI8l(6 ?կxs\x|dvk-BfU:)=HDEEEȪ‰'0M*LK_ =z2NP$Y+B8޴KKRNlsj&YF;+f+<=[_SG22:KV`~K$I,/-mb~/YZ^D2hAQx7o21K/rM`ue3OAA_0Dww7.7HCq-߳=Ç3 .]<}47QWWC!nܸ>Cmm-x EU9v8J?h4 f`O8@ M ɤB0Ak^VպYP{)\NkΞ=%qx<dn%$;9}!p<==0 UrMeY15y&6N<!s3 ";@BO >V&C涉vlyVxۊb̋.S~5U{;Ԝb*<ػo%DˣM#}ٍpokx U(G5 P&$HFS5Q [,s}E5Ce2wZLv}7e;mʗ^r5 I9r .+g{QކU넻EB DP0Tzf+( (Ed"3R$_,޽݀Q/)9VFS$_ `j!CCC!`lYM,dEc1, TUQ_d2^@ )'>saKZV3'PP;/z4SexV~?>uvRVZ}Sv0 X6jk8y$z{֎:2h$l$N1?7ϧ|gp<.we'ci~nfff8v~ͯY[_#p]-V@b[QPufj|LLL`4ZTz*Y[[eia+/NiO} GY]]AB "se66Y[_G4H>}:l6[Fe.?.* H$1xj&ZZxg9q8M-& gyt(Y"C煨Lșpή5'=]DQBEezz>Ο?ǩ'~*KK|=ܫ} oA]tˢh r-P9~8UE?CZ<ӳd捛,..Z3;Ay=PN]٪M!s0+lhtiwOO7<`h4bXhljѣx^l6VɔVn:QH2MX,fd% j邽^/ӳ8LʍwRÇ8vr|rsD:y;,.-rVWaxxX,fjk hPasTUU/ɓ3hI4xxݷx*Y][[gnqd*5H0;7dYX\{g144@}}-anvd(N*v*/lQr!r(wj/.ܾ}u;/ |0XӞ'X,$ϯē/{j>UU1 A߫{Ims5RuٌﲬR[S,xW~?ϑ#GPT&.\8( guۍ",,.bU%ׇAt~1m͘L8b3@8$1:2J0ʊ bD{}@c}-|M$126:O=)B:Ν;ٿo/(ZDj`d?CUUܹӧ!R~Z6av?*>|zNJ٠o, b1v;GKgryL2-pv)t'eyN,PȺ"c<C!-SPSmV{"Zl[}C0C)VmeÇ304`}}۷v:́b&# rC2taZI}KC'nܻwO?% f3{x饗HR\|'N'ks,d l~ߺ&<  gypll l`cÏj9ž=]8Gf|gpL&=y>. L&ٿ?Ϝh40Ocm-tiai rVVVVx0iY&LL(pdZЁ#>|`0ȵkWd`{mcr1anUU1LR)>4r1p={b6btKcSɇ~HEEO>$ϑJȚtVVVxd|>R[[C}}#333ӑzsV d |_d2CjgRJ[Prn[*׮]cppIhmmb61L_}GɏJ[R- ^ R_Wc bIȆyc^12swwnT۽_Zh 82*gj)3$I$IQ-\D`xhJ~xO]p $b8Vh4FG{;nӧjUeymLwW~Hؑ#T{kZ466 tյ·N"==444gOoq54ɧb[IdBkp ́Y[[㓏?!ODŢZWE%Q4X-v0U5(W?gEE;d:IQ4@1kw  c4qUq9]LLLpU~8A"gaaYN+كfݻ{;~#GhNp(iYL@$8ᆱExpNEE46jp("J"Z8xK㩬?zyb!<]y9N/enmbKgJPP?kL&'d2Lee%Fq[X2=X_,Iw۷GR(. 6Xׁ*Q?zKU& d2B sv(&Gq[MVRro ̈́oN`PVSTUs[Z\bff!, 33MFx`~yp8D4emMsjllD@5 nkKh**&1@%c} 5 z &h$́𱾾͛79uW^ciecdiyx2=tdfnn"ٹUU#<Eavf)‘(OV+M-MHFť%0 S3vxAФ@g DIb~n¾}{1 .&#$J94.h'$eU1ԭ|q)+[ ;9(8ofix ijjP(DWWW.[R.ES4TfǺkwOW{fͷL$PE6A^u꿪KH_$v%}?&v[Iw[Z-nj V>nXsWVRdJd2qQlVnݤ ETOC.km8wI dw~纩J2)fkT0;7ǝwxc2X_geyLLw5^z{d2VawWtLUU]]]D1B!ؠK(D; kװYAboݽ:}}}?qBp+ 3$q>?dp$J}]={ɓlanz}ܽs HٙDDr壏X_["t;wnh'.sW_3^xEl6޽믿Fmm-.kWxdEA$DICͨ._ݻrii*~]]ݸ\nE%1LD\ zm\.,//VTS F%`07*$UU%ITbΧ@DF#Ȋ˜`229R@KiYF-JJo~YY\\sG A4 Mh~gªap`72Uf)c`PEeR|DU]IHērGBOϹsp_b0_y5***-EQR)F33TUU\ACAE`-M"L&q+0[|{b$  u?`ueӡ9.̑Ny/?jk㪬`4`Y C<|4`tl߾;*n . F Ê;$S *,&R|>u$ ^(" W*SSȩǹp)N9˗inn)~3g9 \3OqI[:r)8z0区]n"(#ǰٜجvD9dYŋ+L `4YmG̾YH&?Fy{*?x- 444 1;H}}=#CCLMM&L222{.-f&&&ill[og]? nW.  kUTc8NrLB4ݻPy?gA%;ϻEKvp5B\r/t:GUUV(ֺ𿬲fE(C] : ˈeL `(H4qfٿ[ma(Q"8QI{z;zvU`dd75$a69~8555|injs1ìa;pX,VDQ`vv*^^0gۍQt'LEUdYapp =n[u6!k0YLw$OCh /L*annEQq9lv~dYFE```:&&'DB|'CA }w.~ C6GavfL{{x .҂ѬIQbiid"'NAP//XY^SPsyկ! [7"hhlAQSY9|0D l6 Dc;}UXZZ`D"bZYmuZ4]rt(p|Lkkc#H]]PWC==k;;p\*Σ'N[MV\Z)%a7K6RQ6Ns;PTfzŨeqe&f񣿠SS M\q޻zVҌ?x,05=$Q21>"EUU.;O,/o߀ jrȥqWW!Ì x*=੬NV+twwaw8q+X][[,-sm0tg/`  Lya1D*]nf3c46qm6VWqT%ă8.TE E";46 0<:JSS[gm=ij_+++n3 pP8ի_.нwGcǏa2Ǔb1>| vg~9VVW_/mmǗijjF$ TPQ!O2łlF%ݛFu]w{os0#I I85X VlIqK/I/Y^NұI K(QGy 5V AINPUs9}}K>)))e~GOO7`TbeqVSXC F+y5%>E+dPe zH߽x^4?k,,٬:rN,.۝(6, V'VC@V7 "# p蒊((`Ԡ`3Ckm85i=z> 9C0Ac{$Ff3qd3ꑲT*E 裼| a$r!-e5h8"zO ࣵu'ׯ@hxseFGhݶY vzbbnvkg-D >:XXp~ggq91$25P*~SpӃbh&UFђJ\8ɑ#G137K,CLfhhhd~~^,fAfgeFCIq1ZIFF KKNyDQέj_D"NV΢4`tԘZ6}]VnF9(~rYZ[ܹsș,=[\~ȆJ***ؿ?dt:ŋHB]ŏsVC'?=j˲W}:Lc0ɤ㡁ҲR~׾A2RsQj%_۽x~V,$ }{( o`DA%HT 1 WPhyYOS__O2p` FD5\$2>1dԙSXM&~WHƓtDr8t3Oc0nRGI#(Hh2VӧO܌( t%y+$QTd23R]UE8fb|ֶVB5޹;ttbXbI:LO͐Ngsk #Y qn'YYF2eeTTl@5?pϼ\U{!JӶceeexr LF#%466p>n lL:A#o$zz|2ť%ht:^zK=TWpRI@&mh:'| ոxs8MMM9SN.u>lQi<gjf7oToo3m<~4Iq9癛E$nΧ> @ׯř3g0v&&&BSORSSM,u6d2l6jjjHe2݋lfgjrݻ۩fh\rqm۪F4ͳ+cYzxb!֦ܽI@%j(iχb_~c"EEHNA2׿e__I][ ŭTfuGmzXOQd2 ǭ,Na,ϼjc1:6E.nێ,bIz-yk^9[dr3??Oee}2TQ=Z;'D#1>Lw]dYa=|;&T`m;P,.~lR^O,#zyOֺ`].1 ?)ej"_2Ox@QjkO^< >I$) CtuuDѣlܸɉI*++l5SODZPUoR__GqAXv|>*lD"1m hX,LВMy ZcX8ZTo'"x:;;WJ*b>h4$Eש|K\2J-C* v-֢YXjhJGaBN_s,:Z!wvalY7lm-}]fW2G֏wV{T&OQ+'`^R-AX8}>?}}}x^z=vS,5N,\~t&d&C#Is56+FN8uyr6*jgk30c4L&C8FףjYXX^ Yٵk(} ˤ!]Z,ZZZxHH4(FdԘDt2f30$#c4l̅ۜ9( ¶mۘ2E@?ƆFN Ue\ysawZZZJ6;abb3=5J2Ad{. Q^^֭8w<;wrAEi]]nk牧LLM❛]TDd$211NGf+ 0lSO;vAlvn Z1&X- >d2555+YFS<3JGiۣпIcr9L&1[̔WT).&FT9O.B\^KEH$ݻwD|ϑLغ};e445O&)+/7vڍbQgU^xeXE***9}4۶F8vop=vرAc4jz]Þvݽ]L&EOw/sz6m^_/E"~SN,fO==8l6gI&ӴGEe<Q 1::FD8 M47o駞f#tǩSH$t"}r QEH&AD1^d_~/LF~~HZ-NQyǨepx@G:tN G”qP]UKuu t}O;[lUcB{VpoP̿IR?V n4cǏݻv_69~?b]>s&޽ قjk?~h4J˖JnwQR\ ٳ7a ٻV=%NuE"v;  ?0%%%{1u8vl6+ZU^! N-[5]۫N򕇭Bd2\xSNq=vjctl)v;}x^)))arr.6l؀F̋,4E`)䗕jjYm1}Rg\j>w&ESRVڊR0h9I>}\h%Z Mɓ D"h٨S w``͛7 J6$(ꄗN&hjh<\:wv 03oooF~_ -mmm,Uţ@D(8U-d2˿+LOO388ȓO>$iE5Pإ(L:FwǏ1Y…>B `VGoh6ݵ7A200(dhD&- MHHgϿoM4f箝DaLf 5Dq::b8@غuW\Eo4˿+>׮_$ c2XlQnܸO?(hr".ccchtZ9dT4ff2>>Ѩj!L!Z:;(**IҠhرc33d2e׸YC>A>X$J~/AcZ9{,㣣4^{5~~̜K,b2smYgGD48/|HZ ~p8(9s >W_*nw`XpX\xwhB(bszJKK"H "n;/mhT^d.\1t*N56P؏^Wy>655 &͌b 1zvt). }>rz ҃:rB0}n~Ozr=Oj4(G wJ+ᡏ?߂V M /,319żoB<.b9uOIXbTv;v`6 <+U}8dYM<==+_ezvϋNnst9)2Ъ]hN IDATK:$HP.Ν;9YkLNNv;PTTĵI2huztx2/।qΞ}Պ jX-[I&Sa׮]X:v055͠ O /DA..job8"Fy_9y$,Z]`$+dq\ $SInu&GilhddhD<ͮ{0رFʥ`QaY  jꢶAh$ 'O˹i̗4 };D ԓмu+eee(rI6Ӽ?Nl22==͗ EdA|{t_`rrO\8w\.`!#J/b8eS)Fڷ/u3/Wwh-a\_ݮ 7O柝Nd2LMMw^5>ɈoK8fffޞvq9nܸA[[`#+Y95&ee}}~=tqº50E5 r FQDii/Z,A=lRc2S]U$iR?駟p" jD ?^l.~Z9VBPx `%ɲq3Ͼ95UU~(A6^: Zm ,,7~9xǏM 0OEf )M BXlFd!dh6299f@I'-'fbVD }476'd1Z z8{]fG3 P@1Mgq9ɱ}zQa$\D.R4z|uulܸID5GD"$tZ-CCCj9^yOHL&#"L vIqg6dYA#ȦLNNa01t}[ϜYb4ʢFﳢ(, Fd2I,%I$F雌寮@0sX'FRӧXDp%Q`GHZͬ.~A5uH*ȲL<hM%dYAjFrJۏF!JSS]ͩS?p͆dr*bw2;DsS3;w:3iN>׿u~ӺX"J8j11|[ߢ¶m-h%jka~6}[$ro߾(/,ּc@IY9ߧƛopcپcϝ'?1D _'r{Y^~ܹqx|əgطoM*d+$ (0=PUUE8F&B>w24:$jl6ˮO-ګVQ^wֶr /Idjbɩ1@nspeDzlooW5^d9/cۖJtN2th+i:[7o2>>ΦMO~ngxp;x͟pXijn$ u,`ݽx|'X8.晜TtB>lj2)pM=]]Xf^JkR1 - .q\An޼(457Q\Z(IA\.'xrm4_ʫ( PS[E߽^9s eeebɊ dLܺ~QQ(_'{{IF&L_ʗ+d(+/ HdsCl(czlh߷LMM Cd22\zV8zioofQ\!N GQ {ҒR4Rì,/Ka]( (.yu͞yEQv?ج%, Kq0G^*X@eu AĤ>T^3Z0ZeU@x MaBP% N)DQ,uѨЙ~~?###aV+6U}fNPuYY#ã  P[[={illwA 晘͛$SI6mD+{nvgill`z/_nW'464`2 9 Y' \.gN"ӥꦧMٰD8^<Ƹif'n^G*"JaXr21;;$iP3S}^=n'H9<`r?nWFt&-Ig҈(ꩬ&Jse4lmAiipLyyz sxNK{{;SSS;TWW~r.ZN6kMW^[ _2-DWnE$N~%eFFlۺ?A1 NfeWwe}J끘փ,,:^/]]!I} ,~_W`rP(D8F1'MZOgy۷mCPUT剣SSS`tqu0d3jX<fLt:ΠBx1LF}OEXx]etzzb(+/e֭tK/..Fo033TVVgpprcb1;Ք3;3M˖f&&Ƙ$fؿ?Sӳ"C_VA?^E  xEN^CD G{͛ xxʘEHyoD}}-:H:2,S^-[$-_|Ks^VG#lnl{a6f{v&''H$$INsK 0G*"L9  188DE.7{졡IRGAc2ΐN&&Yg˶e˜={[h^ɘ؛oqmt:=_W))-&qoVP[SCuM z9N',c4/P^^(͛$;vBPYYh vܥn4J? "fIh ,kJ{ ۃp80d3)|>|[; tc׮]?YZ[[W퓢E('aEly7FHɓ'2U$J޾caɑR%gE7R`QR#I, "'>\țVoz6У0u$]EQXjNq ͆ED=?[.]@ ^:t:ΝC`41  1>>Ζ-$IrC9@P1?說&ں:4u좼bO `Kw]RdƦf5ÝѵVzd%ޠrS][C,#b4m8N+*0ܹAssn_~#JJK$`C8NR$DC!IΝattT2N#^@7{_&el$@^6;dxC&uuu\? g>MyE5l={Ng0;?=e֓Mx}c/E= ȲB݄Bt:y8ssąϳPF#F[vt:={gr!RI',s H45ml6R\ʕˀ L}& zEEEK g2\r"#"T=y&DŇ\ƍl(`blBAp'H:h"NQIIn##c؝.wv1>><&:v262F$eΝ9r~GoEn7i9V,N#?3Pl6&BHӿp*D[?7mY9s c㣔ihhe+]v܉!夦9,JVԿhEY&sU݋UӬ,syY6mڄbnnn:?>2P75ꉱR@_TX& , s@xf7W6k]Oze>%*LMM#"h\ͫґQ0-Ԡ@ee%칌z|~rP.ZN9hۃިG5# ۷oXvjk$:&'&R;xۻgy"QR!@B!pe =ܵJKJފh0>W=L&|2olP,'N;СCVBUUUf=ڮ`O+~N(}\V_r ' sI"!ԃ&Zꓠ`Mʵ܈#V7z8dYq1],+x7 w0tl+""I D}H"_{k45m!K0 o&NOI :d2!n~d f#PAĦ͛{Jٽ{7 !(޾M4c`dj FVO,djzXH>FSS[la۶m\8^~ɼnz> aŗdnvqstuwR^^B]D"aFG5s=+ȑxK_CC$q/_P\\L]]-##f˹u&M|pv;@Qԣ,M TVTp9ض};v漤2}3۷͎'0=3MYi9]8imݎN]b싂D1DD"]yup>H8怕pGQe2y]w ;wࡃ8lZZTCׯ_so&EEŘf<nN{PSE Gz|  ۺGr m=_Qe~6t:M8h4.*DH$\zUÊvO1OQ;| jL6==6Ţ,,,`0ꙙl63==M,{QS]$I\& x^n7 +c|s$Oz;G7)([FC ry*+v2D"!FFH'(rN3YD$6mn`0155 #+}MS1-[LOO֫ETVWbޯz(03=MV P(XY]abYv+8zX K蚆i:5MrH6rޏ9fr 2JP,*X#j]WJL>k8$Q|hNkk$pTz|>KZ+wY EQ0-h8@Iw3q.'?I$UvY{1L׃i{ttty뭷dY_~@?331VVVX\X+At&en@,pI > C%K蚆$ e|^/ 9X{ X#+gIzzuz~AvR/?4k3-O>o}M'A_?GԩSxw@UC/5j#r9~?cr>^bccccd2B1:zO:#*^0gQZOv9) ڸ}_ /{d~^de_5CTT*5Юixo`Q=4wuefP*>wL&C6uo]{kkE@`a~0T+DQZQ2[ZjD4˗'N088m :TA,A.^c30lATm- \)rXVYp"$""z ˲¸ BD/W._ڷ$ƒ{Nq/֖ Rض%‘(G%0>1c7oo}4U(R(p.fgjy~7J­1jjӧOa>ݻVrq-N\%Pm6^~ 07OXBqɸeImDwW'H!=zg~ ZjT|O `(aolƕ+W0:sȲĉǙ_gjjDo?RX(Nxp>TxM"6`Y`'4BCbqw<8:ʪy#~L‹;N&J"qklhKgp]V^o3}R^Vl>ăqn|9tzFR;׸x"Νĉ{mFm[S*70:0cp^lb+&8dY<ۍ$J{#!DI skHɖؖHhʫIXDKQٶ۶ᅬw~k+ rc$zzX[]%e7׮%t2 2vS_4~󟱾GJ$7~ x< }~Mд s\=ZV14v+\nuK,;:q ?&mdi&|(L&exhcǏdO淾E dxx匩ЈKG$7) /!JGȤSln ōOii S, qebqj"NSְ ǁQQ<,-L4Rgr}]2-޸zX+J8~8]]lm& !044;ɓ,g0? e,//3gJQ(8} o6\:ŢSĉ  l{;qP[pwN`:ǎ% Pb[3లvʕ}{wPzm\[̙3e-N:eI'~WLmrQl(x׼r JMPU?,h/-qwS(T8},[i'_Sժ%K KWw7=\5B-r.rriQ*x_~%NݘNT| Ȃ]EtR?###4%>Mgu]?Kd2$ ò0LHg躁,<~5NQ,e/]Q,$2>1,aZC`ff˲xbqn~}Ըw6-qZy`sqL6/>0ܾnm:( jh@?,^RBwW7LJnA"(.Y&\%'JmiTR.W89z]Qk*&'1Mh k*UBiQk`j L=%B^᫯nՑ0J(Rn7##"!!ZmmhZ] PT<>?ݽl3'7l{XI]QUDxGDtX$yH237mܾZӛH07?C0'oS)VYJ*LcD7ΝcOm <2STX^v*F[ŽeIsLxإ?mrc" Q."h '3N,QUkLLeiy N.Fe66 qn aZ˕Oi Y^^& 288~۴ƈy1Z M՜NMpVODO8gkX0C!8w~9NKM]zt/:yw̋uK0,ؒwOvQrV.} gJG\䋋KHH$%b$w9wzX[]azfh4rNU677uMxhzGY][+immTgΞ=EuGrLMMQ.~:)ncttUո^^˅Je &&&p\D# Ad9f@FRabbb*:Ȳ ӢV2>~l:EWg&m*mu k:Y\^Aѵ:5N]KTĶ-GX`ii'Tk-04dIF4܊AW^:_g+N\\̓l!6"rQZř3gTNG G?!"|)difgp8Mc*iP.bnnr9d 6)9tefz]nTMgt$qz{{y (nWA$Ξ9MR! 9iA@3?+g_auy4y||mT*C!dFf_ϒ/j>k; x&xg_MpnZ&aRV5d2nY196} ~?˴xՋHe֖Vz{z)cqF_޽qJ"|1bhA zF`aj"?; \wwob>iŸKAf 3w0  bxG"`E6 q}ko"g?hׯ D0-l.Vr EB1pS'OmiƗ7_ോ ʨZR D$"EwO=` ;pZ%`d0M7ooX]]R*"I"u*&,b@o$zϯ/7n} 4m^}5V(J;~:Zo&>UtCN!+44 I,'N^!tMchuʥ:@ B$҂D-DrG}eYDQdYf}}ݛ iqJln&{=$7`G&lmmIo %0,A|mFt͠\,*cumaXbP(gmmQ$qYn|Iww'ݬv{Be*G!T(r%g<N;wʕ+ nO ," "r5oxwS/+C,v)Cm!ضa,//FX"1??OP ;iML&{+oAX ::(b1zzp.L@>&>L5vN<j.^/Pp+ZTqzqawc7QR8ӓS>OL  Sxa㚿F?ߏf> dmǪ;wen~`0H`uuPYTU;$ 4MGv(Jz}rl{M,x ۱Rvӛ\j7AP,eNZZZjrsO˦)=sMSYY[A7 $ٹZHȢP(Q,//e1- MyM_0#NxMo|(>T FUko!^3F>? o:M#mc V||ߦX.Nanv@0@,#oGu24}PgUOm ~_gܿ7`F)K^,A QfD2VP*%`X&uMm!6JǏb٠?In:#}^?^xy;ﲵIXȑc|==u,} 1>q+W0MիW  s(8,..NDtww#I; KȆ_:w׊~f---|>b-ܹ(G%lǏ#2CÈΟ# "I"LZߣ^aZv˅$(L&˛o㥧ޞm6 {'ELϳ,8r"y{{dΊ^ CX ~ݶ׮$|0r(x{|sݴmT*`0H8⭷O0=#0ۋ($zXZZT:%# 6:LB!GP4u璒.bpnsUegN%_(`YPgl _˲ڑd?@_?6twwc5/qڛddxV"QĴ-* ?O9q@V݉ jZ[K% uiJedمiX\8ӧW?037kH4MC%$4U'r( rlm%uDɇ+2 S); ˍeZȢnk :vQ<ARF,\ A%bZ IY^^fxhωd2Y=zD-\| (|} I8& *Z= FP4tggikiAIm&iFuBf=LFx8~8PB6x}>@ZO)Uekk^Qd Yvvя~xZ[v18ύ/@%::5vt&CooA::;a8>sZQ^3==M:& M ;ȾQ m9"65۲, NCL$u|Fbض#w hb]oG##!GsI-)B0vU0'N0:zP$0?ͤ짣_=DfLv+;eB黎 cyxgi-r=$Tmԏ, === 07?_B&E>#{UN>V+iuePȣi*p4tFP\2^nh?5Zb$76V+۱'YȚd6^ש :NHX$Gq{h'M$J[A%6Iϋ$JBElI!}ba< >}4H"ܿ?AX Lmbَ'85hIg{'@9jYZV4v">hS(=$7ר*X,` &hS,( RTjئݘ.8h l' jia[nB8&CQ,GA7 "\{]|>]]~_}$ = {2ժ|Hm%롐QV% a#A^W!@b6㤅v)dRilD)Ű,0^^#I<KKb1'RZ /14ǣ0vgSOӌRHg3~,:tts7yՋ@+KKj*+cZ]pkhiNz*^24h ITSB$lA::X#5E#\z*B`m-]VVVɦҌ8K I&"Prg6[ʥnąsdkttuR*EYXGEu"BRR!K.TMCl;ډwDHn;u5JBGG'.A81z|wt$Ξ[׮a:3S3x<>5P148~ oBqXZZͯgccdz:}A ~ZP*TiLܛZ;M<'!J"?b|.j]{Μ>ۭ pxŭ K)p'͟g6Ԯ~xf705`Ƕ}L ;BP0iSm>8fulQDv|~F{t@K /VqZ#ˋ˜y,ǎO?X(U.^~H4Oq"\IY_dlɍ :$e.Zڹ(nwFU@gG;|Y1&]ݴ;l` ?X,8>|"!Pm,,&b`iX]YqTU|/HUӘ;(:jjI*Vc c-=~ L*e7CimJpUffg%|cn|IPe8¦Z:ᆘtvvHt1??OjsJF%%` mR(!K.Z[ڰ,8z|?)1](ٍ ˃nLr:::O[k+&'Mp(wneye5<^/\nG4{v6P(Q(%IHR<~NNhj3dWؑ~|?$UᅻOe.٩pt(8N͌7n|fQ$Ibee M7pdu$QbcmDoIr2˲LPg(.:dRY.](J;v'N6j24T~4qǸ4͟C(ةӞ~AA`gM`9,{;aaOeb8L{yy},Ss[sc/եeZ[y}_ |/=e:?$I`{W,.w䒘 6&W^_Jm"$܊'Oa077XjULDbx<^~ufxU$݌T,RZFZ\!K!24\]QUH$#C7űmS.%?4@V%sn7r@BxDܻw~\ܸq#tvvF[a#ݻTXjx^{2 =,/wKZ#Q`ye ơӛhʑ#G&9< l0Qn ݠBDtɽA GT.a*N豣ΓHrC#rܹ5S,W.@o_._$ˋ,-- ?| ss>I(Sa:JI0%TUDQ  f)(Fo_\A|+Wy NljDT5LӦP/wt£(MO0?Go(_,GJex~(G1-ᑣ]n¡P!|^/sԪtM2O w#{Њi=Y}܋ 8 ;sad/JRA3t>sV.Mdlkdm /{R|2+k+{0e$1tֶvx=nJ"HgõwADқNؚwb+gUCxyG+%6CCC,.V< plmhpfgg9rڵ@eֶ6'nj5<ǍcZ\,sQ?Ց$R$g!eY23Ӥқlm%xǎ#ϡx$2 nō(B[[B!N~I+/,080D2DSUJssHoo[[ITF]dk+Cfgc*u. MSip\*pJF]UXQ .I?{d3DmY ΝSRIVWd~wi)m6_L6E'Hps;AM5 ܇m(~*lbGCӜ^i@\Po^ڵwy1IVWV5T*KGG" 1>qHRJ%676$kK9s&0Mb1IS/CS,s=?0LWw7Nx>Ńi,/-0ω'I DQ)Oya/9{^ |u-"0;?G!_x<~?xGGC!'Pp6J44M% Nyk<BAMww_X)dYƶɍL󬶛_g΂v?[j!i?ǟ6  4z|w~?{&u1_ϺA>{iA:믿ƫ(kksBsAɺ}|>wP,|tw# 2##an1K ضɓWikkr{PWHD4NJYj@Zo_phc`0ÇFXIPvFq<,#N]7'>D;~qy'ѝ ˓dp b;p+n+]]]  3<4@$fnnp8իWyxZJ%$5"mQ(r+ll%ɤSj/]K$*J\x3gϠi5R$nMX `e`:V\躉Kv쥦eo^!(:51t 0(ˎ Wt MP-n]._a|?pخTjٹYJO)(nϋezf wSDLù/4qe4eYŐ%׶#(Jk*slRA=zOB.fyiN`U;wcdiqtdn!׋%N{bmx}66ǎ `?@[[ -f,//S-aapH$%ʶ=Ǜ2';g@S@}εc{ IDATIv x^?4ǂMTd2I'#I;ekkX,?@\.Kkk z?w/--Μ9éS'bhhIr~;mOJ[z=q{&gkօWF>HW=ivAq4s&JsLҟA v2^w 2AHaj/{ ;;Y]x \4XcS&~w }1HOd<|xTut]dkktj\64_~yR1E DIdiiUDpm;M"-Q:nZ:(i~C(nYVviimVk~pJ=^r W:.DTj US)jUDV+Vnt'LOQ,M2+5%c-v6 ~se[~4wC9ef1cyX+sss2Y͚bfzIµ}=v{F4ZYZYF4l 4M5#d/`NA8D7== ō$I gU mLܑbyv?qSVn6T ߏ 8~9GݛGrw<>P/"lHIQ-kG+{qٙOፍ؈ݰ?x'ilylzmKAě&4иo++Uh4}P㙍}#Uz3}CC"H_̼P E(?#BW_:w& "PhS <\}^7m7Z0lY [> 5> oTx/u7u?O Fn=Z8B!dYƲ,:::hoo75׮\N]]]u%mT [,v:VvIIXY^C Evj2::G¥O dz }fg|",c:BGCQ aiiIeb!ؖM]drD\ۯ~I9l|nV2Faeu~ Zɥ˟8t+.Ex?s_55o7lUTUap`x_U#">E&ZgqEeH"bZz"LuD*fQX,8{7xfB?>q4+׋sP,&S'Fի ӧpץ*U2ͯ9F4Erqdɽ'{+۶ӱ;n;G;s2?|nwnM;[_7zV>n-lN0ږo@* ?O|2!^WGWw73tttW*C |k:>%N@fËH<'ɰoZZlc#ȒH4Ӵ,/̑\[%#"㷈FcV 7ݼyյe,8C>q{՟V9uc7%$z*a: &''ihhd` SAfff좱0@9J.oŞ{ikk瞥CV<9)9p]]~4 ΜLvD]lvtzI<ē47ƍa&'iikevnFfD! b8:ط[^xq'd&ex$jc(*~bQ.鴵ĸE::=*\*bTtlJ+_G +Zj+bǏdwGFEl'W:]6E\&!K"iPU."O?}@0k׸rur 2qOBWQbdŇ"+H-m1r cmub@G{+Ü9ZET*]WQhhhT*?C8pǏȈ!)Jrv+uR?p*ŭ%mE9z&2k}ڜk-v"NlڀZƆGȫY#Eo=owB&qRIIY5 j9^av( {'?)o\ǟyDCGc8zBPW^ett|nIf őGW׸޽;8 D^w⑹mFD]WoKK457ʲ!8HXVоw涹v A'vAT}mֽaom;=o%-޺uÇW7$Y >5xcٸT**m*ϏiD"Q666x_21y (Q,dT,//R,H'HC|~~zAr}{I0SS+8*((?~\ JAE!388/XYY&366앇6u\67,KcnaeL33\|O?$WEEJ%T}1wH Z̵k78X}a[mňüבd  vlHSNO(xAJLE׼RЖa>,"SHJEz)\avvIMݽ=caruR㥭E~=[*aY6_oo?WsAe$QGE׽︞PXY]T,x:#J^ôL)@ldI2llp8+}G\eaieHg{CTt`5X,(~~$Iyxe@זamNlOEMӪiڇ>d?=  " og{`6myj8b{O&w~wG"=<=}@CǏ~c^|E|IΞ;Cz}DrTSsSdJˮ& 4]G=W^ye$ID 'ZC".cym~? ."h YRhoogllcG3::J0\~A |Jm--!?> 6cc,1??I yꉧ޳0X^^"W!+ 4!I J5ru"d4753gXX#0;7ϣ= x(>|JETGWw7{Y[ZA4"0e[9zDL6 ؎EXT`# u+mc[(Lñ\lldE%cAg%^e-)2i`j:#`[bhXEc///!`c^, 9t IT H44my(b[^=QQ1,5\e Ip)>醆Upm.:VV`[6ڎkȒ""qNznX>LOMquJG}/~%.]8z8tuuyֆ\ozﵸU:eeYX,yA.#HlfFl%mۃlYlZM4M]F}Cm399}t>:K$Iij4 Μ9hkkf)Hbq5ƻˋz)>sS,//XZ^Tǿ$++twu>5|DvF} ]aބh{HU*2l|."+T"WmmuhO󡺠w=lm^K8[SAnw w;w3淹wZo&;5Ym+AѲ,=p4ikkݗXimWU εjy^"@4B!(33[oNMlW"3_B*^r._NV4mǼȲBk[b^O`uum_YKĹzׯ_#YG)A>}$iZKaDͭ ?@[ktkm,e:wcp.ֿN!R033^[og}JY+"3g2?Ȯ]-D! : Yπ/hllbO^dY$Yjl=A$) ?ل"j!m:oʃApwssv3T}(Dыq ,$I cccݻh4yΛ}85w}w:(d2BU$\|@(ȵfܺ5A:" EXZGUER5F"7d3yYǶLVVojR<2i:.>Շxb(/n>(BybIhnnMR "2Z$a;000HEzd$yW dUV0 LmA4CehnnBavf?T*y#< &'XY^ƶlN~w}W_hrYr9"eL&)KACc=kK9ze n"`.(y*zUUCoBlk{%kH˲( EDA# -K+SOs1~ÿdc#EcskZg}Ȳ^7 IDAT :Z`ð$4V|T4C, DZ(b  DDQLMMS,-zO˛,TU4L*gI%E% dIFXL`,gaa0hn\*y, uLO٧"eICCёaʥ2 rzffdsa^~0 9LK좧H+_G.a[o$I22:̵k!q]8$4aϞLܚ$_łzȁCHbخP۾3_A׻0vo?yۂAAbBHR!H؈m/Ѓd2(V{4`(䑛Dzlzz{8^{U669sC\aldQz#N$iՋѷr1^)aNi  G"R@$cvvϟX(dDAdqiqʕ23ȴup!^ImU ":t*>b( 8t={0 $x\YVWDFVV8vW\ܹfeyŅB0MLMM!BIOԈ zGš8~bW}T,8xz2hJaLE7|Sx1tQB¯8P.JEAUDIB7l~G#|~@!_&/p1) \r I]0 ˴*BejðOkk+ު.QQUoqZ\dYflcA]"{ナOX.T`KEXzY>T(Q* xGq ˫uv'z1PeheˬlP$eDq4C:癜ƦZr vX[¹uFFH&SMnHXDu9\?|9(M\W`ffJYgph?_z%; 280D[[{ݻ&yò3S?i?r,BPC"|$Yft+K!VWWim}{qgiiiG;SD1xv#[C { v O:r]vRhllyvbcclY=,ZT*m 0m ˲0 `Гu\Bb}C=({|!AvD|y0RH]V! 1 0(uvik\.BΎ6K%8'NH' M* 4l\ Ȓ@h`&te&g~cc\zt:I2 E<AdttG ߸,+R|?zzzQd3L%~\t 4P|*|[|mY4*qla2M<FUd]בDƦMre!R &ٽwohnl¶-8_xp(<"u4f8# (~tO?w`C|*YQql/UͶ\$IF+=vJ<#7n˨iZ Ҳ R\ǩjccfrJ%}X+K|{EΜEb KȀ"q@z9t0 9ʕHLnէ yD}CCJ$%^ezj0%VciyIJ<@CGDwW! @X<44 _#cT:KITOj-@{[$"/][{xSڵ /Նw%Q\ldӿD 9*d6r^JMqtFB88H(8:UTI_*-XZ]@*EwxI::[vuOKk3WY^Y"WR()˘F4"}> ˦R+8$ p0LP@DM$Is&2&'cp`R5::е ZKϴL2N.dqfy P\H._DdIpe >'wh1'nO>A8,TSC[]7(ȪK霝ݻwH: cc(r$E,J"H[#ēO"J5i$oc|ZS'NދӎwϷ.H"P78Ib>uO&$ yk_eϾ|>_sgϑ\K͛7M,. -A$/p)VVVjxO=PUY؜^`ٺ'׽͏^ٓex HPw%dŅ9t-$>Nz4u Czz)tlӥ薹;ӭ(%T7ws GVZ?s*\G貅 zFzPF\vv?ڄ.I&T:VtoCw{5NC-T*+hbY72S,g>"—^d8?H!wb2Ѫ1%IeW\dEE%N$:v?{,ZGe7*a"pG.G:¾5q <7MN<(:gzf@蠵i/|Eׯ9z ~J:&K4#kZ[[W(IH0gOK.VWVxwPUL{ՕUTET:Zr ߇ZiEeOARZccXC!| 2=3,hE 4XY]dd TpmYU>ڨBY{OJ%) HRո^UJE9 v2h~uJt .~/"H]]k :;I֑E/\,C=~2V*FE\u*?DUU鍑痢fҩuʥrp%x!T`qyǵ?O>X[&$OXs\Ǜ&&&x!oH&<䓴Bs.,ǡA;:g߅Zv 2}v_~#Gҭu<2,,,^FS9km$/a~N_.ŧޫ(iOJ$7x?\.immW_R8 Lvz{{e|;<455o߾_FwBq[ ¦+UsE(K\10ss,--R,yPU?m]|d8r\>>|'OE ι2s{^ 骈fsN  xG0Avzpܹs^$j-@D2L$Aįؖ)m+ܲM'`+U׽\ZJ͛7L#ǎ7ɕ+ TVq\öEBON`Xo0 }dYEUA[{/2sfIR ߸iJ%{Q 밲ݻٟ)-طo/ehZR8_WeZ^M@T:Y^R^lZ%UA%VVV1 p8LXB+Q{y:;ڙbnvTjJTh* m#H"(IU#RICũfWeFȤEAp2n  ,.̓LVc˲CAOH]${hkd~ao8v8H!ӧyػ{7KKHY8f9YE6^MCS%B.WtݨW.ǏguyQoHPW'JssɵULK\+KFv#K6G_X?e*Pl! qD5 B53oT+.,ىZ*[\+Qdoxw맷`_U=ѨjNeFoޤh,(D- r'[y ǽ».\rg-~ÿ…9:BXT.+_GD lL$f|-- 1>>{Y/ԭ~ΛvNƱ߲V/6ޫSSSS;T*++[ܸqQVʕ%^xE Ke&'&I>vKOOYzZ[%z@Mok k#'R^OIѭ}P=Cp ; ڽjFq mo,,..;`&hu]ƍMRZ\.i;w9LT&XvtäT֘!NqE$F8RNinnBxkk$u^%X\.mMY0m200D.T*"IJ4rV|~DIF*Iq7G`?/}#Q76(iVV8yT2n\tuq=bh<^ UTcnvDZ70HP`h~ >P( %D"ͱ1nMLK[[;\KBQW RI ||>a~LBM)ݻ5qH$B[[RUV0MX4N$(H,20|A^"Cp-.^94-O# >xVIX%6y:^躆ȲjXk{=T T+ |S#/%nvTDY P*4ׯ #cZr0 00tEQU_ʄaObmx1 JYu8pɤxKGG`jrYQ9y>˫k%yYs? x>,+DÕgvv@ g~~qx̆[X`ttU[8R)~?gFoo/4MEqlDI3$iBRX,[åv75}wk,}d."STX[dxKKD>:XtAB<E$uu5>FXС446"不EQ6 1; !m=Q{k iZ8M(i蘦*˘eZ446ͥ˗r@ely'qqihejj{ÕnGg0u4: PI"."mLMO،,444_╗NTB Z̑#G)kEcñc'P9DICaVcvv\PV6v\|MQd2,\va~g06viFnYX*aمc;<Ӵ~ pNZ 1qcvOLLlJۋԶ˗(J477\[ct:|.{Gvw~*GR!9gp8AGJ[걬{v-dْ4Lؑd7b#BUPMrF#nJ9AQ3A1_ EVE3l9FWw7xӧNs?@Lŋ}9|ߚu$Iv=ӛW\AVcp`uAڶERt4#ضxA_p(N6c(W4L.=}d2ƺp$,׍O㩧/ IDATI:4um]npwLbpEPU^ O8E|\pjsin3[[[162V&oˑ#Gz\vW#ɐbphή. )ofbApq|>^{5gjG._:?`[4keCS-]PnBt ǶeUplW\A\AĴL"(Ba:;zFb\W^>`ltF[7KlQpuDmn/,a&r~>U 1:>otE ׃1 d׿ EZrw/G}4:]] 3??"P((膁C$4CC(XMX қ/EUM% >N~6SqrYN%_ƕqu@7 L|ٳ?bbbW 8KKKH IGG'Gf߾T*:jH$B.EHvu+W{Е줧h¸cEQ]bX,ã(-6Q+'_%zw3 @0H="(:r|~AtN>˱c4I- +I>0,8`dwP| `)+vwZ?up(DP(4g|X,8T*]˲X^^СC"HeYtvvFAGF36GQx?ganQѨ(8LOO/ <>`&8F I( d;ۮn`Z}gw>CRdtUgG=zԨk˕E`K^Bf+x9y4}C}x^Ij, G_捏Hc|bg3 ;y.^|5FGPp[iR"ɞ~ Hdx뭷`uu6I1t՝[6t!EMUѵi HkeZ.!ǐe IuG0N"36;9^=4j*EF$DzūКp0 - lGӨq, Pec,ӴuMHcu++d2e"Q-A4Z)q!܅ eߏ葑=eZ |FCɃ>|"ccx<>9} s5y"WKRB@mj G &kkuN:E Ս,3;3MPkhƧ~jaJ"pQŖkkX_+*2]]x}^_RSO=?ssS{$ v 2M=wm$Xo{׈N~ Ӄ@' ~wo[100(u_xz2dvvY 𱺲8Ir#(M KRӤA4>CVWY]]Z-*D[f.0M,#ўZr!>chT;w?#$ LpA>6A1=`?د6G _ àVt;$L@N xdA\𞪺6Tp$ m,,,J8ru:TXs)"zW/_7DרU+zL;%HÇ6[8v.ax|> <~ d2q?m v6Zj p{}G E¨:z]U( bt?c|>o6~_Yf3LM%@RflR\8@Hl+"Ȳ Su~HF?D]cO-^VDFEORŶm4mqTUCeWW4tdE湯ׇnY(F:G`ÞQdžjeٮc#bR+Pk4Pd ]Qd-:;0Lji4T džje^L@3 A4Y BTLiK$lmuLCV233lnn044wv( PZN\"9srA&!caw׫Xf"'aY6$=-]X`zjb@!g~D{bȨ\n B^Enܸ:s3ܺq[nb6J_Dz-14Ma&F|pHKaqNk?q_\\dg{y64CETCOoY ͑fUΞgg'8px, l[?& £ id$c/;nP {ME9|ׯ$R??s m'y;^* ;؎P(B~gdO^tRD"`b|A;z{ȲGQx)KAőKlkK`YhgwAx$A=f(I#<<~T'mă,VzY\\+",a&K>>R.C[[P0K/184Ξ?#80<4v6#FOZe;ׇY`A4" Rȗ9tpť|> LVUD2=7C^' /  `,a[.w^mh 4Wag'ϣ`&Xcm""^ݴQ \r@HX8$B,JBB~6{k4&/wܝ|/r J"e*Ǐdd}}L؏QLJɦQ*}ڢ6։FhZ.B Rޞ!5QP8u LեU**ݝ߷dw7= DQ=s=#sQT5rjCE4Ml(r/=jo}ΝHgG7,ǎ%NOlNK@pn`X?0&'e΋%1o3J&D")Z`BB~{qlGrq0+++=zz!`f3fWgxN>c6?cA8P=oZ_ ~H$ҤnJӧN188b4˓Y-ٶfyJwg`vxqZhH{ldV*5j%tHt ExŗH(dwKѨ333iLLLL XtuuT,.-Qakkl&C^s pAz{LOʹnduuuLرٿ =_5=r߭%IquEðxW9u B0@?5IŢ묺͕w/S,3Ύl~+W}3>~[#'ƿdO/Tܮozs7n3ϡ:7o駞ȑ  36:JC:_lnC:Оh' sAA{{{ !_(HR=Uajz!333KRƶ܍둉>KZip%d6zd HA ;;MǣP*78IomLh}lyZZi89{dȫT*F r9zz{& Shd2 b̿0wTMcldcG "u=,}t `ߺ7}'bw/32:JGg'z{ _Ÿ388H,#DZ[Pإg̮FGGnUggI\2<_(6T2x9y  ҖH^`{'>.llo,[C ;; E4AΝ;lnu͍{'xQfsқ 2==;CTp$ʙ3f+ĉ;v`0½{sEO`>"K2GE7\ 㲽xv%fF6>D/Y[`̥PȒD\&oAI︶bZF:(b1QNM~)8|z3a?ʇ~ȩ'ɥYZƶM_au%E,;J,o}E7\Pggiu$mRULˤP0%X6pUS,ls E0ugβ](?@jcR;ωX[]A@ģM;a$파5jhJC4gc}4âEMNR(l5D l" }EqHIcq{|m~ӗFD"AyW)sqq"# Ht'Dh4bwEjU^^EADth>@d>S4^a,.ށiwS*, `&ZGAqk .45$Lt .ZR - ԶmŭȄdEQ04ׇ-+ܛǰLHGdHmB0 FSwJ-YtJ@_#c8nhjLzCy=TD fg.#"J ##c8HjuffV(^/>lStuusAi &++ `iih4@6- r P* B|ﱺJ&e|FJJ.er>N>A84.Dz\ķ}7n(,--c6D%J 7åKu&;;;t'C!hƩWkH&AuDVdlnv+uף i,--g|,beeiXʼnci4e5Daii˗% q9>;/G0?sô)OtFJoM(b|l׮]Dzn\ѣ9v>x,N~gx<Α#GZ]vL1_ rhmmueZLgdddE. ttՅAheYvjq=/8Oj }kv[{~7Ygn UU-fgg9~8 uEmJDVȑ#HL$\.ek388RDH46ܹ)hH$‘Gfx景NoН"ˠkuLâ Gb ~M|>Y1tlò8t0Z Mmyde?@RvZa1;C^'bFsR5PjuYPUBZfzrD\ZqYz0 񽨪NJZOK@?~ ra8A" k;`:ZI9r@MAR!oۃa۹ aZVuQDQFV9q8BE`A]u?00F{ 248#iKog*.U#іqQ\96,F28s oĶ $QV_+. IDATh] &`UX__caa86gΞ%~BqΞݩiN:3gC0{G(x%r9z{{N&WS9@bc-d Њ;;x<B$KAܱ2rYLKݷߦ-yfSXX^]roً8qb*HOƶB0?~/ׯ_q޽Vy7E{%uBAb( nŋ y|rphR*'O>kV.qW褭z{涷DH,ڒ)$W\VqQ-4}cc 믽y|)~'OoK "x<D"3__"K"b^,W8v$^O>EOo7\"4$xz}KV % Ӳ < yp@%TaZH$y79I21s3ʕ"{FIRtvvrqhqgCE) MYѝX )eF8m6`O?US9?ri_]%ܠZ-%,ldPְgw?-( !zl^FCmq^{S(i5 Vr Шqp*m++Ms'NqYΞ>ɓ'iooGpVr5 /Д$4n5M6c!OD4݃&> —U p|iY:պP:t=cc\r۶k8mll+~^ v(IT4:|#Gr]$ Y\rKzL.믿N*A*b;MhhOCkљ`in@$E0 *KK8X@3]&ib#w0-U^ L$ a6*2 昚C23_čCywJrIbmb$Q$N13=E0'fpditMD\HY^wtY rP `z U9z7ov ]]Hk- 4gJM736bhx|t:E 'a} ;j pd Ce!@$"5\[ǴMd@$# K2۶t]~dŕVdHd<pPap]31դTϔ$h4RT(y0zMSy@T sl6Kemm|1mlmfY][# 3ϑh렫I 6w]̀c15Ν4uX_[#oQ~_$<{E)22g`r$)^/۷hookw^pr0]D7^A!w.c;o[E}pw̰'(d"nYS?uUô]pxVZR(/p)VJ@K0M%¡0{>xX|~g}@U.vW[Rݬ?Gfn.ZG@4 w 03S3UlL[ G\&ͻO\IJ4l9$_\6(D,ZL]q,( ӟ2-Mgl|g?5Homi: nKtd~~(]tuuSשת|RMgg7JJč?T,{tuuo|?up,X^\VO3::#clb5CITJ #Gu*^V'Y]&²M%GQd EW2ٶ-bK 1cZH21L@dكGkkTT\1םh+|~~F xIW(q=l$A@eQxHW*qr QJ\dO_^^eye σTj5F[b̕+ѓCU*nݺI[",׮}H˨0w"J $Yb]LàZ*`Q&IJ9u${W_'mB註[L޽C0VE#ttu} uŪlQpDs#;ƹG>۱]D[>kt: 4YnwolmOpzj}gϞYijgOQ9|M~?T+U15uvS,-/$OwjJRw78@ȱQ$Yu?T~<;c1o=Q0 ffGR\zA\a KRm(x=`;FwW'@RPjC3x|~y7o(yɓXřpla4U>P(ȉ'<؃,v wq]_l<. x痭ϡyدz{lvP( "Pez{{+,LTA0GwT>:yh\rjL<WǓR=eL2$sA& hhD :+]!ў`,hGڨ!I ah`p0"8|b\JHwWR0 YP0-L#! a!XzD>Cc""Px|~WY]^&/188$+ţ ^^}x~Dbzi>sn|-R B xd4 ^_e3A8@ȘeL.QSkN1&G%Ph;};] &"]IleH70´Pu*<=sǬ-?k+Xư,,@TdDǢP(!n@pn ,[Xnh(rN0Lri8&Lb+kI\߽иtY(ϝuG>E&f4++y.^8ϝ;`:mDcafgyWb``sgݝD\֎$QnPDAPp >&kgX" &u}>nxb(bnB%n޾{WNV" Pسgsgϳ&o\㥗~`,ܾ)w>wmʕ2{&Bkz'[[Y C 8_{"huCl+$ nBP|x iV`:\ &mp\A:׏GS5Q`my 6ީ4;B^Uՙ'kch`+W F<8pi{drp)ItU@  4oNP{t Jv)]M%Vp?ui$ZӅG>?nnUǭ\%ãH(D ci見u۶-a;zAzɤ<%tD&&&lŅ:pd q,ʕwzx>{|1CQAhZb!a+t\.ۼndN%-q9W4)#HvvBӛ\.!:`àx8s"?c~@ @7MDVVʸn ( ǣd.[nDdIrvQP<^,A~sqv6 ^b& Ii,Ix-UFRgYV])TlJz ~DAtA`0i/>M"fH$<3D1 `yiH$ī9zz9,-^TU xDWWrضD !^>o(d6t'{ hvn \+3BLf|>$IkktttrE߾_+ŦOa-m݊yrqlj@E;puLY__gccv0mNLL&G,=<>ŅFFFPdW ttv?8@<' ST~:X̥2۹ŧbxxFA.C4פQU|}~czҞ4l]pvnZА':ӟU P*a\}9O8==9`fI`&EIuloڮ}[^_-JkQ)b&39t} !(]0>}7ݍe%*;wٳg h:####I^7m-"?}$&Kwm mgWGsvu~`0xd0 |>x< >C*l6ۭ166ArIvo|5fl|wn3::J]ױ^~ZN6ѨoL0E7jdJlfxE+ TULey_~ɓ'dld{,00QE7 7m ֫-RfHUc T ERk:~#dՃ$|~WPnZ[Q%, 'bT9LA&}&ܿ}p(BV" ݉iY14xd& O1wofh8l:O=s۶ⅳ%6?S7 66P$r̽wǶ->>,IF)~ Cn8ǽGF)"靃ad;<.$4ӱ ##Q*08/VɝzS+].W$^#9iQq4^<|>SS$i\y_ea~x0*##LAQcm}W^yv(+X*  dKz@B79O= 'GFTjLOϠ:iho$RXXåDaN>!266KUqfkSd*EoO/r(x<0HMX,.=BQdFFGjR):;;R-o}ö`v$bQ o,/)ܾyo;Du8-|& GJ%~bYܸ΅W_Rhةq߿/ 0uP3 _v~W t;;3K& zݻw}6,Ioo/`qEpMQQJ"33(>Tŏ~uWJ chx˗/ +Ha|{9s~4ybߊ7Lp;oz:Gf!ߓe(}~]DuH8|޴,^78RB!ŋE=|6|ݽݣ- KKr9`l|EQHR@LO#򱷻CggpPx((G3M)r KT5hkkcph.^/||C\J$NMJp2z~4y"v9uÔwJX)KQs=gNsM&[ҋtt8m$YZ\Ĵ * tbR*"E%N<֭llQo4xWX\ZQUc}yFAKkEUO~3)J3cs `b4.mTkupgTlnn\z29w<ӳԪ5&4glMs s) ,Q]*T9Pdlgk7n6t,CnPבo. ˶0-@Q^7jJLqd 4E7Ioriؖ h ]j! DKO]biZԪ5ik$Nhq\>ǻo4 \pO?CCPܚF[[;mmfh$Oț/YmQ{Xdֶ};vsT iȣVWWQkog!LC[[k>=ի1;;MGg%" cG$eZ<%yZ"I>^h4iZDc1$hhm%‰8Ǐ@ /J>s I8rժ anlpp$ RTf}ݽefvBgc}>'OGVg}m$|~^xE޻G&Fe@8>|#iY +H}l4ƯC$lIP" {&Eޯ9)!,qCW }Hoqy IDAT2ᨸdYϣquy󮭮G' DB#aYF^GG`g05yB>˃ͱA&ds}߃essѓ#DMs&<Ht Ov122 8\ta&,/PUMS-f%YZmR@hX .jiPr$Љa$Y­:6lCsO~c*A%R$mm|^ [hh\&p4lkDsyQT\6K^/|9{z4?PxgVYXX,sϿ8~8ccc,-/SVp{$Z[rQ)ԴFCgkcssғ?'ς$` Y ۶&mvniؘEZGBLJ$a87ǡyU\*uxlgYQp=8\k:ibKIukG ˲1- IMl"4FNi^,æKO?KKK E\fa^D@g2i>vGc{op8 Z_&=W86DgxG㑀`.H<lh|inTk5,"!9022BPhB -(|VFFN(5/<"z${墣1կEo) Y ΩVVRo~8{|\.KS s8qܹ$;>sx7x=X_JBWw'FEM;/U#JQ+1Fii#7UcS,YZ^f`4008HkӲHg2Da**ZɵkJHO0?@_h ?$WT4dYBtuuZp(L4!O4a bH*Co\S(|87M_#8o[v#5T:CR%J$FO]hpxa 2~$5+#R'Ar$(I|~hb|4cchn74ciΉEb^\&2j#V )T*% jֱ,LRf>#<-CwOZE67y8?mB?$<%1tD2^3qSUQTii5^G4LJ)W*Tkuws.>/)d2nff&UaY 9p|`LlEU\xYZ*o\24x O8[9uz6ϱL_o7ۛLΝ[XEѠyFO25=E>G\o! /ȍ7XcIH|ϋ#aOQ&V\k2%QVQ,kYQe0ql4^4S'e1,Ixܘi9XH,*\YƑ@VW0 jF8b KOpw^7̋<9e|r*R=ҙ4Ű륻 0ek{P(/D Z ŝ;wQ˲FcCq|O?ul&.7n J108pzbO$ʊ!_P(Gf2$NrKqћ&bQՈbPVj+lood顯D`yyu83ry=Bo%.=$d<ÃiVWI& vVרTNZGu:;;qkn>|Hkk'45x Rɤ BQaybv&}?&HjB)s^$%JL:Kh8Axx,~W%qd~_{$n G$ssql!A 2>1i 7EcZX[[?"Iq:B\h4[Nc 6eZ(+4 ,hNΑâ$IHKp,qrJq؟$ شLl@U]GD :R˰yKdEj5z. Ͽ@Gg'p|G<}%:6b\.@O03zꩧ4$­(lmmrE;8q$?ٛ~ |P$n&_,%L'C4 @(0 Ţ$<^7k?} ~ȣu@d'W˺Bfs9ܼy>wS氶͛PUSS9|Ca4M# pInve?Mp7#Ea~hkczr|S41srtTEXT*, _5 n޼m;۷hkk#{ﱿOWW,E7z}M?r R>hKxK n}Tu887kp" $ًNs_E~dY7  'IK fLR=\n7/э5qi-KZ%Qי{Q\(D)Gu Ы5n!Ѩcvmò>1xEDQp$FlV駟f~~`8Ho*imT+%j*-l[+ |Ui}.jJ6#}pm;EWwI8HfHh.7Jl>(.lAQe"P=VW<"chAhUQ؎*;ΤyfF**e-248F2dR~>x} }Z[[Y[]R+Ű z)/VȇaȒi(ȲՊ«4Bf֎VeonTՅm;\BI$3c!7f'CHXM[[=d9dI\ 246}c~~73}e;&4Mvvvxpwo05}uNRԪ|N$W^}8Jg0M|,+ Ez{ktwt{:~,,.{P($e F]Ue&CAͷovop% K26` $<Q`^C&!#fyn 7OW lmmӨ7;u '\zw}0 va4( CANP(䙛{ިsMTUvkMS<?8'GO2rr[nr'[[Ѝ>^a4\p'NES,(.}:BF5qo@\6K^Q1 66>A__?mO?mmm<ܳ;U*"|?X KF8} 3mM8%gfzP؏ۥo!J yWl0‚l4bDqPXXXX^Wt%Ri2 !HQU;}xtq/AXYKg}.* /u^ՋT 6NGE"0Rܺy n61 `0.Dܺy\6׌FͲMTbm} ͭШ# +@BuiEz::I9NS(&ޞ>**bl&C.Esx4 ,=^<Ǐ\*Ѩ( 2rE&0#*ibb4 ٞV˴EҡiXzV*85:5Q p8cfVD<ۤ%c?)buC'rzb FtzN EU}x mx)f3nwK8e(|+ aPVi먊lVh:i`&rp(LRĉ&N"`R1j]C7[8́P+vnĊH#bmH84W(`+9MӶQM8ZM:E3|b3)D[b9wHm 24\jB_O[oQi`csr7W^FoY^^S{<‘묭.r-O0qr S? ;{Ԫ5z{xhkkTȳWX_[R9u{LNޥS,:m5Ξ>>nM4MNqf;";Y vTgCSAX¥ #a<^c&[̌GݻN<VJtè3ia~U.?C*yR\6K*&re&Μ% hՕ%"xw}H4<|B1ϕLd3;3MCQef̐Τ֪]# _K5m)|~`n6-.EVV?'7oJ{m3>1A[յUݻGR9J=dddNja'?aphgy\Sf󌌌200H'ihʗ .x8N0 mᕗȉQ fv{>}zz{cfz۱ F"E=qnwSڠX,)ջBֻCA>G(: O_@fW3Z <>SS0*86*zM266AwW/Jd|>ll4[m|zaťKO-q7s<===%;@jzlk r H,B:-sz{{8gmuY!Po԰- IV5Ӵx<Me@So!r0tg}6a,U(2;5ѨˤqhkTJ]yLۢPȡjbT*ض~3!N ,-nJGg'|Ǵ?~7`(ܻsi5A?b]Μ9SHS*yXI ;ks/0?jB. j ˦Qϋh4|j0@zibz<"Y0OM$M7Gq$Tk`UUy}c7@"-C9{~?K+lnlp-4Z-J|.佻.'@>`8g/7Ǖ>fiq-"(=5]ؖia0::nX+=}2ir\>>j32,C, f\ `i1u7oeE,eqx<m]Q!By^fgHRdrYTͅ(hˈ.!pܱ-j:V\B2LJ|twuzq޽>y:{;rY*Mx]IX'ܻRIdE9l]sq=Z`{wӧT 0{[4Hi[ofgg)~ҋ/ QLÉLl ō7 Gz?PWx l˦י۶ۧZꫯ288H,ô,,_ʼn'8  BPP(t4MqDD6zx qbQbb-1@ʇ!M0kFIҩ}jeb D9OӌP, cMs3|bJsT*%n!!`vZ&, nRi\.5k嗾Q$*2zSXc61m}Au&Y4-4̓%wsi`@m#t:CPd||5*"aP*x>:^l86.v膎"~RiEz2R.Wa<͒XZ^Q/,¸g? ϿK856F^+ʒpEFV4P,$hn|NH5Iib&'Odpz~0 S7uh Q QVdTM0ͦ/, ,UULͭ:hAӎXj6'\]7eIAQtvf…'88HO[p8=FJmZ,>zDec}%S|J%$===vvw?R׮Ȱe"o=іd,ۤX,`bP DUV>; -]q ɂ$188e0H*>{G %Vɒ`gx}5k5: OA8^o|s>G0(?\y7Ihk/"ry|CǘG8Vog歛XIش2lipDkv3~jţIEQōzdUqdE55QB~XyҨ7a0!lvB O32UnxD[?sTUeem)_4VWܽ}t* Rb&}4sgPf||ǁ-F~vvc%77 <3INRbow7E1lؼ79>2*+"EXvNO36v7ռ"sur,Nk{Ǐ= R{[{(L&X.Q*FB.\b&!zz{yqkn^/񖖣;_?X`mm 4jqlomE&Dr>\_vNƍk/̳ceeQ.{8n 044ıcCw~2IR㏯Қh޽;rLSt:6-~L6mT%r,S@SjM6t8RΟ?Okk aQ >~租9?hQk;6|ᇨJ{gs'81LRl_~A*"K((ʅ Qo$DCܼ}յ5~? hj]pb)O%:džO!Q,W?gH|^3MS EvHW_aggjBT 8z#9AoOiҔI\jSU`bY5, U,s$.FN0"_,`6&'EnX5xnlJ" nDT?boofm Ev11q٬C%NZJ ĥX[_Ϛq>KKKDQ ==tvvpO$^dYAR4K5UxѼ m 4W^T.[oEKK CT%>Chm#Zׇe\8DzY_7A d?M0O@?==ݼo낟ܹ GےTXUp(\vUBTD",txoJg  1|㧚/ME+%E_rQ6, ,.-rlh_eqq>`KO=:Νa:=A*daqjJ%f`KK^ ]?ZijvV JW:{):ۜ;wGbu)˨.Zaֱlz[0>>Ksvko  CE=2IĢd9<>/==,/ZM!uC^òۺQ7AdYAQT2 CCdY4KU<LDQ$:$ 5qPHqFr/p{ܸ}d;6~1>#vIx`ٙ{b;6ΝSO2;3I(Sda!n .=W|TEwO>n;jeLm(dK`95 qETEAr@o4 2LؼfEAUѰR(JX"I~^6؎X6"*7uׯԛy!sI@.Dž$no]xvq{!676k.]MG\D"a2 ).y࣏>P}`0hM;ql2lӧ:v 0 ~'FFS4neew~xk+J|>|?'v3(1Idkz]?ֽneK)@ 9uO>ůgkkB>}osUUޥ3ڵ+M3=u"K+:zi|)Yb~fB J\۷x<*n5 ֲ-:۵Ax }fg5_V2Z\~~dm8M^P(25;4P4jB^=V2\4 ܟ:%WHȤ):jU|_ű]<[$)L'deePH'sjմD#4-QcpDZjD""?ѨE\q<CFS%rO(^cj"}6Ld)  Kܺu~wo2M\&4dx`H(Diɴ =q:ZHZa&GۘGah*3 k,-"I|N7$b6 ˴r4:vޞ.ݛ@$%a&bI:2(*CȲ٤eXՂYk8ۄ@-R?1=X(B$Bۊn)"AVU$I!չ3ey;;u) K<*dZBR!j b8 P_V!+ Y Zx?ݳkoޑFNTf\~URmi<ƍk8CWW'&kgܤP,a;v豓w!晝ߧfi7U*߿UFvr WWC:3 r.sۉ﹄t]f?N8FHPT\g6СÌ4d"A_Yn޺S$K#ggEAt¡M@sEA7mx,B"ud{eS1jUUX-\50i4=0뛛$SIFf 3X,/};0 VWBOe'ܵ{r={8Ob333|t"K j}N8C0FA'\EV+199"lxxKhjڰ-lѬxb׽wyw^fmbgUE~24c"k>TE&k#GHIB//8GViZc{hg"ezzp,.< -$E" f#4UC5t=JWxTYcuu__#sa|tig=}={ GyYRmDbQ= GtK/o35uPD-=rNvˎ]{tt o4m?E_RW~J"#dnfׯ"a[Dg#Gxg1<Kd6G9qLɐddU%ӖJkhK$I p I4Zw~zN<$ޞs]Lqs ϵ4*9$ R6"(&##hLZX,D9~}\tM#K4epd-lKPY]Y&cZqltM%IU*$]fpBXVP(W]U!񘚺G!NR1?H$ڨTTFŴm]8O=SO=W/]#8kk+ t"I:E*A$fr9FFGIO_4^-@^=5&~nV( |A g~m"egs>sOi)QBVUyH&jS%җpk,.,ҋ/љ N3t^xrʭ[D" bDWW++,.,pelǢ4axxU0-aK{.~xjΝcȲJhґmǥPhYCKubDX4Fޠ=jR<_wMV[|ިb8nsssxexKy@'YuqEݟ$J16:"z2-(#$~*FA)g!Z\dccvc naZLӨ*Dja4}'x4Fi.#;FAYX# lbǎo_a(EHx zj$ x"!ٲD"u2 ۲,,24Ú\ϣi¡|R&KPUpQT d۱QTxsss;{1ndnvD"IWg'rp87oc?I{: Ν{mٷ0:ŒQiM,a-)0-kOg$(rYZ6c[.P7Dsrr9ܹ144qUbC<3ҼFs%>8>r;rwzFGGt{H]S[Y>m#zb4As=p$ky**eHGCK8*5 Τi4:sd߾}g2D#Y&sl;Uk$b1{F#rzˉi~aW`l xM[F'˭`} [8 T5?#`-.ٳ;wx,Ηe$If~~۷p18@OO/;FGx" x77 q桇1 u_X\ #q#> :q=ށc:=}}|/q: -uPWnU,jğYWo [!΃,.-qõWQ1':x;H*2]") 0==az$Kܽ۳YZY(,?@kuв$yH?F-)m-yeq8{o=n}0HH$B8KI:/] aY7oEYX\bhx8s ,-.B,GUͮEע1:2+KlJo377 FMv.C$[P8}4{Gm7X?Nc6k8ĢQGT"EzkDYV<'Oa}3OwO#2;=C4F#jQLSjuŠQ*y+/14<=X[[3%_ި`4 l6K6!_DZ,OHVD.dn0;=gۄ5MV :{ayeJa4qZ=jˈh+EumYttt H.Af1 IVEUZ;kQŭ&YFQT)K(^c98iX+ b66 `[J|zMH :ĝhkkҰmjAP%h*MCiiύ7xH$hK/ҙqe]J4^ѣ4Mevvw1$ 4֪DQ8@ ۱x"J{;;,4-Ee]tvur8hYo\cqi׵Q5aVB~>rwHif~s,OQ7yn/ťEC%_½;qEVVXV-C HYVh6MPYZZ`iyZN(ؖ;c!\TUَ q=ۖ,a;.AH a# ;@$ԫ5|; %(nɦ;E*54YQw2==M.cffu7IWw7 K100H[[JŅ+O?C[:C.CHanB R:YAeA|A ()ׂ8` e`>3b!ן~OYgo{4x<%O<~xhTmך8}ګ*iP*7q>8faBIxMsۑO5aVx틠jTt5MFul%MQ:2==wtﳲe!bɄh<[ֿ`ͦ2}y 2k뛘A^D"N"ےD#aMYD”JEl& MT$T۴ql CH:#C-cMD1&$ ɒ (!A JAў']ø)ʲhPdEbp==2+亦E9qDD"A-2iͶh40 sP,$z>MâL6GX7h\'ﱼL(v˲$iMffFȲB<n\/AtMS_f~~b:<補p:fphRoA"'ӧ8w:;x'IFGFvfI2d_fpulYھcMn||BeVVװmg}ٹYܾC8!n޼ɽ{4\|.ҖL?HaslpQE!AY\K_z|~Ii4ll:t{vbŘ^"Kq?~]rorôE5VVBLݟѡaV Ep]GDԷ>cɂFh6 @pnذ* jnv࣏.  8ۈV8lZK" [Q ,`thGzZg{X^\i4L12o)XMu6Vѵ0dY<#֨fq=sΣO=2u… >rDl3+C zxK}b@4[c"-TRI-'b*k mH~"y^9|뺤i+G[:q_,YY]]A,홌E<<JHӼ[ٳz*nݾIs}fgq]]vQTd۞pDwbdܳE:9tF@V\$EzF{H8B2R.#磵|,DQ%* ߢT-/!K>qք²-U@t]' zR&!}UFضϞ=zػ z'zK/JGY\N+2-'O_\)0;7@6IOw?*j>h4qUp&&QVjk[vѥRu|r\," c ,ϯכ 2:2<2硪:Ҩ7#Ba{ oݦ0pF$+y38#F 2! H8*,e(`&jr`6-b:|7naNܺQ7=}؎p(5mJDVV&}q9$b篐J$vX 0u4U" H$f$ 41-x< %px~x-'<(lY_TF-30H7 tCQFGu٤k;7`vfSNў 8.MZnolG~e5EaAũeb6H Ø`Y& EgF4&j\zHvjn{(L2EԲDMl}.^|T+ =Ƈ^_X.27=KKh$˸T+U¡}}rH%O(288D:ctuwp%ʥ 8i6sq =BTP`6/FH4lGta?eY[[-<|/@_ȑ.Λ"tv0/evvJ$)::;&#@E3E!+2zHv'Χ$ s)˱T4 0ժݻ$ <קj^:pY' ja?oë:j{'}J,]]]Tk58q HGWwHTn԰lh,Y^YBd)rL!BiP*lG}anSd߳8~x {ҥl󬯯s)8ȁq{;-vw"x g*IXXX`8+++u$'N؉-t .o^N|m):{ucnr::ygiZm4/.$R4-ѠD2XolSOC8xX\\B$8=|#Gh2zH|VV8<7 "@ X]Yeii .011Rrd2y15=?;"|`C!HY *:R"ٽ{wK2.6K/2;=e`URٻ{/ZAtM٬q>*Ǒ#GXsdcmu Yk 0ǩUkl֖T*BOӰ(B*G$AeR ~)b7Vodh8I,|Lâ=LVCowEBId{6<> }Rvj2ssLܹ툨T EQ[ͦ0jZ\XT(ks^@0/u|?{i4tޤ^m:DPtZN `ke(N2v>!Ib1dEިPQUIMձ,K؎4L*M*D45L&+4hԫx$8ݒj8m[>: hI8,N"ft.VVWQ$ոXiD4ASUjHDkVVhK$!_,2$K$s=YS6mq9tM2$Q,´Lǥ_}:vzaQd!t U+:S.Uŗ_fX>HWYX+8+ d2i3|tkxA_o/tT*%Hn's> "^v$YP*" }hzr bvI믿NZ}xG2adnN|cpёE}&{A ]=,.]$142H- ORX,":s^~ϞuTM(<#p%̦%|qp]<]ư, wEt@ߺhr|1̣*GccmYV1 ӴEcTdTMu=X(Q\EU7jK%dE؉TJ%E-hU\EgMtM\ciy$?ιsP*uϨP `,Lt*w;ج4&8P$ITضKoO/Cy$RǴI$mC#:gQ $f5FGH&$R I dlB@\j&hAgӞ033qָPEH &tvt'H$S35=FbH6%Й$JQT*<ԓ *K(G_ |~||A|{"}ACž|<=;MOoD\p$ZN,u=$}}4 &E+Ț£<́q\un֨.Q<4Mp+|p}zXX\daA'K&h6p=99rR(z$j47Wqw TemmFX5B!]vJxX&BBXzAQo&x>i 3#Uc= e4߉Vl)ͤ]a !>>DUZeaanES$xWT\.GT"LE'z`;dC˲y6ÐzNHѬ*2Ď'{)zÐ jjN4 ~m9a숈Qξ> ɤkx{eڌUB tD[/xqgldW_*o<\x$"s= tI8},>{0 ϓLؽ{7C sK$ܻw˗/={04<@w4;$ d7~J&"\|RguCʃe'0.ܽ};wnq /q ?rcJ9zŇy4~ݻk8,G븸{\tjU/u6$# Fz"zM zNDt?;[ +/"%Di:T6^muh۸C_O/|L6Gӥח_z/|K?Z`phfDfY0 FUlBR{"v$YAEc>ٳL"Tǎ߇0(  ŋd**dqvbH&!J޽{$K;wi2BI^/1<2{!LnȤ3[mzz$gtt3GPe{aY6˯Cb(ǯav_|x&'Obdh b*os >zoUG&vxz'1J` ߖ뢡?i?EpSd2.dQU5m+drY D2׮]ʕ+?8F* ϓܹOŋUf0 (t]\z;mcbb~hg?)ZT*08t=*NEU$r$Y,j:QaYivM @RddE&aBFhأaUK@a;7'PdL2cśկ2a_.s!kY[[u='&{sK 9zx qyr=ؖXY"Y2"(t  @@!e1;yzJ*Z4AD)HB:e߾ aNСñ|G_&+X]] |?x|x&ܺ}ZmFA__U??Hޮr}>NOYxoDOn(bvv(VZ->34<ùY*F6Qr-gxkl5h6,΋!N22pws2xnuБXŋ~HtLʃeL&@& Eҡ,KXDH7I #ags.?smchhL&C^$IаlaqOWܹCfjjjIĝ;w(˔J%aՎl6˻vFX$N|~vk8ǵgxd[~Wtڤ3IPP, EI٬l04loos}&Ggx";ŀԯ HaC R_<9I¯N~Mq#"L DuvJ`ue EV("@4 c;O=ȲHPKq=0dtlG*dtlMym,|i( sᄚB6aem$ qi4+$x,IE¢788veDzBzy =y̑#,k*b6[A >$%*/'N2GG˫~Iҩ4W^g-\ea, ϝ{wH$b}'VLb#W;9B "?۵vMTl8JvB $J& ~EHvmr^LNMRٮ`]lD!N*fC2*&B"07CO>Dg.<8g09x F_~]wo^.^z=|,&/8zlvܷ(=v,|ߣj"Iyp.Jٵl.71M?*f ?tcR Df.ŲM$sstU%{s™ӧ^ʕKԛuڭl2+ɐH D 6vKLcq{XE(;r'x.\)߽2o\S|;dR-B!/DLOfi4j5T*255(b(–m޽[fnaq[oNI&?~7o299)Χj4ɤ?`iiIQ2_x%PPMӤM1KsyFGG$Q=x+8z(SNr,/&bI@Q9?%JOM>.Rc#F O&&_(P`scG=:CEw.BBEQbDIHĝᅫ؎}HF^CUTRzqaJzJ yFFH3x(FfbrVI$92nWe_8[t;-FF2l3>6a<>$8l$n4MYEt֨תF8KV'H/}>?ݻIVQ4 s!޾^4U(*<ZuE"sX% }dYѣDRt$"e "`df jHIqIqϏUB1aP%4I'S yQ4Df>FUÀDS˲HDH$a =ξH ,[trgΜfkkJw}Je EQ/@r4AQ^xvC_~?:5xl-u]%HDni^}ZǎA4fU*N:$KT50BUTBt&CA <gq/ `ccM޹C*)VQ ˜q]"K2ҵLGUT4ݧYu!UM^eH3(R%lZvŴjtr,V̎( f0EQ(O68fA@2iH?y?|Xf?t=jHDp&&rʵf4AODbzO*%q)ϧkrtZ-&!p!4 FV266FT\.H$>q8>^3;;lj'v貏5 b@d2"%RKi ,Favk~׿ Lƶ>y(w/tؿ?}U'&x7?MPbrr];LI0#~i$G{>rK[9| KSO xwٳ@X NT~@Dy`jk p{ܹwG$xdIZ&aYH2D ?C.'/ȊB]{x 66&@SUrOlՒ$ E2|W\١_FY{m$I]DBfEgPFOkiw6;]SHR\vx)(.ݮcDq,q@Vp\=$ H *GZ I]ABBoឌ{)G O@~"Ry7 w? (tE?__!N=y=$f ,U݋$Aw:._$adi" Z!dq,v-v#i;G8mbdT5FƘ>Gy0;m;9o칳λbd2i "lˆjsϝgvnq|0MjtVuGFXߠkvn.k*Zn${>|u7pM7<ݻ£EdY&J>HO[;jRDVE$׌{wr=fgiw ZufC#1mYVV#:鴅0wԿ~өXk!sc-E–Eڝ0Vd Xw \1ђ"LUM#026WG=;o-n^a$V`c{.k2U)eٵ{/?@UT$tsQ,yCL`qaA-I׆"+3=wn~g> fﳹN6!|59B d (  q`{oO `5f`dtg8EQcsssx!wަ^ /?"Nq`zEQHرB^㗿|-|0 ,wljm+л$fyN>%V8a߿:|ESyo2:6&lAf;p j_J\(;',<1xR\q |Y,s5í?r [[[M4Eۀ cGUᕗ?&0hX*nwo?m6?3.t,ajF3r}ʃCdɳz zݻjyPq]kzQ'-YL*IbeifӧM2mD gα֖YZ\TANAon$LiA1r ɰ B@Ʉ@􆁋ah\ K b$,ϳo>|?I'**4($ c;8(0BU54APUV 㩄@,=( ]TI@H)d:J`Y]tM# |{v[nQr$ĭ4ju{0^0??e8v۵|6}=]eie>B kˬzf+ ?]dxȚ\t2 KQnxCi,k- 4I QmA5;>*mL 8;)*$4&g{f6I,΋0p P7PЕ{+AQ8Ϯ=( Ld&AlhaES$1 \n+)D?mȒJ!_lQ,ٵgHt{xvRUU|pH{ӧhGT&v^!P5ՕU'LOIziRHg2膁y,-3X@UUW^euyw߹C|dמ= sE4U1͉ YtQ\j0;.~0#"bww95Eop2:2b}c ׮]2::X=roς" {`|ݺ}t2N8i NGZ޳q1~nz2ANLR7FG0ˆ{gi1Fuۦlqt8}=ٳ˗.SJV%0.g#)l%tM!L0ct:mt]%I|Qr᝷qL q \\.iݿG!_lkH6VFUl$"n!Ɗ"+>ĠE8tD"͛ lv8~ϜcissSe:.^*CTqlnIB9TOӔzJlW*8n(?Bv0DVd4M(±\,"N EB$x!F"iLMѣ%:Ͽ?cn~ŗ^`~!ݮpe !lL&+ص{pifgak:vgS\3B/n5)"8D1hA:]xOE߅AR0MQ4M^o026*G ?\.*z^*ŔgN({i,$N ߱m~2::J:vln߾K2ׯ]C%Rf3ht:bB( a[&PFeסT,b*mj4Cӓt]NF2(&X 64mZ԰l{"`MQT_4uf],/ЪISȒ'P]h,D"ͮ]QdYwQ? C4ʍF+WO}(7cheYa_M⍟뺜:uaZ6oݻwy{ch QDOžⰦ$$I|@G%!ǦyBQ"ϱkj!$WϿ@.`bNZGUT "arui48KXZ{ﱵ$ |/u}4Ughx#!|2H B"${dr4]?N10?/ H"sYlۦZFVtCc`̭7ywT*jUM ˆV[b0$ ͢(b\WuP1;v PTPe )ɲj|K_}LLND^gxdBm;aiLڍ9SploWH3ۿ!LuC߿ǝ;$(j5$ FFѪq,|ۡYRF4DA@Vѣ R$U""DxGN'ϲO=GkaZ6 I2rzBh4Z9ۖE(#$X>Q$HhԄ3 ,3HHdi[n[(0-UPMG&h43sI& WeݵQPQU8 ^8wYzJxHiM Q$UlL|o}\=>X\\! !:>REw4lׯ_֭[ n$X)bM뱽]%sssLNM`ƅkOOA7nҥK111sP5d:E6'IL0Mb}{A11a ĉQ$ %6'Lx{@xxZxں/qof@ՂFH$ DB{h4v?R$lkqB1M]v8/^"HPQ{s: 7oݢjf$M$Ow>E!i6m3;7KR{vsL31>Jb;[[<gjj$4L6;wyܼy{iTUgbb2M߿,H$Rv676X*QB0pǶtSxd222VӧJ\ϥSia;N'i5;nq]бmEGQ+uFƘ>@._bPlH.cǎcee]";Ƶ+hݕmYhiZA@ bHրHL?62Iy@1H'biIAj "i*k TRE("D) C$,n^vDm;Ȋnt:'!}=,W]?|`]$ 184k}ᅬ"NW(]qab0)\C B2FIHUQV٪l#2F"!tD*&384hXʲPerm}T*Ñ#3]׾I6'ɓfw>~1p9Nۉ-}~ T[LL߷c[m4Mo6sslnnL8ˉ'ys:u[7ob]癝"2]7(cpz?HgR1{#0sl[J^dEVo2=w]rL1!Y?-֣u2DDz|"EDO(!X!FquE#H399 BL:c;\;}EVXw##($JqGmzLU1Ôz՛˒m)N<G>; OS.q}CufUQٳ{RL'OS*hyTx.^I"ҥpQ(XYY}6Zf(B&hp $J D"(T!󬮬_AUTer,,.vԪzzQ|e/0 # B:+WlN\3a$)[_bɊa@qD'7}'ǯ==.'{"I`Yw2m=!aDXY2xm?+9Eαu P5%y#l˶tō( ź)p19~ssoÇ_űg=y(zV Ñ$T*ܽw첶,_#MS80}@,!ro/ DaDX_ѣY`csť%X|>$f T]GuJ()|} wݣj!I>m*"cR~R,֝;؎lU*H?ӏVŴceepH@${Gf/Gq}^yӜ?>v;,F<1\|-(}\rJeL6I*T,Fq5]]'m!Jd\O؉T" |x"n\Q|I*n߲- àX(y{r( ׷ T6H !uQ˪"Xo:.dyy0vEod"F( p<? дDC$FTJSl ( S*iImA]l k$S\~IVH$؎ÁDoO/{crb| ^kcLhr)?GEt>/B֊mٹ9j:gΜAuv\ R)fy"RbH\0 n\={cZ 6Ifees5޻xo޷8}̎P$=%J~ f,Ih@yeacWxV ;<3+'7ѸǻO+RxW"ߡ\F4Q*طo>)"ZR,ɤS\6,[$)\HtA" c;D)MK2[lmmb&e122B*!Mse\"|yp}>ZC|*,-=wbuuqFa2/}%ar\HD@UU@RTVVWtLz,+5TEXڂ  ~@S% Rt 8c!>0͝wB$VVFh!fZatt7oQp.!t l"y?ľ4IJmU') B6Msi=ź!hg ~YaJº*MLVs KD#Hh$)1vYX|8  xGfg?#[u:VWm<ߧ 06$ӬFAZCeU%H6RrQVb(e]B._`t|c'pvDdcMWeH$d39GB?SFA#ҷ~ťlWb~n HVfxhl&G19z;wl69yℰ%w/p%Μ9WGQTͭ ^j2(i5riE*Qh[a"Mb88 8)ۦP*8eMUd0tM')t:E;aPȗH&ܺ!f YHu M7M %/İaA(ea vB"Edv,vQȗp}qT2_`c}\6h$d9 #C|G'җgٳgogfMY:RK4[mDrT2HX&C½Rqܾ́i'D@?T*U8xM X\XYu.ԓf}޽ioyi^g}@b#$@pHE-vl]]3131_t驊ꮨnW..Z(D@ľ&rɳχdvMg`D&y}n&&&/"xcVVVt#ccan擏?>4L2,\'_BQɮ{{Ѩ7x_lv n}r+W.k"gѣGSY8(] 2N@/~w?@|jh4$ ][vHU ؓD ˨Pq|kkpum C\~CDt{wn7{>S8EӦn8oY٣k=G,FI:Νۤ)=J<cl|?L&wPDc?B"<~ ,eaiݻK4iVWN-rA{.[^鑈|0-NMR7{LJ1G mp۳- %BT/rcA L+WǶmJ%߃vx{wK ( !8¿o:\x5U $Ѿ IDATG =N{ DC(02:(]E݄ u-~~Gt* i*hX,9WhaC:AT ,:F ]CeAD"'ffhWnuORilom GgH$&U6 EVE\u7`wDx,$iPU]=dӲ?'shHb&FH:?ZCCV\_&i58uop2ƛbdd\6Ofm)L&?_'; ʅ ytK.ի LXYY 3-rч7961td*!>~pczzx<+|+l63Y^׫׮")2'hRdիUϬ#x%NO$PT5rHSt677/d¡3xZG%j xwE_*aX|@҂ <{Ekpç$=s׋%nSU{3 MӞQ>8WdQE,cqiqF (i괹x#,-=fowYs͉ &&D:v?p%L:4׮H&3\pAE KF*}8]d<8o~[?u]_ݝ=dEl>gϞa00P@A$vv7-굪7] {}}K\~ )X4MƎVYVIg۴-:D`[xBl0P>I_fl(CCE\csc=\T\\%|cd[n3sP\.K8Ԑvl/"(ղza IxatpG86\i^L0M zDv=! ||OPb8"kxndǶ\qCԱ){4uTMap@ףiYhm#h6⵸.~([*jVBN wt:q]DATiDϘÐBetCDDΝd{{fN<ǶE ,48Dcq E1}}Ȋ}6שV8iNhqeF|Ri7ni paAtVd>sX4FfxQ,]Zr,rl<$`&V׵CD&EE=ߵCc&?!0=ײ, ]I%S8KZ R {>mG>5^Ϧl⸮w/ @WQ CȒtj #ɭEAH%IZko~ID)08Ph 10X@FF:.1=m.>{u4.= ##чﳴH,-.0<2̑t667Y^_4-‰Y8H|>/dt J$ ^(ŋIbpYZa!$: woL:͇7?\f=1dE,d~"ɍ?|;ߥЏ֭_Q*,1w-ԻEvvv+޹0??niv"N[bK緙ʕ+$1̈!\eBaW g럽_B//a!pIpx|Xqܯ[P~`vP /aK`z%2cJiG 8{ v| 8>=E2EN=GѤ^ 5tq\a{{M{MFu*=h,I .304@^aq1L"-Na"gP4/iZD"104o#|a4 s˴T~SdsΜdeygsQoi6./FK<gnn |@edYs=TY&GL,Ems9}Re;E7hȲL&IE^{?s|TE G(*z>tsgv0bhnנiN%Fu]EUYEdW"p,n m =H$8{,rrFRILhx(l%@ b}$R)[ؖRw?hz+5~r} ɌriΟ9Ii[XV 2 EMM33{VKK-j +;ru ? 3i;._Vj\+yb]ruO-vM60MvM__bqg.R44xX+t[&}Ai5mP4TB ~MNnY__ckk-~w?VryjLIVfc}188p8Z"A|F<zG4UKLMMJ"284Lv?@ j*C\סUcdlS55  ài. }\|1|cow_x4#H1` 5qX,_-noC]Qu,_sw9(C聍mwﰱF^|g؎HEW dUUC4q\J2/2|>b?ih6d2T2LOOjg{GTv\->BxwkPVHRϱI ~!@fgg%:]LFHa 0"gx=K$왴bg`=760C0atSAp!GA#G`P,>Jxz'ի$SI:6a˥˗ۛTj2؍j{ c'"@uaMR(ATUYJ$L_Z_ *xsǓ}?2~0PXD$m# Q j6D2I^cxxj"ܞx$BCt :fNt]U!NǨT*I.="QJΪ#L0zYVܹsb˒3192>=<ɲT+ĵ^3SS99;8;;lBTcWx_0}8}"jƓ'juz=CC 䓏%g}N2GS~367׹7?O*$ aE!i8XeZF父jj6" V! 뇙'"Imޏ@d gIN:ƚ4aniu:9s IQ'q}Z.='?$rt(IٟkfR٧_o813PqIesP5yΝ=dTăvlM׸{.|t CJē E' pmLLN'_B3T# ! P, gQ ގkJ'Qdݽ-k.ќD˲}HfRiѱvwvY&j(E.YQH2W^cTbcs u>A*AQ4IG?!?mn} lf6] kI |g$'N2ss\t{w0>:G<^\4 FX~$R~H=sBئc3]Q4%kp_C7r1$I\A6m͛ȊđQ3ޤgwŢxᆉr) Ig _ HC&!L뙼9s?u<ϣZ'NGu4M0;I@?aL贻x^D*!蚎X;M.ua(Hj*|i4~@$#˒J&G  ^rmD?[c&AEkb;6hsݣlC I:k8DRdr89{RG'$_x0<0| v}vKBnV=c''lf P8m!)K}}Uڭz."Gtn؆uB'fȞ/ ^NX(ykUTl6tF8 jP,g-{x IQI$$)7f`,,V3`"+2chCwW5!h,Le{{@Q,b*/e,N1l.W^wa8DE4j ݿhʽ{wy>3Ǐ Nۻ[Y KOh[]afxT*˵no1>vqnvGS*aXzb)QX]^fzШx.7@\C('OK<}O?Jǔ$cǦxo 2b1J[ [ͩSs"zgYtI˲+|㛯3882M GNljFώu1|! j𛊉a7gˮ"ITx<>XyҙFI\)-Rt*i>&O<\.ӗ#LH&*aY{zVOxɑI*$'HJ:fbq5t4F$L"RGV =Gt;X&H|{'rs߻ ٶ8XE4TD,$#>+k+":رCHF.cyeY;篽@&a}mMp)x"N6%ߗmOI$ɹӼwtFeb^sGyv[(R@'=O |_$*:CLy1e=B\9D z%.!zTciJb(HrÈHDiĢ9\zMSh6"*bVKe}h:R$^$ j,U ; %8baNt \O#H m1L¡ж]V0VZFO& @U2xDQ8I,%S԰,Bd2IT"R,=p=/էضE*$X\\s]sc#4 /yJFL&Eh8C*" Nj#R/@z4*HxxD1 TMX{=e #/ļxUUe!Qu]sÿo?jp<M0,ꅙ'Q&/\IJR VC꬯z,_9hzܸ gΞ7?o~ܽGP]F U<%>vwKD8}V6y`uuWgnn~X">;wij,ˢnJ$Ģᣇ<|#!I4::c IDATWG-vLH4烪F' ,*XͭM$}} i鄩O"FYvvvs=m;i,S.J%XZZ޽;[u|{Tu<;{ry91u/xᅫ z&ϳ#GhwZGIU,"9wUa^yU66֩/:JaЎ$Xa(ĢQq침*R@$n yi@|?f-lg[6=A K2#rr B. hr=\ΜJ^Ϣj 300j㎬x|4mIb<{eE-Ԋ u] ~Kp0YXX͛D"u-R$(B&jRzš!<|as"Vtv:9_ddlFE:̙sllVܝTk5ҥKQ=8Bl s_γ_Gl/ӳ d 4MGbFd9FY][Ƕ,9B.|ѾַmlۡVS֐Fj'D:$,?]fmmǵ G<]yB8D#ž ~~".'4]WEZxE5ĢQ7s@p]1k;!s % =dI&G ESiXE2Cl5D-F׵p=N`m} EUQ"i }@kaa)6/<>R؎'4[ml[Ȓ,@T]虸{8#!Ń`ߥh+**CAs\ C''&X@%bQnR<$O */^7c}cFJM,Niwi6jK[ؽ#ˣ fd,/☧ɽ{wՀwySNpmvwFT^r$ćNLG@7xrHi}M2PTcc/<&C* E͈b#%HPV`y <ϟgcFGG Dc1tME#(M?^C4ʕ:=jΔIi[oL&?•R !2㲵")y6tMM1<Kth'CPlnm` n\{:7M2Y_[g|(cqbj9>FٳDm hro~qdRiҩ V^^NZ-ɑ)Or1L?Hg2DqvKܽwO}-~-} s)>k I*gb`HAS5^~#d+|-*=`8DӟqdKT]h˯P,^~?~ת a&nDZDzؖ%^=,GP& l_\.W-zFOp7|.= 3G<7:o(<ñ$!9Q l&>gOu^TEVp6md|?^>GLb[.J~__Ųl.??nu8 *dj:&'ת o~"Z ԩ9>}wǣ6qE\:X^O$$+4[-As'OjC([dY:NWT9c sGIbLdw~ ~204/Kxphzq8} ߾60\ST8!t ,.=I4nA6k\zwҕ+9weHܝgmmW^yUbGNM8LXdcc~X4J3-85w6n\)olvE*"%<;^geef^T'Or <}L&\.si$ VVhiw.Ca1>~!m\81=C\AB0m#1sb'OEdYX]YszA*ĶmK{c[u>tqJgh׏at\VN"O>'O+ߠRGQd_MQ<;+Yʼn,8xOx#@J< i2f]=AK](,%P`9NbUձ/(HEQD&zgX3z@(,y!zYVO±6sq]FWBN~yiFZj>޽۔J[Ԫ%2Aϴ{qX,ȪqyeNby)33';HOI$bTF-n}1FIU]n@H,k+r9n}.]C>z  .ll۱t!|w eaXdf8dPHȚ"HP[= EFcә8I3f)jB9 L"@$[ޮ{D"I46tZ8E|XXި KG4A;>dt S3ܸ/>kki\zNO~#ڭyΜ=ǽ{HS,/?^aL:6Oalt'OV:.Npzh XgzjfE$`j'"ܾsյFF8wv始dsYP&NkfZFTe"k+.1u!+*.]ܸ*gΞ!ɐpj(h0.cE++e@@W=x4,.>_>&O1y|ACbqQbb1jjc&e|lёQJ{%7Je^u0PΞ@&CQ4 Π(* #>wɤQss> I¿@8)zV~wuM=xȆ_X~%}Q92(;;!_(nv3 C>*" ܸ v˴Pq&&&8|0kN&ckDR t]3W177${{0իTe}"kk 0`Z> K ,/g{k>>#g$ tU#|p*?s]h6aa{9x0x>l>N>:X 082")nU'ƣݵ t߇@E+n41\ocwJEJW߿lۢZb.=~!ޥ鐈ř!Ls-&/^`}mt*K"s%i6$" :!R>jr9߮z.%n⺂6D#nP.ha3 %(ȊL&5$pnӓ' =16ARjU* iHvYRiS,g16=y)-YfA ګ ") 懘nD3B6s|dhh tu ,'&)rYvEAQ$ږKt:1"7&*c"-"A|&쭮019A*E"gϞG<=yyܙ %9L*dYߏZ&kKﳽS˯|KGUe~Nb l^9&z͚j/~j*C&Oa@8xMH@2kv uIc4H&(KmQͻŐFxx2&ZMQU!s]EQI$iZfDHD2N?8IZahhF`w!S42#lmS-W>mI2'O裏u$)*ϦЍHDύf F$qD<$"qbH-mpw')Di^4?#Ë^@NfGAibzNq9w._l5{4UFGGway }qaFFFI&QP,rN&ܔ]B I[js w] @l1:<>w%~>}n_1%テ*[ӧ'b4u< ܝ'rQj:}LLNp1z{{X[_uz{{ `/瞧\)wfyeROp)zz{,-g4<{p :/Ȇ>kGM6iKh1{ d"!:Eq#eȲD&kupQTEGSѲMRgm٨DO.>J`NjN) {i:9{l6 }6Y[]fc}ba˴!EQGIUUE"@Td$}C8ر& fWtdEك#a" -ȊDZ}ݨH\(F"$Hqa8FbѸL۝6=}lofoySܽH08r qQ߸;s=w۷ns ;AB( -$ET4ѓKt:-d2i:8s gϞ٢H D>XY]p} Ш7@-Yc`p!&''di!Bd"x|>[oŝik_S1]e.s8t?p}gGg饯u4 vq&ǎ(L٠ZbYSSS(BOOIFFF0 cݓJ \vӴ_`:]ׂ"{|pCjsH .¯w~S6o-ܿo&W F UTM\.gΜ7ߠިaZ]f$} CCӛG744YEUN'\FU6670 sJ;d2FxwL6gssGћSVXh{N6J,DEވyz;qfKT6p+x.L̈'u#A_,f>fW"FLc``4tۨmtڝHQJ0<22/Ծ/,[;yvub1677#lT dLQ<-@21 6dR( \޷m5-) !r͊x{IM v04E xrmy'I nwn; c{i1#kH"T(<O P9z(}AV2MtUcblRaDZQ\.GPv矣RhINQD>N[ҧ|5=L.R"e&&x_ܿwr̾U]*NY ) j:&C\1 OL ޷_{g/p+;U2F'N |O<ˏsIVesRiy_RUXZ'㫀KG&(H*B㺨c{0b ~r[_:O> ?gbb?/>B6akk^[7o>zfɴ 輟 p'`nO17;/}ዜ8q|gϞc}}WkTj58t>BH'7:J20_YG4MN>.Z۱K7}QW#=nS%ѽ@Y^]gZU #Naznt!ocno6G_ի4 Z,..`Y]Z62ϝDz\{}8|bfzZTu##LOOESM ? ml6M4U*}I}t*KtYRO&fkcqчrEd=ݣz]uC>SGlp/K{;s][{<l!͐Ii5ꄁGHF\X6p!ʥNEt"@P5qQV0]BFUTdI4mTE's-̮IZC% DB̿TEk!v ^VVӨWK%lH]ZfQz4UyE]P鳤 łh٫ZXkx O((#L1z8($K'E=+a4}|nK<Ͻzz<I0T}c;]FFӿk66T:ybksnj.lQo4QTVMÇ20iP5a%m!]{m D$<׉p!T Uc1z-4@S5,ˢ \]U(ɱI]Ƿ;սlF..rL0xēÓÇ8v|յ=fhd}8qM|5fJyfJՠT)xsK[,߿cuPI~HdeMX]۵ | 3][ C,M"[ܼq5:6?ګ_|J*1#99b.`h#H-EE`ʨFLOpR8w=UWV#PTyoihswQ~{bɯ[n\ ٝ: 0 V,+޹K/D#:'8x0ln"ISyGxTȒHb`br?=== sss+_ d9r^~1?HutCLjkUm4$uR |˲i8hM=%XɓXbrbsU+HA($SկNڋӲPu 1BVut/=S~F\d{{KUX^]噧arr?׮]ƭܚSܤT,k*Oo:<#YN?N_lIkYv>#>Dkr]j G[ QB >#&7?$8~8\>U#׋i\|1cAdN?yFX__l@ mΜ;:nB:ȗ7~ٹyq=YRh1>2ի(˲QlooRUP5N1Euj&F,Xë7nda!`?/+?t:MPdll|ONÉ'bR)?c>Ҳ,Cd Nss(y DL.q:b: 1 *iE(RvOUO,LP~޴ JBfS#€CG +$I Gȡ4]d{F,AV[/Yq|>#$ ׶7$VBUZ(UvpE@Qt1۶Y |O?ݎI.ri&TBpqnAӤjh4t ~ַ6Qt H Snsgf 64۷n`]!\zz9~߇y䩧eZ!~Y駞橧"ȑ<("_a4ﺤSbuiT:9$IBĨRUsjjRĽخ%0d 9I= \|v!zA ߶Kqſ\yE3XX,27w>W~@^sE"`ZC7t~_nݾAYLjqAoo=68r0J;33=zE8QT/~ޙGyK?qLYY(D xDCVlnܼEӢ^+.v*dil:zmoT!k_8r4O=,g_`uc{K˄~H2bg'Ex :T_=K}qi{Vklo*3\bl|'rQd{c & UMxdddTYaiy#O9ds;?bt ?7)$MixLB m$W}_*F73K%v E9t ndjkkXv0Рap+lnlS.wo ssF9jG!Bfg LP,~"x+_׍H$bb:LmZI .\d~~~&a(~al] d4Ï>dn~FA _(B8<,6ۛ[ [o12x!q!]@cq# q9F?4AS,P.yifHS/2>1۷rI.>TaN:$l˲+WdrrD"Io_/VKX$aB!?2@sbx.^ qxx#j|Rr̅bHHv >|D"Fv^2VafY&Е)Il4|4))N*kkbq^y S&J^bxYNmxx%ZmR^1>"311A$LR,( 8'OgccF2Vd{?HguD\\md2)Dza@,/RȤSޙh}Tez) (<@,f Y=*gt(XQݏ>eD3Y?"ة"K HR}7r{鉒$4E HSUۤdUQf0JȘLi:pxDZ3SӤRq ;[8O`&J1JHhi6H Pȏ=8'  5,Ȓ8cq&'S(l#I ClBWUH( a$So[V#lq$K ~ T:y{M9y;[ض2BuW9}NێpvS07$@74,绸~,+Nq UEPeIxD蚎B:[$Izby\7@;>R*Wᅗ^`(3tۭ+{>B(!fs4EwEq)Νf@cryI.i**S{ܻ|/+k;[-\W M]IY=] |'H#{7TU$AcZ6fiIJoOqq L2::JTbtl~w\h6Q4wѤMt")>+ףjc$X+<YSh4tt{P{ced9"FG㤒 Cgaq+WSgvj t#>/`bl˴DLH__tvEQg-V-R4O=43Sy79y$_;{.wy{(a]'$l"L@ ;<#N.\#fNgu&f˱Dũ*Q#i!aKQ' tME:ic[H2|/gϝg`h˶(W qlղDªT2$s VF˲(lmvT5$ncO.mlM3Zvqhw]tUEUd$Y޼H4/D"P{n0V [c<@t\F /8R B]]Ӑeđ$L&S<8`? ۛ457pP.|>GbrrF.{ vlR,|zN"-RUu|GcmcB@*DD^pER(vX[[l?Џeuq=7B0 E ʽD4Edp\p=q<tKQc͡Cq,Z&jIuExo/ )YQQ $BYS"#K`!)i:fhd2"^o(0PI5 %WdeddnׄP_SbhhRiw9u5$nO( mf00D:EU|7DU ~W3SY]Y^ hV,FmrET=C[؎&qoyw{XH EDJdʒŋ;ĉLɜ:3Su͟0gTԙS-LǑ٢J⾀@z~|x^$ےetHnp!`a~6>L4 *#/3<4 [7n2}Ɵٟ]e^jqiH`tt[7o/~qG!a;ӲXcnO2 ` dyi_dp`޽… 9]q\Ny=:Ga=#ēS2xOp)7޸?}۴-tC ,IPVӇnMSl ۶| Q)dy˔1Ml(d&)R9ImNz.[ӡ<:z޾DPR@XլSThđ&YE.o(YI[Rnx(S#?M\WZӶAa'BN^CIG}r6 N&R,)CٕHbWO*_ Ie°;.&V]S14#g]s$j{{ WB"5ImDM:Dӥs构 dUI9DB]ҔF}dДt#Nds2;ES( q*}퓓uk%^~9uoqJ ]䯾mbee۷rg#eС %"ڭ6kvbeOۣT,PɇޢP,(w/ÝY]Bf<;5y-nܸNF QoHA2ŋo޺3g8v([(|vy/Eb~Gi>~*>3v P$'!+(D5i_'][,V.%^ys-[|S]667y3/+׮pi 115M4At.< Becc.]dqy9@{W.3snW 4QSBlE26IS,Ӧt D@.P(h NgJhE?=qmgVGU$bm}J#/L4Km8IHt(Y}v*sv6V@D$xK/a(i]W5Y5$J5kk!EC G NbLݣi{04\#_X Q!;8 B9{$d Bu CU٘ľ! !0%nG`ۿN-.YWD1!u JUQY]wN`2wpᇙ>8E;:3Gf)=t.i ir9{)}I<ҋGc|>UNaY*B)1 n`X6 i\T[woS*s!Ve1-/b:y yEmb>**iq`Ea2H*"$$,j #Etl{Yy>_iw[ߟCS\b(&L$.N]_ <+-ik;G? <LO3{w1N<ɇNalI }3<: ByswgAz^>=$_|Ӈ'x?HVg~M^z%:BDŽ 0o\UHqn!K!|y-!QJB ˤh|xr?D76}wX G}6{/z_>7tY!ި09|0\zA.\x*'? \&'`aa'.\SzFI֠u8ifg1?FA{w Cy^Y]crbE4]&N(8 %;8P4LaRFaRi:DaI/"(AQT#ܾ\rZWJae:}gΜFS5.Q,ѽyX]_~MA )a{,r5l<ף벓9)0gk{[Hm3*(A(uT뺆HXy2[!@c!-۶en]YhꬮlPڑ$Dab[6e޽{R(%34mCyp9tF;wgTxy VòM_._.;osA(`Ep FFFv<cE`c}A"OW d)yLNN‹/pyCe2d0SজC=vDF-gB]1#?X ֛osT!|sܻwEUi05="qX^}}244HQ/ IDATk_:hٳť%)㾷?N"(@Ԍ{@iJ1iH / 0? 4, XbD27!?N:q, ҄ I4JIRI4-0 kFW9 vvYZ^dmmz}0+!&[[αcJ2b;w y]»";DO p]IFKc{{Ce'/M14 W]Ν]ӥ[s6aAT2 Y*IcJ-1\0j1T<ē+t:m.^ rKˋ)FW*,%?|nɉcGIn%;qB%S5p\޾Det(GFVumaܾB+hk*eɛoTkO8ܿ?bQ"NcVVW̕+u|i\|0X_[ܹs<4PKˋ\~ 62밸HD/!pͭ۷8ȣ,c23s+ "E!_̙G:u*>| s.W=i/si}ǥ7&}P8?+pyd>zIӴ0MUU2s +С`&[lln1P.^aALdRaeew} 㩧"362ʵ׉O}ʥ2 &Ic*Cv[۠.cږ` ]$w qaQvv],;ua>ۓd8W*}DzsssyF(Cxt1vofDVMdLf?EUu=cLMMq4t:MjT ͒$ΞArDzm򶍮j\'#(r$Dn) r:lmWfM3$\6yy͡3gs[z2 3&_uô@HnѢ*eTAU4m:]y6 NZ+6ݽgI }4MC0}RU@W]n2Q.GtK6e b$<+8yVqDyL٤j!8}C6WBAB'!V~t;m.:mY؏ˆe*r :e" t)N"Oa"T I:.i1#lo?>=iB7o RPhs9W@Oi (duCCJE6664z׮^W_elLr:fol0pMe`p4U%(zhI4ȉ'ə)*g<8MS~m&&&eO"Cl?ۜSOC?]WD?(J.F&{q^}r2#Xl6-'Rƶ-zMޛiVnDž 8vkI©GrS)Wo\i!n\Cn)X,l60˲y R(9v+(4C$*Bq!R s{st:4EbKIxLѢYmZ+gv Iw^zfwز+ݨU!!3iSCdmc۶n!kZy" @jhYeۦfOEQT86 MUX(TV Ex4vRC3@4PYbfL ELݠQg?T"cdl۶贺A8AQ[D-/irO=uO><:esj5?8/":\3NHgMp(ptQF\aHh)eh6@$J1YDtnM_"! cgn E,B=Di*Ru/;qU7y7n\gQm7V1 \_59v5V wMZIkas'8q4?2_ԁgs؄f닜R ctM#S4Z-JI"!:asKlQd. !;RGE2zAoTM 4AUtLGϲ٩4s0" cL+ϿH$9|U5ySIQ*P"*BQ 3 )|}yݙ9*TkĉK8nVؖAh5؆Nls deb,8FdFEJǏj)C) il4iԛ,/-Bζ=(سw/z MIHQː&jY^^dyieCGYZ\4BSu{ٳg쟜bdd~C~xյu~I àRu<|&DA@FxϽ{~f'~^ aQ*g2,aȽ{s80226saQ7ҏc"D\ώ<>*J4e|KJ*$jsM "KK F1~y׫\'9IN:nbiy RU%N || M>ү4[u/`d*B3<թH67:t3No`[Q$"z+'_,2ƑT#wXH !>!I*+Z-  d9z=ll180ѣGYXXfã~laq:BSiESTiTT(%SD0 ]Xnf{!IR 1TfEii"CPGw%MQʳϽ'/8h yM~h R ˔8uaHS8rKhٶMD8KL? s9Fe8AHYe4z@עTΓ˙nzB zq(GFE,s]:.nc10P"#ŏBȲ(A2Ϣ\ȑt:Z6a2<24B^ S$Qө@ SZS$>|z.q,[g ]~8NY0+[M a 'fz0a&7oc)ΜyT .0qkqz=TJ%.<,-07u~_ifʕ+D,"n f:]q:c@1JlmJex'זnsQl[= 0::B>W$f|^B0׶,Nt~FérPN8K!GT+P]ؗvq{=HQyi;D!rEնXqkT,3,;GI(iP5IuATajb{wX[૿k-j:V"_,Su]FG:GXch)S.pbiwo^X*D1{'&lh5 1?$QrUj;lmmDy`C٪n5;  ~}ijcr?zL:ĕkWy&W4zQ/{/{G'!T q366a0>>NTĉZmx 9SJ{ٷomT$!N^ <'ywbeuϿ+E?!;Cgss)t:af9N$ٱK [T+ʥ"ڎ̹uM4&t]4M"jIN*o/3pבE1 K%C/iʾ}i:]Ifir'RK CIlPS!N4$/ϾhQw;O⋟ꕫloo10[7yݷ7jIm rul;K;Ɏ'cGFG( i:j$q{DgRᯨdk% B,SZ,CAi8 ~l|^(`R0@3t\ץvq."Y8"vj;ضx¥wfAGLLN`:v0vTdT5UN:CbccFA`M2 a':AT楗?c3lomaZ'QuXY]f\X,ϱ7uwIO|rg7OX28k?~|ikux-N<W\% BKI,~F]e7\kivRŏ~m@ߵt>8sLV< J{dd^z EQAS5>k{_zog>'H<|/|q/x> #J1)78;㐳,KQ#Nc4{ÅX 줕dq,rM(I©ia"[*))208@Ք[n1 +kJno(#9ONqUEπ=R-QQH &aʭ$wtBL2wnͲgoS(mJrηjuCAE!=s;DSƩiQK[ivuy7t]ΗI+9J4T4IPn: !rSKhA$;eFIk8##92P5MC\AA JbQ.ek6- E EY@ U ŰBUl'O8=Zf?_/)9=*lUs[SOrU "Oq R,(rlj'Fɴ<148)%st"5 ]4 n8Yd]4bAd[{Mg/u:qu'> ,4N<' c|/^$lS!*R٦0( f\'hY"b&)N٦f T0sU"Ty^-":Nݐ_`ri&N MU lJ Pe(Y/ Bl4nV| 8[5:6>Jζi8`6ÐYSe@y'$iv4IڦjҬ7X_O|E[6qm5Xn37w-ܽ;|EQ2v0(ܟ\* I±G`gK:\_l/+%Id?@Fs "I$,٦z648HXdhhar`jg14Xa{k]7}&VV;.1{gqسggϑ/}6nrQSUdJ,"lQ;Kl DKLLLpe4]X,H ^CgQnp,膊t$[oA`{*OI,/>2;;&i084DqT`7WxA\dkؙp-ʢ-y F &7 EddXt1e|0( h3x/ 5Ej2*_U˞Ώ*MAɈ(;ov^{d8 +++#hߗӶuiФ0L9r"d"ho؊|.m[9PبJd@K),޿/(80G!oʃe.]z7_1P(|\.GK$q"u@LMVodL9q,@a sB?IL}gϽ T$v HQKfZj[;v=nOÞQn$@+<AZ( Uy9}}˴O{ 8C& τg%ϥ\) / 2Me r0q\<&sKdV ~1v?mGRLLN#3sy b*2 )y.Px99cKqBQ#NRBݻ+笡H$M)uRβBapMx(Ee;О>s}s m(s1MT^pE~=n~o\G!?`nnYez͔a9 + |t4뚆?I??5I d+.;1jv%pǸ{3Ӵ;;W5~ӴZDqO<ӧgii}_~1oc*Eݥ\|C[gif&OSIԌ\Y>&"r-E4 0Yaʋ/KK 2ԫ,McyͷL{lz.JVK!;=#4ٽ6BʳLfh T]}ȑq9KͿwer= $ c.\[owzIj\GM\̴l668878@}i:'~/G:9ѐ<~V#N@§}>ɳAad?6[ eI[H30𐧟~͍ ]ƟɟēWpQ _5^ ~6mpxx]( ~( v[C=:0dEX0J_s]WHq0 \ץ913O7okf0P˗K? 2캲q[,d155I7Rnfn4<# "1A*#A¸'ߧ@155CIo~;~_忢i-wau;wJe``0:%}^Ks#z$S myckQq =BOk=܏-BSn@#DJCҸ 72->^_Hckku0 ұѲ,I"aR(Y]8jc̛<'Ju(Jib*t;Ʊl %錞B9.A`4UAH5Y6/ (D Ӥ99Iӕd[ PcӢx,͋8:]L݈YEEb-B$! %RXX[9ww+I%B$Ѿb 6`-G=€8 um纸}ZƔe;(eP5;]\yg>F? 0ETs<.~O} \O<$gΞҥZ#JB>y;Z(F8qiyJjA!^W8̙3}O#_S9>8bc3moo5~ߘNS$q*?%|DZ  gd4˩{]vؾwDz(KcطsaOx!·L Cޗ Ѯ8hNL3ϰqZI~==/^9QbԱ@( H 긎Ge319>NipcL60M~O,Q e`IJlL_XQ('r(d P!H42Ti``4K屣:G0yM$re!1L,I%R nY09(%lN2 Edi͛ظ#b¹9HR^ u988q=8Ĵ-ՊČGG-IPQ^/hexӏafzQ o?13=c\q^CsƝ[B2%D#wy㵗ZW#$MtGuMthT* .PV}-d'xK #i8& %!Na#ȴ(-FǢk!Y L$$B1^*=Yiƙg8ۧ?io!?AATbww|xr5$a bg+ڝh#fsr<3'?RvEV <iW$ƳaS$qҌS%޻mc&$YJeqqNaYJ%!8`g>11Aȥ[% ϥ/tX^܏2 ۲>qPg+/T˼dY. IDATƫA:Y*+gm ׇ 'a${p{(yQ}\![[[u}YRt]`aak ~wwyǵ>Rh4?y֘x!a`[&%d%[+a;;PJg۶SPH! U^%Et{qYwNA`ێR&.jPxA0\)333M\Sz`~KNIH)~F(X`+^r0*EZ´\L0\ֽ2_@$zim6Ih >PrOL8a0?= 8갺%n߾IQ<ΰK;?*{$Y43K33,//rtt@^dt{] XE: u{q>Ҙ$pm"V"qL3Q$C?2@,Пc wa~SyYYX\bzEgOG0|{_c \~S'h4\O=]aD}GbP3o퓈Aޟ,a(ǧ#)G/$u.](f^{p:ƍܾ}wyFAPVEFe tc?@_U8a*'NrM$= Ca;.jwľ4Sm#c;qB$ 1I%@)}:p ґDRa۲T m$dĩ)N<%?RZBF7ϡ n $c˜9}|!zi==&t,[o%\Iӓh4jWp.e6 z'8Nt@HirIRTѠy@F2XV_dl5jhA8DN"I2 $c,"cʥ;{B C155)IqP2e(X2|Qyd-7]YFfLP2Wm13=-aHؖq8&iZ Rc$bű)lKPAQDwvY| &'0@˘ e]a`rEA\Ŵl׭rE40L̰z|esg_- A~[\I<\{ܺ}%>b@  = ]yGgL<@|8rlG t<2VWI_ 9<GGEN0,3ۭs$iF޸:T4M$1q^K @ #AV[YV:P5d|jt:"~ QAȵ=2Uqdzj^/s,R*7ymے&Ep _(!\ C `!~haZ6/ę&CiJ"O ۳uБ< l !.C^hw:7 CESHsDiyq"y!FdƑ7#RGic6QOű6* Y\.SQ(ƢqZ_&bzdYA'J%Ν>CF!vnQիW"iD*M(J$T. bi2 p=Wr^JFťʕ2K"Hﳲrv{ =By\&''%M+rrx%1d}M,D4N@"JɘN7^]*]w",ĹrDzqKe .^֦s J%66ywVw#Jǵhu`a~@'ﳵq /R}y!E:8IӶxbnn/=6 # Z9[[4mq{{{2B4᳔} |z?"%,I VT8~W9:83w%ˡ/.a_a*nǗu@sj^u {Y&O~k@1-7wA7ه5g꟎ptar9,eckW>If1$Ξ αE4'&Jbo92u7qO^P*Uam333ᐻ]qjns6_P#6YZ\u]ZJ)Ξ=6m9F20jls=u]Gfc6PJrBzx$ B]k:I Pd9EZ`h")4wmw,MA2$(R!)Eʊd1KHTee6J˴lYE1^h?(ˤT.e!MMFacNBgcUAF\xR;i1˖rńam;`JS[ՈɛHI]&h*N@u|yxq]T%NSfgr,M0,ӱ%9}"˱ مp<!c\ۂB%L0 I8ɒ ôI4yAi"?=HV-!L#/"ǴFK]y^pY:]GiHQR)1;?CR+{'N`Z;{Rt̑ťcGbkyčӒdԴȨ7j=w`Sg(%Eo۵HP6Q婶[!gϞC'Nvy0HzCYX:C2K+\x,Y^^33G?G}Q2wmɥpL 2)\Q Ic1* 1ϱMNXE)AC^lmmnw޽{m! C:rEq4t{(( -dsկqc/,oP\xgsS<{9INw=^} ܸq"ce9SSSItmܹl& #z#Oy7?>y86j'IR:d$:]LlffӐLBOc;TkU0qj,{Tje,$ d 9N!+2_t]Zml! #Ν;ǵk)jǑH?(i0~<8:<'|+dOVz.S"b ď|v]a0?,wrtxHĤY땘]²mu&ȩU*;!$+h67#Z. _[՟3_11cO\f~q{iٳOY\>ljooq|eeV`~anKeJ P ÈBXI EZ& }ʱ,qg~~n1bJz.llF']<Ϧ9Y#I$rlk_ir~ONyoUjL'U DGOc&M&MNz' V{k >׿,vLԧ&gpErE 0B> ~F==7 ,?Yy~PAOyGZ}}EЋUPCnߺgewojF\>h '=Kh:<1&ea~ϝMffx곬ʉH"5˹(`2O☛7o |#ϟ?x]vKáI/^@;;KeT!E|- !AcbW)x2 P\ni6>iI>=oA(Cq)A0#DJQ*Ww6nJn7m ;tZm/2fuw$J嫯 i?<(?Ӝjop3 }g˿oR^$)s˕ uji133K{ba[$D ޹rWUoZ^IqI*QZ^hrbf8FFK'}`wgW.R e\eLY]AGn2H*D)`δɓm8%IG% &IBi2D1f?[E#21K* կ3B,Aђ\VWJ\amuJiy=Fs 4DHiiF4*4 IDAT$Wnr)rx1R~wXO[ {+%z y0^{{wqX}{-4e9AeiG!kz.A$H@(!YA%QHG$➉%#$L^Bt2"o(qxi677%%0c wT+5~ב(QPit|fcibe Pa vP> ;~u**i!#a~aq>~0n3xŸ Fx^ËF7n,ggg4DA}Ioo ,W^u=9}gss]&߾Rdz6'xz` MJp)?Ʊ,Vɸ@ߢ0`qqGDaLdqqP̋ )LDyt; mWIѐ@áOR/i"ǡXVV!\jYغ u8Mˢ\@H3%GGJ"%(T*'ږC ұqzܑ幌CL{쐘ɸ)W|9 3VW׈D=eڒ :< 03=aKg˺o1 @N$)ach3ٹy._B64i>G# ~_W 0I=Z2Ҟ :EuU)? (]%ϥIy ^IB ɤ1̋4EX$QS'9}4wlN}Y'#d/$4'AB&jCj?ؖ-iz=9AKaq!~Ag iFKܛFuw6@;)mK$Y8tYgNY|3ӟ̙/^f'=$t?% e#hx@?g;Sq&V CY|4}> s }r/_7o&S|~YXQ Er9Ο{*F hnqS 1ry&&rgn( )W=4 asǤXV-aMDr^tLSqv*Ǐ`팏nbdd#;wLu`$fեei: 8Oc;Q062ž{cxxeSdaq4I?Ts}n޸@@GO?en߼!Z+ pk6mN~~={2g6oP6ż[Ϲt2Ffswg)6Cz3OO)cAqD|\½?iǹ=h`10 IJ7{1}媄D1s R\Hcq395IR\NSI CÌ"iQ Qall??oi"X FF"[__'Ib$-_훠$4ʲMHK!HVK4KFi"]TP,DA͑X r .ކi@2[Uo̐fYN>_ :yPr_v>fŸa}R6xG'`ZbJ2F|ߧQr֭x4:N1-$Ne 1R,˦X(j3 W⻶- C)Zk<%X4u8//9{ z q) [8:q}zs Q̙3gVƍp=| 333ܼyi u>ӦXpQYXQ'xZэ#WW |.66m4NL(L0w}lˡjR^^a#,/rY~Ypl=E*fgoH;qZƥ.pUlr|X__vl0ku9s{21C362F>W cgg;W"!S\[ÓO>mے0@dRgUIJ FD!v41G=+W8C,Ʉ&vRij=KI4yfq הv-L8^)6I2^r9<7G$#lab9ʪdhheP$1βkf{xX*$LrY\^bbfZ&vȑ(4wOǵkӬ9z 7nȜRntڐA*6mƮ]Ӊٺu'Of8 h5؎EV VaJ F8tL ;AȨ>O=, /Mo~=/勤iP DAǷ_`anVE-,RVٹcߧlrQvMN;8{|[ogi߿jγ=n{=v˾yv^"]SSK%o |W7bOq:{fdoPׁ%bh` F x ~O=̩ӧߣZ]gnvӗ}յUvŻKFElJ.ݡN@++h Ȃ&iQֆv 4MeQ]R0v%b5"I(T,J*i` &Ya*mb7n\̮e4PvY$wYv&,6E 9\#*C[c[0(# A#f&c:eiaRUH5T.^#uIˢ "IF]M:=cp]q\Ov(rylۦ\.n8zHf[BT^$oۖL 7UX8 are2RDwTe6?fp`J*8… :!_mw@rH(U>C2V:: QXr"~:*)[C"Blϑ0-f`,33Jٲe3(Z/jV&E|Gѱ1X]]aݼ(Nw\+ګDQ+W/{cp0F 81kOyL˥DݿCp{v1)KJ˗G0GسgT ;oCt-j*ghx3?ڽ=D\"=f@j(l ~Zg&CϱVpAVjT*e^ye髗h8l0m[gzG?w^~Z™3os vl߁r&HTǴ_+-Ƈ -~7EO#HŁ]Ov ާݝ"wI]|7u@RoA_iGr%Gd~歛 >)ZQY]YT,hR0$ B6o(fn2;߉# SY 4H![4B)q"oim;qB__?K9uL"IoQ7-[")(F2hwZtٸc}LбhSʅ=# *%ymQo4;n dI%qa)lZU&+4b6c2ؽtYiJPO3X>eЉb;잆/fK.IcYxKOeRtG$vy=k9]ϑ!g( o02BN`zleNG,$chI9R DD]y>ateV30- LJҔjF}*J/0!7N:MYYYEh12' D$R4, #tl4hضM) z#! P VMh0 ۱|WΗ(%d"  lɘ )[2u2Pl|cfQ{R>j74>F^AAFS$=>r;}gfue[n?0k}bS'OR59INcee%nݼʐ"XǞ blFuvZۘ]2QfFy,330d%<{384B>ϒe;\:ռ6m,ݦi ܚa~npjyWw>Ӝ~Q3_\eǎܸ~{0s2N|yyrAvlΕ˗89n޺A`h0(/qu^} ##:tuy3[M\a|&J>@Q^Yq{$8Qg޿ynhQڨJ!w񋀏B4 z{o?W{orϱD>pqqLiifOy׫|ſK<"kw]<8;4;NUV[xew/$>љ#@8]iZ8 ?֢r&TXqp=\ Fy+ $qLlhgP(v{f[otvqC{YK^0,x+ig4EtĴD;ibX2CViđ\tDnw7{,8!M _JZΘiFLҔTi y$zѻdEJخ-nV'IF$4mVv.faK2T8吂eJCI(ۀjLa>#nRHҊE.'Ţw-( 13/f!`QjMK8J@N2Ueމ]UgK!iLڅrTr\v̻ LlۢlGiBi05RQ$s0$JBrZ ڶei"Mex^HI$,8Zd$ }҆Q*z]:u3;Q LRcCe\gtW |EcY-6l c*kXI1VYTfV&^" &Mss;##l۾YMu}~my6mȅ}n߾(r`e(utqǏ:噛S32:MüA.P p ?7_?II 6YҴ|G)Jܸ}gR)j4/_RZ;wnqy DY n~ $)Qbˮ[*7m\'SlۥX(P]'bY۴. ]2;(v I*hfCA*aVb}4v4b6ev}uzOC'!F[ 2`:)i:K QaD'wS s9;d6ꮭO4I!T=GV$e( {u=(7eL,B%~r9&z ]ћi  Sk"YX8i((<ۑ'qc8 5ISdA5)'6uJDlt75iR=XVQTlLt0Yxv tR+ I5JgΆv]82̞ȵͺ!evKe'1#eF m$-ՉR2Tˑ4Y'+A*KnH jZY\RDks>}QΤb}MA!yV$s2M$Dz?۶ h6[8h#WPJD:i7IJGDIL˗RL;w_%7mb=Lهilĥ>`yy56395_evvE SDTbQz$J,1}$_/tj]{y̳+`u0ؿ iK|~"PP^]a|tL(.^ȕ+ͳ^)9/O1_";q7grnSu2ފ̊rJAD\[BE~4ΊY,KbU @*C(Cr_%yt.Ő7ZI`O.e"Qw IDATpO5/1)hE٦h:.QI&B* t7Dp\'t,)*$0+zu4 :2}A!7jzׯaؙU'ҹ)4, tIBOkW6VA81_u۵i`V4%镒b Ap<0 z2i9"Zjv) 0e$Q" p"2XX^\&D %tiP(V0=Jaa(1ؑ:xݻv<lٲE:UIVAsV@'vًeDf# pUʕu*oే8v y-Z##CVXXg۶|pY}i\`uivoorAE $J"m#>Ω=<㌏ofn=tPj<0Wlnw}ؽw/W:\sO}^xϡ E_Ţfhtd غu3[nj ܿ8]'i,~\' -D}\G? Mb]K,-,pY◾`~v5){;~*m277G sxQ2őADvLBu-x<ϧjVi;Mu= "2c;2Mf\tHD<Ʊsl=iFEȴLԥ);wB=(0mM)lVjUBwgIM'ha 6$b! f .4%abX$d!B%|e\$Q&f,b3d)Jp̦ :}sPY_e, i.|b Pȣ^LO_! # -">0鴄>h[VCeV2mTvqdbLS"bAIbBb@V!Mc֫I3Lj՚\V2Q'ƶ]~UZ}l.HHM!oږ="A<#{/(%n0Ç)WH36@Mt*F*mh%  aˢHu1,"ز2[Lϣ 8O'bJPt0::ml!Qg`Q>)?#$ȱcl/e?8[n! ; oqah|HUs㘿tYE)o?[`5%=IO;}R=;G7NiAry>9^ߧYow^nݸAps o nܼ纬U֘_X`}B$>tZeTkN5idd(m"2cti8++2$k]m,$)iR2z8J0ۆ2Hݤ'$EBer.`\ߗ$EH mbi;ReEBwa|hZeR $4m[ CRI+_e0{E'=eQ)ӲH`Yx $#Ě|Gx3؎Gƍ EjքA+~4ޢWebIq9h*LedTibz9_ԻǞ}X][EwQZg><۷\>jyUZ{ Z ͛7S(y&vbH STXZZr,=}nݾ嘌pC\e4!=8=X<t2CI\J $xGG#NnHZQ(3WHB&uNH#$Sec69@֠VVW^wt1 VMܼ192BG0:JRq|cT=/.rVvR9 i(:q 2quledh;wh6{]i6[֛osmfnD *vGW(+ J|!X]]WWڕk$j$& {7cž}_-~ <|9|`?W.OS8~WLΙy*WW޳^LOOc9ObӦ lͮBj\@vŋyοnܼ8e8+_O^hpalG+6p;(V04"SjUrnݚa֝X:{~Vw+XGWF v1;bE]XG-|7ÿgϽ?F "ts!ՉdB孷"IbifQʢVo;)!gϜen~۳;7NęgX^] I,/,r19}V~aa)4R.j,fA@ ?Vo2T&V-v @ XJD:NX̪4MeȼXh,KDI֣4/p6:1 v yUAs;$GwI1 .Ǝcо]$ž6NL4?/j?Sabbٙ;WV';ft'>efSIw2 f?N8dOep`u)ҞW^e\Ѩӧfaq;s2V ^%w4bqaK!h0ݶm='ٷ2Q"ljZdW \ )+efwQS G듹P(hgy F B)m`e,SA ,3Jbϑ48^`!@3>?,@!cDim[xQ__j6`XhL6j5bPK s?sLLlVkkٷ(Z`.NeؑCl~}l *╗'#1?qz!|ϧJD!*ׯ]ݐ̕M|*JٔW*\xgy=>ӼKA{۷o355 iʿN# C_Bocȡır| Q:?)b:|^qO@ΓTs?Y0rze|[ߢѨ^^^fqamQ#x޽xGoR^[!Wd-\u 5?2cw~N>3岿{Dĝ?2LLj1Mqp?23wk.VY+{W4F'ĝ|@S*e|Vj* s n"ENV KV\ ffUHU.f--,iw>~J,BP,ٸq#k2Z )Զ4 .k5F1Bjb:M5BӲˆ(<)J~BnRiŅYs>S|ӡDXA<$_WPWX^^deF`A:QzaѢYoc[9ua._HeuNl<0vl+:x S)y+9 KMaܾ=öۘLe,t:7o8ueq/oz+q]zg_a}K.C#se*vcnn 10K8a 3#I^kӮ'10<̕ɟ!XUX^gjjws=<7G8 I|B7':̩ӧW׹p=\#X59ow>keÆ p=."#:IՠX(PkTі^oR(x;y6Ol4 *%S1|R/?~:냞 O5V!5?յ>I ^CV|/0ŏ;C'NOǎp- n^Ʊq YڍqčWh6kM0$0.|/] τbS9(=\|_puWﺤ,_YXO ǵlaZ:)N ,<'F)WR@101 j8 J{vEࣄ`ed0Q$8|D9\*R4A(,@'1:sD$𘘡F$x E%8P u%bW*ܾy7nClܸ={/{M;l - fRҲC 0 R2Zϳɓibg=/0y3ͦ}" 6^/}S,ϋ N ^aiyI0uf4NN=2Jߣ]Jk5qwfKܹӀgb~~V2::<d[9?ζ(|?N}LO_g!7^٨ҋ杷dqq'xGy/v/Jke|vXYgR֫JSȻ:Ĵ|T #_cߡ#} :~HOQvǎ{t*}白3Oi9w=9u}y衇/]^Py<}@ 1M{w3:BB300a%4To3pKk+ۄ$Fr']ѿmA#& OJQ _wWI44}cAa>~=nC+Ht2,sRR qmҮ5$1K4 qy9zB>O  ިaiE̬M]{) 70īDؖA0 3/^FF/3B#iv-\ L_">2 q,0KQ$?Ν!M)4ͻAKZKi~($" ti[g2(0 2$8M%=JvQCbib [Ÿ:(Z4l޺Z UNͨ";vdx^z/"(| <++,P6[hh4p\ h!B08b{g`N(_*"Img# BMY\Znh4D\S'M%ʰ-($nlnDBE7}7`ڂ99j^^FiJS6lcbVX2Ied$ʑaVWWi=V/lI$]282u,40ڝaye4)rضA!;n }8Ӷ[r~@,l,cjQm?cG=ߧ=ޏ9}TAqMbg%/Ѩ%kUt^%h#bF Kαsr׮MSWCaY6Z$QA388H xEL,lw}=Y^E1i1v6OH&.."T4e!lۑ0_!|ui6d,|$eYOzĦ IDATץXl˶ٲo9>$,4/'B+QǸ > A__]qPk7эWPy( 'OW^yzi2ᣡp#Va~amȢY,t$%x'UD)ϲmK.dL\)(R_QV.9,\>(.\my("I"<3&:oT ,bl[@RY̷2AJ耉NH4+ 4)G>jDqvg_069< q1Arͳ'q&xS$Reɒ,Sq=ϫ;x"1#ٲuP( @@~Û!ډMF]udeg P S4wv攇,. tENZ 8n/HdxvΟ8Xȯq&4 vVE*٬H$TӤmc:Qcj:"m ' B`anJ{ \tnrjZm-[dܼyWw>Y[]CJI>EW4VJ(X/pMicM```'$l_zyx0M|.Gy}eVVWS*^(6t!"oS*L',.N8Eqbޞ^FFF;H6E34|?=(ܸ1?Łb)=xV\.ɤq=%\g=xK/̗/ױS)8|040ޝ}Bg(W*ͦHj:dޞ~~t:eZHKreI03Z,fjO~Gttw1v $R {0M pQ:sD+&NdRnO?^mm\8]#;h5=g9uRbSl Brpv/E_OnDQ}W>jqfEJ2G~ qxr }ǡ /P*/.dJk###\| 234U j;.BhhR`BPV6n@xA:v2?O- 3Rk#_u~&LjW:}\v 2<4RlNq,6tmXSsD",cgA]WVʆ:.5 1 PV$6F VK&zvW%XfC^jT3UQJI6nqnR,bʂЄcBlB\.x48Pkvt>ny^Jb'f MH-*_);po}뿰>ʕKye +kMW)AA|L#M>E1Bl7LѐP5=v "nTY"n$V*EH\kNGڶɤ9f0 3߽T&"tߡV31>#ؾ};hԩw֡ת|Vd0w=k{L\>O>_`zof ^fpoAx}w8lkp/67|?8HA<:S9(ZYvM2XZ]yEĊn0mz]vW)180ȁcqɉ$ 2$4Q,-)8a'Y[-r֌C$<}/ni~͒+ T289ؖTĺ~e=?Ioo?J:|zRX"nۄQ7 cXfɦ28@$ JI;2\6G2ڱ}^k <0Rd*ah4 92L Jw'H 7oPQH8ub2+" سgKKK:tYqz\#:kM%I}$)0ca,TC+ք 7 @|Ԙ@JDɍ umm qh:k J fEZO<&riXA|:sI=ݴ-RB ,O@JpEu]eW~4);s7e!=ȏ6/}_| mkݰxZ-%Ntdt~lffzZE̋R֎ 2 <]=ݴ]l:!~J$Q)&I2A3M>쳼{sg_\"ҙ,; A@"4c2ALOMk8MX[^J .]H\V90:eZFǰ$ShBFoDa,ˎB Ӽ9C8yӧO[o8ta'tCѨ+.ԁ wm;صkצ=l6g׮A{v2T*Ƶ qj5[V5s i7^Zn6ֿm!wq|{ac|pzT葄x\*rY+ؖaկt テa);>!1u+:n_ʀ jxg6P*V::ؽgׯ_eh90M6Bbf`F dRI*̺I(8&r7TpH;&~D*zT.cbM0jկ&`em]\|9XUH㴱mh6\ôl,+*LGy۶sȟZA"iJHS\ 0D-LMNM:maZ6 9,fvvlΟ?gyy?nTN9Z6dj~&W\fmiŹ9&')JJ t:C>®;|VI8*B,K #Tj:<,lۦFy J۶M$4MjPP(L&i4E$(Z6ӷY\Z Ncj%ٍi&M43bN83N$P)Mj>B|ߍv`:jB,N$7&H k&##0 ED4 -4 aV4 ,=b JY\vD2ٲh] "f.մ`|mChH8tvu1=;nVVq6dr"?b\QCaY۷&Hd1JPT€Rze?psnuÏO}-dY~aG4/dΝ4mZ&L7n|z!hAF.#Paлe t>I cr+p ޏK%Ts1M1V5%8$SSaoێQFQBiuյUx.KVc(fUzo\%70b.m anAmE|#;_'^F1*hiwSxG?ffr}{Q^;{[7q=$X 쥗-[fy2B/1tSq5Gg;=wz!4i$ pr=?Qbmm PP|ܴk `O_! ޫqq14eٜJf\zshcIP@tP vͧ~3g`ʷ^LF9Ņ>tFQ#؉B ?糊N)$n;ds]HMv0t g4l@"=Bdee 7^dei|T!~S|X])}R4=[zАF*Tvjc6tӎ}>L!+ e:qiT؉fHz@ԩU.P+8z,foq%L]%X&Iz{"1):環lE谶F>#3o055BZZehk?]y j6Ѵxu*RMq,Dzt/ih*ib&뱡]6HEBbm8 $7_Eh JP4Me!:]]twtD Ebi(^]$ $v[e]Q 1tl*i O¶ңkQ@gOB7|\rYry:o+ztT"M*{>}ܚq4'lfI ]}7~|ⓟ3*][5}$X>1B;/ bf o[`Z: ;Ȏ;zAXtpkq&ahH8̑kV$MjA ai⻞p\ls\PyVD&Q0 M 0&U*wö15wzJ5# 6a $+#4=V!Nm'*N.a!4P-C3BRo5H/ӟ¥+rXZfALv۲f T*-M \t*c}tIv|_q#.s C4[))ص{Awc~;wͱ#'X_rQvM"T:N:2in^ȍy#tvz H|x5}dd+/QYr71 d,}x9q8cl:L3Et}1:twa::?FTWHTuҕBזn9Mq5_?{'9f&H$S<39f 9Lo_/}3ժ n@o7>AZ7G qY$c@{󅏷>>ne7>r\i ys/eW/.-4u|ߣX*qT|=Hr\/#߼RLdߑw059>EH<4tN^zSY_ǍW޾ĚS=4"DӠ"hi"Y穱xartM5 VMa'J>FF9Ϣ}V+Q@$ՊPPUhorĶmrz]$؉HYe~vd:MsnZ&ŵz\.CQgzfd2DQѣ3<أ2²Mj*qkf ]\,RI$S|ӧ9]ʥ".ihq\wS!2`ȨN0P=NBcu +\ffz Aeh5#ؿc4줣Q124BWw7J(T V2"lS$F|!4̘b:e`!DN.MaYCD!~䫆4@($E7ttmQklb}:i 4 aX$mURC\V&NkNnNLac.wM*ur,kkka@R4Q݃0ulzeQZ-GEJ*3RgL! 0brzW_y+ԉض~k s;zN=8udFE\W_ׯ_|7˹so2?@Wg_yjʑcg&< <(O< z{yWxIhhԪ58w>9~(ׯ_)? Sgӓdr90ڽٹ9J oV$l^K[{>} Ν;KP`b|z]!tAH&<NQrQ# IDATX_dϮ=tr»\rBWa(Gy7Ν;y7i5똆"5%a19=I'NbQ\B$Ѯ7;`&JT%? ϾYolV#ym{#ۍd6ϿG=nA<_v ۺ۾ @lp#\'޼vqpFN$C Nmn/-ZiHS8sss*nUS5Uff&tul6C2aؾc8!t<0c0tCͮ( B6%*<ȴL0$Nmλ}O"M$a[$F0ܽfITbeul.ϧ~z"~d[zzȦLMLIq&J B50m.a000@#,;yx~?NN&Bnb -WV$/YɌm/]}ʖxlb[6DŵU%MvG\W~FT1>~V@H")cX@P!XB0H ÈVSG<8 0;=E^##4kLMp>ufoT.!2DS27RX&z&V$E?LFU! T~BgJ8*1 zeþ6 44Z-Z !,nehxRB*GRرNb޽syt0;7 dӶRpeH"ffoAp *F@(=}[;o֛0{K6jUT N0EJ8hZY^YannYJ5ο}f~ t4zFZWxb9^{,o&Μyf0aY(C;@SIpe^|'ˬ*eBQgddxKˋ oe`P%"lt: Rϗ%fjjq8vAZ$QujUZ&< u*粲;o?s twvGNrE@34\K/Je8w ]݄Qjq[3ϡȑ# lu}.6l nܸA\v[lMqm l4B{Q6# DP(\gOb/9sGn BRJ&''ٵkVrߡtuvB^8QOqq&&HSS'sߌb:=[5իWI&lڭ21tDah"׉Zxm۔elhN8uDi:e y.V,aS ""Q(TZ=*Z$U{H B0&JsQG,,#& sOȯ+tvtmdffe2>xŅ9:Q(9ɧfphN?-{^J2~Y*μs^,W^CJIY'Tukyf9r h5[ {nkk;Jo_>ͻ%H(Gefzy.^|oܺ5g_C,31~6(KGg'[A늸i j6IR,-,01> {w6-_µkW%v*q̫LO35=E*dǎKIҩ$C}8u׮Q*Y/9|}LNM23?K0ЁT+UGp62 ptvwH$mffxJ5&&nRZ/a2",./cum VWW\;rhzLD/PcA7GHR{4^G%! řau/{>1w@_s뿯oGBe,,,L$D``brNx<7ϱ^\d$|B/ (q4;t-CLNN`h!7.l$ =%Q3 A6s]uDf[I5]aJD8m/J*bnZb&D mDaBW.R48I"ÈT:]{r*`|bRi If&tRf ]hO3 6L"$[c2ݽAHNJzV@tzzz(KJN$S)v4GE74XZSY-R7b"A!DF!eR~cL?v_| !ǎ90v?wtWRqB Ai񹩫QQ`jH0{A2fdNv__1{k;aQQfX\\&h QZ+q1f}MӨ7AgBjy $,fC%H4CG LCb2 :ʲ k@\:BoaY)4M kMGW7]=d3<ǏGH?S9YDfFI:gdvZ:3R؉4l_on~m۸x<3uf5~u%")&L+(|HRlVlW.S,ٿw?Q()ٵk'[r[\pcjrmFr]:aսa[b2b(8u.jP4P<0 'OqT#mLE⌅ }4!h6,9BJ M4B!{gs2 |j;a+iHlr8W^OsVyX*2 1MDy^jB8O]{B \Wt];: ELc|)UABs.U<%T|.j۶-)#5&ؘ A)b&2RŲ/P *9vnߚ `'(d줨L]HgcI KEEBRNGxaaXhqrV#)%nę #a&Hr(KKLOg]{AX$xc 4EBI?@P:mzb}9niaF7Bi0 4QMS4A e7ʘ0M,ی]7Cwg t1\v}T/(O^ǟȑtM}GwW7ϟgQ\' Ks0lf*kfM׉qgY5S#ސڅQ ^j`eڭ6ZUoRF9 tZqA4u}XM٦4ELuvkҴGR Eb{ׯŪ ۶HJ<_.5$QlƁ`Y&lؖ88 FKАRum.~~O|)$qeeFLl($BTTuk"BGbH&1AQ-+Z&++WWq7F!c$+ UPGȴLaJ58NHaH2Ͻ@5a g(sgT*WCyHhx/QH15m#KEkkqnfd$ܘL*&!bq7i6d3i:}aDe}ưLיW_pyY#$|aNR<7Д{β^VQ˶I&S !4hZH\q[ΰw^ rnŵ E~1v#vY0/yVWhȶ$s 1<\n(g2cϞQXZriְl 1LòLT:;oM@ʥ\z㌑kׯ12df|ӢTZXNoo7kU(JelZN$vK9ĄTA"aj5< 2ifd˗YXZ$" cf]hΩ>Q2 ? QkEVYZZ< q\ C/{?ٵc" ci,dU RA 0X]-}pa:.+Z{&ۘw6}}&c {xnio{%:m?>a-D!6ok9vT*'Z8nuعc'xU4T.*ZO= NJ6-l;AfmuV Ӷ9u4are5UFZOg(HB ҩ4D7lE# ianD2uVĊ@n\/n$р T7R l;&~X; !;W~r "A3@.7jwutg]Y%KgWr|+I JK-3A ɝ?|y]yy{:};P(ib$./@_ ##looN"9ˆ!ljuL۲$ !e@l'+ ,:B_?ϮQ*}MUE;UI$vl D7)t/`b,>y4ț@T늪<ZB l*T2t] Mt qmJ qBa9 SvFGY_]guznѶZ8˛o}3ef$3sHpD2k, ӌ1wl]Wp]Aj"p9^ .\|qj+eGzNd]v7Y{?eyFn7iwV4⳻CqHَ!.GF%7 0L40)a&l!S\zE'ӸMPZ\{1۷X[Y&H173+2 UaK/~IHD݈*FR d؄Go>o>t"/},z_ߗ^ LB0W.! b ;; :f,F"'HX[V{@H63r.٨q;t1X.fdGHH4 \A4aq+h.3B3?xV1D㭠:+lvMW HC\(r͘ anX8e["C@'ZB)fƩJX e{*jO%Fۇa贬lC{dsY q6<;ө4TJm[|z*}D IDATK|c% ̊{>n71ic\6GTASΞ=(ݣ]Uq\XT\M])"wnqmlK\c,ΙKJ"w|ALqjN ӡD=^JM}.- 'OR,d~3g:׮]R.Q)drb<*fZd2; x o?8!HQ018-ݱor'K~:]N`$_>n}^:|_{G:0 "wZ靊S7,m)-cFyff/g qZjP,тb QoxA cUc}햨ODIH&Д":~ؙDtZJRascM:Tj99==Kn2*Gj)-HYr &)xѸF1s ؚ&wcHFH"XɊ{-\"!D,㹋Y~{BQ Wa%h FCh"p/IcUXX8I&arri*TwDz~u@q"'q}Algzf"fMU#Q>٢T*>^zUWQ^J ZRBP&<“0dG2C3D;08ƿ7 3:kk +R)zFUu 8-}"ǏNd2p]jB"GtC턧<}9j{Gx|_ǐ_&Q}it#I2Ǔd mL&*b+O`1$[ܼuQPڏ| f}޽+ZxϩSx c)Ķ-dE"{~ p= C.R} fSh]tG(!=L)H]ӜX,&Q9O z=\6y(; #HWQL !d26|^©3~ ɳxsvI8CXhQZ6e& $)A6%1"a&)JVdi$UQ)088jV*[ES"Aq}v\j {:/h4|Si"abgliׇA(LŢu@C,u;>uP$B$EoMMIYŌP"1F]pEQ𢑂(YSX,9&u\ǥZ.fmr,^ӈ188ȥ.I34m$Y&L!Nz$qn߾7h4H'LDZi6jlmm KgYo2}yt,';w)Ib4ʤyo27?8cnI'SSi"x0٦Vl61M۶&&9EFmLd8s }4u";(1Ƙ!f&zh,װP v4 u]3"vCGULlǣ\ (`m {EEewgӌh49~ddȔ`h`5vw\A4UڵO^rvw}5Geqq'v\vwX_[?EVVn7\X(7ndwgrDVg_oc3 W,b.oq4'4'+Ze/tA`:UP'3~$"E(GĻj'~Pїs$u5v៧X zOo0 !+#888j[XEZs?A&IfvzqӫK J>~JB^׌@F&f}cA^ Xv ˱PT޾!6^ @W^k Cd,:2'h*JBAP' FB1N"Ќ! SQk|GP]/DqwUbwos,vBFC^y)RTM"F(XwE&aƙBUdVWW||O_jTHDE!f&xWfy%>}V70bij6vD!q>X'G]7hZȏtD"]Qz5'C> G}`N-B-vu:ģPa)DYHX4ޞH=4V$HjE8li222J4%|0T*0o^y!~˿[Qtdt4cKB,z̠Z9>Ӥ$Ibc{=^{UvvY[[!fβ!n(# !RK2fmcÌC=VzD2Wp6A O9(B*ݶ{(Wj;sO>ew{D*\/_-|gkk˲ԩy߿O6g?kR$V ݝ=TEBl6G,`r|\BJT*pSY2iJ v]vl[k'tb&F-fM=r\%$t`a4R(>V 0P$FCB<$) Js1#*'ODunݺŷmb[[4[mBWr} .dpl'b؈nvL_ |U5G~W$}iC°{T6"N2k\0 }~_aR(s- CgeeM4unݼ,)h8},fU C0 0 M=t v+q]-rkxH>HELXl? nW!9ʷ,BHȊnFFBx—mն^$$ `&n5_ Fs0:#8Ȥ(Nmgsҋ$R)6gy³U>>c7D˳m 4{"Ed\?` R,?яj؎8Ͽ 'N.pIJ2FBHV# Ir L&!B ~:V;n,N*EVEwެn;BQAQ|Cבe$(ȲJoo?e3BYJ [a"KD$%WӦRT&L&- YEEԛ &o~x&UYŌ%7$IFhj5P ϳC굪8.S lms W#?WʲD:fltfx ?3Nm[{ŋ) |RqfXY]ZaDz.J,#Vo's4u|fgfVk011XԱҸ Ìs7]jUS8~jVk{,?|oWܡVqP1<<W`xh\.K.=/4L z\x24<Z'Vl2:6&KR._k_gΞ%r-f\x@ݽ=K0c&_L*&I{:8Sdsy'`fnS%"㓓(/\DEQ8}4_ Ǐ`xxt*c#{{I$qh/W(+QkGNmߴ<=jvx ӗ/Wם.a sFOJ'`#/ :Զm*tt:MPWض<;;ܸycs^I96zN>CVS8ֆDTBFETbss^j c U:E(Y@KL'o:"WHDҽb&dfgg(Kؖ%K4UI /h=(*Y۲:k D  v% ^(¦%S7eضMoo?23;1L'J}k,^R*R*4m(E@@G IBE_Yܦo-,KPbzz'xxWpYvUgA*ēq {6Acۖ*xAbۢm/A NS(XXѨ*VC7"+vt{xB8+%SXm&׏ ӬD<uX<! N*]HFcYpj|DEUL%cZ 7cA^~) "#fqZT?d!サM"v,#T%7iX&m ӭev8TD* 25=Cb|lYVmANgyp Z㬭a;6#233Te%j@8pJ_\в-^~U7YYY#ҥKn`8>cŨL0>9IODZ)sP)c;xKC#T*ewwp6H'tD<,c,?|ҽ;v⋛oh9p}!U j5AQGVd\EѨeTU%"+E۲r4b_K˼osU#S7h64mUOV4FF)Wܻ{16Xgw4ӓ|~:295dzϽ@"$@b*MV0 R4tAX,q]?Y]dn+?AUw n]c Ó? lx~T;AcMy>= qG:?yoF5"xqz\bvfubf( r1GX[<^c;R,臼{(腡O.hn&I ~(C'(QXdU /ho'L\' %P P̨$"J6 ıAgZJ- ymR$ض)YwWYu#L^~U ϮQ9(n58p#}}8{drIرy.><+l$CB{噛=ԛ-N9.x뺨kی l_ ) XN+b: vQT`uurخi?~5mx<ū^f)n-~/ =LLN248Ԥ8o|] kLM0:1I2K/j4l.G< SYmALm9"\6u"ϥVSh[ʫ@>?f )LkIr3ejzV)4掱aj؎fxO ȪF .H,la1Wטʕ78} r NIК=7@%4M!] <8>mj22:B1<4B@?v3}#Q(Y{4]# Djdzx<)؎ب/,#DHncH6Y'x.\? i=8/c}|чlXJA{hQudUFUdUlnhԨTJ@$Fd<Ω lc,ds8a8~8eS#}\&  *d"aU8Nw#2* $ $wpyDz\02T,! R < (ԍpmvww6zvmsQeE^#Lr/pl|_DȵkWq6zZM"&!111իW LPݣ\0=3K2fck8wFA]L&{{ȦsbGJ7@ƜG?"x!AjY0!sI'O'PxFr)|ڏ~gG% e H<ޙyrؚ= |Ǐ\b;hF[Q$!9n&rvIV%`1$S C^108WZjܺuJJTZQ$p&CCCz$RΞ9.ZUQ&L I&Je9Y^2'N,Lfoj_ 3d3ܹ{rs0 248H<? MP f }۬pP-DD223=zIodX,A:aggd*>/ ַqa||'Obrk>FTx) vR?zDp)("6D"RщB(`cӗ*Ǘ/%a'p#VGC$m.,OzH76' )8rݟCÓ Ub&T*|G⋤(h0`q:' :ETu>S C{_>.A)H qa! !z+:Tqh/1q:}%$C#nh}ibEw]!raD+욬 d2I햰  Bd2AJȲɓ-ًUw13=ŭ럑I 7x'=¦Q\$1<2L:}TY( B@cUUA,U7~NжP7T, G4{ =2ccOP Ct]$%TFKiSQUYY,1p tav)#ɐ*HP %uEBcOr Vh[-N, ɓgw.Ke,'N#ƂEΝ?G^w}TERw))+`7/amm_di:J8{<[cG:~ Ldltz8v$y/~ |.ÃEQ o3h[m>z|/1=9OsJmLSgrr 1a3:;=zMdYa}} \RDHxҋsP*XYYa~\EQ~At~{ʫE̜8 @ #FN(Dy g?^C@hZ>KBaP:jgݢF Q'BK؅P${ND淡[i>dHd 'O{qlϢrP"L&p]'ɶ9s cS=sBHXwfE:fyyѱΜ9Ca@&\,ch>vbwkKqmz͒'j4 *2/pOpǁ1 S'!!3S369XFn}6}}CR>\j! 'O3==G&\,v? 1i:]E?F&׺$wAdYBdd(qU"%Y X QvXq"$&;f1n̈x@FjQB}t'LN Ctz`})!BH#9Z-TpT6Xc_=Q?8= 9ૄ'y&}$uBbׯFZdmmA^t_W$qǏ.7oP`bb> ]+I&ܼy~J O244~ac#'G೫y144҃%S;;rYnݼܱyVVA^&X E 'ByUJ؎K``kI9ivttEd"R$hAmq_~D\3GIhJe "R!$})>:cb]-Oܿ+tGf\zJ?2 yrמ2*8j Gmi;*>=-,P[V+*[~QXmd2nI:'ht5TCث^"===$ 9~8gϜ'//s".fI$ J$LCњ0t=CY], FY#?t&˱3J;MYW,PU@ ޓdӛnv76fg$s~3s;2;F=rll5I UUt(fmVV((X̃`;GSBpdMHW&c> \1N:$aGulZH4I%ti"0̢8m;GȲWȫR!y(a/@i"A;C Ew C.WiRU4MJ Gh[fi;"|ryH< U{?/kkO}3G9|JwwV@ϘHXL~,,W u۳\`1,ݽD93sL.𵧟avz4YedhCvO]}=2ׯ^%1;;CEe9Udhx9r#{^zzz'N2<6J*ţ=O>ag]*S(˿K\JMM5?ɧ$hYI#c:u(uVRh4L,g?{l>˻9:5e,%⨊J*Y4P<ٹy&Zۜ:!?;ɦ]TUVP]UɵW!]qvsXN&FƅpS*H&MlO] ۿCg^w bjz}S4463?eAn`~y4ECTZ6lK" |ߏ*(஛6<ً2"y=şUv:.+W!MiU96+vEAWT.m^RO@/aUwWҥօ}0BLm|WuNJI><psv[;. N AYC9B#4i9n&":M |)cvnݺC$crbuEݯm飡Q:;:x"8Ls5$"յ5|vXCkU= \1"(ֶ:ab˲l!fu5Cuޖ>=K$e)B qlômYBвWelBrEGQlt((hkKcئEeY9ͭHRh~c35=ߧ|n5V5E$ @\6,$.Wb2/*A:o3~tl"Za~aN䵪AUUaXخA۵Qt_J/XyO=nzqQ= YN*x^fO+Z K\ $OAQ$E*+(s¾ # La bLTr(E&SQYCn2" M͟rBe._Ȏt ^N196@yu9K\@(M?m;TWk~㬬dַ^cjb00p;wǸp"aSϰ'/]f˖>ه$"ёU ~ A_k.&&E{/]d|t6648p8L6Eaanu|"\9&'XKΟJMM:Hyf>j\p476 Gv 2'ǩ,!Iwn291mŽzEtL Fb!~#ظ)l0"OlMyYK 8PP$ž{6M\I%S*(~aq8LcnfB>cܹs ihieTT ub2W*˫xf>ȁ,XlGcC47y$jw%H+PJo\ֻ]']oy-z1׾cu={s,l ~0"mV!K <'5e݇X}YCWKRǗe$}u=A|u(=u^hHΝ\/LfUAN~ljkj0  àVkum>=uV {%ZIbQ"y@C#>4zۢ\T2 y薦i391"8Wyt(_( ^_~%ss=j;FeUz ^`Z.wV ãl43330Ied+]~lB9PBSمT IDAT6\Ӊb͒H$ds5țo ZXXc~~ŅE><~!tHP-p$FGF r ~h+h> MUw˲(K4!N.~_TB @TMX( f/)(MFl϶Aq$&iMb[8,G& aS[[Oof~wX,4iMg)L,>LMM BON~|w6#c ޸4}>5X[FX@BR675eKXX\$T7nH$8u9jj&Q]UECS#́I:qٙYr bh4ȑ#DYE9 ;w;٩qz{zvf 9q#$QS[KeE%xUVRi rd޽3<44vf]o#a;6@h8Bk[]]]m}_|?? LdyydrY@>HdY dEő$ac[>F6'čT]`0HWF$`_pEeU 5l줮C.Keb1fI%9ioo&_c.tQ pX\sT I`p!Sg'ao˗&A{jV7,^e^D(ᕒχث0i(L,C455b[6`d2)ܹ Fr2H ĩ:6p*(s/(C6EB\.'T׼/Kgب, uN]'IhXttv 2%AH8p\$ ] `H@\A$]t]w5F8H!d2R(ijj$ sU[6oѣ1mj&''Y43 E=O~ cdV2mc~,C0 gwadd+W_x7$1;?i kd2Yfgp]1]D<'VVbZFi+$oH0̟:E#$hm hI"]Ad4M'j@*[$.ŢiXD"QgR%c"Q,GI߮Jx2b6b{剋,Ȁ^2bH^@޾| nyyIbHOO4pڋĢAfg1 ouP\.jH*O׼UtGMU+,;wciFO?>Votm.*mX^^fzbrVmsEh,~ _X%sC!!3B8&_ȓq2Ut:p -`6O<~/] _(1 DcQ*ʘ]\P,R(}مYɤ"1@MRPdH(Q(0mkلC:;L0عw/p<5dsńLdhx:Sl8F,Fcc#@w8up{ӵ!ԱQd1˦MdxxP(aiiiax|l>eQ$=z$ҁ$\zX9[ʅs( <6R[[Oy4S(Ll*`Y&GHX@U8[޿O|,AFD,$o|(d .^G->|KW.lݺo}[c{h8BWHL\Iz=v~OFWJ0 8# ޅqE\9K}tS=q4]T]lvt5-u7)qX #]DA/$0}/U!m޹>mgʰ,q*\I 0U{yߒ$H 8} #466 IF(/t~fi|>Yx) TVUj:(~7259 /C gqqFk$8{H#K" -H2hPR|Lj:Hg2XE0IJU$@ @>AUE0mAuBP"`BX}V%rqs%vGy(U%@M<"L=hG]ɲ]`+ dMP(YZNȲQJ< [Hz>~Af BN(dvfQ`|Bc~I]c{P DMM g-d d2u/UP]{ ?$-7a6lc6\iI\{BC砸}sꙚ#suѲE'O;vjKqr,+4SS,///0Ⱦٱ8˩~϶m818ښ*^̍0mOLhkkA4YGg9NjzA ck *=뗨mZB!.(/?.]J<_jMS:~go ߸A%_Uߛ/ݿv~;:k]ޯ~[9[4\+ 0۵Ģ477#LO044D(Z]^ZBTIEY9c475L299I2BVU"n rMvٸL:GcC{c  OpQ}~1 EA 87.U?v) ȊJ4#L (zո梨BYʝk~ޅضl!(:#io{@6Z,;%'GUE4`MωP$xКʯ5UBQbZH433Dbq荖:mfY\X4pirKmӳe+XNımml;Ug>z-r==DBQv DcՄ)Π|T +*aW mi iӿs7WONXi+лy3;wq&N0f`p*OLlUQyŗsz{& q!*+*9x mܼqEa9@$YvTϪ@ #beHO>9ɉ'8t0Ǟ| X &~C`NskjIg446snvډ J1 YVy2aC;Tmlv~QSU q&>ʿLOг]466aC $;?4+$#V)1 TP}}[or?edvLNOẰc?eٲEYI4H؎ 'zxP#]g}|t@ïԣO6pzB+IxHour4(,>ږO/xkJ,&'k.V}^_Zٽ-X{?w}y?_0QT jj/ر'x0E àJ@|8FUU5PH,sMett,,,kxݻ?2ff`qnsg16:!~01LdB@X.#$I\%YAUM$gY3nHPd%Au@4-,˶7KG(D8Ou݇&!$Iވɘ5H+yDRqT CtU\|-c6ESCGW5@lߴL4]Cafv32lK6&8"DDR#I|L&K>PQPF4m .R hTP,ڤ`(̙OQVVJ*$uEvRW]sϽw\6˝!:::cǎlH6aM޻v?Ο?CSs|] K a'=mDGGӄC!**+ٳo?"_ƍEeÆr s3h7^۶#*  Cw|tcl݊b*KV\Qs5w^] Axh7~,.bXS>!!*fc mմU4׫l%+&>~>> ~o{Z' A*sý-բz.>v.xYl6Gz%ѣr)R$R]]CgG+555O,(BCGASs3EäHhJjju55,!K2Q$ݽl}dl~I0K2h\a b]o^yKATUfã$q,$ZŶZ6l`eeey/Eâ DQ\#9YDxxIL$˴EotR%VdE0 \#`jȲ(I$XXDz-g^%k,-\0M2XZ\Be*SM_كERSUUi"a3Tp^lيiLMM! UU$<ӼKoi&^oo‹ϣ>FGGȤ3ۥ)ʛNgf<3LMMb N B|$Ɏ\rϜ`ŧN`aq۱(diGe%I]2$#P+={o};<‹465az$p )*+(C\|Z~= -4n@k{;p|qA|JdI/ƙP. Yא4*{%^xe}](-Y wwsQ<+s?"si9 LKc31<<0b hjjfӦMbڥ9.]DG{;Kp!l!32`۷o1?HyH4Fww~];Kh޷Mݽ.8C+ė9r.]$JȈQm~?|tJ6Pr?tRA$km ow]PƯXAi(,*b b6m _O E>H$I$}?W/i?|SN(W>x)!QQa$w<,WMɕ<]`jz ZU8=}7;UBWhniͽiokǧĢ1@Ms9TUm%9'\ɕFb ]H^^d";Nq$ $VM 觢L&Q,@,JP msE9&{X\Wttg T:M+{"N"DY-S"ƪ"F\۵~p%T݇i982+ @ ijϿm;;,HaQq=`I~-dYűjb!ca~-r,F 6mw\`;->l0m"KIw\˱Q4OKAhnl ˥ȦSȪX,x Ν^AmtETt\%ɲg)A SP?lEt?SE_0gϜbff]Wyd&$K|>VѤǏXm`rlulFVaYEh4•+רeS&Z[Z0s>3R+ICl8TTVqRh+Il ͧQPGsLbhx9癟eqqT˶\:&f IDATQ  !lБ#<޵N SbOcS ݹgSW@:d~nX$|A?. o2p* 45D+yB EU #WTSOټ&CD.H(\vmwi~ٶs-ZuPU'sY'M 9R+i''rDc1$XE0KqA˽wJ[4r\ ޏ%<#LScڎgFz`EgU$PI/kU_eK_NRZ~aLկB>wLGaD*>SBCG16:/BggeR]]׸v\jֺ˗/TW*X ]ӼwZuininzx++)2472>dMH:M4^sq>LsK ~ׯ.drEL$ Tal5xSTS-NYBSTnMijlέ, 'Ke"*ےƾ@@K+ o(WO` ;B߶>#q ,[%FG:UU$K( P#eeUįp,]{W9"hN0¶,n޺CCc }vE&eE&|TVUS^QšCG:W/ .,L˗>#G04t!L@yYy dID"IsS+=xkݻr20uAҙ,`MqLXYI8.>_΍ڃ߿ʲ ,%t&J:CG$.~:WAUeS3gt2\Z4]MNS[[o-n3,'ikowS7Qd^zԵMg$7߾UUظ MmbK)ҙ +alK<ԎM:AUB0`??bܼYziEQe͍ؖ4qujjE!Hhmx]>L|A;FSu4gϞ{RS[M:0Mvt]ǵ]ajꈄw-7 L1HM^yǥ_Eäh88ݛ1MLF5]voOnˁ?x?hŰ>R>FEG}z-J&$_#9%yw2Z[[ɩ oT-G&Ae24B` @"gy9_p]ۓIJ+?\p dIe.^@&Bȣ> FWMܹC@qw(fddpL ={EAYQDs33ydI&_(-/}\52` [mH8iq%3:r0-˂`(JG[i:e4G.FFFPճ2)lbzf*LӤQ>i{g?s]ƙӧWࠩ ~UP%UV( n >4SSysMG}gx $Xo)&ma~~ð(/ QV^.lzQa:&Ț m;PuB Z+)qQgm׾ /Hss r9_??Ŏظi#faalF(;Ckk/M?'⑾>~7o PPAy?$ /^ĴljjjLGF9Ran~ifgfYXud9_v"_׾CeE5hXA,;wn35= < .\G'lo166 S(yq\B&%RV^N4]xtOOJb)ݻB ޺C֭'?ql8](sE|ׅ}0 =[6ؓO!ZZ[ٶm۶nCen߾8Ք㺂d{u|qS'OFss3m3;;cccTWV )pG/afzn*+*ÔǨ@&FCR[6s/ oSO7TWW322BscypwBpBQ֭}媨:)ٳg/S33( m a[ndE'Ng7kfiynܹs[X/ry.]J:/,pu{ ^|%8$vgzr~ 133KCC#HĒѮoI.iN=$m}QǧNr5ZZ[(+/!MѿDYzVRIQU2>>Nyy9*vWܺs˲io}s%;8:;IWɁ*6nDoO/Ţ@r˴}UU$`sssXE,cqaS|… X{09+w#"#}VU*rp$<@Ё(r%JvzfygE/2vZLZ-H)J$E{P(oP҇هmWU789esohQ[y۶099HM$"VV,ή.G!#EE(D6&I MC 6v/kׯT-Ӳm /022FO_?SR1vllZLSUcҒؼ=N:gzjI,_͑J_ f)р]ӂt*}++Y@EQ57L&S,F%0c6SFB&òBU$:(j=FI-Y)ٶٴeI!寠 l 9Nm]n]'UU5WxϲVRѿy |b133ͅg'b455-7n"(mi& yרfvRsuѱ6l ːJ_X`yig.E,E SF{`SO_X`xxI::Q[WOEE##3==C}}=;vb]{|{QKKl4 m135N]m-._`ێGȇ歷x_g|tF^g>K]C=9*dr"DF6mģ}JgFt[;zB!O@?q'4܄(d3iܹõ+Wͥ 0-x!YYE<i#QE%@WUӢHvmX^lt렪vܺy#-r<qmjqjps7AQٺB#w^^,M)WkaΊK/<hNOi%˶P_+Lc! -l}=@uo!ݞ&7*_cɊr&cY Va-AW9nWngǚx5>/l5X-*kZq-h4KKR)ym% q4\˶ mN…sTUܵFlBFgHSئ?xƕ&A y,& rY5Ѵ++"(zP+ y S zP Mg>˭ۃ,-2H 4LSXB'NҕjWT'M"Y2HE n"&f4'◿B$eb|ٗGF2,\^SiH Ӳ1 Q6!ݵl {/hH 6QSۀH\$ -dFlSVpu= B*/ Ģ {^.]“O=C"YCuu-7oǎac} #$**ٹsuuܽ{WRUUMss idׯ321OlWWxAZڈ'477QyBATzE_N<JtfٹY1MM7e60M<2.C04t/rEWhT:eYi%I391|;(cc#ذ~a=r6;|uѣ}vLV T`@;`=F.'H44 lC -,oyjQkH8|=s,,յp8D(PPv+E]G"0a]g/_Ag}E ⳟX 3?7G`/}M{"<};--h4"![[SȑryblwgOA0s5<7` Y͉U00- ={qPNRPM}۹uxqS {! O ֒k1n(|jcp~[-s ߼ɶ-\vMQ4**hii%rUΆ=A"ΝA =N?3S$9'ٶ0C²5|o6 쳇| wo0$3l> @>Wqx#O!+Xi9{4.i$+ Ip,f|t\nE tk LJPTTU#Nq|t;6B@0P-KHFU5_B]r r]p*B*JoݛL$9smؼyɟ[όOcۘBp8ĭ[Dc1/~7~:U5[F9 z/稪F(*z idin l² `j~PP(U|^RmrEѱOa {Cryws6 oN*&J0miAvC__??kWosZxHD ET|1m5Ts%Μ9K(չ0:1NV*cg?HS/,JQT]GXW^aGH&cN* [X䍂ܽs0z{H$ >S ޹IsK# qu忲lۺ{cl۲} 6nbyyho*:ݿ^hjl"߿mFU2I*,d}w'N\xH IDAT8B6!҉c`647uv2/8u+K)**+Ycye}۶H/_eY446a]W&b Rn&%C}3] > Nlh}Yl:?rMBtI`{1)88ɊJ2%7 :5U|^^C.wej AXty^ћ[LQeҔd Y5Me;NM֋еZ_qϻJ_ zZ2X; nƵO˕ [)=|>jj Xò޵ ro|'N0ߏ RO*cv%${.N8Fgn~OhcǎrBss3ms47*ƻ1 m"!=HE,F2%ͣsۘ^Iq[4X,JMM u TEE-_b^FFK'xtN>M_O/z-,uu|폿(ܻO/|efy7"cۂH8s?OUU333\|{Cw.'&?;K AN<KKKX ¨8c>ҙ ]]i(MEink% ɵ1-Bwy3gpA**bXXDz-*wWrΟ?KCCGtt/]$`aXO}mׯ^RKB!k{l Q U4^Äd#O:mAZD,A,ECik_G$MիTVUL&i|˩4N?NGG'/B}c=ssӃj]MU4 IQ"r0xam>-z*" ig,̌у@xԕU iP_ܫtr cpRz5Z FA 5)|z oo!( A iIh˛'? c/~-C/\ۻ*A'QUUE"`jrV ~֛ܸ~p(ī;YGQT ݜ?iJ:Mfݱéc.$i]_ y095A>P((q`H,Y6eler,tS`1=sgβo~]ƩS0 ,4,U!3M$Q | YY?%2!=fm oܳC|~S%c#J6gHTeT&ܼq={`#=xEEŲ yv^|%.I8gc/Scg1rׯ^c]' M2CSIVҸrY ]]tKK$ r,?ᏸ}'ʞ}{xW^f=ܺe8R7L. >̣vR0 r48q{ht/i^u:;:ٻ{7B!1>>(4p[7o13{~] _iTWUS[]C}c#=}}|3/Iױ'hjn,?${sL).]elt :gϜlfg9?qq3Kڵ] | X E(y҂ ʪBp9FGGcaw2=5 55>}~r<9ˢzjhlnftbl.Gk{+;NWg'SڱS@ڥM;Dz3ع{[mN>͙g8s$KKX.^իW {ؖ-e,F|B 2O[v &.8C@ʼfXeF/|bȞ򪪢nFdBu,&%+J}y\rm۲/=lXӕ2^2x0Eһt q<,#.[Kﮆ+p\qYKL0  l" 8~ TT355c=έ[twokczzpɕKxÜ:{[nIGP9jd# h f*XXkirP-[sYwviffxw)J.2L^m9rEE!XLNMr]{If t00sf|.~ߟp$Ɵ'@E077d2ٳgI$D""U T); WMQT=nݾ$ƍ={4!Hf(HӲß777-[hhiҥK466؀m_"Y-~Y <34Ge߾}(\l*-2v3Eml蔪CZiF>+m ,6m´S_O<M13sE(0?7ǝ; Eܼu|>rM>>zL:a| 552o/|&޶J`f(B|x7o0 ^}UjjjXZ\ܹ֭3-[gttw}N;K$aee>>‹hyclp G˲xHmj`}:; &ёhm[𳇹ӧϰu6¡0S&9s{VaeiTjI6B8!&|]wϾ?|޹>C<sy=Pl/{fxdeb$!͛7ywX7~ ~OVġQLh sy sk4Xbگfb('CT4-+Nʾ~i42Wi%k# MSs0D"~Wq]UZB//+/̇<|\>Y ~ HOiX[>m%}{m5HwVZ0J1Nhkm…,..3TWUOpM&&&P(*]ker| =|7em( @ ȢAaAtM%" H~C( MX6b&t?7%?6duP[2GdlۖGǵ!*8!{(J@_!33s.tM@Rk4w~W.-[Qb40Bq;;2 * \SN066͛eeNmH$ "8NmM5tuuqu\vMZ׭mnn޼S'7XUK;]:fbjn,KTѝGhp% R}t-X(h{+)Ξ=ρ#Jf;Q~[444}vA,7no伪*r5Cu]#a;?NPڵk?~wv299;w;tH8I{h4JSS#;wƶTnݾŽ{C!jjkDCܵyz' g!Ҍ-lL# 2xg3g M pZ[fs|ܾs'| |_S$*شi#7oA*b1l7044" -H8*KYw ˳k>8O=u}f~:۷o޽eY%5L @ .HӘɉ'fjk$YxxOh0dRA4I^u5ϱ(z eGXMAH] oˢt2(~i{!?q)Zp4`P' cY:%(A3"q(r`+˅hXk) 9\FTҗVaXKYy/  W{,ɍm0Lx_RW[*'O'gqiY8{4+TWW?+W1>1*ؿ\>G}]-|G?#R__Occ 3 bS˔hlia&PY/&m f^CVTӲfq~-[s臬,/G##ʀRk}@ KPT BՈ%*ػo?ɛ'Tp׵X?D]] KK\zѱaYֵI~ψDBݷ͌OKE&q|)>CcYGj2HUTL0e#00M&%d˘PUZh,Ak:V~*BhZL:G8&EpURvC+ kJ i{^:)Z1,aܿY Ӥ{WVX^^͋ ``:EQ$to؀оhC8¶ ӃTIEihBaqa]n8M==l޲j,s1i΢iׯGp8D]}=ZPRҿlF z[͋mЮ%[s'Nb`@0k,0{9, JQ]-[WWWs]mN8 Ǣ,,.a صs 0H6#ʪJTUeieӲb]{;eJhoo' z{Nsxi[;1+4mlܴ˲ ;~gϒN:|zvv),--N<ǟ {mL`[ PrTUQXI8v(tW1MMY+{Szzs,>-;[e) l޼wFy䑝:dH$¹sQT033E*"͢j:flٺ۷n22r9x#^ڍe(hjӲgRdE]hWJ~-HVp5pBee%uu{*CWWX] IDATq]jjjXH$ҝ{۶ncmJss3Xή.kJjjj1@!£/8ʛc(NȍS_[OsS FP-[mv{toZ[[)s9!pL&2\d%X-[`##d9ZxgpaLˤ`9\CFN{]S4z]<܊B<'Q[SK]]-ʕ+`^ H4Lӛo8VIyѹֵpW/K^;>>MU, Qۛ?q|VMxV(Muq_+sAQxp2"Jz4 (p|jQUZ[AuU {쓽֊F,..q9wSS]/py=}^x 7~, 1 S*pz HKZera|]z]+hV^?$YYͭSOF$BU$B %FبX&>)C$yJ6!N'I\c(zBť1u3nܫ Y)RSS75PQD hDc1|]Rp,4%zLq S)\=_(9~" {ǤA~:##{,ݬ%'믿N<c|t 4#ΒY\\de%/^D4'OPW[я;(6LOL<l1 yL`rlٹ9<-mLLNR]]Ͼ]2-'"pWSSCks eSS]`O߿vxpC ik:uߏF#ۨBSc333ߑ^ BW\{, ݳx"ƽC GydN6mKA9{ \]שo`]z3 cYX}Qpb&d-?ҥs9=ܪgXJJkBxvȞ({ mq~Ek{7p~ozB}Hvzr9``1ScD.BH5dC]~h>,,)J~$R/u9^goBApIϞMPlTG[ЧhبضJ>gc=;v°`z>==T$+q5Yw%, Rٻw Br߫Q0d2(B2 `R|^{6.ex%v)"Guyc࢔D"n޼ICC:Pū^d!L*|;,,G1<<ݡ!*D6#LL&fjjDBv) Yq5TM%;{fISTWUQW_OҫGQUYIP׉S҇Rt]'аj5rJ pW)Mm3>5Α>rR>bxdL6K{{Bjkk?Bկr9~S ia2<I0Qƫ{'#5+z/Dɷ(_s)w8<9gqe#Jṗ\ `x(ofnUC( Pd޸e9T54(.+ӜJ/XKx"zrעt,5Kۄ{\PC;?|gps#^&o|JAb쮦=PŢDQhomM=lڴa>w,VXI*?=Ƚ;D:FaHDLmdh<3z˂Dɱq_J&BQ,Ė, G4E``[*۷?*ͭPP8BE{#R)IR' ·fkW6\% 5OWb&lʪ*DEE`WU7a8[S\&WGT !(K!H7nb@]Q<]ףjLh4J]}H4IV&4 mrY\ʹsPU0HLOc H 1LD"Am]ՄB! JgYc!^&7޿~`-+kk[BUݻdjj:ŝqlihiiZUUN/ Ν#裏wޥk=hl>$I (]Jֹ(D#O(EKdXS;b"Jmkϫ[G٩Eþ+7'A cܭg6uHpB'zV";.23Q=3sU51?d`u{D"/Q]UM__N(57 2<\%dg@YZP(d\ͷmda>szY0{ri'eDPl .Fӄ`0bG_Xm7lGwbsss#Bssd2C2vWROo{2WNȢPU*Ñ'Y.3t~ft<=]e.Sogv\ʔVpgnk7_nsssԬ)˟MY);4MB0aCnf4(ptwƍɵdD"b1޳H4RLcu>,v9ERMӈ ʉObܽ{!V"YݻwI$+BRAVntyPN{*~lf3+ix<--(M|M\!XBs;* Ӭt,74\TYFt"+oU}rx@YX[zז1Ob0B|.Rֽ=(C g;)w Ge62$` &[ƹs~Chϱ,y?G⅋ٻ!p;ΝA~یOBTWWP-n^$mҨaɦ@;;Yױ={S[[?w(%-=!(LZti:æ~폈*EcAn2-QQٻJjEQ)?ەz6R.׮z2U.͒==;?G8&8OV@k Ydvk ]!.PGy(23Ȓ]l[/_̎; l(;gT{B* +tK*>f+ &#蟛mv*{ krӶm,'@QLe%ز(gq\U  C:)=66BEӰQ婁{XE{{;9 Ǔu_\Zbl|ֶ69:W-/9X ӶJހGvϸ[G)/c8dc[-OӶru)y3˲4-lq>a@ӊi`H! $mtU`:U(0-:uMdʍ8"(A5iUϭKኊj`Kg{pH߫Y\w/rlܵogw^lQ6RT*.| lR઄[%,S.8+\R>֚! =9_(ES%vc_^\,*ĀvS}16z_XѪy0~k,rFuB3 2Gۋ/''򗿬 SgeL,ƣ ?s,4j ΗQ1K%4P_ YrO%ad5rQE2gm:'B8ë́[Vxry" >s덀Rs:# {\^^Jfљ*E'}k{gV۴2qRsœ6^tsYcOmDyB5..^2wL#2.*ŝ3sa 4E`FĶxuRmM\/VYtҷ0f,5ZYb:%fhј}Н= _x%nTu=cͽYKɓgx_0oqswoos~B )'P)I1622za<;ٳlVxODĜ扴[)s\^>ЇSҚ(ހԲ^O楅˗s1"a4^$1cXK1Xz70J3}+S V9:-\o[\l3zm`XsΗ04謝OyU`fx|1d7!+ 涅﴿8UY60<[{nIY֌܅@Aŭ F+e|y"pW+7k\>^wgoJ~e2ٿ5G]N"'䎩ꜷ+l{b2J?q#V]VuދyjNM9v8I jl!0֗{x{z4B14h |A*\I2+[Zk݂9pyf] e큇09\\`=ڮ?oo6qB$8}6 oCu?snfPHE`pjXcwÏ~t E<;gF+ܻ^5e,LK4H)ww{n^m-)-RFo}+/XjaviZem ĕѥoo;~S YWH\>*F_ @܄5D@kye| %4Ef=Yc/n>/<͜Ḱ3lA%Iӕ-GJa7IlZ~w?Fp_` ,2uf:g"]jC&*9 r7EfS0W^^z+YyLk_7ע-ե83}wCy5?m,RJMw<~F7^|c֎MIlM^+-_;3#j~ "i`fX$4l]i〜;|-R~Tw0%]p+%vwyb-yѵ^c瓙1NSY/3j^t:Н׺͍n|f̜V R i@v~dyz&Ќsc9O,<srp*D?ӵ.cZ,!`@Suio> `f3eceNu=ںC'=,е7dYb].𻾋r3(t+ `cX=TXjYH8{XC1ugК29U}^]ce̅|3=s{:sec׵z{'Rd :u(6!s F#i w?e+{#RTK΢QOSڗ{;|_ʗRDZDl~?Ns~_c~ߵ@iaJ[w3A xLkZV,NӾ 2/~~)8/^6_\ :W@U ݘ<ܼ]ͷ2!iӮ9[(X漰z#򇙋E*<&}X(VA.1hpJ^l#| a?>QK1/ŵhc zzfLDn%HKdz\ߵv"ςژNo:Mal-ڝhrwl^J O>C\^^篱]o֘VgL)a 8Ȥ=As999hbHlУ:}9y/_ޮquu'W%q1!9]yo)@RI;G:ٵ-ؽxQm4k9}V4NM,'Qǔ` FK)=s:0< B>g]{ڝӺ[Ί͌7' IDAT,RҘ s:734||v!lg@<\$̌jz}{$q bΎZyBz``y?GŪzmۮˉƮ*1 Ҵv2v/_s缡޻:NpL7zU nXynNygn5R't,emgKԛ,Yro3Y[fu4љVю]|qj{lf7c:e%DŽ9)Z=K.OK{_9|6*ە4b${3օQL{ ߅%m͘)v/x|'X7g~JjdV=g CJr[y }vR. <_?}c|]nHx1OϹ5({x1mXpel?˳F B|L5 T*u\ǀRig>:A*l{8f݌'L1ٳgx^z/_jlK}d's`tB.<_d]'~Jde¸]ί~kvO":Y7X4z])w5$A$gCz@@ ny֒p+h|bX%m+3zn)شV=o,Vk˙@y^IJg'S} jis5#o]"(۷ΛЖO4kinq*3!vrKzv~*ϰNȝ ;PK31 |(U7F|v~zn*z|{'gDP1hmӂօL]~qv2iGsi]K|F "Bl5b:/fb0 z9;T*4lmR^I]?//.?f\_@RH[2\ ,g&?5Yݓsl^@9\*9ˀr}^ ,T&{Rxe]n pƩqOCʅ&` [QG,_:Hu6tPa9I5KbH<7_RϹhY=+P l>a O1CL*08 Yޟ -fo>RX|΍)|m]5 e+Ϝgk(9'10M޼q_%pۍbZ9H"ҙ3:/Rj {Gn6҆) Ĕs_:~?bį)>"PPMM5)^YӜ"l R:FC2Ɲn3&FX0fYZٵ%A,JRPԜSĬwK@Ysqʌ i1k(m]rpG???G}w!([0o&WtNƠs,zTcX4lU {*Mh\C6B} ^ rl Q:g(Ug\*bZhSj卅2Z 4>m4ZC/N6 V~A֕۲Ke s3٘ssMpCK@V]tYpn ͜h91ˇyU$Y_ O B02s5c1"nƱ-2*!5"{#Ĝ&u#"5 )CϮ[’nJge)gIi*1.LuMN5R NGӈ+{xqlpsw%+/Ϳ|{1#=`]B{MՓ`ŀz@BEGlָyDS9mگ&޼;\w7 &?7IY2)XˊJv ZdN0-u6ag%.g R9 Ze%YNͶTX n Y- 7@fʳRL[~7~by]ńYVHmu=*Ln:FgTP+X:dנS.-BMLCM16~-_oW Yۜscj;s*^e}NI=5GsDZ̅ꬎ`K;qnskh׮9u7C=~NͿYM+*+`JLs,aj ˆ䙠Y"lw]4 nIMbUrN6q)g]@mh 4a5 5 :gv;3pX pKjqP- 3yhWBܼYc+z!9SAGmP8sp!, ׀1[4ƶD*!X)޾ES8eQA>EIJ a<(}vw] OM  y^-ưZ~A\ 1N?x->'_B=!(<@qgRB@1FLZhjF}WWW#Q4Uϴ 1I(yA\Ӟڅzyzf@C2\A>k$^~??ᅬnXŋx緬"_h)#MT[w*wRUNU *Uܵؗ|UPs.i1i?+=Ba@NyYS>ҏgg(WV?t]γ}`@l]Nf{_뢙 ǩTZg i3&4"i[pYY\qwˋ<ǐ~4M|>j;a{/EI;)f;!tT;X"pwv#n~xkpd2֮늖o4M*¡>fp-ZYPZ^8bX0c:oo"'jDZPono^]g4"Zd9gLӈq:` opm=͒C #e=t$H!}jA9 Àq8mg1+k/0x>CLӄo*'NǛ7o(b[l6t]9dNJS  +H)fzՃq iIg}vsX38E0񈋋-K+^vm3unhh?-ëְ́guڒ%[Ƴ@iQZZi9uў}{ ,էK{-]A'{t8_)-e4qQHp)ނC=k ܑ@Qúi%ŬAu gxp3Q;gIC^Rq8pww<8iOvBuxLe|B@)En1 RpwfSϑ6J){0&x:f!]W~ZO0R,9;cT?ȘJ`u xsF1"sqӧO1)Mˆժxj4@V1#Rq? =Bzq:aـsF`0nnnld t޼z%~\>)!l{ 2\g:(JM3K;#w91Uv!*8t$6W7x>%@RFpa挾s~-"VYx-AOhP#u5z À{< =~u)M1ARD!a8 M+ Àv @nCADŵh${ZGV|+PhԀ')RUFHH9uWW+!H=0Z-]^^?n<} |v@@7 I-Y}'\*`/}XjmB;&zStΗ>F X5*YkCZ@[TRژ\rݗ˽yv_Z`eyy6uRZcFȲ|ffseq稂mHSsNq;y23:37][;uxJsH-2@c}$MwBt7U<`nca748Ϟ(#n@N!Ll80M W-.|JLBKVİؙnM!-;#`F YMu kD6[l(ոaakqD)aunl6WF1_-N >Հ)1 =(ɫ+^5z:RJum1iX!0 IDAT]j5ew]c@a.Aۇ$ksӧO,7o?q&<]\n)׆FrDl RyeբNYʢJ1a|Bh"Dp(iq::q)k\]=P8#=}U\˗㜒*x;Mytc63uNs8NG)Г3<ʚX%Y)wp8+-wxpeKnPV-s\7>"X? P s=ІP5 NQfE5"y+TX[εK-Jz SBI-OR%;;o^x#EWx{{-RD<\]!ҌTOt9|ᣟïoa9%^z?eJvWŒA,>VniJpK*|voOό6Za\@ Ҳ/A23k]*y9/Vl̏I9`{/ƣ"Jό&a pF;[iΉ6:s_@V>bIOfhW8\֝MÀdñ,^%U%aa.DZAW^p{w#,AN8 t\`F+KۜzHcytpLZ_b4gfk,;p_ fq*T> 5˗ŋzK@ePx9 罽V,ح\u\\gA'~'6XlosLӄ> ϟ?~˗`5я~V-vxO>ó 0y{(5 I=dw`3($!)G6?,jؖ'هS(!"tIPaĜ@ 1 tf6ZՄ+"<%@36zrZ6B(ڴ#9暦)+V_\b An1M{LT y u=R43m=zXá2>hf4lB`F^'悴XV89*fei}$&r>jQj_*ݲju/c.//S1NZVD _,}ߗ,"(q]'5qxR*93=V}gO7^q ׫RKИ3k+!HT~nKƠ]/Pfݮl( oL9%|߇#q{0$sJmt!Nje0r&TҤ,1Xq6!d(g!J9!_-L7>"r.i{Us-YTR CU;&,:-I\|*| ۄHXrb<]GkiYRz`sj1l>T 8*gs8X4^sqGmBE,h+J}| H͵@X#c@ SWyΣS b+O8?1>GN5EZ=lWi΄S UJѠi#` ZP"ƴ"GPSOwn&bb.~UktUY̠Zj|fUKJNKKF׵iw WB'9eh^I,:ˏt %6”3rb8Kbh,3\3Q7 Z#C▌ZB9:ww`q&dn*P34;`.&}WX@SՂtĮX~Sb&15_c*V6 ؎0jR#DBD mP&5op}KX]iK,a^# qz?Lx5>G^^x; ?Q a=\]J[O__8씆zSBJoonzHesJgfGR'=v;*W?&0Iw])8sR KS6#*JYFNUs$uFZdI1MG5E&V#eR=*@ p ED9IEJ| ,cSJhӐtUJ-Pcd>5j7lWkU;2Y4=j~- cZHd=I$1@*mX/B1gu1t$0MGMx)vH9c{qc 0;NR+131l7|LQɚ-1B']g ƨzYcl+Tt9Ҥ=TפNqq.Ya$=Y@b38-`Εuh- 4xZ1)մ۲,dI:k.a" w[lX l EɘH[¬%ACD@6Tּg1gp? />B5~5iI4Q1;'L %"M]NRY-2QY3[53{S P5KjƷ8GSKY@ཷe8ÎGpf  < tA?x=s0 xzQN{L#Rx=O>Ë/s8ٮ'?@Jo|dLGp8w9"/K}u%,2(N3È.)6ZlCRϢ4o_rFLIwFLjJ0%Ji &|}Ŝf#Tه8Nb*V cp^AE>`lZ$ηc)9RV&),vPRg#0o>F5(nI-"I%NA2eT/ZYԷ4;chHu^R }18f$\Κ S,LI' ]i6 )' b1f(mD8~=`sq&!OUzi0X gzPhP,g|GZ%##7:s]qp$yU`ͮUA ƨVs1Asɰ">/wX<%2)L"٥nUZr`}rf$_QJb:<ÑЈ#B:A,{C1pX*UU(VCԠ2K280S1CY+9"C3oQcuX|(==18n1e"% qZC- ҂E3fFUgoH:V(~I FL>s¤(CJA<: K)T@ Q n[ԯC2$X tjsĪU$ͽcT@Ig!w]M(-nnވ\KVMLBɌI-9f$~w;})H$y }puu[(黇ÃDeX!Lp\2(>e ;YoWȒ@P`X:C!MpTK9;y!L,dr"#!^.gW%XP4B1y/e4 *5ѰE B .8aY6)i y) Ռ Pŏ9쟎c}0r  $BN@K̀BBօ #F;Qz&'YTԜ脐Hբ c.>yiJÈ֛-"Kbpϡ}wfR2; 3B愞0qB!d#Dr1]n \ 0e~%8n/$nƉpYtM9 wA'!F)0<:D&Ѯ5{&t˳r!o<)S8-q0@u!$8!pb}óTy93~h(pjK1_K뜄;FAmUpJ1yaZb@Tn 050k0ALm,'r I2 }L32\&;6ܭrL$4-Vjt (i 0%4{Ĉb_w"c%puԥuP7Q"9PMlԃ3HGJ%n %I BRKGjyЀd3$fKZ(.I6C?`LQcKRfM+̊D;IѮ4R^wCȄ^]{LqWM@L!z9VD$JZrȖ@iX Ittl"qUQqث(e–}C`ELr-&sRT4SWP56,-Nrv%H( N0q%W-BW|TMP\D꙯`6 ´'.,_-*29`c1CXʗJ!IC`<5@SyRݲj⺩Z3"5),nLbIzN0s(/k +d5Z Շc`C~R/L<`u/%G뾛KhWWWv91"i``RQ q2+=F ɕ9xd!;Ӫ.k%d/  օYDO)99g4C}3 DŽRx,u EK/]-H*>%V"ճ WY5s+P.I*rC n%"r H*t:W>=Z &Q ])Sm)"%F?hZԙLQb}x/aVʄ#Bi(igQWPlDCILbT5j{ا,@uB(R%E)80MVs^zei$Z:IAUK1&yK| YSH3@b|AhJX?)(9i/9JJr*iY&Sp;3PL$Q|&.I$ZBUZ PP<`uxxeXJ]Jl}6׮Y r[U"`q-%̄YW֜EƜd}ڢ =rqyOrØ&^58GXiNc E:_bNjf3ftb N1ϊ|%Ur`5ܓ -Pل~w448j5%L1Y;dUKPV: 0 cɜ1̮)@J#BSPK_MFp cQ<yn&4HjA0b V 2 d,nYsb=U K tSK*qb"e:I 0 NEQ0"cRtO5O6f֘rJlifAUN,d|eA*: όġFi iB19D򪐳B")0|%sU zV-{P5-z-xAUsVGgS)'l]4`L4Qſ%KYHEp䥃w AL*@bB^> Ph: Z9]4k /ׄ٫ i\/pfA&lzPX2'^%iiX,EUA܋g#E5X%KI*!NA+ Dp@@f;ނMf;;1u; FDzf8+HIShqUjɘcLemٲ{,HUR3`Hh9"&$cZ9Gj!9f G˞! ),Tg*Z}5S4L)@LfQoK)KlvZ5Ƨg'qh9Ej OTA0()7ﱎiMWʅNe(^X9 ҏ'.K,>OCŝ5" ,d)"a&$8{1 @;zdlfX3\2 AR3ڞ7RJ`@\Y1V,U`3>C4S9!A ӨfvbY;eA|9kuݙ}sbgA.W>cbbq{oh">ͬK^y7I1վ V(HIUN_Y)#=P(fNHZҴZGGhF5i/2Bj̜H 9P*HKЛ`ō¬wvG ܼaWjX5 :U?XAb=bn~ _Yp4J>q<WGz9&{Oa@g=15Yo1fT IݬRI+YX9%sF֠6J́ğ&'."FS *4lֹ=K1 (4"E DeꢰؙB.!/-Pn#LB]_W 9,u^K+`R6=k IDAT@T$7Iεh򰩊*67&b[UuJQ׫Lg㬅LWPfRQ3ĢEKP `YQ) cZl~F,@(KZ}#bX'{5!?cfw ?rT<# #pSNmEB]7]r:3웥'E /+ٔb @˂ҀZ1B:j۹ YoQt-سa!;w~_%΂KdhCtH)Y`dWP(T%'r*Dsaw"$>+{?rE"fWDeD.+ ABy.Ә \ִjMgAWzX+j ջ5Ͳ]ՁcKf  ,e1v/A-ҹ\c̕u$뜽*sys1OŸO rMědL1s%0#3˦d ;luFt+ qsl.F!{V q‘ W?#N)}*䚑7Rqq>h牯oK;Q ݉s4RsF||Ipv6qyK5/$(= rѾZa@$&.;RR h1r)r(JA2 Pg< Ӝh]t.1ϼƅ0`+jbWdbs*]Qa*9 3$ }5ЄcuK)eb\]EK ghATPݯqܩȌn_q (\dӄEaҕű]Z`${|#bs$Ŝ"cFIѪ5|s~ރ7NGDrV8&)x xOPWlåRt$ԡg&q/9ʯhՀF0$w8u-S`:9s)Qņ@3:T ޫ> eJHH^ uSbZH솣ce8Ve/+o]!en<]6A_R+ I'g\)]? 5HKr2F\|`T ER @H^Wj K "Rs;P. 6c\=M(qۈt3!V{>}!,ey"mGMNg%%ܶ[xy%cͫK-~>SB?PDUgw?ISp@rԜ9-D[-$)nEtYޓ\|ù$gYT$3 p`{yQW3#Јe)$D<$,47> )Ą@ea)j}0#(drwڄM0k-Ut:sqq-lJ>;LhNQ92b㎅0N^vy`0Ԭ"E,5 `ʸb?"w n$\9( yzMX+GRN׿K73OׯqTCX1b$ b<ZkR Lld Es=]H@>0F P) |aRb#ꂸ:s!`.Ons!~ ?c 3?(Qgu }6ncs5l$fx;OD3VnN<8&~ //vĜ,sw^j2=Z'bmHBC)qI3cƥZdITٙx5q",%И&k̜ApD rCs6"0xW΅1iLcr sٮZbD gCIYD9StL  qv} >s)q29 }n\.#gмV,gsFL]'ή1_ m6d18Dҗ 梈rwd1T h'a>Œ=bb9aPFֳEM$x,,÷r1@?J&4X ZS$ЗϔRCE(V,TLF)YA} {pVpk$9g@cG@ƫ%!C$jL{KR4n>.3'М^\4xbu}3fvv/A&lq6r+[Aѣ+3=BD2c>?^ɓ`QP\x}B`LS-Fjda>q/f2ff@@&G&Jnޱ"y0N)%}8 )nI1 C\w6#Y]CLq5v~W lPa/ XP8ҥѠ1i_ (HV%#Q 60KnMAzɎ')L\S.QQ))OVazkJܙsУOy Drb̶ xsF6򐖜Tt'Md>aIM='gq7 14v]ؓ *)_}R$,ĉ~ u~ǵs!Tw"Ne iմg2^ljd%$_d(V34-dB2osmlMXW31AL 7U뜡tWQc42|ϫ.yU"B~nZ~s)WO94 CțqGDhEvd&"mZSG \B<; "1uw T=X,yڤɗuٞ?JQrY9=}@ua,P]P; -bЉZy=- (`ԝf_+pцV `SIarEP14-4;."5lKn9!BRrDJ&JSƙ]J\]I 7D9aΊ<9ؙ(e:;]q=j ~aW_Eؠ-O@RO*q.;o[;Khlac?)A"(~ϰ,1t_yzzU.+ۥ5&GʼnVּ[A]hB5w]sZ?9J,/1i`E#(ijvÅq=] B("IV3 ~L0\0T) m80ZWvt[s U\28|4Yd zoHkRzw x$Z4 "~pK  ήC :TZ7PDX:yB%y$,#Χ`}!iBg̻$Ka{i 1!3m}@3O, tCʕdi6,մZ޽3 i܀z@ihjDm6\p ɌKυco?`w|WW_۷0e3z'n- 3l\0RW&2Ux)n7T'gik慬GeX̜$v.:7MeK1)4Ct0,΢La]H_o /yBOT8 qIe,^b}6Vȯ0xpSj:t}d4qKHkՕښ׾ IzeQ̦>x'>xWDezo̅<1Ϣ)TrTAL^[LiSզ^gSj7X謣+gKyj`'B@zӛ _ RK]Zl&*&]; Llߋ )x ֔.oЎ'r).6>$@_.(L`ӆ}@YuAPc9uL%I1,H±$Y,joMf붭t;Pv4c&_ȒqE>YbPVn>}Zx TQW^ +9& `' 9#\ +] 6 4s߷h5q"iGg6*QA&R9G\IX˯D9a!TA붓g`._v`4>"q6a$dA XVuMYHX "zEEׅH>ꐹ~?7B;lƸ Ex{!LP.gҬ"}N٨Ps,c89ŲPHE|X><1PDB>OÓ%aj@d3։ǖO/x c,/՚4f(nQXb mSJ.?}"',iEh㥊1.(H/wV;,#1qFg2Ql%:$1 ,sOYU>%xi^5i$ icj"K l@ N1!gސ"F05.ݪ_3׭VNy7.\)'l S)T]d}ƾ߸!dn;|0;10wXeqK'K4>]BKݐsv}Pm4} z9'fD0Ŷ`PKF}1h3fK1 ̑X>ljT H P8_P89{mcRrZIj#'9XlAsc՞Ӿ8QL}\$-q9' < zĚ6\h9X\̎p9YZ78&ZM>;mG6X|ԊNqTqLN~X7r ! nU) Gtp>,SFZ, puh6eIHEK$1=2.k &+61!Hch.E-/ƴgk YBRSX3Vcղ1q<!sF-YuzK Xа4M$__k&'+S}=lTJ6.4)~ߑKF+[oR& Kk;J L5:^^2@ 2eDDrGM]1vR8i#pB_<0j`)d2 oL1w^.S:Q\k?E,NLHev8P$V5{7pbS531߉wlJt] tB> nd\F|NpH^1?焥 (,X4;cρ}9#P"R|E0$;,@'htAO5ZQ؜\pCU|"흳e=C9W='XGS"uJSIOwG>!`iLLzw=d >P. Z?k+8za)we#̽8g|нqf*OkW _L3v}tc7qmuEVa;8~RpLXWH)05B.C&Qs{st*By#I&5F݊)!E )v|ebj'4{s,3>8変HuYLr}8ǭ oS9SFܾޮB AꗆhTVHx~~'ۮiLc^Ld&6)b]x"TK %s^t)BBq$W =F-p_28zwl[eN IDAT|ZqΝ萍u&)<\wIfJPeCiqR͚"wޕ4Q,jֺѹ8eC?\qT'L9>='03!XY͑Q800*kS0^\4CaQv$0 cH3:*ИŤd%wm+n]Gʉwv-qU82|`dz5|%:mu[;'|~m.Wp #H%/tkA,ts-j|]CBFgkEsٰɚ{55ULu#ͺ8Um XJt̬ ;hJmum|ܜn;y߰U=jkŵ+J9G(}!i!s%^m"#~q5CF'~}+y Eܑmp54K|[W Հ#qWHa d׫Y!jq4!gaHρ([b WsЃ +Y b%ت1J,tU4 )k3 *ruҎe#z1FC}IBc4&|xWRl\yHkdVA&*]9jXR*/>/a7*bV,M2oۥ&sQ8P/TIyHFtIӅLe ;ؼ,(@DK0]eg"/\Y 9/YJDO ;!$ o>|3] O0E1LkvPrE ?ɿ~UcNA38OKab$΢i0I8Q?}@} ~×_~  lh:Fk \`R>N`c7Ԕlx<Jyǁl(0Nz Lx'l fvMCx?|/pʫ~5w8D<;ȥHM04(F eH16"{oph]c(m mٚCN!t.%#gZʄL7tt05@7 [{ڱHMES.7, 8eXPOBf)e!> inAV³p>mug4 \2<'qۄedNYAg|, x_3w]] LJK`c|pghluԺ9ׁ(Dojkj!t \&%29XYr)Cχ./dR-KS"{^0o;sJAݐIĨ*d@Ph{Gdo,8"By1R:s"|>05^aBۆ|Z΃.!aᙕy|:|xO|_ſO4JL[R7l۶IgKY 8-c >G nqȗd2g혘!YyxX."T$9p &p\7p'ъdB7Nm AQ䌷ϟqoxZ]p&i5[sPS!='Zq.MA#,F\k$~6x~>}RQN|}>33p'fi'"03 SBf@JTt2͵hqt._nPKVOzœ| \N;SҘȩ) bky>QsFJ7wB /*,ǰ$9z$ a'"mvg Wlq="35 /A~'so7F4ov&A ކ -Ss\ ռv qG8#GBrVq莒+ގw7Md}M˭bGG bK9=.5ERbFݦy,UheWյ&R,xDҋ.ITY)  ƎdҊOVfQvR;e.,ŽJ/1־` /f8%i)c[G?:ZJCȕU!׸Kif\Klacw*; pD"K50K)T"dɅ6euTcX|YvVoy D;Ǐv{:u}G':^~Q}`wmȩ3_Ӈ/ÌֶDq_k[ 7* 2z?FR{q`6!w&{ΎǓr2Ť'ʻ]Y"S ehl a0pDEw?cFwJ6VM1°ƒLݵ@9\Lsr&yLDE۶aQxvBJ1wlw՚X9ЋGoRT/>|&JxFղcv'R)]G ۭ"F%g<%1 W Zi.ϖz?{E-4ɔh78jn*8OPdu"C{ 3R"x"d۶YюrFP9 nl1юV1`XNH)Dʶ14}%sW$cG#?IuP/ V+ZHu Xd$?1qW. i:>Ύѯ8+tJW fCى5r#CLiaK<,5IR5ғ/:ddŋ{G-r\E"%.?=*Nȑa7]u([{&jjQunm61,=G-܅/oްmj-(p88r틮ɰiWȄ}>P@¢FK*H\ϭJa8Ϸ:#$JC[|nw~ ,Ȏdz7! vI=\(;1Q$hGcsZ@hB>OH c :FfN%P}R\C>V2Gjg"B眵$}C cl[~ۯqGhǁ;ȋ*SX\l,z,kKUmJ&A3g_LaԴ DmR0%wT9|c'r#9 %ȋIɔ8Ͼ֛#s7ːB$IFW&o1~E~bN!#VoG1G8hZ$](g$AqpuK`)vErc )3VV,^L.V8,E9dÆ<.8H/EnFIuḾ2-kvzl\tw9N"6h&@bMd5"!XgPO~I[Sr;C2&wcN'II ,&~.tGr,.# TYH虀Ht8FxÀyJqo9%ǠD]vͅ m%smϓ$$.8&{@#(N6!tXV4b$T8j_v¿zALũ(bBf) 2|om.,?r=2> ~/o +pB2;'`6AGBIK2o8<$g71 xcxGo'GG{tyvN<&ioɀ^ X,0O;dTc B]dIvl/}t[[r7C$/3G1I(Vl}x}%펣FGY~S8N9[SU}q0xGli \] VrG8Rܜ=$ḪQ<Ds"[vPL@8`990PM!KY=5r놏x<ĮsǁX',Aܭ~<]=nsZ0\!TevNQv L(8X^R@4ɦ:4p >}XH\lj2lφC~x>6ٜTE=Sʦb 95^38޾EJd98lw{q$,yf > |pψ%eb5N8%%WbH$̦zOQ3!؋0ol\L.CJ<%A86җ(z!6{P<(.p_p:/cZG;- XA '2m: ןz!+tTpdK;Q2`]vBE. -VyiwP$(b1ivN N Lxg}HW+x>p8byco !ME4 eM5GC??#OtI~<'Nr Q ]hCxOD*:nclfmt5ɐ붺>n;=8xTh.-y>#]+\uۖ=4n‹ ;єuh5:HV[SfegZ(R zh奣OT|́]{JJfN~~R t`c 1sLi慬%uxǗ$rt t~WB\1Fn8I + >xRjNR]E DD$$ˏ&0W)MJBِ7'!Kud 6}Nh:kzxrQؤEfskk&Zm,3$:kFv}6w2hC3 a=5>cgdːlsxĸMxYpM"i[x8IZD_ӧx>O L3$a5u`˿ć/>P3mJ1$H p7a9d$sDL|9-(sUL} >qNo܀rAG&W`[TOIrq ?<;2w:5R2mDF"B|!o\C`Qt-[m=d{E];p]eL+A.NQo$4΢B L1Pw!䇎X&31'PxjR4& dA#'V󒐚Qpƈ4nH;Ddl ~l$㈢}ߑa۞gn9F\J9$@J9!Y?g9g< 7ȣ9cCm:ِ6!IyZjFRsW5N^4Sk~l;Ɣ+1mkYp 9+Po;R&E5`+X&aFjBTK}}S5qg ›ibY&C2Vf=9۵ТUÞ7@&ѱi[J村NAr69VǼXWؕVv2$VEźJq|صj$}T3~.SwXNku/-K^]kE/latLk~&Fsk})EuH܈LoϤTK~`"6`덣yL,ƚ; c6Gɏ2^gә&(k{r4Ӧ+-Xb*Av1kB-wʢR@;:WeKgqkwE pa 7Pb'XEcTd;w,-~\"%3uH3H͖bu=@ 4*ADJB>OTLc } #/2Yx_L3!,D LKuvds{п kǀ;\ag7첛wЖE8޻66f F\H;3)Lb^>Ɂ:h<qpL/a~8Ovh\V6  -JAv.Nz>*0И ^㡌Pfb5ph ^Mol(-^Ź8Prc |aɢ LH'{'QPJ!2GCJ03e.^^FH4mD 3Y)pq/A, U:(.9灲Ux_Ϥhk8-5}Z?_n q fCj-.e]/95ͶȊ{@43\\NlMթ}4£3/ԍ76(냚h# kIqq kնk(m_1}/א[a)"l\f6 Γ g IDATr6k#`oWɢAݕϥ,eXC0H.NF@ѽ r"5?yp2DDUqů1!|? ͆Ea9|DPsNay!(!CzPr7yr/O >.UO<;Zrvvvqc,;d]xG fn\ٴZ"PqوFOU8ސ*e]Mx`ZHTRݶܺʃNL!dt_hzr BEFS鷝m^ Cꅜ: \8p t_^mGC)Gm 7d,@lc5#F]X\o`B|r6\t K;^訩NʷR.2qJ<] XW]5U) aDNӒ#Çq^EsKE.O0bbgslzƂ2 $q"R\F3vlV4|fP63D fEI l1Po2:8M )U}a&}*`Dypy |{ߗ=Og‚ ʅ{ǟ|s< ረ%Sض"(7'>0nOXo\~=c:XUzd[]-,*¡ q,ZhN%Y\G9h\^NWw }1d#*>j ,??K_&`iͱl5[YPJ؉ &rv(ҥ  kz8!DB ]PIT;e[ojs#e1&-4>oUPJ:M){+c.XWp\% 8Y:@|d}D*+M`7ή4 !ΉNjEG*发 r*7&T3q^nA$/~D;n3,uGzHԿF%?1R2]%ԭւQ <1rB}0pD.+ČZj~88GR8r hh^JFwU:.d='UD u!5uxW29(A x>0}+!ct\+j'>7dCc1BNhftiK1Ckz~ӲP @ɯrŝ2\2RIkQ xELS!-Oɳϳ<m vCvH'&i1H z$SL9]IJP/~dB̩ Z{U4r{aIVw&=cH\ꎣk̰@VK0$URÉ+d]t L*Dskk,|Bsg#rgy6GGJ Fqsg7Βs ;2|)H h/ mF 8w؝ЎYDB^x{>is]<xr|!ԯNX1B6h`JDD^u%EUt!? íE6˱~TmktL'<Oq2꾭}3y4:^wՃJVOTHM2. T%3݈(9C6iQZ̔qYY6gj'p*"VZF'/8Wa6Ƹ<WKd`ko$܎)7F/ 7_6E *saR"b7FTFG XLRMMk\ӌ|bNٜ)%.ds1yBts-f,dclfw=2"!J IT V0V6x4}DϾ,a6C'R.C7ϠTkՒ!.kVpCp 6}yСQ&UAE%R;F.q06B&׷fB|\:F4)L8 ēȊ<]%t,#E9'L.!.]Q'.x 96hp'f s||<:z>P?nAFtⅢTts>QH16)Pz7hH*$DM$W5 8Mo'φ9_~ީN)q'QVȔIJ]I r&)Tu~0&(d90Q,OI@Md;.}+pLhoN,Pս XA,9/yz\Im[ Uwx-AiFh'@HJ4~U~uxCUF6`UH3'|}g,.o\ CxZqZ!陳Qp7q`\tre˞7|$JQb¯O`L}s}6_еZ%D)HUdh+ڱq"x/sPIKH6l`NS#1w(O#Df9i.Tb8-R)* 5ǁR*J8 ItXC"L&7Ђj`s<jd6:f¯M Su~TmH^1K͢0.?DZNr^h $ɟD!Ka%-ZjN(4jaoΆ5)Wo)RRz 8"×6Ä6ah>]œ/F=s\8_n^VO3֨Y.7 .5TȉӨrfJ)uxD&Ytr)-2LV&xNɍ'= „VeT&nd-qAH$r\~D&™B<(p&63s\9CDUQ(Ǡߊu)oZgV.J k T,:9TZΌ8NWsF;Ny,)-{vwX;9 wL2ep6cݣH3HZ,4Mm\D 8>Nyco7W0z_c<#ڂ}h'j^p>Ts*]f<$'J. "?[2\W W~ >}QKAIEeשR@d<F-c#338Lld2F@pAg߶ Ҋ2K_;ka dc9t|>AHPI8}`"{?S+G }4<ס d`sۋ\lj36kMϴ:B1q$b*h"uXs$V)2lbH>p?d6%`O௿"Q'gO? P ^|޵)f:\@6m쀤%>я'`1"hYgh͞ijkB7$v'f?uYĈ ӐJB-; h:OJF69=U&OQB\?Hc%?6\Òlj:AͳA.||Yf 8+ژ\g0tsNn[ZoHcŜs:K.E2&J@a1TIlJ›ೝ@vmoe<(M2zy~?$ ֺ 'Z`%{Nxy:F a7mE )=ctÑ0zCzF@rۯxqq<0n8ԄYVyvB.in;:5y%-J(;&9<(Kr`gB&|J3O, AzLLo'3Mps)(F̜@;`lJ,Yߔ@M*N4R3˾(ɣB tXǁN|ssnorY-ɣ$Q s.3"AUd\"QFr}~~[܊p]+]Фn*yنgus]qFط#̡I9\E(>_˺ЌP',BAUI}{ћy0l:LZ9|Iu'vOS?{Vٌ6zVaۭbbF& 8?y"QDy`>Op{1I_Le\2֌\"8 ~wfu$'z)EX0/(UÞ0,1\z71@'-4&1!ѱԗ%xJ@Qg3'1z >\#vߍiJ3ΜOf#a0t铅oȬ.\҄1=亶CfGC-E<&&RHNR>mZ;1'߉$yYE%WzaR)d ko|{0-s++ DJ`VpY@IYR_ELٵ_牬hsjMeo{Pi6pfJ ~C8(o#VELn?Q 1q̓P<\ݹ%FSޝc&ݐuf@P)EZ \D?'7 q}I4w6<;jx{y.SpAZ p4\Dd@=j[|FKEHOd 0g]%v ---3&9HͥZxQB~UAdg"/5C2,‰4FC5 `*&`H"hpA+?`d7S9!傭ҙk ڢZ&w/` LyA! W=6Ԝm-p뽉Y 8(٘$[;6qX)ȏd5K,v~)yvaY$&1L ,Q>IqCBooosxʜVPJ^Pco$r+$$rŐ \ ȏ$e-==ߪ- {R OĒj2|hϡ(‚H?8ZۦL1<bqRx03gW(y?>ւT{-!`>v7<,"D?NxjvRh~R^tZ!!欷M,2F uwt@ը.~zTp!U`MguFc`h}, (ɱ$f$>ӎ%G{w_0$\!Jn;|‹0"N@=#A/ ۍ֨hD,VTn/TTI&a~RiElǜHVց-O­C8WǔB|H!(٫5D)Lt]1 bW̉d7>۩gUNi #%0$[CG;\Y7csSc9'mɫ ֩b8J)8֜հ:ޔ9ۍSz3Nu9%$޻ sô.NQPo0Ky )TI39?&l$!ʞ8e8`ys/kxW맔pl LLH43 ?yB.P9WȶBwşH/!we>'t!cMAt {Nc<hH)ێ(MP?yq<|3dCυz'9<|فLYg9EE@79`cZr*k6g'sT- ׋.6%O$ ˆx OԒQK%_ޛx=GcՖ A})2/DYZ+7kFφs3umcnɋ3 \'SJSlJ,϶RF5 4)j=Zku[)aȸC ډ.!@?ӆps}6#lFa/8IORhTL ŪN&FkxOZھ2sY:>kAMżi٭dNe=\]EwBy5vb&̂vNS)ɐp\8U "drFtǢIs>*rI8g®bE2G_9_eQ*%8;YME33"P::[1ÙuXNB9C,'<ۓ9kQ&T.BA9m.$'xac;v5BC9їCkIXGy? σ`hyL)=IT 3`G_I"69"s>qQѝ:CDN\N >q(x8JbKbkN/AfހREWJN@<~,Y{ܶMFc7Pg [ GpXBJq(8F;U0q: g!6Pw{v24y ∤9G>N \C}=:o*ԸliH-TYp}ŞmCdA$˯;~g||Iurr܊>3j29pl)^.>ҩJ<Rv4nڶ yI4Y h8& mؒ{.rZtds>/)QrKE %mo'R-+YP_ lmB; QnRkp82"Qa&hie{xFhVm@ޫ2HC G^-< y{yS`ѻUEǖ/#0]$gk5)s`pE9!+w:gd\MYо`8f!gԔ 3]gI⾐28vϗ Rm80ĶP=޲lwLON$p@sr,R",H0;1%8"!*0ȺSo7<Ebs]Hy4ʝNA]Bc%sw >s~1mslb h5z 7zq5!BqٵI ;tV\Y\%.wJ+u]svǁm;06QLFa 'y/Ƌ!fDܖOr1 \ɹrݿR0dc+cb|3U ێ.%-sI y"bOsJe{G8Ӎflo3*Rɣ<?0с*XH pbݩΟFAL(iYq?7-'.\̗rL3^4Hnp:csh^D$Bnurƺz G>N ]ME^[_uyM}Qi;9 ~.NWuY3Xkj9F%muX× %vu׿fhʭgv2 }];4!IJ< " A9Uz%+9n$ "x=y-@c[ՑB suFB`t,\2bQ䲂L n~[ɯXEs9n>McaH7E(D/z+/f~V"DLK(^/+1I\` =M<E/%2V$ԹRJƸm;VmUj.jW^e8r 4WnDPudhc7D,y䗐 X ȏ&" !zIU*a:S0*LF)nC B52ziIa@YnuluC 20EhmHڻweycqi~Yu1!8)c8wAfƉ8גe6 5Ǒ4/н+iz" 9+|45 b䲡"!.4Ky-H)o m"%KK7Üm͓X^_ tYVހ_ȡɏyO~-.rE4"{:K#̓b:K%yb@y"|eNI0Ô00)v qhn7*XgI(4F{57}YKʫӥ;{[w`$2G9Í<;#[q>+>Z_QL ~ r5GR17dy{Ķ WRTw|)4!P纖4Z T[|&B@1b}ʄKp$gLl"oeT$[m j%3N98+uZm/;U@YP1(t~Ԅ]Hi׉j0  ':ˬq:R.)sܢZ+ 8,>,y 5)xB-og36GP3+eBxBxvu}yܕ`?\kb  z]ScDawQ8-{cb,?|c &Zr$MXQjwT.r%75@ +qR7sYBf2jZqȕ]*-ayAD~N_6*\\Blq40"}?ac26ޔK]`36g>cۨsIJn7)wԍ?dq,T8{)q9c 5OsvLB9RxGPRTUiutˈρؾForJd;0;Gq}G 6f윆S~$wdq,qomcJcy\hfru_&Cf ^Sj7_KX\P5}+$ t[T./8K4c|Z|k=jcd9e,+OE$ӄ$BaPb@o72)D\Eљ<`$: E>4e/ktxi+x";ή>,J֮Ɣ->@RwQc(/2!{6a)qTU*U(YtE O* T9#X tepM>yvb}jR0Apgmۑ"o_S2 5u@>fGVao CQ>3 \2I4 N;!CdJ gks?^ZA|D=i:8%gd խ9d$Bӱ6g !( qP2h(˂OëV#RȐΊ&SyN,nJq.=p"ۛ.jx ^GN8#]pR!Č lV<>mgPyӟpthZHן~D d,Xs,Z6Ldf'0'r[Ծ@]9*M}N:AS'+D!~$ ,HsRW=18V !=}KyH8cst]eDuf,p[Et) '0r+bLgv?\&dqN]"\tsRB ;,;JtM~oxxe^Ɂ}:;YԶ@eTL{X;0jKR9ᔯl>;GF<*:h(;]k0wwK+-U9WrِO<n7*eZTi,_ :0Ԅy%u]b:eț(‚eڑClwœׄ\,hl]7|*;a?mw a`Xy]rxȠQem~zƵ8qTan"']cIuile6O5,P19Dſ@ / P5T@| us~Ai;:bjsKMͬQQ45HicC~ּp^~pٖ*]N0a9'kd-ȠÉftPvI~d0Бen<ȆRbW}Ol"$Ө&;g$O83 yѸ sSP5魩C{ pï;R*8ۉl=b~[䍡:641&<<{pƜzɕ`ч3"OHf02$v)"Eʏ9\-uO(shZ yH> v):3d[rA{qs" &%5hl u1,_إ0=4$Ev]9I  9н=.8>%D;e2QMY{ْ*)Fag|8jnomySC@U`a5$gF<, o;Σ-u̱dԒ1cqөb2SFGˢ:?U!4Kkf>:r)nBP^Oj8V))4 47-9&3HÀvݑ>q?N8,L^f^BdiБ/-i8H! ^sa$/ !L }& IDATIU>VHtxIa#8V#vA"I p#.簆fքaT7-H9V 9jdo]~҂lk&iUiA:1Th`$lTD1,$CqʆfX|Ms$ zLk~֟O|qhIdh."bض;c&, 9:l9W<NSfDI##MM3i K9RC*'Pg0sNHBeb%߶MD0sLn+ψ\*i))4H ~D@Y!Hu6`зuEfUg5dRD?Ae/`jsUR]3/uۅ48ˁOԒxf\*FQ@Y +}M,M$r!qL!&}oL}TJ 5u}퍳|]y5#٥jҹ¨qzBV?~}5tVߚ܃Uty K2DV ![>5t30/ !{Ov!?$Ivd e f$ΕÑ&S]?ٍ ]HѸD=!V1 ])vkٱo;Jaչ+L˧a+ETg 9"'v43뽣ƎѴ „ o'⻑h90$"Tnp4?BwC: mtDo9C0@3 E}rnє7mKzzV+1Zy@ GTHb*m28m4~bDJ (Rd/^0g[H %o.yGewB>چaBBd)&1y 6ٱ*@꼇= G^ sKQߊZЮcA-_9٪`]$1eSspl=sfSFƅ[k3}}@fYƾWqľDNt/etXnP8 a\QAHD"gsraNdhs흿a쒕,C)3̐9|1}%{i]hh$c@ˤ/`^#9c.%O ?Vs|i8g; < Kx8|Wf_/I j*;d+Ғ! LΈ[VeARriAM,fBʦ"AD I"`n,܍䮺`ҍ(S=ϘMkjC{y?#/-);v"ɹ$!>eE! S眊Lfw$ɼ`IbP)D}a. hrg\cnn,"9!Q$.]Vwmn71FBl+DDRFJL%@恷[v/R_l+SRm+ 4Qъ/YW30N I0VJȬ&sf&vpf<1J`>1牏/_7Vv:$hiKД" .wEA,$ͩЅBSʒe=S[?h>QR;Ŭ lJ2(Ml ,Q k\y7dɠN!% SѴh̛j!D8'p">'ŜBٵ|(77+⮈_.jW0;[PkӂzUɍxH;yrvMuNIMoJLpl`|$CdMyn ; e(#qZ.JFəHhjW6_>&T!@RΊ)SE)qv('D(hP: P񱵎&㘱n8xS!9]X]h8~4q n )ѿH?}ӆkԚ:^?σ I#8)0K:'RnȖ0ڧHrZ{<`"9Z;pAK ~Hwǩ3~k,sDtkKj%y<$RIHshyH9?kqs ؗlNkmՒ%ϥ`mͨX(= I0=:ɵ8a(\]"X<>O*ݸPÆ:׬ a'6Nhg 9NFg;jy/C >¾C-usL,xZJ\dS9(7ZfEF,lq:cwZNy[d0\ ԩ5~pc6f'!3@J 6*gLDc wqYRc" : )yK׿Iv 2sw| P,mFk K~`srN|trC\ oDBVqCxD T}}XaSY>bȨ 6n{7{1Ce1eVQtˁ}6gݮ N sEy;~6mYgJTRQ'P*IÖ !#K@ Qw\m 3A o;IBӌ`S^ W`( Խʯ4'~ְm7DpL\}vABT 2[0Öy c%GJv9TPvaS[r9$@5 jv[T%ϗp'8Jɐd5r{V3 /WȕZ*d㾡,)ltddx4cX`l8,.O:5jHP7(ra3L .Jn/[);5+97)A˓!$ʼndD>xr#?|mY1A:@hk$}AD 9㼚抭wɜ0_:J#VsM/7Kjs-C"CHZ*9 H^;_<ȹ:?.4 EWo xxDoU߭l;QBK;6]q)FWhĈd3'}g/H@b\ɢMB^tx&nuk %  /x##0/F#,<y5쾿k+O+<lhDG9<*C`FČ$hJYYtFp$aB䈻9'BT >Q@MT lʌAu\p][6(X7S YԄh:ҤAas)阞 4 0-"t. wdTNct-IV^N+2uGUU@cP8RUhf q >gi@]s%`IM@_!=Z)e 1HP[l1Ry4h"W衑+~7o|{G$1=-t.#kDJ} /얨V3d3<'q]K cdB@%oKJPغ^D#Gk2Ꝑ+*_sĤwƻv;V~;ջC`&$&91sN܎?LC\\ZFQ Rp] 0M:aX9^2ٚ~ĥ3/*Hw9cx#(C}Ngb~Ot8~ABElvn'ptS#Xz6Vǎ%d&ed]z9ulMHh,I01L4f(N#T#ðmϧ[ K׏wG=^h C+|lILZw/ioʳ`N#97 $J@[<ƻ0 S p y@q7p$h@0+ΫQk\dF;Isθ'p!CPS^  &RD܉=^Z3ؠJΕqzk)qͩ5]s͎rx$gUGnhAѵp1 IDATeLN8DȁP* BL6X\_C#1[#Eňjãp3(cLrwdEdrZaE'Ta=_:7\BkmA1[Bn#XtK);-aGxن]숪=8(J]ޜAJ~>$`ɑ5|lA# -ZCɞ7&jI$#}ZGN[ ӕh3qf'`\,獮9 v5$9fj4"x<}qlx<<j^|yIW}W_ͽ]2=еRxỷJ2G`dł~-oZu䤄\%|@쨳K,xoY>({ Q'Zf#6lSkБqD(}6Z'& `] 9 P/B^cv2hA=w!+}HȶMuvdezbo^Y3H[eOk{%o S:V%-N\f6*N8S3n0L D\pZ톟~زH+ 3D J sZ|KhE`&_A ^af8m9š .l 1֩kd}7\K^ƨlN"`bt(X?+^<%Sޝ4kʨ ád 8]|ؑ Dqw"S=,FTD}MPo^9EpsqsJOCik܆:mybcJF.NNU׎X:j-~?4lFqq4D0YNN"7PSA*,4hgve$9p!lny(H4Mb,0SE~`K :xij. Ժc.۶DDZyAjԐJus>wЛ횸, 4wmCqL۞u Ÿ]^'7;cPE $":YǾNBTfh3@\=9P<8g$ bqҘt8~MN/G9qMv֭ygx(%7O7Q=N/(L803!IaY// /|~}p8GR/8N'$Uy x>hD=%xb!]uZxΙd.ԑKBl>P02HG,_;u 궣a8p)%g^lzm%snZG 3U&seʂktDLzB. Ղ#l}|UȲ/gKb[h\mѲᆺct͜uwq%P. ֆkZv]EGR%cvZã<;dN.dᐾL@8)bջY`yepsAbG=p;nHcDͅ#@W$,zڅ谧# <0Ľ˕X{m*RrC\53rcՌ1׳8A]Q)Hf#h8kqBR%6ṭyĐ1~nѠ!d_ HU<*9^>6 DD+jXPtF:QLy*Lm) !q;0${ ˞TYG%trxiqӺ$u66eBLHr2 vbβ/8F ^;uN \J p Ķ(R=*RJxOB.CeCBX$8[.w߫KHiA QPu~x1&>J9m2)99F@0@',~3T7Ifc<&F7FT=3YQ9җJM]H'W :"pb:'41H qhRDeArN`*a9Ugv11(TUTP%1$)]m«B4@!!{H~mpDCcKPD\1FM o&^τ-3Iwba8Gέ(6'yudfHqll~$5/WTM@&Yf~wg;'(5<@[':8=b!a3^9ELʈfgI48"Tj0o^N܏G]"L{hAupK&~ߚȊp1 _ky*(<Ӕh'߇sQ6&:(BG̰n@2aQڦC)ey2X:j P"(/@u 4GUq(;q':)iއOlξ#taJVZ^ /[$d'sBt/bN~V YyŌuvaCYcA'CFS"$ExAS`v||Wa>X3<>EHx xiЕ0v0ewo1Cf{ 39!/ "Jԑ^QH^?:L8gTs ԭ@o3'?B(޵VNjEM~syĹ5$v\q T@1 Ӱ I8"ΟIH%/f:ј*:HnT@Х9#׌9hM0h c4V:ٝ{[D=S" `+ ˻o~䶾nHdcK x{{UKId-h!$Jbƙ7gQDE-oDAJ$| ^.NDm+kX-P]N]#(^tC4"|.UxvW&%܂v#$It $yB{!8hW2Dbb''ގ7?{ri tt0@La2\5 ݖwX& R٪W urX,p(8A2':eG'O,k>:#9g0Ͼe:9$SF Q%h#尗N)2> v[o67 QcA}4oo7v6q!caK@QЦ|>S b: m=/lcq 3^}u몊'Knlu&d$\)2~<;ZohFPÞxo¸뢟CA"91Jrо> 0AV`N>q L83bɞm2 *11;6;򖐕䆎T@vٸ" ea`0T} WcvNs<еvwǷFL,b[k;Hh&:Enciv~'$n1Vʆ]u~}C҆RM$n]ȉs᪭2 e/l7qXU){ҙ(M!!e|>xt"" yF2&  䙈cc4H5<$aA"a1.I#j)*=Ag|( PJ*J* }N1:&,Ha\ ϟ=pqsg[=;v Ht "]Rт=^  ɨn;CSZD s#SxG)C Xx糌>\7^8](~M#-W@*[A`fk'..?HGpsH0 9&XP$h;9F8%MljFn3Q~1V7 ."Y* ͉J_nNH~"ez蘨!i̬:0ÆF˟;1g^*)4s).z.8Sx6hP{b!fܝ vBg/itv %PJE|'ZNy]y OE' |Iœc>C)(q Fp@ 2f#_Qs甡r 7  Qv<޿"(F+s][-8sIj-NDMB[&݁|!2.ZD :lsH E3#w~vRX m7Ѫq稣4)9ݗa/]+I0{z0GG.#() RRTKTc0`k676[귭?k 8 2 rzux!B3{ސ@q)e'` ){IvO'D 1CdlzgŭI)c$?繭'̸&Q,@8TY Tx^F@<^JB?ܸVLd eý%EB$(9O$-d86OHr 6YdG*i_~~T IɈ.~#ȥ1 aAڧ*ث1_ؤĩQ)ՁC:' 9' G2w \*J>K-geKòW$u;ۘ ꦗ~ikt(!U͙%H+rR}{`0Ì u$,\2E Dv6<'JٰoNJ,ǘضcWv5w9$r<*Ovd-\''(hlvRNXP*J  9sLF #1LPo9tΆal1{^ 0bkNB|TB3%oacz>ɹ)B>2P񠣂 lEl>N{+#uNe(o<'nqlT9֙F÷y׎U KHMpl5im8ހI2b s7 |phNߎX2Ȍ?9Jqx|{wzZ̀fF7${8P$ Hv|8#DRE:; 9/މaLmG{QPg@NJ :qoqE:p.vy:Zw9Me8PaJV8HYE'9=[ANty '8wfΉ+u/$p\۵釁.'֜1 QE7_ cL"zlaK3H E-a}v|?rqJ '۫w>L;0"!pvhπZ)#LXWzY]ya TbԜ0[ʪɱfR\<^J+  A;\J`:Oq_#_p|w@x(2 6 ɦ]Da؏0i/n* T?ۊ99Gn >}ڇwlF8TBjgncT\*4© qx>؏ ۾cLluG׶N}';_xؓI,#CrƇφ?/'|:E,LbQ&mMXEkKz! XJU_v IDATQ0/%)cқv ){2t"sˡ~brFJ(G1Qȥ~}v[0S0#X%:' !4sD^$.٘)" gƏ؈EO/(fzk,O>p GxV D8 "k$y6;\?#-j \__Tɐ?OlnT|HaF3ڤwx{FDfwr}TX*`ɢ: +RJ*d^^2sVmd5P/~;ۯ6/w+fߗ2 ;}]2 d0_puqq Eױ2뒀˜(?x$$^Ƀy[{)F,N9,J\V83FEhbc~F{zdLK! Y&%Xmvb6LGR̥o4_c.h4 (J_QRֲWZꠗ별̖ZtB(7MKq? KY ƱJϹ j@$ԓ*>K{a:Je}_AwcZ~=H|Pu "*>ƞJvh!a"k9m`8ka)5!qpnn.7 ʫ)dd@L'18D/for&cƼFv!bl/lZ@pjl% dlҨjeYߴH,e%/?Ƕ$v!!i1}(Q? _ 1Gpe}<.vQvYW ɥcupLNtɳ|uu\1H16ڀ Gވ K5C YsABU`:H.j W s ̃]j21W-ǁ,/֊ uu'0D?9~->>}MgN)T:_rY!cZ+K17f{v؏#]D0HЮ}GIË~]e]):;r.uC}7$׹3'HZ%%܈ [!IvIbis7"r0St3UT3'${nFWu_ȄzQr$D6j0Wc/,*٠!?Nّx}$KRv:C=H?NjgD(MB۶f_ctԼdP!qc MbmzTDdvTQBdO6l}'OT 8cCف 8}_蒨#[c;vG ~B J&QJf Q0z[qmf9ɖH. r_~#[u=u&) _?nFiMXUMuWц9 P}iAUmT`*F 0dqe[۪du 5asKٮ1/ϣu'_$"cwus1#)SVDk:,_匉1k{כh&'FAr %r/Q0W~}5ho :&FHd+v@#3Õ˫7ܙ0Cb4Q*7I jJ,iPcw4-U2M= dN;5\]eiP}4Ý2}AE2? NꊳUI"|>\G!T]ѯ e-nS}$AHCJ B2e;P(gSђ1uS>l!8 Ju6&44 9a\f X# *R&8W12f5G']8M%lťz]u |l}'L+&``s6iMI2Z} gu5SFWkB J FWa9+H֦:U p5X@\6K;/H.?9i![حk25rHޱ%g,GǾӪQwNe(|~)}8oJF7V Sy"gBp u (cWa9 >`WB-#qzGJMԽCRJ @#6.3H,"m+~ }ߪFwٜ Gjp;@٩*(-ֹ^hwLq0M +[V (q?ȟGNԺqGP aJ?9,]/[(xxLa1Cf mkƽ@52`$%0Gs)%t>ܔ=2^fdy8U"<.󳛿0:3bh>ޮ;נ@GG? ԦF;*G TzT"Wd[Ƥ<~NL/VQrZjEF'~WrqTpw96ciki>DsہeC&vvv?ldžS/vOHE1;|J&gH(ϯ I Js%n )s͔pa/9l}B`jnu Ck.zgS7~ \¹X$I)$ln2['6(W|C=<9:1k3"9C&VCB7`I%%wUV,gx_%=u\p5 t%q9paZ*gCݝ]ɵhWG81Tˎl^s ? s*=Fj<[PڣcR\.B,F=_W8Đ1&IhViR\Da:*,C (A ]ʖ?Ɨ/_ ${4Uڐ v  G]=~`LIdc`6B^kG\ 0f"x3@Nd)(`U]SBl_npnn#WNضȘӻuX2&xo훊΅\ .KŚW!EՒ](_WJ4 R) t>'}[3hª,r"CmBA5;14*S"3O@'438j D_'eo]۠Nt*l+*VkACv_H\,~;uϹ( .[ŴF'n> l " (͐))1 eƀ_lӑUqE qIϟ5>G00&P\}LHi+fXdlPxXP1lo>(>73JCBet$6U7^ꮒa sDZ$\I r)#ً,)Wd'@G%">OEe,.%UOrŕh"HZ@k 烖u˸0RI>B}G4>/?I0\kUQ5ʎ,(r 6t[R#$!Y1o9"l8Kmꄻ8|WҒg/·!~m# 6-P~lNkض}ujvDU$*2I ! mn݉[F H{%̵ 1r!WO*iuy/zuzV+F uwF`*vR!FsHEhy dSsED:Qֳ%TЮ ׌=OnDhmL:=Lp>Nvs(L :|m;X@ɵuZC= M%?˞ PE!Сh iFxn}EGc/fzI9g0ch6xsNJ0 x"M5$Ds3٩HBGHE ~x*'Ot~T ֽPJN֝(xG;,%KCp'Qc\(RVP =gt1$}83[/89Lw7L(NP41}w@ f V5 zkض#z*,5`Z0 S/@3߾]>;$'uCrBp׊C$nc1` zwbs⅐;CsɲK) fG/5 "C]Ÿ .5谗 [{Oe򬉿X_nYܮO-,M1ac L!S,\ 3`hgz0  ILhF,)Iv20ew`ǎ9ɝQ`Pk&~=_Y U[[ TdŕS"e9J??3R b.M S"i.vdra%dqBRV:W/_rYw|s'&nۆcQsF oW O$%栙o2^qS:/݄&ݻ}xA^Jz?(oڶmp$ˆ$$ZH,&c9ў}R.xy9|^j8"_퀲<*p* o_6o}!ɖ#LD#a3a~ bg0 $;)5Gp=7^7vU}_IYI덅N *Oh>OѰd\ 8!+B?6/4cvkι(oQbKY9)|v d&;kvt@v2ZW;Gu@F%ľUs}ݍa}qO6-SOBrq{]/TGg.ѝg@)ȳxxS8GR*-/q[GwrB9WԽ؁mP!MĘgo0wʆ 4kƌ4Q`v@Z՝~{L]9k{~&]|tC-oTDTY˶TvLZ~ fC~I?SNkS%onEN a>8b U& k$T%uo!bt'¸$ıT%N ){T8}\aSㄺ0I@XPQ}'okYc3}7Nã#Ax,>MRa* 3_Z-?gҪۚf [ksKeR҆` *6]#*{Mlذ-TAPIJQ*4Ȍȓ7|o6x޹Ffp8{5{y \.ٳ-WBsRWc>ReqDQQRن9 #r+B2sy/" ΣÁed%\ gɏ'ûw]|+0^y1Yfۡ=NHeH豫Z;w#jr@"c> ݢc̊8B$J6@.IFcx!DL;S.h2r/B Pw7{UAn>LLڝO(1$ejŻ~&' m-qN}b`5ٓvt33T (Gp8\=Vw\c.Y1 3[$L'R̠eF: ?~ #e:0!̈Y'׊j||y?v[4M t'oxμ{AiΉzsqCA P*%+ kHp'lL,CYL2LubBJB b҉,V {=WB6 W6`t^cpłKFoawS"P߷|nI2wko8!ڟXDX5`ab 8o`Ow0Ksf[0 &tW&w+p_H&'IMv(|0!k0`yKH%Ey99"8}P|ɧF|E ̅IB0[m7˘~%Mι=0Ga&}l1.'HOS]6x! eYɇg;#LI| %enk]Ȕph4$Ex EE6"SK<ɊF8H}j>%QcLIp"n|4MAV S芛 :D[ҧSL m\-8ı([ae> Hi.τ!YB<z*MJ\Bו)(mJPn `E6&ǎ-†9'[FG4YvdԥrSٻ4wVK˽Vt!eCXJm|zXiy+houhsxnN,$d |W @v@f̺5Ē?wvshc'+S 6YR@CN\"yNΓSVc\Q:Q?{5Ni(M3b ht> is`#4;ʝ㯫8/Q^)<+ `W0G!'T ~Vdkm8]pM qaNrB%{R+8GECˎb h?g[yz3&;!wN );*K^FNqk.ƍ)/&]PRBwZyrɘȜY)̻\樼 :y13> , # 4;kU5; r08a&GρOc'3 4h1Pω֨s,(a4 ZA8A>6֦$ߥ ӧπn8 K Y1]D)T]S;;FH1@Qֻ!-:ffLpϞyB[0 N]=5pg0Pa*/ycWhRV1-&?km<,: UVD=Də)jXy+G&a NlqrLFˀ942Kh2TKĔLFnw6[:^zE0 $1,q/;@n@_Bn S.s-qyHrŀ+.yy 8ׯ^1 Cb]D}m &hV5+Ў|:}!(h?(ͥIOCc3)DݫO8#{T ||uOOӵ!gy]H)II쪖HrLla>vһ7tv .{ زmUWw&=Uڒ cCytk@nMQIl;rgAMZK:oН̩yTN4ǮǖV&0D;*>y)~?Am4O,kQ{-軏 rkS{=m },[QE0Cx~#DT7-alZwAn*8UiƄԣ2a}0Ȫ> ٦LrtŻyАСVieYw&j ̚*%ZYz6zeע D|Cl!ufWiK,hհ6Kc?"ȭ5Fr!a76cߙ71 ^5fIXE821Vڪ52#ԮOZ6bmƃ3'"|S#9 TxވD䫷j|vnc1Y.4"eoo4QM{rlXT~GJepBDUfŘ /&ƽ$BkcyuC.:ľݨ on?B)UYPkQWFth"1[}Ddc59fĔr:ۡ:g0HU\.c.Bes|F#CJc4vl$ 9}""Ⱥo|~AB-Y ]f'dv0lzԐLN.;a.>A҆qCc2$"PV&YHGΛH6)K tG3{>4/.MΓ\hP6([HcBַqa{!*׬ 8gN~sun/ftv89Wyot&B| X7#Vug[pkmL&>-sKupjc0$L;(L?8Նn̑bF$& kv G'E̓)%*oZ%j4Ir䥃J*[r*l{JF9S&o_#JdfiK%T8CBCh vBJS WP4΍ j@5ӧbTu~3$k ӎA ӥO>>d\|SQ{cq+MPgȶ۶EbKFR!ˬ2@`ӚJ1S!RFRĠ DRa1~P):m]A]b͛e:yNk'tW |d{r=p0!t 1:I*vcԛF_̏Dk!dDin3᚛(X|gw2I\TgքJ x) isyhyV>\N.s S\1 ` ar$Ն8$7,灥P2ܶ;G9 @rllCΟW?K?bbX.8V}UP#SSZl.;}; 4Ψ)Ȏ}whӛ/,P w ғgܛɩBMZP_1*UO!K" D |7[]3`e_|Ci[eLɎ. KDD~|?yDo :)a}>L:46 B p܃J'?7;+dULݜ ۖ0FY;Dk9PM@$Hn0!DzVAԄI(y>7ZF2L鱟-MCνν! @}Ĩ+z8r,27 bw#̯"1J[=FCtL͑x[jCqI.ߋ蜍Z$VwSLkNs}icaE`4^|YAw3AWӤ O"@=*d(M" w BF$co@#׫ؓ S?`vwY1"<tMqM >8QmL4"ӝ1i5Y lHИ+27{̉p^ùl]S@^.^Q.NxyҭpLJ2('47!y~"И9ޝ { l\Dj<BE[ʴ1UBS~h`+̬B1 u([M!i"ΦHt"{ωMVO55`ʿ({{kuZ2rֳj pb°66HPI\&~h]Fxؖ׽\\&J}L\ jS(a1Do.*&YF˹V hYp!%J:'qv9*G3 IOYŊ2/#Y]j ` 7aK/_D}T$ g!|_}R !&VMXP9 !2q̊VrMӘp[#9׎EO2S)L)l[ŁM঺72J)a̎>ȝݙavOtkRLT (Ƅb"¤/@=B7A%O=;l쏃|YqǙVOe-`*`D7^CtWT2Bw<bEJv91}{΄91.b ?-cH+ӁPFV@nG0S &jKfZk6Dl<2n vٵ$ncݒ;埝h$!zV!KHc۠ц+4f B@ ;y`R,vI:'H\I$@C6w c',讟o  pe xs2)Jt]ccH;"jEqgC il`0,p9?9kșĮZfИ}%/46m92 [XuӾ9QE8 AԌȇGc&ߠU[ ~GY:=RO-~~˿`տ.;q)aU-rYD)ex봗^۹ ^)fL#CX$Xq 5@0dԡ9dSGM9\sb]n0CDPہwIҜ,*Jr&9p w'ؚMe녔?od.r qNǂfqN.ȱ?b ߞ;$~_~g0cBcyH Jq\)J#tix~wvˈaT8]Y@rN6$帄,[00gC賣Nr6>$DH6+B2B6xҺomTq~<Cg+O?OOO1<!2+;3T;z%D^&gGj8Íϳ-nz_utxD=XJB*EᏮx(dߊLɏӬb/ZwgG={^sm5rRG=AD`>jq TN <=7&*q;hup$e3սƢ (Q&Fr8&E9*I4DqA`󔒺t5>b66rW:0*̟Y\Q!s1L4;+'f5fh'8/eα>ckD <%؋.042)hL~xɋ'gB%kFW'B9bt"YL f㫯?j g__ۖEK~d|yt;S!69Hy3K 8d5c̉M̺sI!M"y vh++ˠ#섀6phi cUCJEZsJyɠ_Ѵ./`hLxxYHbabe=?!DF2J%YI΀x{4GG?.O?öm]1ZH1ܼ4g&&,Nʆ>#n<6 @ u3#xXM~s9T)Ѱ>,Ja.S* b6SɒCƾoc0/_`HӒ~U?)*8|Vy8y/^^mぁvQwst)\xRId 6a>lƵ"G>{ɡ)$x`[h(J׺s={}26`Bl"Q}͠o7 G "&( gN9e Iݷ~TH.6x9GGBhDoG3}j5D6%F(%!1-l \ˌgRljc}#_$t,;0FF$щEqR E}&yweA|e%y51@T7X !30^lpsG e;l9{js8AK|c =A'ӑ/y{@bĿDqq3PraXw7/D8+s$6de\M7pB$,C|}kN/,21,g윗9f*>? mx]D2!6}M|n ЊCx9qs5dHض HXD)߷w8.bp5ł|11o"&# c4`9՗_#̀ J.xsG?cn-2_;]|` PBh`(x@ 0hW%z+N`:ls蛒VkKJΝW'bk9$[IpnK䄉S`1)=Pl}Kň2 2*psDC ǠM eh7б#CO,?ͻ#@d6%Qݔtp)km>Rpm4Ô˜$٘q6vviқ ؕmLFUg hSBYe$,2rQHGrS.ڙ ysV4qP"aNz7&;oeφnǁcCBєdI$B{4,r+؅ֳ.UrD!aLSŋA 1N[G IDAT@IE_ɑqQ 80ŭ.h4(Ϗc\2ƯhjWpps6(s];ɃᚒG?OׯZ o'oTIt!bΦ9&r傏p)x,$1k qa2Ɯx=v;1nk~(Û]F4b@7}9<6V?ZiD!>c!%8\'dBcQe?<8]cN\(\˜tg%#+UVQ*SYȅS; $Ns`RxvɈ^_Q9s."@?(;u!'4ڍ d)>s$Ή)#;$$E!ElrMH2QԽuhFGQ&$679 [hYbKMjJYcxqQ1,(\NĤb8-a zs,R9 mGjBfC'PGCSA2pgXoMg_4^wk_0Z[a>q)9X;PįCv|C Lvs) kcdNI&5ܹd5f@( HQ5^}Ez^RU Us "T7r#oa9eS/Oea|rst#HfD kˇW|N {gI٘4FiXaSN aS5#R\Md7uØ,*Fh'Q'ȯCΝ5 Z)Qq͒*]ئ]MYJ 1E}.IYiH$P 1a]raR . c`XXW?Z:1\l|5:C)yۚw̾(:)G0f4c:gMJ,FvpҺ?:[RU0;S*l"s~'\S EfNbl8Fv< uvu;% KCgdV3tI$ 2U69c]fCR%޲ќpI5kK7< 2MqßS(U%'bSyG/>\ J{~ƶc#>Ewn7i8+'lVOuxT< h<0qOpEwv漫b(hfmD]-HK2SrғŃ>:A EpQ$Kz%?G@De±EnwX^}|zAuNNo)KaOɾ@v3r3αi; 6pωIݪhZa5ʙrysI%3sb #}WoE*IhK@HCEshT'žu]R?P!:7"A{Lԝ:!e%Ɋm΋(n#\QvfDY)'U8>/s+dXH9{#nePaJeu!EwJFp4WVyS4yfauNsȓKSSGI{D *RB]_yY+t?_c=ED 1 Qb~??o~ _w(sj3{XLhii^1aNVzJ!6^)eEO; șCs~&V9rs&φ}ڼM-p!,4/2R&Fdv.r?s8:APcG26?[N3^ovv+77RL8kŶ-xH]2p?[^2Ƿ|p_R_tpsmYX"d"JΊGEխsJJT}o\w&=`Ű#[ 4)5~Ϩ˜];&l`G}4e1]պ i"tx:w"sDD2!&4BPy]>fcZJDuKڜg E*%wv :aJoݍ(N޻֞`:NçϟϾǯ_[B!OFE<=O082'x:;|&y\*(cٴ]T3t3!^QE3,[eJ!Fvsvsi]RʺS @Cx%SHEE/N25 2굪[g 4ƞ"T:"`+A!ƀI.g*9tivX @`/FsBȶք_#ltByeƌQ:oItrxp׷*7~}t ^Cx~z[52h2ݴLA2R_7^l0L>:i=GHiRgd Bѕ$!\4QB=1I!U )p @dhmM6 YJVTY*4&$^vrkɓFU8yj8j8osdYhe[gc5{;uN8 %erJ0k~_5ԤsD IN20#cm2' u< $< ΤO%h@(7D7Ȏ",Tbcp" `6@m}4@D]NZR S2hsGYy1e#hT0B48wI}5ctzI*M%֖f^\g=0@L̷1a,i)2^_/|{[/~AVP^ OhC!aR(&v DKXOʦLN6P_}ӼgOA$ MrN΄,ѾaK*oư6ДUknc Ȅ@[x}j<2tk7"Ls)$> 4`ϻ::禫 ޽[# HHTfKp4,G)陒]n12 ԫ܅ONp!&0-{`!L+qo[l[O:]8zʀie|HI\Vb1aU%vXgȷtoCǎv6u}=??42*Q`3Z*[Ǿ"B7SFʾq }SlEJݦz׻o`:B}́]a&aҘg4&HQݻ%b% Ud˳t eca@xƪ$D "q*Yp$2뚯9/‹(CD9z%}b=s-pW/ IFg[Y=P6^8j%/u8~ROnL:j(78\OF hoZoԨ~xYԓ{K)JQ~´O⁅)6}|Q?+\9ָKi"rr6l2:oAV0TsCHM ց0Z IDAT3:'-0" N( ^3,PKx4"71gLHS"z)p_b+$3Y)kt!PI!=n:BgIHfF~iLe`9lIC;}FkJlEv+B'{{@ٶՑ,"97œ\T * L<.UD9ItY6L0V9L1Qys8S!1ge(Q7%k+NJxX2`rNܶ e~:CLlV6$ x<03ܷLF 9BHԆ[O:{3W@qi ϋ%Rʆl:L!w{'DDNOچϋsjw:*fp)o/6sX[I: PBm ! T0ZXP1-PP. R9!E@R+1T0fץJGH`q"C1Ake/T't$i]hs{TJ ِSYcRct:Elq. UBHM|47OCV pI N꼠/usZ]L#b2BEHVO댎1搟my-Yy;Pq p{N Rcc<9_ΤzV5 T Tiw <,«6#h&Tf8ίo{L—{?>_1b5IVY@PWr1h.S@!Dѿ?&1f=+S\D%gf2! )^/-ӺY6PR!X:fshE%irӆ"arya#dMF>8yaBs"lZCTk +s/1A'Y4&sSDpwFB:r ?muGQ&[H Niődvs xTʆ6<6B͔Rh@_^6v#q`*%нr&6&B`Vmp0I ZR*ptםa4WXRL_ uW»_kk$ߞ>TLEmu"-7L0|& 氬8m\[ǹWg_`n3ܨ?a8)&pя"l `( SXy !^1Pډ F9.:ā>ivmh}R,~S%K;4*-mcPT>N;b0UDh<11\RGLZY (l'">9'9ga{Fb4Z{g/~ x]zGdNCB /Lk+DA`g:$Z%1b"a勸oynw <[JdDsG[3 RH3Ol5[Aʮ $qT3z%,Ąwo,wT /SC4SHlq!x'KkcL2uoۆ8ֻR Y ^O12EС]DgFFނ8hQə/S/!.2.YN DLB !ʏh([DyL iƘ<9IMDd9q;ZyO*恧;F# W9wBct =/>C놔7Lq}8z"鐕<|9Djb,7C"r0ľZorL3n"k–3Ol .^__x+_U9»ߟN6|‚C ~ K~{ۍV z.{ <)93= xNBux@Wޅ{pt,L]5/.MqT6>D:S,gF'O]g0FGO08ap{*[@M Tr-Jʜ Vy am[+>WUvl h$:HHn!  'D$H r?޳&[Utnw9{7sLECލ7CJ漣 /0UV!^NG|JwT;|>}|Pē_߆Rp5TJ^+lvy [ɽAÓcFZ\o֎-$ɉ.Ͻמ{rw"("q[移GξR]礄k989m(_ϦXtvnkP%-"B1WJ ׆$)yOчo |/c&BDْ,C]d"k/"CFWMzR(  U`# %dM<,{iÜS 5CEɉ",sˀ]Y6"|u͎ ِ8;،9\0N'ys JVObWm!9^x}у&9i,<Ãk窆$ɶg' ?9$;qxEض밻' 86l^:5Mp>_QʆZ~/E.;ϊɶ FzyPr9XD}g>OtDy n/w˹q`y){0: (aFc4TlCdC^M7CNlLL oX,c\jSs|mŦ='hǎT’N_nضDր 3 q!rP)ƮדKW?mÈ$Toȉ`Vvĥ:G 6> &f &~/dSlé5HDZD4'KMNFDn}v5ŘlZiR. f ֊-gzBV8ZN{61:u&GP`Bmy!M$맅vYeQHsI5d45 "Mq!Rg9g n@ Y cLRqxɘ/|UHs*({+0 Ȋ12cιZQx'jmZl U>zЁFOcUְEl~풕;+0|Zc~s^(J) D 6D.ǝV? M0"#sz\:ă. B@ɉPr gs2"-a?voRusMfm;ao]HmPSB/h P 7KBQiB{ F)MxKz[e\gA)eYBd)~xaV)̫PFRl S{x#QNƢ9,%iSN6s\@{DJ^BZeDtcsJBH$7kSlϓ&PkPXTtLCސSB=Ol۱mI2Nca+(8LrT.g1DD0čx1H%6 (1q~ݿ;?oZ1?#PXlv^q0)JE"cuuPN@$3 Aq7Zqrn֜/6'HTdp^?}BP1 SD>ۮLo mrSy5 .AD}CD!jc}<tŘDbTPV>yֶZgz>zó荅}{gݻw8n޿Z' r|Qrw9 R/9p?^ϻme5L+bxnEu5VN $N) r7|@Kj,߶Mp~///x/k6mÔKhہQst6CNCS:<OqݙHb<+r8nwNs:&q ꩂ.vcЌlQ'n<'JpxT 8q>>}=ӛD9Wspo-IV)lj;1W<+hY5Zo *HgnqZdw>ɾ%GI o_j=kWTf"!S91Q|HyTFx@\Hڏ2J׆%r4BBӊɟT2Ҟ6]oބhXqtеWrB c@N@)yxY^"!_mx=,q!&SњĊE:e+)B#ds"k#qh/~˿MkObW,;= DΛ`׊҂@`|s=e}X;2@D HLz<(|l4/R8|w8gRQρoR%n-)c6|F&: 1FɟɂEH.MRJħORq;62 *)* ~C@Ox'nZM3kc{Ho'{DSOT$)u`nndfط,SL.2c* wߩC.!cW5 $N'# kCr{J{_,>QmI sI@ld66UI8"F|Ha/ٷ!&rc tX@ b2Plk\a;oMk<"_֯s w91cLN UԏpC#k_bb0k&Y 8AxP#WÐNpiAdAV0ubx!Lhe\uДFodž}H!Ą-Gl!ǀw;Ox֊eF!1*6Dhv>~@HۑdO1gz4Y ?6s}6C{Ipny3+o_9{!WF$9'|J_}IwӧZ|k$1wM5ہD#8חOxJ0HsEɽ0h["F+: cJwٻõ!!5(ZM&Ws=Af % # l/=tXh8sUFб)g`HBj٪C[R!C9)I c0C״ʩ?S$k(HY76Ҋ`@;g>7~2ƭǜzMcOl)an϶CG1/"\|+8_E([B*4OB9q;n1!Jay Ls"$ݾ(z_  m@3URd)%B lL+nȹÇ϶<<ʕB9!)"빲>1g{mEQ>G˟ <{W,SwQ cp01!k4B!I~zu%)כspǂtꁰުܩljJ]]M{~n|ni$Q "IT@9wqtJ"L0  [j.g$SVasB!Jpbzws^qD#4x z'v(ÇW`)`wԓUGYijlj1*w>w{ە.Ba&9„z> Ky}q,>O8wy(- /ѡtaj $kOt~!%`P}eX!f5wd<ЈѲlZmbDˁ>4î s^^n?x7WHpW#"oK4C, fKUlΐ׌P:S<׵+M|P$>Wi#0[fw!,.u/\kZbybZ\޴ 0 %Ai\q1VvԳI5䄡׉řl 4ktuMߞ%[Fv6Mѝ| vePReD)R*`m8ǧw7*o~bo!EJAS K59ҹ&%w&Vsg|oeg軼%) ~ >^.o9cW'f/ ~TQRjh+)1 nl(-?#"#tsY*!lC l<$mK\EI3m8-pbix"*mq஖.댁Vb#&G̘0HT0@w^0Bůگ>~@ ~зac;H޲w/7|{)uy/<*}Ϩ\X.J*}>8@,VZMЀz7+˹SYE2%$&R4Y:}*b]PmN>āT?HO29;EThB{[kAZ tNrqR g}J719Oym J@bBښ'bVpbR~ Ql`@&VXĽ5eA3w^$[;׉&`4XH|k EfN/GS伌8y}\Ϲ,"I̵6fT5NK%㉃sRH@JK܆$ %*y3'^k2h c#"c45іASm%CB%ϪȂDZMW5are~>Ik&LkB$뫒iJO 3}yqs0am5Jn@ c1+??7?i_~l1*-J$6B'DDw?1[r W 1'IEa6'(]q ]gAJ 79I'ɊGpA%F)p|qM$O9e@Bl>Q8"  N@tCBd*ޞ`q m#t)+vUezAspSAC:gfLʋ)t>FMm6pjVˆ"@ :IPT s>|$lhH[B ϓUʎDBp?n2jn;='q>+b(=( I; *P(Ͳ`c#B;:+0rJ1׏GYG355J$W#x<Ǐ_9ɤڹ=cXC喐o `.5$vĖQy@%qg:Qΐ7 o#2J@ &F UfYܻ{oS d ͐w8ZCm[ !I)`F,iIL>"{vf:eIkO7Bjc T悥]hv:9{@oQ=jZb[ET;MI4:\eƤ] nD$n4@BH9_h !JQjŐ눔2bp,m|}[@ 1Պ3b*8ApAk˥V.ک!u̦)7r: \c9mȱ`q< .3:j✆0!g75u=t~WMT/~ß5jw?,zy "1(ELSԻsGmw"yh,<ᵴ`"ı5]Cgk:PsDkk{C7hqb{gX tc@$p ƽ:H@ԴOorp]KP;X&h4T{(TXBTjjnؕ7[ʤgw_+} b }1[طKT܎ ۖFyDiًI9J*wMQ>TCtwOd dMeА601!$N2l(dvlOZ~oN->!eF-"'R^Ha%!}{r[ۆv e##iɽF"M$SZ<7u++hH}ƽ"bIT\N_[6ic$SΨLWѨ-*`gRRDmtcZ[IqvurNȘ#BJnDix/{O^~WޗpA,]l Z@T C CyVT._k} -&& ߄H?gd)Z7[̀K{a5Pǽyq& 25!FbS~GLM,SI_<AcWNTXL|3 3n3˷= f=,&KcpI 2ω&bɸ+ՃH=<}'cɪƠTuY ~I>S5rb)#r WI*RWqXlL&jd~}u )'< 1ILT̏X"5_v t#m<ǞH rds0døM9b;6]{.0ܫbLf dB6toh"Y *Č;~{0ej%Ϊf {G s0&٦1E ӳUl]w$UCk" Qо49i fpF"Dw wyqH)hW\,f5[=)t +ҖRz2X,= |w_WH]Ҧ#f:X|*yDJBRJYa0ҶPtCyrrSmm'5јxXz0I矿^W $rZm<\)R*dt3幟KB'hhu>c2O!" m mv y>{ P{E*mJ#2'YgGL>,yK({B"r@,dʼ dC2*$05P -d!#H)"mcZ@A)ҎV9[ݯí 6\2`u9#^刉HӤRD%»1TF%-qȿ\ )w^tyBK)^RLd;6f&`1C4VF&iF.i5H=޹~Pa [`;!}wTD5Hal)w%$<+ s? .lC*n^9ez(yxTI*Ύ~Y_'l^.~]WDE{*8J-{Ʌ 9Q:I3S lrՔ0)#7RƖ7Q_Ç?O |/ʷc;ZvX]"‰2p 9 Ľ SKsdhb=LCLwsN@}m]6|mk0w {'D I㐢mDs 0SIdɝ<)DXd4 k/{Sʼ (}MxM +M˥PkOxHAk0w:H/>I#/1&ATEQÓ8E#\[uᚦ]0o)g<\[t 62A+1&ϊgmą>I)췌XF>,=b8QiK3,Ott -DB1䝫 6Y3L`JL.Jfjy靖k@~)$"jX1@ bկt;#,^GLT0ōз$5+XEjV*S=NL읶~֊| g 1 S3yFxk7Mh fUf4-OhM<3SVBfɘs`2i* c8}zkfG eL֖֥C1GVqs9<Йֆh8Wg6*9C9 1 Y6&s&+?^ɑ( ۖjPJN$ ǀؿZ`{6 pF)Ja@ yG+W~O @d;gT5*/4*:jڝ7\r|&[lLũ0 H?O_ o `l)cN&QbOOMu<n=}Gl譟gpdrψiU,삂rxPJҍ{)9՜ܭ-9K\7a;lٞ!ޜw6b^o$@<YN]',Q{]1!&Ly'PКy Ix SW1e?Ym͵7ǁ0k n'z#_=)dzŌVǒˆ3z#r;v|E`bΥ8pj̈́t'2O>([FG 9FZKhq,Ld|$;{ Tle%vm3/5{&:$ũYy)Y?a)0>h.{Z@ɜ;͌6&&f u~2:S~f 譣W;bbA333. e2 SpNT ֛+t.UE\HC[X6gTWQwM'`7H6jb|G "=MrDρ0JTr8nǿ.A.'[htnPm\M[3>~7%Lى6"ћ#P,WZr *lK&C9pA7?&uO4/i ![΢3;HJ\b> ^2@a:(bKl"~7!xaԨWJRJȰ6*䉈9ln*s'zfa"E KθR 9p;mE?y+Iydc6@m'iv r 54BXy}P}Wl6޸, kaE؜sx\N\Kfພ8[e#K.ecgaS2@k&cc@;u9DG N'lRI" c4%-r|ỎZwd~P$9XtBp'>^^N1 Q BPTŇ@^BiMϳ a"LnkC֒ڍ T"0Qܑыг`Aض cMs}"J* + (\$m W#1Ɣ5EzpR 1z(ѽv@fj,[~_y^BS9^ 3UU!bݘsDYEW#]ßCfBkS6ȭ6fhHk#"ɧ)B© I ^(DL)k4h%'"j2jlU Z9P1Yuar̔5@%5?qܥZߟ)DL ɛIc86nLI tIwjԪ(\6#@s!Ғퟺg c]9˒Ypߺu LjJT0H*aA8X2EE컘E9 f&PӚ?NȦDz}A|nʛv]e /p~kuXۇv@zΠ"\7:wmV n-!:F':B[=.@TVW; N3qLZ@uA0gz<"'0 G}S75fOWr,VmhTPǀ)i&_^^OW~JbNh+j=6yﲸ8yu!5Eu0|:b`e,)&p} [zS2h5`MSE`z>t% AoqVæ:P] oNA<{͘}xrh,?aA]L+h m]B!HJ.}A{R k`XXGr#u6Uֆ.`Sg!rs1uPB/vټw$I:x>O5,o5s»Y7rP>C02µqȕ#ԕ}bRq%!7}B0)AܘÎ)s( C Tϊ*hZv>P'AcRT60^|E/@k':`g &_-Dh0A8DpkbqK7!7"d>od_?s e1EY&g x9 Nלр -1x>O  ݷ>??@=O|z`n̓ҪmMSpZo0Z8q x'H&\쭫aƲ] !:3|8[ #iA(~F{\sϦ~$]8y=̤fkZ]jI z8ijׂ Z _qwWL% { aH[M)q 蘓 %ӟ=aY_1D>uCr)EޑcľmbҖo;TD@ɗͩ$Mp^Ѧt^sY̵5F;E9^۽$)1̄:ẉsb ~.IZRӛsaߘ#:Csm:xOqv' j-?!uWʨ !XN)}Ls2 ߫HIbyӍR10p鿹Jn`6YI6Ǖ-B".c*B7ʰV4~`βg~_qNǿMaE! _t3Lquҝg˯y?ω㕨@IDY@ac+0Jhi۲AcҊG(?8sTlHnĢJlI̼rp;7d'GgMj$Y{IM7fHjR'Kɝ 4w'dhXlqˁ"OMcCk؈ w9/۾[QG̕ALqihNN gcJ2 !G\Og/kjK.WRHoMUQbC̹&1Lݬ.` 7()Ym韷}z*5 iYv8`il l|dP'ݴVQR2'9^#؂O}"=!Pœ} M&'=sХh@r -x nJ2 RNr9'&zUӂ~5 esofhC16oxk*ԮJZoJW;f0%DVވ^8;K m4wrG;ʯSeΪAz"7Rv obmr:mAā/amCoSSN5u?'l<~3m}/19˙s Z=KZ!FYC2w D|"g=p'>|z7<:%0A<О&X-M5„Q{,MOW˥D0IԔHVӔ`g5-HWע&K=`0!4[N2碶vcF6Ort5 [繈Jlj>'۹ƙ6))&m]6;E3iWI̘tmq``c&RWv9I )I̜1qTOsG>;H X]'FR1daR#!㫾C6j!{%FĒX|d% sNAFKMz.8% <ŨlIĦ,gNL7-[!QNҳ@ FЕ]P0(S.yM&+94:rd$pm@l”gMZCbm9DZ49J#FH:mU–b]0=Kzm"e}k^pxFj:J:Ǥ3p;)pE\06j!"6ϴPS2x!)9f†,. &6ï*+Fz5ѵq:@j3IyOx3l,C!ת X.)};5 rLqe49y$KOFLbs ]I. >)5eydOMbTK *wZmȩ"?{ld'Jp>O= 9`qϞKŸ@Sy?чg |{_)ɣ,pߵ? nӧ4g4m@-3<X05>:9^>bk0wdy'|11 +38I>chj K!^P1mF82IlDnee#}yzystŔtcNgGt3LR6|ÿeyKSY aD[7?RŴ=hd 77SPdPwʆPd+ \'qn?\41$ \>;uo)29BQM[4QoU 1F[ (#{׮Ԙ vumwht'I<^eþ w .a5R"r7J|F(uВ"R:(2ҶH`aKOG~⠃hq"cVp}9:2YM\9:`o̝(j丂fK|DplL;p"$E.kb!Ã&iP޿Ѱlj"nH%ը5=~7ph9P1P;Wa]k*a*݄ѯⵤavq,p-¨.2,s &n ԜR&$k&x`~_ij3o['7AeI+&/RK"D`qWÏxyjx>N {:or()D>_G^PS~U5Qy k 7M8K2Jлzq.sKE|r-.{9)HzkgC۷ݽ˚~"K88,Ѓ#) J4%fƣVhǥ7؝j sy,h7 5]$@&GٍeRL^T5ؤʂD$lSsSJHL \F< J yk[﹨)S%+ZǦ\/\?|ֽ~}dV.< Dq !dP=\BT%yA׶O@6ј$$^# e$@(aBk `uHJF#"Հ#:Z1J5)RMAms(鹌j|#"5ٯgBIͬps#y^?, )r$<zcp #VZj1/Ӡl/{Lk씹6%—W0<0,lmc*K)CB]uh΃x 䧤ܐU6/Y5Jkz`_?K> )j$ c4H$LEa8Ô2BALj ۶# )}0o(`JBXܒ[f( kSԹd26a AP&{mZAy@^VS&&]ccC!pGPOyxJ⡶@.!pFe?2]\Rҏu!(OFgMŝDiÉ  EmZgd$[w ^ҒڐqM~߰kYHRn]htA~9)DGszyrU*Чy MYl¥0IQD!Ф퇚 ̡?p¨9x8aQxy |ao :9%n蕼)2H1߿eK~_O#_iM7$5݂g{]o4U: ={KƱ˳2azНʖX+&=ޕRX؇BZ8A퍚餓j [X榆ӿEmY$2(':G9];6M8$mJ!`i!f͓+{"WyjxղId4mΤ]U٧ D#|Y )s.[/FT828i$zL›b:e2&oNmFwz͍&dΫvs?2Cy4t =<1ݟkRc1wP9sɞ+F"W _|;9h$Y8zm$atd+nҀC<됶@HnNXj3˘C;@HB}Vht.6cȡppP49ds'ݙcF5z'ZU`qTF N)arydprX7Fc"9;1:yztJ}"6߭B(3fwɄ - JNE@`H9]| $-JkXwS'bM*>]TFbB#f4onȘL8aikn(,([Hں*i-X0K"zR-ts5Og¶#^+W=#~\(%HpsmL$6Ӟ|d.+wMgD:AYd5{ )#bP K:͆= 4Q &&{ H&V3{'dl&uMH]ԱD6>sOp4+~@2rJI//$e ;쵟#[l,sL@C+ev+p^o)7{ j1훀d7LL%$^@]GU\{;/';rPb0j1ƴ- mWA 1*MgI~{bFs) VCgd{K/J5N=J!Y NRB I](;)g.sXrR2*9E\`y \cR4g>)1x\PQʆnxٮ=98)0!IڄIQL#n8ȁ)[ʙESGH M3>M"hၵ&.( 5ig$jRL3,hZ^ g֝n9d,!T** 'Q #!%j9C>Fʦ~y `FKXwM$圐IYpق"]g:7ɉϙ˰3 ` 1R 199wwN R&@g4J lJ`}akE2ݸ96x9S ި; Q{rQXDŽ(~K.&y6,IggO2Je_hnpyECs 6wQ:G&x6{_6JC ն2:dk_r*Mڃ  N$y3z2˾Oo/hr_ͥUj~^Py=79摨 5bf'70ahD"=>N{:/|K "MWr9,biATm⒋ I30費>'<Շ1JJ8 u%7 j9 CG9u6A{J*7hLm ~Vs3nV.J)une CV B8y][t%ckCɅ!"&jA(LjQ!;wo=c!B60QsCkN6KΝH@ŧ)y]s▼y{8qy'PvqA/;ׂلț#J93|:@k*o܊<·fs<\DtȈ X6vJ&.:L+Q 0F€O!)f*đ*C cF{ {"p?7Χ"0V RɧkBw5j`5]ɺ~?k]Q__yg0yZEJ^g0kCX6|;MMQ!ŷzwoOn0`-#IƗvZlriLa iӳPimRd kߝCdvo= 2}^ODҘ%)BB3@:+Qv] o3XDkyʝdmbs|(;PQpf7u*H4lγ%4j"S咵M%p!ȝӯŏEBaޮ79@W1;WA#ׁ=A3o *pquh-Oh׎bPZL ia!/A2$!LM ~4MSF' h0%!ּk J#()/a$/Rv;sC?[y|xӳ;-fnkl 1ʐʝ6þ^cLZ l:ѴZn{=p. *-D 1&ݝyTmIyr9`N ׯ>&(ZG|?[LR]y2,ҡ/G|V}xb^ 3ۢYXחWӦ6kẞ2Nbۇ'B˃D's̳H6?Ǣpnj)eB02SΘ>}4[kaÉDkb(Z 7c,ֆt̎ F"긮's:;+ ]1C&M )Cpr׳j[)!ɬϟ:@D WB(vHei)a!hMn c3p^Ca޴ s!Ob! 6dMɎJ J\7ZaPf1NފU &Urܫzqه\0WB iʉ1.,/^!s`eY;<+|fZ;LJIg\pThj0@B%"vuZmr:Mm杓hDf' g,Z`2B$$thAiam#Ζk50F$֖^L4*J)! pn͜9'-xߠI]E>,ɞs%EֺQ!Y>bk@w`ZyTj7xE~ӺQ,H ޜ꫑)C8_ QhYM\wWF.a 8I(]{?vA_?s%%G#jPD"[p'r )!!d@ó=$CN`G<'j9) a09Ӌ8OXg)'ϖbdD,u6lS,wIhVgU8L]>X8'E 7LѐͱJY>T&"nm`rn6&*7Ęn>@a 4I*hl^8/6܆R'޴w{ւǞnJ EF<BHA ,xG (@b]Gn `*d6&ֶKq\3̀R. %18a.`ԱՉ"ޱ~Uyi܇U/rF:^^>#(,_up[lN 2YL^~5Pv;5 XE2xk_U,b*T-1=4zEߘ4~D 6G__xg)Vʁ׉q|^ .E?/X@NZ@yG9QkXWP[C mqå#l?yY[ENAaNIhZkሴm 1]|T.aw(!j Ndc+6߬[YН  Yd15Q8!ȎPM&D$K  _y4`bvƿ.E`%xDJإƩS7qKf*QKw}..j~RC'mu;%qsXWtHјTΛaƢJo% )6s(g7lKED%Ι璋0 oKKZ<up=]i%jq  jY_קYNғ%Vq ƉqE޻vIOH3$q% 0fFEl|`Q]_oxo\ׅr0cB,dr2gbL0Z|LƆȸa~ /}#⿙RBS ^3YepSBI"&ʕP0<|B|9=r99Jx S/:_('*rDwJVM0bNus?KlI^:(9v7 9vvn&>7Ye3`h ĭ!"GG*y'E"Z$I`Bdk]+{0 ⥘5$W@ 1w} O {\u{O.e9Dij\{ 2؋h 5G<\@gP#s#7S 'iuq퓠%QȔs1ym&Eq\9\Ki'lyi&y:Nlr~ 1kdn&莔scB>myJ$#J2EZu)'k;8km 52R`랕L=r] 42#:܄faqMG^e$v^YC|e&B5(quwKFi44}fjמsr UHt h\6Ҫ^m"|O{௟z>_ cl* C^QrD)}pM X2:I9Nq.Z1Y͆yfP!G\qSJc8EdFFv {ʶ,yqjO^;DZW1Oj#g{1b=_PYD}SJȬ rкcGxJ_B4k!̐)md}qL9@oB\cg9k"dh/# RX``$ Mp53`,,>l8Oc`tLq 4q u$" X ;, Њ"6*wړ{%7̓"͵qz(rV\"0(HGL50q_Kg/el4Aw Et\ׅZ+} )P;Pn$)O^#938z;o5Hjds~f/ R,zg6*7  v O1EQ6]8hs[8}hKS^V,8 IDATvGz޻n?=m=m{SЛSCR hn!ʢ<5I9#Ć]ǜ< ЌND4:`Tsivp Z݄Ik[W(E H[J&|c~ |U5A5~b1")|q*su&bx9O{vQ 3֤{ YFۇg>PȽ(㣰pr"P+iOeB.`Tk-I )p"3Ƃ$$#[m*s ڔ^h"Ky=4M|rɈq"DBx#@6֧șe-MּP'=R-쉐X@FYk J:䭰&$kAHŅ ?&b@'(9 w6O=7<T@`48(%kT5&ΗS}a,b[@LΥ}#w-bMpՎ"V ρ  jpCIl$'BT֤H9&VOy 'I`i @uUD:l^"kLgc:&څY"ф0n(YҹsU~t5٤'p&֐Yn.J];g&)|DТH_90 kszB>lr?F Ѡ3lK'RHxyby4 9B2.o} /y MpB,+,><5Cc`L8q> )jJŘY x0 Qp:|)c.n8O&lIGnY21&:g\]H6Jdz'XkA2'zHCzĜh|#e}IĪLY"h4V2:bs?c4r[5Sa.s-d x>+"y1x{>g &G1D !3irS\(!'@A,7Y7 _+r޻z!g!T& ΕV&\l"!w9>` ŏq7 zhjɬfvX 6P`Ҵ۫Wz hJ_>+M}|nдC|Z !>y/*G?~͂vWd 3aix!q%\9 *e,DŸ{N"Չ49҅ 7 "$J~;y''$',o9hQ$Å+੘Qy nULuĠ g&LCPcl &9Nvh N2sm7&*EW\Z,)<``{E?)OJ\/,2d\ȅlW|L{hrHrNMCXl䈥.:~pPNJ&zDa1Q]f)Rkb|[=u6ISho)l8N\JG=YE f Dx, S"1 1c-uޕ%#P+y)MG1 T s:6뽵NV&sDM37I>!G>xPBB@~Ѱ0@'9 ץ8ޞ4 *DB`N>6N-$uy\hw714Ejt %tLs Vtq>a&(׿1^&u$qŏ@mNak]0I0Y\ Fǡ"IEWx^'!Db<EYf\ e P6|6Qɽc*ڍ'l6q~d8snAD[('>:EZq 13zw3ZR o:D9wVK+e 75nDt6녛wC֘x%D bIXaܘ|߱˸h2j n+%ųQCM jA+v+NW0$ %P촁' Y\!)p9n?g;?V3~f ~η~m_9# n2 gXko'?Dn'-P$,y:1a.Nm]89'>`]g붇s,%l2cv.NhBX['=,%}?I/5_, 15]ęN|49>`" 1E8,)6?bҜ}xU~K‹y13q7cڇp y0#" $'iU9 ҄X2] R<Ƽ`U&R0*Ejmc2Oa>rP;!T9i:bhu9)cNwKِAX֤;3"BC~P6)9! ] p' ܕZؠ\"IhZE G9q8! `x,ܣI` 1;WC.{>/Nǽ4#a50b螼!Z5"Co~V>}+n4G~F =zphoBԍ_7DanPmO!z_{5]kR?oޠZ%i! bb|ꞠdM53mu6eM#XW90'ߟ+-O]\yN!1}.pw.sB%BzcvY߉lxN-Ӑ|x>(Wȿ1i,ISsLTES+rWphtͥjlj5B7K,}>V~&nr̒r%f~ǩ?g '!K\wc@·#xy׎IYY d"F2\};Eג JA Jq9aL/YdURLW=p).0y^LO:J&FƆ:B|Y|)s/[TJ\A`9O" =G.{"a jǤR4]]XfmmGs1^L>bkxo*5%YVR40m?t\;L@Ém !6jKq h\Ɏ!q>R9f}Ju mް] {Ǒ fQhs$]X#r{Dhݧs)7Yj<ɏs.\ -ykx|0 p=wMȸHΊݭϛJT !)o^'L X(Y[I th-׵j₸! 5b*v\'1ֻϑ9N3lPD6᧜LX6Gƛ4SP!PEhY=¯cD.(Yk~WdM`g>59S`7 T ;βxsц$IPW2{GV&lX{oRC%:0zQ >st6hHnXJy;h,9+=_"7O#XW SNRHW#_+3m[_/%S!Dt `!T2oo85-ڢ5v׆ًvy&zA7}0M߳`GVh}!$ (1 ]dA$f`~Z6;r1F ȹ1pC/GX뛍L</H)MP2y/ X@''.5'\瓍UΊ}L5B& zcO&_ɖC -D evɴabZZ/8(*k.&&Tx.>)L և]Sk*{k{s:tZRbC3})0r*-|irX e7~NU=2&6 _Th.Ю؀Qʊc.d~PRf `w^$*&r|`ոN%)hܵ=Y㤮یdL ٧Bvg͗J`+Hß!c"MP7\4'"b,/y:qcК gDJ#Q,&C{Z!'8nhLF(fCkv؂c}~xʀ U)夏|,M쇚P 1/'S7"OƉ̠"^_>کO<^Eiv]rR'EWm$~|,GMGtf"y"(͎#D*xb=/M@/Hom{ >do{@7} }|l8Y "CS2eoo0@1pg`.:j0c E0-Xv D"n&V̟ nFD(5Q{ ~wnw` (7ޛ ys6wL4GeqD*p?R&ϱ3MPa@ K<óu\ulaqDhXHl.NH]csifyd:g͹-ձf-('(B"_hRWB2*$̄!ČiflJrK%.M~"w\Hp}ġŀd:xH9m^Nچ`SM 50U5TW@ޅE6Ѱr4Aq3/| `v`a~˼\4?Ͽ2S,^ԝ5صe%bs᳧^X y7䁼=;rR@X(gG| 4rj{YLyhk,#zƘ9o_kOSFS5:\P$z2@SM;JحRCO7z76ȉ"bD4cJ t܋RԶܘ9PMInK;! W~$Uα_]?s{lž qdMGJv4Coo 2v~58qk`z>hEk2)XORZI?qX}l4ʉĬ*i>ԱlmRak$U{UMT+=e8B9'iyٸ6{<rœx]xC@א G^ܭk[ y G4[ޣptDh cRoO^3m0\p$Gy#2`t cL# rÞOOZ?61Tb~LLG٘[_SjB:0{"^^_w:&oY Mb/uӛPk ׷Sqc_o}}~ӈ0Dhq}iJAR|v;,\|^Ox<4Q֊ve|7#ҙ!,$|h )t]W?+hautf+ VW8 HHja8FK\X(γBy)c/ן+ǿ&p[`LcCﵣչ.)~s3ɬy .3a\̂ٶʤ#S]%;#eN).L*\914 #" T8 ]ltR /C؄%f  5ɷ>8Հ8bTS cuT]hTR㱨\\z,dXhZgSkH48&pW}.@.k`k&)8smA|: 9ru"_IIX)Y;nNDWrBLTxTݻZeAߘ9zّyi zi ($:PP"ܠhz4ӥ":@BB AIppbȉ]r| 7M%*׽u,?{fe3V1|VoHGsǚSڤvQ$ %5UB9p{!>e}kjòG̵1N9S"L!"q!•ir^% D%gk'' f$)g(ho8n"omo|r)ᱺeIND%wօaіBtU),eVIc^gg&ń9qkKz%l| i a8k1mT' ^c[EX }/#eVgJaYZjC…^[|@bA@N~ёN/w>_^B"ύ<*LnzϿ?[R _`/?: 'w3 (e`1_sS /w&e3 R]p,=&Q e|vNM&qM/KtE Cs&`*+^B5̋e*a,\1,s%V̔9k` 'HƎ7ev)K(ƒ{'$i6-LdrD%mbO !>Zk 5WxcJWIMjLpl$D:iiyEQY"u2Y pbeOcS)%d7,rmpѨ?)F%8ɦ1{ڒZ'Bg<#kz</32אq#k0 X 5z4D7Dm CSðE|9f=&~[._cнdzpNcmjI 0?bZ=R Epۏj n`.S.h·0Nw\Ñ%y$Z)T׊h:䨥:| QP &OdhIEMÃKyglM? qyeQښΓyJy Teьb$!68d P֧?Z-jg A*|Xw|g?}};|6(E>;r÷d:a%yMZFZͅTpL XG~ctgU#P,qZ &el ef)L5J9YRܳa~]OD*y[ZL"-ab!7Z|hz ߱'6 M)y!SBh(,a\ @*I5zGsЏ }W7|O6)J.JzXѐ݀pk vp80~N8Ľ}x pѢK4ơ9dM|aP + v4DEkէ( =?­=SvM^HܠϓSx yP+iʒ`uw&RIO)<-u9e&rz6۞pr|ܯA뚥5v|POIK~G$^N k,vQS9= cjtϤ=F{?(y7}(/ IbxU muN90:;rC ƘWA߂!\325-ї9{"oC|z)gի97/s8/uzqR',^иD$ OhE^xxT*4ʅz /kb5Ócd_C:EF:s)ԒiH0ӥmIә"Uh 2$" v mDpD=rX)%qZ $ Dž1ia`/3&zjulZBcKQat"y[diyv\TY=/w\'숚~hRBu`ke("X{ W'3[5NEh˹ KLDN["\C&qԃI}@C(Wn.>- L45Ï8nE .'e+I<&%5Ue,ضzFs %eR_V*/JZpK/>|`)Y xg qf;aQVu.h2ZؔǶgqkl!Ksӥxό Yu-t<]k L9\ܰpI `Jip=@=*Bk1V:6,J*P`2!|O9:H6),k1ʤqg_O~q0Ėҁh8sq;EH%.8T@B9A\r  ҙ}?vipԆk\rP&?7) 3v @g%Pm 8s/98 }L(ҨnJ!!͖ 0p)tnLmɴdt=rEl36#g\=Sb5@K-2md%rSLp@s[SQ-< ʼn^2d;n1&~PH**2[iU|rK^D|'/" Ro'K"ЩG*u Rg-!ԡ{2I<˝sAJTf~uq|PUsk\p</ {ȵ:Q7@''A O{B H5qЗI)PTŷa4) &Rc1ao:9sw5R(1gRjm}*ŜK$@$!D#bZaut%$[p(͋--(eV^X|sNTŨ\[R#7N*R`H^F Y ;R+}LzFXn;f>B ɤgT|0XTTX"aWG hsO_/?Ӿ/uٟ7X]Jx(\5 y)z~)K)ebj'm!aZΈ̅~׵S~=)Zu]!~}QeK}FKaqk]s|XV?/m]0 sُ5QZE]O[/h;蓴E?>3=Lbdmn>Æ條Սy>GkbY%-]*hb9Zq͎DԼO9} 8;c]r82D !gId`Dj(Kx J+KNEq8e>"·P ;9Y| qN<,_[Z*ͲZi>5$}O7k4BQ8S:Ra}3 9EvT!,-MəEaH][ e$Y)K[_CTQR )vF`3z\< >z:AX&+1$WKϰ)5A|ֳA Y %u}jcfw'>)#?[_O}x7OK= oo?$s`@ksLe=Q(RAxSu 9m#RknV<,{Zk*I></FZ܏;ZN( ;U*}; xB]!zvS2r?R<Vi]X8Ov Nph]uxqFShJd(+DjvJ/"+?k@?:Z1~*Q3U1Iݞsv@:Cy. ;@*=._H%S+_Ð?FZR3ƚv. !:589\H|nhMq ֤3~gUj Sgq4WU$CTNUar&XS aLXF%drw _o7^iRd&*]͡4o%\?nwo(Ž3]D2g 9&[y:̀o_ _?/_F)j)ioQ A/)nmÜWglmn1oqKEXɌqQԔuI9 e>cocV|1dZ^1cv+WFm8xMu0\ssfgrEߥ!B ^USɸ|۽jܤD7Z+k"ׂG0R6AǤ\v^C‹ zə: 8Х8(8] 芟rD`gxQLaE]Tj^k=CrkEz 7#m-bT%I11eO[z]\qs}9b IS*|#sE1rD)'z2m:بRaW̩"u)>iޝ:f}|vi79do1nAўqҤ%8;90^ IDATPs;(Sl)dFPe{g9~<?^rX@$!gAQJq3^v38FU%$S˙䵴<#6FGn:.)tCmzLO4թsp-%os ׌@>Lg:|GrPD⋛T+<4 A ! l|aekZR݊";׭Ɇ2}ڢå-o!gPV8!^ـCB n,0[AE cS[C*BͰ( ƌϹxuîg,0P3Kl5ߞk x/"c# {m-ClӻN9F7 ? ǣMi3#h*WXء?Qށ1r2CҘIL qș1ɛ N JHJ%>QΈ-B !'lv?{ۧ+#ێ޿qK\A*Iϔ-ZJtO@5i][cOcmc!P^e\. h.7B a.jMqPB2/G!;759̅)q.r!#`;z;kDߩ!$}h]R7_A-Lmk72Jˊ4[}11\h9m\~nK-BU"ق|${{^SFE5mD9@]Txvw,qaeNyh ä'p**崋(v|Vhiĭ5}f }ޅP__G?lKER9ͳ%U3s:*s̚C1_KV'kp-06=lÝ.To-riK|Ȏeq04,K*Z N?=}ٮ?+! P߫04Ԓq~r%Ԁ}=П 9~lIMW M B;ZZtTb,-h;~s}fnRn9E .*10&n=$P>w2+`ătƃ4n+tbvQP ߛJ@KcJ-uXx=)%GۃV#eR.CjjD2l*R 31uo!q"l~` 2QDO_Qd!= ʖk:` 7_$g0vaEikeG;_붚̙j R>JYW=%.=,oGGA $; @fF2$mm y]=hH}= lhMP5lK(,9"!6Sn;b3LǦJؐޗ Z+8…й0 dPHv, "OjL¢-85dZ>1%% ɷn[vSCѥ\^D!AJkP|&X (kO]Tk:^^|-ν=Se[:V QI$cGAEPG˘%skMJ$9 DMzBc4nCzU'1g8Vaɮ(۞%}.4=SD@1q3Oݽuu 8kF;O3RE"<{T mseUlsԃ| 1*QDd%c%4_ fu9,rNy_4=8jeT7{֮,/r  v]"0R GM P[Mp+9j(' ٔȮ6(פ*'3- 44NX˕ BXZPA&Xi>"1ȼ"**4Ȕn1s< DnUӼT<0{ϰak#ŚuZ7:?oڕd2+Z>{gBgfp!Z wعdf\Dhl[B; k-$0FA2P(8~~һ__?7%ptpugIRxeC"Z–)[B"h((2I~a\s( h_r*1g2fĴG",Dq(NDA Go;Nn5`9ƴVhO=Ϯ<t7iNZĜ8/צVL.DdU1̉kHુ|t9M!&lڿSInTE6F1: 1ɖxwgae[eƮY..ı7f(G9U%xʠ bjK%T,>^ C\<$͉5z͙. Yte82E[ճt2Dq:Kԋ](n@qZۡ$A #f82!^f\~e Vf7:w|4𐇶A"-UGڐa򑋜2rfQ{*}[h 5Bύ/wYR|kK_;ݎ at3 ߻g@xrZ0}9q͎Ir4 fOo59kF sK`!Cl3>6?H]©2[uM tmt XEMQI]tE2A)(X A> zFd=9)( ҃:$dDM76.=+lqgD~N{hy6EL'"jMwj;,PSMBDb}0xG_ٰA“b'9 sz kiΑG+= |z()TV9lD`~:g~-JfR*~4}a[*hߚ2d b/[#WB&Nv:&ģ1O'k B0PdD6r0NsUW&\*%/GQMT-`uW,mtEzBsւe\<%L^R5=XJ]-NZ&W%Tk-#)aYm+)iC/7r按rt9бѮn8x0攅 w<΁t]LE`J|& I]NiclJ&%±U}~HrS˴RH(V+b3E9jBWxvи$ju-aέ4B K?gKN,RZe.xj<LNRR @Aod^b'nYRɔnKpAEp@dk}[!,!q 3Cw\\O|ggCʊ;32Qrs)WNwj>#s;OJ//;L5Dʹ˔\`8cS}0=ǟQ.Eq;W̨ZoXx\/Le Pm>j&oM0e1}b9nTj_(A\ Kh9?ס9q*[T_EN8PpEU$|k[ ZV"87<]fm3.\3x |,ԔPSVlL p4d+eoM$OnVACB0GxNЙ \r`Eъ:^ n8cF2.S5'= .!s\c"-Key]s,-) ?EkC/%of9YȖ Y R6CsZ+kL{C=q@VJfd苏= }vŻa[YM"P5ǡaA1xD4^p YM%!¸pTrڮ9֦$a6fjCrkQn1E+='J;u^) KHe$-Ui5&Rị$ 29ep낣(ׁC Ԇ2sd-~_p})w|bbJȳQ3h0vT#쏐6bM$L^ K֤ՏXeU?aBaE6Cg_*aA=9㺆 1c.|R2Wθe \ru8<<<{ښ0$;RJݢ޹4mkw`DX:moP6ⶄjWF>\^oqãU4_StB@VѯbڿlPLZi D%?k8|Œ6:Gm1=Ж8o1FEeq;]-F1TV3SB]/GmDJ%LG#,ԊO^ YlFRw($;Uҟbj{3B%q'T,F kb%ʢAP熛+?<1;p;%:'v0 }%kY5oʋY$Z+pe 37SJ  Qcc*5׎p&5#騊>o5.ZWv(NVFUY4 K.BsT :$,>D%XtY(}/,N抱9#o}e+%;CbѸ(Y*Gn0 LF¹~G@kjM"L6cf@ʦm䣍!_R-^ΠB]]5| 'e٫׶051p)R_& с5a!ñfpC򛑭mʟZ`L0ö~0 AFwK3BpX;vW!Ly `b?D_LQ v1wwA9S$Q3lo,Bb jÇZpS$:FZp=7R\/C}i3 IDATDnRM%i6Z je܉5'EÓHI qA.RhR,ԃһ\"@~z20.\d3cLJjVrL?p 4Q@\ 5uJtPnK; 5 Dt[2 GO) )ڭ:`hDȊ$]o]%%}vRß(y(rы>*$ĻBX>cfa}$>p(bᦙ)P K<ϭ9,SԜ=hȷv;lRS@Akg-ϥPLC&Y ANJv?8% !>v,`|\j#+D5HhUf ?Ci9'B9a_/Xٗf@/OqG@=h$>`EpDt ul^__ve+^k{)Wڭ|o,4 ,N6D K`>OB! ضj++^5lz9 JI[K|uI%U¦bmYvCא_O'K4wr&9CSdT@-%3Ȫ$*rdZ{ujv~VIG^@vCݴ f5P*7`B?JA JQϡ PCTBK¬MkmB `OuBr~Jܒkɩך?CdX|OVyB_O_"(BB/>[yQnHZQs5 T#޾80Zh ygtvbDǡaQ!|#kH@zsC\6(WrIZ[ | _rh^`B0q a-o}Q#P*QDIϫ}`!-RԝDdsYYstXg|ЁRUrfhȖgp Q+Dv}ƫFǰ3THZ.Z8#PA|Okş3G^~0{74iEs 횇,(_Zzvڀ΄x]ˁb 坸yyf˔ɚdq;A Iʒ*&Z2 :"*X Rї\ p3]ВJxpoonJaVw)'6fYp0s.{LjR1 PӪ]S铴vh%x<.Lwڈ\9k|=8eEXJCך8/x\nM"Tp ZMy&ڃI2LtAOXipPJDKN>$b|Ƒn %opOdtjHT026_Qfثle+S SAS2 S&n% MIc Ԓ%1:B)J9XQ}BT2noBXw~քd;ù>{ߙ`T+-31SΓ rx}y ,,҂" ǿ?K9 ڷӿDzdž Sx!l=]^-S2KĒU =28g>|ct̉3%/:㘸FA(&éȈTkcc߹d}1:1p*#|K.1b抓6_3m!1]yaN[mc*Y8xxnWӟ*Qj}[1I1`( \b'\ʦ騲4~꾨JOd_\Z @͞BxzHǓmDLPh0u p}n)߽X{"7Â*b8J!VON Ü3l `qv-N9y([À_~x7BQN ǐqqmCxy46ICͤv,^gaem͚3 (/]A!jY{>ΖT+Y`fƘ%x<,WWz ŦӱF}KJuc)9,%s` x5ޱ*o ld8j5 8\ 0Õpy0ْn{cd$ZY&-PmTEޙSs6"7X E*礘xK;d+ 3g8ecxtњˇbe4{d`6"^)#\ wuqN ,cPIPL쓶:Vp[Ҋ|"lL%ǘ r J1pqń375a( &8!萍gTPrC + ~xyi&Xk[hV{4߼JlVY"]d\N`m, /aijxOH)k|EVr],A W*ZV8l$KW tH]M}a3(\-g]dME\9բ,]ADicL;tmNK<7ƄOH P#SAeF:Jayb\Ts].%i}oZ!:I\Q }"0G?o_txT` ֳƏ5" ЃPif8ZN$FNt Bptyuu4pCO(Fљ#D.@?'ZQJ ,"-H/Z5/1i"r:$N@L &)egF9 "VrEΤZWs|ϭVQ`Y2br{€m06ux}ηw<[e%Z2[SVQtD.3'ǼkM`NZ!AժB(q_KY_kFm7(W1'bۊ-LnRobZ?4 ˆ{+mڨ7BʋM@Iipg^.䀁DեE;52ϡ9Q ;KF^ZO0T)&ēKT_V+,svR)p9G:]KcREt5ajcj[tE,ّ x}i%]%J5^RB@rGKIɘ c\ɡhU" :N:Xciaܳ  <ܢ}ݒ 2QQdA9K7ٮF̣%.NƦ9&0Z4[aFZKGFjk&[&M#jrL2$%naSH53j7Rx^e]Ƙ"23>(DcƳag6I uox<{iv9K朤-ria{߄4PHOq*sPRKQ'.j5yF_l3QnOn;G\o .Ԙ3nZmDlfߖBkvq۹d}q}n?/䚕&Y% ` 8۳<`àמ×D#[YkqLL;[IYzb9gMJbIZygdQ{SM}k+.Kl %:>cX^r~þ5Xaj9;']6sC*^WB<"<:JxI\@Vvw8-d1`ٰ쨭Q:W z%hG,9)MM%'!N:SݯxZGqkИpyxb\6)&W] L"攀w\sNۆV ^o7rdUZp3&&d*m,.`~YΈDۭhXNBԻSTΠZu4R7ҋ2s5[ah\1I-;iKÑH8lyiJym+%&b ̈x0$[CDeS䵷 ۆV o"'cbAZƉdv"'޽-{)!,`iO nAl LHe~R:.ZU6Ue`qP\ӤK*+ۘBl͵b7܏Q[Ŷm+a&\ DRVUһ;mZs)I dрZ˝aIq Q3$RI'L\:Gov%E8f(zOѧz \F9jc(a`kNc!@ݠ pNI[#u⨣6#JL[ 0| oV WQO)"DyTU=*!CUs!i`vL)R Gt&YFsDLk_8Nc9 '8*S> W|~&=0KZBGm~A naЩ'p%&RRn{cP5U{+Fܟ 䴠 G`mWFL8!a0Ux7e/}yRxcD5hTpcH^ k/Q1 M D޿st y82^o/,;_!W !T Q,e/xNC d-(䣰o9t8ioHJ ͭ"J [f"! 9(%YӀ|tYt,M8n躎?WD&6ܮ`Fʣ+%`Z6"o|V:wK%<.F)B8SfG ٿS4_ZQ~}ex6P+0k|ϓ_ rR.,Q<2ޘpωۍѩ.fHQ%/yYaB穀!pܘ-Ό+!Se-+Ą~ޡ~.wSؤ8ٯ]rʼ80[J̬G< XslmL[ɰ{Ҩb8d0i22S֦0q}-Nn+ IDATD?u|ą<&FgPJf[)DK53`Tl1 Qply/1D2+a=Ռ^ I"Љƀqw)cJ VP)~LL=1u)ZR~puH(iKe `P(L`K i:9sn90 ~6$sbcS{0N.8wa0=1D' _to#:jhJ#6] 5Oq2x or٪Zx7;,1ͳfzGiS_Usb8:Ŵk,%Ղ9;!+)4*U tՇ'"WSu NE2daC 3 3%J̴h```9Հj]V]xx/?&QIBCEfMIagVӲj& 7@½xNI8:&>>:w0'pt֩1  ,y m j9ć T9CS2tEh!mH K 6ҰnzRפW50ZWB(H'*m%#p?X10M'1mcUMaֽyԲ"3ւmoCW71-_j|2Zu(IĜP AlA @{Ǐ"ȁV KY ,[2Oܟ>55眨ai7>*z?cK?y(XuG* #Ç}}ʸ>ՍM{jM #cϫ~tTaYa1cOXyy2c"P̐h߃j]YHꫨ'1Q S{e2p3pE<!pӢmu)Γe]XR(}$'t?S>{G]9u؝8-pǀ't AcrvN;.qv7xZB,p.Cq2l*)T4+zѺ-g^L#BP L$ nA1f"uk*1/j!eh 9J?aSs1'qCUaNaH%>)~'켾>YGK>/uuH-_hݺ6 <̗ͤq24yx s^강FJ;-U-$³fc!AiD+RK8) b+Dq<`eGi0.$7yq:pm(Ɠ:}wF."P=]y4d qxQx4^n7BrԔ$^3xʅO4CmVኁ͕@Ȣ+ Z+Er(ļ Kv9#x=P·s $8l% sIU βZɓguh}fL''>TK&Uӻlk%<(\J1a6 h~Pa֞uc9h4 - 7* .WIIL~ t`Yg<09LsC'c,r@ Kf`pLPt؍¼dI7)pt Ajxm>ʲo2#5cHA7:)n[C $ 3)|{CJXA&TJy()58+/\ HBX[NGG`Qڝzw2kǜ1c*8OG)OAmxWiDʹXGЛ1Zfii]Sl9a@)PigQqpyI"%1qOvg LwdM,'1(`)s-v܍hT%p"h## (yWB7'j7﷔mh=&re3lxNrmO1\5{VBdWiнDj帆,H,9R9P1`|eHègXjٳ OcYx}exQ >dk\l<1iT. svl;eԼ!, lvzL[ ğcn .DIŶQ='rP9phs:xc{ycap<@{aAd@=}9͉< a{ّFq8ͯwwY0ƺr kA b+. (qaptZHU׊<m̅6ryB`q%]lIHRN2 '!t(ˆZ=I=HM>H d8ܕolx'; …\NT~HW}. FʅM6 e f&BVˊ-Ѷ \1pXP!߅i˛}aA1mPj9N."4@t嚮@+QKʰD"GG 쓵mH5J9!5R/+9}:)eL> Wq3h~'HX"`/ ^CI8 䆖# 3o6Wz@Ǐ_jSSPTܵZ1>n5$OHȈ0x X`'ܼ3`TT_i`/oǃ\jfE<ȱ|6CwZ+zv7Y;Y#}yŲ!# ǃL{ Z̥(,<3MPp*emZJFZ+o 8:>||PQxU$T V Ә5h9XU6dxwEg^1lyxalJ  ~ AZoR^g0OYP*.S9|v=JoxQ.cLu"p[ÃLdK- 2ڀF[p[#_/]VD|&"Bae+zg=r@ɪ+1،a^p1N[@k%8_&{k4JҢF]$ԍ ?Y b JmxJ–I*Z*7'0!/^6qIFQu T'[OlSc9+3H|L )qj0Rh:eZU553X$+ } M;.N2 VE,K[S @ vp5h4|@?'\"KK\<,_ψH 1|| f 07'|7ގseǫM|<"|rnN.p9&nvPZs@Il;q#jBIWg=GSH(±ڨ4) Ǣ!uKcU3X ~LtCIDpraIΦDZՔԾ)Mt?w~4^)VT mJ TUNu f1EB'ǝUMZ,P *Paur̚&i'۞ѶЩh LT8;؜>uXt`FDwysi)(ꃥ},88̧bo>ጶNC.8 vωmo8&Sc2lvj<3#q9*u똃8s88~coK6PNH_eTqe 匢jXK̇XŞҚ3FWlq" x'PK?/[q D>h 6 5(`ԟ?|#>䰾P}p*@:1:lN9nB$IVS- eQA5qY$N9$"@jm9dikc0Y>ZYtqN4L;G~AcßIJ:c&-I~'>y=ۭ }@3P;:␥ґqufSU6صՍԀ\@w0=I/.AEQ32T1m,q3 9$XȃES+-mlu&utOD*8Iuk❋ؕ7ّR #pk/@ O<ϲւmks`J< љ@R Q<Ƌ;-9!L4 ܆fY9ӑqOdLG)~qN1L~-JĒ pPxrqJn 2C(#κWF(\f¹?Z3`kWF?=3D֖2|`+C^pw0 b[E O^FunSG<?sl{"3(;g @P!5'VzϏC9h[ UJ snow$f6x)i.+sw!D)p"> i`J=Z*i*^S{--6]9ȕBhZ*[xmZZ- To;טa(1Ɂs~buiGGSf%oIfE.e4z-Ts|l9d>\{Dc|0 |%vIsj# OGƘ?.RSşU~!8`I_h%a 2j9(J>n"`rWrFAIk(k 0\ zS_u/nNYZME![5f|H[p!_Tゃ\Uݕ)4Jd=7P2i%]ZUU;ejcNcpēz;&"WjQjav@(tpr=m<VxǸR[l,ODƇ S#k-9GJ1@V+:LqYW,Ŋ TVkAY[~iVԆ'J .d].T x~^^\%g_zp[Qr(M%O;ȖDZ10gB}Ϗ_;ȗ^yoHmޙ__ó>M:4\ 8',b8RiKE$TIrlƼMɑɀT0MjqULOMq:`q*1 Rf9$oE%~V3£0yV:\2{6i\HT[ɸPxueKgʡ1!,?IMdNxuڡސԗ|sr%IN\:rhO|4~j}6P51F۞xg1!p^VG5JejzI}@}sB. j>KEmKϰXJb+"çf8ky[r'M"$09k %mks6oחF?(.|dB IDAT +%EC+e-Ҭ8q؉$(<'LIO@v#0O/!Z4àYRlwJi@=R]H`gp݇:fNzH5D ]0{.x}= ]^߻\Rס Z D)حUl8KrΖ<ˢbqf3NٟD(.DB9&AˆԬjɸ\PBU$-'rc qE¡ρTxR?Zi};:O0g:[0KeF-.+f-j"4ĕXYj̲reA>R>U[( >xƻX8Z5F^9EB$} K[L1&Ჳ-+)0 { rƅ"&ڲ)\e[ΘҏOe̐ pRz$.5\N +JN~L0,f6pnVh6Çl+@u T䓾b=7sfB䪂?DɉQӵ/C~+aiM" )p A⩕ψ9PAʙ-$FX䪓~`ψ9)鼶PeB_a]nrN8(~&Vs%ipl lKgR@e ei)ω%91/abҪD|B YzZ ·|?z@xp||So:5R =\ԹM~f~߬SڔpC$0 XJ*U`L*J07 Zf|wOYp6 uwj:qyÍm+R'P 146<Ħ8 9ZZ"ZjLZdJ=$7$[KfSyy:*8B6s=}q4Cۂr,.6W5s#,ڒr9 lj/b~L@B9w!2>WZp`j aN'ݔ„ 36/G$GjfSUo*sc IA@V9~9샡X)Ѳ 3pZY7TtL5rӎBD5Bܦ%`*%@ziX )Kрgv)bRٰ"'ꗘR~k[N%f`.c!Kz`gu_gpUD{OZ&Uw!Vr\61',MZѽY__|N cs d b]Y H|(Oqf7L?_ 7{?i`dDGǕ%S^+pNǮWzۧDB!9p}:92K yId;+A4%E<ZOkWNIH#/} ۞w(I9U -;mt}إƦUՉq qx7r5Щc4Kh m!/Wk  V䗒]9hoUr23qߪ7A\q1a> Xn[|a֊A(AW_ɫ=gr&9+Bgw2tuFf3DlbZ} 4@J(vp@(9%dyr ?&,m++!q>^kӳTma06$ P]4@LQ\!ũk9'?[XRɯ9öq Mq scϢ t#l{nEtxw-EehkD` ǔKY>ǤvD1wWb]t[pgUCo!z^ZnsTz5rҵ VRBp<&ϹˍA!M՘@9<}?V{}= =7llKPeC00pj+'=VO%[Y+t%MAK" }0~f \o5P7e_~RraJSl&;U | B%@fsΊ> n^Utbzi)b W D'zp]!h֡dl'9l`j[(uB!/3() 7BRs)_jRxpGb?[[QLJ+HY2&?eV$ * 7#[prpNe|vۀɜ^}#UÍ}*N1*ZApNXAH]$0kL~y{u7˲ʴ6SsN䰐Ec;q*$+_2[#vELOgH]VItg 2U¯ҳi=å@B(`Gk7a(BLjx\YBZbb ]\&r2Q\M0nqtPo!MPа1~3c* .9_?«S.i$Ie"t MR,\XtXXȹ\K%4)3w;Z}-x,܁~LXl/ K 60i},kS;+k*zېJƽ(O=Ɩsr6芮E},'7zp4$)j,-]|//rZH0VJ\$$uK3qN8[:S?Nn3-WV RIm˳λO  !9/;v:_+׿֊ާ:DYHQ*"(q(|}o9Q3کpJ}TmX93Vo+l!T#-ʤ^sfPԈ&3q=ϫ%+8ua5dj DҰ1Qs>]ShuGu?R t5dzrͲ *^'zp<~fg pBN+Q 4NvO0ѪVp  (ZL]OX~ |^_.'x%lr_3Тs1.r#-PNW+䆜r,(Ҧ'Ro1oʒҥȅ~}Eܘ/|Lt\~$$]T'n ч3耳~vew:`p3t l5yY r&tviw^{g$k28Cԙ]K>`qu@\(G.D#.VR R%ϝT.G*B%I=ۓ> m8+x$HBaH"||t01qݣHv2䋆>sAa"I=O\8 \ 5$P"tJ'҂,*8!- .F@E7>_4E}gkƦ9dpH_l K]Y(Uxwöu|r/L>θdtS6%> xV dFmWU{Drf3cZxHb^^ {ٿ9#EM/\j6Ö395,G_u"hk,Iɖ-.Z\& l`+/XK:OPKg+X W-Z*0¯^"xC'.$v >qSn\,gx'.KNk]FhZ}1weIM~Ӹj]1)}rO9UՅV%q+ZOaQDv)䵕6)Ĭ♍CR/)4d͡ShAlBG!8y) JI:{fu[;jNxoV)hm>W49HセQR[>TJMayǮJ]ʤ]N6ՂZ 'EK8QhUc RﯡZyhmۥY(YtW<< ?iN,ؼ/sK  )ӺJV aW)"A j+#R`jIJ Pi RM[Ǜ9㼾Yk[Mhn9/`,oBM9)uB @)aS^33KI(6pQ9uj^w@3(Vk# ;;`LUcb6'9ԚQP$xbnLԋRe m`gmNl ]0JK&];0in(d&<ے82s(ܱ/`J */DV~U&6䱠`+pi\-_Fӑq :Y)!Z ,%]J7T޾Jł;碓#@ 䱨l0 %eDZKtdkaي! HQ%Un5cߘ9zB!7(evd""[+hJ4BiL4st%xU}"'x_=lR'W](L?1ȉ6| 4#*o/m=3IkD-RTӦZBPBePxZ,P-%!hԖ\J)T$9'9|17v699;ްk;9=? \sL򙴴_hؘv 6:vZ@Gqi)CT 6S8%$WZ҇יj F"o\7lzZ^o:>>~| ,osY%?e/+egɒ~Jr:(5KAF^6*G^p'ΓSq0j L2M z}m"iZ+P1i}Z =΃SPBYzkz-v,4&'bM:X[JRp%e$qν jEuJ8}z$ s?B"˕RMi2g6#Iφ9]L "Z85dKkR1l7OWDGCuBd(l ٖtt-Q?Ӿm6)|H¢6$\dq.\ȃ7Iy4^ 7s&t2f4??P"FwR X2`ͪ+No":gaj2I njAX_>& Sd$1-iq`A*vS{ns-\ ·x?j_~Ϸ맇K8a3{FPg$+1:6qS([ #geoA@Px8^VJAȹ[4!sNB6^, .1гn 6|MD8ݤtSyEuԍEq0dv399 n_ ߛ> ZKH6e$BQjlOyCnb9œZr$49Ze0e/W&4TʖZADG⒚>9j5ѹie%8 t|@ L?<[CX Ϸ^ A3qce]= T'>г~s2s P 4C3:Y (׽>D>=)<3<,je @f]S/5̌ |ȫRL˶1c$~Ȕ&Bl`mgSK B "z晾9LQjY$5 s/$#h}ۯ |뷟>h_'Ee4dsa(}/m/ 5ZghXЯb@xQ#aX )n(&T0›:ZpKL=03R }`.u"?S`bd< Üm5 .=ytH5"56.?'(Aܐޔ޻1+㱪p0Nhq, xPlZJ*b*&nX>cJ,$Ǖ= ö]j =l8q>{>[XsZ9 > av6Ciyҗ#!`)CesT"P=h$ ۃ2.'@ц녻x- `*⥔\'܏L˅>'vD*ҾXaʔгwhb&XKql[}|b!aJ+u$jJl4TΥ sԱ \ɗMdb$'Džl3jTd}Rיbi4 OOHڡ3~ؘ[<ňaښ2_Io4<ȫ|7_>AGv逸F'`˛V6A˽udmaj=O* e9$Pbs5tġu %p@$zB~ّ ʛ;4cxзbBj 8 HzNt(]~#r)3~:E`l.R 4R1r(ĪlDZz]p>V =t$nEU<9}%~!'LPE&dsq0ڑ rjsgb$x]5NSڒA4>*$H>6c`Blr9E]"V茓l5i<ᰈeŮz E?(ڈD+I~glPSw~&&X(Ni€%\RgqWÈysMGKPlIiT7Ñl~1c?Z!?1 c,K쨎}8(5Q9׃7; BK`ڭK¨ZAfa݁UE ɋÀn(Ǘ?~MeܮsOw=r{׿o!KO֒8%NL|"h L g Y ?RDl"75ɓp6'.D[{]G x}GpyD\iΈubcF=&:} l1hJDsȁjUd=+CyݱZ); tph뎵>/"UL6 >$\4fFB #Nޚl=WnQOLva3HJ]?;w)l:# @TNL#R{00`q2t"q>ОqԽ̚Fȡhm C;%}QI0dN4!rj" IDATb tE>HlgDD5"6O}1&<2gb*+IZs |)B* ")nFy45)6'Dz3m(~-~!u[R F|NêU^#GOw] ~d%GQeGWj/\+%(Aɶp0=RpFFNY#3Hf/'" [hQzw$? xn[ś n͇o>{`Y_B3R> S2SF~_I"*D=I>0\M`AgWAՓ>[DXtvRꆴ 7 D .u/~f& p7C K80[eﻇOd~Aeqwo05- G;x(O 'L\`vjtpQd5Ēa M^Qj (`Q*?(vBjuq[lj(U2q],]lX\ Ƞe% Ikp'XÄugOpM!^J~r 2 o@BLlMTK;p\pYDOgix/UaUt뮝h&pz}prе*ݴ\uch?Mb^ ;Ʀ!P1.@#aDF&'dfk( do%5b u0XrZoa,%he;^Pù\j 5^lX1pӆJuN[p7`PԸ_ ܅$2`a>h b8NVHi)(t嫵f[ΩTC&/H7\dk:~HZJps Բ'QW9/zhw~ ,D󛞣.g(/4#`R((;r(53깄r)$*Ө06B#ʬmz>_ _/ه?anL^uIjToc)zݩJc7OoV~`=fvE^*-v) Bd&/0[^.[mNz#d セ\qzH~)6'd qLzoinUc[-R&OshI9u  $Ps(2B)`R9?ԯ |m]Bo_ MR`EBv_҇H#ZܻJY`,pǦ/daWO8";p'Mhzk8x*"9(\S>L AZ73z × V* "j9шn4rM!@`*]#l9!<1P'0lMD;E-;.0RV@Y 8X> 5< OUNש^a!9,N\ }u4?WEmtI(YH, Zb¢VYBRu>:*_+zo\Q ˼$hwecH5fVcG;DsI)ey|5*?g8,j_xzsNC ʒraԔr t@Mk0N]ͪCG'Qe,@.(PҙjasHuB$HiM2m($N#{} o$7g~ijl N¥I 8̂.ny#ս+g?x>\ ·:~?-!H݄UJ]Oley62Mr&Dsb(wypTfHO]vI1N4I4 NG! bS9]T`ɫKR U3]1QA<x/ pM í'"SThb>ͥ"It#'vS<τ Zo\)!lMLw76c i92F#,)Lʆvne!gY7ap}󵟯c ds}S˧IPHFah/Q+Ses'BΡ[!&i)]-zp =uOb#|S40+N_ol\? F?w~RZP]Mj20|>fc @T&Z Q/y)NZ㽢}R\|qGhӰـ3•3DtGt #[⁞_z~n~^\ ·\__]-f fXCDZ*لcnȔfg'4`ˆi(E@/|$g겨ikgowl@>\G2qbޛ ZgaFϷd]P3[RTcZ$H"&v`oհrC8"4[H4: n,V^ 2bZR;-TdB>)OodO|qP*n*\!u,/O׊rͱ8${9D&#W1P |ݽZ4y1UBR!ς%ʉpe&f7 g)~P]σѡ7M[|K?Y\V3C> E~qi̼s~#6ELa#l͗"` H׺Ǡ&BEOis͠lTWIu@PEg3MfK4B^1sTba& no|y&^A_y?gz]V5'kp!5=].D6C-)ȑJZ,DT,R5hmuAhp d,*]F, &xՍǑlbr:Mz_djQ>+ lZDᨥѤ 7m$9uhE8,iBadaTPLDz4!d83t>G[ݰ s)eOH•FԪ#q@E{vn~6|wlDC<(|vR)5Ɲٗ8U 1Pt_*̠nq- ;]>%0lPxKv|mC!w6W2}ef^ȴ7rJZ]JW?DvJ^^ ">ɕŔ! \tEZo/$W!bΣ1䳑wfgM)Ɇ+[1-Bˎ0 Gm^_}etX&6̕wtM tx9[ւ(,l]eAcw Y 8jd6=B;v/FL ; apS["@TSXu}27ǑJKN5p~*'WcFg ljuS`]p&20-Nl*4zaT ߱ʑ5a;MZK8_[NԀQ݊0m hw6#p\.?lfwN,窵gTA%tq{KLC>9P,~fFR9*e> >3PTӳ"M e, Uvz2"53f'Bb`2Z$gLeD$;]"ś:Ϥ%r\O^JnE,k^\EdJǸG~^AΏϿ`ކ O)u4$'*vr{8~;W8PΓN׎D6 > >8Mp&aS"dN3fZ6g sʡɉElxh0$'!>pAkaG2"#ߧaOqro?x;Ja[6IZ,nx,fx^ɦD/T@;F0kRڠV$rF 'r]$!?jyr4NdCբC~"{/\e9T箜r)83\`(?`W ~?3HISA1=e6lZJO(e|!I 35HCI `N 7 LF 1'"3!n:dE8?j(F=9?M*ҝx٩b9nmɪ VHzeH~\`l0tsL8SlI'M73k?{g~_ ¯u|~~Pȳ 3ű uZ0^ 0C_o1 2le CSھm88Z}^ wu|z۰Ϙd߾EvçrS4#FJ$mٔG-G⺕e \vZ weL\?Q}0:AI!' E{-tчBDfzqpmXp' 林k& `v23<m5Qa xũ-qn>LWRv.<Ɂo?la 0 ۼPr報mLMB|f_P)z({ZPi1a*Ɯwׄ'k{/i\48N5y9K]j>8){8}3;>V8;~(:H)RRP &5QQ _Mn!yfSD(?-ƌO@O=Fh=X)|.u솕03-BLrA3bA6U}/haںZlN5ܱ8'NzJ; 5Oq/!U>7xy׷v6A'[iD~9Ķ3 uN}{? =Zd(Gqahܳ`ㄖZZ: ۶~$3>`\PPf017 $Z|> [Yٻ nLȒ>9!}QS-fL:kjr#\&C肁Fv;( a#NR{ j6*QpC0w 78YF{7{.f;_*(z<vr##^}rf'5P5oCAGT6zFϵ9z⃧+ZW}S)`Ăp]dQ wBA(P|4[hBb~-== uӪI7s.Z; -[dc +ȵ&ET*4 $ uY`_7>oVhYu "X+WHO[:CL 'Jg[e7(9dqȇR%%``&o~}'y] w_{{g`8e;yQ&:sp aDlp3kũ"6-l tLJOSAI7t%H8Aۧp'bbl'$\!tpUKx-m,o0I+jQH3mz詟I&~ qo_:%c9${d'\? ?M-9]zv6Ƣ::͎pB+2g3QBu$`qd0P< N÷.XyzܞKOʜ*))y)ӮdB6HB>gD8\F=5&,`m/C <)3dCNfR8Q ) 7@IJshS2Y akc@&@& am|@HDpJyp@4cc`-C(|d 'iNJQvP~3 IDATz~7IsUw"XÀ~,'HɈh,Kt[kt6'Sf_389DlEI K *Y73깺ۘwRݑͰB gF2< + RbR8׾v~{0^A?h^gfs"%y >=[Ow"?wpGpwA/DsTb0o\z<Wcdph]B~MDq}րMŶDh; cJ M;d /Tsģ譡8 -'H($$r{=ɚ>S)D<}ߗ hb<؂= 0޾ye("˖HWZ| h?s_bw6WYC%$7T] br[*AIWSY Hs9Df\|`R\<&/dB h塉?#Ζ4I'N Ɏ=Ys}$o9J c,y4M8/aK GFr M&D&3NcJ^%OFwM٢\ѿj15g[-4dk<Ʋ2.}Ao QrӪq^C`Qk^p{ j!?4N2iG gs=dfU xHdZP$}&'$njg$'MSYp`Ka7l}`}!^!eus{qRPhFAț!.NqBʀ]egXԠTm!oBB̐{O~[;__A\o_} mYiZ1!M>_12Dib$4".T1rSj$s ͞ݸcG. K$rPAdE#񋤬arÛsdU b*C`/,˥~ =6V}d@/}pM!ٮw瓖*ƙ& M }- ,씼9NՃV kT4lFHg+\ҴmB1v<WSTpEKѡ|Z#c'0m+m2I<4L`$|x<kGBV\z.sHI 1Hg00!^=ˍ2mZDzN @C|~=* tR59,L8 qt85)o s6d-u+pIzÉ7Yco7~o/|~E t>> |xn23[;`x,̯O왇'GC\>d q!i&nL߹$ÀLsx`NZp?u`o"%B92})EfFFIr$ T=Kj b``T?IAS5ɢ#-AO6?z0Γ m9m2AԫV2so4zAj^pU%t!cEE@WTƙGZ  M"E/ )MbAT>V'9%T"MP*щ<u/,)$FL!*fjL8>yU)| v>:h7E^lliKX#I $]R!puY,%uZ4)LۜH^СJߔ9>Ȉo wFRyld/ %z (Iڣ:9 x+,So$AHwQ&M)\'q!O-:"c/ BLnxTљpvPrСsfSڋa )g~Sr)oS^'C( 1 yPbOo͚M Pv3+bnz@|VUS()j,/""y_Lr`3 3L@$W"hN ;/>E "Mhzȸzٜv膽0 "D[ kl@#ؙm=_x^߆A^?>y×n$M"A/$#;94UK3 S2E>AvM}Ires!8GL|׋y 3#M8ӂ94vcΜfPk;%LC\63Qhj8(ü0˚}!n"7GE>Q4oц{ jV _+q$s}? \GaB3~S)*yVs:_ d4@@2 ?(+AoĶD H>Rvd')\KV Cq\@GN4RQd 6%`<:?w_z}] zqKGlG,,Q{n4+HcxHf60C2K4Q Tޒ qNIVZ_ ,a\ށTZ޶+q=_ d0&M+\c1qYA=NG?w?Ws-¹9Oz;L?ŀ\/PMR`݀#8:DvlTMZzV1z0gc;;=4ݮ` Я@j,?W;q>nT#f#j1h,6 4>QP31Ou8btM)*e+* `m ̌ 4 M{v Tx~2+@Φ<;r 7AX67' e~p2IVR̛Vx\Cgmq @xggvဢ e+tK<Ƞ &JhZ}n@|*`X]ꑑЩTꮵsX pZ|F)Os$ΣX m ʷ< Djn=UMΕ l$xE3FЎ< qvB=Zۦ 'o(yev;^pV?zl5x K|#O\Oy#Dh"M%t!j6] LJD?u/LdM'~ l/{E>?>^/ *w&.T8.]kXpzt{^5E;LnMavz7U,4J/lX 킳'djûsZbuR*c\s,<,':qvP68umn8o>Ў)(, 3`jzMtE|Mn]$~?s.;`l57 gljh<ɕ|}#) ۡq;+:L(pRt ׽`Vf"}PJmwqgsW`p4Y} RPJSbr$g[@§b_Lce{l05XŞ~=9 da%ShŃv km!Nr$BU֙`crbtcA A}L^TL9Dfǿx"F6wT00DzQs| \8,정$="hhu/+b]\%ؤ<5lň[9G_{_z}I'?} q NL$WF''LbR<]XimEbgp&{l @vq`M$f%J 3DŽ9i`^gARx@͝޹@Fw觡u3\a+Th1?SQ߀&{T.! 'Ϟ+ղV va.B" а85JA~lGNsaM(g$۷ї/evzϼ/$pjeѪ1P$rECrk!"S[ 02/Ɇ` '?:U7̳+|{Eq0 uwJSHV8r?\{<0m D+@$H6cui̦vmzKd +Ne@/O~&:?~}Jzm~ ]O}rc/Y1YK 4ӎC}y 亖 N>n]*D R •pװbo2a밝E0FB;Amkq e L"A|Gac NClEvF%AS}4<ȇ"0Wiz}u5 PQS/ST [uJgpR:k%Pˣ.NEJkz]nU)p,Ssn_ 3K)x¾b 1!㥢 ('qgd$)?eHU 7طBJ,02?&LT:R`r ~5Tg" մ|a(}$l)L蛿F1KV8ZG0MINm }`l 8r`U}}~;X @M_o>+l>RC)=m HX1`qG lDD7C>V8i*jB2"ߕȵ|ME]WX7ߏp?rܾ~oykk}́V0͕dƣ S8ܒ$D/ڝtw:& : gN-9ZaNs0'GZZ7<ɀlfョ0j*LBWy^o}Ɔ*cbΆ3x;R`5l|?L\+<*\CǑX+gAS"JG8NSzf읍LRZq5\ᙡ?_E6. }=~-X3" 0}kuU3Ɯs;큖R)JJ#D!ĄQD$0QiD!$hԈHrGD(PRz }ךsg̹A-'wwkܞ Y􉰓1 J0J0Ӭ\ lAƉ,R%2Le OhQdK!HI AY";*I@ Ԝ=B}{^Yq/h/7.R`s)hf4}5 s#6(;9ZuL[ ' IDAT:h-z!Ab):\I^*etD AVA- ع+8+{B<zAk@.PruG OéFXXp?#T&c 2-V)Ƶh͐r{@sV;=* ;% 7yB|0#kB[;f((Fm /THQ ЋQ0ʘ<^jfA(xZt &eZ8P+)WcMsJzLbTfYf;#pg0)nð ۟ti㔛L`kwfc`vS)> %s\eOLvm=/bcCwnF&CþX S @szHW@qZ2$!'Oǡ*t08I}:]es q /rc iJKՏqɀwC`dD&j0pEJ1b撧qW/r{7#^RqG̨} _-(,3qčsuHɻ T9Q(hfQ,p* .+2GKV$!EϽc91'_+ u\=;XA$P8 f$)$i x|bd+]#-#`X&p3`LRb۸ߌ65i@axLQPxþsI!q  #77Eg9*jBWń]e!-{13 qn2(8Xï)vޣxD|NDaWmiIQ$5 2СA+"I }WݿQ?&qK*'w=~}m1yhzmiYdȉ-ZG'/Mbl.B ǞXud8psZж~㈸pHy*;jUs77 lo1MјT0t/D2 cǜ|^j0A3?wݲ'+鎣Xb:0Љ] s%F2E9(z^w`!qkS. a8 86&#;o!ӝ*2:lcZYHg>>0Qs-B@Gkk^49I|V&dRXQA3 ͂SXlΙ!\'u EAk1! uAk=m_| Q}8Q ݣxJ~U1@cm 0"۷:Q -2gI8lt 9EDs37|{ pjZ˒pYP+pk#N9ߠ1z45w؜^ mf!?TV$`k$8XPXʯRv'Hy6+ᏜOLjn$ HӒXPL `/%)~8S?B亰f#ϩBA2A)di? WM:쯵+D|*,_͊mШXEPDvgN@p&$T]4s׭ gDbNP]XX? "P3*㵃qm.8#5O5;EϜR ,C\wUAɼ0cX$vlow; ̢' ߏ= 3ߧjI Jy}įn2`k8vǓOqs{bqUsv@\Qt!}2N<,r +HȚ|iG흆߿cwvpl]]$ɧh=c \]aw#:7{[3v)aƩ@$H|s5A( 9P7cwS6]( cՐs`"]S 23Y9b {1F1bg!~YbA0ZX ~U?~:z*)2Yb?ȱgYcq\)'9') 3.XL t1=v4فc39(јxoRQUq<)y +4lG1)c LEw0ELeRk2Sᄂc@|o8ak }Y{TnquXm?>\1=C~rw ;nxf\mTX>XPWA.W7!B!V^y7r*B%ra%{.Dc0ěz-'zd8Ge$ ~"V-?.M#;LH7TE1\0Adi#1=s:Y<.9,€mz`0(%@Ʌ \ @Fpr3@U~(|4+u#> @hDq8gAS %`bʅWk7ΔisŪYpZ txu}İw 0 ] QuO:%rae&k~e|ǏxiQ c{~? 9W 7D<貢իÔ&f"?EE3cf!d4SL"]e!L$bLsHD {>FQ @S)8lrya+Gb$'1f;:Ƥft+7GFaZ]SxzI $I->S -S!pxxHIҝSBq,zzOPs^!.02ݘ39i}ss+1dr䂜 LPOK!K'%Oq.PprM WF/mmGO?(xb{pIkU'rUKn K*'jc i=FuA%FzՇB ?0Al3NfgrsDeҴdG|89 tPQN!@:9쌩$8<xPO aɰC,D:{r|Fv H͂i9%rI*h1a!QlжMHd^i` &Ίkg"/Pw伳OB.aqQvu@ ր:\$+C>,z*LТ˲@d8B0b‚'6[_kG?GqGQ֟jo'M+OHXJ 3,72;씰 3V!%݁q*ВkFdTr7L"ޡGh[ M܌T1.7v\7V% '!9Zw?躙s!צvLT;,m3,KGu%3҈zU<MSb_qLQeEk%,KF fzj.~^RbcL*@3!Мf~]B!˾tO4V*}q>|G_tg/'{RܒT NP@OqͲ#`Iem[tiovi=j?G{(xѢ?~?}hv\x>() Jr^(k li Ԏ AY9Kԋa)ݮRӟ'CBIao =P)/;R.+tQ.gàgk , ]B%#̆ށ[a3(L>Χ:唯vYfSCCkW@sqsS.iQjPlj7Cp IF^t2#X_+xV!N `1)nt:G"yP5#(~(8 %'2GG&vne)vi)tsFNۊ.)gt"kƶhհ 0`mv?]g_|1G(xwKO[^Q%?$P Knn<{G8noA}pM:cʲxU.dA@sUMAOPRI.,EQB;͙J s/kf#IȘ>{(RS!Er2.xZQ_"ӌ(`h,X i$.*ju ]fXfF dx"AEQސyI;[`)4 lsY *~J1BA;~ \TWlw<Cw-@-rnS)zi`WX5䛂n 2: "w`\:wYڑ[d < &<('9>'l t7y7e$z(dI mu,ccf).~g|qGpć4'쭔iY :nϊv:j A Е:K⎟: ^=3&{pYGw*PR Q{gFw>1Js GO&R;NudJ84DL8Kf( z A+e C ܞkl4TxB]y=wx h=#jʏۂt obs,mϬ;/#wv_}髓//oGr@uEz}*Gl%4E.5vɗkor&ڮBd&hAy.*]$X\M͘($˥ n0so+:.M*o7+P&%{erBY|*(9{rpcUa1ar%9`YͩB1& I =|,9Al0,ڷЇn $^- ҉KƂʼskh49=qt(tFf;K@j ۶ޙɿG?"~8@89NOUW$]P HƑetӠph*y}w_9gԵ>wZt)gH1;z cؑ{.Gcq}k HÉ2GuFG5m%a 8Q),_7Jvc @`2qMwNwR\l." ۨj) \ "O!mD #KX m!P 'HC.;̹JĉdZ 5hTPǂP*iLMӜxk/W#qz铥\ZJ3 Y@B爹7CJ}Ud$\.-:q}"b?g[5#圑B 8- kv[;JhD] xBH.I;P= „8EK# ,)֭Pm> $)@l(cz,,@B@nCcz(h)Z h|~YVPS~arJݣ R@L@"58ĎeE#q $uL:fʤ(OP͠0e2A ߹X}[s/ሏ(eOeSZ0mfI6XJf'nn -@Hn\ :R!dɁEÿSFu{4rai4nNYnV /tEPut1ԐhvwX`&wm%`WRZwCABw.S8kB(e#V.kӬ> NPmA$,$vPYTe\q:/ aHLN$ER-,TQִG,;q!ሏ8!}B'V&ݒ #rB@DGK"ePhe7gsFNz"հ<#K #R`6*|CE/|#KaXyeQ\VCm)v1   ,)m$,n=FZuGC€p`p!Abs ĹΟ`9.t^tR ֵAT.Û [_i\9 ܙEМ[:'OQ m6ųd Ng>"!6vH?^q?ሏx7I)YRY,͑ɧrwkSp r puHhj-mYNzMYE.9TD;XV$d2OH)1*2 CX:Q %^մ`LC z޶Vվsy=a/x'_k/:gfO(%AvCN)P6wGVv}3F>t*֭")ͫh҉yU>Molk.@:!Uc)-w9%Q[ö1!FRqc9C! w(JPH՟p, CJAfByS^^/@@J̓AD$KtUX|;=%bH] 9wgB7yA m1hv ab 2\'* Bzbp1(S'y#h[0CPE&Տ=SS%Rx1U("l J4хMP@X%ж;q[ H,<*^VWQf/U{׋!<8 #PD JΟW$}%R(Z4 ZnN PwHGv|lv, j7 K@`YF VީÑ0b8 DtrF X'$ -}pE HRRhOv.|FAU*酿.Lک;(4+D gBjEwդǶ_^qGNGq7-{osr*o.r鳛uP¸B1O\ G]ǤLl)TtC%#`NX MUd0a-Aiw_NTP cx3@N exWj5> b_-2 dhTqݻ-_?EGGpXǝ?,ӛTg/7SqwwE<{A@#AmM ZsRf̴W*,0]%bA VÐJ9iap%ʮ^]кQ.ZN%m3q>-؞mFC(A<ͩ 4R!kb6v2/"NH9+VT@cO?~߸0iuGqG<㩧?՛Od͟ٻ|jʽ;j2Aqt@, a?n sAQbrVJv[5HVx5Н 45 i>& @Y]ةKvܞ JQ\` 4k@.%wG)0&.@"zϊOul y#xIQ qċ>~ `٧KOKOSTR}.42@"hK'P"~tV,zFo(’(v2uD1YNEPu3-șSޜ= BG&EvF)Q5A*&6xm.?qn>hoG(8/;! .DP}].zK΍Z0:/B8P*ܳ;]5tLaAME0-JEaf\}XB, б uCӰ_@)+/fF7#/{JLhilosjln;~ #>qG- [?.1R>!~U< ha JNoA@S&(uE8 $X5R踘;@)$MX6-IѷϸӚ7jw+=ե?#GpyqX|\*JU}下载ukui-screensaver/src/screensaver/assets/wu.png0000664000175000017500000075607615172041106020567 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATx{ewU',I%ddBi*  veJ4ࣙVԖE@/':-rFEui!Uq8IWhHR|{~}9|j{{&LWЇk7G%..~\|Q}NxQ 0Y @J f !b@@4#f $H&a^Tϰ:7s\tXǪգ{9H(ԳG.9pW%2 _?U8뾛0aBL0ao]W$HߐjDzDzZ4J/)%9V b{H_F:W<Ň@/&L0:&a„ guiੈxF~[xtZʲRJC >[aug=`„ GLpĩg3V߂)W rfd#><B##~g0 #W]v`A&L L0SPxE)Z[ʓGDڥ1Fx4ӟ??]?>֛ &aN>JV#h;S!䵲s٨KYG!"Kd4S 8_E&L09&WVp(Azds+| Ӧ4#!Jƹ4Pz+ p>r^a't! +L„ 'K__?w SWJk b:zbEEV_95sh MS9~mg!Qt%o9&g N|!w5VD2cЗsC)p8YUUg?0aar&gvՋҋ>RD*bi|-¦xGgDz#ɰzF| R.`&l1&ayҁ)}Z$eįG VH=5Q'GHģO<&&&&ay^^ ))/zCۧ.R.^d] `4-OѶ}$@i{Nc7߀x;~O&L' GO>E/SD%EnE:r ~d 9,Y"eCPm'_mc|͓(x„- Lw8sZ+!+K n4BT[[ -[$KAˎH"r{U';?UA^)o>[Ø߻ 1a–`r& ~~L&l&aCwV73`'}->_8Б~{}QEJ8,!rM~k#NbߺZ5 g>^D&LX3&aץ D;Rk+mN Gi] %[#G/z b$C!9QZu ٨ yG5AG>4>җR oM7N6L„;=|J|i\-Y*#EZZJ] ?ɑAxGN$Ɨk__~ AL„=ű/D!?@ņ!3{%[ 3熦$]/Ӝ#h9<;CG2^9ֺa65E$6ZwwO)嶺LPA'8qju " 5 XѡoLnxDMPΏ8rI6IDGI}e0"c\]q1חH(ɀ40rKn„196cw ^?_+ǵ1ǤrJx4J f@$9H#!Ao+ddv) ڪ@:zzEMar&GOy>#?kD/ {<Э\Nj>PAk*_{rI2[`×&09ֆ:R%`GbТ]$]Q'" Qk8+C@KY O^ɜѶtl;U˰GiFcW$8aBOL„qCߗ~>*'|\+/S֞sfJIw\$<߫\;FepqEʛR:a7oIUK᯺VV֤Oyւ!Hc5uzqXrP89f]ڟ}br ~Uf/&( 3?ZͪO u$#ho+PakrJ!/Q mV Vrt³ۼ]>a&֓ H,u-G΍ivhݪԧ+Z!ڗRY.UNTC 2-i?wL``r& Ʊ7!SJO-Uc9V]AEBͪxIAjRnc`BH2B`m8y7CAD"?{'_JE\$ ow w…AGOdɰb-xhF P \5z M.AvF3|sq8b{MeCV-6BeKcγ5Z[$x} 7iDž)Q L„b}cUOA+҈DyEi~+SoR2t@ټѼd(WQxj1d(6$UC8ÿ } JN=zoEDRlOH$Z^TmIPXY$I:l7]b&L„0:ÀfHҺZTH<ACS4@4+[&-ʎEÆ ]˥ 2-h~lYb Y {@D2yNi)/%BOYH鿿ClA?Jˊ ;y*5rAWS#- pAP :@J( !թ @1$3#;I~GNS[VHV H՗Շ~;$Ȅ 0AʼnӻGR·uCiI)c~NB4}OȪK1}hu\F@D,55^%;*BӾvÂJP6o] "?w4:_ ?˾ÿd)K(lctNΕn!xiiCdkK⁈~לpL 19:8qσפ^A{Jˊx"JҸ,{R=l"]`H[%nټr(_"kZ/ƃoSdǩm>5 ]QG+۞a(_K(Ağ?r'.8L„;'c??EA)*f$}W$Aqm,gGHvys\C #2F\FƢ4%USK hC:oq񌁜簪 = qjżTw'VIzn[_rldTU/ bY'6xb{XhئsVU:V@XR2HP-^\LZo,Ǖˤ9iU$L?}Ҭ/=_+H0˷qR4# `+ /Chm-UA[q~{B[ -dɲV׉dqֲy3q~h&>.ow'R]ԍW_~N(&16L>{>f͌#*jXcHP8(qdw}dGFÔP,C/lą 8c唝 Y25e~nCt.6y!N1|<қn|֡O#4a/1]qtg0ptr+M/:u,eF'O4ſS8ץеHScSy\k:=)C3(ǚoSՏ8{\VI^~^*#Z%p=xW27_I Zcns9+BD9QS„ E#Kj4s% XFV*썧nUsM6+̍'6>Y5RKn5tE %˼ _ڸqǢUsJvk>[\M  $xÑ!脭´Űplg]sQIM \akT^o%),4Z~Ӵ Ѳ5(MHUU6. H}@2޼x{(mV]wZ޶υA`Wg)hǁfr>)Acw[ ǔA8vꁫ! Ϸʕf4EQ|VdDEd.5O{2{r[jx[riQk>PzׄDHFeH5"YI^) cOԉ,Qꖩƺ}ۇ)e8zͰ @ɌUJ( ՔhI4iѿv&g5HFB͡5<7DRYnKFH\-Ҷ~THI2V6@ku,b;2%{>Z㚒YV9o.5kS:q+zZ1ev[p$_RPVd/q>|D)Uȕ!@_F| Lw) D {yDs";CXKO;|5hY1[a dTF]DsIퟔ Cg\;j"JAe-2ҜQSŋ'-M XUyoO&:aL[ {ZQ\C:boޞvqj#QoGqk,mՑIb&EOKsh "ys<~FLץxش#M*!dj4=#`9ͪk=tmAs)ZƘtBtNss<\5i_j  n_kcbNG{4~k 2DO8'>!iaplHo+S~E:\y*R~TR/ZJbӡ mύn"HO\i .RYV b2Šѯa/x򱤠}*ky\FPXSʙi%By5cʮo4f*ДS P%5'A2VH΂8i/ɠ9MYWv~s6J풜Ѳ|NT^mCltDR%:G`C):/;{-OLA ^DFp٩rjz#@͍U(?^ڎ(IkuQYyѲ,t,-/e 6q|~Ei#Lo[76O Vc@ H p!@aKJE GGs.K: )ٺ%Qρ54R<Jzu6M$Tu[jkBh+m(%!R3 2۝kN4c9_,RGAPkE^OPm< IY$kTHc98"aW]&0e֌;}M1ٮ3Y@_Zx2ފ9Rx,)X^6yk=H ҌjP/NDeVU/-|nH|ف:%ыf04^:/%տ90 09kѓgގf$³IҲCA U`GT<4>$jF Zd;;%%)KH:IR'Q'EfH]~;QPG2vHqI1 N+Bdx iܬ~mF mC)%Hr|g badrK[}axzuy *Qh/AB3o͋OV-kHkRFMZwhDHn܏f2II!dinBy<:yi̹  XjS<$9-ZD'ZҰΟe|O/n6119#CCC뇦Ҹɰ|UV}KjRJ(0+3lsY%/}nA k F0J:L^47݇@ 0J2 9#wygM01m1'Ư~vx:O}? XU<|A6MF"IվpʗPDD1;Z_Fss>7u֕iQZR=ϼX:bκ>}W'} !'4p3 -( axnŝ Dףϖo˙In득ѐ"z(8*PZƒO)ehsGĩgzBia 6 0n")J+J2ԤDSnyI40.ג'dfBKJҶFYk$C]kNǂFa]EnR~z&\"cKǁʶxҒ 31IWp9|x,ԅWP/vMP19p\WfAC:SҐG V9Ai) DZAD-ZAhwwL!m˘T7U~@5$ bAtI|DHm57ڭGn; %,uW HQCs.=:~ogsxKs!F:z釿fǩsmJ䐎F Y726(zt}-+KC?y`&m5P{5?1'vGdSXN[S֩>y4Ǽ8qzH]ÝlsdeA$F~~:@.)-0RJ=bI)EӿR:^4)ieMEM$IZBkf~@{/[z(0vrVrYkFז -t-ZYH/`8{ɑ_s",Ԅ)Psp` (x d)$+Jߥ֦|QuH6?٥Qr|csˋ. ɑUidR2J$OQx 4}`ecAYGO#!F`r8vje`}i'*MUJ$Z#*q:49ʑ0xYhY?:"Yɐyc9+zy<~:Ok_swG${`dA9UJ;8&!cp2cE]{IJǒR(bR4H R[wTXIܓFODh> 5{)p^սMd<᭟?ߦ}*|FC:!s|\Je)Cd\ϱhx3[P^NH;+5d 'tnZEy.\aowu{A*Ue>'!#fi!d`sJi( j"eyja6dqdK>"'iRnn1e ܾsڪK`E5j8-m{[@)AJ]xVoqdy&F5+ >핔~):&9;_XFR7Z mK"&!Juɧe rf%'L۾Ig3Ιk}>cr~jXƣt8bl,)o͒O$䏣|7sGwzdÝDWc qQdsPͣZoTa3YXo-1'7qP'O&CC cpti_=HiQXxݠ+ՎYm"Rk`ɜhR3r|Sb%Y"Q]4ݬ WZ4'oVicd]55>(i <̋Hvi4^U#'[Tܽ rK=@VBJցkL"`M~Q{k[!9ςl*@`0j,XXbƜOVu%l}5Z?s FۭK]QHNtD^MflD;31#U[\yb lw*^Am44"͓GR^VO8Wajn=,30񱜓\çH[Kr$z"A)zh&Aˊ4t-C ]F|`D/{w7xQR}/ NSZm,p7#g\/NIʑm9vZ}UG>IHTB=˿I9G5xT[5ZZ]~^X0QΕRb@4%LNGJE\ѡzVD߈a' yJeΥƮ2|HgUq"/k̴}+jBgDN/JAc?x|Zm ܹ&C%-AK@ko؅D|Sk9ǝ>pTտK$Ûv?D 0-){M亭CZJ~覆>j+QkӖTA՗W[8As.2o!ٕGJ ZůAX7AdQ\i9J[zq<%mᯃhg'NHi0^xD4Nv>p> y8}FrΛZd)9hH#ͱR9t{_aߘ:W)ul[փC3fyXQ^4Ƞ}*j|J/h /_uEZo|KD&Zޚ/1 ΋T/cB|COJh?sn%Us t^2bu %#g ("#U`<*+> CP>ܰD@7V{Ar:5 ĉί-c)Y]ug^s噲H$&,\>p!>#ks_z{-A1SS82JR}e4/MДD΋kNAqι<76R*d$3#V]YVgy6d50_׸ڋFvg1:*"jN.qQvjJgyt;%u+k%:7]}_s^qK c7z=6OH]^^q^ nT_~}PUWreeE7.62@bJm#'K J_D?gWVpxSƼ, \涜r33ݔ`.:ǤdQ$ t$odz#3ICN呍zKv ˯_t(}/ok- vhYnd֬dzW>sR 83GwBJ/VH^ 4+P<޷ݭXՊ M1pO^)>s^a i%k*&Ty.K#Dۓu%Szݚۊ<,ꢵca1[YZ}mh=Һcrָl+#l9d}2 VH@W!f9Ut qé_k9ʽl.J# %geܖ #~k2e:)rTjZЬN8a Td7|`kCS2oxʔs.B :$g--h΁4ؑPSg ~ ү8D* 콐ܫ`)MC9DuP۠A22AX]H"J5Ȼ|nn+KReVNDRfK`U%t@H"Bi4"]y!zNpҴ%gc9u( jqOܔjɋ((AJPtqZf ٔ ]faʧ+ం$ZfRh.NU^x [.p΃AkVQdJ=ZF! \HnEBy_n.--+RnRZˑlhXIjn_9O! )BxC#ϬHlEcg,^{cry^/%^FoÕG.qZ}kzꁫgh5ɷL0')jiб=9* z]3-PO7+ă&Wx[tJ GruU9G][+JJ"yMh]Z?:Fo IV7yPG[s5A̯sUE 㼹٬y^O7j|6#z@/XI I=7пl?Vh6NA8S#3',76^O6iqt5>ڋ%>74+r,t.!_+ҵ|y?.EܲZts3nJښ+ A+D!9ؐqFm G8/̻\oq*JU(yJ+_k{̚Y2t@Q^h>xkc#U?B̀S#r9u,:>ci<}RZg|=i.%f NklyozUuE%Y:x8tyF,ZVm9X+11׺EVfD]tu!=!h>"R V$V"4r0IF>:f,!ЋL IGQZ#hu`sŭ7*g] fkO>E<OR]}Z{ HBDxoyZȆ @ا%:)X+ce4eAzc /z IDAT,X%[7D> $Az͇/42{v>T2(CBqLvl[(D, ^*>~4RYeP19}䥎4n%s(s{ :FzYXml@< 3:YHmQnldoY>FľENt RɴIXr1HSJ_3?){$JE>G4GP~\v4$S"e=$q 9d9K|:ޞHy ^K2Q-GB \ c*Tþa9wy+ \NK2W_#cpG4NRJ>{% G/bMJ 1 qΣHʒDyd^6XFeT)mE|ovmTV/$aZ,H嵾)/7^غۀN^叿!V:0hZ o~Jrv] cϾ`{p}̵]R:O[ԴJbްEWr3ETfPSY_[sěOR=PɊ;(yȘ*TV% p ?S@WeNJZqH8k:ƚCjO*t׫/Xs~e%魮Xϻ^k `JЍ$#F},i/E4cx"OEYE +*D\ފ;mUPx!*OХb9=]V2*-KEKQ7Su(='bUK{$U/᎝3oEO vLL)58&mHkFC|Hyyp0"]~aZwhc .Ćk.19Τдr2.iNXNʪOmṃNZ6qK`iy\h9ǖέN,mgTolVzonώ5|\1I>}HJJVǒg q%O QOtZM:PKf#ژ̑]Y"~cʙwp^BHfhĮZf%[oifM'7qƓv=jkzSytbo\t yP$:E1J@e/j&ɡKRj/@eBt&1譣!ti{ٖuBh7OknҲ`m-H??9"~($hۇ: Ra9bQ9w `;ϸe9p#b3s;c"s]p 4w7m^i U*/ZXgle IV}I!xH۬9eIq5JmUr ^|V=GBtE$kzp;6%SlmSg^5|N `{Ax h m EEIO1 F[|K1"Jr̠GA[T*yX $E;= AzXhvd(I2%z7^uks06;ZzoKD'Q4 (-eԪ#Y2W,@TOϬEG]tHKd4v Gڥ^#2t7-CRua/7J,">C:2Ѧ^\K/>({c4E#3rS(\EtLKjƲ%dyT7[ε5֘t'b֯QXku}mR[#A NGB zCr̩H/*IGEm9ZĿ/Ƹh4Ɉio(._њ+ޙ Fgώ!. n;-3HAF hYiJiBX)dηt2Aʪƕ֌,nH.Q`r?ЌХrRy_Ғ^NpZQ/bOe󢴥r q).iXyI+Z֘#$,JdmH|Rmk.Q0.PAҹ>6JmπDi/C*$xΆi4A,z>}6tͷ#~uvO՝?KsJRc`,:Zm=Dld{8/Z6:"h׫uZXg7Q`@DH_r*yQ(Yj+|oӢe_y]hPHHTG/ˠKkcLpOixn]}\Kgc4I/K|u'@ ֤c?t-i+Y)"ɨ9'c9Wj2oǽ~!@3 i<7r?ez'#2Ƽ- )s@3NxC{Vݴۀq9f@VAq{_@Wd̲,sRyRcL>>rK)ѢiK:c6Z 箝{VNPF1:hyoI'K~Ţ?؍5_$0$hd:[}VNɏ 者EER:-Z^;'pǸHx؊짵_dэ$K]׭Oy$v,G ҋJ*gtp`ߛ@Vyv۾tQu^| I1>"s_OLJꭰ25vԷnbARREa9)ю9uP<lE?ȏ+1.k[In-*59+źЌ G"{ʛ1(;quMl3q9x.=3c{AIx+o3Q:KYE{(F&㧬JŽKj1)s(څuNPh+6K#F^OVĀ6`K?VvLfFT,g!SZ(?ag$Tf.͔edǀ^i_hs4P1tgO:ًH BUUo51x;繩KIvbSfU;B#sh.͠m״JL)ס4@Zcaܣ׈0#bO:՝:ڊ8f`Ƙ,9nHF9w7QBJF_4qP$"6ZΪq̸l{̵vDġn)_|"!"qI $9Z>u$#c{AHV3F;F\y,  h O \{HR<$h\dVmσ4Gm}F`5HN[J W#sK+l]f z|ocրcp4%}ֵ `f<{~tf~Wt̗Fm^LvړeLhꔶ7iAD]oi Gf.3Je9:Z9 R_HTaqK֎G}:dd;NSiYKغ2@U1|4tD@oGӾ͢=]OJcמuԃ?]쉃Uf3"ɰ 6'%+M(h%-ke#( ~txJF|^(1yKYN~EFwLH"|'VƯA\FrV1iF ՞ȘJk#kq0dž~=yC؏7ozW#vSJ$vq|\^ה~ OYdܸGhJ|9~%4RHe[ҋMa܏ x{mB-N%Z)`5Z\Fzw/Zh3}dDS Ty%c)'roAz`jSMs%)fXRcp>!1G!eofZ6'4:gFzcP:6!hK{i[yM'оHe)&-D2]' U4xچJF-G'$4@{&S MۢetG-'=w@şemEg9)%Soeu]w-w#YmqdtMp\t{tp 7/Ԋ{)1VH:4ZDyY&lZdLglSg 8V%sGVTm]h` ?'f^n. 8&Qx[2hCׅ[7UX9r`苁GFSc-Z#u[KG',UTAKʑ꧔vƀXJF=k2Ge%Od}iO|DkY#+agGZ(2hc B:!\ DǏ^=V!i\(ҹ^'^'/9 TUhJWw=bL,K4pXdQ(nQAq)ɐQ~:sLWCa*#N_3h6MZ d^]c9}Wւ%R>)^>[#5 A}ްn-~Nʫd8 )u,xV$s=ΝPڇts,9K|"ފH/ g(wipH/Mz̦\OX:s9dzNkkunr^2|[]@wtQsZZtfFglhM(ɮ]fE*9Dy,7<b"r"Dɓ;4ua/& f}W] $H,T.h\b|>cNα'ydmLhFoC|lceEN*ߢ9Xآc3tNjOg]=H F!9gXu#(u~7NOqqR/dcbm#~=|eX@DrPwTp$]ѴPDA9^(FLT:gEdQ11~5=ۢA取|Im[IS+SZ˒T/zÒyj9yj~'QВcrhJDf c :f|ES)u o{s/( bX_wb.Q|QYK6TV-4,\.mY?P Ҝ7'ҕ9^""! zZm)|yz'*H !հTU7i`)J _HJӴ d,9~\_]a9HNdIt=XNEVs$!:q`I:H 'W6KCl'ʳAgښJY[$Gv5< 56wL v4i*YRLzBB4ޒҳ".+ƒd-5e$#MksQ.#XYΡry bƄm="׍E,c 9d(4g?bsN/-JCzc[mź2!Q7'XwlP |_/z֖by eL$QbȸKeK"B*qȄasΚFSZs1TtDҸ٤5 LԹֿb{ )!⌖꠱ucgJ[TVpHR|G6x4 TbuZ ҫcd4T-DI)?FWju,hZ ,&Iҗ\NS=fa-I9JXNFo[<%E FKciʂ%\4"(u:$^}UgN\:"ǑY56hV֏?Oį30^N/f5FRe(f)*"%:nWqByZer0Jis5vt1փ5GikŚZ?F/|Kk_#5sڎ5$'Ak|XLIm-IT+#=oAk6i3RDt_,-Th(zn-N;'&0$J Lvl|vJy:F~~4^$Ǵ~']S938Mo>c`,{QIzVď&nKwK11۠^{͝EٮixFUWگ\HX2UJGQEHi<5cxJل ahMSJ| fv yY%/H=;*,}/x-$&xJ7ER~´ ζF%\MUUsl#'-ْ|1Xf8M?cZi9Z*m_ihp ޶NC;z{BlVVc.⸝JR?~ޓHl^ 7=TzUVКȝ:A(Wou:-hE+!\Gɽ[ѩ'C3f<%йwJk]D1yEo##cũPmB6AQx7]cvqKȱ`e 1P9xOUuӞ풡ُ]#m]GvZ^h4 qsut6$?_>1V^".vn)J RVvvOuų{iyBEdJ$E[#э-+"E24K e,'it5xi6~`~ ѺcmufRw6ŹƿܕY;:ffJF3-:a/.)cAKsI2hv9u ~WgB^QpE9"PKd@6ۤ"շk#͎~̷[̝$`h] Zc,S_Q2H%e^E;"|3.f/#Ѻڛ6%oeE v QREiQٞT\:Yc1hhǬ}\H񲔿ԏV?Eyeyngs'ˈ{qJ8ҜIKNNs(:.DT?, BkDc7\TB2͑^5$@묅zy}>B#ŐÒ)A[VHO?\ڂ4 ) S8xH+EIrWZ>^(& dh蹾wɘ.TW/R1zhjJ:dncM7f}뗎h}i1xKY}LcCaJ *55$h2â!hrHFU\;j9,9s5z}ڧA3P\jњRR}>Wh?jX-)!EASs|t+r%]IY %ѝ!6QC3IS3V(q4H /e ylu,]Кwq>b;vB}wAU"&(_C`(_ȄF$,"$RGtǎVj4>ܴc)39Yt(QkǜK!h[޸x6O] #2pڙ(Q*y|g`tv#lpðx=h F z{^#((9Wrb8M9Yw_Kʗ%ZvP@Ht~w3"sguy?FiY&9Xsۦ/bH6Fg(ODi_T.:^?Jw͐'hg9<ďN5"̕I4#k!w5^&w._(omoC'i֦I΃`IXbPYaB6! _A #X! fX,4EwVee]Q}џH'zOQWd)?~'or$ mRJ{?7+ɱ:q9{F- m`%{Se2whڮfb֯Lt歯=[,ߊ.dE yY09>/l})^NtR1?3^ꉉZ?3зFu"ZA]MzxeG_-R1Wh4t/1dk*Itglw-/,G;ʢ:xD4 )S$|{J)I('RFiC@SL+2`+J**GָyE ռ"}"H;|{Up:S{-߾zһԒ-kOe=mRnoAR)GSY5[w`t0hyOZǃJ)Z2%( I?!h܈LDԽR!ſ+{{|Mꥰ>4A<+$An;c^4Luز8}_-ҁM:^b8 y"d}%`S;&c?3O~?)k(wHNg8` Q!ORQ?O*!Fec }Gj{!UWy_]UaKs'u>;w^{-{S GաVoiYoe>=ƀi{B4a7?fv}^ch5uʛ_uN!:;Vd`#^L<=w{Jά WW,H1|/덈eR. 6NYesRWS?zń(W3,P7SW)S?ו{6fL\$==W^ۯ?$\i7FEe,>Qylxң{{ `;jt=^' 誣όhON2J]cj\6eTF̻`Ye"j ~*=~%-tV_qW^qe%olp(X"^Y?j73?F~] YFkŰDi, Fu$ "DJ(TV1j'^ר R77"ㆎ3{6R><кY(uUG}6&.L3 Գޒrpz7MxJK)?uu ^H"ϹzZ+ hhS@.ө'_nk'?eEҠ救gV?C?B:#yQG$ӤedDլjpeOHIVcQMmkrjCwMH;Q o#;^WQ󔧕 d%=G5Ӂ~FA#E[כ "I)?3XU;T5q+]eVÄ#RֺlX  uc,[E;9?"X lA(r6bc(qLmGY0f4?K;: &3£6@/ }z=AeufRdwyDiz!0!EG=as]A(?y^NˆdF׵G=?l==Q1(߇A?ُ*k=ȉhNU~NO׭t? L DYHs F3H=(8e )QKi,TfS^z)-$Ag[qmVJ~^* /}z${=VlsgKA`cc=\Wq_G _憔(80ܚLq|+ *zHkm1Zx2"ƦeGc~Zʆյ;tUUCw:*hj6Wn.-E$O&jx%Ow֎V ?Ӆ͋u'TJW  l3( +ۀgXjINH\NaF@l$XMl+br+E>=m%\2Dq)e#%̢츲Y#KU ǭ?d]n}!>X<2WBv~+[߻-H/[C[X1+tI(U!j 2}eWQ2#{7>bŶ[2}ade:4}t?  4oB:##eG=ed]xR=t7ՙA%}ʤѫ{KWI]Cys]PNp~YSu&PWݗK y[+/[P 8H(J+ʾI_~ Z}_yL>d_4K0ZEξPߍYrZ_' .&ޔՓڧ,} "|'ˠU.eDg ޷d-WH΁>w瞂ee>H^}DV ^ø<KV6Qڛ.Xp۫VKr_!dopeǯZ*;.tt,y5}Cb+l9" 8׶ƱMR%^4FNvTZ(҉˺̛WG3;[(lZ5wHgR6ֺ_%;*_f^#3%~(@Oswwݫ8wytsGZ7R"#9={"zX.wP@K\} ?c2*gbuVᕲ]2ig%d>V_C ޓJ-E@%X̬+3w#_䘲Lt MO8ã;Lf۶/fdݪzW[ FdᕌʫiA?k3+s-GmK7y׾{L'V F@ނ߳ IDATf,ƀkZz~mNsݕ94iEr' ѳ#tY }Q4Q>cZkp\ldEtWOyŴx`~P48 Agߴ 2]`o,ԏ_(0P'3)!MO ] s[(-jY\ ]~HH2qwrȖl|n[Ѻ׊cG 2d`BIBoo${jvHX]}󨯈g?RhjzKZJv[Ӳ~~Ot7 }5=$PG H?%e)00ϻ㓾%սmy<ޤX\6L%ye<R:F*/wdF^M˙yEwAJ>{/>CW^bx5ƚG4VJ_G(e#Ucon od0z<=RA CRCe:B5%"q{q*TQ?3QZ]8z"w~M։F"}0> sG3vƧlxܢ{HX2>!XG<,b6wH:ҕEѥ+̑+˿fgDrmrjS Yy;Eͣ?BhDv4/#@WMCj!>;9Ud)#o%Bo/f\w =.u=OځX}KH^fշlz<Ȍev5ph~WtޮH~#Vrra%+D ??J1 \#W 'yX\ c^1@J`Be$#oL:?~tκ)Lcu2#G|5X ϒ=H#>w3!ic*ٌiF3280ԇՏ<>ڿ?hw Iflޘ3< ,}z;"J6gEIzSW+7XxqZX;{].kV z>_%%}ݗ1j#LxH1㰂(u&%!]F*ekS M),RKVoEв]mJkcf}*QϊLh,+䊳)OJjuZ! :22f.ym3uB* ·"mE&ˆ;$(ͣ+װ:0E MF&{)8ADu_?ViЉ̜Z)]pʳ+`DJP@UY?KV6~6 p ykm]:'o1mHy"'mmWQiofrd0wm7ċÓf$yG4k$ҟ67&mډ _o`a(Ia#_x"+HGv5%(,VEFQTE+Zǂ_LҺ$ɹy{}J=[]h^ծ{l=VH:.[vh:`@a꣔}~vnQWM{W+(u"?7"mV UU/y?}zo@Tep`9h\/"PDblWF/*='qoyBGsyviȁYy`EkMO<پ61GHxOˬBXR}hmo<3cçBua{TjiȐ|OdmH}.j JWkCDOϳeP"X}D[M2O4 W s3܇3\ B}k2g! &>kK7=WiHf]G^ݷwhUϚlN{Vu[oݡ礢,L`a5b,_5=8!@x!rg3'^W] GaW$ nE.h= 䌲}zN~[,UcEmaلgzyow#%!GB)C+m*7W{Qi#Cn5S)5lQ B&e9e~YǏT2'E,^wLj%9kr K)חn|E}u"1Бx^4H{'nHA^#AήB?WOX7cW LBG^S{ϑwg92~2 O=кm894.N# Vf=4fG:]'cbK _k!me*2#ne/&+P=w)s<#y:ےsQN/;(CtǧLKKn&Sk;)xOnv_uF YZvN+iGR)'2G4~ٲή͐*e}zk \fU>+EfW3 yfH@G:ON-1dC486xSN@@oEW{G\`Jmͧ(Jߍ;DD_~`V=AB;WW(pܪRʚi>O~vZד|qlBDFsRf)PgCk ݋bX+$:ᕻ'Y_ˈ&xi9 27f7V͟Iֿ'GFU}^zlqߕ =f@o&*%YBC32<'Qeؚ|Dܩg_pj!W qFaiG,&iaQ]4H1k d^έ;qz; ^ N9_` 𵞯R$v9ycr \Pd|[MO.@`Govw1+N|AW?EVi0_e#L[Huo9gRQ7DX"k :D}25&@*s F+CɌKʲ{;u\Dne>G#H0ʂ4 lڎ9J].׎N.ؿ+YMZo]V翅:ԑ*·Ac9lhr.NdC]EdG}:㒑z$Oݎg _ LP߽(Sh^G؅!HA/gD奜Iق.qنz>W1kR>{wQio&dAgdyY`Ics~ c=Z]3ϩ.ϽZi+?+ZXl]SQz|EDDXTsԧ)K *jk=vpo=C;)ՃCTOwS'GM9aKEHuǝHe1 (#=>":s:M0J$0n]Y괄x$(2yxV1HnW ϗߠbN q>GsԎkm'0cNLHr=[2-T9ȹ }($#l5M9g S9|oI)p-FSzW[W ]!E6h=;0!g +MGšyt-mNq9GvĒvA ,ӜP~XZyV t9+d[ii#^VVUԗq::T>cim'}q&^KS~Y=y{ "Qɝ x-+jG3`u#䍢/YyuGSպg>p& PV2k^<(#q>E(mO*Ycw"5y5H^&z$.M&'o^WK_o BרZA~mMO<-5ـˑhVs)2N;A[y%* gt! #b=P't6LM  GC'8(P;ȑy\k"ݼSޡVJJ)4tۯS_n &G֢ D_q#w>wWfVdU/k rWQ]T+ 7!k6*s3@sI֓w8TNQB9T73K9*9{'hѢ4{}i{ ,DŽӡ_~cut&?I{B_EO=!âLgЉb:,R |LHuג?[_G=t>s5^wz{{33E=ՠkSytatz0F Ff@oqr<~dR Y.@,z Zf=F _=`B;7ռ?T}\=NJ-YNiE"+ Cr̶(~RX;'$k*S8 d"ejqy7uw"a ֿBwwts"[hA_ARKT8.#W/B^DWul<EDHB +MEw~,jt?7HL@ Z;hG6PFyt@r @EѽyFrooUPOWԀm;*wrlJ6C筹{ڣ#1QE;Z|ki 3q󞔈6E;串 eZtyP+hYODNٓ _ݸ2RV-z Z+Q͉vә=Tr0N nmG\Aəm%/ Y`SxohgmdNw+`?YҿăMm`H stcS5."о@gW%C2c4&$<$3g5/ٗl[JmМYsW~坈~,B_)FhթY$;KUD|m'=GٳS|֩)η~:LCɝ B3iH(pN_""D}da@A@4A,s?˲wRIZ.W)j@}ihzR%z'*oW"$Fh̛Bg&mЄh4=A_A-ĺ%]݊PFoZ/>AZ?7s@ҠcDǠ4(oYte:K@wZvd,Gehc)Cv'㏏6.R"Y6EЎ-HU$gKp IDATt\>2ߚֺ>OA+H{=J9IG8AA+ u8lYO 2ۨVPm&3I{'և 3KvQoȔ#gZΛulso)2hQ:k= qwix}}J}'>~^{*S l흏8U"9A_PđCJu";PyX2aQ>Yh]/-COaտ^hF Ae{s:¼glETuy1\ wbuu0MG^~=2{Ue:VZӡWNjLcTm9oV{TO~-KԢQTv쎃u~4[\^=OX6GO{-|}<'@w)g@( 9$5Mk:9M͞]A8=kh9o^2D$c~#΂L6okIZe@oBS[kSUK, $&#_<]Y+y,uMȾ Wk`QfMFD\Fc3/1ΞR;|E~䣍N+Ei / DCW94=Xt8Fǖᱢ)9E}[ dix3Ԣ 9W(@al|N~^E#j,py} 8юœA8:@tW `o((Ht/zxY^}|5z5Xy!_%yx;b:Lߞ#)(zEzY*y2,>6J wdrX򪍴lUj{yoˣ"@`Ck+)k "ܰw+u~[S|"'SL2EеVA۾V]Wg+rG串̤6t5#Bˆ3q,G:oe;"}FtXQGQp"G4!}<[ FD^ OoŹ۶ R;{O1(=}D2ko#{SH"#om=:JmPuXxBT3"m 6dy6K\w/CgU_!4'e{  tZ;=ohZ+NH+$N0gɟy_L ue17nC9nG1h[ m/t? ?5o}ɗQP5P?U|߄S.me>E@-)ta"'e؆|5FY+:DkQ+ϤzDkt.;OV탌D_J(X_Z`uԖϭu@Zey )vH03";  i֋7d!;:Zk% HY!zb\O4tɾ4x~Vκ?7QʑBoi"+3$VːObӖkYNIvz9]mF~/je2?_.ۯ˨>Αom۵Aώz-K HhI:jH^hptHb\\qYk~HX" xK.E5qsDB2{Ȏ^Ow2u;C}szAlơgM %BȄ8+Rz:SMi;u;bHV+\5S})N1ܷA[6ir=25R+uRAZ"~_CX]@6lp@cmw 0еvuF([eZXf|fKF 2̚QPLÊa^97?g@*/̻!OχZH^,@ڶ*Eo;2VVE2`jdS %R,ܣl5ЖeSwtTYQԃ%7kT.^EPz"r_uPwe{<4yD+]so? >}e? XrFFD拒TU#?o0O#T&D\_ϘOGh\+TWs|t ߉q{CRdez]) -xԉ3Y N~>S_*>V2Rw#<_{+'[p=gh6nVw Rd=t}-j=UYaN'(^"%/]z_%晽_ ζAt?0w=?;Nfr#݌"x/d(ӁoJZЇ &myvzI5yjּ[.r9+NF ۪"<\F<$,Eƀ׬܅\1OFszU{E<}vI6r" ^BrD" B@>3. >%ld<ӯ}B:+IIo`iՠe#G-UC5wA uxw (I6dy,^Oyvʭ잻*goP$ D'S_:3nsؒGcyO[gyD{[ 03ƥ.$?WDA^~0WNE95|ڐ,iڟ {$R,N-yIGNmzyS#c^#@*md~ID:C5DcG'=E^i TvΏzWS>35O[>/9Rln˪;uTYۏ9Y1켐3eߙ ^+/$ a#Gm ;xVpQy+ =/ބ~:l/#$A UF=^cI- uB8ÑChzA+fSD"^~KSa9Fx_xIM^MV1)NtkpHpX8ϸ]4 d$@ў|N#7+16nh~Xn /筃 }~Ytu6y̛np~FO|"w Z}U{(#H۟0z݌txUkV,;*1G{ạφwKqE_[ٰRx/hZs_<򌣷6Qm)l1*f73]c6Bmv~z,Hɑs$gSt% 7ړC@q)quGgqz0~{\JnZZʢX#:df'HvF|^3} A_nd0`6Pd(f{)8\E#F̴ы9Qݏ~I8.1uLf0h4\HM\d;rO _~`uΦBc?Շn߫ks-lOZȪdW$q,A<4w-!m;R pxQ-Rb49#%r-%z kPyR늈3nd(F&S({F7\zYK|G KHc2~>R&_sΪ{%D瀺n'Oi'Ih5`mҜQO!H/|-Տ6=yK^+PsWq" @}zJ9:^ٶ1}"w=˨gģcqyIwWxzmXb##1kc/2;'yf7bց};9@7VƼuS{CKzfhȝ^{d_Ve]36|-gp&lC^nw мlk)G;RehxET&} OPvz݈̈́(5 "[6h_#M(g#G`ȋ6eV E[냕ű(d8 DWY'ko=T?UJe[Di")-3 &6(ؙy̑(5/EW=I@*gm"#H& d}RG;ɣ@u(P)m"1mfyX#".>'g"F,3qQ]K2Fk7ΝHԭӱe=,$˽HvnRK3jE}E9I2ݢ[/3\0S'=tg?k#& =9BYZ$Dѿ2tEv[el*X!p<ۇ>zLقd$3fo2]NchY|cf6`gPf:|&3eAVy(w9"d[-i{T˭^eu]q(h9%8D9"[4k2E׏${@G11g#֜S"nEFVW"gh͟q?QPv)On$,yɧ];ׄu{V=ΙQhx+S8hO[m^Yr"e^Yg^ׇMM1vF ZQ'{xJLi $kzΊދGB`@2zвM#7FpѸYS&d,Pe);X‘ Z_Ԁ Eg\"439K3H,E댜Wt0wFʊ(;:v|}G-,!\[vFAASFo-e"mx1x0FRF-r`O܃1e[JizkDG`+؇zw<+"#"$]2Q`ʮ12Yc*` sŐx̵ x}^O*XtdӢH=uu,zOeo=[&䞕kģAeRgeRooo j9\/:7@d(,xS4YaE#ohm͚2J, Y*4L^+Ȁ'_uz>A(*<;ǖl`E5mWʞjJzIzL`|:._"׬ڼs0.D]*VWSoǥBlȍ=2x˧-$ -O6(B(c[}3)7dhtn"=\99Fid]%yth4O\ IDATyuUw5iee,njMT>/p#n:&+3K>iM!cS )TjOS u-9:-ͱ{I*e;7^D( [uC[ϻN1r_׷s&O J}[+ zLvr\aFz6=ݛF  Rjg8~ld9\ch G4;ȹ2Dk{6y__{QalWH7C!^UP$Ww2{D-Y̅i=xN^ iE0 ϦWvo}}=z1.[Y8C Rڝ!B#RJx9lQ`<<`MgR8ּ<#r 7N'm=tm Q\ԅ%^GA!Ysu^`ٵ_j 7{$ WP #3F>JmYћo#? &͝VɦVڨo~ 0w![&N6lj k~=r= TvPƧ=r ͋?S/um/5pJ]TJ'PLt 0jq'R:D]37d#`ɒGc >f~-͝wٗUs蚬=q|4E1!΅$x*XXԒݳ/߶h>M8 Zb | ({3l;/%Z 91;'rJYa[ِ(@h"yEq6"W .t_bb݁\{Bdh|F4rrsGU]u`@st:EI.XiZDZԁ4})IVnLc]E(C`w@h\A>\~dVǧypm-_ i.t>'$i#u @uLG  DmSK:ΏIʜY/X"gس}JBYٷt B}@GX0?Z j\0z%&)ʒ]WDjWd86hE~צL-!Q<yARUAJu8{yMR_I?On?`E :`˳W>{obsץ Vʨ3qb9^[}|'<, Ak7GnK0Ρ:?M*G`d{,P= .Vt5! __h@d?.ֱY^Uote }Q2e?DyW;FBb/$$9Ues<rs'Fb#2i笑ECK[dߚJ3>iPÖeEW6]JVփ [Z6oc2R K`*sG9/0ʌ +j]A_3q'ʿ4xh -.p{oeAʐPDԔc7{ԒYBkaW3yh1N"?m{ĵ=Cc=nYN.EW 3Cx VZ2Qy_Pe.[u^=h1DU&K ez0 >涊?+Lf$r :e3KCXr(87Gt@}H+H" ˮT::.dV2: |'򻴷uMg-q_ڀѦ/P*ݮ@x {;dCDBDxDt\=,s C#bZJ;Ǭ+2i=cz"hPM^SEi;9^$D+* s(*^eWJ)ӛ˞^2~)99غ)ͽB7RX:i I.t'/Q]|<8}kh i܇KH+}(^ϣ2lz,Wd9Oee.1XEK)uΪ=y^uWZ贕znW_)9^G5YG+phyE#;W59T[y^PuU*%ACIGƦVg:d@DŒ -{`rS#Ki g&GrD滼]+Ht=c2{‹pN U6^Fη~lė I.EjvƊޑX됙ǫ3LYXoZcx=fuCQշ'kl߶&*#x2óoLY ;%w=)tQH|Iؑ=(X|rwOd2+һ7HfE5WD}Q[8VюuP;jZe΋W̌Ɨd#k6%!@6#xѯtTo5'Piz-j[W6+Y=KhOGu񧌧Wa#mlpf*{J2kLwKֶeǽ/  @vK&քU42x+rXmW莡ze3U4Ztr6eV-9ѬB}yؒVkuÁ9-%+ Zt"^ d<Ȳ".+ :e@_(3tnu-6X{)[.y~$gZK G5а" 5_ MlM6/Αzz}i^Kkm1Hzܨo8Q{TW+<=a$ #0cTK 2 r&hV^hOJp9ztt~EX`œ#Kz\Y,V9ZR+4C^ n m@36cf,YÒ#X!Cu"Y3Cu"'t>{t}\wG<av~u{,1%Gz}Z?Sf$>vד'KY'h@Eg #Y{/(d_ٖi X!iOR|hɲǎ(_^4VVwP*_L+2Qި"WE,Ыoڶ}Y΅ǖ3=@O(Ș@DxJٳFKh\ض>>>=.c9Yj;MV6W#Bg֗߰_Y oZ+ם&mHK< Л"Rd]_"}<'AܺH~=fX[%VaG'ԟ,P$[ g Rx,/KSϿ[ZӼz-FSo?ѬhOO2mԣh?rg2zZf zݏ1xѮWqXk!yHgX}ܯ ٷm ۃ&Oظ盼!:>_B4:DhZk9OvIW}.@κ =~yHU8<EsտW?Ģv^is:/+?!XL ί9I~G2k᾽Mѻ簵z\3os2ʹȉ8+yf̺֓DP#=[rI~zEXH#hyy*emڲ>xhL&@pcX,#Bo$xH3YnT$9-^: :Vkhml>g̍16$>,ơBr"ʞFkdsut-U(}=[}W 4 cVYcig sbk9ˀF"_W 0O2>(Y}quΓtj*8+BgQ-KxxcC+ujEЊ!< {k9Sb,Oa,Ez{/5Z0xxAF<3SOUEa4HvJYQPȓ]@8_lʸ^sTEFg Pw#v e~&zOPIGc*:QPlA1r֦EV[mYc{Ë2QTQ+:e"(EXiZtP9в"Z ,g;Y4d;[!t"+DX \Xבhh>o\eήXAԶq)MJjYJt~2F},ZK*MXy{?Qo-6 %'A05)$kuX9O>x epohsДԩDD[o/ֱH7mٻR Q;Gd//JTk-Q;.L*&~.=Vkr׶l}5xr!*u}#k\ FDTmei-"C3{j~\Oa{2@;L oW;w pHc!=bG}\GO4khH\̓562Mg)-L*}p0 =?7W{5:t} wch6r2вsʞB{6=sBןu?qn͟hhl3o:Cm< uR:yK>P-eA("ZYo5 X1"9CцR̗>!*3dJxجMG !aMWd-EV4y"`h'f)h!lnl͊.@BڀyY9ౡ@J4xCrWu@HwM$߃SZ]qƏ:8[PW7U-#ʢ[Jl/=LGz,x Hxiw, _isU>3O7D HOaKO "  gU֓:#3ώ#nsɩ_"9/H{_K `1U:Eu -ŋ TwYa䍍D&B<(燢0/2' iX mDVȒ+ e>~DwIY%3Z>k>1oHc*> 4NQv0< )}(vUgb߹et'߂,XJi* o JLde&ETr|e<8|j9Q[ʯew "Q&Rg+Zle<" 5$r`dڛdD w~Jek\@kKM;0SRV ʂt޽A(5{ IS,,V[*s(Tk9xyX ,u 7>2}D[]yvY;| ~d$P!e_]z8|h'2H[S T,cE{wfa,$_7kc HcٍUz^4L!>"w5LC``]h116xh,5rn@t*oSSr{Q88ʖ喃x!ê#oy [ "w9_ tV``YFSGjWɌ\O m#'a)^4O+rkFvV]-b9뒚 Lى[:dD#hُ&8ȔݣzD}U2E=|1<%;D]Aۍ< ɉ/#xN9 Y!!B t^+ӟ,&"xSno9U鿧h.%O4ǫzlƋ2"s2ˑ"YZiҲ }AZe_iEda}޺Pϙ ݱ ϓ=slVK'Y9":nweyՑ."wK_s5d2 ˋ;|"*C932XHD4N&Kڹ_Rn`xhΔ[ eMV3ː#g`2 pE:tQ447cB|>=^=9Z j;@@CO#]cͶ0xg[VNpɧ2|uС= 6qo1+ÜXde,,Zxt4 ^jJ ZI 1 E=R Ef ; N{C`N>9jP,U@[GAvO1Dxd5qNr:4OVO=wL"J$Y\ӕZ0;a.H~,O6 ?NɮdoJ滳\딜R^*OU|P r0EΦ+Z+Ƕ:RNSpe2ţ!왻V$cRڇ."^vZ@^9SC/)-ka:HI9-HBu9smX^u%m@g# ɫюшHyވ+ty6,rS4ZqZc@/yv?5D+脪~i7; qrDQ`X^ʱyk 3@GYXfG'?XBThz~We̴/Tw* Z:nN^_EUs#e*m~s7¬ d𲐈Ơ>Uyov-ܴr/V`˛o VZp7ZJy`l?NUUyu,/:s>GDN4Ȉ οJ?Wst 5 6`EF5A7jZ2$RƓҜݨ>e{:vRsJǰC'ѥ kQ]GaPݘ$@kžbQY&K"{kwU[z;Gb4g~Z`7} ɪ78bu:)u7ʧ*Lu$ `5c(HTXOʭt6 9gKCYmP !ꛦl6f;8:7zkOH~Dz--9@5Texg#b 4_^e4j  rhlucF{5h*C<8Zo)=ڗhת6sQ{paha4J꘶#~.o_zR,qDq}~q)tz~Λu?D7EJV X"~ւO}LtnZ&"'Yi=ȁr(7)8C2)f%j'fׯ+ŝqyds.w:|&X>yw?jc{=7 6oZJP.*T = ;y\+Qغ>RF[QĈx7A(Nk`GFMs@ݧQC:آ2/0o hߌLQH|8p}ˡUlha%NM5גuq-"J3|u,$6hnkrOA>2czt *Zq$'O2Wu^z"g䤕,c6okOWl(AKϳ/ Hب.[lR04rӐA\Mzby "D!#'7 `$<''Uz 9ĉ.%/ kdз޶caQȑxEl!Hϩ,il/ۼ^IH.ƨ?il.Ns0+$TGH ːu爵Vχ5V{:Cѯs"B`SMmU&!vWҡ3<~)9{4q[lTx,ᙸ`_%6YNFEx, X8Hq]DD:zm ѷjsmA7r}%aB UD{@[vx*y7V {x=LG coZFH!w@m'B҆ UHFn}h|8B_GNF*ӜU^ؕaW*}'s*N4d!C[?RZ'pnm'O)2^*IӽW_K{ݾl&D.hsE37v6AN;}j,?4=:8݁j}t> vBX|e)_E>-d67be>Vrml},hCZūAZeYex`wـUZ=-}NT?{ u+ \ ߿ʜM=jYT F]֗ =_RivN3X'1c}GWT7UDOuw *x+,> xY}/iIܽjߠƻ|l`ޟ 8IڟӸчԇv!,wdݞyPxj;v!AW[Cq??&U/Tvdu0栻+]{/+۬z;+{n .!\Ƈi*<1>BX@aB@z0Q !tL 9aBSXΘ & W6oGEg=f[{ouu ' zof* 64$:8Bi K:l!-ܼcPkSni7ԁlZZ_3jIn=+ӸG+=`HڽjLTTj?HOhmVc';h?5 v3￞ƙBO{o=7yαx?n5@YoGhU@u{[$Z zR\'x\U=./ y+ *zM6rFC9|g7 {'W\^R`\yga(2 r4 @Ia",vzIs]vEeQcx]i6x 7}~}o-p=Nϟ C}+\록v;Vt?Rvq}{߮CG@<|euem ls>[ų26Cx\ط;N48-@or/ n}tN(ó[mo:!]03W I1Z얘}VEs}450aѮ vZ:z4J0g dϯwp)ϥ+ޤ^kJh*%qx(*o71$UsBƫK&g Q@d#u JOi.!Zӡ.Gx5_N !t#ޣidZou_?u49_JZHK5|z#EZIW?J.gء蔮C_QU*_&tǏHpO^K!'A^mﰹ ӲK)+_NR9lKDLs)P{W0ͦvD)NUFDyg+)ABW0ߙ~ˆ |egj4wj )/P>ѽoHb}}+w#tOI f]lXzFG౟M4EV`UF ƾ/YbʇxқS19g)F딙ٽ"'-p<:x2^%DN@>ڜzgU3& j=ye7eu~FoqݷE3Z[* jSUui@]<蒯/g!nCzIwg{ưY$P#|5;Nn~v!i{Z}O{rrnG~Ͽ9;hV:I/(*ï }:MNħDDΰc۬A8+v/kUwиPHWҔ>R-#;bl_c%c 2fK-gAS3߱} T6Dۘ$o-bQz*bk/.hb]iDжùTTOMnOw=#jx_Rˣ] *޿ӏŚ%,ܮWvCwé~vɺdCen?ѩͩg%{?4ֆ͈RO}_ a>=@p5z|*B<"W@7|y"|mgǼO$ԉz,' ߄V`S]L_:A&J+R=1ka6a2`[YcFZCoj4l# և0sF,{hpZ0>Pɠb`_j~tWWjwXТO&[RlpTCavӏBngC"- P-Ge;uۍE靖! /[u?ˍ22Lޤ{-Rfs3gE&5첫%13ا:)m-kcT%*t٘dش{b\4;q:3%J` 20CX;ʿapݲʖ,@0@q}\҅c yڛLkȫ=9edb$j>(dx?=TQ{4?Tb=YߗrPйm!:-);9͢ՙhxNRFKU ЗBr(p@9\N!jL$߈t? i ~=yo}.] n"?j$+3$Pb =MbHމەw-zq ;Hߜoe΋ [,n6G-b.>B6+H'9oqwI1]|wPiv}3n'!01ﵢjkGPh\Iɞvl'=>* <;tz <߮W61RU#J ٷ1v?*cϬNk IDATN?U(s;9?ۮ xƲ+_}Dy>W@!U;"Ƌ|b8cu܃Q&ydkPix FάԹCTo8w[vΕa;jˈ]ڮ~;>xA75Y-%!Uי!}iC T~nmeӝULJsPAl[sZO$*ߝ s㛵,¹=[:?o&KN}^<_V,?8x]oWdFS/F>܍ %4uOQq{׈Mۙa*x:)B=!;-}2b Z ~kc>>1oZZ{!AmL`փ:]}n`W:o>)^! ɡjKW%?fHްʶ+PY8mw5UxێGu=#W-=~(-sY8%tV%"ؑR $X/+RRՒyV*D;SQG<󓂨;ޡe ND*U* 3.5OϹ $s fR=3o.7tOm^Ua ʜ嚽ϪOS8 X[U6YqE㝗J6O9u1F,t~)*QƝ>NqeƠO~'m!54q{YKĭ=Ӧ_^yRoQpKCy0+uf\࿯lP83}7@m.wntwomƌ XS[#|&zKb܁>U瞾t:,qPږjLש\㉰ _]G"όyo*n{xG@ew9y:훹?Se֦kT3;-w?"ZVO/G+1l'>( Д8|?b05:o@aw6mABQ%Iy|>ƭ}iUBGʯ{uv^$w5h58}JW'cx0eex%ZэWJtl*VY[>9{} }ҷ[z(jƽmEuL?>n6ޮzիo'}ߔ+X0ejw{g~C{"aids1C>͓g`T GrcDlӎ%{J9 v78NƐ9 X`i 0H<[е%g4Єk1Q(@0K.x' ss| "N f kM;%Wuq8ԛ}ۯקsvt"Kv>Ac^xAanġU8 ֓{vޕV'8)Tȸb*'3 BzvJZn!PcBȀbZa*܅WM@B0M)~D+dz [k!֝~Tjaǥ ];-cYc6?QyFZkɍ: X>D v@+ٿ;F)&}=|7b;KidxP.ghsKkY *70z?&pdߦCHVtuSߺg7u:=LV'Q왳*߼P$: O0͹鱙73nDO)jU},ʹɱUtYN>]ktXQyP/ͳg1D_&9yqbg͘WCRߓE:`khiT9J8>|| Q50H'zKU£V%Mw2Lή;c~nat;}vט8yvb,D%YG}\{wNʈD}ɽ3d@ǼsHYd_])QxzҞBy ^9}2y1<ޫJ&=#cy g7Bi1zo@ɟw/"&={b5Dx9(|!5Mi ~1{B/ŗ><>.O闈D^}֖Ya~؆*/Yy{V`ҭrYߝ#䓲j}esm:c]:Q"3OAZˢ~l?$(Kp0~oփx+?o ϫ=_f' E*K %"Z5Hb̮`)p=zfK[-iBG9J{]Ғ{10pC b"Kslѓu% ¯DgJ!œE6Z=&~UZ @#W/}>2_iOF (ia8]ZxR8fLϾ)m9҅֗8%wװ\xe{Zwgط TkqL~. >"Ïֲ Y0|>/z<:zjvm{偊}Q؁C ])7Y9kFϓܰYx+d޷}|9c}} ~5%H\lZtVaZUws^1gPM??gOP| K&;X㻷w=K'8*WWqzM(ۋua 儏OLƻ׍d(qj*ߚIVz_Um.-mO4RA՞[@<ǻx7sf}V4vR*Fox^d=4oCπ8،88Fr}\-9d+d}h-/Zҝ{^V~ҙ `774/KK};}{.NXsN;VnWjoΜC*F5"9[PaD'Wh;v/?u[瑹Qځa`22-ۼ6'|!|fvsh߽OI~'}in/K0GiBߎzDYy`k7h{N/KRiQ%;LP~@\oX(fSث)vKBei,<6Lbo~wռvZtf 7I;yM< /k'w&pCګ/ lB&\gDasUǧ]wn7{q*/m7Zt-e$_v.!S8M9G~"OBw|_kSS0ᩥ_%5FicFav;L#!: DQ~n&@mjgۯgy@g^%[% CZܮP!&|}vpR8|ZmU:Hu1mppce>MsZhd·Ӻ2@enN/2R DegSl»~' }Z &Qh;vvA BZ#EVJlU^ÀIIPD8q]-XO_I@ M&Lx6#K\˚@V$80U⦙Vm.@^!<Bڽ襋T^yeM598-ɽ# yy#ѕBf Ru5ܻ\ t1UX+X`_M-Wh)ޭA5r}zK/=7(C(e߫ԶDl)gAʬ@PG7tV͵&ua\%4`*JP%A-3T}MP']o7Ex2t)not-+Y_zN44cbaS}d=7DR7}MRJ%mCOw_*,Zrvb> Ԅە*8V\Ϸ?z|i^AĨKAz=,qUU=cU xӆIJ!Ǜoc̼'MK,x2(srLfdh]A2[O]H3ׇnmrKd5xηw9ALKTrRT+ޛƕ;"vms_&e%HRՍ~RǨozW#e^ٞ8M)< &#j9NX1nt_'{q<_;{ 眥>!cqKɓZdnxNSabr{6<Ҽ=$Ko\CGe v yp%Pls`\%f5hv$~7aJC0,QB,@SnW̭=mr5P azxka@=$VbI. IDAT黇*@SIP1# Z0dM  ՟3bmg4TFK7c2{ 5r*s^88A4>]=Ǩ04|>¨U=7RiU!گ;~HwCחk-`-0"}!-4b[r۟').=)jHd8oBh/o.Ϗ9n9?UZv>;?*egI2Ӻhu^ d6B]?k욗4O{M6'HjK  O 3+ȧWh^*=L$!1I7A$UTsW 4 ԛUzR*8s^Ol+vIu}{#<51ƴ~j˨`:*O3ڼeϓ6S9wx<u>ڨq7o 6&jg$w~9{_9_l~4 oE45U.J'qkˠ Su}I` Y6JW,AFM_~͈xҥ9DOYƬk—kG yNSn(l2/xy!ucV=ow:_5 o,ݗF9$Dr-8zQ|< \̋ˏOHkږ=;_<+ȭd@|B 9j0=MaGf{=SVh`~h} ݐE[S~'л*o6GBoŝDIT;) F@*laψ]O޷1wT eތ| cW5$ֿ !~'na$DQp}\0ASxtIrD%31Ka4J6ZueOB9?y?jS뷃E>һ&'/\Z?"5d1ijJU>xqTa^jj>;)?QڞRDIc(*IEfreZeAQ*,iͅ[qcyjH]mCKSX~i$Ҭ*tg5'785៏2|B MZqMBa.ЋdP!]1?HԆ=Nɲ;-\a-O*sc ,p'4)oΨZ~DeZscЎcǤl7mDT6 5C]k2RHmiޤHHYVk+"~b3vI!.4Jd\ճF* <ܸ;ae}[T*ׇaj4$3bљ\zHz#xtEa#+<9"-,DmQ.䵃˵rƞcύ&L.h Yby= DQhgZn<39VW'Yqv{2QiN]ǭRmrc>yLFt(xg$z_E"u2'Am]L680u&h!D廡zW}Wm.RB> A l ]# ߃ 1 )V+R7'0W ? 4fȄ}E22Y{T*6⁌_"]W.Ep1B2@ Nyp7!G3c>BՒ;"x2Tk }Of~ ͣPzdv5xk#ya}{9J<ÁiMB'GEXTb| xFT(:E!6ʟ@_":}_w T~;为 Tc@!iJ2>~\mW:`5CyƋi,TX#*#ŷGBXasU?Pώzr}ᰄY}S6 )Q䎇\}E0]oaOl RMvPnKSu,𪨡 =1wq0HtoK,$ UNK(=yN\E69 @1W[(}Ra,),_S$קU 32ZDYY,kn166ȼmQ|z5SZ>T<7pOIJe0GϝCa">/?J88;bbLHFPqHǶ#apx =[EYcA/9bLx;ЩFP\ F7'<o_==0h0= M9D{p%ppϭisOz[ E&H+/&6(^H>\ 0= N%1J48YrX1Gt9lߩ|hZhϪdeF+.37WdFN҃)v.p hh D!)_v 4&83pÑBlyBy. J%Lp*>5;6=nŘ* m'l۫B[抍/ѥԷű=5'a|@H//Ο*(RUI#}-/5ٸ?s`7:~V8VI":]7`MLD΃߯ ;"m) |\~XFAEsA+fo˲!1$픵VЏr%!j˥]2 QI #E4Uù蠇4ķ'ʅKpoo߻1 G2}|>ǡ4" H,+Ų-3B4RM"j2` ]F8r|uؖ.b8Hq鍈Ğ/-LJߗ,‘-*TNf{a(%t{Y[8+8漐 ;AJ̗3>qٻF:_xslN6`ewd9W} Z]U}5ø=q @M\dD]QSjLN0x֦)}C1|WG˼'߈O!1%}a[iBpP \*!k*tI%OUKFaL(^ ,}L1>>A)C)Ҙ/PV1MR BF*3 vQ:]^XȄˬPBx,VR {\n0ۻ42ʟ #j=<\o^R AFVؤqkYqEVh3C.vUɾ8< 0׽Ns@p5oۭ䐯wWUO5-Ŕo[IxBk^ N0 wd \/ et?Ӧ)eWX]W=?Ht.ߒy,`ݦuNv]w'OB"( xwp8zb&pd(m-;fAsN@&p[@{Dޘ8-nB9nǶSg޿:XWNMm7QvXo\xgɉXJWoYu]**nUsc(0auET2ɔWDS2|.dhUf(Dsj䀬?_ی`kVY>!jkyPPMĄA3Ll߹CϤ@QIQ&y/53k XV]T%mT t<C Tlpcb$εRŸvW~VV[{]o&R`" eqx_X^{mi}C@O秾 4{lc5~No6#)^|{p>-_-mk:ƃu`I̡s88(+Rg1Z3G3)DR0'O#g]kڬ牥/ҮԿ"jלּiwM'b[J, RKEZy,)Dzv}OS#ֻp}T!Km]G W7/0v|.e r [iWuD@x\tI^NZQ }/,JpXo82 \ =>N{ɜ#p2ԭϊ iD2$x„ۤ-N>z蕠D4׳hJ97@3wT59 ר$[ =l5x8ьUʮ,NUq] <%VZ{;Iwvd(e12&w-7PF>$i&\1F+f>k;5 Wi/ <2zy{ѮpEؼ)͜Ɨ,<LfDjasX!BǏ |<ii8@'cLL޲wZΉK)vSTm|7tvxGODp_μϚ>ٹX'cv2 T"z=+M+\fGrT-6Td)mYA^~RT;@[%fT̨vSqTede,`2t+ICl?k4I;BK7 e8`|nJx)06V.k@9@foS]gN@URxzDs}1' 'xYǾGZE"De T`SJo3>4Oʀ߭ϦD[,<f8+qY),\x|=;KIIYʺ^UWu9iV1g&|u{v\ng!6[`D;"YsE:RhGWg)-"~sM7="hf폃aL 'B針IiLw UY ' DO t"̫hޖ>ܞ==0&l!) ѼoMܕ|B`h/OTUv8SNe$ňY[F29 v_Ca /cB 7FG p~jRkt]^ntՁ#hkΗѐQE:ca#Vsko+;1k\qHf϶]RY "oV-4D)ڦ;Z'B黲 S\[2Uy"aD \f+]Θ{wCLc!oC0oJ w9Ep1zۖ( C$\P.j>!щ4LI ;c#gi<*>FzUi4'S^rA~-&XAwx(6_<#He1+?>%UCyR O$9)cƜu(bS34˶ o6הK"&YԒ8?z|e#8?j!sfM1֛1%)K @2b[x0 3X&v9%-hF_y/;zh30|6-=5i8#c(r9<upyF| c0<"OWyyʖ[6oyԀN3őN.Rs.SGٶKicin!Y- MWFi?G{{c(n" ‹*+V|)ˤ'29,^3$P$ze?Z[ pR 03h^I,Ľ)S~xbI0A`0;ΡV3i6ZAX&X(^|Ekpk{Bvz9(?JԬ@8-IjN:epV̘1kv$ 6x2X pnfȬ5H5%y[ z${ŝ4BVF cF<ˢrBhiAUd*\BA.VkTʫ=Qm,_uMx^h}{eҜ_93l|Ћ Ưe]^"٘=XB%l} MA館fsV=U9*z|2\*l̟f` 3q$nEFDsy:_tOg E mPW{# (<`\;kɠ߷ O<#" X146b;T܂w>n2g Wz6ь$D2ב9QH>yH< 5E׏a=z-܏c_c~|&G٪$b'@>o9csKUsyT,R䅠EyxpZ޶yrnV  A͕u0N;/s^=ĎT.wzyQXl!Bj<G m4# "+O7sf^Pm8 rKez~LF|~nXz*^c-5_ Nznx{~#l5)00:!¥𲐲vwR/[ϪyZoh58_@{тIp|t(%ot3=偨2Uw@'P߮yQafSC&* .P ?_UIJ_T˔bd՗EU 'j9%Dz/,u%'/1<׌8/<<9 i9+n̈oMjCLr1dU8Ebsks}6lUU+O[TЋ 7X-YB8 kQZFKdJ(@' /U'VA:DTYQ̲2!WT/  u5H(/=j:(yl4Tc ^Wʏ綨tDU<IZ MlIWtl#xU5iB\qm+!ImdJO  2J 4#1 t0X! ZܹE@fd~믁Iy&y[R74A Mp4sC' RʹJ&h"H+~|66)c_sRn708'pVVSR;\`0φr6y˜==zc6p^t$%*?ezwĘi9 j.Ոl;eT}V&* e,xS@ f-`)l#U:++wdX ƩsXͮEL!s&fˆ?x\aT22Ru]?+Q*Rd=qMmtۗ#&4!6 sS(ԣj3l{m޾@" Ɵ6j@[^vum_wյϠ5kAC:3$ QuhX[2+" ?޳ c-&I M74|Y-@= K '^ŒtX J[-z ϼ<4A Sx-Syऀfg aH+C팤V1/Ks겙_/eԋ@|B jP #% gt'$g< \!8\J#eNOz)SZB7~tG 0Nż| 71o[@(@#'B"~[`H{e81<*VU<ϧ12pCŅnʼnU?j%C}>|O¶9 1ob; ,&XN4\V4KPV;a1.5[kzC>ؤ1\1I̳$(8c5|<<,\XNZW%KB@eb$ʵ'UDϟVz?`hwّeYÖG>͟ԀC 4  &P@t5p!p7 l-3꼷NʌpDz]ţhGBMZАnH>tUִ32~\իa#!(SIh`s`/Y7qr@fF^\`b=]NCo8|c6t2k>)~Ϛ%2% 1-렷=b깾HWz!8|a ;Գd<P= 4~P\:6HsYk\ϳk?H9=0tKitwewF?tSklZcឞcOS!oGjzewB`y(/zEƸ uWِc%atф0o/>"H?$700G՝w0APH\ޭchvMCi;`,jܥBsp]DtIО F9,"gϕ5(c0&Z1YP%BJz+"!#^QJW if/ SQAC*Qi͆A312>i4t  A/Sk~<7LUNS&fiqNɢ6AM')c@bO/x5,3CfGkT%h><NO^;%Q"$σZI/漢PIby{*g9Nxdh׆Fy5 (9n%L)]OA@K ѷս>{Kwǘ5W= @OƘgu|򝌐;x}aX܆^ Ku6qz@_? /rݱؔe>z^l;ݹf?a1I8㑼]`br)+~-`.|ƕ}]lH7z)8;_1ɇ:hѻ?~oѠ1YӋ'溥Q0eu/A?߾hGܹy#x4*mVar1{G>Ts#`Fo)c_!+YV eѩǭPr+z+X"v+t2)t}Ni{`3+̚A)?(:V:)NO^:sܔ=BSG~!+꽍:hiގkߜ3 6G vMa:*OA=]tPQ:?tgнvĠ,Y5K1Ɓx:eY%udOn'jwb&W)({?Lo򖋃x-Iq&em:{e,=Xos,@ 'Y;tKzX=JH!V^BOFX d/LLB f CuEg^1DЄ#H+߻2\tށ-J] 45/%zH~ n$vr;iZ7֭H|كF%;6+[QI]<܏pX^)'Əw78 JYTg#KĞmQf7Y&Կ7@fѾ|D15Zw $m!0hC:ʆQytf[-g av괓Z|);?twQn?i- sٳvin@{vwc ;Eus^UBGcۻAtZ;2I0n@7bP4F tgWքh0zz)  )1$gՍ^LL;t \wχȌgeS6|=,hW9,,YO0FT IU,EXr%u6uKhkuwo0Ǹz29`+ @t TXq DID0 ~fvttwse$FzGĄur. 2*GGTK :J͆9"bGZz[Xn9rw 9$H{qyp鵞F>O OAln(D̼\k1qpR!K]D׸G|v(Q"lX2~" `'0:@^FtAҟ IDAT29!\i/{Y%zp:cϲ<^|:끏 Cz_6hAUk{ɢ1UUC4࿵ T-M!Z+FM~`RYa7ݛad{u_{ˡP7k|fAs|_*-vWQgnquezrr-Uzx_gY {rΙn1MkmPK9b,kamչ֫[{o{XM}饱zg?)o~x3}>[o SS/[Dӟ!=vznn/'v(,̀]f! e`1?tV?o_qaa7<,ظ:8XXi|XJ?.aY]# <."Ck"`2Z4 2:D K93`VuR#yuPsXcgD3e%0馟+}KV7:dVC+,sj4 {#+.&ArcsIO3 :-<RϔfqgI=y`y 2 4iuBc)-䋬۽Sp=g){cpFogVUϞ2hM̬ɲ0(3S'_C0+!} ,ZJfxx|,rh#J8J3&K9͉FH_8*)Y0sRUSPgA{Iցg~Z:ap7"<" ~؛+~"QރՕR7S 0cv>~( 'G!aKPILFөŜKlR]IwK+K3nwc'޲,whl=?ξ Tk A@1MԎ<%C۟m24H&CQ--CI/Z $m=xP;ӸDH~iIqg#nIeў~Z:j.o;z_E#QuOztY t%?;B^tkVZ1.E^ <d5"NȈ] w/*JACxB`cd;ˑk:`aX0{Goj^ĴSwt=!kTf0 OÅ+5G{m3^sD]9'Ͽl+/aݼb'58]?ܬeY:Vz]TYܙ4QfV/ľ9E{x7 p]d10MP|drD Brnq(yjJFSvPMlz.ӄNi I?1۲8yh8-T aەr uߘ` =CPpQ9:-;=LoBeBbU"C;6ĥc^\+wN o gY  ˆQhU3s${o5r>(MVqCaDa!~kFx~SgIHK c+gZbwgoC,ِj!x` y]qKR2ev@ɫrTF9)ՍҺ/ z?b@ Bϯ8RIhR03N)/qܻ[C&ࢅ??hv#jky (/΢,`_npWFVHPӇEGYt;Oܟ9_MugCdS!,*|s}n̙s(j[NR7PPZ{3y0Ȥ@gޗ"y|BGkV 5o-@̰WtNJ`t4Z5~[o w܍O7E~sU`H+$~{o?~L닦I2K<Єw͛q83i+@bw4Zv!w&y (eaU^_I0MS9hTDum%j}rtG/rMOeg/&YBt(%Qn"[?vљ& <Υ3ئ=j-@~>}La8/ؗ Ծ^7$0vYaBU6',pe^p")3r{;q%"w|ـLy-S3&{B^=PO *O}߭a} "TfR氋gƛ@0yx4fр1LX;O+~ht<ԘAD#DsDz2+ZE@{dN 9i S%4A 5~  a9eS6N)+p`X-hi(7v=y V7О|*aj}5)?? /luOgLv[g[SÃv;p8{uLb"!Ft$Bpwe`i_`a#~({Nh4(K4Da%V6h"#t^*\|$7%*80 3g[>(ꛢU.˼dz˚H`g rTCT9tƀouɩ :D]qoqVKU6NEX̐t+TbkbڍvyN Op<7 0o4ӕV%r-40S(oxpg젯Y BT E.eÑ9r)#>0=$;I&:x#uo%m;phaS[oYf`JxG[k2v|.sts)̒s8@Og/ȫo!z\_}kuwCmcJpts.|'&C!_&PJcIYSRz:߿ӾG癞Y"W7)$Py|!":~;h-?lf""><,z?v e֊{`EԞ@b;T<7[묧J 4s^5$j8RJU*heG2bQ2gEOg)hCYk 7߷owKUuWdr}z* J!@|VɲO6tx O(`yHO({fٴ)j-5vQ kdobo{~ݫ fӽ"[ Ъ陼܉AnK5irz`3ķS9p/>h*>}yMUmuΣ [}4Y>yh{rAgh1cWN ε:\Skẖ)+ŴHN~߀u W#xzܾ毓wP(n7焏J=w^'Y:f/YR z?SrG#Q2n^(M*nj2͢i:=3À)~w$>PMr&i[l!UqGuKxLѾ Az\^t% cl ^i'xv(!Jtܳ֔}2ـCC ңUn*K1uW$#|VpoVXOZqoɒe9/YKzWp"RIV?qN2ӣ^Oc@R!tZI#q*aes:M%ͤ&F!+#*#su0dsNkAgƯg>T0{#崼X` M[Un0`XY jL=_N4!]/ ŷ%pQ^וiѴE}GQ=c,܁zcW::Η@нGqVȠv# ԋg퉻fŔ@ d@,GV QW̔;G.\F•}?q-J&8 !&A6PLVVG&J/Ё#?b)6>|ut>vɳ.Uu@G=ņszT,v*HnLK&F~U&O1H@@='s'H[bpyޜ{ZU18p 1;@P oݞE 7-g)cg D}x*ըgI-N^.kK`- 3!ކJ|u^^^7~ hlH'݌\wz3#+q4zKЯuw\`P^Y?,F d %AOݱYb3Maw@M(-h9w]̯1880`UKh!uDCM5HpI?B-3bwdSF!`u,eZ Y`j=/0;]%(E়O[NPDx#Bs?+1nBtlƒ;jVF2َ] W 0̀X&K`:)t$Rٙ߭T]G;B ehrc&iI>+p@c*z:o`aFrZHAC3) gECw]Jla)-Oٽ~h{Aj厬X;x2z+@[|g*y3Ac'ѥ>S*~4s' wЕys N,PUkgsXD 7di:9qY󕰪RAE,I?`;+Y>ʑȆ'R hsTBIUG~b @{+Wsk@k}1F6XvYqJM')3I+Y8ӟ^KPL_Ux,]JmޅsNLdʞvg^&48P b [ -'9%kS 䧀Q0YgR}ir10~*nwvw ~ʰl^Yq3gYn&Oȵ5=gD8dj|wpƝ f 3XE)a)N<ӭ 6FO J\k~(F ¦HL1,M_W\:H9>r|`< $3؈<0xz0PF hT-cJΜo7Yܚ) EkV2sQ>BtDց 3D]]~'Ta Tj>~>2̤ #w 0"W:|7#U̝{+Fmݠ!o'M(\ zz9@xC F$vc.$]3I|yyL&v`7tsi^\@, 8Я̃ڭ#+i^Zo{r]b"1=#Ilju)U,VUU6]Ls, 6Y泦pgD멈Hoive#/o>bŸos3Itκ#[_CQ޵v*Wpy{#<#@ QUB/DwOx/+ي2S5)q'S`3#9QB|[{D;2*@i`E; EJķ{HzCJrȖ%yh~RE_E eא0NQj.ɏKWJJ/AR5(0GMR/`rDh>ʋp!qIYdb~a 0KbJȲnCI)gW9ѹ*pc&@Kr2#n.bV[͝4$U;&؉+ xy絀ip $uAȴƽT0nӞӝVp[U#ːDfd̵I0W;!xxPs./lDf= +GdR-xI5a % q7AWKpg2e6(7)uy2ޛh,ډ1yxEOڛ20XR§{\g1(c-mYWߍ}3pCuĿy׌VJuӤ$XV&7djY%Ԡ je)^c08rs2~Es@'TfFj7\,hHpMr}ywD;Zz)pEG\pE3A>z+C;3%_ż@㖨}4Z.0)kV+R~2&5]lS K)wq#,[?wybʼ0`Ǟ:@TĿ"Ty0 ƯJ+F0œg-*s9f~ ,=,f.p[\@ В1Ӛic9ic0h}? gaoN1@>Nl v%]!Ctv]I{s܇'Mi&cI&. V _: !9BpbŚFs=_n${ ͋l_&rt>pb} p{pLd= (>}XXŤqRkH?`RE4GSC@DB`WȌ] Hlwlc(MrQa.Oi #v(^;{s}0煵7~3Ġ53|}}ikxSof5'-4Mý?GD8zO2AOPn@`M7_C8p(cCIT\L`Tܑl!D].P+s6yj&-J646R򳧸o䪽l6K-7y@s2M 2=w7.ƒ9?#Hо_rqѿpa^ݕ[)] ȍ*ymߋۮ] }v4V ',붓p+QU[?OHÝ"L]|>{3Nh,ýv˹}@1+][ٱW] ޴8׍txrS'`W #oVbxR5_+ܣcR;)HͫL~@i@9=̖~ #ޞ:wxt$P>6Eh^q~,1)n*7ks5|͋x%$җEX`0~쪩,-NAkʓI+e3Z+cѠhr"H0-V AZs)F: 8 @0{.17f]RT J0y|W "@lw除M Q`KC69CW I7~ Qk#fsGl)M6iLeⰊiKPo}JR7(*˴zyGiԫ PHΉX:ĝ\ֱP(n^,vӭѥ s8,&w?֭3J"M[{~s*4d,u(qIJ^|yWsM+Ŧ@. eH%AVji\sF+[X+TZ7N㢡xX84p$.ZqON4]>!ՠF4p .Aע.t3S)we4$3'[5 ;x@.i)< ǯWrO_ [\lQ>XI*=CLY5z N;zxP]&:B +j] X|={[H%"gXH?6Ny$V WJ6#B7ZgsRt2Z.&k 1Ę0U ##SeϦ{)Ͻ/U!|k&`PZnPY"LᩥmZ' 2_-F{m+{5,16Wcojsi^P 5֖\t%va? Bo)A9A]¶L 5,©|=w_?Ga&Y5^JEyz#9ڵb,GLg#j)oZkW%.@~ZoZ:wlb(y1xfQh$IGG'EY֫UyYD#vaY1u U<0M&i eK^ɘ WD9ӣp!~Odlǔجv  V]I3 7̎՝z^sGem#ɄR:ɿ]БD&\\误pixW8g>-ec_}>CXhy /2rVKc=C4^JyQU}=cB@Ѵ@^@to3uRkHza닇 d%Q")p KPp(Z azsA;jfV8#:z8C&ہBg+Uh,K 񽻒y8,ᢜ욵7zYpǻVX{cZ '!FTd[Z&ka{=JJ8.Xz!uEV)́pWKyD@VɄ]_սĭ~Dv@Be3[b}9G`\I`/zBfj5g:wUx0!cPc2hFd t4Èrjolv`}p'|Wuu9&|ͳ!_q1Lsby T`Q#0`oI7g鉰׃nwhR PxTlftω*nh۵5wCc*XCE$dWrqLCZ݁̀/ IDATcjcu+anYYg|Ju70$8H -d`:/*j\tM2T({Ua{ ݰ)ѽmazx!/Qy>/P]1ӌR< ix˽jG VƵF}Vcl*eb.lGjn:]ȑ+٥ bZw}c-gMJsAx-yrh ‚θVBpWwI~WY5&o[)Wfm o@Vlc +kP،%s3r'4t9ߟSݟC 2P3>5θ'Q3~7n6.fEèh=P6E+|{/= s)*`:_=#t#Gc;0R;Q:Dl IELZerG+E}Yβ=9JSh3D wpA /f<LҸ]'+D.62':{Ytb@|Óyueʡ> 29뎵ճޣ[(BY)^Dԫ=,Xt=;"#4Wo*}zgbp2TnIKkl4,_ׅҁDBȒ[浄`tϣw.;sMk]빝9+ BZ] ]p Mxј*z;}z}Fc 9mp .^ҁwm rCFlMLz=gYXydddhEyogH<:g"=[4@;z-%D;R`aN0=<4bq  C VoJsJ+OOohw`[l~Q^XFm})򸒩]@d lHE߿ ݂JKxb#4U!/l0pcy!Je4qEwNC ^Th1T̾ɉW QhJB#YJf}aҠm*A f a&BO5,*1Tɖ%;rdIɴ<5[I)3p5I#c*;}5apŃMbDlJlwFzm_ 8!kS~69ҏ.h&mRnMPX z_֠e2oǾneU.$=Eg!U)zX^%-&mtR IsTR2`exg H)uY |]/@qfh\iJF%gRs:bZ =yB{2s]~=Y3i#< r05?ǿF}oàxa2eCi6*`I-OBy Brr4 {Y"?\Ҷ6yJ 2=ƤKuFQlנw .ӥ^y_t =8>AŐs3 ۍE=Ğ!w}GKbvWzz PkZ뽽?xɛ` ==2>.6'ρ0XjޡgD {{N0 ϡ66&%, )ÓbBiU! n⼓ }bG{-긮Ipp>ȐBqa0|9'&Fv቉aYq+}m,r;tWRr嵆)M;%jN:(ըI, ymC(IQg@&?v.?]B(="PxXOtʫ\w"+AfEO+N]`O1z) 2u;g?C<]`wK;; n7{6YFqw{}cӍ|UR2ļVSKMl>3Ej=R;\KUq˝4(Xkhi!@t8 PTp:;\HSgf(#oٜ'R?%$9:[={ r(QmJPDvf21nZ[?_okIk ;"WBi[⊇AP-@ t&pNʌ09fn}֚3gfg:{));7镔wF:T3 F|4{V{ڼ!>Cpyى{b X*T_Hߵ1N/!i$tE KSzo9ڜm˻8Sf]^oU|іukdhޓ+H׍s=o=D 1 =2V<60&H(XfLTc:$[0ں$ا1F'@1 Tg@Quמݓ~Y;LR?tWy>Ӛ;PNnWPj!dU%5cV Hu6e<TIDSkV(z~LG\Гʇѡ)B'Iҍ;&$]EPf9^cn?ж66EqstyGß^@}33X < X,yy%@\V&%)ϐ ?7~3?f.nF$1+N<x]}ߕkgKqϯyWx|::11ٰopxUf$ֵ“N!X{):6;Y18 S9$-qe@Hjevu_ 'gQ:4F y"@Hj'it&d=)hLFC;SIja}N9Σdl9=}{:!/<Jnfy1V)@"9$ےYu,\43B'IT9tVO@xS,a1 JVVטlQh+yoJ!`O]Ux)|%/j_! l Ye FpseC#lw\b0Z kD[ ; maveOاt>=#H~XwhUYUx |ddM 21 &Tp37XNt:ut%)qyyҐj0ǡEX Y,ghkV4T' V|*Ggt!=DX=p\<?1}]?S{$ǧNg :c2:%׳l o&fnuʈΔU{]qC|{gS2 ? +Jjڷ- J+oDUP9?~ 2d2 vJvcbY@ߏC6$klP1fȵ5#SU-5;9grR$~: /,]7='mJN8NÐ"L|ꊔ0Js:2(e\(6t^'"Wokg\(E? qzM=g+``k -:S`GALDRZCxVr!b|KxkA` + Av+$xlƘ@){wW/h,JvC:pi<.M{4dJoWڇzJ/P@CZ>.R0SY3DճG;+-Lt\&i9A{WW䬗`U"j&i{Z(z ^ :s4_G>[_8#j5~{Ec0O\/;'g;ץ|\\H `v-F&.r c@Z8{q&v\=U?]Z{ q&ЛFtsq#^ՠr7w:HvMC x< EU~SgۆEWZF*ڠ F<2 )<ܛ3}.~ )@Vn\2R#>m{LWaRI/{d*`уZ+Q0+1_"!ZrD= j];㾳~'"\(9o1+L 0<'il',jXk|+6χJ'HBLj=\bö=nrpcuaU&byCRSD[äQa0|xqk{^1 H֒^B NoY8c`JN#CZd!C$5$ڑmʼSI&% (bm6os~+6 @|-,% P7QFC7հI{hj'wXt`Js Bm$Vݠ9ʮ910_.Ie]Hl:X[ fWKܣU+[FtEVR`4l!RX=;,딨QP)lßlijϤ!&ghw@;:s= @3:82gHa F궍&Id8z7*vb |0H+, Wx$'c<$-~%a ,ΐ}|}}?u1,[K 2 <~s9׍NRɧ䱼_1 ֳ0Lq4g}%QamfNhhuvp+(y:R+Ωm^~n5gmܭ &HtVegA-蓡O6ve̱rq+[s=s0ACgw2(u{H"Kb~]ɂI[h z~P+qaIceV>>< [k߿wϽ K?'D%G8Cg(3Mo8}5 2ݒ6ʠDgR;\ai01<8G{4U d!3Qnxو 9vs;f!3_KN镆Α?Q,GO  "M12CLL1LG+ N,=+= Ybel7#(L2)1[WdmY«3)B  ~8}G4cJy\M䞕q7,OVL"r~\(dp b+eP+QhmIڍO]S@_lԀ笞3R YVgj,ŏ kF6 Ɇ>+C4hywb&!Jt g v@Yuvl I1rbJ _:S~Lq걍7yڷ=Jx0ǜu ˱km풱'ؕDz--f/qH cٝQRy2ۚ5hR f1r̽6 ьp Dt 0]KyRd elu2Zzn7S ()&YrK %ƹ>4+w'^~g: x3$lz0 ُʶ'e"$3WWec(>\Lw drA7h6<L>8il-o%K\Dg=~PUeME%>Wb9H}sU4LwՍMuzS_Z4 "R=78~ u 4{l2@)&*Le  4%b6HdbyfVFg-RȀ%34 kN|qyXHTώԹNK}9 NDRovk1g2ۿ3Н5#+VEp|fYM{P4 11jAPCU,J۳ <| ^ޚ\BS*(v[+>E~2UI N:~+ط=䦪bN0~nJ%I [$2 N]EaDz iQ ZemOϱe4/~Gi4qS5:>wZ)"p=YplqLtLxv $鞸QbQ5ʫ$UMo_Ԡn6tKY֝p3YYזwtYOGxv&%2_y@/&Y(I<v3 ,Eqw)0R&.h9; x>뇝^U{I6 k!>tpٝȩy TF&m++AĄ}  Ozyo$DzRB<#6"}-% {Om+r1Z!Zk4ΈCv/¶w[lVCFtoWbfz6/Z=1# Q#bR!- C,'0JIv]7|}]L 1+3ãWT Vf:hߘ(Xqs [$Hd 4τa"_URj4XDooњ{kܱr]UjYdC&Ibfb9^:D=!\ʺht ާ9X[Fo8Xg2}tp<FrhEDžEuk vFzŇi|?&vc%fXR,ʎg|(O|r]duYDʂ?/~VsY1c]l?ML+|1>^ pvuW`M2'4H|6 0d4ND\9U jwj9Yhw5"òX1' OwKӀf&J6:'@ԥ'XMdC_F崽X\9,wC̕ނRx2!{h 圝f^CygK` UF@QӑzLG"_WQ?P4)=Izl KBa8隬-:FY"nUjǯkBք3w,[~nL] *U@$v`( =Rz7I4ߴʖZFaKy7ʥ]O{@9$@G5 Y=nߏ?eeCGcByV8~!spcֶ-āGiOE' 4 u;Ǚ c,<>u~RΑe4PD,kG[vFU8s|r g%>+#XXd"zK޼kz^AyH (hܓJFF߶\?u^h^bR{#p/ޢs;`G<۴B<d0e.e_hG*< D8 a\V{4CHל鵨. ! l3 ]Q9$a =M~F<_{&P`q>Kz3e+It൧K92  3"Fg,q.3/*ܟy#v9ux>2uLNLh ! 抻G9 d\>ޫP϶X9CYv$\hf45p Re, ұ݄gVܛH%7Yae* ɼ>v 8[z6}>e`DI 1p'Sϳ=<܄4I0̼!UTDH}Oh$WX6A4/RF4db{dz@;,7dC#\W H-B{)AWLvnMBjlW'gW/#<#NfQ"J[ ⪬ȓ&Hs 9;.1yo6Ù&qw[44DnլyƐ.K^1`IM/XGi;~zΝdRp1POO9kנlDK^={[fݪ`msV0dOֹL@Blk<_sLR  $^vvc\7A aܫa^mȅn M7#.fbPq +n} Dq]<&E]u!P2 P; r潖x٫YI0爵2 YFJJOCK(6b]O`"́Ða{iIPf'llor(4}K)+y DЍ\%fEDI HF^P\ׅȟ׽jeX<= z\yus?xO!}7CP3 r  7FYN!hK ]q pG!3 a94eqӐe!3MKXgdz/6̛t-VDq_2"oe=*{ SW}c |͉1yζ2-An*YpD^+,#[PPLJ]xwO`9{ -9YL0 gIƈz:Xd~+evW|: vQ_σ= *ɒT""N}kupCnh@˛]k㾿f :)W%؊*MX{C{ OFIꞡ*r %7ΌbƦn);udZy-\6 `a0%=ϤަupU/(rA$jc1|=/*/WJF{kQLsmcM%c)vH*vpS [f> ]*;SG-YY1}Fpg(}&%zf р[YO]_X@A` fBSȱ#G Z{Tջ Xw\ |UKPlI|.A{Q~#TuF1\~&fyɜ=NK ژ6<#߭9푡 E>w$}'ܵJvm!D>Ꞛ9 9V~f~8Y_|)tkRnVNuJ3IdX.&@8tr<)3aC\6I^kg׾s.)!*vFsW3B#I9.l_ UoG>dk+mQc " &4~ IDATYKJG(Sӛʭ 2Fz0GcVF4 -nT<`pMMDƣшF{SUKJ,S;o4qŎ=;b1#`^l}{W 2*(lKgm탆|byl=?* q+".ƌTx+P^RØޫzj.]s^m𳉸S#/^ݧq<Na: % 𚶁MeiD uN rZ  ػyVQ D2ǸUFufUVt I2t Fe,^-4U(3,ǃ NT)Xʃ9{Pz̲&e\3 G#@D}_$^Mvw-D,GC3#jJᑰ -#̨~rT],<2/`)PV[ǻmU2% (Vk{q*YO9NDr޶JaGɘ4tSC7`1Nm5\*A#+IvG {HV%dE11Kc(>h^x^}c' ;;oMBrB/IE7ySVϭՅUbfNFy3X:¨F=3&4^պ;QFtT K2ڱ 'wAx6Ec?oS~REt8CejnL;Z`¾!~YbIl$Żz+{,`<(Kø R 2>C#af&D y_RBh؃!FȢ=t~w[2&-f9%g1ġ4T,0-SCD`yǞtCVFw!_ʣUAŻDi9, pf4)@s@TKqεdb!AA3iKI@O_ܧn왐  M%XRW,fYUѢ'̵bH?GeS:ʊKJ$ VR)p j6..sy>U"E"ec'c{׺<2-^Pܡ!iE'HSM3AHA'22&˾<,oLƨ<åm,jȤkÞ wYFXTVV &ɣT{9*?n *1Ir @BmA$ؔL=M=ѐuNxwޝ_3OhYD P8}6,eg=s՛wH@Z 5C*Ft&1)}N36L\DK$$ʧs YID| 釀ǐij#5:ֽ "dmomf"B<~O7"=?r-,%GY^JQ)*}-ohcMCxip愭)w~12/T; t0$e^zD";lKz^ARm-vTHgI>ԋ^&~_22EϩʼoGO<1 <RazU E`V~MH`@gT"mJ֍+r{˻UOdzw6zDw&lm@op>~WBy:y %tƘ.Ilx&:&FW<#NkƩÝ7I7a3صIOw`osT׸R+B2&o9.F6SWLxJgo:\.澡'[Cg '\fv`)_`̚lp7zD4Z ䷱9^C4={{8*HCF%#emòznz~R=[,T% 07ghvq}RI ƪ?h =nާir$ LN &St?{'-y2/X@`HeI]k*>'Z/ eo3\cq6,0&`D=AX5L;)g(y\yS&D܁.C%8deUMfe2Oί$ yuV>LY:+pu~U}=94lw";򆎒Ǘ1ֳ3 Tb|7pqg 4c´l!|xf{4$13M<iNa+4D lKt4W&T7NC "Fyj̪eRDO3KpqtWfڀD>,u^>g]ƭAWw{؏\f @Ube~]&~(plV 7HR^]ge LVpԚny@չmƧ=Ե Sx<8vԑȎwc8İxШwGQ&*H6.V 7 xD#"s]ל:HM} oo蜐/IڰӵYpt Z(gxˀ b?^Q(tsmfǽ1Ge!d714a/M`iQB{& ڮP͜]k%aSl͟+2_uh2YfR;!a46{J |xn^C|fV`>* <^@>nHPN/R; 4sHZt#S^z|p!C_c vC !@*2i$aIyY^̶ꘊc ގ9t![,G&j C5tc@: ы'e@~$07(M5wy?uUSlaLc_^νcjYdc\kt`,n5oDG^2'E*9j0h2$Øq0+XIzQ-|"j#~p@lEخd4rZemp0&3wvMdPCZmN@&Fw961prx5[Jױy% #QM< +a0#Pp˩yp?EF 5RZ!͓cvKPAθou/V*T Ƴ쓡zlQ5ʧ'K?Ie\݇6ߋ/$t XeCbyWVH兽˗z5Q e}]3\ו8T,s*v1ֲwV(؜#kp$e)fSkl쏯Yg ՟ z(ZSQgsLAz]?*3ÞR58&ם;~ڬ5> JT"DxgHLxF_h1-=(t`K:ImYX[?w%ן],qtT23]#.$8kdDuӏJ-Y 竢8O.0Z͸x3,6?䷰A&0Mq~P-{~@w'jRTX"*mU.r( oΓ0K:h@v=gs~a1HvҠҪ7O s&)*΢է% lҰ7%с, mތ=[3`'PN/#xAJ)O%^>@2o!&ƏQߟʵ*rzbWzU|0=`t]Vyu+;Y,v-qzD52ޏ `=4A nӗ3bAAJ=D:2p}w'\[J%m}`\X./'p'HMc/cxT(ʛLG v|7٦F/.ýTpݛ3@}WɛM J+vլc~U*syTLЛGRDCO{ ڈDgɵ}DܙtI@MWⱝ=[Sw`C!~Wy]ٿ͚[`2M-#60NRْxyjOe ҳ$;u3ȎEZ͊D|-z\»1>B]5vahs<= ކkÚA=H{ү{ZswGQ|5=} εw*1~}]_~{bNU_ r02F떭2, RN2+)sd :G Im1C*^aC4gޓG%5#7$uI^=o^Ӡ85`qW9'e, W7\?*xwc1\Я)gA+oRjy>n5AJ? S,ޓ7,^x _sN t #ˮ4΅pgEIwr[.%H&xv0-Eᭋ7zF5|}}s7 =iGw[ DNϭ )wEӮb5n}2>LiPG%Q!3)m;ooGO)uE_MqW~LQ]7aW}i6Đn {SaiL-r 'pɧLN<ٖg Kn'ߏu[ {iRrm2g2^mSћ>eO?Wo76čMPR_D%}Rb^GXÄKv p.\ XdCV/|?wlԓMcu8ʗ3ל8EZĻ4vX Ww|!-;{2T!iG4R'~& M X)cRAu9J3.?k"U-C4lCM#i5~_n!QIq\g%(f5'(4T J- 3}0bK&G>0"b)==A㌑SfN!@y-b QcE`2< "[>=GH99`;@gxC+@j){1WvƊ:pJ7 %Wi @'4V&(\#4gkO%ҧ~CA/8PjƿuGHHϐ̒a8٦Yak$h}YVqr ݬ< AK~HLHE2SC|𮩗u/r|'ϨW}Ϲ;$1; ?Cn\ۃE0#XrIYb[aaiޙ%ɾ&(E +YOܛUq 7\yw|}ƺ>yVϓilH &"E~\ g{ސBY0'mZnX,A`-52x @zfѩp5J:IRP!'tmQmu=7+EwOwl`+KΝ8m-Ɏtƕ!z^;"2;xڌ*"{݅{VC=unb΅_*6@vtT`5k!gu>KfAϔ7HJѥ/z,N=D4ufATSBQ̤׶ ٭}1`vy Cfuẃ_~ eѣ)5$iNسyE/dss_~tf\֩c`ÛQx~m, ^3IGاNQŦ> pahw$M۟@%ikzk>@:Թ.~|}]^O[s4xa-yh‡=|,Ϳ 34oz m ?! PF~ ۃ=>QYŽU@*ЁwKC4Rgrk:HvZ=keYy_'K |LS=߿0qVN4Z39+yy*?t?> gm125;bRwyIkv*'oC1ӨwY~}}yK¥HL:~w|, h}h_,P$'rfG kO P%ulM&Pgו#ֶD$93amϕ fv84}ԙ;hxtQ';n$s:kzz˦d>7b7vb%{4Эr<+g+}a$?rmH;9}. c;e!GY9yV+}_7RB]Z:X!Qn' n!5v${lПɒkP`oXLT@lܑ(t 0;E x DKx^SZ!Ps1-QaOg9kwC7˄?ՙ,910;{{n^#=zkF WAع\xz8w1& d󹟣m2^nn墯W&4Nu nW^=O3p-#p}ZxъG'%RO 7nj *M}2JoG: wUa yYpv>$$;L{݇Y&:fٛ%|p%{K3|VD>Ǚ;\ 5=8n! YXw_"uA!){#YO^DZ`ۯ (ݯ!#*tZ{笈IoTw9?#C z ~iZ4؈ْ*n/^ܛZ<qyH=Cǃ"7}M KY Ә>0yYlfFA:@0|Z~uG#i`\^\_.S8;r@w O%r83aw@|M>{g|ug8P _~9,k/:*zPԈˎ,K#K }B D4 h8фc .՗+3e37uvUf=-4uH)Tq PJl3Jrܽ-B &*=[YlB6`i|b03M"ɛHN O bDG%> d_zf嵫z"ώ^!ɒcp2/XNDYF٥E2jURiz:Av"xK '3Dc}`>^GzPO3j&:ӌ׶K7d2GB 䈂)ЏI`nw~~XY+\eVa+%bȭśQ^ޯ2Yw"_G-qË<"O!!e$2DšqD,ܽzF1C"o?ôt[Rp{33Yԧ ͬsR78pV (y߸RSEmd=G j /e6 <**OA?c"g& -R4s&Ÿ5']2,E~<@h/yq壛=A5 Y$P"3Fƨ'\7 p\3*vi^y"*2m%˩pjޕ .g` GJE˛P1[]`h@iFJ&q`kĹc䜖T콒DZ(aTX4+]7*;XXٓx@U|oVn[^o煺4Df>U;PKt..$/ :r/ ` {t,=T1H'na|Ұv['0\H=/596C$]"1B Y*br0g̽5M6jԪE&XlH{^;gg`ENkv5Pdf4,~zvt&߭2A0sM7(HI(3[P?>CSJo/kzPx^QTm9X1U=备Y{,{  )>d\ؖzi)pX%}}o,)"T!|[=9E[c\d+Sw/< 8r7JKDtVBݶؤ(ZGıf\WӘNFK|VO i|S*R&{'' UyxuwϖrF>T~гxV#^gd;b'LJlmοcq^^>+B! ؁8e1FoƇhh@*cE>H\ Bߙ_;|<|S(u7~&O{z{6!\,a̿2fRGSsQtzLg8bu>L%޻҆#^_]6:PX$bm)ϧK}khQF(i&[%6s^a+ +B)E~4ϠJRjhVyf t@f*'ǒ0 2)t8{j,rEߓ[Zs}_KgBϰ{Ȩb׵o CaYU(WxIfwlwovël}P/YP@3'+,"!u?٭]ȷi!PF ZliO*)>3+@YH%v3d(2vz&;I > eVh?c7 ],JX֬ޑ]8.L:}ߊc Z>O(SU##@67\Oos^uUvv 0ûu]g,%\̅ЄaVGyzR+NgPÿ~BX.lkRU!\/PK (ٛUlE[>y]\^E=7\]k%[a nAu'W]Jc@T{ ޼t %wb]ltA>_gwdh\ v:>DY(#lQ=[rxS8jo;dX$e= <;OB'9W*D%wP@\aYbi<:&OCsz WCϟr=_s&q< ͯorH#]@ZK8a YADlz?_8',_j,A(qo!o8Dhi IDATqRFpRŠ'^ Qf5wwƴ:F' !w?[h#:-Yc___%ڣ",3+ |ѥ _K oQk un-iIuD736A = +gHNvD,^nmvBH?gXN8gtձ u*"2B,]QeA^sr_ *;o9oOUA%G*tk, n>+0 d=15*KN*'&hqo,5g(X6րc95hLPҹjg@[IGkg^_L t154,51qp]!)p z3.{kQc)ByA4c9zj9$.8@ь OGHp AQ0# sӺͬ0FZ'*f]%<yQnDwc&^ϲk-p[-$MDxy05Yu8,@?e3c:JūOf`Ñ1F} ǻa.ro+`[ g e@d4 !<+-Z?fF&a6ɶ[h&ͲR)e,gZ%،=4h \7ΧWȰv% xșQB!gJOFsj@)4ݖ Qgp7n_g1 1;ZҲ3t2,J`_=:A>Kt8~TJgGv{65PW kH"aGlt>` 'Ity|5zF1㜩æ~IΗ9z\YoLV0C/^dH~~r`|! %ttV j?`icVJ$u ދ.dQa G)M$q_C\S<6'XKtR&bPuGx;+ЉXZgU.1 6iM,VJ`g2:yGb5^Wz)`սJ7a͍B^_ kL`c]-5y_kMݵP8V'>ԴK+hd26IgleffC@#Ggíy X{$C L̿ÙZG`R~ ^~YUjW:)7*ʜ}KQ3V ~>1,upP᧓seHdNJ@uKzFf } z!!Q%TO/JL:bUȳ gTE3`.f05X7Z-oK@&6(88fKWkq3Cx0O(+ ֔$WFfJwpce8x\26Β2k`/KztAS9R~Ӛf]% 8& g7RTb$9ȇ*N [ΓhyzwRs=A.&;0)z:kWF XsDr< URBTM\&,2VPEUM*ab['3rTReyG^ GO)1,г"$y@6ZQYC~Q sdoZ -Py#ei^OtLV1gxxPԼ A)W ߋwHCTr_¨dfaOp5_0g!M>Wύm|S',1he'EW܋SGl%pϊ<ӊ_]?bj_Ϭ2Uk}|>E2Scxl_oͅl~k)źmnyKCǐV}9/wZ"sjגˀN{j EW^WY;ضS銊ft.H̫=X|`FʂR`Vb YN'[齌[5A=\u<ޞ6@4x;&>cEL<~Ҥ|4}`}-,XlE~' 1@]Ckx|tE=V(طb %Ӈ}zNr4#[g7Jޛ):_ytn3say`@̡"_!DEE`7 sZʟ^\ Q & (KϪI 6%yͷuրd$c Wks ⛞]{HL.[mR|qr}f;f%2F])@hφԽsztLo킧߁TJ*ƺ WvIŻ@3l#8m=zʺ)Ӛ_W7n+lVDhu)ga2-zMz{[>ȡAH,p +A]aτc}#谁ӄO37e8£@9 l-{T8<ל`CA$c,m) U~ޒ ݹ.  ֺ)<=&[u^'rl ߻EAEQwoVLs4hgOY˧ B{W}-vw4 GUHzczC6yE|>s`>ĭ` =2[aG{k`r"gZDUuPJ(;;w!HAqd)ʅ]K^3gU+ey s&-FzPc;zsbTO 홀E9NO<3xNo!Ƶ:AxG@b;GLKtρWxZHWs|X{ ʴwVlx]_mhQ<49U)*}ʦ!-mAϽecmFu],|(|BM^~0yhf5zw&.fU5W~>kPRHY*Ghi\l/^*Bzx*)}= W;iktRoNp[ʤxNqBiyO.u1rSHz -r6 3w+3,y`N,2ޏ5Һ<[rVa{ t9$1{\ Ճ4&H/+`QnYYh vu 9O|nXz_(mP^DŽ芙6\ 7_3Orl*}SZ/y sgLse A'#`[7ߛ M8$:ܶ@PGWEpTDWQ^GAo(s/=h˟b_3MѨ2R089zA+hG9 +:&< *~-s{3Qb{b18ӺIƘk?~ԐaA>Ӑ٬nw]\}GyHO&73Z)}gSDE?\$f~ىGmȀ0kD˜7DT P`܎EVa^X F:/w}o@JxYus[7ԏeCdLV< Q{Z,5K^垖;q)#w<=x46̀m^wOp<:4D]Eq&t5oy( o}ßϢvy Ya޻zL˙ܖKۏǣX~,N|0,^sq@d.Q32hdrY9<3P@EVvh*>ʬy2 IbS_[uc=XJޙĬEZzkэ`Zz 8tMrS(3eU@uu] &704u'.KH4ڳ1FFϘ,]yqYVKz_1J||uݍ݂b1J-¨|?˷7iVʁ Y1'fv;f}&EZ?[l'FǹHdvЉ4f6#뵺Pzu<Ҩyݳxvg2c}]չ.D* 1]H3Tyoʊa6~w/1vqcB:1skH{6W[IPRޙxC^/|_ u5]G {h"_眸;QI xz؆8oc9䞋L^hwyy?$w|vTLsmG,;&`,e;30M1d$*mKyqˁ^۝O0@! H]~P  %N+X]Rx p ("FWj)/.SC4IA1 / \(8u\0Zt''FEI)=#<[mLyvǚVIp_w>WQ%T53f@M_ϞXEV)b>ibY㳫=<S;4o,~ ;+c>-.# SCRkʳŠ{Qȍ9|);g:Һi۫<#p++*8ypǼv 4j={J_e˞6Ssk@=ߓU-:'gunjT_ @"MqU9qrL[kF; ~nB&yM DfN~2 EYf #=kn(/ de*"$N!/j’V2 3-ׯ0-brY[O$n=װR7i+s$ 2FQ@ж$\,DDd:p{x7jADZ#XWQ`~e_:H?WPeƀ#MeRDjVK3a$PFOQ.)ЮBN׬aG\jfsQ +n"(tnU^B,.qࠀg@:8p]wHyYѨa^+ nZL(z3y5'+o x^+l*oU2Q(n?,Zxټ}xZ>ޚDkX# wAA eb9+9@Ox! zp<{ AaPX0Ӓ *}&Qdr$)FuȽ[!?;7<a:qRE:QM~%-(y,/`!z-hg{d6eg˜2LeN7ӦUJ#ݪD7TǙ#ܴ;z(~e>0X]fݯov}NkxL Z& s19W#-ա;sU}ڇ}eVீIk*zn51G@8!Ay80dX_eW=k 8o哐a'<M}ݥ+5P2 nOJfEOV:aiJX&aP!u1pR772ls((1,j~oc`Nga9M-v"q ꢌ(XUpow-]rDZPQicy@v|Hbx#s"Ű@~c ;םnjE(lHbL0Lcຯʅu292dV-:Mr4j^,~=K9&Z6~է>-j $hE;U_8]A  X IDAT[|,XF|X;4ukABPr 1QS[Zъ8,p2tUXJeRNMOCdG,ɚrT^j\ݸo7=%݆MDuݮ} -;{L N +H;[Ԥ\0#B6x\h޽en_*C3=Kb O}߭}:Pwzwos9X`"=-^ibL0f^ =g{hی?_f(سP O\>q1y㨿ru!C:%:NL$xGZ_~Yw Zo|?q"S^|Q'~(ϟߋp3٥8-A___"//eg8l8!{l\_8{e͐DM{yO{ahyX<YϾ uŹ\|nq6'8fE8Q8ny[r~%h3C7Qܱ .gKPdΣ-->)q8I"7 tKuܿ2!ߓ̢Is%#Aȉ|RqSQ5¨5hu!q[( nnI_[+-AO3"g{ L\\3ь{>x>=+IF'=Z| ᗮSLt 6@TH,xl}axܫ"E林1 k{< ~fѩWMV#<}C%x%0`Y'!e>iISWSxz:'d,;2tN 3(hZVxX :(hL% YkhxWH{vTm ZdAYM:n=3ez,y6>g$+*(ؿzT+Zwz:89;zil;w`x<*8\k!C䬭\[܏΁\=OB>o;PϾCKmjI%kyv΃Yߣr+kWou]UkhBNq6J={Teet4!}f;7/>?QkS܆DE8#- =5. Dg*`38<P.D>o\sJXֵZw\Z-HQB@f굧kU|FDlKƯ2:SfAl{-C.a10&(잳^%k-,￿nwjK譔Xxgt>*}ͬ8p*.DOX5հlQ:)Vxn sq +H_BBc9,xi)q w9̶w,Vf}3)1D3]ұ5=+%?*{Y(ІLG5Hq?A; gz9Azܽ)0Us)?s#V.vpI; ׶/Κs.׹Z]a~^Ssr@{'~70T/?dn`` ,m0 ^`rXLqF8zGCsx܋ָ8`_#kOS۾g86`40c(bM {ĄO^k5D#X*OXm:υTkϥS3U7}\ݡ3T6P^dLڔ()1Xϗw IĻkS- *ZunknfljW#iǐ<8 mշ6-Bd̴,s0/ ʑ^9gsogFGgIx0f~vӿkZc pX==1ՂH;?ʸ\iI u9uu2 A5rʹ*Hc.\oʊ$T, (r_;Jpgr 85u*q[^= VA範l p|~zR)~Wkd6(O<@UBǹʯ8x;UZBT9L%!\êD'I$x ~Mz6,rMm`Oycz=MuW9 N݇ABbc3c],]i-&Wt<ǃ}طoIW?~?#o!7v&^2{A!27*cI!uw9}ŬeML[:DcJP*X;!Dz;6KJU1.$``f CUG RU )}_?{ݻ@ۋ!-JFOH!hQ*@p/e.z`l. l,QnTH_B *8|fc-cTa5W@~wY |ltّsc+XyRJܹ: #\B5$ eլaBOr1Q]ðx̵[3 -cɝs;SaU9  ʷ 6+:޼4*Wdc''<ϪPRŒ' ^^(,?қ "XP1S C{Q xoN1qy~f.OL" b'^tD 'J|>f -º$-ZD~q;ٮt .:9jTkP .9f_/+& ,J&%e vWgU۰ʮuwx,Up74:y7V_?X%.UCL53~e C-1}}m~;dK~7^Wİ\:džQ~ z_L:u;ٓ-Fgk<7o#B,?$kȭw${y3U<˛U"f7>'{5:=oɹwUAo!ACXSU{qc yoKgc.UUEzxͬ/!UqqUѪϚMV#wĸxF'kaC4ɂx874T]{\k+5H*?oUE`4]z"C},Gwx7pLLPĐхP5tGx =4k,W1ԝRB^wָVT9<uXW *Ͼf[;*"+>pIM,1 9%l xX)cfR RB5'<\YkOL7|W' FZidy#YUǥ- #^5Y-8'"`;3SHtsXmZբX#V՚H&ryG r!oς*=pT1Ry~pl4R]suG&/L[3F³}{}yjO]{hpE֡9E=77U_%\;g ᙝ;D1/Bu5#Gi{g]˺D].w`^mPB;6;nЍHd.f=݀9KޏLy^(j -vKפ:f"{z ZLdNdPfy Yր*<~%vlL#5=ϥhkm_+@–w8-E ϣ +u繎%uG`>*SBc |}}-Si{JcI^GBs&M4]E.MQb$Z䀱7CʁoH,[k6C˵Pmk93՛BtmF&CVİ*eGrb՚SNKP5 vV~߻*o@X9f??5M c8UA"+0-zw!~0+vU&vAU[b}ZI|3me=XНeS* ,`xR0'WJXy˾ljyFFk $c$PXӒ1}w~’(8Y@P[GX?S1O=[*zM z;"3}< rCr>2MoU 85eCcV}p`}P#'=,cHS$7ŽTa ZcT PJqhJi~ :՛GCgy)6Ii ^=&a0=o5"js9t4˵5Sq~wu_1~O6!ÃgVZPgr|s,U{M^We>LlhVF\h( W.lzgOX& x}yMn59_? Vф4ar}`Ga^=ߔ8t`ؽ`6d[>3ÝuHk*?~z,L5Uuhf.fu25[:]*Apude>\^ ,:*kD(j{d笎e{pXTR ^swg pLrWs)7^ +f*=d׳zF3A%H51m a1 $]`]w^ҌފwKMaO>@ɐ VT56Q+oM(AMxeGN0r*4UT>`(m1UP^hXKU/Z]<">FWo${#J`.T9"dV]~2Z)=#Pg{_I=^F}> vpr~}w/lhaeff+b%~n۫W#PQ6S+gy:F2=Gϑ6̇6xE"+ .|?]kv iizee% KgH !83L➎':V -R( &Y^t&^(E?pΰ|P`/C 8GZ;_yn!z_+v?7;ӣA^ Gh&\K܈cH(]:+FbsO;Jq0ʓ{,ՊwtQ˿ m,XyXyRc{-Z/%GptН^U.߻rKA:r&R{Mךa 8iCRfP읞3\ P [mfL{İ_#sQu|wn@}&gndp&\쮸h U+|KO?2M^;,.LviX#E29q q?q.0&o6[ Fjaف17~?-1Q=(-P٭[c=-yT bn`FgdkMX2.܊8|%FIXy?tκХ|%$|#פ9,D X"Ls:pkǰ5=xKٴj;@|\3{0qX3»VQU[1tUc@ Rf98,Z*=E,l()|!4q(dbrƾV,kTUpS5ca[ʻ<͑c x~w(r5/:)χ{7g7:SsAB*OdOҋ<?+Llm2q{;vmGZauLX1@7n t3%vM40*%IGMhy(UM#yȯ,t*"Mt'E!JK݉H#ۇ$0\]3MF(*=>]/Ah8pG=Ǩ-@Q`X^J9۾[= |vX{,>}g̷/c$runzv\,hd ^ $TB۸ߕg$ccv[2kP{S.|F&L/m{ ŗ5P V#]6G`W`{ ת( ס7#ˎT8GVj6`95wfg5%<8ƈNV8(8<,o;gytӮo{ó,E {{gX_EوPsЊf}zgͩ IDATJsU_l pB><Z&(4qhvo^?+#N̿Bx}4S,@]g#6 { V"1V8+HʥAZ YpP {,t,9p2 4ѲE*VH X4g2 cۺrO9z#gpt wБqK>|-K!߄^I`p%6]7`e'pFoj})8(/B(\x0p߁tbm!/}}v*q`UiݳYuAd p-X[C T{Ǚ얼O5^z[/^+ov"zib$LJ^t^?y!lfѮ$#k:^,M)2NikMȍ{F($U8>g8J@6Kyty~ER? qgU_;v^_/@3Ƽ^5R> MN2!W>a]}fV?)晚um)xp؉31}c^zK^I 72!s sAhzR 7 "&6W\0A 7 f<6# 鉭/C}d'O(J'|>UM?sGZ.{!Ot|g8> ,Ǩ n^ozמ..KDS+X=,sw+ٶ4*갔T;vdx{@)tP\elQ$IUtKY8*gQ>b->PFaHVQ/z+vG-#%yu-= ;OP6?ǣ2ccZpdj0HT"&kiE%uѴ3N֋Ks,E2@6ؑh>_;, Sw Hb1%ĠVL༓ :QǏnBEz^kZO%d.TG&P*@.L*<#s$aCM-aQj >'d80ˬ{D%U*Lk4Zkm #ቢXZd}gtjLnZF>l>/>0Na>=Q᥾gxtiY65 ך>]9Yrz9kP&ybtKy42=x1:Dk6P`8=ZMn!&Q*/Wә;&=}13lMsbє#B߿?GʊB-w5'K ,,$ "Rݩ*o=*_=}1 j6Ly<Ѩine=,{f0}IxD[Y 8HߋQɰcʒc~zzVeSh+~1 F;{)[>8 Z3g/kpW 8Ѩg gwʼ~8h.+? IW YJbV9׉s[{80Zw뭔ߋy 'pվ{ kjIu]{j JYVR\֝neV{۟O{{E=?TL[dKoʏ圌oGҚ@l6p|==Tq=͟kw; er3)4ä6mrUI@-9v8'ވ{vG(׌Ґ=>۽I>pw@8gQP>7sV w>D$ht:,f<:?,])wo_CO`ǟ3%O ŗc0HٔF;SPU eCvgp"cçҟ ,RE+d~butb_[^ߴw]36wJ]绬1;G>nW^ksG#+,k-3k9V@jIP"Z pmYZ3usjk!Q-{[ O@&5BtpϊsF${(}h`oJf&&sG iLt5uGz\۠B,!O]gc%òO*C>C y ZdK-{s-r`<K½:<^A"ĠԽxjPq ;/p]G*<&mj3$>U&^>:ye\~fB~81n15E3Iz&n.\#ԗnV-cߨr}TJ4Sևaeڷ8RȐĤk(לo*8GEBIW`p `H;u+9cڪ砬Ofa-L vjHmt̢z=A;G0Kdb=g}Nn7?sv1Yt߻^dS ;ik'mPP֞$pОB4Q@xE СE $=XIDIb1V"-Z g5g cƣ^Ḳk֜U75"b(Pi`XXe!ZsԻW>9XkE^%ʿ`{!ۺ.0h?#pCw@{).<YP D;_Q7|+@rlJ(>z^yNÜyF0}vO?O;2]-Djk6Q ltFE;\N(,N@XQhQ,ubhJTnS>4&Z|Y_,y"{ʆsOߪԗգYs]vh YEPB Y.mnn,QQ)SU;_e/I: #ij:"/0ggAFrY+i Nj#)k5+kX;u t/D[s$XiDukY3~8|㽭c D0QuZ*dz |`BldA1SJ8<`30k OZ |M|~d]9D 4DQ#s{vTRlgG'_)Ȧy5Z`|-^0uq1MmiؿW0{^?Us➼}ZLf@axz1Y$"QYh :~U-X[: ,׶FO/P|ժmZ"A䮤@E1f$ ުCӢ4H1CQP*O՜5ϫ{d;X* ҅uzc!ebl(phWiWe]iHO5Z_xޞHJ|V!|8Yy0i H'Mߒ%`IGDq7k-!W0Nr[~eDΤ2KG'ZQ.uՑTUH!Gt)"41(Y+ս0},R840PiJ[&@sU4h n-+jP+X"n-z ѾLHAh5";Bꆻyga*wc_]ԤH/ʬ2u/ʱЀ7;S'+I늚9RvQ^*zԛn!4R.Xm@"*kVĖKEX( %֩Eda_;[첤P>Z9{E1[^lM`, yY{T~uJ'AW󌤧]^IK@=ՕGo?y_GUh |~Ig!O^YQWU9Cozteyu]::Rs%0vFhl\7Z =qY8AQ #cGٕYjdwoV0)՘]gSGbnmFXDsnizȌF> [F^o`~(0(lwuH#ـJ2ЋjU_& eDRaZĢm<&2BT|҈ZvZ8^3$ \˞GM1xO`x{IyC}޷`,ހyTFՔ_ \swqt=*) BwI}> 2ݍ) ӪM|_|۪HJ46<8-8[Z9FҖLuiV SuvX>F8Z*p}Z8@7*|^t 7Dȇ*NNcaxj624Иk>\N_b1"kmW=냨$\+)[in=k8eωBk-?g|**!ZH4,F@P* 7Q>[Lb 9{~`(Uidt 㾅W]sVǛ Q r gqytAKx[sb"}+Z/?j"нqCWw_4"]Tim,ש$sK <)Kk˧U:*]ޓѻ]~g?*+voPtaȏƧ\ ks"}3TM٢ľw9hE1P]2 מΟ\.͝Spus4}e R'`=JnhFL@KΞJQ .FرXҞ.E4 Ώ:0uVC剧{d}=n&CPg5A[TOq%Dn<+vz?k׵~|izPc^}eAʬ1{]x*{g,&a$"3o!Mۢԟ|h "40vWc/]3sOƄ}_O,R= )sv#'se{K ?[:CtS+11ŽAVWp1FF耔0+-'؎gDNM\NoTe+.DIwzV/iXؙYrlaW` 0ٕtWw4Iu @^r30s2YvjF)ųE7ouBch w`(+{7x=oyZ?Ҧ$|,ιeLBum)g՟-叶!/͚oxzJߒ Qi*3BrFt((5WH. IDAT·t"Pnnժw#|X "x +RY"c|l ȕ9/U>?yQ[]x-{IjbPeC!m<;9F˳L bJ{\/zSUѐVa?}z y4D"e= HwmbQqh& Sg*K g k r|lUt[Ҧڏ]D8ximÛ΀D[st<}}(_mNy$?Zׅ1B Zu5]x(:OtPO"݀ۖߪ|ѿ%ZTJWt&N]}9Cs3QPG٪,R6UAlϥGvMߛGW V#-ŵ9'u!g'-³{Gñ|c;D3plE@d9~]w>"1F< _׉:c@ Zp@WHc,8CO1 q~fہJpS34sOq  H :㘐-YVmzqM}niZ#4v6WM燵 hV*]ĭV^uۜ 3iƛ&SvyEL=,1=C\EB(xc v眾cfF* >(t|`Js`JB 8nuk"9x3๫~f:+c|N5"BZ9HιrU T y@riN9OҀMwuJܩjQF.{U;)F(R׵RUߺl~^z,H c4*Ty|Ԍ5 ƃn GscWvKpGBZto>?>~  WQÀQsvM^|4^鱼=xke`߹zsmҫ ovA.!Y{X6U,f.]Pa?)gWZik(k%0i@B@uv4aA0D|}3ɕ;=ݣ*3z9Q(xMĪyXm)!xo=41^鲖 ǜ{ZU,}+&c;cznsz]xݸ+UmaoF'Tw('aOu09`uaV~ܙ7Ͱ̉qo1a*O!], S~qajP.Gkӻ0Q?^XxnC>vs[z6 2Xˊ#}nHw P_x֮־8vPSNK;so;_ЎE<0!ouI1&[%"F*1Px}?ʬ"u\NgvywEeHvf_KZxzrߕСXK BܖyThxKdh \+Kx2WܳܫďstH սߵHQ4?]`6ݦ\mww)^oLEnP]1XN\?&X oؾnX>|盷w|صQ5b?I%'ꠊ2>(ߜgkaB91t`A=P5@c)acãҨMvup!Dȗ[}G\}~f Kq3AR+jcp!YF-"MH?W> T{鹖1F7Sxkz"vQs|kzwC"u'h~U;fob]=JVRRնU9.e a@3ƃ_\mmxHm]+%Ð X*ϟƾno;Pޝ|l5{&O#؞ @8^8OWqLݻg] %59.jy %^}$L,XM jTŐ]'vyzs'x?Rc w H $q(X׉$Cy;/B#`XOdC|𕳗 ΩF\ݥ֓^T&лe$gcˣ uFv^cHtWD7Ay D}$Mp7HFDF^X\F3=(tֆ-ul܇Q=+M[fԉoq+T30kmN0cb2WJ~kPe`W4AſX(rbiɤ:HcMc@d'bq=spZr:=ORL$kNkz 褩g9۴F{8`xo R%=q WmȞ8@k_Ն߫>}݇ʫ<F}v׵~?/%zӒFЅuґB/"?}YBbۑd-s_;ž;ѷ8ԕoy( y~Z1OcǂȂ\ ] 8;s2t'>?octzN)1P=$ehԢB3z9imohg5+E5W7T4 j%^ִdֶv$2S.QS!L!^yeknռ8h=ZHFmuk{T] eQ3/F#-(AfczNEAk^r1pY)@Xvmys?Qqaz%{`9&}=[i A05Q\? D'T|ε*<Nͫ&"4sf4w }fAm<<}3/RhDD#Nq%%ː}:`5o y-gb^{:s Or;nu'5@<30n 'C),FL -$!eQ` =8uf.aUg(PS%\%l J9g zN{`zUc9Rщ{xxGuYj3avFB`,C&qaÚYq]G2^=GG-ɴ5ӮV`|꺩j+?n6NZ<+}~qdԶ(apA1n:¶΍.UȜ.6#K $9Nɢ \b]I:ǰj*p ZXx kmPg{5]V@YH3`᭦ 툲tDs_7$PkqT3 gwPcb򍦜s]'z`_"h9*__ŏQώz'3ӃntН]njsW|^e^1dl|k#~Ha]j;Zv`|s4 }9l7OaT{Z+jOmje= (ֺ=nc y%#}݈? o{W6ԙ ^`0sX180MZ //2Ȃa lϵ;uUk.jW퀁s(<5k| ;xΓ4"{0wًOXV+S[sq2I6^ռzNͨ[† sOFC_n|Vr}]wߛqUNr:OP]]H:6TgK#" ; {vR WzT2D}D)g1o<Ӝdt&X"ݪ+&F4 1rOe ڷ%eHd$|[yjFxbz*O}烝w?'T׮XQ7=϶ȅ(|zSNNv\-cy4f.F9{ ASCGF0r#$系JBZvmo}퇕8泍VC< x -qUs( w4Ţj8Nzsos+s_ 7KEl,<]Ro4R|a'@~]WJ+}P?`xf} ~V7k*ʞ,ZHnzÿ͟Oyʞ)WgW"*\ޜFyʊr[:Zپj=D{#u#%nWy1:#* (gqu塽`OK #3n\-x6I˕⾝|8OzW}]S};`U{'絮 v^!}n g)?bKMnr1 Myi 'Unk]BU9ABS-ww:qM‹< B]!BPḾU4>fkTBA)f(-M}* *=^'W:4ƯJ>,Q XEAK;Ǜd}΀ k:2p]gGHUs f ڹ.p /x7'UA q4~J+w<]  *;F}[Ybdb֊v<9յzg;{2LZ|=7QU~a\C򭾫*daU?GpDX1LWph\C wxudĻπ,5}[^USfIvUUǶܐ2]ǮDXUm"*I:I[!lq&7pY}7F>xg-7fwxZHv+7ÙêөX$Gm1zAs-EX0c&xW(v&- Q (ふPZsՕ95rIFS {ΨsN@W}mIo5i'E# YöֲkYT;ߔp|>?T`_'bʒeŦMz IDATۜϧp|X8WZ-f{o~Vs-=`i)'3uwf2ba, Zu@~o3ej:9 TG+AEF*|HT=%`ԀDpB=m[hҿ ^q\+@z:&dlh!6L LxSuAĺ[+P㱅^:;ϱVcEQ<g Ҥ2=B54{W=ۇ{McL7tje Z!69k{⠥dY*ӰИ<UԫjfGLhy1U4 UƺG 2D|^<jH8hFq~A{2^IgR Zu5ؔ|UKN.ޮu4Qdv`c_7~H: `;ˎ[9@!z2mΫQ*~^KW4nYHb[/w|W}Q(\l WL= Gywɉ1zYs%i㿛m|YCn4K؁xuSs׉k P z+F1cxx-lc\'uU#k U&s⏘$ & { 0.o !QqFt5b`&UT߽{4#{W„ ]QV/* X*yDU~[l½<'B{ȈG]C^՘0lx^iL@׽^՘VDRiY<|n2]}G3k!=0godR ͭGvCUxo\]ǜX 7<Q(Um+A벱fk ՗5{9F}ue|/糃;/`-L0*BQe7bNoيz Pk;GFM<}ZQ3eoSgGOO<H>>0guyty#r79ߖW |=ICOZ}@dEUdGUyW Ϗ@dxqvɅy̧ Wf \0i ~nhw; JʁOxcSUt$wW DfgG#}4jVeG~o&7xϲ׹K«{G+T]ië*W'%xk+ek#~ G46S{Yu]w]"SeA^ym]8!+d_c>c=g{Ok{}?7ɯQӼ:=SA5w@ݓ`ԯ>}溮*Q)gx/RD0y;i>!ssN|||1}& hny(ﲛe+s1zzsbUO M}BzdMv1W}͹/0`<~G\ZUlCr78!͡qn[GttW@mK֯z*oBɂ9҄H^ BDŽ7 i`"[E3LKpö|b/ URXw0rSUFݑ(+_^ 4k^ ,5c\ʨ,\ݕH zoͻU` /w4o1{tU{^u7 A( ycUI=04IV3Ňm3F=uBU`mQ|`S~(s_} ڪ斸!ub;RH9߻Q6Y=u4\?`>uap~؛wg TCUzw >*5hnsF/>#Af[1 v쌮?'ciFy{s)X}molМ,0ߟ[ meU?pba8rwI!2M<\a]a B8#dkT6ԼMfDKأM0W h$ۦ\h|`3!,ƗVh Wj36P$d1ݷ@,*X[tq+O dhTrg+RA=–% SG\ZzXqZ;3/s]#Հwd'K>+-qՃ-}øR~ʪ lsM |z+y**P޷p颀xwmw^4(r^cX[qLh9q9u,"=M޵yǞkC!ШN=PA ༁ƜfǪa䂖gU#3wXTZYoyZa]F9LX +Eo9?sv@֊sl=ԇ6Na,:!AqB-#A၅7p=X_P1'=)AD$ve^Ës[>yL#iq #yȿ>Imr4:zeOm1rFnD662 @|~Od@(T}p XTFduϱ]$1P`[s\/HS?ck^°_x{*F^1{u P[]Rok]:W=V_B2(q5(d>TF3(F{n{`>oGSNbNOm~\϶ylJ~?qsî떶hD] ΋! t"y,0(oPiW~s]HACB"zo7&x~kZ uId`I!HCW`P>e ='y8[@ hF/8ܔ*R S/ Jp".8S]^=NJxJ1łwt[yBVx$u#`sn# /?&&M|enz㾥m`6Sדz#C2ıψR"dEPz;0PYPFw[[DzJΕRjlW; #1 [r~.N~~e1p3ޏr_ݸ^Փ#ZkE4ˏ@O.j:a%:%.>\c8.Bа^'>պ> #6ʹ5rcGLW;3Nf[o`[+>v~UwJXBVVs ;k!FDC12wE) vSZ#KOgu[DmzW/ ^kiAK>k$͢ =? 푮*[9p~} ll4B5bl_({NG=o 89)"R2BSN;18ȩw]MECXYJF3|=\^ hx@S' cPS=7![ xOn9kIş"*"-4!ԕ" Y$}.b12^GأI{es'k} D,uAxq&-Do)~z%ETAx0*Թh3?qH7c~72w3n1!j0UL?g]*^cm*QU`5wDz1}΋ MQ=FB+PCެ^ _Yy,DGѭS- ]w.!՚gl&1}ѲX #A7ƈ7jnk-2Cku>U\?3?IqN~;#"`7}WjQuJC^dTq;W!=ޣ>c{MRySI_y'a4~$Tt PC4vo˿UW>'ǮcOƌ}6;ohUb-e ,Qw^{{{%X6ֲγ_};~;ݫR4{unLwO5Y]fsݟb]֩z𼶀C&U!czRsxZ 36T%/xRWwA=]yG ]*Oq>5y/J13Ʈge+S]dӁ6RtǤөym-xs[cm+>g~zȐ*?DXMGVg+: 6B2bd +N}yCuܺOu19:xZ􎃖S+c0HuEͅjZuq=0_; Dn8za5 v5(aYӋ`(]银m+yl}͞6 z粃0k,/i*91|+jx>}-}z{_cU] eX'S(; f^okh#_<u+ 9Kw^Hc>nu5]1֮&׉ZaLođӮA͈$PZ]Tl  $hPikI;xӋ.woH9{{^sS!eBu*CWoBR@ D ->'ќ52잿D臄hTG J` q݄ctdhF!vbJl_jw<Q +-"{ot@kJ4„qr%S"OZj.Lac-9eOS;ky~OQ+_rgy(H97kt}^̯VyGv4ASk콍2=@WFFO>϶?v`tW.pvppkŁ">,12[lkTy{0""*#mfuHWk] ik;=&-4it?}=f:͸jvU$B*T6Ҿ".drg]z|{?_Zkr"8.FUhc8[vOh4E*>>>^'VWUb M J{T&iẟ4 ==<uM;]hˍRU|m?ѐGH&Hl%Q e@Vm4qm=PaU,O,\̥^7Eyo7uWWx2|ގl==QkXw<ݣ-W -=c*pw*k~>]|F2yVS=Vs?yX!YS'>Uv VW}P"[Ns R6cQйu1[#du׿#ɓͫ;IwIz?@[FR}NҚ6JC]Τ1p{=̹NTܝQ!UG߲V@@i(2iy̛"6RP4AB삹# "LZO檞 1FT1Aϩʐ \U _y>o `f[شͮ:'%W3p.D4ҰW҂tzF)EAc. *ΪDC[wU(06*45g9Q!Dhv񌝟ocg֟ q}gUX\2Ou߹5!`U$Yկ Tgj˷*њs=צEײuN0 I446|Gc' 75? p`m-D]^HF@T)W;x`A8ǘh 'm.MKn~c(^fѐ CH]Iy3GT|l";dЁS`>ks+W!XPZֹפM{NwZ쀏1^N/8s-lf$sg12r?Ǚtt׸YBʍ.MJ+qc8u+~l_'zޓ}\}NH'Ӡv/8=@.,$|Z}v]_[ IDATrsD0cā qW ,U!"3>I KO<4ùC5:}5)8nj暆S|^%l)y"C$ŕ{ө;kYNV;_R/~k-\e{Τq h̸kr"pwE+ֱ$OP?O`~ǩh zywSnqgUn6V?Boξ9FFf%`REʝr TYБkB8C ҿVRo#hj}2vUD}cqU нٔ-ϿU:ҷ8h(d0;5L#ѬhWl# "Pyו$ ^U/[ wSc*@ -kn5&,%= ë{?^Pu~2K){]'Wjϩѷǜ=;F壮}i?˺ @/~ʽH޿ߌttƋ-y.:z)P*!E\{t|,ݘ fsr} XSNM>qC+GjRyR'FyZ>Eк]N8jhyqxO{,1xST-]nS{>ag$:۽|cFx)a?ou/ڸ!rZiz2-)U5s[M{N)}"CD|am!HVi^ZN3(1IW~Ea;n]2%a#^zZ?8r؏aT%h>h0b"uRѪi{W7pPs<ɮm YhN@-V(ƙijED܁] .u3Zg06=ߩ\ARe:\vx۩cU\IFPXt XU ]mZ /\׉x p3 p߳A%Q -=BeUpHt m9 ߌ'C1ulO*dU:$qp/@t 4L C$<+PFi'%2M{tìO g5*uk]xg+`wn,A9SD5+Z*?UQijJhǶ_ >~>`tO^+9Ժm0S]Y+GOµ{]8W,z]АM.<H%Vb5BhUŦ K:C^!}&p$ME;@Jy~~ұHҫ,DȈe5Dh 5gqUGOO|TH14@{ֽyнʜù9r|1I/5 Ͽm?>ޝr^٣iFuX'LFY K?gWD0<] ֹ+du'?w[rlbj#:ؾWV8#jg[D1{3qKaAs v{o> AAkq/@ @Ƚ9d@> `舭}{vh<#Bs(8ymMQoߓW(]j:,vIVN)4xOސJCRc`6_)!|RN+xQLslF㜳>o}fm}7GCFz0z?HY]sFq!: +Y:}7:e.Ę K*饥 2خuOE,2{I@Ƀl'.;@OѱA.暏B\Oyu;Ww癯ϿcM)R|h5DP>3z%N_ x* ,:ڙ^Ѳ- ɬg^l?'ڜǙ*,dT<1af66mAG*4[q@ k~vX!R IPUɞ!lcx õk]O5z)mm_#D HXQ,?vz` @0p5H~c . >%?^xb9OG#( RPҫ*ZrMd`ΙWP]D$=kx&cN!ޅ{@Kot^ggͯ8TGWVfx2x':&8昖z :q(=ESq+vᜟ^sD0xCzu ~F5Ѥi/xB48Tݪ+Wimk0Nu'Ե"]жvXd&CװRNWer~E9{ hYP@4\EezkD5(\qOt2Aɺzd-'^|ƣ!B42B+A.OʵȂk@-.wO_ :y~TK#,UL {@IDU]O 61aYΓ㌓ kUyt39eG^[ׇϯm{}aL8Obi=ڜ.Wh'҈L޹w|nTax np>'vzTH'\p]on&ԝZtE.ۘ^g5ƌq{rɼ63JX_뺢T:xRf1FA;zQd*>ϮB(-a 6kGPmK:-/?+8 9z|ubbtw՞֛ qGx׽,@"…,#uE{-k[ ¤eRnN%7dBcږV}Jc]Q']s;mφRILoֵ(Mѿ䉁( tTьq}l{qj4%e%^sNܬր^Eܻs`ihYn\3պQo^ko!,;Ly{uNg}m5L%dW/S!Ï,>?q>ק NYPFI7vWA\'jj9>@|~NTt{L BDq:(\Ͱsz1b*WrSLA36_w͍i?f&e()OR'!]5TGͺ Ͼ\+Ckz+r2= ҟͯThZ9 ggaY G6zw•OS"7QIwl 3@/ ET)j7pMܝʒ^Y7ƀJm5c{ь])XXk㣃⴨P+;a8$7c"NZ8\sbUE 霧+*oT x|@kkR{ss ]QPabzwC[;U"hzۣ5l*R!fLРėy&3,?-u; z;vK` ,(&.1x7&AhH[Xb0(Jwe!PBBȥ 5 "&ݵ|8sW^koʷ;;3·>Mn ~7]2s?=pIb "YyϞcxY :SN9ĘdЮ 3W4m2 5Qq i_E`h`w+ֻa5G=V[dmƻ{80TE.gjkd^X1)6atb) z0PjDЦl(Z|UK_t[O" =O"`4T0( xUq]/`{O[ ;s/Ul<ǛiHMy;cNM*A.qJs % AkՎ45F~dƾ8L('?{ۏ||o'Uvts4iȣ*0k0'u.\^;^7PU"tm3Aogm*d;}[qQXymK9LSGc"zc>?ҵ*My!Hʊs]UKɘ)P7V<1jC j}3Q?dYVI+ۋqP]w*EDpJUu>:+Lk!~NPY33Z3gLh婲(ZRs_#4괻U j+yBxdgfI4i@XY ֵ31Jqqf5(. [h`q?Ї~"^ ~"-n5Tr *\v"b\Ad|=[}&E]x?Yoښ@TB0Y}7':ZMUmc`|cк+g"TM5sRn>.(~0=8o] ^>zY@(=֎z-Kު=1)Eiua.c%zU_- _fV8I:Dckp 0:Uۓh3aYK+쳻iBF<*.:P\ k Ʋ \,FIbp8EwP]cC׺5R.uuWuog 6_ IDAT#j 8/g{rh ?g ~zMQ*Zyu/lAjQcIjEUlݯe5]!~I`T "OڒR73]mA#yV\ZT{xnlj ꪄ[\WB1=f q\0kHV{-rþB4 $ٴA g+Y{~5 X6j$TOZRS7] Gna-"8benVܵ t\QMC6paт3~-5Ϊu< ))xgESg)_CM=+j&oH$]$x\w0FuaZ88Y rK 'O+ioݳXBJRuIra翦sQsan^OVPxE ^F9Z8W P@0slcv"Vfb1N Ye t=F_+0V]㫸U| V1A2URDbdd|N< HO?]كb&%8R ɴ>zOؖ@75DA=(UFH{PڸbiΝYz5lv e3o B rCnS(EuuઆO|~\>=bV!OXUXoߓrȘV_>C_\Y a5=M5b/Ū=y}ǵ< -=; PH (F!iY;d;@ 4Ha>j}|k AxsFkyUIU+?u ,J vp. 2mSgݍWt>s#^Ok^Ms: 2J|q8`@ w뫲 ؈熌iۘ- ~T1w Q\״(|,qJaQUӦ8 3y'X;@|LHTWAɝ,7M(ŧ;pb/?]G}7)Sj5N (RB ^X4|pRT\5HhdkE+?L`(Y5+Ǒ! 3>kӶ}y~*s{rG)ɚk-+\g&P8wyGY_Q3ߧ`t0:A.bӾֶTek(|ך. {6A= [ l($@@tҊ'/ؔA?P*>{d30l$T;1tkjI$Bf_C~c^x}3,(YW; 7RO 6࣏>}ՠXpyIq>Q/etdk UGƞ\9z/"ۼ+bNOivS-gﲵeQY2)gy}age+jj7Ӊ~z𪅁W%n2K#1~@~F\NSK2%?8&݂|sfD=jh}1\;2"C``bR>uFyՃS jx'ADy 0_}Us*}vE 0P{Ey36_pR x~UU%|_&i@7KBg1wRÝ*☠kiv=7Ѱh`X&8fao\ƎM ci2ooEڰN)Aۈi9 ٯ:v@y~zz%P Z Mڱ"-CY(c 9L_(89Ty9%QDp.qyM=VvaOK*<1JujIŮ(%P,?{#u_k??Cm2vXe13~=w4B\;(@鹺kՇpe5}|TP5L]nw+b+++1!ѻ1JzъQk[v1eb)[חL1; cLY/#͢DX0 ǝ@ &t< X} Gy ^1(fCԛ}蚉}Ҵ@9B[W1k ni }?=g|YڪYŻ!xL=߱~k73k4arɓ왝wў%dl׊wXf~ƫdKt A\YaNb=ܞƫJJ@6'^Nkz)O74GQlKMR=$h2,Z^QAd$?%t˳Lz+n ʎBKB,?3so]ȚrzG!bGl3n?=HW- *-qx;O>z5 :7ZpK9Sl5k'NϵIڭsuɳjg D$RI?ʕ|{p|H%4ű/ažbdqU:+]Iټ:D,hPʹl yq~qI: i6L#>|ڗBZ(\S\Tj%l({oO1Ro Rig5\_@x1& XyyIaqM 4. 5r5ص^ڑ `W0࿗9)f}s=bumRQdo4TMy8ڽϑ&Wpͫ1,zNM̖b#S%sX)Ĭ:-|-M/#=ӄM+ߜ'xeȽmVT$?tpGDTy+͍Vл{Lᬜj4 J=vuK_w1T-yյ"ĩ"w(: ~@͊d\/>x}|Mz6k ]A~̬A *\W%h"`j2F+ƻ o&wvKyj涕~JL?5Y8ZX1;@@AI B> `@'utPLxnu_4Ӧ9ݑ<¯TdZ3s}dn|mNz1ŝa$ī$f,aZYNN;3rb>hcP*Uc4?\g!Pvm缃?sqH#oo&c@Q4JKÞ75-(߁x"F.)_&<[ qz\|ɵ+{=63ֿ Z}. 4Xf b3LSX`Xxq2 &8.U~^[1N~f^&?~nj'Nrxg$1ϩH>/낂An58'T@ 3 KR8`Oku3X 8^byw5- `Բƕ.oa*]g(YZ5^_3@[~ַGLnݴƢ;AtGM:Pܐy4D]ӔQԪ.DwTAbߙ ~H먱 uU+zA촊0F#u)W j;@dVq-Q!hED \V ʹ߭."钪߃E[ -+Y=%b*LG2P4g&S=w.{"DSQwZ! ph^,]tm.ǡz=ͱ8 nvv#K9RBTh=>xuiEU{WwE(Z~>W~^3eUōq.OoY|o8POSDG$b _`qrFn̝(d (Zmx#ĔU@\ 4pRH]֭DtaS?Okf_F>D*Ƽ^u/ܥ m=ҫ֣6TKt5yU}zZK_]Y;eqLSk8.YÈvY=܊PewW]?GH^k sXĥkg{2|л6 hz{ܬ[w"*ge))@[4w5 H Ky* Wոzߝ8ݕ"Ou?V|lAO~Oq&@Z3sRS_43)Μw\R W݌iQ T5Yj,*Jf n>R4w :qmeZ+N3Q@eUFm]wjJO*UpkV'2pOuZY |),v@i <5?k\4L\cۣ/zuiTBy233J$gNm?Efv^r~nkуA4ޫ3vI1uI|gbA_/ c ֲhTQfn?  !{SoTeMm֎8,D8&C{쌡_wZj쵻ˤun{_ P9 r ~#c6ɲǙJjZ!< vphlU-, FL61#@yՊ F"RoicӇkOkﺉ勿[a~Pzfn5g~m#@;!3M,FWQ@ ;Co@߆n4R~< fڹIߩ\En$DPiuEK`uUNZPj D~֎8ccxpuhcc ~έm115{g4뚓^ʴƴPT!FǍ>ٲs"W~&_X`OȐ_*M~U)35j&ilD?".-ri)V7p3 RH Z+2>0JB[z׾jp@ Qp܍yH P۟6JgD3XwVdgF[ۣ4LQLvt!+68uD_qt..tqxGCrZ V*u]Х87FzʺMM @ÞLw]Rf94qaS2;$jЍ ual?N;;~C8G ڼk9u{02>IMoiʹKF p4,pbaF A2I-ʅV)BKHԕ%`MlNރts Aڈ }ճL~$Zw9\kY@5Tf[O!KI m%y3楮ڹ^ՆZ~Yjj1+EXlW1oAPmC-lii<,A?;?ft2aU9{0O@/P?qWy}f :~@DI9="<`nAFN,jϣxY "LX|XYdMc_4$w]BWm,,eP-d U~7>D,' g,MВ,+)@RLj{.f I5I59Zu6B΁k 6x |cBw2uV.ԘC3%SQZ^$b:Yy~8Z%v` IQ5R3XH*9XMaRjt8L8.m+pG :X|]>Pb@aDaa&Sh!.˳݄G, $,@O{C(R"ZB*&cTF'万(4]}U#6vxXt4Ij5jب qȂSDt+fSvlm\a5ZZŽnf9 /`A|X4|bK Qd$ B 6m_%?G~׷_#~3S~MMj\<~:;Ih^>OƪQ@EMXWN_"8FTHa!#,N!7ס`q<\fwTZyi:MS|W&}xpҧFw &#~`Xf\fb^\^4/iR5BpX)ZLKz=s(4jH.it"NDL6g3K;fuK>V;×AT-)3q?Iqb5C |u|yVsΨR>:xyɸ҂+MHÐVIe@O ^ò`C`s;1D>eP6&2 .^ju1|oaa lQiXHdk8G IDAT ڱ7K~~|FgfAwm[j3#E#R`q@M*0qtğ rkry3®bX A9CѩJhR$$W(8Ez_G73e7f6{K{_+ukpfya  K1cفM_w ?=} ;šYP%3v6J;0-F',N%whbD5)jʹ(,de( uzcw鍐ОjNs^]oӪv>'|Q}z$1@p0U`TIHI7mֺ|\QMZzWk3Ve#5p3j<{Wa# IIv{lF5Xo%޺'=v3Ҟd iJDXZk[hW ~gAU`Jrtq?HX_ZsVq*Lk!n58_LAi0әDs NEMQЦUkcl met&[}Mbq5wMqC|U10 @ O;G~Qe\Zq9'dXhr7F])SWr3eǑP1j͞uw a :rF_4I) 53 \%/ ,i.D{z_TaN_^ yC,P'jvVs<* DeUSlFm_Y] i%u=` P%#kTO= 5^' :[]`۾l5qF\]vT H9FaOk^}I21N󀻖@dÑ_B3lx4VsI8}r0i.©/=nc4ByOTفp ȷw@"~Hw|%IMwwv! `4&OKLĞSSN rwƇ~^|04[Kq^QFkz¹ jMO&\U QqAC(!RG3rsj.f=B pN ^O*~h5vmSq`b]kt!4Z1q'4^쒡ZnMcTE(]@0c9ۇ7jk^~?F t [PW5WBwtwY'.^eC(xJ> A#)yZG#e4M{5Xn\B{k O:|#hp ݝgtk>/&5^~鿿" ׋M^l/ eDU&3%e"LR+Y2PmC E14 lP0ddڑ1d%\'nĸ5XY}4?6I[D VhڨN WPVTC#]#?Kd jOSiH`ʈs1tQLIs;N-5,|HfrOf^-f<+h(dil%@D0(u:6mǚo){жֳsL. 4KdlZglj9kBW>Is=Oң˧/@()㬄!_d |*@+_&җPϨQ[*$֑:O+Ժ\=zG;]T\<@Ɍ&)߇goHL4x5DwmF=ist|l.7s&|h DF3kQcV\JG|^%|*i!⛙XS^esҢ l ̑u6dK26 S|^f߳p8w"EE2$-:AYAN' 4׏s>p['V^ O6 0Cj~4iV%M]]Jy@jaNpތnJs4  kcta&Pw56<-8 IaEJ(oȹ[-s P)D5f;Y}-Lje>ȟh6a 5:VPX&Zӛ\=bykژd)u[] <][ZR'T͈Tjg+"T,I J!Ch֕F"a㴌nCn^S@TE Xe\j \@>+JHD 1IW(r`wl4|[4Ui$(kWJЄz*E@z!†Ⱦ]G#Fl ol:pla8s6+_ ^^&5rCLq2AJʚ 5j>6N@ȱ{J`W/[!Ly_D֙2JqP|Nlk"3>4.*IH ]`&/yZrb|7:=У/|B=[7TM0U_}uN}䁤BH J ljSVJG_'<"֢M ^ L{JǼQ暊u)Vbcop}.Qibۼ!WZ R)\KS̶n^>* pLc%MЌ䷹o:@Itjת)I,a#}0uwd"yFCV|Kwn)0P0*i7C3q^c3e*E9`&mW&qTim.lq`BNI{Mߚ`vn=dY6m.^5L?dM ^M".D?3sDL-H|[^Fy_`FSXZJ|Y,Ï$J7Q}^v:mQ=Xh n'Zl۞ӕ? h\l[AϠS~+4V"~K=3>7 хP Mu.4wG Ld UOE&(S̊ЄG h{$bA?1SWeSC'::PhC糴oH-( xDto)xu͌m}|ILc=n$t_4-oZSn Ԭܚ2ߝ7Zd7kA MI:,ևzB=Z~ PK3zȪ$o{ FcAg;[Z `#Ё;- 3[(Ȳ]gת2TMu_x.ծ\M J=PqϳlxKOnᵖ'{i"#"Vp0eLI < bbh3k[$oa.\) UQDO瓱wdO(n7>SK3\p%L|jAooU~z/&Z ~& EP}v|ydEzk;d^u:n4)Kt氃'HMӒh@!|>ۋ p imo&)%``NqDqc0x6F[̤ju)~vbLPs >]\jA5=K,bfȕ] Fb]E2RL񠝲\܃.{DdzLN˶pnȽ3z箝A~w!dzl (lx[T;-^;]XQm̵ⶾ{[j^;3ͩ 5zbT2f!Sx3 %{_G `z3}%$ǐSUՂo/ AwӦԮn/ Jf)I헻,òY@ pxTw?3g"),`[L,yVP*׫ I<*(/ߓe\3C̊f:x@{ c}ۧ}[sFTUEj҂3jrRVA?@Uރ6Kk.~njuB!Q?Vu{%y`u[%|eޛR!~d~6z~חWUC^Gư #+Ϣ6ߙjX}H0q'+g"t @y^0k=ʵj-{PӓgJ2@I]DUŪGs=&ڱ YUC岟ݯ8{kMv )?!)nK#ѻ¶/ykPMl3i_S]2k-f` v-+c( hu Ps8 k;3`גR eN?x8WVv낑N +>V^q ?_301~ڋ]Yzr_-%1]l8NQ.n""웒V*L|!ݨA1ogfQuI 6gӥZaeB˼Y_͐ +3@w6vˌ[Z7Rտ?o7݂ DGt#ʬX?.q5E)"a:j22ݍ"d3d72 $Ql h25rAY d8GZbeW ÒEb5VAA/K|jlk}}/kEF1ie=5˫}il90"iEu&MhXb=MvY52L$-""K`MvLm_-BVtǞ}&f F<CN''I$I-ECh%mR3xΩҳ`, Võ.@򗻲`:odImQj\sU.:H/v g!iU/eeszFg[(n3=҂25Ѵ Ȃ[Qy.8uw|$Suܞ|5HZ 10XbP`+)WzR>W?wDytPz|EA!c5Yޅ,r;  ѵnq$w&|Ya44n Q?:L=0l>Z)Gu\Jfs74o{T1٥#(rI4Ab _̟*2@OWi4ZxwI</fhyX}7!Ѯ<3aOd8k\Z4*cM?0=Sg@H4S5̄B3P8qzjLuAG">49|Um8|qᬡVnXU{:!96E-Pʍ1Q1MTղCw9{oo:\_ ޿DB6̩Qռ9x8um?LwFT(4Flj̞ge 3)UgNSH3Y } or4$\:˱!?l"~<몦G=\.ec~oSk;t4ӢexdΜJ/M y>}s5e3^,س+h#e p;p?X\ uf,"rU]_)kRɃ~O`iV6q2_j䅩) ;-:s- 9W52Zrj^"֑:܊BF459`ܗ6nUÿ+%مcߋ}~UC!hc]qvO%-8=^f T0Ԁox?&1>8,ftqXg)Ƃl5J]$ lWl{XiνVVUUL \ n-~q wz}E gϿ̯TEjijstL/tU,8dPԛ[/x ʡVȂcjQPdd\%fF=GTTv!]Cm}[{eAZ4Ghuݫߙ&d|Z{Cpdv0.˝V90Di/,7*pYu̠yZXv+xWK1vvgY IDATV=y.!^Xc306L~i [#[O_ͦiO5f ubZ+[7?"xgԟW+R[8ki00D0JI +mk^+ Y*X'_õZuzl&ߗkb;9F(@Jd>ӡp2 pBv`Dkj<dwqH5ByonM}5xPDAܐm1MuC٦\NHS:AƲJjr8t kgs46b"b~Je9]K[#XCB~~ KUamóZ!c%X!S2uat$#/cj?O}=b=Ub3;xt܁<*xv?>'MӞUu[GZ^v%w:8zϐ{PXF2Ki&eB|Va[^sOn U 5ZWT_axuխ~?\[~}! Fb0X"]m͂JFLjG~~MjqJa?ь}Mkf_͔}H} ,wP]J7FL,E*-a Duhu:vPXRjc&Di%8MYXuA1:ʞ@6> ָnx_lō6xgW㰲0pCCI$aUTWcO? >躼ZgtY k=n%*&QdmAL]3Vǁ]@Gb~SJ](Ecx@I"v`9`^z>%Q3#X{:lUؾs'p[@*WSURu޹=C^ C߇xU ,RLK$F&Ȍ;X><8\C9XŒF ;fh6ӦܳsxbZjoʄ*Vu຺鱮[P݃/I 5EBɁG6dvD&PڥQ'MV`_Ye9p߂NJIz\UfV7sEL~oJTwnTU1J"獿pڧYǑ<Wv9"00+GV!7m=ݯR7Uok%0twyl^FXSI z g+b!O_u '?Ahgە&v#+RrsBOB5Bm{ t(  _5p73T͊(fj^۞_@nk3:`|-]n\ݲb簲a9.ςq$Q7/bw" w 2a6;)NޙMpT`HJ5fvt!,:(IeR:{֑!-QZ(q9'  3\߀K~|>׷}+2`y~$eT~&plROzϳbZ#sjyA`IG&@CEW~X>,5;JT!I'@XeO2UoU̸Fui)~wLp3)6W[y"vdyuRb+T@GMz,JĦ0܋g:Bo{Y=s ogIͪ.~ՙ)ds_mG\Niyk5scZ¢ɲA=>{Yk[qYt38!DW1o_6js(ghTr"  ҁC]h׫NSQ(xCv_vHǚ8 #w XPm\x_lm!L7}j^~٭ [2-hGU1!֠ro}GE L(YqƬE2-Lwx&H4 C&m҂k{)\t|="ǚyaVb~ifPT>魵ώ+]7{p* `a0cP۪*Q Afkd5$Fњ2kNϏ9?bHYed ;~ZQ ~>&%1uߪQ]S}}}F Z(35t1vԙF#u]~SJ>D82"(I 2pí T&|zaa⊔IFd^qxƗ%g(+A::=4-pYAX ޒF\X8i6iv8{, wcӾ>7̂{%Dm?w-_dl"qPz[=!4/A{/v{&;2uRl[jFy8kۑ~3k`g5TXL?qY5"i vS^0Mad߻ SsjpK;_>$| ޳yy`xu`D hTV|@Έ$i"z""L2jsmbܿO ,x|)^,ar{J`Z$c@PBjm*ϭi؞B'1XC/eNXh-6Y%۳0n&==&d~%XDJV: (Nkuc/jɞq$̖T|\0 C_%WcGc@5i*RM/7CD" %Oz-d<ݔx _{B3}GX/ )6ꑌ9ZPH֘hV6-fIמȰה` 2vaZʢt۱]cT/o\8֯uDgy{__t")Wn!H K&kN{?sBC̎`d8Jk`j)z [^;]k>i߫ZV} l+]DӔOCjw~pwa4`;;g p!uW@W?[yRKη8 H`'5 : WZ o \Q1vjMIU7bYAJ[#2,BJG5Pǚp X5 rMT`ˎ)<ktߏo Z2&'"Bu+ba0J7k/=ԠW"`>hT1:iaGOL$s]?[_cJC~@ka;1X=-cՙeeWL@]i~ݗge= *DU㏫hŻҮ+aֹqIoq md&2x+c6G-5t+-R}=1%x2>8҇gZ/EzY ț汖M=]7zۆ'lck_gu鞱ENԁCY4C{MR=5c4c8'>evzh9CvE3cSRkTZ;7Ov1k^LJ6?y|o/-$څ*喟KtqDwiy/'|/ hvxyPZ025*3'|XH7EWOD 3V<|@jBwZSv&[}]5trfcG!gVQ{ӽ7JWAlxKkRq-°a$C2(imu1/k]ļ.\ JkȂ˦@C1p`FWSMO2ORQڲQisj{|[ ׀]${cJ^gotj3ia &t2>)QTnJq1S 2@ZX"ŧ 5Sshm51cD1T^Py` u9xxXy[#վj,FN,TDYV`օd$㓀yxHX871{Wڬf7碫j$mJWk `YShI?wtV+ZHp/H|bscm~cA+CX%4cd|8Ks[~QyFM8qH٦ SE8VcI^Z8zoۉw\׻g?LLBbBNy^)/-P:6,OeZ?[DLt?_:7܂_x9ҏU ߠ#:cQAzI~U?TuAzu߱V CN_tqsR?K\V񙩆`o%yD6˂9gnŖpn\ˋDEBlf"pp1ځ<1Ep)pp IpPCsĢ.j݁d峵;I?x΀-TtA Ec3 "N_.Le^Z{x:3مǽXQݳ'YU9^y? ZS3ԸOtcyE{OW +xg*4x YWy~#0QUqs͈bl'Q_׬,w _M,c4<{|<Vwua^ cY0pxӥsp a5!KTjyp͗1h cE ykA4K|"acfVڜ@JBg%=ݴxLq^\@X[}7 >Jej f#\vz` `rgnֲ̾g,I3yg8jij4A8)'1"?,;Mijə>jB#ӫ067wC)|~%55'r.rXWqUU,%Θ5"Z7۵⺗@7@WgyH?s]v{ZӮ~e KӺp %!SfTB~zg4 :FV6͒Zzi &{;=牷/ęcj/umo߹1@S~f*hg t0>3SMWs|iYeY{)}C #?04+bI -0n=6m`ELOL4^rcM}U+mdzޣ j; @Vgv+BП  ch1lº.^XY {\@(u$uMXdTqj>#5eˡ49-W)V:-JWkZG|vXϿӢҪVPC(g6s_r@$>t ܁ή]=-`V7%;*v3:^hXFyH Wn Wew^Zwٮ;îY~BB&B@IeA[Eu*`cnTj|mu%rr&mNUPCUDTi ۘZTDkc~{0kY3V@viHd"?sl]oOcR-}oaw_@ԎY*!sX9Tt!bZ 3A*гpw\ESBVgʺwA/VZֹVwsg>W}w(X؟0jWw؛;2t7gCH{vN<`LWOX`۝~"aa\kfL}wyu=S425(b%#KX؟ U:mB`wEu]ZOw6Giѳ[_[a(Ժ瞀:V86$y9 oFl-a_sOHmpm7EA sᯱm1Pp?mdT *>g'ŶlKcK3!?;ppBZmKfZiA mEJVZ֖LNO٬vݘ')[71*>˾G_XQ ;XMA Fw%2`~lg^=位չ2eM#lTD a.>:V&8F>[6V_w;CP'4gPA4YOzR;\i|:RbO̜c Fb%!)d/|y[!d"G&LZ9ZAZg ap= q]jf= 1zdҭW1y#m>aFV&9Xiކfml5`_3B4|b~T.Ai!P2a,?&&Gkq%@(\HGмh-ʪ-?ч$_XY@u~D$ܬEDh_Iq76"Od @\>e] ́]tKؑGeŽUD|.Uq&Kp:64B zRPվ&׾"@찠&;lw79^_pCpM@Ŵ|6{FOLS@Ңe&R>sN6g9wԖt%^dP9/gc_M[dz ;Aч2%WKƴgNs|ZnߐA  -D/P'Ν%AZh:5`mfQ\@k tbfsМH>ϐǭ"dx12qq[պޞDC`^!?FstuWt&w ~s{2Ѐ !9fO&K-sJ{b/UU;gbvGoc+.ўg!BT22AiX-SZvz6\\kmࢤO@aIj~{;!T= !\P[3dJ $0y/mUO%!m+%8˄bw: =}=Tڤ4Z@'cG Z >J2.dldMH RL+2|-җ2JG> R솣g_J&[uwK }y'=CRsߍ'"3bm::%V~r>P?4X8 ' {X ^_`X֭i<ùTYPm,>4U))kp!u eJ`)'Jݶ rG|)TKlAc8\1jdz1d&8OǙÇ=>em\)'t>e߯?2" AK-p  h+cK: q> &bZ܁qY.ƈQtGg2v3}^+;5\&m:@!-bLË&c2n1PBINCh+̹[6+]"a)Pɵym?⻮Rׇ^VPޟ^N9]eeB#e#wڍylNL `5 (xțy2ImlӒ;=cQTx.w:O~*6`~4Ys},8lKWZ)g˶v]!2_Ǵ<xbY}2IɮASk7Q>\`;PY+"t3ևXӲ`FHtV:f{; Cd=<qktCdm[:zq]q;4Ο}I . χPJPh%Ƽ^r~w+uG_GGF{ykO$]4UM7b  10Tbҝjn!r&V(i1mLkc¼N>KIY~`mOZZzbXh9SО[Y ,s± !"[OzΝANt~O*Tzh.IWPmH)g _a⼐9Z=3k5XDp=B==|Q=D`־վUq3:}g5ZT!ΞR!|Ƹ_,$3G_G>-ko071D0r3ݘ-[UV/-)Mh®CXU]t;|!vf8ur̪%0ͤ_d6p>yyX_9dJ<)MRƷH^/(V5&\<dCڽ9sx,y~qmdƙ@[zЬ~ްFxF1cɠ,b)*wEƕjݡCʉHmt!MBOm·mGK8-2d3O3k™k"wwwxa ˀMmP D}/l eSM! Q4e5n })Lk֙KZl)*-c*!;>Wv*f6($AjmgzFB:Ic)!TYs𭘹ةﴪivǃ.-'|ЙYH`MZ̵Մc Z3c2RjiL KF]I{C ۏղ-Fј H`WwQ@>)_'<W|kxS%D+]Y-F!ǑPB đ{M۸hfZ1j.pZ Bݜ(tuyz?%z^Si}h6.awkCq!=%~I!`@@,' "(6F7 aG< (0Ζwm80_e:,tЍ.߾BNȌu +k BgˑE5&x(_:&4j-EQjK M"ȌrwƼ{q,:9-yxϚ*5py3%O&rIp-:G@֬>a ٗbحYP<) ]!=%O!1-~imYZMZ(8~H`Ɔq)ӟ 3S~^3BkVD ǂ)3QRa܃Vw*Ԡ=,j7=DoY"bVI'O@IPVqE ^ ߋ"*mկÄ~>B*~E[^~ltc0&I9Urk#W'# dT1//oռS͔sNXxH x:5 jֆ`ЧB+ )@Xj_27LdVlYSbf~\(IAKS$ M!㡆^,`M &jI ֌9AqՐ1Ώ7Ռ"9jL̡ҭQV?! S$C# pjЊU=ߞtcTŜ~YFF>Izc^G;Oz:2MssUr!9Lx9rnyYkf+cKIJ[M*9ƻֵp$^Ԟsuz6­] A Dx'F?nU 65XrMN,& /E@;tǸo Yq׽*3!}^$gk](Ym!hV 3V_w SmZ2]myVг ϩ:W֗6)P-R} mfIFLN 1]8TS`Гُw Qi[ u<)יL6ۙ2oMZ:׮YsƀI3̊62&m]T;huXđ+(cm9oc :J:`<;q ({Tdr"Lk0+X#)Y``3^`m ƥU_i9]I@\tT7SƏ}]oӛDm1l^S7'aOjm) :SӼY $y .\#Wp__gי#b|8KS.#UYaa.MH\pc_{&riedg lֺXuS=cODW!#ҏڞ&v4q~Ȩa:hEHDܴx8 vZ1tMgUSH=z‚P ->zjO6`䀈':WtjaȇE V&Vz]%SuL9Zz9uu*ƅ{*c~cK~ѼH`2A;y9(?LҢQǤҗâi>aƫ'҂uclo]*x@ďX&#<pgDp}n,Z Ƶ\#Ϥ(r:YVArwAUƵ) o:{F浟OdS2/-N~*[b9zmSjhFdX鈪gA>Q24Uٜj:uXR[AavyN/]sA@-#ଜie %Nֵtm;s]Q>VF;kjZ};Y-:[PޗS$̴ O Ў6 뢞ZǻNn*%CbԸCUǷ~<;_iXH&v'8]jI^iC) p?&[Q=,٪r B^:t&q8 )tjZ:VAw$?hpD_d69uSc#}pCאtyVPK%Zێ&萸eNU9δZlbjN CV0P`-kYb?â"w-m(vND?Į |9Y!$A}~Z%'7+_>}_a$tQ3-kɫjh! I]Hm(g <8|ߕ 5 b5>9{i xϽ _x2KODy"XqKTE+z},vB( wAvTWwc ˠVqmzpӛ*IqTGUPLSGuDӢ ۧB΅ "3=Tk bԥ0 s̹xPcgM6 íA& ѵ]vDӜ>gB+1!`I[H!ٟkWG9X+w!8;)v9)rNLdL^y;”BC2K~Jچum'^~)ȣX)߳oqv[Dv85j@ k0f~\a?"4Fe[N^>Z|x<.maHY)(#j D|IE6R>g~"iך1P0|"ͶG'2Pc%f~i2 td9O<lֶ0mbi)1^>[dVY@ *HC8QCK*f_7`\`׽!cAʄ= /iU#TJjJ"s`Gzp(N"n)ڪ;"K rX i%r@5V5b.MKg~A aYh R̊76HwX>Agv 4zA|H4nZ2)>2ڸ9Nk0@|Bڜ^lB ƜZ +`bkG<'w8St!T$x}o:Tx?V@~H\t2*Y<53:c ^_8A@0ssXAf2N+ +JgpӁ,d}X+A WpX`DғumPX}- ],ɖ !2g& f(I~&JE1/C0ظU@@G>/eE$#Y܏-s"K]-J" Ey3R^UZs?89.IR#tD>75/CYEApf4)d@X&x |I{j\}FAs_jS9S6}bYo-OK=~yDNn^J4HX ="k-+ϭkȮ|CZ^:d|'1 gO1ƿjp#̽v1bKeނqU _qRbh5\d֘L[MXZuQ-Ǻb`v.mZ+` IDATR Dzzd(/s» ·jliH=wMV }b K!K_pj15ݘ*@;hrק~6z d\.#;V,=Ğ!Z?[y2cZ{XKIuL'Z^o @lEdFpn \Lfo]1"cȨ@XT*=Zc>POS]V#! L: ;dI3]3kwxT7,`=GB8GmHZoHuP;`)o yW+ԭ- g tŊs8c8?*.:Ղi{!Ϧ)۱s6y 8yA3@pwsR'o"d. XHg(&4dNRPڜe$"@4 Gr ROnp`B] őupqYLXcO=O̹z}=#a 8*uF&rs\ +"1 U)LYs /C:Ұ*pkjtm}\6vGE@ zW)*O@O{ş |= cBLJ37%=rq vVH(c$q]K /mcY֬_aYF+IV}^&ZL,3C;"O|wz4}闃8}Yd\}ܷouKiџ},9>fZpM`;c%R?wj{h܄{`Ԁt顈lS 'ꩻCAY#q%x̯[XCimFy|x@CLm :G@7Rc͹ Iu,d5NE2^@ ClV?քϮKMOxyz|JyחOxA%zD 3pIꬃ ¬ <^VmwNWHjACXBYB07|SXyGt /9r\Tuou8[sߍQa2u8CݒuV01uPM^Gkp *T<p0pZ%!j0tnQU5QV!38U`TFn ߞ{ܓl#i۶%}Dh,>,!c5(4G -u.+׀E'sj҅VWUec(Kp29͇H'ƴ?fi]U;OΟRT"ul1 h-Pfw|"p{}BH dG%cZNھ&[ ̚</?T҂o!a"iHp!L_Q d/5Z$PSS4fJG4/sяU TK1W;0ɺ8^͔B/BN(DV,՚}ڐVj)jxrkEYr ^I}?SXZw׳ռؤOhfC5yv!?=@7D"Do Xul0Œ'O}j+# N5O`Ulo椘%3Yj76Zݳw.4_ ~YhYݟQ+c;67鼗>46I:| :t2L flaCO2.ne9y^`H:7QOdܢmSx \mU-A=zDTj=y ,@ܖXlj#]*dފ(D! =GئTiU.֨,AgE⑪Zw< Hcp[ ɭV?wG wo𔖧 }[쐚F!fz> ~۶4t\Ѫ٫}mG3vY v a" l@<(YwAW52(UOۘKͤ!(&EiR/I?B;OO}5HmSuNW-Yk[V(@ڶiogtPU8v43 L?3uM ] i  v-tz5@;tÜn/B[dVot]XDVZYֹ]qv>2ÈD"*۟46j 4dcTձ8]GuKu+}*u ?(*=vCW<39p֮ rM޷=EAOV` oʿ jBľwe[}R h+èiI[kT[@uHߞ25GA{,]Y0^9r/V9d@g[4TrЉ! _&PrH@_i ShhjaakFPW;|;kݶJ Ps姓ނo+1|g1n1Ƶ@z4h挀YA" 6閏ڇށ}3==Y. 4 XaEyvBt#ˉ@ov0Vl )kVS#RhumЙf# fjA5&@#sߑɲE?cY*&D}+3cv ?]R]P;JqrA"rb5+Zr䲥u\Z- ]y* oY̗m{urb}Kl=ΘU2&-ֶ2ڀ3sklϗC1V VN)%7x3_]_>]r\UЌ,n0_Wsn8v'~¦Chfx&Ģ L@C3-y뽡dKIC)=0k I4M~xƨ/12g`9c^"60%*md .&ܶ~ خ Rw#{/2(ւ 4 ፿Ԯ5C,bfE9dk+\( ;4ܪ@}$?m `u;+LYA{oVk-czi߫Ӯ'ĭPېJai;Sv}7')* @O7 hAyxʿ[d?O1Kp(C!W6r\rexu_/ U W3Ȫ5F_Ҏx;/RvNNRS%:XrrL[ZNޫ`h*?v~:J#-`nWP`.5LmVvІXPliF81vCeK uz j_+(` n~o[~7”ۡ17 g|2Úy"t@ h -,SIW|3]vi)6;%g)t η{t^Ebm=ZVPpK5MY@ )i]0?u?;^?L8xR${4 GaVy`aJIlvw&fzcU3:ٜ;TaNcKJ+ tAZ AOg΂?|'=wKuis`_*Eۢ"=5# CK#D˕ 0Nxט')Bk-+G,X`iw3 'x:GK_Jr4C8NZد`Ғq ]V@  (y۱څ3*m z{߰v@]2Ivlm+y{ iWkR$ #"kE0ya/uv!{f)֬r U@MY.$Ҟ3:Z UOuj+pXJu-Ok[Cδvww11m#Q-"ƁY݉TU_~}wjNkn]N۰.(k*5z%q8[=-%k=mPZ8͈SכAHղޖ>=[A9Lum?~g<>%7"K7Ka^pa6{~N~k(^Dl@wRU\wאAM\2Ues3@ %ՆO`ݏg ^TΙ,U;: {01FXMڪD AIMVS ~jt)=Ϫ޺/ZNU=2Bcg) -3 P5M`yj&v^Njhn6 ZE 祤5[`:і=ӫ˓%0e #?k[P+4s LV :vk}ugb!E3u h-g__m(4GxP禮̝N]龖|3ZiWUEE{Et"R tcQxjv`ye22ݾ>17~0-u u 9g"Veֿȓpn?9p|VJUٮ I:fr4a90ZiQN @ۖgH ]c50wUrX-njux0΀K/N;&㆓[ )cQslXi@ yJתqjuά*Ugc4)h|G X疑uw+sV0bRn /,!̓ml"/E#l0 ^`+"egwcǗh|zcm/{Ɛ ".`IMBѴ+`7 Vt&2u?2TFP%p-!ba L0fMU~ҵN]fB@$_WMDdkLCJ5)_)~ ض lVfvUSHWf2cw9#dػIyBS8K8eoegtZB6AʶTLlj =^_7BUݏ'nBKqSqCr*/4}>`{ٮ90fKYAayBli{n2Y9-g9(¡(M{WeÓ4oRsq*&Ztz|םƳTcB퀖aa@mnb]l3Pb<Z^Vm?1uW+XiM/4N4d׹I RhKUz\UsP**DX85|RZE8ڰDW+u9nhC`Lӯ|&txT:)8o~Wn<:@yr+9scQQ#5.ܳ0:r^N K"Og@aܯDR-mW}nWX]5e3YE9{oy*op=@(_#~> IDATQUEAc4><8"Jp|Is =4m:, *@:c(M@eG7֞Nc$;jiMq#&"ikP8$)u'\A< BoVlu9ąv[.ي[bX4[ɍ&d-'ygo)()C:/@s.k8RuuM8StEIW||CwΉbQ@V YΟc χ;dcWy1>z>jDzaL^-BIԢsFOVMʭ }O7~! g0£{?/?BYAVVQv@2,rį'}öMWye{覘X,34AJ2ɬ"" V`Bix(bb>ZQ F1J?qؾi1yeq.qЦ܂3Fi}jRscY4k c~&8ipks?5;uCz2k#Y4\g.`[V2g9|c[ʶrӝ.(p(B_p;TĜ ]T \.pVVZ?Ioi1QUOsp> zQ^\H1QO !zWb[u M! #}@b4?AHdNrIf5  \5 ƵFTM%ڡBX(?#V؍(u GtuVXJ#dbqʣmMpR5s>^n~W vUe[u3G #΃"49=Z>ׄ(y'6})NynKN GZɨ#ҽ}_.|: kElR$ۮd< |6 ꇬ kJ* rc(%Zi H&fqႯuqKG{5 URi6EhzdHy|NZ;#ǒ1%,Z +M{?JDJr?kLS]R:J@5R8vM8_Uޟ`EL?AUs;Lp8[ ^7f; 3f'2mu{o瘠 h̩IpO-'*ꮯN5N.Fy/ԹM}

`^[ѹnZ\BM.=xWcX}c M63v зXiyM;`a՘$JDn*\9A߶E{ڏ6f"))G";-Q/^i![V r ԞqGg5ahjr2-}wKTɏ_(RpN}O^@YFL \r\P2+n1H oA{Qh["LhVW ?Š6*J"Tn{=U/S4 XgA⍋y?ݯ;w_‡*/w^+~6Jatmֈ hgGɵ\`;=e1Dbbl!3&03ɷH<LyI6љv=T!a=Ⓡ2ed4N#euMebIR֬HT3/NSg2_AE@MhZēEmţ|&Rc{N-([$4 cFgϩl 689D)z{$\ګg>ٴxq,̠!܂ `U>&)phn8Tv\'NIJ@IYV[Z@ڒ/Z=PQsMt'ᔶX5tZɶg裈vB{LYĒRHv-ទ֝(~vL<>?7q_Y¯W~@lM+ \U1&G?bc eVӎsAg*FLS%ZbQsWK+,^𱐁m9IN3T n&h$+ ډqe9聍(fU@KfGR Srrۀ#$8Z_5zpN-Nؐ}نB]GTb3ڻ_ӑ߭%}% ZN՝|>X\ }F"#V5/.[} mC]3a=ȫ*_8wYm k+•{n@I=%6]#rZEG s5u03h~: VQa_ #aܒQH W60̵9dCɿ!=@0ʋk?OtЖ+H}C'&. y5˞ w->r}5+ʐejtZ:>>vղ} R>+wDzIЄݫXw u"0tJ7=139E`/^]25~zNlH[97=k(1hdkQ78۴N*рqLuFD0T~* TZ]% h16lƄ Frc*31K lUpLwC>4JC<#/呛aNkX4y%C 69:ؒL*˯/xA_+^3 rHuyS1᳾6inUa "&!Jg[ Պ2`- KiϬ9 OsU۩jNS2 DZa9w2uPx-T#_28fƒ]sc n0`*ZqZN!1bQ29]HUZ'sQjtG[sc?Ng)~( 9V>i;ej5KY/]|=8p=@oWj&!PG1zggLSb˛b,[YlGύeG 9a84s-GpCfիcw*i Y<-\ݣJLA@-@3,vaЀahJӬ&s'W gfV}8mlDbxoY8L´%ze37J_~0A="[WLTTףY3[&GϜ'UUUR6:k)NjN>FK*+o'*;ayv`(6[kņa7{7+TovC\g*y;CAo[E.˄y oY೵Xَ'̩v@NSПݟs{.s]x(-~ƙFTPHa3kFi1m!8A?L+"/\-ʮ5w`\e.G[|9;HxI>eQ)yL8Pfyq -M lɪjS0I8̭~9/*t mY h 06.@A1L3[TmxVpiz 15v_i~_<kg6]{W*x (-Ad8i5,͉A-d֫~V0n~uaԀጴqݝ50b}?#oe=@U{9P4K/ EӨ4bzՕVgMq}WfY-O>BAeqAKT2K\P8++8~#8Ϊk)>^C 3۩q2 ^l.c\~ΰPP'7'uL#%bsJo6hsԊ&@U:)a|lY^Cx. }M 0LnfB>oqJ]?SBK=y26[kY5D2\YΥEXB)B(5>L?=r QM1ן/>}R-/Ox1e.삁ag2Bb\k:҄əV̭8zl16! ,l+@҆1h`Zx NCaDE!PBmZT;oHwXQJ9~ PPTۧ[S40gn';Q3:eÓDU$X 8%9Sj6`Dwl 8,4m#uޝO ɚ$9.p1#vrCkeM:ߝ6c PCc6З:%\i ́Q[ۨS-\ަ?.X>s_<܏@Y)p| =-kE9Q-Yʆ1,g/d;I˭l[[^SjIN9- zUyiRǘ{{ G{8X &ZطYxO=k蕳~~hƵZ=߀ o+90-^Lc&kH_3>gq@ 1GX3@BP @pp yMz͡WK[X)cϪVy[C+lϫ>P*8PtV-F]c2|&(~r>wC""*rVv s(.|4˙;8{jn)iRiW8333`CRZ^Ϸ:w}֜Cbq1ν [31 U a`}H9P;65xGm-$OE sjˮ T6?e@e)d!<:A)El?h (8;*}̑AK=R@IJJjIͲN[Tkۊ" >m&* $MVߡx=x~/vF7=V{S uX+ieQ&bRvo޳'(b!hM9d#cxq@Z }O"uLFd 330-0t6^@dcdf$l{x>gbCs~ܵD]nf{/0(2|}8ݶt>.ufa4{y>Z>-af -wvF6ɺHuɽ/r~oWJ| ?1~ROL$UV+-MgZ]ڗ4T2-pj,ݙέ>_g}\uo}mOj5jus`cvk&o-s RiCU ?Y!ɹȓn-^T1^Yim ->W?31bj}!sǾXO<g>~q!hp/º~w;հ. ;K!^Q9:tH.9u ըGYǗ%o=Q)ס/Od4A䨘&O- V, |Y87U;f9cƷ"mBk=ƘjCMG$?5*bgqz~p~O }:^9߆0gɳΫB 8m,3z8qP11y)Cv;_[QX s[S͌9J.bz^+ TUoǾc)hs EN\D_g"6okC-hYUۑ=lgي:e@jI0J?-m%r"=c70'yH7I9)gPyaG_ _D *nf<3=`M[U=?xL^q{< 7D6E1ZW B󌙮Z}>ܲLYO\M?3O LGwuA7 ,# IDAT(`s϶N,|Mز8Ĩ!h!6ϟsWAoVZyl:-$19q2C]А :ρ*U"V@9Hca7 ;qmYF;Z:-4WG!w=e ر,'GLIw]2LmC@V ˏ6+w(j< ng15>|^/c~Q b `P,37Df2fc1D0ICpOz5j7_bDvpSӟ.trcw|$ eUN1 3SQ3@D)r;~nul Ȉ,p g%Oc,E#gnNs3[?Q9#1:57)eAȚ=o TR~)͔oV 0 1 ެ]TL5X-|7ӣ¬@GQK:no^lYczhE]JaoADTű6b4JV%Ȭ){X76J* f8=@TfJ*e4w3A=sб-RT7߅ʎ BhvAO|gi(f4081r 4x#t {Q~#`$_Af>Y08jf#~;9߱yve7˿Qx~Ty+Y5H*JUVP}HS"VK5ޯx/"Rkҵc@ :ry'<+`V d\$r\ǿxݲBѺ9&@ Y J.6;=<20V~5/Li]vp3]G+-paM2ŔsBxb϶lFZ36c i”IA%rfVl;ڲz-MS2:6F>X|<)Ybd=;N5|ُƖ rLJ>Nz{4~ E\[,7 Ԃk߭~ S?on{tfcS?Su\P6}gesNǘ-ǮAN.Gzۓt\sѶOsK^bL $2A1-ƿ] JP1_q2*`&nhkMBX׃cΈ(-Iת@T޺Fk9#;_(3&_ec\޿/.g,;=Xϥy'E ,8S*o„ mҬ*RfJo ~\Qa].@3?ʹU/bjHB+ 9!/uBY9n-:XYSXş[uΗa B( R#扱B]) E1 '5(,ajBɳHg "[/ :[|:bT 0P@c]qo$uYyX~yXa0lkH EU7"'k_K@tko$j_~k~]n.Y,wze?̏YtQ*f&q|WnUmr_eJ&Qha99AhzM7FZw},KQzಢcJa[f`&$ǶT )VM+@ s * |Bb鳱l3Hk VL sl%l @[Ƒ2_ `)/M>gY xF&ETX<d b?-VD&ȑqNR5P;Akk^c߰}v9dg\'=\6uaҞ7ŮC͋BRU*|Li &fY 4Bfײ a zVrpYyLs:b\FpjYv5h|[!iqG+[[팉<~((pfPsW2D] Vk:a"8̙3YGky Q`a^;ǥ+=)<}zǮ/;@8>泸]vyfyk2UKʌ6&J9[(12֞,6Z[hl,B: @ ryeIX_R5h0[_}|V_3g#U&uV@Vx.~Qep1w1뫧8`xETĝEtxk$&hb-A$cG8 #sl_Y0EƓBU#;F`}obZvp.gx"ZH~8JcuQ)2`s<7$k@o7 'E^.˲  ) SǿqK|a^ i>`3R+#;  N+je3] ;+uNW 9#4/µ}zU'`APx@GO'-P3)&hLS93Q{}erV1ݗ<+臩]Fo{-p=OWSh[:7l *M4 "Ұ,EPPN׬VZ}h& bdEG,H6s%ٖe@&B*vGᖣc2ֹ7e6"-2PѢz]XuSPޣw■z+@u<~<+LBqfNܸ7Kk}@ wo2mwݕ 3sP>ckLM)3K.jXu\y?JM~ky7!Y_\%x}a6}Q)hjM,6r%18W_l c+J47| 7g$wvr?yJ8{^kz'슶;~dP1#|g'uC%n9PS)wfVS*("7 uҟcE"ǿ'嬖 rDf~VAՀDӒrqs+> y o 3ZҎŭv]miaQL_d.)yUO5K <Hu{a=k L83/6=VUf0Ɔ k7ZƤfSvh  LM,봽H W/ag F ܘaIyjt[k*(42؄8zq-sh>oqZxd$ @p߸U(ي9ٸ@8R(36?#1, EWGUr=23F<6y뮾圔 r?3~0}AXFKC+ޫB\T:q֨WB"E0RiG,h`+I6s*2k,jPBAZ6  zR&CEA|]sNКVн$c ӂWc!qUQ3v!5H,Fkza2(+(Js5b L1 Eeρ}ci 13k?N 4g 2 vJO:s&M!hm9cGfQ* #'[oѮI(q3A] Z'_+: >9.lL%0 8 ̉^VK^Q+^yx`PN3#lv,<FÒaZ4^1`|^s"BQKo}\G ?{\硵&qꞑ);I\G`yZճO3(Wh ):v=D?^G< ܂<}ZJsdсG8ffX@V.wuK +c\Y(xhƇpr 0%)52  C?P 4^(@Hr$Җ>"P^ǻ}#*{{1n?*eIhŰ|XУҡ+,TiJ{9X3>ڎ1}LSNq!g%M =4qe2տw'Ɖ {@m (TxFy헃X$+o3e`~3w~{E/.(w}ȓlehss/`Ze-`Y#}mW|z?Y <=] .ުlIwhU;l`Tv=l K(1,1Ne\!Pv&,Э;*( ~MA,Y}dEn}76Wକr?}}6`^Uְ #>یS96{(Iȼ$A2T' +: |{~wM.y,;@]I֗=`~.ӺG=AD)F?xާ>g9?NP2K f)E1z|,#]6QVk3WԿ h@lEbaF -t( *te&w?h5k"";-x$D1<bue2pv rAAIWă &jg",#BooӼ|k^{G引 Ak݈NLZʍ)FU2 b!czmy%́31f`v.fz .]"`H8(/g=@1(T%,t3 `*A,@IH 6G^e ⽥'=b=uky]Lwi'W. ]I}W'lyPflBD cs,?־W^3 r ]`\`&Ewĩk I.?!EGÔ*xa3ѵN>-KfEYPxMB]+Zp]Fism~NKvH'ebE9=hw2Ȓ\yktKWEk=+0|#3a I ѶםIDAT 8"F W{``SLSkDet>g4l̔ 9 !%[6?\\tO~p3d\o[N0S $N%^%3T 1ٵ#81^5Nnj5;Y̳V41A9I bo[|ϟ9r 첑tg/o`#u e&atpRtr\4qsQ;܄hr]FiR,dOuqQ1}a&xhwGf6XyhhUxu35gIY/CuS0&aaYBmle3nMAk8%Vh6&;09L[՗vEv9* Opobmlc3өuR峪.ԂGY {KUs郣rטY#7fGiZхֶˮ2ԀqPZ6t$yA `glR ~"sa6/D)ˢTbX+} #RG_gkܞ g>(8u[5+-dQC p Õlj 0@K~쳟zU轥o^9|\+ӾsFӭD2WemFl lĕg"h3R6ǔLڽ($bݰ9+pQ`JkJTADꗂGM]gb 1nx{v"y;D\}O`]N !%}7ok.lǪEGAJW4qoR3jkRsȿf6=:gdp#m ؎r߇bRHz#Xuco)nx[s:G,4wBdF{e @`V츒cTV; 5, DHN!2Nu$:#V7O5~qV?D?WǦ'K a'ӓ oIE:5u5dDAV_tQ_O`6`We샽ۏqn7+m^ܑZF'u ЮE zr0EKzʨ88z~ b6N @AJ92khP#c2@0 6Oro"́V v 3g#lWW{Ƅg^ {fv.Oj73󝁲S4hMy%U܂24T=(SƪbXibEAZK.ґ'Xц=HD& 6HOFx}q?9ĵufP[k; s9♁ ;vʀډ-~5fLoAKԽag(0q sW?bw/J;\NFN\\Nе3֍Pe\ mV&ڞwEick훙5Px"(Hש\IQeg}`\1LfJ4aʌQwg#nI/cx y8j_NcCt,aVkZPC]=Ŭy+W\cˇ";@L<;hdvW妪ReHz?ٔuTF 3{U(qa&"tbQwUqu[6yf` :l`d?p}QuEq4EӘھhGQ ~6ВKQ,4@m _G SfoE 1!lx*%}n<>LD?GeOv_ͯz#Z׹ZzgqXв4ErinQĀghm0)Ymn@V ȴ `s8;["}s}G6cE.dk"r[3k|ԝ9SE =ڇj^]*EƘx AZ{"1z(;K`(ccXrY* < Q^ӟ٘ +^<]vehr>K;WсgA] Ey!տd+3-SU-xki@ȼBpeXʹ$DfIIrقz48^ VCK5r]סdݭ硂明<݉~ߺfꮘ<o`˿P@0+1g wW#K^r] d_{11`K1זSU^I%ˆzqA(+l;b\A R%۔bRB"9v@((!~T!0S#`^EF;.!F8fIZk噐YF*8JYFT+|쓥3 .$~#͒ &?N$S/#7`Ϡ`P |6_\{{{.7aLtV33װuK XaN]\&xs.}kM|Ȫkn]VUK:#M3UZO@lˢj,TzAh4زk%Q`TPXˆcYEkS`iis&iݝT PL)g-?ǠY'c;a;jʗ_K/#M ;@fO=Q<"׿W?W +l#65M[*0Dpkr foTkTQ@X pS` hȵ X K' w ܟX_j9ǧkHJUPa."2Zs kPUwݫ|AeYv*weNQ| -J G0#t!>濍1=Xp"diG$ٰ EgdeF|r-I}j{2+V((Au;H혒3 6Phw3\ک߈R sc<##S hiA2l J$ĺ z$k_x{__.L] .m_n9}&BZYLhzC\ * `I𤁐ԨIёA97YYLQ5QJvSvaơϕ;B?б*pfH\qJbűrW~i J P6AoE ^x/]qrv9=}# 0 frR>LFO,s TLpԾ =g@ۙ*HVLrX{O\ϩSH >|;16#Ǩ|cSJU 4@ fL*n1{n'/(hɍct~򽯹#v rCrmFq`hlRаc%Z?(N=&:^5*9DXf3,Dživ%0xqLoLacV̌NQu/)*iT\^Js81,v5S}&nzw}X&m_*3 MH)Lѫ^'{?.7asBns Wr` U[Jk8wC{UsæFiV&Z "KQ4,$8rޱ@4QH j`sR@Aw7lPkڲH¬L`fN(atpܿŭd'w]v9es W|9LKb?f-uCQ5_cU2+ݢ*7j<mJ`:oRͿ$ !kh96EIM6#Zdi6"%F(Q@| h=7^||  a~2G3kt8+ @oAk?}q]v9Ker|=pif[k-zMi&@:ֱZRDUtlE'sFp7L[ xP@ 9r1%(.lr.=2@DBc ˳ "흉zfUeAvoƊp"e୍ٕ//iv r_{3 ?OVh#йЃI <:e/¯q^-M?R 4yԸ 7n"Rچ8[Jd켲ccӎ8Xuc "B세 q=5 LϿ+`] .|}t?{@?W rY37@k9׀*q1H[DlxDS9Zу2Z{RN9Rb!*j"԰,^bMnC>j!a\G԰83`70+7M.a^>>_uL}|4n4ՃV@|u0#X3TaZGW6cXhKc#8X ƛ$P`oSٌ_!prsؘ P:z?#LOw#ҩY0hCP4NNN^>9?yv< rþa' |qI@hZZQLj$J59ZCuJ@%/ }̥P+TKU"t VF@} A-X\1Tki$bݍ d'mԙi)LM x5uyѫ);@-}G\|+^] y-;*b2.MiF3ހUT⁕Bߋ`f5@>g,{X+!I|yl<IkITTv= Eə:~Y냊I }̕adPTоC>f D_{_K'.|89wX?7']ݫK!OAPBwܕ$XV?a!?&/>?>T` d, }o @h5fcV'ƒV ɛLq7}]n]vK|Cns2=gRkR!dp)4%[ˤ?6HE+AJ,X3Lmm%ӸABK@OR'5D$P8g; I[~ wM.`rɭ𡟾DFOwG0HL%ZRYeK]=]עuaL"m)pq;=2-{=?~@ >,: 'Nwy #]v4.r/KOj>. ;wk a(h$ ld @la xJJ1?U]dȷy #k2 :bz+CtCIq{.܄]v9y'r㻀q ;q1ӂc?Ke0DRX*l) ;4$aIF] 3no?+Bx[vF]vÔ ˹&>V_.8}#X>nm۠c[޺~t:N|N056\hԉie3`| hq=3Dס~Ԯ%е _}yG]N\unށe]>YIENDB`ukui-screensaver/src/screensaver/assets/ditu.png0000664000175000017500000070002315172041106021056 0ustar fengfengPNG  IHDR߄sRGBsBIT|d IDATxHvG](ܪj93hMss)tk|?+P6lv&)RA AA7 AA }9l a@AAA@;Hw A aAC}zxS @ @PM!Y A A0"Dߐ-BAghC^okm71<^0}&4"/w5xH1HhD?^oKmW|Qs^u]vK|{"/UvA 0A쇗#C%mQ}J+qKkAy ' R~{_J;xUL/K)Rx)B  x) >"/?4Ev&ѽ;RA"%І> PUAۂ~#G@ ?ߓ-}@b B! k}9!N%Wb`#pvq5/C>o6h9) hSSv ^ bom ; 霳c*K?=( [A]gwB>ۗ*-=e6q6AsA~jkٮ#/':Ύ:4':=Zn4~!CCK!J+CAC m+/J] o:v;緑{.CC-l$! pkC;1pzVb_̳D-jp]DQ:@Ds>T9TrAd uGхCMv{Oϯ }a:QO>4{rvA +{6{U6 Z|=mHb {g]r",bGS^ !  6~ak]_$'yoǟ~Sϩ!͉OO`ݜpk{ ڛ1_p')q_ Ws !_ē(yq>ĕ]V CA8߱]ش?Āx9|H[TrS?e4-?$G9=ͯ|48"Wd(]H}eB/۶j *̽NKt=  BŐvi[<{Nίts#4o@7Do6Z]tLjFmH4uD| 4Ĉ%GIrQW#_f=Ly/aXпDev-B,zlA!d א8G/8O?sx@topKѭi8fG4䢛Xё<,]ʋ l Qb&Y|o#=0o"Sv)H(~hAD%CA1߮! |hEdjt6g$טb_](rQ!*]/s9s_$8ĹT3qqbn~?~5CQAu wkW4g a翕p2(¬y]^ qHHĿu39[K?ʞf 瑈nuv}QD `/LB}[ڞUI J4%cHY|<jw?acl8EQr#ۢHԺGX[Gq(t{MIA i LP~e3}yW2Gn;xoHRj=Aux51o(p,j @m5aN#kZ8׍Tճ2}/:b>M%V#Zynz|Fv(·"EA e0Lb=;]-vR?=ԋ%4Mhl~W-;yJeB nQ!4|l14=-nE>e1?ωt.C,=~b0ys?~D ސ0C A< ڔR[~ LtWҍ~ܕZZ&{b_; X1?f͙S 8V$O ڶ:]/_ΎKu"㷈=(NWq|Ȣwȶ Ps)w!lwsݰ12&FM7*IJpa,_7Bg(r~:<}q#HN⍊8:q>"gẏ-]A0:0&1ߛmFo ͏.b͹nF.fHv.dsw+B nBr7i/|lN 4@'4W\Wf(1C^ l_zSQ؇&fA/ x fv N{'ZQVg1`_*jH3u]xX^j0۪*t7nnnMyu{ؕ:E^L^!3qt<=6gv1o-ߦXAP2@&ߙm?trz~]*ϛ_6=+oysE4 I.C+)s灻qю1(EC ̾A&C؅}Mdyb.ae⛗92^U~(ebke,,dalaLd'\繼˯>0`)yoŪҫ%q=}ŽI搜M}A  CؒOWB~hlA;( bB|'R}i3(+k+(huyG#j./2U3_ҔRs1}@>sۄW.VOׅ]!,7aȶ10<&bx 7cv){+瓾<`M/+ZWE͕e54hI&pgHŠ^$5ߟRxّfVІ镶b\. "*8gu:3Ԧ[lmخ8]A  Cߊ<] }S/8[͆5gajlܧh-tؘERnMj^b=X/HRC.jɊ0lV's|quVNa{\{r8!%9=xO=o[N!D 1ߊmӶ-'37,暜۳˂=Qܝܙ֡,Vuw[ 2ѭN"u֮P 6$Ǜ w&5B<Vl_f=z~ 88 eEE 5&¼a~o-x˻Լm04<4{C`1<;E8[MےWgj>"CUu" Wlotlhȶ ‚qEq_bpWCޤ+=7 D&{SE`Ɏai㾟31wnҢ4 7E["Q>$[Cd1<E>րkl s1wq}Ld824_"% ^]P4hBI]txQ`3-]+n鱑gf3_dlN9^#3Hd qb#.K/s/9|Gc[FV~i8g,dy{n {WQ/b)IAb ^C`Se&4 |Zʳk-Ⱥz+h`_BWQJ--h7g<]'U_Hyz> 4#n89%1'Rg,d%zG(^DQua;rmAĐAP}յ-O=ᜈlp6F+Ug:(bcm+69:K@Ӓa%+d -Wt ~衿d;꧗xd98N<&yiSyI ETKx|#s{zY c/Y\RXWuaxudߖVaDա ߡEDO uND {ޗ]៾|'UL cM/,5V[HZk&SMlz [g;L•36=ߧL&MW Pn]܅-^l]I/{d[  ֡ ߡED uND? ;އMٺpsz^jqZ³,up [4+PΟgw~N؜bd$a@Qsӕ8pQۉy穗-fW[Q_.^BM'Q6@?uw6CC~ojӦ RwnO/`N ʨ NV;1VOis6'l0{|Es͇sJ>o9awuÁ5h-yȅԾk:p/nq}[a!!DĆ qN O D8^YddM/`N΅ }ʓxסuŠW8ۦ6]s_IV#-x\~G/#=;4󌧈:K^Ss9P})oy{ϸrrSҾ-yI  6dhv 6ghM&o̧/P5=9)Wywz-nW\is{۳Kس:UN|WSSQz5ҾC=g[!p8r'a;&{Ź4AyG,}]X.Y#Á=-,p~ Ymqu趶a~׷(=AlAlо}yy"\>Z2Ojq[8M*U@Ix53=^'_cSklMyI7iR EOoz*"m2j!\;d$}d:,Ľmz xī\1EXe¼_FQf(K{)sv9Achw 3'9Vg=l8zx.;Z{椸XZ$[ȐAtgHMtz I^Fp+G|ݚ6 KCO)-V=LtkIz |?vH˧iZȂ磐ё,C]m鱴j9Oy%D 'ŅSrsgݖ aÚ^%rY2T7={t]!!BD4'Cӗ7O>{G8ZuEgZ΁8f>hl塬YV,)fyȽk@.[y 䘊l Bˎж⻰J| ]W2QûG\2ǸGqO4`8`Ax>Ncq/臤78Qi~e%[Q9 )_I^@hi}s mDE :0A *f8wHgW|Ntr2N_gՄrϑsq,W#p#^ +oX.cs{l>a.ms#.\Cl֎og5>t=5-.ʇ`A@ nim/^B?}DDx "-X IDAT=<57IgW@Wg9C+a! W6a`nMc^L&(/K*γԶ⼤{˷3ΐغueB:/= ɜt g>u{B{n55=?% oeuG5ކM'э}~w)= Ķ،p49XWm ݪ _n\ ŭxj/3KkT_&oS~LgnȮ(5osU_8ϛ#%7\?{& s!9nS~ȃxwĚ^>&no]ﳌC 'bپD[|Ȇ199S.(_[IŃt{zf(7QSl1ym>69X}A)P  iR]}477%RxtC)4+X-gX3dnN!~LeŕəZlsj{ĥ/ jI/k[s;}_h\9CQt ֡Z }4aM/Eo/*={!trZA!1N.C@_~>ϜZ]At8AK-$ :3XsIJ`2| Ox9euZv|QW&bU-,et(m8W@[=ɺX\G$.#]!4 ^&1V+2d?g\?W\4>H?2#0O *- NDƾcf!|hym'/S]> #$ bD=z?4TxnN>AZq0tNc9ŹGJ`hjq@wo6x#{*j+3a(p-6m/ov>6}'Ͳ7 xCUȏ|ݸsݰ12cd⼐Qls2|679.pmT_)t9A 1L^o^x o8[ U##Fo^^Pbxr?,覭X-4X[+]A'B}lN=j5a$+^ރLG-Q7{iٔ~=`^֟rC WwlUD9A!1NbW} q>56 IJ?9TQ΍t Gc *O^_,=Wjv-փ}etyo =)Wafb[.#]7&'aQւW?H~?28+b*<ɞEOyG6I.ʕj;[_x>޺aܙqԮX7 5w33lHNO;»bŸ8&.`0FGkƾ ډ+ts=WZxOP, /^)C(jɋYv!X= -e 儫P)x΍ ֕[qa[ F {vq)bE1f//YqCqKEKcVJ_Uea뙕-"QND%$ b;i}}p7o{0|Tu~4'0SEbj& )ʯ?M=j%\:t&ݞ]rp9ٖcn.^tlNm/o+}l:\L醃5D%C|+OK x gxU| Y@eO<T@7'hLJ=YE,Qm}mBn`Mf#*k#r7?>&ZZ$('Ab >gUl$?}ީ s0/1YiLszG-jT6ffIӰZbљ]bde;]V޿湏Рavqdk`dN4pW^|Iʯ(ڛl,K2,.S--|UyM <Ǒ1t02&bpbRsL(p=]# cϮ0[,_!X-n/n6hlazy\?fߗww뻟F?K. [GUtNx5ԍξ_~={O;MG_BQJ_(&߽֡hlMaKMX閭޳갧Xrzseb4v>c.IUcP=8~>`;s ?Mh+UyK2Lbyln{/ټps اs#ByQ| U~Nk߅َaÚ]1Q^$N<[o#E7RݡhѪq}wїOV'l=}hċ٨ 6{i]Wݳ7O;Mxss"󆣔idB|==19oЉqo/S[NU"ƒ/mD5>ck`[`e[D,Ϲ_Mf^>ֻV/AHǍAa_ߩMr̈́'#Z%sʅ7S%Q/seMOxV|QjY$'=s2C2{zWD(z$\BCqoM +4߄ai)A9g0ۃ؆9hT&/``<]홝ck(oQze}q/sɇWP'A8HD?o{U^қ5 ic&eLwů`KV+ޜtt%:5)UQtxOl#{VNBpg*=fX C3.U3/ّˇo ]XXH_o[y{[+A{[UM4 [/z9nh6l*nIBb 6  xk.|ۓ ])nbaƌh'LsO(OxU"\GbhhZ@ui:>llgp0J#Ų`غjq.7]j/9doZh׫3qԞ[ҝ [YUsƅ8u N>Qzο_G- GSq)kwzZQat(>w;=cBUʾB7svCrc#vwW]F?,7K^'9oxSR'A wh:[|mܹ˜U2y>7A}99hlb4$s= W۳KsX:-=ld_ '_ȯiqGɪߩj0n~:;fwrh8TŻ+O÷|[^Gq ed@:to9h( _Nc|ؖhgG%bEU^_٪ O7ޗ\γ+QN ѝ]6o8B3'9u>m~r߽W_[ V o(l|jqَh% WT|SE6nRݟ]sO-s"_ <Ĉn.xDZ9aGk„zclM%Z!#^U(ڠ%/Y7;n_0G>\e9v9Sԛ oHub~WFc G+r0~.l=5)mnG/Q%A ٤Φ69,$}J4Sr 7'g‘%;HDfX @Evm0U:<9nT|2-|5bFiI2K`{_+\&Lǀt4<҄ց=#ad8pfW"D#ʧu^w.=HOWO=+,~s¶Lj*_.'Fgul8Izj^6$ %$ =UZb5t`'bKk|*OML].e'\~>=K %ٞCQJfҙUy7X.w'K7A*5D/|SzLSMƼɉSKAԠe Q}6{׍ % cWj scj\V}68YP#sW0na$ Cb qHB/_w1Js 9U ͒+2 ?SzӺ{󄃉qA]JNٱ$ZKVۭz+tSY8*VS>dY`gib${r]vZ( ETKRmIp`Ϯ0vjj"a6Ś^^ 5Vyy 9[0'l겄1~\thioۢ9A ќ(ĿpqV8 iqpUvJ&+ʯ:sΩٞBArB[@vdإ::'O/AMSݼa2w5;q.cIY>ӀK=|} M`M/v}֤_-ns\%Cįw,tcæ)4lA=vw5l+ywoQ'AQA%]ZguΚlYi;)gFk{_Y~Hh+UP@+I2 E^Sܜtr]޲ܚ'\%1+E9-ǛQ}]Vm,Y՟20]@Z=8U:7&g3Z&弰"oskzyxm+ҤV[=s{v ;ٹԐ_^.w߾dϑnL"[9΅<]i)#IqP_-+jq,#Z-2|T]Xy\dY>}l'3}ZfgAޱ~kr9'dxsy6\mI&u9A( 1NjA% ?L}n'97tj*#*U }V E r `%foq<=evx]qAmEz6~pgɠiÇ|l;\#/Q7TUh..*JElr*ys&\N*qB0=eO%Wj9cz:6Ϛ]p9OxVu%;C{H`NتB:Q}w$ BqF׺!YX{XpNٶe* %"בilhNΤsIױ5=ڷ<SZ[M%4?˞rm>~Dn: pI.TE s?qK [/4A/rqߝK4:BGjp02سdvU;;]ra؆}|nU X'nX0'bzmUysUY%Y|֛ɳmA}C !2t!ާ16Z-ThgU=e{eUGsr9EUBl"sMYz#[m<QFP]q v#{n IDAT^#&EY/ν={:T`itkRiVq [FHPԓ>&sƛ0Br&J+_~ѡ`t}u40]8|ߞuy a ͯt6br-ړ.}5=O"TQ}:\e>HV| rɼkin oG@o C~|xQm3>Ȯx;s%9ٙ]!J<]qPKE /#:ؑ ehl:HrsgB׏ )(o {I{z\r||j5<d~L7|S r8HxXXg2u{pJ(xQL7St GQfJ|7&g祱\Ӱ -4h2ެ~Y:1|!6TNn/rq1ǛNuM/ﱓSqWʗ6 Յ?HD~9c ,%Q1;+Ġ䂘1I9*1{ī/\>_b5?tݘ>~[elt?b?[I}N2WcY%ZlW7UGw5ʫ,TA~j$ Ab v=طO:/VZzrq{ה_N꒿"] oP59Ǒna46QT!^-# o2/׈sX֡<kz#Fˍ yV9}rRpǑ̡.W/alͤbذfӽys.J|WjQZHWܦ|95 ,l yvNAwWOY&6F KxQWЭJ;[yOC翉SryhoR/yEGI,fxy#?h [o"T_('ANjH BGN.t^|=; >pE$?G>|! .YqN1vNj]&pZ< ꟜÜ+5IoS9ݞU]uRܘMgθLtռ(qb| "dcÁu|U2M".+U]eDޯ VO׽oͮ`M.fTk,>/>ub~EwԤ~T/_'W?~a O 9A0Q"A|oR~{靄;J:8M8\Xz^uՀ}@{1S3΃& f>&K&0&gw_aT9ݙ]^@Sկ\=w[q\˼}ucɼ/>|;sPKD>ۊ+_{K_RLL b/A]pWufsq-zG{G-f([wcBدo.aN.Jrj<,~٩~0'l͍wy9Z Ut wW/ [7j_|g]? !O8Z[?|GEda|My#F=2Q\S>~42ت)rXPxEU M2Z1ox%鹬${b{ۧJI&K2ѭBGsW=b"et'v[Zt9؞[^.k%$JXPxˢ??؆}l~Z\E ktv1z[y[wm6L-&G˚_ܵjH [gKMy|jԾ_#94NoR>A  ^4|7)uB4?\A^30A9- ծ yû`NaM;.}3Zl*9Jxq=Tz79>ʭhM]nz_]rz'!?;;=;An~4!A&r#"||S!|P']mުx40m(Y iX-ss79 .=-4vɎUv|@+~\|,݇.:86'T~iG_Pq!/pw/7Jyäۛ~UbCBѼG\qwD:4IpY[?F7Y<7 Ibϑw?KV[A$mw1xqlYZv?|ߞU>yýϜ°O%If uus)nmo8f&Hժ6Wzي:rNX&Yoy{0`{WtQ&Hyzî [/~2{5WpuBֿ*RE]z|}ݷ bGAs޵; ɴRzFUX E嗏s 9EU5,*S/K&gy8duE,d&}woqר}U'\=_aU_$95<@B>L!ִ[Sb؜B{3B= U'{BYO9;?) P]&? ۗ-7lH(Z\)n!Co#EOuILv+P˯#^UH7eNN+#J"PⅰgkMWNP' &whR#X= XG;\ 9ŋ=|w^[*tβtaqh]sѪs9OxZ~/ĝ2Om;+MByI?*xE軹m ;Bz Y؜t\ WKIz?.u lbr?eiA*=Oh&WFع}*(<5#6Ϛ+tg/%7ju$ryLup9px'v+ vJ|}ݷ bTݵU+uBmH _0'7t4٠E9dBu}cr 9GaR/ښ';Lg_Rs1ؼM);|cŷ@>&].E{cyN[ NY6\nߴ7GTH-J!_\_%"0ZdG?(Ynr1W-դip|ARU'uy:Js%PA"%W aJq*E>s *[`ϏnLH:>xry[T1&6)0S^)o4`N/p[dσ<,t};?-JCu֛/M7l.l܋7z_Q.O:MA6om!ANH/]<. Ah$sī ޣВ7͑cr[lPQ|W=~zM3.+|%5;B(P_sEjTJ҂%qϕn8 ;Nvj!BˋA/nĈoc{%i.3{ϛfD֡CHwl3Y#{#݂syi7_7/C϶yۼ]wau֨3ܶ 1f9|/~.}G#Ɯ\ ÏtFMX:/Rq.֖.es#틻1{6X-yb.a.bǨ }֦9}^J\V|.R^<㪗\exj?g=gOQn^kȼ@7'ﲕ J" Ub-"?jH7#n:'\rOmYK7˅ZO(7G&=Z3^MYy=T5<7zO7;{lN02l '_lE8O/HUaxZɶcq kbcsVsƥz%v3rFˇh;NUj:=}S>[ {= M:ގ؆99H Q̥zXmUoz@i,|yuv:')#/e_>ixr{VuBS+ zztHen:[NoEӏtc#Λ#\=YXPT `s}j@Jc"_;{.XK;޷'ܚ}*f5-|4ꎭV_rW'j9ő&D,v9 d7N.&3QޣeKbOE"CہbX=}nz%E3yH7 *fQ_v/&Ab8T-{pBw&#]69&jCxOm28sGE4  FyXzJj/Ιz~xwU[sקP]KY-zT9ݞbNQ'&{ ܧJM-fsU+sݳZMK/K6N򗽴&ml2΄z"cJ?= !cG0G'ko:*졻7b}297MBk94l=[_G?,ڗ.eS= kO9xA%P&Āsۦ>;!OQQpNFbu>霔fJN6y7'=NϾ~Y}{ղ6ўt$R3MM_[t*|flQ6Yi͜umuWp{Ov̅-Il!O|_"tZ?nMќf|lr$Z.採HH.,K=ˇow028oNEJC\Sml؆=ڒb/24|^1mBbV,٦7V[vWTd[[/,6yF|~kJ!OR3!gM}啥uG<ߞߜ V<~S9򡥫歋@Z=Am|,,] "w/jWY$9v&y=w̪juYLM E6EҐzG=OhK4U434<&ŧ1mnvuUWnwwz/pǍUY&8"?+[- %,j܎N @{ @\EY*ہ@g>uSov hZJjMd;?3/իkL^j:S_:r MuAAGF}GyO # &!2ҷ&嵥0t˅3=nV(Koy|P WYٞn ԟNtmDe:z<\uAӰD뗽r|hU {[ty4b{ǞC'*E\mV=qؖFkdJy ubXA鐳ݕCLl?$EJxRM-u21d &aÝ?@nCR G>C:CʇZ<#2hĈoc~iCC|9@9L{NHaE-ڣ.P=?SX)O~]^=e2}j wy]*˷Wk.SQ-z,zm@mStg@hO&֒36\NMLJ\h]icD;KBDWȓqON/ʿ$g+ě7uCqf3@~yt &[Uc mai[ ۶>B>mWOhx ;:܇c9xm1U҈=7W:2 3zm4 YV^Ø[ZE GxK˧ӏpbX]wIp˜E:k8Le< n%e gZbT.}wh;OCdI9mnr&_} u5zMzmr @Z>ƛw>}iKS9[{mG>Az}t73?Wruq `}&fuTMŭa449gk(@ ubذeDdg[-S %>>8D뗸NZb!{Cc1b0d#%s^"[垾:]僉џ.gFWw'h\IEғ*ެKsn'm(=.-V%TA[҉T.L&m8s!47VIfsw~#dxqSYЀ&yG#ô}1b0F2>}H{?/˥!{G>mΌh.e5x4Z RL搆&$%WdQ$P+Nvp5vyNݞ +ܣIp]a% r;hCbh{hs}԰G,>ڽ>BgaiJ>FJuAHwA{fOP8;/$䛟OE ) 3+mմƢ2<\,ppyl8)ç q%;<Q1]B7nhsx{3=㟷ܾc$Aߓ9rQ- {'H%z{ÁWOEz1?d`biUm7ruZ.9=#g7b[cCeW|p,>D̟ $I_c:X;WԒewB"ixG<6#?5n6ˇ qMw~ov)&=dEroH#[Yz솆kIm-3;TӂfR- $=BPd8(3˗Rer!qr )^<&x_?DӅ==+_Pjƾb$ vY<]?U}WXI3CƕQ@z9h/܏h!_ C{2G8#>a$ǟ ou0>A~.q=M+ܨ[܁$mHіAaX&. mKtӃAK_1Γ;sXG N$|8wBip;;G?`]pwy^ y1 =G"-*W9 g@ dDtVt Vh,bذ]Ѷt 9*4+-kBW,IJےG_;.yiRx{h{sP;'$&BJC<'͛7?}Mϕ_L~ tvb^f(o|?xm fI?!d{{Tsr%EPv@[7)镤hXEvYp>ao#y|}#ْPCoOJgk/GfPω&#g-g[_.)-JM|$#9$#*=r__Yχ(fQMdUkdZa?խP(Sf[z&%hYk)gzF7p?7ZhsS:^z8%iOڼlBؑhXg@A3/>RJ[}/qZR,ْON;pnU/$G-N"b鑓Umg)HHy<ٖ[[<^;~/1? vl_#O#$5){z?Sx9 և#l) K&9FNg-4hto!!}h ]x|$#Fd|Ļw '} <(`9 ,B1EOt)c;C_g:䬜g%/K-=O{*bbJq 8kо3;nT8xIjK0V5~kׯ__9 S2x(>q~x kOcϗ3$ΌNU+ӮHZK$@Pzf-ǝ5hOaXUG>r. m3ПXYtnoJǔt={/|d)q&V[Nm]p[Ҿ-` oIv HOt YG?Ȫ|.KCˆ9}oKވʐbFx+8;T}]udeUq'w~)]4W{bUSvAv(wxN_Z\&!2 iG`t_3j5 ޡU==S{i7GS/KWSnCw*.?7T_Bb ~]tʭ’X@o?߮J'8+$@ou\vj9z?]~\uGֳԪ)9cHvN.UNK<ָw&j+<o Kg.y!HV;,:}FHyKK)Z ^`')tM$ dIx,_7l8 HxU-!Gu sޖ# YՌ8;T}]udetc@,"İK}[^W\XޢtPP,v»Vi9tn*\5Qi"aҩq-'-~*m|oVhoY5C{rOaLO~0NtKsEkdSg&Bפpuyے7bD/ī#Ǟwr:Έ'E—[YZeE"K*İmmpKMw/q'r' Fe߻$vOLlIo%*<*%gKK`<vi,HWj/Z6Xr7u, ր4&Քr˙ԞpHX9Ӟrksgv[\R]. _6\27Wo;(8=8-Fd8 ájt7HJ%lT#S_}.ERY?#C~WBN&V-?}oKވYqt{*ᆴrղZġ'bO6krC,t 4k,^e' oΠC#iB)ɧS#yy5b'ږ3Ǭ"qpK” ls@LU%=۵5 6Wӕz }ԗZRVۻ a;Stc:d.إAOäڶΔKGqi'H[%c֫}qxWZ^C_,<}lA ('vfpf5Fmnڝ_Bo~G6>]gŷoS, {uǿ sL ,yb28:H_W3nOKܴ֦̑&#>>T<|yPN)?X_cǃ;1l؞|ۺYt7G7lޝ%Ty3LDM>Ynt/>_!N+ HGPDxĈ#sPy}uDĻ긳K:PmMWasP~.=*38rܽ Pۖ.-}t)a8`wvl9m5KȮjh{h} }DWp\}I'ȓXAo/-ymAs)ŰㆆippLw~IoMD  Rں?c9u}tӁퟕ$yN48`bpڦ@RjSuHG8:F2>mBĻ<*o-:rf0/!Edtgnɚ #VWr->O^*˯R;m;i[hqc֞/K.K27H7ۭ 0%񘱄(sTBr.1\&ۍzoWj> { yֲh]ig!TPgih(T\}pOq,mPŠCO2?VB+T-8gEy\mv}G!uyے7brhĈ#sPy*tK="ί ;-hӲ )&K OP}F\Q".n'Kok#yw9޲9wуccڟs;ɧmYK^\Н_Jm 4PtX[(P4,Gm_rl0*Whx2S?6c(a^'M6|<69rΓ>+Ws< _GOc)Z["R~]n^-Gdm_&Ai%8 ?I'ZXp59G>cviŠ1ѹmE_'FIȥj7u#!F12q?tiIj:sL];7"-O'Mb +w|@U)5gy'iBpږN:K7>v<`'d<]@;=oGگ!76oCzc*g^_i%Q&0K!UA{ʗDn@UVfTl qr7edqzԴua@y aɨ7,$/~@2>d &_ 4$s"7ob85b>OFw)tHhj-wÙ?.)\6ۿOCBC%o*iĈsPy*uxNWџ.gtf0Geċ}J*`[p+լ4iF.+ʮ)Zӈ(NKQj KC޲w|b /*V I wpstwa$5˭ϕ~͒ˆ~8ʭ5in/ɻ7.[o񶮖[dVw\ { xjߐ?ee]"\>Ŝ diT; IDATrv]n:_mq{t.rp޶z*M;$r;Ábbڥ; #[R2X[lFzw}_ A߄{ޅ0Ww#F c̩Cdӵ*V?ǟ{݂;hD679C,ΟRqJ%dRD<3{Vk8=BHpWiK=<9N,KRU"nX. 4d lTR,a9Á0%6,Z-fh9ђnHJΩ3<':G{-/![]۹ '[(X߅5ݰ}GΚ[ih}k"~|nMNo?;#Zoت|]job8Km]>  UGxq~J}. N/ּՀpߐka'M9938R]>@KŁ~@,@x&=_ ] 'yٰKCiĜ=TRΆ!ںΓǙ|>Ojp{O^`_ޜ`G\EnpC߸śWϖ;w˙?\FoNyxX ZCΈ"<<*~WL?xɬJ+;ĉnRMFx].yյHB cȣ-w˙+.ExRY>_<,br_~nok.+748rƑDƷ"Qu6}`7D&~ +*ʵiy0Buu{l{# wf4RE׬[ 5U)dA١~ w9? kP!UDu*~"roonz楀p @p[hP{Æ/qb}MbKŲ3VdKa MF!Z$Lór-)e!w?H:#HG| FPc͡rwwy7%JtyN/3󤰲˔'fRh2[[n'[k,!x3DJ![/%JIC_[ pfk"BlY,W]""GTY&[;ۼHiJFk za s<螥T׶'ږ^]-U(s{#nmۓ] s\9Rإ1\'k*/wVՓh@5AUEdK-w*HiOɵ=%/xbRpR.UmMk ۃ7'>]7-p!ھaCnpg&z5аKt=,W,qH[xP{m! (BU"bꥲ|9~"_hȟ@6 ʗBT*ouw_PB (!W!Cc摐8IF|sglC,_!f,k3A/nEy,"Ŗ:fYG_X!n VȢÂx.Pn|aNI>}d%n-hnV}N019ϪEt[D+dc7KhsU[ѧzx--zY_n+p%ZH^ZRP@^GazFGt4 mr~Rv)e)?P/rLω_2Nʭx] ӳ q$%1‡~_OtG(>O+DIT^IxC5dLwu2̫TR;B>?t?!u1eBJ1Bǜ3Cdԕa0U-~s"^D2m,F[pfUh4 %P?"ȑ7FkdJY-`ɬh] \T_][i;'9 M!)Sq&Vu?+ʽJVh\*hՠU+˷7 Z$|b'҂i OC/)I) ^~r䌾9%.WgE'Ws_yu N?7,iޥ䵥|~S5,ZM]csAg@7!V9E.A>/a{VXo ߵ%@@~;.Ey||ĈW#Fel:C5mėt1LgÙ6@b+|m}hLt RƮ wAt7Z~gYEȈx}֑cc#U|DpN a&xG=-*M7wPN[ gv!\Nyg%tnZe7rNV#VH•|(-[Zȓ Y6=?/$i? nPeڭRwvwz?^5is}]?x 9Ig3ġ$@V;ay%q< Ȓ7/q@嬸o~~!nܾ@78M\H!i'Qfe8 |9i. q21p21-U7 >7 FkO&:N&:3 mLt ,BwH覍''zMl' tp{1yәr 7;|sÆVE:FΜR%ĊabXq2>$$}.Mɯ—r84M"_K~uX|hCG|G0N}x_AOKeboyy3 Df7]ir(x|˝r-e_>zma{,0bqW,$|.,0clo_(uK8R 1eY2յ;3=cwxVhsU[‡_vΉo~~ rl-$=y@-!#|.=˓hM߬h9GmNrk`nϒaNjWί_[ʙ8? `}9u]@&[D>rpVsHv @=p58V/= T?d[IAݧNqRڬȢ5xa,Dr_uV5wO_j>L: lAuwٌ<7i)Lۇa0m3$/X7wj,Dx"pwv r[qERϓ&񺮛%]Ž|{ݺ_~ 2 Omj6,jg*ȢeBLʲS蕗f[Lgԭi/ _jOFD a$#>T" 9'~?%[I8lc)+ּSrMwίNxCΫ)ȵ9+v[.%V#U͟cbXb5hnH!hE#" m-ӏ9)~Lm>UvTG[*-^zKxKKO(j{(35iI2~KfzǷx{3QxžFdɶ yOðâAxVim< Vra{pmWTg3`LvYhsEsnps҃ [r`L Yw 7Ԏ._EHûA,?WK^*?3ׯߧJrS< ."7_vʆ9P<RtF|xy1DJݮ:2Q~7(,g̔0[,͕EtfG,@ʲEK?,-ɛǷ& ,wAΠk)ny&!ږN/BbŃ-I9GZ ,%poN7َ^iê'ѯZ$S54d}#&~_a4Z+#k*MwUΑ:[Ԩ,gEQ!mO__<9 f[.qk* KCA2K˺]U8ݟd{϶3=];oI.jp{VlonC}#;Hn!4H~j yyCڎ)+\,T%N0"^&ۿ!ʺʆ9P|]!AtX, f]Nt~8ȴ!OUצ=e-D^ueoj}w+%}Qv;ZBN`Pq;mm15%9Up3]8#W]k8\Cq~@77sgǥAX Vo!5a1nd'=UE#ߌ'ꞢT!W n҇BȒȓ-]HyյiOO[l덀r4;;cI.K?_ctg_lom3E~ymD-/$--w=p' ` Wn?뵌dZy/Z Oʣny_ďB5oHjCc-xu0Cc·Rޕ7?V.7b:X!K7O-gVZfxEE^(osۜ5`֯jN Yt3p ug% g!&ԝ_UVpǭK4v(zsh [8 5MPV>N_ԓ}/򪵮L^gg 9L}&Wrb^>h^B(=Z[N֥5< ߔ3;O-۹"ěkYtPhhWlo7?O$kq{AvP6+E$|wt;p{EkdJInE4*׈xFIDHݺ+?XOt1az\)!tˁMYzgO`d7_ b;KrvM\!gȝLo! BRrڄ\,^7>̡[NyǫEJ +S)?!xl#㌏нR|{vDtޠx/n®K7^JE݂Bte7o:Uד"^-' j,w˝dxTsvǩJNY~uK82DH4Nm oCl%5] ,.?n5 S@2d<\!OCܽ'd* IDATi ۇ/1=}`OOa~ќb?y ~ "^3"93 fWWBԮ.0&& aO.>u(r$-4Dޑx*?w ~}~]}`W;nM\i|;d4 HĀ/ϓ:FJ{,.KDbg6mShV[WyK'>L@=@OtyߥЀYd>O{gKiތ*d_@viu|.}w)4h/eHF$oqzn?N>ġ#+Lк#FqwK/߾/aO!u=1L8~GmjKј@ɶyt~iT8~[h-btL? { .QQ!YiZnζ I!x{~(`X>S/֧&uvip4z~\"'o\2. bNҒ/]<-ٴf*c vWY.z?h*=8]Kͯ~qhq1"JG2>HG|D|X;x9p,"U"O|yg?K ,ضάy/ovU~ugz>Ő~XI9|x$Z q)n 4IEVfQ`[zLom 0 6c(7JO3f1۴FO$yO;s;hs`zP)4C& yhe~Ddi,֗$lY"3$]jxUhYi ^[,Q%Hz< -0=_㢴-71 8.rp/!7HD}gf85;-A|,_7.?X eٷ`bD׸ߥJΞr7qz`3ɓMAN-:c>>?~NI=p$4^ՎOjț)&CLN>yM#uC)rIrqٸGѤ iHBѨ ޖ.~o鷽i_¨j\ 9><Kx> e$O"첨<޾J(^+g!F5Β-EM3LJ-XLj. {IHy"M2N]bF6 8ssKrzP zm!yr=yܽHp K.OvGI- Tn!O +\v~37Q~)JUCJ/;_]6@wd R[W!]򑌏xo+}ѩS}~N&xT<ޖ$je: 8K lu#׫,iR`9 keu*ݹ$MV'wk'~=@/LYe^h@A.P0=zyT?Zy(}+ΕcC´}x˦񽛂^ yݍv?w/h3GϘS=T``$\]&筊 dp.>愔ju8WQn_߼F飛q9 `fp޶ވkLN#ФX~"zP3֧EyNID7ΟRRDWzO~ ԐYQ^7O*n?NvhCRwK͈ ;L5uk*gM?{a[Mx 9봈zd˙4V āTxF|bXpgOOGkpZ[+B7ln[z%av!Y;HWooASgE3C9yݐp5OWaoB= +@cxx7#rma|E3˕< ^-ay0,}o)nIo_?™[e/58ʂ]#^>ۭuhZgR:T .̓ELVDDhUV.`Q=`܉n]JO#Jә1N9>Sp~Rм=9[1wnQx_^GÉnk7l g]ONN:CPYUgER7n›?o G~<շ-5&Ux4e쒕Kv8^nȌH% -vY}0,)߾Yp}/°<>j/jrN%ۻX]},"OBǹrwi}"CkLt }:!đs2Noߝq 'q5aϐwn[lʟL X2;T~4ZApVdߓPrߓDW'pi.7:R@yӋ!V&C3r9'.AuNqYF*948"[~ErixG~AqrfPiwFAq?vnwl_oK_žVpV+y5\7gz~Ak7Wh4xy7Be:AYo9X#6 .xʛY"<ܛ?3P8ѿ)__}p+_<5G]c{6Y釃(Pe  +q驼2^޽$_|h!x #sdćF5"=ǟk3;'ĥ $ [$rtb |+0XpRM& rQ)&h3y[ ͟C7,ePnMiV@H;$[ Z˓R`\ 3;3,&%:\!\>Wm,&K$<1īuW_V.W0/Ϟ\5İx| eH߶ 9q+wsH^YP*o+%&͖HuGigÂxU \.OGG9~$J[΄ȡ+ȳ{f9LyDu}.|h|7n0-Z{7( 21I) =ϟo[%RZm7(vFB1ӣ 9kƗ.ls59oF4wkyoi`MrݢrWFB:x)ya'N [G}b/[;'wIp;? KFHH#cDu\+T(_>V4Z#bwo5Xy?]Uq~2RYOaiוZ~hw!o{jgw|CeZ}O{8/ԯȉa%(s۫_;ǿ0%MNo7ԝ_˽K6{~^`ȯ,mlSXLLIY]`Un5T\ iiB#`'}=xxPo4%=9⚮02mmnN%e[ӶK3``Un:SXԙU!/ݑeDm d"!~UGhmՕ74"^OYnZ>9{x n.ٌ< _k7,rG>W,:}t-G~kV$Qmouy7~'vOaKGCj!ȟ]P,O!&Y!xYQZ+B3yIL{F 7CnqWRPc9tk_U?zC2GF|wqg>TN_iސsO>G˜5~C93AͯWc9q!(A<-9<8]. ".C%t#8וg*0lv˒V>ORq] Y**w%, MvYix-kߙ BdըM΄!p+Zh5o{ߡMǯcf ۳pݟsQ5FqcL8٤Yt3LZF~EdeV,O㔛os:9F"XߴR&M]T$Ю )?}wz{z3 ,t/?nNp8 8*b#ase48.K7E5W~'{3+yDnԯ=Pr薧[d_B.:?^u}hjzXx_xWQJYKm#(Ox9Uz2ioOo*ap^y(DU Jr6-D󪩪\+ܾ~"{+NECsC({[zuZȓ8hП*us83h9 zp[ds Ik}|!4Dp I"WjwCkl۾v,2"ܖAMq Q5($q=y԰|LOAxE0ŏgU\kI`=ޤג$;w|Sċʪb'EH lvnPA+-/Dj#GQX@k)"tG^-l67Dkӱc9ǏXhGAoGO։]Az1n ]g`ۻ1 /5m됻!7Й Wܶ!55ڃ7/GsfA EBqLϩq&7w v YVwhZWe)7 >P86aAW;W-ߖ>4m3?/>G@ ,J|7!lwߨo -& ڂG=f9B`>S녓$"'%͘˵A$2w-B^cq)ǥ}Fydz LήCw¯n~[x6*BoD+yn};]s_~8V(?9b|B 5Y2oAߐK 8v@u$su|D{m>OYDhXKyYG8Ku,1(4?Ve~i2"B^_|`<þ*eu#c5l ຾:I8$7?~hr?`_ k>}|5 B]G_;TEḾ2b>cmv!(dXHJ|uM_Bu!օPה~:\x~" ~_Qzs@Y~ 9.|;29.sJqBV43RmK孜ei/twf;4+'xop?x~ :> /{e?Mڬ;.֜$(W90Ic<f}T~h7(O0B"T W% !&S X{@::-\L^Z.}E}!g2{TջBoJ0_"^OpMnystfH_'5#ЯcoK6%@e0O~Y~C|sY?3KA ^c~?OUW޽܏ h ?h*7뷖s抌 .vqusm~Ş3蕌s(Z4@4qfBڱr׋0=ZؗKXv#!wڊ$\;]q Z߃/`A;k)s]f]6|Ǧ?Pxj|'$@ۗ߻WktXF@l)ۨ{qyټyLOM3%YUf$M|ꉇc)dSyZ/s 0aWkTYS"ގJ "GHAd5Z?ALUiۺWO1aWtX%u-}5?1¬sb%C5/a|C] />: >*#Z@^Zi^[|x"`t>H6?j8w{(lw4=CA{6lW/`5..G3ijKe. d>eAAmxhocG89NPWx]en^8Q QA1`LAþ*Q FgzÂcxA4]@! o6x ̪%">8!mFTq6=ouH$CCAb(LUfN+%U"('J.(slb8&_qa/onܐnqC~4!māaF|~_GԂr۵tu#Y4vD ?ƕ@a/B3V a<[W9v׽tΟ@ͯ[3O^u K;&g>)Zj{ңyKy{(RZz?yzQ$l@-Ϗxc'K,e ¾:O/f+\2. IַXM>ϟQ}pc}^ae|"a|b>/D\ ~4AMDMu%fm'X6fmЊ4#Gj؋[$]6~kk-~-_j lAODSax)Ep 0'\Ltyƛ@ ͑2ɸJ|*%?T #U>Lhn(E"Pf]6y!_ Pi7nbU t08 jle45gM-?/O>bz4j6|O<\\f{I[AU&k5Ai-_.N|<Ƌݽ)Z{-Jćw[ȶo@AQˏͯ +;=ϯ@@7nN7Oٮn]:!q.]֚ɖWm57͹9͛ok~;7gZd'onԊk[6ͷcصz"`5t`|vuNo3azգ3Bum|t%ۣc f+ !!ǘên8*2;E6??m7tY)5w?WKun|A 8Iƿp)o鶥>m7yFuGKc;6'#㬀ʓaRv(CmmDy,_'E~NSL>F"ϴN$Xx4=W=hP)vWȓv* _~I~e;,oFN]M5uyC=T[_ȞV|zv~8Nm~ (v``#;-~l{w:㍆v>4Px/S]>Ng.)/-ק֖MMW{~0*Py{f_vKa:t2 ><uURzhaa.־.Q)"zW5r\ UUp lKݤ1fy=P~4]'zHރKYbl>䶉>=("}rcӆT· !6?!nO3>3d'F}[|xaɆZcL2j]Hww`xN? X͢"$"{o;G&nmO %&?d\ohzhrVr,*!t{k 𸼖.LB݈ꔏߢ,Njg8{cXl^w*Әc~׿D=ט]c~#DMg8r|Swh\@i@auJ:pZT~D_$UGSU sKM^VRg% ІH%,$p=ӳ)@]o=?@>ϗ+|VBM!ÏG=߾*%KmgLA^8Krp"AUP蟬 W#3N|ΪU!+Te A/|weƟ|+Lo:p$\ʜR֌(]xU׋w:aupiowvq叜rӘYVwhZ_C[}: vVM*N09dQ-_usgWTS2NT. $ăxNǴpCJ,AD\绕BmGKuCm&(tsHk^crv07$:IַHַG jCr+R/v7xx']#&x;Iʣ:ol(ŏ75;_E^o vɸZƵtK??+<> #`r mC]Mv6j?뿕>yPf ݼA?tqzT()*Q]֛#>r*}- M/O/DS5s=kv[w-co{|M h}]曳k_ȓ%<2l 1?`C햝xja*3l1={&:7(dxr"dRt&es)7x _9ѹg Go'瘜=Wڳk6g|F![q}9>x },@LÏ&rjHĵl[c{hTM٠4UuW}\d[lAnz~{sh뮦lW_oX2OnnQQǍ6Njh!kn<4J-ʢn`|&m9% -'/&lKEZS!YfVsʵ=/ 6b덀 OW(M+k^C"Fz\Z_C߶uoxÃcuo˳I3 χ_/..}hvٸkB]H7M%^|l|lnPy/ ~<&eAƳp@O}Irʌo1_k{UQKP{ϗѬe Uas <tY {D ƏuGM.Q+';$ۣU-ǖ摬oGsLϕ7϶ @2߭ N47|GyE. &@lσ6~} a4x u|ŵr8BIlS^d۷~0xD1d鸅 ]84ەt~$wM^~LϩI7|ͶWid2Y0͍C ;ɒDV[Ӡ~S9w<Y[|~<t>|يWn??u6 д(=w__W}.mZًkh~A8wK[h$QR2'kTyҋOXco؄׽&vUu/>Bxž;={ݙ,2M_~Ko6kG,-Q7N @7ያOObgzV:o~Ӿ,dX$PFIr`SQ%j 2Wf׃x=OzmCۣr(1z;A<OY > -]ozSd.sq^?tW8*gzuqrqwt]9JZwdz ǖN·͍8AѶF/+CmcWQB(tcƃxϏn&k$tQW9uՃ,^B;zg_~rmF:Mqz݋ǣ4$Πn~ 셴 ~B|d z "17=R]Jxv!VCVw_ ߭+,}@eu\In}_,v ə܄FE}@A: C9VJO!Rk_CAUorEznlgl]:-?.u<"#Yc ;tzum,27B1(ce@mAL5H>)ݔ~>v=U,y@%F(^9)>r}Lj[~_nh|dAE/zUn/F__b"M2HXD j]d n !Q2l9yaPCwew]epPEHduw;jP Uʺ`ss~n7{/NEØ>M_Y/>6''8mƿ.18pFѶ~瓳+v"D3>twK"oJ)_3U+5Ϟat-QJanP^|"+LR{Cc'tN.@0~\t[k(= .%.bу%A8bAnK{_b_U#m \b9@ysJ~_Y?5jN86Uw?|30oTxCc-m߿'8pT=!oyf? * .N3tM=.ẁN2 #A<|*&@UHozz:k&Yhp."0+ҝGF;C8Yt:-nFsM|fPp!^0=)i\K %45t:vV7G,LÀwxcC+;}9"B&5w!No@]@Mͷf`a| 40Ea61^sP,v-oPǹB ^keb}ۣڊV6Dgq."#ɓZuWg/-`vc%B(A|2فIedz?RA>ytӉWQc֕ޗ7c8Iƿ[}ڈ҇^O|>ȱ86~9*'A4W{H7(5v|jFjځ `dzgp@m'+\ u,Y"߭㥮:6kc%n UbuAKDsʼl /%qGS7bi o~Xi$n(&ge@DzHf'A8|Dx(aK3W%=|`bq8MP7E#ᆴsƳP[Rd\nPW~{\m7n u>C?qeħ26n@5ezؗla7Q|#G@@#p܀Z&AeþnW"b/lN}#Kmً)eAnP]x.6ID1zxad~¿F*u IDAT\|D]0g DBV旟 g e*nﰾ{!7R$J׭YVڏ,OcXb#:N[ODi?Oo7(wv"ۡONU*l~}͹zӜj ͵G?}z6u%~']U!ѯ멮J$7_0HT!Oz}? L{`|6Df!2ԿH!|kc<0 D( b"hEyYq\~dwK}1|󣩐dwһgzU z^(s~JB7]Y~w eщo_Ԣ<מ7Ʊ3sk>1 }S.}Gi3݀wUؼ!ih[Td`y~0f6 #%x-ׄKەfIɻC/Zgke=7{~ųˏ!Ky H6HZeb#hxAǖ??'-~D5 og`l,+9op񪢖ܓ؜;"9=' PlF톄,cW U EE>:Ao7ݎ!(4(826Y[?6Twu{lǃG;@K.i&{FصnU &$ ?DmD6UIuS(OĮ6?0k_5F/꺕>&5\7DzC.̺znMmO N>xWSnm|>C@7" tJnW^\w$um .0S:ɻSlШoM.ฮGtEuٻ4.Y=1>rx}G.^<߈om9n9\rM;cvġHR:.w_"Oփg]zr@Ya|!HַXejxg?AMS_Db^ȵK1UƉ+^vŤߕ&?ſ*! lW%r͙89ԩGRIJy?/z>9p}E%դ_snI%D[*nxB*撬m'+Œ Pn_UA<ӑ -5¡@/r>Szs+nF.8c8N\[1]G? u~y䳟Mh&$  $6"2NĐDrFQrt_w@J]X@׬7=M6b"]ȷj4h$eQqb2~Z(FF 8Y kZV] ?=+7X{/~pϮM/ivh+ۭo;??xh͒nɸA _ e .qwk);_Wvu=8wd%*K 'g [/ ` DZX%xA 7֥2OBwK%x>uȭo_tCM 9.hHuUEBޘ?2To4%M gRnY6m7OVgt'cbH' }ϭ 7o-U?5 Ы6-/}i]0sӒTЇ]!{Gi3·Zؼ!ifڶz/8.-DOrU|>QA4cRl8eR&#Dž kSN67ˢ0^H=qFMjr^F#FPWed>#L/`JE1 Aš{O@0={ ? |.IϤD4=GSnjHi2=APȅXHe6ם" _[d3834 $[.51hحnqHw+aJHO߻|*1vy"|\DrM15LA*8h^0AN|vKE|<֏֙퍪&*kĚ7%`¸ֿ}]6F↏|V~4C] %|\.ը?u ID4K)f$ ?̙߶?*& Wur}rR3 -kMPɾ.y8Ғ #,VoHZWǖ=NՂ|ʇ1!ih~?wV G^IL|;S`|֖-ekx~իK0k~bts_̂*2V{0^*\Y!VnߠʳAxp^KHw&爧U]QfcLY$._#KV_}B?ZYau\4C Wҋ:޳g8{czU4A1l-tgW[J|-<ݫHo^ޠ˳\JyXOoPuҍN譛#c]P%ҞٺMl7'! /v@;DU=PBB/{Wy|{v ZUx r2_\?RzJ# TGg3AyOMoUe^;Ds4[U=ųlx*l/iCzmQ8m?\xWq(޾rm}ml.r 3Z@D AJfլ 'gM;5NA꺠U-0f(nZG *eg$,$HoP1˥mp)Drd[1܋6x89{A?( On u9ht^?Qqz] O/ ~Aka~gWg _wEo ŕ6g%'Dg.̷]ߢܿ f Յ̶tﺀ2.s3a.n=?y1~*o]xS4q;^)q\PhOvqC@蚛;mwJ#omÇH ,pڌ.18ʶ M{6m6O>Q4DsDqѐ[@*ոi&qsǴ }/>J4TJ^!y vBrІ'vI;!cd)-Ϩu``}5l݊r:Pu$Vރ痟PCG dsG}3L_ !h[Uw4E[ܿ-CK uA]S\Օ>qF\xBYW 2ϢH"^*Ruً0*ydq3nr\?DUGT͌K2rAXC.3xD'qP2m&qWtWB~ a EEwFṋVrOز'i?\xy@wozLCڸv/ҙM l\uC4FѴ6W< 4G9F^|A<_bfW9v7z=/7@8^nc\j;9ӌkۓoaҟaMϩfSlA0ic`?ij t[}t}K-࿚c9~jDMi/x|E];簮 %sW U!\0t7A */̘#/%~hjm=֫>]p Ϗ䦵lM~ŕ4I+%Q_L)4B2PiQKΩ.A)ҥ[H~#Fv7+>P-E\92f>v|Pj'$-DϮdGy_pv\?n״.Nݣ O\1_z6[ >oFh& !)HUܭp,eyO)|a](Zb/xQuYAZxF::Z>PzSWF'+PI9![mTGGc8'҄st%YW/Fu%2rZ cHFK|>@yE?/sz`-҈JNsh~v]1eH-&>@ϔ|=g])p[yv 9f+K^_).qi3S-Z>p}Щqh[+]ǟXWhr)ɂJŋl+P g4Zķrd ; 4Eu݋o<kY[ws.e!EzM2nYc9 *Yi B L^b`dѪ̰z^&q)Fae:B@œ .?ƈ027 $;$ۣW?VU{7Kfc=F@_x? 7mC;% 9"nJ@3ƍkz~++|5i桀ˌ5M8 2v\w7t<@FX#-K eG:p-/_`WgLWc)<-'xb8m?Lxߋ1clj|TǟF B /Pj܏eH6 wjCR%TA O.@F4$45kۃ[9O%yԺ̩Nt>BD`w׽ 8+jJD{hze|U)_ҫgc*9n(H6w8~)`~@]qDEBЧayR lKmSx ;a]v+)w}H?[uyyVyTm`z y7ٱHP)Fn[`:H=_h9y]*!$tRNh?zm 5v 0s,]ei( 5fAx^.sZ_xvzb.zjB}n"Ξ}XeyS2~ %L7"6C+}qYa'I)ޅD[w9>,@Vq&5u]"߭czqnOBxkK5BI=pq[%Gl*S@Z$^/lشf3x+ UԧAs@zJ:)fˠz|F>vSfoчضw{'fÂZ *{̃eHZWhYFsϤlTt׈= g9ԅ-Jd.5)P$+;ϘEd`ze"ftYjIc2Go퟊?\6]2Ny@.cfҜ,_*Nx89A i2^da#1` Af9?+xBHĆ _* _ZwfЧV حﱼ9IO?JpƆQ<5$큣 IDAT/Q/B}u{?El"׬UꂫÌ9ù8<_w/ "fT0M PU0~څ*SxAm6N j̩' &B|_ǾF1QBuu׍ k8>tQGmXxaG|~J+]=*aw8֕~,|hxNpڌ8m,{)<ߧK[z+oK2ʛJ?r=9,A] uL˜Y[wރB-+Z+!t[׹tڂﰯ{$)$F]] xW6j8׆>WK,w7܎/1|4)1} IHx'MO?U 9 #?e4IF27Ma4&$~4+ڪlb DcBz=Rv?o6P0/`1dۇn_ ~Jo6'7\O1ցP #<2lH[9K:Y23ڪQ_Uu*ȐP]9>*dѳ-z6>ݐhU  O=cHOpĔTp@/+3)D̶j</@FQTRJԅ/]bD"u+= )1,OI6|ŵ'3ꊹEk9hXEd{Ы Yd"sPY׊rrl^/ϨFkJ=QG/q̛`rUR!k:*4>(eH6.99ES?9_bZkT,FYNe[ bjm}_JK5w%=KxHߗ h)ɖOo.!O7~cz^)mZ#v4o6,f^W OMLJү6Wޖ?$ۥ⼉lkyq}fܬDmp|&uГ6*ьb|8[%mdEIl؃02={ 1^ N8n2O2c xx3.0r]e =JTE,ld2ftr^?HѥoJ C2 v_wl5tV`}+ep5t:%3X#"Hmծ42NSHH+AD)Q-]2NҒOϯ@U d\M/<|XMW iҢə!Io݃0& Mg0-8D8 <]Y?<̏ħrR!G_QfkO`:KW](·d6 *4Vhڎg 9д@$ |y y'/kj fE%@m uxVD |G}a<떐w! ԝWCN2-s,Yy!M7S;꺷x?^{fK<]n3ȹ {(J!َpOB7M=zr!Z_L0BV)SLwsn^YYuN>SUy[re-WZWOOtv{"__b3@ Qsu_a헯sSΜn{[**`@`_p}ӷޭP-yKqhič6DӈXBO%u.ov+DTi-MX fYlϷ dS(-[[qmq6n}o]k}.sno޳!(į6>Ҋo{sjȠ0L#Eқ2&/5 UUƛ*n:0=)Oh}AfBso|QzarGDb8z 3udYvu1ng88}Oȕv,J. @gķ )dVVO*yW۬.^>7*1=o3][|FN ƽC^iB%3/,3y^,2|X |k˾{ɻ/~Cve{ 2i姛<6@ WlPl4Yq?%dzOo ;m˓b%";g?a&wS^tA|%ef 3D;f7I_ WyօUv/- oܵbs[q"uUOˆ%`7ݚ5)l]zѰguv1HÞOlzI⇈>-;x(!mڐoc`24`[F<`vokei.P  1Uv7Y ]Іmh`czLnsmЌ}Uv,6pS3nt,l+ŖTju0ΐ.vKE݁;7+U`LjPa`ǽ{5eQu>}0[4;hŢ=֌7 4`dt}]Nkd\ݜ19 t?:= vEa=¨m|3C폾7Z jbWv*"j+%j1u!u<;Ǜ0Aګat%3UEM7nfKs̗*^f}C]3o[_O|02ƫ_kϩǥ5Ph[!֌6jӯ0MZ!-9/c pk!A^׶fwx>fT;w`C %+}b|}aO$)Nk頿&&f$c{>K+5|ք͘b0Ǖ9"Q Cpt.8c/9+˖ ` Qz0tZh9I;_Sq<$킮:b~7FۺҹEuҏyx l?֍l,MnXgW1ű44^*ǎ;AhThi&o}j!8.JZ>暠D?˜¿uO@%oqn 1dg *w,zN͎MVeNtv( f'vE/Q)Wybk~l_̷NMÈG='ôϵc8~ .#5#~ O(C:=R^10֗OzcٱZҘ*s.~_EwJr#ShkۇЏ)׈w|whȕ|gnk]^b08G|7f WO8ХW<-pCe{||$ôOb:?Dn}y4:Ng'|Gɟ/__"__qr޳ىosKiN|ca0C g)PWwL-1|dUOۡ[!vh6PVZ96Izl {*8vGaWuUݐ`}V\b&aDt&3 °ۓVPM~**ݾɌM\UN?ȣMod]QJ[r,l7DŽ"33Qaz˧,js oUcyNlw(k3nWϱ[]xӏ gߤz\ L_k,vX^}fd#vCwign"QlSYlP[Ėm}>Nͭ!o9t]r0J1 Bf\Q 0x>@cuQbs叁|$ّ^c5 (uky {~ws $Hxwlqyw%wJʩw3͋g7ƏWqA q yl`O|>QMjlif3J4Ty,m{1}XF`bsfw0AX̪ǐV{ J44*?r; 4uEӻ'hS3eVQx_: A qSxӆ㶅߰Zk`i7Եw|E{ 5}56AMWѕvu׵ňl=Skķ|01ó5P0C}Lݪ?}].!'vzѦ_Z|001EYtntvx3 A.whugzqnF\H3z qgwCΤ\4D``wMuXlұS߾*xs 䳌zhtO, Ĩ0v!aage#lhmˢ;K~#pݛ>0 4nF^q66LPWӧzkĐx0 JJ;uA6_:h8sXՕ}+1 c[#îza ;+{F^{聑=u4ŧ䗑FwRJG&iap=8}As]ꂜ TMHEL/b$w0o5ߖ˖c?yc?<]5hN''W̠'O_~uw3m0;8Ed=6ThIUhl~0ɰꗃ&ToxKd3G UhШnHf'( |KzS~ҹ1Ep? w LghyzD4e@sGb|sϭQz133^x&ZM.Bb@8': Jt4n:'5T;/sF]^yFC0Z yU:Cӓs7Jlt~I tU W{gܡkDizZ 1҃33C]>IaBM-6˯/V{;}p!Kv檳"=?|(Q6+~Bvӭ-̱>8k`uib<cdS_vK=_l+,SӞI&PHoEWϾ@mO{޹Fs6w]/2+1__g<;8@/hOd9lM]N v|C>` IDAT guN,UD=MChӭ.y1Pdvl,N؋oy=8<5\}?o(N zvbh10dGy5`}q27|_tCܮnOY 9[]rJ_'&7i0Xn nCCiʺq/ߏђc F} hn:V੘p+K _vp0LZuSoӣ{{6{$ّ H?0K0 ߾`!ed送an}ɟ9'Fv5u]]`~Sdp]Yf=闖~] >6Dǁ?0i5=bzxxEnI1~-Bz=9(-gH%̏ٴ'qq~˺˿}f'4[o(|2=FsUtI/qz>_v\uZIYw<\ 4EA+^EP~Dҹ>߾pw\#+s}]unRemXWra׽i[kxPo}p0{uq4GJ,Hll n ee8cQ~XuӏC!kXA-bPT.r@ֳRiU!Og*>IFظ+/f3:[_"_sWh41 /LhO,t!njƩW%6|>9os'ۧ›{x7{A_~L )Y=GUnqtFQ qB Jї%SDqCbCU|+}{ԤI=,+>w >n;TlvX5l'8[C2;F]ܴ=LpqIQ_YP$0Otj+ \a!?~[ʏh\f]WVفv@_[?Sqi9?]]pi_݋Zv*сʺn˞t1pMꮕ iuUWp7$sT[ꙗUU ,VvA1X竮 Govtv#]=خ=BOUcXѶ{0>;`$mG=4B#~ܚ6yn#>Ц 8<}Wƃg46`9<<?|z.GSǏo7r |ສY>GdSbڈvzM9ֹ*6벗}%bzg} D{lN<4 {Un{2YAl ϋ[&ד(d 0+x(iSW9]&uU8{>_k^|aєiXW9p?Y]W\c7m,F0~\x^W+λN>A{~ve :N#LJ/G^8nE6:aPX4TaNu].:–m2 ,N]浳 =f2S eE/rzc/k@6lUg oi9r\]0?~  fŶ>z >'+ .,+5]0=D+Jo1:}{ x,wzӿǏ>m矞+ys ֘ߘ2|Ar~lUtGM)/KA.IL<81uH31 Mw_6Qs=CC6u}s̒Pju>תr4RHz]N0=|VLV*V!ëOv0!0QJ=|1.0J|@nOݘ?\^\u?1wNMe&zޟU(I.t&=_ πк U˃ۜChK?-IZqt}ό^~l2;\ '7N%v5Zݭ/ аcp {]qf<,Wy8𞱭 ֗_bzKLjә]?+0Sabvxϐ )תQ wS'jsc|_u9;OiAOܟ5?Ȁ39.iΟlSɨ91ە8Ti٠Y[z zDȳr&{_ю+x6:Je*-7Sڷq>Ə9aҺʷ󮓶iOk26R]`&c*/V`r\A8br3`iuxvbMfea#D*_Reo_ѰOXf!0`zp&c?),P^zQazxfQ!cc`'惘ƄdQ!tH#S5&gMf p)LG<?nC}znN#cHS<|aRcśK;J2K3jxJh4[d>5$~Mv͝mFI]?a ֐Slm^߅f?zn)$ab_j4Zw]O!;b@k9kջz0F6z.pcSW9|_Q E\/#] js5/n|cu g7(cV{~(M1"ncE7b׿IqF]=@ -&{_{S2vb FŎ # L/0n힐 !#٥gu~+D?tz*hc'^d/K݄js0AFI@Zq&WbJ@]ih ^lW~9sbU[uu{;lbyPf2Tr-t6?d 1Ү^d|U`@H.&mXi0; Zȋ3,aR =ݓv䄑'Z^~Az 8<{ai!cJS& Օ_h4ǴsJZ~SN0̧b {8U2zO&!rK&DW[Dq\K!klS -51ݐG? UCWs{0`) þWO+#`ηH$ʽ*׎Ɨ[a;¾.z&$!`POu! cSӎt{7]pya=9u.~_~_Fb_C+YV<9cQ]*dߣNf`lB]PGmME>P+IyIC3'&_+N )nV{ir liNgd''gL̏ڑ70?y@Z0B=ˋ Ҹ@zVL$եLvh|7?{||@6ߥSo{</6wc~򺮐sn&Z,dKH $!|;^]l,zvB t^UTܺ$c*w,j|:[@1S{)k'`ăw֖>(~dZDqK>:=֩!N͸7΋}maxrM_t?  o\jeYf\ڊ;ڋtfVVv_dh/d+~4UI6N~H+nk8-wKwF[ىP0ݒzLҳAR!b1`v #W-Czۉy&@_^z"==8E6?UI i3xve L]s?pN72XߟO+v80 u~d9S-y~(x\k[!SLH_0PW;'m>31ckK 5m}za!^L(dQ c4378asp_W0V3>01$V3vD1)&t6D#в_#̙kƒ_ِzAucv'^2W~_]w$og'-C&og4U1h;X-ېozT &1m7ъUٻ: pM]Rix*aOCji(!0Oc}mz8i٭9frח! `[c=AL1 tg x/SR$CL@*0Cl-ݫ*M^(FU} 5d3k}7^?x)Z fxhu/Nȶ۠^]w i-qsL4@vDϬ@VȷW@C2-mWخ/Ї*j(=Cܮ3#a+dlG\[d =DN;,\}9<q;qv@;óɻok8Kn:Ky'hRZׄҔ>?R,h!/S ;;c< rn }첁ЀZOp?>~-LS( :A Ҏ3~}؋͂^Ew@[s7?A!J+ۅI#0f[N~D:w:ju#,dSI?xOl|k/ľ.{饳^ϞUfU/=53ߌٲYYWʖ2-b^+%9t3iÝ xu˜an@mܠc !ߠ~Y;)[n6{B9;ܡZUlW!iЎk|6S{C>T|&͚G>V#UOo^~*E@ZP~:e{kT3kӼ>&tGxI0y p"7}ɥK:<_e"D1AD]"-=}N3V0 MA09 Wƌi= aȵ [^Dqf͎@B zB\7W/ΐΎ<sL~\ÓoJ3c~6_ ecy!ï_w??nu r,˵OѼ?IG4}>߅f}J^&dhHAG4Ў )ȆnuIB1 c +as_:=Bej M:쨗bb?HWl1C9@X q߿*_VO' %~Б` ?3fpz}u\7c<lQ'w%P*>XM5_?NV 'iCf,wjdV/r,„vZwwi6*FT_%Hg'ʰ|hݹ͎&?4@U.zΨKrmqiVAU' R?-2Ǻ|n7x]S^;SYҌ[q? 6{U)&AЌP9(иwsK%I`ُw߉eUF?{y6'9 vuh]toiPn:Z* +x~!sq 刲_vg?>wGn$Ax_67*}m`9IU6¾N'o}';>Vx5b*Y:8긮Jm5uЉ[g \;(ٱSS O%&tLuaz_6;Nx[}U%C̬"xa~_/(8"- X^:}Dq ;TwK۵?NDr xk\v6ydU,wJӏ>i?=ږnjF<);-{uKBFݐ%rg ̉u8;x# 3i AǤ_9؄ɾc{+.cH)MO5DJ _րm.@Ⱦ(+,Bzjh}F՗h꺗^2;#3)^Ng4!ν*ŗ( a} ҏ>ṋaۂ?Vg w&J*~Ʃ 0s/+.w*v>*)V< 'a|lР.r?SGpKv+sڭa>9*wH;:,z*T?N5ܦ[SU9Y.`щy "n{or5 -{N /7stGe?o=y_*Z;-ւqi0*՞Vfi;840k G'1Ls/1 ;E{:~n)C?p2.~"Z_ fad Z",c֖C]ȭ>za$GB#%gו/yfHNuq LU|ӏ>n8r Gcɝ^u*r8f_DiUk*Kny%)SѪC0j=hZrQ{ ,“#)6A3*<c䬍Xb]s&?QXC)f]2rdaw>\8q!( 4{10tҙ^鱴i?U /\Fp .{^K.~WԮhsE1-4j-]aNiP;W4HfGQruJ/G/#kY'Oq!ŗ(m{??+mztSVrvas ne}cp0~puo"ΠpfǧgJ+3m^'q0L rU`FZ}U: LiO+n8O>5cfX4A;Q( ~VïV_-uy5ڠ_Ƙ8&$򩫂{# d8'nFg&Mw3w94GE6,Z5ws!sޢ.V*8P { + 1_,loˈ =l]\ :\Y'x7TkQuUHQmԪ9O+g{fђGE.QmlȽ .s\/2Rcf?rؖmkfѷ?x7J^#Ə?p(WO~.'ր$CƵ+f/yOc_f'?øcz^BMM"qvʥgGtRT \2?C&$:+*\ޏWr0%wMG|bɾ Mowj]6?F0jƯ9Һv<| 3 -[ɜ\ et k&+U!W?]pJ[+j/@׊3*⁻  ҉彖1Sc#]B+yutsۅČ<[eyo)y;Mz 34_^g|:5']/dvyoq:lޮخ&UG6|7>ӫu#ɸmoӈOX*Fa95v.w\I4zAg-bvxmM{AY? 콮peӯd JzhFU?'{Ўw$J;b|g;nA-œy&A=Yݬk~x)&ݗ[ fno2Dږ 8̕ L4838o%VDXجC+ VP~~oKLaZ';4VtcNsiŻQ{qU}4]<땷HqkCkvh](Ej(y+&͂{ӛ?@_gfqxٵ,]_o3~19i놛c-?ag4t\9O(YTLTR)) 'E~/'>序l8=!+N~_]JOX[㈋8c|5]t8ԔIlk]|q2g`vF\׌ gt)Ѯc wݶ/Bw f|8DUv<|ȍD{] ÿZ` FvMݹ1y?,uvMVs5d q6 JWxi`v ݭ*Nfu#zǂ]V9g|#M[`1Ŏh1u HSxbP}d{[{4ܱ0oB;YO>݉e2+rxۤ\'ƵӪQ:Ac"wPo?V48XO67ۤx|`Ԍ6^_[v|dcZlKkr_E m=\r:fQsҎ{njFE'ӣN~t?g""^zv%b_<[\5.z" My +f\B8=;YkݰzAS+Gb&@_{Y4Fe5\:]eIX[  R[++$.olQ֥.:30YosB|^K,~它_nG,{uM[| "Ҋ+pV#06:kv}^',s o18QQٱt4׶rFt,hNb֑?xO⮉5Mó?~y˯L"l#o RNLcRmӲns4 nkRȅ+вӊeN(k^;b"3kVÉms|fJ6~o{+hl.{N-4%l%7^:h0Ndo:[î0LOMrU1\׃Ύ;;oe y]X/OG5UcFo/MpvEK&բ)ߢ?}%u~u]9(g~_{bӠ^?@qo, +:-[6NdYf_Udph-̘ҎL&!9DBE逺*VN~d6 KUe@v7]z#0j.\vݡt-G;wtQ,zظV;G `|V{ynۜ0>:d?C[Ҋa-a]3N%&1t !_Iv ;ahLv0=:~$\.x.lCrwWe9!)2~͹z٪G5xf1XGVx5142at,!*y2ބ O<9oiۏ0Χ׹ |۾}t"oPlӳXXXQhbv{ 9H*\:6VW5v\cFx/l; DB0w\<ֹO>w--;K~r$(]|5ڔCU g8揘LuU69bnT}T>KɑczLҟz!,U ٱ:_]RD'##g{C;^8q١%=KɿIw=\4K>\[~n/00Bu"l=G\-Lyg{2da> ( M|FI0h@:-x4G!apM.ľ /wl}v"w~(M S"k-MVd@k؜;@7kMοrKs"onM_2=$+L:A8uUg"6:*=NPNa g{fS4Savκg+0TEW7l#0 >usr+]6oNK#n g`mPh8Fj[Ƥt{9ZuZPw k i\cq6~L֣e@SԷwduU N2.^1K;nQL&J!pku]:_#8b>Jo~`Ԍx6^Cnӳ rU(F}}V6 9j9o8Ebb *n=yW3cSw kVj«;<$jڂrd0DW}aV Mk$uĊK[s}G>3VFc|UO+**e%Ǐlx$88;l/yb|lÔ&\#W;k|\mN@ͫz|JYk~vXOސ.:C2h`ۘ}4|]e}x_ CbݏTqz`c }]U0LęoҴ9Uۜ3CI IDATn$@etvuc],\q[n~H+]<8c-޽JjŻ(nzzƫmt6;5w!5 A 5:z_Ȭc#|=)3m"GSmHR=:9tח=O(56Q̹O/JfN` yOs+q…xCkL~(~^A70; u7?0Ƅ;A9|6?ͷ Ťb^a xQz> fozq/|ߝ `tWm PYn/Q27>=Ne$VkK[Ǐ+jaIO0Rrz_䔤8*0m$CK6J̢9Yif3 Kz'rZsJLͪ2fr10ο'U2CZz,kߩoZ]_Mcߢy^Cؒx4=::IWN|"ꆇ@l6+.$'왖@ዪ7 (/K5~vqW$4:nU%^I.S>l&Κ׭NA0U ?J_M?GE?ƻUM&MV0Ћl-TQD= XVa4ڎ`#Z*4{nsG8qG6Vf"ѽ^ {*v^~2ͷa%pjuU4z{.i\װw aj|VLظr`^wjހGTjAVLF#=cQw|Y`YRWNX?TgηW#z+ۅL43L3m+BF\bò[ȗJvC4kx's_0zEMջSν jب} Cw&iH_Q2l/w73G9qݻwx7Ƥyыs,GiԬd[~ons}]у=yr8`r鸮 ˆvC[r*?æѪSt䪽ȮΏ.خ/{D| RHf%)k~ M0s v%|×Lz>~t?k~n.@?Ʀ ~2jF).kcrI _#AM|^wx L1[T/'7zIHv(oy_!yO#wc0frzAg_i70Bo|[|@>/ >C#tw]t.nr80Ks[Bk+9Sy/Fs//>EX/l=/!X,Wqj2 ֞r 8D\[ v<ZH_Y"g\g^N1m0t:~**ǧ?=WiMH W W_֯kr/M$\;6}WCߒBYoq}jɥ`^stn~:0v~'e@c[ jp6muvWV@20I.Cڷ( ?i]^Rs/ySA9U vx̗̰?h[hoatOsByk=qzPb(+dP@ϸr]j$[ xvјW"ɨ d{^6Ǘ(덈NamuT'K 㶳K0fD>y2r櫘/`r'ed- !6@sۥ)ߛ<3>Ο~(ˇ4o&*r˧tmZ^Y~ދsgywmU{k56 }6O/{`ʼkw}m 'Yy1~A ;yyB=p$2Nz<$s}A3;\$鹬8C/ќH­6qzKq+g]Бִ0,䭹: SjXbxW>[׹mZ[yN4)Y$"C۟7"Z^[~1'T B6t||cWW02swqgkZ3v7dIC}x؀4mO"/< mlv>溡J#;WIP:N͜/i;wͫuo{kθw5rm|q)9<(_{ϘeYj<^V *QODSIܒ0-Z"BIy ˜< #7`y+]V\9A0ly\-cPY:9йWQYgog>zr-ƱӜ=EзdG/@e)9 ĉ˷{r?T/z|ro1VXa_þ_;Ej3p:UJ?}4v JM03A/WTmt;#8e\N20;_fq} Oѧsh۬}=^LyƲ&ӨvI} L@&釡o,%l{ӟ?~~y9#!K7#t(q ֱ^5^C 9ѯ73Iz.ۜs)"/<9xI8眽>f6<ί6v&+h>ٱH&7{ =f0v x_3[Ey@WYW<|W/(O /uY%]Vvnhwo[S43)b{,J/wSqlΞfKN^Y=+Ϟ9C01?#1~|۫[yt7GQ6 o75?611#^UOꝋ/msk^KS< Xw`kw/'_4OGn4" wvB'1y\-pH&s<9og"p<2NkYf )G?  ƼVOKXHX6e`pp 19όg_Q;@HJ$.,䕆v0D7'&9la)z(hsMV(9h9\~է,TԮa]6"9~oy,'b{P_;0a8.dT+|ai#]K=w_ >c>kZ֟N/\ei`3e1 X|2tƖ"|xA9.l>t}е:|wmVRw@66;<o8γN/R9ƾۥoQo9:`R:Gc%)fLa8@ > סH׽VHS_̐Ϣ|OWn&ǡI B }'*s$rXJAs<fTuGA4sA9N|l1j'jKcu2Ok@s;{[oñssy$bpkUof @̛=B>Agiޞ m8|;kL4+|#(gh9R/Ʈ8GC luz52~6v\&>31td< f{.>E\Z7PPPCVEMx:7ۧ Jq4zQToئ" JֲtSe)BS.Om/?GSh#w9nZ7/gqxC+`j̞S:$y8)se9ODMn֓xü'ZV?'iGxfoq ͞9N޷w:oU5ftQ1no|| p02>O,DNFaot_]lC)h#~_Nw~o-vo 7'<hADЯے'lzWDr!ò$|/|":e+EOt [M| y>p8ܜ%"F`V>MξfaAf;׌}]d>ÍIý)̠\.ʵN>u;Tۥ)};,_lY,j2[.wѣ}k=&~XmVxoJUpߵ؞'ֻ(wqR\,.1=d8/urAG"oO@ar?j*L]&}0G is/(_gp =O!=.Y%/=QumRPOQsKs>=}s<r8MO0fufpV&#KNt#ryi9}] 5$F#^6g]9%=iЛaXax7ݻzZ78_>hR>skGmSiQm.N8#HBcE1|$nO m )ͬ}zʵ4Ho6r{D""׽M_ >?  rh___ IDAT(WRFvI(2PeU9P{7s :2Ƣ^طz=޳3#nL+z Sok:l LNee- hϽKB STJr0Үj(c;7&o-"T>A|@g#Z֯AsUxc5"ͧM}s9IڝB=pR&''QzscK =O6>^I($}/nq'X~, wa? w1Թ?W?jf2خziy.9!=]p=0 =A"YOdbUFu3gsGtX syNsΞf_$fOUml: Hss]NZJ+{;+ʇ\D)@PmON#rt,1yξkZ?o?t{F/] HhO9v۱~K+B/᝕m2>^a}$=Eb|#arȮU7343"dq9)}Z^iDјWSyژr 1t相1uWiB} ,I,$܆dE#˃bCћ=HUBYgE=}͹ƀ@:W”_Y5zq= vlkoctru|wݟ5kϔ) ;joןŀ VXaÿuLŻ7(uxejL177nEt e#|*E ^l-m*P|~ؽEٜ{f _(z}:ZI:M0`޾Cfϱ@[o,clGW;W˅ⳟ|??%=M1nje^ wkdvXOMxKg11,+KvXΕz"Ⱥ+?=HyJK|'7]cek{4M<gʐ=sya=9v^ah\n -؆6.c ")lxr>:;.%>AP~rԫ+K]FZ֯G}+ƔqBOsem$ ~彀psnrGAb7wjjs烠sM0>;7vfȫ>[,yZGYL~du9qE9ퟹjs>?}wX6bΥpܕ_W8o؁}0U79u8"϶ةs15c=D7I0La~gxTnF`N(vj̯~\P С(JTMdEw7?ӓM00^-ԍ:f2l4y8QʧQ,;/Gg} +#~9Dzxʽykylys;ectOͫSzǾØ[}x_q9rDhf9"*qvv)I"$=t- tχN+^?,g=zJxw >E'U,ELH>Fƀ_Oo<%Ϲ >ɡ\9 eOs1@FA>+G.tU5y9mٿ #Z0G=Oq(vVeXon{@ӑ=)^vJ)#WFPm+WL#G2y6?)İ8EWǹ1B~erj+|\7M}ǚӤ\^ԛǁ $̇է,k.׷Ft<:n"9v`ʭ*po>12WO=ߧc˷B)عۭn?\u&s1a8Xsݽ N7P0 )YlVLFv#Us>Oj覫y};ҶF;>f axr(?ɗKVUӏIXo ևM_7,_s/\'.Ba;WA:_0jǡO҃J| s#WBHSo* Ō=WPz]_J:Ǿ@! EtcNYu|+!S,~9))q~_tHϾpaWs4Aͯ9Q57ͅ ^_%/sY|Y*$sk擑g")ONO^[L>,"ĵ3W!>}wǝjpuʻw}!? ]znI]uyYxsBc"ȸTǡڇP:.\{]yH3mOr$myK~KsdAp1+OY6ƨٷgY2fuW8ls3fXhɒ<64Γ*OeUJa{'Zԧ[<ЃkZx|ZR>|,__M J;{% 0)}Ǎ 3Ic1z(+/FMI1=PFowZwVLX3=@`.:!$2 |+xS_Pʐ)(U2˲%,y%1'٣\Nk ,ޘ3GyOzݽ138=͕P|go8; #SYVY9\,GP毇 3wNűub,R1cW57)97. w11J@v΋(U/%fQ yF,KdXCrq!ˑ] (˼>ȸ>ʺ1v&Ҹd3@CRk9{*'<$Y tMȱG͵TVY ʀN⢨y$pǰ[:E[cyqQ\79>eŋ9a֞;}#8/M8O;~>.~_zY'cw)W:L塴V^Je瓡GG|[k)QJsWӚ7ݴm;[|><$pn}'g[.y9USyzǰv79Y ݿ.T5xon4ߛy/oٜ[%x4y7qߢ7҈;5zJS|3IoFO~K\Z^K~w`8߳7q"x?B7SnKNf$Ia#[ȋ Ρ`ڬgNŜ='eY/q)&2o!}{0'^9>ʬ3p!&{i6uwx __/^5Úp;}Ho0es2:ఖA\ 2㔵ԜwX]*m9_/)$BkhUDTsnќ\oγ. ]/0o=LxܧLǔo^r' Iq[ׇS밫zn9c6_@#sƛk9_ܴЗ:b".u]@(f~sshs9>$()y }*C熥QtD\4=]ι0Ś GlN:dnj;yy'Hze@g W/Fۇ+^W|gXWV'0h OHaR&lC9z.&_E2@";2afќo[$i{̭2K{̭2y}z{hN&®?ђƗQǶ+85yn;k \Xݒs~bmR?iDW?j~༤~]_AN΅$=@QH{pדrW@YolY*CC <Iz %NpؽMU5V3=zk^G Me/| ط]VS **`=1ݿe+Avӊ;pax+^#@__* OfI8෮rKQ[a{!pY7z=Gdq+G /@)+>)bYzv}n'gCnu~WF"9>#ۿe2D37wcZ :Ur 8{`|p~;nt/+aA87$_yj졆 sNCcZN_.]Ż$Esnhy6 \8=rss#MO+/ {s>Ȳ}si 1 rvqo>MT<~_/:ޮSO d1N\T"9f8IO aNz8I$[ѓ'AvX@o8 dw>N hOϷp&$|\xo4g4<6!㈉E7 Vku>XXonc6O>#LhΗ=&t޵KS)z#c1iw{b @EmiRyz@sΕ΁+@{7yc:R*CIW-. xX>@\_߷yO`|jO:3 >x_~i#N]x5@9)/7SV`6ayY-.&I̯\\@wxgjë6[PWPfMFcRLrU`=TlӅ Jb]AfOTCb`qav/E Бo a~:Wb-R!IecrmEv/zO魀PC朇q1OW0+^W|O{w?j lyj ? #^y03P9CR_<2 B01,K+\h9 #J]{! 4BPyM?5;-O>o T/HVqt~s@'q$|BKYdιJ3zs>rT;'k,G8ã,L9 yxi&<\g+^W|sOy _NѶ>;֙Oۀs8vYHcI˺aO8KO⚔|?x| )^m0p墜'0cͪ(r|%W}M|p{XWS>\Xݒs~bC9?}Yk TR#`ۇy},cw$=E'jHC{:ֺ9 ~-DZ.(OsA1IӜ=dnq>sؽhVO# IDATmo0CgO9gGcţ)@ \^G(eL0刯?"Z֯kqmqRz5fX(6Zg[;?d %ΟC{saqz(&_Dq0N/3!{?1 ׇͣ%w&/<7e̮=9 w 1oҷ_CsŸ% ] n X#iX2smuK q?<8`{j]Yo ۬9o?͈@˷Izl=W_Ma&I4Orpi"ݞ?M;/5ixq$A\)敏x77|}bs8G4Зtzbl%AbLQEe'_%Ry-Hk9JOep9ySPGBY2KOGNLMH*̷A71[gC]@5:U!_VL)묀52~Δss!,Sg鞽|XG)WXk`#)йizPsbs'9'HPc˟ nϥi@ 9&WR`6$A0+kaXZ X{޻6I\3Qy37Ov~OU, A C~;=\u`M{ 9:: 'x;h҇s%_gū5Pƹ7!ǾKi\s[KU3|JbNR'tќ2 `sf!*Y4rؕM77 W NwYl>>{ԛGQ=@uNmq+֯kZ?F"Sw`,Fknb~Ll&tafm6Y{=s?I}Y9܊NyxiQB "'ھ @QTIq_g>\+"Z!:nL)/2ãw!9EUE|ncGY &4kYv>9͌K8gx^hxzoq7vИ@Jԟ%4)bk2֯kZ9)>ra͙~|ra r"TJGƕҧ,]nBD:Nt'jbp)M˪mHi8!eIzW 7z镥'%yr=rR{=^Dy}@ќ`U嗇pk/_ͯ'au>朹K>{ԗ}OYTh yV o &;[:<l;BHC&)b[]=)x7zQ2֗#Omjgx컼=xit]N$!ʑ(#L a'n'䜛pVXk6}+x߿~f yHD?摋~'*Hzzr_CNE43ZBoAbD>{ oPg`>Gp9\w枧(3g&2QnbHȇg׆3k M9:Yu Ki'@,ch<.Hy՝'Zs8fsέ z朷,=|9ޚ>YǟG)ZȁSΓ#?e٘8GX˖M]_O  !8֯kZ_#{8 #7XkH<9*>}gTR"/XbqQmejdsM(7b|0vy=;•19}{9zR9pqTdcˆCRᵉO̵xwå=\I 1%OS.rsMxUze\%p1!]OMl+70)=Ľ1t,y_zև"G I9u Ǣk\1z`WEʅ6EoWEdEU"IcAMd'5aR~=vU3F2a'oKff|xyq È7h[LWN "'=llYJFr3Dz;.7ow"9,=pe@4 rL䝳$#BxM<%һ81gNrx6.q:ixsOs9V^J|^JP ه睓0 l)Asν z'%鸕2RPtTyz JIrΣPvM8!91zsΡ>ֳZsaAίZ{:.~}Q)^V4Xd/a9{D/ϵ,B.Cוa@Nbeeӓ4QHOx%9/9;!ZYf\$:#gs[l@wʡ/muxE%s<,=\|ιBQ=9tu9w)KOs}sιo}Zs =)a/.lxŧaf/oybRشVi<=<Kr %i=wx|mc#ܧ;Ķ3zc!xE^>F 6ڞׂ%s 7O3x(y Wjyٟ°)9SHoG*^bEmi;7+L}<睎 HK\蠟׍;NDw~ b>Rn Dlr<͊W7E>@f ӂL498⹜﹈40{Nv|GN~~D$׿v]WA{S t[ |<3?ꨯc0ɑ/fTϰ(13}~bvkQc"뭔-x&lo|nޗ8\*(s~'tϞĢkT4騹kӛ60!X{3" "1ksØ>?:9o'Xky-*I~%Q@c؜@@yl(YQ@Lt}rq ҧ^7vi9nlCs[(!GdC,59Z9W#+tE}SJh:i> /8&rkUUlR_>(FB^> O]|lIJ嵼ɕeT~? L+)z7sTJ)zhZD ˪_/m0QyS':3*!80+OCαwQeʪH2AmJ-a"8]b#Ns])>+,%2~pޚG:LIG(+'E`P23N{=9z{£h,=<,-[yznC$N(*}(睫ȸ6/BЃd>F#z\m0e"Ɔ:N?]I'Yky-S*qwr24^D:1?M}\oγ9$>W7Yz'* 2\v{9Q{(+;y'朇g29`.+9]!ĦzϐmYWwWg p! $EUa䮡?L3my֛|R|o 6J']G5}ڞ|[.^w?|k{>EQa@OT;E,}~盺])p-X#2ߏF`{cCC [sJOqi%=@+e}U9G_Xӑ%vÜsner,dRP^~?/FH8SY>u^ alMn;kŌ7mw&l[ׅs8yXƗ}^"s+{XrN`9zM*ׇCu ?2GF]I@923rTC,1g4cs*/A;Lϴ.TrzZ֯kilS= r)xl\bX*bZZ{9, 16"(K fpy@`__-Ow~ܦW^ց/[z.O q&陇~VfOi }o'@ g5\=ω1䰬8Y朧宏|4G>N}/JA-QJ|Z^k_>頋Ogt?m78 zuy;O+=SQ阛`r!8þ'L7C˳hTVP}폲?9vLp{CHs-7?QbB(_W wz aWVc)$q IDAT/+sn=& IO͙ko"!jiz9g0zr0^8},}[% }UsιFHL}&+FƇ9G$+c+}KO%Q d-嵼O)ageP3/BYhn|fe$ysseUlpz ,!vT!z+F&e_-q4׶򩆡=,3S<+}B! vW}ar.rn,1= [ -:-]1)\-Tf{o:Ls.CqKӗOV}qhr/ xŧqwr}岍t"2.P91i`AM+=)eVeo2a&8[|>^䓹\n/m.# 'k΄99CL+z@SeҜm4}l0y]wsٮ`/]xsn$O} &2NMNMnwqkd!xuiw /Rya.8J~Pr}E\4Dygmf:)NKzQg'Y=p wY=8V>LrC=LtދlHArW΋u]6 5 p_|C$_\>ufE\pkUrR:+זm?;Gatz P+s=xPezX[KMoEIg;p|=K!-O{DMϭkZ֟P/F jOw{;? NXzґap{5.]$(: L,^_VūSJGڥ配 PD׫ w s0(KKbo z%VOᢪv9ѷL(+PW?h~p+aI_Z'hNI;a!Ÿ+{B;/Wd̥ 9 z,¯-JA)sY4=s 8!M_"O*i,YD`W1Ssd+GUsUUU[מ}]/f-n`9e_{-{+CYC΃h鋲!j-Ka:L "s5,.kMʼnO/woZtΟ[]5!0~uCukїz$# c^#'q2l󬳞N2 d0v3zp|4cC眧9?B(Tsk\ &h=2B>ϧ|ާO]+^Oi“4Oܼ5;K=3􀞛q%5vopYPk^ =3erYUzXbSGz}g:ڜ@eY#||GTH9G֚er "x?(x4ڴ\#l{JX k8*Jv'&8=RpדL,Sz9}\VGЄ?fmb9b=Y˚ZS6pXq{\mӞi>}r'Rk'MПx+^C_>N:va7&ctxo<nE=lηvIz*DBB=#m9<6I?j ܏CRB_ xYQ9~K^GXWS?sº>x 6/Xm\Orc8GOHc^I=E_}+5릔s?5]8lO{η֦ ^U>i5+9z{ -Gdn;V=]WS_>t-NAҙ6jψynn֬:1p#w1hNm`ު< ΅G9G4T؏b*㐥W^"IUJkg'0֣Q٨wZ\:%Av k2#ާ`^k3rxPd Y4zd+^ g= wv7x;D'D53Kgp[33+t~crOvY=tzV+9`:I{*hD`GC 0زk<}S!@%_5.p{= yNMF{͛ dE뮵}<4g}<Ç03EU[&[yz+^=AoލyJ8C7%[ 3èr}Ce§99o8^='eGRK<K#ᓌmKޠSS&P{%odwE->)l% 3]|mBJfk|>ʞi'6 }S^]Ԡit;eM<쩉X0WHÓjk+d O]g1`׍S,d7j$esx ^ѣ>[س.juB zl1ОuybVwõ>C/ZҲ\Yx dכS"2>~[r/u-q}{5!4'9s.e&o#Xͥ )*ȔXH3W/qtwj_|82-s~ kxm=)'WJ~Xwy㧜!%@6L#eF[yKw%nԌ{xq/nMU`kaÞm'W{#]RdQKcr&|?xeY6g {W@1^ ^p܈/LTޮ~2JFTJ 1-S=3>#e?d,MNRzDP~|O_V>),U 9aC (Jdهn϶dx?_l,E<b,\"$-^ ׺[lNmD1> r=6qUv3~mNx 5lg9nHMk=m{q.SZƓ&ioqtN:^Nk?Cŋ7zUE2iheHZPգQռl +pvo y=M'N|H[𱞾E^bWqzL?RҞg_[ O{zx^b{;4,R+PuX'}[ h,ƏA`ze4jr|/-dc OW΁~S *_'țx 5cHv5 brOWKڦe . x>>˕=up ElSs?~>Pǻxc롩]] lSEBlW@K2NTփa.װYD /s\+St:#ӭ]Wy/Ī<ڌjgduoJe},^~>DpZ1xm-Ք>x=5/»VǻO_Ypx[dOS ft畽) )^V^~rE7ܫߐ^ڴM 5oXޜLJG{CDh| FoFV>f%"HLr %%݋{Sq3{qz 13.(r&7~.\*W ]|Wr y er?7fhKi/ G/iK]OIυ[*\XKz]!yC~;q[.㹻Yk=ʌ| x^m<+\Hs35 l>ߠ+ύ劸(ӓ1s/iG+ LRTKo%r/LIAvmef{u$=?l^=z˷-AFeu3nx]L;9C>t{T񾣇;{u;6ө}qaWTK"UIǣ[ˎX{n\1iljUpNdJ8}ʜ,˻+%+klz񡬠{roYoo{C`>g=XoKk<[kn |C~2pmM^Kuh13^`4gWRJV+ӜBFCƯWW4@D\w}|NYɝyk+q| :`gbtQ|)|ۺf1L -w,G|WGQa9-{4o<)ړ.۝J*G{5 _M.Tmtɾ?>ms;w;y[ݯ?TlD%/.ƯWFC7@Qn.6G9z[maL_ ZS-|Vn结0iAOi}PC_֍Ay#3PڻDlx8#+`;=C~,X_} 5qw5%&~_aȸo&ev)V/ٗ}GZ |*=ZXK8|*'pe^V( ڭR\tb3!Iz^WOm*}͊(W*#wO]WTuy<&Urxă\&Rݸkr3M<7lBL yTɼg8x`^ah|mHgfݩp="\m17Ut 4קsį873Pj ++^ o"γR_K%$듟m^gm:ʻ5D|'sk&ecF#B*sf^o+; IDAT1M8N0&4GbY.f~#MXO=XG OtH΀/\ʓ\/k㙜;chn@0QRx"Vu )KQRӎM|۳!zڳ]t7~Rߜ\k=ثuyz*qGyDaBγHr? -<2UOiGL_|si3>y]t]*SKy>Mi,on{}SΞbPԖA\Ь6xNm:^m'Mg2R<ֳڥyR>>^ _\'+Uۤ^w磧yw?NYo^? Em~?IY>-pw?s3{ejriϕG< %,^pZNuL0%z(B:3fK[k.&d-/eh[o/93'&yy!㥅ě?xq-# q`b<?&z~4 .Df6xQP.v{ Ƿʳ y%Y_0Q |}{eY?{ o|I o9Md,Iʱ|א(uN|Ƈ湊k)4^|wƣYSܖiޑ¡$wt}ң1OMdɢٳg 7bg\^^tt_)o;Kȕ/zrC#8#ocq\7=5^xJϏJ Eawo2NOYPamQފo|Sp㿗f a3a=%} zFPjiwG]ӃCYn9+$m4eO#×x AT?9 fhDX$/!=vJ @`X? 0 DZ?0WfO֯㺘J?Kί~=ҟۆ)ň> |`{W͊ž Ls=+,^G[gIţ&SH$o\@'mbwR[=6³>/`y-ɘ^eiUac=*x iT-=$zxOMm= g*KvcvHsdyK+n`0ǃ&/Iz+gՖ5^qlv9SUwգY]OrR+־nΑ磦/ڱh'y]ɟ-2gvY Kg].ޘv-_/]+boy¯"&rW8[,bZ&eY, .//k|x%۰ E$guq `_??g 7(v&?m9:D?\oAW6>na]: >$8Vێ{::ヮOvyhsE{ċe ؉Ǎӿzz@GΪ迴|p:;v5~n6>m5gro~sx3x浉'D̍Iuy|>g==Gtg}hD1 ʩµeO7x*ڥR:qOSZMKK8w-G5Jdf0yÕ #.xղHDѲ~5^zp::ٞ3nMu @YtXz܆!~Gڵ]?l4K>pz2GdXBv}?pkш;\Bt:%'ۦi w\~UEM0D`= 21vnCƌ<pr@ulLI8Q}x CQPޗo׃OsW̫%O3ϰ*T7*]`[Rh#oԣTI.ȋ3e]ap.s4U7brc&Xo!BOөxx^,ny9z*gVO2C3ou+vYOB|]u_^^=岯\/ʅ,og׌tmw7237<~Af]𐯬v3qxf4!xt F{ƷӠ*ȋBm4nz%knhk<M<*}I]dutF{y!:}'v+86n Ӂ_iO=*"3WLXw2|~;wY~N=ㅱ>x[zPxHz2@S|2~__JG *\_u;˗/1MSXl#^gϞWe1?}_MeXoMaA`bXK0F:8B#2^_7%>^yx* ƞgWvm" /eh}G\#gSG=둟rU|'񡌍zL=^Ofnc=J ppWU跹Ɓ8D^{ģœ\m"qv:x0*xkWLNinz,ԏ[2ʊ6w?-9#)H3(!}Y|/&[N`qJk!׿ ;N{nHpZ~SHq^mW4qi}Z9~.tr<7=#%@`4 ^D|ȯ~e*uz'x܇B}1?-Q;\3.i_)^N Y~e[ƇNaDb%|C/-M\ܷ4UNM]xJ}A;x q#K;rƔ;\H%=4u!?rE.euO믿Nex`;.bk1Ej߸գ93ޡxKNه=FDSr>3|)߁ox֯מߛ#_tn+NQXߌ]P'@ٓ9g )=ZC'݁`u.VN]^QÓA Q^{oNGZGq˙9k%p<+o!?9Y{ED^r)rz|M4M|1+\^ͷ 睷YzP~a`ON%x:t_ey> #s6^We<7kxi?GK/6;/h_7W*~hࣜ[x[V%-QLbƩe_.MyB7G4E<#9 e3x?uge1zrکjcm=Zu( uy'i#^ + *DŽ kT<~|_~]^c,6\ʜv<S9\[|ˉ<_j: G'35n_I~ivcJ'x]zv+Sm#*zj3#r~V+II\w:%${…b|//~;*_$hj="~#VC>B=R^?9?GxA!C~2rr#Qo+xío@迣> Ӑ|$&3'Xk]&W7oC"w%f }k')VlO=[ZnHS[+s,(lp3`hO(u/x畮%+ɭzoH==h\moEO{G<41Hd2TvDFSK{G="wx҇|)s~Mb ɬs6Ƚ g#wpge3 #C!˳P]8oM^X׵93=vKx_*գ],= J@ VGf>"Imvףt@AzG^iNOOumAn@($x3M;tD(o>S{|W=o'=lx ro>Q z&13z; *T>a N623&"N3 j0yɌq.C9J#9c1ukx->Hũ \de%Jx3ɾB|y&44RE|˂s{[o40X>oC܎Q> }Wʛt5.c^f͋xJٮ+ڝP{$0Xr{Usx}O b wܖY{{ӟq˩g/<dLX |VANO~m|6l!;j%J\[vKkO*6^ α7c:h_jxoOY&88o8  0C_n6o0+n0OQOߐ';:2'1"^f.DY(mcB!S+x`ztWŧ,USʼ'2ni}YDf^KNp&>G['q}~[I]1{op&AgoIO&X^+&ENMQٛ*xm .78^(iYpz:-{/!`.xiU‡>S+v{xԱj/Q\(r/C~r2oHW1pvM!@00 0~OןC_޷0/z0ϟBDdW3 9~|^y[ǻ'ȂJ\C&rg/s6&yOʨ:GmO/J3n̔jvS4r YUgw }xj@AR۠Lk h,.Ɨڵ&-Den2RV'3Sx9'7q58x-ZxrӺg\ͪ⣮c: r(lit3]6AJ45Nqђ/{4-8R# sևW-wa8&w7ni pFC@'`k&EI ,L?\/&)~ޖ=2`SUY+@8>j\yɦǛiJ._mEgPgs88Kr0)ޞߩ<NI7-*T=%qd]>>mx~.Ƹ#'D&Ş'Cy0|ܵGO8sߥ^c{>~Gʾx>N5DgZwm| m1sfv \[:騪6yM yMAјG;vL0Z7J-No.3c$/®>eɎߺ,pE}m+oϓHǙqރ6M#+<%|OvOQrO-9;"7fºEN*Q~(7 0YV nK )ދɟ.mYI"A7 J?i˹r:mo^Bfx_'c cB%ܮk=W/źʼ@x/sŻz}5zp:T{Cw7af_w^Z z~ƕz|ӛޖwqZ=ezdJ]]/{Bu47cSs?2 aŢX3޹(&Dn᥾gkQ$cfXz]["hKǛ)*#[oճǭY&'z $IE59=lG_ͪw`OUN; °"L$ 0V1 /On-8;A7O)r3;1=W§iqxmɶ7+plx^XenIa * ]<'ފ HڽzpAO؇O# r{&}nI1%c]?t_:r' '2zOO--<OdHLzY|tkA n[ x<=TS[u=Jxߖ2I}Dk}+g<+ʟfyU?Ob-;N s'pv"y+_C>Ws|hGxi cE8ZID9:q'+`Uq|295n&6RAvX0)DZ;F16}/=?۾)Q. TJ.h,Ʒtt2w}Zp\a*yj2Co[9x?%p Jj4)Yң%LV5V mӃQzƐILK=s d.=)Jn{0 k]$Ivqp3Rcv =^|׼7| [ՃQãG)=p:hCTsk[o(=e?wJG=M>ķG-s.uս٠hvOLxTq*S-=ڼ;zV@uŵG^ޣl)rюG6H9gw;{f:L?r=/oHycOUPMd\N?DrqM +`(5G_|mɓ5e@y!X{h':\Zвc&>Oc>@QX(Ojcq^&4`}= ybTz@"TqxԟԦǴ q]<NACA}j׺Ko=EH ?Ha,7ay=nxc>a[@85St.ϫ ]=d !(yUaN+E G-Ưm&:ݤ=u#ϝ 厧4.<^,MY 27{,z6y)Ykx_G+&LY Jf.m=\6fʶYZY X]HpYH90M3FPZȱM_zrx盷[:/d'N2s eI]9FyŸuB:!ߢo#w`Y"#Ngbvsw xz|Z|b K8m#Vw!58缧n5y/Tgw|H/׶92i 7I!Сpwut?-Xype<6M>IU mezr& 6PDlO2F=b|e{kujDw\{dwlЪ!KsL, ?jJA@]'.0lL$Tp$:Y?^/#$W2pUz0~ !5$'L _x;xH5QW9دG+=E|.AYH#Q)G st3֪=R`y>@Hu<ź=1&$m)\iz`^A@hVzgk 4ֆvq3j9Tr0#oϿӷ|pVg@- p)]_"E }A? O*rsyUV3[s)=i^9LWayaw=8.gd߹qR,Ɣ2ޘx*}(܈;!YiϸvטttG?k SmyL ז >\ٖ`屄Ne/^:X"{*?%XLx&_`y~R|k5du2qwYjWbDMb{]R( e.0f.?/NG$匟~w)ϸ2o2M ʓ_;G7_u?K{?tmyǙ%)q73abD\#*fZx<\[/L֜|œ=7,/M<=ri Oe޶gXz6(N6x OY_v-7[O[b6z6$笼}}-+;uO4dzng/tV{0MS}=7k4!o@f% >^:ٟ5_ gPQ!a|y4w񑩂g\ނɗaS<>6漃jȔSny|$Pt?hxƟlؒkjڠ_d H֮Һݳb#'FEW~0sPb,pڐjخ]L Nrv ׭z\u?3@)?rr> Ďߢn{̣/>~0ז\v8ѣeauQtz.M< s8@xo̤- w1ɀe<[2(N=oͤ!9dN+w:}"Ӡ]7_|wF_BZ+LJ/M%(Do]GuS|3fJƙ{*}cH+G# {LTAή,suRC%iv{s8H?yºZoH"䏾:޾Dor:\V8M~#!+%Yd*l)_d;%=NϺ;}jo.tI=h5{&r (9]Ia^| pAȶ!2= -*I _~}>47st2q\K1XNLv]`_=hx ~_{JOsoVy/v lI҈R+(쟯ylbf{4Wt5}XIɤ4Kڒ1my?7Kl->]pކ~#8ͮlV<{Íe+Yq"{ҡxbc@>ckPz [缀s,v̇o86}PxÛU:b=CG\ xej̸|䴑4и4Dl-~ay[d7>I=L6x[hyZt]LkP|r9E.`QA=z|i&{ʳ>r8ߎ_ ?y57j5%ܗ5~6@7%Udy)P_(1e{44㏟p#lJ2[x9oNMN:~1 2p_Ny]N<5]V@ƜKvVg:⽯U}wq9N1?70hРA76񡔙g\six??I ؋22#nYNqk*^={IEےʸ=[^}.JKј) g\O~EN' ClOKt84ïv.ZXk~`^ Y(eϷ3o-Vu '1)%(>xV_vd n; {[ =DּYҞ ^u#.p'®1o|РA*}% :78 R'e$uUdBߴ>ZmV_*bDMB?ܒGYO<uc{f货Y>pj7oEtK;': ,MnԒ7l|yr?f=im_ ^mbjsb_N'VC[g" ۷ymӤ{} yCC??G|fw (/<@L0,nyK#HR!< IDATL, oLoS"îMyXzGυ{%O+mj^~k'yxUNfaBU]W)(QmZS=sNw]kp77*P贬Ng46DG? ;?~((?C* yz /d4<*ȸ2Oq񬫙U0̡l}dVSr _N rR/ҳסCo7)/L(%)o?i]?.>m&RC3Q: lvhJ(xoFS"OʓZKG?_shN'CP#2m/ MVOyR+ Wt+C-(YY l>0&Ӹ|]CNC/4["{õmSg]zY|f%?|X?DG)?/n!ixt_7O%qt /F5^ߧ3V? 'kn34W"LJJRwUj R?i=۲(rr'z(9構N.[T>G i\±irSQ["yy\}vaO3jx}=TZ}ū]OjgjE7-[H˩J"/C6eͫ}y?Ŷ:p:{*qj|PKyu/>d8γV"{}8<_pve;Mΰ5]7}Y$M{l]]3\oW_!B!DomEQm{By?h7w<~5ŧyR PJexo}4n&w:[Ѳ+RY-#nhژawg]-b-peW,rTy==ol48,hө?_>.a͑pdGrˢ<%.SO?o i*pE|O'4wWgzOӔN,_i#o1n؜^҃XIY+d74NSqsO8)Wind kkN""}ڴ}2?S+45<;%MRORjktÙu @F7hz=%:lԹ|"fg8*Oԣn>G?yly!sS.fw)zd!-f۟7]7R*ӨH@ʸ t @RJ^[1[ޭG[eQ:%G Exץ'{=マ +U6-[]Kۋ~~Iބ-?-W:iάlJx )'faG%[w#M^O'l+ÖF%UG/+z|DPZN _[C">GAg{^^Q\ՇwցKCx!~:[/~&St:먅;``ܜՆbѩ~_v wĐz|dwjwmzwO9î.xI6jPg$6ndBɴs]^7kbwI=hQoU~\? u2&ɢNtnw{|48)8NB&sdۖYzm'=%]${K%@ǚڟ5G;zޢxs^;}ǚs^/i+A(7*T/>ۻƃnƞA["RoɓX=uˍ }Tğ&f{D&__h35s 270kx.Oxپv]6m?.uW/c/N~߸/\?֦t)>G>yg.Oٹ&q|:D>E]:Q64mt?-|.(]WL ɟ)rg&Dg1T.pHCbdSr2E^iZf "nDT ^N)vC&5w Z7i-\vwD^]1o$#w~}ƻwZzq;b#JDv`/(}mK;ZNIN歡9;^i5M^o$-$pRov1OBOXG_&]1`rB{xGõiU15GDZf0&у eb=fӔ3G*l|H?-{Ή1ߤT،.S 2 `ni}7I p3A\VnCrcI__2\+m0GWk_+[WitXOpmB_ fcS~c\_ݮz(=psb zĜ;5.gS`g< us^קh?XT!,c 푮 Ƙ!,,F``vztȀv$3<Qe:Әoq(;+:f7Mm>u8^GGwK}br-ĥ Uiݐc@_,YFBHM95<&̥Lb~l]}}"I8:Wb΍.G+\ϻ˘I`b=06:}?񳐶MCփԇx4B,]Nq2Tn:_z..԰|A.RJWj˳-+;LkBlKrOi4A]s􅊪9Qѧ m}٬]Ճ1Mǫ_* ]x~~{V0`$Yǔ?<}/kCɲ9+\zsn T9o7w2xs^?4%S{{Kp{!x#tw..YN^3&Ϟ<~ ()r )U>$=GY0˞qس,Ʈ/A|Ucgi5=,͢:dq#[R,ZU?[f궕&[w\4}zYx [^i5)&)Ge{t%O=:\CViޙPb )yLPuD+p% @X^a"1|*RabiB:TS惸u)I7aHh NYve8 CwL>rl%lI.y2Cj>_艭U-WCd,Ezp1%u(hC^A$[ocIU}:@)(7}Q> YWXŒW96Op:!ϖ鑾bSM 1|< 1r kY {/6^D@GT/^-}ĜA$cγ5Thw{e }ҽy,obD^aXz%Ov~ROe=3~EznK$TNiEZbظ J OvY1MUz_V>+p쎱 x`/2o Aw4Ggo};|@_vBJ4)U DbΉbl,s &'0mUە~U}ؿeۦX 'YG8}ɺ>5͸_55v gꁳՠyQ= YxM`A~\&6^iiM$:R !XXϯ@j Q}kiekiMcLc8R,:=ZC)%#6>ch%o-IM\-L|^ G#pXc>땉Ö3HWs"F=`'Q* 98ơX]し#\ :Bemܧbшex:u1.ޫ]'55~fx;vn1n pPf.g,_3W̨lgۙBN*&7- 1yz-L_:b'֒>U7cvZY2s1ez$4_CwM0 ,>x'9%cX~3~x{nbc>3I1=.6YiM y#-2~-yhZe\HR}|9!>"Q҇ ]2=8{Ȫ=2ҧןgnч@U3IR5E]J5:A^=]+i+xQWw͌)a<{fJϰi͓Kya<\gfLu]i-@!;&M}Yʖc؝N]LX2RMh#WjWR R5|lגl.^nzn^?z}E p7>U~찤O1~hJ.w`@m|b R{ \bfyU;f+`1F=U}(ܾ,ֻ8GUuɬXoNzkGϫJ0MGWgz+ ' Ji&V+^` sig},PQw5lJyKiEZ,qE%LTק=;;>T6#O>;B}XR o/@2WOG71`|~㨿2vk'ҧ̍qJxge̹X=H1z眻cJQ:bwU}R"@fcvUb0cZ  1h}![#^K}tfbI5~&1%d8=h^5z]KY\RzEe[," JpxұZ2[(K,jώM6?w;B=zYw,aLTaiq`-/=K,Qr3ϣFIc1-xvYs^tW2xF2;,U>l7sQ\Jux)%ΐm6bL΃< z[K#z/:5Vx(I?G?zIySQ;Wb9=My 7nGs,ZCsy v59zbk%N(no0fsJ Am*u.9 )GvY'N c7vTؕ42'VR؝H*%f=- |F|zg~ˇ66K+ 51,=R64~wPKu9~4 ,=T=@d6{Ѓݹb g9B~[݃TjGw;L[3_Vnc7Ż1HH8m3;dT/,mtq}.wsk _OzRKO{s_Kӓ<ʕdN*Л" pAH? ۔nmx)rp>X9)O qճ|(˾l/ Հ͸'Jf~c˴1Әo-<3G/-(EY\F IDAT\̷c 2PEkRd]PդE&曰Nxx S81UW>loګ.ͻNzZ i+@*0 *\݄$^ne_>Q9wHus^U?YL_(e'Ezw,ŧ5 %}fUG9OUJ;wR? Y1P%~'u9ziqfii;{Lbe4Uvy)VӪJ܊.gdCءQS٘oAHbElU]^nN38WUT7u>pk|Yu,߿Zz-wPune&x`u⮋fH_GT/n[,^Gԙ-⬋Dy1t Db+0 X>ҘBaWO/?}w"޵c2~XvO~<# Cq ۉ5JJORYkk3ʘRZ]4U(.-z;n dcVZڢoG•1٧fi'KPo˺:^Lg7CF#Mu<߿Z\C&Eݛ1wŜ 9Ĝ]l5}LI̹4Og<> Hv_V-}te_È}kӑXi5x٘S'ۯTL۾fZŖNٹ$)hen fx3 7&!(|mw>5gȖ2;oVлmQ>;1k˘oR+{g^z1QJC*j fBχ .~O"?c&m_o#|cNpؒǘIo-lkt> ɇȩ%IXn10yRz]BcsXQWHRX}EOLAjqnǜXJ}@Vwb!=w ">|ebf<0X^{67ʗv*{NWz:Dw XE!SrBHk]Üöy<%>֩`嘫rcY]͗?wnvǚq#C3 a,SVƳqg xaL/G7zg#;Iw5Wmg>[vo"fd11.q[r̻ьV+/AB̷GC`dbƚr0B}gn96g˝#kjr!LZ26gﶻs#=#=9W|}߷ND?S_~g[Gs42[xj_ʬ:CL`@>py|^+sѮ `;\}p nhqgp㷦>(ĜD#n*+Givp0_]y U#R(94c3;.$л.̪r@gm[ƘN*U4cr}EaLp^ѾdO=x#}0|ONe7GkW^YM9YȐ`_woy]V̷5>zF` jˑ ժs= KgNvR *=r7}!N?G~=/n<ƥip?S+Fx^_ =fkS$t1 ̶/sb+?|2wer㮢L9o[t6p{Es]3v/zfIk =y=q1%˜_>8C?yUϩ\XůA 8-y`M|,]eAE h_^8"Qiڪ==d=8ºӾ<˦$X>-y1-D@H4G{׸ͣiRP&ozZ%}1PDۂye,qю]$YRei=y,S=rH$ cFݣ "P3nƏm}ڹY78< eh=WCTH߹yۡaL~/rrxWoe6qsow5qe+"Qnxe+Yi3|ݫ$Ywmy.xWľ6U91)w[[S*&;GrlM̷'J͸N}DS1)π&u:c> ͓N,ǘ !VbƐǐ7G~^)5\~XcWLnХZ9Lftf$mVe-+d=BG1p #ư{4mǓ;WWAhĜۦ-`[&g]`p.]|~g/?{ڽys);axcy%GWӧ qgqj5$ZESz@|bBORkBBz:}B͌Lu}dԸׇ;tMZ5c-FJ<#;lxLH5]ڷ+c-T#=#=k6WF>|w=?d0v'E6ΛZRe>f*Ӟ!1IE<k (cbS s>sq> ]]iT_ۅg Wm~_<șKX$g uL0"PHRcѴ.:x o꤬> Zֈ,sX;Khγ|j5#=#Ly8Ċ!anZm:W،3${Y{z3>-y=(ĜW9 4꾒\y jlZ?}g2^_<5 3>G@k^6|J oZe҉%vg^#vO!eel>qqEs1!GZ ϥnCPY(DuW,?\Nj|qFzGzK監~]S!|IJؿ)yoaχ=--|7 -ғ'cB˳Pi>g*R{d c,t*1Ч“C?M0g0O 9Q7N`=0MgyO.E vpoT5+a;@Tz,|7bΡՁՉs[13<.ۺBEP~P_}yy>~S3 gj5_5=[cs,)k󉬛S(e c=+HO}15zpkb 믳4l}UiH ۘoUGTsRҏ[]1p֫U/;︇H#=#=&ՓZlOe{D9I!W re ̾($U{Rvh\3F\YE4nj/^қ>Tu&WcB15zuŜNW'-؏8KC>&Wd|_~ٴ-]Xqh-]"͙"J^y"U]Be>V N+􀵦Gu ?f+-w<-L\yYinH'VK>qy9)mr%$&,`v>܄hwGzGqAc~W5IGlᮮ.l]dۍ\Fu>\E8찞1.ZcN듓C ՍCx}Դ*ҾjZgb1Nmp*xp̲]1>'p_ˡfyƖjlxD܎reč<>6@o% P|['#Ѯ'xæuާsu[HIJgE2XU>&~~ [gWe1@=͞"<b`{79X_M\7r%&vI|Jo +*bsn 1)2?gMHjy(`.Ӄm<6rH-2 ?wV|y:s~jxn\\>.|9w_ZMnw~ٰq?o}oaGz_nn'-cO IcLb^0N2Pꫤ_t/n'`S]wIqr2e]+ (s.5c BzMݮ>n:sukcckp{P) )" d0<B'VGo" .W>c?ez֦syzƂ/-+dxMbSLHbsgyjZ)tݢ$Yxi,(|wEdWQ-#>fU ũJ΃[ym6Gȿ<;.?K^ĊNf 7y?ĞhTmX<<&BTț`rDFR/dcΣIV q}N_> $JKCIOz-6aICƋ_<.r.|ϖ_)z-߇'?o[oI+LHAdPd629ӲU 0YwAns"щL hݐLǴe ;%0kq] Ѭ,t]JaMO/aUxa@ŋϩu6Ġn7W{snc:` ]: VjR`j?jIg22@Mu.n`U %8UծWge; 2 ڎoV ݬ˴k2pGo^h=?D 6&ů g|VAV7JzA+R,yW+dFS2^,L"m&y=֫U@NG5Yx_;|G"ʼ*j޻tf+ Y>Le#INyyHx,Vg 8DcR}Q`1^xs%JxD^Z}D Vʎjx'<_&TX>81%^Fx !$r6dA4I}G7WO~Qش ;3_ 6FV=ohFJj"=1Ck "We+0L篹\B#ZXwמA>D~YM`JAROE?s<)V^mw[1Ko=Av1xmqK9WgtxnT@"X8 88p sۢKIbO5rt|hEY]PLLjU{R48o%z _&0/K)js]XJl+[5 U$"{/6Q}I=;(Nvl8x:+gu8fߨ*"b|Tېf{s@)k)~Vr[RthZc 0s@B8y29{U<㩝2iТ=,ۤdi K!'.w厱l'DM+dr _<Y>{Xa'Z%r|/J>Wz48| GUc至|1\k;MLӺH&-1ǞcJ1ViɃ<{ ,.SD>q[!Ri}31A [COjK؈>#˞-g4+Qn/[x\33?eޒc\f)3ѽRS5O dyLRc|v\ D:=snzݮ%Cn^$s@gHls 碿`!wc^W(,Yz<xņVֹ?ráXW^_҃;y*\l8e||Ϫ{Kq 76^zGlyUnbe$1n׬sy'ɶ=q]ݝ>QoxWH9^KGWᎥ!{GޮM%>O,v6IDXM+k 'B||8(ZZ:}w^V(״ZUY⛤}zKJ; 1,v5o{KY=1d'5;YF)ﱧX[9x+[9izR9t3%Wt':(ާcS +YrJJyV~}#U *}ЯՍבEWSH4.}8yɱupq g]{xG='[L`Lń>2S rRvEUvP4?ε%y!.txR^\nѷ?[&uߛkf;=0:7 19zVɓ19~S+49/f}&onܮa\0L} ?<ks^ӯE2>ric$F̎-Vl}8L>>٩Źp7,tCtHaxƗp ՀC7btl^CRxo]Twhzgڨt7\}] Q.yXϐ@x3^ ,jاMR:!: cKwwIw܊u#R;}9Mxōw.p+ӚәK+t 'E~oScb%yD˲EzIȵ&1s3i.TғKxŭqN<%;s+SȅT.w=VK]; n]-O>ֻZ_vhEPM⸍!xeQ0fyXb[#DNr3N(g<|`n'Jt8@&%z|+' fD4Ђc=~%3ʋrRkϞ=~ů>NWڀ3?^8뗠ƿksgmIXPvKc󝡓XfUg%cr>.6H%>~p&3¤T>d;,T]/uS:>~z(7vgӂ;^o eszvV%I{{kۓ#˿-}p_*Y$VD+^TϹ3Ocm@=]|uO;V7%*@^YՅ~sS/duKN7p'beR>(eQ P1>R^RZ-vW**ӺV_o͛oQb)]+=fA&n,eK)3e%`=+t;%]K:Rϸ1d`X#^6>+1U=L/wy \]7%l|?5=Dh7!#K(l,u>.ېS 8&"+<*ER֣_q%E+1P;Za™Y:I[ Œ#Uyxۖ.^;8e0vP\JRh+.sjr-9~myJ4[sCp%ѱ!sNq>D_sbέ \<Ҭg5<|JNvgzzW~77N[nR[RֳU[:W|7@? L!PXcH ᔔVSNU5_xCn٘ڍ> qzqty;4jPӏ;;s)_{ho_A#ཏe{ozA ֍?(Ljͮx!'1{#yUb;1zviY%>.$ d<ڌ*5#=R(|7 bKIqŴ;+=xW_Cŋs}t.g'$5,c:Mϥ:K'~->rKoVnDX^U>NcqF\f'swT#暉˯G>v /ZWXoVqCǪ;_ڥB}p=#_X8~Lk6I&4ꩄU/p[uFy=gs .(aV < &V{S1-e7gvLEn<|jJnL>C/~s١ 79|`%:-ns㨴l<`G?؞qYZ?:Gi`3Ywi=p灻KLf}[hKӨKM.mj15yts M^wK<%{^#_L7eL_l0zb4A5?>,JGb6N?tni" WLM7.Z}@n #`'P,SH߹Dݝ/έ$k Wy2~,WxK\~ww>fGZ~60bޘzK#tU&ݛ#?;3K4M~GıaU񻜺|X .MuwVeG<߬=)}[-d{{/7,=)nǘ K,<8δƟÕ?;\ϝ]Ts/_B zm!)NQ_X18cտ3 1$~;:چjևؓf9}1Ɗ%39yؕ3ã{nZ>Io;:! ڴ.;vZ K ǩ%9 Iٸ,IXLܘP lduYL@yuHEz+(+/2*'c?R]Ag 3\F|+z^S0#sON_ 9H?ivMM~ƛ]{8|$,Kħ/v(m'皛c!k&s21獶jƜ0 :F!mO}ٸzx_>Vp=㧁|e:I?9{TA"[Z!7Mk@ z"W1k[[e1 slynGE;x61:=Gf7Gؾm1NMjZEϯåCWesiaxKҭkNOrVy8o]=OgiSʰn{KAaST۫ͬذˤ}G0p2^X@,F ,xAΎ:w?weusQxN o-SArW<~R)̱!XZXlbĖeǣM{kzbCl}M?}2yyo]ݵe}NU#3ɿ6>c3mL͓t~KT߹FiP&%yeɃV?0xJZb'\ `&Ċ7+bLd%8o,"dЯ\N9F\kĻfLޢ?ɼa^;ŵRo}9@}~2|$&LԫfejG\iwo=\)o7Rfbz&nג@Ω fgҶ=MEä^mzx#l+VVWOx#Jq̒Q{XL|LM/$#6т<6TL)ĜNǹˠ,wTb|͋CZ |j%ˀ=u_/WVzÞBO?|3yk&hpH1QoLai ]%6;D%`;u+uPU>s$Hy 0oV7Qm>2 ruoج3D6}}W|vΗq᭽cM ?ߏ?jc1[xr΃»AVIVEvcXCf˻@~W " o'룄F(°ݚ?{x^GIWy]O]>ɵ޲])]VĂiwnv qLJ :&-eƟ;'?NjJ/mfӹ =I}_\(?#=#}{_1J߸~ )qz 4u,c+x;tRD٧_)\nV'H>F<&i~cJZ lCkДSFSJ RO/^?ļvs{ˤy{jN[N*E~B7vSUn1)Fųd%vpǻl2%OjLH{?}_n=?V&O9`4;^\>V&_&kѿK~jjKskY<_Pe> gv?2MbVŻ5p1'2dy.ǿVa&㭉I&$:wL p=n},A7'p_0 kaxm3Z]gLJL$V~RK3n0H)]}hcc4m'%'N8@ -crUW6¨":a Хn>ئ | ޘ?fnL-v;=#=#G 1ׇj?,f=B:39˳>Ǐqa>+ucK~n5y"}'uzpF v(1$ƻ^O nևj_Օ?9[R!{kJxØaݗE(ܟ1!g=_X*aN͎ 6ڦ|J#+ Mz=&ZZ%V?*8Yu񲬂~:6_}~:wi﨟{ ~CJޗ.d*!Jm6H'x8>scLhFJ?\UQUS*^vmߞmke]I)EotRiO`u5 Z,}8䨄ikJ^g!+rX>(Yw;̬AP_xmέ~nC _er ͗j0 w4;_{* Knk= b_3]e\q t >ܮ_+^[O얧&342t YSrRG]:NzOdp̞Ѹ~,Ug ?ǗzƟg#@zN/yyoRhX~:@[?}MէIaD%G2 ݊6x`\}#կ[KO=gCʤ<6vGLb1ˇE511ulCGcUÝq-߹T=|L eJw{dGD ,h҇\$EL}t4uyr;ҧ`蜪d=grl3XYmmڿ/{ wp:yGM>}y׫`fGHJ4ge,u_Kϻ#1ye=5~aNZ?ɇ;qu_|zM^)36bєG:'x:3eˍY"8f?%{,Q/B+P4'IH`L< ;Ctױ\?o C_&/v|~?_?xW8^S%$c~\CV)ropl|,s8pwa/2Uz]vܺo_Pzw~n6HӴIMRz̲+H@&mE=v;fcy(u\gsV,c M IDATcV2<~L~.I\iB(CDd+|.D .W&}976"Fȿw?{{L!6k&Ʃ?z޹Wgf ޫi P?{??= C{M_/"JS?Eh?@PJ%wAϦs??|7/zaX ~f%cwKrk闦'Nx-k]롩mF\~s"Z9O?E lJDi4 GsbAGCZܗHn"bCM+{R\ k\?*觃ѐ-I6sQ왉},c7媽nrp@gkiDҗ47q 񭵋s$?FG_qt!Wӄk6XtH<P>xs*?HƊ;j᷊y1,K+EĔ?\68/d\zzn7 7nܞxݵ-t9ϸ@0g{RHU) ,9z{8ɖKT ȝM\uW^1We~kWW'_h^~4<ջ(%g7{c4Wy o?~"&ܸ!ǬmP{l!l~gK?]+"Ϗi*~\y~SߨI/t~xHY'~*O/Iwgkg`ƝStbSx/DmUeO{6@\5uѵx_%җg=? AHd]1("̶|J̀u͡4.O Zybb& ;C&`ǜWc.qԙUOR*c j1[E} ت~z6ߔMAKNoج ^fPu.S>zy o?j⻏0r`mx yF25,c2%;^;S|$|v~xu]~Yn٫md,K۫^h?/[]{l7_R>Zbq]f 'Ryq?J;bO'W?zGJ5rGOMyPg<7Cn}@1.m'_l_cDݫAʿpv,_n?65K1yԪJcʑY*wܥ>->=;|erU#_O2j/ꪧy4Fd&>W6u/%7/~[Gok%q1$q7i7L1]gxϝ8WܥjSQ}+O:8ez0Ty6'`6w$5- Z {%}p=|JRM>zՙt:xԯG./[Cսl=8{{Hn{S,6pt"}Mſ_g7=,Щ03ӛ_y@.MU> /S]k|+`gc&"3XzõH[cznL̙kk=gF4?)\d=mŎ~O_< XKVN'պjd=@Z] [>,A1cwx{<чs펕9ܫz6}hy^O& ߧ6E]3]¤,>OoE>F'{Zى}{y+33¶T㝑{<2>|x¶NQg]. s%~폶?WbT>ol=:Yو>t;(5dz󲷀mľQ_ޒkzuFE}+є ɪ=}uK#~ՍXj ?=[/z/DR\ka7q]WZ{gǚ@O?ܖ&˚xrd |(:.@;vr5y\"'ЉPH},i|Դ GwVMGcW^IqinW:y!%}ӛ_~?|k}GޏQw<4Mlֈp?*GJIog m}Ù~~wC>^vY>ڬ5N)S?wc[;_I~C?nrh){QM_|}R8= x_ֵ2+S7uQUׅHћo=xsSXxH&އ첨dAP֢wfˢ=~d.^#T{]q1zJs<2GWf@zg_l΃ޒ~؈Nܿ4B_lp [wov:Iƍ'ZxZٹ`fee/ӻ0P(PPW0G wG}C~=^qx :&[v; ڳcT^Z4/W6Qǵ}NH"Xyzz,#53ւ{FfnsIPSe?Gwp}w,'o3 G>WzZMyQHJ^=_.}Z؟]OgJ\ Q:x3kv>3=&Op}0]W~,s}x D>&n.O??`a41 W z<=CzƧ;{ueV8?Nh>}a!w/{lՌo]WHM|oG/sQkt1kwfs-ӗyݝo]\B>= ;= W߂||s*5 (ɳW058G^12=Zi..?$<3]k/UWkoh#I`"e.Uz5 ={ w Z|1Wph8Tps)>Q?ǫG/SY_߳/La ]/?x%U>Y?Gl?7n`5c% kfJ҃]o#wJ86oy~?۽Z*REqW?DjZ]rpqs`7H>~)gW/_:]9`>E/DPn= 2ֵ4~)rN;i?"Ti0h9 j{jmK/МN=?{Sϑ(ӡ\yTJ9[^q/tiC:Ã턻݆Zlv^#=黏?7o\>Jzn%n7Z'fӶÜ}>XqǫXuIeɵ~nbwgdlq ',%xL!'鵓NOr-k䗪/tDj2>8\5Lxg͚mI/j} C21(;H`Gқ';)`ʶM" F7@vhġи+3UYY9gskEʡ\UV\kpٖnnƱ"(IH\ޞ*2OЛ y2>3na^G珑kg!ҼѐK `/l=}|?ӊ`noyO+Gk~T DsR'I\n)<%G$1<ތ&o h#xJQ |/{x%)ױo)(wUW?Տμk1f?Li B4#hAiqF:ZvVF1NKh'lWG6;MMNOƤ]m7kO~PF/Y9j',4ij㉧hhyXOҜC--Նgc`x:~:8|Ę2'2Go jl-`Q1(Xk&EƩ nDK#ɜxL)|v~t;oQ7TkRMk㫋!9U$rp"SoObcb;/24ÖX8q bX h)8})58;a{yo48y$jmye y][4N㍱)7[j_WzC|):҇܅ )Ί}β~׬<ocP>]/iTB͖[<| o>?sʷM{FK] /Ӷ!NJ 6i<󧳜ģ2 {z+d'x(bR6'^;c`4i#CuP-:'rWNz ʴiRe8Vjjj[k:rclَ`G?xFX1m3 8#cj\΀2ND'3d=%GÜUVQ)Ƽ$$♞o 9+3nj!F;GwTx1-/R>!ތ"G '0@f[BM~˺p^>[WZ(O4x+-]=| ~1ۦ>~?B;;no_XYM! iQcc7QҒz#7ik;zhRd B$$ڎK)2q\Ɣ5V#͓ jVJtxF OWEOT_:mip<[{'x ّkyg~僗|f;}uZ꥔翂W׾["yc;SЋn$yS "~͒xRǫh=Λy3Px8zK(SP'yc? 6#O7J/xճg }J^ zo& ĜTIִ5ٷ ml`x(MoNO 牑'Wy>6 6㼁xG/xsl>pSP?ӦipyyWG!tL\ڑ.=鏯΁-e~0s4jc>N\ʨWR [|Sq G Lx t`-*8Jwy|") _SVgCͶkaP&s-!;N(Em''?Kߪe7ޕzQmopio)LhLbc=oSJ ׏8N}n?rlNFo6Dj<} Bu>NM<`~3JG#V ΀ە^^ qkSjך6δ{N'Ri! wxCoYx=l? &ms~A{}y(͘7qi7O; giGv|G|EBnR|JY7ܥC,h)ϔcmBun9+c6bYNۿ@O/g>{kڽxBNPP¡bqh;+s IDAT:bexѥ\|'CtokW״(L_o`W c{- FE4 ]qҚk2X.{#?*Vw::S)HXI- է"O|5 M- o |d"D96[M>[_2>;Wh|z˛-<w|_yױο 3j÷d/B:O4om~Y~AAd[>x7} /W1W[کmF{PPx|=n㺥m\31CJ^ ??M=>)Io%A4!zNI6;e>6>  }XO݌%ԲfM9?W9DZ%iliI&Ä^DxMhߔ6{ ;@CCT p O>y)Ꭶ_k΁m[{$57k 3tS}xeш7쬭2ıSEa-vRx>~7N__Yev*[ZJm>CRecJ>TFi'M3^&$_vY>q0 WJh|%'1~`kNmQ_F; );lBwP5Bvи-DwH[Jڱ z>Jjk_/]\/>x~Pχ/nw-ߛOHGxeǰRi>!k{v"F3hZ6m?R/ɗ߷auΓEi'|[!m.L%RZm7Myh sC嬠wJ)mVYn}H;^SK[me\6h<#p 9`ّ󭾥 {׽v6䇆k|ǃ燡g{<:eK륗.VsCga;K}{Wv_a͎C?dV{F+nʔSO)+9'VFIu㜺%j{cj+/lHovRPPM 㗝H~r/ؑ_]]ʷ4+3m>p??q5{(Q%k8I,h%@XcP2"j'z9~q^l\ƴmzw5>6zgі7gGY=ߖ[~7hU5õ|u+QUn@~Jm)s_,U_,涉ԥXy֧C1/cux.GEg 3z'?nRh|(xVQJflKc1u"aQZ))Hd^cő?19>x׆M~x5<@oΚW@/ ۶O#";^PkM5< ŗƲF:V8V$Us;`;[~67}J)CwKhۨ_>])9BdSJxƝ2ig9~~{NOhr?RO!71;7ְ̟R_&[<UV7SmʗO䟯 VZycen]N=7MB-!d)k򒪣%#wOe*^ۅIR|hRi!d7;ƗcdiUw%OH@yW3jE>Ɵ ,=̟x &暘H/l惮kRt`Y=𝁏ѩ<7M_~IےwyrI9~Sm8;I* ?0` Mc}U0Dn4S/ \)X OlL}i<F>_fLxsqF?bNS^RzfQVJSBBxm -*^xAi6:SßOS뗤r!i_^hzmm4֎e^"c&ُӯr{xGk|4.ϱuR|eiv;4nBc [[^ya5V͋mLoZ'Z\Svx4!r칍&S3/O3'1lQ)5nHvi|- %Þ>5sbe?Sdn6JЋ,nf;tɃK ,Z;wtx")ՙ30<|a>oְrak#Qy3j7E9ogk\>~3g?so8`!@n-~䥔^:x| E.oCo/Z\W5>?oO?<)*pT(7j׻ XJaXwV[o;8EA@?ǾomspmmlsWJYanjQ6hRIwKtKccm(dž(A'Gl]jQ'Ɣj{e|Ү}0zOV|԰r]v8MwA%[aR˖mP; OTs}=aS`ڸu6cj}IJ[WG;rް_ymZyOڻT=ߗ' Lxf BL9\ `)<%%ɮ<^:E ~ܦxc Ag5snmǔ2Dn-_mi5awN1t?RaS<~k=ն{ǗRjMN7|s ][(øPO)5.dDF1<>/O)>;SID/̩(oq~OA"ڄR:h3;;9r}ml]F ?]@Ǹ)_^G.k5kh" {8'IPO F2?u8>mMß8ȿmn w<0+?C!|taESS7x?L@oS|;EȑW6L0~zYus<_M・W= ,m-HF=/{V>_6;2M9  뼱_ g+%ݠN%]?^iymqyyzM&}W_op8? }y-Gn-=?)|>$A<7/SZsͣx7x&Os'e㏚4yP\+G'&e4쌀%?E=D*Q- qS9N@wŁ!wnsc|uбOҚu9: gذ&V8'3ZC4+6~ 0)xvȰgwkj6=wR!{GZqۃ:zSi !*t9F3kS\ʟ6vɃ`IKB/%p2q4gw)Pm_]=ݮ=n?>/~<6YkWi]z4l[,lԚm}fT0g8qԄ6%5+)v\?1|Se6)| ~:Ҹ>"وaS&4 6eKl]|Fģ7:_;'[QO}pvMɶ WG[%o(kK^{ܷfܐWX[_]hAЗ/=N eivJ5C؍VHi̩})OkMqRcK:E}hR}-jYn- mф.T{m|Bal<1Qsc.&7ߴ;-¯Bw㉻tFϐֲ0ѲmNiZ˯I?uqzv/4N j(3@kOi+ 7EbڄncXen}=|k.5F0)>LtJFc+ORs  N_f')o~1L&bkGFӎ'8SeL;8I3Ҳ@FpaBJ/3w{Ms0[Vn]pB80޿o?7 vtߥ^ ;آ``gph w /~FHh65$߃gSHqBRi{[;~ްz]I}u%ezmt>6r(/F q:jWWWz͌*%gN9ovknze۶4z~N0#OgN~kCiU=$=t kN,B? Q?&3a뷐'ryrmI<XgOAx wǣ\fvokL{t`," 9/}fi΋եb)Buܧ$8 {Ww ֊)RC?h2 3va% #ghиC "U|V4:?)ƧO.r͍Y IDAT=E\9[8whR*w08zjml!ӥR69gYl-[`zx~;ĉ۩:o"PocN]ե |KMC/pO?1D~XB #?ъ0n(&RR ڷnKlZ2.깱v:,/=vXK,)i ҿʽuǫd*G.&DBᑊ u nʁ}ǟ]]]c[ G<5qq%Ғ>O À1n\PcaBFRw_Ìٰh*m/RJ)P.͚ms|L ޥu~b^nҩhIZf,(xm|q՚{}qΗ@fK)5mjCj|5θ>|mC5|ƹJN7oUgQ|wO뭄uCi~Yڶ\ۖ Z%SkhoTi;|4b M^)%H|Ŝ>CR |0 o{+m;2>cF~w#?pϑAxM`-NR<1~6=C+:zeKTY8USFigGutSyI?F쏜'\&;s;}gEx$ }(|7vjN]o{ŅY nhmx#ɥ9'?&F91dJn}Z~CHw)ٛ:xɣ?d\V]Lح]VڞRb%Ty.͚m6Χ ݡ5q_mQg^y+%1cn0{Z_NM1>KY+~ډ_]G{cl#x9+ŸJ,doRgE͖G,ݤ FR˧4VGIeKp_v瑠G;~3c:@h=SJSVZzpE)]/aռSUc39]û>Ckeq:61r2<缏_B{eW ڠyuӭ&/./m琸~>qؘ)q^7ܤ]w$14-ۤ]w՗ֈi|c#N%e[5yD=uRtZ ]kŻ7ZV>W\K8-$P7:3=Tc\XP[OʖuKbuLS>Gzjw񗮔w K5[y?6*@ղw5ɿx|_Rc``#iuZ;.ɧSu9m(tV)X¸ ᵄq7=I;Nh#z167q'bj\݆eyOomPmGOy&.R (Ғl[rwʄru^yms)o+5swhVEonMkh X _?9v NgM&?*= ёz#ZÖNxO'?xLӗ0'uKrJ\sb SqQbm|u>-)x n@֫ t[0v>fS0 )<|)b y/w&'>5P/eR u]є؟-.Q2GQyTkMF0F9kHq851|?v=K߶a:6~%:aX?Vlz]Aؗ.[ZKu%Ǿ|N. Wߠ.k qmU$386qWpe /?R`&;[C3\_fԻyl;h`o:\ b^8݆g< T 6ox`t;qNe4mMT߃oˇ1?YnIRuyp\*Rf#^ K1)kRHR]}[! o>] c뷹ri!{ w/?޴}w))!eg v,&x&/Dc3y3|/Rv]7ߞg;;^_9,JiFNvrk0}KsVS.Z1uݐD0amOS?>N=\d !oxnYzr.9bg3ۏ%GP^A3h{$[WnҴGqݴD3S٢ؠEzu^,y3/TܜxyygseZx,m!/ͭ[RV ڸc_>R&j8mQcXYIZ&.@ܮy/a4ְk#9.KE~8ɢx RtQ<M;^_)~c0z(׫עP7y)\QΛLpJl3pfoRnqcJ>yqҜTIЎcBiZ-pN;5 [EQ|UN3l&a1ggnNӸڟ Oμ#&jxCo۝ƕB/F{~fxBwO~nӶ7u!&Tà VtWq`ן+WG)cin]7Eh(=z}_ aU4oh?BJQ{ly-mhfynr=[ y29q !lo{={]ǩw#/)W@W-ҒTZffqh {۶[CHibJhb1}#_F\qC; ~SSO:vy3 NJ7EfL7m]6WHΥ-X_M>7~]ƁNR ce:_Zڦv9d9snD]|[\*xĨqO}hO1k|ǕlмNSńeFjyzu1xjO:[_3MyS+)ǕрmRSfyטIэvjcO:TqLߴm+o'a|T!Xq?j&F ve;!'?RZ˞/Ǯ y}[$iOm(?ƨP~Ύ @\'ԎZ˗j(սѧI";'6;C|݃u q Ah:seOWe1@L~KbxhccXYn4wӥMnjT7چk!|K=-z zN\|&4\vJxy4BIq{zz>C aOp|~L[ QS 楂K5p9Kzq 5U#W}X{`Li,Ah}2&sͳj-˓+E1YylgJҼ[%P R Md?@KcР\wUw;8f%/y0 }%K?|&jujޏc;ڢ;bt![I>qM>4 N3?Dw${R$^Imx_>iV9V,)5sυEƁWUc|u2VTgIYM fmWW[ͺ|_cRZSx'!I@^sP+7T)CpHKfX4j|J>L:dJ?SsAk -3<ٟͯԡޞ 4F4tϩ-[[C/WM_؜A9,y@PҘ"JzBQ^j!<œ8^.F3z"wD> nv'񫠵VTiǜhcgbP+?Jm[1P^IGӢ=d>\|c)zޗ>諦xy~eO}:m/y޾x;=乵2ŸZUm+LqyL%/֘ב;aLkyJ̿;N>fzz%%WFe{ם8nZ~sNv 8^_ UЄ qwlZ̦ճF7=^5u\TP;~-[MF]zѨdsb|u"eαORC}}o?_W_zY3nُQquJm٪e[b#]~{ [5m]2;36{!dp-"մ7Tv})oK1z_\ğ<_IjrsW;@Lesx:q.RWn9w/|:ըs+]}e[֓?O[g.nu/zvH5tl6Jw"c`JV5Wϲalokb[nc=gF^>* Nߒiq}b39BcrCz6|ˀMvf nAn4 +XwI%^Y/QJL5քͱ7șl.k<K]p|_wlO=l|k;}-:wsҦ_Px*OՈʖjkjkj "żokh0r55ch@T{VK^˫і8M{^yZp&s?_CS|l;G;zm8gZV}NZR;sbjwEuf覄Pmߪnқ})əcW^Aa={ IDAT|[1@Jٯ8Rzēf&q)|>k=$pzr&Z+-_}ݦ煍<=ɜ^o%eY AkUvcJ0Eq픱Q@gxzBv]i zwRwu|I% }c|N|'Sn=FWeix 7;0ސw:?0Z&B8vYiʃ`|Bq9Y+HwԊEfWW лB'>87gK_0ޠ*&6VV} Q]f)zh)(ePjS-7a?~L6؏&[6C!/PX,aO[wQjOiM5 Z62m}%ڇ>rY\bڝ8<ƿ| hm\V2JK|).uN/SFiƹ6.sC@0c/ڎ1m]\C/n^kDYӄ;5>g M)1&VGKιWq`5qZ% 5.h8kdZ!jspր1-- }Z3k~n^;D>A^}wԻT.aRӶhwP^|s^xI'z?TjZ=:ssF:Hfå?7mKh:qJk_I@ m'Zr59u1*sKſ]/3m"r! nYg/C6Jl\Q·_+<_CC¯_jmCe1o+i|{C}^Kla)[}zۓlj< _)LҾԾzliCv{@n؉p9&"Sm9evkkiӌ3X!ڗ 7%A?|{a0N˞xC*y7clb3y!}pތrwzoO4Y-&Fc EL9&ZCj?]{Cy%G-T xLo4Z{ss术S!-6hibiwh wqgivY) k)i8!pr[~C6;*^o]9&:z/N PX_VrLoT@|1~*F]-guܲ%9ՙ ;t[<ۺs'KNe_9pmϑ̢M5ߔ]O ]ۯȚV\ץ~\BT\΍8?@ZWgV$MQs(婺vkcJz5r<|*_")Bx.W)Nާ$?BfW3"tM9R#5YO66sgL9ovڎ3Pxۑ<o&z}cGzH8|M R1N؜7 on΁6ٟi;kxގ69TJj5mҚpjxք_- ך1ioZ}NnSB}Q|2g;}-4xN>N<ëϨ//bqCu6Kx_J_L|ѽ/M_.`rq7D-IϩΤo鯀@sЂ7 +%'261cо)&!,H)8|n0<&،O_Iѯ̧oQn4)|@hJ7^B8?ۛ1=X#Sqߊ/޾ν%1[vOj.C|[ZogbiVӦA[4m (554sMÂ7hx١yi}U?Zл e=YsMnw3^/ET3?-/ye9O;<&B7?)t(>*~K%<뎤؇K]aV`Rc7գGߣn{Hl ܼ`TxL__SaM^Z:9)eq&uu>sxg? pg`ta-ÀFJJW@Kߺÿq,c$n]/<rm5;1}TĹTb}X̢GYSzTN Tj8cݴD3<)F۝\O@Mv}ּyeϴA\koio9"{dڞ+ݧ~?% Fԧ%Sו-=sbKۮqMTc9¸*(lIzSxoBېS}~ұ1lfGqv><;W78C BvR3ls4~X^jKqfd误xn@5OEnO_|f犂=p8u:Pe$D7GYzĞ usOI_*y>i-mh]6o<9/m$| rJ){d~ӒL/&a(ݪnjw,כ:_v^)5lmrg=lvmM,ָO TPs@v5g[Vza3Ŋ|(ɷN 8D%?E|Zgx=<rcH;y!t+ΏTq|s^ȹ?6HAƿp߈?F{//#i ޛ]=F]y'6Ra:Gy mXIX_[9Q|-Ōq w6jmyCX.#p[w}$ƵCJ'bk2> 7dm`\{!t@1l|+%5M~-f?LzD3<;ti4^}~mM\d&\tE?n#!^ڵ:"ֶ֚A m;T\%E{O taIhSB9E[]Ci+&OTx]0|7!߅֌Shӊ/oGM@ڄ9jaruX&M-.2+}l?\Gن\͑js<;'I,GPmǔr:Қfs#ȪIMo3j5txeZh))gPq~2|'\͹ hJIxkabUotF#'qMz+ iB,e/1nOoPşRk.ZF>u#Ϗ_B}4M1g1;'?8~>"uwQOmKxCmKr);C K5愂ksrnIsb@_yc[+=zr^wCvʐdKB@3;>RRV =V3,jZ_瑼))!]f9|Fk]ej]%<9WyBpxG7@\ ԶKu%%P.)0 m9Z- Kw*=u]*ڏϤٮoAϰΰfqϙ69|ysvc̦l]0u=L[?܏؆PoYƹѝH:פk-hLUOѼj_-ZwCĐmﲝ$T;e4Sw߅6FS\z TIoo>okkm\[n%.'F9-N| ײǠ !K'8v~-N%o&@^[KZM %i@ЙPEn)q͇"7Z^) By-67y(Xj}fg祒Qpi^3o݉#֝wm }^@MY^Dމ4v{ [l)s/7okoXJT5Ӷ{Gn(ďg3j乃5g3ڽs#ݟYpJfOkAwLjCdX-ů&N]w|dѶubO==ߎ;>>~uܞyiOkPAv [y6qn?~N m;>i]c?)?OBA7uNi)ΚxN gG0Z_X6Ն<I;>w6c4`V(6x[ M%u8?EyIݼ%?PNö |Rh<$k[yN Meā.MEvmղ 7 %+/m\3K~OxZ?q?= n?ךߝ%9#8l]?N 9Bf-;>O%nB quH)SmnRO e1GwJD iFMY~|yR&qcf'Ϙ)4߀&DOS#J!8zOZy9y+fL{el`̛~x_iaN^ݴKh\,BA6M Q$?,$S/dx,wXq߻[hQd=X ߉׷q;o?}穃>)xlmx !le5?Kr+["!̑~^c+, i<; ;l޴z%6g-p IDATm&93w!03$HAj%D@>o+v I5W --`9UoO=;AwnUD2 gcad6at2 R_|eos~!ȁ7:ˍDcƝ/N+bn`*0yKHq+s9CTgc.0;utqqbIq)Ĝ9mCg㐋d|)s.1_ڔ@%bʿ"qȳן؛_.Pi\Yp:K:M>ru1Ǜz9\,5uw[o&ekh:^zǃ4\=}˺9Vh-u~X}ߏ tQ@aUe1kcfK~Q͟^wq4Sz|5~U^b5z.JUUHx<@׶wwki`?vqq?x#pqG郫Mh.>oLUȼi>ƏƦ|mJ'(eN?WƷ:@rvFlsF` :,B@v4r^<H|r7抾SO H$ur6m94-' yDǔ9m0%Uǩs: A 㐓As.1wٔ@ҹsYڜAЭ)"c_mHEj:f}0^4/K`uqu7m8NUH\_.SUp{mVU=Mn|+o ѸWո~b03LQkyߵa:TIY;mv6;|VSv>@Џ9} ߦtsxߵٹr3@wd+,{=?']ɴ %ܯaQL,7\D RmsOO!̜kP, u~\vRkSTDӆSwcvX7G,_(獝-7gk#O_euhvC\_MG8q_u u'feD ?K_MOZL)ft̙}߱?ILJ!;~u=tX<.wS#2o;'5QD1}ct?qw.$˜6#1m'(6(@#8HhAs>Aұ}]vNkCg/xoZp/?(v{DDK 9 uJƼWĜfj4,j)Os$zs%hyf]._zߝγϪ5psnܗ`fg,޶lǕtcrssGz: |Ǟ,v[0Gk>Gi+seT#9o"D(w] nVbߟWtwD܇?4}du H#4/-F)+$dgGz`cZ@m%xWgaw^֩9l./oP7DO|b_S8-2O_Lp)s0sn gyPW?i>0gJmp>לlkEovistߧotcctcdßxNiSbu:q\ܾnJq(t!Ő| [ 3m::$Ls+p/]{$E꺁5x9G7c?޲\7}A׶<Һ=Dlʾw0kw.[U aWhvz=]p7۔quN|`pqr)ieE%F[6ȟNR;%)xHDZ&q"S!N!஺4 ǖktݩ-Yz>ֿ _{, Q#HvŚ/f9˽]7$`9"ql0x<ga_O5\_t Q[a펧m?\a%e>6@׷w#\);#[]:['(Ĝ\%W g}x<.1FZA6n8Zyc~/hISS=ojy5#.vc>_MlRs7ؿyO~CY'70a6(lB}9C2-! %Hhdd-i7rs>ƕu3F&w.M̖q֫~r4Ӳ@#ݔV!y;ѯTդޜΙExN:??Fw;X.C}%⊕ I. 9>½lsIMccy/7jzW0(ѿwxoޮ`G:utnfk8}?š#ǽk\iD)78-:||j&,7X+롪[oUuSBU-z~O׏_X5K{áe׵c%`]]mWD~i)5%Bs"Rn[vuÐvYq3yq). =y.׶ ߄gӛkDw_Ο&zu"wFrp"fͭk'=n03p)34? 0,?D\o x3/:N?]quݞ8N~3:֜!=^qq=~׻KsF fS m\s#RFWMC} \M7TSPyvپ˿kS ;7Fliy~2gt{\xzHuxsP];GÕ|X&_|mG@1$S#YHj$xch[`#~^(ഴIPSge}q񗨛qWs\^ir/͒|XvM/C?_;|pc.3S79p_2 [&_aMй6:Egkd>yHG@8LIBK]z,D1u)[M rC:N N}ޕ ;aKSlˣ>-EesKp/)Qr 0[v97g__WRm{{Y/Q}B׶hsy|)a =ЛwTקz`R/ A:s}>N-w7#Fi|][Da9=%tN#S75N/p<\wf1/eҥon)R|t,JRlί1 gKSe95bt9o_™T&s5o.7ЙAl>7ƟCr(Ëܖ8Oؠ.M+0lDg~*98/fɼtM8+^}oWs".aO+%{0ꜶK擇t &ǡ42se.=osHzjV__Gxbo{_+"pD.a/-|DR ^Oxr[x.o,0lB}CvP nPM>&ɖz.T_>]rm9!G}BǞ>7BdubI1':?Gr0QM?U5ϏZ+)mCM!;3{3dtrFxkܬ&C(dy1򷞎i*;v=z_ms9vp77~?$`8ׅ/E$xXwC]M_U5{Źi>=McyMm]c翆+}4~_>yܯ)ǿ-pMN{)?løA1|,ˮ=x_{mr9xA6^?&'?! lB9e 1}]>Oq찐wW@+Hq1>}6.Mو#!1}*fk0w`/c~_ΙDJƞA*BrLD(" R@: p[9䌆sKgFƈ#ܤ:nj֦|bCn2&Xy 9&)ʵ^~ bekƝCfvAivQe~w7+ af}MXV\>mIiv]}}97D">†C|sA^ K)>C>bx (D7A)EmDS;?[y[D8xXqk.5sr!W~ ƺFη5" ̏t%E֍VY匆1ADL $ƒnW7GƩc\yHGBx$42.iB'EC2)r.I±МMݩ$mf"?Njz~̫Tu|^^_7 4Ms"XP73LmB{~F~|x4Uж@7#l6al(>}>|~9u8; XkܛkHs}mWݥ(sEE(B@>i3'3^is]͜;6u`<dʉtKvn87g.zg#">K}eJߔ#vkq!(=%>sy, lS /bOɁwu=\q*2B[?G]j/Y}A{;&_(P* X[_I.Yy9cm >R]*9Rn:`9hzb!ysHEc{^yp~&fcOG-cpd:!./(O܄\1UǐU.Gz O`$h<{<UOl94ymc> ׻;3ed qo"R\\u(mv?Ϯ^?6RmHw;|.09ȰkG}Zns?o8y+|/dqbP)SJ)92NC: "RgKc*Y+:S'/`7p IDATsK?BN{?;eڮTmjb[bew~W?e<_\m8njr%.y}wzk^K L{wqu@4s K TeVogyv#!/qcD>~F% fm0H%[(TFǶ}rmHDZGa-D!ql,dídݚ%/Ug"GKz/͘/5p {ym7ntR;gc^y_~8RΗ6Ka+r"#y>O>\g?ߘ17x5=G?eDĩD&%%u_ɑq.tǒq%|XTB%6FEbz n^#I9 {ηs~7eaY…^.;\rs/Ŏ5BKϗ9!S}Θmno9m?#ڶĮ G8'|7g]2E38'g=gNh\?^%i2#GdN'0z]!.E2  |(rFcBʱ>)Jߝw~X5<{=. L/Mcpn˄wu:[뻶fd)|˪fa`z]N}j?4nUUǯr30 }aN^{Qo7}Ey~0 c^{׍Dm0 =tmw^֤S=N= ]7-6_<~XM0EV7Rfoma{8>ȋ5ϭoϡ{]%ôTz8<~}דn'ǵ]B{J7*l=ٸt6yHԻt:UVR`F_y7r}v3xe417.wyTTs]HyDYqPޱ`]tn{Y֧-Qo]K{&:.m۱4/]$5=WBp s;\q?Ism*/CUtOQlrP divb]ߥvvc_nWZ_Zo9'>w1;Ok?w\=x`NxkQӵG8nASSqAd:L6"\Cc8.JCcPɸKa9!5$#䘳v[v[GziD/KDd)6}^і':`|s*lv.av_p<|Kϡny y6\$ڿ1w'wKǏ?/֎e3kswkn.9C:'(6(%b9t™lYJFB@#7OƋc ,91R{ʪ/[zeDEf0tiiuH9S `')0'y+e4u>Cy_>Ux#+c#s~?=ɽ |'ǿT˒zɑ!.CHsKgcdp'ZRuPwZǃ}Q\nt9sHΕ-F20ot~Ja_$"f^1΃;>g'D#vLF""J:ɺM!.V$YSڱ$ܦ/ ' F2 @ig_Wyy|̷b|qnλ/Ø4>=1v=wb#]ki7s}n/fWO2fApEfKr}#}?NbK:s99wc.|Asp%!l+2 (!y)|.Rn T:U46BNsxx|E1nn7޼?aG71ޝfݣk}ߵy灝x9\y #c_eu;h,\zǿ{0}ێ;nPM%rZ~?_H%KKDq\ހP}YtN3.GcXl%X5bb8c,?X"I!>VJ.$dC]2kӗ"%!}ϥKSҍ)Bz!%2K%Hp^R <.'>s3k=; \4瀧ks 9f>2椧E1=dB)Z]2<]ß%&c7V&Ec-u.gO_blI&p?_0h#=޲͍qEz /ґr#iMsRK\D`yV%Fm0NtRm^WŜ1,~p;hc%{6;ױxmHcڱ2N>?Jd2sRF%X}J1U窳9Yͧ緎벮kxxY/_WɔCoYyp*zwÍ\]Xo64R\@zܘr~_F>]kw1]q>Ed{+ŬpuJ2:~g"것~$ywnΤ~]&OQl|eL`u=F(XTkON!}ߩB6=FFit.+ w`< lyeGq5*I.OyX%uq|ZNHgngzW[f)(qω5c玛!?`1ݩCmTD{&_B6>{[bl*l^%{^{u(ڻKer|p/s9 vsRHFdhx7HE}X҅}2~xt$n0m;Hvsm;Q9~~?!6Ͼkqv 5 @ulrbBuc-\ې&v[g 6 36@ّrK.$%.OsmsQDAulrזdKMH0w[_3["HA9؝ 1?( \K!;:@|~39LF!TuH1m'blsQDBxyMCv) 9%[ T]H`FɟW㷟:xpΧ1A~aY.?/rW_F!6fW)g~y9SA9Nie؞k'y7Tx||PIo39}/po)5#he~? F">Nat\ [2@VH<Ko/e1P谥csd!(Bo|i׎KF)7ui䌖(=r΅^. 껼?Oniw[+m*@2]r G~əۄ{$}uKc\۔>P2^.J"1d&pKS,%@IJunU fs&7wnX9_x`T<@|wy-_6MsA)cN|x9m?_LĞş.i?6YHO*):["C۔>¨֞ smrv퐽OѭUb1~(y?HA׈#Uq>.>1inwd޺wfČy?.!/xS\z=MX=. gD韒ǴdRH̩;_wF`zc"5^.Ţnox$!!6tXUbkQ!xo~i>yHcӗBUDQ$Ň֥12_ G [M1U[Cŗ_Ʒ<-POļ{95^Lpk$:0R?Fy%҅޹8D8fNΗߞor6Sd s(we+kyY=$?^8׼= ihhe|hpLJP%pNaM!]P2 D}z^ʰR%[!T]l|znv)O1Ou{f9M˓G~iywc+JYB0H8#~P&K>X҅c!]&B (>B6R볧ԩ2MT]l`\s \/W\SbKxNy ^!s%J_iHyc~[N,]3QK4=sn< \2y\_}'?#sy1$aMUÑa!]>FخS yRgE2_ G>#"|ko~0 2PAn?(_Ht׵]iDGq"6;+a`$Y"ǃq s1Z &o~`umR{gm&0?^d)xn~T=EgkSd>9VOBxP2-H2]uY#<4pNR9澋Μe@puOxs|rduL"ɱz-J mAyIr^Ksb 8u\_}/~0ּ;<>~z\e'\s6sRf9Z+x^u7 Psx4 @fy}|$vInjLnȕL) 8`UBxP2-H>/% Rn֟W㷟clc#;]dKn=9?Ҟ_F[1"9O7W5"]yͮK]8 ~kYۣ_]7S#G=0?jP4\87UaOƑcT;RVD`(Rό'dose6V%>Fc'UqD,[9KƗإ]Y7A,Gt4aOhs?%џH5ސ sk}~K4$f89NG!j~bGW"$@xC2)2RD`؈lH>3/Kgcdt=JJeݥZq >gZjWsc<$wJr7v4ftaq?^&٠<,ȵ_Cs#s%`4Gg)D\ʖ#cY.Ɩ%C (B<9bH6O jE-[xSWd^F?JH'xGKxw6.ϖ˵uʱz> |VlύgUz Pu:/Q ~{ꦁ'Oy]1YEcoǤ` K#/f~/+w=GN]ȮԟkznC;}V ]rxb淏{St, ,Imspal}1m'깶kT$B 9M<^ce_o?o=u[ !c?4DMy["yۂNϑz;y.g&]7UV.#2ח_?#x ݶ>oSov=NѥALYj~eoO>˟`$e˶D]B&A)Yl]͕t)2`՟ޥʥK=f IDATde[0Jͧ5OY75?E&TaE~T>{&(mn(61}bm9غ-K= k_2s Hu fZQ%Lfm,6R>S3(2<9$J8S-I7P"}u֦|N>?EF( H>GI2ӗF9kNto<߇",u!E#. dn2]$=>}D|@(u ,SP;dKX}}._{Sl ϒ+dKǦb9zbmHFݿ,!F GD:Krڠ4(1sD琊KFs$N\zL_.dX}}._dP*!q<!$[#)e:Eg_}oU5|xx"K93M_ 4Sn8o^] 0:?lլ5 8gѵ]5}g 0lhlo *Tp8L>@# F͒".QW"^|r>>EP2~PB~NMm2ib"!ǶE0 f +a˼@hCu]O'"]q"f?{H|vmFdy`ȟ9o@ ۧ^JwD)2jñGQ6$/ŗϖSBsd}H}"KM~|jrM`nv wsc)>AUA]QS0-/ @PW 49Xs3}C׶'}wе]ϟhzpz~<Dx~2c[<>by%鰘lc8z pd:EgkSdFkʇ@Ta!)|TFkcu:Uѕ#St"K_}K: `-{t˨G2ԻSD]#lnvؚ3s$[gpI6/{s$?lp>HS 8u֦0:>>EP2~XJȱz.YWB-e2Kןy_z_k)HWTm.}؅>#:b"Sd}e{Y֗U] }߃y~ J߿\irS\d;gS$; _ʖzN.!rS:EgkSdFkʇ`gs)Bu7Tru$Զs>e-})mcH#ȹ{~kte+`vOci Ǐ{? ђt30kqqw%ȱz>.:sBAc-t/Лg8şwi#.fT\H_me\$C̛fڱܐ^4.9ƿoSǃ9aA\`$xm?$pQ(2َ#%CmA ʱz>)|( P)n/d҇6V%z)Ap96)sd1-t)JhcYREt}R_9^lͽ<7ݵk%^?R;_N˱^9$09(n٦Bs(X]'əlH?o?ˆCm*9ϵulLNbK.#݁#ڧ\),Sn0-7MrNVUJ7Ms79#4 6;*nr7~8^/:yy</")O*nʋH8r~ "%p9`Jc꒤ge"mSd>9&>E$mAc\zfG!nG!T%u)ckD ̊jSs--M8>3S~WG×m:V'wHŴ69VkڏbX*n)9gΥ92 Se)quESt64))gڐqsZ9~ݔӟifR]hsy'GJ p9IoL>%b6Sd>9Vk˗p(OH?w܄<-ImzIsd1R: u)6 z:>AΪ&C·Mr~.twX̐4 *bvy. S䷷ IPpyY%yXxdT{q:[hSd>9Vk˗b*n);gΥCֱzK'W[h H9h`z1OAGoi4T4Ms1^Svǔ}BI#sDp4e[է&J[Kg%w% %j&9uvټ #WTqk[e~yg?4?E׎``6>wsoϲ )=Mw.1q>Hi"# .xtk-t[hd9Vϵ]ßbP2~XclcK^ !@C2Wх2yRΌ>ۜ_3B|9/nMn׋\Bw"5bey"ߝҽ = S};$%>x*¡dQ2!긲-r,VSmv\&NhW|^O56Gwp<9{(n.y.HӷMREL X>+,7=ڹk/zw͕I\y$16l8[)Ƿce[ )+J)5 Z$BN\;L;d,RƳ/7r#"]6{|"wVlkEn*H~<5 sĒP;A!sdiSdyHcK;qEdܧWBK0Ծ]m8sQ@5|nRځ79t]OT4_sr49}7s Pr!5`#0.lk"6Y1yWB^JF6EƑc\[ RUlO}!gg}LSt6Vo!))/aH۸ّy̒l}k$V1|M2?;7s͵s$]'6KcIhj-?E_)2RMS~ZM.x-j}3ݗ V˾Y{{~Rz`~@]:.’>]PSϘO/asv/%)/pvs֤s:ۇ0Ǐ_(4%g/tdTASm%q(W(W\ ;\BKZK$)~dhd.yRR$漈Z9]sec9{ϳ_Uq}y$٣ᵱ1:)@r: $u^Bc, [ILRk_oņzT/R~&(1!,Ȱ%kb|H.Q@f7ܹKo0;6!(O>:cb0?aT$ܧ$%lu)plP"8Aɸ†[!T"!oEF):L"sɳr^|ocuk0yw]sk;a*yף헻af O鏇G }]?ɏR"yQXDߢSQe>yHGRҷbpD*)?[ (QɘԱ2N?qm}uӦ|UD~Y~~YNm'KDVu$>|RIdXRT"|bñB J>(!k"!}I2^+3!C`N̟f vӌȇ!<6w} ]BUvv]7PUՉduc_Ri"߆|K\Obx(= ɰdW.Ms(zl]2]e\sy$~ExfWҊ0y`>/}MGl߫oؖXR!%XRT?+ȩ6[*+P2rYBӭMcw,A9;s+sڭQ%׉7阬nJe~[!qǥ_9mCp7&7K!T?֎qT-J,(W"g7dω2nDΥQu1d>yH*1HK;4unꊲ0}]?|> c6pNHŐe;Aw+{+D;"ȵs.\WG B I,QZt96Na>y Dg ;q4B}6rIv}m}|}):6EF B'8k!\,I2o.;_z^ya䙋W<D뵒R$c#^s]jB>B%vn$X;]sQx z^Mh nHs#!w%H)$%[lFC:^_. +rB [t>8uI%X[%5 Av1ăBd<5KK0m'arS(P2 %nAOeW ^;) RsVɹBOO%(/RQQp,' RRws|F//? %5p ܧ/t)Gڱu͵|rn gowKB#5 OW* _KoD%ȥrS(P2X [!!Wq\LRcm(v>6$v1'~ ?%%C%H81vm#(6rS(P2XJi28WW .x ~>yHcm8~JԷ *6y]tFK![ 9t6EƑt~)((W['>Dt :N;#0z6ua5fY 9_S/ @[">]yh8Eicm|F_. J%VO_"!jj )8Kv\~.eqPؤKpiRεu=.~6QyIDATrS(HP2(J2ͭRkGsp ]9 `'߱m,/.""9".ynlsH8[)ҭD\XJ gDB&{Kq\LR†cG?r#HS} plx $:EGڸdFۧBAqEIPB$侾D 'P,%65M&ٖ$ޱ!FrSʸkJ+E9p,7)/)J^b4h0:k#Gn{v BlNEq\j6[~}*Jd \"E̱I$ݚd}M}+9מg !mOiCHrp,TwMR-9PdFOrD\Q8A{ ."TbclrD׊^&9IՈBI˂~/ڦ&)I}f-.M"{%aXbwH/!/Ahcm}r>-JqQ HI8d0dKᵉtk#e듇t}*>)lkG%H5֎clSGCIx }6.q9P",Aq2!ئ"R$= r|N"cXV_nRHRS:vܤ<' IH8ՖcρqŦQ BAE-r,(nK騶TX2<6>oR/)HAJ 64)"Aظdrk˱BI&/[CiGt{St1\2\JO+7_x9MKK"kpv YH'ESrD\q3 BCv{n1W&+lKo%:.6;T_ITP΋')OI[۷B*EBqUBKڤ<ƖgK\J.OJ?k@%8 Ac'_K+C:j'om BAr(}kTX)dyH'Hw}a"Eű)lNcl1mn/)dTMZ}n 7O G+*nAҗR}be>yH'I+m H BapcSZ&6&V&aѥ+aS2#\>E|)|9Z;#6;~P % ň\ k96E$ykm>2 ["9d,Mv1Hy Hw r 9BQ,neAB _+IOi+$bs\/kE]d}-.TҽV"wh(W(.q/yJ+},bP2P(yC DSI!R`tz 6 "} qIyq6ؾ)u=6O +˂B!-rjT(7)ʤHZ;sSg 9"1ܤ&RHzn?2Jċ6Gļ%DSI6*E_96J)!w,-GS' r^qnˤ\ZrȥT;d-P+!7RN-]6NB/^^rDܧRoE&a+)S)oeLfb uBOIyɶ>yNBئQ*mzjRN"JKʘ M_"!AH_e%RvRJ뿄}K#9}R}pcR>$1qBkM厒cH".w=).kcW.HlR4kFc~ڭA"J !l@(Jy~owMy.=ֆcDLs5 t)~}r>;% {zqP(rCIdD^.Q_"KSKLct6}$om\⦡d\H5J%X;(OW --zMc.kcϚz /qz 2-db.)OPbHʘ ^-!\nR.$I-Cz .U-#ɔ&yk,W_K^2 sm9KO[ B&rI&)ѯEQڬD=kxOEZr\Ĝӧtb.)RاQX |P( Ji[s^s|I%mbS( k%o5ЯMS>[}*%P(fŗbV-rN t;obCmcB3tK^3;U^`e% P2P%xTI-am/[^$)#}|P2B` / ="Z/eWamXqȽWtK!/P(KBe:)Bܧ+aIQJY2|Ԅ&7Ϲ\ܱ06}RY{ Bq|ls}劒s-锹6ms ɗTfOK k(65I5P(LlA'z.9OtBs{4E-ҍ+JGsk#+OJ?P(bkRܕ5 Ŏj'/i[3zLAc,SlRqS)m,BD/ f")U-G]JQc-B%k-m>}6):B`_[ctk!-q]y[ˡ{'_oRl(vT[}*[SP0d\&]#v-2V*N9הʎj'\%ik]7FQm9:B``/ bIKK%ޱ5pPrK"rTk擧ڿN=W( ˃BqK(o8RߒIyH#dbIa'_nTx,!f>y}:WE*0B@ / "Nʩ}r,NMSK^OJ?%`͍R+g{n(e [zQP(%-:R.1RbG#oNu(z:,gx" ;'mRn[_wKe;͐ @R}Q+P?9v"mF}v_~evDQ;򫝻)Eo.c<$~Ҿ;o+@VTWڮRg%=ޓcvx*%ui;caO裺g򄷝, ߴx$W{wQ;;Kۡ_q6#~]=u< 1/%&8Ec|FO_ff?*^vg7zܻǾi3>FK N=O*f;];3wwRHMҁRi?!YV+۬/~w8}kԸANJtĢka>'Wz3zw']<{fwQq >f7Rrl  v ۴]= EZ O 91y~{mH紟J(W]ҋQ T'Rvp,>+ٝpևv͎v(e2ϼI;EbH"郎XcZ\? ߱EbN+>)pJ@$ȖN$O'ty ["E+Д[߶KIa$ikE͘;;v=]L ʝפ׋1ډ;߁^} u?^hcH.;R׌EӶU^l'>~WgӹO+ʍ S19J^7fVm1G?mʘɱa$kNJ';qvXUh%qG2a:' @Lngq i']:vB$@o)kMoؽ{{#Xogo̮Q}ߑДX%mI/*bi'v$iY.~VTZ<@C5'礢6?p]rQtvJ,i̔ЌX-yI.J)g*=CZ(ɱH*מ p'+MEIyb?}wf*&@U֟ a~Z?~+Y}ZV%NS5(M+PXLU ϲ 1keߣUhDVmLr|3Wg7jvjHLJ֬.Mԫϓ>[XU)fk=0BR,_|$[ *e}}c8W$-IPQڵ2>JB $8+cZ|gXWNpsY81ux)\9$uUbLߒ%a NoD=vN^6fr$@uֱ˓ބKLJ>%:펵ǽ+9*MǝvTXQm=Ksqo 1pOBk_IjErtRuMKw$]R!usi\U5n$@7׵w\ԓZ$Ը?I(UXc[y#a]H>ɱV!F*&ŕ s$nI[j\+Pkab̉1K.cK'cFrab1}RT*cMʱ !ۚ4X0U:ZIsHenxDbQu1i~Ii.Wu* pىONZ+̵BdPUq # N]+λb̌QKY#kʱQBr8|:Qa]u(:a$ ]qljs˜In也\u{;9حu^е`:/h&NXO#=P0GH$N]O7yN-HO7@@G)6&*,Ӻ9!$(y{8(pH<2Y3xO@I9P3X!B2X!B 2^ pCI5P S0$[ĵQ% Wf5p~$ BK$USp J\$5TW!*0~SPD\Υ, ݸ \33\ \?$"D;'_K t5U?cmEIENDB`ukui-screensaver/src/screensaver/assets/prev.svg0000664000175000017500000000055315172041035021102 0ustar fengfeng上一张ukui-screensaver/src/screensaver/assets/qing.png0000664000175000017500000141110015172041106021043 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATx{mU͵>VEB $!U M0ݴ HҶ-(a2huvc0nnMTQQGxWQI%ʽ޷n=?Tww{Y朂mlcO?]Ͽa?^~h|/֞5p h;̽i R j)`tX=D ^,a_2 d\*Ks"y~Bc` E :CDCʐH( huEyuLR!t$;L,{W)V~c;v?+7cx lc6n<~ϝ9{=z >Y/_U>XJqfi~Iz JL_ P8&͞4dwgy~":''zoPe,R0 pm1ԫ uFvR ZIxi^@ 7LWlmG @1Q)~&L"/h\7?kO曥?n6@3z{Mg>zXp9xa[tf(˲xZ-F4RFX#ń3`LiC2Cn lj f=hiRvC24I[ELg0B1yZuN%3T'e xKa&vtYǏs^|ݟ~kx: li?ޏOȥ|/<}Ᵽe1Ȑ@I\TX'{,+&˲a:Mu/ˢF_}6@nۏ}Wo x{X4Mn_cLE"A~r˲xAՓEVZ8x`F\U V5"z 1ډ C݋Z :־ZMQ \+/x(EP (VyqXJ,] ECף#;i|`Ȣw=9Z40U:&\yʟ*@ni66xmg ?{G?•#H >+6Sżg $0Կ爼` kdE~uz+ K2Q&pHM?lǝezZ ֔mAM-P)IȚ c fXtX]ON,HLY 1f)&0Xzʀ^!LVqҺ kωt(@ZUBU`ftՏSuh%3MR,k)@{yW}UWƓ|laOCq=>G/_qø,Em`Tqx3M86cx,_DT,h]wB;)z@7HwE\DB(H=w _Ů;EP k8se« rtK/+K56Y".Pr݃Ddv }4DhΟx??;u6mAϣ9,"ψd^%v!i-;@do_W>i (9vskTqir)x"E),Z-QRHX 9(-o=V^?SUQВKT/+]i()SM3!X)tXKq"K zI\-yB͟ŏ<5~'6m<ƛnu~]qD_Cs'Y)C\k@ 42 Yk y0E L=4cg\=:rޠ1iF Tkj3<91MD"0VEDj 26Pt#K;,Ntp(x8 ;"pnCAXD6dV ıЕdu[}n+F2օڎ;d&,̋+b}My'm6>cxR~w>w|u%tFX7z)m06oYaU,H_q Pý֚ܓXEXEPTK2X@=m #{>#&?CU!bFLAfIyum!U!X1Xn9kY];Jd J, VZ\eP6a60TZk]#&TT8 *|}yuǾW1ml @Ƨm9 ˏ>}o76>=cxǛu3̹^q/5aeB4^Vk}3(E[B/)viF0h*0;+V\6V򨵬a)cBAc5R5[J!$b:/R& UHШ%',)!2:dfqИRЃ!7RӖU"d@DB13?Ȭ?0&eReXѫ|F]!@;jxW导7Zmc6}o޳ԙs/|_آm7Zp^z4|a%֬lpx} Le~냰XB͚4uOkpΆ3 $RxV"; 1<3XLڃ6Y{TyI3M}iJȡ$%Z!80H<ДŁBiL&a iRV[>k OVfo2%a|2tAsͿ_SگJQ&l &|}[$c#+D 4:xm55^(d 4U$#3K t LP-\c5"<9FC d5؈b 3ka]Q߇ѕ?~鶱ala޳@GbjZ  a!ƛBCQ1@Sk s V |H1ihHsDP- NGsnZcP ޽^8fN~CI > چgiƀ ?jՒTÌ1=h8w5{ 0K{ˤp1u3 e)f"PHJ=`IgJ*k@,&O,Zhr 5:]"$e%U ;ëpC?9>6 lqo?]>UD`4 :/YҮwた8=zjF3mޠi~i!ʩeM2Va{3t  ׹X33 .bw9Mzjst|zANu~~/ūVB@(`[BSO0JK[( AC:5$#e"k: qJMe&A;`iH ^e{PO5>6Zs.!#Eơk jRZ+i+àѬC_P VQ֣ $^ ֳs6>ɱmm7ӏG{a`0RA@ r>؇1v љ"@KőfȁS2F':Fش4ufؓ$ď ^5y!sjH9XKOG4=ǰ׎vB'1s")E;=\#O{: ŵ(HSs!qL(ZLhXUtEQ&&CDA=w|$#)&do{E/L0>*)T&_C529;}ם?^wӗ6~Kqw󾟾s_pe¨V5D2MqBU1ri.4.s1\m=(ec39$.7XM]QsUMB`ր ƌ  i y|S/!Q/uU,EVc#C384z'Dd" ^ٴĦw Ќah FIHs!VnFlvMEͥ=-s𹯽K^m|c?[_[{ͥPABy+00sH`m5}T?뀔VXv7*ٮOpPuHign) v0 M! j^>u|S/Ʋ~=B C{KXzi-ZO?Z4'H| aB>8\{XN@ ~ {g& =rs +8@G(C< _ZS&X2U4s *^YBu ]8@5iN yYOy3[_R'36Oh3?}ꁳ_teWـ6ks@kQ?7,zEPONϛ o]=@tB5LXUyj;1Qsʄ1/S9D^~%)aS8gtb%LRPXѝg+p2?bƌƼԂ4l310u ;`՟\7Jid)sW`))uuy 4'0|p8Q dsgIGA=R9{a'ִes䃟گO_m<7gӏ|  ifMF$+A=J={Y ~:̖IN1,^?^0 z댬:P]NP1gx"t옭wZ#x5@z,m)w"(l=fBA3!>¥2C2f&Qc"IFUi#`cls-+CגbH< pT Cf.ŵ'XWDz/α/8O6 a: \k!ֽtM{ ի >uyM Ng6~~}/MÊp0@ʠ!dȥ%7CFc҃}z[EНzc08˲`gilx xDY/@i zˬHTJqy^4AuɮM4B!@Qo 8c6${z fԕ(h] IDAT* KeFX {@J :#& A#`+!9 l1N R:9`fLPj5!*UG-g\C#=}l@٠U_?/6c۸f7{O;~=B7/XJy^akY'74!s6&$3= 4rH"曪(CAz8OP(R Zc}j1^@C#2X78ahI"NTӤ{WG5#)bK"IhƑ6+ ]6k _\k"Kuph\HZ'!)Zcb #(Ƥx:e`zK@d ~xv؛"MmqSa9T Cx)~e]7X^MP=B4xf| BZpNk;1ZbZρ:bLG2/8 찢cz f#ìq $E:~zP Y %mY8/bA"\dQafaQuYwmAI|u.@p}bⷽ >x& lo]wK+ꛣdєŜAU+̃6 UCH GkvV#lMX:q<,?@Ței{E? ?$onP6G+S`hJuH ëf+~L`O,5"gOyeMu X[ƆV]r/ K\iCbKT F{j ;LlE66w[TQ8SE XJ}`J1RG@![XyXU[g3xf,<ޣZ=z3]" !T73raZI+h e)7@z4BF 9nZTI`5af.,zȶsrHxk(l~wHTT,_L*8Id>C +8s}[xƍ <~7GNyҰҨME;ޕ:_kR iJz%Q`vPiYS.W *H叮mĹ4G>z4.p fDCiX W5,T0d]'h50Ͷdx]n:|%CõV?_1ÿJW y]R!̀ŅbIos; s'?4V3:jVValYnC)4p]ZϙwKl) D``.Η+]09n֛BuZg$ *LdH /fU2akYRt}&}_~?pݳ+y61cϠ;w=5y"0AE`)7&^+Fۢ'c`*q1*f`^88vc4y6ʻtު7U45K1[!dnx)DY`̇G2Kḱk"g-$LJ<*o2ȺxuI Z:دv>z%Bl:%SXb/ZQkY! TV/Su~DQivzxkڀAL8L G T?1O&^5ƅ>{vaTX:FoYbg4_zU1ͳ+,I5%E R =B;0+mqp`} xwT)kRI?{,{X<_20/-uV,6߆A,LxS.sd {}i uNionpY%[`zbWsx)E 1 W}}dس^ŏsxš!BLI&{G?|Ͻ76㯿'>x[ryXBICa!޽Fn"^q`8Km@dUy]Zaq^QM62ϟ[IhI#q6#ޭkf>*Ο+C+C=>.^OӤl9a?KEApQ(FȜ]R =2 =i-R7zGk=5jET(n-Vq<}/=pD==jZ+S /1z0I!5-Y&H/8k`PW1FhiԋW`VMj) c}"*MЂ8kЇ)@ a97Uwgy9C?oJ 9v 6yuhY1Ξ-%6c~,Nd:֡qt+Ok=:`+e ZM`}RAAY>q߀m<-~cw?[OJaFۯmjDнb5!rv>r1޵@ 5όXTD[K:;.@( P˙2l#l|XC`1~L]mu/6u\KV4O6i(=d0C7묨zNI`WK}ۣfF2G2,@ _Ioh"#vYhtZ5̞h7aze馫$Wc1]uhMkLz<t`gehA(u ¨FLgX VSAL]`&>Zc>eq4%֊rhƻ {Mς?"ji>,T섳7#US[ UxZ/%Y.4l#O5}&mRsJ:8,vCw !I}w M_?mq⛿Sdw?ힷ&0pϡwppJ 9`z2ͽBNK.M8A 7cepG9g)0.:5 H}:tKӤGɘmVsaP'08I [Cc* klBMFN8a Shx5qZT^T$x֌Fz\:KaFqf[킺} NYEӴs%l^..55ωw;m{IJ(Y ^3bCet~!L>S 'Rs= j34 ti$T t2Ф]rKCEf.}ޏY\Yxӗ@x ㍷|ϭwg/]@ABS0%5v5~R\Ec kwR0 rG5Ӿ"c牡5{i],eME^L䅎hRd36褐~nч\ ;ѝj@:"}C. 7ӳ]E$hEקyw CDlيl-h,a:>98 "੏5 !}MqO xR Ex"Xڂ"h 3ֆdqMY+4p59<@~=)"@{VBU!$Htb(*]q? XÑzdE1o:9Rr_ `e[,0v8(1k՘ϗ=WD>Z,IY,}*Qe \w3:C@VMִQBL@Cn .v` 3P?f + [oac%5p}Ljs|'WEƓjlI:~w}O܉w<0B X|c =01"~8RP5 WSAڙ9qKx>3J܊n`^Y&B*ysg@wz~y`HxT͊ݰX2(5W%,d_l3 [įlB_< x*bAo{EcH<5 gМ@ZiX^x%.I;P2?lEt5ƁztM2iS(vvFxQ ^ iMq:6h\X'̳lXj>"`i[  z{EC6wI|D{ N?xҍ-I8џçx@4cC?V?;|6pn LRH(b%"\ƍ Sz$C^ {[6PKUp:vo)[C#P ׎PQ0$BW\#dDX=+a oUu+ ?jtݵVR=RqDPF{23ֆ.hU%,B4ԧ/;i`j T.)g1k1+f@8@sJ3Tę1z~8=U-=d DY| ԓ#?gR IDAT+1mÍ^9~;O''7|1oiA:ʤ*i!EeIoDE4`)}O 1Nndn7H (+-6"}1m"=g"3#?[hg:ŰX RCO('M`@) keШe n.H5BC%2R%zcȚ @k6&ocĖc: e0 +^lI36$?}ϝ~mw~I6Y( l,*h`LYڸM+ ޮ؀f91mUu Xe@HkQrC7;Af}\0&[M8{aXhرV&9UjAC >*%Eq̲OBOaxbۨ?p{\ʰziZ5/X=ј(R[Zb@4z}ş;lWRj"\+B5,tŏtUqS՞Ii|$ vJK K\{_n`?#P(i&D"XCC۟58` zvR{%7} c'x{Q̫wl`۵jfI杽A:éknޭ-ucRЖu@R#R V#} 8'~YvCڠ(R)Reԉ&VhB I!*œ-[{K qA!f5J’%g,  Z%:<*dE @>gңGA"&z?}W5Ƨml8wv Xӷya\F(a^VL73vzԯ GKif ĬPKhkYLKa18 vn8|L@V*?6>-c˧>/Ƭvg|rӜ}Tj@ #t7t_1pzz*b kh¶ʠÀP~2Aу .!`]^*0w$Ɓs&Wd&<]1(5jeaKJ D @`a zdV=YI (BKEzɰ`FvY5\Rlւ`ؗ(l'KϠWI<'=ë3j+~[czL= Z XNlL3_MHplF4_;cc*ZڕRܓwC\˜6kM^y=-dw }X! A G'tl o׼?|'ٳrA~Y q`sEm5/i?3ZXHgOࡃjjy8,FDH8  xLߨ@dK @za#/B;٠-)@Xkh:dXZCDd"4kg5A1&nU(LՖh]͠ Ì [/Z'B=1Nfޣt" <]ͻ^dRE2>Xd9y0IL5-^ kTYռ$Ah@+ʼ D"k }NI,Y@@ZJ0z!`(:U" 8p` 1/؎,1w)}q;83?7xnO潷<г3՜p?kRe.% 蛮e՗2ԓ;FtdҸzzw:!}Y{NdC;*x+%iΞ~)^EuJF7Au}[СYu5+RXV Y_yfWr&~}oɶS5Xsf^9,Kt$jP[G8{uӸ1&Βc\VD Y9>;ei0%ȔDL_O- y-pHVܮX&+&-3.JcLSgK s/U@N{Ζ7ƆF.&:z7 t}yL 8ĈU'"J0Y pfEr3Sbw2> Fz!wį앇/G}l"OGV;,5bNgs]m}{-(!>y~SRJ̙eUD"v<6;Q8^Is6އskEa=1 S9u!WY,0 b!V *d?VcTBl]io.Ú7?['m?/ϟU|>3_v'os}l <~w?;zU|A=SSl6򱲷CtEjyr=8(UjnE\E/v㪰,fԽ<ܰ3=@&`PZo+cL#B?"S ТI"Ìj]w0_nc]Rq;ਫ਼CcZX ZM[WEtEP,(J 7uRq5u>Nz\{ch[>}?y P9fP30=1qeiJR$܍0YqHUKAU($}GiMuU,sZ" "zF'ϖ@̚`>rݒȊ|HYE\,0MWڳ*Q`o{ݍ6Chl5j=*r3 VcL8>Lz#0`Nd94c U/7uxCw=?vw'oxH&*pM9C×t T,uZ&P 9$Agݍm )zm 4Jd)1 |78Lcu?^b96zP@0: 2Yv:VǕIP Q*+Ǧr)~L9*^# -̵fA"UuL[M}% xφxuS31U۾9#A'8><PC#5f~n*KpsfN`5)*.\b~VY;$">Ȉ2"k B`Ew֙-LUdk1 7Ch~{V}\Wgr?{omV]k90i1v66)NcJN\7Um*QZ$JjԭD8P8 ֊2`0a`a|a yscks3;=z|^׺WlKa2p5T,"m~o41VaI! h^Ώ+kܾ~Om{/3 ƥ07#8q;MU`9tX!T1B%9̯&ug&C9M5U# m@RhSV7  И tc)eZDn |7g,ԁL.S:Em^ Lac 5 cCU]xppѢZL@@sݢ'kεU֣yp\2s\cl, OPeLs/&wR, @`αQY}G@i@g n&RIg5#j Ub:6Y;S{|r>c~ܾ붉p/?y ]=xU@-B4sJSB9ϺBTYA` /I$;AzpA kR駫t2!]UejliA2FМ޶\EiL <-szʘv!G4-6ή1.,j4Ȥ-̸/68yb dr%*5RP\7[/u`pkNϊLL7S\^q¢qُ5u`#U嘚YJ bx$fz+ Ǹ^S9\Tp=#L42~O?$L l*M7O?Grx&{POyٷj @6+{oy_ܾ~mx}w߰G)XYzM G ^CeLsu:9j]73Nzu2tSA]5I>@:IU~0GYU`< MfXkXq 'L IDATaP%4SYeRLp`TȠ3 !!~s`"TR鳐ΰWFX maBMHg=؜3*2*9 ln/H5bBb] 94ҭ1婏.$*C3Qfo_ a=o3ʸpu1<p @7 G6?3 |ɏEp!3Fj? up {ݫW~wnw}-|䱟0 m–?4FWRU8=H4ڥ$<`JmJsND\+xjC4mYCpN}?A2C^3%Al?F06М1IJ3Y jC5H֥z.,яcRB(x9[ƾ {Ap:~=){3C t=Ϲ1R0փEhNI1y\MZ HWv p}6&4[1u=וm4CH KX㐅 [3 HyIk>6`d| ؘt`pB56 s,pbK8ks>K 0|o}cW/||m-a`EXWLm=OT=%HUΨ>󪠧&Enb/1朐t>,px;RHjb/^)zYh?-BrA#3Z| Zt3li}qA9~zqZsi`R hۖAagc\Ȑ@)ȏVS"$mՃ8m[Pࡩtc.NATt ,MP5ZpK߇8O\cra'(R{ɅF,dHڼ>\LY%5Q@"0Ad) "Oݻi'2B%jüN|Ӫ$L1G̗F & 59xy,.y lӁڒGM1VAuE^-Xޓ򝢘>ؽ5_¿?[yMǟ5"Nrw0?|LP{1E!|ñWj=#ӶYΠj 0c/Ep9qpR$`u&Q#>jQ !e3g*#<ҧN}_g,Ꮎ 2 P0)Zsf8@5?u$|m5^?|=}jJO״x{|'k"$u  pCl|>=)x軀4}+1:1B hL}6r4pУ}"R~h *ȱ&, NҪ fOv `4&1:6sdTA/N'2TH!K|Jiju 4 QR\( L M+͑6ƙ[mR7} .c&| -$p.̻tem!ܧT>:mi+*#hbOaH@P3,4d"rk'zo>1Bx]?GAܾmU{?3@ҨGp U2@.`J,1aPP\#[#6CHڟӕ".䧆fT{0QPjXm%Y M;T0OozdegK&C %!tyP l8T7qe,dq e.V# {:*iFg[3$чy"'Z Df -bFԀz`=4$=k@I)%bM%A^ |x֘10im Zonf,f4{9Ы-q,^uQX&k #M<FtoD FFΆ§T*|m<9p{  o3z̑ 3`zܠ7Oz~+v=HnY+i،b fY?}~pFF-k&iϱ_ G^֐KCn__7~=uyQf:@+#+ѻ91wwᇘ"L2r' p(4caާd4M(gG;`Nlh)3ƟjiRVJL;ii:K`kbʹ`.Z@G i0GhL2[y! &Y7>U*ժM?@V鰥sθH+p<6_6%77@f ft%"" |s#]r3׻z{@~HH]ǶЄ9Ň=zD5' Ex F~p.,:%gbp'U۱{6 ʏ{hvp.`,-?Cb:*<bF@}%6y>O?pr??RȌx($[Pن (sPsZU޷oLz/|\1IZfA5o\A{|iNpL"TMu+E0>6 [2JȖ(0ۭDN5Sd;34[X ǚKSڊB<%7thi Wt@fj! : 5B$ kTkX`^ `= b,SguDZE~.56A䀴 xFsQfͅ>#LSZ"P  R픀2zLUsZ~T;hu=9=]4C-օB g ZϷhEYEjՙ˃RkʓgDE&%}XSCqӒtPx$Z Ez9hQ  9'NLo10w+EOC<sl:ϬB EMXyT bySKŗ @P◀g~u?=!ܾqһ?x{yaX* vd@y)Y~O{moIP|n8@r: Lz^tڎԢ|֎|08XڒP9jv& &!ETҤY cyEHoIsͫe@38j:K;E 6Dv=a uؖ*Pi6Aә'34Sjq\ a˖'1J0\gx_H qV=p $,Y kͅ16kYP=MqwBw*T]5?Vmz`y2 -elE"A.@X}+dTHp@Y: ϼ>y_+?o}Q ~7z~p*YT hX!8-EV. <츃r U* ?H$K5y pXgFy>ػZOZt*D-YRDS܌5)1VEXL QD֦u^-{@ `д~p]4T0+N^O 㡧_t_m|ǞK= {8Z(c4.C N22W}ϠdJ^%:TKQK%o!> CM !i%p2f1\oӷ%~j~֙SeLS}(B|9g...+ϋKBa=P@ǣ&I[3Q&``M U;,R :J+c/tOԷ~зShThЂH_M9$B}i[ ͱ=NZ,Rj!_wq:3{k-Q/jaFiV1O;F!s[+5:}Ɵr (smht>& !CˣQ%ԛsɠbERFP%nGK ^D_3~67ZLޮ9JMWmڥ֣í;8r:lM@+s1LSCgTf 6o1SYxRaATe>٧ 0#k>7(|[w=u}\?rk^?{z IeCfWAuVih:plnu m}뾙|W|^}ޕKlY*^R8H|0RW&$|kpd ٔfbNꡋ6 ;m@msTu9(%)P3|* , P[7< JiJmkj8]żs(+5" l(Xh axŪk0В̶i-7;1|EH01auȼ%0@[ R繡づz2'@RNHfy eq@ :]9.C-ĈyS! }\6wrH‚#^gs*HeBLm`,A$lihDIr_7PW+ 6y,?ǏzgJ%@_}Ͽgg9 *I5U!(Ki)&3X<%mϣ/BZ&;]QciV^PD`Ez+t$S0%S&FU !%YǎX{@(L vB'[W񵍕>jq Cj5s9qNby5] 0/coX79еt@$ݲՂN TBhIF^jCKZ}h-f#GJ.Q(*"AB+qrlxҧC!_v~9ՔC@sivjih`A:Furձbbϳ߭ETf!ff1f`Zs4uY\)?BE\C\:{ ۭ6ܓ\> IDAT_OϳygbcO5QK7j0Ūs2&VWTW1;#zPP5|;MnvK) LV+BGz`)T{Ԭo3ZXj:hjj>XAw>^|5@-' t?1ο2yotPDjߍLl&)l-<"ȃHيI@=هзF0b?SR՛e .Lk ]!'i47 إ}ߣWݫEQL t kEh"٦QCnk@0 hK{Us7H?,NG̲|iV-nB*c P&t %:R A&hl 4 K(+S^|^s'5ʟG~{կ+cx+_o~燾}Gzl#n\+* ^)Ͷ ʁ!»8 JVN -ЖE- *&AsIoQ.ziN cY8*ӏeʔ_f:\y1:58QV0!X;OZ^f`g}Hr}m'/Rګ3M$ye4 &BSrD5rVg55Z]8  H713iSm 9%7 8AhLbΣH1׽Q Z yu |i,4c\1` k*y.Y)sDd=Pr 8Oyy/5T2RAQ"xe]IABg¤:_ֹ:X SOzїg3x\+=翁_^^a{p?RWch~Sy jzXŃ؍}.Sړc-tʼn)> 0q:1$p4 φW3%@8V8Āk :thRИ H;ҹ0wj 4{1gPˮ۟ f2tSu>#B'c`@̼h5^&<iΉ[]VjVְ9V%=ϡXhWL9Ѣ\91T hCO`ljZ0xfUE؛&mYmi>V WP W(vspZsЊ;<_AsX?C' ; ya;+^%/G 73Be$ !zoۖ9ڪLkD@ Z43a;U*|&x,|yW.91O`Jew!LA2Q Iym]~%d굘գԢLH"xQ:}H( '{^^%=6b9ig&L_c$`r?o_n;[)#8=h≈euQ @PsMg@Lm$+` TQT,EjfFU;x;yހ7?_ijZ&LV)֐P'GsnF\u0f"3AM֮ v 9N~t!0vqi:R6R!ت栣9f ȥ@3+VdJh>9? W$IEҋH'òwdZkKD H0םms:8і D4w &EaVD҄Q Ǡ?ב)?Y>c@BJl dᙕ& V?ѷ?[?'_f>Ԥh}LZuǴf{417ԭ Mt"*0GP#Y\M![_P#IJ] Jqe\qՁ#rԘT$4IED4sJbLB!x:520,ҵi>--QI/:#(br+ Rj@-Aވ j[)zk$ڢ"~:m9ͱ5!D6lG Xs]Y=u9w" ilṯf#LeH'[r4q6smfrZ!&8V-/4*텕ᦥBO R!hlTƂtWēbۚLĽ,V$u"]12w[I2ۖd5)CU+lh9HF 6\y`u,?76.Q%pGs?G_ |떹ni|vc?/i#2v9fѤʡ:؄k<͊q2kT!T4@[{[H qv1d*yAܯ1i(7M\~{}6Ui+Ԣt:-9 ܳ0@DA!ʆ{P&\jA>s)s;X X ٖ1ghT5 e0E^ァs"ZWklq֞ŜrVgޚa"v@Vhc9O+…| v[tJuD90OHQJ?i S. hfJ4 p`WzP*5qsT$$}vz8O4Q(-ePZuoƨ>E * 8( QG|C|,Δ =gzSp^?Oh0Q w0#p5)0{Z;`Kk. ^s8՞ΰ/^E ZR'(X.?^йjG!Td9 f?pQ_靁C6.)Rd*uN4XվɎhXo+@$-L8A m1Eu@=*y\w fuȘy˜s+` uMf=><)f{}Ҟ:tfp(\斿9|U C+"0"ssKqOu߬.YD*ur @ F+Le/NXXጜCk"9aGWkg&0Ff*! MAfj,&}(`XrLw`;@wҗ—=[^"_ޏ=MۆH WѫJSXXVE<`\Bp£@GA׫s`sW߅B˽i\ I'HNVZt! Pи,O@kڶP SYk@nϞZVa6uDH=h`@'B9@ 8txo}+Ix^׀?cLQL՜u iop\x+MC ,5;ݺ,ۂWUf_P~P X+eA L]GO_W\ݒ >\(ʴ2Dq=~8ȦN ?]Z |`N`JJCCΕY(}~tG+M/}ZSfCqDZ>2-dWň Bvkypjyzk?3 X?MO $ ?c%JlL]KWh[ܖJ e]~hj>q6d24wiyjʵ.cH 5ϡEL[Z j0J&Rդ+yJ*$®^ "U1:KiC3M T<,yZiaL]Ё& ;&M/f_ 88ދOA\ofy??ŮTU>/W͓ 36ngV#D2t4 iNcm~`#& @r3@_=WPAmadA U5iHK KhشR2 "d|+xv:+6R";2V7qO O_d9]IkHV'o},o^J15 #dxJ2v)Uy W!5^a^A;@#S/DcSZ;ݙ v Myyl 1F PLL4#%OpZ;3E > &Nbot[ NX2[̟t ̞qEnA[As5ok(l <ǽR跉y5-G2Xߧ05lOقq d= /^1UmR7uKO>ٗMK?ȷ^թif_G:tSDp1h݀US3Fr,ؔ𳃐hܰposa֌p~ HeTtd><\KDZb)^VAY+PjB<5( Uf{$sQ-7J/;0PɜC)5EDf%`91~J[]]*LJCS5'=5϶8 [ėyiVHHCfs*֚༳Ư,jfcd`k Lsvv x9ȠVsϘ i yM\Gqs|-L־ 2 *ܒ6 4̷~ w>/-r2&~Wm 67WxӁi-PK!J)0Z6Btr3anNd9^Ձ W-?ikDkf%~pL˭0]PWMZ63Q`TUgIi;N-34%pPFVC~FAكmI*ߴ6`  =lj6 cΊlk55A2Usw5`J_Reӣ"r?ǂߥCc8 $#{5 c98xBJЬLϹ;s-@#jfs9&5vv|Hܪü#ijv'r 7܋fAG|Jab"oDCnbQ=5Bg׳--!=I@Gw3T踎 9ߠ`x<ҐWwRGv{=LMK}:)KtZ9GWQ`ܡZK# u:S_׬jvs;դY"w:!gK)Qx[G%CO7;ȕy&RR]{ ` UsȤXkPiG;3`y3(5dy#&Dr~E461,}}Sax̞֘rQ@袬'3#EJېG#|+eJ@@iu;%GqS%d-@`牺%H¾*ǎO ߀@"Η@e*b@~@@T3qԐGERΟxuKg[bVg9fd<!7yF&K#2C Z1!.6a,~jU@9qqqaLtnSX(HVRyTؤao}kmCw`Q7O9)a@B H =u֦jSTXaq&es>xpOXvHϢEGEUc;'?\5-.pMʼni&Ф}n\YI +dƢt0槰ڱ15P_7FkwI^h6V2椰ea!cAD.Ü !%f>? +D24:3E~mbKX40)EHI{[K njDm4s MyC a*|ezSJqS1[ǰ~ VOuԸoYJSy>5b%Iy ;߷~My ϼ\ȚWjW2ӎJZiKEȞ-m}/94롶M9WS'޽Fh k9}8G΄$x`)#jW>GY z_ͅGԅߛ79KP Ž`fF>zdگE5U$6w4Ql)3@:Zhf /3|L`Z_^@ >V4]MOE玚Ti5 wE2R`ۺϓE0L3{ `ۺ#73y,>?j1J%ɓ '0MafJ9ҁ6qwP8V:=o,zB]q 83%[ A*,n܁â[Nf, /Ibƚ YF_Buk> "ׂk.UկO}O?$fO/1UЇ+Bbz`S%L5პw{:\ӵ)d?NF^Ԓk? IDAT+@f<8B%A[C#yIwgUNsJZrh3|X8v޻O+`V0 `Sr\rqFl툒ŦG@X;]2$ҁR\Tpf %eMgk " Ss` g;YKga݀uNus`x.d> "/ mt4`D֚דg6%ֈ٥3;edIi͚-!\3 kS$f`&B)(ؘ,XD/0p0&st$S;Q mbBgaf{pORk0Ua7nd8@57p"'sU軟+*;3sA<n`^7pS7uO_>{s~`sT{1 @o0; Ýe4alL zt)XAS7|-4!Uō7V;wqN?N# (~8rh 6u@}o)"3wyoi_}۟zܤMy=>tfF\ h(&u04L=BopMA-aȔACJC[Ty9hX@&9 #imЬ|hsc4崘R zA6C2q1l ?tVe:aͅCRr%j̙ţ"4tL)^VX.5yAip0"P z8I  MSOf*@ZEz4Rd7~ Ɗ#֍ju-z !0 XIhNFZ8Y Fb/6z{k[Ǯ Q>Osڼgi \dHh玵kYC'NyJ5mq(.eBFEȢȇ0*E2$Ar*EW (}x]sf#EK\7 7p)?ڄp8g .KiԡiO FAXRz5׌B Dh[oFϻM}'鋓#` NWJ0TM}jR*& QC#QE(B6}ך X-F;HBpv ]!H R;lƾ.YklPheM* ^Lihdzϼf LA`85WLP2 [A b^`IA5ܱm(*MWEi>d"4zjG,͹rV*G? )<3e1)aP{EqƇH܋T:NHHFiX7~l4/ )?kW>C@[jO9M8<##QM;Oog+{t[/'>'-4e>aGgh(Wv.xo(x;XSkMWԀDHPdl///m-":*vsaJ#:cKp8i4Zc Dbā֜rzY;zD Ҽk*Ja7_g ٦`W|\K-Θ,G3@g pxMp%C!u^\wwq]77̻?9Y;_h;)ֲJ` np(+N!q fB5ENmabp;iY"(ǜ"PS:z!h[g+U&жnG풶XFDB{ Yp5wyI>Ћ_ S&n*oCۃ?,#Ԑaӫm1!ИbvICGSv Д:.hN?ONsc >F?+~4t 2>lDk |B(G;&kY9,3E6>2ߵm+4ұsp2'QHOm95`Q`{hrfϹ&- )dkgwV{qlL3iW@n~Z}9 &Yۑ!Sj7.o`ߧ:{k72*j 1LQ3ab#Z={dT,6L,mĞ>y14`Q!5nr2ݹϹscTׯ08CnTdsX-"R!j' G ;8}[)xU[~oc<ԝs_D̨9#3hM} u`Z^ebOzR[<=8hUc|5./s'L #'M9'َc{"%4 !м|kz}MfO~s#=/sUwܛ( B aŁu!46s0fŻ5t[Pn@ҒVVeb,akL\WLi9!^(rgQdm;4=UsuI6&z4GP_W}t<$@ C_AjG2KR䤃SU((I`fwv2LPq({.-X":&\\,';,7:PNdId>mq5<9*#hhkӼ^dxөU osx&o 6D.V"ч:xc栾FmĄ\Α+!YGI)jϗC|O۟Mp4&wo|?љͪ:YRf*ICN cqH֪&zy!E=\9QBL]tC00aQfG"N0$ \'PsyƯY{i^)+HjM{iwN5PƮ޿A #K3@DYΌ U슯;r KT@e@Z-C`iwX+̀6RHˀeժx H! lFs[6-m>3Y?=AlUV߄T5vݼtA8?*Ca}ieQB д0dd k^eo<ﲅ"=5R0 Ò{T|ERmh\B0!'q휬qxS~OzߒZY4 ;'^1ihTv!ň\$1#<9q=5fiElw'/EE͢^U|IAC,Aicaaak lNj<b:CLkiZp'#8jkS+r(f_ΡG$ S:2NVt& XHKA_ T|^d?@I#^s-N\2SY,gJ9F3x(LTN6.}{S*y.g mkTRpO\3&j7gɓK't m#h6Hıf5X~zdjNuO{1ЅX`adC'Ͻ(ѕp>5NR[M^sGܼG f@r3`r7? (&M1/ ťZY$9؊)>B|A!KP4ϭ1S_550`6I/?*oYkȒ:ظt_iqxrٓ= &( >+V:y'1v++DHsJG~ [.cy*Ŗ6Xx*8X)f0<8%>?*-7/bt0vA턩09gL_+(60T\/:hK兂kB)U{D A!h>S捖dq6Ԇ>he IGVfWFOk}tZ n7.-Z˘D^V:R+dAV?Zn%Bh ַyVR 1A ׸BBK&SɄtHր9Gb(02upqpg!\wIJ"o^R $k[Zkޜ)AcA( b<ÿ\>hyLw}+W_szN}9JXzPozpSHg^cNQ=iI6OoY +>T{z٥U $P@3FȾW{zuƬ9›[V@3hH}$e֚p \GVD_t.߅q_`y L8dg՛^#+Q󖇮, D'r{鴁;{]dpXܩa=%#y݋#`+l]Nͣjb_ֻ̍_s!7v1W257g 75Tl^2_IN5gIɎlp},n93*ɺ,fal΍92. +O40ǾY^I;Y35ը@C; ?u[pHLg~R1l>do s c!篓b6X4l!ZPٱaf$u+[|"ؿY5Z"H6T4Oq_ ?E5'6i= e:{-7$}Cj}UQoQ]z^v ʨ9V&~̌4j>K7ISeZfK $ ΪHG1Zl[DuSLto(&Z7 [)2,83giه.C$Q \Ak;h:Q%H6یr ']9 Г#O8pP4"d`"j&`: Mv1x}HQ7kgo=ĩy:Bh̡NX5T>#V(RLA_ jDvPf{ܠ-pX(־M:4e9.ůó:RGđVk:/Eq XW y<ϺNq[Gvzt{oE#&t v0!cyUc!Zc)1uռU4k%BkK J&TR~HYgqeB"=(`JF9^5*?^ܧu8`ΰc.̆z@qdD sX8ul=\0!kP.\ wn΃1zqbGIc7Ni! z*qP!. .zTт" H  8m@ڤP6 %ΡvI}>g11\e~]k!6}&&^( x?N^5@7b1v-y)r~dJ=}w_z'߄7e/엟=>!0vy;*fHl1I T+ct^+¢R&s+}:5+2 X_ .p_ &ywMb?qsДbZd%L}&1v*m`!GBO-kndM{2k s58n+@)ӵK.&2܄(gzʚq >x`5@*Mޅ;; IDAT@2N萲ƚM:~j( -[\cgajN*8*{.8*4hmi_=S.)߀- wO  u >2B26d"׉ Hi ūH#DJf&RHJ4ti q`)>,7O|cR#FJz2Uw3(3꼙JY#, lb<'sǏ々1uT9+gR+O=ݼЅ)K { HMN i"]=دWiqQT[5NI>ӅqC 32@eGds^f[i(w]aeѹĹ5>MQB{Z (Bq@ LZ!$ҽ6XZߥV5Jn3P5Bqn@|?AԼH7 6w~y7f&O'+K2 &]K'cqD|\Cvxyk{7ȕ=rDʿhu 3Ui{DS2kw5 ۺ;d@ V Unxx c&y v9gtY <&X IլNPv݃N7z/=:k,Ag~?j|8GY{܃ft=BLf?eฬi`UB}ևelrwזsZ5Qߗ1Fvz,о_u^x>]R/uytkMqhK$?஘*\A][ι!\ f z&IO[6 +3AN7 :AA-HR7շu;iE3p)p?wЋb"PM} ~0.|G/:|^0?ffoMb?K?|0GjtɶIBkFǼe6UȺ5j}iB@O͌SMDX3;xQRw>9 ݐfl$xl ~+K4BfGl)!8zB+}N2O= 1{EqNߺVJH~k#lq+\6yF fCe ݂ű(L +|JWChZG+A Q9zfumXGKQKE3#\O(4}"m\Y=neF5Ĺ𘋲0,^fR`Jgt UV%>ȭܳ- Բ"X.+Auwg fs HjpO~1N_\zpn$n3qo6_=?o]?[׆*+!*Pihxs@f~ l&0%.En%2&9u a.Yw/ɳތCبֿ _zЉpP{?GY 8FXZ`e֏ܵ|P}<:X  hA,X rLy|EDĚf+o||Gi,|}g\F jTCUkF&5PU9ʀȌ^#qI,(#ITkY -)A~+co3iYM|_`_+ AAsDY&4["z|C\-2!M|FU ï[E ϖkI0xVZ\M`6ph*ibi{j{1C%Л9j@DY:fv; pHGCvcfN ;פf+=Uܪ-dVB7|J0#QP @,;ܿsfdҨ R.4^;:ȟqhkK}{l!T~. 1R`BZ5C$_Q,\e8&] \g{89+%y֟a>NA5~?5m9/>ƈۭgֳ2rdZpμH )bO}?5ޥ f1%f+V ci nv>bE /^fɑb 6"ȯs8=1Q; 4S_k]Ozt.ཙ7YG3R#CҶ3mls"woΩryyԒ {q%(w{:^]\OOin 2v&=?;]72FdeTeKOEm #~2da]< QVp H,KP! -%= ( SP9\&fez^~ >-"H\ȀRhm"H uO5 ȫ 2[=?> |ß3+xK0 YD: ` npkNt'Sţ ;PtQ[wɐ> 㖡#(n~"jV+rϳzOPY.';8``c}ƷIjlQ1,WghelR8껾%o? p ¯ !Xhdfs(h$S MBͅtM'J3gQ\~|ġR@#C͹x9̈ کw]g1#NV+Z$a82(phhwshkp`BHm=GT=~vWNv11LZ'MDfyV+j{]0Xz@${ :@ZgfF`k$0gaQ6J} 9J)2#ESuڠoʸ sL(:`fj( QUE+XA 2 =ZT,ΐ 020iN&uzdδu9Xd3?=¤CCjd 3ŗT%AB($T+TR5y{h|L4Sv S4A~tm93{W)~O` z{U &Er-k>xF6]ouP ~y3~=|,Q||M&Z4k̻ߣR>h޵nVݘլ*SvteQ#_7G2tXDgAj-1ݭ R>μ(Xxv;iR0֞5z/ -Sg8(`q'1\oo}۾q\{&5R}h!U!upV{ʟ-rԀ2y']gW3}XRt |֩MKZEG; ozJz1TCs GXˆ p[9ߙX"E0<'ǍF,ǍtM+72leq&h9IPq03p - |+ow~fA[MO ^uiv!\#RV`q\\^.ܻefbO#ͫ)p{[.\hPk`}t!1w"֎K Sl#-},ÜyV'ZT_M߁Cf}@%ke881qkG 0^ `{[q$R``97 %Xi=pDJ:a`:KEHOɬq*bC^S'`Qx2G>O(MRuWq']̶rI#737zܟYݒ25`9Cw"鬟 ޔM DYmJҎ[ZPII~)p^]R]-p#V qZU]х"``*,.~nnm!xO[m dZY^z 8W]o@w|c7͂Pi 7spy? td޾ $3 lf5xz5k>=5sOClEVUa)a`+xұ ]bAg| zjj) p-uw.*N`jOd}6cŴ! UKBD"](l8y>g|Cw_; bnrЦM*HtdLH7ve1X,RҰja(I]8}uv@wA-W vpqcS%@ZAf<͋V# dS HPHd{keO[  DK %5tE ۖTEMr3$?g vnɃC`6.9Z:#*9zU[cHtpc~O߁7zC<:Û*^a`K3PaYz'o - ][^^J8pO?J0B5>=ÁZ7U{CRP$EmY[ZSjfHʾ iR`4hY 99djE5 k{T8#aRmU`OmX/U'#Xg!sEiAm ^Z<V]Bn Qi=օtAz#gJ+D3 KSu5&J v;{C+DIyDCS$f=n0*3{` tX_Ps XQ}f9FH|?N0"c뉩A? bc>q 4dy!Lg+ȡ 4~z!ktl~W<5; Txʨ(A.~2ǔ>.#B <5A#;:/C u^ o|¯7?!<̓gM )gגY=19px ُj&@*)93[;ztr @m-?[J™̰7IJrwTi~|CSb"F`PsM=3jl{F /|-!޳2)-vKqm߹߮=kyL:1~U3dWE &6 sdv+. DFs]A#iTR%9T.X.Du51 INLDv{3t./b= A,bRTld.{X݊KK3%hKP)E@=g[6t^[. 4ҕf]ĹTv,+gP]=jhJ[}Y>ט {,%MJՖ֊;袕)IM0={_:_W_@OR)cz:9 ذ 8RrɗZ7C"g bb8@ՂJ$Gͭݕ= nb~z 4J~l:^ AB  c48,^ \ ,*n=f\i.vuHX<hB\K[ /(R"Qs`V1KD !U2]ς{?-_&Xk&@QC?ϥ]:Se $CIsmp`463Sޅk_nAH Z%f~afE 4 bi@R%( ^ u%nA & ]=yC^=$JsV@Y$c&*a A[%xFrAMJ/bV Q1ċ{Nj6[syH ekVˤ/10`^:׭kY--=xY$R.v-_ ^] ,hU}~ƑߥkT Dn|^dj IW%JV4^oa{{A*~g fo%IspOFjרf4!RZ@ȪEwM)qogdDEq\}6Zwm; [Z=㑍msyzlz/KsbAKF[OeTzj\Kc e*(chh![ `mgPub1-e8Q^BZ^f-R> CZ@D&GF5jh$/2i{ʀ e[WYPsNҊ|dP02R$#((!I'`%{x̃X}V: i5ᜇY>F1tPX9 Y aM R0|".;&v ר)`[}d2 X- Ն@kѭY !Ԙ'A ,! [:\OɢWѿ~ȕwWX HV q͕yV")x=fm˽n[sK32x!@d&knuY$j+<c!CU͢81nV1]`}&ؾo=4_?"g[j(d(}yٻ4~jH1Le1.ZiY!5\9q?}1 B!lpZVZShkiE3RyX%W^ESTK-nTBڟ² 65bCZGV3\˔IX:2 "H2 i)ew; d\Vҕ82{bua7ZBxP/"dgHtQ ؐE׺8FԘ8g=ȧ!4)#/eB`fԋ3 Bw$ }x^5 ɿ'r?5|y2}24nൂpl D ͏|킅cp@(6uL?(]4wT&-ԑT@RG\UM@+kVVh:?!hHcL9^5M}A_m\Zf%|g&|U‘BipLSutjYΈlUk]i#Hv*<#hY-@@AwNG3[XZ)1+ݙZ8"Oh!5P_!H GjMEdLM,kji XA KfI80˪Q0-~wr`ː,o!I5g)<(6"[OnL#_5ϼ/?>ҪTN2R}*:pA d(7-6Dchk1<. [3 W [?z̓W>jحfЎ5QGDR".XIi S8նᙹ v B$ P~[ie][umfMpY+]o.媱N~5T_P"@pmwAC V$)Ųd ZeTI1Pd\=#.35lZik^tZƓ}bn;L|KSZ0[0]8۟Yyǡ&ʓ-nF4 h2(!(4-|wdݬGQ(,]d$1n_=%]QC):*_[^)@?^vj0;rF疛@\ Z6xsάbXrksږ4a1hBSwf\IC;S$v{kw=yhh@{U9< k@N?R  5#%m֙! .K,pivtdRmeyRniz{p=ҹV>΄TPRiЀ]U18TS%B /h`CPJҢ gS5}ea2FEwa'C_iQpU-1)cѱ)BNΏZPSw/{+%tƶQs}iTRwXY6"X.QV t67(6,HN+ Q{#n5U#!Kj3{{_Ӕ4W?/mB*nӾoJgO M{A Bʊ},S4( J*d}W24 fKtcXUޭ99݀{r };-b?ωE|KB:Ǧ{Tjsj-~C6I_Tvʱgn [6*1M:ԘWQabT3Ut3hUL~P%%@sHYiRɓRjd|-+"3S,{c, ;~5~C4!\Cţ!JWU Z]xFh-E9u,WeW2FIŁEf~' >vh"HV9yC#FA|kbP7Ɏ| och]Z/ /|7 ^5e`4LWe*ψat*A,,#9R+SذzDvs6*NKD547Vr[ nNEbJK@ijApNcشx~srݫvDe+8j\>?Xۜ#ҕF'3L,{ 0jE}kOX!Hk0*iiym@@/vZcf*'7 b^5ɔ:y1̖I?\u(@E]6NDaL0=7iT) S*]~}0 Kh)ژVEEZVe"&jszDOa JlU59؞j+ nJH>yF)ճgϢЇ4̈́gWG в`^z <;XSV\80 ^ƈbg%E KLȂG}?^\j|]kxf.?c^D3|5UX)#m{Ѣb|٬kdc LOϣԴQ=AGgj' ^Rve ˜Js+xJb)2!rc2ӤF6&꧛օ *Biw hhNa f~yR vjԩOE %uʳĸMwdujL\\0 y=jn3[*pd`L[xw++cʯ`8܌~\8ƌ'Y<}pF5dPcHj6ø!@9?n&3ʆzl }crc{= !A[AOLǝplhy@1;AnB0bXP1Ï=*Ǚ; hwcx`(u"1p%YJX{8KbHH׀hU8MbܵijfsEOZ9@$-t <+@ Bi+A$:_Sݰm[ԗfw? ,R|K/~ktf{ apϹkԨd_vma@ Xa~?pcIPf9t틁==[$/{/v3zrS,)GccO27T&g* }k)ELYѽS_h`քF62f@P߇k3DGph{m1y}p{i칹I0Zi ˌ j=VU({-a\Kshw[}Yc5g Fv{a<6L \`IaƢ /猠BN(-gF㘡(ykIp+)Am&@ 9#G\hi }+3دfle[=uɟT`ir{;-e7[_)2,LR&` .دZ5Ks (sڂP-9lK͖#xܬ #l^>덃Zy9X9L9Ӫ'*x~ 5{IXo6} qE,i6s΃~_xsidO;H hdY ].+Q'^s<I1%MK"ܽa\@ٍ߭=_<h{ߍk}7^Ub|Ǿ\3Z?Ե1F]!/2@%gtH8bMyK*pƵ|WV5c kOrX |$L{ht KKkgocc= q",&)`f,[QeBJ3mG99',4g짖HZZy3x֢~ q5=jlu&\`aőZE #ԅBY;[@4v:V +`7/VGTq]t0[Z " ,{mUgg/<%wPEFv(9 ouCiYKw - 9G"baO"ѕs:9p @kL@^ [($ ֭ P*ż +kdPwp@S`-LaI/<GKz?GGi ({:d֙zc_ރWzUɿ_ {Agqy^7&>x0/CHuYA.N 4C^M]jŀ5H2t3tw SܫY ny 0s#tw7 -=jJԭ'=~J#C&kc I~am"d2y$`=6BB]`k`U:9 j6t4݃0esYҚB@ 'ֺp t{kд&1Ngq?#f]Y~KAKR;dl!^kA Cfe|=6(HqRͬ)&TXc:h: gHu9f_ MR PEcE" 4Νr̔0IZZ3|7H?^@Ar31vWDzSdJs ^A9g j.%>:#1Pvx55;{LD kηc<k?o曷]x`Pk/Bg|K#'m{%H] /ƪ2JncaWfEjА*sM#䨋>(karVg/@_l?{4#i7t1qiTsAE$D)(>vlZZ_5B,En}a gDeVΰ<$c[ܭ@5Q#Hn QO$i6k\SX߁ @W"jsե5!"/Yw< QIɃh!>~bp0nΨsN4SÍ=44xl-VDZYtdhRH[h U=@R<~־Dw\?%rjVnގA\3ךiNG۩I=f a Ij[Mcs]#i"|X9q2k-<b !Y냠,&eC"ijƛu3=TamU\$@mEkH^< K*Xz-bSNtZO̵m`xlnBk҅d`}2yT<Q *n(! ,xT!^ d%2MGL{yVlrV Z oz7*];/?w?6(qeV7TĢnx7wy.4S߫Cղ݀Z5R7Eu|d<YngQ ϸ[࢙[\}Myh|OFgC{{Z~sZr-6y@Z_xbU>:[qreȝ sٰw2`PczdoԩiUd*! hrG˄'n!ṢڹjD ]d܎$^!Mn :w3܊>0s Y{c^B~ 7o3pR~Egx~n4<ҕyfs2âv68MXԤTx>;0 m{P31* e< "}ŢLR|$cM fid bw'B<˂8cm~׀~w4^U |skQ]E\וi{\=o :`M3e!wm2M0+kcn9]{lHsUalC{[$>E;}u.&D7r"e0yJM٭YݻK/Hc_zIj4Ǡs^51.ɤ~,(DŊ`g!h&(b},bM/7q쀖t1Bśfx&Bkyܙ"J#o~oK8bNKT0fY0ΆC<:?v~CCDF!-|ZV"a\}$%=SV5 :#bOYK!kE 8kV2ww;*\ @ܗ:&X3\ '>`o\ޥ%m=6y#V .2=}.Z@sSl1`={>*L]wBх+znVffyw[w=>`_0W'IԦ5@/ol t^-ӷ[{@ͻhx|hU=^ uJZ{wрc*\r|deD`,GJAJ 6<. Tbe%ȬFw1dά/[N(F%ʃJ&M `Q|+ҟM$A[oÛ^q+LeOsMc-oƋ7變 gn;>t!Nz5TP-LcQ>WXX"@*~Ȑ 4/0)=ŀ%Z4iQX9dwO WO7{QZGKTvJPU-Ӱ5*S24HhE,.~ ^Uq1|p]r b6 ]{* $`.\B N?0WB-"Z';/6Ot2hתy]?sz]?`kcT̠dEwu|*az:$|6E}?gПygj"}$*mMpsm\H×eާ9I B~8A weUώ65a8h*$jnU}c̿Co؂g/ϱMÔjL"W~4ɫk(]X;|VdD|Go|wvkFZ2F!d¼[Gn; UWǟ!茘zu"E0w#p3YH_^ځk;)ӂ^Paӧ9zN^uw D%;tsYm`SKQ@*RݯTX4x =Bՙ/x/:0]zbp!$aYJ@tp^L㾤;w[2GP)#!-&`3kmP(G|sDPAkK|Z ϛgxRZ¼W<9DpESti!!43 2)0+ך}nZz5&67p}?g\&;7-jeZ@sx vᖛ=ޛ;.xn>ggH<c goVG*vO-ٲ;UuӚ5I ϩyHa z#5,>s5 pn2o>L?ywl'(w4gP_bQie.4&$*#CiYL D9@n,@_ha, /K@;kM<7޿O4*l{cycdYt1}Zks~n2 yh6Je. 4E^\r˽r& x~fSޱѴ] DXW&v*ܕ}8O=\]ѯT`ͲlC!Z7m"qC3םMC"AO+ӡ󃾎2f3`?s \@Ѽr5$YuCz=x}fVs#:}d,lM8{7ݮ) jO~N8kPhpw4QQ5-+ 9g@1ǀmJ}<~נ̨h WVL>ײ3 {Ē`!DJ`wy|2WGɍJi"A-'q EiC d} * ^T.S֤+XgVFj.hzFzѵpBTP<0e5N'f]Ht͆ɹprm#?$ a bR?c)МM-̖gq`eօ3@.g־ߤ^|Rg-ߞKh} n~|.´Gײգ琗T;]B۹4ZC~msJ>`_^nYZȷAK'eK-+0(i=$##X` f ϥ T\DϞg]3ܗv{d͇7wOZ@W ~jU+(g|g Ҧ IDATtUSL8w]WMku0PK_(tg]d̬r?[l%ܖ #X#a0B L@D301pKX&-]?AĊX|7u~s#vĊ7[nQ^h^ˮb82.[Y{C/,! XHf%I)F&ڹ޷ױi=kf ;=\ *֚<̶d-!~g #(x&tVy%DV2Ut0ghsmY{z$(ˉ*XXo'r(~wYuwjjFeyY +EqzM//'oᒢ+i N /ǜJ>opɹNsyuf  VDM'S2Q,4nj!`t3C ֡pMʌ^85H ;5}@sȡC՗P'ocqyS3=7SO=r|?5wܬ? *R7?MdY=Mgwm 2y}][Iw)f,D*rc@{F] ߛAק+d3s-+>[2ϮbA{Oz "ATޛy PPwm=Hhxϔ%͖L9=Eזio@Z+ *(6Uk/@Ak\2 i-y f\Z#i,Q V%WQd@g,R+}yGfܰ}L`v$s|+^˙'&XiFtFCN}4>{Gze%SXrښUϤ6+k$-̬J/oX>zE'1QZ"-+gM[gq nmз`hf$8|.%1f[׵ړ.LT g5Qq?w hB{0U#nm(zퟶeA{G)u;vW526!- aE|Ta #wK8.9>^^5P@짍˯`J)DZJ*Vd:7Vhn^X13H`$5ν piҥE 45mqFX_uH g跒"ەtC%.Dm/N9ѽGߔQтʑ'G XQSwo/xo9kͬSD)SPRDD"d ޿Mײ ÃuԊ솂ha1fA멸d $<0q r3(bF㩬ggΨh3x9M:$_6f:#^k6Z6?L˿w^MjU4h`P nv-XKᅴ@TjPIaq9L>8>B Dwfrhu}?q_4:W:;R"WTig6Y ]5zw ݳt}jxkwY3׸ՃsuISPQh֕Uk~X AEt.u{]P2~&,@VU1a-ǨBZQi>>{$2rwtx0C'j@05[E[_J+kct] ݊ȴLekju!ȱ7q<yH:#Hj;pa%1,øXZp * UTBfiP 3vs#Z{5;ty԰vZϪu҆'TZ;\,{!Gg=,3łεd+ub+v߯?/cW3lfNjL]G34?L([v&V&x@3; lf̴qQE;d.|&y{8]g_[2e27`F QlSXI׌&< ];M\/5wfX58D׉5J\@g3jqʤ\ 7'g"j޶/ ZU{{ n%sTxZ0 qD(gßctnjj8v[xbdjQu2z\IC~ !1,CⰣ VW ?K֕= 3#kwKD8MPj`ﶇ.aMaQ$: ̊7h ,l>㭫Xru'&(T !5"h@s \7Xm ^&OוּKfZb?/#\?@֧ꦽ ov0ȿ# ^E0a3p:4|:r_2q}j{)n˚Xڀ-XTK|ւ:wil>mb7͹gY>g2Aj<@ X3!yevϫFcg96аנGt[|5y?p<''kY-1o R;ЅA[W3xNO;s4bxPљ=RXZiw}PjV:m4֍^/ҔZRLxk v Q6fX.>B#uب5MaHz?sdV#3)%СK*ؚozwc#5_㑼0VZnY» |- ult30{ijɸA/Iɮt)6`e'MBx_g:&N>W@c~lĮO?}~hK_ǟ'h 4I| D @/eX4{sQ ϶ws*newƧf۸,|1So0^h2C;Р@x~4G9RU2-+ɞe{{yO < 11fCE0)*Q!_cW:nefDwItY7.dy;ɟf¯}+5 ҾI$>A΄z=x#:s} 2{'ȹ/=C?SA>"p0ktKAދϪէ@~9.B[ivW=sS{@h7~~1'^ ={8uYm5-5*3u:rlq,̋@a0&y]aAHFxD X|٭ UAfH3 7N,&QAWhsE>b(ڂ567= 3JHOeUG2~dqNqV^/ce t ;ⵄ3hII*0'2+ ָ=`x>փA55cn?;ϻ[ Y\ka^U9'$EʍLqkvl:{W*SAܘ+C1ʭ3t]da/X{yg=L!]?Y :NΎݨ疕g}Yi0;! |_fi ʐi)K5-ԦhiWgST,|1 L.ߥ aU 4EJRucpn~kLV@n xRu=pS/u=n j&\[jg/B@jwU8g>G:x@'hf (tN1F` w00{Ž+ɽzcnMT^$`CArF$exG0I ABc0مqS=x\(p^-:vCRl>NA`j>oE#H& EV43WItƇx?{f⽵il2 VjMcA-wQ9eR*TY)V VQ3u`U`&ckfkBp xZkE18oqbo O%gL'g=j) m F0gK]ε`nѽ5g`gW!u W؟'|-=fmJ @h 8zwdzk4冂u]ӧcVE'+Z@R8Ɓk^'#)|ϑg a(M81σnDQA&]p,Oc,E+FaKG4z$-xy9(DZl9ɘ1㹏Q ?o*zf`md-_EX!s3k9?NBc[[x,D|Yި~6hVM*I8΁`w7 `(702`Ɠb^J௴JGf (AWZ|X\t._|}\??Χ=nDg(d2ɏcǓ] x@}̶4eGS }+moc1׹s}96|_׮MǦ{YFWtB|i)SoS"Z8&}k0&hsOuFk{}= F)gҊyc/C8;<_j<͜c";iK֚Z *ȇ_{[\N%~{/oY7^,g_fdZ4XI-t`J0O߁;A 'Tfˤ -f)M4xnʍPѿPf[e  ]:UBZnMv5|2` ;kF>O]i:TAW>{?[dO^~ 9B@Ar'е[ޒ5aF ם5*Ͽ·?:JѦ[xYbDݬm\}s)LYJ0o ߑ˭.E?nQ sΐO},5[ԲKƵܘ)I{h )D3;2(t CgԾ DG+//'sO|MBŢ4q A4*VK3,+ͳ0òjN v17AMR׏@iswG` qȵ6kAKpD-|2aX`Ě/XTx,Tk"|^xnUY5Lfo[|V4;pkbB˽x'Qe^/~d.c PZBEB'HАxoέ 7^+:5YlqDe؝>osfNGegv!D=q[+.U\ 9C?Ȣi^g{\?@o-*@TS15:>Kw0աZ<P37]]PRSu]`28- #>bXD@]hu`/PxR9pևiɠ"sk`h0}2B]&kOjQZ8j/A?H@J0v!w )O 1j#̯#itxn+H@f"XW3)[;v)P ]q1z 2iqi4IoEnH/(WќB̰Sld},D_ xʺ/Ԛy668a؁ )pf7 x߁ݖ=Y-V~|"a\) -"E*:E`-8j.2B}D`LAY8q&%]~o X۽AziӤ=+]@TtDSY5U4pa $|2(90-B0D\gz  ݟOw.^w|~1 I=S0K= @EʝbpFzЗ{* ̴{(uV fҎ&^_I[{׬ W} Q2Wmg57*s^%ϗ,+~=DoX@bSw͚9Ii'8* @VIWz#S&CВ䘊FW`X,MގNIN@XWM֊6"|\ _CUy6PS\%Xh s|R[gi?BHW8KcFKSp,S{Am 1;xy H !P y.Îs9?.zzò/ZPRD`96I:v{P T#3HtQ'ޜ+3A;9ZO8*ad Nq VY0X8GW(>ʜ[ޡs Zp5쀜`OWJڪsf )ݛLj ͻ|8g<5",g^[[Q| `I^aX *hmL2@޿".xp9]wXip:$t.))G:xx&l7SkI,>&g7G[Q,yS y>;'Ftwj_f@}Ae@jn1Yg+p%FkzsY%gZ$F1\4h ռ^ZZbDpICnP_k=T{kp>=~>xW>|9rv}}\޿fP8>~E _d㜏j ;z|gA.'"y7A*7mL衘JMYu ]sЙzW(7t,AB+)#ڹ:E=b& bˠ0SXe>WF%""+9V1ތ ,y ~ BV Bn7?,Va(:apkws;^c6;FI\D" TZ)!i=JKwdJAB., @ax**]3d1x7͓1׿nq~}-cCx\JP[HJ:tFbKԾޣc~F#yNoTKiY`X.4|t<_nq7hY"-4YiVXtZ qD Z8|>XtqA}-i[:k%hhOHʛHv8"-is[ק Xc 4ȵcGWfQgc>{LH[əq?$5Vў;HDEќHQI13Y1 yF0ܽhЍⴵö@<9>80r j5D=ףq)x5m1z!RY\WNOف3x^fQRi)ټKp;'gWyE[uM>1~cJXFMĬ0ܻ6/8flUwa x:yjZC@#yjl4' ۩s|87a;V|F,RuI,_Uőb1R.PBdϨfGjSk`@*O/xf]*eP@Ҭ `#6r@ jX1qaZWY/YtV8؊XAd s?\)o\?Y=cΥ 6`~T Π^wdw4֚nq"O|rK#ߪJhb$~x<"_s=Bx^iB`Km>שmr3l,^iYAx ̢f嫊-.8|dm6~ jIwђӖ]cT {4c&yX}ʘuVf[~V$c~g&p/ yB(y`u osU*|qn(fHT3'8)[ZXX/3|I7Ck #qmYd%,}x2ؔ(n Oǁ L+G0*R2Qr^ֹۚj&?;sD4R[%`/ q&p_O.D//%NN5Qqx6ܧnqԒ@!>{dcX^ݷ'nwVY^%jD?j`2KZa\ J5`"fdp b?lt,*[%i`%ـno-ql]yvLYg$}\3w/ m  !2%4[q@Dӏ-0kgycV`9rbI?iv;Ƒ|jDu i3h&ǣ /oĉ3SLH+Z%-q܊bh!{VBN\;X4A8qvσZe:99X2$Wz \y..zC"FC.W]Sv["J-GBi_ F_{\ |Ħux@[5:5OɎk Sn?QVJisG% _jGw߬sU8 lYya=RFЗ&:F1VA-g; RҜO0{36qWy5q-YLv%B$Ƥ‡B \q b<̦A5S5i^UxUmT[\c}cXw ׋@YmB02 "'9u3+\&_#x r,cDn;sWj#|Z^'!fOQ `!|m#V2&-_8qd*Z٤*>ۏ qax3|9.hk5pGFC@rd&EP.DBʀB{6JFjN&s2/^YSksfCR)#KHbf $֟!dGŨpzi'#gZ8iAäɕl+뷁Q=*piF&!5X"ц/7@po'=U%аf J233|"^)x6ƕ8e[6զNH/xsq`eU} 9_<8`W!7%@գP=ILIڬw20-f<2a;ެKܟqfU{ _Ҡ3:#d[0ɬ608|cLO_y3+-c>NMMAHVȽ1eEj5Ee4wqXfJ iJ5A xx1/[Wl`jVG1@q [-#_835Fy `ٞy=fH ²51>P BXhI]TRf; 34Rrq"Pvp>6O{P|2`~֡1|/")رA1Ú1zrΛg晥wÉL2MU{ jVA6]& MX *\N!e|C Dɝ܉;r<(t,*xsXL`]S?{ɖ%ujy=E{5׾lK,߯xZ:27]ۻPsk%{$+=0Q ґՈk[ޯ Yk͔`xݟ5' _Dps@fB|:4ZU\ށ> RHp=I泗`޾[ ; `Иi}}}']=u.+.z,UxC[pޤ2N/PnjY!|Z9฿Ipz pUiesϔA:Bxg6غNWֳUFu@A63;KΜs[ޕ!ΙQ9̟JX7x8y\Isnwo{\?2`hP>y ~fd)ɴ](h=B^иwghb͐ G`/*XSzv">sDuQ%T-q?W ?Fð}׬"/[lofmfnưz ɦE=΁yja +k1{J `v͊a\WJ}OUs#TAwZXxgJ}Ôg~*o;JZuTz b:w{2~~NjF_gcU(I3ҩsZ ^s y]WVxw֝<HG"HkRe+ZG[ tVknLb@vhvuGwCӤY{F3,ɳbLjH썿7*b]𦾬vWT0X^;n`h\fP0ZSOX#mbO0Ls:)ҵ'7_E9; [xU:MXR!2z5"6Aswg2\w2dঁzpyTEFM$m_%8XM;F*5&Y,$S=o ]HC[Vt]UEf}4VI% -bM\͇^WnWMoD g5yi=0di|zyN{RU4Jqg,m@ޗFyk> FpGU\^Zg *t_!wb[ OȪ>,6Kfh+z , 'kC?Ve0[{licPd|֩L8*b!sʫp(%0w_o.g!UBO+H0oX"OwK[ߧ=1Z54=~A/M銬I$b6ħ&<U4uj}>m XG[V݂ok۹uPԈ` 6 4oM2n=]hUN0& >wQ0@f>}4u8#9%#[qӂi  O[bnt+ tKu;soyK( }Q玩gY+GgVWE(]z*L V9 @(X<ҝVђaNf"<|LZwFE--#SC!= xԞfxkf+ *K_? pfP%Ґѥib,҅Z*CUkV+,lt5 dT kG'}yE2-HYۚia~F,wAq__ug't9 IDATuhs09 "02;r=>`s RP_d AqS=Zԝ&!jG$ <@U0. [hzf`l/߄jkz9+ [;3gt`eN&K9OH$$dO#bD{\ˁӎJT`Y0,G $ ^iuy 8e5oپx5GHQ('n%H'Scy\[_3[\y 4x˃uBJ:w5,4;j˲fZH (+hהŠҬQ6ItAœǀM,`̲;XqK%(ycoY|w~-"BVe׺r4ҟO¨ ؖ0T>]ݿKmAu|G5JYc|:VG|TQ5Om9? ☬%IlD!Ouq[#6$27 #XJ5}\Xv[!p3h)̝Y/#59ƶXLYg\ҵ{eGJM+̽L7,z5QipʚbۢbGDm16ߏ˖MYϙO/\k+e3V| yHg-F+WᎠU7϶ekͰGXVmW8 Pˀ}'i16|o( d倝/" e߀)fۗgѕEЀIVUwՌ+sJ`sbv[ |N]ؐqy|7դ3Rk5^Ɉ:hMPT^se)n5҄ K8xr: wy  ?'-~5GԶyFtmOGj} JX nV il*d=I|bC=Lym敇o,)ZLŬZD}HgۚN% O խ4밖U螘XQc R.7Z\@4ƅh$_3o)asfRBHs]{Jn)VEU?v O_Iz")^NM7% X&u mwDJZYwdD~ sEw;< Q:*zd 2Tjn'*Kj i`9\S8%&ڜHV=J }3`fg[K-^ @Vp)l=6#&q/:W0ֹ:i$kWM&S8CECf6UE#^7§ xr 04ZkVTH^h3uu]ŲezC0̤ ʥ׷'h2g.qAvg45%ArғqΔw/] j{e,q7o\_#~Pv+ !a!t':/&g(pF294bύVCa|kEAF1J<:ua$%k7L)k5:?|X5Nk@p8:EnMߕe0ƒ?S4FgS RȲ{,y$IܜiS?Ycwg\k{hq%FaA NIPfA|4O`eH0:>j:QZn9޹g{zf+g.c:QZޕ<`fy)wp8Y"^ـ 9C,k>YH[ V$ZYx}rִot~t`'lg:>|ӛ_?+SRq#i'=F4o\p(;셄m(+7_?}n(-Fߺ,%V%9d)"{g~*!e-?=鵁7>-DjU!R|Fw%旵rs|XE7Ɨ%q3{WttMwRA|qY1b"8k_eviY/2Q5:j +s\0 n5 iY]мOlu+(UhX쁎(-*3̼;h88OV/5.52ƂY 8}8j !s?:&Gwm&n&Bsf1"IW ©:>O>'_L{<'`U@|ǝn9WZ"R|w0kΰ΄%(Jb tw<ey?9o~Vs}SxA(|AMx7.4&̧V{xƘΖ-/sel̽=ߠ q&uhPQQNY'^/I7]sΊ ݶW&}&}0V*Byp*s{>B_@) dR Lf&, ԠC8 ;?=CPhDYӢHSNהN28 H>8wLI׆28=:BUi{lW`o:j_cD]p@h u gN 0w؃~ |"cqwCs cpnʐ8*:?똤REF0FTD3Ѡߟk{4?_kg6AJ(}oo' usQmTwBksnѮ'_;Fdϊ|˳N"b:ݮ@[`Ipa 1pAGZ.2ª[aMKky) :RV^Sk̸车/g0Q|,i|R /}=5]%_L:+i]X-%C԰-T=d7֓ |,a))\Ҽiv(ݼVzTK5Wa:1d\crnEu@2_IH(M \PkQADG-8Ϧ(<δtLh~9`W+C"R!:﹡E"<91xAidmF@1=+}Y?莌L}^ocׇ("^8)Ze 8q8quc2OvVz Oh|*\H2}bH3{Ĉ(9mUv}2 %jVsҒLQ5VUL) ; 8%- $י BaM>B(@rdztb tδz0SzoeŬBH9f#%4dQƶax>{?>}ڗ ٻ{gdd[Tzܹ1\e~dnh[byw3o;mF9y>,PCԒ4 PjvQ{|Hhor.ɂ3 uq ((y׃o:OAj~QY\4L3dEˀI X2ژWNs3?ji0#Hy uvA5SԲ Z_T >:JUx ѷ-b[`,)Mx8-B;Uf29@'KU_LIvw<1=*;w= 4;D͕srZ--\$3˭[2@87Zb jvH(0u%fMQ <B2`ZUƹpG`r75g>];ۂ%++wc`;Cm!Oj%"a_Ζm1 `gYL=b)4̶bju==嚬]eܮ͎ίw޽s1u}Z3 @X 'ƔyjBhvgx87 a 9ι|=oOE-\#ny ܃?b{nsY+䞙?58pRD-u[k;MVX=f۵?(4<4nO1fy[TVq4ٮo$2~}[KTw{o*])8`ѻ%cŒ#?"#G2+#*0|'|pp<M"2s0xEG峘-5R3,谤7ҴoZ:؄HΌ;8y6= p,LŒը{j柟349N7T;.p/ ^hkEDrQ ,hI h,uf`t"Z,?f:_<ǹϬ7r]Pg3 ?mpɲ͚OaVHJxQJN=fɢe L^^3y-ӆ5ЮWZ+\i h+Jܸ~ +-JrvGV_<ē˚3 u:=ȻL(ՠ逩.|}zdk.5@+iZϚoM#ur+X"4+w,UrkD@g* ř6uy-ItnrtJz\u<@g9xB]ޚ;oXh:ؙ)ցku}QI*v6_>/5J c,^& ^>n2u~? T q` G#mj](|IKYG% y B] UNkX8xP5,:qp1׻+۶$YB#322k@Ph0tDAPZCkV1úi׽>ܧz֜1#1vfQמ{V=\׌&^]q]V)킯 0#si]ܚ_a9Pec f3 [^ iX͟LviMœ3BB'٬\#jaQ.WH2 H]m/تv Q2E`Dj~vѡX7X#O{s6 ɵxy3zFU# dШJNǒ/ evJvDZoӛ  #P8<ތ6L Z3Z z;nZR!ј'Oh:bNnh _go&91F,knKVu]mPE(h6{njjC=,IsD_mV{Ph{3٧_gr[uwz~PKYl$| Xg|=M;G5Y*%Ś?Y;*7XN4$/bB^{aa1z%LӚV{k, "#7OC-`F%,˵=A$ޘcTۮCw=1ݕ {+iˆ@rڇm%sQڃ_/r!0aa|;;ޮ~uPMo徻{B8'g6.45gЁU5,~ƅ{=h;eĨ;dawypZ 5Ć\Y{%9CMlӛSl8^s\ybInSl> k u%\EBre5] LQ]MθOlݻy\f_rTuJN1{HY̯5O?/_ߊRJ\]v眃`JV穀R/)nauXwyon.̠&&<)%v&, ={!*j=e-=t{}f9昀Gl.S̢3G~6 eߕͶYv+D-Ygxh\<9-s3iT;Ƭ4] *K01#SzL>D`\~xZ]R$?yZ3, 5w8ILYEI!YՁ ]`$rp"60q6,c.4N d_fel<Fjo<rQa?sB2bt2| h@k=,zda\p* *4ޕN֚@1Ȣ.+tvot #H%\m/Tfv@˭ X\ =_p x` \'=q\)\ĵsu ot3 @œY&hgY׏3{Tk-!k$(vӵ9+,HRݬm[K3KYHb&9%9j6a~ @1@)u>Α,H8 ^Ჸߑ就rMfl|07s\bEa]e;ʤ3H ,/E6C|Rv`v|`quI0,@QPYt,pk - p"v#ӕEkyѾMk2ں{yI8ϒ0,`ֆgpDԗSYa4ŜP-򠥟vVgAHwegzWEU &֖Թ/>H?usD8ˇy'@'sj%JiƍR:{0lWѭ9/PRpNkGgq DQ.RnadD4f BJ[,kP1NeٳeoLڋ]@*<-{>R0?s3 :O*`R3tNo&GGO1`l^uʧL=I86WJز8#^"wGUiiG.ޭ޷`"^ /](ve?LjTIÌAOWВښWwh3|ʆe2۾Wٲ|^ TrPF\kcxݨM4=.;7\]1aOy:X=4Jf+VD#- < )~'*+gi{2δw] e8x#)p :\'MZ*K_)o3HN1w׻bpt?suR9=qEctEg72 AZ^$ }BX4mR}0Ph <{ȁ,8"Ι+ޭ8dqVqgK\(JH[8GYeo|4]&z](;Io2^3\[u00qt EG)o#K@ ؟< &4s(H鮢gq&u!c|sL uϬ& -< P6V*uyjZ 5`w^cn}t zJ@ua8[c09"!l2-iRu]xg- [hTt/x_ UӮ)yYյ]L+@B'C<4y6ph? 4Tkcc m]_vP`"eJS=o`\d2; !:L1"s7 Ψ|7A@@޿ԴE^~^~Oi=FO{f11nn (r 1M[R%ޤv#`牣}߱鏲ni)%PFt^#^<뵲޿UM,-5ΟGD cc]_L鲙 `"~JtDg8W `6fֺ1yGU4GI5i: 60}UI?N? f WӋ\g3Rm.Tڞ& hQCy+B[<&:ДYN}N/PY;6"1^] kU| #$$"W$áF^MD2ݓnjQ3cVO!?gw]sJ/]RKHMCߓc/i^ 0>e?g{D0ƈ (p!>3z*;M@ Hwdw!z͙>`',*MPk#-+@BNpЮڇ RN gn\6 zNN&6qa9nGC]s54!){˾HG!+Lvҽgֿ>=})K훷鼐qmIͨ&EV!)WDbyM}UDu>DUOG|RYV6Y&{8/ɔ`Pt7ro~jPԕZ~/"xxH4`I-{k|@h׫__ h׎Cˌ,i hȌ~FR:cDSIy#Aru;X$<%(i JT3b>3v˵rM_RfgֽIJ䶗[w-`:ͦ`J}Hk<=i[54Q=fSh=88hS_%e|lGC;NsT>K&gY:G6N%8lT2fbhߵNJG6'`cdCT U-%#>U@ru*c b3CWoLu~a9ڇ^3pe?>\bн>K8xM85bb,0 Ã6g^Y+wWcԇM ˢ~a3& Τ𖌪 F~sY*]J\|Iÿb،}ѭg( F>͆;kS!cc-G z@f.8.sB݊)1#J)GIp~xs?7o­dD 9g3Q>mvSܵ2[U1SIg<`؏!>Fdђ{ gpTIU bH%[[ES!\Ѥ!O2 n5Oa"g7z K2'k,u6$F( /LZK$s=w xATG12 Mb>D%%jޒlL&P5 c||h 벣}RHW8$srDcTmCAG/VOS渆{;<9j<α8i@!B+ 7oU&f-t % #q?r^D+d,BN潭¢TXVyvUa)jjWznk Bn<)Y{4,!z'9D9iЂ>6\;8&h/BfllFYqz~_BI1iѡV[Z>&ʄzNxH sFc,{;<|bsfm12qR0QHfHzӪ(~?B8̧U^ Zr s8}3T$.]bt1*E3w { _@ԌO4t{ ٠,!0bnUuRe3S6} #-EcxaH8ܒwFGpx@]/Ǩֻ-. ݔQ觲Svpg)Gϔ+ڽkq=p]&%yIz;7,1y=(+bݓr?$BfgEKY\=@;;i+Jd*͕x&8v/:c^ @+ {ΩɜO`E:P" ցG0βZ dΞ+\uUS_4oqzCާTD5^ynGnSW̘ 54\:ƌj)PT4~u+*iE&#9r[UQRވͯSw.ܯWf0]%ZnVn8Oy^6Yzy) XS҂`GzͪR߇g'i/tMEܗfu `go$(x ,0t9po&P45e"+3\bt5qQM0Bz(h]V=3d3or=w~x"~ IDAT1CV] Lo(1&K^6XBYz c@A/0:'= W0{hmcyf9tY"]^ڳ{Y;P8'hrm_xj{ǙFxRy# {\j`#>VeWڜ  H 5q߯H.T\r^zA|8r4*B_1m`0ŒQVWN U6v"3Jަ 58(?kr}_ 0)Zx7Ϧt zT KX#ӽg5zo$|ۤW^[}?1U;ǚ'\-N`-WM{F@+[΄#'P? Y Ok3œ?U5T3kۏsɊ10Iy)rj1F:g>Hexz'Lbm% b_X02Qƕ !-hdʮVZH6 Mx݋w(MyJ@h?9qI@GxlTIUk@"Xqx{l( #)˫!] [Y^|,:I|ih|ύ3a>j%# 9Ty3lxɘĜg:uWKsq3PGY.= ~g/+^;^vM~k`r@S}`|a9=|a#<{sA2V@c?^䆘̦2uRZ"Zx~mM%aBe0e \/`+a3FZ V1?YL! "΍46, W ]gegu5DhpDa@UL9gu6s`7ޕ qW8>01ZYb難W܋˪qźuKK䰵Z_H7[|9oוShg&^|FuzJT1DF D^T/tĐd`X~'_ (u]9rν7]r;i[AgU +j?$a-Zi@гeCՙॏn?J_7yt`X9Gf)`m^]f ]$QL9!IлV6|UdOjA7,oO'R^P`0 {Dc8!ir]'Zq>d>|*oJb};}Ocn]{(?O3,RyoOG kV,=VR]I0`<jXP}8hiJhoSI\1#IZe]lpM&ҊF0#`e-/jŽh>dxT96qf-1!h >( (cS+OM<+8"ps|%HG9ʫ4K ~c\𱙴[+;uh$4g( ۥF aWeWDd+vL+|08w;Y!}d,Ba3^B]i%[-syyI_t̗@7Rj|ydFE:;k(1µʬjogv.e;߾Dpdz#@Uhwj])?v$ŵMnqY!G3Bmc,3@ )(+QM1;s96LƨCv/A5E2S MSX#\ͣ9Rnӽn^/.t:ϕf.0Qh֠{\= #95DjAITh˩E<7; u&3V]XIRƏhhJͰi|Z<늴w1iX +Hy,,k(9QVC׻sg?VtݽJ]e|B`>@~z9]:̼4h JMv%Zٗ%sMv425;H/nڞgP6<$EC6&=CiX˨O/Z?_^OPEO|7RYB|oYJӐιIO7y^נ!YjТ{K~zHb<3} W,'2watr%Zgx' l& 438U%(EH{Út`MT&$!h(VzX$'dRoooaiV%`! z <¬+>//!nj[M]m; ѺbN+O&JJLFVǸt߯W@U vz/TYV\^{3]M%BAdҟ/6+C.vPax}_ޟր|=OK сzL>-11-Yȵ1|x{; ss6i=3O^'xPji1Is3+\j FPn 0gBV-lZЊk9JQ7G&ͳܻ^9Y=d@ mb$M{ ;b _m'4XklRV)Xʹ]ϦWęF{rLW<5<{̇-$JFl _e<ۺjʥ}G[YPtE/1cÍnTPzzJ 7F}>LZzgKr0 <^}*׳ OoՃ.wS}ArGǍ*ׁ)kVl58eh6Y} ʡU׵!G=dBWNF T~R31,HOusԷ1G_bzhXwO?VYgZ;@܊a#-NwdzYқP':gY@l2o46է7|zv z#5dI5lǟ!ѽ6>3۸ iqH ^;t8A{JU(y&/t%$ <8e?rM Ƚ[zz| %n^IbZBy2J'u&L{tq_%@a $=a(2J&W>4p)a r¬rUTܽ Զq?Ŗwz4ធqM,I#λ}O׬qi\ x:9%W ߇1<=cCM{y<_hi+G^l&ŵoJzZXO>*B'"zz dfȥ{"vY{<-kal!ŎZ7:ʌܑ %iE|c({/rfoSHv[ Vvyv{qY&њX *9C%2g2g). ŵc?9aL҈עZ}G|UZ4Vֽ廜G.FJ}~}2 %=(x&:2ށj}8c܁quϑh1\_7~?ϟϯ8&$s@ g1R/>ϟYx {h\*[ 8RIhN O<@?gU5Q*\'Wݹn!jItr:&Cʳ83rR,ivVmy^R7UIu<krrlLT1 ߢ,U4/Ȫ}Q%JC݁JH>-ć ~÷owc=0{mˣUݓ[;]ugqtľ Y=]|ߤ94P{6Ar{u\mX59,trn{ּS!O+[KC!x6ct F0rƘ^ YvkUapp EAtaW1wݼMNbG9%]tMҶid 3?#9(m]Bn&ך ܔ!R%ٟ'!؛G%7X׵ӝ( /*"ȍʦNr ̲{d^W4m97yR5ގvJr/f,A+z9Mߋ[Cu~AP{>ƹJCOE}ߝG%` aܒ&RNFk $]k J(r Bd}Ȕ;00 fX'jwBU힧 ~÷/U4w[*(dmdԝzx&#83S'L ݟv`ê2jɹܚBKK^#]vfu@E@%Cfj Pʙ _<%ս"-"}2WXAB^t ?|},N[kXΪ׵2IN ȷcjPSB2M)$X:j}ȓ|o|z_7#տît^[,Xt?Gy@:8O%)J=VH2죗Lk3@ @+z>t1GdI='#SFi'*u|kG`C40[:Ti"ZmJtmg>^|v-̈́6^R(!.&E P@ueLwEKP6% eQqqE2^A(*1FZ0Aq|sԝmwf(WB#D3B܁q pDٟ{d9X{ ђSv36R(3p(]$rf31"a҇sKs2МL4}+P+VA'g[r_H\WxkaDt9#86W0Bo?|?wt&tvfYr|rwRa dbD%#J Lc~&1*B __?}H@Q5!M fI!metWfMkq`np]M4+ Q5ĻmW"ʊ,~0^]qYu59"@gy;sSAxyJ|9ӄM(z vY'-zVoyz[eU_;dB;gSmysV*a;ױ$C^@rRjJ;+VV60clYmz4U)*?(KI{(dUK,0GCɁE7,ⱋ!tbt[-ecbZcBYF;K7]ej}NI@$rO^TBi=ȷ ' W >:i/ζUEo\(ְ ["w?|էTdކmwu* }[+#/D},=}8%ƣ Rl^^lya_ٟ cZ 8݄TԲRN'zUЀo|~$@ѿe}F7ڛ2;,K~o%Zɺ^+kW<3G_Kwc(y r)îya;۩{#@v7eBbC 1CQ%^rIڼX0xps{ȺK#;UƤxF ;<}:z$'0[£ upQ &ZDZFa%(k:Nr]EY˝4Vn'6i5u|:뺂 R`pZ v1zU߿܃ohܐ7U<V{qM f057^ч7{, *AGxּ>Ӕ!&m=߁z#jmѹ+Voׅ9wW7Cx~9(OXM!<)s&=J)$y&sRѷ@ DR*6ru yXyv =>?=.p wD""GS,۞u=ہй;7)x阰 {Y>{#"Ğy(ʱ@S yiEd<9&rNsH+:*H 9Y5tt)cI'm4#S㬅DtYd`i?? J [}V+DV6zhatʄ-s "P"zA,’1yE DXwzj34>,qB?S1Æ.g&RUu L]{LAK@NJnZIM@ 5.@Px01Gz{-|;OkuyԓRA(Yp݋כK±=IK۠^@F7J}lkkL\s&4|շϟ?^ka֠Nbh'~3iDg ڼaIvT,hcqvrm)C3y$vrž ;XRƈ kJ> 7Y):{ i|"ZVPо<[~yZOK`|gY!h;ϙUō*6Cs :ii?+,m%gl~>trw3!XQ;-FB`9-z4|uɠNt7Z_CߘD?IjeFbĖF%s^(XٽJqC[wOB;ss~Փ"J4uU%{"aJzz;y)`(3CE->.8@%"& YzAfQ kXoOXŭYXֺw|}}8ߏ_!*$ng0K`bIO/{5~o__x->$s\T7ob=}zUELkK>/M88@*_f-55;ͺ| vm0 -`ƲD)_MH ;D?=y[d$o~I`x0?|pjE@W dy'fKkl:T\Y-wfm9lt,3];~5@A |O/ow15'^kު)Ϗ $T儞{:e'8=xk= {Vn3QPktE9Ou2 9R!S ='+8ØA?4_%# [˅ю ¨.WKf q0&b[ <{@4gX*&|ާ}WGz0q7 s>[MA,9DG- ^ۣzߋמWnaS>= 3-h*E^0e nr>q{ʮ TT43SYuχ +Reg*RͲTvWWs*8 X[*_MXOo?q/Ͼq>ی/'O7> 4 w/h/ h1JՒp0h 4O/#8$ϟGҔ\䨡UٙTǨ=;F%vzjʥ^|'DTTy>#K]9`^*~6WΣ6Š`#zZ'FmHq{667(o #ZٹvaPoT^3 RɣB+T :UߕpYPDŢްkk޿n2k7qQIgڗgW1p ]e%*Jj4>8u!ruۅA1Uqf#vݰs& ڼkv(~G EqVן>ۯUȳ?_~M2ۅkgypi80s_׿w5U@!`vGñ}JS(@e7:L*C8+f-a6B^!$t~ $h`+yڛ G=KX+Cɼ3YՃ0Q> 5R+[\0DX `!"C "D~'վ'P4>dlW).tU{+%@bsP:Spǎ IDATbXvsPd<f.m ܱD>U"vèYP"s*ç@'@jkIC-[y9+BGFx c5i9k զD*k4kLDyKz‰8He}q\HXcѳbAҊI Ѿ}J]z 3}((0۽jbm3k|=g5漰NUڮ-P[ScL|Nr* (O%$0v< | ~J:OG? B|{_t!tpxn9S"j)33{O1e*-x޹t掑gE^4-b -,Rv6y+!TP *}r? h&ڗ4+ɢL.S+ *EH$2dW"4p!p=oC{S%FVW:7&o5+*K$Ə܌,]y-s;zC$3V9lŶt_{q{6j>Q4PU6 ~fW?ś6RKKDӺ:Bs=;Љ}" gh̡KYn_ Ϙ؟{r.%ϾMFME)EO@cl8QΨ{du*"r68b~7_2M9 Hŧf)=TR ;pʑ459g.,G4KH>@:EW$.7>mI!h+ԓA~6v1Ϝ^dBZ/܈#+ -\Z΅P|G X`|Z ?A3 ~evKW &*-|[j ,̽8 bl t0he&@e.llr y;3sܩӜ@X$0|}{௿}^_Y땂MxR}pfkxpOc^w |rE_b9C< 4$J1<#21+s|(P ͸f$wF҈{hhbs,U{`!`_g$P"I1XB19%e}l <\l f5|xe_KTL&Gc#s*қgBx$V{,Xk*7^a}z}-'k#xrޖgPxn#PnB&( Dt3^cio龨eB)nȗ' ~d Jb<5ߡb`hB!:Ddb}ѐ3B7\GInڀ:Z(ŐRփ*wTC _{q*MLT>- M~r+VCK\AZ|˞\M18weEf]9DSB@`P^~9.t9#i0۷//  VudYaz5PlOJjP#I{:ᗯx^GH98?6Ӂy;~|Dž煏+(݁Q4Θw;%kQV*pKRR ^등JfG+ ꫔}ťlm~ {4 Kwrܛ;!|3yqzTfEœ}Wsx\U&s>*yRY1/ǩ^-S4x0uW.>^<-Yb\ާSG܍YE&'7;*=bܜSЪC ]kzNƑLnnM`B}Ĉl^# 4,^ttbFF:ZdBH E, .k'[J#Q2ZBn}`vy0+Nyc,C=[xdyDE76}=ZJj[KM*<<}_RMq q7_̶(F<ӬJ2Ixv=| _0<Fâotܷy99q7.|}?0-u9gzjsmݵ, ~Ι N™1@[NUOFEy:1T!ݴguP%N${8]FRHk?c.Geg' @ h|# i]N2muEk p%3=n8-r B7H`4g8dMI\˱&2q> ypYcɫ'=ʠ;˒ZCTBX&,^}LTVPcԌ1Fu-h 31"]y^S mZE 28<֘] Xׯ: W-t\u{̕6bg{jY QЅS 4=kʜ 8ؙ>1`hZ];{GE= B05ᄍ vsf.WINy RI7jWR^Y{BU("El4W2__o>'OY} -;]=,C_}_~VLV!D)MXvZ)^XV@¼c_<;݈Noߏ'#&<I/O<plk23۠2j.NO5U:Y>ЦaEצTyhWy46A@4GӾjlQKkjFbK:aGe{mD Rޣf*YG=X&3r\PY֨AİH4gX'Y"?4z6"ہ.?gxr?Es;Ա6N!;~%sB9ܔ{} ؎qIW{cy8|DH~^{8Ɖ+_8,DFB8(+RB6Pr-[pwJ  Y YWƯuA̜z:PC @M~7J.g9aOX2s+xSG}vbׇJٽ?x g1VJŪKKͫ4h*_ӹ`b~o&-W)饋-?-S@xP-$,&43\W摈ZnfcE`'ڡ? Y܇"[L l8Ǚ+Xh!W# jM=R+AwI.=$ty+qj,LzM=&HD1}~E7:[+@K t?y28ŋjAb5oEmdwhyn̿ ~{/0+A5X]Fud4Fx+ ŜבVuRqJγ%TcF҂gSV1G<0?~>aeb=vӫ_,~6F5{+sɇ`-2ݼNC迟<â "mAWyce޹ycT>F_Z-Tөp(>3`xBCg<!Υ$*]n+-G+d.- J' ]Wx"{fk%cKwy_J57aAKcDŜ kѓ`ZIuAYyL>ךьP Bј>|#cpeEF1e *PR` ̷vV Gk^ACtNw J1x3]smJErKf$ \QH<ݍމ[)96ACDZIk",G֧jxA^D[:0p,BINw(-@2Ez~VB?o5sV=/-ENB߱GƁy`&`x^b:kJaf_Z*O@{f[ :FWW|_?0X࿗kW~ mU{c=-+, Dr Rlڤפ  QeWK_%?|GA%CMѕ:;SK^+p<\ r\u!2gż@&Uj>٭}71,zjcmB_6jZP.Z K&0 by8<hҀX\AlM-Sy(B&VHW"ɋ0ElͬZ-YǺC%c*AkmQs5AUpRd\ G4sU'i3j k@^/ox{Vm$RUvwV$MXד3KyŎ5s^)ZZz. :8}Žx1#R#vv#QJ70H=k!yBq82G^>c(Ԫoϔ3S@ ]Sy=n3[`bq CUu3ݫax]"4GYؘߩXq%L)ɢ 'حe F6ܭϹiV{0g,%SC6I"C &+=0pߑ?F2] D SaW?ds@2t?cm<(B*Nh&I ZN+$*|DKJ^<߾egA0@߫R ) ot헯%i|+0gp>QLq9-I+BB&4l[I`2F~kιG7K9|␚~tgfV熌Ϙ\U從2g )k=0P8üzO-pf}gtK R ^K/ qv/9[I;ǀ:/~W])c (zB^înݻ=Bc pC^XM;a{tϭ~F!N;dgOrsh Kd?ơߣMoцu]QF"!4rX_la4J p> 8cʪè>;'sS°w W(wnw/A0<yoEātME(]rÀt]\Ae6shtA41>knݞZ247 S0gv)`4 EiS&bn>:ܫb׃ryVj4ŵiH `yb H/A8xzr~|f1ߞgʼ#+fʈ+3;C;cDB#U(JZbA%2(C"T)̕_.HA={׸"nuD4+ג([oz*#8U!u#`$Dcmey˼ປY y{ϱйIh Y ZNxngVtSuwmکw>&!6׳ί2s \j#zWx猎p3a Qf ל^^J_|ydszɤ^X!`3U -&*$sO+1"^g? 9ge; \߯;a 0`v$"y-P庤SP O\1T&CA)+wd*JH_w%؇@ _-J.#)r0Gr(f9PcJ@hܛr,,k8kML+L-wEJg 3Q/Z{۰րfa y >9/DLVMǭ!Ŷ.#znh IDATu<@o|9Q[<}⴨'ʬF"ms yYДaxO/yb<̝| ~EP)qD*ƙkOE]J*4n d9Zu?Gzbe!IQ|E#u9~߫LR&j"Q|a~wϫ\r=#dXns VsPe+U_qb¤`g|Z\yTo:,W`F ؒ,T`D-HX]=h@QZMcUhfHֿ(]"ֲAzy =ЊJ!ѰjOO{gc(M۷|KF<}7/&#4swpie?F^H(<*ڴ Iht#͵h^(y1ʗjw/_߫gVQ# e25X%勥R<y`uYT _y+l:Î E4VL~V24=ct@e/{qͥiP :hL6#&&]rn䷾541h.mF#L0 qA x߁niHU.!e"^yPyD#47Sݥmml@.2ח/$3vCB3LAkdJeY|vcL_顊U_ (v8kۦU)N^u\qIN3MI̺dޓg41tτ:CQy[Ymy,i%IY,E=mǺnxx!fieջ{;HtK uG/o" {;62 Mu> [K|m>xD?z`r?o 0֮k )&Xə43f#9/a#0!{'1-YV= MUՅ]e+x6oh}6GȺ|[#y1/3fp.UK"5۽zPu3(˓O|&ֆawE7@zijhhy1EK}~r?.*ZbVL )*p=Kc~nNk2vfjl_ }wYLO<|D` Riͤ>2{EZѐ= 1UЉ}V7UrQi*Pz֦F#1H'n )Ne˝[|?NH2j- D"^4@1/ T8+EL@ɰmB-lc8,-t㺯3yކZ.%#3: (C`/_SgzLX6ywx/mW/᝘ʸP6#_1l*'y, P3O6e+w PeJ3b[ٻz)K0_b0a.'0Q$#3t tKy+LXz{<"?XNOK nq2$,&1QXazY̚/tR"cu@V%:z3 qLnpլrMƒԓ^/0'}{{A1pK sJ:L1tL%,E5b<#+ӐP3rrE#G,]g*68ox:o|Ǭݥ J@gyk=qǷ/o؄XCڤgr٨S!`Z$Rڔb^a(JY `6\R?v.Ԑee5L<#pF0F399(S1? ua3K,he)/VnBPgʃZ!mWqXҼyYY\ۭ[l0/ɐ«Fdod0Dgjfinh.*%j:aQT\Wұn}m7-k4Ύ G%JA뉜g/_]@G U95QkJ/x#:Yٍhy󥞈,YM/ g})Ң %xˤ%0W,&tK鴌'+h%cETiML9ZM|_Kнyͷyx'CZʍ5yXG'oMI ,aCvD0!JU<=k{BPjeocdL<g `9N=]eW,kClUm]/уb] :2ڑ6\Tj([X[Gr`'".%ne8#l!.6eXj`.xս^4 ){nqy86|-!/DVYƣ־Zu4_?Ђ:8/u|qqݱg1,MLac>z V# E uX&¶q,=nD{S1Ͼ`?( +4U31@Sn>egʝԥh,OE&ä#Y,gd_cT!7:?#{~w'u[;:1/QsK-:rᅶ6Tx3yjDA<oxw5qTx~y_~ڕ 9]P:L*^q_OO\+,٧\,A &ZV=i{`'N|y#9C=V'lW3xc[o*C _w%ݩ,Xa ytoyYxޏ)k .!lay--yEy ^ϱ*{z,,hvrSXy>0Xq%S7U Q٠ͪ5T^! en佀ePM4x-QF~wybBnLJ?a1)@U5.ûrrj~ླྀ4l$HBu9 tҲm^wZ&[x;B}E 0KT|:޳7yA1ygrpFϔnrZ'TVvۄ m<.B6Z>|b3EWᐢMn޽G$p=Q_[ypdxŜ;lfm~}΄g$0-yVec\[X }W \f" 5)k~Gu9gZ$q:I'0AC`Q9SS=r`I^=]yT/ѥ֗ ڧ;etW:cKf!E3^:|4K$1 $dXgɧ ,{@CҷbE.+HxN\^VF×;TԭJ?6PfSʵ]00+4!0&YL>tL< w$-I=ѱ\R\aUvLyfF 69C[3G,0>uo 1@w&v\ŵ/t:|T6q)lb#\`$nu0݁䏤AUduMof*ulDC 8j=Ϻwo` [(:3{*K/b|2;<|{{z84ʣaf8( }Q Mߙq=^b8Ly>4ΙP|2; O$7v8Z? uQ4:ΌyO}")j{RtO(G"q+H-*ˎ63^U"ê97r{, Z)ǁ}= HG%ӸV2R>ggX3` aNAh 8Yr&0yJZ'U @A}̼&Z(PODxgbuIMxUV?J\Tű*c0F5R YK^k2Lq a064ѫAyXn9g{2׃*Āum힨]y*1kg}gTӇ4i} S&v#4ǶkMKʊH}ACc-K>?ہ883ź^殍NQc;T1aeŀmsW v`$"xl~h@H2s IgVΛuH|F^iIްR6N4v >==H,-vƵlt ~ D,zΨ4%(E8 J^w.hn"tiY4q`y9/LXF37a8q`Gy "u 5Qf70[ yr&iRTXY{^I:ԌXg ܻ9.e~° dKf VgG+yi'ZӦnXd8o\KSZlvGt08# 8k32CiI3,ݙ;X- 6*kY\6iپ9Ԇ XY3P`pw'G~eм TPQq8X#YCcEЛQڂΉq:g^ 7+pw|e)ĿkMZ02ϗyb$@O{r<IsBJCǭɒ@_x3~LJ 4'>oa>tDݸ{ d^Yt8C74 OP3D)q]2.֎?#, SZ;{vy<c]DxGcT QXx>F{>^-`ZƪiqUIv#PZD׊U~K/a{?KJ|KW IDATbxUuڈ41دZLzkZiR-֯;-Fgz5Aj{'Q.c{cQWm{o0VJI5vYdH/uۼMr K0`D"-!<;0F9,:fF*KŨHVu}8 f Ggܘ*ʽd| K.rd2W^w]Ew ʞV?k9ŀR%kŠާÑSňk1~ӐZs$&Ռ0hZ-LT%oҌZ}k Gㆴڎ㱌Q=(h'(Vt#3y߳:y0LV[ a \糳+r1(˹wi:$!Osk>zR>E{g]l[((/ւnvyF˯e-ZfĪVܓ<ˑkp[TKPnSvܴ)LL{>o?(7F$jU>d}(1c{pZ{ 3s:2VE>񌵹Gv;Ƒ1FhzY=Ta&4, >&,^tk?%3@T%- ՟uKIk3#9`n=ⴐ PGf[SDrm$OFMtd5D9"r B>/iV } Eg}ZՊzʮ{Ŏ|4Ȭ@yGt 1ǃuޝ/Vs$^d|ҷ(sY91X}AW7ٚr@#C1+h% ME8 {^w!8f__0ڐS%q+KkC,>԰a~E毱#CL9w8X?TenN s^8׆̤kYj[95s"!U&V2s=GRͻ*';j]>^pL"4cZSW%B]J-k{,|^_dUP&\U"\g|emFx1`{һm`{#\A$6 X mc6bF]wL@Tfݞcl>{-V 1+h%^@(װY̤cD !H&wMYZܨ ,(όC7[5IɊ \eɰ.CδiC E9fpR И! |^29?i1#]%xd3yP]j@o|dfH@0ǒv$ ѽNU^zb97uZPo5Xyni`||t΃'+4q7&x؛CA0 6*l%2Rzf\Ug>QУ45ӍYz~*PKy /JgbLm[Z*pODGfT‡,@>}\~C7=*upZ7"DS^-(@} 1pm˻2`ԍko4›hI?dʴ*2=|:uX-*7p\{%fZGǿ+}3{-;9V?T {P0=o<y9KH0U; Rj8sL T8tF*x 9i䫗>)jT' ֍ݙve=(K0s (,̽Nx^@g t_Gr.t˼RZTpsI8G63$ghꚿ3z|Nq |=X`B'ҏ2|90C&wֽJ(yeu+rES/U>jW+!Rs.eUuвW+9E+ܰ'*6[&? D9v?'F  xl+Tڔkح#ɗUa[|EJqSN(}\JvE@m37|F},Lp?3x<1$֎.¸v=r[^!͚]+|գ4-}!碜^i 6Gvk?G@8:VkeڞXnjSQP^:6ZDBL9'|DkݢQaPs5ϫT˴^`&"Q7&::;P䥏a퍈3 RJJu-V\ uR1}ϪG#ZNK ^ccX;kU,kj(WTkG`xW s aS9fPkβ]dehs?t;xp݀+:h50< tt ˆG~u3鍄9Ci{^q?HrxJ|<1VǾ}d_W/Hdrڨ/IR!1﫭e4@"{Ux`Qox}) `?x ፉx϶a%)`yn~RU L;rՉ9 ,z&1aX(u/,0[%kV -ꠅ`5j t ~4"9@M6jא!M]) GA=ΈAʦ'V%yPOY8Ml8, QeYֹ$S#6M$󉷷4$JE[oZgqUf Z$ac8B왉?\ EGrL(c?yȵvp,CZ mֱ~9s!nt`=(o5JZf~yC(<3 Hs39WŔp -n~آZrzqy,lBcaw_Wy+$ @".6!uh${D౻n@kQ KQ&U!SRK{]LFS<<'/5kaYYbOcAYFT7xZԢ j*0q?NgzW*ttTГq"䑴Sd 欤Z,i.KHn!0%e{$:P.цM P%FAxS§[ >"f&]Wl+=9%z>=`;I,w]6zpW@{ zȩ?| KGvœ9&JZ?kXR%O[g,{{2/ ܐ0xd8BjQ$V $v6MekLh!JtY='٬b1ꎬ8n.PyӢ. ?#6S(O$1/L7FNՆ@{@פaQ54iB$%͖bu5t2xR:ǠR8&u/oO|y\qZxN v R`kS'~WʽO@r3/z\lKc i[9'}3]eB8|ng$,r C6fqrlkjgхcۍ Yжrk5](ICQMLuܲW[ fW}#91a11wAżx(d {yy=^Q*8[B0 QM`VS$nӠ;غTZ^iV?ͯumyYxGݩ #~Nq 9S^js|jS:BN@Y`1@3-u>aJ,9)*m;(Z&&_p!kf]蘨'Ed+ Tc [iWVQZ$JR.Jq+XSx%뀜Lƚ* ;#aq: څ5>YrքR:'X1s @,$͈uo_~+ݫ_l,rm3G vEFc%?x;2X]VW tU.$Wz$d7# (rkQNkd";:Faz&<=MyWl+:@x US.Ocnw3#CM=$cRf]uL_-M&- еl~yVޏ IݱǼ!aev^9:L+^x׽EM9^z{,K'vXC՜}YpMpC^/Kk-A?evzqJ&RU2΅ꂲ#Hyp?dF[V´(MXʼ45Q֐h}w!R>}&PŹg+eVL-8:)*Jox"Y d)Lp $(dtuݑ-ЖQ^<d߀ܝ7ȉnR"21ylV8pi^i f-8He %:Gwݼ0` rz>}G& ?w?@k [.òTlJ j<+yo r[#ܨ~*g-P< S,egJc$ټ@(D@E|1:ɑV=c"ձǣ㾙$x[Ni2Z]pgsբy #Ui>]}羺wBӽY1P)jaCpu]qfW {=a7ku,f)d]ybqLՠφeߑQp suxP굷poԲZ>܀u8eY Ǚ]k9!e$|9Pt9@ OwL{^όD]yՀ.Hح([F-tl&ʔEk䖙sȂJ_'mJzR t e71`3b8H`(' ~81߹yC+dHRsÝ1@qJ"xNb"#xFo@hXU}xA|:P|]WyK=E 4`lMa'aF2yNQsY4;rտ$eu+cꀊGrbY3ނ.37L(̸ڰ!.둟gՆ !I/Dmvܾs4Ap Խxbbj$R1#"3$ qFXO嶾 v#5$a [jYco߯ P_B60cUf2Bg[i}r|;4xfۗ|? o͛~Īe&ax^+͓>LƱ’OY(>GPnl}BcBȒGXs:T>+E(I5 Ͼ/+@bZQΆ%OIᱠ Z7jtkkF#_ؕLף z(3g4Ƀ4ams!=nX߸JS v5,wx Zw 52u`1wUoo)}s XpL & %N7>03/4JG24Džykbh`VmCv ;HK,73 j4|ʏVⳀ1gqG|s{k:FݯG~mUzk67RE^ϵ\Jp͎S#]JQY挴Nؑ@R\t2Fb>՞@5m2oXz h`i@s1Ӿy~owxNh%*3#,++-*9҈tp`uL^STf)tBEADg9q6s8a8WUsLC%DppxxJa^r'༡ˠ]ì<N0Wr7&$(7^ҵǏSIc/_8q':|DVq!VAr/<[&ң`xty&M hko;3K<8Q ]x"44x?'Mq2Ɖ q9es}&AQi=nqc0Nk X!{ުP|4PS;0i :DAkT֒=/7<Î:U zJ0.sBqל; m g6핏]~#sCqLFҌ0_=pǿ$'`x{֍8,o;'*15שpwh]x3B[ifU`+"d6Qf)NzyEk`dm-W(5dw˒$ɱ/D#2Q]Una!W ?K-3̌D̢Gdy< Ż({0iƿyk,ous80Z 4XWZ]5cd97f*$yGj;]TR'@ͰQiBhepO Zt\\YBxOX@b&uQ|N+rI7@O\ ` @A pY)Uh՚>$ iAúz׍DwM9mRVL4b`+UNe 7ݱ$TJ4yըIֲ$MeEU5(h="hގ5),x ]l1.ڜRΔ˹04h08;c}["`]}/*p8]u'T{0B!3xWpˢ1T iM*H-nu̍Z*QFq:@+k&"F)JIU4YGwu?i*&=W8^qGGMO;XX-!vP5eGIӔ'D>`0{oȹ} #1՜ mn #ӍAT r >&>ʡաr?|xBZV"To.0xש;o#C`r? "麁:47N\:.GҢSVD ֒'8+n ֚iݣŁc:Ȋv+^^SccQ0&_U@yfLA"h=7CMUZX;.X*7vkum'3<΅xњғf nj(4 VTz}Ana>,b*QE/0NBGiB8Hɤ]iJԬr!wVK!]57kf**Zg16Xmo>-LDYҘ;Fb0Wѥ pzJLBL|H`j9,>=|zƊ*guYW3턧[X=p8E~$X:K-7dK5bf`DgԦm>߷_cz:_g 5^slp!4Q;CPt9p{N+;4x{ᱺ{mߠ3y2i.7cd-hsZ}V{vkKIEiRQxY ljz'u<%+g?}b iV |<[P]m+7?jYԃ2&Zeh2õ:VRVdG>2hQn#h-Rdev^=44nɭ 2uc7Y biTYHI;Iy$s zigUc$ܧ qn %)8YӇMҒA_=X%8GajBW]XS{0w}%&f {|< \B!BX! qlz=/>¿_?vzWBV$6n=J4ڸ1 TcIg 3e Ǫ|mQ "`u>m{Ija{oS͵b^or}%ǖ|z2s@V X oDHi&󱾣𪝯6 U1:7o "&^[9]˽?K'3ljFZ@4C#> ŭ`Tϖm}tayV"5p2j\Hsvp_9FJ Wp3ԆN~]2/ CQb^"i,y-l b4jB "/VA!Xɬw<`${wv[,Z,-w_]^? c78 mcVjjK;Lvz@5,gg9g GshjvΓ](cyvvv?_Dq?|Hsoׁ1AO⪰Z݄ 20Z95?g3$4d8L@G_Y \[OVGp]|F. 5u/\L+~iOv&vKM[^-k(_pc. it:Y^Su-U[akZh{:%BJ:v΍kYKElvS5&-_|Ǖ`%xUf|iZu̎[ Ink:!uBrʭ?|Vs2oLy'!KC!>N5  c/]ƴ_%yռ fkOt +!SX T?$RA\Я gf>Z6iiIgӲ+/vXDN*Aˎj\o ǴYgn5`e^pX6I=sߤab[9~BhU; RwPϬ--]X=zd.N:& ൎ!\%A򹭻SAoѪwTK wa|\84łPݓ@dO߿7v͚\ŤN&1ͬ \]kJhIZ 3GPH7Va&QP,\=0B8Јl3L>E:psBF aUE#]l[s=H%\.K3*lzdisuûn8 M=w ieLO臥ctwC21znVZd<}i}xCuwV>-7k I?<=?k"ף!n%"(AOETcZ4=Er*_"5,K!tX}YD*] Q5 ktBV|˙Yϓk&R- .YMy/\E ^Pƿ ؤ$iXo६08D;} 7%ҏZ̭a!}ƾk_]3xzoȏ:FwwD$|>DZ@U2}2 8(H\֍ nC}0FVP]/>Ƿ[&G2%MUc%anH3] xf%PlZ[j5#NZ,bR$:UVu)vZ9xRkFW|Ncߛi)0s.Q"4_i~Kv Q6̼yzÏ?d̚`p-u(鈀jjf1p3~ d#<]sy xw[EV \} JԈk %CBW߮(a[jIUJJM1 D'qel4h3oO0ͮPPHy{`ͬڬuMJe4%*>?݃a;)|a Ӓ Z& X<񑬓  Q]@5ʰJ)<`*"=x(|幪@!gXe%ΉUq,9Q\Gn{8Z#e-am(tz F灔-"@M۵GӘMr8W5 툰.{?@eWDYߟ ] #V=q}o ѺJ_e}t~V͝@n:@:F854sz3<'K ]5Q\0<`}Xk4M\Y2qA_/׉ߵq p@UvoWGJؓ&ӏ?/Ǹ= hDORoZdS pZ,$v~ji;)i] dTC"92ێެrh+s5!ZhoXLoaiOsL RmKX뀈W |P 3Jciq#!vşztR;a*2P IDAT-iHgiK 3~AA:y@';=`0l fiѧ kWj-۵e7i>lG3?5WbMnr vt)%3֙4k FcJ߃+ Z0{ wQ:mRs]r1+r+nFNe7yJ-# u BdW鳀k 45mA l@coBH  e rd1A Y4^y@\A"Io UaL0OO+z !;=OgI}#FN'&G C-jx$ 츷)0JFyRm` UkvڽE#e=* 4fJfhpy: ` r\GhGOP.sj1;'6[q̈́0톷nc`ZĔ*h牿Ԯ%95פ- #I18ĵ2&}i?㗗 rȌj΢7/ṻ[gcM{„頤b8ja:5`_?]c U>{2zAWPyx(V$V5Exm&Sw)'؆hKX"% “Io,q,&fK Ջ80Ož~v֠8M=T^M=eMz\(GM bJ464O-,toT}}BqXY 7gʙRL>Z%AJX]n=%ͥ²>d2A&‹x_+?\K{buG*߽p64WijI5Zu ȷZJB+Mqs7М)|/a]憲>S :T,6\hj9QA"_싯-#.4·z:D5UoFtZU P_bM$(O2}|o㲈؃帡湸+(NkkUN判f14Mn#9YeӣsgNa7^< e{UJO x:^-n` CP9rP}^Y5GȚl_ʀ`H-|6fhG wf8<"A;#iAn,wQg4GUsrbEMJ}x\/(N3T5E t7%wd@ح)ڰ~k z"XsjhLFѡ׷sDƄ cr@x~~2t9= 4.vyk~Zu 0C{s%x V%8'DYF#O"'4JM:"8XE ]쐧cL|{?;XMF] Y"yy?۲b[عV]ylc[Α,UߝYEj;y%y1,-_52Xbηy. sU:4Y(!xt\KQcŅ2b]#콝Nx>I+xO^X-lU<p C#uw,Kv;LF~J4w^uQxX S&PWFLScx zd~K,*0hEQP`}DS6^\rf٭J%ωf8e ʾ%Qfs=6 t4ۀ#&"Ig|LNjai&J''Tg(q V>;C[eV I^3klvo=`&eܧ2${ޭK41ZzZtaldtL π"`e&sT.v M333Z?g]vxy{ &C̼y<8@"hѬ_\UU.!ҭ*L?two~ JfBDS,MP,UMLcV4 9Ͽ}ZN>b9D %"MUecz*y^(zQF ֿk> Au>G{+KZZ p׃q/\Glr9d Js:pNBR#qw3 vfgq>1?4tm+rve*ۿ}~`Sy)"8I֠d"(Ƚ jJvhr-cEV'BW#>H$BUIƺ!t1f&Zp/pj+ޑd[=w=*#2]˴Lgef A@,$71`@Oy:<@5Rԣ1Ta>F38sPxa*ΓB:!+=<= ~{EkSMW.F#Z=\Äc5ptxKMҒ0'pN x ۂ­w\1+4v$\`b*qv-ɳ1K!sw$cE*8n:>-E Ԫn?1 ntPf*.2@ 2ёE- S=6!+7OWD *?5/l "*ﴎ c?pZ\9.X*KEgޅΏfbvΊJ0I$t!Tx\ϽҦ+ ?C8\gZۣ9W\@VWKizm﨟stBJk;Hyn}IoMjd/`R[]~oAE5(f`Rp*\wͯbFaQD:&I͉`j;ng^kY|v hK[5:~Zh9 XUiگiiO]~7ly?+ U]R;7 @QNaj,·{]vhVkSYAo=|"b{.޺OBO5M2z|w]z zN49ѢZ<8?_ 38ܢ 6e ܆$h@'vf7uۧoBPh E5lq9p/PHZ^iEZІ/ '%v_I&%@*cըȋ(cx߬{tjH]ۚy<}@|5Q46.@q qhulKY57ez[  &f%@l&O ph,9;DlsS kf85k%vM-Vcԏ2I X|dUX [V 鬵Vɖ@~3wMe P#rW۞FfS[$:Y!!X| :mrs*4FZ2igLdDtM,+b]u\d%[^Eۉxk5au/o )狼"0\'?2-M/pkAs8V' gib6dfuf=z۸D$ ,VXo'љύrO~@XPBk4d+SA~2Ud֘dS&3P% oG@$Ls!KR J-wXWjLjtlW螦0TMG8 @ q(Ho/J_B`vjsHôK L ]{@vq:>qTG:ۛ1;ZZ7 dH L|~aS( xoYnS^ K0v|wcw63 9!)E"vyE+Ϊ;]Ѯʶ Mj]Z[CijvnXЋ#䜫ZsU Q'`DBѸ%Hlqg!f9LnM*7#oZAqYm,4곕i5=f~iu'6R#9 e\̟xUF 5@iP N-Y瘘bS#o/5KU!c .%i2%P h"%N&Уbua`f֣7tC(71p`h~M,H |%LA*ZDv9=D+_L:'9RD?:g bV_B`s?H߇:ٹcdA35a hntA|_#&}'1RLg/*F{:Uu4poJTG8VE0'fUs5)Wo䀱h@ ENjdԳ4- D:!-|JъdF~,q5,g%j1J6{W(w Iy|o3\*w~~~Pwrp.0-[o~]AIK~7\[$o$ДU8g }c)}Gxf$5a dN\ tXIcZ )NYL)#:*< T(= G^{\zpYCvUޱ˂n}g j.M&"vbaNf#/!>٪1t \c3DyO`e 0e1)أ}GaKij9/n{09th &!?\ˬ6Y8 1VvI儕Q)F[[2цB4 ث?hZ4M֦1X)Yј8JZk [6 IaAxiUWXT+0w G @-赱 e} d]K EI:A3~OnM@vO_mM : m-gYL@B{3cg^|V \(>ee]SrŠ?F៌`șc.gϔOUTAܓʓB>&͠|fȬ0YѮʽZ2֏A+C!ĿZ7U+;qWX#|};vWxΊ`0q(>n:yatMhځ,Q)GJ4,`lbn6`J3e㠦ҁ< BAh fz_n}&hj0a+u9 O(3x/┭nf?6̤L0? ٷͻ0OUA1Z`CFD+z{2:nkJng7+o̢QȔ0lwI[IRV C3 "J].ԤoTп߂:LEn 3dF IDATP5ZvՎ &TMo<)L댮8tCͻ >]|_>\OO2p;{+ & y9j@1T .g0 DLKBzV٤g}al`pJ,TS7[&}|"xm }׷+F]"#uRS.pOG?YJ7)lT|pej~Xt}ۗk1t>EQ ԼzD1PXJ֐J-#1r=q=wO~zrmx=[uyCp"hӁ<j鞧:p&]vÏûiMlcɄGEY C8zSSB&]0YVqrl2ֶOW|=:g (HflAekFӒ5Ed!AHw^Z4 ֳPlrk$7Y2EB Y[^.;4>}Uu%S@02MLdM*B+_lm[tI)䚙 ]ݧO!I~`pQ2BzLUpV1jx =Qy4=] *cz^^`k`f\ޫe@yUA\c z;N^T5#9022>IZA\hoqlrKbU%:ՕP D)ХF+/SL,Zt/,VB0ڍݙI-ʵLFSr(#8v[0͋ M^a=tSk~dBKD`N/U}f5 G8<cZa(wjs`o7(RZkie `ylp%\ JGЗ QWYw9?:~%RAdgPFkV }@#ڇ jq97죃[MKN*#qxJ ~P^jY͏ibJhKLkdžS(qB]=/Z*%T`)M?wBfY]1?-A\Ýjmr{ЉK?k!МyJ"͊y?H Έ DRINs79GjO|:VtH)kLg觪Y|zi_'^l'+^hbIqk2ܫ"Cr5ZD0bE:n'|{/olA2Zu-&"SՂu2hO m`}aZ]U[ژOFݠ5,`Z<[yoJSY]l:̵ _;V`zcl8. iG{}Ͼߗյ0iGK)*w;Ga!j]U{^_&q uvMpjc,68 O`Nc{Q}QG1$Jn~VMZD&݊~w"-qeinPV$ђnr*o'3$-f{$2@COGhbQZ54Հfq Iw*hKFU{dE3,V5P7P_瘘zM,0$6&^l)z4qOq}yy_?^_x4|}!EqAР` kj5ב_Wjvh$鯟/h" ~xwOdt uJdХa)`A Fg i ӛМc:+kKHˠ4b< e@q036ACwg[9w] 5ᑠXc!(Yf9041`M+ʒԟ@ZUtq{/#Iq@=tLLsɀ頦@`_WKX- @K07rޔ.mol.ruW#I[8>A"%0r:in l RyPp9茱x|o?ٹ8a׃(٤!msFd.Cq]sNO'(gnRQ_ gAzYHH0 ֲπb U{0 ye:\'$Ƹz17@ҫ GFŪ"$ ;3VPsQ(y5a4-*ԎL0x)?]Sjt4&\k` s}t0aѣS0͡D4zk߿;4YΨ^gzKq2n2V 9Ǟ$wToO_MޮzEykDU嘾R|{tcz]ovZi`]2ahצh~ @WɃ Kv1S׋GhfJ7TŤk.^yŌO \al:KB"fG #쨗'Sd1_C_vVHAYolHƣ0H#l!QP X 9cقXEsZSH+}oBE(ZSJ$EdRj!_q+Mb@3į⯿֨rL+Kb5q/\4X TѐLg(~qUV=Awj!)Yzz؃T2vl6SI$5IB㲗2r=Ҫ[k 'V-إgG5IDz2SZ!- ]-21gq&/z@\zݕt{_Is1An^˪,Wрr?u|#ߩR1zG=]>.MAA[Ajf|4v.}b@fֳDv \x-A׹:-m"ܱIsT"i 3 %ΥǴ~oeHpeSr<۫./GCлU2,ps ]̚~w?4&7jږ!C-t~W/V"Fh-Xigue;lfoI?bFbgV`Ndq _ GpWФlPW@2v&B Wptw8Rh ,!hlq7$sk bnY[v }֑]Lg^XVpP_yoK*16 6%Df[]e`͝1beu=l +@ཏ#NbU˫ϣϫ A"lE^_n WN[31W|0~wsc֟/R^wF9̤oYs>@'VVc̮ecş}V+Y {"A(UT kdnw ^O|Nٮ{(! 1*~8W Yk&.hawUU=95ɤE] ;5ɥor8 ϡs8ܷ+>.<52L9^o_^vktD1-`JA#f82G~mϬ3 w,B'2T*R1 YyU}zBl"vW<|'ȧ< f*γ28NUoVyZ1t \7g-|s]/`xWH@,?sΈ%sfs&tI8zu#NRJ{`hh!X9xP+NQ+*&݂KA qDwԆEգj;` k'0< .ۥYݜ'-Ƚ~[̄ wf h2j6XȘxY"T.P$GDW"!%sNbF<80oNo-fxXbM"f04D(psg8b,HׄX2햇Yɸ&;_GsSـz7Od4Ha2Tf8=m hѨQNK#m>Z?}} Uy|{7 כ5[:g]Yꟛe} \G~w6F*: %8D?:y:)Z1ؼxiM0Gbs5-W o/|irC_AcTXi~/"hr[={ՔqTsbI` bT[*[6&wM@JܓVOT8fdH3˩Aѓ6c7 ?ڋ.U*-`C~O6z)%q1X;EgTz]S&K.{V{jtIp~ .j2K^%:=hu; IDAT;.NwZAK{>ľ+U #뼓R84ey5EX˸<@Vó7k }D8.ƘMvʔ)'%LH<}29'&ҟh1sٜg)zk MO8ASЍivB;jk!hFGqyϗ) T|yy1Zyր";eZ8*L'Z|c cLMq@n#vxuA/KZxaT5e&̖3,Xic*xa݊Vk3jO|5kд}TԺąGK&o/Wf9?"n{EW#2>"P 5&^I̞jN^ M@MRU]qdD.0O۪ё9S]h- jQ1pAhֳ?JEw dkhCtʸfۼ7P:vuvvDD.A]۳8I/G~a eI@R"Œ\.ݼX_}ٖ8g9t#;^'+"],+  flރ(pι3Ozouy!9-OŏIcZBYzX2/69u|3cin$6vRe33&C> (lIƐC>E>AN x6y8/c\[2Ly]#gy3u{cͳK}=B=8<%k@@%u4,ban,XBƫl !,r{E51cgΈX\y A+= fG0e7b¿ ܳBl}T:ldELH%)۠x*"Nu5Drk{*THq erf??#h2 scrߧ[LR@+&rn4hr4s,lo4'=2+)Vlj'BPtH k/jG)1|TuG1sź:5k} IPI9!Cz~/Ʋ=u65<` 젼0ʲqT\ =Y a<qpM~Cźxr/ vkbFzvPceqw>,T' #B& u,۴8:BA tuٙm{mO~獔 lDK[u)Rv"⊛)Di759/,o>uEp9ſ*36\ J R4_gɋV,=0{I6k Y^@*S)}̥ZC!BWWCda .COB|!\{aD ՗{vIɋn^h0wܷ7~/H}rΧG7՚Q6s`y0YիUo<Ͳp&{/$@&(\wkHo^ 9lMNt,x|2&yXtNv (e pd .py`?1?@B5!`ϊu=B l^-x-,1kc"K.+i ׹~M`Z% ! o~+~~~<\fѹs`Z&c'5:O$|}yG)ͬ┑$t6\<) oG[[_L(pi}W>=KJp[ͫ1`9p\ ET,˧i SاuϨ/0ܒtL╰TH6keyPEm.Ԙ1~OubuT:}َ=x6 pE*4*f<2=:,d~vKZПA.Sem-az;WaSa8b-]+i9g9z|u-O#h\}]|G5[쏢P 5 VWaq\JW7 aӑ3*K {P/Q9<<`ȧouÂЌb@E[xh$ozzA.l˺]4uZ8MK0yJ dlGt(V oK6 aنfqB~,7 <  f -W#ěML2;!;]Kbꮍw]qatEvz1Ahhmfr8n?G>rˇ3.Iw|^]4U%#V2z#l\ ?w M rǛ29llKu f7埖7쌗FHE&=@'aI(x*E]%HI'U!lY$SZ 0eH`s*,sc==:ʙeֲ@YzpN}WZ iC ~v *-!{K1 w2Gǵ!N7? /pтu߸॔;EkX_|E%Ǎ̌RDWa"^2Rpg}xʹNpS[o6W"?1ȹ!4 :9 :`{DXfWv%#EyO~?(B pGW=Xˑp篻{s:s6(wN V9%)͝O㱮' $TC< 3;~ xJ?IyFuyjpUUV嫀؀>+ݨGSn;G 2WQ:2!/T&xBk#|w Ųd6n%7D+QO c1/}&ӭ[:kg$Q`O{W!^OٟdOZRHdXllfd6_>ǥT~x&p%myq |M;(O=鑩b#pX j`k Z^= HʒP^Lb)wE6r]N7^\*>xN]@;E{X(5OSM2CrA:6}wk 9#S~=#0dʵplFOY ƗնXl*1-},3wཅiL}_ǰmoc>v8#b࠰VH*Tܩ71`  DZ'aw66]LwGD0°pOjJ}PcTOt}ʍ8WO@j kϧph]1e83,JDr>f83gte6%@8z絹UeY/[<@K>4HZ`l5w\0sʓ!| aٷSqkzA\SW)x4@ch[䕎T*/빾lJ|mk~~_מ޳&|內197cUZl c} ¿sNq<'4@5D21r}$6!_H2YDi6U 1:Ō/DIVN;;:d6>ns~}"h4V=igɻH0D +\h듇0 3E ]HB'%DY|;'Mбp3;$ 3e[';iF%"2b!B7z>j? O&4CC\iJ >8)T0p+\+]~q?ӛӽ8ӓ꽼1]ӕ @YtN-o<~XU z,69w܁sa~tc{:3݀d[#x( oJa` }Gb㸅ހH(z4yG@W Ryꂋ2 {xkts긺]q*ccY_. pѸ#9_o麴!N.B4XXll-,KB%xl4k<h=2n^=VYcf([4ff9@%jp=O&8A/P.ݬBw Hd2}n"t&k1EJ|{DC5.g,+EM.t.%P.'`gw}Uॄ? bf>sf]4d2/2̫je]xrH<+pY6p] rkEȄGϷ. ZڜssES,_ WT~x%Ep=NU֓'n-Ug6câc{3jk6[Q~;lHn174Klw7WX+EoHqY|X% y1Kw{Ra{R\?䛽EɲT|vci=;+jS"bRy0Dc쯋'kG:阴t3fZ7aQ!t3(<>J]7dȃo_pT)r^0 ɸfŗ9d?zF{LtUN1,|GRP[&%@y{vzU5eS]pA'&dŏc`س{'݌hknKt VJ =SK~6RY~ũNN~F~kQ!?=  1x93zrR -Kt܃S\/u} oTuZ %}ՉhVP|9m߬}}%p>-;WǮBa5kh:UAd!q`AQNa{׬UuY+ǽݳkq!I'a]LD/mc[f7&] ê\sUVE86zduI#i{k7\0i38 s|oU ߟυ[BԋiY 0pn{LE4U#%/?|t\ǐDr{w苻ܱ>$;*9%k烵~UZN<=XY!s*th_׶yHܵHD&H:?-M=5ͭo&4F!͓]:Se3a?>aRfVjI$_4/תd,Q'KJ6F[\4s$ IDAT#8iYT Q|4yVX{ wy0=[{~rLaՕIKݳi6B۳GuJѾlj 4NW߱d>vWܻlCfQ${n\AHrIto~{頁;QY"ךՃX._ޗLyZj_H3 ѝ>[-`UmB w7Wka G&G ՔE`c"ul![h|kx {k=JQaRXW 5cN/|&1 [I~z R Va(€s x4E%=r>$qxـ$y (XlJܾVvñ$\|E9t_x|3S# eb(OtYdN'Rz^&On v~"=7CR=2tq=N]Pc`ohۈ5: cy cdJAd+@W91_V:7\ }s 4Z,{Ip'!'!c{A2G䠄{;bٺv.̪7<(7@qZ6v L5El=%r Tb8ùT8.ֿ- #Is x#V@=ȟTǞi& 3e;oͨ|?{Tlqע@ JI^2\k9H<=cMLǤc|"v;"uO q-ʈ*bY;S_-ˊ?F6UX Y0\Y*3=P!߼U ;z99"Z24tvDmR(Opi+ -"DX; L<uv| ͚e'abX抔z4Ks ϼ} m?@m:aܵ^9]r?Tg~m{K΅tYU/a)uKIeXpz[7C⩆rY}9ѵ21O5G-e0ZRQ|F%\kw4]׼fJOt|nB&Yqw% hB׵/^aaL+ڵ75k65݉szUO^ɚp2Pδbua*t3hzje[BwIgD|/hPfD;/ c񾭅.Ńz8>ק }կ]Id[ܻ D.VO/l#&e{P8&C :T Ӂ]D~Ͼe\<ˡgS3xfߨ=zZ|_[hSeM }sUnhc7:W_\[O< Bұol=&stixgFs56f<r^ZXp7Ov̪=go';_:S(pgM5s3Nbuyi5a~;7E7|a΍,&GgeFfC6.cݑ}ƃ` 88\<ʱ򅉹 ~2#j"s ۜkneB1%:\7r季N>jOJ[z1URcGln ql\nz1aΈ3Q|G}0.4w B! { u K̵?a2(yao}8Ra(r*(di$љ Ա`+O?5_Xe'W멼VQEYjߧ{{}Fal9Q;Xz=٫_csb}o,*I:zN`(2ViqLfHu $7ײ}'V=|hϮvZ[PNTC/3+77<qEO^ϭ}WAʹEt-O1F=BW]'\hfqm&̝sR>$1FCZLG ݛ@kfl}v:Hݮ6E;6OffI@NrEyC1'Ѯx^*9@H`LOi=7`6*/{ ..AJld1cH msN3OK^`&hUx(&"i-J% ݸy g9Z+%|ց!O(#yHyw:rW?;_~a['|/\#/ey)xKzX#K D46\b2Aѳ!1tv(0O~U}'yѹNDצܷyQv2z#f[m)q՘ϰƷp^҉Rq6!&6'ʐ>Y(S3]v^o@9@=!wLHD5_±uӫ`|9#v+iS&_nP<~}~N`? 5}ycOl uC_o ̺6`ku3@8D'*dD~z"=M#\2rg}9&@νCzs%Tu,)ֈg“9lYkc cWR'LlcH)P\,CN[}N;O>%_ Puq!oS*$ f"Cw ,MҲpDȣjqq{Q8 o;:/cFFhPJiٍt˚θrF8C&* &]`&w*%%kwzP5a-l BWiߍU܏%y4*afeY+fHA8[Ƿڨ l}v\Ӡ\Ӹ`*Soc>xʧ,mkU{_jp],W}{Qp^t ]+j\iA3 t㣼.xf<@yXS8cAuʢ 7Ь&8Qff+ڔp 6g[͋dC 1"6a]s5@GM(.GOBNP"]xf5Q̀#vK+`(”g9m$V9qPM v oVR(0-,?O|/^}Y^0@)ᴄI9F<ҭ1T"7"|cD2gY5<,Oun;wŽP<.怯T6҃^HjErVJEJP4`s]Y 8s&# u ۸BiPXð)4,Ժ+wZ[݋y juܸ9:a+Scg21 RX<-91 v M 1yDYQ*߿?|.\YpƸs ]8pb&= )`2njjWW"&Oxgy<0AgV)mE~gC㶤;74Stœb*[ Q0+O\>Ǻɐ45+b+Sp=s9iYu$-U>.T@wRFO+fSuMI؟eUg~]htqNA΅J .ݜ hew f*vߟ z=Osƹt@ 2 QCب}Zi-%B(QymIȰcD3sٜB,6Y]xؖ05b?~_\=CZӳ36Ez$P0ٔvz %XiFJ>WϹs]ƹY0Lo݃cRC{K:xk&Es"ut߶9X䁕J Ge):>r4/FeaVdzW9T՘EȞ hý=>]zIh&NBHWn8V>Cp{3.1xtC|c(=SPT{"ȉj},OCa iU>¬z =1iNJ`i5 `%!Ǒн{lu*fzǞ\>6&N<0&&рu'\?לEp*ΥvYΩ2!sPw$)g g}cVG M}KvRpGduK磑"@&0s |*V7FD_wKwz!4:^FX嚎j Ʉa,EH>?B@nhk0 PNgPz x הRC|SN ~}"W|N!!CDs޾qJmSgLH.JVuD.ߟ" /vZwʃ~飇fh:5$s< ѹ%!~U|5!4c 7SSڒ!#NR[u~(_%s5еJFN5'}`CAe cO@ʞ IGXg}ov.}5*uu= 0ܬ1i(+`Y-In9"|2y{ tE+ltiT,acC6 wY~#Wۻ 9dF]ݧ/ ئMڧ6 mպZ"]F|UVwnG˘smض:$roBǺfLܧyrU&ȍ :.gN_97:\_39]t5)wCU{ ŞǴY3Vgxs˧yK.t?LIF˶; U.)v|-'=o_Q݁:[%?Z{*{:q!x`GyfЍoP2"Blp?[r LhY*cxup(b tksRKn% *ߕHF(S>vP0^z7k1YxAyBG6XOBp.AQ GF ]p.a8%x3ISa.Ǹq|4-\>W ҩjvvGeY| fzgpΏsݠBB(֐D*<.pw]6lfgGj--M8'B1"1,B>q}wDel|>]XGŻAYWӻ _la,s.szyc̺zM|b Tz Oq1fbk[#aluFmjOd~5ra-xO _U. 5aG2$5TN5#2v|Yz٬*3jf蔉[~/1>glgU6'K&ȅx:2mur*Tab<(ٵ59kvo/uվ~&c-=V@p6==f2?ߖ:/~HÅA%@眸2\ʄ L@l4DfarO}jY 4O(ĹQ, 3aC3p/ <341^8#>LtKIP$0gO]}s(bؗ`U; ?-ADׄ]B&Å%iCFMEf X͞nj07]]Jخ9\+PXS`б7#"=0rQ&VZKpbҍkLuypJzםIFI#skՉGerqR`;,z=R-zsP#=lY=jg+ZWz Siz*6Kλ h5  nHcSkV(Yz̫ZБNsQxjǕ4 O J3gM9ai^W|'u[:S x|Zn!7wS.$pXʑa=z!Ehe_Q:c7,KBild-(9D^B/I:աρ,S{?FQٙɹ18tjwesK}YV-א"Bpʒ}ku.e z?lH^ غgZ G"=7 R2y:|H NkG2 8 6)c>Fk9);0Jf\k&0x,Oů/+* #9Xf$>xdqȼ=w)u2Xv8l8ʟsŪ%  _RUM=GVRgc'\t\~0)Џzr{2e LJ/_}c|OCVԚǾsuu|>rM]dT UZ\k;_<e<x5cKT9>BR/֦?-^sc=+QQӗ5ץj1}-tT* A=Lg\ftVtX9w!Kuo76^uWQ;Z 6"ZJ2V9],|6t۝p×XeP@Og-ԛlTBN*RxfY9L[r} .k҃N6js/,ʊ^#S߅?;'cdWgύ)d²lߓV=MB/!d^AA&3CV?(=K!:ZbS~jH~~>ku;uT_l;%qʜy% ܼy^Z\"umK 8x~ܹ ;fB朻EcudKQTLwv!ѩpn՛'Jb\ 6!PzL\58'WDxDĵ1 0B1-]1;v35| *K;! CYd  sX~7coH+\T: {#ae 3ԩihA_{M@*'0pO#ՓqF[}k=*c\KsOXw`4DKPTŠ&Ppyoo(J+z˳xKDy!NKTqzg u nyV#n?o uF+ {'R>c׽y%ϵh-;Rp[י4&={[,AMt#*ݞjς>-e u[Vp]Uyʩxđ ? #'qMYI'*2a+j^ ]`RIls'It~~ o՘]+x?>?.0ٍ "fV9\5ws+~$`^:Od>)g0Gl6O[;m7}t!Qfu|E`"r} ֱk3iS0˨ +>Ue]4y m]-sYkw1qWr\bMQ(U- mPoMQ{}0d1Z%Ϛ\uYc~^#8U iZ_b]}H D  B§wι|JC=%e8gW#t8uX,:c'oܡ@_k|o@F~2Er=7]&liu[۵279x[,B#Lt5,OHB\v^,+DrZɇQEifysekrC;!𴟞2cot`P߈L LL*WJeHuՃ Z'enkao=H5A{DE GO;a#Qu>2Eeb(a ͨF n{fȱP[+ʡ,-M˚Tn뎬9>b^+`"e0AX += ;qR!LyG) Ac󭄪^Syd97')C~'\X`(lꑘf1g 7vtDҧЏlaIT!VEO1;ΰ>|v!I+K.Ҽfg3{Pc.Rk׋u] [xk0xmڮ fy&m@Q]ȔcԡbVV ,VȻH_&\jІ| |34^@oמ{vC2o= BR\î%tUl޻2Ny4XW:^ R_L8oXz-i293瑦-p1H=Sq,xL6}*vJ銄$"en@Z.$lE|gl.kQNA~˖oq+`J:2u@2&4unn, Y* k0!'b00+̘v@%1c'{ޖZS;܊n3Gy!4"˙e@dE+ƽ-7z4⢥3|}"^J.h`m([HXr*<5  #NRW_qB.=#Joro1G/& |:Y~o2xRa*,_P,nZx`P@eٞm-aAQOV4`(\MB9- "#8ɓۣo9~l5ERi%oc}o_c9ܸ1> TxЭgA *"`m9y.C[44‹&M˜SwۇVz y9&츧%S(J;Mw4_2(5#lzr=c':aM\":~ޖRW.z [c 4 tUΘ]48UK9W^ U2q;np|LgyK:UC%lg꾐G:|Ր֝Opm[S_,G!r?AX:tmIc-ASsC#(9-7J7{tqbyS#:2~N ž\lYiiR;wɿ4 C+0h0A˺/>K,,T4bLXP]37=`o7>庥AYqBWke Z$:@ |>@^5?c)MV1 g?Yk3FC 2zg: s({gڃm2`jlܜe4 g6ڥjktRD_P./jMVH{iZ_Th_nɁrѫBP Zr61PO&gb]pfn!Dʲ넗|wm+Q(Iq3Ղܑ}} .<t*`czsyxrE߾U2P/8^{7M~ AY;R:f Xm:ZpLs_݀Jpa<H@nF:2 6osӎqz_/?{ӓόp'spJoQ! 28͒b˳J}%r\{uj9+QTsl˘Gm} 0 <(=9Ut[J*16}H<=h!I=l7#*BsO׮:t!POY]r:A <{kYPC" QRZ-ӚΤ!"su}b Vƿ B[fhXbg˓{cFaA9Q{QsW0[vW3:s 0Ot/_SXvy Ltvz])G@a̧ce1o}穠gg*7i[kR 5ѓV}qdVBʘcD8{=ĵyu3{t813iq85ef;Ý֭ 0smy? ̐!Hl! qϩ:8bhP8'aY9yYTD,e6|*B=*|}ߊ2;_s!e@ɘ.'Z {r wE%ƘL@"^*CL{  RS3D OLǶطv+9,rƌ UhBׄLԞXEQnqC#^zYm-U[%,EtkL*$k>migiRC碪weT |:P}z<A<9yc=%R kz4Rp^e[Lx7=od&[Y}6v Dc OO)CyxZ5ʡ!2&~#~ a0T}m&'Mֱ3ސC\w02 'n]$:ܣw/w^g@'! ۋ΃F~mCyTPTu*@]cnTj۸ƳltY(] 5a0D&H ZlSzwfW6 ?;?Ͻ;|Ӌ0ld Bo*ok>י7ayQIĤ3p}mvj gթEULT'7(fAv]Y+=0.m1R - W 99r%L)֖ erqe=@ʩ޳A6o^ߡeT뗉tP;{} p[r=-vo7ox|=(RgW/0ɖG Q8pE,wV9oN5ZAPe Kx̬WxP+ZuQg8z.eNNTYiL|x|cWt7=z&@r2}񬖗a1Y/keIu4tΙ 4}sLrO!yTyN7{(_r&4x㜖ވ)єs!sͮ}.i{ 3KB$lJKg,ը/Y wn.&p9QEv.ޫ]ٰ%~&O10# r$DES"DĺYthwa<h'=8DưC!'ʽm?۫yy#|]; K8OFmLD6(& JQI6'3_z?ǡ,}af֊'/||jɲ{I# oCDaݭh g) 蓧uh*mctb3lU_*WIySD~$ M>uUx ІUVVc)kZS 1 X9Vx*'7bNbW+vDECo4 \]$5}͖cDB`Jmu 7 4g_ˢq7`O6^8Pecpݽ-ǚ۾V+ rt})3&p(HZs2fo^M IDATSi##*Y^ h}5=Cf~G*<+W̳vx&M6;PV5{<4|_}5_.8t|O˽aP= cqZ^ wx`.|O@g7~*_wXx[59qfQ$l'2oZ& De}qJagl.m;/6"^3QVǟ46$%K Skۜ3|׿5``~ޣխ!AK&A7}"_ý[xsN*E@V<66/ ʐybbBձO?390KG_m7+ qu5P޿&jaI~{̰c9l<ݤ<2rz*M)oo(K!C**@q|)<49^6s܀9Dd;wv(Aiy,kin_N?1l?&B:p]H{B1+'*AG+] Gν5*Pe%404y~f-l6Dllb#,Q =b=}蟴YE~ YؿX Ηa W _Dov,Op(Vzm@V㈖խބ{ϭM2PDU^Ț8'M^Ylrwp܈dE ɻ)slUŖZV{4\ZkuAo;:Ԋ97O]f'IP]>Y53|38le[Y_ 2FӸW"3ʑnO#ֶVFz nRopl\3vϳ5y:<ܘLlInm= V{SO7,%U&$cSY,}t/'-H *NZVe|]9źe5 +D˽:F$ui&iu_㖿7-0.:M Ϫ1c=l`ھ/j>gܗw#XU9kfY5D k"]p.` g]Թz?Z )?Q>!I{.)x@IfWsӤu7){%Uqݯ2,#|xc0ʒR~֥MO{J/x9NO]z{nUq?R&_ȸX{ FB> pJ,uheVc(@Zll'cPQu^֩Y;5Τ&"<ଖ|8>撹n~lf+Hj@)&넛`ۓ.wUK{T8*mBAމ# `sV(G0-,MMh(::mbS\gje%nQ E;MU"5A9F%71(` ?]i1? ]=^V""7 +U_e-,2 `}pB dsΌ!SF/{Q YY8ڵ^ ~ TIsNc\Ԑab]N4 B йZ;=wUc l4غ.N.|K}4[Y5Sy;=(LQs"`Ugt:B"08-Sm@+.t<$Gv3S.VV}sj`6 w0_p)#$|iKf]jd屫Z2"nYwG q69 iT<eD)PkhhŪHӗf[:AfV6d\* Ь@rB [Dqrx ^mCUXj፹(G#'4ۘ# Uyl<%uo]U{s>ZXȷ2<Z J,zZ]߿^pi'UL#\mPc:`?)\0<;+y=%:S}xgد`VJ`jѝ ~2jvj^pi99p)MN-ҡ'Rr2\M :8,4{iturlvk!QUte&q BZ8}ѫw萹PcRNuhz*`LzZA(L7JkFA,dҟT \g!st .μK *mg\gBp3ῒ/ab3#O  ^a^aSʞ?`b,2* &( C6hYHLZҮiQ!ÒJ/Ț/Ox(7=2Féؗq)e x`Dz.R5Zhs%P&rf5FD?STM;Lo\;,?wY+Ң!3} CYQn;yCh1"c hwZΝF9zrρ_# Pv$?}G}-rFF4hyrj0ۯA>s9*t߿[OV1(E\^7U8?W/1Zj!AQAIS-?Us;<'8^}}T]9UAz9@ru$Q[E?VUa@^k9 7TWAzI2fk~/^;z٣H [툚x\òtӖHG%8<'ϡwhqZC&+ {w-8g s/1(=T&mvAȋ >T[WV?AUM3FQRTRABm7Ye~z#gĆ' NsRuAH0 ,XAŒ@`'؈XZ|Hsz6A&PTڥ}UzB[[ubf Sқu IU=Bm`04z "b1zM{ԴUMe8/ yb63(H?fٮԩ}P+z#luĚc&itѺ0%{*BE[SYc-.wdcL [)q!8b/E]4X-32{3d]W-^@vڇQy߯Zy |X~IG.z' ~{>) t£Y5:`aQGmƧl|X٥TZG| NY~K:6B],ɏq[,j>pPAhHlﯶ `Xҭrܿ4:u! 1'=dղ]+Z:",ȏuw;ɻu`\kcʼ+' >ޯtZ#, 1yxZWL^UH42<.KdW\. 8LL2$-6@#ޣBI'9CQ/xi긦 7c|n[9*qZfݗ0-@0Do8X B({ i?~G ݂<ÏN6XI;LjȮ}CaHX|# 0s:h\@,Yu;?xtݩ@O!:4E/<[!_B㱤`QdE+߹r\+ O<+1ڀ]rgg_eQy7{G~<z;#8rrП[(Ͽj:1 ˡ"qTzw"&QumQw kr6~ImFh)M#~E{)ge- 0 }nEbM p<3;me"mS(ZoPp@Ӿdkc:P{$)X!ٚEuUa1Tzrg! 3*i{qφUN8T#=E @ B%_IQ{, ͅw`uPc "gשcf!dAHΆ^qgr2gOݐ=xDrT525\k 坅P:f`v Z@:n\: eQ'.g$kàq|hOf6Tx6"?U+oܕRH(FL \Zh~%=^_d+pq\{pWC7Yc~q~h:wT?`PXFcD:>!ʓsry]-喧ZPM\}: `y~S'C{N/M:`#]* '8u}vQ?b]D4#I1RƸIz4/[3>lElV;;: `H C,+jTXM NfRϖ:1D:Y/y^L9hz\̬C𞊆v`Q.KFi:хcF*8 መcgXPRN4^*.<=`y>hA`} ةt;8莄IlAkp;zp/NPs7jc-dQkq2#Uoa02֟p[W~,y5ք[3,x;@Vɦ<Ŕ Z,ȂofϹkp2l*E]/QCA;#ՓƱ4S! 6n~wr2,%߱= ֊c~b2G yC~Y)"xŇgZ!N.;O!(T VtNYzt/ {;Ҹ2ўAGJC2o=YQǢyD䅹kp(88u =-2r.Xvp5I)YX26tu{hz;`r{Ovioc4~<V8p_3 Hbo5g臮<ܽA :r &.]Cr\s3e= Uu,)Wk%o==( HI+;vp&?xpaEJ5'EX4г@?/qˬ [xz*2 ûxs7d|l{2J2,h(ժ;J*O5 IXWPUAmb OK&EqY{Hc5gKƜ;dN.cu$z 8l90AE(XS4&,ԩ ?ˆj<5> zWC@mf;]A2.Wy"Q+Օ0:hzEO<:uI`Md]-w{zv=ZZsy@<K`mKKu*Ncu ;bMgtepP"ݭ' \_V1eh-3גT'!@ߋyx6{st*sXD!@7.XIxHIx7E \u]2A}P wa"@#`bY ֊a1ê؀>بhƔ-j̖x$8pN Źg2 sS*ˎy(x&4eȄvн7~bChuM`-xivpMWM㤀vڌ ,}}/,:7ڂsKs'?i+;Hq{ae{< x5h8!|djOz TE;p 2"cT"~aV+?M|;3mZ絵6k.V*Bm {u0r(ώS2|(g>ЃqcزC]G6=kS s:9AR&ʛD¬i` Xt Ѩ-mmw +ώԕ[xA*|[{#=hyV ӃLʛs[+:޸t :l;-Ev* \<;@ai0au1%wL)xH"Q>펠b&(v)m*zR/Eq1A4"Vp乥S&g ']XŨH#BP0Y{ůB+ȝ' ay@EE~PGT~p=y|^ږ{ϟ`zR9xN' )4mJzx~F/SZ4L=D&eU}ky*N#%a ~c7ӟ!Л4njш[xGyg淦1 ޿ *=_y?A=q/?=ekrۣcUI^;iߤ9൅y}cٕ[b~OϰN sG%w!u3q{%uQFzgQ(%t!s-[A!C8b B]kOp,/|*}4QƮNꮔB Ayڡ Ý:V*(*fz "_0x6~W[oe\(Mc1qsNJy,s#,Py k:{w5?]e@uK^w?6N#@ 6wYcrd$7>4f Xu&=*_Xx L{/#As!p+/?O Ac[vލ_\l^K! bcJ[{J'p/G sv =̜Hv@;"ɠ݌ýϷXNo2V{o\͜m`NʝئF/v-XDHw]5\%`@K#( #ˢiٓ/-?2Y|;b-%')I-A|XѫteVG W$ͮpy (߹-@%hbd_":ê@-wu IQ[N*]h$ֳX8'@-ȖnxDMM'65΂2u~VbSVH؉B YqC`9ۅpq2ʎI{Q+cA#cۋaXӠfZFC8ap}bRb_t ,{W$ຯ 32)&z]Yj\kW&)gVC/Ys(RB?^Pq'jQ$<եhvX\H/ `6RY;%Ѐu ;;e7N`cDu -%.iu0k4`\fN H%tj_E.@2~IZwygTh ˦V lNd|xcN@_<ߦʯ1fiz ' \= 03WC QSGt"sCizetcXizwTM 2k&-/[ƭpQ؈JnA4q*'i GC 61l;:VDaCIBȃ9,gY!C"ePP;'o{ssQg;~,Cf`#q+,LwE* 8XlYL<2FZ46In6 *ry" 1}́yԮLel~r@(՝v܅xbߓA 7؍b"f6@S@ZZ J,wh=/E4B 4Hh4"aq1Jwj[QHMtGֈw/?o9T豷!Pw̢7k-idiQ|ѡD:qN_=4K@c>i p[o_h3^voʹ/@f-W=6#: 0o8qnI[ݣ?u~h]J!"bvMvIfI]IwPǑGU 6Xi!{l]=*.k/,¯W`={[J5</s.T-<*MZP׮8bIml̆Pw) I'T\ϳ#["#uuc-<G@B71]1yMʩW\^$[B{VٖƴpxrLs6 c]+ \8MUU.]]Rˈ"{o½$HN<}z3dFywGUO a?%R^7uˈuċ +LQOP!o+0'J&8ҵI?ғ~ޫ/Os/G":Η:XM'M3c=8̭7bPF: 9"#aFdFw'GJa'I)H\!mİ^ j?ӐXBNAfE!Ov@CGw% JQ }+fϔ( G6N/&X>6 dX2Ml}ylNDaKd "N IXoQHPdSAU XL25ၟAئR}}/}~U]+AFy#\ޗJ;dk-:52G:mNYϥ>taȧJw:czʣ/:*?3@U? ?&t\|έ 0r` D^]ψ]cZ݁T(Eg-eڨ(QI)qcVQDÌȟ%INHZQǹ,>G$hu|+`f;Lဧ%}+ǟja7Cg1;Zsfb$xp~Zu)I@qan+Q&_zTޅSr=H*Nu6Qspi1z Bkk'@hu`jiIQ >^C>is5i6khn jU&h И%2SͩQiEVҭIWή hN)%ZEsN*FgH)h8*Wbg*xAaЩmPBV:ʉ̢9n=\|+8mjdw.|ovce|L]-{+{9!TKxΝ%#¯04wV$jMxH[&2"xO-4m'z%Hc{ bl~p{z41* ɤ!YS\ ۑgxS15:\;o%x9+_W? 揭PG>:=k 䩈O6zz/py/_ ~h-8'a" r2xbz5$*Ek2;Ew_Bt bIre4%G`D'QuRim[5(^>[:{Nyg)8uX){)/%qi). Ҹd\o@i̊W멥Fd9oyTf+Շh/\Qi Z'NGfl/q6XU  鞼U ؂e9#GZD &AWq%[^"-=S[{6E|*p"6S[`bMxL߭C~8|9]¦{;U 9}= .ΤfwJy5}WdA?PzQ?JiȨm2 N rxzpS UR+iZ03aޟ]>sd>+,F\G商F{;w}g ֔kz-s`! )^@):ED%Tq\g:Ϡ΃aY9 nVoGz$_(!wœh]X/CB93e64z )JHcnWS˪f-:W3n \$s >ƥ^8ͱ-ZHuUw攧U㳝sN:?^dI/߿ :UU_L}jttkHtn?,H]ԡ|TU9OiHLl:ϿŎo`_U$0Nםe½ ݏ}&=]6~26ZTlSPېz?*=6"!fE*P,΅0E8!*``?k#~a+J$!R䖰ܳ VtS=]#ܮz(9ߡd}vg0IlK>݂$ g # ʽ^Rb̀H$_EϑƷX,fN,Q;Cܚュþg/$]Z#8&=b$ s"2Ѿr.I88SSwy.*z Y*.KCeVu_~yF+u&xa{Q*kCpU`O#< 1ͥZ{b+J,A|6 i)*-Z lҖڣaӻe> 3jgJ~罢4(xʢkq\yJ_H# ,-\-by&aQ,j3 0OZPSDŽѕ3X#r F[40U3U5HNǬڥ=A}w @ ;Ӏ>˿Ͽ}dZCk ;#7kt΃S=c_|sg̈²к zܽt `yy@(>_Dɤ_ra)bb!bG4RmPzx_еE3t./NBi(/^q\SIϿDʯ΅~=*yDEg$hxpVPr@p-ÝFeyYY4“^e\O~s[@[]'}ɕsR=v<(b,c(d+rkp? ۮ6s::oHй夸 PQw>N ;.Q#.[+`І~: q~N%D7:O4:TMRSCW=av<M͟z009]E4565 ;M1_uDڧ+f=v}f@8 FfYH{eQN>mw FHG"t/w )0Q}OXN{ :5mzܬݼTkxq>G%;jG9P *\dh֑" ӀřY cA] fk4 h⿻ 5Ѣ*=Jhvd7=}c_ IDATB$,㶣ݕTƝsֲjsW.N`=gHMGpZnMjC镻 R'?Jt2wN Pr#RFCs,_e=(pz(8M*_yp8Y=yAOX!\x'F@ֺ&/y:Q= ,|g^9>i.WI74AmF}~t(\M}q;~c f=j͆Qpv {5=VR)_gEזJy7A_, Pȣhɴ$PTˏA{OJБw2->:&u[k҄hrR1! &&=Lcr.Wੌe.ᑆB4P뮹xy2D: YD|Gf-r(C[_yY.dhOJ F&Ǻo6y6pVסsw,ވ4%G+ T.J}8V<EjzҀm wh8.g:ͰQ7Hw ҌWK cǟ$ :2LR$֍&8~w-|j u$ {+:ۭ҇xhaGx}wNJnb㐲]J^>ʲ (z:q#u:O5aI*cYZCFFӘN w*~~WI:~RsFZG?IcivGz#цp9Ѩ=+-pΐG~E8TAc= ?czZwOV{fIv) ~>wBIBk=Nmb(+8R jƿ̙JV+{H a BSL(TiW Żb4/;tN? _W"k]s"[6f_eUu} &j gX zIj6B{P (C&?B8X{g'JWHUoì. =shtաuPN” RDZңS ܶ &BbΌƅx`ͲpMr+#0YШ QP&;LϮɗ>:OGx7ω tW-z:$jT#_#uy.h5JP\<2 U@ ]4J{|IMI `yA69j;BoK0$d Q~,̕W)wۛ9QYP>Gؼ0)s- +Ә K8퓀.Yw)`Wss|ΡƻU״;gтoᯉ(_haֳrZ+yVSJ*SK} qqGUA&Xp0.bWunjŨ2P% ЃL98"a.@ JZటFCt(b@J7[T`|)D܊Ux&vLE {91Kd!0|}>TIe}F .EQQ!80h0USj~ bmOjPyG0JpԚXO_s)|ƿf|]O`z0׷ d1wG}uC)ko8i| /r,WHƭQpxڃo>>΍l;~A"y҅6UǠI,|h*ǝS9M7F YA􎇘R1j^TvՇQR,=`5*!g>Q;xmEğgK/r*)4σeu k_ik(=IdjC0l XDfkS(׺h*녛G!|Ɓ'# ~ƶˆIjU xC;0,`e2]Ѡanp €FehXxʲ{Q{Yj-3KI;u ZPL~S1ּ8R-qQ"E]9 j8g % ApL#4T>5?iϗ@lnBy|ӛ'eDl窇sfG54$>όvzu5>ѵR kzHf򗪳2i;@1?2'{\F7 ?&OzLAH{b<]nv!h_}TL D'9eՔs'+u\L\Mqu0|z/\KM-001/QdgDZYa(Dn+ d c3I4ΞDHa^3~zNSS޽T={u1o?]WyJOeoOY~:AkeXWNϔQwZ}23d[ iqU/EۄU&Tw#0 T_: |}Ϳt8y[G%:T}IOjHw? '#/j{>Pi㦼Zrҷx%_:){4缷֞VЩ񾫮?;)'g?M@]y`Y3_EMp0 A/*ڵ.֝[H5&ESDr)VQ]^@jt|Eׁ.;Ne8hF^~{ҰeP$]4:ۀEO^:PYuө]Pv Q#q~@!G]Lc=} 9;eKZF+44oPbZ|KKޱF˛aQd=`uJg8V]ަ *RF:i3^R\oʟ5ӈzWYʅ>ہ(}hp޿';W7I|we-`sJ^ϋhv^б09a5L{oGhG#-2t55K.Ty8ߑT??Na: Ǹ'ͦ|)?Z ;Cڵ*wǻ5 zl*՞14k<蹾(+^+Z2tAJ%Cx[t33s}; i9r&t'j(ϢNE5 #D?MF0^ǟcs U览(Pl礫ж4Z୨Ώﻢ(O߳ i0aY@@TԨw.ęIsZh߀{|_i$BSŰy-;%cFJ"[!sGA兊izQ_3;o. >wr|^bzn֪m~hUmg4e{G%uQRe|Ff/t9.\ pՀvJdsp< ek=Gùq~o ݓC|HسF?_ؿhgV8B E41VݷgDV[,+^s(q`[yX.Huo՘dъ n b'uAm5? <$(i2rg"5ɿ73a n4B{GFa8һα5;Jd8 P=-y~vaqF Bõ:moy1Ja#"2A6ƒ'8f>XNCx^#=_W=}߿<"8BmSO4:2׮^?Ej_٢mѻ13duZԈ\{\6H7Np든7F߳:,0a\ M_^,TE\T˿n Y/8%i" z'Q"0G υa]w@H͑w>Y9xقtpH.)Vʫ$W[r޼n?RmJf%qrC#;RAryϟReG$G0f2* QOs_ו >2eXYuOF9.'\@j*fP)՝ BH>{Oc=bOyfs_ti@W{uHo߹ tou)^Гz;+%FNޜsx20n!M"r' 6'x$K).=!>A``Afؖ]`$(v.SR7wj9l{ J)3 4- OKyӸftIF_S6玪"7s|4gZ(t)7L@ 0f}#MP驦h) YtV,~jB2AڵSʹZ1]p \LK!%CV+jCddpm'ټN駟{~5IqduLnj{RqG> dy3T紘_o٢! 4TRb'?-Փ3&SLi"z{tFғ^tq\GTQҫAOZHh V JKY5uSuT|cuhpX)^ m)]Ҽ.l#Oq=˿±[+ע1), <5/SpOn;{A'{5i)#T6(?eGe&UhߋZ:Q6XÐi3Kla]'UBWԷ_t`{hp$FΔF%f^j-zK)y뚐|=UEg ŋp"=WU<;'jVO3e2L܅Q{QFս!I)t?W&hF}`VpZPV!=<(lku3i3\J}JY6qj辮x^xNpr<rP۾%-^GWL~wHOOy,ϟ?z.:Xb3F:Zf4Q<洋ºs l`B_ 4`c^!fE~*$0?׵~^9+Y|e_FD.;/W)nʋ͂N7=J8S4x/GzW(OV(^_Ka+^z):(EE;l{#jQ$RRRΓU ! h {wCk]+Ǘ<_bګ k+=`Z OвyVܞOcwSET]|L'ei mr]1uc9sp,RL:CχzbD IDAT1S_)Ϛ ;N$uJplX\@3cB?FșʜAioztEtgT*S=<"kwW{c>>X}^F1RRAoj3ջ' Xך505!S@]>;(rM? J>i } O)49ֹodkiԑHg/S\gǴ^ׅ{&ŷ^Bm_ʡ=hO Y}xif`i/EE̲ 00`H%.j/n VZÿ@ ѱҀT)&3d'T?)48#4"c^Q]\TTz;W94ʻ Ó!_JZ-eq5$h,bNdzG1{F_r6MƧ`Iq}_SîF=MKyݜ>OS5seW Μ3zL315:Nߧ2 (o1'H\ SMR7C$߇QgsT1y5x\ Ӑ 9FFS1S*x=ݶI) G F[{ Xk dD33r?2@Uz^c(@0TD_T/=/eK'?iivǐq/_/AQn@)z!CbcqBumԲ &Mn^;TqpIOu]x~Hgҡ mǙ=e|\>V,;;]Ӱ|)oU|gڢ\:q>/O/ uRZw|N:à{:?uU?sGuYMzIƘs6 mq~m_P&:AV^> {eO}7Nh0 DhyՁgs7x #Խ^JkO=slsDŏ=xrG?иW!cT3Q2\ ў3W"e!O-γ]4ctρV";b,Ն8W_FʆC?e! s]#ܕ՗6-ĩQu&!w7}㾮ߧu " 'xXu'1q*ypex=ԽRN6O'c=_T,5J!m eь5: ?Nf.Y@ Jg}|f?m*f=S!]n\zc=ôkqZgn4}Tjt2o/ҀJXh n?sʳ`Գ׈P<ŃMJڵx*.ˈe+7 ]'`B[GȾH\ꀰX =w)%3 A?^>ꧦVc%4^kϣ:/c !)ȿ/F2%2rǟl}@@NM{ۃ7cu,3AbȹF]E`9w ˇ0+=+CJbq20qN(߷SA$XaUF"yZKV}÷C+Z΃dBq,'_4xrrm{'#`pw$4eaT0'ja5 eX^֟OF#%bgBe XyQ/q!~TDqaE_xe|Y,fDs : TB&3;T6?@v=OוZvɦZ "U8n@Ҥ򾤙PEo@ц?dS1r Vz\d{Dحsexo$!7s><2_ P zU֊.9l!NGuaL.Ƚ!qa46. ޕ'obh Ҕ`p]*Wwڐ TAAk@eFiTΦޡfU뺮$7ShYbU}!-ugG(Q~"PCOe-zS`1-ѕ\i#;SRgob30S)lͥ&83:xǩ+~DQ{zQX}s!RhסIrUٹ"Tx2 A&|]?QDS10Ύm,4 +[ z/,QyhH@Ɖ?pQ υPTT(g%(N#Y+^l&+poB:~I#,Ѯ*{Qi_§b͆-K~f* VZZ9F? LbO8^s[z> o'#Oй(%~ǒ٤QeּTPW6`j`V<#ecvBzG+5Q-XQ a`)SI=EG0D#QG+/ L:/^Gv{G`l`DL)ol$Wi$CD𷔿?-aB2ed5Y׫O)"}ʛڭ&<2LJ s {6QE3ID2 ^鍈\&[GCQZ3Xpq"; c[;5k::uqm u`2{q)\,:"P%rUQ[sa%fà"(ϫy`V`ZちÀc,<#VE &p%Օ/'Ns}_~ܓ.#0v8iV!1[Wyu])ش$S^TȷBŶ,Aqw*8AlHw*/D0D%RIKݎDju x bCG*) "-*xO5.ư&#k|s œ_cuΊ\uw|Ǚ?,g'eHE]9^Cu~U]z˾ XQrYI/iUegcaWg^gD36"5U)h|;/߻/(/HgbN8fK9\ sK LYWۈKEHH8A pږ9'ɩ_uH5Nb/\`.xS"`6) W ضsb>F/`+BXl$*QK2 d0-SF÷8WHZ4 u()Z`}ߢt7*W _BYG'/yz^h}Su@`Wi ^ 44 Oo?W\uasf0*j8V40 4zcK2Q>6o|?ř!м  7ʾ+JW ܫ#",.]GFڨ`e#mfN}s y2>7ՉWS;E|$Xi!8mQ*-ƗkXO@ =CC %@ZQzxb A aͅ` lݏٙ!^TTb%4p""excR``['ǴP}‹ zӋ 4Tr͒ZU g(Y[FKF&.7ς0y>5WclWLNR_c){֚UO#{h 4G\-VQ%,2 Fxe5`(ԤJ )V2x~#-\w5EdzfJAbʟ=M@eYB]_'Rv**V"8=? UJ#hf^&>YL!E Őr nRַE8Re3~No=<y}TLetR!MXz(rq r,GƖf.HycDFm'^Hw8MJz{I•B_L>S)|*bv"A|msu*+IFyEQچ:_nb.h:˱rzM(R+ ^\<0k6fꜙJ_[YxK|y䴨s5У ni}yu#0x]*{xV~Wgk8@)z\5bOs)h5u&T;1zu6?#9ǕYVƭ.۟*sdHu~ɾU:p. ^t1r[rkk}8(mR"%7ҬE 1sD 4(tg@B<ʦc%웴8g'-H@@B4Ôޛ[c {my-sfώ& &Fm|5o]U* hH¾QV#={?I\VP{e?{X!8PpZڼzv}2>[ :9*샯LvWWYk+xWѤEkkIU>r.:W*GVo G9t6Ě%<} 8RiDC^-ޥЭlM"{Z)+Q>*>V#9UNb$0 XVX 3§Bwbbb+f"|2f9yߕG'(5(mDsˈ1&߁^+V97v)iTLtP1ecˆvoI (pՊc,z*d F~XNsT0 "zv@U,^S֖so+m@![9Q 2+׎)g~:nF*ٺ&E3ZQb祼kL{]MUyb4{ a2niliP FUfe$BGL$CCdM5^_)SҤσ:+]Լ/&beMk #>0Tg3EXUWI䴗bg^z,uu}•}M/V{"SJZa8uD-b1{eT;RW$V; ȉx>z*,AqX"68E%4`K0 h2x;+@ 4N98RC1+(jT*e–b^[NӲ!}H}3ar'9.a. 4_r|#B3+Ѵr)W`\/*]CNp>x_y{}XREfB ]dgz.+ӻ gO,`e xU|:YS3_Dθ uH@.u)j o`o}j86Bx.P~ Pa'Ih)YMQgm@hr~9<ۋf@vak721rl⚘Nwq+<1,OD)6)yi@  T'*c9= chs~]"Uz IjD% ?YF1( ( \qh.(KU0qr+%^UK~ lfF fD"ۄG;ǃ vh_SZs6U,E6VYa2J;xUwU8zB6eY|fpx Uw8=a8=꾹`wPUBo5D IDATrϟg|7!"}eaUz\UA`ħ˘|!h(2pgIzr.+le4 kuW\d]@KԾw(^Yި&Ъ*=J?Lj+59J<`ǀ s 'Wg#k*b23fyHtȱ)iD*P {ԓ-MUš;px 1B&1Nw_(9䖇6h(Ϝ`cDd! 8+D-i+ 3ѢkΑ5`sg1[ES%?{_cY&vI9&+rg|Xڙ%cf=t ?-$Iܛ/59YjʡtXOk˗}` &=O<*pW}IF0κʘUq4OV=2WʂvmN IЂa%>9%}Rp 3@u[1T#Z?+(_`HU>Fo@JwBZPUCAk)/e;$ZOtk_Ͳo \wWc-F ,ਮ:G``0+ŒsYW[sV\>--+(^=?+ DJaa֚\ yЂYpA}rL b2TV5YJv^+Ek^~yjsP++\@+1[O"x:^KsV1|+0"5bs{Fضp-Y~S]sst:0T[Wsn="jHfXrWP}]{z0_j(Xy%ުE +B_y\l j&?^)WΪbJLym()n-\e:=&+kڏaKܶ-Kzj~|~6V=pp<ʧ6ku9m*8LBM=%}wO+?ڊgcL]1pWӡOK{e;A]WUBzR =f kQ]zQ$; M\VkLϔ;\+8.YpZ{+h"W+T=ϡ%Nc*rIfRT:"}CXOsTu&\7< ԀJ :I|#{7 ^+1)VfWUXij`p~',J?]z#&Hɷul(sm\YN:[ 4G8 p \5^&^HT ԟ*n͆b*cXDEΊUަ+m?E$چ _HUYw Srڥ8F 6. i)W]wՈA05HeӸL^СTY&PTaP'6 4FnYldZk~Szi] U])͓+|]ef̞=+mVWumw  D>V]rJtU~u~f8ʴm3ѿ?=Yuy𞮽6\ǻJ4䢡MxZe60͉s4Ǡ67"^[-"GNDΩ^V| Ti!tZ>?d4Kse{fjXѫ(س%»s 1c펙|wuև \e=S*a33JԪx =qD$}H!O6@W V.\.:z*rm%ϹgExp#Y*3U"Īظ>VE}b vwjϔ|K&ֹZ<9*%řM3Y׾z`sp+0wCmc8W!}2ol\_g㵂8W8wVP]۫;y;Oh6RqE&C\VYX>+SY0B&y,΁^1IQNҲ -0J G.?Y!@ːDڝp%2SD‚{y1-!Qeg $\)2CE~f zsz)$[dS@3Q qxV,\'TxgFWJ cF'E:+~}l .\BW *k +ez%kUT븮xn=S`W4߭ pmf'Eȑ22(^jl!Vpna9XxuK+ VJs5T|տ&]jCH#}G͵e{Wl}ʔxJhٷ:.l.s@ڷ@/mnLƪ+( yQHd jF_B.8&c6 I(MT1yufjTg9^D){lWBD}#ό&VUtu!0Y)"Spaz9Z6m> v)@ fJDEJZ s9dqGBCJlk r3ZuEJiLgEUO {:G+EK@3EG:L>Ѓn͓4:+:){&HVW|ky5 H]>RրR#ljz(0("l5b+id㺥`+=cyջT޼N\k(A1ޕk1"ƀ y¾BdmUWȤ=Qq" 2ѧkTwYcaZ.Si`tHOi+,"az 6F&2hWD~ ̸ ߠCm[x3>8"NkGlaEE짜o@ 2cZ(*CHމ#m?"IslƖ}4fU ayQ_)\19֠L@{W' D0*m)e(ke9(!YK"=I5s&Hb+T]cС#hb}KP,U>e޻8E,b{(6*^tG@>C'vpfk=ˠk;6cU1~^ͣ*Фt 뙨 k hX!0Xh]Ǽ,8 K Rɿ*b56" ߹Uf[7cPlik@D*RYLaSe>qR rV,⻺XQjgy:8։9^ Mײ,1-ʘ) zky0W2,` 2p#:!)eWZ B\ϑ\^(*:&L㹯-7_s SzX{0Sgcݔ> eoA"~T@!ȋ]˕ESIt"y(]t,IBws+>֙jpʛVMW{ͲZHBJY}JYD߽Rdl^ov^rry?B ң,nI;;״XqPH' `p8v-ʖ*ۙ)|1g$ f>105 X[zTk/T/ko l%nՑ,!L!Ah`A"VUTg|WŪVQL^ bYF*s[g&g ғi*=;P9xv$XL qәfh|C].ypZ\$H}:'(@ 2~vDa#eX c`{䟎־X+~Wן LWhHī/W}L5 ϭ[xf]E$μeO@["c@l[^04D.a P&*n' IuKJ'ȔVl]y*s:fF}u ]溱}aI "s k}a(.ZF!H%S@k{ S^lzȲWJ^'K#@`Zvj?eztpJ(뢾(vǙGWzp^y BZJ^UQ_\fd1xwZĘ?H$} r4#a(z|B!82:h GղMuEsA/1}kVtD& Hڶ%vYle/gP[=3NFZz!kDw3L!ޓ:fm2U5qrY!#+I1UnMfs߼"eG!a0^-yW R׭TV`w+5\ 4?^ؔGh|: 3N(VLφRBR$Ah),=Xjڞx)?#ƓmTP!O`I5dxd=5&WF<>^d7Rgk |Wkwlk_}19-Jq2d- -8mEk{V|f[8KPV޺{ddq(b%4l!?|#8JfOue>sqYeCPO>-.-20Oms3{E $?ob#ѣ Zv|֣Djh͇yD~CJc8WG3Uʾmz Vl()5+EuQ@Q!@H,zxzL#ZN d`1ֺ }f TvUHY.`FEm T΁c g?"U5 H eOJ[lhӆQ\~: xv(`UAi[$f#Mh6a`/M]2ߚu$1(K*'[v\(}<Z T#J׺i^q40,c5]$tw G1p*/Рy >P%f3^%iU;5n2Fnl.nEv.E5AE Ȁ\qB7fIWrw( };~ۿdi\gu"fŋB2K*GM$|fˁ& ]σmx$0.V9, w %f7dD֖|H@ܽҡד(nRт[ڿY'A bcK7oʇ)ER}?Y3W9>W߷ZT#Z)k aOwq@Z}>:gmطð V^+: YZB(e3fHOO̩`yf\&`ʉ9UnWB=`>?59v5Tbܤix8YUX鸶39/fw9ݼ*& ,Ǩ|?˧ I\( v[!2|okbN+bMHШ.2j۪=/r9bR)Hq]I`ܱ 1b`+Vfm4[7^D}oSz na.U̞tt f0<L:n~vu,:tPy¼(ȈN:5]19mv=5x0pm8̾T}*@Q`Jhg_@Hߊ^:&'ab[+E](x{㲾&JL DzPk":̵wv(hgaV\pVF>ϙP?J[8[WrJJ 1dd@Pr`?|߼uVz\w~dwL@OfP2a3[S,Nj7.rUvgU:иuk~@s^Y )pݍ\a:%M ׳˹2iCQaW{vRSMY WWdɰ(cܫpbvٱ\E]䶘ʪrEs IDAT6xZ~ Ed]h04 now6G6^Mž_n牏1V7u ]nzq]"n &}&=t껖6fM MSwZ4ؾ4}Һpt9*GX?}q$CԪ @bv|4R"!mh K%[Q#8E/yO2,֢Hd-} 61-='V~DDf$*Zg=OCTs>cR\L&f*@h~`g"xœgYۻ|( (%H1DW:HYrW,)?ӈk]m\\sF l"5f.@ `b1W^uы+<4{V~q.yOka ]u yty2.?sG҉߮y[tBiK%]~elU: qaxZn\OPڂo͌5agF:_|pۚ b)/ʍUVB 9hOJ`5VDfʬiig̟.YbicYL Sۭa%@'F'#[~,$nMD*㿾I( \M.{X$0N-ATDwgjEQ% RLh'pTΈDKed W.จ)G՞L9+}1G6 hQZ64m\]-sc ЅƺBGu%)<<}4EɈPhRUcS4us?m1h k =M66ʀqSE*wp4k@pHǸĀ0A@/͏q* 7<FKB HP25V'B[6D`XQDHߢBx&4(hsPZ1",Eg:L{UB_vu'߫T9G  N> IV^5@x*^?oooag'/^X_q!( $0yGȞgP1٤7G,vrlUި=#\7;"I{fE1)Ezo$ {zfu̵?)^^{֜kZ} eSohk̉Gޮ!P@+G٩1$=T-*3=" PMj\<.\ya\J bZgWØt:ܭU Q 8-aUXh"1?>8D̛5@޿F߯ҮrݺmaTWZv2'jtA Yb)Bgyb?J ̔PP,ħ*$V TJs1(ƒϔP&r\&~H =+ 6l{ /w/w׶?}zH`f)W<މf֫e˭%}V~M7t,WOH@b\6M̲8DK$]d\Vo*Ǫ"^i#~B7w%3NzBRx׹Y΢%O[{4\Wۯ.N\ w>O%^5ILcs|eQǿʩ5^oM4* !p@TTFXDDMǞTpng $$]dDV,?)^\3%_+¸Pd&fN^2z "ޜ&ga3^R參x$1h&> >*}h@4۟u6%OJ=ں>ִ@cS5ɣO hђAtz:pm߿ۻu$q.W7lhxw||m߾8 HC!NLynRnkC/]yF]kEbM`]9u=[πNj:^ʛJvd]j\. 9Qpi2\ۋq㳵Ԉsyz3n!5quqsp3zX{CʿuT5cDeGtt\1h9]SUEfrbbAiz bO$S$\tK*U->`?zʠfLL~X PL,`1\Z|̬b[̅'ֶݣeIӖB9_.vSXm?s+gbcYG@utVy{mA=\L\)`덟qM JJ0Dz qn\ dp+wlځ~зp} w_ jM0ʹ!7Wˆ/wӆ??@H ٺT`~.Sfϖu}sǘ̀s5z~Ư0Χ')'1$}zg&e| u\i@$(ciz*­wk*Z >[5 @չ>QhWC*0cs-ExT8RL޶ * l[|+?nP>!͂jq/zHaF[&$3=X2WZp\Z{ mZ Ūë׉|^uE!3>j̇q4O͓k*Ue Eo1em.yKA80hrPywVfH c l}R!AD#V2g{m?8E,{In*{dd{b %>uw14TA;;YV1a#: imTT%YJ_XWJFg+d/~Q5Q"M};}{P+#h>X30;YTĘs(`ř|{64=p^_qu|ݔcۏ7j1¶w[_ߎo`t|VQ-RkyB)\Cge3ˆD2 +}s;Rt7 p +w?ҰmItϟ6|x|K7:u3j:1)s~qOR@oli z-!'Ua5#%u JֵĹ.VbYkJ5@dΕnw\e1( űXTBՋ@'ug蕆.(k9/&'|HNQ\|*s2<`0&E2ft' AAtP. 8W0\p<,qJz\1J0Yr8Y W"RO s2H(555Ar|1X"~ 0>4no[3^o77l[Ow7[_ ~c?2l_x{~ϠA]+( : [x<o9߀BdCƁO {}/w6 ߾?~vx5Sx6_ ~x:[oo.ww|zy x}~ş^7x! .*^7Jnyzñ6t&'}n7=%52Tgz<lM7~bbޜk7S4lHע G,:ǜ< @Wᐎ_m8chz`a`s`&%IX$Z_)3 1`ZιV/X74{f%m-AB\G?J4k:`ǩ=5+10o?KE$?Rq9Q18#WE=j2DHM%bRIFYwm "V}B-HFZw"A#bu`'W` ;uF>s:%Ѕ…[DWTyʠ\H2d>c#UЅzO|x}?rO7'<>x}}w||x@ /w ~7M C?|.^W<(~r-]v?geI5 (_ zHЂz5|ika>W/w<}(>lM/oo"=>;>;w0*~厗[[xx&0 x/__,voa~SWlj5onU oV} /Mxp&; *|+HtN>S!{Iw%^V즆b̀wu#+tkucbL!g 76U{Owt(>o?5~_M6 Ht=;P1Al˪N5X5k$PʺR[QE8WٽlAq:+)ֶzV ?W#9ө%jSzJr9 `EZSgCfɃhp~xl÷2.jmTޣ@Lk?kqPb;D:vF7x}/ю8a)ұ_n/zǏ;zϸz7BVJwAmxO/ݶ1)&v<G[8vܷ MM7/gt*o(G:ˠ [3 xw>Ʃ ukyW||cG_Eó+ߟ3+P!{[3IvmശUE[ ̽Xū_߷6W -Ϯ5^{41B<.pTpXͥ]s>gE_" M%j|fu:W QF>] T/ G%ۤnkue\ =}ZH"v7ٯ[ZW`lEؤq@ IDATDmCǎ>p:6KہM0l/}ǐ>>>C˧o^_nغ~|a``q8gANɋhSܘn/˵;*_ /[@x<Ͽ{kh}ݶ(O_an;~tQږ*m]pm{5ǁ}^Eۏ~ommC:^^;>0-qՋX{@wppȆ> ?Р*mMP(^׭hPܾpvT~ço6Mxó{6\|e>!bOŹۜ4j @KʭvUn&J|xf0;A "x742%t~q\P=k[IZR3*EUԥXS!\MrwA>TO SlØݨzV@S$> Ɔh>g̀V;oss),QEĭA =/xmM1FX9.-%pφy(on;noЁoa'{ R;ANZuFZ!Hf Ȩ1ϛW|m.o~3@pݰmV.xضn8~ ha{pox<$@=ECa{[ ڡC!7?~{ŭo|<>pz:D ~(i!z`wg㱫)ۏw |x} }^&n)̮xhYn~ ǹ6s-ASJ{Q@RU&&Sm][s`=pm16غ*ϊ׭Cp ~zs{Pj~pvtMpq5&kf?'3)[xuĝu^x|@nm5jsn8ax@6p:nb7`YL zD! v5H;S^~\QW["E`0jT0 w#D?P*IUGSɖ{^q}epֶy ˝Yka;O`2SP=Hq%ԆNPZd0MPלZ:J{IvD\N%]LNa:DCLƊpgb|uvgg5j{Rq}&=נ1#QLeڍs4(wS {BAa;`nvm mçϟ>>>8pS+_ikQYQZ9>D*Oe@K).&fr xs{lrxiua)Y=?v;l?/غ} Ef۶Eyx,yah8御 caq͏tclc 1=>,o~~>u5u:q0_ 8|8?};{0Ԛ?!"@;Mnm [;0R;C`!6 (L,M@lI&D7tUlqk[3݀JZ@;X4g{;ތ1;~y{&˧|M" ގ. 4wWZŴ9ʱ*kg/C-?"JKy@W /6a1gf$vCdNؾji SR\fCW(S>am;|^YE^s,GdN-.2 dI&˫&ӘN$ r&{Q~@~|5a>]qm+4&֟( {o/ow|;8ގ[wcDZ!ڻgZT @:/9P gtL֔.g%gk]?y; S }{}^7CAoxh5&yz;~^x T[';hk4TQh8zcZ?X[05pA0mTD$x___o6j0p8тi}BA؇Ov,hQ:pMos:~<22Yq 3::&a5.vt:qNՆ1FpՀDk,~a3mC{y Y v掯kt7גG%*spt5_}u4g|xuoU{ldaJj\҂:n7(-v*PO4B<+JQ彦4;b)u *33Nv';S!c ^?mNWqhy+b ȕV"ZWU7zc%I=gIxby VB9TPh2RBdЕmcC23,D+2  CT@رy.YQ{N3*sH1%~ GEnwmcƌ590U0 Nsud}* u]~}}a*"%Li&4Ӏ/L7L\߆ ȁzRag `a!pfSK? a|W|? 7I D/f =#:A'!A*pq}#f:xiyme}3M[gn+LpO+@+'k';ˀcKF=?^'C;tX߅Fnm(Ǿ6=t7US?`" ,<\ v7R2Lw|#JIs;lY+:IYߥ-lh+2ܟ?颁Ugau$۵%hk{7F:ni44Kz(<\`j ]Գ1_ tS&}]<A9DH2's 6 'TuaΉ x]Ep;# $(]&8Vw1eA-P#w3CbZ'q^뜘_R?Fe!cȝA|k¹&ל1%Q$Z/L(z'iW8t#β~׸cN|(e7 @=&L)A lvGaN)6xZG=cngI+]u~s;QY Etjqs<h_xu.Gka9/!x Oh#'@QDiTy_8TN!Γr4D{fƀq|ǁ9M^JƗz!'.hx/-w2Ĩt N3əԫ\#a3m2D0ƄâO0RloNؾoԼ֋ 30&hN4UcEk;Hm1X zY ۷W|Ì3|dk.1.$@rIca'Qǣc\(לt8xmi 8. w1:5'Jʎh=sV`Ɓf6'4f8[ !)rAk8' wYPu>1F;1F5*ux'Ȣ˭:6OVR wK;] cL?O%Z:]e-\{ID:2zL*mCpL3{p4QYaOI2HМ@QrT(+%>U`#z1RD ,xM"y;LMr| O7ܣg3kIx=JlH!#N޺#@a$K.M@/kt76:g](ȩ'x|Y+ wX%xe)뇻K%ۦ"yPQd 颢Q0xWXzn Qr>o(;"9^n</"O6c- ܢ) \@݀BܥP=&5⎧ wEsewPT1@Ʌl:/V` $ ]xnmN cDD`nK!k\?ap4sUNE5oy~~?n"ŐƸ$r}-m ux_ Om80/:3 9ޘ1sZm͊4uń7wygDסx<뚸FsT(i0U_#! 7`\K"tb3aDDHd mLc3!sM^ 9I+ _MF-V6=ulXHj $y٬ {7& x 쳔$V HLx__ya(p T ??u= u'l>EHzم?hx<`;@V5Ĝѕ;Y3ҍ/+]I9'lr44=F$ 9,GΓM 0Co-TNRk==k@(-N2f:i¥tlҍAeQjl:%3:$S7NQYSWr*k^[ׅ>[3vVA/&xH6Vn M/!P[/|Dzh}`Z^8{+ IDATH# K4riſg 7~qkmȀ*~4娎7g2'cDjk!Î[ke:byzEocb@kHRix>yqPPڏp[ '=j#@x_*hr4 ~3Ǹs; *:>jL]sbMl"έ #*δ#1FMT'y̜: W2U #n 2mb/l& ,*#S0}<ฟ+Ϡh`?t8oF T9N'M~ƻwIoPS/Gf;#Xj& nc ^x< j߿=׏om}cODƢHDB8~`p*yF͘z::\VW<0&>/i.pQπFD*1|Ѻ.5'9zpǁITEَ_,t#s k=KLdABV2_W+c=КqἈv΢p]Qҽw|___:G>ON piUtBN:D3sL"Ia~߰Ɉ܍1.*ffF|1YOB "*itb Dq:;Lk3k>WsAK1C*NԤ'|V*atYΦ^{O9oO`13)P+6jWDѽ0 Ԣ8uizo|-,KON#bOߋϱ={#XWfVTƒL[(P~ ̥l4"y wYf)nڪLo;\?J[[$$ Q+Mm燋>΋>h;axeI/+`mBn; պAᷴ^Dt>K6#^ڂ]Yo{PHk=zlm` C"0~=80X]M[s1N'3ƄȒwg>Z}at؜xmP?=#ީ|xg+NDOM|0z*4Z0|į1&.s`!SH̘w)J3 $y _ SCu!LAxkỏy@xb4ׯpĀy6 &:9kQDp]c ̐_돆s .`Nz"dp0|Pe P100#9z6+R<˹3"Ͻs>3 M:Eg84  *E_Jqȉ*50ތt;^qҎx=p׉I.ȷ8+(51䌠*B;_P^ݭ5\sFa40Jrs"0q `La5UEq Y1J:'֟﫢A)i-N ?KOCl9d4NNuFP*v̿p#"xkpiIT,D\LGF32e&fu4=PsZ3k[ó?xl)1pԫ3XTAq΁Ns@ccvw4/[ݮ#%mǭewgiN!XŜRRmcˇz]ug #, wdzQG㚋c?ynu*L=vP= Jxؚ%qDԭN jt9g9R5Z6sӻ؛`{FB+ZdfV!Ha\KtDo .8Q76~:ܽO ?5fAܼ*{۽ܛb7GlBժڂ 9鏗]כVA!,>t42 b }B0G$hѧ As87FU%$Wvw0KqrY8v^$3#H :05QU2ĸx,DMd硅a%yZo̧Eî Wy G8Fp` > 3ywɫ5>/hk ǕK{[Ns\'EE1zIYṕuMםSfls>Uv?cѢ!e _,sQQ0-戳ӡHGQy|.@tz3 YX:Tw5'"*BB z`AI$Ŕ'xN!bJiLXD?9B<*\&1̅ @ /N w<ϩq.vJտPR 9>:2 ݙe$]p_Yy1f#jG½bn,bf4Ӄub›nNr`C ㅯ QPH<s}^U0rgb8\p'5ޡ9Z:#"Iuz\6) t6N:eʅ5ժ% -up1WN/kЮQ4a6p]H\#7kOX?mlz_aQ9*PYT[7F6Nլ`4cBE?4yFG)\ 6Oؤ3[F@B4E8m68dvLLs<h;TMF8Y {t#P+W+8JrF{(͜s3B4tu4%(Roi\L2;h}#}1xIbUg\%ƽC'<3H>b0)Pfá5!WѠIqs )ϘT9~! :Zo(5 wMX@B Qs|4p "kUUד<2@oGd)[*w'X>: ƥhdr Vv$)ΞX296K^JC| SNJknk{5,'"_ݜyKZcs'< aJ %DfJ! ٜ*8`oVzpxIg!ڢ80ig{­?R^\.!(B.\Cck ^w1l,;JUA؝qc|:9;7:=qzPp6p];m^ СX r ׭aYOxܧ@, Z@ {Q8-7ioceM1@ua^')$`QUF4?zC7Ř1ȧw1`{3 ,Z@Zkxp7"?d 7MUpz@XJ:a I|ߘ_|Bx^PhGR.X緻ٯcˍn $\C򜍈 ϨrL>oE 1<;'7,􀱓%4Q'HitZVWH1: '/huY& Y|atܙ#<&\?7~W*p?3DQr"'ŴH(Z a1v`*P]MZI}2^W(q+&z0vXXjbRCyx߲!O鐥{$}ҩHCRQ5BAe/ߓE4"q~"خ@;\-nEkWOU>s [aY{M @PcJr tiDY3]`8#`Eݙ8DKE 9xx4!yb^m627f_9&^wVh2S %:u1us\~VD38= +MTU*~67}w3-QkՄQ *HQyUib*pFS^\k 1$L`}ZD RAJx\0l gw}hN?jjmsY@(91& mhnlkf3 ZLxl8;Vc C:GeOqi`Lk_.AqgP37#֎qV֘v=$U} T9q aA yAZj'ؚعHH[vi @&?3C +IԙHz?gKpg`DRCu:1g,jZ(yAGm&S3-+x_LI].Dܓz]>ܘOcΝyuwlsg[sl0YC-Zo|L>Tq:||/UWd,F9+4 PU11H\΄{xLOh Zhɶ\9F+ΌTbjoUU.NV;fwg+HL '&XhFDm'*ͭpʗ|S R>f:ZdXx'mWeZ>SOpϳ]΃+!EG9$(W̦*Ң(1 0Xh(,^SўJ}&L)E0LvLĨ#B”;iD^ju^NmYȘ)/4z!9&HŠ"'P֝,ХwрTH;Z_{o66r9*5# h 4\s+Vnz1Ove֬٤ 0I0'M_sQ `K|_G9;˂#Ӑbj Zuƾ퍔{~~oOO]%˟;xLQ KܫNG]ñ6Ȣtr2 竞1tnh#a ǫb ?a 6|RݠFaͪ?4 y˽ Im0OHe[XY:UU-C눈ΓU/'-rFQ28 2~=V 3r(BVhDsFe'9q jό &ᙆq[J95DFǁK 5Ѫ"־(r: U@@xokWYܿBZ2eNre^Ƒr|&,(w1jb]LD_(Z*yfFTL y.*z#+!ȟ=鄳9R"[Dl4a퀀P2Bk:xjm‚u d#/nDJ]Ty[ k  sk*5)tG-jfJV*e!{;ϲ#e07xY]P! "5"$yhJga)/9XuhJՎ$s H԰7Z| [#'Nrd N<9=E8ID=NދLwUi0#%BzKFtJP-,Odʖ+phW=e4ӽ+S6'kG5BRi{*M[)$;uZI5髚d[* IDATUH{zrS9ZFNqU Q!Кx['w[q*.F6CmRzO72h媞}[V;JaG(9ـʩ)=-,i.ڠ[Tr t_/g3NHE$}՞"NmкeEm- =n=G*dޜ`>l è3,u+!S`S]0Q8w朓7eft4i@ޭ!cxMbRfS oݛcf! J5=`w5?txZu-@"50ĈNtɶ'wSRWU+7l!G 7u]AU*aۋ>+d[*d+>-@fB lvs20 ;`bHl2l 9]1 s3is R͈p$HwDAh4|@ҙ3d3bkP6ȜN1v]+wUFoWA6k|3]!Gy7Mc Թ.^"wc[˟ehCϜӐ:=t&I:9%Q7fg*A#slWx @ަ$[ 0g™ۼ,<.,Ѩk$i fm(]Q #sQ$}pX]A85 c[~OϤGʵ:FBZF&`Ժ3)P [%RsrTJaRV>?ٞ廒iu ;B_ڈx[Fz44r|.y)Cf{ijjCOYNeu2Zk\c6R¸ɗ/g*]DiR~Sbyn"&Ʒpp,ZB\" Ztx jS = 2BWhōJ8|OA[逌ږgq 6NEgGϊvŭ,6m 5bD|n2qe\ Ղw ` U ;I $ 3QT-"U<,9hQLS/V銬Q܌s,JC;Q,#:[k۴N/(D ~Q%#oT{ sniRoUM#M YrlH$?z)X( Z9nemdV‡sȈFŮ9Z;R0)`RphgيsYVLF qΈ2W?q, nvҌݐVGԺob Ċ TK"`H~Sdy߄=a+c/Ky@kbPוi8vC4F)$%ˇNU HiqAK.)0{DbkMj[ y%CpnMc'W?ˀ1M:&,c[mvP(L-lݡ"5\jIܓas>ims= ,}\wM/K\ Bgd`:~9:B8ynNJUB挽[c>і'0nq SxX2 r>Pzw/oFdM}#B5w9 HgL4v$%bNLNas)iUʹ+R!X "kXgOMߌr2dN{UCڧ73ښSs?`Ήs_]|m/~i] ˘njpZ?΀Ԧ$:0lޔ *4F*X C ժ'a=EW7A.N^l"y3z;l{!M"Р@65 p3&LGG%HT M$^юB1>;l;$YtX7},9YU2Gu\0){Eptz{\q]LhcBZ8 EN~{n5 .Hƈf^tl"i#]9XrH\Vd 1Y*=sn 5uK*t4$xg;\kp 45al,堺按xt n"H^q))@$ς@T76LAz:ؿsVJC'UkS;bg$xwkRC5"d빜Rd[YejI)`)76 M@قk5Jf7Ğx(*¦6ߣ2٠Z#%Y8Y1:D[g씐[ %v-Ӏf푊:/1U3므9THQ#~ #m7v :8{ix!Z)ݕmY7)E?ш@muqrܭ5r;Y^UdYG#wN#}F=7^ qŖGD،ܒL{u~YHp4v{}ę9PHGP~#3]tYHbiQJrZd:m6=@<7GYÑϟFWK}kut8V |Z$;=#_D[^S JasjsF-C9[顙ιcdt]m.HCޜ?EuV;c,p;s;?}^L(|y@j`%Q('ǰisgcl{־6;ytݚ{rn2={Ǿ2s[ͳ#QBL?Ui Iݿln7FGPwx=};-# J1'Zl}+hh dn D^N k Q|:iq+Qp~l;-IFkc2@V Jvc !m/ FBC.:C~&tp)mO8#EC5 ~\3ANIqg~,hS!nѠcAj8 cxB/Qa~OV%s޵"܂;2y")ng'1ib⬗`r]5<(@.NkL/i-e!(3$"vtgwq[*(G7;g#payvp)7ufp9X$ \dΏy;JoNZ$Iψ9ٞBa[?Qa)-O:󛓆EY,AUV"׏ n;|c.1R?~q/G=!@of']k*[A<#QH"^_25tS|^;d ૖xy?p"_Te¼ij|6*אR #PU[ܿ\8e[E^RO, Nr^QgvVU߭8AQ.}z/T"̈́[q钁_w_;_?KnRJMwO*$ ty*<F$&rZTx;VuJu \7XNy}MfȄhQ4@5wG+YY0Dg\Ɣ hޕ÷=KuON*4IH.k{`ɅfIe]˴IbYs{9& P ŏ"wQ[KF,Au)uZa< :RU*lID|}kB6Fq,xpp /{Xvt7HεJ9hp0t#^;Kkn.B3Gp pͬuHyǂ n_?g+F:!b^!5[ۍ s,Կm9gmԟ<򘷅?; d"d',ӛލ2@xUrSnܜaҳݡ*&߹'XsEIQ(zטª|M*[DiRp>'Z²ZZZJxiQ@~g?3dLG%?"-Q .? a;$Kΰ"]w9XiNK4̷)m<#Te;tJ֋Kp z-3gA_C{1 ˪k0 ,#R^v'^ A z^~Bmo,j??i߷ON醼mgrӀHt:s颋ּQt-ߢ>x,sn"rw9^*6?wy y\$aa[( Vp9{4 sD#ݎ; {oSwڿ#‚}gd-e 5'lj=faScix|H,(`p3W꿍Z?q%[M}}qϣC^&-Sʱ˖x_*Z:yN[rжzӉf3e'(s-gs,cP0FGݐ_ἤMdBMI8'}m^?f^yS{S!"jNs?]󵓱q0!Le^l߯={@{HHyv[҃Z~N%A;=s$H@Ap(bЁ3gE DA҂#DPiZBsl1$zLWzwVU"q0wZ+C寵ź[ ZJ< >r߷ 1q;0u!k \Uy-{bQJu9@ڟp`5V5q(9{[v}. eTKI2I7~ YjfܿRZzzJWrt] ͘0 "1+S&BIS0pW|lR42r\v[0bɎ_6hWj$ų@9WE$R#Sje 451$^ zN p`yNZ:DK_ ^ֻiS!1@$`fz'G\@Ib_ν` MQO4]n@MpeĹp7dPf(8"ZS3z$hf|ߊsI~/YwSr;fɔ'VJ t~ i])ӝ® $hE`qoV - g) ^B>˺4DkSVG> U Y5?k9C MkX(E|)[V5Pā`/Yj#,ըkQG܆ގΔƵ<$-zs m5\^Z?:.,be>Xe5 ,6PZ(h;MZs)H=t n/~ɯKsh HԻ B݈6Pn}ioBm $b~-1±ȂN>PC 20 #] <Ӓb~ K|s'(%HP |3zvU:FS<و%2=nuLA@03[%ϳۤ>cD5ptN8(\ (;$s&HWjM{X(׃ʂ-w: ;FxM|\ IDATFgbu0sp~%FmnM0A|v.8N.zc]ո>10ӫ(4Vf&+2PNvY9ld_Ek7+jNao$ j-mx `k[Y  됨)al H1V"QfwE|n'T z;x6e!yJי!!{ :%?4=hkJ @]{;6+A'۵=0Ibf/Piv Ka6S Žr.$-jE +ztm sL,AE\Vp "G U*Shvh-C۞s4JZ~i `m7?=߽yOǂ 0v">(M BAp?%STWBh6Ņ@@#3xNT$ǹ"t)}Om3LD=DS5Sq%))S .hiC`4J_;*-diD yqضI֓L,)(} 5'02u3C{]WE )q^S xDڴiiGjv{r:. iJwsͩ[sb2clcz6 ߟ2t-J8/_d Vڴ YEWM iIkf@k1]tY0sevmg3Xn2him^]8(T_:=&Q;t׵s繦}ku1;dD_*- >>[o/|[S*nFkI0 ޜ ]^kLJL9]0cYy+un`M9Qel[t}V9]EHWD= W~&4ך ) N)j+BW/+Ak#+Z%GXX\|mP.?PpJdĥͅ,{E:$=2gpwZe~KuݯdI};=QODWxi)b|)pdDQ2rA6߅j΍٦fl dd6#Vts{T|'7yu0hT1}i9'tyye` Tp{6D܄dMUlٝp,=cVⶏ}S$+?E&GFB3{*y.w^@tؽ:M|%;7`Ϙ T&0hvnDK*XQ;. vՁŕN]vi[PyJ.ȷ%nT_-uwkiEsDf= a/\Naz+C`+mQ77W= U:nfg{۬w ,3^D^HidEղlbDR_$&!I(RHu vOD(,e@M>VBiAUؐ~RBn_+!~Z;x%KӘ,ZZH- CtFU" LAWBWdnU~Ϣ 3uhdb}O+F1"GM ]D Q j&eג63nn3qLW 3ژ ;̞vGÃIͿaΙң6o`:ZbV5ghu2*n 5Ǻ]L`%ΰp0f &x]fl{dUź-50"=Wg4o+xynH"#Mx!eV8(ª 1t$Ĭ-kiƉ*aSvJ1/-!4]Dv5a׍LՊJM( Sʐ!82n4 +ʮ`EG@7\9PӣkPLt7ҾeM2OƆ6jXo4&$yl ,!H3Έ-B 6c jtSagcx"<{Q2"O3o‴Q2u6fؑǁ O^ ͻ^RfYơ}Ag=㑖ckoE Wq5IZ1؀/”vo ܳ ~OPs?>pPHF7B0Sў1,A -m 룝Q @ 3q;x .d JYB1E7 06 Ɍ-b\i)*ƫu30ţJgsKl ΐ8Ka\qTs=gTYO~8 Hv^B}_68監Hh-͢[0PA|g8zu>hBZ-F- ,ZG-"b~ohs^l^K[,o2ϟ??W_b`6f5>)Y,HF[QVNt?2YEeX,HE+Hy=tM .!`^Zhi[M|fQ_6 f"8׺+SH}XnrZR `FmM `| )g <7x#v$5Cj]էMLׁ1L}+#I1YoOA/R$]důI;zphm]F'XD70Q 4k57Nb0mg`ʛ"H1@)iD}ekiUv <'10õ]Ȣqo)_sIEaڥd[Rj̄Q2;g T8rWAx͕YoE)q#)PV?$x/7+ ͋2C^Şq$T$#Fgdޕ\HnֽS r=h5zHҎ;א`Ot44ql5Z[3E"-՞g/>O2ݫ)ll̥/f6Y ~Ve.NOͲjvm&fTt=;r_l'P f)l ?i΀Є!p73]Rul{DS ` y1~e]ј@k-;7YV6fAȭ6m. X`̅O srAVtYdEQ=n`iP} 7$ރNs.}ߦ6+@ޏ\/y(Ѯgyg֨woKұ_=y{[^_RXz88=Eiiugvzqos/bӂ@6 1Tͣ 0RCڱ&R-gj*Q,$j'ʵnPle`:eٳJPjƻgsΔr_%|?8/ESo"\w=̌YjX :V[M~*@6m$tEUj\w33K~u apZ $;uiz;=%SUS\Q1Tl@2;٤Ϭ e*0,֖*쵪u]^:6SIzE̠)P [Ԣ轾z̨7!L_ SNҫjH葌@SI冫D~*vc3F20< T`^P-V;[O78RusQ} 9+6W. ` M>^J-*PHN0q8(i\$?9d0B4e qc t7F0aҧs i3a n&,j Ss!ڲ_ƨ8SlsG8Q3}½QmFu\WB}/A=} ԬQFZ7Rs:P;;g䮤̭||Yy`0}w4jb^3JP5m}Cp]$즗keF oΦ9.*"U_x9_7 EԳF8L6"cJTh+!IU)*tYcsgB9`5 D2To;*}Ή|4-pG2^@b뾣6.%^g@hA l[Է%]QΠ :   T *8f1لB|š1p=)nYEnʢ=ywwǜS}yHY4|b'S[Gy>j;=;1W)`V` Zy8 / OUQ9n)_$t(QI?nL>:K*kMW6=ӜM̈́ J=DJ?3!_20߬U(c `3=!3#rqFNf=ßLhxB\$!|m}2b6|\OHNtWpe-m5úNhU!C2w,M_ ׼]2Q&K g'*jm)dn^k%hCRe@ *€UwuwCjpa6}[ 'A"065ؖa^uQ`LU23s9x4*9ni˝kRMɲT<{׷ɺ3Hz (, /bॽ,=g41@%cG6ppLdʘ3x1k! θT갍1CH|>q?>/NfuxG %4 C𴋠tswE7qEN&/s"<ZB,X$,\œK@e,a@Yh8yrJW"6 s3weKӚR$8IXPka;tw /ϟ?}!XIh_3ͬ`FJV MA8*Sp[k Dghp]29{ %.|p/ "B.`zV斲.>3}o㙖 `ȶÅO+y9[2_48=Xz}Y(-͖k2Aw0O@n>wBoΏkBX1oLcW?2F0KyQ l_@[yš,Q&X*2,]c(" &/g &,aL<) HTyc^xzLM.%Hўyks:drl3]Z *̳3 l-5֘l,j˪>@Z i)A1$1ր + BY޹媷qW&3{`)H>o\W( E2.i3T IDATmY(䀁Vկ Ƨs 5[g Elp w-:~ &n"X}3蚭Og\qEPhfY[XkʕB<_/.NJϏ@{2_&Q>;#+U~-1ƿkM 4t~Yew/xyL2nBЪo}7/F%8Er~Ӻ$n5E .,5kUZ60 BT 4"-sy kDn.W|z<:̓Aeۻ9qX WYK ô`P%+XO*N7G2ȥ~pA8gWhٖԃR`|j0u`HWtZ0.g?0uUlyp=c%aQ9(|;Y/hfx>^eEs+j> #S >p.@>etc\+4bf"%cswTI n1̋[V}u2S#S?4<.\+pBWq}]Q p~zEۭ fuӎZ0<>=0D y@T߽k [.,5~?zIO#|7iI1} t-~7?Ǽ.<W>pmh a/__El^ò~X@v4las%WR߄rmӧNХ@r|D h;Pz_U/Կ7Iᷞ&aR\892Wd*ҏO&1Dh,103% w> M^ ($O ®ºp2dAW- B -mVq]Dxc:9Ȧ^VycM2„k? t7K6diAkfmg .|ogPvnF2z24 [sfmfGpaY4* 9[Isbf^Y )@3sXăo'cҫ[ u{Ÿt-k,xLA+xaY,In@҈_ȸ*3  }!1Ьjeli]f@dv+*XD+":6ٖWMwh9PP(R>U 3b2B@8,K 2?lgp&E&PńGg/)&>U*80f #<%|<Ǜ֕HU3T Ǡ*3 Zb s '7K 6$HKs[ RS̜?AP+:' , Z 4np*5qSgվڍ zKVJ+aq p0WDg?e L19a v+t\ᄍ;nV- d%=;vCLd}e# &am@Pn2gc=P *`oA0;C^8ASѴF>u~kwa3 ܔ,`FLScx@h)9f1!m3SІ \Ù^˘;e^QA~W&-¹U8dY3A#7tA;6 }fv]RdƎA?N ,0~ַ`jnrу6càv&)O"|+?y[OgٖJ-ĵhg~'oWkA*-< . eHƗ 허 `\c~8L%j:ST s02<4~kg!8!UE3HlrEh>攱7%iĵGnR?cF34h eBN,FL@QUY]P:Z1XPg87V˧}qKg3@}::e$ɪRgFBAK*5R0V) GX -WR.z0<׭x}qgŘwaH("Y:Rgcl",v.tcZM{*E޹>@ا}Y >>B׷z9JyS,аBV"g`QaȐd<7 T!څ3_0H=; HlHx,k3Yb*1g2_x W9l~XbjuI,*D@{=9}_/CxtnB:Ñ<;r)xZ ,5KKtU)q(;O3_wgt!S\B!,\>yIU4%*R ˊ,stKTۘru~ۃgf $2Fڐ-g3-@;`Te/|1Rc X\G5Bhj3>ŸXڭI١6_ nxGs@sГսG ,]@饑!{j M2#-=HS{ \l%jkYO7#-]aM@QgJdk9e.";)=cG4A{}ys^۾I33ً5JNIY{XɪOh%g林w|^'ߎ^|g&*Ѯ19ew+zmAR3KWs!y1lsH U5sg0 %4c\t zYvhYustoCě֗܏GF[fg=1 鱃2Ґg Hɹϻ{0mc:5J.rW#} gptX0ޤ6s.-}Z]"W8qsNhB@^ؿ(8݄}㶍j9SD{=9{Pj^ݜH5PB8 5H6:s/̕mzB~7Wvػ5Zk֪(ꖲ'n_VsZ~ƗwŹ_۞tZifO?/G_"Td!B9;fZdSrE nEtQ30@(R(V >AMf8p- @KWZa'w³;!lF}."Uz؟r  W%sJ׃( N+%!)A\v{01p.ƀkv\k.RB*?dϧObyq=}\uu]ߝshq,,rK 0Cy\~mќ$t]" >w{1V/WPY;W P{ - ߳рwFe0HpkZ9Tζ&!*ԙĵsP̹ yK*/ 5;q_rA˜w;Pٟc|օv~ցpf`GC2ìo%?uR6PU(xT/ɴɻkR@Gf/#sƬYB!z!xNڸy.)I ;9=byDsD~H!<[ T~A^DUܛ{YYIZ:[' he鰢L#=AYyB ! ک"Xz~~ |Oo~#W7͐2{4`,u]En{ *htZסUš@AQwi0 Ϩ-ޟgGDϊH6 Ί{mƬݦY0x1|e΂V0\FFn<W2AU~d:iRAQk/+[PhT/TZ3x4FL'e>DtWI`$p5(s81`[Mޙ[NVbrAsY^Yp#wbYxM:aGv GwFݤ{lx̝sfo oH{3=`eCdi`V']O?`]}~#woS=nEX>:~5;&m.ZVݣfMډdD yicXm|f2 ;2s2*e(w NjG' Ƒ> IDATnCc)g|omao8**B^V5B kP(+tRZ7`dxLVh%497ҍnXSg )`9zoëEgvkedX2ye)ݾQ4ʥtmr^IjX1GO]e+ N;bDRڭN :?-k~?_ ?'U@cT]h0iRy BPglC}?Ǵ\vlg\bd`v>Ӊd]0̻#Ob *&Jbu!(sY;b0鵄 Ȩjd3$ǽ;!- 6bcEvAC >hǽu!?{IrbȻt*ʾ&3*0UjF5=D(@λFԔej4ˆ~vr:Ji;]QrZO oȴZqgs=&(71^_g27].^)̃H$QI/7uC}rԿxZ;m B"cNLD{n- p}AxFύř7 dXi<$n$q(l 1`Ҵk>ZS!$:w I8z:uIXj\TfhJpy_~Bwu]^!O?ۚvW*%bPB-c3r %_Wpӥx d L5u_rMЭٶ~f_|ʲ@JBNWW㥧[в?^567;sѿ/s`s1}tO2$+r}K#}im6̨2f6ڠמwq68 Q5F^ jc]%7^%}wN(}[tQ$BӢA?;Rc="ؖWB}E4qǃ M0ycfn)i 7ogϲQJ@K:*aL孶ZGfl^:r "8^'S;ӽI!Xߋy յOE3*bZ/i@! בfLM|IjE֐Ic18 L( >x3v'?xXVlnfNUJ[PxZW:ϹG[eULj{A0K!Z-5|4Jr;1H()&ǡr}Yz[iSf8-7\!gƯqL ޷Ǧ*|wl+@$?oZ>{v C!c HG5jى-ZEk1a,J"\`g+M+ Z=\K4t.s=N 9?pѤw0($XČ"$0Xy#,RϥIuGgȽS@?u]-,([%Eqs ɻ%ssʚQAMѻ1ykibxȏhޅw<**' CqV%`HhbL缸^jn&2XFW;n߇3kN-ŵ4n{1F}s'}w ϱtKu(BdeuZuk\к7*i`[b} ,ϔPX D؎uTC+`-dV +~ڋ=葕 2xw~~m{\=HKl|_ۺlq|gr_># @?_fN(fc"%bUO$̄ݹ8N`-7Y|yMHGQ 4Y ~y.ԻfL 1@b1^1ϱYϨJ =Ṉ6 习uYE{i@A+ R{F\לٿ&-56Va⥷ہ[zig8f݃9'&@7\xWGXuTՋ|N/ Kf10&nvi,[5@Wus~wt霞?nU!8e 0%Rǧű6sUp]RNϬM ֐:| w=kXhOx^b8j ʘ|dwMËi.;q9RT3zHʞjP]KП4}X9YһZM7,m}ؿƋbAtjo6ZvbPkҾiWnx$x vN4>e&;bG.ZN&zmh~v 6؈`20̇:?$>5@<&1٠n.SBJRAI5گT#>:@ -WB61uoo9Ϲ=t9 Q3|zsg[sr*xjg?=Ci0|f2MX՘?xs L \[A.K >a?Us/̹3zlw->V]3{Jh-Mә20h,nqP~Uk.=#f)ccx q D?}ށ|֌nKWSЪRF+zg] 0a1<7zHum5n : uIlj@(1uցK"n;:c0oB4vTm5ÕiVۓ^Հב\@a<.y*h{1^chV yrX#G$4uJkCptgM?4?ě-@wL?nS`@n{7Um0܊}!h(k[xX2--C?yw“Ws"38 <߉& -n7œ֎A= Su2R(@35*AuN[3AiqX_0u,;! aN.fQ,ꫜܝѪ3f|&He} 5(C1R@xt3ϡ.\7k{yudspԃ8M wzIKb?e+:d\NaQT5}g^~Fa{$ qpM0α"]T~9.h3YH+Wu |f;T0MF|htPxI}`DΟs'!na\:cDE {qAgnevKk=Fh `G=;:pcNs|KW"&VdV./5vw-@_Wo8v2j1&76Չsזv{k(Sd?\3&ݾp Cgyn Z.w4hg/ ZC14c1[ӼNTuc_s5zX(zkmJͦPD|npixe) ™n <4R _"VHz{<Uk% +uEhRAApMȌgEc@ZCmj4q4N(yVf:ݻE o>]qv5q]wYRݺ uz@jL+2,:[& pޕr_-{2 \rNE;A}bb؞=,3\Q~L@| #%nIS44ܞټk_|tzk# 3u@ϔ*eW!2tmL%GWnX'OArx'׿7ܷL S۸g] RIn`!/6# +[D(iZ,M[P],R&G%\eD#)!'?]ZM}CN[~wG~;Lţv%U'B(OhՙY?Ldtզ4j;Q P=ˢ򀒆ZZ*b'.ճ;+;0XEQj]951"Jݭu qa9GV)nm3[t朘LX1."U%ESO S#5%AO$^"9gUT5][BHyZ:zhUo3LWMr7>Tt/A+nU|>dXii AHXRaΣ\h.E뉧h)$PrX1']3R>FF AѲ[5kY}Y1WGO!-;Ϋ51H ~J[@sh3KgaѽnJF(KU :glJESʿϿ5}ۅܶmA1\{VU@*!($Ġ/>c4H@JBIC.bїs*{9[s1>ι?.Ծ{[k1?7<~W&e`?؈z] o5>8V"2ִQDž%a:TVXp0mEd38"j%H3JaDGU/`6 |PY _ߑ2ߞon=P{gg>J ܺB#xz1!"?g\1a+>#"]u_yB/}Vdzu]e31JeH4 +[=oɂ "D!$"@n/} #(Q=Pϲd=Zڐţ[8;fLI>`5Q1v- ~'H߯k_hv &'\rvCPbK'60_['[w^k|Gߗ>g!qfPp_;!}߿—_|>#wO^twٶh] s)Fm 5 4UjQ-)x2Ñ'Vs| Fj Z|9{!h6LFAڿN523g]W4tcp戶GZ}cd+ϛz`+ V Y,xΉ_ Ğ-?3fL!a(`keO9T4c3ž}ˉPy>>,U_`*s/?xn .Z},nX+PtvXZ<2ٵ(D" ~?*}<ӕ)Pv*`:PM\HUٳA~><>rCآ!U-IX ܳSd}u-W+P9~{ N:2ؑZ pN9V;r\(KxeǕY1ofph0~}#>֙F)5X)/F"5qjk/okgI+nu^+iGޓ+ޣ A\PlͲk0`[tI4 IDATe:9MntSs|$h 1A@LxrM#{|VQK(MD< ;'ZxF} S׾$kZy P* m ExyL_ceD,a"TKf XVC}#K nsoֵXY|F_QA"iW@Vq^~8):g:D2- ()m밣sZ|Y:U.;k5πrspcU> "9ڝ O A} d @,bvw ň Ws8*#)|]bڄv4NVz]9}`_3,g:D8c}g[uhfwTJns*^DO˾kvEek,F®EZl.UG *5쵆cعHA&P6 ^h>W.^LMKfynYFsj#0o#,b$GmڪO.2wkNG2707֚IAQ}t&(8z{Ke s 9'uMu`nʬf 7b xW`59i!Eyn _KR9Ze_k0T,ĵ0AUR3y_Oץr@n"!mQ3'JTBPk Sb}{4}l#O9cFs3h|=@'`l^m<]+E*[+b}݈o1\leN :b u_Jl}EVƽG@{74f=3/Hx ,Q6j蝶^`mkȅsۜ=I8ܗg+}G)x@V_%ۅo%RD뒂vZ*)SDXZLpM]L1P4^25l;d]njWو69 /KNl}_Xg%j@_rE`s ~bfKVS'-ހ_#߳ИGPVt*'YǖE6՗^Fu;?S84xqQ8oa,y9&۝/=X6#ogSGe:Wt ^w? y*oYȈ5yk X+}F5\YQ@"u3}A]"*Lu{'%W_?F SPKH| JwuklJ1\0|ޞxy"d% F=aII3Sk}i'h=_|ڷ~U Ͻ=*ZBxs wgx1^;0)jRz wU2HT,͙Mau+Dԗs6w ;RO\*4y{LUa),V6qš"\u0wx|.h9=$l%Kv8J:w*EM8Ww>[/. G+0izyb[AA97sws;DG0U/Z=V ah.XߍbNdι:VNg5]_$#@z 3[k\1WX#Y4c tܣD7EsEϠM)rvQ/N}>'3X'ZĜ6C@#w@,-T5V Q]}텰='*aiVʶ{YEn-*6`tX9'>)!'='Bi5Io<Zm >QPLpv[ZdZwZ;>8kK+ڦ"XQVT֍d( byRrk2|̔ڎ7MI~B;KT_B=w_3S9pT/l(!L;svP#Cr\kNXrco|TiPlGyκ7¼E{A``J5 罷Y`wהF0:+?wUX+'ྟ/Am9QnW h wRpc w "E@0L= 9 sЧ@\P7Pff7_hEύWAΉL@#&5"֪@'3-pl_- = qҙh,g3Kv9Cj#0Fȸ{GVۃ=|Ո0{cyHVjHaF)+JpX;MkY) { >| "(l. ˹t Y&jr,0c=7#ŨK} -a>A;QI$y# M &~FpPkU #H'LH"EL0 wijMs 7 %MLcȖ&ri8-r&6.>20,fsU =[[ TBiF0;e^' +G uz_QPδX ^[׀ ?E5"9^=nl}KrG:xHf{HJZ 0N ަvmFᗢDTZmxEd۲3Us"3ʆX~>ч~;jo0_3?|8f4ȉ}A40 Z4i:+Ih)EY`yRL!Ҫ#J D.uG6jT $?`.\7z6SvVG{߹i8- g-B=o݋]'}K*i&o%mZV 匍 1Sdˑ|4] ̎Q_|_xW~#! p;=2m8[= Z] ?6OhX¨V=F^"h76Rd#Irk{#I~}xRʜh=A.D8k) c].]n6:WWPyծ9ljs-:><'eMfpoxp!Jjd2yhL9тa{C>zRӷQ6(6dMAݵHRu  BBh1Xjkx)U"ۂ幧;Z]a~~zGbOg| WYZP8]jqR!sLgK]9L}gAx%&wB͇VZ#vmQy1¾sO XOL',LAv2q4z ҤFOںZk4!IT+ {XuZ&Ʌ.Uj_ݍx-[Y11VDP&Me@ǎ~ѡ[ۑW$Jx:"?e? rK8%ٸ$y +9j- T0Ic`;` '({,ZeP̯n<2xp,hք5+Br>jn҈w,D8 m"5.JDWWl5#49;K$BBuَ="2sd(|/>f޹ᵹCN BL5ľ壸 P{9C8Tt|#!P0|`L9:zg1$5a( hq3:0qm䇌Ζ*j=M(p^y*KZ鞚2#e `upk;(=RFe帱C3Y{ǥId)8Mιe\LT pJôIP{XBE;Z8GkC/+P"&RRmB[ebs(KfqϞ76G{ߒ"1 >ځQ4[]C}ω~wSGtouA,YE]s5+e)+ %_j{aC:[16g&@;{^>;DnD`sx 7ohkjGd!xYQ91J.!:q)' Ur,zZ=dky53ΰo}#kd'jo3|!mG\c@#H7|l真{LmL@^ w{=`xU9^?R?T/dܻ 8xO]1jLົШ+|ŗ8Ssa-Ֆ ZQ[ypaxS|*v0P[Aܴ HJ;)2ݠR4 Y\ &IdCfD)a/)t͐@v\+!mu~U ֵZP7%=51 5NxN|SJC)8|^7q6pnB4γщ>XZa`IO{۸aV?CGDSa,&Hi,r젘ي&7>1z=PɽS<մ BhrѪ;!l#]r쌋=H'sߌ'}p4Cڮxh9Ás:j IhCoN-O{1ZBȇ&WAz[<mϔƉ>?ǯY Rs&zp΁V>cѤl;yDTWIAi'>̨4ې%$V^ǬUlJWg5 h.*C: .'%򴇘n=ӳ`Bc x9Sx{ l{~!vǾV{a$= u]#ķno}-_|ŏJ ?opgEraB#gֳk(4LYsh h*T񪭡sюE640lGg9P0_RMnĥRVƱE;<d'kx+3{`npiq9AO}HjHߪ_ьsN`=+T '2*ѐKg+8 \x<ΔS *a3ww 9&xuyw=#h)T&ã<<ȕ4!\D3%f3r 6z{Qݞ FNq6dj+ 6[î̮k fd'PiOe'5:?ȳJϋ"*Z&,C"BFub $ @ZX2SW80ߟx>ܙ{,ΦG`cUN-*"o[H@f">ZK,Le6Ak$v>qruD?ujч(99 ȐoOl}͚By y(?H  :gyK=$E9KݸV _׿_^?R_~0lkrhUA2,\6|ݔ,V IDAT0H|PF Kh@Y_Ȱg/,cmݠOƻpx@eտ?+s\vˀo\fTv<+Ȉ)2YI>bðZ-gE%DmA g>>xEgKҳUao^9Z[e[k]w}0)6Us-tC5yZՑ,fzd; i~$j$*7s.=;+|RQ c$4]=NI1 9ZfB!e[X9b{b"}h 9*i%Ja Ľߩ2U6Fs+ݚ;KnlۭePɘgHCC|+w {3KB>pǥN`mA\g3{(-T e6P(E.@*n,Ži߸U_>- <@$g5ыKz9y&ܱP=ŎXOvToΉOkg^?Ry!_EX|aB#:7  D7硫ra|5}JB#_P *5̀!bPD{T7Ɗz`֘q%t%V:Ō٪/!"Y{AƦ0-ʂ BlkY-K#}_©0!-%6@l$. :JZ$d%|8  BgƚZ` ]ҩH14@?UJur@"a+gnpC_]c]By))У4|e1BMYq>nilq྇jˀ1QV}2B$|IiB5^a5 :X0$R"*VLBsNekkn" @0@hu0N(B#Hz!>x/$yf/> z[Ykl hW=22Mݤ ("7P w} :[u8ZA;HA.941V[k1'\FTeh;з{t rBḁ0R\w): B1&}1jcRLJ FdYEQY+Danm1!6ڜLBXT3DG@|;\喡1,l)DHw^s*X!XB'.cʾSd$nĜ1`X+OYU?6@6x-|8xñ5 3`&62~!0QB_fXdo_`ym8S)/O߱-(,ow~׏\bm̚Ig.4(TUkL1'u]ǏjL[MaK! x9Q82 \bxkWt]kujgpf):,UrR"&QZx2c#m! 0"ձd@  wrE->kQchlK^0~Du?sjHש (t2r`VS 0fP"ކpP| & ZAG(^krg Q;PS>%8ժM- z_73lTn|X{Y1M,ilTw?zqhUkV#X a\ z]s":q㺮rJtH(Z a [JZ^y>Aޮgk)Ds݁3_+R׿#TLhu9nJZjkpc%:+1$PKu?SƳ%Vm~*Cg9Ԉ$8Z&z_݌-(^lq/e=ǝg<:朒O'Ղhoo׏F>@q91hh炨@=Ǝ[it\w/Pf=wTeRu[:zY0CPQL DPd]+E&,3 \gJȜke $% ƃl5:U Y53ѳaRJhhJL((58(+z&Z_M S`pUOcQ}\\g[*ۡ0NȫC-yb׏ywz ŐHE !}XE4  {zhV}-.f_ P4 08$^çz'F*t;>-":SblDZSZRP'0U>ji2rXq > I|J-Eh< ]84J!tLtw+SoL;_ G3zlNe3A@CB% ºJ{˲Mks.Azhr/VPN&*N4j|_FFWz3r%k5}hjO2UÒ x&"0уviwֺε}P.^hզ~kcϤB5{x "~[ACLNp <[k{$ riQ:wgbf J-lB^m,g)#X<_:{3i >p/C@+k댦aD %;b.`S\P˙ll28P K%VTMHp(T k*U6k!xZ)`HUGՒL0 CJv &>EtϔJ_Ȑqk,qA,fpk*sj gq94GO>'9-2@d FwG _*jsL!9VS4- >04I /Yj7ˡ5ND!9EVEf'#"1~$Jz~DM  8`y(KQ*Яl4Ô5Ki%5[3i!Dܥ1AXyeaQHڑMS U2 x!D^I{ ¹?9j3l:Rd|!:PKrd? oڧ"Ag<ugQ  J '=Șt = B<Ɔzz.,(K;Aeߜ[໚nrG:kJH}(Lkg ѷ]arGLGˌcAfqKgJ6׮%2})f] pCfEEgdteã$5BPl r:EԦ 'Ñ'>w|;\:[n5?ї +" qNxX>(G)#>Hu!w) k牳3ɸr瞶G=߫F(cWoL -Cj ӀK{ϸ qλ ?7uIB>\e=:F &8>l FUO( . x$KAA[CC6Og=U_1?OD  Tg \z:d5ϹgI8E喝ZHl}Y~&<K%TAPF]8$zRҬ`zϲy, :ИYrK ; g3VήK_sGJ5gJB{!pGŒ-)hs LDLB*;!_//^_(@?~ z5R393).:nJ;}MX^7W0>]ws)nXF 6AK(#6QWj_s`'YkT.+h&R6IMKA "=mRȒmC%%J"q_NHýdRn8mbE G?h ه$# BV TfrT^: ɩ8 !iڶ\b2tHi^BBZ(cOsfJCG\kc\"]WzO>C(y'A`cB V&Wo9xXU|6m;> <_lSϹ?D!3gEr, 甾ôLPNJ =>P_nb>Cc6Uxp,xd201FtΙŸcVCʠ"(Zw%D_+СŌEsxRy&>X[jQBB"j(3bK{ RSWq/"R&`v뚐}e9 \8'f Vy Ƭk7\v݁yu߀sl}^Fw/ A"jr- FT'7'hZG 0,F{W7֪2Pk_?J((2ٙ(Lh/qaf'99"ӎ=,9/J=Xz3P y-!Wɺ|W<l9peF 8V0Q8m%z,-ܣ5I+P sUMi'`Rf}(Hw< t2aJ6ZȤK ט,L:Dc8T+gey7 ? IC!DXc"rԸ?LX7$)|S Z9Qu.f=i rOdW_%x1GJ,1߄<] rÊ>7x}闾8~bPƘd:=D2\bAEX?ΏZS<vSם0z`jk/rgmig:5<f3V[РWk\ƨx A^{{0lIfvBJ+C 6|%vh) r߷PKh˵q0oWfo$}qeóْ><;4! ѯ_t؞BHstl`jJ(qZ#Hrୃ0H[_5keI q%@pT@j<I}f JzutvITF0 qGeVctGk9"ç7\u_Bb*_ٓk388NM:-mO]];zrg/ʡdLit=; 8N|{^, @)^q[kһnu*CnMbдL& /S*nXc@LM;j%TlWx4*LU:N\GYUw/n|7.1WO Չv6;EQZlNU00Q!k5BX pfKeJ\ dBs]\T)d1_O̎@, P*.AH#f/dG V:}Bl̬ɤ@ y'$r_! 46Kl%87ӹUMZQFQkO˺锱I'"H>π񚀹n<0!!.ʇ;+T9Ȉ\WǬ!4q Jy;C:뎣rG LXc2P&O%`Т[`:V^5ޓI{+JJA+v DvBT߽ߘM#|i|<6l7U|x>o<~ynr1j;Ohfm  U2h-%By<`}Q`:PZFR+J%:Y3B^hLZ[DH;ro'OOt")(i|_ S걈̿j"IEtc5{{{Ck 0)/$l(=;\%ت&@1T]ю&".NnF9HqJP\ uBa@;Vj=Nr|Ee詽,yܽsr-ip|}=K+{*;6Z-=@0J#T}sm$Py9>~pf,o*B^nc t>O.$Nq9"`16HP̭8<6ZtF zZ :&V8 *o9|v|2॰t3Bl\7A.׵?!e!A؀u(oLHLt.@)$ˉOQW(=Ȭ}?R۟}+GEnvH 2L]6^](| F&K؁9'>˟_oT^Gx_8jQ74>=̉P#LЫZ^[zw#{E^b o"=Q e[!kވ> $_-a߀c>_J@f,scN&GqMՔuAudek9;ݷ{AV:YQ"B>20bA53eZLpw|&,l N.:5&]ͲF*9ȇ!smMD+([%G#䀌~vOa![ֆs )a!4N pVԞ8 a'*M"FlJ?Br,qRy$I2( wǨD](y@ہ&F[eOgsRc8lJsykM]>|MϚ#== Hk/G3b%ωvjK,[_[}ŵl<% ]u.]BދWW~~׏Ubί;?F Gd]pÇz8E~Pt'B׃cښ־TZ55r|>^@H#>_C̀qw<'p(A* b!49(N 87S]:CڭN74Q,ɀ[K"0yx?7^{o#kkQHh3eAk<6[k8ZEkϣa(33v`jCS/ { Vbԅcۘ#\ Z.М\JBdj̀-2@ 6"lܩ Iȧ:VZH sؑ6\SHEրTf$q-jd#6IR]e;GJn]8lR3j8w8 sI$lJ۹a=yf"G䤱~W&~׏ ?߿@kv]b! uELx=Ѕ"~e`QYɂbC?ƿE gos+r2;Y2L)Ɠ$b e g!3L;ȨI6TL_ѹO |9`fuڮ3-ȗѫeYQqp>0rZ&^Qdi.Z^&&d\ $fo* <X=' a}}-@v_2Z\BW"1Χm.#PHd\sB`I6(΃6=&ITRY"*!xp9o&_tx>Q яyq9q]Odk3e=vDTLH w<{-d7YV;Vdw9~] hRƁ sA 8ڑOD! yJaY-ui>(K72UUgOb,MD/*ZQ҆ Z}<k,# ƅ~!2SktU0OJ;zz*v5e2ZZ4s'v@Lm HEʜli z೏`AE<Ssy] z4ƗoO;;5( rʋzΨ4')8'uqO&q4=䬶dz%806D+?<3C g-J姷`> & N[>ҀC-GNB)Ev42+lM%R+S 0Q7W4`kWT*5x mT` NA}+K6uGXrfp:R*JILcΉ~5a:Nc޳JTTZrQXz-!H< Qp }*'Ǐ`pRj;ͭsaC@DHYaa25 R+ nM"E ^^6f}% [D>}Plڳq&j@;{VxO4S@ʠ +hΏK!qc3#x {ω9_og_0ՊBj4!6kjZ O-3Uŭo}!HvMH’XC;J)6]ϣEsAoNZ#ܒh8lJY \K@%v/~^1g /!̆J @rLrܾEk䳧K\.dPb&q@:}Z)h͢eP]=,T"*и~GC>1?ooۺ]3\ݻ$1RB9PSQ$h6ĤSDjh'Bj8PҖR{wg@1k7]Ys{w<}_ղT>9Oۆ-piudH[%2Rxc/X:HmO?mv>%g>/fѐJE,q .93G3Y[# 7vmLQ3$s2)묺[=MۍEV* -t663m:CREA&2'5J6Ԓ5L8/.c}Jyw)hQЌ,GjhM']9W": b WJ} U֩^yrLL}g\Ey$R͉3qAyx,D=gM|hZiğPMX?kP)e733Mh 8Q_x}CПK>7(!6bbO.6}cѪs9A l}JAT w  e\Vgu 9|Ȕ>@UR (]ܫB`mX $TXX۞z_xh9T8$P0Thק:4bZ|xT9N+X ͬ>&3YΑ8$]p*f \lI*\8콭4%tpslZɐ[3ecPtɾS*,\G$t3d V6#6'kX9Lc@sBMYP@Iݷ ӯ gܷ,h-NC(vL}]GHp蝶OK : =k, MnS1@DNV\`Rv8*4Dk\ptiB,(vͺ5F4;0!d |` opIœF5V*l%}WꩭQfޒBݢ7!LJ*$,ā@'" CC?{C[Ժ,%qfXipDWs1r!Jـsd9TAR&`Qgj:̎a )Ido ]䘺}-AnF*8@]N`4 sYDw[Zszg?b^l#M)IMm[؊$2bWk[J97 !^>/7_̂t錸EJdgI'tb**:,`Xe|Sa- >ܼV7@5jbX'3;Pl؂lpU1nsl}CYe<|c> m 8 pHml43H\+oaf -2fkGȥ:z\ש*>-_$n^8[Ys1Ud}XP3cmsNtDW/+…ƛ=A)!G;=}?HSrDİ傴%Z`vٲ lw.7${"Sls}pF>.G9\s-ƴ{Dftsq9夶~sPpaHskw8@)f2WxInVf I~/x 3F'" u& tye48@xj$>Dw=5*jZ!Xw05[:Cթw$2>:-]igdw=D5~[ D;1qaAo},+P,#8$Nض= x.~ 53d1-Q][#dKJFrzp&0Kɳc)!,a˛ })1Xga*!Q>G}׈NdDYO> _p?'~+#-i& C`nh2~1:SEs>mr`f3QC} G.i%VY$ijHp5LkkTG<6s=utGgFiZ)6uuS4t&mMMu)fܐv"qJ$b37-b谫di(Gg!G_Ri}~ȴ gKH\&C#̝osoc =GH|iLH2(%k^ljMG:\vNASAs(nScy8:TQ kr*-'<*ym]I|,9b> i,k. w&x*'Ipd2~`1mzgz' @Z4C#P.rƘ)3A8FQJSɸiVٰ')F‰rMg,F$/W 5_ F_?:9͹EpyqR7zkX;|*,Bn9`R6kBalIj̀"1vج].^0:5wQK҆cuVKohT|DmU L|)'p2s, 6J3ҡe<1/\:7&5B^(tq C u8qŠ:g[L{ʽm5oxt ⏎d'`9JɁCO;CELg 0ڹַ,TLAp:x RN.]H率a c\7̔q 6 ȜPK/xe]]]ώ~4;ށ[Xkl8cOdG\c3C2&c vee&WD2ԏ\G{0-aoNJ19' s O}^:l4s 3*+lp#~ߕP$Y) m\`d25!^G8 3#a'fػhvHji-;w,X}>qtk֏UB{e`P?yM 'XcxW$7wcp<'P7PcK``'KԷU'>Ђ@b#)a5B:鸶C!^C)g[]c@4$Jd3ºrrJ 9#[<᳗:6ә A/? M\Wyt?_:q%X"=:Ɓ&@A5M~@TDQ򂩪M'1jt*Rb^RN$zᒹɗJ2,t}2 f+teN\o##I>xa1i:z)Lz| sףO w@TK,EPh|k^e3G6j|$yBf9q쌩ݞs( ϕwo\l!n&1+o IDAT& S@£MM&oѧ+!p`YN'G-i'˜+25~3HJ,f/r]9_:a~xp]'jD BR2>)c(L(p/W.#(b ޽V(NJTX*ȹj@N O%(d0߃7Ap2,{o烝Yxdi|~?W| #?>4gGUԒB6aKM4~#|$c)'W7Ę7ewBPEX24/ P&ȥpXF1rS)x~~^YR߹fKC>wk2"%dP,$:kHY<`Y 3DqQa+I"RpZ1!.'uӎAɣ#!q2WY~p Ԭݎ5 ".cv@d/04`.E"SYOSAUWSk]ݞ(d4cN0exotϬ}&V)&fz( ,Ba*;^E7Rz3(XJxm *"6Ʋ푀0' u #h Q,:VrIyMZɭ$y 2#{tlG[c1hlYA6#gO~'.!"ϐ/O1<}Ƀ@r 4zXΥHXWF @v*FqT/:9'^}G#rLC1tLc'96#eR2D|<9345q'*`pOHm7F?JڄMZCw! \z6&jJٰ͘m54-l;0&nnF@8vcD\?EȦ Ҝ8ھ,=-8{4D>/Qeý&|'cuO#+]R8T\U`ct<]^]!,CP*0[+c^XkDTqD|-E-|v8?B\G`9s%!:8: e?Fl>mp}'G̽nus,0"@ 8ՆcGɗ9$ZU,gtJ@wzLk_ۅ9cP78Q[?eL%]9bވ[LΠ<_#gc*̤r^jt*dHun$@ Q$iym䫿+f>WkcSaWD.3R^Lѫ8hB$;f *{<8<]|Z=yGJ.8 ˔ _,/YU2lu;%6~ ŏ ~BVĢ+XKl2 rj+b]F"~Xʧ^ض~RJ(\ -HIi(IO[I7 ߆OBU#ʬT*=ɤAlx})>y}Hz37x+|Bm>\I*) [T禖ΙnEcJs4w/:8Hv#e.ڼ6]ks;1 v$"f!~?!* -grC`lӧ"2 l !E j(9~[aiUQ m@@Hu Ƙx=vq$Nͦ79Jg{"Iyh]!m/ >dcLD5#H,OU0K0X)PW\LFN UY$SLP)R1b&y >IV&Si{kBJ2œRvuc0ȑ5"Y84cggf) O$ȳEi[פw;-%탲}߁(yvY.3Dc3Cq 5l#-$0'@9Vɿg ïAݧ?' y.p?AI4v] /\12\D=`!ZG)R*J-%#SWOʲ,?s\aD}!r(^$ IPwlmcPR qU5'2r:ZtU<wAf59hd^Џ1jlT,ls̉Q}̈ ?"9 q48 15?Rd<;i$d xMF4fkNjf9 3k|XhMUp>'ҼN.*I+mViw\<:I%^l a9l!O8ގU87s6o1t괏X?om_1\ik9)ZOJ53'd 1sk!+L;| 4 ,I `FFnhj9󓉏DB|UA83T8 ^__VGPJQ"&KH5/cnVN5w<AsA)f4kD`p$thK)(h$\ņ IA'oB1w X?ԉEFyucM-!!l}w[Q Q0!-Yw**;F&Gtd;Ǹlb@9jG v9[IHZ쀋q6O$,q6*kRFM5aFHݣٱ$(i3rV>فޟSA4SfA(MFv@D Ǖ`ڍ:R4~=ݱ-XBKxh̗[ kc LL؃s > 8?3ÅT߈ięP0f3YxW()A (wYڏs%/bB|9???=?c?3Ƿ/n#,C+b],iaT͜FRY:qZL&'RaatO BsjH% [7=8?wIcXr2A[vF"~+1i#鋠GU\ኆ A`T*(W?6)BcDѺE,^t-B`'$ڼU̴Τs3OITi0Kh+VR./Z[d [?Doۆ,TuO x}!?/H u$53vC)s L7nv?U_tY`taz%/S}7n.b6E|OI$N!&g܌V7qf8@͙>t!!|!_F2WI_AUnJ!rxg4uHjAA[Ø]-%KnI|m@0zxX8Y$%C9T'vM#{>_B~dXbhPї) !Y!+[-u%M9-_lޤ9NӉfp5c/ ֏.jj 9xF>Iyic2 Q 9Cz% СDG9u3wX\ʕ|HgO; Vj)Hg Q0,DGC;Zi$2$gjВ D(&SpI[gv&L'.b9@nJh;q%R{<'2&܁0%[gbjӾ$ 6 1LpZ8\#5Jgѷ:@Y9e'^{mbZAnhzN0/ >&^Z4q&#ÜdkF &3ptA厄xp䴜ga,OANbORGB lEN*ER.s5)-5K #D0灚4?m̓cЎ"[,5JIȤђsF2K)hawui8v<DU)oQg$Y)U}eq|S Cg :NFlﮙB4&QQBZ@_í0UjqNL,^ 4v} Zb Y4{Z3ۆoo]LywK|2TJ:B A;;Ϫ=grRm#*ޗ919 XR[fCS? vܶmYӣ"P2yMSnP((AZ'n[-\+Jhu{H"3Z!~z h@}0w G i(pyO^__N`̜@NN2!%vsh6OzO$4mo^?Kk >NWZkD:*Sb6\ (:4BI9 86I'0'jRD$coj1 GkNjfR賴QH(NQԪ!uE%@>dn$~4ItV[{T<]h4s?> wCeL&;^SR% ΃ )毨VǚӗɂdIi)TX$ \A`%I:hr-U3F[En@cWЏ/gw 90a?Y\'=71H"p#P.DBlDfoც[{$88}. 7X2g3OeN,ɸ4/a OzHɢO 9D؋7:@uIެ5$l&yGҠbS|1|* '}r9.)PQtXDqh;@'Yku;,%qp6tX҈<1@({Zfe@_˜y>Y(+s _yz K5^x*dΔܴdܫt BDEDhD0/&OvBc}PK\%v<ߞ^?s|ߏ/}U/~}$j}]hnۂnIJq㐄;RaAX.`FG9Mz8 卹dx Rϫ ab}鶠vhCv.ITy36cltvcZZB0NLª?\c>>f05X~#m$wpmٖ\-is}N؜]PlGJ}m':Gp$a9 .Vgsy׽!mZRXxsSWLxcI+3tNq\9zeUcVЮ@)H- q #"iV΂~b8diT6(JeHYmY^i!0ATDh;4E=cߝ?jW,-eYy8gMcRtsbn^cXB|IHVtt5ҭVP{Trl1Od̹ 2?R)Wm. MnԓՉ;r)+yzBȂq?6:/eGgW_.9Zg ʰ.߆XjG;tS@:|-:TkQa}A$xnB&+:϶~W򅊝(: a`\m"+g"2A &-:s <nNው0ZH;f zau,'=+$o+'&y߲e$ڱq!XJcr=K.5NN\ \B^=g$8LC%4$69p}RB$9n} _p9`T !$&ƥ ߈pЖu;  3<eƴe}7hoX5Hn?'z4ew2[| CgXʢ+"x'z+߄/M)aDN[p.m+6.dvdgAiXLnډ5[8^p)T8䰦 gW P1aD訨OHn  ԩ ܈R6& `XѼHR FR@8!z\Y;ux9$*s悭T4ZGX ]xR3l-tLU`Le7Ξ'pS'N<R)Zo[űn_z;9n˽Vk1r83r$A,f"KsEnGcy;S` -"|rDb}cf)qw}zGoQK2<GaL TNn78<^^e+wo\snF¾79{;z8W3O2mȱSΈ>ԾМΎ#ggMg Θ}9Lv<_>#PI nrt+4@s,٢Áщ7*aVJF LXTBebR3Cʎoy+rԼ*#xyx>G## }]E3_x<.3{ LDQ|]kQ‰2DsuHSJvl,B{{c*d%ؿ]H繥3btw=BMO;߄7@wcw|j]rqMOD8fZ3Qk.!d"'T; nZ| 'gaR+%tzI炿|Ia=܅rY4TM#Ӣ+ pIdx`A(HZ>aMl`cF̎0 ;#aXv H&K֬`87xx_UBYXf+46A)Zq 6%u眫z5^atEkO?)#Nd5>ӱ9}81#Q Cy,CsKEK)f"5b.^ + q}dPĘ0Okk}N:hs%s~V3>z~}'f4\9>.Iꆒn$`+ǃ%w!fuqLk> 8r$9GdXL)J.抜x`(:O X<9iL̙І YȘ,ރl˾R(jm#B[}+%>'7l)ᩊC3e^IED g9r^M\qN95/Dv_aȏg}]mqLc4,܉5 1s@Xc)!3p*%Mx} u0 G%E@M,mFnjh2=dzJ/៩>]p!ιy!Du>FkgAѽDr6-8l uWQso:I 9D6bmcpP::iv8Ă@JصYEff@w;As nRSZIIYH™z2\QTr,q=vH!x\3R"tm!g~2O|'cP\1gYue\UzB7Lp׶UNl,Kzs ^?|@8 ͸_}$xj*G*z a˼oRiwGdߞ8k<}ǻ8b@ "3CMIs4m+GcB4x@ɲ+}ehťsRKEonΘ~*gS2d#Naяwit/3-e):A ;4l p_L:6+(F:}O崹*$=ՂJ5ĒY5/5P xeTf\לlt2m~97|+/]og;Ӆ W!Fr+?Uh&—?_+fWؕ2?9ubj3ntr BL hM0Ig ùp HTA/ncSβ)úyxF5XG1N! #< Rdwwl7Z̘ukD!_~$ YQ8) |DjsH=DᬎS=t­nMXEDIYiLj]*OE]G8F$Q|L 9v'yb4y`<#+`_~u q_#||_|)ɂgq=}gf+|cvqn|5<;-z\$٩(ĄHIT;5COwosk]cy<{="VZ1(($( FI0ФE~!z,4-ؒJ& j-"F,P8Syϳ}9qk̹}8~]ov~^Z}95q '{peBx ƛ慅lyYrtɴ A/Jh6.1[Hk@6筠A퉃θm8w::^=^0\Gg+ZG0,o: fYbbovbK2jBcL;lTcvOi5cUе/T}".̜@dZ qCvb 省myfV3lkGO|n_5[bV9Y aJuaFk"e *?867K3w:wlarըyrOg(w(3s@(]q>ZQ &{қYq̣33p,NNcar0Y D"*J[ Qa<rhۮb|;JXb1:^=\aO, )8(C")%2:xtoVˆSl6™߲ћA ζ} IDAT塱9H&%1(Y4Yg2kFgF5J2s$W,h3:eVtW-F5/ DUˍ @`AHk3O}?X> H"2EsDw&gZ)9?vWZV w έ*w>@;Fxxxqe?bvO;G_xƎH e Tb 338R]rdW(D=)!e Z'B?>RPZ͒Q%Jm㢔ja\ˏ;{Y#iWi gAG> ?+?wyf8'"`^7'?yˡA-O Zv(HkͤנqF7$*p'eP$1^{Uϧ9gA} ćp7cfry0fy鳇y7=`84ɧ?pj;R prqmx|D`G`2q%RYJxT" =rM)=4)oQV.VwkK.VW--4F4ovXj_lcm)H,=PGWïgNޕ_uog 0k- "*= 9JK&R~52laqZ[-A`a-Az yqF%0¢vxȒEkSevX(.3%u-yo-tS/H;s&"RyiF5[ _#$ˌlm%U*S o)f[hu՘ tB~ 1%, EDKT@W*hƳѻ̴|(~m(ge 㞱S,">MbWԈe였2' ŀ~yi _DJ->O۶e4"gvcj[yPxf6dH y9Sq R (,*7O8G=\ZA2 q ؟}Ы=Jv@qz lVKYi[qlY*=m#(*wWv[Nfxч9ԧ"+vΑs$s !0/m (.zq.V ь\Z򹫜:"Z)):#[}'ӽqڝm`8;rlx>p:ESo/.P*;z0uLuGGEi{1NZ06t=N. $h3X3(Vo$*j7P3 P (x;!yqGGEHG;(SW-16zgYb3㊭Q|S+*-%jVc88q:?%ip0] K$M bR/۷gQQ#y}|6n>{ebRI܅P~i ]xko[ם,ZyXN!b$]' H2ݭC13 Em!A/OTh2r*mKpU@i-r+ cjY2VdДA]|`D: W|~(+uaz oZ Kp#t62SvX8; OZ8Z."Brtnu:g#4:{KIlҡb"J=#8zKvm|@m2؋(P)s &^k=d炆j(vt:|pn@s(~ʪE}q D# fIN/w P?k25PS&u*%>X~%2?'Ļj}z?q~-4_o2ᓼC2xdjyPT~jҼ~tؒ ~}h;ZE4k-4 lZi=,ZdоG_n QJ~/4:lQ$C=ζ[#:!ѡDRMB*F݇EZA{O1ɒiiDɥcGT~%V,i 3.8ж-[Pѽ? MgfSca^ax{A}5,i,1ۿUd)>J"pZGk;0b D8 8+?Yƒl9vбID>o7sZé:8Aa2:ʖ]#‘c ,H@&2Qm'(-{̘aЀx@}5jۻfZKʍgVP{oF9cE|˾؀ƟCy9ں>NxUwG=PZwqpr h] "J__9IaG_4IJs r>"ױ ࿙O=׻ /$;@Չ"2 c>`y-}!ZW L~m#5\1{?ݮ?4r4< z<<9|K)7s1tdNL OPWkɶ„ j#ZZcۻD5<W:@`4b !E AT0`!짳OeKcf>p1ZCґ ==tָBHc!bk1ds#;z ~- h)QFji݆>ܟPK/_;{cPb[ :m0xyzca!(!9)܋Ls]t~eIE Gr~xxȵaVlTw:)M8swu8ߺwK˒ -+,JAa ^S/=f:Kd s@SaՂ`-P2%QlvɊ `0}D,/Uvߕ܀2f,6Xn8y+A.1`RR%UE&鐍Gv(tK}m@CrK|ۏ˯wA[~+wSz@ ,3orHRҸ3 `oۖл$4o6Fd͉{k&xM@<72%*aio&309D=.h\.*> #4Ȁl#l=a {%N[fx|O qy$B/x۶grk*"C{|CUFǵUlm2lrf[8:q땥e\)E t:hLP8ߝnS>lQ}c\%bDGg>_Uj?]7B^x K} eKpp,Ԇ-&ӂdm=zOp<'2ߐ6zؔt;4at``*s&iWԂ}P @]|>#jD8#uLޝNN[2CgWvH*U's l0ᎉ"ߑ%@>L"SK)8Պ?x^oz_D͒ZFMu(wyĽRon(%i'd*r2Ƣ'TCp^<i tQ 6#a5U$@UY8bt%8FFxu(A(£99m۲c?bnƠNebz Tt~T,piiY{.t05:hꔩA8tΌIygHZ)3Zl Amfa# (!q:ZĎ~)DD6'r̡`XAe+ ޑ.+iXQ8e[8ƔhLy\LZ;,qUz# 4zpH!(g]^,fVJjd{@'4Vl.˵%s:;bّV-biL[k,dc_BVrZ8G3 Ǿ#;*T Z:7g Oi$ ,Wj3(տ̼Bp/i9Ѷh[Cְ_×|7 o /Y_oG?k80wČY}.:9L1KouC Rg[0_/{%&E4|o L"!"CY}6\]g1tr+@í}BLJ1Z j:Χu ǀĩ(~e$B#[E4-03d#ʃW53CFG)cq.*յGDYi HWϲȔpT=m9]e^/QQ?7`XQ.w"kfS+`L%~@{%i W}ՓYK<TAcE_yg,cU8L'bAu5#aӶ)1QaI1. Ր|6|4Ͼ3 o(1_ ۾oyUsfs䈹0GdT,Mn:NHG S)dͰ_vZ3ǎ%02Ε!h3&P;bzw p8:)+!ޢP&=h <8Y^P~>GGF׷o=FpxƀJ"~/3[.1,R !idn#Q +[%e9AТ}2XzbGvlՇtV$29@]>;Kf÷DX:]z\L<"wDPI{@>ͳ=q} "16lوIX~2+Fq *5H5XጪNXt ^gV cA7{{Z$Ϛ8csXk-8kB,3"V'8'L40(H£QirA0%9cS;$0kꎈMj,DJY'f& wG+p<J8O4KY$I=8<l:4;76bd#~ 7uȎ[k`kʃqbQfH+ lh{:rucYR*%*~q4k{ڟa-s}{k^Ȼzcx;?cٵM|0X]30]%11tڬC }Uh*]#FqEH`=y $wT o$LI+O=<tk-:1GjuM[ms}y-Fn amGE+k+(Jo(p-J&%IXǘ٠13k)0} +a1Vw ='*GS14M\ɬӋg#x>S>%?c}Q9L fk֯)i΋f81NT77#ogp{PwM"g_y2@RȊUpۣ=xtࠢ%CrBF;>3@9Iv$2췟{mvoqrV|LwtȫOeHptUSo};!7 A|?sr>'l p!;2n71|kVg!P07^2a?F 1cK鐁zkWjdoOw׈CO7]} 4uu-Uw?3#6O ,9dpC cT!;gUk7xgd@O9Xݳ{\.@:xt59'2 Ԙc [).x5!t]&Pچ 7*\xWl!\>c 4N3, #ZZűYkRH!(@+Z9ꥵbfߏpFXA@KR,W4iۤnkxj5yF18/^yWAFއ3tQ={dBi1[y/KPS[5mzBvs~,IıZmS2r(_b1-WS;e3Y%{VRIe<*~߱>N8mޥ|h;~w+W_m?E>Iu@րzlm8dnjpV̭uAP F\׌`XL 2I~mR2]$CIm\?Ǖ`ضxfDf#Gy S3qMT@Q#9ަAep>%fd4 6PX2C]/5Dt*GGF^#R؊T5;`H^ !.8V`ՆrEcFt> Ng 1XrZ2:T 5W?a#TP!, (y~n4: l&f@i:Ga93bΉYQmˮ 39鯨]k 9+ }V: )Ez NHh{kS\]O>ץg? v$QkIT+mc:b[i#-yMKSwi88ET=f珮O%"u:!,l7/nKYM-|v?}W=?Qa72@_;W_lDͮ Y|^ڜ k40NN ߧf `-y^G~A@TsO~l٧> Sa'r$ #G( ½;pHbg9=4HXhӑ(;Wd~g_Z ̄cӾD(H#;C'!x%^gf怣@FlG]Jɲ<㖉Ȯ`d@ :aTOǙύ!JOu,o K)~G>E&{JkNr}ֿ/ |ӟ\7r2 A;d/KhY!h=)S76L9uκ|">wO'BKc/a"wd`ԝ5a8/צ]kc7z@Menv絕=yP#~d~SNFZ w٢ӣV<;>b\/zŵwXip'a}tv`ǑXٮ5}5)P f63K7-Q X!>τvALm-$r7f?K-D N_?}kW٦OWNcт xS1^|ꍱ $cd- 1lܽ@s)D| k=LR Sp :Qs7ғ2 38fă'B%&W@:{|KZWGWϝhF>TڶA .4* j_;,w< /giFV (tV_I4 i1)HZhL y%5Si鄻9ٝR:5LBk?vx8U 1<hݫ%J@1pj e.#fINJtËlX);HT$g];9H狶Ť"Z[>"'_!s6H`+Ak&zJz<;ĵoɟoz}>ٲ:aW l2j*aw-&l" ل5͕ǐFq!U:GȌc5 U랭CCdec_&>Lmn6k" {6I}|wcG~EPo)62-GdNKE= ) /H@-mhmdaNcZ ʄjk/ a<>/J$Z8Oe!6y&R^CDࢬ|s@ppHA+ {U)!N.K??k2ԑXC#sQm[ř]4:$CXuN=֠@CM3t7Zp\pwG8TvuvͶY*z鴅h`?"x jlUK(Dt:a .xVUGΧ3Χ w3N;<<2868x^]w\G51Yf5[/$U c&B:ضS"W¾z@fuFp)A6e~lb-ͩ3A뽖Z8!d2/{p*l͟$rS1PmQ3xn#r`3TNpn[1<;8z8qNa7FLY>"A;FNv:ݠDZWh xϾxľl;GK'J9v00^GC|~[RhrH%Q9RפbX9-ob+X  Fo2^Vqw#QJūW/@#/Vs%#L-ٰDŽH{@8#[;̐At*0lV #4 ausxX"^Qy^ږ7j!_w:04 $1ͽ bm?n'@r 5SR JWJS dP hp6lOtMG9)l-q9I?Hބg`k ЌNv۶jE3mk(ۉSkWWW1܀u"ɷ>X .1hy!p 9nljͦ_D47J#zO~>{|U)d Nm>KFp/g,,N'k=Xc:dGA kVo \~sTi$2YU N1C %1#++–jm[9oI3 1`RJ*)S,Q ^j #bf(蝽Y9.Qؙ ӽ=<0uLk  48[!R#Tʨi锭pbqiL1`¬Tj)2-\ ;~r.} AkFBSaKwq#ț"c1e2Piub 赖Qֶ9c=M`P ʯ &!ք޳&.d:Fǿ)@\U>zY*Wv`{&@x5Kշ~=lc IH511p>p)NrToǐ&d ӨU[akYsT \c GR@]P6k = t"R|AJe+pӪeQ5BS8qw >dEB]kf 7 (,ه2IFR%Z+0<A h'$UqwGN:4D&( }fGm]Af`9r}Y=Cگ0L;1z8`tJwv%-8G Zh{`Z28c{d3&AD+UJNad5Ʀ{1+|;D| xFmzm@ԼK(^2vG[x>Dl8H弚]7bgYP8[:"y'xBTGXAnM [K37\t/+}tl"O'g?ɿ׼Q7a}ѿ=Go!uNz>(BQudzn fRn 1 uC,ޗC.L*CEgNy3,]*R[|,$B2H+jٲrMMcA4ڜ4˙p#Jd Owxvb}dcy S=~sݧf=rz?7p/pD|1rZ # mv1|5C0%^ȺDPr*rzF )hĭ^Jy67Rl+9Pc8.17'I6΃";"YJϙN@r HRU~0̄~kB0t2Ɓt}(% ǜ"ff`{"a7r-Yo5Z0EO]:5ixŁяwA}fA_vNl[YݶuJ9C cogF~ :zٖf-"bW{p  IDATs:edjo`[jW  JoR*J'"151x)xc̐ό-RY+;I Yt@!_eZ;u&Նm{I3p* 5[k%ʢD$}IrN;EDD֖3UyP]s0V+mB$ $(TT@ض G_ {.@w|쓗c)Iq``:GyWtjkӺ˹"ǛDMY }G~4V7" =yj(X`:͌j4vy"fw:=,`BPM81gdgڎiñ0cj(h$Qs`AA^hiWH6{aA,,CMډE@Rm#ϡWgGþIHm[C+%5?N6QZ[?ۖy߯8xдV!CSEZ Ri9Hsv$)H1`eL"?5.V+W.)49?Zw(Cm&'}P{ JANtK^EX2q h*b%k㵒Ť [%ЊY2Gy"3tO>\#H5:I+|кa2,)ULd& YI#KZ&aQ/) >fJh>*(S_zW[֋{)1#S;Xd1T "nد=0d"CietGg!7iok܄nk«$/#܇˅q,ĺA ҖzkGG9dN"*k|L>,dYZG/*Rұj+;[6@h{* .|d["DV" bvKDQ*1*#@m20nBx{|P'T{Jv=@>ì媬' ,s uVi VxT,TC R3@c,%pJݶtjG}xܩ310jD]#K].<IpzedD_+bYyڰ'N*s;F8l 9֪F\ 5D6ڥ3u3ٗ6eՁ1&,5g_k vt rD͖@]NWҡmkr0K>-ܞ%iF[~Y-[תJmK>b<{+Z;^B+3Ν-[CACjmx{mp4@O '!{+1?[gSN=v7NeC}H}Cb '@q3k6mf(S}–<=DY{\ `mF-v?pȪ&dlt:¤p.ק(-dU 10c/$Qˑ f3{cHY,h=G*8Bڈ HFYLcP7 6;?{l>[АeBSq5$r,{!D{P0(я w'AVL E{aԣjV ڏW(p.׻P >XԚyP)5TC>cfjFK6|M' HK5y>c_l݊;&i<^]3_ њ B]\߶-铁t8y:}DHCaqGw)j_?e!^?_>_0TVw:NM)<w_4w0ঞ0ZRn63G]EǸ10=g9EmH "A3Mr(Zl1,ҦCF//aEJ)x\33П@!$MA:9\!rG=zhYʏ=:&=k\ >Q,i5 44\7ѾK $a{=*c)'!ކlB%10+~@)0 ILE߼w(X=/Ôlt ouЭG Z }{Kpl2s5iؑX|F2º-R^YXK ;̲_X2:h(y#-,1?)|q7e%j{N+ud(丮8^hTx`.$G`JYA>+{>@o{>`3*lxwkT"b[;sY}4.ܠ؎*z4Tǔu'&@И̊iDCfY!s=_f  Yޯ;2;T.'U3\:yu<>3C;Բ5yM9eP 5k1F,qa%/+갖,R}vMM!*cx0>#gRQ{7KV_s}Et,YUi ʄdFZgZ& YɲQD*;sYoGKkn['PGsB١SW0l7cx3(ĵO5qK+1=l!dغPҖLts}B82 uu[QR>K T#W j{ +2.I1z~mMTDR &Q)*@5SoIa@ h+>{_;iۨKb$yDtKU803vS:[BJ{:Pdg fQj gl8B o;t:*utc5! $@u>^Q[5ԛ`f9~}KpG߃P؆pMڒО @ VbKz4~m_w~}c.ay=_hG+'}qj:*8Џt") fwFIY?Pn FB#{}>m[@~1̞rAǾjqƆX }VHF٬96h_!!k&$cǁϒïH`|:utw0tjġh5"(dR&B"0:R[[ZPLCd N 9gWXZk=w|ϙ3{}<>uk5NHH4 fz iq[kcu5hĈk0$a餧9 îkZRL{s;Y@LMB`&[i;}E:cN{Dɪly3o^W@kF6и@c°N Z|:eY55k5jĝq2t ##S rXf{)U[- !?s9u@ZMB=Fc?Mpj'Cb4tJ']ֽAow$%3Mr^MMi۵-.I/-9-\7egyQmp5l>7Gt?jKDgd7B"k)˝ ^ROnz9{2ךIpB6|e ]DN_ށ8=/y&3A/w~ƌx cAO6.8vfAN43?]"t]]pжmh> x:bWK(%pޭc@?U~ڛT( /e6/|*ͭG4ݘD:!ixwL^o"ڗ俉mw$JSǂH #23$j1~sɜ(z0? IJer-{@~ߍC;P[rQLfw0l"Wa^n\AqfҬTA$֚SWUV;'[~vעeduFN =f/M]Z} uHfZK2<RKtlѱwl9L_upi##w&Y{a D٥f0,"#^Gx̮N6l{߯}G!3*@ /oyqXs<4>lwxx+*A sr" \.=N[@AR 7Lj~gV5q>+S>lmYt^A(g#VC$ף365˲Çhا Dd;[z.Qg c}D{8ޚRO xxqT쭹ԬLwϮ&{_Os dh}cZk [+u/ hY%=Y6'B.wl}uo@Qy P8ԅຊuL8AG804ȩCt*qZ قZV{0 3ZAٹ\xZjz @z- KRƞY<2`Uh8Y6JE֚(̒0ppR$9 FgDMMKd@ެE &120 TX{_psœw݃3k2+ԍeŘ{ꍸceYesY6KtX \ 7Y@92'<Ԝ/QH9{v2LXu4՟!GFR,QK+-TI;ivcFq }@Nj?o[ݹGkgzqǰv7EfT<9FR >f,LEn&@(pxP,W{A2"42˘,:qK|CJn픱c=aifkԉ >>OtD83(OA,q\͒ @,)bAי?NE{d3+Z 1̆kC`! ӗu @7/kE@ka۶X#'PJ`Q 'TG%ҙ93s~pa½LD$Q&oK@[@0'}1T#9S(H6ًm?(|Enj2"Yx IDAT5UæLh)RwtlgKt {P2>zZg/l9iq, O[/}763x_M?y8O`]NNrڦƎn{xtN?BccN˪:{.~p[|Z}C8l!H*0Fz@ .&Aqe &T~6gw}#z$€uFIQyٌ uҠx陡"|ibOcdDnZ#M5Z?%)O-T$s=AWz 33-:-z@ԑk9BNE2+uZAS˲1>7~_Xۊ@uoQW)sϨZDc| : 1r:]̯>=3 $Eyc9̻mlM1ԞrN* {}Ľ!yk!15|[s92ZVg.buL-70s<ӕ6XOdZ%4ީ"8dTPzٚuYqu*JjJv}exVXk^=гuHZY1Y" {|{m■ع89QXBx~ן—3xFwۇ/mwDݧZCѢu !DScHɌ 8nlJlyװwɉ܈M1C-]˒h M8evC:1ioY Heuv4IgOM:;hi%[朸s ;9#ШȠ <]s5e*Vsz` pTՠfܞ!E5T`zi2C D}=ZpKƽ9xÇ=wY<mل#։f|=2m€f- b\hĶmB4&wX%->)QuN|!Nh 8]{bkcec ,Ile%G_zd<N'>[A$GES:R"x-ec zH+) :GlxI^sRW|"D|4y;mǶu5DtLŻ^_<?^O]|gX9[}_ʍNU\og n-0 C}cS]v PQ ֤yf Yѡ_t?Fy;l9'[c,mҭ5MςLעl|w,K>fvo|pl>c&IU~:# egQ{K>Md$=y4a[XxA.e7dsB@o}zhM?W9?HMjܹ%`1ZB+vs sX꺮$Ey& (NUep m!Z&nbxw"F,<O)C;WGD0Tkt?Pye(E";S9cw#d[~pVc:'>7= u`>2(2ڬ2ۇ51*o񌈮m9V/sU4ȥɞ%zo. Z ÚB&gh[SB Q)N~gD8 Ow JOBM>Q\dQװ'3:ٸK3ߧTsER~;벞:58F Nک=KQu:mYsܫ5ݶ- L:Kps֋$ڑ&ZDZa.ӯH)Gٯ}RD:c ~ dz"@~k*Hl4`m"c4؂dF讷PX-EL51h gg}NrsD-wyd@3OxOD`N̨ 8K}([ M%Nx Ydk͂7YM򛗈ۭ4Fk Z$<: rpvބ$I qEsd ~av0PلFyO APE@h K/SA"Aû>hnU)C~AkI"Qauz9DjiS~^}uOQ9quudBl)6DbMs+3H3"UӃPqf=Xk$Zb*+VZ%M,&9][Quk'1pъh[r~A%B3.60M$ a{z VH6Df@%cM'3%K(՜9XҏDCNrO꿹b?T/*%l3먀WsEL؊x©v'?vN5vǹ( y)vO1?#%dz*@om~z=7cusrKPnBY"2]~Hxg DcƜ> fܻUžmXZ, )T5 jX}D=36;$|qq$fc\ݹrUWk-fꂽ/c?r͹&9@mu|%-;F0|SD)s}$euqx6HY:*YW5Gr 9@?̝gH\x.r 0k &ٜ% ۓ/ė0, ibAЖei kIȡ0AiFFnC&mτc-3TT 6'nRfA^$hH\}? /Yr<_쟽/,5jPiHN>bBܘXw,\Y4hd4V5 :eژϥqy?Ξt/Ό̌.Kjݸ@oKGϬkc$^eZ(z$$ȒҢ9h.Fxf5Qnf@PZ<̨k2 p5z1]TSkDsuYKBq O?!cu]|NgiȚ\$7f}Ɍr/Sp,Y>w!1ۦThRq;eusCf^@٥Iv?0/~00%bZõCW( gcYGOTQ 17xҹb*Vzw\ųZϴ1 ϹowYt<C/zO?Q (S| Sct XMqwCBxncx]yD4Ap*ϳO* lIk87WBzTn#CH8 {WWq[zp.jucrQ.2yjDŽXǿ3[(Б+і#9A% iDx/#X`8(* ^FdE6-GB Lr"f|,P XS;Z " DxH¡"s D$ՀDnTT23gsDjFwORlx0 thֿD!cO",@oj;Afm")1JČi"&\RJlݙI`W3V*3=/c] )T@'!jdvQtZdb%X(-}p"3+k ~OƳxW?Y4n;ٜbdHQDRaԈf9 o{ӡEB w^ؗ~egˋDO7`F{3)8;$kҁ3x-GihZ‡)Z9?/'RF| x0F'iDlLQ?"xЁFGd↱Qw3󬎻H]ps5wTt2( GLgurvϬd{Hgc?.CF"DL =jY}=pRkbQV"Dl@:Q+QP] egAT_J aPsoDPnٴ]_"abk5vd-91p\M_SsЦj!27}T9e9־ݨwud 6H<쵕ec'7ym/}ijxV_M+gT^2ߑEڍUpfmUZ B<")[sw`G^ ,羇f;&-CeB$ju2#XU1"!/yT:MZVY}BɬE]UlC>P2\a-{)s,}{D6O#V 8yi0(.aW5#&iI{xޗz]Jy]e?.Fo @3= "f~yDX:G2h`ڶyt0؅dy"C6LQi;ub-Q i `Mb3D.H&6Mv٤9J˲zۦx,;Z]1|6Ƞ5a) O}68I m6!ѻMwŏydz>@OǿYJТ'թ=q8L10vKgw;fâ9`Z9M XL fAPEkjrd/Ҏ5j߬5zh︺sqA{[I|m8\|^? 10$}YyXs̓4Vpy}>hҙj%q/NQMnlL0\1qr&u| 1 rw882~;mE"Đ"wȒB~-E͔٬^"_$%ߺ/%e.Px A!NbW$Wiw=*G>gl`N,9q2]벋#l #![/X3Ew0Na]׃ #rQ9{,HT~Rba3-fk:`iϧ vIk#ֈFEz \[96cXQRT<<=ھKz׸nF F&]ʈj٩ƌ KP.o&"gg5&-- qPmXgR ֶx5 '7?r,w| O,?n/?oypU'xl0ލ`99^2b p}}ΙrtYȆq{)UvpܪXJ|R$9+1$Mp0gLsgm rxa*>f0I4泋H Z1=`Rq=A  K(]$yk>ga0.,!MNH ^;QQ~*u5wgsNip7$ ׹ϸr45sbieHXχ)m<}ߝc-t˺hie5:NJ"vZdjT]a'Dyc9,jS`H,5NW1|쮛0[.A9?: C8:ZYf-#o sM:}r>CZ1 S%]x?~↡yB9}''F4~1oHxQ3!rTN8O0}+z>݇#H:A\,s('lnWM^3Ma$V]eFxcz~c9#Wp՘=aPb=W bfY/3+l&/eBC-fH.3osM=?xMxĺ#{{whi_1Q8 zjֆUprnq"-=řH#IkZ H { Gp$[{D|qk>NL; {14ņ$YPdKc.: qwq]8`|wj̼XL04ǎ.}4@}z#,>zҚyς˓21)6zpA>q҉܌ (YdnD+i_˥ w'˪Iyow(q% IDAT6@q't'+\r5 q|Rk-h.%{0@uz[ $[n3YDcz=ϙcڤNJR8 %i$X{>e4fwGcqך9?3 č0%#p+Yd%`] 3 *|{ɐч;p-(V dk )0QHO ɍg!"0kI4_Qx"X[E|)AWE/}wm^P@<;=bɥ'ւ̑ $3\:y<Ȟs3Yms tmORʻ ?%vl eȧb(O״_c^ sʖ_J4a+r 0с˶ (O9pd!W lc 0"tGʑ@Dڪ M6֫Ǫݮo?q5~cW~޷6@x_şoy[ox>a)lxwcoyk88^oqBn^KJs~_~7~Bw/p?wCH@Cp:GD` 4o=G  M $X- Ǿw;'S=b$oI6N&k2ñ#DS=YF%/KAaiK{ X`P;)cx^;YiGfĔ4 ͡L<7ާs`6\àax'B DJ0 wr],\eʚh a C tME-;}my.}HJpge2Z')u^E7vp˖};{DJ]Ͳe6v˴ [TQ 6 ;S9k_W?/=*F}c`٩к;idEɂN33Ѡ bݽ X;xO+p{8nQ/嗾a{?p9[rDVH  !B^t14xl(35l fqPj`q~OX2N!"ԥ/U;ޯXU|>e ^ϣIM`s"% 2qW.BM!V:}?wBqpڟsb#d\Ʉf9]Dv,8gL%Cs2H.盰,sYxt@J`:E[g  T _v?`<w~ ;~ϖPPҙTթLAQgNJJм~z q[X!8I@$J(dx h,\y?F<^+%:cWwDuY\$%b]/>ꄥ/X3v"i8nڿ>BLaC j pϗ2(ס [՝Gk>}nG¿?WSw?z?*zlH_ j'`ؔ͘V`|mP)"fZG5̙$t)+h_Cze됨ڨ5<|sNܻwYaߍ ʣF۩ֺۘ 4Szl'Ahn/Sũ[Bݧ2%ȴB|+ͪw$gaS8=[Gʿ6@EY_u1 Kdž9šZif[#SHxuc?ȋ~v_8WCNN+(Nwd &wLȊuqnؚucz=1B+S? `!;H+mmyUCM>4=ދ?R,8R)T jt)eZ87X<'J?V:[:_W!꽔 ǀ$8MO"dYS~74펨kv[Ԁe,]zZ#(cˏA9s&~@8l+#IcfJ nA%kຮk9o^R9}FP70!T^᭐ tjR+cE-z}-{0%"bPM z8f+ "NQ($%C=vW9p꤂)= >_⿹ ~ m8>_wKyY*6a,d5ra$HYk B($bC^dhbG23}j% 2'Pq?i&=$1ȕQJjA3urfj]ɩ \/Cz$qD&9ĝ7BsKV%tiΉSω~tja2G_D Y|v}>t`pzY`־.+.R8+T^Wtl=bÜnˌBPyĠ}/eg RCP}uCչbC?/0 aR۶4m^bI$ lJd}Q֏U4Rgm7~-=;lm2(ԟuk MWmYx~Ϋ_?}@*z8tbKǤx瑐RK_ ]K5 Dzb#L}x2P>6,͙Dܹt>>.4iKǾ=ɳDN$%}*g5ls2 Ժ ^ED/"X$'c3Y)& HS̀PZ'"@ h \ KG` |@R#IĀZcǶD0*_q#6(ŘWR@VJN f"HrAȶDl]"zA.wVĤ:]+P0.ieih0T V1TܭͰ~(z"Zu}L-5sVka񙴟a-iE"/Q2ـ- ^5krKHe¯ް?ࡑ1=hQ ]ײ M[* q=Cf H1F116t 7 p[B/Wy4q*D兒@ĵj<8Yo;9\>3?2鼧lYCRv7w{1z O4ǒ1[J%˸78̿o9OI"ZTEB*{S .BƴEy;<#uH:sz{`ȞI?k :TKj5@iOJR4'b]0tr}NtBž\?s=9Uػ ;X 1FtjPc>rR>pN3-tMnO EY+k\ub C<\<-ּ= O;k8B|]s\9U>Ί}%zwkGC^t` Y1]{tz.&zxZc`#00Y??@uNytG=}7ү G~Ɨ~O2Յ:z9;$j豖\3Z\U `.У@li;m)u/=,\? HM~5$99xS+ɸ aN!m)(ua,.zR؃?Pf198γ:銌#Ӊ\%CBb7eր2H`< >E\2w|֯|+p{tO+=~_co~+WÍiّo.fC i-^ f5õipNP5x$LKr% }z:WB&S]WmI I̳ض :SC@CRƊD(sO|V 5<5W> y)lĺ`ylSU__Gh ~fH'˝逨@HtJ4y4Y6A 35kqH^x&ٚ@p$gFidݜ.Ȇ\6kA x&)C."3GQІe9ր!l4f䵻NY^WxXt}x)"Bz wđ 1lII~~ӻ>g\G?|^z;>׽|',8u> ,pybdefN@6e>wJ{oul#3K }g7ȞL*( Ow/JDg(Y'0ia~` 9˕VMN+Љ̸H*y?A=Q:NPAE:Y95C31Zq6NBKd}qva% x˲$`G`+Ne/x qФQA(*u iu5.LPwpRf%y?R#I_0{%?*F| ki_@HA*pDv.Xymhh?[s74qZf] XvWt):'lzh{Dl0 U. nﴽ qv:n/7^z쭶2|+1Рo3 &$ggKy%!pc29/ Eg[dsKJnc4`|>13K ssN̑Rπk/lKvЫ琧hK+Y:10ǀ#PϝzSSN֐=ʳ|݃=cFp@Zkr @l$UQ%.(#t;Tlz_?gM*(ܬFN1g! *#?6-<GbKF9Z뮲ǒ Lj6A]E{u Vu.eCFaYeG88eC H 8.=$$gd9NF}j `lSK&cXNex¯O>1pLeYi-k@0! ذ&jZw`Y7z64t"/--G:.C\}5429:vDAdR{ w/!K^ή9pQB8Q-j$TzNWض-Z4 "#J&f<~]CɱBsJD|a2ڎw#&^58e`rJ0p\[dČfN Rоס6n9&tP[Cw(AV-D7 ,@*4=3@vj" * £Вu@X>QײQ 0G2`}ƺ> `\0=Ox¯>>{Oۓ[Hj4q#)ZvPV0#b1_CRh t2Ҭ~&1d .`Yem=.J#4s mBW˕;ʎ҂Tsz`1 IDAT6̶u55nN),!S ( G]SpJvkkihPԉ *mM謪 u@1djdjiKpp~ 8ïC dNESjwNhc1.EkܑPf-w sjt=-JU: HubfpfJSQh% JVYY8dZwCU:d=.ϻ(RXf+ds~ɽLOU =ɿ1F ˆu_7ߓPX Z&נjw; "gJ4DZWWSY\ҎeBhb/{WGǯq  GSSK[ y%0{,N峪_fB>R5$ЌDNi$^g H5qWd3*$ώO+(fbN<Կe_o~Sũ #Qˌ;G.I,#SQ W,f ?wɮ-[.~MF}q."+U%e֬># Q1Qq@e2 N|c 2{ϪUU+DtHޫ 2/} \ˇz9dg #J.`Fs/`- VYrYs=zZ]w~>Zҋ^ o#1FvAӁ({^v%Tu[QXni=a۷PF?IcRx܋п[#i&$] ;QЊ'{_ܪ#xO~ŧ{z7aPTCt@pdW4r2 (Жt`Itl cDz32{$1LF#z/3zuqBaM[nI-h L$ xm|D" ͸8|T9?+?#K ө`=#I9 .4 J|S Dohv|6:'z gWK@PNa;`3H^(Lݾ[ƚ`d~׉n- uq͑cʊuP}C wmM"`)(Pywp ;D޽-XR){uΉw˾Vt}}oxj0b_5uX > 62T] f9>buu]3<^iR늬`E:eYoFuVGI }"nIkA<1@:a0<{/: 2 Q^z;ole (fu]ܲiSm:TD1vRo8!V|ZLԠ qO۲2@{σOԤdzN1vӊroE&Ÿ3\׊%fDs4˜1KU1-T|~ȍx6sDx{/w; ~-~'( dbd *UCV5kaPdƴL}j /Y@͸#QLch#CeSs>/Vg55 k;/3q$^z TGg?}w"])&֬ Z񳫣t˲@ jVe`t#.(TA@vH=D)_!i*ƴ]Y ?=[/|Y)F\5.Vf)b:8kxr2z_zs3laղg'~=Xņ5AHh6Q\ݥM'1ƀ9_H&O=ձ6r?N|' {C?U]?I8Y˸Y6ұͬW}f{ XP;;hY?F7/>{ba 7TK(hҝƧ8X|w1 ˤ-q4ӧU:2+0l=e-wҗѺՉl0 `$Ν|XQ.+c~%;`Tc;jY nY Kv [ yIӑC]#GZ5B%rN>.>[ Rz HE?(uAovrTZf[y,]Bq@1A_J;4 TUOP]qTg`k8R;oTq) i9 c*H,|6-[>q___){ s )c _~P&mk|?_cO>o^"Jc] "?:yAv,TJ3Q1>[]e[c#HDdZl$bϲ5̶it"F hP^: {j@XydQ0j/ed8kF[s{BPE0l6\'LϓA__ALApP -qaݡ\B̶"@<t|u6B6ANB9>DCϿ4S9D'A$KqmQZY.䵏y,/7ʁAH~>X NJ;32fxUO_`6@/5oC;>f}ӳg #cịC!]­,cP z32$;}BṞs&mF%RksmpuN8_Uo#-vEZrj6΃5*8"T&Uq~ ,=ey׋JG)l"8ɮAg^y 9hӻ ޮPaǎC E8͙Yh eR?k>UupxUg}W;s#(y-<o^':uhXi 5 Un,`{ʡ{Ġ]]]d}oo=~Mߗ~g?__ W^Y7ۼ JQ:x0/;<ڥ( PV$,c EfdWBS_kݤ$u, Y2tSϜiԠz*Kt4#4j=+gЋ!O89vU#>0&`PKD+εŲf]2 ԣ:nnYf瘖IMK`5Ɉ#cm{:܁X|/J|_}v) |vu_5S I2VFʳ@l3ɫ\*ɜK h'4mq@ g uM~Tss47Q woWď˟k~|߻Gۀont<ցj$PG#1?BhZkfgV9P}k؜Ϣ9;zi|}4t*nj[3NU} !d#㝷Hus daAL` $ϖP?*{=1F.n^j4EWwDxZ]3cD |Sv ~,.+N$T-Ck^ž4XW0Z)GWYv>^20(z$SyxtZlA\B;60?l=޷mvU={sof2d&21!ܭJڊ"JkEȵBU"BZ)$TQQR \23Ǻ7j3~L}{>kg=Y+.RbZ{\Eʓ0+yQj@re"U-_KtAK8+^ol'IhcWyPS!;bM ?Çp |$EpF ҌJVZ@!Xr' kyͬjfk3E+1RBώ)"/M.hu>6pX|g@W P@բYJAL*[Rž 4k2B2A=^<[oDn.kd_2Ys*{ǾvD^ ΥXHD6X Pg+Fb9v!q Q?Ð!svC$0i z`C]j U{NCY&>:P#7, ɕB]Ǐ!<ZcqlނǏ˽TRZK)щz-y؍xË0Iesp}~G>oBYk.75Ftr8f vB,u:i ֙ 2j~~d\xlnĥ qCsp$-=2"Em.(PhGB<;6Am: CAuy@["9QPŅ(Q| `/9%^uMW_,E%[>e5Qb)uUB\%yNY:T^O^# %iy *sɡa^܌%8SE\hѴ dR_'.Z84ϗG2M-%+2[6m),]˖3 Rc:'ߵo])v /7`yUB7A;V#­eIh"Fu)x*'[ǩ.O 1}aY5NIVL-LB'DL h DɄԤ׍67U3AĀ@Ѳ0^wked9hFfԱMy&s*|o 7J/k`]B,FG/"JsG֙LV'zz||N\ba+: W\}8ˠjx} "3e/zɚtG`* T lQ?"rȌn}<60s^jb +Z!Ͼrb mwU2s8oצ%AX+~둝9@8쒟x9{8QmܶN,{M>\FuXGV}SJL}f6^}nUyQFaI\6@uE-v!pm"\(GW3m@I>p'flԒBl~PEA4ֈa\9(bOZKt* [YoEBE[&p QfE@}n亂$ֈ%ei?ʑBh#!BuPK$}@d }Y>Z6\Uc25[ js kRr~įH'3I#$+'(/D8+~vpkZˠ vmVo{l')b.x腏9W7?`=Bwy:Hu|3`7Y;^)k03ښB>P XSkssu_' J>KbA(͉H(relv uؒ/hK.ZQ?j0|0Hs5@ࠢ;MgP)X 9*R8t.SEk|ep$9%-d#5@ D̰icvp*B0|}!N<%]/ "CadA#UePrͺG_pÌ.us=G ޼#fgbT1QеྛI8/ }|_9J#+^ZD}sRmўm|߭{ Ϭf;lN1{ ~<@>t8B2!qc=3"SW\klx,0 GP'E! @dY 2:H,$Pb̥}_s= ܛqDZg2kAGQ9v|G_RB T:" (2pA! /ת r-zrLO0-̓1\ڽnΌZC/K1Pu~ J),(qȡ,O)/L[CMQKIS}sWVVڴ"y4L A:g]w}o7aST]?sjvWGgk)m괵61,5Āc&[ʍ@hhfG'% ]P jI$;Q%Gt)]IAo5(+s?&Aε_z SԤfd-+J&֨2u肒Ԑ{-$L% rn 'KznݠkODB>œy@{v9c J6Dħ/\}}c< u:wLi7:MQ%"&f%ltZB%~W2D/i!"A¸$34hC"_"zI߂C]sW߀}l)n+_z_pd+;:!VWFp4N4iIfKm-FdAs4=G#F fH+[FBPV͕:(AR]h5J g" 5[.M֩dI]֛'،z9gF\ D/m b1f؅_-)X F #.5"-ͲQ!H* Nϡ(*e-~ݺ{ƈu`Y!~V'V19d!~<~{s6]>qu{Op*v۱>~7cm޶,sBJgU2d&6E>:fQ JS/K[׾b"J c.w:*zF2UnՌ.6A }f%zBaSY?X8v?9O_NEftXK+Lc 'A.*A/D -D @,+< __T8 PF[ lA\1P0T!MmHGIK&U- |'T TmANGQ i@Ǖ wFgdDQ -ֿP]\FkuFqC:XǎkYr(bO 8 It<)V>1مgov彯}Kp1vی w?Gӿw5]u)v7L\ܽ}YT{U8 `Eg؇Z;P (xL@|| w{na]Xf:\]ZZ91g]g:kShKIε)aOYbue{>zUGQ,C^H5%Rz:s.8|jL{,| yC$%*bjLP_,ek #?##*XTR. R|+e J VH^CkQg/6r0܄r-׾n4HSCK6NY{;t_WřgGWZ³+++L 3d/%#ě8*uUGu*3%goBי ,oLL?-Ɲ%CA`#*x8p`0ulʶ°!0$kS$h=~aKF @[9#P%{]ιr - A/*y3 P!|^ϭSJy|Q|uHYChJcG{[ Nz lz)3]A>ri(?J1bq{4q?QC>竖|MRN ѐrYEk#.ڵy}?]8vhF e6#fm/'䇍fMF 6HMdmUl:%vY+c)nP iC8~8VVVA'P0 ~iܥ K;ˈy.sl81>:<9R*L_ *w؞rѮI [ƽ*J NZ7Smk1iΈR ź'@V΄$rFRЅh*KkIHu`ߧXQCbuLQ XLc+k93jGh>H.Ƚ`#/S^[uvв6!Q4&ƈMVUGvՋ?6}iW[4[*\X¹OΨ2Bu@ -  N3""XZѕL@Su}Np[y+q)N+ـww=E;ZyhLS3v(eDsy5{4z-xlmY3m\ԆaUrG٦]mY ~&I>u҅j ZxJtucZzj>͞7jlj9UȌ88YaMz|ƪӹDPQ'* WS}B^V=@!&ss׫0 k.$E%k-u"r^4.sEL,:jCyKLct9B!C}ƒ[ 6}={_ vf흙3!40 F)zvϞtg)ښm !˜|@ " HL!vX9g̩ )\5baM‘T7X 0KeHt3w~k:N:^1kR[q$|% jY?O}pN;?'8#cbBk}O:Xq-1$240Y 1%,a۶~/SY  E(G`EQ5>lϙ7?m6l'>忸\|G?wOҬlTeγ 3pٰwHd/T%=($$mbMK[A ~^ 7 PR@"]RJPgB,i;w8NUy /r4)9cm$+Nz6 df'MntM] :)o8 AuWk~uyf w>U;<uSP;>"A@u1S1W:Ԋs욅jɈV+F =k]O3U 0ss*DR}͛ v)gr5%-)١_)T?J\4OJPYzus10}1b1{D(&G]<,wQl } 6,"s< ~b6(:r<&+53qdTs }JC1FRpy;3q]~?qav=peO}{o>yQv'v2+]J16`҂.Pl琒ADd6VǎMNZ,f^ CdzT@l*sdhC&`ʅ>Vk(#6X%S`ǜjfT|Nj3EYPax@B*ZϧṞnj jϗĒ)!̊U.p BBKt]B1`I- ph,B/,i@0Ho ^D%`t1 RVMo;ڣӯllwξpeOz5._{#U~^U i,5?3 Ss_wnL^o}%l}6}q uw {(wQy\̜~i6u9Yj[I[Y#d&H.<\Q[ Eɨ]7!/a k˦l0jfYBz`XskAdί紼DD۽pAtAwͺr;xT~|m渉R6(_`{={N5RJo-toپ`;~_zO_;#Bf7ʢЁ*:OAm^:h &=`䡖BbH?yrDDNT RBR4kiF^OhwX6BC*b] jk킨ʹ\&ǣ"\Z"W%e.o'"FXIK#&@(P.GZ 7\ 1ӗ [5Y[ʞW\6vO v9gM@ZWEm Fg n \GhkV_vhh??/ H)Z"Pڕ)|uszխ4ZM$Rb\'u!62^ 'Ʈ\_JY\3h" x~ҋ4EVA1X6Bc4tŗbv-e'ޟ/1E2<!<`F]os= PCB υgЇ~+-n1g9@Kc睷'>- MWC#C P="Iy[$mz)u\R16B0ɏT?@ G՜"C jAd `M/PyiP0=u(uD0PU5HBKuPH%^.%PRjrɦV!܊.$ӏPH~'qM肺iM~JLV cCb`*;\CGybhP|ʁk}>|XD,,`-VB*>׶{_$r]w9lwa/]̗zm>r)Mr򦐹 c:@H8"]uK)!vMHBZ2Z۬g+G ZcJɺ|>#'A(~sT ڴ[L-C5ph[IN1*MMvr<|X$Ft(B tUB=.O {Y[oZ~Z.$VZƌ;=秾lmپl=κ9p籦>0qHXU lRY`qz*yBC#j^8gR! ̓P#X,p1s<>2D3SbnAn^H]gΫz$Y|y&?_Y4p~ q_? LrVkX%=z}Cy\9͌0tJKZB}3PU_oƗ$+ ϫOŜ|ȗ0|bq}po<yoޝl}6}my{}۾iӀl7cA@Dd-1N]ayWUFw +kVVVPrFv#9- i/tC/dQӚpܒ3'j2E} fҴ% v\5E`&VL4s\Γ *ĄMUIo; 竎^c W|BSU:{۔x"Ǘ@E$ߟU9-pέM^g;~1l6dcɯ={͇&O"LOjL[usSR젯S›BuCRd;ΔW`~T>/T'\X=%};uA7¥8q\{~?rW fMΚ(938=31YO^yby6Ww\~?w0'.e`Q ?o&@P}fiRgݚ\>vI2f %" 3-dNX,\.B%)%r(E  PBR<݄p_/Pk"v vΝ[b];k#A7"]eӑ! $ΨKIt۾w TہBꨦ:'tZg4K%~䶾7:.:>hF 5`pe"mrp5wHg$@<L(K"-`f髂=vCtN)ry"5> N$ \$EBy肧 } =.]}U/9(`s0۽.xæv$)ňK3:h@=A2--kR` b=uBz>%)PWI.X@AV?oʷh1ae8 ajZwahPRbP;BTR?#Suv^Cjcc 0**7(\숆 MD=fݑwn7_y0l"觽ɛv#Czٸ\&-~j3|}JkֽPSK RoQ=&O,A@S!6~1=5F5jyC|9I"ϋhLUTIА2!BU``pe)ÓDc$ J@[$M6+vq|CXۻ_x|g픴9@'C\}þ1v `#7*23V_+gnv06lӣJu kkLK"b"`"%VG<;:Fۡ1MaZ8R bVJYтk\.X, `׽\.k&wO~ 0`bUwm-;7n8w>w }fT9@G=߳e}q-榃wmcBP[HoS )m81KUD1P =\R :ǗBBHU3ViDJ rАirevGNka;,oK!$0EZ~,SңH{:$\ -Pc$έ̍ 7SowϾ;;ll})l}w\zzܴQC!::l#L:q[d#lTrG:|Ӛ?쉨)$UZ:SYw޾3a1?"ϽlvӥsJ.ٱlW7/wÑ;?7pl6;~w?뉋Mĕ-)wYk=;P-]yDHe@0 &=%MByJ̥ lP'&)!JZj-9Yt XҰÝ](qޫ^vqǝ_;4ll4vw,]\Mh*3}|n7SlvyJH /IENDB`ukui-screensaver/src/screensaver/assets/default.qss0000664000175000017500000000305715172041035021563 0ustar fengfengQLabel#dateOfWeek { color: #ffffff; } QLabel#dateOfLocaltime { color: #ffffff; } QLabel#dateOfDay { color: #ffffff; } QLabel#dateOfLunar { font-size:16px; color: #ffffff; } QLabel#clockTime { background:rgba(255,255,255,0.15); border-radius: 6px; color: #ffffff; } QLabel#colon { color: #ffffff; } QLabel#restTime { color: #ffffff; opacity:0.6; } QLabel#centerLabel { color: #ffffff; } QLabel#authorLabel { color: #ffffff; } QLabel#myText{ border-radius: 6px; background: rgba(255, 255, 255, 82%); padding: 24px 24px 24px 24px; color: #000000; } QPushButton{ background:rgba(255,255,255,0.12); } #escButton,#prevButton,#nextButton,#settingsButton{ background: rgba(255, 255, 255, 12%); border-radius:24px; border-size:0px; color:white; icon-size:24px; } #escButton::hover,#prevButton::hover,#nextButton::hover,#settingsButton::hover { background: rgba(255, 255, 255, 30%); } #escButton::pressed,#prevButton::pressed,#nextButton::pressed,#settingsButton::pressed { background: rgba(255, 255, 255, 8%); } #vboxFrame{ background: rgba(255, 255, 255, 12%); border: 0px; border-radius:4px; } #WallpaperButton{ background: rgba(255, 255, 255, 0%); border-radius:4px; color:white; icon-size:24px; } #WallpaperButton::hover{ background: rgba(61, 107, 229, 255); } #autoSwitchLabel{ font-size:14px; color: #ffffff; } #screenLabel{ font-size:14px; color: #ffffff; } #autoSwitch{ background: rgba(255, 255, 255, 0%); border-radius:4px; } ukui-screensaver/src/screensaver/assets/weather-icon/0000775000175000017500000000000015172041106021766 5ustar fengfengukui-screensaver/src/screensaver/assets/weather-icon/399.svg0000664000175000017500000000041515172041035023034 0ustar fengfeng399-雨-Rainukui-screensaver/src/screensaver/assets/weather-icon/209.svg0000664000175000017500000000103315172041035023017 0ustar fengfeng209-风暴-Stormukui-screensaver/src/screensaver/assets/weather-icon/901.svg0000664000175000017500000000420015172041035023015 0ustar fengfeng901-冷-Coldukui-screensaver/src/screensaver/assets/weather-icon/308.svg0000664000175000017500000000334315172041035023025 0ustar fengfeng308-极端降雨-Extreme Rainukui-screensaver/src/screensaver/assets/weather-icon/307.svg0000664000175000017500000000237715172041035023032 0ustar fengfeng307-大雨-Heavy Rainukui-screensaver/src/screensaver/assets/weather-icon/499.svg0000664000175000017500000000245015172041035023036 0ustar fengfeng499-雪-snowukui-screensaver/src/screensaver/assets/weather-icon/309.svg0000664000175000017500000000160615172041035023026 0ustar fengfeng309-毛毛雨细雨-Drizzle Rainukui-screensaver/src/screensaver/assets/weather-icon/410.svg0000664000175000017500000000670615172041035023025 0ustar fengfeng410-大到暴雪-Heavy snow to snowstormukui-screensaver/src/screensaver/assets/weather-icon/301.svg0000664000175000017500000000240215172041035023011 0ustar fengfeng301-强阵雨-Heavy Shower Rainukui-screensaver/src/screensaver/assets/weather-icon/306.svg0000664000175000017500000000166115172041035023024 0ustar fengfeng306-中雨-Moderate Rainukui-screensaver/src/screensaver/assets/weather-icon/504.svg0000664000175000017500000000212215172041035023015 0ustar fengfeng504-浮尘-Dustukui-screensaver/src/screensaver/assets/weather-icon/104.svg0000664000175000017500000000221515172041035023014 0ustar fengfeng104-阴-Overcastukui-screensaver/src/screensaver/assets/weather-icon/318.svg0000664000175000017500000000377715172041035023041 0ustar fengfeng318-大暴雨到特大暴雨-Heavy to severe stormukui-screensaver/src/screensaver/assets/weather-icon/313.svg0000664000175000017500000000161515172041035023021 0ustar fengfeng313-冻雨-Freezing Rainukui-screensaver/src/screensaver/assets/weather-icon/508.svg0000664000175000017500000000553115172041035023030 0ustar fengfeng508-强沙尘暴-Sandstormukui-screensaver/src/screensaver/assets/weather-icon/311.svg0000664000175000017500000000374615172041035023026 0ustar fengfeng311-大暴雨-Heavy Stormukui-screensaver/src/screensaver/assets/weather-icon/204.svg0000664000175000017500000000077615172041035023027 0ustar fengfeng204-清风-Fresh Breezeukui-screensaver/src/screensaver/assets/weather-icon/205.svg0000664000175000017500000000115615172041035023021 0ustar fengfeng205-强风劲风-Strong Breezeukui-screensaver/src/screensaver/assets/weather-icon/102.svg0000664000175000017500000000113215172041035023007 0ustar fengfeng102-少云-Few Cloudsukui-screensaver/src/screensaver/assets/weather-icon/208.svg0000664000175000017500000000114515172041035023022 0ustar fengfeng208-烈风Strong Galeukui-screensaver/src/screensaver/assets/weather-icon/405.svg0000664000175000017500000000355415172041035023027 0ustar fengfeng405-雨雪天气-Rain And Snowukui-screensaver/src/screensaver/assets/weather-icon/203.svg0000664000175000017500000000101015172041035023004 0ustar fengfeng203-和风-Moderate Gentle Breezeukui-screensaver/src/screensaver/assets/weather-icon/300.svg0000664000175000017500000000211515172041035023011 0ustar fengfeng300-阵雨-Shower Rainukui-screensaver/src/screensaver/assets/weather-icon/408.svg0000664000175000017500000000325215172041035023025 0ustar fengfeng408-小到中雪-Light to moderate snowukui-screensaver/src/screensaver/assets/weather-icon/210.svg0000664000175000017500000000104615172041035023013 0ustar fengfeng210-狂爆风-Violent Stormukui-screensaver/src/screensaver/assets/weather-icon/101.svg0000664000175000017500000000152615172041035023015 0ustar fengfeng101-多云-Cloudyukui-screensaver/src/screensaver/assets/weather-icon/SVG/0000775000175000017500000000000015172041106022425 5ustar fengfengukui-screensaver/src/screensaver/assets/weather-icon/SVG/153.svg0000664000175000017500000000101415172041106023452 0ustar fengfeng103-晴间多云-Partly Cloudyukui-screensaver/src/screensaver/assets/weather-icon/SVG/399.svg0000664000175000017500000000122115172041106023466 0ustar fengfeng 399-雨-Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/209.svg0000664000175000017500000000103315172041106023455 0ustar fengfeng209-风暴-Stormukui-screensaver/src/screensaver/assets/weather-icon/SVG/901.svg0000664000175000017500000001761515172041106023471 0ustar fengfeng 901-冷-Cold ukui-screensaver/src/screensaver/assets/weather-icon/SVG/803.svg0000664000175000017500000000146215172041106023463 0ustar fengfeng 803 盈凸月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/308.svg0000664000175000017500000001403215172041106023460 0ustar fengfeng 308-极端降雨-Extreme Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/802.svg0000664000175000017500000000140515172041106023457 0ustar fengfeng 802 上弦月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/307.svg0000664000175000017500000000745715172041106023474 0ustar fengfeng 307-大雨-Heavy Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/499.svg0000664000175000017500000000245015172041106023474 0ustar fengfeng499-雪-snowukui-screensaver/src/screensaver/assets/weather-icon/SVG/309.svg0000664000175000017500000000600515172041106023462 0ustar fengfeng 309-毛毛雨细雨-Drizzle Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/410.svg0000664000175000017500000000670615172041106023463 0ustar fengfeng410-大到暴雪-Heavy snow to snowstormukui-screensaver/src/screensaver/assets/weather-icon/SVG/301.svg0000664000175000017500000001110615172041106023450 0ustar fengfeng 301-强阵雨-Heavy Shower Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/306.svg0000664000175000017500000000534415172041106023464 0ustar fengfeng 306-中雨-Moderate Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/154.svg0000664000175000017500000000221515172041106023457 0ustar fengfeng104-阴-Overcastukui-screensaver/src/screensaver/assets/weather-icon/SVG/806.svg0000664000175000017500000000140215172041106023460 0ustar fengfeng 806 下弦月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/504.svg0000664000175000017500000000212215172041106023453 0ustar fengfeng504-浮尘-Dustukui-screensaver/src/screensaver/assets/weather-icon/SVG/104.svg0000664000175000017500000000221515172041106023452 0ustar fengfeng104-阴-Overcastukui-screensaver/src/screensaver/assets/weather-icon/SVG/318.svg0000664000175000017500000001775615172041106023501 0ustar fengfeng 318-大暴雨到特大暴雨-Heavy to severe storm ukui-screensaver/src/screensaver/assets/weather-icon/SVG/313.svg0000664000175000017500000000530215172041106023454 0ustar fengfeng 313-冻雨-Freezing Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/508.svg0000664000175000017500000000553115172041106023466 0ustar fengfeng508-强沙尘暴-Sandstormukui-screensaver/src/screensaver/assets/weather-icon/SVG/311.svg0000664000175000017500000001757715172041106023473 0ustar fengfeng 311-大暴雨-Heavy Storm ukui-screensaver/src/screensaver/assets/weather-icon/SVG/204.svg0000664000175000017500000000077615172041106023465 0ustar fengfeng204-清风-Fresh Breezeukui-screensaver/src/screensaver/assets/weather-icon/SVG/351.svg0000664000175000017500000001116015172041106023455 0ustar fengfeng 351-强阵雨-Heavy Shower Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/205.svg0000664000175000017500000000115615172041106023457 0ustar fengfeng205-强风劲风-Strong Breezeukui-screensaver/src/screensaver/assets/weather-icon/SVG/102.svg0000664000175000017500000000113215172041106023445 0ustar fengfeng102-少云-Few Cloudsukui-screensaver/src/screensaver/assets/weather-icon/SVG/208.svg0000664000175000017500000000114515172041106023460 0ustar fengfeng208-烈风Strong Galeukui-screensaver/src/screensaver/assets/weather-icon/SVG/405.svg0000664000175000017500000001363515172041106023466 0ustar fengfeng 405-雨雪天气-Rain And Snow ukui-screensaver/src/screensaver/assets/weather-icon/SVG/203.svg0000664000175000017500000000101015172041106023442 0ustar fengfeng203-和风-Moderate Gentle Breezeukui-screensaver/src/screensaver/assets/weather-icon/SVG/300.svg0000664000175000017500000000706515172041106023460 0ustar fengfeng 300-阵雨-Shower Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/408.svg0000664000175000017500000000325215172041106023463 0ustar fengfeng408-小到中雪-Light to moderate snowukui-screensaver/src/screensaver/assets/weather-icon/SVG/210.svg0000664000175000017500000000104615172041106023451 0ustar fengfeng210-狂爆风-Violent Stormukui-screensaver/src/screensaver/assets/weather-icon/SVG/101.svg0000664000175000017500000000152615172041106023453 0ustar fengfeng101-多云-Cloudyukui-screensaver/src/screensaver/assets/weather-icon/SVG/801.svg0000664000175000017500000000133115172041106023454 0ustar fengfeng 801 峨眉月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/202.svg0000664000175000017500000000077615172041106023463 0ustar fengfeng202-微风-Light Breezeukui-screensaver/src/screensaver/assets/weather-icon/SVG/513.svg0000664000175000017500000000134415172041106023460 0ustar fengfeng513-严重霾-Severe hazeukui-screensaver/src/screensaver/assets/weather-icon/SVG/152.svg0000664000175000017500000000113215172041106023452 0ustar fengfeng102-少云-Few Cloudsukui-screensaver/src/screensaver/assets/weather-icon/SVG/999.svg0000664000175000017500000000054515172041106023504 0ustar fengfeng999-未知-Unknown 副本ukui-screensaver/src/screensaver/assets/weather-icon/SVG/406.svg0000664000175000017500000001132615172041106023462 0ustar fengfeng 406-阵雨夹雪-Shower Snow ukui-screensaver/src/screensaver/assets/weather-icon/SVG/402.svg0000664000175000017500000000471515172041106023462 0ustar fengfeng402-大雪-Heavy Snowukui-screensaver/src/screensaver/assets/weather-icon/SVG/503.svg0000664000175000017500000000121215172041106023451 0ustar fengfeng503-扬沙-Sandukui-screensaver/src/screensaver/assets/weather-icon/SVG/510.svg0000664000175000017500000000363015172041106023455 0ustar fengfeng 510-强浓雾-Strong fog ukui-screensaver/src/screensaver/assets/weather-icon/SVG/312.svg0000664000175000017500000001765715172041106023473 0ustar fengfeng 312-特大暴雨-Severe Storm ukui-screensaver/src/screensaver/assets/weather-icon/SVG/213.svg0000664000175000017500000000161215172041106023453 0ustar fengfeng213-热带风暴Tropical Stormukui-screensaver/src/screensaver/assets/weather-icon/SVG/407.svg0000664000175000017500000000340615172041106023463 0ustar fengfeng407-阵雪-Snow Flurryukui-screensaver/src/screensaver/assets/weather-icon/SVG/514.svg0000664000175000017500000000365715172041106023472 0ustar fengfeng 514-大雾-Heavy fog ukui-screensaver/src/screensaver/assets/weather-icon/SVG/350.svg0000664000175000017500000000705315172041106023462 0ustar fengfeng 350-阵雨-Shower Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/317.svg0000664000175000017500000001773115172041106023471 0ustar fengfeng 317-暴雨到大暴雨-Storm to heavy storm ukui-screensaver/src/screensaver/assets/weather-icon/SVG/512.svg0000664000175000017500000000117115172041106023455 0ustar fengfeng512-重度霾-Heavy hazeukui-screensaver/src/screensaver/assets/weather-icon/SVG/316.svg0000664000175000017500000001412715172041106023464 0ustar fengfeng 316-大到暴雨-Heavy rain to storm ukui-screensaver/src/screensaver/assets/weather-icon/SVG/315.svg0000664000175000017500000000752315172041106023465 0ustar fengfeng 315-中到大雨-Moderate to heavy rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/900.svg0000664000175000017500000000403715172041106023462 0ustar fengfeng900-热-Hotukui-screensaver/src/screensaver/assets/weather-icon/SVG/409.svg0000664000175000017500000000510715172041106023465 0ustar fengfeng409-中到大雪-Moderate to heavy snowukui-screensaver/src/screensaver/assets/weather-icon/SVG/303.svg0000664000175000017500000000761015172041106023457 0ustar fengfeng 303-强雷阵雨-Heavy Thunderstorm ukui-screensaver/src/screensaver/assets/weather-icon/SVG/511.svg0000664000175000017500000000142315172041106023454 0ustar fengfeng511-中度霾-Moderate hazeukui-screensaver/src/screensaver/assets/weather-icon/SVG/509.svg0000664000175000017500000000421115172041106023461 0ustar fengfeng 509-浓雾-Dense fog ukui-screensaver/src/screensaver/assets/weather-icon/SVG/211.svg0000664000175000017500000000103715172041106023452 0ustar fengfeng211-飓风-Hurricaneukui-screensaver/src/screensaver/assets/weather-icon/SVG/100.svg0000664000175000017500000000300515172041106023444 0ustar fengfeng100-晴-SunnyClearukui-screensaver/src/screensaver/assets/weather-icon/SVG/507.svg0000664000175000017500000000164515172041106023467 0ustar fengfeng507-沙尘暴-Duststormukui-screensaver/src/screensaver/assets/weather-icon/SVG/804.svg0000664000175000017500000000072015172041106023460 0ustar fengfeng 804 满月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/457.svg0000664000175000017500000000340615172041106023470 0ustar fengfeng407-阵雪-Snow Flurryukui-screensaver/src/screensaver/assets/weather-icon/SVG/206.svg0000664000175000017500000000115715172041106023461 0ustar fengfeng206-疾风-High Wind, Near Galeukui-screensaver/src/screensaver/assets/weather-icon/SVG/501.svg0000664000175000017500000000364315172041106023461 0ustar fengfeng 501-雾-Foggy ukui-screensaver/src/screensaver/assets/weather-icon/SVG/150.svg0000664000175000017500000000300515172041106023451 0ustar fengfeng100-晴-SunnyClearukui-screensaver/src/screensaver/assets/weather-icon/SVG/304.svg0000664000175000017500000000473115172041106023461 0ustar fengfeng 304-雷阵雨伴有冰雹-hailThundershower with hail ukui-screensaver/src/screensaver/assets/weather-icon/SVG/500.svg0000664000175000017500000000307715172041106023461 0ustar fengfeng 500-薄雾-Mist ukui-screensaver/src/screensaver/assets/weather-icon/SVG/314.svg0000664000175000017500000000554115172041106023462 0ustar fengfeng 314-小到中雨-Light to moderate rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/502.svg0000664000175000017500000000124515172041106023456 0ustar fengfeng502-霾-Hazeukui-screensaver/src/screensaver/assets/weather-icon/SVG/400.svg0000664000175000017500000000232515172041106023453 0ustar fengfeng400-小雪-Light Snowukui-screensaver/src/screensaver/assets/weather-icon/SVG/103.svg0000664000175000017500000000101415172041106023445 0ustar fengfeng103-晴间多云-Partly Cloudyukui-screensaver/src/screensaver/assets/weather-icon/SVG/310.svg0000664000175000017500000001403315172041106023452 0ustar fengfeng 310-暴雨-Storm ukui-screensaver/src/screensaver/assets/weather-icon/SVG/403.svg0000664000175000017500000000666215172041106023466 0ustar fengfeng403-暴雪-Snowstormukui-screensaver/src/screensaver/assets/weather-icon/SVG/456.svg0000664000175000017500000001137515172041106023473 0ustar fengfeng 456-阵雨夹雪-Shower Snow ukui-screensaver/src/screensaver/assets/weather-icon/SVG/800.svg0000664000175000017500000000134515172041106023460 0ustar fengfeng 800 新月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/515.svg0000664000175000017500000000444515172041106023467 0ustar fengfeng 515-特强浓雾-Extra heavy fog ukui-screensaver/src/screensaver/assets/weather-icon/SVG/207.svg0000664000175000017500000000072415172041106023461 0ustar fengfeng207-大风-Galeukui-screensaver/src/screensaver/assets/weather-icon/SVG/212.svg0000664000175000017500000000205315172041106023452 0ustar fengfeng212-龙卷风-Tornadoukui-screensaver/src/screensaver/assets/weather-icon/SVG/401.svg0000664000175000017500000000323315172041106023453 0ustar fengfeng401-中雪-Moderate Snowukui-screensaver/src/screensaver/assets/weather-icon/SVG/201.svg0000664000175000017500000000074615172041106023457 0ustar fengfeng201-平静-Calmukui-screensaver/src/screensaver/assets/weather-icon/SVG/805.svg0000664000175000017500000000132315172041106023461 0ustar fengfeng 805 亏凸月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/807.svg0000664000175000017500000000142515172041106023466 0ustar fengfeng 807 残月 ukui-screensaver/src/screensaver/assets/weather-icon/SVG/200.svg0000664000175000017500000000072515172041106023453 0ustar fengfeng200-有风-Windyukui-screensaver/src/screensaver/assets/weather-icon/SVG/305.svg0000664000175000017500000000441115172041106023455 0ustar fengfeng 305-小雨-Light Rain ukui-screensaver/src/screensaver/assets/weather-icon/SVG/151.svg0000664000175000017500000000152615172041106023460 0ustar fengfeng101-多云-Cloudyukui-screensaver/src/screensaver/assets/weather-icon/SVG/404.svg0000664000175000017500000001035715172041106023463 0ustar fengfeng 404-雨夹雪-Sleet ukui-screensaver/src/screensaver/assets/weather-icon/SVG/302.svg0000664000175000017500000000522515172041106023456 0ustar fengfeng 302-雷阵雨-Thundershower ukui-screensaver/src/screensaver/assets/weather-icon/202.svg0000664000175000017500000000077615172041035023025 0ustar fengfeng202-微风-Light Breezeukui-screensaver/src/screensaver/assets/weather-icon/._511-中度霾-Moderate haze.svg0000664000175000017500000001000015172041035031266 0ustar fengfengMac OS X  2ATTR;Xcom.apple.lastuseddate#PSHcom.apple.maclP^#IL Nn 1'This resource fork intentionally left blank ukui-screensaver/src/screensaver/assets/weather-icon/513.svg0000664000175000017500000000134415172041035023022 0ustar fengfeng513-严重霾-Severe hazeukui-screensaver/src/screensaver/assets/weather-icon/999.svg0000664000175000017500000000430215172041035023041 0ustar fengfeng 天气 ukui-screensaver/src/screensaver/assets/weather-icon/406.svg0000664000175000017500000000264215172041035023025 0ustar fengfeng406-阵雨夹雪-Shower Snowukui-screensaver/src/screensaver/assets/weather-icon/402.svg0000664000175000017500000000471515172041035023024 0ustar fengfeng402-大雪-Heavy Snowukui-screensaver/src/screensaver/assets/weather-icon/503.svg0000664000175000017500000000121215172041035023013 0ustar fengfeng503-扬沙-Sandukui-screensaver/src/screensaver/assets/weather-icon/510.svg0000664000175000017500000000116115172041035023014 0ustar fengfeng510-强浓雾-Strong fogukui-screensaver/src/screensaver/assets/weather-icon/312.svg0000664000175000017500000000375215172041035023024 0ustar fengfeng312-特大暴雨-Severe Stormukui-screensaver/src/screensaver/assets/weather-icon/213.svg0000664000175000017500000000161215172041035023015 0ustar fengfeng213-热带风暴Tropical Stormukui-screensaver/src/screensaver/assets/weather-icon/407.svg0000664000175000017500000000340615172041035023025 0ustar fengfeng407-阵雪-Snow Flurryukui-screensaver/src/screensaver/assets/weather-icon/514.svg0000664000175000017500000000116515172041035023024 0ustar fengfeng514-大雾-Heavy fogukui-screensaver/src/screensaver/assets/weather-icon/317.svg0000664000175000017500000000377015172041035023031 0ustar fengfeng317-暴雨到大暴雨-Storm to heavy stormukui-screensaver/src/screensaver/assets/weather-icon/512.svg0000664000175000017500000000117115172041035023017 0ustar fengfeng512-重度霾-Heavy hazeukui-screensaver/src/screensaver/assets/weather-icon/316.svg0000664000175000017500000000335215172041035023024 0ustar fengfeng316-大到暴雨-Heavy rain to stormukui-screensaver/src/screensaver/assets/weather-icon/315.svg0000664000175000017500000000242115172041035023017 0ustar fengfeng315-中到大雨-Moderate to heavy rainukui-screensaver/src/screensaver/assets/weather-icon/900.svg0000664000175000017500000000403715172041035023024 0ustar fengfeng900-热-Hotukui-screensaver/src/screensaver/assets/weather-icon/409.svg0000664000175000017500000000510715172041035023027 0ustar fengfeng409-中到大雪-Moderate to heavy snowukui-screensaver/src/screensaver/assets/weather-icon/303.svg0000664000175000017500000000274715172041035023027 0ustar fengfeng303-强雷阵雨-Heavy Thunderstormukui-screensaver/src/screensaver/assets/weather-icon/511.svg0000664000175000017500000000142315172041035023016 0ustar fengfeng511-中度霾-Moderate hazeukui-screensaver/src/screensaver/assets/weather-icon/509.svg0000664000175000017500000000137615172041035023034 0ustar fengfeng509-浓雾-Dense fogukui-screensaver/src/screensaver/assets/weather-icon/211.svg0000664000175000017500000000103715172041035023014 0ustar fengfeng211-飓风-Hurricaneukui-screensaver/src/screensaver/assets/weather-icon/100.svg0000664000175000017500000000300515172041035023006 0ustar fengfeng100-晴-SunnyClearukui-screensaver/src/screensaver/assets/weather-icon/507.svg0000664000175000017500000000164515172041035023031 0ustar fengfeng507-沙尘暴-Duststormukui-screensaver/src/screensaver/assets/weather-icon/206.svg0000664000175000017500000000115715172041035023023 0ustar fengfeng206-疾风-High Wind, Near Galeukui-screensaver/src/screensaver/assets/weather-icon/501.svg0000664000175000017500000000123515172041035023016 0ustar fengfeng501-雾-Foggyukui-screensaver/src/screensaver/assets/weather-icon/304.svg0000664000175000017500000000213215172041035023014 0ustar fengfeng304-雷阵雨伴有冰雹-hailThundershower with hailukui-screensaver/src/screensaver/assets/weather-icon/500.svg0000664000175000017500000000107115172041035023013 0ustar fengfeng500-薄雾-Mist ukui-screensaver/src/screensaver/assets/weather-icon/314.svg0000664000175000017500000000170015172041035023015 0ustar fengfeng314-小到中雨-Light to moderate rainukui-screensaver/src/screensaver/assets/weather-icon/502.svg0000664000175000017500000000124515172041035023020 0ustar fengfeng502-霾-Hazeukui-screensaver/src/screensaver/assets/weather-icon/400.svg0000664000175000017500000000232515172041035023015 0ustar fengfeng400-小雪-Light Snowukui-screensaver/src/screensaver/assets/weather-icon/103.svg0000664000175000017500000000101415172041035023007 0ustar fengfeng103-晴间多云-Partly Cloudyukui-screensaver/src/screensaver/assets/weather-icon/310.svg0000664000175000017500000000332615172041035023017 0ustar fengfeng310-暴雨-Stormukui-screensaver/src/screensaver/assets/weather-icon/403.svg0000664000175000017500000000666215172041035023030 0ustar fengfeng403-暴雪-Snowstormukui-screensaver/src/screensaver/assets/weather-icon/._.DS_Store0000664000175000017500000001000015172041035023656 0ustar fengfengMac OS X  2 @ATTR;xThis resource fork intentionally left blank ukui-screensaver/src/screensaver/assets/weather-icon/515.svg0000664000175000017500000000134115172041035023021 0ustar fengfeng515-特强浓雾-Extra heavy fogukui-screensaver/src/screensaver/assets/weather-icon/207.svg0000664000175000017500000000072415172041035023023 0ustar fengfeng207-大风-Galeukui-screensaver/src/screensaver/assets/weather-icon/212.svg0000664000175000017500000000205315172041035023014 0ustar fengfeng212-龙卷风-Tornadoukui-screensaver/src/screensaver/assets/weather-icon/401.svg0000664000175000017500000000323315172041035023015 0ustar fengfeng401-中雪-Moderate Snowukui-screensaver/src/screensaver/assets/weather-icon/201.svg0000664000175000017500000000074615172041035023021 0ustar fengfeng201-平静-Calmukui-screensaver/src/screensaver/assets/weather-icon/.DS_Store0000664000175000017500000003400415172041035023453 0ustar fengfengBud100 D7-Y'-H307-Y'-Heavy Rain.svgIlocblobax01-YN-Cloudy.svgIlocblob  @ @ @ @)308-gzM-Extreme Rain.svgIlocblobx309-kkۖ~Ɩ-Drizzle Rain.svgIlocblob;310-f-Storm.svgIlocblob311-Y'f-Heavy Storm.svgIlocblob312-ryY'f-Severe Storm.svgIlocblob313-Q-Freezing Rain.svgIlocblob#314-\R0N--Light to moderate rain.svgIlocbloba#315-N-R0Y'-Moderate to heavy rain.svgIlocblob 316-Y'R0f-Heavy rain to storm.svgIlocblob;X#317-fR0Y'f-Storm to heavy storm.svgIlocblobX&318-Y'fR0ryY'f-Heavy to severe storm.svgIlocblobX399--Rain.svgIlocblobX400-\-Light Snow.svgIlocblobX401-N--Moderate Snow.svgIlocblobaX402-Y'-Heavy Snow.svgIlocblobX403-f-Snowstorm.svgIlocblob;404-Y9-Sleet.svgIlocblob405-Y)l-Rain And Snow.svgIlocblob406-5Y9-Shower Snow.svgIlocblob407-5-Snow Flurry.svgIlocblob#408-\R0N--Light to moderate snow.svgIlocbloba#409-N-R0Y'-Moderate to heavy snow.svgIlocblob$410-Y'R0f-Heavy snow to snowstorm.svgIlocblob;8499--snow.svgIlocblob8500--Mist .svgIlocblob8501--Foggy.svgIlocblob8502->-Haze.svgIlocblob8503-bll-Sand.svgIlocbloba8504-mn\-Dust.svgIlocblob8507-l\f-Duststorm.svgIlocblob;508-_:l\f-Sandstorm.svgIlocblob509-mS-Dense fog.svgIlocblob510-_:mS-Strong fog.svgIlocblob511-N-^>-Moderate haze.svgIlocblob512-^>-Heavy haze.svgIlocbloba513-N%͗>-Severe haze.svgIlocblob514-Y'-Heavy fog.svgIlocblob;515-ry_:mS-Extra heavy fog.svgIlocblob 900-p-Hot.svgIlocblob901-Q-Cold.svgIlocblob999-g*w-Unknown.svgIlocblob100-ft-SunnyClear.svgIlocblob;(101-YN-Cloudy.svgIlocblob(102-\N-Few Clouds.svgIlocblob(103-ftYN-Partly Cloudy.svgIlocblob(104-4-Overcast.svgIlocblob(200-g -Windy.svgIlocbloba(201-^sY-Calm.svgIlocblob(202-_-Light Breeze.svgIlocblob;!203-T-Moderate Gentle Breeze.svgIlocblob204-n-Fresh Breeze.svgIlocblob205-_:R-Strong Breeze.svgIlocblob206-u-High Wind, Near Gale.svgIlocblob207-Y'-Gale.svgIlocbloba208-pȘStrong Gale.svgIlocblob209-f-Storm.svgIlocblob;210-rr-Violent Storm.svgIlocblob211-Ә-Hurricane.svgIlocblob212-Sw-Tornado.svgIlocblob213-p^&fTropical Storm.svgIlocblob300-5-Shower Rain.svgIlocbloba301-_:5-Heavy Shower Rain.svgIlocblob302-5-Thundershower.svgIlocblob;x303-_:5-Heavy Thunderstorm.svgIlocblobx+304-5O4g Q-hailThundershower with hail.svgIlocblobx305-\-Light Rain.svgIlocblobx306-N--Moderate Rain.svgIlocblobxsvgIlocblob8501--Foggy.svgIlocblob8502->-Haze.svgIlocblob8503-bll-Sand.svgIlocbloba8504-mn\-Dust.svgIlocblob8507-l\f-Duststorm.svgIlocblob;508-_:l\f-Sandstorm.svgIlocblob509-mS-Dense fog.svgIlocblob510-_:mS-Strong fog.svgIlocblob511-N-^>-Moderate haze.svgIlocblob512-^>-Heavy haze.svgIlocbloba513-N%͗>-Severe haze.svgIlocblob514-Y'-Heavy fog.svgIlocblob;515-ry_:mS-Extra heavy fog.svgIlocblob 900-p-Hot.svgIlocblob901-Q-Cold.svgIlocblob999-g*w-Unknown.svgIlocblob0 E DSDB `8@ @ @212-Sw-Tornado.svgIlocblob213-p^&fTropical Storm.svgIlocblob300-5-Shower Rain.svgIlocbloba301-_:5-Heavy Shower Rain.svgIlocblob302-5-Thundershower.svgIlocblob;x303-_:5-Heavy Thunderstorm.svgIlocblobx+304-5O4g Q-hailThundershower with hail.svgIlocblobx305-\-Light Rain.svgIlocblobx306-N--Moderate Rain.svgIlocblobxukui-screensaver/src/screensaver/assets/weather-icon/200.svg0000664000175000017500000000072515172041035023015 0ustar fengfeng200-有风-Windyukui-screensaver/src/screensaver/assets/weather-icon/305.svg0000664000175000017500000000143315172041035023020 0ustar fengfeng305-小雨-Light Rainukui-screensaver/src/screensaver/assets/weather-icon/404.svg0000664000175000017500000000244715172041035023026 0ustar fengfeng404-雨夹雪-Sleetukui-screensaver/src/screensaver/assets/weather-icon/302.svg0000664000175000017500000000222715172041035023017 0ustar fengfeng302-雷阵雨-Thundershowerukui-screensaver/src/screensaver/assets/xue.png0000664000175000017500000140322115172041106020713 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATxyfY&o/RKeY%K#lCmffb0 $ $@$UJMe0Tf  #SdƲ Ʋ-m}o9o&@ZU.s>b~Ͳs&mfdݼΘ i'XBaM8 &,㌂KeJJ%EA$ iXK Y[cPƴRpJHU xm,e#0QVF?jtfLXnX_Zh!yFAHmӀs~a-0U0p6u@)ECF>I>8J_?5pX,V*V*[ý$KiZ Bc $@)%yrgo>"RH1(#?}L҄$I,4 AZC=0ƍ 8Ȳ ƘZk3ݝfGh]׃ B(PJqn@Ba;&,hhc9`! Z]4͠c1@]7G'ۻ4͠1ZkHSF)qag>_7vY*cU bm?37ieQڶ#9x1 m[p!Zh؝HBt]H7m)!RƟB+y8Lж-꺎 B80@0 5c}c ȡ]C?,J$F[Q gi-fsPP5R( ]!RwReY?qùVʍ#( 0,ː Bu /\ J)$@Q:2g׻ϚeYQbk:K#Pя/t4#c؅ãYkҿ#R;{ux-cU >~÷|ս ,p%}=! c EQDIgk45>я:b5s{X/Pof @1B:8::kkkZ1qk-!D۶h籷b H`lmۀ H)mٳ8}I8.6rp|,Rp!EQ@)u̳ # 2r/~@YJ!IRp^$~L D/phK:~_p^'oe}ZQg E\]wPn a**0̩`#/J3H12(O8J+8H! ?<4[o~ѷч|?}9VbU|S71i6ͅ, Ƅ{_cPj@;"{{{;@Z+t}fE /o bX'ire|SZ I2, סrkP8_4/ $T5X(C8*!8 !PTR?c ؍$`b$.YhܺȎIZyҘ_dhcPUqet] 51愋"6ק؜N\2zp5\4ixTccAݶm3c w !f|25x饗"r{DX Y!2,KPmȲ FkD](`e0۱\.qxxx!hٍ 37_2{3;[;hvwNM)&Kp|1ζ @h, \8 e(c'ek (FCJJa;œ;$<~8gE(mJJ9]"FAy2L#lmnxr}P,AH83j\xacz$M%zm]7@kAeQ2Xk{'<2xeMw?g}vbXw?ox/t<ϟ\HZ)pNIt~^֓/F)Fΐ; ּ1nWi[03YkIOo g1/b' 8  #DFOH J'`4X,!XR@IFC"mףܩ+mSUY !uSsc$q£#lllZA*P0pX@~p{";a@.QSL8PJ!I0HTR:!UUaXD'9ӴQVU5X3gئuN k`C1%I M7&I{'I0=ZQ?_U/+aiG>[x?xwYkOs2D[x/,Cg-26+>6ƀR B 4t{܏+BHLΝ/]Cvqb RJ@ VY2lnnz#ht:}P"}w(X$Ixg]2J>]C]A89) ]q<$q !ж(:hA5΁02~#Ȳ줝ߥi&"a<= kQ*Q8桇2^$M AYd8ɨxT@p(`ƆiQ!X-B wX h4"sƠSy*swz`~\*B U&?_yK0<4==s4l)(^H{Yu]/>h1AFo@323g *PmTLԍl6!pQ{R0 @n֯4|Ixtl:BP$IbœfBI8ߌ.;OABugBz]! Yn$"81(%=o$q$P500$KB\Ϝ9~g=iBD7HyxK^4u{KPJǜꦉ] Q~v<*qzg6ױ1Ee`a6Cy11 n& #*CB1PǗah.[uY=dw.c^~Į#h` (k_L$Iq]0P 9 Qh4+5pj w)JJ?ߢ,X,{ @h P dxϟёGA(""I1 5V m7`QNhΝ7\p rNC4k :X__|>ǩݝy 8g(vB25i8^RZWsQT?sۃOr.O?~|WzUW->zI\aO)V*mC}9ԭ0N=Q'Գ뇄=ƕR( eN9I801j>ݧ.'4qTs>M30ZXD4MA P\øc @`8G^n-2cҝǓ~pߎi\."h (Y#],MS In: Bu2?`4ϗCQɰ?> IDATgwwb=/sFZ#K<qh1NʙOs=b`[2% Àn c 7Y_SOaooeYF666y ?vӽoz.~cvCЫxŪ@XW=ޟ;yi_$AlYA[g"$3]s]ښ)e^j,?:IwF91C+|@k1A焠(rXc`Ac w`$"2#I*I$#a&)9TrץXHJ= T8m4NEO&`q#!$_5NYyލxя޸*bU /O>pÜx(tCZ() ǙoHlZ`\Ǹpjk[ڠ8jYJ;())%QHCء#sY4a@S7J$3)rrsu9~ߢkHlp_3|ƈUclygY P(X.x4 %y\N?>LCTkdYjTa$<Eb:!so.2ūE@y( <<$i5H@꓆I1꽙8 s~p3,I1N`-X,K"BáguaȄmעi;7\W͘,;$r5w]-gTJ;QCBfNdL HsYBɇ`a-s>wEQzEX:&c-Y'Ma8.0iih+Wbqbh4:`>Hm( , E7p(:P%I?m㈮"O8rgϞA;̀Jq\hՍ4.ȓ IB*k7އ{_qYg; 5h%ֆ3JuBw~[f?ϼjU|CĪ@Xk|~ I%wo,͠B1H%A=HAu0J)Ųiqeou[I!8,I^ 4h  00 N^{0469KwascO(*.l!)H?זRbzW{,ˠc!!DYh&F'KB0&?}{0qgȲ4at:VPxYJaY/!_#'!C[9 {G Q5n Wx[k"GB-IAʕ+g[&D811ɢlss3&pqL@eF$c`[|QUZM4'praUX kΔI@Neia( '(V0u-8c`B*跼Oose_?*Vw/>SaX{"MKFs7cT7Fr sI[;L w&gP`t8diub^רh3Q{99@ .`wpp[g54MP5- AΞّshoC\@fΜ9Ou`L ʲtLiEPj# P'&0葐-8HOhMۢ܈%CI銭h2 - Y(h),D^ qzXP1DeY2xY;t3}z¼HƍI{@BhȝrI2i\Rh>8i׉'IaEAg`c}7~\4L1qK EN1$)ѴCet2y}<~ͷy¬%V*b|=k6 ÝCRzD M0pMgqxx#, @2,  5H:X9l[X61 ~g 9=cFx=D mĵ!9PBݸ,&  uc(ukkhɀ|5ʛ"qQ7MGaCe Ze߻sl]g%?1e}78 $žЕ X zNqд-)av-ʢgNƗ,q+L&Gp+T{68Ft~bM)<*&ùuO~O~1:U _N Pc-ty5֧S p '7NEpb5wy7R`(x֩@ZAJpnƟ!Ilmm '`YĮs\"m`X/$ aOo yme%%[c-wIП{A:`(BFگ);0л  !R|*c=@(huZr]T(IX,qZzF#o|`l-hG*a 3pl4MLa|TU꺾n%ݵ ˡ转r{{ҝO^!ᚦiSvg( o"e[.\`ss3z88Yo,sϜ>8B[Ρ^W> >?Yt y|Ǐx\UnD00:p8ym`08nz>&i1FhkGZs=9@³]Esscqw/p}Xe gv0cӻyQ[bAFBpdY_2N6VU]υ@w?I9qӑfi +JwTq^s{{+w$ʺE #^hdl;ʓݠs` L028+i6^n6\r%w(:A^(t=@V(eH|uX%18t|!<=7(w!)؈!)ED 2?1(^)iO-#9Ό5 T|>T#MSŒ9X/37rqr,PJcM&Fj_T%I'B榳gqi\z n?ʳ݀*bU ;~.~ =7 RBÿ:-|QKh׵s tM E}x4sī@r=p{-n uS> *8؅ m[􃃿a@&DvA/#+:gs(`RGLI8 p\vH^2,Kg[E| !R+1?HBA(bR  ^r%PMx0d/$ӧO'7)u]@;L SbuZ+28|g~OU|-Ǫ@xw/~=l14qf;]acc#j4"Sn1Yjs{%i]Ɨ{/_Nh{'ycA;۠:G8׾͍M!͒imyLy*2d@qzJ^Qѭ=x8[zvI:6˺[]ִmD;]uľp $`J)"'a^5sLشi4akk $vځiȋH| 1ӧ \Vw*]׹-d:3,^y`_^ IG :gcf5ָuJ+|D>v1B4M޽su]S'nɧ!Șm;9pY/Po䄖㥗^;yֹp2;;Vp ,MX,A>ɒēm%`d`c}"s8sWڮC~>cW -@3zk!;y,p /Gk$1Vp΅ja .Hx uv<>t)kPKDwzu ʞX.W4@.{[cC_lU GUЧN0SX&zWp˟:$I۷o>$ } |6 W<2K\qD#p8. LS2ʂH(=R1*(U?̏h5b)?/W99G\`GY^P/ (B&_jBUTػb( h0\ 9eJt ,w}{gk]?u] Z+Ż}@{kM6Ɏś~63Uϛ+{ (Fu^pmāDYn1(E1,PT\Q oZM\|XSV'o Ňtl:8n`P PU%Q(<n MbH!~(]2zn+B0F!ߋ= k7RDHQ4~W>' n^ȕB``A-'E※mc ֽl0L0|sÇYa"??P~1=<~|3>bb0 DPxSn5i,wk+K#s ]K1[(W9yk-5pK'r DnLDHV%@ct$WG" ֯w|[~wg-+WJ2뻾 +BϝrHm9j%yI֠i鐙&kJ{CoF _,PְeTtnˇ2_pF']{w<0, ,+۲{i'3ܿs1`:,Aq0Zma8:  w}OrN\:0Dq4Ǎ9 c8m*8:OL(uCss lm"Mt}G#WM00$IrH ) 㧦i),H)Ý}kH Rfh2 #p*3?ϱ^!DQ(^ y-?bj~\(Dmez[yJYbgDL # ,ː?9 7ΏxCms#)GFan$He,\9g4. 3j0Lލ=Vg~u /?Co ˪'] 00!`(9I)Ѵ-0D wk ];v|s@]ۓRh", e3yG&ܼ7֓c4Mhdd B $E['wn09>y>!Q[Jhv,gU0>R`o#%:E89Y mɏ^yˎ`)>Zt=(BHI& 3ѐ))vÃ(V|0 Dv.H Z#|`8 _kn—.'?q1MI7Oqvv!7 >h8 KbMvdYPxxYb\ "O^5ɯhݑ(m[pp42rߋX6z[$pr0ǭs7Pd r#I]"yӵ1(rhsB rnaH}Q ! ":4R yp>lT`:& ȒsCz!VWRfkc!AZ^SjD<.*d9{4@BϜj7Zv^xfB&ElO#LL(#vRJL>|2?3oۖR-]& VؖhbU:=).Vءhi\.Q,02(EzQ+ Owܗljyb0 2?18*?9y"=?99Ǐy$4 Ehk?7{{m~o_^c]_;?<}{aM:o~ V+M#>KX>o"p~ B0" ;<[΍",ܢ hbg0Z !83 0`'D'r,&F5N dbX@u @iku@9KS7HJ(H@U^ɇBWcB%x`A)p„81ɽt6Ex?&1Vbe#E*34E?Rʀ6k9XaQ@ MgĿyKh}_ B )I mS|<C{ބRiJ݊6Ydewe^K~g@W82Lr Z{].x?p '@I 4ꪆ@Q Je&QltcgȒKQ55(Axc7%qV. bRGG\q$ww18f[b[nQUYHRAl2ma-<>r]B)Og<ffGqMdIA{߀4MK,IF[sͯ!7:oͼIB]>j)sw0 v a8,Y! 6۲n 37`Ġm[پX}#HfXk=ǃZCEhQ9?w/<|7|zt|wAr7LH(rv,jyh4b&109#J&0Dc';S@2;4jw3oda\sYU8e9eye3;TIQ@GY|XoţsʫXo!#pۦAGt)ab&9}^AzSb]U0'YvheUa]8z[bziv;p8Z899z`@2Le;E+oʤ`!Vkۖ}@9b$py(N rF3¤66[cNU7\ҳ#qbPfa4%e+XXdH^Gc|Y&}! `<a<4!vQy@P5zGb䃛QșUUM}j勪}Ec&G~G Mk Gv[@)xBl[]+zmyp^.׷~?H,WUᬂ Ь*wZm%\C}#,wx+ۄuyRb4yXߌ|(˸鎵d.otol+m$@W\5]zm-ܾs=+Ϡ|6CulҸ͞$&vpqz|+bFt !A$/xR5em]zuM(q/иpgŧikE!FMS&S1+ $Y5h"BOzc)cL'c'<~Z,Q~I;Ԙ(F!X#pJcB<~|j|xj8+a"s(prrBf$XU4.(pp0orG)Ꚋ}UU퍓Ɏ޽#$qژF}k,k}_ mG]G-59s=>$}H˽%"Fc4M+䷼kO7k /?Co=0 :r$?d8F'Cq|2>4 71ǣG]dcM1 cG wdk[Tu@@ Pk!ݻ /僔9#\0H nJ#cn3˯5Opv~,G' vn ;Î=2x\}'ys<~t__ RY]<@׸q c Ee@N7 ib>00xEO:1ʪڛeGGh;Km K3XK}Ug;X(RgּQjGPF!!.9nKm/g+\.VXob`' #P_:[ ;JfK)Rm!v@JJj(M1Lƞ!0ڡGB,K˿%ށ;l/Ҭu $sQICH{O<)͑mzvv4Z8??Ϲ9z6"@9[k>.nK0}wnh6ƃG; dzerB8U5|oFWL}Z;;m[^6Z`NS׵7g (ptt!Ey"> )(+UE>jHGa!r7ZYA/ yԑ^gE4Hw9N2k-I!-+4Mz)Cd؁ܓ6\ו$$+䃈_P}h"Qt2,KwD&(}$F!Vw|x1{OP !pHmb6"uA].M"Pg^oe<A-oyYA@ IDATш3ږ ۪]gfA$ފ<456c `0>^g<:aWҲzrDQPf ڶi5p!ddLZ+g}葖g0 C4u^)ZW-Yy¨[mx})waibQE1@FhuύǾpҽWSA`a~]{ymww]_w?i;0­7q~vN6R"MP65߻W_}oi!o:4:jI`SnMCeLB"`겏DHz/t&CI0w!ZO~tu/KAH*&&gkc2(ˊo\RJϽ0z7(qxxzCRr] I$QkQ mR BAАmבR 4>H)<'& `O<Ȕ]( w0 BQ40 |(@zF׷yAHKIR<|4"rP4 ň $BU:HסkR M(uz | SX@9J%xi!(G$|Cޑk5tOڍ!ʏhkZidiFˍr{~~ !%OȓC(S %6-yNhK)^go}H$1X:h4D^ j<0,ArIIit/K8<ltʲ$s 0}WFI˥;%F %rZA ^sr\ghZ 92Ͱ-KHIj56۶Ex39∜hqE$IQ(zM8n MGQB`ib4t2q.n Pa>'j&Z7`&\wHEgJh{i]#KS0:h΂L檡V#ݻ`8駟ɠk BL駟zryy }oyՐ7IZ.X7s}}1H "E9D=G#JBOX|팸WHҔп @zŠkr;F>{&y(th{#.$}owNȲZD!~Xm8p"fG)f TJ2N; R{~_g>:l돹 ?g?]ژ-Y B Rƣ!~*px@n=1@wj<3XzA6p(up0:w׾s@hih6&17npn+a{O<|6C)HƝf0n= [v.2c]Ǐ"!V;7zgGk!zoj6};$GNfE!&)%\ .XXvㅨ]qDR(5l-MIEhGd<ȷ_P6>3&( @?f= pΛ?9.J&HRJ,KG }rt%C˚4]X8<*s!RX1RWȒi@ܖIW!]cV+L&([ߊ LM2?|ͣ(B۴I8ڶAFE(N"0k5&[azyt]|{y@FVD9`Y}B]!AQ1 }ߡiɚkZSEYU3 __xM7^Ÿ۷oˋ XM]q]!%q$IQ Xn}(${as,M%阓8FQ +۶E] XZhi)V !S\6ƢzJtfIjyT2~r8޲vF5Z%0Ź'NGC7WU_|'j[ {M+057P4HeY!MU%4C LSkGP<3l"(f O^), O2pR]C@%]2I՛m9>O=^yAp0۷o{!:;̻+ 1C縼D~l&Mv^lZ+>d-eYbِU:Tlgd=2vDPFP%FGGgw]ˋ eN:sS7]amA 4pz4x8@P乐|f UJޠbYbxNr/eeUK2uKϛ-䕡İ O-MH:-)b<$rlӵ_?7hk^{@x'?IOy;ڰ%Ql\.} ~ZǛ mMJvpToڶ|;c* , Dx{g?PdiN[_,`. 0X,BP}c cy)vPsI66w2 }M` 'x!rgi޶ O>~ӟ|.oښSZrsrh4zqz88<` ǖ _ y;[e M""c!!jd$͙%- \0NsEQ@)JKqj,Q3`:<4)) !E!A3Ign}X&:zMbeC{V wnƫ F@&88$  S;CE1<> Ri"rdYNk]c Cn"bDv{Jwl $w'''4"ofq!0t<th@2u)UC7ZQ6֍:̸lQj$)ggg2 ^lF=6&ޫsx2bxꩧCd253NX1W UCIm$|I\\\/r Л%)"Lb*`/pQwttzcIT=FE,ː C%d>>Nbl[byoyG~~`;^u] A;| 4}^J #"a(1RJ9>"u,e& byD:=K)Q }sMq^׺($'ƺ} $&I:M:,tp0GUOdO}Fd`2[7q1˩XY> HC< ]â@ۑo;G{s!Њq,Rx;*Ckf[`6Çk,c0#zd*n!Q⒂wF0F<38usU &gBE[9 !|@QGYGx\- m;3$ƽۧsAu~qዐ@JqSШkߠij(mab!UvCc?ch]sUU{Bis zmx/W`rIW|H)qzzzG eYݻB`\Z8&㉚|ϯ)ܹsM CdIp@b9qeS<b0t HArFe7F$LX.W(KȏzItؖ5^yUXKIcp.V?ڿ_x٩.ހoWZ} QA2d:t:Za86mAH ip8Mɢ@z@z])AQPQU`<S< =χapǰ߇o۶hl|blKl*,Gxx;Ѷpp8z1o~}$)A( D޸PhJ锤lmIѶ-Swdy滭>FR;MF!!LQ5|`$P}}f ር.Pܰvj^X9 ugq6ќ1}][9tÇX.W8 SFyb٠idDZ˲ #W !0]fy6ltz|Aa17,W[|WW@hZXM))9{uvso (={-TJc" 9lÞ;amYc[jWHȍ0&)RcX|?_u] /Z0K'^/:ef kqr|L' 3r#$1"+qqѰڠW iU]#S߱*kq⾜Ɠy uIgꔜ.;988B)7NXD >c*}Z)I|+qqpꦅHq̊( )h ³[7t:A-&)CU7B!RaJ&ө310zWDxnZihMyRПK X8ӨAZSč#iJA|$ `!E}.+"G#XK!5dD7oi@%#L'SRV(mUA3_zU\,ιZ;x|]ж  iB0SOa=SHL& _|.:)`92(N-+ XװBzi!A0[Bwкwj&E"@쌩C~%$( m:=B>ж ޹ qベňxGh:DB n\l4ZPDh<{:;@RB׶X-WBz6;E8A_C F$ tI*0(r2 FD-7f'\ZUU! BQuzɇU\6OsU¥]A`%6cڡcKYb?g7@Fl T2Lҫ8JUvkۇ)*|6CX.hSA+_| _"t M,WK '#^mt'vϜnnvye$]N9Qtհ$azCf:ș3'[,N8>>bDEW gKyuSq?Q(RY#uZwm"):\x«69uKH(q(<,/$0 $[w=fw¦\۪FIySL&c_ Ch`|>Glx~F& `hg`6 ,$F bTuQUUd`H9!IyN! IwHK|qhhYu}8KV6sYsΟ9=ſ;23KT^XPV"TqK2<񎯾~ ؿuO~?-?$s]_q8?3 b(fDR0J{^ nZcY:ynKy{G<~)6c? k;7pSH!ZoQM"Ks}eY.s?i[ܟ1hLXieUG`p4bDxm%`1(l6z+#'Cqę(ꊪ搷._ I"KSUE2$EsmȟZ{bSni"3r}]׀ oy5Zڮ5\?6Si}#w$=sFJ///ѴQG-4KuY  Ө]{(UkE~7G=ێL~d+hۺjpjaA$>G1hwp=ܺu9 u" Ct;$I4MP[R<0F#3,\bqAJil-ǁXVC'?tN$udϫ<+-)'G-=tRͮǜ9@ku`c4Ҕ:4:︷--aASCJy%/.` 2pё?L9^w#Yhj7GKz^TU rզBQtu0L8WѪ!M4`uƥtJo9c$Z96 tN'''D"c /}(Ȣ:mRXIDϋL- -v6$% aRB<~tqrtqWCa-prSr8I18N9 ʰGH#)"'I,K_Y \!ufJtm!r;0Yen sb<bPf11WIo ═\(x<~NӔ'8><^yisn+ۓRhjW(壧;.}GD] {dpES|rGENâ2v ChpvvFNYy. vZ, ?B>칻LelT~~86֮ t0$AʳyA 2Z; =qȳ< k A MzYm8:<Ķ:k(t.RR\c8ܵ[{dLC2s)9WNIe9815)]0Bxv< ꠕp8$j\\\"4M1ϱZwŸ̗9 Xh*{B uM$1Q7 2JR]sT{9DGw9S2>lAr{JR OFn{ ű 6cdb0 2*b5wPK(|G~w_} /u] w@ ŧƣq]789>Fdx^QmInoggWFMKs`X2n`{۶)NON{DkcE]U?_箋w 1:B&ˮk4m"d(JE" 34] 聑" xCf4;<~ GҜ}aݲwF)޽( # &{x8Y OCShLC:LJkԚR^Uc2{*J$ I"}"()r'$ ;N:䈣xa3OUQC#KƹK@x]yz6C ˻HǍR|Z%, 1V тz>!M( H)]T3,vt_ XKqVir5L{ȳe04B@%,3'$@UU^$b0`-k(כޟ е?0(8VEm4(SXE^Gż(yi0`m|؈Jsឆ4I% BȲG0 MH$ c8w 2I= =)6e,|hu͆#o4۾=.ȅ!n $ǥ{2{2\,-vђڄaqttϟ_;? Ͽ|>K/]S*EC Wt ,ztt'=\HH@RHCeE8ylyrp6&A4qLHUWeN( }#!΄wIBfa4Q++pkm0Nhmw{ly-9>β gg"za6**0ټ&+qEŪ{] }7ĿW38iN3\:4m S٬}JZ|ObzEMJ@Q49o=,گ˟KvKr(R8?$Rd!9&_6[EΨt]w{gIж G׵qIe&OJ4ϏqΩ1BU9DG /"+c3RW2|3^c'Lq3l?w_ۿ3] __vL&hJ1"-12578. Vq2:Ƞ.N(&';.JBDX5` 9g۲Bzi* U)*-=84?ZD%G]HxNrRسo>i^UU;y(NQw.(pyʫjOӄav qP(ʒs~!-Kn~,03(aax gk>@9 iJgxp!}]8 3^Z[C01?@j],5wj<@ud;L[k##Shji`]CGj(ڜr*Wpzju<ž!Xz36Z!,1ȳ,___ǕC(a'2F?L@e'4"*di#7'[AGxoC_H7APW">KȋdE#xL;e$AMc &8!Ϩ1Q΍"H˘&{gV!?s`FZ&q)~eYbYgV8HEE蘀O9Ύ65?~ q ..?'qeQ°A8!.s88ӂdM._#{mvw**G&'Ĩ;HLi~XAUեɖ]G$@$HM!/ I^XJ{XgȺEHo쉜l\.hcss$uDKB=}oT ӝf ˊMҿ<TD';PAe)D^h=fq$9e&*Ipt|LlLv~OfNFzNظYY%# 8beQ1B 5{3>u]NHAOoqPXTvMHĺ r9U7$5ZyÇXLu `Lɢs$Gݨ $RC̗yx/}63 A)49ʢ'Oбg ( C,i9o`L"cڻ {3 hυ @S]Y xg]]:\2ap1u>}4M0 @1Т5O44Eo"G!NgZkr ]}dYh)P\XW-fI'M|5ڶ!f!Σho P JKuwR|潏M=9J s 65Prږ者dH! #H@Gb2e`^9,kvaTMA1$OS}GCU9|*0ה9;o!Qv5 glZOSe=M4B}}rrUooB:@`kޒ >c^Ĝ8_i 3v, j,fOJCDJɽ %aX; vF$"32>?;Aj jDY1ZrIȆLm0&E IBe{ 2.xəx8H2!*D!JFݢL:&Z&0@^RNgJ,Kv;KA$Mb,4U ;MԜ84K1g0++z:i'Y|6~o>@^_w~"£EF4fO;M;fnwC`ZxʿQ&;8MXhI0NmFә4MqyA Yc%+<4yvD 7>y"ˉo;]$i'W('l58;;'ޢz}i)̀g麮5YfKt4MxcJ+KdYrp{{8`1rAQ8 b5MTD> Ibr@υ#r0;QC@ֹקI gih)Ey=І mHEv,racDV9;yIGRR g,C6TS' I .C0QE[UUPp(˂I4ZA@Ͳ>;ohjC?b䕖nX̖H,1 y25,64E&3&%>uF%?=p`k{xy5I{b eL ;%s &3 @P0`VLdLb(Z꺢XRt"DvG2if] x*1#Ϣd%4 ,MH evMTi=Di|ӟ_/>7S7ן3釁ٕ IDATEN3vBP@JĨW_}]; e" ovb(C9a)!BB~N>|zNy<ڮpR@YVwl=P` 5 <*^CӴQ%0$I~b"L?_qR\V+3;tqe#M88!eSma A)!ItܵS~H.%yCߣ3tO2DȐ WH @0w|Xb\{y,XLiObq1. 9k.{nG"Ցuo@Us i_*OeUB=!!{L>amB ]__a\0PcpA3cLR?A!&9{rcȤ3 ,ý{vP:*K3$!I6u r̙Xo֠o5g8ʛTCTNpzj#\pqzFJ%\$=ƾַk d 1L,)^y^~O=e{7܋eQW_3Ņtu 4)=4>5%4NkȊM,Z+N,nYH䨷X̱d@dgmi& uyF~xjNbbg,ٛb (2G)Q57=ÁIXEH0(Qt Èa馮k4MX v2qK;wȋ:x1M0 5rqn~p.ec>S03k.X,cHȧY F4eY0xH }.ΓH!VYFN޽" Ib)B $#/}?F)Md,U,` 5fE׶55m|q*^kŞRyV1" D*}IFQBMPȑ窪YB+DΊ 7 "HŬ{8Yqzr pc5&xD{1 @JXפ)]ȩ($3 bLVt?zp^=b>'0 xXцJjyčgBs4~ٓDV,OMAef;:<:\,V5v㾁$gzǘ;(-Ib%{0SDevy48994q?ٟ_OAM?֏+eڬ,>K-j0YvkI'O# D(gyXDRόQN&)GqtiN9l,,ReMXtAz-%dyqK>prr<2_9ƅ{!*!%AƉ>뫧wAn,ˑyYeg51h?M '8;?n )HdTb(,(JDT 1 3ѯUU&Ӿ4KSM#~0'bD+CxO'\=yaw qBHjiKk<#B8 H}FSR&בm/Í-8kI#jioꬅe"n]d2?fF١hF́ 51c$|ICjODZkTu%Ѻi[g3 İ[veAiz(}pKF&z2#^r~LѤeXBo1_@/l"΢Dczf&.ٌ/,uLjv#II~@f 2Q ./P)U. EQd:ՁIQ Y0h4 ?=o>+UUEUh1#чbx|^cr8B[G;w'x4R!z2S aCɍe@EhE y=1>DO!{V GYP&~!(J.L"z PK#uDZ`YJ\,BĔI$>-r \~tRV&nXdO41ް)3' .D@""KšL?˶̈́$x}v^-w Bxyph?7A|=o'h# "MaF@d'Xg1y^-A&z v\=|@i"X !m*e-88g~l\i: B+ ;lhVpE9h|(b'w|E0-o&"ںh\0nKBzinʨ1M^Goprvfb,0+K ŜP`]c,xuKƂsA7D ?38eY*& ''b1 9) C`54UJ8==}_!7Bw 777qo_"~~*ʷyOg5g$t`F:_;5 {1H/&L(H?9T\h0X`|"^EtH4CQ.ZJl*fA)[7{Ndf8iL@>}R iFƑS*.*^3 ј$Ԟ I3|ɳmr16BLGu8=-2NeY0""҈9.Ч|o"M@ yV@́Ho&(b{옳,D*KٞZc{_]N":FZ'4mK4Y;VȾY)r ELҴ'm18??~I"2 m۠(*t}ϊG++cpZtݷXLL4<Bp'J%v|bAk, iv]ȠGYx\\c1[ ăY,0 g35ylgD|qđm?b'Ң9Zr4\EN9.// k8("qp<<^z'y-]7G:;Pzzz k-nv8DTP$Ϻ9g=# }G9, rjYyq!Vk&1r^ANӄfOO\0ŜI69d㧿߻} ?t1֤̾Sc`kpu4X- z9w#4C5I_n [$iS&fo+Ŏ!Df]Us6Yt#DXhL# FkUMKli((C4K0<؈e64{.ܴS?yQb@3ARL=9Fg;HH' YF0M"oĪ&nR9BH>FFb\F$D,~WGKg'3+ExFJk"ZWsUA$#۶Lyl)a#cUU4x*" gg/2v}EeQyը uYSro=9tÀaЏM?`١Flw-~ 20 @h;:Dt~d5Kie{0ϱn!6 ]oGڶA&wNyɧEG4RG# ]9̍EQ e >`YfOM48e2h@(ÐxA,J!&r:{, z(r"?g~d~_6,_:&)^WK ‘oC d1ZѴ=nitֻ=q);f1+)FdSX$=|777񸾾0P&~h;g:Jjx? J)$|vK6ɜ|9::K"icDB \l\䈼9:yK}߳A+*$am+2^'8Tuا>ꗾ]/F][oN>,d<@[ E>wQ\F^ M=?,I+`6砕w~w`Z]#p@Ս1PZG3=5֛5tYu.Sv2 9IN>bN[&C") OO>EV^Pk PZ#MQ9J|&{0Fʳ ɀP=VxWypy8==!R22,ˡtBU tY Ό͋I!8 d<l'&3dkĆĔ$I<3{vHb٬f$iIb%J2X*M/6c1B6X1Ĭ IDATľƳ*k8e%IqvvvneY|!bB7 jaNJĮW lUVXV|dԨ\ w5}yl6[v{C}CXh+dqu}aصMͮzQ@1(fsF:O\5M}aQ(+<&'M3<}z%8BENQe z3g&eYu4I+4m ]u@6Oi>pqH8`'r x!6F$q* (1=;I*wX- YRODLh" ,}2"Gkv˰&@}߳ΜLZqp)5% ߄"a\r<gMl@%f~k-f /\]sy':^~h8ϰ&&_&6\''0ɝiwd\.# ҃{xt]סJ!2dI&;vvfZK9 ɏ22o[!(jE4)32|om>;je{u`5M7~ ba8`YbY\hxa/pbtX׷,lvT䐈xd9,WUX0Hdy$!ha,֛MDlqh0t=OYEO$&c?=w]$xWWWHL_| #g!{pكOz*6 ڄ6pLN"+&%#6@1'ӕGVK2 8#a\_ݢsB`Ը6 ֆ9QGz#\]99H0 &DOAG,,,M1` ߮km$8E(f9!(sh#8yW_{I_:4c>6>j>0Ľzm;&I& ,.NO PWz9O*'9()tJxWdly(x,ݛ.Zqfӫ /w(`aB((GrRh QR#xV È2/I,rڇ<͚}7fvݷط=&\]K cprvc Rԋ9秧X74~ vnvu=C#98yɂ_KR} O;ĤP*Rz (͌ 09daotd朥n4͑E^"Iy^|pZy̘M&OiDjx:PVa)t5I&!M,+j4n5I2r4m zu#4AfRWe7AA MG$Kb : `.3dڒI#e@ v&9RȤ(N&Xeܫuo1JI"K%P0X-_pdلig)PI݄DkL֡_ ~~ 'q|>fKC?pP3ReLb@]t\$:>z$IIg4khֲAucVHF$FӒY5S3ց@ݝp4MY1X+3l=3i׼Z-(ZBFP*pzv͆b'Gh#KqER'j=]s ꉰI$iz} vwR ]N @?LCht@|D$k;Z/Ǥ膉3(^B]^^F. (V!?QzjF)A SH/G9ʲ|>nא2QEjj\F4Y$iF~ nRy HDIJϠbYƀ A!M *X&e :qjbBe dp ?/_-lo~49֊&qC#_ ˲pzF`$ QIK^JmX.z(t792O16* wÞ&&"́U9Њvk\ȹ iC Rbf3K1i4K /'$ xƐyO$G˞Z:rYHs8sxEa#sZ#9$a@IJib;4 e+\\\̊<s8l(؅A0db 0 w͎Yַk8#bCӏ( Jڶ '.WnkPGB֔ȼB$IAˬ4P54E) u=Y!Yo/Į4M1prFH6xwe9iMz,J37k]RÚRpy2<|O;?¾/O~wm/H=kſ^.jKD#2O 'b29-_c CsiE0џ2"1=y±c}zBQl^#yo@a8¡29|B'C$+?nn2?C&SJo{bv `H4MVaDJC<%<`Ë/?蟉AD6eU.iaCU'MB3ii9CjJP@QrDk,a'ŋIJg)JZuP'T2dR:HG:wdNI+\?W[ujrl)LIXV{GVذwG{˅-8QzTV_p,$KHrt"[/ I YNȊڟ{9 ynJDdj oBeU5Oh8@.˂,PEӬ0E|Q1DKkq r A{-./Ѷ}JK$kJ >Ni4LhE K21Q)γB2%=wwwi K ž͊l/ &kI1Y{B{A$RsuObj}_?^_/_dTb%s$FgǪ*m1 v1y(N٤Fxu@h е}*Jgż,$<(E (mS YFZa0J@i+EdLxFFQapff8 XH|z8+4/p0;y. }2Z1ޢ(سd'ceC,~k}8)NQq, ?OY{X t.Ә4E'teW׿@8#`~dBHf]XשˋyFCc,acH`ɬDR 7$O 5|.//1#|#" Q@(IH;\喵v<I>^˒F(,gی=j3,~v%E/E2r$49tB|PќYND#<./ie4֫noopl;@E9v eı~Y}|Ɠ'Oؼ) 4FhGy^}d`u$ :?(< >usi/!vH@)d]K|h,X侤,>)b2p}]'EnE^ 8{}??~/˲@?Jws&IѴ&ҢQߟ9EjcPVi&(4MPU~4MY`uq󫴆2\|w^a%{]|3wLvsx1;ǶfA]{3 kYcўMT<`L6kF{<>}nH\OOy~js1;3p|l6}/˪@J#"J\شg&P%4i2Ouc5hSrB.NxqqAim㌾4(LDi["70N(\AUQ(z ZUQk[33P83uX(~"wiQ%vOׁ>.u"$QL2cs %<2# lQVu*qv9d^nFӬΙurGp%ei+ *Axi T4X5+#rܲ\epF9 H{eIs9"Ҝ (΄25>O̫M,zikJ&b"OKJQQqYaY7>@^7vchbxh3F`^h̍y1a(KҶgYbћ'|%a2 EEiV֫ W3C0ƩCY_ObȓD ** ~Y%q%gUɺ] F rT S@96?20OvUIϯB@0N4f۶Z5llV9ijl#FD(~̀0rg>fO`f͜! vsBHqJ 0Ƥz+,>$Ib' c $Uk꛿_^b>l ~BRJ%"˞6bNKkZU /{OssY0HP6 n1{b_{?3LKd+,ŀ\T1E`3DeIP%YyF]@,ٶXl+Y^G*@N ɄiV³ֲlfeSqY8rAs xgw;dj|.a,+"b Bڨksht.ݝ⸅Y.Q4ͳtGUY>$9#ڂzV֏b<([kEϗCYV +RI}8b&<@XUCwv#:fHI\tHU k5 z} ysR|"^"NWp~'~Rx|OS?y…bM3~尦"f `&(gV8,ٓ=In$A!L."/1=Y-'u>")sϗnZ etf+.,u3lD&$eY D] ":6 䞖 NdL&hcnq8I: cj7_m =٬}O\ ~6(SkUkm-Z3_ Qd9HAz597 &{yP-%W]?*f31.p rU%_ *bn`ГDO>dҞwQf&SIdM :qix:׌&ߊ!p|{S3ȩ1bnI㊈Ӹ@w{v}_OϘYeг w`C[oe犆6"JcmHpb#{IcyfYQ 0{ gaa *\Zh{.ȬlW yQ$=0tFSH1 N՚4Y'E)rشe)^>PrH / Hax"1]9 jj(DBϬս{h3kx o{z}~Y*K[61H$0}kdɏV7:FN[6uPJS7j_N34؝#ѤFidFT_9R3՜4'AAhCi6(.qHffH<(2xO4$CiN i`dk "9A&j]U"Ug-^?4 Ja=|lwX 8(7߄#r'ªdv41F##FniFsZ's]?Ƅ 92.nWb>$m){&b/\qx\0@LET4`UBdDF!+% 'c$7,8blCE!*bPX;lV5ʢF7P/&T.$e.!!=(ղm;d.j4yY~>_^Ra茾K*܈D!Ŝ@n|Fy-\\\BkpG>w}:Xm=yFT#>яpesPIH`HR75"Mn˲TOw=!'p1jF|Z>d̉-ɖaEJS!<êS(ɢ,*X1d~EYi, γ>x;rVUU]e-,|nxLyǒɂѵzigdg%)4MX bIQ^;UۓDmiMe#1eVUa矌ʢXگ<503KG*k8[ʼXd;-io_'uMW Ur}"g%ȁ#)0)H F 3K2ιb7MS'(gSXrjE^ E q9eN?DZmq8`R!?{aoB.,bv:0z1  ;{H=UU] o&,(O<0 3P1hA{<b`]"Ax6[>$HJ$2gU})2ʢD^Vq-F2Wubf4)/n9e Aw  I 4R/\d`4`d:O3BF9OyQ51uBf߹>AG1D/FFr!>"&:ZjdΦKc4?zțt ^|w[xO6(Âq"v<JY\]]so/_< 2~-'>ebd\Pљ SιHM5lў&r%2G2cʬxPA$SKmWǿ?&tIJ Qkta6uns#}05۲y'R9_n4!Dvna83iFjdz@1Urd,cvX&+hۖLf?zlCE21KX6JvMgSDxة}e!ҬJbiN0WeTMQP\njؤbQ1=eFɽhsk8PW~{}%]WWe9zpFWQڶG ÈQX8H@E}8w=;"L1F]l M|,r(L#(mc0΁׋J(N"wNAh#%ҭ{1#HxUS~VgORF?C\7Zض|8L dIiT MyΞB?gG+Ή9Kr~pssFV%2^s,iBD|>*1IMd7 eU%^OK\JZgv2t;Gi]Jsa##eI6L |yf7>hc%g0XAo_g~㽼U~H@@)IE%C;G/"X z<9m|U0IS۲x8dNKXI bfuΉwk+Npbd9ji?Uhڮkh% PÃ/t(K@k@AP7#YVxl uQ -qBp6C`ћg9MYѨ`^ey˫+t]y~V+h"s ]KabP1 pkvmَ&4O)Y!R؉b:hGv ޽{ȓ\K." m##(c,o0LkIa 1=ܻf^z77HqZir.G# 4 BIu]Ǥ_fEb':d( "ݑzcZ^@Rc (q(GisvMZ+gȸ˲`8Ln m1,*cR8 X0OSW> #ʲ@QgȪe?sI()bR-i`Hk< xfNŲfaQaYB`ri{:[ Z4 }\^^&MkpK^$B65GI #֔YpvFY Oů|NƘ+ATe(䓱^9 .;GGl;qT8PjI/[,Kz!<8j$p" zDRåǔMӤBYRITz?JͪaPJŽJM..]{ܿp}}YQbEr8KxÇ# ͚ѿL?dJ8˂;ǿy?w¿?ie6mZ4NYLW$HJ`K!H/[UU@TWUU8d?uңl<8CLh|3X  :X葺$@o&"GoǶ4ByFTLuUY`:6Z 947wiDP2Wl#?A*I(J<7 f&i%'⦳mQ2nW^z $J1?x=?8DswDPԚ} ^Oг@B>R C q3/D3Fo}Hd}R@/<ߝ=$^ͼ6d^3#0HZhVfΞ8G]yc#Nkݬ1h8nP IDAT-Rr0z9c2dڲxl6[l[@ yF)L\XęDX FHYp2N! TfDY53*} ˋ-v-ƙ>QsLE13"z֛~0 >(YJ$"or:l$gY㑾&'KFxx$5%TO42S$ԼO (ͪ#Go9RH#Q08K@v8Kܐ|Y ޳zJy@7x'&K{Mp(߄HAk ,DWя2{=e.@4P۾{__oo/}U 3Z=bRC1 T\Dr&xP'3eYRaxFQܹ&3Y- }2 ."R&:І"3s $r&@E2Ox7?v$׌3BbPnooeƾPD|8<+ o ʢu DL {5Y9 !&5'Czf4 ͇~,ԶN+d*ribW>EerdSLd<#]L4L6jiF(+&쀮N _7 x_i?NH\ʀ‡0}fK*pYlZUIgMNf$9]*u<+ pUikP9Ϳ ɤ-R2d+AyD1ʕҘ= 9)9`QMaBR'n8 Ƞbǡ*\l7Pѣ2h (Dl.2f` c Vh #_ 77hZmkl!VU*qr2eY1&'y1fHo̹4kP>@vz"9}-L23CI]$z,Y9a$ْKmG!XՊ9ͤ/I,gg61?N p7>uBsy<yuEa٠, 3IzVez8KL/=MyAle8dliNDQb i$(JK'{8"b6yydG_(* q,ȼ=p8'r1)5p.onB=7_Zk%5L4F,8&}B\oeYḦ|KD 0pFaUӡ]0w/ QU%-ǤٷZcz,p1e<֠ t},f m4BҧȿA.ܮP >BP5$Yp.'0!rwd ݾK?k^CmwǤ9%CVY@ MfvC ТǎID(&COIjΘ!fK1Rc%z6Gn1MY.+ZU,F{QTiY<^{5ڇux @d3 Jy&tm Rk񣖅R,O/ƈ|#h9v;{TU#>ǧ{_¿',aoL^bi m! drVO&1'YwI!9 IfىaL{ S|^CߏɎ2=3>8X i]NXXq΁_<8)NXi9HǪə9@举~q$T$hEjh ΉV'@BYxC u\bQ山KuUL"fJ:KOC@,">{N^x ]ʼ,xT$8EɐtED(RQyyc+c's7 CZ\\\r@2OfaQ =n{ Rfo-1-y۳ҞAY4Ӝ: i a(ͨ(pcTM)0Fȑ9?u_B?O-VU Ĉf ?Os&P6U:ıT¸3#WQS?,(R R ή,'@Zor^e)8.5< <Ͻu9"F̋O62uYbz˄O!1 ivUqb"s #e~ qB)p82rq|߫U!E1 u>* /p颥HPʂmÑd86UI4R,+Rl/M{^ %a`ݒ6x⒀0\e]}\[%;*e!`'TMEC?bsZr4>k{H錷8~aM {M\{*@o9ݎUKj CfÜz#b@(3wK$R?A#/֫Q1ͪJ:k^հZ*, ?2~yAlY5ʢD۵\!-8MxN6?E IĤ&8\БXz ݃U#:YGFe؂nksJԕұiSSFEppL{ג8k?Nc'~D WWxw}!s\c$jV/oOz'i= ̳}&/=sq I.!꘩?8i*B$siLTlЦ'`1Gq"z1QlJUTY;F,έTծ:ߌ榫Uf܍RX q,NmYܣ%2GiFDI{Y'IdG\ CwDf52C]d˒ c(M1]M>1TOl~Ʉ!!b#'ڎ£aCiLQ9_r>7k?q{8%f ۴D,2 ҚqHbf5' d`N|%&Yɚ0W( ]9TumHkAZ@a "5yz>0}xxa5)%9n)|Իi_@fAӼZhmѶ-v6yFw9:0~ ?NO{eYRg)Uܹt%rxF=RCHKɊt^'f+ H KL^8# B,lu}BOBM"h{VG 3Kj+iҊH6, 䙅? NBL,(OA@bt8#| ⒄e.\x$QYJRZrnݠ y%Zk ӄ}wnp8vy*RI̬ tmxt@>hcRe_C4 =q"TsϤ&fmEF $r+ΰ'ϱ&FƑLnV S\5a81#7R ,<[45J 94$_<Azʒ?www:"&HySy]Wc0e]‹/߀6TU/Pg"Fb~nqrNFUd(2ߩ4њR7jwŶ9ά5E#wEPfEqNLseIB@h Jg>/Ȳ,s ᢻ{(S%g26Q8[yhZGrqjL@iZ E#Y/>9WN̵Nj3ڶ8K]Xx'XN#:i\\^b >D\޻B!Fd,8/ k -~?=iyFOzs {,cbȗ/iXKd=ydY0~(zXf{煅},]\\rE  QF#TX*C*Mqm s+6e$tGs:Ec ڶņ-f#r G\)x,9hѩ pXBsS.oJ)bTBjP+(<[a+}mS@3bI$D'(z&uUi~Ѷ-IH\kYE**\31"Am]I Ӝ:THHB[cpTfYb86gs]1l.&"> 5j"1*d)Elml5f!.<'Z$Ky&+mgᰛ /7t΃;֭vn(A&Ɠb%1qDFBBBHd!+0"@cC~Ɖ5|ߙqOXksJHWݺU9ֳa"_0 X̭h#v[/Y/ZAKPf!C'[_StdxEU9( yկ&>/qLj CYVΧFB.QgVWWƓiwN׮c1:Q#GN&XB$V<ϱZW!$qF:!R$ڴ܄'Dh>%Ϟ{ `Iih!(.") d-JF|.a)z*|*iYr=(ȷ?GGǞ8hVHdi3Qk"§{U/kAο?SՕR M<ȍ P<)y1@bk!18tiߗ"L.y 9|%V:O#xwl:Kˮj2ݸBT8j"Lb6;A(y_.D+in=йM8i1-2LoʪB{<,h)0IM, +98L~D }@@Z+ZBEI2is3#`[!+!4NAC?i%Ri"~A<3\\.pyyzOi /cK0 gbcBXN)":5TGu^Oq"){~0qJ Szrj.`J_H>DYXȈIg`'ZJ+xU3ZR;qH_yQpup!V \Bpx ŧ>8w(2boVh4IfN}!c3LoPde `.eYrN5}?`5_`>/_BYp!`;&T^r s,ǂ^%}+Hl‘dJ1].'OI 2wwwt,5y[o펈h ysP}a\+&g{(M$%rSTDrĔ[p#0C@+rI2FncdDŽ+Gʲd>x|?$Ky]|i IDAT~ 5 '?(Pr~=ߤ&' ("^ n>+ '?9՝ -Y^_ L1X,Iwl-ev;w.%63">N_50ZMeRe&O O !OҼfw9M4]K&/Zk+/T!AKT5lԌK- #q¡%kĤk 37E p)Fxj]A3d?}!zXg^CIk$ ;ct'߉#2dq>0N/ r |P1ϓzb9, !BRAv6u(?dkÌS<F$JHrn.Q9)"p~=~ Ky\ J='4)KBDY#IX ib1\]]X;,(M, fsܽ~IJE*CS7Xf$\CP~t8VeM'>Y&tͻpgy}책#w$i9le"2񤇵kTI%T)0{#,xb#lIe'>{JAs7| M&O1BcbDE,* tgZcIj'8OUO zsڙ@ۀJ~ IN{Ih0a6UUujti^xFe!Aɦ?N#v@Q'*HY {7<?~'a{iDx|=uE[vbgI!V8#qyu59v:gQnI8di~Α$XFudE\ɳ$#v>nB=4`EHhܣT hFhF]*<kP ޖdxǶv,Q)UJ'/ਗ਼?YO8='t۶"Ja 4 t,꺮;"R$%&n`]0Mp2.4% QJL'a kPUFBJ!49E[-2H9vy`%zrDגLUV6&*:BbF| V LvXH\&NᎲ>Ȁj0X4n.Y-P=BQwrk `᳚/zyQ*v;^ FxGR=αz` 0G׷i=f2hEa={uE W$Αd7=bs]WMNDT1! Z2M6)x!'Lf(YiqRP;eLzdxAZdZ`[jM`XhN$Ů1'dh*xUg;r,3,ѶbM4,+7dQic'A@nkjtYK nx$3`Z Vp*ru, G(dՉMC8MnBYT~3_p2~2(:4 m{|C2"d+ ` õ p1AZH{o__*Ln(*e$D).s{")Uw2#m:OA ~Q U=he;ܑzw#B%K\ajE %ȊLȂe\|nm ,d,hChePL3f!`jFw9i@x b'cc Q)>f31 $}q9xK{XVN4͌ "OFQyg|MFr kV+8g\+ƈbXJ-ZtPλ8pAKzjG>8C4ҼqIm4 Fbx@f .//0N65hնiY PvhM@<y`FtÈWw[| `%#o~q/7C)L !b!#rGa,GHk<~xqea&~.! bQ۵-A|hWeA09Zb XVhlYacRsHֱ=)T"sDRN&إdVkt"D?C?Fg(YbF~L&9FYV'@h]?p05 yP #{uT1Yߧ\i.}^aߑ.vGZ #ʲ>8x'4uyS#7|Ț=5"~@ۏ+,b9B72pΦq4FC+)f9Bb ]ϓg4rn)l5 E"Yi?F=A|/.7+^|6)yȝ7W(91G]!񎨰R8&m|AC߿H~S I cIe9?ZkLØ4<e`Dg5sY2۲?IÀnw_~7\y}M4?㟽1t}IyLsB*ɣ@p!蕼(28Kwx=qNL$x'=LPycdC]j]װgz`xbJzϻO{/+A8*ژtH!™p8.qn9"]G# 6!( /D\^\PGQcL4QcjY T͊lG#O~gHۖ9#no Y}Q<#<$ P,8 Dpb%#7ow|$% ;sAe! 'c%|y>w.1s5$[RSd:/odKBu2F!Jd)Ժ+ FZ]d-1票CJ"Ƕ0YÀyjy@RDs#3f5T~wwihZa}%WLj&vca/w^ mo +Z'o?IS*aEYu]\L?A HJBnMFYf4'!@2<g˲L[S17V>1gASA8MmCi|ap"y75Wq@P0Nl@=J&G"I='( 92Uk/ Ts@)MI)@)%SJc&c =..u= D53䠥'X(^q$:R=Fkv=ZN,ՊP9(ж\آj'8[ȩ4ϋd"n{eIEtT!OKX& Cb4SC':OWTdúݾE?Y݀c[oisXk8h[9*q4Ɏ(Kh4aO(&q S3CJE }Y F c ۄOvJQ9O>tMlU%EJGm-L5Kz}?M'DTgbģ vf~&$)9&* l5誩8H7.m* G٧8iE|i$\ F@ )Cy1q\%=Lv 9sc }jE[ EQIt]QpJ p938K܅W0`^cU~\#nj=x@jy#yXא8YXv=>ySb0\,zGn12i('Hzn+>]8"Fpl81%m;# |/xEّ'Sz}^g!Hk=2hÁ< gt i0KLUTY( כu"'BZy1|HSox1.K4Ec?~r44&]q$OChɘg-|&à64g1Je?PU_}i*77|eG#+[de(N<sY$TG1q!9:#Ni@i-Ɛv@#ѺfH*GΨxY,ϱpuB1%3uS ;>R#6m -;"W7$yh#f9jx)0SZ9 8u-n>/_'b ;A+ΈA'"9u]c IS#2gy\:v';P†,4ٗWWg @ɹ+3e5TE5Bh|퓗4tp@ra쯔J68M8bAZ+8pqyh IDATH+H 7y=e˂RZF:}$ 9_J^Jzc ;9`"ױm08ىDVLXl{8& z $35O0#8*j~w~{s0MZ ^?"n|" +kU)N4RRl ԛ >25d|U9LkXG<5#Ğd$3M(fWUrcZ!*OS#@4˫2|6Lm?iѶ-f̑>bw80CcסGBbvMܑ8ˋ%촥X{n˗hXn=>|㑜L˲ypdic9 6#r6EOaސC2D'gx뭷RƁ،-!.7k<}t MAYZDE' }ߡG;L0e>Ai)=l>CidA^0\ X.f,6;dyqh4M#}NI^vX/W0 HB(laPU%2c/~'\5Uknf3pN\*9a{(PG+"iM\ _,HE-?+dw'vYSj H@Csof;j,,\ϱ?3"5P8lӠk[i‘w`zV]\<nD"{aA*V+ DJ%T`IGkJ1A 1FNS;AFQN8aNP677uU񞘦<-j,Ig}],=-'dE=Nw{QW%l+rDuH&Vb>ИGMS᭷ނgj,3dF@D肐"%rWIɔ+M 5X=u^e̒4=Ñ̿5%}. 9K84Mڣok D>M^zO1ZMS^_bNRюK ]( Mjnϙ` J?vBU׈C2(Ha= ["#Y*GsK;uY!VM*c-9FS@ 9>ɠon g.pdm*GY4HVIQ섪,W)~,) ƌ )oD^#B Ir:%0 X,(37k,f% %kv~x!CzDJ Q!O˓ɔ֩8%B1԰ISLP851F\5?tO2GazÀ1EY7*Z e7ї~Z֍){i@~4\XD^3 LPzo,Y̗Ig.`Y(S9l%f%P^jN; )Z\}%̧/ql)%btcT*a.šp%pmfp,ʌ,c˂drCMlVStlIT0X:t=g&0.5?PdpU2ygX"7yNd"̜$(, {\W8} DٍqkqeqP#FmCiƳH+cy&%.6l[Ҁ7Za?0 &fVxy6RweEbL,p()!fj+{O[ŊWhrgYk91Ԧmb ˒8*ͽz 'mrǔNl6CSר=y"Y 9L#qDDky #&,b~ K>lY1`2Cpy}=RMiM0cb Nbו"쮧D" TBZdYOI]"O!Qx2:7^J"gr\\\PcczNV!5h%Yv Ea9a1Q)Xu3Yf}w[Eׯ^!0HHfo9<ǑSwx)Gd&* xgZ.MQ G6e<~pͼbVcū״寎yWAo[&/ aPRwҁWo4u*Bt"$?c4 )$8O|).3UId}ۣ,˛o~/~AJ Jm9@FV?uj@~QrO=ozn8WA+V'h@SƐ&Rg$W5xFUltvS '*457Z%r징҉>#]bid~$pLɒ"a rf"(jfziF>(q^;q WJH< eɼdv=!1*+4uGog~YJm 4P \"OYEY G2Ua?MVuFK1&Cn4fl8dM"*n6\IQC]U(!M}UU:z. 95+  g8?W@ͥ6E"F,K _ƱSdL{zr`Pqsi.co?~=YtEk>x~<W Zd-s伙/(=䏊Bf~k#tSɽS8Bhwy_ułNjjIk;B kig'ZKU%"˄CjXVi-`LƎ0!QrAEcD?L0L~ 9%{"׫3($g XEkOB xqq`K(,~QiY|{kC>9!&UK(Rn^ \ɊdD*. `<9x0Śj0]uY_})7?7ꇣ]ظ}%/ˉ$}S,B*Ƕp&2|d]jzc-|'*P }%K(iMNR58R͖O@`Brsr&Jb7 X^Um}B 6fIKڞ;ɱlZe>9Ll6Jdyq`3ύ:hWD/*teۑQ9e&pJeGL>O2n&av@V&Ɋa{WA}Fk,qNt$851a fńBt$M2QZWQRS8:^H#C]ՄDYfaC1.+{ aGjm[X\,I:Ц(89!f2 z@.B ɂhH>32Ys 2Ү$6=ts/#cqIiñ4urt΢m8dKIQ 4PD3,T=+ X,|&} Q||{~y[빜W =#vC@Ohg_"SZ@ЋQZd2uIx22R Jy"}33ZIVlڻH(x $# >80 )2(Q['֔Qw#IYc#O#g4Yi` ;k%um^B R47ָ}~!7lHMB]7,hEjPW?~PsN(KGɏ4bpʲ,`ɚInKWR#/q䄊 bDF'O~{k$-Y1O 93RMA]0(e((M4l` Y*JԚ ++NM*FtXtߖE) &,+L&<3臑)7j(徔3r:^_]YnTnW`^Zf1fD!AgK]{%zyQRX1G3[_he-!A8Ei̠5'Z"6Z'Zc^3YafB|*-k', ʚ؀ 1ULY !B !,էo~ez}E?WyaztVU%X/:&**Ү*8#`X,p9%7"S-8Ŝ{-5#Tդ]sNFxw1t=2ɍ1:WrT)*KT5@sH3 ?}"9 >O< п_zÄl|<8Y_W5LOB,]oWe$eV}q"m7aHrDod?edg S#E"6JC)4MOܒIC+ Q#SS9-;!#tW8L!աO>#βg`c|v7Mz& H+^cȘi#x r!߉<ؙ{UWAWmZ,3o}\^^`X6if M,#O7a^dϓ4J r"z+<,?L M]A)<)M/ hqh cz RSaB@9E( ";E@Hd$[]CD,0rc߳n@0+ KP.'B168IANLTU 5. ]a(!e X-ȳ">-tX,DJZ#<ϓ L2+ٿHkPc 5)e|@$ܦ!MFbyMχ8JVqqɔr@]0fCd)]0PYF{,z抂b,![:_Yq!c%›R,-pg> zH:N~eY(y&nU]kP޳1&u !&\_\Q4%քpsD~xL :} !rE86!9!$)8JA&T;$w-񘾇asl./?Udut]dvps{=5L]UI SI4w{4|Gr's ̹1rd(EP ̲ !bؐ(i ٳg7a2!Nje-%}G'3<|qqyIIQEJF EȜb8N$oɜ^4qk[JO,O#RV%Bd 4r.9"pstUBX81Adn ]16u*GJ{U{ߧZuHqQ.{0ƐawHMAh^Wg]^^>ɢF(3#ʲZ;ΨqDYV``)-V%gE*Bi4*rr=Ԙ*DOH  m4Fc~CRhBC3Wd;Xi=\I;՛pq c Ze gY9$7ք% Y -~~5ZMXJ&bBuI}5 Luˆ>xmߎ}s:f=j9~2<<UCS&k, q|3>EAk1izDփ"ƺ$sϙ<~ X ?YݨY7ϒudڶCw܈,AiL RI !#۸뇴Hg vRR2[/1zh,OzssF"iH3Yp}H #XlMGMM bD/Bm[DX̼a¹iv¬! k-i LW4ҽ$*Y6H^9?"?c`(RZW/0 q>7"^|EifB-')5C9\]]%Bnd3;O+tm'Ys& x&u;P0HfSHl1GFF,(7~?| as3L]MX.ZpUl?aDP'jET9D2V C: cl"gbBHk_J"%%&0NZlj!93 P*BeY-D/E2BH6tbqԶZkrٵG4M#a=}=Tj<"{5ҋXsn!]UUKv,cVBHJsa`A rQ,^h]%)]G- 1ITB&Ij-*;]sc2򄽄o Y2M?^|EJYϟ?1Ibi4M`{D&%=g+!4jaISVS__/X^_3?/Ve'ź Y79Bw3hl< 1 3N*Azq&) ADbD5P15.9bg5RMBgGxYSy ejQōaGXS8r&e]\n{F41f:)΁^c(9Q"r8i!}IkgMQ9 #z|vchr5˽ܕ"R90c ,Kz Ä۷«W/aWж-5)CU ),@CDs Q0S^ iD𪺮]9? fskXtao%sv{zHcD-&c`|Ne ( ʲ%:Y;U.Cߋ0 H'91B V p #|p:Q׵\Z U/E]S/aX! 8=l7G^ND7͢i x]tF-4""PVƚtk~s.!7P]\\HPZ *յfe|&-yJQ0N))q2iOkyK_+ ^':[$e/C_ё(VC`6k@\٠|Y^Xs-{o//X__ D uh%Yj-3B |{ᅦK|FvuIWw Yq}yܰP$B Տtapw;ЮR:'u)4AW+uWsrcoH:|X0 &iECNE `H+Ƿ(soF"ZqKz5q.vuE~luR' 볪DRڟ$(s^wz8LMt5=u"hڜC{8kə1dE65|+((C~vu=U:/rcw&Rjh-#AY|:3#s&'DneQbT2SA#-,b$XnGn(@~ӘlͻHǏfMnEY$¢KJrISg\rj8gDh{tZ%bVsnqYU8Nx9jIs* )SgegZ0 i;d+dLRלг|d]MHӧ YkAla;)'7',WCX gd/_(&)KQ&./c2wH,1fBq@ҜfRS`K EuzG`` sm+r3)r"psuח{|!3\ !k"ND\Sg'}9z*uk"{cEnc8vpT.#EaA6iLFrX{!A^Ʉna0OחnS3d^Hlni,V}5=4.\Zs% C>r/U+! bŕE8*2"~Vx*y0HcLTWJ#E .'Iv!.K?0-<Zws\7/zv͖d\@/δQZkpYE}iPJWa[ra9m֤UESޓs"mn>_ż??eQ@ Bɏ$)]t'Ad۾y]D*\ BaSGa1 Qut[UI<2uFz`![tj0M7@b4PmY̜s7.k9H2\$2O yNdǏo^)ҥ),=Þ+V&E bMCate]"˄a4) F(hZ5Qf˄G!BfukENa;t]8E&eeQC)P&nJh!L]}uYH%Mw ;mb@Ġg4uXee 14 .röB?vqO6U DU<FzA~x4\y vWWhL49:R[X#骓ء tmU>r01ʂjᱩ+BV \0Nzvf48aJWc 9Q-xO/`A{MPwL({"UUm[II]W0s 1FJ뜘A vkdzgBD^ +v%d{Z8f-6U"4/^%}f%N܊vpj< "|K?;V g0S=zX#D3@=gGc 7`B0cS)Zl9}3 (ݠ +(z~3/NsI@=ĵksv9hLh=Hq@YH1 7ݦmFdw^Q$SzZIQkhLEuYZ#U2s8c:kKSLp~⡮d:ENw])33YV5&$ɻ#BFɛ_R:%nL*4^KȒ1D}WWf})n6pN*p--vʔg~4MN-ZOɌƤ5 `1@L9kSm*Ž4 "9ˑe9 )[W#r1A)Ɖ܅˫)cW;E^y\V_m~aL* vߣ~ Q"$Aahl!Y:ML8Liҏ#qst{ɢvH=آ 3tSs&ćiȎs~:AJR,j U]Yׯ_'F5;UYl6V6Nwy\+-Ti1N/v}\Z umy*#bCݴm+Y6\)JHg"KR*tέ/#;9HaF?Lxuu\{Mz A@ `߻|@bwk1N#NsZ<s26 ~c{-E[I$bZ);ctJ*aK$1C-ϑ弟M]8pjpztLSCB4(+hrU 6 +*XpD|aBiTI]ӧ躎]Wd@m"Jli֠,sĚ;(/DYE| \4Yi 4sIjKRLS;#9sP EQk;1vr)bYr@o5d hɴ-Z)B@4p$_EW( HM}By/]bٜx <{glM[9mgm͗ɓ'@yY,y4b] RZ'!Pgn̄!\)ͥX XTULHvMuZo] B`A.PfhF_:J]U%B:DYt)w)AH>pF1 ĦmT9S*$̈́n&վƭe߮Q[ɛߣ:P 79OG_Sx_iL&ӏC[_"`ŠT&8~ݻOh0(s8^,3N޼)A:ȊFl(B[ `FIV: };̳ ^sL,"C,5F;dY*qF:1oFTLPdwpsL5Q(}^ 'Nscmr{GYD!~3yC1ƕ4l "ߡCB L'b?ʎEBnÀ7 w@]Uhd6.G^x폠{WV FZ!Iq2clGiDDt yQZa?MڮCӐ0M_]/\0 YэsL̤« yEq>qqyddG<ǫWҡ9MnCz9=GFsiÁZj8x=Mf6 ʭa!0M,(Dz3&^<2WNr%>ʈk"OÀ0Ml,j?%cGkЋ4]E^=s? (mRE(9R5b;cyps} ̡̳Ty!59;'0֢iL-SAU&akaSa#esP2} :Y&dBi, V*!Vn y/ ұ*xzH|9\x!(S4Y+ĥ[;vaRr:=jG1~>}IbmQ9~>+,K-NisV%eA5}w>-y膁*`3̨|{WEx:iK Б .X$e,㨥kʌ֪L+ސdv"6-\4]C LDFapWdJIpuǦqըy*xgIfR.“M!|̡)SjݵUtY#=zF&JQ1LTkyw<5Rs` Ā2LcɎg v,j S`cQg% yxk1KvL}\:B8m;nSH:7,Kn(v(*4KFNɤHfI? :0$Y,fM1p+.ȳpT9&w.HxQ'q qB!,V"ۅ@y^҉hMu3,1ZTuQ 1 Ac=F ɉM Y޻]x=;t60yt Y7*a3RYƑZHu`DTvб`]U__şi}?xY%WԢׅU0W/_E qQw?.FMjv5w/҅p*6fv5QU>IOxӴt?L#$Kk gū;hsT:P9#\t.$D27m!` hǣZCF(T~9wA)TDY X1Lq;ѐC1EtND1gb"cB騖;vXf[e.w6HUM1U]ab~BLݏ(r9g^6g{gbݸQ:`oĒm gŚsYZVU%@1u1FhUɰ~GE` ;A낉IH"9j40w2^THC{:Rݘ$U{zɄvkfG\]%c87-J&1RέHH)tFtQΠ)omJOn.=^Egm"H{>#+0`[38g;)}±278@$ <j̸8 >"S2H2GqQdX׼6YJqw;={6Sh֛mɓrRhBe:oDs4wMfk:8Y4^y&_O;τy~OAz!@MA]uJ~&0 lu.*O|py`z솁|Qp::6 .[ꒅAƃ+TMףq+j2 qRr.0hqJDZiaG稊0J;SECs(Px[!ed iቬIg8Q1 nd4ēb-K]_Kʁ -g)bmFV, U+?LeGlLIyP$,)IfN [ 6-ftTZR!`OG+s"f9uĦ5ZQcGix*o"G޷i#}F&{>B i#[9x螦_( N'J#ǏG׸;o]Gء3BVv"bNy}? Đ Y E?i?3z__?sۮ7ba̖tJCM 3on&UY\P nRZhg?di\fΊЂ wl:O$INVLquUkJf8ϩZhZ\;聤pr]2妠f39#E $ӘJ^(d5sD+d̑x'Ƥ*uq  ޻ʲݐ})< Bo:''错PvV9&OJ֝dF\|g!+% T$RY,,'Qm*e3p$Ϊ,%3D 1bQ]{n mZ5NeZxTM3gX : Yg!eؽ73o2RԱ gY8,@‘iP' &+[x"!WZg[ bU]5@ oqsyPW9 AvVn8čs'\\^%aV=rOLƖ갩9k"eYQթAySfH; h)~b/uM$yudf#3#!ūlUO3!@;0.ߵɖGu[]z?mۦH{s*Km-ײTkl bŠ+eY7dk\\\; _y~}Ÿ1߻꧌111R9FAF(3Z++q! ˟J%~KUJ~t|s=:j,qRnNA|,anZ*wC8TU@·@E&|sRvhѤG0(uUqo)ʬB VVIe(m3:L,늤P9Nլl*:E$ic~aT#-+a\_U^w>7"c9P]WqNݴajQbd{e[W.r(FMٕ'/b8I!EHV6O!c y3(i!`'Lㄦm1,b5f^Ia?G_՟Z|ח] ?i ssHX] iF]bF:+3,|?BY4: ZN–eΤ&A{ s@bNg)bY&Z7L̥A..vh34r(!NRoXdq$vjtxIS):(:&5Oł[I4н:̘ EaqCx2zf3ש7lw&0" X#^aS}v?>9߿_UU n<#Z+*4ݨ*yk h=1:ZƣPZެT(8*ƈvא(ZP/ x%&n[ snSrKe.7_&y0g+AW8⃯cZ7arY*?}N4InoX$Y8Zw>A*7y O~ZAt|Ft悖W 4>ӵ~]" Zey(E (f.(7@Y8.c:tzn6Ѭ2DuZyZ;~ =^?!ZD@_ *{v)xk^* $#.Fk(9EXga@a ùECl6oH#᭮G͓[U%qݒ1MsuR >tG&yzQ#.lYF#hRue\r~0N&OעPaæ*psãΑ0D2z{r%* x}&@Qq(YwDբHk\8mThV 5ʟȩ95T~yMȈ^?Y3!p  Zej:U1yѣ"FwC4oooITy܌#93  RqThhKGSEד>iD-'5NSB6-:U`ީ UKSwG yrO^ xEA V&Ck*I(TbDߏIe/=2)Tׯ_1C'l;l6u2c嫗x9rD2-&9B841\ }vcE]oeCcSx|u]]"sDmNm˹ߤ GF6)Z1<œ -u(` QKAVk;XHAq>9`TaKw3&)f]591͢z%)tg\," e1-l$ {vptwZ\*BwdlSz,D*, ɢM gA5,{*z"MS`atx%>)|nq>0 UY2 ,/DpUU5` xA w9XG~f#wt^[(*S,cvKn4JYʬ5㛅b}Ѵ ,Ó'OдKV:֛AQrdvc .[+q!@&XgI3EecS1 zh>6zZ]yɞ_V_iu 9̋w,y ,Ðy\8K>LZ/^APQt[3UڔB7yD+*Dp&%֮|:-:gcLZ,UY$'AӈiPB>(FMvl`,Mvp:`TM. ybCCM{PR-s_C”u6mwvM+a1.liz_5XVSϳ؅1{_iЫbkEuY=<ENu08mBl4 ;u=1hd( gJp7H}"3ry3q=y{!y",Cy/o=b)\'=2X-dlڀ:vSdՒw,:v]/C:^Yh`J9\E kѴ>5>}qO.//pyyOnMg \]ou&$/9z&Z1 dT!jA|5ZO'ay!R€1z"|yu|ln7~8>pÁMzXA4YQgp|xű i=S,c,v=gӈ[4v.C g4M8$vw{s(0Hc %uA\ `O?_OwEUSORY &nأ, 9r\_xnR-.&׸wdbj?SqJiFL,d'}~\%bv˲렱.9NUuun(".A![6v6qLx/Z|iPňɦ+~TG %sPxN8]z/q+㜅|;k $/3\Lŧzywi$܏S0Q+E? V|>GCQ=(lky/V!{՗K80tFu#m8mShq! ρ#~!DI,M]jaNҔ"P4'Nj247&=32bPX{M{U8N2hi`l>π:QC7<<~C?X6)#=ٓLInC+=/f^\ALe\"B_0Zx煲ϙTs8{$r#Ì~0LG^&2B6;=r4HdMCe!sږVǂdkR$)F ӌamh2u"&iZi5XBJr$VUDD3Η^H-^KYғs*)II<&=DaIגWZ8l8Z.o3R= {JYW5`R BCXRDUrQdh eE h`ZAuխK͇0d~{,2[Q=%8.Y7.řR;)'waF_13qП@d!Z{AVMxX,ANFBs)xE;>!q^$)<ѓI/bIkôVFYޠ"5y-{V&Έ}##x&zmɥ IkUF&8QfGPiÄt8$%y~8.(+6IQ7).էA,YZ i!,:ŲZ'[2mx4&|1`l?nQ-u/l6BZFNfK$Pk-{6lxocgvp1`~o|sX Wz4"I%kCrqXAs蚈*Rtwї~o~/ /ukI_ *N#$Y1nA~4#f,a8 2TUmYb]K#Q5$,j3*k 9yQ:**$̮ AUZuo~1`FMSfm5Hox:c S2Wq"+Ex M]auo7*Z@oU}Yf9H"^)uTGNȚU2G^˄i\xN) H,m"M> c'FgANy08i߬ IDAT§?)|n=(UzqEfs/L ~$3@dJ &h#D7񇐃£6躍T3Fg4yyTp:oコ5qRBuS 4™9ĕh";Y%Z2Wk&;4Y!QK; `PtbpEH,{nۮ[j ϗ)i\yp}s##35#dڒ6DmxcPðH=BZZBD Dv9,(Dvb ؈\2s>NB=/:Pyr52ɉ 2ipLm zMDw2#r߼stXһA N5 ѓ\Η4`{@eh hpz][#   1Z"G/~n)A9Q[ ǣ9Qi[ :.מ,N_| Ofo/xpAC+tmQ A2!< HX;x2h;ap PvBpb 8#j'd:r)&8I0 Y( &Rj3CȜvmmϮmke⸹AVvk3Br@KVbݝ1̡r77n6|xs_Ut:UDipDtœ= BɂblI7*)R[!tV`y~{HҿT LfDqb_r>HbKSs~OdAx#?JOxpeElk<{\p0,i3gppE?Q'/cM) Su'TUaQ&ORX"!sLTûzMI *m&J f|x}G5aN~ x1C_/YHFm"Ku4%. }ߣdApb%TJY1%SN/=}GֵO[V:'~S Yß xTI+klP P{՛d$¶kQB(];d[%:)Jk 0ZMD7z 8r80YZwAR}3yQ,FW+=%S޼J)K_maqx8/ &Bl&a{/̢Шvs/XR9,%V6|\n* Z kl"̉٬A]F&uTR71UiデBY N0XJx1Y!};Q4о=G=]ʄ{0ibe9)48\]&dO8#ۭ?צCr8NN=ir{TQ> Ar2z̸"ܢm|f)j5cm[͚ϟ@|:jGZGBzUA*>g$J]#P8ΚBH^L@<A-lZ %ײ@}$\REtͯZ\dq/v)eHOr,5hU@U;l /! Z՟+W351*zM+g v[%k,S7#MڪSL+4Mm8 K8k e͵b2V$z<\,$ћ"Nv9k}opp4ə M]wp?y} o sΩ$cW)?arclTȩ89ٜ r03`8^FBSDS-g7/G|y3L&pIJϥjQiL]AJ oBג'?ijO Gg4ܫB͆PI橵i\o'8_4qIiz@}͖ٻ]H2^?7=䓒RT=JH~Ul^.g fQbbw;ET=ъg;(J]=  iD8bfae&^WWWVmÄ=RꦜT( ·'΃Ϟ=3d\}XP߇^fq7, b/GxRj&VT%OVZk7776)C4niu @Sy\^!%~#r}WRz#޼y#jD/YC_ch %źJdB*Z9/9@ThȺg55w\mMkh `!t(`L&[WDyȘE$C?LƱ4hq*n e,xɴ̂>CzJ-˥͹8gw]WOy]'?-eJ?܏(PLh` i3bT# $|0;^ b¦Wy߼6+L @=V^@? 糈4Q[IУaRzb9$#,iwTh/iE=v0E筕dJ^"#|Qp 7$f] W0A k8e^`i;Cfc޻/1Ad')@NԵxwP叞(E7o*!*,ca)%!Pu?CZ\0HK@D! k5-yy]5՚KRmrps G4OdFV29!"f_7XťuVѧaXW. ܣqv4~f (HqCD$4V KIb1lj ۪.q4qq> ž8>DL*%:Y 1Fx&P, OmQsGKC"aXÊ2ZΡ93+vU?N(ΉIXI\9|V% KɈU-{c^׺ e/zZ ?m[S* sZjk{F'Eti2i1 K+ƧX!FpN}=x "e,Be9- 9%9 LL㈶n^}?}7w |ߜf&6Q)Fڪ`鞵S\Ks\gzs)]gtM{ cTGWև 0!f ы|8G A{ӈ( h/,IDxX-=$U;C8l;z.K&< uJ7"x\焇 (O ";ϓXokh{iZb4jI)P=yڋq@g|8JED+c=-1M%aUuOHKzsp8H^>4!f%R`O^H XEΑ+&$^((tPԌs|nX/K"pșb%a`uK4s0*〮$Q8֨N{֪1]׋ I<6"*Pղrb_GFmn}˜fe#\c5 4|: (\n+Z.,XEZ`=Ccǵ-KbJ?1O3Fr!aN~#z}\ԱL"N ^; 02G]hvͺ-‰ J5PRMy6O`TU}2p8ZC /_f㣘V=)t"{uu(3DhVopB)xB SLXedSC+Ś$r@f:x>|˞*X@|RdMSœ." fgH4O ;M]<JF'2ڑsEe}={I 2D9AGi֕ir6Ck#E^=[RY=^T%ЏmuMzN3Q|m}8Q E8TW狿3Jď}9]>P9#h:4QaGXpszRcG`eQWd췝2J {d;ȡo( ³[T=3y¬fKkp!' Oa;sI5xgҢI '1IθJ gRdCb1ɢުZXUAIug􄄥L%|٠sÐrA(2Dظ Aո: x߼Ű$IH׵8&2\]>wΡM33nnnlʋ%#M,$ ) I.DA8gTmJc"3ʡ:C6rJ珞+qi -f _^f|.cN{ RSg8_t9t .{s \*nAd^b Hr 7gZ&,$q'k{TMZ*iJ!`<@@p>_oVz (&h6hvªd9/źy6BgaASGxu6sNHp6y'Yϟ=J5s\59gs] ysZï1FXպ. ^=BV{eMLfB- @1AYhڈBq˨z*XSJW^kTkrWm+膒x D=6|o^{-\_* #$kZ؂*j(O?{Yh{ ۍGB:b[yEMc0TagSTޑk&k-Iw\_]_θc54~65i~Br Qзm_Ψ d6LO$iT"YN5J uPT>} ;N~Ϳgx=A* L5a߉Ju%>nmt?p™9!ldSBB48P8JwE2O IDAT҅CQ4H_<_rX.n#n6.44*P٨avgv.޳B.f0ߧ)~lSRc}еm'VdL‡"cW'(4tg&dI*%H f a"2>-#K$11$DWQ23W]\-Cdų{oITEFcT f^|dzWUR~#=rFjC$2oRmr] Gm#8'ApzC'^|GcdlQVU5KGd8-(e"XlL9@̫+K<(pQ~gSY8Mjpe2p8u*ik R#/,I6ڨ _WWWv_-Ȗܓ:$m9M\1˗:zyB]HPxu?ɮQMYI=ؽs#y6§%u)7K2{6z gD,[&G 4b2+"}/ *ExbLc=iKwy{-/*~*nβHBe*6RJFh_vqS% 4XWQt( x|zO\UhCY-gM,8Nۚ1VVQndW4Q 10TXs,՛sJzdkD&bL:Ȉ]1]0*ʵ>kRyh/)TR׶hУijl N3q@62jk [[- $Qk/hd{)rRߋ0T5, ɬtu%w`5k~2sΦtȦ:yg&㰣,`vD{mal&RY.r0abS"aҌno(i2T)U86\ܓ@**EuXK@uI{&8bۡDͰi'"VՕA>?;,EcI. )Ȇ<{[lu%B:z=Υi˥7ᣏ*Σkv;ItA\begKиHyN&l'cM[ㇿ?_Q'cUyK-1IJ$4X:دm1(Agmk{cJ6+whb(kI]nqJvm VePZ-{9'II:ҟl3qbpb5Iw\Ʀ%&;4bZ0kn~!D'``Bǔ-qѨd D#'3bp֙ T+q靕 Da1炜46CP rmӡ+z\8&o0abUTH#s)88^z\?o|]WB?*ͳ @j=g$3fkv8aRzH, MF%,"sjL ,]bۉ 9_95 5!s#~\;l S 2 yqdj%)9K0i!M];2O3Fɥ^>}kd ;yqeJCX>b#-j-d2jWڛn+iĘquuzTR<%]axsK2 ʪ `ڭiTHryY]ע+<> p:U\$kBk&GN&$I]>onn* pF l$NZ-!٦E{+g-"K`~Q=>UTQ9[η5M~@N/ɨvkNRm?DdG,*qfNpPեqppxL:mSx8-#XQ0pC38GYzoA<ùŞ5,})ųBtZVTVWbVTWoQU^˛+$4Bcu٢bDhԠSF`D. 6RhՏ" \& ȉRYAS7 I b Zd\DR.iM-5 -],[kws@F52}8r:;`Vd$'Uĭ`^!rWU"ɸ x <q+l7~B?LHB0T6z7̙ 14~qGy(9 tJJ̚S4YahƊ#j,v]r~=ޏAg\(%ήMX<۞!jH@UӺƉ&~)64!Bqwopѹ~LBS۪ }X#uRӨ\ʭf YO SfP<+q<Y鄢RvJE$H fQb]¡(?EދHDk`-qM1, O؜m)NɄ{*b6vC[eC?NrF)<BU:`2!fX,$Nm/PTL~5NdVht+,jphȞAzVs=Ʃϟp{{GwTdAFP{D\uA2B)3&')/^03QqུL5%d$dsVm׹&ֱlcԟiǽn:i%䖖Ŀ'M1W-fD '?)gsX\Pt݋L!޼b(gO28z Coz24t:x:F}CPd}q@zXGrk#RY))8xIZ6Jul0Bc`"`O NH"˸9\V̬&z"fUޞmJ2p8p{ ~aǸ342_ۀ5fzbbgn],hCYFQ95{i^ {3n]S4SJ*%S2YC9H(A)2kEO^^9py`DVY1~hrrA(Y :rxC"M'L˄P\vsʗ~ķx}?*Pl;/}/d}sJOxcڶUNo0X &({ <r<~)4Qލ$$?{ }/hNF{g~vH;/JiI)EՅTC=8+%%1i ׇtPa FYO_sXN,,(3t%5 AVHa'Inq!`QMnb߉k|YxA{ۼ>l y6{ 7ג[sqOBb,ZU^|)MD^%nM^W#vRu@Fj$5!&m\]]at>Ϭ?I((b5'SO"W &L^&܇\&>״zFp 1q=Fν%>љkX9>Ze@ۛ(v~o&LjE+SB>Z:Xf{s`IڂXqBtLv٠*# N˫ "L k(,H<φXX -b>m"e)77~[K?}Ba؅VM ~<@avO.S'YͲ,#PIom4, S4{!\ݽ~c&:1Ȩeڐ\7 vr _zP1ixpVlF"Sʦa)-jf`N a"ARq}mƺѴ-6t<RXW׵1E,kYWmN \DcpEP 8_.b/}fɽ2{5a[px2Y5)aHVZɺV !wcB%uݪJ'OG)Œ/cӇ:N5:u& ZWU|G/ ryP㻔"c_w{H#ڦFl:'z" lZEMgqu$ɚHgQ׵m#EߋrX'#݊pUe=vh gm[cbS@ I49YNal6ѐ,ws`䜐q2 3ߪNнM>Tq2z+Bo^۸eE_A3j>">Z9XPn6]@PJᅬ7wwS,x~ʯ᛼eg~ ƀ鈮е5-{ޣ@8u]!iU=ys0' fd$9(4[x5l:/Q;:-}?Xl6}^Ay28=J5mkZf!\_P8я1FSh<j6[ۈ2B)%{qpdN\=`I[*fI&DSnrYረբwTެ[h{v%99.e :":/^jC㣍RlD)@jp|nohd%VHE>Og5L7T,fs;\>xP[$ʻxKB6hjiP+wO9cJ ۮEPWo٣&8eAD7aW9m.f@1#JdVU A99<=ơG?I ,1;ORݮ*YB`\[$aB}Ee-) k@c҇\̩ܸHsk$GxCY&BD 4XIXuK)6NbI5O-&k,ဇ^1bN+5`#t2=}V2z5ہ7mi9p U4sΡ{!Tz>Gqv^Kϟr- wJ)i x||p%U*QsgB# wBcPek'J9߂T݌Qv)'[Gk8le`fA֕=uj#6rމ7J6{`N&9,v{6M g+vy/Y=S2](sz;XrBύ"<gઇnY՘ߚ_ª:`I,2@IHՃs&F`#IˌOUx6/zm\mU).,бqo[,/E`WG.ĈaT;h.h*db,{]䝽c#&bo_/ mp2D+ڶxG Zr N4,,, 8h4cdߜjh;/z<>|7M~'O;s2+췭QT1. Ṕkp\. OB- =-6dI_ugˠZ1U4.Hj] 8 !mx mP,U1bUh^{;ywqcYEk {l'2~g8TLm.? BLz|N9gzKB8k~ _%fFY 'bvݺ{=?i*%A5BT葲 cL\ya0ኘ(օ1&&U (1$JUp "OiVŜcsn}>Zs9cáT1J h +@&)F鮁AKW-!Ⱦq8MqNED3`7S8^3P4V%d̂Ɵ#&-ƽ@<) wnDF= "M^+-_i0^4hu0"n|{D:D-6) kQ>vkc&Ӻ. EK|\e!ÄqY4xU]|fw#ŧ6^;[\t~Q PZUZ!((QZHB)'r,z-LeKMLx˹ѩJdMxn[uH bח@TZ-9LV*ɴ%&p}w"ܔm۰3xx|+ ĉ0jD+T֎>lǔn.׫N )@{{{c\Yc?'&O:[cqw>a=v;gﰭ[sgazSAa׉QmAlI(Ca7>}:ڱ!c-/_×N84w*1*oCrU3GSNwmȘFAO9޵[[Es'Naӽ^[ ///%`qc0%Wn7D= dx΄( ^TblVSp q$5[,*h[ AP2 ɖZչ\@ lK{5,)QYRSD-%}6QcB(ml)8D$xkFqtN,\|~#["A/$lMygiFN粶XIrd~/M|럘 ?sGqa4èR_ KU3֭J}g\avFne UIġPVJYʴ܍#>y<ሩ;֪kKϒ?7'2+w*@儡z/#Pm:(7v*#0~J5Ǭئ}#fe2ۀFyYy):/En[b WR tړ^:B* AswV}!sX?Y <==XCu]0t:/a-=֥u ~xO&>ig<4@{'&I۱zD:w2^9jHRYm Hb~Xd3A:fxG%t""JbIՊcht80Y8ثF*AoQU&3n___qS˴j8)?E"rXЯgPsV}ߗDw%(^s2n1/D_%T"e4{ R""6 K߉劭QB %abKE4JĂ.$\,b7JN]Aνբ㱘JzҦ͙XFGiV>?"VtutfGmbY$MZZŘyǿ_;^ߙ Odg5{>?4{[[cF1˜s@+3$%{7aǦ i:I{HJl=;Zl8iU$&zҴš5$8NZQFiyϺ۔{\H,sMN5R䥘Ή25CL5~(bu=G.X?PyYM|-ܷT.M#a7t;؋bߡZ,c=wẻo$.l09yJ^¢VDhLn~Dlª%5`?ާO:Q7"O?yZkNzTLgPq$x=2>W] dXày؍~acGœ.^\+>{jhaU ==:Y=Ԅ&A찭Z%vzhmۂo>`@aw{<'muwc< D?AZĉdB=]oC/cDhR7K8?d95c]a0X3w1=^OI )u*|m*Ť @C;=,Ōb Lyƺnuɵ8_8Ϙ& h#hH&4Z+Xx!`Nq!mb‡ӹO(G$)e|g%rյT_3a%s+Q+ܶ D: I+sMVT/UR4!vƝPeh a,=Eȇ Xt1&dH<=>Ru/HRpY\c#b^r}#mY+Saːrgۂq5I0x?+ kR$x #,|Iܘ`E y[nDGUYd*G nГVwlok*mShfcBXR\>cA޼߉x}g~waa7 :`K\䉝& J PSn1Я~RcY}Niw#Ez.dqu/ĻoB}&j3y[M Azs:acP̌xP7 Bo'C+mCe_vmݲhDj;sDsb=l0 2\aSo|;$B0x}})Ϧ9gNvŰV26/..nkY؈% ۶!Ĥ3[ڄn~~#~RsƔpSR>^, 1 u~L:A\*꜔(TtY2n8v5)Kk~ӛ3w{]_ tX^JV4XlÀJ(k*\x@Q =]sØP5q豛&m'?]'ct3SmP' \SRi$7i 28(zJ{_ 'OwHY:qb=z JN7#-e >G WA ZwdBߎ/AB"9VߋumIT#^E{2mlzU !\F꧂Zkq>5"'%R|6ZR-G~?:v?tqn,ǠH<dfl*sΒe+bTYÄZnu VT0J ۪ P)| #{B,l8V3}?3taQ/9m-m Jz1ycV n,f\#~w`_ Q7nJZ۶-<ٻ nDtk[7-EN==N`'8:/ p,_[7#/]0fe 4MMq5>!"0iɛu =4`7V8;ooL `bB(p-*Pm bĺEUnSL"hzƟ##SNP<.X;ﴢ-bSw۲a¯"[Zq@L19$g !bY*uq>T?,:VYmAj%Kr !\j-##asL~z9Fir`&`Dҗ-b^7,:uO0DVr5P!WWy0N;|8/z"Hf^UZ JztŻ *ؑRVDH^bşs,ժ7ňgg+yQ zY$ɍw0tjeC#"! p=>Zьs;l nv;6^wtʟ$dԑV9»p<$Jq(\_Neɯaü,eq:5X1exߕYas>bJPtLp ;zYSş^[S ;S}W|A 5DOkHH!?<> \SOP5qƊ&30uCHܮ]Gx{z!Sup[69 NF3RBTmK&%0hi`! I;R5IoL4)N7X=  Խث8"_mpW%$V8,N aې&9`t.!ü[u {@ m1a;()͒-X7||%Bܼ%{OkĽ($:<>>IrYqѳ j[ s2~Lj.I-7gHReRDzv8hNyN m[XV^8gFI AEd‚L&fPWܾ+<>>b]_/> 7?M $b}^/Yx-lSYׁ IDATTpFp]M$Nc؃2Zl*T 8*CVyݰlb&(=^F .VWоcq$<)*aŘ2Ͼmzz/07+9 ƒ+p$+Èr᨜sQEwA9ZJc΅5 |u%6JOֿb0%ёϼT-+˙BeF>gpI@pqń|SgwSbUl|_$KyXri% Ɩ5$4Iq)vdNWnm|[8f?p[(P39 j4Pej=XkK]ִ&2+US)ޓ!\F'I _U2"nvⶬΖvz|9Z~t˜ GrQ_-!md0Iu秦U zinsIyhP1:&jD:EZmD3@gxýGW u *6/J}o A&2b؊`"qWEƁGFM l,SJ,Xșs\.%]&)ȬmrR'@(jdNEs##i{I[I1m!#Χ>|xq4S*{$崍>qD!愛1)e\?vK#b!tҶZ` !u) ci+bMoIz c o^C".m3kLb(A$KKG[#à6Sz޻;?Aaz#52!ڶ a&m$Rf"!?zDr2nEƺjۆfm~wa^CvP&z/cVa3u_T$ER - uE{-<9|>㫯*176ѭi~, KL;?키Ō?\u ش)9b1]&u DM*\ȣá H*}=6P;t1!:%8Affl6r[0[a2q)@*$Wy^:m%^Ya ʅ$h$NLDOUQ-8 F7eD,,m, xvqO@+1Nu=U Uu3w|Ea;F5idt*6ÃY5$nQ4S- TYq*n03F7"l:2߃kz^=pmdg$%Jth1i}}? D)hJ·-~&-Z[A^% _r5F"i8y/)B*(rz(/,c=!? ~mU3#~i_Iu}6McЏѯcߦ̷B6D*Ju^JĢd:9&H*my})gfRi<`QZ׷K&9(ے*dĊ#ĄE upInq0SΉ!@%S $̾l1 Ye=Y'xΧ3m-u%ɱ 4">о{ao1)ys/ g!`"%$/NTH!a[VA QC֟9dA:_F`xhDec,ߛfn%AarJX9>ï_-d*Ԡ m"hXt ȬrbPzx) މu.3mv/: &mާ`uh@ϡZ}pЍ)h^e02c`8ஓPސ&>迴Zi># u L5hr4¦"YۢpAHjv"Ā8 A$jf$Ą>u=rPb-zW ~W_~ "2ߏ lnHd.DJXns-pBIśpxWQX_`pe_qCJ HS(&뵐ق!!6#eQwO7$e]gqun3:uY[9Sc\GEaۧ1߉,vٖ(iH_b1ؖH2\ײFt*4~Wׄ\J &!c?<`:{+bb[+ta7 :B9Kw*\&-zwzxzv q7KYuz09In2C/n8f,8:xTlA ;&ქ2(qUъyo^- U:!NLt/w-'(0y(J:sj[W?wn<p`P7Uк-+EC"x n0 j&B@7QKbV$)=5U>?‰O][2( R B޿{mJf̃K־f[ńLwy9 c*?AҖRKD-q {ʸ(&/62ҾSV!m"jY $E_B$-1۲Z^[ gUִ8Šu i3Y9/8aV%cc esQy ]Ĝ$%-TLx 8N,|KE-TP^̼w۲" )Zt+!`z#D<^oͣ|Q29;|/RHڦML#b9՝U \A*̪η^nspZg#:@+.qڭ*dٕz顖XK i{r"n V[aKEKúleym1 E8`">&u(bdmuaƈ˼ $q]Zbm-UT-ߦNdX"1`]ag?H\Y'Yfw5o3pƇ eú|vRxw,eWfp֠T FH*V&\W|72N;MvBXx8jiH8MAEu,D~!DјA”] I4aODKb |݄<0c/ "=Ru^tY`a% e{]jSsxOG!k@N ݖ4™`IǾrθ^.Y` [G!cyMmoS&svef ̴p9%}|+5Xu`T4 d%茣TȦ,:ZЌV$l4у`9c,~_ZM-?MV`#rGq`:9-LEoInJ t 쳬6dD%1ଃz04s˥\ Op^ Y{4*% z8 C_J@esESH.hFN`yn*= ۶R߭j8ϸݮz.:_^^ n7Q:F͟C/m&_T9GEn;L}Ψ6տKLbBjҒu>%" mۦ`$i] efG'-mI),|&+V敇PIAXÄEMBCj5Yx9d̈́l&Bn-n1o}*ZNb2CTRV)9p8/H>$BoID3ԩ՘"~X?%:/m1Lj\zGQd^OWo@R ?5Y{BI6*ePe*Ai3EȾW\s@B$``2P{e-njB8t!ňÄJBCYy8"rQ #zpOx}<N啥w" $ ^Y"Ԇ}$YH0zfkU"!z n1 ECP+N{`ݾή诧ѫ,Ǩ~ubcbL]`Q4w4CϕpVYуﱅ YSsCD(V? :̵%h4S%OVWiWBl;%>o'5PAF΂P@y ,܈Y`I:Bqmq0rȐrKB1$:#Rp&鴖$Hkz(-b8Äc7JEJ+RR!qwRa#l-bv /ɝ=BHes=_p0Rض١GEޓIlٶ\4$ڶﺮ+-eXwh"vt\JqƖB S4SX?~{.A3ϤySE}Mf-ඬ8_gn3.N lE (mPXPh= 51bP>lj끛X1ņ0fKmBs/x}anR]u) dw$y CuŒBL)cLz?٧xTdBeYR9j#LciU@X%]ZX)hA흓jZ PbU; $$su8L̋nxyyW_}WzW8֤tV4= !p:EY=4MenZT;dOd8#H_Yń6y}܇ mxg\sܙhSش19y^FeYeVkP[uR8yԚbЪcN)[{0{1K"8T ZDT:,u)ņ(hN6f7h58p@C'hMFY*򚽯rTF{0hroεB:MV^HPb;+I"("V\)&w2 NQ9_NȨ_$)N'(nϣ,!Q닱$iYZ\gL6cnI&"GDhIK_=rg?_H1lqB&-ca@lPxu]*BW =AVI6CdlZq0r޽ ޽{WCz_ (DZP kB-yP3ސip ^faMc CW{uNttG>ɸBBY3-WGϏi)R3!Y-`siuiU T(|9#'R &BQ6fr+>4^B2`Cu= 0|4k3*ÆnJ~CE :iCJ#[/&1-"c%q:p\d 2F]{0Y&#q5Q"Pk 8ߥb/2[o$xV`yimBT/IVQTJ)2vGNb2`*\ZxHZ;{X, [䇌EKKg&,:J#'1ɚU1 -7E%v?m+ĵ#&Ebm֯閮q^An][LXNBL IDAT֨/XmF2 ){ƹ^%;lya9F5NlVn4 \kYRYYG^`(*d:˟ W*CqkXk3p .8]oL>"l+~STΈl){aMhE4mZHmbBaDz_RfXt|=e6 gS ?7~?f@L2nRLEaY7AGl u`K-tCp<mxڬvྦྷE|@>}:a/zwXc j a(Փ|7tŢ^ޗtms]V|κ6j@!YwEMNɐL,0'u'#z)"T_)"`*G=yU1LCJj\#VǟTR}v1;LӨF_󝎅T^ wP+#o :2rL8P =moj~o5޶*Th^x& 9x#o*:X:rJc%µ|wB(IFkkrq$VL|rmHu*"hZ6|4bSq{JyRԶ1 c¢2N$Sq|.`ڂ]w|:!&qx !$b紛4A 1M8V[zn>,G!:jD1#+B]p^_4Π8߻Viۖ+z&;7xؗ  ڨRʗd/Uۂ+uC? UZ>\^z4GU_B ,dEϖZkaYW$$ye&[6ͬWp` &}èmu: Ϡ90f:5 a*t:geZ*t4 8%91"#qOQ32Y|1#xTeZDr7lcUf #f;;7+mbqNNZOa"B"keϦTDCgv2c[`-2;%$ǩfKR!IQцagxqdNj9VxIrsj |3j_^.K7#z>zv{ X$C?xˌ$댨g}Rɔs!IQVއ50mI$GrVҦʘ,w"r9f {8tb? OnDNt*[} %BxghD b5h^ϻ c@m60:v%=9_kYM I g5j9+i8J JpäJc=h7ƹ߼m Ÿ4|-!rYԂpt:g-ZAE4<_^@S¤nq˺a یm,$[(jC4&JBNPzBA"[XURGZp)=uq9cKo{(.:te}{^a7|ו)-CXWI-~4U$gN_ؒ:0=q˺d(hrAyᒨ%mhDCb˸$"Sa7CﱟFzTc7O2*Nvű-6u2f{t ""帷X'MFhh&tzO޽{_:Ĝp<t@m$ߔ-"A 3hLc(VŬn`Ybp+}jթ&Zw;1t 9I<=v '?W}?}Tf^^E 9N0 iGQD5y~yY5\EBxF jHѐv=5bbϼʵ;[%67~?jTUVʡoHs&s/8q>?///)smɖYNtt4xh:0/[T6i1r}LBY&퇔QvcHrcR[ !?uYvt|PS, >y5hQEtϪ*uDIP IAgy0U1{ZƒLi (~>&<9fبaR. Ugsxz~EM7asH6 bMϔtjjbĉvrH֟/L:a^WI:T,AuݰEQ )a^eܹwIkBDz-}-5FmRҾ.d_41BT'ߧmq _z&72v(>=Ы?o1 lZ((([N |V9 j]r}WUgBZ?O@ ?\OYeGa٢<$~Ux",5Sl[ JAN&,3ևT:WnvNT{Um O]~N<="zb \ [}-QV=;F[X NEnH1Ca^Vܖ F 9ºˠP|1yu1 #R|*jӄy{Ri(EY>#}9VD&$^E|:lˤ | {|e ؈%ᢰ@ιi%.ɀE1EQW>EM Iƌ*/-YC;^P !焗n1oU;oSŮYZ{ ʱQz8%Lm;Z,*ݬt%P' tƽ3&R[N!bc$}/k"^iUtU a J;g}R-U& DRʁs?i=ra]#ƈITm\e|-Qmc !T6l놘35B}e$!9ZW }W+ ~p-\3hOXz#hΙݴ~WXlHwmƶuغ:Ic+N\*@YL3*4 ҫ#?gM_3&2k9ћ Q{W·b {|5QQHC ?p |A&p_*7fyd9@jҾ'HG08McY5(mTr"ZWMWT>femt*.1g;zq}9WA R\d϶訷c:¥Y Z%<[]ebQUsǒP }:edqmfʙa oRd,ۦng\ e@rGl bkPF,z Cl˜κRU mt Y„osRl5l4`( V)@Ƞn {p|cm\v븧zXdT%;f4xS___KK{ﻻdm`/Q]4Vȼ^8uQ9|Xx EZ=cX[2.)3S4$"SvYқfbH:dߛrk LY݌cU_@TY"e])Sڻg*9UUdLa ײMe/[d+Tۄ},rwӂ cT)*<Ơ3/~ @l128_nDJz*ͽV(h>^$CCuvlZ/pʁ ~c2;GMJOK@ʷ_6n@?u{w1Q,|?# n&-‡L|Ok4`P{RmVܢt SavƖe#(LV@1rJ)# e,hCeY !by QHoCzlm.Z[4U+^HOޅ%$=ʾ2+igjGMs߲%⤰Yb՞_bV? a+Ey[sۖ}{s=^W\vEVv: ) ĮT6' !@QZJdChDND' $!A 2u]k9'11ڷtt{5לo|_T /'"tЉ #YuqOK6JnZV1/(j>lZK%E#˂îV2%e&'qv4 J)8:.@(A J$`$6)u>cdPj`n B, ^__1$ Y*!)o{O/싞=QBhȱȣ-*kic,C"h|6%>w)dO%Z/3$`KAA24Mr EB#gqbR*A iVA2cE^QYiPs_chs~'g(/ր*{zy<<Ns#`^hrx0dZ$ܨ(66,Hs`QuYLgb3cƐ}s %!2Z,) 9?#HՕ9BKO#u/BR4d#@}ZUUi|o}K'f-J9 FG%_M*ćgңgY`Xa'ujPaAit4l#FU"|z~Ai -ULVQyf"삤 `ljQdDRG^k  zȭ؀02pNŎwfMZ'ͨFo})78" UB "LY6Su˚Z\܂R,$@շ[b0!(!O;MZݪ'ṞgΉĘ5S+ t:t:cjQA,$/aN=jR oTn0j ЃlYQ'lgEwt}@*~SZq`$[K5iI'qT>1b75C=&tv;vbM CsaG fC@2ZJV~v֣Pg?9@Kĝ5#$Z1MY+AɶVZ1b#]=߂:[ZZ5Rjм͈(ÜZn&YQ7W(˂V*rVٗc~ YOg"@"g!GEYJCkUH^Y&VjL9*6iZC@ l\:C_$aCsEKgg/*LbjД!gL/G[! dZ k#BLh].DK뫉N2ψ{~zFCv侔Ơ>]4G @֮k9!HAZ? b 9g-EJGۼ0hRu<jg71v OH:>|z{{yϖqz?K}ÝXIgȚSgP*yRw]WǏ˿?m/=F0u93"랭5F/}p{{ˋ1?: 7?̚H'f9t9lr^c۝4ӎ29MY9e C3is̱!4?Mf7*=r?;2^XL3\;= Dr8U)dZSJ6ȈGtAڲ/a0ÃMHlh*[`jN^d/9QR?MNB9D9'iw2~H`(<Ŕ^Kf]P8ŀ1^˔FUҨB|?;W-kҿq̖%A\~5.07*ٱzIbP :`u|_8礼13:Zj]'$D#{]w-a#Kk^k grC d0yjçGXB UA@):UQ@DR^gmgZn~}|,@FN+=H__G$Ȝќ^$~_sLcVbn:!%END!,\: 5` Hx"}ZA4P1VƮؙEP׷by0$*qg3y[<7AV~*ߢz:^ِii!hkdhfmQ1ϋ____Gs %+,>B[GK'5;Ġ;R]Oh%<`FV?@B//;ϋ]ҥ'{{ɶ^gCw6$#$} ]bo%^Ů~׬!^;psɩQԾן֌ DeIb4H 4잫9n'bN?#Đ: op=qspcR C G+M%~n3L,Ǻzh,c뿟R:ps[|x(5x' ZZkx~~QnTH4Ce: l"nV%G@~Xl[ڂv QNu@='m=Qgqz#%y{ee02*2 Sl&C $ I]O̘+O3BGNiEMx=p<gr|t(>==m8777RԆF@WX~xxVy_Ҫg6>SNkVpT߽{/9m$J7T`iI1 i[ZeM={LZgEhIbw1TE7x<>9;dlμeX\6jA ?NC3>qk )t^@;d@!{U7Vq謋ؕ'##J>?3>Yt:EdN<:x(ŔUnw^G,k{s&b/9KOl*㑹oG!gX50>g@D3yh=lv"MaR- *U̳:1$޳o~Ee(D(NdrH`r#Z׏AGcq}}mĸ[~р1RKx捐؜C~ϡogd*1 }Zѹp%EcbzlyYuA%j.ȃmsRL_VUQWp_gҬGϘH)nl9]k҈G:韕G)gxgbٶZt>{(4M&&޾{4nXH_c|i>h¼<0!$qt'>> !:X{&cZw4CEp 2a8rzn8"NБ DX92܋Nz!ܖ~x!FO'"jVHvϘ7xy}3*w^z44SBJ>'3,,pc3?R_\033ܷ Pr^Uynz#{rA bX~Aإ`]u>/!V}ĒdߩTY{RaДPVԵ`], ԦpssO>Va5`]*Қ .ϝ*Dj2/1a)3f&'g=}5*j:w{akM[YN/#CphآK?ãL'e+擈=Q Q:%ppgOo0y-ٴ/=F;3]|$("Ѻ>l%nz8MTU+ 0!C*3Df<۷Ɔ_|O+={PNd ^{<#0+:vnp˳ڪop!3kd֊-nД72nB~vIB@J1H4Ϸ,3ph8sR1ձCxk98b}K)RWr+^Ogsj`hU( ϨIxy jx-ǣsas|r;̻;\n0qdBDZm\]_Y |Vx\O'#wѮW^ˊqmN'[?}|LbRD4JDE[(;4Iq&C$X^BDŽ)J .{,#NیZ>њ ֒zRW[Pr-s)RfX9Nkm|§O5P8<٨sr8[npw#{Mτbcr:DjTG?lm65HwH6#奔=-˂? rRav߰'*<>`SF[$@u4L@t *Ar8S͉֐Q?յ[ }p "o`#fBĞtFLtj:l϶A.6/!QE3ī֚[zMe ְrdS2e/O*Ś}u`6,Xb&W oQ[ {C2#eb@EnRVaIef:2su^ %h@e:_)iÈ; !YnR] XՈ (e8RNS9c^6j m/ 8S g_.NB:q@Y! DpԁE!OLXuAjvy>q!ǝ!ӤAFDW[,dן"F)uʫkɔ6WV$`4rVZe[r2ZC>ӳz;אXt&amekB M:siHhQ%jdEYX"JRXN+)Gti KO~02c.ZTߢ(O}_ڏ(RCǺ3{+(CꪄkHd޷X&T7axm`jrvhʗI:/!uG|MvY~NYk<} ۺZA[-7!$qsڀu]7>LU=z͗^p^BRj#Z `l M8+*q&L x*$Us߱eH~\gx|WZޚ:ݓ{^ږ,pI̬#̷5fji9b iP=m]*Jv,ll?׆F u+yrUzRG)E_'{iDxݴa=h01A+W`'9Xq My^7~1"vJ:QZNxC̅atp{xw<)n$]~o~]WTeJSa&*? *5 `vH]ٟ_+.F^Ae03W"YE:dH(ڒBkw2Idl'rggj㭥$9y% "Wv_E[Q`C=D9C䶥J*nț=jf_bR% ^(Ĉ7L$9'5!X !`1\R ǣ` DKQ \drF"c\d_?kEiq<0A]~2e#&-y^__c&>tݠdBqb.XN|K}PQ2Y!'ep2TTү bcmoO4ƓG) EeMt9:$$rnwnϜy7.I1>ꑽfAb9Jv>m/?2#e46ˢRdo$nE';ڬ5@9@p7 EL}ᝰ'T%}}]×lC_`lZ ۞x@zvB ج  57V8KI3wig<:?>;ƚA<ğQKG@TτDGᰤ `CnxZk:gF7&C)2[R+|ՠzG"!u>WkR*NLq@ :e. 9a] i ///b.^ JDs#5.wJP\JW,?xz|Ĥ%3(0 665o6Yo_ۗ RfJ0H) A`,xR#o/so|]׿ꡭ˿E[! FؗʹqBrZSH9jeZM ~.RQ@ll'D-m h}2L}o',L:Ȼ*k>8 )z-u?)<<<4ʙ8Z5)mhɘAˠb&#?~TUƍkw{]˿maE/.!~#=Y&L]XG]vB'|&{.!4{1;ͼ6e\,jb;9ٵ{tY!ňW#-;̥CڣN>#QuiwJv}?| :iFG:3l>ZGcȐWk n3YFÕ1pa09JG50!]9"E,dr6B絖y.W4jD{ zg%It8稥y'l4{I)YFNgr9頷C=-18W &m<#Uޣs%[R,O5a uhatvγ,VUU5"&&HLb {ݸ `b@ulH{F-.Z; E6*$1d 3$F =Hu NM?$tGEY{q8t93 =E-ۗ۸&ͷXPC,\8,Q1k k Պ{ [X0ǯ|o0 24, !8F?udTw8P=ukSY`K*,cy|0C(B( fb"y 44d$qZЃaAuMr_p48/ekbtlS2@!5IUP5 u7& kҟz9O4}TkŹ7~zИYll}9q~'8@52˽)qvvTOVt6X-*(¼BRq }mdǗ\I5OZ|LE=,.ʌC:"amu{Zqwwg8 A E&BiguhKRkTC-ɼC4ɀJ*dVj`xQd@T,{όZ?}sV4ohf SJ H)$kѬ~ޝ˳s[1bAn?wY5 Eg#\49< BDUqc[kx r1)rB2Z~?!utrU;+NyӋ=''YR׏er?[@ۜlk/ _W:XkLۮ6ZVˊӄ_~ئF+l.+ G۵TG,Շ)yh4DˌaߏX[żJ.>n#AQZ4=M Hn_N etH(\k٬φY( W`E!HH V1f$QJ_J  åw0s;);uzaf1^K 1sCl+[ATd?,A׎LHWѵ4Uƻ!,:9ivgV h7CIJOTKcN$?m d~d)U#Ղ/"7 X&7 WLA&΋d#l ? N)ĭjo[ZEݏ\Lia$ʬXLtIrH<9'F蹶<}/Ķ4$}5 n1Ad?IWtrijy%aV,y5v'y9EIR^ml4es}+ JĈΕT=t&\C kKyww(RGjdP- "5YTk[݊Iq|ė["ķߍ 1eMtuT nU{G=O+VQP ȹ/bӘ4fkn)S%[7_l> 2yM5BH7 >|~|Udd/0ڦ#@'8c7Ż%MՊiLkȃ*rd@Mz?ٴ@Qq<Ư{ù) 4Kpֲ,0ڹ^U})a-hZk3# ~Xļ:*ÙQxDz Y\ă@5 ͇6crsN'hd8sfyNVBdCHp ތoP̰,İdIȴTtbJ1qbx]Α>IUk^¦_ۃ'0{7 8x_caYm/mtVh|{{.Nd+>KllA1J L#L1)JudƜU14C6 lQDz9LO?{^oh|CcP7 :꿄;d>D.|5N3^ Y4'\t/!v8!f3Ѡ^J ! !j`mZ+v: qBqˍC#* ²JxY{K2K87}L虽L +F٦R ޾y#5"0>a4~O/bĽFCDPm$d򶔪5cJ:3 |>+!njWVG^ *$``J)b)lY-ЪA~ ;3ÜYaPp3GrƲvi:T9L:Pt1HMC1F&-\,sp,eе1v2cH 9nu(~q w^,I}}}E#{[U>TyЌ-^5r L4OŘLxFot\f.ZP0D31e{\nnU'Qq|}Eu)1 fiĐmϺ2Z?Q"$$k[-;OI, 2#lby24nm=&,Ζ3uБnoo7ve&!HtLxfݠrEش掂jRy^6(NLΟ*ji9fGYs$${yY\]pHi6)tQتZ>DQQ$\i9>*ϟ-Jdb-ln6:v9g!9g,1BxZ]/ipys $Xp4߫z{sJ2c!A4 J MG1FLb1FG*Rp{~g/moY/׃?i3j S+Dox&, }OwwwdE{pk 0ܳ= #^__7F/*Ā2̤s0Dn س(ÂKIPs,a Z0UT8Dϣ.0lwRƌ1@*62¯r\KѳYJiTR+#v, R!ːӈ3 5iB]W%VpuU)(oe_NijAN"j5]ԊV3""Dl'hdk)π#G' D^"٨e*kGymW\r Z~/:qJ>슧IQ u5=~=J|8ߓsx dH>N9d £Ѧ #o"h' 7W#~c+,gPt{3sCGa⇆ 2f7qwlmiuYS9 ;ۏO^\ZS?AV  Mckm*8*Pܟ%ѹ:lA&]ۚ9x":8`7fjhAU0hAֱ:xG)ZN~&BL4{5&gb){cVZMvp Xے2hZv1)TuF֢rikDs֜3 ]cFL3Ę;( /$SX&qi0=J֍dN\@{w`yVtAJ fZJ 8N}޶$)fB~ H,A$#E |vs@( , aTBb-!z?a̚q_Xf dY B$HR@err0#>h/t ;^#y'Z9锰׶һ|r mpxYDQ]ݖM5E-=u!>Riyb*%361#!A7HGHsww'KN S!0ݻN{{9js&O!Sbsn%VjΊV >{]|vY$%7WrVwDW}5}w?^Usd<>D>vM ])EadphcJhؔ&.U=ub!fT-dnK{P޻gļ7Vy(0 r/ClH@%[i!n(חX1JGOgQ8%+e-*yà<ϦȵK"Zиdoa~ٖwIdz 7y6dJYC, e5hI:{mSk+"d&$Lќ^K/OR6)z^pz=;|+\_tHU 3q0Z+vae-*vAb\);tit~)'yTJ8_w01FƌnҺdb4>qNȹw>)I@HyBF#bFd3[!D3zA HL:;p؎dF9es^y&u15`yc9L*rLZ_Qh:: :7h2^Fb5z~]j/jd_遪"O! EhK\UhduGWZx~^dmu{%ΔoeL!Kh8d+=1N#fow21FPLHluy&Ty|UˀY  H|Ʊ#&mZVQk]W6!28XWJ{cl]؆yYGB)f C 9#)Vks*lm`٣gx ^__վzc!u ? 8<{Agּ4W) E59QPVR\=vj Aj-hwX!.&˨h*L/xR(caj8/e]Dc, RJJ49 og#I9*z%%$sd`4 BHdFH~U"{Kb?l~h/o3д#A~o%ZS :-onkx=P!CVեwhVCwrb:#֥h2&"6,kAdB4 IXъ$a(ᆀbr2k]M~'Z:Yg94)? }C8H&<Q݁BdokamWAֲl-Hh U(4}܈.;WͩCa\b2}3Sb789c^DnHcf”z{(`Fǯ?Q= x9*N0@@ER2*9y=ݤ:3ًPh@Z_gb1YbQЇ'rݓ"k{s2`Gz*!/U!`-MQMZ`N[޻&n%HaeL!ƈ)Q^g l +e6FxUl^->g?(a0(bR}i)Hpga i  :4ϲ]8BHeOg<̶ox '"Lujsz*L&`CL*68Є6^Un &eٖW %]oH1M=8Aa\)U7 \ǖ")B0oOYEO bL' H ea-> K dأ@4 yr$(08D^ hh#mvmάNt:5^kZuIʅIcסvesf+q{ ZzfpתcMs@_#laMDLŬ+uΕ}5&پ+߮P[BCo༬?=gVF 1;.\y֑YO(4הЏ$y<7~\[ zBā d$e}Iۈtq%ޤ'Q}V2C0r_O4#E Kk͆qʋ麃2JвwκZ -XxQ $'ney>gzD;$q~sfjZSв"6v!A[V^܃gx{)G/_"u=@2/ μ;!>[&$he\җIܝ!8`';IxRJxxǢ"P|G}djl'~-l 1 @lyBQ·b-يm"(;9s*˞8e=__O dQy>?˟bf ̌q@ RW^рt= !zlmu85! 'Wsh@xl g6{psG!gZxb,4H##dTU+/q<䝣bW'{B:ᴆAE:D<NYfS&p3$9\Mv7YVoe Z@6pCS;GZzvs3B@[l'P|-djc[k6J;"hYG(zThuo?]E7{́BanC2j(lA2^g< s{dwm 'I_Iϖa mD!&U3hΨl鬶t|.YiV6=G,E6!cYǾx&a[\qcC1k E\ >k:O<"ԑcg/~}_/w۷"SJ=a@j¦M;eEr]XUmD|v4/ms?sxzz-ຬw11:Jsޡ-Og~gtAa}Np qep -ٰ4 EI)KFc:1p1*Dae ,X`\$`HDk ̢51*wQڬ8b7s˜-[K'?Gd9aQ*9”_'Imy%}kїB kDʀ< !>/CLt[]_'50N`ON;R!EOe&u _ثbt[_䘍9]v}@'I-A0@1K[UJ=[8,:A_x <,*UE=#zxxyb9 ߙ">ʳOI U@;l~.tHTm.Dd)@˒eD$!τh/ɬkw8t  YeYtrB88'ZD"_"I$h"Fj6D1 {?s]66˟C_6ĸGKI l)raytb2QCl\Ѿ\V_4){${;b2CB|QkEǞuMjC PyRMsfyoG<&x-#Ԕ **nf? F{0߶#L;A5DkoDߊhE֮FmxƬGsxV)! Iu>)egF ^D1/6:"BbbLulj`˳2 TBСFi3p($HˋGsY4qȵ^d+zV@eIMzi m'C93">K33ZJӈckv؇6b*,I+@dI1` cBfU;bh(D?>t.IR.UD[|< jæi{ܓN_$즰%qMy=olr׮MAA:i"pнR)訯%6h/y;%&'at=|}}_ŗa^mTg"c̒f^8?`Z+ZBR&G{a3wnrM/y*N\J4p,3BwIocO: +CWX_#nq_K/ yNamI729Ua֐98hF/ٺ/3^ד4MrBqLR2/~6òoPB͋Lb?|xO[KIpnKTk 77*lSi)g2׽9ĉJW!I7EA…e%qgЄ;0Mb$Z1Q2gTI\cϗ 0j9777fa*4= ac{JXoVqV-9g 4QF )*ʲњzDK>Diw5ި3̷y5y$1FhEʕZZnMv˂A5j !#XpgEŪNֆJ -UZB~wx݀VxLd+Ԋ仜٘, i+(`]M-a霼CY4Y ]aDjT&L2Ցd| kEgLmkfM,u؉%3 h2g a1 oz[TJ|^jr=Kٵ[U_.w?~P+^tI\C=O{3Y4-XmPeRHMX=V0u1] ;%=1"l҉SiȒ {½}+{pqJC  ZD]qFkxAgX/~ww- %)齞p[ q23Q>Dޫ&zp0v3)Hq8 82Up9 *kw?{>wLGAj$uDeEc ޯzBo}Gӑ"aEVG?8Z>sED<.=EQ=;)A[TcӼoi^ڮթILJ:hXV6AsH"- ukRIjb+ΩxBp %tE U1Og Hz׽$T#*',JF]AP,kDN{́W:M.gܟ'\hYڰB=Nnj͙QjP.M"aӦb@xT*gcL=I|g]f}ڶZDSҢC]5z;}^ps-{RD꤮oJV[j3qhsR1t:|"}. vh2?mM!g\_a __m{~ukR;C)iGs} `SL21 ն ]s'Qڥ`xa OٶACUjٔ f$ .JԱ7 A#inX]:W[ʚv u$f=A KYtOBr>D$ eA)Jw& 䔠'q7XL 2'&ڃ`M*KWjŲ Ѽ <:(k2ٕkD,ipN .U_[#>υ0?y]w !&0HaNg?~Ķ(Zձ(2Q>u[1l}XGjYwyYvI0DFt_i?ok') /"Kbɨm";;Υ'.\=v9'6 >+;e˗H5~/QtyE5V*m3gٓt@!Zp<{+ar$r]Wt'R0Hd>Cx_ztD|=Xn$ m=? `Ҟi9~A-e`0>RD*P"PYN w N (!Q|c7S $y2^C2B,9+}ANiQ=kZmU։ M7e\Pa$׋CiCZw)9!*C8\e+tlۦ0; dmQȅwQA2j1fʿePҺnNOLٜ(wF)K!h:Vz^bUth(?YG^{s6Ys6GYRI NZ-?'vzi C#4~,Fw ۇw,,ˌeE\#`cApk\T"(9QWA'uO{Sh W>!Ȑѫk4L4ayۜ̊`l)ɢCjnbd7U6G#ЋzE@E ;*@RM \n4 Xc64;W{ARa??aV A%}`_L DA>V7o(GypHt%R$d {+) hUEtN1SkxN%P]3 7,ekP8/n˫wF @(g\`4^*,c⬬ٶF9F=q oH T+ j_)]ڂugӥVl$\~W\|1GeCxrCp3WΗ:`rC&sK83Zr`ἲi"//J >Y읽IP85Fcb9wAa~^")L>N' J s=m0 !=R9k!B/W0ũk_;1<;ZC_DF<\x0 R;sg)SE)hv3qx/_A 'd 2}dDoQ19 A5Bq͹&9vEMEztToNO2sԡ$~^XxVȶ;T+, !X.QFʁOnN ǛKa]m ⼈q^h" 9uY\&,nh8CT4M mۆ;FD2Nּlۆ,=} ӎ DeS(W ͷȭϧ?gD59ݰvȶJ i:9Gjz@gls~ hly^̌ k"$~o)c}rf}7y#Pbݾ L[}}П={@kp6/+u_pyi4r꩙Bq`ʁiͳ`Kg+CrRJ#. iҢeŢK|/zh֊ם-+G{hֳpVj)LXk3V5Y!F.T%ʲI y1|v~v9i^o]9[ 7z ZAID!/Dbdd@~R BIkRJtPQkjțWh=Y-j7YPm)}BG{H; E3,TX6@hr]5> 򑧗㤵Bt:x}}RE, &?y|>+XиZ}s,hnl5H\Z7y-h|stG:_U;QN֚tanY%#r:~K|ۺYj#N\yݟ'sc]>S/rsY*un87Gtn3vC| U208dMmS<@~axM_tIƯcՐ* [5JmlGG UEQ聞_Gp5޽_Fhmې!%,X!(A^lol8/VTBy=Y=R2 YQfV{ݭ5#ۉG|+3 v!NCWr et< t~@gZ*Q<|u]HMh)D]/~R4I1iSY^ j"/:#A[.9RA[efIJc+lM@wsxؼ6&źI[Z uAЉmj2@IWRjPZzG3mcףsBiwT;_hhuIbJxy~V:͎ ZԏΤ(/wGāןNpMy{.Gw{3xfAֶbh{o=R&"T}%K8G2kt '6(Ek2Z1N97|v &˷mgt}ʀ/ZwkK\J1r,Y#͡!iz!"u)hr/^ļ9ЪN ۚ,8KpSᑎ vF¿ϻ ]!<,^*@Ӱa/GǏ>EzcohA^IP|"$Uz[ek P aIj"(a$8^VH8j <_ PR nоYt,XC uA6 -V'rΚzcBu@bR n˼ E*{ucDUZάVQZF}ۯIgV@tB)Ep5uMh$VtgS`JeAҬl tAAV]>Gc/6mVdbUQ< IsP:Āu]P[VJ m V A1 RQ'y􊼧Oǀyz8ރM14! 3jUڭGBe۽Uyy/; מ"6Y)x2.$ߓ F9EmVEL?}\r($~ $հt(T'H׵V"I#m݋NGJ)HY6(=",!I ߕ;'D|*oϻJZWE[:"]*A– `%@VTH@ڣ-i>jţ/~[mV Țv4ڛTm\Zk^.o?k.1HMuD梵}HTvȑ=\Vqu1I=2@ xdLhE^0WI8󅕩rzZ01<;J[ vg%g>籼 11kR$Ifm|#65ݣ:pOy_W|r:`ft|~_N!hQ II+(o(;7w(ޠ{$"2µie B˺"X9Ut ~efR8M_^ C֔UoS$S WgQ/>5(}z:y^@F;ZZ) F.r仌dg8h|y.L+1l/XFijeUY/պj]3yshKX#>^`]=EƉtS3m!Tx vGi>ߞ4xBz(: 9$Jik1hs7>RZWSԨtIi@At_k XsҾ2u%?RYKQo$]4vPe+ 7z9M.^ԝ@1>C/j#YU {>1ϳU<<Sq}~2=r6y.jph9R<>>"*RsJ*8^`)!fS$>_**@r]yHBop3|>CrttÀiT+* v4s{;.h@G"~Ј0 8PNoyڎ]K֙,Y]ΧUSMЍ64M: hB'y>}0ʳ穵^8٭5)R5j*I4"`慞BxY4z׭ek|`)_&tcp ,u %Oo0v9M8k?uor{zBi!c8L w ,3哇hb+]\IG>^s5" uZXP/+*皃Lps  D=|΁q?gYsDg8ŽϱYdM Jh1t;$6e+**V9jxiNmU;*׷Jm9+v`gEAo&lJޚ 0F; \CgQx2Q5'4F{@= /tg_{hQdkJ[= ڙ$7ϳAt <]hj<A'3r DOAϮ腪N4Mao\c*ܦIYjSGb^ݹ|Z >~o/ϨnҚvT vbUZ\Omwuwwm|ۄg|,ꄶrړ2b1%5PZMIy#Uq7eDY 4j$(pgosTD&Ѩ,ңgm&yo.NTRiҺ?{0]%>}b$b[bT.ˌ*sh !;EÏ8nxϛksƦ*wc*EBug&*&`>F|>8T7 DFq᭙ʖ.W 8N;tdxT>mr]?/O:&*4ދky/gJK«D8A8h'akTEݝq&J(ϪNF)U&gHT\gjk϶DyS.GlOj3ϕ>&Eּ FCchW N?s~1F˞p-mQrJ)x||wx} zԥ5 gD!]*sx9~> XՐ&KmUt&ӏ%͆|^+ Y#ZO:V _Lmp/Ke|zzz~^8x1 +/E!Ѻ E7lLqgFע%9Uя5HzӅBxmi a^)lJoIф㨐Y?/J,>|xםsP[HQ8/x }i@ϑvHR0UsX^f*8@fPGU R({_:73wf˓NEyY _~4} }t9 ِs"gѯ p O'WTS @[y}*yO$"nڋo=fW6, q7A^ Dx aƽU RԋܨsnaAO+F,<4; C3SѺbﮠ+sս9NpҠmeò.;GqAT֛t#. ~pS' @e-A:$=^FE=M~,ȋOc͈G\oc4= Ȕ zxP/c1Fs  Y+z#q:pݰ̋孹v~/\*viN#.'I;c~`IYy;WRu&uƎ;MQ(rJsw)iS8TN"#k"UR@ n0YO#jZg `5}JqJVxin"iS/:9g,bՖ0|"G񋊑޽(aPY`!DX2[kVMQ:_Fk !' ӄyLYd0 _fT~_'ww1DۭTLxzzQ|T6,FAJB{^-" U ?/rHCZ0$(4)H$]*u2NzqvBѺ`s`NubpwPmyBiIc8NZSS X%+y_냇c-JelVVZjRJ|vk4d9\;9+N*,"{(2ѩxYq(aЁX꼣#Lu.ހBtУKj619l*V8TZtZf 4w m)v/O(" ttm009|5)rl=sx:P.۔#D/,gB6D"s;|{5?n3^N-YJݣ ^w IDATYZ. 4U>#Z-P3h- ]G3i#"y0ɢXp'YWU74AvJ <^Y Zk_:%6OOOiG?3"(潽h*,!S]'l 9?pP Λ=WlxT~駟Cy0?КOVhe8d9NH?m{v+]0p#vN2$=#x.Fa htϖRZbp!$gE+;ez)tnaqVWw>$F׌p}dފaP8 61B<ߴf;v"b GBIj,7,[q]T5Eӯ"I&5IW,En%Z$C(T/DŽSw ƂQ 0畝7p 5"Q<@ 6+x<s/ʶ? AWcZ6ijϧ9eL 狿|oȹYm.-nePc_gcl}zj~:COK>ȵơ2I)Iw)uzlm^PܖM mNYhMjjxrQג|'D4bx~~ˮ;cԗQB8`?һ :Nڻ-aM^dE iuݔV;:)%g|4a$b;-m8/oSٞ׿$c9t4dͩu>U{x uAP?^qώQ]tA㊻UIlϪ"(lзEZ2g]EJZznW ~g)aIJAm8`w@)_B]rHdOyֵA:d?_^P]/}sa6yy=MKg憷MyGhVLpȲ tʋIH)ݻwXٌW~DF&7?=!dJ!hߝtU=bYeAwƟo@9;Gt!kg%J@:;gAOCzqi۳Q7yʧ7|祹g})ȑka"&su Mb Z!HIOla^:hf1փמٖ(~EJ/lXqag([4CnT4ZQݝBӻwo79<3rʾINu%45lk0 Br24!*E߅&===a OTx*%@"+#ŠaO#3 :7OM$1WG+F`(NA3~.i\nY{J,Ul ft9cyqwp=<WR,L9{A f}͠! 9vAcsuƒ*zPޏ Y^ /;I2ZASK Z]#AM03z9g )3?mJYDEǜ<'ߙ?2,-Db{cGmي)6Vl[l`μ>~*_0t_* 'g'ƌ4.J+r ^x=oiIkd|!(,$c2HaPxQFRi _pgkǭ(R)Lz )ڨkh:M8`֎%2ֶoe EZmR%-S#04:#ia`bT~G9^ק/Qm[2vjq'muâ J@"sNƜqFC<~Em!:^w׋5OסŐP-f?OD+fLJo}?|&!Q6A[yL^IgaՌY'ICfߝ7WJ2OsS}W>3=/*jrs=EpFLCe~#*Oj`W7Z؀Kh7޻bf\b-2]~R{9)DeyNjc+,"{Im􂢵}xx|5s[`A8J\f 0YPȷ2#"ҙZ!&%iϺx[j]o:qTyJ zt~% U*Zgg3$ uuǃfDиҰSۇCRFEu\Raqcꉟ]ٌsw: CL{U#?Q 2L6g|ӝ^>|0ɵ3\o;P#bό_},m8B8j9ݻwƵQIP]s*X8XnTk91wNt:M8O#r 8O;EY(U[B(w]]QnLl6߄viGb8v(GwSDڄ42B6A=;`^y#Kaʕ .̱L]s,*g"SBOp0nG7#1iɡ. Zx]n"9r@ -ZBnB tfz7BWrA ?ʺf:A6,Wꆵ krTNB1J;YN% ќm#C $BpsFN z®.ǞJ}Їw]&?wWEx2#s|RDHj&ji4CxХߞvV }/wHC @@ߧ館lNOks_aWqA%hojշMXdZ*Rzw NϦNiZUl\:w>>k8L=Q$nQe |0NAliknX}Du>pW]"qZ:HZhAי>.]k :kF=۠mR@e i\+&(4i5έ]2;3IG&SQzؔ2,Cal4ױWˠPyX9؉=Λ= F6Em|2)S"*wJuPAy}\^3 pEQz,Ca?v3t')%!640A> vP ]ƽ(er9G{1>/7a]`ĩߊQ1EG2vMr&aT;\g*4]GdΕ!CfhaT5k:msDﵮ}͓3yJ-`6Jtu4 +e !cqR.1eykm6чYW$I $HN=IVL]oI #,LOP6ZMҟ蝄m+ /j{UY2U17Ûmx_&h~1Fwpi/ZЪM1P=^$9$ |tF8SyOC+E>)cgy-sDX7p(hNszjF>+"}Ryx$[BOګpj' Ye %۞rWd"B@R.4ɻ~L@t| MQ5#e Tn:(YnT(4AF>&R< RH°4;JMa@i̸^Nx|x+ ;;nz*VᒭT!-Q@$ad$ gMPҰ2N5&{#S91ZMu/Y"bOa4H McHCDㅵ +W 3 n83jB9X yl J2ry]yz:H6\(y޾t|oWEDi40m7րȱE+=? ɇ=~z4Ј5К U#'9;IDENLgz"L\ɌF)%)+n˂7u `~agpk9*mP\7nW>~Y2g0c`X4Mxxx: @g)Fsweka@q55­I+4rSI|\ SDziEn/q+5;b;k!E'v%Vv?o߰h8wƲ{Ļwoo7JgA ?gt2*F8 y[kT wMqh ?S72hO%)YtyIEdLx~I[h>['UXAd4ɴQӺrq= "zZS ¹&R/{= G=^:xBޟBC%Q1!G :*5Z}|֞&n70ϋ)yQI7 fEuROqmcAYV yn{jg|h٬ t?~>?V{ =yhs :礤3­< l!t> yZF֧N+"J!hQ9[]KF fޗLhN{WЗ*ú ;LAX`klGEW1I6hRJ gFj88V0 {ʼmRxx:QfŁB[%K݈GC^N,Ͼo71F.4I$^5wOH1ۼY!ȄDq}Ĉ>^[)tcam.׫6 pW=U)e_T^t9Gq.3hnYyA@ug-R0 DDeZ])EG SRﳇo99hQ&3NAUtzxrH|>cgx~q:M<r{ n؊C17&H} @LI 9~ΰ\\y{F{p=pHيL[q*EZ e]JYDyt u A,a\W ~|ѥt 󎆬f.*z1&.P|_sC8_ΘX})OOOA8 )dq䃃cc: w(h{%тtD`y@- BPUsiYhDEb 7$W,fSFD<<j)VnF:XQc_}ʆ v.7G^1ё 28de{fZ;ASaB`8z?5ϳx<[y |^;\"X'h|>4NX?෿gc 0wwxLH)Mflf\{ʼR[IՔ:Micr4_e$v9H=ejȃMgZvWokǘb{҃`_@ƿeSKt/YLFG;w92d8S?3L?,"hVReiȼ|6*_Ցgh:ͳ2U׳aHR9 @G/>S”1ſ84 <WE7}U=)~}.NyvClYVp& ~oB{܋q^ bTvbe.UYkzeQq5р۬4MPրadegB/bx~~Vo`CI6VP۰mu6{yy?o>Q)K<`ia7yAn8HwvJ@7+SRmtZ`DDoq M](ZY␒A CaxaG4,>]S8.}+{y#bmVZQԆ[4_kZ8"Wz?9ʪ2"M<t{je C" ??ztvK8d,vsɥ8>t+M_'z #bp~SJx||Ħ?H=U@IKN{덳w0 e)VFYڇ1ݕ#aBzDF9<X#wx![hFp\\ko[1J{Ν~WUS{=_ĺH _|BES Q܏Ju]7|X[T~JG ]!etpeO}l=L;x)ZL@=ιq^+B8?eR0:2*te!w e۶gx~#^_@.}^_|?w"zuʮNgи[1 Րa`3T1H |!3osdDQ#bM\k m[/gLr X`+a %o*PTX w#G'2+S81%|i*%&&C4Vk7.+__+MRdTa:ɴ#nJ'0Ĩc+벚8EaKSkSXD? $˗7-z $2EϋG* 2'l}կ~lX_mOdQx:Op=l+ت6qҵuHc?ਸ9ZkyvG~Ʒ%v},/Q!1P`RRqZ͹f޾ȑ!:T/ ^Q{5O!1:QS Dƀq̸ò,x+|~,3 (\c_c2/ ޴gۊpɤ&붚xzФ59LPS%vo73 ʶHU5y O{,޳UCmn ;֢ c) ڡ9 >m1H:j(ѶVt 14Qjm^@8'wwwZ? Ƅe,xNY'V9L=˺f=xN/^ $ Z{"F7QII:!F#S:mu}e/lX*15ᝒwY0ުp_q>0k^m ڤA?D* _>A e-OH]_3ES4UZ-En+ӄml$Fx׆i<My2!H`Q[A TŐAu[^ ֭6 YtYfU7weN69jZ v2C!0u@?x !Oj,c728U%Q%jO`Mj?&&Xڀ3cԜn U7 5www9 ]i`3M_vZ:dSs@|{{V.蕏9Ɯ0@I{7hH pWse{jԂq`YKgJV껶C&&DCrЄ, J @\dAP.|y:ϰ)Wy( wkv|k ( c!]bPs 6MUE?L܂v};W&y4GjMjkhr>c::C5LNJDGs\$[rޑ'} ;gxo}2<ɐ֚'YcQo (48)7w|̂Ayy5.dܶ͵ko"bH͠:gǣ 1@b }6|_ zRĺlS4 2rruaS=]ir6Fc=N=M}J%,+ڇs'rN `mxh<*Io3JyL ~y'#$c5)|d1/f! erR/!Z*db;-2jYϊ"J٥bp'[ x~=*,YDgvY%2Z 1J*EǼ7_ v`)">YrFͺACDz͡A|AY ([5H'X :5@8C;H zi5w^@kJ/%+ވvms-Rjk)<'@Ax_W>̾7ݡThVs4 瑀hh ^'2%fm_WVQa$,xc+0N';&H~5jM}I0dh|$%aKOk/vrVJaUzNܶM r/d *ׅl[) 6^~Yn[ON)4YЂZuЪЗNaVV e ~'co1k9.XИ~O@'aXuyuHS)l-"S0Nfwgwۄ[W+mݶ=heG{7OHFz \픱:P"S.8p^:LӄeyVR}qRR !o}A37NkSBY0ƿpd$ƔG_sww^h$ʻjm_"ɂ2sZ3#}ڟG" l9hZ|Qq3@>7NJ8Tv~aYe zo\TR 'M͂ $Y1ҩ0E7sdy. ,P̠K%EEųrd)Zwă g:Pi!bxy}E+ V5?GwdQ̌~n8a:iOm'h)FZqYVlfcKRV򧔰-&$[{,)3eb<ҡpk}zŔb+< :eʻo˦w|p%s\ ;Z< 9'۶!Ālk,mU>՞YnY| [ʡVc?FPg;] OJ ?|痗+k&~񚗳D aGU,?;ל?óE)gk@ j;a{ g 0 R XUɷ&Z;7$K""3kFw𐠨#p10 Li $B+DP]! avURɓ]?gڵk#W֭Ƙ u퓾|> y<&<l|rX4Oۊh謅Л*!*(eO:>;Jz/b4%ۖeݻw}(?%EO \ ~zDjd_ mA.,{{ȃjWZy'LmuaXҿ*=ځJ[{}лhVQ p7GJ A6 & dYK8f<Sx~~O5.Jևѓf)0^#t4wedJ8Y 91Fob|@GJk6jmՉ6UU[ZdV`$˞Rt)t:PBk jgI6C7v!GD/Wf̍nc΁Ne}um\ȯӯ=m̢Ԧ3IQd?$`yDaRL8.k,`2,yf5l$ TsL Qڷ*{Qh3DhCQp<ě;EvY`"QtYx%th6C<_D[S1I 9O$um>3so|  vzSZ %P#=n6pv;N#;=oVD$%HԂCγhTiu¶.(Eq0] OOOxzz ///^sM1(J\zvNI. a\*by'ٶy(5^Y#W08.1ÜbōEElsۗ0YܓFK9aY@_6VQM"rƳYgz,+JVCb(DpJ"I}6m_}?2۷v'e&uL|BpC;Z+{#;}谏>yieSs[8$ŭ $5ZS0aqu 9léaY7=$#k! !(]춮,H%%k C2ڜ˨Goe35Bqw1X|c?]ΦrqW_f'Lj:MȨu.:ܧf%=@G!:Mbӄq, qܐ~MroouA‰. Y GӆlyFb>2Y/|\~V=FE I8NM[YYl>#绢8sގkؾᓬdJ4&RjzǼ,Tӥ5E6*&mW>o?~4(#cfA',ւm2fwۺIM]6EVI JdzQ8"2KUYVFYH9GZ*H1pX1؆v,yqxw>x1)2Iqo#:l&+=&\\g/"85JkY5,[_Z5 _$ZJ`Zz`Fagig6Y2 Dk v 6}6$,ZƐCT[}]A ievA }CN&/x~ DEAɺt( } XF{I>iAl^-\+m{D*u>Cep{L4CW|Y6\'a߱hx"k3&yJ8-34Δ':}>6#c.fѼNCAE~v6 ]S;ֺv2pn <[F-ח/-6z=D>*ccpxoS̀ R^uGE3:-“l*noC'"fflg0n!^PBRzc~z9B3f;[tyͿBT[0RZ^ÔZAcO˿5!vm@׻[4!|GsqЍo`!(?&6 Z| %KNLI4-蘙mCI6Q^@>)rh/eMثm|V^#qJ-Q9` ٹn\_" gd#:Mq<CAO<`69B[4 ByeƘY:$q躰*7ouc̀nec$Rz -Z!o@ЂY']+s[r=tl4 v!{ x>y|4N^QU|a#u@$rc(2S,ᲮkWQhK ȁ{ǚDG0g3 $<~C ֻh E4%]iОIZEe~C h7ekqe1|ܜ0ƈ}zzW_}%L*`>MP4̇ A9P \N(9Scg/s:4 BcD6R7VJbwBh6CZs)jutU0J[7|I'f$ymiB6xegY@1;MœO'<;taφ@XPJ8ׂ|'Ƴd]ӎs:E|,":. IP<+Kz2^J)jٽNu!yHP$ip{} s y`{,=zQ׆&f0t7jBr \V^9b(AȪwÿygRSXW)o޼AO1g6F@R¬N/+*K\$6Y5)gɐ1XzLX˞D Rfmb_?P mhj)0%<,0SZ7!Eм2 w ` 7MIlq@GXy'Zɤgi cJihVUaSٜ+v<ϺK P"q1jn&q˲/. ,J)Xɂ1њN!`mGJ }Uz5x+v9)s ܅Tp{6{AѮY>Wpz4MUq\fޞ8LIHS}$,ZbmPc0~{5\֔  4RvoځMc؛{<铷 FsӰo֘rG<'<8c2(y=[B!{Ï j lYBrB(#LYtJ`D)O;R5v[Ŕ",ls +a"jdz7?BĖ|>/^s7le #鼘F8(xZG5do߶s@|92=8^eehx"rD:rBG5a!vc%Fp4xnJxN醐Y0$( iipQo@ÌYɀ#߶ W26B4kx jݰn+ Ӝ5(CwpJ*!jA.(LU'7yl4#$u@mY%%eX`;QNbZ)7氉؉]>KY袓jiqJ mߣI?"ǜ1(BruQ:%9#۶^*[ŧM`e"L^{I1ꡝ@Ѩk/?X;W)n=~](xt8_ÇvuIG͝0}D/#~oLq. (i}sJ be^ay<0ϋ~'|ǘAE~wg<|ެ{$Sǂtvh<glnH/]k}ݚXuRLLx+R^^ޘi<1 j0Ii=uK{ x %+zQ^5E4cojB2ko}_ `N-߷_Wm\ƧEV딢G9cM:"ZtD`-+=Pd$mP\03ᄬ'z|%yhnUnQFd6@uLfNMvY.rhwۜ6!KW|RVI&\.X\m+rV_!8W";5Y'. ,( hj[LA ٨*@̸N !bֹ"Q?)z%됯RE@8a!i6w!]P>% y8[D1D*(0;yVlUOXD ᫯*>F(dzf<}}19"G-[k%+luwb22"5wx{:baq[)XKEC ֆ&' 謱dò!=g `9,8zXJȈ&ff;$y)!lR ;V'ǟ3[U7)Ȧ |1O@ap}}لT8}1D Qw޴caJw(<F0JmoxoT|IV`rdnZߔOT8<"&߳< I4P@I;s(Uãbes*6*W\IlMSc41H`@%ϟ?Z{0ƁO qXr޻;lׄHcuP<& ($e*] zp:^΢Xe/|||0 "C| ^Fih LB J~7޵|][ŝ%k|찙?O_![Utx$Б|.<8ϘLmNc|Jd`< ux$Y9Z۵{{$q%:z ~:)%mhgٕ`',bcؘƬ뺍Ma~cĶ'yo;3EɄCml}.nXv?je'R-`۽hF1eH\Jɽ8h X$CI`AAQDeD ޻$@r3iWV @BBLljDi֩S*kMuK΍ff\h^R R,apSY\W#~vH̭Iq^$]3A6 }T6rINP%E]Er, yU9YHYB;iYP;Z\Mz:ozc=l%ρ@WP[X{evoB}Ց/=7H*5iʦ,qPnYw\1IcwT& K:(BWBh,7X꭬i 6@@(邕Z,I%`ƒ표06ܶ]!c5I 9D&MLj΅! =IM$w!N2A lzY;2lʘ!1uCȎeqÌn4͸^ȔZ"s2л@phN%VO'!bk, JWOC|K =kq/97oҒvXVSSSN8+qz_>H(3mݗ;be~*7nׯ"Sb2']6y{U@д6|^&k޽÷~kkI,=Rld0̘ tBqyMRO"DuT- Qe^ibdLF,^a_gkv}J â'GR$'CqV)޵ۀ>Ke.[6!Ay?YG?QPH̃[)އQH_=7AkWrf^=|)}&3d=|]+ƨ-ca [[:h ÏAc/1u11*.*f1jͧ!lֱnkimk)شAbk 2^K;<`?rB3t:Jր<Ek:`{O'^o71.e{G.xYz$Y蒱<9뫍g9 xm.zc[E%# x'cER20G̵5CRʞq%LJzۘF-|!bp(U]BHc`gMcwd hRa` ^sg\^/=(XR{+18Bf eٵ݂ΝFAbY37Pjǿ|^"Om>}d6zUKT=e.T<YU9irf4vY:V߫=/DܟQ 3q$8E>E$GY;^;`$ګ{o@${CY= 9^9()* )QMz) Mވf5mS] K#˞uU[j6eNg^xzzr$!ydsƦڜE` ()5iI~M%gUG%唰>GS(ƨd1NkM=qy`{^ZUe>1DjvuTZ)}?m$^߳Κ& Yxd°^}4Ox:awvm 胖k`{JQa=5 f(ˋGuslfsg׀DIrۆxJC;EGz q;{]p\fCCF lEJ[m E-,=pE|.]ŵ2qVa=$m]b @lsԙ_:?U8 IDAT?NN/~V7KsS_&IwIID't~ [k ,Ly/JS:dswNXyPR[rp*ע0lC+ ǜ[)=TNi۷oQl\AspPmfM)b3R|Xه*e̠42ZC+R4^.Ɨ33?O=,mIG(O7ci; CED(r ۥTb#{r=KBzb'}3i)Q xgo$nՐ:`_1 'rPu."£c!N-*TuG'8( ٺ\IyKxzzkAk)E8KL}T(JxKo t#PntۆϪqof>g2UFhPtrkY0IuE8xۓ=b.F1aߥtQbN?|67oި$w7;~]rL&.d2m$XڸzWi?^Hq 9?^Wg[Ů!;K;%^tO%:r\gXscG=OT칁y1AsaNή]9s$4CQy9y(HEoqQ;lfX!Sr_z/mW 19:z~a! "t8HmJ6 @;z48eCJr6SմI.qL М!)Gӄ鄲m?fw[)vݤRa Qu6< bm]ئdi+ņ݄OO8?=v#dɦ8C (޾}vdzL#@HR6u_ڬ# :dmk}VDn\&|k̋ %)/Qz%ɚqCfzWEBrzyzgI9&K||hhFEz1\ E1PAj+v$7 D5& }BqѮdaT>pDra|ºdA*\t_R6)L1 g?KR}CGt,,(p3^UֵD=rAvEo<<էP1R:kكޡdmE<qqӵ'{<д)xs>ӧzUޏM!vᾕ6m<ͲBaY ^ASKJjCWZ?˿?آ,͌|a?tg5cCX,R6;EԝQ0'1&Ahi u.Uz?rǽȳnbYf kC~3ir/p\p_7WR51=02dfCe^_#ϟ_rA Ilݣ*_Pk՚杍byw~rr8ӧUXvO[Ht/Wv8t`??UarXW)[Cm!P6WP5St)BLYBF汳h IAgw}gꤚp R՛6QE=pA}WK܍aeua;!vAP+1%ِjuf~JQ~SISNrHLfZAJW4Tp:"ok/20(~M"O?Zjx 8i`iDBL :bT;6E2S3&V :Dwg՜]+TQ'Vi`k!-ZtApe/>p% ^Fdz u]QKAA{<ЪZ啜b ض9e>`ə=)AܷIЬP`75胴i;(mMơ;>ά:,%nj}e[q7CW;[vPB[q)gRѶ9G9tX6b&zzy$iák __?;, d@sE~H?^Fw91͠z+c<-SOS >-XUyo>}F`vr8v"EA^xA9*~YT^M`"{ǔ|)p$F>}BJA8S?)#,%އT2O٠y~0_!q13Y%rcX"ն| U3^"NJ"ln%Kқ~LmSGdQ~`Slnভ[Aium^^o+WRP{@M% .iHJ5蚉Q`Ų%u\߷#(-wNF9)D#bq c`8Ӟ#y^qϋ8sedd6?;ƨ3/HMCGb_~a yY$<޿V,rwZ@]0P-}!Aaal@.?J&Z<8*m! :'n鍌AOD|9(Ȩ>t>Q$ilSjVlvL?v5@{¥\s ļz:ChET2H4) ְ.zo(yi`v]8&o/ }L)phs!qߵw2azClۋaR[ioPSu7_2AT(u;N 9=n !`؆}yG<˗x?SgzbC]T-% BF{v}!Ocq̐qXf[;в<ܞ[@{Ă7/8.,ھ4ǿOQIR,q 42ݗEg亁2=A>S =#9V^RĘ-IJ{^c.D \/>L-OOf=Nxzz( :ߥd z1'=:< 3?YMLΞ)[NDؘѶ~JWz`>HMi |fx&@ddag8/2r'~alw'vh)r̓i.bzEР'J߻,:dzkJ] Z*8PБW ?7/^p/ƿ.<)oK=2' E'dcvPF6i"\)aqoDRJc)`KmЈD͌RPrK)YkQ@8SN xeBMہDCK74)K ]pry}vWrQ2xw0CF;礵ZYYM 1++A/o^ )%7dm`,jslx0!HU!Q[:?!oAGub$\ݻNǣ8Rx )GBeB}1m[N2V4hS9oDZFc g{{aY,l. E;KQNcGy.K^th{gnf{\>`YlSu| IIe}w lXw% K<ݐ.?;3y$N z-m Gۈqo1<;FMEֽ;PsV@xMV}I# Dο ׿ Ȏ*lJP ךyDX a;<_DR[YtUG7HFH/dCPcP$_3^7¬Y,ƞ a>rLIsJj[a֬DFoTfOQ5/մ1:^P:ƌIz6JX8p*7h :і5ie#+G~Q=FڿsnLÞ2+ԑ46Q d)pSNX,Kw%1@P$qyOOO*.#>(1htVrSJx5bQP- ,O W.W߫96u2{9.bR7QC3z`x<ɳ, j0 e'\ _6%ϡh:p0HŹvlȄY8{'=:2h?uMJ8MjΡR̰BE."+xuj>ЫTTD P OAC5سV5p>؜Ͳ[K ZR8 =Clkb:LIs޺9 uZ䪰gl1%+O3:[Q.Sm!z \jD0ze66B!/*{0Pulڤ] ]֘{>!& 4*G= ޡ"I!2Dք+8$ZL״`!~azYkon*`@rY}r!?TܧwЇ1m1hL9>&w6g~pGLyELb$`ã~H֚bF"9g䘰4ƠA$8{$vi2?̭@>,3jmt<y<ӗ}YJtLe%1OxUmw?&T 3?AIQhwZkQ=y: vNO2/9.}֥6!75&,u$}-B̒E*%ӯ![+(Ǿ/}d8r/AVǡ޹+OIϬwXհ08|B3A3ЀQd) B0y@mne@4=:HN",u]є׵f51ikC Ё)&~RTANM9V}>:P﫵m2duw4.Bќ}7Bd[M*ͺiVUf9<@Yb`P^"d] 6|0kB itt)4M&(9>kXjcyxŨ39rV5ˊ̉OG Lt!RثOOgԲ!E0I~n^$Q.1=@sb{$2?M_, ˢv5ѵ!Dg0@_UKxvP31HH(-۵I >Ϩ ee  yy'*nћNYpqK"tmN::.׫e$d4O".U;($bLHؓF0G> elBP8fUg1;fn1bVԍ$wk2=;ň0G:cΓ %X 'L!H@E>%YFӖJc |o WFZK1bќgUD/{K?nh/]oM=2.*z fˬЭeD١]6rA\l)B"l2Q13@7hS0$P 9T Rηx$4~'Z@jcȖ=jP`kkƁ`t\)ts#MլqQEVg`-u\ {&#!g$#\SLnHG+>z7(ۃ"_{. qt,kaMn"mG9?F|3c5a?$`/ξѾ dB܌J^dǘ ff[ jͲC/7[Xe^oZ6WcD0m;Qz6N3S%۽OG_|L_|軖5f[ nlo_m)}Ll>rT[iֶ0O CԸG}f;VC*,O{D߀$Juq&?GJ4e;YDӺcs ޥ 4Ư@Y FI#8LD٣+>L2x|R#|f"уtA, yn$b\M`mjyRL]?  3zGq>i#,ܣoEN:ʡ~ 1$2MpTa,(<: ȼ䗏Ba t TxkݵVŌC9Y'^Qaό=g9E,S2OX 9|Ѽ`OϦe$F#Ucwe_jÐg~ >HAvdqhv)CB{wOx>Lt[q:xe:4@ (sg.I3ˣ;#ZXDP4⺳f;=:5v!жhC~??!")CǒtCꗰvr-?`hZ{s8h[<fCx.n)%NԀ@Gx =bA5ŁBy9fmۆ^ڿ?O(C?7|kuV|X|^p̠˲\LQh tª*ݵ˜#^Nf71[n7OGzqO$I|~1J)ـ0|&HX٠w-43!|,sQtwɞ4{lgr;q)>[n 3m>Ǫke-{Ak@s@;X Lhyx4uFpyMl9;q*އ<ڞ8n9T\XvߩE1wUJ{)OM؄6x<9ESg{ދ_#\o)n0}5BoNŽUVd{LB,vk;ZkG*//X~r#{\ȓ95M!f'!}2{ïhjӴ=bǂ@J˯ep{_ 8~Ң_@־Ó/50~p˟yD(Bhx6s. _lw# !Mwm4@DP{GQXXc C5VHTxk$^Rw3CC$ k|0Fg5ӓGdGox~ۏɤ{E1o_̊a۰)=ڭm $p!xvY[/+)e{_7A|\WUӔ5+Y6lc\k/K|y&>:ȐтFڃxoL`w?~r1{>_,yX] u`L8fp\˵gF`1q:0gґ`Z:F5~W jh\-ۆim}8}M˾k9}+sA O>!pOvA;"I; :][GUN]&ޏ'xc4…YYitip? 3E|i[H_mg/Fg̐ =K̩fpf!E5@%4Hv?dRH 3 l&Ɍ< ˀٿ쀡7"wB{.Sv*Xb=2믵7޻?χ s 2"H%%^{|<= h_u$)7:^u^9'FTBG]JC'3bO /: ZpZJ:JG+)1E{"%Q J ӔΪ'q28gÓyw}ct{ jhoA ~M6"h+ߓG<+>}A <%{97|ɍsh\C ONQK7`BsI~]m.>>a_j7`Ao@ZNrI~-Ҩ/?$*4&e$S6]׶_dAcV6o}/߸6ZEi>z0q~xw)cͅ`1s[wM}R9 ˊS4Fb̅Hy6flv ::D;x9#[{V? {򐊃dNm  a<}&Jw4:M:jn~.Zkl16~}O*$~ ޜlU ȷOi(|2h 8]ֺ{ώ ٜ2?Cd{AC.#:;Enycļ̪) Q$ !!P>`Ӊ#V[ AHX9{RO6zk//ض,=!a6 j+J`  :lLֲʬIB)CA3ZxH3g\"%}>恠1ǡw$ rrp !r& 9HZP+YyͮyUQZ빎>ر]F\kOZjt @&Gb 3^~# `[7Z6E=7[æ#iFCaSʡg LßCVoxc hgtrn}X 7.ft4vzeDZ. Hΐ}oshz(-Y+Ca:b -jxal%q}xq-)V QY{hI/ˌU^ٴ$B 8OZ j{>xt@"Nr:2~63bE/)z|>"_Oޡ w:ϓɣs0StS4^R3Q].op~R&cr7,XxӉA.}tV/]o%I\]s̬~'=pV(H>. ݙ̈pw0Q\ *32>]S#(hZ$P(zQdu 7RPgN#eYpw RaDOk:; 8ʘfk0vC@Ν|κ?3RDV(o]CbMp)u_ Y.?F@jQ 0;}gxt(Zﻧisض DD ~<6n'Jgmt< Qe< =S AH'qa}n,ӟ_.7j3*d7O*VXPiŅ<<v7$BwF6La3#Ɣ ,+Nzrbj#sd*ÔRPrB$8$? DķMoUmOOh-Eml1:` uyȎZc@IJ)[q4o6yq造)4 18;9XONe pU`=NAjo ^L2 ;L~`  ">jlQIDSNQFzDԊ CJ)(b)D yAɳ99'7€߼=}SJ&e-%ttr_Z _'9P]$Tȳw&;nNt`1!Y$xz Jy4 qԽ 3xd0RP($,;Y,`;@87ץ_@ghy!*7<:gƘ{@#{ O@S!;wLuNE9s2(Q0:ޱkө9u`-Lږ{ Ǯ5=,_/aSM8m2Rn4k4tҖ2j$>KbYFgun Nddw4 x"A>YqȰoU/}٠w,iNٙ$HnE;4}m@#ލu6+i{ `F:{fK4&Bhe+D6{~<4 RܷĨ"/L9ؕ$, ԰ļn}t0e6#J/^'`qI0o cxGDN">_DWp^})>(dB=<ր{ ztDN)4꒰^t8)Ǚ!D_~utgIex}dc3:FD3 9>::kĐ YI8C5ՏpԊ/o S#hA$t\ւ͒-HQ6}tՐ`i[0@:j):({)?/K/੢(cg Q5@7 ́eL,DWϸ&7SXYY7t:6[D$P7SǤ+*ι'-ۓ/ 5!C]l׋`R4=Çe]ը񡹨hFѐG#oz8z:*Kծ; F.0Ԫ]㞒WGEWx<#Fl"ltk-3,e"sxm] dx*M]9~!xƔ=s兡X:&**d@ʆk)OQx1bڀZSRqQ2^.J;m  h#9AɩN3p'+h3O|1u=ZwGD6BiDF:AD¨b14u))e?\{W֥Ky+mPBS'3 &YG"[m ooo>!0:|}.8j)5;f DtwN@yeTr'nR\fSХPaomʲw-oAG{&@xSD?8G///V I+-ӈFgF;"'EH8xzxQ_~ՂޱAkP̓ױ?LF,QteR )""%za7zX ǤL)5 ύaR =A1  ,/1zQe?0 >e)8pO>ۥ6V$i(KQeFS1z1b>/?519HtDݹpN_]W @JQmZ:Su :ƀ#c)SаQ}vQC69#?D<pVG$FSjQaxO1_Q(mkhJӓ͆}IsV 8vs4eQ!eG<5T4A$!R @xvY{QBkIMkU.?$ʘ)y Sv[%$s:|ڛ߫ze;06uwJ]ƷY}eqhfgr6t- - ". `(LHٌQp9ys~ϟU|9ʨaE3k/0$IGF?}%5XkauuҬ_۶-*G%NRz:/F\}A{!%EF[LLxVGFX=:"Ꞹ3iU3pWT^!Y)ȱ4GiXm{(! |3zOs de`DFBҋi#=>A{Ѩ8TFRj IDAT z~97?{asZftDG(^Wwt"}Q\4()Naqg(2ޣ+ n{+#a{n4v48QGt!n Y^-.V-Ѓ^}hG&dzëydm=GuI.FQQQD>c$G :>$Nw {gU Y{ڠOģkV>\#o5ߋǹjڴ)Q)ϔ.{#xv.ESɌfO܂#u Y27֛)Q0jhQ(DPh<+6kR|3Ug4t|L?!YڗVƀ*?;v|'N7f(TBW YhiĪh[}zL~{fw+[ m{u߿^Vˊ}>۬xW,,ShZ,hp &68jvKŔp|?p-y%^'ĵҡ FB:XkQTi(t䄥$Eڒ(3,Q~Ft { (5\%Z[GqeN%Ĩ'ot6Zk 6z"<qZ+%iNXp^ȣ,uPdq|is}טϡݘSjFٻeYn<|xJA^vȽao螊ТS}Z1`SH>:.8l~G!u]XJ1A}wB7j3p.x%2hZ3Q@>=Hur8jYGiݞgWĈA99Q <+DCcF=JOn|nσ"ZdJ3輗 zqT_%^^^_0~MXLIT^7,G<ק 2.i(|<G~ٓ}(66%VQ>$q#0[r"f\'_'+{Xd]V' 1z}DB_6 enۨ7ሃ&7( \60F%i+%YQ%;feyh\# i`nv}m[Pdzr0>==M ]- BedizzzT#i4&K穙Q"G][8s^xrfcǡ<*&)zcD{!&+g<83蚖M͢eY\V#UK@3*\{ȅ|Cn{Ե0rDg@G(_>gqOZFDrUJƶ?,,i_ِP3>3e v02d;r(4zs,zz6Bqgoǔk0bEӀNQd6Y ASur}] ֢?*Y`8 &hD |npj(ѷX~ԩn C1̰kl ЬN\b5t)Ig|xd|`.ؤRs[ñxlV-eff[WH" ,:_;\y<c|mұh N HL{nu+9);?4&"Z>YHCP|1Od\1:7=lM."Zh;, cϥД@#c5tֻ 0ǦsTNłvljpJsX09C@8N}[rY}"2IkiQjt#$s|)tKNx35fD'~V{o:G1YQ4sQ$8=>n. Pgrt u(L̓|GbAhx_5]$#Vt!N~\: PJ8QN~w5b5TEGGEZyDj,I{>t g0#-f0n;tXJNPiƂҐk*bD'TiY7#vV{Fs֑Ar8zǗP{ú^KPt gh=Pqe˃;ӳW;GECT4>>N/ Ӥ=|e^4po[197"gfND~>/RspH]c;d6 .GH5!mkaxHmXB>^ 3Cuz֭7H:3g8~fse13ԑLSj:sh\ӧOӟrS Za_}GDf=M5"B|-:S[u cb=I; ӥvߛR\(Qm bFex"gts#S㮗UCaƂgh|ae<]Pv!ꉆK<5-UMFm:i%>d sd)eFpt|u\ees]+qmS@O 2 82Ϳ?(C"P^MۯtycNu}@5qO'W@4Gw5`7,?Yk݋W!L1 O.4 YB߽wHX21Z\ ʺ56m.XK19S6#CCa;O O~FHcǸdlMAoq{]ps\ TBAFםkFhvѺ(ޙX8+,tԍ~A#2E4ƍ9IyxD9x JhĔN$ekȽhhǠ\VeY'~_W]u`mF5 PG:&uxl.Ƹ>ݺ!i1-^aH+ZYA"XV0Cn%'ՐߜX::y͛sBīOBΎN3ьec80SNGmhV|x}WTkUA\G؎:QޢcCCε}ծd9903*IHy],! #exTMt,'|zZQ uK[( d}wvbAV+~{×/^L 9e7 bʁqT{8:J\lsд #Q- 8Jd(攣qiQEG)u!pF ǁB:DXw`,_|h9rl,k Di'vTm;7|oo]eZc٠bK>Ǩ;g @541vj5tBW[r!-~e ?3qYqlf VnWX?YmIG&.`!ֳ<ٽK.Dљ2#ķ,?.4&;/يm  ǂ=Lw>Y~"B6y8`Q$DZ5(,YBPAFknׇ`(9. dh-NV~9eBQ.}*ג|6G41ka%; Py.V\V 2vqC^Kĸ C["7v 9mxvGcz}yuܿ2>wjd;QccIuRR2_:zb =|8<b|CXݏcϜpx~E y/HQ,3{ YQ<Vtm)纖idTK `7*mxѐIC½cte+:6!;*w`LSxb1uUJe]uڝ9+\3E%cI 29=W-qfƹR D-VO9ж<eŏ?% nW\/+w5K@mmiW:b6:d2MFUtۦù*yPg;gzdA0.wjY/!΂I1Y1PdpZb%UfS&y{wChvv`أR J|0dgbPk7F+IN"+V7wb^DHljH̎ r7a(q20&s }Ĉ&t)/7u .+.*/g4HjZ8#pYܖo19ujjF-ۿ{'D1svҵ$'Zː&̮ށuYm/F/M F |.`ɋ:]!% Z?Sk7>02~tN;W=xt47 I4(KNx~joNqoq"i&hY< {&|F#Y~vC'M냮}8D)%\o7<~NN]0BKnuQcdl-:9#o.lJxn(]v]ڄ,Omr٪|C ooo*O^8և}'*WT̊9lzdԽZ`~kuY$bYF0%6cTl%iHP$tÅh'cU۾9mud(# j5AIhc]2E蜍k)d$9.džW0ת}Qq2r"R䅅 G!ibk#2patyiR$)9M>q IDATs@!>;pJl9"sNW7֛Ahvjt_ˎ$AbhvTvǟ_x~$"Rx4?Fe览xϜѣWkC* Q:[Nr腴2{s>F >e]~ONt]VW-lFZFEPJq{ ꔔ'.m/6)"$1TM&bdE ƼMe:>BٗeACzTlW!VKQF`ڙ[hnY3È9w & q bSDkvŷ}khCmȌOHw%:88skE ߾}Sj4ߢ.fLJI8B煟KͰ-7>W,&Q4sG x*0Np@ѳhYtDb* AGGxjǹ}?}eo߾Mi( H// *ָe dIIu{JQ׸oI!~OAR X@[,tNḙqI]W[uIt QdT8G (\D&8#!:*P :sddnKB9x~*hL&4UEsh4u~F)s4BIܜ*0~z)Y#HhkQu BvyRy>yBOVv6֚\U渦!ښ=ZKeKHqd>l9's\ǘ0sfk2ZO1n7={GQr] %bt9E>|^еۜOL{I;hԭıyNן$B?#bgÔ#z~ǟ+\%Z9ׯp6䲆@sueXGQA09p4!cYT_ߜ|>KDxm*Xsu DGנ#; uȘµC=ӁvĖ $D;5*fA_Y=||~GoC'4G zW_kj!p[:|~bqL*ȉ6+<#C6 ։ g?o3qscYR;gd.aoucE!x")>% f1iYb͖ч: E'c0f};ٮ?rSR\Q:"aEyF4pVh%w;ݮkf׿^!B@9D!dlǁ?+rI@2*[tƜ)6h-8YFg{w5WFZQXJ&I EK,HlT#>"S7SBT{A7f[mxoػ@~h[tq0h)L.Fb>*}e2*bDE $0όNcJGZ`XtX[ק|e]m@=vb)E9Gj=C;gelcj@U끯r! owtKйqcjm ;^42_ۻL׶fc `?y ZkM"(\F.^+# "LmیztRj(KVInJY?hs]W\߇C8tBSr<-6mX v%:Z}0SZPۘz{E5وm6߅)a "$UIח#ҘmO x.qh~n&'99w"zL\._a)**j2BYo+ۦqJ<)ԑyt% M{dwM-9iu }~{BTƠZGXǟ؛}2ai:ѐ"Dr"Fu:hAgSIĜr2d gU ͐GDŽ^aC;3[pJrV>9CyӜ3n3a|}}U#;~ ސ~y M8gGѵcTݣ<<)hezk_W?n2DJs0E}. mߓCF{ QwC {9`8>AI N [9ź?>nƸYVKWLj2-G9 ֿ'}lHJ[ӈt(":WЭEomRXε_=T̷y-2$~I˺v_~矑\9/͈4"r7~^1"#EZzY&cȅwHţ1 ѰZYLrx1dm[}At.me3V$&ӮmcwԤΉ{z˧2 4gۢ.?sQ&A㎧ GoGm?p7:G)iqGoD36y6xy1]1s|~W|vU+VXȽ{NctbdE爔FguuF!\~e߶C)TX.mJk[F1:[mK *05κ''; ;)qT D[39K]{щa7M5]Sq\&t/)ic p-^/%CWϼ U넞%: :% -Lע=f7bi@il>_aOmllkNAi2B4s8S 3sD$3"E;Q/ټh#^I@=8꼰~7k/Qx>o6Vc.hQd-[ţ6oێcfQq?b4+>?.D)fVJüы9.%[i(3ܩn|%]EsQZ:cm}"Yp] . l5ɘ%z} ikcBE,}#XGˆhJI5}I1\jv(+cۭq?mH)a]\֋EKQLciH},ngn$Z|GT"F{g0Ā'ҹ?# 3Jz<|y߻ҩW}sJ 耧͂,)GI1Adg;ֹpXY|H#:6h\;~b\qk3D+kDZV,=dȥc0t>UD3r/({Xq~3S>'Rz~?T %tu h]XbxRDC ?ܣ!ysYXt@NSރ-s1`ܛ0h-Ǖ5PGTI],уĹי s!0>`ίY"+H 2FŠd> D\k3(\0iۃRG轡9mT(WJ0TF^mduyj}FLt`˅Tj.6^jRJ,tmCԕ4X~>SO#Q@IhiP6Д3+o%͍qTl;fŭE|٨s??=/_,sQNՐBu(+89טh2Fs̹K1G= fD^G=7" qU\ t) AgC"}SƲhH/GSJHg~o'rp==׶=pYV-F,;زI&IcfH2~H(u1)X6/|Q)/VFs`h7N2VSG؃a3C" Ms$~QqCw,ü&,.(r#9yjG;'O6!c[_˅C{cb\`Yz0 :kϥl1=py1$&w7HyT& ʅkrF HPmR ?(O$H`8H6wڼG{$(ψN&2LePI2"8=3TB+ڢ#׭bEqsGd kуme6Ђc:]ٗR!eY P(罏l<XsQBsp38CnvusȨrg_QE"h0`> pǔg+Zbk(~zj~:G)!2*}De ~18c(L0DH"x<!({7f+lF?[5ۡGA: }ijrx$:)5k#{Z4іokM9LF>wgDਚ"`ڇy\ch|4f[Xs߄ag{P!p8'h`~LƒG<:@ bV憏=.$I(&;AR%,Zt^6 (%74݋s.EGfWvvQšXD{}?`P4ЍeuܟO\SX:GZ+$+4x0;GlSF˜QM9|ۭ-PUTX2Mّ\шSI'gA[ºIFI*`SS\lqc b1QZX%2~"Z|oW{Q;޷o#ZBL]l"S Ymv3jNn[וZݐ[.Ϟz!(y暏^tZ)~\z?# VTZCY 2yx H^C) LO,N08[˨kG9)I?>]P!~1!q1ļ[ G##~rh0X{1V;?v}Wl 6͛OÎqhK.8ڨ{zPg M4^Nq]V0S%NF=NCIӧO-n~[\RD[WbSE+ R>ǝ1W052\wvf2oy3·fFMw)V\ӂ $SBF8u4D.z=M#~8^G֚s|23͵Uc{ _:gt:F{P™bwdh^]uJѪ. ԏ"Qd-hлTT4HQ#5p~vHtx=7ZfǑ\vDgYD^{^oC'u jSdAax0 ٙ SYGtas$͌ƇG(QsBJ:b +F!k3hYcpH()h G|gcޑLݰ2[PkXWO3*)Z@72`-hMʺw:R.;z1pG4%֌ˊj0>p*CvbSgCZ$|:IQHJ4Q>JW[2UAw%:j~4W\>0bQ lGD̵>8x CQQmF6=ΧPFlsFf9RxoQ)A Sć=pChNJS)*Ϸ)8BH1؀4&@SVm)iqmml,0,cP[ TO`rbⴿ1р ƳsCW79OÙrt{$$ 2d ES-84OXW!ZGAEl噢Y;8䒒~hrqij؄cdpm7!kĩؕ/a`(s78)q6:bz`?vlFIY~&|4Rfψl[8(b{# (,Jsd W"CkQF;̅RRq%"J.+5KѬSVɡY9cbKo(Wh-8yf9%mS(4"D]6-H\ankCEHƕk0EQ*u5k^>>2z:\t.ڏ@NtQp?QWF,#8ϣN-rBQD;*H>GHw_Ry0#M(vQ%'CUX> q7'VZ^.D=<˜S@tLN4HT'3,sQ.`.ftG` C߭+H0=l0T 늷wm|a;,Gimܛ *(&˲ a烵]a'~Ѓ51ZsnPQgp?L`xG~G˓AMӡBGhNO!ad1ԞoM+E F 89F}LfӅ(Qjk3ȧC=/gv{~ȡ3?ȋ'?:,:l8 r nLW%I6'1+AEZCySnJZ O[Њ~zT\qX-HTy|KL4F 3õ,#HuqG3Dc~v o 9 .P:muZYB#$2eNo-߯ }ONxZ1T.,.KOx,x}(%Y;zZH) ׆T[$".9UYӾW6}7t )pH oF@ݜo£p+3pq[Hc(8<)Lƍ tf\vSXNofi$w$ E.ok%Q|)Vj߃1tGr8UV\c;?vDtwXQF)Tnb-(r95K["ZI%YS0ᬻӟX_nhߘ:  ˹s R7dsoZW kJrЃ謟\d?NcxװV!d)50uߢr ~f?*eB{#Q}#&~ʤL*- <} r2wG#%7"z.""̍C(U/Šǁ'5l,$qGZkW\<_We&Lee)s@{f%|DA1.3A+6y>5"q:Af2 ei>wAD?_WӓK=LfZZ V`pβ(:j;*ہccWqT0g]tInHUuX, cPiQF֒ӊׂۊ%) wvQUz"(Fg<~6ϸ|e&X NbIgVU.bwv1y!MϨ^U`|7gt\~眝;FϠ0g2=6y_\n#P@^VʳN]O}޵|~OE䃝嵣L>VR m =ԋ g%:DY0q^o^ҰQ_ ><~daZ4w=KΨƈP'8N!gV+w4":*>LeDFw&L_}~o>ݴix%݈o *@2): ޺~-S |F|49jeNZRW^Pytو@4C_dm0 F(d5%3i ?B2E.n R\C08FT-~hanH Ftu_EEuHNZ,fzgR;%gיּeg|C';u=c&gNE;l1=ZyK$NS+^Lz0cH,J$̔yն6iJ/?wmF wTbzpRbaP =vP8)pb_6; FNA3|lnc T-}t=SLBC>.!jMu #m*>ZZs̄{w|PcК^KhaW:' f,U#dU[׋]U4~CݮuQ'GXZ%CPt:8Vt3"g1Fj:.Ru npj-3O Ttt'ڃhO]7ϴ)Ik ]F0BE>|IG^;JÅu_d!W2UmIeZr3{f3x [1cKwUfDiQ=}*3#].--ݤΆ랠QÙe,_Ȟ\ׅS=[ ){~dX{ՊV/f^T V[2;^W`(Γ{-1Z/;6ߗPj;>e:o0_ kv"YX@p7P"a7bZjʷ8@g+嘐D-gQ8N3K_Iʥn[a+gkV@快0fQ^4vϒLq+'9DW:flC}q""Q57OsJ`&!H%Lnx`gIu^]M'OV&L=@©uГGESOY+ޝɲ:ˀ+SXy2XVv Ylİ0C'uųm!u \Fe{O?Hϝ<Z JzG!2{Uo;qHg Y RQwcS" lsl{gJVhi5&w]WJ:0 'YL>ѕ`2{8^EAKeUL3)SYE(ԕ!P⋟5s3F³^煷oG;o|3$BX㮙-Mc6Uw%&ݛJV衟er8 kUP}hTl192G|]I띙|_:/ߗPrYCō0}.yex(!N`a |kQkE܇tfɑYr&RlŰCq[iΦ*?O5˙R-s?(˹"⌺C9Z͜tޯ) ޓV*02&0YJ?Ŧߘ'hY8" xI^TA 'f/ޘ~D(1#_uf'{`Ez.d3͠*^ՊG8ex'e(31>cI0fhbۭ8?Š!= jK+1*~{C-5 9릳+Gd0VOBMl Zg.̩ S[R'B gĄZ9Y8 ~{D>ޱՂV9{~m|ըRgr:"y1X:tdL]+hͬݜ3hVq"Iw\׹(d~bZ\3L{YikC}G*6j+t&T^p;0Kdߌnf9 FUE=큭YB%>l[)Xgu;~c)"*Z+t)X[ gۗ?ɟ A)61Z Λ.;SpQzSRH$G@V#"M?':xcpGPpcrJdҚ!x~`Ȑt,lvܓLLS`d9(y7R=)#''ZxR-πׂ߿AՆd+zl;Ҏo߾Tc̄ϊe 0s37o)s;Z=֯ ?yFV5cd4p_@$dg#f Ы{?Msר^(y1HTuEp9El38G#L@乤7NjV;??>c!; NͲͺgΦw7[咹Cc̑߾G̵d-e>˥Xs$t[󩪨{h3*(R,>G8@!ELt(xW2d:-"- z(~(@t˴gCB3G+A g&bh tt!rW|dܾmj(NZ5q /._9 08FVӗƄ[$9yCb`Ek68C3-/|?4Lnjg9g 9*[ CYdςڪ%#_n3;\<@u`wfI(`9~<s+s]iXFlUg*d=w3!b>KEȌ&ٽ+ƵW&aUx֓{X:LvzQYpGUW$C1?~V!Lov!Y躅@B!96߾a\9Q++bXh:@O'{_A%aJ ț#hQ 1I!R UX+-e@pG"5P._ dRJqsJj #sz2gQ6j*ߣQ *aZ!杳wtffp'r701RF',+̵%KpV:n)bOD锃IJ5.TRjPX ;\,5cc(ws*dInJ8GD|ZO2cudŒݑT}of2sEŒ>ρ18X]Vda{Va}]XAG*A%s9؝, #ҭ!}Y[p<}>)__f}|F= @;(Y:t MCz7A˵/liYI9robo;W3Jř/֐ooo`$b)KIlj5'@6VgDׯ3"{t۳[.M{v(/LX8YlY<_XJMREi;) !_K<(mrL&=z/{x1"2ޜ#n.; פ,^ź4@<MAKvK٭iy9č.r/l8gL/ 0_}esYT<aVd&cF"5s C| 3ءVn*Z2AD|R}ļ XIg5{B3,D"v]AϨ]22 -VKKi佚% mo>C/,́L\o%VN&NRY%XbU˒:J"&;}ǟT49 ܚdi̻<^/w-3`ʾ!V,n1`Xvs/4:98^<`ި1`g0\a.TӃPj Vk8d@Ѿ憰xX&JY=dٛ|Ժoox3:5>mz*낀eV|>N:B%}˒nM/:xFtQqV`hگ_ n§WȃAҁ^fZZgZZhb7*PzސC} Tz28O͝6RP|6,;6]8rZ"4Ǖ>@5d(Рq2$nҟOucT<j}{w1y`qp4PgKՒf\xKb(d?TZ2i\Η*BHV;9AFwD?_s)͔`4 }\SK `ayNj "&=T'^Vlm:ѓ7nuPMT޳6@dU]pVdЫ-3Dݢ:-!Aq],<~q8X.h~||Gs-,a%@ laڍAx<{ ʀ|&=lWR!N3S(L@'tnnۚU-0 52x+1Jf|3b IDATɓAftn3$eL!wOyPpy!U {{7voH<^~&fs$oB e`Pi;կuLt s4Jm(1F] 6 ri 680T@(i[ kEJE6<_{[)a׿ $Fֆֶ?}m /yku(O*lQEr51AQx{x{Sq^1-݋%xRL`^Xȣ43(EPQ$ 䮝 @qv+pHv ƽJFq9*9XtM؈nc?M%e ㇽ*3D0kYkK9z:X8l{޲~߶AL˼r;`ѯ-6=0q`&p>0"߽)6Mjܯj\$mxXxϤOmeewf`1 )Z'U^0Bwmy\F)0;j%[wLwa؃:`g`Ŕӌy^}è0\h̨H=-߿1w_G[3SrVx5NxQWb;r?U*SS& XԚg˕Kk>fY!89hNɒ\›vzx2bgzO{a+*dnNE-cy^qݜ땽)ƋѬA}l 3hOCVʪ~5 dI{s}tlEY!PVIy$9gLUI%Y<z+SR߿9Wror9|ϐK6b5pRhk"m^q2 ra͆=2#Z>%oA'"(L&['>YHBK3'|&ޏgT[5e-&{e+Vؽ;@9Cy#eRZ\V+ gҊ՜?چ w#I&*1^>KywM3"(`v9ު?O<$\d;i7CѶEzgj">ݓČVO8Nh:| a#ֶG;k*nl.Eط*2)"<1-{x^%߽Tkhm}Yx^nLgj/B ru6YO1_笓ѳ35_ J}ONyau1VLK ,.b)mGɥ~7OlIILPb?ÕIdz[;ZEb.l_;1BqdKRffcf ;>y)V?:z?!l[^ *& Ro m"|9 % =5dmX)Vhtoq#mYfիZů(d0JTf`x˔ҡx]/ϧʊuv|ӣrE0x3 & L5  09K溕e@$ӓQx sZը.mi| lD2H@ 9#hl&דzưe6浄F[1˰2[ !j33nXn,grd΀F SdV?+*x&Je=Ś fj(=uEdwqlx /ѩ;0U4c,i j8 @sϲ%L[)ˈ!v[1[.|!\ʯjeƺ9~.pz>_ⷶAG "+̊f`.B^ǁFt3A>`b8;+2܋r1B4kh.l0SHKn#[8XV{u%3+JWbad?gg i`E9 U=6*6}Ƿm^-pfھ;gŞ=|||B_5OkA*~|JaIjcv T[)N9 ~TU_h;VJ4=&Cd8+jչDk\6z牳R0Q2uyU=A->9sN1"̼1G/l &7`H "uХa- f\dkr,[Oƪ,r`ӋkRHbў{=Ro"84.Z[fyezhLP)|@3I@I5LV]tLrAx[|>q]Ǝ5~ysmI>;kTp,7t,c t\rbc)Ŧ<^o6ּ/skr[ܵjV.$3L{Ӕ8cePg-CCg Zp E+ԯ=`|}7ABWkþ?wxۃ,g#^s>T$U$ ^g /hix.^P쳽wzZBXeQC7y}c(8cg6]|.[ C, JPWF]LRʽSᠢM_K2ԁ4'Ouevϋj7%cK{]^K[QT6>{,9MgVE<"Y; p(RkFA?^) 2),%Y Ճ]AS*3ARǭu ؼf~M|Ĕ0vjIq;:Ků<чόqij}h {P<?|*$BYfoL K Lu 06kՓƝ%v\2 ysnX^sU[Tv_2'L3{1f WzEM)dVc8z̼mEg[O &i({FG frN JsOcaS Pm(Wufuϣ" @kbzd j]31 V2t(Dc#)qTq^+CؐߌHn&zpdk<.ý\Lșpȹ ;TV3ږ:/ñ 2}JΙz&j~P7y0gIH5۪zf~<^B@A}'1v֒.9|WM6FqYo6'"tx^o3Xͯ| oc0 ;7/Ǔ^Ƭ] ygFGFˠFy`bH$z*]E8ynŧ)4m?(og$%-KL_l4Y}1 %Fa`ւKLAN`X]uV)~8sH})_yPF7K JʜBU7UU8e<# *g +%eO6sٗZ.DžϑB W5˪.Y#(a k04TD6xYK=ZKf3[gzn7Ǐ:ȲKyGd?!-Xț1Y~;Cu&SElDnJدg9< > dPL2%:p]NF4l['rjg1 l{OZOoj $:ɑIYg3.y2+92腣@tVöm?Ύrc hPHs3>W94%h.D25k5SwF,Y%9Lk5LP)@8?K`F`W7!4K)Q_#2 _ Ŕ{3:" cvׅ8Y偒PN|w%/`>߿&"Z<*~`栤mo}7$䩸a,4r͵;z圄ʭoԯP@IS4oo4Y5$}N _nx:N YG~I9|ya艙Ppϗ Fu6f^ ҙ|'L `_|csg!saN<㞕b!= a KTgP*dlQK37e&`g6,u:6Y=wXYlEn4CUc)3aPU*>0tήލ^& r#hTw*1/P쾙 Mjf0HpT7Pz dI}RQc5n7 =+Ӎ9, ,&mIJ{.30+ ! ?E P{)Ha.o:@ DTƪ2j`PIoox|v)>D%_GR&%İ,#LblUpqO?d#X@5L+aqT|+]Xۜ(01ʯCDeEITPt.t7ghZ; ۆw/u;"_JmHO)9.5sxB&/{q@(nj'-5 :&7\2O`:LzpV)_꼿D}κҘLZZ"RRDc "xa s]>Dy=T1d0Hg!Nⱆ$07TYSW( "ԡhsܻ>JD,c@Y^N-F-h/冩61Oimy{r x5ぼşCà'X0."oqj,9Eݰn<Q%bI$3 X&L]ԅ|̣:Kl(+b@bcؽ|NjhK&n:+ Q'f0?W"f X+<<2:.U'lvd9 I!=؀ 54e"pvI2ڽv& pPoB㠋@F׉k$P=~oaz~ ꞼgrJ)tR`@C x{Z [G ,crh=eMڔAgχuuSvg\)ɰj:0"ِk%{̕ Ord܌1a@_qD|]YͣgjxNFɐ<ü:> ~LyA7k+\;q\@/c3k_#/o$y' D,}>#Arze,YɐMd( X3zd3Xffᔇ1JZeSo-ވGߒLTCO%a^Y-4z57ç5^!p*x]i}}ZE@mIaDә9d;ah:oGY:Y_sLN|;Y'Cwْk4`X<};p$&ӝm~KW_K(+R0! YD"V*ы *axl W0\bErIAk 0 [S&,u_P}`hq^4 %K!F:Y>>WW;<yWD CFdF &3QA/hK^4$KW0v3!GskPrU2@+O|!7+*0#ROq/}A\9SIxx4 OsF")^^fs& ZT{R7||tߗ: (>j23HAzhiLV$U=Mu6Uj~tnj91;Ù˒~ eZ1qx'H2^'5r?fC mZ?~+l(2kP-LQ7|#ۯ3y#  ok[*^XTqJ_Y>Y([E5f"C`[q"zS([ 8Z!DyX _LXw# Ō6܏ Mo9 w@d3(PYB.nsʚ8fX9L!Fj#N1 j^^1*ט^)D~Aޟ.Sqȳ=:"f XIʆy XLNogV0d_Y;H Ϫ:@eLg/H3HDŽa(Q~Ntl]uNf*l, u3EwRrU(Œlazt>>>=4{Mkg,\k(zTQFG5j;n!ޭp-X{%\pwe Y 4:Y;`"hVw6$ֱʫٽX i3;#:طMTtK Lvr>l-98e!>ruR|/hI"$ X>B궗Ynw:gN˝Ar|3 4)<rȳC:G@?N5 B&z=:*mk|1SX׵#r]U9A̋9 2Z-CDy!";dR.&ed&NǶ`NQa@هQ&aD1J'oX2< 3SVVl%vF-nxTkv\NK 3Q ׻IwJ.G3'it'+F[aq&xR.zaЬV<X|_H}cQ[eFûP ),R4=,pB1ʤb&wȺ(8<灙O!BWwk츺YBjiC!J^S`spc@;@93p7ˠ1PEŚNU 63QyB l}e^v? ҝW Jb zbsk 1mMzeqty3l1|04OsDBdaVӈ<4;gFpP&j>k>;WJOOj!fNZeu93asdC ~x=ha: U/A^2,ZZJxHB٥@IGNX{-56(/H⟍3_q O2-Zԓ%wqK$_3_G v~Ub V8&(QvTݼjRlnBVNv}orL!+P_F.hF|_5{_:@^ƅ6ӛzYGD+6. 1 ߏC,$*d5sG٠5\Ԉ{D3KwWc"8/FXު{:u20 ;Q_XN{2A#UZ)ckЀCԔE8usm@K#+YwL}&t^(dl@9TNdn˶[2m~(kYu0)SZR2mמI#'BbcLsF?Ok0g'XlVbç9NcA%m:1`yhN?{-. z3F[cyT~ b ,ZȠ˽$j son*J\A .<MN$NbSŀU$^$ՖSLln*7(la#(`<6Oa_\ykR%nfpr-3P4jV!?S%wiҒZ~vY,'󾭒~~|_f_Ι?/K~f}tg]}RIצ) * 1l WMDzU'5NNJ(Jv\1k`Uw[kj0B +QsL˞"I:c6s ZUXغȟ#ڐ7l)e2+0<㔺˅f˫i28gl6e&_!ߺ&:s^Vi޷]zrI@9z%;Rgh m<|ȚߝG&u} ]?ܶ6eXfOgT dZZ XrKƘl{@OoV_!} 3yMv̐6#x/Tl&XSe6?/gR%ݚNF0˷ B W[Zڙ}z<4zXJ+,U`5I0̈́j#o"T[A] Qk'|ٸNnؽX;VCY T|5~oi]`>Oom&c FﰦyX*ՋMaBbb$'0P4|vT>MXhV)e=Or|f%c ؠaa;ھ{A;3d&tpH 7`n55im(t\C/=u<^Q$ye\2) np|kzzܟR>%śt8PF0E+ЫckL ux8}B,7tMs8q?ItRD^p,yQ~fVd #"_uXX+ZPʆ~aߝIfIn-¦_Tp'&Y 9\W)cX X~}nmX 2Îgγxxyc'b841"`!;p^քBDЯ\~mӛW&3!W  KR-Lp>:;^9Y]qNa3FVT gT,8"2S?X9mM~c/7k` ᘘK/!48 P,,3՛| /"H {)n "Ng: Yh9dg~ Љ:T;Nվ5I9fO=<L^F"ŬHbzd*U-|fz,7_ 1L=Dw {HWв>7bku-短.G&0cPl;~M]:ZLjz',%CUP8M3he*W45zdÐ~q]#zXr^ LL'k溡}ox|0  Lf {_s`Y+ZưĠ/`*Gy%ҌJ(eI x/děG&ЭӋēD\sT#ȩ{`Bm.,Cщ DdR^+~< - an_<_PΌ2ұqW t7 ZY)Ψny;žxyUPq^)Z5jK_ضe;\2t:Ly2vg4C0)|=2FJ^H jPElvUBzŲؼ邜 L7ì$y j snr :Iɿ{lĵrB2`enx'se'ofkȔH^{М竱+zЩBfŒ6 Zb"SJ)ֺ(JiNu2. 8s@o 7 |4(1gx`ӄo"K6Ҡt@NTv@}S5Shk`Ƒ@ky>\~Ǿ~ ʟ-ݽǤy(δW>ߵ%_:2pWWԶk=pq(F`6rg,Po\>1&ZAj yTj ɛa8Qճg29ݰ2s.WNqj6͛Tt/,J≊j+5&sDfͻ ֥~g$K.'C?N'Ŋ$ rM3;=W²=[*K%$6Eֳ*%ŒGc:gղ6k8$͡r:{` W5F"!e\3QyоXC*G8(H ̄URV(p[IuŽg8wOs0TX7v\׊ç1' }1E]R;PG¥2/=Y+FNG4Tdjv>=ao3bo-ZqQ5:39wLY&`!1l IDATw˻kx4zVk^\|'̾}'129$2RV2 LWv{#<<ɡɂe:d ~UʝFF_ v"I> eܧ ooh&Iw=&Zmz~ ݻ؋^ѓexx @\,>0+? ӕ)ly}1nɀN`+&g0T-#{(XHœY-:׹ 0|\/%^G(fMsV)xXEx,m]kF<֝^xk0p}~~Z1+u w3"A%RLLaps?oqazϤ $I\0~ng *[5lSj~+"WIs@88 nPWB=IjBl΂eo{ä|1֛ޝA)~0rc^l L;TS4Ǭlͽ.hL?q6]8f@aW,Wڌ3y^/lۜ?r/Ⱦsޝ + %׽P݀p`S)ybk Żl4t @+-t;yk); mښYU{^& pl ҩgq}Qu]'BƷw0so]K23c3=@pGT]5Qjِr{kx[F;]];3d0k`b3ժm#-*¬(3SqJZrފ3\ N{yl/ՐؓaN&Xop7&n04t.Dp;5 uKDI>I zs_xַJʗ9s-lkd䍃IܳV\3PK?[4(zYlA s}YZԚ c5I_6+mp2=;1s"P+d30cȧ tiOKYE Q9#;V?v1U|eg0TH2c!by?τ9m5M>rCo5ֺqZRåcq\yϫm^熲ȪzX1 |TN=Ktf،Vׅ7mjiqM8#gvoLNf v4U>ጚ5y[ufFI@uǺrٔ2k+|U5T)'4 4 f@f/%%).V~ ^/zt,g4ezi;Thz #|+}}<ϻv5c7x,G!(xYqN tlGe_>ߡ3TadfG۴+EN|߫B=.s6k!YϻkRYy'ڹu*,%x ۃR! |8QH~1g%\dYc8K0U)'Dsn&n~ҟEo_V ?} ~⯿47rۢ"UQkI@C GϹ-JSZﵞ/BzH`7jRy-N/;Z.+'ϐ@ Ł9=P4_ںrIt;V$3@`lƊp 5@-X \ou;k͜.sdm$L K¨dM qK]|%y)^wPe]Xt7`"O#th2.\\Fƒ}Q"^"WP@z?'oM2$"JKv8ʫn[X=syY\/XgyU_4o0Na aAJ@5 9 S~ݼN0x{FQ.lN,Fsw9E*yCkNϿ3)YC\U}. _sNS:2[ uߕs y[pJ{꼓J@/|nU}N58_P(< rpPWIP0TRѰ˙<W(P< RO7\j1XP Y-?:薁[4)"$qg1hȀZ#ryGQʬ9}';`MVG݀"^*YciHNiQDM,':& =c":rjM_(iV 3RRY qyW.FG]؝fUNE\ôQƇrZ%S둽bu\ew p-!x_ӿ}~>OYNf'}0'LyΥ& yB`2C<6-BLy\` w8Osr|!="ܜ?~4s8_Dk_- tE2:{|n] ,sz⺦=f PB@9U#c\U.HV-VuAM]GZn ʌaSn}vgȆ,,2{x.8f$QV z}i~@/p߷ z; O$1.{jCG/'Ho-q8cX/߂}Ep #~AV3+ֵqW%Xmq&85O.O<=\s {v㨁peM|srk[z"RsT@}s"c|Jp=ɋc)o~}_/xjibINŒA'X \^\wɭnhpScbx1 ;e,I5vHt;_gXLM)L)F%:V!w\zbv?V)UʛZMK:;E ]{s׭O wY@ bzvAB$Lh+8Hr>(q8ZD@ Cyw̮V˾uG-?1:l5Nz˒Z# )B3!iԽf҂?X?^??Lw=yЩ ][+.懌˘7MUP>V:Z! 5D̨"üZ D%\ksZHOCv-в^dk 8:k~/ӷV19EPY=I克dgz9lm۟}ʀ<* xJRu^Ω+ 2. n;]K{Ctp'RПm_Pݿ?D '*Mk":NP؀Tæk0u+? s{9G<:E53m.Lj}?%nL=n4YmV4衫UuTB hP@6G{ڹy|qao,T*GP =+ Tߝ-g $ѓ% %\Wy{T߃k<_FKOK=f|[#*33 B&jA0oЃ`[I+U,նhXnqk·Yk=~,ldE?^^/@2Ij)%;}3 5Zyݞ=8h/?=ࣂ*:Шߟ"kPZ$*snd4[PWwYAr)6iA٧.c-p0`y/R,)xQyu02J5Ea^XTFZ=@ƞˁN9URx8Hw-O2pC[[LUwe+8;RX_ CK}Q>E0No5zXtG^TF+/u+G-e%3O5ql.7EȾ4nȩy+#3/<9Wpv\?)kwn:YqEܽ}<5ET˭,=nYOޫ@|!sƋs[;A j)F^ʻ^/zReճ(-rhQ uBup!h\p#C]|8ǡ[rH{g˂@i^vhB>(v{Msu:E{}MFy^kLb|=57{l2}H} "͜eTkû[Ɔx|2%k[iBp`lj( @\ Zf]dl,z bLqqr?q@aer RX26Z2Tx)qX!,hҔԨۃc ;怆A![͕8{jZSQ8뱹&$<ۺ10KΑuO;1X3gy?8 b 0h3K9_їa1Cь@5~f }kD-5{m)(ʃfyJt \ҩ`yB4膒}n~;8^)P& rKh )"!(c\q ?״EhG4m8\>Na,g3@6h`=!1p 'u|$ u塭v({v)\ފf zcmJDBx+ ->Zk2 ~8ގm-TL>GRwo'†隅s_qOoyIwo}9|X@ hPǭ]ځ.9'Uʹk^>^Ql*rap_XM B*i"-cհT;=<@q-)pTjqb.{(T:7LK*r q{02Lx,[sOoW]vGДkʧ*v&%Ub*}"pq {1wU]I3W탬hˬkitfRnܞQw0 x@udoד'U߷YɺLDErWU+\(TEB ȌΙJf0re.`?^RDM:Cp]?>-y(yń+0j|@3<*;?몺M +A @UmG+Nk]Z(u1xD@ M#ZUE"t%&@KvYXyhZ]FZ=˅]߬0W{?ջix/7Ahb~dS'Ny1b6[VՓzG1ƀ,C{#R +{P/ _pP'!`N.T@Ŭ㷪MbN\G\^n H=kW_qD*$ .#:\;uQbУ-fHgmU _EYi-[$Cm H e޷uΣ5N\U(m-ffkJ:r:ųv4f#jh_Z (cPLݗE[A˃c8YOA^?\*=qԂHDӫ28euT9D{*ʰe p !rrn_Nx+"ȬP hYOE>p agT* o^r^+v/*KG7= ir믟zfjz)2yc/0P|u\'6:y?X>eΧ{łx:z̉N,4uZaO/LY+ B<,ovn~]omT%HS۝N6v׾?nWVYbGޜo׫zw{;ǵyGp&áFz&-F&0o]J,' Aɹ7 Ϛ]@~m25ϝepTcFF nU;8RyhJ{ѷoĶjjr<۱?^Ŋk })onVݚs#W|Xcg8 wCxV##:2dq[ IDAT{O`^MlOҫn3L9x&b],71ȮS1'1X5bMd,|I+ʢU~=|T;ƾ}OQՐI'H*{ ^[Ѻi[/z՟SutY3'ctoMގR3-ĺX ڙ<خ[-8~yf("zGֵ{;W S;ht#q]B,)jnt5&DZdZ9mT+lQ HekQqDaYuVۯ RcR+##= j-]Vdr,`es %kZN$}tT'>p\g%DS]Aޟ5ZH?@n1 :(R  O>gz)cDnsO 3;&W%b&a uyҀIu,kK|3׮na>'ൣsòOsw n)*zvh^کxO^k9OQDl뀉j# aZyË>A[UlWEP2G"5t6ETM/n>,8@ W!AJ;^ mFj}d -Ps-15r܊Yh cd 6wo|}}-G Ҫ]= >gJ㈢\f"Xևp+hSqz˳&B9<PsDxrCyn {`+rm=X. ˪>'%kL!',IN߲@*zv^hf+IC 5qSlnܕd$0]5Ei ۨ*WchT]P`gwqBSl;ot{{@ÎDG['mW<= bGRr׫Gc!DHC TJ2o>zhgGʳ9WĘÜ7 \yz.9fs>~.ngn4k:?*V̜Ƙ# WsysI0W㪎@u-uٺ@9mSy\ ]tLK7XЇ-^[g:zW8];=SBG8f>i?cs/WvZ Z"&jj"$5 ?RXx?Atg~69 v}VDbOF?csEZv YQKE^8/{vY$(-c<3ڇ+c,%`%~. '@nc)ԄK!:ݪ0ҢSglt`WqvͿX?2֑A-nXj2ªVzxA(G(Q̕ -F箯$=_ՠpy!KXB7ԋpeE68Bk)42B8g\Os5 tA\gόؘR?a|,gHk>ϐU~FEUz7cB[já+ѕ Cj<%|y!j`Ϡ{sGBt ޤQ@]*۫ʾ/^X za6go|w1LWH`4|[X z;j?:Ou]AVb(˷σPSX5i.lV?SL_(y\Bt@"p7t!h]f+![Е'Ohۿ>u㨹־Ux]R{]1Y`.-bbi Q")AgzXq;veOL"Tw-E+C +MEۙ C`|@81͓csn}tEtzo f{] +w@M*C7sv9ujwW(e,`֯>f<`[ (8x*Q,"wl2wM=c݁'Zuy4x3HE'Ƒq b1/x c$0*EjZ)LUB$+jݼ[>Dӟ9{t׾7Bnc11^^2/6gB^ )Z<ޚ aS ~U$gL ԅe}\5>;OM_ƟM҅KUj{e๖iڲRN!Bגe$]R#;t^ |nǫ/̦lj{{(s*t(=NǼ2EWQ[mZFD8N]7nS2m1/2 /Dg0he!qΘہq"s.NN+vG`9uMzߞ~c\H dҠ x_֪XBzeS/]!ox5m}5U7Ó^qXht1ֺ&j[Y0{k\ !_|{r(zDvYYOQ<(B>~!&R!ڍyn %ԙ@Cwd};A+t;S B@EvĔUG:*qUeBTyE_]*p`INSO :?^'&Gp,S? `GڷR6*+n6o(Xn`+=>!*x[υ6EE!2jɋ\ҍJ ˞K6/ mWv˽~uL(p[f7wHy=UK2%r,u݆spBnVXkXkr1T$=2)e?=v3;  }Dj0r֋9_&wM,hY 缡ŏdr]  ԍ8m*A&ugr=Dd/̊~+_@ =u1)  "*3N%Rnxi-/k>@s]芇`MGsI(!0! k갌AcqhF{B<ƀ 3Jc 2b>ງمUDQ[ kWq#zJ)L6 l>{?8:}H DΗ>dߥW,e vZ&ܭs_2ư89 pH0q< ac*[yuw  =ߍ@)㶭_,cJNKƭQC+3:5Xjߪ'zNļ ezMV bwC^9䋛kʹVK%,2zu>ϒkΠhGSji|{ȭ Կ9$7-ܣ30f\/PĒ\%f;<BIR!L{/.w_AmLB&gM'gIi{\DL]gIvq?Rmۜ"C;1` Qv­ ?[Fa+lMPaLү}p[| yRk#6I8x Iۭ&ڃVJj׫P “Aƫ;{"~_?MAD«`P I'0}'t6!@,.ݝ|'Qnb4U6,¶2- !@id=tay Έ"p iZŻjZ`G3XE(?8$Kx=/7OB^6I;DODA0zm{-R"eDB1`ŷx̺$ ڠB1!|?dH* 3EY1wcZTbhșE]k _~|vsy:%lPMxҩCσs.PT4ߗbyVw` 'E; ]2z0V[p;4vbIG_{^Lzc}_a%''֓[3 Iʩtެ1n#~X7,T9©' U_"PY˩\G-OIk>d2]>RWYcȱX!1]>!e\_ ]> Q􋷁65{Y8Gf 4qef:'-c*.5gKhe'gֲu(cS^^%2E_(|u)gVFT˜̔%b! [pf؄9SzzsO&|E62Qȩg3=В zgf[{9l|߾w};ZB+iUrB!_'c@0WctofV{ϟa2.@.w1sWZd3X֦g<_w+KXg~nPoFA_&ؗ< `xy1i guhBsԢBP\0z#HRi1>dx#ֱgI)e6[ xSczeeQ|}}Y?.W.߭ybiM1r̨mj,Av~bK ݀Vڧ}qxR =3D6*ӏZ&X8}+Sܛ޸bI§l|10Dv^=y\@ /RX&ŵUi`Qke7-1,/wQ]MK6Ay8=@UT2J:]Uq\.g 2&5URr8*'K mܘI`F?wN5R#*]IQxa循(l!DAPfTůi YqZ^lf(K,譖'T~qr!W*q8~OYU3vՉ$8}īBG.F>H Wj--D>oux[ANoHD[sϪ'-eOA8:Ǻ/ яey13dy@dK,RsZ mcN\X=86Xx? B&|j?A9Y<q3%>nS쨌)HI#Qۏ>=cȀ:9 WL2`YifQ\jɅ0B +lnD#LQ]]1׉~[qȬFmt]1:,Ϙ>g StRՅ>\x槩cԅYѓR8o?nU0 glySOx i{`g&I+&Rs(ӆAKP0;$T0T-yh:?V|\ vO~g"qެ ae34Uy"@=kR6sŢ\Oa-&Ys-8m HA6Xp>ѳ IU˽|)TG0/* 8vt"Wш"-V("x=btX%-Q$ >] )k9b!6p IDAT{8n*RL0`iL\ PR5c`UC*=XA_?#>?̅$@F//ևZ1?=Fu:z2cuG $Qgn{ӺwW'T`|op?w׺5^[sObsʋ0J/؏wbEɚǢZB`P#ZVa2Zߑ*-/iktpX|K(2ob|1Y"أg6.m2\ 򾴺w Ŀ k!8[0Zs#a 6Hʑ}tIpҭ=J饴Wo IpUI):A)J[Sg׆r#;6b ='[0:(,ɥ%\\quQ- Trf&żcÓ{a=]ؼ wA-BeH#X6ǣmҌVwUA샍y(n>A@BWm{Fۺ@mǏݏU" wSlLLf'mBZ_6 sR#7hF6Jv? zy$R!w^˼vbcGTTU/% _v(F6;KJRnʶ+$%Ɩ߿N-X᣺&%԰SVm@!AºաqN^p;ɼ**+zҰY=),nyбfG F0~у @{jh@Hq|&psCNwd%\닼 ՑtSv9F6%V"\$-w+o2L-t6 Շ3JgZW{ސ:PW^H%[EPǫZSbbN <(LQ~"'%~7+V;#mnB ^uE}An7*C7=QGiR:A<9OeЯ*ߜ2P\aUyQ/y`!8*;VgWY tױ4%OT`QN/S\#xcU'zB0OńTsgr#i {<)U)?Ͽ~g`8@^|7@ $ 3O+d|\Q8=:~^VE)'|3qpPAB [䍿NSK!ϰjBRIN+\TJmRN RaC#uRY:yr`7nK$m .}wz TīrR=gRN|_wzM~ק{߿N(@ԯr;i^OEA(ي\G .ߟʻ7kr=E:D4?N)ý(/ (H{Hktg2WIB{/@7[ȹ&Wԯ*/OJ6Ntd-H:2 ϑ}mMVzNvoȈ#~$2ol0ENL`~ tXTYr_7[qi}Ȝ{Uh|sMx`Ab,4^*JB 8j_rͣBЯjIhZ_y4䈣g.qFVz=HE߻jcd2֓ H5j08J$*u?*#]BTXe ]`sjm*FۯC@ $A]}[ ˚8!Zx|; k$cH&ҥ+h#. vs 1칚(U~}Ӈ]2(2sU+ڭq4Scg@̛a4U}wv 6_L0U <4Tqg">vh6c&aA9^|h뼭_LNZ*Ogo#V)/53TܶK=L<~}fԬn JLc*x1xJrDL@0X)ۭ><=cs7q\_cwrʁc]-}BSN\|wς[tBK:VPSf]JΥ|;ѯ~ ø8p :mC6H:l-YPqB't6S`0 wIӞ$)V=sRDp'jWLxYߟߖΖ?c4cT0"0y p/#hӂò>Ȥgs1*f7C<96 1]$='dcCʴ) Ba]b3]*Ĩc_]}1|s0(>4 /y7+527EoCr]91W2`:[Iq] j"r={O>فY}eqvP>s;:9A\&8Hu?8r7^z:~vUS]u͉/^=Į`5Ty_ad{ދz?˱3e8J`G 5;] N7g pcغfиIbBX1H!~7@ql̗p%KDs!BLZ# za̢X.O'L a݈ Y+=P؅>2ou/! /O}#=xW;U?zg D~҄nG'// <]W5 ZeY/Z 4ԅ9n_3όEXyy>@2 5{W/w ҹ80źVs,D^zuiS0 2NYpU#%b] so5X2Io %ٵb~>o~uWS|Z5@ ݎ o*5]wuwt=l}W$}:ckii\f+]3>6&8{EǸ\Euܺ_5s i<<~wozBٿ%&~]7D͋FIGmʼի+h,fmzI, Z㳽?'/>BlQWk[d7[6T52yطkQ~/@$d1D48-*1kPcL_9K@$Z%[)7 P&d}C"Y"<7 k\,*,GwJlUtB󗟻[r 4{ 䜎7:`i3@(-#m]\q?@p F"\NTF\xIc4:_?( ksQlv_!"-T 6(|&H:"9ýFy,_F$euG)x6YLuYlÍ55fT_wVr(?,q ;so;5g\3c \[\~ؖFݚB5̿'J=8~3|jw`W7t?b.!{T+,Ҿ"4GSsKo!co=ۣU9[ D. Y;KRab~Z\!u#&m};C܏*xbv 0ɠz_Q[.F[iG9"WۘDC}r|0pSVUxm=?ɞaBxU c~PH@?CƝPdMe&c8^/+. 9k\lZIӐ)b#^": TDb*U|!w'W(v3up[n~R02CD7 Z貛رZɀ W6` pܻ~ۗnwK|VU F=.l}-Gív)l-VܲPxr]۴HlHw+ jѸja(AO =5pcȪr,Coy \ER;[ȦEGfTL>c~IXۮ}>=Iy1Lʖs23:}>.J0$w<3MLu ``R1Am<Y§ S-Yc֏8d*ì{r̫X~ЈGG(m4~>Y~]ت(Us/bAGj J蚤wYs)iQhHP7y.0*1%Y@iU)Hx펕_gڃ(y99;a+-ut IDAT%Ads2XʉFk{]\6QYfmTu1([ YjYë/x\G;*]Lks Y6A&=LϔCv;ϲulR!iFnQ0[o==aS!G;+(:g9! (ɇ灯pj:ju] g~K icdXEjRbUѓ힁-3Lf0"8gxϧ1uuaF uVRQXFnY!AW]'$:5s?ۥŵV;wH UDI$?-I{8 8{5sN9-꺧gڅ<@˂Ɠ0j*=a1@y&Kyu9p |\ 'ssGC寠B%lMNRx|gqxxd+Dz(|:!r Vs{gJ' n6;OA;řa*ۅ.@Z4'L 2pVֽ ^wRZ^X>dH=q.Pj3#f,d( `D 1pS[6\$ [`RnMsf6gU CZxI_#7y0L8'~iثWS Z>l{QN~w4z0s){ ­uTߵk3CA޿*B9`gH7WGG~D:(Qo;Jݻv4,ۄ擂 %6c>J9QK5ECFZh/g҂b&E@+"X~/$¤*>zMn u\/7kB#A3]޽Bۚ$=Yv%{&4sUsM8 w-NxS c$iPuo~-dg vh첍GB\}nk9OMٮk=~yj Eq; K\N;0Li@%n+Lǫq_Np,j ƫ :jݎ9O<]^lUå9 I8.qNd vo=rRxO]YL0=//Ky03LG]ZVY)(n߁؆tQtS)oܸ?~<%sE|;bh[vAɭL ^'_ wVfvmʥ-ٯUS-4KH'1oMif,mC@}]E T<*dI03 d"l;ߟ mPr%uNF>d\WU- [ ҤnQ;vJfr<ӟ4%ta>Kj;$7?'i3]i[NL|Z@9V(v:X Oǁ,qL厇l TܞXDpSғN_(yRu44x%tU="Uyd*Lȓ :L((CjD%I^;Y)UDVE(@mhcbR^ҎV"x w\=vt(Y97[bKXcd\MDr[ӤwolK}`}VBVFI~e)yw;bmcZʳRgJ ÌK=2;"m_$vpw/6!W>O<)GdL';!3_+w9]@ l7Gȳ @S\I_dk:d[VM`K`87@-nvx~JcTH, UlMĖ4[;Mh ];K]@d[#lrUNf}d_xpavsxqHD\NA@0^>Ń@Nː|{F}$~^BJLVv._ʀ \0;c͓^O 6 5HNpe"{@<Cm:(a`fYb?Ud>E@.%Ua?:~Qw^N<Ѿ;q2:6)w(TQ i HSD(sU1sX)7mkdŖoPǠmy{DWjY (sT~vTY Wػ3x>/cRpuJ`W}Xڲ΍lM ,<%3Ae? f[a' $ȿtV߷hV=E9w0tx B9|2~~>#Uш57@t yjޮ|Ĵop^y&xyȩPBe(@PO-A%}">-qL_6R|IysZrXҔ:l -k>b3 _wnN˗J)5~N^L%28$Ⱦ<m^ 8Çܝ뜷Un+g&艺ymph2"sPEWa4łMAH E_BPG|'< /OayR)eE!mXw" ^4vCP.ri [nOce䍙} h}F*u?%R`骭DKҸ^.)>ĪB-d\iRB˔3yh'MnXd/|; Kb1t/,Jr϶ܼ>XH`24100FwCT]"; 6od.Sv- o\U亶>U1x01nh26DyvYA2# o TtVq%Moϊ<\!ʟ(N޳q7q"/5Md15Q( ȵj h#_-&tACI({Ħ>CuwEQ.ke(0i+@ E$WbUYANL 4e !+,輿RA#YSBYVZ3orw'Pbu"꒐ v^gWZqs{]T{9;ҿn$V~\n~۬RO+pظn2eo׶Yx:0D=1]s~sb:"A4CG5.}4⢩I??r)KFSd:Agǟi(mO.Je,}W$g@PJ"l(NP~;.@pV*Ozւeǰ q&*tQ+;ۅB9n }۫r! o!n'l9GmFIm` Κ_,$6£ҼWi2gf}wwwϮ7e}Vҙ[gO2U+gtMt_13nQMP(sf[kg<\ӽ-D!PŚǬoIeY eayY/gzӜֱ) s#+'}df-w5H ocz9Mq:nvbs{߹ܨ݄Zi4e7zJOIJd~hɷxcvc'3Qܰ Վlg"KX \t")Qcx)^VŰksWrzC@A8yeү=1Y@+>\!=KecF{f@-|W_ ZWs/=K=G\Y{$@ ш+י<5 B3r9hsM%REQ*"ӎ}i@eI+*su&뷉֋b0 =ۓz\P|h] 4F6"Hl w AyN?_)Ѳєة;Ko޾ I4*[Y&g /l["R)3.y(ݲHӽߵ'^7Pd].OA R#wtEQ'hCaiXR mԦ[L玞2xM1ؗ; zxāsŦȦ2cdqEk% fjXA_$΅/Yam#ץU,LMrUNq >iП_)+o@vvK>n8OShXbƤcϓ(cO4޽D'T;f2MRbֻ2w%^ohL ?G❇;8Ϫ0FQlFP&/NvQt@"RxW e2WoT٘Ss=E:PwyM֚Gh9B%f">{0s1 nXK {) Ͼ0w5O";bA:[;Œi@UV>\r'΍y^JuR#OvEv"/mSEd4r܊`Ɗܳ_ZI ଟYH+ҳZR1sx:oİJ%cx j}\׿ЖȻ޻dQJsaOVXrhK/+/^KF2ڔ*p+{;uR`p_wmSeNH=jW2U9̲VϢјED-QdKˌ5> @Rn''CaQf-lm}zdʲTn{Nyw;WHq۽NAհ3^%NQ;j:.+ߋKmUw+k$X~~3/#뽲 wޅŸ,/0ey<$p]@#Pl[=>}S$!t@'>NCz5_QqjYE۶gr΄սz㧏=?'>t+.@ݜ}u$rʤq2; $#̀Z78as)o HGb9 O VK$e/ Ԧ`1|ȧrs~D fR[_Wa=]z,X#,*(i\?PNc߅uǩLnysն >NO Ľ$52[iOO5(8Fk(G2ڊ..y"^mfWr' 笑g4sE>7aD9=*rxa8R}'`LnZv8(? 0iL} |Fj㶏f08!Ni6>#&q$ IDAT,zޤCCV4@`7ܾGoBXZ~1}EZf&p؆XQC@3Arٷw 6Jtc_ќlv;gտO51#ʜ:^]&SAd7-@) ~&0o*%544X.t4GMP؏g|96Nm˛(X! pܥ(AQcɾb xoܜ._6.UVntsMPhq~ķףrҀ:62_λ$[_+XdF&۷"lx=mG*ƛyu+I :˩wpMJ1Z r8գ90جrXweZG28z*]_b jYf#ZkW4QBg#ow0wITv1cxur]M.[1w jW2 N#rk[ߙ>[[=3s/Uzl+"soev k~vp鴕x^)%D>at_.Yc@))>|0k&&w ݀fY. u.RvDuq* F;Ww=^~Ӳ1<㔌>^;t1Fdt5~6JdC^\7/4 hN=k Zs.*MxjY(Nh\o+Gs/!!=Q ܎ܿzIUZvnѶ:t}:s+bXI~οf5/mg *6_M~3Tpۀ) { K >yÉnS\|@ͥd<%-*к^vΞeB4~z]Xcga{Pv7ryM* Lm<@9D!shlF`7lʷk ($:q5b^eyyg`"=UŕI>0;WRyX%:/VLp]ʓk$E29vSBf0Ώ2^ 6xC6\&pݝ댉6Ҝ,wP֗zW0OfCIzYRV*#bcwЕS@ȆY8܊IvވdE\iko3O"jdǣaU;->v ݚ[/ NG"s r$%/nج.26Ti> (#6Aw^ Gp Yy sL֔.ߜO ${kWji BI+"^(W ej&6h*GQԺN)LwbŨ)};^uåҧʎia'_<SO0ם˞^5hLF1Mę^Ry2?PBwoVҘ1|iRqO\oyzd<s0zEa6LE`Aލp6R7wi)‘*bDwN{z霱+p[ Z P!}Ƥ7 AcHvt ! KlۮfۉV®msoP9" ҄%x2>SzW&l47G$w.Ʋ((G\ֹ+]ղ+lQANm;(w,Ӳu|M,k`e g=v0p: v[Xcر껌d_A@8d#дlWvk~v03/^[.GԹ%yjLǼ ֥E!4ו|[Y-G8qHcnT^{aw6#IEW-]$ j d 嵍n}l/&$PXs=,!q%ڿD~oTVE.{A֝E]{^#Z޻k~8 RˡR^k%|Ǧdncum_mr@<2͛9[2iE9pqGy2;YuPYXwi #kPņ|K0%tہS g_D̚D0{Fc|x2FABt/w0ms@2;tٰ;+4HXJ˲y݈O?a2BwnbA@d=iEwEp/=ݻoϓwxϕ4b}+礸;=Y)|WϾd;0Kw^;KE1W׀`pvDVD>&6y[ *8h`,[L?Vt4OUWՓ1wZKDp+oB町[U+0\Bo4aC-o3@c=xo]s٣,oX醠d٣Xr 4GTVWY7<2(}e#n0&6mtSNdIePKݲ[1ݕ&}g-TbE,nҔ+OpAyRMwI؀KD PW4V촴r[_ڕP;w}Rop҉\#fug!}|xvB] +/ʻυow+w繳4Z"PƴeiRBGp`/ޔKKyeMvfi6<R ? ϥr|KjeQ4ǁϓ}sƷLfࡐ ̋;ua3'`H ׎q<о3jޓ>P"B'IWtJΒ&q10ynU/1?_mcc#hS 4`Gd h!, qIձ$RtEኦMG)㎋7uxG2%Py.TQy0h"WFӱRi'[ݛm\5KB0اEǿ12AWT$ ]w%S3Uطvm .īz{2!s.Z2P͗8 Cqc:Oj]f\B6aAy.3H2v %1T.a.}gxS],{Ge!#ϼ;@8#u +gdX R*NʷtރtP~1ǐ)p9决|)i ) {>[Y(lMLI5g/Ն*U$=Xy9ۅ~ jx}`Wf7JM$H _g[O+;t l mږ\)`}@rdJ lUն+wjՠ(r{xY\zz[ve k6)&5WR߃G1A@;# 23>ؔYd޸(}dSR6LۑΓZ`8rL#s|Fşu"}%B(Ju4ܹ{@%vV`GGy_O5('4`C#G 5D,D]Xtc} \>w@z2軩E$? ϑFhgdmԃ` cp wyV$[ Low~q=iJ[{;6{VB_QƫywRoHmNEfW}ʁppeͽNjG ɋ(7oҫ¹^F ͝7qKd46|]a.o~T&5~.BÃ%]rOͣ.RzHJn啗|4:6`9S GgnE\>Ӎ]o\3^Nh7ܗ'㘏C6XmjT`PLKyץV r:.c Xdڄsz=7 +"GXk=WQy\P%S>V]];CC'խDD\j)e$e/&+ )h?nh'B inR(6v XƄ1W*#ue-MMbw-+};jZˆ+̘$Ϗc/H]Pzlis2+w,]NN?@m7`V~{2+^q̀A>@:VENw=bs};V2oguN1(~MDi"0}n;,zmΗ@Z *^P2s; g+Iv`-:##y ~t]B+Y1v."/ѳ;@u[e=}0EO8\G_@&̌=2"|$)S IDAT\|OCxMW>ޘG ϶Ine%VP <4'hbUvNEYh M2%\]f#,1mΝI1˝o=N&F +K }ݯӽY*HƄXeJȴ( anZǢ2ۤzhl"FI,]%9EoIX㽄r. $ WuՒ/n2󲁂a1X%>YhuL U)*l4=97ƼŬ*.sx"#@SCq qBHu.jXI$]y*]l(7ʺJ6$s(yg$?btm)P7^2"WҲ~Jv0.'K\y ^텹/`|*%{Սk`ȸ>2P7@N@6tB--E]'&D饅tC1nV+#27aKaFxy^MkE³<|ҿە ry=^LַI̳sZ.7ֽa>-oD/)'cc3d޽m i%&.l~QGuR\nۦW_@ȹw4~{ӚJ`nSk,﬌b_WV~-_tn͉V7*{ؤl8ڧ_Mwu *ҟj \~/#חڥw2b~}WZ,{K0c' P~0p*\(4AJYD0@K[`ɍ eԕ{ )i,er5 ,1X4L\-$(`Ψ#a {{wmRچڪ޾Rڿw"waF `9J嬹1{>22TϏ:iYZ{|^bWm න6]l_֏ʉ |2~!>'kF}Q6i:@uq "m|>sȳn|h%)n"4{wًk6S0!ݙXRa# n(Pzn@uÚsSɗ'k6I< b6( ig}X[ Kh}>;eB~d[+Ķm MK#f36e7ђie/{,81YknV:a{ XMͬ lH9or MHc qܹ\~'b @e w_$#.̸@%ÏAҙcKW+p+! 6m9xuqWK$7(6vR~yާpAc0,,H\LtBd& wȒҶmɏٸdVuI`LX0k92SRNeQ{X1dLUf_,P20jJQ6܆8=&bة5O2几e5s$5$C_- yJD/6Qf@?.Y%ebP/i剌; RRC6NtɐM`bQJI^5$꼧AzYZ6FrC\ 4\Zjbo,C ۓd&R-̵˕B҆ޙdmWDRgGedH9_^U,T.{ie)^]Y _HKۉG3>`גjtC)?҈$.j.`檷T+Ӌ^]5خY*{4Pl%}Ω@&&z,2f/Xv0LOi^x5ktInOFM}#3$fuzaވSjS(wi~ז'܆T ;:Ҧ|iJwErtk@A;`TSr=xeɮuHqzvSU3~j"⥂A[Nު TnEk[exCU~@2~."̫)N֌_--;-كF;*_K( ľ/M9~Lk EdJ_tm:AHb>\wynǽ'p 4y˵aO䥖~m'bd2#Qϴݔ-M0н ;ڬ"$ *OEQx9ʨk͋خw<0cj.NN[ dZʃ~>Ncs %7Sy/%.< U7ːZzI-8T' P@:hؕ>%DmX/k3ʼci'{1R*7]0L=|ł)tzCה[p/6oF}p)Ay"\L[ik^iϬ3"m3F2O; _殹B]k߾]ʸIp9[YPR9NSf?s~'u7GH4Sb=X #uL(6a:AyZS $q 0nGlpr/ӆO(Ѡwo8VZhNS,ю<uoQjHGَl֖c/'>4Gq#ci]r L@8>s^RI^Hʘ QfX[/mY'G@^tw X:SGA]I +(0p/+̸DiͬmQvdm9Oy% ;^(ϯ(/%_b Lrl۱b)p?uIYa>ihWڠgK?-ڀWHj%^%{3K?RRڏ-2ijWk:6;Kݬ*y o< +оoQ蛩z0TYۗM򷍉e7ZHn$IJ vo}:VQڡd2vxv>ȿ\jQqQ`xȀ9y+/Wx!=(ё̔Q99qJ܊%dRҦ {a}#c6t.j,oI/Z79;;Дg]g \ DޯEd@݉R;O[ )<4pzn^= ܶQCf9hq'NZ?F/<ۧ&\x5 prblRkM^q|~_ gd~k)YzwXYʻWm^K@&*.3CG]_Rmm]wN G>ȟhoZ|8Wt 8:|wF |nkfѫeWxQ02;`}5k4cXۀ\E2bNttrۖAEɤ.c<"Si_@ffjh YR>cu+[?f殮;1')̯ /(/'WINi2KܑPzd^29GxxR9")MB˼b6w$yǐYPa vo+7jdJxys*Ks2I /zD"Z9*,,*a*kPɓ-6 ,a.Ywt7=mLli%ocX%]hZ @|15՟Zj;a;à =[RB|D2d`h|IY~&DY[3,{,$PYlQ$SIQeWe2.`ިG`\&[v |_s1BHd PW^}zo.#"WGI7\>b3[ J!+cxHFK6d5|_KwU?dɺv"Z朊ΦZ[~uǠT~fSu|C efxvZ\T/2bN XX7b̻Q{Fc {fzw VDM<wTUxnv(6w^ST''=}w;w c-ܙ10祰J[OWҪrK1I_w{yKmTA%|li2 v_{('RYlAvk;W8$?hGW5&v~wGSXg8@ds f }V MWT|*䠜ni`/ÊHdbaǿX./ڦV:Xr#*0X@ VG0r-kY} {Wb"3pޚyg\3ͭ[.' |vWoW*na}ʃ?lW%~s(/CS 0V @*/VkcDi ';`\+uKWS˶`--ԺFٰ˫yoÖzb+sQ Suc!A9Yvsf?y%?ti.i}^ Ͽy"[aFRR>T5^) v]x4fфW;+QY+oō>>&SYw*cUbqpP% ޞV=3\2rcv9gٽ|"C E7[&W YL/uv./2{} i{;e Rg&^yZz =q7r#gǒq DIea.e/bCYΖ[͝cxs=m3gSڬ{PxԇL"00 K1\*Inr}YcSzbՖVg~)e_-~t oj.0eٷ-A3㘌܉bN.gVeQ6F@F_c[/G9Kk' ``gJDx<$6}]~jfyX;hec}+뮱"ڞ KQ|4x!M8eXʏ4@DXl@ЯxH/h AAvz4x)3|[#vWڗb2̰Uweۻ|^0n#VNa+WJϫ6ԥ%{ܥջ+UW˔~7j|5mo.]yMQX発t`*jON<?c84F>y>5XE}AGbwz =Fxfxv62H!!y9vHWjj5iQ#Qu(дz7"Oח1)vL*vCz6Jh&E}M_^(%`HV 1Utm-zZr%I*z(^>0Sp|~g&.AKIC%Rѵ:PdɃ@|]{{"6Ȯ1Y(?t0n 5zv xK&gO㥯߳ {i,聝+B~RB{1f$NOqu3|Y=wN'} /2lF`@DO4AhQ I ͱ;mD HYDl HX3nⶓE,zt{ 3swa${*芉ȿ>Mf)M3?!=<^/F`^֬}vh5!>z}F һIkQH%?#A o (#{3 u1Sij~i<|hZ IDATFgI+|-TVpHy]Ѓ'H]mxwTAyW}o q/ۓ7㢊߬~Db63Zf2 W9.)DNKj,Ymȿ :7EW9&h< |g`V9LaS#Ly*WC-Z)ߴʻtԨS_'8,TQ,kOIԂ殏۸J'y( g %_1Bj>,Q?PvǢOmɥw)>)6B9zK#n&3M~"$$Ljs\)nӸ$"_s6ֶ:0t$T#ov 6osD$VY\DYmu5Aߍ%.ύmo7&U.HSÀ?\Kژ}^@=}.Zkkqx>OyʍcĐ&bI馕~"[.C_fh VqĜ$2-6we] 6 8$"El@WM9+W\,Ek {uZݗg{ .d":e3eG;*K#zNv ^99f;I@ d2&$?~}<׫5an9ɹ|i-6X% *'gI^.z56k Ƙr] \R,: }@k;KIAD "Szki !c AWyO0Dd"r )%NHmIO?tm~ƨT <&ȤEn=s[o7A?sw:yjcLY8`ֿi= AkWH[.γoNo `B#*lc1)ɍ1 )ӫkm>x>8=)/%R?I{|mz*=66/2a͠]N?kֶE+pߋߞ("d)gM3~2߯`/v,5ݔ΀d;R<*hϔX֎ LQa 2rO/"sRkfpn{ sGd4@hN˭.0lK`dPV˱nA\O9MfwDWtqSe<8#Yڗyx<:"ژx`Ӧ*f̓w$J_[犞b(XAcHBLͱ F,Sq3OQ> 0zqhcAmw\"CPrYon'~,G?1 @KW7zI6ybo b`-Y_;/+ܽ<Um/3(d!J\+ԓcUk :I1(?DgbB`K-yPM" p*>d&:0hm_\O^ ڧviJ Mv(קyFGn}~yktlc_KvoH[ϭ&jBmoEUAc-t`*I-񾲦h21okΉO| t'3ssc/cmڤL`TEe>҉S6Y<{1\'3c?߾}B}& >ωq6 m#:R?h|j byyfviT:Gz]:\+<{u|)d@*=({(!߁T610ʩ <-6 eE-9aeʿpC~J֤dEHW`o 6x%ںooܾh@x.ey^f_}zYyvS_/[z_6 @H,M/{_4]ָc8ü{:l~ӎHIfz{2Vo_jGBL\ -杰;f.8rKn9r߻_- ns:´bP"য়~C;L"N<^r̀(ڼ&Fؽ)AQO" y3ص(ڰ}(vRyvI;Dyt/z!xhr kVkuvlőR;ٰ|!%oյl6ܦfĵ W-C \ y%72h@<]pztT`{&cjur덈pÙ#&#!J0T9B\dZ7Mz'it~RRj y._HQ܊i (Ύ?QI @);C@wnZ6Ý1RhLQVÕhtUxx'fť"O|>/I>q <> !|,?w>lypn4A~;yg A*,Mo*s4kx: )31/ r!PeL0 z+M-2 6D)F5"\myx `cxN`?o1m|SM]1A؛B: (,u㮰jriUS˿c::~[YNY)D`% 2mH*?铩d0ܘZ_l!N)ۜ3[_>4~e M ߩa猻f,8|P U2wh{LVCU?Wbՙ5VgLⵘ95E_q2l49dZ⇻DS# \4x*g(9/Gw~q$dyXDi<;59/CfWiuQƿBVhK5m2cnmIRϷ Tx/Mv"`KC 5I"لyl>SeOFGF Iܓ<|E*'|_? ' fiy90zj@)Z_j":>ER{[ nQuwc"~)[a U}P Fh9~p~#2%qe;yr#ö9eQb f橞Z՟zqKM)Lާ J:e#^;lK7oɋ7(7ױ>0uQ3p4etX[Vw&0c F3og:JD]w_"#- ˆ.}+CesIՁ:9?g% wvw(qJdb3$C&0` Q$2Lo@  I'ؖnlbnwAպ֪{?{Iu}uYUjZudn1y߱] S?nk+y߱??O>{v驻7eN]R0gx Ă?AvEB{yOťf1*qfn޾3O??Ga6wx=SBo޼頧+Uvț2797[db*S] ̩eB5Hw* :v%F7o/  Zxon m%~[ufV+/M",m$W y;]=ϪK&#+8RK-gN+7͎6ܹA 1= hW؁mZ7Omm-[WlJw};nD`'qQ[Etߛw_nmz'~wDr^4A瀻׾ߡ6^C=>]蘽*.f P ޑg;^H]}*Y9|Y|PW~%4ɋDfH_|lxno|#I,/k1 ++ =Qqۙ66rF\&Iy|S!, 1+D0Bsѕq`d(t.J?},8l8kUf<6h *-1Hg`  /<i-2_#ww== mZ>3nhh}]7L{;oaUcPL}ڧY/ ,,7in-r}WOzeYQJ7xٜ/?Ջ!Z""&>S|Ļw/Wo;THh~۳w3k|/'1 [\{ ݝM {(|j>yw{=]i[p$f_:cIzg۲q>UEA(w!.-!<ߩc;دq}٩W*NK@TE*#  ןk<|ޝ5kJfʿQT(Ho$>*Ҁ9.KENĉkwGB}oۙ6[5JfKnY¼5 ~s ӭ􄻟#ռnf$FMu[eJЅi):4Q5b_m{oo[~!>C}fG2x$x?sGjrM̵5O #obXiԔ2 HV)v ;*ӆkVOn;A-df`N)/ eDA) @u` ;=[\|?~7}=L& /j(7e㋡ЃHnpوJOfFޭ-ȎQɗdyt +Fؤ EdIuGsBp% sMʸ'c5 @ǟ4'ap(سqx]!hzHty%:g5‘K;C$GhAH7A(T薺e8эyfUGY7r @IHsPzx!|C w晃pu2 x2xe_VoshrkB=Β`XKy0^&[L{+Vb>iߍ8mnOpZ畝ZElA䫓.E u.U Ve~[ona0κ1\ڽ"{j*nQY.VJG~ 4)32G6فv@?ۃB> {`8'μswL~瞋>]}Am( г'Suh$4J\Nn-SpO8(icS''-&v|TġIrWv p VfVcJs<Cx\sgߴo^sNwf<}ގg;Jz$ΌOGf\cn (df =+>]=ޓ k7Gk;”~s]2۲qǩK" 'Yx^Էam];XVu}#Gc6DbVe{a2blF IDATVtS\<w{FYnm;.fF2U,N?4{Fx1m vez`]ܐH}{̱whWM p't7eO6:]~:aB!VX;n! WE U:MD}/T;_>ɵ:@`ƯAzMy+^P3pp3ᅮi |3hnu])˧e5dG6z]yLMXR۩1\##fsOEYgTi& 0l[*,*Amʡ3 n0 ݡuqp N7ؕ!OiAck\E^͛`ηss'D]5!^jt 'leO35PJ8' X ?!~pEx??>M}_me[zֿQm9!j~a#B< ʍݯ0O{ЫgyV^#59U* 3e}*@UaK$S>]䔕TZUmcs.xw)hoQrbR ;)1KtzoWsq+ܰ^1S9oXi7V^^VC` ̛y0q0@ܙy&r:`hE- [qlmB!_i ( g# AE[Yhf|[pFV4$Cof A[r̗R6NR%g+#Jlm+oMWb?} \I b{4ifmFk^MhYYX<(V,,›BB_kBŭn+һ)iN8S%'jPKW*[ìX=rԣh LZsVB[(v @S,bT|qѾod5=Մ> ަu_L*ީ]V",^`LSxQ/?R~lDd6WSy~?#@:3L7)d,Mɞ7XNeěK t@Ŀnd5,Gise^;Ȼp&*]N`9X NdeZ!}8m~110s:8 ";ԃ|]8oEjbEGHI#Vj+85\u͌~dzxwfG.X+Xl8`7P3IH/ 30敩Obƶݒ':ԢC[xOB[A5lul?]T`k//{#l Gݙ W֕{ ԷH4JLoqIj,lY } sdq2!{nlcv5\FN4ҿ ZUKׁƷCzuHg- \=%%).>/&նy "3zszrw'BAY0r=ɽ}WHǴN"'K E$LDӴxrd? A9])(isj'طi :>j)+d6vgM.7e)Թ;PCq6G^=sMM mN鑃ȅo?[Od>jb&6PKʏCYa~ X/uwTAK:f,fX(ev\.Ë6L"<95LzLyvO |͛_SV>Z 8̣x7[H̦X;h7 乪ŵ'QD@ٲml DN]9%8Mʁ g$NMkggxDmݻ"޷Y(CU6NL"XJf|+QxO nJx r&ߗZ珙=3[nc2K qۉZDͫ'cx{?tG M d0.AclzITŨ<6r;G2Rܹu#d%m Rk)~h_?M׼ rg0+NA&; ,,ȎMRB ].64- rE#1-\;X~pxPo|Eg%?2q,nh r*J7G#,,lDz`8UT,}Z/eaè{&j TNE=> bNd'98F?"EyE~,e /؊^#Gʡrz$@blVAD ƜGQ~vʒĽRz"Q3t{/sbĊr\)^Yà#RJ 5B#8y\]n㷈_2c,Bv8 @m}xzo9 3<@m~E |UgQAx.[}ß?&@SF}+|?nsf' u?4‰a=Z 7{Vyaf]:(ج9ҾGV |` tZkFcy@~git\lP57tc)7r#%u:OcSe;l3]1ItuXѠ/994R}4.c6IN| 63 ܳYum|;)_m"|^WWW.(LQ:3ZBĴkE9& ^W: xc(ZKmr+ECwa<{߮<*k)~w4&]ہYxBOx+ѕPy@TSŠTΈ:9-όHuOd ezQ&(/W~onpPmM=&!Aax5s WbEUoP+.;36Z`M6Ե̓ pN>'a.]#bW(#oQd^xqRNgi!7F^ y τwZn+&^G9T|\q]ë8?oU~1GݾG=>|]%-l[!" o&A ;-"L\ ^NsYDm{3n;>FJoN,UT!@!aB Ird0ex3F ;sMI-lb]=~byG2,?eIw)EY~YRFg(<+ PYgO8{ip;dl({VU]+*ՠg^6T3NJ~t@u#ؚ]=jV&S:r~ZfC'f$09 a 4ltϣoLwJ²Bi ~8]tr4OϏ((Pn1GŌYc] #_:Gd4tG=[_Ԭd@Lq-2TKIVJV[4μ˿ vyrgjشpOx5u9{Qn XY~;4 ԊL' ;tlbL jb^좰^aG+nݳWr^]~< %AǬYaVR_{ PB{pq:2^ ivThG\Ķ>g`TM}c9^;ӯ3~O>kp8wna^"䥅cY0GGXe)hy/k+6{7gJ1?;[QX>kݙܮتQ؜X3tff`mYVW_#`6d[*^] 2yy  Z|MzbKGEru+TՏ6J4YHjZ)F1WFe_I1.4ʪOY+$_IPy@fy/yg@?aU"-ٝ9S|֬>ގץPJaf љ4:dup<K6&]uq4Zn@Nl>lkxRpzN$-OF/6G(<%z)Sj'/h0.|WȼQϧmRu1ePÑ'S}| C)ZldlBuyy7H+y]  KbbҾMRi;v2 Y]\YX#Ҕb跙̺p<)3r^aJPT^Z,v?z_ XxVBƗ$GZm6뛰X0s6aװ/;I=Ҽ fv zM>b(Tv鬴֯v'NeZ66AY5mXE3#mGdlw߉-05h]/^iTRڝ+[Y!@4y^MY cG@D旗]Q̦Xӹ{NLCUp#R-~~Ju  wf!^Xs%pŰY"\3Wg<*Gtm܇Qb~Hoԯ ץ5mO-8In wT.}?!BqHѩzQ}n,PD9 ])U*>>޵b*G90X(Xw*X <9 !}(J, tҡҷf%Q9y?%3HB)j1;>'̋Ŀ/ a?yu6w>{{DxfdkxlL}l=rJ؃;ޢ3sP4 !/5Z~XZ>M=^bdcGݵldotfӃY)n kf x1ombs@,u[Wy̘Z,|b *HUϙ'%_S~:v4qm iFju>F#d3z@_knH[M>dTj3".ʴ'ɟUڕݠbfA++Wfh8OJ|kTw$Yܚ6L*wE(xA$>gMRտZ\͢h$][Y> W̗-gRIֶ˧Sd\Ou|{QFN|Av[jLA[f0V߫LƆ:+@LۃRxnIXصtp[0=5XiyO/j#큠<{)tIٕ>Ҟ- ^ V"x:lj7rE|ɉ.^ÌĈsCe\4ݛ +\WB[KOs-[J!?eke{xfc멎Ps3.x -i-e v'c|vt1'1YWywGZ=$omvf]buQJ75UܿxrÜv%R"„R͔ IDAT f{@P9(}"BY:%gGT:YCYS_V_b BT <*_meXO #D&ףd${6-tYHP)mltυUKPt(Ikv:-*Rt(=κoD)/WI$*Fs._> ǵ%u;ζƝNބ삐NC;.OE1-rF#ܴJL/Z$E8",ʮyBym<+ԟ6}+,1Ż] 6:O1uɝR=ZJgWഽHVWa4Z,ZDuye$r;% ZU#)?[e/uTja/YTSKtm"] ͞fxi@}yFO4 /<}?p䨽믥I NC":K畑fQ*$#;&T xnzkP?dTEx#+7V·;ގ[;59zz[Bo*o !M&2.VOjgﺻ %62gVϬ^S/D煲i -]<UQʥ\6od}#9pwaj,ΧHjZ]g;kUڃx˄6xW*5Oԙ VO@yO>kh,)#8&V)MgOo2ӝkJh@NHS$sZݣ[Nن{p|sNPQPj B^L 4T*6v \]#A4(+,?/ G^Yu.<.rS;oDž`Kr?ʻgPr&ǡRЫ wT\̂,<hs ѧyGW^e3ìƜ<*c4 uV#%qV Qڳ}fOc@$fsx ڭy*LE)̌;F#40u͛(Ps!v,"3c?2H_zE\U+`)=yޫq4υd&ea!]-J< mg e2xhʫ1bٷ9[=' jqH3-'ūx7@ U*CdVk}VYbOYM y:H1MC 1p!5GS_<-yVyvZrSމY?ں޾yX~+`2v/}7){]^ҶW#?%`n֏)RzT+Lfߦ;evF ϸ9.i-2(rX] ]6Kpnmdڱl_~y2:pWޜGK GgLF{ gl&KԮ%#bpG|~^a3W2s8ɬ[2DL߾Neosղ+Ɔ\$EJzk2M(3g{pQ+4<,bGk6ձ8~9EJF, |G_'ߓ3̅ͦ *Tj;|LS=~K,˾7[z8p%Rߗj#;`Dw{j4TnRZhſ:P:0|ܫ>frDS `S.8]Q=̯iM:Z˴#/a_ڏDVBr)04:W٘ #i6I7+1ݐ=pq9c86ygk0HmvsEiewkL4\孨 u+7??3O.r yx3y+ό\zjQr*hwGkSz||nxTڋwƶQ#9ِϩw+&)]ꀓ^++g#`G;Gzw S mʄϴ@ޑMHA`"=NpJ]@+HVy]SM|S_lbPL~,Ws7=Caϖo@}“ǞɫHS~o梗[>݂K^Yj:~jc-gd'y4UyL>b|1<2vzjwU hF$׷P ϠМCseWOi2 :Mp!Xp~RGCKr uY5>EݫAނop?Gk|uM?tK|dE2ԃ Kpd2E.NQG\f +B $$Yѿպ<%509*^[9[e>hmk yp=Y~7gP}냏~~n>6b54 =>413 P^O6C3z,74<[f:[?QղjuCn|0^!B #⪺G0uHv4WLŴcLGK:3+E:?;H)eћ]6Gc6¾xmiv23ĕa>6gW Y:Mex pwfπja}fyz55p 2e%$ٳewo*"m?gh1gpt'ʿۄwcv0Gs%3#wԆ-nA$rE@ʅ9WEl'Lx@۾rOؼ-CU$hr1('9 * AWOik{%|53g0<`t;( y?XG?%*x~Q. }/pt⬬GYۡ M=:3iZS K(Cym6w%߮M<۬ZqI=XJt$.@gCfDVi&*wA4KˁNK>iU@!Nn#lk}Kr VekPsջ,l{\Z] 3QKxxº8 ? ̥gr8RoW#c\HFxM]?*Oa J+.YPK6M ^%ܛY@1f%y!@h#H삼U<[Dc&8d.>QJLݽ,/&papAWH{Σ\85O>N,j<hUCZwBR vY~ZEh Q&U[` 1!'0SyWg\}gCUwDT^*0i @)y~HJ6d)X]C{8T;$%֋H{ӋpzA<~ÏC/ŲNawn!K¢uo$aO_L5,縋6gEz ,h`‘Ƕh`+؝bm9 VUjjmkǬ n hyZWPn=Szo"G8OxGŝRoɼ "d3xq*(_ ּE5ܶ>TiVmҍ[˰bZ(Oi~WTO3LSx%ޮ:ȸ޶FЀ7u&)@CBRppq 1+'!UhwΌUr杵:=aSOhR7e JF#_\> jxYN{'xPj Qor%ot4bȿʼЪ*Z[}͐i)Le7 4cG{ nꕘ{/q0;mGͳ !Ep.WAIY.aٸlgC%4OZ##$: ~W"faѶҫ bc \)*3y1.6%ҕ3놝NhLǸ ڦjG w@uih?笌}'@n@'bRrSmt,+B؞uy2\nUKL 22[r25'Աy[~|tz`jZ>Ds%v4k#L<|3V>@7vZVQ~W?V#j ;~I<*?Èfp+ e<nt[%DWXa5OI|" *p+]t6k[[d}5җPEY!/Gv+˷̥D"_R4Sy .O(O1yR _Rl|`F\ GM+xYfO{ol/~? ~YσX06NF^1骡ainjj|d,\m;3H8b\]ogKpEPtw}ڥwpVðm9naYVQs\Z|3% ]C =xJS DӨ^fIe+i2=20TO"e58=fu胀(=pvڇ3^W[=T9Bnz` yg6mK=gK3=qۆ~Kt~ﳌNJ&7ԑ{ů/ڶ;EsHQvȂOQ8:lRqh#Vlڬ mBJ apJ a I;m݂ϾDqUl~TIa,[ b7-#\jh.E V!BnyJuW˨BAxX.Uˎzy/C s#g>LRͤ8L^鑒h}4KC_0ţ]S^ʋ5;o~mlD ? Vĭ*Ø H^+hN8|rrE*xL)h\"M ƹU(ҕZ;g: 1QMVGKy_pg_+}Y@,½ *Ȑˎn:9zj<k+n-ݐucY@^H#m^CkU:\xt~]8UrL/yg0E 9)WA™*G}rW[g;cƼkzH~FkW &ULz=”.omew` xn WꩄO"i0AsSŇQُV״Ki N B B 헀UEcK8VE.fb($mCwm}` ybxg&,5+cyx7`s@}ӭXO<%}I!!mJ)N3 +}|y,H+øvEDh }A0_E'zJg#~6u$X@ IDAT-O Y^,%~W:Q8ODSn`Gj:,_&HW07'ݮދ8X>`mE7/u=-NllvjSq,+62.]=#WB3oPʥ@£~QkC%ϔ40'9a-;m V6^4E[\NMjT['?@QɁTJdxGGZeйO3|u ShGxA/ Yv(|)g3\9S_fM,'E_y$pϴΝǹ3\%EG냬Fy ~/./ul8(nIFPXNK1Z WٶHU 9P~h T?O;[ORZ#}E5v~hPwayD+E9}S@8 OhNF)++o:'jWps ?'8)蝠8kls )(Z_Vul^ERz*ORmqkYoϣ_nEۙ)3H7? {f+:IȨ![~Φ/7, T.g03*{ݴHiY#KG) - lAe@ P#W(by]M1S !7Wh7CoV)}-vͱ#iN+~oqyVb{ep^\;h. @w<~Jޏs8fhY:V0JL*NuUiA*ag)9̡iE~WzlQO;MW~%zxЃ?ODIÖ|4>2㈰V\:N ):2Խ k%Fa,h=@]۹|fmbo.IvZbdI7U y&(vd2v0 ϵ.V?;q3B!W#`X/J| iK宰ejۆoUa4s/?̼k|c;L, yM7Dmۮn;Lȭ㶃wFV]v'z^ F򯥟z[wOIZRKvHMRUf`^o4f9B8]P /۟8W6W$N7S~jY"?T3Z)G沮|tn/xQ]~E+q=^y85t~2 rgGS =3~Lp|r.RN-̒aRtF-ev\y|1z3[J3Ϗ+ 0!)BWNk$FOƜe z%Ep;l#7B k(wŐ }](6aOB[O-^*JW ׾u64liJUeJ 0hSCR ;ꔍ м vr+Cs ̫ɷKyZZH+0W.e42}8.yg`%-{A6/ |g3\9}S a*фҦP "cߏE;QGWfr7xw蟝 yݻkzAj,S NU-0g8?u?E!s3c&W5Ų Bu [{ٍ; M1xW8+I4{c-TXn^(Ͳl;[s iIJd",7x/ԸҌT1;٥!"woF*ㄚ:J7ʊ$yv$3Z<\h?3u,݄6n*AtW؏ݝe BWUsv^T{S q#š0 }2DAjd~_(QeՅH$Ԇe'j;:=#WM@Jg{~ݪxmvDmZ|:cY4Ů5BRxas2(_G[rD-zP?uB.} p`haAX7Y)0|)ELWt`tyLY@u5i*wc P+DI}gMV+LeeKd[YA]d׊UCiJvKfG_,#] ?SGND_8HN !]) WE6Ig[J׻1އDS\жn*Ͱ 2Ɣ즮64:w@Id5VE4߾A _YsOGBP 0`,W,{С gp'5{^-yĿ+V?)aMVVg HWUYG 5JGO#.?PYKH٠/Sa Ⱦv^E戾Ϝ=4*xV{ *ǣWj~wn%%zewIXNeTɉۧ!O‹?S(vvͳ"2vO.#~b C@Oc̈́Z  Z`ޱn#mF*+郺K:҈ :$ފ+|M$|mU6: ZcE D!Fp)ɋP 8(Mԡ'T&)TsLTnx++{`65  _ 460Hب= x+UY ' ׏2!ۑw-vy2TBH<Ζ T  .,>r.Iyx{.WYخejwH;,ύHúa^a4[뵑[wVi7y<`9^>??GI渻YaL|>Zd̍HțivN^ѫtL "Kɶ݊;o9U _T6f =ř+3S."hBDV=}_<^GA5%Igh z:ƒ |,_ Aeu 0Ƌw,^S·mMH鈦Odx8lޚD:IZHWO2*x|rEJDH3жۭ$bAQ `A~Jǔ,V5OI= .E#r*$[UyǟΏkOD? hmܮFm0(}!co3z|5+̰ٳ ~qHTZ0?O ϼ#} :0F4CEumI-H7,VMOZ] $[_>O 5cq|={.fB6o~ їN/y}<ևn] 5ZQR .0qs0ǖ$bbrqOJX$mST`0t]@8 [DsBonj82Y!!X^7fN0 mlqj<7PVh7񶼼ҵǣ׾ޛx.^ǃ?GD?m[79Mٛй0 = Rv VBKC)r)jvqϫ1 ;:`.^{.Ķވ jW</sPi37O*Ŵt?̅$n<$_u[$2Yrڇ/dK6u ,@woD<^̓կ~?FDP@nZtd[Oid(]7-o&PʂWӞ'[50E[-%2`xkbǶRwJq'`G$npm|Dz\ Ue*iR&LRN;U8$R&-<)OYԉ.S}t\%h́t1{??&q@:ǝy{۔0%e 6brZv">ف0-Dn4g l FDu ԝ߾8^sNU0TD|V8F}әnŻGhB0鈷EbΈ}DKXvf1h@eR}$gOB' xbBD|i`1zGϻqr˳#,T$h`WdiDnlVE=f|v9( l{:9[]gj:yR)QGս]އ zurf@/UӼbqF YK_r_3Dzw6=CG`ܟ+F؅6D։>ɧZ߲fHߧ^,~*Zw1,ALA t?>onb{1Op(p&%,{1-eEX700RX"J/ii\ٵbw`g|)ef]e$?3dU1BRM1{4[WmqcƧupH #@B]˩p!h05NBQ%3n7Hua-yyX27ZԻvsCdP.x[c3& {{}h?%`ͽwyOD,51Kֆ9Y*2ԓ@8e,q#c"o!qOᶑ%33vFPuKJKjOjh6k97Ј/Afs/Wz}!vy- \[Ǒ^$D{CY;[1AawV ӎ{ CpV$tmS .^<5h35ͥʶR TxMky˹tn/L)`'W/uU:zq]?59iQ)>bհw\SfHUHƋ4L}p/ =yp~o՛c|N + b].@BCʴun&,r.څ)%-8Dr#_ \Nn}+Zٳ/eo.^crU; J 'WZ!]*a}\F/mOOyesQA*f{ =?*-03Ԙ&Atm:!`r#0[ `ƟSXXG'<zv rJBFage& TaDr~.H1` ,^L%=-RvjU~L˔ Io}+nktɅ;}_pIsQ!E.xp·$B3^Yj]\u/_Gnsk8Ci4tMyQ,0\^6^0'伅~M@ߒ6DtHXRPyQ30[,ƒ|N6˲<`m;}K:)66[+,oۆ9}_RÝ pjB{W @(X<b;̜+HJH:iaC,aׅYvT;p:5.ȧkËd5,7[@ׂ\H2QWBA¾r ZEef.ա!~@jU3 =v:8&4_ˀd,-I yP"9T"%>*),kg|ݫ%]@ox^ w 'V(@m ;_yc|НzX9ɫtFvi, NG:61>Ytg~NMBI%U/xK?>}/ IVXcp H 5yԼ((3LnHB Fo'3vtq<D;3ז]ADZԣJ'.[yl=bpw'Q)ݷ|jPYϡ8f\}*4«HhS ww;Xv=::  L MHIֵ/+cnaE04 m(W]Y_ӱɓk:B4ջ8s7"ܺ#eiidzT: ^3 D۾HWFQ1LHRe(>$LxPsE e^ֿ]4ac/dFxW LU<$A߻4-97^F FR,L+|ߧ>ugsWz<GX'cY5#v"MD8'7\,a 'N]mG}a\T:Iij7euK/i/k \L¡w #di}Y;N ?{z<3`v(z{:0}=w5c|K,ix| :wl+} V9JI[lKrwjԹDy8t@2:o@ M&g!b|q V kAiO"xW~( b#3ޱC_I-#ks֞Hv ؁T.7cŽ`@2ȶi&?nrvbdn,@ff)vR$va 32Pcˀ oTVYKQ jg}+ΐ \Bߔ5NFS?YOcte'!Iz X2Ng@+^Ii`3r+V(?zM<ْehȀDWDjYXo^̨Gt7P Tx*f/$?Μ-P)3,1ǎy ={*Ъ=r C@6\z6oF,ѥR'cZ{;w"gt]h wNij4jNY eFFG)_~iAύ<bt2q/!ZtVg]\[<&u x\I[E` ̤7#\y@[ bۥM oѴv绠/ fw1C"<~W3GnN^D%yJ4X1?d["d8+¼b$7W J[nrgh+\pP]@a ب|G $ӊ{go3rv^#Y2}3`jN,Odڍ!99mI o%t4 ;ӓ起OBG5Kx\R"\Vt(g]˨h5ʌc{մ cTcP EKgǛY:$ +ۨ8]'_$dGgtʬ}4 t񦜁g(Z|t׺w jypctݙu㻣E|dx%ڨB9&p M,,}.} s* Tc6_NhC(s55A5wi8h=-, ~U[tGIZO7Kyr{^_]QαC=.I0]6J28DAߏI?(7c&ŁA_@='TRt Mt.Lϙ6O~^mu.UaQ<oOoh6%w^%x9jblLhۊdX;|l&˷ETH^)INBs*16%P {@.q d!RƸmw:r`}R>}gjPܐ}C|QOs|-AGl?1`8=ܫoml_i|4t_GE`|xt_\ +߂dIRJKTYr[kd0FH;hqT(6NpۋPDVdx޻±aDe"0srȸwE.Fq!5 F]UXl>X"&7u6-ҵ!WT"Wr H85_;k{@juq_leSV[ sCSbѲl-; {AP+{!v g5&5OS7p?2g'߰7*-Q8@AFZq1kara<7 }!CCW3[dM%h}4Z!8XnO*kE777637@i&=Y1汳 f- 0E+Anuea>תʟ]HE憿'͂BCr1 rGWm7W/mJPX]飀5sb2rv%|DU 3 fm^f"oΩl_EuU9k9%#MO$X!3(14C93k'3<Dn_GoEff j{@[@fa_T 9Fa{я,0iYl!Y6Kh |mg;skeb"¦QoIR-X:/Ä 603%U#i ԐhgGRJd1IJSQ=pcjljB P2Cg nu<9C_O>O~/?HD= D'X~/w dd cM+Dqu \}]B ĐPN\Is0&TH*|ↈjQpPۓ?g777ضmiQܾg*}LmUGoSץuNtOO.Xކ5cF֢tL;v:FXpVv7l, I ??4)Mze8;ܴȤbwQicϖxI"~4jּ[~:#)75Wgh)YiҖ)O| Z DwF *#-|թן=KܢZE&}-J#؅^ t&uI6<_OI=MD*q`4kZց}m<J,w>' +.(k潠ӛj+cc EyDè>4B^:l-ju}3)z$()-$$T𷥕tw_}o T͑k4ܗgP23ZedpRƊQp)cIx)z)X@;dHGFXIs/t<6[ `[]d_yG||B U0i e+l[ wb+͜յg~z\<݃wWhEuM3y'T:&ݘf)2"o&[EQɸ(&班G`v홀NAH…aStP,,F!i<Ř/~og֙O,aۚ2p )2(Xo1H.ȃxwuu`BL.N. Qmd!+d(p.2O{D%"F2_Gsո1n@ҮVW{;?@yX.*tH]+F=HV<;t>cpɮʌ2^sn3b   Vl +yWJc nY )Qvgi3/A:+VXv:O @;YPhMOv#fYڒXA,/O ˗FrŃQaޔַz="g>jQL-\23Ez ȬbD,.ݔ{F͗aݟ%&RK 8=(K&O(9*mB<"9 yܿ[qX&)a܂}rYF,K<+sɲ%eK0<d`$u}/L!q@fnǦbtA$7GǴ}BKΡHnJ0ǞT̓Tn}TL'];#ϕt6&kn`,F p!& d'^\]7X>Ѓ$c脖DE`&~쬚[%ZҁRQ7T*skt˽wcL:~ ]~Oc^dzrh_D~6:0tc9˼/heÐruv;F /`^Xؗ-m,aaonHy7cc#Ō5M `uz؟H64K:xl:feۀ}A Ͼמ'|} -o@_֟ NS( pJ}_+NE*yj 3+S*,a[X(NLV8Kڔ}m#2PPkݧ-OW%eЫ3:r#Zr2R{\q.gIx'Q;Ea^jOcg/XԿI m?)(`>kO(}ҋ8 }ɂ(IϞ1@_Fp}j%p iz%⁴<99~6$Jwnߥ/ |K/O_?W&v yXFG<O,_Ƅe ܕcv?CxG;vl(ΗYD66zKiLgc&eϹRfn- /ϡt([ UxqmV+Ђq7˼be{#gʿvϸ|k2r^ESf@|Z6fLGODz}Ѫ_oF#ݕ@0ׄS|"lcfŤL%}8?|Uw-s].. oƝX8&K :ְʐk e儽 hZz2l)<+u ^12k(HO &bK³U*pFv{^KcQ*0zvhOmyhEe$"/.1dfXU(i~yE&hEɷiˢ r}--1X˦@$f76=Ky7Ӄx E|M/uTတ}%ضk]ܿ?FWU.Ac/]óa9ݔFWrVɩl`h,0C#609\T~*;IۂHsCwPu2[[.o H D2OƥIdk'na," ׍l^2J.̓`&T6 kdAax-?"dgp>SbQ3c^ȯh]AHp%މx)eڣ@*eIdV'H.%hפYq1mH&1ឧƌ/ L^Irl\AWq?gۨ&ԹgFy#<넼#Ca(rOR'űS"M}!]`:'O:&[q=!b=w<>H%sK>dP Edre(`3R6K~f0|BxeJ9^=ms0ۃʈڎ&YS? ; 3-xvWC\O|Sy7ӓO^y?YhGp7 k|FTEpHtg?* 6Z!6nUapA{#[ t~%Kcw*;Da"\r0u0 DXcYj MHV{Xaw fɁ>LrcFDӚ2F;N>i[*G.6ƮO0)ƽg[sr=)qI2% +#*PyDBtmm$%Fk<:7DKygj\j󑓱u~7s,k67^f_kh7eS Zƾ [L1}ABCn(3Ͼ܃Xc |s_R~P폎'.4 $yB+EڵDj@Rq((̠<斩 2%%|5{\o$}Ӽ=[{Ө\9GY%ݱe˩] {@c r.i/-Jca>{uJwE;>75ᇈ썀Ue{eo3æ=Z~e-K*:Inc8y@Gտmh qz2έhg؞e'eYI9!N>{뽧j>+{{D }EqTɥ^ +1gSlח;<⤏6\WK<@pSތ1Qdi'Z(vGB^`E&=u${P uWr3w' {Bܔݸ ceݘ({gd4}΃ D\ğ@ob}*368 b3}`<L-jr5Vu~g.μyL#a [,9UFυ_ p4 gd[ zp %Jv-= >?~<@e׺qu3ރЈ]c8f?}y"{{)DH '럔l1 P <q.'WyDB+O@ QE:TVg)K=Fw22A݃䢱Ewx@r pH!( {˺ ԫӇp)s,V뒫5[Ąr, bi M6 ~+q2ecf#1{\.?S%xyɗ_}w-zL^9' 1}4 ɔ++WgMQ#b[t*"ڶtaW]AY?\0GT#";GR:U:ȕ52I^5f3)3⟭vʷy3Ɇ4#tv( UVqXo3\slmxLh˯aX@5R u߁b  ٲZdۉvK}-/>x=B^}Udآd?f2'C5K AWJNK2Q#R F\7 pʁE);[9 KHr`:sXeD' ]b983~oU{`/46yWʬ0ۇ>k-z 4ꬠrO1xŖ&u:c}kpoG@U]˝O<̓[x@eɳy20Q_嵀bU+.}xwԱC]` o3w[u3cۓzo`x^z=5A6=*툧>7'P_m䃈C2ȀA.3x|X tEc@< dԬgk XUi0s۰Թp_WQϱ%<ƃ 6޼W#Kߝ*EYb9ESA'.*%-f!sZ w-=L_m~M 9HWVikH.BEˆoG|ཱྀY4JMڃ$|8S}$zlxnE p!OGc)s'5{ʺO=K B|yw\bvX :1"k;ZhuX#ȦBCAyPQpl ah{#NKOFX'LDH;ܱLu}%Y9zdϊ2~GK[,PnU<i;II٥%Sl~u{]> ǷzVA^YD0C?&|Mjp|@<B3qZv*Կ 6Z4rT6>:3wVsџZ‘0a E+E-qgyk$𖅀c',o4^3߳]TYә7p-qӠcAJ:= B;IA S s `ăt⧋>wonu YjQ ":.}[<c]/1+m;I֠+t&b͗ &@%mG^tRl'|cyFYK?(;1SGߵhzSG~H=5H]0jO/ʙ=3kL&礌z66k `GֵOinL["gN|\}c`d, W&tm9n_ޓ.i&cnhatc~j=F:N;`U:ݬyFW L W.hY f2Œ7I@ Z`x%tS=ESsIҝ]c7~G֯o |(UXZU90,,N.'re[ /$߄/]}Az u[帜藧e.q, `ׯ{dyܕn<*ݧ !_t<_NEɟ۩ jO$^F" Iƛ_7ao @ v;{*zo"[{mZKY9c>c1 g"-`HLB&Ft@65HcO@Xh:Q8mci+ύkP7Ɔ3:Z"y|0/X:; \K+˔^7㉱#(\#yD\hU4б&ЌF, mO:XF;duGI0ady/D"@x M bRHI z3+h zVs{/S%#}*op#D_BVm- Su"Z2{Z=22ãoQm"hn&EyY_fBQeV3ns6kCg䏗d.Ǧ|\8ITȌaU_qI0.J؁8ygPCGҖ3{3(h[X$4 d"2TNwʡ1l^h }EP]9&Ee3m-ёpgM.=$@ԋ/^};2vڝu)fQV+:e$VREU6^`VhH/9 B +h8L7NfF_~wXŃ/%:@JjZ>!MeU-?RQ|k-~X `7ZZKӧeY4gy%=(yǘl|tIJN QAS,x4>13XM̫+# oP \P. uǠEߪC$ z=橥;Ψk@YU4%((% qIR6(rquљ~+S ]cRcj4*7l&SePR/S)ݸ(AZE\k>Yw%$t{D2VւkͥJs%nƘK()܆;AԊ~7Y[dQ:ƶ ,?kXH薐MZ0ZyV G:˺ [fXG 7߹_(_'9Zg-/@Fyu]n"2ADaQHcc?|mD kwu!](ۆm @4HM)]G_#H} 7@tokf:UJ#Ŵ6c5_C#:-b aHuTtQw=W0@&!(`B_FaKg/)o}@VX2M`U1l\̷2$?%w~c{p'mwr %?c~4:p`<ۤx@{^IO-}Ge uU#UԲ.^RV~vJw(893gd/ҾW{~ Z. fdYN ֔q\xQ %) }l&ql|:_zw6)<@W^};㣷^m[ Q(xIUqZdOb;2]!-=m4cE SZorT%Ă@1`TRg, |7٥ &HM]pG}t]Gxd5/<C۶YI HHz_V _rPp Sq_tA)gro 8#cujIe] y˱a6&@*4@ ^sėtXN |7\ܘ>^~_"3Qy+6ݻ"h!V[cf\գQJi <, ]ܛQfZ\RZo Vrs~9pfvu֌R"`'H~n-azd}7q.xYRvic^-\Jd} Yz}!@Q͢lQ> \F73l:YYC~9~2XE<3wݎ2.GDcim=;2왊 `Av zW[L 0zx->2"o+C1ֹŲ״D&zL6sk_f3ORt'Tl&"MS[W Mˎ@J1$+MNg!NV)wĂ]agfؖ }ܗFo WW ﶯ/؎L_RZe զ~ceZRGôwWH_[ hU'X7G\s?VabeXsÔhH)y\}͎!PAD.8n<q%z[`Vv1c߅VuzV MGÊB8'CfNKCJؗ6􊝱3) bFi[Aвw0//1YB-rsfDXvļlH@] nlmd%@${;6PrWQxu^K1\W_z觞>T^y?N|9;5f)pGjѹ}^f&sZq<), w2VcƷNgGB?֞ZypRr;=8 1 ؍C$ IA Vϱj2늍gI~޺#{j o»7ԸhyAx^L=2ޤ f&?}szH(Z~=:=Y}?Ġ_%,li Ph(#kBʰ JMm[;F˟!8yݴ0W`W;D1Ey뿒 *|H2QOdujc IDATlfP4& ( ւq6H NlY_8bF;rƛ)w QŤl(6islQPyX]!/"߮b=r q!YF[al|!G @m3#ꉏrvaxˑйA^q?5~CQWJ)3{C٣ZǠq~6Go*#Ѵ3X&B!2WQ4^F0}fkp3Nj(bg!r7QʄMV` l;s!*lK^Z8 Zeĝ\qSھkz!t؃q$%/b2_:%K/Xc>c|E6Dg\8ܶH4x;IzsgKi}W^E y *փw!{B=7@dh4z5P^z=o/3fJc+ ( `4S!yRW7ޥ0 1&+8a%[^ؘ@򐿷Y?ec%R-1G*yRE^4R%t7 @&2Ap"7*V^f7JS.al+A4,<u0:߯)L$`U:yRൡ  [' :@IG23Ϯ06/>yܙ駘>^z?[DH&@6%!F"(QNb% ,G]HnXꖧx3E=Yfi}߇+^8m }X aniACwwϡ'#%FOLD Dnl,kV)o̕yrTvJWsߑ'hN n̕-䡁\0g[ tlHi^B3(&ZP"Yե:/⃎ .[ن)FֱF?|c;,F4qy >M+^-ʒosf^{)`\MaՄ4nKϓ>,:|"$XA.وPh;(B+$Bps";#}O(8[ވe[+ςUV{>a{,6PД(in |QdN&LnCg01ܸL;uO@hrEJ pѻ'z~MYi1)|tA{E0bHY#{ h} 90l=gA䧱It #@}70cz^ ]W] #M%q] g}ZJ}'O@Ӈ ۟Adj]B-E#6Y [l^ m`0mNP6'7~ۭtxNdMw^h%y,e/w7ɾHvk: 6%E*6%G[*J;)N\DBK-b AbMsOrtE><#KW7͸ZR X.RT?713Z'Q:ŌQ٘8..|ףH),@`L4yeۑ/'>|UxS3`DϏ,'̧d|c#j/8MzWП{5"Hn Z.wo°x7Y6"XN ȝ,G|~=d F~ݦl/Ev1)Ҽ=߶m-t]kU/ª"7 K-A$sfNehz)m^6SNvQV ++@bX/Xa]x$4ozݑvǻ fGqKc @>aԩu )K:lCۛvU@sH9_ZҦd5@uv3s{ JRa,O=|?4CA ֿEoUҁWsޗnsw> c;PKԣ0swm6$NҖd ]Cd|ʻ@Zf[cͧ ~/D=Ú7~H`` e336mWHg60f/ꚽ,AoC/S"OEmKRǰ+e}F{J 1- H>{;*+Jp -H2n~K:&KCj-v{(pF>jGƪ{ f _C,S!婁c G$EM mo2Rl1{N6uP+3o Ҏ" 9#WL_|-PG ko[; +tWSmxDf*c$ zc6)>."3 ] 8k #$PK>Fh䚴&4sd%սox=&gA` #oB?^F)?s3Hx}AUnG-I 1[K45fZvj3]A-(@XQcq*+idW`7PA6)WAϊYD)yKQ8y(w_+ ':lO}g-ߖW]nŲb: o60/K#KX^N$XٵVĬb`AӮY\*žN}5>Sh-'_ {> W֑>Q}?d}2S>PZ Rb#˫̉,2 /=_:dCF9k ʦ @64 {^0Y9U5w/+)x  I9OB\W8G]^g-4_J?[b=v->Q"=<7vLgiOSUQ݇ 7JP~ }zfyJNbAҥER ׳Y{H8X0R?1 @`;{ "l7Zf0p-g(d/Ɽ1J/فǨif̌qiO ]ptVpF~o{jm)Fjsd!Kyu:|0tu앲 qѪyu߯,n0q3z bѪNa<Mۿ+] vLud'Dt~wmp޽I,sf ~Cf?ڛoO33Wy*!t4VHCxvrX8Yv8n%Yx39 . 8LcZquэ ffLoig2f3ضj95X1CЮ87?Z"2Ϗ\šrOmH6?/xe :{6*;cV̏"sףe( FIFYT ,H ^ZГZRxyI[ ?~(oCZ= ,:eN.)G)\ @L>`ew6c֘{`ENDH&J!\u@9TgC -)IԖP HY>!=RƪKUFtɛq{2?TIFw^<)ȂO1Lm`IHB6o梊yx,2QoX=8Wʸv,L}KK?B>B b荷3F@6`ACZ~"mtK<b.#L]~YM#s$Ԙ&@ hniFYG 1|򎦚geNדO?}k̞>~X̂~\y~ȟA7 )V_{vYefs4WedSb/~>b@\fzdF3"Yb肵+:UL"&]KLeȢ\Q&4_K ٞ6/@B OG{>5`qaqK\ꝭSlq %i[-@(-b{I{-䠀 \2mEҺ=51qi rŇ}"~yvo__O|G^yK4$J$zMlI5vc9j.:gRLG-kRr{hr3ρOٰɃճI >H<8-*^;꿛7ދreͷ)YG``^$]fޗ<9zpVql@ȥB7Y">W/, ٲξ]ˁ'^}.90p.J?0<=wkI@I 8\R1ي,Z:Wy cfAԮq}e^gz`r|(d!RVxxmO^+nV ;7[y{~ߊHDm< @w>KAkb#]|~x?̋y[`H\Cӟ؃,GW_*g| 8t xeA@Z($!"z2HӺQ-bw/D px(WO|99l"0r Lb t(k 2ZH&HEbDr;XQ @h 0DQi@VOxҀV;v}^EqQ1snp2sSOI(%3Xbǃx#t<@x툸Eo*Mwr39muHB3"XrbHN^~ݧ_|UoAX63ѐ7P{8 dIY6 "~.芋M_bث!_tw&DH4 ̢K4,)׺I'a ܩ틔rr`Z2zG6k#mV4po-=O ¥K /c7N;^C Xlˏ2\ױ}߶+KݬgԂȳ_jfƶm<> WNȒ].g|A(iO7 F0\ <> zC}e;> oxg{:U}Jt< IDATd] @0؄f=o-s=wt8i!II+)me m#eMc`$Z EGdjwoo_F~ݲ 8ӑ^R2#*l6xfuAXaG%l;|Y710lNYaY-ϼ 9PhænіZ=2<D#IԻ |flYzzH+O0 &{˙ {Q# \'PEv!F #*]n"=8ma= c8- nXwT0X|w#@>Ws7zFCX6M7E)Z4:]@\x6& 9Qݶ5,6eWM[&|ϑ0ͯELn{p$ϥ{Vr2,emr.x"<|U+ oBHyb:LcO6sX>Oԯ6f!3_gBc/*)6b- KuɈ+R,Bݯm]iq<ܺر7^ Y@Nkѻw]a۬أ/Y,yYr0$*w&oUV`R_y"h?V@0Pbo$B&& i$/ ,:f`[o}<y^Jѥ ,38"GE G1-c+]%d@~j"'U;5=(}y0rT>'Fi67-&\L>a ^Qƻ33UџδC_#mDXQ#_Acz27`n B';m`QwleöV5eWZ_1&t_baMQUg`b҉փrKc&$T㽬2X7?J_4,VM!Tk ­)U8O S(JQ20Y2:%-u0'`FϮj޷'P &;ذt%92vB~oK #K";k *Y+]\-H -^4H4ەq)/C0dƕy]ʣg%j ׫.! T.c6wexy皕}r !3 /VtVt_7= %뢬^)^S&,<,{R W ns4PN^7rjJRl5 $gg"|ȆM<wU.%3`]ĮLYf雱a>s! lfmv/KSW_=%r{R$A݁6g/=z ī~cI9Y#Q)?a:nI"l3NZso[[dE_TmnX krMĵ-lRP6Ykpq󉊺 (W[y(<œbO. OJf1zFfMwMؑah7;CCY @X+u,.{ȍ5{6iN8w'.gYؕrE1J!s5TuYZ|{e/Te /]8ґ7`N{Ei}veg 3G_3k܄4Dle~m9}6g-K֕'K 1=3mPE*JQ-B7EPF*fPi'@.1@wN F7{! *J4Qz0 =hBH8^!Jy2H&>Oo Y٥ȾmF;wi1߶}81y\y.V(vIAG?>/ \ }rx_!K2T.g]7DV4E8KOz fa:J &;GBҶ`If n< CC7Wxe+1^U2'羟ww<"@Rb=kہ͸mEik^cm@.T}ɾln1 @S2Mn~Z?mWU FO= y=Xj2~x~_~LAA?2Ys !ͷЙf6 @m'|]3.k-{ſ .t;ٍ^vR斳@!jkQ[4} 4We K{F Pղj *Sd9W}|~ʏ-F;/L1)3Z8|53_2țd]p1HB "H^|w]]D»L{o [ d'UoJp JτʛkiƦG"-KvLoDqrwG-f)WO3x\n(J[h$#-uOx/+ +iҨbCZ~ة.^ _Tɰ.Yu @V s^YV SdvyyV6Y{.Q>G@Y{ ߥP *7PD4%L5X82s~'_|;.g==z_'\1'lqǭkޚmUFE87"9zMPDCٍl%Κ2kWdcz֥DkS UC%2&뎭+*ċ" hU Pz][,_+{yܽ~^/xt ^@vr&8BSPe0JZڈ@ $8jWkSZk(U[3`EchD1 /t//}6psZ-SvmNXj)c?Zc" M;_^gIƔYc_ƿ6F˟Wt1꿽{4}bƲB@2 R)f%FrGƊ0a;nGHW y…w<`/_zGo*,P[2J FjI5z1& sIiIԛIIan>^Oט_OjF@@T)D ORwvܼb4<R1zd!NjǨ #ʬqǞm yrqJ)~Ϧsؒ1?;=ijγ@EZvbi DI<^G8BZ_P^$௹̍C G/S<@a@CE !o妪!RT)t4fsIR.,O^rjtsXh'7d/Oan&oL)k t?NiZAbBZcC,Z&9p $cLY"!_IYI?Cm6&*ڑQ1kl]clm>5W1W!0\yMK@r̾7# D@Bȥo\h;.+"k׿?FCj\"hj͎a6b<~LE}тD xc`ꕠMO7_:/L "u9LpL( -ROоc2s0V}+x)@clow[ȗbX3dq-%=Eł~鞳dvv,)hE)gYd]X9jC{ r곟~BOI\Ra g :*),ɲQE,hI,@"ld+٨%Q>(/c~QEA4bK[8MDKOs}?SW#aɇ琥‣As9e{߿%Pmo P5}2<_/ރrew<쳏{!>Jfb1 cEigjeB9$0BQϊE [d[PA|-h\(&H i~_iϛdf4Ԏ70XTi7^PhvQk<arLYg(TY -1Za!WM.f6qY7A^<;;*ϻ.^Du"A7C RPeCEyls h,j5Yu+!R0&]<}65ka`MO=ƶr"?zeO^u8zh@) .ȹ[y ܅Ե?@A>> TlACSi]Jr@@/Qn!KDnk[Zg뱱FXLR`ؘ3P 8\~ $#*>L@cY&^SaakKEd`nvڇuZ0:/KsV>\/}[6*Ϸ.+Wn 2B`XytdE% Q9NsxEF2Yu~ c&8yΤ%w79Q:+:BXrQL[jss`G-u형EH0_*Zu K]us`QnKߛneŏ7eHK5Uk<Ǽ_hGt;1z|G_~Vyew3I^:޽'_ r!'Oo֥2?9 mTL)|ݾE9?r O~Ej֚BQ*yD(55 czc&Pq]Q u/]*RWFM?R#2@E 9O~STK1c\ͷ\'H5E{inG Y4$As۔JtHg*p0=X0T5dƁPZs4$ إ:w@{X._:f1C+\ qQ ڵDuҸs ǘhӫ KlU>0hYKءg;:g|DaJiogNpuZY9~i,1"YUթ#; >{\^Q~cJC #KEFkd1Zz=gNY ]q%ٶ;/{*gn9 zLcL>u~ L5jԺFLNق, pgwCC]`E?lƓrvV !~$_?ɫ%D^79d/ # t5)/9'<ރMNc};s,5D MPݒߎ}^h BA][u(ȭJ~v=nkYfGjs{kC`q׷2ѣot{DVp{{x5S6!'jRj`vnA/sN9+qGR{IoP,Yeƒ-9xvfGRa_YOH+wNOjO{) PeN@M'? ʙm׼j))6ժJEql"o o>uP^wD·a5Ly[_y_P*Spim7gَki\cѹA\S${= YnNpa7 G62sn"Z|I*KBG|'r/ SvG@ 3R%@6Iu^nlÝs3Z|Ru]K~lSݗV IDAT\3G,D暋5~}$@Tg3ԃgK5*L7n luW<a7q~PO/z&"_2"iJ*@92r| wə؇ဃ1Us~[Gu >"qt틅yͅ18ca 5qFETnlKy?)\ F A_ɏw.jFV ۈÀe Ĕ& qّmb"~>)uIU.K\OKB/1% HqZY,>)J"a,dA꥕ ׎,9wO lN54f# D[7%3pMŃpFVf  @ل-룚& ݘ +׵/u>C1Eϝ_j7<`)ai}}3vXԹ*`xVwʹD%Oɜ2T@r2{eΥ*SH].|Tgs/746PX7DB1'-0륎A0R:T c+8) ]eqs173ؓ4*ȩ MeoR$>Ve!1WCH`2LHF֛a9%@h|c~stUkYK\vsE7CچQnFSu7 ,kDAǵrnWˆS:6ʪ9nGlsD:?3Ʃu+@I1nt=(qD<QUQsҵ\zqL;bp DtR x_| Vfm-Epʄd1 iș sV7cD|ٔ9\kkn|FuƙrX #2TYp96!68K.oXV@uDt~.Sr]W2c,r6rrS&Q:t6X=?R/r-ewca@y^ye,[QTC0"ЛM<#@!@O?|G7UVdY._:M>0fH~)[wiS:-.1Qd6ŐQ!ܴpTKWÔ2d&tq ﳂA-އ"p%13,ZS}.!>:hv3LOU_Ϡbl֞c3ctXw yOܞUV rk_PƉWlU):nvl{b6R)F+\ nSc%b3sA\ 3Q'l|B!{i"` >&%Ik(nuO*Ƕ e91bqظ 0 $ńy̲6Uܲ]ģݦCA ]1Kcƾ`+}Xoz+)+٘s/^xv}o)׀{?q.Tp.ѱ F[WFc= 8 kbS b Fy ?2VYU7yڍ"_~6 W(ZM^BLbEp ^7}ǁex!Z^7L K 6I^@`u/X,EA7< )iȆ~qbLɘ'BA|AG#< 0+fdl)0l;kD8iC$)NaĞW15c2ݞw^*""0wOEh&NPv:\{hZ0J |Pf ӱ{$A~~f9e<W苒E^0jYqX'= *I IMH_"y6ŹkH(Bt+"`W)CtQ(lyX->]o*q!@"=DDk(tl1,vx?"Q3 @ l>)(N[823apoA$cL dXO[OH4BE®@):q٫rGd`rL/SX)*YW(d( h犹Aت@A[d"$&![*>v+ @@TJ>(+2H1wH 18`H(v^}G U*HOH+@O4hmD&Σ>.Eiܕ4f<YW2%D+A40Č ]pŋUVU^+~#+|Hݻuc"P `Yft]_Z4zGucԼ}Maj"B D LNTXB(ބ ` z͢.]W%&#bl!06=3)HQv_wyE6e0qlY"fX(@{ UgAtWi Xa90;|H8w83K]Xc[̕`ބS}GjcevG. \eIVʋ*!Õb~s 3ER1-v;U%kd :]nRXtOn$D+ܘSfB2VS~6PМCjlb9Mћ}K!pᨮe- TsE<x2cMI]@U+,DbMʀF|Q#C!^+AgKwx/" C>xG_UVyd{D pNDUPPl ҿ/P\vcԾpX]yŜ"i|::Xld#E1w.ك 'j') 3#17F#I.ehQvJ8)3Ive{@ei2Svs :S miz?$dĠEzrQ [8WG롭(+@X宓w]}}#@nl:D@xr^6o̺-N ?qXc&S&Jsm5b]R&G,і(YRb'Qk&v؝zQL9 0 hInȄЋh@m>Mn[ &a}-SARDSPd=%roc/ }ipcVYE r>F>>Ӕ{[m{@o>A9)}[봮 (yN P"SGe ,K 0bs(Zfw9A'I)Ec^KsDGc1\O89tRt. ^b\2&m!-`sN}鏽Ev>N3"1A- +sl)Pރ%fϺ0"!߿#zN1}ib\Bwe{}^)a-i`4횲w4*r "#1ź `٠t~Ni8`qe\CIq$e8 6133)|TvM:U)T .I:R@pJbT#w'B@QLlOaZC,U%mS @}H<eu0^1Vx>Ưʋ/+@X垓'u:_ 7Z"7+>61H( YC𑉈4R` pv]q1$HP 5^3'=v>6]C2Ul';X&QVq]18'';;5> Fw?B`ZdI|FbK:DVE I* RnB^P*I* &ߑ&D]qSXsH'!CGo>뻾*/a{Z>{@wǓ-?ƯT|nک}'+$VP|6@S:>(6xY!h9.1z\(F&/Bs l6΁Hנ8>i܆ 1Ve6cuAcwa\F" sb1,Ea1Pr1#h` XjA׋:,3KTH?(tDZY9w!?G.^UVd77>Ef|:B!m]-rw^V٩UzrzԷ{{sgP6:K+[ǧ "l6ܸy137r}:2At.~Plaa8%t$)@ H "AČ]t]H@mIkGl:({mk@tNuբYa$8v~Ң (uDi%O{^ W_dܗ>uqˠ? BT7zET8OǧɲzlS۱K(;&#ta94߾ds}k?*] H)fc%+2-/?78  `EE@i]S2 ,)ݟz0&3:T#h޿&D"HC]@.q抔bӠ@1G"Y%Y>&-HjU|Ś[t5Ϝ|h;⮲]$+@X得g9~;>C B98%3N. %b )P/]>T?,WYκ@H]UjղO t}hA)7ă2s &B!fbT]>>uzu G>]c`CH)d i 2@zC1pc*bFE9Aaiu}:9\wq.UVwdl6| ES=v#xQLn7I;GTG1rw!V du4\zf  h#@b @Cte89ŀ?(zNOO#XfvS8.^qzk]B@LYn%:n-"XTH$A}MAh^@k28qzI5C× L50Pw?yß?K*aܸ!Ɵ? !x\ WKQsnX1.'AD4 nEV{ʍnp֌Z7hm ՠl1p ] ^vnH铑0}SMQ4QV3>fGYVR*$'B`4LeVA^_!$/ˎ}_UVdR3? }s :X%?"I `JͯٚxIMqDJ)fZ@tl x{]. N e*SrS:n.vk~C_Y݈ [CtPC23 [ HxiH V䔁"Qg"B^;q z, #Y BO2;y'*aUf͛'o'y?1OU~k9XS@C eU! I1j|@Abk&D/\ b{D U"[6Zeb!فX0`5ܼuH |Hsv8w)03#Rfn$IѯB_z{lm*SUV9['H}|X0*I%f\  P6d)\"IDATb5rjaOqlT[ mZȍ(H6p j7jfnTB.c\88ŋX >x Si&;Msh~,XJIT5b_ o_8bUOY*ܦ9(EƱLذ(#c\+"E t03Nw=NOӣg+W/{9RQ" R<=w[ +f8mwժ oC [o-O<'*aU'rWxUG8>@Hfc ,{.V IJֵu"4=^ABj% v:1A1:8ש[vD!SHZ_q\pz@{V\vk* ݟSq-Vtmc\eaU^`9>'CCO!ˇ,y ֚nފe5c߇Ti{;lU=Ou&1 >P8ɵ#M,8 xw!`1bJX4S@sNDo'CyUVyers9C!+{^/^~0"[.? 1|;p l7;Bh`DjКvZe9{pQcA;V1-qǛw{]tqWYeaU1}{ߥ7åvsCxsۋ"1] ;o6!:b"prlcK/>c]ԟ˱>&[p怯>q"?C=GDխ*wÑllIENDB`ukui-screensaver/src/screensaver/assets/default-intel.qss0000664000175000017500000000321615172041035022671 0ustar fengfengQLabel#m_weatherArea { font-size:26px; color: #ffffff; } QLabel#dateOfWeek { font-size:40px; color: #ffffff; text-align:left; } QLabel#dateOfLocaltime { font-size:96px; color: #ffffff; text-align:left; } QLabel#dateOfDay { font-size:32px; color: #ffffff; text-align:left; } QLabel#dateOfLunar { font-size:18px; color: #ffffff; } QLabel#clockTime { background:rgba(255,255,255,0.15); border-radius: 6px; font-size:20px; color: #ffffff; } QLabel#colon { font-size:20px; color: #ffffff; } QLabel#restTime { font-size:20px; color: #ffffff; opacity:0.6; } QLabel#centerLabel { font-size:18px; color: #ffffff; } QLabel#authorLabel { font-size:18px; color: #ffffff; } QPushButton{ background:rgba(255,255,255,0.12); } #escButton,#prevButton,#nextButton,#settingsButton{ background: rgba(255, 255, 255, 12%); border-radius:24px; border-size:0px; color:white; icon-size:24px; } #escButton::hover,#prevButton::hover,#nextButton::hover,#settingsButton::hover { background: rgba(255, 255, 255, 30%); } #escButton::pressed,#prevButton::pressed,#nextButton::pressed,#settingsButton::pressed { background: rgba(255, 255, 255, 8%); } #vboxFrame{ background: rgba(255, 255, 255, 12%); border: 0px; border-radius:4px; } #WallpaperButton{ background: rgba(255, 255, 255, 0%); border-radius:4px; color:white; icon-size:24px; } #WallpaperButton::hover{ background: rgba(61, 107, 229, 255); } #autoSwitchLabel{ font-size:14px; color: #ffffff; } #autoSwitch{ background: rgba(255, 255, 255, 0%); border-radius:4px; } ukui-screensaver/src/screensaver/assets/yu.png0000664000175000017500000221735115172041106020557 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATxy-WY&j7!'9 !  \$||^"mu[/V[mhViE&L!yG7pXC]{s$>Oaժ*T?A/o$!G[$7RFv J҄cE9dȓ& #IaG8Di(BExb/>BuJ@& 9aJPZK@44ep\lLR rDʞA 6ZYG|pwC1O<J!`#GI@H(!{ywFFυPRJK)sPFE57Z)%R7Zv0 蟂zF4Re/B ' Ts(.B^/q7܄8I"MSaEHy JZ[mLsǰMR0!ԀP! *,Dۄ ;^I%%!5ap^(0P((H)&)' CJ!~(s.sl QXOZ=Vط׽{ }PbB '}˓d|Yė&q?kX|>SJR 8HJaZUfUnl!PD)htZP! p0{8}Xr[*@IFXRAn RHIeA$ݱC?ā{g5o v7F@,xånVVPCE*T>F^pO82//\'E"}?!y/!8H%X6ߘ](9IAgN(Dbsse(xF!(! -(%!khci8!}}((.qcc|޽yXl6VoSnKmx;)B TBƒzIahxq$b ^) }p2-! JԄ>"YP\;S8'I@9Y(eVs,P LReN!qHv,(:ݥ{7]yWJ'Bg1*P9O~8ps,)V]MVh4z)ÑiNo3X32Sqۖ; 3|eYǙ((;nV0. !f`2 %/-پyR6d"ma Cw{j},s߼$WpJ"u]œ{Gwnml˄:Q{Ɲe e hᢷR'B:" Ҩ AT \P$c0# hAeB #4a *"Ђ`lyyR%5i J)05'x&tFJ)Aƨ1cXVQDe-hOȉ sW{c!\^Ą%lO #'z%  M9RCj 2gr'.RGx]wj6<J \Piג4p9w!9r !lA*]i@|կ;4KSN}T)oFxW<~@/#B>!(.[B]3㯷@4eJ_?iỳL7};.u}8mD58@q"d'@ N8#B)pf,%̵n$1:s2H6aRHS MZ%x<Kv"HDjO %z !5n9/B&DTޜJe @pPylHeQ # Z`i= aF?p S3 T8p. 5{7/ZoԆ,ZŒa5e)%Q ,+e:/s}C@A&я~`AJƞ3?nګ4@%5eyU\;a_<ӟ3II1juDak%JGƛB:j R2dsWA-:Oed@W R?Jz s.ehԥx@Ht#-rH!=(n*Ud6%RJjZnM6>pk*qcS ApJ޺lE )pF X-џ : W1#$:Ug6N ɦ=7ߍ{?k ,`uO>y(^$K\ʭ-Cwq͂WxTJ!URQ!OAD J8c u1(#ܧ^vKƷ\pU; {=;EPxW 4mo6VVwN\y+ͼx煄Tv#veo:( }R!fs芕[_ÐF\;r TxFqmw>1moMT@ dĀ36 'FIl^ B@( ! 0G2 z D( @-vp砵ZaL `8w?H,=H T0 3fG<̄GۃT,wWn5,T2Фz>SO?t^ucߙ{0n/BdLWBR $In, %.C{ZayV(x =5,ȁn0D@Hj/:&)8PJv-_j]ե}+TxP 'nGG]nvS0 5~]JhNO@ *x6 6#q ,m8uJ< J18|򗠳 u {G`!^~PK00S U32,,(ȆM{c/x%qe\T$D )(&dab b{+Fw2k 㐬ne1)r2χx'Ƌ>@ˠXkk):X^^2Xfa Py/O.81M&6`k.$Aoc$#wA0T'Ha 7d0 UNXyʗ=:aP$"r{^.!0J]c/@&IERe0ʜ@0CrƖsJt|]*ӽ51: 3$=Ǿq{ۃVZ RHzc}k:hsPHrj,03̠[c0!4xkuHo~o=3!& "h<@"Kd2)a=[9xqobE@7Q+3Ȁଗ_C k$`8rEA# (kͣ; k@ΰFu%EQ`v:Rb%5PPR :j9#Ќ2ʠ\@ >%0Q0aj/pK(I3!R 4 T ڌ'p)cP~H1ƣwx>sg]%pTp|<N:;~Ʊca ɪЁR 2WЦ%CyBX0|sEŀE#+"R(I ݅r']gcmJ*=m/te+E JmP&^R})jh@)Qь`'(.-La8f#WJڒZ_ףbǢnAy)|rMlDx(*XbaK꽍Nz)>yePgR U<6m`uuzash~{vF$ 4EZVIeE` ].5R 8rx(r E%U\`|tQCў ]pHkcIݼd h}`eDg4M1`ml6ղ4ґn=oxN=4Ie,sCx+xzŔp@.HW wQ +T‰| 찷^o{?. Y@00zoA.<[!6 n9d`$VƖvSAF I M9^MggT tb5b4 1 ,i81'h[B<JxH&?! r3ϖ9 r8X6yT\XA@V&όI f,ZxPY%W[`=RHP(,lv׼JoH "o?yo62P>0_vDY7uWᐚ82seނi,RP jYN=Й @h!tѠ0CJB(A}B/ڸca ..x( &#kصgUR R4Da>{,+’aDr#0+| Ѕ٠B*>Al̖d!׶;f1 !19^H*H!]Ωi@xEu5;7P ǁ T)ȍgz[?O(Pյ1}f+;]\H.P-B6o7)l@pnzmħi;CW=nM=vSU.f_$Fok^-C667vE&Vc&"ɹn+?fmVv}e9n)qLC~?irI/[ς#Dg-%I&~Q):RBJyXq-8Y\tj`ȆҡNo(T*PaQx׻{nml͘Ňk; gfu `0DQC@`!9m,/)ȥ zy+p 6e.o~kꃳ,8 @Į]`Ay I#?w4k=vr,KTKmT9‒P7.IY s?>$ /W\v):ӬJ (/&aO.z+(C*]Z)C}l_P "~Wo糀wx`VY 3*24 ]Ր o_ @FIAeF-._ܝ lݴu+P، 97ޅx̣NA+4MQs'EdU1-)D/x'>ѿ #=?{@J+n`22= +k+DX++w~.Uy Tr׷_\[Ef IDATR) S, K蒷RJj5H)2-B(^*Y| @IVgeXh.+f*!d5Qr Ӽ~<ޥ9$s~ܹ2VCE2]c5xы^@:w!R.c E ?kؽgzWr%Ϟ 7T'>\ ~m}mؼXVk3Վtj"a-ة]Y|Ki#>1eFtڶvZEe'A(9,8O?ӌ|#kI6A1_THbhzZ=%{8]ڿ dHHnwFKKK~õ;TB=*<'tusc%ڈg0v(5Z_EiT(j/z<un_;X?bBA >u#~^Qh qpyW;;Նd/qql{'d{!(0vY%@cH^oAI$IQ d2,r?|Ds)%(BVs+ccc==fLd饌܋خBLlW3i2ƅnşF!M:BrMMa0hB }lllVaeefK˿kyTa*<'o~onH lʢ~֘YW3 -0O9R؉ ʹKy!u%ޡz.qM4C F45oxV `D ސ ƴAX~#Zf!$ɭG#y 0w8;(iA8_]d@kAyc Oψw͐J*s $I !dQHX 1ǭ_^CPC):/~Ubw³Ax:ǎspxa"DWλ;$elq h6E:e>^5)L Lc3F2 /AQ'8p DR87; {Rcre)!.Jȁ2W@!LKe*r W%$i!'z%K( @3Qv J1F4*gA̱eCh{ MGʦ?*RJ!ʋZKd,MsQ%EP2-bZv6Cd鱛c{h[E밄'!0HS( ! 8|q޳a`4jM 8~\vg \3_Ṁ }NǮݻ0 c4 @:QH{vc vծ;gNBg%*ֻ~es}N1vYװ Ah!::nȄp߳V[E{m@N;W`,,Lyha1F jZ $q01IFI&G@!$!Q`J+2)S(qOvQX4skHfEHP Em>o Q GcBp4 ( Vc4-H)16R#P*5z !J)&KۯWPSs?LXN& `WDLJ="!+ {#ECvB4E姟ld+<.EaB~Rj5j\$:ݒ(JŖRqpccxX[{<{.Z`LMm|{<,sG<&7 8:{#4 H0~ރ7MA$U  9>|Ӈq=oYYB-R[czYF !A)-Jw,hS{H^gdy!QPXߜϳAIp󣄔h4Qp! hVObs[Y.Hk[rw-|aN9TYxnompw^_[P!2Q -loAαY!Fs7WrΝ 7,Z?^Wuκ^ߐ̊W~mf'^g!9z#KKKvSv4}_^yez xo᱇_]N% ! yg?8v a҄נ B,f'J!JEuD5*pF \INzmb/z?P_İ0ILlq0 'z)XiEC2Ыqtpz=A=" cLzƣ8cu7:g4AL@JpWǕW_ Ҁ$LPq|榛8P6;7ı>~'~ QEt>*}<aYS]~( i 0pj5vi  ;WmVDkV*-{wͶ҉ҵ2+4z=[jh VZm޳oߍfǾʋL"2߇lonfBF1ifuO%&Xp  ¼@бbWŢf`aP7VpiWQVLvWra^bx)s% h@pLiezE X褐}oeұ+mg|sVdAJ5Q&I "(c0Ʋ}ʌiu*;'j:=Jm)i1PʄY`tv_5ҋpJ"IS_yU-"^b rŎ` ) t[  dy?v'gƱ|u Q|GBO4/vB  r$u=. RH`,.)(* Iɀg2 PBI*${yTWA0),:Q,/no>'ȿQϕ>6 #J\ R(;c̬ʢ +8fHwb2tR,Km6AId5 H"`O?m\P ³7~9% $MPP^MpVVYPokfQA" 5Ns#%\] O&1bYcY+1ؿE8Px8xؽ+&g̸FHXЬ4 IC@6HhERpM0Rz#S4(@PC\&<7eRq*P"ե!]Î֘H$E#aJM0 yg, TFH {huba媝O@tuFJAHPP( % \pHaJSK*7O>W5yk.ʎ/ӊ*pywoswu Jɔ<2!h\fH̫OgV 0q .x8Ɠ=5DQ+8j m)($X_[C 6 V> H{LO9(%CXhB׀MxQ4/T s0ucƀ0PLA 8tH@\)!`""(By= J0M?PZ1cJK&bp(oeHL!xIBg@m^Z~^͜ (*p|s_CO@+b],̐O^`e.m7)ƴ=x*3"Dz8{|Ҵ(9(s^". , K )!3CaވP\ ޜs;vAPCY7}YG0h4tjl q~ }O T2)rOsY+F %%)$u&&~s`ũRVh(B tıc:KM IX hn./4 )rz ]RavlR0H#f2’!`{W UDENA|K]6dؑ/J5͑Tɫg`s ڗNH8󎽓cE7~.JQ$s=s$IFeO$Ib4#YYD "QJPA !0 \t=@pfI)x+F#@EĴ Hp.[-֏! NpoěN\tEط Jc@e:/r+Nw&}V~5i^Ļy^$\,<{ ~LaFJ%`2R` +fPJX G$R͖hݴ^uϝ O*;O< (F`B$l}I/IHhZE^5+z ^ !H1Myt77g{|ؔ?BX?Sp5DA;|F*Bp;sqEcuҨ8U׽+]B(?څ.4в")"qsϙ13D(j M&2 W D)dD/PdEpǾpEo*pJy?O4ԟ9tMVQe. )X5 )@1v]X^Yr,01cV2 ԬE<ǃY.XB"wDj>_e xn`;0FA+Dw~ z,5໥wܑܾEzؔ!\TRJGdˮF6o>q/}K!rp>gݏR~[61mga  D1]BW"SC "ecvv/8עXElIfvrz+N45r!uFƣlK`ͩ ѱشVJW-m%RހqBp =gxpT'VŽJ>t@`nU h/ 0{*0Oxք2 ʈ.5({i".cZܯSk*٫yJrkWV1rqJIom\v2Ŵ.gaj%4DhbsݻWu&b[=ڽ VdXY)b/5oAh]Vka"B{6g{ּO]?!NQ ]l;?0s0B*zWȻC|'O1]a.ӈ/lFˈd21"s5hT¬Z jʋ+2L&Z!pNhdѕt/B!khClz?!!9eLmI)%BDmڣ,-=m+{;5=ms]7t ]>z=u\tOA@a:BXYD BqP#qe˞YE R'z]Y*LѲOq;o=_" l[s{⼕ag6w^].FT( p6z ° j* ?P^aF=͒?wdT8!T7~;vo7*U8^d^VGy&1k`WE}o0)o!N?cȦe/y.i8Q\XJBABI*=| 4!>bA0/qu[]WV?zM.f yƢ t$I jQŞ׆o p}r\t]47ju&$Ġ; mcNIvq#@Ap^o_W?UW_ /y%^J5R&BRv>v%9s2**o?~O70F0pF?EZWmPg C[Jp18Fؽ{aX|W{Įlx` R\@FT `0Dp$D=̋OCٶ~UNΑԫxzSӤ4Y&m}"tc]#@SrRhI~/=hz6J)c79miUP$D 8pؿ?PHi,u̪}F AA, P(Q,)d;,"ȲƖDDg+,hI4%eʄIE@R E V!+}wZr9t*<{{<{9q±8d _J~~ʕ*Gc+mO:6,uJeo{k~@uQf.gGć>?:u_RM(J *\ 2W}Eā1R`¾W?KBޞqdxOkǴs7.V-Vp'Z!sDGE9h'_ȶOCU(wMW[X@{615YmdYc7:o޳뱹YYL{&x5[[[377WT _}V`"k}V1Xm$nZ60"Rq|[oeii! 6+‚uM'Ϡ"Š7k\D.~ ?N-**LBߠp+>KGEpR*65Y -[4uQ?hDUJ*ul!MR0-ӶX[3LQfDIPw'O)ĸrc~!X9!O[S&THA!"({B^". Qa={𡟽u_JrQAryww;[[W [$rEoA~ToPP0"Z CZ$Zd75QMpQl4%Q޷I|?JrTuD!S}3 ޯzL(p^)"utN\p칿Eɥsi3*wA{{cu]|~nwviJ-θcB8g5n,,AxM]AjpeWB$c*'B` @gdƅ2UEp5W8usqg{*!}ϽGoLvQj?}vwvyvH" RZcq\xc<ۮju(}/Ġ?Ol/kaqa-c dx%o `}i痠цE^ʖ*/sE2XN}e+Q4!1sPsu=M}O U3:ф<c8k]3gXjju_ۛ7.bxEIݻU< du?Tn%kpQT䃯-%="iXΥPYc8y, ,-#%X1 Ҋb2R@($w Qb<_G>'־G޺q"O_Dy8 DklI9 @}Q&] A&˱.95\Go5<&>ַF"mTlPՠ+U%h1δsb!Vdkcbh\A׾=p،qq`VuVQƦ*ǿv]ip4U8{$F⨟5%Xե.=#-DJR#H 6Y9P #$3~6e*9V"2(% /)${{;K}u˻(SU_$~mۛl>#;S<3baIIIIyygUh"̧<$tFwMґRyuIӔݝ]$6hw8r0q'I?hМt:EvcLEQQE0 (oݮg3à?b.AT . ;|'D1~u+/;8S/gCV+Qg\WåA:B =Rbs?u/J!c(̣o{{;e&g-x4 c\vø#0A~*p T|MG~baee6^)EӦr,ˈ9pDSJM,udž됎ec݌/|Q>׸/*S¢!nX[[| qL\ ^MS_Tb Szwuv~ita3n7 f}Z(G=7C8gAO>1=DD<㟭*a\QF1Yr7N8@pKca15]!.1aE*r(MXB? +S!vc?CM}|Q&"p))5*?( >J,swP&4idYVXV-RpQ**^KV^_tblMQqOi ȱj (+Z=>7i 4?jfBR` %E_Zz[[[(XXXm#+ѭzU/x0kӟʎG K|졺uWa{gά$ Gen~5 SvrZ?:/fL&-Йh&yr袒p (G޶^r#H.S #ƤIA/|x+ ߽B[ )։vXʵgHz 8{ Kt:n e`1b>ƒ @QV`QRU H@(&1(QPݬ Ɩ~l)֌b ] RF3 x9y$Be4‚P0pZ߷Fz=,,, ,\Y~6)MR;L 8X~T@^ %n5Y( $].* y3 Eagy{ 8:s8IZnqol,g{>Z(cN`RԵC%(z ̄(D0ƌiI2Z۽|"&B o~ B Py[uHqNySRB]wa1=oW.J!}>͍v;REA9AxP9 G #Fi>JIxs˿>XyV u֚^G"I9)ZHݐn^֒wLb]LxuXeҴ9}`L1 mG32o!Kʄ[WY͞!c2?w>(KQ/z nyZV`tΚO9VptشmM_}ڵjB, (j'ű!eYKtNE9ltViRZ9"Kk @jK% Zw=c3\Ec!4` t^ʜ)C%]3M㬌s7419J)φ!@uAߪH0WL*@Kznnsߧx;H.f14Ƚz=K"24"q A()3j`1hѣGJd4 byAicPO+n(q*lO̅ wZ|`.u,L3{4'_w9<كT;iꇺkUQp ;;;t:'1a2|;n^~9!&T_}anGcj^e?`~r*ucZBJDGQP95z߽2ۺ)&d(JhOr]wc4Ns7wZZ2J3FZ3HSdюHOD( |=aMɝsKA-ϳB"Ak zd1dِp`'IB ݝ%{?[/2EFkz{^y@"@e q+)arPDK 7|%ITB_~b6mUmcҔ~DKZ.{Wσ#ls{nEQ^c=w5^kOҫ/ Yۯ"wՂx)|&]<[^{;G$neYZK &HI Bv]D&7AWfKEUew?-oS*\8m!0Q9Ƿ4q rТuƠ?d}}G'ɗ0ӆ~_o}׾?t oIv)vϤT"dEqc@)hjY?,Jy9k|M c2(k)*4v9Wg ?O<9 Rs(% ̵"FWiƌ"rWO fgg`@$ ׹Y ܫoy5aJB& (a&C DZ#ӊ#JY6S393c V+Y^@bBj 9 "?jbq~Ϝ:bg}}>|f*cqТ˽uAQ,@rR i38}P0/%t IF!Z z?05!DG>mUA:$k-flW}<ߴVcFlmn۝ga~HMy}ųV*Vz EWZEG*voLVԏca&5} EU}&jLj8Aqi<+ަ$ iS$IRr%B9ѣMcN7𽧾O\~VRIa[  [hbRä0\e1E̖k7U =β<ڱ_ߛyap6O4%B9:t~DDe2hLR5P֟-V f(hH]e^5KKKSgKA8WC.Y^ 8?o5,7:~fk+YcUb\ gIGQ#|~0Cn*%C "*G#iJ)g6fڢb6q[A$B]pdnzrجm-ѐuYXXx}T;_:B>WjޟfR h~RX]],s668zn!tBF!'cW|j7\!Q$} iǰhь2J\ωbXCr[{~o+? >Ɲm r+ .\*tu87j5@e R!MӉsbdžZui!J2K;_׮(f~ʶx+&UA`kK@]u&JS0s(Vʧ CZK.YC)ESbƱ 7p X;p U5HlLeATLwLVTk-68{B_=~~~xwD>zG676^)H"F(rA9jLpiJ˰ % I_ȡ^&y˵zR8 ߶uni_U5u oKXn]TY6Pj!j- ,ܜQEIqOT*%c1f"P}VP4.e Μ^3]riqy\OO5>шc2D;lHT#T܍oΞ9wpAc\F'8}jВn#pLф[2DSlu[VEh9fA:xןj& *^gVdԱVi_Ue+ M.js}h4& TCG>|?as8ϐfǏiF<=zׇjA/7m$ILgghx>ܴLANۻ{{#FHDD!  mV{%A o %!1Y9ύsԩs'>o/hyS'Okyreln9sj[,z :L%*9ފ˳|&O:ogչ[,jzF*t&K`}u鰶DbJT!\@k(uPs&_VDS k{U&ڏ4e{{k-,ZK#QΛ197?7odiVþ )ֹ'<"# !(-)B~TAg;UDacmHuXJIAYKfa;şsՁWFK/ bsbukgFףcqi"D#W(nT΃ ]̑\2eb{yScӔm5<>%k($pK+snAOB⧃ze]Y8Cu 7>b̢MLoԦ?0#I07!k3,+º ut]۰y/TɘJ E&CDCt>\Dь_<|-yC][X K+m4ų)RŹ5(Y,\>|n􋧱gi7+bpwf@ 0Fو}tKVK8qfO~**6$ =ֲ{ ~ﲶʑ#,,Ӟ[鑯*z\H7loo38r䈛oT{A9JG ԍKWD-*ƪRʕo8EQ qp혾џ3$ |,8+KO\BJLfV:v\{ BBw; /Dy+yѣ['޳Q:"qܢKH68FI eY\eF)xM& f"._;N2ViS)>ٍSK;~[o]`c ή p_m[$2B ˰RJ&ΏIgW!btO~)d&h%"*f:%XGpﱠf@^677Y__}9-*R)ɤw͍_RKx7ҫ"?SocMv6p/+4{!hw_+vr8`<ד$)H1@R!+*A/ZFQA86Kv_g!D~ViBW*h S%H )'O/<1NKoF#)XUFE{>裯y7`< z?ڼRENL۹ QF&#$( r\\)5ueCŗlUc5H2m5O~lwnhTDc?Ymө_2+ʗsuWq*k.cʗw>_]$K"Kt%[[RRE"!H4|Z9Y[[,.. Uc-6YQU6KYn5ByUQʺн`>76wXc?}E__뉤7ʍ+D1;[Wom~{?п;y7{C!$*# hZ $R-19L-2RbP 0 *"?a"%caSJ4qj|CRl'0ioQ `Ƙ/YO719A@  D #("I<׵1&^:r !K* f9~s/oWzdC9ܡUl+~ t|[+qݝ` n⨾2i]o=j֋YDc;ᦨ0'-f Vl~&g]W糅&/1)c;|7E(դi>w.yp@ 6--HTd+ "pr7Ƴy*죿ryH,(r.[-Йeh\n@%yY+~0\57+bXkɌ!Ն=>gxyUq9l~snu,̯a>j~mJO8iʆWB jHCe IDAT8k Ynߕ2F XbOQƔc"[>upߋ15 +V@ş{ ?ܡE.an~<)]#()K)uYrwQm'h-TV+v-l):`lPC9(G )`j[꤉Ы^𮤘PmO{b-[6WD$XCir^Pdab K&@ւN_;o恟[yQ*/ܶNEjKe9Q!q[e) "V! y:;pvW/uu( vk-&/E5O?O;eui9H)nq1N=8C$:FYYK[H)5:˩z'ZEPjMSsT>>5?:9m,!y)%ݻô`-QW N>}~.X"U !B 9*`18 5σ* V w۹W#q [{\i'] IZ65>&=l.΢|X*n|/a{?,(/:<ȸS2i]%#:nRT ֺbOS_xmw ʋQA{ي=Lb[nQYܥ (&Tnl ሕB1Ϳ6+L _I6!;ogq!ban(nwZVfq]Em!' u/ [a 6}7YM~*4Mꔉi] m}Q$o[A,$qG°LJWo . W(bs>_߿SOUlN$_E4(\$ ހv{q7\Z?qCk?=X ๊T)sT幖}W,έ沼K*(&MS Y$InΞ:şObcn0o5!!6C[VT-&hZ,QwgK9\~Jͳ%M 8<8G"+۱Fu+mT\ sIdlnom>t;[w=E&6Ҹud!\?{=ܛ~?gyQbGg9<WR9zW)s!x9h(~4ưXqtm9v8"E/A&A[r%P eC>4-ЊW/MM5>m P}gH T i>:DiHXNfw,:^r%,$1Yo>y7[hw9 yEB̷+2TPS$YD_dgGt/BX\mT¹}Js|V~'yJ 镘8j& (MMӻs~ ^"u>^5b̳,rHߧR*O 稠ŇU/[n^v'$W]}+K-ݽWoإ{y ~m!\QC!TH灇j hPMnӮ[(h'-6EVɄM bg20VpI`I9 X__K.js p8$IyHFĹrxNzhԽpAa _2BSi}_?ϋ0bqdan5^[q:Xyxiګsq|VAۙ$IcHJ)z{}- X# >aM4-R8.@?V\4 _);fea7ñUn]o-݀{^ɓh[:*sC5]W9ׁ6> 7\.q;[ln;O3z~n#"(">zs7 z B:+SKs!xX# r*DA [RƓC h6MAg<۴6 T'YZ])09Kx&zhu<₿,-/R(J _4b_Γk) k8b"[: L̪u_=>MG={cA򆤵M'$RJa l4CW< *qi 1.b-Bk~cr16)@cMG75R8ya޾[+W _[ }3Os٥GQ9㝱.tJB&Yͷ~& #kƖk֡BŭtP[V(rFs>Z/_Q a%# I {dl[;&iD^k##)e(lGlu6cUJEn VHʏֲr^3"cEoSWKǍsw}O}+hV0JGv[bYĆt6 _֙3H!le WFIY {[>?1ÇKD^do.[W>. ݅\/shg ?c1_{Wx.oAkFXF&^$θW藾L@܊hta.dF=v9?]2DE@gr?;y)/|_:y)H]Ay¸BLHW/z!xu/}ߕE֠NMbggFÌ~o@`#Ks$־uΏjSHBg U yc+$!]LE<6,}MDi?3+66Z0Ptap_YH_Έ 4 }z{=,//aZETf'&V|Ǽ_،4 nhN+PB+tUN5B'J%4egk k U-nT]l^"fh[4b3 VvRnrX~H[JK STXLg}xO-"Ǟ9F^06:7MUBԦiq\ ?y%.Ǿ,whc(ݻ6|A.kc(ՍixcPJ2b/!@禪]2WSC0$`"I37<KHuLN(4 Zwߣ.ȶn}]jwfx`5*+[ub^gϞeyy i[`RC%8W)h+__Wsuq%@]ֺx^ǓO=.~{]8F鈫z yЈA {{=y@TYHD9N3(.’e)Q$LHi.Gj"VX^ ?;]u d3p>N@]p"j+%Jv\^%+.jU`ZCY7#߂̡8F`(_)]fT8plb_NkшV8U(}eYRJnm4+؂Aʉ=3_ҕ6Gi,{%$$g[5Pn D {tڿUBRr7RW^sssh 2M "tdiZ P9yW 6$Q^b'P"cO6+"%#ĨH8q\M\e,lmmciP1]iC %a1[]WsuET%Dc|8v٥.γ8;X__ɓ;vmcXXXp(T[z#NK _DILoϠ6yb6!s+X̩wc?Ͽ{yA>Go:󅽽=o Iܝ"8"RQ;kLȁv4L>(eL$ fX`Y_2Ȇ-ڱ*+s[К7&3whhUV''Xj-ԙf4tE}("DQ(N q<&/_V./2mtFΞ9è?$N˥Vd3 mQEoU]8j`-#=~Z}H!E 6v0b;$΂ğ$ν7M88v_b ;B`I}$fslUV?9]]U]mf<7lŕ'J4);$rIY`>jpr϶'ێvz'a虐kbq)}_?sX^h> Ub\34R8)zY)m[9|1|8. i-Rh%Z/JkS=ўF$ͲEۡV"pz\Ç?uϿZ|{Hp ҘK'dH"EL@p3t;qBkֻ1;w| ϰӏ=kP S&n7x߃, 0'C.v_6){p&*I #%+ ]2ồSy{~Z]!R&3Nؑ&kw>Zi8kz qW#OQatC@1& h > hQŶiN2EJn_ڞ$L{\ E˶H#۱v׳eB->t|g XJT}7o.-Ro-=Qnl1IW$Ja|elNӗk?ff,Bgo(QdX(M=܋or{hd*kA&9[盤2N X&ؐF=Dxħ5{)~1=FxI,x3%Z5j>B"P%hѺZ}\؉41u"ٰdZ $v{;wVK20S(JLu3#ؤ1yTFs܃Vf8B,$!B.Y~.1i\Ҷm1Pl!R)z.Ǚm kTBxib1Nu6 sm(Biގ<ҷsSFvjJ K#>F6'Xw<ߍ(zN{S !ĶBR $VCHp8" Lﱵuh׸; T1>Ҿ:53T lslQX9mO}ӄ }VWVJ%F%[w7ė"?K&1!a`fi1\X@X!@L5ьFVW/̬^QLwk@kư_2sK3`vv-9~(^-$Vwb^Y)]4hf*KJt (Ux'*u Rؾ-v '_^^yJ\3bאkw۵d|an7)m (|Ub`}+mMN1lyv.<8fuJVJJRN'[Rή /\uM铌b EYHm~dTn^Wp\nqh4"kmcLGCi4!2 pЧp+Wg3# |{sOBdEgy\%m*b'E@/j>cu;,mr<|? bMYzZ'7[gQu֑R#*:̴45{}/8G8JdX`~pV/D%yܽKV)vOuꏑ%2:ta=uf.p'ՙ,92O=F/^$ =sbNÙ3gXXX`yyy*;+XڲTLPrE-T{Ūc9'4*!K$m\0*L |FF4/~D.@tNgϱgOߍ"UhqpymlQy A*hȒf*WWsϺjES_h=HQ,2Ǯ6辏bcɚh ($| 䛘xeX>-lűL?+/?/pSyow@kRھv=gd!NnN)_ _җI╯7 vE$AX6XE DbOD_) ±W шZ0 l]seI&9KyCץ5ҥk&Qi)ZgR0kiPX+4?a^PzQ ޟ:̵Yo,B!5-11,YzuNde'HF/ P,)]duu+A@^٬Sי:ǿ@|\mwɒ&yU+e*z&ܭ^+I mQK+K>+z>w*0LL5XG;)m jM O=YEggF8qY|Itxkٳ_HHږ&rP`M.M22{*D')bw !@{(01kw_~7U 9wRN6 `|( MȄBiϲasrh a7FHsxٳ@hZMY\}9uTQRTB٢ }2 s=zӆW&1WﶙyHÆ&L11dPRYn&WhU._Wfp_)qY9ᑻb3vBo0Hsr+.}ؿ֞a~C 6qZR tΰݽcߋm9j?k$-z,< y(feS~Vۥ^ڳ[ %xߓlmK?n)ѫ{WKhI-օj&C\wЦk݉(bcc#%w<8SaӼLˤ\EQ_a>Y(j߁}QJgvv1dm2\Bp3;Vk,A+ŕ+nx"fZ0?w]Ο?ϑ#GhZcc:m9kp,T=<E?Q0%I_{fPÓ8IT5 3R+q=wghA1Z>>{/kSQM/Tէ`0.OWo[*ѽ?Iz!F{N|8sd߮\O<'8Fۿ՛?sWӋ*G?{^pZ&9$r HC ㄘ\b*idZ /kS3W %A!2$1n^+2Cq\ U>;-67VL-Sd(\cso^Wr⻴ 8VF7ؓߝ<0y9$d 5gfg'Ԅ9;a~wVq& C~v%?|?0=bBDw/KS#^ cIћr͏;Ld0KHga4Sa*J%eMP β2'*|La(yWL2˹_͗WՆftjdqF-[۵:ۂ^ZZV(8IxV+p8ŋͱTdh},m\7*-)͎3¤vFJ:  zI5i͝ڑu&luKKK0Ic+]gi5x30 +$x̀ N '#.B$>O Y;]+PZ4>Rž"FX{3J<'=^W(ehڨ(aY͙ RU&[3e)U0<'ͷ}3G^kҞgG?G ;k4i0I3\ӕP#ưR gۤaUTj5áIA4BƜ86+w(C&]7-vzx*/߃*NӋA޸^~61R*xM䖋M vCd ٜkӘg$7N5\-\2HWlp`B%va"H`nT~**I j_anBL&&0Vʨ"Q$(Y~jEmmfim1H٤ynnŋYXX6$[ /ȅ* T2E;vM=iݹyahĩdSa2U0,XSEzf" K,z7<~ʎ=>/wD%gKO[ƹ`Z"/ eTv:X˰j"OTeӌaBe)3*:H+Mo˓=g>uCS6`%TdGހAG =gΪ*Szl1^pIhZ{/_dߞ=p2t$8W+$P̅ᆨD1 z8^_͍ +8v_YH$K#S;ws%Ij1Nb|w^J.$Dnil3U3e9LҚmBn hs;ss_縲ʑcG kpkšV@,b: k :DټqwlB"G#}*e-B?=em r3~V8`R<6d7-7_!/"04,Yqk& ;ruzf|lEaV FC}O Lpk8xV@ <1FRbD뒇< *ȇ?Oεz:֌.=O@B_4e§+C3RGC6Gf]f. <6׶x`քNʤu^Y[9 <lΣJE=3|aʇl;^;|݌ G!Se#[$keX~>ƙ>%*6(- ?b{O IDATρض1&;~;W/(<{yWy)L ݊@ oɾv|pI6ȟ.2m+{rjOyi$cq5D + 8WYi8Kt\2\G?{>ԙ+U†6MQ4*e@$ 6f3d7en<L+N+,f~T+;ZM~_ޖHv*fgx|z 2 s{yF|"?Ze@?qE_pcx2˸w~FNЦYآm=-\f\Bh K#t0io GPW4^b`1OJO`߶SN?ZuZxk4 v;c6p8< 6j0^RݖJQg$tKhwڃ(Q }V.^AD8|x>Je#'eĦv"Inc1ڰ<&c|sZ|<)8%:̏軑BryFjwle}8j[>\Bv}9G*=Iaɓz7D(T_Uox#_ )z21TDdR=?v#b~=K F{O~Դ׉ɟR\}ؗ$IIeL@~j54u^oHn8>'IΟ?Oa[`n;B&񂪵7dW\8̃Arq̾ NQ+.ny5>@vwCJ)o5LH,~,،ac(5;R&@E֦0Q=ڒxo8wq? >?oͷV#i9*)c IqПfߜetQtÐLs:Z~W[#/ 9V(RHi5R'jnAil'Hy=oҘߋ4NS/r#_3xٳ|Wt&/u4gg+4# k3s[?GDш ,ӗV/NwUUW*a(CCÐ: !ٚʜ8A8 VJzVuqᐵ5r󞤐:A3Ib,,2~-v>szA|=?z'ւյ5sO=[x^Lkk 6{0<͖7f.@Il|S}YC$lKyb߀A7eZ6K/,Ҟgv~FkfF ~&Kc~Q4_GjHRg٪eڐ(>#=F1op¼ٳ=ib}StȜ|MltFAYmnn)EQf4ɴ)MČCqL~o_|{>Cz!=!]9 oe J>8Nb2qBPH%M woT8!zlAd>°J)Qh:m)0ń#A.Q"(IBVd= aIbڈv`T4jl 5_{>؟\{_kͷkJP@x O?rtk|yװth3 m.7O}Q^ SEoh2ih DBTFHа̫:DH~>=_`c}=o}=?_ T#a5W#4?+tI+id#\Bɂ"E9DGJ-ȊsnY{ZrS+$ w۶Wn^[S qF^&N28J{qW?Gzד Dq!%q [[}ԝ ~ap0-[]V&VLiP\r=XTn$G .{+YO 5(@Eۨ>[ !znq<Ⱦ*Aָſ/9tH-7@s6֨xzim߬gJ VPÜ^& x /3nH6H*toYoW5Tn7k NLlFCEdfl Qo"JFY.KKd!av]Y"H+N8c&Dj ~bT-`j|.:i<= @>UL}gUk$ OO# a(hZP~3YJ4cZk"t> ԗ~8}!uMq!@h(FКe~a%I=lRlmmQh4YwEt76lm47{i̠lVĩw6'kbss^ܼa>^% вUR+РFWָ‘CiΠ9tmo3A4;~W^waal\XO>I.<}C'q>ɲ n_p5lQU2MMzV@j yv4kBH}Aݢ0hra.^)J6J-- D^(YM0R5EǪߑ;Ӯ!5}jx5?og&yQypN2;?s-QLsrF}A&,(Uy'ҳ, ,n89?b {HkD cFSQ. n'|{PM8 D'jhg>]G~YnW233[M1g^; 3SGw( Zvnҹe<'! BfffQI('O"ℚJb4IՑ"?:hDLΏ Ex|sDqqKX޳G" i61_̗yyw l6sZ (\[yMVc=gqRvnK3m^>^*N G;qD Lh0"M5%)X(z[V8Ơ>Z{ߺOSQ5Y[4"-},$(lv,G]3v3 $E=$shak㙜AHl-*+e^Q OZe#:tGؔIKe(#OnJ^@`hBMoկ7&]^8Y fY6 4]n4g-~XC#nf:JԴ] IڡT 3~dsdDFW *-wuCR5im7ws~;J!8_¥ /q^ +:A@1_4EaR[h ~0CP\̣¿yⱇMO>o~ww]ӶHR16Dž!h! }F*a#UȬ36L9(rxQ7cHח6ʮ6D^\R/fnv[n("f""Jh!8FI?xg/Atz["KWhʐWǮ9F{f9fh[H'R sYə`e{Q$_;yw 6;_<֞"lFrLX^^W^KBk Lӱ!Nh:&M"3aH{& ~]ԨY; MTD$zI\{t:]@28F' ZH"E@< | d[X\iR8#J&3OsV+ڹ&IL&! qbR&[ 6JLi@BF%rXlKx{Gkn7|M+++&rYkIvӮv$UwAx}Ή%)=6(RsA=$Cy}QCN2;8A6`LZI]s?N2Q6fQxLSH-!K >{OPZSOUľ}{h϶HZ8-"#>|Axk^>Z9ža tx8gs8w?F,é㑯ϭoܗJh̷k":kdiiz{f/K@O6o^}1ZKCgLQ'5&{dBR2:{x$t~ YE@عsORKI,NU7_%pZA"lR&0ȷvynYҡl<Ǐ08 Zz 쀲$"IB$2e34W$~%B&KR:x&1ED[<0V7 C'c"&Xcr,5#ȅB ׳?I&Ur]\Hax[R-DE8f 8 1A(RBQ cu qsС," u>sss]Ҙv$I -dR hġk0FK #ģ|O`,nyRW_ Mfۓ =,Tݿt|۷?āt:^a~a=M'AYMG׾` pc1l;k&@":?-j[\di~z te7Yu l/!tRU0/G )p|lh5( 7 >* DET9_̽.Q_؏|{n=Rzg෼8~r?f %aG17.zb$ /QހG~}෹﫟gE:{Ik~6;LnZ{`Y$e;vL_ n~3g9z(VzrkJHRޯ0 K48fD }W._ғHO"ֆi<ih65ku°xAHHнgڃ,\%Bzx me<_d`}֞k,5VC0 /h0槑t|ߧ^mWG PkdJWV7J'#MgVF_H!6e$gW!MSY(xxG_9g<>/U8qblTqw06 `42;;g(EL0&#LfHq;_=Sz^g=R4ͯRO$5:|M c:eo- uD:1*=.Y@6&m2"Dh5ߖUlG/ӎ$hi϶ifu\Ah߯n|Q#K-*]Θ2*C|̇Y:e/kt7 ` ӘX& RdP^T+#u$sssı Hd*!yԣ/z;.]o#R |p'=FA?H0\MÎ>zChS -RIm4#KK7%>Usϻ=..n@"%)PHfavEء-MSa @@ bbwΛ{#3GVVgUW[RF{3UY]WV ΰJi67wX_dqyW5Ua)nll">@{@IkosklF" 4ueXڶҴPGt!t3F+mS\#*i4}[z2a&C%l"e@l2fx~Vrs$ᡇKU/#>q+b]@s©^L"Uu{ =x(20`+J$/8W@b5( v T|{UG^z܇eh h!ED9z0+D0j6hD(eXVtfBVL(˘cxma+N$-rMRMᘉxEJI$L} b9yn˪qiUmgPvY83C!BH[~)5քac#L~nÇ::yl  Bi&3qǡ;p{ a`KiH6uJ1gfݦw8ynf</j~bњĵR0W1Ќ&ݺK ,:De<.k74J3I|i ӧO! I^bqiť"ݠl_0rCj5^APn޺u !76ĕ=o dYVl3p *B%lda#(10,+H9>&ѦkZY/\7J-i ?ln͸|(]<#t-68H=^ɬ(#KKous~_ZY뿚ѭ 0 MR FZ+\rMEW$$RZM8)]CF`BD&0&Pѝ||( cȋGnAPmt)C]|offD&`Pz<ޕRdYV r@8ncc|B&)Si|\HXHX` 0hcC{aaOgDc+%{MEy6C U-Kywҍc ٱ𮦌p1̱>.?nc&*omR37?o[D+w:x=pݙlnl;& 0m8>],@ z{߅T 4a~~nwtpsGp9~P؍i|[YK֛JXJ%_4/۴_ /9qy$1c(>jTQބUv1Fz:$v{p Jh`gf9#|>_7[[M*,4 |룤sq.,,,E:UЕHku4':OSdD膟?Ѐ|8-I2@_aO=OeB-0DbtNs'nN^FA),2dZ+VI ++D[h' Êl=~8_HN8~xTԄ 7eY`} چknJiZِ-M33r! DI&ZQ;3QeZ f3J*jQ`2XEQ$u2N@3u4xK4drfyWVHud ([ 1asT3V;Fte]ф=B{7{?wdVp>!KKv ЌjMGrӤ OC|!lR13 3!0`0X0i.{Ӷӄ>hMӦ9Z N Dl%ϏꇷEQDelllѥUc.5i_>vߟ+jggr}alo]dNQJۄV)F(Eٰk_~ 'u *PuB cIoGƜ}y.?{3CK#6g=5O/f'Km+""4 xi_c7CSHY@YJՏ1ߋWZõ7YRw_${X[Wj@uS,1ln#!RQ!|j U۵ ~󑊽BQd9d`{751E0kZ:M6Zsk$ZQd\ $7=T`8K0TWT;8t[wyyGF„@D!2 mص#(l$A+{6D)@K(_,?³O?7\o0&`|ڬFEI•Z}W{ޙ0N()~Q_Gr}?rq9ʏ+>zzҙQLx:W `pi#r#-+6Ў[t:1qil>? ?ښJ~ BGFah7 YqQ A @ƒxN+.퇿M?m:đS6t3Ѷ^ ɲ: y?_>O}m路(Y^Z&/`m I-\yzeSX4ɼ` 7*0NbS渺 -tMljn66]* ;:u)^}FWz;&0_K70Fگ>AΌM협gRh|XV'pd]X,#ښC0% P8L"Uٌ, V?/{0j5RͽJ+[WOp6amMZxR<8-#`iӛlթ S8uX{3ilˌ{Փcn21B"|>E )QTf;t6=SX'.pYq̝w Q¼ˋksq\ n$teKC\8{׾z >z<Ȑ4d"F– "fS-ϗ֦a{y35c|qcƹN(LfnL2hEr777t:ϳpp8,lΙ%Bs?bt|HV@ :Me !IT(ъQ2b%b v3\/BC^ ZX9 IQ:=EC,$) JQ`|.턣PFd<3};:@-פ5 ]4;MIkF#9T" 4!+r%ηic"cM[Ӽ0hMh}~VI`j_ӄ&Ԥh)#|M_Z1US,3[ٰWGktnaߙ^m.yS6bZFy-702os 2$J1h`4J&#:.a8OP6&|)?kJ9RT֜ !0F^|5\to2«B61^u7NO5%x&ELpX9Ll\FѢ*M*itII2B9Gؿk̋lwA)~ `N w$g5熔FOhٷ&6pf~~7ffgbyȉZ@ToѯQhҪd^YH3Cđe~DB~kZlyN s}TjOAQ <<QsqN`;1g.57F-Vz+Kz^:_.Ѡ5gMWLUB"AMp*~ZB0% YI W|Mh? Ca0 PdM@T_`Ǟ5.НvV1wq{V_ iLw/$I+6^_89hkG)tZxꛫpYo~2桵fg\z<'NDđxhdT|i45mu!5xsW<Ɔ ĔM[CZn}TP;F] 6a4H u5>f2E60כC!irM`,SU{_^{=R x8~)qBoEսy egu^Bpaqv4'>^xYz޾4?OU>>q\Znc qi6c6˩oSZѰ̔*  ї>mЋhWae11sY^8! J%Ƴ^ѣ0*YYfA!ȀTPi!tecO{^mZL] 3JMSqLk{sl3J2-X8;â=^f}ӄiC{܁m.m&:4Z3$LSZYaVxB@`qe2caڛX뵂Ahm];A`kT F#Zq8 p'Bi6Wk~?21eQlnn־k5FdyF֩PX>0H]-U?r!˚}`R3h:W,kwyi W\&KTjt5{7F"]c!Fξп((Owاa)n[&7 ^-/Gp~{i~Ҡ&[k2kԶBRt:$ek&Ӑ栔i_hiYъBZaH$|mڧ0砰`:+*I̥kH- d#tڱj)3qcM:ת dǮKy$h}1+ҭi)r+8f #Ogk5]LPJU"5\kn?L?Xٵkp}c*(;_ֶyS^ "0:F橧bmm ݳR`!5[DQL+2V Mkz IDATS<)+{x?OCoB &yR݄ZD-, Ð.y!e@.%d!(M:\:_5PRF#RVSJQKX6QFrvn'ؾo>(9,dqy( ,2+!(mZ{ir`O`ٽP?CLh!JC3ŏ˺^f|4${ fQđCYXYqgo>LE{CqNMϸH:tږc;t{P0L\Z̃&WXZ7!Тpk,Nn5["MF~&2v a9 @ʐ\)1akE ]=4m%gY[[+}ǃElU!Aа&M{a7@j:|EЩ .^$na I"tѦ.ȉn G;,79[9W^wo);}NE rF_n8^tꊠ\ڟt @."J[Yk[-4ir}PU>V=}t@ﳞW;` i&.K)baG?D)" ~ =*'DE]0Q 2f.s‚ q[_MC%;u513ϲ^%GP]_YeicwsoMywBO;m:eG1M] 鐧)i:ve\:욙ld!X(}:NbIz?#|o;`n~p,i˟L(Yٳϱ|czӌDArMdZ+[5UUo>fI.A5s)muT6O`L"Z0c/pe&IF$iJ܊)SO&Lk#7nB%dAZKՄN):!Ņ _c/-=⩇)/6D0}E"Q&ܺID+0z/oU5CfU)4@V\]ȥx٧84۱uJ .<ߜ!V x :D) @ Aga_~C|nO}_L &AC2pNC,1KsT^1{-NwicȊxUQ:b2r%nYs _b4o8Kk;3]XLsֲs2m8ydD&`*JNո獗~Q"639iB9dL!c̯oҚiH웊4M#dM@n!bB{UiV҄\gfhMV]_:T34s*++H!$ 39PgժMc g(ݝW}yCPDƆOKy r9rr(})kkkhTYkkۼc+\K$Yv0 5vlǤe KNb{{r4=Ȟ͎|c?߼\<,.n8s ?S Bq\X|Cvww+<1Z# MͭuoJ-_]Vq Ѿg|iay: PrD;^c|:$llnmkF l_va8Ӄ;ԜKɾQT„QJ*)F!q~/d:T)'}1Gx Yqpv󲇰"UE80EJ[?g%d.: ipMr v/]Ξ{Ҥ&E} K'ah,u[~O}Sמk3p u lHT|4 ; A+ Sq,M AkbB٤2JXj8Hq߷ʹt|x~qsԩryfYFE =HxuwoQh]P$L.61C*@+B"@s(ARP% t@Q#dy(v1P<ә#IFiV1bia[XY݊Z!" QZ{5D] 0"G_B$IA[mJƩ (G#Q 6%m5ɏBр/}S<}/wvo{{Qh U̥И?[{5^(󌝘Fxg+k'O`~)BxxNASҧ I˲F;+pSp,luW*-`2_A۵YQDNSB)00L3B-&HoM(?[oN*˜xJ|_kVؒ{^\xeO9}yxmTM.s^0FVJBGtBg *1`/sr]zs$+0 IA 1&A{u6N9}q;D\Zffd8"# & s b#, 6 Tdڐ)3&}~Iʻz܍qpv%3C͕)O1yk_"zO![P[ZOl\-?P_J)RRB" FZe?հ?ޮdslނ `01b\Kƅ˝{q!{yǻEA=9R l"hץxv__S3}(kv-Uy맊>8 V/sә g x'\++$IO&r}Zs{8r0apIӔ"T: ,B$mfffP*G dw/) |ߺo(Hbonkcy[IbܫZi6߫a7vf:1Bl"is˲l'4v$<>#7@c ,-,u&UpۨMspD׹?Sޏ4ٟm?NP &ؚNF WQ(-m ØV+*0Flnm"9F 1`P¡9>>ev͸GǼGI)y1:"ׄ1(c%QPΤY3':ku&0 ȇF[[;Eel oaaP]Yho1&,}<ѧ?̉Sh1'`=7ƬWm{B ckˡC&7&^W_Wrח鑧O,HӴь& 5w9t0x+iXHfgfR#LW'hߓ0H/˳WSiJ3%׬_^^avvy۔CR;hzo9)0 ySwZO>~Q*u{96;9yhZ杦-g-3& h/ڋӅ=uc*a׿dI_k{; G#vvvX\\,"L0bL)ZP xAh AԊ CʵfR s, 9z52dnci#,c}m\DÁl u/Q2P.#HJ-$N jQMdIIfо-9rGtrB ڝ.6J^TTHXÍGL\SXȐvcTNjqQyy(K{4 KRpS3?Kk6hxJZ&Վ d1N`gq h1AX |ID] sDݜynԤ`Kˎ/?,̘\h,m 7]'Y;f? [uS(Ҵ(Q$1Zme;T>.M"BBFilkĤafZFcvh8cjL p,eQJ Fcx]ؿM P&GmK"ToCY/- Hh>W#,/)!5q(kA]Bnp0M8w$km={Qc4&G)WWyGlmss3䁄X VQ+$ LM'^DN>d0):Qi֌FFb䐅( ֚B)iJ`®i4!9JD( #ZD o &ILGe]]Z1ZWMWPm}Aji`Mtk@FAwZ~2dc{cfBHVW̥'a/;^z;N)"4< wN=Kn![=B.ۼp9n D%3u(Pِ&; .U7su"#1]zo) j|1̲=efft;9ä1wƍ)*xQZkhց(- д-:vӡ,Hk / zr4c}}Y+ BH"e]?t~՛ LbTFHw"nh Y~u# 2r0ơ(0(2V`|HL:FI,٧o?[er$*׈Lap$!XbMo 4kmsp:,r жBXbm^ 0g\^[Gb:6yIsC* mrt.P TN $ZY'JW6;0G݄SX޳¦UՁ$W`tNHST@(hhղd>>~un_U~ a'fvYk ,؉ P,1CK LtP)SG(M;jc1wde@ eZmN"@d*F Wa(tB!홦TR|(1\bE^?I,rxۏ쵯f0* @ cIۮCqZ+Ǧ^f]c(ISP(`V;& CIh0 Ez66&Loq1":2)c2FZV ȉ(HJ5!C!@9,qɴ /-bwi[K)r 33gHu3-@$%_j0&HD"$l#FJ⸅ L1 FkDyA#%9&1ՍuW_GccF^TB[atQh#H&iF HS䌷,gR&AAK^:ֶ2kuXJ}abꏰ) BNK.JřYAV+J> D~+GB:UG0y=rI"I:1;I­Z8 Kk[%zDdgg?"o}ۏHio>>RJ)ip80a$IaUIӔ+Wc< CVpqJC3?+\yk)MSh\VlMqLj$AB!A( B" q% wNc,5 :`4;;[4 v렾ϛ13oa=رcJ25 MvYa !Um|m"~m[nrCqdadH 9O<(VĝC<3IBjqw<2}ޔ&y%TE%;[_! _m BO?8ihEӧ*{םrƌV1`@$ 3-6)J@ ?("5Fa` n(/6Blh2dIw-vqX( Shl .7GHC r+[8yd0O>s/_l$E I2i!Tʅ!$K?zBKI20V* %&J)vPmכ%Ӑ+E[;;|oq9:^rl0xrZJ1'־afv5O"b+B@)?s(ct:D<:]TəiD 7=f{On( !q|] ÐW6 WFy. D's&M8|筜9s̔R$TKdFaКR8p,x /d9~4BV% _WZ_׽םD!Z@笭_<_ϲvzQƠ%:tKW.077Zi76iӴs^_p$PԽ~1T։7cXc$s666X[[o`{kȲ0 i3 amDԲ.-#oFo@{K_կxz]h#8|=A <3|pwTOt҄2LC3_Sk_ZZ*?}[+駟  方馛_-a#rZ)()!E8'pX8!![;wgPJ5>wN"RV9n4u=v78ʠϽh4&bcΤaKY/C Ru17aE;  s]Z&ˈFr֗72_YpXnVoӛ++Z˜L[]E!f"L-~M(F!hawcO#&@ !`k1HsU WxE ?>ȿWD,hҚv'( ˆ٢R1&7Μ\Y0{+gKxwLi)sdtُorMS}>wJσF$C{^!j$Ɉ_2q,Gk^zϷP>o>RT.[[|q=?!8tk;<ם㜂>>(=0YC?Zn}KY^F$$F\Kfg{~_ OŒY#4F 򷅥V0&Ǧ XZGE '{S`]s-]؟ $#0$Z̠2F"e(:ThDCa…iK=T z[\L2ͥ˗W?d^/}<[tfvlP- ve@Da0D䊹8$3LA!Q [ R76ʕe~PY G(.~ "e;4n|%"ȍ weFkCЉcКΎe`" ,>)*KCju!逥dKm8yY An2NغpŻt;]|S\FwUy˜ !](T("E@73/} ]X]$ӄ%0dXx"% [,/p={Nh#YF%poq^7`jICwR6 \"٢Tl”k5o@p?~?I4f;HXX9VZ$*KdEҔ%.Βq_XF& $.:ӄaDE9ȒȏsqWX:v旗XZ9 NR6+)[Dm`8)M#l}!@xoo5Ǐ h9 s+j88`cиTE*$J_%pdBP^wGe !W_ WY!cD /t3ןnӛȰ/ !A1-4m662 TD 6"O`ynHqWJ[@dyΕ+,ӎQy:禿v(M[OۛY\}̻^]UOwtOφ`b eʲh-ZalYaGp_d)MR $H H0l3ttLWwy}^5t(~u߽ys9y?yCl&W\ 6pPB~7PP~-澫FK7שrF,#<[owӋNV],54zpj,lΧJ:v UXyn! ;_;d2+xX]Yajr!Bʕ 3$*GxkOФQir+B);R6R XZŤQȲ iLL152yo?M+۞9Ty,} H(<PBV ̝(m" G I4*5m48{*ҥVF<6[& IG*M&T*GBwF2jF}z.޺b-Nӡ 8șڢFӔnRhQD;1*8IYܥԘ0r_ըVlllB؍ z [[[Cc^C"6Wxe.=0'q!O^ ,I}Lî'xiJ4 mhRkwZGbHTbaa!.sE1w_k;Rgu&BF$U8QМ`i2id t,9JbؕdQFVteGq)JnqΟ4~Օ[lqS9 \?V.y<%,Z}&qzonB)Љ%90c,^kT 2R-av˲=+Ge\^{/=_JAo: ܽ h&Q)͠;ߡ_Z{&Z"Ȉ>4Aj.rdfDj9DTCA\3)4w_@w;r "LVbB0lR4B:ukfffXZZb}}}8ΎIEt] KӓS<p xa BZFţY(E(߰0ViZmZ)Mi!HI& )%)3eMf?ܚp@pS*JP'zY$/yHOwYMTV`;Kzށ"qQ!V-'Y.`i (U=CHIb#u-QDRn,d6ԩ`mmm/fZk[-"iLw>]BX:^@xR3kk}jy&_ q3?7A Iml@ܺ6Ǐ#eivok<V("N7 Tk Mf!% \6(ebfʸ1Ƽ;ASeXDZ):ǝ۷ɓṰ{ETe\9v»zf}-?27/3xCc^fh!I nKMuγKRVtAN5t\,i hl}I QR|Cb"=G 6)M+H!xK4gqfhQ([R e$iDݳsigGKX%-/QV(H,=۟[VcFX'; "QjeRehso NgJt=goxԪ."w/>&VԾ$&?ĵoD&AkciO͏\b AD{oYX&N䢉MZ۝njt:CjK Y1zhqT [׸U˧ORiK!BC҈ ';SgX[[j t&F['Tѵ5'lMUʠgg|m6Io"!\^J50?v 1vq2aQwv (=٩ixq#T& H&#N's/8qnl$4hU)>EeǷ3R<)Sw;$.у|l]59_z5`Q}3,/-q&;;,_@X8yLHV; 5H{}x^}9n_>{Te.>1.]zfuY 1[DIJ%F "8^Y3tQ*an~fV)>͟O^tDq,ka8?RLZa (<2mlxF36dPoiz8-&v*vW:)n'$jCh }J%R@fDZXN_(mo`(S;bABq,yD Í)/'a@Fi(G gP!?" @h]Z)m_+vw'>ɯ3RQ->N(XYYaoogReݷ\ 4Mylܺ;>o\~''3yheA5 S<zݧ\dS'MRa Rm}ᝄt|?q m%*Vȍ2$%?(>A\ V"?ƭeh!g> ;kH)ll9L j(2wݬ׼=\J@>j=[Ez&ȫ'O1{{{=TDgK^9d\9v'&3Ƶm;Dn񽧿[,.β|AIU̡ml(2b6wv׿[oRt=_r9{FڤjOUB`0'QA:;lf$HIGKǘH9s)ns2J =\Smd 3/A rϓCD8ngVP1WڳǺ26B g|ikQ 3us"uh'@H$)1Oj=xU̧9HbԚO.}r4U6=MSAs&ʸ}CKz3=9e"qػ]#E";}} i$$6&HO(E%P= $hV œfTkvll85crkVx~p:w@/ %333xGӱ Oaȃ>?s(;[c#=֚ V Csjv\:0946VQs( b?#fH:.0q&A?}w Nm9BhK#&:--iin@bpy(RώhjBH ͊;5Im iʲA5ɿQllR顓޾ok/ =~d};}#,?  j af%V.ʀ"_tIˊveWbP$4.377t{,^o 𷙛#8fmQ!4B}$Z _4M 8%XUqҤ'IB|gfk}(#hJs - <`ml=1P}Z,.(cyK[cskj44}y>S,DbJk2G訐WJSe^ "b,,TJc_"i'x;>LD22JRz>R@hNKЉ"Q1 $ xy=x"~MTHzXJĉ\x [Pf J#1P R2Rc 'ŁqnZϣfTl~V~ I"c9RɯJw'1rZ4X)KY@a׃X+}{O#78ztt1V}5A+8eGb<ُV!;1cJ,nPLIg ̡{ݫ SYՠn}$qYd }36Z$ar)Vnm /?hX7&czݮe@09܌ă?0drb 4&'x[JTxVF'IK^B*ȩxګ^yTRќ`zzxK_dY'x57~C k5'1emE}-cdw{ﭭ-vwwQd GO2y;>B*D 2x4GQ'-R m` !iޥnzGyڒ4RuG$+/~?ṛe`2!Ljj)Z$ ILccsI5Éհ8Rp߳})$ŴZmm}3A*S)CTU.^ĹZ Mb{X1_K;+\r@*$>\q 2K-De,~HRhМlP.34XK'OQn6*b<ڝ 6.dF Rr>!JZm=e(z2og7j5xٹY<H!P\J)RC 0pV'o{{{,&ݣB(gcf!=GY$Ml,uט>pq`ACȼ/^p'!bքiO\c7SH\a7W 5I$LhVV5}2;ⱓJe Ν{Νeq~mgyOH'zum~w~;TkUf2gGG;xuկPՙFJKT+ $ uX1':E17_˿oxg:1>u]mOk1hm _{W zfSLnB>GyG vNsN:Z+S8,󓚇tݡ+*JVh]Uq "3{yVT*Ų[g9.)h$?jJm c7:wցYX')$ F!Jr-vvvh6'^HE4e3\,SB}? ~qK̕qyDq̛_o}F¹spi揲071{3gqk[<":(ܺAŗ ۻ>})aZ@Zpdjysւ5!s(bfjf'=*~81;[kοsinxa뵩!t{6[+k,,-13;k+ FȷYj[:m.nǺ^<gƞP-l s>  1nFƐp0ĉ)e[SO}W_}Zf1'Wud SI@uC.] Q)ϳya`(eE!235jA \ 2ѵvF7K3B&4xoѾZt)G_G_m.L@h3=DW*TiY2hoRIvg5yE!-҉M}Ā(_8z@w0 Sʩ -[ }3BA &gj>.o^FY>Tgސ[g/Swa0 _HD XEĽ>7~o՟s5>ٿO~zc;; K !ԦZ(o4qjCf $=<_}]<~Cf.Tb* Z-RҔrAW!QR01i:ܡY357J c ҳ0yAH 籈k , HK8F&}XD#4>BZKC<~ϭ5\Yt- kJ>in>r%+YP4iW1N!HBkc:{+I鍴U1kP Z7o\GzGC(|"jШN23NLJy ?#n^w;+crzT)I:@=c+!WnoT:GfPҳ}OQ K;m@+?h LiE .WHC${KgN3Yk@.6'ϕ6p\~Y~~AH`Yy#s^j*R(*"˂H,7J"[4'&h6yR@7"A6Qmy?e"h{D}e~F)0G +vJ1Kt+o}̩ZiAC@BAX j.6 LBڍuڠr_X2t&7j)Y{(C{>3S>xH?O"뾰Ir-ˢ\1j4x g;E%Tj pVLgH)?yoo(LPLI.:N w;GI90笕ZIT5^ym857Ug\, JV6Nñ%VJIY B)+W P.wuZxGhz<|bi9BdYh@G$6wVz'x$EY\Á1!+~h!k3!~JCx*[kwG?3"(Z r!_DcFV.y^VX]{/l첶1ȃ +0YR];i-+c~$>$gN BP5912Rk2=3c#W~ "@zR*~|=e&f0yMGhrMZfc}#aKvh$1/a_KTzg$ 0T:Հ)54Z]_އ, &+m쏿ƝF)bcu7_X\'|ژ$2:!YN8Va@%ߥgNcqq kfkYP~ϟyWԺ2 M{vno;_I%Pc LOOh4HTja:bfaSR^o(jwLF RkSH\Bztۥ 2a_w#_[39xCxyPSů!;X\Ip+a M* JSZTlyf-ד ;Lpg(lq>JM,J㾠&զ lН.Gx9~NӜ>bfn,VRS /і-$N{DK3rD񽍅Ea?]>Nz5}jǭ9qc^‹h&_<ڵcbzjyC\NdQށ>@^G7h[Ԫs"I% Hm4gfX aNŠrpVmq̴5ᣄ~ 4sSӜ^N97GdMQpe߷lA1=wCDa|㐄5<IZzlm/qCSQ1I Rte^s?9>X؉SY[qΝ~p,.T)!r 2iyߤmTU>I} ,,a٤Rxs ͛ygbj !ﱿC 8~e{U*:iN*駿WI!5xF;w؉3| +nUI|ho >N IuT˄M 8&Jz+Ok/r/A,d+t vQeV,>yE»L&K&Q)NynsiLÑpu33r]#1ϢE! (8NhNÏ1sd4iJI#N-OY3u-ь6yA)RQ\޼?LanfILAq7diI)7 õFx^3.Kz4#pR~X{URTVoI17PQA?Z}zV8֪OzGTnQhL͠dva+5zvG>wC45j3s?u3g0t$%:>ݖc45 buuj{\3+|9gE15sz9;%ۻ;ܸ|KqB)HWx}B'vG\2T<5]ā!fK'$(٘D{N;^̂ :q7%at\FdAƺAJkZ=N>$%_\B C.<R+o?:&Ҿ)H Qx^'LLNfs8h8 φ('~5 b}m z4NR)UHdP L6f8xJN1{/0?<﹇I>mV_9n$NHm?gss h&g&D~7xasc>>j No5+с5-f{EqD7n1a2OQe]lz-꯾־sDC7x'Atir0yO\lW\Snjmشq/nG_] ?w>EP7@*V `OQ}EsǘJxgg )́>_i;fe&$yNd'ONNtrS\x?JTmBYqڎ~k7xd0{tw[4&ƝےY E+כ|E0du}M<%pk+'&pHJqʛ2n3eoZ_ EA *C-E(=f4y&ZZmCSv>KGY#DZ"/ ZT抇}S֠=m3HC,&HH/L7wY\8ʽ7r{GO=Ecr|LMϢ-GΝyc ^m1yz0;3MRAC:sWXٜmmoet2gXyg(G^nv 24R$ X©YS%$7yNѨ5ISvbPn/rI4 AXG@,xXkKQC lXf>UE1HATai8 ^kq*ҔFsFrZyex@}3UTe0eL/9n Wko]>nR`6%h#4]Fda -2SkEO?ϢJ|73*$R"җ PI:-uanVYƼZC2͕&w0=5US ڿ IDATqx ͡Zm-MP%6R6X[ YYgcu? ~{\a*x)6˾r5?JpH<ϸ2tk%MSvvv=gDڵs H VQ5y6r౿M'BgS.K\%O;A*y{%Q valdH=N$_ dU <^h0`c}+h4PRŦ&֮pPYӘvMT`v{huѦJ;=JG2ek(Y%Wb~%fOpEn? )zEzݏQ:5ʋYj^ ssWm%sZRr>VWWj)v˾dnm=h-B^mEp֎dNN܏ū(4EʔDKoӘT)nFR1IdWi <Qa'Mr͸+F躅Uz%BHT)==^;MDx!v K!J&'=.q <#;D(4GxR T󘙘'&T {lo_mɴ2{)Idxe0H$^? R ?p֐Ğؓ&{;֘l@/W^vW|TYH^!P)g0cr7~3E\s4IX÷ K rH6?=?=ҐI~O[-t[,..ޕ %Yɕf]^rN!Z:TkxA@[ֲ2{dQv(cska\(? YYRk߁`X&})Fb@S-l);W8! c[,22if5+u7E2-76BRMxivqjI[&M @(C4RmףCTZæbK1.MS&333CQ1s|=y:ӳ-OAJJ3kTBfk`.ſ'I`0*]|8.n`9.p`lR&5,Nzb4//#|#N@ ;u ˈZ[*4?cV˼ҋgףֵkZ-N>OJJj7&K0==EѤ9y<ïK{e*"5z? )W2zrʿS9К$sEX)RL;k=w?=HR## $1>I&j5*gu6h@) ѴYVM,S4@ietw3&2j`WE.N)`"?Čq0ƽGcre=nDyhz) $IL/Z-@IHㄛWAw:Q9Dh+ϿUWX2$X1 kԝPVV٠ )&7bZ15I_G{L)WSxSΊ}FycK|e6ۏoCboo/_?˝[+|_̽g-! ;͐C : q^ЙQ-~Eg80ҡ= Ա'b׭{<099nFss@( `[$<$! Z< whh=|BLȨ+ƽs1s@MD&p4#͠zIũ!Ic)S(erX4Mܴ.AEV )qX_4KaH^f h<ۦ|ctn⽊cEd3Fy?Fy%gAnE֭[Ri|Cq|0LiJ 9v&hhKUj3}A0)%<@n)%|?7g6(OL(EtoNZ<2c +3KiHA\; >8th=ha`R? |L$Ux2̔Q1 åvl` OSJ(ĄU"-;;l):K#!_VM 5F=c2%I%\Q .ʏFaș4׿/=rfÏ 0;=h0=W8931,Zo]2 4k>^X MbI{SH[{B}TDFlunGp3ü$AEd$]ᚔR|cjrᅗ~'?Q./k'ĸlCSFppg@աqlhctFYƭ"B,Jx!ϳ%*5\d.]xAHrDԡǝ1ŹW_CQ<&VE1^tpʕk?虷Pw/⳵RPmT%[l/o\ԉ2ғH!ZR)D@TEJ\yyN?EZ9DݰQc+.v3|Px2Gs FD3#qD GpyƃD< 4!IbUYey[kOgc[F*8"iJ䉒mv-؆mpI  AC  $mvwۭ۲l˚DQ,jPj|ϴVZ{}$sg5|yQԞ%BԿǐM  ]0R"/@8堽)]6QDEW%F2&3{R) aʦ$]Ns @t:ATͯ}-NY^^ihGq}qy /}Lv3rU1Oxowo`Tni\mhQTŔ=)$Ġ86Vuhtd:aoo, B>Gۇx ~Cܼ x;Zpton`0Aca՚nh4#yC}?EQ0>2NY!QX|R'XMxWD.{^>uO!nϮAdu1)5 dd$M+\x4-A]p@{#$ygx Kp6RUlN iobm4]h> 't,sUQG9UlGpuNE, lll Ij6_}bc4IFVqr˿>b?OIa QKC{2?rtq4KkK8 hǨьA'kx0`*E&td1dww`6@79:ȇ.6KB7 k+]5V> Qtbɓ<~ٟX8֚)*a,,,2Nyʛ, ' TZ׿5rUr1FH y믿Au`Nyq@ }Wc> ktR~ᾩ IFөCgn2=ĭ5MDIӸ蓌bOF(U!0uїct1ΜeVCfe^?Ӝ~\((f)d-*cyJo|o;o$v4mtS;B^BB&96GW9 潺sB2Bܼ_+{Щ6 eq4*,;|Z"svvvLs#Aы!>ʩS\%%ZHƠdiw@Rf"ͺ{c^}U8Rhija *NG^jnB$'Љ쾕UZ3KJ}??駟❏?F])^;ɏ T<@?ReQ OeiK$i_-. zeIuh?8{\˔U"2rLMz74*EU ls}<{:k^G 7'TݛeYk*, wX]+``clکS鯭tm?MS^ye6k?H$qToP{v6:)8wgYUFGc|Օ;FZ41-m@;ѸYL|_;'V~ap8JT7N!$o\y{,,.]! \~lqfc&kc'41K6h7񮬬mڽ!- :zVZi^hf?w^3Ͼyho)>zW IKX1ۥWTif-37ZgqqU.]\+*2HK 00)9eQko:O;`0kQxT`>SW^2a5@็ojB*c8kx:AE)qlӧ ܼy#Ăb2?hOQwr9(' 4ØFQ ;:-T`!,}U\U?#zk*nݺ^$I`gg(MUͥVXA{wwG.`WŠFڝUj oG1<<3`Ϯ1(!) Lf+nnq<ϙsfoϜӭw_7Ƿ{̠fao0`qi$M.\zg勏ٽ&׌I:loV+emJcw$ PBiW s~P+[Fm~o4i4fގ-"jgFA_ e|}z:ux̽{Eٳ`sy׻X[s0}Xl7^|nq+td‡I Nݻw$I\G_95Kg-Mt:%~ k {TspsR$d KrU?'e*ǤiJsgZ~%v'}3ՇBJR(8܅-ij _v8 )E1̈AnRIK)2 * 7*:%T4aZ((K|( CceQo0 x=ߛENGE;wutWP`{5)LgR. Gn۵2k8aBO"iy-&x~k|MV1څ;  cI*UUUmq$7Oj;vooo3X__':`2yt:FcҶ+\xM"b!NSddRyq󭗁mj HҮHt;3L6,..8:o ZRVBD'g-f;kBPAs$m d y* RjAzid?[>Wݴ z&tͧ+FW\ɗ>>hû9z W^#"A۳byqY[\D{-k mېv}\?ƥKPJq5ZM,Cgbop=Xx5wy;O<ͣfGlNtKSvN5F [-#+Q,$nY|5I)IlRPEuՑ@Džk6R῅`o!N{N L >ch˄@1+ HcFD3tNwquS *yhr#17oӧR7zhQ1ޖ8잰ۈ%\ݴnm2cZ )lUYbJ[% Fa1ΎMKxA.e$ a:>:k  x9s_UW4 ~~K.q֭V_7NK)EL9+KMszcΑ_&.#le6qĻ񗈳$ '۷!X&AJg.,j~p`II,,,@$<\U22Q&<;YCFEK("Jbt>c&${2q077@a!vˋ` {=dž4QTLgrT4@Pz*xhpϫ61iUfyvttƊ1+,HNKJa.!D=7G1d\NY&M@6d)L3x!kaBe/_s}"+ ~GIm8ɳXyNQVt>wޱp7Jpi(R-|V7TUo/_1g\%0 [e!%qDd*B$ ,ɜG єT,J5ި*eF8Mj\qƦb)INyQ8\Q[Vj{tQ@[p8L^oW7_~ T̎CFqLCkC*tY #7^([>xyt>yTEBhq2U|| c4(ݷؽsg6&nPK{mu^ҼS͗Ky]qf"y޽= ^glGQO{pmN$$iԟ1b.\;w19N.gmUiݸEwp# f2I:`nؼ}'>s8؊ j<DP(,qF^πdBI)˂SNrgyg@ Bz$t"PU> { Db^Ƕ1~K}yNP2§dD١>J)&I}N} $ܹ3ܹs7+ ֝m>'ygDlmo۔,## y9:NyPfE$IbE׷#R_$ܻ1$Je4/},&ń4T=sdbK}SAeΞ=˥KkR)‚&crPTumw0U&D2bIQYJK*q!r%32j3Cu )J<=EjY>G(?J:ĦuC1DQ@h|t8C煔$3e3nݺW^7xʫ~z6T+;NC/?[nB _֫#G)VTsxǻ%0HDcl-xBMJ ;<0;oiuΘ&|6^"Z+ijYڨɌ'VmB^eE0ܻb6s 7e]"r5{ӢAhE1q0Eɠ7@ޣp;ϧUU"?2D3ܸyL%&.ਰN/?o>sqCQ14F'#s^6p'%[{[|>-&#KdYzwF%ý`YMYZʂ(qdUiolX_*NScdB]5=VIy[);QlQ#"IFhI"0*Et޺Cx;X#|6իYJ'I dD[_./ V,.TEO5ٴUƑ+h#.Ox\ھ]b=#>g'?IY]]#RH z'IV+e=NmB2Y¥KAcã Uk'8*+rDqʧ>*\C4*(ʒ[o׮pv}.oQeI1V Si~#~X5TC2Lͨ#?,K$˪d{gZF:?=y6Y0hQŲBIon ;URL4+!> 1 ]PMwo(ηAFuCV44>29YT׈zM-GF6bI!"@U1cpP 4%4Z RJ1X?̺bu qFa}FZ P0)+qJHOgޥ_KPEt:zx$ip9'3^{U޼vis6ne}dAv`W}FgSI<֚h MJW>oM@ K>s?CfeA>ϕ7 3Qt0\^aKVWچO%e9ȧLIQ**֙uVx!KR1ٟU$!/*kp c5sv"P.aӧBʄLٷ!r6s*~iP +xܡ{<LFJ!ʼn(m:}:4Mh!q$1>hHᢼG S%CP-mYA Nle]$CWu0"+vgcxU064[ڥl莐!%4e2SE lzy[_|WZ3/OS`$B\_[{֓1ȧGw(ws0Ek$͸w6>A~@W{|uZ=ϲ~h4b)o1-иQ2%qcsU$G8Ʉ?s{;Q9lSRhm(gc{D"i;*:3R )dEl6Ro\CUʁ];nٱux0q_\#pg,-uz($S&̅l>E5_2ҚJT)җؾO?v'ܼzdBeяXrc-Le$LG!}B_B!kk'u!94L&;iqYY`<} Jt:8q-wfl,P6>NI؆r!6snjq@ /gZd8(yamBk|W0.s2mcRZCZUt{ m+A1-)gSgh`غy!e1iޠ|ڏ`ww5 =ǪPUsPVUH]Uٽ!0J LZ餦chgxղYQl=EMI IDATF4 o`|勶$E0?CmPu]j2!6No1az Ci4dy͐?ǩ\8wS~:[[[dY( LK C:N _668.7{kщ6nj5ڂW$H:zfA[o]E]6/EĔj6c/6%*QHh ` Me$1/=N, ̦3vՎhE"c?ihI0N@/h^oIOJ%f FDH 6Z_fcO=>|3.d}=NΠk&;]?g2-Dh`n~gɧE->>vG8Fm,t6c20(ʲ˲.sᨱijC" F//}" +#[pHө)CԂsچJnu쉨J9Kd6A36: 96ɇ^k%dԟAzgM>O}??;<;\ (PdɄ[e1XQ0s@Z@;F]7b3BXVj5Q3dzٌ;w6Rv "iJzA)~".$Y_}?o@E>}xqs'Y:Bwul'vlYlۂVVWId/.l`_>g! ߸IHAD(gQʞwSOD]G9s8Bc;&H 3Z*F)hw6DD? b^RDۗZA DUMbVsDIm7r0쏖Yc]7{ :/)%*-eCYs RKǸS@e6IMKK VbWXALg `юX>6/5XU(m5PcTU 0!/>5iwIW !!rf\ӏ"h'q? |}?LU=s=DeAHa??%J, k2fey8vzG J3o2O,̻Fq`0d{{ hi FBTL͜ K!+pu$\xׯw캅f,ZxyVWWyWj\W/, *5&I+\if~N50w_D)7_'b~&2_t{]lzykyOG-@rn:GE1̤UUVvrx2I 54ƅNfy KHL1~V-Dr_$]|XޱF67-z:)ol66@=$t:u8KxׯtҘuVܼCO[ch Gs%)B\o?Kۛ;(J2{ܹs4K6%g-58y5B r'dDPQiM2{)ՒCM sn^ {A6 $?2 J) 68P9aҶWx4-VfY1MEhA.N+Oqƒ ]ۣϵrk xZ^Za}},뻼iG%80BL9iK`~nw gQ-ɍPHlšn 2>(x=c{fx#$gM@z\QVy(Wg'9w錃3湅,,,eYlaS";'+m"/3΀:FiJj~DQ-`\#*~´Tӯ8i0~7vmwW @ AבdA ķ9ɺPhV;)S'm {\w#Z0*Nb;Ǎ!Too NO.fK!1c'q$ѪD>*[x$Mi_hΞF~՟tx4zxV̈$邐ln>J4'MX5lzץ? mo"HVc%;;t=cc֮}ԍ)C~9e};z$%/r4a21˧DIR#쥄4͔ƞ1lmm!es\Fp5٦RuyF66/ {GH,kUuPhh9xy1TNzC9zGhmKY\U*\;| _`:wz Ҙ,I#AN'!I$8`S>>;OEힱB{g/?Qm9pΛw|t:6`ylV,VKe _4>HipT1J#^ RzHu:iLf$IDI05efe&iZ{mE.: ,TrBⰰ^-XY5DMؑ iP\80MSYésgI(?R8aj2\~S./QTY(\yk%B8 J ĉ,e{{ڼg쒘UQ!:1k"^RE,TIUܺy+N1vxdUEn7ŨLFۼ"pi *(&I2\[21!^X\D੩ gel:c{s qeK6 $I3KkZػE2FcI4Hf`2c{i^0gL\5`g,Ȟu6XۛY'yKF{atϷI>b%9spC@bD3k+G~#v*=I"KrIB0,3YrN>[XO… L`Vdͭ-ʕfuŞc $2"FBIS۝V)k"LQ2g+rFkZN1,f)b.`Y$/pyfz2QDnt$* ~giab$YG[9e 7 @ 4ƲE孹oUUU]]dolP&).)J#4aF600l3/3 [241RhRf7f{YK-%"!"eVSr|˽8pȞ\5uG-Nu|!IXeh4΢UFVXNj)<wX bPr pFh⥴o ر"JbJ͟Ϡ J_# EnFMa:#@ZiȥzFU1v{JWϋ.333cS[okdx lL~4 {5A»-% IXAiC"|_4bٶb\@0YFߧJ Ƕ)+*[!4Rm{wvxWټAǬY'>Remaac\Eq@sH %lyGy:P * {Unf5ƠPFNue'Oi)j0 HSMJ'OaT)us0*]o+옎L<3qZ EPFXbiӄ6 i5nsP@ܹ[;(O %t,TTm1mh/qQQVhgá.Oq9Pʯ5bs%ct’&(a!V;dkw% t!.>|ϗҶgn4}k=DD,oyv CW VkO?u0h R A,V!&EQXp=jkZq[l:OQJM(* ܽ{Vrg? Cc]|>:x%JF0N@Bo0jcfǞxg}DE.*Xsw퉣ȊvMvsA@Ecl˗Ś/2sssHy.&l ȿIn\LRdvˌA4IhZmVNd90rj cJRi9gO4Qh 4QY_BHjJv](E~ kT]) )@D6s3sE q§gL1RJк@$ض-Y\X$T+(Ea" ߻P֎[%Z~/r,cRl@Td01;c&F@]gt2] IDATR r.q& m A1F bNۥ-rwQv)E\/?%7~Z Hfl)w)&ײT)_>+>γ*G0Cnr8fii9rW~us0~  &'wع~{[ ], Gi B7LyN B:╘r.rMş~׶ެ-N!*xǐA CBߟTYG8*'R03_ח٨'s𞭓W*06 OҺߊ(wYjӣV"giNOsp&3*!G]@k}4$ni׀GT+Eh4O.h˫$FHR㧯/~#2yo\ 2O[KжA Mܺqxk'YXXBfBh&yWy\k qʢ_Ke2kȊ)9eɹ}W_9s4ggڥqs QTZ!!Cjqɓy\SeOk^+Z(77Qc?0#UQA5*(ȳvŦN2ը+^yk$lgαr4=R (q"V.િI:#*T\x5|<|#]= %Iq:f{nJ1~ A햵)@֘@e_c#h}w}=EUn߾] ew?UA\-Ffq`87S` )Li JFCt`WVh@~-_問_Iiu(J%xrpBU+rG};ªQ׷_r8]+MS }.4D` >~ dZ20!aNXb0$q ʒs.5yr >C >5FڦF6VW oS [4.N:";h迧 hc sap]rĉ c3*Q*;>xt!FCugAJsƦ^_zs/PUZ:TȾ24PBZ~lΟ}V/]GqG>c$TF[>ރƾ_M[F30 rS2ҎX)a 99SuzT#+RTn;EQa 9^z坂*4ZXĦ)Ij 'NSXJ&wCm{ljjz6I_%++K|G&'39 Wܸ'yN;oKzH1F6b0ۥ 'GZe̠ߣ9Y^/;oRƒ>7wOԀ.&u SX0z!50"$@=>rA.^?2BREfn68Jy]5õV%'c\i6*30bMqhm\pi_b|'m{k}5id aތG9QJ|V+/H)j,//3 R"ȿM^FR1}ҹ(ʑKmX4 TBce$YJ+php$1(P"fT ՚qZh^cXtk׮eC>%7*/[-7 c˙VmO͍[߻ͭיY33ʲ>;P ;* =6cH6˸6kR\Yfk,EPeyAYgal,hcK֨DrKR.( 1w4JF$F>;oFo#С%ce'̒( 7غus[4^k  aȀ(,#Xf mAIB@AB0== z]zl5%G 3?Zk j >u5c|dzHCV|nEjSMtpп`+=MIߋRAnJ֬\ !sۀd2$-nNp43G5OZ z`n$?YN$1q=dneg/ 1$Ɋ$9}f͛lIQ@& q7B0_Eck>?֫?=L"W]>x 8{aaW̴ADo"VL8ػG^%4Tcm:\'s@%Ha[I428q11H8C8uV琝+a%Սk'ڶ3uS aD @U&vOjC m`)c&ȃuy`Y#۰HT+ sg61EBcxGdj\z%8&cĦeb~WaJQLզ95MeooώRwn3H8F԰|HLQÄm^yEffy_֨EɕJϟRpr[[vn؜%nF≯GLUzS4g ťjw{|<3Y];*#QnKь2I㐚#4ʏO̶Վ"0ύ0襴[w~u:)shyHzm k?uU(sZޓJ)8s IX}Fljcia,K٤8kpzӈPЊ)$Ov$hqos_n#%L=lmnd%ޅ%IݻwATkuC "A5 TܹcYl 4cbvv4KEO)N !)5k%5}t?'>Vz*ӳ{O\/Ѭ{؎8%{\Tc*p9rO<8ߣ֜d, ήy77npҥ1X+I66X^^fO vjG%^&NAja~ӏ=~%f |iHRC4Bh3zp!g9776r2_q֏Kk y[geeucl:rCk/R[V+'0՜bmuFAelmm1]ݹhavN08r&\} ~nFJ7TjVOr*Hݝmnݢ뢵 d8L9}4kkktzC5}vƕ╟=V:p8pI+1r_98&iwc2g_ 8ǿU˗vs s<^Pe"qFh_CDe h}\lyM--Q'$cޠq5S a/z>aG(bvv{ ]TABzjyspp0 @Μei"$ Ҷ>kg,™箃sqz}g !/"oFI h^?c)v ~p}K/PBj ss4gYX^eIJ]td{LO7HS0rpئѨ05]0mҖ-4*,,3L~" _`angzj\tZtA*ra{N>T1csڵkx*SesǸq?gY9j Et: 2`uuJݻá|P}Gt͡ ]rXDJ)T#FvY)0KuַؼAZ@R_u~`Q!Ɗ())j(?Hcɩ3:MT+|fk+ݳMNZQo"e 3323Ġso 3SH\`5Bѩֆk\3"R>ruBSǑGdGEcLX^ΩX}'K4s-ˑŚS غ;r4sEUvww t-cyW^gffVU}.-t`'{6{B Bo| /bn` 66ngF ]p.MBX@X!0B&A)nn޽={ +`€k60h#Fr"Z"%5Ns,]+/$!ZQ#Fd!‹.~IX#~u˾ho6/_[#XZ/_)JKњzc4DS"- Z<+|SCRJ^^g D8fiN?tUM9\x1h.`O $88h5q:6W\(),2歛ؼ~ f> Ciշy8wApho#+zIZ:*G`E(Bay4aRwmvsadKPϹ/rgkW6?du[ mS] YX=g~}Ub#=6 }昙)!s2r3Z;_9nHtTnATt0B>z#a0a3i,;wwhM&i{\,MMMt ;dcͨ˥ 7Jq=v~4J2OШYѼ(܌33ml9^/~ ypGh dHd7/p%g#8m~mpճ,. BB 'cmiD!,'ϐ~2`0HYX:EFݲC@p4:K<`SP4y:"z<^,?$91GW$O>2Ytc,E'9і@[Dl-,,D zt)t9Z_}~׮)k0 ]b98R e[oD ^%g@gy33>l ģlƍG~Vܔ 169ca;+Da0ˈ A`L+WlN1]w0t[Bz7OEI0dfqVo%PI  "#?oA,uc+c|>"}51ۣ( ڇ6֊^.gς1DqheM}.7*d0 `]Fa n:{u[$ِ_6nh֝TqDPk ?ilch U ß4z @I7HTRjJj53u"$DaHjB䥗_cq"AJc,s"69Gu(ڨ3 1nt:Sz2v,I+[mi{FݻMen~(J -i&,ZO{^6K=M JPI#h6ƛuM<^lFKJe@6-lZzv{oqe VuD}{? aPU .?glodjzi di»KZ -ظ2 muI "q $"Z̳~G|/PM1jԪMΝ~k?غ[c٧1[UG/?+{ =>"!ZHnlqNZevf@P:/G{W\S%φ$î%;j!lZd NO1djŪ*6&Z8,6U29 RJ*qLV2套Lj7塻rPA3#QF`<ܗcF¸81dy Dܛ1r G:砵ga dvfֵe!Q<{R6`F$Q h6lﲾ~&2 WA &qE`6dG)rV~` zp0-;Z<(EdZ!;ne9.>2_xBǀqkإj`mJ^ 0nM֠l귐Q@׮RxQ’2)xQH BMgr BkZe~a|yԣOSVɒQ05;@8*QDlW⢥%!lrg{LMOunYVt`!Ia!H{ew('rP|j9넄aH^gTF֔KW::$i I7ѽ ){XSA)cXX)B0?7g9Zc!?<|g76d1hWJ֗Q_+-h4@(e޹i@Qm"gK-WC  x҄7^_4 ++8}"@BXG?>]zh~ngC ma8<~Iy N.-! "Ȉ@iD rHV6崰fhZ#ÈJI%Z71Ҍhޭ ?'rga/sb2H9i6ڵ?;77җVW,(/mHF)sdac2a$ xAgCRe1kmp,lf #JH$9|u,oF| GEwMVU?frE^ jeiZӺK̶ZD `XX4XD 煒;[Z 2[,4*$=V͐jʅ PJqZkzu钌"W`h IDAT:=wwC8a/;oys`wgwSO!9.13(d&Z-(kT3 xGy聇Vj QíIȌx|6LR*+8>pFQyN$EQD.eh!d'5[8r u'Jl~_'鸪W1L)@e/~J7ɏ_:>ʔ%Nt,JV@MIJ).JH*26%j!*R*7iC_-^|<>Sc:+'Gn a?*_◩Z%:^ϊu*;ɒzUЪʀ V!9Vzh֊*(Es0$I[}$iuh,[J~HEQHY0 Nwď}֚\SD=Keu;]H>FV%eH_` i`\L Kɍ&3eBW|;ΟxAKFX"80MxSZıK1i0BJB@ P*&渃_"YlP޽{ƛaX(QhYEYi@(E I@uj (:im$s;7nԣ " ;<2.d 1*'[;n u0k!룺z[L3$)GBb߸ sD077O9Ł;~{qZ# ?I2d8Cl*BiUtQqop̃,/Nc*ΐxIc0۴ M3  e"4u74M:ZZFZ#q0lij] 'WWo|0chS G |DqD)*LΩ7MYDZ%ćԤ5Lyzbl y[QX(C!Gb'quk.έ_{z} e ;;[xXs#:rQSM_1ЄmujA `=2nrɑv4pe^~gqąO>z4gv~.r>}\裏[,36MY3jEA DHLL{*-8lY8v}# 0JIDiw17;OX$y]q@m$Jk_ǕnPhRRO.Aqt¸ꩳ=Qu }FoO6ƖifS^j$'Ns܅?[Ac٘;\ #0?5ص`㒔JypgJZD~ `Úcl Id`4yp}^ ݫ7yn1 &D:/0n0oYSO}j|C9Q뤓l6RrppPY+뜍G(/rB kr~ilaH݇;e{u??qNς 5/{ A(1E>*'2`wqv ^ܮjZDFEvl'0 XYYbk. }rR NPMsp #RgٹҊ}9[8w ÷0U ڶ9oZHRRX1''LWT"CJȢU!J̗,n;;7IӞ-ʢ r Ic  ɒ?wySOưdeaȄ--ېrPs8{20:"6j aY&" J!*RV|.d^M*Vrli7ak?-m]X;9dqqZF-yi돳b{O>Oq::.oxLؼy_%6+KqLVeyyylSȨ8L9D13gprM-8] HjypA T+S1HJA}zn&Nr qʧ{/}K|K_DEnrĉ⾋{td(!0FDY4,B1)Jå9 C',===GVnҦ1A$0pSw,"W2 O*P$BWb ߻_zwx8 iҜ=N`lKlROr'hNҜ1$VaBogffd?,/jIS/RCkAg 3* -/D?rͯu?1;䫿4gB.Ve1O_$ Wv&9B hCߗW*T1NkPa

sH8ԦR" W9F3.:?6l[8 PVXR& ${{xQkΓPظ;ws⣜Z^cqvz X=qmSO>F%Z1N@hG_C8wa6,Ah[)AjN?m8"jK4ɩGk|{\.ciTਟ(0H$0iWJa)4E8 k;Sisb{{g9uET[}=cR@b<=tYd .pm-BR+Vm#'J؅336* Dq@ʥqvhͯ}VVV?b}1 l#8=(4/PHIG#Nt TU1mr8fbwnaop4gfHKЬɵ~mw;5h׫B!UX[?Uys/ g }4es@`Yn JJżȎ9+Iʨ< :W1]CAd,ν]vvvG>W4d*煟2V,WCӛ0n8hM KNSLk:(都;޶-e+K :9R}__?woqrNCBbt[6{Bfjܸ?gT?WY6ttGaK767k1ش,MIC.U8%I)`m$a;~(S8?A׹%kEߧJm587 O ä0A*,7$lE1>^CrEsDPV#$ت^Kaff8;|5Z3}C%e=ϞG>|H'7ic*z&7 - 2htb`L!dFSsJ^w/~c:}=\M1mtQE>X^B(1O5CT#V^d5$ ξgE кˍ˯Z$RWcHmL ’+4%HJD fȌ#p@T@b>K~$,8ϴ%НuyQ+Ðb}QH4e4֡+ ad>BQiD 9=T}"hziȜ;B Ύ5 1*v E)̵^'8g빆"p]Af?*Z273M,,8t-(cLфux@^:@iZqGdooc VSA IE1 >gdӖ?%I2d:{iwxɓ'RM9tTۮ#f`CnFs`kd:6i8#;J66HYƲA펮 wy/m w! cL"CʀR'dmZX&o=n}FQ η7I;Y yN B*֡riS[xkZHZT-B![{==, 7T d 뿤9U}SSQ.;e¡Ev2?ј_</^r͹uN̗#pȍ7/H2Gٺz,-䩧'?mv*cA0CqYtmuvnov8}z![U! >77iًXX<ᐙFw^̯ a8PeÕMg`쬌v9:k&C>X?ƙ3gIҌvo@R%PF2/6߯tADǨV\zRQ]3#[AOJ,2WIӜ+k45)J% !+$F i0N?T PV9Ǎ[hmQ3L|?җӋ@@'>PZ^VuV}C>xM~ǩjpnYP} Ynd)+EP(=Ȁ,O- @f14J/5Vr=eGk%hL!k~{$A4?'2Q[iM[-Zh9WÐO?Y,scg.*@4E_:j=8 VҷooFR)rW;Fc5fl0peg]h{IY|) CsK,*М\ϿL:Ɵ|pķ"X8Sν{,ZkORי./Dsȃ6a&n90&It}:-8e0 fF~M6MPJt@g4~g`f5ېi}~ Ԣ/~9Ξ]# ;,,.155s@ Z O=SL-pJU m\e$ÄUBw4j &O?K_`0'/{|oxBJ X_bl=+啗^a~~H3NطNA$;G9;VhVt;mLG}9ڻ͞n2g sPo=GCrv:Ł3ho|}!HAX:.x!qq #]l6j9ЌNջwbqd[j+#իE_m|G,h@܄9=~׻R#63}$6) زQ~~Fy-4c%GÕ9xH<5[ߜeu w~zF7 fqi2lHIUbW~MT\ vٮ8bɔdME8 M;a{htw9{X{jwzLc"#,%g3[+WO&J*OiZ#r0A c? ZJ+<{qL#GC"g%1Y6E#"![zuΞ_&u1G.u#nWBs"WX^]bM9*Y)`\Y>3Gִnr͏[E&8uCʑ;9tGr-wPUrP.-u -u"BcSbUb@6z5ځxҖ[r'Vx˟osxxN<|rSmݿd2F+ s&W^( YWiPCڑnP.iruV?v%zIDceHK|?xp:xg0_oQpsCΞ=VK)7>p8 NR40f$*k&<"jP5#KֽRk˷MKŘ7n.3ܺ]ޤO9g=EQ[oᬔ@+wS6Ri_ſ}泬u{LH":6I#7Z0 CN b@(-X38PuM:*Ʈ>FqcYo,/uQ F(Aq5yi%Uc-;{/JLi:enFX+iRd3:'Z:t4}Qms~KAg9*Y>ƹ&:m`cCC)R?)Z.W.?E˜sZ#:IɛoqƇl0D%֜;wi3ϫy.^0goV`C)5"5\qh#u \Zk&/~Oq9Jh𮊲Dt'{ikJt%Icb, 4ƊɄ;Ix5|0ǃV5jiPAfNGܽ{UX(,DP9W}گx;wX+h1O^HU^|:6ZI./ pե y<t]n:gP`r6Z{K!.RbB5E.8i2ODʈ*SۀdEAבU i8eyzIt*h|g+( 4EVYldZ9dL%I-,\rI_ʧ5Up:|+#F1.2,ҋBA$rZxo*yR_iz)DJJ#DCEZ[^efHG2@AXKbq7qq|YEƔQb1PX#CJ l2kȬ%A5ISI>gꄟE[9:qﯵOp42u$Q,z(41q`"FK_be:qe5!uTij\@Jjkk\8&6 <#y|ϚguE^sIb-^o}J[ekLB8V e#I1w_ȋ")>Fk H%U*24Rhu$ƷgJJqLJENMzy%b.^uю@ɚr8[ b^jkkkUkūn!(HN/NƉm6NNoeiyDWr*Q{nJD^R'F)^z)qg<l $pvJЖeQXaΉo(HXl8L'c~w'#q&_ΧiKJFFFsE>䳜8qubį(WX@pcqI܎گgLy6b.UĠB{^)`-1Jct(cܸAդZ^f}T$P' 1b9!;ݻ*lyI䡬p`@'+ R:XF΃wV7ug]JJ0jeex'Oʟ1ڟ>q"8F/8a~AG!J딦Ejƫ?[d"TwkT^5GBgfwɤ&~5O ArS>gr .\HQη苼έU1˽. Ggdg~s,--l4<˙&\X;K/7n`I)y;.ZkƼц#c>DYD(byex^Y"B# E/8'g1ڡKftR5IS?G^NqJ֪,+A'zB<&Qk4M&!rF"M>!NFQ}^iMk4kk G#+жRVs2ǕrԱOqNN[wX{ Fwӌlno1L"i5EǍաQ>I#qp0k[xߏK 5SO)['|*(LʂowH1iUf= WwYN8pU?F`>$<,KOjsZDP'C vD hYGA,fg8}rvCl)9}d(SD'tH{D[dSyV_mJ%lNb2OG`gc4oc?bz89mQfSF]l,o٤.9A:i4g$ӹ ZlqpDzXm[Pf٘`"R9Zibh4)eQ!V5p>LS(c "E__֘uR=t{I2Nky!LG7yWٻ}FFiqyfpа^wdb')Z]76y_KnR+EN _i+qtʃxڻܾ}[HMP0vR 3;`|Ya:u_q٧mFyn$D A,gm(cYIߴOeV(M9"c6U>sCX.yQ&xs}cfEV#GRnEO|ӼO_bෳMlED~|(?8q𾑎rЖD2W[|+_+fQ[dY˞@?"MR=Q96I$It: OEqjk'OFstW۫"sϯ:P*7#~OLStw!r g8fmm*!\GjJ+l:}㷹}&E1J|mvwi$*0&4  r*jDyLh}D)h>Dw\[kQĖ;4wѣT|?hHtS4]Cvܣ' s#Vv8Ub2DkEJɤd?S$8qag<Ӷj)%mJkq&IS8E/^+OthWUU[Y`cm(Ȳ)} ^%"Q"_ZK{(h}4 PhVEMxpCyG vp,/ <,Kkϥ:!h9w d]hl5R~ih7[?N3 d5M]3gh,v- ?EYzBH7(ay7|;y75vvw8} tq,l)e}p 'OpPz^D fAxo]o}VoX@{"Sh8 zZ֪輎\s!̑!ӬolreƓ ܸ·nu贚bTH}ԫ^XZQ׮I )dj]Rθ(R>ˀ;fh%zb<%Vp[\c:vh1XKApo?q.5k\_HqQd1=Rmyx`9"==e $U8ZgAe91p8@tشZJqG̟|yU3\t &e`2{M8k1e2apDY:V_3tѧzg?AO4[R&#8"l&NI$1Ml1>Egkkln,LÊh4h6`QS1"R^,X syB +ues;\RZV\rvmgܿy M4IKǞ~7zR{$ǤTe+9rgEYTU1kƨ2xU}=Z(Bn4eāYg4k'0:k Q׺ҼJJ&i Up{9/#SNN6tm+X8!1ݷ^ !I-8i$p oУ@`C1)$_*|'v4=iJfSFx\Q#V W46v`r<0^O)>4䒂,gtʽ[`-u@XICع)oϾst%!7#7,`sUrQS(Qh=aX]bii~σF_([uxB$S:ꆩTyoE@RE靗 K!8gh̷oJ[ Tiq¸ Cz-oX%?G.4M9͢H?$qJ9l1FEIU?R}\bPŪυ}Jg?0/g"Mc~?g]~on,i?P%!{I1BMWsD"dSbfCfc V.h Baqɓ'p2-dwd48h:+^]$s/QR3΋R"+F"̟Z AX:+M"8x*d Enp$KRD& ^WŃ ySΰ}F+!nk-gC)+LAiL[by)mP20B+c"DF t$9ߟoZrpp@8c'67PXkJQl IDATkөC8z QqT59F!;͋~PI-tAi &o|ZMzWs 篹|1:rBi-w5R;B0˲ߕW\D:nف ^z@ivZ`'L'T㢩wqu!U zw̬HD01{VO:0\QL(957"VW7P(LiQp)/'նN C/=8q(p&:KhMuFxypP5'c{;C|Fqjc/ iY__Ν;bt;ͦDkev3ZqOEi0T1FsP}^.crn6LDSwHi : Nf4M&Ju8#u=ZdݷWs:&I{>ïߦ, ^}UrR}W`W9b'N`tg2NCI$ah5Rw*~ >*{idlu-[@ f'Z5O,my;=]i7n*yNDQGA[rUg)o]1Q`˂`ӒZG`0b)C$$Oo%z8וVh[S cM;g%Yf8XY^왳 cSLY2, \4 y;O&&Ie|}՜m%s//~Ktbʒz*_SAȡ\;< X_Y/|KaTJ건6p1|?W"0]-s۲~UG1L&R¦dL9Д eY ISAjJeiPf)άq^VQ;1E{|gE4횸"IRNln(YD8_(l^?xY<+tW0E I`YJSϠ3T1Kvpun^Օe1N9C#NIJgB"x7wM2FDH"I`0UQ'hsms?Gׯ^eUjgFqd2D$Ic@Vjn=Y,=C̉$9}|$5 QZ17pgUJUFVx1#ͯ=$%44i2eL$z_E:dVVBDEN}e-း;DaQT K݈rG vh[Ji># j'{U9U ZdWD޳w>~ s_ptp(UJ;5z(,8Hh]]rEmuker &Ģ,Ӣ*W#\дĴg!味gQM[kkǓ3#<I0J 9OBY,fV%i M)L!&tꔐ4A+E{c$R`-֖qYK>wdoO^s߹ōګpu66,u%IEϑ`ƪl2GSN,uܧ?sϾg~{=.=qVJ @*q&s԰z& @1M :riAnN!XXTq>o\?PgNZIGFY67sW:&J²w`M19,EZʁ8m;su|uA)C %c +֠C { Nt&E>mxPZrΥ>>HB:Y3Z> \`G&ISӉ؟=")ݍRʌP*- Rp>4k1[d i; "h bjmh5D-UP-ZPNE_/׼GU֚,!mQLgs>ڦ;pT#^58FRDiS:{(I$o*?? nqlA}`^deuNonKӤLIMc`3gOZI}G!TrLV0Ӱ0C~(D戄0/%9N&ɫ?>o&:Ν=Wm9eIJ 3F-$+<w~^x<3bL!N qJy,FIop4 nj+R- = \ B ^h~yC*ShlH8 ךҊp?}Ļo18xD}p%=޿/|o`jvs|<= ~%#NY^^ާ.ļꇢixԣHEF}MaKJg6;[?y݇ӷh4[ؔ> Qz 3W~Ġڪhڳ ˜RGooiqxx`0{ﭭJM2\V .pxx8X`|W5>0uCf5kvsb,i4X(4 MR={*aC# $;oueVIҰLn"\LfCKV!&I It:xRt·a@⺨M"|v}jEQ0,B// Ir)LYbJSxUfꔨJ:b'rΟHS6m48u\5NB|U)>W~3N&f/ #9{,!OSW &@"à ѡF =,-?rx8d8^.,q1eI !ZL&S66OFǾ"I@7(L݇!RB3La`w%t-j˲]$cyQUg=j퇃:K6/e>ϰE#0w\)EY{P5t59w Og !PphjWo,] ͌8eQ%H!lعdp#zaTb|+#3H{WO8a>wcwo?g˽i[ZbccSOh5{g?Ɖ0n! ʼd2)O<ˉ3,n,l/s8\+VňŔ]iX`%,!7_'rJ)OF\{-6Nbm}s "ؐX+ܾ?dCΟ;Gr⚔F!1|`<KPmyNߟXg(GKjZlooWف,ϗ9t9ϥ=J y(UG&ӌfMFG^r< UxNC4U'bd&@m<*OwT.e)4!5e.-)y6rj:?_A٫X.P8fP}FU)kalh6GŬ8 cG.f!H*$f4ifsҾ?A{ۆ5]sNE~3"yK6rXppDEQmPf<$II"WX[V] UGWuub?RJv|rU%,ڭs188e8EhΏEV3)>N R(N*Y N( ={ .TF(_T9ȌJ׀R(h {c|k'>9Μ{ :5>/\@ŌvUa 1ߒ:ȏlX%"e!OOUpвB |Öt Ǽ$fY ;wLʘq1O鴻O?4IpV.05@z%,/W6U"NΗE9YQ[w8m% ;34dwwGxp&$LmZwqPBbbj}ԧ]&hoM|H5HXiSO=ŅȝaZDJUbyy,x"@E/{(!()rq{q6P%ܽK`cn'VeL4(C`yAl6 u%&@Oeh՜:y cgiʼnoPe0ĉO;U n2ЎC"J,D+W'ULo3?"M*%i:& >͉g 5(sɧ(=xj$Ed \Arƨ՘-:&rN4p%fcX'@!o NfmMQfyx<`;o3I97}yITOk VV:!eZQ\]YY+TiǬol6$UsBQ12.?p2,hz40o^Uw8Ɨ\D1psSݗg8CL>Fd}?9eɃΟ}UVyYMsYPcEVys؊\i-+\y)Ҵd4RHPR AP˿d:!2#?rE%7n[aS[sp_4v$N(cJX4 Ӡ|O9y&\|wgB7B)l['hZRc%/(gv{z]ݻ1$ir& eCX:}$N}6EH+\8z}~9H)ΕޙP*ZKH2J'CQF>A+4i4m Ĺ }qK.O=7o0d`yGELFܾυsw?$갔CP%kyQ9͊^'xApFʬ"ŰBgwPA(@54C$mG/^/<먋ʄ4CMU8D2=G?7Op:]>yzh-Uڔl6wTֆH!hasymN][RCZ%[GHŸzY,%#V~~WނR[3dH@:;ѾlGG}NUK-b ,)Y_ߢpA+ ƣ1E\wJr%{#'O[h![4&(ZIH(jh64DQBY١JJĿȋ7j2%g^o^ IyR1ɂ!&.Z靖Ih4(E#/jSΑ%w>ɿWzrҧȇ"3 q}>O-ϫP! 93]vΑS[RkɁ/,g`4$&:I2X]mܢ;44G~͞'h7an *A?ca~;wnr#IN8Zc`n~Ω 9>/rcE!vb훬Gnqr1 y<ӑilY6[lN9Jx<`1#P3/Kkrxq(X\\,FW#%bPV qP%xw`}+EO< ϭ137KsvzEڨ"(?{."W#MKnVKAcإfgu]Y{GA̪^Tnw}_'܅ ,--OsΞ%MkuM3Io2kHJKV(Өn/\N6Wpt5)XRW!>>OVFLrA)s:bk@,ǽ{˟sxèczn|!VSa.(gp 9h΍:xNx[>6%m JH"H rmyÏ>$QJ튉pZ v{ADP,sA`W.5^@XT(N mNנͺT:B C5H>x,/՚cQK:}|<Ж'p)oS?~j8:DRRd#lHJ9 \*I*΍׿'UF33;S~#`9K):՟-#_Tr3 w^9̕J8Jc)g-d½wr$q<p탏ǜX^&JbTS{ߠAn$%nsmBVC(ă7Ǡh~e?Fkejdi cVNѨEʕ{g]jpgff*p!y{rUj9=_ə3ť4SAzg| ΠYkіDMcd +5gkVGW~_kMMVkv4Fݣyt̍kn`~aOx IDATHsCp%"_X"^t J! z`q՜~*W02;;;F#(7>=4B9NK\z*7o?G'VUvѳvrF>i´`EžʗM0l;.^Xgoy{iyWjbL#@^$^r'B:n-ww?[z))4I#Vo0;W"RCp&cCo'\ M2(Í锏Volp1Hȭ"#enySg)I#wQG*Atc%Naa5baW5J`87aaa+e kn{irpȃ@zP C6768:rhDC&s~ vRgh4rx?z% iIA=K`Qo9!Mq˯_ 8ccgHJFu][ V;,,(apja BHNqxe܄ﱿW&Yu3WeG< G=?Cϗi"T7qXBXkQ>47m,..NRZxwOD٦wp58 2X=j. / n߾CV5"@ v?_X୷?DP`ރpZ {ˆN,TJú yx9 dRo4i϶k1/%,ѿ:^R0Q}3j~Maɋ-q"e:vh] t]c^o:NȋX9 YZYhrkV>fUmDXKQD)̳Q?cmH13ѡzW|sCv<+uwp+8io>֐za$m1i-<&mK(.9p)im K+ӌG}&fE:m;0 j@Tm+ JkNQpr,BYIJ噶>G:q{]X:G)Вc9 M45Ma!UTA]*ʁr΄6΁:A$r1D~Uv u<~d0RBk"Dg#p0eei'ik==>z=9(D:޼AyXјm3jc"^㌎}}(jx,Fg ,cwkmf-VOhv:42Z YNCF]cLv)/""GSaM@qCZ9cZ> (҅8=HڞEuAFW~p\uJZ[Fh]Q;﫲`3[;XFkAw,0S&vDc`{uyq5"8u _Wh6dnS'Wr\=ILsܺͨcYrť)_LY6_)Bwa 8U`QDG&'O L Bf8GQ\>0NZU_oL8n'Puc&rRLt#3ֽ FEDٔiDa9c0vprL5cʑBNq6byiʡC(&b\opEJ(oѸy]<[ VNaLph ?h˗uXn65aQF\>1P^iHEfЂk4NU(,֕jv֗JΝۼ[mfVœs|_WtL!yEFsm~Rd}")\dln}6ۛ|_&[.~Z1^ݛOh3w]wќ>}O>vI)͔7vAo-K'DZ{=\X{ML1rP T f-}I -"YF9:1??.+i?FE MHk6v+˟c."'?cAPh:G/?9SS#n\Gc ‹Yˠvkf@Z1hJD(#|QH5fV8v ,d1|ehG{r :;=})׬cjzG]^+lol_bmuաh0Ya|2t7n0j矬BcL4!r%n Nv{I)4|3|O3;75jificZ'}C3myV؅h/ŝ,Xã# K'8<0rpW~,R0]D[}:[ L䝬d)Ъăt ii~o}[45j:?|lѫhjC z~nKeq6(Q6Qngټw߽?z??_ U?F Bap5{)[]I=^ԥ`? a% A+c9΂YkK=G(J\d @UI:ǝ.tebØYEqցZU$>W>-RkDrz(+tw<§y ¢tgI:x_dB#pa X̭tQΐ79?es?,k($ C}<.u"'+aPBFGX+u#xI:˜9~Exދ\=.[[[Sx4UKd$2aAܟ-|F#ڍF5S3p0DEey'!˻[_?@ ,4_X4 K} G=N^43X  >oIMJcZ;S>w=zPwLte 9, znDl߻˫?!K c>ΰ:ՆT)|0eݵr Aӡ('m09YQA6;NfrϪxX>ljb;r뗞PB>a.na,*IT)4E^L鐌 X1z2~ߕ)'$_ 5ZM !X;}gspx{ft Ec w i5cxP.Xi23;`8:T; o^4 'FDFrmn޹Ti1o|>Iwz +?.;k^GbUQ^yVCZ{|h +;z"Q^H,+Xgc#^2%y4J9 9a W4u{lnOg.0Ӝ^oPoi.Ds)i uy{B`ptc'/,&!'N'>ŨWO#bjU2 &Q?pHY$) ý{[\_Y^ZhSquqvS1RGِefffHm>gi7ی=nlԙ:'Dh88豲2O;bo6f0N*lSק9hDI^|Hȵ&1@z ȡJ)pk݌QLCٳkP*w!'Jw.1 ;Yy_bVk\X HY,BאY;}4Өq6s_%v[YK/o} )79اrq[*% bkk"pL8 8.Vp)jq)G2#D>2љ`sHLj)i;APv9!9_$gbs{o.G5_WhH)p,JMÿGz 1'?B Y6$:V_]XBXTM`S ;"|?t8BpyZo|ŗyO`@O~5iQj"~q6Ѣ3 g5q!SFkA k5VB:@L޿jd  YS)7ݝƔ^z(v1|GEFs d|z?`Twyw:a/ 1`cc lompru "B8,?:D"2$ϿQ9FbpAM_%/Yi5đBvS:;`(訬PR3O, /`<6?#lȟwv ϡ,"r庍FZm\Os7~F%&Q,u~Iq"@kukׯ'_ݚ.ktNN"7n*BYK&V B ZxL\K\aһƠG5u*AƚXEԉ%Ll!֖1."=٫0ƾB*wìEEzFV8"lPĕϣ? [;H+xq=Hȍ_ID#\=+Ik=FQcviw<9yÍ=Di$QmIOXׇ`qfo"޿vk_cNPDu6 IDATwYmcXccB92!&Nd .bɲ cl%q:i22wh -i[gI9G]=iMPY-T0[хr3߮;rk!j}^)ޙu(wΐA3˔^TU@@79qg'([w`CL9J*F1Q#Ȳj!Z~hK;6sKˋp?o3lxLv߳2 CXq Ŕ D:x~+{*p9()4a4ɵq-O S h$歷YK3l(R\t%oZ@sjn L-nAᝉͭA8N2awD2^2R`$7܂wdܸR}w啽׎hL#*di6 C<1ҙᨏNIa-ay^zBa4;[fLYy6k盤qp??r]X}p *Hy,jVJܢVcݨ"nC2peZֆn>ju0Brs!J!'׉ҵǵXd.r=!ά48:s$&'NaaeF{ZMÃ}ozY|Z ~N͠gFtu! fa͛J*ClGkF#~_Du )Wزk-i⚝I\*gMI1-lVOKlnl^9J!6/srgu&+F/"E^GpS$*i`t1G<9pL@@\$y湗Qʹ(h՛.V rU َgna9>Y_3lbn0< ֓,ҏ;Zpin߾{YjϢpѶtAק>,;@ fffigt:50acl6Z3G1+ ]\J$ 3RA{xءV2A&%]ڏ`ˑeZ KLdU4Gbb6>:#c*m p.F. 1 F*j6/<|ϋ{=Xk} 2AyQ$]b"DJ *gL>((ePA9b>7SL&ON% S8qn]?^*Ξgn~8>ZSXB1w%Iaer03 6{-Y@Y15/D1I;ϸ!"5I;ZU(8^a(@)6*Re,..Znl|b-R9'JX0ޡ=zm ֯s)+WP _MT꜆ZI;:,s}ĸ.R$(y>eHBS5ڄ*/pՓ'Qf+WG(g?#dRoQiA5جDœ5y5']LkTр**i\dCRr 'd<5dc>?U'&(tOr,ieiV6Fo@&NC_ ,2;3C/פ͵XHZ2ϜgaySkg1)}: đ`O7n095{ґ49V,1͙Yf9|3%2 CGϋ?A]3ϟ`"g^ߣ6yIDJq{HO;aΧ**V>BVY!?g7\<H.iZgaaC ֣pG&y6b<*HXaY[;&<0SeW_%G.יkd4SΉ*j3.UIt4Q J~jA{*'*B9''cZծDŽK9h{llܤJb!sTJQ8 )U(#e)28ˁ HX !|1W/,Eq k1e;`-=r$09hE.?ysI8湨:~@ Vz}t)\&9gCWx_Y;8l' FJ63T%L_Rh4J_4)ISU\#45wAkMQg8tRkw{kwfs>JAI|ߜ /#ͻ9ņor¥;e =lSp2܏LƥJ=>N8D ͯbdrS;Mjljh'_wFuoAyM%Vu2pnoEOiV#N8!R1D)ܹwfI$,K|ӟ&gpHPXce" 3m(!cd$A|pWЭcc*tˉ2Y.|=d<6!u:@YH}+Gd Kva*_,PB)rKiڸ!$i_ {45Ꮦ_ZӘTJш~h4*E:$\e`ea$"Q#cx3OI@1 os%'wdc.m>X$v \BEtz{oܾN{@#E[+Wcuh@6e#߼wy'?fkgEjIbZ 9~V<_‰|_Ͼ[`sp#cM}vf\XS@8.-0sH}l*%ϻf[Bz LJES0'Pwdϒ뀝ʄcxF̴kQ)BB`gw{wX {{lnZk{]ѓǪ(<5d&tN?I] 0PYZNVȲ1a{|Gܸ~;7ih4J8e=/|LfUΰ'\Hs&iڙ*Zn׈E?۷ﰸ0O (aK[vRW,98h3n{֒Af]i4bRNX:ʡ3%ӇЫ!X gkL3k;RF.(cNas0wn!nQC6rp=6˗i5'6l W"w('/BDnm2 l+,h2U \Y?~q6j \H1;;2•M޼[/-☥e33cW^XS{Y%颸Ixclc3Z"4Mqm[w/(xS//2 4F!wٺ6'R t;|EBT!.xZ*xM>- +zq4j'B!+rڍxg絟~rw#n}< HPb0ٻYG y`@ UQy2;;ѐ/+}f֩) $GY[qJV~?˿>duevMK_k_q֥?\(f BEr fgłXqӉ$!MԒu4ӤZGxvQNsO >˫ p{H A)D92lkK9f)ȆHk ;De=NE 19a#0&!qdyCdr.1y^8oZb "D`ju6qF` B{&P>q} 'Zaɭ£?E8ᨇyǣzY`$,,.2g #P±AKn 뿣$&iHGud!oh=]Z6 痘J(]!buB)pxxcGEj(RR+$~zXW(xh0M]zԥBeS4{Cg(Ν4h CiY:m:?*qQAuK3~< GQ4%35ޙv:qJkf@|4sX]c{XG k\_twܸW8}|w\$O=i[5ssdg>5!#)IJ!Y__GJ,=@3;;2wF=` Y3h4}X+㷰Hkypd#q[EQzcƮD>u6oh5Th4t{G_9 $RT5 ɇ@֥R)xÇ |\pt:/XYYa zр|4ƣo(#!gKox'i6٘GΟ%RW`=|eC%髩8RHUhO34Kéyws$;c!Zw;O$Ҕ4cWN.eek|?%X+)%S'8g'&~Cp|ihb}: Kh |6gϜaw= zY/!oƍ0RS£qVVM$4x֗]KG7Rb}#g~?Ξ>Cplu;8؇W1yޞx%2V'".Il6򜭝m㑗Y=~N,njF~81seY6R9n[!{X_G*_6-UVl98"MSȉڸ4(r\5q8iUjN6|vM$ls]}q/qkpM$I)E/zixJQ.ݣ>FDʍC61 ɒ$;{D-̪^=ىH`&2HLL3e?@zIhQ$.`=ݳ^]ͻFGĽ=fY˽q=|9wKƌlռ4;CJRU ?) )$*7\!Lxt(b`JkYǬ̴1ݥ'7ϰ+2t噍$UavwP z-.=%G,u8ƇRjDجB=uI4>İg?5z4%QuTGI3 Mƽb:#I X\\֭M9;V0'bKU*P]i):|+_3WZ)Np ?K|_r F {Ds 3c 4ϤRNx)G)4ѱy^)i#JEiF%qk2{7*ENG<|tR!Q>7^!<ùsGÊ 2^Frv5Vd:çb%kĹv_<{C4t{{Ox!ZArJWeQb$ŔҬyHЗT4&I8q%O&{$PEP Oj'"QEQTBoQb߂W뼓fDaT"@+yǏW=Y=0 LH|d2ѣGj 'c~;5C'eytq>UA=?*"YԚWSrU-hx>uRS'eЭ9Fa:Q&($ Ѷן{ `N7NssȧS޹IxL6*3UNNД;.NgW.3MM^Nx*^xWHOhNK| ֭[AUezonOxݷɲ`8b:nM:贻G_i3{1rn=/ '0V10NVKruڭ6E0v g JB+WbJQP$dG4ijM ~}!:sdu')VTds(oǧ]J ꥍf0,x}?WW֠Z$d;UD#BP]#zBʳ+v/zQp2yIt_lBsFBTv li{<'MP,c~ nc}}4 08#>^E4DP (qNSz{C \Y] vՖjM %RL #>7 hX|`/Eej܋|"Y(&%EJ+tKʏʲ 1 FJx:|B}AyOf9ɵg [[au8[7օx7o1Ts#1*yZ&.Q VRc.Q gE>u\pX, $' 8 Ha*XJB}KJ/&* (ɋ) -掃C66:X]0N4i+!R6Norgܤ;o xpgᬱ='On7 D8dqTˠ<:LJ<PLwq:1A4S-m ( VF 4ID9Q<SI(jwU'%Lwob#ZLHMS7kk|+ș*@'W-mx4Y[[n, T#WX%md hTu$mᰏ!r -+zD#ֆ8wh/tYN+h9AZrޣM"},I>7`|+_ dY%67runxյun}r}K<.!( ^*ngQ*))[m1mܣe Iaqq(PBRΆu ty4eyypHZ@D7vƕ5Sj+M-YkvSır8AIc;LNhc eX6$ vwNw$mQZ@)ZL,~ \8r%n;x|GcjWEDkJy{ヒsx2/^Nca4 9n~><_CkAWÇ< ҈:YfnqɴgwdN2h</ =zlmmJrhx< $.7M0Et.t)bY4UiRC,mJ;#RUbU x C*<ԑ{kq嫗<{L&C߿ł*Qn]Ҭ`"smNK7xsby-yS%NM.͇z=$qUZ ࠿oN>^q|:![xW1!?VU_^ujc(򜃃)R( hyWLՁTF!;e]zd$*BAm.kUxiigynŃ~-s4k,>G|6EQB~/Fg"1p-.\*ј,YXX3fe}41PԩS)筷m0!95_ɘLTA(4tr (+aBގ?iu˗W!e88LCiX;f^X_?TGIY&BɄb<3ḵ$T)Ee<caie{=&!Ѿ$f)G-;]xI.K&MRv 8 zV@|HUx!f S-SS^I e1ْ =[8r&Χ+6͞SIFJY5=D~w +Fֿ*g`Xofߛʯ~_|X^Y8$6b#M_a!<F*ۻ>[Y_KRoz&=֗; njr\9go%i*iuX\8Ǐnwޥt묭,b<{GDcPEcD_YGQXHkKG䕬yO{!{{;t6R9hU[!&BZOhFS=eXkIH<[vyapCD1Ln';oZICNYliI)áloﰼD'fiI/1^鴨clqW]H+o?pN4烲à"Wٳ,//8.qZ8WHFl SXeq4bQisڸ:)cҙZ#S@/TRCimh;`S C*ԌYƹʒNe?M RMxoh#W(>v<:`l(ڕLJ3]˞x9P XX[WOW7HNː6J; I4ŏRnp8{ IƩWxgX.6PS4K%8s,VpȒp=`\uťe4e_e\ [=ɵ^B{ǝ[1 ]BM+kWVsT.b~JBA=&Day K+˨4*v63<3 e{Npq F|riքĽˀloobeee+ ƛud̃wKIZ)lld0u΅RL8t Pz;~k@)0TB |-OUEؖX7 G=>JPGDAں^Gi+8<.;c1(Uܖ:w)A"GX,*&u|ӻ!tmv*kN4g`VO2fq_d_ 般x#يuG*ȽQ*4|J5:F$\lb%tLۑxAR~io!r"Ֆ<"sɤdd4(7Xe^ ŧL)R2OReȲ5i e>}(=`Bq@2XgT oƕ=WiVWW0ڰg{{jT5!9 Kͥwsn҇%{[6*ͳg9}'O7~2gT ׍5 :3y$%{`Oi% ɲIhn ]Z)1(-T):I轆@"²Ԩe(C@i245aY (˦YIl!)$ɧ13\y]ȾU82iIx0pKwry]ȭcowG #eQT'RLZ"+04ڢ,jeo#ބ֚gc&ӉPCO'S?ln^t{lr&+?w{;J/ 6It*XXk wӜa87i1)'rm1hAJ5r}DKb&_/Tl"_[[~?;2ǘ 4%#~G|gﰸ3/I20=aO6FrS6 S:s؈I˞R5RA*$Iɲ$Y˭[18` /sϋ.R {Cg_9ݖ¹b8% b!e™vQa-2d+6V Kl=ڦ1R%<ƶh=vs|s(l=l%+C~q掬e0 M='VH6}: ށ8]2{\pMB~aŒ;Ǐ1uECJ4ڈ̏F<PG*K9ao%k`qS+.cyS$Hz+K1b2(JgD6Jᕔ=y*;`ąSiw8.r1Y:o3NȲTj(bI20i-TIH;r`yY^Zc2$F]TY`՗>ٍ5:l<҉5vmR?kKx1g>?:JQH 46*  juI&'ʒ49DGIa >@T YH8nXZV.]g;_(&!-a{'Be!J0F)WȻFȦ+gMc8ȝ$0ymB'F %Ji>sn}BRE@lRʕ<z2|C&/-Ȳ,˸}NEY NK~Cid hW(u\EbnDgΧ-m1OfB=,y1a{{i$t;}ym)'%(wUMŽ=Lx7X[[ < ވIEh& 2._Hb`0>}^\ W=1,^U)~Ry1x _ak=ץ[ WL% IDATs~wme(LH [ؐ,aN'c41\6-P Qn`?ZyIT*, |ۼ-S4my>a2 cO)õg^70{/L 17SVX^ q88PYTKuI()?__B/sP}nݾsh'7tR, ȗD?:*N#$CqJiJ.:<++֢!Twr;g-E0tG7 :sj<4J2v8RFV=`c \v;1g畡baA$Z~ťdYR,css[QƵgXYaӦN]vLʴ|]ڽ ^h-0zC? '.--qppP[jV07%RH!i>B1+ ,5K$W3 1zcݭ:y$yKѴgV<kl r8LR<"Loh̙YK^(,(g)lh0֝,v\z.jVU&C* 2pڰĥKztz}xGO,26hOW]{`05LBU~/]_*(ݝ-Ϳ'My1)!_p ҵ }fT{Kk>G*W,.s3:J$󳟽_U9<͹9i<|毺sc ;3Hc8zfyMWbqQW/4|x&ߣ(JZ%?|HQDS1,*>W<ÓG0Y+s>t֪rN6]HC2-Z&H|iA7m-;OB?P>&i* 1ij83+dȖV\?Ʒa׿Z=͟%ہv+_}ŽNYs*1:F ۿ8$AQ &MP!cVQ"l=F@םYp[,jNq0{g50 )U ƏQb=b}77< eڽELڡT IΛ?^%2ʐ*<ҟˈkY Z:d*Aciw1~pS8)pMd-LY^ 68+ojnlMik%u#Zyd: a):ӿړZW8q.zZ wB>tb寬{/x<9tpAŸƤ!g@h "׮]CkÇɋv`8I˙*]Kp5̺X *J[ָ)os}o|OnD,8(S~3]):#$֤^Jpmth[mXZZ^dy.]Ƶg^?ef$4|_-5 zD_ԣ}M{=6668}Z]{fwg>Ji&7^`$&8 ,,VBJHsƓY,c#1 R[-iFP9KK:uNKߟ >n434iLqR9>x|kW!}͐ 5XuS~'9sfS|`XeP&!/geL .%JJ+HކqTFi~P#]Ҙp4?y Vf\>%G]Q4e~4٣E).3H1NH!%{|fggpH QԤz]/ϳ>|~@땴r:I&5)ZIL)X+N2T 7(yw8sZJPڐh ]4N_ ڕRdȞY`*Kь'9O..KD2 43Q1D?!H\k iI x1`[LŁ!nm IFK奡VdK)2 Q%˼x@y`o>44@R[qY^ao,vX"Ijn"@#dA!NGG(|2 &(ʒA[Euk_ʕ+ܽ{O>t\g1$ )L, MUnRUU Y5Z+͜9x1bߨ#ɃwG7oa8 ?/bBGfʽb|nۦm}64>&{ωчeA' ȧepѽ +/[ME^|G={V:&9(/)#*y3zZ>ϰ\y~!n:b0)iw5xe$ PE.˫TWM@r|-?1T㬲 HXc,*(=mR)N]UYv._Pe*!]UY3R>kXx!4G#&R&: '6$Ls@MqW+S~ɭy7Mzne\,.,})n U:ڵgs·LcLLl82h u̹sXYnZgS)C!݅.,m}ֈ"E5 k\z=t\)_o$)IT̄$1$മνs9Ίۧo5aY`Zy$Rtݲ̡J :뜯|ѩ!IRLTq-CƁt4g̫RM8V-bcYx=ZN lbT"8*Ov0V(,E o*1h%1m6H*dRjSjɲ7୷b<n ?#|Yq>UZ{>/ѴPk9sFoommQLk.IQB) ɘG#$RrtQIN-dPAr\5ic/0%5*xj#DBt68/=&Ӝ3gR$Z'=lEsr Q{]gΝg`pP9h1k%*!.J3Ė(6ʹq`zI EH p8D]47@5Y, F1zZk-WVNHJLl$i+*@kSGm^K_,"n "LR V:l9t(b}"eT;b1QRcz= /i"a⊢*)˜y8yQ!B;M0JŇ7oX5W4 fUNDDj-bPo6E |`ċs$ێcL ֨^Z^aeu*gaiν Uqފ|ZDlx~t!{[?yLY\>L/n%$ѳiFD;眛Ud[!M4C(sd_sd(ugi-"h1!! @3lbac1;f~i}AԡE(-^4e$ >yNQ!hvc%Tjk4MOƘh&Zr|wǏqFFvXhw#._B^s5z'oOTIC@ | g[){aB=M\E}{\<[__JW3Y1%E$ WֹeY9[O"M[.ĕ%iNʜLqHB|Z4Kds#ɅPm9iw?e^x%=<+k']N,b%`1pP1q~6*R$ItT/ [\%;b:N0vq98WJ]7IRC`ݿ|| ?.ttBMLYt{ RYV5R0!~_:Q BH$_%02:a5M[^ fDYYtF1.tP&GAYy$`(:*ɟ(c%VO=%6m>S/4ϱtB=W}55ikXa=ѫ(7&h" Ե\|d Lc0Qs_T*o{c/yX x5Nm!"-%]u+kkC  WdmBki*CbPCcnK; -PdYVKdow~ۼqyv2v nZhy__ee}q]X>[LA4K@V1#uZkPK MBa&;<޷D(<r5H[{0U%'Be38< "}HcO_x2^y&)W8H34:E^bvu. je(9[=ę4Kem eJ2h%%=J/c߹~NТs=Rr ۷p(73ʡB)QljeHVGk9Βe=Vpf++x`yeE:nH^@gheYb4ʈqk˗ީ2$ihxtrtq>!)E^PbĄY(iԬpj {!mː,i oC͵g_:pGn,c][%WTTO%?+5;11] ^y;I IDATk>i׬!G![a.Dž񉡪()[0O98g6C%{!zSj-Gf?z 4 :Ca:JIb` HPFcrJAz4(e[Obr4R" IE. EY`C8ѡ`հd|, kp濖]םg3z5,EqE")EOv%_ F8 F:F; [[%۲eI8"Yyx{w<;?=Gٝ;.e?RDug#sLSB7ھ޸.m.Hݑ$;YB  hm aN(t  R챺_x՛)Yg)[f2ܭ@2N&(LIڊty}L b00??_?v{V&IƫM11xf$\zȔͯ|)Y];FwFetTx'u~G M307LJ_ϾݻwO,Ҧ(܊6DF L?v gLK68^W ( L&4n@QQ΃rjZ % E㍳o$e@4h%u&K˴;-1JqEFXRa 1hEA0R5kyJcAdDm]4NEeֆoCoZZCDsT2cӄEB o E:&MsH[ gAD!qX'M'HlN9y :6Nݲ ͅ8r}YTg3Q;3಻рi0%R{a.ȑ!v^h4*SN8 *N!B Uh39< ,,-gu,(6 $݅E&yv{NzԆ(e?}U` 'Ϟhvk|LAhK%%m40$MHb dW"ʀc͕DGNCQL&R޹/lw:D3)RܹÍ[75b"ֳ8PZ1‚ #U?`wWc=ݺ py O37@.xy$>RluI w坖Wc wd{kcܻwv߀V˹Y]=IѤ]k4\G5GA.dlsJ1.r 6%?(Ejm_~ 뱾Nјn#\UVJ+1Q1lpd|ť%_RSl:f/}+$y6FP!zUsiՁ;yNPLm\pݲ35uV$O&I2 0ëDQG!%рw1~1^/~F Auklomh[־-, REʩ CG4MloorgibLRP։UJ9A#K5҆\)Ibm |{^z.=C0Vݝ>HZ'e$)'$Ɉ".ǧ 9uq/atNDH9B[$[i6V)< zx< [">9s. pifo %RE18!7Q&d]L5ա̅*XX# ;xe&Hi)eEc WYI>a/v*ʻos$ւǎg~~ȹч,{_8acl_eGR^fy{iz4xP]2Pu6|w¥%{Ҝ7o 01};J xWUdYFl>hRwViu? ږ BNߦ5c5pfN[uQ:#Bؚka P0( #6·N€HahdŪny1 8cEʽ?GY[ڭ6Z͊9J+z󋬮#5h[HѪi4h;t=:fZNDpܽ<Vh2&Wʕ*ZKKKlll^كH3s}$pC~q?2(%fR"TV"g/ɯe!,癓6olTΙ ڗ²e5nUϦ䄰=!!$Vb&@qw^:*jDQT3PAW%5^,Ir#U@{jaTTES.0(O#P9F _CE#ɳ '^j %.(a`9gyQ0[`l@{Ĩ7) ey< WUoP"lomƻﳸS?Q@DuPr1@ʵsq* n}.0-QO|vwULȲn^c8أ.DQ2i&u^|傷>s;L-+ǣR>!^0MH0Fĵ =yJ=j=5',~j\>a(vm&!y E reygyxu(rJf=R j:)@Mf#G?wv LҔQYF;?{ji[P~i_Zk,l277WNwPi?տ`8dc{͝]  + 'bX][_%;6㐔<|C/͍OFjMKd(EkGYkRE Ͱ} {@އuf2BVL̗5?xQ `$l}˵?ٳ4-cYoO}@ nZkBQq8E 6F[3 ثRNO[o[Yhj7~gc U<7.L`2[mҰp4$aīa4s<⫼ҫbI:in\*|?P{6e9z?u2#m*(^ ŒKELҌ7n?T$I0.Ym3Ѝq%kx2oM8:&s77XXZ$r0\,az61EЇ_np+3˳J}:})ǏQk`"6(JCRMA[(WtRJ%cZODO*whc̰Gy %ј4J#2A@VF ܹy~̱cQre>/>BkctZst ,YcyiY_1m54Ԣ,~_0G8c',A-cXZ:YO)75vI0"M3J2!/=/sβ'I p s|QN>ΑYݦ^ETfdRYqM޽f F\ij-c>z}"bAr3YVCj =C:og{d(ڃDkX_gKNPgoɯүp%:Ae0`qƍw@eqQ !d߻wc4+&I27%5h^#d27͍jZ*[<YYk7?/~}:<#5tʋ2n3I&ɉKai^y%{Mb!FV2D58QlS$CC"b0,W-N:9M;Z\+׊VRIٲbmKp [/_]ސIBٳgY_3jI( :/_O>ܫWǮtWQ#xR%CJ0 :NdLׯdjӢ! .D|c|y0e NLQeA4byRx=ralmccW_y2 o\ %V7~PTe/S<)ab0Rt6.Vht6u&ȸW5:m,\d9XЊ@1wnbkUO׷>NZ)vp/-#Bs:njKI+df mxdie˚-ޭ 1$E s@9@+HFme[cyypx< -,," }[eev-ϕp)$* ֶ]/?e<$\¥G9y<+'sy%A\gp/Ui"]_>FVWR˲AaK(*]ގp & cڝ.<@-\SF18ޭ{ll>nUĉAa BhhcQS'Nr Zvv\Ccl5i4U B ~/}S'O(ŗOxH}Ѣ}xsǪj[c9Fi((Z성$^%>YR][)%g+rȀ3)wgXc^ VEꉾqQAuABBU"uHׯ+O~c+tst_ʋ|s)N<͹x}E> ,..RwdVP:1\0hj6\~I{ `k!hD[DՈmqQM1?DPU16xƧ ¦'f(=ys?7`wX=wy6/RJ}Q:;;ԣ㚕$/#shaK ,/W^3,BH`%6H) rlQ{`loo'vs}ˏfs?Oe2r#&D3"> #tR 0"A( wPj!1A1jb?啯K" crј͑?qfCޠhRo%BǶdpϕg9x̱cJAH|wD)m#g19)m _; HC-n 9}4d@)bV7~0 vJKJɬmQHN98H>}:vBoƩsYZXQ{0Q2P'(e9Dii2YZ\Ute!l5J 7J9zcȁl_7oޢnsYN>EӤFzHr[.qAY ofICYr*Fq]MI Vҗ ?>/8:a4$@c1VdpL^GqL cDYl9V;,W}^Z8L& =$dY^" |K_b4M  U+CCvmmK.{oCWV(ETdeE muqk'N?Q@ h4]`XK93} BDaz-˲GyzB%ֶ]ڴ05 7n\ 7>zzh8{$kN'׹c4u8rv6h-1 vf6ش:3_y OכYAi$ cFN1K9~u]cxp]:.{m' )Lܻw( y ORl477WB{kZ:[$I nX6n\<Շ IDAT|z*\{0#$k56AT:Mq (r ^fW9ŋEQVځR̙.!fޫ2u!<U}z;wr޽d6ns{X9p@gWt7Ⱦ9^ZPJD}]Np-?<.2+kkx+.`̟Y!_y#͖Yfe/ω>FB#%{!$&}ə$"~T 1^HHq&ܾu#7>Q1i` &sE!#1:](+; QBp|~_d雿2O =_KhVwTDjE`J~3h |/RH 4h $?_oK|YYfE8[uPK\X' jnܓ4/{/!dX=><'ez8xƍꊭZeGɄNQ=lߤV ( UL DA cܐ*p'n79y %ܻK%LC "nu$1.ܪ9 "7s΃} ީ8L?sx[}%  E}) a+>DžKOrǗ1yF%sy&{;YFHI=k5 2@|) M XٺJ:62zY`8,AK²y'Ï4VR8gT!*9.  Iх"˳} iyKϳMRd2!Lۈ6cB0)hHGȈ UA=C+XcCH3W۝wQ`t ˿+ɵ+Ư&A8Ͼ=dD#a! p7 I;~tY5 b<Ȝ%eNZQߪD\ZZbg4K.|BJO?,F-S7qDG\~"BQX:u^M жF0Ho}5VV8"K #@hƣ1[o?ր gN%5 횜P vܹG9u?RCJt`nayIjAZӜc֘lnmGȶlʑm}(ơrM%dIBAQ oC|DKK-jɳUF%!MV۩@4y0: At-x \ˡAE'+7796Ɣ\G_v<qj:YΈMg$=#)0E6]\3dQf($Ɉ\2D 0V3Dԕ1,1L (+Xbw!${Yp|IQ L  is#?_~J30FqlmzNAX?$.Γ>Sz)P}1VAKۆ h؉m[lr:(Պ?8" BP@*@Z3c 3^⚅=Litzv-F8) x̝[tlɎJ3Nd R\)7$i.f·ʶ"BG} x]C6Yɵ!wbL=ON٤Q_vh JF A cE s}.^^gIG>ia i=˲)s\JE^x:9k96?s*Q(i7L&^9.y3 z`0(IF$2n!a.I2&OGE n5H|nfM` Ciz62IfKwA&E=bhIzki}ۥK9|Ǩj\z•&!?|+/pgl=@g'Ͽ݊9#$$Nh1 #^/ K 0GpDYXM\@FyH]8E{CT;}>1f`aa4M%~=}6{j' >1{,0xHÎsHᰏ`r,u8(<+fZƩSa0BZC&ѲOght"l [)9rUUc8s[%!yY. ’oqlX)|6dƢ[\y0J{S~6Z1U@`Jyk{H%}υ h6(w!It/pյl6Ik\L&,// Kvc Kwc3+WGܻ{RdvF)K09~ (ia{đӊM`sX`KדהR,/-sYz9^ vC1"/rǟ7^:I2f4u7D?cs)mJ5:c6r= 2Z8l~]a*YOW\~oz¾G8kU1G!23[܅GdxGlH@` Wwnp?ӏ#"z?Zc]NLQF^,0m^$0_ynEn~+,./[BwD4Xzv<,//3hǸшжHRw>2]s: C>z<ÌX!A0&J<@Xo߳bmeغ]UΥ_* AaHV*W!mhѶ1dqqGLeB`ۑJn"0vkAD`" #f(PiVoѰl<gCkl~Ҽʍ,;*S 15tz qb8~ΜwfooZ-BgDi+K]5RZ-n} Ǐ""n޼Y)KpmY۴Xi]W(ܙs\8w7x nv^. j&HO֝[?vx X)3=4S9\uA )M{wpSJQY֗5eUHZ)nmm19yen5w6oۛx"^o'j|lmmBК@\lsGY`w8+۝gf{oI1t$0-W ,͜0h/+ o$兯2f)%Yr n߹ގlAp&Ӱ?1S#Pʊ]IaH1tL )Ȓ!U[a&Fa"H&gpN,)qQmT\L}A X$F#68XEbOJ\Zkڝ9ڭyndGhaB̞sz#w2'u!MoܞHrt1A`;¡ݯ;SzZu8Қ!9RʒV xosY``4fsc*% U(mo: wLIh~s<寐iˏӞ[<5BB[oVKjS},+ݦ(*! ./}yDeLYpзJQ(E22 D((0hTVx/ycs0l>q]k0r#"%z=ƣQ)]$OV;rF6 Ʉ>xz 2 ho?1yIo׼pǏ#2JCŘ"1(N.@kɫeFy9!YS䙭mSGqlK-: 1oIhFWZLsMH9=SPnyօWf q`qq`PGZ>KVJ!: 8ȀHQ9;[\!wnd=]Wʳ(?EyH^iD\+P=V\Pܾ}yûoŵߧR IDATQo0K) xS):\2Kxn,1<V_H!H QɹA+"aHBOno̓t[-Q;BD6pʽ?ii*'y(ϝC] GG!"(e9Yt*2:ȑQkdZ#)L%h֚[ɧ"eH#aqvΣeb-kʓ m* HG<L?wy;oo js,-6fLg3_&q^V3QezMj?Rr#<}q5N?,ig` %I)K/ǟ\3! 3@[\xЖt^p8DrIUh'{dBG-.[[[x9E{+LF#L1B63TG`4$IߟxɌ_ձ8h$ 1/GO?,ֲJ"pCyb2I&)(J5TWԭRۆn( /n^hWjJT*)dɜy㙧=D!bsd o"yS޺=to}\xy~WI$BmY{EF" lS+PARޓIKkB  K%iKtSwtJ)DQ*n&}10ʕ+S| M *_԰.NM4w{IQQaB#T0mznKm8/C5Bsss CvwwuϬpR0VR:H$ je9Z5H"%!#>m^_#"C8^&G6WF>瘎q~#+MS›AVہZ#V]ջ2e;M|'sE٥Zh4d,qV 'RjҔ;ڝ6eg׸y;f3[[Sf]Db` cׯ\efve;_rn)&sɿ%;}6AUdmᔉ4(4_s8RzoݦOϸEN5MIK+aOR'.KZ cii`@ѠZw\Dc`nvJ5m0sf>f$:#il:y^ k}Ð_Ԫ58f{g׮IbSsZkcJh8CJry~o6$ Q#G 68,rH8[V^OkI.Aky_9q6]O\f$Pj:ic<$)ҕJ"kkM9j,,̳N TEh'҂+ I';.0_dvn'p'm7c:( \I56pq+U\) ҐH1/9p!xiN" skF SnABk {OQbk zD.n8"\|~C+ԾGQ~}"X= glP5⅜,lnlGL<9 =0s= ݊4BHΉm~O,贩>jO cyAFlG9~$fo\|yvtg#Ш IT晧FHaT` LBr$#$:icc\.%RT!!$m}AX>| /ll1$pzdr98&RVV)NCcoi.׾"'O?FTc{;ȹ>\N5#V1B k\P,F1aMmmu &Idq~񍗾]´WqZA`mNJFݲfJ0|I5ͯEDTf?lN}㥿OV!bɓ'g:w/>G/M/__Ǐbfvre yAƕR!1BLEO"n?ZuH#lR2f<|wV9/!W"S(#3Yp<:?g"(tt~ֶ14uv BN֏%V˖5k5ce^鸠R@&_65{dksNuvzi`i*TH&7.$ւ|yC G* Orr ci$3vqc,7˘K&Iy ~I[jG]×%#S%e 4N0RbL3 vԿ2i,5΋HXXDBy7GZY֤]ָB BA'Ceey0H"4l6; WxJ%RCJ lf;wW{xRy`TƹT`}_*JӠgR!КG1N{d<qiw{hi54uT2dY!t8s4G<=̓FFGۥqh뼁Ξ9'{( ພلTBbI!XYYaffFa>I$(9GVE~cwV׸rn2Z bLzҚrL>iy4&UԶ76+piJ)Lւ86ͱc$SM$/)eEPLZ9/]8R a*slBHwt2τme[cE\6Ǚ3eccӴ# HuLR ^nǛoUaZJiv:4M:^JZl AJ1Vx,G) C!1 jwMM+j9;;A@._رcܾ};E<4”jSr0/<N~& >!DJFHh8Jb"4J%ܢfED,/O&Mo=bY4(cùq,aC PȧѤ8L,l4-T.s)D 6s4mw&+2ǟOY\\T2FjpD;6rT,ƅ& I- $IR kJ &hSzk }<qV]xlkWh4GaDOD>P(Ns6X"8@W8i@@HpGبX:saʴcK~nDC\1q(8= FayFOiDnTlO)2RoGf-7[%hMTQ8uJPI}(!^ؽ)34Joҥ5Gitu:& #| bP *&P!΋OL y$!lZ P\6E 趛.(6}bFnӕ#Wΰv,iGY59i# {҈D0G't;XLZ'㸓2fU&}ZkKN 8t8 i @6MV*T'٨VT$ɒlϟgiqA7t;m`nn'| PgoW1NFcl**{5!DŌ{hԛ:u:5bɘF$ -U*fޡT)=U<&7FlmN>p8Hӊ< heÏ>`k{+ƙljzl6WjPDhbDCbQAZƁ0FbD6ֹ9h)A*ڵ+r!ΞX)#Ӎ~l4zԫ tʛU̥K !2?\Rٔ)#`dܭ~)o9f$6OQ}GJGi Q $qHdENXY[ltRĮ4.6푞Wׯl֍#&HkRzzrf3[S7Vfv:J+-;lLhrNX\LCJaHqcrD&rI:Ѯ{GO>xH0fGSR{A/v$30 %_ʥ>kh4J7كwik9nrm\p3.Z+G昚T20@*$kHezf\h&6xrrFX"c>oۥd{oV0hžH.od׿9[opUJ"BъLI 6u"M]%#@c Q*PF:Ols&ucƽO5nRWv؀hIqe1q8{9R0 m Yel UG8leMy`l Z6n֝4[asi:mby&pg~[lo=`o' hZ&o7|GQĆsGV~zut;fBH^jneu>ZsI{^7Xj&5R!v[|Gn35]#|yT*"1zc]޺ݛZOqYR5L' (t :B"F o JvvbnqFwFUKfA(2NAๆBKӸoETP KKx#ryg90ރ1i${PNw. u\j'巶'MNd|HqB7o/u yQ ,VTAc~K0HӍD E"g9`HBbHscQOl,HZvdkIVk(hD|2>sWnL@iPDqg_\~0@8a0d&(65o9U|}HVcCnݾkH)L e~sXq,b^Klkt8F id5&˱n/0Ks~h{xSؖ)-A8t&pHFlon<#'8 v/^K~!ѠE89}<ˇW[9],$v&MLWpاPțV3g( X175Wlw`Mirb](lN{0ѧ`:cds 0vww}ZFMS@BLߛVԘw&$h(rV'ﳿǧ~xԷwLP6:/]j*O]HբT.cEZW^.'Nƍd4&"39gnTq\X!y_6q@5b0358ȡ~ƙ7Xw=]Cݽ-ׯ*ۛl?Xn^ѓdsd39i4cYD$zB !wn߽LÁջry\LOMZ9=>mF4K |1GWȗ˦86B:{ 8#e~*NbqizPҡh!pp&&&&4ɫ~۟Y\^" ^ :N#] v:?H5I8 looTL&[>ɯM 0RC=1" /f.],,.O܉{.aD'Nwyj ^!%sӳ?џ3r}jF,p8Ncx \t6 :+֓{/Ai?1FA㟼709Usq3mtC'1:2رci8} ˇV|Ew0D fpe2h*1h阾Z+?j$*-Ui'.1weJ|AۛTk/P͠yU޹1Z",F?A01 ]+9ıg[\\*,5aD; ɣV~XT0EѬf-qx8=A[cxs$,j@'sk df:} ^d)Ewx)N8A?YcwTy8&,%=\-lܺyC<4|q3!_d)_BO?OwT^| \)qGtZܹ~6*Ad;yj榫Fכ(d,0IB0K6zJvwMJ@@;9ˑ|1B@lHI&cKdԖzar{8cR:}X9I&\l6j邊A+towp osit|fI`s~0X0.^|ATm\.GZ%Inݽo(G(%>_T_"=~\0~o>@NJL' Sn13;3{>xᰋ#A`ww~L6ǕfOv(1ULn(2g6[+SJ1JXc1; QcQAk7n3vO?Ea2iې.g;.[k+[:BHyhƱNUIӀ*="OFZ'vincG}x 9!Pto:+bL !iPfAiP RJ|7cZ䨴V/B9'{񸿾zT~R-Wd3hati7۬9Ͽ\mrL/Rɖ)kTj3T* URK+,.bvfas]\c:{no#t]m`&dbкOw9k\9vYfg.ɹDR$B2be^~smL. ɑ*b rӧRBBFhQ40fww`h_YްMT}qE&2!F `tt㰝-]ҦN0oc3稑QewwAOF޺Mr*G݉Eǩ¸vHG!JE4[ ZXEKiP r=8<OxOVĉqmfEa"R[>f4M-='cF K%.fǮCcaw~{2ԉ.kw!N #9_Tbnv'NR(Uzƾ(E`wo8M{okW>gzfǟ| O^dqR'zOceFi md~Co޻o1+-¡TSi{-6V.{ ffN"%&G»Z:r'[_s̲4S癙^`$:$A*-:2 뀈Dhm!z']Sj0A$9 Ge_ $ig~IbB:V ˏq3N1bjFF LWQ%cbc&j9#JH7υ'_d6CXZ.S+(T*U~n?avn[ˁZubSOgM82foAM`V0RFTAVȩEڦ8:%NHKGv#،tg.}_UClrFAa20i' FAdϲ5mے4HLXh؋h4,iޫSY1B!I&@djFv$AJTi3PamX!2!+RYP.T)~4 ap\4`бACHAujz_Oet=l d,aHN,!.]T*!Yґ-aj(<;g`SF4r Sd!֩h8Yocc(Ḿd N 6pDl&p,Lubݼk[0=cmH(g|.K_Ĭonr!5fK!f ʕ "#ULұS[7I MYHw~%ה%ͽhm(J;u3ϥK6 O0W D*`:P">*sf#P/]T,LytϏHI)![7npj2Rk!s&*fyQh`wA*0u5Y5@Xے'2dz?yBF0PHÈ-\CA? k>qrQI7x*4Stz#RDB0ܳOɬ`Q؄`{,((7o4^MZ|G8:.xE|᳴rzT(h!{Q( GܽyRʋ/DZEܞ[ϖ \AZGǐE(D^7=J|VMFAu R~l`a,@;?zO\dnf)$\TX\yfݭ<ؓ8ßɏb}~}քaի pLHBɖD822'g{i$.~.$q'pRRJH[II.ZHQ@ks~RTȇ|k|#+Gd^sЖ&G0 J # g/O^D*fgw6E7n IrڞIH>IDGӱJ ۏw:]>xC2U!e@ =ǟF\y%"; Y9Aޏ~~nd|1VaglR yHTJ/(N8 'TL=Ϳ7TJEҸiU~}LwB08|6Ľ>ʪ)+9js%ÑBC< GBr2x㷼7PU¡_+GXZXBq*42J0 $BD<6C6׭ bnI,661B]XZ6qMυ(X[ Qܾ{_0;;TR!'0$l%q8 s|c#?D"g9tds% TFt:7Js"LoOJj A8^RHO:mW!nz}J>; 0R6p]F0VJAjD./Ouࣷ^gsG Cn_ƭkUJyĀ.V=z(0: K(}!fs)9K5D(&B`"R! LɛģlbEZU7q=I6=mgH%9$odjs5V7q]޻ BiX>뺩ɉ{rE|\ANAS8G.7UXzP A)(O^ޝ;\zmWU88rhV XElm7>vcE,z9w,-155C.W`{o'ON`0\p1iJ'x}U&ZMdMxZ\%ϲL.#ɐ8`#~t8vԶOHRk כCXڔѡ#vthv4:77ڕLjd<:zV.8ݵG4XƸ} SJ*0ԼGlCFؤb;G)4Ehӄ"Yc`r3WR6qҩBRaP$4:QIʹWG9 R 8v {:;;ӧtY\\\(LSɠi$+Gxٯuc6ȕR&ݢLtݷ~ffrVaA&Pu)KXYpm((Jm2s KQ*"6=M+WWVC:!Ji(d )`HBr864B98,8H1!'f:|c+4e%S"8UaqD%#26 V/}'&BVMJ"pp\#h5f\^k8n:Cere;5Ϲ')h=3bs'Ɋɢ$#Ҋ0\3Z;{,SD!ڱBT&"nߺO䉓 "I))J &:AO6s]AxLd0dǻݾqH`D|Ð O"v7wqQw9v| IaV A v{o ~n_P.R.ѩ4p!D%i6P(8+c|9R DmΧ)<9.i=2zÐVea`*f 9{,|~aQ.E=nݺɽ{\XXcwwFL8!cL$l91o20Jk!؅xDcǕ^ckkޠE.!]IB;hOwm҃J+v%rQbx!ϦʁԱO^x(iR)(RvFKkM"#%!<>yrTm)j33B8ܞX?N5!콂J;_1ս-P(ЍF{7gv9Cr7DVFM1 & zֈ\v7N-=0U@ys=&3yνUݔNU@սZ6kkt:+ywHEl[j5*Ce:x!-tuS5Ij $ :^ 4Zf݀fAńaі;oREHIblrqX5R%yg˨]XCnمpT !g҈p{Y@dـ t}OVȂ zR~@ eIϔ{w~;hv眣Jdn_{)xi&v! #AOy"339Vr+!9oyy [)6Y{PB `O>RzBSV7֙R3\ gps92uFYk:w6RLMO111NմOk0쟭~=C M8(z}-WJugӜ}<;u65Br}6TB wo^ I!V h7Z|;ڵb.}8#F,8 .ӔUeh|G- ͍ $,iMB=ð6ֹEZgpjw0R~ lᙯQ,+'HLSL󬂢1 sjlcuG+E!IҔjA󓟽1է#t\o I1}7l8&B+35v>c x+UNZ\.Qڶsylln!Mۣl\*P6A;mlv9S4a{ nSJe:lQ IJ `P*T'`٣LMa&z}IY;{\t[117ər9|/ UrBX&2f&&|ĉԘ>> CXM~_X~ۏl7mF[  ryPoI;g[IhQ/Mt^yϼKl }PM^#|u IҀw{sM9{SS.0F:o>r29X{|W~n/r*Ɂ~,/[: 069{^0 ytz]ٿAM::: 4G':uV nXhBw"'쐍Ξ{;wEv(m4U>"^"ndd{ulg{X`;"a1;L2"kR^<8iq&Y?DVcn~C(QNe\uAq Ch CfXTPxvWX8xN+/ k #=ѱ1T JlϪ{V*,![^_zY:]T**}shc0.гdMm ֭כ!("%~;X\aFْ};]BԌR>8"Qe{g jf AI/4rSF4䔿ҍW<`ڇZk ZG"*Ux8#GIvCVuH _޿/ȑy;m?ܢH%Zh5;{1wGsj%4ES6{78M5^f;i vV<t6D16h t{̞^v0رcSԔkŜ^8KoIX(ۈ($Vmq}z*%Jm{:{=:l<62~!6:O9N1벖 g-Ռe 1L(紛ͦ0IRPڑjiPDsuAi67-<]^edd16خRۣlʇRJFG*ilPN?RǏY ϝfdt#x_Wه1}xrFI0,Q}'Nqn:ui3@.?$ujVvd}Immh8b~j'l>99dnZZ'Ytf+ Ͽ6:5U~+#nP,Y9)MhŸ*2taW=`>;_əR+[-*uĒ=ac~y׮i}}_|sn\ʡ*>{yr{6 $Zx7쬚vӕ IBGLM/JgoTbG>:})U|1 @NQPҧ\ftd0Gko׵: v+=h_1 cMYW&fѓ9<~D}'-Q֯~ч|٧\ʰFj*q '=MRA (!z-*CzJXk'Gt$bmN4~A)m&xNlbǗFIX)|׿f _(JJq/ڽ|z6>j^}=sco3O-ڡp.޵+՛Zmo1bV}Lv6\! #&B$ vß#ѓe`_=ȸN-Akuzc4i$3 %(BzJ)kcXX@\= Rmvbn߸Nc{ = XP(UXO B@Գz |Q3vAF@DQvֈ5I[[?>&GE o#h: (rrMz]W{Y=͡H18tM0^mcX fssN5v|t7zox7F/~@ۙƞO֛@Jɣ* BFFk4{mT6`|MldiG"[Z M1IV@8²pRXfܡV)CȂ}WJwo^+{[a;Ex4% C$رS,LQ1rF&À{7ot$##N+=lpÇ{?>dy)GFBp]jm {pK2f"Rg<}7۵oo2ϟN?۰NS++QR qǃ9~$BCyZJ /%)OW h5, " ,Nhrh4f!hm\8@'h44kCJ8hhheQ14'v*G}~Gedsi/] dtxJiCF""{fq!Szb,I&I>xDgQN(y!ǎ*UX]}½踹>TtB1/vĸ ]+q~?J IXN#\[Eu*UNCij5B,C|׆$ݿYNGhr<;Qa\7t&. i @0.tԙoXdg_+HuY_YF)L,.-jCz31!H,Vt(8g- ˜޾͝hli?S^,W/0=3W(;XՑj\9{wzfEdCEjCa@TiCBu`|ƪnZѭNġCao>GԋF3JL9"Ӑτ}C}ua&'0oNĬbllfέ7,-q vZ]:=߿eSgczmw4RvYn\\:qJ Rĉg80OE j"J$)SCԆzqM;,,,?}Jl/3CJ Ǎk_ؒCGlln݇]}پ 2ÜβI$m!$qt(Jv=B۱jrR5}&v^2;"x!ÞI)0Fh4wB_~l6=@=yҧP, ^~3aߺ sAg?uxC6ؖA퐻fI$ԪU2=dWvZk*`4/! QcLsZi)mtrBHմcUJ9=B?w높s-3{;Jqjܹue<-Ttv ћ#P6}MYg{cZ!Gi廷Yyp@5t)4mE<ȀlvC&'FROkKTiyiLO+/ v!xڞHH& <844}6:[_xfY!U)r%M*L7IRJ !5)8iFWS `EH+k0٨Nk,j2Á:ZD @qID(jHS+xcLO3<2OuavÎo0@?OpYbH _ȗw^bڬM $J'̈́7<}|Y}l0$vT\xFK%Bz0(R* Ke2ABPx0M[lgQ< /_f||sT2ΟEֺn2.e<_c4'+ypH׬/{P)*pC>}wyOgdmN)7|/>$bf{_Mg,m}NC$<^䩓!v`=OMP( EkT#FtUJqCrֆɩiݼέo;J@ $4w7wRr1N<+ۻȔլ-U֖[֓>8v 7n\'IS}ݻg g܏=+++8^~9 CyaKqI:7oޤ2T!Mc21*[gT*1iq%yINYsdWZW_lmn ;Lki06՘قgN?%9'66=K,͓hiӥ4I_@{CYt((V{<_+Hޒ8!N^)LO -&|JEE1fdŃ0HDSOO۔*CI҄],D|a9>N9)|%ZCP(< ЖAB^O2\sfa5ʛ3ѷB@}AwrY(=PʖRc!`YRό̃ˊZ-u4" HbwpPRYfwȌ 8hyZ+}t[to.b(C?MS(掣AXP.{;y:/\ Jav.$=He^|Q A9$IHR .-,05/%V 'NS.|q*8 i(scJ*iĝqco;,,,駟;Zk>Ѷc~d>!IXY{Jyq՝-43WG)yM/O?ZmICT5;q^| ~]"IRFHӴ?rw:|2yR)l6i[N ڲr#5\v/\&%B(e._. IDAT3w.+ ib }[&M|M4LLLsLν;[=L`IHZ&{gll<0΃_&IP3}YjJK׮ʣxR2T)bG8v(c/l"7hWb0`$=x_!m61fBCؿw2/9+$F1V\xÿ0sh V7׹x".g%1bVm[FnD I-@JߥVPl{@Nv|Xm V3ۃ_da(I(XRɑ޷u9|[dmc1֩Cy,~%=} f&0ũ')HwйmǑ]y "UGːQJ#'n%˕!R 'g]fm ط #%>Iz'iw/*JF3wh20)Zx#pDK*Lb<{(KsR~!SC:%S8qI[vw<ڔN>qP6`m8F'*Cĉ%~gBJiw~((0m,$%/,U`N ~@tmuiElm(#y0( ՉAX`bjp(ITj!A&߄ :Ri_|i:<{1Ǧ0G  s?cڟ O1x@l'2Ny@%g*܀{N3<DH.ɐ x ds#~Ѫ4)3k4}I$Q*RshjΝ̉g8xǘ4Ti:wާ0$:Ϟkll'=&g8r_7Bx%HaȠ_t7J_g7Xf"BI{{4 e^k/Af2;[KBQ&'[w?½V@XU?޿G$̝isg"333^0LLS( b k\*(+=gNZF+N#I;j\+7qkF o퉀n{MO2:>F(Sx.> _h^"01B* *ShEB;U5Xq WBI֒bXٳ'ut[{,azJA8@c$tHkk5`5RN@'ܹu;p|vo^&@3V*Q08E7Qf8믴 ztfT0(JhIh!t$UԖ>I1|Esg+8DGGyyKI/걵ɓi7Vt&"l6)nc9=o}߁k y;+Ma%<B@ytfC؇ZlhtZ|q */~q4fww7΂FN!,LЉzccLLOP`~(Oauu7oq=66s^jxj8IiZbjz((b{{=6smӉlV&KÌ Ost:_>x8ruΨ]\EAïZkQܾwaaq{dkgRxxq^[[[e\v4ݏby^cDږM OqhHkT|2Zv=e#SkSY2>lojs.nom+UD&$ )W`tl$@zXPOÞ5A֒862Nlnn9 i4LOOSՈ#Gx>}uJR8㖝hbW_-Їʳx聭K\HeHp}aׯs:J)NZw%C7C/5a$+(g_֯zi222BZgWPcg!s>^342q9}YܼAX;y `PeGCX62B*;+6t-0TLLNq,JQ, 22*?+;u^261a35͞UF@ԺE0|C!)FkT5]fP>zc;Lxr<0b@{wv 'پ³t:RJwRM >b&G0ֆٹyF&u'7ߠi~v?˂U1 4cjcdS4w?RܟG!>aQ,Xu:$< V}:|zՀjN){0WZq~}=]9؁A$('9fNg'GŢKZ6SI}n >l5ʓ2n0h8xfFl911tm|g_]E6c>g;)#ib<|Y%$R{x[AZQ$/i( E ϝ}JXxy8ss34{h™h(%Jjl'TjVhHZT8I³g&kV̞w?Jb!RZޜ <:ʱ%[8իe-,0=1~ѱ1RF(82c>Bfa`{}?׳>@Q*[mAā HP:m{;M*dĠHg}V`cwοgN CH)}BH\fV !|/S6yש֪l5`tdbھb' AQP`"$J9VaITׯ_:z'$g9ɣK>?.Fzt J:o<pI=s\y5U7Hnݯ3zO\v̈́ǏSTr baxWXy*J@w^Y6HySi!R>*2 üsa)GE$q1#w6T#N"ꣿ/ؑAg,_QIbcN ircQ$iҗu;W240eϮ$9ۑig+ȡ֨C3Cl$9~$/xBࡵb%P#>oWfxT*(}˳Ӂlu05<# 32\Ç(T*J%K6Ά aQ PZ2,Reld'N++lyGb4 ώxea^w9 P GN\+uN #HZ/dgg334-QDףnNԉ[813me0a˜FptT| I+֑ !|j5Fsg! 8Nmﯟ`GRlll}{ ?C0= Zm$kﱶ;Ɗt>qBD*/txD f&Y]_ϡ܃*F82'lx )3Z%)Rl)ZUg2FGѣF go%C0w=c8¸ TxA{Η>ZHMY'*W=Z6|)`adA`013ny$<]& :&b>r5XM򲿰AٕsS7!T[2fC7M$^1y66~08nb&e^.)%F24TC:J Tm{V#R|{tI'plnnvH9DM%[,\re_`|]=•r1,//cevdܽۯ *e޴R`x"riǏ?vSP!#]< w,qTNZvsO0~-(| jW3S+CB^zeB.m5Y Ɛj46|䩝&k +sUJnhllAjFMF ZQɉNxSkph0/G!B ANRtz=:Q,h=^@8=%BD\;۴<^T>1U}t*K܃`=k vi~`H֒O8BbfeZ%LP,y,I6PHb^t:t;ږl\(,9z1;Mn߸;|Lm3rW9y9VGhhɳ* arLm5 Umoюb 5 JJ+7Z:j^ Ðֆ(";2z$._>͆ѣDŽCFF & wg?97o ##AhqR1::J#IbJZ DI604TqkKE(2hx/AϓxeK#V)n]ݮeJINT+ZQԳG E&s Ӭ7w[PBN8AGDNU.3$s\cB NǏe"G׿+/ٳ %=xHJn8n6"Zi`;xo;;;<|8N)5.ɒ2&,uoD0T:oۼwy!v  tgʡlRA0k9t)JB(Qlo?{7z b1t1Ejl$JaMJAwz;vׁfGpyhu=8h-aYYgfL *uUuU8 ɡLD$q1Wɬ"=HF1HѴ32՜魪6/DFd^={=<(QQd"n?(ܽw]nݹC_y &tȯ`ht)̓-VJ*4Nh斗.YtFIQe7x(*H ŢsRC$Mbv<[2 5V֎S`B$Ut#FPٝ֩ %)FNC26o<8iFf$\$HR "`vv׮o}JLGC׳nc!3=…팘$ n|_@&%NS=xH6. ^aM dAc|]  ԦfuQBmfb!tyqTa";Cvy+W[XD4QPVy=C]o[E$Iad&S8S7d>ޞ!Q-g}>ˏ:b\}p߳Ihuzz:ǦZew̑yY>G9%9³qY4[Wkyu.sM0_t{֎!r}N CgN$F FILZ-!5Ŭ`׊me*AUu="sӳT3Ԧs1--4b}juRL(%ۏmf)q!09܆rL<<'NSģ!X5yfWH!$Ib+qyteFa={>yeIP(0;Tyx0ggr:ƨgi6]ZsQS E}vcsBreo~78"Khþm̕?۬3]Y{kIԶ[F B\yK/_FM%GhmоG[o|' gfr6I8Ag0MHyj˴bˆ:_$Tm4+0st *)EbI2 N4!q^@!i⥗)Wk̎O'4Vq4MQ  Tj5>[ۻFBP9%??8K瘙" # aiRX\Zb ZfIo8IpGQȉGucW%J2BL'YG*{ޙ _|%-I95k nI"jP*?(FuS:*J)z]+w:33C~wglN3WVzSSS2CH\C2WDTY5B<l IbKMɈ^.<+V:ش1( Y;~g"ݸEaW^̷Wַ~XF͋寤GÆ0 y띷_f6dr٘ }sN/]nd *+?sZTjLQV)J:Ӥxu?,2?] X.--blR8hK'm([ N^Zb5! :QT rAru]>آ`2Z}'O1? K+,g A Co0ڊk=zݧe{llHi; #,?;`Aץighffw!MRiҮGֶ'LE. -KT4|G [Λ\mĉ쵨VkM,Pϕ%Rd%QciEbA!B@6hrGg$FR^8uJ)ĿOrkZ'dwi4v:t{=fX[ty)Rr#ш4)Tܹw0*UOӔ ,QFb \<:S"e`[`IGj4BPi LI=g VAҿ*rV-lmmaa8?N’:pZ[/U%Vk;w>cRZ+̼_P(β͛7QhMVDapFc#`{{E<͡hh4ÐNEJ%Z'pm ;C{﻾ M yJ}Y>qEq9I!'aj{G6MY,s(qbk_:<ЦN^?{)0(֚w΢V6 :$3O:( e]1Ncj"^]*ǽ 7W"@$nt{|,--cF\t׫g'NZAF mr^FT h 1 DFJ1/~w:R6vlS$& @:&no嬵M)K aks^wDZB <W\x&BnsMw?` @e9[/ҡIZkgׅV"/_ g]˗*e0u7Z]?(MȟRUJm+kɖњ@Jv&NUX(&5r "Imǒl5pb'ёw BUNz~[6yTb:V.) Eő˞n}!N8 +g{%S3o=0q} hW [BHN:B$5$ӧי_%DrH  aDָHK Po]PڇGwozrrjE@Wi. `4IHr^jb("`7{yΟ;G}f}ڝmYR>  \^9^9NQ(]Jj, Z'ܽsʕ+/)q:Dِ7 <88h2ڶjqrwBa ޯ/Ik0cxʋ0 )W*fx/$~^^{aFXQdx~ &a .`ɚ ,٥/yyu}\82:.r"#AmH׌+ B =ݻKZ4f2 m(GsW"ijp)+- ї=?+/ҋ(QDn=T#t6z>qpnl5HHR"s묞_czʓ6QyC uo>[>A:A@ԦixEjUKY~-j}vwgNcqn"+K[^azn^ l: a(0fb)+2؆Zu,hW 9:Ky cկPi"DhKs8),ABGh 4kq_sI[a%3Y[j}G;P^ͣkoxe.dZ!(sEj*6A)K ÉN(P ՀG`dLd{޻{ϽK&Ë@8BRQ M(@`0sO38ieg:1JzT!.zH"ba<G@XHa([ TX@mv,77vĦ m,][^[Rhh{_??`Mοx̒(,R"hiУ¥)+etEuۇVIXɳ\:'OlNee3N\c &UrELkF}wJq)0[0 o~a,Y?T*s-uTe]5'Aj{C0s=^|ϳ5 l/'WaooN9RY ?,niao-NdenXnۮvF۶FJJ (Nt[|/zӧ dI!{U!ATC@lb>!HvØ~?v\fύj lJkd4YKoID2`iq_xNKG3gpq^f%{ !*4.S#j`gnϏvZ+\8},Ũ;I34s3uB6via]0820 Ef G6?u$H:=xdm"D+/-$!%*/-N6=B12VPDl( |Q[=l4Vkrfr! G JA%BWo4EjY:%.th=˗QqJT<=ɣOLBAfBIONAُcHc6.׾Tw|Tυ֤IM󽞣#DtUfl4oIRaPz"S3S_(U+)YgqSq%8A\X,c$ajz2x7>\kdYfb  Ͼ۷$V\}F"""JN|{;Ib8(ʠ9p (!re**Jn4.\w\v]!Kj;ajJiJ%z0 $N #zӖAn; N7VHe4l6DBFPJ^*&&)n'N8D:@,/KIX}gd@7M?{ '1bT&66ZN8:oP8zdG#QT+x8hLTA.LssMgch[@p&&>MpHMNQ(LaT.- ~VsJ!I")(̚8l6z;vls< Pe̋Tff uEcww7>TT8qJ)nݺrw V~5^~ jdUdA8΍{TI1U >VpƱ55 >Qc3<'reLB9*KAs^!LP,& ϝ>s܂P,x,/Q,Hwnv0:6YʌW-̽~hd{X~euR7pI ~“wRX͕+oS[DCdMlwȼ1#X.1H"Q)4an2'JHF?h:QQ4C\1 q}#MӌѐA.0: (2Nܹs9'qR^ܹs\t@Z#MBH:tz#7^dyc9-\ȯ|y Hz|cOa&$Ɉ0 \5 fw-޷IF#>v">gU=@Rpyɪ~[V+hsyr,1Qe#l2vϜPzbE)EPOf{ RhjBR*"BRa>833CVby}VVa@btHH{O8hr bj? ;]d<ʂhIItPi{UWdRFR`ea2'N=RFLW(S3Zspp>`@ ͱ?賳K}f#nܾΠCl7 Lv|HoAN|NoRl=KR'+yr6o(sT*Y_ݯ_\Q_O4d`R>L>`rö(RVyIS¨mٙʕ*OwvJMuXt&IP(lzARezL 5V(9 v|9ӱdpT%.2g!f%l?P{(?!8veBFTht]y'LH*|׿uя~ģG2x! i>gЙխqBID CJ~1+oo?qu[h@ צUMt>J`;Wj\R*B:Ç~c&֧GVnnnlnJGrUl v\\z:`BVEKE!)v{7ʡ_d81Cgp^%y9 J@BpcR !ZH5`4,!NP[BJ5IRa$ NSJx1FR2B;eji ./?Gz* ߷u ^.Wv2)VJ #jibEcdWcx덷YX^6H2C?:" g)#eL!PX3P_>)M2rW3k@cEZ?wz FS)R(e8J&`JAb0< Ʈg)(X*QT1:aBa{b-Ns:u7^{^xh0I8~z!ˢvG0N4n`Ӟ$+%BZX L/U0Oo%]py͉ͭO a#;Sx[\8{O " gΜɓ'c_t53F# ]jH^x"*LbEOy);rϢcc>FҼݑ"20lNo1I S",DVUxb^KPV39ŸtHGl,`4:NVrgP;./][oehȍلS`#5Q v{oh0==C(}Ul4{. N ̞Qh#S֖5/Y*(R57nטXI 'ɻtU{:/_sB)mK[O9h>i=%)3~n8-#+IJ/cǙN3;3kkwhQ(DQ&7Vnu+v6c-S-GR, F@w#HZ;V^v_ ÁCE)%.//S N LgZCZ)[ε%86WgqbQ*Hfܿ{~L :4"Jfkj {楗^"(Oa6Hӄ᠇Nc* jRڞ|GXšxהd10@szaQ JGu >! kSG}E'~FS,B\H ܁6;ƹ;YJJaipFf(Q&\djQڐ3nb+OQ# YvQj$CTX)m滛1XX^82P Y}w8q X^\ԉ N_8˟ٸ`}pk5=5M\q` ƬlK#{GQF#YǽzfA`׬w> -,.ĉmN}xVU  M:R"Xn>+XUX qԈRjMMe{xK %'k@k}1QI}]a°% _x6i2`Oa4hc@20贳GӪv pՓ~uVe~~%+DHoXGq)aQF;,2kvf{<Bٽc~ӿo[HZΏ_[aڸUVYQ6\3sܩHb:2Ạ{QCkBDXdqaH8 nuAkziza$}|sy7µ6!P,0 i4HF֏aƀ~!f:.}k΃T  r׾{tcV '\wd"d_|)AqIJ3 ]~?y+cc6N`onz oE$Օ~׾&OvKxJQ#n^YZXVB9 N|cÆGSi2GO0xd pZ)Z[!`hx\Y\R$bI^~2/ri42br*{<~Rq*[k/f ʟF vGAZ7'uK9sa g+?'c;fz:n:zٺ)4  u"f`޿{!2`G]OT GV*Ry+A`ۇ(2R)w矒 \±UDVJʕ Ǐ^wfjxV َE֭Bp䗵&x}G8v[&t; `zf! `I59}4Ǐ\gϾ'套0Sӏ>PxW F7OMq En~qs/P(A؆Ԇb^B$#IJerd@ۺ־pf4M)!:T,dH~"Y3B!Sv:V' BPPv*q0HLZZ|ϧքaHP`4e%d:0)“VB)ER 1;vaR5=={׮2'tG[[[Ϡߋ/fy{1~,..U)b)|iF6FE>~WVJc{}O J牫XC2O2Xny;)ƀOx\zkkhR^d-MҎcN*WlEO*^&M1׮]w4a[X$(]VR yd!֖Y^?'X= ՙ:O⋗y l?zLg 66γp@T3 K5}~༪0 )9)K!!YT8 NԉqW瘙_`m}~3Q\y-BsV,q)7_nQXX:F\!XK/o[_e{-(rb!HrA A`o FFEڃxjDG"Mb0tA0]'ZWT:b/jPS.3ezy"syBT%=nwT5Vyͷywxt-:YZ&{20Y'9.^HuHP(jR.~\F}?Oَ;GvI5Mr;!ky7xf cW+ͮ׫>.$rO86+_]{/qp k8^sY' !w@5ϛg^ʿ'pu uԹ%Ę9E~ }yѪ5}u]!jo}[|781&(!4[{|_C}v(mb}?ڲ@VrmK ZR cӥ~njbꠗaXc3:q- J˅: |娧FXjeo(-8{Fν/xy JU\cޠH_vga+ >ߣ:dm8bqu#*Ib˗8{ŨL`B\3۸ӧzx?}S\|"A`u0{. YPjLd4֛LJgZ\DF,84\]IVWY[dfjZZmbLT(ᅬ "^|`["be~^}ӧΰv,XF!J%ԇJB0JFRΠo[~ X vT=GbtU?g66lhe 呄#V%q YAaׂ(` @2,>!n7|UDg>_?v3BHQd +ǎ['!Mg M|hk !`64Ξcu}wqM SG1L7d]Jo%1G:xndY`ЧP(Dӷa;$Nhrsd,c++2A;ѣgt,C>?X]ۿW ZiffXZYdǙؑ_0zۣ/^Yblkcrw0eu8y?ƶS O.Z<g/GQܵ帤`^cc>3zRoCf34X]_͢ `16#tw55&'㔍n33 vti ,?di-A nqsBђLj{I5@,R'-3nR"~$;ۛtvA 19Vk6z >Y6a8IBbaqZm .YPZ *r|}UNdyLfY[>c'6[;O8ynt:h4$Q6.̘g9ڴOi(A@"4jj铫. $t0R2tX$:4H-HA\f"ZNQ ( A@4!_XaP63`Ƚ1X@"XDxTj&:|tj^+Y,/c(%"9@(]6–h[(ou!8,׀Ђ's 3'1yfP[x*MO9~8sssR m )K)-a` dODה_d4 ߾'*UJ݁i+r!yHug5d?9B+-N#=/ ж$MѤiaMTb0ś2d?tOާy^}]fVVd*E[׸' --SU9%ffAIFӸ{)8l_@a(9vOkJ( lnnU?$IǏ2D:z*_kijB 2G;PTJ 5nݸ1YCX5Dܚ> M<!{^>pO [kJj!_tXZZqs .jMLMhݯ*/gxw5Z v {/+wsdAH8bQcI5zP/e//]^~p۽VJ*(( A fdy9x7n$ ;Jd"#3ƹvlmmqpx7j=A^.)ڣN̙s?Skk FQ0Jr&ŏ[=cki;_G+{yYyg9?xp_gݿ<~xj܂Lɀ\zLN#C 2EfAjc<՚8 9s _ݾ?)ƑV@&Mc{GPR!vtD={QHʾɑZ#Yp8"5"B[$dv\[h0ŭY<~+IBd3s,/-vBmf+aPH4Wpi68}u)EGnOØ XA^<׳:ڠk `%Ic5I\k5<]A_ھ(Ѩr3VT\R)lҗ(n~u*Cqm:7Z3==C\+Ogp_z筟{棿7>3Әjut50 nlln|~&>ZzfW߼B%h%cq,1;7`0`|ŠlɡpSaʝx!9 dj&{{{26L<aT5z@~]Y뚲&*d:H{1.]:๵lW1c25)aEёo:j;r7/8c]vkt]+-P81ԥRi\dg0:roZ[uFцh` 0uqBE+/>~zZs8C5dNЩ믾ɫ_{_{& c` %G.QppO-` ^`ggwNf vHS Zy urIpS)U{+/H!QT`0{g9Ugmfhzpt ++ G#ff4NZ) RM (cyMJY)ʼp~bp49*e_&sjvwn3 (epIQ@ fgRK;s'O114F[9z eQ!yScv:Ɂ0&*PnovT$69f%pW<~`0#&o+-bCcj|$|QtGC~u77ARu]|(=&3z^Oƻ!'rD}ݷ~(aGEsgXrpR:|v:t p#M JBD@UQ 6$JLHS EgSGsT>7͓dRga+in5r%n2GEq]9ML{<<ۿA)%c~jĉ^ywywY>q/n~1ʵ7'IHae8*旘Yٚf*Is)%>JNњWr5$5nA~EFg8j_o۷'zA2!8Bqxx]I}.r7|Up}>SSS r?\t\G%$qFsk\y|q\bq|Z6, $e8 ?=[b` ,LcCTp&Muܽ%_^Ϯ]_pM޹Kdvv60=~`cjYxOMe2(%$q`!5Sc-<6EJnǝG_|c??Zd@mKbZ4 zk݃.nRd(a̓O矢R8P=4ɻ茖FAtAv&IRR m q?QSo(5 RIޘkzjPT \NDFk#sT, 8bGZF [ڐX"ra:@"y>?'g3BfD܊d2+*(MsSpXaȇP%2^r¡7pVo u-9L F?/Ea&)"O"nέ("6 EjL! K̶=N;<,xӣkoy{=l61vwik\_J 6;|._mR9ٔ- (尿O:n1:%#wx׸-sOXZt1x6ṟ*0Fiʔr Is=jq"D·p`+#cۣfO>жDZL8[4It ώyfo -W|j(Ԝ=}CZj[9cVϓ'xW‹|79e;X }fl$oFֆEQLY6ɓyG-}=}L:(dq2a~!_}Q@D4IaeE2DF$BIPu*ޫ|͔njpcͪhRcR.2n(#cuJ~&8<ܣQ-S}%NrbZFQX"XZEgzd*+h[c HN$&CD')s%.A$}6Iᨈ`_?Y;=vFH1h<ʵ)jn;p]>~?up′r,1ڶ1`Hvs$ևa$78/RLڗ(7\%G&iHA~ WĤnzc}kʎ%VΟc~~^7a4,1-nQΣYp@F$_qpTS͜R"rƨ1_t@E)ې%;cG!CfwIdhc 2ǎVÜW|"J!CY,,,q('*%<""@0¹P!q!-D:7@a:*5] $UWŸ7 ]W^yrmLY1Pu|wӧDw;~;e֚fIRfggf=ZCT4TxHPZpy ܾ͉'jMM+)5>qa8bs)n7ori\8ϙ.q(ĉUCMNqI4ܽ{|3R@DFd?{hHVDG98.ehճ| 6 ݓcHVnN'g@2Ga?pEt_ɵe\w!h#s^cB{H.5?dݘ3[{Q…+/h6m?dҞܿ_އo'C&zxz%ɧf(D)TG8P\-5dşw\z} J.N^᫛M!Khzm\P~V1ցfDkL)+Mj4I&6JZ /^&?yBFQDYeoo!JTs,ze9 E[i 5hd)M"!*(K8FIH#*zV~PifgI$xCbDjAP0ecL5XX\\1ʓvQ.{u8"sZA?k[Or˯Оi Qh" =Ejcww dI!9#Ȅ3)&ger @ DEQbM)9EG)ghMMp_.\Ƕ؀A-8JI~Š"/L^<aHRSV' nXD[1%_&@Z2Q:(~]scNJWGU Τ8!2A6:'ۘ%2{(Acݿ޾],Cr@C0 8u>/SXҌKF/^" 0O+ٗ) biիQ$ȓ+%1ġ@9L7[O(U#Xi&!M\?dqqJCQJ4MPE6gGz\ĉ\&GJV|KZb~_^|\q vD(|ф"C&Vt:}._DV\JۯⳆvhH IMH۟9_\lLɬ5#³%QAMcmc Z64I$h@ <ױ% U|]ו=y$ЖDА1۽?,\㍧0 ĕL<3q"W| l~R4>|N>GZ3([6;<ưjF0+:lQǟ'$p)[$IBͶh)JduL: =o;b\YgO8gɌ4Ml1N,:b9x}E kIℝ]FyËfY)%znQ|4h(68ճ3!pكϓIF3V"N_)z(wڤX#Mk f{emm Q<|x-K^\h^׿u>|#''Nckkk"8L NԙDG4&k?\";8hw6rnGbbv;=k5I& nO>aogS'7k,/-[Iezm#)) G6&s6.G={ͥhMO'u rJUiJ/?hrjh'b;yϱq9gbm : ^t:.1ݚf`vM"t, rQl`iySq9޸6WOY^:NR#Ic8<8;!F'J>R٢bMaD.D9hPo4IZ &+88h#/F1^ x>iB ulccpKej8:I03 p"Q(( (Z$q5~/~4_8 (gq};6)WLsD6#~211A4"4)4 r@Lڹ6b{$QlbQ6U>aDhxNq_~筟EHҔAYG nO?#)n_'oؕ'.sض~Fdn Mg^#5Xt3cD{!w"z,k+|j_\(QV(%T;PŚ&EŬRLaR tjy0yeYd,fR1G pnGa^d3mOˑp6`LR)ؖM\W֤Zl6e6}cyr))![*789ummGH$\'k ‹myYu|4NREQ޻?q=(%;q ^^8_Z~|x)GN.u@a;(1ܺ{Ǟxt68(Nsץbn0X]^Os ; 1kkkHݮ18Z6PYsln^}!Ȇ6$El^@X?{,'N`W|oeEiޡV(wsk+_VNFPD6b2h{ Ѭ.LUnduP?G3 6{b:IiP H7ϠM8`\(pF`^>체6O'w;wy#Ƙ|FH^ϳ,C_XXvoA8`ksNN ʠS8{,_:J*W˶x+bH IDATOjʁhx~+W^L0?K+>a@QECx"OQ؜$-sEÌ9Wٚi[~.;Ke.;:V m`G"`!s}ݵSpGkH)69N!BQ*l ےZ -N\}a\ٹ9t>= zhP,cAe䰱<qsR9nǙ*`L[()~OFx*FN,k5Mvz*X(Ss:,jתeܒǝwJ>f hWsZiQ@|znNpm zl\S11mUt֤ ELH|HXcP C!&@2T@k1:NsH;1 hDd!m83+dZh4DdZJuTKm3[7,P` „0F'iPU Gyk(N 0>tfanh@&4[-4e{cZ7}u}.a8+2d31voeGψ?β"٘w.\9;z= :roL^\&sgo|aAP`Ϭ/GK em&)6R*tDNߔuDU.ܥ/tq\}ܩӔJ>ܣ?e연0BdlL F)+sb)Hh1?2uWK]wxpW.^f9l&̱O!] F9ʮ; 'щAT Bq Tۉ yjZ) *,)AH#]V&[JcScۡ:K2SǖN v[jE52Shj^/~+<}LI6qGI@RLmFBkpˋ8ƥ?Y>ʱ"ַG#$g0=3˅x>-[d8U>eL\b4^y͟8C۳c|\q 6[;Di y> p0~TQ&!q㎅AZsD%')CB9@!όG2Mwב<"*2{< N ^Q8踾ZJˢA)C'Tٸl9XCXE/_3l{>&8"b6 A-%eJVib/XB#u|q嚕8SֈL_@bv%#fiN#{NwW-84pT*ufRDlx=rŇViRK\&_""Ol3F>}&NccsZ;␭Sg'E$#q'O/]-j)Qbvp4$iBѐhkVќjr vC u1&!#˼Ϩd9NR.}$&1k[5!]$BUlxwJ)5X@ZXmYVUAREVϏ,6ԡjM33LH1ν /\민?du$J9lllșFԱ\Pl:rx8p0% S@Dܹsŕ/.iI"4_|pI:$&!᰽ұjt>8.a"z^\,AE[}U/~*G"( cң2W |Ddh[w, J-T g\@VC"I5&nX%EM؟7[b}[<饓;N9Cj6^_{Ζp~m6N2*ǥZx;|07?Gq`4B9֤Bc4!`K)\D` _҅K:ATra16#ΰ\.4Yz8fx*`ig|@G,|en:ŋ/o({{T+e<ϥl t]<! m?}ִmQg[\3)fW+xo 0.E8N}ȃak69rV筯֚0W59 G[< fQT>Py MsO\ZLAVG+0f1 (x'ׇ׭`d$ڊb5Jͻ6p;Ic;1.c cR?67&%I4>GRT R\KRÐOQ\.jF#粿1'O0=MW m)ۉ6xCkh8LJ siN5ۄAHTMxOԜ[9'q{9?=y^g&}ftvvFazpZYeRZS.*I 2ܳ後 iX\\+/3?3OBړ'xÃ=n0 d{h4䫛M1ELR0??tW9K^g?kfZӬa{o_a0;4"bRRlkI23;cU$O* pl9z6{{QdS:cK6A%$qp=81J$`5=gCH+ܕ`ygq<.I3gSSP~4Y1DA.AĉUJtu{n4;/g*b_7wq%,h56 a᠏먉iYW $xo+GP^:Ja#R샮UR72޵h>j˗/FALMY`V'6Xl(aHv6GKFtMFrszB _#[O ]vM353Gb1գp1HP#SXSuf*4!u}I35$!-S$p҄$'#%vw:)Y*llIC&M"+W^ay8[[$IHPR{n\c&~?'yGgoo`4$Ib " 0/2L3EkFҔ`W_]77+4jUN9ÏGz=> ('So &lcZ 8A2DnOJy@)ϋQqU _~QηĮ&-;''_zy p=?|㺌zll 1P6#,@&?$ sx ̂)J >RTO> gΝ1[>}TQK!_^ojptFa0^kt2H6"gbowwyp;[<l;YP170JJ'r]4}>,蜬Gd)Jy!/CiJx C 3jM۲X% j@hm \g_ov!q֟x@QĉU۾d7C{@?(ժ<|ƃ'YX#(b8`NevvN0`[.ywA $iG3( /_ Aa0D0#[oNMBץRrmJ"W*jՙ,֟'&)jתee:)|%j-=j4r96:"JmԧtZȎڙpPxL2:ee~/۷EgP6E}cpG/s%(Gt{]pDߡ՚`<< .Qqs]$HZ c[_]_B?fnzehp2}?ɭ[7X^<W "7Vw*!Rl9(=[h<}M*3 uY=BCcea_DWؑGl\S2T&1a鵏y>q)^x՜(3 ] 2=R"U,,,p}fu,ꇣ Zӭ}ո|d$ВҌB\,g1xTMx5"A0MBEX>JQ$%S455h8?Rm&eҔ7nWg(-fg wfsw;$Q$匩&KKv;v)%@͒=$6hץ[.1FPgߵ[mmӘ1ՔA9n\[\pM )@牒!_,M-Gd@)8y$O*_#7c9%2Y3Uԧۢ=QOmDkŐ -JQ.|_{/^a"6are޼zut:k Ԥln0JLkzJARáfRDK08NX[{Nɘ3y~N:IdnGۡ53C^* 򵬿{ϣCo8G$1wP2`G)q2%6՚'Op/וv(*Jٶ=.hl̽iBq]ORs\qQ`kTvMflo1?@RT72ѲIq䨂˖lTqb:FHػIr8xs'_F'J_z'ϰOBbqUr3HolgsiN6MWx N)\svO}fu[,_|o_} 8{"_ ydrs,r('_kqU*&Rϕh0lZ΃5. o??|/ob ^d9ISVgaq^@$ɯ Wx ~Dpc4Z#5PSE;0xxQ!-(پԤ4ʾR/VG^k$(Wxp8ȳ] vpds>!R ܻu$ur%G9… (q=!,xIEs t%ά5{AeK{*=Qx_Kc!uac,%Ζ2vIp\pE:x}p8bo8!j)kuJ~$NPDvb"U.qߕJeXY>A5&2θϾW~[ :T% 5"0^-~A0`])c IS*)4V WWD{z;qq"$pԎa7^g,)N9>*r33A$H[$&eHc+&ν \OկȤN( W{'pef0zlbnO$LzvB`syzQ"ެn-Y"#L20MqPV*‘s5m,ړ@~ǜqWB!o~nOxe!iTIgC$$@!(ah۔J ~{d52c>g/?SRJip>Gth\ߛVh47?&w^_Ωh6$ILf8RVZ[BVEaoQǁ8;?'fǒ:-rs0<}cKK4M<5բRCΘ%n'yxxŗ[oq" HL\;hܖ׾54e0XwaX[[m圕UA˓q8$eb) IDAT2D)g8hqRcq.eZJ˹w C \$MsCܗ2%E@䇩TV Eu ^ m37Pʀ>{{DvFu9x01#@jQ( }Hy8 lnmJ\a=]8%4U;\QPʳSfZF>q<іpR]n߼_1C\7>U17r R=6! {E㘵5._gyuŇ:Pu]߿<:baZѣGGuBr7^eqq+dcGkk(WLҌ':Zƛo|3r)^*o\}W_-ff;iʱc<..AiaaIz=KLv|4M*J~Fc+IƤ̴dXp.ym ncR%gߡR/$jAk 㶴b@/^`||jLFLR=Pνß0,M/_"v ڒ Wv*KZ\f~nFshZO i6qBptpȇ? woܤ Ȕ޻dJe%a^]s&E!vxn Hn"lolМڋ/wIKƛ:}LFXٴ|̳^zq]ܹ(Cs]uyvfuuߧ6ͦQm:|x@ONVϱlk(+Dy Q]l4Fm2IR)4.\ ]NQ*E鑞!>|w5f;dU t[ÐfsaG SAzsPiiVSZ@Eh! |mIxx)}.^B51:ҠVNeFul9v4$ʣ?yW^#!_|9RsڔA9FE& Ed<t:?tr珧 yqQ[q!MV)x_gnvN=T2 vAG͔ b2,kB61g/DiM>DgQ"33=c )`ȥ+Wx7qV2ېRVN? Q4̍ۀUs=<{eQCz>ސ$XZ]#T͘"Mya/2/;hW6x*Km#I$#:?JD<|tnL$IDF3+X&(1@iyy]Aqu:.re4:ݷ絗^c9ŕx].ϗVL E(VyBI( RCFc /7@\sy*+ LNM'o:#m ~)iDFA)z^aa@Β6ru6JV&w+~q}|Y JZ#Jn_~A0`0>ⵆ3 & e(T035ͅ WX=ss箰r6WϠ@2綸wUO= }@"##[ngQtیRh5SKBű?Ry@XH Oyi*WOsZ8(3ͲQ)4Kя"G͝M!S<}F" Ԛw֯36>uDA!_߸I$ pl=NI: {i"tc# 瘚X[;O% )K+gÊݬ|4.4M}<Ν;366KRRu?| *ajO GyRzxd:dÄӧN3S)Ba~WUwvR I bAAX*iV@uv(9/)M)vM _vGu,O?cey1wa@Ѱ^fl}5&iZq|^ݮjmJtRJԩSS$ıhku <ן+eP߲4!]5℗.]5Q1Q/.]246Yg& ʳΡjm`q1w,tvh4HӔV$SOL2[*^&"Gc5~ыzY0\sΟ?ϗ_~J2׮>sϽˏCnئTҥK,n0ۥ1:$xW'1Y`ZId&9"$Zx=°bS4ՔJ)%r=.HS֐G|[H`@E42OYYL5BS[fv {02>HcPl.A#)pkXt-#<8 _ޭQsG8G6) ׃}ֹ̞5~s,?9dzݢ #D moII?X^^fvn66 )} ϶7lvU=O"B*H QFe V+{BٔlRJN&ʴ !%*顕fb|Fm$M;h^g(f _KОCI}HRmd3]Qg)RaUe ifo9oOki穩)}]>sı&Io*MOT!kaa,KږI #AzjsnySU4c 9gάIBMU0!H3]Ni3[^,PH2kصҴZ;Vϰ-nܼIRatt]VdidL,_%U9i9%!ɷ;t:\=\y}De&OiJbq{v Ϟg T#4'&xJcv D"ض7>i^Rҙ^mr <%1#c4G'[bjjaI2#!-HMtdnqi!(Gy)/֞&TFYP~=)%GGGt:cxy_+G,$Lp[|8|8bafR>^7ҕKfiB@S$>o&ok QP9IvȒJh¾ئtŘ6Ҍ$H$ΐҤ&JHo(A.c/K6]V^fu~(fWj0]p㔖OLss RǩA6R2S^X,?bI!aJ3:Cߺcippy}V6FX\g /wfe./'d;w9T/1;1hcI&'oNd}pDdZ@i~9JD^fY^Bny?Io99YDz8eNEݧ{J8OK |?6O|֥\OB$%kkkT*A̫/Yx1AJt?[O1JT0~24J=:&YF@qP֚$8x3S&U%t;JYG^a ^lm=jAXr߀8J9u4gVۤI 97om R87F\~{Hf'ýػ>inA^ ^\(M*{{{ QѷG+$N 4Mrg5r3c.^2 z}t_Tw׿;;@zI$#QIbY~T8"=rkr*7n`}u`?D1nH(!$jhJhJ\[^lo&lJf^gikvvw 9h3JN5??bkcaOsh`oҦ' CVlw 8zN*Sa`GGLLO{ݻܽ{hw:(F}>i;gh8ßZBRM2| Q"D' #\ Ð[}q@ӽ_>5tr2CnuAc>\y͘7g?Y)?|߷t}|8IX\X`ggG|u+0y4@/6e{MLK8AB8RUdY kT48tQF1 Bؖ^H}d+[5YlK9ǝ7g.&!BT`#8"uv6>ZpΎnx!w:VӔUZCvvq>T?89p/@HcN1&U=adOLXUCe\6%JL4y57˩SvHUd XLEDfp 02\<5&H`/TUXDJGݏ|#z-ܺeO g.>G6b7"Ee9O8g ת{99xbޗQWY IDATP,tr.Lϔ>}}'0iAjrʨFYV&7yue)jJJ!NbψIn5m4?ڇ{h܋//yJ0T0 ɓG2(I+жdϲhDiN 4&PPڪ9j/%S52;w &{|/too[9ZcH8' ڊѤZR6Lw:Rw9HG|QHĦd<'5YZ:|ޜX&n;i~OkNyxWk+f'!d8]MDY4Kٸ~c6wafnjADfkm Q ӼVazrisZR<0BG%Kk#CЊh`&@ x/{">4~,MVh Q&E;a+pB292$_kNZaks;7@۱1PF |K2r!)9,O)CJ3M :I0굫 o٧`84-Y^[Z Ȉ!Bڂi9,%I3M,AhpʴΌ㤬r!< ?!d˯s]aϵ/ʫ061Ɲt;t{]L%)B9R0ةa`y/F^Wk|R-Lyf]M#ܛZ箰|4B #tDŽ/=J~G $6;ﲲD{4a;FX#lI/ZN= aV[2+"L|S7Cccxa&3N8afEߍ45Iu.Ge2t òO0:lv<$<nճwGЦs1Nz!}4(Ŕ44-{QaZ77Ph j-( 3Z-GOBj*>/_cfv>/~I"SGhɴ6nꔄ͘kóS%H R* i+cꨈ!wCy0K,Y&I!AID6f/{A,'gRu=7[Hh5HWX];8w7'Yx{|s`Ak(b)Jh"C3 ֐I3D'Q|'a^#QQ<OzLrej#|spx'MocbQ;6j|v{=jZ}#fts׮w[HhkRF!$Nɉ)\J0֭[E+ab|ȈHuZ^'F)jn˃ɒs/n?8Zts&*(`2;nج(hM>}1Hԣ4g\(+Y])vyg٤9I+i4MsNÍL5Xc_p՗€-(bffm^T** ݣIHM%n})O |t[-]buc2GK![h_'H3|yz(J%𝌚621/,s2QiVJdko>ZsUTU6e'JQԩ5T*5nM(YI[]4Q[Uysl(*ZgD񐣣Cz>~X^oV\tWU*40̠d֖lpMag~S39\ާʶ7uRA׋RTMRR xSl bȘZ^z?Ѓn{w1==5'f'4 By B>1Ҩ:fܴ.$QPJo[vs4Z9Raff ;A7֘ZZOj4ki, r%k:Nh1)Δ!wzzL199óQR%:ڕ6"o!LwvNiKsVO216vkdVWB t2kmbO !8}4t{=2Lꎂ cy}VOAXzn>1Zhʝ7zN0:nT3HnH1AhJ|rttRzT^S>_p^}v "cq5p 8?A5LeB :^]ff~Xq[la$~ _ݸA3R` X63Ow;FRx SӓO:]Š YfcH Xaj>0i{w\t_|^wpL`Ơ7> 4PZkԈd:3Mhrn>ݽ-~g? _} i72Fy );6ӓSœ1]cr[[[];6g)ٸէ2;)ye;O;! &'r/2昞1 *[j@i!4KyAд֪ȧ 4*p usI/?f~v.ÿ*EPTH),)4`g'fI:aJ}$ ] 4jww!W7Mš!igl1z+PfffNwp7VD箽L6­7>h=$Z7id]Į"PJIGa7fRNq/Q"<íO 9BsBb7HgKץ?*q\XF^6'O# 3'c|{ˑW lmmjrE^}:aҜԩ3 /V{R "SJky'ܿ9l#>/2Ч>2(sIL'hm6Wڤ2߿ǭ7tZm&gh6tIXI:+uQ)4aŤv-^H/C6~ybœ%B㛿 >csr( @xݝ-7@O 8eTk#}ꂌ eάgy431FX`~bQ';l# I fM["DwnnC% n^ߔ~q(2zF0 ;m%_?c0R0h/~!{n aj8TlS/uMnE  W{}xv/B+EM)Egd0QD5az290QtSJ͝[' x=?__/ޭ/Ax]^;pD"a33;~,Nt)edkc6]*$I4Q@9,2כM03)6N ָiQƽGk#"s5BSR<mik*%Ȕ`c!WWYX:2LLM<;;;L3;``z2&{7xW1cg?ogvv(Il<ܠl6  3 ֎E5m>q!}r,` ^pxxHٳ5LjӄJh-ZUCڌF'^P󪬜^Q_%D҃N/n~͓'txA'J)8vxX9!p1>wn`2R6K8Iv{tGh9lDLM " :Φ/?˛7GR-_,/$RA9-!=C0:ke2yNZJs=|_btl 4Z^^&"?~/JwJ$c 'ª9@45=<`LBT}Csdt{]j:͉qj/45U1IE],\wO"BC8>JhaX%4+g8:E)!8f/MAtշ5ʮSiԣ3x_翠?8n Ꭻm-Gf. CmmT5=47竍Y{|ߓϽhOimn"g?ʙ0 ҼzDqDR)".} 8:@DX/L6 lQZf4ȥM㈒!Sn5 Üx'C6wpxJ%VqwήcYy~9 8NҚO%2eճU-/SEji#$k\:/?: 3LO̲x9J:g~J/A.hLס^hiBS#㝥1RGTV٤ل MMHi澐Gu);nqx{~Q0$HdU*UQh2ar/ܺ6;[˗9w cM&ǚ̳4":9jwtBrvm5I[;7~D0Nq FGG V((*XSiff6dfrU3պqqA^' 0ʕc7e$fݻϝ>+^mΟFiJ/w[C :D븝*kH"d~Ѵ IDATLw \8kWKN̰k\^!т\ TB2mz.7;{ww>;wns64[ Cݴ#ϓ'y~n(e6َϸ{n&esNoy \t{8)JVN !QzH(8j杻A$CDKVEEgM|BǛtFg~ @&x9x=D(ӴrbۥZvTp/ov ܋׹x㓳looUJ0|Ԣ5K>Ɠ=3x˫o; * |122qfxj5aLLL$B7j~Df;S.Se?`9OCp<?eo#YvιKgVVfe־WMd".i6I0_ ahIH쥚]{9FDVK/]˩rs=3K+e:AhSv}&g^gIavqA>k$V~J3:9<i_ =4Bk/#ym6v6X]~ЊB4]kE>ۀwaKV^iG1E%A8#G#`Id6hš@x`If]g4u!|&D.>C$XCpR*)U;u:q5z26_k<>J9XZsmgOϗ48J׎fr|O = ^-cijIVgu}fMy<xŗT*#r߿cs׎իW9RHnܸ$cL06RX, .?/Fݒ\nYqL,:vEu"1zR!~J|᰺:z-gI7?){{hEt]>zH1Z8zjZN/豳Gp z\.o6|sqޤ\|ϒf}{z%M2 v 9cMrJL }&h,HkHǥ(3`3P($ZYA)͈,riP+j,.,PHPVy7pg6059K"|ʅ2FAц_c {F!΄E} Bj:vqD<Ɨ^Q7itj*|b<.܅xyzs`rrG^+\|25Iq X(R* ɱ)Wz&cSĮOҳ=(,I+cVC%$ 1B*~.;ѓlͬG'4 Z,RRRe O!?r؍Gӱcj uZ֤>oB:HG1dJ嘜iuĉхcW'ڈo cDh[aem7G4|_~_˯s=\״b¨#s pl'ZQȐ,@3&hL#8K!04M)f ݫ ¼6 I[R=rnVK؋\SR–`ҽv(+7HH!\hr(Bz.'7QfÑ׮af˗/6mGflӵ@}P03FCN=(r_'>LJ;DAG$V;êk2t-y8ᤶ(tZs^fPZk!׀w\?4u5v1m[\:s' ΓF_z'<~xeiĉLল݌=ڴg{ 5H9ʥ(zbJ'5J4AD cRLh@[ teD}l0XLY/:KK<[VÝesmzݶLV10Ҟ\”ɤv]!HA{dK9q:k+|G<}nz(Ͻ"I2:F< /…ˆYq.ݨ7u=(s̟<}GlnrT1HEf "{POжo鰵EA@8.=[,@ (Ҭ82ڢ@P9pQ'˗3}(###h`gg1ftdčfj툝{?.ss'YYY5+t?Hs<8faaYVj>k_z137O<5FFg:1Cwkk3j\q_T |N-p&*q/2aIZQP. Y``%IY%Qʔmk~m;bA¢Y:I(JgҍGW~ǏX8;_hu;(t쾔g Qi6;TݧVk016nmLJL˽HH?}f}E!P)XBXKRmmo%C~sOܦݬChjGU ,he 19)ݴXH׌yhL;_-LQLrrv093kU%eǦgi'N!/ *驷5-FZE )FHfNiV΀N$)%N 'I- +§^ƳĽ(rAC% b8?S^.]Ν;C[)X+67@HR !%q<%`UIJ &OV}@E \8H;RȾ&Wadd{촑~+}>۶!VvxpOV;x 9IFёQ}Rtz=j#r̉opΥKhCe"%B؈ qԗMތOlͿT]]k/P z.nο7\^ZbMρ'L$!Z iN?"& 4k;qZ l" q?wnMrIM&x9OW˞u{_%# F(YϤOdpcS';./| q '(K%FFFbfpY1ɒ;ۭt6^O} mPBx(#>sҙ+̂ZHZju}z9JE| Nv?o=I./ sV4')˸3xBZM{5w&J1G ڍv}bm 9cRae`koX@xFz^1a}󧙗ikvn:C^SL'&C3:ZDF D({L9a +@9J v{e=gM K!A䘜]bU!c1t;!nDPb||\!G [8C@7ѣca@:ڟ?6?Ri]GV233N!QϲQ PDq1 (9.1~~YdÒB4M2t\xd_ }/笿l % k*#T0|78 x!ͷd|z8\~y@Ui`U((dq7dA!sHRY?G5MDaff.]y|Gh\cedLaA; OèRLE\8}!~ mC6^}U:=3 R=_Az1BR_5$tMr9N-buy/`-$ʮT @S 9v\ k=1K>gcs?˿aiiGtL(_&8)J^[oE2/t~F) .)3XCr ,:Eqa:+?o(=7塃 q fƎ2<3(Z.2nHcA&TbcT[D8ckkvAݵ%8K"[ccd_o(tM"Z vG MAʠKyӠ`p rRY!3˄nС8'qmLHs4E@^An7`~~eEAhz-TuCX2:2FT"A`1Ѭq.^~GWS@8ZPb`iD#b0d \j0v3P*np[yxu-x)/a.a^[R!?D%(X{[&ڛ;ߠR AKN7@zf}ȉq0CZzܾ ?E>gqiؽNKma1i{cc#l"Tk47gy׸r:zNG#m@iXl!.(k5~' Vk7Q;Ph IDATLRJ^KrP[đLNM/ \w}Ʒכ;5lGNR1T(R㻿Rm=AU vCtaIM/hevz@A@lQ!`]Q!nz!c6wC~Υ{.^ 3N#E٢<2:QG}*# `lr<cU)Pʕv<'' cV*xGAڦR;YO8uvwu6hҾǞˈf$Dqv:ú?v*!Iz|ܾ1ٙyjMG>EO(E͜nu`JP´x q /̉YJ)bm41F4k(W@ RllFP!Nt`X +4xQ8W@ * ئ Yw!8 ߔRLMM119E,,"/053aF|Cj88LL0 s\KK67&KDtȕ ōg(y 2\B.9H4ZUnr1s~jO2wr( AN_AMB-Yt[mrK2J`9I#fOil V4C>iM{i1bfL$6)Ξ=kRhD"JS={kD"?g^^GK6jls.?Gc]^P]gsy$2sblrH@'t{!9?Кju8Ü$s;{$Ks?qťBA2!(ЄFQ @b]*auu ߸ /_xjs ,Dg)xNF'1V>%u(FvM%z(e}cmp#GU$9gj|aE8{6Z)0n mQ"&zMF $-sC6qe:Wn@qC(p]:qnz\ 7DjpF%R* B.~6Qh̙T4H,/XXs4 YTpK J>n޳>#&xNO昛q"4]۩[%>}bjHhYbb^mGYkӀbX ;Rd_=HSTsnݸfX%<= ~7F :xwqv7A  s\QloǷVbPfBf ¡225/֩ҨFlp_'LLtpı'1f8l#]IRfdBO:f \G: F??tHV+3 Gܼw;㋯E3 DƟ"3]^.$mQOKRY 387p s ' /P+%v&Z&1;VsG8¹?- 7o8fQ:Tlrn@+.SS3|孯rEDQhӴ-ܽ=gMxQ,^"Nn䓇yh6б0}/ 1261a`$+oiLJB>'Od||b#P4'юAŲk&NL5 }Aciij4ϼ&]σ2@&ܸ~k׮#VUODmpuʥ2خtD0Al3Pt:\Cbq^_G!(G12:zx@iѬP,s9Ʀx:c㴻=b#@qFGi4[$2G8JP?n؃3Fg[G)Zg1W:stt ZkQkgb/#ctVFdh~sD'ۥjQf7r rV+ %/q'Ore* nQ bZopާ7U@.]ް%S3 € c4c=d2`(]&\x^z^(RM[W>̢zN=HwGFz` <{!3)ܿ n?Cq-4})!LW{0Bs |KoW׾mbiUJ(3yΜ9oޞu03lzA!_@+ŗ^ejtr+l g>(2H-y_f9fgO2>>I>_dcsg5yV*/lw^yKK0RBqqqťEVQ:B 99(E.Q##J)2#Q&0H-;5 -bYla!%Z-S@.V[)zʭP0==vȾje8_gϞ(L~몯J>/^uA0>>J|.ȷ=rʖ%YvxHݠi195Y.]y'IG$(ΜHSTtW`Ouo㓓,,-MJַ{ρEwl܏\p)%n)$V41|gwOG U5p!-ӪG:|J siR"CD% \߻T,.Q!f'OWrtm`)?W|t=*#)Das?Ȭ.MIGǵm QW&}Oz4JkA b`nnVJ' #‘x.N5#(vɥh)eЁ-a"#B ަ^3::dDNngЊ# @Dqlas]w"0V=_xg/31=G$aku5B 0[[T&(KHd ,4l4Z mH|`hC7 3g3iG֘f(iHS-mC hg9*a!K3zAMikW 瀾B*[V:.ZY\1=?O>`m{ 7q2O?FŕSuAG 5 9BШשZf!lf lFJϾDKMy|Dj&f lmoă)jۧZ;blrڔ$=S  [Zr\X<AñM VğN.$Z gjOtT`3KEժKt]"]2^/8jPR[$Ƭr0 ?#- ̟{w80۟dq'Gc/- Vء_:syGGbx!췇)efa5M_0-xw׾6gLnZM (I"T&G<5Lő2t:U|F(X_B%9"pyS1%uIG]uQydsBq' V=w8{,/&e 1E#gϞEg ^ty@ڴ-ġو wB:F:Vk2o:<~"4hlI2a|zD% LRC$T*H fcAp0^!Gζ"Tc64R+p\#v77yMR-<3q?SJTX,(Ά[J- i 7?_''9.,? YH6+#)0- el?lt U9{Ί9th^'FE<-H1:1O@37]fbtr>G"DRaKG(*^W(O ȱK077ϓU F)2j(2XX !2Xqp t5uQ(u<^zEv!VaCdOImqAvPQ@J\lànǦڞ;ae{\J?882:R*rRj㓗Tԥu(j:0Is~,sk I2жd7&PejєE ?Ɩ %p^~vv3M go0mT %}6> rFclKE\ף26N&膜\<"RGd62R(5=|p *M 1~ha刭tຒB.52hM؋x)+k{z i[8IGZE1KI:Z17@"_.g]@yK^ؖ8Bg- al}^[AKDgBjb+t( [[xtN#3XkJ2z}o!9?@]\W$Yɺ F)6jۈ|1Էg}iBD!0jC4J~PJeюL|IĬmltf'+yS+&))KQ_$Q-)AAD,?^YkFRuP̛p[;:B%aAkhm$䘓5"S5u! -pCqDt > _}o};4[M:.(# G{ dpL>/?g~q1s4XD窒>Z }s'H8vMO;@%n R.E,..ZVPΝfY(_UZ[Z<~.en\( iwZhex*[[<~-ELh tSgxŗR4,ɉi&'Jxn FcU&9ccSl! %F W'YXZedLd jI#]~˿_7OQ2!$u1Lb)M[RBT$#ЄQ-$4ĴGnBЃicﳳ͟ng'3DŶ@ku9wIFJ(A%P]--[-C>MubFIӥi7#$fOR(yz͒kMb>aK|'1NIz5gTlt]W^c2SGHK7h') tΛҝP#L-i|/079Dhy)<E*cLOL1=1 s Ju•Q8bkS`+m dD298=QnTF)H*!70W's,. \v/uN>G7x*-O̳p$fuur!ckn<[z]]bQ*X':jsxPǩ6?YMMZX=)EQV:cO nSQaK{@j0 Cj/*ܤP0;;9NsjBlvrㆁ[lREy#h4АNi{دA|Zi(U4~汱129AC BԊFQcffWH7ÈRLG?m9s2'Y;ZƶtS)h*;) * IDAT{L [!3~2 ̇gI-ӐB%%&h`U7Wx|S*% 09y,XAh:;DҠx-J@7?tiCRу͑,S=#C:{8X,_CJIXgCSC4&S=yӕ'|_4 >"W Lϑhlt#3 a߱3/Pvfstt/~|p,E.Z\9S)9==MŬ<^ cS 4^73$0 H[TJ/h6\Ϣ]?Io&e`R.C=]#hvqӚ}c& |1Oe|ufl'˗X%ZZ k$:gBP,zXKL 'uV"+0֌ښy`0<6lC6<6<1YVObj"YV9snqd]I߷k]{IF?5:p!5-N̤zsv)d3MN B\a^)FR=xbٮ/,ubmt<68 q@'G&XF[]7衕Y;`V1i6[_3ie~NKlųT_ի>}FA/4ΞgBdGiG0v8Z3>>|!͑d=f2xAJvpm_+KH2cFv*mT.uiTݻ4[r9)_Gy-3ᵗ^N.dla!IV(􇯱Id=m]VqJ xOûTڦ^/wy ㈃}#G+4j$q\]ZՏQDbksݝ=fN쾳Ep߿yqDdl| a㺜:u__!QAo7|TRVjlnn:n_C:Fk+M2;4?A))S̗((ʔ efgs{83QZi- 'm)({|駌X[%}vM_:~[hjllllH_T2lQGda<'/ӼsV*ҙ |׾7~۴c}7 0J `FI׳aeL~̤`S>|oI)*aQ|>?8kciBvp<ϧr1:a̵_λoƽ(&Q<:|`6qbĥ81D \?㱷[hUk(&;XM52^*o*fύn7A鸴fQ ?Z֗lɅ^[;-8+:cǒ2״Rj^B Z#Q4&Rf㹌T"P`MHq)\̽{wB2@%Hmj/XrnE$qr 6aȧ~$Bnϯu( qlTxp[aپ-p%^|*v] KtuloY]{@u켵d@7 )54NwZ6Ej蓎 3Rj/179c1\B@>P,q{gO+$Ȱ+#`]Zݨ9FFq}yX2ҩe&V L+B69HmG:l$ OR.q"mlJ&c/ LjM3fSmڲG.Vb` P?<8@z^ң<~,$C+GDqaffJiwXV&7ͦKTZA*G*3KFy>v7,qby O<6 a@6gziiOa #MWs2+++8'j@&VP0qa_y/I`~v?9~?mC2|HQTT&ha,(J"p|1cHuxc+큆FIZ's+tÀ^R);.\fB3>=aGm X,qΝۄQ0Э! 2799H N>= [!$ǎ-iq;mΜ=˹ X9uݽ-G Lc(ฦ0c+Ӄܹzz](q\˹1\ e9@FkN1q+*@J U5`pʬtrd~2Ҡ2|7(ע29tb@H€Vhu*u\ϣsx/#r<~( Ndasf=hp3>|ȝGs&Q( $њvVHb#&* l@._ zce:^v7V*zJzÿSe<9MEVa( =@gA/0@(Ni Ѹ6 #C(qg] ԪHpKbYґ$Q]z:tw+ dr.B@FJ%#ch$` "Z Ʀ&Q+y>sl-!Q$ P4M78s[[[iȸGibѦV^#ߐ 3}cD5~-os\6K!g&BtRItb*^ϒ4Z *V,qh$*ݪC=$T@I@`}l,$v@#"g.Ӎ]Jyg_}?x1< ~.ftTCCQc]0m֋'Saw6_\~Xejjs/095rhGEpXxgfiiD8:vYsP%LzMVU){k#ǒ~ޕc+ܹ@<ܼÇ88M2H:yb^٦E?ujA7CnSlxpQԦQ*cm؎oGlJ$N σk7\MmM.:tTLF:8aLMa@NgBku3Kɒ<3kۣ.^xA6}9С-ky>Zk=zDKԮ?~z{r*ngbjTGkswM!~u/Q> Zi*,-,'s AU,b>/ S+Q$Z#F#Ӡxd0G˖!,S )͌R yI`-Hiīq>,GÇU2nnf5:1 ),Au9s%I.=y%N<{f( o#1#m;?ȴwwQlZ7-4"6,th.h#<}@4~cHryN:S,p}O>A1j)J%6K?z]L2wqJ岕Op!41z5WϤYZSq~KS ^J8Vox8SV56{HPWiq 첵[7wN/)| GF:󹜣Q^hQFbz*gTv +R|֘|fnx i)_6& \5wж|mmnΝܹy(N(Cf` ER\گSqe\f8 p]kڝqnҊ+~ }9)qlaY8f ?0JbFBS+ }5<ߟNM5 |SBSOFF^ckwfoQt\l7Yn9斖kaxqb jV~n^qeq:++8GǔeF&Ǒg%#ßnhpHH{% p28LO8s +*m9<'M4kTU}N8`p<%3 $Z XB?%cP: }~ry(5HERi5;8CI)>U$1~0=?11I!B\Lh:rA@!Q"J94 a/![(4AKOg$\r˗X!Xq #m/Ic||L@!7B4aR(~UN}4${!j.Qxט0Άxd<|ӓ,,,\6 5OS$z>k'Nql I('aw )G5ܫǮI###J%W,*CA,K 2 fH/<`㢒$ 鵛wy>wBis$9|0$Ǐ3==}# M~֩[|{_% :pw%Z'H lHIѤިQ >wT&L҂ݽ,ǎ_<>N04M"0L1WDw~=B1nPdif&~Q 7M233C#G333vij޾e ݨ˃ܾsZc bh*bckBRͻwh]y%Mp:πQDt$48ѬŁ8xRuq$ "L4O"ϠUbY>҈\r`iJ_z7g?#r+e 7 IDAT|+xbFad))I(|[gS:ʲ6LQ xxxdP#Bvcyot:Dq+ ?&7 )vJ:*y =m*+d?p3\.{{P!4m4 t'h'׍.W?IVWtFRB!ӊA726n6{#Ǔ!~4J7t{t vmW{bX7l6 Q j'+W-Dq  #p4_Mk(F9{i.?S#t6VɑY?Cvv7[\_=iku] @ I= ')Qyoo$Ïl21atnfgfr)}1;|5$;$q KKK_j ?uzܸqv>LO^V8rr{gƿdH+HBñ3WRKpglٳOˈ ً<9 (powL6c?훡Z 'jGc Ib:5r,-c:C"3ss< 8ڨC޹ Bf{{_ks=30R*}P*pOY}nAG?s\ N% Z̝?!ıyM/``J+7ujNbrP(8B8ezn=Ē;^ǀi1C3щ1հ2s ,,/&ϒJa9~u8顛n5^?l[$)fa 22?0cxzsZA._Ceo 9w-\Fl0:>֚ m4E2$EE13sxG^7DJaZm 0`ca$1zqf2(ҵ>B1O/Mql%@HL|qhx9v9$\l.ǣ Z)`U*MI&m{L XU'!M Q4u)~߰.rMsl,gN]fi~R, [<ZJ>>^:.cɇzniU>nm5cLϓ$~V*Ѩgm߽My.?זHtW_|.="v ad S@*j=L=1gK8"8|S,;q "1US YWm҇k3x߿vC/PGٴ6_ :.MƬmgcs(2h`Q] ꅄQD߈Qj:Z8 ^E$"<;8co3Y>Nl}:aO~A+!Ɯ<{d?#rH6#JAtcDQChQGF?%]HTBZmj*lO:T]\6[= hloSet B!KX23y f'Ǚgaz3-gqgDi}.<$FRȨ5Y =ǭpڱƗ!&劧J|$&ir`tFWk\zYn.1gݧP,.ZH67tz[6˯3==J9nA"3[EF=vf_XHZzz0Zl.Iu #%9( d}L ]>hZB5~KW>7N/⣏6/43FxP(VH CbznJ6w;4czSp%BUNi-c<3鱉3cc &''iZ,?c)Q.ae@& # EZHRދJ9<diKvHܴv;$?ω(W?}*RFDзvk֯8t:(xy7ܿ0Xo1S 9#6ng7*_]ڬ鵨i6fAAbMR%5ewk'^)^kyŒHT`x&ƌR] pl6 is sRauL֞$`x_s"ᅨyt;e/mCbNuB:?CE:>Qr2|lu%R YX0RF) 8CRHݹk6#PB߇2W!a窇ՀІpJRQsgI!H!$<#%4L&CXb}}U?TҤMA[~fX:j81>qL$ѱ[ gVY+'o=seUN:ヒZ!*)adyvxd fCHn>Z_RkA#Os?^m+4{M~Ҋ[oʳ#H瞸K/Fjj10kTD#ݝ=<`ngPxNz¥K,18iC=|3kTg\G~9BPoZLOM1:2b&e$Og}3=,4^AxB cZsq7\\__嗾KWW-zq@i$t7n_砲G7G)|y8Ҵ1mj:bVԏ ݐ_8Q&瘟\* ۛQ}}4Q } sE1ZS^C2ÿ8AԩLOMԔGFZ9uI:aail''k˵a8EXS<>7oSϲFwϒ$RjT6w։*|Gerfx6#626A' qj Z>OJ{b;i%,Ֆ+$Rsai333T7.Nb7iԪ<riN:O"_.321h6 ?X(E1Qd &&&hZI?CqBP l6MUm!#DѨt&IB٤lIi}אR25:en{woq6a7bfvVsE3JH6i3Gk4q#2}eCm{"Ii S>֦MohQ.xڦѨc&!0-(xNPQI#*kkt16ٝnC>7~EGg9<}ݸ nlHԅr?Zccs'\&rpP_C.Riگ~y⩧eGBby{M K4/=+sWy2N Btŋ~[Kqխ x\L7J1R*o?ə)&&Ўރ"l4x3yOG z럲tl|6\F@+n-<:f&677%e[xp:Bkz.j^Gۚ Ӂ!qqQ($xjvVtm`UhqJ7*W9q#p.ɸ>bE^diiq9bҍ揯Ɗ8չW [qLh98 +ǎsS' B6 k҇09Oπ2‰?qRE)J/KWJom1 4T?obfE36<4 V 2~the']Ima0[#2pp:2O+^ R&HLf*Qos*v5sx FⓟSra`EMd3>F}z66b$E_~w^HX?Gswjm!I:m3m{Ij="sSL191Ei|vcs&̖IضA:J&QwjlEidZE ֕뺜>sջ=~JJdp]rD~hA FcpH#p\L>fsd|Gk|q:;ۄQ1K 7S`|jӧ2>6橧_'9`jf?5cQnpu]ܻMQC[UL cA:f GJbV5`K 9 ZAc(%\SJ 7I !g4ӨU)Jc 8d2\pB^sh4kkka҃q82AP\}*#$|z"ऐ(#f &ցK %)CP=mb7L(rHmt(d2BKHęg`}m q} "Ɯ _'yƢq]VvAZ&HJ+~o- 8ڼ聎v[w> i4ٌO(5+ #܌}BHjr?!)ZL?~/$aW6b90Bcol4 ˌNMsC A[[xSxt=eѦ7Fsba#c"0$q\$RԊx <~*=||J"f@ȶy;tp"}21&F(pTL82?Oj9s`0R.i[("{4Zu $)d=vM'#}^z*r?;[}ٴ)˔%y!v|u `$m<@pg,N~1{Dq`A8 ,.,}H 7(52X(r|$V] /ky~rfpE wp6qrxǽ;7*de43x? КSMRҩ$Rm$!c7?RaV$9w N<ϭ[6s&V+/ʳWYgzry1:[B1:t ityfDÜD!W@Ak%\L.^d|tzj`~sq ʼ8WvDQD 2.?dCp%n9AC`u/&cZ2I296/hͭ{MYEG$  g,"&Nzh |\V&ɓ*@JG鴸{.cccGFRrM5lN$4 7%S#+!ª16:ӗ kj~/gmmf*h{Gy]@l-člhzV/VcP2I=H9wcy!AC.1lv),..R7Nn>n|i&.Q/$CZ64 ޿NϙOso_8峄I@P*t +4: ۥnB|eiiFEF@ S?uSO]fviȄ?O&z Z'4Jq\Mt¢T p IDAT'QuTvTbm4ڈhe RE"HKpLl! P*zQ?jk\ #!I(0huZ&RJ1RR#}L>G.s=$)ܬ # {QkV~07C qoSv!0j}i2m鴹u&B qO6|#D'a$Rƽ#] A>B_x&#,Nϱ8,P׭"@u] $Ѷbv6o&ssKeQmEH"޿6vpi{#9qb/xn}Iق1yRN6؏j|+&&z'S mL2i] o:n}@.ܧyp@N<|$ܼy{ Jc?Jߤ4{/\w9s#I?G֚Q&OgN ҲlUh^Bccc}ads,87?@iC))w߻!:9,CN>SxMέs7XswnB,/R5HNSnFJYVU0dis~ TVX,hVh}(J B}( QB'?{nlFmNA"g1>Z[[[%PV\(-//SOl6vs0Vop9xm=#pUncc}*B,6~ws\awOGs@#+Òa\syY:v\***^k e>g_~j5oq$\0 #M3tEiVc,,uy,cuV1I3ko9z]eY|Yo?+\X훨Pp65( hM'gTm7Q1CpRYE0"ݜ=`s.*H.>by [llFEFw5?$"wMA&.TN,2^crcP ?C QXeIh %SQSW6?4ynC*G9X}4M:^te TFC??2w[{PP㇪d#·KkMA?$ |)9FHk,ͦ^XLtQzd:^=" cpjJ Ŏ uR@qk4LLLcgȿyT*C1NAJ?|}!ŘgِS`%0`0 ܺSO=˗qְA4KuTqNZ*S< Ji6A}U'kCӥRkONNr3gPD(g=d^7:Rޔ}vwwFKZ4Z9ILǃ[;cc?:Zc39>z-cGz OYY_)ul\{[i14PcP!y&5om vb|EVVV8jܳ2ˇ7>;9/ʳYU/,rjnw}X앱mr"T5t:2_'#_pkDC@ ǠBqSq7)Na{l9xxF롬<,ZL88>a ǻblabcCmtC߷E쥭sB sQx_EFlm30y7ǯ5('ȥ̪$@HҞSy˜>}o+r#Lz}vZ·9۷ֱHX+ g$8gN1v@ҏyYX?swpNJ=sW?%{#N?XC,ZgYAO.rqEv$G4`@jQ t{a@! AcL6Nu>k(1'ϝڨ,@ZpT(dT֙b@$U%.m!tqiSD{IJPywzP^1 163eDѿ'q.[[[Z㥔^3J70 bOJ0Vr AT!ISސo(h̩s`) Ct B1ΕKsXOV'U 5rOa:~#VVWO8bEqxΝ8+_bbs'9w.]1=6A|YxnuS<uD1&NJf_5utz]vCԲvo=´1. 㓍KoTxr_Co0Byv;!sQ /*R%]E\|c?ðnY`HSn{y~EY (ъ8Dy)./\EKz ԁ}(d0@9ˢ4ⵓQDG ؔ,Νppp@֠VZq)\tql7ISZʕE1 9[eYIte,?y4Q바.5t2Waw^D:M^#w1bnD* C]h6diV&EY,M%@pʊ@4bKe- M "ㆠ+~n'_`AV!ֶ}XyKWYZ8EcR| V<]:I8ɭEgS+8`KG,/;;[,qrj;wJѻ19"ȓlmlv~X'YڠiaߧVr>⣏>7oRQ;w! TtۣwR#@+8a=C\x(T{#DwHh59:g^BO(Td(AsCbUR6?pM|R(:VRF}x0g vSu~sptگ 8p* 53|ϳ*+jaM@ i)]Ms`%JF/iSOlkΞ9GJ,'[X`og 4KpN6z^<ʐ$)x,̟err3<<̳_as{' A"s/?lq f𠃜~/Vbl]^xG8:8DY cSTjU]R{HhsE@:AyOzt;]\AAW*X'`܋XD}3P!c(pr/[#?;?tY(|`(J9vDZpnȄUJe /+B3DQDjr-Sxa+OJmm39!~cqasZ"e9q\j~OCy*Chͭu\֡}-' È0V7knlmm177Ǚ3gĄ++] hP0ee9Ali_-RO2+3~ DE~IsiixhJ2AHZǺ G"uPRfWdjr8dzq4a+~S>3sVʪL;]#"zUJq\9uJokUEiAHnr"\up/+SynX_`l:Z@$c .^:Ǐ~C~֏DYFU1 eq%HxsΑiQV_{'p<8dJaPD\f txƻo)&ӔVϽyBXEe[[[pHh 6pzM <}n>=.KKA0տkMΞjPHQg񉾿c=')˂(2氭3cyJ֊1 QNp@UxO!`B% WJ}[k[?;F9QC2rК+7}-k[La`ʋKkޘ[SO@ybTfffzJBky)(kZ4) ^GcT*$allX xQg\k=XGMKK YFqr4JL^;JJ ~MGRR PoŢ( LMMs})'?Z J)NHt8sb1VWW/#Oޢky-aHa4Yn?*_jt \Zgyy8^Zf)WFqz[-2Lk墮jloo4M/L9n}7d|!,K888b||8|"H>*=+kk>*2bHtY? Q?3衕fq~O=$kܾ4ʹ  UY;w82A|\ ˾" |bQkqJ (dQbmN(v kpXYn25a3a@%7$1}Kc>9ﱹ ],jyRiS+ IDATO=Zcp /j JQk?;B'%v@Xq~N&L6j"pҧOٹ 8 +B9xe ܹsd,+|CYhɭHEDZ(ne &-,uV\~lMA 5@yk0 #QzwES$NozZbK^GGG(095|ѠnJU?_6@+qI9{XYk >z`:^IЊã(Y@d.V(%AXDqL &K>y\-a.0ȍ%С|VV{&S5S"'G,ybrgy]VWiw9"a= 0P=n{]ˁR9|k]Z-uZ%_uV4ppd#Nge,#97b FX1 2Z]A -9_)?894|Ke$468C,h ƢPZ9Nq >gWNea0\b^@3 ActpY^X>s>FY*Qȥ%4Pg_`iyn}HZCWEhc]}}#78l(-n߹a h7z8A /SK7ťKWqƱbx (5#f {˜ ^z#B^?Yw=W(D13VA5Zgyyo|} 0^0p֑&n?!iڡ^kk_GY833OWny@KkrGƇt[)Jk(Fhwgx3ȝXYOf{UZK,vv c^<tn(L+ߒpT o˯ȥ9g]fSSܾ[%WXv5ZkgCDx%;7`Rv& ΞҪ?+Vh$.pGZ{ `QUǁg053+O+Ad@\ }+_ʕ"!)i#Uc<`l +*horf]'IB뗗l$>}4~OXW#h4|$ CZ#r# ?K/xc,/gl|f"c"-SIӔ,Ls $<>4wgJ :.c Kg3ZcB6TB5.I0H|BVFRQ0i9<<=_vOΠř\t\TgDDAmJdc,,t;[Q\d3hbh3,-.l61F(e)_qNETXDa8 @A&*ՠp=s-- &sZ.5eX{<[=^ov63F&jb 2/#~`!)͘J{WZ" vm~a{prДDy> rp Rr26/֗z09Y [[[4i9בx Qѱ+`G D@I̓InH!ӳf7]czzާoSƤY0?i*NKclPV {aw;{G<m_֯qi6V@C%^?'#O·?=dCaYv,FI(U kSԂ yW&FXhw|p:?~QG_CC.u+NkD:"PVXUΖɉ4ze]Ia P d@DF=1HJifT(=.6V` blݻAIrÙr:+bxj:333dY&BVPZ0֖d0ȥ1GG}NZf_Ye`3zk%ciw8|I4dBvB;K_F̢Pݮ#I*C@DnccNx( kA*a(dHU(^(YwSZ.Ica1+On=v.6ϱ58E>[V;(__x4'bXX#3 vbjj~O %Ѫddynl)MZ9KV{}L1ryNq+8hT k6R#b3\h0HkB P+;+P?{9߽W%6Aa2X$]Jy_'"q#6T:W>+·~?ƺ>hCf Ib ,'1vQgJT)M%kAI 8pC(ZuL@). .g:&~? UtWk_7xWȺF҈Vh$Ak{Ջiv[Lc4:\z1扃t֖ P wאּ#~E^hHA:({)P KQ ]A*T o+pz9dx4s%R,"ɞ"T8'FA#^TzӪxxVxg4(<9Y܅ hlP  x|Ƣ=$X(Q3X|sFY#˹˗_^"-tgs۷oPTk & ϜGfyNDAĠbHBX'hVғnATj5gO 0m>5 eь$h1$mt )0`4\>#Bk+iXJ%qHeRStQKJj 8 Z6'#) v$@䡭ǟ_2k4-qy7};wIiU2Nyցi@(m > &Ĭ)gh1q)R1zG Gr=Vonc]\);ُeEP]cϿ"sK$αxN?CU+LLLP+mF~E~k_k54',ذU8ZŐ5V4[.kx֔'W#@CV9R5FE?:N9TFt&7džx7ɉIUqRqUJ6ppwm{yuȝe6vhɲDFnq6E;G=q9|`Y-<bii/eկsYܐepBٌX(66V7 Z$iųUk4 y}p6gow&WRrx,9gaB?Ja_54o3>9AސX9[;$I 9x'/$}7vJg9;sߤVo NZutDDAIHBz%\jX_YΝ;8Uk>-UpP%($cw6w>U&&iL31=^WC1+Olo?A9er(.)ﰿ^JZH;yJ` #,.foo J!~E& Co3^Z+z ;#CE_!fYIEs'@P#nx؏u˟mE jp~g[^C) N[,`~FcjTdC&&QD j \` Q c#:#IzFBz/Q_|xAR-`0?)AcSW΂(~Gܸq5+=}TF\\%04Is сMU|(gs; ܱRQy= .^c/\g^>sE5Ae T^xϽe\@^νۤ^ m_I$Ra\"/ު fzzJ@93v]ޕpM&&&"H^ۯiwgS`z٢< 1(ɀ11d}(*x+3h<^} JEA,M{|8z//6" " 19EF^0Y]]ه8Pa2ˠ?ypH% Y]J+eK s"s/]<ř܋iDc׾>ʫ|~4[e&~>ܕ*ЦNʰ!t?㽟}.]?c"X"2/L%ea>V Hl_W)0V I2>>VD-}@@3AMKBEayJJX z^.%wrz.Sϳ/ƜKH%ix91|Q."(ĉt{]!= Μ0i1omQU\ct. Xu;wyvww}3YatmW R"2:!38 2kW0zq:eU*U?dh u=>lZLzuD`,ޤ5t;HsggD37#ȏs3m v:l{\&{nn(qi:Km&M?K9qj}LNY<ʍ:"D(klN:1s{)ʼn# (W\1hHMfRzY:+bzEWjl$-;h4:KWQpxtx{ߏZkj ~䕗爫OTlǙK,:sWxSϜ`1M%u@([9@CVaUZj%R OsyZmZ8JU* =ZCv7gkU]{Iz.n:A5+L8a3q6}^ !GUl4cqQTx_+#` [; .2pmݻ2VϼH>O?4NnW /  R€1\n8h6I211.],ajrZS>vpjSQBRǨ2U=Ŧ,z)f4GuHR,Z1s.^Ba\F|8 T*qJ<١VWqxz yѵ#Vȥ:*,D=2e.]}?)^z \t>zouѺ@>Fx$I$TxCJo#fYM+ƅʃ\hR…( K~wٳ7ֿŽ}.Ot$YҎj=t;=?1Q+,C3(?yXY٤?X߼"6f }?PvWk@9x/0=>xuq8h9f"4gC^}LOpx#CuYs䰪X\ `2֩w[CFҳ ^X0?E/嬏_!,)/̓垫g\*wnYI+/h?qZRՏ/?[A` ݀`,V4 4. [2nԋYg1y5)x IDAT$W!A-S%_`tEƖյ5+J{` :T)!p8e%kG\E'KR$;%86}6{v9&hR¦{}{ĕ8@X_|YGUaיcbfY j<#K,S3h 92I9#UFe6Xpl*p*aHc=*8 %sĭ-PO>8gϝOl=D5E+S(HEIR1o,3{'c ܑ$);ġb$F Ŧ,TU_?25Qp! aP)ST _ژ?>oRE١SI <6<_"eP!":"jԫcm]Y>u??`/}X#=Xm6{0YMwq/.?ڋ5&G2Hj&³_%\f*))~(gS_]yosΝosD@ $E#4);R4ENر%';+YA>D`(Yi8@ꪮު;y|{o\/W]U=gw> ַv)W ͲoE++đh'굏FjN '?KQɹi,ғ=F(;l'8AFQfwwg q E$`@E8tqC~JDԅ.~עxfIJAZF*lE~ʡI<3*-`w{׿Mt?#}Wy ^BI T# }ҵCjR͡*2^KF#?:$'p\{NR1ZlFէt55j.qrqrP<ıH#}N[ z.qЊlnn!]A ROaxe+U٬~\^ ޽d[Z33=ëʋ/D4[{Rߠ;kܻuqe:}v$IHR2QIt:]cs"ͽ]8q25H,ȑEAҥ@;C8~;#'Nrx 6R9 Jj. ѮR*hkTVmA8TTBXg>}@+ycL&V':D Znz|ٗ(*t8Hng|헾6>rٸUU`mlײq'T'TJ E7_杫looX_36,E$q2=kmGJjiY ;\} B"R+p < / + 0(|M)M0TZ#N9~q\Nw$ ($N4H8ʳϼ'goM\BKk*DQz"]\+sM?t lϼF Cqp5ΆXe豴Heeu  !G]hpw,Kگ&En1p4TkM'=zY677i4wPV`nvSFLAG=r( nÕkWX֚nW0ĝ-2UI#T3^a+gKZS.ys)j\yVklI:\91SD v~\Jl+PivHS$8+p,j$bjBX(Údi4՜9{`?r"?y X&N ϫ;w os{~qz)~ifffr$Irb^8΅20{ʴG}iLMMq!-mq559zH{h^lnӱ_{=5X~Zr ֶ`9Xq`ݎȒvr^$Di{k.̑H&_ƍK$D 3«_…'QiDLN3ࡣ+uOso~0 BMǑF~AI>b!0#p<6ܧч7~_<gK ;dm6S?oX"'imgٖEaksA!̨QD$E& z=#Aqd03k4[> u/#NL1xG\z6knS(HmF|1~7k^~W_c=Bh:#]}GZmGS8q!jU;憽lfg<""3nm -4$ϝZ5,ZzZ[㷌BIB^kƼ& QŔN>FPr= d( G]?Rq㋼ۼhwV=xjFAg?C[6!z!A ?x;k7zON+H Zi^y ( ZkN8t] s _ߢ?FE.k9p*ջ}:]M@)v177C?HDa )JETJeۯ!wi7^]=WVTeͽm],%ȁo./w:5(0 ,DɳFL5!`[/w1[Cdȟjȷ6S_AzCo>lZ4 vwwCnbb{8sx8 kM@i3wq^K={``00hj !ksO0 T B2$f'CTLrPVs_317`!~_\\DJAױc}~!\JqkO'G>=$b48-ڢ:Yu$,Kg 9BT(]Rf~)$kQt{-z>')^Df}ͦQrFG\" PU&X>pNEsE1/gܹ+O`' 4%:^t3ta'@GRZ3>1,!I<@u`t]Rӏ6=ennN3.Ct;]'gHq8th*^z4ISmdcKiPW<ٗ8Y1ۛH&KYtMq6E3WZ% sѱ)9ÿX,8"? !q07@+ВZu +PTp;M+BH$iBjESe]LuHa1P.i\7Y3ެ "efzOo/F孿 b=gu6[;LK8$ѩҘG$6p9đ&M$I؃Ґ PJr1(bIc'"J0$W-fV^#U BU\ף\?#U_ {-04ܡUM_3y&IdA~JUhZ#ұ h*DB)xhTΆ{}::@P4AluB \17s_{ Cŗ^fs{{H 9-ҥA>qN>E`xW9tbXg=I,J4, ,qʒR"O=( R8p,'(W_C:~{.Q8.IlLNz z-LbijgCa4r/K3N4ήAt~ZB\%ckj|>E|4z} 9huvP!fu0/H|34 6772$-{) Ədȗ}z;;hĈ c"mVU)푪? RK%\iUI4pj˓oJM(!\?D"iZhGPG!R -*\*zKW.%>aGOsi\ȱc'mbD\0}.dZ|sie$5dT3&+5Z4MEg#JJGOB+~W:,/Oوbd+xn~_Wzƞ5M==RgU7^gfnwn33;CVO~@BȄJ^=M$  [K|>Cz4[RR*U8qa\ĉؼ[y3V7ێIC^,Ka.ð $Nc$v$H|;UMREbFŲ# 4< *Q`fn((fיQ4푥%ب*{棰cg~nvg7=vHU G q$328Uv'h4siɢ0\PV&U;AcM6RVر#ATEYghu;avwȡ<b(9y!N?r͍ n L g0.6_rj&1ꦆzCVu oѰw?ڢC2-xh-!YEl\ԔZ(~?x."tZmCkږ\TkAcIczKK~2s}:kQ /3qLE~9C #G͡@69Lc{(W[=^~ B ^4oEF葇?[(JV (RLIW"H0E"EJ4Q<%@ TƭrvfnKɎ K6J>ւW"AaH&Ql^HI;R&qCc@i%mc8O>NzBx>B pPJ)X(#HA88rxqHXDQh EQo2ӍQd] "Y`7fSQVRRq:Dl ulU2ayE{)9#뵙\?_^y/y >qС!b$i ~=)qmpq2]511bq0y.VYe(9 G-qЎ "p4QBTqA&ok+QYܣbO?!vM~m\4N8P7ߣu}CW@JQ*8|07o4ZU(RvFڤ>EWfBcǎrJ'f8Fאf͡8.FʊIa':̈́d_fwq:e*gTY^);*KMcoA#X_ܹs4 q099(0#8x/ AtIuDoc4&6FJ}$5}.=H{4٥23`DX^]%Ĝ?4o|嗸p3|ss/)lu,`h0I;w~17AFzY&h(JR$6UǕ %t;LNLPP&&&@z.A.RHU+FLۿ1$&pen n:7w ^jJ q\iJA%lSf$0 455eS$QDsKٽESlaTjE@~_ p=Btb8`fz9Zt*)NzO<7[TȠo`s5: .+|헾W5V?x۷}A?nr\>I]ǥV 7(+ǃr10 NxW2lVy_TJAZennHVcAU=ȮR*pe!-G$E8fI'8r8q4Fm2D̬+ {Q3Q3QVy$*ϕz$ؠ ʢg;zL35vQibr$40maAƁoLLLהI<45m<"ȑ#w`,mVHuicZFi0r*>~P RGCur=Ճ(Wj @hP1kܿo<3U3 O{ [^ui?k_˱QaƳo6-JIcø IDATk,8?7rEM}'}QW.}[:N{}-B?`ffT)5=N??o(w^qL+(Ν9Ͻ{th Ftf{{kC!v}B?!2R$czb܋?oGLN_tH뷹vcyt4 ӧ㫯ŋzxk1` o~K.Zcw~ :6fJb=l}!ݶDL4A'إFA3|mkC[5PrG>4[{Se21I"75*U4 >q=9ED<|헿ιOu уNḱxn?z暵QZ9g &Y700N9/)*R^}nG`vFZմ K'4D^V 6GܹqHסR⹙ q !=$g0T*Q(8MS+lfdM,(k-0b!z`P0y)xIJ133W,#oѕNVz2~@qıcy'y%$9zoݿՌHȁY_$ } n*YY^4cg8/wԔ*G1Q(4y7/_kܻu}&jR*,facHr޽5]?~w1 IRs>|B&Mv~SSSCۡt: T*'>NǑrSAt{}݀[wEF&&&V % lolr>;tO}nBdD(AN(Y3vCO)&wN:5=H~Ib+Ks45n2,YTgJ)ܑQᒼ: n~uFCEt;\ǵ ءݧZ)^`&>O8NoP*;$%wʃsϫqbDIwIud|m1"=VsS(#%-5$JH:Iib!}lSm"Kal+56y`҂}tmF2 `b`ԀnLXnin5ѩFjԒrpA33;m͇ *m$|_zMT#XIegI@tL"%B[^"GH&ȧƊY)cy&_p]!7Js/NǞ:fJn޸ɝ;=ƏFrD!miÑhRNܺs]w\<{}^||/r-7[;0lZIl2 ). --f6qSKs!pn=>5*ssAR4 [)&6m'3aI-ZDvAڝ;$q ڳA%! C{lߥ\+m1$kS}]iJSҊ{nst&j]Ȉ%^DeAjV^|vw/̸c hPY8WH˜'D:.I0UR)lSc~LN—EO-~rT(☽f{8@: -4S`2^A+WVq%6;w6ƷvdLhz6sk+>[h|(!@)$|B"X a_Y~WoqhXB]y@{"|$EVDp`L_p"њb,HP BH&&~W^L⬯(HiY긎)=KBdFjcnAG-D1|j2@ d\NZ ^Hu:3LLH=Shd* %HJ]=[;$ik1zFEVs4Z/Ш4ܥR t6yezSͩzmg+0Ë?Ssӄ"E\  ,,, HN9,:cf$AAo(;C2W̒J1d~! #FNƬѕw}fg¡\XߐNfL]ilos5 `~aPE!>S ~\.ט֑"E Jc 6pIS_|Sk#hiz_9zCt~z'aHCfىqMA~$2>L$J8sG(~&5?T.t7=Ĭͺ9r$3T<8`\6}[+f_M:~kׯArXj*ٮ6Еr/Cqj.yW׌ ɉW~RKt'`X->Y8~EWrQJkffgkvqlg.l)HM je#7f<Ԑ0pI(zY "OH_oӟsص'hҥG4s8tz^i3;tZAQ[R&5X_C&@J|oq{U3$ oq.]~bȍ#\$1a?D&ua:AzsH'Os1nC3@8C##jLaDIp` D*ˈ/:LN_}.l6uImu; vP Unzeg : -rgmZq0/CasMТzMcH^pL&]VxB]1E1#)K{.B ,"LFi@f09w"/nJؼWpH!pϒ5^%I¼M ̦E|pA:9u1γIʕ rp++J%Q6(SbNVrLs <(;63KT % ʢhsv`q ۨ#Ka˦U Cɇ2UB`gتmz]qH 4P8gX]9CGY]>?Mjׄ9r$sI~9S,^(!6^@,9rya X)TNu vV" Rc :~;o{@o +㶇&W:7 [^ѣGB~⢩*2Ji3zcF<5|}.y4kf6ȑt{sPrh&Mc0TN|ꩧ@nc>QEG5(ld  I 3s=v.]Dc%zZܜXЧj7"^7^fe(%c jpM &q@`zp³.r9$=*BdNtz=8hהu 3;;:V"LZpg"<:8=v7p Efp\[ %~j~v]\@ٚX\\4hwٹ ! ҰʅƭۄqL>A1 FCz.9]N~?d3DS`|> łqT@633C|;EBjɿRD1I9AR&U)ZrDLTmmcss^|[t( pSTtDJ 7 M" e&f\k~؋+r)N{%{ N>r O> \rhce>VA 6U,GbG$%b^`IZMȁ#lެqsL@<1ό߇ `lcc=']VSS6i(y6[Zm:߼ݛ7(T*xJhCL#t)"GɧRj5>YZ\$U0)>x87tm9/`evy{.aa&j3,呓27ġC9Yy!TJC{9̍@¡OM=R0qq9RD式qFٹYկ{,Yтz}Ͽ=O=z%ZmZW9)eoSZ!Vťep)ҙ'm!c VMb|TtIT9'E)khq]8}T@ĦfT|FV6~dЯҺ[ߙyP( eVC5Bf'?9_`8;dz\KCw:~DIDso hmB~g#H LF_x %EC#2;=M\q\n޾ah:d-Ѡ|s٨*{=Mt0 h-VxC,.,PTYd~+Ki>Ȍb(*e81hIX6#RE1G# S=ϭk7g.< 4mn\ yg_|+_J<4!茞gR0}N>ͩӧsqxAJ`7P~L(RعamFHEa-_ /ӟ׾ݻ(~Ig.<ϋ{>_||Ko坷S.~EclF@dUœ8sR:m;2N%%Ip9Im*q2CJ{`ԶZ!I{MfT$Nq#s`ezKsLNLG}z ˮsνuӓf0  Q( ReI\$e,ڲlY[^k{UUXQb 9 a9Mqν5Ӄ~~7-"(t`(f-جeCrtIv(8 8J5F)*7.s3NSO>/!˗ߠۿ;|Oy vߢ8 Q 38ա1>+cci\c[ yoNvqn.]޹͕kWxq}Qd tvAEQG+*b}k˭[Dqg4<gYvט7Qi AiwE]dSl*-dxx܆ș}vA͛Q< D60D4]6 ɞrx~G C3uL\fskz='Nɧя<¹ggs:i@͙A]f8|-eST AJӋ3L/s<*CABݦhpwe-48o" AEѤ')ipVX8t;-: v]tjB~%?S*TAXXuF@JAGܼuw{0\C_%{"os(h36tJ(x7s5 CS Ը h5t;m[d I!llllsC;~rLCK$_dzfJ\R.0>1s'0j5-Rp tI€N˫lom\xQaNˈF͝mZaw|Wo$x; ]C[qD^Qۢ۱mBGxvJ? .mr=g$͌(4^Pq*M0 >%8Ľ${&n*h4+fs)} ZVI91DCarbj՘YwV !sN Ub&Ss: $/lS G1AxIyHi4'~m!%-8ilmq5v8zN{lm R{Vo?sQ*a|j5[;;@8fD(RPp%~a/~ׯxVe3•le>E]FybrNC*F&33Y*^sE)wōP,9KAIOQR$1i u=cs3,bCr/d#fe r٨L^Vwh!vpe^Vg0Aư/&\:t??RE@.!QV(m Z~WVͿyHPu- oڿS֫\|n(OLYDJJ"$.Jy퐐 -"Ugȑ}^{5z݈BA8,pQ>8~,WٳgIT[3o~n43p;#>[2LsyƱC($t̗8| $ I% 0^@gm{b i*0FBV N^G3::g>77yhwHGQp$LH(qpzaݚ{liQ#p-Y 1 Mm>%mr'7%˴ T5tJ'1J ~X1;NA{%SqqܸP1NΡtĝ,-,q=}ǥ_;[N;ϑ'qRlMMMvmtst*rV(Вe-)AF[#%ebb%04p΀]e&}:VBjTJv*quﰵALr^.>(Ξams01 gG/rS.}6kkk.Ԯ2!nBZrzfyQ666}bQDV#c ʜ&SGԡL 1o3ۛlnov $[-\1P>^T.ŢC%Ң(6,~0Ctik*$8YXD t;G~ؒ}B)ۨn8lE!=08Xb_Ç]PVrl4R n51>p S0Y*0 l%=Q* I5v@1.2-pF'v*xyDv:5eװhMoZBL?bW9_H;J@ IؕKz&1|Lm6\z=/㴷&޿vqGBЉN>+o>tţU#1Iѩ eq%BRJNy="%{/?1T'IH}3-0 p@pEjBR^~wTJt[`RnL&0^ %<ϥT*S*Wqt2?X!3ʊbȅ x饗ᩢZ|;pҵ |Q$:MHs"58>zhvT35: ^OǝE2escN $.),"EJB6d0G';E)*[[!}®$ R7kDAFp2joe_  l9؈d獔}٢Ŏ|G>3;76Q>_\S Ů&DAP%)z$TϜaR,si065GREV `~~k׮3&cv֥Vf}}y\g?o&qj`V<g gw+ɛ9dd~sp$Z:% >SHLBf"ascׯp\Rqvxbۜ 1b10 vQˑvV?؉#A5*R!a81Ӛ@x[fk[D!8f͞Ð׮psGOa~vq3k_bx} Mp܂o7頥0,B:d$@##DIdHp)c6H2 MRS0]R,|w f78"{)V2®!37j44jթWGńʌ!ːMSO>2^M! njQj${^K:%=@j)]T4;47qKٽSDtzFbYLFvK$ b|şcfzom HSz$4d +98 o|TZ  F >ޖm8 X0wLQ..8 M76X=g P2hn('E:8Lz*0Px@鈾jKF3LDx^0uQhI? HaHvZꄂR8 jf,]B*PQ0bFi2ȡWmE Q}c)&&q*a*'N܃WLm%GotIuB1 ,t:]Kϕ&P4u8~1n޼ADPQ~r:(ʛ/c⤇&HNaE(TLNΰpA :]J ^'>ywi5aSMB`zfnMnYK ^9{8Q3pc Y"Lcp?N͉EyBGvu޼:nuѶt/1|{j r$G152xuqgdbdrD/?٬3b.V^@hRTE8!њxM0"B>|)f@JYt!',M8woޥU+.8&ER4O$UExE'Npug8؀CΜg_%WrKr!9| aJv[D%rSrrU _ \vՎ3'>S4-Vn2w%^K LBiubɒ Cr,h`H8p@4%&edj !n58M8$qdIzm͞3>c|t[ W?tjqU8hs ;z9^&nVM+R.PrM+8(L%+\4VxƎԞC :|R%N]$. D':%uVIB%@@8 Z+PN_k@ .h&Wyw9v s<rBNve \Y9v(ow.!i Fl͹_u\^wybznZI{gH?ñC sCii3R#)v& -:2QJYY\St:rQϧJ|"r^pI(o4jR4)>DJ_ڍN>1W0cvqV J[%%W im7O=t; lFeeDO~)E#S²~& c81R60r IDATbHb{I&''Y]YE"66#0.Kgc{:-֖L4_TG#" <0V;;;TU|J, uƒ; \4wvXYa{yfcyE.-Qbjjqǚq23Ft?v|զBw$ &!RfS ˕ If}c٤adKF);@ T211MlmF u06 ʺ+:4ecq:x[oǏ:Ʊ!\z[i| Id~~/;;$Qru  Z uH [[[}\Hf~~ʵ!;V HwG3ee}IҌIJ#("\iOtJQQ7Yv 2--/ ._ܗg>˭X^Y9{ÌqΝCrNk7J(Ӣ0D/tx"qУ|˿G{! .]DHU W]cc{т=ӳ>Fc:繼k+%67i %?` ["9?_HGR,egHz| >p1~K_ࡃGϑX#zQD^P('qNٽ4fqh4HpC"K BΜ9m̱|I=Ҙ=!M. m+nInݺ8Էꄉ1^)J9DŽ7h;x }܂˯tu{+Hma! r \ǥѨ e$ SpK|G)+lvwXBP*ZZi259 En߼իW)+O+Eq[X6Gdc rq1=;M/Dbpi"SL$~IR.[u990Teey WpK LxwH!N̗8:F!3Ѩg_7RV+x.A`=o~{LO]Bae!q]/'ef`aqo+oӘlN*0##WEl^q$ި0>/K%=)k!%ڝQ!sxOB{gscbƉ/ s]xR Je2EB#WJЊ!I#'Q1xLN@ VXf 4'^_cu} Z*R_F@y&T)cA 3{J!ޤ?S=O?YΟ{MtJ,,Iצt' NNF'&U4^.rɺcʼTdu.[4|uZmd^"x7}V(NW6s("ɧ;^^LNl^! H%I{qfNaxa<^7_"{h R#vZ2T?rU\<)_䓟p}ܸu t\btw8 #* w  |֒ͭ 煗cc."Io{~iZD={I7ֈr"2f]h#/Wauy<>3j ǎܹslmm!ZCR|+w¨G*lЮoN$TjZ+W@J+'Vz} @)m+d>SG=N֛^Jn"G)OVJ!!T~02˳gÏ)XBh8ɧ׍W`j}ILZbh<='ٮw\869 *:++ˠ5bɉq&sNӔnפgjJl^Ytw)qz ;,kๆ W7ur>ii4I.N˞}Gѣ_cOqi*#c Th_—x3;7R֢kH^u~5$px穷6IT!k`I"T)Y]]˗xxwY^W^8u0w3<(;RuZ&CCCB<1޿zZ8ű$iуD4M ۍuv6?Dv4#``tfՕ%^U^~%DqcԾ_$I㣍6k)>LO=g8{"=#< SSpט&N>ǁYX\"a!+Nax+D111N*L8*{q1U! WR(8zqJ"qEHgEiT)t2T1Qq/r{m!N%gN11> /_~vs$ͅyQIP$ѳSgp >PC]HňTfȬZ"q\\޻tFxRiaMz}Yu;jj;mz}86CcB3/^@%6mOV⅗/}zBg,T,^`~>gyҫZMH$EzC|ӟ xyǞAk6N]HO|lll`"cܬ:E:)HDH"+FXr#ie6t"dƤa͝m&yC@b2mcb+dNAk :BZ :“)2qtj5ښ7a! ,JhnT.{nwuBkjnZs7ߣy>VRİ5bJӌ'd$vIb.{1=R4VgITŠHǏ>G%J'9t0h6$v BZ=h0!B]`gaP(Oﺳ̿;{Ԟ_7̝+2H~|{MjЊNkw{˷ %*!fgf |wVp>rKK/?Ov"м,//l\ώ2$uJ39BzvE]b Ea\z)82#IS8"{wy~'Q\qn>äuOa[DaOZ./ vb'"cNc1XSgłO%)ӧOMGlU!8x c#\~?DQ Ǿ}X0n=xؿ(##e~~HT鲾\>߼ɻgs`^4I5˫mT/FkA}g NpfH{iz4;U<@#"Z-TIudGdD2G,@4uT)be"UskXu[-.>n{?oآlIkh8G$b:B4ٿ0cϞdkܞ5n)5ռB ؇r).kKEOK`ַpQ/Y.028i:$IKw#嵄0$QA`̱d?K*W t:;8BƱ,osN4-~^C tb$\ZۗߠѪ_YYY`mefNTG0:63EU"B)DȒSC \3X1aA$:uwdȎY( Vm+g"QHT8z&a=6au] Nb(48:%N5ab@=0SJ#߀cx }! ., CD0Lh6[-XbIޤh3::sAJJٰd?aBNMmgm) t3őZK|y!2z (xWh38'J#EBw^wtЪms-WZ$Q]1};ӳu9;T{GJ6Xgk3fQ#jFG*_.bWAKS h\!pxY6# ;tZ=v: 0/iuLb$R- %vZ=Qb/ Afff*G01iNXiJ#7O1TBQrI~_摏|0 C;Dq@6Բ5I) R8\|AF8%7X+F컌ˌT E|@;RoMx`8V8ڤrI? \312|"%mj64pwrk ?i0,,R4!vIb־-vK*s9|^F(x&1T1QDT%t6ݠMct[*_}dǸm.RHa 9TtOBnR|Le$5!Y_[Ñ=rnEb߾=8tEO~ ]w/$w߽_qM?h4ϊ$ƑlfvkWh̆3e4C0b'Hx#_JS1v#$n:q1\F Maʝ7Y][fbbiTaqAO=7tz]#m278VBgᑇٳ>{X\khAdk΅s\F8|?#'ٿ0Ľxy'xGXY2Xc[Ge㹖[&K AL\64K,#[mBHℝF^32>Fw;~ף(vm%9|011Ì2\w;x|믿Av"'OTjvp='g9g,bD؋T*X:q >lӨ Oc;i$IBXD)c|g~_ϾtoZn +h8L( U ~,^l5e}EfWlv $i0Rq8I >)1>؛IL i9xr_vhmacYM5)q*&I,(HiJݡZHgj5ljr'?E;u:CCUFt]R]d4$AZM#(0k裏=?B2avvbjB,_`umU677J߮8! Ck1\ h:/ vAy s>Lv_:rؼW e)4 c`цa?AAsC*ߗ{iY[[X,2j ?HʗR[(2 Cza`54ss{"`X*_Lq]9=BR(VE).a ?pSܺu˼T<*NilאUT3=5C"MWP U-{ձxv#Se {v;SX)&}6wnqm)WǵkWM4ASHb>'34ss\~?}!<>~<8.Oazj =9Ri87p!'v\@Ell ##fơ4 +k|auuG /V:w ޺D/xn~iejM%j;lն &Q|VFhC?{ a440S"wpeqbIsl(3bV[RJT!s㚬@X9JQ0=3gً<ԧHXޡ G9s)>L޽G+^+<diuC5>s sgo^gyeAO#?v@IZ$ ҂uׯ^l67AE0ǑG6@ RblxaVVÎ}u`mϺ!K Kh_8r+b E~9>yӜ:ua\̵:#cb/.QThLj]_9QMe*lnn֛o[onySo`fjJFe[g Jry",A:l,dL$)%Q(ngƈZ16w}FsKִ@EmOgɓ3;3ͅw.sn ݯ/F1&,"rD)N%9!{o8vn~>0?qG{wq;Zk(#4K>*+iqSRۼߚ}K/s=W[RbTcccBfgh(G ,FA9ڊEzo7o_fzQ C4MSF|+_arr4Jد4.+^+ZV.P:{U:佻S!%7"R{+f9Q'⯿W\xk{[6JVenrV6R>0@4;}^PS2:O=y 34Ƿ`wοOW_"MxF;e`}OASB3=;a06:FEYa)EU8MexlJzJZG%|`!1'1ȕՒjw30# \Gn{^n qS`,M.fsKxRhL=GJgqE\%#RKE/;"KrW;D-xQs`.g%tR)"̑sqRƓ< ʣՎ1F9PdF1,g8{dqu4O4m;J}R(O^O{ڵ<,I=oݘTK^(&aЇR%(l;թU)IcW晚[dyR D%֖$,OI)ln#FIy)$R+SO66FZs'8{^EBP|-AF7I-FJ] )I6Y%{Z+"[+_`^N}FX@U6~>^XRtCy1P >C)о;d|zJ}XQ}O>2MPE5\ܘ^'Q@8]2TVW駟aaak'78z_Q;&sVHJNX a|.V7i.0wLhф|_bX?!K+s/_CaCkMy݅ȭ.7WouϞ=VI[}~U+ύ&ź6 +a `@xVlAx8dyd )-g@6YTؾ}; iNNiR  }])0ir bHO[Tآוhlbadd'CΝ{\tƆp{Jy$,i47imh` *,..k.FƜҢrz}rB_6PZDQK\j-@8SSSlZ 䬗3%U!{accFQ{z]t31MS67ժFOeoŝgBdD,|'J|^JUV|ڕX"JZan6ʞJ/+:eU޿Pi2p( e9I2& B$DiI"{BEqDR`ciKU]WT5N}L YNއ{T*rM1IrR>i33=ʯo0?FC3kiFFGu6Q'bseՑ- ȑ#%A,IilnhlbjUPx^Qvk d{IXY^$ܺ}ZddbC17?IKܞIN71)J{}y^9} HeՕe1PsoblAXAFˡDyB,(`#G9k ܺ`i;$Iߐ$ QqQ Z6Y5$[C6抯տ+ٳ~ʔmu+n = 9w䋟gym]5qY>N\"y~c24fBeX㉏<˕+鴚dy3;)G{d$x hr}J8+VFeZka\ oI&=R,1{}Uvo`.77Ɵ)׮]Ěʑ%44Rwallbsh,7~5hA{4nxUexw/tn IDAT\b53s͂4 bl=nݺ]<¦[є*7=a)t`Y_$ _/!wa`X,(fhpU"5ߵk;宙LNO1<<§? ˋ{ْ 2AP4ajRl6}&˴; Ae8jOȨzVMM옠j8*SN3:8 vč;Y\[b`d@981q^iՓĸ-;Ϗ] PJs7@0![Zi?|^D%BuZ<$IR1IJ] 2:6J͍0&'r$vn=84Ķq ׹Hm~vOL004HXnnq3J,\rnBB0d8u,F$eqn,+q admq,h7W]f)\"SY!mţܼB;zޭ,бmXcI8vJE5j]23w(FA 4(&N(-kJ+&#M"y[e9f+bCYJh=M%@OMCrX_ˑ!LZHSf/|q}!9Mf"3d-k؎=<bqy;l.i*]"Fе󿸰@Npz'v044L)ߢ{ 0*G?6We5^௾$&2 ZR\ oA8]75Ins9|(fSNhl44; Q *eEZ,tEYf8ؘ Dxh%R0yblfSVX8sDaSʹ텬o΅7v23\[ K#ڭvA N=|5VW5:I"3<&Y__kt-vg"{!Ske.xg16;v۷3;7Fx֓54ӤqLOe?%q׹u :O{'< Nc K#;ࡔ*OȫT1E2e=} %)RfZڡ?(cL(+/bgf,ãx^,(& .'A{Czyb'Sp5M(.-Eˋ?&&&O3[XmmcoffH~4Cã<}^ħػwY\S~& MV֩׆9u)|DTU* A vþ0;2p V+Ӑ<^+W*#MV>Y25=I;vU:^9-ru<߫8ȝRE` JgP3ȡCG6Ny4{iS㉧α19aEL0&/{Mv*MҼSJ7ThanQho̳qmf'BRiNdܺvC K޳{Y?+ 7_^SW\F*lЯg_KǩY(X(F I\sW*5,OL,/ט֡]ތ;T[Țʣ`>DvŅ2=s,O9p(ǏbyiE|Dܡyq2T~ K3\_F;#ǘw%g c=W~v:7nrɳY_[<°?LNeye k=4MxER8woZ'h%`un d${XYY)t FyiE|W4+Py)Ze`w\b׾5nܼŅ:'Tg~f ORda}u<1aJ5D $!Uk*vѨ^^M%ZMGNbL&*ZA4-y衇XXXlu*3sX\#;O:wl祗_fvfVsSFڗKxu'ȱ!Ev k-VVWHLT뀕{&(+ Ur58ghpV2;iWH (Z:P|*-Bߋ[[߳.HJ gBѤ鰲 GFJ ػw/JZ\P$ǝS"h6l6EdV_j +TuP.Cct5V+Q7{G8s۶ӌ|{pI}bO<$02: l>V&s H] "KR4r "';,--caR򫜿.$ yṃ5= }(Or,MAk`@&,d`$IvvN~w9 ڣ<{5q,wƭWr y?VV9{,yT(C29a {ܾOkVK nޅqSЦ Uy9c_7.̳DVg#^er1 k@Zci%J7WJ4axϾ''ߗ65hB"Vn9?֪m_?ehOX0:_;{]J>ICj5Q޸kݝ|aO)٨8 Cߐ8T]& ׼'r :y%z/r%3ehq-.?_$N:Y*0#z:g<;*L?C(#IRi{NlgmfAPpV]Rd\mf4 $5YZZC$q{YnݾKsTB{SgqQvO&ٵ{Ď 3m'G8N@qҖk7n– Q8TK54M\y4K}MK)Ez-&cdxgnX Z[fIĴm667>$sM>?]Ty<-"M<"O:s]Rlqmf[by!x$l~DE[ Y*\/QJi-m{\~$ %}D[+l(+w*Lt(}HY/=Hfe{8Б#,/j60&sg j/~E(bjj(P#eԽ w6#Py;AwuHgēOVG;&v3<2*,V'SՄ~uҒ佼LŐ 3slmЎ"vy?=Hؿ> WύW@\u&=)F~<UyWUKU$ky ln6Rv ڒ qZANe$@gdqhߏZ -+X(V3ƈQIM|4x`@QG4؜#CY͑K5gl5)&JlN˹sNaG6? ޹ѐR667'3|`$dAFFe*AՒe:ȢB sZV]eCwˑ$)(r}xghS ȍ^cݫ,/aYtS//5 s=ѮN{kUx*<zOj˥8\8 BW+ljIBRZ%~vY1:[2"˵Y_m}]\z_ .!d9e&'E"U'N=Na_{YF$I-,%sr7e D)jzzcFs"k1:]v\FX][iDKɰ-i &c9 [7XS[J;xϱs.n\ZUϽghh5GFHY&+5q#_*Q9X C*y$F_ E^wF*mk$:/k|KOY|u$XCZOZ=Z"9G%.ʆT|3RU<|̦tҘ{rÜ:ӓ\z1;;+!X;_~cǏsIff&Lb2+XTZvkF&C1F&q8#mC_zS$iBedX1[L~4wGL޻wWp"idp1,/l4=J3n\u3a:]")eQGXg9x$ZVg(nbQ%mh4x-bL)y-{ޞ\T#Y~3_̓r" shq;.y ,Ng~c=VזH $ycsR8_[|N@9Z>Zy 5!OMYYK]  ,-LfthrPG{EЕI+4+kXlq]V2瓞AC'O#ghU676̚d]:<:*h6\xz0qЉ:0{Z>V{d*<yZQ j*2%0llwtmO;vp߳~Ξ=‚kISCc9$۽cyGZ+'N>}9Ύ &d|wM0#ڭ 4[RiӘ2 ڌ'OxtIڑ.a!0$bqÒ疉8)e9(?#Cf'KwL/sO֯JS߮jh-:Ndw\¬j<IѴmOw%5Y&/Bj7UhQiOxePʖ-}_6U*?co$uy}K. @a/ "ANQ6S-[Rkt31G{zbkLV[۲,ٔDHQ4{PUYf( IDAT?}/3!9b2H{|,+d%X[]gccYm9b:6֑9Gj";tus!; ek81H!IBD:wrG={vSo4=vz!._Lܹxwg!Xaa`()?9?B+VWW׾JN@i8#!1&jy>>p?n+WH7\~0b2QLřJys}9wtZf8%V4x.%&U? t|s)Ɇ>E>_fhsgtY1++q}|\^齦~~FCLI1?L4ieO5z[݂V1KsDQ`sOWIZ3mւrBϱ8Ҽ喁f~ZVVЦh _("]80<1x|#C: Q%&%5,,V}tn CZ3?7GVeee1sG )fllO}SLOO1333t:qD=B`Z vq[*[8 RmީtT*t:m&MP(@icw) ˜B1@q\GE86\8ϕg("TqA7_>K?ʣ|J\^O:$I,1-ڹ F(H +,.r}]բP388IVws4uCvQ{lLRi]:3Iť9{<Ν٪jո1zG>Gm:N\Ejej\qZ{>bF8LLLM<϶EVBfpZytkv'`Ν,//tX_/shږ(!%ҕCD澫_ Q"mRK6rI$$I0V-0=d(8yHkK+WilhVH!q}Tp kim:*X bv؃>9C%rKaec}ɥ5Dv1VVW cn28S;3{"gOfahM.WxwRU@$0:AbfvG S@F:kt۵1X*9Z6q)*vwdm٘F'";vv/8YJ4NZ'#<06>k~ҬQ<})融^Xَ044DY߇n`ظ-'>s+ﰵUjixyt3ngЦ&}֠cVʴ !qgY 4(4+=qcu}Nzmj ]|3O8K4SbI;$Y(aP ɴm73VrGnY&dsuPڅnڢVQ(hDuџ}iFJ='& U3HQܷ'zϒ$ƙoQBJ%#EDiAjw!mbE 9ݞt3L*+9XP8f>>flbCãr{IT"5ZȎ]{i\ehD©ifF).wNg?=\.3/AL 2<|G #+Uv 2B-ӱà׬Vknaa.HZR)3>U}ե&ss׹z"3.S^|Zf(IRi3 fNE;hVִZ-u1zFJEHfv}: SOQ,)y.BK|{[x_\^ϙ^c!BGܤ^.(Ld3. j{8&◉Xmj[ƹAOmU"0”[Rb`p0+v1ٞkeN's"v\7x[`ݽk۷dcc Gh<}!_Fec阀feeVJ S!۶q=Jv"#17f癜؎#=&&x_|Fc*SJH(ٳ}450 Yיv݇($(i$}|÷/p{y4Z%+f>Jhtzj]"h$QDE th[OJ"D:&AB% g晹65 M"37.2o8IǒWN}ɈDn \#]-%v[Czϵ 2MlS(Dqӳ{׳k*JNOF tee4;'{P>›3Yf}cXHku㚙ZbL[*&qN+.i +=7 =q{C>6u43} KşC#_GRaY 渮"eXv'WGTBkMMԺK1hepD\~W~G}hJ淿ͩS/p c bnViMMM9( 4S$҈ ]'fr׊N'B8fTv(Rn&25QLsc}F S;yyLB`~n0o+,yԱ!ʤ5顼qn=|;$Ii958Vg rVi D:W)פmF>)c* -ۋvRTnHtDV rylnV>{; В8Hh ڱfGg0_?fx(b-@$ QCk϶Nq"H|~sYk҉:8:E$^?{e6BJƸ O H)΅r6N!=QgJ`d{ b`P&WMp#۾]! إ9|۝q`z?gΜBɽ,/n'ZhՑ8ppdcLuk8( ҡ^k222BFARDLOOc)Y/R81]U qJ()+8"ъ(N ۽g`|+ǏKÌfr|qslnt !bCF<7ǎC e 3&f{.ɓ016~?t\^~Tx~VR/g>N-\D)cXǙ(qpViAKA;x_u<7F!$icHj6̡V+Y-{BTݱfBAƩ:>>0O=~^?08'?Y~i>`y4ͰE\v( t-/QlqmuFP=ȑ#QQZ-Hh86I2FY}OkpyYΐ`mf"Vy* DG1*[+eʫk9W A1ҫjU.^|"zfԖEȞ"g9+D"q!##t;_%._zFNب2;wFV qy;͑ĉ6dWƸxk1AСjÇ>1c bАɂPmzddt=8H8r(뜸(|C٪n ELs̈́0 sE>{435]$.~'4&Ǵ\!}T*vY`;2#GIRe7Վ@ MޕFv=@:.`D `lqFJ͛\.n]wL4*cHkV&LH#>vŻ~-#Iڠl#m G=G Bw~M'kkofђvEyu #\ !qnrDxke}߷(yrr;~;Z mFf^ֳRln\%n=o/Mjh6(5\K)Hr> @%v;`cÃs> i4X*Zu:r^+HWlpF"lbB|XZ繿Qd|lյUNzSoFVAH{h6[8˝wI4 2Jۓ !x衇(lnnv !LB <4kh!;RM}RmopM%@zHof̦iG@S1a' (%l*3%ٷ6O<$09oNH Ѫ֦E3TlfmoDaŽȖ$*ʌUg,СCl۶-bf{Wj\Xn;LM*eTNNph)T*ڐj Z]GHGH|&Y¨??'i;VH fWxi6F Dj( $Y-32Vp{c ШIS ?I`Remq2K 7hY^^д`)###IZ:% ^y?{va|tbșK+ -q\֪h4X[[f=2='ߺc;MLGMƊI\aB PYf tfA>)zcOo='ORm阎aTXՏ&9`q= CxG8GA^/) (}ȇ)ٝbKK"X_`qiJe& ,/D4HXBHOΦOcccl߾! MmR^VHPY_/ѻ$AHWULu,S*c]N5aZTu c9SZVA`uH( rYPdA&&&L◘DuLKGt;/!DQ9t^jyܫJ300tqitaJNv`ef8W6*V׳?/yλo)0P_aq쿅~hm6jkyUf/E5\iHq084ʿrBZ$wykkt118s=&\ IDATmmiIdYa~&N0hPÌE (I_eoR^[:ɂIn =3ihrgNVռO^c 9|˭z |BwLIi:( sӀ; vڤ EBZq@n( !0`'Iƒ]xr{9rm3w?_-:lT M;w5uu]7HA72묭hVʌ şMLJ)jQ8fZ -9qL:H L={wrp͹Kϰv'*2108z(TUDQ,yo߉l 9j6܁YY8V0CCdu2KD{za3d3"{"U(Hb#cB>y&'sm*}=zkWIyǏsECtO QFyàC=Z!Tibmlm86Ynѱ |? 2)_H=)ULBOZ2ShIVזجl/q=vu˼M N@1rzf& \x_|w>Ǎ,RVX.Bi8ެĹo֙٨U8x(c"#CDAh^*p#Cc .' B\5(Uc>ҵD1Ib^Vbaa?~81{cOpۦOGqV㎓:lR3cKZ^5/Kzk"+'o a tbq`Gٳ%-S6:DAV4/wb]??h]k\Ña.k\)UD4k!B8n$P>@لMMXVEƀ}Ä( ,,,^wϒ9& h0X,2}33(n7܁țdg02#uf7CwwM47̱ĩzh-U#!6% .m^5`ȕIɮCk!Umϔ!yg~[ahxSo{6K+ ŞZiCΠ ,KJ'uz?}ވm;_vcvNf4ھÇ#22> \nyTIh;t bϙSzmt c[t& bV/crkRDIn>288;z|mq<ŁA=^wΝe aJxq4#MLoݼٮsHތ cfQiz/-- =ǵ}W>d5oߣլYڥ rm1=}G}w80bdbν<|;=~}1ƶMQ] .]@ 60C$bnn:% a^m㨞/s#U6WN>n);{҄d߾}EjH3g!._="A:_CL3968|z;K4*xlK7G.Wu ECvBp%b c۶ I;wS<xpJ;z$aym|.O4`(ۻFfF/\`mu5sI4MyvN!Bضc;ν{e1I*I 9O PD!hR^4a$\|Hj}j7{!Lu{P@*;ՏiauFȻ.k[ s]m%])4 j5vlۅ"TXCbp`ϑU/:׮Al2.aoА*kԶʦ85AMwnW7,PqW"} 7nȺ!2Ro/yُD_DWvG()oqi=|W 8*F&”z/|eVZ(SI´6\nHmv{Wnm4{vZT*+_gA LsPϚ]|rq]~C peh4?$\.]SzmGI;DQ|&&=F+h49\mUt9tPem~* * ij5sCՠZo26:΁}"E qLL;mJU8Ry*8_ض Z1MV .\0 NN9-zk8,G0#MBkSS* Z4`tQu۷ohEU$Lk]1JY?H@p-\'N:qB&N7fo0*u ZǕMRhE" @+l nh E mW"եbFRbd43u" d؟)Jۚo2]dɔE,!HU#\rZuHu ޻z3xwDP܈!Μ=͙NEBʐ9hӒ~5 E5EgO|SLMng)FGXY]fq ~쓜z5°ex ŒovXiT)W~ܵ 'ܸ.R8h\G"=aJ0|}Ӝ8qk30~q"cqTVƈD[wd_ɗe]SX8 4DVG|394ƮmSޱqC(!8m~?_LPk"5٬ CK/3WF3K+AٱMQ [lmLq2=}+[im]=E50bVVk[p]6˖(⯠mb.\_&/˳E!IcGKzېJF6Q*&S' *ѐ9|V0 hIb~<>11W.Y\Ѭ1g /N8&4UfC(sJ&knHl\B{ѯg]n*vz $FU!e[5:펙z߿\.gl%\*g&[I+ꍠM#6qcJEIw]264IWרmթj$e "3uRT$r4xCM:b4h`a/N.!-ꛛXg\¶bs"dc8Z 0'zLHu搫-6=6zL J߻ڦADFu1UЅcKK+MbuڶĄ,.ϰb[ۺ.N"ry) ڦ֣ Q8DHfq.&w"܀oH– DadN*]'/~Fi#E ҠO:$D+}N1ZF$:)St JӟĎC2bF% LP9ffW^/}7ϽΙso:։IɯB3u_g$m׫$ID%8Dlp Zgϩ2R/| TU֌2sg>LFz>o|D;wKL%QB$FeaqŅy cAl6t ~G}_:msoB1oVBf/8R0=|/Ut\gk@_7RqHQhSªa04:S>BfS *VڴiCLIK$ 8q~ͣ\lt w(}(Eڦ6LgPQm΂vB3֧8͍U^',ϛ%QYHj24U4:v4j[!\'}@ GD;s9FxRژ^ ǜax'vol4"gL1cMyLbk;ϱV^epdpyG9~ǝ^ 1v~!O) oK$IL۬6zzŕZ : U!.iͼӇ M&:]?$UM(8H%]3v.:E/$bxV! KtuYZZD +\;9|vxuN A/0u^JB?[l~/rOR0y(X\\ 066aj7*AfM]jkuHR8PD; R T*5He~YT+#wu~+WΣNDk4BO2v@4+VGr~N+dP_xmFkqe#Y-tr.$%˻zG*5܋M&\҅OGqsʿk0V:3$$uR3glq(If6v7]ʈIP*HTb7J֑a#"?So?.F!=뺄kNYWgCsϽ}>4БDE@dPe7>:(,&0`|lÇ\yTp=^ sYS B gfzNӛ8điM+ 14t}_1&6oFo=Nڲ)DDHziE06<nJ2³hg҆3599vSNqlEl"5cjidkUF+POl5t36)ˬ4gi5TX19!A.%&* &zH's>} l):8B$6"zQWXXXJ0IдNd4 2toHip(mG̹S!8-F ϫʯs@_Gų7Sgqrf+ !Rԉ٘N&}U(:{1>X$QNԦl320fYkqDQ,m1Vkk\zHEmÅ?(:)-UWIS.A hIi M|cO05u81.BXcE(4"BP.@zV)Ѹ㸒f6 ).JVeP6~t:5 d:} !LC| ?=Oeyuu,rb&8 Q% L] ""5,X`ph/gV[Yľ^z3Ou:fŗKyn6n<[ }>ػW\5e}J$82F RY Bw fÐ+kt[]FZl#np%FBI-XH.RNSZeGt>Ȭ{n:jbCeaOYm͙g.׿zH!3Ifi+՚mfay7_',/01:wAڨis."I2}%t%ϥTxWj΀yv>StM$1 tl֚յ'"xVӴ։O& "Pa+wo|K'I D1Tm e׵}1i{:`ІUC&M lq2νvݗK8m-2(99,Rڮa8&ePjfz%6 _/Ď/γZ[c4-b%4I=hwiFFXYYjuFap`/OC' kkDID4 K7l۹?I:=w#G^:* qנ10*1*2* FNC2HlB*kemt9UDQ@7Q*A:I(H!>)%ax*d͆{}sBJ98021:[F'mvnI:H-T C*Þ={9v^6Ol2'.]C#&XEn#?) m3ĉu=R3+pa! 4h;R{z]3 O0tѥQ̧9sosN)XTgG}y7v;] !2^BDlۺaJkFFGygxvʕ`;B S̷w|ogOQ,bקך4M@P)WWJuk7Eo'9N8 6_~;POʁer;k%E/rj[8,9/sgN!u(p.zm{F|=Z0dff={2Ϲgh *#0gUMH?ħ> ~Cj5 S Dl۬,N җ$0Y7ؾewFD,.35}y\MIqv\xZ(bR#=K/O>CylO>gJ^Y~^\~{jNIM}qb,(M1\ rW&*0 ̃ |z݈W Dqi8q\j-㣛hAij$X̔ a`/5ВQDQh 2A5ã#ԛuJ૿ǿ+Nx ]yg$Qw|w%ׯ]?٬!u9B.tqu={{r;cG*!]vG33;KsqDD᠉)CccAN뫫B bHW޶fФ Ft.:QHGB@uX>T/ʿ=36[fXk l6SlǴOv=cmzVD9TVf-@)܌ŠfҠm+qwlY^Ybp`4&ԋ8Ў^yHAe`FƊUjlٱrsN33NCEJɖH#R{j9 W^7M#?lt;nz(tn|`,izaMb֞!$\Ʊ{c>A+PP1 7fgiQ=G6i~+W.q54v**2`uemneBIX/r2)Vv<,OKXxO iDA /dv)|cg8ҩn~6AiSGoM9/a@No+S[*FCCl߄i⥅"hvuw(\GZ K`h׼:t^8o-fo̠h$f]V:yց=,bDc֛U*UسchЛȦoP常n~8-p2o}]ȇ'H2h LXÉ087,/EGҤa -jmݻ_@)%`0 JcCY6yfa7IY+^ g+ =PZ3m+beeMsO|򓬮Qyv4 S"dFBtFn1"~7ϞW`6+˫.}X\GTY[ic智vgi=CocMUKUL#㹄Q +r4N !J!îU?8 k,-09_+|ߠ( JH_`dd˗.TXu['A+#v8Tw``/ 4n'GF6ƕJeGQbeȧl Bm1yhG]Z톅4B;e n%W>:KuHtdW ?/Y[]~46թi&LKt),|׾]GqY$D!6cc|嫿퇏;?uLb??}X[Q,{۷$N"Px060Ezm u#;ӘKpD_>g [(k{.t|f_n`}ZM°kM3>1Zf2R/#Ȉ;Y3WT{`?Sׯ(˨$jl80fD44!B (gYK#ws(eduqq8qb1Vw K$*Sh[^ƍÝD+B &vm"B)TJyiFfyTIpigo0w80#І-$ ">x=_BFtQNf}IalQ+(Ƴt"4:10vSa6EO߾&HR8;t'~ǩ54MجQHQDIt;ZٰͦsA~384HVDa¤Рowi}VZؠBE1s (+x^:>.*IlXm:9%`yoByl 1|CôuD#]%N>qZ[.(ђMcf/,ߦ^(RohՌ 赴Xg[=;7 i: ht{~FfI 09Jb#~ Jh^Ry^)ؽkUV>"8ZԂ&`nQt`tf<-ع{adxĨejM`Ea)jm /V'cΝ;iVݐ0i6Ye[e2]w/UG=^:'ۈ()c(B1dy8i;$1NJ0GCu*'=]\ǥZHPk<ȃ?E\zm GV5ϡP("F ^쩓N}FG8sIr5@դ^o0::o: =|_oĩMI6 _`]yz ss76l*WiV<8,_ޗB96)3w8iFuLKG~L8TU/\A7PBkKE9&0k6BfL#>TC)ZS  ^V*(EkfN&1Sՠ@iihR0:2N4@'HtBFaZ'\JQLAr܂1Sir`ހפR JsuSչV {Ct_okD6`8dzo +QUQe=~BuM[v8u%63s3|}iťyݘX(OJ,..Ķ5NQng$]kӠI$O8Kds*  G%DKid"h6Z9!&Ջjӱ2AI~4ɗ.^ǷR !Ӿ}>WNҜ>:ʼnEV8HQ%0+XZZ >SJl8|0^˶ַGRp$\pxՕeW43M>ΓG߂ m{!Ӗ`tMy4}^a-3oiOX]5yغeK,-,EŽ`Bf8 hJaMXϜ9t0sקI^goxy0eoHcݟٟ㎻rԇ(ss,.-qAŚZ}_ׯ7m q 8Z:A0Qdddɭϛ~'LP \ũsyb?UiAJqs=Ϗʃ8|wr=).})Y脼r ׯ!qkn&6oemu+W/kM0 - *OjbC>J'y葏{@t։?y>Ët:|' sDDJLH PD vهV +KWY]\UZz܍YtDq $0d}}f͛ZQ{Nٿ?ο|)iN)5WBΓ-FhGcR4-$/EI͜> # -h>~˷΀}VI;őʿC bhء"CzsY00uIEgbX_^7E/EkyQDEdIضlOsa`t#7tZVFoF12G7^'NfVяR3CRzM<ڷjau]amu^2(ܤ;O3أDqhGh;fR8J~~Gr wR~p\|5kX;[@\2 ̵%\yvoc#3 ߟ_h7GZ[ =G ,)u֙~Mτ~D)[AHqLiR'o16^gPhZm3 Opclߺ;x@kvo^9ygD)Eu+/̵QDG9sxK.NaR.5AHO\sO/wLN N'_|CaO!J%v={?֭;>nuhZv^߱^S/;Zt#ZmKW.qu2+a;<<(WDӥ:6J-"j/] j^x~rL|V" 2Ry^HnI'őr.lAmڵk&{Jq~hTH: ᯤȶAbC7Y]^65VkkmŎ9Zgj9Na{RJ'?@RKVaYᎉ\mZD;Qd&vkCLe#6 `<,x^u.]Q񉏿w29cDRJ-xo[DI®xWxOq!_8Ŧח똘?g̙S<¨]N|!s?,pE}AHGNԕ)Gz a!mW1!.$%823mCYQI+{(FrB^ep`()GJP?xh+5y"qjg Tcr4FE!R(xt;][.o&_geu>ky)R3!^x^.^52i5! ȨŦM[dl|~ w:55Μi/q0HW~kW~WAK>"M:& !T¯2ˋKUɔL/} ϝF@ ,?F!]i,rJ[ R#?2]ۼ䇧ߧԭU\k䝰09Qu)lt(2e6lsM~^ʽ}VՓs̖2fsY)e+zJ%c#w, 7JLGIOTum \ITDUgym59P6(II ?)T{GJH[56>FvJA,w1QS6Wh oĉ_f>yŅ%Y+5vek:;߲i,ʌm{{'?0B'!3cKIY  ?>|P?Be,L{jrĻט O 0︓UAooۯ:oJDvZPHGFΝ;gٰ8Td``v'S6> TdYH")D!P`.!R#ww}\!3"cJURo|O~vLlem+{zw}oRҞt X:H,+"1WHL@J];J).+,f~Is7f8)fg!--3iy>CN!7n\^_a+DjŅ%V-:!I~7X*3iz%0kuF7O2ϓ:.aHiz"_"L HYsW[nm<#ڵFS(*F"!a E7CkbfHl~>C< RsH[L4׮\/]dמ]݀YPI/P+W;FZQ˃T qJx8qMԙVR BJ*?>EM1*(& 7F8̭r7PTٟE~~Zō٫HbLݠTڙBMJBTYesMM%&ɧ=劇 ]7ũ.N)5yTC>6{oVHeCq.l7qI@Oٟ7n]Rk"88yZLGȯ\@U#|v E2%rBScc#P*F1IQ[37DBrŠ4r G..sI\•?q NJmv^t`QӶmٽ{7ZCK0!PWq4={ahp(#fYn#OçM)J)(lSz5!C!gS2IlJAJNy0" GPZ,!)Z`M\j!)5ɛ1Z])<<ֲF*_+Jeh:6QHAcqDRX.$LS^3ءXa.t2qݮqdHzyҔ,\k01>IXngf8lmŨ02o C$1O7_c~:Il8߱Xb:m6TQ[[ayyo<8G7pצ.%4gARtRxS/q]?{۵u*!g$H14W[|K_ffקs nL`0"6̽ıdHas;FѱQ.1?;mUsCJF싯<Գt€kׯAp&!TUW\6\ez~2Wf/p{wIcm%\c|n=oGWa!wu#CܘilEB0scߢyg)x®&] njb;F6#tiwvcGQ R4G".8̮]{ӵr]<܋~0$Bcv#3mGhaKEv܁H)fg,ԃ0jN>5AhTLaSa;/zcelzEh{o0#xRBJYYsH8;vҥ(X"j߫V(M\رcEf6:B\feu &0 z,Lmh>լOT7IPVyWZCZevvVkʐJ^zefgg< [N"LJi:݀yFm~u,ƚQJ[h I2==#iOF8*: XnAȾ˒R%L'_1vͥK8{F'Fz:QDknP$VVy7k}?:7w[ Dlz[ڈ jt:>gΜbj2Iu'7n"&vO< RHuf )p$H{$ztM? a`07?+qijc穧epp%VPqL~$&$ eivL@KwIs3W Y,9FpպL(ȍeDLFMVYSXq֭[#o}gxx8 ' ib6uPtD 1䱄8Sؚx9ݜ2'Ed1ݿ ?_gCDwl:ggki%cǎ,-[`yhP zĝ.q <Ǥ6K)M*)MP C|r~ 3Ÿ0$?P Jb<0cǘ_XvbU\+Uŋ,,wm,,Ӭ5mrʠ BVJlzJXct j$NL L)Eq+߭lhHtQ5J87ǐ)-b$]P.Ʒ1usi֗qP=mbÝwIZQsE# *n0Bu/l&L\F?NC։td6҆DM'I9zQFGyﰾLoR󵡻"> ܘF|,A [.F^Tpd]P*1<\&|) n ;vݮQT,OS1a.B ! "C4EkB ^8V4[u.\D58N@ti iJ24F\|o|\nlJ:O禤A~~?zs!5 L]FGgXZ[$-?牓BH cctqMn}ih t_I&hJ[5$(9:kᬭ^J%CB9Z*y6haaxX|_Yb-yC9}Wnub!n"n#P2 atnZ30ӟ7ŅTA87ߴ<7ʄQAARf :K}B'1t[M֖xCCJ簄>)$O? [m孷$ B*1ԱcI}Gۗ/Z"=,*;D?A"s(Nڍ/'.Jٹ`*?_J+_A]^[SL_Bn+l[8Ht"}%L^[#Ib;!H 8vQ1DAm{rY2qyn,}*BJ*Uݘc46Qml_S,/ܠ6kQxEv`T4 w\.YjHdG`(PDB4l جض>6ci"RDZ-17GTK(ac ȉ%84BM<B32HEREB<3nLkJE1vp >*>MR({ ??#Zݚ\K(RM v;DqDc4 F@u?2ΝS9f]_DHVE ;04%QR&\(2>6ȉ?@8װaWVX'#FĺkFDz:GYXaay -=Οeiy)6u7,{;4*a 1:ìh 0 LFJ3QxN8Z}Çc[SSWЛRJCO<$N|&fz^q@r 1.&{A&mH#Wn 25ߑ:\ !*I*:^RVEL6TRwyEGjD"@9fI z@<#4֥!! T,cZF( uVΟ;GuTMY-1N v[0uHIdۨsn[QJ~˸f%2Q|uhr O8W/Mi5qT@,a=T+a/6IN~K6Q]yOn9 $ADJb6)RaƲ,y4,{o֬_a9,٦lRAЍFwu'[P#{|B{O ~';G) $qb׷N;!qar<5悀QeJK,wgnپ>ʃ~.9 &`NU-]-Oh~LVt@ThC6L aDbNIB a.ow&Q"&$(( hh긮7M8;]0XO>.FƘ>^i6"m *@ f:fdt[7HTvCR"t-46<9q%+Dj`NAo|"+K˨jm!%wW j;$Zp xyƷQdNz'pX7pZ$H*7eLJʌ]?ȻY7Yzg-9>[Mn24)*BY 8@$A8fnk8whی[="iG=nm_"mfCz( \[K>$Z8VIQAOiR!^&wszv`hn|rO*='\\Ǵ9H%Y_]cdxryaQJLI/ɎmYY<κZ9I"{p׎`$jz5.W/pw)c2ޔgx z{{iۛS!DIj&A 'ףGk!LLSUA>;tQ(TljiPK%ysg-J*\c]&q\;7UGڃMXl#O}A=̑X]0:>AO1f2Ʊ1}O| ~_KֈnKxF ˑCcO14qFGwkw{r=\kUYP4Ս:_|(KUبdARgVVV2G#%wx Ӗ8xyvNwLLh6GHdqawyB!q+K+4Fpmmׯv6FtENq:9s^Ru$ٴNs>713׵L|iuk#B;|^￳IgWpf [ +:DRɮQoګB|_f=+$2B"ڳ6y5Ɇ/p1'◿2 v%%wj#;ueŅ9(pc&}*='a5k\'?Ɂ3@ys?{nqyZH] et]40B6{#>#+|>/cvnݼIEFF8 E7K%{}̲VOPs9w$`tV=V6F7~>4yC4j ry<ߴ CdVu6T7x7X\\D'BP(|'ysDQ&4 b%qxmvgjHogC`}y 162(^!Džwy+iYSluT)=4Ay}QH+.}p;FKCw}wec9u_p6MܗȒ$"EبVZouiPQ,Qe|sg~n{sܸ~{w!~`TTuaM7 (BpMRX_YFEɄfi2N׵*e*\WPY_رm;cCKs>9O9_PTNؼ 4I)unsD\ζvТR5Ġzc{bkWZn"8ran@v=ԥ4nphHkULоS&z֚f xiY1S/C"cvnAg+4 zDD33r؇j}]?OKLmGm<|DVO=`)2G!5ۂ*͇ek+}?W~|_f% [ܝ,c0 [X fcfhj&(Op<}Ըme~^x~O~Y{3wb@>_$$>ZZ}VAݰ +X]^ƭ\9Re0inTQQ rw|!OX`qun"hSN8I$6]OR&vn7pql9Ehj8x]許YK6vk7Cjc$}%K+TfMg1v+r8 1A2s+ql4nTV7IL)ҵFY mIRUiM* f} qs+Z0&$2A+@JW@Z i6vȵR3RVz! :݀qȭ\t̃|y9uCaL.01}<_b8|AN~8S4v̀%^y'K?r;,."tZKc$,_@4T(iS1^j86s^YЪXjAjY^3<tHD[Yg-\Ɔ:&?W5IfG8Dgs$c{gLM]x׹2uv['kO.M7!F@F+ɞO7:s;s*{|/ _ݮ4j,cSO}ǟxٻw8w-V]eH`RR۱'IRu"rv:Ï<ΐ˗w>8بmn4"qL~q(77杛(a=T66dM(dIF($U6n$nlҢS(4goqgnZBGȘ jS$m@`LyLbkA\q/\BD} ڍLϖˎ`nqsK Y9i]k9pE(T42ɎcDZA ~޷ӎH]k3100w8)Wܽ2`H9mf¯@$I8H*hl"G$Hl:dGnat?#2LFRGwK\wйxcXǡULÃ~= /2Qbm}N(i(clt_7ػ~N{3>:ٳo#ʬZ8u$sw*5HdhZimL”Eo-_wBVan^ K%?beu{w3 {ezW:_eyyX2?`FMV6Rb0Vxx^I̔Bc 'ky]M׿%Bv]R/qlh:In01Ց` :siҩuwt:ߩQwkAJ ܪx0 vEsh/~yͷh0!$Z{2w: p&HLq#X(*zYN _2@o/w8?%$Ib^{e.qc;NKFGطoS>RY׬̀(enT+y$AHCNv hyW}&jՄkVušwyL?=B+74');0}K]l!ٽ*j5ST0:i)bB /|8;ヒ EzS|3_1}*3 ʢ :$GzxAvmrJ'`V/j1DhLjm;x瘞mC#ܙN?<7r%!DJ3|O~S qp\Ѥ؋8 {aݻ(_o򳟾T&2ayn|d ldeHĜG}1i]+lj']+&y]HMÏ&O<{;ěo"FM`SJ166S8nu]o Z r(3P!S)iGhŜ<ׯ]cye$2P(Q56==yz߶(XYYZ08'bú:vyEkΝ{Lw;%BhZQ{.+ˬ.#Di|Dk vdN|ߵ"M=jl&H(4eue'!eiEp\>׾?x={s#<ȓU*W2Ɍv( )f&.fGj%Ip IDAT?`bbugԇY XNFbgoRʒ+=5[3e+QRk* 2JB>D<: G)X-sU;g?E> wo1xzFbqY|"egNEjߌvp}F$i8/zz( X(# N*ٹ===P*r7݇J?Z(]cǎ;v3gPchd!~JxaMMj@W<8r(>Mu @Z{ڝs̖c*aE1LNNZ>rS Ȫ&( +kARmlX>p\3;.igM5R韹@nRK\6SS:R33kZt1s6͞]зVkL-a/?>G;^AČNvwT6~$nS8+?I2)1/Sb zfHQ] 1}&i %O*3wڰ$T(ctZO~"o}/fcISzsD%a˗X[[Y#M<ugkVA4LL8N#,S61JQN[^ѪNv7exaRVٳvM[,-%)e*|άHhy/,t"B A3O>vVdLOyGIFwLvK_7 R%>K3s RYƕ,8ISqΠ#W,Ro4rp~TIm2Pw]L:5<"A KvؘqӬ7ڗ麦Nh8s}ۧj[80_P|/07o]Y3XABM*-J"}QH,-,Chd3:6@6lGfH=5&w]u(3 Pu\Ө{9T`%tͭy $ɢj|'4y9^y'䕟2GQIJ:Dd"Y_[38fiiJB>Oi4f vTU@! ]BHp|y}^&MPbnnb:O?E@? tX-,-sh4۶OR(7Y_/m 4 b{z2 WE < >Ah)eQNc]a'!ePЉ sIjF/\s8uT̢wl)\7#ReLrIYn jC@Zmxܷw}%;֏Ad?Q& : BF `vv 6%;݁G~aq(VkQPi֫hiaE='ng2nmM[yV+ˋ,-xoW1"/%hnp5֖y*]>={KG4h`_!0eiL4wM*泑86GGZ!=sO>˞ =AzujR\ŨwlA2s/MByEH503O014=}$^\[Y30>>`ʪN(\ l٨lP lCSڂR*k VD)MiYhrFjN,#SNa"UL`1r~rc^cG@)iJ%eMu{<ߎ/2sa#GG;yAL$$aNb$cn: fVIlLk$`iӂ1tg$ZHTL8єcc^H@9>}=$QLV M| GX4 Bk\Z0<%L_j!h[SA4z!|#_Z @@1e&cK_:/^b'[νY aݔ<3 #()v<DF.A,^)\ˈݶDIs9s,i\ȡydbL 9044Q(H4V +ufH܌ё2m\((Ka( Ra._btxmfA!W(Q*6P%R2)tx'Kwt?cS`(j Wy\|7Q[_efdzLKѱq6IOoz qM<3[i<\;|_ίhw(TVu8Q;7~c:4_YY^٨ j(̘m}w-5V(hG5sUWv"OʪIsjS*-ITK"\!v2;'>A(YՊXx#D4kܛR08~Lh[5(ZѕiKB p-S"s}rv~) H%IaV8 -5]ӈԀdj6\i~o`2f&80H($~uT\rZSQe^u3/){zzDaBa0$8 PQl~]"AE|4"zt#-?r92lWV+C& hLkzj t)%hi֌ˆ|!gaJm]"I#Gx-Im ;7K+h6[usMS#5*F_0Y8#y1\Qp pq{,:UlT͛H2R$<l2}j|'fLOP0[,--S0ܺqykT8uFں}Av{&E~ԑekvͧٶͤմ-F;g埼ď~y:Nf6edtoH0i[q[GHϝ=˽L!ZצTiH @9 H^< `]DI"eVIYY>y?-+YZY`qq;}8Z ; pr!K'Q<{[o0=sյ匄i ±eNxѡ1Tia8«vWBSޥnXo05{ٕ_-JZ'|qfhA{{{7 \Ԭ`KZ#Gl"(̔mM `ǎ]LNLVj쥔+˘(X^\" z{{|thw./Ld~~n6^&Ib|?+|>ԕkYǘBf׾-y9zm|4}9>tym&o:Ѷ7Ipek]Sq\\{7`ZU!-~7nPlY(g@- VV˶ycmK"Kp|&J +RϨ6**n\H]E)ê-'gYDY|2pG>CX$"*kk$Ah * Q+fcLM s6Z~H1C*F]FByf0(J!yui١^I.$Xgpeámvfm}ٻwp! <ɍVEE~?Geo( x('oyfi0kGIEbanhf6ЂqVWV\] /g2i2z)%uN?4{w(f Rb^iP%*SgaaІDBHq"PPZf%Vp\IoJ02qn5 -3Nm.;6-Tټ԰f ` al6VW#+i:":\&@~z5|gccDžW/8!e ǢNGpƞU6Ǭ1pViHI6ܖTI3-`hJ&qd&5me͂[N9v$;'1Am5 mU^sL:0L@ wggzB$Tw|Y[s9s 8gݑ)K( d炰*d5;v@)I3g  @墔Ba۶m`X?+ý{wi-jWh58C3!<IS*126b&jUȕ SJ 鐥1CЩY,̓y3|׾ȶmܚ0VX!?ʕ^?!33Ә羙I )i3xsMSup\<[Sw<6F^C9affׯJR#( q3RJ`1q $V(O!DQKYGQ 6B|;/}|n]{XXk5fbb!\޻$"ebɈA:.AIZ3y4Qz 2>4GptZ;tcɝ)bo CZ ;-%a;@<}2qsN666 qw@?%.2(G#eL;l <3 Q(.˕^_xZ(,9h%h2]I鯙2WxLLHLkzbgמH'މo~y]{ #/`6)虝֊E5H,fl)qR8lQ(8}4+++(%UlT"gEy$2Sz)W̎=E\!s~m~Mɂ.,ɔ|˜>D9* ׮^V3>739U!LAIX^ZVAHM=GfqHcM ZQFF05؜40ALđl)3<((u<*FC =Hv"(Ծju \.6 -"_(z>) $WaWӪ$h6i[y>mj* 3Y빩sg7wlWR;{f񨔜HWlXB:f͡3=7˿ݿ}[TlLhDM+(zlјZND,eΩݻ7R#sg~+W.F-ʹ@YqN ֬wxZem7jz^&WgN|}\rVo| 2"ep 8 'O;]>/|8{M|Ǭ5)ՕUQYn͇wEdhDxG\" V*:S^)@JɡC( T*HKZcANfw}hmdR&G:W8e4YW5i::?p Z LFӬ]+ {Ƿ~{ sM ck:ٳoRċy9x'8q+M; 8Ei 6YrJ[]H^vB#ǎVY'#)YC$Z%ί㏳{޽t\>σOsg&an_;+8zKzswɇcu-Ajv tٟmh4[-2JiO~kSN*?=y9ylxGgGXrg9-_MmRG(26kZЦ %\=4U$ɄM=L t#+29~T&M׵T3ԡTC8i8XFxf`BY!LB"( kxgގBͦSJmu:)h~Ƥ3aEUg٪&n--lx&-0Ћ,0ؽ{7GVK9ǽ== ݣRdh$ 3f|fjNfe!WΝ'.@\|Aku8m Ӣ%*7kGPn'~Q[gc}zعJK#ٳCTh4\-N7#h#w ) Ĝ:tU768~X[_yd]K{48Hkx 0 3ڳj\2ݻ@Z< .x.ʴ.Mqxp5^y'S^yE0iXn^y*M.L[a;YѸ"?<+˫H-|")rqVc("D>\ցxn>fDOo M>o6V o);Cz4ڈhMDkzz Mk<\g2$6t J&ܸ~1Qq,,#UBX8@F[eFD6$b8W!_,??OY^Yx@lXu29p\\/oXcYYn.? zz{D6C~h۬,rt0>v~/{R]_G{PQg[8.n'Wim1,d?"E6D`u]N~je# ɂ`]62M[ǨMP9?7oFv_10GgMX"@A 6Ay {1accK= vJu;Ɉھ_YveM @KPR# L5<-q)@;.|b08odԥDjRI dhh/|KLٻ!Ţ)g2?ȾR8X*>fX&6lk:2g~xoP /t;S #! IDAT-vyZuN<ʐ(I*+6*QBd%hm}ujASh[a`-I\*SJPҎ趉 *R +}(UA+#%$1q&^-ssSg+Sb(>.4g4hIʪIHfZ)+d-Nh _pu"v { \H g+Ʒ28ضFJ9**(2coT*|w! Q~G}Wg:sۘiJV.olZu0 LG31 dp|CfyQR(DGrha('_r_G{k34[ * _{Ξ8{4 Kuc|U^|yZ:Ν{8rN.]H[_cL&ҚRD%' !!Y_w2?>ʕ!'&х"$MbyŊv[ONcȬZdbskUyE5JW"[ʥK2}F77]-!CrX;KtO`WM ;w1cs)W!-L| <_GYaRn5r"N#cUҴWnK?'9W\\;ラ|'Y[]fmukŭL2/)Uנ$r'j::](R*޹%k+wa t {#°x['PQoc4T,Jwenl1&aFF'iNёQ]\*KR T8yP$q쥘7VI9ޒ5oR "괹4{ĥQ)FN]=kdPV&B>q'ZA1eΐKDd)T]z}ksR,3/od@(-p:Q2x>aӀv#%,9YdQ /{>o\xoo0{ Z2]wgjj=]:qKðO;3u@ݛ=gttTPI9RSJ aBjVœR7~OdEJVZ`C|ɯأ?Ɵ):;_B>p!:}mJz]<90+dvY]l+W&=uA4ǏGصsCNs?ΝwgރnЌ bI䛸sr_ov˥+0.eb| s2^+ĬG?%Cmc}8'IoܠX.01 .082Fb,$aB^j"M W._R)qu"pIZv!~˗.Σ?8B~{Zm(K|sem\%YO5>088+?z AT(>F4!NbEd2}{pcqv+}TFi4Fi7P.隡8Ç'>u+s Ԏ8Y[[lq7U$q;{_:;w5_c8G.jKfa(-qs$Jg]\QP.sλٽs iJ{7N`jm|դX=ɭ" @ YY[7_%)7#[\zՙNӧQ"Tq4)a=v CA`rlcMR(g~~+Uחi%8?qZ\t>IX\\9iD^#Em2N̚wÌR?pC`#OM3HUݪa8? 5'عc'ׯ]!:dG 8l޸qeNh1a}S][$4ׯ_ŋm޷|?cv  sE:ҍyνsw>ëJ܉ G"'.1h~sOpw"gٷNstZ  w}kWgqF$8Sy4MJCCTGfAO`])qkkYZXdme:i,¨e_%3Te@128pjw tkoTiŢc]>Ib:vrRRfm=)2nId,QI8SǤZ]l %WuՍuVWhKE oa49jE`ΝtҘ2g,}}'Hm9°بs=˿?so+h:ֆ^|9.^93O?P(+ni+W5B(s"1GMPփR|s?mGoinn͵TG|٬jA* 3ٵ{7n\s8)>Ϣbiq#0DV Λ;zۭVVAe vv;ۧvun0~禱TB"D+>cOpQ&vj1<>8828O} *Μ'`[iW.}6.\`eu:,X p~̓F$qTWb`t@T&T$68L)DQw!ѻHcltU}E0s@NuwA1dK#}N5I& (okfi÷swE\A!ܜlfNfO7ivA@PAdӢ(f]Y'hZKP&5uI aX`ymRsGhfֲ2 ?QMפƱ?~[;a28-!"+H ee;UaT"[h Hz'|_ӬjU"&Ƨr_*(k.ɳޖ٬m2{/_j1wMe82c5Dh˗|1>Mj#Hm~7O}?@R/ |ӟ`;4U1V%]-BV Rv;m6UNZՔ$)aH4bHKVew"( dXbg 5׫L [K[ v3R(®aQIR0eXTR#NZP aR?+G.|U >OӬLz? D-67I*@G%R=?{"XmR[, WNQsbk8}}}>3iuF LT47\clr,-a8U&)n7E)V$"?]Jh#R9rjlyI媻ςd{9I^!K2D5f̘0Y.tLnα÷qj 8GK o:S,!stZ-KH:7r,WiR{ EŜ tOpth\+-I#s.]bdr]^? šl>&BG|5Ữ}oPJIv!ZImıs!FB_yͼRS.O32k?9$O~gbdJ>!?T+=bldax/翋V)aYlSԞ-k9EΝ^̙]ǚTJ(tayCRATeG(BaVIwVK:08N|!*\x6s؉v3O%,TYMJֱu qAz!+bb[,xri 19'4[()'Y/A(8ꂽ{[(RZ,I3 ~lAwB ^V~98=w YXʙ3QolhU2TM& u4I1zSlejd=;WgN69"VLf!18AҤ,g3SZ0P>%>';!i4[lNӠiW.%$qBF 7-z*Fmp#۶mc2U3eZ%P,SÁ못nFeZ9E͆ $}Tk\r%LA%ku0rrL; %v|RIӔ9μ/(ck**)NYIh6Q&y6VR*"c#cWu_BOEoHCfSLO~ >|n#O>>yWHMWSZzaNQ,T9vZ%Dй%-~MD+r믝= B#}ZV)-6 4Gٳ^}$}} 'rj"3aTHFI}fFmc7x W._,k#G;UtX^[Nq39O?sbf*( ZPY=Gi ߃[{oڻn^sv:Ľ  {IЊ\0DZ{ֿ\ ߟ1ⶪ}8:6oyR*/42?7Iʉ !^E("K+>v'﹟{Ʀ2:Hߐf~W.qDCS IDATK])0.(8lfJiZ;vpQ0b~7(J/D+z*ܝ4% ®pR6 Rp(&ƷwsPޠbarbPA%YZ WVljr# ÄKmE"M\F%֟Ib@ONqދn1332MPiB3NX.Q,/ѱQpkӜ,X@:~26< }mkp߶Nt° jM*S6:^"Ss#pv)?c$" TF'9tv#כҪBT A&a[ژBf.VOp/dH@V!GnY=H{FwFMs'@҉ %4үm|@p;76P&BQE'EVWWDpCDGիO.B8"JI5ZԊGFQXQXBE:\hQA7ȥحAwŎ86rkڼH^A044&Zh+y\YyYk *8D_5^5?{"GMlQthvK@4;' 9 QLRUVnlaIq)$q&q;Ix>^NH}9ffx ϱѬI.k W6 гgKHZQ9I}$3!q1f f瓧;ΓTJ$&;MDAr4<5($ K 4.0>]8t۝mw302÷gA4exh_9tZ+:6FCPm_IMq&\gem*I腧rM7 8|(&r?+70>_vSne@щaIO~Wk_;uҴ1N#%}ƒqՔT 1M ?͗~zgg.] YOk{6LjeJZ7R_!׊ }EKNcC|>{~@KjWݥT*Q*XY^!7Kl%h-iFQ4hQnjcRІT8008RS;v{O<^贪#lk:~u}%2^H&֣5oK)9eDamݸNBf2uTLvRe+:ٷw?9ͥKS'H4ןw~.Y'HMEqˎ&F(i*Jj*pL 8N(9vqfff&Dh29H\V;M·@\>OSUyi Aē_y'?/%.DIq:b1DVSphc4Ɖ<M#t-9ٿ,n45Ͼ! xH]YgK dRfC^?C#%uo XJl} O~z~OX]#2,B tl=c%*'>իWywi!{Cv"(YY^ nnxWǐ (YE-eBˀ{GμER+ @rR:(UHۂ JXPA7( w ْΡr5LVXO缱dـt؝kRJt)A7"Z؆)F.vu2nRϫ\~g82Οfcc 'R>r)u[2:2Xzssׅ}3lV"5 IyqbUXĤ)_S~@q!gsQX3i|}o0qn.ʴC0j(kxBq;KtЭZIط X{F3khnr&Uw1։L|dH3)i'Bty¦gpa@$ Gcq~Sg^"NbԺTmDq436  C =IʱۏQʕ,ܘÙ̐I0NS,U付޽?c|I# 䖸YiC}KEZlic< |_zI""Ua[_[g Ab#YaY4S |̂3 ή%ێi#DcAvfJ @]M'Yu8u('*u3,*PH2uK/2ݎ+u6͵ٙ!Y)'qq[6}%zƉ2&D@M΋9]۩I X$rMskk [6~)oVHLm1\"4C[Ɠ'2PJRR, 70@kcFKIP@'eНzMTmA9pN$ahpV)3FU ;Qk 4{'.Ihv T(PT! :pP)Νsڵصm)J Eٗ=ZPbxƏmYP2= .0 U]{l+W r%B;H 000IY ua >FGi44[-aD8}q0Ga`6Vy[:&eqCiJ JaP4Ťg!I-ômvh?=%S묖8OPy?ddd'$ݺzĝDLd$\)Zv VJ[k)b蔷*]$8Lq6D&T(3޲Ï;PƝw6Vתp)&IEW\*b@J l(p/? LO&3`_t=vx||V"歮(PJwuMl*,0}'NBjHK?zc9k$q2ƗR Q]{(Qo0kf#K&Jf&ZyC iWnU/c^{?,VOR9@hkoG+oʐR+uέgx* [ >@p)Nsg2\>Gbh6;"{>Z:P.׷Bb=}2qي*(l7?[ak1V%0P(^'+BP8rbޏYZW6ʼnf],,a^UK-cIIf2ߔ,ͅ4bi~ۏ-TНcKPI闥(9p#KE/fQְJ}}]jVV䎐jʷ:nJ>@,ZV+er|IFB-icsi;,..ѦobLvQj6Iɭnxo8kXfFt󧉡Ih46r*։KV"/c#X(quJb1jBE~߇fbB"1 逵8 $fqe{06>`i)O-.08ǵk[;e뜝B ְ@2P*{4IBjbS~-AM)Q+WWgSZhiڛ+_)f``V+dmW~Wy?/Lb2[Y`ACGNF`1I**$*VA5):@MT䎻BYQ\VEm<|ߢܸ%o R_wkCswҗOFm m%iL&$ḃ?1RV }j233˗D{c&5>ͦ%2|{BY}3~ i**Ƌ90qWX$C) ɝQ3]t1FgBT$o&u&,}crbk~Bqgjw3wʷ"}?)Imcյ\СC|ӟbyyI=A;90J" EE@DTQo?9[c;GjS+}4uҸs1Τ>O>{~#X$VVi$jIZJWE {9i}U+gx9%FUd"n #ãIAyD2C6$vIYx5O\W &{0 HTJXD[ I\ȶ5N_>˩ 9?{vCudez4`%Ξ;*QL^*$zwқgU67`Ƙ#9RҎ([cu7 /hJ2u(Jk]g,ouV'P*XMj c#K%oT-RiSVz7)c-K˜:%=QzVn&!/Kj\J,ALj}T7ꌎ*^͏dccz ps6uVph{ތwPN#JyWx 5N' fgd^XK9J;c,ZDQ!'T4*Md(.ޱzBw+dWa#zh5aT`phv[$M>g ?oQkRuEh~KܳF!QlX^Z7v\ͶKr-f.CLm۞{IEs2NA(;{ dia.U( P3? Kf/fZkLK/[X.ē_#l4<sY^]͵uk. 瘿>GVYoxisv}˗/B=Ct7ӎSCJHJeMPkr5By~LőڝVfo\eqq^&Yˮ]s=&eiETY4!>GO`P;}ohhNI9 xʅNoNBq2<<,GZIyĝ'hyMΟuONs'Ei5 ]y\~3XZ_!51m9ȲcwObcGZ rb'+%iuZշlfo S&&im4r DM 0P@G\qhvLM3}S;JӉ^R6$SbKٽ_ٳ4M$[S+2]B!)"~ "TAj+` !ݏ}P6* wQ2ΉxMTͯ;~Tl#v.+ܜdAf{eomIJfD:U:q@34<ı;diiHۧ>sP55U+,a(d"aJ߽s{.0A'Y'b"[>r|S\wcHӔ7nePG>;ҊM֊bYTZۏ'JlVZQTX*12:R(r&5u7Nrn,8#SՋ |A1$\8Ӽ m('CkvNaǮ=ٻeZ /#ccZ|o9؊,t'0UYɿi5//| ,^ E'2q! V9}'9t ۆ8uU 4Ep}o ]ڦӬ{7ӀCT7-U~"펔Xu(bO֤(v-PۨRo !oܠJU^^cvv4Mini7 DjK!e'MbRFgt|r}.ʣŠ((xM1ZCCӊBEm}k.X#Iq(+*"ZXgUoѬǝI&SYp(ou6j51F2;ҵ'6K%>QN:rʀ*++i6<. 8nK[cj6aT#ʅ2:Ā@(h5ԪUu3!$X+r*IVj]$bDjipG 9](*hX]Z7NQU>Q(Ab^ZI]jpsVD"Zfb,гShRiԁ,$28qƙ6(>eCy Tƹx IDAT2c5CXjhe7t. γeaԛ]?|،Q4JBLL3ZD4 *4 Lҩ7qVl*ZX̗OEEJ294&MSZu:hg 'u6?EX6 kYɗbjVz$Lv8;dm}4i`iyBA\z(q]BTbxbA 1B]j%8ء5ct:q7m+pnfPG[z(|Ӷ)9`Q[Yj$cYo≟ c.?wؽtG|PV2$x="Q G`] 2eɪ[ʗWs9WC× 1,.\콣,39ޗ^U97H@ HAb(*pdKZ3^Y8^쥥e{lQ$Ғ4 # I D 2htՕp s} r{Ogo7k/DRK%(3~T:A+LONz34&2`ptQã=4drAԒU_8OQ˼ˬ,-0 Qha`qR}p]\/05'NZY#:I:_g/lo^)eZEQR$9A!le@.8G#u \7S%f@E*HV1.EAǮUm1-.J2=TbȿRHR)ne8$B>:-.BvJ6!q!1@Nw Ӕ'~& Jj**UӐv2NV# 8 G+Ҹ!%VV65ĸڬRo!zϱBRNqjK0:UlF QfK#a5x8Ɣ8T5 Z?ʅgBEQ:v$S݇F 0hI nQrȘI!u!oV&RɉIҲE,b;d/Ǧ 똺 ;v CCٵm3@3sdGxu B 1;v/}vVPGtaxeGv#5rMNA.W`߾CVGF(Y]Zu=cXM7сBE1AG#bI'!ǡͽƱ= TS^D oxϽ9AY^Z[>\'wsqPZ_e|rf8feq,_$MDdfrjrw15f+ۆ8,ԡDZnqB[c!98v{Atm Cjcqulj߱"YcnZ*h>#cp(s3ۙdtlY.]ٳ Ӄ\Zj>FRjCTt+zXk>SDm"KKxGx?~Jj=%cִHVя']kuwOj;Goy7ٱw}G-;喓wsyƕ3e c{&q~0`mm1Hɐ59q!Z6Z u7A v!%K+•+瘿vzDEnGed39+ Gdݼ)K0ȻEY6%P(ϙ*v &'mDD&v SuETrVqwm.jtоRh4Ҏadz}ߢߣѨQLcK\ Y60=Lɢg$}j(l +UaCdyyIs'3qNhQkӊ> y:eW;-N@iD\U•dmoprse/VU69ѩ@ɛ;$\){NVhbZ^x~zuSKg"9oYc4eOzW 0tIEyFAE4. :AY&bxB280>1_hX#9ro\?\G'd$}\ԸxK+ حq]@]=$u kXlPG(lS!̡ Z(p ̣XX·-?8~%Iss5˂VA:.R8؃OH>d}v|>v iY_ZA;#NK)$%1jm8;| %Q4*{ognn/;MS(_avvƣ'9cc\|Ņ84;HB>#$qhɾ:ux I,d ?p?uTeNւI|[BI5P]ۛHy7oAPJ122ZJr}%B[ "fw{~!Y]Y`؏8dG <g1 w͖-s䓟feA(08:5Ν=tzrbZ5IJ!W^z'x xW||#d|jK.81tbBTB"Hbzvx&.$۽Cĵf|oATVe*i9|a Y;GPd86Osc<ô5& ԧb8J6gƉ.G琊uOZLweukW/ <ܳ,1jؽg+++݈4|;z7wÌL / &$RUL !,:4u(4w/kk@kj5P8.^ov,rWcDWi*VUMMUF=əi=׭kj`y/*DIE~hĞLbxhbX.'DoX¨!ڂSS\*H&uoG >7'?Qr䭳S+GmҠzA'"c*~ (j%2 S*pru" "Aǖ{G>Q&')W֩5jF?6MC?F!:ADMt"YǕp" ץfiYlK:'688H&IQJ4M77D8$ :Lzqr|2%4LpGG/<8|+5o@J40-//׿,,]3bh }EZ/|c:-N1:k(c;pmN0Pdt`qFFfVr2{YWWӏ +bHAip4zÃ}p{mttR ҥ-/?z80Gchp<_{ 8J Dh" PF364#:Ct^@>!_dddZFn#i霦%۵T Bv!`1[''Q_OQaN鄭˔tD*B;kK+t145wN!I-޹k'V+}^NM\! p809kF7)ʜ?& WitAF"T۶2=3CAMw(k*˫W<;M(^nPGO¥Q'J֫kFR9R)73:dsY8)E ,lgSzC|ц3y*DbX,RTl#" yى=P*Ѩ׈BϠb\!FF㇬lh4A@XDH;-s> +i/RnZ8vjw(+~mD0N}lQ(LuC(P,aEӦިD 9o}ou~((|Yg9v}rud=)r^Ah$tU(VѶ5-H={X[[F&so :њk,_ŷX^^lmIpQ2= K mgX)Lmh5(deeX,,/iwFaq5*:6MI޺p O>$7לy%fti٢@wtH[*h?t U(| #\"h ba{@(,4[oj58b_?Dg^=5R:ġOem Gu\uFȹ<~ʕ*A<&W``pNvNIHuSO>M5({"Zf_U{GKO8w5J͝03^#;(B鈄922v"*Y{˽wr.S!6d[ac&?tOх%70dCC’] R5¤1:YѯIP ym[~0[aziVE2֛mcߞu/syEaVa bvԢѪ‘f;=bf? Ez#޴N<(ďRmd#)jg1Azi_ѪF!㵅+O_Bh4s>SO=AR!$cdWoکV͞xACa̡֚k zV+5\:qё@_o뉍u6} g|( NITn,--P*ШUÎm{KZHKd/qM>* &J*ːv|1Zآ6Řv"1N•W :>p Hp͠@v!Кl_0 7h6Dt:F51=@/y0A1R222k'Hτ( Kıbߞ=,\r]qV3gR-qds.4p̗Oy'?FKe 9I@,4\x-~p7Ѷ hsj5J>_oQ/z"neHvN..,s-~e"tD\  |?) Rr2'ps1Ͳ}fzR1~TcAH!!}'p@p Hexj۩W# Inj`3l&n =g}I) jUs YQZ+W.q˼u"?}7{vڵk"i:B!gڦ?2%p$J@Ȑ@4077≯j嫗6KK\| 㛹tJl= L M4 NpF~wKt:> hbŶ;3-V<Տr-HΛ$Ro0 Vi嘭`+WY\Z`imz@>GQ`3sP.h5a;/ : mvibҰQfd|VNY7U @:%i*_QZq]@eEQ IDATEZa03Hƿz~^ _Qq<F! cw=U~N@>PK5 k6%%$O=bHJRol喓8vJl⛢?w9o>Dc9xfi"N$Is)@HM.AV:tV-}w|kSWF 0442Q؉ւF$'0Fu0H"Rz Nt))\!]^n8z#zF$w s;gY/Q`m;݉ ב uA͜T_$g(_%%\O5f4)211r"Tls\rBǕ{tiCDki t-,^5,^|_|Y(4$u;PYA [-rȍt';#vF'e8qS3TMb!ٱBq;u!VVQq#9յAYf߉F$@6 UjM) Bi2Ґ+[퍟Kd\\iPHyW 2alݦ( 6SVCrWv ϶mxk!se흻vkY2~=8mHdBr%.# !X\Zdi Kp,kUc<*ɨ'eݔ-ecug*#cSW*&m6mh%<>OQ:>tLF,Rתt6PBy &acA!&#VW :aE|' Gd1|uie'\&`@sɯ|O6߽[ $V7|K%^{I#Qkh }9B.jCCC|f}\x1[ ;!Z]d'-B?܎9^xYaO;d ABZdY<"Qa!|'NlwZ+aǔ]I KjaH} PZ/Ξ>MeH)|mzǡm8 lmMA0 cg{_oqq~G{>D_?cDbbbC1;Go\kD3)mE^\C(P( ץL?4 bop\˱ã#([gxOj!R*h|CyIhmc,Ggye:R+K<#zeרVidË Ƭߣ(J;7Jm)+dCcvnAL_lx'fp b˫M M[x/L:qvepp7ʉ[Nr㱓.9=CY5vCQl ^NAaŰ,ʙ8y$9b82ThKeΞ9c(X"`fQƶͲ!h; 230Rt!̧!? p;we(։GJGri}C3'ulsӟg83OP-8OFBW8}~4,[86VJmT@:be4 ޞ W5vVn5PV$hh5[6 6]D=:)g6BgϜl4CA2M a*lzZV'!%zyB;x9+4[Mv )-.\񙤔hF9qIWi8|R-:6h( wSON5GnaڭgˁG6=ν˵/u5$9ЭdOqI2ν΄ՠv&bO`d]l!b<~uȕ+"ӅfZ.-,ɑ,)8]}z9PDA+AO !F!:uX]h(KnuxPfn,_^Kڷ "ΙKgY^Y anN*ZAnGE0 og}lard|0}sٙ{;09|s#WyKsEF-zimNyanB.BL+ܞEc |\.F7Y FhKǕ&#ުYh8!Cq@8N|n00-$c{=\p1uFپv<"e␘2QD ffg>ط(۷fǮ=DZ042Nڻ2Ă(4C0;3G@E&FodN(bSb_za$\&ɦ{||8SF&#&Dt~2Wvb\VU,\V)p6o_L*)Pui"Q387k;D0qt̚N`]װ!294u\S}c:W&K/5._D f(%ӳ;ؽ{`rtƦ#E=?$qҮDr x?+Лrs{F[f4H+[HcǎYW^6HI&K'nߦ ۹ZEx{q7gn7].}ZeC\7K"EAǴVq.2XiL>oǖWJ:m_d3h'2k ɚAA2S sEnVV&`ZB\FJyW@owV7$&|+[Re@ue3t6ZP1k9e~1ds{Vښ\ȶryTz2x!&$lKZ$h5m'w|/'OPa0F2Xt mXȌGW\y\'O4kYVcu]C zkf<ͨcT@G)3+ى1h 5vCd1^7N177RMCC}Kxđu3đBrP("ErL#ex}s}g}}{Џ2]Jf d88zqi|mem0i3s(T:x 'Qŷ9vE+E{GF!O<$KؽJJЩģz砣a.??F80ʥ,.R.\8d4ʷZrc!,/:Yj7Kv!뤱wrRP M:(}1Ͻ߮PH g K\ 1";E,+؎eTbdh2vW"S{vf$oA'`۶i~2+Ha86.\ i->twoW^~ŅLx׽"ᇾ"6++]4[7/<2LLOwBg{JD.wxY.^48Y;믿겑[ƉR&K//N^;ŋ/>baBJwkL0.^q]p">FNi9}u})8s3sLMmcB!?~ySoUi4+:2/,ÃTK%FFK s'zFnH_~g1:^ɉa{9?'Z+&JMk էX i9!R|SivN̰!>Og4A۳^7n>6_KntwBvt+WE!#|ۭ<Q@R1FX]ܓ.λCο.S)LfI ˔V+Tk8@kp{:3煶R*Wt<+hVtaemEN%NfyTe2 qo/goI\[Ft]2ꄎ(4~!6e}B } / Zppnҭ&캛O~ ci^MF:5R|FŚo7p\a^(Dz6"2^#l[d}=i4ZЪF\]Cxm۾%tQ6mv1H(w~7C]owZHGJ06\ !%g?o~t(/9~&t)</{"q7tt97Y_yeO'h bB*R#3kE!_ӟ J|A{c+dwovH7_U/٠wrD40 }& Ck D871A",BG,ӳ|W>Eiu:/<ͅ5>Oqmw O?AYp6fPE<ϣl#$Bhr}ye``?.Q7pD?caZc)s(%f d2mb@'WRՍ 8B8m(a4+380xc6 ^Dw$WlvMj*l:đJH;@٦^!ۦC(q]ǷU,ǎG#1</ͩ_76Lh[kZOqÑ,--sYNᚏ88=BnxIqM ڸQ^:8MzqaƦFhb8O2gΟ![D&7`H+tdNM-r)$g'THy٬Att\fgf><ӏWKdxs@o}tZ\~q|_Nf8ıc!V?Ap1vC#v-fy穯A3oH`FqlO F(4+!dbȷIn=1j> IDATn(k&r%"7ָ40(8vPFH fglj(l!$+i]AHxOak)RFgw1>m׾U.{ b-R.$@ )=愊qTCB" 22碴CD(U"@fW^]1k0 ̰}vN>m_ D,2̀p2Amb4I2@h\/Z0<DE2߻˹|;q`e]lZ;jM-\d&CXddxB}GG?ES`e6?o:Y248*N煯]ۤW7^?w+/D1>6jz7c׾}oie%I3Ʊq =TҌE!QhQajLP ++enN]afJG ;|CO4"BG>-glwbedƃ^e4ĥ%#'僚0!35#:ڬ./<s3whۇ8Jm~hhFYX_aH1pL߼hul;эf }ܸq[P^#5E:+˔6KN Q-]hE6H+y9"]t,9v ccujGC0ڀKj=n+&$DGNI 9p`GAVAGîk:avfH===TUItrD;=cۈ^+CtWf4"T 6DJ76(W&EKupsѤpVjlAhKAKJH65[y JX[(c[-X @ì&= /">0s!QYBরX5W~Fi)0RhTE!]<ćP&MASJ$Oij\!524bE4DL&K61-mZE 9ٷ??؁CDDhU *sk,-̲Y*1lޞp"T"׿o~qtpNJ)}{4GQՈ#S@gf5Z("~d@eƖFNJ;\׆bӋAS0 XŚlSwM>{uekRX38=NnܼaM&Ϧͽ./_T.ɸh[BfܸD՘-: B-ISIQ>r֦_}1B"sEaV='~DA1ѳ0s+ xvg/#={ΰHxhG?OZa~qϯ32ƥY#u{@^ÑN"+Gܫnk(bW0Y^^ꕫ6Jdgۍ0 )onrW5&}eVi!ƈƅXAD\V3y>+s؃dcc[ŹY @։ _?OT2bK>>}Muw~ж#OGo7#øCCqMj56[ADa?/q n;%b#eF `҉C>iA)exYSذ͔S)[84Z =ѭ1֤:-B$=Ex#ǥ;qs LjuMkN&R0 M5Nڰ e +iaZ`҄m$ 1l{&IOO7A`eϗ|x;A`bF1Awދ_o# !@;c\&t4 {VZS7T*h7$"Tщ=z#{r#qYwo ,,s,swfXO+ Nqd-ۯɑGh>A]Fô!Vr+\!OI1FxQf\w&"W%$3G ḎXbKD,"%(v&a ɺ+fesW^+[oQnEdru fJ<cݷW^~uЂ\?0O"dY[\5 E6ô}xb03=Qa(B?~2 ٿ^z}koG:|gfn_'144ĝ;q3Y]35wЎ6 Yydr:/ID](sYXoM6Pk;}g>͗eTW*U?8[ ZS=0;7RöpvPw_I_c]j*H|lr 8J6HH>'C="@i^qnbD5n\?TfoQ8Т$AhEn`os~ꐷ9r>O'G9|_cXl,N,; GKiҁ,vg$X㰷<"\)`%q̹AHl>9}J$vu6ҲصBRX$Ϸ3Gls(\ !o024A|ٿ[:ZkJwN4qyWJЦ%Q3c699 GQNal\igv `Tsm}HC þ5\7h)MMm?i=p\ .]||g>yOW)OymhK!])X){&; Wh܌Ctw12q$ʛ#Dg)LBGpŏrCsRI'kT[(y()dT }]]],ftdgcUm/Il5u(!2YB%)t{A*QGh'mĄMT"4$uqHIo_?Edc˭%~@:"Oر|鏳f 7ߙ7-pۛ=/ 3Lߜbee0,TPTT+#P~#C| uC34<Ƶ'#9uE0(Cb9IgLڃoyQQl6reoqmϒF}Ѫm oa.2Q%E J+FZ! n6fF/$lX)qa[ P՘}KW33;M5-.S{4)nGѤ@_B''ҊpqVWWU51DMSi]KEI2 B@ BVڜ[aMFQ@GP߸]ʦ%k6F2nar Hhzl|!KJ/s]{vKuL 4B $D(s 0H/8MR)D]Izgt`nN2u <5(ٴ nˁjmAQJI딑9{nSEޠ5 a`۶r5ccQUA۾ }c ?y>å@hKHa^zboUVY 5-`k@04:Y[[M2a?Y:ꅟr埰J>|#Ԛ5V|)mp$H?-)['?CJy=hkd{`#A`/p2mH{$cj2Jmа΋F}gμ)n^ R;FW(島ȋ?/fFiu6#|?``p 8p>f7*F_I4ui4xG$ôZ.߁+َ* Z?cADK.AɄ"HHN8:F =|N048LI<> m,e;xꩧAys0HT4S@! CÛ",`5> >K T*%^Xe<Ǜxƫĵ&:V׊(N8V*-։B^xrנ]o }ZSR&B+ALttuw322NR6BIilȏ8x_%+ƋRN$E Gzbc/>c9a=gjl8zܙh67a2|'?`}yr,ufs6zV6!z%I(y?`fviF3VܧP(|xI6&˒ZGX n~g|kdr9z{hzi,!Rq̋? ?x\:*|RH60,}}lll06:FLsȑ#ԫf~۱cK- j ./K6ɟ&DW1hq(o3:<@ec?X^ G!:IGG#L|}FN^I[f]9q]{Й/ngedK#9SS7,m%F@y_@E16et8xI\|ь2TH C&EG+KlvH'e6gb^d8q$|!lid%l\-Q.9D8#<4ek5YY^7ӷqmM~Y'h,..pRV" tޒ!9{q,A R xod 'wge{ "MIKjFcZ>r~,,,0ϚBNn޺m J4AG!.q#G8b-ЈcMF*Rl6Y_]hR*8KѰcmw?R{)vSeph 7G2B N+!ckͳH$QRZߠ^!RXNWZҦBߤ4-0#A.QnyM'6 )U:^N4S'+y]WJ%QιH!dr }lJG~.\<^y+,LCdhұJG|Ҩ8JL S2ܞ \tt8ubaa;s7qe}DGHV72\!ˑ((ȸnlWm-Ɛ;m.|ɏ_.ܬi25}G_`a6Y{*wr_1BK'zd Mzz糬./R±\(=0HD~}U[Jtuu'KO^xOpRVuҥ7}@| I6牏|jP_?/_+&Jyi+:HS#.]]]42zqytHK,MNW5E!f8Ɓ(tQܾdlwQ^RY;CPn2K3n,vsQ~sTkePIm G[=!r/Bi}?ǯWѡQn{HL r1;|(GQ(099Fg6J[c$=0$"DXY5CK"}SRcNóEŵq; YO$칇{XLM_Gc<}vy]@ꈨx 븦"6"RQAP0} r]zdIl %pB-4: t3;.0SǦA۽ѥLFu]:;;Q"J-ktOl'CHGa+2 QX'=h43NKx/Vt=`qyPEt>u{ܬK|ȯ* ;zZvGK iR1sFF gqaE߳=UQ6 7%Jqts`)!9&nQGߣ4-h ؘ~ B(NX'cӦ:&Md!\tZe0$^IV3J%}6mZD] Ν;K/ȭWX]'= q(;yQhfg΢bSp;z5+++[rɽ5;۝:u;] :=?˱677IBC6cjzU8F%j$-,1?wpD jB?8}|'?ĕhڒ%! @RiT՟%cˬol=08} ,--z -073~i~iϾ}>r%ô0c"`H"L2bg㹝(esLqrD42)Bg'}f_ƦfI%IY8masŶ,!{s8#㌌-x9u!vvkKhT3BC\fyqma[kѮ=a?LR"b/=K%A5ڶ5 !"A4VV; $Bffgh4*]7YXtwO.O)ϙSg`UO#:qhdttq{Խn̜?V/QZ` iXlض!'NJ LKkPAi4)sry:Bsȶ8b`p^z FB);/lYkUѨ E%ԚXÁyGb2O\-l)dy~nsJ*M(L+dtrjMؤNYo9ur=#hFJ^㝏>ƞ݌L293gr^}M_[g2ۉ`;(2{7MhbQ:XlXXgJHƴ8vX^ӊ3YHǖQ0^Yk O*!XY]4&ZHLBP(7Y\au}c8D;ǝYaRaLaHFѦBlʕ+,/m>Rv0mw;cb(Rolq]|5! L F^+'l#քSq&Ar;,#+^s[g $[%R !юP CϮòmj*.^Z >J.V1%*׹p &Rh ȡ4 _VL蔤|.rkKNY920W>-v}d2x w_[Pżyk&-܊6+7;:,y60YÓd#k\|rtagY::: BII .bO/ LY3&NlzMDus:G6/E68$Ţ8r=Dp1'N\/ǵMP Lc% ĩG'Dpw(+:2hf3hhR #6&ǘ}q3hXh8CCelV hE* *_iI&SЁCRY/J LI2dV6(KEH0BS4h"elrb}$:[`Hk@׵b[-Ul'ہ] qt-Y 7A%JN%5ZAw):B"OHN<(>:6r$"ZVc: ~Pdlv?Fg?acP18v!tDZ]~'g\2~hTH$q. š; DQDwߢ(R+$S(ć>NwJ@ ɫ DBDƘ9d׾Og­&/E١5!P dd=p]s+pu* Qĵ++BeѸnjBKn_B$ Q34Hq`}u/<נ3໳)q:!N@splffҲQaiG>O6Ѩmg̏iT " +|Q,gY,h4td^*@` y{9^9ęhgTM%9ZQEkɳg rMοqصki`KZDaDJW1C>^w(6!{b/n,*W .~E42Y;)i; *:N%ٞR4!dCH)٬,QZ@ȭ+Z{gHA)X,kM 2Yz **X,P,vRG LGDq`Lmq[*D #W+ϿslwapCo ȥA Ӹ=)Bd{];\&gFnJ)o4}c,JfD+hpct"3AJ Vt P쥫{gdqqtttv,9 !蠷oN b(yR,qE|vbu5^>GEWWn\CZN"!Eqouvvmtή"}鿓(zz?P8u+KNfC+5VV%E*JHoսox2CJ&(Қw<XXXH'u'l%Gxwsi~1|c<{,3}zĘ䠥Ln2*~ ܸu_ʿb={UqZWoN{l.Ky @=@V'! »G $zau11Nr{YXYff&~SZN*ܖud`r]lQLHl8Ȍk*% VuRiUHU̤m*eSwCV8Fu3هdbdvɥ7k8Bĭ5me7'ay[$mP<+RYOV5Q}dPRd}d|,i07wUYtN ڹþ> -#X( -mo 5yFHjJwg9byi[,޹CyD^+Oia*D(mjAG31>0JcuS7ln:t}`l8QD? &(mӅR;!#̧uBpY>@W]RެG&p]\.8z ?a` G:UH9*HzWHG|X|vŇ)EN. l3 %bo%މ&i*Z/6RWvmQijeX-sYcbptwd2 8cg8q;Ĝ45h+MN`V8= w"E |5U[/aLaFGf_,gϜZ+O~lEtQ~`s)&z\J)ĨM7oƚM)u=o\}>4|W.YD>7mj Qؚ^[EA:l~:B[pp3SmcR "k+enQڐ9?+004 ^;2/l%B>·?W.]^.Qy핗3WmKa[QR)WRcyJbw{8q8/^$騹}蒵q%ˋ <'8y M2:2N@민LԬ!0BR&5R.^dW8'AGḦ}R#s:B0g?*=&̺|[%8rIX--T!:@MxL{S'cbx{092tbuwl6q"i()0/,05uFȬW[T_<Ã,,/@wo_SeQ:aZ`ꬾ+ l!o#o'նXD?u?>NniHVhep$_Kܸ~ESM8 vQHǶ X8fl|84*37r"r ¬Gev49R>.iWgnr`޽^ZA&,0vO0ry ;{?jEbt~4i(GXϮ]ACTIޏ_^Kw:eY24<ʮ]F:02xC|3<k%J>.K`Nc b*,J+8N1Fjf[\#\EiM6p̣LN#C{LpQΞ}{362IocG14<ū7(X]"m#mjaI{Էs{_YXAD)Il):xMK.PVo~Z Xi[zLIHs!r.mK,s ~ÌM3=seB/g;"+x18MCQPqsxɻcm=LǙ3r~dž *W7 6FSAiԭXtN'^i]Efo&#`&E̡oӴv+mOAզ+iXn0v.[%@K'y},ݡb AՄͺ ! =ISF(e[^ck[RІbWКX BMoK.^`~6"R)6jvG ch'G!K7K,-.xu|OUr͚[neVBS.WY^^aiy WmđD)Q AE?F^^+yFV:OLsknvZTteƄ;tG@&cyu 47oNqyKmZ9d2 t\N Tvs39FF.ݽttv&vLy=N:õki58ɱC' 'z:ѡaƆC39"&wn"#o Al{}sk%m[)%SlWGm[kR88ҥεWyCS,yu#\[m0^ob@Nbn =SoykJÈ0) Mz$m@ Klaht/u#ۑcd|V Poc랿s 0"LZkcmǶ=qK*75~絞C02$/27^{Yg?|7x0q`jfr֠Z10:IWw?#Fk_L'?HIed L`sVVWVxMI,.sgٓwgeA ׮]$S{ro.տ+οĵ+V^XvyOpiٻ~Cz~=j&{i%duG.'{IF<fTu8Ds4G~~'*?~z 3JB8C8'bիיy8frCw_/JH)2<ݽԹ}Sj[9ɕۂ& o 0>1nv[|.7::Jkeo]Nr;kB R(9rl/AT֭,,Ֆ֓^鈴 CjqP\.: jm8r" .i՞msaS1϶kh{j-mz4B8<4l:onʈ+MY_]٨Vh%8sA> C^"k_Wk,Ҏ@ۦF:WxH#gѰQbu};GJ*XT6(4$E!Z O1ZF6 dsffvȮq8˖#Y[_ y* fs͹'g]Yn:Gg dK )lkS.Э\6A`?MT đҵhI9ls䓴DJ82C)Ed2z|)jB[,qC*N_(A06[K_xοzu|2Nx ,m,1_* MYqi Eݻ't"wm#J 0sod~ IDAT޴=?qn.^=p<|ju#Wfbz{{yG~ Ij(j<JS!qȌGБc$ 4s߲č7M:ib k.-G^yiax2\Zk;$+EyDV+Sר7n59ií%bN]gZ'DP4.CNw1-P:m$>7W[gٻgwy7FK0(TؾsOH+pVd3>FVn9zBs`7U k0W RE@laNrbTVvqZ-\~xzyw< hhZum_K<00mqV,}ـ|&z}"yyiN|=k ϣWdx-,Vg{V*t= "I-AAġ-2tp)Ο;k y|! \))|)$@I&K=dO>mhR thh(B F0S$uV `h-]'BB6}=iNt{~.]<8h״jۖD*!"م9غ}7;Qr0 !bm "A;(jY aݑD(hP DlVF8ul1Ԕ<31ׯӪ7\$=u:{t kU],ѬIyNf0-kT1HeI-WZHE#V{ظ~^(6{Gzya](\^ƁJ:yy\a0CiÝ<;XٻkטҀM) ͖= (3195IVLo-O=MN8E|dI0];Q)-¶EH\}%Gٍv]üek,#P/bK|:D c*v0a߾y\x Pю4"QaD܊ؿׯgff?pBJ.ѮAHB;`snXm"&ϓ7N`(FwM7fHWO}:. }|scƍ={XTDl%IZ-fǧ6J]XsZRXN$hvaa4;אD0A.t&B`FCZW̓O2;359tR* t A###Oj /d.<ͻVK$lCߝf+ 5x<Ͽ4WqIU-%DlrlvTSt?p/Ł<:hsq gNmM\I>8!+kM. ?xfILLJhffyX/u&QY,!qpJ+SgϞҥKT*2,砻סlȧ7~&T1;3%UJa?42< A ImyTN屢$ v+صkF#%$KL&ᰬfwyJ;:>)0sYZ&Fmg'(WJaRiW!>>˂4(^gjԛMaزeo:BZ%lG~[\9~i_uH@Fkg^ֆ[nG}#?nu5!uAEK{#bL*)p+2>!kX30f m77'5KE/h01'TJW\k^wxgx{p)N,q\F6Ӡ8Bl*p!r5:=໹ImSwEBs5NOZ/E~:4*ΕM9rE2 {#BvqDh72j-{?5Y_7''-sh*1xi`g=833ɷ_#5_;W|5[MR _He{lk:āHZw7hk4u֭Prr8:m$;qdY:V^?)혙8 /T5!cnծ[P*ʜ{Lŵ8*PDIT[ԐRI`jj*8O?X~w˹9ݟy7" v޳{'s,!'_MXF`AiEʱKEsBN^ҧ8m;F037A"vd#XvGVwrVMݻ.u[?´HM)BHvT*l߱w n fxW#ySv|/l(HdږKR/~{8BO"3 5ha! #d֐y2=0a׭1OgYUڮx=~j*\6Ghl|j = G`strgzFv},G 2:X!ϯW,\>< B|I1(v ^!x}(qC S(p%\\Li"R`|o# ^I]u6]"Oۓ)FLS<c !F)UxѫWy-L1]ɥ5V۶o7L%}87uO')b%#St!~0  Dai~Vj}8[H#=?u`_L6Ke-"*]͢!k,_HȒeAb32þ;Tѳ (Vb*jK:CVvg޻##̦6gu>=?e .mka]{&FoO;*Jdeq޶M{5CYa;[wgph##70=MU%ry~ӿG{KϧZǩUJA6mG#[ӏ1*d{Rt?m^ykaRlU7>뵩m3_tN::wmfɽY$L:"YDԎ,5ʖ 1X'sM S]z/ Kggroɺw`qcpp4DkrB{}q;m^d.ǚuC䳙)>#XZő_マ_%]t~owٲe Ƙ%ݟխJyx7֙Kw|^o#gcl ^~'J4 fvn*8t3Χ=y?öپy7c{>2+S'N1>=nZIR.&|1q#gNl,٧v|{O$Z8%7)/4dYvDZB n!/m:%Q2;;<{v䮻+ƚܤ}aOm! m}"I Ky$jLa , ( !oEI1@Da tɾ8& a "3zzP*t6_-l޴OQqTZnW&)Pm-p7Y#ر{>D_g&iMEJR{>j^yZt$=ׅp- 'ҒT~o/))RUOAVr|K$ڒD3xsvFr^`Tex&qxss! aLMӨXa} PhY Q[?ҡ7,xXB7?>ohVٰq=ȇZ21~ݪp譬aa~vG !PjTAZsmso}_~W_{+.\|{ݽ~Ӝ=}ڢ)`E\ZrDt=eH^R"bXZ׭ވSO]xe6ps8b" /On \ 30)1FtS\n)'%E3đF8V6pglUƏ J[D499/2== FbE_JDZġm9K<xn2ġA@wrLݞnlj/z3p;A.w&GHflڼccz FR..RNs3<̓|oqY;Mʕ jN<$ 82h-vR*}6FΛoI60:٪-/pIt9wm 3;;<1:ٹiKs?"ds'z"Q3 Al߱w~+y5Gؼq~6lalܸ];wOjVnR>dK&TMG0&}IzHnH( BVi\c }?MY׿훶uVk駟dʹ.Y]Q]ڬ#O1>q-7RVh,Uj=5#`|І\8 s-c` `~q0g^s}H2#dHdaHF|pjcc0 a.,,XgӖ]<'3_baaf)`pp0%݈a1߇=L&t f\O?7oX4-fo"RiI+c!lObqři&2?9QB{EK<̷ L FVR7ݢe52ћ˻|ϝT.h) I~7oKBhڹ|snLrI^_NL%t҄0Y,7߁renljYwAm$ƒ 7`"j$ )msIA Q'+B 0u,,o-_ҟ/)ϿWαXڵ K+* ǏR/las3̻ﺟrDhCQBXҘs %yquXO|ah{Z/} pS78{`4#C|LNLx[nF6l7[{2a M1,f=G66ƱkqV7\t#8ʙŴr) PGouĈt)n<;F [g~x/37;o\W˧SݎHO>JǔJ%tyv",1y},Wcwm-JufBjgPp}:oOW_"6fh6ĪXqPܜPA/슓%M^` RJ ظi R|O(jEk2v*SK*d$JfBKLOOsT{Qr9 Vf4Os$3LMMXZBXґmISSS[w0Ϝ]4Goql@;ךH7l䳿9n(Uem 0"= s^3&A6ofLOҌ?O ?0FD8QhDۼ}-jsC>+ܷUh{iQkMRqr_K Mt tck1 Kn?Ȟ}{2zkW/Nnˉ6 [! Zs|~* cҞ #|}֮ۈn{oK/ULyӧzu?h5j,;/`xl#.C1>}ri"cWG**,d;uYSDdnlfp-a;F6)M؎[!9Za3uWZ!\__ڻrhmt<.!kRi{c|[aulKmq57ۗ>AѮ-}K%5\E(ڳK ZKݦV"#({_.#BĎ%eq`㬲2D5¯~?'>yQZ #!l6{8z1~ipTBhN{h酪3Ck)W*VobR= | 5D/M[-mMcr=cOJpv2{\C`2đAz*E84mg++e* y8|}5yB ]l-QYCeM5|6kщzAw$_ʟ`!T(KlK=9lZ OL'y>#|?O\$'F t]wP @܎Q—>FgIiw;oBE՘AO_?fpp/~ ֆSO.@7V3?\.}p(2?*wj 1x#6*ѶP8(e)ҔERqTC$%!iGb(J$DÕW|j뺑(ĆaD履#tx>F|i"Vo5WƖ"OL֧ݬVx bٰg?GhOM QFbJO+ v&k4wps!V𔎣G^mk.UFwɡ[ͥKv|(g@d=~-*FdW2rJJA1:y\FJ;l 8{w߄!fl<.]\FufE( M6Cdl"$|eŋW]t?Ygx/) 29klھM62~msg2:zV Z "#F+07qsutԪN'jr8즰9o!&VE>ˇFKfgmkXij|k!H)\3C8,s1q%F2"@\!U3:MJK>v+\zVN򄅎xWx'zzٙ) EdT!c0FE:uԌP 4Zo{V*0„ |_ srd@6SY0ƹ)aмs<$O>w)i5 N=ӱ̅%2a[ #!;D\wѵ (!R- bEy~FLA~ a:΂MM(b}|_O9+fq{pt۷G?lI5.8g9%a;ZZA "*HFb ܫ]d"a1kĝBEBf҅{4*DQ"u-; m / ΏK %ר$zYGٴebn=Ǒmuqs`ET>+8F{?ߋ2 !{6[VQCԶ6vR'BFݔOϓH_D(;<>OՂQK[ChQ.fcj0k_;s 61;D QCTdf0kauJ [mK8TA3ZZ UD8$cL.ҧYrUs_gތOGd0>4ܹ֮'0$6of5mJeoR!!'}h:LI@Z~jʋ/(s,͡A *I ,<͙HjF&#S`jc ]t>#ҖKda8pFE¥Hligr,j lgN+Fi7G>ʓN>qxꙧ)zz-O)rD*mgmW{bC[C$4`>S3+ Fw!u4ʒ}QiDNtȇ;,5h =9(v)6ΟzkMyx#L7[chik q;FܪZbqছy;Op4wdo!%c!(Z`\N;1n=dDҤԉQƀi4@N )rc *ʒ=%=S{ﺋ d+W.R!{;sX9aHyKxQ+ ]GXAG e}zF4.ZI-ӯ5U.;=$éUZ-'=Ybp2!v: H5 ]úu88YKЎ^0<ٹET(\fZBZUڭ6_vH~E,O$O–.QdR?*hcglƧް==EaХdz)lilZqԶ$4jd~ #[?~ [p~3fa~Y8?Ç|2Jkz  5AիWX,i93vfF݊{ۙX) `xd)ГC ,l]:տ;a(>Ɓؾ}{Ć;xz4ʅSy[&1pO¯`R\)ЈU q R5it\ۏ!+wQD\vd?A IF!~O299-ccv ߐ"#o9ƭCK9֭c]\r5|uM7J%S0@a|=ef+J8#6d:>36vv)9*j39>NnF4bu$cE37=-k!v^=~*-t9"#xu|Fah=l SWHۻ@hO*Lq9B}x6A?C6j89WEm*ZQN% C JC'Y74ĸWNdBR]Cdž oTʼ%99+:#KG`dXdi=(Q0xvOa6VRNI6bH/ΑhEGHDžH$=-)Fmr'_&E7\s# ^ ?kf>'U۬@h͙3g8tA& V1G'~8u_?VÓzW\AH?(կJKdqL jZHJ6l0O Rm+ 9VRG~M&߇JSbMW zEƮ\Ⱦ}U*,.,Z !L\FuqkZj 鉔P*8|Z37;þ}x#̩\D.U9Cr^#DKA@դ^/nG~_ ۚ:iWh.{gOH&'/G gYeEL+~% jyr)y_|SOQU]k0-G)+NLg>˿rew3;7Ǐ6~ fݺu,[uqWBhM܎0Jn&IOx0z*zBnv:8o.Aa8qVKsD&* sD0]G*:~6QZJNAC̱In sp.#&ggN:(yGL |d)ЊBA{9$F^J:*$gϜRnP(e|8@riR,}ғxR 'N9:[)M] ܃صkb|{__gq~nK_O/3\rT1Zf3 |P)")>}d יY"rs. k n,##"3az~2;;M`eR${t'4U3ۄkm+R<3G[l^XinÍG=zX+8Ʋ̵~ҶPC@!BΒ3&KH2V RxNXPe'QƐiFГqXVj_K' i*%Z'3_?SP/\YiKkh;ury~!V{RyN#=t^'I} pۻ7~icaWQ#}as^纒¸,c\rZmj+V sx'1JPLnRioD1߳7$&MXgMkoEDFZ2GFlo-Td]YCu͢NU)׫.B>EZ <[nhC /a%hq={8|0<-GL=W*g[H݉(!::vF)u7Xǎ| qw sBqV>RZ+_z öt|}jۯxW94 %"79)a'Nޘ1 IDATDc1r3Nӧl0f 'G]G8Ѡ<X;hT+OY[Iv B^+gbfr<ʡfhԙNƖkZH ii ?.~_afa?ϙs'i|8uPc4z$Mu?wgRB #of\|W[.'_ 'wHb9 -bPDu$Ǎx0+ ~6$Sö-׶E;Ap>Y\,Vi3tZ- @(a5WHAp֭KJ3=r^(K˶n;em _Q-&y^Og1?)CKHֈ1BweAlH#'NГs%녹ף$2MC2v+;2(d,WL; D8q>?^zYf_x7|tl3, n}( ^err;W+H'fA)A=N;8QO'g}m͵M"WA @(G6,E&JF!J[dQ:Ⰽۺ6|-ŝ!k+,!(QllfKMOaVF7yxok縳x$vk*E]4ěc20;ܘBAPo iq3lnD!Nmݮ;ۮw[ !BI a a E!tbÒ ?tI.^|^W^?7ߡ}_g_YZ9ۃݒ:gŅ o֥ 9vǁ??#ZŢۊRfb a>a~a>qEA׀;<ϳֲa $lRc{_P"G\84 |ş!BD6 z5V%->p};ZO)C= sl2wTz1Vl5[]Kr$/|Da}II02PZa167~okio"ق ry?7Wq ܆%2sL G 'Oދ޺`Y޶JaICBO<RjA1#)%{(o\_,*aI~,ܱ%3ȜAj~t.\.SO=믿bW8nRKx1bwwZX9mo*|El{mvFv۴Nh=҈L=6 Z#sy>06:7$MfL;wI ?0GO7.dԴ}Jő Cx>9v< Jre#xwfG_oK+k Z$0i<>LCġ!V{$6CcKe9 ՚60,)}J iwB,fi Z1zH qcޓ7kb,R(Fz=[3F 4tÞ}Gg~1nν7Vܶr=}af "77>{&)رciE4yk/s5!@cc=$;v'ȪA_*dŠRԓmG1C,a۟--K,("uȹAT{i |YaR5^tnޝ}6xDiP9>"4[UOD bAʷSF9B`DM:D.'|Ryڲwmlr96q|$5idx{kƺ a782d&\as=t#5{{9s,52Hz&xޮGi}fưnc350x饗mGhmcBUD&DMO: m%7PKNC# TH`vj6߹9e, ZAAn^Y32<)0>>){6D,QzG8~ FFFXRYet|Q l|i #b?3|-PZ]uN o`${fTh/P)"|! k%jjtF%jc,A=1qs0{l鼓Rfk^#dzQ'uq(F^(XmEt2{:Ri[|O/0z#xvUH0A;4ޜ77Jc1vkv>§?@ot1i5L4{m;pBjظ#: 8I8{֕kT6K =욚^dFUƛXDI<ý'``<ٿ#GO2w4x:z|3'I :j,4 D1DI$NF"h6kh ӻbp|C L70?^x]}͊ԘS^B4sl#+vƓ-B? "0:yȄݺ1>|#GN"$C(ps{ Hđ> Ӏ T 3=$2eǝTP/+i8gѶV@޺u–-笓cI"3޾oP⏣0{sR099: |w(UJh} ҇IAkkkZcSvO^DV:<N-RA.2SpF.#+j oֿId@$vKK-dm|81gR`nWsi&Y|KޠZCH?0>,A`CCLMLrK:֌1˯d"%Flmfũiܺ8ft|B2w />+WYs:[YCjHs!T*lgvZkq^?W^uٳ9LTy9zˋFG'eߡѨ[^:H|Rٵ\UٳgR7kׯ+4?}^Ջ\x%J ,#L V"{YZQkM'm=my }!󉧟˯Ԩ+~eB7(m ^ΧKPaG)Ȇq:J&IIllQn5+HL$ vsvovsBG.jr9|AΟ?&B>|oEHER CC4uVHWx9 zx }=|_<|a642́yŗ3_`yy͸0vDd_w>~if*B@ٰpe{z<)=z{{iTDzHI((lvT) S.UȼȌ8I-)3hپly \.O޳ :=RЕ>!~{Q(1"}OPM{EAY}!Rt* 6~9mXx|>Oj[gi"ځyRtڪi?=4Ck)m7|E}dF#$'g RiBGYVoɁֺar~F`u~~|k<C{,9Ojo> dyuG?Y~=_?3P:FF}5[-#,Syٔ[Ɗ u#/#mK6[ 'Z3{sFN ʍd BP&B0qpđ坋= 4x<)fI*Jcf vh#+c^OyZ3 qle}cf~xI}'g*f19ɉIVHtĠ l5y&###A`;H0dai !%7_R!JzVv|;BMG8j J /SO*kMLR 2Mi94]~I6<3/_t:x'2Zb)șn<%TPȻmP¸^6_Y}h%dKmQbҷRYzB4UܞNWt`S hjl- hW)on4[#}@X`ttVn9FΞ# aTgqܤUkn iomƸpdT;9B:h[ʹA]8j{% K_ݱ ?wX]^*MFOȒtcwxw*c /?ҝXJo@*AHe}`BQF)[EYno I0Jh;s?_Ȩ(j t¹RE2Pk!aI4]SfAl((&A$Z)mbY[@KCJ)9 ǟx_>8W_ة|3Ƶ+X_GA IZN2 -#mjgiv@,xgaG `0ƴ!qe5d[3 l)mwn$zr<3r olJ)zzzhZ%kxqz B$NYuD:ɫvn7iPrQ8fqqJ4+JZ^àEiDJ3ol2nA.k,wo; ӧY[]can~F`HY;3vB ^ʥq+8ȾGC`+!8yg\a{bth0KU9TT*|?^@.r"nJf\ٱRw ^O#RP2جf:Bl2oW9 '[H|O"-szB!0U&V08>;DRX] 1SLڏ^fdvvЦ? Km ;paPnMCƦ+b =uNR-Qcqm@聩z1n%.OܾM&eLW1t0 r-r2LǒZd:Iy Š@FjƋJj(z ^τe $XK,-.ZKJǘڽ ν2C<ĻM. QP/#j$@;c2Q 7XB Ql ; 8z(|ϓ/ -Fv8+VU!/ :BV=ۓ~v\c _ ÐÇsm(r5QE(H+{.^s6l :֐XJ [lIw$N3T@$4“XNV[cg!ãT.%\TWNw'1#t| H\-R~H$~>g3(N@͵Iv߾' IDAT} 1~`lnvq) C@sqthm+Aq`r 0@ [n}12ݩ)텛Oa<#LK%' W^ٲU"RI!3>>{^.^m1'_fρ㌌M@iuD  )IV Ĥ6L6Q >"{FG}0 )82Ϊj|;w, f;ܸ~(36>+/Y84=}h VFlyZBJEb0y|abڬtvOOj4Zgp|6'ܽg>hfhwHXRJ2`8!њzU:,Jm< Ⰵ}~g{1nܼMXǰ9KHkNǬlcwu P٢Fe,_ ߧw`|_aDAà 2:5A í]θ1>A108H6/(|?6(3|Vn-cyLNNR-Whr>6JDI#QD'2{:kk$BD&ѪZZ['G,ܸNlY D#:u:]cJƦSCengEBRBK'8ƧܻPAcSK#hъ$&fTf#3vzvI'Hm**% 7g!%b2(ݡmB֚f+vl28|zH OJwb4NY]?@ O|SW^{_VnAzGm[v͆${er=Y"똤T^ <(jZǏKickAzE/-dߡ0?>vOҕ+*g;v JMK%IR"2׮^5:{zy`~+AebVH[41^#W%FPcdr8]!Z);1t4{њT"2$s458vaI\ꐑ4 c >(DN#)%mH-/Oצ Ciˮk jܾ}Vsp6D^,IF&ƹz:B)TܵrZAͫm &EyGyG){T)6pcΡ)P (?/?GG(8yk_QƂ#Ǐgffze+1! #TzߖK{D[:WdVDwa<Ék4<="o]@j~,oǀRNLq;qwIWWK6M:SO}fo]KvƎ`Rs'E;s\u/yG)W*!rU@"*gDŽ\ h|; ܘΕט[s˳\+˳z|2I&aQTHˆF.D{ l 毱B/=##VH+&4F9u'w38Oq`={{8yN 2sqqk:=RXQ {<=l ٝTq&H0q'uV;t.wԴDc4Q"}l[u*O3SfH71>@\'Z=_cGzw Kբ,cC1[}씭bfA&wˋJyx~ZE"E<o^́{h45j -1\PV?8g֕Rd`nٳgn:IRִZ0l6[wmzs\zUsJtQ9+T QIrRT5:#Fvpɳ ?088G? ٠V29!vw_&QEܤؽ~4C^z;,%sY|$ ϿJ{,vLM;XJGʈ36G'GS,r'j4 JIˣ[>ц"+sX^ /O'a@لFݖYB_!={;R9NKvlN39S)KM[U 6| Sfu<-i5mB!|0Y^ i %ׯ_?U5QcCOkpQ#LPI"^nkTk>NoA:"Sgx"燆XY# $- cё) 0<55J+wB1{5Z6cHAYQly",]Up՗,2iArRw]{y *wǞ޵@B a9L}3g)KQضfipRX2m1sO2?R]qI;Gԩس </NdxnP+X\' kAʀߥzgP*P1>gn6#<|oA+5 #Z]\q/sì,/#DqLQ˼ٷ5:Ł'9~zuMK!|Fˡ&Ը{fHD *#ϸ:[w=\=' dLOgb||RԽw uX*-i[,QRTK/KߡR$ Bq^xVtN:69'XZ^D$~ZN̾WH!Lf5ZItBY~L!_c X!Y5q*;0;mv߹ݕV 1Ap 5&&8q$Jr(k瘛-˼a|⻚z9oZTGy=)fnQlrϑ{8x YT4݋vZ>\(blb I]ұWd ;DGDa tppP4߃Ui:v fxe- !<{=9r̦5vIgf(Yx{\faJPpryX6I T[% .ϳZ1ەaev˃}bD"qdBB`b>w);}fV#3:sN$Pz]BE67 €X`KbW fV2uF E@\VRl+͎>աЇ?2+++@ ]}P}9xW,07(S㻘axp)YX^ɭQ"΢@Hi,U{;h)SO\Uʦ%5*'OK\euiugp]15]G'l Cl% Ow=Bo0kk%> rCy|Ɠ>J|?>aNVś_}>O?{xWQ>OhRwy("FD$ ۚaGnch4(J`$(!Cx!`1nI%¸zļKh5gga׮IV€Ǐ [lnl<j OBƘ! |~{ 6fmtt;mdfRZM*5lz޵m5:^2쪳pGlo}aoTWY3o :?=+B[D߻W.Q\' Q-0I}yyx?6зRzi$oX.϶1;w? $BL%$R@tL[;dk`u.fnQW,N4PUMmPil曯򭗞~2ʇW^OKͯju8^~HB=wk!*֛ua}?DOłϝ[et r3(}i{^&7o_5&~]󢣥9\$Dzh%JY6M)*LMN"ty˱#|#BQWY^ZV+ܺ@+&OצͦU .\w BtC^gth k|FF|nܘ/~4+K(1FyQ .nm]AVmfq]lSQg.c8xllnfxd)6O"t ssgE!~kD>ƨjDQ؅P-/.'i)H„/p&Iޱ"ۀL\BsJ^޳gtBs%λ=3:Q q] 11fο`emsW_BZ.ȿ@ sb)lM@DG/K/Q@'1QSި!vaqՏ4c/WEFaH{ Q046WT]짭s7!~kk'`0@UH }-^n,ۥΕT650QH--k\{<|/^.Kn]HTV>ٴD;MHJhE:SEﱾ*qbFV6Y[]IWk2T 嶥 EEgb`t K'f_ȔF0$1 cxtӓY,^͗g~` J1<҅*\|a wiV-%3<$KA`4Woۯ|~/Xx!ZBu1O'b\|ʓ[==¸Ri<ᣣ'_Y@)(R0GQ>,bLLYZ]+1VWnSUA r>33G'R *٬K'h~}kɟFkrF'ܺy7ro$FpmeԐm!O?å@Wrァyh\tF>\\RqtK4Jg:so+Nvm</"2K|C4V3%DIDPV LLp{y&hMNyT7WPe}uV*uYb/U3ICx?ן 7^R##5q\8xgXXX/VGA-X F==yy^N:7({^; ֵsRӍFvk!*Æ77|W7</#ҳL\}eDޢao 0HIu҅a-F!zX:ϬC )$B*sWYY]" #l%c|LbEAP̂}YMN6}ƧDJ&gGCǹgrY__֝lKm1mvΕC묯,P׮X' Y@h?;1'sLiR68D5p IDATaիWXXeqi;~5/_y< $sRn%"XD'07{(1h\ Hb =hcf$"tD$$|R`t`Jz C#L29:C -[P(PW]*uR Ƒ8x FVAPoccYƉv~F!&vQ+׈5 mE yN:I޳sw%VX_]& ZQ6yZަG.D(0$`Kyn=relDD!堒RJa}_%>jpMfΰYH: n7/r;޾%I1@ Svcu[ U%S'NKoYeMU|c!%Q{{sc4q"f0i"$@EZ$%P-Kӓ{e\Tr^Iz{r)Ғe*$=*@y`f0L霻o'{owAHu=}=gǵoǎ]zFP!#M(YbZڃZfCFl6f4>9766!\,1<*\?9jlO{{{r2ZV5{9b=L sdsYі::H"y+uTP5DB]ajrϫS)Ak!4J6z;w 3.^ U[>sz%GJzzzR)%YS?;:v(8fGC[g/2S|;|SU!mNh50J#'i4\bȥ rR{ K TUG_4? P%ƐIB02:ʧGTq{˅+FռְÐn~<OkB vcwGw cz]z1ҳhkHZ7ųuش Vkyn*WcMV!M?1!gϞܹ𽚑"F뚎n6VWWk${bsQD)6)F__$X!B.'n뢧m}ܵ]8.kuݽ Nmp7@jL6Ga}VR(Q*{UB' "B U}ɡÇ 5 8R7Ƹ:)=ܼ>0T*{> T*&2ERX*֛opLM^XZ1E\N%[܇{E'aRCٵg/wR,/kNM* u Rc6[]2d=tBMhL qx,RTT*k5GPn& cT) msgxte^|y._ǟNimZaR61_iB? 2?OogϟE͐Juz ׯ\R*a!֚( /0;=Oa}zFݻO>G).Cduziu:=>Ԝ)8(anz# V-즫Wσu](6+720z@qW)QAȵ+W=w9r0KKl L: ]llGSFkk|u&޹*>&>an:r;/\Xeqf gn=T ~ﺉãKƋV55Ef߲6ygCacQ2Ҋ02%4v-^kj6!'"I8dٖgJ>oNyoLJ,㻎02=uu"iomc;8t{[>qGLDg9Rba ^EEfRIww'ˬ,γ<7KieR`.4Z8%_ [;5!u=fKyJ9RT[7!,He3,41i5Hj7C$_;ss_->4k8u KK+\t^C^ba-#? nʕK2 .Z뗹pm(kr7Nƙ7OW˿S&'0?_ZCb/`i2Z ۞o@4R)8R l)2,=]ԪU D) AՂŅENeRnTڥW=ͼ<=={=Ι!9DTuq7; $Xw{yKɀmM) 1Pud=- m@dICm_H)ʜR<## nǷ\v|["l4F$Rʎٹ}v:rmdrmtmC 2:{>8J{G7Sd3YRVӱjόRPT\T=5 baI`@,cָ  *ЄB4Bd]l\r5u720|T$ :*!1+b6 @ E%5Z@5v>\)LC39y+!hT=AGr̭nNWwIWuEf( ʹ@ɤ"vHΔ>;]3VM:B@r.ś_V.0yRHIX!s8cÜ>u@ ni"V Jyv3U7R{Z"9"%X|GSIcޣ\h2m9{#aT"PulUR }vՕYOcj"W^^B(it5p̽unF$&Jsyǥ\h"Q6Z[G7hCjՀtmZdߥQ{!èށ0nxy"Q*WRNm!F:n1h6 nk8N|x GHm+FvS.QJA=t|Nq_|([> giRLB\\XT\gi~̪#zjId8sIh204Ln-޸νպDžCГ"N3yAE ޿<]ͥ˕22']C*._}=;Օ'W"J2߃rMk TN*e#QDHEP:D2.q$(lM*bj9p))#v)BS*!&Ul`$Ur SSS6 3 sfzW^xΜ~jNO?E*fnaPD*V&l$͛KDhaJ%/QS0KsƳ cDܥl6mv#Ao׵HEk)]Ls )ɽx0z?4R JR&ƀR!4PoN+EqBբ wVcȰf%݄*2F "rΝ;gv]M7|S?w_^,t<8o>gϢM q\, QLxZ)N(Jry>tyǎ/=afg0n" (42<ni;wqdQn.u2'+J- M0 Gvq୸ZOW)ƩOcDtLvH  x>rZN:% Bn4kfݜ~̅dj:rdvnAeb$S"*.צ!$y^rچ10QlYSUXWq %ml?h'!@TE{6[0/iryeV-f[5{0%Ϝ9:R\/T4mkSx~ 7-"E.FOO's8#L\ĜiaA!Zw=<s{PADTB^ֵ6dVg~<#v5?3ï/vSHM*AF>u]Cj:"Vlh?!ĦD!SdRkSP"'nfRlDQ ۩+AWJt:,./0A[Zk o^k9f a6qk`M\K/2fTh*oPi#yƵs}a 2[8Fp<vG`JCh4S8Z#Q)#Q"NQB%v e[(ξťKg-M48w.$5LiC9~_+'xg3O/} TߑٌJNס-Z'ֱj!7 QZ$X[c80t*o{~x㵗 "k tkN2Zv\01=;%~HjRHOHG?IWG7V|?K_;E-d5DcL~s*ҤmnënSm~5J.333Thc-{CsV%:}Ʊ! jiSA*4vIIRv-{U2ݫR|>O.K\͘B<ÉMVʊb5eE\/M)nkFOG<@ʕ'iiZT)xDž\Ƥ5{ia-&<C+6?[ZH4W^#ߞ%b2oG~|7OBqۿ~TD[q@F#~_W|A}s~G*mTі lhބ ^|< MV7o*DFYxynf6I7¨|o[*ō@4pƫy(G[i:hGA`RԤ\pr(+E~?&lJ6-ҙ7?5m],͓ΦhYRJBܴS3u~gN^|zZ~_|C>t-֙zi;0D*:V(rq3Yf_#V!f&2 ##:>r4#;gk\ȷe>Z-uՅ&LNar;Cxîj\x{6z ͗vo+TꥧRLnyŘ |mOٻ>$.]|RHpS>C#dR)^Iyi{i- h|Doovra9 2rŠ,08F[Y j ^P&}kk,NQ&d=\Jт6cù$l)v>u#X3+Z8280Սnդ,..Q{n.]*a$Jlmi1hRn7j"~@`O{A5TIi>0Z{](lXv#]r)Z>&y-}k$MZ<8fF3<2kNo?vsdtE V-P.-BQ-x~Ć\ IDAT15Ʌ F1Ϥi%ZkQjx:rSvA bgmQ[UA)\emm0d&9q7:ya57ioe6d58͊f) ljtᰴ:^=0aaITѠ_%BV"~HzW !JķlcNAŷ.҂=^s{yy>'78~Vh2p\!)GkW3;x n.KOB}>QzY(=BjTV~9:Qz %o n*M@2J0ꝃٳk7NZ(1t9F{hx{_7XY^Fl&cdZbq~v??IB8},%$(Fihij[ v8.e EP__ܬLA3_+^{ ek,.S,}vpF9Y 3 7OR.Zb9{hA43INT<ҐMgPBG?8dڄBfjEJ8`|"R%毢RU 4oR*$! cH{b4`)*QP#d3YϷB%&|mmT!||+@1Ԕ @;vI-ZW>]UުUDTvGҙ,سw/ctvvf HAxS8ԶN8}?A6ML=I:jx@|zN>giq6LXQ0;ן_߱8gdMa!Q&8Q@Sk>C~ 0;E:k !rTgcBJ:(J H(!)q[5[VE"D}/a ^ ^my+|#{̍0 =z" +AԼaHM.ҮݻqCT" M,ˠ_}SgN }t_RI1, >׊T*0?~lT;v1j_xեE@H#D9aȶYY[ q" tskslGevzZbÌbH ;^?U*rRlC޳8垄E\_+_ԙ::k Km˶<ݽ62$ΫXCRڐV_+:F42壐z"sӬ./S/U~lN[tMó^177ùsorYWБ  01L6MzDJd{>kIYUfgK^Ht!a!"cZo²jm3ASwͣg>aN*Qd5fayӧNY^Jݴ-6f[ vh3q =9ҥ^beu! 7OrTEF+9+e<ϻa,6GA+MDaQ"q}[[[dHIH6:;j$ݻIR E2Jr?N9;v`hhm޷޾=qrlB;D1"+pJSC7 K߶~WWk~` 4hjmq6DrԯȷO uc|7U8BR,y\bLSm` iB&@#&&ξ *Z⋋3y3;9iExSZEHB|#iZ—(³ZlپeڐΟ;G\16fN3CKl%k܉ (SX/Pն 8Xj2Ւ1<?7ߖcxdB`CmiH|wcZKAD]J(vMI3cds,QAz^m?|' R,4\ (RQBRٞ^-p\vZA}'0#1 ˆ0 ZDa1%}+ ڪE BL~C-k'C20)\G(2q֘oo<1^Bg7ώw}{{ws/>1{zh#ds^–ֲH(m5qBEIUnlDUd=fkybكy g'KH"Sihh%|?0^}vzzz`H`D0gbϑ43L˽l߾B@^.i޾!~-~bqur8CXJQZMڗJÁEr~}{lsh#hɤ3L_5d5 K >[O$9B$+1U)#؍"NN:O~#\tSoAgW)-Cg_VEF:2)BOJHC(1&Z+S@i"Bh$%RBT}?|ch9y_~+BUh'fÉǫ٪i4n1,ΐ\eoDt.L9*FZv)tq^=Hs446He#tP)lT7~!Oj$ Gnf 8m d)~l66HA>( (VE2c'{ <{m!H : #RN.|㺤R)vIxLOOSήn::;*:p(6w5 nֺe~6M5ނD$$Ҋm;ts3s6eB`S ZyQWW'=/+KFb5u5p} ^i@1I~MIO_/RHkh[2J2JjU`a |f9:)ڹ߷;p؆jCQ Tk(p2iGc盙#IS~@O4Bɵƾ@Jڪ͗@H-\w!BժgmuM%Z>jL:Cu^5P/=}B q{ŋ_IWJ*إ(x! 𑏒wvJaooPQm*CTl 7!-QAq5x"W [AH,0C&}5y[nޢXEӦqAHI^cv~پ}jyC iNo=ےD1ʻ7%Z+k+j5?Ok~_L >xxDR AWo?v[niO@Of%rXT&"jժp؀>nn>t7>(ts- PXY!t «QAdHi 7,p8nK[+D 'ѣuRij)MhG@s(!ގ: Ev&X[YpDD$LJz`䶅H&lo\oCH [X4 D!sxG6rrbd)Fwc8"P⑗o9Hk6ԫo։͛uaj"G?ǟ|G>xUl|F4 /G:,-3?;K\6Krnҹ10$Bp ?Nu0 qn}a9.{?w=KW-^GfQH֡ R%kvMhZ%,/C W:hij$BA(lcccF{2c9мߴ P+Bq;.Fz=A$$*2k [ zz F2>6KE b\ynG?̗~q9nlտ~J(vӎ)3&l x栋k Db}}1F9Bx*[NyRDTZ)SU<ԧٵ k%μ +K@Pؚ㘘Y&%D `!?!P"՗w)VA_۷o<nza/>ϹKX 4N:)BFji7!pLY|C*I-c;vh^}edEEռ0.xӷ1>-c}PFؿ﹏49vO#FGw_IR6X%fLdb~24>^~HkR*As!MFJ(HҶ0u0D8MZs5ɤpP(V?}ݯ'vAW0tv|3.fM:U*&J VGC#Lΰ4Ù_! X_Z0I!減M-nj7lE}nop1L =o\m9Oop&i~OimЌn}G*bEHZTnll6k 0 ':.`bl3gΰdT6NIR+ XX&<9H[ *^'Ű&1d@Ah)HG#nel !B +J(6+T_B ehiȚR \)H#VvngaTk===-)Q?ӆH~P%RQԴRu|Υh`ltzՈ¸PAփr3 Eme36Nb$cutΦ)|/0ר`7ۍy؍#Iz7ND(Vy_$D.*+'z2ZD?)#R(尿qm 08G6UY^Dяں(p4ȵ噜*w-yj}ҶV: =o'biiSGHgOroI{z~#Go') ^h>}O؇>4zIv'Y_^TI-7f23v 9|w HSdIT~:f"FXqo@[nxx8m^"'% ssQ+iNqrJ' *`Qhcuu{'FcA M r(=erŅ3<{y 53"Y:fFI9 i?4Z$IjBʴfzt+pܔ ))T|[6"i$qjAv> B0u?Lx,Jq{=)Ad;{" Ù5ZEW5i7mbK8B_@J]~RG?C8Ո gLNNA1ajfR)J&rϽV(!52/ZGnk[or@Gom( 9s4ӓטJ5~w KK3x+6Vit{z3|el>OG==ZuXZI !ɵo~V,-/vrh^ZdeQJقb9 5W#M1<8HOW;gɶ8w4_x`aIWfE0Ή=td6<̮]lƅKY0|~s7΅8Ҭ,/sq j pGJN ڴX_[F:_}!Ey=x\^և~7Wj̧a(FFFB|\.G>70N n^:CJ=[jrm=3<2A*%Rl\(I6 I7m륷Ξ^I=,.:qS[K"q1, 6YKQZPG Q GGY^R+FxOh[TOtZEW;vV|eo:bMw|͟ONyHG&_(tV&$ɸ mdfv@; t0׺fjj*1Bҙ,I*ZMW78r^?-?UrS7o&t8!FTIy{ﺇ^￈@ISj6&&ƐR2y 6ExO:|gH""ce+Źp/`yy'2%FikkQ@f3O?Ył-5rۍ3pvb #drC# UH>Y*?p8 6G$ IDATH@@*b3*""g~سk lo .^<ؿg/]8/2?Jz&׆i̊cZLyL;> }&RDq l6yER&7?3J:8՟m՛&\8X[[Z+ɦٱslfEMEFM^)[(imee'sRaVPxrM4* .n?~W\asR W^R)m MºީLaVV%0^$LjF L&KQ!h8ҊY-t>wyV_qmqؒI z-]S47eTd\~c?~ = V>!|v \t:m#r~!|Lwc䲽T*eT#Q:~뭷r;wD&޷avpg_o/###K%&Qm%SOxB<8wG}+/űc`xxҀK:M>B(p B\cItz AE}Ͳ$}1RAɮݻiԫUSi~ش㤐<~y<JX'*P[f5sl}J3:`CCVAV.Q(tuť RXen2Wf.r).Lasck8 겶J\! `3H{Hi* oboaF'=>2ӫӜtFe'M@HY#Ƶ愐illnZ.ubqBEjK?itC\Hᚄom?uJGjw`P[?M}Yk=$,$&@\P(3c7kkds."mQ?Hf{L[&x8qy|) M pd SX;1FiE`KoGMzbҒ ',Z cN"i|#oCCs6Vp 7E+>lm*:농:Îڬy}k7, GEą{zzbTB'>Ŏ]8v=v/33Y]Z@D&Xh6T^+ϛlN宻"E&G8x`}gųg+x@:R 2, r29lI9Ȏ=y$WhL7CSJzu!8Vwڑbpl5cFOC^;!$~;vbqa9jujbha 5#+Tk,\:@Pk )w֋t:߇֊ͭX0ƁN!]:ε^ar<3lnnfffl{*F8JP:1.TP-IK}zg(ۅU69tj ÐTʄؤ$B.F}RFYXRgNܗa߾ &" 8rs4ۇ@H5 1$fyG)WZDZ1k7IQo4Fw:-?HJQww7;vPն:m7EBէˬBֲ )%ah*i-\g2A a&WLZZ?OitR6n,Xfr.34ϙwN  1kM6}X@F\_sA[eS (nl҉rQR'#HӴ A(aHiszf+aZuORm"O=~Ϯ;v^CF$?Q*ZgCB^QN^6Iɨ$ݙF}C%6մlOXuРL2e #sV,uiḢd$ mФ2iZA4i7/By\_74sEZJZ//.7?5Ϋ +ZTa,HH4&FFaj&voAYDDkP%Cv[^vLT*qIb{Q:9G1( ,.6sq8 kKDZguS+qGI0RD ._O=f8EDH y2g{"l} b8"SiwLY.tLrX_ѨQlL"t ᏶O-b*|" fN3R \m 3-s!, V c$Lsm8i(77 "OЕO6݃g|njukm{"b}LS:B c R4]Chf!"4(BF3T* OR)&JXlѮ֘R(M[GGoJi208HPBQ:B;T8}cdrJE.@;ރQQIGcU+RdZBǮQ&D&EVk4 @X,.Ҩs9^›/sϱ2!f-Q3T|DJFYՅYZgR @RTX^ZV-llʼƖ@,{F#C4u>*7\?6ZN5$ov?+_asc͍"f5+noV\0u^UJ˼K_}^y[+KuѻbAz{wPiHr1G'9~=s_u<7KD͌cԎIn>p2;HP+-6xWq# famfB:NfV!Zf(p܌fe M]yi 9ؽw4MC#piR.7)oWx꩏399T}kVF}c=3::i7^qD#- tх?;qh4,ͳt*r O 핢sUh4+,̱V\\j_$:Ta.Rn8gj0mWՖm!1:b:W:>ۇ@K#(SY86l7uzz l/ݿ&Z٪] Ydeuek8̧cxZ I Cc,-?6{e``esA(9qEN<'N-f#O:28|TW *5h(zj1Ah4$`hh]S{C7l6ёVP"QK""qLX)[%$vK%bm l_QF{v1Tsޮeqd& |) Z12#XZ^|f.]R,X}}\DS..)+ QL{#SA uMzt &4 Mc)R BG#31I rx.X[YbnF@ZoZo$L&pcDGZ&!q]x6e՜#HE)sd]8@m IDAT6@vRJP!B# b|lN}N[":րa>V#QmɄbly/nijWơ>@?Wc&ݾF鐅9ywmΝ;c+m[$++6&h 6aQF'ՖnMŞ#_#kk+ )fbmmbn SI)Qyqt19ν>wiJ ={nbߞ S!ɓglt;_ԛ/x]Hkr7v"C}>kk`dr?nzzc@0ƯEdI;8itrL#rX#Z@v$k~$g"]=}h) L]sp *f"(h<2+llev]Ν~+fbt عg7Lks!>qxbyyfJ(BcGJdOGg6D J304Ï>ЊFFd iq?'{G}TWѕ7[8Mo6Dq[%ɱs.0e#[b{CҢSNZPn0>4@R/+<)06lZ+7N?fEplB]צĕ&^i>RJl|RGLOOswyP(Δqg.KM|O8|v>عs]=y^x9.μKyc=ȑ#(h4wK)lٟpiJL:á#VA' 0d'eh|\?#  A>[^*`I tx>kkhZZkT@h22JJSl|ݿOjP!QRIˇplt)Swt&smfv5FC qi!;RZctof^ƻRxl$M鈈zIvLoN{zee|l>zr]4u̿ˉ69#߻mL|۽^Qq[pwnnp`pdVPqJۂt‘iAA+pq+aid{^3o'_aһ*u ]FZ~@DV$ fKi$^i,վ>C~a l ; EsH1Gqs S =kĀP cnV79 }#ds,/.y@)J!͒JVa &/˧x V 7R:_G<:oz7z={&q?OA^L\_XؠX,R56I]l~<9 kxd2^N;ë/@:|oǹ!^K'W_dyu6&v߄DV9r_ی8h [C,%QLB ֫5fgf<g@$Qg?9n2,jg~ӧF]^6~ObP 0:6Fw&z"leZ62i~c_`G \~ bi4SSYvJ(0sEV RV4,Z2s/?AB R~o;< [`?Rmg~V72W81ňpc঳|A#kq'hSM"xH 4}p#C5P8.i8 nLBHht7)׈ T裢P pqdǑ8Kr? xeLIٹHHx^bC}fgghye.]+ή7qC}MHid ]= r n-\ݽ4 @T+lVZ+yWzu( gf!DIi[EDfBcjsA&x(bE3@5"fGdrn/ChmsL(F8JĎIjF>/@^޶^ڍ6e؊h)#7bwJlAL_$,F(hAlgi6 u@.j|ul#HaP |굪E!q(-gHxcoz{ -F?s\(Q"o?c~̏s?gс#ס^W.p#Q.X]"ZP ⦛3>2P_7=H&Wdܴ7݌SjEBpߋǐ:Bǟ >+=Oiy G B.A2֡37E:9T7#QhmǒpБ?$&ԡ r(H ӭ2&&k$NcEU28B fhVaw1qV|V1i'J4NDfL" f@(D8ic y\͟q_8 義EM@Oֹ>T8t_K˿osLLQonKR-8I1SS-Vkq >44̧8|˭j5]&|+in%:2,7%oFy#grje(0TNYH{]7E4i7CW/Ɨ8~]ԛ5\D0,:#gai5 @l?pRY䭊yצm9ӶZCX(-8)l4Zi^ˤj͞}{җ~U76x:^:[U4U(/~0XXXؚ{ûz/[Q JA$ S%T!&o 7"Eh!BCg߱iADQh_Gj bZDq"n!MlEW昻2J4b;Z:bͷ2;I&M:E_ Ccl~zӧ_c;VH"e)|^}{v:Ӝ|Ex%¦:4vXG 9{$Sizk˳M!hTakz#N~ EfI\!B-_6_ւfəp9߿.\fM랻 ] tgq~zˤRR8q9N2.l)X-PDD:B:lzJ'2h#ؿ/Jj͈.UeV\[c4*xٵ|c$ߝaf s3,>-t'HR;3ۯmUӨsC)Nm$ZfieL&&cmec͛o{M#ڂ@*<͒Yrizj(@Ey{(Ur>t:t ~HF DKB7aKk 5a$q. VAflb[ʯ|OS8&}=VWRJl:={,+I)Y_n{* Q*bj>/O199so3W.ZA[!L[_zML `V&r^adx$ F=TIH`kyGV5pm]rZ17;KVekӀ1d4DQ0__Gyi5 "R;ofi| `a~5n7(5/^ą >8C\A 4,ySkmST /1̡뜻SN<~~g9JƨVCdu]ߟT1c,Z1&"ZQJ^x̅sf*k-f僅m4"IfFa3m,H98] N29^'Bd\4al$R kd)%&ke5MX>n;\v>du]&,^11:)GFuJ˫ųoruA/VJ?i1fXNu hڌx |յU Un;z'=(wnWBH9DdD:⺎ *bxhl*WW`.c\FkM.cll}|yڤ?v-̍f\.g>kLL[o)Ҧ-lRu”r \JJP.LcR|4!H]>](o?4BhRn(-Ru]c lo׹!Wn'Q5K>dg8311YY^n{&fEk_yYht:zYCT%Vl#%w&˛̒mצrgP,nN(Nș rrzǥK8s k-훈mQrS:Cã.z[oTC(rwriV7>z_lDRՒ(Y,KeG800 A  f ES-Q")}ګ^{w9s}UnQ>#Us J#\^5 ro]`FpyCΜ}jZ>inݜGGVp%^6OJKu1؜^p9Td>l߾U<ȇr 7nܸmCUbi` NMA>#:z`M0*ddt8jzzccct}^Psyfvb`'W._DmrQQY[`S "z0BkKl5n;tBy@iZƖ{/ij[E3:: ]O A#B:{A +Wͦp冷֩ѝZ*V…-6DMT{fUx?W5CyZA:B\!'(hc*ckUj%Z=Wde]'Nݪ֡y-NRBumJԪ+Ł&jطWrFe^xgi,#THZtJ!65Hco28'k[ R]GpF[9GR4޸V|ri :lqܸvRFю{ʿ2~/4~0b*-k7=O255E"v@ !(ĉ[n Zh}/^ ?}>(CEkDZQG!ٶcf!'|sbqiD-+gݻS f!\.ɳZתǡ5pzI[DF{ }46ZJ>R n:xZk<#[A @X]YayaX)xGYZ^^;yRYuL\{66Hq;i6>jJ^OSOﴑJh4Y P*N\:2Ē{ "wARaiq1#IA}3gWP&&֊TzYXYx^;"o2Z ,`PΣCX^O~HnBb$p6~Vo44l@i]І6e~VIrn-NJræ i$ҥiM^Unԭb8w׊Igm}N3;ϑcGdj306>N]z@{>rZHMFd7XhkLG5Cؽk7vcz|B Οb!%Ql6TBTgxh( -zEp]kG==nmw Smq-Qϯ}a rJ}˸1&Z* &% ֆX(% 7w~Ovr漢ؽkۦ& /r)O{AMFA$5Uyq}(ժ!BHA]4ȡJ xESOFNSagvm1۹s'R-5%̏g!l(.C9dK+<,{M 2cȜ$C yI(B9IMHVgH7O۬Xa: Z~@yv+"AtNW+ n#vNJ%}ӹ_T>ޙ}jUVۢV21:7nݤv 3Z3#TY]\D(=~JH)[jBnGa&@c ?gj<&/!t Q6g/)盕1Z4W.J)?l.܂e2ARX6]De=j7)֫D*B8G`bt}A'8zv<'ftdZ5U O288HB){hX\Vp1 hv(0},Ut Yu^Li + =ο._!lVT(;& a2;wJev;aSUT ^қ e!%[|VR(c~4RUnK؍DwE̓bqe0xƀP#QH2 Odϟ3Ek,,ܢ٬SvlsϓP'ddd hqBI  06/ H;nF Fk zU*abV7!!p9~&pgPJ+f 7I3: wuTkU֫UB8$")敲͕J/3㸵I!b'>|ݻ۱m;vSiW@g;(s&v! [LHpN #êz4#mFFEBE!x =Dʀa+R5M-4c 4qI&}gF3oCp9^}UڍS'ETbhQk[lIn~n,AY J2@V' qaw7-%㔄 RT[M@HtR׿} ~9V^}k DK]Ӆ"(Yx6/_vǠ7?cٳkF!|2S5khm-R~Ano~7 MXiEll̓uOZkȓrΜ9KO}Y F+.m_~e/;a?//-D:clR8 0JRN6y!g?!J)*+s!gng_H"Hu2MGv}Xta~NreBbwzR0 ) F9` :7g_{bޖ?n:ةdv=Q!}s-[XJ(QH/G/P.'j B2OVoֆW_~rDǖW#%ttbRCNk<)PQJk?{_(A+W+iRl3"&ҝGrJ-3˔ e#'sF?AZ0}v!<zQ|i6Ҙj+3ds:nEVT(mr팍240LXK*5Q[3 Kvx}>i] yP`llz@h1'yiѣϱXm5 :@h-502g>j>( Xm;x#O1T*k$!`0|*\<2u,g.Y2d&d# 1|b e!,1.'H cNQ˅HwmW#v*jw>2ˈu'vҖmH?Qku܋T+q~EݼV1F/R[^eS(:\w`pžH__SG3}BIpE.\xGkMU:vhFW&e F_.gzbWj(c0^_7Y@<A`v% !m0T`y( wPyfS,-/l&ƦM 2]206m._D$_|4U|_[*7Qa DlX,rE|M`ttO|,hl011`?\`T\믿⢵>Z)6GU{lll,AMdBs=q/_`E B)c_wI;YN^ eU7/ ƶMw]\ >etgMr;7c! h8V{ټ mzCF[œ6bU׹x*:;s :@X*Q+AB RvѵgucTX]]euZ&ؐV LYwjI*a7_{읙"ks.]8c5Oz1vz2H~ts"ݫO3$B]8Kl4%|w0P' EB dH+Qg9"$J$F)56>7D\rrGx!GS,) =9|\n{%>4iLM:_(Xc_8gt$u+ .B8HNB"$' 2jRڪgy8P_>2gffػ?͍&2.oP2|EJґlgEE126Aզj੭l/z&w,Q}NK7AIYA7M`c_ҷh.^|ߤX,2>>2 ++> C7݌ۜo[-WkZt>0FZԥcZaZ=|!ȕ4DAoz#}zҕҶ$W˨BٚFX,3<{$Uw#c5HB0(iݶqV>GwRJ^{562D{7gYl@Iː  Qk5∜tعn'hl_GaMeTԴg灣L_څ ɗOL@)E}5w`ŅEF8~ah IօtfIQ/癚\fa~ކQ(ZQa8%/wړ["x#=)h*C7]'/`@"FZ~<,|XxVxP,Z.܂*0\t*:ԅcvTLs-]̭k4F|S巿M7r<Ἰ4bn% = |8#1¥i+p/hթ8&oƉs9KtLF +} //<éo`htlYIlVǖ2cp=` 9][J!">ʡC'$N>]w?"sW/Aݘ0fkA@e' h2Pr˾%JL"fܫ)6d~3{⥋Dqy d65K]ϭG׻+R*B|1X!u@ZCY&4{wi ^X"_,ڹ) ʕˤ-l(C<+9u}ewJIka3-*a]Va/aP't tIs|)!!'%CvpCq1varz/'NO>Nnl#x׾W?|~&i5ewh̓>@ݢlZ!8  VvP'Ibafxs!1~H3wMƚf|rL bJ1ZdNr~LCJ'MfMȌLNHt,iVX?ߨO[ߦFFW(FOnځֺLBNS'a{%#CB ZGدjP]׵ WN7| cO"/CF+I"o&q{3M\'N3_C\q(n[FMEx ,ȘMŬ܊hQFL >,a(=p䍼|y :!` xt~uUVyk>F82sɩ)\ȥ!b[at?磍&|󛌏s%TfRҒHb`FHb2 }9C൹'N7xSHGXm4鉎!d$w]YYYx>@KxTxC0k}eݴ:W.euv "e_reΝ}[7o8MVڝVЦѠnF$ -Y6# Mz&V8m{Ɏ]($C:b3y@HwݴqZFa$\GOq=r?8=fz;uM1 8ta<|g tбv|v^CAih`l{r:2hOB _ h-V [hMwOGFF_5,Ͽ?# Qf: 1*F }}46j Z ..#7ȇ3gWm^/ϰ$/tA闰cN{18_'h~.OPf!a֝gUHhԪ,Y/΀R/_\ o6z]F#|dqiz5M9QWH"SSۙe<dtpQN?ǕwBYOC7ggb1]ٌ1 *4 ۇMBpQw27\58x>,h d.G((B?[,kd"6= hܸ~EdNc VNCyv3% җh@ ¢0 CX'ަf ;[d $?{ש@@v=+ϗ9r|Y IDAT~G 5^"U<gi}K7N2AqאָWh:J9#r>͍Uu =y]J-kxanxFU mK6F!hik4 Ei5[,5!v;QJq5DtDfT*}OzX5xrB iZXNBwVQY_clbwΝR ")uƎ/_Ng/dxd/|K }%._U:wu1uč\xm>GX,V?ߺٷfs%KKai~+ D"5uAF1vsf瞋E>Wؾj6MlyNƸ+-UƧr'ٳ{۷M}n&F1k/{ghb'6k!\DvqC6׮]fiq븐"^GDHPi>O;=pmX1g/w}}wl7m l%v~/M9r!l$^{&ܽ":'i`f i?/ĺeNޟsuE5V 4UTZDwdmRR*Hr?ZSq…Q!'*dn]Rt#yҾ70fvtT>5v]G# VgpWނ9qPMLhnh }vv= /p7/]; + K aϗhmC2-}ϧX*bŶ(T*JYåKa'/HG?qnܸ2~d~ .^ ;{DƫX]Z~͛8DzY"eC\|vE¹gCb$Qs50fxH癜$l*~|7kjug`xϽCG) n[<5/iukWrGh y:"c h@H]N[_ٗylJR#z_10OFS?Ğ\~^Gbݹ16Rcxq gfB8udVeB3B[2:,JZM_.q?Kl4*heȣ(nBmzۼ+4kU<V<9 a֖\A Yv>fc a/L1X2VM;~jjbs3!Ĩzd.ǮL&{&G9|XrTVcrrVi)a *iZ.t 1]Ck|iaX 70a0f仝A!*lL>бհ@ Q#-A%841F(@{Hsw<FX zmXW[s_[鴚4; >B e">h F&342FE#gK$ =l,kڭ7a3xheZFx6mXQc.v>HٴNVZ0WHA>_`A&V6CA,Mx.*j!Fضm'hI{i7GoX7O-+Ps"xC]0%^ ܏@mj#ρ/[e?Dbwmm = kA 0V6"JB@d1{`Esypyv B8sc䜂"X$ C8"VmE[f YjmwE4\μ{c@@ :]# o_}ׁ_JY s 6b`X[[c}Bdnne*k|<@GUbzԾo R Q@}cS|3ϰ8LVU_1DAD Bv3m" "6*\Z\Ħ#YT8`hl\>Ľ<ƓO~mhx9A.Ws_U''G`bƚKyCسNA!z@T"/}7o|n:/$r]a[o %#s1z-Thk$d I> D647Z61|L͛a` d[͛1Ϟu^zjT N>lնz {6 "o9)FP, $R +C h9S$9?f庵&?}޹p7<xKDizzЄou$AW_K.q$1oԽ0{E!F7sDAH\ *hJknbhx\w0dtz7G JIK((Ι70'N]wg;R?ss,#-$brHr.+-ty#Oι T1l*5@HYHS "'N݃٨:Ў̠cE;h[NYC [}JB:2]mu*`qiG63fyraˑbmRC^8F($𣏳w Kqt9=@  s7nr%.;<:rxҳьRo3es38wUXýyv>N 6Эب4+{^k(;dGDB&s5 tY| %lmV֘0;Qdɒvtl$l!A$ZNHkG~ z|i8hy$k-isr9z?#J4 ~np\BYg| \o&#3gU׬Ke]n 0VBt}:Fil&uTx{8k؟mFgT3o/cb۞2I|A\FY v,c8تJy\~3oɬA+<φr"ah+C9ϒA#|+_fiyt,)g0vxU"! o.,#Ta"dJ4+acn @އL/i 9΄:z33~"VEͫ. [3tfnP,P(!(ө/ Kf$/ɜnӢWofA'GV3-¥ # ?G+ #}{-_ӹArnByy<ٹsE*k,//Am3RqHit;䞻`mm [mi(l4[c׮]I It6 y6 jn3T6͖)Ra lb.5]"^Btq06yIklT0#\hCR ܰůIƻ$.tcӹ 9X,v.@3Z pB(h6ܺuffd;g2$861ÏZ^h/lV6 0)fiiJe(,aBHd' ]jVWq[T봛.cN&MɎC2d5Xl觐/HG2}ߕ+ IDATԯ־b LkG7Z6춛UGS0>a+qqwWclt{;sޱcGO7J@(i4P6j/̱^YcԔUn |~k_ v+/լ|aSVprzGXDkM^wBi2 K 0@*g i33_Raii -cΝ}/fiq"F}8 vF6(nNć I;k;: stB˭o~|cO .+歊tpRO|sw#;|xS'O1_ $!]@elL]x>+$0aA*d'ƺɋxalSJuN%)leZ7Q4kJn]`{N=Ԏ)jU¨m2Tn0nA{}KCXGC^mZf_ݔܸ6ˍk7W*J=~{<)Tnaw{9z8ce3m\"!]kêUr"5ͻq2?"3ۊt8 Y__^ĖJ{!JRGf^g"~mn @0BVISB΀+Ort f_Jçr W(X"$]mMH#YRO>~NßBtlmdqn}muFA z:JVF٨ȁ` i%g8zHwL6;bݟ +Ӭ.ZVh{^b,E:AQR=nY4[MHϳ e,qSz,JFa2[85#HÌΝR ʏq톃eJPm_1upxמT[LlM;1kw}~ÞmcKCȌ[.0 JGHn_w PE exf.֛o00Mcph!T}Ith,12x"&!ǾԩEĭxR6 QN_OJT9V@bm :zm +..P/W%JF2mյ5+k\n1v)RKN&TwʊĽ- \Ο;{o?{oEP3683W.btA_>:Kˬј;^|ψMw$!|“za~.+HHGU= 8L#4k-t$,7QG>0 ζ{Bf3Q>DYŶ1Y;°nz-}D „g AV+ݛXEM|pϞdyy(H%_|G4CV`KmRJ|zARtMhe c q9Mb\1!|!afL9=T?0;$&<<]>_P-7&Ƈㇽ ?;Xa`^ş-ThhVl/=G_iei1?7'APgaJɋ/ڒ82iϾg(9ztΟ;`Ho[ENp{CLl"t,kƘ Z]HExB[>F#Et)cSJ]t,q+%'&h6;&K)d3ccq7[oSMV:mϿ|#P*˜&dB VWV_gCD*+ K/qcll#垓l̮.b. hC__??89F™3'8q= CJuBF@jL&жI&&wْ]$dp-B ]6%z^1**ms0DJ7}a:_OF[B,a汇w/ 006I;;L_HTBq|k o=<{38m4 u0@! ;]b[#Oq2wj:1B&`bH Msdd\.GD)E__`VH˳pz/eٹs'VfܽC##Of qg\EBЭcƒ.ԇ@l4lwI1)38KJ:XG/W/V߾n8WKU=F.}RϼѨZ!#C,--%^鋼*DM٠\a4 7x{'x9}$.errm#GNx Ԥضmwh18|L.Gѻ|I&3;;xR-mBvWU&33)h5[,/OH'Q b= #^>mphRDp{ @;N8kQAc@0JX&4Y"C"QgDЌe 56X^)X,2<ɞ]>x;wjV9zko$E?sGٿ&8mGb~vQ`D|yfg&8u`pxf=j6Ԇ$dYGwΩI._ g@1SNq %7.ԩS@]tp;7ޠNh~l74L.ێ0W^9a^;?<@yqD\-.t`-aJ;Et )29y@ ciDG%!2]hmr9Ex^+.PZOЬ 92e nb+++T+U[ #+ &. +D~sOݶm<䓢Z@P,rLn0-='f}e( &f1KjC#ll/JE* kK|g8f_SΞ}N J#H͡oW~?f~~yG|B#rv2]PiTBQ-#Q?E[ mߦ k>IV! LQm08 Vc%ْ!@g$O>,G}:kncx-Ɠ7kmGUAEy}Z⬔Iv˸ ][x*jke| ^+ !U   k\FXy:`&W^c̕/ݗ@ymIyI; N[`&A܇>Ƿm ;8*\hke3|/q6Б: ]S]VJi#Ajd&C4F4J7.\D' x/Gج"U ,2N|=( Μ:oC\g0?Hh?=Ld|n6V+67m]!)4[*e06!G__?AR/ |h4Tfg.S^[Ä>(gh MW(CZ+"ϓϗg*96He,!c.u9ha<\| ްMvA\o/j677'ZV"c)f mhP(YY]AEV %O/G>LY Oz {byc.vAbye e9q5oҬ1NRkx/s3Z10AQX ~inf0Sm|JEVn(c fNE5(JH|RWǴ*°5]3lzQ%񂠕j5Z8cwm G"]?0z7%"AǴʩI)&+y啗xW|Ĕ4z#av~SOfXX\DT!մ0iVqhdF '0dcv >~LmTUhh& ifWsEZHnnv?~V+FwkM.BY`xlY6LttnNIkpOԉ4jG9L`bjf._Aldڝ#Bq/cK mF(1\>G&Y!OVELm2DHP@j :ވLiS aoXh,vO7&W&ԪU._DZKRꖞ28H^BTX"vwbh;Y[[%l5\ʰs>:Αzd(jI{f7v$,-Xur9z=_|VeҴFGf]ʕ2+KT*he~wGyG|g.R#KOzk[N˦*&<\|LRllFŘoC1frp-mVЄ,,,8@vFFG9{J Z60tH93F/bն|a&ܽS%k2m~x{mdɷQYqTu<'v3>u+}EYV &d2YAsL'Z9IXL|7YAxR`Dn1<xj:HB˗j/\Hܰnd~Ak^%PP&i.ci=̥AC,/-6ø$,$=z\aDAhH 8hNPFFZmHW{VФQE5C0vx'GrCR+;oPnx.] /nD\0B .3B:tgzlscQfqM`8t0F{YԾ[+fVdjBAT?{ݎ̔+XDT[sױ AO`מ4 $ڍg<.DHi8b[:&uwǧᅬ@E4v8.x32ΡhYXii #O<9 K9~U|fg<["pH9&{4}?#<66*ԛV[$źD>Wړszٍ _?4r|S+=EX(<۵P* y.CK YXZa'`Ƹl61a2/bCEKim:A#ƍL5ʢx-Moc$VR-;G'Exy^+_"!@KB4Tז/ڱٹyhϥuRvIg Uw;' >IX/+ePg|"F[[شu,< h̩m8\ΒtH[!?(hDkũ_qK52fGOQW}w|??VsX=n6M0X_bqt!w_w<;O~J_IR8%qvy1lu=E2 y>QOk1S}zm5V顝#?BRX, 榛S.)JI__%Ѫ/WfEfiV=dJ1Bbְ+I]PƸV71AB#l\ Rx[ux7^+3ȊOLp"RFlΧ\uVV,-&T~xZ@!J9fvyÕ#rNˏsڭ3^x-}z^Җm.%~f.Oޱwx7xG?d~nGCdzeS Vٽg7+KF+<'◿uJ>.]`j=h6^U+D:DzluM:4v ƙ_X*J 1B?W`}۳mc\\_?&wuܴ&ƆaiDjk,kkk\tKf;Mdq[]69tmW7+f&xx`4z0TL_y'3>YY[;IY4miBΝ;3Օ5s,ӮQ,?)+ ,αHZC .ij Ŀp>-J|nGQ^e}yEٽ{7++c(JLNNRTFokj:mqB/}#EY]^ M^{z;q*ƣ-$cS掕%6F(Wu `G=u'{clpmCcɡdP"T!~x4^EJjnu9Xh;B%Ҋzf;p-.3~RҌYZ ^Xc9\H@;ނ^b&!wDƓjhVFHFYMh7o3$vFR "++nƕƙ  |B1aj>[Jg >|jm A$,`6ar0(-) ZiJ)- Nv k\椀́O t_CMh!ېۑnIZ[-i I5:uxe<)a֚L灇bp`+3 H6N8NV sc#MqK'39)U+!hi3musck!Ͳ}|;BX.iv7_Z@G-֖G,2?!Eުpoq)tt pk{к@BkбX[CHpѢظ.aV+8).\aZ0"zVlRc@"Dg^.yjTmfIh[Ԗng9_Oo2~BvA-X ꭵ\twV? ZI?NF#?q.\'ZLVũ0L8h! A i3ΉY_[Z.(E)Ο;Oα(ʘ B"ok?)+K eA s=w #uB^o4^Խ|< %Vpq| _wss(EW[ vmsș΁ņĀ9%l~Zy?ȑ#(h4F^c``YT+ &(6Wf~i lA9׻!A0ΓO}K,-%_f$+S OfOYhqN!7Hz|&.}6IƗh0U*3k6}Qt&5?-<뽢쭾cB^j^I;~B(4"d\Id*#GX]]Ck;x+_+ԫvDZ (^:3(W6ge~vZBNZ[ǭwތ2~+\L6W^Й|Pz:*cb(olXAdq>&@:eaJ.\"A"AGs{R\qxFcj2H.GCG g`RY%Bl9]:>N/CjޑtEaO{0YBhܕNY'Y<9#b1&&HAd_ 6' |͙aKմ'<ްOסRHdHAO-¥Ф}n>t3_x@Gqm d'et'h6VMiƦ<JYv`yqZQ\p,rʅ>s7ss\6*k,0?ayV=HuPYG!j٬ZcΟvv))Ql<;ȹcAp }cCd|բ^;}׈Q&yOwg'eٞs[\]AY\\J*tU[&HWS!>!AmRuIJ)2j_]Fc,`<$hɌ _v?{Ո>#\$iAK,dYZA4"w/>KK oL_4NXw(D+[ϖn!ͽ}Ű!l>GYe\/) hY涫,mq/gB.}t2|gttD޷ArS!k˴ ݾ)?<,G$ksIA`d 2,?9HSO~D:LHLLW( W()dúuxSF )\d3aĥ c[$!İ{Vy67klxk6CbfsI_aS >$c ;ӏ##(s3ي kK/RH|?k6fٿ?cc\>Hamumo} PQDFݷC77^{9u} s3wz2'2YDD4Ķ(=h˜cuIV~p#x\tHJnV謭*v.err K7u;ݶ h<'_(Ъm}!Hax±ZG ^0GRqvr;ɗo_n8E~G{"NaM[C)2 HOh4~7)J.Y]䟟5+aY>81K}:3M<߭6D[S7/qs.W{o:B43CVcX\ZanE1Afh$IO!:cs,lM_9ɏ>(ݰ_=A[ZkOa$wĄtYL$ iPҪ)YUkT׫V .mimKxT֘1J_wyT+JEzypth<#v+ Aҁn:{4$lcT"fBt.EaښV9up~As;O4k+L_BVs6>u/RN;6mc̙S@:d~2so?ZF6#V}buuS9:w<U-ld&Bep%6G?S*s@WZ\FT+0U"kA2d}HlecB oVgD= CGL{>i!PLE!J;=-tMP(#"%x bjvbD ˋԪ5 h+a`D)ծ) sg&e߾O(63y6֖:;fX<.= b_vn_p,Xghc)f)(WDr忔^iGJzd25.yfQ\?D"=6_O=.v^omG辋4/ @L8W ;vc||Z,_B5;=_9n"U+p&-c8BTX^Zbc}F[mpUO!FB_O92/l6tWTՊNP0 W+U0Bp6sJO  ްT 5Z}G^ܣه|z(+1={|) Qo6@imdEwrfڭM ̝K7m >\i2RYA&/]Gg3\x@up&2KK&cH$^॔ clll8kj Xv^eiq(0jU'ERA \Pݨ$wc,BI z}FFQ&o^hO!kSF)6m:gÓO=Ǘ:_|~NhLd|Ƭߥn(hu Y_P7tDYմ·V 6D ZGƥ81*|GU ٦^ʴ hLÎ mЙ2fZ1hK'L Fغ'FP-Wy78{\t9";X]u\O_^&"X/=oڌ+d2lr21ճF KN4::ʾ{jAqVC_\{졘+PՐ GəM fa~~ӆ}my6;nsM W WlxG1ʕ5Z&*T6Q(ly +{ ٧ [\ +ڟ^#2TUZ"E@@87|Z{wbzHE('#nAhGd@{H-(RY_T*2s7 KKږv2,7:Dy]yng| p2ͽޏc!adhlh_B'v\Bh q&I);ڱ/9zܽ%>[NѤR)"rv6$1{1_&f3ba Akُxoj9 xmѴHDt߁Lv'B:: 8*)%|:q慮 GZuv"uf ͒q4Mq !~۟~6k* zdKW+( m?9?lc|xڸq+m5Q~o]>TĤۦ i4&k%a֑HV!Vq}Dl۶!~` l!D焌DfrG_c//15y v6Z|+sdJi7h;7u *Q:T\?VbcCmsU=sml޴jNAXq6LMr8jx~ ͦ{8t^>v֤崾TK!i~%2Q)F8a!%]=ds9v5JҢ![ D>Ţ}"ڂt 62%#VR3O rJh4`AT K E\Ĉ (錄|_?`o/W._g!N>BO<ʱGar 匴\ՉqƯ\f~~cL@l*kYL\;pHh(橧3`06ZXc~Ji&Eg*Y쪿 L[Jv ׷x& R0 lJ>YZ\$;D _a"^[Z\ȁRX s}zi\U/_brjXG(Fݶ!CChct"afzbȦMtKKK!$%J˶[P}Ah,g`ZA83:,k[?tBD]PH0]ƹqeqTG ٻZ@S9t0h&ٱ}"VWȳ_ʟTYV%Ol:RZfIҢܑv !jKAwym5z7;^ ]O@PY.3;=CݯwY`qAY V{5+S4z`e`6_zʒéh0c:>`QR2dK!!?gO>nlIڜ{bN5G,AVfB#[L ͅm!kk!-,S,uH^gvv/}K *)#l6zg ]zue6m^E$qjk.D&b"S Jݺm{Ǜ E˱")XZz|S8)%HGl, DD%QiggX1P.( |S DFe|߰wn2 3ӌ_V]! aTb0*Is"i}VkY"(4]J}o}uif׻ֳК]58h*F1':סՂj5!Y^Z+ǵr-{Z¸I5,hСYhbN6їQ6 n"1>~.iS~_g~-\֯{s_L|Ry,M  9[ThHN ->ٙdw'W~?˹YX%BjZG9hW۸}p9H) )< Tj144ۉvt/cbWh]]\r^r-B-&]+%7ZXt) Xk0ƢMW VN\AyJ)fgg Ȕ$zQ*5&Np83S3DAZ!MRD zɗVy[_V\Ax-Un]o+w\Ef5GRdPGO^^M;ja}L]͋orTFDDcO|}ws}ryMQ ?n XMK_ #w CH% B&tȅBcǎaevv6A'@=+!([pͤ \)% .]!}2W/3;= Ƃ\kڲy3RS'Or,-Q^ZNb,h rz\cANSV:TK62-%@&%tV&lJz3^n ZKEdR~^1ցl;,uLVS(hL`u4EPDǠ4IHH(וj$=ݽJ\KhQJqp!ʋK,..4;/Va zGTZ;?^i-l'~\!jݝ7Mhw^>[X0"1*^JZo]Jg揠1`fa3PJ9bl~`A2:.>id 2֍MԂ*gN˥kx[wY:455e% zX6:2ӓLLRUe}:D/x~-g_8 ن LLN8'#P BOOkkhTAbJ B<0F,zҤD5m]߾Tng, Qt6aG1AhFru{&A#t /䎚Z"` /\ !K ҴsΧ?6kHy=䭓'(f0_~6hm,)UZY^tk9ZJ]]Ha3a J)z{a~~6T*O0==K/oMB`D/I/FWv&)(fIC/|)?ΩS mؔ<:fho\ڂiN=ml-QTw556٫)e. O $+:υ8ha=\cdeXHl̖bRmRCR Z_xÉ]([ Yue=A߫cdz/Q.]MvHF:>>r(xSV\^nY8DiחlU6~&c a){~STk5FFc~~aO"f ]TMo'cRbL̓mqEss!$FHPmYZ wq>Q._DVksȫ- 憣Hvn'b)<ǸAw|n ̩ vu=5K:OBk-ݬ8.6=(byyIG깰@oqqk sss訙OeG ͨZ"7n L'ϝZs9fa֠SڵT杏WJHS$3ZŤe<ʋCbIH:ϧh>.)\VN!0\xИHfFKЂIiOn8gx9\԰|D 7I6@Oƕ N0:Yg%j~Z=J)s$ hZL|xc9p g OXW!.cԿ#e6fyX;`]c (r. $gϞ\.;] )g@DMpeJD ,L32ǽ]=Y^ᵗ4JlZNm嘄pOnΝ;ٸq#sXc-7L߷HpRY.37;IcbRD|T)B%ĪLRAZK1!fY2aH uo5s&v ZhbZ+yh許BH@Y k6h=X bZ+6R>'). !(#.Tl&i+HZGAkI%_fUn? [~IXaيh߉Gp$^.Ol2'}_27?_B6%fvfnWcP*1Sh{v"Yܮ_s?wJ !JܭfTy."4AHE"G `uD ^ X,j>ez*&T7A;m-Mdk"LZCVYcͻo#i]x?&:4ȄS{%@4>=UE#ҜdI :8h-u$vrNX$^T 0Q#ۓ2Eu P62=9傃[Nmw_.+VL=YB*os )10˜e PeQ.aM 'Zˮ]ؽkm: .Qld`p["Q.:9qYL!ŋd8k)zCnQ9%%Te@jTFAG]G@% p;s.>O0<:F= )v82iU-q2umj҆Zw\|R64R@.dBeNʕ*_%u4R +vJ086o12:7^ůk-~#޲ر];ٕȻɬ[c~u KK$Ƕ!5sNR$hZђNw-`9ǁqY$d EsƯMחI9 yH?ux%u)PטTNb.Uir~eOJl pS*XDN0-F*acn"HO""+}P =FyS!pE ApMe}k,q/Dcǟwu7zRZ!߈fCmEUbWsXfW.T`mG~gپx3Q F Bc6S$=, Ƃ۵_;C>]{$^qq!Z 6pR)Ŭu4^s+ٰe>D#~rm RYkU7tꪆ\~ɫ8n슬MpOk`^Ld(bzz(F( o%PH.j* ~E(0Ʃ-儳_4Q4X\#@ њ!eb& lrL0QdKarQc0ڥع|>ǕIZe{F  -<iPHB3;688ؐTnt$4.l̿l ehh> %\/\geƬ z ""@zwlq%kXaF"&[(|/_į0ȢGϖm[Yɛn(-a,pb.,N(%ID0-<#EzEFLLmA[' wtWtDp86o/ya@335bN*B}~FG618(Fqdt߿ %6& :Te Sgb /KEHXaD< `~,\?Kqifg6)M$;?gjw IDAT_J[~-ez䗭r1=ǏAeq%%\Gb2EGGj'lNP$+N>o?mNOAUo_/AD!xfʢM%mrajW8pv^]CluR BΜ:,;5Js2֢u J0r5-C\%̝#^y8J7 dD* ۥ#qʞsO>iΜ:?._hclkmOl$i& qS,vQZpq8E 8(D "ӘxhYWbeށ18&+IV\Z ;9@X1=Z,Hdڕq&]AGRݭZ%ASľ?̾ݰz06sq!F㳤 a;B$= ] 22 =NVcr:jkRfۖp67kNz#jQL5;SF fz(:CK~{ Cŋg짯*Vn{{)JdrY0v8X$_RY-4B22:BRF8x(L>K6% \qa fG,/r8z08C}.y_` p4#f\}{5hT*-z__6mbrb( [(Ww3.]цAc s4$kM$\g9ԪU$aHh:t'=:a* iwI;!W]et=HT#c W(2qcw`X88W<{exdl%, q"( a3}>y];CB[߯~^#-zP{'8 ._~Q"ഒx`$8ʞ}{ٵe=?{o㞻d疽hbNyIh+B.exI]G[*nFFx3A'Da 'ݞbeP$Z7 ;v )$ss7c \L&[!bDSevRq2ĵld2lٽ={ax`Mid xn#׽KsFatjmkq܍s4WHe >ր$R++%cͲ;̎Mlb&vnxri=:եJ~= D 4%:\x֢Ə}Q﹛1S<$ X#j5]QA@"h]\M'l~m=@1؏Z'ٮi,BK0~2z'W^ZEťEq; ~SQRfI}$N^'\q0<maxY2OsQqZchA7PI / N| 2|LrIMR/ǻd|kE!/\̩ST+W'AܽHk,Q6v&w("~aaaudZ<Ld ak$ Ay:G L|ۭ c!FG颫LWMs瞇<Яq⚝%NA՚!X;1-e'K  [#ݟ|sZHW2N1[`1?`_繗ۯB;ԙ $ǠXen:LՉ~m/^pzZQ>uŅ9U868A}.[juZ|2͖TN >P֒z=+ |\(+]'Xm0qct 6D: qN<,:Q6bvr C}<8 k- Bˆ-Z.]̙S'{.+Ÿjdl3I)*.Zs;>nnJÄcV2& + A=jۀޯv(ٺc;=xXZZ@\ &6Ƒ9D\r E ;0JH-Dqm*HV$&Ip+ \䭓oɓ;{..cK{'\7nO`p#f5-BlQ[,͖YELcc!NI/ q}  &Ͱg.0CoS _T~m^~EǕ-)cyIWWxV%n1@X{kZyku&w~'~<=p~ލjk-1r58n %&ֺ!z& YWcQx[o!ْŷמo!#2`k JIqKtAD}g"UO+%P^h~l=@15]qMQę( KFmg,/W(/cc#QYKo6d{nG" $V@G8&]U\`ґ W+OO;-Pd'dnP:h7ZZ`G!lRDEX ZYr,|RWR#5N|063"Z?P(ΓvٳRTz n$B$SXNRVs!v C182BMP48{ɔ-h4ק(/.G 7nK:reG9(/*x۶7D6_d]9p:|GȖm,.-1w"9gmSN21>I(yORL.\+_S'_$X#%UK,n6|tj] hf6):oe䍺j\(Uhm_Xdi~ F[]a-B{w7Dz{{! *'iWZLK"B8քz磿Wwo"sWdnޱsY.w0aDc2^9~!Ojkظq&@n^G[ƠSRS|p)0vJtvuۅp<"zoc!TC!ٱcwy'APUH*080HhcՈAe݀@I dDW_5X!J$Z4TjBa:LqAN+L\6K6gԪ5 $e\S.HE(eR*Ͼu^xL]Ixd{z.>I[%H8ZnŘ8%DjaLob2έ'Om%j|Bre(twP'<2F."!D'$+f!sٽs}%*yfggV(Gۓ.B)P.f`f BM!+JɿGaf&Sn?}ksi^ouk~%M]ly : ¥W/PZ0֥]ww?2F1>F7`ht3[wgǾ;0%]+"1":JZ!\7k췮4 A K5&/URi b-\m;&w-{gp͠De$c72ɝw 38p;̕رs+lkpF}}}AjW[zlr/l AbS0hEȥ癟E^e?筷_'<55N"ұ$_G*)t3°N:PZ߼mݾ7[bXh?ހwWo! tQk>&  ¬Q_AȥRku{lpt~J)ݯ 4;ҥ{-hETF6Hl,0W(,TX(`qLWOdyyHOb4mЁ&0y"|+.Gh! 7KR$ݖb&7lffr/ԯV?x>~V>m{|n-K/tu *,^Cvy¹2jk|.] xH,4}O b@(1B) >?Fu'z> a`*O6gKX*Q3ݿ2FM1_#&dtl#z<&֮k qxWG3S^^ftt5KEr,SS.k#*#ʐ)rwu|Ww;+\h`H;ߤvZM(ɸv+%R7gؾm7קʗHuy`^Ә#!q`/ǎsC򬬤n k1H)EZh=vNę~vCs# 2vt!ϽK%>errb捷^_;v3f`~`kѫiͿsmw_6b!fWy2֚uA@\!7`qufk99 uU`,VI103khŚ5ǵe)JR0&vźJ5PTesŅ˗C.]cA`wf?r\{g_&HJ$K-J,ˈljc a yI? oH,0 WN,1epr>]=yU 2%Tݮs9,IٶN;$/fqS'!CT{zzK# ֌e+i?Nro:,Bw ڱ61ymۯw4[Eb-*rõQ<9W%?R ~>H+\)8< *Ȋ%~zatdNkkl%d=l߱\9!"s3WHN]@ny*S%04:!q6ݴn[S[-[)Fpu!FxϑCH XZ|j -7A5h"a)5Z-eCkPs(*D۾CM}ϓ/م\Io*NXb[$tW׎0&HF*;wφ`M[ٴVf晝cuN\m*`NO2c-/.͇(5]k,..G19w4QT#wS&5J986f2оB)sHɣ_|gw\hX}%Z ,yqj緢7T(ZWOkowb䯳]#od5%J(ogd.r/[2&y[[/13{&2v~<6eu|4zz=1==}hCc}l( O' Q` 0ƁQ'kkq esݰeAJcu ŐvRp-pF+,YHJُ_P2A(񎃋y4ҿ]iF6 Z|?@)W.O}v)/νfaz>8en~֫qWxp䕃 1 D9 9;?]wݎrKq?ë$Dqtt'رq䥠+h'HUCJ~E%H_Jx[LJ1fnjGdt{ ŒR=̴E;meN0<[y.^⼯@,R!V"^EG!mk= Dr|](PבbAn9ũø֎]Dm[,4M~dB js33~Բl|˝/:v00,^x8vq# Z^lçO! |~1N>Ck9xf$G Gxno^[m[9?s39lٵ=8>WL0d+쪡6~ B1𫯯Ҵc; [i,)K.*pP阮Z%m(fS'ǁU٬x>ִ=ƨxܞn˨*aX塽s]wq!4j]sm>XTb<0UKG@2 fq.JgO 6$NR֭c9{SWU B@jPo?}}9PBJ@Z%c;DUȺa6!&dȕI$,s V 50$Rk%kJZwA3)9M¥xKn#vU; >!I3FXnJ}FR%J[P&%J:Pݖ{LR dՈnUt=|ࡇ䩓^oJwe$4j^Qc U5c} ^9*ǎ&I8>8{jٳ|_`~nW[dNٺm oB0AudKSq9@R!A@n/x;ȿuBWD  RI9+Q(%"tM;w5Eew-6aF?k>F7RmSqI ("AKe% `,,siCC 211L:UXcHҴwi&ţ}Z+ ,k sӨIA>Y28Q GT9Y^f6>x/K%eP3n}ɋSwI|dls5srtg?I2hjsOOׯ%L`x'I~{u*ImWB{#k Yܪ;^P|chxjU 6P8*)Q=@9?DO(%Ne]w_#7m-6גhSxqy[WukK:Aq͢Xѻo '[$W-ѐ\)K#L]Y@#(Q&=cT$Y4˗ޔXD72A(Q❇jHXɘlkȨ tH>JHCQ*VP BkMfΉ~X !A4A%FE#ZG%Y `A9%S"zED]\f~J@$1=8:IENDB`ukui-screensaver/src/screensaver/assets/wallpaper.svg0000664000175000017500000000135315172041035022114 0ustar fengfeng设为壁纸ukui-screensaver/src/screensaver/assets/settings.svg0000664000175000017500000000221415172041035021762 0ustar fengfeng设置ukui-screensaver/src/screensaver/assets/logo.svg0000664000175000017500000002762615172041035021100 0ustar fengfeng ukui-screensaver/src/screensaver/assets/setBackground.svg0000664000175000017500000000135315172041035022720 0ustar fengfeng设为壁纸ukui-screensaver/src/screensaver/assets/next.svg0000664000175000017500000000055015172041035021101 0ustar fengfeng下一张ukui-screensaver/src/screensaver/assets/duoyun.png0000664000175000017500000135210515172041106021441 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATx{uQ^ksνHXN*NB\[IFF!-A S$M#IR$)(!)B L "*J)@osޫ;tZk{h{{_UVYi%wOC6I"JOV9xNQWҙaэB7$4`e- a@ʚ) L<$T @/Do+#4~œ=wz*4P̹̟FU?IC) BPeP(=`նAƥ(0jT |ӲBx]p}(.d?v~l*aU>%o/"-/Y>WOOyzl@ &BV`KD\7}n `,>;fbK 0>QՀ PR #(?K=o>_eUaU/WL/W)/")2[Q#] `AHruMm ކc*&؉|;@`RUhQAEP 8 0:@@>j!Ҟ(_L:M/~!$+@XeS^7He,}Y\\ ~Tw0uʺS炢fA'+ZM` AHQTHNTv~T.+ cL ,X %w-@@ ~G|o{wᖭ*O@V*|w>T)vPPɃ8 T]S!Eq$Iڝ)4!`KLRIuPH"9ᔏC7!?_%WY VYqek_ Z=[,\ɑ*Hr7XGP39F5+ލwtRE@W S@E&,?vspEM*X;0ǒ5b ĸ Ls(TN}{a/CS;<3 @| ? , J'_7~DWS!Piv`Ԁ**@`g++&d-]Bh-07UǠ_KrDH$ 0 [<@8 Ԙ x\vf' Xc`0P0xUB %+`Iv;n^zE&؅`64Icq \e 򬖗ׯ=?ǯyb[t"0)AILbfԂ @&u Inu{`f`CVW&s)@Pr@c'B`@@oDˢDnu"bly2,@`^1XLEB4iHNc~ /kUVy VyߠrxNg|FDHLP2qUi3^sz:+̕ W|/h>ktk7?`|HHBk@ g`#dHM?dmhtxPH >ny{a0L @U` $_^KW!\y0 ?<1n#Dd½tJ=Gox6 {M`? f3w~UVy VyFKJkr9P(̚oQ";s&OPrbdWZ-ys+`‰`"(  4X-PJ@0)WPD 椷;8b`*K#=P!WoQAJ`t2-^5Kl ! WT c(|'v-Z,fP h8&C1O.͓ Dq=**R)y#*Cm&};"b!TT8_{S\VSV^Mp/^~R4KEdUab  <~扺]9onqkB(ZrG-WVĕbloW؈ *2zd\]vqrW7 9vӮY,kLBcij x!PDnhv@M nT@c~껾\*-1vXX\?U{Կ2/\طHu%3bDs nMv;ȪvժU*" b gGQD U;u^ yQz♈WXA:Fb2Y&%8zc7臰*Oa'E^ڇ~iy/u+t@Pjnཋ!q 'k Ό!'n1[uc#ڶV3 bVᩴfMML*"IKl|{a%(rpw@"DdU)C P0:W,{t:^(J2`Uv`Z2cTIY~:Ki, !IPEHc y#:gUIBH 6@B\,3o۔O-/*LU>ao_틗0V-ς"$O3,ڬx*M1D,SPAJ!%D_z=V؈ w2 O5X\_hBm>ӅiH-F<EKlk S䪓Z+t bԠO ᬍBʔ`)+]A&ZD!Ȱ Y)Ou TK/Tg3"h$7B]_>qM<8hSTi&j{Aj⹌WאP(Rdn~9mҷի|bdˋ^޿vo}ڂ2hdiE0!YQ]hHH2XP~\ll6J -fQ2TU$K%I, BSv%%cb0b,Nb oZ;%C7J_墋*fw$Zyl5\[FGoj$6׈UZl pN7Z"M!o~)yVYc &}oݻ 99?eVGdeTT[Ar+q z05MH(x!_DZ'5S8xcEƥ8(] iH/* UE}y-L!tuFkJGU1 2X!2y'>F 91]]"{kdG7ՁXlh,`B5` \^{Ws/#B\ fFZ&u$ J ՂH[] }v-,e %Xpf|qKW ;I+n`vnx%0eڥiPw ))ܺ&pӏCPԁF@BO97cNe[SzZtAeܨg]gw1TD>${5`baRUE>CN+[3Gx)k3MJku٥Da"(t3@~ұ:@@h׊myz#4zF].:SC4\;W8 êy8-/+Xe?Y*5?w{|i<T+?,D˿gTv"肂7Ő|c rRpR`)}7@ ⑁5͎Z/Kd T4 ^".vSyE,2*iy@ aI/kt=PT-LdX00Si. T@XSbsx 3jna+*&>\shKcH,{P3D@T4G$s(~T} Ԉ|ŝ*.Kö-W?.\(ԝ*Nj|]E@NZE[v`6vk`.?řn UTږOw]B';gCj;ZԊ4it}}S @䯝g +*AYlPF vyIѝ`\<4+>@T~8{]yCvcnkyIiEwCayMfC Vw5b7Hpv®;̞j=$23'wU2 螦>>@=> v B>-T qo~oUVNV~o~? |qZ#P)}K`mi^I4GWDgnF18S5Q1U{qtsU }P3*)@ܲD7/vn [TBD'ɌMC66#`()(=X >w g Z7bVV0AbπmA`xb7ُl!jUaݫsԏ?B$*`P_ Xcp3rJS>O?7Xe' +@X}Gڻ-bKDn_W?ĩf/e(QMu+Z#(ζ0V#sSq˓NJ^q`;=Pπ l'Maa0Zf1_ijǠn|倫!&:Mm5J^,Vlj;0aܳaDv$VP8S)E-o\E@gO:7<桛(S׻ئEǔLF | Ct'\ c%˾؀JPY8 u 4G].0f:3d(/}ŷ]޾/D} ڂO @%WW{U?~TGX؍f@q^916Ǵ/ U1V|Ȁ叫ԋ9up"z!{t!>:I h!X"R{KPT+@5Ÿ́]n$@p62n*<20+.0B{\i$QJȻAv="r^e+~FPjtdŲSf_^g0BOةPav 1b z$26ãy gdga+W]~Vyn1xIBk7!̇K B,O,(d㡦,0qMDHB5҈׽zU| 2bl-Pq='+P*=o; H܎@&nYgE0 .'F)Apw?8^DĘ`O{>,-$,}o,ia-PRu7xGGb燪^@Bu5;&;Ão?~d R }yg"/}M~Ndt)wZ_-F  +"Jᓯ Na0dV'USaSG$ϥuU} =\ ͍ IDAT4Bb.\6…b2k)()܁` D ⓦNӟű[Lv plqa.1l%~Vb!ݐH@#.oKyݣW!\ۥ=^!Y"O3 =SdU">7dי WobσlxJyM='|g ӴʳLV owy>h d91:Q 3Q,^nX\Ŋ%RǾBF;;8"J '!juMDN:klXcV*8t\6Ehk>=4/Uc⨭YRK^N ##6Ą!YIqr"0`V`qgLsb `8_*"x-zԻP0,klgS-U4Dž{}v 3ҋO(]Zi7=ꌓg.W"g*Y³\^}cwiS!Z/ Qn x{AU(2EpͶrcm i1"ŝP%@+rcIUrGb7J`C@wEM`. FG0 We1*50b_bPbA \fHu%O5Hx)]Y$,IlHtOt]1\U'Fw8{(8grw- b97bZ#-؊s `;i?lv 7zgY*/^? XQ߇-HYP^FfWآs ʀ*vlPtOJugy[ )$NV>)Lo{fuD;H7t)@6M>n3m߳:h-Ӏb$)eE& X393ri*H-1'`T-](p(}1 AɂAy\D* b8D6`DAɶh޽kA/ 4@O2ދB;UĮ@aȄM.vv8LGN<0`kgc4 8eP8 pr ufR# u}J@H"GLgglѬ'CL/Lz6>rZK $άu8ܥtEk.pde +t5YT^:fRn)T? @U+c @`Q  nc̖<EB wc|So{_#],+@xʟ}_q|xHQ9Q;{\zp@ ;/E1^4D燣h:`g8hpY58*_tȠ@ck'\ 'N#`664W "1|֟mLVpʄ@,6l7p8!؍ ی1giF)Rc./ 0"IYegz)ȝVir.G7W"k1T썈on@7Ro7=)3۳Ip1T,U 7X'hI8q͊o?<-췞c`@) @d/77Kn*OGY3H^wq⾦6*9IZ{) rHW6VLɪ+rJ5V7 ZIxTCY6R Cٷ@Ʒc b[#a~bEΊ11r !lnb4%k"X*$TgA( aÊ[٘d y,b`fpN@YTpg#w~l#wf\2"R-$ʊDRZR ( ^)[%|΅@Lx  @uf&׻u3Xn;n-rǍGuU=QβȠPTC˜1RX liZFŸɏq淽}ѯ4 m2@ NfD %c(Z%Ls~w&|v=p-Zk% !JbNa 9E_ ~Pc3U% 4AAPDZmc͵n)b@`v̵0dgj2H;^Ǵ^qVy =>\\>wS[)R:Kei{Τ 0 OIfAXnyj cq }a\?na s)- )[2t*h)zbXTv윒fS,AV0u֛$klEN|CM}yg4UN~}T+\4e(:1:hVf)ﲶgК-JմIVCu>r̩C"ѷߖ,8:CQ ~_$+0l7uo/ji,|Co|eg`R2kfu=qԧ¢~ M_?Y-Rѭ[i{ݏ!BQ1ӵ́< vO=B2(= A`͛[̵3^V4Ȃb64T 1 v>iǜYGw)n6Cp<Xd Pۍ k"CA?0H |ĽnN6aH5Rf@AÌG0M FBa(< .' V[I0@)lwbζ $,Ο؞lETO˜%:cmi<$Q$7R$$#jsDQaXVS 9mZ戰siMc߯{wW_*OuOVK軱y|AZfď6vmn X%lkF6hۢǪ4iX7] 0"KaȖ>EwEr%$Gs)\΂aJ? /" JW6(iw+Ŝ'vfx/X),BVXt쁂ޱ]D *γ96dnK8`;& @TjOVƺhā8N~xDldeӜM~fܹo (&eܝ cb= ΆmAlx-1a5XrRC?}m>6f -bmh,{Kޫc8uQe*k=9C#>lOO\^:0 L/W~_;U|ɛ{1<2Ŭ w=&8$_p3Mz "- rQ}O /б7 gVO +0;`2YD8]8CXp,j4"πEyQrcZ/VEi,W6-93燠~GQp,bE.v,I͊'Ď3=.g­݀[g[ndld RBNq ~o%1.``G!޳1ŝcB:(A0 g2;f`6#\ #cd3<#'+%]OPnaWJuCf4ybU[uLsAjRUѳTO>g4vsw$ԙj:2wuyw?yo|`WyeO#s~x 0,PpDXv3Vb\}nJAaQ]ᆀ]h-6A\@<1Rxb"PRc 811aÄQp(o`36#a3ZpŘD1`=uˉx0W> hq큶l>hgi"ȎF DY,Q@u\pdMy2 RbK"XY1' [f1`(#n@@ n3mɌݖ1T9HJu0Bd"Y2-|!`3R+PU!}@i1?q3Zk 5v'5BC.#xLڹ/5*4۫d_M:ݘ,8:wҪqPG[' h2I){>w0Ub+iG%3PfX1z-ΧG;@."|u݂wb8ݲM}>U> zH臨&pR m1XD@DJ(8Gh^a_%'n=RX8a+s, '!' |t]u^4Bt}Dm!T5B0\+([lqZ۬Rz"V?l r``7$ LbR a['LE`~^(1vgl pb8 BG`L&6Ж 91-a-$&!{wxadşǂDL %9!j%fq ɍBSCIRes9Qv`':*~\cm{>|_6~E 2ڸ2::=?K _op*O-YS\;GՔqVL*D8 #1c|\ wc+W~ İlWTSZT99GQ@696CXCE20a_<7 !91lvn1v"YTҸf)ZeCfH[(LeS 7ݒNWUYadkXm 5#'v0ZBQfA4!;LF@9ƀ%B1AP` 2۬͘ L 2+r?L,-:tV$ɄMv47 +a@!`>Yp,.a./&j 垭T0Zkr&ؗT)MQ6?~ 1Sm&1j J;C@U1Zk \X)j:P@gd`o_??CDzʓ$+ãh008[@JVFR "i`$/x9UD绫͊D uH HN@Dj+ZFF ȭ-Eb3̘1AVdo]!qBƚzM΁XvnWrC߆ժKQgø5 n_8L kDT2 cN`-'Hb̥0[ !g CJA6 @%eM$@L6x^ovmU@ MQ:9%mF2J+(5d`H;)gR(wV(K{JutTz@Y1WcҰ"xx$cgw%ao-l eTTeQF^>7st+{XGo_θ'NV/_3~ſ6#B[?ǩ 5[,\̲t@1dfD+פ$^Z JáY /Uz+࣬El A/ $ft,Rb8*PԨfE6*.BLVHwMjV6)}$!$y6Ei.a`Lr?8lUj2N|qZH'(ADwĽQ4Hc+d=) x)`gg <`'PL!䔐x@ {)jX ٜV 0ubHo g. 9["' $bHy8!yD ^Bvً61)A1dE9) H Lv/Ȃ mF:h4P532lf\^,Syj*&(\Hۥ> p{P_OXVx+/$siw"9sEec+w?ҟxͿϯ'RVÓ,w𑟙{SJĺ폂l p@=+z"$IiV`V aV%Һ-:~S )w20&@ܪօsj! Ap.,f왑kK̕-ּ~k?^9*0Y'Q^C?? k*VF,w;u1R-VSQ$t$<@8(^O?'&)FN`$Ǖgb`Bx->pEa/6oz"8 +!l`삍]* 0u a(b4 hqL !̤PBw1ތӅ3hi[(dF(Xm$}nI -f$)Y)ml1 y  쉡Pfxv` <k+L8P.l$?&c# gcd>DH8r2;sxPD%wÄQi݋8;VX  Y mqL@gy). #ȑ@Ų$YjZ?@h70TQ4 -ɷ0?HmY<暑Gs5=VTH1F8n@F *sw/"xg?g- .#;QDH*<#W|]W $yCqHՔc2 ,,`uDJ`QìP(,lllV嬸;3LQF ۜQr/=h RyY|Eׁv&o =km)dAn e1t,ga޻ʶe]1\kU9}i[N(<EA $) c% 6@2Ȁ!cA$@"8MCdv徼 L~nvE"] 9XIb\̛/RyzWIGtfR S Kw|-ly{Xa5xxámڋg-Pe# BH @٥ׇ[-`!sS:*cCNU(%9XM7jm"ޛޣ݃/砬8`stm|h0JFq13ҨL./ ^ǓxR7g~C70~MC8o|?rp:)d5;4*"NǙK76lcfGe&gaYBHIRbnNT+ ;U?C@;")VeYU!G ke! G5Hu'YLN3\Z72/1!8C5<'|( ДB9Y4S&kp4T6%eh9iAx5`!s@ dh .<}leU## JgT ٝnemZJHf>sS ; )EB" q{K %:<9ŠwUs0"p%.0Waqx<40mRC{ףhA'EP%u1q6h~yI^4S<1A) E}`Gig>=W;no ?ן[0~]C4>?|xZCi [cӠeҚKr{z]vvC o'΀7'erd)aqfxwR 6l2i2;RŌ!{X!Z )l5АM`2}HJ䔨6ɳNrnKԁǜUK@x ^0H 3>oqAc2u̖}yݙqP 7ò?݄it 9a$lR݁ZAbV>80in .nMқSlݚRje:%\ᐅ8ThX5`$Z9khneM&R%wJ28̠6c х3Յ\9 Ǚf|&"WqA'xʐT"Xqr3 "Je֥!tM1,H"hHC(K5" ٕꭕSzQ[k"7f<Ča/ eVϸW4 ;mޜE:pW/7Ё7YK(G}r{扃+İ޷zx >-:sM$7ڕ?$u?_V0~!@5??M"}ٯמMr$h2T#\d%YCI2sO$2i 0>^Z.pn!/~c?@5VJ3(f!QI4f vÊX#\BrgH.cfKi?8'dJ'XQrrROm,(4v#hJ{\9D ôc)ӂ-82^X.+>C}fjMa=,Xq|8:f dQhNtPBC11 P*^+Q1wnNjSfrbQnZsXZ'JJ ɸev0*+Jz_쵶5Y?}ylU98J1ps 7/z_+2saq1|+SG0 S?|⽯EMք^-14v+%Ej^'@1+㰺xxbK )hVq('/]#C"Sr O1}(^|p f`@5YTv2b<iV`O1heLa4A̴SUyv\)jaj9w0hfX̩"99:10 fM#͛nRBk@N]k=ϓB҂ a.»/PgOqŜT!UA̐֞QJtx2) $84jrdiQS]Z }(̵Kin"aRv2a,JGieOpj2傃pQuT2P W%BM}A<<+GK&aQVRy[ ǻ#RsqqqҤգqrW憌䘂 =(~ziq}.:7E9#gE 6ɇX|5\$Eyw>p|\ʇ.yۏ2Jzs}{q2G#42dJ_?~G>70 ;~ܯ ރЗ"_`#'S+I@Bsl6cFT",9SK#D8Z꯱3x/͡ꙃ <~^(ᾠ^e0`sBSD0r \'F XT YX P +i!.dh2ZA5\$/.w B\E7{O5CrM U R½2*ٰ*pL\ǪHRa^f);m$ODn!SCHpRVA Ujs&ja%"Ua P7%P9D;ϗ(\@TK GSaYA4LCjSo{D]dF3 ^Q>4ql)a^FF v#ya  Fe? #2TR$(.tC)ޜ8<َ9mΑ]| >"^me|N׏gJ oNK>M9a#Xe0MRrRR~ۣ?G~Ư0/~2?_|S7<뇴sXUiZ548-͡B8#9ystju`A0T YPze%iͥC9 {>—pG潏?wot`m˃M-y 4gCK^텿]of-x~7.!C ZXFkުw4\ZVg8e ;ۦrRpwv%k$0\2&x'{pvTIW)+"?sD CT[ys+ IDATi<;yP.Bprf,s]CFtXyq\xB6eoAͧaķm eqm_ͽqv~`YfV+pbT#1 bm:*2A7PiBD9''&ݝgj#h9A{ZWR*,#ijdx2PuwKgL)\!]< Ř |b*ꛀE+Y!ƃb9ӽ4H mnOBhr ޤdTُux{DDq앚ib昦/Qmi0U]^2_?ӟG><_x7wh>-l9j^9gլ+;rПw7D= 9Ÿ;ne!vmVr*5z~&IVY`{-oQh 3RR>>YM4gD8q~8 uZ ŝq.QB1R Irx7,Ѧ8W=pr9\^ bDfYȄ-tb=te5%} iߨ԰N0i|Z|,"TAaL!䥉a̤1dwh-s*" tװ&ⵒS1&DFTb?k=r\ WG'VŘ*538ԣvT<Ƅ0O{qRD9V(>{tOAskfgV!q1^r"CJR V 2p -MXj]+*U"R|\xͼn.+sa\ӍF]ZdyŔ9NҘ V(w8eTwkWcM>x *7|ۏ|sY9c':Ėm-lޓ: LIcCSd@hӚH7^k]K^֬|%?T#Vl qJm:g)M#8 ǽrh?ZK%2iFR΅:/ݱRJe^b"$) RT0J*skp}ZSzu~~?u][=lwmΎ+MdvpOL9/5ZB؏ISW7nM!o֩PjƁ AIǐ4f%xq.Lg)+)eRCGlL,E.ģ* !JiAh0R#LW$h6ţ2ُLb;x6 (-<aZخJz[V׿) `7kԆd.An*& T4>H`XyHˑǣp`l8'SE%WCQHǟ7co?.~!@"w{-kdr;Z6X³p>s^o|opRCMi8,Jѯ? 0y t} }v%ôe8 Jp~Ӵ2Wjueǥrs\; #.R h+u,"bÌ{<Ô+4q9%)ZfT[fs'䄎D3Hq#l |i]QVF Yp1 S&r+RfTGSdbZy $ ѣuCܓy(? Y|.F.rj~ 1_t<]Yp*ʐaL.{8D'QRܓݘICܤKidj.KJ,!)iT4MB-TWڂjUCra4KL!?sW‚P\jhY\۽MU_玴m3m _i_}/Wz{BBP AJEN&cF"ѹYQ\"vQkC? *|~oC</b|ß?_wkF=pܫ6Ko7K>[ke @s 7b,' VH.-@`mo 5~Z~I迿wѯdDzykZk$L%q!{xOwse Z:%p 1c)ݱrw\8BI2:8۲P9.yqjX3im+8,5gtU9'ZZs ?܂5G{;Ec[g7e.G* U#9ueh4-zrxsi22fȎRGCMz54rt#")2E'LVaHCU/8=$Mp`]*źH#0yFR(N(e6%gv;ެ GΤxQJǶC~sfꋒ䴖փ]F9VnCzZئqO)DO[יkȔFʤ;4>J(W_"DDE%#_c3sm?g/~;;s|7Oڂ|5kuS?sϣ F<. H]?sZU8ԢM~"wAa[ m٤=d= %U!n'Lk4ҙb( !c >.wiu< KUȉٌnëx %QʹhX^즼-9yX((5j绰=[8Ol sPpYB$K)02%eIhI<)M;K|6@ mB-9Q 멆a J0]q&]ݒR;H)nUϱ*k4qli68NMnHH$H9!)Dxx;T[R)89QZ Kd!cɶ!Ŭz.j5;=eE0.s#3y&/FqLZ[Ӻ½r핵uMʺwõz>G2pwY{|"|Iv 7OxjrzrCn"Idh]#j0X[=P9_)~B ؿ×g_!HǤY+!|u10R Ah,5bQw/48h;-Vӫ>ڻmD6soIP>)㧳EYps7>A9-=`! 1%r8ҌŹ[*% Vr ZB}NQ9y1LǏQrgB!Ncv}a/Ƙ+^p4$Jυ M1r¼-M<-"W#s5$rѥP C/B>E[iC(S<z2ݸ0"K>tӐPLhBOi1C[lh\b w|@:^)z& 攄ebgJVn ҈es,;RKt798FՂ^(P3d =$5vrˌFrC$DL"RvpQ%x:(ӘLν}8nRL׬iVe%W3JJ:ӍUrFya&pVrJyj'vqN0UK8HPɊ+Jz̔sKuC&kq/lک+Rxtg\B#{$ 'ųdHc֋Q NJwQ#rw|]?3䁸+WwoOaz9Zl^z-8φoon!ߚ(vxY1Ƒ 07(4]牢wt6t KT) u9(+Y].`ō(@IgJ18Nkȍwzumǘ!YYL)x/Nŭ%-lz|Yڽ.<̓HphGvodfRb3W =Pc\{k[Z,x]H2N=-nbЌqZ!7iHLp<8;YB+*]OZB;`&RbwyDF[lZ1"y&&wA3з $-7U{,{}w/3?+_ޥpXqr@$us^)u*ГW>kGDM4"qO$l4tj}*_6b~TZpfqc WcO.2o텷.2N͑ꅧjKܿP8Z5|.6D(}0fD! m7@+fCkQ^_f$,j L"Q*óՎ!%nwC`GݼK~Ncu*ԻTa)FN4r1 ͷ r _;}@s9gҟE$$,ZȃwގL=VDl?#ڂI2ɞjL Cb4 l͡Pxc3έ(1sXq,+kF@*ϸm8|]yn "8aM~*,G.?0@ʂ.23zŌ AE:H*,U0 'z,5̞R'hQM,NfKOO[g5TE+ ; Ig [ aβhN8Đs[j#gmQZ+RqTR Bn 9aDI,Jږ9 ٨KЃIՈ*/qH\C }eqOƛ=Ϗ3Ը&Ѻ:KxInuh?󣌧_̯kOS6 IDAT\`k{p@\8C/Vx.ݼ+ ?S䥗wzl-YSDFm&[7dM-zq74{}_NrXFra=Ydمg $޸G0 L9.s".FviFjHr&z쒝]S#_3ʅDnG;$rxm\?,V[bQfdN >3GDc0#i$sS~Y~y .*աv^*5>iًO8 YtY?ox0xzi</oO~k_a+Z}*L|oglyzIg,R_:K^^ص|Wڋi\92I8j\ z`'$0i屚MC<@>mJos|ݛ۶Lk#zO?xq78S-xMp[޹1>yg%C0󡢤vv;r6!p;.u Vv|$T%?߅w>ZSUjVb 0R d-Qq( ?_ZKn̾d#PXEoCOw/|6MwuLAi7z=?4Ȏ="x^*kM> 5q|ƒ|T{{`m Z`H2-֏4urѶ=yKmsa.̋aǂ80 :&➿8̼9jX4{'eKX?{6.+{_4Ҳ 4)"ewټ)Mߒ%"вF}^٦{)Q h\12n7)8g2Gps*TZ{nbɗeA4 IzvxzM<WvUxq0tT=yeb@Tut94IeAA32]Jv";J@=]^(2C :yS lP ̈́jEȩlۤZU&o?7hN ^N@Zv>@t.kjXOͫU )Q*Xnfk޺$8:mm+95h#'Yx~*'1c ԉDsX0Q^ nf " S,U2ʥI×CjɅl@{U.r=M\MʧY]PٌcYxqC:g6CN'Rx<&Y7kِSHBWe`@@Xf&zWVM$k C2*mo Eb?=!m}g9Zk<!`mRk+B($X!B)RDb !B- 46RoM$&ƴ17ϏZk9c̹~sMc#䞓>ϳ^ksr3Jr!x.d 0B8d$$Ik!:/&4*fd>]ۯ\d nUc /K6 V#dc@v/w&a9g8sJF\nEX1jO!__)~{~;Aض?՟Ow"m}7  'RQ^ Wpg7bf̮^Qw㪧c`.1|"B ? z>gK~,[Hu~]JO(+LavMsc^[Cbzޕ%0Nܴv>R]?q:0B)@̥WV}Iݭ!3)ºTe,ԚEpH$yUO٥8W|\͟$(.t$k3f8%"! !L30TW㸺S11^Lgc$b^1s: 2bL)~NIW|G!3ӒAhJTژPRQ/h vl=lzWd+sS35W!) 'RyN49AYѝ'&as+ w 1IMEN y,*M[8 ^sxIVr[8RCr"kE&e;lɵ#ʽ?'3)WG#@ϕ|*_̟Ưy>vo'oϽ;whKTß3Sbl> QpziMl`ɂ\<Zk2p%)F[ؼAWB־h| EU̓iDM%`N$^WvwDGK%p[%?-]HA wZits$JARoz\xr6?(]ӎzlhگ_6u"^:Z$rvBl1AY[[ye-VOvvcʼ[,1rjB) T8qTH@`?c ":$'VV|wjFBb ؅\|μ^ӣru]Q"GjW'(&udcWEZ|O2**shDmת!GyffeWVһ-hkV8/pxH-<'չ?q?C-1SI\9Ι4_iҭz\!ն99F6Pg] AcRP*cFVc*e(Pmh紣dǵ2!i Xa^^ϷZ}}o}_K?PV{,U[,z۞ U@ bGדL{yrOzD&1zQϛCzJV%4ƽvӗ^.R Q0zh\$/mC1:&I_@WW-քx|ƀ,)`҉^~Vf8ngljN+:!bfwX F2r)(i (ټk"AR$0bh쒰BJj1d ld"u< +c(hc ZJa.ւօ1Ab!wLA7$h=mTζ;jܭ]ܴgcDI]MV4rn1ֵ.̅pd +709$«]W48CiP,gGf+*d|]\zǤ4r)iγka C"'ldT)4lu]+iD$$-*--u[nVZ< 'SZ8WQCdZJRS|($]3\-u9gזm\?T JL}+ukФ[=U۱U :۹v-V37z-+1kCvG]ȚOKno N}+ZqK܁EMpXpFhؚj[;*Q+e'Li;#ħ'@HXq#zz"UA ^=hbS}aw m1?FU)!?V*}lHUu4z՘»kq\! 0/ $R+Z-XRdRr|/vDKʙ1w )~ I70ioȻ3a l+T SB/w%)MUqӲV"Y%%dYԋ5IF݈ W g+\@:%v"T~-Ӓ9 9PMLSb{D$8IĒ}bhJ|8dzjEXt>} c 7#Z"SȫY,M0^.:2V&ڳu&̼&K_\XVY3):9v\s-/~;c |g .vb`O .f|xso l1\xՋ{.=r&9o;s b;qnMC63~#- ]hUn\#\%UG"T4.k#\FzM?^h*-1QCԆ^6"W}Q!6;&RX/a$i$lKƞ"'k_5u=lxuT t{K\V?dB R Nv\͘Rb<ŜEDedSaa DSZb0M)*]$B"!db֙煻9#!|DTƇ!V[ʹ#.DX'J ]ZR<%MЯbinY{Tuo.߭!lfOpH% +"sy-,ˊnBG'baQcvC"e%Jb7%}6Üy8ş DžpU-q;V;!S45BHØyPGB("* #tIWЈMޠZFٔYJ!8MҦ?≸";OuRc |_~O|V>g~L/ :cs!=M&Os!kBUmAګNtT-yK l< xq\X[[fe;% 미yrv@FǪ(Jm Ѓ]df[6AZsa@=GJvJn4JūJoQdWK˧}On(u4Ъ.ĺUu$H`AY +u*Q^?-a@rZWB/gdCre)3Q;=Q=awqIOWMpX*;\kC3%Zrv f[TknJUZ΋Y +%(G9O}"e6Q mfxCXaaq圫kU⭭n#&001(BcR2J$_a `0$&bTG7*dU 7/F ӃR^N\&zTgx,q^<өbc>*FjI!`(`̚͞6ʚyf.A7w9\`@&.quT۪ T*?U\ĴJK{b͐-9jnT@IR`7Q3?q';FE_w_gcɕX'YA)B-Uލ] V7gO,)8n aPqac*xV艚ؿ_K2 iȅL-,\1+ծm\Εa1wRJcc@me^!zE!7šJ8@qAR&opdg<'VTQIc$VZfH b5n=yacFdF%Y%gok(nլD\ ZjƄZ+ױ>5xЬ%;7FH ꤹR+k),BvsbI>M?i1Hc.kԽ:VAL OLD)SF1弬!j >AEO[LF1iDES \"|@O<;L|՗Ӓx $#c")BTVTQr1s^K\z:ŀYA(pNG! ɕ ׵0?c)C#6Ȱ Ze0 C;8]sa]'3Q裤4F!TZ$cM``y4ļd^ u8,.%ͺ8B. V0mj3yfYɑ;*SFi P%kt^?mt5xP_ۯg]ɓϻ e6ރ[T2TMvXVe͞6 _s'~; wo\~y t'6`29v "^K:CzJYnmACZAR}|}vW,lfS 醬y\noƽu˹>vubU;0N !a̚3ZZSWL7 f]͑Mg"pt1:sRGGbbqj ^p]jHmK6L+k&;=j>S۱GO.\"=K67-Pן^| /vHbFlyaxUESB`{`TA-3+ZQ.d)2N+]̥ ܌ʠI2CH~oҹ má1f 12 8\\RMRWGnԼRseYw_һG^a)$6OŪZ*,B+ړ^ŕXu{OT}m?;o~=TvUj;4|ClcF}T%n$ƶZkf dn +a6;.ɀPWV=j=\1J'͵ .-WldĽ )mcYAW5-B|r: Ks?V}JOb(F UNn'kL6r3Nkü]x6XlˣWcڪ֖t9.TLstS|OWOƀP\uR(`rs f,scqceHClx Kt*;0)Z8T8(ce&{R禚.[#܎Rs&k5.,21>E$P?dfkU R dgdY(# 7Sf M`HH TmdQ&rH];.,.s)4{qſFbKChШ))@1l힡]jC(^7Z g8J\+Hq0+1P`su*YȟLԙQ4DWT5Y>G>A~7arſfpԮ`mkMdC4"1{x3W_W sŵG}k[ЭgV/uoO~W7nLU.A"suKx5pI{~_SRS5Mq )Mݏ< AД HJu洺Bn(0>'dd8 riF5hmh-kU 2R`nF 3f jܝVV3r5G6,L+F#QIDƘ!Rd"C2&%L oeXR@HJ-^q療i h]\Цb#զQ'V| MP (U%gN-TX`?TXri3+nFR󄐆W?Ƃ}~o7'zڦ/]q?7B: $h)7Vc͙aPvCre1םlsBU=1fnUOl:+'zw" S5Hk^'?0D'e4m X !9t\X,V8L 9S1&19T^pS5gXĤ)y5X sj3Z|Ĺ|횛 dcgX sv#}W1()S`#j18,VUfQN%j೯-yiVʓ9eeԕf1JMhߓ7Bpms@`jhhZXRqK<AR'5SJLbJ"҆ nὖt'ͱRWGjV*ƨSƙS,nWָ@!y"Z{ΕMRDl(Ba0#mSfjBHd´؏֦qLKȶ%l+eWXڴ@QV.Bs2udgƳ "Dq7Ϸn"R 'bޟnjVZ6#4yhϤ廿|?[|H'_]O%pޯy|ү6o[? }O > 1TwBN)Vz(Ú±44žə5''rBZO_ K;NÖ)Jgϕ׏K+>gNk厌Awi 2#AhwKWi]")p_K݈_*^5fH+F=mx"qWr)c> (JF~lcv S1 H$d+z\!$20$aEA4Ct⽙ c2R5ʹ2&˾?rM{8?&0«aOeqE8!Љ9^Ų+W rd(Ef,jFC%8*A Urf"l\*MXxX3wsہS.pA+ R~u&hXzO R `PEЀa;^5C{ST,k~#\3+p EqKU'NM!p9+%+9R ̡9xSKW 8X0;QG]2DȤ)@^aϜgo%X@`O&Sf/_O'}WWG6A#o~ߏ}Aۇʥ? t@!Sȍ6nӻ ȅuZF& ZSKµʃ[o~lF?g>*yT}:~0eE1 胊Zbql-$k W)Lp}2䐨Ž^QgTgkiLQ12d̋}"7B!DgϏ$rn4'/TOP̺ܞ`v}V_J83}q]Z+ Z%r*7I ^i " aPl ! x~Z*!AK߹w|K^AfF\Pt>n{ʼ#a )L̐]ʹ1q_8J^a֮'(L;'F&%6]9hJ N֚7g_vTq$mONݐKi}iOIڸ?Ag`JA/eEL=WvL|NY Ld~YzYYj%75Bme5y7PZ;&t#(J]F EμzȘ4n<%"-Qwd$2$0*u J5b2 OڅFOf i$ӼdlmXfsEZ4q =ܐt 6WQk:lC!kDK&՞O=qs77uVT^=μ\8Sy\V'mIa4Ӕ0]d7(8b! S?gXSj(NPDy-hRYP,Vof83/oƁIav&#ŵHJӃޜs$#X7kGlH&~W!ɛl/ZnTdpJ=ήC;ćac= v[gqtHMҍmǞN0agX\v`#6*XU-A|\%_mn?1a~P,E Fəh³a$DI9\X Ǭk s%E2Lj硊> As낁94*֤1.֥%(~x<Da^]81^2{$*{孷!\+1M !*:^?VdH~G~Dׂ{3㻯yv;ߏPReiv"PVa)r;Nw s!H N=TBJ/z ܱqJBp*@:z%ffdvaLO˲0?>`V u;vĂ2&PxkM%j5!"TUV뀬U}ZD;{ qvRJX@p3FTЌP?*RxgL$JtnC+&a0N)@L,>1DvLyO DaLl=μz\83e%GN-.]skDnqclKTGXrKT] +ޕ~;A?3?p[ME/yo7ы ILQ“?\tՌ+C3zKr Tj\IރmE?NjSzqo= .wǼ qQugo1%5!qrW9+拀1 ~T+{ps!8 2&gea9T(0caQWf՚C^p1 m1=vFz_W-+`PaJJ#Jep3ze^ ɄYRċI|<2H)WDžAOY~Bb18W#++ƀI ل$ ʌHpz=~XU-F@ )CΔQ3v*pR|YZ*qJ MnbT:Vj#92-х"l*H.2n۷7-U,m$-}zI!Fu!!Z\4(|(!P' bܪl,rd>R 7?C_j]'[2#nAܥsUxKx37l_уrVbYZ!4AAd `jfnzlG  --aAqnZ^OtUjCxBo&lo~Lc5H0rq;`zUҟ*>kx8-dj1 f$c$Xp. 002*u݃;W;NYm8m߱Jͳ!L/Jϲ ? Q2 0 Da7^*vg#_ȳD2KACAva&(!B.5̹v9f rM6r7ws%QYg;\_j D[CJU|"aT|yY5#*hgv/<REY,5#RO̬\U͸h{< wTa#3)^< iΓ¤hFM.(Y5(G$hXfKvK),kqИPVRkU TyeY2%g'F! 8qH ϬVV&7.yz..V ²PM\?V&Xk5ac\/|se]AJʋۄúr:saBTsTC .5hCA[Ad1$}<"KG]??C_Xx@LM#_co.yl^Uo],O= ؓsznɁr!(Vw8rIT܂V&oJ bZru+dΔt ؛=q9NN<%x:bP1kv<]_R/簴6Ň|/WsPXŠ8 ,#mΏ¸Kg8$#&ܛ!\+&Nkm A )dXfH撏TWcmYu dc9fLD8bEjck+3Rj)0)FSkdu\'Z\8ED1X afB6k^̑&?R_G l7f܃“rEk uu׋_]O&c~uʟ#}dOϾM1|>yRU* 5 q=^k2Bc!*S yw : c'/tY j ~- %vDՈJgͰm"VWKO]joqy>Z6 NkV1r4eʨv-Iݐ6}hL:Deq&c8$J(LS" @\`r?oPxbĄ"ՔLb`w3l-X-0-p%STY88:%ǵ<1E U]TIZ.TQ8aDN ϕ+,eeG^F }]t5R]\HK%#qc"i,WCB_t IDATHWPBYڸ,҂4=k1UBR fy!F0&vQ SaS kxՅ(|Zxu,D9LJB4(2i(SDVJ)@#7_~u$ȪGU+}TTOAHsB[v)J[/vj4\MZ81uخY*S?{&rZe)r dꆵ+ۛKo$ 591g[y+Wfr*Ihu롴Dai\bluX\`^ M`*aKqA| ͇A=E2k48m [c'N{;*sNp`5(MPd64g>O %X~Cd\2cR4&SP1Q{esWWǕBryc!0&PB-jVw0WϙifiǀUa5l>:7BlGx{MT$Fv!l-&j,dKYr6=ӊF#%!@s9kK C|@րD J^VH]P$W!z]{(uf^͐EqVkjWvܽՅdJiD4?}$Ц@*4S4`%folXQK&ׂX%yA'e-^)iK&g]Wۛ]bߌZIQ}:71R1q^(0b] WR-+gcJMDw*<.wy٣E{'5}EݹLW^-B|ojmoV'[hfW$5:$r'7O}7 |O?Og~k8*muH:Q #']`׉bx"Iz`!pU\yͬ(ڃIh;o;,">}7rILZ<\AR/gaDB m md8i -h{q:L:djhEFdD6=y1 +HXX X&X%3dn+{(TUP'PS{X۶-zc9{s%-#!1B`D*,*1.!SA( c) "T/h2ZTQ_Zk1zo͇zcoAs9k5n0/gVGJʞxu7T'rz٘Vtu sy-)[Rrum>"|m+tLQh&<uҀ/uŲ2'I4gh=5>mi2󤜗@9{f-%+o+ʺz-}JE90!1֘UH=TSV^QYr0g 8WHd!\s䏧;g&栭9G;$x*cO=9H@YRD^:B}EVb\[eݜXРVi9!Th;*HH26NCyNp-+)e*W!݄qn4JgI4/|K*,da)\z ,'ZKlmi;XdF>ޗ؀Gi{ d1_zSzF$\m\ЛkïK) tv?)eD|H э豄Aj>Ljh6IpjekJϼB94yÖ) w?uo1OglFgNS'V_uUռ|.iB%3?4dkRֻ=$Q\gK}E==_)´,'XS1+/!iE ?y9'V&(Օ,(I(s"ZfNJ' fLت 9Gf1[pCa/~9Re\!i-lq=c6 g8d 6vHLhͳVn%KrktdCsL b+Jf$1?)3E&꼱贠yv9AIh8M IJ$JJOT&iܥS'^G a ͘XJdYbFfP~O$ħW=cdCh,p[_~booߒ}/5@~zܒs BFFD;4PH{ Y=. г{ڼw5h?WD )#I0tQUJooY rVdhRqR2rB3êwz1=4.sn=cf0]ߡ 1Y0+<]*uT[ yJa*Tõ)$Li)׽HK4cJpV7>r*|8xp|xnޖJz8?,ZB%yNElC9O"9s^<)+[N3b¶>ɛJe&{N=S.o[Rk5 s!gu2<řBZ wÁK7 Pttikq:$HgwDǷ流B2vZs5 eQRA0M$lV\3?cnSNC%`mָhiT)Y͸lxP6E w490e\ܩR%YZF.rƋ,ܗ̔2Y|db,@/f^f2XUIE,`z⮺aG39#h/۾]I|9/-@?W{J`F_̷;dĚ@+^>oa08-7td?҃{l܇XwǕ У;NjqBG 3%Kpm.^1hJQ^^M㚏YV>YF{8˙yYKƵ*wWtAڪAZv8UT-F5j%yL }~qrq@ۥxY*/ Xkř^=DꙏHk/%"HRQM]᣻\y|xN/3@k8D}bmNww ^w5{vJƩ$jkη0<8ߝx%ʵ%ߋįBu}x4aVGÊ3BarB^[ {>ePl#H;q Ahe9., F أMفe+{O%u([;@8(3]QCv1.WdBIˡآYָ^q:X$a*\eml8g(7+M(ybZy(R2˒T(E˙o9SR|7^j{v3k⩚B_s8r]OVWrڋB"0ڳ ?׿~| /%@_G~wUt?0u7# #R)Ğe)6x,ƨ7LDR`egBw@ȉ}ٷ;1ɇo#oC:w/p=Ԧc1DՆ)L'&`kEYq݅Ȟt݉!YӵeT/jugO)[N)s, MZ-A6O0/yUDSPd='CXP]Hb>&yN'ٟn/^L'.kr]lSq3 M ֲ˔[8oXNɹ$󒘲 Dɛn|dC6Ƶ5.RY$sFT)@{5Vve m+KbN< U|U*N  ^Xg5dTZ3cd%V ꣝UTyD[v JGAG~ݿKv|)?7~ߓyL19FhtDp6)?11Leg9E?vd,VG3 #"`S|krIO.gҘ 죛C;ɾ\>3;e7X>z 2(%LӖi%ep$썦T:$8yIKNVKm\.uCA[o'/ChUzI|d*<]wXe9CEARjָԊr:M%A:KR+C]+|ؚs!8/[Of)FD #,xN1K  4?ħ__7>%o@bp(*dz, z#b c#XQ7 *G6#u%=K!0x,wϾ}W{dG2"T]gQP*wp_+?Uh-؉anSB}Y p;P޷&<^6^û3ͥ]+Sb4'[..Z`ݪ8HJmu5i %SS,[F+$d\SS4'L)FO XUYňɻ0%%ol- \胜fy}e-)a59Z@[WL`9nbj1>N8J$:d8Q،-g {rn05.>xnr*$FI?'%9r\ꘗș)'ZxQS\wʦ^hjl2eoT1뺱5UziM 'GZYEӚ95CWFVxHĔ&4dTVu~ϵzpxXW$ eS9-3I2eAʩ/pR҂W&ޅ5Hpp9!7Aex>~> Ǘ |~~_jB =n7Eʎհc+ނ(HѪoCs͜y*)S. lGOO%1K1I+!vjы;H8"n}s,n@CC4{㞺w ¨AKRdQpı"t0홈gj]zБi5c5ei(tir~:GZ ~ X/t.l2c"e9gaN0 mm\"+ŇI_=Kڨ8-_.+,0BMi__([ ]|*I[';XHcKX 7o }?ݿ%9T~z=rltFG)R4N?tvԅ:=bϻ #+B!a9!EJ{+]}#}9^A7P9Ry%>SzwDD}c5zm718XC@nLwϫ=D"܈- K ,Ƶs@<>/j֕dLՇH"$|Y#O٣>lI`f6OʋZ03 &h1[x[2g圹eXCGd5rU,ܝ3,Ox\7§̏ WՅfc]7$Zp.Xz;snxu$$i`/.sAbu YY+)dwXI7ptc?qP 44Q:OFޑLu^S'[5c͎@SvP|hlZc;KUºV>磈2e1PjBx) @rc "-9:gS.Q)ڸ^6lk%E|xyy_ěF`uxVheuqѸsZ"ڳ rdvt䙆@u2'~o~>W@pc%"DUFc{w>9IA fVPlV[u#{BٍuۆZkN&ԯ2%M9e&3 w *צ}ڐ{_0esj'>+h]yj,Pc".Ve*H15mbZN3yZ^G3|txyW8&9=9wgTXspH7:IQ-᜴b633{~/1=b| ={kV(d8NOd;n)iBZgܚE[1_?PUzR2&\nE(3DlDDiv8+rRrV,JNkP(q*ۦ.jN,'΋rZ+<\YeܡxxlFE;6?HoFcz7|cOϳ%wâZuCDƩ9{mvfT4ƪïMT}/8Q #U4'Jrg;<r?!i\ڔ5w U]*c IDAT1r5f>ՉU>VZ\׆O 0`MN\vau!IM (mk 7{xuwՇ'NiʤIEL%&u֭Uoy[$S[2Qǭ6;9p': 6u&`&%GtSa_,x$w@KQnYk2` @5E]|KAu3h" Fݳud\j{*,K<'5ck֚O>-^*jݪ T/6yagRR"((\+RS #*oV.q5%gO>ιn>͕7u %UhAyh kZÕ7'l1XGKyQgzBY\~w|/@>? zzphŃF-Fz2k?xfƋzjD{ ɡ{snxz "r`}stׯ7;0wIcɒ+$;Z^O ď> _u_0":( hsEv6$Ӝ)SF*}\+lAA&*@+&hz]wʤ9JokMtR;VLdNTEH !@Jn1S)B6e4iw݉%\d879$ue]ݒI쳷rd3KF{]٘y0-OL}vDǁK)lG%V;;Ky"_7ĵr;Zݜ܋ottd\sWz+䡭ˍw[%ދG r AʾE bF6f imgQ-T|Le]9Q ߡ-+RfWK,ٳ8Mjd}NVi[h;fa3%xbTp(Ok݉K IB5Q1[QRⴸBIXҪhj\ו4;94!Hc+sf.Qj岮HÒ=;m>߮\^7Χ̔)l֚Q>{nYKPn<\7ujFlj-?xءg8>b4 07uC2^kYzm_w~z@ E7 =UHc__Y?vAHyA#<:v@qۣǞ !;>P},Xc8Cz0g7v8؈Τ GF;F&Yo<Xwr=ch}sRn\t(R0ԭa҆oC 1*UQBRE<\Gl*aָSZ3󒹟 eB¨\|X eR5c3|nf|\_5ӻ+q|ۇA. O7s Jq n<#;,pqtޑDʔaB۳,Q?f|EtQN:F(_a0Kn?'8 qMp=#q]{Nѣnl7}srG[Ky򳼬W'YW?pdQO kML0StFfS|4p\-fͨ]OLI ,t1[&Sf)y16,7Rvq00rX||GZtr璅VV5J̸?MQN3YvB}o# l$-RrNu$]pi,;h c'Խ1\b w ?AKL QAK\ yT쟞zhc)i,ceuSv8O2"@2eEV|H縌RboJ3%Z1rXg4 ]2k:^zMS峇kS\L)V',jlp2.APNu &Px+`65ٿo 2xPփŰKA _on~i/,@~K[ܒDXpd3g`uEӟ#=x4 &:㦁=bpv9ս* 4vp#Sm=.z`\+n@zC?&鈴$ xٻ a6C1CH1zzG챇3.[r"id6EuSa v ]j%'^, svC,0ȧa!b4%.ƺ*o7Zje J)g'Kn D\wa37㳇ǟ].+!٧.YHnwWirrbcBoJԃ!Y 3-{G7Y%5T94Ïzfqjl/{vШܾG ى=;90+&ϊF5c]ӕd9ZOT\myVАVK@%WNϔ-x]\0(eEǜiT\F)PJ!'hH0/ƽX+vhj|կT2[m\7 W-O .5FBG ?_]\xꬤ{hO+(JFNk>_O|/,@x 1tk&#a }2B?!{C+ཹ~[ND]9ߔPu1X!ΫnD>GGa쬞 u?Gan;(8|x/c^gMF&7U/B=S 1q a^A!W(Uis.78'iS.Jk`FɝFE6^hVU֭zwD*fuXED\s|fG}[3>Sm=j6n} /R9=ZѲܣo1,10XgcE~ n7sv0 #쓄Xw &Q㘃+ kD Zj,YDDKfɚj°h(KFgࡰ NNE/2Ŝ3ei8Ғ`E!t<RHٵplx"qi .ZV"21b;Zk."nz {--փT(lҡ}{,A?7?Ogwg:J߶J<}0;Þ=מ|\3{=2'() ϊ4񝥛_xFy{VyvB*%^ 8 Iɴ w''Y@[sv*4mu=jdaY&4%6ǭqrU9eM+%IxS"{1 SpL$J5^.+nxolVv:S *I9t.1n^8;R\)un~e&WV@0 1z2r.L2yȑo;͒;0ɡ;(}X$FLf>B oc )9Dr5EFmiCdQ";[q@)2Jq) !=`ځ=%EYuG}[_8[/~)ovG ПCy;NTmf>\`U3wA'Ljy#^$΃y5vN;"~G%w~INjn΋yU2gkB#cHfhl<ڔ5k31L3xsi<^(p:8<%Hܐ{!gPOZv׏M|EډmpZ2YXfwSb>-;!JΜlǫ,Rd|dW{p"ڸ+©dʒ:zk%*(s=Ґ5LKhz821Z%9 m:@OJ\D"@h61a ye:dRL%F߇-ʄ >RxLjD~,8ܨt6Vu(L j3Jk!ɾ5ޒ_9{?WX¹*I'D(OKYՉDVqP5j6_ԓh ?r<:sp_o+9_ >:6*'Ak%7h#[}=kpxwZ:yr8g !{k|ud_Dٓ-jef5a?2',Ʒ]0OnrHl7Sw\π-ع}h T^pm!D3 ",%qP:ɫ@J5*iHSmnZٯ3}p^ϯ^j+~rm2iΧB.5I+N${d zf =Ie9կN|6.4MiLں : ()y<$@%Ӕxq}6CJ,vAF.-h:˔Bcg_;hSׇa#w .{4!Ĵw8.3T-H#3p@gqboMq6\" blg:ػqb 4m!㠷Ke Z2yQku{ٙj>jœ]En),.^tr 9,%no)aIٲFzU="\K[os!΅H9𧝏~rv[\rk=nGҮ%~mƺ.-R;kGI9{ɤۊ!PܟC=bSW\1R116ԵYŕz9̅xYtr I'3dVi򒹻X.)fH/ڡwl -a; Yg:v(Q oTo?o_(7ۥyao=G($7>ȥ^C~? iǰw#w^~׹Dv=Gȿo;#o߼ǻy1o|@@-\a ]~?2jaHNB2c=a P~WJL&' 5Sp7OV!y͸ĉ`-d(T0UxXAxX+ Z! mϕOn<\6nyq.jᲮA.F* iZ0>fZ58$%JVE{)ʒy)>b8u^CYIJ~(%^Q@;Hy\k6%gRE'cwk&]AڛʵPkupX2z?!is.).3n:O"kpmʵz~ Zg 99]IZ D,FkN10)UswJ̧ө ej: Wxxb*2yrII):uX>}tءAjvG;1al7bu^7~__ُOQߖHnQw#N;"swܦ;'#HQ/bkSv<>n֑Jl5PHEux|v\|;^?T3"bw:u W)|+=+@25%enFb[5 & M;0|.I{4+ͣTe*2fA6WJL§o+Y۸V%R0@+o*"JǬP9@FL&`J"w0'hRzum>( :)u۠5RKܕ%2d6eh~kU.ʬyd 0g(c} 6o qyتחy7ɔ T{D~!j]152z@ye/ 9!M:FmkcCɂܻ6P O,LUyܚfnswڈ!AjrV};(=4hF (qnۦd<%L>]kmܓДxqڸgA9GHNJ!rO8p=ڭXw*-+۴1tlUOg5;oKp|!o޿m??f%k<3 qv:߈_NB{tuCY,1PW΋Xv\\-fQ)uւ,2Նs@26F IDAT Oa*]V3v5[ٟstwxf /)vdle;~S'8 1#? >`FmBmG%BdH1cImK䳇 kR [NL{r_urk12͜$wҫ;R\RLm>y^B'Ҕr҄ە_rWLy'Z:1-/f/U#ܩX7x2F4%J kPMfgSr3JLƦl-&t3-,S[NW>c|E7&+\VӇ jZ$\}nho'ZP1O4PlN0^FI! 'PLnnMm9;od#bHnǷwocW( <}Zgt۝^'rd8~{K{9"~ sB;rwpg d0w>FƆG//#y)2B%ߧۈ5y~.CX xy7Ȯk=?vK^Q&g'׍WSfoN6O[sCWVTs6Jo|XhV<`ШkòG2er8U^+ ”&r*) ȥ$`Pr£Jm$\uS$f,×"lmc<ڰzJv֌Ι$(SwYwY{ swKC4ipH{XC[dILn' I!3X]1ڸ\3ULon5 w偮ڪGnFVqpz`%i1iq0ۍƅOẻ$5I>PR n>yJ%1Mq7'l8+3[ YeJ, 9M< j*B݄Z̛ˊDÅ>竲YbU'f3=vwm~S! w5u OrDw[8ݖtKO'b])9γ1e 3v}{~g9x 9Qꋣ/K8ׂfmcd$tO/Ԃnrpg`{= R}Z8!O位"2ޝXжg=0;dP;Bw8=Wc (Jdhx~t1RzO;;l`,'TV3RD9jl5: ǵv+HV~kbə"P5d2S*&X1ܼ esTjĨGwM)oKm<^7V[cݜy~ia/"G|G8ϐhVdWIz%Ib)A5% )#5o4"0nx$#9@|Α=dG L81Y;0k3l[JnA՜HKLJ`»5H)rb]54wYhTX܌˪lHsum\u~]_6ue3߲p}sO u?Pz':Am6l}o9|gK"h@J#E,""%)1ȑAr#"t@2B"FРO'Ċ%6~~{>^kU9Ǡ1Ƭ9FII׮Ufc.woo|Ѝ.4$>;NIeU=`g3)$Q(93e\P*o.|7q xun&J5XyS9ĨO#;cG%o2ΓWfxKdD?cVN{пLF dh2S?OII(z`rER0K70Ot5Sɉ{{+un-2k7޾]gg,1)|6w&SKg,Ɣ2wɹ yLxʑ 5z $o!,)X,8x 6ћ[ğHMO *UEDc{=o\XsNKU.7d " [LL)8geYWE7jPy'0z7Bmiꑆ7^+oVk,+Üa/ _{Wc2^* O3[p +ɽw S%CvO#SC;}_j/Cu5Oe1&xuWx1,zHtY8 M䧁r¿'O%K0^6;5_ !tv5 0bwcA<=BHϸ!DBja&?g}Z!6BBnη,m_߁F Aܾ裾_=8왒GZ>6?tF8lߘ'Muj\޸/?o鎻?fLDJIx*)s>Ϥ)gv4gtbw BGެN&`uڦ}"/dI.ǾOp7O!Ҵ1\A .f"lN)=!?{ xyWW.eVD)0͉~mW?8yyl#O*p> 4w|t`(=?p}-s|v#\F}g윔/aCb8V]oGo|x6ŞȘ G@[x:V"!!ʇ;(@{FCꤒД]) X%R]%R{eiZM^ܟ9;[ʛ+Z;/ Sud /{r\xN m:-H),Kr{Zn.g4q>ɖ.16b/Q+5R*W5DJQ]ñ55=WH2wced!)BMUC` ! %W{9g;6WiI'|X7|"icB^~ r|r$=ړ8HD?\Z4 Ռ 1 HRLdʩ, e1 V()1e/kL/39M^v]> Do7_]i+L\*Sj|:K|>J ~.ݜ0Ygֺ1OEmm<6)(Ps`>)]ҝ]2TO$5A #S)GrJ!k='kJIyʔ:ЊF/4.)hѭTe т&I ZZb ] jm0fʃ\RRxknKvM% g߹yXDxuئiv6ﶸXgUznUYkaU]RrZ$n@h2BJ«ItQʛWemKO[8]0,urR5Vs:焚F:2u)" 6c9UݢZ@.œ}??0ኩITS»ʻf,?.t |EdT1H!}|@S'6 dB^yjx[4bC0GAy ,>>yr oWF^ҽ;Π 8HG}qLG3Gx.pOux5nNHKuHuVJAə;+x|[HLTx^ bJ\x}b2@LSy7dJѴN^Ͻ6z: w'!OTf'=6/5_h<ķ^s]VG;RJUaxi.<>Oz87w>A¼ Ni9B~m~ c*M >"ޚK1'FPGAJH3P22I$oz:U][WKD@O%J}In404nӌL'qYYuf1N9q>%2/_y\yn\UWEjg}X DB%/?nõ~qn~{U([D2DbYo`Ӑ;{#?e^Ns ega*$cKN;,M5~' __z&HN1Yr|8-Fmu LeǬ4@FRtFՀ`q&G¨ q'IY\=T7s AtO%SE!nu^c\f3'8b֢.4E%63C{lbr A Ya{tYLeE }'yV3*W-[Qv5uJf>4at3v w?|Kp:O0%VuS4yLJ ;L4W¤g/ /NL{8Xv$TD;EpĜ ڐh,)a9kxM蜡%l WddTĪ*@guzzXUhG\GĆg* )Nh(4 l*)%R0LiNSxix =NN\d[+uOK$&_Q3K$FOH1nEW=^:&ZxwYX̜}l3VK_-gNr_:&rT DT3o=.J"M4yʵ9h\.XH/Oʵh!nPɫ^ti䤔bԵsyTnEm3Lʎ4̎-"-d,ƔSĔ*γibd "E; )FJm猒T]ޅBxuћ ]JiO~??7/ E+  zay߶clyoC—oF`<[C^<yi#6`FuG[:$ɨzm GWe-c#F:6e#;zIݫ{޶vGAASe1hc$@S8/[ak܎)i>#AJ>Ӕs;1{|ve ]ս*woT&2eΔ)qN.Ikn4%/.@FB[a5r*3iͻ& ){?&*'ƪ'g+DL)iLkemM1 I!D^!*]0Dîx17Ƚ(bl̔R[i(W&xC(ŻÏ\DeL%pKmxM^Sΐw 7DUyyd[ph8$,~Ta|dk5om~\Z:?zlU)9bꌎYy<60eҟijMt8#/=w?9 "[,&h7qc=9y-tA"r&!)Y6< gRm,B:r:p}"9/&j=}3=k7x#0BMH(Bys%zMbY.ݓmMF3&s-nb4s&*6Z<,$- v-krZjJv'YB-qʅV_YX_42z+MԪⷄrUr^SZmnu5]ˢVMnowr5^f܅B@ sun}//GmZK,=qknB.#FZ, bqS;)Q2\jGD"1/Gb"O[̏4g뒜.Ɓ\SjߚtxZ]mM~_GfIM߾qŷA`g{*WdyzB m8 ؿm# ń4=?M'959M߾u6{T˽dS㹹C/ճ)S* t4~Tɗ9CQ5}e1H{`cTJx9gh*U(pwrmV+U;_x$KrUn+̳pwvO* 2tQo:6f[qK])%1MKWzSxW9ωק Qe T23z9k΅i)\b”[SuqCɲqLsvj oLu;A29ixG[u]% 6)JsR&sM<RUX攘]Su)q7 yq_ݼ$@)P.: kh۪^1,)Hxo|btɠջUjաOHoίqdVP-CwqھQ_[8~})xvGborE6lyx~,G:l?@챥d*w(1;VOcۮEi&{D~ltF Σ!c 9@ǞK}Bym܁\)rNs: 0uCdуgP-q:֬C=go[h.Nxc{s5Qq/x9L3UycWӫɓG ZY{+^bb鼾=]@m:r>'y{3{LEѣ#\ N&֚dSf3j)E /3VhvW(}_8ww%sZի=nhY%#*ca+|8A 6"`Jk}XX,q'&opW/~;eg~:?m돾^ k5{xk'O2|`c!&䩌}L$n@; CyN2{`/!$ x>G F(# !QEk̞yy0?1P-2G2vyd,2r]$zq '\yJ|%ɆĬ6"TkƗ+jp:͔II9N3ω^yy.Ά/Ł${:G1U䆭 h8Bf$6hn`T\[Q⑥>(T݈|j.zR59P3diJIٽOSG$3ɾ`=%I&=rƄ naI fݢ6 ZL.Kc乺**"svҰY9wR3YyU&_}6.tͼyhȷ/y=!6it7l8difܺ8Q KK,*<.>q B%cMGA`j\KQ "{ŗ8 6d1GJḰ^u_OrQ1r̤$ECҽ|7mMZ<4&Z۰OCxIfFn_ژ@A'OwH~1&OƋ8<5)ƢlMWaK# v~A8C-cw9FzG*Q{хc#32o,BI${)5ZNJ#%ΧD)2xqjUdLi]idT5 |r4gȾp '/x'FʅDv=j1fLSqKoV,s6/({/a{3֨Ή.U8GU+8'j3ֵn Ͼh#x$+/Pt#[{D9Rj-sМyLi&"V&6jӭKSݥon+_[/>Pz[KTU涓jw͉KSՔ(KODN$SћyKG勷Bݹ &\kC{aJei N䰲 X8XPm߃ I&! a 咣>B5?@)BĒ"&-a8S,"?i}07~u;FIghGܷ^7%`'qU#'-GHoEdDrpʼnl+ȓGzVϻO_T9I8ͅkVLս/ޯiSM9NʢyJW w@'O/L"Lo| =ls=OYHCiUjc>^"d[i [xv{hV«^1kWF 0&49b~zss)֛)xdޜ_^sߛ4 /UXzF.yH87pU~ e_5.2z?j倭k tRN|ȯ34Sra"U ָJkScJsRq]b<,^Ft&Bja4-H#FO yB4Q͗;9!"ٓ3ƄGM*d5rе+}~s "4;INޠ<;)U,#;gF rg]J^RZRl7\[t+ 華 ɖWoqpYrbV5cĹ>&\o@_~ުO(Oc᷑7cDvĦg<mVOg鿶q C'1q_k8R'Cs" 1U h>> {?!8v 7!;v.1PtM,KOae˨z9";" )3T뾪oMb2 A=+%nDP$:cnؾ|w @ᶬOIS._7g9).pᯡ$&zCo%ɯ1̜)'JOx"hNTJPqc )$yd<]ZsmUZI$Hը /Epjs{x!k/5q*}sRMyu^΅R yG,<n G pa!e?x*=x8G<7'0鰧Hؽag"Ontϟ W;kK :/!G4 &d́Dt0FD+A:F+xǞ#F9ކH+K{07C:뺦ꍔV鞮 ҴCu32aȘ=ݰ:ErpjZK3".05MG:wP47*,ܟfB1pr:s&)'yߗW'hꣻ"f<%'"iĺ,Ƌp:snmUR)3t}9%HmԢf ` hmV'"z ;|7^'SpHůc3wYī{oި3RF怊8uYi8x+Vc0H{s63!Qi5#qLxg=bVǀ߾K/KJxK܌ߙS dNv}^ i&SeMחH 9 v]cd5~F!_^@i[y C'r\C &|C〛5md8scPk(ۈ4yAH/bVݺ9Xg.a֮8#e2sv0Nk%<9D]͉d ft-]yɁ,xxe {;DR=}73PL o.dČEڣC xs(令T ei\R,BrLiTT(SqDm7zwa{!*&SFJ U:#R.X眘O*EtdºmнDod'祜uvlc8j R(1ݤ;]XZ48_dwwwp}(l6ϗxs9w$cZ#v)ko30ؓvPuDjuC<2v֙LI}>H]!b$wU\̨$T!]7 W0sl+p rrkZ)ܪQUՕNݕբr!ًsNް!]Ȓ=TXjg||ʁ7"kEJ$x<^T+/&_3Zoܖja* ,ڣw 9g'݁UYo+K7ȉiFa)SN,:5IIr:J`{o8nk <zbi4I-&\h-9C]q0/ulDD yEJ+ ITEbG45ƲѣȮ-s>Oܟ3NV~sD4lT{Jh]Jmnߨ%onqRi-Hv̪l98)^-m-l1W wv?/o_r"~~0|/+#q2{Ir|c "|n)g:u>gy({v*[cq#$*_:c"+GmG"B> Ξ4RJH\0i77DF@a-Bbvr[:VKt}\>-'Id|J<.JFꞳ?9R#%͸.n#(4#:s$+wH<  ,拯 oūbJ8)iAT4u&<{DH.h1dQ HRH!Bﮍ77Wú+>s.up'ʲ.p+gtunckp,σdWOc?GEs/dCU IDAT'Vu}$'&)boԼŷ8:nĈ, O'~ZCZxᇈ5ݣ}FU[0Ȃ#gFxo>PQ{tFv|<O ."}qyr|C1~2L%PpSa46PAqhuc&fm,81hQ|r}*T, 9P82H69hSBxl BՌUB' COZ R;q85 j0OEuEvQ9E(Kmu,*3ysi|0nÛpy߻JBED&o'%y..WׇHBk [Wvrw.,Tywr]VyJh]xPεuZ Nj o+כ%lsڼdSTB2}K6ifC!Cl0t 5և^HF ^,'>GͿ~s5!adJ j#<[X81h1m`'*(C&ܗsoa 6x rަ23C.6W\kE>?i9!ys㗆0.هu<Í?g8nv6Gd׶Z1<|X}=3^qn iDo"h,4?UO9 al+!"zʷ #v=:V_W+))::uAM9MiJ$z,j^NS¤лp5_W0(93kK[ m ]5Jd|̵ RK$)h[7e.<IwN:6kmZ]qܝ&^oϝXk&)q֓ݣy%'%>˼<01r<,o޸dz!A*WꥫݰK ZYWeY\`] āuDLCiīÓ6/4UɎjHj])$RPի]h: n mB~`-A((Y_ P“wW4Yq+lC"Ղm,Xs’a17χZ@_$F8 tqTSˊ=Ur9V$4o\vÃMo-2w+>,oŚ@U[yH9@ٚ[srWJt;W6)Lͳo$c3k&jSGUDgLAMX4!ü{k*kv4&8bRTT`AiKnx8Zrؾ13c䬉恱dģݼ5֨aFG,\wIC5kqWFr.K?r_[7q fE=hsW}¶WogOD$zR=]m&~ N`,1rQY-AEQwv T  {"_uQZ3ެ5)\*e{ hÂ׏Xj[&N lU=jM@+rm|mW_S2RxQqDH(2UQ6[AiTEI5' Z۫`"{ ជkݯ2^?$D.--1}Us#vx/X\I"`!DvadF X2aɦ*(ηa Ċb CZ┬QJx Oْ_Zg*؅U۽{m/o(EmaªOoV<}'6bX<|ݰ~0_ Yfz%≾qoL뗗G&z$lp|ߩ/ؗʴ+^z@?fM%}g@4.}xg[DL76VWP7N$n69TW}Y鷂#H7&'KfVB6!@AKFF[w hxDO8‹ʁ:aIC/$(U!ի#bj*b~Tl? Ј %#"TMU@9 ^9IFJZÚˊZk(Œ`cFZ7EJ'E%}qOuL0KR͊jfeqWK2%QE ˲ڳ5/MԆ\ PԨAuZdx+]^:8HF&UUЊP,zRq. ߇qHh %aA0iZ4٪Nr Ldx| )5@Ee]:::7brط/K_~DǏ m1P8 F)f%\!rF,xǫ&U9!%l.в *MaTQhАЪKv̉ $07cHŚ3NXrq4VDL&TE" խf9{"AXM jCbF1m1i XtAιOn) JF{HCUA^UQfIUf@ϗ*;^.jIxLe@{ûۜU\b7LHCqqzeQ{栥7d({W WXCȍ1%|2k#Fe<2VLU@#w3XVwsXwGM 򔀇 |7)"NPD̜Kkj FME"eq5W=ǚ3ެ  H8>VnZHOfkDE;a燮dI-'U3ya16Xqyf4g_EjLah3P¥(vDcCff.h(S2VS'\ْ^+ @ɞ(.U55j7/ xN= [!lK中44kBff>P|<~<JRKž7H3ZZ3f] `2M%{ka5phFS2{HKb qN ܈I6DľА!4X U)%xvXk",I Ҡ,X2x٫VdH UϴjP) `9*ٳ['ybmmmx\24Ymo eUh1h+1Ta'GFba@"F&o$䋿'_տ΋Ǐ /_vz+W1^ c PWCu Õ + 8@`1DJX:yǬ|^U\#qF-{8Vf@0]<;zIE?n1_p= z Rr uIYH vU f=%087LA{0l:6Q?Vqc^c밉)FDф3$8ʁ#8oXݷTȕ,"h9\^?NXO@wP`#9x*_?p.G$mW9Fnr4KdcdMjU`@(MpM2vI A(n4ya4"[l#؊Q KhNJ`Q@\:CHH6Dh*x) WBBվEZ;^ q?2Dlqʌ(.\>W5#wuȗ Y$b`e 3Nɘ*Ya .XӇ|vš; FKKpR# HB}6Ibk 8H"@v5h)Y>yC@:p9`o\GAȍ {("IT'#>!J#jlW\^HAOJgoUfuz4wG 6X7mo!#:K/N> j#oK{!n'yΧΥ{;4-Q ^ JVxڻ7q_ d=".5L}?] @zu=9bPw[֌W|Tͬ&hbVZf{Lb ք7'rXS-<ݼzϸ߫`+s%߀/ؼ[͂dI S\jK6;㷽bMNXex+!A)EFC1"@+Ba4D]?xry[OU;OXwʡ*ОNi"MZc!^=^=0N mGHБ9-+VKltڢN\PDzU;O;hal_ / DiM֣)Y9TM`8UpZ$RA"-kHi @[f ["FIJkr ^P*aIS%Fn(N',x|>PZB.@,3lrKBv(TE#? wo+3P v}#Ly?B{NaM\A@hF?a(^OEF*5ׇz|q`3H#MFoݾgbV7?w{a!;=.mC$oQHsUsba <积RHd!F.e3j}ϊ`n b:Hi šաCWBU:UX]&I~ O+b$\qykcٰNoՌ)Ĕ5_x2k݌ktPHΛ-r뼀 ;fcuEM h-q |7 <D!zM>:a/DWdkfkCjc>/d b-mMo7Y>yL(&,ڀ8qF ƦԵbMҀ . _>U} KN NثBt?:Px Ww+ϻ&؊bmh15q*x f:Ti(Pb2"OԬlN^:;%h1P5)egW1X߱T{2K1%zՇlhf@,(dENoʉ ؆ax o@XNSB^ޜjQ`>"Mj 9i`b{U/ nx_ۡ"~,4WiɡKnHJSUZ)JvN+Ck`a(tR, VHL^C|{k=pep.+?3Vo*[!`5hh,u+t=$ܑY+t&YX= /ta1Pp99]'Mߧ3tuAAowZ_J!w04]@O@L'lwaj/Aǘ]hp#3$E7߿0a$)Fh-\k=}NP\p@Ogw)0[8Ұyh_k'kl Dg38[I!WPf$ۛx* ͸a{ysJ8 HqT-a5ϻZf[B`+{As1"P,KBVJͤAUdojIr2{VAdB5VI< y=TRC#ܳCEd6'׉ aXBY 0ϑ\!  =cƏ$BΊ5A;´]a}+`U("퇑A-9 dW%҂(%REQ.lln^5 jwZ쾉5S2rt8\08#{02#&05{D0O 9pb 4@|yX|pRSLLt/ƔK=Bol¦il֌( /"8|Mpq*̪!&%['Gc\Yȭs95ir;|BXWjޑ~կ(j͇DVzUX\XL,.K\8QIpR-4eQk %KS<![C&"ɿ{ްd#V:+KRx`kr~UֹzeGw(oĨ.*Nk̅Yac lyc? *5?M\Pb[ H\dIpIcB&Vyص`s&^bXQByQ@S,_vSCf.ִF[/-[ب0sgk BiXdSE@ I ~\*=$ڮ/e瘏1 o! OjH+b;~O7wU=^:ÔL# -[%Z'nTn#am1"\p],hni'UGF>t ?Χ3h Oϧ9] [q=Ts@MNLjkdиΝsсk^ C:|q"9f7x%hZMyf5*m%% o0{V iL8s^N3([IYVlVDŽOV@O{ހBX.NMk,{ŧ؛0 hhJ-:PfIw[OoaMX_>.P <_}6)j%.j3/DZKi֪WQ]6A%(B#Ff@BbVMd'`f|.B( :JK5 esȉxBЏ|<^9I0Hh Ձ f#y}L! 8Ox|\)5hM,Rdf\VQ,鮅-ٚC?qxJ ?ZXMW>{'(_ x I"4p])ΛI, TeYBwa>_W"d.:O]]w q3S t=_/ ߞD_z u]?ѸGzQ3BR̮hGG0<w U|ʹ1R$8t-Xc(5Mjt`m[gʈxnMu=-X~1}_%P?K7Qax%T ,'@y [3XI2g^2)Lo@xxf,OTP,g{UV!`kv[Z9mOgNxq>O[.GE _'Q狽˭Z),\BaZYQx߀ Ew㞏#d(3,8xS4؎rVG؋Gm+n궛pq4V OF//d[k}/Jg(َ @]ѫ*TW|I0(%HTžs+,!sdUld^"<!I1J piVUz'+r4|OV=Sݥ{S|,q~ufC$FbWHb5`lmKs%@ې?X^ɧ(f#!y c`l! _~𻏾fu&Dc۩z.^3C: u_%cGvID6otд߄C;s]P'vbX2Y 1ۑ+@ 7N;+><_-@t mlo7u fdߋ1oD ,:(V!m7@͍6 KJ8bkAj&o0~l*="H%LzoL9N8m744,=ٲ#qc.*wԍ]!;"#Eh slEE1 Cvu3܍I>hw?O|^{[,[z .dž^䥂ɎstBW {v ݆Gn3&`bcaWZŒ* uI˯~ZZ9\tML,8l `%igz7Yspҋ1 7ug4w.r:6u${'hLzPwsۻ, qxz}^X(H»C=FS!f& D ǸkތeZnuMQ$#/Ʊ ͕%aRQַF8oH  r^ћ*0֢XF PUT@6f>&W"Rf@,_VQyw=5#8NvJ?" !A.'ڼRP_Y:+JײrqEuŴr% O9QtkP&+v'RY⻙̘Q(=k-C G=a%FkܐB%^~kwTY(@6ȉ2;dL\OЍ@CÖ4j#M[Y}I ^U0)4ܰ hX 4[=j> e|< V`4m /bNhzPŌ~-Q㗫\1p0>LIi(Lm;m |cN[ UGJL/^~zqp3?++l{Ќn*4ȟKw"e\o `J9x!5AvZhYM a")C-̊W Χf" _8,T`qnl[tsfe} ?E0;ewۂ TQBiM8^JXAn/g4bpE@*'c4:o8*yaBHڐAtl dhcWWkk$c`\PJöW9; \cDbV i9YUˀK!3ZӮ7!C,:x` :VPF@ BB$fBzH1߅ o #Z,@s/Ff1* N aMکG1 b O-W&{/wュ섷oWиaSA%oq5fSֶt`9?+ɟs ?7<-2;8FECl9x{t#0a@x^[h"|+`|#`F")<Jicף^/)YՌǘ㟭֧N'(k~X 8\/mZ@g{! 7|㹚m;Lee+5qrˮ8"~/BLfm%CP)a%՚|4TXp[iŚBB [@>J(՚5E:` Y;?F=Qru;m`6Lq^EEٽ Cg*\Oܨ+86@HP4}SWR::[fCVfw^^VJUMj˟`Mve|, I0]]ťuy[û3.F% }gԑ<+`"x'&r̽I:`9Od[nw4aNSOr_CDnp?oOM|{BlAFE=#;ya_oCs)ܔsĵπ|Q~9xtΛ޸(u~։_dI{L)<!X1&R뚖cQ+LE>9{G1d#ן;H 9M0%4[ Kچ;7v4vRyc5N _=aIXmٰj!PGlォH8%A~(B؋)hz7CPP£y ZSf{2%! kk'M7I^*i/5,>}GkMazyar7a"`p1 ECs1rݤ`#u$'!@w1)2uޤ$>(s IDAT 4a@3N p4:Q!@ 8`D]XE"c;@IA+5v-·eoqJ̮)Ghg5\ܻ ֮ @>X' P̹LkFl.$Xp\8\Vd ʷP8&yY K\S eE (uVɂ԰"`Q btG}.x[ӳqg߳ZD'pr@ ;, \,XKR߇/ŦԦBֳA#(KɑLXJe]$k)@zbԤd([c=1.$ؑW֧dnEjc*`$2`u17V:fD} 21M-]аd) NTK PA`l]kyh2ٜ Ne94V0AX aRgU4LcT%gVeC EٌnxXrTYP7usP'%M-ϖX_7*"Xļ.ŽR͕~1j`[/K[@ "g.>0&mEJ 0ep9(S2F8S)օ&jӉ6zu0x VK$&}oj^[;O@Čua<.A RlV\a) e+s4VČ"RGc0πy9-܆V=iC{ɤZEXeHFk \'o ]Szw4ex7w~_ߨ7rLWV'1cp u\m 25QB ? :$ۭjK  %l|Pȏ jpZ `,^##Qú@8 x. .e_ {O* ({⊓J|"bd<\Gm8o L U1 r`+ֆ筡4@=Ld-`4 Ր[p,MIZKlN҆#VN* faNͼ / χtWrGY R"G)A f | 36+HuJV%1!3cq b/?D҂Z KVQŔlDMcF\=xHHVfͥ0SMAsaMl]ـEd`ruJ֩qBqg(!C]-1^&ew7 od٪ڮ 0lIgzm`̹e2FD9;sܖ1XNGm31@{GÊt0&^~HMzω0+1b$9`wG0W5ϸ\&(𢡄;R wP+"0/'n > B@CZr];WҞ x?2sx {vƧF%E5gr0d3'ܬ AZ J [Kf;;hidBZ+BZS}?21֜uIhRq\L ^({uVTgi*EFrjs.l̈vǯ*%aM"x삧M ~`k{l;o\QUL8SLP6RLK\ h">`XMߏLcY,]ջ69d!*v&fy0ȼSUz;x/;5;BHZ*lD6d ^eU~"欪q%XZyyZeo-SiOrךxZ*%4(iF>Go1/EU[@|VW¼2~m 1A| 1I}OF:@UˑbzaMw+/G?<o~??oJ(ܥld&A VM: ,! x,/K"@j1;z}ki8>ōq9-S\g k<=1YQ^UtJdy`P# E‰yڬ:捩bLT}:ucyiLW-{~TT/ր)dχ @8BbFʄrxY!T!geB ^6QU$j-38i=1*W'B<^oe\-V`JX:T >#A hDLm ̃7[u`2B|wW @fTE8հxl %!ӎ s&"'մxZc!Q[뒚kbœO{(&MI&Q!I| yO>D$ 8K$5 s6z>AGzGwtht.9_~G|[o Ki!Ŋb$~ID8vY?>zt|#PoL_6M؏M$7{KܰB~]fz^y H z︩E1xqOP?zӚUxu.xu}b#>ey MRDz=svn?{ސuX^ ICu~]&L.ܝӵ?64A{$~%r2z( qKGQԩ^Ƥj~e60+ k H"[^ٓ>l<6h_[!;\TR3Lw I-~IJd,&i7c$^xN# nm ]q:wW@Zc =D\8h*`hHa$$FFc?h+QJIz8z5K6vq8 ek &Icc=%χ??)B^~lL31x[E9 y??oٵxv} 5 J| tOx KxntJ3bF?}W \:}!eK R  !į|[M@g`q*y .0 dzn/χߵϴ+#4s  3‹a\1%_7t|&׍iHw0G,ǐχf|V"8DZЛE y} qЂAvSkt$d `eg0>XFMtɲ0 VI ;KP-k .mG&`? ӢL[fSڀAە]yMR~u[X?Jʻ[JV.V\[Xz㰾1J!d)uuԼ# 5{ӈ.ou\\2ll"KuCdH9Aw4 w(2w׃p'7cƸ̋k\DEG3l' 3ĥBcK_q|mo7sDGy1'#_y-П(c' ytJC QqrŅ?f4bڋCuT ݊V;TݩSDeFj_"hΟ 1-uE50Bj'7֖i^Hrt4UEVS^œƤICd^ P|,}sXC#S`^pZs!,ٔyb+9ךeDjtIZH# PRrO^&Bh\ADjsm%χN֢4ט@ՋhxW_*&:yy<Dĕ&fm2@J)q":/~kIfc_s"5^#<0o7R O[jDH9ceÂ57nܭZNF5hҜsrʛ'Ļ۳gqR7se!K w<<+|O񡜫*&DhK9 H*rlXGC/?'96@Gx^ֆynk®s~-6?H;HJd:1A(BH( BF dB$D 22!Q2v}*ꪵ'sy}VZkժ r@qst.x=A: 'aaG>a%D3zI0.M8 @afQH9:jW".L!H@e,S"fSWLEጇ푑RXWQ$¦a/]ˁϟN|zqX :JKF"+?.[͜q fy8oxy⇧\p;Wc#ǢұR6<ѷo8 3 xYsVҙ+oG1~p}g o kӉ[8{z!ZʙuJi߭>YdA-U.9,x# "u@P"b`M8=bMY⽯ܬ*Iشs0b8mB?.嬁ss@):'ׇ4A0:h E$ңU[ HwQZYD8.TWiDŽ`w0pH=]]:c6z7bgI\<ع~QXhEQq(iFBdE',eĆ?<'> 89?vG|o| xL+Pyҧ,R(gWݡT6F ]>z û BQS| zzs#Vwz&7fqHJ;4tpΩˍk}*N9x W u.|zJuw?\c؎'F0 | rv:A pD|?zO?O̩p gLx gx~챚UYXoe)u'`m4Jj.^e\GLKI*+ 6,e`V߼_^s~8{*( 2u#{{6CʽW謌ìObMnxϓg Glwn!) #f?p*Q)u  YIϔ3#SH_ VQi4u˛iC' WgC_?A*G H!@ RmwBBGJtmw|r6[$g@V`AN]D5UrqCx~"S̏Q9 4kޜڏ^cxqkXV7a)dބ Q߳^Q0)Ԣ-a[o~p8?KX⠧ފ T ]S-T#=ޟOArh#n N~}a4r"#!! p.'b8lI$U n!C i8Pw\= .CsV/^،즍^w_"J~hO!ro“Yf=CKѽW|O0H_%fhx[ZQ\i;2r_aKa˙GM;ZaQ?zE3-́{㏄k'&H̢2#e=<&8M"1V8qoq~XLcA[ 9o2ġEL[ѕTE]H.M+!ln*)eCWFƵ-DENhoRk"2]+)P&m+/Oulsk_pnYd} ( 8Q57>X2v2&nB"\<+̨*m jх 2\l ƀ 1w?p55Tr?M|\tE)G] rD@Y-<?~f_7ހFjүkuř}S Rʘ%@ $VfYsfmG % Mh.R89S{(ViY\,պ$9=N<}ᙁ|1M ꞎg[ZC%wXѺG<<H^ 9 cðz:ޢϷoQyᛷby.'=+`QAuGkY*Aᘩc5PzF]P[ L9 "O@#psr,jkFښosK6"vL&?.$vEhY=wT`c>b< []O0"e8'_GE3s aD"]L[ (+h|9RxAhȃzV \zWE_K7[쀘0(Za zАw/54izÝg7t]c7",YŲ۾)(T7]FRB%bP8*9C{&iE >nL4R+>*H.ciC 9| IDATB*AWؗLeF9 ko~Eyb8M cc0XVGQĬ 'Κ :uTrv}W~ ~zX@x/m+ӻ}o+Gޥ˪ d[GcdtQ7TFllhcK2`٣\{37g D:`8fgOix đqvm5P5PT12D+l͵fP^_MU pW"zMJa W7 k~ 4[3sv+* (kqqV)j#RM㽥rqV[;J]N{v*`.L^??F~x q# i7,Ǚ;#!#VfD lKi7OP9|12_Zw_[C176KL֚VQ [$~_Gʉ6E< ZL_͠iLGm& H#4a)_'+؃Vo?􁉢g*ls<4ԉET#y{ =Ă.0>&yՀq_Sᙛ(SÈL?s&݉E^< %}q%CA90|_g(:O.q+݈+#ďr*{.B8bY|bYTl eT#};~Sa}97H/= iU d;T-¡5U|܌q O~Ooߐ5 cE]?GuJ,< Z@5s~>?uz__4g{EW\D(uZ5UkdfzbVR^qPĻ;Cg͠[PO2q[C@tY m)^E:\jYV]5= P. MƩvj,Ё3Vi&{%@>1#XP{942)S #T SxUYU6|yzov3`X8s,5hTD"hI(([J.T9dRU52jŚO7*zU|Eϸ'k~Ph8ea<ÂS=ur*2v)˃"Ć\!Olxz/9/TQJ%Eo)ZKW|ԿprñNJ(<#~W nٿp|=~G72QOݯg [Nb"[T4ڏyz+In!Y)#~M {ò KVJRUSe .^郹߯5@qìupAg102_%Ta /"U v:`Jx (q4.8TU|z#~q":ODZhe9UžAQlhe:iMVu ½A\x_v?=?|8Qw40QA,R㮵N}{,_׵Շ=TO1\'uI,HeE{8y,.5.T 2XW%;DFϴ'cҲ(edeAKI }|0J1sgRqt|.TB.j2͐ oWu[ ]"Knik3ݯMu}A8?}Pk-CMWJȽ; 8:(x:]a $vBνu`b:Β"9yE ""dI󩆀otUi8.p1jκ?^шq0 g+rqX.dTLVWf԰x?DtTTLc\Mu:knAH,ܶۗ)M:8B|:stGVms+Ĝbm|J薷&{P糎t{Q]Ǭ  5ל&X[j6{n 4^st۵ǡWǛ;2@сKr[Z~ eiYr@ )7;y %7)keTbLjviɱ<=Jw)팩ڠ$@|۝U*=`9#O>V^ |F<d2 X2*Mr= TbmBvK"ο֝ףjku->'ZѱK\p1D@` 5LYqZ+ =P[,[piBqI=apL= C~gsYqӞki_!8m?ڒ(1\QPxJǗ%[݆rɳ\nnr}U&s b?!eI7HkN3lpʻ3@71\wTI1RFi\j 5Q KFc;ZZ1ݜߘ]liݾyHO돿_0?hjRFTiW0l\ܻ%hJPKt;IG[wבLS"s &a?;bVjp9>NckkiXBfBL+6O([b=Z2x-Ti7!Mzi,ν0i.OB8+-*A>6VNm,^l#3"6u GZd~g=2O/_Tf"K!uZ]5Q3-{| rty8qӿÀRɷD!*ŝ,u i=CaYJJ(1Tn[?1cƔcEKQ?wdd}EN_cyV\]Qqdw[* snj_[p}Lʒbx}s9?+8UـLFSaAkt7H|;6aX0?8BsyP?O=N|"i_*tlPj̻=QB Gtxg1%vc|Xʔef!q䇕m3EM% cD4VzNo5bCN)oC9k1`ckGgo4kG?٠~h$COCKKr,Yg4*UsFVFW\߁>7«_z~'\g {+%&}ؐJ+ivjs$2- |o]3phVIKT&klVqj6DI gԨ$FW:T@^B"߫SEZԬQD́2F)q*yg p{QoUqRն{m4rb2G+vAN`*OE ڢ& H5.vM@ҐgPaQҭK#?,5Fm[2x{}4Rq@;Y9jWѕS|Fҷqdch{!%Fm?l>iވڍrEȡ$mݵU$Mb\cCt[ ]J=jC(O$d>Y)'n]T+-Q@.:(8ĈQ}^,^c*(KD:y lm! J-+?޽@_:y,Ү`By$pGَ"/;mK8Ϧ4&Cm_LjUIpmM$3.r֘lsHE1 rTSJ.kW%Pggk#7BSt/@_G`*HDRØΩ \Qb vMd\AO"fTK{;g~u~p~] UktEuh# R*"[7&M) 6!ABU,9wجyT,`aO,C ;`1h0FU۷m`JzyM8 VZppjǯ\Pf>ōs34lHh:I\oK5vTwEVKޣqՐ"n>]c[r'̦a=2–^nz :nť9-QTprC WN .?8[AڲG׭f0K Uʱ70ؖ:%C9Js~u-o}>j[tǵX\? BEApmb/ |jg[pOwu`8Dیy}g(:vٓvһ5V29aqM_30ָ1 (*qgtӲ"_dZ9u`|fXyw\ 먩A7 6^;1鹩@(svڿ3lF2ya 7Z2wɅ}nƝ6~g5VTEȶR;(ȟ%6:&fCҪM9sodFPštվC]dtS8/W?+;`Y@KQ)~yyNF ܮPìiYm?ϮיO_WKxA__?VnC :.Llv5no2!d 0K2 &`Y]ZldEdF{ꁇnzq$(0UM{z&pL˸g/b%8 Vesɸ92]Y{4N?JV|j[`j{d4x =Qd}.{n/y0D}yBEiS:r\yy7 28L0X?\i_5sb.y#cQCf6`z?;oEllk8!}*StCÐ/;aqnИpTU9ώesMԻ@gѢ^ wrHwPf{|5+-rehEvtpbwyoq%&1!to ƭ/K=yeR[(`"^,r6* ̇#OHa:յD!RVʙjL [S_}ic7Uf WWW|Z?(iÍӞ!(f4WּlY)0gC7SLxO|P:VA7֌Av^4)f̄ ?UX+yiWtQp_o,W~]#z ˏ' i`X78-|lЫka;׺K&= 4Q)7yxgd,<7Ky,(e>DB. $;P[7duCp6"!瑰)BFĔa۳1& ģm]tJ@ p|^aƂksMOl|G_䳄NmEŶ2@?lú̦Ĩ-4R@VS_2tZZ4ILOZ(3-ކ74Z1OA|3U9.Tx~6ޡ3\Y-ُ^yLD 6C~׍mu3ʰNї82n ZAgzCVx}by(4P%|C}׮H!F)uH$Q 1+'}p7<쪠;u;Ǜg=āȔum(D]C9ůӘw1pTrx+@YQug}.j?z _:Y玓GMVn +iq,A!Y),:=9FƄ6ESWZVgk"WG\ y <8!swS hF_Q71}}woWDTZ Jm*` IYѺ{#i9nP8·3q`ۿ$#[h ?2?Ծ#m';P'쌊+ڰ&%`A]) Vi>U̺fX;tY&VژkHWB&YyW:mN'1x+Ct"~'xTb:VR/^VT4D OqG IDATsGV j,S~O[ @;xq75 E_^yH|14a'OƝ5fE8Χ` dq|3u243\PeOA8 cъ9U~wDgT[rBA'P^%j.*5e4k߾s i^\dPe؂HjYcw5֨ƕ8:դ0 uD68jhʞ hï*4!gZ؀x@ UtC'q9"B:5g)\A+b9#n#[#%ڢBc ;WG<bMq\$Bv]2+5]X>,Spb 0NgG8CQj'·A^^5Lt ؽqۣsl!+@}A|x.!J㖛*B) amD\Ͱ kmX7鐅vyV5E+CWZQ,\BUfMꩡu. [ZÄ8=DzwI7j3xndYj(Z*XMέlۏ,J?/2(7^˴}xa{i!ؑu!1luѢOuIӕ^H{Wa/їd.܅UV]G¥0Jq1w;4=m?䨟Uiy8 (t[w1:p~{jQXpyTC+f5^EBW}HY7/r; wz{&16\ʃj&(2q0~=5άYUUY6UL1cz\St9 n Q,PeUїLi3C4%Ruֿto(/g<<_὏O?˔Ƣ$ n$%F߲,=9=#W[w7]9ꪊXR^\E6'0D1.wGmmOW+#W /C>TZMuFwƂXc*/4P)b(a ^k[f-E%\`(Ƹ$lcσ^3+u݆rNdM][fuUZZDrIP0x@mbpR>lZq4mkP c

9>N ě^3)i@fիki/$^]5O" {,VAa3+khpUwz.}"7z0S Ո>s@ܯ_];zH<ܬ f-]O<'I|lAJz AGj4Dm˻=k(XscxSuRٻ\>V~AEۉ;y֥#{o#FPt_l[sT(Dm@y:W+Ic)#sQTE|> pSu)34znޢQc$~!0e>0yzQzׁ̎M[yX5@UN/B}}}d|hU IkxnLWݥ"yXUf3>:K^WEr{ZeUuݝBM0r5y|W: 3!KrJlR^‚ue= ?MC2aY,kk_ + e$,ssv=h]G  “C s̽ BgwV;YYdUyiO {-ܙ~I& gEc4^ N _esUpūy.*1^>S©ȖbMKJ-L0fs ҭ͐ t&iVl)  R11kXLj[G| Riׯ+fjwsv'Pl'utiXOCze^Z5:յQ%a b+ɨî! , ,>3/߭o$,pZjí$ÒV۱MTt|"^46Cd&xՅ ry vЕEx^B/a5 ͝.˰ɭ.R\ܗ4RI$3Z|p`"FRz)T!Ց6=3r_ '/7̫ ={ '1RGլ!<i.۝7t<'~~woWcaC~^6(pO +]m݉$I Y'zt| 5#@<.ҝAu2^}]D;mՅFb&LF !=ݧ(AōgmY2rZr}ze]W"m9@]*`~q,@:~A& F5[(,3< 񧌄@/jf1QTW%C\(KieknkGz<&vT/P0GDQ p LS9`ԅiDe`vG2<@?ٶ$]p4GzS%kj (X<[Nvd?|Z]7 Ϩ w"}FK;"L ]մL{+qە6/pw^y,VY!&98X)DjW٤sjt!= pdggyF$6ytKW/JBWTJ/]d|kE RҲUwy o4aJ$Lx3~aMG48~_xu䜬Ǫ>`͖[Q qE`ʜ1Bs/%cg,Х}p8{a"ߕ"F;9ypѾpxIAuar/b\]BZrx(˰0xn-z8v ␊$(Ž?+}nVL`.^,KA>O7kNoc.4wZH^18>7.r^#a{2/ƶ@8{Oܱ%'Hs|:ʤIAg[ڱb*ƒ`p ʲQ3 suWju1vD䲈U2l}AzK2.e8}6>[ūl|[=)*ZO=hPȾ[K #CBa4.u0 kH\Ϲ/ |zc jՙсM[ 3uW AZ[SYZK"LB#TtJڵ#'Z۝d.q_UBtcV †ks{CF!yܟF~ Ɗ>!M#nWȫozਗ# J!{'{t8>m@ 퍴~`ק`s ր[aͺEkW yַ˷e6"85uIQلYeu;@f.IA{q]a0~9_lV82Pj8:ۃ qKQ< lW} }wxmO]ߤk(QxܼR؀lrV~,2hT o.Q90VY[A6zGrm6sO\FqыLϻDN :F}r8V{7J6ڮ"JByK"( q+XlˁbbB #i+_HoaF<Uru7gn JiUV[lmN␯, f# s;dMy{e *ǞWP荂2m(KI_ 䃠9"cZ!PS!)x[@Q38!+1#oZUzz[zqx0ݦT UFNs\ƮZT9Y7[wޣ۬].6YT@ gmi ie#J3fǖQn"lR$Ƈ0cVܽ28|I &tU{Tf`8fυ彐Qׁ}|GX~9J",*jJʵoUtHo{2D~YO#H)j#ɹ4+?'bYVVgRwc03#jEq4pFwV@(C8X*|&LB7,dOwzOv FvpJq73VN082`GK~] Tu 7]ƈN#hcSv1yr㇕},gzZ9Nu6X9s08+‘G\2aF*><Lz6͍1i 8GciQG ~0,*wl Yz'eq!1y(aⳂh.1acK~ҜxHh*ULK/g0MDJ ?uIeFm Tzny mh;tk[rJk] CvbhcBaG?#VˣRw#A@qX{au_yQG3#',G( wYcpKaE1i Y!q?9a8^y*w Zg@FkwLq@ ٗ }ze |ݧvUhek%߫G 8Ɋ}c`Dg] sCc(w$|O\Z IDATQ O/TԽUHJ)`]J)Ats~R8-vƗKkJ{7]U~TOc+(WٌE⣹[-r\ztz`0;5Y^nA 0/|Ӥ/N $Q/G9YągԳ(1hj?_{mMnvyo+k*>7)8{7(#[^.YHT9"Ό23gdhYM޸@(T#tnTk;]JXryf6 GD!KNU#^ .JsB3*AeQ 1-??`ܬcKUEP~%df@Dֽ{JUv*菩XqHL\V7}ɔke\K|HznX'! S8kMhg]V/3Ͱ-P}Z>7*\WX/ƛ'>*25<%RcW^h| /[֊rxGVK8&?dEvfSD@ sSi ǻEI03s@-} \erB\]*Y¢r5Qow\J{|e}y~'cQL7#9(s:E^(յ?AТehn\qi;'qr~H(>oQ4Gi8L}/kmz#OFAD JIfW6~ɰ;6AxE~`51DPʫ??K'f*[kD3 y.2elDjqKwos6kVH=(9 BdeAيpo0F_,R4 ?XsxaWQΟԵJ}AB{[X1 DrfX<+^P0uU!QAE. +JCƊow2dWFT8ޟlzȸ@84hگ7Ut\1XP܌][Ѫ%;Uo2:ett:%U|/ڮ(|~YWadb;)\_t鞚\/<'QMWSG;1jX>TlF;wۿ='p\ 6+D-ds@e(NFNjZJG(܏bŞZ8\&0T$̳d1ǨqxLbfw~s Zˡo˳,}#{3H -rƾ6A,DC(,'jB3:^8cTgX!``w^4[t&j &VRzqb $BME1k*Lz/ P$q>mvNadT+Ȃ; xD]csTWIqIȶCV1"8T!q) ~MP, ]#6S|J9i %c+ܢ0YqZb*h,O*iyWFeѵ|$>CO]O yzK?uQPoKwpAAnG=P'fS4/ղAC8}3Ƒ D9߽ 9)!%0Z#Yy6/lk_;\<vK'wu. ?ǣ`g 0gmz JS&LWDY3 P>*C\i$uҌv"1=5x^y9Ng!&-1ၰB@h">Lq{0F($!T¥P5cQ`Xr.qŨzn4#Q? /_tsMi5C!aGHYV\%O_#T?Bf6qqMX܎ bLoP V%+]2k ݀sYm16sCIOǜò ous RdZQ‘ 1'W~@(sYRN9:2Wßi~n Guy&xnXRsE?]Hڠ 91qԈGlNք,vo0G~;z{[0go d1j8Ek/Cέ|@eX k>ryUwX[Am*ntܻݠ_z3ql:w廋wnxVA%!}gpj>^1mmqqEҔPoBySxo+A:_WלJ3qjdN\ cILfU_M|Uy鹛EA{;<$x~\'FN|jcszCpt/Yž1F}&/L]Ox̤aWfۗs,ˋN ڗaM6.U6XҍVijl#`_ 7럫O;gΔYm#wZT̍mT]E.W#3.9^keϓgC,mւKfϦƱx m첂K9WZo Myx|D)|0x8 ˽;Vyw5}+oO^Sy|zx7o%>#=(೏~(2uXbdv'lB9/ e(YL &YH١lB(@^Zz-R:@ jL A΁^=:4>q}OUȩ}o5;w_V@in#*S\}$~8.o;E5-*6txBsĥJ>n Ș16%_ymc18c#8"FQ2P+25"4m[ ^C|J]kH`@vug*an4ncu%'KLhU" ԜzW^6|@)!7L,:MOd6οrKr `oJ;ݦꅲ9E>?I{q o~~4U"`>8P{}0[[zʢmX~4axڬUxEާz+lZe ։ 4taFN4J# (J t}(,ϙ-&ZCozoTp15^Vrm h=nK#B0m9+=~ͭ&0*˚աIlŸFpq!Ul=qxVS^˹[j,GwQSIS`c>[8YheGE#4:3zjgT;+l^2kNyEXUD͑iYfZK"eSMLv{aKij,͋855RR%Ot@M~2BѥSxmcex;hń(ƠX}K|p/-9lL8 UIJgj)AK3ơ?m&qLnF?-.'Dq93A }aY j#GDTh3\RBYCɉRiaAA!$s1#ŰL/>Ah%ŴYHZzD%ZnPv.V4Nf}aL΁eNΌ}P2p8=e<;wBM9f@Y2 1Fp(FB\T.J ]Ӻ&Cڃf''F 8FԽQgWl1DyG|Zmd&dvMTl<>UU ;m8oC XR$uֶ+Wa,In':zX@gnw1փX啤8l @H29Wx Jt qTY#1a9f5F梔0P罅kqԤE Eαo\F2%y6h0f\gUo[t 朸 wOOrxcq\lNy<' [b^O"Q8C4*5Vt߶^ 8Qjf תI|5,eom%E)vZdOCl({x 8ai6L)4H>U}-4W,uT+kx4p~YyW1&_NK'ދ<'-KŜH.ϩ!BWTEEvl$Ψ_|Tb@{J´}V&+^MW6+uSYoUU@^ܚǠ!;{C^) ꯍ3(ELmreiN , GjļV+x#|`" }C)rD N,jBejI!l85v@4rv?2^X Mˎ1[ Sm %$4o;RO/O0u5=*yONLio [׆gܽe%dbYM{u `bu(_z> ͘Hݚgls~<"VaUjLyэĬ%}f .D޳51eBޱQ ZM**3<. ol_YFq? qgH0]oȝ2SO֙ŰqRĩvOru˙4ttzME݅>b Ft{u "1Dx!k+w@UyY%K_!DT;I^%EaPw.F.%KܛRqj"kv9d=?C@xl4x{,.Y=HEٝK-0Jz%iCM䏀S-,Ia =h9l%LzcMeMsN3")EDg7~w'4߫-2wzmLb܏*%͌f`S8ΰ&n8p R$zT$ ʰ[CcyĵɊK8t}JPxgyՌ"JUw$J厞+23TF[-vքc>x-V)i`(,c(EZ=kTʘ7QK.҆lqE\y㼼tiGfpWn𞤷ky' Vyd3 IDATfۄQtӀ!Dyτ aLKMs 7Q3ak疗/)8/S. eTD(L^;[s!K\㕫o\x׽L'þ%4 RfT.:L[QXV nDZ_GT*k<֢+ ?cvؖj+~7$ J[ ] ^7 Mkw+CK§OirxhǩB5.J<|WmXJ%Y{t)+; `В?$\+)h({qEyF&/`\I/o? 'ȑ};ʆkUB^-(e2> Yv/;.s<2<RI\xw>߷, zxճ6f紺#Ljh^@g䌖I`VN&09:)r> R*dSM#S'z dn&툑ߐD@LTN8c Mv:QBkx`2V\q*NU{ M' AɔKر?B-2n鄑à4j55aQQE=ar[1cGZJ-rDiӋ}gf z1)ýg|j4ubeSBYB]8!> Rp l" fn !DGxz)81[.Io/4zEB6Q92ux%yxkц\ȍ'v_qxcX8C] _+^Y@Τ Z(yrgqt< щTsζ@&ftHy씺Zj ] s}D4Hƿ^ӓo?߷wAӘay-Xw|yOIIrgeL2"eBʻ\9"xԙj >#Gi$dxJ#vk &{_]G.=_眐A/!c*Ck.Ő|S1aRs焿 );= (j@xBw/+ZG Ayj5KiK}m'7 ߖ9i;4+B+$AZoEea/[ͬл?Q۳?]{JŀJ;=Aw>1{St"܁GG_]X.Ba] c nU2@yW඘pEb>BJxIE]~\RB QĿVEp8"-u {oMHOv*^Hа *`z^(j%45z&rwl[j{)S$-uD* pe$T!cM DCsRP%U>Ic"3EE~}DJ,'gtz\~v{eO i*u-شh&U/las[b O 'bwؿ-l7'R.yNF% sc. oq;of)aWe70dr-*K\9x#\ <ӝ&}F/_kx?/ Gtx]cwܚe'\InjQ(#|40ׯMgLEh>;㸧}r͏GH NxubCJ82+foc/Ɓ'`&N_z],]AN~C|HS  )9鵱Ҫ71BVB, w0K}# 깠S|<AEi*0wA!?h|y9׽X+T޸ f쩏5J`k eub /ctg6³kYUkQR w+c싃j"aAp5,=;+V`C םx[= x6Yn\l,DU^VYv59f7d0Jhd{JϬkU # ;A1&UJW21RRx.h|@Efmec@elTpsc+̦%2F{nz3sll1ލy3=)tA6RcқrgvPDԶ;i/^]G ׼SIqw[-2 8=%YeXOY@=fS5}v8p"qT6޾ _{}^u ZsySA;ǮcnSP:ttUY,cM_Uň94q9e^ / p|I$:|J̺ c{#6%2Hh`D'/mw"mU26@7%+zC$$YOJ<$̱L9| Sw_bz312d=T1E/Vnń6J =AӤΪmPξ`].^FbbWz4N~qKζ/=02'LD8aoi#T0y"T|ܥ흔%PrV~5 T3r"7p7QN<޹^Lw: =]5^pJn7DX_NjG>WP>k>ݦO;TAkW`]0ጐ_}BD&[O s;u;OLECw* Оۙ(Fk{ȋ > >o+SٰعݭaFt|Ta_ф]%u_9[=cD_0Yh~|I}z(m;/xQo«ScV}Αrwh8NթP:Q?F(Ѡڞ-^u8 % @56睕fQyN*y}CҔ%)MLt0- Ërz>OV(CI 8>jO1 .ˆ!-v 7&|@bxwtLx1p$ߍ7o-$t tBh<>N{\ :s 읉Kd)s<ja965w }it͐W9z L'{z" 3#b"3Ǧ;;kr DpQ!eRg𧷥4 }ᶝ)3!>~D唽Rr7J 8*:%ŽI ;V^o5J p++:B ۱t_lXqRT#Lil-q劕Ŷ#,jXIwġW <6dB ;^׊1ܜ- ʪVu=HXRٹs3p| `ThLz G/Wi䱿th7 AR2һRVRI+;vVX)gJ]e{њ䈤P|BN}Qmy9d罽GpVVh^ \ \{%[v{%Yx>) kX9J6 `Yo64\,ĥx QC'v9՞;RZR՞tJ!{}Rp"F,`zch U[p/؄8髇B+eH3ʐHӅFK9 NRhoȼͳ*Kٌs{ײvW"BYѭ2٦Q<0i?`ƒci#?:(PyAV3_d07M`IW2V y?pO)m{Pp6%.ԫ&5ͦW-e˜e$\JP_c'Ǹ;Ww\ڻr[$Kl+"+2W5&iG=CF>+mRІ=zQZyѓQFΑ *x|4+qVx=gYG3+ 9ߞh nc](#(:۵ݹ1<MyIB\lw۹'x9hgIi4W2wa]~k: r?<2xOVi/g5}0P0vnŒ x"au>q;kC3\NF̛#Qn[v0Y F?;WZIrtb߮uNLu y*ـ%n_ >wZە+c_:d.bf(,KauSaO.LOux (^+B byo??l ]-T B#sۑX>&11!ix [FlϟO6F  p*to_BxC0߮/^mG&+y+:F#/]Lz` x0*-^eAKyUϗ _9^L`QA/]pùѫd7W+Qű7}wZ`;_9D3/ o>yEBWbĨhٍ8h'MHӘr㪜`WFP^y@?,LId{SRv{ֻ:˒AeRɤ2Kb">45++.Y1rsc @=❆YV31[+IFA}{XEʚW^~Sqa2{n=8{;ݺ=IJUAu%lnZrU[ MrR6=(| nITB| n;Q:KȒq׾z/Z 7syK.-xm.C M^rA2z39Ja/\gnC(!XI c[i.¼[il$e}?M17+B>Y^{{=VɰVsė*C*ܝo\^R|y )wivrbC:/E+~eK6sAlDy0xeXRus=Cf. ):%|FVxqX; pkB\42Z)_ҵҞcsT(n:oapϰ5ߣC%aG@. Ï Q.6Lי8}E@p7ݽs5\kSÎ_;H'7tV#s/wd[{G7/ܡ4+EHqn[1YK+N>C~^LȆmuFu`Dh+˕.p.;঴~K$WsW7zpr>&;4p,HSPY.);f:]{ P(q3Sgo 7-YGx+sw.BD!p7Uc22kZ7dLp'1YT,|a Gl:0V^J>uq_ "zYk;K^.ua{'w2߄sɷ,aS-zElQ[3'حfCfTQ]u33c}.jleDa_oHτol 3C\:VX'sra9JEduPڰ,9x;`g@/㬬ױe'Zᵝ!Hլ{&dq UH~)oKbst0=韡/*?w7ˣ `QW o,+ Iu XuPf۸x!w-{٢_HWcґ9x3+mFضfzf<ٻ|\7O8Kngz%CpBl,=mOJQFwk|z&+lK4*6 rR&ORf^(&2_LNʝ6:f%3~. !O!vHύɻ4{˼ߪXSvŽ**Uu{B O਄d'bpi##=jUhTY{BWcUs"ν*~t[Ǔ !aWj(lnmS_ʺ M,=A܇t P8FɷA|8zhtrW+$ 1p7"J#nZlQET/=ËGŽT)E6qI|Tl!oV^`k_OUY )#g!>_iP0;ZT%.VE]SMuGhŎXelMKbAqneW#m |^^ډ4;_v[+9-WzxHjC "ˎA^9(\[pLl^ ?r~Pz6K&T(wx<׷ê\ȓ''ZrRhhHm h4 '.n'.~+Cf~2fԞWx2 zeÏl5 r5Y?dڽzZ$knћΫCVvt\1?id5GK~Bb[vllE8j~wx5{3z+no?fGvrRFn bT&V O|a|+ oäVP1Gu"R)|];Ý+X#eX炱HU,I:S ,/ʣOE2b̝  4T1Pb;#S /W9-uK9wbb*DQ/isTG/G_-*~\:t 2iXڹpâYl%)z6T#&jXP6ceV* 8ʃuu ȷR\l}ߧl*@XlpLiiX Q*n+*):xc!H@tcSuy{:PKi#[;s*꺅nXixYUFa{`~s@B'YjAMLv0`sX!4m<=<Ih^0w@:\<7!qh|Ӎ|xc,o҃88b>@)fl印 (U(T|:j!Vz+q^|~?ۣC&xyn&deژnāʶ11}Jxsl/B$2eZgk%tVX5| xݫe| ܷTt+a)Pj^32*~ ̬ynV_‚Dxs%b8EC3;5os5D)p g.20Sݕ@ʢT.f2;v }̰9zJ414Lj4 |G&4BqȾz~̺xY7%i00^*2v:s_e7dÛܮrnsT A9֤mJq_! E)DO딝',=Fͯ ~QYPڜ Z08?~D'=5~6L_i.L]jK ` Ats=EEf4p84ѱ}y4/<ɯe8+PoxcЇԝ5۹-η8nɟ',NoL?]6ƴEPiqvTq5 K|* s'F]Z}'x2-dǧm-D7]U=gLY;O;Ha5]PqRU'0ؓ6Aچ|N QqWQ4q0yM•V:EdkeT<|&ܕ; {eI/f5ߙCQ?jƷ1qc%6 3Yp7Xu@_C)*HPdᧄ[SӇF_4x,xҞ_?퇲yW#06"8xP>_97hf~jh"ݿ'#So2>~nHJ a}n!5 g e.}Jl x ^ 3b@q ^Cj'UpuhTk sE > MFod;\F~QsD5 )2fxt_Y=] z5 i kuʯ Q$rgLOxzS9}E'@5|s32' 8b{@h/@0,8`2a`/ ;_dVhL >k?1FG1,kxt5S {> by̔+孼3̔m5>t[*_y+!g?z(/BPY h' s Zo^brI@]y*c¨G?Gp 1<E!\- { ?zLy4N @ WSf] bCB۝BiVp e͕Kdh8TPQC0g2ס4D휥+b Nj4 z\"3j,p~>19:6󦝿bb? :j?wz.qM?5t*pCbETe*tc6ur, 捂0D4D T}žALQ'{OW^k4ď'e"{>I?;h~[5*@S`H!)\x;j'c)B(7pCX!tXKg!|x%n{yP l˝y؋4(q3h,'Q"굩sSil ;IJǶB2sH%-8#W CGqąEPv8qqyn>d?2rsUxL[s<"6TB>@oWš<8}LG=yeLqz*~1ӬcU0 Cak&M8)U/m\㿭5U;rC{j_DavdgaIT0cބ^4N {D&AY 694=c)x 3$˂;1Lk nܬ R<}/;d.ǡ=|}>=M$%] Tx3zۋً}q4j(SfָC Ēx~aI⥒啓.בy*['Y5w/|y|[CeXKۏ;MEEW**eGe⡍W9q+_qA|hltoyu6U^HPw鰷6hz}yg]"ԃC0tX"߲Z՘nAICzUzQԦoOxJCd *jb\Bl8jjmcmD&*mȀUce\К~+,_̄4t=ι6 P;ΫWz@dAU?d9䳰|~e>= J-{u 8?൱vR8|}qbn_{O.F#D}}i>8EVJ1 »'1B%v^ ǵ\1ؠ'*,A : ` 7ߠQ`q,̌ME8VC;k<5!qpJ˭A^Qqk<ۗ1J;.@W`T6=X7CK2DE86HU^$Ve=y*U͒OŢ*zsh8BiJ2+Q'eV*bkuڪ?Y^ 8BȱAwf^2j-3,جU8W (/Wb@98VU57;hb*ݵoJ,vo.؁?B{`mH;lN8w4|="[e}p? ܺ{bLŠDZt̑bgUL˱$YRz!@b T'nz4dB[5cKG@VW媭ʖAg?TUG*;u4rSkQJ2D19͋ 3R\ h$3b)cD q*cy2cCe-7M& fU56" OQڇ"`=Ƕj(7OXcZ'^#8Ua[FBڈI#ɽkgV81C { d씉Hlz!˽wPOmX.hg Q]1'_"?x'E xxI1!mM1bJ&L`_cFl,2SyO `Gгd-7`- ډimX 8(bo/DUDi꣼fj{aV᠈?8]U+ Z/' {Ln|!)L^< 6vfE]Pt-_@բĺIK9ƣ~/ 97 m⛢8- 7t^5pV냡, : IDATUFʣ :Ҟ qIKRl*h*GT 7%qGϡ(_~5*ng8kM35Q @nQ~KF"%1`S& ~OXB: )]T O\E[lX滫a%P?)H~yvSv13{^,8PG?u ^k2z?~ QzRQ$MkshD7d<+~q[ekނ+ j, To(ɹTޗ7[\cK{T)x`xy bb& f* <h1\{'*D+:r1Ux.o94cb(r*RFʇ\=iiZxŘwV%VEe3qRiޑɰP=[ r,J'Ե#8,XS![u+tGTFˌ!J ./Gz 1ղ@xa$$s~o6œB袌 l+cYs,ILMO:*b&*ZOh`"JjNS3tFx&>酏n3-\Li |Ǚ&K t,nQ.+ft\vGOA9:xԏN7VeCY݉ C&!! OEV)Br́ȓ6P9nӅZQKQ e0Q54M,OI_cҙ϶Y"!Wun+qY聄R(/ȨK̫q SE7 fchjĊ!nl('mƣeT#=m5y_iH!:7jkm1A⏮#Z _@zLyuZ#Yέ'DB']`NmPs˒Ɇ=BN]?`X+qzqPU֮kӸSbRb>l@?pOeƕpf[޹Z} (,Σ{kQ1nW+ZG _ \Ef K^1\RBe#ny{evP>7yy-T^F):& Yn{X\=iMX[-L^eu|uG tVܶhW+ =h?)1+Ea7> /qWF> 3)(ƎL໓y s|0=#qYL8GtGH\O;3Ah*EE@'Sun$Dg`(%kPch\"ֺԌl˕W|DM"qU7qyA iXmK[pܹh_4v}TCy$.V=gyl#Ufh ޿2֑xFg7p#+M`s]CKTslv1rk_$x0 Ü.0/-T>TK$)e%vdm\c۫ʓJ$Ac)Bp鮾X?-wrl&{#EoW|I^%~O_XamA40ljALN)5W+/?#Ҋ8nExsN+_tؽ*(C19IFyt0DKwho40:nu;ƞ NC\(.],}lu8WC2Oԓ(.Y4o<AH@<РEWd1nXl4׍c>RY>㤟}^3mE }&D\2PÜZqbhrnC|W~o< VH^7prv1dP,!:+4U-y@r:6cʼn~yun>HЛ4w͉Phd}ovx-W?ՎJDӻIoSڼWab>Vµ(7BXzRo O.X03%lFF߹7d&ߨZ1IkZ QTp:}Ԛ uBa"hdSJ|7 i\v]-a4/]V*uFoٗ3lDRxSB*e<Nو"e3.VsHy㹪b@ *NBrl<ȼ-9.ҢAV!}w^ȐR=7{\޹=pWFY2ԅgX!AHYhh;ZZWO\>7S;L`1x E?]F_ˏ0Cjg3l"9>8, ?c4pAW MAfյZQ?JXZ%@HzF.zl4toAVN*sY/&v"F+߮iG{KT(4c{^x]VBa }PY3rޯ '}9]Ig ϫ'"DD1ѯ;(yUE8fHx;$M JR__x[~6 U˹!7wH=Feœ((FL94^+ji,E +-q N ?^.ƪQ5. ϕ޲׊#޲M0N%}=,VaɏHPCu=V*'51M''Mwx:5o_MaԶLZ.zқ45.G(%G9,=GYP*S 'p)UKֿA-^G~oS803÷֪d`hcGɈCBPXJѣ: S"w񚛉\d$@ >b NC 2r:=⤝Cw Buճ"*e5 @ⷋ .PV Ox3I;1P5wz޽,τ:b[" pr(j5m!+'e%2Y!h0% ( rTqԐiɽÔ9FHw_*G8lC>OOn̍?hF9}-;佷^*{d;a(*<$a҆P?u@a LJƟۂ &SEsgaA*N~lhl>Ypu+gHyw"4ࡕy0E-1c)EnT%*[totzV#~߱ JPg6_6(8([]nEccBgnCì b>qPW Zƴi#+%ե2U72ҺQ}Ȝुj)Vm'ޏ)H͇xnXkaCTp:QkEDRyNSUPOD.XkDչВj.G&=BaIicb/C+ R~y>o+(_(bkĽPmlMgܶ:AHdHsnr:x*7EƫWg)ϝ[#iTseۃH^AM^_x6ihM+nYzg|,~@һ@1#"PaS>WpJX2g+ܒO)Vj6l^ϡuAh'%*k]r: OZ/c@'Hj,Ը||gKPB952; v^w0 ҟCWŔ+z\Kו1kU{Xǎ=k/Lc]#~hHpg\ʋ* :ሜEv1dY2c&tt+8I75C&OCr>/x|| `й˸-پ͵Bi~<;W\96E0cF2fy*Xw8I<‬ڶ*O6gO{;] QY3ٸU 󕉊۰[wX 3V#NYT|3qaXܱ&H= ;PWW|E[5E} _k1#& MZF͓,nuVwOO ۝Jϲ.i(UM$&mNgIR% ޹*x{}>xhیF%FWSfN]M>wOȄ\}d/Dm.{>Snai |֐`ՙ-.=Cܾs'T ȗ+ry@׾[Zo0~e]jyh|s>a.{o!w *xΦͽ.o_v,B\c:+iHRʢT auYg\6Bxc.Ȼz/R !n}慻mgW}~k j-qb 0]+Lfj7 0PAuNΩ~.> rzXb-̩:0ZAsdQ(P1٢_pb 9 og'lco-+\=ñ;!$َ@&RP>~ӫYH'o9d *+BҝqP SAv{a`߲bsEx)*dk.Eq# tTeT6W)?- qZ{| _' Ət& w\N!_IUC7Ǭ 5%c6sW0pXoƜUPi7w p4cȲ .398cɋU2Uҽ&ê, n!GҋzSX{ad2}wl&כ^mJ?cSt0] !9Ƽ_K K|n,X|t*6u{\6g ǝ<PFj cﴺ+eNݬ[%]')NvuςHYV{] ߅d>qz97NJmqUܖS 3o3A#.iZ#1τ`&cS^VC}^V)B˚P&6|/YMWKxj9iC{b'3T [l%[Z :z!4F[n8s\: N{ioVp? D{=wtS6p[B{~Cd?^ڋLȨ.[Y5PkQ 51$:Q3NcU>, <)Y|.U&x0a\c]eqU[F֌(gi;fH]uOXsobwݓA``yZ#1ʊll}icQ+ ON p IDATt3 bT9X)ױ29{ΕZ>OhS0![ 蟙1٬J'hoI0x*8zة`Hf4Hu2uyoAnt'BhD0̝\1 +0c]-HGsՀw]0V[ iZHD0.|C0Y|fCZ8Bor{0(䧼yxdqA3Th _C9rY$Maf8Į:@ ; Q<(xZ,Cįm)oOo l5 |xtl%˳&Or@b<Ė+yeQ{NH0am1utd(aV8[!`-9-Ad: ҝW-*R=<)HSY33;ع}l&nUsW33_gHc;,p.?3ou<ׁ,w'͙HIUquc_@n'ǮDTZjcXB;3)W'N%p=f0u{,(;8ސ{ۓuwhWcihO c;%]'v}l6(h&3R\OD@zUBb|I\xxJKckV+@KAV}ZKeX$Wb 4B<\xBK;a򫳙Mn<h|:I<޴ZRc$E`kwN @F @}v|{'tYg}$v8"W2eU:KQ0xoz.. GCRaMoѰyeURbc8:tQ$Ԕ,{$9<҅\zrh !OJosŽhfA/ cGme†cM.^ qk7C@RMewokTFbs<%^`ۍoAR`yfQ,+@^Nzx]MC(@=-=z 'IeǑvV9k%P tOêVFȬ]wNo1y]U+~ƙS:1"?T#Iߥ }v϶V^xm624ր3a#B zO5>u g>{D:9)rw4.ĔWba:MJ{gw37*F+ d*$8ҴN!+|xqw.=GoD;ZɣP6kEE-Z.9 Ky8;=\'$b}(~i$l,ڋj r{P?Y~xףG|X~pMu.8ZOx69 U)7`̰mݞ0 gϛ=Cm^]0dR"n*@EN5x Q/摱B#YH8S8aeŨ5"og1{&rKEwϤ\JeQ9ʻhęBiDǞ)aTG:oLv_ƆD ֯TA5>|8]L)1[ZACV$[`Z{_: `GšET*G nE&9 C`I>_UJҹHs)IJN#^'^Z^XEe<%{U:m"8!̛(<sKVbO{?:hQ5zIaDMҔؚ^tnݟTyp>T4:23 \qQIЊ\b|,8"Z1; D|WsU2+>k KѾ$6L/c6JCd` }/<:&o);.pEuG&Ei\q@mtuӈ_Yo[т dxm}ng7Y&͑:j_PoBdx̮znVD#BW=h͊ygIj" *On]#^xĬ*_~L4H@7ؙNW3VɤPJ8A̫^ nH+޿(,3J H&Be|6e ePkn+U܌/ay.Lwz ǰPLu !tl` ssydUBܪynJGH_FJmMx狈TŘzkȁqZF xa(6d(KGj@zvSt0D:/\kwTN*EzHaxcF2mtݨ9Ex73N5?w,F7#qa\(B!vQEL!Vx0̹C@m5ښ`\w4U[r 'RfuCu‚7M!N%NdKgPYXR,tjd3{K`V`> APC72U\zK )kRʮ$m$0P֧SjT /=C;0铂,#̩8 l*ܢsv5FD̼ME+ʞ @c=a1~M 2[#NVU['-q*9*`җ]X=Ul%\IUl%mdd(K|MYF{^Նip02D_"ut| {u_JU5U4mc}Z1yuaAJc³ %qoY|*IdU YI}ס"^bߨa ̂(%O"|#Q51_N"Xy1Ê*/53"2p.; n+Lމ\l@Ujkuv 'H9Wl1d`9w<7R=/Zh<]sɺeTx׬{P_I/?%{^lؠd=ӽGtfdgk3z"ЂMUԁ 9\ք9=+r' 3q2) 9OPӁ8TS٢+㲤at'8ʢO F6Y7&b;sJjᙲOcD 6jA)bi(;$Q k6y[([kV5A BEh4,V@tAG :d\|M3t,֣2Ў)ly: saSu hu Lw itI@J*5K @z.=1 w9RU>UwnIyk,Q 6Jy3ym@?|l1=AtI (;<ޭ+'a6mkRH:2CY'ˁ.g`HS!k\St,[/l5JW4!Α|^Rud[jzcn 3!=^]h$K\_S'?s5HZ-9*mJ5#_'('rq$*r@'-\P愤2[ɨ)&j/8ejG`Bkc^lEMdL-VG9B_n*|\-qNT MZ_P#:R+45ㄫ5+ q2(V([68"7ze ڃ̙GC[iߌ"+/p4=*T`&Jie>[..B3kPٌ'ܾOcI2dT̨M+٧[G*v[MfVGYJ% W+.E)#4re- L*ڨ2g%}UB{6"J oimG-cQsTyhY~5q_ʗfDܕAGA{ ZI܅H KK!סeoݠ a:W`m*Kw-p8DjN8?a;N{yr<;Jb6|f{!h jd]c*PJ[WO tc WK8hoBV&f22;E]CKj= -(i4sT@ <IxN< ͝9͍xriAMr˜z HUbuE~֋+7l:!ح7TpΑ;`^`.;Ƽ24ؐ(|ȻTgpo.2,H=m } y_JA9p!!-ʫ-AMmy5jޜ; AMNnܛnAG5.{~+9@lHc-#1rǕds4N$؍)tO셀:} sF YcHB|}tO6ՈғaxTBiYH+f.֮a?_02QiD?y(ȜZp]kѩ֤paj.pKLzcF`jrᩪ$ix-HzUeeGw/39v9 ZizIC_5fzoA1F\G1q)DsQ-Ԓ]-h)-k)7PVjE3Wj"x8\mq9Vh/E+0,ꮍ#og|ya9t ё>9/2؈\JY!u GM%+Xs,+Z($3aIu|кΠєTP qu>\pX@-yT$}.a|j_/o IDATXNa{,GXA:#ʮqDaץWJP§d$AƖSSF"3A-zߧJ $m;j% *2c31\)5rtN4wcjp ▿oG7Yl Ȩ0X;2eWɈSN*jckbʰ)7=oqtЙo%-U/drΰVXt aljq ،C]CqaVN!ۈB(@.)r\C({ [daQϣpm)ـB(smk 2nl&L.YJHgE޹0s=o;J }iXϺmXaKg @ !OZTQ$AWz9gT%DzE ޿2b~kcwHC!޹(`z$wlԢ3p].恣bB6瞥jɓy=v&H%“սem24xmSDƂ =GJLppdiCKn +AaDX{&eᑋU3a&5Sq*yǸńkeF??Kmҵ^!iYPn/`PpjEI;q*`my}_\>w7Cܭvz83$ݨQGd#=y{f) |՗lm]mcY3L=*WR<\%BvK %|:Lo0&` Pa泥b&籇j9y30)BC*4EJIMaHuFXFHq8)ډ|y 7+r-T0ղW4ˆƫI[8y)] UaU0pQ$%Nۏ:1VF`r!, e.}z 4/{ejwZA_JLyXH#! Yl=XOGVJ3&[?E4S/KQ)oa};%ja5j]^='>󍪣c.N:'Dٯ#|힂!IP4]Y?P!zoP؝!ݭۚkL2:uџ9W3rob'">n4h>#JZ6'/e^`:{e Y'V%ezXFPLJbuo6RY+FwB'Rv7ti)~HP^J<\MWv ۘ!] MZO-k:5tM^c/We]wՊ|4{\o+\1սHk+fYWe"z("-̅HawЫBѝ$Wg 8~g_B{7'uDaiWT+؆ʏG[ptˎ=qJ6 #+J>OB(ei!ʃv68bb~C (poG7qeYcѣnCVSUwXHT*Ui,A@gkRfР|idwtvkKGU2=OXïE)Ftl {( `V@3y;0.*XIȉkK[#RmDˍI=$B1!wd\_~{kvO;o4)i?)E17=xIKF+O~ڃu>ʲ9S/87]9 "679<skiEG%m:>`ܼJ\yyco6mn42,z南! >]_$<-c3:R,yd fagOq;艾Зa8{\fWppkvSoⱌ-/K#!i|L>*^k+Mܲ*C۩0QgƵ2G<)-:jx5g @ ׁcⶁiVf]>vCɳkf5֜ py* @(ڻJmʾJ4~;4׻ˁxUa=@?~pKXS[z,Yua,M\VS@UIoMOqwG#b. avJX֗q!F&bzC@k͐}dwc0QY S ;#Zѓʛ'Z*R2H]< N no?JUmThTJJr2|V(!He;Y<ۀ\tc'Q `2H66G/HC᤽Wh,>tGcC[)~w!1@eujM9b`OF7_5Zm#T}S++o4Aͷ%b!G@yxԼZh8rzegRǨ(q&B@?/,Xl,ruVUS7on#g_<+V&#غR*dҢ7:VQnEXc&L!+|^) -YRYq*r W8!s9uRX7CB5`0*6K ^'O*ZIk[3;ƞC) 躽,mh|V=T;8 z] lw wZV8GSݵAK}X}f{Q9X)iO/Ӯ:,?x;+5t$I <\iX#h"Y4Ǘ2ͬdM*K~e ;r;H}ɔCh܏OYߜg>D,AսZ`#SS+,luD<8*Cܲ)[C۰1&)_Ps'%1ݸ:iuI2S 07WwpKB[n yÎ4v*xqqNA"jr"VGs!nzILe/*6{ i^;/qlAQ H,=)ȟt'!%YwV*16j40ĘtqFc\goF+)|Kk%o9q@މkrFt8`<)7.Yk[ެ]mh0DG2 NI ~ܫ\^vE<Bt4VA;RֻEN=(YW}<n yy\Z0Eʈ<h :ӟb^W#tyPYmVM hlZQi:] _χa Nf [bm1k%Ҷ;̚68SnmZRXQ)X6rzGpy~YM{ Wa8"U7A -e@U)ݙB{ 'E~AͶ8K腱9pD:jA gvFhe(iPW5׊Oۄ7"N@oW3Ҧ͐x4 _-imX8!^ }؅BE5M=zI8k&ww<ĵ8G ~З{]2sd]1t0+bc1r6|І;*l+k+ވ6kR*P-l*r"]X7v e8n\BQIVú ޟςᙊC}eYm^9d3E霤ƝpNL sS#7 ]"֌lAbgX5,ĦW^ETV<,waDG;\PL<@/A'"R:_pt67Y/&l4JgX+>Lf@;k87KZ}X+y"RN-atJ',w/Θ ,G?y?LjBh}ʾ:X #xp牄 pfЁ'bVS$8{DS"©l EЗCD =eX_HŭĻz(pyq"xԱUMnq=(zžcПޑob!?I=*Ka"WW-l<{,k+`n'T-C!&#=V1%cVgo{jLGe$:}bxo-\%;\bz9ǦjGy#U+ΣsbO OV3y 2AwJ fl̡' qC^L:oRW+cS4aӠG*>nloH'w5ZF S,255FC#Au9&pL m$_c. ɗi +ي="`8dݝaaa-~YXnK/6\GfLBe*_w-lj Q.^FK!s?ǖ{1&+"2o4{݊/s*rտǦV1ߊQs@Xm+2m&筏%@[SOOsp b="y'g11X%q"+~Ad<>~x:0VJ(H+ qGy.3 "|ݺN$]2=KI*L4X^-6x}hvuEch(`-mF]&b@EO#=3!$w`Gzj%uaLev'/u ӥ ^ XʹDoѫMv^Fb/#cPÔ.32~ +!řqSڽ}@m^P8 z]NJڸ$ ;k۝!;l`1BxU|N_Xsr1vpb WMW_3;QUx:۱^aO`Yη xgyw=s? %̓0& ѧ>$β8w.pΐ=5(UF K>dǖ?,<>vՁU a]^B9BwZs(9q+j@{OZ!aض_`ro~7zS;/W=dVLkhX敶v+OzeCؙE .F?KZj/X,L7.ǣYg;n%|G0yYDf!0RJ* +WGpZA. %$31(|U9r@#sUzV'AO>Ke4>0Pi)=p.[cWZaidSܞ08xш[X{!65ܸKсB[w= 8-G[.Jm<ǹZx M ~+N,]=XMW |s҉nx6 [Ct^n-4c?;Ћ&zx`Ǥ{GJ8Tx@V+V'&1p&B:ݱ Bd[GSd!.7ޔ{Q|!}[?dB~]^y70.xncAh&uۄʤW}'+10xaĘZg[l,r%"!/Phԩ6)' *U$0C6yGމdMa,CG yr`M aL@Ϭ ;?m݂4a뙴m 7f,tVn?|N ìG.vN@:a9%xІE|`WݔQ>UJ}r&5A|N@U0 i\x/P.j<Ljto/u/~QWj|/H|3 nz@Zո!cu/g^TGR\{=O B='#4ҚOa8v?M62>.2yWiQ@/Ku|-fV J 8:>kwt *V-6Q_NŻp>a^NDp43'|)#&ʹTz)aC^׸jy =tcLn-=SN[n#=3F08c؇Ү+ ӘOEyX/1|E枹+&Xi!I,7.ώؒ"{M طYTӕ9΀Ļjf1i|f N ENE4 |Ԟ OG#CByfE ʍץƘj$ƻ9fxiE/F5j%6VڽQqhy CPإ)7ϭ7D=Z}!8k)e{euNIOF ?5읆۱}4x.T=h\ģK;hťFTЬn2\)(8."JxJp\@+%[)Qt˛&Q^;2QdHI4~{e2RMy; cۡ狆R%!rI*;&C'cШ!6 YFgZ0´3pWBcQz8em+Rn=H"vZlPwb'NUv)MBb:ȒeWТ ֶO"ʛ[JO&qPvⶎ :3^4}tO/"8L& 9iux͏,UZ̲Z%HYt~~9c.C] r6+ϋMW`_ėW|ZW6ORB|4;e!uenf#7DчhCf(=$'2FGty#k"v =6E%aC;U@[i"T D :=3<&ڥhE]Ti"}a2.@2 )D)2PW̃4#RIHX^EFgl !E: *ur{o ǼЏ:{nyo5Yߗ Ckw1>sߍu஭0v?Pie-RY*QeLM|Qo"MHBZϜHY/T)cG8, gk:Vi;"ivD{#_TkUPɓn=<\wW_#?K12 /9{,1/xbyZ_J4YTc9޲h1s2r˾pTQznlT՝nQ NY8$'u b7FR+- xď1F&L9>NfW?wַH;&O]zޫ\✧;)}Ǯ A@OXgן'vG.v,Cze] 7Msnx*xKo߬}h`iF)_slIqA0:ZJݔ3:Ͼd75;HﲪS{]Rᖓ>{xGתQp=Ǽ'儹o+:VSpTV@J#/ [EX=xΡhJdR q0Kah6i`'-9s(>qԼ%X@xhcsscPC !ףI,{s~FN:"":ou dZPju[լuEW"b! GQ7E /M^4|L_"vÝ.kWI: ".\\0ox&~SZ0R;Q큍o3~/?9>׿ϟw~Oy;ן7gs[^IZ *%hEC`FwJiʘCX/xf|nX|ݟ.~_7}Ͼ ~_}͆Grw`&QqP6 >f])$w+\¡pO2% a<ͪ?s<v`i~LC/|x||OO-~;;3~?w??TY,wto>ӧDn7~7[ O~o3yAϱiKBVHv Q蓸%_8-<,`!-i[O+ҧe冇a=g7[}fyt~ ?-|"!ˆp%#V4T:up hJUaȋƌwitdԠn=1 1-y!}ױwg˹zC? ޾e<ɹBTT(\m_^~K%8[s/J"}2؛ w= ꡴p=O7 IDAT{[Ͽ?8xw^ 7$l2W)- R0x7`x{|O,[A'o?=O~?__{8ooooo~w;!L毱`4 S1z$Tʶ\~+6Sz+*}ﺲl5S0qBxBatl/%-R8Z c<yNABƸشF3Ћsfҭê˛ŖVu4NiDώchѦ<4jºz$:\U<dĹgm)F4@a1Jʡ)I#&[N5G7G6ުqӳbfmZK׉g%A2=:<DTyXkKJoLqOo̕[`߭?O_?g/tOlw}?->}XOO~~ma=>Es8ް}OZ*-fy.a ,ˀ_?>{L+IĠe!= Wwb-6Xi}r|'Ư>coo?z_ '>w ?__Oct4'F@-gH̘gOνseAmOԘ 4b+2T) kA.`Q8Ga+ = ^Lf6W7LkbiKXG~Ni#3UuM5OLwdQiez!ɻKpp+м hhH"ȭqs \4h 0g)bWZi9_͜):2%RV}VH7Xk{ZvkB?ei<& ȗ@ _ƀ?n'?_??;7|~>a0n]dy;󜪮Ls!0%Y& ԓ^[EY$R0e" 3N?D|UuNfKqǧ=޼~eYߝcޙMعxQEh,-0 '$0Ox%.X˗{ ~5nn:BNhq$qVU~oAgIa; ]8w&]PK|}?+w7=|.? 4Ʉgo=>÷?x/_C>qX3 EZhd$r{FO,Id](i)pxD RbpDd̤!=I#>P N0#/68Z2">hzy V+=ɜxej0W1oGze(f#Ո= N2Kk`/YP2p K}'B?ی@$oi΃ BC!<^ݕCNӮVO,51!|cy/Ѧ*Gg~_=y?p4 YeBk4AO{Sǃ{1=;ZM~b[c!&UloS`75gƌQol? ?gN*V)m%4]K]o%=!$n$B̪6B 1֛ &h |`B fU|:9puWg =< 8۽o!<¼ߣcdFWI(>?޾Kȴ2v^lv8] O0~7޷XWo L -GT4++J;D%AytN6cչ8E+)(&L7 iM >w8_Pu}̆st"\Q4"N2IH}q_`ԒkXmU*9mn*۱q둶M9th2oM6 G] ,)%9A|;ґ`fNXɌ͑E[o=:[9g3. ;(^|bY,NaYV2HdNSjo45&Θ9W&S+n[ /05`Fg>z~?+\<8=/m,Y!Mwh]NV)v}Nd݂{+_MxWgw;ݻ|~"{Gxqy =0aY˺ZIQMw VtвfF EaC7?}_P,ƌIDTQI VީS$0 F9D'Z0 Nm"BcZAi "T2)vɧۣmI>$V({XHvN^AF7S΃Ӭb X;<"eHo]/bVqyP +q!ٲ C09`ux(ӔuNw,FBV@Z)a{X j!pOb= g;;qo|27{؈8nu`ǒfJ, UTz<2nrm.ڂ{bƳb˳s m 2͸^o^:cGbGDN6D0ߒicVNӵiљC7IMeA0Z̳1`3S?~ ?'Wx?_8*PL13ŧtO >}>}O޺no_\׿d1vڼCq*vq{Xp{Xu݄yq0 +]X?şfm~Av6LC#4V/n͖ zw'.z $\B`$ƵuNC'8n(ӵKwVGA*&| cY%=Sq̴~na8 9_-_"R<2Ix  f XC"$<p}"s(lwUWWEXQYit J;rM(hBj.awU}~Y|b6u<`*IsQFӸ+ATQҎwH/'i-B ̡AC:-YZϢlZ4|+|SΰN l>GC$ d1uae[-gBC*մ9(pu*ezѓ @ZXЪi ggmƬ/WOOk|ńuڂU:aZi!~9._8bMV7˂I) kz 3p67,k7wܐR`CՌ1@qlO?|u\^ Ϳ má!bkA|t]ԐI $L+^V8[vS}>9 i\}{ 1CgP[׺$P2I$&ag2gc=8ZPCa[dX׭U!Um~jha^]xc]' sB1M$f@JGt,x~%Μ'2 k"n͂@qW :4t+w(ns<[L:Gxp?%~[xلij-^߬ح'?[x罷i[m^nxLPm7 )C︹]vfURHo Yiv!vSy(*P8o=GGï~3|kNxTxj>F ڮ_o7po}2NF: |$vRKjy`ADZޖiq+xڄϏ4FW}_CFH7x/` CU 3Ziwy.@V k L.I9Ls -[^3y)̂jwt*$9Ōa@@П@@IQֲ sd6"eH4<$!M@pFʪVYYp>6M;Lsp1MxKGߺ ūM=,>]pssޮh]-P ܬvbWeE,@7WvӾzqX j 5_[& g}Ž #dd)ZP2 ?_-}6ؗڎ?9 _ k%KV *O쁘n_: eTJBXCP"PDΦSB . T?Ba >:Iq񉙀R$%8t+@2 ǂ >y{O{iӫs\='xsXVع)whm4[ZzJK4|iyBTޞKŢdQP-ӱSXuB9;;4=î5][1~/(Hƺt U`Y;4a2+pX~b7wkn Ze]XU l g3C]Â57KLO_׊7AYsY3oSxԈŸOT{tD7pB@uP`1&cC&An'0n#q˾DzNbؑޣ:"".TE%̣&+d액$@HNQ X[V/ouЫ-$l"1M"J\K!ʵn ~oړKu:SZ > K?U21pb+ zCPQcs\a4[Wt}U}kj8c) @{ >s$m;"Hv)D_=e" g'd(z½hM;&4ѥCu/.>=!{;vr0,c @#tD8?#r}o{pC̜ rc+]S8,L"#ɚc,ڔ0Mg?$v45;3~7aAcyst -od/cnfa45 Lf X{ò:z(n2a< K,%=, 8,E1[I3&*\=?wۏ~_WWu9Zj "=>[ { CbpԮVnc!o,uI??^G 1Xvt?aq: # 4O=tWiutDr— ofJChЮh1TL#aRipsgFk)g htq"+!5:|@X}vZ q>c*M ]{\<(8Jm:1ݻf9fHcٸ5 ؃dA`fYre:rIp%sp#h<*! cYKW$< 2mR0 4h@}P+زwa:83=<`}ZȚPzj~GAn)+#ǁ6v!jjʹ5]!na\ AQaAB-xrZU8TԴFnӲ{g8]pway4aźPSobXmls:cHDz.f4/3 Q}1~vow \w5P{DzL )yϞC>)B0IlDhl8>m]kK IDATI+Bh&{#<#W;Q[W}43Mϱk!~_Ғ(2p[e48﹭ [^AfDz2ҲxKPiP/~mbd,ݩZ`价aF8ޕǜ#(܎: wGVk=a-zly‘k :QaRkaf|\JL0öFz%e+Mkgt{"p շcMvCIQ$0P(|_OL'GOu S (x.G p_5x:#RknVkj }hS46}vn#9LF܁7H(k]2mgd @y0WeinblܦvUH{#uTA -!HR@pU\) X;`)fHњȭjGgSPQ aZ6΀b;bݴc..w^$IViP+ro݂ #fk$@7]#gu:eq hb7kpOAj3Tm/矓D$"S3ȥqA$$xo@Ap [3zڌRԼ#yPՅDH l;pl]$& WLm*j[6tQs.lmPt%X _8ԇ+lKqJkB^;GxYDzL_DK P~?u"ĭ )^WJŽkYo;,8Qjɟ)VLL hYg7>GXvnn;jvynXBq93YwΰC$hV{XYAZV('RꮸORxcffm /&Q֞\x%,Yə: bH_Տ6khjF(dj&+tsXp8yKRA8AdʄnܮZjP#?h zO:&h\=MD!0ѻ~K[q|) /ϲ̹% B ymp- ^k~C b`q Cr2{qHӐ v-[ lssbY/$SSY] >}3(nl|eS+Q[]+Sˠ^0Mo$Ȑ5VgSd&ki=9:BdDҵM?&B0Ѳ~wc_%7ՅM/3|2gm@-)]L jl<0RdT0ȴ7$ĤpM_ ]iF(<*4h%s2c$Md 51!&*ȀNkVMrѾ$N[[5JT4#xy|Ω N٪ mxw'eC@c%Hl7Đ7aE:%ۆ1I, 1#LVt{1. >·3^4s[P7MȠ?TDD˻$zj7)l`̍+p4*HzhFhK>6-$r;Z6Ce#Xճ(RӜ,ꂏ͋ WY6.Z5n1M *e㙍6X>w{G_WUq%.? ~qϰn;ω+.q*21-ô+` n6PqQQ AsRs1qm,}ѧBja\hf7SQ~cߔ-"e6fxdu m`06 [Vk Muh9Xh.vqۆ?pM}:ԾO}m_ɯoT}}T7 £gs8L;Tq)o |?zWӷP0' m73)e0լ?0 sXHYlnZFj(6Jwq`U.HCf%XMwbYy. .jEXWɓg;ԫDjJVeYMb BE+;NpM3?߾no]Ό;2j-u:l!~H#DM ABstX ),*3Ͽ5QL+qJP0NV:qɄ_Gi†?r>CeުM֙pOmk8 v5nƄb[h H ( =2:_VkCv ZqcAC[z3"dX,wѳzw a$o-c \gwtdE?%p\) ;&5da; Qi,SL--QtqpffE~C<~vuu 1ˊr SkhbG49UZ@i"miLJ荸/]\F kDEQl u5}ݫ@qFt"HFehd cY&q3Neen#TI+V)W',a75HG X/[o8` <g6W cz+dwj~J&`frgmXx;/=. \U\;EK0eo3 /j@d9^eBjYHQ VF\]|g(匄Ժ8DNŴt7\`>)i8)ͪ1FTL.;ϺFRDk=EIḱ7~: V[4WV47A&t蘠M!c|͜(JC+?|pfȺZuD@?3;vقAwDꪑ̼Dr+leIZ'1Rifv"%BAކ/ SV+-HiWL7#IտlPLd"YxN6~ym&LNbe J_; bZlc[9afL]] ?+ꋿ͵vud(p LLj@<*Vsfce~Bx}t⅜,=+*W 5Ƙs4[?KH e_8)T?V.h ]"*t2KN |$T/e Y&kd X_U;r&$}m:<A*f+ KԈE3UQ"5fD-qboeC6I\ HH#<9qNmTt)|T U}2Զ1%IrB+e+nti&oQ*ބ2nDS@EHwUJfus  07WM%ACSPY-\jiͯ-"Ԋ*qctHìVjJEY| :.'GgOAћӑÊ&;^t{zԚUx r\DTZAYئnS&b )I]6&7 INݶ\+cPY0&l7ƀxݰM4%^I 7562yYý=>~9yk[L>k"b-@pnZ aZۨ$A( (goY\5+ L|1^0@&xQ-׭CsBK[ZWt3!0-q-ދ9bS8S0>@џ \R_/BcQwNy|>#TaӀ܎k3X3Z I@]EN)*7gE"] 09;u0cҖs|;GF>+Qp2/^jtv4?"Xt m繷5s"0i{){C4j#2*"a'/})iV0K&,mAoE:ZF 3V<:{N:ή{{gy/f*Wɾ+qxAў!4 O X8,Iဟ #r^DHSv3$ !# ԱFU%9au9{HB(rl^XC}~ <ƒ 3~h1FLV\SGpKS5#fL6aQ)` 4#Lg}|v,$N̓wX~qRyNnLj&(ĔDP0=3\G4M'!+yjag@<>{ 8M"x|u{;4]L(i FZҒ\4t8Lm ELx0H U1kԚ;W)Gf:sIe| &}pWtU;'9|yԸE4{q0\6>5SX ͳ*:WW[S\Pw2(׀0}%=d1K|#͗DR n.4H-'>A%7-q[!xD_c8l2z2'9fLH_>hFJ jϮMq`>Jg䢅@#cq#-ᨘf߈ *;tUqo8klEk=/R%./v8Wzxr6f]L5ʴ&V:.PQ\ 6oԢ 0db GH3S6L8"LDCAؑLYkZiV(t˹N&&;c" z>Y ^p3y KƆ rd.!ظO&K?l+Ĭ$2{}3Y~*~DN$}/onq}͂n|u|u77,o9޽g8vY!KC #m. Jθ44Q( qHNtnܬ27g*X6.E@;(_2'aN ng@ަ0,=9e`S%h6(5B6&oDSsc"Fhg`$ܭ6|3"Q.v9>lVw1goPV`t ˞tJlR #-M\S:=Ѳ.E( 2=~чp&'9׿ ~kˋssa,P*, ,X=UH4Pz `sxGB iFObz5EW1"+~wd]XՃ(x6Ρ+ # 0C?FWhn,<ݑ5?0<)#7vv:A:%C IDATk@-3aZ3m0oN&|)a͊A[ }b>`j뢅ɀҽ,R eyQ[ᐁO 5 .¬僸8A A\J`T9>ʋDTy*&E2FM V f}?-y\9$t;Ê 榠sN}6_(g8HL^Ãsp'B ϑ ޼G.#ny!S{C!C·Kb `" [H af|Zª9 Kx2a\LJ$rGW 4J E%e\MRӧBCI 1Ϗ"nGwv_| }Lg;KRh ESx77D00M3V]|C7/KZ85ԠA9. ,^D尕tN"n]AX<$SO.Qi瀺_A6й_WHbAaF2CxeSEs+e^FtT54 if2A6{'}hӶ#:nRS9;ܣRY,?[@G &X#UVTl:`AǁGVՃfNw/'?}$ ȁ10cfnנ wL΂o!VժwEnҨ 3] @ Evw"V%ύh Q#BA$RF6vI eOtLjUՏl諽ˀb0jievhb<.񓂅} &OR wDbnOk BY@urہ݌y\{>+I*SI$6g$ Y͊.pX쎄ⳔiiEmϑN 1v-f)t}ZJm52szF<&xfЯ5$e%0zxFk#}KT2vϣa\T9abpӰb=8*bM HžCb@enZuۏ}Y K()spT>fW`s}vUϼvNdCn\цx-8BlY6X=`/w 0GN5cU$q6 Q/ "Blx|g;4A=:J(;@p#xCi 2iz P;OIZڱzڭ_,@ @Mhn pGMT23i3I*QiHJN ?KcEu(2 vT-)cxRrm#ƔЌTWS1͠5ŢW,^/1]2 DbD 0Y~J;<`U8:s/)S֤JBIb&wRSwM3X Pcrw5b \ e33ǘrNlH5Ԙ$7@m|0b@! AFAL6 g*wM/ A;1O3>LNFSߵ E_Lm B!)K4qSRdϺaq'g=? #|$iZ |oe+;彾YRG [X6PH|q,d-HfN3&xvv7㳏{ж Ur`AWX9  D4c(4 FSE be" p([DI`%r#xe<>HDڵԐ1@\ΘH*<2جJ P BH.=-p~R"05载ɉIDBS?˨js(Wᓏ❿緘P9p*:5Hafg\An+MJ"1,#Ƀt$U!ݚU9"\rڋBRZjX - &S3튏/i}^ s1aռ7x!d hB0+eYMS3lrJ3)2AaAX@ 4w!axHE' syE؎6^ОqMXW+E߯AWwxdJ7q"J1+3݈PcY\ƃU`tXMnD0i ;<~8C[?4J<*⌣R5' Z=qJm5\’+,vLi Wl-dXT6 #GulrhGj ։Y8[]ZYIs Kai tġ%#,՚p bݨJ=J o#)\ߜ  _?Aq tkc5'{Re$-8yz ۘOd4d 2 cVu;.%^i>o@:_U pPqќtbw3@TT"¸鯦[W5dp~d ,MEۥ怾arʰұ:0mEiX Uqsv5#hI~ӟSiN22*0`\JsWH4<m<3VuUGH6sP" ʠ8`mXŎ(d*Y($p3ZMKDўRiIξ.&P8?Fkg- : |FIFMSNU\A fd.(SK2 MPR!3){F ǩskpRP:|,-i_c_Ϩl0n#DHcBZftT$wuȼ@٥Vfi0\Cπ!k`GVZ'kԶ||;140xF%J?GqG3ab/B@)laŽj!gH "! DQv$X.O~'ϟa>cPv,+‚FOtɏT15EÂfC)Y('bά6@Rbe٘%c)xY?٨G 0?k 01[݄T|~j@M6ŀ'J83#s}VE&f0X{%2zS(=-t9V؋t(V2 [|( ViX, ’v~*g0,k`rXXyyfDI^R#Mh ;!1LEm/( %ƒXM^SE`G d-`#W N NlK(z\[iw+3ʜvqї?zUa€ ͂SV #%}QB1^7 T 5ƽ$w 8G?rpe l6ߩ`Uwc}ʌv~{dd%嗻ckie #Y~o@d30r/ɹkJr)0Z`8~6:Q!VQD+;ߟbAh.`2%>q}܁#;ґwc {eaeF~Mu/wxFT: ~X)UyG^cYGvu,P)}M ky ؇vC;tTD{X||j v8-h܄OV;>\ހ N,TGl310x.&+1CuQ5)beh  !DyB3R@Jb$9@6K3)jy~| wU$<G@f*C'xr_oWs7NNYgq0|>fBu'n>.[_1[wk̵Ыˢ (%"̵L%,/2kØ7:g-P۷d V=VZIa+ ,5pdC~\1ibUZxF3-.fq i:1t,*zŘuzZvY0ƱC9ʾ؁FţYkduЅbƋ|+kTƬHO˒{@.gRFz=*_[6㈴nF><ـ`/HzW'R Uu˂1NiTg[7ۗ!E%u{Fv؝'; qYnfAe5MOi;,ȲUh! S8ܨ pBōRnY`No$cQF%WJ @\ۑ7l"wo%9g)c1@BCH!S 4R ֵ[O£'"úg+wx1@3fqi )jm$(y#Bs>Q~9I,NJflh{X r:[*xA#^CY,Oo,Lj!3$%Mkm6@va]ڦZzwFjTUڰtŴN(0IIH&@֎(,dبDLDlg=@ݐׇ%e2Spx%Hye!UMI3cNg 29y*읛D܃Ѓڥ2FJgX>Z];B̰,02w/o/[,ZOj&nqY ;=7pl3qGjOpכV 4Pqq6UeILed(\1N ^p=8R-maڪ",ꦍ1t+&.1 da,[;EpZQH"%& D}} ^x k]Dd+vaP& P²SKKG,#)`oX50LDO5*'(aiߪQX8:Abkiv|[l^$R+L;g}M.{83wUqg-;٫au/_k_ФaeDZv7' [[pMFo$ŗC齻"x2XGf:|9h?+<$$(hPwfd^("M<7nuf7D*&v ??W݋~A`*Ne{F4 Z&s$QZ7Z8j?4qxYk9ߺn7p2X'L4Y٤ 3 x}&9":^O[ugtγY1om.-KrkX'a-^ĆYi#KF8WTaOiU/:Rb+FKQ\&{Ry`c9n $ :,ݤuHta-ĭ! 3\Z_ $/ 0)pQ8ʈcBR_XŮO޿{~$JCHPzpbتCChTE+äCBCh/|\ Vp%Ё0;Ӳ)x:hJKfqK'ٶb%bɫVCaVD'P℥5l8cƕ>|t3Oq;fQX@#<|GOQR<Ï&|5 ^MA[^ְ* G`b5`a6;(Pb:vnRȭLD K)}_sOZ{]VYSR 0~)&SFQ+dU7T0΃^`ňeTU+F5>qyBi! uc2qe)H[?}p'Y+48k;\qe8l'*U4Jr&tؕXUKcu:`~qZ#m@V2m`K|ҸzZegY\(J唏>'./|:.o(n~u$ޢ,-z!)eAKnn«:ᗯ>4uT&t,@_ϰZX* tjo  e*Au7fLV*4~APk3IfB}P?Rѱ.{82jIny]"S)!>h*y 0a!,TԿd}~ evQLiSUN Ƅ2U"n鱙lɠ.ZíJ-(R zoh-v@%+W:v3[.o.?|s/(<V3ӿ.HFMX`Tdg3)xZM8B`f '+I 0E(-@zX(?) IDATX.x%,z+4tc #R\TmKDcU矅=3wT,H71h375&"ɸؕzH$+ ҡ3!^'yjڢ::7cZf1.AU)3 ^KqԵ o{ڨBwP;`kYMv9>o瑬;^8| 1Y4D1x$n(M)wSO?縣_u_ osG"RHN+* ѳKxq~ͭA+ FlvyxtP2&>!`sOyYE\ i:BuW/xc'!I{tysܾ\\=οuPX@`N!)O#ΝamGy/%&Eš^flMC'KkDiԿ3Mp4QuKxM[ة=_rpK0TxSdW2K 9[FT-{ӚY_ZP(=+^u(lM R@MHyN@_5{@{8|_;?.MR c#V0Ɏ,$/m4F5c3)*1-JZ\+ڿn}`L`ۜI{#t0  $2m' '. $J2'!hV!~*m>*|PE GU"ܕk͸IP0p|34,­9Hmum l 5ǜ2u^ZH_`듸haҵ-y,(nhѩBIG3;"80qs_opOe3ajgW;u{n{`\iEmhT*hrpJ{C% 1CVF(H鎉=F %.ڭ; )= uGF5:m̱ȟuςd4İ\orKWfǪ L!i5w}MXk;Q #`8 EІ.1zXlo1LcY&h?\-I9o#"dŀ*A.vS[|4*l}'ix`410Nad8\b q0άnL xab?e=1sHz~FGABt%d:2Z m(F״-arfc<;J5"ԯ`bRIb% 8f u Jfm7!B^s`H*cAQV wky|[Ys7a8$, qkHo5,4k%UUZbxXJ)(Z$Qjfuh|K}NĚkeb쀟_n5ũ dAM vpBA spxqWB003BLiP(@jJ{uLdaE`2[p Fx$$k8aC*>k`J\{ӎX->>ڨNBgt:pE:הj[/R| m0dzHYM|"6ÇmthqxBYV |$YҺ_Mwu1Ar47G% hr:JB{/F| H`ÚXr<^D襣k\Ul1m(tQxt1-\>~x37u30 :/~po @K6) #AX]i*2IYx֐uմ@Z"%6ܔ5,*xpD`"76NtO9yHc4ݑddMR{aʴ_ٞO(E4< cy? cZq15FO HL̛ ]O8͠U* jg8gAb?Fl¢.;0'%A6 w]9/R&WT j?;T 3+i06`sn1KZE1C-f$2#IyjEq^]1߲REΝa2F!o /gS, p Xc:GhNH 6'mSeE?8eWG{ 6dʄ=In9"V@uJF);.aZ&c'l_nfy#c#]sf ?ŏˋspp}},Nږ@ A% %XzGS׀bW1 fѐQ.ٗKlEcDד7BELȣL[Y C8)0'_h=7jhy9IfyPoDqo[TD+Rs"[O2adwD(KET JZaE<<`[&{PGGsw\όƠU%ȰT2,2wbWz4C{ɪp**=8 ~jB]vE?]jGSwTp8):< g1) *xnk2N"iv[D5_Cj\FDFC* $t_ '0bxfDiA׏z p _8zfSJTY167Z\~.صR B&_ %@C ^{ܵ %}J穀:@4 p5\ zfw?([xPҚdxXLX 3Υ3~uELˆH)eEkb֪|HW9S :+p,)9XАL} mv@Tio%LLN):U#j:!۱ݲ}b&ȶuBxrs~{ktȘ`L7= fUv|]PIy%<bzZ_ BJ[gV-K)dDT02o G!S`)$ۖd`#mߝƓR+kIY2#9).Jq0g\i!%kk(W|c!{oGE@b4v' -)hyۜ2KJqpIU,(A~m=s±J J" FPݠ=[F c'Z!rbm*G J}W1Ԍ1C 3t(bs T펁{x4Գ񮆖y2axqO)5?Iic"s"1O1o2(NX qAM&&Nu"ZQ+54NъX*Z6f3b qdLS?{~_ko'3I#͞ZkX0 RJz+/~w?] \ê&XZe9lU {y]1YϢAwE#=>p[l\gGZft 7Cq]o@gV7vG]enZ}vާ2bR݂xSѐztQF?< &]NRJV Pa7we8 D+5oq/ry#ipr0'gȖ?]2iR^M⃒m1U+sM\DFĹq+΄}븽kxv-k")hj=GЖEG(|8HnaM7P={֘kKPw;(&]\?r? \K?HSYѵ3`9Y?t`y[36!M+9m13 ϻT-gA= <=m"@ZMv- v\?¿>w|8tAw2`ӡbUiCW >*[X5W@$VԘ` e\igO;Q\)\4Mbx1f73wqy|LU`g JbB&i+GD_e9&L%nL;4mJ>ӶHF66tS AG="5 bgVWaB+߸YS-Yq%lZAXz5׆ [jY{hK 1Wi A3R=bT`;nof\Nq~A#̶*nDbwƻ-^x"̄vL7s,[-4%$ %3WL 3CQ.tD҃&5Ł.uG.Oc-o32X6Ѭ1(,/>~f##WJ\ى<2&K}{A\`&x`7<4, % n_ow8 FUJ!vđ+;M;^dh{"h9z]f we VdѻM9ß~\P[qEДIDex! :{"[ J0%N7 nk]/]M(o)h=B!Y ȪL=" 6X*xT܍dI=3LKiah1Qqo0m̃BqV+]6,'=qAP|N|k޻E nt[lEL8-Mi!s I)P~rF;5YPeƖ:]~x.a0o6^Jsi,.HLLu޼w {}y1o(ʓfƎ!@;uIp13u֝[)rvmHAxPzXvLBƌ2_78ʜ;w:pڇ03,q8Gx~oV4JAAZ"RC&bu0E9bslaAIx[:n~Z&!c߾|Co޽7/"3D `n]OGkaJ m6>y+cv<fa{]wӝY3 v[D5%[h}ǪIMZ{g 2$xZYQ`JA" #֗#dwr` +/fmA11^ĩ1cdCfϊ,4Bm RЩRG 2tgc`mi8,a=^fyA'ܷ{Eץtd 2r+SR@:d]WNnݬuPo^gZBEC3?3+'ƘHӚyJw LY!_Kg:& ktIpDbkS3 Xܔ۝>YgX"ColXh~m ’Mw\wE i8[8%ڲJ+dҼ͚[79FzOa}׭styC)HuMj4d,$q'O'n&w'BXX u30Z_Ж%qL.*cbʸ E.@lQ<7;Ă(=o@?R Ҫ&Dr}͸ƚK4Bnך%'aI_cʊBҟq<ٙ^Vc]bP"x*բ3 8UnM3Jtc4j6-ձG)y>m ޾#e|f9v!3z tF?nZhX#CcikfNpܗ&OZ h2_O6auV}}poC. ^|@5iQh8(0outgnb2 UfX \0!q@0Sw+)P1Gx!zMc=E'(vwSWsZ Acd̋q,K9էE5"#eMRPJxc_-s177m`[ =3Uĕ7>1ŐPe̕Pcn.SƢD,VYD(P""cщZ_rP,blna;C  %xD6j& R̵5Xd4!h=ov++@ ZU B>>KE^ IDATEdj-Nz2}Ф; d٩)s^*ׅ7b'JFkށބf l7;\]2|wu_žݫ]]q"-"i)q#)gGk( mXM[ARo9- ӌT/r֮s,!u_";_PB0e8#9QMo.KwY8AѾY+㈚*S 9E&i@h~m4_g$d&XsѐGU_5'2T,f{=kʢlԒY*-_q 1'UIz$ otT$EgHypXa94,zJ ]Z QX6Pua50&ቊjRm>bf`$![Χ(َ;Pp'0JEj3֪b4P%mAV;,UuӠ23ZD=#Q(؎}FdmY`Y3I{8[0{' L}% 5DU+I:YP;Xv.Y*vWg1N<{IJi}5F劅@]y=+*5{e3I/Ak@`h]iYp|KcU0Zp"0- ^\"tZuQd@1PGh+:*:xI$ ;@]NjN˂97U6H‚E,,LZkj N~ cYD ZKČ9uU jv=ڎ6~XX 0))R"V U*OCv\ M+VK-ԓR[(NP$+'Uގ&gF$u?*n a05,d:9􍿉%ٽw߄$B7쎅= W]y%JVN6ţm gUī/`'%h)IYvo~ A u -p(*a;WyFnp`VnAp5d U(&R J^ L$+Y]wDUێ _ 0i6u 0U8NS:|~yG30&w)!):v6kULbȃ[cEʊwj:j.Ó OU ,`ֿxEZZGql}p.$̸~t;TXgߙ+ (}ah'K)0ƠEу$\2~N&YhT.(hD[pك7 *SaZK߀{=3a! \ (JY)lqB6Pk*CcrF˜2rD[sQaC>ٯw;6;vg{fMGk"_}՝QL )e~i6x"$TxХܔ~숓u Tb@Y{#o)BX6KE-qܶ)@?4]S'lf8t G)yTEQ(JN+q}q&X"*ԟVo;o~7xluԲ~ixw^Nh\C[ w8엁9 O#)6bdesqoQǎ#Iůui-mTJ`Fgc.?"+OȔ&xr%s;d8LACG8_EO9d_#؎BE8Uֹ%fZۈq πTY0blybƫ7A9It@͖&ɶ oY)b32\77ړjC 3r8'즊m, =6sm㰿}+k,=켫⊄UP0ae1*xJg Q*qpwfC޳@֯šo "LC)Ąyz&CB6nAg5= c}3!NM1АVɑ  Bc‹C/~ T,Q ƛ{bjݵ=1m=Z݇f'Ia%cPSlA FTPA,jc9z5W͎B%3uiH7j ", iA < >zpdM1tw<% HI99?t)rw(tWR" [.ٔ8=M]~,Dz{ :wo۹Y2ݖpu^)]?teQA=\YݫZVD!լcUG)p%pF2/#XR!pF;~ _]=sܾ{_g ܾ{eC%͟_xxaj5Ƈ"e=n[?aFPz{&,VCd4Ѭ (#}M3D[OIç49^`;yƸJy>[ȏj\V/n9cRux 3>V amGe,s1kd::sn+m=Zϐ9 2F/ ގC2MOZ(sJZ/[/^5e2XX&G3w e7K`94`=xLDE@)MFgbg&6f !#WCAQKh62ә-?3;r`Mf;XPCPK}X) k +/p.nϮ%O ;lc]G Fݵ ɀ5qa*v1*u\l .6-2aa`n'9ڜ5)IENU3JTM\[7)^%[Hh@ֳSS5Ď 5_os\]Ll6eFϾ yT)c˳s<?ytt\Ê%@3[g*EQ(X;:.=|VA g4%ZB,RH+?杦ѢlDZ YMV;n!4"flۇ" N (m2x)m~|L(Y5<67La0S#}bWAoVə=w=|v}f 7 =K4KIKQ>Fνu>wK3Pt$=n  ꭃ[҄-bRŇʃ{}М&eە*l$X|KzP D!g@RACybWJÉ+* } Mѐ2-V|xq"@vin{ZAAiL~;uDAXx+ўDFwd3~능vjS?zK0(JQ_'fm2EuN<O.q_]AKW}tC@c۾BJXf\6w2?ŝUTX$жwx|O/O0M/}o*6BaZAgWv=͒05̚ 8r4CBt" +=F'C@Tf ij+F: z5tZA N \)zom6;iV)V;c)ib<'.S0,VS8iN](|ٱ!=d\ +$wk7}w9dm[Cd@Ia3g0HXUV( }1*L&hN8o~wKCcLhq/hM둳ܸ-b h95hZeZ*JP* j`U20_< p;-p2AxA)$I)?DkEgTMWBǎW[<5fm3  >6W ܌ #޻3VO0MPa|<[|xm"T>3vix}G8,#pE1^h{#tӮc.?<̀ҏd+^+Ɔip5㇟m;OWbj"[pdԦv 5eSȔ)ϭO=IT7=|G%S\(bRkz'߼%a!:mLfJq[iD|ȂE"O]2kN\2Kigΰ+Ky9êrpAɣ̈o6Eǭ]p2"1ɼE/HXTchc(Q˷ ߽kxsׁ M1 )@Kz-NM͹#}aDP- )1Ah af,6ZB%.Tn]0yNw&M;À"sY`0Ap9V69:p5"{]vstE'3s+TW$mf{=\[]fpR= Ml-p-L Bͮ9~c*i߼:]ǻ]/XbB+ |ſ]!n*cV}QwS7Gn|Un) OK@Zg/^1DIU%}ɢ%%ϞÜ٨bZl ؄ؓm$:.L1MVKrdG+RÞTptq49,$L^4r N \"K@AB%"swB,ZJ(bÓmGR?7q?k\^^;_~ } ΠwYQ5[a`x!2u̥ń݄ۆ-JO/;>{]PȂB:3E0T73HutFnnf<:CΨ*w.XxBpE8]yL[f ۵X+Ry1:q1SZTM&-v]}Y2xO1QERb&sa1!5ݬ^_q(T dlIa23o 5DzjiI|eO\fP;FѨZ[VFQV`iW J8O6PЙqI! YSmŻFXct|Bbt os[RZk{'x ^ ?.!v]v6M)P=Gw MZx;ŒԽɼ>_ C}Bhql&Z-,¸Qnl%--d b !Lqo5ZOa$-osjNg&0̂\]+U Menn.\bP*)޳Ose5^zoݗo0S5K*<.<0,+ǯ(訅gOo fmͦv'?O]J {0qCk "eU^Lhl6ÏŻoKqRzD %  7"godg8mZA6e "l壕gU.#Sh-7gX0sd)`` ?҃^t*Ûs1<ᖎBeia%֘͟^\1qe8J. H;LQT: LW2h5>x9+tis8W[)~eCc]G[?[4.w7R h dZAp  ;}C407xB#3V =7DPyt(@КwWè8bϦۄpxTJ4C vq m;Nw~(ܻ P+b`yM 4PH{G *C7%E~k)l&lZ 7l''gwo~e_p[t* 5A tT*c; pyQ;vljnG_O>f3tBwg+" y>{ف˟{j;,\AP!eG;4!: 3dzH}ca72 [Z)u آ[MZHKןOr-IF295u^Ig1.i7f, {VZ[*=)`bin\3˔),ގCښHnӿhĒ r5o̭AA&&WA"0* T^w?߾|2\-/yZT<`FRE N!t ?%?bf1h{t4;SB-UB.`H0JBJ fHn<It'VA4@o-9g= sJBc_y x 954" p{ F¦L`3UMFhK|=<߼k0R8tz+q!\θ.w3f긾`wVQ+Pv3 ?=vdWDE3КDF!9en8| /7}ph҈+KƬ$Ұ=vrBotᙋ*YN /nS"A2-uGxoBdBaȝȽnM0Yp1;'2%\,f1'ApY##dŠ{z'd`x1€-}E%ݙ<[rO+VF0R[ ZȃӚ s *<6s`[,maogp ́ ˰aINm\q-+7S܄|r_C ` rn=l @: ”:춋aX?1fS~6 i@4̼<<98z_Nr4 Ca|mCLr" t P2cb̗1;)6bapiDksEӔPoGw_nquuP^@hm:W\*J!^6k7F/@݆j;\UlbP*6fh 1ANS%1fjE<96=7l73>=/kvxBh[vDG+kʂ4q&](gH3RDSt dM(|ÄFVVG;'Džڱ;n /(KIo&/ӹ UtD#,,>?-H Y++44ˁ𮈤q@Ly`vxxJve4;c|)Lk-Mh'ki\̙XO9'L^ADŽ-~{xo rT!QhHE {Z\S3g$ IDATf#Q"<тw8U1sN k`DaPj ܾ}76R8oWkb*"`å1w D1 a)!YCL> G%f82{QrrYVZ~_s"ú3t2!fLXZvFJ |xOvP+~͋[|[x`i@!ٌ'&\*635\ S<[WߢWR /9[0'] ]ijwh2 R*"i0?5(Y@c><#ia5ߘbqϾC*,NT`h{iJ^Օ2]@qQ >#~.$BIZzCBbŊ1zGܭٕ#3&KwՈy>:?1̦~ĒZ1zubRF\vb$IlW7[̛ K'̛̈́I lhTTJK1 Fg:ٚq$_ DPE ;X^D*y?,ұ8ټؒ,+,fB|IV#ϙјbf| CkgxznSP t|Sp6=^L23a*SR@: 4eÂ/Jx ]0oJ!QV`ۭnG`Cb|~fDދz}}yw"g&ѵiݫ*WIWՑRsN93 :(mlV|ym8 ̭,Ώ_"+=C Ãt|feʆ.LhoDJŧbyWQ0y{!}eϝFL_̊`9|Rc 76_Q9 Y=?J@wemN.w)}ԙYq>yvc&P1UYa; rok-`E2[lɩf Lj2@ưʁ~g!)`+MXy(Nfku=7Gx7G!JQp5HXq5 ao05 A&]cdqyQa⺰>Е kMRsd3S<,7uZb/;c+댊w O.'-xwdc^Atiom7ArڶXzN}^-:qͧ#k'3sZ)7eVYEf: n \8o iW7P "m zh g|%=9ٶHI;*Z%eiii:D8+1LB\+!06l{f3{Kc8*D0 L8#y#:04> $Wq8ӕ%ߘtPI&Lo55FA+as,.=&FjsYU|('{`ڷr6fxm6'ja=IFla^Ւ@EvѰXZE-\_bb3I$>O˷[>P%1u +J]9R> jd3vL2&vO 9xI $P!J ٓY]@[+lBqqk an&f#C67SVrQ y;9e6NS>sgElkij:EX˵wlP 7{ V>uA]R 2C6FEM!(ILՠ *(ؖ 2宝5%E2 , u5V! >apPn֘#b13ixCO 0ư{It/'XNin'xvdE&!E#MVXU;r&} ?}b.ZKdM7IL`i &}.f|eq}E ^}϶WliCuLΘ'Brn/5J!,s3ePG/} F>x,"1?5}k,D f?Ř]E*VI&cF^RdX/{wƏo:x,ë%cO0`Ǹ~.z݌IyQ:OTSR|".N-%'Wnk3g`Cf'8Ɋm<1Aи > 0 !wg*G OA\YyI|$,T 3<>vs`ʁNgtVr 0!g)1 #2Ef dV+¾хs:'ﱯSH>9LjZj=CkK})Y2pq y*ֽ(^&T*x:0cڿ皀O d\> ?<^f]麾qep؎RlQOc{cfl)%v-sľ)BpX󶂩 +4{ckii1_RLJD?wxH 6GdaMH;w,'M̀ r3qwy' Zhq+R&LkT*'10[wZ),"1rsY :)߳Ai091/ژ c/ Jp5"jZ@w$=y ciB wK]uAI zk6A"[ ydzqRAa@cIՄdlt400Վ?Won_K@,#s?kG>p a 7b^@DI9COaUyBoH]Ws]j󌺙6  gX:q"gnC~cFy!&\m5c8ƒa,A$??t5`]Nˬz<ԇĻyW'rH{?WH,i_|<#uAk%tz &yP;e }% +4$2&9ɉa(]x?Kl+Iq3 IZs#F~#V4*#,`ƨVnЏ3l0COǰF2/%JLiT$%ۺJ0U"dş/T1"vE$I@-Ɓ;$zݮXځ3ujQ{*z[kAiyaoaihd5Lb7K.xvW-)PQ:e8!3',rtFu='Z6K-!]TX]tK@6S+^ b!k%m,a v6[M)k%<.opBxl]͂:J]XwO2 {! }&7-\4gTg3[<~|G*u(Y{6!1kkLZ{îۈ8tB#t,1HX<.9F ({#|8U=^Oy2wǻu=bl)1]JB$hSn3AG(!ߍWKP|jGɍ}2k%dGSwy`vF'3CsSy>]hQG  VYHvJX{͎Ls"PxWt:Vn(픝W9~N\VuH]$kp2mwV*^ ׋Q1>d H >&߲? =Yگ*$T4m  a"gn2a;o/g>ÆWzwy(e 0~,SIĵXg 0C}* }_񍵴oҠdг+8^y fBgUpctLeo$Xp"LDjRӆA=u%"#,ibklbKao+v*J w*"u4+ƚe 瘤>B12( Pը w#') OF6pwnwT^gZ-2#~E C7s`bxK &T#L,B=ňy^H'=h3$f ["ykl]ő8KG4ǝ-jf<?%j#RI8.!t*Y"JE?><(ҁȆ6)1VEj񚥯c"_I5m+B'?|\X*\ǛLS> | xq2pm7 mZ2CNUmcZ09&`,8fFFCܢݰhy)0Q(Qo6RaF4(\1bNT$CY\М%ijǡV#1t &5&~9g7ZݢCF9 H8*=yaf'8ަPqEACDF ueuHAP*6",ZL Rv(E% ᷱ/\Ɔˆ6cwN0'` G?z?y0FvL'1eB502;-ߺ/ PQW2:<*>iЫ"o9o s({F 2 L-UK՛23 KmNn3r@xyHjUi;B@W6 y86Q ,|H2..&-@oA 8$>^N śÛ l/Ì1aۿby X--oa+k(Jczt0;cK]&[x;Xpvcmg=7[Z C-dQ -qCl&+i<= k~ d1>B4֜`^*vOe^)&.!$\FZP>k9Cl 7Uݜx((:c((t`hh a$2|s(t:hJMb+HEgD5|sޫsS@(εd;jGqmiO fOժ01*}9*"Gi ]% -[ UKeda\ҏGhUﳏ:#CsvǡG p{'A$>Qۗ y;_.lE6gdz~eJpߩ{X eL Dţm 1Yp)>IȂ y1u~0x4Q#T4piVgt`'0D:RK#[ ST(X.(auJ(j:taV'.hAr1)Uv,,7DU١9G(Ux$8M6PL yƘr^( SPPZ?ltoxy>~'_P\|*6,ƪ8ILUh:]F^uY Ձ$b!`o^|%ڿ; &f7Xۤ#\S; t>FX[k*0Fp4|u%v'HC-6!j::qٮ4ulԆbwX̒X7_# !_pMU ]d-rxP$!\ ԁhnEG c6Y;8T< jÕE1(ZI//g^B3oB$&zxbFNK %'R4N¡8$m*Kр5I/E iu| 6Ԡ4Bz;q bN.Quq;0y3t2r$.3"*F嚰RLʘY#7.wMr 6^n MPUEiǻGil ~w;қ@1!gIRc||%s΅Of"^tQ/ W F]7Rm)p@z^/XIm}=DQֱ\V%iyHNMJ([.ea  !H3S^td9t|bdR HNt?uGg?y{<<\]K\N ('YRQR3 3f*Qax}c ,ef"\4TJ , |b_fX,J&ܳaZ2g CEs4ý[(늃opdj0\ez]naϓCQ";T'b[ƜUW֐u-7Cр}hʒJy5MT!%HE}w+6dxķtWucm)L{cbW%;|_|{0<]ۜ9)U쀵51JBGp0[ے2FX(;wUۺ ksb$&}1L jNk|a1܍I&sʆɛAPiǧRS5$Q,8C 5|)yab@R_-p&b[ÿQl-@4^/?].\pwa~p%fio$.ĵy2fҠ&n_ # o30 IaLb zA]O/(0,=f0BPě? E FXlk$&b`Yδ$ïi8X{Jr:N(&p@N<(S .ʴ\4IO&vU$~Ȑq*uK{a"~8ɶ. Ori*B'K]c?ʭߜv'x¿}wxkɁ7/]T&?8@g緢dPL*,l2 Z m\6N>pإ" ~c?$r[7WG0`^ɾcLow,thJd񽪷l /vCk3>)i5tmJ>`/\y!,!bCoQ*z4 *XEvTԳv“nGz(!9;v3$W@9lI P P83Ds:$@b) pox)+Hd2 w+ % W& ގ%=굱3K$50>{d(d-xvsƤ44C#@)"vr?fhZ1(9W!0wgx8evԺ( :7vڣ'?xō6iq Yi.C , ,0۵[HxPV6# qvJYhF`{HiB eZ4v9 $# l sBs.p&F 2#C;Ϝ$r 9նlK񳼿yz$CdPW#>*})s&#Dv G',3;Urww9C%X|UЌ$Čcqg8 +l>V+FnkQC+)l ҍC942̭/AGi\^6>lHvbjxt L>?1A{ S| ŞXlLj1ģn:X|y-QBrFx&Ks, |FCU(8U\g[#%JG#B#DF8U{b.aA9aT!(DTk*WS:Bks3 fa R(emӡ{{e4!NF 7 m 0T--D 9/Gw?)hQdczȊDN̖:+LQ, ke|y!8J;iy(¯щZIxҮ5N\!ȶp(!w_۫w <}cFs*[Pڜ@z%>r3~DqENקN_ފ*|`sU&4.bvv M=b`K"!lOs,.voX+- g-'(VqV`Q2EU#N"aS0Qv9A<pMYn2HqM|e}D:BU7uSw-(#L0j<>f$#i4l;pv;39-Gi.̏P(z we1NE6鐝+3 ]𬠮 u-#A'_f7T(GBV^փm{C7$g)S5+gհB'VtڕD9S(豀&3WGmMYΗ]33v7S;N_=քˤA_.w6^8:n[ @r؈2'(D6`0jȧU{} @01 sy~|*YۑF=5u7,:>nlEv6>fW &݁edm+[~ 9+Yvb wq~ׇ.[IوU-OeF|dsA'ڍ*VYs&arKROM]H A9'gϰTՉ{b[?q{rq:s)ϕ'9M 1hD Z&g̗[F3+e ?8w>{[-h/eV8q:u{XiX@<.֪W;Z[mv5VZj%Ka/ pET) l.;Д9EUr-\G<fv?*vBy-5jt#9in`uœ|eB$)DOz.Q4 {B]ŰGۀ;Q̧GiGÓ!ca})TPUW#> րIJKo On #R1ȧ3I4mgPjU) dɚ 9Kf3jYrYdTq!;SscԨ;k$`y$1Qqa$R‘0N'#Im::3O5:K BK^KnGufR 95 ۃx0YY JOpE{ITlZf'=.Yf{x,Ţr = 9_ kuރ `aŶ ^\.xkzqx .`Fh;)=+r0T©b zRU~O:틇|tw'{)[;#/Fe Zu,ωr*ubgFC7kس3֟~݅ P7jY&^)aɮſo gGM̺`i]M!^$YiQ ίCP p16ϒpMaE"N+112ϒP۩D9}՜+C'AN>8\RP %47i19<*Ţ2o^='?/~s|44vX646 ZYmŀ1lxzsj3!a(!]cO #-ހ`ÉKGS #i3x뗿/3pV[$hyC$W}raItl{)Un '!!쉣e01ekOfi[TV-3?F*,i[!ls?Ph1H{ZEϿG9 |85 %\Bl#(akt5bB2d^.CUj" S1>rl:l4W)ŶW,|ťLe g{])z=թ^CWAp:'_цZu Sc5aMG. WERzBc7[/xuC;xtj@ ńРİ$m@PŐ3J.Ե3y%/d!]f<~/wkqM,:UQժ*sTےUl'3F8ϢQ 6:!*NiK ʹ N)y ]ZfEUЯ_)҂[%3|N~-bY7hru 6ߍZFBn)jDѪJU%b̴⽙M/+[8sQ_yfEa@ wS~Ot (Kfq8@%}:ASsjPw(؃V R8յ*-FF UBJ;ʕM`:s6,!YNͳ5&2쏏W<^w<튻 ?ǿ}OPUW7۩>ù `yo|6۟:;A!8%_,eGa!)ьw`zk:-3qdާE_ 02IT-1e5A\~[w yH @9ID_P>^;v5TSlK;\Xs;]-&Rj#}{UݝyE P[t-2N`' Ӗ) QWջ'Q"?eD؅εHzVZ6[RI]k5ciŢۆ>}W9!;*ʰORH=h+.˷ed _ۑ;ar w䎅N{Ntv;{p|Ј%֐A0]uD`m w-#qFH:W8R]P%:J;?dU֒"W(`fr?0Ze${uhf*<"Vަ4}yV1֒S&l"Lզ-%óUهv.ih IaUϮQmgELr+`gPy&Wl%m2jR]dΈNxt&KҐEίBJ<𢡊q\+Ug{9 1aV|`yS Eݴ:,(k 3&eԪ\JC g(eY;}4'7fif}W|x>#?Ǘx|ãzv.pf{dH37Hc3)GF15D9IgBdnjM~#P2> a}a@Rt,wMvEdygk ݘp$D^BP5R^E6 #zZl[HOE40vu8o IqQ57>>0|\',Z]{.9Mo SC &mw^()JţQhG^V)\;81CIKPнR۹p$ܔdnޥNd}lJLYʔN'0 *G1umԀ6ؤ<(T^x`rAQZ:!5X3| _PH>+t'?+{{~7_>Î<&(&##U^Jc",Y0 R'>NLyTF^tH=2`?)+ mRa=rjQ^J+M|F|E+ eq,40LA+DҪЅ,cP#·䣃oE(E4YL_T߫M2Cc̡- OlF[ \X~Ev5YQ}v=5%*Ew'~/*:'iQT :{(0;T̝JIh7&0A$<#%A6RΩZS)Tvou0KE/nl;hiATKE>P x0j--7OLxQXCKRJB7)vV+|Pԙ3ISPm F%⡼kU_c- q OiZ,Y\7!~,z_o7;7>U|W?_/b^ԋ ρ(r䎣KyaNb̼ape|:댘]b@5 E%%Ư +|xv"3n|ai #Cmul#G\~C>rU<`T b bX/fnP\"R1J\0/pZ\Qkږ{Z\zh͠RRWF.C =4hlwȦ~LPP4\Bv[/3LHcKQ訆G(܃X@>컭Ω9s:]4S2rILAk\z|{;PfgEIj<‘\w`߿^|xUg_l^?~51KeU]irf88<ѱ:2 bjpe!4n"dX^). 'τ 3M'wT4\ǘs0S}Vh9BLa&qCx9#_EmǯoE9opolWpƈp׶;#Lڭwz$\:iآ~.$<Q4d^Ux{F65r#y>␦cQ3JC"([:t(sϳceAWhZ&M3S7A6D &VIǠRLe /dK\Vka\|%Ҡ҉*q]O iή 0e =_Μ_7%aFE¶-ʵp}9 T?PwrB Qq̐RD43d^K< i5>/8)w9&$N؉&NEljc' Վ@~X!8sYǽK^Zq 0$,MhήK4ΨZG׎a!9AfH$E Y}aVBǢʕBI1 *i %kvZҨFd*p b9aߧ' CVQ,D;=)Pz tݸ8>ryjc'C'^2m? b<8ONpa$ t 7d%g g}'zx9H\UYu1=]kμ쒑&r,ёà×Է]^*j Oxa&oXƥs+lƳO;qDpl[8Ҳ kV='O\}ڶ.plBf> T$ g$֢;J~Qhs ms36.ò/ m(WY =++=e*DV ɏqHUơnU~BcF6j:ڧv8 $;|/Ro+'IX|KLGa~kj(-/+D 9FQr`i ="A6Ӿp*#xp}㡺f[#Z"Ccy `Nbj޻9(8 Xh$hx.Bk!ǽ[U9:kI5"S /3"3-SPvB)>:̾݇sQWEЭu'g$$ [qMr[w;A=cMhx,IU[#Χe(E%!}DשQ~lr 0w`X3 &MZ ~Rb!R;P^T:d j=Tڡ>T<;>\OzbmB:뎻 6pw+9:QMD\ E%Bu(N22JT^.A$y3:) `uIL)" Zf븎EA`L_g,܆> 6toUo6Wag׾ WN(՞͚]7P4fUUBE*:pmyɜ+ ӟ~o_~ءba*TTL0!r5=F,1xu]:[#*mϧ ;^\RŸ ֠H=]\P#I(mCpO jnHKJ-H84LV/9$TNx兀߅Imlmr+u:cG(2-+ ?Lp'5g cq[挩+W:]4?>?{ P|FGEGO(ڸg6bx|-5=!UєRVGKKe͢*Ѭ) 3-,|x/̐#Bq{މO~=~WsOO xv|5Uki#ڕv,c:ljtd~_KwXk+oe +BFnE5^6<ۙ16!ЖfwT1j|rX: g6)[,^Q e;9 M$}ې<.#Q _{ EV \p)FB8LfH ۞78۬z(.>pN=j Jd@|_B%=JV:xPa2ĝancW d?;qx&`@>mhA *Ȥ2^߇&'}_H wZVw84@ðJۈ1.Q ΪaSB u9PKkoI{[j#鲁lHQX!`C)pOWQ qܱ <{o"omxx/_N6|GLL]2% `l;U}y(|f钾X! 'AtudRs wj[D899"U8;FT㟓NpIqWK*9 } mSiHY~*5e[V$}e愽;"e#,IdtӖ30HPks̰T}aH/6 ; ؾb3~=:&S4a93bֆ&.~Og^l՞o}}QWF&uǀ>zruDG/9׏{)x:0AӶO76wOn(\e :D{"#/-lDw <),VTmbױq?!KP06y(~ \FS$mk>D@T!v\;&68T;Я|YX8/tJ$QFb'6ʨ@Yq# uN`tͪUi9&pJTy?CL,oPiÎ l~qIS:9{n[0œ3޵PmKgnKѱּ;([a{D#ᜄA@ ̒/@MW!FF9]/'?2sLp K{àM fۂ}zf(јvWY.$Z;z 8wZ1ZQornXX`*Juxi]:p\> ^~-s~{ſ_/Q|S.6bz D;eV&3MmB2^rsV "y$ >ӀW.XE:n{& :7pX#0w#Ȯ=T |"Q:l*elzݗBf?D[G5FhB=AAÙW=i2w\uz(Q;d@gdV0B x`!tsxѣle劁w2X)O8LެS1PuniB 9mW:9Q%(2PPWiT44Pi[ pTVu9kP0![ew*>64o].W* TʉP?8H31e'n |^QM'GT&v^uOpU;_?{|)~?~1QG*mHJQdZxԲ @^ۣKC?dg9h᷅iҐk5"YK,^U!1m"B8-|%hh#U7+SIQዮ9it<\grv r4?"EPxzRSPU1JbVdeJov6vg322~ z8 )Yh>ٙvz>4j`yIa/>!m4IbǐBwԁ*=9w W NOH00ZN;@7U͋=e.U<=y{%-6H CjܔlA,VV-<ԒGǏ{1|duZ@"nw  /Hgd2ыNt Z o@a˖fWq^x`lvQ߮O}Ó\=n] G#&gO-41(u H,~"%DhΠ C9 ʧ :2Ԭ& ѥ҂r S$gi)qX#"tRځGKwlDzijz6ZzByve븠7%.\-9#NIf DnnSvLz\HnS[&-T4 kp!sZdC;c:TmX&-h0=1dFP!4 s Ӯ~2gɰv#_E_٬FB %l<ǧ031`$y, qcPF%T{/V)”E:DZ_k1A1Rҁ**R rauK?U|"18~9eڣN d}S֢l':dzW"P&1zXBbQ ~uN<|3po7VOa7|/wۿ7If9̘cW'% 9:eTtTV#6q# [*2<!ຜ?i=L686 `c%f5," 00 c,\^F=>aɤ9:$E= }Nq%,5%pB:Q<H0 \~ cմ7=ኅSC',>z@F&dzb\DNY)eu tyɐbEڱP^vc$6CXH1&$)ʚ_pƞ޶1p>qmMf7!66X01ƀ*ǧBDg5܎\iJ2YtpM:0Aw'Fm1҈a)5"Y8+ki\q,~dqCFnXqpPm,Tv+ёzz -O7sl6-- ~J+ @QNvW̔X+5p=IW`6(q6oIBN-.Ke݌[пd4oϺ0Ǿz_k;/o^OH62Zr WP+gϱB`E {Î.9Na_ _}71Ϡ"a4tNucrdx~&, Ċ.?q_Ff/O<?c9BE]91c 9KŒ_HR2U'ԈsRt@MRJ8/ӌpH2GDX"<+cx3k/)nak<__L:g#h^ώLcJrx_ygt- xyk0+#Qk2>2k݉-oߌnYyK?ҌWh]푗^5ʰM!srr߬_~|t[aV2xy*%d[ɼv2ՠ-u*z+YVp@k/N?ftZ92ފ{E'Yk&y| @ 8,jflOz3+gh!ޢyyoوJ0,ieSlQ+S{r; Ɋ\>+ ]\P1ۄA~^{12PF)${Fc[`%[@/|1y9}%UҰsn49KtT:,3YB¨e O5~ob\!Z%O֣ PQך8tRNFӱaߖ`c|exȿ"Ntl:v.ZTft6Nx ϯiF-:߯W&Cwk}:jH1|ͱP'ΕOq ):ω>ǃoay~Gof=K3ȡgEMb )Z/}s >N㄄QƌwL(%-,.2rV>KOhE`;\%veF]kѶvb^5N\z8P-:O\&ںC{k#HԬ~_.2nҼhC2,AF9 tY/14U—80C(kȥ ]aE']>٨2F+o_K0/,p'nCҺVE2=Y&xw \蜒?GU-2PwkGպ;WyAVY`e)nO]eTEoFfi7="ΑgkQ `yg9"$pw\.oj:}Պ\9G//7ysqg~o=0|¶+zPW˾ ObqKbfʍFӓ5#eJNk/JM28qswQΐ?GSx8c{NU i"lx&0­Wdd#e~׀ղ>O*bVhF}<**@:(#ǽ]a =Ð6F%t`;|%+|ف#ayFA8'iHQtB@鹁)n5]OQ^8bi.ڋu'zb7U8<gcs` Io7aȓ4pRetg9^S =rxPZ%p5RܗpGm`X{&|ØMGa,d?2Aw5~|;FAS_]j%W|±h 810kG&1jx^nѰ6_pħ]"_VZs 4rn.[s†;҈zN*ȬtrV~hܑYQ<)=h&4KPzZO6|°C`oNi faHF8nʼ[ʹ)mS EEU[=B#a@_ ; pEM-%jza ?A25hzV%bRt$83v 0[GT} Q!i W`2Q{94Ƽw p,` Ƌw{,Ivg++8#.7i5[SGl}$V OK̀<7impl<'OGa\5kMe6dZou;E|,]˘<7+/y|eݷx^Dnk;+ јwC8]Jn(#&++WQ/x]]B*UWsۀ'rvǮPSZiEՙ⬏NeC9g֣#*.!GƄ1# 7@ 2 ,P{/b^L IPQWb]!H`I=h}:)Rev_\BjTrx=c;$KҰTN .< d)IYn>$#i,P6Zgʱ6ɼuUGv3({L.A$gݰ.閉:ƌ^Deb֞+iFW3*ć9ݠ@m+7'Gぷo(UUl kXpq sey6 C˩R:S:1zHȷI'ęyź k[ :э}.[I\ZRb0z -6=Jgξ&cӫ+: % 5L,?X-+`W- \~aGvڔjܖb_O/@E B{9\Ԗa}_(6,PEV^m HNG&磣R#,@ i 5HQRmIUe2y!MCUߠBY|qϣ2m+|sHRLT؂IFv1rme:/g8 rj;.+kUHUfL Z\{csM{t=ROiwӪ##QJC"!\e) =AS8wj*ZU`gIpc : ,f7 Lf0:.Om?aՃt6nH)ƬX+\/iA&*kT*q2g! 莄BmɒZ;5{[rwmO]i]I6Zhv\-LA0&0+1o ><0?t0y~l/}*"N p'H*3)ND:yy:]q!'sƒ:-t u6:iB2b=̙ꀀKmT)Y}d[;P+7M ^+e}jѾaW4;+qln"~2y >$-̰ ;'k4xmXRSB u&ةd" #0+3dIr;78] NsOEI(%7wd%X0c+;I3._yϵتLׄa@Y޸32*栆Ψ .Y`]7&0$Gm9\bN;IBwt3NJ\ _=G%&_S1ܽ4kuĘ|cyֲ{~× =TM%F厮X yP} w3ay9xVp66@y5h܄QV\aX3<6JAf)hzI㭸!KAZӁg* 쏒q Ά`Re_y>݋eU*H ANgbFz(46 x7 Q[qZ{y)AC~6j$'(q!{WaqS4wxǻf}@K47ϱZ.5qh9vrӌDGR]ooHăFh}ɘXYm:ks'⻯_~#^OLi):d*fZ'$WB`j,Ly.bckv8|%-ΞоF7BC';Iީa2ͼ Q ]\з-|gζz 0es'5[mzcR~eE4KiTT͎=ZTud >a)PVOh;xG*zj*k1&~wݻeHy>"VCJÛJص+׬=dį@š(̛NraHDž AyaZv)3Xl,q&b1CWG4c]\ؼvz~wzVBvvkN>rFFM[okҗ ӉO60|aۄNrU/\1U3Gb]#d}Nt7hPycG{`X)ۈzGTu 6%6&{j!a Q9G8-<yv˚PF=9#nJ}sjo|%Qnpzkf.棜㕋7\QNGJlh >/y?(2L YhCcHZQ=Uz GmBPӹREHHUe5VHe/I~ ;RqN^35<82X!A1'LPMF2%M#x,N,JD$4{4+2Sn J!t>>0>4ɏܳrtM(J?: .Q"wHcڨF7 .}eBl'ns΁n쮮g׵Upi?u|-> 8֊6px5R _{Sfrylf`XgOW귲q_rH;/jΈVwus7 C}jw B B'$˳f]`^9E y8Pp)[]Zaժj;$C6/C& *m)$ޓƬە&{EqMI!;6'Q贆GB(SHpEIs1ލY(sAsU*Fgg=El[("!QA6hV1Q&M'a~ з:'Hr&älGnu"$^:mSz%ΪB7^M6P,B>ckmQdH8vۥ2]q+9pxZur`a10Jx<8?ax'pRJ&0r:l0_NNL%XLv;ϺVe+;)OKʜgmֳ[ٟ^1 J#Rjm͊a9=CD}C2a Ur` F t(nnP/qu2J9@B-k/07iE֌cW5jcԡQQ:GX|pQ'0%%Iw=.GDn T{* #Aߘ`3cs_ h aggr ĪQ {ԡe" srR`{9[9r݁H]yV4 cxo~=Gxµ6 ?Exp@/#cS~T]G2s9{%5:f[mW`F.M -ddžZo tɊ*)ՠXXUaSA 'wܶqRS%ĴOE .w$B\a7!Dz29Vigm_fwfՉWc]B&Ug;YDmx"0ijϛ`\W:d7؊ԛQЁ`RvG3|=[[\A`3KܵlbN70J.cBQ WE{{vahA40**<&?I9,e vZ 4:b6j1;VQMP9޹kQm0ET6 y]#oZvQ!\N^!aeTeLT:P={CWTr""nvN=ʇHm%i]}A<-!dcix>O?`v`7˟w>0c +T':<}rŗ;&,8 {.;_Q%+9Gbkta@u$ l!yE|RhF{@ld+.}N'.55|1?Y˖bBTBUZB& G0[KT7?#I}%-d;UPf[R+ h˃N ]ZIxtgAPVdųVna%pԘR9_7x`MZ4^RGHkIpVxRY$G%'z!GI?<ן޺f$8]uNHbnr[i 36;<9ϧc^ c$G1f}:97c.?\GY+ 踑v/}]a e3Ĭ$MkcE3k8ejϨkEQ̖3)Gw""0Li '!H NL>KXڼ|Q ZRY6M75F4[uZKRO2,{D M_yA2] 㾅|q"~޼8vUT)OH&||ˮUx~325ÊtD0_.my~y!@ٔyS6Qf/&ןJhT /sh>dgooeê]v)[zTڗ'TY>q>O0ps/'ax\r{8q1gp<$5xx8V6i'2 ;ȄD7Ha+1ZiD>ypҧ=d´;I# R7ϟuEJSr3hd>Ky"{KToe*9Pgw\k+UiErsYV(=%K늯~"Usj^WU}Z=iQ<i" /8`0#:QG(@91A,n]5_Ց?b䭶7Q^RQ2qtqeKx|Τfoԩrt`/ w7FtEd8kXs|Ί3|;o4y/_=8G~??^cm0 362d3i k?n0 IDAT*d8f`)İb;CZ̵CEYk8 (c/y7#.y]AWYP kt}3 oO65%2DX*# wX]Y|{f[|/]Ɇ 2JƒvFeԩ|##v`r ^ż%Fz,Q-?/^ D- +um%Hd*8l{hYpzg0xe:鉯>nW O/~[ g߬=- 3>8_ <xN6{?많*=w},;=1#.UG:YtGVw-# 5 EE$?X'zf݀n—!.*ttL,{vMtr90%VxW~|pfc-u T7sH,L o0ؘti+ $e,_ʁg\ hycw굶a>Vh>~:>41?ab__?:7W3~?/~_ 'GoooďN||pwךT>ORɍ7A@3])+-C^I mWeɨC-sHH<z9 KQ)ZzU*~Z Vϧ0cNоk Z6ho.NDhmAm n;U_yr55S#Ha\iu9wM8\)"{ Ok%5MsAEf͖NSP#k| Ug-C|3Lp PdԷ-*B2%׽ݯL`BsSnNӪ[ѡU<ˉG|4<'|8FkUqb!c~5_s?????`~z$ fQ́@s5CqC%cX,cl+9Vk>oH :h%[X4\t.림[G%UCĨS 5y~B86BXNc*uJ6V)(V«5T֒b (lnL :1^`>!SL>IWIKJn1ř)}R r?υy8` >Xs5x<0ZS_(<Ɓ:[;|/->|-ni8gws%%f!4h/M;i\Xk⮛:sGX x]7ZGX"V ׫D?Sr؇qnNF|*P}׵?wHicR}!v>wbh63HKQe9!Y H{<3=kB)!' 'ũJ]WdlIGfwtH;s%̣m/!;Zִ]iJEte\ؚW潆9 +2nɃ2U(=RW-j+*2}R61>HLلd$7#:2%e𗣹⽔d4_`eÞ^?~sÛ7#~ xXD^/ ~?o>ochvҦW$ʰ63#\t7y:‘7c#/Xʿ6I nf7m~#t 0c]RBSsΨnCoZ{5"AtFRcy:9zJT`}BT>.kkfqZg񬶬y~Qw/A])I݃k]iJ</$b;.ZPjDuve{џJas9O8 I0n\xbLl BjsF9חxa[lץ|-Yy}TY-ޕDF4*'Mkt?6g2%Q^"JǬh9xF`hxtv\tx-+'a2f7sg?R*(pLmK5>ql@D P3~0yll0&rg)2ts@3j\I+  s:+A$_Hto+>yQYf}oS2;G oL,^8CsTO mIT# #]G\[Z0mg.SjE.XFY Sٮ LWʥG*#WpT#,z)w/ Y=yesisTS^s,S] S2͐ +Ӳo=b_8J 2Nr9^Ήx$j->?xiuDJi4Py6̑8,38x:_?w CROZgȝɕfUtZХ`3ðV% | Ƚck[ agD%mx4{a?꽦nz}Dкh1/a˃օ$8L  8 S-1¨+tƀv9y_  :ct?HtthG/Tvk{z|56 pYN~sS7ü}Nz|0mn ֿ:pPJ Q -dm2xI]h/*,p=NQcKM]ygDxM-<&B:Ix%gMf|j$8('{|AWneAFy}ܑK8 fkGOax:/į||9?UK;5<֢ESMҘ_ ځkѮ>O@c8uxvYosO= $|%6._ Өֵ6^:Q>JFvq=$V)YX=T5M74Ni~dFbH$d+10CBw%:N>`1,ލt'l,W_ Jeꑛ~pF%PvVQ uVB Tmu˳y[k1Zy>O|-3/x79y B7l cx?oׁARь4< ylM_i#4=8(yalc_v[lVq׃O_]3_vaSR輡dE}DLIذ9V+.}U5kaVy04<ݑ{tYx\" Osm.!#gʌi6ָ`DN=NOZa(9(ٶ7H#<Ț ]i4;CaSFuwڡ\ԩGp}؜yd+Tʈ^ ,p]uYiUSGq,,ToIZ @/m)ϢTr#Z{AV8ݫ H0ĕHZhX!֭ƿ]zjɽ֒y t9Ⱥٍt"+M91m<s8+bp='~3}.㜎CG9q.~j [:fe&I2 b`B/jLszcsKgGզK,8Yф"OVK9[ضaPb/ՌgmVE; k@\{3 lVŦfsTa@BC5dRL`rp_4B,Ft:BB IDAT*&T2d'x6RJO٭36F1nqqV~꺳W]x+iG\!v3|^z{rXȅ1F-N4z=IQ Oߕ%1cX`HV@9=z# z$$[snc` c)4 vDy۹9ΜlF^z)u> ȸ)4X)خ[;S>o`z{\uk)|r ˑ[ڤq C]kݦeܷҶwFMc[DGI C?E|sPVƿ_.JP{$O9uW%amj<(yՉJ??ZHsv91cN19 9 |W4xe@nStL^k` lv>ڑ`#hg" X'27"kL/TGd %v:Li 3#W/P4RzFshƛV S[&1j]Ӧq<|y~8N| Vdц518ǒvկ6w2p_lg,'v"$0ls%6J`Cp ۳]m@}\浾a9)o7t|p~R4 v!t)bd{I*{( Rqh6& AWdAsU"~:Wx K$%mw?lIra2fc)<;UXsb@ZU̼d ݭl nW\#2e;B>/2|Li!K aR$ظ!_hA^洤]Ϯ8x(+2x1rtXQ⾼JKA}-JóޜBH3etn"U_/JTFbL%o)Mlէ!F9۰z >|OO?O+wmXQ5P`U͉w >PJR;_U(13mEhQ c)8$nP{Dbs@[RK&!QF}sb2kkIka(qpɈ V!v1H&Gx o~|wâaX{zVpe5(.g:-asn{i @TW]f>ضn_EtGI@{eʠD~;lȶƧ-Nn_ֳe.#mAn(ÆK|S{&U?hhS_1l:ѣܷ v '夂ma6a뛤mVrjWȑYE #7c9I|qcAnqV/T/;VmJ*_|2>aZ&Q6ʃNH0;.GF̸o?_p۠5ٛ G[/W:ZIJ0k#W>ԢtT'Kga\ѱrY2S3ЮKj M/CqL+;˔'*DN 8nCClX!cEcs (Vc_AǚlUA "\Z߬K";I dįuT?{_;O[!ڧ*2-<ͱo?wyrPymϘ^[A&Qkdd&u !a57UxO7%W\1}bzvTq)32>ʍ{U.Gӣ(^^o/"ϺP0 xow??}z`ƲD =zpRnCE"J[41𣳟F7XqmgD).IP>@;o9FoACV2NianҢ<4JX6J-6M¯Tm/r/v4dԅyK^ݷ~G|y[*2`L6NTz %xh0}{^Z$ !c 9Sp|ېk/I! °RjNs9K@_8{nSHQWU_UmII4ZX*KIe`؛s29káy湋HxPiޤ׶%9">זT<DxB{BhaO=CALiD5FŶZVU{2<ɵ}zxZ_DFFfh!V ?ҙ@*I徛@Eu..DZECsU@y5ȷ (x_4(W\8S.E hA}p/cH~i7ҝz!t%;؆# x0?em4lӶ e3jvoppm9G4ᐋZ~ wS|p@Z~)}6S%M: A+ ҹڐ" %I-U ThYyo[%4֕%VXcH hVHD˳^n%k +q >NBRrd1<\g겻;N 5!> GO?J rWS:^2fk:+t `J=TB8@z̝e4 Z~x׺N!`bd':R+p D!#噑Ir; zڅ a4fXٞ;a4ұo Hw8bQ6: 2SϷJá,[Gu|NzBZ!YO݋4lRӅ:JY~.xf·N LiOuaU>"UU!f@l)3y;=r *8N jFD}|4)4$Hee#'GϖV5 L3+2a )Ee7 BuJuvi\9BIrmhQ/dI[(b,]''`y!>Nh4ð[d1%WI"Tv K_Z<;ɹՖd2 RRLqHG5ToJڪSIiWU8{!Iޮt =򴄄1@EvSgggaoTw@p (,'Kw< ŸJ$3AMGܫ |u=yPSt*d~wgH.%6pSƱ+/sA'vP㳎:0$$IuG+E|. mO}=AR"1f}nR6[0,+/u?V!C#ԁLӓD8yWg^bQѸO/2C E5QȕB7H[;WIDhA!)907$ONx* A)lVL^jhO¢]6(shलDfts&dw[qmL.?/K[1I HYh4$mw^O 5ĎYGwJb5UGzTϡPͷ"[6Pze0u(hn}hYdmW-`4[߿zīG IDAT$C(X`֪cGT8AJhdIS~mb qkYV[}epbTU`u~bUΏ(ciى"4y[*wMόkRW9Q?h\[L>BXXnhZtAKA_#yJ 7JM!~](퇂V4.{=v3+@Wp@ץhcbS\Y`m؆`ys?{_]1=nU/x<@}Ѐxek"$">ՠ1?`F$(= bŶpb0^̤([Ja=GYdEYRדּM+u=DR)/0mL $!!}NuW.pww7n%yG^i3W AVxASSnS3dv̙ +Vs6X̗JHJ^'X!FX\PVuĿ\(Y/wM̰-!\E )>UqRCȤMg B*Rm nm}tšʮDNj:dL XF{GH>3,'$ U3 8UJRiI<&4kY bKľZ.TP0gN%GQps#7͆#^>O_ŝ9`ryI#N0q(~_j+F]aV]Ûļ< qՄm])nRL{DyU棯efÂ+UDy*_4rD =grVf.S?)ίBz]v Yl7bn-DthR9'2K76 J7-6}.8fsMH&C€i_Sx(m v 㵔TE :7_bTzʌTxgIi-RQX[݆֗`]b 8إU5xЦAm[@Ry2B-JĞ>Ǩ |$8b:;\FX't ]u! r)x:s\cSl` em \6?qn.?< `_|qϾxć=?}5P T-}ZzzMg^3w_h'+*XR~55)?R|N~YVl1}D&ijnjB$jh53򔩎aai Er ׮vF#CHPT C?#޷Ye[]GBt:i#ׂ&e&Ũwhț JvaH]!Q9ξ Q3۳ֶJih92\LMy:ZuIQ*rj_7˗;{|_?/ɷvU4^kl})TG3`GHFP\6sO'4f2ua;'='#,(8|DwSL9m,p%~Å ۛXv ד'Щ[^UFuܪ?/KhFa*K'z8#ZsU /9 EB_k-w@ h{/|KE{u'08-m4)AkW'3h 6< Z_N *Qؘv+$9Q~K./鯠LaJotu` C9mzJY_ӁW,$~oo9~:2w"ow?wS|w*\'RzQsEMJ84D%Z 9Np\k_`Bn!B;Z[6/O:mV>)r}MUTŎ`, ٨|.N]:<J;U%_!#*Qېwe%k@@d#cٚg%q`;K:), <;X.=#5wlC|Fo^@/AdA;>bItFGHq~ 0۽酦j4P|eqvƄ sqJv^/=Igc :6E;Ęڪ-c;tɜ҉AiOK6mGa t q^^ӮP_)uϬ<4`Eu2r}hĻjwFA%eR/NBJ^ eHX}VĖkiDF\W^ &HŻoA9EI`m*gH8¥$$%bZ@,\lY_~ sIz K9COȳ䊎0 VKmV Y=$x @ni:nENr$zQ۶c`mS p0͖#$a|$갖8;"ݩ>3I^өg10Q( C5d਍ClHK84u8B\w8"F_#x$DV>4:]% j5,e՜o>4'O̫dϞng /wrGҩɅVbgPOS@tZ:-z|=M:L [*d+gGY~i@On6)j`  yaJ#BDl$!3y(p0vZ?ј A{! V7H\IԌ0R&2I$+ st~zt ϜƬ8 w`ƕf*Hb«@~YĂ3d\} +W|Mc lhA[VvUk_u(c:YYDQUJdF ߮Q WlXVU)lӲJN sNJwa{)K&u`_ U~2Q)U+XRfSww/omt@)iA3N  iIWT`dUPkfC8R<87[ %mB hðj$z0\HQ,K98sx\: PvD|VF|2V]F8(U(qsTNHQRni[El$s1b2!MBF.q>uȬctޮ nBeDqe;PBbsh'$tVw:ͣs8m S:}ld!UVmβ`K{8F.ݟk $\!:(VǡD 7x\u/2:a /_~v;_}V^@)qlrMi1Jhsk.:-y_G88},mXx󥗦_#s@.Rk/E('}SxYfUQuHCDk'6e:iWQtf0=lIns:vTCt)j-6nj-M|NDV=Z Q×s:|e$=_BI+pPE l6y`;cOO_GXx}ۀm3&p}T|~BK2tebhQ/A H9mη\^b}KHMTH S{AhcQ򴷾j):|jwqHYoЋx9>ڰ'lHR|aT#)|Y"iF)kzY:Yn8^wsQkqK/j/H!)4׹ǛBW夬*2bOk A׬%V֭SBX9䔹$8$*(+S05He9U__i>P%@jT=~4_f}2h5U^6#"OM9 Nl'LEqzЮeHe'ue'd1-<%L:k #2|l}w.^dAA0,ax~g?M? F!8EkFO:$)F߅53t#^zwѮ?ΠC@^e|i{Цx]gX#hRQO ,6g("Uc2y)[l^?p.?HxmBY>G݃!瞡Fi #xP9^ZYUp[R&8wVٙ&i{ ^/o ziƠKYjm$uF7&ޯ >.W%h}͌rt笔QRwS̴MeMpwܜ[cVm׵`C2r|gY 98]RoYYJc 5ϩIcEBf~;wet1w#~'O>]lBzX }'ۢ$d Zq6T'(Mu< EmjbYn!~Nճߪ_,;1ҖARz+m:}VfuI_Fhk[[ TC󛘒Wgzz-QuYF@sZbFW~hpQ}Z6['1+k v`,( LfIE~ӫ ϫjNjՙ ~IXQ&cE" PI$ Uq?0 10ىeGdc#kU5l<$-Zd`vj=!n4Tֆzu9`*FFlϒGWyD ڒRͰ~xiiUZ ^>;H.825jZ߭#ugfRMfY(` %x{L]z}@e:c9׳x+t_fD_X?gԳ!ӹqwC"1881ڻi *?KVcG:zB/<5IkYNN%'^{+ yJgg??g?}]Ccw28kTl Fv aҩ1MBgt1JYO6LT!}V1m0%::+U9"0eo uKHޏHr?tDH&kH %l[օ?3 W>J@̑'=9gW7gF *0wYNžO/6lS!^d(h9)<SWOek/"5~&&-cHk啦%dQ5K>J#cH9tH〔6/coWwe`l:r-tLltGUbt~L#~`:i6lA4`;Ő!T_Ĭ%61Wydeڃ Sz5sVZFOX Bzsj>Fx?` q{P`*5`Q6!Kia54#<7 pѯe}M\;kk)hs(E` ap>3,CoimO(_g׆LD5,,ZYI>)},-%˹8"v@[z2*PȜ*7ϙ;&s#-NT`lR4pMDܔАouXD{XZW,9嬜>pZI;5^#-6~f*!8q@ :26l 26=Bu@A(6;UߪGy6V*طG@Sl% 4!ۗNz?D' L4 ֑,7n"ј¼,OX{hBmHKCw%hAo! vkBrNʦŒt }diܛsR|5$zYAi&vjcpbd.DrSɛ/E9;=i |QZl F5Tj9ɿB?,>WC J_oW<_nNm~cEM. )uW0e:X/Vl׮i ;PkP.j 9CHB`u=;AfQe=PcA.2BIWfպɾ9+IRVY?Y/~HIsc6ߣ~l;ISM) v+&\w{wk1c*)S]0b)WO@_u{gO!l:8ji؃s,`9'bvYl8Т>3Lۯ<5 ǡR,tˆsH|ʍB5X7LBE e?8|>6)>O= 4IN$R8Oy.糱R4| 'MmK4QCi| 逕1jt7Zʩ;7*f3 fZ=L81s:年US^"ktB%t{2(TѐҮWAxxfI3]G*H7Ț[iw&Ǡ18O[-9 ?'ͥ-w1\Ν' ?*֑ԧ<M}G8PuhQ@SDxEAOXWtpo ץ`s*派 aE8w5ZZ 'H̩ͤxw)x>MLz86Cė~ A_x6Us4OȓdvP6` bG|szܿSI.(7'> !x-KQ 6WKуbwy \.ww|x?G?~G kXhj8|&&VaecYꬎۯbxy lv؄ [_F I xqԸzh_Y"f9Hl5T+lm1?l@k~#h ۰QLu8`y<6{%h!VZ4Ѩ?[E~{ =uHτΠf?Goð(5N[ W wĚz +KBn7VbVY yK>T,2¤8^ G$;83fr}*T=DofN<6~-s'%m4(ye͈k(@ّh :pl"S.4Y@9b:'6pS" "!1:LJo}trəڇ< h lT<4ҫᐠPiNEV( 9%,Mw-):2 4i(t$end fmcnx daDM:g\J%~ݡq@`a~y%k}1y"=|O9z 1|&p<;#tYAh4lk'>;朥l&!APƊR2aIS%k@@[7bB!^WzTN+ tcgRRVQpa͹QЈ j?<2p#тf~ؽr:b|:SO!u. P ".r5 !Jt\8AGƆm  ;^_pmI@r 7"jٙQ oD0Řf_Ca _h#cb}26F7Ј* C<^u c[AK/7\p\p{s#6~/ww;'o~s ܉bW@IB`/ӇFn鰴DphDW`~b8FbS'cS!0ּTr9% !3c@pP#>&΀z66jNCg{蚠 {XZ)Aՙp줘7bZ^>{Q$Q9%HA Sw.# p%g,T @[7㞺 r}8ө`%=T;-l~8Qp7#st "cdXE2 +Qr,K& .ըhl:b?2b^7)h%@2"wQɃuh_b* fpGTpP7^&!˞g[TGI䯬ƚ@Rµs\Pղ:zEYvK4'_$r4Z A]F{_GqH`KO~2>pb8n9|t-ZmzEĥ̒(8Wm1rtJ+x5F sNM'tJ .eO~NθG.άt%;?F13 -0x(5yPH\1PJ5)+"qڨ__|o V"2acC !$b!s,-^体KD2"gML1{lJ/Oqee*-vt&Nَ>㨟/fcP< @:v{g9 H"2g}LˮBN_)Rq2rFhN^Ǯ@]"h8OSc幑oFfh C=MJ}W?mdu+% t8i+6;6#A~:=m|(t^AͳG9BGXh'ʹAp) 9!:~0 `Ip B BL:`5WR Rr\z* Zܴ^sb{ g|#j_H"e]R\T!ܠ^EX2 d D3s$۲ٚ7lU1o1! uYFyi'𜠝! 9]qՀyBFgю]1M.GL,A.FdL0(]0!t_ 9NB!;sSqe`3zTf(LGEv~FN wT:;M&!'|7'=0lz5AcNCfB鲚YlrU@􊗟>ğ~r\ :ވЍT?3|J611Z(ɂz61vtDӰ<6cW̨dV USŖIʾ;ApA u 7~:F$,X9%uI_H9!+L:.yGNiخ:3~`(0.soKjľO}7ާ>>fa GP )F?#t:v5oSv[vn;8;:'pV5c|/.j{Bªj(d{/32$G}\5t ]ۼ\%*|- G`\}M>ؠNi(p y?.~÷?sBz~9XD?y^E Mާyo{THlk_T Lpi  #Cmm EmbqicdVZuV/chqN;V*,u~'"JvOj;6g_|[+?"Cꟑ0Fw¬:]v=uT9tDHJ? 8;<8RaNK:{) BBQy`f;HGhR_tP4>='1z5{ e-7:IՑ허XX1v0 yZt%Dy JCѠ]'͏P p/]4 Ý.}6Oׂ9|cs#R>KU*[+5S!xm.lY(Ep>OOw|?ݼDnPhzp|#&2,'` 5d : !573:wer*i),#?F=Gh4hxzXmȕ[lmjpf-ZsjsZs ũzc#^|OWN~ݿ滷1ꈢ3ĺf0tRjFi 2}y&sW9ZϭLM%WIœ:{B$ai{7Z먫*B6# X(7G7KVtwp Ws6"-c%cKbPA6y5@ziGBHNXz+PxT^* rʿܨ lU"SMfw<;Q<_|&^ %hُN?ȌW4ӗ<:mF|(W|0Wri狼M1A,n7hnXaVy]n6 evxvsl>LWğ{-|ņOpMmS+s9m @b‚]DgBۺEt&!ٞb.x6gZ& "<d:_袑c\(]^`$nB*,,*GlCce38y Ͷe^P(Z8%}9*reǺ nkw3,!o}뵰dmdC3B/pA׽ZgZVDhՂBUosщɔjͤݴrf1L0Yq6ް+zj7xvЉ5!T(/; M(+)OcpX&bSaP$Fq}*Ħ9{[bKtM#wƪڼsue-Z%im1=KsY%*g+6L7IBƚu' `* \.9Ə?W|1/xLד:$S|O%GM6 s"ivg#7OiQ+wۇ8;u%tQ)pOؕF:+|"n'AM`ͥ6|9ѐx>~ >x_0u@tBEm vdo0l :1: ugݨa8Mg %}` W^H2׭QI]H s@{g2/4isWLOC+f kz!AқpOG5 ԖJ45".0* ّ\6x3|Y M ĒKMW_B@TBE@Uq/܌\X ֈJ DN5YqQ SfKlOi FpQȍ8L:BQfZIhXMZ %* .d,\L ($ i[FcƦ]ϫAjp:Ô DU98=,(#Q&~Kl_q vPqK.u֯I=Uxx{g_#!U-GDh q#!b 69b3hv\fOAe>CSC[#YEX]\qz r^ @dCጉv1EO`uqXQ0mpT-ϭ C\vK*M,}@!f/lJ(8iF*j.NDwnyD9| +'YgrsMO^G;5 6W?zLȖKW=t|ǝlSn,lYMjML"k_y "9|X+WsƑGg2YulLPw;7C ih@́ m}/w&-{ e8 wb[2L^im /Z)Bޚn[˭ Jc*֢Sد#\__y~xݟ}{J:*,P<O:= 1[jH@uz!׭a6<|GPW k$4HC}NƺzB_\!o1paf@1@l_=A=[@6̀*>m_&5朵Dߑ[!OJ1*IȷSb{c  ^L{ C%4܆gnZz$hh*Se)%Ya)s4rjwUi*وJ#-Nw|(|R]ǚ!(tjl"'|Xo\*jd1D䓓es-5!fQ]Z< IL l^_Ճ|akPy,rY /~ůrExA?~SDD'VE/p h[qI6LV;I[FgAhIP*g i+99"#Am^ m𔑓[R !!:KZ jFVk|OhoO@M6,drӠw@T`ީ4Ga؜rQdqM  FjB.tXo*υz?0ь :RZI)/\Tuavw(*}C,nooqsszk?'}72%{kX}]YB-J7UfNHvQri|td) f~Qf aj$YD),)0% yX“Z:`b*W$CϼޔZGRFPްLi &rkUnJcuH8 779߅ FOWVC'B( B~|2:˚^{.I0[\SR4(q=Be Eк['{hӡNk[H(r Nd$@qa^HzG8wWغ1B^"DHS4*ڀ摦asZ~AsZ~ϗN.P1KCUofI|Nq /N^GH]niW "0B⼍Z~q*&_HuB.4oE_L ]j(]}ڙXȥ7&vW={/~r h bp&[[fFtHE|[Lڻ:pu4'AΡH3)gR-6g/'},HLpe2U+Buz_'Y='цIֱ9 %>?[xzAx||O [WɔA@X4s__ .j5:eu.t F"8Lc9IkEKL_Zۆ3Sol;1 f9(<0ew"3Ìl#T-#[wCyR~N4λ۴D@a}ʸq=Eݫck0t&62mr506ޗP>ugb΁΂0u9V+*`2׫xP-KV J֦t L469Onk Ȼ\J|T׌lx3\U\+q|}|ObŖPoYI44$ZꩡmF)H'Q<@TJTp T@' ADb@v~nIxG|N2!uzR~ٱrqfF)Z^+)|ֹ\=d{DVv‰F?ӇrDonu 3׿4:qΉnISB.F;ަpsN;BA cl!:|R0L~XgjW/ǭum!mw"Yg ,oLH`vcz;0x΂29Ս٤CR- A,Y8οSeٖ r!>iEHl[P6>RvP?kat;@ZXsOsCcDdR`fs>fpsE{|g=gB]%]?%!ͅډ#"eƏSxqaV77ݷ͆{{c_? %~7^GLɃ.~FaĦH!1y͹lYlLUh?m4v T *[c3nOCO>(_;A& %۽,h<2} ۦdd N/I^!.‰ ENKlq UXEa<4/>ܓxŧ{_4.Tq -})#{f3ɤ ^G}?{%~bË7xSo["8bO.ajdNȽHO[Q10"wbZnRQqAScb 2̾:wG<^y!gRRM"Mʳ*{hTyeۓ8'80(r)\k:/~юbfXANlۖB|?:PҰeβȢ;Ѝ #Oj&MЉQ]=(\ {9̝dE Wx s@AVG;wSԖbuLs[~œҿyQ5xȨSȲ;=Q66T6:AqShgԚrZ %0a,>׮Yn&'x9rP%u3E.et($,t)<7z ?73|>~~3-.Sw7w\ KMPs_p`Ďbv:mWˋ&%5_T4I{)Ę1TGcRu~C,k~1U=Q@?A8,(jCibILy&r,>X!]WUՒxfK ősyA^ZJ^^1— сik2ΗrXXAh8(l]~jNт{\Z)A:*q=hOR ^8HfŵD >CGJϳ5w|M<G_/؇B۸LD"'j9?&ZG/TuT OyQD2C R_5(`а Yv )#񲺉oC&2dEN@Bj%)ȳhԺIgs4f_W4o Fum ?cz=xFUs,saN>qb4XKٶ͂\<5ؐ89TT"wLw(2xDp6"ƗXGsȡ9l{šˑyiF z@`+ _ (KbdœNj|CGP}/3-S7aiЖQ'وp2jx0m}&g'#56)lX4# @j1{JԲL6z S 5r3p{AN5-xHG3tU+Rs=V7; d%k[.CbI.7|7?Ƕ]زAщ>W8:wȼ@ĔGc^੃axKs{Feml#dX;f`WΜ$,"6c_`[77aMt)27#Z?әMzƸܴh\W4g#W;b'Skw㉆=q$L9{9houJzK"?wN XֶyyhJ:f.zi#D'p)D6@F r’eE]xm1ŻOfV;4S0p#lrlis5ZAi֣s::Lq4t̏лS\Glg*SIz|;=|NqL:ڵ8K qJ7;ݤ/x;دW3;]9r,GxtU3B.!i |IaVSz! E_ϦG EZv`ie >h%_gc k$-Ó9=Yv -UOZ!h$>W'=%˯:@ ou(cXe]ʧO8/mQYBDnه -Aɉr^}oK/u^~ QDË}ҸS@{V3٠GNir[o U9,E)qTR{ {Kh?X?ǧA*]9mv(АRk.<:F!BbV")q}ځFg d@]K71h@g)-GD)Ct(Q4%"t7qc\rڏ#-W*,S|XѯL+"? ; 3-|r\#`5c-/F:d0~}ԗE,2ї#seMgW$/k쒲C$JBZF>'wv^7N,"0~Y>+Vy@)!l]aHߤxTަRn@l?@ELI ie@m.~BY:N~&?7 hu@:9Px{ͲNQb|ւVn ڵ1 e|D7^VGKQ ihD]3 0h4!%SriN /C LʳAZ/8q=yls퀐 LOٮ}05A>H^ˈO jeHtؤXNv3d4ӱm&XB6d yL1 dLP K]aHu@|GnIiRq]It !, ,ȫ×K87~1Ξ>zjVWx.3(1Q3mԽl#"/E;/hkq"d є[ڣ,Jk,&MWsHnd:.LP#c ,, ߱b(MS? eEXq3xkG*_ׯ+UYmfUI5>";_xk,%8)Rm~Ϧ Xs]s=BBפm ?- 2yy a&D4 򯊇L*B 0aP.M#%Z֜)7X g2Bq@1Q'vP`!q-Ψ|2Mv$I& ޘc-* FxN \- IDAT" RIuؠĻ!a%YeAs(8 8ѐeNUF@ /Y8i¡uX"!U'1_FXbCPH/ˬ84|CՆ{Z2Gx/<;YѥfbDKóľK>9\[8gHH!DHQs 4hX۠PND}Rn?\Kbv%@35A ᆎ.sXU(c*bѠ`JE1(t2h wf |͠Ic)TIoM/>S኱r1;|1`).UMJuRM.؛T" j`fhfr3xU-OJ7О;}I5t˹ `esWᑎ|x5YHDŃ{-W^}!>+|~//r[SP@–8G+Ⴎ[2R$V9h0TqϮ8*^%,_*\%!\&$i*rc5R`\1y.h(]Wn]9 ]/l <~ V IȪrnij)r: #^뻣VkxrvĖ`ۙCB" D)⾬tR 4BpP;wy1gx9 [8'۽ì"(:AWhP6-5?_3sW9]i*1_ ^1+"Q% F_~Yx,]Gc<7ס1=&3 "|Fx& h9aQᑖ&ƙ28^za{~_>x5U5OWv/ܭ 'Q$DM2:q$Sz) A#2J LAbzxM-Ѧ&xB h&7KwЩ{;jQ/s9]9el K=yf "L7 U|=<[v9Q?ik6Hai9;B";Ҍȩ0-}̊[5YIvr:e.bl %mu×_qOh\^fo_xG:^ԙxdu:\9ay:±= &wn82>| laO(F/'c=1,)@.ԭ~h{A}DbjY=v0Ah/G !O?c`xukxm]]9h`^8RH#2Fo0[k\>oocݴl) oN{Wr˥ S|LZ4JCu|'M?*r: "(=06"N R!CrRWFaRg2.3JW@Wo' Fe+D@(kzvmǂ:o/LGU3ᩢdȤL^R$'i@;ǡۍ 0mq(;h̶ MN4FXN@s9(@ߝL&jwB9κ2bÜxWBģO |\$S- 6!d$OENC A89E ~ Qma,&tӠE)1v:g+b zwl!;fĖ_)J{ǐm *8%.F|'dB-P8dYW,|-eTdhxؔkfĢ} U0TNv:)Z[?pc:=|Smw2I5lu10vK}#!8N 8׳0[O,)1iȄ)ʫY5y6rH|&NX gg4&Ov]WrS7goνNw y֧Se.8,&=Ja[V ;]G[6{E't^iS֌ i[Ճ\F1g(Ib @|?Ҫ !d}iKG"lgC;UKEO1ӉN3Z5:ϖ' xLꇾ 뫊 鿐OEw$SeZ}2ݞūgUSK}Z !Ê a0y2>ȰYCi&I/D@jŤ5.J R13k2  ݘ ݗUƵ6Q,:2[3t1VycI5Ҽ>}h>/Hx C8jx`xB ;)v3*2\ bmXlS}0<\9Z4{&,T2Vغᴹ&YR7ɫ ikvş)iET/BCFsJ?(9o)')q>N _'F$5 \xGzٙKF~$iv<᝷/=Ɔ hG,zPq7ނBČci͍ ~4tj*[6&%Օ-K%=Z!!Va1xSP|i9 mL 9hQ!j9RN4a 60N[ nsh'XVWT˅4}-MqC4%LYU= ) 'x1c@ǛWMZ)SfWfQQEukdY2¡ҥ Sr}OQUtiQAwZSI"0BE23>s$(hpg>(ɑZǾgNRx9g9:zۺNlwWG5=?Ɯ8sJ2{זI=T4]5+" ֣nL&bsN{ Ukv2%EuZ(Ʀr Xa%ipg Se8;ۚAUmbYz@o8$~ \7'aR,MqĆۣDŤt78Ykzgh[OZDZKx7Ud}U@XZv.A實|*ƹ^@]D(wG&?.l] e+ !mأ?/?z56ELȢ ÿ Vƹǿ,ę[y5k;6fTCLe wEzO5J(W_]qrroBb)J Vu|j D"&U=4J(o8#Ҹc)?@ s3!Vby%7,* 'U[+sX9V.ppk0cTFyLORY%r!21f4 łۺXD{O)Tn!z${ky۷خ.^k/+ܻ:>{=[}ia,*8N}/*v,޵EEsSXNAs%eNDz{Y<4U+|L.L帔ۙa0lklK#&+2,HQWlviTV1.(#!Oa=Ƨ:SZ*Jӓ_OU^_uŃ׾Ɉvyf4Z.<ÎO]#? rLnPXҡVET<`IkԔނɵ Լ]`OA8E,\>=0l@n9sv2Oiމb*"7"BUUqgnM?'4 Dqc N6]DOkTmO󚗭-Qՠu:2G1~~Siوu-ћb4D(YTMq`BXIU4?CqJ=<>W4uiyDȹg^ߘp;h~oQRiXS(NۆSm]pQx#pWu{~۶͒zNݓ{: !nBյYRy2=-8-vu{S;:tφmhz9Һ DRr+T]Q84GranɅ `*%?XĶ] =I&Nqג˳b HQ=&5McӼuh$Dpn)|i%JxGUɠ)v@h@m# Q5y,ԓG vY2FBtl-0Aw5PQ#QUDFr8$ KfAX`AiލGk=^r[Azm,d?V4~\6 B'N }ʷx:b[?O7e ZCY[W+'?O?FkOp=|7x oޗ+%|#8銭/DdM=g F85oi/eY*[kN7G;'FK#\xCDsV( \犌 [g錑u€=ܾ忇/t˷]+;  (L )kL ;Ũp$1ڜ :Z5DȽQ*FE%c K.T<9Pq->rnQ ʼ O("pn!r3d1י"4]1n#G^oagnnh]yiH~F0BKF[ زJKlTiv%e$"EUFdcaSxۘ, {Z{.mѸFc!Eu,8 $20 $3{.`e4 6vPXò p,I-ĐxͱX.8 kNeLw<G4:N#N*ba;fP~³|_6^~!>K,/_gxO ] K~ZE$%Ֆ1 frv6Dݤ81lG[aĥ!iw!B8扢Z^% RSÅywv;)nc;u܊b]Z,xXLU,+ڲļ-9Ċ{ >O|#@S\%jÿx?SǾ_W_}l}< + <)5$40) Ĺ1ỢBƋ@<d }yX 4(5T8ι>%ō!DGXo5X NPs4R\֥L`oHVޏ}#|k7nn?Ӗ[ dt9B! x19Q2ƌݬ:mT rCxrmʬ0_*u\w4!GjF SKOsf.8%T=c'W6_΍d4J/`䛪" ^ e`Y.T΀8RxXhd9'"`\whU0,8&3Ī0rϼˎm,hh*vwjaC9֎t,-t h^v 3KK6pEoݠc1&,e=b]vHm$A&-NWTP5LiK. ./\.` j&a9^{x|}o޸ONj5WzQk NejTP1J(-7qXPz/&n\Lʣu%"e6z(k4P"n&:Uػy̵{ꙜZ:ŧXc Rsq(摪B["U><&Sp4DrېDb6j3\pת`9LHTRMRb cVw =W[tTeײȸܐb4뾤t<ϸRԠڼo-k &iX(Î9SRN9_B n&xިU/g%sY1 @`L F}j=ù`t19,Zt'< Ұؚ$/LB5 'ԏ[0rtDf܎Ȩ%:ɈK7GL8leŲ eq#!Xxk=QnoKo=ƻO~oKb^SAL=mN+H-vK_- QgⰊ͐KGVqw9 eeDd\+Ej͡] u.Ԇ\e9fx(_s q ަG LߤCu׿[k\?^ꆀy=)PZJ q p IDAT"!ۈȯGՂP]ߐp[xJFV(4n%|?>P$N2w  ^:Tr kTfsԙhAy&ͿF+$bGߖ4*,#Ta̍U % ;/tW[˼T`4b/3Guc(҇:Ulu ;#s9ŒȹZRb3vv{l7Ҋ(Ϲ1zb55ʶmh@ƶK[":T,AT EHwXOs;-:_]5^BOuæ 6@yL\fkP % W$3TTPr/Q!΅ʓxeP+〞TAV~/#>)ݺ 4bpfFw~q/v}]QP!xC=Y|{Ub< &:$h\tXB5\KcPBC3 8"vCXQ p/XM$KRy=Rl!~F •-yStlG -ϑ`@=*|Ņ@V ;`*)"gqw8#''9t-c "?Mh+o!F%>9y9:3H [*i$>t\j2giM7K&^b96u"9?E.U./ 'l"<)NGlC@5L09\EbBy2/UbaLT$ nT?0 %QkW"(e }?oϽ_y?g+c. l_* QV7 @SOUlMZ^YE n$,yHwh`DYT\W{k! T+Jd\Z ++Cg 6CO/po*yNS"w|~Y/(CDPF݀j̓p.FIwMöH:dyX ,wD35M(~M˶U3]o:]K1;#  UώCȁ^RSQ lp"eT:ʫ=SrvY6nNZdl0!ɲ%yi`;՚Rˊr6#%K8VHBYr:yDF*Ƞ5X}V~'&0]o௔yCX-..=hd"i-JU~q =/r^ 8={?B@,TѲ(-Ek0<ډ4)f@ݎ+a0[O6+"f`T%NLq46s{h>-\3Hj놥SHJVf% k]ÐpEU-a Nh@b7910Kl|F,6ZUTRP_ ED#ʷtWčqx^ §9WX t"3h" tlL8]#Y93!"nUR}ZƝ4SBa"@A$8Nֻ+ &rcQ=3e=#Bץ-4!Y lpK\]]+\, - npͶ'R ),C4z?G6q& * .ˀ\];ڃI.QS+֪x+OϹk>b#ycNRV)9 yh4C'D # 1@߄rX$5U;@U1h! D b$ $BhyY}9R흩px7D\1ݐIR7*f]+oUϭ2Üfwj@%"`%A迉R `#yv_Tw.+`4V4<%#Xf@(kH(|\ 1s!psw~u`&8{Qf8-)v&0DxaELU*1K"QZweoVMg#jYl>U8`hTͱәkJ<YCDadz/rU8 l+'G<==8 k!Ĭu8;wYG Q8VN (@Y4V}Ed>wxpDrVJiiJDm"9tvƈ@ o?xkyG?S{/`hU~Ѻ+kfĨ.L,'#gz[s #ő<9cGT +Q4NFYI 9f?ǒ݁Zd#$b8`_>Ae`B?šNt;Bjx&J0]IFh5{oXdi`)9Gs*bbTіfae _>T1򻢍s1O;jΆW ila~$"LI>b?wKE[lCE\Z~J({kl}zॗ.||^“8O^(puYO(9A>&cѨR[hjBEF*w$füie"{{.(s :J6@fG]%$Oq1SN_y}r DTDa؟&ed /<*H xq+ǩi՚$ `7^NzkA-u9scj(ўh{߆~m 4J)S}!"H/YqV=D4yFBT;( w}^iMVCeB$#,=bdX!qk% S{@4r`ǐ6Ew1c8>&U`~7,E~c^nP, ڰ6B#PM]b*O~fЍ3@șl*jSN3f( bG|A|=umX`]ϊitifъ)j EOq{ɳ#{/\ҝ ^âDVvVr*QQΪGCBvH^wcHJ`ْ7y?J^CVh8p!4^v:MJw ;qt &ƇPQ~v:-KUǾ?T O,>TGIuS)u$aR@E |ko2<`؅ Gb 9" ,@EaĻ%VU{dgՒ"+ sLT+91g*~o@ȵw,D@A*̇Bgg9Op}%S_5>|]8ƠvK6PbfIufʹVnB&,S"#Qdt;<=ݩBťohTb  Q1&F&!LNLPnjRn%FsP*(,r#"P7|(/ټShP@- .-ǎ i\ Cp¦ D| iȩWFE!E{R֔ `t0˽DEd2Z$Cy!IoOi:=x?G8mU,H׃}No>3rHx7.//fQ( VP^X@^~qO<utf$.ڬt00כ#'Ͻ?U-e@nߡ|e^P,Bk)rfνxcEvZ; ;B59<6=C,UaQ^`J.ͱ= ]hߋ0F95y蹴FU m+Echw]AF M.1vauΤo=ۻ3 m.1 [}9fQdMMMս3ZdI{Չgq˂R}K-/zӊe=yw޹Mo.-:Η2hS!)"a!4ca*3],0TO )8^AõNW#)^,Z X<:v%~˶B5{44g8٨4{[.:]W]/z/e-VM I6'jfM y~UmZ1/VΕVaP1ض-]B@ˆ^{7#0iI$sD9QyC-m 隠Bp(E+rdY?3 3AD;/?*Gp4R̓x?vsa;ضm9xNϨh,TD@8;68&ME(6ro նزwEۤ0 !,k3[,cms)xR>ȥJ*2tٞbd3O–O L[%Y5aOPo\E$\fT#f)چ~8ݞ{7~6b\c>TakvY[N '(qM~2Xh*ǜ:D #P:ڟ_x3BpE$;=$}ܝno?|@AGwڽO~FĴiO esiDHu 2*FWdA?)non[;.~w:l8\!L $I8T=?0̚M p, ȌAnu$}4ø(Uݨ*{oXM#ʝ4ۼvX&XV;C`m͒8#:F+?Kll~Ա?7t0&%x?Es{W5?WF1ʟwM,=%|cY+2<ϋÂú`mFI1ErϨ, A`R74gTn[sID`]ˊa] H*F9# f Uz_~?^XpOp:yc?|9^;2ZP]0Q./yMmen5Gl[cm+CIFE\JH0L0O-M'f5J,}}뇿;g(0׳h ,C[[angu3 Ei Zm5h`! eEn[$~6-I7B܈uHkm cB½XaC9Wkyk½kos^.$:aboLce4fM 6\}+ۭ-hBV1jٔ,YFnOUPz^tGxi 1/JnT g3AV }~d|-DذQQke\4 `VքWh azB qtQJhׂe wȃ33JOUm\̟!Am$`IdtH,\Y1{U =FHʳJRyQ?AEWXb3BU#f45[W(#mQQ)1Oѷ dzS7I4[l?u0sf~hR34DwK'OI-V^HTbY3|]05O0$L>;UN;w-6T#N,[(uW +y$s Z[h!RA¬v\O:<8F-ʼ+~~bEylݚ㞝vv`) $mѻ~i0gC{84 *Ų ?K ܞ,&/1!<9L`r*q?4bL)4|\eP=/xѯf"lH cލx? ӆ2M"0S)¡ siBD5vB s">1OEf|Pձ.@W00FnrJA0*/l0Vw4r~l'Xw:M 6#{]j`;K,;./|?/ASAuE B=f ^Co†k|z1 SxѶ As{ IDATe'鰡Gmwjr\1i#@ÈOݽ}g_yxu; <PTKW,00nH*[r3 ٣*D/fᙡmM[W$ĘKͅjW'HpF(,2zm_?ãS_Я9uɘ%ws ~R_\sy`{5sWĒ+i*C^W%R#v1ׁ1.FM픹Nv0͕j2lXT3^:cԕZ[%$}tNGrzXjbƓBMN[_sWUl Ϯqs} l_Zp's׾8LX+)9MTHhxL<wTc{t^ wAdL%&y LM)dIBcV /=)#;jx}}C"Cr +zQՍ).gERۧBGp&|u^yx22$^bJݣ]#Z K?mYp'WMm{/Jrx|S-`+yF>Ηú UcQ0xc{Sn|Ѣ85RcT㬛r3\xsxn*`:noxώGaVڤ`|ƨLW88Z*`~H d!0zݔ;C!#E2 ˣ<QLd8''zRW)k`%8i>y/ݙr}"3`ګRłEy<1,A&f! #bЃehZGٖwuI,y["LlcX&ō\0&|޹?{B\.p0$/Nmoɀ;nwTRv*/_E Av@5<TiyBGvQ>$pG(oO!GW;-rm6l%pƶ9;<#ɟg~oޱTƥ*=o{8ϱzlsJ#Kx h¶?}gϞ2$`Yaz8Ul>]'wh؎{ vЀSâ ] &S=u_x| 3՟sQN}k褐'NH7NE( l&!QCI\(8HW"$5~Y'%zg;XG= E+$Ͷf;͖xXyHf .U! {"*')k̇m6h8iuwsnp"X­=.<)VH}m4Y h1J`&@ky?H?#anF9vM&_."k{8uXn4yXf$C" ((-Sltkホͷ;޹Dq<:" K8vyxJsHFqzZ2h9~0 jWC_(>YGN-D_Ks :ĻM Z*FxPSEЙ{uyɏ|z?A@5Ec"Ȍ"va j(pB(z4 sʆPe=' 1ųC[ܰ@fSH )R`gPs(gYZFY7`æUzn2I3!n]k [W`#'3PE3&ξ cNl…BcdP,.>=8aY2"ƬF{8έJXH952PSrz<GOKC2i,N"b(*cύJ݅& jP)LTg0.IMWlǎ~h h USKp삵w\K+ Ԏ7q'O'G7*N7Ef*fMa-G0D}WKiEOHb} YJq2 >$+1O~Y9ɻ.b!pX;^{)noOoux`_odwe e}EZ!dx ś$ f$ڇDE,8ŗz5 Ғ}V8(Tv(|iT沈%są.Qkx=Ƙ3,) UV:zjg_ ,6%Ѣ h`sH#߉"aHCFܖL\v|"xHqM) ha7IcmHZkhk&bnvA;)[%1u^VWv;Q>Oẋ}8^&ŲK\^43}_y1Np8\p%_]YBtۛ#֣#޹V6NJ[)"t +FVzJs6X|Bz=U##(!H5̍ /P,hhTCjǦ.9w.E$HW>0Ek؜Vtո''YrwϦ=)gd-vPT'D{C$qU`:noOfpW<܁&K$6/kWP57n7 OoN8)pqy}!g~c9&6'gPu y.KtE)h%mQ;D:bKKS,l#T;Z\nvp#Rno7h\^\ ܻ5ZXa -HEӆ=Tł/bA+ U:R+2K#("#nBN=G!s?L4Zk&~GyL9SfrU> |+xhZ{vow oGb E{oSEg\~>xu.:&NNZj5r|n0B^YaV \ވsly*ҪD-GBquo  /sJx[eC3$V>W多!c{͏+fUF<5 "~2EO|oR|<$w~wJaEԖΗ޲b՞ò;T-]! ۊ<{vӛmV,mra\?;ٓ^@KkòϞFG7;@8\υeP,5`[i\o 2 H=jJCfaĥ u\/~Q`tݓ$>޺S>Ⱦӿ-ۜ|^īWm>'oc]{vO<1%(GJ 0͐IXEe S0o RX(:%]$^uB 1o}\I4*װ/,:|.9N-L"|z^4 ?*Y4_ n"!ά0sCwpJM%Tc̓tUi*Ɗ@CH dFvIJq\ ./8Zs0ؓDQ2):S59x2Ȩuly.4YyuF]VyNb]]B|n o?g' ,$9"ߪz_gpHl_d\1Iᱡ ! _WEE(j, ?H &f'gꮮXI=EdddE8ZU,Iv*l.0 =/%Z^]|oÆF Lnx|P1劤:fs#;5dվ*bﯢbq2y.GXBƲ!ѥ^aq=ف %@N@v CQ:Q@TQXNgVxdUR陧z w"XWG&YI雷Wd2{܅5Ly AyzT 5{&-Ci[)+R6%R0!{B*Sgi`Eeژo5r29#G Ijˊpqi.wü pldς(Sjͱ,؅C%[T+&~e,lƀo͐(|lylcI2ĒN \m'\ㆋfؑ6b(X. Q r3q*zO6, o?v'LFpހM0aDt(kFҎۼq#{ĜbP[Iquy>拯dr42Eڻ&h9cBy"y+-/6 "ȶR j(m mJyW.^*@~>ƉcDW3A<]lٕ724*B05{nM7@Yp=|5K`řNqfqjcjKjQI fT :}&3% ]-U(8a'3(џ>EV)[$?@LyJun=HX8\IJέ 2H,=SR}"^]un u@{ (xiY(Ow23P n(Osxpd~P6&ƮL5`72.vňi*Ɔaj+>{w߸_5v\1&L4A6l{:z'}B6 @QD Ĩ*G b8@ b2./5 Xۡݏ}p6r&d ,gcnCdkI%I?y=`8]xr ڎi3]gh##Dn}.$ uӳ3UxxB#w)` "4cHL(| X,.d7)L‚Fܺ||րV)%Ô&  mGQeѓ7`C fאmt735k˲U3e 7! C+ͬwϾr[LaIJ)^.wuKT[f 9qdnhۑ^,#巰 -I\L:0)٨<!ad 7y[yrL6MiLYŦ4M9<,+n`&M^`԰ 10 wp%?W.b, &Rf +4GG2L0qD0\d k߄X%&4bK*ZOq@ӗU1q@ho|H&o!h!'43-1SWҠ0@$l:9Sap[<-x``0 7>wVa^e7WLU-+M[8 } f[£R1B02RA'Q10xSלdg!0%"֧e[.Gai7t.½ƲuߒѕA'Y-9YgJϵ bgQAԉ^6Sw?tr4nM~ ftۚ6Ou޽z-UZe *_Os{ZL%;I Q0pZ`0Jߘw0{s5qgΖξ IDAT4ĝzOO?ɿZWi 1:灐P1} !+?E"Y "|Er@SѦmB(AS( bN݃"{hI#qM Κ6mhn}$퍇=;S^65T鈙w PaYtgIk ?~٬:bcYZAsD|fn@-a؃+S ]lEv=1)@Xg~5@ϼ+^+P`2R0, y`Я @i{`}?-~ ̑A o&,d@C*IZkF3[+f*ЅǦkM3yYS%˽4=H(#ߗs"& fa1oWrPF2 %3ceB >>peɚcOeQ>+$ LQ \4/G Jw4XnsLԶl;;Ѻe3| (pJöC ҽ2?WA:?yk(,kGO\ ctx c2gLYxnsc ,Y a `Q*>L q'k=|x̃2K埁 ovu8ߧMnz:yZzxa7VQ"Z}!2?ֺ̒ה99hha&=0%ܶ\e! YlcU2ESq۹xLH=K.dRg9>/6v@xi[Ȟ}E=,Σ!4&Ԭ ;K'Dd^?9aFE=fYiHgs ` 0%)m̓ZSysOGo2]q+N "VS^Ƃ _^k/ VI`VXeMQ-Ʃa;1&&SoƔsE۱m%,ҁ9xhrWkF Db Uh{mǝ C K ?)c" ^a! XE :<2!"DN0E`s!` ]!g W-1TY؞xGKw[9y> NCc2 vBLҶ"Me!3K{ 7+0<tvK clɓv6nn[&C5UOC#@B ɚeq+سbj # kXǰ[J4q 1yy [oXCb 5o*SF,< Ic ۍyd6%>6>-BsƸJ3=<#* JWeOZQ+XVrr ̲b` pkhm6G{аS/"1/~t8"O_f@B |6,{oV4oy"9IdV-r1`X_WvYIRTh2Pe\,EeY,KZPW 6ie[eʄq:)ታ%02ݻă#a*!6g7&Qd,$Ĭ< e}r9@6ewDm/(Kd , RgR WVubA ҄[g <1^z=pq|/\wzo=x+̠rm$r,@*4ѣnSrYq-AʥeAW0Yc8z/.sqk!-H[#K!9M d7e≭A8@ͳa.ˍޭ.=U'$ޔCLCI=O}3 fK? *%$PyFAVHx wU"-{ qBvE%d3 ȟ(. f;Y[Ut~W$O%RޖXOTVty\,qΞM=dF,LWXG(} 4_@CE+x4}Ɏ'[|Pz%-F ;^BRXmdy''0"L4F꭬ ZP jJB E ;^z7{[[B]ptŀ4 c3UW#[BhC Cé0I_qlIcKD\ q4ٙ:[5BCRy @Q!͓* K. *J%PjZ49[ib͎^ x F?bGX-h8_ߥ.^~w~T/ Oh-8eBLHK'Z> *s0zU kȚH;-5TjJ렲soWcD4QrANms7XPb։TfLx8/:M.d1_Jؒ 0gTF%.w 9E,y(I>W:2- aXT Ò5l̘&i Ie*I v KxCUeMrIy2ZEI9Đ$E&XVWhsxso͎FƆRM 9mk*BfNP=TBCa zٰZNX%x8g[Ʈ noc/>nami|;/KۧX v _l[K_(xpĈc\5 Q)i`$߂{mtEOdpK1Iњp|s71x{~1q ;ܽ/qvr>wOX_~?{?z[k(}j kQ}w Kĕ]jl!U>Y{ g BvĘg Zt~E73\tB7}[7sO':bϮAYk`6 \sܛayp{i7>$-Q1E$[gItY{3`_> rYK!LE@P j-CPxi z% $>c"Ƞ8yg{CF#_Hu>650 ˁs/{z$Z7#H%\K[ɡRPAXeCz8>"9[^~/9 X/*KL7z}M\vu^|yJ5Pno~a(!XOvS~V 52!>'a3l`ڌuV; y(v*-8>:7%S{+0(@/ZXkb0*Vou] P)BK|K)wQd!F @ B;goOē7qx-e]uF#]0MvQ/ompWPbM ~7E0CH ~[;`f#%r <{6逓pZ%R]%_$Md VquُӽG ?sć40c6uE R öidh^FxT`ZQ nV,Я+G7yo (8Q&ړ{3!eɊimmOz`6 YƐu1 3i9_x;U_ywNVvlj&:y$dY]n CQp(vI٭8=̣ N&t Ucl*g>Lr {DJtIDS_S6Es>'= ȱḭ5f+pZpc@4b3Mء f`7 [.  7N,o;><1h,Bs( 78vg1 &QT c3/7QTP>uQ6%o1%ngtr¼6:s\ _B q&QtG퐲7B 5F m[ MŪp41H&Pc<|?߃ȸwٯ*I lRꀪ26H*l@] .Sa YʳJW)$J9tQX#K[ZHK)H<嶳Dq}YK\2,8>O~iY0X *%i9Vxf\PsEnOWXiɒqn-ܓ7pdEeLėD Tu0k׾/~ۋ{L^+\޻S'O?cV@ l5),Z3JL,&H(ES"{S]dn[H\`^hULcԗ2e ?,>J]G26spg 9 SS30_*J4$ֻgKrvR;xȺ*=AWYoz r/~:u1P=*yQ 7t. :ErV%۲,xp@K4 -plaE7%Moֿwg|ӎ+xj&X-n=OF]8R05։j7]-^LLh~~tT,[Ǫ $}iL`D8VeM2fL%oEh ٍӤ",IO\UM\mm| ޹ v-KIvqrg8`˷lOe@1 I=]PZF pkv 4PP/ZlSx]{|+S=~OdcdD= .C>a&M+P`T1y=39}g0jQ@2%Mt\Yvkby >ђZ?i&F߳໹Z̞@ߖRޞ$AVa!gM9!s7:nᝈ6鴠Ԟ1(ѷI\bke| >yJqD7[^Qhvpm6Օ.VUHHH;Dv:*ۇsa cMmi\? l%ظMiѱۜm7XSk(e ?MDBa7b7ϰ^ sʾ22X w lv[{k.7αx&^z&*X'LvimfDVb^"<Aҽzlj'즆Hx7llytl ,R.@t^v`;{D-Zr ;c] Kr]^T./ZC;X CP `+ _|o7>ޗ~ k?ڔ58 &CLx3ȵgme,`P0-XuVHaf+aaI @$I;K (B^s]] drRPV=@+1ʢDsSfbrɚ u\'Qemc. = Q0 hJԲ;YCd@BB L)le vC%e2 Q$[-ͽ'f/n^vDF /%PJ ̧g \@G4" @6%SǍT˘c@LEs5}4NU32x1nnFZ^4v+\8J~ZO%5)ZdN!Rq5Fq7p IDATW7xgwKN5]W @, WL>'hzlκ1xjð O<k<Ɲk- #ۓϬ>wK vk/g;O-<{V^1>} <(&AhT צsr7e)'o=~%wOB GQwu<`V>rxIgО 4y꺊{&sހ_.K $Z:<$\]S@;{YFJOT7XtM%NϫN8v;9W.S )oxT0saHJqKr?[p"YMd. X.ҹ1Ѽ9QL PoI,3l7wpC|c-Z L f 6vS(бXKJִnĨ:'JZ; YhrN ɉѰ\NOq Ƃ]S#F`.h((T,aZ$.2;2o\%WDIJ L Lk;1w FMX  Y,`öƫGi?< |R&7mJ7ݢ9"Y+8@IǂMfEB(iblza̤M`p[6uk( )f]p%چs#:2{P켵׷$AR{DҢVv~<"mMS^~I.TN l l67Q>`.ZXv&d7QƊ^FC$U`(P-ě `e_sBw١ Ҁ_b+TNm`$Ƴ{Oۦ&<[B )AY=MZݗ9+`e;}V ;nٙ'ƴW[\xZqe|bNp$~+ȼs@7Kr&iG!W^)ƌL8 |9vx~æ-1Uœx6IIKEeKIo0CTυA 4U:#NxlEÍ7O yy>k^bJ.*˚^nP ")fdB|sG8lꞗLڬN& e͹ڹMx)Nb1x2 -66 [,'fZL?Is+$4/K$7IU> yH %*$*TpZ%r y{(+Ϯ5.^`2.nd(=ÄtiMw*r„Rc7ɉ7uɪx`E!%:1eC#h6*#a]8VTK5}6O[I?)G)DV^#@&:@=J3IQh#)ۿT{2BiYBleMĭꯋ!2сrFAAWBXHv=\y 2mϱ綰`a"炶]v V@ЀFNlQydey8aVbxEOd`ڍh n 3Ʊa˚oœa0e4-<*tCef\Z)bm U) er-cO e֦0R DFHHZOr^ Ejөܤy}y ]4'C^mwvzb1} &/kTtwEi.[vq25\ 0E`kDžT8#\r=8b#ID wPb$ˉe>)eI61Ɲd0'ٵ5L=hG,Adʆ\$dJQ@"{!̋8 "Ig`5T=Ã|x^~NNKZPۄ O:=8ɮRʲY^Ob$-A'fLgN j^urp(u[S!C ﻜ{?_3~OG]C@\dB; 2 MRZ+nwXSk)z|te)N-lLu?~8mA~4@qzg:{H8k括 ;}:H?4G@ΏqxҴ17aDaBE[O ?Cd|DBY[fMSy6KSl/ŷ!6y*;+fLW-DD3\ȷf e%!Yc6ENDn@MNXAU݃1~o#;B36s+It4Ʃalݤ qA Neq/䦶GvrA mAcdx(e+Vb1XBA刻lqaLѱ, @kƝ >-tOpp<, X#1Z a{î(j YHMbjm/Ǿ?cp=|7x9)a.FZubqbvs6GTreS[H'O]PZIQrYd?DpU{i<XU(tgMEQY;й98Ҁ1Qm_@ku QS׌ZnriEr'ٟޘ=H(XG$XZchx2fƤ;ja ͱlnv_~p?؉=sM,;~-V͗=FF"ߑm`[ȉvJnl9t4y"s,L y9АfeYLj](!lyIl39/²PSV<+%B A *n'LMA Dr,Hp^~}/=yOWXB @CUKhaW ,WK\mpb,e]aX qv/&LMb;/|;4_\9;M㏼%!LuyX0#߰g' T*n$.>(jN&qVc' ;j7'9>SFK^-NҺzx`O;ۼ3\>!^)^|,918r&wspG%'X428z (l* h"E2 R*jxT;6y@M7e;8`8Œv6Ogv|ebҘsx5P z1&zU1)qlJKF˳$(cc|Qn,F$L@X 늳͎1m`g؇nCOt d[p x;8qٍS$P _‹UJ!Iݿz,?,O?؞}[>we'q29?V#@e+Sg[iBqARN(LЄ \gחٛ"Oj8RP0.JVJ"HYx2˴t΁Ɠ*G)HքuaA3!"󟔕s!XVC1 oUR?1ıCuhI =]Ky(8SqObnLFOQg!~}h ,Cc%5nk [NB)C~o)/:?/w ~LUgڸh=qP@TX,<5rnփ .GA,?T%`[.Xj* d7b_nq|zӳp8:9z0`Y5gU2b ʿ'~{>+?ckoayv Pyx`_b"WV\&u؋Wa9'#uNdw}r-}_w;{[ckN_ʌk$.րRTASY6#5SNs뚽A1 (0(Sk6ϓ~ͲL14li4@L0=oC"eϊD e 6[G4}>9j.e30Nq2RY O'EL #ICa/4ܷa܌ڢfbB x4b(X 2l'p~ﵷmߏzlryҼLx|y'dɺgy`9 hQWR)4p]vZnj={ת-3JKSIxs߳DI͇sn*ߎl?{(T% 7* E]|pU~9uT$H`[]T4kdXQ*z=6h0$%DŽU(C<@"2*齫L:zdax2!*$n6wm@R 8|pp8RM_?^,}CO+Uf/mrݕpsU*^ݴbniɄqL  (X Q(1TS?OsM_grTr*j꾢(TbuDnn4n胮mR Af,?#h.F`ywNc9n1 ̖,3 `@)Hjn"`>?Ŵr(z:hQ_. OZባ%v[X7NWlw[\l6j( Šx D'ÎPJ~?'iNz,?yڛird  hE%OhM)sFѓ埱{7,Ke녽YdHTbvuWF]ӭh$b=$4^WjN-[9 %eI0³bu`ޠC  E69PW^Jɽ]zKAmu,yj rJ;c b}5bKy퇙ĺ zXnwb1$<, #}1B.k%Y{ nJ+  Ϙ5HHcH*8֖޸8OKDB*]nΗ!40y:LFڭs:_~JT wFv*c0R0t]pƀn,ĪiCch5`MP-GA"GC<#T~}ocz=/ e(0]>Z@BVDβ]Q=Oܦ5j9\{}TG+7&kt.AL ~!-zWFU%m.>N{FS܃ޏu-i=l{PtϖԦpv$G{>ʣ-<J7R W$hNzQ)hK[8~И IU7&^+̒{9HO$0Xm4{N-Qhg%MhGODۙ &Q E]@54;2[sdlݙ )rBʻe%]Vm"`UIQI3u-yЃU;\]&XVфgn-qbEvƂ 8]q4hòbG%B)㣭%FIz]_.koo jnʵBLPNfN'lSٳGlJeg;6ys:ԗհ#Ӹڒ蛞yKK=i8v4#Vw5~'uɼa^g)YssxD3,rғ|'cߒθɟ C9+0X(^9g{!@f *l,4)N LȋED)PuCʄH`ߓ%rD!xG[A-Kݼn_&αE~ls'nd'JZ:,ZO'4+k7B1v=ǫwńF@-8Z9[' qݡd܎z|xj|bXKD7}9tXgg|ߦ,Z@|hMTI4$p0c5s$VnrrԀx:ӟo20ݬMA;Ğ8E(=c}>F󭁀e _ȶ;B*6x=9 B$Rrfdu7FbNRQߴqow_ IDAT pO_[9SCΐqq]\)d5E A@B^7#'C# s J,<;&!Nuz|(se g㚇LN4y1ֽ1|I3 3_86v> =N_2uŸPjmGlv`oܽ7p>vT\n K²05ʐi8M&l'I}>^ퟖրHk_?T}V\glye6y˴5lb㵭! -;_Xcd kSK{lyJv0ID`L67xض<+kIGp~\ADʠR`FOaj0i,n\~)}}Hb|oC!] {4 '*i > +znE5zޚHO 5˳b? G2뉚MmwwТbQ$q]:9!"MUnn®*ߡ݇wG 'O>du.nA%)$IL'~4$`wP uJ^NO'j;$Ag^wED= ~RHӴI8}x2_5/xU%Ta ϤF^-kaXZDyEVI, U$źGsi K=TAqf@V%gW#NK ol`kYzxj JVu_='! !eJ5_b7@㘸4@x .iI)'P:0RTHGIi. l AI 7 Ȭ Ӳ;Q񷃂$Vݍxx;l² m+7zQ$uPDJDR7_#vdDf,TD ÖkSX^ssJt[ %$:8J N^fr~(N@;B("j^i@P)jѥ cDDa7ťdA@:YR |UHа|Mirt[1x v] r>'\1rk= ^DЏWÓ!Cxlc?p^c7,1;H*Y=z5J2A ?{8Ѯⴭ)2kXb|R5=ܷPKW2qbg%.S< &=J#h:Yf~ ?(I2VKS.8J| tģFZt,H$$H7堧@LUCeZ>w܆JX., ɬg)ifX"Y0M681ƱadymF"NW`A^zzfRens.l}}X'GaNg KzTY)mL߳@[#eҗ5PQ͗b̬ MiRa@ pd{Us~k}^7[#J)Z"#{{@hBֲ-ʿʪ궪 (AHH?,Ea$ q<Ͻ97@B{Zg=c91ӮTuNua@yF'ΏG?)q^D7骜@"efΉ VFkCE/܊j@ v+ظ4KcIbjȖbZК <*lYtqV +<0lf>,xa*Q@i$useղ0t1 Q)o=u+>8b^AH=eẉTRI!d{b"(7&1Zb l,,㚨=[ ^A[:93fުi{;@WąFcp+竡Y(1ZF r0V8Bc h8}I/hc @nNmh3C&D Ñ dY?Fcm 䕧P.)na A)j:2_i;XI yƼyL]|]'V́A$M,ι-l} @1X@-%? {~PE9}/7}f=yw#Yj sSR$M7͉4닊jIgRHMuf_g򔃤4Y˂A؏Gb0$&a%KwE+=0x@nqf+y؁3m N뿎ZIUa QHXTuEib0_K/W;{Tg l$ gjUCcfY>\X.M"` A`>o Tչٖ]g N@hV.NG> 3E:2 gFCj!ʏH96I%x { prÍkWqt}%T4wTPwESn'Qd~fGE[?8ט  r'm:M&㩳ƕ tK+OA[]LI@),j'L<'o;Y`Tu`d,]XXΌjh_3w!0s'x A'ӝ i_,y x -g 9RJЊlA ?hA*͒3N'GE?\&;gs ECLdM $aFqɾWԩNi*EZ܄JP? ٟ+)`s }3~L4@a:}e#F>N&Ibި}y9Vtͮ3gI@#:lyxW6v̳ҙ,RaŲ~!,N5RȎ9-+RNpA]0*gv0m7G G$JS @2[6ֺ1bp=\ Z{Gŀp4I|U #F,A3[( U4 h9OAU\=aW cN*UlL<fM`vv "~aI<2L,uE;  2~4VԇD?%_2djSjAj56`bIBNx+[hMiJ I}*qݶ3BgVѵ̮jJB x!eTkFE `c7O=-R_d"5sFGS],{ ō1< "bN4xt@!D/Is/d@礹{YK+ϊd{:bSSgx2e;$Ĵ|hÝ2-_ΏG \c[DdVݽ?JaNa* so$B0 fI`T }U`Vj%[@PsVZn7dIbs R)D-Y)LDcݦu9q42gpb4Q\vRqp'͛Z`|i Zkx:oJœ/Jk 4" P? &l޾ސ& ֪O!%fbL$Gq(LMz fX" ڪHjBC^ؗoif%uli ܲҘU)ߒ5d4]aI@ Knxݽ@o-͚leFd}VTklz+]A)i7a0 D?~y=ʴdTJKb!mvxa ve3^K! ױ`ƹ{v 5cs|9ؙ #MOX*T=@QVԩVu*V ,!7PX<pmN蔈Ѣ_K){Mr/-y w)mzGQ]AqJC#Uѝ"P?QF344@NI*L@\g}lq $|Wyeηe>D *:5Ԇ.}Ʉgm_NBn1ov-r7֊Ne C]q`)=t."6uii] T=CuRZ즪1 U>=u78 WO8≠#ҲS @NPh 'Y8^8PH#PEJ]`yi E]:_ IMgБpr?K*`*Tr {V$OzU]`vpŁ9 -}_ۦ.Nf{Oœ.Gjf.3ČlƗ% qvE[#iD`BvE˩4BhBHH5Gc-^F!A%H^iDN^fލ& tux4*B-TYcL**hrvAuUZ~ξm "lL?kTw[Dn7Aj&u<1=PH+K^a›F\ Lsu*pb Bd_qpH@*T !SzE;-?~3] nԂُկ~ׯv{,X=xY/ITf_(d[.SCLv6)tkfcECOLA8MjƍuOAH  vvSpLS9M Pۨ4*ΒLa[Ʃ~^hz}r[~ʫ(Wgg6Jg!:p*)Y6LB0߬!mk qBY26*2(p~ΊĪu1]쪰\P+"i%Y'24ܸ{sX][ܪ¸mNBRmHcOcitv7LM>׊c},7y̡{?@f .+6Bi/ k76$CNpX*&Ĺ]5gRvkw?u}3=3pޡaqgONQ-H=SaIcKv_ϐ`[<5{O#s=OR nI1th(*ߍQɩ&z"ir\N@=H dɺr G~O/}F CiBܼ-2af:"4>~4Ahc'[B./U\oC0!P-`$8Փ0d;IFet[-.(̘|Jja鮏_po"\v;qvsw+JiU  IKAs;;C8J.18ppAWwk:`LZ2:ϊ-c>bs'MZ u32t+6C`dyv[+O~^feაӿW}7~EۨBlu0]w+wqt.Gv%'M%*ul">ۦ"(ny,x;(E&[aӷs 5iVד7qPgDA.XTIkhf+Zf¾*s8`Ɠ0ΪbIv!mÃz ȫFϛju+q|/-go=vgSQT,=^U\';;4ɶ(&aI}s#C@XSr3|QKAJ't (( .1jNchc(1fH7Y"kufՍP/ :`9[eǟ+xB*P=2)4ϳj]~])m$}?HǘAx^;i6;!5?Kĭ=d"JbT[.3n=w+'y]Bc(1ꄫӄvö rVp+#̈́㌻w8"|qJ]Ӏ`!)᜾/XAf!N{GiP4o V{\UNZzbw+99^A)7WZ6Yӊ$fXyB7ד 4Vw+9/hU4$r '`¬+^\Ͻ ^Æ *k<01((e6 q~`lhK7 f[`hTskȌ>g ߶f9g;8pCfwo8;uoyc]%37Csu()-I+0FeYLQLY苗s!q75ukr6|&{Dž sPȵ>DU(ƥ BRt{{l 4k\5Q&]wg0@AxPmGVp5azǽ&$/(,FCb=^?nCn5xgYiT NNrFply/ >u {gw1*|Gƽ#p|ā&`]h@;6;ͩq'tucm8KV^0>J`L-ɀoU A`fW@ 3!\u _=/s0/ZDaQX5^ W7?3 ֐xY>[+3#];l_Sxz'APVlwSfMk}=QU9jm]ƴeÊ%uT iڈntEG6L{&`"5AG~bZڡX)44{\x$VkblD!#Jqoo}Tub  X!,K!LU4LENO׮q Nwmi8}[k슛77TّbK *4.吩H଻>s2],GZ(coXJ P$ 5\bȯǏQPиl{Ee`ԖZ^C*@,d(IaAMbג.=(!k#7Mhg<ꨁ凊;YšcVH_5$ hY7Y{9NB†8y1469fcMsx2 jvUk- aW+_=Jܾ}i⹛;n7VqV+ؕd9bY燆cc-qft=UD |;>*2d*؟0$X&t9m )~2}!uYLaZ1KOB;;/٘-Tn*ae:_Œea,ze)+ӜTp`H>P5P(Yq%"㽏~_>{cX,.~g~|[d%e۪<%- 8k4 J&¹aq!k4BL~6 @~n38P+&GrTՃ4Dkz$1ԋr@}@?<}n\넶5L p:QBہKq4Vs2w1@mkT WNp㑫X̸3ízĝ plRpwd-hxRuDT Ԃ+hSA}EMh ܺwGo0O4ho,Q!wǨntlzp= qKǸ0acqf;tHFX`Y$2<# 1 Ӝbwů{qX%@_s=;O?w0l 4C簖\2Qu q Y0ϘO7{#߉`fX"9E\_?w'CTaWNC}aJ=9$ fصW#<-adsk^W'Zd}[:dɼ=Ð̝'3::(8>wB"e >5{XR8 ' W_ÌZ'')ILNv{԰5LSµ+]ÄwgNrg:@{8i3i Q4=T@^ZQw,h]m8=%NjIWgw|CZǴ $B?>x{ˉ m"Гo|jEZ)63!JND,(EX@XG*%tzbo !K.*KoHy$liP&|^p#3~׮ei+;i ;Tu'E-E*?McW*^qu/!KFvSZ6 74W^AC!;=1X`$65ƖF8/?EH,u5pcM<⋾{V2y/~o7Mɍǀ0hj&TC {w.Pm7v) Pw=?kNg VlG&X& H6as'](0Ԅ^;W}V"@ E7%08IPO*:x2ԭ`d|NlyDt^J!/Uhc! =[jH>Xo1q"9Gt b$Bw !0:HK-1Z %')1R@)sPc\%ƮNv;Ln î'LhS԰eׯ\=pzBIJݲ0v;~Gz0QrR19EZcphϏSҾ1ԡї ΆNӗ#-9D҂EM$-߅0_5߉tt ^Dwg|h_ KAI%FH"t3c䇸I e;|z-) !g\w/I$FnN8M(gq;xfPޖ^FK614ńfv ўF9L+25CƔ˪; PH 7꣸uT\7v=Ն2m @VL1@?߄oLp߃j :*vETbjOkn\ aK ݄]%"]zMJZn\Wpږ{xP7Pq%s/XKڭv8ٝuW9rh·tt{Xn/zX2%@xC?uO<+fdt04iwt}&vr`[/LSjLÒ) ch,I `@dZ`O5ەkgDM!б N&Z񦬵xKFq/z!\-uU<Y} biaYȢs`S:2d1MN}T zRqt•]LU  Rp2Tj +x`'EN\|G|C80cŶ29 !xYQP,M1B gD%__y!i.ǐ~o'xRO_5F(ɧmǐq@KNvg( }9uuRy>uNϥhE#.L.;}%*H9=Zdc~i,u`t35Yh͒3Hh<ܥ1 #ړXKSgvU}}WR8jgbUPO JҾ9@(N9;laI| *a?8 NS! hhbq֛H@8ہqd Rؠ P< x? \o)FM]Rf-$IJ4Hc$!%4}'ڗӪڗt >|O}p)xLYI):3^`n9EP Z(݅JݙXܕyW!%uZL ֆRɎIR__i(31ABvo-fz?9}L PB-Zܯ쟟n^BOr_f,_R1*M}OZPU~g;7J-2p9;`fBvZvNTGHRvbɮꉀ;-餽<'8-YNrQoJiɱ)601MfFS-lSLY9n>W_5]@w߮'7#:urkߘ`Q!j0sYS]2 JB- { "#Zdf{Cb 9p]<~1L;Pf>f!N'|]Ħuvo<]k(%]Nrݒ!zUhgL=N0QnoE Ŷ7]| ,?ZM? 0ͱ\2J]Z1&W0N&t*`Wev fe}Nw׮pz(R?R%@;zOyyyjLӼ"mO7J aY6(,RĢ..?K@wgG돃*:'rV8"C|k9:@Z95 )ƻ-Yc ^KbZpz~W0fۂ]L8M(E@ SS @bmXkWv[ź56& ө#E' `WHf~ <|`$sF Hc(`z(hc{3ӇWWc~ &ҏ?ģ_zzkQz:OilĔ8x()H ?V~N i0$ q,oˤkb2D0 881 꾗sW1Ѡʀ_ G0Z#2[~O ^9M˳~o{ܯv{ +N,e Ɖ@~#̷Lf0kƟt ^jPHPkX`yJiKqv1<,Q lY{p-k`4n̨ICRx4{dm/,X/6nx8v׋|M"׏<#RV!&'H: tl\[ .c.c?BQMChwLܸW=|pTb@#> T &BME-=0s<Dpj RsS]l xU+wؑ\hpLwtWwН@]SrV$ QR0L7 ̄yg8sqfȱ/* IDAT3+)n7΢5Y~WSܸ$nq᱈1GukyU 8n9ps>B+@)~&efǹaYp'3.OF{7{7f.ǔ.oa?s>$h$&=(fR舜][t$qZ CDh4< 9ii ־"A儛2/xiVhgH2K:PY3E0P5trAKOIP+83:X ! ogmNd]G${@%oi6 DrˡS 3le3 $R/7J L [09Lv;;#;8e>-|:X;9'zRe;dᛏ_߅s5Bh CC-Bz,쫀bH<Ì,e΋v:Q|vv|7\[.}~ ?y?yۍ_g 4(CZibl+p+)gݝu I00F$Մ:}QoL+'Tf4M_0], m̘&ܲKބ9LQIIY8čQ߾Ԕ;^ߝL*SnדFU$~~G-S`eP\s7!Tq%Kl04O%t2yynW*jaLd`x>c0 UOܿZZğꉜP(Ya5YXYW&0+[JލDq9k_/?Zh꒏?Sg?ӸLe~2&z3~_7G[2&VW횜:9z^\"go k% 5V kϩaT'rFbHHmk=8њEB $K1)ck A^^ŭ|O'T̽[SʬAm:QuPtZy 6VsmO~qkhhgBD cpW`^Y7~!챛* Lys|^̗qd{J`9t•=~W | g )BVZ "|NXA(ʇ)l- A[bVH ۽ԚXZ0 o//ur~ |[GT^1B$p CHv^f:4M̟Tܯ˧q3 (-kՅt{+(KZߵQ cniG!2+7.tP3,`RGV@A誱.ifό!^+0bo5SXf$K3Q\^CV{ (Ē! Q T' nR])&L 8W ˂ü`>.XZӼ,! n(dWv~َ OנJ- #u;hhcAi*^ :k7ݖZ]0R0CBkG~u_/eKqNo?ïɼMBv= <ǔ-Q#K\,mjX! F5jok\^ 68J@,͟1)BhT`3rHT^53˰`fkmN׼ >54K~BiW,#]ħ0;ijV^(ezz*@'w@ A#jE5s׹5y^,-cHb&P-Sɮü.CN5ã$`|ZkNw<:踷z4؎`&#@hW8a]0AwIԴlʽ7p>n |w#/e 0dΎƶBcu"3k8*kzY ]Y0+uQ|l-7(C -Bv43+BJҰHwKHN;7k6Z:ڒV0a926V(}>FҾLN@,1pS@xCeO:ڨ؏k(}R{d}n!<L>n[MMDvQ ŪRsM pCp@ i_&BE )<HP@ †ʏ eb>&s 4n>}ěWt~K%@ >;j]MB϶#7<޷l, \`3U\5=_ Gl27ԿKpM'㘾Ogtc]ނSʴ6^`tS31`;Sd>@-L1J녮-f-…YJLN0#[s.KŔ}QHiMõ e%/-.'<]Z~'Yc 2g&%yGw&S(6io]Q-?zx8[- A$ vf)@Cz+ @6)B{}6kWf%Šߓt/~k1qw~ki@HۭPR+ QJY^v>@1{khvu1p0Cd5{sS= aXVF/y^0/$ NUUSB's]nj[ZM=hi)D`iyd9A^Fn]3s)F%2_hY:veˁKpۗ.ocG/Ǹ(E쉍dOa8fZt<.`ǧ;r~ m̹X@TDcz1%*/ؙ_a ?B30 -K1ƪ ۦ0x7݌)Î:"&k ~ =0ܙVz d;m`pwj`@+hF4K\j?|@4̒4E[SZCŇA+;ao[ E2LzpPJYũ-̫| 4o[9Z$ou<;>ԯ7᫾z2}}eĤ~~U?Ogfp3~ʹLx>9ogsl4iҞ6 4J}Ltނh(t i^|g{pu pQ (٩Qkb[K >>%i^u~t~`xڞK%֩7[DxZPmyafL59ݨ\ږG76@DQ/p,{Foh?Meg%қ``=4ݶ Y^q ,X"9[fh XáD hbWY):j -@FuW2xE0V_$-4ǻOW~W|/2KIߞ~ʫ~pugR68j3iQiVdezhEwmD~5b?@л%w{:w?V!E… yf)_"DZ |9@\A\,D ׽3]m fd.L|-ZA)n% _hxMoI!Kdljҵt;{[Dtp˳&VK%@p8B;3V냘W 6i/W(hL0c"׶;Z>}ݗ\Ot >?}}I6ᓘ ̩!B`g4x$!~pbTY $!m]$Ρ ȎaS1{`)US͂w0pm6h}` ʵ*ѐxD\jygk1mhe`y^/y*U3Kr*CUgRWl$kBiY@m-aQ@ 4TS-Z^jAU5jѓ h29ޖ. s76rYs~:ki"uΖŝZmXƞ. {/R pGooeM%?qݻk\my…yמmː?i}O`!W]6ô{7Fw4EQUnd.\@@ޛ[F8m.V))[h0X/܂R6B!Lzq'm 8Pa- > m_o:$V8Y% >;^d4ewL#W@mﬥ`W̉H샩`Uh鞠JN8 E|>l͟|'AĠ*B80(9[Ӑ 6#' 4Bt8ܽ}G72}KJ]y`%X]kAOT;f4qo i׎UӋLyhh &th 9xWUm+g2bQ%hwo@>W}L('a6.KM4v :/AN肿%M?ֶyW5/\`9Z~/X`5e 8V9&pA 6ضB!"O'Vɻ$,@~%u=Fc;>FJr`elXq8*.MO6L1 <1u,A)`ûȎ-ЫXg1 +R ;?nL kU LjI"pgdžüpX)o^tف7M})ƶ#<e`yC 9 `;G2%@x ?#7>s!j9, q8/R7vG6B8l\˶ Gal jv&*\8e$eBdZWgУ- :ئ1ҸBrFr U@$v5_.KQ 0Ȫ:DC,KX$ 4KM-7d}#, {,*;jYw\$NTőVGExᰨ߰,zq3mEA"' RNNuvlOoezI%p?gyv[UF mq9bf֒٭ %+#2^$P:a= 3_Xp_뮫&(2IAΰmoﹺ\Eq= IDAT4z͋iĔnN[$Ș$8gY03Y #{<[*i9}fCNz=7cADߒT1>F榱"tEf^޳m"˲`iFll,@> p\,c pAmqf٩0y1#[Hl)@*̢9?3O}/K;]Z~?ƌ ,ŬLi83,T.fPt%iRp4eSo!XbCT=Yw}gOBbLmۼ;?r>[s i9ٺn/ 4)V ",8{ѽZ^qa3bp!_lY]W%`7M8ino"dUwj9*j>'BO*H!K6ngha@H{40*dA⸩4w;p=SBTe+nW1_ Dx 숳󣀰F8<"kc̈qHF%4`~#͏}cqw^j҂og>Ks=pTδAoᗀT3c&a{=$4Va|O/ķM7㧜5|cIEUڥݷʼnR^nmmE/t{' G'/X:1:!J'炲S"@`QydBxozkވ@K uo_{zpq)l틱&l\C`;9[9hƫ0/`̟h~Vl^3bD]B4h~Ak_;2K̅ü9ɡJ%;wCy߷\3]Z~h>y73U_C&_Ö& Zoʂ9[hVl|sE_(%!U˒E1Gg(`Ķb kU.Miq!TSsnm<}y!VF&بiw+%y;a$G98HZ^'c*'S~/~f2s::_ uF׬F(52Eijڢhm%Ahkf兏Eohj%8 gwf :dQCu\mȷ% IEF3Ȏ``^f|_o饞ow.7wͣIZQL!Y3iIc/;ލq "v=pr)10j k6!ccM}ޚ2ƛش ..?r`JyM$v!΍qcvK>fko]o'`ИTU'عaC˄F,jrP~XSDT!``1Y) d K.1kfkY]AڶPz>knKeKd H"sp<FF?Łk*`cM?,FCVo=;w}7~ƐLPLZ_:yPOMHܼ/[U{uO;)lGhq; H%1kiEڱEj[^kmsYZa,ȅ@0J1V8]'ƲjZ׹#ra׎ KR!iZu X? M503%i"* L8pzCS%J -zzdp_ Pi*#΀#4)w3%XXۮo9<|}mpQhiSUQJiD)i@ /,Ȑ6J !C OĐ(JiRUhG[MDV}R~{c֜?sεϹƀm_^kY7[ }שR&'yF!L)c;&l&l'4 %!T >JZ% `dA``6Y, < Atr1Ӻ3 ؃% UiuPBmAt0g̓}l  nu l-fFE] lI~mlz\ clZΨ- g E.hn| =2P~Έ`mY) x\Tr:p.͹E?z )]b u{Nd҉u@ݟl>X}s?Kk[RV`RǾI2QVJH%c:a1C>0f*Pl,0_~}AM圜ճݿ>e\\=K{Ġ^㚽fwGa?JDu-%ś$q6jW{uwF% LSW͕srt_7OX“vKQ*Kl?lSW}D$JYҸn4o@A3 ؗNq(M"9Q~I%D:`u (1"ODa;% {P."MQ\EL?q n 3b؞n/}䖛3&?~o [?szT9_wRS0fr*r*jVr ! e܌A[`: c ΂dA8 tW.wE$1FI 2ӜrDwҶ]+{(ϬPV@G $ޓlJcI,+V*2{XVKm d?sN8rg?[l'W,a0ɯaPGEi_kxCHfkH(UJ uT%k{1h'Biޛnv@?QUN\Sjp^~ˇ`L# P-RSǮ'B_WA5'f& qvPd6[b$] ZZl3D.zsƕ+|[^~K,jwWkZ'.tXi"+B J7 Y"ZgH3Un&" S3pe7L ϴ3cnn3yy~|W )88{Ls^~ G{)go&d+`ONDÚ0lS}f ! 0bČXuuaGe 'Q%hiT*ry&vyK@0ׁK w\f!o!M 0̗BD a:Lѥ{wށK,,aqGp RυC&Lټ7d8g> %7ٿ?(!m371clj{-p% DhV|X~wݟە9{kŒA3gFJ^%6=hjKH TPB; ~v=qkW<{ A{;@rҹ]XU.jB/L\ɒo8gSJi~ٻ2H702No~IugPv"]Ym;O* :ņʧ_ge%J,a_!?J[$bCm߯s\YYi+}@Ib6T5m8~ AJ{,N,E0N6KzP`갿6TH#q:*ro&wT۹S &h S8 -\&F!q+_DK,qN,ao8~q}i/ ĵalMfQ<Ξ[;bTc]7-P(~G@÷&fzl$v\z] \ۣ~B\PtDS#(" QK 4!IV'K@.gA1) 0J>TP!MޑaC ].`G\s^܏\z\7mWzd_`@8X)P>C2 R. blEn lյ U;t|?}ՍzX ,M;>to/<'IA,p$pd ĮhLVR0H]6vWf,YBu?2Kt(kKT?&zR^h@Ey7Mf4 4ߧOCGPm7%NfZ&3O]Ah5Ȼ] cC*JlXfuzNsVD KwE:Zju_M3S.˂jC؊ȼ&@ BTq!_T v|{$aa+=,ty͍~7K|XrOw=/tlּ2mo4o--XcѰWMYpUPA"nv6ψ,bJ7 5 B!!o=yYl @0bm-ڈ v~9ߢ|_2.Կ.Bfl%z5϶ -{ŀ>{^cu s&֕xw n]08X.UuR"Ĝ&m  bon~/x֏{Xax>+{QOr bՠx֮y.ksKJSdh|3R3@/ o~ʵu` ^ZEG3;As}b"d7M A)")%lJYM~笗c X\Cu1lX(T9iSK9:@蘰gaG+tosJ_o%^&ZS)/fCb ͓ಟ`) ܳ`v3Na{r/{^%xbK_ o!XrdV8b~L@YhHNy (`QBz[pAj=v2̀ _k+b.fߦ.τ&ؒf;`dV?ҤDEs>ّe~}kE PU.6( `ne1d8%T{%mX"@a1WBI5ׄʗ"0ʎO-CdU8; v]!eL"=pt{NNO>ڛ~uX[ ,mu?_'GGO 00"0m+ʺTu :q4&XevnL8"I10Aԉ 'K:v3`51-M6#nH΃]2)UqC"h{ڔ|8arvf@^t;9>~_ν X ,;>$Эm"ȓ&`K$$:%U$t4 .l5P셸9'@$J *8p[Pڀl[bqv;`̀-Hup2Cڦ4 hm)(uɢ'REsդT"tV5r] b&I}`{}Ԓ߁#/%Ŕ/EV;`f(Δv&$3Q)l|?_[,c @XݟN)_P҈Smtd$}og3#|g:ʵt;YB)kzɶ(V TV:JttmpS3K -w0n'-Qe4}u8hY'#q1f=TQz2&!Rm vks@8X"if3`;N]qFwHmALP9VВ r\|rr7~K,m?{y]LRUڤNpzɓUF Dj wYe![϶$wpQ 3&G HiA4Kfܩ]/v^uQ灄3yًnBav;@2ktIWO.YJʟӔKhb] ]R^ Pmn,9e2B>5R>XE]j6IDATq҄w`UJ*b֜IE?'~ܐ&YoysؽK,Xx\ϿS {CHS"ʌ̧r2k\ څ &֩N ɖf}W Um,AC4ڃz ٮ nGb~L; U\Q~@`;Ml YZHvg9*hŌnDS5)ct m}_-/Tz'6HJ@A\ G#v{&-_4`H]>ڽ_a`?dD:f.1BUzOOO._~ԛ^?>sіX1 ,񸋷n5&FAIb"dQ.dMJ$`Ŧ  -p^%؃3RF{a65Y2E>LȂȸ)wD Rjf|J)cJ6AlǓ_ 1?]؛nc]%vxm}p:?$ư)w ^j/<:`S@  <|_f ҅b$N!(ұ&!'hwֺ}Z2*g1a#U 4Տ!ἄ31 ,֥V)U$*bBwtC&zFwDAKm%A# X6fz _õ9%L)Í`n i!tU (tt=[_r,# @X;"?sm~=2Pk5ζ<f脔lEKJ9w]@ >W ?W6c(e"Z&);g@. )IzY"i^gBXŁB΂aZi EWCAL0߄+bs(&0e$k%Ȁec^3lnG9ޡ a AEKZBd"{0t0$b2˜P]5{kisz|ߓqzk_|3K,X{_7_EODpW.COA]K" $M?2 D\j n?,Dz tܫH0#$iRV&31& ht2vZ>RBH)cL"4BH Xh /lLz+hJ\yTD e! ;RV'LbT@Bϓ~12V]=aَ [Q$$8n'koúXq @X;:;ޏsh 5Bϻ I1 hp XZr ZRhuu愫 IطӚK ` ȸy- h+_Ɣ)%LS*Gg-]C%䤣SLުHTl9Q@Ʈ/ڵ+han"`[Rqd^U9Xu}dL0a 9@Hvg~}䛽X @X o}o?ݪp٪L@$ªg^eN?Glԑg@8b,T x8H(aت|v#sa;:)A}yrPyo2dVn _dݸ#8lAJK~ &dcDlZr:ё) 0}1),A*W=V+RcqJ2p=N.8 x"6\  @X o;_Ba׸pXP}2PmݫcjrCm!TP;] WK 1KdWP8&lFlBI,6J#SZN66հ>'I2)V8d@$ T*%zJ2}SLYA-Ar=`ldDgD/X9KAV]NN/ ɿnO?׽ =XOݛЭV/>cC?hB*xQ;#pej},ipAň%6i/ 3irvNHs'>L8:b;esE&k- /0͊bm%rAL0h&3AklK"bB= `5t&Ӣ (됡΃>]ύAfe3r5hkM<2ec;28oFi)}ko׏ͺXO3!_mZS:FG1P%AX'83mꚩa6(#+yAAF\9:v3`pxa%nfP e9-vϸp^LR6(z$E'dm$>lJЈHkbϡ]:ܠ @T.bNq~.KF4痊Z_ONH_ H_|^|#z#.<ē:v~ v:3ԭ;gy_XK4o f?L;n*̆p_nm@Q;+ڥJܺ3<˧D2*Nu3G[{y@H* B tQle 2dc)7a7/Fc){pGps䉾XsԶFQCN:UJME,drPNlNy?%'y{;n}|%N ,CĻ?Lȯg߿xňZnpOvc 0pV+়piɒ%9c7.8ڌd6@vX YY%n !e cEcQQA |%ϦU}$ | m̂3ac8%$k_\;pj6m:캮8HC7}$zx2l{w^o{ t^_:::qK]L x_9߃믿#?o[b%~EANIENDB`ukui-screensaver/src/screensaver/assets/underlying.png0000664000175000017500000131627515172041106022306 0ustar fengfengPNG  IHDR 6_sRGBsBIT|d IDATxieU&s_ʔ,ɒQ`A1P.au/0maESPT  ` 4<`Ye˳diL?v/4d k8q'",?w<yܩyݔW̏ Iy>,SJؗUa'뼣*򤂠I, "@9 20Y9̢ AtJ{b5!;C[w =?u_O;",<XdEL^y n;{׻O{wٻ):yNwew=CDUBBD @=?]U@}7h;o Y9gH:1LLA;Yڿⱽ;wڏ_|`KxS8͋,( @XdQ^qN}{vSO=qN޻{N:R6eʛv)9W "4\!B( 1F<@  >D ][@#ZMCVw;x`g/hyO>~$,id,)?꺳.o/N޳wsԽ{8qOH9We,")!Px1;)]υ%p @=kAрA ~n>9?beôZaw^"hF=B1MрKJj'U^y4=սewVo<1ݗoEY"|xo8~O;N|h^/|?5O|EE85ouy~]>~B寪GQf\aE0U򹙡pPיy0ŀB @]s`ߟ؃?o~bO~E~E(sp'y8v׉)yA Ta +l]A gV5DDb1(51k P]HlJ{WpP`']B@N(9qpk@GܟܿC\qW~?>Ű"3Y"T5|Gn?cws;OfQˏ1b=Š5%`0 =W(4.)Xՙsy >* R9cZMH)#k􀆳'rJa4i}PT4QYPN'=zdy3¥ E.WZ~~N;죷?|)y8-z;z0+ƿ׌_] YczmʆbN R#=T ϒ$7. xaRsD`JP6ϮT(ؿW9x#\y=?=wN"< dAnx g<=|ggߺT\Lȧշ\q+?v#sx=[P]к(\ 8قGsq;GU Rw{lqke&3A)ՎM(~,-R^rdkTqcRUh PWSn+i&`e (1x%ϿOι>3b4M gCy=?zg'" FIRy6CU Pyr}',,P ,׽uӛn>ǯ?z_xljTZ0+V-1N}ƌ6r櫖-׀jmbXJSZf|8QK 1.gV,C)` &0Sl蘑M٘# mb-㠰̍xf7 )C:EZ+Az(~.>y|}!~5jEES]wÝ~-w\xS^~/ G8Zìluwp5fTrǭVY gn UbFDk"f Ypp:PtV{j]0qx`e(Dڜ!14@V ` ! ؄Zk@qr qw̥{g/?Q"<(d+V y$z5\aT|˵=PRٕ1 6>`dC\ho?ư=s ).12nrkl{Gl?]DVdOi`3[2(Gګ94s0n٢B>-nQr¨SJ]Vdl(t1C\σg&M%K Y\H hMj #q#kv r#* XwNYVHkM :2XJfa7Hh?c#j,#Ҝj=uy?k<;Y ,_|{=>vǓo;~w4/:JBf @yFf9v VBT:\ql ȌAxu] Y93(rӁ"HMXi>קGttٹd+ Zgtuࠎ-k1'Gf͑ 3:_:w__y_aE`Y"\W>r?t#!eKSlfr[n[Ql-mЬ['.r."Fmg4#S<[~nv51߽.$-)\!T`n 4[l-gcbJ|t0KfI-ilEfeE;"a A]'sS<- 2ŀ~ > a3u5M8w\C)RYzC,՗}pٱ߾Ϲ;'SȂje)NthsP^я7z B˵Ϡ@>@ Y,P8f͊jE/&vgtfJۄ~~Ṿſ@Ur|]]& ,)?o~~ѻܣP+{[6D1:pҦp7`SklX#`Ų CͪsjOc30^sq ]W\ys~{NJ3R|0QC[0MS͈H3/ϘVC?o>'"(gv[} d*f!]<|syO=ُ:Eeܧ}9߻>'c(vRRombE XjڪxwYٺ "i{V5S;c %n(B?>J+Vv8VU={`^o(Yf9RY3c*)2rXIϳAX)W'-9~c|ʘR-r M!Ad@)LZM5H e~1(vCXvXXj+@QHB̚յ{ӣ:g Y> ,roַ|䏮9z3(nn )wVnƿfaWìX?c2ij-}2lt|9$_h~~~8qAB,"5-tX!X/+z?ܱc,hϹS"Vb>PRq5!;_nl#,Tv0Z uJ^Gcvf޷/?짝_\E$e|R_?ͷ?]Q5&6`%6 7)rpakYQy4a&-lfH*kA2Zu. DbCإҜ0P."ܥ6ffZe)q%R Uq,-\D#K0mDhLǍApJxl116R@4HL Lc Hli}#x~乏Y , ɫr.{Mz;t@eUQE0QIdW0*VҬYa-qhӱ7. GeZCusdQq̴8sY@>9(cB)(1>(pqBh58?Ih\{'ƀ*cvAʹ6N`Gw507\PI)%mȷ?;u{J/WJ{7 5X2 ́#`RJSht[FSΨ )3>Ơ(ell}o4en튱t/bAbk:6㊍9G&dTxNC9g9ہ pgB/Y. f9".kbgR-K^ߏ o^KQ>t=}.#δ8.݅ zf (E eQ=*oML[~?c4VzY?. 3}!ƘL2?{9 p)(HL@ }\|2Z# dn g ҕ$| kQRd:no Nym@Ls;ւƨe,vC@p48By&bvg#O>_tl"kY"U~q5s閳ef50R(#+jZ淩xn Gʙ44Æ+|ޑ f{a]--?ӹ g1nϜs-Jt,Y6{s˹]=L}`ѯLC  ֶjHd`kd.`Ikһ}ֱ&rUQٶ6ɀ@ dc9r~ҫ7"KY"x+~_~[>k.y.ՅX4uVL1Z0PBcJ6ڶVcz# eH|YJfTh2/JaS7/f8BC}l5 4|nli(L.")di)ep67Z۬`c+X!F <׏&y\MzhX=>C@( _3|#QM\<[OeR)n2 AtHٿ7 * @2lL@ؙ&<̋~׽3bKsYX.U '׼qNpapo b+`TG)a`_ wfй7`Ql̶ѕ׷yUԲ~>g4&vҬ.C}]3T&yD`iq `n sR1ƕy.=%J'ҬjU̕ ?Öfϰط7?_y&MB0K;^~mHWMU-4,¬@dkjbK& EahMw)rfO{eFH _8Dj&ib ܏dB5``0|R{1xX9z߿+#h*8fJQrf?v.܈tE珱!9g@ (wJ0P7EoYʭtqd\id:gk e'{c,3(Tf*8xg`E@,RY 0,G cX(y =ǿkN0W^~űyϵ7?NDj:SV{~J;R1$Mִ7*<=L3EQ.!tuk gCXu1Exm}K-8EW:AwpP+I,`#.b[`ʌ Ya+z&H֒>i*4>a(QJSh+/gTu38i얨3Ğ%0q*Y>#14X67~b83mΉ Ar{Plȳg/9 yFݿ?uv[.b=ͯ)"Eb4k:ع94Z"Ҩt1|hy=cjǨA>wBKO L#](zz.y!!tE<+a[+QлQ{@o@+L mY<zkZٷop"-[yn' cb`4m̭Y(6ƅ VlmL+ kRMaQBaw%,Tf(Iↀ+JE]>;sxӿ <,dOknn_6Aw5ֳ~'R6KUs6n+0fFjq{]yOOF_q3  :@E5m^Q5t@|gb/R߾1W4zAJAQimpҾ[ Ceل1-r{v#%  hF[-k9Jrq\#/:xq9!({15Q\^D,ǵt;K٠"Y~~ʺYQGQG;I8 |˫/7# L3 >PddC{664Q'w?%>έ f,T~cRpbSu{8HsTjX2 HSU sϨ ;*jR%6.n ҝ@{OwO>W%wW %?3V)%(u=@Zam YdtOI>^D@I1-C5EX& p:ϡߟ1NT;гlrqG8hsNq@!D@-Ӂ8猃O} n/]<<< ji>POmzڪeJaY,€6&n+)T1)\THijtAj)!3MP!">P+4uԹ\%{?⳾&PVO#׮/бo{V em+B߽.}P˅[c"ӼJ$`inNT6[RǧvF:uQsB+lTct 6߹7Sy^C>c,*ː ~y Kff"2XX8Ś,VHoug Fmik գ씔$R6pπ玁`@a.@seUYgwPm$Y/g͊HeB@Ddr#{?}"Oy߿ܷ#oꚛ>Ÿr;oEȝ}31"ͩuZvV;5v+UF$PctNZٶͷ*@WzYZZ{:#m+Õ[5dVe VM@50N"~?_D>} fFϥT}L~@%Z+Ml͏ x:}?ik"}܃?< 0 ]փ?ۋ;G?qGCZ~?}-wXƁy+_v >znwc¬ b^{ (AW<8bMPRGklfJh5&n|\a|5׫ŢoVX!WĂRJBsp %@ T_X0 -o;?CF i׍jz];H)珽1XIX\ (= &Ef}@|bH)a;|:뭂 Y\oCi[A:YR=>'6 >,')s8g|S~]_l΢}7!X`ٻmG3"9t]?J(p;8yo4F-fָۨH Q`C oB;$ZE󦔰fR=Ke͘eiHu*0Z_.ғsDJ$7[gSN!9 |h,j }*hL/rs3S3ΙJU>4GB}mNYU5Sܩ Ytg0Νs5RCb ;8j ~~g6č,fnZEq6GO4F* \%=S|^Bs{X\?5dt;8/7fV:5oe괮# ZBһ@FzL5㥜+6ALx^祟KE^ ?W-L76`&W0؅`"Yzrհ[ߋiJ (5V2ʼ1Q-sbZ/;@zz}*`D,nX(R):.i*j%cc}}N{c<`xǏ7mOF2BѺ"*J\@)X=s)Cſ]B .ʳn(C@c4B q K^EoIN7Lj~!NQ]e%S2Oxi@Mv 7g۔QQʙ(fSvFp}׀?#0sŰKЊ<#y=&6Q/+v! e@@kARYq3u[ (*(L JQ٘;gD)k-G mhlX(X*L:X{T$Sw>W"_:V0+ϰx|ÏlcRydӫ>t_˯t J,<#0lAQ/8]kܝүoXfo@ӎUnk E1n^nedoJЛݟ:$W u,gel8}5CP9?y]l3i]j145ИjAЪR2qgU Æ)lϬ|7y{Λ~:k)V֬*mlwM֮vݜ$࢞O]ѬrZ$~ g:-g9=3@9_A4i{W(2e09՘-ͬs@0MHv9g>Y|n>Z~ <n@yɲedVZP:P22I Ti,$Ö_(ԧuXV E{V'damL 95e׊մ <wsr \S.; PsGHE> S "ପKb䶪|./;ʪ\C?׼Vj*Pc@UbXiXm86 ٧NOɬklYMϱwsNN`Y`iSSYٺcK5.brdz6~ BZgerD@]Su lq#0Xk3ZvE*O_>/ӺTeݨ Gqާ]xK>6EOY,?[ Wo:~Ͻ›+ә9;M<瑨1`L 7HpLYZlVv#{A(` h\ʭ_7>w_D5gmR#90kЕt3o@tc9m{i-&Ev[Bhs@ )8yW5PUS(;w{:մjsBH|\ޫVF2(=^_qQafdp19smVt\ ƽʏ1 =hk=[on`TҺ|߳G~\X <|zB_([|Ck%Fk2[>6%[n!=є>jУ7u7x4Mh SֺхV`swzw.[y>Ό71MoU9TI\},~3YZ1NKEZXXhx326枞<0@ SYZ}Rj"Aྐྵ*95*={`wwZcr[ dhs[kU MZLxLsp1@-]i#d_Xq3>傳#/~7&}E?]h)QGh:/,@$*bDY\6 IDATq^̬U~o#qj~7o5Vg)-(,V3{mTX6.hݞa+K1FoӖ{o-YƊ5 1+宊X &=]xu=k9r*XCI  ji8x g1EC->e/8yg^ݎEWY,wխO?{כ?t#z-3։[QUhݦ)} Xh28YdWŲi) d;^ǯ}`h (8,y_\5Ql=RrEdͶs&ct(}"}Lڻ@{ J͑`=H>=T ;e/AZ5`ES$+\k5c?rO٫1㸣D^7be=NspX5ӿب`UTkچ|delTg[cƭdh{нs[Ic V'W ƈZ0>#O||ދ>3߉E79}"e?W\y[r6tB@+S`Z@AiBP`(灖~Ѩu0S`VJ(\w:+ң6%Me:n;TQُd1X} {sZIY@@s/8g\E} g8M+p&I>1:^37AG_L;w[| ǭ% Q!p HyغQxP _9#Q֬5+2A"F;xkZ! ieWU m)ySf(ۘ}u7><ϊ 9P3]_/aӭM|m7^3aM~wқw+͔)V @OrpW`I3JP`H?̬Pq_ y[k΅GA@37TH̀²Z6ipԩfpcm)ؑX/5̊ү[uQN X{pg`QQ/X^*q+k%a}\R-zgž[ "mr1{e#Lm9q'SfJof:F#R9QJV3%U ekNK5Y*i0度̦ajkZ SEPU!sIͅ^Su7g'h2e3U ػZ_~W_MXS. @ʿzӻ [L*u߰fNlYTt \fXX%6gkfZ A,۰]sQNWwHXYF4"߷W!}딴E9> v9Z:`GZ:u@6{KۭVܫiR6}X:w%|?Sיִ:kH߳dx[V3pL6UldTyBFZԈ=mp U'j\hje15&6斲'\ח~ӿ|Jeb_w^Z7+)lȹ|jGrS G7>$(ӢuKZO!T"ͿA ɰnB1sefs`T737b(rtk' ;5UDEPH/`hqx5xZ3*yj1%H}JɀaRZ;)Esw1ӭ+W`6>גgTtQSC j1;;;]?r21r-ӽ34jQTs Is}i^%H(~vΩ?dYm5\rë+2K)ؕ1U~{UIi.VU'wU?g^E>eO;/.Fo8GrZe[U1nTUbȢ&m(eEnBheqj-RcǍT,o۔H7>>g"u3mjnͺʹ$ak&(cf|VC-5$@nDIњA/(z Q@n*9KV-7T==o$Ga&2+|\l`0Z<'6?~6_sH{Ԓl_SD s006hY: C6=+ j^$W*kHTK47Ee4hn>3'~ꋟĻ6",AxӵW_ѕ/oFL=m8r3~ߏ`V""j탔ri6)#%Ezd5*P-"YDEqƷ--Nx9-]q?9d#Ύ}gC:2K{cIT=1d{˷%{x#DQyڸ˕?Q~'z6-UA|1&23vkeW˾)xafqkb\b bn/w?΅Ɍ[mK6t<0X;0s G;O'% po-m(JxPh1_6-ݹxC7-ˡ*+*-+07||:` tVj rS񽱉&i]=bs9uzAPQZ!ff5kM$+WnZ[6:Y9@ p{gw=cʀ*dPIQzn|"2ũ{{Tosx9SxRȣ sG6N2¬z[:y&$Ush )"0XB,jfd*NJҕtaÛR(ͥ/gۈٳHURabFWg8<~~Smn(fYgs?ZX3)e{Jݘ–,Q95߁vi1@s^)gy+y;a1ܓ>5wq|FH s;96g*=K꼔c !$0 {^5KJ} ?W[qͥwm4]8m,F DA\%uj޻T@k庠F@h.V{Nd!*r@u>ԫ&_Y8)ړ@4݈ hwlcK`@\},0"{BΆG=]t,UŀTU)Hn7ڥVv-=Ge,~°yw @U7j Hhޯn5?>t; qMdTc`d]t.7F|zw}^ 3ם6̀7!RN1PZeR9k.,?_^H}/z<_k7[s'(wuͣArƁ={YOxԥOYIʏ9+^e^w+j6[#}:n0|.[6z:cR(uV8x6w'CqLoҵ\7M%eџ8Y}Z߄:I·`c᭦xdU"ЀN)Kbis(jlEVڻ 9EOGͩZlڪQzK˶ZAmy^2Fc {U9 W{wpz4}̷[fb kE4eP e1Y{ʀ7a Z yڄ[?OljO{9|S|²> W^Koo;+sTh9{1/w{mXбۮmiMb.buFJϏW3|̣/Y }u뼕 ⪛zvY:Af nu{YY}pc썲Ȣ9%Vf;;4zτ +[CuGE? :o^|cA|oށ<T1@Fўscmڹzwv+c(V>z nZD-O  Q6B5ƤM0SQ\S{ֻrŵGe\w_3E_zG /b%H{/V4c&RmO<6*&xqyccB4VD[(o$JQ=ܽ@by/9ԺkuQ7;v yQZ)Zw;HD`*Sa RQoJ5PDy>?g/Btr+oU4<3hk+y˸P!.3W#t1?#SVcʺ+b тW|?\Xpcz6&??8%sNR`7\VA:2n^Z~XZgYmn眐tlfmue$U\cWaOH ߺ׽ uߥRkT}OYp<1W`^JmѰ_4c<猴K2fϕw WP=̾0PF2ZAZ[~.8?ꌄK{]Jj,1o3hVJ76`ELi z7Vs6?8ůfv}3D*My3bAėSqo= rgc+!qA6?Qk;ٗwzG}Pbq;][4xSݏ\jHϨ0^?փs-Xs/>eO^ݿE>n+\s C].P}F@S'뀭ޜь~^m̥Liz{SQq 0SZo2F8]hjt* Q5ʪu3*rb15 Xũ )2)~RE(}*`)yرJ4"`7o{˭1CxjHSz!Dz=gʧгT<Ϯe)lNc9 qYmq|rʟ8Wu1\bksf"V0UM@K68?9،39Wd7? ݀3sF'4M@Y-r2}/[\3Wb32 t3%hNػ)Y?w﫟| e+.eW_B;xys ;uFVH@)U^jRζO:o$y"E Tu4`wiwimLS%"XA#ҾIz5%[Yu Dx3ڀh!Fv}DvsĿj!Wf2(@&7^0o^ϘªhgOK^x-sO,V~_ɘznP dB7j "J,Hfh׻R,z^&egB&5 fUoc4֥67}[z^./ե0YvT|X2ikdv*))ʍesyj$V5wFBۥts3Sa@)@ ~BN`yh9ΑN w5,l|}S\oe?3kOzC\ 엮x߸.6$/˦i?)75tlrq?z e`^>R>{}u-@yV Nz_;v| ~UWڬZ繖"FMkqJ @eFLy}X{s87mIA0)aG6{TP){`̉^;]!"蚒A1F2ǯU>; Uş--݉ªk8i[2tXQ2﫲%'y9< jMsf4Hc hYUj@umnpHǝ߾㫟Mq*KGK_~%8'}apyӎ:o9۾[@B)kUA€)n)Z\ri>)o\@\|mh::CKm}te 9isZ[?&9j;b3@9+wVB0T (q 3x8A IDATbq_8IZ2(Jf@Mz:5"p!1t :h?{]v=k~R05 !iH**"(E*H *(mEjQoBET 6@ IAvB3ocuZ27;{}Y{gݥ*8Bm`a>.oΥYͦqr}?>K Y 4ѭd=LBQ9ڸ8iM̼,33,ZHeuţR@Zj51̭Wb XGG赬.G3OƮ(E2'A}c-t48VB_k2h@ {)8uz{[gWJK (=@֞yG>P{zpf\C?ùR?swoGu-Mϛk=L?ֆ/|5CNL>xTŁʆܻW|:l'`>7P9@F SHFWPA3 TYHl&O}^f`ydҌ21W% ~RL 03%o2nj,}ՇAbWz9~)VH_ Od?(ܶ9Xꆿݏ6׹1q?`py) hE̊0CX1iC[z8[^`,+.H3x t>oxG IʦcS?&Ґ]ć矟R(/\lQ AcXǠ{R蝽M|/-M$XX6yM1}=;N >O#j+q^ת7@$s꫖hx ifq ֶ݀w{s^_.~`ȞABͬ}Y(_wC>.cTgS{}ƣH s8UQm;F6wIWFG*%zδ*587ȊVγo{kp@v`kcf^js\HgkP|j *L~idh&\8[֡[?;AT}ng9;غHz@ j ^Pc{jLbyb72\P!@U8 T0i`sfeB|:40RczïHطtun-mbkݴ1p1 5O85A vT`A9>;W8-+@ۿ?'>}WU|}eKgk b-f|ߦ`(xm =p0SsCfSmu59Z{k?f!G0vu 䋮 > D̵e%WڄF!-9^ھΝifk~O mS N~fJ:].4NsИc#Zc qJTCְ@}z<6 }} M޶PHpZKb[ -Aw/WbY,eWE#_kala2fS+ ~o|헾Ls!O}[łC[p ,ϔB|d,B2I5ҞLzX h-TAC:9> z/q!1*.`LJˡiX Nvf^`c&- D4ʸ@؉}sߓitK]fv5l]o+\8| 3*־umRFgc-S @`Р?$~Vu8nёW|AsΪr8e64s).~4u=˵Ǿ_G|⩏ɜv@DSFߺT*Pm=}9{gڏy _i9` @@5ᕡg[ZqK4+ D |/RdYu9Ԉ wڪrbPk$Ŋ1Q- |gZi3K`O1gl -KG4{T\*7: :NłX;Z|翓~c/DɀιJ׺OGl 7?1+^*=Ne\+X \zZ*!"soj1R<(?LJYe[ku׽ֿgߎ] ''>gBRvcz`&~˥LkL)ѩΚʴJ22i{cW Emֆ&Mew-Ө~`ފUpBqk NaqfpČ>Ö=,nA(mm3,y^KkQ}?BJ4Kiuc7eΪaJK${Oy[ _]ap_s*kh͡Ebo[iy1Gd`̉B|`=`Vc)a>oǼ;6v68zWX?d}wQp aѡͪ8U[b'v-@-O<4!}]?`Q<J3_^+zw}w&pb6 B[5&(:y6Pk֥Vҳ=}z: zPߖ=O1ʴPֆsϵPm˯Wk4^uoJ,X0oPw L4՜6;Lʽc0!4@v[j>-u+Mg0U:umh1+gGX 4ҩ8 zt{¯0Op}G\%+^1[\kȦ̱W* ,i lۭ t㘊ƁSp-߃w9 ],4t[E3gOY'6%2q p` y_Ge^s8( sWfE2xׁ DM'7ÿ[߉Wt1|>;[5U{XIi9d-"ůʌC8w%X#րq9hkVϽVo-2%faks-dKab2T+biVyuaR0>0=2 V 2.u[E9ՊչQȯ}d}bAvQ ]rSxĥB J{:{V9ϐ(XVq>5n}n} dϺdVV`Z* [< ASM` 8?cX1ǁA{TP0i=3̪E[\q'ڤu<X,V#c)j8Tp%Еͫ//zERzY?yW]KyNȩYKP'{[bhb:1Lr/v՟;D2agZT+U3s!)ɼg2p ^NyexݺV&jjQ FWpH\ϭJ =lo2Cfl>s, |=PĺJ{^F5:>'p>wY Y粮j{}Jeu|t54۶Be;:S:W15K#5x 1:O \BT*}vʝ{!ɖп3t(&g+ﰵ-jIu0^zzg?ާ}5^a+ }k~Mz}r7i"-5K/l-AP߬|qʪa5 ac"{VRli5ާyp^eVoSƣh2p`6=^.ZEP-mZWt{Qa뾚Wfuڗ4Oޮ&q:Ε.؏{uwVkIHHuvZ}KEY^j нRk-P@uj?^ޚ?n N \*ϮwmU k3ds~Z?P,W ,{;C,RR3k9z%xjއrdkjNI3Aὲb 9ן|Ǟ~cO?~}+zEG7#Űo0EυKhےyspbk̢/BZ5FL v3%}ŌX%H _ ڶ殌-SZd*U2aJ*Ʊ׵}TKF2\$FKя:V)-_-A{$}Up{i2/wKKD}Zs&: Ȭ)ރN]d+]myN+l֘k3@83&{ 7RJk6S>m#$nh)6]ߢݛ ,`8s3 mϳ+@<"sV/ͪ ОVYgRsG)x#p ?OG؛ ^1ȗ@K3-ZömAgGVCOst4He%}'骽**u{aÞj#wl-m7,8&,F|2QgOxKw,*8sLD1plMUb^pi͊&:ۚ~L_f9.ˉ.U%g\uI\/WZE5{X S>ιf|Z> ~wK5FY'~u)m[2Z威_zuI%Q9Jy qΠ9˧軋Eg6D(uIJ;O<ܗ||rmr/뛾g'P𰅿|H[ nEVqi'Z-lVz1f6rA %Ϛ6 Q& *y0|lVc58ĈYh7q0#F VVҴEgpEKaMΕGQ+@c(;Y=W6byγh.~k?/exO==) v4z>^Ae$Hy@1Ge+\^v`w6@B]mUQ&逳 \UsPe\fz"a5 ڈ%Ѭ tU+ŵW&CL(eC؍uU_VxjE}[AGC;7)ݏ-Vn}'AƗD p"8ZkRBqƽU;T[i˽^eI?wwwfqzn-t2@iMؚKYTŸgH`9㜢ۺ^sgf }|"yn7feiX΢oY'tƶ#@GkwPy5{Zpes3;sW^g:oiC^F |#O|c}h&O jzLp/S6>-E} SqC=בܶ. 'K$DЯdp]49RAJiTS VAdxf/?y7@ \a-qr< Hd|m+0ޫ}linuR (FTTߙL\ScxpLiZdAV'q;&0q)gKolK僕'mSϖhqT {򃇍Evs 0pinCZXTd6 $Zf[)ӭFMѡgPJ˜_w4r(`faU97#X9pTʥaO:Woz|/eO~?=y 96҇g,rbf֐0 [ 96;vG|SFK&,eh̠e~udSݩIwģ,8f3g_6׭:؅>9,̗- Mܓ̵/1ǭh0W09n@6ҠqwGcvֵV$e?n,6q <ƠVs= gEB*#m 簮Eܩ|Eg҂R͢{c;}j,*0qlh;%\Za==Up~3h ʀ P0 =5#bεp93}Nչr7 ÎRJuӟc̉_F |_7b`b-fJzwK BG2F[Zs~B! vz kVIM8k0@:f! Dj IDAT5_fֈ\34cr LmeS@|m:&M UeŹ%h&Aױsi{Qj׆YWmʆ8kt(gOcz槜~ݛa327{\- !u9hUkRTl0 kVk. z=]5ooey&1p0ݽ2w.P<"06TZs]&xoMywx^/g-1TVVU&k*+Ou\k ζ@WC烺g-{)ևo°w!mֆeesy 7eJ+ZNs:fC2dBbc Y\ȴ`f+X-(:Y~.'iHbE]uwj{(}PG {U$٥FYg@u*fՌG=5*2jhe;u۶E@8<knAynm+5l\8@&ڿ#^TW0޳q[Döhި/UUCTu-a}K3^Oi/z2`(qF@TcIduCMt3V U?=#[m;ǁ~bk{z2u@ZBRA zؑ-]FkOM40:Ʒ z]#m9gE;[A)|^*0KsdTqe>2}܃v+0 9P o-IuUiS:+l@ Y#"&<2HryϭO{δ8tj49e z>جuU +}"֗y\n/xT0Ry(\34 shbsԚҿo츆eGt1MuFj2DW SB, ?8H!q0GmW5Y;[Ǧ3|NnYփJ &LJM0 EU'H160m- qﵰ2Gy[z:%qzr,$Z ZvJ?:uHDPawKi-"Cw<+Mrpީ[u >y^tttmT+5lV]Es|k?h] 98i-Tr`HANaa6ѲvLFmZ E蒮hva'&N!אXt}%pU)Vl Ӿ>~=5\/+ݯO>zLHBRaGlK0&Zࡦ O3b  GEQxL>QX8Jp -4LփïgZX6ttZJ= b,^NT91ާ"G|wǟ7ޏ R3<[ 4OrS m@JQ ѯxtǑp7T^ 0Im0✩8~_af#ʚ@x5L!#_6h[(ܶ0ʁP ̰0`N(n N=`ʟx-@PuXx˔81a2um̦}^³U]/V0kȓh-삧Լ#d:ŵ(%ptGQF/~*`aÏ%4π hlI Dљy4?wբuW+4|@&P4PF<7, =ӽK8|h+YZ ~hWD.T둁<2?[r,UӀ뵵* y6b%&( xp],\)hFw&g"6\+zAx?[-x\/ >oS_q6rqC;̲Qmo/-S;u k]Y5R7s@jR5-QjGy`ZFee69X}eW C*4'%3O~ͼg2ϺE >(09/53VSa0wbĴ& LaBѻOjL㶞u?$*9rѽ߶l}fpp'DtU˄}O4kP|3ӫ%+Qx?֐B1#MUhiKQE__ Tro/ ~MΫ3FV|H'so`6ދ9Nt^d2WZs.[./42$I<>s_ox2^1?m+M&U! c ۶q`TS!lmI54!1MSZ XGO+&n`߾ .@$<`R[R=emqww7ZwG$:8\|/>ḞK9|#@ Gq9vcOι22j*f[F)  _ MR-uĒ 4CfZx/Ƅ(XQPcNykڱ|4^(h6rPo[0^gʱ2oJ#:'èаۢk ubmM AACXSX  vy- OG 5mnDZDeB,( W>]wW@ 'o==CHzw67wUdxԴ|ۻ~3"į?ϼ-̦by6 O()+"ve0ﻻi,Z3@"Z6e+̼k+j%lt=.Gޗу&f t510N ր7`85ڦ(iλ bmf k͇݃`Lg͚تU(#yꖯh^45+Y;Ck x[ZIQ3=hvku1Q6|v,ctڴyo!fCAMspK}j+w1lįsZqсlglIm[Kms޻UVzWil24/J i~uIhdh8,kS[4e&x"uG~o 4cmNV!5|KzIӿ*0#cqmEXUf4] `sX CHBAׇjys/8 3ܻ\ȖG㸻_wG?'wS_AG|ɧ?@w9*HZ 6 K\yh&f">#rg9s4hkX!Bd\0lwO/_Ř獀>~[sg93[ŘZJ&tc9Ʉ&߮떶E#w+,t,vw0/O8keS={f &F>cdܐ5W%k nbS8> qX`̘xSC [^T{`Թ Џ,Ju,׵*qi @7L3J<8Vv}-e*M,sBgGeD.%E6]kVGTQf:5s:%` H } <]k" yí5\LJd ׎Ifђ' gO )>GGF[-O|kKRѬ% Uͪ^7 Q2uK3oK35J0Z*jyHPlaFJ111͵FZ fmd01Uj,9٦ G !O>j*h'I 1́S5&Ay@ܴFtEevTP{oi5LsB *S@˴[xYiWi(0RWJbF{3>[fxqmkdDG5n5@``8g+ ڗ^S5fg]8#d^ /qx~åbE\<~}O%x=׽jCPt04'"wW?5A굚V4=Y b   > m;cϾ`3&A1+q2ЎնVa"`5"#Z'{ %_~з\5^jeޅarŢ2VFf->h҈[[1$8)yGpώ-o)u7v^U_byXڭ@LMȷGΠDiZ_zyY8RE0;Z"5ͶЕ:RM9~'̾AZIk~-)sx^A]3yGC:0̅LbiV6DJ:jp٣B&# :6LcXhk=@F ukq9sŹ@@^O `2 H&zި&+y,~mxN`YΜu9[JH5. f{f4Y HѠV{֢WLwfmvٱ],d%@ $ôвU`11; ؟(E0٢hާ}P>Q-@TQVc?yqĮ@'>www7M 8`\݂໅_2tСqtʅ(1nhAeCWuO+fh{tiq ?s~e'tH+ LwkQaVh?K4~`5}齐4`MYQ\Zs;ICKam}bW 4A?zh1G~=}ṞXmf:#gKmjR۬{(#+U։Q)y*4 ^L62 vx0cIYffhv)ʤ{4ͿB{ ݯp~Grź,.xaamZ)TP@Mk#U.nv`ΒR urTr4kM<تBgfxُşo x ]gʋG>~,:Q`*ؒp3e2ԦL:h} px pBrhv "^XY^G/$ށ zm85 >qth.xY&9?w\&0jqNL2E1 FGhsZ\a-RX%ڲqO4ę9aijaN+l:bΘI@؜ Xl*׽krF`_ur}شr1ބg {Ft|E}_j- "I)2+ WvkyU8Ғ/^fZX PZI ;$0%M %nw}+-ڹ A:&(eFkx zVq,Vט'_hZ4y^݁<_W;鵿~ݯ0^K&៽ȳ^ZjA@1)1ZW"QL m''e-H3v:Tk`l[>`C*%A2e,v`Usec s/ c9i<ۙ< ڬd(i]{j~S!*#y[t sx0P_N}Ĵ$m0՜}!Vmg]gKQZ%D`[s)8Ƙlbe5ڿsPlwJh$`k KSxiF- UVUR$] 8kuc,*C}kثqq$8/M=,ZkkЦa=2> )MZjJ6g(zq]A*W>hB5@X2?>hx4{O~f[|[W: GmlѸCsA4j ·#DDlбHPRf[kR9xzZ?68Zd;xEZ5gM3w"t@FM38Z)*ef /k~ nį8Fõ-bCD(X%oC8IϚ֟n>3t:/8XXwK²ñ_܏{-p5[s#^^*gÚkYZ49=E IDATt<'4[{x>seuR@F, }^ 4%Hӎ Vkcilǹ3%AB;uw*?x޶r,*CNY#>=<߃!^,\ҨZț`ӷ7uVRݓ&y{te;8- W1(^2gE{W~7%p꽿'k4_U94ьS_; (;˥"{2ڏ F Z2זLrգ+^?I6islxa+` jMk ! ^%hܽ깯{Tld k߆R"}-}Ύs49ߘBaa/*0 ,(efG! RE%^*ՙ&t+@ V10hlA1'!8T` ߯1)}:[dzJ;aJ`kq lͪh EGӸ;dV 4j>bF ijS`ߢA2tNV6t]kt-f機8/W=u *Z~~ lg"/}SzT_J҂.FKK5 V3B_6yFq^Ig~7@M 0~9-"9WQ $kl2}BPsAiEe.t- <n) b:F2 @BZLl% ~ê"֕Qs0Ɨ4~WU{?ȯ5@yO}MA *0'ze2< }0FN36f*В _wFu\ 2G,IS*m FODt&8EjJe@*QJўg.9\sBhKq) ymޣѵ\ڵRp辬ں{v]+Z1u:2X/JӘqi϶٤:96/.3T; ȯs]au8o݃w^8P6CgP*^c=q=qƎ?ҳQ=ҟ r5 @kNUjar&B5vjt6j/nK+oz-~]`Koٯ?o{o{j(N[JH zò9z!jVq΁s(T2NM5OI jmlKl+QЃ[xL@,U;)½Jkӗיּ*{XГ|u0}@(Z2v}{:U@l6tqm9FcdS8o2Pm؆HӥPh/ͱaeCCW_,:[i*3- l+/" oQ{/~n@Z3 TE$pM=3`~R7*2 4?+| QV 3Vpfl6,PQ^1MX`x={2a2yo;ދzoG} 'S௄{X503oVwbjU|q8R ZvmV?##u0ek  9*VMpJg@ IY1 *l}L* պkg(Ta-&u٬\5{Ϩ%HiMǫ/?1pJWAij6̼@qwڒƔ. @m? ˵u׹kʜ{i4pw^lWeZ\A߃>+Y՟îv8GBy+SJqx~OCXk+r,45!ΊRm[Tss:\ݳNv@s}ꓟ?g]zQz/%P0/f1@6ah2m*XzhUh͢`ƽ}OVFb> {@̌GϨLFQ3E}ɿUS]`B- MAzUXzlXbF p.gu:`dl,W [B^ƴ~Vm~g[0 (u гFI0rb}h˃a.Z ڿړ*0ԗ:Kr~,ȳ] xzF`tB=L]yo+`[KjYˠ}2zLt/)\=+Q ŧTAPzgUrP]G/O4m5H9 pLi&! ~v2d6ֶju+tHjy陥ÒA]lFoQc$MUC2*\8dj<~ZG)zt=7@jE0^[]6l{ CeQ! ͷ8t i$yۆnm]_m34nrg͎K%$7jԲ0W-* p.C޺~Hh7J3ȁ)W,8ˇ18im6gz)%Ybl9U yIWX=/+2!oo2CBU&ecS[z51^^# H˘'?Z`|kB2J.>f A]ug(3,RCeF!]uQ#@;Y9X~m۪OXV!/enVI#-勏i?ZʤgKjPssCuyKG ָI^l^!#F?̗xϟzQww# >X8PmkLq0}U@y+la/l}eoq.ٲ `9Igzwϕ=4p?GI:XUZQqk`ǽwީ>\-fV<:>9vjS}ʫfM_簺Hk"^E!6ު91׿L(O_ Yq#mU#g<n/ n`'K^Q l6_صwU. jVMZA r(i<ʗtljw4#`(=Nַ.SKFjqƴ疍BRzx9YS>k=ut= ڵ hRzr;5tk:p[p}uA7GN*6:?k3Ó7~"^_~O~7@4ki}h¹ֶ>tG7'͙W4miBi8:QCBHzi^}C< F Sym.K[-dkt \#$}^D׸9*~ Z[8ɯUx^ǧZ(9,hWلHN+5ЎȴN"m BY+38h]l)SV!q)5~뜺'PukݕtQsQFf*lTc.mAҢ %`h^!B}?EgoIA)f~;ii;yZOe4c 4IJs&_Y9Yx+UiF%9+& /o'~ ZG~K>g?ff@sfWRi% 8G6eqrx07|s"¤/n62A7%k6u~f5+ں~& k~Khr9?=ʐ{ެk2ͩWPŐ T2)e>gk-[ 4km#]- At1-.|އ[ZH:]@~(xӽЋ{8_5F};5ۑN!WT|^JJZhEZ(];RϛTztAVks*Q>K;l^6)A1q(]bwHO:_묞*FL'oo~"^1o{_uxb}mȅ%HIEgYށُ6 c2 |[#E} I}$P0`~YZe14`3U]9 =EP!Llff!{ u^08TZιP6r1j^gG)zyoCY]@="@YjU0ִ9*S]4y+Pнo>gmg9R3"7#˿CM{6ZC+ ]rv[k\imt8l.'J3mh{gS*rHX)֘qۂE]F!)_^׋‚'wGl!xxzH(X#Z8\qȵm;-* 44qY$7} >8cZh DdZDG2%Va&CޗLSAʒF؉0 ' k2DÚ12klʯ1e0J5. %1%ٸG =/(L\a{4joůXtwg^]/~?,rVY#Jj^Y~㙚ʀO~IxØFoɫ~}=< ٷՏ@fS1n[.YpIZӮvjbB!d_8d 7!6A| 0ۦ3U^I0*6sj°PԌwww7BmG;?Ok-׌ {޴ 6[ۦK$Ĩj1{3Rv#Hf_C- VxJ]?b^E2r};Ϲ2)ٍ: 1{.S/ЌGh6b.-gkbzOSsKvl;~ mVz峽wףx[Wa^|FWYV8[A'b.{O+lp [-#agRU8m8y(>i0y5~oo;}1MӨUCkrLJQ0oP31_GCicJLDtNF$*ϰ>&)rk OI@u`'PMֹ꓉_zko>T3$:ϹȪȂ55Hߴ~:r2%t̓t{r9п:1G]k]blu}1CmtYζ֢1!}2\7lQ?^)zs+ZIH#0puNJfk[ĬE,46yRL Ms~?⨮u4`8:ҽ@%ICbyf:j%b5C e-@i\׷%:{=uj(t={dVJAca 0,ofaS5!լ#Uy\׾O]/@Gz}R`Fz:j(8fqw_aB=GB.=xL]7 寧[Qo_`/P﨩 UH8 C^us俛Y5?pٸJ}9dya)%]N5"4M1V IDATB3GyqmôzΑ#Jκɐ6V 0?7Aw:406GL&pZeDDk3;uCyxO뭜e2)['Aϼ|=ݻU NyCf/%_+?z\ MO>\[ d"("⭵"j{ 7sPF_Rke+t/Cz(Vuȁ\BV׆xZ&@`pkZi+hQAV 5+;Ym|6Pʸ,Dn1NA`3vpuf'z|y.gxнvlzcdB@>Ag:ѹO*93CWqN;PKܮ414z+#YS']x?$u?=~y/]R]r&@|@טczUI*$aV.6Sr^a>"yO@Z]w{?_!&zascU|?{|^}u˟ŀ(Ё4eTozNĹ8"{: ur1Ĵ^z&{6-Ɩ6 DU8y=Iźhkez{'~)m30^a}q hNhG^ )>F=ӯ$"FG*h20ksՌ8Q)\k':TZϺgaS^Km PX:wpn w*g_h`u |pܝv\4V0kFI1h#(T7xΠ Zkl_Afx_M31<Z;~\%u߶C)A~\w|O{םnp i?>uuld~odG+Zk3fmoSGh)i ^^myLDp QϴbHy&0Sn >؄0XX'Z, P3RPwxF-LBj+Pk5ǔ28bE\wZ떩T-f$; {V.8 A"# $s "BBV"nr!`))Q p1IrF6g<OOwOoZXzjutiVZYZujpLz3 ѧ 43 Z O6+764V:!w"0}T= m^ l}z@3g(H3v&Oiu9> ~"tP!(VV. V MNʄss@P*$m۲]6@^c?,>FBymS*F>okn L0䒿(w=x|eExOim<:WlSn9Y6FCC!sAf3t&9Z:\֢rp/zz(Ӝ!2 0Ռ9zy;q>Uw3ږɟةƔ%h o>~gm|ǧA{\ &lmU^>]\jҳ(<)(}][A_\K&בe|5Afˉ+L&2S@@W!~(WU_Iȩh}V3uΐ_uIzﱴO*DyX3 v_rZׅ+ F{0*\zbVz. 02S7l>|}jws=r[ڶ-ne1Lm䳼OJm"($""C5W $tiQPBo9~| z6~4Sz|wS>>uOk[ok#xf`]m"89'y鞵j +ESocX9NLX%̄8D2˗c$˵l ξ&Lx=8l6lUy EWFi *KZ' @q'rYt#1wF\Vd* ukن5 &5ANֹsLB`q? yFu30 j<& ơUN!LL7v+;D.eoUx=@kKjXA-)Av͹XW"E{[U!6tYZl*cti8Jl]A0yBˬTPQ+e vgK kjʔbR}zI>,`i*QS|_x=i{fFlxw˰&uz%xNݬEq\_(Ǡϩ;ݤ GM@9H8fa Ŗ%_\aQrI󱙓RicpElfh [,] sHZefo ]i?+ǮkyfUpJ QbX<*lK0<#b>l-y(?NKePhZGR[QwcpwNT@sJ}*ucY`Ɏ = چ7Z[=/§A}z2PA]n(0ţ ɩ.\';~lؒ>rb(5E݌!2ҠP 6fkB/ b/,WS*;e@g^_3ou<)$ddfW+U|U,lZ6fsG=mzKZ hӪp_xg__,~+}wuLV=Q`2Ai4o,4sd)8ioW`珹%x3s `sZu^PSրGuE.G1>lx&lkV dskgʓ}gܽпG |$a{҇t6of2{ПUIAqF^ZF~Йv:.!e|cYgOPut^ygB^wߵo>\OŃ_xϞ p>bf9lq6h-h0h.;o5;N?TjszcQn0a=2Cn,֘fyna6P(׍}imC*q.6`=ݾ>R5IG5 lϊeTWW^Xvn@74۰َPH:}` m6\@+G6482:IX(7&>aK?X4{㹕n쿻OmŖ'F;=pt`H*TKYT kW0}k {r7q5OGoihOC`CFrϑ;oCPH6*'naboۺmn_oKjg`,6nb '~7S8>O|/MO(:D]Fy3l-c,ј=] 0X/>xa*<}1΋{qϱF> +>9NYs+29ݨlO ڑ+y*i&%pJLE,V)Nɑ!-^*ÒcY=SM1fg5M>]'7(뚿3hf+<-%EߑG]8v ?{s}:l#=@þX^? I~ ns務aX@?{6c<>qS?~žS%@%D$Ep0S8F2UXi nChAlZWq.$jnBQk=`x<WfYЇ iy2I,n`Q$.:UR(]¾uY O߶P`$d.ISAd"C_hڋ0[P&**PHЍVh_sZC[ǕՕ9H= .=QE8,BbཪV.PlSO^\.LBU:{۶dd JUNZ?T5|9H}XY {0 [[?cD; u8LK gt^#X$ T6)g=k ogux]#q \yvz!Ay>g}lEmcfRjx(`坯º\ =' 8/`@V3u<m|' ~W~o|Oı#;LSk;L 0A<㹹LX|gr`4u$)mc2vzKHWkk9ʕ"bAL rݘ^ksY~u֌j.-d!( FY66vc`g^5"t[-⽎*_RTX*4urik#w.<*[* 9e(̩ gi*Kg3ޯSc$5?~f~@^\&3 )aN/^ ,9`X| 6(h9\#=>AJ}ACMtg>&rzm:,\<$üavĆXn3weU]W<{`ko|?%>7|g,dÅ6 %g .`|>ʯ ah#.P+HcuPڝrqQ*KDbc LK]Vj6H&T%Ƹ *d@e䳆m9H9*E_-;~dYU^u,`&>G7?Y=s(5ܾ?vjM]¸hkKU S 5K{yNMƬF ̼'S7K=TfY"kj]Hd}WrIEjk3,A-絏ZbP Z˜f2ٖ}erd LHb>kcYa9ǥ Eg2V2` ?CXREuZ1>'>طKK̦Z g*H&֬yZ:hksՅV3(պ`T~Lz%pi{Lx#>Zkڟj2Z|R=UY$Gk軄'̥E+cɈ\5+f)DWq-,NV8K(}1sfs+$Za`9($Yj ܧZ^cY;BQz/ʛ:k+K5Gʹ0lvi5 a֊1E6Jb"\9/ 7{;ǐrͨ!X0~wy`l,!\rv?\aI#S+J)OF^ښxͣ*?۠_woeb(ms@r@9OOOFH %%xLm3k Tkb(Eahg5hpFփOoM/Z*y?&u_ŋuܧT yb?wn˪*7v^aٻ* DOG7.G6hUA\: @p_k c݁(':2s=XkOpE \JwUsyI^j%W.{\VZ\z܇/hĮ3}9]Ӈu|}_L 9UY%|LӅHj)_[w RL+>amի|񋯽k}_9;?>寽kuc۞^ ݠ`8Sh1SU\s@VGaP$-j^OLIl 84j+^XyLlSD@fd,ڰzj}8cL#:bC hkHNT׃\LBX-\ݝupM.,]+`V[x6*۰bFBt\=d,5#xΣ\LZi|7nwOǼ;=ʄ;Z[^}C0EFse!UCS2Auֹ ޯ:?>4<0]mOkmP6IUT ,@m Z]7@o<^rU1Λ[a=kl5:T6/~?w}o'rdK[ +s!IZST‚mm2 Xވ[3<e(͵~܅R!&/kh@Y&aDV#h !Ǣ0Y3FxVz"V 4)]z[FHar~i]oMtj+0;wz>fVL0\,Lju]ЄT]_zU|z,J *Ʌq5@0|7nXѨg}'PK@z+8W\{+(s* `f_+,sOG7讟wcQIsEhN(sp[Bd"%ܟz>{N-!sI(ܜL=z1 {~$q'9y< wcYz\Ho6"F0Nbo p3 >=֥AwW^}+|H r|Nb:J9gH c!Uxե,exUjs&,9f-ZNnsU{=Qq7y 2ǚgW=Tɫ$C_jȒ">scd JysΑ*L&絈UUzEIk'UYE!c;uαMw | U/ 5wrP*?oT%H\@YAL>׶i"iJZ~$i vу$pSO"窇I/,ɉ)j 54m3KdWAnkg!'Q IAsۙЕ!*,!U+o^~cS_濳{p1YCl \KƘge`"+߫pA (bn[& j6\W@Ffy: \Uk z ɫ{zwc6s+19sSPh1dfįl*כ0 ׅa{Nֆ" W8KG^i9I Z /;=Rω>h7sEeBHWX挙<7M:ĪSt*y,lzhq.X'@(k%s,yr] 5#h]^1Ujm%Μcm묜k&&:rO¸DzٓΡd1V Sz>y# }f@^ĵS3uttl`>Sf፛[8pG:rz.;:7BJ:mst0[Ȃ}P{K@K A܆/K\M>2CjR@Zd8Q7_4qdv:-a&|zWot|9k}C:qe(>ӄ ƧpɣL }qe;KNldinx8WNj<be`-B.Z@.U;zGZ>v4cYk wU8}e():prh^UǦNyw:651iKX@r6,o}]V9FB%~874C5 ٶ@ }c_r9#e^Ci]^j<? 7:p?ۚ$O<ۖ4,s1$`T10^g-'/_kqPmELd쒭Q]\:AM+Yt)GE-~/Ħ[P<ϼ(H[q0&S\{&iPe*fÜChիtQLС"DozkSnLOrf O(8PQy%~sX׿Z8x5!TkMҞ z yR+׬ /rNZV@{A&ӐD@ЖkOa~>+ }Kj& xP*Pʭzx6ȗ)8עnVqfYH^x10=rjՄ\FKJstz3T ́۞;LZwlfiTʟ۶ q,ǯ|.@7. |՗ Ftek^ pL%FE*`uRkb#tE*ƶhc@RTv ;l7bYPaҭ &N!*) ztQ&^[:9.9+t TYs1iҲ>sG dNk}c:&w *ۓ7\|#28tu4ònzGBvbKMYfs|}}st[2hʌɹl#=Ӳfʓ{!QM`Yc):ˮW3l?n6ֆcݙɉs.V9r@ 5t>c+;c#k1 وB}j*1ꤢ2 \зNHCW}dȷDQ=f](ڮx2|k˄3*K`BT6Kz_pKn݅41[TBھ; L 4-\UFE ({mK3wm-x tkILw{D:VYA = OGgzA9wӃ /"]!;gڞ ܬJ箞^O3Y0rcz{MwGٱVƨ1!1geO KG4|t!Oj>[Ri0r{Z8W9 K3#ۉ顭aj܁ywe| ƻ߻ tHPӂ3|3YN7PN:W1K̼?]6JSLdNkNL Xʬh8ƴYDH#; Fc]Gq)-9Sp&ɏu +A\ _s *U<^a /OOO8'wF__M$7{Vρ/T~" ꍫJgAGߘUoĿg|ȱ2"-xyF z;/\fk: j5d>a 𶀘\+&^w گ?ä^9y׎C*yc,G!0 ́m6Hg_yp\}k?}'ľ%C!sh̉8yPVs4@*vXIL+{.RV{1sk; {aۺɋ,aK/"U`zw2ci"X_$TE ljML0u>el\*BVE O oGJ?iz=rydmuwYKQ/mi.x 0`<]=zS| lv2[d*'`Zf+ Pve z?pA` s*HGwme ]W)L:9 ckO '>NJ$3cWpwXyOnw?6,=FxMNƘ!8zV6>7W(8^vwOtʧ2h*O Ϙynf: G:Cf),<b7Q?w~W̰?]w7~vp?/s/tR%(V86EbEwhy ն=*~}cOAZz)ymx;B-qY!gO[ 6~L5V|K^B=4r~@M&sǪ"y̰˽P],lХ|HfUU.l# rmPՍvџ.  7(q?K=h.|b*{&]6Ƒg(2Do[ol6 ` )?2$,t#`ps5&*&Jg=1ؼinj4J̳h˟[}QC.Ἅ;2_ ,&[/?Wxτb)\|&pmt`)jDz18y|#՝HV3U:U*:vѾQAR!4a gBZsqN17.FYp@15l${Bh IDATEKi2ڡ'@) XΘMM\hG>mHULU-uV:EA~V9Mͱlr83\D#Ao`n-E*;o<Kgptݲ*!qh&:ED[%Q>=jbޣK[c;u5CZպ&SeTYa?Xܣ*Sm[ 0oCWF6ޡ|/jmT˳*𸫧Bw sNTUtʧf2|S@V/~WϹ{U!xUv鏶Es0נ;g_~$:zpr2iQ .5e,43,NJ }Jsw D N7L$lqNHh VI)Ч}xaz^Pʠ9V Es{3WsJt:-<ћ-Viᄣ7r1Z ]+D#! ߫ UGPd~#o_엿=>&E܆~Ub<h-bʔŒI+*uhe^]K*R_h<֦2x J['mav|AZ8'n|w* x,跿 91TWi;4$TEZ ǧq]PLz (H˄Kلs hEvLvpێʹ$g;79\P={TE?*HL1 @!= W@xBR<";=mqD,<rhxr0xvWE1r|}XXwrZzT~V9;啎}KmEajTV7lV޽OvM~j(DWط&SNWDГӓ}ǯAGNR|w!7 τm0]w.HAmUn>W-vP {GzGõ Oc)cS*egEYvy[.mU~Jm]N(f2&~D^z}tLXJ;YQۻX}/f8#o;CCTQv~ q<MdZqC}9_3~m6y#w!6˖f'ǒh+r|`<5uZK.]!1>k&:%][ XpNiTqaPӶ f*M"r؆ FR"w9BCkڑ+hlR+E6B66k '嬰"wscKQXtABEކ2#e{8Ŗ3kNʯ\*Khn 5 O `2T#Ƽr>Zã;b̼[@{x^#MuO2ZT+r;(Xl~oĪjT Ga*d0^dũja^ؽ*~N03+?@$St4k+kZCzNbq8gc֪ ThLWU~՚{u *񡋘G(~WPiC&+o^ug36uBaM<[y{ޱQ9T kuL2 P N,z`ΙLcyE@ ̿1]x\ڝQ(K^l8 u{NJ}]kO [! # ( F)ϹKcH/]پs])-g* c@̢܁o+sN uJ߫Q< T6RԢY0Lzۙf c[C7C7L 0m Gۮ||k(Ҷݐn)=a}[6K{>wqQbTXFmmW^ߪ:^!T <++›q؏kH7_!L<b pɶ926\*%E$H _[aK1UZ:u,eKmQ"Ȫ+wJ}ab[hpy@n 3cFB[UCnH뢀cJCŪ0o'%3b(cZzƹUXZcEq9f2ᡜQҠ+ Q -1<b=. 3k6W5Q'~گ;y}Kr/=y\ Fj1wu\<w`l#ѕ@ e>2 w>mW_/Iqe`89R]~㮌R7a ʬ 7 V2'Se] Y`'2W FEZ ԭNX]66tU *ڪ<02r r (Rzܠ(RB{J ol6*l=}'K۲IԘ c >].|L}S\9FX7R0c ;Aw}gT,hXOI(ɖ-cb[aSUWY2g6˘{[S$-JrZq D.+Mi]-rrٸժMι|~ ZeYnO-[ĥ!"WHsNos;/\ꅥ7K|:%iLcʃa/.<~2HۧtS0J}uףܶihʦ{9_^ 86=O/gm.^g0 𭧗_x/[DE t*vubb3ԳͱӉ [U$];I;cfKap1UAVN|o|\Mk5Kyuq=W,+$ezci(J zdv3/jUS6Og`z؆z>;6Aso z9: >GTCmv{e\WMU.{ίouA|})ů3?^L씁U!$31N&(SPQylT v`p ˍଂHTN^`^ߠtM$+aQғK>q'lsσu"i)^U+?T!ght+P~^*Y=SÍ SCQa0=`WVE//1<y{.t^%p:~w Z;֌y-t'|1qU1;6[h*6 sqM'"s|[}ͷԙS(ڤЫ։Y?cZN;EN\3e߼%UT-z}U&ZҊG%\IC خn뫢Vꂤr'<* %B)ӂVOȠLz Xf'Hg}(>,0R8F)k@a+o;@y5a3^*'9J+>[ɛˮ0y4 -ύ\4mY&@7Sxɡ|ᑀ2߁gu83)1qh_QR[znF ,p$ ;РO}*F?^S Ё9Nxi-7_W+-q9-7?-}i3U-U{'+SkLρ,aj {y8Ҵ.cz؟>^CP "!*Q]nwlV/'g[y9'/~w]{MU@3iB!SYћϼv5\qQ 'V`҅hBW}5 @+m}{}fuGg^Xn縭@%!%'O$ hH_$A zUڥ*\ ]Vh~\Y-9?=%#h>I4=aaWo5GِC]V?<.oa&6wTݶ-Iaq ake'ePp}HegmKy)"T[kHf mes|)E.zx[kxIOѶo˦M\͕ڷ6 ?ڻ/7kq t3!Nݐ1`}6ʍTWN6=u9Qu \g(y}r#c3os:ZD*ং*{QҘeC'{&ȳյ^AʝձRo ɝc|Pޫ>h[{hKT)VP}TLrJCcE@ TȜcyJKX'J_*TV@c\.GzObww3=fY%w>m#ar[yگ*U;v ?\ǎ9Ag` )g|n:Z'|Cz>> sKsW% *0{TS`DZ6^Si$|cU0m 1=]'TAs>4Bp1ZZ#R+Ud*-y6ٶ=]k-ŏIhWPK]hٹr[X,xщ `u[Wg߀wܤ }EY_ad%S5 wJ cY^ /r#b\C>SzUz̗ B(0[5$TGka%g@<"J>Fۦ,LKsxn|O++Gsٰmta| _k?WΛAD } akx*. 3Ϭ9ӷL|z<dk}Y'03x#35t `; lf42@> u Ju\bt#]-']ݣ]:BS' Cn? iN (|URUqOt ލ@9ճm.|A=oI[i1lNsSg /Ga#wO|5Nۦcf '#z饐vS<7ε618eUyؙ}mOL`^OEdilJ5޿𥯼պDcT0b2]?<*AW #E{U+]wF6hi-SfU\z5fָ5L{x"ưalMYUP6-8Lu_k-QZ|UP{@zxwg5&™VWӇUB+wZUިL=ff뫕^ ,jؼKj*Uzd[ g\].ͪt<@h-+ jȃG9 вsr>57K,3ѶHT0: IDAT)/mic_T1E@?{ 4]Pgح<9PW'䳞:VlḴmG4?7 i.LJ 1?P;\">o7*|TL00'c]/-2$!D>N=$=;SXÓ<|=6̲a<VJdyTAFރ a>7{­`$@ jmzqQBC)k߫P'o ?%qn2eР %>0=f2funij{HY{z?}ңE I轞3.V2W#"Qq}u :2$lgKnA0,{ V Bhv{}/_[e[y c I-a e /<<.nwC 'u<>ѶVEFA2Բ *9o ,BaPЧJWҊ+Bz<$4o! 1/͍[)9W"5G#]j1>cEwcsSP<>vMdkmݏgl_c9qf{k{mQk /mXW0ky vb xpU{.Kw?cuvqdzM+p -A, $1oA_tP0ŝ^QdVA?[^۸4fIǶi ;\bXUHLa;>FA{_ 6B/j4 0t,܋$~&mV=$}ZnεaSvi{7&=4\z5,pg ȵ U*ol-?h+q/EJP}qrxci .mKc%90Wp 0@O@l=||2<ómsa'5,m?8> |wl,"xZTx.= >ٹj1T @(`ì @+jv7˗¯t j(#q-=;q@GѢ8ǶD5}'4cxU L[(s^'C$,(`srëTP|{u)>,,?O>䮢gϕ8T.VVU B[[6hxvV \*L쓌eY10yZKh;\UZb XIg]kZ%eba̫S(=g :j@s. m>@}ҦҾѻ=F]ǥzD|4*lKGρMV=}vl%^6=J#*XiK-M3^})|!7y{VfS o&6Xώmk ۶xJS,W9Xpu Orkm4>;HlC5XF)зa][rpԤs5t4dZ]== ^jㆢ8wɎ6`Td!Bfvͷ I:aՓpQ`hfcݨ!ku-Byu7C5IKWNwy9F"hn+Fr" toiQ L`IeN+?|P~Q@u>[qLaeIy2\Z;L>93wqC+۽5~IJ LX 3b)mW%>Gm8j`6>,~ΝyF\B&d-0r>Ca[j̳q}$|B:5HP|6Q!U躷l1E5<' _6wQ(rwǮ)9WUO^*A6ԕBaLq aqӓc{m,pWøw+ADI< V[aR/7Iė4R4w}UBÞ~_n-`+?ZO#ޫȤ+r8ݝ^]17a ې} ΢KVS V?{Iׯ/}F* Fim܉wgEDZ|g; )O~yՓD񝾮?CԭISAy*[^(xYWF{(+4OFOyDF=ysʛcQv(fdaω5f_||k'0JgL\vtqOVm[]Vp  ?.KUK*o덅khA'";3?"ėnOЄuܣyDx輻{щ3*#3@fa@ıᘯmx޻grCº`(tY1溭k+2nM[!8Vpkq|_0r]7ܗH>Wuң5?wU1uuݙ^Ӻ .Ee~*lȵr&օbܞ`EU򟌥X֚xBXЅBfgÕjagW~_;g^\gKnhty]r^ZO|<<^{u .y<0GNuM鍬36>:/~O }N͌ Ht!FJ\]`YvmJ U-ҾNL0\3f]qVթ7W׉P0Z'v}CFc.gxO.5? ?L@xֻYyZ];Waw] T"c>պNcN2>e7Ψυ{ߘ_G)}zF2ր Iڨ,[`u0 l<;Ϳ^ER kTHt:7m?1[Z ГLw'0W]!j%דu1~=,f?) {إM=FvUg@Qiĭx\ hfX{tqm_ʀ:ާ=tQ8vwF8㧤bV^#4JJwɫDqל8Ć)^>1Px)n O'{Y c_>* η8&6;i?U*GyvA={3N=cgYuvWu* Av:Ly[/{=(>ƕI{z;>]Y!.zF?/ԃc(aVm+Cg9Îq|rhk=`k1&N*d͡^JԫرOo&\+2 BpyԮjAؿ#JkY5DCWk*H%|u=ľ{]KV,aY1llo±ih&.נb-(ڬ=xɡV^t\(54!G]tT܇t6JٌMe$K༳g1p0wscXnN{X c^zJgʁ(igy,qV mbi<) TW\yR6yY8zcur*aXV'搌%+> 7CaH#fJÓ5a}<\*Zؖ:nԚØ&{l$9E>ěE_ |i)j7t`@Ԗ;2|@-@3T={0T(*=ū浹(Bqa4tqmr]hǼO@|^s3B&~aż8u羧j#=+Zk^=7]81*9ZKް. fTv''Lp}Fu}ʺ#bHŷrw?JNYv˵'ULzSZqҰi1w,wUk">ѠqcYl4biȢY~~ t3j1c,;[-*W+C7HJĢIL7ɍji acj}Fйo8}o߉h_1{VJ~rtU.`E8)8_:7ۡ8 eYT5 `2בI!4S!3[hO֗ 4La)\U@y|y{;|wnS)ס0/e!׀{??S5v{gZV{^Ӿ}FO3 x賧͇y=SDJoU'ޫ'jr(pj( '@;"/SGX^O֬|gVGqD j9=)ol!XSRsiZs݁g㌂R7n@J« ˗/7`i'C?O< ~ϙ-O23ܳ1FcBOö>C?ۇT|su8?EI/_x.ĚG6Q&]B^grG;U8O1~aQ:rk]Cg 8iO^/%,߃A?w;ޒi]BD{ V#kSSV沅m6qvvQbй-_ nt8wUyw[+fQ+_dȞrά9-⾓LКz*]T$͔7qDg)l]{ٶJqۉнQ\9ݳA;}]t,JsNwwK9+LֺOŬi@npŅ1 Q.<3rS@yƕ-!GU-u~:VC)7:3ɾNOYvY%.́zի'(:ɳŻWY '8w?ۂ˯OtDNdi^p֋mw[+ՅTj$t~zP>I}]Ȭ8Nr\@w'lBJ.'㺻eY3\\+$z*ka't'3V?Sgm}~0I%(8j:u/s`ɠT|1.W IDAT.ʓ$ܮԺuZ ~19Nʵı rHJy_`6{Kߡ+CU\y2W}zKA=*Q[o 8 ǐtKa'/tH>l-} "{sKVN4+è\mzm\# o>,l-,ڈk|F݂'nOayPDj@ל{2p?q9YJBn8 |'aLm(݋u0'ۭl2Mj%P ^07.togzDnYYAH$,&sh`Wy8(xcڎ9qXHh<@g@L0'7i(ΰ7Lː5,1IN MWTp@Ỏ*ğs.wu+J=佞d[h6:ס jd!bsNX[ᱨ%9n=O%Gl݁jLL?~V]Quku]eh?9|cL YLdXcgb`?;q¢]pĩu?#uX~J͗_Ǖ*u~war'R'^:+pm|u?ta7}9`hʻ~V6^2e9ߝչpzf㺮?Boou)x1 {\$'e&ken-7HC4RwMIr@j,Ԝ=OoIa9~ OD.F"(Kݒhh^ lA&72|1.Yu4#bߒq_myV­3SͥnT'ZGeuƣTO|GOr(3.\Oe]w Od>}1}ފ`*uԳ"qtع_r\S@5=e3}GV $M|'A~X-W ʇy+}ngr,:07ʯ4\$/< S|?Ĝ`ѹ*v2[8v3 ",kHp,C62wmw ؤ2l. KCTz& aH+L hM躇5e [wM;2#Ǖ]$y9'׻ɧ 7+E?G+:?~B/ A @P!l7 +=E͜#S(x}(9L4; gz0}"Ǫ'bN^+B6w $am mݽY{G[P<:L|߾=>uS~W?]@95B x:5_UD" 5Le "B\u '^\-&ADSD)G _}yz=L~TxZUW*?ٯTdfl k~.%U/I m,z;1qXQ2D+׳0p!@|qOEGPʢQ}ZoZ2_isp'@Sz]PJ"RmNH)cG*oҝB'+=;|Ja:rk x›&LZX4s1aRRv=*$#ߨЈ!|{ zvU[ğxQ(ix/uNYPe)+H6R; GT= B*(ny]7L n/}j?CסV>5_]0]?)DcG$ybpOWv1) R:[ 2?y7M۵0& 0&<A{Ԯ$I2Fjwƕgٽ FKy_!`3cE|w72;+c"[+:fLzVz%U%.ε:ΜU|uM](v|KLGn>!wdɷT~x4[r{4ÉXb5k^>LV{d/GZ>{?':VWMG;L g@OWn@ڇ1FfۣΧ+f~1HlHfUVD;~_&'uߖieDbN9:9*=5<p&>#qC?zF서/&=ſ5+KJQH{ & xQ2v[<2t÷ҥ{a` 7\[x(3+XV pdj&ǯ݄ϘDØKt-^k1̦"qg,+Y+:Y~)Qwρr&}:.{\4 RnSIϏ{nyLЛ 9x{QIYa\i'>An<@/P%$9[OR{~טp!!x'z%O/ʆfPQmgZ ]M%*X`)(X׺wq YQ{-zr )&wH"O+tUPFuŌ|3sPX-ͥdžҋ B(Pµ"S9'VV*l7 ;ˎ- #mQ?elVc(kOFDuNsHeY_:aYdO QmٴL*OAs[F4ߟ a%:`cl0W(uZwֵ蒱^Kpw+\9J@ {Ux jH<<)H̶<(++' b{RHuw(sJoyJ1LdL|1J,}ˆ)iU:Uvȋ)`KA[Oc"fP| e w1p,^{cyҬ+H@؊EUߏ8{ qƍ(Ǖ^EXctkR2HfTTDf=u@Eyf*t1Ӻ OAva!SXZݙd-{Nt}Du]ﷴ*SQT@%e9 Pu1Q2Z\Z]Cb~B8N(N@qXkbڈs1dH`<4廏 ?+9ʺu Zl `;!i z$k?t>X-r T5gbExPJ`-QљxK˴.;z'1s XӽQF\FI_?8(@4ҘaOJiM×yM |N83F:ēt8x<ˇq bfzV;.ryU^WYkts]1|I]uX'T >:Ruty c |UZ'=h..*5IBUǹ~n!Ɩe+i\~0ħF>8\y"$i%@X)q*0+a {NDt+2+rɘ1vkCkM#3I-uv=(u)`IZPVjkE8"U.LKE⧛)=YgPw%m4NVě[| u/%/LkoOB'zgև"PH h5SXP;WcKGE')~/)N wu1rIGG{FMl Up~k5{ū^mQv_iXnV]mr ׄ'9s]i0rILߥ צ9!Ƣc{G@ntrG50 &ސ! A61zfFV@:h88kc,zN&S JKpޫ{o\`/qLk :w[ WXY.Yru]X\! snKEv5=k[9\Ȏ{a%5h @yI2T݁y*3V^(j׵7ʹ|ȱ7qr} ;}2 . zT 'iNUY8^9G'+aP m)'NOMAʸ8]FyAC4<9G^nL(K~7+Jɫһxpg2_ūGڱN[PV KnqOU;Z+=Gdb6[uo /gZ?2\3YShBu_sH'monPuuM3q^}Fav UR [w 8(ƛq8>-1ZTb}0g5]!Ϲ@ :B=J6l|@_{a#>3L5Hdnޭnv'-Jɼ'C^@dنqԛꩠʼV#xY=b|^6{Wrמ"T9Kb(ZF*G,+u{+5d)֔zo= jJ)XUOqj{O&#w:(Sα3 F6k2VeSJe]Ę)T.8Y,/Gt ]?y8u]lxW}Ft}~ .?~W=fUvP{pd| _);Ғ fycfAWl:jo_dJm-GCLXpwΠr_8}=)EU[ys<4jPRw?|@-xf$ ́(7vs;&zft|ƈQ;`ὶWF8>;t =?g}PM؜0Հml[s4Z?U@Wng@纲ʘrJ&60#t(2=ɁHC#N sɻ֏o6;kE$G.d;Q)ad@:G¯c8o68Zx6~; JZq}=œP3;︪ ^ߥ]v&NuuOwG9/=OFB+L UuQ5'X\G 8BSs;L3%+4;]юaTg&{~R>`\f:Z4e]h+\N!c)517hg]Gl]=(=}t y喇t[};)w!pz!J8"Om [ѢЕ'{P{+.s'8bwI<+W |r|I%~W~:_|{`!V+7zcZux$WjsR1HТx_D0gP(_Ԏd47 3fbF=խ{lLd<5{\c\h܌ËgԇJƂɧ4k/.Q8?YӓrϻW}?b/؀u<9I oKBu+<CC*^Jqݬ=m7aiVjϰHJ}7`uu@˼9gYؓ9E1pl 붼kAu廊puU-c|SGg`[Χ!`R3)h=%^tĘ̀9/=Z3y@ڜ_Qr dZek%hv< ^.߹q3WH,r-YBw ?waΓ*>Zx~=z&H*37#wKɎ~$]Is (-f0I>gx_8<@U~r^ٞHOGq.@sЃ(INv k is7ɩO{BO\KrVtGXC}Bz9e3W5^e .ٳVZp3uup^hUR7 5x( G . }ԸXe!ǁl66OuXn| ];[ȔJXZ^RuzcY>t_+ o0j']*$7{#:NmxГHN2Z$+g.FsvΉ]WBTԼ}ڶtRqG u`kuwWpܳ+4UYZg3 rE +3PKslw<7py=s}e`+5BWӆElaz :Z&ҿ Zy68጖&ktle5]{p[t+BC>:^VjͯvzXIހ OsJ 쯣ZW"sNeW?pP>p VjN}N ga6%oT[;qhؼa#}D#u {wURUέ#z!."ˍ\ݣZg0YXntQZ$H2nv.oCy9nP5Y3+&w=1{*̘}$IVGgy z|i]@YeXJ6?K 񺰩9bH hB97s/>YKJ# qldۺϮs 6>lwN-C>}*m o.$h%:ƽg!>CcZj4yxFe4|M= IU6iDC0Aֈ?!x )3_NQ+:`=Trw^~}O@gbyٽNDk&?>[Q!Pc,](T{9&oŶrR@eN ׎HHAI׶Od\N\A\V3K*UHD{3vAF,T@=a̒+q2^k*A5h+1mCHJ_ ~tV{d#B|7C\59ZNm>AB w@`w=Aяm F~sEI]5ѭ˛Y ;8O!p+=|F`]83H? x;C[y^)Tnؘ 19=F@5SaqМ}ty(Ӓ+UBl!p7ir{W)SkG(Jeu 97z9ce!gύ[ABsTBxT! ݫL,8Ƙm'N&+H"_2e^?uayHp.yJ]iX`<@&ܱI(tS׸TCFO6j;C8$X;Rh]܇eGiH+m[׵ R( 1^x+-T]EG Sؐ$G6(WZO>NpٻNfK%poGl;Vw_ĨrqxP-=7znՙ{fFVi?Z(|yay)^M +6*(,csNY!QZZʕR¸N:wH#N3'#^F_9ۻ ln6y 5w򤳲O_?G@e—/_1MS %ߥY(V~03Lso=3@JFU_n3,FfYT0vf< qe0 )9k dݴǜ\knTQA1|3a) +"ˮs+'d'ԫQ_%a&3[Jum> ]\GZd1RI41p%32~HypE-2#XXDw}"LzM:zw ] AWx^+~͎zV D}-+ݤAQN=y6!W戆VJhDg]Y ѥWu;Jg U腲5n&qjXj,_?AhgҫU }!qee*bާ{cb]0}Jn!a*?7<]҄Tݭe@gXKzG@>TrLvy) 9c 9gٶ~Z+IDydV¨Dvhiκ6wR6=|ߣBqe)p]vX1qk0tc oUCXMY| HmwKXi\K\ƫtaV9 |aNiYI}x= 26p)Z``;_Y/!TkLc}]nR@3ykLa~'zLz YSڻ2{3eNas#,Ra绨sO% ^k̟oz]H,5 {j,gc ̎'twf#%JP;"Vm'("cu@sԌ\ݼ\2z6k}u'a:m~QpRQw9]Ҩ0QǏ{椻PVZjq+-Z,Md?;,uhO^+ ʬ!~V_wsSAm,]%:e*euwGUu֒p2\z-VYX^I ܾ Lzn:|s.YRn4 U#/d(r;.x³*Ĺ2Uf'-~+x)S>',̄YMϮkŇ*q[I|,vFBH' >poU/3e!Z+)V#ɔ D'ʍ`b!ŬR1f$#]oOb[+ʘQU"{p[ L),Fjng6z).L?/@$/ L~m̖&lMD d؜ ݂ *5+p-F}s^k ׀ዜB zOEYG`*KhP*`{dР.t˶+b^Q僪Ln8)-B'gv 20&/Im0l9o\`tةET )U440K1'0yx@.Oף T}W w)|~kH(qGhکƁC!u_ P)8x+ڟRT6q+T[xz53詴Di'zI 5 W_ SrB08鏏 )eZkW;vrk~9>VdY4"JEGn7j_-tbvz7ɥ8,cic32'ID,_ruhSbۂ=屼B(革}^W5랝)=],:s'_HcQ{j8>S<Z.&dHKj ?w$wl'_W,Bi7(+8fvOfSz+ngʋ*־}[H+PcL n1`<(%*޽94Ck(.7ݷ-W?Y8-{' 3f[{Tw?ğS~ܒ)oh$}˰ii(2 hƿ:ѐakţPۯ>; @j֏hJi*I9Á8*LQcATTp\9 |.(oXH]8_+Be_"Z`b!}&dN*46",mаHy~yJc䩔ܫ pq 4 qi.HIM+ 7ucvT6SaW2-]~v0$<,ܹz*2"]rvLjhY6$4A0,KyNMM+#Sxw&IHԜqzoWxUV#VC}X;0][|˜c$:trDw8!3 y0n OawgИK'Ϧ *OCeDUKAǎ6.:QVs C:̻Wس7LE{vSYÿ^~4a >~ӓLMqT" WRy,/_nw*6{/!H`*/dGyQ.oϗovje2iHqMw_;\(mg0["XBpw]lyx} 1=CX(W͑LL +šA`U%kk{U^DBz.]x-z(dJ O]Ӹ?aDQ!ϩ3\q>iFiB~8i YoR)s^w F^3> >?_ِζc}>q>THO "a'pЯ\[_7t=9 @)|xݟ2q]#˯™)|U@SXJNx̃oe u/YTgɣgc O2C)"xb-}+ɂٳ dڑ4hVHlױ݂ );3K G">c$0ss2uߎ<Ÿ'{ZǁK` 2n}_FLݽ9fw1np\#T٨a8Fv5ޖ֎Lᨼ}&W{l>uȝNaJ*#"eLCwE=ׁyԩnL6˕t郊X8Mٳr|]Tc]>ܱmK֝KӠs~p-KE9&2{,eHc`{?}[XxJ&!֥}weq,e_SElƺ։}}uY}UPXܠKOo>kQcAQ<^JC7fRJ$3W%uRuwg-s-DR Ǖ;"ᒨC7`PQu[4ḭSjW`u*e~5 Od|z'%N}>=d[_f)}^<~=KdUeETtԘbEt9 aYyi(Bs3,2 ^ZWV8\裱H@k+=qeSL@T D·W`9 z0q`$tRj+ U2EpiL骖6zh߫&;W厞']]_g>"{`#WSh?>s67?#te;"p+ƪt'*cUj(bόb/TZQ;N~1QvOVGϳ0h!QEpǞPs9:LUW)`%J/oAi 3+4䒄PުTt 8qmsb(0E;6S 908߿W9T0vOp ]qZÁLྱ>2RF2w'q5Dw3#=>F᱁ɽ+P|㾿Vɛ!P^4SŷjIoaq9 mk$ڸ{h~kt &x+c- )1ʨKcN&fQtv#-0Zd!Q0@rR>z=B8ߺyHm}f ,7 wn:h 2hT7y ӌ=%]7B,&M;=SOySuO[8?S.*WE>Z}g1:RjLP c+'N`[t3=u]cAxer%$6f$6@ rd=\ @gI~]ͣEEFҭ[~U/%8ߏ"lWu]kM(knB#!MSx:g3G_0;w9Mأ!%Weס xs_)*5/{̹*ӳ>!c=w6AWOO\ʋ> 0qː {VYxGAU89\ùk!5d*w-R`*yOĽ.ܧL ]!lQO25AA«E5au:Գu9)KST\1Z7g'cQ+5"穊=k36L3= Hʑ I 5ur-Lu9o`p߃[y@^%#X(It쩨z3w20 4և3wWT_[߻3= t;xҏfSbzb.n{IJfz T ;0Mxz㭀RTNFoR:IdP|H4`75y5ri7?덇1s<>H) 0HLd"I_2_xeSV >02KvY$ITG q:s ܽJK@Զyڌ&R9Ayٝ`qU绸pO&R¤۞MI.w  0 73T/E0F;67keLW=Lhͼ'IXh!Y>jq.:fif7>h(Y~oAj7~E#> bIӨx`.9Y6ڞo4{cf{ R<<$}]cf~wϏc ʹ}eheg1̦9/Њ΂_z(&cw-6uc{n;KuGo}(|wG㤿y<.ѡwUN4<4 zmpώ,oi7t_e!|:2ԟN#%:j|EcoHok3 U&yڕ7OuhZv'<ۧ t8P T1u9BT\e}H kK{PkƯRB>9"wvgx8X({o|76qm~3հXLjeQ߰ |Y>0i?{_-At`LplJ8Į.@"wdZPX[0h5ܕ NK׺yLg*8.Y~ m "ui>0@7s*qGcoBϰrwFjS)DSjpu,s}dHpxթ;\hQ4L}2R|Xb1ի/朿mkh`;=bv Tƫ5rzRJqZ=&ZTSjD q5TL!pɨvމbkiі)h8I8%8E'.*ݱ܉+ѽEnEX[ IDAT:0l1Y<=qNV3_d~i-l:✈a*/"WZ+u^n |m s֬Ug$ T;=BE0U}\c\V-+rܛ}WD-"BsHi,y'^T?y&":OᄇׅS!VlTǂLc>_d)+NoS=pq7e_ϵY/\q2lGġE_>.; =)9ȝghB- Wkd:,y#ޗ7b_ )``aN`jc6ex:g hJ)>W?EiA4waw[?,få'h*Qegf+`!2fL%՛2f@=΍Kز s]gxqܤ%͌I0VBV.Yt/gȊ[,O2h꼲s-3%q*SH&~0uvɬF^Ĥ'01eνSEu>!@Whb8SeFu,pG,A*3/I=l5@@ ZgPG/Џ^8E8#k2ཪ}zTW&ߓ`:~mj[K;J¹g 6SbzҩȨDYP#D0v*T%z;5QOyI.3pC!$+{9r~?*w1T@g\DW у'g3wD'b36_+Z;s iQ5QO*)Yws Ww,5c͈Rs$6PZLǢ=<=s?byeb,j iD8%2`ӗnTTcskCA!uE(+r4h)t>"n1yYGoDcQ 1i"UrB j"x >tF:t7nS{FwpBWlj4"m_U^tO~նE6{.6%Le~=Fj"Ѡex2SI 1$s9RJ(PU~!3Dy^@;ʜj3$!O6M=&o"jsoY+Osv̗L 4'Jfs#N#jѪazV`Dg]Cgܝ|8SAo)Xz g n.?nj&7h_<<{ 4ye^wŏi2>Y4 }%@lEYJ:`-n#tH2Ǥ<2=ꕆD]k1V Deo8Fdjbҏ}6n=-G]Ӡ萢?Bv4sُW,Sj3V"p-!k# [qԽ ZV>6 UQ"s >$L̹u 2܍2 >.=_`z7z73Codnet|KA1/Ʌ2 {LBAJ8 (P~Z!瀗Onbu%Ofjt:A5a,| {OD;$?o %8 >b){xbϟo©W/Tb>G^Pml`<(s77+pv*jP`S?}6= ߪ8`D߬2sƵt8QgT ;7+NϨuц_Hy@ wɲN nokW :`8@tr0hRҠk.Z^ C%v*_&RR~'2ԁ@E.ԉ`e:5{bK:B+T&#*]RCJ :skd{s>x=6ݯ8mD^}xaZav}U+AI'.$% 3;<$ՠd5b ᵲv "9W 7:io]v*v2\2uI[Ko^SJHK :I~[پ0Cie4:TmRf1Wqm4i3o<*Ch< ;zmGWL 'Q;ISPa".ěLc_uҶ"F#t#K34'g$<r,eX}Wzo-q-#IkUwꭾ !s&ڦ|Y;}rüxЈQw!;A"bFAU,##&AW(` Q/(tLgBhbQhשW.u{!t܈sLMc4Ul֕d .[yza >̚_ȥaqљK7G[.,ÿ.nuU)rʅ+?K 5QU dޙ.}~D X}NA?CȲEχe4n1OR6!$v-E=V= #TZZyaQ>)AX]d4r$؀Z:!@Kk[K'u?<`RCgw/=4.r1=1`%sf5sqk{C:\ ;='x`}ᳮÐ\ \^L(T9tD>!da`mT8օm|$-*<+!FjxfU, YvlF4kG,]m8;0`n!0"<1 v99t~3xYݤm^o8)Yp{:%qr+RzmaKf FL<5p\+u] ]9M1 +B-) >׹\rq}>a P~f+˫I^%7~k9J,h+&m"'8.#r%o˰wE신j+VԼ*0DpA04/H=QFp閾-ڻ=V?{K Q!cKvEs5s.C6nlYfGyѽj4fD[jX(_Ɂ~kʌޱ:` sY_Ӓ]*O`toФtC϶Nӻ>Rz:P},B* H FMuI OzkX#Q!Ewy*x !Eja^ %^pA2@RY* DBl*!Ƥbt|elc2 :땍!"6 {C^ x" n^3y1|_lSY5znB>>T*sL4Yh[J9> *>5/we\Sx/䊙7҈329!6y'M+ {zUv;8qΊx6@;7NS#όw?sZqAxGT4WևiV'N~y# ʻ\ odTwȹ*:6FT{4MSGLZq|E)q*8mACVo.u$(hG|BpsV#s(`֊4:쵳!M5!rSHtHn)̐-Aa\SQ49Tnp{VXI|[ķH|]&b9!!o:p 9&("&Z2D{4T*0|4mԪq:7%wTߌ?Wq<̋9ZN/=N˔̪CgIø$H< c]To1sq4@{."}LG$y옣V)W"eM)cs˺nkszýbs7/qKZBށ^̓oFiO;/Il]9SG47H>SOíZ+kZjA/}'N~ YpOze+}i=fX$/dv4Y ?Tr8 (ؐr5MOm5'*e(yHsy`DXGiFу  dT )4<6(_?@Ex=bF9f}a_!$hğ,BP%< śB[*fk˭HL1ۉZ%}@(&0Y+"\TZf)b{qI잞ǂem|@''f/< Qؖt襘hᝆ\g" {w}˼<獌f |m1q\IUNR Pg p,'M 0c4{ ޙd5 R x/t83٩J>, itqU6لp <Rv2(/8VY}sUPB~s3v%_;2ܛIdI˒ .&}hѼL)蘋h#KF՘.u=gNu#J0N@Ay||pgMl YW*`c5Rtwi t uḂOswZ1{oGi@U D6"*֋b !Iqlm@ hϝ޳NF4?)tp]g]pOecr{k-؎%3'/`Ht}3Hz-6 1uU8ȃ 'ex.o'4s5έu5=e*6y{],{nEfS=gq:x&2] =U7 u]5o*!ofqI?}NߕGK8"roy錴ݥNƯEԱvſwl- ҝ@tZk^}mBO~syjgq6Izy$WP\# DyHq/yS5]swɯuU=ᇌryZ-!Z`g O0dl2 , $Y.< d:\X0P_DŁd}sUXZR&ƈ1w~u6< (Fb}4ϳ.%[0˜[euKgA=E(zV9K籹#w*q[J<[XGmܑE޻K{#$YnSduwE3Q~M$|e2uavջ& {u])/.;0V% L<,tVzye?¨w3:`\9'HucD#Z)Y'O*>7pR#cHK7O9@dΰDˠn0pX΅X =swUv K~dm>GfqH1U&W\[ۘ,P|hAI8ϵIu;C_Ar+>A@zk0~yI &&a[ZN';^eE Ag~-Ws`@m_Ôj\S()XR ?*C5fThfєS Ғc(}Obu *r-Sp3૫\px-KBNx:{ge-$#GtG04V^uSG՜9Ӿ=+3TmF|c*sDK" X&xx1L`8Utgg: @r՘ ֡uzvr8xW3J9Se* TWY8MhUT2?%C[ζ ф]^M7e/Jk`V++]kUK4{F{

48_(]5h a^~? 3R7&w*çS1ɶ 06  q~Z5?9k-ybIyP , $l9om]22>[mXc@8e3De7!oa>Lj35ψě_4Qt4k.t>H*8Ϩk~g8r'kIHeb(rD0f^$g$0Ό}uҦ8}>cg3TͷMC; 6 s+Γ.oIۛGzkٷZ輾 IDAT ATNoB7S:wOǯJdJZk-\?aS'Aõ_0\@/(Q]٠c&5^0)R: m[enx P?:8 ?;ٖʔv;3T?m'.8zX >BztB0CY~Cs#|n.cfU@0upOUo84 2O$6P~+g4 <Ә=kTnH3Uu볺{P<<ޣtȏ>l[doaKyC?/Ne 8mP2zcս4%1̤_owzW¬]I9Ow e (Luom.qi\OH,xJӇKQXJ]]^Jiu S: _]7 /`o~rs>>lW:۔i:[Qu> O߇L}Ax9wf&@h`O _7e$!Xz2 Z,p* G\FV"[0˥k}j ==ٞ3MlzcGZ}vwGhp0ɐdx k{z?ZYqQrQ4Ϟ{4IcTEcTQ=_o"7{BE'.eYXjM* *wA~٫\VgLd9h+gٗ4_ylWyI<7H3Sw}#P:Uh4๴HQu3j9Lt*: 0N{:G=BYͬ$QUNXR-W,y4J}N:@~w*s}UڮsڭS]o޺%7#&ӰT^G[(xRcW'w0xG`x*vzp+4w;X9p*pOgu4My[!cs~c]~ޛ? ,Ie$s~ ?`eA͓Kq: ʚF=vF:pN};k0t M`cvMyOQyjzMA۠FPw mJg*ׇDT/͉@R1FQټzJQ`V/ZWU:9g[瀕 Iω}>׊`EӞ,C*7Xɲ z>{<0F[s'ѣDU(o.*df.(Mţ竟9q 玓LIS?_zL\%3[r!PPssܵQ-ZP@A8 h{P-ͅ&/MC8ۯ{AǥNtKV]QZpz]Hxꦡ;3%03Z7*ٮv\rHSo|Edo}X* wz VJP" _QPH;wSDr222$0=n_hsb*(9U8wla_8N1Lo}@Ni~n*DhCSn2&^o Z0괛>m/R;㤗*I_6>5~jj`7z4o)9^bUx>+iEjc+pB;[tyzпfŻ =Ƚp2㎇)Uq/ 8E>j9Y/&:nhE/ytk \p<˹gO]łL.GC YK1Nh_!p]71lŸMY*ͽ(±84oAA` KTy@2Ԉ!U=? txTxc*+ҕ/`警:_t} M#cMǿoΉPLk 4.n_.Du2"/} a,`SQL8L HAJS|:'ku]㙷 ݏjQXŢ1gϙX`v}4YTua XN1+m\8a0K&0n䋇-Qܥ^ˍ*{\)Tp ތ?/f$LO}au+1=S0zfRVzi[o:VSi޶Rϣ77l9q}cqI#Š/ >dVe)`hc]YR a|2yܳj5H AҘb># (VA9 {zi ]]ه7Z`D:)zwgTZtհrY碆 h+ڮy{qxSS)+]O>ӓ;Mځ;SA'ݧ2| "(<6d!+<ϵsB[P p,೮Zu5 f.Nùɿʇw.Er/ ^}>꠴} `N1 cPsO`>ћљǗGzy}#$ bX#AU+L%6RŇ .\\ ruHFg[f(m~o [f~wl;0qBQKήU xVtùɝX"wޘo7ppbΐ6["A>Ur"TyիwMpZuYOv{/AQv8;W$?Ő}( `B_8=Ayq0Ke1ͭcy" 7KWyNR:$3D@yF&*˫y u/Z]>4pYpqu|ϱ{!Ƚ6 >Rñqe}M8c., /W6lcp NZr"Tz9u 㲮b<&6J,o쑦 Ux3Ki}_2_pWG)W0>3_z>1m<( ||"ʆî CN}0uueZi RK+0(2:D|o!lÁJL*ۧR| (6CA` mp -P헂P޻s|8.z\٬UmvzZ+2]؊x"7o=M)|.9%H&Șe_G; }ۭy}GDb 晖pk/Q6yK:mUR =%+k'`㛬n '2yꦎ<_Ttck6 `D?_wXпǏ>;~ݫ͙m9![o)kx/ju\C&=z-/Y0) 92BJc,k:F4߾kE[ZZ9OL T?'LU=;>왟 *o~h\W .3OpHCw:Op-Qc aM|*oW8]'QߵԉL8E`MBOY D&LA5[L v\ْ9p:eV1V{e["XM ~ iA&NP6=g*cW)<0{Y/`Gi M=2{34N@=NA"vk{?[ΊIeg[ióPwR7-^!it!ϝO/ղḥ@q]W0HSɇsq'ޥ\K.Wxȗop*:!QdxZx0Nʴcå ,fBRuvu䟺2,ku(O'A4~ɋw=?|>ZQ@7-Y nPσTg U~wډ'p%ڿ HnILhhi:]?#}JvSGIwZ{`ƣn>]6cs^DS)^Y*DZ'ɬPV:_~se.+]`9\&up\_:h&ʳ]},("A-+a`࿁u_Hm~fPۿ~ߨ~F~YY U[= Ps;yZ'"#?9oEs@{-{4HsXXC oX54v}P^XQLu3 =VIF; BQl ZbeF.%5<`jx0AAeDG3ֈf U9.F+tc½=1z'5$W|u2K)6HX WqoQcOKt09!kxk*i.Qii7LX*X}M@M^iD-LX':VFfOM _ȩHаHc]|+޵䲽,*S)v4µ*Y$ɧo^0V9韻ҽɠn|;{a|%y-E}Չh=XUT?fbL=({?ml7ggJu,v 6Q_ҟap"$ 90#o9@ BSƇ]R:?ok=yQ@x*3*X* t.G=rw Dfpy߁y"7kEOcs9__ .wd$e>^.U{1tx8 So4z1#&=Nه%',>g-w k.p>mUF{OU绵ezW1PzMsA+ICM_ޚ{lǸ%?pWn8Fzet|9&O,l@1]Sy:+s58HNgdc~; u4Stٷf3iAh 餪adFZ;)jg!#гl-DktfmNW VPuxn>aT!G;C+inM襞*cqUAJi)؁Bs|}Ve&2oJM똧`m)zwiP`&824mqi{4S6*R9=‹}:tzJ0/Pi}2QP 2@m ZQ{*n}gΡVsg:>u`0 4S@L=R(y?4SfG@[ڋɳJP#?N}}%rFjvPl>/YD&FL[7"v)8C}?a IDATs@BsHGW})6 Sg(4s(PRsB]f:b38+TioUbf@?:#{nU H&(ހW=M9vjc3%ąңoS6H֡Gc zY])}F#AoYm;Kl&:Sߌqr=D,cᱞK^ڗilsAR< 6H]bf0w|iXcn<@BIsW)^hOzN=zZֈڌCxGiRδSgZkaC˦Bz)J-y* Uͽ 0s|%bc'D~2ӳ(ڣ{wK cPVW*̢m娞F_D|Q֑kF\V0 6)7iwj ~D\u9:xNRs|l# [a̻jp1Α:te3c.91;X]b*H0/XS/Ӌ.#y6G:ę}Ss*#O `cֶ<\ռ TAcTO.FVyЙ9ӰO;4\<\וCm̧/u9从{g:OzQwi\yTU^r9Ǹ+^xN>QB F!z{e7. 5 9Q4HK2 s>S $dTsR,Q4#U9PSb~GW-$'2||~~>z҆'w=ڜ¯WvH~}> n,boaU&ȉpqt7=!-לKUxw]C>l'o8tSC]X"#C/kN~+=&Hl.z?Q"Cu\͹hNE7׹~O SZPxǾ/&-xwڟrh5kY!V[*ٱSN_Lޯ<9mp\=E/}v,=!ݧ ?%6ĀcM`W.siK'b4134;fi,B z|t[|/@)Ǽz ,+z5C<,ڷ#\9u!2]Utt4;GX#|- $0ÄfV'u;1ن*9b.+;DMٶt&S' x\2z;|vl|f^n>BIKJG ZFX]7b~`ܮ!R-+8~utߎYsx-1N!9ͰmFj~3]lKQiսZ(?ۘ xɨ3F~*O~N8#\n|3=|FzUA˳ WX'.Mwɲ7RzVF(Pw-_5M'S1ʿrOnȹ;WA!״OZg__\4aRs[ x5"Ե74!{+VC7={e{׺ -l72L< 'F=<1;q #ʝb{xheZ0zm)>j}йr~c9.%`;0{s-JCy8s2ҙNMXQ*ƴk%כcxLo5XۓOmm*+$Xs 9b5@$h%voY0=C.ƤO:v瑳l0+wZX=u-n쵡Kzy`e (5VJVF"ΰY"7ZOߟ-g:&<@Rώ.1F!)[ 0|MK}|*} U8gy?TET NBe؂ПmT\O^}mu o{X<^sӀ4|,6T)QTX9Gd߹ʃrVT^ӹѴF1#@>;Ҧt$fA`z#ǁ;hW$8uIvjϵNn9ܪ֥&gz,sy/@gu/hu`8@tDg<5q;|G;SYO 3=S8[@h)o٨U)noiO$i(V^KŦs L==23itc]b^aB1(K}]hH/5h$fEQnoaRdu 9tsT6IeDNjF;N|q镍P]= 9wSf7|?5\t[ V>ET rJ󽫗um5X)3ۙF04[7;[h@*o]yh/+H7C`zȈ֟]JJWyb-};R$TY1~>vYS}>*>6Y73ް k7"-dH<-͐:_iͰc!nO27Z6_Aכw}84jS&~^M`jmC"Mϧ N:wE, e Ck!*ÀNyЉp@w> a-234bw;ULSʅkIi> N92ZΫ414i N2W4J!eʠ"zޮ=N'$-oq6̑^ߡ3nqZ儷$y!!.])Up. b R$|Oxg4TStc:Tyϫá7 JȀa 'uPϵ̷;E/ٸu?#׵` ZuGZT(m$ewZnm' ~ d>]o+tԸ?stf;O*FnW{ώ2ULtRӫ$ޑ&(Zy~ByN྿C_|eu!咱N߿Ud>Hv˷gQd>s7PqekcAo CMO9T x+x=s'}UR;o=JXTaW^vc|%HKMFtkE7e- 2po|j-Yvlq+Uٔp>`=:g`8 P(McZI|o[qX};WO^mcf;*ϸWה9UJ3:zuZ]x[OOT65=r ?V5X*oAt2v ?To0AQ| jF$s{a ðݟ]dT)HZ{ hj eoђP))?2="O*G&☷VT-)iKpZ+O{b[Ga'?3esǞ* wŒdP89^Dom<&՘PvzLetEݍ33 ^LsY>gzK ΚLQ_d@꣤uJmg{Għw AEg"ljT$Lo/`,8אS)e&@@񁁛DV02 hh8>j vbn[+WZTq"}tl;Zp]* eҢ& EJ>ߥ\=S-i+o~spQ_ 42jΊ>RHGZ0ԁ2=6Ps/D^ *(CQJby.y1= в}]0}A;SHks%LH$o{olK5 >/cQLkwy BL'X3LS݈-|>eM 92 prF{ rkl;>B|8pp|spP:2prV )v;w8D1 ]vMf~%x2V*f6 e;;3aC̨r_q564ćP2C5qhThZSqp@gk״ =ICOd?mDU+缞 y`^O4NuO9>Ưk c?x#?/F&ki;ܭ28&ɳ}O7*FבlpcА~^5M敏{AWpnT>)V2g|GDk m%O9 ?@;̀4IDGp[9/;S҆t*im}Au;MgnD {َN:>]FFo3}&ٶ'4jA>4UF%}(Syƽ H[AnF8B,޿B-ϩ`^R# c‹lMC; a>Z{<mnev=q/j @PiF P~) ~'ݔxC wUꥴC{?yPҿ  @ Y#4Y< #@V7SY+OyzmԲGl|umN]}U/ ,lg~&JߊgQ[)Cxa ]{] )B\Ǖi6YC[YHpD}#=1? Y(߉3ocfƴa|rgF 9> عf;e !(>~fՑ5_?S~ye")wNp{]xD*IoRNtswZ(%y^=7=ljĸl%޻\4}p Ql7|IS iՓz0Z?Mѱ , Yn.Jxs=Ѕ\; ]Ji0=wO`tim̔z)k/{o|qԕIF((L/[;g'5^SU~Z+wZd:{5 mi5]m+jls>BASC;xɌ],LZ5mcVۘgBh;;9u\s+&6ܣgs2? 8sL@T }||twr=h ܽ LW,`iA% gѲ( x`'yMPJ7E=|)dܧ< Yᒿ(l@RfqQ>*_ ڤ&Q,k#L@J,w ^8[,ñ8|}EڙYNԸ?V1V#=m Fuc7W7\R?skP'[~?{Y.\_h!c j๗xŇz% v4imoµݓi4AN )D|OMP;k_nӧwr]1CFk;C*5ũ Ea3-,V!by _nyd# ʬ~IBm1N!*[@@Mh[l`I';כaZ\'|<'2$yja͡FmgU?UީKSfߙO{.o:~1z͔DuKOC8Uש%t\A7>9~A0 X9nRgāZ%:L!YճHK(Xl¥o?=NE\^ ,@U*ZFݏXZ?UIvFkUQ[v.d^n+37rN!?`13\ߋdȠ%6v>sQkUI#ZBzx/R{.7ϓOL674H/`11JRHpYa*#GZ{7zsm,IN7ZT$(et#vLνX=UzjlTR[$Y( qPRgoG=Ou0Ҫ,Y߄FP˶{ ڡs~9k28Q}F)_Ib5s`[y Y%D>9zaU ?Sv~G:dHpdL)fx>uYw iv=hW=,Nymr2~:NXw74Ú.ufsUNb.t ~~=,WdR.0]qӕ}T QPE-<9J-x~Mt #W2v][B 8FD^"e,]2ŠCPŖ#pr"6(y|w$МÅ܂V_g*P$xJp؆!;vلbSsnוϙg;{ iSi>~#XflTG):rBF7rH>”Uoreɛ@r`}J}c|vE/ۢl`mG8a,|>WGX+:'RHcl\W%"aX);e<=SE߬/fNYyqwk j^Sjg7P\ Y%,يp1>A۸G1_3Qە)V*OTyu}9857FJ%5ۛJiTjb~99FmJϚ_k rV~x.tncB\ 8^+Eeǚ\`LfUY&x|2}:_SC:ߟN~%SE_]`Xb7g;u(Kye"-Ʌt{ARЈV۝( ͌UV9 {6nP|[|mtw Ftͤ7d@E@ C xim[K2°n7WZ*V 1߻Ny| #߹~wNW?sa[O[$k3睇կv#DYе{5n& ;s]M>ﶙYHS@em1W`]supbv X)<;%~*0Kǀ,&s4n)Qn,OYW$4qNټ!T_Oo0iKgtdM/Լw)!)n)P愢'(R)Bl: ( W_(B+*+9XYs!.{8(zERhd쩬ϘzSOS3eC5SM T ,^F)fOer7:m>H3Vb/:o╊,Ȏw4Qdh!u]l\|=K-~)/h!Z;NymȠk[W}@O zd5#'U@Hx:-Hڇ%b{Ho5Z{L/*BZ/Tcwr}iyOt~}k},Y˷l!QnXݪ ԆL#G yq8`jc^'|N}74U L٨(M>y P]9=[me"5nwJXD)^ߥDuuq* z&#NEr&Ͻ 9s.*õcxvTqL z ) 'xQZR;}y/H)tqLˣT jͺ[l;Nܖ ş\j9Y4[k,&7͹ >}JRi<$g34~y gFBa(V.P(ګyz‹oԟ=f  ~7:i"de>}\`a~y9-Ŏs:߹PC3ޘ#bkLKPx:yz >j{Uߔ16|slGy)ԝ&4:&x#B{^-v7l'5Sx(b|bxطq6}igS}E{4>K?~*4klwE^;Q^T+=E&Nǥ^48'kN"w)DsO=*0;@}LP7)b D<&ѠN sBuȱ.XqNd"z>$qJ=;rs#?p*ך<}yʪsNsI}"P<$9IzYQ Ig4n"nRYg$TęOȨ5K= Z.6~w Ri5pkl+xp:RC1ߴj.;.`#2Kr(!GNeCb 7y6t?,HfC]q&0O9GԦJyq,OփIT4!Z8JN͓ʺEG IB b́s%& U,g_ Ts\o!NJn1A0,7Ų4z bRmqqP }Dc/jhYϋPr>2h܉@heXK,_498Ky]3.ýp=8 ĉHϖ_<ވNz=,) (mm+_R6ò ;"uQ֣ݭF7!P+H s]djT k5ߕڜ;'p`)* {_,xG 7NwWJZta۶(hC(w$B)xqķq-|nGFj+Nȃ;bVgaAG'봝 $5}<bY,-k1|__@gCGےߞqɹiɥ=Ϻ]; [JcDqahxnU~Tf?<@wVjna%7:qJh KYË mœm"y԰o?ފ7#LMLe~yie>ZG' 1Yn4PAђ ~Hz#. w.ʍY'!MThr'Aka7Nn<ʉBSC1md' I&yZl+*)Iw:MLCEg 9-veie^m:w))duS?m;SG'=~C(xzժ7 Pxosתn^z6@O^y-DE.\ɷ(ۮ%8ϱR}nTƀ0bJtv,xyizIhcMs^:du]ԻZ-rh3A `̤JCh"o5Jɫ^h18\[<]1~=&0C{^ʓ-n5Wxgo,Pk Ix{>;gt+pYީThO1VlD6kTln[f_*]̬3|WrY缇ߕ;I7_.ڻµ~}v<[TD:zw!_AX,뻃#~HC$}?sWu#3|۰V$з˒SQ}FPy]yS;G60մx)r56!&-D&v rDzT5㬗yYޞҹ@@y.OTqua}`;_ƤC+>W?qn |-yW&P~1g2F~B(ާٔ:/2,$V׋?mN&`,b T2TφݧAMТ8i)qRt q:r-t['^-ev2ׇ~d@~mYKW7Gw#dc Wh\ 1Ԭm{f)Mvރep+NH65<{ш],J1J?<7>Q@Ft/8uF tR+axa}.NJmon)X&ޔiĽ Y @%%ݙP9Cmrr*wZq\j J](@8`P\@>xmsaM}Z{+gg7Z =\`j瓱(b]ן< +vsj[4X &`{{;5TTgt\'*?h Q!{)@+FK/j֞W|4)4GB9I>9޾{u Cy9̺mMY6ݲ`b*?72_ xC/@9@-p$[̓GqTy.TCfV[u3`^VwӍVϳ|~{GGTړoZÎL~}Soz? B:N"Rg1Eܡ mar?ZI{)B\!Υw:5L۸ϵUqrtj΀3O&%T< _l!u2{N @֙k?p,s?x0 T?]}.9c5*Gד7TYsIP#kuN*M!T0(ډͶ(˒{7eu"&~*׷ˌe!joKh38MUp] =?y}mt\};`+C^}Yqn+tLW^d˧iwMM0AՑӝ dȩZ?uzX= ` Y)UMoZW 7։oJ'qq՞ i0ng~Md}hNzyfᔕ]*/ 7:C2 X U !n3Uy!)BW>n^oȬx`()8=[뚜q7F_ƣ21!L`#`}9JY#t1[KYȤ_6x8%4$0Y{_ χʥၼ 8r"7+/ .,Lx4.̀+TŸ`en'opZU3 u/B#pfy*q^SF ;ZS\~g*'#Ma!*SЙ ˒=g%֌Y+q{_ /2V{o]ID@AP307av oUԊW$M]?mzk< j!Dj餻 p+b8lWJ}5R~1w͜a fx=v{͹FtI7f7,>quϔ>?=cz`G>ӶWe~B> qMK冧'oM@h MGTnϛֹ}0tnj֞|bCe_#f+,vXSc;d털m q̮)2POkJ}1ԔkDbuX߶ {%F>!i+zdcXi\KU.x6h+_ M|uotg}hJ[1γvzEi [<y LF/1L=Ft;ݒ12ݱ\>*{g|Z ؀sMkwwK|>Q!){urYx X鎏 Gr2+e7 s)Eq)cQ}[ {w.dcx}bʋʫk d IDATZo:ǟe-LA_}/* \Bh|cu}{Fh莁P]d?4Iz Ϻ{Zs 2k_ݚu/zoGm2{ hH=^?Xе1 [Ïbg e! +s ! ;!Ty+lgN14*#t ײ(G `H^Zt&t;g5uiб uQ=o+":,V3n7 Ud|0ªʆYIỹαzu9'JOc'7!t#̚FxP11uLP78Ήr:S +XuZ+Ds~Y(l &&/evB (W0˝v)mGXWт $!h*6?p~i/bœ y qtDBKLQHܥͅ&Q|޹7̮!GqBNaX{ aխiWKJ<T;@_ZΘBNR08ۂWѬd9Q[,Ҏ'ή grӘdxsQnI͡ ˟(1-<|g]V@-חg wA:5s'0{{^i^l1]PU4fhQÎXb[dWIX'ΤPZ'!!^# (z_0LHtIMl3/k@*MI(Be@(Eyyީ&?i=PMy\ɽܡ[ncWpRtc?%poTV UW#Kp:{D>ṭ0K Up29|{@Bύ\.;S%pIYk; Y 3B")Vu) ZsP(]*D-/1 nv; $t gp|CVLpO~?EH=/zڮ Ü'>ג2Op|oxHXJQ ^M)=f$zoC`^_(Sn +%^{ z|GegВ:sMf 6?_qlw<>á?^b'ms\y/<4cpCS& TΫʭQ1љ۩~鑙fe%[ !>"IqIA=#5vu=Gz@_@?ٕi$Рё]^\ɇޯmS/^_ b2悰O[(򒙰F"/ 9Ks˟x6=d@AʽS;|QO*2$92Y ,݂H`(D#eeieݱ>ު%^E$,>E>9"ҚY8;EP}xx{-KFހ즗pd"Ӵ&N3q+Z`qZѪT1&' 0q)0QmEmĴI~C{ob=єZ9sky Qa~JZ1֎  P:22~M8|yOMk ׹C A*\f̌lp~?L64?0`9dI+Wף^M0k؜w8V3WKǿ\Yه! óCkLRt 诽oqv'U1ջ%B`Swx܁#H_7PER/Ͻs Pf˻m-@ .=ä1p<7z^{:7D4yegl{^ 3%P ,$*, =LVZϣg{ h4k2:Gm<}Vzj?ιp}RԳߖ~kg4< 5\`ߋޞGQ'G1x7A̚pK;{V KmOYPY"=}v$_W?eCtY$w ܭbҦμ$ŐIZ1gm M]C:n`%t xx*Pdo^}XM՘Tg7/;uCLAď6Ѣ"C3;=CFGTgOԔI1v gkwMzjƻt)\ԒF跟8kDڄB Υ2ߖ/m#4Ds,6R98p| }Ci0w {~em^WRd]pTiݛ^!ZnBaRq2y$1@O~ps.J3DW,S_ɳq/!~ب& pܙOq:du`=J-ٵyxVC'Ǥ(pv3u Ro6Mh;I/>wMҁnv7K$$kCd߅pGT*mq i^V2>ﻲ |j|A,peD/ֺXUhx$!R{ fŀ ,/VX#cw1)Zq`Il_ ϏUY,enZogϚ5H I3g} V]n{V%/KtVRS%ϱ7M.83Y:1i>҃႕R_$cЫg H2_Pt6/R~8`Վw"yƔ 9qJqeI_YwS!ecY[DBzZ{o|U!1-Uw%G8Ezd4)?C5SM<>|"yx To8#>uy -jsÛbbITmxey;D5frf'}[V*5]oQ7 ~ * eJMŨTAFY hٗ7PBZ= mM)WtN:PԘ|7-;ILkB9JV,:\yMe^tOKk ia_y3}?~_:5,%»'w5Ps٠ҏsܮ'aa]x-/^pRDIDu֨vjpzFt $X}w|I1qc?~nPC'] T%pkT"S<@4ltVis&rޮ9)XtP`DDW~S~fqmNv UqD%ˑ fs(E'fQ/݀ <=_ZE4SyaVXc+k83ݐ^Ly@U)? ¨\Kh;ݛ ?>GAK^9շe'4'zCKr隦\s#z;"t5yIUyPJ ^PZ{݋.*FVsHCi|'0}їcDXow呹SyY*(c#soOf4!JmM3'3<hw?Qw]WlE9yϞزoU;0]p&,M9s t0T*lzE 5N`hH~=N1>Ή 1];?Ww!]ׅDZKlGtݖB-bBM`Lr;jOo$ʰd>䍽o =Ƶ@ulZ7p*7Lk~/AԂ#N54U2=*JIt&E u;ˏWD4zC] RX!CYVk)yf|ODՎMTe O|j͠pCQ%Ϯ(,`y+Tqq}NSwb| N]wrt¹>Al9SuWfQʀm<" k.y~w5H$md,~).f6τwM!iU!x\xz8o1OS,J9_|W펖Q/ȣOU禎)i3_))r=mE%ҟ_VL/i, oo z]xM;4)-L"|Xwm4c>g'ׄ{V^㉕O }B2$tmq0v;Oh1/FR*:&blYRص ύ>~q|+܆0 Ega뉛p%#j*\^gJ|u:g~hVt|@p'Z!7>rGoUpM׉2uѽ߈T)y*ra?e:$r.792lL&}ɶ* XOFBP+tMa àcNdb,3e5,NB7fE5&YP,ϚJP_ó dLrαѹq,e αD?}bO1U[c']\609dKҜcA/Rh#U#F/GֳGKD{{[[m}7 eW0*%YڮeişSSȄ!:};p0iQ(`5/}wÇ^R>us>zZ,pz2ti/:1 IDAT1y.ּK8'gd#:`}.ڈ@pbnZdZ֣}kXʪKF;^[ƬVkY-0kW+q+8[V2J_oKbR8U%\㻾 6es6S}x~>Ely5^{q] نK礹ũ`!o_erZxtqmޛ~b_VuF8f#L :įȾ,.][ږ_TSf?s0A1ku#~\Sշ_t8Sx[sY}fpt3yx愌~{I=X݄VXjJlmHWiUGΥ9zh^9~~3 9nGzG\}.zaZ':?$oX=ԩ! ~j^WSH9ږgx,pu$qsD~;r H+)֔ ©Wbϱ@'Zp(~f`|.y*/V!DԳvS;ғjEGȻO\3q[?=8ߥC^+DKnFnyU~3Jnׅ ؽeQ8cRZV )ȥkN# drƽ5w1= ȡ*j1>,eN88C/E!DQ!P}ҶCp8ygXKAMd~pȞ{cn.*H'M(ޢZb@ .J/~퀚@ks3/N=@R`"}pXo EXH6n(e딱m{BcФӢhʭ%ٹ}*ʺkKpЬ&/!%e%Im 2T`\"i;s (\;ۭi~Ai0Ѳ !CNZW+ 9?];Oęʹ)7x=޻W(3?'h e `ԹLbLiX44cg>f J/W),`]L%L x|se{K̈ކ NL75evxDB)hA7znHFCN(pუ$y'/)6bUpw9 Up]]%@Sr~ ӌ[$'=~-8_E;A ,xg渧x̐;Nucso6گ1qE˿/Ҵ9qiϲ 'FQ(~ƛժ|7$y cܽ|^h("磭-z B8:8>oPbL{.B'.{t qwEZ|4NAm]̡/[8ߙHRDD:O݂=|QE Gj`18wBXmO }5> ]btLTME*m? a:j|D{ `7 *I09γn̛dXh]9t|}ƳZhp/XI"7@'3GؘLj&ts~Ub_d.d:gbgL4|?g8%3PCM3}=t_T۝3ERM%9AdR{ MЊ@-ZBӲ(RT1#CyTr>i=fʥ 4rޒ>a4zr^䰓$e@!$C4jOw7.pSp/-{%[Ԍ;,YfxĻm\\݁`+M|@ؿ+ʺXRaھtY Id k gt啺i8-3[8γ7Yp:!V1 /N l`y, r}=3á0ncwZDɢ<pl6벌PO<9TD"έw\ȭH,G4ys>Q{ :J]pO%ce CDHEc ,TXjX!B֎53\nN0VRaf;ʚ+L͎ȋ u,6|Ywqۆ/0]# ?'8t] ?^͝pX"!=?$}lK/w])"Eʉa1QZ g+#b>^,^6V\+s(O_ؿZõU)*8V &GmT.p`L!Ѝ~S濌&εb?QWqpH* @zh?5tOmWFOxD RtXL5B1]'# =ms]pd PY_{rRW`⿿cJ}J`U N ]xZHcoYG5nCE_k3?vHJbSEpq؈kR@ܐ*D#4hŷ#JmgW/x]O;^3|]?=^g yZ39O=$3ZuNUrcٳ;ZdO{o-tx'ۿGyEF's叇g@Le9zqy]P%|7]/Q>r7\KFߛATàXwSXf~U;UByigG$uf;bݛ|.b,l! 1} R3k2-%$IlG8Ep ZEH@||LPr-:j'?CɅϾp,G(<;eJ%R?:n=UORQ6fPzRVtrm\qp"ᱎ|(-#- վb?4—q*~V̀≔w;ȟR }{gSQ)'Jv52=q]W-0¯WVrA AGN^o@n\L&M7Zq,C~Є<єr8_i4|}sUMfܬCE=Nϒeʐ |5;RTjvU J ڠX 40A@kM1*cgXt{N6~6?d9RAͤex09,i!kaf؎rC,!44#YNPu>c%c9BDAXf_< O*{(>%G~;TW%{ 0t`,yћ1yZiuu]*>qȃj76pf>s} 7\(0ü@e+U3K49Wp]RSu,Q"P0VyQisΣdZڿAP9(''x0]㧬2̓<] WXR7P~UsG hCC{op%U p thfpK #E^7&mχo([Ձ}1b9X/ψݙ4GmB"Q4y~>]f Zԁ&sG t_$)\O$ë An' NSP̿^'LawEPZL%<3PWZW\@V=ʤǤiGL%[ͬbk&updDY.a5}ҿxK.pz%r]flUNVt]^0~l'X{< η>g4@WOn4潏l4\1˭UT/sSifhl!a ryW'` ![V6G瞾U`%9@P#V` ON'X4p=z(jv^бLԸb57e83b"[4BNWW]u@J((R{1.֏5Nv,nlӺO^^EvW;8Z.ɮs]h1D352>GPvVKZ kٰgGx;1NYsC(NR>7`$x/zS^ڞR4Jɽ4=iSIs/!&01evzLt90: :i?ψ~OKz3z )sǕ7=a<`M`͛xs*Ѽ^kc{ tgyN`kbPw ::d>YJZ4{W1y7dD;&#) y.rkγr㌙*4ɹ9 EOW&TA˛pҀ58Q4}¥? ihV41ء]s=t)3XŮ"_TȌy^ݛ?fM%NPKt~!~^}=xPuz UN aw aQ%O07pG+$Un_*F>x48Gz|4A9O)y15s|x79^?(ȿoi:!%6/~ 9o!Y-R_`V,5[N%ۘǰZ׾PAh&Yn,lGDXՙ?OƺzR2ss <[R.TF` th kec*<' O[V[j5K eۢmt)E jf,LDQrNvNV"c,wSb @bܑO`m"]k{;hK#ۺbqHԄ(ВOZb !'Gca^oS*uTS)32mƋ~*)y0AֽWY+J *2Ζ+9S @1ڽb3H!KO!Ϯ8!$V?RvC} p^{牌<S(Uw e^t[|;ΎB&'y\S{ՠ c%aVw|,N2 JE pU Ll 52\0ź׺jDrȤ4֊x~d6 2 [wߋz7!aRGSw!g8ߐo)F]gήsP?s۞ ~ 3u9f e` /Mtzq'q(pc ++Kk-}`2)w Ͱ.BQI/{p1GrV%<@##HBd=^VGkSӱo2s1u'54yqWaY0@a"8@.,BG!m+g!LZxq2=huc>1#u,$ :_Zzf$;HW;6yZ$a-;W}cV>ZhU[ BԅxA2КGwnq54燿'(M׼Y3\q{W4O-E螮?mAm@C g=Pei>1͢B=C:VK/tw0s2"pEIB` !8lKBC'M BU8BY'm&U +R|]/ؽ^jKHѝffk]>>Vt7?wy¢T0:ٵ@&cSQ;_54'98ۜ*ra?a 9,rjCyxMl_7u=oCo+{S^ޜ0b&S~Y.bP0(׼z2Z{8e~bXⶭuw";i.Qgіky#3e;+=6J5MPY^)z!=mc^0{שK\ezx9! Ъw!j=;3 Q,?=jtz{g>aD\ƹC]jͭ㾏N\duPTs).4;- C۳/covIXϴxnżsLåG6ݚLWXCgyBZ-M"P+W0X8 *ctE26g䌕yGHNK h4S/ƼngPP(Py 1Ūi8"}7 \k?&`(ʝ:bvRf6IO ȵ_݉~Gl_>͹ #/[Q€n^!r{!/ۢ]e QtgM^eqYZ k2vdBmmj._B hLF}SPirRǚZHsy=qk?``x7 ϟE&h'HA\>(Ѵr]B(KxqÙh, 9h5K(,_ ʷoLҢё\ѓ7h߭ɀF@lYԽWd~KPGqzcm iz~qq*20XO<|͋n>fɭH^C+}g `ZOԲ2CM=y~704/c^Ɠ8x#]%遱3~Y/oᓮx:[˪Xk MZ8"i$ƚ$N/b0[y 堔N^NY^OS#:ڶև IDAT7@,xØN5:Z+^gżȧc, =xVI4 ԙIcJiXMH ,/kmI+]k"0^k٫pctSp4z~5Gbi.fe2SdoҴtŭ^ǩ+k%Gy K(T7 nr x'u/8>}08 ?AO5+R/bqe,Zc[.gʉ.Saű+-<2 W .<uxMZLVR?o?u+VCҞIV_9yr. t!p]2ٶ,̧qx+jp$ϬOp }; g }>FY:>y&U,vqEy uPl/U Аѯ8Eo .*P7IAGBckkU Y;' J;u:޶,!ŇMdRԂ/21*j ӗ' =/`-[xElgz(3u JC~ymש$'"ѳv ;'>?(|{&}m %$h&H'Of #by>O3 :b'ק] k[!Ɯxt~-:5uܧm m[J\3j[mknU+Kװl3UջQTU)eC=' /ZyQ:α*OE96"m?Gz)+9/>M>+cBs\2X3Ik{T.O{^mlNtN=O&ɵN)!ծ! -VSuj'c2;DȦZz^ l` ?a,K޻Ţѣ~]OGIݥ:܁W"r!zv:ٔɎa0>Rוr3,Hp*/ d8_V,DLŸJד&Բ?<w[!"vX['eNTIA8|ƪw|.)lL ¶1F{(0L5<> r^09%PaN՛4[4ҀsjiXCz>XDH0Wũ5=ߣ&x{ 8̗~$wp[_GS<> ρ;`vz*}{S'(x+ﰸ1];,ƒ ]_WfGځ /oho2E56l` u(U3G=Ld}Ҹ wS阕f8iʣXū cR^p*Z˶''  oΗp3 yn)C!U>}F[w{FwI>RcLऻ}LkHeUyzЦyS1[`xW 8Ox$b$ şO<ƕ~|~]wg{d:s:}z)-rt te$W M j)0Ju5 ܽ K-E6d%9O44=ܖc奺K/wo}liK}WXrYms{ZK{y,ìqP"N`>"&&/Jk_s؞ tbs_|oUն? 2ؓ9w OfK>3tg9}rs]5}so n(d80缟ߧD01^4yyL^'E2׼u(wAJ8/d٪r{ Q4N&[ւ"`+3fcOBTx-W(<ߣCG Ɗ mtdx+PUpp* >z8H!ZJkV0eͩ S7즂({uuDP`J8&Pr?]0ݷN~. f%[{Bu )h|zr"]- !JVW! -ۿbGyEu5S^{{fs%Rp[*i4TQѫLcFzkU-{UY}iЩ$?b$q'^}nq`/5۝4p`=UN~y;y2w0$5u:0mPzxI_N[t ]W O@F y.G ?'(#Px#67d{+#/D_V U&/pp¶N R21QOgČD|5#^Ϯ|EaUt+:} )l"\"c[:P"wlm8ZrC;̽v-0"Nz̛>XO5l@9s~pc ͒.=>*wca8K0lemTWfGk|?\oVI-g{W#S-!%Zw2r뷠} Jm/On˰6zLuxטlS坾_M/W w< }(-;?#/y4$wL^Nl? jypAUzg;ly/q4:􇽾Wh1ѕf lDUf uxJ?֟GПP+ ((A9G8Wa{L eR M_I ['))hخXԤ%'kKd!\HK &q )|t{QjcH}I?X)*$eVEv +y"t[#+IسvcrbVCYYy3'9d4}/ mB_3m;63VO{wJp¾ZM_}K ]Ip&$0u0ߩ_3B9JW>;~G|W%7 x8<Ŵ\"3GZFeNh?_oA HiA(n^s~ḘWd(召z 7i/`x.c(=+P̙2ѷw%4}* 1;3 _h}ь;! !@8s^CURoeC^-k^h,Ճgo]{lSu]<ރ"u>u%szПJMJuY{2ǓNSL^?W:l/;V}.~lߓp~6Tf>ϹW t&}/m̐&*a!-r G_e;m֪J~Ls\:-%O2ރ *εQ S _ t 縦03:i!p ]*(kYOHEe9zc+9M6ș'X:wrؠhf%^}eus4o~8Muw5q$ƛY&svB'3K~*^"Bl6bd'JNsM؜Jq9ZVEÅ1X=E??ߣ:|+Wx"dQ# y?Z2paSh$*d C+hOZgWco=Q_M(chpp!lEw1F{#cyt2go3t%դ3=^_֕]߆׋i]~DUtwTykǢi^FB13|}ދSF^<'ԄTz,}{&(!oHD]T6}ܕl\5@hv*u\JJW&Yӿ0xM X?3|N"sDG7*:9Qy1^[ q,t;gEE- ı_W.7|$==~jWh|O@!I)7^/P=,`yAߌaO>1ѧC`Z@vҚ\k{*T8B )lULsMj%'DkV!^fLd l+µ?EU +^?,8֗f麛}PN]{Xwo,&ʗw穆`8k;^8_ס4f?'+MO7}Ƚ>NQsCπ&-_vbF`I+m=s\|IB6^ׅo/*g k>$ꜨŬtw.@E6"CPnT׽jwbZPm \J\ՆjֹJ mQ7kɂK"U˜X4΋# D㸧[Pd+j_W#.;x д;b"&vXG-O3 ^t,4E}HAR Y3]ݞs +ݹmRAB| }0DX[jвޞl0<LT ֲƵ"y5撁K B+6l~m\g#0?[ƝQ;*$n\vPayW#_X}:l JQ^:DsPi)(_Fp9gZ,ۗwyV5S̏ҀVP;O˻w׵<;q" s 2`[Y|](K0*TȩI':2TKײZCKxCGT=N> MKZ+Rons.W8 6-mDRTҟmk IDATa U |(?젇X\ h x 'OrUjLkZSa*T@Xčߵk-qY{w-*h= /#;b: b2 5r&Rqq->srH xb2ģY+ॄS| x-쓑 { V5@TW9 \~F*(7˿>*(ĹR^_c|_@9;{ߤTfz2{-m9-UL(+}&b3*,Wsj 5aF2Mt6ԟkzkTƶ>d*'e,Nf8 n.wiuaA׋Ab6U`f9C3 T._̥״< j`z2GExM$;3 py^œfosT$ q_RS`3.۟Uݔ6T᥺7u'JVU7@q}":o'0PeRQ3ڮ%)_P,1zp ORʪk鵵-Xs G1WJi4  >Sva9G;ڞݴbwgs s-ZV߹-Utb0^gnhK2?/Y5BCN D3v&'4:Lv[Pa[&c^TQ3tP?<1^96ky Y7@8={c-V}=@6BAvfZZkVq\EųJ9PࡐDL2*P<t3W!7E.*bzMgVUo D???ѧ<`]=}nwb8Un!Ue.* 22㴲~_#WG[܌c( R2is8S"!^|ﶊ_̣ﻶLZ|R Dk%0buAC1][͗*(>Y UӲPs㩘5Aӱ=AV\/egiůCaߧ' j8y`l9̩s6LƫVYh8׌&zU%DPg sCW'&㞝tHv<]l~25!^ej-a7* ÌS0] q0nZ-ƣ=hQW`T?(Kq b&V8 Ar3XG˭s9P:Pc3B3f֧ ɅPJ`U$ȳ֗1 ޹=KV(} @M SSXs] ̭#G/7S R1W݀c':خu5q~O%Mt[,Fz%zt:`)WԽ{AXwhFի܇nPj*?"^HcfNdIxA kD9͗%֏ݡV葽{s9V~Zp9R5B)0cQ+CC چ{CyjZntԎ; 8^ ר>Nޣ`.2~?C$B-}?-=B} LUڿXTii$-bZtqNڗ0 br/eu;TINq<&Vhmv{Q;3,㛿 Lnw?jN8?_ckp't [.罡U p*,xJ;x};K8v=~ׅgܞzeO1h␩?f͑vv5€}4jh_pJOMc|ׂ~u0딗| rw]%ȃv0WiY:ŀ H\ 0V ׀T15:x?cp, O ݓBU)r8ܺ 8%ÃnqG)&Y(ڵqpѮ?Y e$u|TUpk{xm jOdrS NeWaPS}s=JYZ~U髼"Sf:!}Fܲ.%z)0x3a^Yv{.Ĥg*cn0'0Pke}Vm6A@Bت45ޯ@M{B>v$x߯aLa^)G{n z}_6rJ*DֵzXyy-nY=I-W'm?+`!CjM$e#35Od*xN?57.pxYƒȾM2cPqu׃:e>p˲ʓ u} yl֪AԔ[=U3̑ؐnHV=~U9Υ\WF~'$bPb>@xC.|0aqɖ gmO%COՓCp^?ފwspXZ[Bs=B eaYx_]8]*@ uR~sdʼn#LPRB18"* Zj[''nlMƫkpX>V 8jGţPTbװ^=^T҂[&(P0_h*I۪1WCTz;x@˨w_s]sfXƝ)",R m/-_QDs|_kQy{2лX&0RZC<(-:w^Qnw)$¶Ft9^;;XXmLvCSYll:>A='r2pSd<9 Jt7sPãmW? &"^8u ٧yQwe}+%WzpU9dSuܚ !*ۻ <˽; dX.x]yd KF@L P͛tP8 !j?I_„T(=ѱ(w[@Ccka%mڹg7^i-zxj(sR\`uH,|ZfVVZ1t&W@ϕU5, |e^n[DlzPTqPK܃?ؾPݸ @`ZdSj[Ь߻nTFqEHZj@[} fF̧O(zk6<ϢsXLƣv .~⸲M0ÓYeAw?/ WBd5;,wY}+ __wz=N`2Տ|-ȧb.o oa.܎iFZ+>wo ڨ1&}Ê3XGkkzg{wU)hN[X3/YG *-Q!o(bk5,NKH>Pt̵ߺۓΑaMЩXN-ڦ bI T;=}ט8w&ϥ'^Vnȭ@w4uM+fLt.>犵RfoKPlOȄGoŻ_s}+ ~8ǹ.,YrqqbtN^}GѫgN} 8sBJu泶?B/In0 O-<B{wEqM’coOcЋ.;4i*IzzkmA1Wꜯ9#;>Gה^%HKRq.DA r׺;[܃Գ9J>[{7d^l Wׇ_97/)O(l+) f_/vhG`s/d)XVdUu?ZS`60TKx)`VtJU ]i}Z"wWN @,B?E']:[T(Hiqyl0KH齭Ra|@@` 4߿ f_'&k+oOF[ઐĻwYЩ9}Is*kv%E1׶PPdhmIs! 6&Yݽ6<{5?#|ѕ}`]Ǹ^H{ˣC'=ktKMXW/^sYxm345CcmhI#ZmI5I4p "شt*<Cwt]("; m&)*f%#Ⱦf"G| +x>m(vq/^S(tܯi576(ʝc"[}T؇Ч }d˯Nyg&AqDyIڧg *Tœ)8ꂷ+YC$3z[z|oTrNj[%4LZIXayW\5nv>mo<;.cZ_ uF:EB' |^$(8Cm b~Xϻ{b;Y 1+Ϥ-Q m΋ G`ߔ8kaLctF)YO#s:rdaM2ezpҟmg5Sꨂ$(u]S`;*yVVţ@քTSQcYUu?dŵ[tg*SC$-DS=@:i-EK ->i̬޾4-֪v%RA2 ftP@ H T#s)Ǐnk kE٬s0p,eT͔4l={nm#k=}AUCDwsv~.( 7p2JYf2JP3S8`fOaL008ڢC*+U ^U@}h7 ^+ʚO'չw5 tgk_5+Xx>}zq z No???H/ ^,P% n^ QAI{_t qVkl[n94[z{PgsDv-[䞍+px8 gm,S٢}.! {Z'0j죖0@ ]o\ z:;WOŵ_:Ҋ3p:T{I9g}_lˁ- /~K +=CVrV_,z6CYiM΄do?*eQyb%* yW|1[кg[˹ <%iuZܧIs"_O(~?f;.|;f(Oqzp)5e8q| 1׽^G^w#cp-n**,@d<²s"dVq( H+(y[|SDʍ}(Me~%C.@T)rB/ E6q(o 0 IDATR 2wIwDžwimKz.Vht̋w?o(4Hii}09C`|TZBFA=baY[ ]^.|"H@ TR:df!SrR#JfInΑ Ԭ:w0byHjv0 B) S6|@E66OCBtjŝ^q^?W =;\7g-m[%g#.H"*Ex2y,|9G kijwǪu֞Uɋt8}ȍQ!YF46ܢ?Sz ѐcoA?3>9oG 3J>㾮92Y @6~9f&E%7ҟJ;?b"/N2 .'he^1 G^Lԉ@¿_qʽw$@W봜 ͡*Ŝl \;=vH=~/& eIJ+\;*:x/%)Bk.҇G e2IݞaC p=yzThcXDrQY믬0#P2pf0C%Ag/A*Bݥ !P/jMus PI ?TdVemתn=NA_=6u:K1c89F,1QUi<Cn}X{l|GR J),׹KaI˃6ඁEWW~?{$XiC/,B-rSG^4 z=Ku; gWt? s b 5^u,,C{? j)i}P+z#duB2_4Xva}Vw,_~t hpQ~~~n"r u=`*ҽ1)q!R'BţKʍo[Z~?e86*n{0_^VkOi4{qASRfQ5IjSZ <2b~סּ t{]@G}JIEe%u|;ϸ2_d7fsa[ ,T䉐% k70r+y)hoNDEQ\iO;k-n=…NؼRn'yV) l֭k*9e8iR*G+mx病WsIŏژ )J j/9 ߋL:)_-;ǚtIA `0|(SkwF`z: qx}~Myiej>_㞀84ۿND͢Tอ]2/^uh|GwCD} 뾅gX6hQ6 RgB(v8?vD5_ݻ r<ǡqu݇D}ml8T!!MtwC%hop2|ӧQK~!+\ K (Bi'џuw =cpi~ y'p~^~(ut#OaIZ/:Շ_(:cBTe ,~__f>=!"f{o"x2 m *&MZi;mGuh8^cb0&]>_b Tں  :/Zh(`E-t>\*N%z<;슓zU?U.GoG~_U! *'|4 ,+Ĩ0Sh;<[;QY0ͬXb(f 5&ájݕE>>4:0Jl<2yD 0{9`D7*CBv%Rp;Eeׁe|y2Z1~o^Y ? Vra!'xME'9?ˬ8 vZ$; *촌ӗ\-* X]H+ivX`V/kauјG7VKM!B?|]QO$ ||Jgs^ϐNe*t{VInF̽eiŅ[_[/)o`2 ^JgZ.W H=}m'e 84UZⳝ۾ I:H9cE^Wtaa}g{ѩ=҂tAV=8i?hos ̥d-rD:- }~Q{CY/Tr,/ιzNT/b'W< Q%y":K3w] dV)S̵RM: 5+݉RQ+aИ .Ҙ'8SG˚I)v @ :9] T'YS sqNs/N1WJ-3p77e_J$t:N4  (CNKAXY ^c8]~wkNwȔߋԄXuC OYÍڻJ;*? y]ǘM"Yzw ۝"_nW yɑ Rj!nY_[|y ]։_ZN7NP5Z؅"qg qn401*3d-<( L"qaIno'r[?J0>us\>J`Wcy9N] @E8WJi8Q@3%Gqe !l5oWﳁ3G&YsܽA :!ktxIWkhS}uuNyD5E R$9NGxoHqCY!1/UwڣLMnj:H?E:۝9Mw9ʰE23#Nʹͨ_?@V3*O H_ s>&4Vmq7aa<<b br+k#(̱1y߱`Loۖus~~+H2ۥ+T) IK4:` O!AݝŶJz72w;I ɻ|x;)W晰$zNo_*o.|/zm}s_)J<3K`mKoT| /#\_`{5xlu˱s^zu\:Znz.;0_Q4~Z PGn3Am$cgщⳇ &q(=.l3s?=p~Vb0G`x$C@7Ln\K!QDCQ^'Tw:X ܽMb;{#^8gz\_w*L|ɭA?u\BY\[;%`r;q^eikᓟo}@8[m0dUƷH޻n+WqÒ-6T)-l7R Vadr}oVH9x*'Ys+@q*x<LA;dbM3 0ɿ jxLZP_ZdzT.gJ4t,j/8-)`:DX0;>g(*&U-ȫȘ BL+ڥCDiOIJѕü\oXb  ' ywі"e]{4 3<ː.@Ľ裰ANX Dw^0Z9G4*{Nm*Z\kUռ]tdVﺸMl}*QEd*RԷXzc{ZHPS.&p8tJx0=}V\MHIv;-ьt7P<[deUi5a7)-nr@WɬW`; P"I|_Ķe1c&Zeq)@WΑ@:/C#l컶}D2wm-6otjkc=D( <ϵzi~~YG@w5Jͪ?Oؔ:Gޫ@vl}3`S@rsC+hH~ZydMjWVJt`$n0rP`'F!Mx-ߧk;KW^~n.ʶ@ $3*$Nw|wc[Vre{u6qE ð9u|aR_tBsOH'cM[VkP%7,:"PD.e`\(j2{-UK=YQ<4gU-Y (+y}T(8lψx;b-s;viHOaҫt*eVG ]xMY@YR$2 PG좴?b Oz ]0Κ$઩@B5\/z y)ْMyZჷR,@5pZV%OH;J o?G-T$YQWe4Ԟәuϵd ?wUYfR^Ba1|bo lO饖ίޫeϗfmϗR_ɾ܊`)3v";G2Cz ?x]BPme,/!<{(zh|0 fJ]|720jaѢ"Z%`# pP<)lsO5ʱ9/3CpU!ܹ8W Y.1Vq,T:RHG! hU,ki}Re1j}%*Ms].CLgZNMM0mx.fy?}EĚ.IX7;V 8V E*)``eX/=y r:zkynH߯Jy_s2Jb )+5>}k#ݛ!-QU?t|7U+GM=5;?EƮb)\L@I9g_d4p`a'z>h}ҽ7n=|(-P{v!=MIiM70GA @T1MRB91,(1P&5D%hvmx~:~I& vg[rzaξt:+lD.+ݐܷgGW`0y@\L>.*Q (),=3P{B)>T2!9)ī]aS]gZt  2noFˉ;TOIǎ|s9=v*WxϊDjz;= *NAMK!Fuu9'*u3rKW[&;iIIy2XLUs>NPp@ϩgt.4a~cN&+ $_Ps6w$~Øn38tˁŊ-r^d+ÿPO+PE&[(KkkK\45Jrf~zC |dNky2كB5˴òv% p6 YIHO}B汕tpQ)Wo2k)?UM.&YE;>坊7EUWn] }[of̵ȓi'Or.4Q|A*У Ș`F裌׸;̼v IDAT* kK($y]!Xt員MQGrb|yx0VG&힇h=p馲y t]0΃/X؆qy @97.^iRm/^h0)Ye߼i%c%Z_-UsOׅH&R1S82 }Z_Ser"O,Kх8]Z}~{o\'P>PЋ"3"8T@zÓ\T4=/7}W% f8|<ְZ0|.rR֫G{ #/R bn㟂1nc'֪6PWIse;wxʩ)ժ ½$H]0{̾M`sXt8ooU IY2d|)kQY)A3׿Z]<'s-S|VI98~ͯ+tPoޝ7 enUnIWgn}JZTj  {@oFG"ULh .CO( "1sqZ<VI3jiHA,.ZXg%47ڵb^XX<+p[s.Zn7s<7t]<)0eGSX6n;]|&U&T1R;AbkE,cuoSqZ3<Ӽ 猊eBTb )]<3Xb|y契ōrA\)X)S\{~(Mw;IZ1V'NH=hig[:鷌ck1qC]}ɢ7ǢeŘTZ3RJLW3z9g8б;=ϔg]ӱ|ѿ͂xݿ<)kL_n25h),B]/ypOQMv"9q_pC0~R >5lk3U`[oAOwXCJGPȔJ!n׉rۇswx*v*RV*,<we@Eӑ!뜆_kCAA1} `c֗ L1{}W\%k-%sЅϹxY~yi{]zf~:ޡϟrM&;+?:/ƋTS~1v h g}5S)z_Z+Ӗ%O*9W :/{i缿텮EOfik1sqKqj;JÙP}|,oR{^>[vK~y QEzD@x0ο#Stfz&=w{Kt:q-{D  !/rDjd4*,*@oh;QaÖptoqea/}ÿ =վzlk@*ߌt8? G?O>8Ge5lU!M{q;3UC=ϤAF\Vq/XbжmrYd)}x:%Byq!:`ju<ύAV|* dnPZߒa`>Gg0cNIoU˭h kI?.i gM+TZ(k' ]?n<8ܔQW޼ қ~.yFw*ojo)fAQЖ~}Tw+# '4s3)дїϟ?AI?`8Tݱ͉^s(ŵuDɫKAWҼ(QrY>ɥ=KDb#?&)T$ɨ*t7=18(gV3+&}+L@R~ wëKFjUi(~֪V;ƧsLÎcN> uL_4XkN FS9\k I;?c!oD`bdH@V3*x)IYT3{t>Zlܵl nT@iV][AAgP|-Nq_ N oJv>c0#.UOɿ@2c2d9Jp;=cN3a O{΂o1@S 3ݑUK@Y8ޞQZLY_+, h;r ēo}5 L┙@@[r.x爴Oy.z <&m qhm*/E,26ct=]tdxk4 G|~Es84y9A鎾*ι:Q G/~e4:ҥrMC2hOڅxy}9@fr먭Rf:@W[@J3E7rɗAQ`E̤%<>u: 7vS=A_,G Pȣh9iIX]x?:Ah4DD yg.~L@iRs:=Ye"&;H@(eGї$s#Pel&odASyC1pgUi?Zu4@0S{u\=?c*G T2~C8f5Z(; S@d6vś!>~=?P7<3k7E8s`#t*\_^{; @ym{4d /KP['/0 KJ18Cs, s# \cI_IgiG o^cf{wQ:7e[o;I{1#u~#`줇FǛ-˷;[ev9u-AY6>a 9 <ω^u xsO-^* Pγ CQU "sܞPD%FAhgoOW̘߻CxѧD' ׊j.km"ͺwIیfZjh:Җ{u|goj*ӹZ–sW pP9}c8c~&}Z n5_[10LL ־Lb/ʗzU9~ʜZ|sdľ c}:5N^әtR{F]\Jl%vĸjy>_ x|8Ѩ=~B_ iQOceh:hL}fwz(ɟS:V7n ~*ave@wDH8dcQPy j'rz+ u0hưIjs4|ϗ Q +фVzJ,J*r@C q߳XˇG=qtHP zSS/zDލ+dNnb?L}|_wǐ5ÔHwk.VR9Ԑ|ƥ`_w绅L#xM9Ur2Siޒu?7>[@hB-dT"-DeJi,:QZ~N`3{(g)Aʃ*}4e?^}gA_{FkގZ1|eo/JZS7>σ嶚gě`Y-ٗ2IBԫHA19By^۪ : I<{?WJʳh^X $ gO/&H "p9ts2-UwU[&'پCJn}>K<1qO34C@Is3'Bƅ5ff4ZqZllo=#JL oP=gܫ˜.UoìqI&TpЄÓ yȖ@!s$xᚓ!ja<2|w+&FbJrzߏ4$}!pM(Tx3]ǚ \cFE&/4h)h]YMxzP8XH{mW~K*}H&ͦaqq;/U|{9.@+>ϷtCV%|#|9^56ێ?#. i_|z;<twݗ5צA$-~ҩ蠺jr|Zt_ߝ'rɷ]z@o9sO 80]Fmzߙ}~<AGykQ3Ϳ:u #N@3.0 y +s_~DsVETpvl hQA OzoDL Py%oǡ7ni٧y2%@&\1N =oE>D5ZUVWð q\z+#vWwOQ?ӡVgq?؇:=}Vn-n4z)_4jUYe5̼t_85Kp<ҹN$4}[_ϑY @^W n ;cY0UgxmSzwI2{'o@S;ۻO}t{(QpmvgCڑMyq2tVƓH,8 leN@-Q;ӿ{FK6(mdJ2͓bgeDZl=;jMCgErBpU򚌫e6²ϊB5 ~^"B|V+Gw|S h`I`yp?'zd[klmۡ䯗⇜e174 kSzlc%UTƷ>xG< 'rpU.@I~WX҄GzELx>WU_7Oc8yrARu I܌ XnfqyҢ鄿97@0m]}/t?r.t5//p6{s{u򖃥W[Yx-(/Qtn V7o E|}m+@GTOPG,(٨H~x!adRaHqfIս}L\W"xLb28߁*G+3aYФpk X2 €ok-,;T޶/o6å-rxD ^NzzިA@ÍK Eϡ2%s"r/9G~2?N)zX+zƛy,-&*] tCoC;{lg*MO/}P^^/P0 2wB~_I޻ 9!sFPbN̻Qy$HMhvb&͍<@l.1uzÛ5݁' Kҭ? ɡGFɣ[w=xiT G3Yv %,y ҜGtMN/C.@X|2AG+JѺO)R,FY°(JQ(MqsL8/v9a@PPYql3B`VIEUY`'J'Vm|btf' c IDATjf-2Ŧ@TnoE?sWf~ϱ8?S:a*RsyLyx g>3yc+^栊fS>7pٿNzۻur΁PYѩo3׀ꤕ;k^˕3fBoqUQ!q]A})~!asSw3! 89oΧUbVYH ;^,k(~,<]Ίkޚ|[וg'z#B|V~I+)c(NzO9}rw|L^K[]Y"9A%ؼg퉜Wf q6!>'ڳfX|C 8IMK 0dz-y$iץ)$~%L ;O<هyȕu=6f*w|bf)éܯK;SUTDGJ 4~CJfBᜣ׺OۘJU73S{r]oPɽ3qzR:꫿u(< }K'o먄m9Rp3S+]sphFcv!5s=&?ԡEO~?cpC8=Swf54v֫:m[=u1ޡfH[8?)Dw͛T~R %)0P4% Zj|~صD<^"ѩQPoM^SFS#ܱ.Pv'Z8x~7U2au6 !REN.旁op^Su~t> 87Ty@198 hܿ^~ w>gw[1^v}dʅE.UA+Zt aڡ!'q-<˄sfgC/Lw#mjU` 6z_6jϘz)pA$-ZcfCa^PפAdoq>ϰ-|[Qh7T.L~Z IlHɽ纍u{*e``zV˕|F璠旂՟p>~FݛU=>na}G{XY׻W~)C.L( )]~}6&7:&ֶE _$(sJ?;vȵVm3@'G_tf\"g"nn&v`-%個NZƵ,fִzy\<ދq1w@¾ՓA#~6yvsb})2M[+K#gG _Hd&=Lޑ#H'aVVT}qƙLt a3wRO8#)L64t:LyL7.*`2~1TÐmd*H>Cᚑ)Qa^1]c/7pݯ~!yoh۶V鏗aM#pҗyxr^9?zMO4ͳOʾȖvlq/h2v[n\%j)ueFtk #u*8NSMz/9c[`4 N5ح2euzW@+ qfk\ǥUws͟_z-1 mZ-)4(T7Gσ/ R4=ԡ1lA`rpZ9*{%uǗa0A+٦DՏÀ$a-9Y1޻ld2#[UC<߫ m):~FC.ٰI⹷v/ H-q<<$ҽ2~h뿌u LIoO&O*b4ã`2ag}DaxΥ9>C˽,nM}?{"2Ʋ/U!8%t>@{n6LퟝDF67+r3z^>qDh[Uo6:S+q`x7Os&]R1]cNyȈG4jofܮȥಃS%%$`6!@$ұM}F*ښ`L:F[cOEd,6c;= kC9I~]>\EBb<3Lӣg>å='@yW0_BɸѶTEzd52`ͭ`zbgr,|]_޻ᕸ}?h(c@i>GO2yH`cz4p)wNmCoǭ k T~=.TO*}͞{vtaHfWͯc}Aeڶ&Rg;,P54~#A*(йQ D3!o7N4l:TAZ++U:ם$4c<GDe̬g ȽQduxQpiNcGj4i'WqNPR]k[ʢL3I*;F8뼿N4VZ߼F@&8{c<%1V=zո&q| έJS|<ܴ9Wr`gǤ4n5?73 #&|`p<7_)7pv =x:/@wnS9Wy)?S'gʷ^3mMwhklme ̪+蝿ߕF2f8>שS^sǟJǜU3yjť4_/.y.ʝ:dcXּc@V|6};؅~O>( MzKEaCQjoMrݡu-{0uT`ABVf8]Ta:ۙ/]Ï4y-?[O1g_Q{}&_nx D(ר"t.A5r1 #o:9XLC/d >smgQdLޤ75&cɜhfF'BhS[?_; ߗڸC!A ϰ9 1_T/\2%WC;i9&7a2̦рy};sw9Wi؎4LNmSOmOqfvDZ۝sv|M;Cڇ97}ڳGzUH<ʳ9%hS[ sdo3ߪ`٫A/=.0zLU R[EIX{iy,NaX ߿%ˆF#؀ &"&8/c}t43<$lv*/gGmZ9e$7 #P^\Ƴܷ_̊# sYN7DxFFob{THSb=ۇE]zc0\V\As¸ܒ0җaXygȘ/E/ KM埓!-Z˔3 : (@EZN{$dS~9?Q?55_-Aڻ<1.ފĒ*G !43|qRW_ KMόuv\o JϨ^r0v._l@% QM*8 )BJk7@QorhZ]Ph_r |NJ=3Cq(pA4|Y2J|dZWJw[oVZSwy8y Hj5w@J1`q 4r,kK؊](i`2_@vz {5+g}{]t$O&}>M3ݷgOw;:HX}l;9wT4x t*2Oy2 _kl?s>@A4kCP[h{}[X]{Ǣs-`ytE5}FޞN><~l9ϓG =Okp{K$I{zԭ#|pΤW@ԟlGkܳ5s/2Ȑ^zuJ $O(M^Ņpʴ1d擑}މzOo֗7W*xB IR2, >2()B LhG=.ljQ3Xk?8"kwa&3];P8WFƌ (M NJ!8DL>= B¿7 OFo-:dc b+J(1yzc9V Nt[zg3ǡT '1Zk9ջNm`wkB|1oYİbE8;¤M&=0g/en-f"Mɳ:wZ rq"r!2SJF V^`{ ]\&AѴ) ¿!⒪]^IkQwv7sh+%xqo)#08bz <`g31uU.uaǔy\>Q6I[Gk:^®a3SU)4$dd4P>917 $sЧ£c&a EZ4!(XnMH뽰P#/ 'VT@JFcRJ17-JwMbcU|!zn)\0xny̚ˈ5BC&5h /?ɺ0#9oUa+Q6|wI3Qoe(9{~rmĖ-/+!zޥ׼wǏȄ9>p*2-=ڂXsBu,w-PPd?GJsuDӡkAʈq (]+3>7;O]E_y0%PsЎa8]>ڑE5lA>ױP@@{I@;y8+Z޳VLekoA,)aPZ^\Tt}f* VZZӫlN)=Nn Ytm _^|x'CXJ0@1~>,9]ٙEyg0qg*aC˶uw1&٪@ؔ "\0,:rY+3>s{\ 4h$=elhLPڡ!K~ Q.0qrt<6=5pXVf'u (  T^}/ʚc§p:XmᇹOHLpdx (B8M{q\ bS ہZ6/pluU`LSԞH!]`tmP*>F+y-,N&mgx٬ y TrԁƁKa x|' "P`md`5Ao=R#z\s=m\A9bv#rI&+Ruwlk@~.Sw191HӤ_;8"\#KK:}P֭'qذ}3&g4!Rs7ASr*Ϥ T.ǺV&D)W(p00QA\0*~L($u_N~S_&NTM2$˪ VWB3)+X> ܴCa.ߔ9 0Px:P\ 8|*lt?N?$cNzX{d#.|\" X~;֠/z 4YI abE̙%2 ),wɜ Na:f:*0GR;_tWS }5.[G IDAT\fc'7!.~83vYqBI1%6~XOT$; f ?vgCLPTb%zZZ<ϤdSMØ 8vNN ' (!"]oAmz cY*amT'>~ /7˟V&5;R:z'kvux(t'#Vʣ&gש2"#$U]G>idTƗ!kT]1'T^K*/ e\>tqAQ1"+ˠ6UDgEc /(2X$z{3 8qlV+ xҕ˚;gTNrC_'RwSԑS4rx诇.i4B{c&.n;Je8CzO'm~+Y~d$[dž .R~^0bSd75w.kC`u3wqd^K-vBvC%ukXੰ߽(QG:0|j+  1 gAfp֘l5N ` Ewxڐ<3/=x^^~M}>mRƼzω^@ 34ClI%jwD#gς?S9omgֲxR# aȇUB2b1Eh'sޱv_O+Z!Ę(p+5νn6;?HZmCCUf'sVS]MdwnWgxid9~!70<ߵ_7>^j&3 'An1弚YFr(z@Rd,eGn2ۻnއ)= tO۟zs̬J&'ZkE8:'߹'?׉{W\й~g<|>v2ВFogYO& ]yk2sx2Biw{^")̟n2rxYMCܪLqAϺ^5rh^s;1If:Zcҁh̓)~)I/zpL QO K1㍾1¡;UVE2iPAsهRc(hQZQ܈ͽEAxRkT1+qcA^bIɨn8rQ;RU 8{5Pp;aoz^oIw/ /رmz'uVȗKw@~K;oζgSYU'hs4ie(?@'&4,MrE6`=OYM\ ԅJ =jm@_& ~:ul蛕skY :v~P'>tS>.|K6M(oDF?ׇ5vԂ'V+d% ۣ!cjnL@kKeݢZz3Bѐ"0oes* NNc#s< =bq? o:vBiξa Ƥ3QL{ER`!'m!91g=od]lk~ D a,f[iEGeY'DeB@$[:57= ˰ڀ7oOcsFʃK2F,Y =k WnEOy'o{>< pe? p{;hM.\T9?|s2pdǜdTKϒ]a}V TTJ5Xc+Eaeq[HݏScq-ECbH|'PS#ϙM %Ri˫ZX+Sb5oFLg,ct]n~`e7#"߁adպHҵȕ!d84P&^<׊+A1.ُzLCzUL5vu0x]70:-^ᅛ6+oy)JGDca6SFΑ~g⧞ׯoɑRϛU-^;4\Mҩ>8 nߊ0sP;[^`̲.WSGvul&: y]߀ZӼ,M 4hNK,{]7Of KpDY`ʄ^Gd=O |[ݧG2FLBbE3y#=Z ]}*[24Xt:FHcFC'?}F`cz)ש{1V,[ƨAiï;l3#OQs&,1[uh3JV7pyS^m_O z*Xy x~~yCNۃ<(Nz~ Tuh@4}]Gxsvuz5y;Hڏw?-?(n{/ rNyN@"d21xr` HdhK5#;v^h}Gp() Ȉ3anbX~qJ~=ͬ6,'& Җd/H8e2;31$j| MI^ێQ'w%Az~UJ_< 5Ћy;m{fHm\vmtp] J%O[Z0TS(f"nѶtÐ~s=PqJcc]V1Hz躹a'XPhQۤwjy3v|'] =u-U=i~ɧsL:#gYlaTdE94RƂ@^3L4dNtyzA -|Sds1Υ"CnF l#$wb'w.އy(X }1ޕB o*hhEt*QӸ*ǃa{oP`Px ݓٷ;yY0oFBI,sK+r8PdNvnGRwx* #`Np؁R"Ճ_o ; t' 4ZNZh8RY9&{&f wT!*3A - gl)J0Z ;״Oř~@zedvl,WjdbNd eUk@8[xߢ7è>닭e9V NEEsrJJeA!?jJ9ުM1Txvl޶Bf_ #JYVFNZx WWP*|&}r3:<&h!ilwPTe`xӀ mz賟/#IǛa<~7$H;{\KM~"ʋK}`ԩnÙZ Jȩ SCREp<#dxR{EE|3OaɳۗD9,U1dgL=v}1AwR(*}RA^ugD&@ #0T;2z;~հw7moE;玟1v_K8NMαL:o axn@E=g.u^G8Zy0qS >[ȿ`3>Wy)xz(\W4LgVŬAfk-M O_Vlu}F4O(mn9t1MM>gjoDz^bC'דo*k-92&)I 8f[8?8*mfZ PNÝbPrqt^9,ԇ:9Jߡ>SL]| >_um. }栿͏ɲ!b;ww\ڽ6KnTY eCyǪFpaeqvvYF0͉x^kPY^c:Q)RU{G] 5Ŗ4&}~xH:#J:ܟ5穤*kM3G(\eEC5a{gMp7CнK\2hQXB?2i[?Eןiº(Nv!_iTKHe7Cʟ 4f .h 1 > ތ?o9}lkxyf|vW>%;I[ey4<^͵~y/v__vC9xsIO&k,x+.rG |X c e7#NRL,YiLIf$Y2AG\w<F%1NB*8va%?Y@yy@W3N3d~:x3-*D%pRdF=wW?r(^P"&ބcR-ΓR `wj,Of!$9(f~)^;{TH+8#0*\qvmۘ5;׍en4~ͧ^9isNr:?IJ/VH[[K|繽F_7yR$B;Knַ7GSVNTe9@"Aڍ}ӱO#p~Hv=D^γI0*i&@@lܽT8-їr%s>rqL7@ꍒ F&O>6hn>Y?ώsRO槻k{S̋;V0E}@+wb?Tkh)V~~˲B/+<6A͠Rگ]&=X gUs~3Tu~FԳvݽ)WԽ򾛱ހ6˷_6u/B11f;̲ UYQN6u܊N{q̕168P݃/4i]R07ω[kX~r8u-Ui\ 5wffwCvN!x {-b>D$A!)}'"Ƀ54\=3֣L]IW!}m[b11R{=:p[o7X0i@Nx{[W#@:oQ,%=F ѱK^TaT!$<RwݼL9e/OOm 'Ͼy k~5pB:.yF]&*23 /.t~=6͓̬2 tLf|'+SQEn|tș{p*_`x9p. 7kA_Cf)O:'LvVq0aXߧ h4i0=Uyobc])Zxmѡ@Q9?H@rؓv:s*|qi龕κ5L0>5`᪋3۵I7Dn0)C` 5CQC4&|N Iy(3hg[iփg`u^ mғi}Jc# 6B!^}zs+=1t-\zKyz6~hEϾUȏ~g|U Cg#`/[q$Wr~F2O,IIů^:bu)+jX.@ԃN]5faI؅Ԣ=w1$B+I @G)јTfV33G@d#7"܏ )hogT7v*n"Z׽[/G苁m8i~Ɨ 8FGag1O玶ҏwjQ43gz%Ӟ;θ~&[>.qnQy蘛3*`c(=&gyD^jXkyTØ`8{"RA̠` dC+`Ni`a C@dhyfmq)3r{"60'E"w֓µs2i@m; ] $x֧hs΁cΉ?s<2E1{=TCyH)4@P%㊐`s4#ݰ)en |&1]\+h13P4~PuXx~cۖ#H&ulKqvk2q}gSCyKr zRKk]vLYqpYyP]7S/u=k _|'O6?Vƴh\ af{dI:*)sMH1Y(N%PGN*rw'L}?ƒSk OZh F0=:T0ٿ:v~E)ݦ*QB*߀3ŲDzϛ;^xޠ&N/1& 8nɢrY`>M%R+ެz|]ST3$Q9k1Kg8|:ϹgimSyia9DuF^} "`[c|ߺ<?Y0IHz6j&(8g N39oJ+R}x'߫}紽IӐj_TLwI}^huPoɇw4tȝT: Z><#4G}Rp|:Gɚ-:E$гSo3*Lo7\I2 j%GJߊ~%J[N@9SN5}OOٖ֓[tέ]NEǿ +`x۲=9Ur\9li.."D]>1HI2r"mL&;=BݣOy[d (;(~NK 8wKX )?D2숲F` id1wۖ'dVd8ckBL]ϭ](od=)~f5p|kդV4jdla>iTkȝ|<&&*u Q>fH}C &\U.'8;xݎE'? BX8 Ɂ#C]gLhu<ɰd2g_`4@gW7 >ap++[xos\旌Ш A@HT .84-{^мtҽwPf&}u|sW{oꑛRFYX@dBdyp\}~w.M7_Ffq=g}AaAg1δWYVkll8M~]O9Pp*CzTN#0*FZ~s;&OƳd{悬} ,hj4ݩ+pwU%tS_milնP/ͺ5G}3Lgt:Tg͠e'v[qϯ)ק!~ J[-I"?D b7=}"a@M|3rE,ʕX n{?vvQX$0]qo--xzO25,݁gb'j_mx ńvzzD咭i@c%\d0N5RHz:,"j.CFߚ#: ܢl]ǜ<`nCj1=*Z/6ؙܞDn˻{]?FƬB|v]wo}ŠuIslozgol -%]sۆU+ d ar=S}qY{da[ލeqƸ=xҶW|pkk#SAѣ<͹rJ7fW Fa]+޲QGCI<\zlzP>]`nvF;Գ&H D }'<'1蹕u#)FrLo6Fq) ƌ8M'SM^m$:xZpr-2"'w|94 Hs )r*0F4eׂ6)EI%, 7YQ BjBRB*ZvhYT)hA_};a?OjJٌ(PJ|{+`]JxJ?D"CTKf_ܑ\ 1PJazL{'^*վ!wziXCW=O6ZB17y8rO?{1c[$GRȍhr)U=T I^֭j x@O׽O?b#V%'d@{~Iϵ_\32ɾ޼ z-7{^rH_Qf޵֓mWt~y|Įcb{w8H5d"&\1 +d]%iDžH̬MC-VdJh9:8ո' ȹY9>?>J UYL 7A zHŠM;XQKc|Re52=OTF@+ʆR ZD|wkP!Pi?t:5ؼ&`8§}LXF8S\e K21 Uݖ#ɑ4O̪X]LLwUen@BUY.ɠ8(T=ג/z+ɿQf߰M+-nH^,$"s-y π_ 8dAS?G ;כŢMT]ao~~=kzR*;ae(sƹ>qͣԱS3B?kps^y^?[|O35:k;#E=䆮뿂5yO4D+mbQa kuS<J2A="%2Qp-l&j\x\\xt(%Ԯ?|Yk]u'3pUxQA+ƂÌ<6ȶq R&|fY0^UZWg9Ϯ]pZ3gyVо0:`=th֥UP8ZqLg>TH(xӇuXOX̼y2՞i1308}&\ abߩ??RBsMsjc^B)(߮^wmWJ_y{G_)D?dWlͫwL{թنk)>Wz|qX]&Lϟ!t'`xK8۶M<ˆ+_>e1FȑEWߞa|lk0kO-g|`Ow1I)K\_ I3?ݲ^GKR|3>yH3hfn8P:'0ݕ bVM)8̒5](QF0{\X !M~$V-̅kӧݣIhzNJ9Wg>3`F+3v˒+><\ô=ӢFP<yXGB?J46+YDQP3yҿdx); 5Vu&g/fUM\S?-.9ڶB u#]sY\S1dwe0?Z}Oc%\6̃m (%"M#\ilOA^kM.PH:zZq/Aϋ̌hA6G~"62ztć1tA9M3ʵ5,<R"xc]i ܭ#`ݛuq>IҌɐ >N>r(=+'!dRjT2F5gY;?g2x+iKG!eM2WsA^.^ ;׍}/ ͚bQ2f`$j="vnDj%/~;8H9cJq tg\_Ǻ9w`2 HzwXa\r93ܐmA)߅2HW3z)?&3¦ ,&CjZQqHO2Nr,kb=؆gD\Q2M]zA88i0;FJ8XJV"YH y1%)]eiP]{e辪nGBXC?H=)x<"טO3@ e-6p! tjRՠcf ^)>υهu__1{t3>O)F*b!G+E=g^W-n1o1EWv~)rvHuYlDwfg +r&Հ?_ d/TU65P&H9j3ᅲoK&lѲ﷓SWRdrC 9A2]ӕ653vpE)2 V08(j:Դ1'),y^ۅ^ M2g4Xie*NS ,MDsI`\^l_W$N&yWӥύ`e" uz><*X]hDs-yHYXGe-G CL8O׬Z)`:a8'R -(5&g buʉT`!f*q I>xbymgXb(( uوl)egQ$(|DQpR3zKf) 7uB ?t d."z(c%8Hs% RHtfL4no meڹ^/vOF;N6^MO D= d㔈Ga3-á ]χ:-GNr <0(. zE$Bt6x6^Jbgg2#Nܻr;%)߾yE΃ۏ;Y+e)Q;<9Q.;I]8\3|~7-z?|𒕢<طVRhg/V}1D*P^3B#I?IP)+HFt|&u[eMoJܪ WŚ/nz_΍$.--c!o>cpG wI=gd,gN̦oR e%s;;qÚ(8EA`YݬhYG5k(8@U@bQG=w?+sէke~r`}Z1x}Yɍ,(!>S/U|G?_< |= a bB#7y{y2r[šg@6mB73˵_@O/Nt=P턕GLmfr|`u^s5խwg˅t ۖ~*=!Z5@~2pIBVNד a2祰㮌/jm qҏ}' -a,]1AAb;"V^*mJh rh;9 ~{em;gU8OVJ8g!o녗K67F7aV@T3qOj=([^ I5 ټWr]9aV %x$+lgMCVr!%1PזtOA봣~'Iy=F$de Sݜv_5Y;dg*T,"^w~{rqh'NsYPeY_lf^wywk<+>?$3}!]3czCzJ;OAzRY)Ѫr;gJ*Iە}OTnJre !PJ&EKt{j(mhR?n5 6:VPr*=h'ʥd%*¾gI(ۅܼP*/e#?l/ kV,E'" F̿fw ? tLk52[$‚a&`_&b(>YٙاVpjVr?R]b' ~ nBoR'E ܋59ac 䄹5"4GTa.})hWD'?a(ԄG;mU? yhIQAhӏf#ZqxKCqA:1חRT`ĠއF8w櫋KItlr[c;*۶!ЛҴqf~:{ vshSJseN;=of]h ZM£rP#! =*2H_c:"|\y _w?~Wyp2ľm)dr@ƽu4mka)ufQ̈́Bmzn *?nhW\yxeD:d볁+M#ؾT(9#ο ן~jB-~~i ɽwnG8aJbyroD&k>wɉuz4rE 9˙8,=3}H9 6,$֝m+9;.P*ϊms$ڹHm)@b\e>>xҞBX{Дq!ya۶E0}IpLn v9NdӰ>ws=N\$Y!/)䃯{ѯ1z\ӻ8q쫛# %=-ڭ3ĉ  pAjD2]޽,Tgt| <98Sno Cm= 4e0! {sHemw^pvIyRZ$fyj-eIPК"V EY2M7?Vu=oFs$U5 7@6R󸁞$HZ@=K ʶZ᨝:[lb pL@9ƃZﶫCphXKF 1\/o8\D` iT" NsoP QIêK\>Nlɭ[{*f+f~x'i!2|Z;=T^Ii,;yĭ٦([q,oh͂t\QSy- 1sVq"+M̀1u6 $m :7wWHu/}璄v컰LRh՝zVZ[ʷ % [3,. Bv2Γ[7Yr60$o{!BzT,N6oCfKJey6ޭ2)`N1_oWz|}+2UGa='7~uxRN=['w蝣vNeJaOmlR;CBl@QXnc$fdUd)֨vcPj@%eJBuh2ϞwV<o_^ ٹwZOs˅ž#x?nZGUJYW0-õ[G~2|PcL~:go {w^ X>X+F3a}vWV K.^mXeObHgsZu37+bU>;ܯ@Xq.`(S2aEys ӧ`Ȝ#_ ^)%W+ԭ٣ 8HIh.{+Us+$H~bZELğJGԮskBBS]"iHM&u}Z'tv~?__y͝BGvRLh{G7(*o+ ;H+V4g%[ i dQ^_e؂g8*dBS9Ӻ쭷HrMnqjvZljւ;Dl&>Cuo\zݸsae+%%~@^ٟFU)aJw&r9J5"M~q*)~ +Gc ,XW|gi#YYWr&-+!2` s,0y)IT[^X1pMS}e6OZ)N}k :̮Lf.oğ___35uNwa~(B)WT-qW `};8U(H)@x7Nb+e)$ ;¥X HWQyF;lY^j)ǬZ/,l{2jUEP6ar9X*axO&h͋h?#=?xfq]O_Bߣ ~jd(R&ezaÐׇ׸"4W4\}BNmj|zLc"hZ-Qc1*M|xn]/Vĺ'QjϟD~_/-x pUUET}x&^ɒ(bo~ZV+4.A9Q{Z5x}}!lON$fgR>i HV$v temZ &-ڸ+o]6z&KR./ya4Jv?zqpvv/>&n99'Z%`;y޸m)PRkѰm9SJ4u:~G{g+DyZ1Z-9QziPIY+"݋1T d"yrXgF*i:}88ION4@[cs+$g;9ۍ7QIh]8,F+[Nՠ$iKmTeeh-Ŭ[q$&|)B=Q({Z`veeK]5iZ7 IDAT`|XJUrV+WM O.Rv}$e+vЪ 2lY'j=cn 0C4bPA7_\&4`9.0GO(r[5?ê ǜ_9M+DgD`4/> !_eP3L,6:tg2iƗazAqѪQ)ab$4UK49?=(i/SoiL PԨ/gvT?SRW`Y`ϸ`tTsBF١V|,|_y;| } o)2=um^6A$r}!Xdi>7)[#Ik Dtpz`b:}LMzقz KJ|+d&s"qcۯɜӚrp;NjMx{Gpez 5yJdmCD5&,m0;JN⍡.R2!&9mEc "W@\Oq6~|YrBL K0q|9& \ABUHk`|GJ)Yr^.Uk=Oj fDofhSbVV^g#e#aPْ37J hEJ6O= wkh[#9`F-xXH v gfPr@%O'鲐_;\!R' V.ȷ:(~LanҩX3J\\8D ]?w7\ޭ]K$l۵83Am.^'ݭ0 +2l_t(lusc,24o=|^d A#X㑈$Ҧ\q~Ph1۾緋iO1$vZ^;e+h~A|$wZDV},nR.eYz4eTMC9텟OzvQ@PFx̰*$DVˮ8  T,O8-Bֽ 6@Uq'P2[Z-h.$!~@,S>])R~(e')l|8^8gKq`^ hS{,@hl}+4)'Zjv~6&p3ٶLJMV4zm̚n{>6Us Y`ӵ\;-_<gu?_Jɬ1{qLhzvN1so2UY$%ˀG"Μt迾 0!j{)VDJl_}qhj4xScR刅yӒRvK Y( y|4"bCX {PzwHr:wXO|10r*21z{IѪ¥$Ѹe˙[d~Lv4ٻƎ6U+rnkLe۬SkY8*:423 VhwbEG<9L՜Ow7ZejDw mUr6Xx$[ƅ>|GnQA_ (W KcMr+sm[|h?=&3cFmy i$s\ ymƋ@ڳEHtGh=؋Fd!)j*/;rLHM6Tڶ)kuP.x@[lIggӁA5qY8w(1k3 :JuRoLe]gMtBN*Q҆ ![$ ߼p4ؗEPf9]0y!㳕4,1nTj) y~/x!9#h~Åk&ͧNsvKW^7LwegEX@ݒ'!F2؅l!(Sp}L 3:3` :tzgFhsWGR>54HxF@)!ܟO% >nYnqbSm ᶱ`/_ #)j\x,J V?c]H%%RâfNMY(V6eCLxbikR2'+VhR#R1b3fZ Sk~O^Ǎ/xZ6Jg)d!b]]o\O[Ak;`nzzѝ~i*$ez7_M,+͵p hn)g+nKقq6xNRZtμ. tdGZ%k=K6NYaJE᯷O+u=r('i_Gϳ.vvԘB<: 䬨tjH*VEᨍ|V(o.+;Fc V$4|bQY<ՠ0CYitb C<ضws)v̭e0#F!Lqpza.fY Pv?gL դ^=Y$Ҵ.M1{u9/O3dmLEkꈻUSु/CkYƳs\&K9eu= >g[ū8Πͱ̸%%߇X`oI0e,`ɋ̒SD2fdj }vL3Kbό{R㳦}Ϩpk`)"&L&#娹<AW Butv4QbWxX|ePd>曊WocJƎhR@0zyr/Zįxu /uq80ՊayY ҩ |s̿iq2줽pvy)T@ңMNf`ų>dn=\ 3\3 ֿ 633RWeiӖ':BXs3m"Nbxt[sQV3 T8k(Be,!ZwmnW3&ɣ[khHʔ?:sc]W{&LS[ ͈BCV2#%'nSaj%[c_ެ0uRv>[mk׬D(x런7׽@o$(ڻSZyN}p y} Uui)`tY5F 8e>{mdWd[PjkljWbڮESrI]\xEv xجV,p8Ny>$3"W54:6%;[JVz-QT)H1 c%?]O))q|90s٬~N컹q],O>)9[ ^nT'"D8xJ).+fX֕`Q%ӪecrJpR&cÕX_38Q&q"jQ*Eb^ Œ3{v ̾g.ɀ̖}- "ZOx8lVc=Rx(am snvMgZ,e-,@^Q/S{'p v^=1M4圼}&+bhfH!֭f[#\ r&$jѭl*tkE!/$@{3aSJBNʏn7k3t'PQ:)(h -˪ VrӈyPDZ7m/%*~$iiMxnJKjX b4VR?Ups-ln¥{F t1EՙEG1CJiko՚9BP2*Q_(Јf;2wJ:&m lm(S պo&2<s!֙JfaRX61R\tQY-P[D́nBKRC|c]]{0 *Yӈx JJTO"hr(=bN-3{qcq@C Zql"10xM>1GA0k:GĤȨ #A="֏}{$/eY-y P" Cؖq%9oDuXafrLmi^Iu)rTØDVRr,Z:rLp{|TB 'cjFzB޻WJ&zթsVc^ "; 8[EU/qqLӺ^_P:q8]0t+*Zi-EkEZ޼S,&;֖S񟾰҈%0C+`" nM~ qĭ_j ErS1q>~Bڙh2yMA)970Q[hy6cL"\]7m.b@9cՔz:j=V<쾥"SӃ!SilO+-A#gUED =Ll"xރ7.&וP@ȑq531r̬Z']5k*gj3 FZϿaEKIhҷW6⵫{+<{Duzd\c)#ˀ>wo6 fŴ )x*\^Bzg; |xU3'D&¹&<[bKPT7ibBlGNq77P̟L{B#{ܵXYףּO<~J9\X~0…}; l?$x!N, !]8`<`bYr߸^/ ,`ٝ”ʺKk@# yksy8OȶşQEqx3@%G8nn+#ޤ; đIw{-ʒtjlzǏ szuu5zɯVP$ dG Xϻb >W.ZH7~Z׎<(u]w"WVGIA7J.y`H#-NE4 6T'Jp$0lg%k"/J'"cϺ#BQ@ȩ9O0L/&r&8Y~CyE+YaL(e_ Ї}~';ʫ6ПKYZ5%%dCbS/N~^?Z$@&3R>; u,s5^nX;Vv*c~V~]^zl'< P|f-QM`-E@*9ҷsY^,>214o1ΰZܕFc᳕Q̤w^`}*:9hDb$+Sh{YZ:'ñ(֘ypԢ#zڣb fT8?ď>jIamlO6R gTD|g(J^:c?O6PRoV9p' ӋCr)4|ޖDr<";Cc0vC`;-M”s?6rk7Mcg9urk`+Kd\<+E؁OYAءfҊC3$~^8i0{*)LTVr,X 2 .򺘝LP#0r,NoLEl쳳 ygRzלMzUq!עuY؊L<gB51`Ǭ1<} H>IOunmQR[XFf76qV=| ^^IW3߈HKqZ!A_NuAP (ͬdH/ Rdqء%A~(zANG5{4ͬ@a';f矊I)7zu=gM FɺI26QVq*::*t2<x^SÑ4MkZzX@-c Eɜ )sI`<]iZʘ !P"DVwœj]+zeޟ?g|<"VN}/}9~PnF}Gظe"#h']V+<-v \zgeI3,R8g\kUuHoE*3Θx*֞=n9W} o,bå4Zn R{MVecoIN*= X,y?{+ul/ء~IZVC#eOL4A(hS;%"sfl-nٕ=^YZY.#MVz׀`Kևe!]YGA4tbY&^ @6MnNŕLb%>sAIt][ sCd£ѣIQhC#Nڤ_XC,'+==D꿑ױ )%y5m 3z^G0 4#mQSRuBMhYze٭1'N(x"_SFLf3}/k\ /SXR d{O\t-bDʳ ('D *#x4L}sQ{!%{MϒJ:!ݫU0疬W{0A l~8P Gdu 6AUk5lq>L!|8~QƉ[8ƻKD9hd(qHZOf,媚}R `R` ?ZDlbDžXvTI@xȖC-mʠ*$@~E'͐"hɩ:bA0N6܆u'CTXfC`+K2a؟BqsV^h1ɇn7kGvA@Ð^t׹'=}?WX10F@MF ˭nJtȳ敔|뎹 ;H׮zKB_R+>b@U(mhY"XxԳ e 0#覛8lUͰBĘb*#zd" ^:fydfTm %>&lzqa햙4Xw5,dnG`~d6ވ m|g|Co/CC2lzzOagYп .+0 zu4%Q{l `T-  wM=$EB0g#2arK&,f" s$t B\afᥰfbDJԦL6;Q;@rW+/ʉJyT򌈜斄kUPȺ,h7]m]Nꪙ@ RI=T]`٣B*2Կ۳彩tVE9<Mrh\Qs~Z*SD=ÌϺȆ!75pVwZR<޳ > F_`JŽ&Wt=s62=a7I?dܹ,ѥU5?׀+*k`@0`%LpaJo6RP`Gwo a A^YmM#ٍ3泙tV]֮J25M Q'X4vgx_`2;My W(f90I i :fB(wWދdO^:#jG*h?܃zIrr=!MgxL~~wz$[Bҭڔ>HeK#rbBവxqa&)`ʖ:Z\8:x'jŹiev=qSfJ 7iTUZ!n^AWWX"Xl;$kS(μ7{b[Lb[ƺ\v= P{X:-;In-٤$ZJZ竾K%]4^p]Mum AkJZ;xP;\2=+!ܣ,/m%nL2).jU"20'_! [CC%`PÃs><*px{y(-NSܨ{E߆ay0|+,>D%NJgEvDy8Y~  lurNDk)Y2g~"iiHj2 %cBaWL/E@·;wZٟ0ñ{sR?zL4 ` y?ߜlPKDMKP7, =}*s_HO-B`i`h{k4OgH9Qʵ˓~(|X$Dh)}[3֥% nP+yxh|YWƜr׍Ѣ힯oE{\bhe:Vp/Nj`P|߄ ' 3 [R:DE U H8Лuu)'ipȾ̾)X+0i0X,oTlV5 pSrSMLB3um~b<dn2-S^lv&EzpuM4 ZX q%V)Yr&&Me\8P`UaY{D!FӘyz:MV"@#1S^VzI2/k8s#2h^]v,*>~ ]xSfbϤjG?(L3~A< )uL0__ UPLpF; W2/ڍ2"'u\ͭ| R`V|И^nDSS;u`I2{h4%ҫn2>36-4MbP)39-ݨ@IhA>J!zXҩ̋dufk}H[o>fdD0 G]ҘK/-32 SJiƖ0Y9xA̓ʨ xoB.n[0?3byV+@$d|Ҍ >f WKy-3,[eh&FnR  [0ޜ_Jp`9Ϡa PM%ށpM3!g\oǏ+d2Jv:ZeA8AqǑ#2~*!ɕJ={L4>zӝf#@MW ذutV)$l9'Ow?-\$n.>& LY?#k@X&N=@P3/n"?Lz""[&j2|x8j`' $stRB@J9b|ߺwV$XT],>Tim3DkΪCqj=(C/kCi!3f?- v r.R8@ Cx:<έ׵}I}ȬawV O2>$9LZ훖|!WM3)\}O`W! aU سm䝺$fn\LF'Q*=9o ȯ|,Pۗ,S-#ẞ5TMoӠN!owF,B%LF[OQ*C=;K>Xw z^RS~"țӰ9.6DPiq0PkTlfXphٹ߱lHdn v3p2_Ə[=iAd W߂q9ed9@0%VvHDLwE7\Cy<}\p 2m*%T>H;@=33vDvNJD&OK))KpH {h$[WLxX{6z*´wugt>li3 ЂV}KP;6Z9ggCCkH_I?FO6Ž3|±y{t~32YlS0Tj௨ΪJ% J8Fzԙt I;n ^J:MZb,Hg-]^q jhszPPV0 ^ kRRُ\pBiM\J9AW~HMsl/= W#yfl b;2#õl+ @Sʗn՚UVxZ@4 d-==T,#qvKgςS)w\CQy2 @z1Σ@8zpxW CqJXΌenёМLMz.m~l鰬J V"Jr$nLPx`*c8kLxKb< +"{#V[7k;ҒP]grWΨϋPA$I}[Fk-m ٙV YsDW4(N?,n!g0&uMJ5`'MH+={-ޖqW~0]B؄ڇz?[Bs!L} qGim d^-LFU0n@en27j"b bEYiCA$<Wv?E1C?}sWfU3,z0tgMb+o~ȖXdƹ}5 (Bʺ~'@YCށ]w5EVTVYqSI0=[ {? ܑXȬʲ$t~N*ktt6rciYϾ,;It\Uy0)AJ)s8Vǧ_ uv\ _P.{ pVp?W@x&yj"^$se{\v4F-&0Ncw AGL%o) V,3dƗLZxg|iOY\[d=,a?5I;_EBjl:h,ڙx ͮS)2Yq+k*!覠XB u2CivOCܕw VzR ʣሆA,N0O;6Zڏ(Һ陁l{TkJx/o%`+:HP{hߍ(; IDATi6uQJ^ L4hkUƜ7lL%6 ( Xw]{m9a:4\SކN$$m>2oh jL@%'tv263Ckᕘkv\W:CE2I %Cdkn s[ =e xTdjb]XbAYz43 &7im.<Ҍ[MO¤'|)ZD5+<+nQுATrDGq0%XݩL2ܤ ߧ/iї&S LU%PѼbRJ$<+V )ZC)\:_;qvE24sv^D!5 _٠*CV\ZݛU=E wJ6;P'/s?D.U,MMP_{XSTPA| /Th,HBY5/Y،Fm5qb53 B*W!O\ZqdTD ={c)"gF̼*>oor?et%ވ(~0jZ5<%E#J)grw)#k"[ųՀ08$Zl&{g~ٗ!BN˿N ʓ Kzi0mZUGkM0נ`6MI=EL r{Fl]!cnC820Hޱ8^lT#&s72.ʃx.V\6;hS k{@ӓ O{3g)-g2y_~GruV^Dp(4T,ٲ`&=(cɵs"n bQ.&¯q,S.4i!&e{$>@/E(3!J~4 .]nj&S3.u:c^X*Zod s(N9yXkgLZ|/Z|0KkE'N6GN^! T5r=a LA!9ng.0?Wњ:Y0]ʼf{u})[a1'uu@$4f(*Q&6 3hʳTxI2ֹ#:5s%GrvEy&>fPW8ei$DVQiבvf?:I3tn!/pQ;^D A#렂lK+u%H-$`CSB\ݾV^E4zsC=ϳ3TMx^!>*f ׏*Kz3[ c*j{}0HkܴBթrTf|>OAg&]AʕH~9&Hr QOԨ/Hg4YVvCzkW+7DC5mFV.s%N$`n{(MY3ڻq1~ny2 K7zMp 30eUhMp]aeuy+]$`?1pxFPs}nR3|L= Xcl@`D\ ]9WٯA{J!/#8R1Uk٬a%8ֻ.ny_ >\EIwB9 8OZϦjH7v&`wPwXt< !~@5YLf5ȩ>W 8ts<"Le.2LxfBeg<;j5Hᘊܯkkʌg!UxЪsTwYo,қ4 hy&rʉYa,ecCabz!Bww@ζ9ytKQS~LX8h_= 3_Yږ<TYoo#k}CFmǑnT_0CuE}̎* ߤ9K#=f~+W743 0;QU2jeTmmK輬u;c/4 Ѳ"FDb{*y{Z2 щc Tf픥2=iV !+hU[5QPHuC=nZ/[["3s}vTNY.VZSY3Dz_t^]~o;^O`bD3.\=-q-z4@GtIߣkj:WPD_*T=[OgjklHsZez em܈e鲁AC@wAZ5<8mѹ (ݳO`0&J_[{2(P& '<x/> {<~߿r"n^|J2 )A? Z r-`X]w[ؓ26Pf*Iז3[ß0)qY$u]$(gbEZA0ԹK=&=jA ,ok\SSP5 _ #$g8{n MWfrn5A`,k:7$;)Jsu_%Հk|6&ߗJKxty}~m2 o7XwOYc;R?S.f5O?j}~q@*W %T=Kda9r:ӂeJ #KƤ}0r&}ùufenrk*㪽a=~z~_}Qc5IG%5:jsb Ct69q.`LzP;[K5)Vr'8&}00n/1,]{:g| $ij%D&^&t8xvuwi;#ҕ*5"NO,辶5n55UlƓ"8TlG[L}61#9aУS~8^i,UԹ>sƇ* ?ٙ1[q\gO_77#M޺&⎚QgU`KYf=YA/{ԢB-E Z&kYq+{M \,Gt+@%Ys& `ԕ.zk q*Y99 Э}W%)lWL*O^~EM  ~t^.6G2a{XO2pY&~6lʙX 3mʉ#)=&nC`p};3t;}: V%?PߓцX[<@zc>qO:&n,y>T<}vEI ﭕiJzVܢu"²xkN :D֖jx Y*5+ #1]iZtHwc].OJ FojXr#P)_Fqj `:+A&PT)[74nG퓕`^bL5|=bl|8cyhi&?eu}Zk(:$[_VȢs &L%Pha*qm9Y@i]Jɗ2<*ϤۆPy T[L0(yjn$~+.<ʋJ UwP(\ryxQn3RYgz L 5?S$s#ⓛb8;@@ytǰMImϜ``w!41А%t1B:{U5^/f0=~^2?KoPgu.snnZPLнjQs>bjQd-sޑHjPE$ڧ.<7; T#am}z~3Ra(C s+!b]n?7g33~1RX y%|q}=~X^۽r K%Ɍc}ǘ~s( _jӽҳxüފ?י^6UAkV šiPsna;3Cu֜tYEѡ{!os =9wt0i`{dz(ߖ5 _sƃOޓ2.ox}OG<{:NޏRsW}>߷/)z>ER&/?z;'.nzk8n%Ѧs3<9ϗ-Aw_vZG- şA,q e*L7{8zc5!ZRy SU&7H{]m9+G²u\pv6ZU'#~Ȣ ؁dlq,BL ~l]As2O{fc>㹮B,GT!O`@QQb"9ޫe~$8珞S<i 7 #V8T'qHI^.ynƧʽo4ʸ= nQMBwOo䬶jEE߽+/?Y{:W+:ws/G|BX>7X2ֹ,y`R~h?(}@ˋE)'}9괈7f+*ZlƯ22' >g XZVv4rg{՟yXlWRZ`j1CqR;Z5eiCKlu2Ub*C@喔XH@Pc;KE @t".eB,B }:7@b:&'_qk&D WI4 xL%T=Bk#fic n\ IDATP{q(f wRҍ^Ǒ{Py~Zz%lx< _?0][?Dnh[N}Ik=] T؊oV񕞯Vb U{W3@GRLhldM @~c< w׆ gȗ;ֆ\Å >.C3ͪF  `8.=v"rKA*a}0 -LTQ7YJ&؟ʘ%O;vDw֬Z}G.sTu)FdF fX;\r;~똄qb;d]_ī}+!I~U{!҆Y6C,ή*"ݚwkt7A2;3=m+擠z[805VpO0/D;=3y+PѲt ]N wL9֎^LvpmLU ^.Vk%Ot >!:udkvK)x젬hksO5>SUC8:(C6@qeD)a5arP`|r=#$x;GVT<%9]`}衮Sj%RlrH-z Z++'>R~K b @09P΋aa$wץQ ̷|FQ<ϥJyX)tI xsVްy 1;2 {9ys]P1er yFY9tԪuw%Sҳf`;/K*yOΓO)WyIk `xLJXT(pVzSPބ1d)+j4Z*Vt)Vݙ1]_IaVy>?<Ϣ39:Jv6C(ƞ=C{kmk__]_|-@_:1n*dݧ % I&] ǽqVSǑJ^Ӆd/<I4]뎌 k7!4{ƺ5.+E\_) "ggX]ze74OsL̹\ B)bd<=)q܋4^۫V2h' re ]̴w9_F5o / @~J5Zu *~.g٫}ƏMD*i g<[NaH3"|`/uByݽ?u5 ]eGkTx>zp}>8ϟ :$e|8ޟw)Ȍ[ d<ڟ]ӭz qBs)f]V!*?1ׯOv 8OdQpa׏޿:N|>o6ci8=r㏿Ͽ\EOkr}>i"ӏWy:Uu]8O^>Hkh_߿3WfpN3g:?^].ĵ5?~Kv1SQFǽ} uccrH` ~m9}bJf>!Uވ\=6|y@(TpGy*HYg34G F 'HPH5g47AZFdq%i b+WICAB͓&jAOMм١v 3\Cz3[IAT,-~.j8,Rdxp2#eȑcK0uYs,aJ ʰSrh 7=v0 S:d(0Fw4Te1yxv " @?p`+h!gZ?~p/wo-}g#YBD1F=T W@P,4q(LMK)[ft)`|c*sR!Wq|qg&Vb^k2W{VcY0V P./%Jk; }]HaiH>lB omuN{U<))1k z^UV^sJk-{*AD{V*tϪ[j芍$R/T*!?V*/?K(Ltiso UG}( r G1xDʹ6hى&RYv}w NHo67/Uw/ Á9[ r3ӣgx8_wp+Tzc!KyZ"+4VF 2 ٘jUKQe04G7ȏʫ1Dj(&3Oødv)kjDkœ5l+W8О(֞>wgY |馬**0رUUeJ&_pǧ<k_ ,$ߛEyn(m؁OJ%td`^=<YfE'5XIظnVp*rf]H9+,UftzxGs0C`(!z8b $:YQ,:pFp MLgɃ?˂rEkX~ly wo*PMUT>0MB%{ҙ5'7--Ӟz%έSty!y''?ָhԡ^Ekw&-{>S0- ңG WVo eBC^9 ho2 w`TٸgfM= [ȽR2yjg}xUs{ C޲Jgk K"^ʝ4_ V)FyغD=$LvPY@)g<ݔ7B#̲Jǚu=9 Ϫ/|ٮZvglidH-c?ֽ ߿^O+|*KUI lα W?EG@HpB҅ZwnT=]@Q;EB˖)@^]X[[+ڽ \٩32cSFR1eef}+t+o(!Ќn"tY~ؖIP@ -cf89 ?J%K"Cǂ$+̀={\wt`m@ݟ Gܶ-AO1sll󝀈wX§PZAH+tC4h ~t%2(BAA"R Pعk1FSUc=,{߳cԨzꩯ/8싢B"rt&E@Wo9h]BYeJyWAM,,Y>[RKT# I`9H΍웢kRGڱ8tYG ɤ0|TgȂYR$L P2gTd@s)D\Qh2(~D eg$Φ'ݱT&Zor X!BXT=Bj97֝IUC z7&HvkFsԘt')sHmd^53PNnM6Z+f6@:9{eqضy/:ױ:ncg~:\kc4e8X0?@Xk4#nʪ{= aR$[Bճk~Y|29P2hsXx%8\J e-:6(3k?^ݓc *u!yVwĚUj?!ٻSۡ bu7E 0 LDƒz-w=^_(SR&U;U,g>cEe 2Jm|=yvk?`O<(\ qVʸ]q-s0 6o ~>[kF{-U=5~B;؂ؤЇ}ai?hйB!❢StG}@cTҽnn}Sx,R Jm\O u]ݻ[iMǒ[8 sy:ctr%"X UWs3w֚[{/kfӓpūLFb.ۯ)5GݣvȋZw^AO>Ξuj/&w:HLtuu}B{F^9ׯ};Mc4cTVoG~ҘndrY9گ,hݰ\9ރ<D Ugـ9sW0 i4h$9]Іz\nF&Nm}yȬ@̌<W׳?ĦĭFxC( s}nlHvOB'1mk ֶ9hg0+ސr}PG&Ж[zFEO/9&pޱZa*஠UG1VvOr2Gz7Zfg~Iy.jWDt'ɍ3Po!V+۳W}QԜmMZBܿճZ{w>}75kl7u `\MQZ}w^Ox~u?SZYƮ57?T# zξN+naK!-]oz`)(6MGU3D"bx%Pqu6P 7&"|\sH  9'*H.Dg*WFXoo@gï.^Aa?gUQ')jq@Z>a)X W\KC8m!`kDN wsЛ̥9( *$y{;݋MFs-m_l4G¦ n=WuһM3LB~6%Q֌, ,^WgPy<hmU(; NY k٧$K4@f^򺨎Fz MW)diJJ[rIa_O\zҮPjY} 1P%9{' !`OCA9ke*8(Tv2< 3b3Z<{b@* y_|aX) Dߌ]}Z#Lesm׫A5JSH LTAYMr0wY؍Jc :KUc҉P+x[(I]Z] 7O~7?~7pWy}ߙz]@FΙ;7.Z] l.$djX 5kl1Ь]ܯЊ,0u% ַ]0럌:Ϣ>kkg`Kci4P[,ca5add}x16yq, xR%M6Fo~~pO+ Lx'@4lcѢ=WG}-N* .Z|W(/Ma9!ڶw*(yUU1J{B ">#=btF%2JNVt{mȴ9}L[D 4k e+zA5kBfU,Çr҅9`LLĦ{%FUC7V*g00GXklWz j"aWX2Vu]#%S.R{\s֊$RZ(YETq;~48G׵)?e|ciƼ%̈H~}_w9{)`\lqj v=Y C44tCx~k.Rϖr?["Ƥ9/B4N ykqgeufVbȲU~ȞJJBACo"9eљY:6.{%f?Oo3dndJEZ˾ 0I<'ȇ glEDz\6lU\|r$S䮬a+b /?SPoV\f>,ڿHK[AU-|Jâ '] Dnhy9a8+nzwt)b9# W (+Hc?f`{; i 7dUwi DL-(ڼ:9 b7>&êd(Ze&qR_mT]ĵ/@'O MvC\C}UAe}z>oUd.:'ٔNBf*{[O]缮aPX3z1EAZpqDlڟw !`{y{؞(^=cj[ҶPc Gk8qgIھ*ɺV9bDe"黚ˍ FQDKRBԿnH ,IV^HE.ɳјsy!k|!J٫;@r*d_{ɖ7IaAi0~E\\5ͰT1g ,#t?W92n߇3kQݽ"P";s =اL]+T&F:7bSd~zW7x7*dϛɶ3MjN۞Nw?~m{3Llz_ٺzmq}gr7?M^O}S@|W & ]"D&\+;w(+UǏя8nh<+q BׄQ ( F(5hHLMlr1jW4JE = gQ-kqIEv&~n}l[T rHl VYϺ[B[\7AMM  & `K.>VLXAÊ\ "PY¥X#^8=ǒ`T=q6{vNsW6dqq&X,tXk\S#ڑmD(H!j4ϫ0ٷ76[f p{_ xLSF**_X"" }AZxa,pWmA'@ ώ=$g߬ :z?܀65#f&:-SL V$Uopz0 gbI %scܥSL9>qqp  ]T2l͢7zc!1I5g,;!sX8]\I]v5"||W@ͿUͦ'w Zf<]I 6OGoviro4wY zDG&y1Zf5T05yz7w5W& =VkD?'t*qkS#k rJ<^u=N_SSvH܏pi(kH*dа.Mq*#ISWaԥCK~-@ݯ)fkp}?cݥ䡕9k[֏VBQ:~섚Sw=Jgp:'jrcUNwtjɲ8;XG*8zv=ћA"cu,92 /gVwQR;ܷ)~,V*Zys#@;hjLgR#NU]ی SA 2|kLusٽw,7DkyW1FmWxce@,4z7 Z{,^9hO#ǔclsmN]6yfJF>P'H΄|>= ]ml$t!2(>}<yeO00њ5ʆetZHI2))%ϵ{MSUc3hykA:4MOe -9d`߫1|SiwO:mZj'}{{q>_kfkfSU'Pˍ1Yr^d`^Q"{H *>3wذgFBDЏc[q)0 $ HFȫBBZY .KA?TbFHiU.K*x.oq8Y =6LBke/3pN A' o )ǼhpT;)@O])3`m{\.xng­_o%@ޗ_~OU%e.6 7a#7{0̊}h(kY3e}Yx4F&Gˮ]OكO!)LIP`&<ʍʤv5IJvx5;C}fĔ*0F&PkT ɑ^ PkAzte;$<֑>dE<[m#*eʆ7 9D¾͏S!fsk{Z e,\K IDATc8қߵ%˒8UWy %K9&(zv䚾_M0keyu^  'Xӑa~B^T]h>fxc(+%V} f6HTbA'ND ⣑D۪*ܕ}KxM|v "^֚Y5ᬒ!zdfTGmT~rdR3. p=<6׀[o;;kF(k)T{a{L|՗J_OK7C}|= M5ܽT{wϐTd=\=[n]8@?Uӭ[CʳܟsYrִpLQʓtn\xNhm4{g5fc(Gi^u,Xלtro*H𺡘VozomѨ^J͆Qx~,iXFI ƙT:+ _&VvgVlQlK:m"wP@fI)+\*zhLHy-~) <@ Vy7@כbn`ʽ1 L};؟ԭR2A:3oL}%e2^ U+YoA,3Zt;4@lt3{.|q=sKɝIq4w\%sxWsX=wg+ߓ ۮZse:9k&fu$?TNru8q+/^w=+6hx J۸$0(ٓk_Jܓ57y]x3B|MyNԕ,XPIʑߋ4|S߾;\ +pK,yWk*xR?1΍rVc^UDE6۱$WZ-.3(eh h%rkqHXV1tƚ?6 -.)=њ'WiywsNd'uޜFg2[1: MP|̸S*+aId\M$%\ܛZgʵpt(,SLGm p=b}z"EN?+c n-p{UMtumS[I<Ձeoͳmőpbݳ+An`3?ٙ;ح<[].%kZQLeAн1"kX .3͛\X6Jrdq4\ Be5A@WCSKnbᜏr`9*=?#tȰ/q*yܻ7asx[,KvVh\qjeIr='|oo0D]`; -uԬ.`,D7pO d X֠v|sDu:8) `ŬjLqeu4y}24bzqhVwPUL =vv٭EqhWQR^)]m9"z${D^VcPຮ'^_ͯ|Ctjr̐U=GP1&2OFYn4yG5{C])d)hITGu ?Wv[a jqDf ፍ3aiΜ#"nd9[/݈6Pq>嚭8_P%Xyupz'hYWĉuAN1b s@SGzI1vc$ PO@}y]˓֓jUxis@RYյ ټG1 z-)d Z Çpuы,&) ,\W3S"<-^LZ۠31s,8~|1n@Vi"E~{sZSaL<l:u:H@Y✡. oiɱpM]! 8ZV+@*:MB5eޯK/uyaG7/T"F+(3B$&3f,LTmե`Ժ.Y奈H~W̍%b{rC_  [O͹Ę36#ekqFƊsaa4>%f$yZw&'Xx5^)nc{7@Я\9@pQØJx|xДf Ś2QW BvXIѽ)%@~wytxou'"oȵVצw.) DU{ ˤ-lsjC \hKG:V]p˽J)5~m9 dQ_X3zsGS ʔ=g0gcD4W?(7dV6 xQf&gi|H"8NI?%ĸЅ5ϴ~թZg=Ƅ_c\bGÄxO/m],m 9N9Uev&OSLj%qi`V/}ia'`?=ivDKY&;14for]`qA9#7A\zYȀN` @{y363%λL餬܇;#08EfջqEZuHm@=#ݧpޯ&6Z++4EPSlo3xX,qM,1H 0Gɜ[Q"zw*21t6#{k4`pAe)WMbፁ^MJ{a!cFTwZS<;'h.׬{RXRUdٳ 帵U|?#?3hv<6<(0ᦨݬʖ{h*)Ќt;5saZf,o$@;zē;^x?qbND,]hcy'@.͖ nlraoe"Q *η |tA^#Eet,ֶrRPy@;) dihN5Pm?fk1be\j˺7:T Pu_B?O4ș .0֩K8z6+L ,~X %\czL.چr\.e])TD9рjqox'Ww9&S&ruPg4޵g^F oRC:hXoq!kCk. MR5pBw cCz4aXkbdȊ˵TKᛅh=C]%P,D?F95ۻ 6d{f3" &Y52{33ߛ' [ ҙtM`P{U c),b*`p!bM7mhfv ]gDx$3YS椵dm6V9%/JFuG8R'd!6@R* >/~ ~}> i"qa/KAB&d8`1 i"B4o%6)  1aHP͘%cA)&t.9"id+%Q;n௵ppW?K9a>hE\86m?ÛyZN3*zgL9%hyؓLDuW{X<{w dtϙ ˪xV eC*jwv)Kb8;W .F<; k|嗿/>cl.!Br3) Ɣ̂'(K|cI"(QdTkd>)",})U3D- Z2~HJm6f0{T"=WBaj[Qa OEaS Hd&L4fΘy=0&6e>֌&-c/Ϙv#=6dC<΂BKhV&``ØyL/y dxͫ?;CpZܛ:7(d0јwAַ{@34;6f}EKa$I"ƈH#/%m mOA6aky1*G?~i-m+^ iQBs0C%p+t܈h!2ФY< ~(\/EP3;2`NMp,ke6^٭ V*3>ߛzID2T3~wLMs(Yw#a6H'lT\,Bo-{ H7JoY-'K`+:lR,]O+hU٨iyizW=o}o??Lj܉GETʍ_3э"pp=/\&UU Vn6+i)J ǃ;F/05/:|D)$%jZj_@|sFu]xPZ8Z 2  aj 3-1,*ZϖdLD!˅ӳeǠчS"ѵp-k,3.Kl o;tYk`N }[5{5.b;fC^6KaƇ=\'ڳ CZSȩ0 M+q;~pj zrWν)y w]<[|tk`0qXj HPzRo:eLdœ9êOh,|,Bx hFIKt nuxqJĘNcQU(0"CFBC\77 O VW{Ozl:.8uegbt!_A/МҚYps0ף2Wa.*|[ e:lq-gsRd;A:V7&G=DV+ x{h?ܾnGA/ ϩm?? o5\ƶ7j9n5l =[A zae|UHR MA='wR,qX kc?K,sBzu]8OK4jR~cdlńLHxg06"DP=S>;3Y;C֞]NZBϙ?!cyO]N7&KNt^EMR{7 3m. 4 2Ɏxڤ!2AѼ yL5apεu`iE?Ѻ7wZ C'ܻ5h\Zxq¤9@f{{ۜs:3JkRfcZ<[UBi"U{Is%QvFl @17|c`4Z8ci9>I38Z S4W}x+ )γG!Xde;> o$c<O]M11HyBPZh+6yXX0/g`B 1%LNuztH 6E;y4L E whw7rWC &.VN<1m2ewh݁wPb9Rfͽvkxy%Ykʾ۴yfvǻˍk*pٜN_^Qq|vn^P ^*bӽIWi;/;N?ӎ+>ȨA#B3œ^jƄA{pubNx?󧪸4Fk-*ih>?ãR%%qy޵Ly\ yd@_$Fg4*Y't ]R˓͜lV̯3߻kLsh>^=cLtl@vYeհW`U g&+1g/!Z!L8u|WoV!BQ\rT-nQ `+Q8t9l1'1J:q8TvU?o@)CsEJŖ}4sM4 Zu%q8 j0I'q}{zc|V&y1nD@>oO4XZ if9Pdy 'LvXnBw o:Ly aXgn @sfv*p]?b(G]9*2&SY!Łtkv,yP&W"};`(=,;cl-,‹038塼5|gPL}#xl']һ ŋ+P4DQ<9̸p@iƢdmǏ!fq!B0O=6;U~|X:J8,ӛqz&i3$e2fe]!c}8n]$E>u](,h ?@?޸>b<(l\5"Hp:? ! jeë)a O{%e5]g<{o_% Wp%u2LI1~D%)} 4bʮ;AnLm-iZFð A9F0G jT *xK:xGoآ/UBڵ:$IzA3qTO4O8bß1`(WeWTu솿W`s{߼g:=*1뙉9NtDfl^'׆axӥhpt朑LJpTۡ 5rC̡e4ҩ)t_t:~;GVLs-g|%-0>Q2u򐇭V'hn25TU-b |* iq=:;U){UR,StQe<֓%4_iѓ*\lgc.`F]Q.Il.7#xN},Yrjc/C_[' 2RTGڋXޝꭵp*B 0?~O5̈́-C= ~\ !'ChRta{ ݻ103g5txXČqF`zbEus&^{]O"(}}Y4nݼSdjqhfSUI[0 &QRccGG|F\K/CNlB JOh2 An,$.(9^)-y+&QFY- by,5a# c( &mDCjLPjLuƢeK/]o>|4QTﰐDo)3b*6*&) .5҂JEa8+)N5>^(Xi_K_1,W+\*,Tms=<9. ǁ1ޢR`2\慘]:v? LWNVm@ຮhiJqkM#Uf&+ Wo Ο~aܗ{Ku* 3cEQ ֥5׎HD£u`-<<]!4=1UfLsxiyk.B~k*{ <ƂzLx?3L2Bk}BNI`i҄rjޠxH&*,ZQ󵊁 h?H_QVpa.p6Щh%V)Dh{{0kc`,5eVvk뛷1`n 5Ex6Ui17t[ Cގy4|~Z*,x8CZ }c+H!ǥX \FtouhY 0 QG Q&`f;}Ky\Ó.EEH vs.aY;e&n0:ĮNDHGu@ɫ"X,c 2mRZ{f3d-78-2A+[0H5,*p&ȶ֣*'hˤkJL< 3Ub}e;SjIsPJC<Ü5[NK?0*NbEy >|;`v6`Ť98Y9wu]yRNyf9Hw?6㦀bt<=PF5켎qV}.',!k?`Tpy#_yooSNbZ-)9icEC9x\4g/z?4K1tXBb8U dN/l֮ݚ,HɵV$p)`?3K*sB8d1hDwytV>(O7Jݘҭ4Vn!&ч2gF:w ]uo9_4k#Tks(9pC,LO1j! b= xRBhmݜh3SJ(`Zf%q%|Ͻ<'Y|YzwV# c 2m m0f,ϔ []UO\88sÜ (%i-ifcwС{+^؅t{dr9d|n(()qTbiNل ,@T.?y)ef.~kax0 AfI&vmZȐT|8Ql~;-`8NLrOݥ\0mY2x>b fL1S?T P.8fR0Y@IRaԜp4+sxc0 3Ox9RU$IFҽI> 8*$j(ie(.=@>ELe!i-qBʷ5\ŝ>Ϥ_J>J84G_|G{ F#/%S:`͐9ɖiZ Am>0 0!/9*Հq(beK$C^N)YdkIooo*~=̐wxHI~tS k2ŅRT/T9ZN~)4Ô5GȒ4cB1AXyaHJ]M^Ef{d6qϏ*' ^G򓃨6>#]w# ȩqd?S`H͠g8Yt}_)|1=??YL}: G3ƒ<ֆͰ p%`C]>kْy~nf} @kJBiACe{Ƙ0 |BI렇NyDˎcCfWql%cMjtx'⚋aC]c*6i|c,\ zG*£dka"'4(ᦅKTH tU6l&TN%@¹sI5s_#Z 帚Hl}?NKLx%TEPG$T={R,az_c8V4@cjHu8I$N)$-*3d*,F7>U*Ws."v^ @"ۆzxlLe5 ~~be5"!;a?PS>7B`\5S2K߮CǗ),+3!`c\+o.H>H tR}͏"w!#jx5<\+ :7Kd&AKۦE.tHB%JH$W w0p=s{AXQȑ1b$&FR׽uᒖ^RB1ஹ6MJFÃ4b(m)fљVx i,hI>!́HkC&& 9&QG_fQpZl׈Ȩb.;i:Gg r tP?lĸ }UӚ!f=]8 0[ʷ`zVw݉ή[>lUuo|{h}~ +x0A' 9K}VbQ?]|7LţD[Hm{g@9%bL 5'+{u0 U$$usD1%i.1KDk}4=8% ='4S ɋ;< dIsXXMNܪKƒ\GQ4J*Cxq6B}Ŝ/( s3W5,"wNfK] 2L? Ј~_~Mg(O<&Ĭ۪T.LsuÑb]htⱫ! elhT;K_0}qT@_f .(,w^'+;ZNJUHHtu,e.r2°4Tp: IDAT-9Dd6L1Gސ>Ͳ{oyL8'f ၒY} Ʈk׺]w`&vyZjU MXj-*+0xwlً;Lm"budž] *VJFM!窹bn87H0!EզFr^ܞ4έV)}M)o%o&LԔ[3U ܀) +"-[s`GA¢2] 9g$Z@Żs"20Q>Qd2wQd)pOr4N;θh`Hrf\8ui\E[)]tt~bρ6v|a;zXDr+A]v܎icQS9ɏ3v‘f$x5+*4nk̯B̨w犉@Im ^![%4sNܤ] >_/ |?C; ``OAK &D|>[Z _Q+i```Κ)] !6|A1b0UsЂy✛W>[؄(Z$-A])P !J%2 R6Ė9;lߗU #`Su8 倣_qDaU;1܏:n4fΎ57`L^8Oƀ2ḕ[*j` m8ZS鎷RƇgC6;>(EїYOeu9\D 6Q2 L{O#F7yn[6JFMܸXkf-do'T/cIݹnkLa22CkL& =Dc,sLQ)q ͆ן$!b0_bq89:a4.{=D4<R^$En*37΍\i _ϾG VzMSA{Z?oO^_@_Vߺ& qz_ ƗE6a14#;?fMq80ζ`h`Ru8WݻʏZXZA/ز@dґhSX:X={0lIfvBJKw)H,֡ܝ 5(GB~mH+|:dmDI OqU[zwo\9zᣗ2MB 5 ۗlx ,6xDJnk a*uX@Uؒ]?g=Ȗa;zYj6[ pCWq'-Tq+ٝ'QIo`Ԛw-Gax{yLNM2nÇ? s)J7c SsSj^ 1BQo^Ux^+y&*wڣmwpw@n, ok}@c$'>GUDU|w{{ߌ/#/Ÿ-/91-v$[URVC^r3.S ܵȮ2憥R"[~0Y@Zp.C#!3b -g&}BrFgj"t9ٔH !UDC3zK`Vbu3[ i$I }ښN[]|GQu,>_9s?/hZ@C7K)rP9ώ !4ѤawAJx>Qo7;j40%[{8@ t>9 I`2Z`:ԡhQ 5łd={ـ/ @;x P:Yw s&<—˨Gۤ@Jz9φ7?g`1jW\3 U2l%BqQoο N@Bwhr&4NT¹hzȋm|.aq"G $dqo'Oẉ uJ0~E4L":\22ܛdu1Ax~R0io% JاHِ2RFY"@2 e<Ύ:J-"Ru4c uٌz "-D8xSgou@>ϐ&{I&8JƑ_^z8Lloooh1\0c3HjGW)!`7 ̉r!xσ}6޻xy}Y+̭ k}n,{@C Sjsu;bC^ڜs $@Ywpvsg$HȡGΉh B0QK8~tƾx>;>8cZL8A9ou>cʉ b@:J@)1R=B),ˉOQW=X]H&<^qT4-o+!_BS ^]8}jz k[/x)ӟ~xAo^?'~>$Y-t! {'9cbִzb7&'a9qj›X_ @=Q ZB&!־gD\!2pt P}绛G+.뼰U'`bG񙲩s [?䰸KzYJyǜff,:-YQ&B>Va G}]CTPULɔju|&,lNNTfkF+U$sD@$ۚ~ bn ]:/%!d} laGЙ4Lqu"=At>Ɋ5.>TD ",%c᫛əUkDr;H3ǽVny Ҥx[d:1!y*1тԉ]GYr^hLf)QiCgo pQ K&jdaTKL+ZAN|:w<ΆUtbYx 85Ƚ*I"1a/_-R8Gk]V awuMRb9\ :lO w$r**HND;ӓ&=WAN|n-+7,~zf&G^Q_ė|}i?c~?Ҿ<%%wr`kcv-c!uULz 1m"~eLf0fŌll#MdJj3ܪ՝,KN)֓dJ32f6 RɨI6\L_'Jj 0TZ / h47O? 9;)'ád#0ݛF2B+d ̷C H ;QU ZH.Lq,4$&$WH8(0'y\ >2тZ Q9nc8nD(HVM# Gg"̄)d<̉|&H#dBĵ<; dKd79^f;)!'6yBjK H!.=e B r|d 3fpsM<Q5rrojkF*>Z"k2U'BpAdO8.9#Z'ζOr/42 ֧( [ Qt_ {]ҁzH9p<- 9K;6D T"~O+*RzzF9UE2HBYZ畄^eRq h4:I$ FCL"9s]g+,*1j`odWBPYSFpBh @*,d(M#W&`>A GP)~)st-`W%;9y}0^҇ǀ}΢5uF!u%:gFgJ)#emȴw9[{.9t=Ri۱Bs5!ZIWQ| ZZP2X&xy R+MEpA@DHkf(]2 <۩}&Xd-M"I WB/vY[Č~~kU֧ڍk4*c .+>ͥTy >&o@ ($Tfo!BK׿{~߅/#׿4+ q0[QDtth [kv"Hv] -bQ,H)q ,?-Zc-a"hh8lRX@%JR@$ APJH މ/^%> 桇(MB:}JJ(Ǎ0 9':a˳3 X)pKx=9_;KM`%q=ވ@%Ck'dnQCv@H29|>w5WL.8P,1CDN{&RQ<}b'ZJ퉜 m8tdHG%2R΁s|BMA<ȵQR+ˁ[h\삞y^l_ϳ!Y2@\?s':f3YxFn <7ژQ$srSB-u묺[=MǍEV* -8636}%P":T.e ̉c d 2d1PgFyw)hQЌ,Gjl'Ϧ.œ+Ib WJ"h^gssLL}{]%t$=;H;vę +, Q48oM+H 8D(sG$zB[nfH9+Q^ O|pIQB#(m Z ӝ9Ğ||ŶFLB5+Q1tXDsYյ&7li SDoVAH-w"wsR -$*,muνr Z 7N(,XۇgN8ǶrNAj)/q\d9dž'YV4cbKMTfmu)c'g{Û IՌ{9iKTX\Gg?S2 V kX9Lc愚P@Iݏ ӯ g܏Yr[(x{xvBc::D GM<-rNׂdYcgqnlv;e'rpBcLvgs:4Lc!gYf#t2>0톏^?8{e-s=X[a,|>֨` LpoIn W!LJ*$,āw] nEпPkőc4Zp EػZ,Ѵ`B^8w9hu{!Jـsd9TA$@Z\3j:/:bOO"-x"ԵC$m!0}w{3xVɀϯ#%aKQd"m%mj/d.7D#M)IM>DZۍ؊c?h1+ZgK9?}7| o A|׏qMTqы>fgI? K(V3TX C3G`vbelPZ։"[-ǖ6j=F8Z`vٲ ݹܐhLU1č)|!uJqAAΆɬr DwqsbR1&oˆȶ%Y.\v'23fHLAtcݠBD:rȻ@XqGO#g`,)@miB s 욦;wcYW\4qnǡ/Uۯ?'=cKĮ7C9(B⁏W쮈ؒ}u\XVHX V՜$xԀ2-:רx0p90rHKQQ``QW$g[sSS`iQ^j _;*H O7-b(BcYlFٗTuZ2muI>dsjd30(OYP0j`i91,Rqm@u-iWbLuzijsBĺ/ߎDZ37B1/PȰ-ӧ{1Eܗp*NIf;F,$XߓDNkCԥbqm%GX"\*|U6`D>t}p4XD)B◙Χ56 חTmPF?-o#׏W7>[6S-$+uޫ-S6Ĵu@lAO,Cv矆 B%E ?-IZ:pSn")Vuf- SϱJޒ+a+"i@U'DzKڬ.ҔvXi$'ԃySSuBؕK3,`*vuGA9.Ŧt\86r휂(SAs(1ULήUf Yw5Seq䄗[%3/2$&!o.0QO4X0E8-VҒ0qy{0|W䋓AٻfT4KCd>NI|,9b> i,k. wnUN483db8εNt)$ViF8y]>>1gdcwY'Sfp&qӬaOR .=1rMg`ڜs9u-27!'!Sfi>b}D%LRG!1P(kcrʜh}Ґv=5^H=FL:!ms$c7Ո1]IzkgE("fۊћ3/_cĤK%J|!N*Zwy~w߅o-#ͯ#ts[t8ڼV=5OeLv K- ք’ْ*՚EbYMӻ\1aVښӨ%eiñ:Me}_7~4*>bmU@ R&7p2s, 6a%|Py|sZe<1/\:!AJcC{$}`E-=ގnxᏎ 2pX6Wi 90}HIrpGw)p̟ ~>l lqA[*?G, 8Xm'<||ͅ )t3 8x̱;>GGf8 Ȝko~x|.)^Hu%gcyԃH3Dvpq2y?9$c8qp7 Hy?' \+a\i'~`:l4s 3*+l0RP$Ymk6-02i(儷.cf$ϮxͮIm6=Ng~`"qqtSrc5ZC*X:w{N(u4o[q/<"!}tG;Gs %φjy̸;`a&Z+ЧuJX_uvk;j5{.~z5Ƌ]])L\:}쭂gny&}dxqTgf:s'H~+x}%[~q`# ^!SGg8dhQM=-iu`j&FN@XL֫V D/S?\2|Id CbO\'7l-]Y{'.7f 6,FC:?@|0dx~!AÁ1-ӫS/R0! ~%q>(8T3>WYŌ55&IxG'cNO=^_!)Q^+-dZsraLI t$L  6q6Y6SF slf;BcXToqIəE]v<θ{{-R+[>)fu _{9]˜M%m+cV2pC"{()DLjX_^DR§92G(g‘ ֊חW!䌏?쳵_`5⤄O TF'=ay'#¹:iz>^e,_ˋ CK*b/eeTYSI;T帪ϟ/vU{?shnxp;?Weg=Ч֢@pp -sX&ωI@6k̊3l 7Lo`@(Lv <_t#7@Fc)vv18(Fچ9Phcg\0' ųXx"#>.^ x?6n9HPe#{cN14]A6#O~&N\CEXͧrLsɃ@rFԃn=,KH$d}Xqf-帪x~ﯟ__Ɉ΄Y2%^X!qh]sY],a ;g2j9,"UVVL,1OQoS.% jc&N< l'$@a&@GNӁO>|^uؒyfX I;oΰT\*qh)tUK")1S;%`9id+u%cv*Ns⃸0Hanz"c]וۿi^qX g@Iг@k._!6ގR3f|6GA͆9 D ;0&nFI GcDȽ!"aM# 9aq>iCCG _9 lׄo xr_̉1ۺB)sv3,ӵ[=2{G2&6`c4XPʄ!h?%2ЎM@d赵uLz[n7Ax ·x(]ĩZ | Lx{vgcYU29*:5 0)t2es_aXJ_ejwbãycakmЂ~SOPYe%bφ|,__?'_޿3QZc((WU'ꊣ:ݑE0oBg$m:;q=Ga VcL<g௛+D'WK]]gY%C<(d)!(i!_]7,t)>Ϭ[ KF1e.Qu {9uO!/ lKb?9ĸs!x zɔgl (AT, !I)߆R*Of l9i"\Qa;O(r:*a5 61 (:wǭ_&|E Ao|r~wKʘؒLHX!- a羉bqI$/G&:gZWW&)}K ?Q+gf 9% gOal F[)Q3Xꈄ_H,S#`5~f΃wb<9xgfY:))3UPcr,LB}]D9d{R3,0@Cbr6F#a;$y`dA"1PĬmV;shTxN~hp\ѕ<y BgJ (,G'OhX:bTQ4sG] HPr6pӁD B-*3:%ʉy;=HbWȣ:bg/:ϩ}\ ^k]t6lL+wʈs"rqT!Fw2+DB2iqP.=>=$=K^`O+PqZnmJk1[CHфI5rTCߊ4*¨ xs8*Rx{> G}cLOq$6yfSgq=<4׮Ǎ/ >d󉙈jZ.#]oU0K0X)PW\u&cO UY$SLP)R1b&KkZ$hk'-4~gkBJ2œRvuc0ȑ5"ծ,1f"WLqfxb$.xs(Dmm]wn(i>O`jYCI |ָˌ{oX!W=N H  |Ť@51*Vry`C+E~Wܟp/\乀l?$i<[v^ %12\D= h)\FՓe,1Ϝ#/7H*2 z8nǂ~CIU0<W@|1. cVi#|8qRoO4.YH/k8f8͗`Qڀ!1T?9*߿#4i1lL{g5( G?]̓"1U_p[@}IJIѽ/^PD=zgkVaFqi\D*/L/w<<$36 >0]ksFm"#wpĆũOLX}nӱqsm aZ{u7G쒧+pG;5!'~q$z"^)5HsNɢ4 _`Z9BR0y;kISꘝgqڍZC0p6s1kvb%ƹ; cIN G]CmWb 2 ED^9'ȟE,4C\a>s|V05,\&m32rCS˙L|D&R$䫢 祟i au^d e׭֪I,[ @4kfY#3.IbIŽ FUTxHB {j)Ndër)]Z\C^B&}w[Q Q0!-Y{(AF pij)aghȁ\N"w H[}td@rN'Db\d3v0?穎qhC8h:ȍ1azhv4EO-j7Zh'wL&^A쎸F`I-G{D㉦})g=>6gR (['RިOÊs i18-(, r 1.ӳ9I]`fYx`I+1y͠{Sڬijy%Y IDAT!>×A1!{cǿ+ __yKnm+9[ օɒFɶweHcfl,Uõ: Lhs7Ra95sQŌpkLhF.+|;HIP#ܤp`R!1|:n* Y=~̴8ӇPo4q Rk-a<"RJt "],p q6sC4`ɚv](e}vDa} 9g9φiM~,-;XAwf1¥ہ{9p ^cwMYl.8^O|648>{>1Цy9ᮕE܃ Nb@FKh|sdz #qcɮ9gn<IOY "I*KTpEؕOGkWu]d8іk箤 {|uG3):1Ypg؝!5s%u /C`;}AL\v.'̄f\- $DI. $ZZBɝ0Y%zaQLkouAȂՓ)#V|P&`rθ7hp‘ޣ4!Cv=mZgJz[ߋ;֧>PR"&>Ay%)*AG@|$- B0@)D?>BQyU*V{k}s1?Z}̵/x=9wﵾo~sGﭷzLԌ@Š.PjCAt!w!lp%&?HprB D0tlC|W;HaOm-B_w쌐.INȃlE9$߯օp9@~bN\JmjU2֒)$$T ;ڔ~nuCE^& xxhsTbVwpI>F(Y&C*㞇"(TX@d1XD>JnkJS"1hFYe wIkѹvt3)I'mB pӉHIȽ`Kh=ZFWI|#]9Y6Jr>b|6}Tp_WT؄'N%r%Sj&ԭ?H=ήAQCAV[e<>%}Ҙa&WpZK;ocl!&۾P۾ s1_k1>~wH p?Un;mZ䏠~5~.ͱlh>e/ (Lz{`N/r9(~|r^B4xT)`d#t(48Zɩp#Z RU>nJ(>pKOo Khr99ym׌Dby E˭˜r{1;"lj(D0X1 $C5ĞAR!8sMt8vzp&;f5ү1>~·ϯ@†n; =Mi! l[r2۔cZBb ycNZHjq&X?HAs F>$h=hB8*9J|Z 9]!1޸> Ž6#E (1A(C! ؏3+L^?<7񨾠J!:ED<eEUm,a(|ܴ_ J|؍7cƊ6#y' e8̫`0Jci?~g;OL8oW{d]$PDThF~ˤ|w> vU|qSfP ƞ~bN! JivKw1Fc+$/h Ŝp:* +G TîS&l 8ebާ"XXH-8:G:+dž C-{O8m{ozK_5 r#T_$+Po9%Qi_fkEjۜ3@(sAO*!k_+G+/ɐJnނ) ɘL3V b$(" K\T^ɠ&L}ל1g[D9O9ERײ:TAM,?3A*0sZ;Zi.UJKj%|WG"wjkp[VDJ-LJ <s1`/aAf)JVuR9I{p|NTL0",y(8͐Rk8U)O<1+_`[*B>;,&S;f% 0[ *H`q{oU<$̗YTz|m 񛣟'>M2߻z/I*.3jE #F>͆nV*޾{K^)@xZ&lDhys+]mINTb`-Y|I|}͐ 'Yqӌ1u<QN=MM$t24[gT5!?GfOR8c+˥ "(;aC߶mx$Y]YYJ1}`;d^7ߡ mޓWĝ0,-EȖHnŶ57:+=>WlrV+YuR4Hf'@p>mY-ߏ;vM#2A;pP%dU ^(>:ȁK%3 Ǥ]q7jL"6˘H \$6;OP笺H2$Km;xw91ao8 aZ*n{㼀b8Pocy_20MF49>OO7_7cǁc]Px GQK:߱BÒ ~U-6M#! FN*EJms5 NJAC>=:G-jyx6E`ɖn7[CE-ittJ~.<;'J&WJy~+Yl07~g I(gqܙH;yYFўc+KyiIgiJYVFǐ+;9̖-~PHMژHpW# aT0#@6%$!kK8>t)RaqE݁~c|}~/xU_Duw_&'Ozj_Y{pu5{N FT|.scsvA.3͆~',{Zӣ"P2&~)sek8(GA+U}Qp9nnZck;Ą*252G~P q;Nv䣄4pLd'0EfƄJKSp( ϓE͙k9>N״ /Ɉ'6Lt%笒HK%Q=:tJ8[_l$[LHzغovlN=qt-gg($,a$5ۦj3C(b+Fr;]i1*~*殱. @#! x8Jc*l1dE!5*Zi"qrPl+Qݹ.:?|A&?E{Az$pMޠVuf=C䪰HAV=o J&qæ⥻;%uOI`p\H F:O) IJ粔.Jiuƅz%^!UpTd$P`Ԧb"gJ%XD\r>/.-l㐙\ZAs'156<Ι2Y\k< ,& \A}$4@j&\83 3I|vaPO܏CE&zl)T˙VEVE܃HB͐{r 3Aͭ3J$84fZqz&. 7X㕓2'R2n"sȱ“}2'Hz^Vh7qMbYfc5TçPx=Q~D\m[ T~| d5:~'bIÊK!:P ]=4bC6@(1-V@9gKcȤO&JWu2 _mV!U[Se w%2kJnI0cJj "(DTHZbd)V{~@/*j:x=>}7g_{_ _\mѮ>ܾ ; E1! wƄ Y@f70GWӜ&F-8H`.3׬zhZc`%;1}NRnhD<ǽsY媏i%х@b1NLª?\?tt{>&tZ9 >zMA8R/@qZ{0f([tGprwAT  s.tSH².,|vbTs oDbD-@?hu[~@;##^v|)*0C>$8$zw6B,՗ETQIG c ]䖲΄Wşn@3Kl5 ;AVb;"YC:*0\֑eqJ$ :зFUgy0 COL ܶw$Zъ5Y5xnD (O}y=\9crةUU,1&85-@@ȍ!U 9WA/E(x_sC'w%rsV̉6 yM-z05Uj+]>M)'{i8ge?xJyB[!0}yt3g^ǁstq/8+w\JZ*m@KDё$ȅImժݻ' 6'f^ȴmC n׷Ͼ~DǗYڀY "sp<rtg}8Sfd`^P03rb)$>l)[9ਠq{twwiLtkᇿ8QKq8:'z܎b0f&4svTі߷5uV~q=#͕#m+t}0DHXd !*j7>| [ _ţ=p$5߷2%P9!p?OǁwOsbwl;-O IDAT9 )TĽw.2q <(>q="N9#NTRq9mV@I 8>3yr sq>F9Sky\/1PI@79:=s yN-:X{ɭFhi(`QbjA ְ%V{CkRPV3Id=|yVp<~t}zBㄣ#fIx"~???_z<"El&(nuQBr9e۵:Yk;ڒ޷ ѐ$& |[:.Fw3}&Rc6O'?+{bm˩rqC/D8fww2pFu=*}XB&pSBG$2 cUOՕkaR+}:]}ҙ𗧄6?]hK >9F\M}Әi5 pIdx`A(HI\eV;H0V[++$,LaY) G"BA3'19n: 8]H jC+^r+b;X>crлyL |Hr&\ c25i0'p;iv3~7qtT옝k5lµ1ڌ أZQ4> OP0Ѩ$ۙHgUkz=H|j1Ҩ,͸jmt2Em~97W.IX0r?@q*N!2L%FAG}-z} ~?/iWؕ2?9ubjsrr "C,8Op&-HC*V7"Qr_9[?KΒLRT!o#V 823&"O2̐<9)U33/}?(syND"WJf\F 'B +;.1TM'ܷ~A8249IͷZ婨8!I/GT_+@\Sli^x]-Hs D|z\ŗ.t\z53+|cv!׶b%ZLJO ! zal]LʑQOĘ#epay^l!GǓ%J Xi?;-<%ȡn7uTc~SJ) @!/[, lÃ1%Ux m+hp{0n9Ox|ǻnGG#阰1ӳA"ꚶk_*L([Uvڥf-TPU$|!"c8lYT~by阊UzhF|\5Bdk+dzgU)^1>-:pǫmw'D]}D/Xׁ?9,&15Yӡo*`3'(2JY}aJ2&ӌo,vV a" pǦе:Gki=Jpvve Q-يq>= _ vEZL@- (Ҡk.Pvec>`/jhdxtAVLe8sȖRAX}S@O|7+ lDtel6tߥV[I eN @ \RYPco( VFu>!ᬨfITf{x)L? cniTcB EWDCvcP3Yd+HRAg'n}?GGH1P9,RWL8Օ%D%VþܚCíUۮ)co|t<;wϮ1"Xϸz'ChQQLcu"3R/)t\-+O D)2HZB߳hZ)(f˨ՊV`\RQ&L#GhD#].q~-Wɘ WIÏ=O'w}_|GG7 HpA nY@>/ !yHh`Fդ>N(#$:Dkqe\/H,tp{:''#3XqJ]sa` =9wyU}!c޿5=>/.‍$C| -do׏5=K'>1\]݆ :jQ*fLr`7g>|f56: Lǔ +0E XH+pJc^ggG]CTEθu]~@zTHk\fUzXc"L)-Նi^CT~Ic_4Lm?(%~K)Jyќ`hwް˜ ] ۺ7TJ:{-hmþUV9vkmt-=L-8> i/ԁŋ!DIݺ[!u?|IsmUFpUo~=.P(,.aO:)RT6~g15S}2z>' ?~? 'ց#+H<)hak-Mg.jS(ᴦ\+]OhNXTy_PUuVjݖݧz JbZ't <Ԣ]r\}h{ Q͆#*r%]356y*Tbd@ b3.CߘUabLlj> =3+¡ڬ tp\sU} ܤjY#kɕLJ@j'6 2m"|ղfE0 Bվ:j@$xV@iwf9F [>uw;c>c`D F3*#D"nL$+(BF1fwGCAM[Q99regBޝp99΋C-jk5 yF[i_z0,@VD\lUHd!$JZ1\rKvJ tO|6UlV9%m)طƶw%sOԓNaG<qW̤)*~KHDݝp,XJ\|^T4yĠLCBڥu8z55^koƟS $o_}ΑiscUE TW0en_K~Eҫ6һw^W6HS"SRLryȥEa7%-5ƧH;k&AXkJ c\f\4dWZxK(jB X :t*M3KB~,9 2tԽ|xxbiYϡK|_Q[~zKiL1M I @RJ *xׯP g^p7fxx~ctNYpJ]FU^" }&率y JV(N kܤ|8$}f܊c3E.gt6G^=PU|P\Y4wD0%4s?=ˬOa(R5&}4C/1nΣ'ruX P]%l'Eq{O@$ g22Apn1[]GqM)Y|v|oK__ǿ/*Zʋ4Hx`W.͵u] ^VBb ;6s :2?KiPLJ*yj1UԵ a$?"fZո̬='lJmw#K:c^ ā|˘S_|g  ].n.]P %'bR6*㽩~.p!9M8bQn&&KP+}#tH0 ƥ9G2&)`t}ERp5u1%[-f$%pr1j9rJ`|Li)}ȷd8ɩp IfܳmbXH443T\ofExRDH@ !վ $O{~T*]IyjBU&QFlKllimWlV$E=~NT~,I'V+T2;qQ)K* jp"`]"}eaȒ8({ ?g~ߏ뫖 '_+qr0Y+7.5+␁/%L@13XBԭ )@s6Fd'/wOc3IkbS.U$F}f\ L_N| ^i05(~IxКzHZnS,VP+2i!%4+ٝJF@B~AiԊbf\ jцt 3ly@?ouB/fVX5Ƿ}C"o}joxijx[TK?%I1Ue=SH ,V(iۆsSRhI4,x j(ֱ;n[mdo1b].cxny6Pv7]w!0vSMїgڇ1Y#QO~5:hU};Lzw30AYR L*e3+0۴E64ŵ,VJ;#64Jğ e4FH^ ==WEw@d>&'Ώ$ %:~?kZL9CE] Βq_H`'G ,_[?|«czmW|3_ :Hnhmyd/Fl!S{Udw g>IEȹbvDۇ zIxdPkMt@9fJz0Id 6RsdС'$CŽCc'gωӡւ262pݬp$='1Mdoڥ{OC-9HUh7bt nHDDbݑ0] R„U5D*Z[ِs惲y|HHҁ޽C䛎˙/hVU7czPHwn N.N=uWJ|zA6!%ciŐT3+]~|fbVH`e[I6lM$btI$_COhzVT wtjZ_?ܰ7}, |tcӟ 5Ҹ0GkIS:J"qMYֳj0M8Asۀ~|fƒl9O07Id D3$ 0`LڏaZuzưZ4 {ý8nm'JՕAJ%2bRۍ!H_Ͳg a,tW+9^ʌ=u8Wd>>ܲ6zWП/ÐA +ˌ!c'8/x I5>$BkG4p\r,ِb\kAR@=b6eh ,`H JāaQ {q(EZ`Aae"˖]~G i¤-;,f"Z-e+Nx۶Be & ޷5.o,c{ U_%S%pO8WT(\)e]sqlġ vhε6nZ/^JE b*I픊ܫ{h$xh7\ne8#H<Ѱ@HJ6g#9"J2Ө95[ jz 1K-6$!XU&gJ7-H R'N9/\Fk@$_fAy,i2fD,wq_3"ɨ l)^[m!9AQjbpĉD8ִ^pGӾ3 ҹc>A:I;bk$V⧼WCzM_ aq'6Mྲ}Kx$ی yrΈ#X=+VKV@x=> cg[#_1:Hr?DP[mZVˋbiЗMؚ+b^uwێڪu(l11kB¡9I>>>sx~qܵ.|`k 8&87o#_n٩@r)&+:]<5c/DZ8bY̩VB8XC4"z6OH"J߁4_ sxs o9*(u\ 3/W?A?WAom& IDAT>@ ..[f`wqp@_ȄWl}AW +*i`m܏P:xWO?w8F=0< cޙA3PŽ;p y9"!kd+R^ň뭵&zz?I0!`:HV?+ж~p,#ĘH/D"R@_ _}:[o=V141gޣ54:r?!`OJɚƅ-5F X{=括{kdGǁY@c"ҢÆ~C<PKo߾G_sb;VuFCkwqot{> yh]T24W\U$U>H`[j%+ a M(딍B(vbWc爘\@L0DeWhm=&,-%|^LBpÐ B &,P:\I#v(mWR"6%䐼$_~*|Ooz~J͜dh'\K) Jظ`n۶%/׏=U1<& `r[ %*%:I3H1aA/q'q9#:c;BTHd b#&AE{xzz HSf7}MY\tGZK@/mf ϡyr5 *2rfM5 ҽNvqUpyVFhuR&:}`f n7 53 @- L*$ ?ſ,-8탿~~0U7y&bΖH"Jm/*-J@R>FZGr}b^M9rj+Ջ'ms%1njLf6E n8a(ؔH<9 e_NjŨB׵zׅC T2&_42YL*T\ Rd;Mk%$׵J$Uuݠz{þ+1\/&m޻z+癟#4L:(2B < r$ZJ^+?=߄OS 艚q{FYk>ea X]5{%?!Ro-pyh'dmſ 0zh:]{K}D W¡ѹX\`fp/¥4_"BY ǁׂNH峊45!JІn3# ȪQu$R 6;rJ4Lc r#ʲ|(\|-Q(4' x3ų-@ޫz{66 \C$w}{uew@4RwD\zpSz~*Y&Qb㞶Rl7gպZY0C?O"ZA;w35R$خgIi]G\P X3Hml]J~?ӫ]S$?oe}(9g eO,}9C;''X[ݐ-gTǧ,0$>˅_!& 9ϝ2Mxox8 38>&.FOHw=${Rʋ~p]("1!qzGB$uA4[k03XrG: =d=@$\WSbD˂ .U\+~Y(pD Dr~x2(aׄđIJ>wQ Ng(iX#}:(aDuW-((a, _+zsPK zJj8Ls]>s$me?:_Γ<.T܀iiFC*_g 6slyę\8x#ꊅW'sm@b\W23,y XŠ:g4ga+; E\@ "sr*gn/| ^zB|q(De/MTZ|x52ߧ۔ UT*{Yr|3!!SUn3Q +AKAA|ktN,B{y Ep#Ck UE$%:LlJ/kGPeK*$B߻]9ao7z$ ,{NoɠmE"smFPgp̦oؚ&?zpXL!Px@j^͍5{9dO]eƧٶMc`z.}>EϮAQ5rLnLIm_|粤I}"Fy0qug՜DŽC>vwkYS8AZS0'ޱ| 6>'Ωu{Vϗ(X$(s:n+r!ZZ_k)rsBSl}.> k$"1#+Y)DJP h6 ZY${(IS8{yZ[@$f@xŬ -Lxdc٭5LPˋ|ЇS;:>p eR&cA24Sﯭb֏91n/뀍Da@3|DEsɟ iVa[Tr%N/ L5T}Cza+@?9Uw<{qx'Oϼ~܇<hNK<16C<>[J=V_ټy+#&`(8G砢Ζ!9rØ btv[GH{}R 4rhse24T~%g"f$A{}, su&x띮XjhejY{p׵Z'wfl+^j-3 'x֖z  gScs:|ʶ @rEܯ˛)y}/ {?.ޒk}ɂ\uNdz2n7ݮ5S"]rlzBiZ0qWi!a?ycFI_]@]Up 2wk5ms93; ΗӕPI!g\96lk<#09pU װ6'{u]Հ`x" nH訨gJ4 ^r9p*1:U&>&Z0鋡 ~G {qd5%njk>i~_[RJfׄu?:!ܼ".P9IuM䮉~Z!B.нSVT2!4LړpPct췛57 hZ@Eů@]֩/+B'r$ 3GN=uk8ΎB@s^{!_bͿ$\hADUB/BTUje[k,db2cD7~U\+sQJ#o11R}b3%{yńʍq >&{Gu8&Chݣ)S6L9@L?n A E=W>^Njx8~+^q߮{Yҡsأ? 㘉4?f)B&f\S"VٖؼEd`FٹUL-7i*s}6mCs2#~UPV"2'Aj"axU 88װ9{P+bH)p :^s3 egD'D%Muyw'B[np\9ո,&ч A[\rGQiZUfCyOϚrdjDw ňL!- pRB)fbu N )h\Y&1k7hXl% Puh+>Snj:,`X\ %1qVSlZhS&`ǀ=0fn +3N03qr0'uccߧIA|ҧTkeIS@6;5fhbfˤR[/E ?~VelY[Jfڗlj*!Z"l$ Ʉ5clPz՜죇gF۾⚄e֡A]Zt};dC'-a,ƗYC%QhIfN7'\.m^UH|K1Yn:UlAU6=AwGׂC } eZgWlۛPoX@k},;<]a)kqe@3t}X"+G"({U[rRpwwg{"zi]pz |woPDi=ˆ(2Lɚk2шQ[dpV-V(.vD3ׅbm'CjPJu1;}}PO‹x>wfi5ҿ^5pp,P/ T|c 2|DE8}[|>ڴ؊eDFxu d1ه='Ř*s5p+EΖUR3V͉<ֹmdE%}w&_tB[W\.t}G7L$Stv߱,k W65C"X/< XƼRlϓf IDATg,Ս4>\ hK&]gB%z2US!n~\榘@IDMmHJ,nWx{{v@`SXiC!j]1]벺ݰXt1j'_Z1fSsF fⲮtv7weE[ Cζ|: +*x8s>Q<!*i<'A؞ʖU* "=}so׾E|̯֯گHfrk"{jٶ4@=S# 7g+V%gLHkuU6:"m*lޗo13"kzU/m˃)ZQZѲ0]􁳏n!sFJ8G2p>|i  YP;DxA]gtZ͊{ꕯD)?o3"5z=Xy +tXzi\Gk!30tʟK.tnц>pn%DPuG-Jh:̉)e?.r[&FVuz70F)dnF&4׎8N{92kΓ2E,($f<0Q+iY @|1.+ I&=vO,o{j=hbNvYjE mi(jֆ(//9>"=&by2ou J}3= /gc`MZ2~}T:1\앏Oͯċx"ook)Z'ƇX\"-΢$qyNٝ?щK ,/p!Xz0pjJK-F"+U^[x`pZ] Z9fyʒZRY֤sM)%)I}ԄS0.vg`z[6&⃀BN{Ly`3똙uXTt ^/Ja9 WzMJ^5Z.Hnoo=q;Ηe-JGRKq)r@}}9fS 1BaaKwz}߁M1w2D"/g~q.X6/ b@ZbQwQڣs;hq#ZNzw{j=Nx@?ce,o ;Z1rutϥ:C܍_a7F?8m+&o;T.3eEkk+!qϟgGN'IqjЀƛw)8Ff֌*b΃AgR"nX3;:uevfE!jz/\La]ePm8`t*Y6_NW}O>|*ץhkMXerql˯s͘Ȇ&2HT)-: 2hAHsj1+E|*@t hڊ ϖ_CJZêhG)LvZpm^MN,H rIlqz? ai}q(QT#ocӞ&µAD&/+>vݾM_aϿ凞MEu[3R\IO拝{ YGYcYF@.h 1 B~_CJL*Cy'j:"H]*B[|^,D2lWز%MMcih8a9j[VÍ]`[CYȉcnW u6 wzzX\uj6ع{_<,9B"b#ZY̧0nB+.$YA c-f}XV;`>|K9CXaJsx>? 3fBMcThx\7 4Hĭ^Jü67 RLJC2*lXp$k_!;$Y5FNXg%@OUTrR-6x]!Y0#^K+k'K^@Gcy-V4!zЦF]8Q]Lu)}t+ dPc.jm&>PYj> e,)܅>85C^V5ii>"\5Yux!ڕK%88k m͵<8 fS0{c:ރXTr8svfpxQB2PBVرRNWgY"{)b"kg?a BH֛m2uL囁.qtt5ixEWB}fA7Z %cu"0LH Y>_2$w{;fspy[[x^C$ a{e.^5 CBɵB/43H,31:ScM A%׵edwꖪPo{t{2:*xº4TweAu"[HXGN /e -{Ƕsaqc lwtt(lVw 6sxՃ xPL ៭n_9Z>BlODIu?o H ޝVk|'mV >]ɺ6WYf`xY*7@ph7"sfcQ]s0f+l\ $P©fɀ- w?M~+ Q֟?sWjqf9WMw1N%2w.ZU:W}Ad؄ _}A;6ku3Gm+pm,9m̂֜FӀLqPoh!wR@:乳Kssa{" v!)^JE=DOYQ5t52}߭3@U,ڧggҡa7cj(pXI`= .@:WAB ^*_ t8X-,,*stu4lԶ4RqCc]jՊM|;u#ir؍up 3اѷR%Z DJf8.je@0<`7G ~ncvt-Z&E8tk>m_3܏;OL!wu_E&CU )Zۥ;fLh(CwDZH  Cqu gI^p=]Ǟu!-4왮E eY (dWWڇ4kLu l2EJsY1Y+mq pTe.s_s-")DuKx?u*1g6y0 [Ԧqww`pD1jo4TҮ,C0L.WE; D=? h/AfGiLUR :` (b<sMYu5ǻ` 3:rp۔nk 1 {.zn#,8Z;;. Mۤ{zSR+^[K)SC6XlI 'A'tc{qVfR{:acd'tO1r]li$nv񖉕}`֔'+_X@8֑Z >e8R}i7ԠQ'R1pd{\Y7e)"t2%1]T#g^0nCXr{>!j>;UkUkHuits=(ӣtwnFzRَ>O0ɟԻ!ϤaYn$|:õ#(Rk]:!b㊥pɎVӊEwsP#iPhྐྵw`Sl}]m-޻KJLβ+iu@'*H{05&\_J+t{,\SS(m RG.|!>;ӖZ337#ZM״fs*DYֵ'#pd/݋@;T@AȒ_usYؾRgZ 'y9b*"mqTOC6D< w> _Ο{ ϗ3泹ϬVhwQ ̄aXWsGmV,K6TBu8sPm# UqV<7YpX{fm<}u0:zQ2-;ҊC:m!KZŚ*%):p#d) 5cn6lojd: ՘Ppl{эKIƂ$S :?b K0~^T%%Gj[.Rwe^9 0 ݘ16aqjTBtes=$}.J%RI!mKli5KShb$;nXkE+bA<\݆9@ew n iػ#:S}]1B{W<ŏx'~'>of\G @ݍPT}3{XD~7`D\@LΦEŝ{ے [JHYlk5W]lEUmľe{뺺ipx]51z8> oi%2kRWL^B#j)"Œ}fc =k G4Ψ>bmx}wt nQm+ @g reO=zly{K4$LoT\| "V{Qu5eRfqgµ 0$k{+sF%-֙x'R)('TҬyu:֥=`fžm$YYCEӣXjR*Qb gRY<:5?2FjM=촠Vh;1|I:tPI%%E@H-c/w[]r]`w;Fi lZm'Bk"ZINVU]\[!IGX:x:}X$Å醵|k;~GԓO2~wQSQ eoGPYU lʾriMxV!Ϭ4U[aX.2Z"y8q3rWϜ mH\ ̛FD|#@&sHL̵{L-19h[&̒ AIޭ0phl={QQ[MP,zR4fIˆXq/(H ":F%C yB ݧ q0)e=F}~'0CϼaPɞNɆW%%)M -Њ,C'e~3ף4꾻_;tܧ,ut|!E=8Avu; uQ!I{ XךZ=朇bmIkHє3XԣkW]zŽ'?ߟo|7vJ4cYW#2y6WYOaCUm }(s6 Ǡ>,Cceϩ\س էyݪfmlpPU -ʶsڠ4c 6\nnnØigѱ]6}vf`۠x{15Z)X׉HH fz ڜnco}`Y 1 qX~8itðs`VSs@j^l~"i0|àHVڊm1EIE[*ư@,N-|楔rRbmWhLaSβ-k7+3sH{uNettq)ssPqw@)^+znܪ\4Dd2, gq":uR[ řXIΥ7v?X:SvwN\]E ,IuF N =f/M]Z} uHf]Jdq آc߱m3!~ dv݃S%.53",0­qdA"\lO~7?/% |OᔱTkyi}yۼו99tꐱ$rLU1|ܨa̔Xv۶̎c8/à aZԳa$ }gltjZkGA>l@%Z oس 5x!9VG)nB|[SqTI7'K;ZKZ85?&jݳ+놉63JmVc k_ak%vC-1D(þoу?>"~(:*Of| 9x\Wiwd"hGSK9 N+!$fAcX+ߓ!(i1 ˥w@TWJ)X|$[rbK"w4cg&K~a`;#yL̩-$kYlNDM^mLyx w}@OR pѿӾ~]\<< e_zZk1eѝ2#LzsbB. ZPVaL8:iܤ9h. 2Ig~-= Kjiu dO ?Ռګ4יbߗ .Oגb26 2ȩ} ⴬k"Xb9R-,9xy:\6Hl R~-Ã.#6#]g&4⹊1j;$=fϺ&Nս;򜪂R9Ȍa;r5-ɎI0=,AD@s67DED۞PkN "T@gA@ xa)[~K1U@I6PjKdZZӍ!24;]t*2venk:u-8E&KGs`v ѯeἃ{#,}~]xnm~~Gކ S?c򦵽˷nŰ6FED&]p:pG+WKDoAkM FL1`; |S~lnl~ x1N׽DM\@d2tKs{Rֽj~d1)Db>m\^$a:c""3|ml`Y R` zmLyC*.:dkꧭ@w~/g,ˊu^b24 ۶c]u>u=>m!:=Ő-eea,:3I9DYl΅r,+E8<բU;PHZbϰD0\37k#< '˦7C}#Z]ri3NJ$'09<:b`Ā b `|# ȋHs$Ff]W,ڰެ#h!#Y;n }So+* ϺY*f0#s]%\.̉?~Ri Pds0<8`IG@`TmJD-R%i[O%}7ygR  3w}_Ysܣե|>[$>c֋;QI102 y S ʣ yC &\TҘ㫬,cđ/ !*pk>vr6YAIxњd Gg1HPI2Y%_>:',ϥ-tZWYQJ@P7`6 X'C I؜ޖ%8>>β޼\ ۶ =TZH8<2Μϙ; S !2:7z-s F|ľڇJ}6 bp[ iyۺ;{öoQFaS& yؔ2DT]8e:JJƧX|QN̶]>MΘ%ޛVs1:Z nnOXP\j-hJ6&޳VR0Q:,}m 8@'qp3~MFOx#-he&u`$[P0)x7~ 8^|/;>䋊:k;zvPI4rcC;1,Sr=V@goHM7I0\Fz ibj̲ *};_^.0 DP0]1Hxt2"V-࡚c'|21 + 33-u]yRI:r.e5ԲHf嶘N+sTk www1$?7~R믽w,e|T` ,Ku>ɥT9/z]<3'd(o9u #nv>[F gjdVސǵȏJ=5|[s92ZVg.buL70y+mV̟ȂKLick~G S^C@`,m%ݶ]_5gZR\=[GU.S.M$A<`e^ni%(v&wAT"*P*>|gAWڳ7"S-(:u1 |̌ 8bJrװwɉ܈E1CM]M&iknH'0Mm*=F'Ҙbm3_Lt/޶IgM01nSZm* rsuvnLCBlX`N6(5U rb?3nϐ"sJ0BQX!-% Tp"?6>gcZ98u?>_neG6\{VNa@{- Ș7ĸd۶t:!r 'wX%->)QuN|!Nh 8\{%`tce7wjm$2 ETQY6bQݢ"f%/|d0MG糘h 6ecm5^.Jx@aVA#Bm/Iu` js[>&/v[szl;_CDPT[3-|⏗t#>o"n$@'V;ps/F*[ 1PVTW+F,CT}H5NqҲVZzs4= FEo 1j3vo|p*}loL|gʬ3@j{~ޒ$f2IO/ͬD ^ C-9W9bzhM LMlܹ% bx꘭ݣ(|I?BVL}sZRr - ,́n˲D器_38U5޻sCk0B洠ﻕ\Ћk+HJXfTZ_;oyH_BK>@[GDC>jܳpźdr=# M ݜ5I$sY-k:$L:=Ϗ@AN$703}tsx'ǶPGG߷ ݥ[̒ZL4I@4$ÜPmNH#} aQψJ:-뺸.F.O,i%`0u3T$}ނ2& ֔xN~gD8+O#\{ygq=ju;Dqy2Ĩ=3㛍;}8AuμuXogS9b[LjI^;9g)*:#ZG \qq8>`A0[ XSADd\ A>F[x-1PTB+}MN,"s4"RObȣ1жYz{edb*D8ɟbD&a9OM ՆtHk!Lv_8PnZtF{s>W~/^&@VQX?'zIcPUNF٢y6J*ed̚q ZTj9el:ϫ-ie"=;!JE7S4o0 M=`sRښRp.A 0MUtuo;_XZ=Nxua Gt "ͯA"kL (2KHINS n5؉8V\-^6!|+Hfe0žg@@ڏb@ר4#Zǩwvj+rt<:Es:I2g1rRef9k#Ȥə2;Dl}t֍D,(8NdU &Z㎽s*#v"sOeI#z5wN #*吳ܓw؏r9*|fPjqr [OT8Ǯҁ8Դ!ww[%<Ǽf}wO0LUOWg}/6tύ(՜\u69ʀ\C4C!KWdD۲xݯ pi^AgmJpV^Y>L4Кq`n TjY2iP#ў1NNb6rJ'Z(km}?t͹NrR&"$yZ<[vx` zJ6ARQʺ@dΙ8&1(I:'x1)s`s4:$fgWUQ9ʑ69KD]_2\ 2y݄t,u-Irf=VUJeP j=q=9 [k"2]%JS2'w}\|#p`^b}tZ.a AkY`i sIȡ0A)FFnCư&mτc-3TT 6'nRfA^$,yQs_~'wer?¿%?xTY#j٠Ґ>b5CܘXw,y,d4V5 :eژϥqyf={E&ޜmmK*ո@-KGϬ+b[h$^6Z=P*AII6UE-9h.Fxf5\g2jd3sY5SRN-&ܽ/sA@6ϙ? Y5Myp_2#@r*>TNYЯ$PV)(2aw;emۗNQg?T`YF&bQJ Ԁ+3{gZsy*j|!/W:WLJcԖ41W=o}'xG䛾Gw='fҦ.s)wSc~kVG8T] !n{]D4Ap*ϳO2 ;ْVqb-4n#CH)9 {Wqk58t1 C9Q2yrDŽXǿcP2#W#9A% ӈ^fGz}ey/i_ԥav j *"6r knՇG |ϧ1h?љ|g ,d-/#:y7"$Ū)_z K3ܳCPtL~՛2V) Ö-`h Ŭg!iO,g%I@KBVkۥ"p֎iCvv Y tD&YjgiKxT9LB,L)!!L{tkW|6ȟ!ndIrZs:l)ζD,Io_/ogoD MdlE$ Ҁs۬HFJ5Z$ּ c;46?/Y(c5f "p`cy{sK+h`JVd)aQUhy4Ivݣ[BČO>>V˲NcARݳBDBc蓯ph{ʀ u5`"7d:3VÃ* <9y]`x2Bp>у3 2"2%t&bRy-DRJpe?n92>M5s=c A9J`P ߶L>g+=@ȝ:p=h$.u]Ct`v1e4DU.B˾E V5J la\ϙ5k |_ ೿)Yi\Jur9ȐtC:9rk¨g~[-*Hcݝz84+;[^$z)-0g$_MAPuOM&8}Ffu([:P#r`VֈaqXBv0h91:p/q|@ #2X]sBw3̎;H^3Ɵ9wdt:( GLgvrvϬd{HgcVYһ IDAT߯CF"DL0 =*: YJEY:5"dc% Q6zDՖ {Aev; s^wX#k6hɉr :ck*n4=]%D憏*t29ڷ8  HM>XgQy("ncV1a\G$eS?}:1ȳ@#Ct}1Ir ȡ{ !y,FUALl<2|Y&-*,eYɬD]UlCޑ2\aM{)sL#}{lF,A4oqs+0(.aW9#&iI{x|]R}]l*FեHy&AÜEQ*ֻǰΑsKkn<:H]|"C6LQa6;|2T% 31M){Clf n0 mG"wD])O%o2eG+&].Ɠ3D{jAXJDÓ}Ce8*$^UQ0QMw}MK?Pg&hQu^q89Hb6af/9 /wlAvG gϱ=NnbgJ0 *^P;%CI9֨}H8$Nз}/:6Fڀ%q.10dճ :3OWX]={΄P+{}rZ/3u3!{#-]8POaZGa 1HR0?U2E͔٬^"_d"K\y ^K` @ H5BheJ\$r 繲*qL!">j@Z|rV+=TeYWe@LR}8zsVƢGVM5眸M.uu\ڊ} www]O(oIu7 %fԌAǽ}˦7; W|k_u]/yٰLR jltH05:Idz9e;4Pޝ>[&VL]j6lk kWU`s`Xί,;Pf@^;x_}lfDήg]}J&330LAcdBi>άOGFQpNԆb,As>ڣsfZ6|-6òaʼfӺ:*iϧ֊vMlAG'9w( 0l s,?5xSپK瞻g*.6L,3³S\u Dv(>%s@4p'>zt0C1nm%K=3XyRНh-b^og2? !_3w[\yW11 *T`=mBƒΙrtF' C$RTu^qlHrJ+3;F$Mp0gLsgm rxa*>f0I4泋bV{PL+z(~082J/ wq{ܯ|6_&'$/qȨ?J'gՙ朧4HTo.N&zFc |Ҧxs݉5bޏZ}9_Ҧ*bZtE4y̵2sO?{sJY+M. x>J0P5QF:v^ h6:8v[{|#wR'r{y.c`p>_uFg%"(Gg$ThX GGc0vYt闂ѧM:}r>B\1 S%?z/}ݏм xG>/aTo0۲ļ`‹"fם9 u0xfS7ڹG0z8&tv>#2rՀ|Hԋ{S>k 3.D#1=~1Ckѫj8l0(1kzȞurw1U3} TB'/e@C-fH.3/1_5mߡ+s~u$G='܃詳63o'œ_S8 ˉLa/ܳhc+jdkH3.u)3)`|l lHEv]|;&ÐwS‰Kپ+UcEkə6`h*Y@ (T̞/}7ԓ\R;| \Y.꽛bD*www|!܌PdL$r]'u,&~k~og3?W_yvN#svـ+4="aHFsea݃!+r \u"롔h5&=f4X%SYFCYqAS)vwth?7zC騘 lswn) 9k2cP\|&Ak12Ye" U8QH Ƀg!"0sI4_x"XJE|-AWF/}w\_5.\yv{ĒKO$- }22CA>b#sk{Vq.IO{f(C>G=Ci|0pRWDP;YUzL]1kA"B^TnUVҦAdcj{Sg߇q/@xɛh{w?D}&SlRLq{fgFOD,3yf >]3:k9ӽ\ 䚨Cj0::I5nji3ݽ%UѶ~"5)NV)nkAEMxP,e!Z >$YEd΀9\s,륣1T8*g$oN;CPP'!Gk-: ߗ2X`Q:s{=Dj%PeʇOb:F&9۟{1ĥvD~,Rc GYJT{7R!ZJޥBi3k-ӄYPDB}-gjά{ 3>EB{ >0@hn}apq6ܤ[.1'T}/R$_Zd{UV_w_vBH|W{Ɓ`]W>#k"&," %[Ƒ|FB$ik? $X- ǾwLz Ř3K8&-<:YոW!!2/p' T!wRd,byg1S84&te>]R ;r BT!sEbIB/РGpLFx.$U Z`Ԁw_Qצk*fВbTuﻍ:OE<ټ@6YnZ 2ofޓ)u^Ewp˖zZoY颊ѣGx1Y[]dXZ/)v eV>"GϺ,I~]|瞣T10[K !KvJ,(씑1z&450Z]4η:v<^q}w~ w<8 eo˿K\ΖR&4yÄ#s&dL:: `j6l fqPr`q~Iv,KD]ҧyޯX)>NQdM`DJd08/\PCtf :~?@#dɄr0Xp =R>ưiT6twNtGe+J_)vZ3 F/X0Bk;Pg Yby(kTɎLAQg JJм~j3`$2C:'qxi{ I!(QH@PX~x4+%:c?ӍR&(zzZjC[gEµ pܴyo'N*&݆A i2pnFOe{\Vҭ~nZA75}}[E X4jT^2uDȠѨ QK$JDc@5* Yi3j  P BEMF@(;kU{{콟g{_|EA}Ƴo^Y+GsX˾܄J`Y0Yglʀ`\>2(149{}0q'HY* N PH=IoU?w\z8==*+܈ ­13yFlm z*\Y!"8w\<Ўާf\@8׌nP^6 XhCg7SisvFt5yLN<s'r)Co@k)DA?9 Qv|RTPN[0wg$4im#~$J8H!{OW+ ]RTOK-˪PϺzEKqL#JT) Sz,'Adkn/SžKȅB3ZwO?d{ +},5^=28 5kw?>"%qtދЏ_bx=6(-J)e2sq(`.t0Kib !A^CBF̑ BC9kA=NR(H5Ӧ"Ș3?+Ή<l.iB!e srn{`8L)m&ٜ̈́# xZs*G4{=5T=!RT\`<w7I:9) 8prnvb6z I3zL"vakwESFge S"W+->Zw=2FϝWRy⍗;fh2+c}SVŹZR]rDU4["_Cի\%88:x7~vy0ʲ ) yo͚іū)ؽ؜M7`-h(Ni[)#b8)`~!/xn~w9o69]:%NU>wN9 ( IFV"Cop>`ъ7GsQם**SMt8v1jb)*@4RDh L q'd/n=uc:;)EP;Z_AYg[GW/8~qtއy/yk~6y6(w\ a@c-!ʃ8͎͎8űIⰥE"#\X b mMj%o[֌#p&/ykV)OZVje,Uc54|=9'88i#]<,!k[pADdC7`V0E,v4=Iݹj?g!<8"&vf^>upymeRlk^[^Գ%"aX{.ђ`J^5x:zFl,_Svfs #L_@ p Tׁo眤,7As r1ժ:GtΤk)Ft9%v_Ww|!G}W wPoJSl  ؍:8Q1P%n E4K*vhu'Thi ',Dj)G^y#KnzR?,8RC)hPyIeqa*5?'%}@ :?!5 C"'TIGWť熎ttaK`)mgJFu~@l +0")#Ȕ,WO:A~fr<pr|q8dmueo0!T?&ġy)$Q˼^jݥw<$E\LC{8X5 bQ(ONv LZy(k`I +S{{)8/;:8:ozOvyN/2pyK Y\ -IktB($bΆA^oQ4g23ĽkܖOQ>n"5$0'ȕQJr %Q VFC#`q<;4OMpHL9oILro< |x-;#?7=y ";utQ zI}$^) Pu.NU)tNiFeQ sFqVU#,~EΔh>Z3[FODZ!3t||NNOv=l#h>K7t;DYJ5Pk,dKBm&m/]GLYXgzJRE;NZ3b54Tggm/c[dI-qx=S֥>կ/?qtE/yՏt%_|h9(=lڱK$xRKݡoteA`X SD2޵TlσSԲ #r9<X:`mkpԥb=ޣ],UJ)8vuA-C]qZ5+HEΘ *~!3]SDEuhHQFxo= YKD8-R dI}?tN'DQ(522@4xʩVkQ,)DV&87J3QJ B)@qQVJN;tfIrAˆDlx. =~Cn4x5[TRgt k%E, P2諕s .ZΜ?F!@h5@hٙwZGc:x3rǵqLaNiE TOQ2؀/]|##!G0?ʛG~{8o;i(kр&-65!@lgb XKZщ1^@o@ yWu#+bA^H 0W9vX+^\#Տ72SUEQ(YCnR0ܭiӣh͍|N"P…;xDt_JE5ds7I]T}n͋FjVÈu]cTiQ쩾2!RGrjC-gA Ҡ(v y*=DZ!aOD{\j DNջ3Fn{J(fCk׶Z(ez\5w>\{OٱۓvA-Kk\a S܍\Cy{N?)fpt|U|{ $75 IDAT]";dvx^sRs?x {nkw08G) TLU-]=%s/Mb徹1M /\D-QQs:d{7cn][D5'Iz̾n?a.菡:jyoYXnGAK GB( B+rF)GNO)%]S$mM8a ͋; \r+nDvz5nK=lgJSTs7"Qzhܞm4؝^jtZ;Θ,.z|]8 #͓+]ZkQ.p h* Dڜ7=@ srd1Ev=Cyva}tn:J'to^W :c]t=&zY |'+?yǮ0 8GN \=Dh;Y&6fͽJ)\ॎ]1@`uĭ~z{7 =mTl*^^W@!\x:ν(7&0sj_@HZ+T \dXyo#xl͝]tj]8V{#P5O9ٜZd_] nN;U]kJΉ=D}Ap4Dl!U*7\wលկ8lWO}'_oK>׹"rEb~cÄY)<gvf/6Gsm_X>mma%\ g5ޗ [ 3RR&z6yըv;AH(qԧ9N,9X; 6HvO! ,@4J%/euu' BѴmErr4 ӭH7 ' s##ۢHLq R11cw#1e\cDZԮ"u뒝,liCFaYcI:]g82eCHpl=AH:4A. J'@;?&;[Ys{?&6c|'>sEZ®b},bFkEIs;o!C{ސDL1wzk xmt/8]F'"a""ZF[%`FeQLGҎ(TV1d`z$ITP.R(XFҜw~%"ER&fGt9~CΑPkם~[CoU*nմ+zR6QGNv@D A3VJFMc :V3"z ip,dvp„q}Cu#z͏io:k'_.o7=88:o||PmZrTVMPQːH`=e!<80&C[q|͌yo\3[ܻ*PCJԃtMxLG@ʐh>ԫ?lLTN>ū~>~pEkܐPF% ]wt\jRi*M(Cɕc]XuV Lrp2DX)kOjdZ]WԯhCV:dݶ3;HA OkWsjG)dq{^s:$ConGXj1 ӆ.NB)^c]Ӝ&j3Y Qa)mɉ͔##i4T"g4֊/W} }+u>ul n.G&GtT=8IR?cet&l $%.ȑdmsY,rx"N ٌd q"zi-zw.C9@r۱x^C:8_gN?ijLhY5fc-`/bUCPv=a2Hܜ.ː E6mp$@X; g6;v… 09:]MhZh 677:E(C6'u8҆SꭔGi_cg" xxɑ1W`0ust d&Lk@1;W(I6Q/[*>CttG_Oq|PAxg_巽^)8qfK{?'KD flq-|K^e;G).XFC@Pի/'\eTٲDngɸZqw2mDꭌ;`N T ?696yYւȘWtȘ_v2_ڷľ`)pc|YYB{mp#'U|G:+Yr2,9z͎Z8>WJԋٞa/#ڨ C,{v%Tct*ʶnzeTs p6w8:\lṳvok$쁂f֘=G]~^;8:>{\xʐ#MhfV}6@w,{P072a2>,gI^2 [[,Ok{ć12zA^k#D f7?⼛c4ʊÈkW%ƛv`:pS8Q_oB].)K)k\-y}ŋ8:O[/cDO-%| `_ii"'1~ Q17n5اcsTQ4˲`=x23JjCAq]׈xI"o!)=ц!sy`xwA87 ! "^j>7le (=Gy]2ݲSMesyΥ"Z_ސzs fTB sj8Dݖ%x6[![|/<X 4!jmEVdTCG;s1L~>$"g5#0|Z gDJc" 5h쥪hC[]C-Fn$xyQZQ\z?ٗ}78qt>Os ~?'IL D崼tDe7r@(ۋL}:_2335"v;1%)ZGh eRh=#t=Lb6V]-"wbWfE#ow"Nps$m`B6|6s]W';5dufA+;|Yx#6u8uRȆU@p@yld,$݃'%JAwm|4ITUѺ5";!uʗyN9bNT+XHEtp8}M/h:xy/䵙a6dg'~?؉5kʎ%ƒP]WK/:D:\AjbkkɩiD}|̵ş?5OͿq'AGC8 ?~-|ʀaQ!kZj6:cZ,>srš~g;iXY7D֥FQ((RO2j:wIcv,w t[W2r];DZ50Nd =ptFy>͊BvUKyQ1-KdF/eYИ+OADSV yNʁȀj#%vٻwe [ǥHLQM{ XE1 z|G{5͟W2K(.s(RU,cFsvΠb^ڽ30J5U]ikEO7a1Ws$>2J>c^o։_)hk4kk]nڑ>_!޷szjl*amgCߧP3Y DXK_4`/r2X efB(#Eh1LeQ'#<'{\msӁWD5zCd9C#7`p`oedf8sD3뀰֋Xmq,3:EOh( 'Ai[hһ)\!f[Lk-Eyamu!s QPANB9ӎDC?4RSx"CAd*ݼg(." "2¸b}͡H́>mehlϝ|?I):#  9Ѹ?cǃ0C`|矻zmzGè{d,aHĹ-FK#0TszZL v֟~@6K1ce fk&m`orwQNΝ %b.k]? \/G41ߪ(į#"CT6B%|AؼQ ZREIl"8ɮm<s+Quzk^PasBIFnG-Ucr m)DDRͫ3Z{ dYp&wT|בai8uZ[Nk""ǹ3N5;99 $S{n/W??P=}_{Wn;Y+nnnN_PhdÃ>˃-]7 rx #rD,hh,LhU-ps7)Sn744sLzw ~fI eR2EA[x޸wQ9\>T|\ڕ ǵp^P-~2>Qs.0cmS$A=0q;px0tj:iihf޴]38qe|6H5wcJx^~v#R. ;xvk&;AdLgJIzH=rͪ$s.1>qm ,H5K<7[s?]88:9/+7'և#2 /:`ylYb-t ls̐+Rm@3Y`(þ|c+j*uT戚K1Ne}m!d#>;V#6oAD9Q|nn$2ؕF ^⩖MSDpr\8ۨ]DP*Ti,R@ bP8 S!7GD (ּ*um(Rۙ!VJ?&ʢ.qRruju=)sF#&ϙ@"T%ԝc DZl5g''xa=qNsuOOk(}8D,ȕ鑆$ZYq Ach/88:e~ŋꈞL$F)Cf{l̹.h~a$:r䑅$bn~oi^=*DJ`tlH-AE oq讖CҲ$XH.UQ'[ڎG9=z8X78qy[p8,#p0u,ޠ5`@ S $.w#ύb*db苸3#'R%0wjb2!YKt NcD]29@zɨسUha)ots{*'p Z}zbQJ m#'ׄ 0 CX|5 t09 scA2YBUbb4ĺ;z<QQ@u[Fp|+E4Gݐꞌj絭ϓ1omgIǻ R*ֵ >G$-7'\^/$ Cjx|ĕ/[=m#B>sG`eA_G Zjzzip{3נF' r,"CX}~]y(01與5ʱ -iY8U20b}D6"PqEAL6a4b)$bN v냟Y0*T ]fYo" bYJ8Jq9sK<6&FIrX46_0ȹvu qTDqS!#E[T3aN ז,<{kꜗvQȩ;IV*.9.Qk[=[~Ǿ3q H Z@wΈCD:j 3h6v>GNHH0<tQ mnb{wLǿY+,܀0r92oM(u&Y=1h ߀pr2jt[:D8 IF,3rD0 3Lڬ3 Ǯ.D`;.ƴN/mVVUq'P\;㺮Q`5AĂ1;#k4(FDu2$Qk@-&FTk[C*~nlԫwamQ+!}Oo2H`OP]2 &\|~E6QrzK, *j( "OIT[o%_n(JA~nVN(.y%ɕzBX[H~[GǠRtT䊉l×ȶD%nAK)q5R?ϟJg|FF[яq~}O涅i(hs]hY F*Ep~^l6[ʍh 9gVϷjGRF4U]`'vQ5Jȏy1bZwYoaL aZM^ E%QI$qISG%^[̡k7pUɧ\<ϝp4vְЉMx4TvW$xkmg~"dHٰ"R#66:ݦx9 wBN] 0faBK mrڃnv"|A= 簈iW'^m[x/a7|E%z1m@nCpd}J.ku(b !5%b Vf9$I5,P+%ve#zNa8k񍸵,KpL ;rDEF|n6[g)vȑnJr5X0 +ikIR OR DY_ӎe3c.{׷JkO掑_âoAN44V;B|ԝƚ"buiw贷ts(:5DMa|9#!L2GڈKsM/T[.>qpq<}w[_'?3ʫ=BFpCDdpz"aSWd^ [Rpn򷥺K5JPkXwlVnP{8oN'\[m5*f~Ls^yЯрR3i)dI ssKݲh"B p=o~QN>S+ޱݲ!M/"b 5BKnd%WNQzf_,gLa@͛Bųp?kK< sK$|ɲĕ3>Z';C0'`U;2wWɩ+M湦_0ߋFNhHm>tuCH4ZMܞ@QXOs.HACCVYD]#raMGdEm[U,JxWW@pzzjћ(0rbjD WcB7:;#nz(#Ihȶ°"|WڈH-Qs탠5s)(p5Yϵ|Dj0%0;y9eMa 0 r>mk֬}syiQ6 "Vrrz m[3r?lT0҇:7YJy̠9qH] Z!{C&Av35J|s5ݓӤRrzz:۲L^)7\sU;kWyw50GypÕ_6 ߁hpAFtybeQ{;6T=x'''#ݲZwg6R&^9T*˲.KLl$8">ƙCѲU<\+,LgeKfWQ!0_^DHƔRp|UoSxq|X0_{m_hpq^ VǮ.A:QT9 t H#"UgѕOg2ѡ!,^߹,TN#(cU'RG@r ώiA\+Og!S̒9n}$%q,laN )4;7:ʑv0*~ͪk&X]@_;9k~~!m\^FsR jX|1=6ޏ%4i!`/N#M5aYNwW0'Gwp9mMA5~$ބD=> w͏}8>A|;wy_i ZGR kmiﳵ5 ȽGףs2y,ȲC["h7.»O? Hn5I BܬoB4ꦵP]>X{InUstIkcJ]u2es! x ` F#5ÈdQ;zѲ;U \=W[WԊ5!`)ϕQ"CpBw`piJiMnY% cNLÉ$>{$ZkV]QXhC1AHn[kUUuHNp6?+a "c/3p |s +/ׯ{{!GVR[f(qn5$EsHU菆=%4 ͮiG  YR>XY5) %D5 j)Z45 x_)ǎxo~/T+Yriނ4GĀs `j񜬜IsX~P+}M0MͨsV"#݌`2ĻlAQqB*=;QIYB9(hm>5RP.x|Q1c >KF > M8)y,ӄ0ҺP %P OYC/3xoq|Č:>yeO߸KG52 3ʋۺu:OHqtLGY+&IՈ-.k9 ů?r,  e0[oPqz:&0֪͎f,Q ;1@"Z %8 ͼ2`x 5"\}Cki5'iU<5LcuÖ/;!)2ɑ ѣ"Xٙ9. 9H ~ބΆ;!:A`T5nu+KycmDpRFT'B*B"f- 5;_srG#{}wW?w[QF'=qԀ9,xMxf5EWϷHx /uY:H7ÅM{DlP|+t18 qPA$ATQg6?'&7#8QmQ72x(۔ fRI-"еp(ZzާgL8k:81n3c9tVl^sL3t`%[DA+BRcќKTҨ8qFWvΖֶE8/lUSؔ[-;$uG# v`fZ M!H0(3"m'aT#?#3$7\e3?y^rϱ) II{A0::FC|"Fk'@p6q;WtuQO̶eS4{m &6dC۹Ż1k_j}xc._xMoq|Dp1_x^u^ۯfY3"0 ?C'aLM1sMޤhGHDN9 `*n)}"fsP0a|0Nؕ}V~)mHcԍV$c5>dGYR9wSLj5!n^v~#-S>vnhS7ى 2f{*6ƷF]U8S9<SBHSl3g|_*J8]q yr*Q{Bp!G0ąZZx#o_kpǁpwNWr˥UgÜ$}G>p3 H:7)F:UfT9YgGgEfbKø9a:뺚@*$f)&Ӄiy ihrNe~})Mϯ j-/R/qi=<{^ss-unۜaAM0 @!fA*NAhag&#F3̼r4.AfS'502>F"CD9mbnZ&⚎Ij0:%gB.~IaAQcys: =R Vn sF8'%p>qFDkգ 9<؋@d_{o,$HF~g1DAĜQXH:Ǻ꼂XZ-=T t'?Q#qw/:q5wٳ?[.4S8xAVscLl`n@l$iZ8;94q a]H%`ۜ0xfsNz6`7vɁz3+!,8ɌhCY6ʅ90|Cb""-aT1p#O%WDM@]x/2|= {`8~zmB,)-ɁfВ \p^LHRѭh8>i1~7ւOϢX#(_%UlDK=~ݥ{?+qt>g~HN)S~9^%?J(p,AF+cӦ x`(|89;$`:  MAK86EDC3UyR914;o2 gaS}4Ld-gs=dGrJvrpxl,ectw &!iPoVֻ>V8;nDzkJݥ ! WIDAT6u> }܏|3MG8޷_|ktg;}@027!X.ϢF\MpzH.zdC~N 7Nn Ӏic`F=LQXͻ1u]';Y*Z;%0\~xy~9Hr:@2T^w59;g3A@{VsԈz̵S-gnJ)#9 J\ɩbꏙT'eEDzMy߁5>$p!ӽP+.ӧ\{w<|[~ ;{ғ?7~C}pfeo Z sH`. 2H7;$ϟȻu<׎ޒ}9:y/g~N09%r3]9899?`-ʆUϨC !iԺyvOǡsK`6|55#&&Dt V?.:aBDd4""nu79Vɑ.e&,s?}wp>pqݓ_M;w]ZU}1&7-H(HkL99 %Cp=F A0KOurvh8wNIc) EZksmbBd.6dzHN6WvTGw[yTy9r&7;maU!^ɾ l\~6Dub%H]~6I1.޽;?~18{=|߸Mw~qOW鳍S㾇|03M0:A.L\׃En{9h[ &=0aDdjC w"x#7*o褽Jqb枩hy=&rQ^R X:uHs@ ̷pʈUJ2U:NO!iwNm܈H -m5$^?BA=6R.MU4jϜ9H'k<ї믪׳"GG8>1OyϾ|co opXh p^ $7otպDp{7Āe0R?nd⭚k1H^ԁ9 \xKA$8_@yQiy 7! _AFgG)"J} ד m@3Y.": AJX~ G119uۜi~X62y %b!Č ]=ȄJBױ>],H!?W]/92M;J,X׭Q6Q؍u/e.L= E2<G&P:2I8w_Hdg) QGOl[& iYxKv2_U;Op:wrrÅ ?Ο&%)_y9xC,C^nyR:":Ŕֶ ~ƼgrJ"1Q W~bN9 SE,KzXGSno{qAA8\?+շͷ'1m7ZÁ`F  !YSX>rhφ`e zkh0bÈ6pzz9`*Mُ\9`e|1t>wkC֎zYF!IuРzdM͏:8%h%js5 &G'#59$[45@7\wzk?q< LJr~/spe68lsy2ѩ*t<$bDk-\hqw3{:69 eā΅`s[+* !weqNƎH~m՝6;,4I>o/ӖKC+ڢ<8c8ʱ%fdD`eTXMx];0qnY_%/W|qU6DeBd$Im -s )$#%e'i~7Bì޽_:ΖW~ KCaČ }x=\2G[^3 E%=AKeӜ&zkf؍3=djI.S!\qV `i~0Qs>qWYO|͍ʯx?x98qt!;) Io;E2̀x#XhRcU)Z>mR+֬Gِf9# fHz"DK4pK-X:,/JDƵX"[EA6ITH97b"CdOf?UBĜ0~ɕbM jRfZHYd訮BX^]%DiIt- C@'FJH0R3 8,+Our7ާ8AG8Ӿ%ϽQ<[.*nh(BX/2y୑b7Y4(k[124 m,x˾v17z0-5]W)c IR .02[|FlY5]rpuCQ˩=I)(.Tt S4QeZs5ةXɑ;a= n+… (`]/q¹NԘ:axHp//zmG<w^w)Tt-5!yHT f?l<햙. @ƹ"Ð.J&csAv$x8胘E> Ҳ|08m> 3 %[ D2j!~nwpk?X5\W]rrz׍7w|)8qto<'/vMﺳR[&UQoE2tQk) i,]]%GΝu&n61̓ bLFETX`$"ŇA/J`4 yM@ D0bL@f\gvv~޾ULJSughv5aܮ}> 4I {Trd^"EfY?|q[@i[!RfH7C"+\E$&5s"(ɍ y ^١dNt򎣋vi?FFƛAxw~Sū>ݘ^>==bx ޗ1~|cزao]59ka "Hz 9:lk(;ž1 ^[]בᯁ7/ -ltפ%2DŽ1M4u zdO`- U\I*݄O1-0 9pbif4PΟ;_=^W0#c_"7 KǏ~hتZԺ0hy6F 'VW``E>: |O9`; F?ncF_p# ɉ-&K)CfjT ' S'Ijqnডi >!9$WwKC)ݧo BƁ-cs Wsw>wn( $&ERH)\cC(N B w$HIVɆsUU)|F(HCJU4:NCj rG SQ0J \ ^2bB_ KOO(Hpda:Sluǭ=~zfAH뾻#W]QϭVHZNb'!U 3R'Rӵ$9(]%B G(ʠJ0rӑTW:(RiC(-Xj^fhv xR"'"|h%U<ȡZn^oroq_wl<ѯm-ViD8h8$^@2@.$H!.a`s$EM1+u.N)3 BArO#;-'I~BۦCR}&Q qx"~OƅevaZh͕G?d_} dd8 yU'V'3g6wUp#,:΄WPyڎMyO:1ON${SOOA(lͫPJqF.JYU@Jrc$:j 9]q*)F1oREy}r;Cpta!3/ɗ/!# 7=7>~T n>;]熶 ƂKiU/~ 8 *"D*Sbd,camNABI \! ?I\wo6;S,f꩗>lFFn22.߶x'>{}xc Vַ)8潒;b>[P>× AwFDB Zڒ۳nL7DaҶY;RG9Bxnшh/κC=6nPf{=G|327/TE Km*); Ul`傑1307hfPUzZmf8J[ԌvC{6;fd1}ṉǯ(U\AZ/9R,YRfIV9tk mӨƶ[ xD!,ZF0WeIF "ڲTchd75E__\{ă.##_N!3MIENDB`ukui-screensaver/src/screensaver/playcontroller.cpp0000664000175000017500000001454015172041106021660 0ustar fengfeng#include "playcontroller.h" #include "dbusadapter.h" #include "musicdatabase.h" #include "musicfileinformation.h" playController::playController(QObject *parent) : QObject{parent} { m_player = new MMediaPlayer(this); if (m_player == nullptr) { qDebug() << "failed to create player "; return; } m_playlist = new MMediaPlaylist(m_player); if (m_playlist == nullptr) { qDebug() << "failed to create laylist"; return; } m_player->setPlaylist(m_playlist); m_playlist->setPlaybackMode(MMediaPlaylist::Loop); connect(m_playlist,&MMediaPlaylist::currentIndexChanged,this,&playController::slotIndexChange); connect(m_player,&MMediaPlayer::stateChanged,this,&playController::slotStateChanged); } playController::~playController() { if (m_playlist != nullptr) { delete m_playlist; m_playlist = nullptr; } if (m_player != nullptr) { m_player->stop(); delete m_player; m_player = nullptr; } } bool playController::play(QString playlist, int index) { if (playlist.compare(m_curList)==0) { stop(); setSongIndex(index); play(); return true; } return false; } bool playController::play() { if (m_player == nullptr) { return false; } if (m_player->isAvailable() == false) { //存疑 return false; } if (m_player->state() == MMediaPlayer::State::PlayingState) { m_player->pause(); } else { m_player->play(); } return true; } bool playController::pause() { if (m_player == nullptr) { return false; } m_player->pause(); return true; } bool playController::pauseOnly() { if (m_player == nullptr) { return false; } m_player->pauseOnly(); return true; } bool playController::stop() { if (m_player == nullptr) { return false; } m_player->stop(); return true; } void playController::nextSong() { if (m_playlist == nullptr || m_player == nullptr) { qDebug() << "m_playlist or m_player is nullptr"; return; } m_playlist->next(); m_player->play(); } void playController::previousSong() { if (m_playlist == nullptr || m_player == nullptr) { qDebug() << "m_playlist or m_player is nullptr"; return; } m_playlist->previous(); m_player->play(); } QVariantMap playController::getMetadata() const { QVariantMap metadata; MMediaContent content = m_playlist->media(m_playlist->currentIndex()); QString path = content.canonicalUrl().toLocalFile(); QFileInfo file(path.remove("file://")); if(file.exists()) { musicDataStruct musicStruct; QString filePath = path.remove("file://"); g_db->getSongInfoFromDB(filePath, musicStruct); metadata["xesam:title"] = musicStruct.title; metadata["xesam:artist"] = musicStruct.singer; metadata["xesam:album"] = musicStruct.album; metadata["mpris:artUrl"] = ""; } else { metadata["xesam:title"] = ""; metadata["xesam:artist"] = ""; metadata["xesam:album"] = ""; metadata["mpris:artUrl"] = ""; } metadata["mpris:trackid"] = 0; metadata["mpris:length"] = 0; metadata["xesam:albumArtist"] = ""; metadata["xesam:asText"] = ""; metadata["xesam:audioBPM"] = 0; metadata["xesam:autoRating"] = 0.0; metadata["xesam:trackNumber"] = 0; metadata["xesam:url"] = ""; metadata["xesam:useCount"] = 0; metadata["xesam:userRating"] = 0.0; return metadata; } void playController::setSongIndex(int index) { if (m_playlist == nullptr) { qDebug() << "m_playlist is null"; return; } if(index < 0) { return; } if (index > m_playlist->mediaCount()) { return; } m_playlist->setCurrentIndex(index); } void playController::setCurPlaylist(/*QString name, */QStringList songPaths) { if (m_playlist == nullptr || m_player == nullptr) { return; } // m_curList = name; disconnect(m_playlist,&MMediaPlaylist::currentIndexChanged,this,&playController::slotIndexChange); m_playlist->clear(); for (auto path : songPaths) { m_playlist->addMedia(QUrl::fromLocalFile(path)); } m_player->stop(); m_player->setPlaylist(nullptr); m_player->setPlaylist(m_playlist); m_playlist->setCurrentIndex(0); if (m_playlist->mediaCount() > 0) { Q_EMIT mateDataChanged(); slotIndexChange(m_playlist->currentIndex()); } connect(m_playlist, &MMediaPlaylist::currentIndexChanged, this, [&,this](int index) { if (m_playlist->mediaCount() > 0) { Q_EMIT mateDataChanged(); slotIndexChange(index); } }); } void playController::addMusicToDatebase(QStringList fileNames) { int res; res = g_db->initDataBase(); if(res != DB_OP_SUCC) { qDebug() << "数据库加载失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; } qDebug() << "数据库加载成功"; MusicFileInformation::getInstance().addFile(fileNames); QList resList; resList = MusicFileInformation::getInstance().resList; int ret; foreach (const musicDataStruct date, resList) { ret = g_db->addMusicToLocalMusic(date); } } void playController::slotIndexChange(int index) { if (index == -1) { Q_EMIT singalChangePath(""); } m_curIndex = index; MMediaContent content = m_playlist->media(index); QString path = content.canonicalUrl().toLocalFile(); QFileInfo file(path.remove("file://")); if(file.exists()) { Q_EMIT singalChangePath(path); } } void playController::slotStateChanged(MMediaPlayer::State newState) { if(newState == MMediaPlayer::State::PlayingState) Q_EMIT playerStateChange(playController::PlayState::PLAY_STATE); else if(newState == MMediaPlayer::State::PausedState) Q_EMIT playerStateChange(playController::PlayState::PAUSED_STATE); else if(newState == MMediaPlayer::State::StoppedState) Q_EMIT playerStateChange(playController::PlayState::STOP_STATE); // DbusAdapter *m_adapter = new DbusAdapter; // QVariantMap changedProperties; // changedProperties.insert("PlaybackStatus",QVariant::fromValue(QDBusVariant("Stopped"))); // m_adapter->notify("org.mpris.MediaPlayer2.Player",changedProperties,QStringList()); // delete m_adapter; } ukui-screensaver/src/screensaver/weatherthumbnailwidget.cpp0000664000175000017500000001161215172041106023353 0ustar fengfeng#include "weatherthumbnailwidget.h" #include #include #include #include WeatherThumbnailWidget::WeatherThumbnailWidget(QWidget *parent, bool isControlSaver) : QWidget{parent}, m_bControlSaver(isControlSaver) { initUI(); installEventFilter(this); } void WeatherThumbnailWidget::initUI() { m_mainLayout = new QHBoxLayout(this); if (m_bControlSaver ) { setFixedSize(201, 113); } else { setFixedSize(292, 164); } m_timeThumbnailWidget = new TimeThumbnailWidget(this, m_bControlSaver); m_mainLayout->addWidget(m_timeThumbnailWidget); m_weatherWidget = new WeatherWidget(this); m_mainLayout->addWidget(m_weatherWidget); if (!m_bControlSaver ) { m_weatherWidget->setFixedSize(534 * 0.15, 770 * 0.15); m_weatherWidget->updateWeatherSize(0.15); } else { m_weatherWidget->setFixedSize(534 * 0.10, 770 * 0.10); m_weatherWidget->updateWeatherSize(0.10); } m_maskLabel = new MaskLable(this, m_bControlSaver); m_maskLabel->setGeometry(0, 0, this->width(), this->height()); m_maskLabel->hide(); if (!m_bControlSaver) { m_animation = new QVariantAnimation(this); m_animation->setDuration(300); m_animation->setEasingCurve(QEasingCurve::OutQuad); connect(m_animation, &QVariantAnimation::valueChanged, [this](const QVariant &value) { QSize size = value.toSize(); setFixedSize(size); m_maskLabel->setFixedSize(size); }); } } void WeatherThumbnailWidget::setBackground(QPixmap background) { m_background = background; repaint(); } void WeatherThumbnailWidget::animateSize(const QSize &targetSize) { m_animation->stop(); m_animation->setStartValue(size()); m_animation->setEndValue(targetSize); m_animation->start(); } void WeatherThumbnailWidget::setSelectStatus(bool status) { if (!status && m_bSelectStatus) { m_maskLabel->hide(); m_bSelectStatus = false; } else if (status && !m_bSelectStatus) { m_maskLabel->show(); m_bSelectStatus = true; } } void WeatherThumbnailWidget::setWeatherIcon(QString weather, QPixmap weatherStateIcon) { m_weatherWidget->setWeatherIcon(weather, weatherStateIcon); } void WeatherThumbnailWidget::setCurrentWeather(QString temperature, QString weather) { m_weatherWidget->setCurrentWeather(temperature, weather); } void WeatherThumbnailWidget::setErrorTip(QString tip) { m_weatherWidget->setErrorTip(tip); } bool WeatherThumbnailWidget::eventFilter(QObject *watched, QEvent *event) { if (parentWidget() && !parentWidget()->isEnabled()) { event->ignore(); return QWidget::eventFilter(watched, event); } if (event->type() == QEvent::Enter) { if (!m_bControlSaver) { animateSize(QSize(320, 180)); m_bHoverStatus = true; } if (m_bControlSaver || m_bSelectStatus) { m_maskLabel->show(); } } else if (event->type() == QEvent::Leave) { if (!m_bControlSaver) { animateSize(QSize(292, 164)); m_bHoverStatus = false; } if (!m_bSelectStatus) { m_maskLabel->hide(); } } if (event->type() == QEvent::MouseButtonRelease) { if (!m_bControlSaver) { m_maskLabel->show(); m_bSelectStatus = true; } m_maskLabel->show(); Q_EMIT weatherModeClicked(); } return QWidget::eventFilter(watched, event); } void WeatherThumbnailWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); if (!m_bControlSaver) { QPainterPath backgroundPath; backgroundPath.addRoundedRect(rect(), 8, 8); painter.setClipPath(backgroundPath); painter.drawPixmap(this->rect(), m_background, m_background.rect()); painter.setClipping(false); if (m_bHoverStatus) { QPen pen(QColor(255, 255, 255, 153)); pen.setWidth(2); painter.setPen(pen); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 8, 8); } else { painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); // 178 painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } else { painter.fillRect(rect(), palette().color(QPalette::Base)); const QRect bgRect(4, 4, 193, 105); QPainterPath path; path.addRoundedRect(bgRect, 8, 8); painter.setClipPath(path); painter.drawPixmap(bgRect, m_background, m_background.rect()); painter.setPen(Qt::transparent); painter.setBrush(QColor(0, 0, 0, 78)); painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); } } ukui-screensaver/src/screensaver/dbusadapter.cpp0000664000175000017500000001376715172041106021117 0ustar fengfeng#include #include "dbusadapter.h" #include "playcontroller.h" DbusAdapter::DbusAdapter(QObject *parent) : QObject(parent) { QDBusConnection::sessionBus().unregisterService("org.mpris.MediaPlayer2.ScreenSaver"); QDBusConnection::sessionBus().registerService("org.mpris.MediaPlayer2.ScreenSaver"); QDBusConnection::sessionBus().registerObject("/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", this, QDBusConnection::ExportAllContents); this->setProperty("CanControl", false); // initMetadata(); } //QString DbusAdapter::getPlaybackState() //{ // return Widget::mutual->getPlayState(); //} //QString DbusAdapter::getLoopStatus() //{ // return Widget::mutual->getPlayMode(); //} //void DbusAdapter::setLoopStatus(QString s) //{ // Widget::mutual->setPlayMode(s); //} QVariantMap DbusAdapter::getMetadata() { return playController::getInstance().getMetadata(); } //void DbusAdapter::updateMetadata(const QVariantMap input) //{ // bool needUpdate = false; // for (auto iter = input.begin(); iter != input.end(); iter++) { // ; // if (m_metadata.find(iter.key()) != m_metadata.end() && m_metadata[iter.key()] != iter.value()) { // m_metadata[iter.key()] = iter.value(); // needUpdate = true; // } // } //} //double DbusAdapter::getVolume() //{ // return double(Widget::mutual->getVolume()) / 100; //} //// mpris中volume的取值范围是[0,1],但软件中取值范围是[0, 100] //void DbusAdapter::setVolume(double d) //{ // if (d < 0) { // d = 0; // } else if (d > 1) { // qWarning() << "setVolume error, bigger than 1, volume is :" << d; // return; // } // Widget::mutual->setVolume(d*100); // QVariantMap changedProperties; // changedProperties.insert("Volume",QVariant::fromValue(QDBusVariant(d))); // notify("org.mpris.MediaPlayer2.Player",changedProperties,QStringList()); //} //long long DbusAdapter::getPosition() //{ // return Widget::mutual->getPosition() * 1000; //} //void DbusAdapter::initMetadata() //{ // // 表示路径 // m_metadata.insert("mpris:trackid", 0); // m_metadata.insert("mpris:length", 0); // m_metadata.insert("mpris:artUrl", ""); // m_metadata.insert("xesam:album", ""); // m_metadata.insert("xesam:albumArtist", ""); // m_metadata.insert("xesam:artist", ""); // m_metadata.insert("xesam:asText", ""); // m_metadata.insert("xesam:audioBPM", 0); // m_metadata.insert("xesam:autoRating", 0.0); // QStringList commont; // m_metadata.insert("xesam:comment", commont); // QStringList composer; // m_metadata.insert("xesam:composer", composer); // m_metadata.insert("xesam:contentCreated", ""); // m_metadata.insert("xesam:discNumber", 0); // m_metadata.insert("xesam:firstUsed", ""); // QStringList genre; // m_metadata.insert("xesam:genre", genre); // m_metadata.insert("xesam:lastUsed", ""); // QStringList lyricist; // m_metadata.insert("xesam:lyricist", lyricist); // m_metadata.insert("xesam:title", ""); // m_metadata.insert("xesam:trackNumber", 0); // m_metadata.insert("xesam:url", ""); // m_metadata.insert("xesam:useCount", 0); // m_metadata.insert("xesam:userRating", 0.0); //} void DbusAdapter::Stop() const { playController::getInstance().stop(); } void DbusAdapter::Next() const { playController::getInstance().nextSong(); } void DbusAdapter::Play() const { playController::getInstance().play(); } void DbusAdapter::Pause() const { playController::getInstance().pause(); } //void DbusAdapter::PlayPause() //{ // qDebug() << "into PlayPause throught mpris."; // playController::getInstance().playPause(); // QVariantMap changedProperties; // changedProperties.insert("PlaybackStatus",QVariant::fromValue(QDBusVariant(m_playbackStatus))); // notify("org.mpris.MediaPlayer2.Player",changedProperties,QStringList()); //} void DbusAdapter::Previous() const { playController::getInstance().previousSong(); } //void DbusAdapter::VolumeUp() const //{ // Widget::mutual->VolumeUp(); //} //void DbusAdapter::VolumeDown() const //{ // Widget::mutual->VolumeDown(); //} //void DbusAdapter::FullScreen() const //{ // Widget::mutual->slotShowMaximized(); //} //void DbusAdapter::Exit() const //{ // Widget::mutual->slotClose(); //} ///** // * @brief DbusAdapter::Seek // * @param offset 偏移微妙数,正数表示向后,负数表示向前 // * 如果结果小于0,表示从开始位置播放;如果结果大于媒体时长,表示播放下一曲 // */ //void DbusAdapter::Seek(const qint64 Offset) //{ // qDebug() << "seek :" << Offset; // // 如果小于0.1ms就不进行操作 // if (qAbs(Offset) > 1000*99) { // Widget::mutual->Seek(Offset / 1000); // } //} //void DbusAdapter::SetPosition(const QString &TrackId, const qint64 Position) //{ //} //void DbusAdapter::OpenUri(const QString &Uri) //{ //} void DbusAdapter::notify(const QString &interfaceName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties) { QDBusConnection connection = QDBusConnection::sessionBus(); QString objectPath = "/org/mpris/MediaPlayer2"; QDBusMessage signalMessage= QDBusMessage::createSignal(objectPath,"org.freedesktop.DBus.Properties","PropertiesChanged"); // QVariantMap changed; // changed.insert("PlaybackStatus",QVariant(PlaybackStatus)); signalMessage << interfaceName << changedProperties << invalidatedProperties; connection.send(signalMessage); } //double DbusAdapter::getRate() //{ // qDebug() << "getRate"; // return Widget::mutual->getPlayRate(); //} //void DbusAdapter::setRate(double d) //{ // qDebug() << "setRate:" << d; // if (d >= m_minimumRate && d <= m_maximumRate) { // Widget::mutual->setPlayRate(d); // } else { // qWarning() << "dbus set rate error, rate is :" << d; // } // QVariantMap changedProperties; // changedProperties.insert("Rate",QVariant::fromValue(QDBusVariant(d))); // notify("org.mpris.MediaPlayer2.Player",changedProperties,QStringList()); //} ukui-screensaver/src/screensaver/rotatablelabel.cpp0000664000175000017500000000776215172041106021574 0ustar fengfeng#include "rotatablelabel.h" #include #include RotatingImageLabel::RotatingImageLabel(QWidget *parent) : QLabel(parent), m_currentAngle(0), m_isPaused(false) { // 初始化动画 m_animation = new QPropertyAnimation(this, "rotationAngle", this); m_animation->setDuration(2000); // 默认2秒/圈 m_animation->setStartValue(0); m_animation->setEndValue(360); m_animation->setLoopCount(-1); // 无限循环 // 初始化快速旋转动画 m_quickAnimation = new QPropertyAnimation(this, "rotationAngle", this); m_quickAnimation->setDuration(1000); // 1秒完成 m_quickAnimation->setEasingCurve(QEasingCurve::OutQuad); // 动画冲突处理 connect(m_quickAnimation, &QPropertyAnimation::stateChanged, [this](){ if (m_quickAnimation->state() == QPropertyAnimation::Running) { m_animation->pause(); } else if (m_animation->state() == QPropertyAnimation::Paused) { m_animation->resume(); } }); // // 设置抗锯齿和高质量渲染 // this->setAttribute(Qt::WA_TranslucentBackground); // this->setAlignment(Qt::AlignCenter); } void RotatingImageLabel::startRotation(int speedMsPerRound) { if (m_pixmap.isNull()) return; m_animation->setDuration(speedMsPerRound); if (m_isPaused) { resumeRotation(); } else { m_animation->start(); } update(); } void RotatingImageLabel::stopRotation() { m_animation->stop(); m_currentAngle = 0; m_isPaused = false; update(); } void RotatingImageLabel::pauseRotation() { if (m_animation->state() == QAbstractAnimation::Running) { m_animation->pause(); m_isPaused = true; } update(); } void RotatingImageLabel::resumeRotation() { if (m_isPaused) { m_animation->resume(); m_isPaused = false; } } void RotatingImageLabel::setRotationSpeed(int msPerRound) { bool wasRunning = (m_animation->state() == QAbstractAnimation::Running); m_animation->setDuration(msPerRound); if (wasRunning) { m_animation->start(); // 重新启动以应用新速度 } } void RotatingImageLabel::setImage(const QPixmap &pixmap) { m_pixmap = pixmap; update(); } void RotatingImageLabel::setImage(const QString &filePath) { m_pixmap.load(filePath); update(); } int RotatingImageLabel::rotationAngle() const { return m_currentAngle; } void RotatingImageLabel::setRotationAngle(int angle) { m_currentAngle = angle; update(); // 触发重绘 } void RotatingImageLabel::rotate90CCW() { // 停止所有动画 m_animation->stop(); m_quickAnimation->stop(); // 设置快速旋转动画 int targetAngle = m_currentAngle - 90; m_quickAnimation->setStartValue(m_currentAngle); m_quickAnimation->setEndValue(targetAngle); m_quickAnimation->start(); } void RotatingImageLabel::resetRotation() { // 停止所有动画 m_animation->stop(); m_quickAnimation->stop(); // 设置复位动画 m_quickAnimation->setStartValue(m_currentAngle); m_quickAnimation->setEndValue(0); m_quickAnimation->setDuration(2000); m_quickAnimation->start(); } void RotatingImageLabel::paintEvent(QPaintEvent *event) { Q_UNUSED(event); if (m_pixmap.isNull()) { QLabel::paintEvent(event); return; } QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::SmoothPixmapTransform, true); // 计算绘制区域 int side = qMin(width(), height()); QRectF destRect((width() - side) / 2, (height() - side) / 2, side, side); // 设置圆形裁剪区域 QPainterPath path; path.addEllipse(destRect); painter.setClipPath(path); // 旋转绘制 painter.save(); QPointF center = destRect.center(); painter.translate(center); painter.rotate(m_currentAngle); painter.translate(-center); // 绘制图片 painter.drawPixmap(destRect, m_pixmap, m_pixmap.rect()); painter.restore(); } ukui-screensaver/src/screensaver/mprisdbusclient.h0000664000175000017500000001511215172041106021457 0ustar fengfeng#ifndef MPRISDBUSCLIENT_H #define MPRISDBUSCLIENT_H #include #include #include #include #include #include #include #include #include /* * Common mpris interface,reference:https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html */ class MPRISDBusClient : public QDBusAbstractInterface { Q_OBJECT public: MPRISDBusClient( const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); ~MPRISDBusClient(); Q_PROPERTY(QString PlaybackStatus READ playbackStatus NOTIFY PlaybackStatusChanged) inline QString playbackStatus() const { return qvariant_cast(property("PlaybackStatus")); } Q_PROPERTY(QString LoopStatus READ loopStatus WRITE setLoopStatus NOTIFY LoopStatusChanged) inline QString loopStatus() const { return qvariant_cast(property("LoopStatus")); } inline void setLoopStatus(const QString &value) { setProperty("LoopStatus", QVariant::fromValue(value)); } Q_PROPERTY(double Rate READ rate WRITE setRate NOTIFY RateChanged) inline double rate() const { return qvariant_cast(property("Rate")); } inline void setRate(double value) { setProperty("Rate", QVariant::fromValue(value)); } Q_PROPERTY(bool Shuffle READ shuffle WRITE setShuffle NOTIFY ShuffleChanged) inline bool shuffle() const { return qvariant_cast(property("Shuffle")); } inline void setShuffle(bool value) { setProperty("Shuffle", QVariant::fromValue(value)); } Q_PROPERTY(QVariantMap Metadata READ metadata NOTIFY MetadataChanged) inline QVariantMap metadata() const { return qvariant_cast(property("Metadata")); } Q_PROPERTY(double Volume READ volume WRITE setVolume NOTIFY VolumeChanged) inline double volume() const { return qvariant_cast(property("Volume")); } inline void setVolume(double value) { setProperty("Volume", QVariant::fromValue(value)); } Q_PROPERTY(qlonglong Position READ position NOTIFY PositionChanged) inline qlonglong position() const { return qvariant_cast(property("Position")); } Q_PROPERTY(double MinimumRate READ minimumRate NOTIFY MinimumRateChanged) inline double minimumRate() const { return qvariant_cast(property("MinimumRate")); } Q_PROPERTY(double MaximumRate READ maximumRate NOTIFY MaximumRateChanged) inline double maximumRate() const { return qvariant_cast(property("MaximumRate")); } Q_PROPERTY(bool CanGoNext READ canGoNext NOTIFY CanGoNextChanged) inline bool canGoNext() const { return qvariant_cast(property("CanGoNext")); } Q_PROPERTY(bool CanGoPrevious READ canGoPrevious NOTIFY CanGoPreviousChanged) inline bool canGoPrevious() const { return qvariant_cast(property("CanGoPrevious")); } Q_PROPERTY(bool CanPlay READ canPlay NOTIFY CanPlayChanged) inline bool canPlay() const { return qvariant_cast(property("CanPlay")); } Q_PROPERTY(bool CanPause READ canPause NOTIFY CanPauseChanged) inline bool canPause() const { return qvariant_cast(property("CanPause")); } Q_PROPERTY(bool CanSeek READ canSeek NOTIFY CanSeekChanged) inline bool canSeek() const { return qvariant_cast(property("CanSeek")); } Q_PROPERTY(bool CanControl READ canControl NOTIFY CanControlChanged) inline bool canControl() const { return qvariant_cast(property("CanControl")); } public Q_SLOTS: // METHODS void onPropertyChanged(const QDBusMessage &msg); inline QDBusPendingReply<> Next() { QList argumentList; qDebug()<<"media Next!!"; return asyncCallWithArgumentList(QStringLiteral("Next"), argumentList); } inline QDBusPendingReply<> Previous() { QList argumentList; qDebug()<<"media Previous!!"; return asyncCallWithArgumentList(QStringLiteral("Previous"), argumentList); } inline QDBusPendingReply<> Pause() { QList argumentList; qDebug()<<"media Pause!!"; return asyncCallWithArgumentList(QStringLiteral("Pause"), argumentList); } inline QDBusPendingReply<> PlayPause() { QList argumentList; qDebug()<<"media PlayPause!!"; return asyncCallWithArgumentList(QStringLiteral("PlayPause"), argumentList); } inline QDBusPendingReply<> Stop() { QList argumentList; return asyncCallWithArgumentList(QStringLiteral("Stop"), argumentList); } inline QDBusPendingReply<> Play() { QList argumentList; qDebug()<<"media Play!!"; return asyncCallWithArgumentList(QStringLiteral("Play"), argumentList); } inline QDBusPendingReply<> Seek(qlonglong Offset) { QList argumentList; argumentList << QVariant::fromValue(Offset); return asyncCallWithArgumentList(QStringLiteral("Seek"), argumentList); } inline QDBusPendingReply<> SetPosition(const QDBusObjectPath &TrackId, qlonglong Position) { QList argumentList; argumentList << QVariant::fromValue(TrackId) << QVariant::fromValue(Position); return asyncCallWithArgumentList(QStringLiteral("SetPosition"), argumentList); } inline QDBusPendingReply<> OpenUri(const QString &Uri) { QList argumentList; argumentList << QVariant::fromValue(Uri); return asyncCallWithArgumentList(QStringLiteral("OpenUri"), argumentList); } Q_SIGNALS: // SIGNALS void Seeked(qlonglong Position); // begin property changed signals void CanControlChanged(bool value); void CanGoNextChanged(bool value); void CanGoPreviousChanged(bool value); void CanPauseChanged(bool value); void CanPlayChanged(bool value); void CanSeekChanged(bool value); void LoopStatusChanged(const QString &value); void MaximumRateChanged(double value); void MetadataChanged(const QVariantMap &value); void MinimumRateChanged(double value); void PlaybackStatusChanged(const QString &value); void PositionChanged(qlonglong value); void RateChanged(double value); void ShuffleChanged(bool value); void VolumeChanged(double value); }; #endif // MPRISDBUSCLIENT_H ukui-screensaver/src/screensaver/chinesedate.h0000664000175000017500000000364515172041035020535 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef CHINESEDATE_H #define CHINESEDATE_H #include #include class ChineseDate { public: ChineseDate(); QString getDateLunar(); private: /** * @brief 根据农历年份获取该年天数 * @param year 农历年份 * @return */ int getYearDays(int year); /** * @brief 根据农历年份判断该年是否有闰月 * @param year 农历年份 * @return */ bool isLeapMonth(int year); /** * @brief 根据农历年份获取闰月的天数,如果存在闰月的话 * @param year 农历年份 * @return */ int getLeapMonthDays(int year); /** * @brief 根据农历年份获取闰月的月份 * @param year 农历年份 * @return */ int getLeapMonth(int year); /** * @brief 根据农历年份和月份获取当月的天数 * @param year 农历年份 * @param month 农历月份 * @return */ int getYearMonthDays(int year, int month); /** * @brief 获取农历日期 * @return */ QString getChineseDays(int month, int day, bool isleap); QStringList ChineseMonth; QStringList ChineseDay; QStringList ChineseDays; }; #endif // CHINESEDATE_H ukui-screensaver/src/screensaver/customalbumviewer.h0000664000175000017500000000263315172041106022031 0ustar fengfeng// customalbumviewer.h #ifndef CUSTOMALBUMVIEWER_H #define CUSTOMALBUMVIEWER_H #include #include #include #include #include #include #include "customlabel.h" #include "adaptivelabel.h" class CustomAlbumViewer : public QWidget { Q_OBJECT public: CustomAlbumViewer(QWidget *parent = nullptr); ~CustomAlbumViewer(); void loadImages(const QString &directoryPath); void setUpdateInterval(int interval = 60000); // 默认1分钟 void setImagesPath(const QString &strPath); void updateViewerSize(float scale = 0); void setDefaultPixmap(QPixmap pixmap); void stopDisplayImages(); public slots: void nextSlide(); void setSequentialMode(bool sequential); private: void displayImages(); QPixmap scalePixmap(const QPixmap &original, const QSize &size); Q_SIGNALS: void pixmapChanged(); private: float m_scale; QTimer *m_timer; QStringList m_imagePaths; QString m_sSelectPhotoPath; QPixmap m_defaultImage; bool m_isSequential = true; int m_currentIndex = 0; QWidget *m_centralWidget = nullptr; QVBoxLayout *m_mainLayout = nullptr; CustomLabel *m_tipLabel = nullptr; AdaptiveLabel *m_topLabel = nullptr; QHBoxLayout *m_bottomLayout = nullptr; AdaptiveLabel *m_leftBottomLabel = nullptr; AdaptiveLabel *m_rightBottomLabel = nullptr; }; #endif // CUSTOMALBUMVIEWER_H ukui-screensaver/src/screensaver/masklable.cpp0000664000175000017500000000260115172041106020535 0ustar fengfeng#include "masklable.h" #include "commonfunc.h" #include #include #include MaskLable::MaskLable(QWidget *parent, bool isControlSaver) : QLabel{parent}, m_bControlSaver(isControlSaver) { } void MaskLable::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); if (!m_bControlSaver) { painter.setOpacity(0.45); painter.setClipping(true); painter.setPen(Qt::transparent); painter.setBrush(QColor("#000000")); painter.setPen(Qt::transparent); painter.drawRoundedRect(rect().adjusted(0, 0, 0, 0), 8, 8); QRect widgetRect = this->rect(); QPixmap prevIcon = drawSymbolicColoredPixmap( QIcon::fromTheme("software-installed-symbolic").pixmap(QSize(40, 40)), "white"); QPoint centerPos = widgetRect.center() - QPoint(20, 20); painter.drawPixmap(centerPos, prevIcon); } else { QColor highlightColor = palette().color(QPalette::Highlight); QRectF borderRect = rect().adjusted(1, 1, -1, -1); QPainterPath path; path.addRoundedRect(borderRect, 8, 8); painter.setClipPath(path); painter.setClipping(false); QPen pen(highlightColor, 2); painter.setPen(pen); painter.drawPath(path); } } ukui-screensaver/src/screensaver/mediasaverwidget.h0000664000175000017500000000355115172041106021600 0ustar fengfeng#ifndef MULTIMEDIACONTROLWIDGET_H #define MULTIMEDIACONTROLWIDGET_H #include #include #include #include "mprissaverwidget.h" #ifdef signals #undef signals #endif #include #include #include class MediaSaverWidget : public QWidget { Q_OBJECT public: explicit MediaSaverWidget(QWidget *parent = nullptr, bool saverMedia = false); virtual ~MediaSaverWidget(); void initMultimediaControl(); void setSaverMediaType(bool saverMedia); void updateFont(QString strFont, double fontSizeF); void updateMediaSize(float scale); public Q_SLOTS: void onStatusChanged(bool isActive); protected: // void paintEvent(QPaintEvent *event); private: void init(const QString &strMediaPath); void initConnections(); /** * @brief getServiceCmd 获取dbus服务的进程信息 * @param strMediaPath dbus服务名称 * @return 进程全路径 */ QString getServiceCmd(const QString &strMediaPath); /** * @brief getDefaultAppId 获取默认mime类型的desktop * @param contentType mime类型 * @return desktop文件名 */ QString getDefaultAppId(const char *contentType); /** * @brief getDefaultAudioAppExecInfo 获取音频默认打开二进制 * @return 二进制路径信息 */ QString getDefaultAudioAppExecInfo(); void initSaverMediaWidget(); Q_SIGNALS: void loadDbusListNameFinished(bool success); private: MPRISSaverWidget *m_pMPRISWidget = nullptr; QString m_strMediaPath = ""; QDBusConnectionInterface *m_dbusDaemonInterface = nullptr; QDBusPendingCallWatcher *m_dbusListNameWatcher = nullptr; QString m_strCurFont; double m_lfFontSizeF = 1.0; float m_scale = 0; bool m_bSaverMedia = false; }; #endif // MULTIMEDIACONTROLWIDGET_H ukui-screensaver/src/screensaver/musicdatabase.cpp0000664000175000017500000024315515172041106021422 0ustar fengfeng#include "musicdatabase.h" #include #include #include #include #include #include #include #include #include #include MusicDataBase::MusicDataBase(QObject *parent) : QObject(parent) { qDebug() << QSqlDatabase::drivers();//当前环境支持哪些数据库 QMutexLocker lockData( &m_mutex); //加锁,函数执行完后自动解锁 m_database=QSqlDatabase::addDatabase("QSQLITE"); QString dirPath = QString(getenv("HOME")) + "/.config/.kylin_music_ver1.2_"; //TODO QFileInfo oldVersion(QString(getenv("HOME")) + "/.config/.kylin_music_ver1.0_" + "mymusic.db"); if(oldVersion.exists()) { //读取旧版本数据库内容,并添加至新版本数据库函数 qDebug() << "存在旧版本数据库" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; } m_database.setDatabaseName(dirPath + "mymusic.db"); } MusicDataBase::~MusicDataBase() { qDebug() << "析构"; if(true == m_databaseOpenFlag) { sqlite3_shutdown(); m_database.close(); } } MusicDataBase* MusicDataBase::getInstance() { static MusicDataBase *dbInstance = nullptr; if (dbInstance == nullptr) { try { dbInstance = new MusicDataBase; } catch (const std::runtime_error &re) { qDebug() << "runtime_error:" << re.what(); } } return dbInstance; } int MusicDataBase::initDataBase() { if(!m_database.open()) { m_databaseOpenFlag = true; QMessageBox::warning(0, QObject::tr("Database Error"), m_database.lastError().text()); return DB_UNCONNECT; } QVariant v = m_database.driver()->handle(); // 获得低级handle包 if(v.isValid() && qstrcmp(v.typeName(), "sqlite3*") == 0) { sqlite3_initialize(); // 显式地初始化一下。 sqlite3 *m_handle = *static_cast(v.data()); if(m_handle) { sqlite3_enable_load_extension(m_handle, 1); //允许加载扩展 QSqlQuery loadExtension(m_database); bool loadRes = loadExtension.exec("SELECT load_extension('libsimple')"); // 使用sql函数加载libsimple if(!loadRes) { qDebug() << "无法加载分词器扩展" << loadExtension.lastError().text(); // 加载不成功先不return } } } bool queryRes = true; QSqlQuery queryInit(m_database); //新建表:总表,历史表,我喜欢表 queryRes &= queryInit.exec(QString("create table if not exists %1 (" "id integer primary key autoincrement," "idIndex integer unique," "filepath varchar unique not NULL," "title varchar," "singer varchar," "album varchar," "filetype varchar," "size varchar," "time varchar)" ).arg(ALLMUSIC));//创建音乐总表,自增id为主键,index为唯一值,插入歌曲时为空,获取自增id值后赋值,filepath为唯一值且不为空。 queryRes &= queryInit.exec(QString("create table if not exists %1 (" "id integer primary key autoincrement," "idIndex integer unique," "filepath varchar unique not NULL," "title varchar," "singer varchar," "album varchar," "filetype varchar," "size varchar," "time varchar)" ).arg(HISTORY));//创建历史播放列表,自增id为主键,index为唯一值,插入歌曲时为空,获取自增id值后赋值,filepath为唯一值且不为空。 queryRes &= queryInit.exec(QString("create table if not exists ListOfPlayList (title varchar primary key)"));//创建播放列表名称列表 queryRes &= queryInit.exec(QString("create virtual table if not exists AuxIndexLocalMusicContent" " using fts5(id UNINDEXED, title, singer, album, filepath UNINDEXED, time UNINDEXED, tokenize='simple', prefix='1 2 3 4 5')")); // 为localMusic本地音乐表创建辅助的全文索引虚拟表fts5,设置前缀索引,设置非索引项,加载simple分词器包。 // queryRes &= queryInit.exec(QString("create trigger local_music_add after insert on LocalMusic begin" // " insert into AuxIndexLocalMusicContent values(new.id, new.title); end")); // 存在问题:利用触发器插入时需要额外操作(包括对新值处理从中文到拼音、从编码格式到不编码格式)。即便表中存储的并非base64,我们也需要处理从中文到拼音,包括取old变量值到转换, // 废弃方案 queryRes &= queryInit.exec(QString("create trigger if not exists local_music_delete before delete on LocalMusic begin" " delete from AuxIndexLocalMusicContent where id=old.id;" " end")); // 创建触发器,根据id删除虚拟表记录 if(true == queryRes) { qDebug()<<"本地列表,历史列表,歌单表创建成功!"; //创建我喜欢列表 //先检查是否存在 int checkRes; checkRes = checkPlayListExist(FAV); if(LIST_NOT_FOUND == checkRes)//我喜欢列表不存在才创建 { int createRes; createRes = createNewPlayList(FAV); return createRes; } else { return checkRes; } } else { qDebug() << "初始化建表失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return CREATE_TABLE_FAILED; } } int MusicDataBase::addMusicToLocalMusic(const musicDataStruct &fileData) { bool queryRes = true; if(fileData.title.isEmpty() || fileData.filepath.isEmpty()) { qDebug() << "无效入参" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } else { if(true == m_database.isValid()) { //查询历史列表中是否已有该歌曲,已有的话,返回添加失败 int checkLocalRes = checkIfSongExistsInLocalMusic(fileData.filepath); //历史列表中已经有这首歌,重复添加了 if(DB_OP_SUCC == checkLocalRes) { qDebug() << "添加失败,重复添加" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_ADD_REPEAT; } QSqlQuery addSongToLocal(m_database); QString addSongString = QString("insert into %1 (%2) values('%3','%4','%5','%6','%7','%8','%9')"). arg(ALLMUSIC). arg(SHOWCONTEXTS). arg(inPutStringHandle(fileData.filepath)). arg(inPutStringHandle(fileData.title)). arg(inPutStringHandle(fileData.singer)). arg(inPutStringHandle(fileData.album)). arg(inPutStringHandle(fileData.filetype)). arg(inPutStringHandle(fileData.size)). arg(inPutStringHandle(fileData.time)); queryRes &= addSongToLocal.exec(addSongString); //插入歌曲时自增id和idIndex无法赋值,插入后取得自增id,给idIndex赋值 int tempIndex = addSongToLocal.lastInsertId().toInt(); bool setRes = true; QSqlQuery setSongIDFromLocal(m_database); QString setIndex = QString("update %1 set idIndex='%2' WHERE filepath='%3'") .arg(ALLMUSIC) .arg(tempIndex) .arg(inPutStringHandle(fileData.filepath)); setRes &= setSongIDFromLocal.exec(setIndex); // 每插入一条记录,都要更新辅助的虚拟表AuxIndexLocalMusicContent QSqlQuery addSongToIndexTable(m_database); QString addSongIndex = QString("insert into AuxIndexLocalMusicContent(id,title,singer,album,filepath,time) values('%1', '%2', '%3', '%4', '%5', '%6')") .arg(tempIndex) .arg(preHandle(fileData.title), preHandle(fileData.singer), preHandle(fileData.album), preHandle(fileData.filepath), preHandle(fileData.time)); bool setVTableRes = addSongToIndexTable.exec(addSongIndex); if(true == (queryRes&setRes&setVTableRes)) { return DB_OP_SUCC; } else { qDebug() << "数据库操作失败,添加失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_ADD_FAILED; } } else { return DB_UNCONNECT; } } //如果是先添加至新建列表 //TODO //添加至新建列表 } int MusicDataBase::delMusicFromLocalMusic(const QString& filePath) { bool queryRes = true; if(filePath.isEmpty()) { qDebug() << "无效入参" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } else { if(true == m_database.isValid()) { int checkRes = checkIfSongExistsInLocalMusic(filePath); if(checkRes == DB_OP_SUCC) { QSqlQuery delSongFromLocal(m_database); QString delSongString = QString("delete from %1 where filepath = '%2'"). arg(ALLMUSIC). arg(inPutStringHandle(filePath)); queryRes = delSongFromLocal.exec(delSongString); if(true == queryRes) { return DB_OP_SUCC; } else { qDebug() << "数据库打开,删除失败!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_DEL_FAILED; } } else { return INVALID_INPUT; } } else { qDebug() << "数据库无法打开,请重试!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_UNCONNECT; } } } int MusicDataBase::createNewPlayList(const QString& playListName) { if(playListName.isEmpty()) { qDebug() << "无效入参" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } if(true == m_database.isValid()) { bool createRes = true; // QMutexLocker lockData( &m_mutex); //加锁,函数执行完后自动解锁 QSqlQuery createNewPlayList(m_database); QString createPlayListString = QString("create table if not exists 'playlist_%1' (" "id integer primary key autoincrement," "idIndex integer unique," "filepath varchar unique not NULL," "title varchar," "singer varchar," "album varchar," "filetype varchar," "size varchar," "time varchar)"). arg(inPutStringHandle(playListName));//创建新建播放列表,自增id为主键,index为唯一值,插入歌曲时为空,获取自增id值后赋值,filepath为唯一值且不为空。 createRes &= createNewPlayList.exec(createPlayListString); if(true != createRes) { return CREATE_TABLE_FAILED; } QSqlQuery addPlayListToList(m_database); QString addPlayListToListString = QString("insert into ListOfPlayList (title) values('%1')"). arg(inPutStringHandle(playListName)); createRes &= addPlayListToList.exec(addPlayListToListString); if(true == createRes) { return DB_OP_SUCC; } else { qDebug() << "创建新歌单失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return CREATE_TABLE_FAILED; } } else { return DB_UNCONNECT; } } int MusicDataBase::delPlayList(const QString& playListName) { //入参检查 if(playListName.isEmpty() || playListName == FAV) { qDebug() << "无效入参" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } if(true == m_database.isValid()) { bool delRes = true; QSqlQuery delPlayListFromList(m_database); QString delPlayListFromListString = QString("delete from ListOfPlayList where title = '%1'"). arg(inPutStringHandle(playListName)); delRes &= delPlayListFromList.exec(delPlayListFromListString); QSqlQuery delPlayList(m_database); QString delPlayListString = QString("drop table if exists 'playlist_%1'" ).arg(inPutStringHandle(playListName)); delRes &= delPlayList.exec(delPlayListString); if(true == delRes) { return DB_OP_SUCC; } else { return DEL_TABLE_FAILED; } } else { return DB_UNCONNECT; } } int MusicDataBase::getSongInfoFromLocalMusic(const QString& filePath, musicDataStruct &fileData) { if(filePath.isEmpty()) { qDebug() << "入参无效" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } if(true == m_database.isValid()) { bool getRes = true; QSqlQuery getSongFromLocalMusic(m_database); QString getSongString = QString("select %1 from %2 where filepath = '%3'") .arg(SHOWCONTEXTS) .arg(ALLMUSIC) .arg(inPutStringHandle(filePath)); getRes = getSongFromLocalMusic.exec(getSongString); if(true == getRes) { if(getSongFromLocalMusic.next()) { fileData.filepath = outPutStringHandle(getSongFromLocalMusic.value(0).toString()); fileData.title = outPutStringHandle(getSongFromLocalMusic.value(1).toString()); fileData.singer = outPutStringHandle(getSongFromLocalMusic.value(2).toString()); fileData.album = outPutStringHandle(getSongFromLocalMusic.value(3).toString()); fileData.filetype = outPutStringHandle(getSongFromLocalMusic.value(4).toString()); fileData.size = outPutStringHandle(getSongFromLocalMusic.value(5).toString()); fileData.time = outPutStringHandle(getSongFromLocalMusic.value(6).toString()); } else { qDebug() << "数据库中查无此歌!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_DISORDERD; } return DB_OP_SUCC; } else { return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } int MusicDataBase::getSongInfoListFromLocalMusic(QList& resList) { if(true == m_database.isValid()) { bool getRes = true; QSqlQuery getSongListFromLocalMusic(m_database); QString getSongListString = QString("select filepath from %1 order by idIndex").arg(ALLMUSIC);//按index排序返回给前端,而非添加歌曲时的顺序 getRes = getSongListFromLocalMusic.exec(getSongListString); if(true == getRes) { while(getSongListFromLocalMusic.next()) { musicDataStruct temp; temp.filepath = outPutStringHandle(getSongListFromLocalMusic.value(0).toString()); int curRes = getSongInfoFromLocalMusic(temp.filepath, temp); if(DB_OP_SUCC == curRes) { resList.append(temp); } else { return curRes; } } return DB_OP_SUCC; } else { return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } int MusicDataBase::changeSongOrderInLocalMusic(const QString& selectFilePath, const QString& destinationFilePath) { //入参检查 if(selectFilePath.isEmpty() || destinationFilePath.isEmpty()) { qDebug() << "入参为空" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT;//入参为空 } if(DB_OP_SUCC != checkIfSongExistsInLocalMusic(selectFilePath) || DB_OP_SUCC != checkIfSongExistsInLocalMusic(destinationFilePath)) { qDebug() << "歌曲在歌曲总表中不存在" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT;//歌曲在歌曲总表中不存在 } if(selectFilePath == destinationFilePath)//位置没变化 { qDebug() << "位置没变化,不用处理" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_SUCC;//位置没变化,不用处理 } //获取歌曲的index信息 int oldIndex = 0, newIndex = 0; int getRes1 = getSongIndexFromLocalMusic(selectFilePath, oldIndex); int getRes2 = getSongIndexFromLocalMusic(destinationFilePath, newIndex); if(getRes1 != DB_OP_SUCC) { qDebug() << "歌曲原INDEX获取失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return getRes1; } if(getRes2 != DB_OP_SUCC) { qDebug() << "目标歌曲INDEX获取失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return getRes2; } if(oldIndex < newIndex)//靠前的位置拖拽到了靠后的位置 { bool setRes = true; QSqlQuery setSongIndexFromLocal(m_database); QString setIndex = QString("update %1 set idIndex=0 WHERE filepath='%2'") .arg(ALLMUSIC) .arg(inPutStringHandle(selectFilePath)); setRes &= setSongIndexFromLocal.exec(setIndex); if(true == setRes) { bool getRes = true; QSqlQuery getBetweenSongIndexFromLocal(m_database); QString setIndexs = QString("select idIndex,filepath from %1 WHERE idIndex between '%2' and '%3'") .arg(ALLMUSIC) .arg(oldIndex) .arg(newIndex); getRes &= getBetweenSongIndexFromLocal.exec(setIndexs); if(true == getRes) { bool updateIndexRes = true; while(getBetweenSongIndexFromLocal.next()) { int tempIndex = getBetweenSongIndexFromLocal.value(0).toInt(); QString tempFilepath = outPutStringHandle(getBetweenSongIndexFromLocal.value(1).toString()); QSqlQuery updateSongIndexFromLocal(m_database); QString updateIndex = QString("update %1 set idIndex='%2' WHERE filepath='%3'") .arg(ALLMUSIC) .arg(tempIndex-1) .arg(inPutStringHandle(tempFilepath)); updateIndexRes &= updateSongIndexFromLocal.exec(updateIndex); } if(true == updateIndexRes) { bool setRes2 = true; QSqlQuery setSongIndexFromLocal2(m_database); QString setIndex2 = QString("update %1 set idIndex='%2' WHERE filepath='%3'") .arg(ALLMUSIC) .arg(newIndex) .arg(inPutStringHandle(selectFilePath)); setRes2 &= setSongIndexFromLocal2.exec(setIndex2); if(true == setRes2) { return DB_OP_SUCC; } else { return LIST_REORDER_ERR; } } else { return LIST_REORDER_ERR; } } } } else if(oldIndex > newIndex)//靠后的位置拖拽到了靠前的位置 { bool setRes = true; QSqlQuery setSongIndexFromLocal(m_database); QString setIndex = QString("update %1 set idIndex=0 WHERE filepath='%2'") .arg(ALLMUSIC) .arg(inPutStringHandle(selectFilePath)); setRes &= setSongIndexFromLocal.exec(setIndex); if(true == setRes) { bool getRes = true; QSqlQuery getBetweenSongIndexFromLocal(m_database); QString setIndexs = QString("select idIndex,filepath from %1 WHERE idIndex between '%2' and '%3' order by idIndex desc") .arg(ALLMUSIC) .arg(newIndex+1) .arg(oldIndex); getRes &= getBetweenSongIndexFromLocal.exec(setIndexs); if(true == getRes) { bool updateIndexRes = true; while(getBetweenSongIndexFromLocal.next()) { int tempIndex = getBetweenSongIndexFromLocal.value(0).toInt(); QString tempFilepath = outPutStringHandle(getBetweenSongIndexFromLocal.value(1).toString()); QSqlQuery updateSongIndexFromLocal(m_database); QString updateIndex = QString("update %1 set idIndex='%2' WHERE filepath='%3'") .arg(ALLMUSIC) .arg(tempIndex+1) .arg(inPutStringHandle(tempFilepath)); updateIndexRes &= updateSongIndexFromLocal.exec(updateIndex); } if(true == updateIndexRes) { bool setRes2 = true; QSqlQuery setSongIndexFromLocal2(m_database); QString setIndex2 = QString("update %1 set idIndex='%2' WHERE filepath='%3'") .arg(ALLMUSIC) .arg(newIndex+1) .arg(inPutStringHandle(selectFilePath)); setRes2 &= setSongIndexFromLocal2.exec(setIndex2); if(true == setRes2) { return DB_OP_SUCC; } else { return LIST_REORDER_ERR; } } else { return LIST_REORDER_ERR; } } } } else//位置没变 { return DB_OP_SUCC; } } int MusicDataBase::getSongInfoListFromLocalMusicByKeyword(QList &resList, const QString &keyword) { // ph-code if(true == keyword.isEmpty()) { // 空keyword不处理 return INVALID_INPUT; } if(true == m_database.isValid()) { bool getRes = true; QSqlQuery getSongListFromLocalMusicByKeyword(m_database); // QString getSongListStringByKeyword = QString("select * from LocalMusic where `id` in (" // "select id from AuxIndexLocalMusicContent where AuxIndexLocalMusicContent match simple_query('%1') order by rank) ") // .arg(keyword); // 直接在虚表中存取,不再根据id回表。 QString getSongListStringByKeyword = QString("select * from AuxIndexLocalMusicContent" " where AuxIndexLocalMusicContent match simple_query('%1') order by rank") .arg(keyword); getRes = getSongListFromLocalMusicByKeyword.exec(getSongListStringByKeyword); if(true == getRes) { while(getSongListFromLocalMusicByKeyword.next()) { musicDataStruct temp; temp.title = getSongListFromLocalMusicByKeyword.value(1).toString(); temp.singer = getSongListFromLocalMusicByKeyword.value(2).toString(); temp.album = getSongListFromLocalMusicByKeyword.value(3).toString(); temp.filepath = getSongListFromLocalMusicByKeyword.value(4).toString(); temp.time = getSongListFromLocalMusicByKeyword.value(5).toString(); resList.append(temp); } return DB_OP_SUCC; } else { qDebug() << "执行错误信息:" << getSongListFromLocalMusicByKeyword.lastError().text(); return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } int MusicDataBase::getCurtEstimatedListByKeyword(const QString& keyword, int Number, QList& titleSongsList, QList& singersList, QList& albumsList) { // qDebug() << "\n" << "====================" << "\n" // << keyword // << Number // << "\n" << "====================" << "\n"; // Number = 99; if(true == keyword.isEmpty()) { // 空keyword不处理 return INVALID_INPUT; } if(true == m_database.isValid()) { bool getTitle = true; bool getSinger = true; bool getAlbum = true; QSqlQuery getCurtEstimatedTitleList(m_database); QString getCurtEstimatedTitleListString = QString("select * from AuxIndexLocalMusicContent" " where title match simple_query('%1') order by rank limit %2") .arg(keyword, QString::number(Number)); getTitle &= getCurtEstimatedTitleList.exec(getCurtEstimatedTitleListString); QSqlQuery getCurtEstimatedSingerList(m_database); QString getCurtEstimatedSingerListString = QString("select * from AuxIndexLocalMusicContent" " where singer match simple_query('%1') order by rank limit %2") .arg(keyword, QString::number(Number)); getSinger &= getCurtEstimatedSingerList.exec(getCurtEstimatedSingerListString); QSqlQuery getCurtEstimatedAlbumList(m_database); QString getCurtEstimatedAlbumListString = QString("select * from AuxIndexLocalMusicContent" " where album match simple_query('%1') order by rank limit %2") .arg(keyword, QString::number(Number)); getAlbum &= getCurtEstimatedAlbumList.exec(getCurtEstimatedAlbumListString); if(getTitle) { while(getCurtEstimatedTitleList.next()) { musicDataStruct temp; temp.title = getCurtEstimatedTitleList.value(1).toString(); temp.singer = getCurtEstimatedTitleList.value(2).toString(); temp.album = getCurtEstimatedTitleList.value(3).toString(); temp.filepath = getCurtEstimatedTitleList.value(4).toString(); temp.time = getCurtEstimatedTitleList.value(5).toString(); titleSongsList.append(temp); } } else { qDebug() << "执行错误信息:" << getCurtEstimatedTitleList.lastError().text(); qDebug() << getCurtEstimatedTitleList.lastQuery(); } if(getSinger) { while(getCurtEstimatedSingerList.next()) { musicDataStruct temp; temp.title = getCurtEstimatedSingerList.value(1).toString(); temp.singer = getCurtEstimatedSingerList.value(2).toString(); temp.album = getCurtEstimatedSingerList.value(3).toString(); temp.filepath = getCurtEstimatedSingerList.value(4).toString(); temp.time = getCurtEstimatedSingerList.value(5).toString(); singersList.append(temp); } } else { qDebug() << "执行错误信息:" << getCurtEstimatedSingerList.lastError().text(); } if(getAlbum) { while(getCurtEstimatedAlbumList.next()) { musicDataStruct temp; temp.title = getCurtEstimatedAlbumList.value(1).toString(); temp.singer = getCurtEstimatedAlbumList.value(2).toString(); temp.album = getCurtEstimatedAlbumList.value(3).toString(); temp.filepath = getCurtEstimatedAlbumList.value(4).toString(); temp.time = getCurtEstimatedAlbumList.value(5).toString(); albumsList.append(temp); } } else { qDebug() << "执行错误信息:" << getCurtEstimatedAlbumList.lastError().text(); } if(getTitle && getSinger && getAlbum) { return DB_OP_SUCC; } else { return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } int MusicDataBase::getSongInfoListByAlbum(QList &resList, const QString &album) { if(true == album.isEmpty()) { return INVALID_INPUT; } if(true == m_database.isValid()) { bool getRes = true; QSqlQuery getSongsListByAlbum(m_database); QString getSongsListByAlbumString = QString("select * from LocalMusic where album = '%1'").arg(inPutStringHandle(album)); getRes = getSongsListByAlbum.exec(getSongsListByAlbumString); if(true == getRes) { while(getSongsListByAlbum.next()) { musicDataStruct fileData; fileData.filepath = outPutStringHandle(getSongsListByAlbum.value(2).toString()); fileData.title = outPutStringHandle(getSongsListByAlbum.value(3).toString()); fileData.singer = outPutStringHandle(getSongsListByAlbum.value(4).toString()); fileData.album = outPutStringHandle(getSongsListByAlbum.value(5).toString()); fileData.filetype = outPutStringHandle(getSongsListByAlbum.value(6).toString()); fileData.size = outPutStringHandle(getSongsListByAlbum.value(7).toString()); fileData.time = outPutStringHandle(getSongsListByAlbum.value(8).toString()); resList.append(fileData); } return DB_OP_SUCC; } else { qDebug() << "执行错误信息:" << getSongsListByAlbum.lastError().text(); return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } int MusicDataBase::getSongInfoListBySinger(QList &resList, const QString &singer) { if(true == singer.isEmpty()) { return INVALID_INPUT; } if(true == m_database.isValid()) { bool getRes = true; QSqlQuery getSongsListBySinger(m_database); QString getSongsListBySingerString = QString("select * from LocalMusic where singer = '%1'").arg(inPutStringHandle(singer)); getRes = getSongsListBySinger.exec(getSongsListBySingerString); if(true == getRes) { while(getSongsListBySinger.next()) { musicDataStruct fileData; fileData.filepath = outPutStringHandle(getSongsListBySinger.value(2).toString()); fileData.title = outPutStringHandle(getSongsListBySinger.value(3).toString()); fileData.singer = outPutStringHandle(getSongsListBySinger.value(4).toString()); fileData.album = outPutStringHandle(getSongsListBySinger.value(5).toString()); fileData.filetype = outPutStringHandle(getSongsListBySinger.value(6).toString()); fileData.size = outPutStringHandle(getSongsListBySinger.value(7).toString()); fileData.time = outPutStringHandle(getSongsListBySinger.value(8).toString()); resList.append(fileData); } return DB_OP_SUCC; } else { qDebug() << "执行错误信息:" << getSongsListBySinger.lastError().text(); return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } int MusicDataBase::getSongInfoFromPlayList(musicDataStruct &fileData, const QString& filePath,const QString& playListName) { if(filePath.isEmpty() || playListName.isEmpty()) { qDebug() << "无效入参" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } if(true == m_database.isValid()) { int checkRes = checkPlayListExist(playListName); if(checkRes == DB_OP_SUCC) { bool getRes = true; QSqlQuery getplayList(m_database); QString getplayListString = QString("select title from ListOfPlayList where title = '%1'"). arg(inPutStringHandle(playListName)); getRes = getplayList.exec(getplayListString); if(false == getRes) { qDebug() << "歌单表中查询歌单失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_GET_FAILED; } if(getplayList.next()) { QSqlQuery getSongFromPlayList(m_database); QString getSongFromPlayListString = QString("select %1 from 'playlist_%2' where filepath = '%3'") .arg(SHOWCONTEXTS) .arg(inPutStringHandle(playListName)) .arg(inPutStringHandle(filePath)); getRes = getSongFromPlayList.exec(getSongFromPlayListString); if(false == getRes) { return DB_OP_GET_FAILED; } if(getSongFromPlayList.next()) { fileData.filepath = outPutStringHandle(getSongFromPlayList.value(0).toString()); fileData.title = outPutStringHandle(getSongFromPlayList.value(1).toString()); fileData.singer = outPutStringHandle(getSongFromPlayList.value(2).toString()); fileData.album = outPutStringHandle(getSongFromPlayList.value(3).toString()); fileData.filetype = outPutStringHandle(getSongFromPlayList.value(4).toString()); fileData.size = outPutStringHandle(getSongFromPlayList.value(5).toString()); fileData.time = outPutStringHandle(getSongFromPlayList.value(6).toString()); } else { qDebug() << "歌单中未查询到歌曲" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_GET_FAILED; } return DB_OP_SUCC; } else { qDebug() << "歌单表中未查询到该歌单" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return PLAYLIST_UNFOUND; } } else { return checkRes; } } else { return DB_UNCONNECT; } } int MusicDataBase::getSongInfoListFromPlayList(QList& resList,const QString& playListName) { if(playListName.isEmpty()) { qDebug() << "输入歌单名为空" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } if(true == m_database.isValid()) { int checkRes = checkPlayListExist(playListName); if(checkRes == DB_OP_SUCC) { bool getRes = true; resList.clear(); QSqlQuery getplayList(m_database); QString getplayListString = QString("select title from ListOfPlayList where title = '%1'"). arg(inPutStringHandle(playListName)); getRes = getplayList.exec(getplayListString); if(false == getRes) { qDebug() << "歌单表中查询歌单失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_GET_FAILED; } if(getplayList.next()) { QSqlQuery getSongFromPlayList(m_database); QString getSongFromPlayListString = QString("select %1 from 'playlist_%2' order by idIndex") .arg(SHOWCONTEXTS) .arg(inPutStringHandle(playListName));//按index排序返回给前端,而非添加歌曲时的顺序 getRes = getSongFromPlayList.exec(getSongFromPlayListString); if(true == getRes) { while(getSongFromPlayList.next()) { musicDataStruct temp; temp.filepath = outPutStringHandle(getSongFromPlayList.value(0).toString()); temp.title = outPutStringHandle(getSongFromPlayList.value(1).toString()); temp.singer = outPutStringHandle(getSongFromPlayList.value(2).toString()); temp.album = outPutStringHandle(getSongFromPlayList.value(3).toString()); temp.filetype = outPutStringHandle(getSongFromPlayList.value(4).toString()); temp.size = outPutStringHandle(getSongFromPlayList.value(5).toString()); temp.time = outPutStringHandle(getSongFromPlayList.value(6).toString()); resList.append(temp); } return DB_OP_SUCC; } else { return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } else { return checkRes; } } else { return DB_DISORDERD; } } int MusicDataBase::changeSongOrderInPlayList(const QString& selectFilePath, const QString& destinationFilePath, const QString& playListName) { //入参检查 if(selectFilePath.isEmpty() || destinationFilePath.isEmpty() || playListName.isEmpty()) { qDebug() << "入参为空" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT;//入参为空 } if(DB_OP_SUCC != checkPlayListExist(playListName)) { qDebug() << "歌单在歌单表中不存在" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT;//歌单在歌单表中不存在 } if(DB_OP_SUCC != checkIfSongExistsInPlayList(selectFilePath, playListName) || DB_OP_SUCC != checkIfSongExistsInPlayList(destinationFilePath, playListName)) { qDebug() << "歌曲在歌单表中不存在" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT;//歌曲在歌单表中不存在 } if(selectFilePath == destinationFilePath)//位置没变化 { qDebug() << "位置没变化,不用处理" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_SUCC;//位置没变化,不用处理 } //获取歌曲的index信息 int oldIndex = 0, newIndex = 0; int getRes1 = getSongIndexFromPlayList(selectFilePath, playListName, oldIndex); int getRes2 = getSongIndexFromPlayList(destinationFilePath, playListName, newIndex); if(getRes1 != DB_OP_SUCC) { qDebug() << "歌曲原INDEX获取失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return getRes1; } if(getRes2 != DB_OP_SUCC) { qDebug() << "目标歌曲INDEX获取失败" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return getRes2; } if(oldIndex < newIndex)//靠前的位置拖拽到了靠后的位置 { bool setRes = true; QSqlQuery setSongIndexFromLocal(m_database); QString setIndex = QString("update 'playlist_%1' set idIndex=0 WHERE filepath='%2'") .arg(inPutStringHandle(playListName)) .arg(inPutStringHandle(selectFilePath)); setRes &= setSongIndexFromLocal.exec(setIndex); if(true == setRes) { bool getRes = true; QSqlQuery getBetweenSongIndexFromLocal(m_database); QString setIndexs = QString("select idIndex,filepath from 'playlist_%1' WHERE idIndex between '%2' and '%3'") .arg(inPutStringHandle(playListName)).arg(oldIndex).arg(newIndex); getRes &= getBetweenSongIndexFromLocal.exec(setIndexs); if(true == getRes) { bool updateIndexRes = true; while(getBetweenSongIndexFromLocal.next()) { int tempIndex = getBetweenSongIndexFromLocal.value(0).toInt(); QString tempFilepath = outPutStringHandle(getBetweenSongIndexFromLocal.value(1).toString()); QSqlQuery updateSongIndexFromLocal(m_database); QString updateIndex = QString("update 'playlist_%1' set idIndex='%2' WHERE filepath='%3'") .arg(inPutStringHandle(playListName)).arg(tempIndex-1).arg(inPutStringHandle(tempFilepath)); updateIndexRes &= updateSongIndexFromLocal.exec(updateIndex); } if(true == updateIndexRes) { bool setRes2 = true; QSqlQuery setSongIndexFromLocal2(m_database); QString setIndex2 = QString("update 'playlist_%1' set idIndex='%2' WHERE filepath='%3'") .arg(inPutStringHandle(playListName)).arg(newIndex).arg(inPutStringHandle(selectFilePath)); setRes2 &= setSongIndexFromLocal2.exec(setIndex2); if(true == setRes2) { return DB_OP_SUCC; } else { return LIST_REORDER_ERR; } } else { return LIST_REORDER_ERR; } } } } else if(oldIndex > newIndex)//靠后的位置拖拽到了靠前的位置 { bool setRes = true; QSqlQuery setSongIndexFromLocal(m_database); QString setIndex = QString("update 'playlist_%1' set idIndex=0 WHERE filepath='%2'") .arg(inPutStringHandle(playListName)).arg(inPutStringHandle(selectFilePath)); setRes &= setSongIndexFromLocal.exec(setIndex); if(true == setRes) { bool getRes = true; QSqlQuery getBetweenSongIndexFromLocal(m_database); QString setIndexs = QString("select idIndex,filepath from 'playlist_%1' WHERE idIndex between '%2' and '%3' order by idIndex desc") .arg(inPutStringHandle(playListName)).arg(newIndex+1).arg(oldIndex); getRes &= getBetweenSongIndexFromLocal.exec(setIndexs); if(true == getRes) { bool updateIndexRes = true; while(getBetweenSongIndexFromLocal.next()) { int tempIndex = getBetweenSongIndexFromLocal.value(0).toInt(); QString tempFilepath = outPutStringHandle(getBetweenSongIndexFromLocal.value(1).toString()); QSqlQuery updateSongIndexFromLocal(m_database); QString updateIndex = QString("update 'playlist_%1' set idIndex='%2' WHERE filepath='%3'") .arg(inPutStringHandle(playListName)).arg(tempIndex+1).arg(inPutStringHandle(tempFilepath)); updateIndexRes &= updateSongIndexFromLocal.exec(updateIndex); } if(true == updateIndexRes) { bool setRes2 = true; QSqlQuery setSongIndexFromLocal2(m_database); QString setIndex2 = QString("update 'playlist_%1' set idIndex='%2' WHERE filepath='%3'") .arg(inPutStringHandle(playListName)).arg(newIndex+1).arg(inPutStringHandle(selectFilePath)); setRes2 &= setSongIndexFromLocal2.exec(setIndex2); if(true == setRes2) { return DB_OP_SUCC; } else { return LIST_REORDER_ERR; } } else { return LIST_REORDER_ERR; } } } } else//位置没变 { return DB_OP_SUCC; } } int MusicDataBase::addMusicToHistoryMusic(const QString& filePath) { if(filePath.isEmpty()) { return INVALID_INPUT; } else { musicDataStruct temp; if(true == m_database.isValid()) { bool queryRes = true; //检查歌曲在总表中是否存在 int checkLocalRes = getSongInfoFromLocalMusic(filePath, temp); if(DB_OP_SUCC != checkLocalRes) { return checkLocalRes; } //查询历史列表中是否已有该歌曲,已有的话,返回添加失败 int checkHistoryRes = checkIfSongExistsInHistoryMusic(filePath); //历史列表中没有这首歌,直接添加 if(SONG_NOT_FOUND == checkHistoryRes) { //历史列表中不存在该歌曲,添加该歌曲 QSqlQuery addSongToHistory(m_database); QString addSongString = QString("insert into %1 (%2) values('%3','%4','%5','%6','%7','%8','%9')"). arg(HISTORY). arg(SHOWCONTEXTS). arg(inPutStringHandle(temp.filepath)). arg(inPutStringHandle(temp.title)). arg(inPutStringHandle(temp.singer)). arg(inPutStringHandle(temp.album)). arg(inPutStringHandle(temp.filetype)). arg(inPutStringHandle(temp.size)). arg(inPutStringHandle(temp.time)); queryRes = addSongToHistory.exec(addSongString); //插入歌曲时自增id和idIndex无法赋值,插入后取得自增id,给idIndex赋值 int tempIndex = addSongToHistory.lastInsertId().toInt(); bool setRes = true; QSqlQuery setSongIDFromLocal(m_database); QString setIndex = QString("update %1 set idIndex='%2' WHERE filepath='%3'"). arg(HISTORY).arg(tempIndex).arg(inPutStringHandle(temp.filepath)); setRes &= setSongIDFromLocal.exec(setIndex); if(true == (queryRes&setRes)) { return DB_OP_SUCC; } else { qDebug() << "数据库打开,添加失败!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_ADD_FAILED; } } else//历史列表中已存在该,歌曲,需要先删除再添加 { int delHistoryRes = delMusicFromHistoryMusic(filePath); if(DB_OP_SUCC == delHistoryRes) { //历史列表中不存在该歌曲,添加该歌曲 QSqlQuery addSongToHistory(m_database); QString addSongString = QString("insert into %1 (%2) values('%3','%4','%5','%6','%7','%8','%9')"). arg(HISTORY). arg(SHOWCONTEXTS). arg(inPutStringHandle(temp.filepath)). arg(inPutStringHandle(temp.title)). arg(inPutStringHandle(temp.singer)). arg(inPutStringHandle(temp.album)). arg(inPutStringHandle(temp.filetype)). arg(inPutStringHandle(temp.size)). arg(inPutStringHandle(temp.time)); queryRes = addSongToHistory.exec(addSongString); //插入歌曲时自增id和idIndex无法赋值,插入后取得自增id,给idIndex赋值 int tempIndex = addSongToHistory.lastInsertId().toInt(); bool setRes = true; QSqlQuery setSongIDFromLocal(m_database); QString setIndex = QString("update %1 set idIndex='%2' WHERE filepath='%3'"). arg(HISTORY). arg(tempIndex). arg(inPutStringHandle(temp.filepath)); setRes &= setSongIDFromLocal.exec(setIndex); if(true == (queryRes&setRes)) { return DB_OP_SUCC; } else { qDebug() << "数据库打开,添加失败!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_ADD_FAILED; } } else { return delHistoryRes; } } } else { qDebug() << "数据库无法打开,请重试!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_UNCONNECT; } } } int MusicDataBase::delMusicFromHistoryMusic(const QString& filePath) { bool delRes = true; if(filePath.isEmpty()) { return INVALID_INPUT; } else { if(m_database.isValid()) { //查询历史列表中是否已有该歌曲,没有的话,返回删除失败 int checkHistoryRes = checkIfSongExistsInHistoryMusic(filePath); if(DB_OP_SUCC == checkHistoryRes) { QSqlQuery delSongFromHistoryPlayList(m_database); QString delSongString = QString("delete from %1 where filepath = '%2'"). arg(HISTORY). arg(inPutStringHandle(filePath)); delRes &= delSongFromHistoryPlayList.exec(delSongString); if(true == delRes) { return DB_OP_SUCC; } else { qDebug() << "数据库打开,删除失败!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_DEL_FAILED; } } else { qDebug() << "歌曲不存在!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return checkHistoryRes; } } else { qDebug() << "数据库无法打开,请重试!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_UNCONNECT; } } } int MusicDataBase::getSongInfoFromHistoryMusic(const QString& filePath, musicDataStruct &fileData) { bool getRes = true; if(filePath.isEmpty()) { return INVALID_INPUT; } else { if(true == m_database.isValid()) { int checkRes = checkIfSongExistsInHistoryMusic(filePath); if(checkRes == SONG_NOT_FOUND) { return SONG_NOT_FOUND; } else { QSqlQuery getSongInfoFromHistoryPlayList(m_database); QString getSongString = QString("select %1 from %2 where filepath = '%3'") .arg(SHOWCONTEXTS) .arg(HISTORY) .arg(inPutStringHandle(filePath)); getRes &= getSongInfoFromHistoryPlayList.exec(getSongString); if(false == getRes) { return DB_OP_GET_FAILED; } //判断是否找到了对应的歌曲 if(getSongInfoFromHistoryPlayList.next()) { fileData.filepath = outPutStringHandle(getSongInfoFromHistoryPlayList.value(0).toString()); fileData.title = outPutStringHandle(getSongInfoFromHistoryPlayList.value(1).toString()); fileData.singer = outPutStringHandle(getSongInfoFromHistoryPlayList.value(2).toString()); fileData.album = outPutStringHandle(getSongInfoFromHistoryPlayList.value(3).toString()); fileData.filetype = outPutStringHandle(getSongInfoFromHistoryPlayList.value(4).toString()); fileData.size = outPutStringHandle(getSongInfoFromHistoryPlayList.value(5).toString()); fileData.time = outPutStringHandle(getSongInfoFromHistoryPlayList.value(6).toString()); return DB_OP_SUCC; } else { return DB_DISORDERD; } } } else { qDebug() << "数据库无法打开,请重试!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_UNCONNECT; } } } //使用歌曲的path值,查询歌曲信息(因为所有歌曲都在本地歌单,所以只用歌曲路径) int MusicDataBase::getSongInfoFromDB(const QString& filePath, musicDataStruct &fileData) { bool getRes = true; if(filePath.isEmpty()) { qDebug() << "入参错误" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } else { getRes = getSongInfoFromLocalMusic(filePath, fileData); return getRes; } } int MusicDataBase::getSongInfoListFromDB(QList& resList,const QString& playListName) { bool getRes = true; if(playListName.isEmpty()) { qDebug() << "输入歌单名为空" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } else { if(ALLMUSIC == playListName) { getRes = getSongInfoListFromLocalMusic(resList); } else if(HISTORY == playListName) { getRes = getSongInfoListFromHistoryMusic(resList); } else if(FAV == playListName) { getRes = getSongInfoListFromPlayList(resList,FAV); } else { getRes = getSongInfoListFromPlayList(resList,playListName); } return getRes; } } bool MusicDataBase::checkSongIsInFav(const QString& filePath) { int isExist = OUT_OF_RESULT; isExist = checkIfSongExistsInPlayList(filePath, FAV); if(isExist == DB_OP_SUCC) { return true; } else { return false; } } int MusicDataBase::getSongInfoListFromHistoryMusic(QList& resList) { if(true == m_database.isValid()) { bool getRes = true; QSqlQuery getSongListFromHistoryMusic(m_database); QString getSongListString = QString("select filepath from %1 order by idIndex desc").arg(HISTORY); getRes = getSongListFromHistoryMusic.exec(getSongListString); if(true == getRes) { while(getSongListFromHistoryMusic.next()) { musicDataStruct temp; temp.filepath = outPutStringHandle(getSongListFromHistoryMusic.value(0).toString()); int curRes = getSongInfoFromHistoryMusic(temp.filepath, temp); if(DB_OP_SUCC == curRes) { resList.append(temp); } else { return curRes; } } return DB_OP_SUCC; } else { return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } //清空历史歌单 int MusicDataBase::emptyHistoryMusic() { if(true == m_database.isValid()) { bool delRes = true; QSqlQuery delPlayList(m_database); QString delPlayListString = QString("DROP TABLE %1").arg(HISTORY); delRes &= delPlayList.exec(delPlayListString); if(true == delRes) { bool queryRes = true; QSqlQuery queryInit(m_database); queryRes &= queryInit.exec(QString("create table if not exists %1 (" "id integer primary key autoincrement," "idIndex integer unique," "filepath varchar unique not NULL," "title varchar," "singer varchar," "album varchar," "filetype varchar," "size varchar," "time varchar)" ).arg(HISTORY));//创建历史播放列表,自增id为主键,index为唯一值,插入歌曲时为空,获取自增id值后赋值,filepath为唯一值且不为空。 if(true == queryRes) { return DB_OP_SUCC; } else { return CREATE_HIS_FAILED; } } else { return EMPTYT_HIS_FAILED; } } else { qDebug() << "数据库无法打开,请重试!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_UNCONNECT; } } int MusicDataBase::checkIfSongExistsInLocalMusic(const QString& filePath) { bool queryRes = true; QString filePathHash = inPutStringHandle(filePath); QSqlQuery getSongFromLocalMusic(m_database); QString getSongString = QString("select id from %1 where filepath = '%2'") .arg(ALLMUSIC) .arg(filePathHash); queryRes = getSongFromLocalMusic.exec(getSongString); if(false == queryRes) { return DB_OP_GET_FAILED; } //判断总表中是否存在此歌曲,不存在返回错误! if(getSongFromLocalMusic.next()) { return DB_OP_SUCC; } else { qDebug() << "歌曲未找到" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return SONG_NOT_FOUND; } } int MusicDataBase::checkIfSongExistsInHistoryMusic(const QString& filePath) { bool queryRes = true; QString filePathHash = inPutStringHandle(filePath); QSqlQuery getSongFromHistoryMusic(m_database); QString getSongString = QString("select id from %1 where filepath = '%2'") .arg(HISTORY) .arg(filePathHash); queryRes = getSongFromHistoryMusic.exec(getSongString); if(false == queryRes) { return DB_OP_GET_FAILED; } //判断历史歌单中是否存在此歌曲,不存在返回错误! if(getSongFromHistoryMusic.next()) { return DB_OP_SUCC; } else { //歌曲未找到 return SONG_NOT_FOUND; } } int MusicDataBase::checkIfSongExistsInPlayList(const QString& filePath, const QString& playListName) { if(filePath.isEmpty() || playListName.isEmpty()) { return INVALID_INPUT; } QString filePathHash = inPutStringHandle(filePath); QString playListNameHash = inPutStringHandle(playListName); int checkRes = checkPlayListExist(playListName); if(DB_OP_SUCC == checkRes) { bool queryRes = true; QSqlQuery getSongFromPlayListMusic(m_database); QString getSongString = QString("select id from 'playlist_%1' where filepath = '%2'").arg(playListNameHash).arg(filePathHash); queryRes = getSongFromPlayListMusic.exec(getSongString); if(false == queryRes) { return DB_OP_GET_FAILED; } //判断歌曲列表中是否存在此歌曲,不存在返回错误! if(getSongFromPlayListMusic.next()) { return DB_OP_SUCC; } else { //歌曲不存在 return SONG_NOT_FOUND; } } else { return checkRes; } } int MusicDataBase::getPlayList(QStringList& playListNameList) { bool getRes = true; if(true == m_database.isValid()) { // QMutexLocker lockData( &m_mutex); //加锁,函数执行完后自动解锁 QSqlQuery getplayListFromLocalMusic(m_database); QString getplayListFromLocalMusicString = QString("select title from ListOfPlayList"); getRes = getplayListFromLocalMusic.exec(getplayListFromLocalMusicString); if(false == getRes) { return DB_OP_GET_FAILED; } while(getplayListFromLocalMusic.next()) { playListNameList.append(outPutStringHandle(getplayListFromLocalMusic.value(0).toString())); } return DB_OP_SUCC; } else { return DB_DISORDERD; } } int MusicDataBase::renamePlayList(const QString& oldPlayListName, const QString& newPlayListName) { if(oldPlayListName.isEmpty() || newPlayListName.isEmpty()) { return INVALID_INPUT; } if(FAV == oldPlayListName || FAV == newPlayListName) { return INVALID_INPUT; } int oldCheckRes = checkPlayListExist(oldPlayListName); if(DB_OP_SUCC == oldCheckRes) { int newCheckRes = checkPlayListExist(newPlayListName); if(LIST_NOT_FOUND == newCheckRes) { //sql支持table重命名 bool queryRes = true; QSqlQuery queryRename(m_database); //重命名歌单 QString renameListString = QString("ALTER TABLE 'playlist_%1' RENAME TO 'playlist_%2'"). arg(inPutStringHandle(oldPlayListName)). arg(inPutStringHandle(newPlayListName)); queryRes &= queryRename.exec(renameListString); if(true == queryRes) { //要把歌单列表中的对应旧歌单名删除,添加新歌单名 int createRes = createNewPlayList(newPlayListName); if(DB_OP_SUCC == createRes) { int delRes = delPlayList(oldPlayListName); if(DB_OP_SUCC == delRes) { return DB_OP_SUCC;//歌单名表名和歌单列表中的名称全部改变 } else { return LIST_RENAME_ERR;//歌单名已改变,但歌单表中同时存在新歌单名和旧歌单名 } } else { return LIST_RENAME_ERR;//歌单名已改变,但歌单表中名未改变 } } else { return LIST_RENAME_FAILED; } } else { if(DB_OP_SUCC == newCheckRes) { return LIST_EXISTS; } else { return newCheckRes; } } } else { return oldCheckRes; } } int MusicDataBase::checkPlayListExist(const QString& playListName) { bool getRes = true; QStringList playListNameList; if(m_database.isValid()) { QSqlQuery getplayListFromLocalMusic(m_database); QString getplayListFromLocalMusicString = QString("select title from ListOfPlayList"); getRes = getplayListFromLocalMusic.exec(getplayListFromLocalMusicString); if(false == getRes) { return DB_OP_GET_FAILED; } playListNameList.clear(); while(getplayListFromLocalMusic.next()) { playListNameList.append(outPutStringHandle(getplayListFromLocalMusic.value(0).toString())); } if(playListNameList.contains(playListName)) { return DB_OP_SUCC; } else { //该列表不存在 return LIST_NOT_FOUND; } } else { return DB_DISORDERD; } } //添加歌曲到新建歌单,使用歌曲的歌名值,歌单名值,输入数据必须有效, int MusicDataBase::addMusicToPlayList(const QString& filePath,const QString& playListName) { if(filePath.isEmpty() || playListName.isEmpty()) { return INVALID_INPUT; } int checkRes; checkRes = checkPlayListExist(playListName); if(DB_OP_SUCC == checkRes) { int checkLocalRes = checkIfSongExistsInLocalMusic(filePath); if(DB_OP_SUCC == checkLocalRes) { musicDataStruct temp; if(true == m_database.isValid()) { //从总表中添加的,故要从总表中取歌曲信息!!! //TODO缺少直接添加文件到播放列表的接口 int getLocalRes = getSongInfoFromLocalMusic(filePath, temp); if(DB_OP_SUCC != getLocalRes) { return getLocalRes; } //查询歌单列表中是否已有该歌曲,已有的话,返回添加失败 int checkPlayListRes = checkIfSongExistsInPlayList(temp.filepath,playListName); //歌单列表中已经有这首歌,重复添加了 if(DB_OP_SUCC == checkPlayListRes) { return DB_OP_ADD_REPEAT; } bool addRes; //歌单列表中不存在该歌曲,添加该歌曲 QSqlQuery addSongToHistory(m_database); QString addSongString = QString("insert into 'playlist_%1' (%2) values('%3','%4','%5','%6','%7','%8','%9')"). arg(inPutStringHandle(playListName)). arg(SHOWCONTEXTS). arg(inPutStringHandle(temp.filepath)). arg(inPutStringHandle(temp.title)). arg(inPutStringHandle(temp.singer)). arg(inPutStringHandle(temp.album)). arg(inPutStringHandle(temp.filetype)). arg(inPutStringHandle(temp.size)). arg(inPutStringHandle(temp.time)); addRes = addSongToHistory.exec(addSongString); int tempIndex = addSongToHistory.lastInsertId().toInt(); bool setRes = true; QSqlQuery setSongIDFromLocal(m_database); QString setIndex = QString("update 'playlist_%1' set idIndex='%2' WHERE filepath='%3'"). arg(inPutStringHandle(playListName)).arg(tempIndex).arg(inPutStringHandle(temp.filepath)); setRes &= setSongIDFromLocal.exec(setIndex); if(true == (addRes&setRes)) { return DB_OP_SUCC; } else { qDebug() << "数据库打开,添加失败!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_ADD_FAILED; } } else { qDebug() << "数据库无法打开,请重试!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_UNCONNECT; } } else { //本地歌单中检查该歌曲失败 return checkLocalRes; } } else { //该歌单检查是否存在失败 return checkRes; } } //从歌单中添加歌曲到新建歌单,如果本地歌单中没有,同样插入本地歌单,使用musicDataStruct结构,输入数据必须有效, int MusicDataBase::addNewSongToPlayList(const musicDataStruct& fileData,const QString& playListName) { if(playListName.isEmpty()) { return INVALID_INPUT; } if(fileData.title.isEmpty() || fileData.filepath.isEmpty()) { qDebug() << "无效入参" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return INVALID_INPUT; } int checkListRes = checkIfSongExistsInPlayList(fileData.filepath,playListName); if(SONG_NOT_FOUND != checkListRes) { if(DB_OP_SUCC == checkListRes) { qDebug() << "歌曲重复添加"; return DB_OP_ADD_REPEAT; } else { qDebug() << "检查歌曲是否存在失败,失败码为:" << checkListRes; return checkListRes; } } else { int checkLocalRes = checkIfSongExistsInLocalMusic(fileData.filepath); if(checkLocalRes == SONG_NOT_FOUND) { int addLocalRes; addLocalRes = addMusicToLocalMusic(fileData); if(DB_OP_SUCC == addLocalRes) { int addListRes; addListRes = addMusicToPlayList(fileData.filepath,playListName); return addListRes; } else { return addLocalRes; } } else if(checkLocalRes == DB_OP_SUCC) { int addListRes; addListRes = addMusicToPlayList(fileData.filepath,playListName); return addListRes; } else { return checkLocalRes; } } } //从新建歌单中删除歌曲,使用歌曲的路径值,输入数据必须有效, int MusicDataBase::delMusicFromPlayList(const QString& filePath,const QString& playListName) { if(filePath.isEmpty() || playListName.isEmpty()) { return INVALID_INPUT; } int checkRes; checkRes = checkPlayListExist(playListName); if(DB_OP_SUCC == checkRes) { //暂时把歌单和本地歌曲分离 // int checkLocalRes = checkIfSongExistsInLocalMusic(filePath); // if(DB_OP_SUCC == checkLocalRes) // { if(m_database.isValid()) { //查询歌单列表中是否已有该歌曲,没有的话,返回删除失败 int checkPlayListRes = checkIfSongExistsInPlayList(filePath,playListName); //歌单列表中已经有这首歌,可以删除 if(DB_OP_SUCC == checkPlayListRes) { bool delRes; //歌单列表中存在该歌曲,删除该歌曲 QSqlQuery delSongToHistory(m_database); QString delSongString = QString("delete from 'playlist_%1' where filepath = '%2'"). arg(inPutStringHandle(playListName)). arg(inPutStringHandle(filePath)); delRes = delSongToHistory.exec(delSongString); if(true == delRes) { return DB_OP_SUCC; } else { qDebug() << "数据库打开, 删除失败!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_OP_DEL_FAILED; } } else { return DB_OP_DEL_FAILED; } } else { qDebug() << "数据库无法打开,请重试!!!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_UNCONNECT; } // } // else // { // //本地歌单中检查该歌曲失败 // return checkLocalRes; // } } else { //该歌单检查是否存在失败 return checkRes; } } int MusicDataBase::getSongIndexFromLocalMusic(const QString& filePath, int &songIndex) { if(filePath.isEmpty()) { return INVALID_INPUT; } if(true == m_database.isValid()) { bool getRes = true; QSqlQuery getSongFromLocalMusic(m_database); QString getSongString = QString("select idIndex from %1 where filepath = '%2'") .arg(ALLMUSIC) .arg(inPutStringHandle(filePath)); getRes = getSongFromLocalMusic.exec(getSongString); if(true == getRes) { if(getSongFromLocalMusic.next()) { songIndex = getSongFromLocalMusic.value(0).toInt(); } else { qDebug() << "数据库中查无此歌!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_DISORDERD; } return DB_OP_SUCC; } else { return DB_OP_GET_FAILED; } } else { return DB_DISORDERD; } } int MusicDataBase::getSongIndexFromPlayList(const QString& filePath,const QString& playListName, int &songIndex) { if(filePath.isEmpty()) { return INVALID_INPUT; } if(true == m_database.isValid()) { int checkRes = checkPlayListExist(playListName); if(checkRes == DB_OP_SUCC) { bool getRes = true; QSqlQuery getSongFromPlayList(m_database); QString getSongString = QString("select idIndex from 'playlist_%1' where filepath = '%2'"). arg(inPutStringHandle(playListName)).arg(inPutStringHandle(filePath)); getRes = getSongFromPlayList.exec(getSongString); if(true == getRes) { if(getSongFromPlayList.next()) { songIndex = getSongFromPlayList.value(0).toInt(); } else { qDebug() << "数据库中查无此歌!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_DISORDERD; } return DB_OP_SUCC; } else { return DB_OP_GET_FAILED; } } else { return checkRes; } } else { return DB_DISORDERD; } } int MusicDataBase::delSongFromEveryWhere(const QString& filePath) { //从历史歌单中删除该歌 int checkHistoryRes = checkIfSongExistsInHistoryMusic(filePath); if(DB_OP_SUCC == checkHistoryRes) { int delHistoryRes = delMusicFromHistoryMusic(filePath); if(DB_OP_SUCC != delHistoryRes) { return delHistoryRes; } else {} } else { if(SONG_NOT_FOUND != checkHistoryRes) { return checkHistoryRes; } else {} } //从总歌单中删除该歌 int checkLocalRes = checkIfSongExistsInLocalMusic(filePath); if(DB_OP_SUCC == checkLocalRes) { int delLocalRes = delMusicFromLocalMusic(filePath); if(DB_OP_SUCC != delLocalRes) { return delLocalRes; } else {} } else { if(SONG_NOT_FOUND != checkLocalRes) { return checkLocalRes; } else {} } QStringList temp; //查询当前已有歌单列表 int getPlayListRes = getPlayList(temp); if(DB_OP_SUCC == getPlayListRes) { foreach (auto tempList, temp) { //检查歌曲是否在歌单列表中存在 int checkPlayListRes = checkIfSongExistsInPlayList(filePath, tempList); if(DB_OP_SUCC == checkPlayListRes) { //从新建歌单中删除歌曲,使用歌曲filePath,歌单名title值,输入数据必须有效, int delMusicFromPlayListRes = delMusicFromPlayList(filePath, tempList); if(DB_OP_SUCC != delMusicFromPlayListRes) { return delMusicFromPlayListRes; } else {} } else { if(SONG_NOT_FOUND != checkPlayListRes) { return checkPlayListRes; } else {} } } } else { return getPlayListRes; } //从历史歌单,总歌单,各新建歌单中都删除后,返回成功。 return DB_OP_SUCC; } //删除多首歌曲 int MusicDataBase::delMultiSongs(const QString &playListName, const QStringList &songsList) { //入参检查 if(playListName.isEmpty()) { return INVALID_INPUT; } if(0 == songsList.size()) { return INVALID_INPUT; } foreach (auto songName, songsList) { if(songName.isEmpty()) { return INVALID_INPUT; } } if(ALLMUSIC == playListName || HISTORY == playListName) { } else { int playListCheckRes = checkPlayListExist(playListName); if(DB_OP_SUCC != playListCheckRes) { return playListCheckRes; } } foreach (auto songName, songsList) { int checkIfSongExists = OUT_OF_RESULT; if(ALLMUSIC == playListName) { checkIfSongExists = checkIfSongExistsInLocalMusic(songName); if(SONG_NOT_FOUND == checkIfSongExists) { continue; } } else if(HISTORY == playListName) { checkIfSongExists = checkIfSongExistsInHistoryMusic(songName); if(SONG_NOT_FOUND == checkIfSongExists) { continue; } } else { checkIfSongExists = checkIfSongExistsInPlayList(songName, playListName); if(SONG_NOT_FOUND == checkIfSongExists) { continue; } } if(DB_OP_SUCC == checkIfSongExists) { int delRes = OUT_OF_RESULT; if(ALLMUSIC == playListName) { delRes = delMusicFromLocalMusic(songName); } else if(HISTORY == playListName) { delRes = delMusicFromHistoryMusic(songName); } else { delRes = delMusicFromPlayList(songName, playListName); } if(delRes != DB_OP_SUCC) { return delRes; } else { continue; } } else { return checkIfSongExists; } } return DB_OP_SUCC; } int MusicDataBase::checkIfPlayListIsEmpty(const QString& playListName) { if(playListName.isEmpty()) { return INVALID_INPUT; } int playListCheckRes = checkPlayListExist(playListName); if(DB_OP_SUCC != playListCheckRes) { return playListCheckRes; } else { if(true == m_database.isValid()) { bool getRes = true; int count = 0; QSqlQuery getSongFromPlayList(m_database); QString getSongString = QString("select count(title) from 'playlist_%1'").arg(inPutStringHandle(playListName)); getRes = getSongFromPlayList.exec(getSongString); if(true == getRes) { if(getSongFromPlayList.next()) { count = getSongFromPlayList.value(0).toInt(); if(count > 0) { return LIST_NOT_EMPTY; } else { return LIST_IS_EMPTY; } } else { qDebug() << "数据库中查无此歌!" <<__FILE__<< ","<<__FUNCTION__<<","<<__LINE__; return DB_DISORDERD; } } else { return DB_OP_GET_FAILED; } } } } QString MusicDataBase::inPutStringHandle(const QString& input) { QString temp = input; QByteArray text = temp.toUtf8(); QString output = text.toBase64(); return output; } QString MusicDataBase::outPutStringHandle(const QString& output) { QString temp = output; QByteArray text = temp.toUtf8(); text = QByteArray::fromBase64(text); QString outOrigin; outOrigin.prepend(text.data()); return outOrigin; } QString MusicDataBase::preHandle(const QString &input) { // 将'替换成'' QString res = input; int iPos = res.indexOf("'", 0); while(-1 != iPos){ res.insert(iPos, QString("'")); iPos = res.indexOf("'", iPos + 2); } return res; } ukui-screensaver/src/screensaver/adaptivelabel.h0000664000175000017500000000072215172041106021046 0ustar fengfeng#ifndef ADAPTIVELABEL_H #define ADAPTIVELABEL_H #include #include #include #include class AdaptiveLabel : public QLabel { Q_OBJECT public: explicit AdaptiveLabel(QWidget *parent = nullptr); void setPixmap(const QPixmap &pixmap); void setScale(float scale); protected: void paintEvent(QPaintEvent *event) override; private: QPixmap originalPixmap; float m_scale; }; #endif // ADAPTIVELABEL_H ukui-screensaver/src/screensaver/cyclelabel.h0000664000175000017500000000313715172041035020354 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef CYCLELABEL_H #define CYCLELABEL_H #include #include #include #include #include #include class CycleLabel : public QFrame { Q_OBJECT public: CycleLabel(QWidget *parent = 0); void setSize(QSize size); protected: void paintEvent(QPaintEvent *); private: bool checked; QColor borderColorOff; QColor bgColorOff; QColor bgColorOn; QColor sliderColorOff; QColor sliderColorOn; int space; //滑块离背景间隔 int rectRadius; //圆角角度 int step; //移动步长 int startX; int endX; QTimer *timer; void drawBg(QPainter *painter); void drawSlider(QPainter *painter); int m_width; int m_height; }; #endif // CHECKBUTTON_H ukui-screensaver/src/screensaver/videoplayer.h0000664000175000017500000000274715172041106020605 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef _KYVIDEOPLAYER_H #define _KYVIDEOPLAYER_H #include #include #include #include class KyVideoPlayer : public QWidget { Q_OBJECT public: explicit KyVideoPlayer(QWidget *parent = 0); ~KyVideoPlayer(); void setOutput(QGraphicsVideoItem *videoWidget, int duration); void setMediaFile(QString filePath); void stop(); Q_SIGNALS: void playerShowError(QMediaPlayer::Error error); private slots: void updatePosition(qint64 position); void mediaStatusChanged(QMediaPlayer::MediaStatus status); void onShowError(QMediaPlayer::Error error); private: QMediaPlayer *m_mediaPlayer; int playerFlag = 0; bool m_isActiveStop = false; int m_nVideoDuration = 0; QString m_filePath; }; #endif // VIDEOPLAYER_H ukui-screensaver/src/screensaver/rotatablelabel.h0000664000175000017500000000240515172041106021226 0ustar fengfeng#ifndef ROTATINGIMAGELABEL_H #define ROTATINGIMAGELABEL_H #include #include #include class RotatingImageLabel : public QLabel { Q_OBJECT Q_PROPERTY(int rotationAngle READ rotationAngle WRITE setRotationAngle) public: explicit RotatingImageLabel(QWidget *parent = nullptr); // 控制接口 void startRotation(int speedMsPerRound = 20000); // 开始旋转(默认20秒/圈) void stopRotation(); // 停止并重置角度 void pauseRotation(); // 暂停旋转 void resumeRotation(); // 从暂停恢复 void setRotationSpeed(int msPerRound); // 设置旋转速度 // 图片设置 void setImage(const QPixmap &pixmap); void setImage(const QString &filePath); int rotationAngle() const; void setRotationAngle(int angle); void rotate90CCW(); // 1秒逆时针旋转90度 void resetRotation(); // 复位到0度 protected: void paintEvent(QPaintEvent *event) override; private: QPropertyAnimation *m_animation; QPropertyAnimation *m_quickAnimation; // 用于快速旋转 QPixmap m_pixmap; int m_currentAngle; bool m_isPaused; }; #endif // ROTATINGIMAGELABEL_H ukui-screensaver/src/screensaver/mbackground.cpp0000664000175000017500000000563115172041106021104 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mbackground.h" MBackground::MBackground() : currentIndex(0) { getAllPixmap(); } QString MBackground::getCurrent() { if (list.count() < 0) return ""; if (currentIndex >= 0 && currentIndex < list.count()) return list.at(currentIndex); return ""; } QString MBackground::getNext() { if (list.count() == 1) return list.at(0); if (list.count() <= 0 || currentIndex >= list.count() || currentIndex < 0) return ""; if (currentIndex == list.count() - 1) currentIndex = 0; else currentIndex++; return list.at(currentIndex); } QString MBackground::getPrev() { if (list.count() == 1) return list.at(0); if (list.count() <= 0 || currentIndex >= list.count() || currentIndex < 0) return ""; if (currentIndex == 0) currentIndex = list.count() - 1; else currentIndex--; return list.at(currentIndex); } QString MBackground::getRand() { if (list.count() <= 0) return ""; currentIndex = QRandomGenerator::global()->bounded(list.count()); return list.at(currentIndex); } void MBackground::getAllPixmap() { QDomDocument doc; QFile *file; file = new QFile("/usr/share/ukui-background-properties/focal-ubuntukylin-wallpapers.xml"); if (!file->open(QIODevice::ReadOnly)) { qDebug() << file->fileName() << " open failed"; return; } if (!doc.setContent(file)) { file->close(); return; } file->close(); file->deleteLater(); QDomElement root = doc.documentElement(); //读取根节点 QDomNode node = root.firstChild(); //读取第一个子节点 QDomNode 节点 while (!node.isNull()) { QDomElement node1 = node.firstChildElement("filename"); QString fileName = node1.text(); QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(fileName); if (mime.name().startsWith("image/")) { list.append(fileName); } node = node.nextSibling(); //读取下一个兄弟节点 } } ukui-screensaver/src/screensaver/weathermanager.cpp0000664000175000017500000003537415172041106021611 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Authors: ZHAI Kang-ning **/ #include "weathermanager.h" #include "scconfiguration.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include const QString httpPrefix = "http"; const QString weatherServerAddr = "://weather.ubuntukylin.com:8001/weather/api/3.0/heweather_data_s6/"; const QString defaultCityId = "101030100"; //"101030100" 默认天津 const int weatherReqInterval = 1000 * 60 * 60; // 定时更新天气,和麒麟天气保持一致 const QByteArray schemaWeather = "org.kylin-weather.settings"; static const QMap weatherMap{ { "晴", "100" }, { "多云", "101" }, { "少云", "102" }, { "晴间多云", "103" }, { "阴", "104" }, { "有风", "200" }, { "平静", "201" }, { "微风", "202" }, { "和风", "203" }, { "清风", "204" }, { "强风劲风", "205" }, { "疾风", "206" }, { "大风", "207" }, { "烈风", "208" }, { "风暴", "209" }, { "狂暴风", "210" }, { "飓风", "211" }, { "龙卷风", "212" }, { "热带风暴", "213" }, { "阵雨", "300" }, { "强阵雨", "301" }, { "雷阵雨", "302" }, { "强雷阵雨", "303" }, { "雷阵雨伴有冰雹", "304" }, { "小雨", "305" }, { "中雨", "306" }, { "大雨", "307" }, { "极端降雨", "308" }, { "毛毛雨细雨", "309" }, { "暴雨", "310" }, { "大暴雨", "311" }, { "特大暴雨", "312" }, { "冻雨", "313" }, { "小到中雨", "314" }, { "中到大雨", "315" }, { "大到暴雨", "316" }, { "暴雨到大暴雨", "317" }, { "大暴雨到特大暴雨", "318" }, { "雨", "399" }, { "小雪", "400" }, { "中雪", "401" }, { "大雪", "402" }, { "暴雪", "403" }, { "雨夹雪", "404" }, { "雨雪天气", "405" }, { "阵雨夹雪", "406" }, { "阵雪", "407" }, { "小到中雪", "408" }, { "中到大雪", "409" }, { "大到暴雪", "410" }, { "雪", "499" }, { "薄雾", "500" }, { "雾", "501" }, { "霾", "502" }, { "扬沙", "503" }, { "浮尘", "504" }, { "沙尘暴", "507" }, { "强沙尘暴", "508" }, { "大雾", "509" }, { "强浓雾", "510" }, { "中度霾", "511" }, { "重度霾", "512" }, { "严重霾", "513" }, { "大雾", "514" }, { "特强浓雾", "515" }, { "热", "900" }, { "冷", "901" }, { "未知", "999" } }; WeatherManager::WeatherManager(QObject *parent) : QObject(parent), configuration(SCConfiguration::instance()) { // 初始化m_net_manager请求天气 m_net_manager = new QNetworkAccessManager(this); QObject::connect(m_net_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *))); m_timer = new QTimer(this); m_timer->setInterval(weatherReqInterval); connect(m_timer, &QTimer::timeout, this, &WeatherManager::weatherRequest); m_local_weather_info = new LocalWeatherInfo(this); } void WeatherManager::initNetworkStatus() { m_iNetworkConnectState = configuration->getNetworkConnectState(); onNetworkStateChanged(m_iNetworkConnectState); connect(configuration, &SCConfiguration::networkConnectStateChanged, this, &WeatherManager::onNetworkStateChanged); } void WeatherManager::onNetworkStateChanged(uint state) { qDebug() << state; if (state == 4) getWeather(); else emit onNetworkError(); } void WeatherManager::getWeather() { weatherRequest(); if (m_timer != NULL) { m_timer->start(); } return; } void WeatherManager::weatherRequest() { if (updateLocation()) { return; } } bool WeatherManager::updateLocation() { if (getLogcalWeather()) { qDebug() << "getCityName = " << m_local_weather_info->getCityName() << "getCondText = " << m_local_weather_info->getCondText() << "getTemperature = " << m_local_weather_info->getTemperature(); emit onWeatherUpdate( m_local_weather_info->getCityName(), m_local_weather_info->getCondText(), m_local_weather_info->getTemperature()); QNetworkRequest request( QUrl(QString("%1%2").arg(httpPrefix).arg(weatherServerAddr) + m_local_weather_info->getCityId() + "/")); m_net_manager->get(request); return true; } return false; } bool WeatherManager::getLogcalWeather() { QString weather = configuration->getWeatherData(); if (!weather.isEmpty()) { //"1920-08-27 10:17:42,101310204,澄迈,小雨,95%,25℃,北风,1级," // 时间,城市编码,城市名称,天气,湿度,温度,风向,风力 QStringList weatherInfoList = weather.split(","); qDebug() << "weather = " << weather; if (weatherInfoList.size() < 9) return false; m_local_weather_info->setTime(weatherInfoList.at(0)); if (!m_local_weather_info->isTimeValid()) { emit onGetWeatherFailed(); return false; } m_local_weather_info->setCityId(weatherInfoList.at(1)); m_local_weather_info->setCityName(weatherInfoList.at(2)); m_local_weather_info->setCondText(weatherInfoList.at(3)); m_local_weather_info->setAirHumidity(weatherInfoList.at(4)); m_local_weather_info->setTemperature(weatherInfoList.at(5)); m_local_weather_info->setWindDirection(weatherInfoList.at(6)); m_local_weather_info->setWindForce(weatherInfoList.at(7)); return true; } return false; } QString WeatherManager::getLogcalCityId() { if (m_settings != nullptr) { QString citys = m_settings->get("citylist").toString(); //"101010100," if (citys.isEmpty()) return ""; QStringList cityList = citys.split(","); if (cityList.size() >= 1) { QString s = cityList.at(0); qDebug() << "local city id = " << s; return s; } } return ""; } void WeatherManager::replyFinished(QNetworkReply *reply) { if (reply != nullptr && reply->error() != QNetworkReply::NoError) { qWarning() << "[WeatherManager][replyFinished] get weather error:(" << reply->error() << ")" << reply->errorString(); if (m_networkTryNum < 15) { m_networkTryNum++; QTimer::singleShot(1000, this, [=] { weatherRequest(); }); } else { m_networkTryNum = 0; } emit onGetWeatherIconFinished(); return; } // 注:天气信息只解析了锁屏需要展示的部分 QByteArray BA; QJsonDocument JD; QJsonParseError JPE; BA = reply->readAll(); JD = QJsonDocument::fromJson(BA, &JPE); if (JPE.error == QJsonParseError::NoError) { if (JD.isObject()) { QJsonObject kylinWeatherObj = JD.object().value("KylinWeather").toObject(); QString nowWeather = kylinWeatherObj.value("weather").toObject().value("now").toString(); m_city_name = kylinWeatherObj.value("weather").toObject().value("location").toString(); QStringList nowList = nowWeather.split(","); for (QString now : nowList) { if (now.contains("cond_txt")) { m_cond_txt = now.mid(9); } if (now.contains("tmp")) { m_temperature = now.mid(4) + "°C"; } } if (nowWeather != "" && nowWeather.contains(",", Qt::CaseInsensitive)) { QStringList strList = nowWeather.split(","); QJsonObject m_json; foreach (QString str, strList) { if (str != "") { m_json.insert(str.split("=").at(0), str.split("=").at(1)); } } m_cond_code = m_json.value("cond_code").toString(); qDebug() << "Function Positioning 解析当前城市信息: " << m_cond_code << m_city_name; } emit onGetWeatherIconFinished(); } } else { qWarning() << "get weather info error : " << JPE.errorString(); emit onGetWeatherIconFinished(); } reply->deleteLater(); } QString WeatherManager::getWeatherIcon() { if (!m_cond_code.isEmpty()) { return (":/weather/assets/weather-icon/SVG/" + m_cond_code + ".svg"); } else { return (":/weather/assets/weather-icon/SVG/999.svg"); } } QPixmap WeatherManager::getWeatherStateIcon() { QPixmap weatherStateIcon; if (m_cond_code.isEmpty()) { weatherStateIcon = QPixmap(":/assets/underlying.png"); } else { int weatherNum = m_cond_code.toInt(); weatherStateIcon = QPixmap(getWeatherIconPath(weatherNum)); } return weatherStateIcon; } QString WeatherManager::getWeatherIconPath(int weatherCode) { static const QMap specialCases = { { 100, ":/assets/qing.png" }, { 150, ":/assets/qing.png" }, { 103, ":/assets/duoyun.png" }, { 153, ":/assets/duoyun.png" } }; if (specialCases.contains(weatherCode)) { return specialCases[weatherCode]; } if (weatherCode > 100 && weatherCode < 200) return ":/assets/ying.png"; if (weatherCode > 199 && weatherCode < 300) return ":/assets/feng.png"; if (weatherCode > 299 && weatherCode < 400) return ":/assets/yu.png"; if (weatherCode > 399 && weatherCode < 500) return ":/assets/wu.png"; if (weatherCode > 499 && weatherCode < 600) return ":/assets/xue.png"; return ":/assets/underlying.png"; } QPixmap WeatherManager::getWeatherIcon(QString cond) { if (cond.isEmpty()) { qWarning() << "cond info is unknown"; return QPixmap(":/weather/assets/weather-icon/999.svg").scaled(32, 32); } // 根据m_cond_txt QString numStr = weatherMap.value(cond); if (!numStr.isEmpty()) { qDebug() << "numStr=" + numStr; return QPixmap(":/weather/assets/weather-icon/" + numStr + ".svg").scaled(32, 32); } qWarning() << "天气为|" << cond << "|"; return QPixmap(":/weather/assets/weather-icon/999.svg").scaled(32, 32); } QString WeatherManager::getCityName() { return ""; } QString WeatherManager::getCond() { return ""; } QString WeatherManager::getTemperature() { return ""; } LocalWeatherInfo::LocalWeatherInfo(QObject *parent) {} bool LocalWeatherInfo::isTimeValid() { if (m_update_time != nullptr && !m_update_time.isEmpty()) { QString strBuffer; QDateTime time = QDateTime::fromString(m_update_time, "yyyy-MM-dd hh:mm"); QDateTime currentTime = QDateTime::currentDateTime(); if (!time.isValid()) return false; uint timeInterval = currentTime.toSecsSinceEpoch() - time.toSecsSinceEpoch(); uint futureInterval = time.toSecsSinceEpoch() - currentTime.toSecsSinceEpoch(); if ((timeInterval <= 61 * 60) || (futureInterval <= 61 * 60)) { // 麒麟天气更新时间为60分钟,加入1分钟容错机制 return true; } } return false; } void LocalWeatherInfo::setTime(QString time) { m_update_time = time; } QString LocalWeatherInfo::getTime() { return m_update_time; } void LocalWeatherInfo::setCityId(QString cityId) { m_city_id = cityId; } QString LocalWeatherInfo::getCityId() { return m_city_id; } void LocalWeatherInfo::setCityName(QString cityName) { m_city_name = cityName; } QString LocalWeatherInfo::getCityName() { return m_city_name; } void LocalWeatherInfo::setCondText(QString condText) { m_cond_text = condText; } QString LocalWeatherInfo::getCondText() { return m_cond_text; } void LocalWeatherInfo::setAirHumidity(QString airHumidity) { m_air_humidity = airHumidity; } QString LocalWeatherInfo::getAirHumidity() { return m_air_humidity; } void LocalWeatherInfo::setTemperature(QString temperature) { m_temperature = temperature; } QString LocalWeatherInfo::getTemperature() { return m_temperature; } void LocalWeatherInfo::setWindDirection(QString windDirection) { m_wind_direction = windDirection; } QString LocalWeatherInfo::getWindDirection() { return m_wind_direction; } void LocalWeatherInfo::setWindForce(QString windForce) { m_wind_force = windForce; } QString LocalWeatherInfo::getWindForce() { return m_wind_force; } ukui-screensaver/src/screensaver/CMakeLists.txt0000664000175000017500000002317115172041106020643 0ustar fengfengpkg_check_modules(X11 REQUIRED x11) pkg_check_modules(XTST REQUIRED xtst) pkg_check_modules(QGS REQUIRED gsettings-qt6) pkg_check_modules(KYSDKSYSTIME REQUIRED kysdk-systime) pkg_check_modules(MPV REQUIRED mpv) pkg_check_modules(TAGLIB REQUIRED taglib) include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_SOURCE_DIR}/Common) include_directories( ${X11_INCLUDE_DIRS} ${XTST_INCLUDE_DIRS} ${QGS_INCLUDE_DIRS} ${KYSDKSYSTIME_INCLUDE_DIRS} ${TAGLIB_INCLUDE_DIRS} ) ## 添加 mpv 头文件路径 #include_directories(/usr/include/mpv/) set(EXTRA_LIBS ${EXTRA_LIBS} ${X11_LIBRARIES} ${XTST_LIBRARIES} ${QGS_LIBRARIES} ${KYSDKSYSTIME_LIBRARIES} ${TAGLIB_LIBRARIES} -ldl ) qt6_add_resources(screensaver_SRC default.qrc ) qt6_wrap_cpp(screensaver_SRC chinesedate.h screensaver.h mbackground.h cyclelabel.h scconfiguration.h sleeptime.h checkbutton.h weathermanager.h networkwatcher.h videoplayer.h graphicsview.h weatherwidget.h mprissaverwidget.h mprisdbusclient.h mediasaverwidget.h dbusadapter.h mmediaplayer.h mmediaplaylist.h playcontroller.h musicdatabase.h musicfileinformation.h weatherwidget.h customalbumviewer.h timethumbnailwidget.h albumthumbnailwidget.h musicthumbnailwidget.h saverthumbnailwidget.h weatherthumbnailwidget.h masklable.h rotatablelabel.h cornerrotatinglabel.h adaptivelabel.h ../common/commonfunc.h ../common/global_utils.h ../common/utils.h ../common/biodefines.h ../dbusifs/freedesktophelper.h ../lock-dialog/backenddbushelper.h ../userinfo.h ../agreementinfo.h ../widgets/statusbutton.h ../widgets/customlabel.h ) set(screensaver_SRC ${screensaver_SRC} main.cpp chinesedate.cpp mbackground.cpp screensaver.cpp cyclelabel.cpp scconfiguration.cpp sleeptime.cpp checkbutton.cpp weathermanager.cpp networkwatcher.cpp videoplayer.cpp graphicsview.cpp weatherwidget.cpp mprissaverwidget.cpp mprisdbusclient.cpp mediasaverwidget.cpp dbusadapter.cpp mmediaplayer.cpp mmediaplaylist.cpp playcontroller.cpp musicdatabase.cpp musicfileinformation.cpp weatherwidget.cpp customalbumviewer.cpp timethumbnailwidget.cpp albumthumbnailwidget.cpp musicthumbnailwidget.cpp saverthumbnailwidget.cpp weatherthumbnailwidget.cpp masklable.cpp rotatablelabel.cpp cornerrotatinglabel.cpp adaptivelabel.cpp ../common/commonfunc.cpp ../common/global_utils.cpp ../common/utils.cpp ../common/biodefines.cpp ../dbusifs/freedesktophelper.cpp ../lock-dialog/backenddbushelper.cpp ../userinfo.cpp ../agreementinfo.cpp ../widgets/statusbutton.cpp ../widgets/customlabel.cpp ) add_executable(ukui-screensaver-default ${screensaver_SRC}) if(Qt6_WebEngineCore_FOUND AND Qt6_WebEngineWidgets_FOUND) target_link_libraries(ukui-screensaver-default Qt6::Core Qt6::Widgets Qt6::DBus Qt6::Xml Qt6::Network Qt::Sql ukui-log4qt Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg avformat avcodec mpv sqlite3 Qt6::WebEngineCore Qt6::WebEngineWidgets ${EXTRA_LIBS}) else() target_link_libraries(ukui-screensaver-default Qt6::Core Qt6::Widgets Qt6::DBus Qt6::Xml Qt6::Network Qt::Sql ukui-log4qt Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg avformat avcodec mpv sqlite3 ${EXTRA_LIBS}) endif() qt6_add_resources(screensaver_Plugin_SRC default.qrc ) qt6_wrap_cpp(screensaver_Plugin_SRC chinesedate.h screensaver.h mbackground.h cyclelabel.h scconfiguration.h sleeptime.h checkbutton.h weathermanager.h customplugin.h networkwatcher.h videoplayer.h graphicsview.h screensaverplugin.h weatherwidget.h mprissaverwidget.h mprisdbusclient.h mediasaverwidget.h dbusadapter.h mmediaplayer.h mmediaplaylist.h playcontroller.h musicdatabase.h musicfileinformation.h weatherthumbnailwidget.h weatherwidget.h customalbumviewer.h timethumbnailwidget.h albumthumbnailwidget.h musicthumbnailwidget.h saverthumbnailwidget.h masklable.h rotatablelabel.h cornerrotatinglabel.h adaptivelabel.h ../common/commonfunc.h ../common/global_utils.h ../common/utils.h ../common/biodefines.h ../dbusifs/freedesktophelper.h ../lock-dialog/backenddbushelper.h ../userinfo.h ../agreementinfo.h ../widgets/statusbutton.h ../widgets/customlabel.h ) set(screensaver_Plugin_SRC ${screensaver_Plugin_SRC} chinesedate.cpp mbackground.cpp screensaver.cpp cyclelabel.cpp scconfiguration.cpp sleeptime.cpp checkbutton.cpp weathermanager.cpp networkwatcher.cpp videoplayer.cpp graphicsview.cpp customplugin.cpp weatherwidget.cpp mprissaverwidget.cpp mprisdbusclient.cpp mediasaverwidget.cpp dbusadapter.cpp mmediaplayer.cpp mmediaplaylist.cpp playcontroller.cpp musicdatabase.cpp musicfileinformation.cpp weatherwidget.cpp customalbumviewer.cpp timethumbnailwidget.cpp albumthumbnailwidget.cpp musicthumbnailwidget.cpp saverthumbnailwidget.cpp weatherthumbnailwidget.cpp masklable.cpp rotatablelabel.cpp cornerrotatinglabel.cpp adaptivelabel.cpp ../common/commonfunc.cpp ../common/global_utils.cpp ../common/utils.cpp ../common/biodefines.cpp ../dbusifs/freedesktophelper.cpp ../lock-dialog/backenddbushelper.cpp ../userinfo.cpp ../agreementinfo.cpp ../widgets/statusbutton.cpp ../widgets/customlabel.cpp ) add_library(screensaver-default SHARED ${screensaver_Plugin_SRC}) if(Qt6_WebEngineCore_FOUND AND Qt6_WebEngineWidgets_FOUND) target_link_libraries(screensaver-default Qt6::Core Qt6::Widgets Qt6::DBus Qt6::Xml Qt6::Network ukui-log4qt Qt6::Multimedia Qt6::Sql Qt6::MultimediaWidgets Qt6::Svg avformat avcodec mpv sqlite3 Qt6::WebEngineCore Qt6::WebEngineWidgets ${EXTRA_LIBS}) else() target_link_libraries(screensaver-default Qt6::Core Qt6::Widgets Qt6::DBus Qt6::Xml Qt6::Network ukui-log4qt Qt6::Multimedia Qt6::Sql Qt6::MultimediaWidgets Qt6::Svg avformat avcodec mpv sqlite3 ${EXTRA_LIBS}) endif() target_compile_options(screensaver-default PRIVATE -fvisibility=hidden) target_link_options(screensaver-default PRIVATE -Bsymbolic) qt6_add_resources(Screensaver_SRC default.qrc ) qt6_wrap_cpp(Screensaver_SRC chinesedate.h screensaver.h mbackground.h cyclelabel.h scconfiguration.h sleeptime.h checkbutton.h weathermanager.h customplugin.h networkwatcher.h videoplayer.h graphicsview.h screensaverplugin.h weatherwidget.h mprissaverwidget.h mprisdbusclient.h mediasaverwidget.h dbusadapter.h mmediaplayer.h mmediaplaylist.h playcontroller.h musicdatabase.h musicfileinformation.h weatherwidget.h customalbumviewer.h timethumbnailwidget.h albumthumbnailwidget.h musicthumbnailwidget.h saverthumbnailwidget.h weatherthumbnailwidget.h masklable.h rotatablelabel.h cornerrotatinglabel.h adaptivelabel.h ../common/commonfunc.h ../common/global_utils.h ../common/utils.h ../common/biodefines.h ../dbusifs/freedesktophelper.h ../lock-dialog/backenddbushelper.h ../userinfo.h ../agreementinfo.h ../widgets/statusbutton.h ../widgets/customlabel.h ) set(Screensaver_SRC ${Screensaver_SRC} chinesedate.cpp mbackground.cpp screensaver.cpp cyclelabel.cpp scconfiguration.cpp sleeptime.cpp checkbutton.cpp weathermanager.cpp networkwatcher.cpp videoplayer.cpp graphicsview.cpp customplugin.cpp weatherwidget.cpp mprissaverwidget.cpp mprisdbusclient.cpp mediasaverwidget.cpp dbusadapter.cpp mmediaplayer.cpp mmediaplaylist.cpp playcontroller.cpp musicdatabase.cpp musicfileinformation.cpp weatherwidget.cpp customalbumviewer.cpp timethumbnailwidget.cpp albumthumbnailwidget.cpp musicthumbnailwidget.cpp saverthumbnailwidget.cpp weatherthumbnailwidget.cpp masklable.cpp rotatablelabel.cpp cornerrotatinglabel.cpp adaptivelabel.cpp ../common/commonfunc.cpp ../common/global_utils.cpp ../common/utils.cpp ../common/biodefines.cpp ../dbusifs/freedesktophelper.cpp ../lock-dialog/backenddbushelper.cpp ../userinfo.cpp ../agreementinfo.cpp ../widgets/statusbutton.cpp ../widgets/customlabel.cpp ) add_library(Screensaver STATIC ${Screensaver_SRC}) if(Qt6_WebEngineCore_FOUND AND Qt6_WebEngineWidgets_FOUND) target_link_libraries(Screensaver Qt6::Core Qt6::Widgets Qt6::DBus Qt6::Xml Qt6::Network ukui-log4qt Qt6::Multimedia Qt6::Sql Qt6::MultimediaWidgets Qt6::Svg avformat avcodec mpv sqlite3 Qt6::WebEngineCore Qt6::WebEngineWidgets ${EXTRA_LIBS}) else() target_link_libraries(Screensaver Qt6::Core Qt6::Widgets Qt6::DBus Qt6::Xml Qt6::Network ukui-log4qt Qt6::Multimedia Qt6::Sql Qt6::MultimediaWidgets Qt6::Svg avformat avcodec mpv sqlite3 ${EXTRA_LIBS}) endif() target_compile_options(Screensaver PRIVATE -fvisibility=hidden) target_link_options(Screensaver PRIVATE -Bsymbolic) install(TARGETS ukui-screensaver-default screensaver-default DESTINATION lib/ukui-screensaver) install(FILES language/screensaver-zh_CN.ini language/screensaver-en_US.ini language/screensaver-bo_CN.ini language/screensaver-jd.ini language/screensaver-zh_CN-ok.ini DESTINATION share/ukui-screensaver/language) install(FILES screensaverplugin.h DESTINATION include/ukui-screensaver/) ukui-screensaver/src/screensaver/screensaver.cpp0000664000175000017500000026713015172041106021134 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "utils.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 #ifndef ARCHITECTUREISSW64 #include #include #endif #include "screensaver.h" #include "weathermanager.h" #include "commonfunc.h" #include #include #include #include #include extern "C" { #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" } #define WORKING_DIRECTORY "/usr/share/ukui-screensaver" QTime Screensaver::m_currentTime = QTime::currentTime(); Screensaver::Screensaver(bool isscreensaver, QPixmap *originalBackground, QWidget *parent) : isScreensaver(isscreensaver) , QWidget(parent) , switchTimer(nullptr) , backgroundPath("") , cycleTime(300) , isCustom(false) , isCShowRestTime(true) , isUShowRestTime(true) , myTextLabel(nullptr) , myPreviewLabel(nullptr) , // 预览label标签 configuration(SCConfiguration::instance()) , myTextWidget(nullptr) , m_centerWidget(nullptr) , sleepTime(nullptr) , timer(nullptr) , background("") , autoSwitch(nullptr) , vboxFrame(nullptr) , isAutoSwitch(false) , flag(0) , hasChanged(false) , process(nullptr) , screenLabel(nullptr) , respondClick(false) // , m_weatherManager(new WeatherManager) { // 加载翻译文件 QString locale = QString(getenv("LANGUAGE")); if (locale.contains(":")) { locale = locale.split(":").at(0); } QTranslator translator; QString qmFile = QString(WORKING_DIRECTORY "/i18n_qm/%1.qm").arg(locale); translator.load(qmFile); qApp->installTranslator(&translator); qDebug() << "load translation file " << qmFile; installEventFilter(this); // setWindowFlags(Qt::X11BypassWindowManagerHint); setUpdateCenterWidget(); setMouseTracking(true); getConfiguration(); m_background = new MBackground(); QString backgroundFile; backgroundFile = (isScreensaver ? configuration->getDefaultBackground() : mapToThumbnailPath(configuration->getDefaultBackground())); if (m_nSubMode == 0 || m_nSubMode < 0) { background = loadFromFile(backgroundFile); } else { if (originalBackground && !originalBackground->isNull()) { background = *originalBackground; m_scaleBackground = *originalBackground; } else { background = scaleBlurPixmap(width(), height(), backgroundFile); m_scaleBackground = scaleBlurPixmap(width(), height(), backgroundFile); } } initUI(); if (m_nSubMode == 3 && m_customAlbumViewer) { m_customAlbumViewer->setDefaultPixmap(loadFromFile(backgroundFile)); } QList labelList = this->findChildren(); for (int i = 0; i < labelList.count(); i++) { labelList.at(i)->setAlignment(Qt::AlignCenter); } #ifndef USE_INTEL if (m_nSubMode == 0 || m_nSubMode < 0 && isCustom) { updateBackgroundPath(); startSwitchImages(); } connectSingles(); #endif screenLabel = new QLabel(this); screenLabel->setObjectName("screenLabel"); switch (m_saverState) { case SAVERERRORTYPE::VIDEOFORMATEERROR : screenLabel->setText(tr("The dynamic screensavers video file is corrupted")); break; case SAVERERRORTYPE::VIDEOTOOLARGE : screenLabel->setText(tr("The dynamic screensavers video file is too large")); break; case SAVERERRORTYPE::RESOLUTIONERROR : screenLabel->setText(tr("The dynamic screensavers video file resolution is too high")); break; case SAVERERRORTYPE::URLADDRESSERROR : screenLabel->setText(tr("Invalid URL")); break; default: screenLabel->setText(tr("Picture does not exist")); break; } screenLabel->adjustSize(); screenLabel->hide(); m_strPreViewTrans = tr("View"); m_isInitUICompleted = true; qDebug() << "preview translation:" << m_strPreViewTrans; } Screensaver::~Screensaver() { playController::getInstance().stop(); } void Screensaver::getConfiguration() { isCustom = configuration->getIsCustom(); if (isCustom) { cycleTime = configuration->getCycleTime(); isAutoSwitch = configuration->getAutoSwitch(); backgroundPath = configuration->getBackgroundPath(); isCShowRestTime = configuration->getCShowRestTime(); textIsCenter = configuration->getTextIsCenter(); myText = configuration->getMyText(); } else isUShowRestTime = configuration->getUShowRestTime(); if (isScreensaver) { curFontSize = configuration->getFontSize(); } else { curFontSize = 0.0f; } m_ptToPx = configuration->getPtToPx(); m_isShowDateTime = configuration->getSaverDatetimeEnable(); m_videoPath = configuration->getVideoPath(); m_videoFormat = configuration->getVideoFormat(); m_videoSize = configuration->getVideoSize(); m_videoWidth = configuration->getVideoWidth(); m_videoHeight = configuration->getVideoHeight(); m_nSubMode = configuration->getSubMode(); m_nWeatherPosition = configuration->getWeatherPosition(); m_bFollowSystem = configuration->getMusicFollowSystem(); m_sMusicPath = configuration->getMusicPath(); m_nPhotoCycleMode = configuration->getPhotoCycleMode(); m_nPhotoCycleInterval = configuration->getPhotoCycleInterval(); m_sDefaultPhotoPath = configuration->getDefaultPhotoPath(); m_sCustomPhptoPath = configuration->getCustomPhotoPath(); QSet set; for (const QString &str : m_sCustomPhptoPath) { set.insert(str); } for (const QString &str : m_sDefaultPhotoPath) { set.insert(str); } m_sAllPhotoPath = set.values(); qDebug() << "allList = " << m_sAllPhotoPath; m_iPhotoSwitchMode = configuration->getPhotoCycleMode(); m_iPhotoSwitchTime = configuration->getPhotoCycleInterval() * 1000; m_sSelectPhotoPath = configuration->getSelectPhotoPath(); } void Screensaver::connectSingles() { connect(configuration, &SCConfiguration::isCustomModeChanged, this, &Screensaver::onIsCustomModeChanged); connect(configuration, &SCConfiguration::autoSwitchChanged, this, &Screensaver::autoSwitchChanged); connect(configuration, &SCConfiguration::backgroundPathChanged, this, &Screensaver::backgroundPathChanged); connect(configuration, &SCConfiguration::backgroundChanged, this, &Screensaver::backgroundChanged); connect(configuration, &SCConfiguration::cycleTimeChanged, this, &Screensaver::cycleTimeChanged); connect(configuration, &SCConfiguration::myTextChanged, this, &Screensaver::myTextChanged); connect(configuration, &SCConfiguration::showCRestTimeChanged, this, &Screensaver::showCRestTimeChanged); connect(configuration, &SCConfiguration::showURestTimeChanged, this, &Screensaver::showURestTimeChanged); connect(configuration, &SCConfiguration::textIsCenterChanged, this, &Screensaver::textIsCenterChanged); connect(configuration, &SCConfiguration::timeTypeChanged, this, &Screensaver::onTimeFormatChanged); connect(configuration, &SCConfiguration::dateTypeChanged, this, &Screensaver::onDateFormatChanged); connect(configuration, &SCConfiguration::blurChanged, this, &Screensaver::onBlurNumChanged); connect(configuration, &SCConfiguration::styleChanged, this, &Screensaver::onStyleChanged); connect(configuration, &SCConfiguration::subModeChanged, this, &Screensaver::onSubModeChanged); connect(configuration, &SCConfiguration::photoSelectPathChanged, this, &Screensaver::onPhotoSelectPathChanged); connect(configuration, &SCConfiguration::musicFollowSystemChanged, this, &Screensaver::onMusicFollowSystemChanged); connect(configuration, &SCConfiguration::musicPathChanged, this, &Screensaver::onMusicPathChanged); connect( configuration, &SCConfiguration::photoCycleIntervalChanged, this, &Screensaver::onPhotoCycleIntervalChanged); connect(configuration, &SCConfiguration::photoCycleModeChanged, this, &Screensaver::onPhotoCycleModeChanged); connect( configuration, &SCConfiguration::saverDatetimeEnableChanged, this, &Screensaver::onScreensaverDatetimeEnableChanged); } void Screensaver::onBlurNumChanged(int num) { if (m_isDynamicSaver || m_isUrlSaver) return; blur_Num = num; if (curStyle == "ukui-dark" || curStyle == "ukui-black") { myTextLabel->setStyleSheet( QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:16px}").arg(blur_Num * 0.01)); cycleLabel->setStyleSheet( QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:8px}").arg(blur_Num * 0.01)); } else { myTextLabel->setStyleSheet( QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:16px}").arg(blur_Num * 0.01)); cycleLabel->setStyleSheet( QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:8px}").arg(blur_Num * 0.01)); } } void Screensaver::onStyleChanged(QString style) { if (m_isDynamicSaver || m_isUrlSaver) return; curStyle = style; if (curStyle == "ukui-dark" || curStyle == "ukui-black") { myTextLabel->setStyleSheet( QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:16px}").arg(blur_Num * 0.01)); cycleLabel->setStyleSheet( QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:8px}").arg(blur_Num * 0.01)); } else { myTextLabel->setStyleSheet( QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:16px}").arg(blur_Num * 0.01)); cycleLabel->setStyleSheet( QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:8px}").arg(blur_Num * 0.01)); } } void Screensaver::onSubModeChanged(int mode) { if (m_nSubMode == mode) { return; } m_nSubMode = mode; QFileInfo fileInfo(backgroundPath); bool isUrlValid = false; #ifndef ARCHITECTUREISSW64 isUrlValid = getIsValidUrl(backgroundPath); #endif if (isCustom) { if (fileInfo.isFile()) { initDynamicSaver(backgroundPath); if (m_isDynamicSaver) { m_isUrlSaver = false; } } else if (isUrlValid) { #ifndef ARCHITECTUREISSW64 initUrlWidget(); m_isUrlSaver = true; #endif m_isDynamicSaver = false; } else { m_isDynamicSaver = false; m_isUrlSaver = false; } } initOtherWidget(); if (m_isDynamicSaver && m_nSubMode == 0 && m_playerView) { if (m_smartSaverLabel) { m_smartSaverLabel->hide(); } if (m_thumBnalListWidget) { m_thumBnalListWidget->hide(); } if (m_sidebarBtn) { m_sidebarBtn->hide(); } m_widgetDatetime->hide(); qDebug() << "player raise show"; if (m_playerView) { qDebug() << "player raise show111"; m_playerView->raise(); m_playerView->show(); } QCoreApplication::postEvent(this, new QEvent(QEvent::User)); update(); return; } else if (m_isUrlSaver && m_nSubMode == 0) { #ifndef ARCHITECTUREISSW64 if (m_pWebEngineView) { if (m_smartSaverLabel) { m_smartSaverLabel->hide(); } if (m_thumBnalListWidget) { m_thumBnalListWidget->hide(); } if (m_sidebarBtn) { m_sidebarBtn->hide(); } m_widgetDatetime->hide(); qDebug() << "player raise show"; if (m_pWebEngineView) { qDebug() << "player raise show111"; m_pWebEngineView->raise(); m_pWebEngineView->show(); } QCoreApplication::postEvent(this, new QEvent(QEvent::User)); update(); return; } #endif } QString backgroundFile; backgroundFile = (isScreensaver ? configuration->getDefaultBackground() : mapToThumbnailPath(configuration->getDefaultBackground())); if (m_nSubMode == 0 || m_nSubMode < 0) { if (isCustom) { updateBackgroundPath(); startSwitchImages(); } else { background = loadFromFile(backgroundFile); } showCRestTimeChanged(isCShowRestTime); showURestTimeChanged(isUShowRestTime); if (!isCustom) { if (m_centerWidget) { m_centerWidget->show(); } } else { textIsCenterChanged(textIsCenter); } } else { if (m_centerWidget) { m_centerWidget->hide(); } background = scaleBlurPixmap(width(), height(), backgroundFile); } if (m_nSubMode == 1) { initWeatherWidget(); if (myTextWidget) myTextWidget->hide(); if (m_playerView) m_playerView->hide(); if (m_centerWidget) m_centerWidget->hide(); } if (m_nSubMode == 2) { initMediaControlWidget(); if (myTextWidget) myTextWidget->hide(); if (m_playerView) m_playerView->hide(); if (m_centerWidget) m_centerWidget->hide(); } else if (m_nSubMode == 3) { initCustomAlbumViewer(); m_customAlbumViewer->setDefaultPixmap(loadFromFile(backgroundFile)); if (myTextWidget) myTextWidget->hide(); if (m_playerView) m_playerView->hide(); if (m_centerWidget) m_centerWidget->hide(); } QCoreApplication::postEvent(this, new QEvent(QEvent::User)); update(); } void Screensaver::onMusicFollowSystemChanged(bool b) { if (m_bFollowSystem != b && m_widgetMediaControl) { if (b) { m_mediaControlWidget->setSaverMediaType(!b); } else { QStringList musicPath = getAudioFiles(m_sMusicPath); if (musicPath.isEmpty()) { m_mediaControlWidget->setSaverMediaType(b); } else { m_mediaControlWidget->setSaverMediaType(!b); playController::getInstance().addMusicToDatebase(musicPath); playController::getInstance().setCurPlaylist(musicPath); } } } m_bFollowSystem = b; } void Screensaver::onWeatherPositionChanged(int position) {} void Screensaver::onMusicPathChanged(QString path) { m_sMusicPath = path; if (m_bFollowSystem) { m_mediaControlWidget->setSaverMediaType(!m_bFollowSystem); } else { QStringList musicPath = getAudioFiles(m_sMusicPath); if (musicPath.isEmpty()) { m_mediaControlWidget->setSaverMediaType(m_bFollowSystem); } else { m_mediaControlWidget->setSaverMediaType(!m_bFollowSystem); playController::getInstance().addMusicToDatebase(musicPath); playController::getInstance().setCurPlaylist(musicPath); } } } void Screensaver::onPhotoCycleIntervalChanged(int interval) { if (m_iPhotoSwitchTime != (interval * 1000)) { m_iPhotoSwitchTime = interval * 1000; m_customAlbumViewer->setUpdateInterval(m_iPhotoSwitchTime); } } void Screensaver::onPhotoCycleModeChanged(int mode) { qDebug() << "mode = " << mode; if (m_iPhotoSwitchMode != mode) { m_iPhotoSwitchMode = mode; m_customAlbumViewer->setSequentialMode((m_iPhotoSwitchMode == 0) ? true : false); } } void Screensaver::onPhotoPathChanged(QString path) {} void Screensaver::onPhotoSelectPathChanged(QString path) { m_sSelectPhotoPath = path; if (m_albunWidget && m_sSelectPhotoPath == DEFAULT_PHOTO_PATH) { m_albunWidget->setDefaultPath(DEFAULT_PHOTO_PATH); } else if (m_albunWidget) { m_albunWidget->setDefaultPath(m_sSelectPhotoPath); } if (!isScreensaver && m_sSelectPhotoPath == DEFAULT_PHOTO_PATH) { m_sSelectPhotoPath = LOCAL_WALLPAPER_PATH; } m_customAlbumViewer->setImagesPath(m_sSelectPhotoPath); m_customAlbumViewer->updateViewerSize(); } void Screensaver::onTimeFormatChanged(int type) { timeType = type; } void Screensaver::onDateFormatChanged(QString type) { dateType = type; } void Screensaver::onFontSizeChanged(int fontSize) { curFontSize = fontSize; } void Screensaver::onIsCustomModeChanged(bool custom) { isCustom = custom; if (isCustom) { cycleTime = configuration->getCycleTime(); isAutoSwitch = configuration->getAutoSwitch(); backgroundPath = configuration->getBackgroundPath(); isCShowRestTime = configuration->getCShowRestTime(); textIsCenter = configuration->getTextIsCenter(); myText = configuration->getMyText(); } else isUShowRestTime = configuration->getUShowRestTime(); } void Screensaver::autoSwitchChanged(bool isSwitch) { if (!isCustom || m_isDynamicSaver || m_isUrlSaver) return; isAutoSwitch = isSwitch; if (!isSwitch) { stopSwitchImages(); } startSwitchImages(); } /* * 图片路径改变 */ void Screensaver::backgroundPathChanged(QString path) { backgroundPath = path; if(!isCustom) return ; QFileInfo fileInfo(backgroundPath); bool isUrlValid = false; #ifndef ARCHITECTUREISSW64 isUrlValid = getIsValidUrl(backgroundPath); #endif if (fileInfo.isFile()) { initDynamicSaver(backgroundPath); switch (m_saverState) { case SAVERERRORTYPE::VIDEOFORMATEERROR : screenLabel->setText(tr("The dynamic screensavers video file is corrupted")); break; case SAVERERRORTYPE::VIDEOTOOLARGE : screenLabel->setText(tr("The dynamic screensavers video file is too large")); break; case SAVERERRORTYPE::RESOLUTIONERROR : screenLabel->setText(tr("The dynamic screensavers video file resolution is too high")); break; default: screenLabel->setText(tr("Picture does not exist")); break; } if (m_isDynamicSaver) { m_isUrlSaver = false; } } else if (isUrlValid) { m_saverState = SAVERERRORTYPE::NORMALSAVER; #ifndef ARCHITECTUREISSW64 initUrlWidget(); m_isUrlSaver = true; #endif m_isDynamicSaver = false; } else if (m_saverState == SAVERERRORTYPE::URLADDRESSERROR) { screenLabel->setText(tr("Invalid URL")); } else { m_saverState = SAVERERRORTYPE::NORMALSAVER; m_isDynamicSaver = false; m_isUrlSaver = false; } if (!m_isDynamicSaver && !m_isUrlSaver) { updateBackgroundPath();//更新图片路径 stopSwitchImages(); startSwitchImages(); } if (isCustom && imagePaths.count() == 0 && m_saverState == SAVERERRORTYPE::NORMALSAVER) { screenLabel->setText(tr("Picture does not exist")); } screenLabel->adjustSize(); if (isCustom) initOtherWidget(); QCoreApplication::postEvent(this, new QEvent(QEvent::User)); update(); } void Screensaver::backgroundChanged(QString path) { if (!isScreensaver) { path = mapToThumbnailPath(path); } if (m_nSubMode == 0 || m_nSubMode < 0) { background = loadFromFile(path); } else { background = scaleBlurPixmap(width(), height(), path); } if (m_nSubMode == 3 && m_customAlbumViewer) { m_customAlbumViewer->setDefaultPixmap(loadFromFile(path)); } } void Screensaver::cycleTimeChanged(int cTime) { cycleTime = cTime; if (!isCustom || !autoSwitch || m_isDynamicSaver || m_isUrlSaver) return; stopSwitchImages(); startSwitchImages(); } void Screensaver::myTextChanged(QString text) { if (!isCustom || m_isDynamicSaver || m_isUrlSaver) return; myText = text; if (textIsCenter && m_centerWidget) { if (centerlabel1) centerlabel1->setText(myText); if (centerlabel2) { centerlabel2->setText(""); centerlabel2->hide(); } if (authorlabel) { authorlabel->setText(""); authorlabel->hide(); } m_centerWidget->adjustSize(); m_centerWidget->setGeometry( (width() - m_centerWidget->width()) / 2, (height() - m_centerWidget->height()) / 2, m_centerWidget->width(), m_centerWidget->height()); if ((height() - m_centerWidget->height()) / 2 < timeLayout->y() + timeLayout->height()) m_centerWidget->setGeometry( (width() - m_centerWidget->width()) / 2, timeLayout->y() + timeLayout->height(), m_centerWidget->width(), m_centerWidget->height()); } else { setRandomText(); setRandomPos(); if (m_centerWidget) m_centerWidget->hide(); } if (m_defaulSaverWidget) { m_defaulSaverWidget->setDictumText(centerlabel1->text(), centerlabel2->text(), authorlabel->text()); } } void Screensaver::showCRestTimeChanged(bool isShow) { isCShowRestTime = isShow; if (!isCustom || m_isDynamicSaver || m_isUrlSaver) return; setSleeptime(isCShowRestTime); } void Screensaver::showURestTimeChanged(bool isShow) { isUShowRestTime = isShow; if (!isCustom || m_isDynamicSaver || m_isUrlSaver) return; setSleeptime(isUShowRestTime); } void Screensaver::textIsCenterChanged(bool isCenter) { textIsCenter = isCenter; if (!isCustom || m_isDynamicSaver || m_isUrlSaver) return; if (isCenter) { if (myTextWidget) myTextWidget->hide(); if (!m_centerWidget) { setCenterWidget(); resize(width(), height()); } else { m_centerWidget->show(); myTextChanged(myText); } } else { if (m_centerWidget) m_centerWidget->hide(); setRandomText(); setRandomPos(); } } bool Screensaver::eventFilter(QObject *obj, QEvent *event) { #ifndef USE_INTEL if (obj == this) { if (event->type() == QEvent::MouseButtonPress) { if (!isScreensaver) { if (!process) { process = new QProcess(this); } process->start("ukui-screensaver-command -s"); } if (obj == m_thumBnalListWidget) { return true; } else { return QWidget::eventFilter(obj, event); } } else if (event->type() == QEvent::User) { QResizeEvent fakeResizeEvent(size(), size()); resizeEvent(&fakeResizeEvent); } } if (obj == m_thumBnalListWidget) { if (event->type() == QEvent::Leave) { m_hideThumBnalWidgetTimer->start(2000); } else if (event->type() == QEvent::Enter) { m_hideThumBnalWidgetTimer->stop(); } } if (obj == m_sidebarBtn && event->type() == QEvent::HoverEnter) { if (m_thumBnalListWidget && m_thumBnalListWidget->isHidden()) { m_thumBnalListWidget->show(); m_smartSaverLabel->show(); m_sidebarBtn->move( m_thumBnalListWidget->x() + m_thumBnalListWidget->width(), (height() - m_sidebarBtn->height()) / 2); m_sidebarBtn->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("pan-start-symbolic").pixmap(44, 44), "white")); } } #endif return QWidget::eventFilter(obj, event); } void Screensaver::paintEvent(QPaintEvent *event) { QPainter painter(this); /*时间未同步的时候重新刷新一次,主要是避免睡眠唤醒时的时间跳变*/ /* if (m_currentTime.hour() != QTime::currentTime().hour() || m_currentTime.minute() != QTime::currentTime().minute()) { updateTime(); } */ if (m_isDynamicSaver || m_isUrlSaver) { return ; } else if (isCustom && imagePaths.count() == 0 || m_saverState != SAVERERRORTYPE::NORMALSAVER) { painter.setBrush(Qt::gray); painter.drawRect(-1, -1, this->width() + 1, this->height() + 1); if(screenLabel->isHidden()) { screenLabel->show(); } } else if (m_nSubMode == 0 && isCustom && imagePaths.count() == 0) { painter.setBrush(QColor("#000000")); if (screenLabel->isHidden()) { screenLabel->show(); } } else { if (background.isNull()) { QColor cor = "#035290"; painter.setBrush(cor); painter.drawRect(-1, -1, this->width() + 1, this->height() + 1); } else { painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); if (!isScreensaver) { QPainterPath backgroundPath; backgroundPath.addRoundedRect(this->rect(), 10, 10); painter.setClipPath(backgroundPath); } painter.drawPixmap(this->rect(), background, background.rect()); QPainterPath path; QPainter painter1(this); painter1.setOpacity(0.25); painter1.setRenderHint(QPainter::Antialiasing); // 反锯齿; painter1.setClipping(true); painter1.setPen(Qt::transparent); if (isScreensaver) { path.addRect(this->rect()); } else { path.addRoundedRect(this->rect(), 10, 10); } path.setFillRule(Qt::WindingFill); painter1.setBrush(QColor("#000000")); painter1.setPen(Qt::transparent); painter1.drawPath(path); if (m_nSubMode != 0) { screenLabel->hide(); } } } /*这里是为了不显示笔的线条*/ // painter.drawRect(-1,-1,this->width()+1,this->height()+1); } void Screensaver::isMovie() { if (!currentPixmap.isEmpty()) { currentPixmap.clear(); currentCount = 0; if (movieTimer) { movieTimer->stop(); movieTimer->deleteLater(); movieTimer = nullptr; } } if (!is_gif) { repaint(); return; } QMovie *movie = new QMovie(currentPath); movie->setScaledSize(QSize(this->width(), this->height())); for (int i = 0; i < movie->frameCount(); ++i) { movie->jumpToFrame(i); currentPixmap.append(movie->currentPixmap()); if (i == 0) delayTime = movie->nextFrameDelay(); } if (!movieTimer) { movieTimer = new QTimer(this); connect(movieTimer, &QTimer::timeout, this, [&] { if (currentCount == currentPixmap.count() - 1) currentCount = 0; else currentCount += 1; background = currentPixmap.at(currentCount); repaint(); }); } if (delayTime < 50) movieTimer->start(50); else movieTimer->start(delayTime); } void Screensaver::getVideoExistence() { qDebug() << "videoPath = " << m_videoPath << "videoSize = " << m_videoSize << "videoFormat = " << m_videoFormat; if (m_nSubMode == 0) { QFileInfo fileInfo(m_videoPath); // 视频文件是否存在 if (fileInfo.isFile()) { // 视频文件大小限制 if (fileInfo.size() <= m_videoSize * 1024 * 1024) { getVideoFormat(m_videoPath); QStringList formatList = m_videoFormat.split(','); // 视频格式限制 bool m_rightFormat = false; for (int formatAt = 0; formatAt < formatList.count(); formatAt++) { if (cur_video_format.contains(formatList.at(formatAt))) { m_rightFormat = true; break; } } if (m_rightFormat) { // 分辨率限制 if (cur_video_Width <= m_videoWidth && cur_video_Height <= m_videoHeight) { m_isDynamicSaver = true; } else { qInfo() << "High video resolution!!! resolution = " << cur_video_Width << "x" << cur_video_Height; } } else { qInfo() << "The video format is not supported!!! format = " << cur_video_format << "video_Codetype = " << m_videoFormat; } } else { qInfo() << "Video file too large!!!"; } } else { qInfo() << "The video file does not exist!!!"; } } else { m_isDynamicSaver = false; qDebug() << "The nSubMode is not 0!!!"; } } QPixmap Screensaver::getPaddingPixmap() { if (background.width() == 0 || background.height() == 0) { return QPixmap(); } bool useHeight; float scaled = 0.0; QPixmap scaledPixmap; QPixmap paddingPixmap; qint64 rw = qint64(this->height()) * qint64(background.width()) / qint64(background.height()); useHeight = (rw >= this->width()); if (useHeight) { scaled = float(this->height()) / float(background.height()); scaledPixmap = background.scaled(background.width() * scaled, this->height()); paddingPixmap = scaledPixmap.copy((background.width() * scaled - this->width()) / 2, 0, this->width(), this->height()); } else { scaled = float(this->width()) / float(background.width()); scaledPixmap = background.scaled(this->width(), background.height() * scaled); paddingPixmap = scaledPixmap.copy(0, (background.height() * scaled - this->height()) / 2, this->width(), this->height()); } return paddingPixmap; } void Screensaver::addClickedEvent() { respondClick = true; isScreensaver = false; } void Screensaver::resizeEvent(QResizeEvent * /*event*/) { if (m_isDynamicSaver) { if (m_playerView) { m_playerView->setFixedSize(this->size()); m_playerItem->setSize(this->size()); } return; } else if (m_isUrlSaver) { #ifndef ARCHITECTUREISSW64 m_pCenterUrlWidget->setFixedSize(this->size()); m_pWebEngineView->setFixedSize(this->size()); if (m_pErrorUrlLabel) { m_pErrorUrlLabel->setFixedSize(this->size()); m_pErrorUrlLabel->setPixmap(QPixmap(":/assets/error-resolved.png").scaled(this->size())); } return; #endif } float scale = 1.0; scale = (float)width() / 1920; if ((width() < 600 || height() < 400) && !isScreensaver) { // 当显示在控制面板上时,字体缩小三倍。 if (flag == 0) { QList labelList = this->findChildren(); for (int i = 0; i < labelList.count(); i++) { if (labelList.at(i) != myPreviewLabel && labelList.at(i)->parentWidget() != m_weatherWidget) { int fontsize = labelList.at(i)->font().pixelSize(); int fontpt = labelList.at(i)->font().pointSize(); if (fontsize > 0) { #ifdef USE_INTEL const QString SheetStyle = QString("font-size:%1px;").arg(fontsize / 3); #else const QString SheetStyle = QString("font-size:%1px;").arg(fontsize / 4); #endif labelList.at(i)->setStyleSheet(SheetStyle); } else { QFont font = labelList.at(i)->font(); #ifdef USE_INTEL font.setPointSize(fontpt / 3); #else font.setPointSize(fontpt / 4); #endif labelList.at(i)->setFont(font); } } } QList childList = timeLayout->findChildren(); for (int i = 0; i < childList.count(); ++i) { childList.at(i)->adjustSize(); } timeLayout->adjustSize(); if (m_widgetDatetime) { m_widgetDatetime->adjustSize(); } if (m_centerWidget) m_centerWidget->adjustSize(); } flag = 1; #ifndef USE_INTEL if (myTextWidget) { if (curStyle == "ukui-dark" || curStyle == "ukui-black") { myTextLabel->setStyleSheet( QString("QLabel{font-size: 5px; background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius: 2px; " "padding: 4px 8px 4px 8px; border-width: 1px;}") .arg(blur_Num * 0.01)); cycleLabel->setStyleSheet( QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:3px}") .arg(blur_Num * 0.01)); } else { myTextLabel->setStyleSheet(QString("QLabel{font-size: 5px; background: rgba(255, 255, 255, %1); " "border-radius:2px; padding: 4px 8px 4px 8px; border-width: 1px;}") .arg(blur_Num * 0.01)); cycleLabel->setStyleSheet( QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:3px}").arg(blur_Num * 0.01)); } cycleLabel->setFixedSize(6, 6); } if (screenLabel) screenLabel->adjustSize(); if (sleepTime) sleepTime->setSmallMode(); #endif scale = 0.1; } #ifdef USE_INTEL int x = 840 * scale; int y = 96 * scale; m_weatherLaout->setGeometry( (this->width() - m_weatherLaout->width()) / 2, y, m_weatherLaout->geometry().width(), m_weatherLaout->geometry().height()); timeLayout->setGeometry( (this->width() - timeLayout->width()) / 2, m_weatherLaout->geometry().bottom() + 33, timeLayout->geometry().width(), timeLayout->geometry().height()); if (m_centerWidget) { m_centerWidget->adjustSize(); m_centerWidget->setGeometry( (width() - m_centerWidget->width()) / 2, (height() * 85) / 100 - (m_centerWidget->height()) / 2, m_centerWidget->width(), m_centerWidget->height()); if ((height() - m_centerWidget->height()) / 2 < timeLayout->y() + timeLayout->height()) m_centerWidget->setGeometry( (width() - m_centerWidget->width()) / 2, timeLayout->y() + timeLayout->height(), m_centerWidget->width(), m_centerWidget->height()); } if (m_widgetNotice) { m_widgetNotice->setGeometry( (this->width() - m_widgetNotice->width()) / 2, (m_centerWidget->y() + m_centerWidget->height() + 20), m_widgetNotice->geometry().width(), m_widgetNotice->geometry().height()); } #else int x, y; if (m_nSubMode == 0 || m_nSubMode < 0) { x = (this->width() - m_widgetDatetime->geometry().width()) / 2; y = 80 * scale; if (m_widgetDatetime) { m_widgetDatetime->setGeometry( x, y, m_widgetDatetime->geometry().width(), m_widgetDatetime->geometry().height()); } if (m_weatherWidget) { m_weatherWidget->hide(); } if (m_widgetMediaControl) { m_widgetMediaControl->hide(); } if (m_customAlbumViewer) { m_customAlbumViewer->hide(); } } else { x = 284 * scale; y = 416 * scale; m_widgetDatetime->setGeometry( x, (height() - m_widgetDatetime->height()) / 2, m_widgetDatetime->geometry().width(), m_widgetDatetime->geometry().height()); if (m_centerWidget) { m_centerWidget->hide(); } if (myTextWidget) myTextWidget->hide(); if ((m_nSubMode == 1 || m_nSubMode == 2 || m_nSubMode == 3) && !m_isDynamicSaver) { onScreensaverDatetimeEnableChanged(m_isShowDateTime, getenv("USER")); } } if (m_thumBnalListWidget) { m_thumBnalListWidget->setGeometry(0, m_smartSaverLabel->height() + 70 * scale, 350 + 20 * scale, this->height() - (m_smartSaverLabel->height() + 70 * scale) - (sleepTime->height() + 26 * scale)); } if (m_smartSaverLabel) { m_smartSaverLabel->setGeometry(0, 35 * scale, m_thumBnalListWidget->width() - 16, 50); } if (m_sidebarBtn) { m_sidebarBtn->setFixedSize(44 * scale, 136 * scale); m_sidebarBtn->setIconSize(QSize(44 * scale, 44 * scale)); if (m_thumBnalListWidget && m_thumBnalListWidget->isVisible()) { m_sidebarBtn->move(m_thumBnalListWidget->width(), (height() - m_sidebarBtn->height()) / 2); m_sidebarBtn->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("pan-start-symbolic").pixmap(44, 44), "white")); } else if (m_thumBnalListWidget && m_thumBnalListWidget->isHidden()) { m_sidebarBtn->move(0, (height() - m_sidebarBtn->height()) / 2); m_sidebarBtn->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("pan-end-symbolic").pixmap(44, 44), "white")); } } if (m_nSubMode == 1 && m_weatherWidget) { if ((width() < 600 || height() < 400) && !isScreensaver) { m_weatherWidget->setFixedSize(534 * 0.15, 770 * 0.15); m_weatherWidget->move( width() - m_weatherWidget->width() - 280 * 0.15, (this->height() - m_weatherWidget->height()) / 2); m_weatherWidget->show(); m_weatherWidget->updateWeatherSize(0.15); } else { m_weatherWidget->setFixedSize(534 * scale, 770 * scale); m_weatherWidget->move( width() - m_weatherWidget->width() - 280 * scale, (this->height() - m_weatherWidget->height()) / 2); m_weatherWidget->show(); m_weatherWidget->updateWeatherSize(scale); } m_weatherManager->initNetworkStatus(); if (m_widgetMediaControl) { m_widgetMediaControl->hide(); } if (m_customAlbumViewer) { m_customAlbumViewer->hide(); } if (m_weatherWidget->x() - m_widgetDatetime->width() > 0) { m_widgetDatetime->move((m_weatherWidget->x() - m_widgetDatetime->width()) / 2, (height() - m_widgetDatetime->height()) / 2); } else { m_widgetDatetime->move(0, (height() - m_widgetDatetime->height()) / 2); } } if (m_nSubMode == 2 && m_widgetMediaControl) { if ((width() < 600 || height() < 400) && !isScreensaver) { m_widgetMediaControl->setFixedSize(521 * 0.15, 620 * 0.15); m_widgetMediaControl->move( width() - m_widgetMediaControl->width() - 280 * 0.15, (this->height() - m_widgetMediaControl->height()) / 2); m_widgetMediaControl->show(); m_mediaControlWidget->updateMediaSize(0.15); } else { m_widgetMediaControl->setFixedSize(521 * scale, 620 * scale); m_widgetMediaControl->move( width() - m_widgetMediaControl->width() - 280 * scale, (this->height() - m_widgetMediaControl->height()) / 2); m_widgetMediaControl->show(); m_mediaControlWidget->updateMediaSize(scale); } if (m_weatherWidget) { m_weatherWidget->hide(); } if (m_customAlbumViewer) { m_customAlbumViewer->hide(); } if (m_widgetMediaControl->x() - m_widgetDatetime->width() > 0) { m_widgetDatetime->move((m_widgetMediaControl->x() - m_widgetDatetime->width()) / 2, (height() - m_widgetDatetime->height()) / 2); } else { m_widgetDatetime->move(0, (height() - m_widgetDatetime->height()) / 2); } } if (m_nSubMode == 3 && m_customAlbumViewer) { if ((width() < 600 || height() < 400) && !isScreensaver) { m_customAlbumViewer->show(); m_customAlbumViewer->setFixedSize(847 * 0.15, 790 * 0.15); m_customAlbumViewer->updateViewerSize(0.15); m_customAlbumViewer->move( width() - m_customAlbumViewer->width() - 100 * 0.15, (this->height() - m_customAlbumViewer->height()) / 2); } else { m_customAlbumViewer->show(); m_customAlbumViewer->setFixedSize(847 * scale, 790 * scale); m_customAlbumViewer->updateViewerSize(scale); m_customAlbumViewer->move( width() - m_customAlbumViewer->width() - 100 * scale, (this->height() - m_customAlbumViewer->height()) / 2); } m_customAlbumViewer->setUpdateInterval(m_iPhotoSwitchTime); if (m_weatherWidget) { m_weatherWidget->hide(); } if (m_widgetMediaControl) { m_widgetMediaControl->hide(); } if (m_widgetDatetime) { if (m_customAlbumViewer->x() - m_widgetDatetime->width() > 0) { m_widgetDatetime->move((m_customAlbumViewer->x() - m_widgetDatetime->width()) / 2, (height() - m_widgetDatetime->height()) / 2); } else { m_widgetDatetime->move(0, (height() - m_widgetDatetime->height()) / 2); } } } if (sleepTime) { x = 26 * scale; y = this->height() - sleepTime->geometry().height() - 26 * scale; sleepTime->setGeometry(x, y, sleepTime->geometry().width(), sleepTime->geometry().height()); } if (screenLabel) { screenLabel->setGeometry((width() - screenLabel->width()) / 2, y, screenLabel->width(), screenLabel->height()); } if (m_centerWidget) { m_centerWidget->adjustSize(); m_centerWidget->setGeometry( (width() - m_centerWidget->width()) / 2, (height() - m_centerWidget->height()) / 2, m_centerWidget->width(), m_centerWidget->height()); if ((height() - m_centerWidget->height()) / 2 < m_widgetDatetime->y() + m_widgetDatetime->height()) m_centerWidget->setGeometry( (width() - m_centerWidget->width()) / 2, m_widgetDatetime->y() + m_widgetDatetime->height(), m_centerWidget->width(), m_centerWidget->height()); } if (myTextWidget) setRandomPos(); #endif } void Screensaver::setRandomPos() { myTextWidget->adjustSize(); int x1 = 10; int x2 = width() - 10 - myTextWidget->width(); int y1 = timeLayout->geometry().bottom() + 10; int y2; if (sleepTime) y2 = sleepTime->geometry().top() - myTextWidget->height() - 10; else y2 = geometry().bottom() - myTextWidget->height() - 10; int x = 0; int y = 0; if (x2 > x1) x = QRandomGenerator::global()->bounded(x2 - x1) + x1; if (y2 > y1) y = QRandomGenerator::global()->bounded(y2 - y1) + y1; myTextWidget->move(x, y); } void Screensaver::setUpdateCenterWidget() { // QString lang = qgetenv("LANG"); QString lang = QString(getenv("LANGUAGE")); QString homePath = qgetenv("HOME"); QString cwdPath = "/usr/share/ukui-screensaver/"; QString languageDirPath = cwdPath + "language/"; QString defaultLanguageFilePath = languageDirPath + "screensaver-en_US.ini"; qDebug() << "homePath=" << homePath; if (!lang.isEmpty()) { qDebug() << "lang = " << lang; if (lang.contains(":")) { lang = lang.split(":").at(0); } if (lang.contains('.')) { lang = lang.split('.')[0]; qDebug() << "langStr = " << lang; } // 不管系统语言,默认显示中文 QString languageFilePath = languageDirPath + "screensaver-zh_CN.ini"; if (isOpenkylin()) { languageFilePath = languageDirPath + "screensaver-zh_CN-ok.ini"; } // QString languageFilePath=languageDirPath+"screensaver-"+lang+".ini"; QString homeLanguageFilePath = homePath + "/.config/ukui/screensaver-" + lang + ".ini"; QString jdLanguageFilePath = languageDirPath + "screensaver-jd" + ".ini"; qDebug() << "langnguageFile = " << languageFilePath; qDebug() << "homeLanguageFilePath = " << homeLanguageFilePath; QFileInfo fileInfo(languageFilePath); QFileInfo homeConfigFileInfo(homeLanguageFilePath); QFileInfo jdConfigFileInfo(jdLanguageFilePath); bool useJd = false; QDate date1(2021, 6, 20); QDate date2(2021, 7, 31); if (QDate::currentDate() >= date1 && QDate::currentDate() <= date2) { useJd = true; } if (useJd && jdConfigFileInfo.isFile()) { qsettings = new QSettings(jdLanguageFilePath, QSettings::IniFormat); } else if (homeConfigFileInfo.isFile()) { qsettings = new QSettings(homeLanguageFilePath, QSettings::IniFormat); } else if (fileInfo.isFile()) { qsettings = new QSettings(languageFilePath, QSettings::IniFormat); } else { qsettings = new QSettings(defaultLanguageFilePath, QSettings::IniFormat); } } else { qsettings = new QSettings(defaultLanguageFilePath, QSettings::IniFormat); } } void Screensaver::updateBackgroundPath() { qDebug() << "ScreenSaver::startSwitchImages"; QFileInfo fileInfo(backgroundPath); imagePaths.clear(); if (fileInfo.isFile()) return; QList formats = QImageReader::supportedImageFormats(); if (fileInfo.isDir()) { QDir dir(backgroundPath); QStringList files = dir.entryList(QDir::Files | QDir::Readable); for (QString file : files) { fileInfo.setFile(file); QString suffix = fileInfo.suffix(); if (formats.contains(suffix.toUtf8()) && file.right(4) != ".ico" && file.right(4) != ".tga") { // qDebug() << "file:" <bounded(imagePaths.count()); background = loadFromFile(imagePaths.at(index)); currentPath = imagePaths.at(index); is_gif = currentPath.endsWith(".gif"); } else { if (currentIndex >= imagePaths.count() - 1 || currentIndex < 0) { currentIndex = 0; } else { currentIndex++; } background = loadFromFile(imagePaths.at(currentIndex)); currentPath = imagePaths.at(currentIndex); is_gif = currentPath.endsWith(".gif"); } isMovie(); }); } switchTimer->start(cycleTime * 1000); isMovie(); } } void Screensaver::stopSwitchImages() { if (switchTimer && switchTimer->isActive()) switchTimer->stop(); } void Screensaver::initDynamic() { if (m_isDynamicSaver) { if (m_playerView) return; m_pVideoPlayer = new KyVideoPlayer(); m_playerScene = new QGraphicsScene(this); m_playerView = new GraphicsView(this); m_playerView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_playerView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_playerView->setStyleSheet("border: none; border-radius: 0px;"); m_playerView->setMouseTracking(true); m_playerItem = new QGraphicsVideoItem; m_playerScene->addItem(m_playerItem); m_pVideoPlayer->setMediaFile(m_videoPath); m_playerView->setScene(m_playerScene); m_pVideoPlayer->setOutput(m_playerItem, video_Duration); m_playerView->show(); } else { if (isCustom) setSleeptime(isCShowRestTime); else setSleeptime(isUShowRestTime); if (!m_widgetDatetime) { setDatelayout(); } if (!m_centerWidget) { setCenterWidget(); } setRandomText(); if (m_centerWidget) { if (textIsCenter || myText == "") { myTextWidget->hide(); m_centerWidget->show(); } else { m_centerWidget->hide(); myTextWidget->show(); } } } } void Screensaver::initUI() { QFile qssFile; #ifdef USE_INTEL qssFile.setFileName(":/qss/assets/default-intel.qss"); #else qssFile.setFileName(":/qss/assets/default.qss"); #endif if (qssFile.open(QIODevice::ReadOnly)) { setStyleSheet(qssFile.readAll()); } qssFile.close(); #ifdef USE_INTEL setWeatherLayout(); setDatelayout(); setCenterWidget(); // setNoticeLaout(); m_weatherManager->getWeather(); connect(m_weatherManager, &WeatherManager::onWeatherUpdate, this, &Screensaver::getWeatherFinish); #else if (isCustom) setSleeptime(isCShowRestTime); else setSleeptime(isUShowRestTime); setDatelayout(); setCenterWidget(); setRandomText(); if(m_centerWidget) { if (textIsCenter || myText == "") { myTextWidget->hide(); m_centerWidget->show(); }else{ m_centerWidget->hide(); myTextWidget->show(); } } QFileInfo fileInfo(backgroundPath); bool isUrlValid = false; #ifndef ARCHITECTUREISSW64 isUrlValid = getIsValidUrl(backgroundPath); #endif if (isCustom) { if (fileInfo.isFile()) { initDynamicSaver(backgroundPath); if (m_isDynamicSaver) { m_isUrlSaver = false; } } else if (isUrlValid) { #ifndef ARCHITECTUREISSW64 initUrlWidget(); m_isUrlSaver = true; #endif m_isDynamicSaver = false; } else { m_isDynamicSaver = false; m_isUrlSaver = false; } } initOtherWidget(); if (m_nSubMode == 1) { initWeatherWidget(); if (myTextWidget) myTextWidget->hide(); } if (m_nSubMode == 2) { initMediaControlWidget(); if (myTextWidget) myTextWidget->hide(); } if (m_nSubMode == 3) { initCustomAlbumViewer(); if (myTextWidget) myTextWidget->hide(); } initThumBnalListWidget(); if (m_isDynamicSaver || m_isUrlSaver) { if (m_smartSaverLabel) { m_smartSaverLabel->hide(); } if (m_thumBnalListWidget) { m_thumBnalListWidget->hide(); } if (m_sidebarBtn) { m_sidebarBtn->hide(); } } // V11上在窗口关闭时会收到一个leaveEvent(),控制面板关闭的时候new这个label,会导致控制面板关闭卡顿。 if (!myPreviewLabel) { myPreviewLabel = new QLabel(this); myPreviewLabel->setFixedHeight(30); myPreviewLabel->setMinimumWidth(54); // 设置样式 myPreviewLabel->setStyleSheet("background-color: rgb(38,38,38); border-radius: 0px; color:white;"); myPreviewLabel->setAlignment(Qt::AlignCenter); myPreviewLabel->hide(); } #endif } void Screensaver::initDynamicSaver(QString path) { qDebug() << "videoPath = " << path << "videoSize = " << m_videoSize << "videoFormat = " << m_videoFormat; QFileInfo fileInfo(path); //视频文件是否存在 if (fileInfo.isFile()) { //视频文件大小限制 if (fileInfo.size() <= m_videoSize * 1024 * 1024) { getVideoFormat(path); QStringList formatList = m_videoFormat.split(','); //视频格式限制 bool m_rightFormat = false; for (int formatAt = 0; formatAt < formatList.count(); formatAt++) { if (cur_video_format.contains(formatList.at(formatAt))) { m_rightFormat = true; break; } } if (m_rightFormat) { //分辨率限制 if (cur_video_Width <= m_videoWidth && cur_video_Height <= m_videoHeight) { m_isDynamicSaver = true; m_saverState = SAVERERRORTYPE::NORMALSAVER; if (!m_pVideoPlayer) { m_pVideoPlayer = new KyVideoPlayer(); connect(m_pVideoPlayer, &KyVideoPlayer::playerShowError, this, [=](QMediaPlayer::Error error) { switch (error) { case QMediaPlayer::ResourceError: qDebug() << "媒体资源无法被解析!"; m_sPlayerShowErrorTip = tr("The property cannot be parsed!"); break; case QMediaPlayer::FormatError: qDebug() << "不支持该媒体格式!"; m_sPlayerShowErrorTip = tr("This media format is not supported!"); break; case QMediaPlayer::NetworkError: qDebug() << "发生了一个网络错误!"; m_sPlayerShowErrorTip = tr("A network error has occurred!"); break; case QMediaPlayer::AccessDeniedError: qDebug() << "没有播放权限!"; m_sPlayerShowErrorTip = tr("No playback privileges!"); break; default: break; } m_isDynamicSaver = false; m_saverState = SAVERERRORTYPE::PLAYERSHOWERROR; m_playerView->hide(); screenLabel->setText(m_sPlayerShowErrorTip); screenLabel->adjustSize(); initOtherWidget(); }); } if (!m_playerScene) m_playerScene = new QGraphicsScene(this); if (!m_playerView) { m_playerView = new GraphicsView(this); m_playerView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_playerView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_playerView->setStyleSheet("border: none; border-radius: 0px;"); } if (!m_playerItem) m_playerItem = new QGraphicsVideoItem; m_playerScene->addItem(m_playerItem); m_pVideoPlayer->setMediaFile(backgroundPath); m_playerView->setScene(m_playerScene); m_pVideoPlayer->setOutput(m_playerItem, video_Duration); m_playerView->show(); } else { if (formatList.contains(fileInfo.suffix())) { m_saverState = SAVERERRORTYPE::RESOLUTIONERROR; } m_isDynamicSaver = false; qInfo() << "High video resolution!!! resolution = " << cur_video_Width << "x" << cur_video_Height; } } else { if (formatList.contains(fileInfo.suffix())) { m_saverState = SAVERERRORTYPE::VIDEOFORMATEERROR; } m_isDynamicSaver = false; qInfo() << "The video format is not supported!!! format = " << cur_video_format << "video_Codetype = " << video_Codetype; } } else { QStringList formatList = m_videoFormat.split(','); if (formatList.contains(fileInfo.suffix())) { m_saverState = SAVERERRORTYPE::VIDEOTOOLARGE; } m_isDynamicSaver = false; qInfo() << "Video file too large!!!" ; } } else { m_isDynamicSaver = false; qInfo() << "The video file does not exist!!!"; } } #ifndef ARCHITECTUREISSW64 void Screensaver::initUrlWidget() { if (!m_pCenterUrlWidget) { m_pCenterUrlWidget = new QWidget(this); } if (!m_pWebEngineView) { m_pWebEngineView = new QWebEngineView(m_pCenterUrlWidget); m_pWebEngineView->setEnabled(false); //禁用网页鼠标交互 QWebEngineSettings *settings = m_pWebEngineView->page()->settings(); settings->setAttribute(QWebEngineSettings::ShowScrollBars, false); QWebEngineProfile *engineProfile = m_pWebEngineView->page()->profile(); engineProfile->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); engineProfile->setHttpCacheType(QWebEngineProfile::NoCache); //清空网页缓存 } qDebug()<<"load url = " << backgroundPath; QTimer::singleShot(100, this, [=]() { m_pWebEngineView->load(QUrl(backgroundPath)); }); m_pWebEngineView->load(QUrl(backgroundPath)); m_pWebEngineView->setFocusPolicy(Qt::NoFocus); m_pWebEngineView->setContextMenuPolicy(Qt::NoContextMenu); connect(m_pWebEngineView, &QWebEngineView::loadFinished, this, [=](bool ok) { qDebug() << "loadFinished: " << ok; }); connect(m_pWebEngineView, &QWebEngineView::urlChanged, this, [=](const QUrl& url) { qDebug() << "urlChanged: " << url; if (url == QUrl("about:blank")) { qDebug() << "backgroundPath = " << backgroundPath; m_pErrorUrlLabel = new QLabel(m_pCenterUrlWidget); m_pErrorUrlLabel->setPixmap(QPixmap(":/assets/error-resolved.png")); m_pWebEngineView->hide(); m_pErrorUrlLabel->show(); } }); m_pWebEngineView->show(); m_pCenterUrlWidget->show(); } #endif void Screensaver::initOtherWidget() { if (isCustom && (m_isDynamicSaver || m_isUrlSaver)) { #ifndef ARCHITECTUREISSW64 if (m_isDynamicSaver && m_pWebEngineView) { m_pWebEngineView->stop(); m_pWebEngineView->hide(); m_pCenterUrlWidget->hide(); } if (m_isUrlSaver && m_pVideoPlayer) { m_pVideoPlayer->stop(); m_pVideoPlayer->hide(); m_playerView->hide(); } #endif dateOfDay->hide(); dateOfLocaltime->hide(); setSleeptime(false); if (myTextWidget) { myTextWidget->hide(); } if (m_centerWidget) { m_centerWidget->hide(); } if (screenLabel) { screenLabel->hide(); } } else { #ifndef ARCHITECTUREISSW64 if (m_pWebEngineView) { m_pWebEngineView->stop(); m_pWebEngineView->hide(); m_pCenterUrlWidget->hide(); } #endif if (m_pVideoPlayer) { m_pVideoPlayer->stop(); m_pVideoPlayer->hide(); m_playerView->hide(); } if (isCustom) setSleeptime(isCShowRestTime); else setSleeptime(isUShowRestTime); dateOfDay->show(); dateOfLocaltime->show(); if (textIsCenter && !m_centerWidget) { setCenterWidget(); } else if (textIsCenter && m_centerWidget) { m_centerWidget->show(); } if (isCustom && !textIsCenter) { setRandomText(); } if (screenLabel) screenLabel->hide(); } QCoreApplication::postEvent(this, new QEvent(QEvent::User)); } void Screensaver::setDatelayout() { timeType = configuration->getTimeType(); dateType = configuration->getDateType(); #ifdef USE_INTEL timeLayout = new QWidget(this); timeLayout->setFixedSize(((timeType == 12) ? 397 : 326), 117); QHBoxLayout *htimeLayout = new QHBoxLayout(timeLayout); htimeLayout->setContentsMargins(0, 0, 0, 0); QWidget *timeWidget = new QWidget(this); timeWidget->setFixedSize(235, 117); QGridLayout *gtimeLayout = new QGridLayout(timeWidget); gtimeLayout->setContentsMargins(0, 0, 0, 12); this->dateOfLocaltimeHour = new QLabel(this); this->dateOfLocaltimeHour->setObjectName("dateOfLocaltime"); this->dateOfLocaltimeHour->setAlignment(Qt::AlignTop | Qt::AlignLeft); this->dateOfLocaltimeHour->setFixedSize(107, 96); this->dateofLocaltimeColon = new QLabel(this); this->dateofLocaltimeColon->setObjectName("dateOfLocaltime"); this->dateofLocaltimeColon->setAlignment(Qt::AlignCenter); this->dateofLocaltimeColon->setFixedSize(21, 96); this->dateofLocaltimeColon->setText(":"); this->dateOfLocaltimeMinute = new QLabel(this); this->dateOfLocaltimeMinute->setObjectName("dateOfLocaltime"); this->dateOfLocaltimeMinute->setAlignment(Qt::AlignTop | Qt::AlignLeft); this->dateOfLocaltimeMinute->setFixedSize(107, 96); gtimeLayout->setSpacing(0); gtimeLayout->setRowMinimumHeight(0, 9); gtimeLayout->setRowMinimumHeight(1, 87); gtimeLayout->setRowMinimumHeight(2, 9); gtimeLayout->setColumnMinimumWidth(0, 107); gtimeLayout->setColumnMinimumWidth(1, 21); gtimeLayout->setColumnMinimumWidth(2, 107); gtimeLayout->addWidget(dateOfLocaltimeHour, 1, 0, 2, 1); gtimeLayout->addWidget(dateofLocaltimeColon, 0, 1, 2, 1); gtimeLayout->addWidget(dateOfLocaltimeMinute, 1, 2, 2, 1); QWidget *dateWidget = new QWidget(this); dateWidget->setFixedSize(((timeType == 12) ? 154 : 83), 117); QVBoxLayout *vDateLaout = new QVBoxLayout(dateWidget); vDateLaout->setAlignment(Qt::AlignTop | Qt::AlignLeft); vDateLaout->setContentsMargins(0, 18, 0, 12); this->dateOfWeek = new QLabel(this); this->dateOfWeek->setObjectName("dateOfWeek"); this->dateOfWeek->setAlignment(Qt::AlignTop | Qt::AlignLeft); this->dateOfWeek->setFixedWidth(83); this->dateOfDay = new QLabel(this); this->dateOfDay->setObjectName("dateOfDay"); this->dateOfDay->setAlignment(Qt::AlignTop | Qt::AlignLeft); this->dateOfDay->setFixedSize(((timeType == 12) ? 154 : 83), 32); updateTime(); updateDate(); this->dateOfWeek->adjustSize(); vDateLaout->addWidget(dateOfWeek); vDateLaout->setSpacing(4); vDateLaout->addWidget(dateOfDay); htimeLayout->addWidget(timeWidget); htimeLayout->addSpacing(8); htimeLayout->addWidget(dateWidget); #else timeLayout = new QWidget(this); QVBoxLayout *vtimeLayout = new QVBoxLayout(timeLayout); this->dateOfLocaltime = new QLabel(this); sysFont = qApp->font(); sysFont.setPointSize((80 + curFontSize) * m_ptToPx); this->dateOfLocaltime->setFont(sysFont); // if (timeType == 12) // this->dateOfLocaltime->setText(getLongFormatDate(TIME)); // else this->dateOfLocaltime->setText(getLongFormatDate(TIME)); this->dateOfLocaltime->setObjectName("dateOfLocaltime"); this->dateOfLocaltime->setAlignment(Qt::AlignCenter); this->dateOfLocaltime->adjustSize(); vtimeLayout->addWidget(dateOfLocaltime); this->dateOfDay = new QLabel(this); sysFont.setPointSize((36 + curFontSize) * m_ptToPx); this->dateOfDay->setFont(sysFont); // if(dateType == "cn") // this->dateOfDay->setText(QDate::currentDate().toString("yyyy/MM/dd ddd").replace("周","星期")); // else // this->dateOfDay->setText(QDate::currentDate().toString("yyyy-MM-dd ddd").replace("周","星期")); this->dateOfDay->setText(getLongFormatDate(DATE)); this->dateOfDay->setObjectName("dateOfDay"); this->dateOfDay->setAlignment(Qt::AlignCenter); this->dateOfDay->adjustSize(); vtimeLayout->addWidget(this->dateOfDay); timeLayout->adjustSize(); updateDate(); m_widgetDatetime = new QWidget(this); QHBoxLayout *layoutDatetime = new QHBoxLayout(m_widgetDatetime); layoutDatetime->setContentsMargins(0, 0, 0, 0); layoutDatetime->setSpacing(0); layoutDatetime->addWidget(timeLayout); m_widgetDatetime->adjustSize(); onScreensaverDatetimeEnableChanged(m_isShowDateTime, getenv("USER")); #endif } void Screensaver::setWeatherLayout() { m_weatherLaout = new QWidget(this); QHBoxLayout *hWeatherLayout = new QHBoxLayout(m_weatherLaout); hWeatherLayout->setContentsMargins(0, 0, 0, 0); this->m_weatherIcon = new QLabel(this); this->m_weatherArea = new QLabel(this); this->m_weatherCond = new QLabel(this); this->m_weatherTemperature = new QLabel(this); m_weatherIcon->setPixmap(m_weatherManager->getWeatherIcon()); m_weatherArea->setText(m_weatherManager->getCityName()); if (!m_weatherManager->getCond().isEmpty()) m_weatherCond->setText("·" + m_weatherManager->getCond()); m_weatherTemperature->setText(m_weatherManager->getTemperature()); m_weatherArea->setStyleSheet("font-size:26px;color:#ffffff"); m_weatherCond->setStyleSheet("font-size:26px;color:#ffffff"); m_weatherTemperature->setStyleSheet("font-size:26px;color:#ffffff"); hWeatherLayout->addWidget(m_weatherIcon); hWeatherLayout->addSpacing(8); hWeatherLayout->addWidget(m_weatherArea); hWeatherLayout->addWidget(m_weatherCond); hWeatherLayout->addSpacing(8); hWeatherLayout->addWidget(m_weatherTemperature); m_weatherLaout->adjustSize(); } void Screensaver::setSleeptime(bool Isshow) { if (!sleepTime) sleepTime = new SleepTime(this); sleepTime->adjustSize(); if (Isshow) { sleepTime->show(); } else { sleepTime->hide(); } } void Screensaver::updateDate() { if (!timer) { timer = new QTimer(this); timer->setTimerType(Qt::PreciseTimer); connect(timer, SIGNAL(timeout()), this, SLOT(updateTime())); } timer->start(500); updateTime(); } void Screensaver::updateTime() { #ifdef USE_INTEL QLocale locale(QLocale::system().name()); QTimeZone timeZone(QString::fromLatin1(QTimeZone::systemTimeZoneId()).toLatin1()); QDateTime tzNow = QDateTime::currentDateTime().toTimeZone(timeZone); QString time; if (timeType == 12) { time = tzNow.toString("hh:mm AP").split(" ").at(0); this->dateOfWeek->setText(tzNow.toString("AP")); if (dateType == "en") { this->dateOfDay->setText(tzNow.toString("ddd MM-dd")); } else { this->dateOfDay->setText(tzNow.toString("ddd MM/dd")); } } else { time = tzNow.toString("hh:mm"); this->dateOfWeek->setText(tzNow.toString("ddd")); if (dateType == "en") { this->dateOfDay->setText(tzNow.toString("MM-dd")); } else { this->dateOfDay->setText(tzNow.toString("MM/dd")); } } this->dateOfLocaltimeHour->setText(time.split(":").at(0)); this->dateOfLocaltimeMinute->setText(time.split(":").at(1)); m_currentTime = QTime::currentTime(); #else QDateTime curDateTime = QDateTime::currentDateTime(); if (m_lastDateTime.isNull() || qAbs(curDateTime.secsTo(m_lastDateTime)) >= 1) { if (timeType == 12) this->dateOfLocaltime->setText(getLongFormatDate(TIME)); else this->dateOfLocaltime->setText(getLongFormatDate(TIME)); // if(dateType == "cn") // this->dateOfDay->setText(curDateTime.date().toString("yyyy/MM/dd ddd").replace("周","星期")); // else // this->dateOfDay->setText(curDateTime.date().toString("yyyy-MM-dd ddd").replace("周","星期")); this->dateOfDay->setText(getLongFormatDate(DATE)); m_lastDateTime = curDateTime; } if (sleepTime) { if (!sleepTime->setTime(QDateTime::currentDateTime())) { sleepTime->hide(); delete sleepTime; sleepTime = NULL; } } #endif } void Screensaver::setRandomText() { if (!myTextWidget) { myTextWidget = new QWidget(this); QHBoxLayout *layout = new QHBoxLayout(myTextWidget); cycleLabel = new QLabel(this); cycleLabel->setFixedSize(16, 16); layout->addWidget(cycleLabel); layout->setSpacing(8); myTextLabel = new QLabel(myTextWidget); myTextLabel->setObjectName("myText"); blur_Num = configuration->getBlurNumber(); curStyle = configuration->getcurStyle(); onBlurNumChanged(blur_Num); onStyleChanged(curStyle); layout->addWidget(myTextLabel); } qDebug() << " text = " << myText; sysFont.setPointSize((18 + curFontSize) * m_ptToPx); myTextLabel->setFont(sysFont); myTextLabel->setText(myText); myTextWidget->adjustSize(); if (myText != "") myTextWidget->setVisible(true); else myTextWidget->setVisible(false); } void Screensaver::setPreviewText(bool bVisible) { myPreviewLabel->setText(m_strPreViewTrans); myPreviewLabel->adjustSize(); myPreviewLabel->setVisible(bVisible); // 先采取固定大小方式 myPreviewLabel->move((width() - myPreviewLabel->width()) / 2, 120); myPreviewLabel->raise(); } void Screensaver::setCenterWidget() { QStringList qlist = qsettings->childGroups(); if (qlist.count() < 1) return; QDate date = QDate::currentDate(); QDate targetDate(2100, 1, 1); // 2100是否在未来 int days = (date < targetDate) ? date.daysTo(targetDate) : targetDate.daysTo(date); int index = days % qlist.count() + 1; QString configPath = QDir::homePath() + "/.ukui-screensaver-default.conf"; QSettings settings1(configPath, QSettings::IniFormat); if (settings1.value("FIRST").toString().isEmpty()) { settings1.setValue("FIRST", QDate::currentDate().toString("yy/MM/dd")); index = 1; } if (settings1.value("FIRST").toString() == QDate::currentDate().toString("yy/MM/dd")) index = 1; qsettings->beginGroup(QString::number(index)); if (isCustom) { centerlabel1 = new QLabel(myText); centerlabel2 = new QLabel(""); centerlabel2->hide(); authorlabel = new QLabel(""); } else if (qsettings->contains("OL")) { if (qsettings->value("OL").typeName() == "QString") centerlabel1 = new QLabel(qsettings->value("OL").toString()); else centerlabel1 = new QLabel(qsettings->value("OL").toStringList().join(' ')); centerlabel2 = new QLabel(""); centerlabel2->hide(); #ifndef USE_INTEL authorlabel = new QLabel(qsettings->value("author").toString()); #endif } else if (qsettings->contains("FL")) { if (qsettings->value("FL").typeName() == "QString") centerlabel1 = new QLabel(qsettings->value("FL").toString()); else centerlabel1 = new QLabel(qsettings->value("FL").toStringList().join(' ')); if (qsettings->value("SL").typeName() == "QString") centerlabel2 = new QLabel(qsettings->value("SL").toString()); else centerlabel2 = new QLabel(qsettings->value("SL").toStringList().join(' ')); centerlabel2->show(); #ifndef USE_INTEL authorlabel = new QLabel(qsettings->value("author").toString()); #endif } #ifdef USE_INTEL if (qsettings->contains("author") && !qsettings->value("author").toString().isEmpty()) { authorlabel = new QLabel(qsettings->value("author").toString()); authorlabel->show(); } else { authorlabel = new QLabel(""); authorlabel->hide(); } #endif centerlabel1->setObjectName("centerLabel"); centerlabel2->setObjectName("centerLabel"); authorlabel->setObjectName("authorLabel"); sysFont.setPointSize((36 + curFontSize) * m_ptToPx); centerlabel1->setFont(sysFont); centerlabel2->setFont(sysFont); sysFont.setPointSize((28 + curFontSize) * m_ptToPx); authorlabel->setFont(sysFont); qsettings->endGroup(); #ifdef USE_INTEL // 设置背景透明,qss中更改为透明不生效 centerlabel1->setStyleSheet("QLabel{background-color: transparent;}"); centerlabel2->setStyleSheet("QLabel{background-color: transparent;}"); authorlabel->setStyleSheet("QLabel{background-color: transparent;}"); m_centerWidget = new QWidget(this); m_centerWidget->setStyleSheet("QWidget{background:rgb(0,0,0,64);border-radius:16px}"); QVBoxLayout *layout = new QVBoxLayout(m_centerWidget); // QPushButton *line =new QPushButton(this); // line->setWindowOpacity(0.08); // line->setFocusPolicy(Qt::NoFocus); // line->setMaximumHeight(1); layout->addWidget(centerlabel1); layout->addWidget(centerlabel2); // layout->addWidget(line); layout->addWidget(authorlabel); adjustSize(); m_centerWidget->setVisible(true); #else m_centerWidget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(m_centerWidget); layout->addWidget(centerlabel1); layout->addWidget(centerlabel2); if (!isCustom) { QPushButton *line = new QPushButton(this); line->setWindowOpacity(0.08); line->setFocusPolicy(Qt::NoFocus); line->setMaximumHeight(1); layout->addWidget(line); } layout->addWidget(authorlabel); m_centerWidget->adjustSize(); m_centerWidget->setGeometry( (width() - m_centerWidget->width()) / 2, (height() - m_centerWidget->height()) / 2, m_centerWidget->width(), m_centerWidget->height()); m_centerWidget->setVisible(true); #endif } void Screensaver::getWeatherFinish(QString city, QString cond, QString tmp) { qDebug() << "getWeatherFinish"; qDebug() << city << "," << cond << "," << tmp; this->m_weatherIcon->setPixmap(m_weatherManager->getWeatherIcon(cond)); this->m_weatherArea->setText(city); if (!cond.isEmpty()) { this->m_weatherCond->show(); this->m_weatherCond->setText("·" + cond); } else this->m_weatherCond->hide(); if (!tmp.isEmpty()) { this->m_weatherTemperature->show(); this->m_weatherTemperature->setText(tmp); } else this->m_weatherTemperature->hide(); m_weatherLaout->adjustSize(); m_weatherLaout->setGeometry( (this->width() - m_weatherLaout->width()) / 2, 96 * (float)width() / 1920, m_weatherLaout->geometry().width(), m_weatherLaout->geometry().height()); } void Screensaver::getVideoFormat(QString fileName) { char *ch; // 用toLiatin1()解析在有中文路径时回乱码 QByteArray ba = fileName.toUtf8(); ch = ba.data(); int ret; int streams; char buf[256]; AVFormatContext *fmt_ctx = NULL; AVCodecContext *avctx = NULL; AVInputFormat *fmt = NULL; ret = avformat_open_input(&fmt_ctx, ch, fmt, NULL); if (ret < 0) { return; } // 获取封装格式 cur_video_format = fmt_ctx->iformat->name; // 通过上下文获取视频时长 video_Duration = fmt_ctx->duration / 1000; // 获取视频帧率fps const AVStream *stream = fmt_ctx->streams[0]; double fps = av_q2d(stream->avg_frame_rate); // 获取视频编码类型(h264/h265) avctx = avcodec_alloc_context3(NULL); ret = avcodec_parameters_to_context(avctx, stream->codecpar); video_Codetype = avcodec_get_name(avctx->codec_id); // 获取视频的分辨率 cur_video_Width = (avctx->width >= avctx->height ? avctx->width : avctx->height); cur_video_Height = (avctx->width >= avctx->height ? avctx->height : avctx->width); qDebug() << "video_Codetype = " << video_Codetype; avcodec_free_context(&avctx); // 关闭上下文 avformat_close_input(&fmt_ctx); return; } /* * 检测网址是否合法,如"http://10.42.5.777777"这种非法网址就会出现一直加载的情况,导致显示空白 */ bool Screensaver::getIsValidUrl(const QString &urlStr) { QUrl url(urlStr); if (url.isValid()) { if (url.scheme() == "http" || url.scheme() == "https") { QString host = url.host(); int dotCount = std::count(host.begin(), host.end(), '.'); if (dotCount == 3) { QStringList parts = host.split('.'); if (parts.size() == 4) { for (const QString &part : parts) { bool ok; int num = part.toInt(&ok); if (!ok || num < 0 || num > 255) { m_saverState = SAVERERRORTYPE::URLADDRESSERROR; return false; } } return true; } } else { return true; } } } return false; } QString Screensaver::getLongFormatDate(int type) { kdk_logn_dateinfo *dateInfo = kdk_system_login_lock_dateinfo(getenv("USER")); if (type == DATE) { QString date = dateInfo->date; QString week = dateInfo->week; if (dateInfo) { kdk_free_logn_dateinfo(dateInfo); } return date + " " + week; } else if (type == TIME) { QString time = dateInfo->time; if (dateInfo) { kdk_free_logn_dateinfo(dateInfo); } return time; } } void Screensaver::onScreensaverDatetimeEnableChanged(bool isEnable, const QString &strUserName) { if (strUserName != QString(getenv("USER"))) { return; } if (!m_isInitUICompleted || isEnable != m_isShowDateTime) { m_isShowDateTime = isEnable; if (isEnable) { if (m_widgetDatetime) { m_widgetDatetime->show(); } } else { if (m_widgetDatetime) { m_widgetDatetime->hide(); } } } } void Screensaver::initWeatherWidget() { if (!m_weatherWidget) { m_weatherWidget = new WeatherWidget(this); m_weatherManager = new WeatherManager(this); connect( m_weatherManager, &WeatherManager::onWeatherUpdate, this, [=](QString city, QString cond, QString temperature) { m_weatherWidget->setCurrentWeather(temperature + "C", city + " " + cond); if (m_weatherThumbnailWidget) { m_weatherThumbnailWidget->setCurrentWeather(temperature + "C", city + " " + cond); } }); connect(m_weatherManager, &WeatherManager::onNetworkError, this, [=]() { m_weatherWidget->setErrorTip(tr("Not connected to a network")); if (m_weatherThumbnailWidget) { m_weatherThumbnailWidget->setErrorTip(tr("Not connected to a network")); } }); connect(m_weatherManager, &WeatherManager::onGetWeatherFailed, this, [=]() { m_weatherWidget->setErrorTip(tr("The weather update failed")); if (m_weatherThumbnailWidget) { m_weatherThumbnailWidget->setErrorTip(tr("The weather update failed")); } }); connect(m_weatherManager, &WeatherManager::onGetWeatherIconFinished, this, [=]() { QString weatherIcon = m_weatherManager->getWeatherIcon(); QPixmap weatherStateIcon = m_weatherManager->getWeatherStateIcon(); m_weatherWidget->setWeatherIcon(weatherIcon, weatherStateIcon); if (m_weatherThumbnailWidget) { m_weatherThumbnailWidget->setWeatherIcon(weatherIcon, weatherStateIcon); } }); } } void Screensaver::initMediaControlWidget() { if (isGreeterMode()) { return; } if (!m_widgetMediaControl) { QStringList musicPath = getAudioFiles(m_sMusicPath); m_widgetMediaControl = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); if (!m_mediaControlWidget) { if (!m_bFollowSystem) { if (musicPath.isEmpty()) { m_mediaControlWidget = new MediaSaverWidget(m_widgetMediaControl, false); m_mediaControlWidget->initMultimediaControl(); m_bSaverMusic = false; } else { m_mediaControlWidget = new MediaSaverWidget(m_widgetMediaControl, true); playController::getInstance().addMusicToDatebase(musicPath); playController::getInstance().setCurPlaylist(musicPath); m_bSaverMusic = true; } } else { m_mediaControlWidget = new MediaSaverWidget(m_widgetMediaControl, false); m_bSaverMusic = false; m_mediaControlWidget->initMultimediaControl(); connect(m_mediaControlWidget, &MediaSaverWidget::loadDbusListNameFinished, this, [=](bool success) { if (!success) { m_mediaControlWidget->setSaverMediaType(true); m_bSaverMusic = true; playController::getInstance().addMusicToDatebase(musicPath); playController::getInstance().setCurPlaylist(musicPath); } }); } layout->addWidget(m_mediaControlWidget); } m_widgetMediaControl->setLayout(layout); } } void Screensaver::initCustomAlbumViewer() { if (m_customAlbumViewer) return; m_customAlbumViewer = new CustomAlbumViewer(this); connect(m_customAlbumViewer, &CustomAlbumViewer::pixmapChanged, this, [=]() { update(); }); if (!isScreensaver && m_sSelectPhotoPath == DEFAULT_PHOTO_PATH) { m_sSelectPhotoPath = LOCAL_WALLPAPER_PATH; } m_customAlbumViewer->loadImages(m_sSelectPhotoPath); m_customAlbumViewer->setSequentialMode((m_iPhotoSwitchMode == 0) ? true : false); m_customAlbumViewer->hide(); } void Screensaver::initThumBnalListWidget() { if (!isScreensaver) { return; } m_sidebarBtn = new StatusButton(this, SIDEBARBTN); m_sidebarBtn->setFixedSize(44, 136); m_sidebarBtn->installEventFilter(this); m_sidebarBtn->setIconSize(QSize(44, 44)); m_sidebarBtn->setIcon(drawSymbolicColoredPixmap(QIcon::fromTheme("pan-end-symbolic").pixmap(44, 44), "white")); m_smartSaverLabel = new QLabel(this); sysFont.setPointSize((24 + curFontSize) * m_ptToPx); m_smartSaverLabel->setFont(sysFont); m_smartSaverLabel->setText(tr("Smart Saver")); m_smartSaverLabel->setStyleSheet("color: white"); m_smartSaverLabel->setAlignment(Qt::AlignHCenter); m_thumBnalListWidget = new QListWidget(this); m_thumBnalListWidget->setFlow(QListWidget::TopToBottom); m_thumBnalListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_thumBnalListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QString buttonListStyle = "QListWidget{ background-color: rgba(255,255,255,0%);}" "QListWidget::item{background:rgba(255,255,255,0%);}" "QListWidget::item:hover{background-color:rgba(255,255,255,0%)}" "QListWidget::item::selected{background-color:rgba(255,255,255,0%);}"; m_thumBnalListWidget->setStyleSheet(buttonListStyle); m_thumBnalListWidget->setContentsMargins(20, 0, 0, 0); QWidget *defaultSaverWidget = new QWidget(m_thumBnalListWidget); defaultSaverWidget->setFixedSize(340, 218); QVBoxLayout *defaultSaverLayout = new QVBoxLayout(defaultSaverWidget); QListWidgetItem *defaultSaverItem = new QListWidgetItem(); defaultSaverItem->setSizeHint(QSize(340, 218)); QHBoxLayout *thumbnailSaverLayout = new QHBoxLayout(); thumbnailSaverLayout->addStretch(); m_defaulSaverWidget = new SaverThumbnailWidget(defaultSaverWidget); thumbnailSaverLayout->addWidget(m_defaulSaverWidget); thumbnailSaverLayout->addStretch(); QString backgroundFile = configuration->getDefaultBackground(); QString thumbnailPath = mapToThumbnailPath(backgroundFile); m_defaulSaverWidget->setBackground(background); if (centerlabel1 && centerlabel2 && authorlabel) { m_defaulSaverWidget->setDictumText(centerlabel1->text(), centerlabel2->text(), authorlabel->text()); } connect(m_defaulSaverWidget, &SaverThumbnailWidget::defaultSaverModeClicked, this, [=]() { m_albunWidget->setSelectStatus(false); m_musicWidget->setSelectStatus(false); m_weatherThumbnailWidget->setSelectStatus(false); configuration->setSubMode(0); if (m_customAlbumViewer) { m_customAlbumViewer->stopDisplayImages(); m_customAlbumViewer->hide(); } if (m_widgetMediaControl && m_widgetMediaControl->isVisible()) m_widgetMediaControl->hide(); if (m_weatherWidget && m_weatherWidget->isVisible()) m_weatherWidget->hide(); }); QLabel *defaultSaverLabel = new QLabel(defaultSaverWidget); defaultSaverLabel->setFixedSize(330, 30); defaultSaverLabel->setText("UKUI"); defaultSaverLabel->setStyleSheet("color: white"); defaultSaverLabel->setAlignment(Qt::AlignCenter); defaultSaverLayout->addLayout(thumbnailSaverLayout); defaultSaverLayout->addWidget(defaultSaverLabel, Qt::AlignHCenter); QWidget *weatherWidget = new QWidget(m_thumBnalListWidget); weatherWidget->setFixedSize(340, 218); QVBoxLayout *weatherLayout = new QVBoxLayout(weatherWidget); QListWidgetItem *weatherItem = new QListWidgetItem(); weatherItem->setSizeHint(QSize(340, 218)); QHBoxLayout *thumbnailWeatherLayout = new QHBoxLayout(); thumbnailWeatherLayout->addStretch(); m_weatherThumbnailWidget = new WeatherThumbnailWidget(weatherWidget); thumbnailWeatherLayout->addWidget(m_weatherThumbnailWidget); thumbnailWeatherLayout->addStretch(); m_weatherThumbnailWidget->setBackground(scaleBlurPixmap(width(), height(), thumbnailPath)); connect(m_weatherThumbnailWidget, &WeatherThumbnailWidget::weatherModeClicked, this, [=]() { m_albunWidget->setSelectStatus(false); m_defaulSaverWidget->setSelectStatus(false); m_musicWidget->setSelectStatus(false); configuration->setSubMode(1); if (m_customAlbumViewer) { m_customAlbumViewer->stopDisplayImages(); } }); QLabel *weatherLabel = new QLabel(weatherWidget); weatherLabel->setFixedSize(330, 30); weatherLabel->setText(tr("Weather")); weatherLabel->setStyleSheet("color: white"); weatherLabel->setAlignment(Qt::AlignCenter); weatherLayout->addLayout(thumbnailWeatherLayout); weatherLayout->addWidget(weatherLabel, Qt::AlignHCenter); QWidget *musicWidget = new QWidget(m_thumBnalListWidget); musicWidget->setFixedSize(340, 218); QVBoxLayout *musicLayout = new QVBoxLayout(musicWidget); QListWidgetItem *musicItem = new QListWidgetItem(); musicItem->setSizeHint(QSize(340, 218)); QHBoxLayout *thumbnailMusicLayout = new QHBoxLayout(); thumbnailMusicLayout->addStretch(); m_musicWidget = new MusicThumbnailWidget(musicWidget); thumbnailMusicLayout->addWidget(m_musicWidget); thumbnailMusicLayout->addStretch(); m_musicWidget->setBackground(scaleBlurPixmap(width(), height(), thumbnailPath)); connect(m_musicWidget, &MusicThumbnailWidget::musicModeClicked, this, [=]() { m_albunWidget->setSelectStatus(false); m_defaulSaverWidget->setSelectStatus(false); m_weatherThumbnailWidget->setSelectStatus(false); configuration->setSubMode(2); if (m_customAlbumViewer) { m_customAlbumViewer->stopDisplayImages(); } }); QLabel *musicLabel = new QLabel(musicWidget); musicLabel->setFixedSize(330, 30); musicLabel->setText(tr("Music")); musicLabel->setStyleSheet("color: white"); musicLabel->setAlignment(Qt::AlignCenter); musicLayout->addLayout(thumbnailMusicLayout); musicLayout->addWidget(musicLabel, Qt::AlignHCenter); QWidget *albunWidget = new QWidget(m_thumBnalListWidget); albunWidget->setFixedSize(340, 218); QVBoxLayout *albunLayout = new QVBoxLayout(albunWidget); QListWidgetItem *photoItem = new QListWidgetItem(); photoItem->setSizeHint(QSize(340, 218)); QHBoxLayout *thumbnailAlbunLayout = new QHBoxLayout(); thumbnailAlbunLayout->addStretch(); m_albunWidget = new AlbumThumbnailWidget(m_thumBnalListWidget); thumbnailAlbunLayout->addWidget(m_albunWidget); thumbnailAlbunLayout->addStretch(); m_albunWidget->setBackground(scaleBlurPixmap(width(), height(), thumbnailPath)); m_albunWidget->setDefaultPhoto(loadFromFile(thumbnailPath)); if (m_sSelectPhotoPath == DEFAULT_PHOTO_PATH) { m_albunWidget->setDefaultPath(DEFAULT_PHOTO_PATH); } else { m_albunWidget->setDefaultPath(m_sSelectPhotoPath); } connect(m_albunWidget, &AlbumThumbnailWidget::albumModeClicked, this, [=]() { m_musicWidget->setSelectStatus(false); m_defaulSaverWidget->setSelectStatus(false); m_weatherThumbnailWidget->setSelectStatus(false); configuration->setSubMode(3); }); QLabel *albunLabel = new QLabel(albunWidget); albunLabel->setFixedSize(330, 30); albunLabel->setText(tr("Photo Albun")); albunLabel->setStyleSheet("color: white"); albunLabel->setAlignment(Qt::AlignCenter); albunLayout->addLayout(thumbnailAlbunLayout); albunLayout->addWidget(albunLabel, Qt::AlignHCenter); m_thumBnalListWidget->insertItem(0, defaultSaverItem); m_thumBnalListWidget->setItemWidget(defaultSaverItem, defaultSaverWidget); m_thumBnalListWidget->insertItem(1, weatherItem); m_thumBnalListWidget->setItemWidget(weatherItem, weatherWidget); m_thumBnalListWidget->insertItem(2, musicItem); m_thumBnalListWidget->setItemWidget(musicItem, musicWidget); m_thumBnalListWidget->insertItem(3, photoItem); m_thumBnalListWidget->setItemWidget(photoItem, albunWidget); m_thumBnalListWidget->hide(); m_smartSaverLabel->hide(); if (m_nSubMode == 0) { m_defaulSaverWidget->setSelectStatus(true); } else if (m_nSubMode == 1) { m_weatherThumbnailWidget->setSelectStatus(true); } else if (m_nSubMode == 2) { m_musicWidget->setSelectStatus(true); } else if (m_nSubMode == 3) { m_albunWidget->setSelectStatus(true); } m_thumBnalListWidget->installEventFilter(this); m_hideThumBnalWidgetTimer = new QTimer(this); m_hideThumBnalWidgetTimer->setSingleShot(true); connect(m_hideThumBnalWidgetTimer, &QTimer::timeout, this, [=]() { if (m_thumBnalListWidget && m_thumBnalListWidget->isVisible()) { m_thumBnalListWidget->hide(); m_smartSaverLabel->hide(); m_sidebarBtn->move(0, (height() - m_sidebarBtn->height()) / 2); m_sidebarBtn->setIcon( drawSymbolicColoredPixmap(QIcon::fromTheme("pan-end-symbolic").pixmap(44, 44), "white")); } }); } ukui-screensaver/src/screensaver/mmediaplaylist.h0000664000175000017500000000432715172041106021274 0ustar fengfeng#ifndef MMediaPlaylist_H #define MMediaPlaylist_H #include #include #include #include #include class MMediaContent { public: MMediaContent(QUrl url); QUrl canonicalUrl() const; //获取Qurl内容 private: QUrl m_url; //媒体url路径 }; class MMediaPlaylist : public QObject { Q_OBJECT public: // 只播放一次 单曲循环 列表播放 列表循环 随机播放 enum PlaybackMode { CurrentItemOnce=0, CurrentItemInLoop, Sequential, Loop, Random }; //播放循环模式枚举 MMediaPlaylist(QObject *parent = nullptr); QString getPlayFileName(); //获取待播放媒体的文件名 int currentIndex() const; //获取当前播放的歌曲在播放列表中的位置 bool addMedia(const QUrl &items); //添加媒体 void next(); //切换下一首 void previous(); //切换上一首 void setCurrentIndex(int index); //设置选中播放列表中的位置 PlaybackMode playbackMode() const; //获取播放循环模式 void setPlaybackMode(PlaybackMode mode); //设置循环播放模式 int mediaCount() const; //列表中总媒体的数量 MMediaContent media(int index) const; //获取列表中特定位置的媒体 bool clear(); //清空列表 bool removeMedia(int pos); //移出特定位置的歌曲 public Q_SLOTS: void palyFinish(); //播放完成槽函数 void playError(); //播放异常槽函数 void playErrorMsg(int Damage); //播放错误异常 private: int randomIndex(); //生成随机数 QList m_playerList; //实际的播放队列 int m_index = 0; //当前播放的歌曲位置 int notExistFileNumber = 0; // 播放列表中不存在的文件数目,用于判断当前歌单列表在“循环”或“随机”模式下是否需要继续播放 PlaybackMode m_playbackMode = Loop; //当前列表的循环模式 Q_SIGNALS: void currentIndexChanged(int); //媒体切换信号 void playbackModeChanged(MMediaPlaylist::PlaybackMode mode); //播放循环模式切换信号 void autoPlay(MMediaPlaylist::PlaybackMode playbackMode); //自动播放下一首信号 void stop(); //停止播放信号 }; #endif // MMediaPlaylist_H ukui-screensaver/src/screensaver/main.cpp0000664000175000017500000001170115172041106017527 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "screensaver.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define WORKING_DIRECTORY "/usr/share/ukui-screensaver" bool bControlFlg = false;//是否控制面板窗口 int main(int argc, char *argv[]) { #if(QT_VERSION>=QT_VERSION_CHECK(5,6,0)) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif QApplication a(argc, argv); initUkuiLog4qt("ukui-screensaver-default"); /* prctl(PR_SET_PDEATHSIG, SIGHUP); //加载翻译文件 QString locale = QLocale::system().name(); QTranslator translator; QString qmFile = QString(WORKING_DIRECTORY"/i18n_qm/%1.qm").arg(locale); translator.load(qmFile); a.installTranslator(&translator); qDebug() << "load translation file " << qmFile; */ QCommandLineParser parser; QString windowId; Screensaver s(false); XWindowAttributes xwa; parser.setApplicationDescription("Test helper"); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("source", QCoreApplication::translate("main", "Screensaver for ukui-screensaver")); parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); parser.addOptions({ {{"r", "root"}, QCoreApplication::translate("main", "show on root window")}, {{"w", "window-id"}, QCoreApplication::translate("main", "show on window."), QCoreApplication::translate("main", "window id")}, }); parser.process(a); bool onWindow = parser.isSet("window-id"); bool onRoot = parser.isSet("root"); double scale = 1; QScreen *screen = QApplication::primaryScreen(); scale = screen->devicePixelRatio(); if(onWindow){ windowId = parser.value("window-id"); WId wid = windowId.toULong(); /*获取窗口属性失败时程序退出,这是为了避免应用调用屏保的一瞬间崩溃,导致 屏保获取到一个不存在的winid,从而产生一个屏保窗口*/ Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; exit(0); } if(!XGetWindowAttributes(display, wid, &xwa)) { qDebug()<<"XGetWindowAttributes failed"; exit(0); } QWindow* window = QWindow::fromWinId(wid); s.setProperty("_q_embedded_native_parent_handle",QVariant(wid)); /*设置焦点穿透*/ s.setWindowFlag(Qt::WindowTransparentForInput, true); s.winId(); s.windowHandle()->setParent(window); /* #ifndef USE_INTEL XClassHint ch; ch.res_name = NULL; ch.res_class = NULL; XGetClassHint ((Display*)getX11Display(), wid, &ch); if(ch.res_name && strcmp(ch.res_name,"ukui-control-center")==0){ bControlFlg = true; s.addClickedEvent(); } #endif */ //获取屏保所在屏幕对应的缩放比例。 for(auto screen : QGuiApplication::screens()) { QPoint pos(xwa.x,xwa.y); if(screen->geometry().contains(pos)){ scale = screen->devicePixelRatio(); } } s.resize(xwa.width/scale + 1,xwa.height/scale + 1); s.move(0,0); s.show(); } else if(onRoot){ bControlFlg = false; Display *display = (Display*)getX11Display(); if (!display) { qWarning()<<"get xcb display failed"; exit(0); } WId wid = DefaultRootWindow(display); QWindow* window = QWindow::fromWinId(wid); window->setProperty("_q_embedded_native_parent_handle",QVariant(wid)); s.winId(); s.windowHandle()->setParent(window); XGetWindowAttributes(display, wid, &xwa); qDebug()<<"xwa.width = "<. * **/ #include "videoplayer.h" #include #include KyVideoPlayer::KyVideoPlayer(QWidget *parent) : QWidget(parent) { m_mediaPlayer = new QMediaPlayer(this); connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, &KyVideoPlayer::updatePosition); connect(m_mediaPlayer, &QMediaPlayer::mediaStatusChanged, this, &KyVideoPlayer::mediaStatusChanged); connect( m_mediaPlayer, &QMediaPlayer::errorOccurred, this, &KyVideoPlayer::onShowError); } KyVideoPlayer::~KyVideoPlayer() { if (m_mediaPlayer != nullptr) { m_mediaPlayer->stop(); m_mediaPlayer->deleteLater(); m_mediaPlayer = nullptr; } } void KyVideoPlayer::setOutput(QGraphicsVideoItem *videoWidget, int duration) { m_nVideoDuration = duration; m_mediaPlayer->setVideoOutput(videoWidget); m_mediaPlayer->setPosition(1); //避免首次打开视频是黑屏 m_mediaPlayer->play(); } void KyVideoPlayer::setMediaFile(QString filePath) { m_filePath = filePath; m_mediaPlayer->setSource(QUrl::fromLocalFile(filePath)); } void KyVideoPlayer::stop() { qDebug() << "m_mediaPlayer->playbackState() = " << m_mediaPlayer->playbackState(); if (m_mediaPlayer && m_mediaPlayer->playbackState() == QMediaPlayer::PlayingState) { m_mediaPlayer->stop(); m_isActiveStop = true; } } void KyVideoPlayer::updatePosition(qint64 position) { if (m_isActiveStop) return ; //因为用wps制作的视频用qmediaplayer去获取视频时长有问题,会在视频最后一帧停留很长时间,所以用ffmpeg获取视频时长,手动循环播放视频 qDebug() << "position =" << position << "player->duration() = " << m_mediaPlayer->duration() << "duration = " << m_nVideoDuration; if (m_nVideoDuration || m_mediaPlayer->duration()) { if (position && position > (m_nVideoDuration > 0 ? m_nVideoDuration : m_mediaPlayer->duration())) { m_mediaPlayer->setSource(QUrl::fromLocalFile(m_filePath)); m_mediaPlayer->setPosition(1); m_mediaPlayer->play(); } else if (position == 0 && m_mediaPlayer->duration()) { //有些wps做的视频播放到最后一帧会自己跳到0 m_mediaPlayer->setSource(QUrl::fromLocalFile(m_filePath)); m_mediaPlayer->setPosition(1); m_mediaPlayer->play(); } else if (position && (position == m_nVideoDuration || position == m_mediaPlayer->duration())) { //视频当前播放帧数 = 视频总时长 m_mediaPlayer->setSource(QUrl::fromLocalFile(m_filePath)); m_mediaPlayer->setPosition(1); m_mediaPlayer->play(); } } } void KyVideoPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status) { switch (status) { case QMediaPlayer::NoMedia: qDebug() << "NoMedia!!!"; break; case QMediaPlayer::BufferingMedia: qDebug() << "BufferingMedia!!!"; break; case QMediaPlayer::BufferedMedia: qDebug() << "The player has fully buffered the current media!!!"; break; case QMediaPlayer::LoadingMedia: qDebug() << "LoadingMedia!!!"; break; case QMediaPlayer::StalledMedia: qDebug() << "StalledMedia!!!"; break; case QMediaPlayer::EndOfMedia: qDebug() << "EndOfMedia!!!"; break; case QMediaPlayer::LoadedMedia: qDebug() << "LoadedMedia!!!"; break; case QMediaPlayer::InvalidMedia: qDebug() << "InvalidMedia!!!"; break; default: break; } } void KyVideoPlayer::onShowError(QMediaPlayer::Error error) { switch (error) { case QMediaPlayer::NoError: qDebug() << "没有错误!" ; break; case QMediaPlayer::ResourceError: qDebug() << "媒体资源无法被解析!"; stop(); Q_EMIT playerShowError(error); break; case QMediaPlayer::FormatError: qDebug() << "不支持该媒体格式!"; stop(); Q_EMIT playerShowError(error); break; case QMediaPlayer::NetworkError: qDebug() << "发生了一个网络错误!"; stop(); Q_EMIT playerShowError(error); break; case QMediaPlayer::AccessDeniedError: qDebug() << "没有播放权限!"; stop(); Q_EMIT playerShowError(error); break; default: break; } } ukui-screensaver/src/screensaver/graphicsview.cpp0000664000175000017500000000222615172041035021301 0ustar fengfeng/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include "graphicsview.h" GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent) { installEventFilter(this); setMouseTracking(true); } void GraphicsView::mouseMoveEvent(QMouseEvent *event) { QGraphicsView::mouseMoveEvent(event); QWidget::mouseMoveEvent(event); } ukui-screensaver/src/screensaver/weathermanager.h0000664000175000017500000000672415172041106021253 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Authors: ZHAI Kang-ning **/ #ifndef WEATHERMANAGER_H #define WEATHERMANAGER_H #include #include #include #include #include "networkwatcher.h" class QNetworkAccessManager; class QNetworkReply; class LocalWeatherInfo; class SCConfiguration; class WeatherManager : public QObject { Q_OBJECT public: explicit WeatherManager(QObject *parent = nullptr); Q_SIGNALS: void onWeatherUpdate(QString city, QString cond, QString temperature); void onNetworkError(); void onGetWeatherFailed(); void onGetWeatherIconFinished(); private Q_SLOTS: void replyFinished(QNetworkReply *); void onNetworkStateChanged(uint state); public: void initNetworkStatus(); void getWeather(); QString getWeatherIcon(); QPixmap getWeatherIcon(QString cond); QPixmap getWeatherStateIcon(); QString getCityName(); QString getCond(); QString getTemperature(); private: bool updateLocation(); //更新位置,从用户设置获取城市信息,如有多个,只取第一个,未对接 void weatherRequest(); bool getLogcalWeather(); QString getLogcalCityId(); QString getWeatherIconPath(int weatherCode); private: QString m_city_id; // "101030100" 默认天津 QString m_city_name; QString m_cond_txt; //天气条件 晴、阴等 QString m_temperature; //温度 10、20等 QString m_cond_code; QNetworkAccessManager *m_net_manager; QTimer *m_timer; QGSettings *m_settings; LocalWeatherInfo *m_local_weather_info; NetWorkWatcher *m_networkWatcher; int m_networkTryNum = 0; SCConfiguration *configuration; int m_iNetworkConnectState = -1; }; class LocalWeatherInfo : QObject { //"1920-08-27 10:17:42,101310204,澄迈,小雨,95%,25℃,北风,1级," //时间,城市编码,城市名称,天气,湿度,温度,风向,风力 Q_OBJECT public: explicit LocalWeatherInfo(QObject *parent = nullptr); private: QString m_update_time; QString m_city_id; QString m_city_name; QString m_cond_text; QString m_air_humidity; QString m_temperature; QString m_wind_direction; QString m_wind_force; public: bool isTimeValid(); void setTime(QString time); QString getTime(); void setCityId(QString cityId); QString getCityId(); void setCityName(QString cityName); QString getCityName(); void setCondText(QString condText); QString getCondText(); void setAirHumidity(QString airHumidity); QString getAirHumidity(); void setTemperature(QString temperature); QString getTemperature(); void setWindDirection(QString windDirection); QString getWindDirection(); void setWindForce(QString windForce); QString getWindForce(); }; #endif // WEATHERMANAGER_H ukui-screensaver/src/screensaver/dbusadapter.h0000664000175000017500000000727715172041106020563 0ustar fengfeng#ifndef DBUSADAPTER_H #define DBUSADAPTER_H #include #include #include #include //#include "UIControl/player/player.h" //#include "mainwidget.h" class DbusAdapter : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player") // // When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. // Q_PROPERTY(QString PlaybackStatus READ getPlaybackState) // Q_PROPERTY(QString LoopStatus READ getLoopStatus WRITE setLoopStatus) // Q_PROPERTY(double Rate READ getRate WRITE setRate) // Q_PROPERTY(bool Shuffle READ getShuffle WRITE setShuffle) Q_PROPERTY(QVariantMap Metadata READ getMetadata) // Q_PROPERTY(double Volume READ getVolume WRITE setVolume) // Q_PROPERTY(double MinimumRate READ getMinimumRate) // Q_PROPERTY(double MaximumRate READ getMaximumRate) // Q_PROPERTY(bool CanGoNext READ getCanGoNext) // Q_PROPERTY(bool CanGoPrevious READ getCanGoPrevious) // Q_PROPERTY(bool CanPlay READ getCanPlay) // Q_PROPERTY(bool CanPause READ getCanPause) // Q_PROPERTY(bool CanSeek READ getCanSeek) // // When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is NOT emitted with the new value. // Q_PROPERTY(long long Position READ getPosition) // Q_PROPERTY(bool CanControl READ getCanControl) public: explicit DbusAdapter(QObject *parent = nullptr); // void emitSeeked(); //public: // QString getPlaybackState(); // QString getLoopStatus(); // void setLoopStatus(QString s); // double m_rate; // double getRate(); // void setRate(double d); // bool m_shuffle { false }; // bool getShuffle() { return m_shuffle; } // void setShuffle(bool b) { m_shuffle = b; } QVariantMap m_metadata; QVariantMap getMetadata(); // void updateMetadata(const QVariantMap input); // double getVolume(); // void setVolume(double b); // long long getPosition(); // double m_minimumRate { 0.5 }; // double getMinimumRate() { return m_minimumRate; } // double m_maximumRate { 3.0 }; // double getMaximumRate() { return m_maximumRate; } // bool m_canGoNext { true }; // bool getCanGoNext() { return m_canGoNext; } // bool m_canGoPrevious { true }; // bool getCanGoPrevious() { return m_canGoPrevious; } // bool m_canPlay { true }; // bool getCanPlay() { return m_canPlay; } // bool m_canPause { true }; // bool getCanPause() { return m_canPause; } // bool m_canSeek { true }; // bool getCanSeek() { return m_canSeek; } // bool m_canControl { true }; // bool getCanControl() { return m_canControl; } // void setCanControl(const bool b) { m_canControl = b; } // void initMetadata(); public Q_SLOTS: // // mpris 使用 void Stop() const; void Next() const; void Play() const; void Pause() const; // void PlayPause(); void Previous() const; // void Seek(const qint64 Offset); // void SetPosition(const QString& TrackId, const long long Position); // void OpenUri(const QString& Uri); // void VolumeUp() const; // void VolumeDown() const; // void FullScreen() const; // void Exit() const; void notify(const QString &interfaceName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties); //Q_SIGNALS: // void Seeked(const qint64 url); // void PropertiesChanged(const QString &interface_name, const QVariantMap &change_properties, const QStringList &invalidate_properties); //private: // QString m_playbackStatus = "Stopped"; // void notify(const QString &changedProperties, const QStringList &invalidatedProperties); }; #endif // DBUSADAPTER_H ukui-screensaver/src/screensaver/networkwatcher.cpp0000664000175000017500000000637215172041106021662 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "networkwatcher.h" const int port = 80; NetWorkWatcher::NetWorkWatcher(QObject *parent) { QDBusConnection::systemBus().connect( QString("org.freedesktop.NetworkManager"), QString("/org/freedesktop/NetworkManager"), QString("org.freedesktop.NetworkManager"), QString("StateChanged"), this, SLOT(onNetworkStateChanged(uint))); network = new QDBusInterface( "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", QDBusConnection::systemBus()); } void NetWorkWatcher::onNetworkStateChanged(uint state) { qDebug() << "NetWork state already changed to" << state; emit NetworkStateChanged(state); } //网络是否可以连接 void NetWorkWatcher::checkOnline() { QHostInfo::lookupHost("www.baidu.com", this, SLOT(getIp(QHostInfo))); //异步非阻塞 /*QHostInfo info = QHostInfo::fromName("www.baidu.com"); //阻塞 if(info.error() != QHostInfo::NoError) return false; QString ipAddress = info.addresses().first().toString(); QTcpSocket tcpClient; tcpClient.abort(); tcpClient.connectToHost(ipAddress, port); //150毫秒没有连接上则判断不在线 return tcpClient.waitForConnected(150);*/ } //是否连接网络 bool NetWorkWatcher::isConnect() { QDBusMessage message = network->call("state"); if (QDBusMessage::ErrorMessage == message.type()) { qDebug() << "error: NetWorkManager DBus Connect Failed!"; return false; } QList argvs = message.arguments(); uint state = argvs.at(0).value(); qDebug() << "NetWorkManager state:" << state; if (state == NM_STATE_CONNECTED_SITE || state == NM_STATE_CONNECTED_LOCAL || state == NM_STATE_CONNECTED_GLOBAL) return true; return false; } void NetWorkWatcher::getIp(QHostInfo info) { if (!isConnect()) { emit NetworkStateChanged(NM_STATE_DISCONNECTED); return; } if (info.error() != QHostInfo::NoError) { emit NetworkStateChanged(NM_STATE_CONNECTED_LIMIT); return; } QString ipAddress = info.addresses().first().toString(); QTcpSocket tcpClient; tcpClient.abort(); tcpClient.connectToHost(ipAddress, port); // 150毫秒没有连接上则判断不在线 if (!tcpClient.waitForConnected(150)) { emit NetworkStateChanged(NM_STATE_CONNECTED_LIMIT); return; } emit NetworkStateChanged(NM_STATE_CONNECTED_GLOBAL); } ukui-screensaver/src/screensaver/chinesedate.cpp0000664000175000017500000003170115172041035021062 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "chinesedate.h" #include #include #include unsigned int chineseDays[201] = { 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 0x06566, 0x0d4a0, 0x0ea50, 0x16a95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 /**Add By JJonline@JJonline.Cn**/ 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 0x0d520 }; int Month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; ChineseDate::ChineseDate() { ChineseMonth << "" << "正" << "二" << "三" << "四" << "五" << "六" << "七" << "八" << "九" << "十" << "冬" << "腊"; ChineseDay << "" << "一" << "二" << "三" << "四" << "五" << "六" << "七" << "八" << "九"; ChineseDays << "初" << "十" << "廿" << "三十"; } int ChineseDate::getYearDays(int year) { int sum = 12 * 29; for (unsigned int i = 0x8000; i > 0x8; i = i >> 1) { sum += chineseDays[year - 1900] & i ? 1 : 0; } sum += getLeapMonthDays(year); return sum; } bool ChineseDate::isLeapMonth(int year) { // 检查 year 是否在有效范围内 if (year < 1900) { return false; } return chineseDays[year - 1900] & 0xf ? true : false; } int ChineseDate::getLeapMonthDays(int year) { if (isLeapMonth(year)) return chineseDays[year - 1900] & 0x10000 ? 30 : 29; return 0; } int ChineseDate::getLeapMonth(int year) { if (isLeapMonth(year)) return chineseDays[year - 1900] & 0xf; return 0; } int ChineseDate::getYearMonthDays(int year, int month) { return chineseDays[year - 1900] & (0x10000 >> month) ? 30 : 29; } QString ChineseDate::getChineseDays(int month, int day, bool isleap) { QString res = ""; if (isleap) res = res + "闰"; res = res + ChineseMonth.at(month) + "月"; if (day == 10) res = res + "初十"; else if (day == 20) res = res + "二十"; else if (day == 30) res = res + "三十"; else res = res + ChineseDays.at(day / 10) + ChineseDay.at(day % 10); return res; } QString ChineseDate::getDateLunar() { int leapyear, leapmonth, leapday; QDateTime currentTime = QDateTime::currentDateTime(); QDate oldTile(1900, 1, 31); //获取当前日期到1900年1月31日的天数 int offset = oldTile.daysTo(currentTime.date()); //获取农历年 int i, yearlength = 0; for (i = 1900; i < 2101 && offset > 0; i++) { yearlength = getYearDays(i); offset -= yearlength; } if (offset < 0) { offset += yearlength; i--; } leapyear = i; //获取农历月 bool isLeap = false; int temp = 0; int leapMonth = getLeapMonth(leapyear); for (i = 1; i < 13 && offset >= 0; i++) { temp = getYearMonthDays(leapyear, i); offset -= temp; isLeap = false; if (i == leapMonth) { if (offset < 0) { i--; isLeap = true; } else { temp = getLeapMonthDays(leapyear); offset -= temp; } } } if (offset < 0) { if (i == leapMonth && isLeap) { offset += temp; isLeap = false; } else if (i == (leapMonth + 1)) { offset += temp; isLeap = true; i--; } else { offset += temp; i--; } } leapmonth = i; leapday = offset + 1; return getChineseDays(leapmonth, leapday, isLeap); } ukui-screensaver/src/screensaver/scconfiguration.cpp0000664000175000017500000003070515172041106022005 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "scconfiguration.h" #include "utils.h" #include #include #include #include #include #include #include "lock-dialog/backenddbushelper.h" #include "definetypes.h" SCConfiguration *SCConfiguration::instance_ = nullptr; SCConfiguration::SCConfiguration(QObject *parent) : QObject(parent) { QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); QString sessionDbus = QString("%1%2").arg(QString(SS_DBUS_SERVICE)).arg(displayNum); m_helperBackendDbus = new BackendDbusHelper(sessionDbus, SS_DBUS_PATH, QDBusConnection::sessionBus(), this); if (!m_helperBackendDbus->isValid()) { delete m_helperBackendDbus; m_helperBackendDbus = new BackendDbusHelper(SS_DBUS_SERVICE, SS_DBUS_PATH, QDBusConnection::sessionBus(), this); } connect( m_helperBackendDbus, &BackendDbusHelper::screenSaverConfChanged, this, &SCConfiguration::onConfigurationChanged); connect( m_helperBackendDbus, &BackendDbusHelper::ukccPluginsConfChanged, this, &SCConfiguration::onConfigurationChanged); connect( m_helperBackendDbus, &BackendDbusHelper::themeStyleConfChanged, this, &SCConfiguration::onConfigurationChanged); connect( m_helperBackendDbus, &BackendDbusHelper::lockScreenConfChanged, this, &SCConfiguration::onConfigurationChanged); connect( m_helperBackendDbus, &BackendDbusHelper::networkConnectStateChanged, this, &SCConfiguration::onNetworkConnectStateChanged); connect( m_helperBackendDbus, &BackendDbusHelper::saverDatetimeEnableChanged, this, &SCConfiguration::onSaverDatetimeEnableChanged); } SCConfiguration *SCConfiguration::instance(QObject *parent) { if (instance_ == nullptr) instance_ = new SCConfiguration(parent); return instance_; } void SCConfiguration::onConfigurationChanged(QString key, QVariant value) { if (key == "mode") { bool ret = false; ret = (value.toString() == "default-ukui-custom"); Q_EMIT isCustomModeChanged(ret); } else if (key == "cycleTime") { int cycleTime = value.toInt(); Q_EMIT cycleTimeChanged(cycleTime); } else if (key == "automaticSwitchingEnabled") { bool changed = value.toBool(); Q_EMIT autoSwitchChanged(changed); } else if (key == "backgroundPath") { QString path = value.toString(); Q_EMIT backgroundPathChanged(path); } else if (key == "background") { QString path = value.toString(); Q_EMIT backgroundChanged(path); } else if (key == "mytext") { QString text = value.toString(); Q_EMIT myTextChanged(text); } else if (key == "showCustomRestTime") { bool ret = value.toInt(); Q_EMIT showCRestTimeChanged(ret); } else if (key == "showUkuiRestTime") { bool ret = value.toInt(); Q_EMIT showURestTimeChanged(ret); } else if (key == "textIsCenter") { bool ret = value.toBool(); Q_EMIT textIsCenterChanged(ret); } else if (key == "showMessageEnabled") { bool ret = value.toBool(); Q_EMIT messageShowEnableChanged(ret); } else if (key == "messageNumber") { int num = value.toInt(); Q_EMIT messageNumberChanged(num); } else if (key == "hoursystem") { int timeType = value.toInt(); Q_EMIT timeTypeChanged(timeType); } else if (key == "type") { QString dateType = value.toString(); Q_EMIT dateTypeChanged(dateType); } else if (key == "menuTransparency") { int blur_Num = value.toInt(); Q_EMIT blurChanged(blur_Num); } else if (key == "styleName") { QString m_curStyle = value.toString(); Q_EMIT styleChanged(m_curStyle); } else if (key == "systemFontSize") { double m_curFontSize = value.toDouble(); qDebug() << "curFontSize = " << m_curFontSize; Q_EMIT fontSizeChanged(m_curFontSize); } else if (key == "subMode") { int subMode = value.toInt(); Q_EMIT subModeChanged(subMode); } else if (key == "weatherPosition") { int position = value.toInt(); Q_EMIT weatherPositionChanged(position); } else if (key == "musicPath") { QString musicPath = value.toString(); Q_EMIT musicPathChanged(musicPath); } else if (key == "photoCycleMode") { int photoCycleMode = value.toInt(); Q_EMIT photoCycleModeChanged(photoCycleMode); } else if (key == "photoCycleInterval") { int photoCycleInterval = value.toInt(); Q_EMIT photoCycleIntervalChanged(photoCycleInterval); } else if (key == "photoPath") { QString photoPath = value.toString(); Q_EMIT photoPathChanged(photoPath); } else if (key == "musicFollowSystem") { bool b = value.toBool(); Q_EMIT musicFollowSystemChanged(b); } else if (key == "photoSelectPath") { QString photoPath = value.toString(); Q_EMIT photoSelectPathChanged(photoPath); } } void SCConfiguration::onNetworkConnectStateChanged(int state) { Q_EMIT networkConnectStateChanged(state); } void SCConfiguration::onSaverDatetimeEnableChanged(bool isEnable, QString username) { Q_EMIT saverDatetimeEnableChanged(isEnable, username); } QString SCConfiguration::getDefaultBackground() { QString backgroundFile = m_helperBackendDbus->getLockScreenConf(KEY_BACKGROUND).toString(); if (ispicture(backgroundFile)) { return backgroundFile; } else { QString default_background_path = "/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg"; if (ispicture(default_background_path)) { return default_background_path; } return DEFAULT_BACKGROUND_PATH; } } int SCConfiguration::getTimeType() { int timeType = 24; timeType = m_helperBackendDbus->getUkccPluginsConf(KEY_HOUR_SYSTEM).toInt(); return timeType; } QString SCConfiguration::getDateType() { QString dateType = "cn"; dateType = m_helperBackendDbus->getUkccPluginsConf(KEY_DATE).toString(); return dateType; } int SCConfiguration::getFontSize() { double fontSize = 0; fontSize = m_helperBackendDbus->getThemeStyleConf(KEY_SYSTEM_FONT_SIZE).toDouble(); double defaultFontSize = getDefaultFontSize(); qDebug() << "defaultFontSize = " << defaultFontSize; return fontSize - defaultFontSize; } double SCConfiguration::getPtToPx() { double m_ptTopx = 1.0; if (QApplication::primaryScreen() && QApplication::primaryScreen()->logicalDotsPerInch() > 0) { m_ptTopx = 72 / (QApplication::primaryScreen()->logicalDotsPerInch()); } return m_ptTopx; } bool SCConfiguration::getAutoSwitch() { bool ret = false; ret = m_helperBackendDbus->getScreenSaverConf(KEY_AUTOMATIC_SWITCHING_ENABLE).toBool(); return ret; } bool SCConfiguration::getIsCustom() { bool ret = false; ret = (m_helperBackendDbus->getLockScreenConf("mode").toString() == "default-ukui-custom"); return ret; } bool SCConfiguration::getMessageShowEnable() { bool ret = false; ret = m_helperBackendDbus->getScreenSaverConf(KEY_SHOW_MESSAGE_ENABLED).toBool(); return ret; } int SCConfiguration::getMessageNumber() { bool ret = false; ret = (m_helperBackendDbus->getLockScreenConf("mode").toString() == "default-ukui-custom"); return ret; } bool SCConfiguration::getCShowRestTime() { bool ret = true; ret = m_helperBackendDbus->getScreenSaverConf(KEY_SHOW_CUSTOM_REST_TIME).toBool(); return ret; } bool SCConfiguration::getUShowRestTime() { bool ret = true; ret = m_helperBackendDbus->getScreenSaverConf(KEY_SHOW_UKUI_REST_TIME).toBool(); return ret; } int SCConfiguration::getCycleTime() { int cycleTime = 300; cycleTime = m_helperBackendDbus->getScreenSaverConf(KEY_CYCLE_TIME).toInt(); return cycleTime; } QString SCConfiguration::getBackgroundPath() { QString backgroundPath = "/usr/share/backgrounds"; backgroundPath = m_helperBackendDbus->getScreenSaverConf(KEY_BACKGROUND_PATH).toString(); return backgroundPath; } bool SCConfiguration::getTextIsCenter() { bool ret = true; ret = m_helperBackendDbus->getScreenSaverConf(KEY_TEXT_IS_CENTER).toBool(); return ret; } QString SCConfiguration::getMyText() { QString myText = ""; myText = m_helperBackendDbus->getScreenSaverConf("mytext").toString(); return myText; } int SCConfiguration::getBlurNumber() { int blurNum = 50; blurNum = m_helperBackendDbus->getThemeStyleConf("menuTransparency").toInt(); return blurNum; } QString SCConfiguration::getcurStyle() { QString curStyle; curStyle = m_helperBackendDbus->getThemeStyleConf("styleName").toString(); return curStyle; } QString SCConfiguration::getVideoPath() { QString videoPath; videoPath = m_helperBackendDbus->getScreenSaverConf(KEY_VIDEO_PATH).toString(); return videoPath; } QString SCConfiguration::getVideoFormat() { QString videoFormat; videoFormat = m_helperBackendDbus->getScreenSaverConf(KEY_VIDEO_FORMAT).toString(); return videoFormat; } int SCConfiguration::getVideoSize() { int videoSize; videoSize = m_helperBackendDbus->getScreenSaverConf(KEY_VIDEO_SIZE).toInt(); return videoSize; } int SCConfiguration::getVideoWidth() { int videoWidth; videoWidth = m_helperBackendDbus->getScreenSaverConf(KEY_VIDEO_WIDTH).toInt(); return videoWidth; } int SCConfiguration::getVideoHeight() { int videoHeight; videoHeight = m_helperBackendDbus->getScreenSaverConf(KEY_VIDEO_HEIGHT).toInt(); return videoHeight; } int SCConfiguration::getSubMode() { int subMode; subMode = m_helperBackendDbus->getScreenSaverConf(KEY_SUB_MODE).toInt(); return subMode; } void SCConfiguration::setSubMode(int mode) { QVariant subMode = mode; m_helperBackendDbus->setScreenSaverConf(KEY_SUB_MODE, subMode); } int SCConfiguration::getWeatherPosition() { int weatherPosition; weatherPosition = m_helperBackendDbus->getScreenSaverConf(KEY_WEATHER_POSITION).toInt(); return weatherPosition; } QString SCConfiguration::getMusicPath() { QString musicPath; musicPath = m_helperBackendDbus->getScreenSaverConf(KEY_MUSIC_PATH).toString(); return musicPath; } bool SCConfiguration::getMusicFollowSystem() { bool value; value = m_helperBackendDbus->getScreenSaverConf(KEY_MUSIC_FOLLOW_SYSTEM).toBool(); return value; } int SCConfiguration::getPhotoCycleMode() { int photoCycleMode; photoCycleMode = m_helperBackendDbus->getScreenSaverConf(KEY_PHOTO_CYCLE_MODE).toInt(); return photoCycleMode; } int SCConfiguration::getPhotoCycleInterval() { int photoCycleInterval; photoCycleInterval = m_helperBackendDbus->getScreenSaverConf(KEY_PHOTO_CYCLE_INTERVAL).toInt(); return photoCycleInterval; } QStringList SCConfiguration::getDefaultPhotoPath() { QStringList defaultPhotoPath; defaultPhotoPath = m_helperBackendDbus->getDefaultPhotoPath(); qDebug() << " defaultPhotoPath = " << defaultPhotoPath; return defaultPhotoPath; } QStringList SCConfiguration::getCustomPhotoPath() { QStringList customPhotoPath; customPhotoPath = m_helperBackendDbus->getCustomPhotoPath(); qDebug() << " customPhotoPath = " << customPhotoPath; return customPhotoPath; } QString SCConfiguration::getSelectPhotoPath() { QString selectPhotoPath; selectPhotoPath = m_helperBackendDbus->getScreenSaverConf(KEY_PHOTO_SELECT_PATH).toString(); qDebug() << " selectPhotoPath = " << selectPhotoPath; return selectPhotoPath; } int SCConfiguration::getNetworkConnectState() { return m_helperBackendDbus->getNetworkConnectState(); } QString SCConfiguration::getWeatherData() { return m_helperBackendDbus->getWeatherData(KEY_WEATHER).toString(); } bool SCConfiguration::getSaverDatetimeEnable() { return m_helperBackendDbus->getSaverDatetimeEnable(); } ukui-screensaver/src/screensaver/adaptivelabel.cpp0000664000175000017500000000354115172041106021403 0ustar fengfeng#include "adaptivelabel.h" #include "commonfunc.h" #include AdaptiveLabel::AdaptiveLabel(QWidget *parent) : QLabel(parent) { setAlignment(Qt::AlignCenter); } void AdaptiveLabel::setPixmap(const QPixmap &pixmap) { originalPixmap = pixmap; } void AdaptiveLabel::setScale(float scale) { m_scale = scale; update(); } void AdaptiveLabel::paintEvent(QPaintEvent *event) { if (originalPixmap.isNull()) { QLabel::paintEvent(event); return; } QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); const int labelWidth = width(); const int labelHeight = height(); const QSize pixmapSize = originalPixmap.size(); const qint64 ratioDiff = qAbs(labelWidth * pixmapSize.height() - labelHeight * pixmapSize.width()); const qint64 tolerance = 0.01 * labelHeight * pixmapSize.height(); QRect sourceRect(0, 0, pixmapSize.width(), pixmapSize.height()); QRect targetRect(0, 0, labelWidth, labelHeight); // 按照短边适应的方式显示图片,保持图片不变形,并且在比例不一致时会对图片进行裁剪 if (ratioDiff > tolerance) { if (labelWidth * pixmapSize.height() > labelHeight * pixmapSize.width()) { const int newHeight = (pixmapSize.width() * labelHeight) / labelWidth; sourceRect = QRect(0, (pixmapSize.height() - newHeight)/2, pixmapSize.width(), newHeight); } else { const int newWidth = (pixmapSize.height() * labelWidth) / labelHeight; sourceRect = QRect((pixmapSize.width() - newWidth)/2, 0, newWidth, pixmapSize.height()); } } QPainterPath path; path.addRoundedRect(targetRect, 26 * m_scale, 26 * m_scale); painter.setClipPath(path); painter.drawPixmap(targetRect, originalPixmap, sourceRect); } ukui-screensaver/src/screensaver/musicdatabase.h0000664000175000017500000002336415172041106021065 0ustar fengfeng#ifndef MUSICDATABASE_H #define MUSICDATABASE_H #include #include #include #include #include #include #include #include #include const QString ALLMUSIC = "LocalMusic"; //本地总表 const QString HISTORY = "HistoryPlayList"; //历史记录 const QString FAV = "我喜欢"; //我喜欢 const QString SEARCH = "SearchResult"; //搜索 const QString SHOWCONTEXTS = "filepath,title,singer,album,filetype,size,time"; //播放列表显示项 enum DB_RETURN_STATUS{ //数据库操作结果返回表 DB_OP_SUCC = (0), //数据库操作成功 DB_UNCONNECT = (-1), //数据库连接失败导致的错误 INVALID_INPUT = (-2), //无效的输入值 DB_DISORDERD = (-3), //数据库与外界顺序不符导致的失败 DB_EMPTY_TABLE = (-4), //数据库中的表格内容为空 DB_OP_ADD_FAILED = (-5), //数据库添加操作失败 DB_OP_DEL_FAILED = (-6), //数据库删除操作失败 DB_OP_GET_FAILED = (-7), //数据库查询操作失败 DB_OP_ADD_REPEAT = (-8), //数据库添加失败,重复添加 DB_INSIDE_FAILED = (-9), //数据库内部错误 PLAYLIST_UNFOUND = (-10), //数据库中播放列表未找到 PLAYLIST_IS_EMPTY = (-11), //数据库中该播放列表为空 CREATE_TABLE_FAILED = (-12), //数据库创表失败 DEL_TABLE_FAILED = (-13), //数据库删表失败 SONG_NOT_FOUND = (-14), //数据库中未找到该歌曲 LIST_NOT_FOUND = (-15), //数据库中未找到该歌单 LIST_EXISTS = (-16), //数据库中已存在该名字歌单 LIST_RENAME_FAILED = (-17), //重命名歌单失败,正常退出 LIST_RENAME_ERR = (-18), //重命名歌单失败,创建了新名称歌单,但新建歌单的列表中名称未能重命名 LIST_REORDER_ERR = (-19), //歌单中歌曲更改顺序失败 LIST_NOT_EMPTY = (-20), //歌单不为空 LIST_IS_EMPTY = (-21), //歌单为空 EMPTYT_HIS_FAILED = (-22), //清空历史列表失败 CREATE_HIS_FAILED = (-23), //创建历史列表失败 OUT_OF_RESULT , //结果边界 }; typedef struct { QString title; QString filepath; QString singer; QString album; QString filetype; QString size; QString time; }musicDataStruct; //QVariant保存结构体 Q_DECLARE_METATYPE(musicDataStruct) #define g_db (MusicDataBase::getInstance()) class MusicDataBase : public QObject { Q_OBJECT public: ~MusicDataBase(); //单例,初始化返回指针,完成数据库文件建立 static MusicDataBase* getInstance(); //open数据库,建立本地,历史,我喜欢及三个列表 int initDataBase(); //根据新建的播放列表名的title值创建新建歌单 int createNewPlayList(const QString& playListName); //根据播放列表名的title值删除对应歌单 int delPlayList(const QString& playListName); //查询当前已有歌单列表 int getPlayList(QStringList& playListNameList); //重命名歌单名title int renamePlayList(const QString& oldPlayListName, const QString& newPlayListName); //从所有歌单中删除一首歌 int delSongFromEveryWhere(const QString& filePath); //删除多首歌曲 int delMultiSongs(const QString &playListName, const QStringList &songsList); //检查歌单是否为空 int checkIfPlayListIsEmpty(const QString& playListName); /**************************新建歌曲增删改查****************************/ //添加歌曲到新建歌单,使用歌曲filePath,歌单名title值,输入数据必须有效, int addMusicToPlayList(const QString& filePath,const QString& playListName); //从歌单中添加歌曲到新建歌单,如果本地歌单中没有,同样插入本地歌单,使用musicDataStruct结构,输入数据必须有效, int addNewSongToPlayList(const musicDataStruct& fileData,const QString& playListName); //从新建歌单中删除歌曲,使用歌曲filePath,歌单名title值,输入数据必须有效, int delMusicFromPlayList(const QString& filePath,const QString& playListName); //根据歌曲filePath在指定歌单中查询歌曲信息 int getSongInfoFromPlayList(musicDataStruct &fileData, const QString& filePath,const QString& playListName); //根据歌单名title值查询对应歌单列表 int getSongInfoListFromPlayList(QList& resList,const QString& playListName); //更换新建歌单中某首歌曲的位置(从选中的位置更换到目的歌曲的位置的后面) int changeSongOrderInPlayList(const QString& selectFilePath, const QString& destinationFilePath, const QString& playListName); /**************************本地歌曲增删改查****************************/ //添加歌曲到本地歌单,使用musicDataStruct结构,输入数据必须有效, int addMusicToLocalMusic(const musicDataStruct& fileData); //从本地歌单中删除歌曲,使用key:filePath,输入数据必须有效,同时从其他列表中也删除 int delMusicFromLocalMusic(const QString& filePath); //从本地歌单中获取指定歌曲信息,使用key:filePath,输入数据必须有效, int getSongInfoFromLocalMusic(const QString& filePath, musicDataStruct &fileData); //从数据库中获取本地歌单列表歌曲信息 int getSongInfoListFromLocalMusic(QList& resList); //更换本地歌单中某首歌曲的位置(从选中的位置更换到目的歌曲的位置的后面) int changeSongOrderInLocalMusic(const QString& selectFilePath, const QString& destinationFilePath); /******************************搜索************************************/ //通过输入关键字从本地歌单中模糊检索列表歌曲信息,输入数据必须有效 int getSongInfoListFromLocalMusicByKeyword(QList& resList, const QString& keyword); //通过输入关键字,Number用于限制展示条数,从本地给出临时提示歌曲列表信息,输入数据必须有效 int getCurtEstimatedListByKeyword(const QString& keyword, int Number, QList& titleSongsList, QList& singersList, QList& albumsList); //通过标准专辑名key:album,获取该专辑歌曲信息 int getSongInfoListByAlbum(QList& resList, const QString& album); //通过标准歌手名key:singer,获取歌曲信息 int getSongInfoListBySinger(QList& resList, const QString& singer); /**************************历史歌单增删改查****************************/ //添加歌曲到历史歌单,使用歌曲的path值,输入数据必须有效, int addMusicToHistoryMusic(const QString& filePath); //从历史歌单中删除歌曲,使用歌曲的path值,输入数据必须有效, int delMusicFromHistoryMusic(const QString& filePath); //从历史歌单中查询指定歌曲信息,使用歌曲的path值,输入数据必须有效, int getSongInfoFromHistoryMusic(const QString& filePath, musicDataStruct &fileData); //从数据库中获取历史歌单列表歌曲信息 int getSongInfoListFromHistoryMusic(QList& resList); //清空历史歌单 int emptyHistoryMusic(); /**************************二层封装接口****************************/ //使用歌曲的path值,查询歌曲信息(因为所有歌曲都在本地歌单,所以只用歌曲路径即可) int getSongInfoFromDB(const QString& filePath, musicDataStruct &fileData); //根据歌单名title值查询对应歌单列表 int getSongInfoListFromDB(QList& resList,const QString& playListName); //检查歌曲是是我喜欢的歌曲 bool checkSongIsInFav(const QString& filePath); //检查歌单列表是否存在 int checkPlayListExist(const QString& playListName); //检查歌曲是否在歌单列表中存在 int checkIfSongExistsInPlayList(const QString& filePath, const QString& playListName); protected: explicit MusicDataBase(QObject *parent = nullptr); Q_SIGNALS: private: //日志处理函数 void msgHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); QSqlDatabase m_database;//数据库 QMutex m_mutex; bool m_databaseOpenFlag = false; QStringList listSimpleSpell; // 简拼列表 QStringList listSpell; // 拼音列表 //检查歌曲是否在总表中存在 int checkIfSongExistsInLocalMusic(const QString& filePath); //检查歌曲是否在历史歌单中存在 int checkIfSongExistsInHistoryMusic(const QString& filePath); //检查歌曲是否在我喜欢中存在 int checkIfSongExistsInFavorMusic(const QString& filePath); // //检查歌单列表是否存在 // int checkPlayListExist(const QString& playListName); // //检查歌曲是否在歌单列表中存在 // int checkIfSongExistsInPlayList(const QString& filePath, const QString& playListName); //获取歌曲在歌曲总表中的index int getSongIndexFromLocalMusic(const QString& filePath, int &songIndex); //获取歌曲在歌曲某歌单表中的index int getSongIndexFromPlayList(const QString& filePath,const QString& playListName, int &songIndex); /**************************字符串转码接口*******************************/ QString inPutStringHandle(const QString& input); QString outPutStringHandle(const QString& output); // 对包含'的字符串做预处理,避免获取SQL语句时语法出错 QString preHandle(const QString& input); }; #endif // MUSICDATABASE_H ukui-screensaver/src/screensaver/musicthumbnailwidget.h0000664000175000017500000000202715172041106022501 0ustar fengfeng#ifndef MUSICTHUMBNAILWIDGET_H #define MUSICTHUMBNAILWIDGET_H #include "timethumbnailwidget.h" #include "masklable.h" #include #include #include class MusicThumbnailWidget : public QWidget { Q_OBJECT public: explicit MusicThumbnailWidget(QWidget *parent = nullptr, bool isControlSaver = false); void setBackground(QPixmap background); void setSelectStatus(bool status); private: void initUI(); void animateSize(const QSize &targetSize); protected: void paintEvent(QPaintEvent *event); bool eventFilter(QObject *watched, QEvent *event); Q_SIGNALS: void musicModeClicked(); private: TimeThumbnailWidget* m_timeThumbnailWidget = nullptr; QWidget* m_mprisSaverWidget = nullptr; QHBoxLayout* m_mainLayout = nullptr; QPixmap m_background; MaskLable* m_maskLabel = nullptr; QVariantAnimation* m_animation; bool m_bHoverStatus = false; bool m_bSelectStatus = false; bool m_bControlSaver = false; }; #endif // MUSICTHUMBNAILWIDGET_H ukui-screensaver/src/screensaver/mmediaplayer.cpp0000664000175000017500000002452315172041106021262 0ustar fengfeng#include "mmediaplayer.h" #include "dbusadapter.h" #include #include #include MMediaPlayer::MMediaPlayer(QObject *parent) : QObject(parent) { createMvpplayer(); } void MMediaPlayer::setPlaylist(MMediaPlaylist *playlist) { //异常情况:空指针 if (playlist == nullptr) { return; } m_playList = playlist; connect(this,&MMediaPlayer::playFinish,m_playList,&MMediaPlaylist::palyFinish,Qt::UniqueConnection); connect(this,&MMediaPlayer::playError,m_playList,&MMediaPlaylist::playError,Qt::UniqueConnection); connect(m_playList,&MMediaPlaylist::autoPlay,this,&MMediaPlayer::autoPlay,Qt::UniqueConnection); connect(m_playList,&MMediaPlaylist::stop,this,&MMediaPlayer::stop,Qt::UniqueConnection); connect(this,&MMediaPlayer::playErrorMsg,m_playList,&MMediaPlaylist::playErrorMsg,Qt::UniqueConnection); } void MMediaPlayer::truePlay(QString startTime) { //异常情况:入参不合法 if (startTime.isEmpty()) { return; } //异常情况:播放列表空指针 if (m_playList == nullptr) { return; } qDebug() << "startTime: " << startTime; QString filePath = m_playList->getPlayFileName(); qDebug() << "play filePath : " << filePath << QUrl(filePath).toLocalFile();; //异常情况:本地文件不存在 if (!QFileInfo::exists(QUrl(filePath).toLocalFile())) { qDebug() << "当前播放文件不存在!" << filePath << QUrl(filePath).toLocalFile(); Q_EMIT playErrorMsg(NotFound); Q_EMIT playError(); return; } qDebug() << "当前播放文件存在: " << filePath; const QByteArray c_filename = filePath.toUtf8(); //如果文件名和上次一样,且不是因为拖动进度条播放,说明上次是暂停 if (c_filename == filenameBack && m_positionChangeed == false) { if (filenameBack != "") { //切换播放状态为播放 pause(); } return; } //重置参数 m_position = 0; m_positionChangeed = false; setProperty("start",startTime); setProperty("force-seekable", "yes"); const char *args[] = {"loadfile",c_filename, NULL}; mpv_command_async(m_mpvPlayer, 0, args); //如果不播放任何媒体,则切换状态为停止 if (c_filename == "") { changeState(StoppedState); return; } //记录到上次播放变量中 filenameBack = c_filename; //切换播放状态为正在播放 changeState(PlayingState); } void MMediaPlayer::play() { //从开头开始播放 truePlay("0"); } void MMediaPlayer::setPlayRate(double s) { setProperty("speed", QString::number(s)); } double MMediaPlayer::getPlayRate() { return getProperty("speed").toDouble(); } void MMediaPlayer::pause() { // 获得mpv播放器的"暂停"状态 QString pasued = getProperty("pause"); KyInfo() << "pauseState = " << pasued; // 根据"暂停"状态来选择暂停还是播放 if(pasued == "no") { KyInfo() << "set pause yes"; setProperty("pause", "yes"); changeState(PausedState); } else if(pasued == "yes") { KyInfo() << "set pause no"; setProperty("pause", "no"); changeState(PlayingState); } } void MMediaPlayer::pauseOnly() { // 获得mpv播放器的"暂停"状态 QString pasued = getProperty("pause"); KyInfo() << "pauseStated = " << pasued; // 根据"暂停"状态来选择暂停还是播放 if(pasued == "no") { KyInfo() << "begin pause."; setProperty("pause", "yes"); changeState(PausedState); } } void MMediaPlayer::stop() { filenameBack = ""; setProperty("pause", "no"); const char *args[] = {"loadfile", "", NULL}; mpv_command_async(m_mpvPlayer, 0, args); changeState(StoppedState); } MMediaPlayer::State MMediaPlayer::state() const { return m_state; } qint64 MMediaPlayer::position() const { return m_position; } void MMediaPlayer::setPosition(qint64 pos) { double sec = double(pos)/1000; m_positionChangeed = true; //记录拖动进度条之前播放状态是否为暂停 bool restartPlay = false; if (m_state == PausedState) { restartPlay = true; } //从拖动完成的位置开始播放 truePlay(QString::number(sec)); if (restartPlay) { //切换播放状态为播放 pause(); } } void MMediaPlayer::setVolume(int vol) { // setProperty("volume",QString::number(vol)); // Q_EMIT signalVolume(vol); // 设置音量,此音量和系统同步,不单独设置mpv音量 // delayMsecond(100); QDBusMessage message = QDBusMessage::createSignal("/", "org.kylin.music", "sinkInputVolumeChanged"); message << "kylin-music" << vol << false; KyInfo() << "createSignal: volume = " << vol; QDBusConnection::sessionBus().send(message); } qint64 MMediaPlayer::duration() const { return m_duration; } void MMediaPlayer::setMedia(const MMediaContent &media) { QUrl url =media.canonicalUrl(); //防止内存泄漏 if (m_tmpPlayList != nullptr) { m_tmpPlayList->deleteLater(); } //创建新的播放列表并将歌曲录入 m_tmpPlayList = new MMediaPlaylist(this); m_tmpPlayList->addMedia(url); setPlaylist(m_tmpPlayList); //以暂停状态从头开始播放 setProperty("pause", "yes"); play(); } bool MMediaPlayer::isAvailable() const { return true; } void MMediaPlayer::delayMsecond(unsigned int msec) { QEventLoop loop;//定义一个新的事件循环 QTimer::singleShot(msec, &loop, SLOT(quit()));//创建单次定时器,槽函数为事件循环的退出函数 loop.exec();//事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出 } void MMediaPlayer::onMpvEvents() { //处理所有事件,直到事件队列为空 while (m_mpvPlayer) { mpv_event *event = mpv_wait_event(m_mpvPlayer, 0); if (event->event_id == MPV_EVENT_NONE) { break; } handle_mpv_event(event); } } void MMediaPlayer::handle_mpv_event(mpv_event *event) { switch (event->event_id) { case MPV_EVENT_PROPERTY_CHANGE: { //属性改变事件 mpv_event_property *prop = (mpv_event_property *)event->data; //播放时,时间改变事件 if (strcmp(prop->name, "time-pos") == 0) { if (prop->format == MPV_FORMAT_DOUBLE) { //将播放状态设置为播放中 if (m_state == StoppedState) { changeState(PlayingState); } // 获得播放时间 double time = *(double *)prop->data; //将单位换算为毫秒 m_position = time * 1000; Q_EMIT positionChanged(m_position); } else if (prop->format == MPV_FORMAT_NONE) { //当前时长距离总时长不超过500毫秒判断播放结束 if ( m_duration!=0 && (m_duration - m_position < 500)) { m_duration = 0; m_position = 0; //播放结束 Q_EMIT playFinish(); } else { //切歌 changeState(StoppedState); } } } } break; case MPV_EVENT_PLAYBACK_RESTART:{ //初始化完成事件 //获取总时长 m_duration = getProperty("duration").toDouble() *1000;//单位换算为毫秒 Q_EMIT durationChanged(m_duration); } break; case MPV_EVENT_IDLE:{ //播放器空闲事件,只有刚启动时、播放完成时、歌曲异常时会进入此分支 QString playlist = getProperty("playlist"); if (!playlist.contains(',')) { //排除播放完成 if (playlist.length() > 2) { //排除刚启动 //歌曲播放异常 Q_EMIT playErrorMsg(Damage); } } } break; //MPV会概率错误的发送此信号,导致没播放完也跳转到下一首 // case MPV_EVENT_END_FILE:{ //播放结束事件 // if (m_position != 0) { // //重置参数 // m_duration = 0; // m_position = 0; // //播放结束 // Q_EMIT playFinish(); // } // } // break; default: ; } } // 回调函数 static void wakeup(void *ctx) { // 此回调可从任何mpv线程调用(但也可以从调用mpv API的线程递归地返回) // 只是需要通知要唤醒的Qt GUI线程(以便它可以使用mpv_wait_event()),并尽快返回 MMediaPlayer *mvpPlayer = (MMediaPlayer *)ctx; Q_EMIT mvpPlayer->mpvEvents(); } void MMediaPlayer::createMvpplayer() { // 创建mpv实例 setlocale(LC_NUMERIC,"C"); m_mpvPlayer = mpv_create(); if (m_mpvPlayer == nullptr) { qDebug()<<"创建播放模块失败!"; this->deleteLater(); return; } //禁用视频流 setProperty("vid", "no"); //接收事件 connect(this, &MMediaPlayer::mpvEvents, this, &MMediaPlayer::onMpvEvents, Qt::QueuedConnection); mpv_set_wakeup_callback(m_mpvPlayer, wakeup, this); //绑定事件 mpv_observe_property(m_mpvPlayer, 0, "time-pos", MPV_FORMAT_DOUBLE); // 判断mpv实例是否成功初始化 if (mpv_initialize(m_mpvPlayer) < 0) { qDebug()<<"初始化失败!"; this->deleteLater(); } } void MMediaPlayer::setProperty(const QString &name, const QString &value) { mpv_set_option_string(m_mpvPlayer, name.toLatin1().data(), value.toLatin1().data()); } QString MMediaPlayer::getProperty(const QString &name) const { return (QString)mpv_get_property_string(m_mpvPlayer, name.toLatin1().data()); } void MMediaPlayer::changeState(MMediaPlayer::State stateNow) { //待设置的循环模式和设置之前一致则不处理 if (m_state == stateNow ) { return; } m_state = stateNow; Q_EMIT stateChanged(m_state); } void MMediaPlayer::autoPlay(MMediaPlaylist::PlaybackMode playbackMode) { //如果是单曲循环模式 if (playbackMode == MMediaPlaylist::PlaybackMode::CurrentItemInLoop) { //播放完毕自动切歌(借用播放点改变时间逻辑循环) m_positionChangeed = true; } truePlay("0"); } ukui-screensaver/src/screensaver/customplugin.cpp0000664000175000017500000000220715172041106021335 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "customplugin.h" #include "screensaver.h" CustomPlugin::CustomPlugin(QObject *parent) : QObject(parent) {} QString CustomPlugin::name() const { return "screensaver-default"; } QWidget *CustomPlugin::createWidget(bool isScreensaver, QPixmap *background, QWidget *parent) { return new Screensaver(isScreensaver, background, parent); } QString CustomPlugin::displayName() const { return "screensaver-default"; } ukui-screensaver/src/screensaver/musicfileinformation.h0000664000175000017500000000370515172041106022503 0ustar fengfeng#ifndef MUSICFILEINFORMATION_H #define MUSICFILEINFORMATION_H #include #include #include #include #include #include #include #include #include #include //taglib #include #include #include #include //#include ////#include #include "musicdatabase.h" enum SearchType { None = 0, TitleType, SingerType, AlbumType }; class MusicFileInformation : public QObject { Q_OBJECT public: explicit MusicFileInformation(QObject *parent = nullptr); static MusicFileInformation& getInstance() { static MusicFileInformation instance; return instance; } QStringList getMusicType(); QList resList; void addFile(const QStringList &addFile); int findIndexFromPlayList(QString listname, QString filepath); int getCount(); int getFailCount(); QStringList m_musicType; //获取封面图片 QPixmap getCoverPhotoPixmap(QString filepath); public Q_SLOTS: void durationChange(qint64 duration); Q_SIGNALS: void durations(); private: // QStringList updateSongInfoFromLocal(QString filepath); private: // QList resList; musicDataStruct musicdataStruct; QStringList AllDirList; QStringList songFiles; QString Dir; QStringList matchMp3Files; // QFileInfo fileInfo; QString MD5Str; QStringList MD5List; QDir matchDir; QString fileType(QFileInfo fileInfo); QString fileSize(QFileInfo fileInfo); bool checkFileIsDamaged(QString filepath); QStringList fileInformation(QString filepath); QString filterTextCode(QString str); int preNum(unsigned char byte); qint64 dur; bool success = false; int musicCount; int m_failCount; }; #endif // MUSICFILEINFORMATION_H ukui-screensaver/src/screensaver/mmediaplayer.h0000664000175000017500000000526015172041106020724 0ustar fengfeng#ifndef MMEDIAPLAYER_H #define MMEDIAPLAYER_H #include #include #include #include #include #include #include // MPV库头文件 #include "mmediaplaylist.h" class MMediaPlayer : public QObject { Q_OBJECT public: // 停止播放 播放中 暂停中 enum State{StoppedState=0,PlayingState,PausedState}; //播放状态枚举 enum ErrorMsg{NotFound=-1,Damage=-2}; //播放状态枚举 MMediaPlayer(QObject *parent = nullptr); void setPlaylist(MMediaPlaylist *playlist); //设置播放列表 void pause(); //暂停或继续 void pauseOnly(); // 暂停 State state() const; //获取状态 qint64 position() const; //获取当前播放点 void setPosition(qint64 pos); //设置播放起始点 bool isAvailable() const; //暂无实际功能 void delayMsecond(unsigned int msec); // 延时函数,不阻塞线程 void setVolume(int vol); //设置音量 qint64 duration() const; //获取总时长 void setMedia(const MMediaContent &media); //设置待播放媒体 void play(); //播放 void setPlayRate(double s); double getPlayRate(); public Q_SLOTS: void stop(); //停止 void onMpvEvents(); //接收mpv事件 private: void truePlay(QString startTime = "0"); //实际的播放函数 void handle_mpv_event(mpv_event *event); // 处理mpv事件 void createMvpplayer(); // 创建mvpPlayer void setProperty(const QString &name, const QString &value); // 设置mpv属性 QString getProperty(const QString &name) const; // 获得mpv属性 void changeState(State stateNow); //改变状态 MMediaPlaylist * m_playList = nullptr; //私有播放列表 MMediaPlaylist * m_tmpPlayList = nullptr; //私有临时播放列表 mpv_handle *m_mpvPlayer = nullptr;//句柄 State m_state = StoppedState;//播放状态 QByteArray filenameBack = ""; //上次播放的媒体名 bool m_positionChangeed = false; //播放进度被设置 qint64 m_position = 0; //播放进度 qint64 m_duration = 0; //总时长 private Q_SLOTS: void autoPlay(MMediaPlaylist::PlaybackMode playbackMode); //自动播放 Q_SIGNALS: void mpvEvents(); // 触发onMpvEvents()槽函数的信号 void stateChanged(MMediaPlayer::State); //状态改变信号 void durationChanged(qint64); //切换媒体时,总时长改变信号 void positionChanged(qint64); //播放进度改变信号 void playFinish(); //媒体播放完成信号 void playError(); //媒体播放错误信号 void playErrorMsg(ErrorMsg errorCode);//媒体播放错误信息信号 // void signalVolume(int); }; #endif // MMEDIAPLAYER_H ukui-screensaver/src/screensaver/default.qrc0000664000175000017500000002123215172041106020232 0ustar fengfeng assets/default.qss assets/default-intel.qss assets/logo.svg assets/download.svg assets/prev.svg assets/next.svg assets/setBackground.svg assets/settings.svg assets/wallpaper.svg assets/logo-kylin.svg assets/message.png assets/underlying.png assets/outerring.png assets/ditu.png assets/bogan.png assets/yu.png assets/feng.png assets/qing.png assets/xue.png assets/duoyun.png assets/ying.png assets/wu.png assets/weather-icon/100.svg assets/weather-icon/101.svg assets/weather-icon/102.svg assets/weather-icon/103.svg assets/weather-icon/104.svg assets/weather-icon/200.svg assets/weather-icon/201.svg assets/weather-icon/202.svg assets/weather-icon/203.svg assets/weather-icon/204.svg assets/weather-icon/205.svg assets/weather-icon/206.svg assets/weather-icon/207.svg assets/weather-icon/208.svg assets/weather-icon/209.svg assets/weather-icon/210.svg assets/weather-icon/211.svg assets/weather-icon/212.svg assets/weather-icon/213.svg assets/weather-icon/300.svg assets/weather-icon/301.svg assets/weather-icon/302.svg assets/weather-icon/303.svg assets/weather-icon/304.svg assets/weather-icon/305.svg assets/weather-icon/306.svg assets/weather-icon/307.svg assets/weather-icon/308.svg assets/weather-icon/309.svg assets/weather-icon/310.svg assets/weather-icon/311.svg assets/weather-icon/312.svg assets/weather-icon/313.svg assets/weather-icon/314.svg assets/weather-icon/315.svg assets/weather-icon/316.svg assets/weather-icon/317.svg assets/weather-icon/318.svg assets/weather-icon/399.svg assets/weather-icon/400.svg assets/weather-icon/401.svg assets/weather-icon/402.svg assets/weather-icon/403.svg assets/weather-icon/404.svg assets/weather-icon/405.svg assets/weather-icon/406.svg assets/weather-icon/407.svg assets/weather-icon/408.svg assets/weather-icon/409.svg assets/weather-icon/410.svg assets/weather-icon/499.svg assets/weather-icon/500.svg assets/weather-icon/501.svg assets/weather-icon/502.svg assets/weather-icon/503.svg assets/weather-icon/504.svg assets/weather-icon/507.svg assets/weather-icon/508.svg assets/weather-icon/509.svg assets/weather-icon/510.svg assets/weather-icon/511.svg assets/weather-icon/512.svg assets/weather-icon/513.svg assets/weather-icon/514.svg assets/weather-icon/515.svg assets/weather-icon/900.svg assets/weather-icon/901.svg assets/weather-icon/999.svg assets/weather-icon/SVG/100.svg assets/weather-icon/SVG/101.svg assets/weather-icon/SVG/102.svg assets/weather-icon/SVG/103.svg assets/weather-icon/SVG/104.svg assets/weather-icon/SVG/150.svg assets/weather-icon/SVG/151.svg assets/weather-icon/SVG/152.svg assets/weather-icon/SVG/153.svg assets/weather-icon/SVG/154.svg assets/weather-icon/SVG/200.svg assets/weather-icon/SVG/201.svg assets/weather-icon/SVG/202.svg assets/weather-icon/SVG/203.svg assets/weather-icon/SVG/204.svg assets/weather-icon/SVG/205.svg assets/weather-icon/SVG/206.svg assets/weather-icon/SVG/207.svg assets/weather-icon/SVG/208.svg assets/weather-icon/SVG/209.svg assets/weather-icon/SVG/210.svg assets/weather-icon/SVG/211.svg assets/weather-icon/SVG/212.svg assets/weather-icon/SVG/213.svg assets/weather-icon/SVG/300.svg assets/weather-icon/SVG/301.svg assets/weather-icon/SVG/302.svg assets/weather-icon/SVG/303.svg assets/weather-icon/SVG/304.svg assets/weather-icon/SVG/305.svg assets/weather-icon/SVG/306.svg assets/weather-icon/SVG/307.svg assets/weather-icon/SVG/308.svg assets/weather-icon/SVG/309.svg assets/weather-icon/SVG/310.svg assets/weather-icon/SVG/311.svg assets/weather-icon/SVG/312.svg assets/weather-icon/SVG/313.svg assets/weather-icon/SVG/314.svg assets/weather-icon/SVG/315.svg assets/weather-icon/SVG/316.svg assets/weather-icon/SVG/317.svg assets/weather-icon/SVG/318.svg assets/weather-icon/SVG/350.svg assets/weather-icon/SVG/351.svg assets/weather-icon/SVG/399.svg assets/weather-icon/SVG/400.svg assets/weather-icon/SVG/401.svg assets/weather-icon/SVG/402.svg assets/weather-icon/SVG/403.svg assets/weather-icon/SVG/404.svg assets/weather-icon/SVG/405.svg assets/weather-icon/SVG/406.svg assets/weather-icon/SVG/407.svg assets/weather-icon/SVG/408.svg assets/weather-icon/SVG/409.svg assets/weather-icon/SVG/410.svg assets/weather-icon/SVG/456.svg assets/weather-icon/SVG/457.svg assets/weather-icon/SVG/499.svg assets/weather-icon/SVG/500.svg assets/weather-icon/SVG/501.svg assets/weather-icon/SVG/502.svg assets/weather-icon/SVG/503.svg assets/weather-icon/SVG/504.svg assets/weather-icon/SVG/507.svg assets/weather-icon/SVG/508.svg assets/weather-icon/SVG/509.svg assets/weather-icon/SVG/510.svg assets/weather-icon/SVG/511.svg assets/weather-icon/SVG/512.svg assets/weather-icon/SVG/513.svg assets/weather-icon/SVG/514.svg assets/weather-icon/SVG/515.svg assets/weather-icon/SVG/800.svg assets/weather-icon/SVG/801.svg assets/weather-icon/SVG/802.svg assets/weather-icon/SVG/803.svg assets/weather-icon/SVG/804.svg assets/weather-icon/SVG/805.svg assets/weather-icon/SVG/806.svg assets/weather-icon/SVG/807.svg assets/weather-icon/SVG/900.svg assets/weather-icon/SVG/901.svg assets/weather-icon/SVG/999.svg ukui-screensaver/src/CMakeLists.txt0000664000175000017500000002222515172041106016322 0ustar fengfengfind_package(Qt6 COMPONENTS WebEngineCore WebEngineWidgets) # 检查系统编译环境是否有webengine库 if(Qt6_WebEngineCore_FOUND AND Qt6_WebEngineWidgets_FOUND) message(STATUS "Target could not found webengine" ) else() add_definitions(-DARCHITECTUREISSW64) message(STATUS "Target found webengine" ) endif() find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets DBus Xml Network Svg Multimedia MultimediaWidgets Sql) find_package(PkgConfig REQUIRED) find_package(KF6NetworkManagerQt REQUIRED) pkg_check_modules(KWAYLANDCLIENT REQUIRED KWaylandClient) pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) find_package(X11 REQUIRED) pkg_check_modules(KYSDKSYSTIME REQUIRED kysdk-systime) pkg_check_modules(KYSDKWINDOWHELPER REQUIRED kysdk-ukuiwindowhelper) if (KYSDK_UKUIWINDOWHELPER_FOUND) include_directories(${KYSDK_UKUIWINDOWHELPER_INCLUDE_DIRS}) endif() pkg_check_modules(KYSDKQTWIDGETS kysdk-qtwidgets) pkg_check_modules(XTST REQUIRED xtst) pkg_check_modules(XCB REQUIRED xcb) pkg_check_modules(QGS REQUIRED gsettings-qt6) pkg_check_modules(MMIX REQUIRED libmatemixer) pkg_check_modules(kylin-nm-base REQUIRED kylin-nm-base) pkg_check_modules(LIGHTDM-GOBJ-1 REQUIRED liblightdm-gobject-1) pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd) pkg_check_modules(LIBXRANDR REQUIRED xrandr) # 查找pam动态库全路径并缓存到PAM_LIBRARIES变量 find_library(PAM_LIBRARIES pam) include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_SOURCE_DIR}/src) include_directories(${PROJECT_SOURCE_DIR}/src/common) include_directories(${PROJECT_SOURCE_DIR}/src/widgets) include_directories(${PROJECT_SOURCE_DIR}/src/QtSingleApplication) include_directories(${PROJECT_SOURCE_DIR}/src/dbusifs) include_directories(${PROJECT_SOURCE_DIR}/VirtualKeyboard/src) include_directories(${PROJECT_SOURCE_DIR}/screensaver) include_directories(${PROJECT_SOURCE_DIR}/src/liblightdm-qt) aux_source_directory(${PROJECT_SOURCE_DIR}/src/common COMMON) aux_source_directory(${PROJECT_SOURCE_DIR}/src/widgets WIDGEGS) aux_source_directory(${PROJECT_SOURCE_DIR}/src/lock-dialog LOCK_DIALOG) aux_source_directory(${PROJECT_SOURCE_DIR}/src/lock-backend LOCK_BACKEND) aux_source_directory(${PROJECT_SOURCE_DIR}/src/lock-command LOCK_COMMAND) aux_source_directory(${PROJECT_SOURCE_DIR}/src/lock-checkpasswd LOCK_CHECKPASSWD) aux_source_directory(${PROJECT_SOURCE_DIR}/src/lock-focushelper LOCK_FOCUSHELPER) aux_source_directory(${PROJECT_SOURCE_DIR}/src/liblightdm-qt LIGHTDM_QT) add_definitions(-DRESOURCE_PATH="/usr/share/ukui-screensaver/") add_definitions(-DENABLEPQ) set(SINGLE_GUI_APPLICATION_SRCS ${PROJECT_SOURCE_DIR}/src/QtSingleApplication/qtsingleapplication.cpp ${PROJECT_SOURCE_DIR}/src/QtSingleApplication/qtlocalpeer.cpp ) set(SINGLE_CORE_APPLICATION_SRCS ${PROJECT_SOURCE_DIR}/src/QtSingleApplication/qtsinglecoreapplication.cpp ${PROJECT_SOURCE_DIR}/src/QtSingleApplication/qtlocalpeer.cpp ) include_directories( ${X11_INCLUDE_DIRS} ${XTST_INCLUDE_DIRS} ${XCB_INCLUDE_DIRS} ${QGS_INCLUDE_DIRS} ${GIOUNIX2_INCLUDE_DIRS} ${GLIB2_INCLUDE_DIRS} ${MMIX_INCLUDE_DIRS} ${kylin-nm-base_INCLUDE_DIRS} ${LIGHTDM-GOBJ-1_INCLUDE_DIRS} ${LIBSYSTEMD_INCLUDE_DIRS} ${LIBXRANDR_INCLUDE_DIRS} ) set(EXTRA_LIBS ${EXTRA_LIBS} ${X11_LIBRARIES} ${XTST_LIBRARIES} ${XCB_LIBRARIES} ${QGS_LIBRARIES} ${GIOUNIX2_LIBRARIES} ${GLIB2_LIBRARIES} ${MMIX_LIBRARIES} ${LIBXRANDR_LIBRARIES} -lrt -lpthread ) option(SUPPORT_X11_BACKGROUND "Support draw x11 background" OFF) if (SUPPORT_X11_BACKGROUND) pkg_check_modules(IMLIB2 REQUIRED imlib2) include_directories( ${IMLIB2_INCLUDE_DIRS} ) set(EXTRA_LIBS ${EXTRA_LIBS} ${IMLIB2_LIBRARIES} ) add_definitions(-DSUPPORT_X11_BACKGROUND) endif() option(SUPPORT_NET_ICON "Support show network icon from library" ON) if (SUPPORT_NET_ICON) set(EXTRA_LIBS ${EXTRA_LIBS} -llibnm-icon-kylin ) add_definitions(-DSUPPORT_NET_ICON) endif() option(SUPPORT_TABLET_MODE "Support tablet mode check" OFF) if (SUPPORT_TABLET_MODE) set(BACKEND_EXTRA_LIBS ${BACKEND_EXTRA_LIBS} -lukuiinputgatherclient ) add_definitions(-DSUPPORT_TABLET_MODE) endif() #qt5_wrap_ui(dialog_SRC # lockwidget.ui # surewindow.ui # ) qt6_add_resources(dialog_SRC screensaver/default.qrc assets.qrc ) qt6_wrap_cpp(dialog_SRC userinfo.h agreementinfo.h dbusifs/freedesktophelper.h dbusifs/greeterconfig.h ) set(dialog_SRC ${dialog_SRC} ${COMMON} ${WIDGEGS} ${SINGLE_GUI_APPLICATION_SRCS} ${LOCK_DIALOG} userinfo.cpp agreementinfo.cpp dbusifs/uniauthservice.cpp dbusifs/biometrichelper.cpp dbusifs/freedesktophelper.cpp dbusifs/greeterconfig.cpp dbusifs/giodbus.cpp common/configuration.cpp common/displayservice.cpp common/capslockstatehelper.cpp ) add_executable(ukui-screensaver-dialog ${dialog_SRC}) add_definitions(-DAPP_API_MAJOR=0 -DAPP_API_MINOR=11 -DAPP_API_FUNC=0 ) target_include_directories(ukui-screensaver-dialog PRIVATE ${KYSDKSYSTIME_INCLUDE_DIRS} ${KYSDKWINDOWHELPER_INCLUDE_DIRS} ${KYSDKQTWIDGETS_INCLUDE_DIRS} ${KWAYLANDCLIENT_INCLUDE_DIRS}) target_link_directories(ukui-screensaver-dialog PRIVATE ${KYSDKSYSTIME_LIBRARY_DIRS} ${KYSDKWINDOWHELPER_LIBRARY_DIRS} ${KYSDKQTWIDGETS_LIBRARY_DIRS} ${KWAYLANDCLIENT_LIBRARY_DIRS}) target_link_libraries(ukui-screensaver-dialog Qt6::Core Qt6::Widgets Qt6::DBus Qt6::Svg Qt6::Network Qt6::Widgets ${KWAYLANDCLIENT_LIBRARIES} ${KYSDKSYSTIME_LIBRARIES} ${KYSDKWINDOWHELPER_LIBRARIES} ${KYSDKQTWIDGETS_LIBRARIES} KF6::NetworkManagerQt ${EXTRA_LIBS} VirtualKeyboard ukui-log4qt -ldl -lcrypto -lqrencode pwquality ) qt6_add_dbus_adaptor(backend_SRC lock-backend/org.ukui.ScreenSaver.xml lock-backend/dbusupperinterface.h DbusUpperInterface ) qt6_wrap_cpp(backend_SRC userinfo.h agreementinfo.h dbusifs/freedesktophelper.h dbusifs/greeterconfig.h dbusifs/upowerdbus.h dbusifs/upowerbatterydbus.h dbusifs/upowerlinepowerdbus.h dbusifs/upm_upowerdbus.h ukccplugins/sessiondbus/screensaverinterface.h ukccplugins/sessiondbus/screenlockinterface.h liblightdm-qt/QLightDM/greeter.h liblightdm-qt/QLightDM/power.h liblightdm-qt/QLightDM/usersmodel.h liblightdm-qt/QLightDM/sessionsmodel.h ) set(backend_SRC ${backend_SRC} ${LOCK_BACKEND} ${SINGLE_CORE_APPLICATION_SRCS} ${LIGHTDM_QT} dbusifs/login1helper.cpp dbusifs/usdhelper.cpp dbusifs/upowerhelper.cpp dbusifs/device.cpp dbusifs/accountshelper.cpp dbusifs/sessionhelper.cpp dbusifs/uniauthservice.cpp dbusifs/biometrichelper.cpp dbusifs/freedesktophelper.cpp dbusifs/giodbus.cpp dbusifs/kglobalaccelhelper.cpp dbusifs/machinemodel.cpp dbusifs/libinputswitchevent.cpp dbusifs/systemupgradehelper.cpp dbusifs/dbusservermanager.cpp dbusifs/networkmanagerhelper.cpp dbusifs/filemanagerhelper.cpp dbusifs/greeterconfig.cpp dbusifs/upowerdbus.cpp dbusifs/upowerbatterydbus.cpp dbusifs/upowerlinepowerdbus.cpp dbusifs/upm_upowerdbus.cpp userinfo.cpp agreementinfo.cpp common/global_utils.cpp common/configuration.cpp common/biodefines.cpp common/kyrsac.cpp ukccplugins/sessiondbus/screensaverinterface.cpp ukccplugins/sessiondbus/screenlockinterface.cpp ) add_executable(ukui-screensaver-backend ${backend_SRC}) target_link_libraries(ukui-screensaver-backend Qt6::Core Qt6::DBus Qt6::Network ${PAM_LIBRARIES} ${LIGHTDM-GOBJ-1_LIBRARIES} ${QGS_LIBRARIES} ${LIBSYSTEMD_LIBRARIES} ${GLIB2_LIBRARIES} ukui-log4qt -ldl -lcrypto ${BACKEND_EXTRA_LIBS} ) set(command_SRC ${LOCK_COMMAND} ) add_executable(ukui-screensaver-command ${command_SRC}) target_link_libraries(ukui-screensaver-command Qt6::Core Qt6::Gui Qt6::DBus ukui-log4qt) set(checkpass_SRC ${LOCK_CHECKPASSWD} ) add_executable(ukui-screensaver-checkpass ${checkpass_SRC}) target_link_libraries(ukui-screensaver-checkpass ${PAM_LIBRARIES}) set(focushelper_SRCS ${focushelper_SRCS} ${LOCK_FOCUSHELPER} widgets/screen-manager.h widgets/screen-manager.cpp ) add_executable(screensaver-focus-helper ${focushelper_SRCS}) target_link_libraries(screensaver-focus-helper Qt6::Core Qt6::Widgets ${X11_LIBRARIES} ${XCB_LIBRARIES}) add_subdirectory(VirtualKeyboard) add_subdirectory(screensaver) add_subdirectory(ukccplugins) add_dependencies(ukui-screensaver-dialog VirtualKeyboard ukui-screensaver-default screensaver-default Screensaver) install(TARGETS ukui-screensaver-dialog ukui-screensaver-backend ukui-screensaver-command # ukui-screensaver-checkpass DESTINATION bin) #install(TARGETS # screensaver-focus-helper # DESTINATION lib/ukui-screensaver) install(DIRECTORY assets/badges DESTINATION /usr/share/ukui-screensaver/images/) ukui-screensaver/src/dbusifs/0000775000175000017500000000000015172041106015216 5ustar fengfengukui-screensaver/src/dbusifs/device.h0000664000175000017500000001772615172041035016644 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef DEVICE_H #define DEVICE_H #include #define GPM_DBUS_SERVICE "org.ukui.PowerManager" #define GPM_DBUS_INTERFACE "org.ukui.PowerManager" #define GPM_DBUS_INTERFACE_BACKLIGHT "org.ukui.PowerManager.Backlight" #define GPM_DBUS_INTERFACE_KBD_BACKLIGHT "org.ukui.PowerManager.KbdBacklight" #define GPM_DBUS_PATH "/org/ukui/PowerManager" #define GPM_DBUS_PATH_BACKLIGHT "/org/ukui/PowerManager/Backlight" #define GPM_DBUS_PATH_KBD_BACKLIGHT "/org/ukui/PowerManager/KbdBacklight" /* common descriptions of this program */ #define GPM_NAME _("Power Manager") #define GPM_DESCRIPTION _("Power Manager for the MATE desktop") /* schema location */ #define GPM_SETTINGS_SCHEMA "org.ukui.power-manager" #define GPM_SETTINGS_KEY_POLICY "icon-policy" /* actions */ #define GPM_SETTINGS_ACTION_CRITICAL_UPS "action-critical-ups" #define GPM_SETTINGS_ACTION_CRITICAL_BATT "action-critical-battery" #define GPM_SETTINGS_ACTION_LOW_UPS "action-low-ups" #define GPM_SETTINGS_ACTION_SLEEP_TYPE_AC "action-sleep-type-ac" #define GPM_SETTINGS_ACTION_SLEEP_TYPE_BATT "action-sleep-type-battery" #define GPM_SETTINGS_SLEEP_WHEN_CLOSED "event-when-closed-battery" /* backlight stuff */ #define GPM_SETTINGS_BACKLIGHT_ENABLE "backlight-enable" #define GPM_SETTINGS_BACKLIGHT_BATTERY_REDUCE "backlight-battery-reduce" #define GPM_SETTINGS_DPMS_METHOD_AC "dpms-method-ac" #define GPM_SETTINGS_DPMS_METHOD_BATT "dpms-method-battery" #define GPM_SETTINGS_IDLE_BRIGHTNESS "idle-brightness" #define GPM_SETTINGS_IDLE_DIM_AC "idle-dim-ac" #define GPM_SETTINGS_IDLE_DIM_BATT "idle-dim-battery" #define GPM_SETTINGS_IDLE_DIM_TIME "idle-dim-time" #define GPM_SETTINGS_BRIGHTNESS_AC "brightness-ac" #define GPM_SETTINGS_BRIGHTNESS_BAT "brightness-bat" #define GPM_SETTINGS_BRIGHTNESS_DIM_BATT "brightness-dim-battery" /* keyboard backlight */ #define GPM_SETTINGS_KBD_BACKLIGHT_BATT_REDUCE "kbd-backlight-battery-reduce" #define GPM_SETTINGS_KBD_BRIGHTNESS_ON_AC "kbd-brightness-on-ac" #define GPM_SETTINGS_KBD_BRIGHTNESS_DIM_BY_ON_BATT "kbd-brightness-dim-by-on-battery" #define GPM_SETTINGS_KBD_BRIGHTNESS_DIM_BY_ON_IDLE "kbd-brightness-dim-by-on-idle" /* buttons */ #define GPM_SETTINGS_BUTTON_LID_AC "button-lid-ac" #define GPM_SETTINGS_BUTTON_LID_BATT "button-lid-battery" #define GPM_SETTINGS_BUTTON_SUSPEND "button-suspend" #define GPM_SETTINGS_BUTTON_HIBERNATE "button-hibernate" #define GPM_SETTINGS_BUTTON_POWER "button-power" /* general */ #define GPM_SETTINGS_USE_TIME_POLICY "use-time-for-policy" #define GPM_SETTINGS_NETWORKMANAGER_SLEEP "network-sleep" #define GPM_SETTINGS_IDLE_CHECK_CPU "check-type-cpu" /* notify */ #define GPM_SETTINGS_NOTIFY_LOW_CAPACITY "notify-low-capacity" #define GPM_SETTINGS_NOTIFY_DISCHARGING "notify-discharging" #define GPM_SETTINGS_NOTIFY_FULLY_CHARGED "notify-fully-charged" #define GPM_SETTINGS_NOTIFY_SLEEP_FAILED "notify-sleep-failed" #define GPM_SETTINGS_NOTIFY_SLEEP_FAILED_URI "notify-sleep-failed-uri" #define GPM_SETTINGS_NOTIFY_LOW_POWER "notify-low-power" #define GPM_SETTINGS_BAT_POLICY "power-policy-battery" #define GPM_SETTINGS_ON_BAT_AUTO_SAVE "on-battery-auto-save" #define GPM_SETTINGS_LOW_BAT_AUTO_SAVE "low-battery-auto-save" #define GPM_SETTINGS_DISPLAY_LEFT_TIME "dispaly-left-time-of-charge-and-discharge" /* thresholds */ #define GPM_SETTINGS_PERCENTAGE_LOW "percentage-low" #define GPM_SETTINGS_PERCENTAGE_CRITICAL "percentage-critical" #define GPM_SETTINGS_PERCENTAGE_ACTION "percentage-action" #define GPM_SETTINGS_TIME_LOW "time-low" #define GPM_SETTINGS_TIME_CRITICAL "time-critical" #define GPM_SETTINGS_TIME_ACTION "time-action" /* timeout */ #define GPM_SETTINGS_SLEEP_COMPUTER_AC "sleep-computer-ac" #define GPM_SETTINGS_SLEEP_COMPUTER_BATT "sleep-computer-battery" #define GPM_SETTINGS_SLEEP_COMPUTER_UPS "sleep-computer-ups" #define GPM_SETTINGS_SLEEP_DISPLAY_AC "sleep-display-ac" #define GPM_SETTINGS_SLEEP_DISPLAY_BATT "sleep-display-battery" #define GPM_SETTINGS_SLEEP_DISPLAY_UPS "sleep-display-ups" /* ui */ #define GPM_SETTINGS_ICON_POLICY "icon-policy" #define GPM_SETTINGS_ENABLE_SOUND "enable-sound" #define GPM_SETTINGS_SHOW_ACTIONS "show-actions" /* statistics */ #define GPM_SETTINGS_INFO_HISTORY_TIME "info-history-time" #define GPM_SETTINGS_INFO_HISTORY_TYPE "info-history-type" #define GPM_SETTINGS_INFO_HISTORY_GRAPH_SMOOTH "info-history-graph-smooth" #define GPM_SETTINGS_INFO_HISTORY_GRAPH_POINTS "info-history-graph-points" #define GPM_SETTINGS_INFO_STATS_TYPE "info-stats-type" #define GPM_SETTINGS_INFO_STATS_GRAPH_SMOOTH "info-stats-graph-smooth" #define GPM_SETTINGS_INFO_STATS_GRAPH_POINTS "info-stats-graph-points" #define GPM_SETTINGS_INFO_PAGE_NUMBER "info-page-number" #define GPM_SETTINGS_INFO_LAST_DEVICE "info-last-device" #if __aarch64__ #define ARCH64 TRUE #else #define ARCH64 FALSE #endif typedef enum { GPM_ICON_POLICY_ALWAYS, GPM_ICON_POLICY_PRESENT, GPM_ICON_POLICY_CHARGE, GPM_ICON_POLICY_LOW, GPM_ICON_POLICY_CRITICAL, GPM_ICON_POLICY_NEVER } GpmIconPolicy; typedef enum { GPM_ACTION_POLICY_BLANK, GPM_ACTION_POLICY_SUSPEND, GPM_ACTION_POLICY_SHUTDOWN, GPM_ACTION_POLICY_HIBERNATE, GPM_ACTION_POLICY_INTERACTIVE, GPM_ACTION_POLICY_NOTHING } GpmActionPolicy; typedef enum { UP_DEVICE_KIND_UNKNOWN, UP_DEVICE_KIND_LINE_POWER, UP_DEVICE_KIND_BATTERY, UP_DEVICE_KIND_UPS, UP_DEVICE_KIND_MONITOR, UP_DEVICE_KIND_MOUSE, UP_DEVICE_KIND_KEYBOARD, UP_DEVICE_KIND_PDA, UP_DEVICE_KIND_PHONE, UP_DEVICE_KIND_MEDIA_PLAYER, UP_DEVICE_KIND_TABLET, UP_DEVICE_KIND_COMPUTER, UP_DEVICE_KIND_LAST } UpDeviceKind; /** * UpDeviceState: * * The device state. **/ typedef enum { UP_DEVICE_STATE_UNKNOWN, UP_DEVICE_STATE_CHARGING, UP_DEVICE_STATE_DISCHARGING, UP_DEVICE_STATE_EMPTY, UP_DEVICE_STATE_FULLY_CHARGED, UP_DEVICE_STATE_PENDING_CHARGE, UP_DEVICE_STATE_PENDING_DISCHARGE, UP_DEVICE_STATE_LAST } UpDeviceState; /** * UpDeviceTechnology: * * The device technology. **/ typedef enum { UP_DEVICE_TECHNOLOGY_UNKNOWN, UP_DEVICE_TECHNOLOGY_LITHIUM_ION, UP_DEVICE_TECHNOLOGY_LITHIUM_POLYMER, UP_DEVICE_TECHNOLOGY_LITHIUM_IRON_PHOSPHATE, UP_DEVICE_TECHNOLOGY_LEAD_ACID, UP_DEVICE_TECHNOLOGY_NICKEL_CADMIUM, UP_DEVICE_TECHNOLOGY_NICKEL_METAL_HYDRIDE, UP_DEVICE_TECHNOLOGY_LAST } UpDeviceTechnology; /** * UpDeviceLevel: * * The warning level of a battery. **/ typedef enum { UP_DEVICE_LEVEL_UNKNOWN, UP_DEVICE_LEVEL_NONE, UP_DEVICE_LEVEL_DISCHARGING, UP_DEVICE_LEVEL_LOW, UP_DEVICE_LEVEL_CRITICAL, UP_DEVICE_LEVEL_ACTION, UP_DEVICE_LEVEL_LAST } UpDeviceLevel; struct DEV { UpDeviceKind kind; UpDeviceLevel warnlevel; QString Device; QString Type; QString PowerSupply; QString Online; QString Model; QString Energy; QString EnergyEmpty; QString EnergyFull; QString EnergyRate; bool IsPresent; QString IsRechargeable; double Percentage; UpDeviceState State; qlonglong TimeToEmpty; qlonglong TimeToFull; QString Voltage; double Capacity; QString Technology; QString path; }; class DEVICE : public QObject { Q_OBJECT public: explicit DEVICE(QObject *parent = nullptr); ~DEVICE(); Q_SIGNALS: void device_property_changed(QDBusMessage msg, QString path); public Q_SLOTS: void handleChanged(QDBusMessage msg); public: DEV m_dev; }; #endif // DEVICE_H ukui-screensaver/src/dbusifs/greeterconfig.cpp0000664000175000017500000002222015172041106020543 0ustar fengfeng/* * Copyright (C) 2024 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "greeterconfig.h" #include #include "../common/definetypes.h" std::unique_ptr GreeterConfig::m_instance; std::once_flag GreeterConfig::m_initFlag; GreeterConfig::GreeterConfig(QObject *parent) : QDBusAbstractInterface( UNIAUTH_DBUS_SERVICE, UNIAUTH_GREETERCONFIG_DBUS_PATH, UNIAUTH_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) { setTimeout(2147483647); } GreeterConfig *GreeterConfig::instance(QObject *parent) { std::call_once(m_initFlag, [&, parent]() { m_instance.reset(new GreeterConfig(parent)); }); return m_instance.get(); } // 设置/获取虚拟键盘使能 void GreeterConfig::setVirtualKeyboardEnable(bool isEnable) { QDBusMessage result = call(QStringLiteral("setVirtualKeyboardEnable"), isEnable); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setVirtualKeyboardEnable error:" << result.errorMessage(); } } bool GreeterConfig::getVirtualKeyboardEnable() { QDBusMessage result = call(QStringLiteral("getVirtualKeyboardEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getVirtualKeyboardEnable error:" << result.errorMessage(); return true; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return true; } } bool GreeterConfig::resetVirtualKeyboardEnable() { QDBusMessage result = call(QStringLiteral("resetVirtualKeyboardEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "resetVirtualKeyboardEnable error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 设置/获取用户切换使能 void GreeterConfig::setUserSwitchEnable(bool isEnable) { QDBusMessage result = call(QStringLiteral("setUserSwitchEnable"), isEnable); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setUserSwitchEnable error:" << result.errorMessage(); } } bool GreeterConfig::getUserSwitchEnable() { QDBusMessage result = call(QStringLiteral("getUserSwitchEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getUserSwitchEnable error:" << result.errorMessage(); return true; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return true; } } bool GreeterConfig::resetUserSwitchEnable() { QDBusMessage result = call(QStringLiteral("resetUserSwitchEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "resetUserSwitchEnable error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 设置/获取网络使能 void GreeterConfig::setNetworkEnable(bool isEnable) { QDBusMessage result = call(QStringLiteral("setNetworkEnable"), isEnable); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setNetworkEnable error:" << result.errorMessage(); } } bool GreeterConfig::getNetworkEnable() { QDBusMessage result = call(QStringLiteral("getNetworkEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getNetworkEnable error:" << result.errorMessage(); return true; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return true; } } bool GreeterConfig::resetNetworkEnable() { QDBusMessage result = call(QStringLiteral("resetNetworkEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "resetNetworkEnable error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 设置/获取电源管理使能 void GreeterConfig::setPowerManagerEnable(bool isEnable) { QDBusMessage result = call(QStringLiteral("setPowerManagerEnable"), isEnable); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setPowerManagerEnable error:" << result.errorMessage(); } } bool GreeterConfig::getPowerManagerEnable() { QDBusMessage result = call(QStringLiteral("getPowerManagerEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getPowerManagerEnable error:" << result.errorMessage(); return true; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return true; } } bool GreeterConfig::resetPowerManagerEnable() { QDBusMessage result = call(QStringLiteral("resetPowerManagerEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "resetPowerManagerEnable error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 锁屏日期时间使能 void GreeterConfig::setLockScreenDatetimeEnable(bool isEnable) { QDBusMessage result = call(QStringLiteral("setLockScreenDatetimeEnable"), isEnable); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setLockScreenDatetimeEnable error:" << result.errorMessage(); } } bool GreeterConfig::getLockScreenDatetimeEnable(QString strUserName) { QDBusMessage result = call(QStringLiteral("getLockScreenDatetimeEnable"), strUserName); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getLockScreenDatetimeEnable error:" << result.errorMessage(); return true; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return true; } } bool GreeterConfig::resetLockScreenDatetimeEnable() { QDBusMessage result = call(QStringLiteral("resetLockScreenDatetimeEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "resetLockScreenDatetimeEnable error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 屏保时间日期使能 void GreeterConfig::setScreensaverDatetimeEnable(bool isEnable) { QDBusMessage result = call(QStringLiteral("setScreensaverDatetimeEnable"), isEnable); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setScreensaverDatetimeEnable error:" << result.errorMessage(); } } bool GreeterConfig::getScreensaverDatetimeEnable(QString strUserName) { QDBusMessage result = call(QStringLiteral("getScreensaverDatetimeEnable"), strUserName); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getScreensaverDatetimeEnable error:" << result.errorMessage(); return true; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return true; } } bool GreeterConfig::resetScreensaverDatetimeEnable() { QDBusMessage result = call(QStringLiteral("resetScreensaverDatetimeEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "resetScreensaverDatetimeEnable error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } bool GreeterConfig::getSelectManualLoginDefault() { QDBusMessage result = call(QStringLiteral("getSelectManualLoginDefault")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getSelectManualLoginDefault error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } ukui-screensaver/src/dbusifs/greeterconfig.h0000664000175000017500000000551615172041106020221 0ustar fengfeng/* * Copyright (C) 2024 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef GREETERCONFIG_H #define GREETERCONFIG_H #include #include class GreeterConfig : public QDBusAbstractInterface { Q_OBJECT private: explicit GreeterConfig(QObject *parent = nullptr); public: /** * @brief 单实例 * * @param parent 父指针 * @return GreeterConfig 单实例对象指针 */ static GreeterConfig *instance(QObject *parent = nullptr); public Q_SLOTS: // 设置/获取虚拟键盘使能 void setVirtualKeyboardEnable(bool isEnable); bool getVirtualKeyboardEnable(); bool resetVirtualKeyboardEnable(); // 设置/获取用户切换使能 void setUserSwitchEnable(bool isEnable); bool getUserSwitchEnable(); bool resetUserSwitchEnable(); // 设置/获取网络使能 void setNetworkEnable(bool isEnable); bool getNetworkEnable(); bool resetNetworkEnable(); // 设置/获取电源管理使能 void setPowerManagerEnable(bool isEnable); bool getPowerManagerEnable(); bool resetPowerManagerEnable(); // 锁屏日期时间使能 void setLockScreenDatetimeEnable(bool isEnable); bool getLockScreenDatetimeEnable(QString strUserName); bool resetLockScreenDatetimeEnable(); // 屏保时间日期使能 void setScreensaverDatetimeEnable(bool isEnable); bool getScreensaverDatetimeEnable(QString strUserName); bool resetScreensaverDatetimeEnable(); // 是否默认选择手动输用户名登录 bool getSelectManualLoginDefault(); Q_SIGNALS: //功能使能状态改变 void virtualKeyboardEnableChanged(bool isEnable); void userSwitchEnableChanged(bool isEnable); void networkEnableChanged(bool isEnable); void powerManagerEnableChanged(bool isEnable); void lockScreenDatetimeEnableChanged(bool isEnable, QString strUserName); void screensaverDatetimeChanged(bool isEnable, QString strUserName); //开关状态改变 void changed(const QString &group, const QString &key); private: static std::unique_ptr m_instance; /** 统一认证配置服务单实例指针 */ static std::once_flag m_initFlag; }; #endif // GREETERCONFIG_H ukui-screensaver/src/dbusifs/filemanagerhelper.cpp0000664000175000017500000000213015172041106021370 0ustar fengfeng#include "filemanagerhelper.h" #include #include #include #include #include const static QString fileService = QStringLiteral("org.freedesktop.FileManager1"); const static QString fileManagerPath = QStringLiteral("/org/freedesktop/FileManager1"); const static QString fileManagerInterface = QStringLiteral("org.freedesktop.FileManager1"); FileManagerHelper::FileManagerHelper(QObject *parent) : QObject{parent} {} FileManagerHelper::~FileManagerHelper() {} void FileManagerHelper::init() { if (!m_fileManagerInterface) { m_fileManagerInterface = new QDBusInterface( fileService, fileManagerPath, fileManagerInterface, QDBusConnection::sessionBus(), this); } } bool FileManagerHelper::showFolders(QStringList path) { init(); QDBusPendingCall result = m_fileManagerInterface->asyncCall("ShowFolders", path, ""); if (result.reply().type() == QDBusMessage::ErrorMessage) { qWarning() << "ShowFolders error!!!!!!!!!!!!!!"; return false; } return true; } ukui-screensaver/src/dbusifs/upm_upowerdbus.cpp0000664000175000017500000002015415172041106021004 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #include #include "upm_upowerdbus.h" Q_GLOBAL_STATIC(UpmUpowerDBus, s_upowerDBus) UpmUpowerDBus::UpmUpowerDBus(QObject *parent) : QObject(parent) { m_upowerDBus = new UPowerDBus(this); connect(m_upowerDBus, &UPowerDBus::lidStateChanged, this, &UpmUpowerDBus::dealLidStateChanged); connect(m_upowerDBus, &UPowerDBus::batteryOnlineStateChanged, this, &UpmUpowerDBus::dealBatteryOnlineStateChanged); initializeBatteryObjects(); QStringList linePowerPathList = m_upowerDBus->getLinePowerPathList(); for (int index = 0; index < linePowerPathList.size(); ++index) { UpowerLinePowerDBus *upowerLinePowerDBus = new UpowerLinePowerDBus(index, linePowerPathList.at(index), this); connect(upowerLinePowerDBus, &UpowerLinePowerDBus::acOnlineStateChanged, this, &UpmUpowerDBus::dealAcOnlineStateChanged); m_linePowerDBusVector << upowerLinePowerDBus; } qDebug() << "Battery Num:" << m_batteryDBusVector.size(); qDebug() << "line power Num:" << m_linePowerDBusVector.size(); QDBusConnection::systemBus().connect("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "DeviceAdded", this, SLOT(onDeviceAdded(QDBusObjectPath))); QDBusConnection::systemBus().connect("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "DeviceRemoved", this, SLOT(onDeviceRemoved(QDBusObjectPath))); } UpmUpowerDBus::~UpmUpowerDBus() {} UpmUpowerDBus *UpmUpowerDBus::self() { return s_upowerDBus; } void UpmUpowerDBus::refreshBatteryInfo() { for (int batteryNum = 0; batteryNum < m_batteryDBusVector.size(); ++batteryNum) { m_batteryDBusVector.at(batteryNum)->refreshBatteryInfo(); } } void UpmUpowerDBus::dealLidStateChanged(bool value) { qDebug() << "lid state changed:" << value; Q_EMIT lidStateChanged(value); } void UpmUpowerDBus::dealBatteryOnlineStateChanged(bool value) { Q_EMIT batteryOnlineStateChanged(value); } void UpmUpowerDBus::dealBatteryInfoChanged(int index, QStringList batteryInfoChangedList) { qDebug() << "battery info changed"; Q_EMIT batteryInfoChanged(index, batteryInfoChangedList); } void UpmUpowerDBus::dealAcOnlineStateChanged(int index, bool value) { qDebug() << "ac online changed:" << value; Q_EMIT acOnlineStateChanged(index, value); } bool UpmUpowerDBus::getLidClosedState() { return m_upowerDBus->getLidClosedState(); } bool UpmUpowerDBus::getBatteryOnlineState() { return m_upowerDBus->getBatteryOnlineState(); } bool UpmUpowerDBus::hasBattery() { if (0 == m_batteryDBusVector.size()) { return false; } for (int batteryNum = 0; batteryNum < m_batteryDBusVector.size(); ++batteryNum) { if (true == batteryIsPresent(batteryNum)) { qInfo() << "Equipment has battery"; return true; } } qInfo() << "Equipment no battery"; return false; } bool UpmUpowerDBus::batteryIsPresent(int index) { return m_batteryDBusVector.at(index)->batteryIsPresent(); } int UpmUpowerDBus::getBatteryState(int index) { if (0 == m_batteryDBusVector.size()) { return 0; } return m_batteryDBusVector.at(index)->getBatteryState(); } int UpmUpowerDBus::getBatteryNum() { return m_batteryDBusVector.size(); } int UpmUpowerDBus::getLinePowerNum() { return m_linePowerDBusVector.size(); } double UpmUpowerDBus::getBatteryPercentage() { if (0 == m_batteryDBusVector.size()) { return 0; } double percentage = 0.0; for (int index = 0; index < m_batteryDBusVector.size(); ++index) { percentage += m_batteryDBusVector.at(index)->getBatteryPercentage(); } return percentage / m_batteryDBusVector.size(); } qlonglong UpmUpowerDBus::getTimeToFull(int index) { if (0 == m_batteryDBusVector.size()) { return 0; } return m_batteryDBusVector.at(index)->getTimeToFull(); } qlonglong UpmUpowerDBus::getTimeToEmpty(int index) { if (0 == m_batteryDBusVector.size()) { return 0; } return m_batteryDBusVector.at(index)->getTimeToEmpty(); } QString UpmUpowerDBus::getBatteryIconName(int index) { //处理没有电池的情况 if(!hasBattery()) { if(m_linePowerDBusVector.size() > 0) { if(true == getAcOnlineState()) { qDebug() << "Laptop with no battery detected, showing special battery icon"; return "battery-error-charging-symbolic"; } } } if (0 == m_batteryDBusVector.size() || 0 == m_linePowerDBusVector.size()) { return "battery-level-0-charging-symbolic"; } double percentage = m_batteryDBusVector.at(index)->getBatteryPercentage(); if (true == getAcOnlineState()) { m_batteryIconName = QString("battery-level-%1-charging-symbolic").arg(qRound(percentage / 10.0) * 10); } else { m_batteryIconName = QString("battery-level-%1-symbolic").arg(qRound(percentage / 10.0) * 10); } return m_batteryIconName; } bool UpmUpowerDBus::getAcOnlineState() { if (0 == m_linePowerDBusVector.size()) { return false; } for (int index = 0; index < m_linePowerDBusVector.size(); ++index) { if (true == m_linePowerDBusVector.at(index)->getAcOnlineState() && true == m_linePowerDBusVector.at(index)->getOnLineStateFromSysFile()) { return true; } } return false; } void UpmUpowerDBus::onDeviceAdded(QDBusObjectPath devicePath) { const QString path = devicePath.path(); qInfo() << "Device added:" << path; if(m_upowerDBus->getDeviceType(path) == up_device_type_battery) { rebuildBatteryObjects(); Q_EMIT deviceChanged(); } } void UpmUpowerDBus::onDeviceRemoved(QDBusObjectPath devicePath) { const QString path = devicePath.path(); qInfo() << "Device removed:" << path; if(path.contains("BAT")) { rebuildBatteryObjects(); Q_EMIT deviceChanged(); } } void UpmUpowerDBus::clearBatteryObjects() { for (auto* battery : m_batteryDBusVector) { delete battery; } m_batteryDBusVector.clear(); } void UpmUpowerDBus::initializeBatteryObjects() { QStringList batteryPathList = m_upowerDBus->getBatteryPathList(); for (int index = 0; index < batteryPathList.size(); ++index) { UpowerBatteryDBus *upowerBatteryDBus = new UpowerBatteryDBus(index, batteryPathList.at(index), this); connect(upowerBatteryDBus, &UpowerBatteryDBus::batteryInfoChanged, this, &UpmUpowerDBus::dealBatteryInfoChanged); m_batteryDBusVector << upowerBatteryDBus; } qDebug() << "Initialized" << m_batteryDBusVector.size() << "battery objects"; } void UpmUpowerDBus::rebuildBatteryObjects() { clearBatteryObjects(); m_upowerDBus->refreshDevicePathLists(); initializeBatteryObjects(); } bool UpmUpowerDBus::getAcOnlineStateFromSysfile() { if (0 == m_linePowerDBusVector.size()) { return false; } for (int index = 0; index < m_linePowerDBusVector.size(); ++index) { if (true == m_linePowerDBusVector.at(index)->getOnLineStateFromSysFile()) { return true; } } return false; } ukui-screensaver/src/dbusifs/upowerbatterydbus.cpp0000664000175000017500000001031015172041106021507 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #include "upowerbatterydbus.h" UpowerBatteryDBus::UpowerBatteryDBus(int batteryId, const QString &batteryDBusPath, QObject *parent) : QObject(parent), m_batteryId(batteryId), m_batteryDBusPath(batteryDBusPath) { m_DBusInterface = new QDBusInterface("org.freedesktop.UPower", m_batteryDBusPath, "org.freedesktop.DBus.Properties", QDBusConnection::systemBus(), this); QDBusConnection::systemBus().connect("org.freedesktop.UPower", m_batteryDBusPath, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(dealBatteryPropertiesMessage(QDBusMessage))); initBatteryInfo(); } UpowerBatteryDBus::~UpowerBatteryDBus() {} void UpowerBatteryDBus::dealBatteryPropertiesMessage(QDBusMessage msg) { const QDBusArgument &dbusArgs = msg.arguments().at(1).value(); QMap map; dbusArgs >> map; QStringList batteryInfoChangedList; if (map.contains("IsPresent")) { m_batteryPresent = map.value(QString("IsPresent")).toBool(); batteryInfoChangedList.append("IsPresent"); } if (map.contains("State")) { m_batteryState = map.value(QString("State")).toInt(); batteryInfoChangedList.append("State"); } if (map.contains("Percentage")) { m_percentage = map.value(QString("Percentage")).toDouble(); batteryInfoChangedList.append("Percentage"); } if (map.contains("TimeToFull")) { m_timeToFull = map.value(QString("TimeToFull")).toLongLong(); batteryInfoChangedList.append("TimeToFull"); } if (map.contains("TimeToEmpty")) { m_timeToEmpty = map.value(QString("TimeToEmpty")).toLongLong(); batteryInfoChangedList.append("TimeToEmpty"); } if (0 != batteryInfoChangedList.size()) { Q_EMIT batteryInfoChanged(m_batteryId, batteryInfoChangedList); } } void UpowerBatteryDBus::initBatteryInfo() { QDBusMessage msg = m_DBusInterface->call("GetAll", "org.freedesktop.UPower.Device"); if (msg.type() == QDBusMessage::ReplyMessage) { const QDBusArgument &dbusArgs = msg.arguments().at(0).value(); QMap map; dbusArgs >> map; m_batteryPresent = map.value(QString("IsPresent")).toBool(); m_batteryState = map.value(QString("State")).toInt(); m_percentage = map.value(QString("Percentage")).toDouble(); m_timeToFull = map.value(QString("TimeToFull")).toLongLong(); m_timeToEmpty = map.value(QString("TimeToEmpty")).toLongLong(); } } bool UpowerBatteryDBus::batteryIsPresent() { return m_batteryPresent; } int UpowerBatteryDBus::getBatteryState() { return m_batteryState; } double UpowerBatteryDBus::getBatteryPercentage() { return m_percentage; } qlonglong UpowerBatteryDBus::getTimeToFull() { return m_timeToFull; } qlonglong UpowerBatteryDBus::getTimeToEmpty() { return m_timeToEmpty; } void UpowerBatteryDBus::refreshBatteryInfo() { QDBusInterface dBusInterface("org.freedesktop.UPower", m_batteryDBusPath, "org.freedesktop.UPower.Device", QDBusConnection::systemBus()); dBusInterface.call("Refresh"); } ukui-screensaver/src/dbusifs/libinputswitchevent.h0000664000175000017500000000515715172041106021511 0ustar fengfeng/* * Copyright (C) 2023 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LIBINPUTSWITCHEVENT_H #define LIBINPUTSWITCHEVENT_H #ifdef SUPPORT_TABLET_MODE #include #include #include #include #include #include "machinemodel.h" //头文件以及顺序不可改,不可删 class GatherClientWorker : public QObject { Q_OBJECT public: explicit GatherClientWorker(QObject* parent = nullptr); /** * @brief start 启动工作 */ void start(); /** * @brief stop 通知工作完成 */ void stop(); private: void dealEvent(Event *e); Q_SIGNALS: /** * @brief tabletModeStatusChanged 平板模式改变信号 * @param tabletmode 平板模式 */ void tabletModeStatusChanged(int tabletmode); /** * @brief finished 通知线程退出事件循环 */ void finished(); private: UKUIInputGatherClient *m_inputGatherClient = nullptr; }; class GatherClientThread : public QThread { Q_OBJECT public: explicit GatherClientThread(QObject *parent = nullptr); /** * @brief setGatherClientWorker 设置工作者 * @param worker 工作者实例 */ void setGatherClientWorker(GatherClientWorker *worker); /** * @brief stop 通知工作者停止工作 */ void stop(); protected: /** * @brief run 线程处理过程 */ void run() override; private: GatherClientWorker *m_gatherClientWorker = nullptr; }; class LibinputSwitchEvent : public QObject { Q_OBJECT public: explicit LibinputSwitchEvent(QObject *parent = nullptr); ~LibinputSwitchEvent(); bool geInitDevicesStatus(); signals: void tabletModeStatusChanged(int tabletmode); private: QString m_machineType; std::shared_ptr m_machineModel = nullptr; GatherClientThread m_gatherClientThread; GatherClientWorker m_gatherClientWorker; }; #endif // SUPPORT_TABLET_MODE #endif // LIBINPUTSWITCHEVENT_H ukui-screensaver/src/dbusifs/login1helper.h0000664000175000017500000000577415172041035017776 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LOGIN1HELPER_H #define LOGIN1HELPER_H #include #include #include #include "../common/definetypes.h" /** * @brief login1访问工具类 * */ class Login1Helper : public QObject { Q_OBJECT public: /** * @brief 构造 * * @param parent 父指针 */ explicit Login1Helper(QObject *parent = nullptr); /** * @brief 析构 * */ virtual ~Login1Helper(); /** * @brief 当前会话是否处于活跃 * * @return bool true 是,否则否 */ bool isSessionActive(); void setPowerManager(const QString &powerManagerfunc); bool getCanPowerManager(const QString &powerManagerfunc); bool isCanHibernate(); bool isCanSuspend(); bool isCanReboot(); bool isCanPowerOff(); bool canAction(Action action); void Unlock(); public Q_SLOTS: /** * @brief 会话属性改变 * * @param QString 接口名 * @param QVariantMap 变化的属性表 * @param QStringList 无效的属性 */ void onSessionPropChanged(QString, QVariantMap, QStringList); /** * @brief 准备休眠/唤醒 * * @param isSleep true 休眠,false 唤醒 */ void onPrepareForSleep(bool isSleep); /** * @brief 程序阻塞关机/睡眠状态改变 * */ void onHibitedWatcherMessage(void); Q_SIGNALS: /** * @brief 请求锁定 * */ void requestLock(); /** * @brief 请求解锁 * */ void requestUnlock(); /** * @brief 会话活跃状态改变 * * @param isActive true 活跃,false 不活跃 */ void sessionActiveChanged(bool isActive); /** * @brief 准备休眠/唤醒 * * @param isSleep true 休眠,false 唤醒 */ void PrepareForSleep(bool isSleep); /** * @brief 程序阻塞关机/睡眠状态改变 * * @param blockInhibited 阻塞类型 */ void blockInhibitedChanged(QString blockInhibited); private: bool m_isSessionActive = false; /**< 是否处于活跃状态 */ bool m_isSleeping = false; /**< 是否处于休眠状态 */ bool m_isCanLockScreen = true; bool m_isCanLogout = true; QString m_strSessionPath; // 当前会话路径 QDBusInterface *m_login1Interface = nullptr; }; #endif // LOGIN1HELPER_H ukui-screensaver/src/dbusifs/machinemodel.cpp0000664000175000017500000000624615172041035020360 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "machinemodel.h" #include #include #include #include std::shared_ptr MachineModel::m_machineModel = nullptr; std::mutex MachineModel::m_mutex; MachineModel::MachineModel(QObject *parent) : QObject(parent) { initMachineType(); } QString MachineModel::getTheMachineType() { return m_machineType; } std::shared_ptr MachineModel::getMachineModelInstance() { if (m_machineModel == nullptr) { std::unique_lock lock(m_mutex); if (m_machineModel == nullptr) { m_machineModel = std::shared_ptr(new MachineModel); } } return m_machineModel; } void MachineModel::initMachineType() { QString type = getSysVendor() + getProductFamily(); qDebug() << "MachineType:" << type; QSettings setting(":/assets/data/conf.ini", QSettings::IniFormat); setting.beginGroup("MachineType"); //节点开始 if (setting.contains(type)) { m_machineType = setting.value(type).toString(); } else { m_machineType = QString(); } setting.endGroup(); //节点结束 qDebug() << "SettingsMachineType:" << m_machineType; } static QString readFileSafely(const QString& filePath) { QString strContent = ""; if (!QFile::exists(filePath)) { qWarning() << "file not exists:" << filePath; return strContent; } QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "file open failed " << filePath << file.error(); return strContent; } QTextStream testStream(&file); return testStream.readAll(); } const QString MachineModel::getSysVendor() const { QString result = readFileSafely("/sys/class/dmi/id/sys_vendor"); QStringList list = result.split("\n"); result = list.at(0); qDebug() << __FILE__ << __LINE__ << "获取设备厂商为:" << result; return result; } const QString MachineModel::getProductName() const { QString result = readFileSafely("/sys/class/dmi/id/product_name"); QStringList list = result.split("\n"); result = list.at(0); qDebug() << __FILE__ << __LINE__ << "获取产品名为:" << result; return result; } const QString MachineModel::getProductFamily() const { QString result = readFileSafely("/sys/class/dmi/id/product_family"); QStringList list = result.split("\n"); result = list.at(0); qDebug() << __FILE__ << __LINE__ << "获取设备Family为:" << result; return result; } ukui-screensaver/src/dbusifs/accountshelper.cpp0000664000175000017500000001301415172041035020741 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "accountshelper.h" #include "../common/definetypes.h" #include #include #include #include #include "../../src/userinfo.h" AccountsHelper::AccountsHelper(QObject *parent) : QObject(parent) { initData(); initConnections(); } AccountsHelper::~AccountsHelper() {} QString AccountsHelper::getUserLanguageByName(QString strUserName) { QString strLanguage = ""; if (!m_accountsInterface) return strLanguage; QDBusReply userPath = m_accountsInterface->call("FindUserByName", strUserName); if (!userPath.isValid()) qWarning() << "Get UserPath error:" << userPath.error(); else { QDBusInterface userIface( ACCOUNTS_DBUS_SERVICE, userPath.value().path(), FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusReply languageReply = userIface.call("Get", ACCOUNTS_USER_DBUS_INTERFACE, "Language"); if (!languageReply.isValid()) qWarning() << "Get User's language error" << languageReply.error(); else { strLanguage = languageReply.value().variant().toString(); } } return strLanguage; } QString AccountsHelper::getUserBackgroundByName(QString strUserName) { QString strBackground = ""; if (!m_accountsInterface) return strBackground; QDBusReply userPath = m_accountsInterface->call("FindUserByName", strUserName); if (!userPath.isValid()) qWarning() << "Get UserPath error:" << userPath.error(); else { QDBusInterface userIface( ACCOUNTS_DBUS_SERVICE, userPath.value().path(), FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusReply backgroundReply = userIface.call("Get", ACCOUNTS_USER_DBUS_INTERFACE, "BackgroundFile"); if (!backgroundReply.isValid()) qWarning() << "Get User's background error" << backgroundReply.error(); else { strBackground = backgroundReply.value().variant().toString(); } } return strBackground; } QString AccountsHelper::getUserSessionByName(QString strUserName) { QString strSession = ""; if (!m_accountsInterface) return strSession; QDBusReply userPath = m_accountsInterface->call("FindUserByName", strUserName); if (!userPath.isValid()) qWarning() << "Get UserPath error:" << userPath.error(); else { QDBusInterface userIface( ACCOUNTS_DBUS_SERVICE, userPath.value().path(), FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusReply sessionReply = userIface.call("Get", ACCOUNTS_USER_DBUS_INTERFACE, "XSession"); if (!sessionReply.isValid()) qWarning() << "Get User's xsession error" << sessionReply.error(); else { strSession = sessionReply.value().variant().toString(); } } return strSession; } QString AccountsHelper::getAccountBackground(uid_t uid) { QString strBackground = ""; if (!m_accountsInterface) return strBackground; QDBusReply userPath = m_accountsInterface->call("FindUserById", (qint64)uid); if (!userPath.isValid()) qWarning() << "Get UserPath error:" << userPath.error(); else { QDBusInterface userIface( ACCOUNTS_DBUS_SERVICE, userPath.value().path(), FD_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QDBusReply backgroundReply = userIface.call("Get", ACCOUNTS_USER_DBUS_INTERFACE, "BackgroundFile"); if (!backgroundReply.isValid()) qWarning() << "Get User's BackgroundFile error" << backgroundReply.error(); else { strBackground = backgroundReply.value().variant().toString(); } } return strBackground; } void AccountsHelper::initData() { if (!m_accountsInterface) { m_accountsInterface = new QDBusInterface( ACCOUNTS_DBUS_SERVICE, ACCOUNTS_DBUS_PATH, ACCOUNTS_DBUS_INTERFACE, QDBusConnection::systemBus(), this); } } void AccountsHelper::initConnections() { connect(m_accountsInterface, SIGNAL(UserAdded(QDBusObjectPath)), this, SIGNAL(UserAdded(QDBusObjectPath))); connect(m_accountsInterface, SIGNAL(UserDeleted(QDBusObjectPath)), this, SIGNAL(UserRemoved(QDBusObjectPath))); } QStringList AccountsHelper::getUserList() { QStringList listUsers; if (!m_accountsInterface) return listUsers; QDBusMessage ret = m_accountsInterface->call("ListCachedUsers"); QList outArgs = ret.arguments(); QVariant first = outArgs.at(0); const QDBusArgument &dbusArgs = first.value(); QDBusObjectPath path; dbusArgs.beginArray(); while (!dbusArgs.atEnd()) { dbusArgs >> path; listUsers << path.path(); } dbusArgs.endArray(); return listUsers; } ukui-screensaver/src/dbusifs/upowerlinepowerdbus.cpp0000664000175000017500000000702415172041106022051 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #include "upowerlinepowerdbus.h" #include #include UpowerLinePowerDBus::UpowerLinePowerDBus(int linePowerId, const QString &linePowerDBusPath, QObject *parent) : QObject(parent), m_linePowerId(linePowerId) { qDebug() << "linePowerDBusPath :" << linePowerDBusPath; m_DBusInterface = new QDBusInterface("org.freedesktop.UPower", linePowerDBusPath, "org.freedesktop.DBus.Properties", QDBusConnection::systemBus(), this); QDBusConnection::systemBus().connect("org.freedesktop.UPower", linePowerDBusPath, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(dealLinePowerPropertiesMessage(QDBusMessage))); QDBusReply reply = m_DBusInterface->call("Get", "org.freedesktop.UPower.Device", "Online"); if (reply.isValid()) { m_acOnlineState = reply.value().toBool(); } else { m_acOnlineState = false; } QDBusReply pathReply = m_DBusInterface->call("Get", "org.freedesktop.UPower.Device", "NativePath"); if (pathReply.isValid()) { m_nativePath = pathReply.value().toString(); qDebug() << "NativePath = " << m_nativePath; } else { qWarning() << "get native path error!"; } } UpowerLinePowerDBus::~UpowerLinePowerDBus() {} void UpowerLinePowerDBus::dealLinePowerPropertiesMessage(QDBusMessage msg) { const QDBusArgument &dbusArgs = msg.arguments().at(1).value(); QMap map; dbusArgs >> map; qDebug() << "line power id:" << m_linePowerId; if (map.contains("Online")) { bool acOnlineState = map.value(QString("Online")).toBool(); if (m_acOnlineState != acOnlineState) { m_acOnlineState = acOnlineState; Q_EMIT acOnlineStateChanged(m_linePowerId, m_acOnlineState); } } } int UpowerLinePowerDBus::getAcOnlineState() { return m_acOnlineState; } bool UpowerLinePowerDBus::getOnLineStateFromSysFile() { QFile file("/sys/class/power_supply/" + m_nativePath + "/online"); // 尝试以只读和文本模式打开文件 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "无法打开文件:" << file.errorString(); return false; } QTextStream in(&file); QString content = in.readAll().trimmed(); // 读取全部内容并去除首尾空白字符 file.close(); qDebug() << QString ("/sys/class/power_supply/" + m_nativePath + "/online is ") << content; if (content == "1") { return true; } return false; } ukui-screensaver/src/dbusifs/freedesktophelper.cpp0000664000175000017500000000672015172041106021442 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "freedesktophelper.h" #include std::unique_ptr FreedesktopHelper::m_instanceSystem; std::unique_ptr FreedesktopHelper::m_instanceSession; std::once_flag FreedesktopHelper::m_initFlagSystem; std::once_flag FreedesktopHelper::m_initFlagSession; FreedesktopHelper::FreedesktopHelper(bool bSession, QObject *parent) : QDBusAbstractInterface( FD_DBUS_SERVICE, FD_DBUS_PATH, FD_DBUS_INTERFACE, (bSession ? QDBusConnection::sessionBus() : QDBusConnection::systemBus()), parent) { connect( this, SIGNAL(NameOwnerChanged(QString, QString, QString)), this, SLOT(onDBusNameOwnerChanged(QString, QString, QString))); } FreedesktopHelper* FreedesktopHelper::getInstance(bool bSession, QObject *parent) { if (bSession) { std::call_once(m_initFlagSession, [&,bSession, parent]() { m_instanceSession.reset(new FreedesktopHelper(bSession, parent)); }); return m_instanceSession.get(); } else { std::call_once(m_initFlagSystem, [&,bSession, parent]() { m_instanceSystem.reset(new FreedesktopHelper(bSession, parent)); }); return m_instanceSystem.get(); } } void FreedesktopHelper::asyncStartService(const QString &serviceName) { QDBusPendingCall pendingCall = asyncCall(QStringLiteral("StartServiceByName"), serviceName, QVariant::fromValue(0)); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); connect(watcher, &QDBusPendingCallWatcher::finished, [](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (reply.isError()) { qWarning() << "Failed to start service:" << reply.error().message(); } else { qDebug() << "Service started, result code:" << reply.value(); } watcher->deleteLater(); }); } bool FreedesktopHelper::NameHasOwner(const QString &strService) { QDBusReply ret = call("NameHasOwner", strService); if (ret.isValid() && ret.value()) { return true; } return false; } bool FreedesktopHelper::isServiceActivable(const QString &strService) { bool isActivable = false; QDBusReply result = call(QStringLiteral("ListActivatableNames")); if (!result.isValid()) { qWarning() << "ListActivatableNames error:" << result.error().message(); } else { QStringList listNames = result.value(); if (listNames.contains(strService)) { isActivable = true; } } return isActivable; } void FreedesktopHelper::onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner); Q_EMIT serviceStatusChanged(name, !newOwner.isEmpty()); } ukui-screensaver/src/dbusifs/usdhelper.cpp0000664000175000017500000000332715172041035017723 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "usdhelper.h" #include #include #include #include #include const static QString usdService = QStringLiteral("org.ukui.SettingsDaemon"); const static QString usdMediaKeysPath = QStringLiteral("/org/ukui/SettingsDaemon/MediaKeys"); const static QString usdMediaKeysInterface = QStringLiteral("org.ukui.SettingsDaemon.MediaKeys"); UsdHelper::UsdHelper(QObject *parent) : QObject(parent) {} UsdHelper::~UsdHelper() {} void UsdHelper::init() { if (!usdInterface) { usdInterface = new QDBusInterface( usdService, usdMediaKeysPath, usdMediaKeysInterface, QDBusConnection::sessionBus(), this); } } bool UsdHelper::usdExternalDoAction(int actionType) { init(); QDBusPendingCall result = usdInterface->asyncCall("externalDoAction", actionType, "screensaver"); if (result.reply().type() == QDBusMessage::ErrorMessage) { qWarning() << "usdExternalDoAction error!!!!!!!!!!!!!!"; return false; } return true; } ukui-screensaver/src/dbusifs/giodbus.cpp0000664000175000017500000000412715172041035017363 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "giodbus.h" #include #include #include int get_server_gvariant_stdout(int drvid) { GDBusMessage *method_call_message; GDBusMessage *method_reply_message; GUnixFDList *fd_list; GError **error = NULL; gint fd, dup_fd; const gchar *response; fd = -1; dup_fd = -1; method_call_message = NULL; method_reply_message = NULL; GDBusConnection *con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); method_call_message = g_dbus_message_new_method_call( "org.ukui.Biometric", "/org/ukui/Biometric", "org.ukui.Biometric", "GetFrameFd"); g_dbus_message_set_body(method_call_message, g_variant_new("(i)", drvid)); method_reply_message = g_dbus_connection_send_message_with_reply_sync( con, method_call_message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, /* out_serial */ NULL, /* cancellable */ error); if (method_reply_message == NULL) goto out; if (g_dbus_message_get_message_type(method_reply_message) == G_DBUS_MESSAGE_TYPE_ERROR) { g_dbus_message_to_gerror(method_reply_message, error); goto out; } fd_list = g_dbus_message_get_unix_fd_list(method_reply_message); fd = g_unix_fd_list_get(fd_list, 0, error); dup_fd = dup(fd); out: g_object_unref(method_call_message); g_object_unref(method_reply_message); return dup_fd; } ukui-screensaver/src/dbusifs/filemanagerhelper.h0000664000175000017500000000110515172041106021036 0ustar fengfeng#ifndef FILEMANAGERHELPER_H #define FILEMANAGERHELPER_H #include #include #include class FileManagerHelper : public QObject { Q_OBJECT public: /** * @brief 构造 * * @param parent 父指针 */ explicit FileManagerHelper(QObject *parent = nullptr); /** * @brief 析构 * */ virtual ~FileManagerHelper(); bool showFolders(QStringList path); private: void init(); private: QDBusInterface *m_fileManagerInterface = nullptr; }; #endif // FILEMANAGERHELPER_H ukui-screensaver/src/dbusifs/upowerdbus.h0000664000175000017500000000411215172041106017564 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #ifndef UPOWERDBUS_H #define UPOWERDBUS_H #include #include #include enum up_device_type { up_device_type_unknown = 0, up_device_type_line_power, up_device_type_battery, up_device_type_ups, up_device_type_monitor, up_device_type_mouse, up_device_type_keyboard, up_device_type_pda, up_device_type_phone, up_device_type_media_player, up_device_type_tablet, up_device_type_computer, up_device_type_max }; class UPowerDBus : public QObject { Q_OBJECT public: explicit UPowerDBus(QObject *parent = nullptr); ~UPowerDBus(); private: void initDeviceInfo(); void dealLidClosedMessage(bool); void dealBatteryOnlineMessage(bool); QDBusInterface *m_DBusInterface; bool m_hasLid; bool m_lidIsClosed; bool m_batteryOnline; QString m_upowerDaemonVersion; QStringList m_batteryPathList; QStringList m_linePowerPathList; private Q_SLOTS: void dealUPowerDBusMessage(QDBusMessage); Q_SIGNALS: void lidStateChanged(bool); void batteryOnlineStateChanged(bool); public: bool hasLid(); bool getLidClosedState(); bool getBatteryOnlineState(); QString getUpowerDaemonVersion(); QStringList getBatteryPathList(); QStringList getLinePowerPathList(); void refreshDevicePathLists(); int getDeviceType(const QString &); }; #endif // UPOWERDBUS_H ukui-screensaver/src/dbusifs/networkmanagerhelper.h0000664000175000017500000000160115172041106021611 0ustar fengfeng#ifndef NETWORKMANAGERHELPER_H #define NETWORKMANAGERHELPER_H #include #include #include #include class NetWorkManagerHelper : public QObject { Q_OBJECT public: explicit NetWorkManagerHelper(QObject *parent = nullptr); public Q_SLOTS: /** * @brief 会话属性改变 * * @param QString 接口名 * @param QVariantMap 变化的属性表 * @param QStringList 无效的属性 */ void onNetworkPropChanged(QString, QVariantMap, QStringList); int getNetWorkConnectStatus(); Q_SIGNALS: /** * @brief 网络状态改变状态改变 * * @param 4 网络可用 */ void netWorkConnectStateChanged(int state); private: void init(); private: QDBusInterface *m_networkService = nullptr; int m_iNetWorkConnectStatus = -1; }; #endif // NETWORKMANAGERHELPER_H ukui-screensaver/src/dbusifs/usdhelper.h0000664000175000017500000000226215172041035017365 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef USDHELPER_H #define USDHELPER_H #include #include #include class UsdHelper : public QObject { Q_OBJECT public: /** * @brief 构造 * * @param parent 父指针 */ explicit UsdHelper(QObject *parent = nullptr); /** * @brief 析构 * */ virtual ~UsdHelper(); bool usdExternalDoAction(int actionType); private: void init(); private: QDBusInterface *usdInterface = nullptr; }; #endif // USDHELPER_H ukui-screensaver/src/dbusifs/upm_upowerdbus.h0000664000175000017500000000446115172041106020454 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #ifndef UPM_UPOWERDBUS_H #define UPM_UPOWERDBUS_H #include #include "upowerdbus.h" #include "upowerbatterydbus.h" #include "upowerlinepowerdbus.h" class UpmUpowerDBus : public QObject { Q_OBJECT public: explicit UpmUpowerDBus(QObject *parent = nullptr); ~UpmUpowerDBus(); static UpmUpowerDBus* self(); void refreshBatteryInfo(); bool getAcOnlineStateFromSysfile(); private: UPowerDBus *m_upowerDBus; QVector m_batteryDBusVector; QVector m_linePowerDBusVector; QString m_batteryIconName; void initializeBatteryObjects(); void clearBatteryObjects(); void rebuildBatteryObjects(); Q_SIGNALS: void lidStateChanged(bool); void batteryOnlineStateChanged(bool); void batteryInfoChanged(int, QStringList); void acOnlineStateChanged(int, bool); void deviceChanged(); private: void dealLidStateChanged(bool); void dealBatteryOnlineStateChanged(bool); void dealBatteryInfoChanged(int, QStringList); void dealAcOnlineStateChanged(int, bool); public Q_SLOTS: bool getLidClosedState(); bool getBatteryOnlineState(); bool hasBattery(); bool batteryIsPresent(int index = 0); int getBatteryState(int index = 0); int getBatteryNum(); int getLinePowerNum(); double getBatteryPercentage(); qlonglong getTimeToFull(int index = 0); qlonglong getTimeToEmpty(int index = 0); QString getBatteryIconName(int index = 0); bool getAcOnlineState(); void onDeviceAdded(QDBusObjectPath); void onDeviceRemoved(QDBusObjectPath); }; #endif // UPM_UPOWERDBUS_H ukui-screensaver/src/dbusifs/upowerdbus.cpp0000664000175000017500000001270715172041106020130 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #include "upowerdbus.h" UPowerDBus::UPowerDBus(QObject *parent) : QObject(parent) { m_DBusInterface = new QDBusInterface("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); QDBusConnection::systemBus().connect("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(dealUPowerDBusMessage(QDBusMessage))); refreshDevicePathLists(); initDeviceInfo(); } UPowerDBus::~UPowerDBus() { delete m_DBusInterface; } void UPowerDBus::refreshDevicePathLists() { m_batteryPathList.clear(); QDBusInterface dBusInterface("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", QDBusConnection::systemBus()); QDBusReply> reply = dBusInterface.call("EnumerateDevices"); if (dBusInterface.isValid()) { for (QDBusObjectPath dBusObjectPath : reply.value()) { int upDeviceType = getDeviceType(dBusObjectPath.path()); qDebug() << "device type:" << upDeviceType; if (up_device_type_line_power == upDeviceType) { m_linePowerPathList << dBusObjectPath.path(); qDebug() << "line_power dBusObjectPath:" << dBusObjectPath.path(); } else if (up_device_type_battery == upDeviceType) { m_batteryPathList << dBusObjectPath.path(); qDebug() << "battery dBusObjectPath:" << dBusObjectPath.path(); } } } } int UPowerDBus::getDeviceType(const QString &dBusPath) { qDebug() << "device dBus object path:" << dBusPath; // upower dbus device path /org/freedesktop/UPower/devices/keyboard_hidpp_battery_0 QString deviceName = dBusPath.split('/').last(); QString deviceType = deviceName.split('_').first(); qInfo() << "device name:" << deviceName << "device type:" << deviceType; if ("keyboard" == deviceType) { return up_device_type_keyboard; } if ("mouse" == deviceType) { return up_device_type_mouse; } QDBusInterface dBusInterface("org.freedesktop.UPower", dBusPath, "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); QDBusReply reply = dBusInterface.call("Get", "org.freedesktop.UPower.Device", "Type"); if (reply.isValid()) { return reply.value().toInt(); } else { qDebug() << "Get device type failed"; return up_device_type_unknown; } } void UPowerDBus::initDeviceInfo() { QDBusMessage msg = m_DBusInterface->call("GetAll", "org.freedesktop.UPower"); if (msg.type() == QDBusMessage::ReplyMessage) { const QDBusArgument &dbusArgs = msg.arguments().at(0).value(); QMap map; dbusArgs >> map; m_hasLid = map.value(QString("LidIsPresent")).toBool(); m_lidIsClosed = map.value(QString("LidIsClosed")).toBool(); m_batteryOnline = map.value(QString("OnBattery")).toBool(); m_upowerDaemonVersion = map.value(QString("DaemonVersion")).toString(); } } void UPowerDBus::dealLidClosedMessage(bool value) { if (m_lidIsClosed != value) { m_lidIsClosed = value; Q_EMIT lidStateChanged(m_lidIsClosed); } } void UPowerDBus::dealBatteryOnlineMessage(bool value) { if (m_batteryOnline != value) { m_batteryOnline = value; Q_EMIT batteryOnlineStateChanged(m_batteryOnline); } } void UPowerDBus::dealUPowerDBusMessage(QDBusMessage msg) { const QDBusArgument &dbusArgs = msg.arguments().at(1).value(); QMap map; dbusArgs >> map; if (map.contains("LidIsClosed")) { dealLidClosedMessage(map.value(QString("LidIsClosed")).toBool()); } if (map.contains("OnBattery")) { dealBatteryOnlineMessage(map.value(QString("OnBattery")).toBool()); } } bool UPowerDBus::hasLid() { return m_hasLid; } bool UPowerDBus::getLidClosedState() { return m_lidIsClosed; } bool UPowerDBus::getBatteryOnlineState() { return m_batteryOnline; } QString UPowerDBus::getUpowerDaemonVersion() { return m_upowerDaemonVersion; } QStringList UPowerDBus::getBatteryPathList() { return m_batteryPathList; } QStringList UPowerDBus::getLinePowerPathList() { return m_linePowerPathList; } ukui-screensaver/src/dbusifs/device.cpp0000664000175000017500000000161015172041035017160 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "device.h" #include DEVICE::DEVICE(QObject *parent) : QObject(parent) {} void DEVICE::handleChanged(QDBusMessage msg) { Q_EMIT device_property_changed(msg, m_dev.path); } DEVICE::~DEVICE() {} ukui-screensaver/src/dbusifs/kglobalaccelhelper.cpp0000664000175000017500000000344615172041035021535 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "kglobalaccelhelper.h" #include #include #include #include #include const static QString kglobalService = QStringLiteral("org.kde.kglobalaccel"); const static QString kglobalPath = QStringLiteral("/kglobalaccel"); const static QString kglobalInterface = QStringLiteral("org.kde.KGlobalAccel"); KglobalAccelHelper::KglobalAccelHelper(QObject *parent) : QObject(parent) { m_kglobalInterface = new QDBusInterface(kglobalService, kglobalPath, kglobalInterface, QDBusConnection::sessionBus(), this); } KglobalAccelHelper::~KglobalAccelHelper() {} bool KglobalAccelHelper::blockShortcut(bool val) { qDebug()<<"m_kglobalInterface isvalid:"<isValid(); if (m_kglobalInterface->isValid()) { QDBusMessage result = m_kglobalInterface->call("blockGlobalShortcuts", val); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "blockShortcut error:" << result.errorMessage(); return false; } return true; } else { return false; } } ukui-screensaver/src/dbusifs/upowerbatterydbus.h0000664000175000017500000000355415172041106021170 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #ifndef UPOWERBATTERYDBUS_H #define UPOWERBATTERYDBUS_H #include #include #include typedef enum { battery_state_unknown = 0, battery_state_charging, battery_state_discharging, battery_state_empty, battery_state_fully, battery_state_pending_charge, battery_state_pending_discharge, battery_state_last } BatteryState; class UpowerBatteryDBus : public QObject { Q_OBJECT public: explicit UpowerBatteryDBus(int batteryId, const QString &batteryDBusPath, QObject *parent = nullptr); ~UpowerBatteryDBus(); private: void initBatteryInfo(); int m_batteryId; QString m_batteryDBusPath; QDBusInterface *m_DBusInterface; bool m_batteryPresent; int m_batteryState; double m_percentage; qlonglong m_timeToFull; qlonglong m_timeToEmpty; Q_SIGNALS: void batteryInfoChanged(int, QStringList); private Q_SLOTS: void dealBatteryPropertiesMessage(QDBusMessage); public: bool batteryIsPresent(); int getBatteryState(); double getBatteryPercentage(); qlonglong getTimeToFull(); qlonglong getTimeToEmpty(); void refreshBatteryInfo(); }; #endif // UPOWERBATTERYDBUS_H ukui-screensaver/src/dbusifs/systemupgradehelper.h0000664000175000017500000000260415172041106021465 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SYSTEMUPGRADEHELPER_H #define SYSTEMUPGRADEHELPER_H #include #include #include #include class SystemUpgradeHelper : public QObject { Q_OBJECT public: /** * @brief 构造 * * @param parent 父指针 */ explicit SystemUpgradeHelper(QObject *parent = nullptr); /** * @brief 析构 * */ virtual ~SystemUpgradeHelper(); bool checkSystemUpgrade(); bool doUpgradeThenRboot(); bool doUpgradeThenShutdown(); private: void init(); private: QDBusInterface *m_sysUpgradeInterface = nullptr; bool isDbusActive = false; bool isSystemUpgrade = false; }; #endif // SYSTEMUPGRADEHELPER_H ukui-screensaver/src/dbusifs/enginedevice.h0000664000175000017500000000551115172041035020017 0ustar fengfeng/* * Copyright 2021 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #ifndef ENGINEDEVICE_H #define ENGINEDEVICE_H #include #include "device.h" #include #include #include #include #define DBUS_SERVICE "org.freedesktop.UPower" #define DBUS_OBJECT "/org/freedesktop/UPower" #define DBUS_INTERFACE "org.freedesktop.UPower" #define DBUS_INTERFACE_PRO "org.freedesktop.DBus.Properties" #define DBUS_INTERFACE_DEV "org.freedesktop.UPower.Device" class EngineDevice : public QObject { Q_OBJECT private: static EngineDevice *instance; explicit EngineDevice(QObject *parent = nullptr); class Deconstructor { public: ~Deconstructor() { if (instance) { delete instance; instance = nullptr; } } }; static Deconstructor deconstructor; public: static EngineDevice *getInstance() { if (instance == nullptr) { instance = new EngineDevice; } return instance; } Q_SIGNALS: void engine_signal_discharge(DEV dv); void engine_signal_charge(DEV dv); void engine_signal_fullycharge(DEV dv); void engine_signal_charge_low(DEV dv); void engine_signal_charge_critical(DEV dv); void engine_signal_charge_action(DEV dv); void engine_signal_summary_change(QString summary); void engine_signal_Battery_State(QStringList Battery_State); public Q_SLOTS: void power_device_change_callback(QDBusMessage msg, QString path); public: QGSettings *settings; QList devices; QString previous_icon; QString previous_summary; DEVICE *composite_device; void power_device_recalculate_state(); bool engine_recalculate_summary(); void getProperty(QString path, DEV &dev); QString engine_get_summary(); QStringList engine_get_state(); QString engine_kind_to_localised_text(UpDeviceKind kind, uint number); void power_device_get_devices(); QStringList engine_get_Battery_State(DEVICE *dv); QString boolToString(bool ret); QString engine_get_device_summary(DEVICE *dv); void putAttributes(QMap &map, DEV &btrDetailData); }; #endif // ENGINEDEVICE_H ukui-screensaver/src/dbusifs/machinemodel.h0000664000175000017500000000262315172041035020020 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef MACHINEMODEL_H #define MACHINEMODEL_H #include #include #include class MachineModel : public QObject { Q_OBJECT public: QString getTheMachineType(); static std::shared_ptr getMachineModelInstance(); private: MachineModel(QObject *parent = nullptr); MachineModel(const MachineModel &); const MachineModel &operator=(const MachineModel &) = delete; private: void initMachineType(); const QString getSysVendor() const; const QString getProductName() const; const QString getProductFamily() const; QString m_machineType; static std::shared_ptr m_machineModel; static std::mutex m_mutex; signals: }; #endif // MACHINEMODEL_H ukui-screensaver/src/dbusifs/libinputswitchevent.cpp0000664000175000017500000001020615172041106022033 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifdef SUPPORT_TABLET_MODE #include "libinputswitchevent.h" #include typedef std::function sendEvent; LibinputSwitchEvent::LibinputSwitchEvent(QObject *parent) : QObject(parent), m_machineModel(MachineModel::getMachineModelInstance()) { m_machineType = m_machineModel->getTheMachineType(); m_gatherClientThread.setGatherClientWorker(&m_gatherClientWorker); connect( &m_gatherClientWorker, &GatherClientWorker::tabletModeStatusChanged, this, &LibinputSwitchEvent::tabletModeStatusChanged); m_gatherClientThread.start(); } LibinputSwitchEvent::~LibinputSwitchEvent() { m_gatherClientThread.stop(); m_gatherClientThread.quit(); m_gatherClientThread.wait(); } bool LibinputSwitchEvent::geInitDevicesStatus() { // 其他处理 if (m_machineType == QStringLiteral("SLATE")) { return true; } else if (m_machineType == "LAPTOP") { // 永久附加键盘笔记本 // return true; return false; } else if (m_machineType == "ALLINONE") { // 台式 return false; } else { // 0 非平板 // 1 平板 //-1 不支持 // int status = m_inputGatherClient->libinputTabletSwitchState(); // qInfo() << __FILE__ << __LINE__<< "当前设备的状态:" << status; // if(status == 1) // return true; // else return false; } } GatherClientWorker::GatherClientWorker(QObject *parent) : QObject(parent) {} void GatherClientWorker::start() { sendEvent se = std::bind(&GatherClientWorker::dealEvent, this, std::placeholders::_1); m_inputGatherClient = new UKUIInputGatherClient(); m_inputGatherClient->setEventCallBack(se); m_inputGatherClient->startToReceiveEvent(); } void GatherClientWorker::stop() { Q_EMIT finished(); } void GatherClientWorker::dealEvent(Event *e) { switch (e->type) { case LIBINPUT_EVENT_SWITCH_TOGGLE: qInfo() << "=LIBINPUT_EVENT_SWITCH_TOGGLE="; if (e->event.switchEventDate.switchType == LIBINPUT_SWITCH_TABLET_MODE) { qInfo() << "switch type" << e->event.switchEventDate.switchType; qInfo() << "switch status" << e->event.switchEventDate.switchState; if (e->event.switchEventDate.switchState == 1) { Q_EMIT tabletModeStatusChanged(1); } else { Q_EMIT tabletModeStatusChanged(0); } } break; default: break; } } GatherClientThread::GatherClientThread(QObject *parent) : QThread(parent) {} /** * @brief run 线程处理过程 */ void GatherClientThread::run() { QEventLoop eventLoop; // 线程事件循环 // 连接worker完成信号,退出事件循环 if (m_gatherClientWorker) { connect(m_gatherClientWorker, &GatherClientWorker::finished, &eventLoop, &QEventLoop::quit); m_gatherClientWorker->start(); } // 启动事件循环,直到收到quit eventLoop.exec(); } /** * @brief setGatherClientWorker 设置工作者 * @param worker 工作者实例 */ void GatherClientThread::setGatherClientWorker(GatherClientWorker *worker) { m_gatherClientWorker = worker; m_gatherClientWorker->moveToThread(this); } /** * @brief stop 通知工作者停止工作 */ void GatherClientThread::stop() { if (m_gatherClientWorker) { m_gatherClientWorker->stop(); } } #endif // SUPPORT_TABLET_MODE ukui-screensaver/src/dbusifs/kglobalaccelhelper.h0000664000175000017500000000212115172041035021167 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef KGLOBALACCELHELPER_H #define KGLOBALACCELHELPER_H #include #include #include class KglobalAccelHelper : public QObject { Q_OBJECT public: explicit KglobalAccelHelper(QObject *parent = nullptr); virtual ~KglobalAccelHelper(); bool blockShortcut(bool val); private: QDBusInterface *m_kglobalInterface = nullptr; }; #endif // KGLOBALACCELHELPER_H ukui-screensaver/src/dbusifs/networkmanagerhelper.cpp0000664000175000017500000000373415172041106022155 0ustar fengfeng#include "networkmanagerhelper.h" #include #include #include #include #include const static QString networkService = QStringLiteral("org.freedesktop.NetworkManager"); const static QString networkPath = QStringLiteral("/org/freedesktop/NetworkManager"); const static QString networkInterface = QStringLiteral("org.freedesktop.NetworkManager"); const static QString propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); NetWorkManagerHelper::NetWorkManagerHelper(QObject *parent) : QObject{ parent } {} void NetWorkManagerHelper::init() { if (!m_networkService) { m_networkService = new QDBusInterface(networkService, networkPath, networkInterface, QDBusConnection::systemBus(), this); m_iNetWorkConnectStatus = m_networkService->property("Connectivity").toInt(); qDebug() << " m_iNetWorkConnectStatus = " << m_iNetWorkConnectStatus; // 监听属性变化 QDBusConnection::systemBus().connect( networkService, networkPath, propertiesInterface, "PropertiesChanged", this, SLOT(onNetworkPropChanged(QString, QVariantMap, QStringList))); } } int NetWorkManagerHelper::getNetWorkConnectStatus() { init(); return m_iNetWorkConnectStatus; } void NetWorkManagerHelper::onNetworkPropChanged(QString strInterface, QVariantMap mapVar, QStringList listValue) { qDebug() << "onNetworkPropChanged:" << strInterface << mapVar << listValue; QVariantMap::iterator itVar = mapVar.begin(); for (; itVar != mapVar.end(); itVar++) { qDebug() << " key = " << itVar.key() << "value = " << itVar.value(); if (itVar.key() == "Connectivity") { QVariant varValue = itVar.value(); m_iNetWorkConnectStatus = varValue.toInt(); Q_EMIT netWorkConnectStateChanged(m_iNetWorkConnectStatus); break; } } } ukui-screensaver/src/dbusifs/accountshelper.h0000664000175000017500000000274115172041035020413 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef ACCOUNTSERVICEHELPER_H #define ACCOUNTSERVICEHELPER_H #include #include #include #include class AccountsHelper : public QObject { Q_OBJECT public: explicit AccountsHelper(QObject *parent = nullptr); virtual ~AccountsHelper(); QString getUserLanguageByName(QString strUserName); QString getUserBackgroundByName(QString strUserName); QString getUserSessionByName(QString strUserName); QString getAccountBackground(uid_t uid); QStringList getUserList(); Q_SIGNALS: void UserAdded(const QDBusObjectPath &path); void UserRemoved(const QDBusObjectPath &path); private: void initData(); void initConnections(); private: QDBusInterface *m_accountsInterface = nullptr; }; #endif // ACCOUNTSERVICEHELPER_H ukui-screensaver/src/dbusifs/freedesktophelper.h0000664000175000017500000000342115172041035021103 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef FREEDESKTOPHELPER_H #define FREEDESKTOPHELPER_H #include #include #include #include #include "../common/definetypes.h" class FreedesktopHelper : public QDBusAbstractInterface { Q_OBJECT public: static FreedesktopHelper* getInstance(bool bSession = true, QObject *parent = nullptr); void asyncStartService(const QString &serviceName); public Q_SLOTS: bool NameHasOwner(const QString &strService); bool isServiceActivable(const QString &strService); void onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); Q_SIGNALS: void serviceStatusChanged(QString strName, bool isActive); void NameOwnerChanged(QString, QString, QString); private: explicit FreedesktopHelper(bool bSession = true, QObject *parent = nullptr); static std::unique_ptr m_instanceSystem; static std::unique_ptr m_instanceSession; static std::once_flag m_initFlagSystem; static std::once_flag m_initFlagSession; }; #endif // FREEDESKTOPHELPER_H ukui-screensaver/src/dbusifs/uniauthservice.h0000664000175000017500000001172015172041035020427 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef UNIAUTH_SERVICE_H #define UNIAUTH_SERVICE_H #include #include #include enum authEnableType { ENABLETYPE_BIO, // 全局总使能 ENABLETYPE_SAVER, // 锁屏 ENABLETYPE_GREETER, // 登录 ENABLETYPE_POLKIT, // 授权 ENABLETYPE_SU, // 暂保留 ENABLETYPE_SUDO, // 暂保留 ENABLETYPE_LOGIN, // 暂保留 }; /** * @brief 用户信息 */ struct WillLoginUserInfo { QString strUserName = ""; bool isOneKeyLogin = false; }; QDBusArgument &operator<<(QDBusArgument &arg, const WillLoginUserInfo &willLoginUserInfo); const QDBusArgument &operator>>(const QDBusArgument &arg, WillLoginUserInfo &willLoginUserInfo); QDebug operator<<(QDebug stream, const WillLoginUserInfo &willLoginUserInfo); Q_DECLARE_METATYPE(WillLoginUserInfo) class UniAuthService : public QDBusAbstractInterface { Q_OBJECT private: explicit UniAuthService(QObject *parent = nullptr); public: /** * @brief 单实例 * * @param parent 父指针 * @return UniAuthService 单实例对象指针 */ static UniAuthService *instance(QObject *parent = nullptr); public Q_SLOTS: // 设置默认设备 void setDefaultDevice(int bioDevType, QString deviceName); // 获取默认设备 QString getDefaultDevice(QString userName, int bioDevType); // 获取所有默认设备 QStringList getAllDefaultDevice(QString userName); //生物特征开关接口 bool getBioAuthStatus(QString userName, int bioAuthType); void setBioAuthStatus(int bioAuthType, bool status); // 获取最大失败次数 int getMaxFailedTimes(); // 获取是否使能微信扫码登录 bool getQRCodeEnable(); // 获取是否双认证 bool getDoubleAuth(); // 获取用户绑定 bool getUserBind(); // 获取是否在控制面板显示 bool getIsShownInControlCenter(); // 获取是否使用第一个设备 bool getUseFirstDevice(); // 获取是否隐藏切换按钮 bool getHiddenSwitchButton(); // 获取人脸超时停用次数 int getFTimeoutTimes(); /** * @brief 用户名是否有效 * * @param strUserName 用户名 * @return bool true 有效,否则 无效 */ bool isUserNameValid(QString strUserName); /** * @brief 一键切换并登录用户 * * @param strUserName 用户名 */ void SwitchToUser(QString strUserName); /** * @brief 切换到登录用户 * * @param strUserName 用户名 */ void SwitchToGreeterUser(QString strUserName); /** * @brief 将要登录的用户信息 * * @return WillLoginUserInfo 用户信息 */ WillLoginUserInfo getWillSwitchUser(); /** * @brief SaveLastLoginUser 记录上一次登录成功的用户 * @param strUserName 用户名 * @return 0 成功,其他失败 */ int SaveLastLoginUser(QString strUserName); /** * @brief GetLastLoginUser 获取上一次登录的用户 * @return 用户名 */ QString GetLastLoginUser(); /** * @brief SaveQuickLoginUser 保存下次快速登录的用户 * @param strUserName 用户名 * @return 0 成功,其他失败 */ int SaveQuickLoginUser(QString strUserName); /** * @brief AsyncSaveLastLoginUser 异步记录下次快速登录的用户 * @param strUserName 用户名 * @return 0 成功,其他失败 */ int AsyncQuickLoginUser(QString strUserName); /** * @brief GetQuickLoginUser 获取快速登录的用户名 * @return 用户名 */ QString GetQuickLoginUser(); public: bool isActivatable(); Q_SIGNALS: //默认设备改变 void defaultDeviceChanged(QString userName, int bioDevType, QString deviceName); //开关状态改变 void bioAuthStatusChanged(QString userName, int type, bool status); /** * @brief 将要登录的用户名改变 * * @param strUserName 新用户名(需对空串异常处理) * @param isOneKeyLogin 是否需要一键登录 */ void userChanged(WillLoginUserInfo willLoginUser); private: static std::unique_ptr m_instance; /** 统一认证配置服务单实例指针 */ static std::once_flag m_initFlag; private: bool m_isActivatable; }; #endif // UNIAUTH_SERVICE_H ukui-screensaver/src/dbusifs/biometrichelper.h0000664000175000017500000001121415172041035020544 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BIOMETRICHELPER_H #define BIOMETRICHELPER_H #include #include #include #include "../../src/common/biodefines.h" class BiometricHelper : public QDBusAbstractInterface { Q_OBJECT public: explicit BiometricHelper(QObject *parent = nullptr); public Q_SLOTS: /** * @brief 使用指定id的设备进行用户认证 * @param drvid 驱动(设备)id * @param uid 用户id * @param indexStart 用于认证的特征索引范围 * @param indexEnd * @return 结果: (结果,用户id) */ QDBusPendingCall Identify(int drvid, int uid, int indexStart = 0, int indexEnd = -1); /** * @brief 使用指定id的设备进行用户认证 * @param drvid 驱动(设备)id * @param type ukey的认证类型,2表示pin认证(需通过setExtraInfo设置pin码,3表示指纹认证) * @param uid 用户id * @return 结果: (结果,用户id) */ QDBusPendingCall UkeyIdentify(int drvid, int type, int uid); /** * @brief 设置一些认证时所需的额外的信息 * @param info_type 额外的信息类型,ukey pincode认证时传 "pincode" * @param extra_info 额外的信息内容,ukey pincode认证时传PIN码内容 * @return 结果: (设置额外信息的结果) */ int SetExtraInfo(QString info_type, QString extra_info); /** * @brief 终止设备上正在进行的操作 * @param drvid 设备id * @param waiting 等待时间(秒) * @return */ int StopOps(int drvid, int waiting = 3000); /** * @brief 获取当前用户已连接设备对应特征数目 * @param uid 用户id * @param indexStart 用于认证的特征索引范围 * @param indexEnd * @return */ int GetFeatureCount(int uid, int indexStart = 0, int indexEnd = -1); /** * @brief 获取当前用户已连接设备对应特征数目 * @param uid 用户id * @param indexStart 用于认证的特征索引范围 * @param indexEnd * @return 返回是否存在ukey特征 */ bool GetHasUkeyFeature(int uid, int indexStart = 0, int indexEnd = -1); /** * @brief 获取已连接的设备列表 * @return */ DeviceList GetDevList(); /** * @brief 获取设备数量 * @return */ int GetDevCount(); /** * @brief 获取设备消息 * @param drvid 驱动id * @return */ QString GetDevMesg(int drvid); /** * @brief GetNotifyMesg 获取通知消息 * @param drvid 驱动id * @return */ QString GetNotifyMesg(int drvid); /** * @brief GetOpsMesg 获取操作消息 * @param drvid 驱动id * @return */ QString GetOpsMesg(int drvid); /** * @brief UpdateStatus 获取更新的设备状态 * @param drvid 驱动id * @return 结果: */ StatusReslut UpdateStatus(int drvid); int GetUserDevCount(int uid); int GetUserDevFeatureCount(int uid, int drvid); /** * @brief GetUserFeatures 获取用户所有特征 * @param uid 用户id * @return 特征信息列表 */ FeatureMap GetUserFeatures(int uid); Q_SIGNALS: /** * @brief 设备状态发生变化 * @param drvid 设备id * @param status 设备状态 */ void StatusChanged(int drvid, int status); /** * @brief USB设备热插拔 * @param drvid 设备id * @param action 插拔动作(1:插入,-1:拔出) * @param deviceNum 插拔动作后该驱动拥有的设备数量 */ void USBDeviceHotPlug(int drvid, int action, int deviceNum); void FrameWritten(int drvid); }; #endif // BIOMETRICHELPER_H ukui-screensaver/src/dbusifs/dbusservermanager.h0000664000175000017500000000363215172041035021113 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef DBUSSERVERMANAGER_H #define DBUSSERVERMANAGER_H #include #include class DBusServerManager : public QObject { Q_OBJECT private: explicit DBusServerManager(QObject *parent = nullptr); public: /** * @brief 单实例 * * @param parent 父指针 * @return DBusServerManager 单实例对象指针 */ static DBusServerManager *instance(QObject *parent = nullptr); bool serviceExists(const QString &strService); public Q_SLOTS: void onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); Q_SIGNALS: void dbusServerActiveChanged(const QString &strServerName, bool isActive); private: void init(); private: class MemCollector{ public: ~MemCollector(){ if (DBusServerManager::instance()) { delete DBusServerManager::instance(); DBusServerManager::m_instance = nullptr; } } }; static DBusServerManager *m_instance; /** 统一认证配置服务单实例指针 */ static MemCollector m_memCollector; QDBusInterface *m_dbusService = nullptr; }; #endif // DBUSSERVERMANAGER_H ukui-screensaver/src/dbusifs/giodbus.h0000664000175000017500000000152515172041035017027 0ustar fengfeng/* giodbus.h * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifndef GIODBUS_H #define GIODBUS_H int get_server_gvariant_stdout(int drvid); #endif ukui-screensaver/src/dbusifs/sessionhelper.h0000664000175000017500000000414215172041106020253 0ustar fengfeng/* * Copyright (C) Copyright 2021 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SESSIONHELPER_H #define SESSIONHELPER_H #include #include #include #include #include "../common/definetypes.h" namespace InhibitInfo { struct InhibitorInfo { QString name; QString icon; }; QDBusArgument &operator<<(QDBusArgument &argument, const InhibitInfo::InhibitorInfo &mystruct); const QDBusArgument &operator>>(const QDBusArgument &argument, InhibitInfo::InhibitorInfo &mystruct); } // namespace InhibitInfo Q_DECLARE_METATYPE(InhibitInfo::InhibitorInfo) class QDBusInterface; class Login1Helper; class SessionHelper : public QObject { Q_OBJECT public: explicit SessionHelper(QSharedPointer login1Helper, QObject *parent = nullptr); virtual ~SessionHelper(); QStringList getLockCheckStatus(QString type); bool canAction(Action action); bool doAction(const QString &powerManagerfunc); Q_SIGNALS: void blockLogoutInhibitedChanged(); private Q_SLOTS: void onSessionDbusActiveChanged(const QString &strServerName, bool isActive); void onLogoutHibitedWatcherMessage(uint flags); private: void init(); void doPowerManager(const QString &powerManagerfunc); bool playShutdownMusic(const QString &powerManagerfunc); private: QDBusInterface *m_sessionInterface = nullptr; QTimer *m_timerPlayMusic = nullptr; QSharedPointer m_login1Helper = nullptr; }; #endif // SESSIONHELPER_H ukui-screensaver/src/dbusifs/systemupgradehelper.cpp0000664000175000017500000000661715172041106022030 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "systemupgradehelper.h" #include #include const static QString sysUpgradeService = QStringLiteral("com.kylin.systemupgrade"); const static QString sysUpgradePath = QStringLiteral("/com/kylin/systemupgrade"); const static QString sysUpgradeInterface = QStringLiteral("com.kylin.systemupgrade.interface"); SystemUpgradeHelper::SystemUpgradeHelper(QObject *parent) : QObject(parent) {} SystemUpgradeHelper::~SystemUpgradeHelper() {} void SystemUpgradeHelper::init() { if (!m_sysUpgradeInterface) { m_sysUpgradeInterface = new QDBusInterface(sysUpgradeService, sysUpgradePath, sysUpgradeInterface, QDBusConnection::systemBus()); if (!m_sysUpgradeInterface->isValid()) { qDebug() << "systemUpgrade interface not valid"; isDbusActive = false; isSystemUpgrade = false; } else { isDbusActive = true; } } } bool SystemUpgradeHelper::checkSystemUpgrade() { init(); if (!isDbusActive) return false; QDBusMessage response = m_sysUpgradeInterface->call("CheckInstallRequired"); if (response.type() == QDBusMessage::ReplyMessage) { int res = response.arguments()[0].toInt(); qDebug() << "CheckInstallRequired return " << res; if (res == 0) isSystemUpgrade = false; else isSystemUpgrade = true; } else { qDebug() << "call CheckInstallRequired failed;"; isSystemUpgrade = false; } return isSystemUpgrade; } bool SystemUpgradeHelper::doUpgradeThenRboot() { init(); if (!isDbusActive) return false; bool mask = false; QDBusMessage response = m_sysUpgradeInterface->call("TriggerInstallOnShutdown", "reboot"); if (response.type() == QDBusMessage::ReplyMessage) { int res = response.arguments()[0].toInt(); qDebug() << "TriggerInstallOnShutdown reboot return " << res; if (res == 0) mask = true; else mask = false; } else { qDebug() << "call TriggerInstallOnShutdown reboot failed;"; mask = false; } return mask; } bool SystemUpgradeHelper::doUpgradeThenShutdown() { init(); if (!isDbusActive) return false; bool mask = false; QDBusMessage response = m_sysUpgradeInterface->call("TriggerInstallOnShutdown", "shutdown"); if (response.type() == QDBusMessage::ReplyMessage) { int res = response.arguments()[0].toInt(); qDebug() << "TriggerInstallOnShutdown shutdown return " << res; if (res == 0) mask = true; else mask = false; } else { qDebug() << "call TriggerInstallOnShutdown shutdown failed;"; mask = false; } return mask; } ukui-screensaver/src/dbusifs/upowerhelper.cpp0000664000175000017500000001170115172041106020443 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "upowerhelper.h" #include "device.h" #include #include #include #include #include #include "freedesktophelper.h" #include "upm_upowerdbus.h" #include "dmi_chassis_type.h" const static QString upowerService = QStringLiteral("org.freedesktop.UPower"); const static QString upowerPath = QStringLiteral("/org/freedesktop/UPower"); const static QString upowerInterface = QStringLiteral("org.freedesktop.UPower"); const static QString propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); const static QString upowerDisplayPath = QStringLiteral("/org/freedesktop/UPower/devices/DisplayDevice"); const static QString upowerDeviceInterface = QStringLiteral("org.freedesktop.UPower.Device"); UpowerHelper::UpowerHelper(QObject *parent) : QObject(parent) { if (!FreedesktopHelper::getInstance(false)->NameHasOwner(upowerService)) { connect( FreedesktopHelper::getInstance(false), &FreedesktopHelper::serviceStatusChanged, this, &UpowerHelper::onServiceStatusChanged); FreedesktopHelper::getInstance(false)->asyncStartService(upowerService); } else { init(); } } UpowerHelper::~UpowerHelper() {} void UpowerHelper::init() { if (m_isInited) { return; } m_isBattery = (UpmUpowerDBus::self()->hasBattery() || UpmUpowerDBus::self()->getAcOnlineState()) && (!isDesktop()); connect(UpmUpowerDBus::self(), &UpmUpowerDBus::acOnlineStateChanged, this, &UpowerHelper::dealAcOnlineStateChanged); connect(UpmUpowerDBus::self(), &UpmUpowerDBus::batteryInfoChanged, this, &UpowerHelper::dealBatteryInfoChanged); connect(UpmUpowerDBus::self(), &UpmUpowerDBus::deviceChanged, this, &UpowerHelper::dealDeviceChanged); connect(UpmUpowerDBus::self(), &UpmUpowerDBus::lidStateChanged, this, &UpowerHelper::onLidStateChanged); m_isInited = true; qDebug() << "upowerServiceReady:" << m_isBattery; Q_EMIT upowerServiceReady(); } void UpowerHelper::onServiceStatusChanged(QString strName, bool isActive) { if (strName != upowerService) { return; } if (isActive) { init(); } } QString UpowerHelper::getBatteryIconName() { if (m_isInited && UpmUpowerDBus::self()) { return UpmUpowerDBus::self()->getBatteryIconName(); } return QString(); } QStringList UpowerHelper::getBatteryArgs() { QStringList batteryArgs; if (!m_isInited) { return batteryArgs; } int batteryState = UpmUpowerDBus::self()->getBatteryState(); double batteryPercentage = UpmUpowerDBus::self()->getBatteryPercentage(); qint64 timeToEmpty = UpmUpowerDBus::self()->getTimeToEmpty(); bool hasBattery = UpmUpowerDBus::self()->hasBattery(); bool acOnlineState = UpmUpowerDBus::self()->getAcOnlineState(); batteryArgs.append(QString("%1").arg(batteryPercentage)); batteryArgs.append(QString("%1").arg(batteryState)); batteryArgs.append(QString("%1").arg(timeToEmpty)); batteryArgs.append(QString("%1").arg(hasBattery)); batteryArgs.append(QString("%1").arg(acOnlineState)); return batteryArgs; } void UpowerHelper::dealAcOnlineStateChanged(int, bool) { onUpdateBatteryInfo(); } void UpowerHelper::dealBatteryInfoChanged(int, QStringList) { onUpdateBatteryInfo(); } void UpowerHelper::dealDeviceChanged() { onUpdateBatteryInfo(); } void UpowerHelper::onUpdateBatteryInfo() { Q_EMIT batteryChanged(getBatteryArgs()); Q_EMIT batteryStatusChanged(getBatteryIconName()); } void UpowerHelper::onLidStateChanged(bool value) { Q_EMIT lidStateChanged(value); qDebug() << "lid state:" << value; } bool UpowerHelper::isDesktop() { QFile file("/sys/class/dmi/id/chassis_type"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "Failed to open chassis_type file"; return false; } QString chassisTypeStr = file.readAll().trimmed(); file.close(); bool ok = false; int chassisTypeInt = chassisTypeStr.toInt(&ok); if (!ok) { qWarning() << "Invalid chassis_type format:" << chassisTypeStr; return false; } if (static_cast(chassisTypeInt) == DmiChassisType::Desktop) { return true; // 台式机 } return false; // 非台式机 } ukui-screensaver/src/dbusifs/biometrichelper.cpp0000664000175000017500000002072715172041035021110 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "biometrichelper.h" #include #include BiometricHelper::BiometricHelper(QObject *parent) : QDBusAbstractInterface( BIOMETRIC_DBUS_SERVICE, BIOMETRIC_DBUS_PATH, BIOMETRIC_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) { setTimeout(2147483647); } QDBusPendingCall BiometricHelper::Identify(int drvid, int uid, int indexStart, int indexEnd) { QList argList; argList << drvid << uid << indexStart << indexEnd; return asyncCallWithArgumentList(QStringLiteral("Identify"), argList); } QDBusPendingCall BiometricHelper::UkeyIdentify(int drvid, int type, int uid) { QList argList; argList << drvid << type << uid; return asyncCallWithArgumentList(QStringLiteral("UkeyIdentify"), argList); } bool BiometricHelper::GetHasUkeyFeature(int uid, int indexStart, int indexEnd) { QList qlist; FeatureInfo *featureInfo; int listsize; QDBusMessage result = call(QStringLiteral("GetAllFeatureList"), uid, indexStart, indexEnd); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevList error:" << result.errorMessage(); return false; } QList variantList = result.arguments(); listsize = variantList[0].value(); variantList[1].value() >> qlist; for (int i = 0; i < listsize; i++) { featureInfo = new FeatureInfo; qlist[i].variant().value() >> *featureInfo; if (featureInfo->biotype == UniT_General_Ukey) { delete featureInfo; return true; } delete featureInfo; } return false; } int BiometricHelper::GetFeatureCount(int uid, int indexStart, int indexEnd) { QDBusMessage result = call(QStringLiteral("GetDevList")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevList error:" << result.errorMessage(); return 0; } auto dbusArg = result.arguments().at(1).value(); QList variantList; dbusArg >> variantList; int res = 0; for (int i = 0; i < variantList.size(); i++) { DeviceInfoPtr pDeviceInfo = std::make_shared(); auto arg = variantList.at(i).value(); arg >> *pDeviceInfo; QDBusMessage FeatureResult = call(QStringLiteral("GetFeatureList"), pDeviceInfo->id, uid, indexStart, indexEnd); if (FeatureResult.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetFeatureList error:" << FeatureResult.errorMessage(); return 0; } res += FeatureResult.arguments().takeFirst().toInt(); } return res; } int BiometricHelper::SetExtraInfo(QString info_type, QString extra_info) { QDBusReply reply = call(QStringLiteral("SetExtraInfo"), info_type, extra_info); if (!reply.isValid()) { qWarning() << "SetExtraInfo error:" << reply.error(); return -1; } return reply.value(); } int BiometricHelper::StopOps(int drvid, int waiting) { QDBusReply reply = call(QStringLiteral("StopOps"), drvid, waiting); if (!reply.isValid()) { qWarning() << "StopOps error:" << reply.error(); return -1; } return reply.value(); } int BiometricHelper::GetUserDevCount(int uid) { QDBusMessage result = call(QStringLiteral("GetDevList")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevList error:" << result.errorMessage(); return 0; } auto dbusArg = result.arguments().at(1).value(); QList variantList; DeviceList deviceList; dbusArg >> variantList; for (int i = 0; i < variantList.size(); i++) { DeviceInfoPtr pDeviceInfo = std::make_shared(); auto arg = variantList.at(i).value(); arg >> *pDeviceInfo; int count = GetUserDevFeatureCount(uid, pDeviceInfo->id); if (count > 0) deviceList.push_back(pDeviceInfo); } return deviceList.count(); } int BiometricHelper::GetUserDevFeatureCount(int uid, int drvid) { StopOps(drvid); QDBusMessage FeatureResult = call(QStringLiteral("GetFeatureList"), drvid, uid, 0, -1); if (FeatureResult.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetFeatureList error:" << FeatureResult.errorMessage(); return 0; } return FeatureResult.arguments().takeFirst().toInt(); } DeviceList BiometricHelper::GetDevList() { QDBusMessage result = call(QStringLiteral("GetDevList")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevList error:" << result.errorMessage(); return DeviceList(); } auto dbusArg = result.arguments().at(1).value(); QList variantList; DeviceList deviceList; dbusArg >> variantList; for (int i = 0; i < variantList.size(); i++) { DeviceInfoPtr pDeviceInfo = std::make_shared(); auto arg = variantList.at(i).value(); arg >> *pDeviceInfo; deviceList.push_back(pDeviceInfo); } return deviceList; } FeatureMap BiometricHelper::GetUserFeatures(int uid) { FeatureMap featureMap; QList qlist; int listsize; QDBusMessage result = call(QStringLiteral("GetAllFeatureList"), uid, 0, -1); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevList error:" << result.errorMessage(); return featureMap; } QList variantList = result.arguments(); listsize = variantList[0].value(); variantList[1].value() >> qlist; for (int i = 0; i < listsize; i++) { FeatureInfoPtr pFeatureInfo = std::make_shared(); qlist[i].variant().value() >> *pFeatureInfo; featureMap[pFeatureInfo->device_shortname].append(pFeatureInfo); } return featureMap; } int BiometricHelper::GetDevCount() { QDBusMessage result = call(QStringLiteral("GetDevList")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevList error:" << result.errorMessage(); return 0; } int count = result.arguments().at(0).value(); return count; } QString BiometricHelper::GetDevMesg(int drvid) { QDBusMessage result = call(QStringLiteral("GetDevMesg"), drvid); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetDevMesg error:" << result.errorMessage(); return ""; } return result.arguments().at(0).toString(); } QString BiometricHelper::GetNotifyMesg(int drvid) { QDBusMessage result = call(QStringLiteral("GetNotifyMesg"), drvid); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetNotifyMesg error:" << result.errorMessage(); return ""; } return result.arguments().at(0).toString(); } QString BiometricHelper::GetOpsMesg(int drvid) { QDBusMessage result = call(QStringLiteral("GetOpsMesg"), drvid); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "GetOpsMesg error:" << result.errorMessage(); return ""; } return result.arguments().at(0).toString(); } StatusReslut BiometricHelper::UpdateStatus(int drvid) { StatusReslut status; QDBusMessage result = call(QStringLiteral("UpdateStatus"), drvid); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "UpdateStatus error:" << result.errorMessage(); status.result = -1; return status; } status.result = result.arguments().at(0).toInt(); status.enable = result.arguments().at(1).toInt(); status.devNum = result.arguments().at(2).toInt(); status.devStatus = result.arguments().at(3).toInt(); status.opsStatus = result.arguments().at(4).toInt(); status.notifyMessageId = result.arguments().at(5).toInt(); return status; } ukui-screensaver/src/dbusifs/dmi_chassis_type.h0000664000175000017500000000245615172041106020725 0ustar fengfeng/* * Copyright 2025 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #pragma once enum class DmiChassisType { Other = 1, Unknown, Desktop, LowProfileDesktop, PizzaBox, MinTower, Tower, Portable, Laptop, Notebook, HandHeld, DockingStation, AllInOne, SubNotebook, SpaceSaving, LunchBox, MainServerChassis, ExpansionChassis, SubChassis, BusExpansionChassis, PeripheralChassis, RAIDChassis, RackMountChassis, SealedCasePC, Multisystem, CompactPCI, AdvancedTCA, Blade, BladeEnclosing, Tablet, Convertible, Detachable, IoTGateway, EmbeddedPC, MiniPC, StickPC };ukui-screensaver/src/dbusifs/dbusservermanager.cpp0000664000175000017500000000434415172041035021447 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "dbusservermanager.h" #include "../common/definetypes.h" #include #include DBusServerManager *DBusServerManager::m_instance = nullptr; DBusServerManager::MemCollector DBusServerManager::m_memCollector; DBusServerManager::DBusServerManager(QObject *parent) : QObject{ parent } { init(); } DBusServerManager *DBusServerManager::instance(QObject *parent) { if (!m_instance) { m_instance = new DBusServerManager(parent); } return m_instance; } void DBusServerManager::init() { if (!m_dbusService) { m_dbusService = new QDBusInterface(FD_DBUS_SERVICE, FD_DBUS_PATH, FD_DBUS_INTERFACE, QDBusConnection::sessionBus()); connect( m_dbusService, SIGNAL(NameOwnerChanged(QString, QString, QString)), this, SLOT(onDBusNameOwnerChanged(QString, QString, QString))); } } void DBusServerManager::onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner); // qDebug() << "service status changed:" << name // << (newOwner.isEmpty() ? "inactivate" : "activate"); Q_EMIT dbusServerActiveChanged(name, !newOwner.isEmpty()); } bool DBusServerManager::serviceExists(const QString &strService) { if (!m_dbusService) { return false; } QDBusReply reply = m_dbusService->call("NameHasOwner", strService); if (!reply.isValid()) { qWarning() << "check service exists error:" << reply.error(); return false; } return reply.value(); } ukui-screensaver/src/dbusifs/enginedevice.cpp0000664000175000017500000002760415172041035020361 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "enginedevice.h" #include "device.h" #include #include #include #include #include EngineDevice *EngineDevice::instance = NULL; void EngineDevice::power_device_get_devices() { QList deviceNames; /* call enum dbus*/ QDBusMessage msg = QDBusMessage::createMethodCall(DBUS_SERVICE, DBUS_OBJECT, DBUS_INTERFACE, "EnumerateDevices"); QDBusMessage res = QDBusConnection::systemBus().call(msg); if (res.type() == QDBusMessage::ReplyMessage) { const QDBusArgument &dbusArg = res.arguments().at(0).value(); dbusArg >> deviceNames; } else { } int len = deviceNames.size(); // qDebug()<m_dev.path = deviceNames.at(i).path(); getProperty(device->m_dev.path, device->m_dev); /* connect the nofity signal to changecallback */ QDBusConnection::systemBus().connect( DBUS_SERVICE, device->m_dev.path, DBUS_INTERFACE_PRO, QString("PropertiesChanged"), device, SLOT(handleChanged(QDBusMessage))); connect( device, SIGNAL(device_property_changed(QDBusMessage, QString)), this, SLOT(power_device_change_callback(QDBusMessage, QString))); /* add to device list*/ devices.append(device); } } EngineDevice::EngineDevice(QObject *parent) : QObject(parent) { settings = new QGSettings(GPM_SETTINGS_SCHEMA); power_device_get_devices(); } void EngineDevice::getProperty(QString path, DEV &dev) { QDBusMessage msg = QDBusMessage::createMethodCall(DBUS_SERVICE, path, DBUS_INTERFACE_PRO, "GetAll"); msg << DBUS_INTERFACE_DEV; QDBusMessage res = QDBusConnection::systemBus().call(msg); if (res.type() == QDBusMessage::ReplyMessage) { const QDBusArgument &dbusArg = res.arguments().at(0).value(); QMap map; dbusArg >> map; dev.kind = (UpDeviceKind)map.value(QString("Type")).toInt(); dev.Type = engine_kind_to_localised_text((UpDeviceKind)map.value(QString("Type")).toInt(), 1); dev.Model = map.value(QString("Model")).toString(); dev.Device = map.value(QString("NativePath")).toString(); dev.IsPresent = (map.value(QString("IsPresent")).toBool()); dev.PowerSupply = boolToString(map.value(QString("PowerSupply")).toBool()); dev.Percentage = map.value(QString("Percentage")).toDouble(); dev.Percentage = ((float)((int)((dev.Percentage + 0.05) * 10))) / 10; dev.Online = boolToString(map.value(QString("Online")).toBool()); dev.State = (UpDeviceState)map.value(QString("State")).toInt(); dev.TimeToEmpty = map.value(QString("TimeToEmpty")).toLongLong(); dev.TimeToFull = map.value(QString("TimeToFull")).toLongLong(); } } QString EngineDevice::boolToString(bool ret) { return ret ? tr("yes") : tr("no"); } void EngineDevice::putAttributes(QMap &map, DEV &btrDetailData) { if (map.contains("TimeToFull")) { btrDetailData.TimeToFull = map.value(QString("TimeToFull")).toLongLong(); } if (map.contains("TimeToEmpty")) { btrDetailData.TimeToEmpty = map.value(QString("TimeToEmpty")).toLongLong(); } if (map.contains("State")) { btrDetailData.State = (UpDeviceState)map.value(QString("State")).toInt(); } if (map.contains("Percentage")) { btrDetailData.Percentage = map.value(QString("Percentage")).toDouble(); btrDetailData.Percentage = ((float)((int)((btrDetailData.Percentage + 0.05) * 10))) / 10; } if (map.contains("PowerSupply")) { btrDetailData.PowerSupply = (map.value(QString("PowerSupply")).toBool()) ? tr("Yes") : tr("No"); } if (map.contains("IsPresent")) { btrDetailData.IsPresent = (map.value(QString("IsPresent")).toBool()); } } void EngineDevice::power_device_change_callback(QDBusMessage msg, QString path) { /* if battery change to display devices */ /* judge state */ DEVICE *item = nullptr; Q_FOREACH (auto item_tmp, devices) { if (item_tmp->m_dev.path == path) { item = item_tmp; break; } } if (item == nullptr) return; const QDBusArgument &arg = msg.arguments().at(1).value(); QMap map; arg >> map; putAttributes(map, item->m_dev); /*recaculate state*/ power_device_recalculate_state(); } void EngineDevice::power_device_recalculate_state() { engine_recalculate_summary(); } /** * engine_recalculate_summary: */ bool EngineDevice::engine_recalculate_summary() { QString summary; QStringList Battery_State; Battery_State = engine_get_state(); summary = engine_get_summary(); if (Battery_State.isEmpty()) { return false; } if (previous_summary.isNull()) { previous_summary = summary; Q_EMIT engine_signal_summary_change(summary); Q_EMIT engine_signal_Battery_State(Battery_State); return true; } if (previous_summary != summary) { previous_summary = summary; Q_EMIT engine_signal_summary_change(summary); Q_EMIT engine_signal_Battery_State(Battery_State); return true; } return false; } QStringList EngineDevice::engine_get_state() { DEVICE *device; UpDeviceState state; QStringList tooltip; QStringList part; bool is_present; UpDeviceKind kind; Q_FOREACH (device, devices) { is_present = device->m_dev.IsPresent; state = device->m_dev.State; kind = device->m_dev.kind; if ((!is_present) || (kind != UP_DEVICE_KIND_BATTERY)) continue; if (state == UP_DEVICE_STATE_EMPTY) continue; part = engine_get_Battery_State(device); if (!part.isEmpty()) tooltip.append(part); } return tooltip; } QStringList EngineDevice::engine_get_Battery_State(DEVICE *dv) { UpDeviceState state; double percentage; QStringList result; state = dv->m_dev.State; int EMPTY = dv->m_dev.TimeToEmpty; percentage = dv->m_dev.Percentage; bool is_present; is_present = dv->m_dev.IsPresent; if (!is_present) return result; result.append(QString("%1").arg(percentage)); result.append(QString("%1").arg(state)); result.append(QString("%1").arg(EMPTY)); return result; } /** * engine_get_summary: * * Returns the complete tooltip ready for display **/ QString EngineDevice::engine_get_summary() { DEVICE *device; UpDeviceState state; QString tooltip; QString part; bool is_present; UpDeviceKind kind; Q_FOREACH (device, devices) { is_present = device->m_dev.IsPresent; state = device->m_dev.State; kind = device->m_dev.kind; if ((!is_present) || (kind != UP_DEVICE_KIND_BATTERY)) continue; if (state == UP_DEVICE_STATE_EMPTY) continue; part = engine_get_device_summary(device); if (!part.isNull()) tooltip = QString("%1").arg(part); } return tooltip; } /** * engine_get_device_summary: **/ QString EngineDevice::engine_get_device_summary(DEVICE *dv) { QString kind_desc; UpDeviceKind kind; UpDeviceState state; double percentage; bool is_present; uint time_to_full; uint time_to_empty; QString result; kind = dv->m_dev.kind; is_present = dv->m_dev.IsPresent; state = dv->m_dev.State; percentage = dv->m_dev.Percentage; time_to_empty = dv->m_dev.TimeToEmpty; time_to_full = dv->m_dev.TimeToFull; if (!is_present) return NULL; kind_desc = engine_kind_to_localised_text(kind, 1); if (state == UP_DEVICE_STATE_FULLY_CHARGED) { result = tr("%1% available, charged").arg(percentage); } else if (state == UP_DEVICE_STATE_DISCHARGING) { int is_show = settings->get(GPM_SETTINGS_DISPLAY_LEFT_TIME).toInt(); if (is_show) { result = tr("Left %1h %2m (%3%)") .arg((time_to_empty) / 3600) .arg(((time_to_empty) % 3600) / 60) .arg(percentage); } else { result = tr("%1% available").arg(percentage); } } else if (state == UP_DEVICE_STATE_CHARGING) { int is_show = settings->get(GPM_SETTINGS_DISPLAY_LEFT_TIME).toInt(); if (is_show) { result = tr("Left %1h %2m to full").arg((time_to_full) / 3600).arg(((time_to_full) % 3600) / 60); } else { result = tr("charging (%1%)").arg(percentage); } } else if (state == UP_DEVICE_STATE_PENDING_DISCHARGE) { /* TRANSLATORS: this is only shown for laptops with multiple batteries */ result = tr("%1 waiting to discharge (%2%)").arg(kind_desc).arg(percentage); } else if (state == UP_DEVICE_STATE_PENDING_CHARGE) { /* TRANSLATORS: this is only shown for laptops with multiple batteries */ result = tr("%1 waiting to charge (%2%)").arg(kind_desc).arg(percentage); } else { printf("in an undefined state we are not charging or " "discharging and the batteries are also not charged"); result = QString("%1 (%2%)").arg(kind_desc).arg(percentage); } return result; } /** * engine_kind_to_localised_text: **/ QString EngineDevice::engine_kind_to_localised_text(UpDeviceKind kind, uint number) { Q_UNUSED(number); QString text; switch (kind) { case UP_DEVICE_KIND_LINE_POWER: text = tr("AC adapter"); break; case UP_DEVICE_KIND_BATTERY: /* TRANSLATORS: laptop primary battery */ text = tr("Laptop battery"); break; case UP_DEVICE_KIND_UPS: /* TRANSLATORS: battery-backed AC power source */ text = tr("UPS"); break; case UP_DEVICE_KIND_MONITOR: /* TRANSLATORS: a monitor is a device to measure voltage and current */ text = tr("Monitor"); break; case UP_DEVICE_KIND_MOUSE: /* TRANSLATORS: wireless mice with internal batteries */ text = tr("Mouse"); break; case UP_DEVICE_KIND_KEYBOARD: /* TRANSLATORS: wireless keyboard with internal battery */ text = tr("Keyboard"); break; case UP_DEVICE_KIND_PDA: /* TRANSLATORS: portable device */ text = tr("PDA"); break; case UP_DEVICE_KIND_PHONE: /* TRANSLATORS: cell phone (mobile...) */ text = tr("Cell phone"); break; case UP_DEVICE_KIND_MEDIA_PLAYER: /* TRANSLATORS: media player, mp3 etc */ text = tr("Media player"); break; case UP_DEVICE_KIND_TABLET: /* TRANSLATORS: tablet device */ text = tr("Tablet"); break; case UP_DEVICE_KIND_COMPUTER: /* TRANSLATORS: tablet device */ text = tr("Computer"); break; default: printf("enum unrecognised: %i", kind); text = tr("unrecognised"); } return text; } ukui-screensaver/src/dbusifs/uniauthservice.cpp0000664000175000017500000002602215172041035020763 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "uniauthservice.h" #include #include #include "../common/definetypes.h" #include "freedesktophelper.h" /* For the type WillLoginUserInfo */ QDBusArgument &operator<<(QDBusArgument &argument, const WillLoginUserInfo &willLoginUserInfo) { argument.beginStructure(); argument << willLoginUserInfo.strUserName << willLoginUserInfo.isOneKeyLogin; argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, WillLoginUserInfo &willLoginUserInfo) { argument.beginStructure(); argument >> willLoginUserInfo.strUserName >> willLoginUserInfo.isOneKeyLogin; argument.endStructure(); return argument; } QDebug operator<<(QDebug stream, const WillLoginUserInfo &willLoginUserInfo) { stream << "WillLogUser [" << willLoginUserInfo.strUserName << willLoginUserInfo.isOneKeyLogin << "]"; return stream; } std::unique_ptr UniAuthService::m_instance; std::once_flag UniAuthService::m_initFlag; UniAuthService::UniAuthService(QObject *parent) : QDBusAbstractInterface( UNIAUTH_DBUS_SERVICE, UNIAUTH_DBUS_PATH, UNIAUTH_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) , m_isActivatable(false) { qRegisterMetaType("WillLoginUserInfo"); qDBusRegisterMetaType(); setTimeout(2147483647); if (FreedesktopHelper::getInstance(false)->isServiceActivable(UNIAUTH_DBUS_INTERFACE)) { m_isActivatable = true; } } UniAuthService *UniAuthService::instance(QObject *parent) { std::call_once(m_initFlag, [&, parent]() { m_instance.reset(new UniAuthService(parent)); }); return m_instance.get(); } // 设置默认设备 void UniAuthService::setDefaultDevice(int bioDevType, QString deviceName) { qDebug() << " bioType : " << bioDevType << "deviceName : " << deviceName; QDBusMessage result = call(QStringLiteral("setDefaultDevice"), bioDevType, deviceName); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setDefaultDevice error:" << result.errorMessage(); return; } return; } // 获取默认设备 QString UniAuthService::getDefaultDevice(QString userName, int bioDevType) { QDBusMessage result = call(QStringLiteral("getDefaultDevice"), userName, bioDevType); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getDefaultDevice error:" << result.errorMessage(); return ""; } QList varResult = result.arguments(); if (varResult.size() > 0) { QString strDefDevice = varResult.takeFirst().toString(); return strDefDevice; } else { return ""; } } // 获取所有默认设备 QStringList UniAuthService::getAllDefaultDevice(QString userName) { QStringList listDefDevices; QDBusReply result = call(QStringLiteral("getAllDefaultDevice"), userName); if (!result.isValid()) { qWarning() << "getAllDefaultDevice error:" << result.error().message(); } else { listDefDevices = result.value(); } return listDefDevices; } // 生物特征开关接口 bool UniAuthService::getBioAuthStatus(QString userName, int bioAuthType) { QDBusMessage bioResult = call(QStringLiteral("getBioAuthStatus"), userName, bioAuthType); if (bioResult.type() == QDBusMessage::ErrorMessage) { qWarning() << "getBioStatus error:" << bioResult.errorMessage(); return false; } QList varResult = bioResult.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } void UniAuthService::setBioAuthStatus(int bioAuthType, bool status) { qDebug() << "setBioAuthStatus bioAuthType : " << bioAuthType << "status : " << status; QDBusMessage result = call(QStringLiteral("setBioAuthStatus"), bioAuthType, status); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "setBioAuthStatus error:" << result.errorMessage(); return; } return; } // 获取最大失败次数 int UniAuthService::getMaxFailedTimes() { QDBusMessage result = call(QStringLiteral("getMaxFailedTimes")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getMaxFailedTimes error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toInt(); } else { return 3; } } // 获取是否使能微信扫码登录 bool UniAuthService::getQRCodeEnable() { QDBusMessage result = call(QStringLiteral("getQRCodeEnable")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getQRCodeEnable error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 获取是否双认证 bool UniAuthService::getDoubleAuth() { QDBusMessage result = call(QStringLiteral("getDoubleAuth")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getDoubleAuth error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 获取用户绑定 bool UniAuthService::getUserBind() { QDBusMessage result = call(QStringLiteral("getUserBind")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getUserBind error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 获取是否在控制面板显示 bool UniAuthService::getIsShownInControlCenter() { QDBusMessage result = call(QStringLiteral("getIsShownInControlCenter")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getIsShownInControlCenter error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 获取是否使用第一个设备 bool UniAuthService::getUseFirstDevice() { QDBusMessage result = call(QStringLiteral("getUseFirstDevice")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getUseFirstDevice error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } // 获取是否隐藏切换按钮 bool UniAuthService::getHiddenSwitchButton() { QDBusMessage result = call(QStringLiteral("getHiddenSwitchButton")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getHiddenSwitchButton error:" << result.errorMessage(); return false; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toBool(); } else { return false; } } bool UniAuthService::isActivatable() { return m_isActivatable; } int UniAuthService::getFTimeoutTimes() { QDBusMessage result = call(QStringLiteral("getFTimeoutTimes")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getFTimeoutTimes error:" << result.errorMessage(); return 1; } QList varResult = result.arguments(); if (varResult.size() > 0) { return varResult.takeFirst().toInt(); } else { return 1; } } void UniAuthService::SwitchToUser(QString strUserName) { QDBusReply reply = call(QStringLiteral("SwitchToUser"), strUserName); if (!reply.isValid()) { qWarning() << "SwitchToUser error:" << reply.error(); return; } return; } void UniAuthService::SwitchToGreeterUser(QString strUserName) { QDBusReply reply = call(QStringLiteral("SwitchToGreeterUser"), strUserName); if (!reply.isValid()) { qWarning() << "SwitchToGreeterUser error:" << reply.error(); return; } return; } WillLoginUserInfo UniAuthService::getWillSwitchUser() { WillLoginUserInfo willLoginUserInfo; QDBusMessage result = call(QStringLiteral("getWillSwitchUser")); if (result.type() == QDBusMessage::ErrorMessage) { qWarning() << "getWillSwitchUser error:" << result.errorMessage(); return willLoginUserInfo; } if (result.arguments().size() > 0) { QDBusArgument argInfo = result.arguments().at(0).value(); argInfo >> willLoginUserInfo; } qDebug() << "getUserInfo:" << willLoginUserInfo; return willLoginUserInfo; } bool UniAuthService::isUserNameValid(QString strUserName) { struct passwd *userInfo = nullptr; userInfo = getpwnam(strUserName.toLatin1().data()); if (userInfo) { return true; } return false; } int UniAuthService::SaveLastLoginUser(QString strUserName) { QDBusReply reply = call(QStringLiteral("SaveLastLoginUser"), strUserName); if (!reply.isValid()) { qWarning() << "SaveLastLoginUser error:" << reply.error(); return -1; } return reply.value(); } QString UniAuthService::GetLastLoginUser() { QDBusReply reply = call(QStringLiteral("GetLastLoginUser")); if (!reply.isValid()) { qWarning() << "GetLastLoginUser error:" << reply.error(); return QString(""); } return reply.value(); } int UniAuthService::SaveQuickLoginUser(QString strUserName) { QDBusReply reply = call(QStringLiteral("SaveQuickLoginUser"), strUserName); if (!reply.isValid()) { qWarning() << "SaveQuickLoginUser error:" << reply.error(); return -1; } return reply.value(); } int UniAuthService::AsyncQuickLoginUser(QString strUserName) { QDBusPendingCall pendingCall = asyncCall(QStringLiteral("SaveQuickLoginUser"), strUserName); Q_UNUSED(pendingCall) return 0; } QString UniAuthService::GetQuickLoginUser() { QDBusReply reply = call(QStringLiteral("GetQuickLoginUser")); if (!reply.isValid()) { qWarning() << "GetQuickLoginUser error:" << reply.error(); return QString(""); } return reply.value(); } ukui-screensaver/src/dbusifs/upowerhelper.h0000664000175000017500000000503615172041106020114 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef UPOWERHELPER_H #define UPOWERHELPER_H #include #include #include #include /** * @brief login1访问工具类 * */ class UpowerHelper : public QObject { Q_OBJECT public: explicit UpowerHelper(QObject *parent = nullptr); virtual ~UpowerHelper(); QStringList getBatteryArgs(); QString getBatteryIconName(); inline bool getIsBattery() { return m_isBattery; } private Q_SLOTS: void init(); void onUpdateBatteryInfo(); void dealAcOnlineStateChanged(int, bool); void dealBatteryInfoChanged(int, QStringList); void dealDeviceChanged(); /** * @brief onLidStateChanged 屏幕开关信号监听 */ void onLidStateChanged(bool value); /** * @brief onServiceStatusChanged upower服务状态变化 * @param strName 服务名称 * @param isActive 状态 */ void onServiceStatusChanged(QString strName, bool isActive); Q_SIGNALS: /** * @brief upowerServiceReady upower服务已准备好 */ void upowerServiceReady(); /** * @brief batteryStatusChanged 电池状态变化 * @param iconName 图标名称 */ void batteryStatusChanged(QString iconName); /** * @brief batteryChanged 电池状态改变 * @param args 参数 */ void batteryChanged(QStringList args); /** * @brief lidStateChanged 屏幕亮屏状态 * @param isClosed 是否关屏 */ void lidStateChanged(bool isClosed); private: /** * @brief isDesktop 是否是台式机 * @return true 是,其他 否 */ bool isDesktop(); private: QDBusInterface *m_upowerInterface = nullptr; QDBusInterface *m_batInterface = nullptr; QDBusInterface *m_upowerService = nullptr; bool m_isBattery = false; bool m_isInited = false; }; #endif // UPOWERHELPER_H ukui-screensaver/src/dbusifs/login1helper.cpp0000664000175000017500000001566015172041106020323 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "login1helper.h" #include #include #include #include #include #include #include "../common/global_utils.h" const static QString login1Service = QStringLiteral("org.freedesktop.login1"); const static QString login1Path = QStringLiteral("/org/freedesktop/login1"); const static QString propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); const static QString login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager"); const static QString login1SessionInterface = QStringLiteral("org.freedesktop.login1.Session"); Login1Helper::Login1Helper(QObject *parent) : QObject(parent) { m_login1Interface = new QDBusInterface(login1Service, login1Path, login1ManagerInterface, QDBusConnection::systemBus(), this); // 监听manager的睡眠、唤醒信号 connect(m_login1Interface, SIGNAL(PrepareForSleep(bool)), this, SLOT(onPrepareForSleep(bool))); QDBusReply sessionPath = m_login1Interface->call("GetSession", "auto"); if (!sessionPath.isValid()) { qWarning() << "Get session error:" << sessionPath.error(); } else { m_strSessionPath = sessionPath.value().path(); QDBusConnection::systemBus().connect( login1Service, m_strSessionPath, login1SessionInterface, QStringLiteral("Lock"), this, SIGNAL(requestLock())); QDBusConnection::systemBus().connect( login1Service, m_strSessionPath, login1SessionInterface, QStringLiteral("Unlock"), this, SIGNAL(requestUnlock())); // 获取会话激活状态 QDBusInterface iface(login1Service, m_strSessionPath, propertiesInterface, QDBusConnection::systemBus()); QDBusReply reply = iface.call("Get", login1SessionInterface, "Active"); if (reply.isValid()) { m_isSessionActive = reply.value().toBool(); qDebug() << "Session is active:" << m_isSessionActive; } else { qDebug() << "Failed to get session active state!"; } // 监听属性变化 QDBusConnection::systemBus().connect( login1Service, m_strSessionPath, propertiesInterface, "PropertiesChanged", this, SLOT(onSessionPropChanged(QString, QVariantMap, QStringList))); // 监听程序阻塞关机/睡眠 QDBusConnection::systemBus().connect( login1Service, login1Path, propertiesInterface, "PropertiesChanged", this, SLOT(onHibitedWatcherMessage(void))); } } Login1Helper::~Login1Helper() {} void Login1Helper::onSessionPropChanged(QString strInterface, QVariantMap mapVar, QStringList listValue) { if (login1SessionInterface == strInterface) { qDebug() << "onSessionPropChanged:" << strInterface << mapVar << listValue; QVariantMap::iterator itVar = mapVar.begin(); for (; itVar != mapVar.end(); itVar++) { if (itVar.key() == "Active") { QVariant varValue = itVar.value(); m_isSessionActive = varValue.toBool(); if (!m_isSleeping) { bool isRtcwakeRun = isProcessRunning("rtcwake"); qInfo() << "session active:" << m_isSessionActive << isRtcwakeRun; if (!m_isSessionActive && !isRtcwakeRun) { Q_EMIT requestLock(); } Q_EMIT sessionActiveChanged(m_isSessionActive); } break; } } } } bool Login1Helper::isSessionActive() { return m_isSessionActive; } void Login1Helper::onPrepareForSleep(bool isSleep) { m_isSleeping = isSleep; Q_EMIT PrepareForSleep(isSleep); } void Login1Helper::onHibitedWatcherMessage() { QDBusInterface iface(login1Service, login1Path, propertiesInterface, QDBusConnection::systemBus()); QDBusReply reply = iface.call("Get", "org.freedesktop.login1.Manager", "BlockInhibited"); if (reply.isValid()) { QString blockInhibited = reply.value().toString(); Q_EMIT blockInhibitedChanged(blockInhibited); } else { qDebug() << "Failed to get lid closed event!"; } } void Login1Helper::setPowerManager(const QString &powerManagerfunc) { if (m_login1Interface) { m_login1Interface->call(powerManagerfunc, true); } } bool Login1Helper::getCanPowerManager(const QString &powerManagerfunc) { qDebug() << "getCanPowerManager:" << powerManagerfunc; if (m_login1Interface) { bool canFlag = true; QDBusReply stateReply = m_login1Interface->call(powerManagerfunc); qDebug() << "reply:" << stateReply; if (stateReply.isValid() && (stateReply.value() == "yes" || stateReply.value() == "challenge")) { canFlag = true; } else { canFlag = false; } return canFlag; } else { return false; } } bool Login1Helper::isCanHibernate() { return getCanPowerManager("CanHibernate"); } bool Login1Helper::isCanSuspend() { return getCanPowerManager("CanSuspend"); } bool Login1Helper::isCanReboot() { return getCanPowerManager("CanReboot"); } bool Login1Helper::isCanPowerOff() { return getCanPowerManager("CanPowerOff"); } bool Login1Helper::canAction(Action action) { switch (action) { case PowerHibernate: return getCanPowerManager("CanHibernate"); case PowerSuspend: return getCanPowerManager("CanSuspend"); case PowerMonitorOff: return m_isCanLockScreen; case PowerLogout: return m_isCanLogout; case PowerReboot: return getCanPowerManager("CanReboot"); case PowerShutdown: return getCanPowerManager("CanPowerOff"); default: return false; } } void Login1Helper::Unlock() { if (!m_strSessionPath.isEmpty()) { QDBusInterface iface(login1Service, m_strSessionPath, login1SessionInterface, QDBusConnection::systemBus()); iface.call("Unlock"); qDebug() << "login1 unlock session:" << m_strSessionPath; } } ukui-screensaver/src/dbusifs/upowerlinepowerdbus.h0000664000175000017500000000255215172041106021517 0ustar fengfeng/* * Copyright 2023 KylinSoft Co., Ltd. * * This program is free software: you can 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 . */ #ifndef UPOWERLINEPOWERDBUS_H #define UPOWERLINEPOWERDBUS_H #include #include #include class UpowerLinePowerDBus : public QObject { Q_OBJECT public: explicit UpowerLinePowerDBus(int linePowerId, const QString &linePowerDBusPath, QObject *parent = nullptr); ~UpowerLinePowerDBus(); bool getOnLineStateFromSysFile(); int getAcOnlineState(); private: int m_linePowerId; QDBusInterface *m_DBusInterface; bool m_acOnlineState; QString m_nativePath = ""; Q_SIGNALS: void acOnlineStateChanged(int, bool); private Q_SLOTS: void dealLinePowerPropertiesMessage(QDBusMessage); }; #endif // UPOWERLINEPOWERDBUS_H ukui-screensaver/src/dbusifs/sessionhelper.cpp0000664000175000017500000002504315172041106020611 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "sessionhelper.h" #include #include #include #include #include #include #include #include #include "login1helper.h" #include "../lock-backend/gsettingshelper.h" #include "freedesktophelper.h" const static QString sessionService = QStringLiteral("org.gnome.SessionManager"); const static QString sessionPath = QStringLiteral("/org/gnome/SessionManager"); const static QString sessionInterface = QStringLiteral("org.gnome.SessionManager"); QDBusArgument &InhibitInfo::operator<<(QDBusArgument &argument, const InhibitInfo::InhibitorInfo &mystruct) { argument.beginStructure(); argument << mystruct.name << mystruct.icon; argument.endStructure(); return argument; } const QDBusArgument &InhibitInfo::operator>>(const QDBusArgument &argument, InhibitInfo::InhibitorInfo &mystruct) { argument.beginStructure(); argument >> mystruct.name >> mystruct.icon; argument.endStructure(); return argument; } SessionHelper::SessionHelper(QSharedPointer login1Helper, QObject *parent) : QObject(parent), m_login1Helper(login1Helper) { init(); } void SessionHelper::init() { if (!m_sessionInterface && FreedesktopHelper::getInstance()->NameHasOwner(sessionService)) { qDebug() << "Init session interface!"; m_sessionInterface = new QDBusInterface(sessionService, sessionPath, sessionInterface, QDBusConnection::sessionBus(), this); } else { connect( FreedesktopHelper::getInstance(), &FreedesktopHelper::serviceStatusChanged, this, &SessionHelper::onSessionDbusActiveChanged); } } void SessionHelper::onSessionDbusActiveChanged(const QString &strServerName, bool isActive) { if (strServerName == sessionService && isActive) { qDebug() << "Init session interface!"; if (!m_sessionInterface) { m_sessionInterface = new QDBusInterface( sessionService, sessionPath, sessionInterface, QDBusConnection::sessionBus(), this); } disconnect( FreedesktopHelper::getInstance(), &FreedesktopHelper::serviceStatusChanged, this, &SessionHelper::onSessionDbusActiveChanged); } } SessionHelper::~SessionHelper() {} bool SessionHelper::canAction(Action action) { // 以下为代码结构调整 if (m_sessionInterface) { QString command; switch (action) { case PowerSwitchUser: command = QLatin1String("canSwitch"); break; case PowerHibernate: command = QLatin1String("canHibernate"); break; case PowerSuspend: command = QLatin1String("canSuspend"); break; case PowerMonitorOff: command = QLatin1String("canLockscreen"); break; case PowerLogout: command = QLatin1String("canLogout"); break; case PowerReboot: command = QLatin1String("canReboot"); break; case PowerShutdown: command = QLatin1String("canPowerOff"); break; default: break; } if (!m_sessionInterface->isValid()) { qWarning() << "dbusCall: Session QDBusInterface is invalid"; return false; } QDBusReply testReply = m_sessionInterface->call(QLatin1String("canLockscreen")); if (!testReply.isValid()) { // 解决老版本升级到新版本接口不兼容的问题,在session接口不存在的情况下,调用systemd的接口 QDBusError error = testReply.error(); Login1Helper *m_login1Helper = new Login1Helper(this); if (error.type() == QDBusError::UnknownMethod) { qInfo() << "updating ! old ukui-session dose not have canAction method"; if (action == PowerLogout || action == PowerMonitorOff) { return true; } if (m_login1Helper) { return m_login1Helper->canAction(action); } } qInfo() << "dbus error"; return false; } QDBusReply reply = m_sessionInterface->call(command); qInfo() << "command = " << command << "reply = " << m_sessionInterface->call(command); return reply.value(); } else if (m_login1Helper) { if (action == PowerLogout || action == PowerMonitorOff) { return true; } return m_login1Helper->canAction(action); } else { return false; } } void SessionHelper::onLogoutHibitedWatcherMessage(uint flags) { qDebug() << "recv logoutinhibit"; Q_EMIT blockLogoutInhibitedChanged(); } bool SessionHelper::doAction(const QString &powerManagerfunc) { qDebug() << "doAction = " << powerManagerfunc; if (!playShutdownMusic(powerManagerfunc)) { doPowerManager(powerManagerfunc); } return true; } void SessionHelper::doPowerManager(const QString &powerManagerfunc) { if (m_sessionInterface) { QString command; if (powerManagerfunc == "Hibernate") { command = QLatin1String("hibernate"); } else if (powerManagerfunc == "Suspend") { command = QLatin1String("suspend"); } else if (powerManagerfunc == "Reboot") { command = QLatin1String("reboot"); } else if (powerManagerfunc == "PowerOff") { command = QLatin1String("powerOff"); } else if (powerManagerfunc == "Logout") { command = QLatin1String("logout"); } else if (powerManagerfunc == "SwitchUser") { command = QLatin1String("switchUser"); } else { return; } QDBusMessage mes; mes = m_sessionInterface->call(command); if (!(mes.errorName().isEmpty()) && m_login1Helper) { // 本来应该判断错误类别,考虑到运行效率,不做该判断 m_login1Helper->setPowerManager(powerManagerfunc); } } else if (m_login1Helper) { m_login1Helper->setPowerManager(powerManagerfunc); } } QStringList SessionHelper::getLockCheckStatus(QString type) { if (m_sessionInterface) { qDBusRegisterMetaType(); QVector resVec; if (!m_sessionInterface->isValid()) { qDebug() << "session interface invalid!"; } QDBusMessage result = m_sessionInterface->call("ListInhibitor", QVariant(type)); QList outArgs = result.arguments(); QVariant first = outArgs.at(0); const QDBusArgument &dbusArgs = first.value(); dbusArgs.beginArray(); while (!dbusArgs.atEnd()) { InhibitInfo::InhibitorInfo inhibtor; dbusArgs >> inhibtor; resVec.push_back(inhibtor); } dbusArgs.endArray(); QStringList lockCheckList; for (auto iter = resVec.begin(); iter != resVec.end(); ++iter) { lockCheckList.append(iter->icon); lockCheckList.append(iter->name); } // qDebug() << "lockCheckList = " << lockCheckList; return lockCheckList; } else { return QStringList(); } } bool SessionHelper::playShutdownMusic(const QString &powerManagerfunc) { // up_to_time and soundplayer can not be define out of this if(). // otherwise run ukui-session-tools --suspend with segmente error. // because they will be delate at the end of the playShutdownMusic(). bool play_music = false; QDBusMessage msg; GSettingsHelper *gsettingsHelper = new GSettingsHelper(this); if (powerManagerfunc == "Reboot" || powerManagerfunc == "PowerOff") { play_music = gsettingsHelper->GetSessionConf(KEY_SESSION_POWEROFF_MUSIC).toBool(); } else if (powerManagerfunc == "Logout") { play_music = gsettingsHelper->GetSessionConf(KEY_SESSION_LOGOUT_MUSIC).toBool(); if (m_sessionInterface) { msg = m_sessionInterface->call("emitStartLogout"); if (!msg.errorName().isEmpty()) { qWarning() << "Dbus error: " << msg; } } } else { if (powerManagerfunc == "SwitchUser" && m_sessionInterface) { msg = m_sessionInterface->call("emitPrepareForSwitchuser"); if (!msg.errorName().isEmpty()) { qWarning() << "Dbus error: " << msg; } } return false; } if (play_music) { if (!m_timerPlayMusic) { m_timerPlayMusic = new QTimer(); m_timerPlayMusic->setSingleShot(true); } disconnect(m_timerPlayMusic, nullptr, this, nullptr); connect(m_timerPlayMusic, &QTimer::timeout, this, [&, this, powerManagerfunc]() { this->doPowerManager(powerManagerfunc); return true; }); QString xdg_session_type = qgetenv("XDG_SESSION_TYPE"); if (powerManagerfunc == "Reboot" || powerManagerfunc == "PowerOff") { if (xdg_session_type == "wayland") { QProcess::startDetached("paplay /usr/share/ukui/ukui-session-manager/shutdown.wav"); } else { QProcess::startDetached("aplay /usr/share/ukui/ukui-session-manager/shutdown.wav"); } m_timerPlayMusic->start(5000); } else if (powerManagerfunc == "Logout") { if (xdg_session_type == "wayland") { QProcess::startDetached("paplay /usr/share/ukui/ukui-session-manager/logout.wav"); } else { QProcess::startDetached("aplay /usr/share/ukui/ukui-session-manager/logout.wav"); } m_timerPlayMusic->start(2000); } else { qDebug() << "error num"; return false; } } return play_music; } ukui-screensaver/src/VirtualKeyboard/0000775000175000017500000000000015172041106016666 5ustar fengfengukui-screensaver/src/VirtualKeyboard/README.md0000664000175000017500000000004115172041035020141 0ustar fengfengA simple virtual keyboard in X11 ukui-screensaver/src/VirtualKeyboard/VirtualKeyboard.pro0000664000175000017500000000200315172041035022513 0ustar fengfeng#------------------------------------------------- # # Project created by QtCreator 2018-10-12T16:43:42 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = VirtualKeyboard TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 CONFIG += link_pkgconfig debug PKGCONFIG += xtst x11 include(VirtualKeyboard.pri) SOURCES += \ $$PWD/src/main.cpp ukui-screensaver/src/VirtualKeyboard/CMakeLists.txt0000664000175000017500000000246215172041106021432 0ustar fengfengfind_package(Qt6 COMPONENTS Core Widgets Gui GuiPrivate REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(KYSDK_UKUIWINDOWHELPER REQUIRED kysdk-ukuiwindowhelper) if (KYSDK_UKUIWINDOWHELPER_FOUND) include_directories(${KYSDK_UKUIWINDOWHELPER_INCLUDE_DIRS}) endif() set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) qt6_add_resources(VirtualKeyboard_SRC src/keyboard.qrc) qt6_wrap_cpp(VirtualKeyboard_SRC src/fakekeyboard.h src/qxkbcommon_p.h ../common/capslockstatehelper.h ) set(VirtualKeyboard_SRC ${VirtualKeyboard_SRC} src/charsmorewidget.cpp src/charswidget.cpp src/dragwidget.cpp src/kbbutton.cpp src/kbtitle.cpp src/letterswidget.cpp src/numberswidget.cpp src/virtualkeyboardwidget.cpp src/x11keyboard.cpp src/qtkeyboard.cpp src/qxkbcommon.cpp src/qxkbcommon_3rdparty.cpp ../common/capslockstatehelper.cpp ) include_directories( ${Qt6Core_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} ) add_library(VirtualKeyboard STATIC ${VirtualKeyboard_SRC}) target_link_libraries(VirtualKeyboard Qt6::Core Qt6::Widgets ${KWAYLANDCLIENT_LIBRARIES} Qt6::Gui Qt6::GuiPrivate ${KYSDK_UKUIWINDOWHELPER_LIBRARIES} xkbcommon) ukui-screensaver/src/VirtualKeyboard/VirtualKeyboard.pri0000664000175000017500000000136215172041035022514 0ustar fengfengSOURCES += \ $$PWD/src/charsmorewidget.cpp \ $$PWD/src/charswidget.cpp \ $$PWD/src/dragwidget.cpp \ $$PWD/src/kbbutton.cpp \ $$PWD/src/kbtitle.cpp \ $$PWD/src/letterswidget.cpp \ $$PWD/src/numberswidget.cpp \ $$PWD/src/virtualkeyboardwidget.cpp \ $$PWD/src/x11keyboard.cpp HEADERS += \ $$PWD/src/charsmorewidget.h \ $$PWD/src/charswidget.h \ $$PWD/src/commondef.h \ $$PWD/src/dragwidget.h \ $$PWD/src/kbbutton.h \ $$PWD/src/kbtitle.h \ $$PWD/src/letterswidget.h \ $$PWD/src/numberswidget.h \ $$PWD/src/virtualkeyboardwidget.h \ $$PWD/src/x11keyboard.h RESOURCES += \ $$PWD/src/keyboard.qrc ukui-screensaver/src/VirtualKeyboard/src/0000775000175000017500000000000015172041106017455 5ustar fengfengukui-screensaver/src/VirtualKeyboard/src/qtkeyboard.cpp0000664000175000017500000002412515172041106022332 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "qtkeyboard.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "qxkbcommon_p.h" xkb_keymap *keymap = nullptr; xkb_state *state = nullptr; xkb_mod_index_t shiftIndex = 0; xkb_mod_index_t controlIndex = 0; xkb_mod_index_t altIndex = 0; using ScanCodeKey = std::pair; // group/layout and QtKey QMap scanCodesByQtKey; QMap m_specialSymbolMap = { {' ', Qt::Key_Space}, {',', Qt::Key_Comma}, {'.', Qt::Key_Period}, {'\'', Qt::Key_QuoteLeft}, {'@', Qt::Key_At}, {'#', Qt::Key_NumberSign}, {'$', Qt::Key_Dollar}, {'%', Qt::Key_Percent}, {'&', Qt::Key_Ampersand}, {'*', Qt::Key_Asterisk}, {'(', Qt::Key_ParenLeft}, {')', Qt::Key_ParenRight}, {'-', Qt::Key_Minus}, {'+', Qt::Key_Plus}, {'!', Qt::Key_Exclam}, {'"', Qt::Key_QuoteDbl}, {'<', Qt::Key_Less}, {'>', Qt::Key_Greater}, {':', Qt::Key_Colon}, {';', Qt::Key_Semicolon}, {'/', Qt::Key_Slash}, {'?', Qt::Key_Question}, {'=', Qt::Key_Equal}, {'.', Qt::Key_Period}, /*XK_kana_middledot*/ {'~', Qt::Key_AsciiTilde}, {'`', Qt::Key_QuoteLeft}, {'|', Qt::Key_Bar}, {'^', Qt::Key_AsciiCircum}, {'{', Qt::Key_BraceLeft}, {'}', Qt::Key_BraceRight}, {'[', Qt::Key_BracketLeft}, {']', Qt::Key_BracketRight}, {'_', Qt::Key_Underscore}, {'\\', Qt::Key_Backslash}, }; QMap m_funckeyMap = { {FuncKey::SPACE, Qt::Key_Space}, {FuncKey::BACKSPACE, Qt::Key_Backspace}, {FuncKey::ENTER, Qt::Key_Enter}, {FuncKey::HOME, Qt::Key_Home}, {FuncKey::END, Qt::Key_End}, {FuncKey::PGUP, Qt::Key_PageUp}, {FuncKey::PGDN, Qt::Key_PageDown}, {FuncKey::INSERT, Qt::Key_Insert}, {FuncKey::DELETE, Qt::Key_Delete}, {FuncKey::UP, Qt::Key_Up}, {FuncKey::DOWN, Qt::Key_Down}, {FuncKey::LEFT, Qt::Key_Left}, {FuncKey::RIGHT, Qt::Key_Right} }; QMap m_modifierMap = { {Modifier::CTRL, Qt::ControlModifier}, {Modifier::ALT, Qt::AltModifier}, {Modifier::SUPER, Qt::MetaModifier}, {Modifier::SHIFT, Qt::ShiftModifier} }; QVector m_shiftKeyVec = {'~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '{', '}', '|', ':', '"', '>', '?'}; #define DRM_DIR "/sys/class/leds/" #define CAPSLOCK_STATUS "capslock_state" int QtKeyboard::createAnonymousFile(size_t size) { QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); if (path.isEmpty()) return -1; QByteArray name = QFile::encodeName(path + QStringLiteral("/qtwayland-XXXXXX")); int fd = mkstemp(name.data()); if (fd < 0) return -1; long flags = fcntl(fd, F_GETFD); if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { close(fd); fd = -1; } unlink(name.constData()); if (fd < 0) return -1; if (ftruncate(fd, size) < 0) { close(fd); return -1; } return fd; } void QtKeyboard::maybeUpdateXkbScanCodeTable() { if (!scanCodesByQtKey.isEmpty() || !state) return; // No need to update if the table is already populated or state is not set // This function is a placeholder for any future updates to the XKB scan code table. // Currently, it does nothing but can be extended as needed. xkb_keymap_key_for_each( keymap, [](xkb_keymap *keymap, xkb_keycode_t keycode, void *d) { auto *scanCodesByQtKey = static_cast *>(d); uint numLayouts = xkb_keymap_num_layouts_for_key(keymap, keycode); for (uint layout = 0; layout < numLayouts; ++layout) { const xkb_keysym_t *syms = nullptr; xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, 0, &syms); if (!syms) continue; Qt::KeyboardModifiers mods = {}; int qtKey = QXkbCommon::keysymToQtKey(syms[0], mods, nullptr, 0, false, false); if (qtKey != 0) scanCodesByQtKey->insert({layout, qtKey}, keycode); } }, &scanCodesByQtKey); shiftIndex = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); controlIndex = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL); altIndex = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT); } uint32_t QtKeyboard::keyToScanCode(uint32_t key) { // Convert a Qt key to an XKB scan code uint scanCode = 0; maybeUpdateXkbScanCodeTable(); return scanCodesByQtKey.value({0, key}, 0); } uint32_t QtKeyboard::toWaylandCode(uint32_t key) { const uint offset = 8; if (key < offset) { qWarning() << "Key" << key << "is less than offset" << offset; return 0; // Invalid key } return key - offset; } QtKeyboard::QtKeyboard(QObject *parent) : FakeKeyboard(parent) { m_capslockStateHelper = CapslockStateHelper::instance(this); connect(m_capslockStateHelper, &CapslockStateHelper::capslockChanged, this, [=](bool state) { qDebug() << " CapsLock changed !!! " << " state = " << state; m_capsState = state; }); } QtKeyboard::~QtKeyboard() { } void QtKeyboard::addModifier(Modifier::MOD mod) { modList.push_back(mod); } void QtKeyboard::removeModifier(Modifier::MOD mod) { modList.removeOne(mod); } bool QtKeyboard::hasModifier(Modifier::MOD mod) { return modList.contains(mod); } QList QtKeyboard::getAllModifier() { return modList; } void QtKeyboard::clearModifier() { modList.clear(); } void QtKeyboard::onKeyPressed(QChar c) { /*判断大写锁定打开时,转换字母大小写状态,与x11keyboard类逻辑保持一致*/ if (m_capsState && c.isLetter()){ if(c.isUpper()){ c = c.toLower(); } else if(c.isLower()){ c = c.toUpper(); } } if(c>='A' && c<='Z'){ sendKey(c.toLatin1(),c); }else if(c>='a' && c<='z'){ sendKey(c.toLatin1() - 32,c); }else if(c >= '0' && c<='9'){ sendKey(c.toLatin1(),c); }else if(m_specialSymbolMap.contains(c)){ sendKey(m_specialSymbolMap[c],c); }else { sendKey(c.toLatin1(),c); } } void QtKeyboard::onKeyPressed(FuncKey::FUNCKEY key) { int keysym = m_funckeyMap[key]; /*这里的text根据实际按键得到的QEvent中的text内容打印*/ if(key == FuncKey::SPACE){ sendKey(keysym," "); }else if(key == FuncKey::BACKSPACE){ sendKey(keysym,"\b"); }else if(key == FuncKey::ENTER){ sendKey(keysym,"\r"); }else if(key == FuncKey::INSERT){ sendKey(keysym,"\u007F"); }else if(key == FuncKey::CAPSLOCK){ sendKey(Qt::Key_CapsLock); }else{ sendKey(keysym,""); } } void QtKeyboard::sendKey(const unsigned int keysym,const QString text) { Qt::KeyboardModifiers modifier = Qt::NoModifier; for(auto mod : modList){ modifier = modifier | m_modifierMap[mod]; } QWidget *objfous = QApplication::focusWidget(); if(objfous){ QKeyEvent event1(QEvent::KeyPress, keysym , modifier, text , true, 1); QKeyEvent event2(QEvent::KeyRelease, keysym , modifier, text , true, 1); QCoreApplication::sendEvent(objfous, &event1); QCoreApplication::sendEvent(objfous, &event2); } } void QtKeyboard::sendKey(Qt::Key key, bool shift) { struct xkb_context *ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!ctx) { qWarning() << "Failed to create xkb context"; xkb_context_unref(ctx); return ; } struct xkb_rule_names names; names.rules = "evdev"; names.model = "pc105"; names.layout = "us"; names.variant = ""; names.options = ""; keymap = xkb_keymap_new_from_names(ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { qWarning() << "Failed to create xkb keymap"; xkb_context_unref(ctx); xkb_keymap_unref(keymap); return ; } char *keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); if (!keymap_str) { qWarning("Failed to compile global XKB keymap"); xkb_context_unref(ctx); xkb_keymap_unref(keymap); return ; } state = xkb_state_new(keymap); UkuiVirtualKeyboard *keyboard = new UkuiVirtualKeyboard(keymap_str); if (shift) { uint32_t shift_scanCode = keyToScanCode(Qt::Key_Shift); xkb_state_update_key(state, shift_scanCode, XKB_KEY_DOWN); uint32_t modsDepressed = xkb_state_serialize_mods(state, XKB_STATE_MODS_DEPRESSED); uint32_t modsLatched = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); uint32_t modsLocked = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); keyboard->modifiers(modsDepressed, modsLatched, modsLocked); } keyboard->keyPress(toWaylandCode(keyToScanCode(key))); keyboard->keyRelease(toWaylandCode(keyToScanCode(key))); xkb_state_unref(state); xkb_keymap_unref(keymap); xkb_context_unref(ctx); free(keymap_str); } ukui-screensaver/src/VirtualKeyboard/src/letterswidget.h0000664000175000017500000000321715172041106022517 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LETTERSWIDGET_H #define LETTERSWIDGET_H #include #include #include "kbbutton.h" #include "capslockstatehelper.h" #include class LettersWidget : public QWidget { Q_OBJECT public: explicit LettersWidget(QWidget *parent = nullptr); virtual ~LettersWidget(); void adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical = false, bool floatStatus = false); void changeFuncKeyStyle(QString obj, bool isLock); public Q_SLOTS: void onBtnClicked(QChar charId); void onCapsChanged(); Q_SIGNALS: void clicked(int nKeyId); void specialBtnClicked(QString keyName); void normalBtnClicked(QChar c); private: void initUI(); void toggleCase(); private: QMap m_mapBtnGeometrys; bool isShift = false; QGSettings *settings; bool capsState = false; bool isWayland = false; CapslockStateHelper *m_capslockStateHelper; }; #endif // LETTERSWIDGET_H ukui-screensaver/src/VirtualKeyboard/src/qtkeyboard.h0000664000175000017500000000327415172041106022001 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef QTKEYBOARD_H #define QTKEYBOARD_H #include #include #include "fakekeyboard.h" #include "capslockstatehelper.h" class QtKeyboard : public FakeKeyboard { Q_OBJECT public: explicit QtKeyboard(QObject *parent = nullptr); ~QtKeyboard(); void addModifier(Modifier::MOD mod); void removeModifier(Modifier::MOD mod); bool hasModifier(Modifier::MOD mod); QList getAllModifier(); void clearModifier(); public Q_SLOTS: void onKeyPressed(QChar c); void onKeyPressed(FuncKey::FUNCKEY key); private: void sendKey(const unsigned int keyCode,const QString text); void sendKey(Qt::Key key, bool shift = false); int createAnonymousFile(size_t size); void maybeUpdateXkbScanCodeTable(); uint32_t keyToScanCode(uint32_t key); uint32_t toWaylandCode(uint32_t key); void findKeyForScanCode61(); private: QList modList; CapslockStateHelper *m_capslockStateHelper; bool m_capsState = false; }; #endif // QTKEYBOARD_H ukui-screensaver/src/VirtualKeyboard/src/x11keyboard.cpp0000664000175000017500000001243315172041035022317 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "x11keyboard.h" #include #include #include #include struct CharMap { QChar name; KeySym code; }; struct CharMap XSpecialSymbolMap[] { {' ', XK_space}, {',', XK_comma}, {'.', XK_period}, {'\'', XK_quoteright}, {'@', XK_at}, {'#', XK_numbersign}, {'$', XK_dollar}, {'%', XK_percent}, {'&', XK_ampersand}, {'*', XK_asterisk}, {'(', XK_parenleft}, {')', XK_parenright}, {'-', XK_minus}, {'+', XK_plus}, {'!', XK_exclam}, {'"', XK_quotedbl}, {'<', XK_less}, {'>', XK_greater}, {':', XK_colon}, {';', XK_semicolon}, {'/', XK_slash}, {'?', XK_question}, {'=', XK_equal}, {'.', XK_kana_middledot}, {'~', XK_asciitilde}, {'`', XK_grave}, {'|', XK_bar}, {'^', XK_asciicircum}, {'{', XK_braceleft}, {'}', XK_braceright}, {'[', XK_bracketleft}, {']', XK_bracketright}, {'_', XK_underscore}, {'\\', XK_backslash}, }; QMap funckeyMap = { {FuncKey::SPACE, XK_space}, {FuncKey::BACKSPACE, XK_BackSpace}, {FuncKey::ENTER, XK_Return}, {FuncKey::HOME, XK_Home}, {FuncKey::END, XK_End}, {FuncKey::PGUP, XK_Page_Up}, {FuncKey::PGDN, XK_Page_Down}, {FuncKey::INSERT, XK_Insert}, {FuncKey::DELETE, XK_Delete}, {FuncKey::UP, XK_Up}, {FuncKey::DOWN, XK_Down}, {FuncKey::LEFT, XK_Left}, {FuncKey::RIGHT, XK_Right}, {FuncKey::CAPSLOCK, XK_Caps_Lock} }; QMap modifierMap = { {Modifier::CTRL, XK_Control_L}, {Modifier::ALT, XK_Alt_L}, {Modifier::SUPER, XK_Super_L}, {Modifier::SHIFT, XK_Shift_L}, }; QVector shiftKeyVec = {'~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '{', '}', '|', ':', '"', '>', '?'}; static Display *display = XOpenDisplay(0); bool isShift = false; bool isLetter = false; unsigned int keyCodeOfChar(QChar c) { QString text(c); KeySym keysym = XStringToKeysym(text.toLocal8Bit().data()); if(keysym == NoSymbol) { int symbolCount = sizeof(XSpecialSymbolMap) / sizeof(struct CharMap); for(int i = 0; i < symbolCount; i++) { if(XSpecialSymbolMap[i].name == c) { keysym = XSpecialSymbolMap[i].code; break; } } } qDebug() << "keysym: " << keysym; isShift = shiftKeyVec.contains(c) || (c >= 'A' && c <= 'Z'); isLetter = c.isLetter(); KeyCode code = XKeysymToKeycode(display, keysym); return code; } X11Keyboard::X11Keyboard(QObject *parent) : FakeKeyboard(parent) { } X11Keyboard::~X11Keyboard() { XCloseDisplay(display); } void X11Keyboard::addModifier(Modifier::MOD mod) { modList.push_back(mod); } void X11Keyboard::removeModifier(Modifier::MOD mod) { modList.removeOne(mod); } bool X11Keyboard::hasModifier(Modifier::MOD mod) { return modList.contains(mod); } QList X11Keyboard::getAllModifier() { return modList; } void X11Keyboard::clearModifier() { modList.clear(); } void X11Keyboard::onKeyPressed(QChar c) { unsigned int keyCode = keyCodeOfChar(c); sendKey(keyCode); } void X11Keyboard::onKeyPressed(FuncKey::FUNCKEY key) { KeyCode keyCode = NoSymbol; KeySym keysym = funckeyMap[key]; if(keysym != NoSymbol) { keyCode = XKeysymToKeycode(display, keysym); sendKey(keyCode); } } void X11Keyboard::sendKey(unsigned int keyCode) { Window focusWindow; int revert; XGetInputFocus(display, &focusWindow, &revert); for(auto mod : modList){ KeyCode keyCode = XKeysymToKeycode(display, modifierMap[mod]); XTestFakeKeyEvent(display, keyCode, True, 2); } //如果使用了修饰键(如ctrl、alt)且字符键是字母,则不起用shift键,否则快捷键不起作用 if(!modList.isEmpty() && isLetter) isShift = false; if(isShift) XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Shift_L), True, 2); XTestFakeKeyEvent(display, keyCode, True, CurrentTime); XTestFakeKeyEvent(display, keyCode, False, CurrentTime); if(isShift) XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Shift_L), False, 2); for(auto mod : modList){ KeyCode keyCode = XKeysymToKeycode(display, modifierMap[mod]); XTestFakeKeyEvent(display, keyCode, False, 2); } //输入结束清除shift状态 isShift = false; XFlush(display); } ukui-screensaver/src/VirtualKeyboard/src/virtualkeyboardwidget.h0000664000175000017500000000521415172041106024243 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef VIRTUALKEYBOARDWIDGET_H #define VIRTUALKEYBOARDWIDGET_H #include #include #include #include #include #include "fakekeyboard.h" class DragWidget; class KBTitle; class LettersWidget; class NumbersWidget; class CharsWidget; class CharsMoreWidget; class VirtualKeyboardWidget : public QWidget, public QAbstractNativeEventFilter { Q_OBJECT public: enum { VKB_PAGE_LETTERS, VKB_PAGE_NUMBERS, VKB_PAGE_CHARS, VKB_PAGE_CHARSMORE, }; VirtualKeyboardWidget(QWidget *parent = nullptr); virtual ~VirtualKeyboardWidget(); bool getFloatStatus(); void adjustGeometry(); virtual bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; public Q_SLOTS: void onNormalBtnClicked(QChar c); void onSpecialBtnClicked(QString keyName); Q_SIGNALS: void aboutToClose(); void aboutToFloat(); void keyPressed(QChar c); void keyPressed(FuncKey::FUNCKEY key); protected: void paintEvent(QPaintEvent *) override; void resizeEvent(QResizeEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override; private: void initUI(); void initConnections(); void onMouseEvents(int type, const QPoint &pos); void clearModifier(); private: double m_lfWidthScale; double m_lfHeightScale; bool m_isVertical; QStackedWidget *m_stackedWidget = nullptr; LettersWidget *m_lettersWidget = nullptr; NumbersWidget *m_numbersWidget = nullptr; CharsWidget *m_charsWidget = nullptr; CharsMoreWidget *m_charsMoreWidget = nullptr; KBTitle *m_kbTitle = nullptr; DragWidget *m_dragWidget = nullptr; int m_nCurPage; QList m_listPageHis; bool m_isdragState = false;//是否为悬浮状态 bool isMove;// 是否可移动 QPoint lastPoint;// 拖拽控件时 记录当前控件的位置 FakeKeyboard *vKeyboard; }; #endif // VIRTUALKEYBOARDWIDGET_H ukui-screensaver/src/VirtualKeyboard/src/dragwidget.h0000664000175000017500000000223515172041035021752 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef DRAGWIDGET_H #define DRAGWIDGET_H #include #include #include class DragWidget : public QWidget { Q_OBJECT public: DragWidget(QWidget *parent = nullptr); virtual ~DragWidget(); public: void adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical = false, bool floatStatus = false); private: void initUI(); private: QLabel *m_labelDrag = nullptr; QMap m_mapSubGeometrys; }; #endif // DRAGWIDGET_H ukui-screensaver/src/VirtualKeyboard/src/fakekeyboard.h0000664000175000017500000000545415172041035022266 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef FAKEKEYBOARD_H #define FAKEKEYBOARD_H #include #include class Modifier : public QObject { Q_OBJECT public: Modifier(){} enum MOD{ UNKNOWN = -1, CTRL, SHIFT, ALT, SUPER }; Q_ENUM(MOD) static QString getModifierName(int mod) { QMetaEnum metaEnum = QMetaEnum::fromType(); const char* modName = metaEnum.valueToKey(mod); QString result = QString(modName).toLower(); return result; } static MOD getModifier(const QString &modName) { QMetaEnum metaEnum = QMetaEnum::fromType(); MOD mod = (MOD)metaEnum.keyToValue(modName.toUpper().toLocal8Bit().data()); return mod; } }; class FuncKey : public QObject { Q_OBJECT public: FuncKey(){} enum FUNCKEY { UNKNOWN = -1, SPACE = 0, BACKSPACE, ENTER, HOME, END, PGUP, PGDN, INSERT, DELETE, UP, DOWN, LEFT, RIGHT, CAPSLOCK }; Q_ENUM(FUNCKEY) static QString getKeyName(int key) { QMetaEnum metaEnum = QMetaEnum::fromType(); const char* keyName = metaEnum.valueToKey(key); QString result = QString(keyName).toLower(); return result; } static FUNCKEY getKey(const QString &keyName) { QMetaEnum metaEnum = QMetaEnum::fromType(); FUNCKEY key = (FUNCKEY)metaEnum.keyToValue(keyName.toUpper().toLocal8Bit().data()); return key; } }; class FakeKeyboard : public QObject { Q_OBJECT public: explicit FakeKeyboard(QObject *parent = nullptr) : QObject(parent) { } virtual void addModifier(Modifier::MOD mod) = 0; virtual void removeModifier(Modifier::MOD mod) = 0; virtual bool hasModifier(Modifier::MOD mod) = 0; virtual QList getAllModifier() = 0; virtual void clearModifier() = 0; public Q_SLOTS: virtual void onKeyPressed(QChar c) = 0; virtual void onKeyPressed(FuncKey::FUNCKEY key) = 0; }; #endif // FAKEKEYBOARD_H ukui-screensaver/src/VirtualKeyboard/src/qxkbcommon_3rdparty.cpp0000664000175000017500000002413315172041106024172 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui 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$ ** ****************************************************************************/ /* Copyright 1985, 1987, 1990, 1998 The Open Group 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 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. Except as contained in this notice, the names of the authors or their institutions shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. Copyright © 2009 Dan Nicholson 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 (including the next paragraph) 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. */ /* XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c The following code modifications were applied: XConvertCase() was renamed to xkbcommon_XConvertCase(), to not confuse it with Xlib's XConvertCase(). UCSConvertCase() was renamed to qt_UCSConvertCase() and function's body was replaced to use Qt APIs for doing case conversion, which should give us better results instead of using the less complete version from keysym.c */ #include "qxkbcommon_p.h" #include static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper) { *lower = QChar::toLower(code); *upper = QChar::toUpper(code); } void QXkbCommon::xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper) { /* Latin 1 keysym */ if (sym < 0x100) { qt_UCSConvertCase(sym, lower, upper); return; } /* Unicode keysym */ if ((sym & 0xff000000) == 0x01000000) { qt_UCSConvertCase((sym & 0x00ffffff), lower, upper); *upper |= 0x01000000; *lower |= 0x01000000; return; } /* Legacy keysym */ *lower = sym; *upper = sym; switch (sym >> 8) { case 1: /* Latin 2 */ /* Assume the KeySym is a legal value (ignore discontinuities) */ if (sym == XKB_KEY_Aogonek) *lower = XKB_KEY_aogonek; else if (sym >= XKB_KEY_Lstroke && sym <= XKB_KEY_Sacute) *lower += (XKB_KEY_lstroke - XKB_KEY_Lstroke); else if (sym >= XKB_KEY_Scaron && sym <= XKB_KEY_Zacute) *lower += (XKB_KEY_scaron - XKB_KEY_Scaron); else if (sym >= XKB_KEY_Zcaron && sym <= XKB_KEY_Zabovedot) *lower += (XKB_KEY_zcaron - XKB_KEY_Zcaron); else if (sym == XKB_KEY_aogonek) *upper = XKB_KEY_Aogonek; else if (sym >= XKB_KEY_lstroke && sym <= XKB_KEY_sacute) *upper -= (XKB_KEY_lstroke - XKB_KEY_Lstroke); else if (sym >= XKB_KEY_scaron && sym <= XKB_KEY_zacute) *upper -= (XKB_KEY_scaron - XKB_KEY_Scaron); else if (sym >= XKB_KEY_zcaron && sym <= XKB_KEY_zabovedot) *upper -= (XKB_KEY_zcaron - XKB_KEY_Zcaron); else if (sym >= XKB_KEY_Racute && sym <= XKB_KEY_Tcedilla) *lower += (XKB_KEY_racute - XKB_KEY_Racute); else if (sym >= XKB_KEY_racute && sym <= XKB_KEY_tcedilla) *upper -= (XKB_KEY_racute - XKB_KEY_Racute); break; case 2: /* Latin 3 */ /* Assume the KeySym is a legal value (ignore discontinuities) */ if (sym >= XKB_KEY_Hstroke && sym <= XKB_KEY_Hcircumflex) *lower += (XKB_KEY_hstroke - XKB_KEY_Hstroke); else if (sym >= XKB_KEY_Gbreve && sym <= XKB_KEY_Jcircumflex) *lower += (XKB_KEY_gbreve - XKB_KEY_Gbreve); else if (sym >= XKB_KEY_hstroke && sym <= XKB_KEY_hcircumflex) *upper -= (XKB_KEY_hstroke - XKB_KEY_Hstroke); else if (sym >= XKB_KEY_gbreve && sym <= XKB_KEY_jcircumflex) *upper -= (XKB_KEY_gbreve - XKB_KEY_Gbreve); else if (sym >= XKB_KEY_Cabovedot && sym <= XKB_KEY_Scircumflex) *lower += (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); else if (sym >= XKB_KEY_cabovedot && sym <= XKB_KEY_scircumflex) *upper -= (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); break; case 3: /* Latin 4 */ /* Assume the KeySym is a legal value (ignore discontinuities) */ if (sym >= XKB_KEY_Rcedilla && sym <= XKB_KEY_Tslash) *lower += (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); else if (sym >= XKB_KEY_rcedilla && sym <= XKB_KEY_tslash) *upper -= (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); else if (sym == XKB_KEY_ENG) *lower = XKB_KEY_eng; else if (sym == XKB_KEY_eng) *upper = XKB_KEY_ENG; else if (sym >= XKB_KEY_Amacron && sym <= XKB_KEY_Umacron) *lower += (XKB_KEY_amacron - XKB_KEY_Amacron); else if (sym >= XKB_KEY_amacron && sym <= XKB_KEY_umacron) *upper -= (XKB_KEY_amacron - XKB_KEY_Amacron); break; case 6: /* Cyrillic */ /* Assume the KeySym is a legal value (ignore discontinuities) */ if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Serbian_DZE) *lower -= (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Serbian_dze) *upper += (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); else if (sym >= XKB_KEY_Cyrillic_YU && sym <= XKB_KEY_Cyrillic_HARDSIGN) *lower -= (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); else if (sym >= XKB_KEY_Cyrillic_yu && sym <= XKB_KEY_Cyrillic_hardsign) *upper += (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); break; case 7: /* Greek */ /* Assume the KeySym is a legal value (ignore discontinuities) */ if (sym >= XKB_KEY_Greek_ALPHAaccent && sym <= XKB_KEY_Greek_OMEGAaccent) *lower += (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); else if (sym >= XKB_KEY_Greek_alphaaccent && sym <= XKB_KEY_Greek_omegaaccent && sym != XKB_KEY_Greek_iotaaccentdieresis && sym != XKB_KEY_Greek_upsilonaccentdieresis) *upper -= (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); else if (sym >= XKB_KEY_Greek_ALPHA && sym <= XKB_KEY_Greek_OMEGA) *lower += (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); else if (sym >= XKB_KEY_Greek_alpha && sym <= XKB_KEY_Greek_omega && sym != XKB_KEY_Greek_finalsmallsigma) *upper -= (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); break; case 0x13: /* Latin 9 */ if (sym == XKB_KEY_OE) *lower = XKB_KEY_oe; else if (sym == XKB_KEY_oe) *upper = XKB_KEY_OE; else if (sym == XKB_KEY_Ydiaeresis) *lower = XKB_KEY_ydiaeresis; break; } } ukui-screensaver/src/VirtualKeyboard/src/commondef.h0000664000175000017500000001124015172041035021574 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef COMMONDEF_H #define COMMONDEF_H #define KEYBOARD_PARENT_DEFAULT_WIDTH 1620 #define KEYBOARD_PARENT_DEFAULT_HEIGHT 1080 #define KEYBOARD_FIXED_DEFAULT_WIDTH 1620 #define KEYBOARD_DRAGSHOW_FIXED_DEFAULT_HEIGHT 428 #define KEYBOARD_DRAGSHOW_FIXED_DEFAULT_WIDTH 1458 #define KEYBOARD_DRAGHIDE_FIXED_DEFAULT_HEIGHT 404 #define KEYBOARD_TITLEBTN_DEFAULT_WIDTH 56 #define KEYBOARD_TITLEBTN_DEFAULT_HEIGHT 56 #define KEYBOARD_TITLE_DEFAULT_HEIGHT 68 #define KEYBOARD_DRAGBTN_DEFAULT_WIDTH 56 #define KEYBOARD_DRAGBTN_DEFAULT_HEIGHT 4 #define KEYBOARD_DRAG_DEFAULT_HEIGHT 26 #define KEYBOARD_FLOAT_PERCENTAGE 0.9 #define KEYBOARD_FIXED_DEFAULT_LMARGIN 22 // 左边距 #define KEYBOARD_FIXED_DEFAULT_RMARGIN 22 // 右边距 #define KEYBOARD_FIXED_DEFAULT_TMARGIN 8 // 上边距 #define KEYBOARD_FIXED_DEFAULT_BMARGIN 16 // 下边距 #define KEYBOAED_FIXED_DEFAULT_VSPACING 8 // 垂直间隔 #define KEYBOAED_FIXED_DEFAULT_HSPACING 8 // 水平间隔 // letters #define KEYBOARD_FIXED_DEFAULT_LETTER_L1 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) // 字符页面左起点 #define KEYBOARD_FIXED_DEFAULT_LETTER_L2 (KEYBOARD_FIXED_DEFAULT_LMARGIN+72) // 字符页面左起点 #define KEYBOARD_FIXED_DEFAULT_LETTER_L3 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) // 字符页面左起点 #define KEYBOARD_FIXED_DEFAULT_LETTER_L4 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) // 字符页面左起点 #define KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH 136 // 字符按钮宽度 #define KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT 72 // 字符按钮高度 #define KEYBOARD_FIXED_DEFAULT_LETTER_ENTWIDTH 208 #define KEYBOARD_FIXED_DEFAULT_LETTER_SPCWIDTH 712 #define KEYBOARD_FIXED_DEFAULT_TITLEBTN_WIDTH 56 #define KEYBOARD_FIXED_DEFAULT_TITLEBTN_HEIGHT 56 #define KEYBOARD_FIXED_DEFAULT_ENTERBTN_WIDTH 208 #define KEYBOARD_FIXED_DEFAULT_SPACEBTN_WIDTH 712 #define KEYBOARD_LETTER_COLOR_NORMAL "#FFFFFF" #define KEYBOARD_LETTER_COLOR_PRESSED "#DDE0E4" #define KEYBOARD_LETTER_COLOR_BORDER_NORMAL "#95A0AD" #define KEYBOARD_LETTER_COLOR_BORDER_PRESSED "#95A0AD" #define KEYBOARD_OTHER_COLOR_NORMAL "#CED3D9" #define KEYBOARD_OTHER_COLOR_PRESSED "#3790FA" #define KEYBOARD_OTHER_COLOR_BORDER_NORMAL "#95A0AD" #define KEYBOARD_OTHER_COLOR_BORDER_PRESSED "#1174E5" #define KEYBOARD_FONT_COLOR_PRESS "#262626" #define KEYBOARD_OTHER_FONT_COLOR_PRESS "#FFFFFF" //numbers #define KEYBOARD_FIXED_DEFAULT_NUMBER_L1 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) #define KEYBOARD_FIXED_DEFAULT_NUMBER_L2 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) #define KEYBOARD_FIXED_DEFAULT_NUMBER_L3 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) #define KEYBOARD_FIXED_DEFAULT_NUMBER_L4 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) #define KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH 306 // 数字按钮宽度 #define KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT 72 // 数字按钮高度 #define KEYBOARD_FIXED_DEFAULT_NUMBER_CHARS_HEIGHT 230 // 数字字符按钮高度 // chars more #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_L1 (KEYBOARD_FIXED_DEFAULT_LMARGIN+0) #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_HSPACING 16 #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_VSPACING 16 #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_COLS 6 #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_WIDTH 1350 // 字符列表宽度 #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_HEIGHT 315 // 字符列表高度 #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_WIDTH 214 // 按钮宽度 #define KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_HEIGHT 94 // 功能按钮高度 #define KEYBOARD_FIXED_DEFAULT_NORMAL_CHARSMORE_BTN_HEIGHT 80 // 符号按钮高度 //iconsize #define KEYBOARD_FIXED_DEFAULT_ICONSIZE QSize(25, 25) //keyName #define PAGE_CHAR "page_char" #define PAGE_NUMBER "page_number" #define PAGE_CHARSMORE "page_charmore" #define PAGE_LETTER "page_letter" #define BTN_FLOAT "float" #define BTN_CLOSE "close" #define BTN_RETURN "return" #define BTN_BACK "backspace" #define BTN_ENTER "enter" #define BTN_SHIFT "shift" #define BTN_CTRL "ctrl" #define BTN_ALT "alt" #define BTN_SPACE "space" #define BTN_RIGHT "right" #define BTN_LEFT "left" #define BTN_CAPSLOCK "capslock" #endif // COMMONDEF_H ukui-screensaver/src/VirtualKeyboard/src/x11keyboard.h0000664000175000017500000000250015172041035021756 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef X11KEYBOARD_H #define X11KEYBOARD_H #include #include #include "fakekeyboard.h" class X11Keyboard : public FakeKeyboard { Q_OBJECT public: explicit X11Keyboard(QObject *parent = nullptr); ~X11Keyboard(); void addModifier(Modifier::MOD mod); void removeModifier(Modifier::MOD mod); bool hasModifier(Modifier::MOD mod); QList getAllModifier(); void clearModifier(); public Q_SLOTS: void onKeyPressed(QChar c); void onKeyPressed(FuncKey::FUNCKEY key); private: void sendKey(unsigned int keyCode); private: QList modList; }; #endif // X11KEYBOARD_H ukui-screensaver/src/VirtualKeyboard/src/dragwidget.cpp0000664000175000017500000000525015172041035022305 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "dragwidget.h" #include #include #include #include "commondef.h" DragWidget::DragWidget(QWidget *parent/* = nullptr*/) : QWidget(parent) { setAttribute(Qt::WA_TranslucentBackground);//背景透明 initUI(); } DragWidget::~DragWidget() { } void DragWidget::initUI() { setFixedHeight(KEYBOARD_DRAG_DEFAULT_HEIGHT); m_labelDrag = new QLabel(this); m_labelDrag->setPixmap(QPixmap(":/images/images/drag.svg")); m_labelDrag->setGeometry((KEYBOARD_PARENT_DEFAULT_WIDTH - KEYBOARD_TITLEBTN_DEFAULT_WIDTH)/2, (this->height() - KEYBOARD_TITLEBTN_DEFAULT_HEIGHT)/2, KEYBOARD_TITLEBTN_DEFAULT_WIDTH, KEYBOARD_TITLEBTN_DEFAULT_HEIGHT); m_mapSubGeometrys[m_labelDrag] = m_labelDrag->geometry(); } void DragWidget::adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical/* = false*/, bool floatStatus) { setFixedHeight(KEYBOARD_DRAG_DEFAULT_HEIGHT*lfHeightScale); QMap::iterator itGeometry = m_mapSubGeometrys.begin(); for (; itGeometry != m_mapSubGeometrys.end(); itGeometry ++) { QLabel *label = itGeometry.key(); if (label) { QRect oldGeometry = itGeometry.value(); QRect newGeometry = oldGeometry; if (floatStatus) { newGeometry.setX(oldGeometry.x()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } else { newGeometry.setX(oldGeometry.x()*lfWidthScale); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } label->setGeometry(newGeometry); } } } ukui-screensaver/src/VirtualKeyboard/src/numberswidget.h0000664000175000017500000000307315172041035022511 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef NUMBERSWIDGET_H #define NUMBERSWIDGET_H #include #include class KBButton; class QVBoxLayout; class QScrollArea; class QFrame; class NumbersWidget : public QWidget { Q_OBJECT public: explicit NumbersWidget(QWidget *parent = nullptr); virtual ~NumbersWidget(); void adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical = false, bool floatStatus = false); public Q_SLOTS: void onBtnClicked(QChar charId); Q_SIGNALS: void clicked(int nKeyId); void specialBtnClicked(QString keyName); void narmalBtnClicked(QChar charId); protected: void changeEvent(QEvent *event); private: void initUI(); void refreshTranslate(); private: QMap m_mapBtnGeometrys; QVBoxLayout *m_layoutBtnList = nullptr; QScrollArea *m_scrollFrame = nullptr; QFrame *listFrame = nullptr; }; #endif // NUMBERSWIDGET_H ukui-screensaver/src/VirtualKeyboard/src/kbbutton.cpp0000664000175000017500000001543415172041106022020 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "kbbutton.h" #include "commondef.h" #include #include KBButton::KBButton(QWidget *parent/* = nullptr*/) : QPushButton(parent) { this->installEventFilter(this); this->setIconSize(KEYBOARD_FIXED_DEFAULT_ICONSIZE); this->setFocusPolicy(Qt::NoFocus); connect(this, &QPushButton::clicked, this, [&,this]() { if (m_charId.isLetter()) { if (m_isShift) { if (m_isCaps) { m_charId = m_charId.toLower(); } else { m_charId = m_charId.toUpper(); } } else { if (m_isCaps) { m_charId = m_charId.toUpper(); } else { m_charId = m_charId.toLower(); } } } Q_EMIT clicked(m_charId); }); // 保存原始文本 if (m_originalText.isEmpty()) { m_originalText = text(); } } KBButton::~KBButton() { } bool KBButton::eventFilter(QObject *watched, QEvent *event) { if (m_clrNormal == KEYBOARD_LETTER_COLOR_NORMAL) return QPushButton::eventFilter(watched, event); if (event->type() == QEvent::MouseButtonPress) { QPixmap pressIcon = this->icon().pixmap(QSize(24, 24)); pressIcon = drawSymbolicColoredPixmap(pressIcon, "white"); this->setIcon(pressIcon); } else if (event->type() == QEvent::MouseButtonRelease) { QPixmap releaseIcon = this->icon().pixmap(QSize(24, 24)); releaseIcon = drawSymbolicColoredPixmap(releaseIcon, "black"); this->setIcon(releaseIcon); } return QPushButton::eventFilter(watched, event); } void KBButton::setCharId(QChar charId) { m_charId = charId; if (charId == '&') { setText("&&"); } else { setText(m_charId); } } void KBButton::updateStyleSheet(QString clrNormal, QString clrHover, QString clrChecked, QString clrBoard, QString clrBoardPress, QString clrFontPress, int radius, bool is_lock) { QString strBorderRadius = QString("border-top-left-radius: %1px; border-top-right-radius: %2px; border-bottom-right-radius: %3px;border-bottom-left-radius: %4px;") .arg((radius&BORDER_RADIUS_LT)?8:0) .arg((radius&BORDER_RADIUS_TR)?8:0) .arg((radius&BORDER_RADIUS_RB)?8:0) .arg((radius&BORDER_RADIUS_LB)?8:0); if (!is_lock) { setStyleSheet(QString("QPushButton{%1 border: 2px solid %2; border-width: 0px 0px 2px 0px; background: %3; color: #262626}" // "QPushButton:hover{border: 2px solid %4; border-width: 2px 0px 0px 0px; background: %5;}" "QPushButton:pressed{border: 2px solid %4; border-width: 2px 0px 0px 0px; background: %5; color: %6}" "QPushButton:checked{border: 2px solid %7; border-width: 2px 0px 0px 0px; background: %8; color: %9}") .arg(strBorderRadius).arg(clrBoard).arg(clrNormal).arg(clrBoardPress).arg(clrHover).arg(clrFontPress).arg(clrBoardPress).arg(clrChecked).arg(clrFontPress) ); } else { setStyleSheet(QString("QPushButton{%1 border: 2px solid %2; border-width: 0px 0px 2px 0px; background: %3; color: #FFFFFF}" // "QPushButton:hover{border: 2px solid %4; border-width: 2px 0px 0px 0px; background: %5;}" "QPushButton:pressed{border: 2px solid %4; border-width: 2px 0px 0px 0px; background: %5; color: %6}" "QPushButton:checked{border: 2px solid %7; border-width: 2px 0px 0px 0px; background: %8; color: %9}") .arg(strBorderRadius).arg(clrBoard).arg(clrNormal).arg(clrBoardPress).arg(clrHover).arg(clrFontPress).arg(clrBoardPress).arg(clrChecked).arg(clrFontPress) ); } m_clrBoard = clrBoard; m_clrNormal = clrNormal; m_clrBoardPress = clrBoardPress; m_clrHover = clrHover; m_clrChecked = clrChecked; if (m_clrNormal == KEYBOARD_LETTER_COLOR_NORMAL) { sysFont.setPixelSize(32); this->setFont(sysFont); this->setIconSize(QSize(32, 32)); } else { sysFont.setPixelSize(24); this->setFont(sysFont); this->setIconSize(QSize(24, 24)); } } QPixmap KBButton::drawSymbolicColoredPixmap(const QPixmap &source, QString cgColor) { QImage img = source.toImage(); for (int x = 0; x < img.width(); x++) { for (int y = 0; y < img.height(); y++) { auto color = img.pixelColor(x, y); if (color.alpha() > 0) { if ( "white" == cgColor) { color.setRed(255); color.setGreen(255); color.setBlue(255); img.setPixelColor(x, y, color); } else if( "black" == cgColor) { color.setRed(0); color.setGreen(0); color.setBlue(0); img.setPixelColor(x, y, color); } else if ("gray"== cgColor) { color.setRed(152); color.setGreen(163); color.setBlue(164); img.setPixelColor(x, y, color); } else if ("blue" == cgColor){ color.setRed(61); color.setGreen(107); color.setBlue(229); img.setPixelColor(x, y, color); } else { return source; } } } } return QPixmap::fromImage(img); } void KBButton::setShiftState(bool isShift) { if (m_isShift != isShift) { if (m_charId.isLetter()) { if (isShift) { setText(m_charId.toUpper()); } else { setText(m_charId.toLower()); } } m_isShift = isShift; } } void KBButton::setCapsStatus(bool isCaps) { m_isCaps = isCaps; setShiftState(m_isCaps); } void KBButton::setCtrlState(bool isCtrl) { } void KBButton::setAltState(bool isAlt) { } ukui-screensaver/src/VirtualKeyboard/src/cursormonitor.cpp0000664000175000017500000000404415172041035023111 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "cursormonitor.h" #include #include #include CursorMonitor::CursorMonitor(QObject *parent) : QThread(parent) { } void CursorMonitor::run() { Display *display; XEvent xevent; Window window; display = XOpenDisplay(NULL); if (display == 0) { syslog(LOG_ERR, "CursorMonitor unable to open display"); return; } window = DefaultRootWindow(display); XAllowEvents(display, AsyncBoth, CurrentTime); XGrabPointer(display, window, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask , GrabModeAsync, GrabModeAsync, None, None, CurrentTime); while(1) { XNextEvent(display, &xevent); switch (xevent.type) { case MotionNotify: //printf("Mouse move : [%d, %d]\n", xevent.xmotion.x_root, xevent.xmotion.y_root); Q_EMIT cursorPosChanged(QPoint(xevent.xmotion.x_root, xevent.xmotion.y_root)); break; case ButtonPress: // printf("Button pressed : %s\n", key_name[xevent.xbutton.button - 1]); break; case ButtonRelease: // printf("Button released : %s\n", key_name[xevent.xbutton.button - 1]); break; } } } ukui-screensaver/src/VirtualKeyboard/src/keyboard.qrc0000664000175000017500000000107515172041035021770 0ustar fengfeng images/enter.svg images/left.svg images/right.svg images/close.svg images/unlock.svg images/space.svg images/shift.svg images/float.svg images/float-restore.svg images/drag.svg images/delet.svg images/shift_lock.svg images/rectangle.svg ukui-screensaver/src/VirtualKeyboard/src/charsmorewidget.cpp0000664000175000017500000002267615172041035023366 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "charsmorewidget.h" #include "commondef.h" #include #include #include #include #include #include CharsMoreWidget::CharsMoreWidget(QWidget *parent/* = nullptr*/) : QWidget(parent) { this->setAttribute(Qt::WA_TranslucentBackground);//背景透明 setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus); initUI(); } CharsMoreWidget::~CharsMoreWidget() { } void CharsMoreWidget::adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical/* = false*/, bool floatStatus) { QMap::iterator itGeometry = m_mapBtnGeometrys.begin(); for (; itGeometry != m_mapBtnGeometrys.end(); itGeometry ++) { QWidget *widget = itGeometry.key(); if (widget) { QRect oldGeometry = itGeometry.value(); QRect newGeometry = oldGeometry; if (floatStatus) { newGeometry.setX(oldGeometry.x()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } else { newGeometry.setX(oldGeometry.x()*lfWidthScale); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } widget->setGeometry(newGeometry); } } QChar chChars[] = {',', '.', '?', '!', '\'', ':', '~', '@', ';', '"', '/', '(', ')', '_', '+', '=', '`', '^', '#', '*', '%', '&', '\\', '[', ']', '<', '>', '{', '}', '|', '$', '-'}; for (int n = 0; n < sizeof(chChars)/sizeof(QChar); n++) { //单独更新符号btn的高度 QString objName = QString("btn_%1").arg(QString(chChars[n])); KBButton *btn = findChild(objName); btn->setFixedHeight(KEYBOARD_FIXED_DEFAULT_NORMAL_CHARSMORE_BTN_HEIGHT *lfHeightScale); } //更新listfarame的高度 listFrame->setFixedHeight(KEYBOARD_FIXED_DEFAULT_NORMAL_CHARSMORE_BTN_HEIGHT *m_vlayoutBtnList->count() *lfHeightScale); } void CharsMoreWidget::onBtnClicked(QChar charId) { QObject *obj = sender(); KBButton *btn = static_cast(obj); QString objName = btn->objectName(); int lastUnderline = objName.lastIndexOf('_'); int start = strnlen("btn_", 5); int keyLength = lastUnderline - start; QString keyName = objName.mid(start, keyLength); if (keyName == BTN_RETURN) { Q_EMIT specialBtnClicked(PAGE_CHAR); } else if (charId != QChar::Null) { Q_EMIT normalBtnClicked(charId); } else { Q_EMIT specialBtnClicked(keyName); } } void CharsMoreWidget::initUI() { // all chars m_vlayoutBtnList = new QVBoxLayout(); m_vlayoutBtnList->setContentsMargins(8,0,0,0); m_vlayoutBtnList->setSpacing(1); listFrame = new QFrame(); listFrame->setLayout(m_vlayoutBtnList); listFrame->setStyleSheet("QFrame{border-radius: 8px}"); m_scrollFrame = new QScrollArea(this); m_scrollFrame->setContentsMargins(0, 0, 0, 0); m_scrollFrame->setFocusPolicy(Qt::NoFocus); m_scrollFrame->setStyleSheet("QScrollArea {background-color: #C0FFFFFF; border-radius:8px;}"); m_scrollFrame->viewport()->setStyleSheet("background-color:transparent;"); m_scrollFrame->verticalScrollBar()->setProperty("drawScrollBarGroove", false); m_scrollFrame->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_scrollFrame->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); m_scrollFrame->setWidgetResizable(true); m_scrollFrame->setWidget(listFrame); m_scrollFrame->setGeometry(KEYBOARD_FIXED_DEFAULT_CHARSMORE_L1, 0, KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_WIDTH, KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_HEIGHT); listFrame->setGeometry(KEYBOARD_FIXED_DEFAULT_CHARSMORE_L1, 0, KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_WIDTH, KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_HEIGHT); m_mapBtnGeometrys[m_scrollFrame] = m_scrollFrame->geometry(); m_mapBtnGeometrys[listFrame] = listFrame->geometry(); QChar chChars[] = {',', '.', '?', '!', '\'', ':', '~', '@', ';', '"', '/', '(', ')', '_', '+', '=', '`', '^', '#', '*', '%', '&', '\\', '[', ']', '<', '>', '{', '}', '|', '$', '-', ' ', ' ', ' ', ' '}; QHBoxLayout *lastLayout = nullptr; for (int n = 0; n < sizeof(chChars)/sizeof(QChar); n++) { KBButton *charBtn = new KBButton(listFrame); charBtn->setCharId(chChars[n]); charBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_NORMAL, KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_FONT_COLOR_PRESS, KBButton::BORDER_RADIUS_NONE); if ((n%KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_COLS) == 0) { lastLayout = new QHBoxLayout(); lastLayout->setContentsMargins(0,0,0,0); lastLayout->setSpacing(1); m_vlayoutBtnList->addLayout(lastLayout); } if (chChars[n] == ' ') { charBtn->setDisabled(true); } else { charBtn->setObjectName(QString("btn_%1").arg(QString(chChars[n]))); } charBtn->setFixedHeight(KEYBOARD_FIXED_DEFAULT_NORMAL_CHARSMORE_BTN_HEIGHT); lastLayout->addWidget(charBtn); m_mapSubWidgetListRects[charBtn] = charBtn->geometry(); connect(charBtn, &KBButton::clicked, this, &CharsMoreWidget::onBtnClicked); } // backspace KBButton *backspaceBtn = new KBButton(this); backspaceBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_CHARSMORE_L1+(KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_WIDTH+KEYBOARD_FIXED_DEFAULT_CHARSMORE_HSPACING), 0, KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_WIDTH, KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_HEIGHT); backspaceBtn->setObjectName("btn_backspace"); backspaceBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); backspaceBtn->setIcon(QIcon(":/images/images/delet.svg")); m_mapBtnGeometrys[backspaceBtn] = backspaceBtn->geometry(); connect(backspaceBtn, &KBButton::clicked, this, &CharsMoreWidget::onBtnClicked); // enter KBButton *enterBtn = new KBButton(this); enterBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_CHARSMORE_L1+(KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_WIDTH+KEYBOARD_FIXED_DEFAULT_CHARSMORE_HSPACING), (KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_HEIGHT+KEYBOARD_FIXED_DEFAULT_CHARSMORE_VSPACING)*1, KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_WIDTH, KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_HEIGHT); enterBtn->setObjectName("btn_enter"); enterBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); enterBtn->setIcon(QIcon(":/images/images/enter.svg")); connect(enterBtn, &KBButton::clicked, this, &CharsMoreWidget::onBtnClicked); m_mapBtnGeometrys[enterBtn] = enterBtn->geometry(); // return KBButton *returnBtn = new KBButton(this); returnBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_CHARSMORE_L1+(KEYBOARD_FIXED_DEFAULT_CHARSMORE_LIST_WIDTH+KEYBOARD_FIXED_DEFAULT_CHARSMORE_HSPACING), (KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_HEIGHT+KEYBOARD_FIXED_DEFAULT_CHARSMORE_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_WIDTH, KEYBOARD_FIXED_DEFAULT_CHARSMORE_BTN_HEIGHT); returnBtn->setObjectName("btn_return"); returnBtn->setText(tr("&&?!")); returnBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(returnBtn, &KBButton::clicked, this, &CharsMoreWidget::onBtnClicked); m_mapBtnGeometrys[returnBtn] = returnBtn->geometry(); } ukui-screensaver/src/VirtualKeyboard/src/virtualkeyboardwidget.cpp0000664000175000017500000003105415172041106024577 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "virtualkeyboardwidget.h" #include #include #include #include #include "commondef.h" #include #include #include #include #include "dragwidget.h" #include "kbtitle.h" #include "letterswidget.h" #include "numberswidget.h" #include "charswidget.h" #include "charsmorewidget.h" #include "x11keyboard.h" #include "qtkeyboard.h" #include VirtualKeyboardWidget::VirtualKeyboardWidget(QWidget *parent) : QWidget(parent), m_lfWidthScale(1.0), m_lfHeightScale(1.0), m_isVertical(false) { Q_INIT_RESOURCE(keyboard); // setAttribute(Qt::WA_TranslucentBackground);//背景透明 // setAutoFillBackground(true); setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus); if (QApplication::platformName() == "xcb") { vKeyboard = new X11Keyboard(this); } else { vKeyboard = new QtKeyboard(this); } connect(this, SIGNAL(keyPressed(QChar)), vKeyboard, SLOT(onKeyPressed(QChar))); connect(this, SIGNAL(keyPressed(FuncKey::FUNCKEY)), vKeyboard, SLOT(onKeyPressed(FuncKey::FUNCKEY))); initUI(); qApp->installNativeEventFilter(this); initConnections(); } VirtualKeyboardWidget::~VirtualKeyboardWidget() {} bool VirtualKeyboardWidget::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) { if (qstrcmp(eventType, "xcb_generic_event_t") != 0) { return false; } xcb_generic_event_t *event = reinterpret_cast(message); const uint8_t responseType = event->response_type & ~0x80; if (responseType == XCB_KEY_PRESS) { xcb_key_press_event_t *xc = reinterpret_cast(event); } else if (responseType == XCB_KEY_RELEASE) { xcb_key_release_event_t *xc = reinterpret_cast(event); if (xc->detail == 66 /*&& QString(qgetenv("XDG_SESSION_TYPE")) == "wayland"*/) { m_lettersWidget->onCapsChanged(); } } return false; } void VirtualKeyboardWidget::initUI() { QVBoxLayout *layoutMain = new QVBoxLayout(this); layoutMain->setContentsMargins(0, 0, 0, 0); layoutMain->setSpacing(0); m_dragWidget = new DragWidget(); layoutMain->addWidget(m_dragWidget); m_dragWidget->installEventFilter(this); m_kbTitle = new KBTitle(); layoutMain->addWidget(m_kbTitle); m_stackedWidget = new QStackedWidget(); m_lettersWidget = new LettersWidget(); m_stackedWidget->addWidget(m_lettersWidget); m_numbersWidget = new NumbersWidget(this); m_stackedWidget->addWidget(m_numbersWidget); m_charsWidget = new CharsWidget(); m_stackedWidget->addWidget(m_charsWidget); m_charsMoreWidget = new CharsMoreWidget(); m_stackedWidget->addWidget(m_charsMoreWidget); m_stackedWidget->setCurrentIndex(VKB_PAGE_LETTERS); m_nCurPage = VKB_PAGE_CHARSMORE; layoutMain->addWidget(m_stackedWidget); } void VirtualKeyboardWidget::initConnections() { connect(m_kbTitle, &KBTitle::btnClicked, this, &VirtualKeyboardWidget::onSpecialBtnClicked); connect(m_lettersWidget, &LettersWidget::specialBtnClicked, this, &VirtualKeyboardWidget::onSpecialBtnClicked); connect(m_lettersWidget, &LettersWidget::normalBtnClicked, this, &VirtualKeyboardWidget::onNormalBtnClicked); connect(m_numbersWidget, &NumbersWidget::specialBtnClicked, this, &VirtualKeyboardWidget::onSpecialBtnClicked); connect(m_numbersWidget, &NumbersWidget::narmalBtnClicked, this, &VirtualKeyboardWidget::onNormalBtnClicked); connect(m_charsWidget, &CharsWidget::specialBtnClicked, this, &VirtualKeyboardWidget::onSpecialBtnClicked); connect(m_charsWidget, &CharsWidget::normalBtnClicked, this, &VirtualKeyboardWidget::onNormalBtnClicked); connect(m_charsMoreWidget, &CharsMoreWidget::specialBtnClicked, this, &VirtualKeyboardWidget::onSpecialBtnClicked); connect(m_charsMoreWidget, &CharsMoreWidget::normalBtnClicked, this, &VirtualKeyboardWidget::onNormalBtnClicked); } void VirtualKeyboardWidget::adjustGeometry() { QWidget *parentWidget = qobject_cast(parent()); if (parentWidget) { // qDebug()<< "parent: " << parentWidget <<"Parent gemotry:"<geometry(); double lfWidth = parentWidget->geometry().width(); double lfHeight = parentWidget->geometry().height(); m_isVertical = lfHeight > lfWidth; m_lfWidthScale = lfWidth / KEYBOARD_PARENT_DEFAULT_WIDTH; if (m_isVertical) m_lfHeightScale = lfHeight / KEYBOARD_PARENT_DEFAULT_WIDTH; else m_lfHeightScale = lfHeight / KEYBOARD_PARENT_DEFAULT_HEIGHT; if (m_isdragState) { lfWidth = m_lfWidthScale * KEYBOARD_DRAGSHOW_FIXED_DEFAULT_WIDTH; lfHeight = m_lfHeightScale * KEYBOARD_DRAGSHOW_FIXED_DEFAULT_HEIGHT; setGeometry(QRect( m_lfWidthScale * (KEYBOARD_PARENT_DEFAULT_WIDTH - KEYBOARD_DRAGSHOW_FIXED_DEFAULT_WIDTH) / 2, parentWidget->geometry().height() - lfHeight, lfWidth, lfHeight)); m_dragWidget->show(); } else { lfWidth = m_lfWidthScale * KEYBOARD_FIXED_DEFAULT_WIDTH; lfHeight = m_lfHeightScale * KEYBOARD_DRAGHIDE_FIXED_DEFAULT_HEIGHT; m_dragWidget->hide(); setGeometry(QRect(0, parentWidget->geometry().height() - lfHeight, lfWidth, lfHeight)); } // qDebug()<<"Widget geometry:"<show(); } else { setGeometry(QRect(0, 0, KEYBOARD_FIXED_DEFAULT_WIDTH, KEYBOARD_DRAGHIDE_FIXED_DEFAULT_HEIGHT)); m_dragWidget->hide(); } m_lfWidthScale = 1.0; m_lfHeightScale = 1.0; m_isVertical = false; } if (m_dragWidget) { m_dragWidget->adjustGeometry(m_lfWidthScale, m_lfHeightScale, m_isVertical, m_isdragState); } if (m_kbTitle) { m_kbTitle->adjustGeometry(m_lfWidthScale, m_lfHeightScale, m_isVertical, m_isdragState); } if (m_lettersWidget) { m_lettersWidget->adjustGeometry(m_lfWidthScale, m_lfHeightScale, m_isVertical, m_isdragState); } if (m_numbersWidget) { m_numbersWidget->adjustGeometry(m_lfWidthScale, m_lfHeightScale, m_isVertical, m_isdragState); } if (m_charsWidget) { m_charsWidget->adjustGeometry(m_lfWidthScale, m_lfHeightScale, m_isVertical, m_isdragState); } if (m_charsMoreWidget) { m_charsMoreWidget->adjustGeometry(m_lfWidthScale, m_lfHeightScale, m_isVertical, m_isdragState); } } bool VirtualKeyboardWidget::eventFilter(QObject *watched, QEvent *event) { if (watched != m_dragWidget && !isMove) return QWidget::eventFilter(watched, event); switch (event->type()) { case QEvent::MouseButtonPress: { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent) { onMouseEvents(1, mouseEvent->pos()); } } return true; case QEvent::MouseMove: { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent) { onMouseEvents(2, mouseEvent->pos()); } } return true; case QEvent::MouseButtonRelease: { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent) { onMouseEvents(3, mouseEvent->pos()); } } return true; default: break; } return QWidget::eventFilter(watched, event); } void VirtualKeyboardWidget::resizeEvent(QResizeEvent *event) { adjustGeometry(); } void VirtualKeyboardWidget::paintEvent(QPaintEvent *event) { QPainterPath path; QPainter painter(this); painter.setOpacity(1.0); painter.setRenderHint(QPainter::Antialiasing); // 反锯齿; painter.setClipping(true); painter.setPen(Qt::transparent); if (m_isdragState) { path.addRoundedRect(this->rect(), 16, 16); } else { path.addRoundedRect(this->rect(), 0, 0); } path.setFillRule(Qt::WindingFill); painter.setBrush(QColor("#EBEDEF")); painter.setPen(Qt::transparent); painter.drawPath(path); QWidget::paintEvent(event); } void VirtualKeyboardWidget::onSpecialBtnClicked(QString keyName) { Modifier::MOD mod = Modifier::getModifier(keyName); FuncKey::FUNCKEY funcKey = FuncKey::getKey(keyName); if (mod != Modifier::UNKNOWN) { if (vKeyboard->hasModifier(mod)) { vKeyboard->removeModifier(mod); m_lettersWidget->changeFuncKeyStyle(keyName, false); } else { vKeyboard->addModifier(mod); m_lettersWidget->changeFuncKeyStyle(keyName, true); } if (keyName == BTN_CAPSLOCK) { Q_EMIT keyPressed(FuncKey::CAPSLOCK); clearModifier(); } } else if (funcKey != FuncKey::UNKNOWN) { Q_EMIT keyPressed(funcKey); } else if (keyName == PAGE_CHARSMORE) { m_stackedWidget->setCurrentIndex(VKB_PAGE_CHARSMORE); update(); } else if (keyName == PAGE_CHAR) { m_stackedWidget->setCurrentIndex(VKB_PAGE_CHARS); update(); } else if (keyName == PAGE_NUMBER) { m_stackedWidget->setCurrentIndex(VKB_PAGE_NUMBERS); update(); } else if (keyName == PAGE_LETTER) { m_stackedWidget->setCurrentIndex(VKB_PAGE_LETTERS); update(); } else if (keyName == BTN_FLOAT) { m_isdragState = !m_isdragState; adjustGeometry(); Q_EMIT aboutToFloat(); } else if (keyName == BTN_CLOSE) { Q_EMIT aboutToClose(); } } void VirtualKeyboardWidget::onNormalBtnClicked(QChar c) { Q_EMIT keyPressed(c); clearModifier(); } void VirtualKeyboardWidget::clearModifier() { for (auto mod : vKeyboard->getAllModifier()) { QString modName = Modifier::getModifierName(mod); m_lettersWidget->changeFuncKeyStyle(modName, false); } vKeyboard->clearModifier(); } void VirtualKeyboardWidget::onMouseEvents(int type, const QPoint &pos) { QPoint globalPos = mapToGlobal(pos); switch (type) { case 1: { isMove = true; lastPoint = globalPos; break; } case 2: { if (isMove) { QPoint cPoint = globalPos; QPoint p = this->pos(); p.setX(p.x() - lastPoint.x() + cPoint.x()); p.setY(p.y() - lastPoint.y() + cPoint.y()); lastPoint = cPoint; move(p); } break; } case 3: { // 把虚拟键盘坐标检测放在release事件处理中,如果虚拟键盘坐标超出了屏幕, // 则在收到release事件后把坐标设回来 QPoint p = this->pos(); int maxWidth; int maxHeight; if (this->parentWidget()) { maxWidth = this->parentWidget()->width(); maxHeight = this->parentWidget()->height(); } else { QScreen *screen = qApp->primaryScreen(); maxWidth = screen->geometry().width(); maxHeight = screen->geometry().height(); } if (this->x() > maxWidth - 10) { p.setX(maxWidth - 10); } else if (this->x() + this->width() < 10) { p.setX(10 - this->width()); } if (this->y() > maxHeight - 10) { p.setY(maxHeight - 10); } else if (this->y() + m_dragWidget->height() < 10) { p.setY(0); } move(p); isMove = false; break; } default: break; } } bool VirtualKeyboardWidget::getFloatStatus() { return m_isdragState; } ukui-screensaver/src/VirtualKeyboard/src/kbtitle.cpp0000664000175000017500000001125715172041035021626 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "kbtitle.h" #include #include #include "commondef.h" #include KBTitle::KBTitle(QWidget *parent /* = nullptr*/) : QWidget(parent) { setAttribute(Qt::WA_TranslucentBackground); //背景透明 initUI(); initConnections(); } KBTitle::~KBTitle() {} void KBTitle::initUI() { setFixedHeight(KEYBOARD_TITLE_DEFAULT_HEIGHT); QString strBtnStyle = "QPushButton{ text-align:center; color: rgba(255, 255, 255, 255); border: none; " "border-radius: 4px; outline: none;}" "QPushButton:hover{ background-color: rgba(255,255,255,15%); }" "QPushButton::pressed { background-color: rgba(255,255,255,40%); }" "QPushButton::checked { background-color: rgba(255,255,255,40%); }"; m_btnFloat = new QPushButton(this); m_btnFloat->setFlat(true); m_btnFloat->setFocusPolicy(Qt::NoFocus); m_btnFloat->setIcon(QIcon(":/images/images/float.svg")); m_btnFloat->setToolTip(tr("Suspended state")); m_btnFloat->setObjectName("btn_float"); m_btnFloat->setIconSize(KEYBOARD_FIXED_DEFAULT_ICONSIZE); m_btnFloat->setGeometry(1484, 8, KEYBOARD_TITLEBTN_DEFAULT_WIDTH, KEYBOARD_TITLEBTN_DEFAULT_HEIGHT); m_btnFloat->setStyleSheet(strBtnStyle); m_mapSubGeometrys[m_btnFloat] = m_btnFloat->geometry(); m_btnClose = new QPushButton(this); m_btnClose->setFlat(true); m_btnClose->setFocusPolicy(Qt::NoFocus); m_btnClose->setIcon(QIcon(":/images/images/close.svg")); m_btnClose->setToolTip(tr("Close")); m_btnClose->setIconSize(KEYBOARD_FIXED_DEFAULT_ICONSIZE); m_btnClose->setObjectName("btn_close"); m_btnClose->setGeometry(1548, 8, KEYBOARD_TITLEBTN_DEFAULT_WIDTH, KEYBOARD_TITLEBTN_DEFAULT_HEIGHT); m_btnClose->setStyleSheet(strBtnStyle); m_mapSubGeometrys[m_btnClose] = m_btnClose->geometry(); } void KBTitle::initConnections() { connect(m_btnFloat, &QPushButton::clicked, this, &KBTitle::onBtnClicked); connect(m_btnClose, &QPushButton::clicked, this, &KBTitle::onBtnClicked); } void KBTitle::onBtnClicked() { QObject *obj = sender(); QPushButton *btn = static_cast(obj); QString objName = btn->objectName(); int lastUnderline = objName.lastIndexOf('_'); int start = strnlen("btn_", 5); int keyLength = lastUnderline - start; QString keyName = objName.mid(start, keyLength); if (keyName == BTN_FLOAT) { if (floatStatus) { btn->setIcon(QIcon(":/images/images/float.svg")); btn->setToolTip(tr("Suspended state")); } else { btn->setIcon(QIcon(":/images/images/float-restore.svg")); btn->setToolTip(tr("Welt status")); } floatStatus = !floatStatus; } Q_EMIT btnClicked(keyName); } void KBTitle::adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical /* = false*/, bool floatStatus) { setFixedHeight(KEYBOARD_TITLE_DEFAULT_HEIGHT * lfHeightScale); QMap::iterator itGeometry = m_mapSubGeometrys.begin(); for (; itGeometry != m_mapSubGeometrys.end(); itGeometry++) { QPushButton *button = itGeometry.key(); if (button) { QRect oldGeometry = itGeometry.value(); QRect newGeometry = oldGeometry; if (floatStatus) { newGeometry.setX(oldGeometry.x() * lfWidthScale * KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setY(oldGeometry.y() * lfHeightScale); newGeometry.setWidth(oldGeometry.width() * lfWidthScale * KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setHeight(oldGeometry.height() * lfHeightScale); } else { newGeometry.setX(oldGeometry.x() * lfWidthScale); newGeometry.setY(oldGeometry.y() * lfHeightScale); newGeometry.setWidth(oldGeometry.width() * lfWidthScale); newGeometry.setHeight(oldGeometry.height() * lfHeightScale); } button->setGeometry(newGeometry); } } } ukui-screensaver/src/VirtualKeyboard/src/qxkbcommon.cpp0000664000175000017500000011327015172041106022343 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui 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$ ** ****************************************************************************/ #include "qxkbcommon_p.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcXkbcommon, "qt.xkbcommon") static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code, bool superAsMeta, bool hyperAsMeta); typedef struct xkb2qt { unsigned int xkb; unsigned int qt; constexpr bool operator<=(const xkb2qt &that) const noexcept { return xkb <= that.xkb; } constexpr bool operator<(const xkb2qt &that) const noexcept { return xkb < that.xkb; } } xkb2qt_t; template struct Xkb2Qt { using Type = xkb2qt_t; static constexpr Type data() noexcept { return Type{Xkb, Qt}; } }; static constexpr const auto KeyTbl = qMakeArray( QSortedData< // misc keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq // cursor movement Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // modifiers Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11) Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12) // numeric and function keypad keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // special non-XF86 function keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // International input method support keys // International & multi-key character composition Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Misc Functions Xkb2Qt, Xkb2Qt, // Japanese keyboard support Xkb2Qt, Xkb2Qt, // Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Xkb2Qt, // Xkb2Qt, // Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Korean keyboard support Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Xkb2Qt, // Xkb2Qt, // Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Xkb2Qt, Xkb2Qt, // dead keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, /* The following four XKB_KEY_dead keys got removed in libxkbcommon 1.6.0 The define check is kind of version check here. */ #ifdef XKB_KEY_dead_lowline Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, #endif // Special keys from X.org - This include multimedia keys, // wireless/bluetooth/uwb keys, special launcher keys, etc. Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // ### Qt 6: remap properly Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // ### Qt 6: remap properly Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt/*, Xkb2Qt*/>::Data{}); xkb_keysym_t QXkbCommon::qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks) { xkb_keysym_t lower, upper; xkbcommon_XConvertCase(ks, &lower, &upper); return upper; } QString QXkbCommon::lookupString(struct xkb_state *state, xkb_keycode_t code) { QVarLengthArray chars(32); const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL chars.resize(size + 1); xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); } return QString::fromUtf8(chars.constData(), size); } QString QXkbCommon::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) { QVarLengthArray chars(32); const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); if (size == 0) return QString(); // the keysym does not have a Unicode representation if (Q_UNLIKELY(size > chars.size())) { chars.resize(size); xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); } return QString::fromUtf8(chars.constData(), size - 1); } QVector QXkbCommon::toKeysym(QKeyEvent *event) { QVector keysyms; int qtKey = event->key(); if (qtKey >= Qt::Key_F1 && qtKey <= Qt::Key_F35) { keysyms.append(XKB_KEY_F1 + (qtKey - Qt::Key_F1)); } else if (event->modifiers() & Qt::KeypadModifier) { if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9) keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0)); } else if (isLatin1(qtKey) && event->text().isUpper()) { keysyms.append(qtKey); } if (!keysyms.isEmpty()) return keysyms; // check if we have a direct mapping auto it = std::find_if(KeyTbl.cbegin(), KeyTbl.cend(), [&qtKey](xkb2qt_t elem) { return elem.qt == static_cast(qtKey); }); if (it != KeyTbl.end()) { keysyms.append(it->xkb); return keysyms; } QVector ucs4; if (event->text().isEmpty()) ucs4.append(qtKey); else ucs4 = event->text().toUcs4(); // From libxkbcommon keysym-utf.c: // "We allow to represent any UCS character in the range U-00000000 to // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff." for (uint utf32 : qAsConst(ucs4)) keysyms.append(utf32 | 0x01000000); return keysyms; } int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers) { return keysymToQtKey(keysym, modifiers, nullptr, 0); } int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code, bool superAsMeta, bool hyperAsMeta) { // Note 1: All standard key sequences on linux (as defined in platform theme) // that use a latin character also contain a control modifier, which is why // checking for Qt::ControlModifier is sufficient here. It is possible to // override QPlatformTheme::keyBindings() and provide custom sequences for // QKeySequence::StandardKey. Custom sequences probably should respect this // convention (alternatively, we could test against other modifiers here). // Note 2: The possibleKeys() shorcut mechanism is not affected by this value // adjustment and does its own thing. if (modifiers & Qt::ControlModifier) { // With standard shortcuts we should prefer a latin character, this is // for checks like "some qkeyevent == QKeySequence::Copy" to work even // when using for example 'russian' keyboard layout. if (!QXkbCommon::isLatin1(keysym)) { xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, code); if (latinKeysym != XKB_KEY_NoSymbol) keysym = latinKeysym; } } return keysymToQtKey_internal(keysym, modifiers, state, code, superAsMeta, hyperAsMeta); } static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code, bool superAsMeta, bool hyperAsMeta) { int qtKey = 0; // lookup from direct mapping if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) { // function keys qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1); } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { // numeric keypad keys qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0); } else if (QXkbCommon::isLatin1(keysym)) { qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym); } else { // check if we have a direct mapping xkb2qt_t searchKey{keysym, 0}; auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey); if (it != KeyTbl.end() && !(searchKey < *it)) qtKey = it->qt; } if (qtKey) return qtKey; // lookup from unicode QString text; if (!state || modifiers & Qt::ControlModifier) { // Control modifier changes the text to ASCII control character, therefore we // can't use this text to map keysym to a qt key. We can use the same keysym // (it is not affectd by transformation) to obtain untransformed text. For details // see "Appendix A. Default Symbol Transformations" in the XKB specification. text = QXkbCommon::lookupStringNoKeysymTransformations(keysym); } else { text = QXkbCommon::lookupString(state, code); } if (!text.isEmpty()) { if (text.unicode()->isDigit()) { // Ensures that also non-latin digits are mapped to corresponding qt keys, // e.g CTRL + ۲ (arabic two), is mapped to CTRL + Qt::Key_2. qtKey = Qt::Key_0 + text.unicode()->digitValue(); } else { text = text.toUpper(); QStringIterator i(text); qtKey = i.next(0); } } // translate Super/Hyper keys to Meta if we're using them as the MetaModifier if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) qtKey = Qt::Key_Meta; if (hyperAsMeta && (qtKey == Qt::Key_Hyper_L || qtKey == Qt::Key_Hyper_R)) qtKey = Qt::Key_Meta; return qtKey; } Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state) { Qt::KeyboardModifiers modifiers = Qt::NoModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::ControlModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::AltModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::ShiftModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::MetaModifier; return modifiers; } // Possible modifier states. static const Qt::KeyboardModifiers ModsTbl[] = { Qt::NoModifier, // 0 Qt::ShiftModifier, // 1 Qt::ControlModifier, // 2 Qt::ControlModifier | Qt::ShiftModifier, // 3 Qt::AltModifier, // 4 Qt::AltModifier | Qt::ShiftModifier, // 5 Qt::AltModifier | Qt::ControlModifier, // 6 Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts }; QList QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event, bool superAsMeta, bool hyperAsMeta) { QList result; quint32 keycode = event->nativeScanCode(); Qt::KeyboardModifiers modifiers = event->modifiers(); xkb_keymap *keymap = xkb_state_get_keymap(state); // turn off the modifier bits which doesn't participate in shortcuts Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier; modifiers &= ~notNeeded; // create a fresh kb state and test against the relevant modifier combinations ScopedXKBState scopedXkbQueryState(xkb_state_new(keymap)); xkb_state *queryState = scopedXkbQueryState.get(); if (!queryState) { qCWarning(lcXkbcommon) << Q_FUNC_INFO << "failed to compile xkb keymap"; return result; } // get kb state from the master state and update the temporary state xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_LOCKED); xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_DEPRESSED); xkb_state_update_mask(queryState, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout); // handle shortcuts for level three and above xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(queryState, keycode); xkb_level_index_t levelIndex = 0; if (layoutIndex != XKB_LAYOUT_INVALID) { levelIndex = xkb_state_key_get_level(queryState, keycode, layoutIndex); if (levelIndex == XKB_LEVEL_INVALID) levelIndex = 0; } if (levelIndex <= 1) xkb_state_update_mask(queryState, 0, latchedMods, lockedMods, 0, 0, lockedLayout); xkb_keysym_t sym = xkb_state_key_get_one_sym(queryState, keycode); if (sym == XKB_KEY_NoSymbol) return result; int baseQtKey = keysymToQtKey_internal(sym, modifiers, queryState, keycode, superAsMeta, hyperAsMeta); if (baseQtKey) result += (baseQtKey + (int)(modifiers)); xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(keymap, "Shift"); xkb_mod_index_t altMod = xkb_keymap_mod_get_index(keymap, "Alt"); xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(keymap, "Control"); xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(keymap, "Meta"); Q_ASSERT(shiftMod < 32); Q_ASSERT(altMod < 32); Q_ASSERT(controlMod < 32); xkb_mod_mask_t depressed; int qtKey = 0; // obtain a list of possible shortcuts for the given key event for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl); ++i) { Qt::KeyboardModifiers neededMods = ModsTbl[i]; if ((modifiers & neededMods) == neededMods) { if (i == 8) { if (isLatin1(baseQtKey)) continue; // add a latin key as a fall back key sym = lookupLatinKeysym(state, keycode); } else { depressed = 0; if (neededMods & Qt::AltModifier) depressed |= (1 << altMod); if (neededMods & Qt::ShiftModifier) depressed |= (1 << shiftMod); if (neededMods & Qt::ControlModifier) depressed |= (1 << controlMod); if (metaMod < 32 && neededMods & Qt::MetaModifier) depressed |= (1 << metaMod); xkb_state_update_mask(queryState, depressed, latchedMods, lockedMods, 0, 0, lockedLayout); sym = xkb_state_key_get_one_sym(queryState, keycode); } if (sym == XKB_KEY_NoSymbol) continue; Qt::KeyboardModifiers mods = modifiers & ~neededMods; qtKey = keysymToQtKey_internal(sym, mods, queryState, keycode, superAsMeta, hyperAsMeta); if (!qtKey || qtKey == baseQtKey) continue; // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +, // but Ctrl++ is more specific than +, so we should skip the last one bool ambiguous = false; for (int shortcut : qAsConst(result)) { if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) { ambiguous = true; break; } } if (ambiguous) continue; result += (qtKey + (int)(mods)); } } return result; } void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap) { const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(keymap); const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); const xkb_keysym_t *keysyms = nullptr; int nrLatinKeys = 0; for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) { for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &keysyms); if (keysyms && isLatin1(keysyms[0])) nrLatinKeys++; if (nrLatinKeys > 10) // arbitrarily chosen threshold return; } } // This means that lookupLatinKeysym() will not find anything and latin // key shortcuts might not work. This is a bug in the affected desktop // environment. Usually can be solved via system settings by adding e.g. 'us' // layout to the list of seleced layouts, or by using command line, "setxkbmap // -layout rus,en". The position of latin key based layout in the list of the // selected layouts is irrelevant. Properly functioning desktop environments // handle this behind the scenes, even if no latin key based layout has been // explicitly listed in the selected layouts. qCDebug(lcXkbcommon, "no keyboard layouts with latin keys present"); } xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode) { xkb_layout_index_t layout; xkb_keysym_t sym = XKB_KEY_NoSymbol; xkb_keymap *keymap = xkb_state_get_keymap(state); const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, keycode); const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, keycode); // Look at user layouts in the order in which they are defined in system // settings to find a latin keysym. for (layout = 0; layout < layoutCount; ++layout) { if (layout == currentLayout) continue; const xkb_keysym_t *syms = nullptr; xkb_level_index_t level = xkb_state_key_get_level(state, keycode, layout); if (xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms) != 1) continue; if (isLatin1(syms[0])) { sym = syms[0]; break; } } if (sym == XKB_KEY_NoSymbol) return sym; xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); // Check for uniqueness, consider the following setup: // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active). // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+, // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired // then the obtained key is not unique. This prevents ctrl+ from generating a ctrl+q // shortcut in the above described setup. We don't want ctrl+ and ctrl+ to // generate the same shortcut event in this case. const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); ScopedXKBState queryState(xkb_state_new(keymap)); for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) { xkb_state_update_mask(queryState.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout); for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { xkb_keysym_t prevSym = xkb_state_key_get_one_sym(queryState.get(), code); if (prevSym == sym) { sym = XKB_KEY_NoSymbol; break; } } } return sym; } void QXkbCommon::setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context) { if (!inputContext || !context) return; const char *const inputContextClassName = "QComposeInputContext"; const char *const normalizedSignature = "setXkbContext(xkb_context*)"; if (inputContext->objectName() != QLatin1String(inputContextClassName)) return; static const QMetaMethod setXkbContext = [&]() { int methodIndex = inputContext->metaObject()->indexOfMethod(normalizedSignature); QMetaMethod method = inputContext->metaObject()->method(methodIndex); Q_ASSERT(method.isValid()); if (!method.isValid()) qCWarning(lcXkbcommon) << normalizedSignature << "not found on" << inputContextClassName; return method; }(); if (!setXkbContext.isValid()) return; setXkbContext.invoke(inputContext, Qt::DirectConnection, Q_ARG(struct xkb_context *, context)); } QT_END_NAMESPACE ukui-screensaver/src/VirtualKeyboard/src/main.cpp0000664000175000017500000000210015172041035021077 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "virtualkeyboard.h" #include int main(int argc, char *argv[]) { // qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); QApplication a(argc, argv); VirtualKeyboard *keyboard = new VirtualKeyboard; QObject::connect(keyboard, &VirtualKeyboard::aboutToClose, &a, &QApplication::quit); keyboard->resize(400,300); keyboard->show(); return a.exec(); } ukui-screensaver/src/VirtualKeyboard/src/charswidget.cpp0000664000175000017500000003660315172041035022476 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "charswidget.h" #include "commondef.h" #include #include CharsWidget::CharsWidget(QWidget *parent/* = nullptr*/) : QWidget(parent) { this->setAttribute(Qt::WA_TranslucentBackground);//背景透明 initUI(); } CharsWidget::~CharsWidget() { } void CharsWidget::initUI() { // line 1 QChar chLine1[] = {'1','2','3','4','5','6','7','8','9','0'}; for (int n = 0; n < sizeof(chLine1)/sizeof(QChar); n++) { KBButton *charBtn = new KBButton(this); charBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L1+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*n, KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); charBtn->setCharId(chLine1[n]); charBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(charBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[charBtn] = charBtn->geometry(); } // backspace KBButton *backspaceBtn = new KBButton(this); backspaceBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L1+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*10, KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); backspaceBtn->setObjectName("btn_backspace"); backspaceBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(backspaceBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); backspaceBtn->setIcon(QIcon(":/images/images/delet.svg")); m_mapBtnGeometrys[backspaceBtn] = backspaceBtn->geometry(); // line 2 QChar chLine2[] = {'~','/',':',';','(',')','@','"','\''}; for (int n = 0; n < sizeof(chLine2)/sizeof(QChar); n++) { KBButton *charBtn = new KBButton(this); charBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L2+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*n, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING), KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); charBtn->setCharId(chLine2[n]); charBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(charBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[charBtn] = charBtn->geometry(); } // enter KBButton *enterBtn = new KBButton(this); enterBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L2+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*9, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING), KEYBOARD_FIXED_DEFAULT_ENTERBTN_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); enterBtn->setObjectName("btn_enter"); enterBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(enterBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); enterBtn->setIcon(QIcon(":/images/images/enter.svg")); m_mapBtnGeometrys[enterBtn] = enterBtn->geometry(); // line 3 QChar chLine3[] = {'-','_','#','%','$','+','^',',','.','!'}; for (int n = 0; n < sizeof(chLine3)/sizeof(QChar); n++) { KBButton *charBtn = new KBButton(this); charBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L3+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*(n+1), KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); charBtn->setCharId(chLine3[n]); charBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(charBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[charBtn] = charBtn->geometry(); } // more KBButton *moreBtn = new KBButton(this); moreBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L3, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); moreBtn->setObjectName("btn_more"); moreBtn->setText(tr("More")); moreBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(moreBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[moreBtn] = moreBtn->geometry(); // line 4 KBButton *returnBtn = new KBButton(this); returnBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); returnBtn->setObjectName("btn_return"); returnBtn->setText(tr("ABC")); returnBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(returnBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[returnBtn] = returnBtn->geometry(); KBButton *numBtn = new KBButton(this); numBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING), KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); numBtn->setObjectName("btn_number"); numBtn->setText(tr("123")); numBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(numBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[numBtn] = numBtn->geometry(); KBButton *threedBtn = new KBButton(this); threedBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*2, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); threedBtn->setObjectName("btn_threed"); threedBtn->setCharId(('&')); threedBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(threedBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[threedBtn] = threedBtn->geometry(); KBButton *spaceBtn = new KBButton(this); spaceBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*3, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_SPACEBTN_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); spaceBtn->setObjectName("btn_space"); spaceBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_BORDER_NORMAL, KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); spaceBtn->setIcon(QIcon(":/images/images/space.svg")); connect(spaceBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[spaceBtn] = spaceBtn->geometry(); KBButton *whBtn = new KBButton(this); whBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*8, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); whBtn->setCharId('?'); whBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(whBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[whBtn] = whBtn->geometry(); KBButton *leftBtn = new KBButton(this); leftBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*9, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); leftBtn->setObjectName("btn_left"); leftBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); leftBtn->setIcon(QIcon(":/images/images/left.svg")); connect(leftBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[leftBtn] = leftBtn->geometry(); KBButton *rightBtn = new KBButton(this); rightBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*10, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); rightBtn->setObjectName("btn_right"); rightBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); rightBtn->setIcon(QIcon(":/images/images/right.svg")); connect(rightBtn, &KBButton::clicked, this, &CharsWidget::onBtnClicked); m_mapBtnGeometrys[rightBtn] = rightBtn->geometry(); } void CharsWidget::adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical/* = false*/, bool floatStatus) { QMap::iterator itGeometry = m_mapBtnGeometrys.begin(); for (; itGeometry != m_mapBtnGeometrys.end(); itGeometry ++) { KBButton *button = itGeometry.key(); if (button) { QRect oldGeometry = itGeometry.value(); QRect newGeometry = oldGeometry; if (floatStatus) { newGeometry.setX(oldGeometry.x()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } else { newGeometry.setX(oldGeometry.x()*lfWidthScale); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } button->setGeometry(newGeometry); } } } void CharsWidget::onBtnClicked(QChar charId) { QObject *obj = sender(); KBButton *btn = static_cast(obj); QString objName = btn->objectName(); int lastUnderline = objName.lastIndexOf('_'); int start = strnlen("btn_", 5); int keyLength = lastUnderline - start; QString keyName = objName.mid(start, keyLength); if (keyName == BTN_RETURN) { Q_EMIT specialBtnClicked(PAGE_LETTER); } else if (keyName == "more") { Q_EMIT specialBtnClicked(PAGE_CHARSMORE); } else if (keyName == "number") { Q_EMIT specialBtnClicked(PAGE_NUMBER); } else if (charId != QChar::Null) { Q_EMIT normalBtnClicked(charId); } else { Q_EMIT specialBtnClicked(keyName); } } void CharsWidget::changeEvent(QEvent *event) { if(event->type() == QEvent::LanguageChange){ refreshTranslate(); } } void CharsWidget::refreshTranslate() { QMap::iterator itGeometry = m_mapBtnGeometrys.begin(); for (; itGeometry != m_mapBtnGeometrys.end(); itGeometry ++) { KBButton *button = itGeometry.key(); if (button) { if (button->objectName() == "btn_more") { button->setText(tr("More")); } } } } ukui-screensaver/src/VirtualKeyboard/src/qxkbcommon_p.h0000664000175000017500000001134415172041106022326 0ustar fengfeng/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui 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 QXKBCOMMON_P_H #define QXKBCOMMON_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 #include #include QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcXkbcommon) class QEvent; class QKeyEvent; class QPlatformInputContext; class QXkbCommon { public: static QString lookupString(struct xkb_state *state, xkb_keycode_t code); static QString lookupStringNoKeysymTransformations(xkb_keysym_t keysym); static QVector toKeysym(QKeyEvent *event); static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers); static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code, bool superAsMeta = false, bool hyperAsMeta = false); // xkbcommon_* API is part of libxkbcommon internals, with modifications as // desribed in the header of the implementation file. static void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper); static xkb_keysym_t qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks); static Qt::KeyboardModifiers modifiers(struct xkb_state *state); static QList possibleKeys(xkb_state *state, const QKeyEvent *event, bool superAsMeta = false, bool hyperAsMeta = false); static void verifyHasLatinLayout(xkb_keymap *keymap); static xkb_keysym_t lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode); static bool isLatin1(xkb_keysym_t sym) { return sym <= 0xff; } static bool isKeypad(xkb_keysym_t sym) { return sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9; } static void setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context); struct XKBStateDeleter { void operator()(struct xkb_state *state) const { return xkb_state_unref(state); } }; struct XKBKeymapDeleter { void operator()(struct xkb_keymap *keymap) const { return xkb_keymap_unref(keymap); } }; struct XKBContextDeleter { void operator()(struct xkb_context *context) const { return xkb_context_unref(context); } }; using ScopedXKBState = std::unique_ptr; using ScopedXKBKeymap = std::unique_ptr; using ScopedXKBContext = std::unique_ptr; }; QT_END_NAMESPACE #endif // QXKBCOMMON_P_H ukui-screensaver/src/VirtualKeyboard/src/numberswidget.cpp0000664000175000017500000004206715172041035023052 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "numberswidget.h" #include "kbbutton.h" #include "commondef.h" #include #include #include #include #include #include #include NumbersWidget::NumbersWidget(QWidget *parent) : QWidget(parent) { this->setAttribute(Qt::WA_TranslucentBackground);//背景透明 initUI(); } NumbersWidget::~NumbersWidget() { } void NumbersWidget::initUI() { // all chars m_layoutBtnList = new QVBoxLayout(); m_layoutBtnList->setContentsMargins(8,0,0,0); m_layoutBtnList->setSpacing(1); listFrame = new QFrame(); listFrame->setObjectName("listFrame"); listFrame->setLayout(m_layoutBtnList); listFrame->setStyleSheet("QFrame{border-radius: 8px}"); listFrame->setFrameStyle(QFrame::Plain); m_scrollFrame = new QScrollArea(this); m_scrollFrame->setObjectName("scrollFrame"); m_scrollFrame->setFocusPolicy(Qt::NoFocus); m_scrollFrame->setContentsMargins(0, 0, 0, 0); m_scrollFrame->setStyleSheet("QScrollArea {background-color: #C0CED3D9; border-radius:8px;}"); m_scrollFrame->viewport()->setStyleSheet("background-color:transparent;"); m_scrollFrame->verticalScrollBar()->setProperty("drawScrollBarGroove", false); m_scrollFrame->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_scrollFrame->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); m_scrollFrame->setWidgetResizable(true); m_scrollFrame->setWidget(listFrame); m_scrollFrame->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L1, KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_CHARS_HEIGHT); listFrame->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L1, KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_CHARS_HEIGHT); m_mapBtnGeometrys[m_scrollFrame] = m_scrollFrame->geometry(); //m_mapBtnGeometrys[listFrame] = listFrame->geometry(); QChar chChars[] = {',', '.', '?', '!', '\'', ':', '~', '@', ';', '"', '/', '(', ')', '_', '+', '=', '`', '^', '#', '*', '%', '&', '\\', '[', ']', '<', '>', '{', '}', '|', '$', '-'}; for (int n = 0; n < sizeof(chChars)/sizeof(QChar); n++) { KBButton *charBtn = new KBButton(listFrame); charBtn->setCharId(chChars[n]); charBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_NORMAL, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS, KBButton::BORDER_RADIUS_NONE); charBtn->setObjectName(QString("btn_%1").arg(QString(chChars[n]))); connect(charBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); charBtn->setFixedHeight(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); m_layoutBtnList->addWidget(charBtn); } // line 1 QChar chLine1[] = {'1','2','3'}; for (int n = 0; n < sizeof(chLine1)/sizeof(QChar); n++) { KBButton *numberBtn = new KBButton(this); numberBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L1+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*(n+1), KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); numberBtn->setCharId(chLine1[n]); numberBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(numberBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[numberBtn] = numberBtn->geometry(); } // backspace KBButton *backspaceBtn = new KBButton(this); backspaceBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L1+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*4, KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); backspaceBtn->setObjectName("btn_backspace"); backspaceBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); backspaceBtn->setIcon(QIcon(":/images/images/delet.svg")); connect(backspaceBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[backspaceBtn] = backspaceBtn->geometry(); // line 2 QChar chLine2[] = {'4','5','6'}; for (int n = 0; n < sizeof(chLine2)/sizeof(QChar); n++) { KBButton *numberBtn = new KBButton(this); numberBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L2+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*(n+1), KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*1, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); numberBtn->setCharId(chLine2[n]); numberBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(numberBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[numberBtn] = numberBtn->geometry(); } // @ KBButton *atBtn = new KBButton(this); atBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L2+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*4, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*1, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); atBtn->setCharId('@'); atBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(atBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[atBtn] = atBtn->geometry(); // line 3 QChar chLine3[] = {'7','8','9'}; for (int n = 0; n < sizeof(chLine3)/sizeof(QChar); n++) { KBButton *numberBtn = new KBButton(this); numberBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L3+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*(n+1), KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); numberBtn->setCharId(chLine3[n]); numberBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(numberBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[numberBtn] = numberBtn->geometry(); } // symbol KBButton *symbolBtn = new KBButton(this); symbolBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L3+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*4, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); symbolBtn->setObjectName("btn_symbol"); symbolBtn->setText(tr("&&?!")); symbolBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(symbolBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[symbolBtn] = symbolBtn->geometry(); // line 4 KBButton *returnBtn = new KBButton(this); returnBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L4, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); returnBtn->setObjectName("btn_return"); returnBtn->setText(tr("Return")); returnBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(returnBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[returnBtn] = returnBtn->geometry(); // space KBButton *spaceBtn = new KBButton(this); spaceBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L4+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*1, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); spaceBtn->setObjectName("btn_space"); spaceBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_BORDER_NORMAL, KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); spaceBtn->setIcon(QIcon(":/images/images/space.svg")); connect(spaceBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[spaceBtn] = spaceBtn->geometry(); // . KBButton *dianBtn = new KBButton(this); dianBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L4+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*3, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); dianBtn->setCharId('.'); dianBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_BORDER_NORMAL, KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(dianBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[dianBtn] = dianBtn->geometry(); // 9 KBButton *num0Btn = new KBButton(this); num0Btn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L4+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*2, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); num0Btn->setCharId('0'); num0Btn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_BORDER_NORMAL, KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(num0Btn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[num0Btn] = num0Btn->geometry(); // enter KBButton *enterBtn = new KBButton(this); enterBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_NUMBER_L3+(KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*4, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_NUMBER_WIDTH, KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT); enterBtn->setObjectName("btn_enter"); enterBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); enterBtn->setIcon(QIcon(":/images/images/enter.svg")); connect(enterBtn, &KBButton::clicked, this, &NumbersWidget::onBtnClicked); m_mapBtnGeometrys[enterBtn] = enterBtn->geometry(); } void NumbersWidget::adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical/* = false*/, bool floatStatus) { QMap::iterator itGeometry = m_mapBtnGeometrys.begin(); for (; itGeometry != m_mapBtnGeometrys.end(); itGeometry ++) { QWidget *widget = itGeometry.key(); if (widget) { QRect oldGeometry = itGeometry.value(); QRect newGeometry = oldGeometry; if (floatStatus) { newGeometry.setX(oldGeometry.x()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } else { newGeometry.setX(oldGeometry.x()*lfWidthScale); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } widget->setGeometry(newGeometry); } } QChar chChars[] = {',', '.', '?', '!', '\'', ':', '~', '@', ';', '"', '/', '(', ')', '_', '+', '=', '`', '^', '#', '*', '%', '&', '\\', '[', ']', '<', '>', '{', '}', '|', '$', '-'}; for (int n = 0; n < sizeof(chChars)/sizeof(QChar); n++) { //单独更新符号btn的高度 QString objName = QString("btn_%1").arg(QString(chChars[n])); KBButton *btn = findChild(objName); btn->setFixedHeight(KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT *lfHeightScale); } //更新listfarame的高度 listFrame->setFixedHeight((KEYBOARD_FIXED_DEFAULT_NUMBER_HEIGHT *lfHeightScale +1) * (m_layoutBtnList->count())); } void NumbersWidget::onBtnClicked(QChar charId) { QObject *obj = sender(); KBButton *btn = static_cast(obj); QString objName = btn->objectName(); int lastUnderline = objName.lastIndexOf('_'); int start = strnlen("btn_", 5); int keyLength = lastUnderline - start; QString keyName = objName.mid(start, keyLength); qDebug() << "keyName: " << keyName; if (keyName == BTN_RETURN) { Q_EMIT specialBtnClicked(PAGE_LETTER); } else if (keyName == "symbol") { Q_EMIT specialBtnClicked(PAGE_CHAR); } else if (charId != QChar::Null) { Q_EMIT narmalBtnClicked(charId); } else if (keyName == BTN_BACK) { Q_EMIT specialBtnClicked(BTN_BACK); } else { Q_EMIT specialBtnClicked(keyName); } } void NumbersWidget::changeEvent(QEvent *event) { if(event->type() == QEvent::LanguageChange){ refreshTranslate(); } } void NumbersWidget::refreshTranslate() { QMap::iterator itGeometry = m_mapBtnGeometrys.begin(); for (; itGeometry != m_mapBtnGeometrys.end(); itGeometry ++) { KBButton *button = qobject_cast(itGeometry.key()); if (button) { if (button->objectName() == "btn_return") { button->setText(tr("Return")); } } } } ukui-screensaver/src/VirtualKeyboard/src/images/0000775000175000017500000000000015172041035020723 5ustar fengfengukui-screensaver/src/VirtualKeyboard/src/images/left.svg0000664000175000017500000000032515172041035022376 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/shift.svg0000664000175000017500000000075415172041035022567 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/enter.svg0000664000175000017500000000053015172041035022557 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/float-restore.svg0000664000175000017500000000265515172041035024242 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/delet.svg0000664000175000017500000000234515172041035022545 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/unlock.svg0000664000175000017500000000073715172041035022746 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/rectangle.svg0000664000175000017500000000075515172041035023417 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/shift_lock.svg0000664000175000017500000000302315172041035023567 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/space.svg0000664000175000017500000000072615172041035022544 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/float.svg0000664000175000017500000000266315172041035022560 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/right.svg0000664000175000017500000000032415172041035022560 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/drag.svg0000664000175000017500000000023115172041035022355 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/images/close.svg0000664000175000017500000000117715172041035022557 0ustar fengfeng ukui-screensaver/src/VirtualKeyboard/src/charswidget.h0000664000175000017500000000260715172041035022140 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef CHARSWIDGET_H #define CHARSWIDGET_H #include #include "kbbutton.h" #include class CharsWidget : public QWidget { Q_OBJECT public: explicit CharsWidget(QWidget *parent = nullptr); virtual ~CharsWidget(); void adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical = false, bool floatStatus = false); public Q_SLOTS: void onBtnClicked(QChar charId); Q_SIGNALS: void clicked(int nKeyId); void specialBtnClicked(QString keyName); void normalBtnClicked(QChar c); protected: void changeEvent(QEvent *event); private: void initUI(); void refreshTranslate(); private: QMap m_mapBtnGeometrys; }; #endif // CHARSWIDGET_H ukui-screensaver/src/VirtualKeyboard/src/letterswidget.cpp0000664000175000017500000005167715172041106023067 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "letterswidget.h" #include "commondef.h" #include "utils.h" #include #include #include extern bool checkCapsLockState(); LettersWidget::LettersWidget(QWidget *parent/* = nullptr*/) : QWidget(parent) { this->setAttribute(Qt::WA_TranslucentBackground);//背景透明 initUI(); //990上XDG_SESSION_TYPE环境变量为wayland,但是走的是x11,这里判断纯wayland环境 if(QString(qgetenv("XDG_SESSION_TYPE")) == "wayland" && QApplication::platformName() != "xcb") { isWayland = true; } if(isWayland){ m_capslockStateHelper = CapslockStateHelper::instance(this); connect(m_capslockStateHelper, &CapslockStateHelper::capslockChanged, this, [=](bool state) { qDebug() << " CapsLock changed !!! " << " state = " << state; capsState = state; onCapsChanged(); }); } else { settings = new QGSettings("org.ukui.peripherals-keyboard", "", this); connect(settings, &QGSettings::changed, this, &LettersWidget::onCapsChanged); } //这里不再判断是否是wayland环境,不在调用gsettings获取大写锁定状态,而通过读取文件去获取 onCapsChanged(); } LettersWidget::~LettersWidget() { } void LettersWidget::initUI() { // line 1 QChar chLine1[] = {'q','w','e','r','t','y','u','i','o','p'}; for (int n = 0; n < sizeof(chLine1)/sizeof(QChar); n++) { KBButton *letterBtn = new KBButton(this); QString objName = "btn_" + QString::number(chLine1[n].toLatin1()); letterBtn->setObjectName(objName); letterBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L1+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*n, KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); letterBtn->setCharId(chLine1[n]); letterBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(letterBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[letterBtn] = letterBtn->geometry(); } // backspace KBButton *backspaceBtn = new KBButton(this); backspaceBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L1+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*10, KEYBOARD_FIXED_DEFAULT_TMARGIN, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); backspaceBtn->setObjectName("btn_backspace"); backspaceBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); backspaceBtn->setIcon(QIcon(":/images/images/delet.svg")); connect(backspaceBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[backspaceBtn] = backspaceBtn->geometry(); qDebug()<<"backspaceBtn geometry:"<geometry(); // line 2 QChar chLine2[] = {'a','s','d','f','g','h','j','k','l'}; for (int n = 0; n < sizeof(chLine2)/sizeof(QChar); n++) { KBButton *letterBtn = new KBButton(this); QString objName = "btn_" + QString::number(chLine2[n].toLatin1()); letterBtn->setObjectName(objName); letterBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L2+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*n, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING), KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); letterBtn->setCharId(chLine2[n]); letterBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED,KEYBOARD_FONT_COLOR_PRESS); connect(letterBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[letterBtn] = letterBtn->geometry(); } // enter KBButton *enterBtn = new KBButton(this); enterBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L2+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*9, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING), KEYBOARD_FIXED_DEFAULT_ENTERBTN_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); enterBtn->setObjectName("btn_enter"); enterBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); enterBtn->setIcon(QIcon(":/images/images/enter.svg")); connect(enterBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[enterBtn] = enterBtn->geometry(); // line 3 QChar chLine3[] = {'z','x','c','v','b','n','m',',','.'}; for (int n = 0; n < sizeof(chLine3)/sizeof(QChar); n++) { KBButton *letterBtn = new KBButton(this); QString objName = "btn_" + QString::number(chLine3[n].toLatin1()); letterBtn->setObjectName(objName); letterBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L3+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*(n+1), KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); letterBtn->setCharId(chLine3[n]); letterBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_BORDER_NORMAL,KEYBOARD_LETTER_COLOR_BORDER_PRESSED, KEYBOARD_FONT_COLOR_PRESS); connect(letterBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[letterBtn] = letterBtn->geometry(); } // shift l KBButton *shiftLBtn = new KBButton(this); shiftLBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L3, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); shiftLBtn->setObjectName("btn_shift_l"); shiftLBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); shiftLBtn->setIcon(QIcon(":/images/images/shift.svg")); connect(shiftLBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[shiftLBtn] = shiftLBtn->geometry(); // shift r KBButton *shiftRBtn = new KBButton(this); shiftRBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L3+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*10, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*2, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); shiftRBtn->setObjectName("btn_shift_r"); shiftRBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); shiftRBtn->setIcon(QIcon(":/images/images/shift.svg")); connect(shiftRBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[shiftRBtn] = shiftRBtn->geometry(); // line 4 KBButton *symbolBtn = new KBButton(this); symbolBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); symbolBtn->setObjectName("btn_symbol"); symbolBtn->setText(tr("&&?!")); symbolBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(symbolBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[symbolBtn] = symbolBtn->geometry(); KBButton *numBtn = new KBButton(this); numBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING), KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); numBtn->setObjectName("btn_num"); numBtn->setText(tr("123")); numBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(numBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[numBtn] = numBtn->geometry(); KBButton *ctrlBtn = new KBButton(this); ctrlBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*2, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); ctrlBtn->setObjectName("btn_ctrl"); ctrlBtn->setText("Ctrl"); ctrlBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(ctrlBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[ctrlBtn] = ctrlBtn->geometry(); KBButton *spaceBtn = new KBButton(this); spaceBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*3, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_SPACEBTN_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); spaceBtn->setObjectName("btn_space"); spaceBtn->updateStyleSheet(KEYBOARD_LETTER_COLOR_NORMAL,KEYBOARD_LETTER_COLOR_PRESSED, KEYBOARD_LETTER_COLOR_PRESSED,KEYBOARD_LETTER_COLOR_BORDER_NORMAL, KEYBOARD_LETTER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); spaceBtn->setIcon(QIcon(":/images/images/space.svg")); connect(spaceBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[spaceBtn] = spaceBtn->geometry(); KBButton *altBtn = new KBButton(this); altBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*8, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); altBtn->setObjectName("btn_alt"); altBtn->setText("Alt"); altBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); connect(altBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[altBtn] = altBtn->geometry(); KBButton *leftBtn = new KBButton(this); leftBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*9, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); leftBtn->setObjectName("btn_left"); leftBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); leftBtn->setIcon(QIcon(":/images/images/left.svg")); connect(leftBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[leftBtn] = leftBtn->geometry(); KBButton *rightBtn = new KBButton(this); rightBtn->setGeometry(KEYBOARD_FIXED_DEFAULT_LETTER_L4+(KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH+KEYBOAED_FIXED_DEFAULT_HSPACING)*10, KEYBOARD_FIXED_DEFAULT_TMARGIN+(KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT+KEYBOAED_FIXED_DEFAULT_VSPACING)*3, KEYBOARD_FIXED_DEFAULT_LETTER_WIDTH, KEYBOARD_FIXED_DEFAULT_LETTER_HEIGHT); rightBtn->setObjectName("btn_right"); rightBtn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED, KEYBOARD_OTHER_FONT_COLOR_PRESS); rightBtn->setIcon(QIcon(":/images/images/right.svg")); connect(rightBtn, &KBButton::clicked, this, &LettersWidget::onBtnClicked); m_mapBtnGeometrys[rightBtn] = rightBtn->geometry(); } void LettersWidget::adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical/* = false*/, bool floatStatus) { QMap::iterator itGeometry = m_mapBtnGeometrys.begin(); for (; itGeometry != m_mapBtnGeometrys.end(); itGeometry ++) { KBButton *button = itGeometry.key(); if (button) { QRect oldGeometry = itGeometry.value(); QRect newGeometry = oldGeometry; if (floatStatus) { newGeometry.setX(oldGeometry.x()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale*KEYBOARD_FLOAT_PERCENTAGE); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } else { newGeometry.setX(oldGeometry.x()*lfWidthScale); newGeometry.setY(oldGeometry.y()*lfHeightScale); newGeometry.setWidth(oldGeometry.width()*lfWidthScale); newGeometry.setHeight(oldGeometry.height()*lfHeightScale); } button->setGeometry(newGeometry); } } } void LettersWidget::onBtnClicked(QChar charId) { QObject *obj = sender(); KBButton *btn = static_cast(obj); QString objName = btn->objectName(); int lastUnderline = objName.lastIndexOf('_'); int start = strnlen("btn_", 5); int keyLength = lastUnderline - start; QString keyName = objName.mid(start, keyLength); if (charId != QChar::Null && isShift && !capsState) { //第一次按shift,按后取消shift状态 isShift = false; changeFuncKeyStyle("shift_l", false); changeFuncKeyStyle("shift_r", false); toggleCase(); } if (keyName == "num") { //数字页面 Q_EMIT specialBtnClicked(PAGE_NUMBER); } else if (keyName == "symbol") { //符号页面 Q_EMIT specialBtnClicked(PAGE_CHAR); } else if (keyName == BTN_SHIFT) { //shift if (capsState) { capsState = false; isShift = false; changeFuncKeyStyle("shift_l", false); changeFuncKeyStyle("shift_r", false); Q_EMIT specialBtnClicked(BTN_CAPSLOCK); } else if (isShift) { //shift键锁定 capsState = true; isShift = true; changeFuncKeyStyle("shift_l", true); changeFuncKeyStyle("shift_r", true); Q_EMIT specialBtnClicked(BTN_CAPSLOCK); } else { isShift = true; changeFuncKeyStyle("shift_l", true); changeFuncKeyStyle("shift_r", true); } toggleCase(); } else if (charId != QChar::Null) { //字符 Q_EMIT normalBtnClicked(charId); } else { //ctrl或者alt Q_EMIT specialBtnClicked(keyName); } } void LettersWidget::onCapsChanged() { // if(isWayland){ // capsState = PlasmaShellManager::getInstance()->getKeyState(KWayland::Client::Keystate::Key::CapsLock); // }else{ // capsState = settings->get("capslock-state").toBool(); // } // capsState = checkCapsLockState(); isShift = capsState; qInfo() << " isShift = " << isShift << " capsState = " << capsState; for (int i = 97; i < 123; i++) { //大小写切换 QString objName = QString("btn_%1").arg(QString::number(i)); KBButton *btn = findChild(objName); btn->setCapsStatus(capsState); changeFuncKeyStyle("shift_l", capsState); changeFuncKeyStyle("shift_r", capsState); } } void LettersWidget::toggleCase() { for (int i = 97; i < 123; i++) { //大小写切换 QString objName = QString("btn_%1").arg(QString::number(i)); KBButton *btn = findChild(objName); btn->setShiftState(isShift); } } void LettersWidget::changeFuncKeyStyle(QString obj, bool isLock) { if(obj == BTN_CAPSLOCK) return; QString objName = QString("btn_%1").arg(obj); KBButton *btn = findChild(objName); if (isLock) { btn->updateStyleSheet(KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS, KBButton::BORDER_RADIUS_ALL,true); if (capsState && obj.contains(BTN_SHIFT)) { btn->setIcon(QIcon(":/images/images/shift_lock.svg")); } else if (obj.contains(BTN_SHIFT)) { btn->setIcon(QIcon(":/images/images/rectangle.svg")); } } else { btn->updateStyleSheet(KEYBOARD_OTHER_COLOR_NORMAL,KEYBOARD_OTHER_COLOR_PRESSED, KEYBOARD_OTHER_COLOR_PRESSED,KEYBOARD_OTHER_COLOR_BORDER_NORMAL, KEYBOARD_OTHER_COLOR_BORDER_PRESSED,KEYBOARD_OTHER_FONT_COLOR_PRESS); if (obj.contains(BTN_SHIFT)) { btn->setIcon(QIcon(":/images/images/shift.svg")); } } } ukui-screensaver/src/VirtualKeyboard/src/kbtitle.h0000664000175000017500000000252615172041035021272 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef KBTITLE_H #define KBTITLE_H #include #include #include class KBTitle : public QWidget { Q_OBJECT public: KBTitle(QWidget *parent = nullptr); virtual ~KBTitle(); public: void adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical = false, bool floatStatus = false); public Q_SLOTS: void onBtnClicked(); Q_SIGNALS: void btnClicked(QString keyName); private: void initUI(); void initConnections(); private: QPushButton *m_btnFloat = nullptr; QPushButton *m_btnClose = nullptr; QMap m_mapSubGeometrys; bool floatStatus = false; }; #endif // KBTITLE_H ukui-screensaver/src/VirtualKeyboard/src/kbbutton.h0000664000175000017500000000405415172041106021461 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef KBBUTTON_H #define KBBUTTON_H #include class KBButton : public QPushButton { Q_OBJECT public: enum BORDER_RADIUS{ BORDER_RADIUS_NONE, BORDER_RADIUS_LT = 1, BORDER_RADIUS_TR = 2, BORDER_RADIUS_RB = 4, BORDER_RADIUS_LB = 8, BORDER_RADIUS_ALL = 0xF, }; KBButton(QWidget *parent = nullptr); virtual ~KBButton(); void setCharId(QChar charId); void updateStyleSheet(QString clrNormal, QString clrHover, QString clrChecked, QString clrBoard, QString clrBoardPress, QString clrFontPress, int radius = BORDER_RADIUS_ALL, bool is_lock = false); void setShiftState(bool isShift); void setCapsStatus(bool isCaps); void setCtrlState(bool isCtrl); void setAltState(bool isAlt); protected: bool eventFilter(QObject *watched, QEvent *event); Q_SIGNALS: void clicked(QChar charId); private: QPixmap drawSymbolicColoredPixmap(const QPixmap &source, QString cgColor); private: int m_nKeyId = -1; bool m_isShift = false; bool m_isCtrl = false; bool m_isAlt = false; bool m_isCaps = false; QChar m_charId = QChar::Null; QString m_clrBoard; QString m_clrNormal; QString m_clrBoardPress; QString m_clrHover; QString m_clrChecked; QFont sysFont; QString m_originalText; QString m_elidedText; bool m_isTextElided; }; #endif // KBBUTTON_H ukui-screensaver/src/VirtualKeyboard/src/charsmorewidget.h0000664000175000017500000000310015172041035023010 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef CHARSMOREWIDGETS_H #define CHARSMOREWIDGETS_H #include #include "kbbutton.h" #include class QVBoxLayout; class QHBoxLayout; class QScrollArea; class QFrame; class CharsMoreWidget : public QWidget { Q_OBJECT public: explicit CharsMoreWidget(QWidget *parent = nullptr); ~CharsMoreWidget(); void adjustGeometry(double lfWidthScale, double lfHeightScale, bool isVertical = false, bool floatStatus = false); public Q_SLOTS: void onBtnClicked(QChar charId); Q_SIGNALS: void clicked(int nKeyId); void specialBtnClicked(QString keyName); void normalBtnClicked(QChar charId); private: void initUI(); private: QMap m_mapBtnGeometrys; QMap m_mapSubWidgetListRects; QVBoxLayout *m_vlayoutBtnList = nullptr; QScrollArea *m_scrollFrame = nullptr; QFrame *listFrame = nullptr; }; #endif // CHARSMOREWIDGETS_H ukui-screensaver/src/lock-checkpasswd/0000775000175000017500000000000015172041035017005 5ustar fengfengukui-screensaver/src/lock-checkpasswd/main.cpp0000664000175000017500000001122515172041035020436 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 static int toParent = 0; static int fromChild = 0; typedef struct pam_message PAM_MESSAGE; typedef struct pam_response PAM_RESPONSE; static void writeData(int fd, const void *buf, ssize_t count) { if(write(fd, buf, count) != count) printf("write to parent failed: %s\n",strerror(errno)); } static void writeString(int fd, const char *data) { int length = data ? strnlen(data, 1024) : -1; writeData(fd, &length, sizeof(length)); if(data) writeData(fd, data, sizeof(char) * length); } static int readData(int fd, void *buf, size_t count) { ssize_t nRead = read(fd, buf, count); if(nRead < 0) printf("read data failed: %s\n",strerror(errno)); return nRead; } static char * readString(int fd) { int length; if(readData(fd, &length, sizeof(length)) <= 0) return NULL; if(length <= 0) length = 0; char *value = (char *)malloc(sizeof(char) * (length + 1)); readData(fd, value, length); value[length] = '\0'; return value; } static int pam_conversation(int msgLength, const struct pam_message **msg, PAM_RESPONSE **resp, void */*appData*/) { PAM_RESPONSE *response = (PAM_RESPONSE*)calloc(msgLength,sizeof(PAM_RESPONSE)); int authComplete = 0; writeData(toParent, (const void*)&authComplete, sizeof(authComplete)); writeData(toParent, (const void*)&msgLength, sizeof(msgLength)); //发送pam消息 for(int i = 0; i < msgLength; i++) { const struct pam_message *m = msg[i]; writeData(toParent, (const void *)&m->msg_style, sizeof(m->msg_style)); writeString(toParent, m->msg); } //读取响应 for(int i = 0; i < msgLength; i++) { PAM_RESPONSE *r = &response[i]; readData(fromChild, &r->resp_retcode, sizeof(r->resp_retcode)); r->resp = readString(fromChild); } *resp = response; return PAM_SUCCESS; } static void _authenticate(const char *userName) { printf("authenticate %s\n",userName); pam_handle_t *pamh = NULL; char *newUser; int ret; int authRet; struct pam_conv conv; conv.conv = pam_conversation; conv.appdata_ptr = NULL; ret = pam_start("ukui-screensaver-qt", userName, &conv, &pamh); if(ret != PAM_SUCCESS) { printf("failed to start PAM: = %s\n", pam_strerror(NULL, ret)); } authRet = pam_authenticate(pamh, 0); if (pam_acct_mgmt(pamh, 0) != PAM_SUCCESS) { _exit(EXIT_FAILURE); } ret = pam_get_item(pamh, PAM_USER, (const void **)&newUser); if(ret != PAM_SUCCESS) { pam_end(pamh, 0); printf("failed to get username\n"); _exit(EXIT_FAILURE); } if(authRet == PAM_SUCCESS) authRet = pam_acct_mgmt(pamh, 0); if (authRet == PAM_NEW_AUTHTOK_REQD) authRet = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); if(authRet != PAM_SUCCESS) { } fprintf(stderr, "authentication result: %d\n", authRet); // 发送认证结果 int authComplete = 1; writeData(toParent, (const void*)&authComplete, sizeof(authComplete)); writeData(toParent, (const void *)&authRet, sizeof(authRet)); printf("--- 认证完成\n"); /* Stop if we didn't authenticated */ if (authRet != PAM_SUCCESS) { pam_end(pamh, 0); _exit(EXIT_FAILURE); } pam_end(pamh, 0); pamh = NULL; _exit(EXIT_SUCCESS); } int main(int argc, char **argv) { if (argc != 4) { return EXIT_FAILURE; } toParent = atoi (argv[1]); fromChild = atoi (argv[2]); if (toParent == 0 || fromChild == 0) { printf ("Invalid file descriptors %s %s\n", argv[2], argv[3]); return EXIT_FAILURE; } //mlockall (MCL_CURRENT | MCL_FUTURE); fcntl (toParent, F_SETFD, FD_CLOEXEC); fcntl (fromChild, F_SETFD, FD_CLOEXEC); _authenticate(argv[3]); } ukui-screensaver/src/lock-dialog/0000775000175000017500000000000015172041106015744 5ustar fengfengukui-screensaver/src/lock-dialog/rootWindowBackground.cpp0000664000175000017500000003632715172041106022636 0ustar fengfeng/* rootWindowBackground.cpp * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 St, Fifth Floor, Boston, MA * 02110-1301, USA. **/ #ifdef SUPPORT_X11_BACKGROUND #include #include #include #include #include #include #include #include #include #include "rootWindowBackground.h" static const XID INVAL_ID = ~0UL; Pixmap pix = INVAL_ID; Display *dpy = NULL; Window root = 0; Screen *scn = NULL; struct RootWindowBGInfo { bool isValid = false; int nType; int nColor; QString strFileName; int nOption; QList listScreen; }; static RootWindowBGInfo g_lastRootWndInfo; typedef enum WNDBG_OPTION_e { WNDBG_OPTION_SCALED, // 填充 WNDBG_OPTION_STRETCHED, // 拉伸 WNDBG_OPTION_CENTERED, // 居中 WNDBG_OPTION_WALLPAPER, // 平铺 WNDBG_OPTION_ZOOM, // 适应 WNDBG_OPTION_SPANNED // 跨区 } WNDBG_OPTION; static QRect getSourceRect(const QRect &destination, const QRect &source) { qreal screenScale = qreal(destination.width()) / qreal(destination.height()); qreal width = source.width(); qreal height = source.height(); if ((width / height) == screenScale) { return source; } bool isShortX = (width <= height); if (isShortX) { screenScale = qreal(destination.height()) / qreal(destination.width()); } qreal shortEdge = isShortX ? width : height; qreal longEdge = isShortX ? height : width; while (shortEdge > 1) { qint32 temp = qFloor(shortEdge * screenScale); if (temp <= longEdge) { longEdge = temp; break; } qint32 spacing = qRound(shortEdge / 20); if (spacing <= 0) { spacing = 1; } shortEdge -= spacing; } QSize sourceSize = source.size(); if (shortEdge > 1 && longEdge > 1) { sourceSize.setWidth(isShortX ? shortEdge : longEdge); sourceSize.setHeight(isShortX ? longEdge : shortEdge); } qint32 offsetX = 0; qint32 offsetY = 0; if (source.width() > sourceSize.width()) { offsetX = (source.width() - sourceSize.width()) / 2; } if (source.height() > sourceSize.height()) { offsetY = (source.height() - sourceSize.height()) / 2; } QPoint offsetPoint = source.topLeft(); offsetPoint += QPoint(offsetX, offsetY); return QRect(offsetPoint, sourceSize); } static QRect getDestRect(const QRect &destination, const QRect &source) { qreal screenScale = qreal(destination.width()) / qreal(destination.height()); qreal pixmapScale = qreal(source.width() / source.height()); qreal width = source.width(); qreal height = source.height(); if (pixmapScale == screenScale) { return destination; } qreal scaleWidth = destination.width() / width; qreal scaleHeight = destination.height() / height; qreal realPixmapWidth = 0; qreal realPixmapHeight = 0; if (pixmapScale < screenScale) { // 图片比例小于屏幕比例时,按照图片和屏幕高度比进行缩放 realPixmapWidth = width * scaleHeight; realPixmapHeight = destination.height(); } else { // 图片比例大于屏幕比例时,按照图片与屏幕宽度比进行缩放 realPixmapWidth = destination.width(); realPixmapHeight = height * scaleWidth; } QSize sourceSize = destination.size(); qint32 offsetX = 0; qint32 offsetY = 0; if (destination.width() == realPixmapWidth) { offsetY = (destination.height() - realPixmapHeight) / 2; sourceSize.setHeight(realPixmapHeight); } else if (destination.height() == realPixmapHeight) { offsetX = (destination.width() - realPixmapWidth) / 2; sourceSize.setWidth(realPixmapWidth); } qDebug() << "=========getDestRect sourceSize:" << sourceSize; QPoint offsetPoint = destination.topLeft(); offsetPoint += QPoint(offsetX, offsetY); return QRect(offsetPoint, sourceSize); } static QRect getSourceRect(const QRect &source, const QRect &screenGeometry, const QRect &screenVirtualGeometry) { qreal pixWidth = source.width(); qreal pixHeight = source.height(); QSize sourceSize = source.size(); sourceSize.setWidth(screenGeometry.width() * 1.0 / screenVirtualGeometry.width() * pixWidth); sourceSize.setHeight(screenGeometry.height() * 1.0 / screenVirtualGeometry.height() * pixHeight); qint32 offsetX = 0; qint32 offsetY = 0; if (screenGeometry.x() > 0) { offsetX = (screenGeometry.x() * 1.0 / screenVirtualGeometry.width() * pixWidth); } if (screenGeometry.y() > 0) { offsetY = (screenGeometry.y() * 1.0 / screenVirtualGeometry.height() * pixHeight); } QPoint offsetPoint = source.topLeft(); offsetPoint += QPoint(offsetX, offsetY); return QRect(offsetPoint, sourceSize); } void setRootWindowBackground( bool type, unsigned int color, char *filename, const QList> screenRectList, int nOption) { Imlib_Image img; if (!dpy) { dpy = XOpenDisplay(NULL); if (!dpy) return; } int width = 0, height = 0; width = QApplication::desktop()->geometry().width() * qApp->devicePixelRatio(); height = QApplication::desktop()->geometry().height() * qApp->devicePixelRatio(); if (!scn) scn = DefaultScreenOfDisplay(dpy); if (!root) root = DefaultRootWindow(dpy); if (pix != INVAL_ID) { XFreePixmap(dpy, pix); pix = INVAL_ID; } pix = XCreatePixmap(dpy, root, width, height, DefaultDepthOfScreen(scn)); imlib_context_set_display(dpy); imlib_context_set_visual(DefaultVisualOfScreen(scn)); imlib_context_set_colormap(DefaultColormapOfScreen(scn)); imlib_context_set_drawable(pix); if (type == 0) { img = imlib_load_image(filename); if (!img) { fprintf(stderr, "%s:Unable to load image\n", filename); return; } imlib_context_set_image(img); } else if (type == 1) { img = imlib_create_image(width, height); imlib_context_set_image(img); int blue = color & 0xFF; int green = color >> 8 & 0xFF; int red = color >> 16 & 0xFF; qDebug() << "red = " << red << " green = " << green << " blue = " << blue; imlib_context_set_color(red, green, blue, 255); imlib_image_fill_rectangle(0, 0, width, height); } g_lastRootWndInfo.isValid = true; g_lastRootWndInfo.nType = type; g_lastRootWndInfo.nColor = color; g_lastRootWndInfo.strFileName = filename; g_lastRootWndInfo.nOption = nOption; g_lastRootWndInfo.listScreen.clear(); imlib_context_set_image(img); QRect rectImg(0, 0, imlib_image_get_width(), imlib_image_get_height()); for (QPair pair : screenRectList) { QRect rect = pair.first; // 在每个屏幕上绘制背景 rect.setRect( rect.x() * qApp->devicePixelRatio(), rect.y() * qApp->devicePixelRatio(), rect.width() * qApp->devicePixelRatio(), rect.height() * qApp->devicePixelRatio()); QRect newSrcRect = rectImg; g_lastRootWndInfo.listScreen.append(rect); switch (nOption) { case WNDBG_OPTION_CENTERED: // 居中 { if (newSrcRect.width() > rect.width()) { int nHDiff = (newSrcRect.width() - rect.width()) / 2; newSrcRect.setX(newSrcRect.x() + nHDiff); newSrcRect.setWidth(newSrcRect.width() - nHDiff); } else { int nHDiff = (rect.width() - newSrcRect.width()) / 2; rect.setX(rect.x() + nHDiff); rect.setWidth(rect.width() - nHDiff); } if (newSrcRect.height() > rect.height()) { int nVDiff = (newSrcRect.height() - rect.height()) / 2; newSrcRect.setY(newSrcRect.y() + nVDiff); newSrcRect.setHeight(newSrcRect.height() - nVDiff); } else { int nVDiff = (rect.height() - newSrcRect.height()) / 2; rect.setY(rect.y() + nVDiff); rect.setHeight(rect.height() - nVDiff); } qDebug() << "Centered:" << rect << newSrcRect << rectImg; imlib_render_image_part_on_drawable_at_size( newSrcRect.x(), newSrcRect.y(), newSrcRect.width(), newSrcRect.height(), rect.x(), rect.y(), rect.width(), rect.height()); } break; case WNDBG_OPTION_STRETCHED: // 拉伸 { qDebug() << "Stretched:" << rect << newSrcRect << rectImg; imlib_render_image_part_on_drawable_at_size( newSrcRect.x(), newSrcRect.y(), newSrcRect.width(), newSrcRect.height(), rect.x(), rect.y(), rect.width(), rect.height()); } break; case WNDBG_OPTION_SCALED: // 填充 { newSrcRect = getSourceRect(rect, newSrcRect); qDebug() << "Scaled:" << rect << newSrcRect << rectImg; imlib_render_image_part_on_drawable_at_size( newSrcRect.x(), newSrcRect.y(), newSrcRect.width(), newSrcRect.height(), rect.x(), rect.y(), rect.width(), rect.height()); } break; case WNDBG_OPTION_WALLPAPER: // 平铺 { int drawedWidth = 0; int drawedHeight = 0; while (1) { drawedWidth = 0; while (1) { imlib_render_image_part_on_drawable_at_size( newSrcRect.x(), newSrcRect.y(), newSrcRect.width(), newSrcRect.height(), drawedWidth, drawedHeight, newSrcRect.width(), newSrcRect.height()); drawedWidth += newSrcRect.width(); if (drawedWidth >= rect.width()) { break; } } drawedHeight += newSrcRect.height(); if (drawedHeight >= rect.height()) { break; } } } break; case WNDBG_OPTION_ZOOM: // 适应 { rect = getDestRect(rect, newSrcRect); qDebug() << "Zoom:" << rect << newSrcRect << rectImg; imlib_render_image_part_on_drawable_at_size( newSrcRect.x(), newSrcRect.y(), newSrcRect.width(), newSrcRect.height(), rect.x(), rect.y(), rect.width(), rect.height()); } break; case WNDBG_OPTION_SPANNED: // 跨区 { // for(auto sscreen : qApp->screens()){ // if (screen->name() == sscreen->name()) { QRect srcRect = getSourceRect(newSrcRect, pair.first, pair.second); imlib_render_image_part_on_drawable_at_size( srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), rect.x(), rect.y(), rect.width(), rect.height()); break; // } //} } break; default: { imlib_render_image_part_on_drawable_at_size( newSrcRect.x(), newSrcRect.y(), newSrcRect.width(), newSrcRect.height(), rect.x(), rect.y(), rect.width(), rect.height()); } break; } } imlib_free_image(); } void updateRootWindowBackground() { // 对比新的屏幕尺寸是否有变化 if (g_lastRootWndInfo.isValid) { bool isSame = true; QList listScreen; for (QScreen *screen : QApplication::screens()) { // 在每个屏幕上绘制背景 QRect rect = screen->geometry(); rect.setX(rect.x() * screen->devicePixelRatio()); rect.setY(rect.y() * screen->devicePixelRatio()); rect.setWidth(rect.width() * screen->devicePixelRatio()); rect.setHeight(rect.height() * screen->devicePixelRatio()); listScreen.append(rect); } qDebug() << "ScreenRects:" << listScreen << "||" << g_lastRootWndInfo.listScreen; if (listScreen.size() > g_lastRootWndInfo.listScreen.size()) { isSame = false; } else { for (int n = 0; n < listScreen.size(); n++) { if (n < g_lastRootWndInfo.listScreen.size()) { if (g_lastRootWndInfo.listScreen[n] != listScreen[n]) { isSame = false; break; } } } } if (!isSame) { QList> screenRectList; for (QScreen *screen : QApplication::screens()) { QRect rect1 = screen->geometry(); QRect rect2 = screen->virtualGeometry(); screenRectList.append(qMakePair(rect1, rect2)); } setRootWindowBackground( g_lastRootWndInfo.nType, g_lastRootWndInfo.nColor, g_lastRootWndInfo.strFileName.toLatin1().data(), screenRectList, g_lastRootWndInfo.nOption); } } } void drawBackground(bool isDraw) { if (!dpy) { return; } if (isDraw) { if (pix != INVAL_ID) XSetWindowBackgroundPixmap(dpy, root, pix); XClearWindow(dpy, root); } while (XPending(dpy)) { XEvent ev; XNextEvent(dpy, &ev); } if (pix != INVAL_ID) { XFreePixmap(dpy, pix); pix = INVAL_ID; } XCloseDisplay(dpy); dpy = NULL; } #endif // SUPPORT_X11_BACKGROUND ukui-screensaver/src/lock-dialog/backenddbushelper.h0000664000175000017500000003432715172041106021573 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef BACKENDDBUSHELPER_H #define BACKENDDBUSHELPER_H #include #include #include #include #include #include #include #include #include "../../src/userinfo.h" #include "../../src/agreementinfo.h" #include "../../src/common/biodefines.h" /** * @brief 后端服务访问工具类 * */ class BackendDbusHelper : public QDBusAbstractInterface { Q_OBJECT public: /** * @brief 构造 * * @param service dbus服务名称 * @param path dbus路径 * @param connection dbus连接 * @param parent 父指针 */ BackendDbusHelper( const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr); /** * @brief 析构 * */ ~BackendDbusHelper(); public: /** * @brief 获取用户信息列表 * * @return QList */ QList getUsersInfo(); QString getInitDataJson(); bool isSessionActive(); QString getDefaultAuthUser(); QString getCurrentUser(); QList getSessionsInfo(); QString getCurrentSession(); AgreementInfoPtr getAgreementInfo(); QVariant getLockScreenConf(QString strKey); bool setLockScreenConf(QString strKey, QVariant value); QVariant getScreenSaverConf(QString strKey); bool setScreenSaverConf(QString strKey, QVariant value); QStringList getDefaultPhotoPath(); QStringList getCustomPhotoPath(); QVariant getPowerManagerConf(QString strKey); bool setPowerManagerConf(QString strKey, QVariant value); QVariant getMateBgConf(QString strKey); bool setMateBgConf(QString strKey, QVariant value); QVariant getUkccPluginsConf(QString strKey); bool setUkccPluginsConf(QString strKey, QVariant value); QVariant getThemeStyleConf(QString strKey); bool setThemeStyleConf(QString strKey, QVariant value); QVariant getSessionConf(QString strKey); bool setSessionConf(QString strKey, QVariant value); QVariant getKeyboardConf(QString strKey); bool setKeyboardConf(QString strKey, QVariant value); QVariant getUsdMediaKeys(QString strKey); QVariant getUsdMediaStateKeys(QString strKey); bool setUsdMediaStateKeys(QString strKey, QVariant value); QList getSaverThemes(); /** * @brief 调用usd dbus接口实现相关快捷键功能 * *@param actionType:快捷键类型 * * @return 是否调用成功 */ bool usdExternalDoAction(int actionType); QStringList getBatteryArgs(); QString getBatteryIconName(); bool getIsBattery(); QStringList getSleepLockcheck(); QStringList getShutdownLockcheck(); QStringList getLogoutLockcheck(); bool getCurTabletMode(); int getNetworkConnectState(); QVariant getWeatherData(QString strKey); bool getSaverDatetimeEnable(); bool setSaverDatetimeEnable(bool enable); bool getLockDatetimeEnable(); bool getCanSwitchUser(); bool setLockDatetimeEnable(bool enable); public Q_SLOTS: /** * @brief dbus服务信息更新处理 * * @param strJson 信息json */ void onUpdateInformation(const QString &strJson); bool setCurrentUser(QString strUserName); int switchToUser(QString strUserName); bool setCurrentSession(QString strSession); bool lockStateChanged(bool isVisible, bool isSessionTools); void startSession(); void pamAuthenticate(QString strUserName); void pamRespond(QString strRespond); void pamAuthenticateCancel(); bool pamIsInAuthentication(); bool pamIsAuthenticated(); QString pamAuthenticateUser(); QString getAutologinUserHint(); bool setPowerManager(bool isForce, QString strFuncName, bool isSessionTools = false); bool getPowerManagerCanSwitchUser(); bool getPowerManagerCanHibernate(); bool getPowerManagerCanPowerOff(); bool getPowerManagerCanReboot(); bool getPowerManagerCanSuspend(); bool getPowerManagerCanLockScreen(); bool getPowerManagerCanLogout(); /** * @brief checkSystemUpgrade 系统是否有更新 * @return true 有,false 无 */ bool checkSystemUpgrade(); QString getPublicEncrypt(); bool sendPassword(const QString username, QByteArray password); void bioStartAuth(int uid, int nDevId); void bioStopAuth(); QList bioGetAvailableDevices(int nUid); QList bioGetDisabledDevices(int nUid); int bioGetBioAuthState(); DeviceInfo bioGetCurBioInfo(); DeviceInfo bioFindDeviceById(int nUid, int nDevId); DeviceInfo bioFindDeviceByName(int nUid, QString strDevName); QString bioGetDefaultDevice(int nUid, QString strUserName, int bioType = -1); public Q_SLOTS: // METHODS /** * @brief 获取黑色屏保状态 * * @return QDBusPendingReply 是否为黑色屏保状态 */ inline QDBusPendingReply GetBlankState() { QList argumentList; return callWithArgumentList(QDBus::Block, QStringLiteral("GetBlankState"), argumentList); } /** * @brief 获取信息 * * @param in0 信息参数json * @return QDBusPendingReply 信息json */ inline QDBusPendingReply GetInformation(const QString &in0) { QList argumentList; argumentList << QVariant::fromValue(in0); return asyncCallWithArgumentList(QStringLiteral("GetInformation"), argumentList); } /** * @brief 获取锁定状态 * * @return QDBusPendingReply 锁定状态 */ inline QDBusPendingReply GetLockState() { QList argumentList; return asyncCallWithArgumentList(QStringLiteral("GetLockState"), argumentList); } /** * @brief 锁定 * * @return QDBusPendingReply<> */ inline QDBusPendingReply<> Lock() { QList argumentList; return asyncCallWithArgumentList(QStringLiteral("Lock"), argumentList); } /** * @brief 以黑色屏保方式锁定 * * @param in0 触发锁定的类型 * @return QDBusPendingReply 是否锁定成功 */ inline QDBusPendingReply LockByBlank(int in0) { QList argumentList; argumentList << QVariant::fromValue(in0); return asyncCallWithArgumentList(QStringLiteral("LockByBlank"), argumentList); } /** * @brief 设置信息 * * @param in0 信息参数json * @return QDBusPendingReply 设置结果 0 成功,其他失败 */ inline QDBusPendingReply SetInformation(const QString &in0) { QList argumentList; argumentList << QVariant::fromValue(in0); return asyncCallWithArgumentList(QStringLiteral("SetInformation"), argumentList); } /** * @brief 设置锁定状态 * * @return void */ inline void SetLockState() { call(QStringLiteral("SetLockState")); } /** * @brief 清除锁定状态 * * @return void */ inline void ClearLockState() { call(QStringLiteral("ClearLockState")); } /** * @brief 显示屏保 * * @return QDBusPendingReply<> */ inline QDBusPendingReply<> ShowScreensaver() { QList argumentList; return asyncCallWithArgumentList(QStringLiteral("ShowScreensaver"), argumentList); } /** * @brief 解锁 * * @return QDBusPendingReply<> */ inline QDBusPendingReply<> UnLock() { QList argumentList; return asyncCallWithArgumentList(QStringLiteral("UnLock"), argumentList); } /** * @brief 检查并开屏 * * @return void */ inline void CheckTurnOnScreen() { call(QStringLiteral("CheckTurnOnScreen")); } Q_SIGNALS: // SIGNALS /** * @brief 第二次启动参数通知 * * @param param 启动参数 */ void SecondRunParam(const QString ¶m); /** * @brief 会话空闲进入屏保 * */ void SessionIdle(); /** * @brief 会话空闲进入锁屏 * */ void SessionLockIdle(); /** * @brief 后端服务信息更新 * * @param info 信息json */ void UpdateInformation(const QString &info); /** * @brief 进入锁定 * */ void lock(); /** * @brief 退出锁定 * */ void unlock(); /** * @brief 用户信息更新 * * @param list 用户信息列表 */ void usersInfoChanged(QList list); /** * @brief 请求锁定 * */ void requestLock(); /** * @brief 请求解锁 * */ void requestUnlock(); /** * @brief 会话活跃状态改变 * * @param isActive true 活跃,false 不活跃 */ void sessionActiveChanged(bool isActive); /** * @brief 准备休眠/唤醒 * * @param isSleep true 休眠,false 唤醒 */ void PrepareForSleep(bool isSleep); void currentUserChanged(QString strUserName); void currentSessionChanged(QString strSession); void blockLogoutInhibitedChanged(); void blockInhibitedChanged(QString lidstate); void batteryStatusChanged(QString iconName); void batteryChanged(QStringList batteryArgs); void upowerServiceReady(); void screenSaverConfChanged(QString strKey, QVariant value); void lockScreenConfChanged(QString strKey, QVariant value); void powerManagerConfChanged(QString strKey, QVariant value); void mateBgConfChanged(QString strKey, QVariant value); void ukccPluginsConfChanged(QString strKey, QVariant value); void themeStyleConfChanged(QString strKey, QVariant value); void sessionConfChanged(QString strKey, QVariant value); void capslockConfChanged(bool state); void pamShowMessage(QString text, int type); void pamShowPrompt(QString text, int type); void pamShowBinaryPrompt(QVariant val,int type); void pamAuthCompleted(); void usdMediaKeysConfChanged(QString strKey, QString value); void usdMediaStateKeysConfChanged(QString strKey, int value); void bioServiceStatusChanged(bool bValid); void bioDeviceChanged(); void bioAuthShowMessage(QString strMsg); void bioAuthStateChanged(int nState); void bioAuthFrameData(QString strData); void bioAuthCompleted(int nUid, bool isSuccess, int nError, int nMaxFailedTime, int nFailedTime); void tabletModeChanged(bool tabletMode); void networkConnectStateChanged(int state); void saverDatetimeEnableChanged(bool isEnable, QString username); void lockDatetimeEnableChanged(bool isEnable, QString username); private: /** * @brief 接口名称 * * @return const char */ const char *staticInterfaceName(); /** * @brief 连接信号槽 * */ void initConnections(); QList ParseUsersInfo(const QJsonObject &objRes); QList ParseSessionsInfo(const QJsonObject &objRes); QList ParseSaverThemes(const QJsonObject &objRes); QStringList ParseBatteryArgs(const QJsonObject &objRes); QStringList ParseSleepLockcheck(const QJsonObject &objRes); QStringList ParseShutdownLockcheck(const QJsonObject &objRes); QStringList ParseLogoutLockcheck(const QJsonObject &objRes); AgreementInfoPtr ParseAgreementInfo(const QJsonObject &objRes); bool ParseLogin1ReqLock(const QJsonObject &objRes); bool ParseLogin1ReqUnLock(const QJsonObject &objRes); bool ParseLogin1PrepareForSleep(const QJsonObject &objRes); bool ParseLogin1SessionActiveChanged(const QJsonObject &objRes); bool ParseCurrentUser(const QJsonObject &objRes); bool ParseCurrentSession(const QJsonObject &objRes); bool ParseBlockLogoutInhibitedChanged(const QJsonObject &objRes); bool ParseBlockInhibitedChanged(const QJsonObject &objRes); bool ParseBatteryStatusChanged(const QJsonObject &objRes); bool ParseBatteryChanged(const QJsonObject &objRes); bool ParseUPowerServiceReady(const QJsonObject &objRes); bool ParseScreenSaverConf(const QJsonObject &objRes); QStringList ParsePhotoPathArgs(const QJsonObject &objRes); bool ParseLockScreenConf(const QJsonObject &objRes); bool ParsePowerManagerConf(const QJsonObject &objRes); bool ParseMateBgConf(const QJsonObject &objRes); bool ParseUkccPluginsConf(const QJsonObject &objRes); bool ParseThemeStyleConf(const QJsonObject &objRes); bool ParseSessionConf(const QJsonObject &objRes); bool ParseKeyboardConf(const QJsonObject &objRes); void ParsePamShowMessage(const QJsonObject &objRes); void ParsePamShowPrompt(const QJsonObject &objRes); void ParsePamShowBinaryPrompt(const QJsonObject &objRes); void ParsePamAuthCompleted(const QJsonObject &objRes); bool ParseUsdMediaKeysConf(const QJsonObject &objRes); bool ParseUsdMediaStateKeysConf(const QJsonObject &objRes); void ParseBioServiceStatus(const QJsonObject &objRes); void ParseBioDeviceChanged(const QJsonObject &objRes); void ParseBioAuthShowMessage(const QJsonObject &objRes); void ParseBioAuthStateChanged(const QJsonObject &objRes); void ParseBioAuthFrameData(const QJsonObject &objRes); void ParseBioAuthCompleted(const QJsonObject &objRes); void ParseTabletModeChanged(const QJsonObject &objRes); void ParseNetWorkConnectChanged(const QJsonObject &objRes); void ParseSaverTimeDataEnableChanged(const QJsonObject &objRes); void ParseLockTimeDataEnableChanged(const QJsonObject &objRes); }; #endif // BACKENDDBUSHELPER_H ukui-screensaver/src/lock-dialog/lockdialogmodel.h0000664000175000017500000004274315172041106021260 0ustar fengfeng/* * Copyright (C) 2023 KylinSoftCo., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LOCKDIALOGMODEL_H #define LOCKDIALOGMODEL_H #include #include "../../src/userinfo.h" #include "../../src/agreementinfo.h" #include "../../src/widgets/screensavermode.h" #include "../../src/common/biodefines.h" #include /** * @brief 锁屏模型类(管理所有功能逻辑和状态) * */ class LockDialogModel : public QObject { Q_OBJECT public: /** * @brief * */ struct CommandLineArgs { bool isSessionIdle = false; /**< 是否会话空闲 */ bool isLock = false; /**< 是否直接锁定 */ bool isLockStartup = false; /**< 是否开机启动锁定 */ bool isScreenSaver = false; /**< 是否屏保 */ bool isLockScreensaver = false; /**< 是否锁定与屏保 */ bool isBlank = false; /**< 是否黑色屏保锁定 */ bool isBlankHasLock = true; /**< 是否黑色屏保需要锁定 */ int nBlankDelay = 0; /**< 黑色屏保延迟锁定时间 */ bool isSessionTools = false; /**< 显示session电源管理界面*/ int isAppBlock = -1; /**< 显示应用阻塞关机/重启界面*/ int isMultiUserBlock = -1; /**< 显示多用户登录阻塞关机/重启界面*/ int isSwitchUser = false; /**< 显示switchuser*/ QString strSwitchToUser = ""; /**< switch to user*/ }; /** * @brief 构造 * * @param parent 父指针 */ explicit LockDialogModel(QObject *parent = nullptr); public: /** * @brief 解析命令行参数 * * @param args 命令行参数列表 * @param cmdArgs 解析后结果 * @return bool 是否解析完成 */ bool parseCmdArguments(QStringList args, CommandLineArgs &cmdArgs); /** * @brief 更新会话状态 * * @param isActive 是否激活 true 是,否则不是 */ void updateSessionState(bool isActive); /** * @brief 会话是否激活 * * @return bool true 是,否则不是 */ bool sessionActive(); /** * @brief 是否是wayland模式 * * @return bool true 是,否则不是 */ inline bool isUseWayland() { return m_isUseWayland; } /** * @brief 更新用户信息列表 * * @param list 用户信息列表 */ void updateUsersInfo(QList list); void updateCapslockState(bool capslockState); void updateUsdMediaKeys(QString keys, QString value); void updateUsdMediaStateKeys(QString keys, int value); void updatePowerButtonKey(QString keys, QString value); void updateBatteryArgs(QStringList batteryArgs); void updateBatteryIconName(QString iconName); void updateIsBattery(bool isBattery); void updateLogoutLockcheck(QStringList logoutLockCheck); void updateSleepLockcheck(QStringList sleepLockcheck); void updateShutdownLockcheck(QStringList shutdownLockcheck); void updateLoggedInUsersCount(); void updateTabletMode(bool tabletMode); /** * @brief 获取用户信息列表 * * @return QList 用户信息列表 */ inline QList usersInfo() { return m_listUsersInfo; } inline int normalUserCount() { int nCount = 0; for (auto user : m_listUsersInfo) { if (!user->isSystemAccount()) { nCount++; } } return nCount; } void updateSessionsInfo(QStringList list); void updateScreensaverMode(QString value); void updateScreensaverTheme(QStringList value); void updateScreensaverImageTSEffect(int value); void updateScreensaverImageSwitchInterval(int value); void updateLockTimeout(int value); void updateIdleDelay(int value); void updateLockEnabled(bool value); void updateSleepActivationEnabled(bool value); void updateLockDatetimeEnable(bool enable); void updateCanSwitchUser(bool canSwitch); inline QStringList sessionsInfo() { return m_listSessions; } inline void updateCurSession(const QString &strSession) { m_strCurSession = strSession; } inline QString currentSession() { return m_strCurSession; } UserInfoPtr findUserByName(const QString &strName); UserInfoPtr findUserById(const uid_t &id); inline QString defaultUserName() { return m_strDefUserName; } inline void updateDefUserName(const QString &strUserName) { m_strDefUserName = strUserName; } inline QString currentUserName() { return m_strCurUserName; } inline void updateCurUserName(const QString &strUserName) { m_strCurUserName = strUserName; } void updateAgreementInfo(AgreementInfoPtr agreementInfo); void updateSystemFontSize(double fontSize) { m_curFontSize = fontSize; } void updateSystemFont(QString font) { m_curFont = font; } void updateSessionLogoutMusic(bool logoutMusic) { m_logoutMusic = logoutMusic; } void updateSessionPoweroffMusic(bool poweroffMusic) { m_poweroffMusic = poweroffMusic; } inline AgreementInfoPtr agreementInfo() { return m_agreementInfo; } inline bool getAgreementWindowShowLoginPrompt() { return m_agreementInfo->showLoginPrompt(); } inline bool getAgreementWindowHideTitle() { return m_agreementInfo->hideTitle(); } inline QString getAgreementWindowPromptTitle() { return m_agreementInfo->promptTitle(); } QString getAgreementWindowText(); inline bool getCapslockState() { return m_capslockState; } inline QString getUsdAreaScreenShotKey() { return m_areaScreenShot; } inline QString getUsdAreaScreenShot2Key() { return m_areaScreenShot2; } inline QString getUsdScreenShotKey() { return m_screenShot; } inline QString getUsdScreenShot2Key() { return m_screenShot2; } inline QString getUsdwindowScreenshotKey() { return m_windowScreenshot; } inline int getUsdMediaRfkillState() { return m_rfkillState; } inline QString getPowerButtonKey() { return m_strButtonPower; } // inline QString getSaverMode() { return m_saverMode; } // inline QString getSaverTheme() { return m_saverTheme; } ScreenSaver *getScreensaver(); void updataCheckSystemUpgrade(bool); inline QStringList getBatteryArgs() { return m_batteryArgs; } inline QString getBatteryIconName() { return m_batteryIconName; } inline bool getIsBattery() { return m_isBattery; } inline QStringList getLogoutLockCheck() { return m_logoutLockcheck; } inline QStringList getSleepLockcheck() { return m_sleepLockcheck; } inline QStringList getShutdownLockcheck() { return m_shutdownLockcheck; } inline int getLoggedInUsersCount() { return m_loggedinUsersCount; } inline QString getCurFont() { return m_curFont; } double getCurFontSize(); static double getPtToPx(); inline int getLockTimeout() { return m_lockTimeout; } inline int getIdleDelay() { return m_nIdleDelay; } inline bool getLockEnabled() { return m_lockEnabled; } inline bool getTabletMode() { return m_tabletMode; } inline bool getSessionLogoutMusic() { return m_logoutMusic; } inline bool getSessionPoweroffMusic() { return m_poweroffMusic; } inline bool getSleepActivationEnabled() { return m_sleepActivationEnabled; } inline bool isSessionTools() { return m_isSessionTools; } inline void setIsSessionTools(bool isTools) { qDebug() << "isTools:"<. * **/ #include "screensaverwndadaptor.h" #include #include #include #include #include #include #include ScreenSaverWndAdaptor::ScreenSaverWndAdaptor(FullBackgroundWidget *parent) : QDBusAbstractAdaptor(parent), m_parentWidget(parent) { // constructor setAutoRelaySignals(true); connect(m_parentWidget, SIGNAL(StartupModeChanged(bool)), this, SIGNAL(StartupModeChanged(bool))); } ScreenSaverWndAdaptor::~ScreenSaverWndAdaptor() {} int ScreenSaverWndAdaptor::RegisteSubWnd(quint64 uWndId) { int nWndCount = m_parentWidget->RegisteSubWnd(uWndId); if (nWndCount >= 0) { Q_EMIT SubWndChanged(nWndCount); } return nWndCount; } int ScreenSaverWndAdaptor::UnRegisteSubWnd(quint64 uWndId) { int nWndCount = m_parentWidget->UnRegisteSubWnd(uWndId); if (nWndCount >= 0) { Q_EMIT SubWndChanged(nWndCount); } return nWndCount; } QList ScreenSaverWndAdaptor::GetSubWndIds() { return m_parentWidget->GetSubWndIds(); } bool ScreenSaverWndAdaptor::IsStartupMode() { return m_parentWidget->IsStartupMode(); } ukui-screensaver/src/lock-dialog/lockdialogmodel.cpp0000664000175000017500000005427415172041106021615 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "lockdialogmodel.h" #include #include #include #include #include #include #include #include #include #include "../../src/common/utils.h" #include "../../src/common/definetypes.h" #include LockDialogModel::LockDialogModel(QObject *parent) : QObject(parent), m_isUseWayland(QString(qgetenv("XDG_SESSION_TYPE")) == "wayland") { } bool LockDialogModel::parseCmdArguments(QStringList args, CommandLineArgs &cmdArgs) { // 命令行参数解析 QCommandLineParser parser; parser.setApplicationDescription(QGuiApplication::translate("main", "Dialog for the ukui ScreenSaver.")); parser.addHelpOption(); parser.addVersionOption(); parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); QCommandLineOption lockOption( QStringLiteral("lock"), QGuiApplication::translate("main", "lock the screen immediately")); QCommandLineOption lstOption( QStringLiteral("lock-startup"), QGuiApplication::translate("main", "lock the screen immediately")); QCommandLineOption sessionIdleOption( QStringLiteral("session-idle"), QGuiApplication::translate("main", "activated by session idle signal")); QCommandLineOption lscreensaverOption( QStringLiteral("lock-screensaver"), QGuiApplication::translate("main", "lock the screen and show screensaver immediately")); QCommandLineOption screensaverOption( QStringLiteral("screensaver"), QGuiApplication::translate("main", "show screensaver immediately")); QCommandLineOption blankOption( QStringLiteral("blank"), QGuiApplication::translate("main", "lock the screen and show screensaver immediately")); QCommandLineOption delayOption( QStringLiteral("delay"), QGuiApplication::translate("main", "show blank screensaver immediately and delay time to show lock"), QGuiApplication::translate("delay", "how long to show lock"), "0"); QCommandLineOption hasLockOption( QStringLiteral("has-lock"), QGuiApplication::translate("main", "show blank screensaver immediately and if lock"), QGuiApplication::translate("has-lock", "if show lock"), "1"); QCommandLineOption sessiontoolsOption( { "t", QStringLiteral("session-tools") }, QCoreApplication::translate("main", "show the session tools")); QCommandLineOption switchuserOption( QStringLiteral("switchuser"), QGuiApplication::translate("main", "show switch user window")); QCommandLineOption switchToUserOption( QStringLiteral("switchtouser"), QGuiApplication::translate("main", "switch to user and show window"), QGuiApplication::translate("switchtouser", "switch to someone user"), ""); QCommandLineOption appBlockOption( { "a", QStringLiteral("app-block") }, QCoreApplication::translate("main", "show the app block window"), QGuiApplication::translate("action", "which block type"), "type"); QCommandLineOption multiUsersOption( { "m", QStringLiteral("multiusers-block") }, QCoreApplication::translate("main", "show the multiUsers block window"), QGuiApplication::translate("action", "which block type"), "type"); parser.addOptions({ lockOption, lstOption, sessionIdleOption, screensaverOption, blankOption, lscreensaverOption, delayOption, hasLockOption, sessiontoolsOption, switchuserOption, appBlockOption, multiUsersOption, switchToUserOption }); parser.process(args); QString strHostCloundPlatform = getHostCloudPlatform(); qDebug() << "HostCloundPlatform:" << strHostCloundPlatform; if (strHostCloundPlatform == "ctyun") { // 天翼云不允许锁屏 if (!parser.isSet(lstOption)) { return false; } } else { if (!parser.isSet(sessionIdleOption) && !parser.isSet(lockOption) && !parser.isSet(lstOption) && !parser.isSet(screensaverOption) && !parser.isSet(lscreensaverOption) && !parser.isSet(blankOption) && !parser.isSet(sessiontoolsOption) && !parser.isSet(switchuserOption) && !parser.isSet(appBlockOption) && !parser.isSet(multiUsersOption) && !parser.isSet(switchToUserOption)) { return false; } } cmdArgs.isSessionIdle = parser.isSet(sessionIdleOption); cmdArgs.isLock = parser.isSet(lockOption); cmdArgs.isLockStartup = parser.isSet(lstOption); cmdArgs.isScreenSaver = parser.isSet(screensaverOption); cmdArgs.isLockScreensaver = parser.isSet(lscreensaverOption); cmdArgs.isBlank = parser.isSet(blankOption); cmdArgs.isSessionTools = parser.isSet(sessiontoolsOption); cmdArgs.isSwitchUser = parser.isSet(switchuserOption); if (parser.isSet(hasLockOption)) { int nHasLockValue = parser.value(hasLockOption).toInt(); if (nHasLockValue == 0) { cmdArgs.isBlankHasLock = false; } else { cmdArgs.isBlankHasLock = true; } } if (parser.isSet(delayOption)) { cmdArgs.nBlankDelay = parser.value(delayOption).toInt(); } if (parser.isSet(appBlockOption)) { QString opValue = parser.value(appBlockOption); if (opValue == "Reboot") { cmdArgs.isAppBlock = 0; } else if (opValue == "PowerOff") { cmdArgs.isAppBlock = 1; } else if (opValue == "Suspend") { cmdArgs.isAppBlock = 2; } else if (opValue == "Hibernate") { cmdArgs.isAppBlock = 3; } else if (opValue == "Logout") { cmdArgs.isAppBlock = 4; } } if (parser.isSet(multiUsersOption)) { QString opValue = parser.value(multiUsersOption); if (opValue == "Reboot") { cmdArgs.isMultiUserBlock = 0; } else if (opValue == "PowerOff") { cmdArgs.isMultiUserBlock = 1; } } if (parser.isSet(switchToUserOption)) { cmdArgs.strSwitchToUser = parser.value(switchToUserOption); } return true; } void LockDialogModel::onSessionActiveChanged(bool isActive) { m_isSessionActive = isActive; Q_EMIT sessionActiveChanged(isActive); } void LockDialogModel::updateSessionState(bool isActive) { m_isSessionActive = isActive; } bool LockDialogModel::sessionActive() { return m_isSessionActive; } void LockDialogModel::onRunningMessage(const QString &strMsg) { CommandLineArgs cmdArgs; QStringList arguments = strMsg.split(","); qInfo() << "onRunningMessage:" << arguments; if (!parseCmdArguments(arguments, cmdArgs)) return; if (cmdArgs.isBlank) { Q_EMIT showBlankScreensaver(cmdArgs.nBlankDelay, cmdArgs.isBlankHasLock); } if (cmdArgs.isLock) { Q_EMIT showLock(false); } if (cmdArgs.isLockStartup) { Q_EMIT showLock(true); } if (cmdArgs.isSessionIdle) { Q_EMIT showSessionIdle(); } if (cmdArgs.isLockScreensaver) { Q_EMIT showLockScreensaver(); } if (cmdArgs.isScreenSaver) { Q_EMIT showScreensaver(); } if (cmdArgs.isSessionTools) { Q_EMIT showSessionTools(); } if (cmdArgs.isAppBlock != -1) { Q_EMIT showAppBlockWindow(cmdArgs.isAppBlock); } if (cmdArgs.isMultiUserBlock != -1) { Q_EMIT showMultiUsersBlockWindow(cmdArgs.isMultiUserBlock); } if (cmdArgs.isSwitchUser) { Q_EMIT showSwitchUserLock(); } KillFocusOfKydroid(); } UserInfoPtr LockDialogModel::findUserByName(const QString &strName) { for (auto user : m_listUsersInfo) { if (user->name() == strName) { return user; } } struct passwd *userInfo = getpwnam(strName.toLatin1().data()); if (userInfo) { UserInfoPtr userPtr = std::make_shared(); userPtr->updateUid(userInfo->pw_uid); userPtr->updateName(strName); if (userInfo->pw_gecos && userInfo->pw_gecos[0] != '\0') { QString geoces = QString(userInfo->pw_gecos); QStringList list = geoces.split(QLatin1Char(',')); if (list.size() > 0) { userPtr->updateFullName(list[0]); } } return userPtr; } return nullptr; } UserInfoPtr LockDialogModel::findUserById(const uid_t &id) { for (auto user : m_listUsersInfo) { if (user->uid() == id) { return user; } } struct passwd *userInfo = getpwuid(id); if (userInfo) { UserInfoPtr userPtr = std::make_shared(); userPtr->updateUid(userInfo->pw_uid); userPtr->updateName(userInfo->pw_name); if (userInfo->pw_gecos && userInfo->pw_gecos[0] != '\0') { QString geoces = QString(userInfo->pw_gecos); QStringList list = geoces.split(QLatin1Char(',')); if (list.size() > 0) { userPtr->updateFullName(list[0]); } } return userPtr; } return nullptr; } void LockDialogModel::updateSessionsInfo(QStringList list) { m_listSessions = list; } void LockDialogModel::updateUsersInfo(QList list) { for (auto userinfo : list) { if (m_listUsersInfo.contains(userinfo)) { UserInfoPtr changedUserInfo = findUserByName(userinfo->name()); if (changedUserInfo) { changedUserInfo->updateUid(userinfo->uid()); changedUserInfo->updateFullName(userinfo->fullName()); changedUserInfo->updateHeadImage(userinfo->headImage()); changedUserInfo->updateLang(userinfo->lang()); if (QString(getenv("USER")) == userinfo->name()) { changedUserInfo->updateLoggedIn(true); } else { changedUserInfo->updateLoggedIn(userinfo->isLoggedIn()); } changedUserInfo->updateBackground(userinfo->backGround()); changedUserInfo->updateGreeterBackground(userinfo->greeterBackGround()); changedUserInfo->updateBackgroundColor(userinfo->backGroundColor()); changedUserInfo->updateSystemAccount(userinfo->isSystemAccount()); } } else { if (QString(getenv("USER")) == userinfo->name()) { userinfo->updateLoggedIn(true); } m_listUsersInfo.append(userinfo); } } foreach (auto userinfo, m_listUsersInfo) { if (!list.contains(userinfo)) { m_listUsersInfo.removeOne(userinfo); } } Q_EMIT usersInfoChanged(); } void LockDialogModel::updateAgreementInfo(AgreementInfoPtr agreementInfo) { m_agreementInfo = agreementInfo; Q_EMIT agreementInfoChanged(); } void LockDialogModel::onUsersInfoChanged(QList list) { for (auto userinfo : list) { if (m_listUsersInfo.contains(userinfo)) { UserInfoPtr changedUserInfo = findUserByName(userinfo->name()); if (changedUserInfo) { changedUserInfo->updateUid(userinfo->uid()); changedUserInfo->updateFullName(userinfo->fullName()); changedUserInfo->updateHeadImage(userinfo->headImage()); changedUserInfo->updateLang(userinfo->lang()); if (QString(getenv("USER")) == userinfo->name()) { changedUserInfo->updateLoggedIn(true); } else { changedUserInfo->updateLoggedIn(userinfo->isLoggedIn()); } changedUserInfo->updateBackground(userinfo->backGround()); changedUserInfo->updateGreeterBackground(userinfo->greeterBackGround()); changedUserInfo->updateBackgroundColor(userinfo->backGroundColor()); } } else { if (QString(getenv("USER")) == userinfo->name()) { userinfo->updateLoggedIn(true); } m_listUsersInfo.append(userinfo); } } foreach (auto userinfo, m_listUsersInfo) { if (!list.contains(userinfo)) { m_listUsersInfo.removeOne(userinfo); } } Q_EMIT usersInfoChanged(); } void LockDialogModel::onCurUserChanged(const QString &strUserName) { updateCurUserName(strUserName); Q_EMIT currentUserChanged(strUserName); } void LockDialogModel::onCurSessionChanged(const QString &strSession) { updateCurSession(strSession); Q_EMIT currentSessionChanged(strSession); } QString LockDialogModel::getAgreementWindowText() { if (!m_agreementInfo->promptText().isEmpty()) { return m_agreementInfo->promptText(); } if (m_agreementInfo->promptTextFilePath().isEmpty()) return ""; QFile file(m_agreementInfo->promptTextFilePath()); if (!file.exists()) return ""; file.open(QFile::ReadOnly); QString str(file.readAll()); if (str == "") return ""; file.close(); return str; } ScreenSaver *LockDialogModel::getScreensaver() { QStringList modeStr{ "blank-only", "random", "single", "image", "default-ukui", "default-ukui-custom" }; ScreenSaver *saver = new ScreenSaver; int index = modeStr.indexOf(m_saverMode); saver->mode = SaverMode(index); saver->interval = m_imageSwitchInterval; saver->effect = TransitionEffect(m_imageTSEffect); switch (index) { case SAVER_BLANK_ONLY: break; case SAVER_RANDOM: { if (m_saverTheme.count() == 0) { saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; break; } int index = QRandomGenerator::global()->bounded(m_saverTheme.count()); while (QString::compare(m_saverTheme[index], "kyccss-personal-slideshow") == 0) { index = QRandomGenerator::global()->bounded(m_saverTheme.count()); } saver->path = getXScreensaverPath(m_saverTheme[index]); break; } case SAVER_SINGLE: if (m_saverTheme.count() == 0) { saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; break; } saver->path = getXScreensaverPath(m_saverTheme[0]); break; case SAVER_IMAGE: { QString lang = qgetenv("LANG"); if (!lang.isEmpty()) { qDebug() << "lang = " << lang; if (lang.contains("zh_CN")) { saver->path = QDir::homePath() + "/图片"; break; } } saver->path = QDir::homePath() + "/" + QStandardPaths::displayName(QStandardPaths::PicturesLocation); break; } case SAVER_DEFAULT: saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; break; case SAVER_DEFAULT_CUSTOM: saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; break; default: break; } return saver; } double LockDialogModel::getCurFontSize() { double defaultFontSize = getDefaultFontSize(); return m_curFontSize - defaultFontSize; } double LockDialogModel::getPtToPx() { double m_ptTopx = 1.0; if (QApplication::primaryScreen() && QApplication::primaryScreen()->logicalDotsPerInch() > 0) { m_ptTopx = 72 / (QApplication::primaryScreen()->logicalDotsPerInch()); } return m_ptTopx; } QString LockDialogModel::getXScreensaverPath(const QString &theme) { /* screensavers-ukui-binaryring => binaryring */ QStringList strs = theme.split("-"); QString str = strs.at(strs.size() - 1); QString filePath = QString("%1/%2").arg(XSCREENSAVER_DIRNAME, str); // 除了判断gsetting值是否为空,还需要判断屏保文件是否存在,不存在就使用默认屏保。 if (QFile(filePath).exists()) return filePath; else return "/usr/lib/ukui-screensaver/ukui-screensaver-default"; } void LockDialogModel::onLidstateChanged(const QString &lidstate) { Q_EMIT lidstateChanged(lidstate); } void LockDialogModel::onBatteryStatusChanged(const QString &iconName) { Q_EMIT batteryStatusChanged(iconName); } void LockDialogModel::onBatteryChanged(const QStringList &batteryArgs) { Q_EMIT batteryChanged(batteryArgs); } void LockDialogModel::onLockScreenConfChanged(const QString &key, QVariant value) { if (key == KEY_MODE) { m_saverMode = value.toString(); } else if (key == KEY_THEMES) { m_saverTheme = value.toStringList(); } else if (key == KEY_IMAGE_TRANSITION_EFFECT) { m_imageTSEffect = value.toInt(); } else if (key == KEY_IMAGE_SWITCH_INTERVAL) { m_imageSwitchInterval = value.toInt(); } else if (key == KEY_LOCK_TIMEOUT) { m_lockTimeout = value.toInt(); } else if (key == KEY_LOCK_ENABLED) { m_lockEnabled = value.toBool(); } else if (key == KEY_SLEEP_ACTIVATION_ENABLED) { m_sleepActivationEnabled = value.toBool(); } else if (key == KEY_IDLE_DELAY) { m_nIdleDelay = value.toInt(); } } void LockDialogModel::onThemeStyleConfChanged(const QString &key, QVariant value) { if (key == KEY_SYSTEM_FONT_SIZE) { m_curFontSize = value.toDouble(); double defaultFontSize = getDefaultFontSize(); Q_EMIT fontSizeChanged(m_curFontSize - defaultFontSize); } else if (key == KEY_SYSTEM_FONT) { m_curFont = value.toString(); Q_EMIT fontChanged(m_curFont); } } void LockDialogModel::onCapslockStateChanged(const bool capslockState) { Q_EMIT capslockStateChanged(capslockState); } void LockDialogModel::onUsdMediaStateKeysChanged(const QString &key, const int &value) { Q_EMIT usdMediaStateKeysChanged(key, value); } void LockDialogModel::onUsdMediaKeysChanged(const QString &key, const QString &value) { Q_EMIT usdMediaKeysChanged(key, value); } void LockDialogModel::onPowerManagerConfChanged(const QString &key, QVariant value) { if (key == KEY_BUTTON_POWER) { m_strButtonPower = value.toString(); Q_EMIT powerButoonKeyChanged(m_strButtonPower); } } void LockDialogModel::onTabletModeChanged(bool tabletMode) { Q_EMIT tabletModeChanged(tabletMode); } void LockDialogModel::onLockDatetimeEnableChanged(bool enable, QString username) { Q_EMIT lockDatetimeEnableChanged(enable, username); } void LockDialogModel::onSessionConfChanged(const QString &key, QVariant value) { if (key == KEY_SESSION_LOGOUT_MUSIC) { m_logoutMusic = value.toBool(); } else if (key == KEY_SESSION_POWEROFF_MUSIC) { m_poweroffMusic = value.toBool(); } } void LockDialogModel::updateCapslockState(bool capslockState) { m_capslockState = capslockState; } void LockDialogModel::onSecondRunParam(const QString &str) { Q_EMIT SecondRunParam(str); } void LockDialogModel::updateUsdMediaKeys(QString keys, QString value) { if (keys == KEY_AREA_SCREENSHOT) { m_areaScreenShot = value; } else if (keys == KEY_AREA_SCREENSHOT2) { m_areaScreenShot2 = value; } else if (keys == KEY_SCREEN_SHOT) { m_screenShot = value; } else if (keys == KEY_SCREEN_SHOT2) { m_screenShot2 = value; } else if (keys == KEY_WINDOW_SCREENSHOT) { m_windowScreenshot = value; } } void LockDialogModel::updateUsdMediaStateKeys(QString keys, int value) { if (keys == KEY_RFKILL_STATE) { m_rfkillState = value; } } void LockDialogModel::updatePowerButtonKey(QString keys, QString value) { if (keys == KEY_BUTTON_POWER) { m_strButtonPower = value; } } void LockDialogModel::updateLockEnabled(bool value) { m_lockEnabled = value; } void LockDialogModel::updateSleepActivationEnabled(bool value) { m_sleepActivationEnabled = value; } void LockDialogModel::updateLockTimeout(int value) { m_lockTimeout = value; } void LockDialogModel::updateIdleDelay(int value) { m_nIdleDelay = value; } void LockDialogModel::updateScreensaverMode(QString value) { m_saverMode = value; } void LockDialogModel::updateScreensaverTheme(QStringList value) { m_saverTheme = value; } void LockDialogModel::updateScreensaverImageTSEffect(int value) { m_imageTSEffect = value; } void LockDialogModel::updateScreensaverImageSwitchInterval(int value) { m_imageSwitchInterval = value; } void LockDialogModel::updateBatteryArgs(QStringList batteryArgs) { m_batteryArgs = batteryArgs; } void LockDialogModel::updateBatteryIconName(QString iconName) { m_batteryIconName = iconName; } void LockDialogModel::updateIsBattery(bool isBattery) { m_isBattery = isBattery; } void LockDialogModel::updateLogoutLockcheck(QStringList logoutLockCheck) { qDebug() << "updatelogoutlockcheck" << logoutLockCheck; m_logoutLockcheck = logoutLockCheck; } void LockDialogModel::updateSleepLockcheck(QStringList sleepLockcheck) { m_sleepLockcheck = sleepLockcheck; } void LockDialogModel::updateShutdownLockcheck(QStringList shutdownLockcheck) { m_shutdownLockcheck = shutdownLockcheck; } void LockDialogModel::updateLoggedInUsersCount() { m_loggedinUsersCount = 0; if (!m_listUsersInfo.isEmpty()) { for (auto user : m_listUsersInfo) { if (user->isLoggedIn()) { m_loggedinUsersCount++; } } } } void LockDialogModel::updateTabletMode(bool tabletMode) { m_tabletMode = tabletMode; } void LockDialogModel::updateLockDatetimeEnable(bool enable) { m_lockDatetimeEnable = enable; } void LockDialogModel::updateCanSwitchUser(bool canSwitch) { m_canSwitchUser = canSwitch; } ukui-screensaver/src/lock-dialog/main.cpp0000664000175000017500000001672115172041106017403 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include "utils.h" #include "commonfunc.h" #include "lockdialogmodel.h" #include "lockdialogperformer.h" #include "fullbackgroundwidget.h" #include "screensaverwndadaptor.h" #include "pluginsloader.h" #include "languagesetting.h" #include "displayservice.h" FullBackgroundWidget *window = nullptr; int main(int argc, char *argv[]) { if (argc < 2) return 0; syslog(LOG_INFO, "[ukui-screensaver-dialog] startup!!"); initUkuiLog4qt("ukui-screensaver-dialog"); // 重启或关机时不被session关掉 qunsetenv("SESSION_MANAGER"); qputenv("QT_QPA_PLATFORMTHEME", QByteArray("ukui")); #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif LockDialogModel lockDialogModel; LockDialogPerformer performer(&lockDialogModel); if (isGreeterMode()) { syslog(LOG_INFO, "[ukui-screensaver-dialog] sync display param!!"); DisplayService::instance(&lockDialogModel)->setCurUserName(lockDialogModel.defaultUserName()); syslog(LOG_INFO, "[ukui-screensaver-dialog] sync display param end!!"); } QString strDisplay = ""; if (QString(qgetenv("XDG_SESSION_TYPE")) == "wayland") { strDisplay = QLatin1String(getenv("WAYLAND_DISPLAY")); } else { strDisplay = QLatin1String(getenv("DISPLAY")); } QString id = QString("ukui-screensaver-dialog" + strDisplay); QtSingleApplication app(id, argc, argv); // 锁屏后端退出时,锁屏前端也退出 prctl(PR_SET_PDEATHSIG, SIGKILL); performer.initExtraData(); LockDialogModel::CommandLineArgs cmdArgs; if (!lockDialogModel.parseCmdArguments(app.arguments(), cmdArgs)) { return 0; } if (!isGreeterMode()) { // 需要判断参数是否是session-tools if (!cmdArgs.isSessionTools) { // 试用模式不起锁屏 checkIslivecd(); } } if (app.isRunning()) { QString strArguments = QApplication::arguments().join(","); app.sendMessage(strArguments); qInfo() << "ukui screensaver dialog is running!"; return EXIT_SUCCESS; } if (!lockDialogModel.isUseWayland()) { setCursorCenter(); } // 加载插件实例 PluginsLoader *pluginsLoader = &PluginsLoader::instance(); pluginsLoader->start(QThread::HighestPriority); pluginsLoader->wait(); QApplication::setSetuidAllowed(true); QObject::connect(&lockDialogModel, &LockDialogModel::requestUnlockSession, [=]() { if (window) { window->onCloseScreensaver(); } else { exit(0); } }); if (!lockDialogModel.sessionActive()) { QEventLoop *loopTemp = new QEventLoop(&app); QObject::connect(&lockDialogModel, &LockDialogModel::sessionActiveChanged, [loopTemp](bool isActive) { qDebug() << "sessionActiveChanged:" << isActive; if (isActive && loopTemp->isRunning()) { loopTemp->quit(); } }); QObject::connect(&lockDialogModel, &LockDialogModel::requestLockSession, [loopTemp, &lockDialogModel]() { qDebug() << "session requestLock:" << lockDialogModel.sessionActive(); if (loopTemp->isRunning() && lockDialogModel.sessionActive()) { loopTemp->quit(); } }); loopTemp->exec(); } QObject::connect( &app, SIGNAL(messageReceived(const QString &)), &lockDialogModel, SLOT(onRunningMessage(const QString &))); qInfo() << "Start " << app.arguments(); // 加载翻译文件 UserInfoPtr ptrUserInfo = lockDialogModel.findUserByName(lockDialogModel.defaultUserName()); if (ptrUserInfo) { LanguageSetting::instance()->onLanguageChanged(ptrUserInfo->lang()); } else { LanguageSetting::instance()->onLanguageChanged(QLocale::system().name()); } window = new FullBackgroundWidget(&lockDialogModel, cmdArgs.isLockStartup); // 注册DBus ScreenSaverWndAdaptor adaptorWnd(window); QDBusConnection service = QDBusConnection::sessionBus(); QString sessionDbus = SSWND_DBUS_SERVICE; if (!service.registerService(SSWND_DBUS_SERVICE)) { QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); sessionDbus = QString("%1%2").arg(QString(SSWND_DBUS_SERVICE)).arg(displayNum); if (!service.registerService(sessionDbus)) { qDebug() << service.lastError().message(); return 1; } } if (!service.registerObject( SSWND_DBUS_PATH, SSWND_DBUS_SERVICE, &adaptorWnd, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) { qDebug() << service.lastError().message(); return 1; } qDebug() << service.baseService(); if (cmdArgs.isBlank) { Q_EMIT lockDialogModel.showBlankScreensaver(cmdArgs.nBlankDelay, cmdArgs.isBlankHasLock); } #ifndef USE_INTEL syslog(LOG_INFO, "[ukui-screensaver-dialog] window show!!"); window->show(); window->activateWindow(); syslog(LOG_INFO, "[ukui-screensaver-dialog] window show done!!"); #endif if (!cmdArgs.strSwitchToUser.isEmpty()) { Q_EMIT lockDialogModel.showSwitchToUser(cmdArgs.strSwitchToUser); } if (cmdArgs.isLock) { Q_EMIT lockDialogModel.showLock(false); } if (cmdArgs.isLockStartup) { Q_EMIT lockDialogModel.showLock(true); } if (cmdArgs.isSessionIdle) { Q_EMIT lockDialogModel.showSessionIdle(); } if (cmdArgs.isLockScreensaver) { Q_EMIT lockDialogModel.showLockScreensaver(); } if (cmdArgs.isScreenSaver) { Q_EMIT lockDialogModel.showScreensaver(); } if (cmdArgs.isSessionTools) { Q_EMIT lockDialogModel.showSessionTools(); } if (cmdArgs.isAppBlock != -1) { Q_EMIT lockDialogModel.showAppBlockWindow(cmdArgs.isAppBlock); } if (cmdArgs.isMultiUserBlock != -1) { Q_EMIT lockDialogModel.showMultiUsersBlockWindow(cmdArgs.isMultiUserBlock); } if (cmdArgs.isSwitchUser) { Q_EMIT lockDialogModel.showSwitchUserLock(); } KillFocusOfKydroid(); syslog(LOG_INFO, "[ukui-screensaver-dialog] application event loop begin!!"); return app.exec(); } ukui-screensaver/src/lock-dialog/pam-tally.c0000664000175000017500000002566215172041106020023 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "pam-tally.h" #include #include #include #include #include #include #include #include #include char shm_tally_real[128]; #define FILE_MODE (S_IRUSR | S_IWUSR) #define CONFIG_FILE "/usr/share/lightdm/lightdm.conf.d/96-kylin-setting.conf" struct tallylog { char fail_line[52]; /* rhost or tty of last failure */ uint16_t reserved; /* reserved for future use */ uint16_t fail_cnt; /* failures since last success */ uint64_t fail_time; /* time of last failure */ }; static pam_tally g_tally_info; static int get_is_open_other_authentication() { char buf[128]; FILE *config_file; if( (config_file = fopen(CONFIG_FILE, "r")) == NULL) { //gs_debug("open %s failed", CONFIG_FILE); return 0; } int open_other_authentication = 0; while(fgets(buf, sizeof(buf), config_file)) { if(strnlen(buf, 1024) == 0 || buf[0] == '#') { memset(buf, sizeof(buf), 0); continue; } if(buf[strnlen(buf, 1024)-1] == '\n') buf[strnlen(buf, 1024)-1] = '\0'; char *p = strchr(buf, '='); if(!p) continue; *p = '\0'; size_t len = strnlen(buf, 1024); if(len == 0) continue; //去掉=之前的空格 while(len--) if(buf[len] == ' ' || buf[len] == '\t') buf[len] = '\0'; if(strcmp(buf, "open-other-authentication") != 0) continue; p++; len = strnlen(p, 1024); if(len == 0) break; //去掉等号之后的空格 while(*p == ' ' || *p == '\t') { p++; len--; } //去掉尾部空格 while(len--) if(*(p+len) == ' ' || *(p+len) == '\t') *(p+len) = '\0'; if(*p == '0') break; if(*p == '1') { open_other_authentication = 1; break; } } fclose(config_file); return open_other_authentication; } static int get_pam_tally(int *deny, int *unlock_time , int *root_unlock_time) { char buf[128]; FILE *auth_file; if( (auth_file = fopen("/etc/pam.d/common-auth", "r")) == NULL) return -1; while(fgets(buf, sizeof(buf), auth_file)) { if(strnlen(buf, 1024) == 0 || buf[0] == '#') continue; if(!strstr(buf, "deny")) continue; char *ptr = strtok(buf, " \t"); while(ptr) { if(strncmp(ptr, "deny=", 5)==0){ sscanf(ptr, "deny=%d", deny); // fprintf(stderr, "-------------------- deny=%d\n", *deny); } if(strncmp(ptr, "unlock_time=", 12)==0){ sscanf(ptr, "unlock_time=%d", unlock_time); // fprintf(stderr, "-------------------- unlock_time=%d\n", *unlock_time); } if(strncmp(ptr, "root_unlock_time=", 17)==0){ sscanf(ptr, "root_unlock_time=%d", root_unlock_time); // fprintf(stderr, "-------------------- root_unlock_time=%d\n", *root_unlock_time); } ptr = strtok(NULL, " \t"); } fclose(auth_file); return 1; } fclose(auth_file); return 0; } static void set_shm_tally_real() { // sprintf(shm_tally_real, "%s_%d", SHM_TALLY, getuid()); snprintf(shm_tally_real, sizeof(shm_tally_real), "%s_%d", SHM_TALLY, getuid()); } int pam_tally_init() { #if 1 int deny = 0, unlock_time = 0 , root_unlock_time = 0; if (!get_pam_tally(&deny, &unlock_time,&root_unlock_time)) { return 0; } g_tally_info.deny = deny; g_tally_info.unlock_time = unlock_time; g_tally_info.failed = 0; g_tally_info.lock_start_time = 0; g_tally_info.root_unlock_time = root_unlock_time; return 1; #else int fd; int deny = 0, unlock_time = 0 , root_unlock_time = 0; pam_tally *tally_ptr; set_shm_tally_real(); printf("shm path =========== : %s\n", shm_tally_real); shm_unlink(shm_tally_real); if(get_is_open_other_authentication()) { return 0; } if(!get_pam_tally(&deny, &unlock_time,&root_unlock_time)) { return 0; } if(deny <= 0) deny = 0; if(unlock_time <= 0) unlock_time = 0; if( (fd = shm_open(shm_tally_real, O_RDWR | O_CREAT, FILE_MODE)) == -1) { printf("shm_open error: %s\n", strerror(errno)); return -1; } ftruncate(fd, sizeof(pam_tally)); if( (tally_ptr = mmap(NULL, sizeof(pam_tally), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { close(fd); return -1; } close(fd); tally_ptr->deny = deny; tally_ptr->unlock_time = unlock_time; tally_ptr->failed = 0; tally_ptr->lock_start_time = 0; tally_ptr->root_unlock_time = root_unlock_time; return 1; #endif } static pam_tally* pam_tally_memory() { int fd; pam_tally *tally_ptr; set_shm_tally_real(); if( (fd = shm_open(shm_tally_real, O_RDWR, FILE_MODE)) == -1) { return NULL; } if( (tally_ptr = mmap(NULL, sizeof(pam_tally), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { close(fd); return NULL; } close(fd); return tally_ptr; } int pam_tally_is_enbled() { int fd; set_shm_tally_real(); if( (fd = shm_open(shm_tally_real, O_RDONLY, FILE_MODE)) == -1) { printf("shm_open error: %s\n", strerror(errno)); close(fd); return 0; } pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL){ close(fd); return -1; } if(tally_ptr->deny == 0 || tally_ptr->unlock_time == 0){ close(fd); return 0; } close(fd); return 1; } int pam_tally_add_failed() { pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL) return -1; tally_ptr->failed++; //如果失败次数达到上限,开始计时 if(tally_ptr->failed >= tally_ptr->deny) tally_ptr->lock_start_time = time(NULL); return 0; } int pam_tally_clear_failed() { pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL) return -1; tally_ptr->failed = 0; tally_ptr->lock_start_time = 0; return 0; } int pam_tally_failure_is_out() { pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL) return -1; return (tally_ptr->failed >= tally_ptr->deny ? 1 : 0); } int pam_tally_deny() { pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL) return -1; return tally_ptr->deny; } int pam_tally_failed_count() { pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL) return -1; return tally_ptr->failed; } int pam_tally_unlock_time() { pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL) return -1; printf("########################### unlock time = %d\n", tally_ptr->unlock_time); return tally_ptr->unlock_time; } int pam_tally_is_canUnlock() { pam_tally *tally_ptr; if((tally_ptr = pam_tally_memory()) == NULL) return -1; if(tally_ptr->failed >= tally_ptr->deny && time(NULL) - tally_ptr->lock_start_time < tally_ptr->unlock_time) return 0; return 1; } int pam_modutil_read(int fd, char *buffer, int count) { int block, offset = 0; while (count > 0) { block = read(fd, &buffer[offset], count); if (block < 0) { if (errno == EINTR) continue; return block; } if (block == 0) return offset; offset += block; count -= block; } return offset; } void get_tally(uid_t uid, int *tfile, struct tallylog *tally) { char filename[50]={0}; const char tempPath[] = "tmp"; snprintf(filename, sizeof(filename), "/%s/.tallylog.d/.%d", tempPath, uid); void *void_tally = tally; if ((*tfile = open(filename, O_RDONLY)) == -1){ fprintf(stderr, "lseek tallylog failed,Re-open the new file, uid = %d \n",uid); snprintf(filename, sizeof(filename), "/%s/.tallylog", tempPath); if ((*tfile = open(filename, O_RDONLY)) == -1){ fprintf(stderr, "open tallylog failed:%s \n", strerror(errno)); return ; } } if (lseek(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET) == (off_t)-1) { fprintf(stderr, "lseek tallylog failed \n"); snprintf(filename, sizeof(filename), "/%s/.tallylog", tempPath); close(*tfile); return ; } if (pam_modutil_read(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) { fprintf(stderr, "read tallylog failed \n"); memset(tally, 0, sizeof(*tally)); } close(*tfile); tally->fail_line[sizeof(tally->fail_line)-1] = '\0'; } int pam_tally_unlock_time_left(uid_t userID, int *fail_cnt,int *left_time, int *deny, int *fail_time, int *unlock_time1) { pam_tally *tally_ptr; #if 1 tally_ptr = &g_tally_info; #else if((tally_ptr = pam_tally_memory()) == NULL) return 0; #endif int unlock_time = tally_ptr->unlock_time; *deny = tally_ptr->deny; if(unlock_time == 0) return 0; int tfile = -1; struct tallylog tally; tally.reserved = 0; tally.fail_cnt = 0; tally.fail_time = 0; get_tally(userID,&tfile,&tally); if(tally.fail_cnt<*deny) return 0; //连续输错,时间累加 if (tally.fail_cnt >= (*deny)*2 && tally.fail_cnt < (*deny)*3){ unlock_time = unlock_time*2; } else if(tally.fail_cnt >= (*deny)*3 && tally.fail_cnt < (*deny)*4){ unlock_time = unlock_time*8; } else if (tally.fail_cnt >= (*deny)*4){ *fail_cnt = 0xFFFF;//永久锁定 return 0; } int unlock_time_left = unlock_time - ((long)time(NULL) - tally.fail_time); // fprintf(stderr, "unlock_time:%d, (long)time(NULL):%d, tally.fail_time:%d\n",unlock_time,(long)time(NULL),tally.fail_time); *fail_time = tally.fail_time; *unlock_time1 = unlock_time; *left_time = unlock_time_left > 0 ? unlock_time_left : 0; *fail_cnt = tally.fail_cnt; return *left_time; } int pam_tally_root_unlock_time_left(uid_t userID, int *fail_cnt,int *left_time, int *deny1, int *fail_time, int *unlock_time1) { pam_tally *tally_ptr; #if 1 tally_ptr = &g_tally_info; #else if((tally_ptr = pam_tally_memory()) == NULL) return 0; #endif int root_unlock_time = tally_ptr->root_unlock_time; int deny = tally_ptr->deny; if(root_unlock_time == 0) return 0; int tfile = -1; struct tallylog tally; tally.reserved = 0; tally.fail_cnt = 0; tally.fail_time = 0; get_tally(userID,&tfile,&tally); if(tally.fail_cnt= (deny)*2 && tally.fail_cnt < (deny)*3){ root_unlock_time = root_unlock_time*2; } else if(tally.fail_cnt >= (deny)*3 && tally.fail_cnt < (deny)*4){ root_unlock_time = root_unlock_time*8; } else if (tally.fail_cnt >= (deny)*4){ *fail_cnt = 0xFFFF;//永久锁定 return 0; } int root_unlock_time_left = root_unlock_time - ((long)time(NULL) - tally.fail_time); *fail_time = tally.fail_time; *unlock_time1 = root_unlock_time; *left_time = root_unlock_time_left > 0 ? root_unlock_time_left : 0; *fail_cnt = tally.fail_cnt; return *left_time; } ukui-screensaver/src/lock-dialog/languagesetting.cpp0000664000175000017500000001005015172041106021625 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "languagesetting.h" #include #include "../../src/common/definetypes.h" #include #include #include #include LanguageSetting *LanguageSetting::m_singleInstance = nullptr; LanguageSetting::LanguageSetting(QObject *parent) : QObject(parent) {} LanguageSetting::~LanguageSetting() {} LanguageSetting *LanguageSetting::instance(QObject *parent) { if (m_singleInstance == nullptr) m_singleInstance = new LanguageSetting(parent); return m_singleInstance; } void LanguageSetting::onLanguageChanged(QString languageCode) { if (languageCode == "") return; if (m_strLanguageCode == languageCode) { return; } qDebug() << "onLanguageChanged " << languageCode; Q_EMIT languageChanged(false); if (m_translator) { qApp->removeTranslator(m_translator); delete m_translator; m_translator = nullptr; } m_translator = new QTranslator(this); QString qmFile; if (languageCode.startsWith("zh_CN")) { /*这里写的有问题,忘记之前为什么写了,会导致繁体也会加载zh_CN*/ qmFile = QString(WORKING_DIRECTORY "/i18n_qm/%1.qm").arg("zh_CN"); setenv("LANGUAGE", "zh_CN", 1); setenv("LANG", languageCode2Locale("zh_CN", "zh_CN").toLatin1().data(), 1); setlocale(LC_ALL, "zh_CN.utf8"); QLocale lang("zh_CN"); /*这里设置一个QLocale的默认值,用来影响插件的语言。插件加载翻译文件时,不使用QLocale::system().name()来获取语言, * 应该使用QLocale local; local.name() 来获取语言,这样可以识别到登录界面设置的的语言环境*/ QLocale::setDefault(lang); } else { qmFile = QString(WORKING_DIRECTORY "/i18n_qm/%1.qm").arg(languageCode); setenv("LANGUAGE", languageCode.toLatin1().data(), 1); setenv("LANG", languageCode2Locale(languageCode, languageCode).toLatin1().data(), 1); setlocale(LC_ALL, ""); QLocale lang(languageCode); /*这里设置一个QLocale的默认值,用来影响插件的语言。插件加载翻译文件时,不使用QLocale::system().name()来获取语言, * 应该使用QLocale local; local.name() 来获取语言,这样可以识别到登录界面设置的的语言环境*/ QLocale::setDefault(lang); } m_translator->load(qmFile); qApp->installTranslator(m_translator); qDebug() << "load translation file " << qmFile; m_strLanguageCode = languageCode; Q_EMIT languageChanged(true); } QString LanguageSetting::languageCode2Locale(QString strLangCode, QString strDefault) { QString strLocale = strDefault; QFileInfo cmdInfo("/usr/share/language-tools/language2locale"); if (cmdInfo.exists() && cmdInfo.isExecutable()) { QProcess process; QString strCmd = cmdInfo.filePath() + " " + strLangCode; process.start(strCmd); process.waitForFinished(3000); QString result = process.readAll(); QStringList langLocales = result.split("\n"); if (langLocales.count() > 0) { QString strFirstLocale = langLocales[0].trimmed(); if (!strFirstLocale.isEmpty()) { strLocale = strFirstLocale; } } } else { qInfo() << cmdInfo.path() << "is invalid!"; } qDebug() << strLangCode << " to " << strLocale; return strLocale; } ukui-screensaver/src/lock-dialog/lockdialogperformer.cpp0000664000175000017500000007177115172041106022517 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "lockdialogperformer.h" #include "lockdialogmodel.h" #include "backenddbushelper.h" #include "utils.h" #include "kyrsac.h" #include #include #include #include #include LockDialogPerformer::LockDialogPerformer(LockDialogModel *model, QObject *parent) : QObject(parent), m_modelLockDialog(model) { QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); QString sessionDbus = QString("%1%2").arg(QString(SS_DBUS_SERVICE)).arg(displayNum); m_bdHelper = new BackendDbusHelper(sessionDbus, SS_DBUS_PATH, QDBusConnection::sessionBus(), this); if (!m_bdHelper->isValid()) { delete m_bdHelper; m_bdHelper = new BackendDbusHelper(SS_DBUS_SERVICE, SS_DBUS_PATH, QDBusConnection::sessionBus(), this); } initConnections(); initData(); } void LockDialogPerformer::initConnections() { connect(m_bdHelper, &BackendDbusHelper::requestLock, m_modelLockDialog, &LockDialogModel::requestLockSession); connect(m_bdHelper, &BackendDbusHelper::requestUnlock, m_modelLockDialog, &LockDialogModel::requestUnlockSession); connect(m_bdHelper, &BackendDbusHelper::PrepareForSleep, m_modelLockDialog, &LockDialogModel::prepareForSleep); connect( m_bdHelper, &BackendDbusHelper::sessionActiveChanged, m_modelLockDialog, &LockDialogModel::onSessionActiveChanged); connect(m_bdHelper, &BackendDbusHelper::usersInfoChanged, m_modelLockDialog, &LockDialogModel::onUsersInfoChanged); connect(m_bdHelper, &BackendDbusHelper::currentUserChanged, m_modelLockDialog, &LockDialogModel::onCurUserChanged); connect( m_bdHelper, &BackendDbusHelper::currentSessionChanged, m_modelLockDialog, &LockDialogModel::onCurSessionChanged); connect(m_bdHelper, &BackendDbusHelper::blockLogoutInhibitedChanged, this, [=]() { qDebug() << "update logoutcheck"; m_modelLockDialog->updateLogoutLockcheck(m_bdHelper->getLogoutLockcheck()); }); connect(m_bdHelper, &BackendDbusHelper::blockInhibitedChanged, this, [=](const QString &blockInhibited) { m_modelLockDialog->updateSleepLockcheck(m_bdHelper->getSleepLockcheck()); m_modelLockDialog->updateShutdownLockcheck(m_bdHelper->getShutdownLockcheck()); }); if (!m_modelLockDialog->isUseWayland()) { connect( m_bdHelper, &BackendDbusHelper::capslockConfChanged, m_modelLockDialog, &LockDialogModel::onCapslockStateChanged); } connect(m_bdHelper, &BackendDbusHelper::upowerServiceReady, m_modelLockDialog, [=]() { m_modelLockDialog->updateBatteryArgs(m_bdHelper->getBatteryArgs()); m_modelLockDialog->updateBatteryIconName(m_bdHelper->getBatteryIconName()); m_modelLockDialog->updateIsBattery(m_bdHelper->getIsBattery()); Q_EMIT m_modelLockDialog->upowerServiceReady(); }); connect( m_bdHelper, &BackendDbusHelper::batteryStatusChanged, m_modelLockDialog, &LockDialogModel::onBatteryStatusChanged); connect(m_bdHelper, &BackendDbusHelper::batteryChanged, m_modelLockDialog, &LockDialogModel::batteryChanged); connect(m_bdHelper, &BackendDbusHelper::SecondRunParam, m_modelLockDialog, &LockDialogModel::onSecondRunParam); connect( m_bdHelper, &BackendDbusHelper::lockScreenConfChanged, m_modelLockDialog, &LockDialogModel::onLockScreenConfChanged); connect( m_bdHelper, &BackendDbusHelper::themeStyleConfChanged, m_modelLockDialog, &LockDialogModel::onThemeStyleConfChanged); connect( m_bdHelper, &BackendDbusHelper::sessionConfChanged, m_modelLockDialog, &LockDialogModel::onSessionConfChanged); // pam signals connect(m_bdHelper, SIGNAL(pamShowMessage(QString, int)), m_modelLockDialog, SIGNAL(pamShowMessage(QString, int))); connect(m_bdHelper, SIGNAL(pamShowPrompt(QString, int)), m_modelLockDialog, SIGNAL(pamShowPrompt(QString, int))); connect( m_bdHelper, SIGNAL(pamShowBinaryPrompt(QVariant, int)), m_modelLockDialog, SIGNAL(pamShowBinaryPrompt(QVariant, int))); connect(m_bdHelper, SIGNAL(pamAuthCompleted()), m_modelLockDialog, SIGNAL(pamAuthCompleted())); // bio signals connect( m_bdHelper, SIGNAL(bioServiceStatusChanged(bool)), m_modelLockDialog, SIGNAL(bioServiceStatusChanged(bool))); connect(m_bdHelper, SIGNAL(bioDeviceChanged()), m_modelLockDialog, SIGNAL(bioDeviceChanged())); connect(m_bdHelper, SIGNAL(bioAuthShowMessage(QString)), m_modelLockDialog, SIGNAL(bioAuthShowMessage(QString))); connect(m_bdHelper, SIGNAL(bioAuthStateChanged(int)), m_modelLockDialog, SIGNAL(bioAuthStateChanged(int))); connect(m_bdHelper, SIGNAL(bioAuthFrameData(QString)), m_modelLockDialog, SIGNAL(bioAuthFrameData(QString))); connect( m_bdHelper, SIGNAL(bioAuthCompleted(int, bool, int, int, int)), m_modelLockDialog, SIGNAL(bioAuthCompleted(int, bool, int, int, int))); connect( m_bdHelper, &BackendDbusHelper::usdMediaKeysConfChanged, m_modelLockDialog, &LockDialogModel::onUsdMediaKeysChanged); connect( m_bdHelper, &BackendDbusHelper::usdMediaStateKeysConfChanged, m_modelLockDialog, &LockDialogModel::onUsdMediaStateKeysChanged); connect( m_bdHelper, &BackendDbusHelper::powerManagerConfChanged, m_modelLockDialog, &LockDialogModel::onPowerManagerConfChanged); connect( m_bdHelper, &BackendDbusHelper::tabletModeChanged, m_modelLockDialog, &LockDialogModel::onTabletModeChanged); connect( m_bdHelper, &BackendDbusHelper::lockDatetimeEnableChanged, m_modelLockDialog, &LockDialogModel::onLockDatetimeEnableChanged); /// 通过信号直接调用槽 connect(m_modelLockDialog, &LockDialogModel::setCurrentUser, m_bdHelper, &BackendDbusHelper::setCurrentUser); connect(m_modelLockDialog, &LockDialogModel::switchToUser, m_bdHelper, &BackendDbusHelper::switchToUser); connect(m_modelLockDialog, &LockDialogModel::setCurrentSession, m_bdHelper, &BackendDbusHelper::setCurrentSession); connect(m_modelLockDialog, &LockDialogModel::startSession, m_bdHelper, &BackendDbusHelper::startSession); connect(m_modelLockDialog, &LockDialogModel::pamAuthenticate, m_bdHelper, &BackendDbusHelper::pamAuthenticate); connect(m_modelLockDialog, &LockDialogModel::pamRespond, m_bdHelper, [=](const QString &strResponse) { QString strEncrypt = strResponse; if (strEncrypt != BIOMETRIC_IGNORE) { QString strPublicKey = m_bdHelper->getPublicEncrypt(); if (!strPublicKey.isEmpty()) { QByteArray buffer; KyRSAC rsac; if (rsac.encryptData(strEncrypt.toLatin1(), buffer, strPublicKey.toLatin1())) { strEncrypt = buffer.toBase64(); } } } m_bdHelper->pamRespond(strEncrypt); }); connect( m_modelLockDialog, &LockDialogModel::pamAuthenticateCancel, m_bdHelper, &BackendDbusHelper::pamAuthenticateCancel); connect( m_modelLockDialog, &LockDialogModel::pamIsInAuthentication, m_bdHelper, &BackendDbusHelper::pamIsInAuthentication); connect( m_modelLockDialog, &LockDialogModel::pamIsAuthenticated, m_bdHelper, &BackendDbusHelper::pamIsAuthenticated); connect( m_modelLockDialog, &LockDialogModel::pamAuthenticateUser, m_bdHelper, &BackendDbusHelper::pamAuthenticateUser); connect( m_modelLockDialog, &LockDialogModel::getAutologinUserHint, m_bdHelper, &BackendDbusHelper::getAutologinUserHint); connect(m_modelLockDialog, &LockDialogModel::GetBlankState, m_bdHelper, &BackendDbusHelper::GetBlankState); connect(m_modelLockDialog, &LockDialogModel::CheckTurnOnScreen, m_bdHelper, &BackendDbusHelper::CheckTurnOnScreen); connect(m_modelLockDialog, &LockDialogModel::bioStartAuth, m_bdHelper, &BackendDbusHelper::bioStartAuth); connect(m_modelLockDialog, &LockDialogModel::bioStopAuth, m_bdHelper, &BackendDbusHelper::bioStopAuth); connect( m_modelLockDialog, &LockDialogModel::bioGetAvailableDevices, m_bdHelper, &BackendDbusHelper::bioGetAvailableDevices); connect( m_modelLockDialog, &LockDialogModel::bioGetDisabledDevices, m_bdHelper, &BackendDbusHelper::bioGetDisabledDevices); connect( m_modelLockDialog, &LockDialogModel::bioGetBioAuthState, m_bdHelper, &BackendDbusHelper::bioGetBioAuthState); connect(m_modelLockDialog, &LockDialogModel::bioGetCurBioInfo, m_bdHelper, &BackendDbusHelper::bioGetCurBioInfo); connect(m_modelLockDialog, &LockDialogModel::bioFindDeviceById, m_bdHelper, &BackendDbusHelper::bioFindDeviceById); connect( m_modelLockDialog, &LockDialogModel::bioFindDeviceByName, m_bdHelper, &BackendDbusHelper::bioFindDeviceByName); connect( m_modelLockDialog, &LockDialogModel::bioGetDefaultDevice, m_bdHelper, &BackendDbusHelper::bioGetDefaultDevice); connect( m_modelLockDialog, &LockDialogModel::usdExternalDoAction, m_bdHelper, &BackendDbusHelper::usdExternalDoAction); connect(m_modelLockDialog, &LockDialogModel::setPowerManager, m_bdHelper, &BackendDbusHelper::setPowerManager); connect(m_modelLockDialog, &LockDialogModel::lockStateChanged, m_bdHelper, &BackendDbusHelper::lockStateChanged); connect(m_modelLockDialog, &LockDialogModel::getPublicEncrypt, m_bdHelper, &BackendDbusHelper::getPublicEncrypt); connect(m_modelLockDialog, &LockDialogModel::sendPassword, m_bdHelper, &BackendDbusHelper::sendPassword); connect(m_modelLockDialog, &LockDialogModel::setUserThemeColor, m_bdHelper, &BackendDbusHelper::setThemeStyleConf); connect(m_modelLockDialog, &LockDialogModel::setRfkillState, m_bdHelper, &BackendDbusHelper::setUsdMediaStateKeys); // 锁定状态 connect(m_modelLockDialog, &LockDialogModel::setLockState, m_bdHelper, &BackendDbusHelper::SetLockState); connect(m_modelLockDialog, &LockDialogModel::clearLockState, m_bdHelper, &BackendDbusHelper::ClearLockState); /*电源管理*/ connect( m_modelLockDialog, &LockDialogModel::getCanHibernate, m_bdHelper, &BackendDbusHelper::getPowerManagerCanHibernate, Qt::DirectConnection); connect( m_modelLockDialog, &LockDialogModel::getCanSuspend, m_bdHelper, &BackendDbusHelper::getPowerManagerCanSuspend, Qt::DirectConnection); connect( m_modelLockDialog, &LockDialogModel::getCanReboot, m_bdHelper, &BackendDbusHelper::getPowerManagerCanReboot, Qt::DirectConnection); connect( m_modelLockDialog, &LockDialogModel::getCanPowerOff, m_bdHelper, &BackendDbusHelper::getPowerManagerCanPowerOff, Qt::DirectConnection); connect( m_modelLockDialog, &LockDialogModel::getCanLockScreen, m_bdHelper, &BackendDbusHelper::getPowerManagerCanLockScreen, Qt::DirectConnection); connect( m_modelLockDialog, &LockDialogModel::getCanSwitchUser, m_bdHelper, &BackendDbusHelper::getPowerManagerCanSwitchUser, Qt::DirectConnection); connect( m_modelLockDialog, &LockDialogModel::getCanLogout, m_bdHelper, &BackendDbusHelper::getPowerManagerCanLogout, Qt::DirectConnection); connect( m_modelLockDialog, &LockDialogModel::checkSystemUpgrade, m_bdHelper, &BackendDbusHelper::checkSystemUpgrade, Qt::DirectConnection); } void LockDialogPerformer::initData() { QString strJsonInitData = m_bdHelper->getInitDataJson(); if (!strJsonInitData.isEmpty()) { QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strJsonInitData.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qWarning() << "getInitDataJson Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qWarning() << "getInitDataJson Json is null!!"; } else { if (objRes.contains("UsersInfo")) { QList listUser; QJsonArray jsonArray = objRes.value("UsersInfo").toArray(); if (!jsonArray.isEmpty()) { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); UserInfoPtr userPtr = std::make_shared(); userPtr->updateUid((uid_t)objUser.value("uid").toDouble(-1)); userPtr->updateName(objUser.value("name").toString()); userPtr->updateFullName(objUser.value("fullName").toString()); userPtr->updateHeadImage(objUser.value("headImg").toString()); userPtr->updateLoggedIn(objUser.value("loggedIn").toBool(false)); userPtr->updateBackground(objUser.value("backGround").toString()); userPtr->updateLang(objUser.value("lang").toString()); userPtr->updateGreeterBackground(objUser.value("greeterBackGround").toString()); userPtr->updateBackgroundColor(objUser.value("color").toString()); userPtr->updateSystemAccount(objUser.value("systemAccount").toBool(false)); listUser.append(userPtr); } } m_modelLockDialog->updateUsersInfo(listUser); } if (objRes.contains("SessionsInfo")) { QList listSessions; QJsonArray jsonArray = objRes.value("SessionsInfo").toArray(); if (!jsonArray.isEmpty()) { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strSession = objUser.value("key").toString(); listSessions.append(strSession); } } m_modelLockDialog->updateSessionsInfo(listSessions); } if (objRes.contains("IsSessionActive")) { m_modelLockDialog->updateSessionState(objRes.value("IsSessionActive").toBool()); } if (objRes.contains("DefAuthUser")) { m_modelLockDialog->updateDefUserName(objRes.value("DefAuthUser").toString()); } if (objRes.contains("CurUserName")) { m_modelLockDialog->updateCurUserName(objRes.value("CurUserName").toString()); } if (objRes.contains("CurSession")) { m_modelLockDialog->updateCurSession(objRes.value("CurSession").toString()); } if (objRes.contains("AgreementInfo")) { AgreementInfoPtr agreementInfo = nullptr; QJsonObject jsonObject = objRes.value("AgreementInfo").toObject(); if (!jsonObject.isEmpty()) { agreementInfo = std::make_shared(); agreementInfo->updateShowLoginPrompt(jsonObject.value("showLoginPrompt").toBool()); agreementInfo->updateHideTitle(jsonObject.value("hideTitle").toBool()); agreementInfo->updatePromptTitle(jsonObject.value("promptTitle").toString()); agreementInfo->updatePromptText(jsonObject.value("promptText").toString()); agreementInfo->updatePromptTextFilePath(jsonObject.value("promptTextFilePath").toString()); qDebug() << "------------------------------------------------------------"; qDebug() << jsonObject.value("showLoginPrompt").toBool(); qDebug() << jsonObject.value("hideTitle").toBool(); qDebug() << jsonObject.value("promptTitle").toString(); qDebug() << jsonObject.value("promptText").toString(); qDebug() << jsonObject.value("promptTextFilePath").toString(); qDebug() << "------------------------------------------------------------"; } if (!agreementInfo) { agreementInfo = std::make_shared(); } m_modelLockDialog->updateAgreementInfo(agreementInfo); } if (objRes.contains("CapsLock")) { m_modelLockDialog->updateCapslockState(objRes.value("CapsLock").toBool()); } if (objRes.contains("MediaKeyAreaScreenShot")) { m_modelLockDialog->updateUsdMediaKeys( KEY_AREA_SCREENSHOT, objRes.value("MediaKeyAreaScreenShot").toString()); } if (objRes.contains("MediaKeyAreaScreenShot2")) { m_modelLockDialog->updateUsdMediaKeys( KEY_AREA_SCREENSHOT2, objRes.value("MediaKeyAreaScreenShot2").toString()); } if (objRes.contains("MediaKeyScreenShot")) { m_modelLockDialog->updateUsdMediaKeys( KEY_SCREEN_SHOT, objRes.value("MediaKeyScreenShot").toString()); } if (objRes.contains("MediaKeyScreenShot2")) { m_modelLockDialog->updateUsdMediaKeys( KEY_SCREEN_SHOT2, objRes.value("MediaKeyScreenShot2").toString()); } if (objRes.contains("MediaKeyWinScreenShot")) { m_modelLockDialog->updateUsdMediaKeys( KEY_WINDOW_SCREENSHOT, objRes.value("MediaKeyWinScreenShot").toString()); } if (objRes.contains("MediaKeyRfkillState")) { #ifdef USDFIXED m_modelLockDialog->updateUsdMediaStateKeys( KEY_RFKILL_STATE, objRes.value("MediaKeyRfkillState").toInt()); #endif } if (objRes.contains("PowerButton")) { m_modelLockDialog->updatePowerButtonKey(KEY_BUTTON_POWER, objRes.value("PowerButton").toString()); } if (objRes.contains("ScreenSaverMode")) { m_modelLockDialog->updateScreensaverMode(objRes.value("ScreenSaverMode").toString()); } if (objRes.contains("ScreenSaverThemes")) { QList listThemes; QJsonArray jsonArray = objRes.value("ScreenSaverThemes").toArray(); if (!jsonArray.isEmpty()) { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strSession = objUser.value("key").toString(); qDebug() << "savertheme = " << strSession; listThemes.append(strSession); } } m_modelLockDialog->updateScreensaverTheme(listThemes); } if (objRes.contains("ImageTransitionEffect")) { m_modelLockDialog->updateScreensaverImageTSEffect(objRes.value("ImageTransitionEffect").toInt()); } if (objRes.contains("ImageSwitchInterval")) { m_modelLockDialog->updateScreensaverImageSwitchInterval( objRes.value("ImageSwitchInterval").toInt()); } if (objRes.contains("BatteryArgs")) { QStringList listBattery; QJsonArray jsonArray = objRes.value("BatteryArgs").toArray(); if (!jsonArray.isEmpty()) { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strBattery = objUser.value("key").toString(); listBattery.append(strBattery); } } m_modelLockDialog->updateBatteryArgs(listBattery); } if (objRes.contains("BatteryIconName")) { m_modelLockDialog->updateBatteryIconName(objRes.value("BatteryIconName").toString()); } if (objRes.contains("IsBattery")) { m_modelLockDialog->updateIsBattery(objRes.value("IsBattery").toBool()); } if (objRes.contains("SleepLockCheck")) { QStringList sleepLockcheck; QJsonArray jsonArray = objRes.value("SleepLockCheck").toArray(); if (!jsonArray.isEmpty()) { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strLockcheck = objUser.value("key").toString(); sleepLockcheck.append(strLockcheck); } } m_modelLockDialog->updateSleepLockcheck(sleepLockcheck); } if (objRes.contains("ShutdownLockCheck")) { QStringList shutdownLockcheck; QJsonArray jsonArray = objRes.value("ShutdownLockCheck").toArray(); if (!jsonArray.isEmpty()) { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strLockcheck = objUser.value("key").toString(); shutdownLockcheck.append(strLockcheck); } } m_modelLockDialog->updateShutdownLockcheck(shutdownLockcheck); } if (objRes.contains("LogoutLockCheck")) { QStringList logoutLockCheck; QJsonArray jsonArray = objRes.value("LogoutLockCheck").toArray(); if (!jsonArray.isEmpty()) { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strLockcheck = objUser.value("key").toString(); logoutLockCheck.append(strLockcheck); } } m_modelLockDialog->updateLogoutLockcheck(logoutLockCheck); } if (objRes.contains("LockTimeout")) { m_modelLockDialog->updateLockTimeout(objRes.value("LockTimeout").toInt()); } if (objRes.contains("IdleDelay")) { m_modelLockDialog->updateIdleDelay(objRes.value("IdleDelay").toInt()); } if (objRes.contains("LockEnable")) { m_modelLockDialog->updateLockEnabled(objRes.value("LockEnable").toBool()); } if (objRes.contains("SleepActivationEnable")) { m_modelLockDialog->updateSleepActivationEnabled(objRes.value("SleepActivationEnable").toBool()); } if (objRes.contains("SystemFontSize")) { m_modelLockDialog->updateSystemFontSize(objRes.value("SystemFontSize").toDouble()); } if (objRes.contains("SystemFont")) { m_modelLockDialog->updateSystemFont(objRes.value("SystemFont").toString()); } if (objRes.contains("TabletMode")) { m_modelLockDialog->updateTabletMode(objRes.value("TabletMode").toBool()); } if (objRes.contains("LogoutMusic")) { m_modelLockDialog->updateSessionLogoutMusic(objRes.value("LogoutMusic").toBool()); } if (objRes.contains("PoweroffMusic")) { m_modelLockDialog->updateSessionPoweroffMusic(objRes.value("PoweroffMusic").toBool()); } if (objRes.contains("LockDatetimeEnable")) { m_modelLockDialog->updateLockDatetimeEnable(m_bdHelper->getLockDatetimeEnable()); } if (objRes.contains("CanSwitchUser")) { m_modelLockDialog->updateCanSwitchUser(m_bdHelper->getCanSwitchUser()); } } } } else { m_modelLockDialog->updateUsersInfo(m_bdHelper->getUsersInfo()); m_modelLockDialog->updateSessionsInfo(m_bdHelper->getSessionsInfo()); m_modelLockDialog->updateSessionState(m_bdHelper->isSessionActive()); m_modelLockDialog->updateDefUserName(m_bdHelper->getDefaultAuthUser()); m_modelLockDialog->updateCurUserName(m_bdHelper->getCurrentUser()); m_modelLockDialog->updateCurSession(m_bdHelper->getCurrentSession()); m_modelLockDialog->updateAgreementInfo(m_bdHelper->getAgreementInfo()); m_modelLockDialog->updateCapslockState(m_bdHelper->getKeyboardConf(KEY_CAPSLOCK_STATUS).toBool()); m_modelLockDialog->updateUsdMediaKeys( KEY_AREA_SCREENSHOT, m_bdHelper->getUsdMediaKeys(KEY_AREA_SCREENSHOT).toString()); m_modelLockDialog->updateUsdMediaKeys( KEY_AREA_SCREENSHOT2, m_bdHelper->getUsdMediaKeys(KEY_AREA_SCREENSHOT2).toString()); m_modelLockDialog->updateUsdMediaKeys(KEY_SCREEN_SHOT, m_bdHelper->getUsdMediaKeys(KEY_SCREEN_SHOT).toString()); m_modelLockDialog->updateUsdMediaKeys( KEY_SCREEN_SHOT2, m_bdHelper->getUsdMediaKeys(KEY_SCREEN_SHOT2).toString()); m_modelLockDialog->updateUsdMediaKeys( KEY_WINDOW_SCREENSHOT, m_bdHelper->getUsdMediaKeys(KEY_WINDOW_SCREENSHOT).toString()); #ifdef USDFIXED m_modelLockDialog->updateUsdMediaStateKeys( KEY_RFKILL_STATE, m_bdHelper->getUsdMediaStateKeys(KEY_RFKILL_STATE).toInt()); #endif m_modelLockDialog->updatePowerButtonKey( KEY_BUTTON_POWER, m_bdHelper->getPowerManagerConf(KEY_BUTTON_POWER).toString()); m_modelLockDialog->updateScreensaverMode(m_bdHelper->getLockScreenConf(KEY_MODE).toString()); m_modelLockDialog->updateScreensaverTheme(m_bdHelper->getSaverThemes()); m_modelLockDialog->updateScreensaverImageTSEffect( m_bdHelper->getLockScreenConf(KEY_IMAGE_TRANSITION_EFFECT).toInt()); m_modelLockDialog->updateScreensaverImageSwitchInterval( m_bdHelper->getLockScreenConf(KEY_IMAGE_SWITCH_INTERVAL).toInt()); m_modelLockDialog->updateBatteryArgs(m_bdHelper->getBatteryArgs()); m_modelLockDialog->updateBatteryIconName(m_bdHelper->getBatteryIconName()); m_modelLockDialog->updateIsBattery(m_bdHelper->getIsBattery()); m_modelLockDialog->updateLogoutLockcheck(m_bdHelper->getLogoutLockcheck()); m_modelLockDialog->updateSleepLockcheck(m_bdHelper->getSleepLockcheck()); m_modelLockDialog->updateShutdownLockcheck(m_bdHelper->getShutdownLockcheck()); m_modelLockDialog->updateLoggedInUsersCount(); m_modelLockDialog->updateLockTimeout(m_bdHelper->getLockScreenConf(KEY_LOCK_TIMEOUT).toInt()); m_modelLockDialog->updateIdleDelay(m_bdHelper->getLockScreenConf(KEY_IDLE_DELAY).toInt()); m_modelLockDialog->updateLockEnabled(m_bdHelper->getLockScreenConf(KEY_LOCK_ENABLED).toBool()); m_modelLockDialog->updateSleepActivationEnabled( m_bdHelper->getLockScreenConf(KEY_SLEEP_ACTIVATION_ENABLED).toBool()); m_modelLockDialog->updateSystemFontSize(m_bdHelper->getThemeStyleConf(KEY_SYSTEM_FONT_SIZE).toDouble()); m_modelLockDialog->updateSystemFont(m_bdHelper->getThemeStyleConf(KEY_SYSTEM_FONT).toString()); m_modelLockDialog->updateTabletMode(m_bdHelper->getCurTabletMode()); m_modelLockDialog->updateSessionLogoutMusic(m_bdHelper->getSessionConf(KEY_SESSION_LOGOUT_MUSIC).toBool()); m_modelLockDialog->updateSessionPoweroffMusic(m_bdHelper->getSessionConf(KEY_SESSION_POWEROFF_MUSIC).toBool()); m_modelLockDialog->updateLockDatetimeEnable(m_bdHelper->getLockDatetimeEnable()); } } void LockDialogPerformer::initExtraData() { if (m_modelLockDialog->isUseWayland()) { } } ukui-screensaver/src/lock-dialog/languagesetting.h0000664000175000017500000000247615172041035021310 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef LANGUAGESETTING_H #define LANGUAGESETTING_H #include #include class LanguageSetting : public QObject { Q_OBJECT public: static LanguageSetting *instance(QObject *parent = nullptr); void onLanguageChanged(QString languageCode); Q_SIGNALS: void languageChanged(bool isCompleted); private: LanguageSetting(QObject *parent = nullptr); virtual ~LanguageSetting(); QString languageCode2Locale(QString strLangCode, QString strDefault); private: static LanguageSetting *m_singleInstance; QTranslator *m_translator = nullptr; QString m_strLanguageCode; }; #endif // LANGUAGESETTING_H ukui-screensaver/src/lock-dialog/backenddbushelper.cpp0000664000175000017500000037011715172041106022126 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #include "backenddbushelper.h" #include "../../src/common/definetypes.h" #include "../../src/dbusifs/freedesktophelper.h" #include #include #include #include #include #include BackendDbusHelper::BackendDbusHelper( const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) { initConnections(); } BackendDbusHelper::~BackendDbusHelper() {} /** * @brief 接口名称 * * @return const char */ const char *BackendDbusHelper::staticInterfaceName() { QString displayNum = QString(qgetenv("DISPLAY")).replace(":", "").replace(".", "_"); QString sessionDbus = QString("%1%2").arg(QString(SS_DBUS_SERVICE)).arg(displayNum); if (!FreedesktopHelper::getInstance(true)->NameHasOwner(sessionDbus)) { sessionDbus = SS_DBUS_SERVICE; } return sessionDbus.toLatin1().constData(); } void BackendDbusHelper::initConnections() { connect(this, &BackendDbusHelper::UpdateInformation, this, &BackendDbusHelper::onUpdateInformation); } AgreementInfoPtr BackendDbusHelper::getAgreementInfo() { AgreementInfoPtr agreementInfo = NULL; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_AGREEMENT; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getAgreementInfo error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getAgreementInfo Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getAgreementInfo Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_AGREEMENT || nRet != 0) { qInfo() << "getAgreementInfo Failed!!"; } else { agreementInfo = ParseAgreementInfo(objRes); } } else { qInfo() << "getAgreementInfo Json is invalid!!"; } } } } if (!agreementInfo) { agreementInfo = std::make_shared(); } return agreementInfo; } QString BackendDbusHelper::getInitDataJson() { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_INIT_DATA; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getInitDataJson error: " << reply.error().message(); } else { return reply.value(); } return QString(""); } QList BackendDbusHelper::getUsersInfo() { QList listUser; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_USERINFO_LIST; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getUsersInfo error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getUsersInfo Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getUsersInfo Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); QJsonArray jsonArray = objRes.value("Content").toArray(); if (nCmdId != LOCK_CMD_ID_GET_USERINFO_LIST || nRet != 0 || jsonArray.isEmpty()) { qInfo() << "getUsersInfo Failed!!"; } else { listUser = ParseUsersInfo(objRes); } } else { qInfo() << "getUsersInfo Json is invalid!!"; } } } } return listUser; } QList BackendDbusHelper::getSessionsInfo() { QList listSession; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_SESSIONS_LIST; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getSessionsInfo error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getSessionsInfo Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getSessionsInfo Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); QJsonArray jsonArray = objRes.value("Content").toArray(); if (nCmdId != LOCK_CMD_ID_GET_SESSIONS_LIST || nRet != 0 || jsonArray.isEmpty()) { qInfo() << "getSessionsInfo Failed!!"; } else { listSession = ParseSessionsInfo(objRes); } } else { qInfo() << "getSessionsInfo Json is invalid!!"; } } } } return listSession; } QStringList BackendDbusHelper::getBatteryArgs() { QStringList listArgs; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_UPOWER_BATTERY; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getBatteryArgs error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getBatteryArgs Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getBatteryArgs Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_UPOWER_BATTERY || nRet != 0) { qInfo() << "getBatteryArgs Failed!!"; } else { listArgs = ParseBatteryArgs(objRes); } } else { qInfo() << "getBatteryArgs Json is invalid!!"; } } } } return listArgs; } QStringList BackendDbusHelper::getSleepLockcheck() { QStringList listArgs; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SESSION_GET_SLEEP_LOCKCHECK; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getSleepLockcheck error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getSleepLockcheck Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getSleepLockcheck Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_SESSION_GET_SLEEP_LOCKCHECK || nRet != 0) { qInfo() << "getSleepLockcheck Failed!!"; } else { listArgs = ParseSleepLockcheck(objRes); } } else { qInfo() << "getSleepLockcheck Json is invalid!!"; } } } } return listArgs; } QString BackendDbusHelper::getBatteryIconName() { QString batteryIconName = ""; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_UPOWER_BATTERY_STATUS; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getBatteryIconName error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getBatteryIconName Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getBatteryIconName Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_UPOWER_BATTERY_STATUS || nRet != 0) { qInfo() << "getBatteryIconName Failed!!"; } else { batteryIconName = objRes.value("Content").toString(); } } else { qInfo() << "getBatteryIconName Json is invalid!!"; } } } } return batteryIconName; } bool BackendDbusHelper::getIsBattery() { bool isBattery = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_UPOWER_IS_BATTERY; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getIsBattery error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getIsBattery Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getIsBattery Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_UPOWER_IS_BATTERY || nRet != 0) { qInfo() << "getIsBattery Failed!!"; } else { isBattery = objRes.value("Content").toBool(); } } else { qInfo() << "getIsBattery Json is invalid!!"; } } } } return isBattery; } bool BackendDbusHelper::getCurTabletMode() { bool tabletMode = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_TABLET_MODE; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getCurTabletMode error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getCurTabletMode Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getCurTabletMode Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_TABLET_MODE || nRet != 0) { qInfo() << "getCurTabletMode Failed!!"; } else { tabletMode = objRes.value("Content").toBool(); } } else { qInfo() << "getCurTabletMode Json is invalid!!"; } } } } return tabletMode; } int BackendDbusHelper::getNetworkConnectState() { int connectState = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_NETWORK_CONNECT_STATE; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getNetworkConnectState error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getNetworkConnectState Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getNetworkConnectState Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_NETWORK_CONNECT_STATE || nRet != 0) { qInfo() << "getNetworkConnectState Failed!!"; } else { connectState = objRes.value("Content").toInt(); } } else { qInfo() << "getNetworkConnectState Json is invalid!!"; } } } } return connectState; } QVariant BackendDbusHelper::getWeatherData(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_WEATHER_DATA; jsonCmd["Key"] = strKey; QJsonObject objRes; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getWeatherData error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getWeatherData Parse json failed!!"; } else { objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getWeatherData Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_WEATHER_DATA || nRet != 0) { qInfo() << "getWeatherData Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getWeatherData Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::getSaverDatetimeEnable() { bool enable = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_SAVER_DATETIME_ENABLE_DATA; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getSaverDatetimeEnable error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getSaverDatetimeEnable Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getSaverDatetimeEnable Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_SAVER_DATETIME_ENABLE_DATA || nRet != 0) { qInfo() << "getSaverDatetimeEnable Failed!!"; } else { enable = objRes.value("Content").toBool(); } } else { qInfo() << "getSaverDatetimeEnable Json is invalid!!"; } } } } return enable; } bool BackendDbusHelper::getLockDatetimeEnable() { bool enable = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_LOCK_DATETIME_ENABLE_DATA; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getLockDatetimeEnable error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getLockDatetimeEnable Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getLockDatetimeEnable Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_LOCK_DATETIME_ENABLE_DATA || nRet != 0) { qInfo() << "getLockDatetimeEnable Failed!!"; } else { enable = objRes.value("Content").toBool(); } } else { qInfo() << "getLockDatetimeEnable Json is invalid!!"; } } } } return enable; } bool BackendDbusHelper::setSaverDatetimeEnable(bool enable) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SET_SAVER_DATETIME_ENABLE_DATA; jsonCmd["Content"] = enable; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setSaverDatetimeEnable error: " << reply.error().message(); return false; } return true; } bool BackendDbusHelper::setLockDatetimeEnable(bool enable) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SET_LOCK_DATETIME_ENABLE_DATA; jsonCmd["Content"] = enable; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setLockDatetimeEnable error: " << reply.error().message(); return false; } return true; } bool BackendDbusHelper::getCanSwitchUser() { bool enable = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_CAN_SWITCH_USER; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getCanSwitchUser error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getCanSwitchUser Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getCanSwitchUser Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_CAN_SWITCH_USER || nRet != 0) { qInfo() << "getCanSwitchUser Failed!!"; } else { enable = objRes.value("Content").toBool(); } } else { qInfo() << "getCanSwitchUser Json is invalid!!"; } } } } return enable; } bool BackendDbusHelper::setCurrentSession(QString strSession) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SET_SESSION; jsonCmd["Content"] = strSession; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setCurrentSession error: " << reply.error().message(); return false; } return true; } bool BackendDbusHelper::lockStateChanged(bool isVisible, bool isSessionTools) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOCK_STATE_CHANGED; jsonCmd["Content"] = isVisible; jsonCmd["SessionTools"] = isSessionTools; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); if (reply.isError()) { qWarning() << "lockStateChanged error: " << reply.error().message(); return false; } return true; } void BackendDbusHelper::startSession() { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_START_SESSION; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "startSession error: " << reply.error().message(); } } void BackendDbusHelper::pamAuthenticate(QString strUserName) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_PAMAUTH_AUTHENTICATE; jsonCmd["UserName"] = strUserName; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "pamAuthenticate error: " << reply.error().message(); } } void BackendDbusHelper::bioStartAuth(int uid, int nDevId) { #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_STARTAUTH; jsonCmd["UserId"] = uid; jsonCmd["DevId"] = nDevId; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); if (reply.isError()) { qWarning() << "bioStartAuth error: " << reply.error().message(); } #endif } void BackendDbusHelper::bioStopAuth() { #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_STOPAUTH; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioStopAuth error: " << reply.error().message(); } #endif } void BackendDbusHelper::pamRespond(QString strRespond) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_PAMAUTH_RESPOND; jsonCmd["Content"] = strRespond; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "pamAuthenticate error: " << reply.error().message(); } } void BackendDbusHelper::pamAuthenticateCancel() { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_PAMAUTH_AUTHENTICATE_CANCEL; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "pamAuthenticate error: " << reply.error().message(); } } bool BackendDbusHelper::pamIsInAuthentication() { bool isInAuthentication = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_PAMAUTH_IS_INAUTHTICATION; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "pamIsInAuthentication error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "pamIsInAuthentication Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "pamIsInAuthentication Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_PAMAUTH_IS_INAUTHTICATION || nRet != 0) { qInfo() << "pamIsInAuthentication Failed!!"; } else { isInAuthentication = objRes.value("Content").toBool(); } } else { qInfo() << "pamIsInAuthentication Json is invalid!!"; } } } } return isInAuthentication; } bool BackendDbusHelper::pamIsAuthenticated() { bool isAuthenticated = false; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_PAMAUTH_IS_AUTHENTICATED; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "pamIsAuthenticated error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "pamIsAuthenticated Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "pamIsAuthenticated Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_PAMAUTH_IS_AUTHENTICATED || nRet != 0) { qInfo() << "pamIsAuthenticated Failed!!"; } else { isAuthenticated = objRes.value("Content").toBool(); } } else { qInfo() << "pamIsAuthenticated Json is invalid!!"; } } } } return isAuthenticated; } QString BackendDbusHelper::pamAuthenticateUser() { QString strUser; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_PAMAUTH_GET_AUTHUSER; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "pamAuthenticateUser error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "pamAuthenticateUser Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "pamAuthenticateUser Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_PAMAUTH_GET_AUTHUSER || nRet != 0) { qInfo() << "pamAuthenticateUser Failed!!"; } else { strUser = objRes.value("Content").toString(); } } else { qInfo() << "pamAuthenticateUser Json is invalid!!"; } } } } return strUser; } void BackendDbusHelper::ParsePamShowMessage(const QJsonObject &objRes) { QString strMsg = objRes.value("Message").toString(); int nType = objRes.value("MsgType").toInt(); Q_EMIT pamShowMessage(strMsg, nType); } void BackendDbusHelper::ParsePamShowPrompt(const QJsonObject &objRes) { QString strPrompt = objRes.value("Prompt").toString(); int nType = objRes.value("PromptType").toInt(); Q_EMIT pamShowPrompt(strPrompt, nType); } void BackendDbusHelper::ParsePamShowBinaryPrompt(const QJsonObject &objRes) { int nType = objRes.value("BinaryPromptType").toInt(); QVariant val = objRes.value("BinaryVal").toVariant(); Q_EMIT pamShowBinaryPrompt(val, nType); } void BackendDbusHelper::ParsePamAuthCompleted(const QJsonObject &objRes) { Q_UNUSED(objRes); Q_EMIT pamAuthCompleted(); } void BackendDbusHelper::ParseBioServiceStatus(const QJsonObject &objRes) { Q_EMIT bioServiceStatusChanged(objRes.value("IsValid").toBool()); } void BackendDbusHelper::ParseBioDeviceChanged(const QJsonObject &objRes) { Q_UNUSED(objRes); Q_EMIT bioDeviceChanged(); } void BackendDbusHelper::ParseBioAuthShowMessage(const QJsonObject &objRes) { Q_EMIT bioAuthShowMessage(objRes.value("Message").toString()); } void BackendDbusHelper::ParseBioAuthStateChanged(const QJsonObject &objRes) { Q_EMIT bioAuthStateChanged(objRes.value("State").toInt()); } void BackendDbusHelper::ParseBioAuthFrameData(const QJsonObject &objRes) { Q_EMIT bioAuthFrameData(objRes.value("Data").toString()); } void BackendDbusHelper::ParseBioAuthCompleted(const QJsonObject &objRes) { int nUid = objRes.value("UId").toInt(); bool isSuccess = objRes.value("IsSuccess").toBool(); int nError = objRes.value("ErrorNo").toInt(); int nMaxFailedTime = objRes.value("MaxFailedTime").toInt(); int nFailedTime = objRes.value("FailedTime").toInt(); Q_EMIT bioAuthCompleted(nUid, isSuccess, nError, nMaxFailedTime, nFailedTime); } void BackendDbusHelper::ParseTabletModeChanged(const QJsonObject &objRes) { Q_EMIT tabletModeChanged(objRes.value("mode").toBool()); } void BackendDbusHelper::ParseNetWorkConnectChanged(const QJsonObject &objRes) { Q_EMIT networkConnectStateChanged(objRes.value("state").toInt()); } void BackendDbusHelper::ParseSaverTimeDataEnableChanged(const QJsonObject &objRes) { Q_EMIT saverDatetimeEnableChanged(objRes.value("enable").toBool(), objRes.value("username").toString()); } void BackendDbusHelper::ParseLockTimeDataEnableChanged(const QJsonObject &objRes) { Q_EMIT lockDatetimeEnableChanged(objRes.value("enable").toBool(), objRes.value("username").toString()); } QList BackendDbusHelper::bioGetAvailableDevices(int nUid) { QList listDevInfo; #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_GET_AVAILABLE_DEVICES; jsonCmd["UserId"] = nUid; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioGetAvailableDevices error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "bioGetAvailableDevices Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "bioGetAvailableDevices Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_BIOAUTH_GET_AVAILABLE_DEVICES || nRet != 0) { qInfo() << "bioGetAvailableDevices Failed!!"; } else { QJsonArray arrayDevInfo = objRes.value("Content").toArray(); for (auto devInfoObj : arrayDevInfo) { if (devInfoObj.isObject()) { DeviceInfo devInfo; devInfo.id = devInfoObj.toObject().value("id").toInt(); devInfo.shortName = devInfoObj.toObject().value("ShortName").toString(); devInfo.fullName = devInfoObj.toObject().value("FullName").toString(); devInfo.driverEnable = devInfoObj.toObject().value("DriverEnable").toInt(); devInfo.deviceNum = devInfoObj.toObject().value("DeviceNum").toInt(); devInfo.deviceType = devInfoObj.toObject().value("DeviceType").toInt(); devInfo.storageType = devInfoObj.toObject().value("StorageType").toInt(); devInfo.eigType = devInfoObj.toObject().value("EigType").toInt(); devInfo.verifyType = devInfoObj.toObject().value("VerifyType").toInt(); devInfo.identifyType = devInfoObj.toObject().value("IdentifyType").toInt(); devInfo.busType = devInfoObj.toObject().value("BusType").toInt(); devInfo.deviceStatus = devInfoObj.toObject().value("DeviceStatus").toInt(); devInfo.OpsStatus = devInfoObj.toObject().value("OpsStatus").toInt(); listDevInfo.append(devInfo); } } } } else { qInfo() << "bioGetAvailableDevices Json is invalid!!"; } } } } #endif return listDevInfo; } QList BackendDbusHelper::bioGetDisabledDevices(int nUid) { QList listDevDisabled; #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_GET_DISABLED_DEVICES; jsonCmd["UserId"] = nUid; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioGetDisabledDevices error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "bioGetDisabledDevices Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "bioGetDisabledDevices Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_BIOAUTH_GET_DISABLED_DEVICES || nRet != 0) { qInfo() << "bioGetDisabledDevices Failed!!"; } else { QJsonArray arrayDevInfo = objRes.value("Content").toArray(); for (auto devInfoObj : arrayDevInfo) { if (devInfoObj.isDouble()) { listDevDisabled.append(devInfoObj.toInt()); } } } } else { qInfo() << "bioGetDisabledDevices Json is invalid!!"; } } } } #endif return listDevDisabled; } int BackendDbusHelper::bioGetBioAuthState() { int nAuthState = 0; #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_GET_STATE; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioGetBioAuthState error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "bioGetBioAuthState Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "bioGetBioAuthState Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_BIOAUTH_GET_STATE || nRet != 0) { qInfo() << "bioGetBioAuthState Failed!!"; } else { nAuthState = objRes.value("Content").toInt(); } } else { qInfo() << "bioGetBioAuthState Json is invalid!!"; } } } } #endif return nAuthState; } DeviceInfo BackendDbusHelper::bioGetCurBioInfo() { DeviceInfo curDevInfo; #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_GET_CURDEVICE; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioGetCurBioInfo error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "bioGetCurBioInfo Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "bioGetCurBioInfo Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_BIOAUTH_GET_CURDEVICE || nRet != 0) { qInfo() << "bioGetCurBioInfo Failed!!"; } else { QJsonObject objDevInfo = objRes.value("Content").toObject(); if (!objDevInfo.isEmpty()) { curDevInfo.id = objDevInfo.value("id").toInt(); curDevInfo.shortName = objDevInfo.value("ShortName").toString(); curDevInfo.fullName = objDevInfo.value("FullName").toString(); curDevInfo.driverEnable = objDevInfo.value("DriverEnable").toInt(); curDevInfo.deviceNum = objDevInfo.value("DeviceNum").toInt(); curDevInfo.deviceType = objDevInfo.value("DeviceType").toInt(); curDevInfo.storageType = objDevInfo.value("StorageType").toInt(); curDevInfo.eigType = objDevInfo.value("EigType").toInt(); curDevInfo.verifyType = objDevInfo.value("VerifyType").toInt(); curDevInfo.identifyType = objDevInfo.value("IdentifyType").toInt(); curDevInfo.busType = objDevInfo.value("BusType").toInt(); curDevInfo.deviceStatus = objDevInfo.value("DeviceStatus").toInt(); curDevInfo.OpsStatus = objDevInfo.value("OpsStatus").toInt(); } } } else { qInfo() << "bioGetCurBioInfo Json is invalid!!"; } } } } #endif return curDevInfo; } DeviceInfo BackendDbusHelper::bioFindDeviceById(int nUid, int nDevId) { DeviceInfo curDevInfo; #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_ID; jsonCmd["UserId"] = nUid; jsonCmd["DevId"] = nDevId; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioFindDeviceById error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "bioFindDeviceById Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "bioFindDeviceById Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_ID || nRet != 0) { qInfo() << "bioFindDeviceById Failed!!"; } else { QJsonObject objDevInfo = objRes.value("Content").toObject(); if (!objDevInfo.isEmpty()) { curDevInfo.id = objDevInfo.value("id").toInt(); curDevInfo.shortName = objDevInfo.value("ShortName").toString(); curDevInfo.fullName = objDevInfo.value("FullName").toString(); curDevInfo.driverEnable = objDevInfo.value("DriverEnable").toInt(); curDevInfo.deviceNum = objDevInfo.value("DeviceNum").toInt(); curDevInfo.deviceType = objDevInfo.value("DeviceType").toInt(); curDevInfo.storageType = objDevInfo.value("StorageType").toInt(); curDevInfo.eigType = objDevInfo.value("EigType").toInt(); curDevInfo.verifyType = objDevInfo.value("VerifyType").toInt(); curDevInfo.identifyType = objDevInfo.value("IdentifyType").toInt(); curDevInfo.busType = objDevInfo.value("BusType").toInt(); curDevInfo.deviceStatus = objDevInfo.value("DeviceStatus").toInt(); curDevInfo.OpsStatus = objDevInfo.value("OpsStatus").toInt(); } } } else { qInfo() << "bioFindDeviceById Json is invalid!!"; } } } } #endif return curDevInfo; } DeviceInfo BackendDbusHelper::bioFindDeviceByName(int nUid, QString strDevName) { DeviceInfo curDevInfo; #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_NAME; jsonCmd["UserId"] = nUid; jsonCmd["DevName"] = strDevName; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioFindDeviceByName error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "bioFindDeviceByName Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "bioFindDeviceByName Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_BIOAUTH_FIND_DEVICE_BY_NAME || nRet != 0) { qInfo() << "bioFindDeviceByName Failed!!"; } else { QJsonObject objDevInfo = objRes.value("Content").toObject(); if (!objDevInfo.isEmpty()) { curDevInfo.id = objDevInfo.value("id").toInt(); curDevInfo.shortName = objDevInfo.value("ShortName").toString(); curDevInfo.fullName = objDevInfo.value("FullName").toString(); curDevInfo.driverEnable = objDevInfo.value("DriverEnable").toInt(); curDevInfo.deviceNum = objDevInfo.value("DeviceNum").toInt(); curDevInfo.deviceType = objDevInfo.value("DeviceType").toInt(); curDevInfo.storageType = objDevInfo.value("StorageType").toInt(); curDevInfo.eigType = objDevInfo.value("EigType").toInt(); curDevInfo.verifyType = objDevInfo.value("VerifyType").toInt(); curDevInfo.identifyType = objDevInfo.value("IdentifyType").toInt(); curDevInfo.busType = objDevInfo.value("BusType").toInt(); curDevInfo.deviceStatus = objDevInfo.value("DeviceStatus").toInt(); curDevInfo.OpsStatus = objDevInfo.value("OpsStatus").toInt(); } } } else { qInfo() << "bioFindDeviceByName Json is invalid!!"; } } } } #endif return curDevInfo; } QString BackendDbusHelper::bioGetDefaultDevice(int nUid, QString strUserName, int bioType /* = -1 */) { QString strDevName; #ifdef ENABLE_BIO_BACKEND QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_BIOAUTH_GET_DEFAULT_DEVICE; jsonCmd["UserId"] = nUid; jsonCmd["UserName"] = strUserName; if (bioType != -1) jsonCmd["DevType"] = bioType; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "bioGetDefaultDevice error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "bioGetDefaultDevice Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "bioGetDefaultDevice Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_BIOAUTH_GET_DEFAULT_DEVICE || nRet != 0) { qInfo() << "bioGetDefaultDevice Failed!!"; } else { strDevName = objRes.value("Content").toString(); } } else { qInfo() << "bioGetDefaultDevice Json is invalid!!"; } } } } #endif return strDevName; } QString BackendDbusHelper::getCurrentSession() { QString strCurSession = ""; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_CURRENT_SESSION; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getCurrentSession error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getCurrentSession Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getCurrentSession Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_CURRENT_SESSION || nRet != 0) { qInfo() << "getCurrentSession Failed!!"; } else { strCurSession = objRes.value("Content").toString(); } } else { qInfo() << "getCurrentSession Json is invalid!!"; } } } } return strCurSession; } void BackendDbusHelper::onUpdateInformation(const QString &strJson) { QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strJson.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "onUpdateInformation Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "onUpdateInformation Json is null!!"; } else { if (objRes.contains("CmdId")) { int nCmdId = objRes.value("CmdId").toInt(-1); switch (nCmdId) { case LOCK_CMD_ID_USERINFO_CHANGED: { ParseUsersInfo(objRes); } break; case LOCK_CMD_ID_CURRENT_USER_CHANGED: { ParseCurrentUser(objRes); } break; case LOCK_CMD_ID_CURRENT_SESSION_CHANGED: { ParseCurrentSession(objRes); } break; case LOCK_CMD_ID_LOGIN1_REQ_LOCK: { ParseLogin1ReqLock(objRes); } break; case LOCK_CMD_ID_LOGIN1_REQ_UNLOCK: { ParseLogin1ReqUnLock(objRes); } break; case LOCK_CMD_ID_LOGIN1_PREPAREFORSLEEP: { ParseLogin1PrepareForSleep(objRes); } break; case LOCK_CMD_ID_LOGIN1_SESSION_ACTIVE_CHANGED: { ParseLogin1SessionActiveChanged(objRes); } break; case LOCK_CMD_ID_GSETTINGS_LOCKSCREEN_CONF_CHANGED: { ParseLockScreenConf(objRes); } break; case LOCK_CMD_ID_GSETTINGS_SCREENSAVER_CONF_CHANGED: { ParseScreenSaverConf(objRes); } break; case LOCK_CMD_ID_GSETTINGS_POWERMANAGER_CONF_CHANGED: { ParsePowerManagerConf(objRes); } break; case LOCK_CMD_ID_GSETTINGS_MATEBG_CONF_CHANGED: { ParseMateBgConf(objRes); } break; case LOCK_CMD_ID_GSETTINGS_UKCCPLUGINS_CONF_CHANGED: { ParseUkccPluginsConf(objRes); } break; case LOCK_CMD_ID_GSETTINGS_THEMESTYLE_CONF_CHANGED: { ParseThemeStyleConf(objRes); } break; case LOCK_CMD_ID_GSETTINGS_SESSION_CONF_CHANGED: { ParseSessionConf(objRes); } break; case LOCK_CMD_ID_LOGIN1_BLOCKLOGOUTINHIBITED_CHANGED: { ParseBlockLogoutInhibitedChanged(objRes); } break; case LOCK_CMD_ID_LOGIN1_BLOCKINHIBITED_CHANGED: { ParseBlockInhibitedChanged(objRes); } break; case LOCK_CMD_ID_GSETTINGS_KEYBOARD_CONF_CHANGED: { ParseKeyboardConf(objRes); } break; case LOCK_CMD_ID_PAMAUTH_SHOWMESSAGE: { ParsePamShowMessage(objRes); } break; case LOCK_CMD_ID_PAMAUTH_SHOWPROMPT: { ParsePamShowPrompt(objRes); } break; case LOCK_CMD_ID_PAMAUTH_SHOWPBINARYROMPT: { ParsePamShowBinaryPrompt(objRes); } break; case LOCK_CMD_ID_PAMAUTH_AUTH_COMPLETED: { ParsePamAuthCompleted(objRes); } break; case LOCK_CMD_ID_GSETTINGS_USD_MEDIAKEYS_CONF_CHANGED: { ParseUsdMediaKeysConf(objRes); } break; case LOCK_CMD_ID_GSETTINGS_USD_MEDIA_STATE_KEYS_CONF_CHANGED: { ParseUsdMediaStateKeysConf(objRes); } break; case LOCK_CMD_ID_UPOWER_BATTERY_STATUS: { ParseBatteryStatusChanged(objRes); } break; case LOCK_CMD_ID_UPOWER_BATTERY: { ParseBatteryChanged(objRes); } break; case LOCK_CMD_ID_UPOWER_SERVICE_READY: { ParseUPowerServiceReady(objRes); } break; case LOCK_CMD_ID_BIOAUTH_SERVICE_STATUS_CHANGED: { ParseBioServiceStatus(objRes); } break; case LOCK_CMD_ID_BIOAUTH_DEVICE_CHANGED: { ParseBioDeviceChanged(objRes); } break; case LOCK_CMD_ID_BIOAUTH_SHOW_MESSAGE: { ParseBioAuthShowMessage(objRes); } break; case LOCK_CMD_ID_BIOAUTH_AUTHSTATE_CHANGED: { ParseBioAuthStateChanged(objRes); } break; case LOCK_CMD_ID_BIOAUTH_FRAME_DATA: { ParseBioAuthFrameData(objRes); } break; case LOCK_CMD_ID_BIOAUTH_COMPLETE: { ParseBioAuthCompleted(objRes); } break; case LOCK_CMD_ID_TABLET_MODE_CHANGED: { ParseTabletModeChanged(objRes); } break; case LOCK_CMD_ID_NETWORK_CONNECT_STATE_CHANGED: { ParseNetWorkConnectChanged(objRes); } break; case LOCK_CMD_ID_SET_SAVER_DATETIME_ENABLE_DATA_CHANGED: { ParseSaverTimeDataEnableChanged(objRes); } break; case LOCK_CMD_ID_SET_LOCK_DATETIME_ENABLE_DATA_CHANGED: { ParseLockTimeDataEnableChanged(objRes); } break; default: break; } } else { qInfo() << "getUsersInfo Json is invalid!!"; } } } } AgreementInfoPtr BackendDbusHelper::ParseAgreementInfo(const QJsonObject &objRes) { AgreementInfoPtr agreementInfo; QJsonObject jsonObject = objRes.value("Content").toObject(); if (jsonObject.isEmpty()) { qInfo() << "updateAgreementInfo failed!!"; return agreementInfo; } else { agreementInfo = std::make_shared(); agreementInfo->updateShowLoginPrompt(jsonObject.value("showLoginPrompt").toBool()); agreementInfo->updateHideTitle(jsonObject.value("hideTitle").toBool()); agreementInfo->updatePromptTitle(jsonObject.value("promptTitle").toString()); agreementInfo->updatePromptText(jsonObject.value("promptText").toString()); agreementInfo->updatePromptTextFilePath(jsonObject.value("promptTextFilePath").toString()); qDebug() << "------------------------------------------------------------"; qDebug() << jsonObject.value("showLoginPrompt").toBool(); qDebug() << jsonObject.value("hideTitle").toBool(); qDebug() << jsonObject.value("promptTitle").toString(); qDebug() << jsonObject.value("promptText").toString(); qDebug() << jsonObject.value("promptTextFilePath").toString(); qDebug() << "------------------------------------------------------------"; } return agreementInfo; } QList BackendDbusHelper::ParseUsersInfo(const QJsonObject &objRes) { QList listUser; QJsonArray jsonArray = objRes.value("Content").toArray(); if (jsonArray.isEmpty()) { qInfo() << "updateUsersInfo Failed!!"; return listUser; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); UserInfoPtr userPtr = std::make_shared(); userPtr->updateUid((uid_t)objUser.value("uid").toDouble(-1)); userPtr->updateName(objUser.value("name").toString()); userPtr->updateFullName(objUser.value("fullName").toString()); userPtr->updateHeadImage(objUser.value("headImg").toString()); userPtr->updateLoggedIn(objUser.value("loggedIn").toBool(false)); userPtr->updateBackground(objUser.value("backGround").toString()); userPtr->updateLang(objUser.value("lang").toString()); userPtr->updateGreeterBackground(objUser.value("greeterBackGround").toString()); userPtr->updateBackgroundColor(objUser.value("color").toString()); userPtr->updateSystemAccount(objUser.value("systemAccount").toBool(false)); listUser.append(userPtr); } Q_EMIT usersInfoChanged(listUser); } return listUser; } QList BackendDbusHelper::ParseSessionsInfo(const QJsonObject &objRes) { QList listSessions; QJsonArray jsonArray = objRes.value("Content").toArray(); if (jsonArray.isEmpty()) { qInfo() << "ParseSessionsInfo Failed!!"; return listSessions; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strSession = objUser.value("key").toString(); listSessions.append(strSession); } } return listSessions; } QList BackendDbusHelper::ParseSaverThemes(const QJsonObject &objRes) { QList listThemes; QJsonArray jsonArray = objRes.value("Content").toArray(); if (jsonArray.isEmpty()) { qInfo() << "ParseSaverThemes Failed!!"; return listThemes; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strSession = objUser.value("key").toString(); qDebug() << "savertheme = " << strSession; listThemes.append(strSession); } } return listThemes; } QStringList BackendDbusHelper::ParseBatteryArgs(const QJsonObject &objRes) { QStringList listBattery; QJsonArray jsonArray = objRes.value("Content").toArray(); if (jsonArray.isEmpty()) { qInfo() << "ParseBatteryArgs empty!!"; return listBattery; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strBattery = objUser.value("key").toString(); listBattery.append(strBattery); } } return listBattery; } QStringList BackendDbusHelper::ParseSleepLockcheck(const QJsonObject &objRes) { QStringList sleepLockcheck; QJsonArray jsonArray = objRes.value("Content").toArray(); if (jsonArray.isEmpty()) { return sleepLockcheck; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strLockcheck = objUser.value("key").toString(); sleepLockcheck.append(strLockcheck); } } return sleepLockcheck; } QStringList BackendDbusHelper::ParseLogoutLockcheck(const QJsonObject &objRes) { QStringList logoutLockcheck; QJsonArray jsonArray = objRes.value("Content").toArray(); qDebug() << "jsonArray" << jsonArray; if (jsonArray.isEmpty()) { return logoutLockcheck; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strLockcheck = objUser.value("key").toString(); logoutLockcheck.append(strLockcheck); } } return logoutLockcheck; } QStringList BackendDbusHelper::ParseShutdownLockcheck(const QJsonObject &objRes) { QStringList shutdownLockcheck; QJsonArray jsonArray = objRes.value("Content").toArray(); if (jsonArray.isEmpty()) { return shutdownLockcheck; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strLockcheck = objUser.value("key").toString(); shutdownLockcheck.append(strLockcheck); } } return shutdownLockcheck; } bool BackendDbusHelper::ParseLogin1ReqLock(const QJsonObject &objRes) { Q_UNUSED(objRes); Q_EMIT requestLock(); return true; } bool BackendDbusHelper::ParseLogin1ReqUnLock(const QJsonObject &objRes) { Q_UNUSED(objRes); Q_EMIT requestUnlock(); return true; } bool BackendDbusHelper::ParseLogin1PrepareForSleep(const QJsonObject &objRes) { bool isSleep = objRes.value("IsSleep").toBool(false); Q_EMIT PrepareForSleep(isSleep); return true; } bool BackendDbusHelper::ParseLogin1SessionActiveChanged(const QJsonObject &objRes) { bool isActive = objRes.value("IsActive").toBool(true); Q_EMIT sessionActiveChanged(isActive); return true; } bool BackendDbusHelper::ParseBlockLogoutInhibitedChanged(const QJsonObject &objRes) { Q_EMIT blockLogoutInhibitedChanged(); return true; } bool BackendDbusHelper::ParseBlockInhibitedChanged(const QJsonObject &objRes) { QString blockInhibited = objRes.value("BlockInhibited").toString(); Q_EMIT blockInhibitedChanged(blockInhibited); return true; } bool BackendDbusHelper::ParseBatteryStatusChanged(const QJsonObject &objRes) { QString iconName = objRes.value("IconName").toString(); Q_EMIT batteryStatusChanged(iconName); return true; } bool BackendDbusHelper::ParseBatteryChanged(const QJsonObject &objRes) { QStringList listBattery; listBattery = ParseBatteryArgs(objRes); Q_EMIT batteryChanged(listBattery); return true; } bool BackendDbusHelper::ParseUPowerServiceReady(const QJsonObject &objRes) { Q_UNUSED(objRes); Q_EMIT upowerServiceReady(); return true; } bool BackendDbusHelper::isSessionActive() { bool isActive = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_IS_SESSION_ACTIVE; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "isSessionActive error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "isSessionActive Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "isSessionActive Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_IS_SESSION_ACTIVE || nRet != 0) { qInfo() << "isSessionActive Failed!!"; } else { isActive = objRes.value("IsActive").toBool(true); } } else { qInfo() << "isSessionActive Json is invalid!!"; } } } } return isActive; } bool BackendDbusHelper::setCurrentUser(QString strUserName) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SET_USER; jsonCmd["Content"] = strUserName; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); if (reply.isError()) { qWarning() << "setCurrentUser error: " << reply.error().message(); return false; } return true; } int BackendDbusHelper::switchToUser(QString strUserName) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SWITCH_TO_USER; jsonCmd["Content"] = strUserName; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "switchToUser error: " << reply.error().message(); return -1; } else { return reply.value(); } } bool BackendDbusHelper::usdExternalDoAction(int actionType) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_USD_MEDIAKEYS; jsonCmd["Content"] = actionType; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); if (reply.isError()) { qWarning() << "usdExternalDoAction error: " << reply.error().message(); return false; } return true; } QString BackendDbusHelper::getDefaultAuthUser() { QString strDefUser = ""; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_DEFAULT_AUTH_USER; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getDefaultAuthUser error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getDefaultAuthUser Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getDefaultAuthUser Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_DEFAULT_AUTH_USER || nRet != 0) { qInfo() << "getDefaultAuthUser Failed!!"; } else { strDefUser = objRes.value("Content").toString(); } } else { qInfo() << "getDefaultAuthUser Json is invalid!!"; } } } } return strDefUser; } QString BackendDbusHelper::getCurrentUser() { QString strCurUser = ""; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_CURRENT_USER; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getCurrentUser error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getCurrentUser Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getCurrentUser Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_CURRENT_USER || nRet != 0) { qInfo() << "getCurrentUser Failed!!"; } else { strCurUser = objRes.value("Content").toString(); } } else { qInfo() << "getCurrentUser Json is invalid!!"; } } } } return strCurUser; } bool BackendDbusHelper::ParseCurrentUser(const QJsonObject &objRes) { QString strCurUser = objRes.value("Content").toString(); Q_EMIT currentUserChanged(strCurUser); return true; } bool BackendDbusHelper::ParseCurrentSession(const QJsonObject &objRes) { QString strSession = objRes.value("Content").toString(); Q_EMIT currentSessionChanged(strSession); return true; } QVariant BackendDbusHelper::getScreenSaverConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_SCREENSAVER_CONF; jsonCmd["Key"] = strKey; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getScreenSaverConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getScreenSaverConf Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getScreenSaverConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_SCREENSAVER_CONF || nRet != 0) { qInfo() << "getScreenSaverConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getScreenSaverConf Json is invalid!!"; } } } } return varValue; } QStringList BackendDbusHelper::getDefaultPhotoPath() { QStringList listArgs; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_DEFAULT_PHOTOPATH; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getDefaultPhotoPath error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getDefaultPhotoPath Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getDefaultPhotoPath Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); QJsonArray jsonArray = objRes.value("Content").toArray(); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_DEFAULT_PHOTOPATH || nRet != 0 || jsonArray.isEmpty()) { qInfo() << "getDefaultPhotoPath Failed!!"; } else { listArgs = ParsePhotoPathArgs(objRes); } } else { qInfo() << "getDefaultPhotoPath Json is invalid!!"; } } } } return listArgs; } QStringList BackendDbusHelper::getCustomPhotoPath() { QStringList listArgs; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_CUSTOM_PHOTOPATH; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getCustomPhotoPath error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getCustomPhotoPath Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getCustomPhotoPath Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); QJsonArray jsonArray = objRes.value("Content").toArray(); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_CUSTOM_PHOTOPATH || nRet != 0 || jsonArray.isEmpty()) { qInfo() << "getCustomPhotoPath Failed!!"; } else { listArgs = ParsePhotoPathArgs(objRes); } } else { qInfo() << "getCustomPhotoPath Json is invalid!!"; } } } } return listArgs; } bool BackendDbusHelper::setScreenSaverConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_SCREENSAVER_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setScreenSaverConf error: " << reply.error().message(); return false; } return true; } bool BackendDbusHelper::ParseScreenSaverConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT screenSaverConfChanged(strKey, objRes.value("Value").toVariant()); return true; } QStringList BackendDbusHelper::ParsePhotoPathArgs(const QJsonObject &objRes) { QStringList listBattery; QJsonArray jsonArray = objRes.value("Content").toArray(); if (jsonArray.isEmpty()) { qInfo() << "ParsePhotoPathArgs Failed!!"; return listBattery; } else { for (auto jsonValue : jsonArray) { QJsonObject objUser = jsonValue.toObject(); QString strBattery = objUser.value("key").toString(); listBattery.append(strBattery); } } return listBattery; } bool BackendDbusHelper::ParseLockScreenConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT lockScreenConfChanged(strKey, objRes.value("Value").toVariant()); return true; } bool BackendDbusHelper::ParsePowerManagerConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT powerManagerConfChanged(strKey, objRes.value("Value").toVariant()); return true; } bool BackendDbusHelper::ParseMateBgConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT mateBgConfChanged(strKey, objRes.value("Value").toVariant()); return true; } bool BackendDbusHelper::ParseUkccPluginsConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT ukccPluginsConfChanged(strKey, objRes.value("Value").toVariant()); return true; } bool BackendDbusHelper::ParseThemeStyleConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT themeStyleConfChanged(strKey, objRes.value("Value").toVariant()); return true; } bool BackendDbusHelper::ParseSessionConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT sessionConfChanged(strKey, objRes.value("Value").toVariant()); return true; } bool BackendDbusHelper::ParseKeyboardConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); if (strKey == KEY_CAPSLOCK_STATUS) { bool state = objRes.value("Value").toBool(); Q_EMIT capslockConfChanged(state); } return true; } bool BackendDbusHelper::ParseUsdMediaKeysConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT usdMediaKeysConfChanged(strKey, objRes.value("Value").toString()); return true; } bool BackendDbusHelper::ParseUsdMediaStateKeysConf(const QJsonObject &objRes) { QString strKey = objRes.value("Key").toString(); Q_EMIT usdMediaStateKeysConfChanged(strKey, objRes.value("Value").toInt()); return true; } QVariant BackendDbusHelper::getLockScreenConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_LOCKSCREEN_CONF; jsonCmd["Key"] = strKey; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getLockScreenConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getLockScreenConf Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getLockScreenConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_LOCKSCREEN_CONF || nRet != 0) { qInfo() << "getLockScreenConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getLockScreenConf Json is invalid!!"; } } } } return varValue; } QStringList BackendDbusHelper::getLogoutLockcheck() { QStringList listArgs; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SESSION_GET_LOGOUT_LOCKCHECK; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getLogoutLockcheck error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getLogoutLockcheck Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getLogoutLockcheck Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_SESSION_GET_LOGOUT_LOCKCHECK || nRet != 0) { qInfo() << "getLogoutLockcheck Failed!!"; } else { listArgs = ParseLogoutLockcheck(objRes); } } else { qInfo() << "getLogoutLockcheck Json is invalid!!"; } } } } return listArgs; } QStringList BackendDbusHelper::getShutdownLockcheck() { QStringList listArgs; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SESSION_GET_SHUTDOWN_LOCKCHECK; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getShutdownLockcheck error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getShutdownLockcheck Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getShutdownLockcheck Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_SESSION_GET_SHUTDOWN_LOCKCHECK || nRet != 0) { qInfo() << "getShutdownLockcheck Failed!!"; } else { listArgs = ParseShutdownLockcheck(objRes); } } else { qInfo() << "getShutdownLockcheck Json is invalid!!"; } } } } return listArgs; } QList BackendDbusHelper::getSaverThemes() { QList listThemes; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOCK_SCREEN_GET_THEMES; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getSaverThemes error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getSaverThemes Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getSaverThemes Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOCK_SCREEN_GET_THEMES || nRet != 0) { qInfo() << "getSaverThemes Failed!!"; } else { listThemes = ParseSaverThemes(objRes); } } else { qInfo() << "getSaverThemes Json is invalid!!"; } } } } return listThemes; } bool BackendDbusHelper::setLockScreenConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_LOCKSCREEN_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setLockScreenConf error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getPowerManagerConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_POWERMANAGER_CONF; jsonCmd["Key"] = strKey; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerConf Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_POWERMANAGER_CONF || nRet != 0) { qInfo() << "getPowerManagerConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getPowerManagerConf Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setPowerManagerConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_POWERMANAGER_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setPowerManagerConf error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getMateBgConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_MATEBG_CONF; jsonCmd["Key"] = strKey; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getMateBgConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getMateBgConf Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getMateBgConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_MATEBG_CONF || nRet != 0) { qInfo() << "getMateBgConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getMateBgConf Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setMateBgConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_MATEBG_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setMateBgConf error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getUkccPluginsConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_UKCCPLUGINS_CONF; jsonCmd["Key"] = strKey; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getUkccPluginsConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getUkccPluginsConf Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getUkccPluginsConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_UKCCPLUGINS_CONF || nRet != 0) { qInfo() << "getUkccPluginsConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getUkccPluginsConf Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setUkccPluginsConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_UKCCPLUGINS_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setUkccPluginsConf error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getThemeStyleConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_THEMESTYLE_CONF; jsonCmd["Key"] = strKey; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getThemeStyleConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getThemeStyleConf Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getThemeStyleConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_THEMESTYLE_CONF || nRet != 0) { qInfo() << "getThemeStyleConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getThemeStyleConf Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setThemeStyleConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_THEMESTYLE_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setThemeStyleConf error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getSessionConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_SESSION_CONF; jsonCmd["Key"] = strKey; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getSessionConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getSessionConf Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getSessionConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_SESSION_CONF || nRet != 0) { qInfo() << "getSessionConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getSessionConf Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setSessionConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_SESSION_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setSessionConf error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getKeyboardConf(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_KEYBOARD_CONF; jsonCmd["Key"] = strKey; QJsonObject objRes; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getKeyboardConf error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getKeyboardConf Parse json failed!!"; } else { objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getKeyboardConf Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_KEYBOARD_CONF || nRet != 0) { qInfo() << "getKeyboardConf Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getKeyboardConf Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setKeyboardConf(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_KEYBOARD_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setKeyboardConf error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getUsdMediaStateKeys(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIA_STATE_KEYS_CONF; jsonCmd["Key"] = strKey; QJsonObject objRes; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getUsdMediaStateKeys error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getUsdMediaStateKeys Parse json failed!!"; } else { objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getUsdMediaStateKeys Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIA_STATE_KEYS_CONF || nRet != 0) { qInfo() << "getUsdMediaStateKeys Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getUsdMediaStateKeys Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setUsdMediaStateKeys(QString strKey, QVariant value) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_SET_USD_MEDIA_STATE_KEYS_CONF; jsonCmd["Key"] = strKey; switch (value.type()) { case QVariant::Bool: jsonCmd["Value"] = value.toBool(); break; case QVariant::Int: jsonCmd["Value"] = value.toInt(); break; case QVariant::String: jsonCmd["Value"] = value.toString(); break; case QVariant::UInt: jsonCmd["Value"] = value.toDouble(); break; case QVariant::Double: jsonCmd["Value"] = value.toDouble(); break; default: return false; } QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setUsdMediaStateKeys error: " << reply.error().message(); return false; } return true; } QVariant BackendDbusHelper::getUsdMediaKeys(QString strKey) { QVariant varValue; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIAKEYS_CONF; jsonCmd["Key"] = strKey; QJsonObject objRes; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getUsdMediaKeys error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getUsdMediaKeys Parse json failed!!"; } else { objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getUsdMediaKeys Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GSETTINGS_GET_USD_MEDIAKEYS_CONF || nRet != 0) { qInfo() << "getUsdMediaKeys Failed!!"; } else { varValue = objRes.value(strKey); } } else { qInfo() << "getUsdMediaKeys Json is invalid!!"; } } } } return varValue; } bool BackendDbusHelper::setPowerManager(bool isForce, QString strFuncName, bool isSessionTools /* = false*/) { qWarning() << __LINE__ << __FUNCTION__ << "====================" << isForce; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_SET_POWER_MANAGER; jsonCmd["Force"] = isForce; jsonCmd["Content"] = strFuncName; jsonCmd["IsSessionTools"] = isSessionTools; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << __LINE__ << __FUNCTION__ << reply.error().message(); return false; } return true; } bool BackendDbusHelper::getPowerManagerCanSuspend() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSUSPEND; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerCanSuspend error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerCanSuspend Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerCanSuspend Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSUSPEND || nRet != 0) { qInfo() << "getPowerManagerCanSuspend Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "getPowerManagerCanSuspend Json is invalid!!"; } } } } return canFlag; } bool BackendDbusHelper::getPowerManagerCanReboot() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANREBOOT; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerCanReboot error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerCanReboot Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerCanReboot Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANREBOOT || nRet != 0) { qInfo() << "getPowerManagerCanReboot Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "getPowerManagerCanReboot Json is invalid!!"; } } } } return canFlag; } bool BackendDbusHelper::getPowerManagerCanPowerOff() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANPOWEROFF; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerCanPowerOff error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerCanPowerOff Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerCanPowerOff Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANPOWEROFF || nRet != 0) { qInfo() << "getPowerManagerCanPowerOff Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "getPowerManagerCanPowerOff Json is invalid!!"; } } } } return canFlag; } bool BackendDbusHelper::getPowerManagerCanSwitchUser() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSWITCHUSER; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerCanSwitchUser error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerCanSwitchUser Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerCanSwitchUser Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANSWITCHUSER || nRet != 0) { qInfo() << "getPowerManagerCanSwitchUser Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "getPowerManagerCanSwitchUser Json is invalid!!"; } } } } return canFlag; } bool BackendDbusHelper::getPowerManagerCanHibernate() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANHIBERNATE; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerCanHibernate error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerCanHibernate Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerCanHibernate Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANHIBERNATE || nRet != 0) { qInfo() << "getPowerManagerCanHibernate Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "getPowerManagerCanHibernate Json is invalid!!"; } } } } return canFlag; } bool BackendDbusHelper::getPowerManagerCanLogout() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOGOUT; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerCanLogout error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerCanLogout Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerCanLogout Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOGOUT || nRet != 0) { qInfo() << "getPowerManagerCanLogout Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "getPowerManagerCanLogout Json is invalid!!"; } } } } return canFlag; } bool BackendDbusHelper::getPowerManagerCanLockScreen() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOCKSCREEN; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPowerManagerCanLockScreen error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPowerManagerCanLockScreen Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPowerManagerCanLockScreen Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_LOGIN1_GET_POWER_MANAGER_CANLOCKSCREEN || nRet != 0) { qInfo() << "getPowerManagerCanLockScreen Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "getPowerManagerCanLockScreen Json is invalid!!"; } } } } return canFlag; } bool BackendDbusHelper::checkSystemUpgrade() { bool canFlag = true; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SYSTEM_UPGRADE_CHECK; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "checkSystemUpgrade error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "checkSystemUpgrade Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "checkSystemUpgrade Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_SYSTEM_UPGRADE_CHECK || nRet != 0) { qInfo() << "checkSystemUpgrade Failed!!"; } else { canFlag = objRes.value("Content").toBool(); } } else { qInfo() << "checkSystemUpgrade Json is invalid!!"; } } } } return canFlag; } QString BackendDbusHelper::getPublicEncrypt() { QString publicEncrypt = ""; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_GET_PUBLIC_KEY; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getPublicEncrypt error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getPublicEncrypt Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getPublicEncrypt Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret") && objRes.contains("Content")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_GET_PUBLIC_KEY || nRet != 0) { qInfo() << "getPublicEncrypt Failed!!"; } else { publicEncrypt = objRes.value("Content").toString(); } } else { qInfo() << "getPublicEncrypt Json is invalid!!"; } } } } return publicEncrypt; } bool BackendDbusHelper::sendPassword(const QString username, QByteArray password) { QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_SEND_USER_PASSWORD; QJsonObject objsingleLoginInfo; objsingleLoginInfo["username"] = username; objsingleLoginInfo["password"] = QJsonValue::fromVariant(password); jsonCmd["Content"] = objsingleLoginInfo; QDBusPendingReply reply = SetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "setCurrentSession error: " << reply.error().message(); return false; } return true; } QString BackendDbusHelper::getAutologinUserHint() { QString strUser; QJsonObject jsonCmd; jsonCmd["CmdId"] = LOCK_CMD_ID_PAMAUTH_GET_AUTOLOGINUSERHINT; QDBusPendingReply reply = GetInformation(QString(QJsonDocument(jsonCmd).toJson())); reply.waitForFinished(); if (reply.isError()) { qWarning() << "getAutologinUserHint error: " << reply.error().message(); } else { QString strResponse = reply.value(); QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(strResponse.toUtf8(), &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { qInfo() << "getAutologinUserHint Parse json failed!!"; } else { QJsonObject objRes = jsonDoc.object(); if (objRes.isEmpty()) { qInfo() << "getAutologinUserHint Json is null!!"; } else { if (objRes.contains("CmdId") && objRes.contains("Ret")) { int nCmdId = objRes.value("CmdId").toInt(-1); int nRet = objRes.value("Ret").toInt(-1); if (nCmdId != LOCK_CMD_ID_PAMAUTH_GET_AUTOLOGINUSERHINT || nRet != 0) { qInfo() << "getAutologinUserHint Failed!!"; } else { strUser = objRes.value("Content").toString(); } } else { qInfo() << "getAutologinUserHint Json is invalid!!"; } } } } return strUser; } ukui-screensaver/src/lock-dialog/screensaverwndadaptor.h0000664000175000017500000000501515172041106022522 0ustar fengfeng/* * Copyright (C) 2023 KylinSoft Co., Ltd. * * This program is free software; you can 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * **/ #ifndef SCREENSAVERWNDADAPTOR_H #define SCREENSAVERWNDADAPTOR_H #include #include #include "fullbackgroundwidget.h" QT_BEGIN_NAMESPACE class QByteArray; template class QList; template class QMap; class QString; class QVariant; QT_END_NAMESPACE class ScreenSaverWndAdaptor : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.ukui.ScreenSaverWnd") Q_CLASSINFO( "D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") public: ScreenSaverWndAdaptor(FullBackgroundWidget *parent = nullptr); virtual ~ScreenSaverWndAdaptor(); public Q_SLOTS: int RegisteSubWnd(quint64 uWndId); int UnRegisteSubWnd(quint64 uWndId); QList GetSubWndIds(); bool IsStartupMode(); Q_SIGNALS: void SubWndChanged(int nCount); void StartupModeChanged(bool isStartup); private: FullBackgroundWidget *m_parentWidget = nullptr; }; #endif // SCREENSAVERWNDADAPTOR_H