./0000755000015600001650000000000012705421115011073 5ustar jenkinsjenkins./tests/0000755000015600001650000000000012705421114012234 5ustar jenkinsjenkins./tests/shared.pri0000644000015600001650000000347512705421114014227 0ustar jenkinsjenkinsPROVIDER = Canonical ## Where the Qt Creator headers are located at QTCREATOR_SOURCES = $$(QTC_SOURCE) isEmpty(QTCREATOR_SOURCES):QTCREATOR_SOURCES=/usr/src/qtcreator ## Where our plugin will be compiled to IDE_BUILD_TREE = $$(QTC_BUILD) isEmpty(IDE_BUILD_TREE):IDE_BUILD_TREE=../../builddir UBUNTU_LOCAL_BUILD = $$(UBUNTU_QTC_PLUGIN_LOCALBUILD) !isEmpty(UBUNTU_LOCAL_BUILD) { message("!!!!!!!!!!BUILDING LOCAL VERSION OF PLUGIN !!!!!!!!!!!!!!!!!!!") USE_USER_DESTDIR = yes PATHSTR = '\\"$${PWD}/../share/qtcreator\\"' DEFINES += UBUNTU_RESOURCE_PATH_LOCAL=\"$${PATHSTR}\" UBUNTU_BUILD_LOCAL } include($$QTCREATOR_SOURCES/qtcreator.pri) isEmpty(TEMPLATE):TEMPLATE=app QT += testlib CONFIG += qt warn_on console depend_includepath testcase CONFIG -= app_bundle DEFINES -= QT_NO_CAST_FROM_ASCII # prefix test binary with tst_ !contains(TARGET, ^tst_.*):TARGET = $$join(TARGET,,"tst_") QMAKE_RPATHDIR += $$IDE_BUILD_TREE/$$IDE_LIBRARY_BASENAME/qtcreator QMAKE_RPATHDIR += $$IDE_PLUGIN_PATH/QtProject QMAKE_RPATHDIR += $$IDE_PLUGIN_PATH/Canonical IDE_PLUGIN_RPATH = $$join(QMAKE_RPATHDIR, ":") QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${IDE_PLUGIN_RPATH}\' INCLUDEPATH += $$QTCREATOR_SOURCES/src/ ## make sure the QtProject libs are available when building locally !isEmpty(UBUNTU_LOCAL_BUILD) { DESTDIRAPPNAME = "qtcreator" DESTDIRBASE = "$$(XDG_DATA_HOME)" isEmpty(DESTDIRBASE):DESTDIRBASE = "$$(HOME)/.local/share/data" else:DESTDIRBASE = "$$DESTDIRBASE/data" LIBS += -L$$DESTDIRBASE/QtProject/$$DESTDIRAPPNAME/plugins/$$QTCREATOR_VERSION/QtProject LIBS += -L$$DESTDIRBASE/QtProject/$$DESTDIRAPPNAME/plugins/$$QTCREATOR_VERSION/Canonical } LIBS += -L$$[QT_INSTALL_LIBS]/qtcreator LIBS += -L$$[QT_INSTALL_LIBS]/qtcreator/plugins/QtProject LIBS += -L$$[QT_INSTALL_LIBS]/qtcreator/plugins/Canonical ./tests/tests.pro0000644000015600001650000000015612705421114014122 0ustar jenkinsjenkinsTEMPLATE = subdirs SUBDIRS = validation \ adbportlist \ ubuntuversion \ manifest \ autopilot ./tests/autopilot-lookup.py0000755000015600001650000000425412705421114016145 0ustar jenkinsjenkins#!/usr/bin/env python from autopilot.introspection import get_proxy_object_for_existing_process from autopilot.introspection.dbus import StateNotFoundError import sys import subprocess import re if __name__ == '__main__': def findInnermostChild (objectlist, x,y): for child in objectlist: try: object_rect = child.globalRect except AttributeError: #object does not have a rect but maybe the childs? children = child.get_children() if (len(children) > 0): findInnermostChild(children,x,y) continue except StateNotFoundError: print "StateNotFoundError" return x1 = int(object_rect.x) x2 = int(x1 + object_rect.width) y1 = int(object_rect.y) y2 = int(y1 + object_rect.height) if (x >= x1 and x <= x2 and y >= y1 and y <= y2): children = child.get_children() if (len(children) > 0): findInnermostChild(children,x,y) else: child.print_tree(output=sys.stdout, maxdepth=1, _curdepth=0) if len(sys.argv) < 2: print "Error: please supply executable" sys.exit(1) app_exec = str(sys.argv[1]) introspec_obj=None proc = subprocess.Popen([app_exec,'-testability'],stderr=subprocess.PIPE) while True: line = proc.stderr.readline() if not line: break; if not introspec_obj: retry=0 while(retry < 5): print("Trying to get the Proxy object") try: introspec_obj = get_proxy_object_for_existing_process(pid=proc.pid) print("Done") break except: retry+=1 match = re.match(r"MOUSEDOWN\s+([0-9]+):([0-9]+)",line) if match: print ("MouseDown:", match.group(1),match.group(2)) print ("Searching innermost object") findInnermostChild(introspec_obj.get_children(),int(match.group(1)),int(match.group(2))) print ("Done") ./tests/validation/0000755000015600001650000000000012705421114014366 5ustar jenkinsjenkins./tests/validation/resources.qrc0000644000015600001650000000022012705421114017101 0ustar jenkinsjenkins simplesection.json fulloutput.json ./tests/validation/tst_validation.h0000644000015600001650000000153512705421114017567 0ustar jenkinsjenkins#ifndef TST_VALIDATION_H #define TST_VALIDATION_H #include #include "ubuntuvalidationresultmodel.h" class QEventLoop; class tst_Validation : public QObject { Q_OBJECT public: tst_Validation(); private slots: void testSimpleSectionParse (); void testCompleteOutput(); void testIncrementalParse(); private: void testFullOutputLint(Ubuntu::Internal::ClickRunChecksParser::DataItem *item, bool* passed); void testFullOutputDesktop(Ubuntu::Internal::ClickRunChecksParser::DataItem *item, bool *passed); void testFullOutputSecurity(Ubuntu::Internal::ClickRunChecksParser::DataItem *item, bool *passed); void testFullOutputFunctional(Ubuntu::Internal::ClickRunChecksParser::DataItem *item, bool *passed); Ubuntu::Internal::ClickRunChecksParser *m_parser; QEventLoop* m_loop; }; #endif // TST_VALIDATION_H ./tests/validation/simplesection.json0000644000015600001650000000057312705421114020144 0ustar jenkinsjenkins= functional = { "error": { "error1": { "link": "http://somelink.com", "text": "Error text" } }, "info": { "test1": { "text": "OK" }, "test2": { "text": "OK" }, "test3": { "text": "OK" } }, "warn": { "warning1": { "link": "http://somelink.com/warning", "text": "Warning message" } } } ./tests/validation/fulloutput.json0000644000015600001650000000207612705421114017511 0ustar jenkinsjenkins= lint = { "error": { "lint_error": { "text": "errorText" } }, "info": { "lint_info1": { "text": "OK" }, "lint_info2": { "text": "OK" }, "lint_info3": { "text": "OK" } }, "warn": {} } = desktop = { "error": { "desktop_error (test1)": { "link": "http://somelink.com", "text": "Error text for desktop file" } }, "info": { "desktop_info (test1)": { "text": "OK" } }, "warn": { "desktop_warn (test1)": { "link": "http://somelink.com/warning", "text": "Warning text for desktop file" } } } = security = { "error": {}, "info": { "security_test1 (test.json)": { "text": "OK" }, "security_test2 (test.json)": { "text": "OK" }, "security_test3 (test.json)": { "text": "OK" } }, "warn": {} } = functional = { "error": {}, "info": { "functional_test1": { "text": "OK" }, "functional_test2": { "text": "OK" }, "functional_test3": { "text": "OK" } }, "warn": {} } ./tests/validation/validation.pro0000644000015600001650000000072612705421114017247 0ustar jenkinsjenkinsQT = core gui CONFIG += c++11 #QTC_LIB_DEPENDS += utils include(../shared.pri) PLUGIN_SRC_ROOT = $${PWD}/../../src/ubuntu INCLUDEPATH += $${PLUGIN_SRC_ROOT} HEADERS += \ $${PLUGIN_SRC_ROOT}/ubuntuvalidationresultmodel.h \ tst_validation.cpp \ tst_validation.h SOURCES += \ $${PLUGIN_SRC_ROOT}/ubuntuvalidationresultmodel.cpp \ tst_validation.cpp OTHER_FILES += \ simplesection.json \ fulloutput.json RESOURCES += \ resources.qrc ./tests/validation/tst_validation.cpp0000644000015600001650000001737012705421114020126 0ustar jenkinsjenkins#include "tst_validation.h" #include #include #include #include #include using namespace Ubuntu::Internal; #define VERIFY_ITEM(item,itemType,itemText,itemIcon,cCount) \ do { \ QCOMPARE(item->type,itemType); \ QCOMPARE(item->text,itemText); \ QCOMPARE(item->icon,itemIcon); \ QCOMPARE(item->children.size(),cCount); \ }while(0) tst_Validation::tst_Validation() : m_parser(0) , m_loop(new QEventLoop(this)) { } /*! * \brief tst_Validation::testSimpleSectionParse * Tries to parse a simple section */ void tst_Validation::testSimpleSectionParse() { QList items; ClickRunChecksParser parser; auto appendItemCallback = [this,&items](ClickRunChecksParser::DataItem *newItem){ items.append(newItem); }; connect(&parser,&ClickRunChecksParser::parsedNewTopLevelItem,appendItemCallback); QFile sourceFile(":/validation/simplesection.json"); QVERIFY(sourceFile.open(QIODevice::ReadOnly)); QTextStream in(&sourceFile); parser.beginRecieveData(in.readAll()); //until now no item can be available because we have only one section //and the parser always waits for end of the document, or begin of the next section QVERIFY(items.isEmpty()); parser.endRecieveData(); QVERIFY(items.length() == 1); VERIFY_ITEM(items[0],QString("functional"),QString("No description"),ClickRunChecksParser::Error,5); VERIFY_ITEM(items[0]->children[0],QString("error1"),QString("Error text"),ClickRunChecksParser::Error,0); QCOMPARE(items[0]->children[0]->link,QUrl("http://somelink.com")); VERIFY_ITEM(items[0]->children[1],QString("warning1"),QString("Warning message"),ClickRunChecksParser::Warning,0); QCOMPARE(items[0]->children[1]->link,QUrl("http://somelink.com/warning")); VERIFY_ITEM(items[0]->children[2],QString("test1"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(items[0]->children[3],QString("test2"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(items[0]->children[4],QString("test3"),QString("OK"),ClickRunChecksParser::Check,0); qDeleteAll(items.begin(),items.end()); } void tst_Validation::testFullOutputLint(ClickRunChecksParser::DataItem *item, bool *passed) { VERIFY_ITEM(item,QString("lint"),QString("No description"),ClickRunChecksParser::Error,4); VERIFY_ITEM(item->children[0],QString("lint_error"),QString("errorText"),ClickRunChecksParser::Error,0); VERIFY_ITEM(item->children[1],QString("lint_info1"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(item->children[2],QString("lint_info2"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(item->children[3],QString("lint_info3"),QString("OK"),ClickRunChecksParser::Check,0); *passed=true; } void tst_Validation::testFullOutputDesktop(ClickRunChecksParser::DataItem *item, bool *passed) { VERIFY_ITEM(item,QString("desktop"),QString("No description"),ClickRunChecksParser::Error,3); VERIFY_ITEM(item->children[0],QString("desktop_error (test1)"),QString("Error text for desktop file"),ClickRunChecksParser::Error,0); QCOMPARE(item->children[0]->link,QUrl("http://somelink.com")); VERIFY_ITEM(item->children[1],QString("desktop_warn (test1)"),QString("Warning text for desktop file"),ClickRunChecksParser::Warning,0); QCOMPARE(item->children[1]->link,QUrl("http://somelink.com/warning")); VERIFY_ITEM(item->children[2],QString("desktop_info (test1)"),QString("OK"),ClickRunChecksParser::Check,0); *passed=true; } void tst_Validation::testFullOutputSecurity(ClickRunChecksParser::DataItem *item, bool *passed) { VERIFY_ITEM(item,QString("security"),QString("No description"),ClickRunChecksParser::Check,3); VERIFY_ITEM(item->children[0],QString("security_test1 (test.json)"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(item->children[1],QString("security_test2 (test.json)"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(item->children[2],QString("security_test3 (test.json)"),QString("OK"),ClickRunChecksParser::Check,0); *passed=true; } void tst_Validation::testFullOutputFunctional(ClickRunChecksParser::DataItem *item, bool *passed) { VERIFY_ITEM(item,QString("functional"),QString("No description"),ClickRunChecksParser::Check,3); VERIFY_ITEM(item->children[0],QString("functional_test1"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(item->children[1],QString("functional_test2"),QString("OK"),ClickRunChecksParser::Check,0); VERIFY_ITEM(item->children[2],QString("functional_test3"),QString("OK"),ClickRunChecksParser::Check,0); *passed=true; } /*! * \brief tst_Validation::testCompleteOutput * Tests to parse a complete output from click-run-checks */ void tst_Validation::testCompleteOutput() { QList items; ClickRunChecksParser parser; connect(&parser,&ClickRunChecksParser::parsedNewTopLevelItem, [this,&items](ClickRunChecksParser::DataItem *newItem){ items.append(newItem); }); QFile sourceFile(":/validation/fulloutput.json"); QVERIFY(sourceFile.open(QIODevice::ReadOnly)); QTextStream in(&sourceFile); parser.beginRecieveData(in.readAll()); //until now only 3 of the 4 items can be available because we have four sections //and the parser always waits for end of the document, or begin of the next section QCOMPARE(items.length(),3); parser.endRecieveData(); QCOMPARE(items.length(),4); bool passed = false; testFullOutputLint(items[0],&passed); if(!passed) return; passed = false; testFullOutputDesktop(items[1],&passed); if(!passed) return; passed = false; testFullOutputSecurity(items[2],&passed); if(!passed) return; passed = false; testFullOutputFunctional(items[3],&passed); if(!passed) return; qDeleteAll(items.begin(),items.end()); } /** * @brief tst_Validation::testIncrementalParse * Tests the incremental parsing of ClickRunChecksParser, * reads the fulloutput.json file and splits it up in chunks */ void tst_Validation::testIncrementalParse() { QList items; ClickRunChecksParser parser; connect(&parser,&ClickRunChecksParser::parsedNewTopLevelItem, [this,&items](ClickRunChecksParser::DataItem *newItem){ items.append(newItem); }); QFile sourceFile(":/validation/fulloutput.json"); QVERIFY(sourceFile.open(QIODevice::ReadOnly)); QTextStream in(&sourceFile); QString input = in.readAll(); //parse to somewhere in the middle of a section int offset = input.indexOf("info",input.indexOf("= desktop =")); parser.addRecievedData(input.left(offset)); input = input.mid(offset); //one section should be available now QCOMPARE(items.length(),1); bool passed = false; testFullOutputLint(items[0],&passed); if(!passed) return; /* * Try to parse more than one section, * Add the beginning of the next section so the * parser knows where the last one ends */ QString functionalSectionStart("= functional ="); offset = input.indexOf(functionalSectionStart)+functionalSectionStart.length(); parser.addRecievedData(input.left(offset)); input = input.mid(offset); //there should be 3 sections now QCOMPARE(items.length(),3); passed = false; testFullOutputDesktop(items[1],&passed); if(!passed) return; passed = false; testFullOutputSecurity(items[2],&passed); if(!passed) return; //parse the remaining bits parser.endRecieveData(input); //there should be 4 sections now QCOMPARE(items.length(),4); passed = false; testFullOutputFunctional(items[3],&passed); if(!passed) return; qDeleteAll(items.begin(),items.end()); } QTEST_GUILESS_MAIN(tst_Validation) ./tests/com.ubuntu.pkexec.ubuntu-sdk-autopilot.policy0000644000015600001650000000374212705421114023156 0ustar jenkinsjenkins Ubuntu SDK http://developer.ubuntu.com Authentication is required to create click chroot ubuntu-sdk auth_admin auth_admin yes /usr/share/qtcreator/ubuntu/scripts/click_create_target true Authentication is required to destroy click chroot ubuntu-sdk auth_admin auth_admin yes /usr/share/qtcreator/ubuntu/scripts/click_destroy_target true Authentication is required to create emulator instance ubuntu-sdk auth_admin auth_admin yes /usr/share/qtcreator/ubuntu/scripts/local_create_emulator true ./tests/ubuntuversion/0000755000015600001650000000000012705421114015164 5ustar jenkinsjenkins./tests/ubuntuversion/lsb-release0000644000015600001650000000020112705421114017276 0ustar jenkinsjenkinsDISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.10 DISTRIB_CODENAME=utopic DISTRIB_DESCRIPTION="Ubuntu Utopic Unicorn (development branch)" ./tests/ubuntuversion/resources.qrc0000644000015600001650000000014712705421114017707 0ustar jenkinsjenkins lsb-release ./tests/ubuntuversion/tst_ubuntuversiontest.h0000644000015600001650000000037512705421114022064 0ustar jenkinsjenkins#ifndef TST_UBUNTUVERSIONTEST_H #define TST_UBUNTUVERSIONTEST_H #include class UbuntuVersionTest : public QObject { Q_OBJECT public: UbuntuVersionTest(); private slots: void testParse (); }; #endif // TST_UBUNTUVERSIONTEST_H ./tests/ubuntuversion/ubuntuversion.pro0000644000015600001650000000067112705421114020642 0ustar jenkinsjenkinsQT = core gui CONFIG += c++11 QTC_LIB_DEPENDS += utils QTC_PLUGIN_DEPENDS += coreplugin include(../shared.pri) PLUGIN_SRC_ROOT = $${PWD}/../../src/ubuntu INCLUDEPATH += $${PLUGIN_SRC_ROOT} DEFINES += SRCDIR=\\\"$$PWD/\\\" SOURCES += tst_ubuntuversiontest.cpp \ $${PLUGIN_SRC_ROOT}/ubuntuversion.cpp HEADERS += \ tst_ubuntuversiontest.h \ $${PLUGIN_SRC_ROOT}/ubuntuversion.h OTHER_FILES += RESOURCES += \ resources.qrc ./tests/ubuntuversion/tst_ubuntuversiontest.cpp0000644000015600001650000000123612705421114022414 0ustar jenkinsjenkins#include #include #include "tst_ubuntuversiontest.h" #include #include using namespace Ubuntu::Internal; UbuntuVersionTest::UbuntuVersionTest() { } void UbuntuVersionTest::testParse() { QScopedPointer ver(UbuntuVersion::fromLsbFile(":/ubuntuversion/lsb-release")); QVERIFY(!ver.isNull()); QCOMPARE(ver->id(),QStringLiteral("Ubuntu")); QCOMPARE(ver->release(),QStringLiteral("14.10")); QCOMPARE(ver->codename(),QStringLiteral("utopic")); QCOMPARE(ver->description(),QStringLiteral("\"Ubuntu Utopic Unicorn (development branch)\"")); } QTEST_GUILESS_MAIN(UbuntuVersionTest) ./tests/device/0000755000015600001650000000000012705421114013473 5ustar jenkinsjenkins./tests/device/check-packages0000755000015600001650000000353312705421114016256 0ustar jenkinsjenkins#!/bin/bash ################################################################################ # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen ################################################################################ # Required packages PACKAGES="software-properties-common gdebi-core fakeroot dh-make build-essential qt5-default qtbase5-dev qtdeclarative5-dev libqt5xmlpatterns5-dev qtscript5-dev qttools5-dev qt3d5-dev qtmultimedia5-dev libqt5svg5-dev libqt5graphicaleffects5 qtdeclarative5-dev-tools qttools5-dev-tools qtlocation5-dev qtpim5-dev qt-components-ubuntu ubuntu-dev-tools debhelper openssh-server" # Failure counter F_I=0 # Success counter S_I=0 # Total counter T_I=0 ####################################### # Checks for package availability echo for PACKAGE in ${PACKAGES}; do echo -n "Checking if '${PACKAGE}' is available... " FOUND=`apt-cache policy ${PACKAGE}|grep -i candidate|wc -l` if [[ ${FOUND} -eq 0 ]]; then echo "!NOT FOUND!" let "F_I = $F_I + 1" else echo "[OK]" let "S_I = $S_I + 1" fi let "T_I = $T_I + 1" done ####################################### # Print summary echo echo " Found packages: ${S_I}/${T_I}" echo " Missing packages: ${F_I}/${T_I}" echo exit ${F_I} ./tests/autopilot/0000755000015600001650000000000012705421114014254 5ustar jenkinsjenkins./tests/autopilot/ubuntusdk-autopilot.files0000644000015600001650000000013512705421114021341 0ustar jenkinsjenkinsqtcreator/tests/__init__.py qtcreator/tests/test_simple_app_template.py qtcreator/__init__.py./tests/autopilot/ubuntusdk-autopilot.includes0000644000015600001650000000000012705421114022034 0ustar jenkinsjenkins./tests/autopilot/autopilot.pro0000644000015600001650000000051012705421114017012 0ustar jenkinsjenkinsTEMPLATE=aux OTHER_FILES += \ ubuntusdk-autopilot.config \ ubuntusdk-autopilot.files \ ubuntusdk-autopilot.includes \ ubuntusdk-autopilot.creator \ qtcreator_plugin_ubuntu/__init__.py \ qtcreator_plugin_ubuntu/tests/__init__.py \ qtcreator_plugin_ubuntu/tests/test_qtcreator_plugin_functionality.py ./tests/autopilot/qtcreator_plugin_ubuntu/0000755000015600001650000000000012705421114021240 5ustar jenkinsjenkins./tests/autopilot/qtcreator_plugin_ubuntu/__init__.py0000644000015600001650000000050312705421114023347 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2014 Canonical # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. """qtcreator autopilot tests.""" ./tests/autopilot/qtcreator_plugin_ubuntu/tests/0000755000015600001650000000000012705421114022402 5ustar jenkinsjenkins./tests/autopilot/qtcreator_plugin_ubuntu/tests/__init__.py0000644000015600001650000002157412705421114024524 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2014 Canonical # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. """qtcreator autopilot tests.""" from autopilot.input import Mouse, Pointer from autopilot.testcase import AutopilotTestCase import shutil import tempfile import os import re import time import datetime class QtCreatorTestCase(AutopilotTestCase): def setUp(self): self.pointing_device = Pointer(Mouse.create()) # sdk_test_mode = os.environ['SDK_TEST_MODE'] sdk_test_mode = os.environ.get('SDK_TEST_MODE','auto') if sdk_test_mode != 'manual': self._set_temporary_home_directory() self._set_click_chroot_suffix() super(QtCreatorTestCase, self).setUp() self.launch_qt_creator() def launch_qt_creator(self): # self.patch_environment('HOME','/home/balogh') self.ide = self.launch_test_application('qtcreator') def _create_temporary_directory(self): self.temporary_directory = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, self.temporary_directory) def _set_temporary_home_directory(self): self._create_temporary_directory() sourcedir = os.environ['HOME']+"/.bazaar" destdir = self.temporary_directory + "/.bazaar" shutil.copytree(sourcedir, destdir, symlinks=False, ignore=None) sourcedir = os.environ['HOME']+"/.config/QtProject/qtcreator" destdir = self.temporary_directory + "/.config/QtProject/qtcreator" shutil.copytree(sourcedir, destdir, symlinks=False, ignore=None) sourcedir = os.environ['HOME']+"/.config/ubuntu-sdk" destdir = self.temporary_directory + "/.config/ubuntu-sdk" shutil.copytree(sourcedir, destdir, symlinks=False, ignore=None) sourcedir = os.environ['HOME']+"/.local/share/data/QtProject/qtcreator" destdir = self.temporary_directory + "/.local/share/data/QtProject/qtcreator" shutil.copytree(sourcedir, destdir, symlinks=False, ignore=None) self.patch_environment('HOME', self.temporary_directory) print os.environ['HOME'] os.chdir(os.environ['HOME']) if not os.path.exists('.config/ubuntu-sdk'): os.makedirs('.config/ubuntu-sdk') open('.config/ubuntu-sdk/firstrun', 'w') def _set_click_chroot_suffix(self): ts = time.time() st = datetime.datetime.fromtimestamp(ts).strftime('%Y%m%d%H%M%S') os.environ["CLICK_CHROOT_SUFFIX"] = "testing" + st def _get_main_window(self): return self.ide.wait_select_single( 'Core::Internal::MainWindow' ) def _get_welcome_tab_view(self): return self.ide.wait_select_single( 'QWidgetWindow', objectName='Core::Internal::MainWindowClassWindow' ) def _get_left_tabbar(self): main_window = self._get_main_window() return main_window.select_single( 'Core::Internal::FancyTabBar' ) def _get_number_of_tabs(self): tabs = self._get_left_tabbar().select_many( 'Core::Internal::FancyTab' ) return len(tabs) def _get_current_active_tab_name(self): return self._get_left_tabbar().selectedTabLabel def _get_new_project_button(self): return self._get_welcome_tab_view().wait_select_single( 'Button', text='New Project' ) def _get_new_project_dialog(self): return self._get_main_window().wait_select_single( 'Core::Internal::NewDialog' ) def _click_new_project_dialog_choose_button(self): button = self._get_new_project_dialog().select_single( 'QPushButton', text='&Choose...' ) self.pointing_device.click_object(button) def _get_new_project_wizard_dialog(self): return self.ide.wait_select_single( 'Ubuntu::Internal::UbuntuProjectApplicationWizardDialog' ) def _get_new_project_name_input_field(self): return self._get_new_project_wizard_dialog().select_single( 'Utils::ProjectNameValidatingLineEdit' ) def _get_new_project_location_field(self): return self._get_new_project_wizard_dialog().select_single( 'Utils::PathChooser' ) def _clear_input_field(self, input_field): self.pointing_device.click_object(input_field) self.pointing_device.click() self.pointing_device.click() self.keyboard.press_and_release('Backspace') def _type_new_project_name(self, project): input_field = self._get_new_project_name_input_field() self._clear_input_field(input_field) self.keyboard.type(project) def _type_new_project_location(self, location): location_field = self._get_new_project_location_field() self._clear_input_field(location_field) self.keyboard.type(location) def click_new_project_button(self): new_button = self._get_new_project_button() self.pointing_device.click_object(new_button) return self._get_new_project_dialog() def _get_wizard_finish_button(self): return self._get_new_project_wizard_dialog().select_single( 'QPushButton', text='&Finish') def _get_wizard_next_button(self): return self._get_new_project_wizard_dialog().select_single( 'QPushButton', text='&Next >') def _click_wizard_finish_button(self): finish_button = self._get_wizard_finish_button() self.pointing_device.click_object(finish_button) def _click_wizard_next_button(self): next_button = self._get_wizard_next_button() self.pointing_device.click_object(next_button) def _createAndOpenProject(self,typeString,name): """ Open the New File and Project dialog by triggering the right action """ action = self.ide.wait_select_single('QAction', text = '&New File or Project...') action.slots.trigger() new_project_dialog = self._get_main_window().wait_select_single('Core::Internal::NewDialog') """ Choose the App with Simple UI template in the Ubuntu category """ ubuntu_modelindex = new_project_dialog.wait_select_single('QModelIndex', text=' Ubuntu') self.pointing_device.click_object(ubuntu_modelindex) app_with_simple_ui_modelindex = new_project_dialog.wait_select_single('QModelIndex', text=typeString) self.pointing_device.click_object(app_with_simple_ui_modelindex) choose_pushbutton = new_project_dialog.wait_select_single('QPushButton', text='Choose...') self.pointing_device.click_object(choose_pushbutton) application_wizard_dialog = self._get_main_window().wait_select_single('Ubuntu::Internal::UbuntuProjectApplicationWizardDialog') """ Clear the default project name and enter the test name to the edit line and hit the Next->Next->Finish buttons """ projectname_lineedit = application_wizard_dialog.wait_select_single('Utils::ProjectNameValidatingLineEdit') projectname_lineedit.slots.clear() projectname_lineedit.slots.setText(name) next_pushbutton = application_wizard_dialog.wait_select_single('QPushButton', text = '&Next >') next_pushbutton.slots.click() next_pushbutton = application_wizard_dialog.wait_select_single('QPushButton', text = '&Next >') next_pushbutton.slots.click() for index, checkbox_kit in enumerate(application_wizard_dialog.select_many('QCheckBox')): if re.search('GCC ubuntu-sdk', checkbox_kit.text): checkbox_kit.slots.setChecked(True) checkbox_kit = application_wizard_dialog.wait_select_single('QCheckBox', text ='Desktop') checkbox_kit.slots.setChecked(False) next_pushbutton = application_wizard_dialog.wait_select_single('QPushButton', text = '&Next >') next_pushbutton.slots.click() next_pushbutton = application_wizard_dialog.wait_select_single('QPushButton', text = '&Finish') next_pushbutton.slots.click() def switch_to_tab_by_name(self, tab_name): current_tab = self._get_current_active_tab_name() if tab_name == current_tab: return tabbar = self._get_left_tabbar() tabbar_height = tabbar.height number_of_tabs = self._get_number_of_tabs() tbar_x, tbar_y, tbar_width, tbar_height = tabbar.globalRect tab_number = 1 while current_tab != tab_name and not tab_number > number_of_tabs: tab_center = ((tabbar_height / number_of_tabs) * tab_number) - \ ((tabbar_height / number_of_tabs) / 2) tx = tbar_x + tbar_width / 2 ty = tbar_y + tab_center self.pointing_device.move(tx, ty) self.pointing_device.click() current_tab = self._get_current_active_tab_name() tab_number += 1 ./tests/autopilot/qtcreator_plugin_ubuntu/tests/test_qtcreator_plugin_functionality.py0000644000015600001650000002325512705421114032354 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2014 Canonical # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. """ qtcreator-plugin-ubuntu autopilot tests.""" from qtcreator_plugin_ubuntu.tests import QtCreatorTestCase from autopilot.matchers import Eventually from testtools.matchers import Equals from autopilot.input import Keyboard from time import sleep import re #class CMakeApplicationTest(QtCreatorTestCase): class QtCreatorPluginTestPlan(QtCreatorTestCase): def setUp(self): super(QtCreatorPluginTestPlan, self).setUp() # tools -> options -> Build & Run -> Kits -> ".* for armhf (GCC ubuntu-sdk-14.10-utopic)" def test_existing_kits(self): """ Open the Options dialog by triggering the right action """ action = self.ide.wait_select_single('QAction', text = '&Options...') action.slots.trigger() setting_dialog = self._get_main_window().wait_select_single('Core::Internal::SettingsDialog') """ Se/lect the Ubuntu category and click on the Create Click Target button """ ubuntu_modelindex = setting_dialog.wait_select_single('QModelIndex', text='Build & Run') self.pointing_device.click_object(ubuntu_modelindex) for index, whatever in enumerate(setting_dialog.select_many('QItemSelectionModel')): print whatever.text for index, kit in enumerate(setting_dialog.select_many('QModelIndex')): if re.search('GCC ubuntu-sdk', kit.text): print kit.text # kit_modelindex = setting_dialog.wait_select_single('QModelIndex', text='UbuntuSDK for armhf (GCC ubuntu-sdk-14.10-utopic)') # kit_modelindex = setting_dialog.wait_select_single('QModelIndex', text=~'.*armhf.*') # self.pointing_device.click_object(kit_modelindex) # QWidget -> QTreeView -> sleep(2) def test_create_app_with_backend(self): self._createAndOpenProject('App with QML Extension Library',"appwithbackend") sleep(20) """ Try to build the project """ trojanHorse = self.ide.wait_select_single('Ubuntu::Internal::UbuntuTestControl') sigwatch = trojanHorse.watch_signal("buildFinished()") trojanHorse.slots.triggerCommand("ProjectExplorer.Build") self.assertThat(lambda: sigwatch.was_emitted, Eventually(Equals(True))) sleep(1) self.assertTrue(trojanHorse.lastBuildSuccess) def test_create_app_with_simple_ui(self): self._createAndOpenProject('App with Simple UI',"appwithsimpleui") """ Change to the Publish mode and click on the Create package button""" fancy_tab_widget = self._get_main_window().wait_select_single('Core::Internal::FancyTabWidget') fancy_tab_widget.slots.setCurrentIndex(5) packaging_widget = fancy_tab_widget.wait_select_single('UbuntuPackagingWidget', objectName = 'UbuntuPackagingWidget') click_package_pushbutton = packaging_widget.wait_select_single('QPushButton', objectName = 'pushButtonClickPackage') parser = packaging_widget.wait_select_single('Ubuntu::Internal::ClickRunChecksParser') sigwatch = parser.watch_signal("finished()") click_package_pushbutton.slots.click() self.assertThat(lambda: sigwatch.was_emitted, Eventually(Equals(True))) """ Check the error type if there was any error during the package creation """ validation_groupbox = packaging_widget.wait_select_single('QGroupBox', objectName = 'groupBoxValidate') errorinfo_groupbox = validation_groupbox.wait_select_single('QGroupBox', objectName = 'groupBoxErrorInfo') errortype_label = errorinfo_groupbox.wait_select_single('QLabel', objectName = 'labelErrorType') self.assertThat(errortype_label.text, Equals("")) def test_x86_emulator_creation(self): """ Change to the Devices mode and click on the add new emulator button """ kbd = Keyboard.create("X11") kbd.press_and_release('Ctrl+9') devices_quickview = self.ide.wait_select_single('QQuickView', source='file:///usr/share/qtcreator/ubuntu/qml/devicespage.qml') add_emulator_button = devices_quickview.select_single('Button', text='Add Emulator') add_emulator_button.visible.wait_for(True) """ The simple click_object() moves the pointer to the center of the button, but in some environment the responsive area of the button is smaller """ self.pointing_device.move(add_emulator_button.globalRect.x + add_emulator_button.width - 1, add_emulator_button.globalRect.y + add_emulator_button.height - 1) self.pointing_device.click() config_emulator_dialog = devices_quickview.wait_select_single('Dialog', title='Create emulator') emulatorname_textfield = config_emulator_dialog.wait_select_single('TextField', placeholderText = 'Emulator name') self.pointing_device.click_object(emulatorname_textfield) kbd = Keyboard.create("X11") kbd.type("TestX86Emulator", delay=0.1) create_button = config_emulator_dialog.wait_select_single('Button', text = 'Create') self.pointing_device.click_object(create_button) """ Wait the emulator creation to finish """ devices_ubuntulistview = devices_quickview.wait_select_single('UbuntuListView', objectName = 'devicesList') while True: if(devices_ubuntulistview.visible): break; sleep(1) emulator_listitem = devices_ubuntulistview.wait_select_single('Standard', text = 'TestX86Emulator') def test_x86_emulator_start(self): """ Change to the Devices mode, select the TestX86Emulator and deploy it """ kbd = Keyboard.create("X11") kbd.press_and_release('Ctrl+9') devices_quickview = self.ide.wait_select_single('QQuickView', source='file:///usr/share/qtcreator/ubuntu/qml/devicespage.qml') devices_ubuntulistview = devices_quickview.wait_select_single('UbuntuListView', objectName = 'devicesList') while True: if(devices_ubuntulistview.visible): break; sleep(1) emulator_listitem = devices_ubuntulistview.wait_select_single('Standard', text = 'TestX86Emulator') self.pointing_device.click_object(emulator_listitem) # TODO: continue with capturing the started emulator en evaluate the status def test_x86_fw1410_click_chroot_creation(self): """ Open the Options dialog by triggering the right action """ action = self.ide.wait_select_single('QAction', text = '&Options...') action.slots.trigger() setting_dialog = self._get_main_window().wait_select_single('Core::Internal::SettingsDialog') """ Select the Ubuntu category and click on the Create Click Target button """ ubuntu_modelindex = setting_dialog.wait_select_single('QModelIndex', text='Ubuntu') self.pointing_device.click_object(ubuntu_modelindex) new_target_button = setting_dialog.wait_select_single('QPushButton', text='Create Click Target' ) self.pointing_device.click_object(new_target_button) """ Select the i386 architecture and 14.10 framework in the dialog and push the OK button """ new_chroot_dialog = self.ide.wait_select_single('Ubuntu::Internal::UbuntuCreateNewChrootDialog') arch_combobox = new_chroot_dialog.wait_select_single('QComboBox', objectName = 'comboBoxArch') self.pointing_device.click_object(arch_combobox) i386_modelindex = new_chroot_dialog.wait_select_single('QModelIndex', text='i386') self.pointing_device.click_object(i386_modelindex) series_combobox = new_chroot_dialog.wait_select_single('QComboBox', objectName = 'comboBoxSeries') self.pointing_device.click_object(series_combobox) fw1410_modelindex = new_chroot_dialog.wait_select_single('QModelIndex', text='Framework-14.10') self.pointing_device.click_object(fw1410_modelindex) button_box = new_chroot_dialog.wait_select_single('QDialogButtonBox', objectName = 'buttonBox') ok_pushbutton = button_box.wait_select_single('QPushButton', text='&OK') self.pointing_device.click_object(ok_pushbutton) """ Open the Click run dialog and wait for it finishes the job """ click_dialog = self.ide.wait_select_single('Ubuntu::Internal::UbuntuClickDialog') dialog_button_box = click_dialog.wait_select_single('QDialogButtonBox', objectName = 'buttonBox') close_button = dialog_button_box.wait_select_single('QPushButton', text = '&Close') while True: if(close_button.enabled): break; sleep(1) output_plaintextedit = click_dialog.wait_select_single('QPlainTextEdit', objectName = 'output') self.assertFalse('Click exited with errors, please check the output' in output_plaintextedit.plainText) self.assertTrue('Click exited with no errors' in output_plaintextedit.plainText) self.pointing_device.click_object(close_button) def test_plugins(self): """ Open the About Plugins dialog """ action = self.ide.wait_select_single('QAction', text='About &Plugins...') action.slots.trigger() """ Check for each Ubuntu specific plugin in the plugin tree """ plugin_dialog = self._get_main_window().wait_select_single('Core::Internal::PluginDialog') ubuntu_treewidgetitem = plugin_dialog.wait_select_single('QTreeWidgetItem', text='Ubuntu') cmake_treewidgetitem = plugin_dialog.wait_select_single('QTreeWidgetItem', text='CMakeProjectManager') remotelinux_treewidgetitem = plugin_dialog.wait_select_single('QTreeWidgetItem', text='RemoteLinux') golang_treewidgetitem = plugin_dialog.wait_select_single('QTreeWidgetItem', text='GoLang') ./tests/autopilot/ubuntusdk-autopilot.creator0000644000015600001650000000001212705421114021670 0ustar jenkinsjenkins[General] ./tests/autopilot/ubuntusdk-autopilot.config0000644000015600001650000000003712705421114021505 0ustar jenkinsjenkins// ADD PREDEFINED MACROS HERE! ./tests/manifest/0000755000015600001650000000000012705421114014042 5ustar jenkinsjenkins./tests/manifest/tst_manifest.cpp0000644000015600001650000001410012705421114017242 0ustar jenkinsjenkins#include #include #include "tst_manifest.h" #include using namespace Ubuntu::Internal; const char MANIFEST_TEMPLATE[] = ":/ubuntu/test/manifest/manifest.json.template"; const char APPARMOR_TEMPLATE[] = ":/ubuntu/test/manifest/myapp.json.template"; const char MANIFEST_FILE[] = "/tmp/manifest.json"; UbuntuManifestTest::UbuntuManifestTest() { } void UbuntuManifestTest::testWriteStringValue(const QString &value, StrWriteFunc write, StrReadFunc read) { UbuntuClickManifest mani; QVERIFY( mani.load(MANIFEST_TEMPLATE) ); mani.setFileName(MANIFEST_FILE); mani.save(); (mani.*write)(value); QCOMPARE((mani.*read)(),value); QFile::remove(MANIFEST_FILE); } void UbuntuManifestTest::testSave(const QString &templateFile) { UbuntuClickManifest mani; QVERIFY( mani.load(templateFile) ); mani.setFileName(MANIFEST_FILE); mani.save(); QVERIFY (QFile::exists(MANIFEST_FILE)); QFile file(MANIFEST_FILE); QVERIFY(file.open(QIODevice::ReadOnly)); QVERIFY(mani.raw() == QString::fromUtf8(file.readAll())); file.close(); file.remove(); } void UbuntuManifestTest::testSave() { testSave(MANIFEST_TEMPLATE); } void UbuntuManifestTest::testWriteAppArmorName() { UbuntuClickManifest mani; QVERIFY( mani.load(MANIFEST_TEMPLATE) ); mani.setFileName(MANIFEST_FILE); mani.save(); const char AAFILE[] = "some/path/to/myApp.json"; QVERIFY(mani.setAppArmorFileName("myapp",QString(AAFILE))); QCOMPARE(mani.appArmorFileName("myapp"),QString(AAFILE)); QFile::remove(MANIFEST_FILE); } void UbuntuManifestTest::testWriteName() { testWriteStringValue( QString("name"), &UbuntuClickManifest::setName, &UbuntuClickManifest::name); } void UbuntuManifestTest::testWriteMaintainer() { testWriteStringValue( QString("John Doe"), &UbuntuClickManifest::setMaintainer, &UbuntuClickManifest::maintainer); } void UbuntuManifestTest::testWriteTitle() { testWriteStringValue( QString("Completely Awesome Title äüö#$%"), &UbuntuClickManifest::setTitle, &UbuntuClickManifest::title); } void UbuntuManifestTest::testWriteVersion() { testWriteStringValue( QString("1.2.0-test1~ubuntu2"), &UbuntuClickManifest::setVersion, &UbuntuClickManifest::version); } void UbuntuManifestTest::testWriteDescription() { testWriteStringValue( QString("This is a application description äöüöl?=)(/&%$§\"!\"§$%&/()=>><<-_.,\\*'+#~’^°"), &UbuntuClickManifest::setDescription, &UbuntuClickManifest::description); } void UbuntuManifestTest::testWriteFrameworkName() { UbuntuClickManifest mani; QVERIFY( mani.load(MANIFEST_TEMPLATE) ); mani.setFileName(MANIFEST_FILE); mani.save(); QString value = "ubuntu-sdk-14.10"; mani.setFrameworkName(value); QCOMPARE(mani.frameworkName(),value); QFile::remove(MANIFEST_FILE); } void UbuntuManifestTest::testReadHooks() { UbuntuClickManifest mani; QVERIFY( mani.load(MANIFEST_TEMPLATE) ); mani.setFileName(MANIFEST_FILE); mani.save(); /* "myapp": { "apparmor": "myapp.json", "desktop": "myapp.desktop" }, "myapp2": { "apparmor": "myapp2.json", "desktop": "myapp2.desktop" }, "myscope": { "apparmor": "myscope.json", "scope": "myscope.ini" } */ QList hooks = mani.hooks(); QCOMPARE(hooks.size(),3); QCOMPARE(hooks[0].appId,QString("myapp")); QCOMPARE(hooks[0].appArmorFile,QString("myapp.json")); QCOMPARE(hooks[0].desktopFile ,QString("myapp.desktop")); QCOMPARE(hooks[0].scope ,QString()); QCOMPARE(hooks[1].appId,QString("myapp2")); QCOMPARE(hooks[1].appArmorFile,QString("myapp2.json")); QCOMPARE(hooks[1].desktopFile ,QString("myapp2.desktop")); QCOMPARE(hooks[1].scope ,QString()); QCOMPARE(hooks[2].appId,QString("myscope")); QCOMPARE(hooks[2].appArmorFile,QString("myscope.json")); QCOMPARE(hooks[2].desktopFile ,QString()); QCOMPARE(hooks[2].scope ,QString("myscope.ini")); QFile::remove(MANIFEST_FILE); } void UbuntuManifestTest::testAppArmorFile() { testSave(APPARMOR_TEMPLATE); } void UbuntuManifestTest::testWritePolicyVersion () { #ifdef Q_PROCESSOR_POWER return; #endif UbuntuClickManifest mani; QVERIFY( mani.load(APPARMOR_TEMPLATE) ); mani.setFileName(MANIFEST_FILE); mani.save(); QString value = "1.2"; mani.setPolicyVersion(value); QCOMPARE(mani.policyVersion(),value); value = "0.1"; mani.setPolicyVersion(value); QCOMPARE(mani.policyVersion(),value); value = "1.0"; mani.setPolicyVersion(value); QCOMPARE(mani.policyVersion(),value); QFile::remove(MANIFEST_FILE); } void UbuntuManifestTest::testWritePolicyGroups() { UbuntuClickManifest mani; QVERIFY( mani.load(APPARMOR_TEMPLATE) ); mani.setFileName(MANIFEST_FILE); mani.save(); /* "policy_groups": [ "networking", "someOtherGroup", "lastGroup" ] */ QStringList polGroups = mani.policyGroups(); QCOMPARE(polGroups.size(),3); QCOMPARE(polGroups[0],QString("networking")); QCOMPARE(polGroups[1],QString("someOtherGroup")); QCOMPARE(polGroups[2],QString("lastGroup")); polGroups.append("addedGroup1"); polGroups.append("addedGroup2"); mani.setPolicyGroups(polGroups); polGroups = mani.policyGroups(); QCOMPARE(polGroups.size(),5); QCOMPARE(polGroups[0],QString("networking")); QCOMPARE(polGroups[1],QString("someOtherGroup")); QCOMPARE(polGroups[2],QString("lastGroup")); QCOMPARE(polGroups[3],QString("addedGroup1")); QCOMPARE(polGroups[4],QString("addedGroup2")); QFile::remove(MANIFEST_FILE); } /*! * \brief printToOutputPane * Dummy function */ void printToOutputPane(const QString &msg) { Q_UNUSED(msg); } QTEST_GUILESS_MAIN(UbuntuManifestTest) ./tests/manifest/myapp.json.template0000644000015600001650000000020012705421114017665 0ustar jenkinsjenkins{ "policy_groups": [ "networking", "someOtherGroup", "lastGroup" ], "policy_version": 1.1 } ./tests/manifest/manifest.pro0000644000015600001650000000116712705421114016377 0ustar jenkinsjenkinsQT = core gui qml CONFIG += c++11 QTC_LIB_DEPENDS += utils QTC_PLUGIN_DEPENDS += coreplugin cmakeprojectmanager include(../shared.pri) PLUGIN_SRC_ROOT = $${PWD}/../../src/ubuntu INCLUDEPATH += $${PLUGIN_SRC_ROOT} DEFINES += SRCDIR=\\\"$$PWD/\\\" DEFINES += IN_TEST_PROJECT SOURCES += \ tst_manifest.cpp \ $${PLUGIN_SRC_ROOT}/ubuntuclickmanifest.cpp \ $${PLUGIN_SRC_ROOT}/ubuntuclicktool.cpp HEADERS += \ tst_manifest.h \ $${PLUGIN_SRC_ROOT}/ubuntuclickmanifest.h \ $${PLUGIN_SRC_ROOT}/ubuntuclicktool.h OTHER_FILES += RESOURCES += \ $${PLUGIN_SRC_ROOT}/resources.qrc \ resources_test.qrc ./tests/manifest/resources_test.qrc0000644000015600001650000000024212705421114017620 0ustar jenkinsjenkins manifest.json.template myapp.json.template ./tests/manifest/manifest.json.template0000644000015600001650000000106012705421114020352 0ustar jenkinsjenkins{ "name": "myapp.username", "description": "description of myapp", "framework": "myFramework", "architecture": "all", "title": "myapp", "hooks": { "myapp": { "apparmor": "myapp.json", "desktop": "myapp.desktop" }, "myapp2": { "apparmor": "myapp2.json", "desktop": "myapp2.desktop" }, "myscope": { "apparmor": "myscope.json", "scope": "myscope.ini" } }, "version": "0.1", "maintainer": "maintainerName" } ./tests/manifest/tst_manifest.h0000644000015600001650000000162512705421114016717 0ustar jenkinsjenkins#ifndef TST_MANIFEST_H #define TST_MANIFEST_H #include #include typedef void (Ubuntu::Internal::UbuntuClickManifest::*StrWriteFunc) (QString arg); typedef QString (Ubuntu::Internal::UbuntuClickManifest::*StrReadFunc) ( ); class UbuntuManifestTest : public QObject { Q_OBJECT public: UbuntuManifestTest(); private: void testWriteStringValue (const QString &value, StrWriteFunc write, StrReadFunc read); void testSave (const QString &templateFile); private slots: void testSave (); void testWriteAppArmorName (); void testWriteName (); void testAppArmorFile (); void testWriteMaintainer(); void testWriteTitle(); void testWriteVersion(); void testWriteDescription(); void testWriteFrameworkName(); void testReadHooks(); void testWritePolicyVersion(); void testWritePolicyGroups(); }; #endif // TST_MANIFEST_H ./tests/adbportlist/0000755000015600001650000000000012705421114014563 5ustar jenkinsjenkins./tests/adbportlist/resources.qrc0000644000015600001650000000020712705421114017303 0ustar jenkinsjenkins simple_list complex_list ./tests/adbportlist/tst_localportmanager.cpp0000644000015600001650000000543412705421114021521 0ustar jenkinsjenkins#include #include #include "tst_localportmanager.h" tst_LocalPortManager::tst_LocalPortManager() { } void tst_LocalPortManager::testEmptyOutput() { QByteArray emptyData; QBuffer emptyInput(&emptyData); QVERIFY(emptyInput.open(QIODevice::ReadOnly)); Ubuntu::Internal::UbuntuLocalPortsManager pM; pM.setPortsRange(10000,11000); int requiredCount = 20; Utils::PortList ports = pM.getFreeRange("08866fd9e24cf55a",requiredCount,&emptyInput); QCOMPARE(ports.count(),requiredCount); int count = 0; while(ports.hasMore()) { int port = ports.getNext(); QCOMPARE(port,10000+count); count ++; } QCOMPARE(count,requiredCount); } void tst_LocalPortManager::testSimpleList() { QFile in(":/portmanager/simple_list"); QVERIFY(in.open(QIODevice::ReadOnly)); int firstPort = 10000; int lastPort = 11000; Ubuntu::Internal::UbuntuLocalPortsManager pM; pM.setPortsRange(firstPort,lastPort); int requiredCount = 20; Utils::PortList ports = pM.getFreeRange("08866fd9e24cf55a",requiredCount,&in); QCOMPARE(ports.count(),requiredCount); int max = firstPort + requiredCount; for (int port = firstPort; port < max; ++port) { QVERIFY2(ports.contains(port),qPrintable(QStringLiteral("List should contain port: %1").arg(port))); } } void tst_LocalPortManager::testComplexList() { QFile in(":/portmanager/complex_list"); QVERIFY(in.open(QIODevice::ReadOnly)); int firstPort = 10000; int lastPort = 11000; Ubuntu::Internal::UbuntuLocalPortsManager pM; pM.setPortsRange(firstPort,lastPort); int requiredCount = 20; Utils::PortList ports = pM.getFreeRange("09977feaf35d066b",requiredCount,&in); in.seek(0); //the ports should be 0 - 13 and 19 - 24 for (int i = 0; i <= 13; i++) QVERIFY2(ports.contains(firstPort+i),qPrintable(QStringLiteral("List should contain port: %1").arg(firstPort+i))); for(int i = 19; i <= 24; i++) QVERIFY2(ports.contains(firstPort+i),qPrintable(QStringLiteral("List should contain port: %1").arg(firstPort+i))); while(ports.hasMore()) QVERIFY2(!ports.contains(ports.getNext()),"Ports can not be assigned twice"); ports = pM.getFreeRange("08866fd9e24cf55a",requiredCount,&in); //the ports should be 8 - 18 and 21 - 29 for (int i = 8; i <= 18; i++) QVERIFY2(ports.contains(firstPort+i),qPrintable(QStringLiteral("List should contain port: %1").arg(firstPort+i))); for (int i = 21; i <= 29; i++) QVERIFY2(ports.contains(firstPort+i),qPrintable(QStringLiteral("List should contain port: %1").arg(firstPort+i))); while(ports.hasMore()) QVERIFY2(!ports.contains(ports.getNext()),"Ports can not be assigned twice"); } QTEST_GUILESS_MAIN(tst_LocalPortManager) ./tests/adbportlist/tst_localportmanager.h0000644000015600001650000000053412705421114021162 0ustar jenkinsjenkins#ifndef TST_LOCALPORTMANAGER_H #define TST_LOCALPORTMANAGER_H #include #include "localportsmanager.h" class tst_LocalPortManager : public QObject { Q_OBJECT public: tst_LocalPortManager(); private slots: void testEmptyOutput(); void testSimpleList (); void testComplexList(); }; #endif // TST_LOCALPORTMANAGER_H ./tests/adbportlist/adbportlist.pro0000644000015600001650000000062012705421114017632 0ustar jenkinsjenkinsQT = core gui CONFIG += c++11 QTC_LIB_DEPENDS += utils include(../shared.pri) PLUGIN_SRC_ROOT = $${PWD}/../../src/ubuntu INCLUDEPATH += $${PLUGIN_SRC_ROOT} HEADERS += \ $${PLUGIN_SRC_ROOT}/localportsmanager.h\ tst_localportmanager.h SOURCES += \ $${PLUGIN_SRC_ROOT}/localportsmanager.cpp \ tst_localportmanager.cpp OTHER_FILES += simple_list RESOURCES += \ resources.qrc ./tests/adbportlist/complex_list0000644000015600001650000000104512705421114017210 0ustar jenkinsjenkins09977feaf35d066b tcp:10000 tcp:22 09977feaf35d066b tcp:10001 tcp:10001 09977feaf35d066b tcp:10002 tcp:10002 09977feaf35d066b tcp:10003 tcp:10003 09977feaf35d066b tcp:10004 tcp:10004 09977feaf35d066b tcp:10005 tcp:10005 09977feaf35d066b tcp:10006 tcp:10006 09977feaf35d066b tcp:10007 tcp:10007 08866fd9e24cf55a tcp:10014 tcp:22 08866fd9e24cf55a tcp:10015 tcp:10001 08866fd9e24cf55a tcp:10016 tcp:10002 08866fd9e24cf55a tcp:10017 tcp:10003 08866fd9e24cf55a tcp:10018 tcp:10004 09977feaf35d066b tcp:10019 tcp:10006 09977feaf35d066b tcp:10020 tcp:10007 ./tests/adbportlist/simple_list0000644000015600001650000000140612705421114017033 0ustar jenkinsjenkins08866fd9e24cf55a tcp:10000 tcp:22 08866fd9e24cf55a tcp:10001 tcp:10001 08866fd9e24cf55a tcp:10002 tcp:10002 08866fd9e24cf55a tcp:10003 tcp:10003 08866fd9e24cf55a tcp:10004 tcp:10004 08866fd9e24cf55a tcp:10005 tcp:10005 08866fd9e24cf55a tcp:10006 tcp:10006 08866fd9e24cf55a tcp:10007 tcp:10007 08866fd9e24cf55a tcp:10008 tcp:10008 08866fd9e24cf55a tcp:10009 tcp:10009 08866fd9e24cf55a tcp:10010 tcp:10010 08866fd9e24cf55a tcp:10011 tcp:10011 08866fd9e24cf55a tcp:10012 tcp:10012 08866fd9e24cf55a tcp:10013 tcp:10013 08866fd9e24cf55a tcp:10014 tcp:10014 08866fd9e24cf55a tcp:10015 tcp:10015 08866fd9e24cf55a tcp:10016 tcp:10016 08866fd9e24cf55a tcp:10017 tcp:10017 08866fd9e24cf55a tcp:10018 tcp:10018 08866fd9e24cf55a tcp:10019 tcp:10019 08866fd9e24cf55a tcp:10020 tcp:10020 ./src/0000755000015600001650000000000012705421114011661 5ustar jenkinsjenkins./src/ubuntu/0000755000015600001650000000000012705421120013200 5ustar jenkinsjenkins./src/ubuntu/ubuntudeploystepfactory.cpp0000644000015600001650000001162512705421114020737 0ustar jenkinsjenkins#include "ubuntudeploystepfactory.h" #include "ubuntuprojecthelper.h" #include "ubuntuconstants.h" #include "ubuntudirectuploadstep.h" #include "ubuntupackagestep.h" #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { QList UbuntuDeployStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const { QList types; if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) return types; Core::Id targetDevice = ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(parent->target()->kit()); if(targetDevice != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE && !targetDevice.toString().startsWith(QLatin1String(Ubuntu::Constants::UBUNTU_DEVICE_TYPE_ID))) return types; bool isRemote = targetDevice.toString().startsWith(QLatin1String(Ubuntu::Constants::UBUNTU_DEVICE_TYPE_ID)); bool isCMake = parent->target()->project()->id() == CMakeProjectManager::Constants::CMAKEPROJECT_ID; bool isHTML = parent->target()->project()->id() == Ubuntu::Constants::UBUNTUPROJECT_ID; bool isQML = parent->target()->project()->id() == "QmlProjectManager.QmlProject"; bool isQMake = parent->target()->project()->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID; if (isRemote) { //IF we have a remote device we just support a ubuntu toolchain ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(parent->target()->kit()); if(tc && tc->type() != QLatin1String(Ubuntu::Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) return types; } if(isRemote && ( isHTML || isQML || isCMake || isQMake ) ) types << Constants::UBUNTU_DEPLOY_UPLOADSTEP_ID << Constants::UBUNTU_CLICK_PACKAGESTEP_ID; return types; } QString UbuntuDeployStepFactory::displayNameForId(const Core::Id id) const { if (id == Constants::UBUNTU_DEPLOY_UPLOADSTEP_ID) return UbuntuDirectUploadStep::displayName(); else if (id == Constants::UBUNTU_CLICK_PACKAGESTEP_ID) return tr("UbuntuSDK create click package", "Display name for UbuntuPackageStep id."); return QString(); } bool UbuntuDeployStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const { return availableCreationIds(parent).contains(id); } ProjectExplorer::BuildStep *UbuntuDeployStepFactory::create(ProjectExplorer::BuildStepList *parent, const Core::Id id) { if (!canCreate(parent, id)) return 0; if(id == Constants::UBUNTU_DEPLOY_UPLOADSTEP_ID) return new UbuntuDirectUploadStep(parent); else if (id == Constants::UBUNTU_CLICK_PACKAGESTEP_ID) { UbuntuPackageStep *step = new UbuntuPackageStep(parent); return step; } return 0; } bool UbuntuDeployStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const { Core::Id toRestore = ProjectExplorer::idFromMap(map); //backwards compatibility to older projects if( toRestore == Constants::UBUNTU_DEPLOY_MAKESTEP_ID ) return canCreate(parent,Core::Id(Constants::UBUNTU_CLICK_PACKAGESTEP_ID)); return canCreate(parent,toRestore); } ProjectExplorer::BuildStep *UbuntuDeployStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) { Core::Id id = ProjectExplorer::idFromMap(map); if(!canCreate(parent,id)) return 0; //backwards compatibility to older projects if( id == Constants::UBUNTU_DEPLOY_MAKESTEP_ID ) { UbuntuPackageStep *step = new UbuntuPackageStep(parent); return step; } ProjectExplorer::BuildStep* step = create(parent,id); if (!step->fromMap(map)) { delete step; return 0; } return step; } bool UbuntuDeployStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const { return canCreate(parent,product->id()); } ProjectExplorer::BuildStep *UbuntuDeployStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) { if (!canClone(parent, product)) return 0; const Core::Id id = product->id(); if(id == Constants::UBUNTU_DEPLOY_UPLOADSTEP_ID) return new UbuntuDirectUploadStep(parent, static_cast(product)); else if(id == Core::Id(Constants::UBUNTU_CLICK_PACKAGESTEP_ID)) return new UbuntuPackageStep(parent, static_cast(product)); return 0; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuabstractguieditor.cpp0000644000015600001650000000667612705421114020710 0ustar jenkinsjenkins/* * Copyright 2014 Digia Plc and/or its subsidiary(-ies). * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuabstractguieditor.h" #include "ubuntuabstractguieditorwidget.h" #include "ubuntuconstants.h" #include #include #include #include #include namespace Ubuntu { namespace Internal { UbuntuAbstractGuiEditor::UbuntuAbstractGuiEditor(const Core::Context &context) : Core::IEditor(), m_toolBar(0), m_actionGroup(0) { setContext(context); } QWidget *UbuntuAbstractGuiEditor::toolBar() { return m_toolBar; } UbuntuAbstractGuiEditorWidget *UbuntuAbstractGuiEditor::editorWidget() const { return static_cast(m_widget.data()); } Core::IDocument *UbuntuAbstractGuiEditor::document() { return editorWidget()->textEditorWidget()->textDocument(); } TextEditor::TextEditorWidget *UbuntuAbstractGuiEditor::textEditor() const { return editorWidget()->textEditorWidget(); } int UbuntuAbstractGuiEditor::currentLine() const { return textEditor()->textCursor().blockNumber() + 1; } int UbuntuAbstractGuiEditor::currentColumn() const { QTextCursor cursor = textEditor()->textCursor(); return cursor.position() - cursor.block().position() + 1; } void UbuntuAbstractGuiEditor::syncCurrentAction() { foreach (QAction *action, m_actionGroup->actions()) { if (action->data().toInt() == editorWidget()->activePage()) { action->setChecked(true); break; } } } void UbuntuAbstractGuiEditor::createUi() { UbuntuAbstractGuiEditorWidget *loc_editorWidget = createGuiEditor(); m_widget = loc_editorWidget; connect(loc_editorWidget, &UbuntuAbstractGuiEditorWidget::editorViewChanged, this, &UbuntuAbstractGuiEditor::syncCurrentAction); m_toolBar = new QToolBar(m_widget.data()); m_actionGroup = new QActionGroup(m_widget.data()); connect(m_actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(changeEditorPage(QAction*))); QAction *generalAction = m_toolBar->addAction(tr("General")); generalAction->setData(UbuntuAbstractGuiEditorWidget::General); generalAction->setCheckable(true); m_actionGroup->addAction(generalAction); QAction *sourceAction = m_toolBar->addAction(tr("JSON Source")); sourceAction->setData(UbuntuAbstractGuiEditorWidget::Source); sourceAction->setCheckable(true); m_actionGroup->addAction(sourceAction); generalAction->setChecked(true); setWidget(editorWidget()); } void UbuntuAbstractGuiEditor::changeEditorPage(QAction *action) { if(!editorWidget()->setActivePage(static_cast(action->data().toInt()))){ syncCurrentAction(); } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuclickdialog.ui0000644000015600001650000000477012705421114017262 0ustar jenkinsjenkins Ubuntu::Internal::UbuntuClickDialog 0 0 940 563 640 480 Run Click 75 true Run Click Exit Status Qt::Horizontal QDialogButtonBox::Close 0 1 0 15 buttonBox accepted() Ubuntu::Internal::UbuntuClickDialog accept() 88 553 157 274 buttonBox rejected() Ubuntu::Internal::UbuntuClickDialog reject() 88 553 286 274 ./src/ubuntu/ubuntusecuritypolicypickerdialog.cpp0000644000015600001650000000722412705421114022624 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntusecuritypolicypickerdialog.h" #include "ui_ubuntusecuritypolicypickerdialog.h" UbuntuSecurityPolicyPickerDialog::UbuntuSecurityPolicyPickerDialog(const QString &policyVersion, QWidget *parent) : QDialog(parent), ui(new Ui::UbuntuSecurityPolicyPickerDialog) { ui->setupUi(this); this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint | Qt::Popup); m_model.setPolicyVersion(policyVersion); m_model.scanPolicyGroups(); connect(&m_model,SIGNAL(scanComplete(bool)),this,SLOT(onScanComplete(bool))); ui->listViewPolicyGroups->setModel(&m_model); ui->stackedWidget->setCurrentIndex(0); QItemSelectionModel *selModel = ui->listViewPolicyGroups->selectionModel(); connect(selModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(onPolicyClicked(QModelIndex))); connect(&m_info,SIGNAL(infoReady(bool)),this,SLOT(onInfoChanged(bool))); } UbuntuSecurityPolicyPickerDialog::~UbuntuSecurityPolicyPickerDialog() { delete ui; } void UbuntuSecurityPolicyPickerDialog::onScanComplete(bool ok) { if (ok) { ui->stackedWidget->setCurrentIndex(1); ui->listViewPolicyGroups->selectionModel()->setCurrentIndex(m_model.index(0,0), QItemSelectionModel::SelectCurrent); ui->listViewPolicyGroups->setFocus(); } else { ui->stackedWidget->setCurrentIndex(2); } } QStringList UbuntuSecurityPolicyPickerDialog::selectedPolicyGroups() { QStringList retval; if (ui->stackedWidget->currentIndex()==2) { retval.append(ui->lineEditPolicyGroup->text()); } else { QModelIndexList selected = ui->listViewPolicyGroups->selectionModel()->selectedIndexes(); foreach (QModelIndex idx, selected) { retval.append(m_model.data(idx,Qt::DisplayRole).toString()); } } return retval; } void UbuntuSecurityPolicyPickerDialog::on_pushButtonCancel_clicked() { this->reject(); } void UbuntuSecurityPolicyPickerDialog::on_pushButtonAdd_clicked() { this->accept(); } void UbuntuSecurityPolicyPickerDialog::onPolicyClicked(QModelIndex idx) { m_info.getInfo(m_model.data(idx,Qt::DisplayRole).toString(),m_model.policyVersion()); } void UbuntuSecurityPolicyPickerDialog::onInfoChanged(bool ok) { if (ok) { QString policyGroupInfo = m_info.info(); policyGroupInfo = policyGroupInfo.replace(QRegExp(QLatin1String("#([^\n]*)")), QLatin1String("#\\1")); policyGroupInfo = policyGroupInfo.replace(QRegExp(QLatin1String("(# Usage: reserved)"),Qt::CaseInsensitive), QLatin1String("\\1")); policyGroupInfo = policyGroupInfo.replace(QRegExp(QLatin1String("\n")), QLatin1String("
")); ui->textBrowserEditInfo->setHtml(policyGroupInfo); ui->textBrowserEditInfo->show(); } else { ui->textBrowserEditInfo->hide(); } } ./src/ubuntu/ubuntudevicenotifier.cpp0000644000015600001650000001021312705421114020146 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntudevicenotifier.h" enum { debug = 0 }; IUbuntuDeviceNotifier::IUbuntuDeviceNotifier(QObject *parent) : QObject(parent) { } UbuntuDeviceNotifier::UbuntuDeviceNotifier(QObject *parent) : IUbuntuDeviceNotifier(parent) { m_dev = udev_new(); m_udevMonitor = NULL; m_udevMonitor = udev_monitor_new_from_netlink(m_dev,"udev"); if (!m_udevMonitor) { qWarning() << QLatin1String("could not monitor devices"); return; } udev_monitor_filter_add_match_subsystem_devtype(m_udevMonitor,"usb",0); udev_monitor_enable_receiving(m_udevMonitor); m_udevMonitorFileDescriptor = udev_monitor_get_fd(m_udevMonitor); m_udevSocketNotifier = new QSocketNotifier(m_udevMonitorFileDescriptor,QSocketNotifier::Read, this); connect(m_udevSocketNotifier,SIGNAL(activated(int)),this,SLOT(on_udev_event())); } UbuntuDeviceNotifier::~UbuntuDeviceNotifier() { m_udevSocketNotifier->disconnect(SIGNAL(activated(int))); m_udevSocketNotifier->deleteLater(); udev_monitor_unref(m_udevMonitor); udev_unref(m_dev); } void UbuntuDeviceNotifier::startMonitoring(const QString &serialNumber) { m_serialNumber = serialNumber; // check if the device is connected or disconnected at the moment struct udev_list_entry *dev_list_entry; struct udev_device *dev; struct udev_enumerate *enumerate = udev_enumerate_new(m_dev); udev_enumerate_add_match_subsystem(enumerate, "usb"); udev_enumerate_add_match_sysattr(enumerate,"serial",serialNumber.toLatin1().constData()); udev_enumerate_scan_devices(enumerate); struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { const char *path; path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(m_dev, path); m_devNode = QLatin1String(udev_device_get_devnode(dev)); udev_device_unref(dev); } udev_enumerate_unref(enumerate); } void UbuntuDeviceNotifier::stopMonitoring() { m_serialNumber = QLatin1String(""); } /*! * \brief UbuntuDeviceNotifier::isConnected * Returns true if the device that is currently monitored * is connected. * \note you need to call startMonitoring() first */ bool UbuntuDeviceNotifier::isConnected() const { return !m_devNode.isEmpty(); } void UbuntuDeviceNotifier::on_udev_event() { if (!m_udevMonitor) { if(debug) qDebug() << QLatin1String("no monitor"); return; } struct udev_device *dev; dev = udev_monitor_receive_device(m_udevMonitor); if (!dev) { if(debug) qDebug() << QLatin1String("no device"); udev_device_unref(dev); return; } QString serial = QLatin1String(udev_device_get_sysattr_value(dev,"serial")); QString action = QLatin1String(udev_device_get_action(dev)); QString devNode = QLatin1String(udev_device_get_devnode(dev)); udev_device_unref(dev); if (action == QLatin1String("remove") && (m_devNode == devNode) && m_devNode.isEmpty()==false) { m_devNode = QLatin1String(""); emit deviceDisconnected(); } else if (action == QLatin1String("add") && m_serialNumber == serial && m_serialNumber.isEmpty()==false) { emit deviceConnected(); emit deviceConnected(m_serialNumber); m_devNode = devNode; } else if (action == QLatin1String("add")) { if (!serial.isEmpty()) { emit deviceConnected(serial); } } } ./src/ubuntu/targetupgrademanagerdialog.ui0000644000015600001650000000363012705421114021115 0ustar jenkinsjenkins Ubuntu::Internal::TargetUpgradeManagerDialog 0 0 653 479 Dialog Updates are available for your Kits. Please select those which should be updated. true QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() Ubuntu::Internal::TargetUpgradeManagerDialog accept() 440 448 649 41 buttonBox rejected() Ubuntu::Internal::TargetUpgradeManagerDialog reject() 358 457 648 79 ./src/ubuntu/ubunturemotedeployconfiguration.cpp0000644000015600001650000001327412705421114022461 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubunturemotedeployconfiguration.h" #include "ubuntudirectuploadstep.h" #include "ubuntuprojecthelper.h" #include "ubuntuconstants.h" #include "ubuntupackagestep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; UbuntuRemoteDeployConfiguration::UbuntuRemoteDeployConfiguration(ProjectExplorer::Target *target) : DeployConfiguration(target,Constants::UBUNTU_DEPLOYCONFIGURATION_ID) { setDefaultDisplayName(tr("Deploy to Ubuntu Device")); } UbuntuRemoteDeployConfiguration::UbuntuRemoteDeployConfiguration(ProjectExplorer::Target *target, UbuntuRemoteDeployConfiguration *source) : DeployConfiguration(target,source) { } ProjectExplorer::NamedWidget *UbuntuRemoteDeployConfiguration::createConfigWidget() { return new ProjectExplorer::NamedWidget(); } UbuntuRemoteDeployConfigurationFactory::UbuntuRemoteDeployConfigurationFactory(QObject *parent) : DeployConfigurationFactory(parent) { setObjectName(QLatin1String("UbuntuDeployConfiguration")); } QList UbuntuRemoteDeployConfigurationFactory::availableCreationIds(ProjectExplorer::Target *parent) const { QList ids; if (!parent->project()->supportsKit(parent->kit())) return ids; ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(parent->kit()); if (!tc || tc->targetAbi().os() != ProjectExplorer::Abi::LinuxOS) return ids; Core::Id projectTypeId = parent->project()->id(); qDebug()<<"Project ID: "<kit()); if (devType.toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID))) ids << Core::Id(Constants::UBUNTU_DEPLOYCONFIGURATION_ID); return ids; } QString UbuntuRemoteDeployConfigurationFactory::displayNameForId(const Core::Id id) const { if (id == Core::Id(Constants::UBUNTU_DEPLOYCONFIGURATION_ID)) return tr("Deploy to Ubuntu Device"); return QString(); } bool UbuntuRemoteDeployConfigurationFactory::canCreate(ProjectExplorer::Target *parent, const Core::Id id) const { return availableCreationIds(parent).contains(id); } ProjectExplorer::DeployConfiguration *UbuntuRemoteDeployConfigurationFactory::create(ProjectExplorer::Target *parent, const Core::Id id) { QTC_ASSERT(canCreate(parent, id),return 0); ProjectExplorer::DeployConfiguration * const dc = new UbuntuRemoteDeployConfiguration(parent); int step = 0; UbuntuPackageStep *pckStep = new UbuntuPackageStep(dc->stepList()); dc->stepList()->insertStep(0,pckStep); UbuntuDirectUploadStep* upload = new UbuntuDirectUploadStep(dc->stepList()); dc->stepList()->insertStep(step+1,upload); return dc; } bool UbuntuRemoteDeployConfigurationFactory::canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const { return canCreate(parent, ProjectExplorer::idFromMap(map)); } ProjectExplorer::DeployConfiguration *UbuntuRemoteDeployConfigurationFactory::restore(ProjectExplorer::Target *parent, const QVariantMap &map) { if (!canRestore(parent, map)) return 0; UbuntuRemoteDeployConfiguration * const dc = new UbuntuRemoteDeployConfiguration(parent); if (!dc->fromMap(map)) { delete dc; return 0; } return dc; } bool UbuntuRemoteDeployConfigurationFactory::canClone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *product) const { return canCreate(parent,product->id()); } ProjectExplorer::DeployConfiguration *UbuntuRemoteDeployConfigurationFactory::clone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *product) { if (!canClone(parent, product)) return 0; return new UbuntuRemoteDeployConfiguration(parent, qobject_cast(product)); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntulocalrunconfigurationfactory.h0000644000015600001650000000563712705421114022631 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTURUNCONFIGURATIONFACTORY_H #define UBUNTURUNCONFIGURATIONFACTORY_H #include #include #include "ubuntuproject.h" #include "ubuntuconstants.h" #include "ubuntulocalrunconfiguration.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuLocalRunConfigurationFactory : public ProjectExplorer::IRunConfigurationFactory { Q_OBJECT public: explicit UbuntuLocalRunConfigurationFactory() { setObjectName(QLatin1String("UbuntuRunConfigurationFactory")); } QList availableCreationIds(ProjectExplorer::Target *parent, CreationMode mode = UserCreate) const override; QString displayNameForId(const Core::Id id) const override; bool canCreate(ProjectExplorer::Target *parent, const Core::Id id) const override; bool canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const override; bool canClone(ProjectExplorer::Target *parent, ProjectExplorer::RunConfiguration *product) const override; ProjectExplorer::RunConfiguration *clone(ProjectExplorer::Target *parent, ProjectExplorer::RunConfiguration *source) override; private: bool canHandle(ProjectExplorer::Target *parent) const; ProjectExplorer::RunConfiguration *doCreate(ProjectExplorer::Target *parent, const Core::Id id) override; ProjectExplorer::RunConfiguration *doRestore(ProjectExplorer::Target *parent, const QVariantMap &map) override; }; } } #endif // UBUNTURUNCONFIGURATIONFACTORY_H ./src/ubuntu/ubuntuclickdialog.h0000644000015600001650000000506212705421114017067 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUCLICKDIALOG_H #define UBUNTU_INTERNAL_UBUNTUCLICKDIALOG_H #include #include #include #include #include "ubuntuclicktool.h" namespace ProjectExplorer { class Project; class Target; } namespace Ubuntu { namespace Internal { namespace Ui { class UbuntuClickDialog; } class UbuntuClickDialog : public QDialog { Q_OBJECT public: UbuntuClickDialog (QWidget* parent = 0); ~UbuntuClickDialog (); void setParameters (const QList ¶ms); int lastExitCode () const; public slots: void runClick (); static int runClickModal(const ProjectExplorer::ProcessParameters ¶ms, QWidget *parent = 0); static int runClickModal (const QList ¶ms, QWidget *parent = 0); static bool createClickChrootModal (bool redetectKits = true , const QString &arch = QString(), const QString &framework = QString(), QWidget *parent = 0); static int maintainClickModal (const UbuntuClickTool::Target &target, const UbuntuClickTool::MaintainMode &mode); static int maintainClickModal (const QList &targetList, const UbuntuClickTool::MaintainMode &mode); // QDialog interface virtual void done(int code); protected: void disableCloseButton (const bool &disabled = true); void nextTask (); protected slots: void on_clickFinished(int exitCode); void on_clickReadyReadStandardOutput(const QString txt = QString()); void on_clickReadyReadStandardError(const QString txt = QString()); private: Utils::QtcProcess *m_process; Ui::UbuntuClickDialog *m_ui; QList m_tasks; int m_exitCode; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUCLICKDIALOG_H ./src/ubuntu/ubuntueditorfactory.h0000644000015600001650000000120712705421114017475 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUMANIFESTEDITORFACTORY_H #define UBUNTU_INTERNAL_UBUNTUMANIFESTEDITORFACTORY_H #include namespace Ubuntu { namespace Internal { class UbuntuManifestEditorFactory : public Core::IEditorFactory { Q_OBJECT public: explicit UbuntuManifestEditorFactory(); Core::IEditor *createEditor(); }; class UbuntuApparmorEditorFactory : public Core::IEditorFactory { Q_OBJECT public: explicit UbuntuApparmorEditorFactory(); Core::IEditor *createEditor(); }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUMANIFESTEDITORFACTORY_H ./src/ubuntu/ubuntupolicygroupmodel.h0000644000015600001650000000267112705421114020222 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef POLICYGROUPMODEL_H #define POLICYGROUPMODEL_H #include #include #include "ubuntuprocess.h" namespace Ubuntu { namespace Internal { class UbuntuPolicyGroupModel : public QStringListModel { Q_OBJECT public: explicit UbuntuPolicyGroupModel(QObject *parent = 0); void setPolicyVersion (const QString &policyVersion); QString policyVersion () const; void scanPolicyGroups(); bool isLocal() { return m_bLocal; } public slots: void onMessage(QString); void onFinished(QString, int); signals: void scanComplete(bool); protected: UbuntuProcess m_process; QStringList m_replies; QString m_policyVersion; bool m_bLocal; }; } } #endif // POLICYGROUPMODEL_H ./src/ubuntu/ubuntuabstractguieditordocument.h0000644000015600001650000000315012705421114022074 0ustar jenkinsjenkins/* * Copyright 2014 Digia Plc and/or its subsidiary(-ies). * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUMANIFESTDOCUMENT_H #define UBUNTU_INTERNAL_UBUNTUMANIFESTDOCUMENT_H #include #include namespace Ubuntu { namespace Internal { class UbuntuAbstractGuiEditorWidget; class UbuntuAbstractGuiEditorDocument : public TextEditor::TextDocument { public: UbuntuAbstractGuiEditorDocument(const QString &mimeType, UbuntuAbstractGuiEditorWidget *editorWidget); bool save(QString *errorString, const QString &fileName = QString(), bool autoSave = false); QString defaultPath() const override; QString suggestedFileName() const override; bool isModified() const override; bool isSaveAsAllowed() const override; private: UbuntuAbstractGuiEditorWidget *m_editorWidget; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUMANIFESTDOCUMENT_H ./src/ubuntu/ubuntupackagestepconfigwidget.ui0000644000015600001650000000305412705421114021670 0ustar jenkinsjenkins UbuntuPackageStepConfigWidget 0 0 400 300 Form Treat click review errors as warnings Enable debug helper comboBoxMode <html><head/><body><p>Specifies if the debug helper script <br/>is injected into the click package.</p><p>Auto will only package them when <br/>the project is compiled in debug mode.</p></body></html> <html><head/><body><p><br/></p></body></html> ./src/ubuntu/ubuntucmakemakestep.h0000644000015600001650000000504112705421114017431 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUCMAKEMAKESTEP_H #define UBUNTU_INTERNAL_UBUNTUCMAKEMAKESTEP_H #include #include #include namespace Ubuntu { namespace Internal { class UbuntuCMakeMakeStepFactory : public ProjectExplorer::IBuildStepFactory { Q_OBJECT public: // IBuildStepFactory interface virtual QList availableCreationIds(ProjectExplorer::BuildStepList *parent) const override; virtual QString displayNameForId(const Core::Id id) const override; virtual bool canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const override; virtual ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id) override; virtual bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const override; virtual ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) override; virtual bool canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const override; virtual ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) override; private: bool canHandle(const ProjectExplorer::Target *t) const; }; class UbuntuCMakeMakeStep : public CMakeProjectManager::MakeStep { Q_OBJECT public: UbuntuCMakeMakeStep(ProjectExplorer::BuildStepList *bsl); UbuntuCMakeMakeStep(ProjectExplorer::BuildStepList *bsl, UbuntuCMakeMakeStep *bs); virtual ~UbuntuCMakeMakeStep(); virtual QString makeCommand(ProjectExplorer::ToolChain *tc, const Utils::Environment &env) const override; friend class UbuntuCMakeMakeStepFactory; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUCMAKEMAKESTEP_H ./src/ubuntu/resources.qrc0000644000015600001650000000231512705421114015725 0ustar jenkinsjenkins images/ubuntu-64.png images/ubuntu-128.png images/ubuntu-qtcreator.png images/ubuntu-48.png images/device.png UbuntuProject.mimetypes.xml images/irc.png images/ubuntu-32.png images/launchpad.png images/coreapps.png images/packaging.png manifest.json.template myapp.json.template manifestlib.js images/view-refresh.png images/list-add.png images/reload.svg images/list-add.svg images/list-remove.svg images/security-alert.svg images/view-expand.svg images/view-collapse.svg click-framework.json images/run.png images/stop.png images/warning.png images/error.png images/clear.png ./src/ubuntu/ubuntudevicemode.cpp0000644000015600001650000001170612705421114017263 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntudevicemode.h" #include "ubuntuconstants.h" #include "ubuntudevicesmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu::Internal; UbuntuDeviceMode *UbuntuDeviceMode::m_instance = 0; UbuntuDeviceMode::UbuntuDeviceMode(QObject *parent) : Core::IMode(parent) { Q_ASSERT_X(m_instance == 0, Q_FUNC_INFO,"There can be only one instance of UbuntuDeviceMode"); m_instance = this; m_qmlControl = new UbuntuQMLDeviceMode(this); setDisplayName(tr(Ubuntu::Constants::UBUNTU_MODE_DEVICES_DISPLAYNAME)); setIcon(QIcon(QLatin1String(Ubuntu::Constants::UBUNTU_MODE_DEVICES_ICON))); setPriority(Ubuntu::Constants::UBUNTU_MODE_DEVICES_PRIORITY); setId(Ubuntu::Constants::UBUNTU_MODE_DEVICES); setObjectName(QLatin1String(Ubuntu::Constants::UBUNTU_MODE_DEVICES)); setContext(Core::Context("Ubuntu Plugin")); setContextHelpId(QStringLiteral("Managing Devices")); m_modeWidget = new QWidget; QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); layout->setSpacing(0); m_modeWidget->setLayout(layout); Utils::StyledBar* styledBar = new Utils::StyledBar(m_modeWidget); layout->addWidget(styledBar); m_modeView = new QQuickView; m_modeView->setResizeMode(QQuickView::SizeRootObjectToView); m_devicesModel = new UbuntuDevicesModel(m_modeView); connect(m_devicesModel,SIGNAL(logMessage(QString)),m_qmlControl,SLOT(addText(QString))); connect(m_devicesModel,SIGNAL(stdOutMessage(QString)),m_qmlControl,SLOT(addText(QString))); connect(m_devicesModel,SIGNAL(stdErrMessage(QString)),m_qmlControl,SLOT(addErrorText(QString))); QWidget* container = QWidget::createWindowContainer(m_modeView); container->setMinimumWidth(860); container->setMinimumHeight(548); container->setFocusPolicy(Qt::TabFocus); layout->addWidget(container); m_modeView->rootContext()->setContextProperty(QLatin1String("devicesModel") ,m_devicesModel); m_modeView->rootContext()->setContextProperty(QLatin1String("deviceMode") ,m_qmlControl); m_modeView->rootContext()->setContextProperty(QLatin1String("resourceRoot") ,Constants::UBUNTU_DEVICESCREEN_ROOT); m_modeView->setSource(QUrl::fromLocalFile(Constants::UBUNTU_DEVICESCREEN_QML)); connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)), SLOT(modeChanged(Core::IMode*))); setWidget(m_modeWidget); } UbuntuDevice::ConstPtr UbuntuDeviceMode::device() { return UbuntuDevice::ConstPtr(); if(m_devicesModel->rowCount() <= 0) return UbuntuDevice::ConstPtr(); if(!m_deviceIndex.isValid()) { m_deviceIndex = 0; //device 0 is always the first selected } return m_devicesModel->device(m_deviceIndex.toInt()); } void UbuntuDeviceMode::deviceSelected(const QVariant index) { m_deviceIndex = index; emit updateDeviceActions (); } void UbuntuDeviceMode::showAddEmulatorDialog() { m_qmlControl->showAddEmulatorDialog(); } void UbuntuDeviceMode::modeChanged(Core::IMode *mode) { Q_UNUSED(mode); } void UbuntuDeviceMode::initialize() { } UbuntuDeviceMode *UbuntuDeviceMode::instance() { return m_instance; } UbuntuQMLDeviceMode::UbuntuQMLDeviceMode(UbuntuDeviceMode *parent) : QObject(parent), m_mode(parent) { } void UbuntuQMLDeviceMode::showAddEmulatorDialog() { emit openAddEmulatorDialog(); } void UbuntuQMLDeviceMode::deviceSelected(const QVariant index) { m_mode->deviceSelected(index); } void UbuntuQMLDeviceMode::addText(const QString &arg) { QString in = arg; in.replace(QStringLiteral("\n"),QStringLiteral("
")); emit appendText(in); } void UbuntuQMLDeviceMode::addErrorText(const QString &error) { QString in = error; in.replace(QStringLiteral("\n"),QStringLiteral("
")); in.prepend(QStringLiteral("")); in.append(QStringLiteral("")); emit appendText(in); } ./src/ubuntu/ubuntusettingsdeviceconnectivitypage.h0000644000015600001650000000272212705421114023136 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUSETTINGSDEVICECONNECTIVITYPAGE_H #define UBUNTUSETTINGSDEVICECONNECTIVITYPAGE_H #include #include "ubuntusettingsdeviceconnectivitywidget.h" #include namespace Ubuntu { namespace Internal { class UbuntuSettingsDeviceConnectivityPage : public Core::IOptionsPage { Q_OBJECT public: explicit UbuntuSettingsDeviceConnectivityPage(); ~UbuntuSettingsDeviceConnectivityPage(); QWidget *widget( ) override; void apply() override; void finish() override; protected: QPointer m_widget; }; } } #endif // UBUNTUSETTINGSDEVICECONNECTIVITYPAGE_H ./src/ubuntu/clicktoolchain.h0000644000015600001650000000557212705421114016353 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_CLICKTOOLCHAIN_H #define UBUNTU_INTERNAL_CLICKTOOLCHAIN_H #include #include "ubuntuclicktool.h" namespace Ubuntu { namespace Internal { class ClickToolChainFactory; class ClickToolChain : public ProjectExplorer::GccToolChain { friend class ClickToolChainFactory; // ToolChain interface public: ClickToolChain(const UbuntuClickTool::Target &target,Detection d); virtual QList suggestedMkspecList() const override; virtual Utils::FileName suggestedDebugger() const override; virtual QString type() const override; virtual QString typeDisplayName() const override; virtual bool isValid() const override; virtual void addToEnvironment(Utils::Environment &env) const override; virtual QString makeCommand(const Utils::Environment &) const override; virtual bool operator ==(const ProjectExplorer::ToolChain &tc) const override; virtual ProjectExplorer::ToolChainConfigWidget *configurationWidget() override; virtual QVariantMap toMap() const override; QString gnutriplet () const; static QString gnutriplet (const ProjectExplorer::Abi &abi); const UbuntuClickTool::Target &clickTarget () const; static ProjectExplorer::Abi architectureNameToAbi ( const QString &arch ); static QList supportedArchitectures (); protected: virtual bool fromMap(const QVariantMap &data) override; ClickToolChain(const ClickToolChain& other); ClickToolChain(); private: UbuntuClickTool::Target m_clickTarget; // ToolChain interface public: virtual Utils::FileName compilerCommand() const override; }; class ClickToolChainFactory : public ProjectExplorer::ToolChainFactory { Q_OBJECT public: ClickToolChainFactory(); // ToolChainFactory interface public: virtual QList autoDetect() override; virtual bool canRestore(const QVariantMap &data) override; virtual ProjectExplorer::ToolChain *restore(const QVariantMap &data) override; static QList createToolChainsForClickTargets(); }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_CLICKTOOLCHAIN_H ./src/ubuntu/ubuntulocalruncontrolfactory.h0000644000015600001650000000140112705421114021423 0ustar jenkinsjenkins#ifndef UBUNTULOCALRUNCONTROLFACTORY_H #define UBUNTULOCALRUNCONTROLFACTORY_H #include namespace Ubuntu { namespace Internal { class UbuntuLocalRunControlFactory : public ProjectExplorer::IRunControlFactory { Q_OBJECT public: explicit UbuntuLocalRunControlFactory() {} virtual ~UbuntuLocalRunControlFactory() {} bool canRun(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode) const override; ProjectExplorer::RunControl *create(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode, QString *errorMessage) override; QString displayName() const; }; } //namespace Internal } //namespace Ubuntu #endif // UBUNTULOCALRUNCONTROLFACTORY_H ./src/ubuntu/ubuntufixmanifeststep.cpp0000644000015600001650000000554612705421114020375 0ustar jenkinsjenkins#include "ubuntufixmanifeststep.h" #include "ubuntuconstants.h" #include #include #include #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuFixManifestStep * The UbuntuFixManifestStep can be used to fill in fields into * a manifest file in the build process, its used in the fat packaging * process */ UbuntuFixManifestStep::UbuntuFixManifestStep(ProjectExplorer::BuildStepList *bsl) : ProjectExplorer::BuildStep(bsl, Core::Id("UbuntuProjectManager.UbuntuFixManifestStep")) { } QStringList UbuntuFixManifestStep::architectures() const { return m_architectures; } void UbuntuFixManifestStep::setArchitectures(const QStringList &architectures) { m_architectures = architectures; } QString UbuntuFixManifestStep::packageDir() const { return m_packageDir; } void UbuntuFixManifestStep::setPackageDir(const QString &packageDir) { m_packageDir = packageDir; } bool UbuntuFixManifestStep::init() { return true; } void UbuntuFixManifestStep::run(QFutureInterface &fi) { bool result = false; if (!m_packageDir.isEmpty() ) { Utils::FileName manifestFileName = Utils::FileName::fromString(m_packageDir) .appendPath(QStringLiteral("manifest.json")); QFile manifestFile(manifestFileName.toString()); if(Q_UNLIKELY(!manifestFile.open(QIODevice::ReadOnly))) { emit addOutput(tr("Can not open manifest file for reading."),ProjectExplorer::BuildStep::ErrorOutput); fi.reportFinished(&result); return; } if (m_architectures.isEmpty()) { emit addOutput(tr("Can not fix manifest file, no architectures are given."),ProjectExplorer::BuildStep::ErrorOutput); fi.reportFinished(&result); return; } QJsonDocument doc = QJsonDocument::fromJson(manifestFile.readAll()); QJsonObject rootObj = doc.object(); manifestFile.close(); if (m_architectures.size() > 1) rootObj[QStringLiteral("architecture")] = QJsonArray::fromStringList(m_architectures); else rootObj[QStringLiteral("architecture")] = QJsonValue(m_architectures.first()); doc.setObject(rootObj); if(!manifestFile.open(QIODevice::WriteOnly | QIODevice::Truncate)){ emit addOutput(tr("Can not open manifest file for writing."),ProjectExplorer::BuildStep::ErrorOutput); fi.reportFinished(&result); return; } manifestFile.write(doc.toJson()); manifestFile.close(); } result = true; fi.reportFinished(&result); } ProjectExplorer::BuildStepConfigWidget *UbuntuFixManifestStep::createConfigWidget() { return new ProjectExplorer::SimpleBuildStepConfigWidget(this); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntulocalscopedebugsupport.cpp0000644000015600001650000001366712705421114021757 0ustar jenkinsjenkins#include "ubuntulocalscopedebugsupport.h" #include "ubuntulocalrunconfiguration.h" #include "ubuntuprojecthelper.h" #include "ubuntuconstants.h" #include #include #include #include #include #include namespace Ubuntu { namespace Internal { UbuntuLocalScopeDebugSupport::UbuntuLocalScopeDebugSupport(UbuntuLocalRunConfiguration *runConfig, Debugger::DebuggerRunControl *runControl, const QString &scopeRunnerPath) : QObject(runControl) , m_port(-1) , m_scopeRunnerPath(scopeRunnerPath) , m_runControl(runControl) { m_executable = runConfig->executable(); m_commandLineArguments = runConfig->commandLineArguments(); ProjectExplorer::EnvironmentAspect *env = runConfig->extraAspect(); if (env) { m_launcher.setEnvironment(env->environment()); } connect (runControl, &Debugger::DebuggerRunControl::requestRemoteSetup, this, &UbuntuLocalScopeDebugSupport::handleRemoteSetupRequested); connect (&m_launcher,SIGNAL(appendMessage(QString,Utils::OutputFormat)), m_runControl, SLOT(appendMessage(QString,Utils::OutputFormat))); connect (&m_launcher, &ProjectExplorer::ApplicationLauncher::processStarted, this, &UbuntuLocalScopeDebugSupport::handleProcessStarted); connect (&m_launcher, &ProjectExplorer::ApplicationLauncher::processExited, this, &UbuntuLocalScopeDebugSupport::handleProcessExited); connect (&m_launcher, &ProjectExplorer::ApplicationLauncher::bringToForegroundRequested, m_runControl, &Debugger::DebuggerRunControl::bringApplicationToForeground); connect (&m_launcher, &ProjectExplorer::ApplicationLauncher::error, this, &UbuntuLocalScopeDebugSupport::handleError); connect (m_runControl, &Debugger::DebuggerRunControl::stateChanged, this, &UbuntuLocalScopeDebugSupport::handleStateChanged); } UbuntuLocalScopeDebugSupport::~UbuntuLocalScopeDebugSupport() { m_launcher.stop(); } void UbuntuLocalScopeDebugSupport::handleRemoteSetupRequested() { // Inject the debug mode into the INI file, this is not perfect // as it will stick even for the next non debug run, and even though // we can then start without gdbserver the timouts will be always set // high, because the DebugMode=true setting is still there m_port = getLocalPort(); QStringList args = Utils::QtcProcess::splitArgs(m_commandLineArguments); if (args.size() < 0) { Debugger::RemoteSetupResult res; res.success = false; res.reason = tr("Not enough arguments to run a scope."); m_runControl->notifyEngineRemoteSetupFinished(res); return; } QFileInfo iniFile(args.first()); if (!iniFile.isFile()) { Debugger::RemoteSetupResult res; res.success = false; res.reason = tr("Ini filepath %1 is not valid.").arg(args.first()); m_runControl->notifyEngineRemoteSetupFinished(res); return; } QString appId = iniFile.completeBaseName(); //debughelper script name, source and destination path const QString debScript = QStringLiteral("qtc_device_debughelper.py"); const QString debSourcePath = QStringLiteral("%1/%2").arg(Constants::UBUNTU_SCRIPTPATH).arg(debScript); QString commTemplate = QStringLiteral("%S scope %1 %C") .arg(appId); //tell our script the appid QString defaultSubCmd = QStringLiteral("%1 %R %S ") .arg(m_scopeRunnerPath); bool injected = UbuntuProjectHelper::injectScopeDebugHelper( iniFile.absoluteFilePath(), debSourcePath, commTemplate, defaultSubCmd); if (!injected) { Debugger::RemoteSetupResult res; res.success = false; res.reason = tr("Could not inject the debug helper"); m_runControl->notifyEngineRemoteSetupFinished(res); return; } args.append(QStringLiteral("--cppdebug")); args.append(QString::number(m_port)); m_launcher.start(m_appLauncherMode, m_executable, Utils::QtcProcess::joinArgs(args)); } void UbuntuLocalScopeDebugSupport::handleProcessStarted() { Debugger::RemoteSetupResult res; res.success = true; res.gdbServerPort = m_port; m_runControl->notifyEngineRemoteSetupFinished(res); } void UbuntuLocalScopeDebugSupport::handleProcessExited(int exitCode, QProcess::ExitStatus) { if (exitCode != 0) m_runControl->notifyInferiorIll(); else m_runControl->debuggingFinished(); } void UbuntuLocalScopeDebugSupport::handleError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { Debugger::RemoteSetupResult res; res.success = false; res.reason = tr("The process failed to start"); m_runControl->notifyEngineRemoteSetupFinished(res); } if (error == QProcess::Crashed) m_runControl->notifyInferiorIll(); } void UbuntuLocalScopeDebugSupport::handleStateChanged(Debugger::DebuggerState state) { qDebug() << "Changed to State: "< #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuRemoteAnalyzeSupportPrivate { public: UbuntuRemoteAnalyzeSupportPrivate(Analyzer::AnalyzerRunControl *rc, Core::Id runMode) : runControl(rc), qmlProfiling(runMode == ProjectExplorer::Constants::QML_PROFILER_RUN_MODE), qmlPort(-1) { } QString clickPackage; const QPointer runControl; bool qmlProfiling; int qmlPort; QmlDebug::QmlOutputParser outputParser; }; UbuntuRemoteAnalyzeSupport::UbuntuRemoteAnalyzeSupport(UbuntuRemoteRunConfiguration *runConfig, Analyzer::AnalyzerRunControl *engine, Core::Id runMode) : AbstractRemoteRunSupport(runConfig, engine), d(new UbuntuRemoteAnalyzeSupportPrivate(engine, runMode)) { d->clickPackage = runConfig->clickPackage(); connect(d->runControl, SIGNAL(starting(const Analyzer::AnalyzerRunControl*)), SLOT(handleRemoteSetupRequested())); connect(&d->outputParser, SIGNAL(waitingForConnectionOnPort(quint16)), SLOT(remoteIsRunning())); } UbuntuRemoteAnalyzeSupport::~UbuntuRemoteAnalyzeSupport() { delete d; } void UbuntuRemoteAnalyzeSupport::showMessage(const QString &msg, Utils::OutputFormat format) { if (state() != Idle && d->runControl) d->runControl->logApplicationMessage(msg, format); d->outputParser.processOutput(msg); } void UbuntuRemoteAnalyzeSupport::handleRemoteSetupRequested() { QTC_ASSERT(state() == Idle, return); showMessage(tr("Checking available ports...") + QLatin1Char('\n'), Utils::NormalMessageFormat); AbstractRemoteRunSupport::handleRemoteSetupRequested(); } void UbuntuRemoteAnalyzeSupport::startExecution() { QTC_ASSERT(state() == ScanningPorts, return); // Currently we support only QML profiling QTC_ASSERT(d->qmlProfiling, return); if (!assignNextFreePort(&d->qmlPort)) return; setState(Starting); UbuntuRemoteClickApplicationRunner *runner = appRunner(); connect(runner, SIGNAL(launcherStderr(QByteArray)), SLOT(handleRemoteErrorOutput(QByteArray))); connect(runner, SIGNAL(launcherStdout(QByteArray)), SLOT(handleRemoteOutput(QByteArray))); connect(runner, SIGNAL(clickApplicationStarted(quint16)), SLOT(handleRemoteProcessStarted())); connect(runner, SIGNAL(finished(bool)), SLOT(handleAppRunnerFinished(bool))); connect(runner, SIGNAL(reportError(QString)), SLOT(handleAppRunnerError(QString))); runner->setEnv(environment()); runner->setQmlDebugPort(d->qmlPort); QTC_ASSERT(device()->type().toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID)),return); runner->start(qSharedPointerCast(device()),clickPackage(),hook()); } void UbuntuRemoteAnalyzeSupport::handleAppRunnerError(const QString &error) { if (state() == Running) showMessage(error, Utils::ErrorMessageFormat); else if (state() != Idle) handleAdapterSetupFailed(error); } void UbuntuRemoteAnalyzeSupport::handleAppRunnerFinished(bool success) { // reset needs to be called first to ensure that the correct state is set. reset(); if (!success) showMessage(tr("Failure running remote process."), Utils::NormalMessageFormat); d->runControl->notifyRemoteFinished(); } void UbuntuRemoteAnalyzeSupport::handleProfilingFinished() { setFinished(); } void UbuntuRemoteAnalyzeSupport::remoteIsRunning() { d->runControl->notifyRemoteSetupDone(d->qmlPort); } void UbuntuRemoteAnalyzeSupport::handleRemoteOutput(const QByteArray &output) { QTC_ASSERT(state() == Idle || state() == Running, return); showMessage(QString::fromUtf8(output), Utils::StdOutFormat); } void UbuntuRemoteAnalyzeSupport::handleRemoteErrorOutput(const QByteArray &output) { QTC_ASSERT(state() != ScanningPorts, return); if (!d->runControl) return; showMessage(QString::fromUtf8(output), Utils::StdErrFormat); } void UbuntuRemoteAnalyzeSupport::handleProgressReport(const QString &progressOutput) { showMessage(progressOutput + QLatin1Char('\n'), Utils::NormalMessageFormat); } void UbuntuRemoteAnalyzeSupport::handleAdapterSetupFailed(const QString &error) { AbstractRemoteRunSupport::handleAdapterSetupFailed(error); showMessage(tr("Initial setup failed: %1").arg(error), Utils::NormalMessageFormat); } void UbuntuRemoteAnalyzeSupport::handleRemoteProcessStarted() { QTC_ASSERT(d->qmlProfiling, return); QTC_ASSERT(state() == Starting, return); handleAdapterSetupDone(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuapparmoreditor.ui0000644000015600001650000000224512705421114020040 0ustar jenkinsjenkins UbuntuAppArmorEditor 0 0 604 360 Form Security Policy Groups + true ./src/ubuntu/ubuntuprojecthelper.cpp0000644000015600001650000001717412705421114020032 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuprojecthelper.h" #include "ubuntucmakecache.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; UbuntuProjectHelper::UbuntuProjectHelper() { } Utils::FileName UbuntuProjectHelper::findScopesIniRecursive(const Utils::FileName &searchdir, const QString &appid) { return findFileRecursive(searchdir,QStringLiteral("^.*_%1\\.ini.*$").arg(appid)); } Utils::FileName UbuntuProjectHelper::findFileRecursive(const Utils::FileName &searchdir, const QString ®exp) { QRegularExpression regex(regexp); return findFileRecursive(searchdir,regex); } Utils::FileName UbuntuProjectHelper::findFileRecursive(const Utils::FileName &searchdir, const QRegularExpression ®exp) { QFileInfo dirInfo = searchdir.toFileInfo(); if(!dirInfo.exists()) return Utils::FileName(); if(!dirInfo.isDir()) return Utils::FileName(); QDir dir(dirInfo.absoluteFilePath()); QStringList entries = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files | QDir::Hidden); foreach (const QString& entry, entries) { QFileInfo info(dir.absoluteFilePath(entry)); if(info.isDir()) { Utils::FileName f = findFileRecursive(Utils::FileName::fromString(dir.absoluteFilePath(entry)),regexp); if(!f.isEmpty()) return f; continue; } QRegularExpressionMatch match = regexp.match(entry); if(match.hasMatch()) { return Utils::FileName(info); } } return Utils::FileName(); } QList UbuntuProjectHelper::findFilesRecursive(const Utils::FileName &searchdir, const QRegularExpression ®exp) { QList result; QFileInfo dirInfo = searchdir.toFileInfo(); if(!dirInfo.exists()) return result; if(!dirInfo.isDir()) return result; QDir dir(dirInfo.absoluteFilePath()); QStringList entries = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files); foreach (const QString& entry, entries) { QFileInfo info(dir.absoluteFilePath(entry)); if(info.isDir()) { result.append(findFileRecursive(Utils::FileName::fromString(dir.absoluteFilePath(entry)),regexp)); continue; } QRegularExpressionMatch match = regexp.match(entry); if(match.hasMatch()) { result.append(Utils::FileName(info)); } } return result; } QString UbuntuProjectHelper::getManifestPath(ProjectExplorer::Target *target, const QString &defaultValue) { if(target && target->project()->id() == CMakeProjectManager::Constants::CMAKEPROJECT_ID ) { QVariant manifestPath = UbuntuCMakeCache::getValue(QStringLiteral("UBUNTU_MANIFEST_PATH"), target->activeBuildConfiguration(), defaultValue); Utils::FileName projectDir = target->project()->projectDirectory(); return projectDir.appendPath(manifestPath.toString()).toString(); } else if (target && target->project()->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID ) { QmakeProjectManager::QmakeProject *qmakeProj = static_cast(target->project()); QList nodes = qmakeProj->allProFiles(); QString manifestFilePath; //empty foreach (QmakeProjectManager::QmakeProFileNode *node, nodes) { if(!node) continue; manifestFilePath = node->singleVariableValue(QmakeProjectManager::UbuntuManifestFile); if(manifestFilePath.isEmpty()) { continue; } else if(QDir::isRelativePath(manifestFilePath)) { manifestFilePath = QDir::cleanPath(node->sourceDir() +QDir::separator() +manifestFilePath); break; } else { manifestFilePath = QDir::cleanPath(manifestFilePath); break; } } if(manifestFilePath.isEmpty()) return defaultValue; return manifestFilePath; } return defaultValue; } /*! * \brief UbuntuProjectHelper::injectScopeDebugHelper * Injects the \a commandTemplate into the scope ini. * Replaces %S with \a scriptName and %C with the subcommand. * If no ScopeRunner is set \a defaultSubCmd is used. */ bool UbuntuProjectHelper::injectScopeDebugHelper(const QString &iniFilePath, const QString &scriptName, const QString &commandTemplate, const QString &defaultSubCmd) { GKeyFile* keyFile = g_key_file_new(); GKeyFileFlags flags = static_cast(G_KEY_FILE_KEEP_TRANSLATIONS|G_KEY_FILE_KEEP_COMMENTS); if(!g_key_file_load_from_file(keyFile,qPrintable(iniFilePath),flags,NULL)){ g_key_file_free(keyFile); qWarning()<<"Could not read the ini file"; return false; } QString subCmd; if(g_key_file_has_key(keyFile,"ScopeConfig","ScopeRunner",NULL)) { gchar *value = g_key_file_get_string(keyFile,"ScopeConfig","ScopeRunner",NULL); if(value == NULL) { qWarning()<<"Could not read the ScopeRunner entry"; g_key_file_free(keyFile); return false; } subCmd = QString::fromUtf8(value); g_free(value); } else { subCmd = defaultSubCmd; } if (!subCmd.contains(scriptName)) { QString command = QString(commandTemplate) .replace(QStringLiteral("%S"),scriptName) .replace(QStringLiteral("%C"),subCmd); g_key_file_set_string(keyFile,"ScopeConfig","ScopeRunner",command.toUtf8().data()); } g_key_file_set_boolean(keyFile,"ScopeConfig","DebugMode",TRUE); gsize size = 0; gchar *settingData = g_key_file_to_data (keyFile, &size, NULL); if(!settingData) { qWarning()<<"Could not convert the new data into the ini file"; g_key_file_free(keyFile); return false; } gboolean ret = g_file_set_contents (qPrintable(iniFilePath), settingData, size, NULL); g_free (settingData); g_key_file_free (keyFile); return ret == TRUE; } QString UbuntuProjectHelper::getManifestPath(ProjectExplorer::Project *p, const QString &defaultValue) { if(!p) return defaultValue; return getManifestPath(p->activeTarget(),defaultValue); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntulocaldeployconfiguration.h0000644000015600001650000000457012705421114021724 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTULOCALDEPLOYCONFIGURATIONFACTORY_H #define UBUNTU_INTERNAL_UBUNTULOCALDEPLOYCONFIGURATIONFACTORY_H #include namespace Ubuntu { namespace Internal { class UbuntuLocalDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory { Q_OBJECT public: explicit UbuntuLocalDeployConfigurationFactory(QObject *parent = 0); QList availableCreationIds(ProjectExplorer::Target *parent) const override; QString displayNameForId(const Core::Id id) const override; bool canCreate(ProjectExplorer::Target *parent, const Core::Id id) const override; ProjectExplorer::DeployConfiguration *create(ProjectExplorer::Target *parent, const Core::Id id) override; bool canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const override; ProjectExplorer::DeployConfiguration *restore(ProjectExplorer::Target *parent, const QVariantMap &map) override; ProjectExplorer::DeployConfiguration *clone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *product) override; }; class UbuntuLocalDeployConfiguration : public ProjectExplorer::DeployConfiguration { Q_OBJECT friend class UbuntuLocalDeployConfigurationFactory; // for the ctors protected: UbuntuLocalDeployConfiguration(ProjectExplorer::Target *target, const Core::Id id); UbuntuLocalDeployConfiguration(ProjectExplorer::Target *target, UbuntuLocalDeployConfiguration *source); protected slots: void selectAsDefaultHack(); }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTULOCALDEPLOYCONFIGURATIONFACTORY_H ./src/ubuntu/ubunturemotedebugsupport.h0000644000015600001650000000256212705421114020563 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUREMOTEDEBUGSUPPORT_H #define UBUNTU_INTERNAL_UBUNTUREMOTEDEBUGSUPPORT_H #include "abstractremoterunsupport.h" #include namespace Ubuntu { namespace Internal { class UbuntuRemoteDebugSupportPrivate; class UbuntuRemoteRunConfiguration; class UbuntuRemoteDebugSupport : public AbstractRemoteRunSupport { Q_OBJECT public: UbuntuRemoteDebugSupport(UbuntuRemoteRunConfiguration *runConfig, Debugger::DebuggerRunControl *runControl); ~UbuntuRemoteDebugSupport(); protected: void startExecution() override; void handleAdapterSetupFailed(const QString &error) override; private slots: void handleRemoteSetupRequested() override; void handleAdapterSetupDone() override; void handleAppRunnerError(const QString &error) override; void handleRemoteOutput(const QByteArray &output) override; void handleRemoteErrorOutput(const QByteArray &output) override; void handleAppRunnerFinished(bool success) override; void handleProgressReport(const QString &progressOutput) override; void handleRemoteProcessStarted(quint16 pid); void handleDebuggingFinished(); private: void showMessage(const QString &msg, int channel); UbuntuRemoteDebugSupportPrivate * const d; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUREMOTEDEBUGSUPPORT_H ./src/ubuntu/ubuntupolicygroupinfo.cpp0000644000015600001650000000424212705421114020404 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntupolicygroupinfo.h" using namespace Ubuntu::Internal; UbuntuPolicyGroupInfo::UbuntuPolicyGroupInfo(QObject *parent) : QObject(parent), m_bLocal(false) { connect(&m_process,SIGNAL(message(QString)),this,SLOT(onMessage(QString))); connect(&m_process,SIGNAL(finished(QString,int)),this,SLOT(onFinished(QString,int))); } void UbuntuPolicyGroupInfo::getInfo(const QString &policyGroup, const QString &policyVersion) { m_policyGroup = policyGroup; m_policyVersion = policyVersion; QStringList cmd; if (!isLocal()) { cmd << QString(QLatin1String("adb shell aa-easyprof --show-policy-group -p %1 --policy-vendor=ubuntu --policy-version=%2")) .arg(policyGroup) .arg(policyVersion); } else { cmd << QString(QLatin1String("aa-easyprof --show-policy-group -p %1 --policy-vendor=ubuntu --policy-version=%2")) .arg(policyGroup) .arg(policyVersion); } m_process.append(cmd); m_replies.clear(); m_process.start(QLatin1String("Reading policy group")); } void UbuntuPolicyGroupInfo::onMessage(QString line) { m_replies.append(line); } void UbuntuPolicyGroupInfo::onFinished(QString, int result) { if(result == 0) { emit infoReady(true); return; } else if (result != 0 && !m_bLocal) { m_replies.clear(); m_bLocal = true; getInfo(m_policyGroup,m_policyVersion); return; } emit infoReady(false); } ./src/ubuntu/ubuntumanifesteditorwidget.cpp0000644000015600001650000004511212705421114021376 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntumanifesteditorwidget.h" #include "ubuntuconstants.h" #include "ubuntuabstractguieditordocument.h" #include "ubuntumanifesteditor.h" #include "ubuntuapparmoreditor.h" #include "ubuntuclicktool.h" #include "clicktoolchain.h" #include "ubuntubzr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; UbuntuManifestEditorWidget::UbuntuManifestEditorWidget() : UbuntuAbstractGuiEditorWidget(QLatin1String(Constants::UBUNTU_MANIFEST_MIME_TYPE)), m_ui(0) { createUI(); UbuntuBzr *bzr = UbuntuBzr::instance(); connect(bzr,SIGNAL(initializedChanged()),SLOT(bzrChanged())); if(bzr->isInitialized()) bzrChanged(); } QWidget *UbuntuManifestEditorWidget::createMainWidget() { Q_ASSERT_X(m_ui == 0,Q_FUNC_INFO,"createMainWidget was called multiple times"); QWidget *w = new QWidget(); m_ui = new Ui::UbuntuManifestEditor(); m_ui->setupUi(w); QRegularExpressionValidator *versionValidator = new QRegularExpressionValidator(m_ui->lineEdit_version); versionValidator->setRegularExpression(QRegularExpression(QLatin1String("^\\d*(\\.\\d*)*$"))); m_ui->lineEdit_version->setValidator(versionValidator); connect(m_ui->comboBoxFramework,SIGNAL(currentIndexChanged(int)),this,SLOT(onFrameworkChanged())); connect(m_ui->lineEdit_description,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); connect(m_ui->lineEdit_maintainer,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); connect(m_ui->lineEdit_name,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); connect(m_ui->lineEdit_title,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); connect(m_ui->lineEdit_version,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); connect(UbuntuClickFrameworkProvider::instance(),SIGNAL(frameworksUpdated()),this,SLOT(updateFrameworkList())); updateFrameworkList(); return w; } UbuntuManifestEditorWidget::~UbuntuManifestEditorWidget() { delete m_ui; } void UbuntuManifestEditorWidget::updateAfterFileLoad() { //let see if we have valid data m_manifest = QSharedPointer(new UbuntuClickManifest); if(m_manifest->loadFromString(m_sourceEditor->toPlainText())) { if(activePage() != Source) syncToWidgets(m_manifest.data()); } else { //switch to source page without syncing m_widgetStack->setCurrentIndex(Source); updateInfoBar(tr("There is a error in the file, please check the syntax.")); } } void UbuntuManifestEditorWidget::aboutToOpen(const QString &fileName, const QString &realFileName) { Q_UNUSED(fileName); addMissingFieldsToManifest(realFileName); } bool UbuntuManifestEditorWidget::syncToWidgets() { QSharedPointer man(new UbuntuClickManifest); if(man->loadFromString(m_sourceEditor->toPlainText())) { m_manifest.swap(man); syncToWidgets(m_manifest.data()); updateInfoBar(QString()); return true; } QString text = tr("There is a error in the file, please check the syntax."); updateInfoBar(text); return false; } bool UbuntuManifestEditorWidget::syncToWidgets(UbuntuClickManifest *source) { QString data = source->maintainer(); if(data != m_ui->lineEdit_maintainer->text()) m_ui->lineEdit_maintainer->setText(data); data = source->name(); if(data != m_ui->lineEdit_name->text()) m_ui->lineEdit_name->setText(data); data = source->title(); if(data != m_ui->lineEdit_title->text()) m_ui->lineEdit_title->setText(data); data = source->version(); if(data != m_ui->lineEdit_version->text()) m_ui->lineEdit_version->setText(data); data = source->description(); if(data != m_ui->lineEdit_description->text()) m_ui->lineEdit_description->setText(data); //disable the currentIndexChanged signal, we need to check outselves if //the data has changed m_ui->comboBoxFramework->blockSignals(true); QString fwText = m_ui->comboBoxFramework->currentText(); selectFramework(source->frameworkName()); m_ui->comboBoxFramework->blockSignals(false); //set the dirty flag manually in case something has changed if(m_ui->comboBoxFramework->currentText() != fwText) setDirty(); QSet idxToKeep; QList hooks = source->hooks(); foreach(const UbuntuClickManifest::Hook &hook, hooks) { int idx = m_ui->comboBoxHook->findText(hook.appId); QWidget *container = 0; //create a new one if(idx < 0) { container = createHookWidget(hook); m_ui->comboBoxHook->addItem(hook.appId); idx = m_ui->stackedWidget->addWidget(container); } else container = m_ui->stackedWidget->widget(idx); if(!hook.desktopFile.isEmpty()) { QLineEdit *desktop = container->findChild(hook.appId+QStringLiteral(".desktop")); QLineEdit *appArmor = container->findChild(hook.appId+QStringLiteral(".apparmor")); if(desktop && desktop->text() != hook.desktopFile) desktop->setText(hook.desktopFile); if(appArmor && appArmor->text() != hook.appArmorFile) appArmor->setText(hook.appArmorFile); } else if (!hook.scope.isEmpty()){ QLineEdit *scope = container->findChild(hook.appId+QStringLiteral(".scope")); QLineEdit *appArmor = container->findChild(hook.appId+QStringLiteral(".apparmor")); if(scope && scope->text() != hook.scope) scope->setText(hook.scope); if(appArmor && appArmor->text() != hook.appArmorFile) appArmor->setText(hook.appArmorFile); } idxToKeep.insert(idx); } //clean up old widgets if(idxToKeep.size() != m_ui->comboBoxHook->count()) { for(int i = m_ui->comboBoxHook->count(); i >= 0; i--) { if(!idxToKeep.contains(i)) { m_ui->comboBoxHook->removeItem(i); QWidget* toRemove = m_ui->stackedWidget->widget(i); m_ui->stackedWidget->removeWidget(toRemove); delete toRemove; } } } m_dirty = false; emit uiEditorChanged(); return true; } void UbuntuManifestEditorWidget::syncToSource() { // set package name to lower, bug #1219877 QString packageName = m_ui->lineEdit_name->text(); static const QRegularExpression varCheck(QStringLiteral("@.*@")); if(!varCheck.match(packageName).hasMatch()) { packageName = packageName.toLower(); m_ui->lineEdit_name->setText(packageName); } m_manifest->setName(packageName); m_manifest->setMaintainer(m_ui->lineEdit_maintainer->text()); m_manifest->setVersion(m_ui->lineEdit_version->text()); m_manifest->setTitle(m_ui->lineEdit_title->text()); m_manifest->setDescription(m_ui->lineEdit_description->text()); if(m_ui->comboBoxFramework->currentData() != Constants::UBUNTU_UNKNOWN_FRAMEWORK_DATA) m_manifest->setFrameworkName(m_ui->comboBoxFramework->currentText()); for(int idx = 0; idx < m_ui->comboBoxHook->count(); idx++) { QWidget *container = m_ui->stackedWidget->widget(idx); QString appId = m_ui->comboBoxHook->itemText(idx); QLineEdit *desktop = container->findChild(appId+QStringLiteral(".desktop")); QLineEdit *scope = container->findChild(appId+QStringLiteral(".scope")); QLineEdit *appArmor = container->findChild(appId+QStringLiteral(".apparmor")); UbuntuClickManifest::Hook hook; hook.appId = appId; hook.appArmorFile = appArmor->text(); if(desktop) hook.desktopFile = desktop->text(); else if(scope) hook.scope = scope->text(); else //What to do here, this should never happen continue; m_manifest->setHook(hook); } QString result = m_manifest->raw()+QStringLiteral("\n"); QString src = m_sourceEditor->toPlainText(); if (result == src) return; m_sourceEditor->setPlainText(result); m_sourceEditor->document()->setModified(true); m_dirty = false; emit uiEditorChanged(); } void UbuntuManifestEditorWidget::updateFrameworkList() { m_ui->comboBoxFramework->blockSignals(true); //the current selected fw QString fwText = m_ui->comboBoxFramework->currentText(); m_ui->comboBoxFramework->clear(); m_ui->comboBoxFramework->addItems(UbuntuClickFrameworkProvider::getSupportedFrameworks()); selectFramework(fwText); m_ui->comboBoxFramework->blockSignals(false); } void UbuntuManifestEditorWidget::selectFramework (const QString &fw) { // get the new Index for our new fw int idx = m_ui->comboBoxFramework->findText(fw); //if the framework name is not valid set to empty item //just some data to easily find the unknown framework item without //using string compare if(idx < 0) { if(m_ui->comboBoxFramework->findData(Constants::UBUNTU_UNKNOWN_FRAMEWORK_DATA) < 0) m_ui->comboBoxFramework->addItem(tr(Constants::UBUNTU_UNKNOWN_FRAMEWORK_NAME),Constants::UBUNTU_UNKNOWN_FRAMEWORK_DATA); m_ui->comboBoxFramework->setCurrentIndex(m_ui->comboBoxFramework->count()-1); } else { m_ui->comboBoxFramework->setCurrentIndex(idx); m_ui->comboBoxFramework->removeItem(m_ui->comboBoxFramework->findData(Constants::UBUNTU_UNKNOWN_FRAMEWORK_DATA)); } } void UbuntuManifestEditorWidget::bzrChanged() { UbuntuBzr *bzr = UbuntuBzr::instance(); m_ui->lineEdit_maintainer->setText(bzr->whoami()); /* Commented out for bug #1219948 - https://bugs.launchpad.net/qtcreator-plugin-ubuntu/+bug/1219948 QString userName = bzr->launchpadId(); if (userName.isEmpty()) userName = QLatin1String(Constants::USERNAME); m_ui->lineEdit_maintainer->setText(QString(QLatin1String("%0.%1")).arg(m_projectName).arg(userName)); */ if(activePage() != General) syncToSource(); } void UbuntuManifestEditorWidget::onFrameworkChanged() { //make sure all changes are in the manifest instance syncToSource(); if(m_ui->comboBoxFramework->currentData() != Constants::UBUNTU_UNKNOWN_FRAMEWORK_DATA) { int idx = m_ui->comboBoxFramework->findData(Constants::UBUNTU_UNKNOWN_FRAMEWORK_DATA); if(idx >= 0) m_ui->comboBoxFramework->removeItem(idx); } QString v = UbuntuClickFrameworkProvider::instance()->frameworkPolicy(m_ui->comboBoxFramework->currentText()); if(v.isEmpty()) return; QList hooks = m_manifest->hooks(); foreach(const UbuntuClickManifest::Hook &hook, hooks){ QFileInfo mFile = textEditorWidget()->textDocument()->filePath().toFileInfo(); QString aaFile = mFile.absolutePath()+QDir::separator()+hook.appArmorFile; if(!QFile::exists(aaFile)) { ProjectExplorer::Project *p = ubuntuProject(mFile.absolutePath()); if(!p) continue; //the aa file does not live in the same directory as the manifest file //try if we can use the project root directory to find the file aaFile = p->projectDirectory().appendPath(hook.appArmorFile).toString(); if(!QFile::exists(aaFile)) continue; } QList editors = Core::DocumentModel::editorsForFilePath(aaFile); if(editors.size()) { if(debug) qDebug()<<"Write into editor contents"; foreach(Core::IEditor *ed, editors){ //first check if we have a ubuntu apparmor editor UbuntuApparmorEditor *uEd = qobject_cast(ed); if(uEd) { uEd->guiEditor()->setVersion(v); break; } //ok that is no ubuntu editor, lets check if its a basic texteditor and just //replace the contents TextEditor::BaseTextEditor *txtEd = qobject_cast(ed); if(txtEd) { //ok this is more complicated, first lets get the contents UbuntuClickManifest aa; if(aa.loadFromString(txtEd->textDocument()->plainText())){ aa.setPolicyVersion(v); txtEd->textDocument()->setPlainText(aa.raw()); txtEd->textDocument()->document()->setModified(true); break; } } } } else { if(debug) qDebug()<<"Write the closed file"; UbuntuClickManifest aa; if(aa.load(aaFile)) { aa.setPolicyVersion(v); aa.save(); } } } } QWidget *UbuntuManifestEditorWidget::createHookWidget(const UbuntuClickManifest::Hook &hook) { QWidget *container = new QWidget(m_ui->stackedWidget); QVBoxLayout *layout = new QVBoxLayout(container); if(!hook.desktopFile.isEmpty()) { //App hook QLabel* label = new QLabel(tr("Desktop file")); layout->addWidget(label); QLineEdit *lE = new QLineEdit(); lE->setObjectName(hook.appId+QStringLiteral(".desktop")); connect(lE,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); layout->addWidget(lE); label = new QLabel(tr("Apparmor file")); layout->addWidget(label); lE = new QLineEdit(); lE->setObjectName(hook.appId+QStringLiteral(".apparmor")); connect(lE,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); layout->addWidget(lE); } else if(!hook.scope.isEmpty()) { //Scope hook QLabel* label = new QLabel(tr("Scope ini file")); layout->addWidget(label); QLineEdit *lE = new QLineEdit(); lE->setObjectName(hook.appId+QStringLiteral(".scope")); connect(lE,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); layout->addWidget(lE); label = new QLabel(tr("Apparmor file")); layout->addWidget(label); lE = new QLineEdit(); lE->setObjectName(hook.appId+QStringLiteral(".apparmor")); connect(lE,SIGNAL(textChanged(QString)),this,SLOT(setDirty())); layout->addWidget(lE); } layout->addStretch(); container->setLayout(layout); return container; } QString UbuntuManifestEditorWidget::createPackageName(const QString &userName, const QString &projectName) { return QString(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_DEFAULT_NAME)).arg(projectName).arg(userName); } void UbuntuManifestEditorWidget::addMissingFieldsToManifest (QString fileName) { QFile in(fileName); if(!in.open(QIODevice::ReadOnly)) return; QFile templateFile(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_DEFAULT_MANIFEST)); if(!templateFile.open(QIODevice::ReadOnly)) return; QString projectName = tr("unknown"); ProjectExplorer::Project *p = ubuntuProject(fileName); if(p) projectName = p->displayName(); QJsonParseError err; QJsonDocument templateDoc = QJsonDocument::fromJson(templateFile.readAll(),&err); if(err.error != QJsonParseError::NoError) return; QJsonDocument inDoc = QJsonDocument::fromJson(in.readAll(),&err); if(err.error != QJsonParseError::NoError) return; in.close(); QJsonObject templateObject = templateDoc.object(); QJsonObject targetObject = inDoc.object(); QJsonObject::const_iterator i = templateObject.constBegin(); UbuntuBzr *bzr = UbuntuBzr::instance(); bool changed = false; for(;i != templateObject.constEnd(); i++) { if(!targetObject.contains(i.key())) { changed = true; if(debug) qDebug()<<"Manifest file missing key: "<launchpadId(); if (userName.isEmpty()) userName = QLatin1String(Constants::USERNAME); targetObject.insert(i.key(),createPackageName(userName,projectName)); if(debug) qDebug()<<"Setting to "<whoami()); if(debug) qDebug()<<"Setting to "<whoami(); } else if (i.key() == QStringLiteral("framework")) { targetObject.insert(i.key(),UbuntuClickFrameworkProvider::getMostRecentFramework( QString())); } else { targetObject.insert(i.key(),i.value()); if(debug) qDebug() <<"Setting to "<textDocument()->filePath().toFileInfo(); ProjectExplorer::Project *p = ubuntuProject(mFile.absoluteFilePath()); if(p && p->activeTarget()) p->activeTarget()->updateDefaultRunConfigurations(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntucmaketool.h0000644000015600001650000000330712705421114016600 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTUCMAKETOOL_H #define UBUNTUCMAKETOOL_H #include #include namespace Ubuntu { namespace Internal { class UbuntuCMakeTool : public CMakeProjectManager::CMakeTool { Q_OBJECT public: explicit UbuntuCMakeTool(QObject *parent = 0); explicit UbuntuCMakeTool(const Core::Id &id, QObject *parent = 0); // ICMakeTool interface virtual void addToEnvironment(Utils::Environment &env) const override; virtual QString displayName () override; virtual bool isValid() const override; void setEnvironment (const Utils::Environment &env); private: bool m_hasEnvironment; Utils::Environment m_env; }; class UbuntuCMakeToolFactory : public CMakeProjectManager::ICMakeToolFactory { Q_OBJECT public: virtual bool canCreate (const Core::Id& id) const override; virtual CMakeProjectManager::ICMakeTool *create (const Core::Id& id) override; }; } //namespace Internal } //namespace Ubuntu #endif // UBUNTUCMAKETOOL_H ./src/ubuntu/ubuntudevicesmodel.cpp0000644000015600001650000012416212705421114017623 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntudevicesmodel.h" #include "ubuntuconstants.h" #include "ubuntukitmanager.h" #include "ubuntuscopefinalizer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; UbuntuDevicesModel::UbuntuDevicesModel(QObject *parent) : QAbstractListModel(parent), m_cancellable(false), m_state(Initial) { m_deviceNotifier = new UbuntuDeviceNotifier(this); connect(m_deviceNotifier,SIGNAL(deviceConnected(QString)),this,SLOT(deviceConnected(QString))); m_process = new UbuntuProcess(this); connect(m_process,SIGNAL(message(QString)),this,SLOT(onMessage(QString))); connect(m_process,SIGNAL(finished(QString,int)),this,SLOT(processFinished(QString,int))); connect(m_process,SIGNAL(stdOut(QString)),this,SIGNAL(stdOutMessage(QString))); connect(m_process,SIGNAL(error(QString)),this,SIGNAL(stdErrMessage(QString))); ProjectExplorer::KitManager* devMgr = static_cast(ProjectExplorer::KitManager::instance()); connect(devMgr,SIGNAL(kitsLoaded()),this,SLOT(refresh())); connect(ProjectExplorer::DeviceManager::instance(),SIGNAL(deviceListReplaced()),this,SLOT(readDevicesFromSettings())); } bool UbuntuDevicesModel::set(int index, const QString &role, const QVariant &value) { if(index < 0 || index >= rowCount()) return false; QModelIndex idx = createIndex(index,0); if(!roleNames().values().contains(role.toUtf8())) return false; return setData(idx,value,roleNames().key(role.toUtf8())); } int UbuntuDevicesModel::rowCount(const QModelIndex &parent) const { if(parent.isValid()) return 0; return m_knownDevices.size(); } bool UbuntuDevicesModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(debug) qDebug()<<"Setting index "< rowCount()) return false; UbuntuDevice::Ptr dev = m_knownDevices[index.row()]->device(); switch (role) { case Qt::DisplayRole: case Qt::EditRole: dev->setDisplayName(value.toString()); emit dataChanged(index,index,QVector()<developerModeEnabled(); if(oldState == UbuntuDevice::Unknown) return false; if( oldState != newState ) dev->setDeveloperModeEnabled(set); return true; break; } case NetworkConnectionRole: { if(value.type() != QVariant::Bool) return false; bool set = value.toBool(); UbuntuDevice::FeatureState oldState = dev->hasNetworkConnection(); if( oldState == UbuntuDevice::Unknown || oldState == UbuntuDevice::Available ) return false; if(set) dev->cloneNetwork(); return true; break; } case WriteableImageRole: { if(value.type() != QVariant::Bool) return false; bool set = value.toBool(); UbuntuDevice::FeatureState newState = set ? UbuntuDevice::Available : UbuntuDevice::NotAvailable; UbuntuDevice::FeatureState oldState = dev->hasWriteableImage(); if(oldState == UbuntuDevice::Unknown) return false; if( oldState != newState ) dev->setWriteableImageEnabled(set); return true; break; } case DeveloperToolsRole: { if(value.type() != QVariant::Bool) return false; bool set = value.toBool(); UbuntuDevice::FeatureState newState = set ? UbuntuDevice::Available : UbuntuDevice::NotAvailable; UbuntuDevice::FeatureState oldState = dev->hasDeveloperTools(); if(oldState == UbuntuDevice::Unknown) return false; if( oldState != newState ) dev->setDeveloperToolsInstalled(set); return true; break; } case EmulatorScaleFactorRole: { if(value.type() != QVariant::String) return false; QString set = value.toString(); if(dev->scaleFactor() != set) return dev->setScaleFactor(set); return true; break; } case EmulatorMemorySettingRole: { if(value.type() != QVariant::String) return false; QString set = value.toString(); if(dev->memorySetting() != set) return dev->setMemorySetting(set); return true; break; } default: break; } return false; } QVariant UbuntuDevicesModel::data(const QModelIndex &index, int role) const { if(!index.isValid() || index.parent().isValid() || index.row() < 0 || index.row() > rowCount()) return QVariant(); switch (role) { case Qt::DisplayRole: case Qt::EditRole: return m_knownDevices[index.row()]->device()->displayName(); case UniqueIdRole: return m_knownDevices[index.row()]->id().uniqueIdentifier(); case DetectionStateRole: return m_knownDevices[index.row()]->device()->detectionState(); case DetectionStateStringRole: return m_knownDevices[index.row()]->device()->detectionStateString(); case ConnectionStateRole: return m_knownDevices[index.row()]->device()->deviceState(); case ConnectionStateStringRole: return m_knownDevices[index.row()]->device()->deviceStateToString(); case KitListRole: return QVariant::fromValue(m_knownDevices[index.row()]->kits()); case DeveloperModeRole: return m_knownDevices[index.row()]->device()->developerModeEnabled(); case NetworkConnectionRole: return m_knownDevices[index.row()]->device()->hasNetworkConnection(); case WriteableImageRole: return m_knownDevices[index.row()]->device()->hasWriteableImage(); case DeveloperToolsRole: return m_knownDevices[index.row()]->device()->hasDeveloperTools(); case LogRole: return m_knownDevices[index.row()]->device()->helper()->log(); case SerialIdRole: return m_knownDevices[index.row()]->device()->serialNumber(); case ModelInfoRole: return m_knownDevices[index.row()]->device()->modelInfo(); case DeviceInfoRole: return m_knownDevices[index.row()]->device()->deviceInfo(); case ProductInfoRole: return m_knownDevices[index.row()]->device()->productInfo(); case MachineTypeRole: return m_knownDevices[index.row()]->device()->machineType(); case FrameworkRole: return m_knownDevices[index.row()]->device()->framework(); case EmulatorImageRole: return m_knownDevices[index.row()]->device()->imageName(); case EmulatorDeviceVersionRole: return m_knownDevices[index.row()]->device()->deviceVersion(); case EmulatorUbuntuVersionRole: return m_knownDevices[index.row()]->device()->ubuntuVersion(); case EmulatorImageVersionRole: return m_knownDevices[index.row()]->device()->imageVersion(); case EmulatorScaleFactorRole: return m_knownDevices[index.row()]->device()->scaleFactor(); case EmulatorMemorySettingRole: return m_knownDevices[index.row()]->device()->memorySetting(); default: break; } return QVariant(); } QHash UbuntuDevicesModel::roleNames() const { QHash roles = QAbstractListModel::roleNames(); roles.insert(UniqueIdRole,"deviceId"); roles.insert(DetectionStateRole,"detectionState"); roles.insert(DetectionStateStringRole,"detectionStateString"); roles.insert(ConnectionStateRole,"connectionState"); roles.insert(ConnectionStateStringRole,"connectionStateString"); roles.insert(KitListRole,"kits"); roles.insert(DeveloperModeRole,"developerModeEnabled"); roles.insert(NetworkConnectionRole,"hasNetworkConnection"); roles.insert(WriteableImageRole,"hasWriteableImage"); roles.insert(DeveloperToolsRole,"hasDeveloperTools"); roles.insert(LogRole,"deviceLog"); roles.insert(SerialIdRole,"serial"); roles.insert(ModelInfoRole,"modelInfo"); roles.insert(DeviceInfoRole,"deviceInfo"); roles.insert(ProductInfoRole,"productInfo"); roles.insert(MachineTypeRole,"machineType"); roles.insert(FrameworkRole,"frameworkVersion"); roles.insert(EmulatorImageRole,"emulatorImageName"); roles.insert(EmulatorDeviceVersionRole,"emuDeviceVersion"); roles.insert(EmulatorUbuntuVersionRole,"emuUbuntuVersion"); roles.insert(EmulatorImageVersionRole,"emuImageVersion"); roles.insert(EmulatorScaleFactorRole,"emulatorScaleFactor"); roles.insert(EmulatorMemorySettingRole,"emulatorMemorySetting"); return roles; } Qt::ItemFlags UbuntuDevicesModel::flags(const QModelIndex &index) const { return QAbstractListModel::flags(index) | Qt::ItemIsEditable; } void UbuntuDevicesModel::triggerPortForwarding(const int devId) { int row = findDevice(devId); if(row < 0) return; m_knownDevices[row]->device()->enablePortForward(); } void UbuntuDevicesModel::triggerSSHSetup(const int devId) { int row = findDevice(devId); if(row < 0) return; m_knownDevices[row]->device()->deployPublicKey(); } void UbuntuDevicesModel::triggerSSHConnection(const int devId) { int row = findDevice(devId); if(row < 0) return; m_knownDevices[row]->device()->openTerminal(); } void UbuntuDevicesModel::triggerKitAutocreate(const int devId) { int row = findDevice(devId); if(row < 0) return; UbuntuKitManager::autoCreateKit(m_knownDevices[row]->device()); } void UbuntuDevicesModel::triggerKitRemove(const int devId, const QVariant &kitid) { int row = findDevice(devId); if(row < 0) return; ProjectExplorer::Kit* k = ProjectExplorer::KitManager::find(Core::Id::fromSetting(kitid)); if(ProjectExplorer::DeviceKitInformation::deviceId(k) == Core::Id::fromUniqueIdentifier(devId)) { //completely delete the kit ProjectExplorer::KitManager::deregisterKit(k); } } void UbuntuDevicesModel::triggerRedetect(const int devId) { int row = findDevice(devId); if(row < 0) return; m_knownDevices[row]->device()->helper()->refresh(); } void UbuntuDevicesModel::deleteDevice(const int devId) { int index = findDevice(devId); if(index < 0) { QMessageBox::critical(Core::ICore::mainWindow(),tr("Could not delete device"),tr("The device ID is unknown, please press the refresh button and try again.")); return; } ProjectExplorer::DeviceManager::instance()->removeDevice(m_knownDevices[index]->device()->id()); } void UbuntuDevicesModel::refresh() { readDevicesFromSettings(); checkEmulatorInstalled(); } void UbuntuDevicesModel::clear() { if(rowCount()) { beginRemoveRows(QModelIndex(),0,rowCount()-1); qDeleteAll(m_knownDevices.begin(),m_knownDevices.end()); m_knownDevices.clear(); endRemoveRows(); } } int UbuntuDevicesModel::findDevice(int uniqueIdentifier) const { for ( int i = 0; i < m_knownDevices.size(); i++ ) { if(m_knownDevices[i]->id().uniqueIdentifier() == uniqueIdentifier) return i; } return -1; } bool UbuntuDevicesModel::hasDevice(int uniqueIdentifier) const { return findDevice(uniqueIdentifier) >= 0; } UbuntuDevice::ConstPtr UbuntuDevicesModel::device(const int index) { if(index < 0 || index >= rowCount()) return UbuntuDevice::ConstPtr(); return m_knownDevices[index]->device(); } UbuntuDevicesItem *UbuntuDevicesModel::createItem(UbuntuDevice::Ptr dev) { UbuntuDevicesItem *devItem = new UbuntuDevicesItem(dev,this); connect(devItem,SIGNAL(kitsChanged()),this,SLOT(kitsChanged())); connect(devItem,SIGNAL(detectionStateChanged()),this,SLOT(detectionStateChanged())); connect(devItem,SIGNAL(featureDetected()),this,SLOT(featureDetected())); connect(devItem,SIGNAL(logUpdated()),this,SLOT(logUpdated())); connect(devItem,SIGNAL(deviceInfoUpdated()),this,SLOT(deviceInfoUpdated())); return devItem; } /*! * \brief UbuntuDevicesModel::indexFromHelper * Checks if the passed QObject is a DeviceHelper and returns the * row index of the related device */ int UbuntuDevicesModel::indexFromHelper(QObject *possibleHelper) { UbuntuDevicesItem* hlpr = qobject_cast(possibleHelper); if(!hlpr) return -1; return findDevice(hlpr->id().uniqueIdentifier()); } void UbuntuDevicesModel::deviceChanged(QObject *possibleHelper, const QVector &relatedRoles) { int idx = indexFromHelper(possibleHelper); if(idx < 0) return; QModelIndex changed = createIndex(idx,0); emit dataChanged(changed,changed,relatedRoles); } /*! * \brief UbuntuDevicesModel::readDevicesFromSettings * read all known devices from the DeviceManager, this is triggered * automatically on startup */ void UbuntuDevicesModel::readDevicesFromSettings() { clear(); QList devs; ProjectExplorer::DeviceManager* devMgr = ProjectExplorer::DeviceManager::instance(); for(int i = 0; i < devMgr->deviceCount(); i++) { ProjectExplorer::IDevice::ConstPtr dev = devMgr->deviceAt(i); if(dev && dev->type().toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID))) { //ugly hack to get a mutable version of the device //no idea why its necessary to lock it up Ubuntu::Internal::UbuntuDevice* cPtr = qSharedPointerCast(dev)->helper()->device(); if(cPtr) devs.append(createItem(cPtr->sharedFromThis())); } } if (devs.count()) { beginInsertRows(QModelIndex(),0,devs.count()-1); m_knownDevices = devs; endInsertRows(); } connect(devMgr,SIGNAL(deviceAdded(Core::Id)),this,SLOT(deviceAdded(Core::Id)),Qt::UniqueConnection); connect(devMgr,SIGNAL(deviceRemoved(Core::Id)),this,SLOT(deviceRemoved(Core::Id)),Qt::UniqueConnection); connect(devMgr,SIGNAL(deviceUpdated(Core::Id)),this,SLOT(deviceUpdated(Core::Id)),Qt::UniqueConnection); } void UbuntuDevicesModel::detectionStateChanged() { //contains the possible changed roles when this slot is called static QVector relatedRoles = QVector() << DetectionStateRole << DetectionStateStringRole; deviceChanged(sender(),relatedRoles); } void UbuntuDevicesModel::featureDetected() { //contains the possible changed roles when this slot is called static QVector relatedRoles = QVector() << DeveloperModeRole << NetworkConnectionRole << WriteableImageRole << DeveloperToolsRole; deviceChanged(sender(),relatedRoles); } void UbuntuDevicesModel::deviceInfoUpdated() { //contains the possible changed roles when this slot is called static QVector relatedRoles = QVector() << SerialIdRole << ModelInfoRole << DeviceInfoRole << ProductInfoRole << EmulatorDeviceVersionRole << EmulatorUbuntuVersionRole << EmulatorImageVersionRole << EmulatorScaleFactorRole << EmulatorMemorySettingRole << FrameworkRole; deviceChanged(sender(),relatedRoles); } void UbuntuDevicesModel::logUpdated() { //contains the possible changed roles when this slot is called static QVector relatedRoles = QVector() << LogRole; deviceChanged(sender(),relatedRoles); } void UbuntuDevicesModel::kitsChanged() { //contains the possible changed roles when this slot is called static QVector relatedRoles = QVector() << KitListRole; deviceChanged(sender(),relatedRoles); } /*! * \brief UbuntuDevicesModel::deviceAdded * A device was added in the DeviceManager, check if know it and * if we should know it. If its a new Ubuntu device its added to * the known devices */ void UbuntuDevicesModel::deviceAdded(const Core::Id &id) { ProjectExplorer::IDevice::ConstPtr ptr = ProjectExplorer::DeviceManager::instance()->find(id); if(!ptr) return; if(!ptr->type().toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID))) return; if(debug) qDebug()<<"Device Manager reports device added: "<(ptr); Ubuntu::Internal::UbuntuDeviceHelper* hlp = ubuntuDev->helper(); Ubuntu::Internal::UbuntuDevice* dev = hlp->device(); beginInsertRows(QModelIndex(),rowCount(),rowCount()); m_knownDevices.append(createItem(dev->sharedFromThis())); endInsertRows(); } /*! * \brief UbuntuDevicesWidget::deviceRemoved * A device was removed from the device manager, if its one of ours * we will also remove it */ void UbuntuDevicesModel::deviceRemoved(const Core::Id &id) { int index = findDevice(id.uniqueIdentifier()); if(index < 0) return; if(debug) qDebug()<<"Device Manager reports device removed: "< relatedRoles = QVector() << Qt::DisplayRole << Qt::EditRole << ConnectionStateRole << ConnectionStateStringRole; int index = findDevice(id.uniqueIdentifier()); if(index >= 0) { QModelIndex changed = createIndex(index,0); emit dataChanged(changed, changed,relatedRoles); } } void UbuntuDevicesModel::deviceConnected(const QString &id) { int idx = findDevice(Core::Id::fromSetting(id).uniqueIdentifier()); if(idx >= 0) return; refresh(); } /*! * \brief UbuntuDevicesModel::registerNewDevice * Registers a new device in the device manager if its not * already in the known devices map. * \note will not add it into model list, this * will happen automatically when the device is * registered in the device manager */ void UbuntuDevicesModel::registerNewDevice(const QString &serial, const QString &arch) { if(findDevice(Core::Id::fromSetting(serial).uniqueIdentifier()) >= 0) return; if(!ClickToolChain::supportedArchitectures().contains(arch)) { emit logMessage(tr("Error: Can not register device %1. Architecture %2 is not supported.") .arg(serial) .arg(arch)); return; } Ubuntu::Internal::UbuntuDevice::Ptr dev = Ubuntu::Internal::UbuntuDevice::create( tr("Ubuntu Device") , serial , ProjectExplorer::IDevice::Hardware , arch , ProjectExplorer::IDevice::AutoDetected); ProjectExplorer::DeviceManager::instance()->addDevice(dev); } bool UbuntuDevicesModel::emulatorInstalled() const { return m_emulatorInstalled; } void UbuntuDevicesModel::setEmulatorInstalled(bool arg) { if (m_emulatorInstalled != arg) { m_emulatorInstalled = arg; emit emulatorInstalledChanged(arg); } } bool UbuntuDevicesModel::busy() const { return m_busy; } void UbuntuDevicesModel::setBusy(bool arg) { if (m_busy != arg) { m_busy = arg; emit busyChanged(arg); } } QString UbuntuDevicesModel::state() const { switch(m_state) { case CheckEmulatorInstalled: { return tr("Checking if emulator tool is installed"); break; } case InstallEmulator: { return tr("Installing emulator tool"); break; } case CreateEmulatorImage: { return tr("Creating emulator image"); break; } case ReadFromSettings:{ return tr("Reading settings"); } case FindImages:{ return tr("Searching for emulator images"); break; } case AdbList:{ return tr("Querying ADB"); break; } default: return QString(); break; } } void UbuntuDevicesModel::setState(UbuntuDevicesModel::State newState) { if(m_state != newState) { m_state = newState; if(m_state == UbuntuDevicesModel::Initial || m_state == UbuntuDevicesModel::Idle) setBusy(false); else setBusy(true); emit stateChanged(state()); } } bool UbuntuDevicesModel::cancellable() const { return m_cancellable; } void UbuntuDevicesModel::setCancellable(bool arg) { if (m_cancellable != arg) { m_cancellable = arg; emit cancellableChanged(arg); } } void UbuntuDevicesModel::beginAction(const QString &msg) { emit logMessage(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ACTION_BEGIN).arg(msg)); } void UbuntuDevicesModel::endAction(const QString &msg) { emit logMessage(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ACTION_END).arg(msg)); } void UbuntuDevicesModel::checkEmulatorInstalled() { setState(CheckEmulatorInstalled); setCancellable(false); m_emulatorInstalled = false; beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_EMULATOR_INSTALLED)); m_process->stop(); QString sEmulatorPackageName = QLatin1String(Ubuntu::Constants::EMULATOR_PACKAGE_NAME); m_process->append(QStringList() << QString::fromLatin1(Constants::UBUNTUWIDGETS_LOCAL_PACKAGE_INSTALLED_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH).arg(sEmulatorPackageName) << QCoreApplication::applicationDirPath()); m_process->start(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_EMULATOR_INSTALLED)); } void UbuntuDevicesModel::findEmulatorImages() { setState(FindImages); setCancellable(false); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_SEARCH_IMAGES)); m_process->stop(); m_process->append(QStringList() << QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_SEARCH_IMAGES_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) << QCoreApplication::applicationDirPath()); m_process->start(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_SEARCH_IMAGES)); } void UbuntuDevicesModel::installEmulator() { if(m_emulatorInstalled) return; setState(InstallEmulator); setCancellable(false); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_INSTALL_EMULATOR_PACKAGE)); QString sEmulatorPackageName = QLatin1String(Ubuntu::Constants::EMULATOR_PACKAGE_NAME); m_process->stop(); m_process->append(QStringList() << QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_INSTALL_EMULATOR_PACKAGE_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH).arg(sEmulatorPackageName) << QCoreApplication::applicationDirPath()); m_process->start(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_INSTALL_EMULATOR_PACKAGE)); } void UbuntuDevicesModel::doCreateEmulatorImage(UbuntuProcess *process, const QString &name, const QString &arch, const QString &channel, const QString &passwd) { process->stop(); QString strEmulatorName = name; QString strEmulatorPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); strEmulatorPath += QDir::separator(); strEmulatorPath += QLatin1String(Constants::DEFAULT_EMULATOR_PATH); strEmulatorPath += QDir::separator(); QString strUserName = QProcessEnvironment::systemEnvironment().value(QLatin1String("USER")); QString strUserHome = QProcessEnvironment::systemEnvironment().value(QLatin1String("HOME")); process->append(QStringList() << QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_CREATE_EMULATOR_SCRIPT) .arg(QString::fromLatin1(Ubuntu::Constants::UBUNTU_SUDO_BINARY)) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(strEmulatorPath) .arg(strEmulatorName) .arg(arch) .arg(channel) .arg(passwd) .arg(strUserName) .arg(strUserHome) << QCoreApplication::applicationDirPath()); process->start(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_CREATE_EMULATOR)); } void UbuntuDevicesModel::createEmulatorImage(const QString &name, const QString &arch, const QString &channel, const QString &passwd) { setState(CreateEmulatorImage); //@BUG this should be cancellable but the QProcess::kill call just blocks for a long time, and then returns // with the process still running. Most likely because the subprocess is executed with pkexec and has // elevated priviledges setCancellable(false); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_CREATE_EMULATOR)); doCreateEmulatorImage(m_process,name,arch,channel,passwd); } void UbuntuDevicesModel::queryAdb() { setState(AdbList); setCancellable(false); m_process->stop(); bool restartAdb = true; beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTDEVICES)); m_process->append(QStringList() << QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTDEVICES_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(restartAdb ? QString::number(1) : QString::number(0)) << QCoreApplication::applicationDirPath()); m_process->start(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTDEVICES)); } void UbuntuDevicesModel::startEmulator(const QString &name) { int idx = findDevice(Core::Id::fromSetting(name).uniqueIdentifier()); if(idx < 0) return; if(m_knownDevices[idx]->device()->startEmulator()) { //trigger the device detection m_knownDevices[idx]->device()->helper()->refresh(); } } void UbuntuDevicesModel::stopEmulator(const QString &name) { int idx = findDevice(Core::Id::fromSetting(name).uniqueIdentifier()); if(idx < 0) return; QStringList args = QStringList() << name; if(QProcess::startDetached(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_STOP_EMULATOR_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) ,args ,QCoreApplication::applicationDirPath())) { } } void UbuntuDevicesModel::deleteEmulator(const QString &name) { int index = findDevice(Core::Id::fromSetting(name).uniqueIdentifier()); if(index < 0) return; QStringList args = QStringList() << name; QProcess proc; proc.setWorkingDirectory(QCoreApplication::applicationDirPath()); proc.setProcessChannelMode(QProcess::MergedChannels); proc.start(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_DELETE_EMULATOR_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH), args); proc.waitForFinished(); if(proc.exitCode() == 0) ProjectExplorer::DeviceManager::instance()->removeDevice(m_knownDevices[index]->device()->id()); else { emit logMessage(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONERROR).arg(QLatin1String(proc.readAll()))); QMessageBox::critical(Core::ICore::mainWindow(),tr("Could not delete emulator"),tr("The emulator %1 could not be deleted because of a error, check the logs for details").arg(name)); } } QVariant UbuntuDevicesModel::validateEmulatorName(const QString &name) { QString error; bool result = Utils::ProjectIntroPage::validateProjectName(name,&error); if(result) { foreach (UbuntuDevicesItem *item, m_knownDevices) { if (item->device()->machineType() == ProjectExplorer::IDevice::Emulator) { if(item->device()->imageName() == name) { result = false; error = tr("Emulator name already exists"); break; } } } } QVariantMap m; m.insert(QStringLiteral("valid"),result); m.insert(QStringLiteral("error"),error); return QVariant::fromValue(m); } void UbuntuDevicesModel::cancel() { if (m_cancellable && m_state == CreateEmulatorImage) { m_process->stop(); } } void UbuntuDevicesModel::onMessage(const QString &msg) { if (msg.startsWith(QLatin1String(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_UNABLE_TO_FETCH))) { setEmulatorInstalled(false); } m_reply.append(msg); } void UbuntuDevicesModel::processFinished(const QString &, int exitCode) { State lastState = m_state; setCancellable(false); OnScopeExit { if(m_state == lastState) setState(Idle); }; switch(lastState) { case CheckEmulatorInstalled: { QStringList lines = m_reply.trimmed().split(QLatin1String(Constants::LINEFEED)); foreach(QString line, lines) { line = line.trimmed(); if (line.isEmpty()) { continue; } if (line.startsWith(QLatin1String(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_LOCAL_NO_EMULATOR_INSTALLED))) { setEmulatorInstalled(false); queryAdb(); } else { QStringList lineData = line.split(QLatin1String(Constants::SPACE)); QString sEmulatorPackageStatus = lineData.takeFirst(); //QString sEmulatorPackageName = lineData.takeFirst(); //QString sEmulatorPackageVersion = lineData.takeFirst(); if (sEmulatorPackageStatus.startsWith(QLatin1String(Constants::INSTALLED))) { setEmulatorInstalled(true); findEmulatorImages(); } } } break; } case InstallEmulator: { QStringList lines = m_reply.trimmed().split(QLatin1String(Constants::LINEFEED)); foreach(QString line, lines) { line = line.trimmed(); if (line.isEmpty()) { continue; } if (line.startsWith(QLatin1String(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_LOCAL_NO_EMULATOR_INSTALLED))) { setEmulatorInstalled(false); queryAdb(); break; } else { QStringList lineData = line.split(QLatin1String(Constants::SPACE)); QString sEmulatorPackageStatus = lineData.takeFirst(); //QString sEmulatorPackageName = lineData.takeFirst(); //QString sEmulatorPackageVersion = lineData.takeFirst(); if (sEmulatorPackageStatus.startsWith(QLatin1String(Constants::INSTALLED))) { setEmulatorInstalled(true); findEmulatorImages(); break; } } } break; } case CreateEmulatorImage: { findEmulatorImages(); break; } case FindImages: { QStringList lines = m_reply.trimmed().split(QLatin1String(Constants::LINEFEED)); QSet notFoundImages; foreach(UbuntuDevicesItem *item, m_knownDevices) { if(item->device()->machineType() == ProjectExplorer::IDevice::Emulator) notFoundImages.insert(item->device()->id().uniqueIdentifier()); } QMutableStringListIterator iter(lines); QRegularExpression regexName (QStringLiteral("^(\\S+)")); QRegularExpression regexUbuntu (QStringLiteral("ubuntu=([0-9]+)")); QRegularExpression regexDevice (QStringLiteral("device=([0-9]+)")); QRegularExpression regexVersion(QStringLiteral("version=([0-9]+)")); QRegularExpression regexArch (QStringLiteral("arch=(\\w+)")); while (iter.hasNext()) { QString line = iter.next(); if(line.isEmpty()) { iter.remove(); continue; } if(debug) qDebug()<<"Handling emulator: "<= 0) { notFoundImages.remove(devId.uniqueIdentifier()); dev = m_knownDevices[index]->device(); } else { dev = Ubuntu::Internal::UbuntuDevice::create( deviceSerial, deviceSerial, ProjectExplorer::IDevice::Emulator, archType, ProjectExplorer::IDevice::AutoDetected); addToManager = true; } if(dev) { dev->setEmulatorInfo(ubuntuVer,deviceVer,imageVer); if(addToManager) ProjectExplorer::DeviceManager::instance()->addDevice(dev); } } //remove all ubuntu emulators that are in the settings but don't exist in the system foreach(int curr,notFoundImages) { ProjectExplorer::DeviceManager::instance()->removeDevice(Core::Id::fromUniqueIdentifier(curr)); } queryAdb(); break; } case AdbList: { foreach(UbuntuDevicesItem* item, m_knownDevices) item->device()->helper()->refresh(); if(exitCode != 0) { return; } QStringList lines = m_reply.trimmed().split(QLatin1String(Constants::LINEFEED)); foreach(QString line, lines) { line = line.trimmed(); if (line.isEmpty()) { continue; } QRegularExpression exp(QLatin1String(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_ADB_REGEX)); QRegularExpressionMatch match = exp.match(line); if(match.hasMatch()) { QStringList lineData = match.capturedTexts(); //The first entry is always the full match //which means in our case its the complete string lineData.takeFirst(); if (lineData.count() == 2) { QString sSerialNumber = lineData.takeFirst(); QString sDeviceInfo = lineData.takeFirst(); QRegularExpression archExp(QStringLiteral("arch:([\\w]+)")); QRegularExpressionMatch archMatch = archExp.match(sDeviceInfo); QString arch = QStringLiteral("armhf"); if(archMatch.hasMatch()) { arch = archMatch.captured(1); } else { qDebug()<<"Could not get the architecture from: "< allkits = ProjectExplorer::KitManager::kits(); foreach (ProjectExplorer::Kit* k, allkits) { if( ProjectExplorer::DeviceKitInformation::deviceId(k) == device->id() ) m_myKits.insert(k->id()); } connect(ProjectExplorer::KitManager::instance(),SIGNAL(kitAdded(ProjectExplorer::Kit*)), this,SLOT(onKitAdded(ProjectExplorer::Kit*))); connect(ProjectExplorer::KitManager::instance(),SIGNAL(kitRemoved(ProjectExplorer::Kit*)), this,SLOT(onKitRemoved(ProjectExplorer::Kit*))); connect(ProjectExplorer::KitManager::instance(),SIGNAL(kitUpdated(ProjectExplorer::Kit*)), this,SLOT(onKitUpdated(ProjectExplorer::Kit*))); connect(device->helper(),SIGNAL(detectionStateChanged()),this,SLOT(deviceStateChanged())); connect(device->helper(),SIGNAL(featureDetected()),this,SIGNAL(featureDetected())); connect(device->helper(),SIGNAL(deviceInfoUpdated()),this,SIGNAL(deviceInfoUpdated())); connect(device->helper(),SIGNAL(message(QString)),this,SIGNAL(logUpdated())); } /*! * \brief UbuntuDevicesItem::id * Returns the internal device ID */ Core::Id UbuntuDevicesItem::id() const { return m_device->id(); } UbuntuDevice::Ptr UbuntuDevicesItem::device() const { return m_device; } QVariantList UbuntuDevicesItem::kits() const { QVariantList kits; foreach (const Core::Id &id, m_myKits) { ProjectExplorer::Kit* k = ProjectExplorer::KitManager::find(id); if(!k) continue; if(debug) qDebug()<<"Adding "<displayName(); QVariantMap m; m.insert(QStringLiteral("displayName"),k->displayName()); m.insert(QStringLiteral("id"),k->id().toSetting()); kits.append(QVariant::fromValue(m)); } return kits; } /*! * \brief UbuntuDevicesItem::onKitAdded * Checks if the new Kit that just got added to the KitManager contains * the device, if it does the Kit is added to the internal Kit list */ void UbuntuDevicesItem::onKitAdded(ProjectExplorer::Kit *k) { if( ProjectExplorer::DeviceKitInformation::deviceId(k) == m_device->id() ) { if(!m_myKits.contains(k->id())){ m_myKits.insert(k->id()); emit kitsChanged(); } } } /*! * \brief UbuntuDevicesItem::onKitRemoved * Checks if the removed Kit \a k is in the internal Kit list * and removes it */ void UbuntuDevicesItem::onKitRemoved(ProjectExplorer::Kit *k) { if(m_myKits.contains(k->id())) { m_myKits.remove(k->id()); emit kitsChanged(); } } /*! * \brief UbuntuDevicesItem::onKitUpdated * Checks if the updated Kit now contains the internal device * or if it does NOT contain the device anymore */ void UbuntuDevicesItem::onKitUpdated(ProjectExplorer::Kit *k) { if( ProjectExplorer::DeviceKitInformation::deviceId(k) == m_device->id() ) { onKitAdded(k); } else { onKitRemoved(k); } } void UbuntuDevicesItem::deviceStateChanged() { emit detectionStateChanged(); } } } ./src/ubuntu/ubuntuvalidationresultmodel.h0000644000015600001650000000651712705421114021242 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUVALIDATIONRESULTMODEL_H #define UBUNTU_INTERNAL_UBUNTUVALIDATIONRESULTMODEL_H #include #include #include #include namespace Ubuntu { namespace Internal { class ClickRunChecksParser : public QObject { Q_OBJECT public: enum ItemIcon { Error, Warning, Check, NoIcon }; struct DataItem { DataItem(); ~DataItem(); DataItem* parent; QString type; QString text; QUrl link; ItemIcon icon; QList children; }; ClickRunChecksParser(QObject* parent = 0); void beginRecieveData(const QString& data = QString()); void addRecievedData (const QString& data); void endRecieveData (const QString& data = QString()); void emitTextItem (const QString &type, const QString &text, const ItemIcon icon = NoIcon); protected: bool tryParseNextSection ( bool dataComplete = false); void emitParseErrorItem (const QString &text); void parseJsonSection (const QString §ionName, int offset, int length = -1); signals: void parsedNewTopLevelItem (DataItem* item); void begin (); void finished (); private: QString m_data; int m_nextSectionOffset; int m_errorCount; int m_warnCount; }; class UbuntuValidationResultModel : public QAbstractItemModel { Q_OBJECT public: enum Roles { TypeRole = Qt::UserRole+1, LinkRole, DescriptionRole, ImageRole, ShouldExpandRole }; UbuntuValidationResultModel(QObject *parent = 0); ~UbuntuValidationResultModel(); // QAbstractItemModel interface virtual QModelIndex index(int row, int column, const QModelIndex &parent) const; virtual QModelIndex parent(const QModelIndex &child) const; virtual int rowCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual QHash roleNames() const; QModelIndex findFirstErrorItem () const; public slots: void appendItem ( ClickRunChecksParser::DataItem* item ); void clear (); private: ClickRunChecksParser::DataItem *getItem(const QModelIndex &index) const; private: ClickRunChecksParser::DataItem* m_rootItem; QString m_data; int m_nextSectionOffset; }; } // namespace Internal } // namespace Ubuntu Q_DECLARE_METATYPE(Ubuntu::Internal::ClickRunChecksParser::DataItem*) #endif // UBUNTU_INTERNAL_UBUNTUVALIDATIONRESULTMODEL_H ./src/ubuntu/ubuntushared.cpp0000644000015600001650000000247012705421114016423 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntushared.h" #include #include #include bool readFile(const QString &fileName, QByteArray *data, QString *errorMessage) { Utils::FileReader reader; if (!reader.fetch(fileName, errorMessage)) return false; *data = reader.data(); return true; } void printToOutputPane(const QString &msg) { QString timestamp = QDateTime::currentDateTime().toString(QString::fromLatin1("HH:mm:ss")); Core::MessageManager::write(QString(QLatin1String("[%0] %1")).arg(timestamp).arg(msg),Core::MessageManager::NoModeSwitch); } ./src/ubuntu/ubuntueditorfactory.cpp0000644000015600001650000000322312705421114020030 0ustar jenkinsjenkins#include "ubuntueditorfactory.h" #include "ubuntumanifesteditor.h" #include "ubuntuconstants.h" #include "ubuntuapparmoreditor.h" #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuTextEditorActionHandler : public TextEditor::TextEditorActionHandler { public: explicit UbuntuTextEditorActionHandler(QObject *parent, Core::Id editorContext) : TextEditorActionHandler(parent,editorContext) {} private: TextEditor::TextEditorWidget *resolveTextEditorWidget(Core::IEditor *editor) const { UbuntuAbstractGuiEditor *ubuntuManifestEditor = static_cast(editor); return ubuntuManifestEditor->textEditor(); } }; UbuntuManifestEditorFactory::UbuntuManifestEditorFactory() { setId(Constants::UBUNTU_MANIFEST_EDITOR_ID); setDisplayName(tr("Ubuntu Manifest editor")); addMimeType(Constants::UBUNTU_MANIFEST_MIME_TYPE); new UbuntuTextEditorActionHandler(this,Constants::UBUNTU_MANIFEST_EDITOR_ID); } Core::IEditor *UbuntuManifestEditorFactory::createEditor() { return new UbuntuManifestEditor(); } UbuntuApparmorEditorFactory::UbuntuApparmorEditorFactory() { setId(Constants::UBUNTU_APPARMOR_EDITOR_ID); setDisplayName(tr("Ubuntu Apparmor editor")); addMimeType(Constants::UBUNTU_APPARMOR_MIME_TYPE); new UbuntuTextEditorActionHandler(this,Constants::UBUNTU_APPARMOR_EDITOR_ID); } Core::IEditor *UbuntuApparmorEditorFactory::createEditor() { return new UbuntuApparmorEditor(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntupackagingmodel.cpp0000644000015600001650000005661512705421114020134 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntupackagingmodel.h" #include "ubuntuconstants.h" #include "ubuntumenu.h" #include "ubuntuclicktool.h" #include "ubuntuvalidationresultmodel.h" #include "ubuntudevice.h" #include "ubuntupackagestep.h" #include "ubuntushared.h" #include "ubuntucmakecache.h" #include "ubuntuprojecthelper.h" #include "ubuntufixmanifeststep.h" #include "wizards/ubuntufatpackagingwizard.h" #include "clicktoolchain.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 namespace Ubuntu { namespace Internal { enum { debug = 0 }; UbuntuPackagingModel::UbuntuPackagingModel(QObject *parent) : QObject(parent), m_postPackageTask(Verify) { setShowValidationUi(false); m_inputParser = new ClickRunChecksParser(this); m_validationModel = new UbuntuValidationResultModel(this); connect(m_inputParser,&ClickRunChecksParser::parsedNewTopLevelItem ,m_validationModel,&UbuntuValidationResultModel::appendItem); connect(m_inputParser,SIGNAL(begin()),this,SIGNAL(beginValidation())); connect(&m_ubuntuProcess,SIGNAL(started(QString)),this,SLOT(onStarted(QString))); connect(&m_ubuntuProcess,SIGNAL(message(QString)),this,SLOT(onMessage(QString))); connect(&m_ubuntuProcess,SIGNAL(finished(QString,int)),this,SLOT(onFinished(QString, int))); connect(&m_ubuntuProcess,SIGNAL(error(QString)),this,SLOT(onError(QString))); connect(UbuntuMenu::instance(),SIGNAL(requestBuildAndInstallProject()),this,SLOT(buildAndInstallPackageRequested())); connect(UbuntuMenu::instance(),SIGNAL(requestBuildAndVerifyProject()),this,SLOT(buildAndVerifyPackageRequested())); connect(UbuntuMenu::instance(),SIGNAL(requestBuildProject()),this,SLOT(buildPackageRequested())); //connect(ui->pushButtonCreateAndInstall,SIGNAL(clicked()),this,SLOT(buildAndInstallPackageRequested())); connect(ProjectExplorer::ProjectExplorerPlugin::instance(),SIGNAL(updateRunActions()),this,SLOT(targetChanged())); m_reviewToolsInstalled = false; checkClickReviewerTool(); } UbuntuPackagingModel::~UbuntuPackagingModel() { clearPackageBuildList(); } void UbuntuPackagingModel::onFinishedAction(const QProcess *proc, QString cmd) { Q_UNUSED(proc); Q_UNUSED(cmd); disconnect(m_UbuntuMenu_connection); if (!m_reviewToolsInstalled) return; ProjectExplorer::Project* startupProject = ProjectExplorer::SessionManager::startupProject(); //first try to use the version in the deploy directory. It is always in the root of the click package QString manifestPath; if(startupProject->activeTarget() && startupProject->activeTarget()->activeBuildConfiguration()) { manifestPath = startupProject->activeTarget()->activeBuildConfiguration()->buildDirectory() .appendPath(QLatin1String(Constants::UBUNTU_DEPLOY_DESTDIR)) .appendPath(QStringLiteral("manifest.json")) .toString(); } if(!QFile::exists(manifestPath)) { //fall back to the project directory manifestPath = UbuntuProjectHelper::getManifestPath(startupProject->activeTarget(), startupProject->projectDirectory() .appendPath(QStringLiteral("manifest.json")).toString()); } UbuntuClickManifest manifest; if(!manifest.load(manifestPath)) return; QString sClickPackageName; QString sClickPackagePath; sClickPackageName = QString::fromLatin1("%0_%1_all.click").arg(manifest.name()).arg(manifest.version()); sClickPackagePath = startupProject->projectDirectory().toString(); QRegularExpression re(QLatin1String("\\/\\w+$")); // search for the project name in the path QRegularExpressionMatch match = re.match(sClickPackagePath); if (match.hasMatch()) { QString matched = match.captured(0); sClickPackagePath.chop(matched.length()-1); //leave the slash } sClickPackagePath.append(sClickPackageName); m_ubuntuProcess.stop(); if (sClickPackagePath.isEmpty()) return; if (m_reviewToolsInstalled) { if(debug) qDebug()<<"Going to verify: "<addRecievedData(msg); } void UbuntuPackagingModel::onFinished(QString cmd, int code) { m_inputParser->endRecieveData(); if(code == 0) appendLog(tr("Command finished successfully\n")); else appendLog(tr("Command failed\n")); if (cmd == QString::fromLatin1(Constants::UBUNTUWIDGETS_ONFINISHED_SCRIPT_LOCAL_PACKAGE_INSTALLED).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH)) { resetValidationResult(); QStringList lines = m_reply.trimmed().split(QLatin1String(Constants::LINEFEED)); foreach(QString line, lines) { line = line.trimmed(); if (line.isEmpty()) { continue; } if (line.startsWith(QLatin1String(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_LOCAL_NO_EMULATOR_INSTALLED))) { // There is no click reviewer tool installed m_reviewToolsInstalled = false; setShowValidationUi(false); emit reviewToolsInstalledChanged(m_reviewToolsInstalled); } else { QStringList lineData = line.split(QLatin1String(Constants::SPACE)); QString sReviewerToolPackageStatus = lineData.takeFirst(); //QString sReviewerToolPackageName = lineData.takeFirst(); //QString sReviewerToolPackageVersion = lineData.takeFirst(); if (sReviewerToolPackageStatus.startsWith(QLatin1String(Constants::INSTALLED))) { // There is click reviewer tool installed setShowValidationUi(true); m_reviewToolsInstalled = true; emit reviewToolsInstalledChanged(m_reviewToolsInstalled); } } } } m_reply.clear(); } void UbuntuPackagingModel::onError(QString msg) { if(msg.isEmpty()) return; appendLog(tr("Error: %1\n").arg(msg)); } void UbuntuPackagingModel::onStarted(QString ) { resetValidationResult(); setLog(tr("Start Command\n")); } bool UbuntuPackagingModel::reviewToolsInstalled() { return m_reviewToolsInstalled; } bool UbuntuPackagingModel::showValidationUi() const { return m_showValidationUi; } bool UbuntuPackagingModel::canBuild() const { return m_canBuild; } QAbstractItemModel *UbuntuPackagingModel::validationModel() const { return m_validationModel; } QString UbuntuPackagingModel::log() const { return m_log; } void UbuntuPackagingModel::setShowValidationUi(bool arg) { if (m_showValidationUi != arg) { m_showValidationUi = arg; emit showValidationUiChanged(arg); } } void UbuntuPackagingModel::setCanBuild(bool arg) { if (m_canBuild != arg) { m_canBuild = arg; emit canBuildChanged(arg); } } void UbuntuPackagingModel::on_pushButtonReviewersTools_clicked() { ProjectExplorer::Project* startupProject = ProjectExplorer::SessionManager::startupProject(); m_ubuntuProcess.stop(); QString directory = QDir::homePath(); if(startupProject) directory = startupProject->projectDirectory().toString(); QString clickPackage = QFileDialog::getOpenFileName(Core::ICore::mainWindow(),QString(QLatin1String(Constants::UBUNTU_CLICK_PACKAGE_SELECTOR_TEXT)),QString(QLatin1String("%0/..")).arg(directory),QLatin1String(Constants::UBUNTU_CLICK_PACKAGE_MASK)); if (clickPackage.isEmpty()) return; m_inputParser->beginRecieveData(); m_ubuntuProcess.append(QStringList() << QString::fromLatin1(Constants::CLICK_REVIEWERSTOOLS_LOCATION).arg(clickPackage)); m_ubuntuProcess.start(QString(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_CLICK_REVIEWER_TOOLS_AGAINST_PACKAGE)).arg(clickPackage)); } void UbuntuPackagingModel::setLog(QString arg) { if (m_log != arg) { m_log = arg; emit logChanged(arg); } } void UbuntuPackagingModel::appendLog(QString arg) { m_log.append(arg); emit logChanged(m_log); } void UbuntuPackagingModel::on_pushButtonClickPackage_clicked() { ProjectExplorer::Project* project = ProjectExplorer::SessionManager::startupProject(); if(!project) return; QString mimeType = project->projectManager()->mimeType(); if(mimeType == QLatin1String(CMakeProjectManager::Constants::CMAKEPROJECTMIMETYPE) || mimeType == QLatin1String(Ubuntu::Constants::UBUNTUPROJECT_MIMETYPE) || mimeType == QLatin1String(QmlProjectManager::Constants::QMLPROJECT_MIMETYPE) || mimeType == QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE)) { if(m_reviewToolsInstalled) m_postPackageTask = Verify; else m_postPackageTask = None; buildClickPackage(); } else { m_UbuntuMenu_connection = QObject::connect(UbuntuMenu::instance(),SIGNAL(finished_action(const QProcess*,QString)),this,SLOT(onFinishedAction(const QProcess*,QString))); if(!m_UbuntuMenu_connection) qWarning()<<"Could not connect signals"; QAction* action = UbuntuMenu::menuAction(Core::Id(Constants::UBUNTUPACKAGINGWIDGET_BUILDPACKAGE_ID)); if(action) { action->trigger(); } } } void UbuntuPackagingModel::checkClickReviewerTool() { m_ubuntuProcess.stop(); QString sReviewerPackageName = QLatin1String(Ubuntu::Constants::REVIEWER_PACKAGE_NAME); m_ubuntuProcess.append(QStringList() << QString::fromLatin1(Constants::UBUNTUWIDGETS_LOCAL_PACKAGE_INSTALLED_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH).arg(sReviewerPackageName) << QApplication::applicationDirPath()); m_ubuntuProcess.start(QString::fromLatin1(Constants::UBUNTUPACKAGINGWIDGET_LOCAL_REVIEWER_INSTALLED)); } void UbuntuPackagingModel::buildFinished(const bool success) { disconnect(m_buildManagerConnection); if (success) { //the step that created the click package is always in the last list and always the last step UbuntuPackageStep *pckStep = qobject_cast(m_packageBuildSteps.last()->steps().last()); if (pckStep && !pckStep->packagePath().isEmpty()) { m_ubuntuProcess.stop(); QString sClickPackagePath = pckStep->packagePath(); if (sClickPackagePath.isEmpty()) { clearPackageBuildList(); return; } switch (m_postPackageTask) { case None: break; case Verify: { if(debug) qDebug()<<"Going to verify: "<beginRecieveData(); m_ubuntuProcess.append(QStringList() << QString::fromLatin1(Constants::CLICK_REVIEWERSTOOLS_LOCATION).arg(sClickPackagePath)); m_ubuntuProcess.start(QString(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_CLICK_REVIEWER_TOOLS_AGAINST_PACKAGE)).arg(sClickPackagePath)); break; } case Install: { ProjectExplorer::IDevice::ConstPtr dev = ProjectExplorer::DeviceKitInformation::device(pckStep->target()->kit()); if (!dev) break; //fall through to clear buildsteps if (!dev->type().toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID))) break; //fall through to clear buildsteps UbuntuDevice::ConstPtr ubuntuDev = qSharedPointerCast(dev); QSsh::SshConnectionParameters connParams = ubuntuDev->sshParameters(); m_ubuntuProcess.append(QStringList() << QString::fromLatin1(Constants::UBUNTUPACKAGINGWIDGET_CLICK_DEPLOY_SCRIPT) .arg(Constants::UBUNTU_SCRIPTPATH) .arg(ubuntuDev->serialNumber()) .arg(sClickPackagePath) .arg(QStringLiteral("%1@%2").arg(connParams.userName).arg(connParams.host)) .arg(connParams.port) .arg(QStringLiteral("/home/%1/dev_tmp").arg(connParams.userName)) .arg(connParams.userName)); m_ubuntuProcess.start(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_CLICK_DEPLOY_MESSAGE)); break; } } } } clearPackageBuildList(); } void UbuntuPackagingModel::buildAndInstallPackageRequested() { m_postPackageTask = Install; buildClickPackage(); } void UbuntuPackagingModel::buildAndVerifyPackageRequested() { m_postPackageTask = Verify; buildClickPackage(); } void UbuntuPackagingModel::buildPackageRequested() { m_postPackageTask = None; buildClickPackage(); } void UbuntuPackagingModel::targetChanged() { ProjectExplorer::Project *p = ProjectExplorer::SessionManager::startupProject(); bool buildButtonsEnabled = p && p->activeTarget() && p->activeTarget()->kit() && ProjectExplorer::ToolChainKitInformation::toolChain(p->activeTarget()->kit()) && (ProjectExplorer::ToolChainKitInformation::toolChain(p->activeTarget()->kit())->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID) || p->projectManager()->mimeType() == QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE)); setCanBuild(buildButtonsEnabled); } /*! * \brief UbuntuPackagingWidget::buildClickPackage * Starts the build of a cmake project. Make sure to set * m_postPackageTask correctly before calling this function */ void UbuntuPackagingModel::buildClickPackage() { ProjectExplorer::Project* project = ProjectExplorer::SessionManager::startupProject(); if(!project) { QMessageBox::warning(Core::ICore::mainWindow(),tr("No Project"),tr("No valid project loaded.")); return; } if(ProjectExplorer::BuildManager::isBuilding()) { QMessageBox::information(Core::ICore::mainWindow(),tr("Build running"),tr("There is currently a build running, please wait for it to be finished")); return; } if(!ProjectExplorer::ProjectExplorerPlugin::instance()->saveModifiedFiles()) return; QString mimeType = project->projectManager()->mimeType(); bool isCMake = mimeType == QLatin1String(CMakeProjectManager::Constants::CMAKEPROJECTMIMETYPE); bool isHtml = mimeType == QLatin1String(Ubuntu::Constants::UBUNTUPROJECT_MIMETYPE); bool isQml = mimeType == QLatin1String(QmlProjectManager::Constants::QMLPROJECT_MIMETYPE); bool isQmake = mimeType == QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE); if (isQmake) { QmakeProjectManager::QmakeProject *qmakePro = static_cast(project); UbuntuFatPackagingWizard wiz(qmakePro); if (wiz.exec() != QDialog::Accepted) return; UbuntuFatPackagingWizard::BuildMode mode = wiz.mode(); QString workingDir = wiz.field(QStringLiteral("targetDirectory")).toString(); QString deployDir = QStringLiteral("%1/deploy").arg(workingDir); QList suspects; if(mode == UbuntuFatPackagingWizard::CompiledPartsMode) { suspects = wiz.selectedTargets(); } else { if(project->activeTarget()) { suspects.append(project->activeTarget()->activeBuildConfiguration()); } } if (!suspects.isEmpty()) { QStringList usedArchitectures; clearPackageBuildList(); //@TODO check if different frameworks have been used bool firstStep=true; foreach (ProjectExplorer::BuildConfiguration *b, suspects) { m_packageBuildSteps.append(QSharedPointer (new ProjectExplorer::BuildStepList(b,ProjectExplorer::Constants::BUILDSTEPS_BUILD))); ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(b->target()->kit()); if(tc && tc->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)){ ClickToolChain *cTc = static_cast(tc); usedArchitectures << cTc->clickTarget().architecture; } //add the normal buildsteps m_packageBuildSteps.last()->cloneSteps(b->stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD))); //append the make install step UbuntuPackageStep* package = new UbuntuPackageStep(m_packageBuildSteps.last().data()); package->setDebugMode(UbuntuPackageStep::DisableDebugScript); package->setOverrideDeployDir(deployDir); package->setPackageMode(UbuntuPackageStep::OnlyMakeInstall); package->setReferenceBuildConfig(b); package->setCleanDeployDirectory(firstStep); m_packageBuildSteps.last()->appendStep(package); firstStep=false; } UbuntuFixManifestStep *fixManifest = new UbuntuFixManifestStep(m_packageBuildSteps.last().data()); if (mode == UbuntuFatPackagingWizard::NoCompiledPartsMode) fixManifest->setArchitectures(QStringList()<setArchitectures(usedArchitectures); fixManifest->setPackageDir(deployDir); m_packageBuildSteps.last()->appendStep(fixManifest); //append the click package step UbuntuPackageStep* package = new UbuntuPackageStep(m_packageBuildSteps.last().data()); package->setDebugMode(UbuntuPackageStep::DisableDebugScript); package->setOverrideDeployDir(deployDir); package->setOverrideClickWorkingDir(workingDir); package->setPackageMode(UbuntuPackageStep::OnlyClickBuild); m_packageBuildSteps.last()->appendStep(package); m_buildManagerConnection = connect(ProjectExplorer::BuildManager::instance(),SIGNAL(buildQueueFinished(bool)),this,SLOT(buildFinished(bool))); QList rawSteps; QStringList rawStepMessages; foreach (QSharedPointer l, m_packageBuildSteps) { rawSteps << l.data(); rawStepMessages << tr("Build %1").arg(l->target()->displayName()); } ProjectExplorer::BuildManager::buildLists(rawSteps,rawStepMessages); } } else if(isCMake || isHtml || isQml) { ProjectExplorer::Target* target = project->activeTarget(); if(!target) return; ProjectExplorer::Kit* k = target->kit(); if(!k) return; if(!ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(k).toString().startsWith(QLatin1String(Ubuntu::Constants::UBUNTU_DEVICE_TYPE_ID))) { QMessageBox::warning(Core::ICore::mainWindow(),tr("Wrong kit type"),tr("It is not supported to create click packages for a non UbuntuSDK target")); return; } ProjectExplorer::BuildConfiguration* bc = target->activeBuildConfiguration(); if(!bc) { QMessageBox::information(Core::ICore::mainWindow(),tr("Error"),tr("Please add a valid buildconfiguration to your project")); return; } if(!bc->isEnabled()) { QString disabledReason = bc->disabledReason(); QMessageBox::information(Core::ICore::mainWindow(),tr("Disabled"),tr("The currently selected Buildconfiguration is disabled. %1").arg(disabledReason)); return; } clearPackageBuildList(); m_packageBuildSteps.append(QSharedPointer (new ProjectExplorer::BuildStepList(bc,ProjectExplorer::Constants::BUILDSTEPS_BUILD))); ProjectExplorer::BuildStepList *buildSteps = bc->stepList(Core::Id(ProjectExplorer::Constants::BUILDSTEPS_BUILD)); if (buildSteps && buildSteps->count() > 0) { //add the normal buildsteps m_packageBuildSteps.last()->cloneSteps(buildSteps); } //append the click packaging step UbuntuPackageStep* package = new UbuntuPackageStep(m_packageBuildSteps.last().data()); package->setDebugMode(UbuntuPackageStep::DisableDebugScript); m_packageBuildSteps.last()->appendStep(package); m_buildManagerConnection = connect(ProjectExplorer::BuildManager::instance(),SIGNAL(buildQueueFinished(bool)),this,SLOT(buildFinished(bool))); ProjectExplorer::BuildManager::buildList(m_packageBuildSteps.last().data(),tr("Build Project")); } } /*! * \brief UbuntuPackagingWidget::clearAdditionalBuildSteps * Clears the last used additional buildsteps * \note This will cancel a current build if its building the ProjectConfiguration * the BuildSteps belong to! */ void UbuntuPackagingModel::clearPackageBuildList() { if (m_packageBuildSteps.isEmpty()) return; foreach(QSharedPointer list, m_packageBuildSteps) { if(ProjectExplorer::BuildManager::isBuilding( static_cast(list->parent()))) ProjectExplorer::BuildManager::cancel(); list->deleteLater(); list.clear(); } m_packageBuildSteps.clear(); } void UbuntuPackagingModel::resetValidationResult() { m_validationModel->clear(); } }} ./src/ubuntu/ubuntuqmlbuildconfiguration.cpp0000644000015600001650000003333312705421114021560 0ustar jenkinsjenkins#include "ubuntuqmlbuildconfiguration.h" #include "ubuntuconstants.h" #include "ubuntuproject.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuQmlBuildConfiguration * * Even though QML projects don't need to be built, we need * that BuildConfiguration to store the Builddirectory (where the click * package goes) and to enable the Target page to show us some actual * targets as well as to build the translations for click packaging */ UbuntuQmlBuildConfiguration::UbuntuQmlBuildConfiguration(ProjectExplorer::Target *target) : BuildConfiguration(target,Constants::UBUNTU_CLICK_QML_BC_ID) { } UbuntuQmlBuildConfiguration::UbuntuQmlBuildConfiguration(ProjectExplorer::Target *target, UbuntuQmlBuildConfiguration *source) : BuildConfiguration(target,source) { } bool UbuntuQmlBuildConfiguration::fromMap(const QVariantMap &map) { return BuildConfiguration::fromMap(map); } QVariantMap UbuntuQmlBuildConfiguration::toMap() const { return BuildConfiguration::toMap(); } ProjectExplorer::NamedWidget *UbuntuQmlBuildConfiguration::createConfigWidget() { return new UbuntuQmlBuildSettingsWidget(this); } ProjectExplorer::BuildConfiguration::BuildType UbuntuQmlBuildConfiguration::buildType() const { return Release; } UbuntuQmlBuildConfigurationFactory::UbuntuQmlBuildConfigurationFactory(QObject *parent) : IBuildConfigurationFactory(parent) { } UbuntuQmlBuildConfigurationFactory::~UbuntuQmlBuildConfigurationFactory() { } int UbuntuQmlBuildConfigurationFactory::priority(const ProjectExplorer::Target *parent) const { if (canHandle(parent)) return 100; return -1; } QList UbuntuQmlBuildConfigurationFactory::availableBuilds(const ProjectExplorer::Target *parent) const { if(!canHandle(parent)) return QList(); return createBuildInfos(parent->kit(),parent->project()->projectFilePath().toString()); } int UbuntuQmlBuildConfigurationFactory::priority(const ProjectExplorer::Kit *k, const QString &projectPath) const { return (k && Utils::MimeDatabase().mimeTypeForFile(projectPath) .matchesName(QLatin1String(QmlProjectManager::Constants::QMLPROJECT_MIMETYPE))) ? 100 : -1; } QList UbuntuQmlBuildConfigurationFactory::availableSetups(const ProjectExplorer::Kit *k, const QString &projectPath) const { UbuntuKitMatcher m; if(priority(k,projectPath) < 0 || !m.matches(k)) return QList(); return createBuildInfos(k,projectPath); } UbuntuQmlBuildConfiguration *UbuntuQmlBuildConfigurationFactory::create(ProjectExplorer::Target *parent, const ProjectExplorer::BuildInfo *info) const { QTC_ASSERT(info->factory() == this, return 0); QTC_ASSERT(info->kitId == parent->kit()->id(), return 0); QTC_ASSERT(!info->displayName.isEmpty(), return 0); UbuntuQmlBuildConfiguration *conf = new UbuntuQmlBuildConfiguration(parent); conf->setBuildDirectory(info->buildDirectory); conf->setDefaultDisplayName(info->displayName); conf->setDisplayName(info->displayName); //is this a ubuntu project? Utils::FileName manifestFilePath = parent->project()->projectDirectory().appendPath(QStringLiteral("manifest.json")); Utils::FileName makeFilePath = parent->project()->projectDirectory().appendPath(QStringLiteral("Makefile")); if(manifestFilePath.toFileInfo().exists() && makeFilePath.toFileInfo().exists()) { //make sure the "build-translations" target is available in the Makefile QFile makefile(makeFilePath.toString()); if(makefile.open(QIODevice::ReadOnly)) { QByteArray data = makefile.readAll(); if(data.contains("build-translations:")) { ProjectExplorer::BuildStepList *bs = conf->stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD); bs->insertStep(0, new UbuntuQmlUpdateTranslationTemplateStep(bs)); bs->insertStep(1, new UbuntuQmlBuildTranslationStep(bs)); } } } return conf; } bool UbuntuQmlBuildConfigurationFactory::canRestore(const ProjectExplorer::Target *parent, const QVariantMap &map) const { if (!canHandle(parent)) return false; return ProjectExplorer::idFromMap(map) == Constants::UBUNTU_CLICK_QML_BC_ID; } UbuntuQmlBuildConfiguration *UbuntuQmlBuildConfigurationFactory::restore(ProjectExplorer::Target *parent, const QVariantMap &map) { if (!canRestore(parent,map) ) return 0; UbuntuQmlBuildConfiguration *conf = new UbuntuQmlBuildConfiguration(parent); if (conf->fromMap(map)) return conf; delete conf; return 0; } bool UbuntuQmlBuildConfigurationFactory::canClone(const ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) const { if (!canHandle(parent)) return false; if (product->id() != Constants::UBUNTU_CLICK_QML_BC_ID ) return false; return true; } UbuntuQmlBuildConfiguration *UbuntuQmlBuildConfigurationFactory::clone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) { if (!canClone(parent,product)) return 0; return new UbuntuQmlBuildConfiguration(parent,static_cast(product)); } bool UbuntuQmlBuildConfigurationFactory::canHandle(const ProjectExplorer::Target *t) const { UbuntuKitMatcher m; if(!m.matches(t->kit())) return false; if (t->project()->id() != "QmlProjectManager.QmlProject") return false; return true; } QList UbuntuQmlBuildConfigurationFactory::createBuildInfos(const ProjectExplorer::Kit *k, const QString &projectDir) const { QList builds; ProjectExplorer::BuildInfo *info = new ProjectExplorer::BuildInfo(this); info->buildDirectory = Utils::FileName::fromString(UbuntuProject::shadowBuildDirectory(projectDir,k,QStringLiteral("default"))); info->typeName = tr("Qml"); info->kitId = k->id(); info->displayName = tr("Default"); builds << info; return builds; } UbuntuQmlBuildSettingsWidget::UbuntuQmlBuildSettingsWidget(UbuntuQmlBuildConfiguration *conf, QWidget *parent) : NamedWidget(parent) , m_buildConfiguration(conf) { QFormLayout *fl = new QFormLayout(this); fl->setContentsMargins(20, -1, 0, -1); fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); setLayout(fl); m_pathChooser = new Utils::PathChooser(this); m_pathChooser->setPath(m_buildConfiguration->rawBuildDirectory().toString()); fl->addRow(tr("Build directory:"), m_pathChooser); connect(m_pathChooser->lineEdit(),SIGNAL(editingFinished()),this,SLOT(onBuilddirChanged())); connect(m_buildConfiguration,SIGNAL(buildDirectoryChanged()),this,SLOT(updateBuildDirectory())); } void UbuntuQmlBuildSettingsWidget::updateBuildDirectory() const { m_pathChooser->blockSignals(true); m_pathChooser->setPath(m_buildConfiguration->rawBuildDirectory().toString()); m_pathChooser->blockSignals(false); } UbuntuQmlUpdateTranslationTemplateStep::UbuntuQmlUpdateTranslationTemplateStep(ProjectExplorer::BuildStepList *bsl) : UbuntuQmlUpdateTranslationTemplateStep(bsl,Constants::UBUNTU_CLICK_QML_UPDATE_TRANSL_MAKESTEP) { setDefaultDisplayName(tr("Update translations template")); } UbuntuQmlUpdateTranslationTemplateStep::UbuntuQmlUpdateTranslationTemplateStep(ProjectExplorer::BuildStepList *bsl,Core::Id typeId) : AbstractProcessStep(bsl,typeId) {} UbuntuQmlUpdateTranslationTemplateStep::UbuntuQmlUpdateTranslationTemplateStep(ProjectExplorer::BuildStepList *bsl, UbuntuQmlUpdateTranslationTemplateStep *bs) : AbstractProcessStep(bsl,bs) {} bool UbuntuQmlUpdateTranslationTemplateStep::init() { QString projectDir = target()->project()->projectDirectory().toString(); ProjectExplorer::BuildConfiguration *bc = target()->activeBuildConfiguration(); if(!bc) return false; ProjectExplorer::ProcessParameters *param = processParameters(); param->setWorkingDirectory(projectDir); param->setCommand(makeCommand(ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit()), bc->environment())); param->setMacroExpander(bc->macroExpander()); param->setEnvironment(bc->environment()); return true; } QString UbuntuQmlUpdateTranslationTemplateStep::makeCommand(ProjectExplorer::ToolChain *tc, const Utils::Environment &env) const { if (tc) return tc->makeCommand(env); return QStringLiteral("make"); } ProjectExplorer::BuildStepConfigWidget *UbuntuQmlUpdateTranslationTemplateStep::createConfigWidget() { return new ProjectExplorer::SimpleBuildStepConfigWidget(this); } UbuntuQmlBuildTranslationStep::UbuntuQmlBuildTranslationStep(ProjectExplorer::BuildStepList *bsl) : UbuntuQmlUpdateTranslationTemplateStep(bsl,Constants::UBUNTU_CLICK_QML_BUILD_TRANSL_MAKESTEP) { setDefaultDisplayName(tr("Build translations")); } UbuntuQmlBuildTranslationStep::UbuntuQmlBuildTranslationStep(ProjectExplorer::BuildStepList *bsl, UbuntuQmlBuildTranslationStep *bs) : UbuntuQmlUpdateTranslationTemplateStep(bsl,bs) {} bool UbuntuQmlBuildTranslationStep::init() { if(!UbuntuQmlUpdateTranslationTemplateStep::init()) return false; ProjectExplorer::BuildConfiguration *bc = target()->activeBuildConfiguration(); if(!bc) return false; m_translationDir = bc->buildDirectory().toString() + QDir::separator() + QString::fromLatin1(Constants::UBUNTU_CLICK_QML_BUILD_TRANSL_DIR); processParameters()->setArguments(QString::fromLatin1("TRANSLATION_ROOT=%1 build-translations").arg(m_translationDir)); return true; } ProjectExplorer::BuildStepConfigWidget *UbuntuQmlBuildTranslationStep::createConfigWidget() { return new ProjectExplorer::SimpleBuildStepConfigWidget(this); } void UbuntuQmlBuildTranslationStep::run(QFutureInterface &fi) { //paranoid double check if(m_translationDir.endsWith(QDir::separator()+QLatin1String(Constants::UBUNTU_CLICK_QML_BUILD_TRANSL_DIR))) { QDir translDir(m_translationDir); if(translDir.exists()) translDir.removeRecursively(); } return UbuntuQmlUpdateTranslationTemplateStep::run(fi); } QList UbuntuQmlBuildStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const { if(parent->id() != ProjectExplorer::Constants::BUILDSTEPS_BUILD) return QList(); UbuntuKitMatcher m; if(!m.matches(parent->target()->kit()) || parent->target()->project()->id() != "QmlProjectManager.QmlProject") return QList(); return QList()<fromMap(map)) { delete step; return nullptr; } return step; } bool UbuntuQmlBuildStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const { return availableCreationIds(parent).contains(product->id()); } ProjectExplorer::BuildStep *UbuntuQmlBuildStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) { QTC_ASSERT(canClone(parent,product),return 0); const Core::Id id = product->id(); if(id == Constants::UBUNTU_CLICK_QML_BUILD_TRANSL_MAKESTEP) return new UbuntuQmlBuildTranslationStep(parent, static_cast(product)); else if(id == Core::Id(Constants::UBUNTU_CLICK_QML_UPDATE_TRANSL_MAKESTEP)) return new UbuntuQmlUpdateTranslationTemplateStep(parent, static_cast(product)); return 0; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuplugin.cpp0000644000015600001650000003274612705421114016464 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuplugin.h" #include "ubuntuconstants.h" #include "ubuntuprojectmanager.h" #include "ubuntulocalrunconfiguration.h" #include "ubuntulocalrunconfigurationfactory.h" #include "ubunturemoteruncontrolfactory.h" #include "ubuntulocalruncontrolfactory.h" #include "ubuntuclicktool.h" #include "ubuntukitmanager.h" #include "ubuntudevicefactory.h" #include "clicktoolchain.h" #include "ubuntuhtmlbuildconfiguration.h" #include "ubunturemotedeployconfiguration.h" #include "ubuntulocaldeployconfiguration.h" #include "ubuntudevicesmodel.h" #include "localportsmanager.h" #include "ubuntubzr.h" #include "ubuntuqtversion.h" #include "ubuntudeploystepfactory.h" #include "ubuntuqmlbuildconfiguration.h" #include "ubuntueditorfactory.h" #include "ubuntucmakecache.h" #include "ubuntutestcontrol.h" #include "ubuntupackageoutputparser.h" #include "ubuntuprojecthelper.h" #include "ubuntuscopefinalizer.h" #include "targetupgrademanager.h" #include "ubuntusettingsdeviceconnectivitypage.h" #include "ubuntusettingsclickpage.h" #include "ubuntusettingsprojectdefaultspage.h" #include "wizards/ubuntuprojectapplicationwizard.h" #include "wizards/ubuntufirstrunwizard.h" #include "wizards/ubuntuprojectmigrationwizard.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu; using namespace Ubuntu::Internal; UbuntuPlugin::UbuntuPlugin() { if(UbuntuClickTool::clickChrootSuffix() == QLatin1String(Constants::UBUNTU_CLICK_CHROOT_DEFAULT_NAME)) { bool started = false; #ifdef UBUNTU_BUILD_ROOT Utils::FileName chrootAgent = Utils::FileName::fromString(QStringLiteral(UBUNTU_BUILD_ROOT)); chrootAgent.appendPath(QStringLiteral("chroot-agent")) //append dir .appendPath(QStringLiteral("click-chroot-agent")); //append binary if(chrootAgent.toFileInfo().isExecutable()) { started = QProcess::startDetached(chrootAgent.toFileInfo().absoluteFilePath()); } #endif if(!started) { Utils::FileName agent = Utils::FileName::fromString(QCoreApplication::applicationDirPath()) .appendPath(QStringLiteral("click-chroot-agent")); if (agent.toFileInfo().isExecutable()) { QProcess::startDetached(agent.toString()); } else { //start the chroot-agent QProcess::startDetached(QStringLiteral("click-chroot-agent")); } } } } UbuntuPlugin::~UbuntuPlugin() { } bool UbuntuPlugin::initialize(const QStringList &arguments, QString *errorString) { Q_UNUSED(arguments) Q_UNUSED(errorString) QFont defaultFont = QGuiApplication::font(); defaultFont.setFamily(QStringLiteral("Ubuntu")); defaultFont.setWeight(QFont::Light); m_settings.restoreSettings(); qmlRegisterUncreatableType("Ubuntu.DevicesModel",0,1,"DeviceConnectionState",QStringLiteral("Not instantiable")); qmlRegisterUncreatableType("Ubuntu.DevicesModel",0,1,"DeviceDetectionState",QStringLiteral("Not instantiable")); qmlRegisterUncreatableType("Ubuntu.DevicesModel",0,1,"FeatureState",QStringLiteral("Not instantiable")); qmlRegisterUncreatableType("Ubuntu.DevicesModel",0,1,"DeviceMachineType",QStringLiteral("Not instantiable")); Utils::MimeDatabase::addMimeTypes(QLatin1String(Constants::UBUNTU_MIMETYPE_XML)); addAutoReleasedObject(new UbuntuClickFrameworkProvider); m_ubuntuDeviceMode = new UbuntuDeviceMode(); addAutoReleasedObject(m_ubuntuDeviceMode); addAutoReleasedObject(new UbuntuBzr); m_ubuntuMenu = new UbuntuMenu; addAutoReleasedObject(m_ubuntuMenu); m_ubuntuPackagingMode = new UbuntuPackagingMode(); addAutoReleasedObject(m_ubuntuPackagingMode); addAutoReleasedObject(new UbuntuSettingsClickPage); addAutoReleasedObject(new UbuntuSettingsProjectDefaultsPage); addAutoReleasedObject(new UbuntuSettingsDeviceConnectivityPage); addAutoReleasedObject(new UbuntuVersionManager); Core::IWizardFactory::registerFeatureProvider(new UbuntuFeatureProvider); // welcome page plugin addAutoReleasedObject(new UbuntuWelcomePage); // Handle new project type files addAutoReleasedObject(new UbuntuProjectManager); addAutoReleasedObject(new UbuntuLocalRunConfigurationFactory); addAutoReleasedObject(new UbuntuRemoteRunControlFactory); addAutoReleasedObject(new UbuntuLocalRunControlFactory); // Build support addAutoReleasedObject(new ClickToolChainFactory); addAutoReleasedObject(new UbuntuCMakeCache); addAutoReleasedObject(new UbuntuHtmlBuildConfigurationFactory); addAutoReleasedObject(new UbuntuQmlBuildConfigurationFactory); addAutoReleasedObject(new UbuntuQmlBuildStepFactory); CMakeProjectManager::CMakeToolManager::registerAutodetectionHelper([](){ QList found; QList targets = UbuntuClickTool::listAvailableTargets(); foreach (const UbuntuClickTool::Target &t, targets) { CMakeProjectManager::CMakeTool *tool = UbuntuKitManager::createCMakeTool(t); if (tool) found.append(tool); } return found; }); //ubuntu device support addAutoReleasedObject(new UbuntuDeviceFactory); addAutoReleasedObject(new UbuntuLocalPortsManager); //deploy support addAutoReleasedObject(new UbuntuRemoteDeployConfigurationFactory); addAutoReleasedObject(new UbuntuClickReviewTaskHandler); //register wizards addAutoReleasedObject( new UbuntuWizardFactory( QStringLiteral("ubuntu-project-cmake"), Core::IWizardFactory::ProjectWizard)); addAutoReleasedObject( new UbuntuWizardFactory( QStringLiteral("ubuntu-project-qmake"), Core::IWizardFactory::ProjectWizard)); addAutoReleasedObject( new UbuntuWizardFactory( QStringLiteral("ubuntu-project-plain-html"), Core::IWizardFactory::ProjectWizard)); addAutoReleasedObject( new UbuntuWizardFactory( QStringLiteral("ubuntu-project-plain-qml"), Core::IWizardFactory::ProjectWizard)); addAutoReleasedObject( new UbuntuWizardFactory( QStringLiteral("ubuntu-project-go"), Core::IWizardFactory::ProjectWizard)); //register Qt version addAutoReleasedObject(new Ubuntu::Internal::UbuntuQtVersionFactory); //disabled for now, keeping the code because we might need a deploy method //for local applications in the future //addAutoReleasedObject(new UbuntuLocalDeployConfigurationFactory); addAutoReleasedObject(new UbuntuDeployStepFactory); addAutoReleasedObject(new Internal::UbuntuManifestEditorFactory); addAutoReleasedObject(new Internal::UbuntuApparmorEditorFactory); //trigger kit autodetection and update after projectexplorer loaded the kits connect(ProjectExplorer::KitManager::instance(),SIGNAL(kitsLoaded()) ,this,SLOT(onKitsLoaded())); const Core::Context qmakeProjectContext(QmakeProjectManager::Constants::PROJECT_ID); Core::ActionContainer *mproject = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT); Core::ActionContainer *msubproject = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_SUBPROJECTCONTEXT); //support for the UbuntuProjectMigrateWizard connect(ProjectExplorer::ProjectTree::instance(), SIGNAL(aboutToShowContextMenu(ProjectExplorer::Project*,ProjectExplorer::Node*)), this, SLOT(updateContextMenu(ProjectExplorer::Project*,ProjectExplorer::Node*))); m_migrateProjectAction = new QAction(tr("Migrate to Ubuntu project"), this); Core::Command *command = Core::ActionManager::registerAction(m_migrateProjectAction, Constants::UBUNTU_MIGRATE_QMAKE_PROJECT, qmakeProjectContext); command->setAttribute(Core::Command::CA_Hide); mproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_FILES); msubproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_FILES); connect(m_migrateProjectAction, SIGNAL(triggered()), this, SLOT(migrateProject())); /* Fix Bug lp:1340061 "Some dialogs have unreadable (too small) text" * The Bug is caused by UITK that overrides the default Font that is based * on the grid units, which is not useable in Widget based applications */ QGuiApplication::setFont(defaultFont); return true; } void UbuntuPlugin::extensionsInitialized() { if (m_ubuntuMenu) m_ubuntuMenu->initialize(); m_ubuntuDeviceMode->initialize(); m_ubuntuPackagingMode->initialize(); //add the create click package menu item to the project context menu Core::ActionContainer *mproject = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT); if(mproject) { Core::Command *comm = Core::ActionManager::command("Ubuntu.Build.CreateClickPackage"); if(comm) mproject->addAction(comm, ProjectExplorer::Constants::G_PROJECT_BUILD); comm = Core::ActionManager::command("Ubuntu.Build.CreateManifest"); if(comm) mproject->addAction(comm, ProjectExplorer::Constants::G_PROJECT_BUILD); } //add ubuntu testcontrol to the object tree new UbuntuTestControl(Core::ICore::mainWindow()); } void UbuntuPlugin::onKitsLoaded() { UbuntuKitManager::autoDetectKits(); disconnect(ProjectExplorer::KitManager::instance(),SIGNAL(kitsLoaded()) ,this,SLOT(onKitsLoaded())); showFirstStartWizard(); TargetUpgradeManager *mgr = new TargetUpgradeManager(); addAutoReleasedObject(mgr); mgr->checkForUpgrades(); } void UbuntuPlugin::showFirstStartWizard() { QString file = m_settings.settingsPath().appendPath(QStringLiteral("firstrun")).toString(); if(!QFile::exists(file)) { UbuntuFirstRunWizard wiz(Core::ICore::mainWindow()); if( wiz.exec() == QDialog::Accepted ) { if (wiz.field(QStringLiteral("createEmulator")).toBool()) { Core::ModeManager::activateMode(Ubuntu::Constants::UBUNTU_MODE_DEVICES); //invoke the method the next time the event loop starts QMetaObject::invokeMethod(m_ubuntuDeviceMode,"showAddEmulatorDialog",Qt::QueuedConnection); } } if(wiz.field(QStringLiteral("disableWizard")).toBool()) { QFile f(file); if(f.open(QIODevice::WriteOnly)) { f.write("1"); f.close(); } } } } void UbuntuPlugin::updateContextMenu(ProjectExplorer::Project *project, ProjectExplorer::Node *node) { m_currentContextMenuProject = project; m_migrateProjectAction->setVisible(false); QmakeProjectManager::QmakeProject *qProject = qobject_cast(project); QmakeProjectManager::QmakeProFileNode *qNode = static_cast(node); if(qProject && qNode) { if(qProject->rootProjectNode() == qNode && UbuntuProjectHelper::getManifestPath(project,QString()).isEmpty()) { auto projectType = qNode->projectType(); if(projectType == QmakeProjectManager::ApplicationTemplate || projectType == QmakeProjectManager::SubDirsTemplate) { m_migrateProjectAction->setVisible(true); } } } } void UbuntuPlugin::migrateProject() { QmakeProjectManager::QmakeProject *p = qobject_cast(m_currentContextMenuProject); if(!p) return; UbuntuProjectMigrationWizard::doMigrateProject(p,Core::ICore::mainWindow()); } ./src/ubuntu/UbuntuProject.mimetypes.xml0000644000015600001650000000137012705421114020552 0ustar jenkinsjenkins Ubuntu Project file Ubuntu Apparmor description file Ubuntu Manifest file ./src/ubuntu/ubuntusettingsclickpage.h0000644000015600001650000000255412705421114020330 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUSETTINGSCLICKPAGE_H #define UBUNTUSETTINGSCLICKPAGE_H #include #include "ubuntusettingsclickwidget.h" #include namespace Ubuntu { namespace Internal { class UbuntuSettingsClickPage : public Core::IOptionsPage { Q_OBJECT public: explicit UbuntuSettingsClickPage(); ~UbuntuSettingsClickPage(); QWidget *widget( ) override; void apply() override; void finish() override; protected: QPointer m_widget; }; } } #endif // UBUNTUSETTINGSCLICKPAGE_H ./src/ubuntu/ubunturemoteruncontrol.cpp0000644000015600001650000001350512705421114020577 0ustar jenkinsjenkins/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Copyright (C) 2014 Canonical Ltd. ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "ubunturemoteruncontrol.h" #include "ubunturemoterunconfiguration.h" #include "ubunturemoterunner.h" #include "ubuntuwaitfordevicedialog.h" #include "ubuntudevice.h" #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; namespace Ubuntu { namespace Internal { class UbuntuRemoteRunControl::UbuntuRemoteRunControlPrivate { public: bool running; UbuntuRemoteClickApplicationRunner runner; UbuntuDevice::ConstPtr device; QString clickPackage; Utils::Environment environment; QPointer waitDialog; QPointer runConfig; }; UbuntuRemoteRunControl::UbuntuRemoteRunControl(RunConfiguration *rc) : RunControl(rc, ProjectExplorer::Constants::NORMAL_RUN_MODE), d(new UbuntuRemoteRunControlPrivate) { d->running = false; d->device = qSharedPointerCast(DeviceKitInformation::device(rc->target()->kit())); d->runConfig = QPointer(static_cast(rc)); d->environment = d->runConfig->environment(); d->clickPackage = d->runConfig->clickPackage(); setIcon(QLatin1String(ProjectExplorer::Constants::ICON_RUN_SMALL)); } UbuntuRemoteRunControl::~UbuntuRemoteRunControl() { delete d; } void UbuntuRemoteRunControl::start() { if(d->device->deviceState() != ProjectExplorer::IDevice::DeviceReadyToUse) { if(d->waitDialog) return; d->waitDialog = new UbuntuWaitForDeviceDialog(Core::ICore::mainWindow()); connect(d->waitDialog.data(),SIGNAL(canceled()),this,SLOT(handleWaitDialogCanceled())); connect(d->waitDialog.data(),SIGNAL(deviceReady()),this,SLOT(handleDeviceReady())); d->waitDialog->show(d->device); if(d->device->machineType() == ProjectExplorer::IDevice::Emulator && d->device->deviceState() == ProjectExplorer::IDevice::DeviceDisconnected) d->device->helper()->device()->startEmulator(); } else { handleDeviceReady(); } } RunControl::StopResult UbuntuRemoteRunControl::stop() { if(d->waitDialog && !d->running) { d->waitDialog->cancel(); return StoppedSynchronously; } d->runner.stop(); return AsynchronousStop; } void UbuntuRemoteRunControl::handleErrorMessage(const QString &error) { appendMessage(error, Utils::ErrorMessageFormat); } void UbuntuRemoteRunControl::handleRunnerFinished() { setFinished(); } void UbuntuRemoteRunControl::handleRemoteOutput(const QByteArray &output) { appendMessage(QString::fromUtf8(output), Utils::StdOutFormatSameLine); } void UbuntuRemoteRunControl::handleRemoteErrorOutput(const QByteArray &output) { appendMessage(QString::fromUtf8(output), Utils::StdErrFormatSameLine); } void UbuntuRemoteRunControl::handleProgressReport(const QString &progressString) { appendMessage(progressString + QLatin1Char('\n'), Utils::NormalMessageFormat); } void UbuntuRemoteRunControl::handleDeviceReady() { d->waitDialog->deleteLater(); d->running = true; if(d->runConfig) d->runConfig->setRunning(true); emit started(); d->runner.disconnect(this); connect(&d->runner, SIGNAL(reportError(QString)), SLOT(handleErrorMessage(QString))); connect(&d->runner, SIGNAL(launcherStderr(QByteArray)), SLOT(handleRemoteErrorOutput(QByteArray))); connect(&d->runner, SIGNAL(launcherStdout(QByteArray)), SLOT(handleRemoteOutput(QByteArray))); connect(&d->runner, SIGNAL(finished(bool)), SLOT(handleRunnerFinished())); d->runner.setForceInstall(d->runConfig->forceInstall()); d->runner.setUninstall(d->runConfig->uninstall()); d->runner.start(d->device, d->clickPackage,d->runConfig->appId()); } void UbuntuRemoteRunControl::handleWaitDialogCanceled() { d->waitDialog->deleteLater(); setFinished(); } bool UbuntuRemoteRunControl::isRunning() const { return d->running; } void UbuntuRemoteRunControl::setFinished() { if(d->runConfig) d->runConfig->setRunning(false); d->runner.disconnect(this); d->running = false; emit finished(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntudevicesignaloperation.h0000644000015600001650000000211412705421114021173 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUDEVICESIGNALOPERATION_H #define UBUNTU_INTERNAL_UBUNTUDEVICESIGNALOPERATION_H #include #include "ubuntudevice.h" namespace Ubuntu { namespace Internal { class UbuntuDeviceSignalOperation : public ProjectExplorer::DeviceProcessSignalOperation { Q_OBJECT public: UbuntuDeviceSignalOperation(UbuntuDevice::ConstPtr device); ~UbuntuDeviceSignalOperation() {} typedef QSharedPointer Ptr; // DeviceProcessSignalOperation interface virtual void killProcess(int pid); virtual void killProcess(const QString &filePath); virtual void interruptProcess(int pid); virtual void interruptProcess(const QString &filePath); private: void sendSignal (int pid, int signal); private slots: void processFinished (int exitCode, QProcess::ExitStatus exitState); void processError(QProcess::ProcessError procErr); private: UbuntuDevice::ConstPtr m_device; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUDEVICESIGNALOPERATION_H ./src/ubuntu/ubunturemoteruncontrolfactory.h0000644000015600001650000000427612705421114021641 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTURUNCONTROLFACTORY_H #define UBUNTURUNCONTROLFACTORY_H #include #include "ubuntuproject.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuRemoteRunControlFactory : public ProjectExplorer::IRunControlFactory { Q_OBJECT public: explicit UbuntuRemoteRunControlFactory() {} virtual ~UbuntuRemoteRunControlFactory() {} bool canRun(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode) const override; ProjectExplorer::RunControl *create(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode, QString *errorMessage) override; QString displayName() const; }; } } #endif // UBUNTURUNCONTROLFACTORY_H ./src/ubuntu/ubuntuprojectmanager.cpp0000644000015600001650000000442512705421114020160 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuprojectmanager.h" #include #include using namespace Ubuntu::Internal; UbuntuProjectManager::UbuntuProjectManager() { QObject* sessionManager = ProjectExplorer::SessionManager::instance(); connect(sessionManager,SIGNAL(projectAdded(ProjectExplorer::Project*)),SLOT(onProjectAdded(ProjectExplorer::Project*))); } ProjectExplorer::Project* UbuntuProjectManager::openProject(const QString &filePath, QString *errorString) { QFileInfo fileInfo(filePath); foreach (ProjectExplorer::Project *pi, ProjectExplorer::SessionManager::projects()) { if (filePath == pi->document()->filePath().toString()) { if (errorString) *errorString = tr("Failed opening project '%1': Project already open") .arg(QDir::toNativeSeparators(filePath)); return 0; } } if (fileInfo.isFile()) return new UbuntuProject(this, filePath); *errorString = tr("Failed opening project '%1': Project file is not a file").arg(QDir::toNativeSeparators(filePath)); return 0; } void UbuntuProjectManager::registerProject(UbuntuProject *project) { m_projects.append(project); } void UbuntuProjectManager::unregisterProject(UbuntuProject *project) { m_projects.removeAll(project); } QString UbuntuProjectManager::mimeType() const { return QLatin1String(Constants::UBUNTUPROJECT_MIMETYPE); } void UbuntuProjectManager::onProjectAdded(ProjectExplorer::Project* addedProject) { QString mimeType = addedProject->projectManager()->mimeType(); qDebug() << mimeType; } ./src/ubuntu/ubuntuqtversion.h0000644000015600001650000000312112705421114016646 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUQTVERSION_H #define UBUNTU_INTERNAL_UBUNTUQTVERSION_H #include #include namespace Ubuntu { namespace Internal { class UbuntuQtVersion : public QtSupport::BaseQtVersion { public: UbuntuQtVersion(); UbuntuQtVersion(const Utils::FileName &path, bool isAutodetected = false, const QString &autodetectionSource = QString()); ~UbuntuQtVersion() override; // BaseQtVersion interface virtual void fromMap(const QVariantMap &map) override; virtual QVariantMap toMap() const override; UbuntuQtVersion *clone() const override; QString type() const override; QList detectQtAbis() const override; QString description() const override; QString platformName() const override; QString platformDisplayName() const override; int scriptVersion() const; void setScriptVersion(int scriptVersion); static int minimalScriptVersion (); private: int m_scriptVersion; }; class UbuntuQtVersionFactory : public QtSupport::QtVersionFactory { public: // QtVersionFactory interface virtual bool canRestore(const QString &type) override; virtual QtSupport::BaseQtVersion *restore(const QString &type, const QVariantMap &data) override; virtual int priority() const override; virtual QtSupport::BaseQtVersion *create(const Utils::FileName &qmakePath, ProFileEvaluator *evaluator, bool isAutoDetected, const QString &autoDetectionSource) override; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUQTVERSION_H ./src/ubuntu/ubuntulocalrunconfiguration.h0000644000015600001650000000553012705421114021231 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTURUNCONFIGURATION_H #define UBUNTURUNCONFIGURATION_H #include #include #include #include #include #include namespace ProjectExplorer { class Target; } namespace Ubuntu { namespace Internal { class UbuntuClickManifest; class UbuntuLocalEnvironmentAspect : public ProjectExplorer::LocalEnvironmentAspect { Q_OBJECT public: UbuntuLocalEnvironmentAspect(ProjectExplorer::RunConfiguration *parent); virtual Utils::Environment baseEnvironment() const override; }; class UbuntuLocalRunConfiguration : public ProjectExplorer::RunConfiguration { Q_OBJECT public: UbuntuLocalRunConfiguration(ProjectExplorer::Target *parent, Core::Id id); UbuntuLocalRunConfiguration(ProjectExplorer::Target *parent, UbuntuLocalRunConfiguration* source); QWidget *createConfigurationWidget() override; bool isEnabled() const override; bool aboutToStart (QString *errorMessage); QString appId() const; // LocalApplicationRunConfiguration interface virtual QString executable() const; virtual QString workingDirectory() const; virtual QString commandLineArguments() const; virtual ProjectExplorer::ApplicationLauncher::Mode runMode() const; virtual void addToBaseEnvironment(Utils::Environment &env) const; // RunConfiguration interface virtual bool isConfigured () const override; virtual ConfigurationState ensureConfigured(QString *) override; //static helpers static QString getDesktopFile (RunConfiguration *config, QString appId, QString *errorMessage = 0); static bool readDesktopFile (const QString &desktopFile, QString *executable, QStringList *arguments, QString *errorMessage); private: bool ensureClickAppConfigured (QString *errorMessage); bool ensureScopesAppConfigured (QString *); bool ensureUbuntuProjectConfigured (QString *errorMessage); private: QString m_executable; Utils::FileName m_workingDir; QStringList m_args; }; } } #endif // UBUNTURUNCONFIGURATION_H ./src/ubuntu/ubuntuprocess.cpp0000644000015600001650000001257012705421114016635 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuprocess.h" #include #include #include #include #include #include "ubuntuconstants.h" using namespace Ubuntu; using namespace Ubuntu::Internal; UbuntuProcess::UbuntuProcess(QObject *parent) : QObject(parent) , m_currentProcess(new QProcess(this)) , m_futureInterface(0) { connect(m_currentProcess,SIGNAL(readyReadStandardError()),this,SLOT(processReadyRead())); connect(m_currentProcess,SIGNAL(started()),this,SLOT(processStarted())); connect(m_currentProcess,SIGNAL(finished(int)),this,SLOT(processFinished(int))); connect(m_currentProcess,SIGNAL(readyRead()),this,SLOT(processReadyRead())); connect(m_currentProcess,SIGNAL(error(QProcess::ProcessError)),this,SLOT(processError(QProcess::ProcessError))); } void UbuntuProcess::initializeProgressBar(QString title, int max) { if (m_futureInterface) { m_futureInterface->reportCanceled(); delete m_futureInterface; m_futureInterface = 0; } m_futureInterface = new QFutureInterface(); m_futureInterface->setProgressRange(0,max); Core::Id id(Constants::TASK_DEVICE_SCRIPT); Core::FutureProgress* futureProgress = Core::ProgressManager::addTask(m_futureInterface->future(),title,id.withSuffix(QUuid::createUuid().toString())); connect(futureProgress,SIGNAL(clicked()),this,SLOT(stop())); } void UbuntuProcess::setProgressBarStarted() { m_futureInterface->reportStarted(); } void UbuntuProcess::setProgressBarFinished() { m_futureInterface->reportFinished(); } void UbuntuProcess::increaseProgress(QString msg) { int currentValue = m_futureInterface->progressValue(); m_futureInterface->setProgressValueAndText(currentValue++,msg); } void UbuntuProcess::setProgressBarCancelled() { m_futureInterface->reportCanceled(); m_futureInterface->reportFinished(); } void UbuntuProcess::close() { m_currentProcess->close(); m_currentProcess->waitForFinished(); } void UbuntuProcess::stop() { kill(); } void UbuntuProcess::processStarted() { increaseProgress(m_currentProcess->program()); emit started(m_currentProcess->program()); } void UbuntuProcess::kill() { this->clear(); m_currentProcess->kill(); m_currentProcess->waitForFinished(); if(m_futureInterface) m_futureInterface->reportFinished(); } void UbuntuProcess::processError(QProcess::ProcessError err) { Q_UNUSED(err); if (m_currentProcess->exitCode() == 0) { return; } emit error(QString(QLatin1String("ERROR: (%0) %1")).arg(m_currentProcess->program()).arg(m_currentProcess->errorString())); // lets clear the remaining actions m_pendingProcesses.clear(); setProgressBarCancelled(); } void UbuntuProcess::start(QString taskTitle) { initializeProgressBar(taskTitle,m_pendingProcesses.length()); processCmdQueue(); } void UbuntuProcess::processFinished(int code) { if (code != 0) { emit error(QString::fromLocal8Bit(m_currentProcess->readAllStandardError())); m_pendingProcesses.clear(); setProgressBarCancelled(); //failing process has to emit finished too emit finished(m_currentProcess->program(), code); return; } processReadyRead(); emit finished(m_currentProcess->program(), code); emit finished(m_currentProcess,m_currentProcess->program(),code); processCmdQueue(); } void UbuntuProcess::processReadyRead() { QString stderr = QString::fromLocal8Bit(m_currentProcess->readAllStandardError()); QString stdout = QString::fromLocal8Bit(m_currentProcess->readAllStandardOutput()); if (!stderr.trimmed().isEmpty()) { emit error(stderr); emit message(stderr); } if (!stdout.trimmed().isEmpty()) { emit stdOut(stdout); emit message(stdout); } } void UbuntuProcess::processCmdQueue() { if (m_pendingProcesses.length() == 0) { setProgressBarFinished(); return; } QStringList cmdList = m_pendingProcesses.takeFirst(); QString cmd = cmdList.takeFirst(); QStringList args; QString workingDirectory; if (cmdList.length()>0) { workingDirectory = cmdList.takeLast(); } if (cmdList.length()>0) { args << cmdList; } if (!workingDirectory.isEmpty()) { m_currentProcess->setWorkingDirectory(workingDirectory); } QString msg(QString(QLatin1String("%0 %1")).arg(cmd).arg(args.join(QLatin1String(" ")))); //emit message(msg); m_currentProcess->setProperty("command",cmd); if (args.length()>0) { m_currentProcess->start(cmd,args); } else { m_currentProcess->start(cmd); } } ./src/ubuntu/ubuntu.pri0000644000015600001650000000004312705421114015236 0ustar jenkinsjenkinsLIBS += -lUbuntu INCLUDEPATH += .. ./src/ubuntu/ubuntumanifesteditor.h0000644000015600001650000000250612705421114017637 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUMANIFESTEDITOR_H #define UBUNTU_INTERNAL_UBUNTUMANIFESTEDITOR_H #include "ubuntuabstractguieditor.h" class QToolBar; namespace Ubuntu { namespace Internal { class UbuntuManifestEditorWidget; class UbuntuManifestEditor : public UbuntuAbstractGuiEditor { Q_OBJECT public: UbuntuManifestEditor(); ~UbuntuManifestEditor(); // UbuntuAbstractGuiEditor interface protected: virtual UbuntuAbstractGuiEditorWidget *createGuiEditor(); private: UbuntuManifestEditorWidget *m_editorWidget; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUMANIFESTEDITOR_H ./src/ubuntu/myapp.json.template0000644000015600001650000000012112705421114017030 0ustar jenkinsjenkins{ "policy_groups": [ "networking" ], "policy_version": 1.2 } ./src/ubuntu/ubuntudirectuploadstep.h0000644000015600001650000000276012705421114020177 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUDIRECTUPLOADSTEP_H #define UBUNTU_INTERNAL_UBUNTUDIRECTUPLOADSTEP_H #include #include namespace RemoteLinux { class GenericDirectUploadService; } namespace Ubuntu { namespace Internal { class UbuntuWaitForDeviceDialog; class UbuntuDirectUploadStep : public RemoteLinux::AbstractRemoteLinuxDeployStep { Q_OBJECT public: UbuntuDirectUploadStep(ProjectExplorer::BuildStepList *bsl); UbuntuDirectUploadStep(ProjectExplorer::BuildStepList *bsl, UbuntuDirectUploadStep *other); ~UbuntuDirectUploadStep(); // BuildStep interface virtual void run(QFutureInterface &fi) override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); bool initInternal(QString *error = 0) override; RemoteLinux::AbstractRemoteLinuxDeployService *deployService() const override; bool fromMap(const QVariantMap &map) override; QVariantMap toMap() const override; static Core::Id stepId(); static QString displayName(); private slots: void projectNameChanged(); void handleWaitDialogCanceled(); void handleDeviceReady (); private: RemoteLinux::GenericDirectUploadService *m_deployService; bool m_foundClickPackage; //wait support for the device/emulator to come up QPointer m_waitDialog; QFutureInterface *m_future; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUDIRECTUPLOADSTEP_H ./src/ubuntu/ubuntupackageoutputparser.h0000644000015600001650000000304312705421114020710 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUPACKAGEOUTPUTPARSER_H #define UBUNTU_INTERNAL_UBUNTUPACKAGEOUTPUTPARSER_H #include #include #include "ubuntuvalidationresultmodel.h" namespace Ubuntu { namespace Internal { class UbuntuPackageOutputParser : public ProjectExplorer::IOutputParser { Q_OBJECT public: explicit UbuntuPackageOutputParser(); public: // IOutputParser interface virtual void stdOutput(const QString &line); virtual void stdError(const QString &line); virtual bool hasFatalErrors() const; void setTreatAllErrorsAsWarnings ( const bool set ); public slots: void setEndOfData (); private slots: void onParsedNewTopLevelItem (ClickRunChecksParser::DataItem* item); private: virtual void doFlush(); void emitTasks (const ClickRunChecksParser::DataItem *item, int level = 0); bool isError (const ClickRunChecksParser::DataItem *item); ClickRunChecksParser m_subParser; bool m_fatalError; bool m_endOfData; bool m_treatAllErrorsAsWarnings; }; class UbuntuClickReviewTaskHandler : public ProjectExplorer::ITaskHandler { Q_OBJECT // ITaskHandler interface public: virtual bool canHandle(const ProjectExplorer::Task &task) const; virtual void handle(const ProjectExplorer::Task &task); virtual QAction *createAction(QObject *parent) const; private: QUrl getUrl (const ProjectExplorer::Task &task) const; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUPACKAGEOUTPUTPARSER_H ./src/ubuntu/ubuntushared.h0000644000015600001650000000167612705421114016077 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUSHARED_H #define UBUNTUSHARED_H #include #include bool readFile(const QString &fileName, QByteArray *data, QString *errorMessage); void printToOutputPane(const QString &msg); #endif // UBUNTUSHARED_H ./src/ubuntu/ubuntubzr.h0000644000015600001650000000267412705421114015425 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUBZR_H #define UBUNTUBZR_H #include #include namespace Ubuntu { namespace Internal { class UbuntuBzr : public QObject { Q_OBJECT public: explicit UbuntuBzr(); ~UbuntuBzr(); static UbuntuBzr *instance (); signals: void initializedChanged(); public slots: bool isInitialized() { return m_bInitialized; } QString whoami() { return m_whoami; } QString launchpadId() { return m_launchpadId; } void initialize(); bool waitForFinished(int msecs = 30000); protected slots: void scriptExecuted(int); protected: static UbuntuBzr *m_instance; bool m_bInitialized; QString m_whoami; QString m_launchpadId; QProcess m_cmd; }; } } #endif // UBUNTUBZR_H ./src/ubuntu/clicktoolchain.cpp0000644000015600001650000002407712705421114016707 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "clicktoolchain.h" #include "ubuntuconstants.h" #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; QLatin1String UBUNTU_TARGET_ARCH_KEY = QLatin1String("Ubuntu.ClickToolChain.Target.Arch"); QLatin1String UBUNTU_TARGET_FRAMEWORK_KEY = QLatin1String("Ubuntu.ClickToolChain.Target.Framework"); QLatin1String UBUNTU_TARGET_SERIES_KEY = QLatin1String("Ubuntu.ClickToolChain.Target.Series"); QLatin1String UBUNTU_TARGET_MINOR_KEY = QLatin1String("Ubuntu.ClickToolChain.Target.MinorV"); QLatin1String UBUNTU_TARGET_MAJOR_KEY = QLatin1String("Ubuntu.ClickToolChain.Target.MajorV"); const char UBUNTU_CLICK_GCC_WRAPPER[] = "qtc_chroot_gcc"; //deprecated just for updating #if 1 static QMap init_architectures() { QMap map; map.insert(QLatin1String("armhf") , ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS, ProjectExplorer::Abi::GenericLinuxFlavor, ProjectExplorer::Abi::ElfFormat, 32)); map.insert(QLatin1String("i386") ,ProjectExplorer::Abi(ProjectExplorer::Abi::X86Architecture, ProjectExplorer::Abi::LinuxOS, ProjectExplorer::Abi::GenericLinuxFlavor, ProjectExplorer::Abi::ElfFormat, 32)); map.insert(QLatin1String("amd64"),ProjectExplorer::Abi(ProjectExplorer::Abi::X86Architecture, ProjectExplorer::Abi::LinuxOS, ProjectExplorer::Abi::GenericLinuxFlavor, ProjectExplorer::Abi::ElfFormat, 64)); return map; } QMap clickArchitectures = init_architectures(); #endif QList ClickToolChain::suggestedMkspecList() const { return ProjectExplorer::GccToolChain::suggestedMkspecList(); //return QList()<< Utils::FileName::fromString(QLatin1String("linux-g++")); } Utils::FileName ClickToolChain::suggestedDebugger() const { return Utils::FileName::fromString(QLatin1String("/usr/bin/gdb-multiarch")); } QString ClickToolChain::type() const { return QString::fromLatin1(Constants::UBUNTU_CLICK_TOOLCHAIN_ID); } QString ClickToolChain::typeDisplayName() const { return ClickToolChainFactory::tr("Ubuntu GCC"); } bool ClickToolChain::isValid() const { return GccToolChain::isValid() && targetAbi().isValid() && UbuntuClickTool::targetExists(m_clickTarget); } void ClickToolChain::addToEnvironment(Utils::Environment &env) const { GccToolChain::addToEnvironment(env); env.set(QLatin1String("CLICK_SDK_ARCH") , m_clickTarget.architecture); env.set(QLatin1String("CLICK_SDK_FRAMEWORK"), m_clickTarget.framework); env.set(QLatin1String("CLICK_SDK_SERIES") , m_clickTarget.series); } QString ClickToolChain::makeCommand(const Utils::Environment &) const { return UbuntuClickTool::findOrCreateMakeWrapper(clickTarget()); } bool ClickToolChain::operator ==(const ProjectExplorer::ToolChain &tc) const { if (!GccToolChain::operator ==(tc)) return false; return (m_clickTarget.architecture == m_clickTarget.architecture && m_clickTarget.framework == m_clickTarget.framework && m_clickTarget.series == m_clickTarget.series); } ProjectExplorer::ToolChainConfigWidget *ClickToolChain::configurationWidget() { return GccToolChain::configurationWidget(); } const UbuntuClickTool::Target &ClickToolChain::clickTarget() const { return m_clickTarget; } ProjectExplorer::Abi ClickToolChain::architectureNameToAbi(const QString &arch) { if( !clickArchitectures.contains(arch) ) return ProjectExplorer::Abi(); return clickArchitectures[arch]; } QList ClickToolChain::supportedArchitectures() { return clickArchitectures.keys(); } QVariantMap ClickToolChain::toMap() const { QVariantMap map = GccToolChain::toMap(); map.insert(UBUNTU_TARGET_ARCH_KEY,m_clickTarget.architecture); map.insert(UBUNTU_TARGET_FRAMEWORK_KEY,m_clickTarget.framework); map.insert(UBUNTU_TARGET_SERIES_KEY,m_clickTarget.series); map.insert(UBUNTU_TARGET_MAJOR_KEY,m_clickTarget.majorVersion); map.insert(UBUNTU_TARGET_MINOR_KEY,m_clickTarget.minorVersion); return map; } QString ClickToolChain::gnutriplet() const { return gnutriplet(targetAbi()); } QString ClickToolChain::gnutriplet(const ProjectExplorer::Abi &abi) { switch(abi.architecture()) { case ProjectExplorer::Abi::ArmArchitecture: return QLatin1String("arm-linux-gnueabihf"); break; case ProjectExplorer::Abi::X86Architecture: switch(abi.wordWidth()) { case 32: return QLatin1String("i386-linux-gnu"); case 64: return QLatin1String("x86_64-linux-gnu"); } break; default: Q_ASSERT_X(false,Q_FUNC_INFO,"Unknown Target architecture"); } return QString(); } bool ClickToolChain::fromMap(const QVariantMap &data) { if(!GccToolChain::fromMap(data)) return false; if(!data.contains(UBUNTU_TARGET_ARCH_KEY) || !data.contains(UBUNTU_TARGET_FRAMEWORK_KEY) || !data.contains(UBUNTU_TARGET_SERIES_KEY) || !data.contains(UBUNTU_TARGET_MAJOR_KEY) || !data.contains(UBUNTU_TARGET_MINOR_KEY)) return false; m_clickTarget.architecture = data[UBUNTU_TARGET_ARCH_KEY].toString(); m_clickTarget.framework = data[UBUNTU_TARGET_FRAMEWORK_KEY].toString(); m_clickTarget.series = data[UBUNTU_TARGET_SERIES_KEY].toString(); m_clickTarget.majorVersion = data[UBUNTU_TARGET_MAJOR_KEY].toInt(); m_clickTarget.minorVersion = data[UBUNTU_TARGET_MINOR_KEY].toInt(); //only valid click targets are used for Kit creation m_clickTarget.maybeBroken = false; return isValid(); } Utils::FileName ClickToolChain::compilerCommand() const { return GccToolChain::compilerCommand(); } ClickToolChain::ClickToolChain(const UbuntuClickTool::Target &target, Detection d) : GccToolChain(QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID), d) , m_clickTarget(target) { resetToolChain(Utils::FileName::fromString( UbuntuClickTool::findOrCreateGccWrapper(target) )); setDisplayName(QString::fromLatin1("Ubuntu GCC (%1-%2-%3)") .arg(ProjectExplorer::Abi::toString(targetAbi().architecture())) .arg(target.framework) .arg(target.series)); } ClickToolChain::ClickToolChain(const ClickToolChain &other) : GccToolChain(other) , m_clickTarget(other.m_clickTarget) { } ClickToolChain::ClickToolChain() : GccToolChain(QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID),ManualDetection) { } ClickToolChainFactory::ClickToolChainFactory() { setId(Constants::UBUNTU_CLICK_TOOLCHAIN_ID); setDisplayName(tr("Ubuntu GCC")); } QList ClickToolChainFactory::autoDetect() { return createToolChainsForClickTargets(); } bool ClickToolChainFactory::canRestore(const QVariantMap &data) { return idFromMap(data).startsWith(QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID) + QLatin1Char(':')); } ProjectExplorer::ToolChain *ClickToolChainFactory::restore(const QVariantMap &data) { ClickToolChain *tc = new ClickToolChain(); if (tc->fromMap(data)) { QFileInfo compilerCommand(tc->compilerCommand().toString()); //deprecated wrapper script, update to use the new one if(compilerCommand.fileName() == QString::fromLatin1(UBUNTU_CLICK_GCC_WRAPPER)) { QString wrapper = UbuntuClickTool::findOrCreateGccWrapper(tc->clickTarget()); //if we cannot create a good wrapper its better to drop the ToolChain if(wrapper.isNull()) { delete tc; return 0; } tc->setCompilerCommand(Utils::FileName::fromString(wrapper)); } return tc; } delete tc; return 0; } QList ClickToolChainFactory::createToolChainsForClickTargets() { QList toolChains; QList targets = UbuntuClickTool::listAvailableTargets(); foreach(const UbuntuClickTool::Target &target, targets) { if(debug) qDebug()<<"Found Target"< #include #include namespace Ubuntu { namespace Internal { class AbstractRemoteRunSupportPrivate; class UbuntuRemoteRunConfiguration; class UbuntuRemoteClickApplicationRunner; class AbstractRemoteRunSupport : public QObject { Q_OBJECT public: enum State { Idle, ScanningPorts, Starting, Running }; explicit AbstractRemoteRunSupport(UbuntuRemoteRunConfiguration* runConfig, QObject *parent = 0); virtual ~AbstractRemoteRunSupport(); protected: virtual void startExecution() = 0; virtual void handleAdapterSetupFailed(const QString &error); virtual void handleAdapterSetupDone(); bool assignNextFreePort(int *port); void setFinished (); void reset(); State state() const; void setState(const State &state); ProjectExplorer::IDevice::ConstPtr device () const; QString clickPackage () const; QString hook () const; Utils::Environment environment () const; UbuntuRemoteClickApplicationRunner *appRunner () const; protected slots: virtual void handleRemoteSetupRequested(); virtual void handleAppRunnerError(const QString &error) = 0; virtual void handleRemoteOutput(const QByteArray &output) = 0; virtual void handleRemoteErrorOutput(const QByteArray &output) = 0; virtual void handleAppRunnerFinished(bool success) = 0; virtual void handleProgressReport(const QString &progressOutput) = 0; private slots: void handlePortScannerReady (); void handlePortScannerError ( const QString &message ); private: AbstractRemoteRunSupportPrivate * const d; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_ABSTRACTREMOTERUNSUPPORT_H ./src/ubuntu/ubuntubzr.cpp0000644000015600001650000000363112705421114015752 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntubzr.h" #include "ubuntuconstants.h" #include using namespace Ubuntu::Internal; UbuntuBzr *UbuntuBzr::m_instance = 0; UbuntuBzr::UbuntuBzr() : m_bInitialized(false) { Q_ASSERT(m_instance == 0); m_instance = this; connect(&m_cmd,SIGNAL(finished(int)),this,SLOT(scriptExecuted(int))); m_cmd.setWorkingDirectory(QCoreApplication::applicationDirPath()); initialize(); } UbuntuBzr::~UbuntuBzr() { m_instance = 0; } UbuntuBzr *UbuntuBzr::instance() { return m_instance; } void UbuntuBzr::initialize() { if(m_cmd.state() == QProcess::NotRunning) m_cmd.start(QString(QLatin1String(Constants::UBUNTUBZR_INITIALIZE)).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH)); } bool UbuntuBzr::waitForFinished(int msecs) { return m_cmd.waitForFinished(msecs); } void UbuntuBzr::scriptExecuted(int sta) { Q_UNUSED(sta); QStringList data = QString(QString::fromLocal8Bit(m_cmd.readAllStandardOutput())).trimmed().split(QLatin1String(Constants::LINEFEED)); if (data.length()!=2) { return; } this->m_whoami = data.takeFirst(); this->m_launchpadId = data.takeFirst(); m_bInitialized = true; emit initializedChanged(); } ./src/ubuntu/ubuntupackagestep.cpp0000644000015600001650000010266612705421114017454 0ustar jenkinsjenkins#include "ubuntupackagestep.h" #include "ui_ubuntupackagestepconfigwidget.h" #include "ubuntuconstants.h" #include "clicktoolchain.h" #include "ubuntuprojecthelper.h" #include "ubuntuclickmanifest.h" #include "ubuntupackageoutputparser.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; const char PACKAGE_MODE_KEY[] = "Ubuntu.UbuntuPackageStep.PackageMode"; const char ERROR_MODE_KEY[] = "Ubuntu.UbuntuPackageStep.TreatErrorsAsWarnings"; /*! * \class UbuntuPackageStep * * Deploy step that is responsible for running "make install", * injecting debug helper into the package and creating the click * package itself */ UbuntuPackageStep::UbuntuPackageStep(ProjectExplorer::BuildStepList *bsl) : ProjectExplorer::BuildStep(bsl,Constants::UBUNTU_CLICK_PACKAGESTEP_ID), m_state(Idle), m_futureInterface(0), m_process(0), m_outputParserChain(0), m_debugMode(EnableDebugScript), m_packageMode(Default), m_cleanDeployDirectory(true) { setDefaultDisplayName(tr("UbuntuSDK Click build")); m_treatClickErrorsAsWarnings = Settings::projectDefaults().reviewErrorsAsWarnings; if (!Settings::projectDefaults().enableDebugHelper) m_debugMode = DisableDebugScript; } UbuntuPackageStep::UbuntuPackageStep(ProjectExplorer::BuildStepList *bsl, UbuntuPackageStep *other) : ProjectExplorer::BuildStep(bsl,other), m_state(Idle), m_futureInterface(0), m_process(0), m_outputParserChain(0), m_debugMode(other->m_debugMode), m_packageMode(other->m_packageMode), m_cleanDeployDirectory(other->m_cleanDeployDirectory) { } UbuntuPackageStep::~UbuntuPackageStep() { cleanup(); } bool UbuntuPackageStep::init() { //initialization happens in internalInit, //because it requires informations that are only available at this //time //@TODO refactor into single buildsteps per projecttype return true; } void UbuntuPackageStep::internalInit() { m_tasks.clear(); QString projectDir = target()->project()->projectDirectory().toString(); m_buildDir.clear(); m_deployDir.clear(); Utils::Environment env = Utils::Environment::systemEnvironment(); Utils::MacroExpander *mExp = 0; m_MakeParam = m_ClickParam = m_ReviewParam = ProjectExplorer::ProcessParameters(); m_clickPackageName.clear(); bool isCMake = target()->project()->id() == CMakeProjectManager::Constants::CMAKEPROJECT_ID; bool isQMake = target()->project()->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID; { ProjectExplorer::BuildConfiguration *bc = referenceBuildConfig(); if(bc) { m_buildDir = bc->buildDirectory().toString(); m_deployDir = bc->buildDirectory().toString() + QDir::separator() + QString::fromLatin1(Constants::UBUNTU_DEPLOY_DESTDIR); env = bc->environment(); mExp = bc->macroExpander(); } else { //cmake and qmake projects NEED a buildconfiguration if (isCMake || isQMake) { ProjectExplorer::Task t(ProjectExplorer::Task::Error ,tr("No valid BuildConfiguration set for step: %1").arg(displayName()) ,Utils::FileName(),-1 ,ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); m_tasks.append(t); //UbuntuClickPackageStep::run will stop if tasks exist return; } else { //backward compatibility, old HTML5 projects did not have a Buildconfiguration //this would crash otherwise //ubuntu + qml project types QDir pDir(projectDir); m_buildDir = QDir::cleanPath(target()->project()->projectDirectory() .appendPath(QStringLiteral("..")) .appendPath(pDir.dirName()+QStringLiteral("_build")) .toString()); m_deployDir = m_buildDir+QDir::separator()+QLatin1String(Constants::UBUNTU_DEPLOY_DESTDIR); //clean up the old "build" QDir bd(m_buildDir); if(!bd.exists(m_buildDir)) bd.mkdir(m_buildDir); } } if (!m_overrideDeployDir.isEmpty()) m_deployDir = m_overrideDeployDir; } //build the make process arguments { if (isCMake || isQMake) { QStringList arguments; if(isCMake) { arguments << QStringLiteral("DESTDIR=%1").arg(m_deployDir) << QStringLiteral("install"); } else { arguments << QStringLiteral("INSTALL_ROOT=%1").arg(m_deployDir) << QStringLiteral("install"); } ProjectExplorer::ProcessParameters* params = &m_MakeParam; params->setMacroExpander(mExp); //setup process parameters params->setWorkingDirectory(m_buildDir); params->setCommand( makeCommand(ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit()), env)); params->setArguments(Utils::QtcProcess::joinArgs(arguments)); Utils::Environment tmpenv = env; // Force output to english for the parsers. Do this here and not in the toolchain's // addToEnvironment() to not screw up the users run environment. tmpenv.set(QLatin1String("LC_ALL"), QLatin1String("C")); params->setEnvironment(tmpenv); params->resolveAll(); } else { //QML and HTML projects are just rsynced for now QStringList arguments; arguments << QStringLiteral("-avh") //<< QStringLiteral("--delete") << QStringLiteral("--exclude")<setMacroExpander(mExp); //setup process parameters params->setWorkingDirectory(m_buildDir); params->setCommand(QStringLiteral("rsync")); params->setArguments(Utils::QtcProcess::joinArgs(arguments)); Utils::Environment tmpenv = env; // Force output to english for the parsers. Do this here and not in the toolchain's // addToEnvironment() to not screw up the users run environment. tmpenv.set(QLatin1String("LC_ALL"), QLatin1String("C")); params->setEnvironment(tmpenv); params->resolveAll(); } } //builds the click process arguments { QStringList arguments; arguments << QStringLiteral("build") << QStringLiteral("--no-validate") << m_deployDir; ProjectExplorer::ProcessParameters* params = &m_ClickParam; params->setMacroExpander(mExp); //setup process parameters params->setWorkingDirectory(clickWorkingDir()); params->setCommand(QLatin1String("click")); params->setArguments(Utils::QtcProcess::joinArgs(arguments)); Utils::Environment tmpEnv = env; // Force output to english for the parsers. Do this here and not in the toolchain's // addToEnvironment() to not screw up the users run environment. tmpEnv.set(QLatin1String("LC_ALL"), QLatin1String("C")); params->setEnvironment(tmpEnv); params->resolveAll(); } //builds the click review arguments { ProjectExplorer::ProcessParameters* params = &m_ReviewParam; params->setMacroExpander(mExp); //setup process parameters params->setWorkingDirectory(clickWorkingDir()); params->setCommand(QLatin1String(Constants::CLICK_REVIEWERSTOOLS_BINARY)); Utils::Environment tmpEnv = env; // Force output to english for the parsers. Do this here and not in the toolchain's // addToEnvironment() to not screw up the users run environment. tmpEnv.set(QLatin1String("LC_ALL"), QLatin1String("C")); params->setEnvironment(tmpEnv); } return; } void UbuntuPackageStep::run(QFutureInterface &fi) { internalInit(); if (m_tasks.size()) { foreach (const ProjectExplorer::Task& task, m_tasks) { addTask(task); } emit addOutput(tr("Configuration is invalid. Aborting build") ,ProjectExplorer::BuildStep::MessageOutput); fi.reportResult(false); emit finished(); return; } m_state = Idle; m_futureInterface = &fi; m_futureInterface->setProgressRange(0,4); QTimer::singleShot(0,this,SLOT(doNextStep())); } ProjectExplorer::BuildStepConfigWidget *UbuntuPackageStep::createConfigWidget() { return new UbuntuPackageStepConfigWidget(this); } bool UbuntuPackageStep::immutable() const { //do not allow the user to reorder or to delete the packaging step return true; } bool UbuntuPackageStep::runInGuiThread() const { return true; } bool UbuntuPackageStep::fromMap(const QVariantMap &map) { if(!BuildStep::fromMap(map)) return false; setDebugMode(AutoEnableDebugScript); if (map.contains(QLatin1String(PACKAGE_MODE_KEY))) { int mode = map[QLatin1String(PACKAGE_MODE_KEY)].toInt(); //AutoEnableDebugScript is deprecated, we always want the helper packaged if(mode == AutoEnableDebugScript) mode = EnableDebugScript; if(mode >= AutoEnableDebugScript && mode <= DisableDebugScript) setDebugMode(static_cast(mode)); } if (map.contains(QLatin1String(ERROR_MODE_KEY))) { setTreatClickErrorsAsWarnings(map[QLatin1String(ERROR_MODE_KEY)].toBool()); } return true; } QVariantMap UbuntuPackageStep::toMap() const { QVariantMap map = BuildStep::toMap(); if(map.isEmpty()) return map; map.insert(QLatin1String(PACKAGE_MODE_KEY),static_cast(m_debugMode)); map.insert(QLatin1String(ERROR_MODE_KEY),m_treatClickErrorsAsWarnings); return map; } QString UbuntuPackageStep::packagePath() const { if(m_clickPackageName.isEmpty()) return QString(); return clickWorkingDir() + QDir::separator() + m_clickPackageName; } UbuntuPackageStep::DebugMode UbuntuPackageStep::debugMode() const { return m_debugMode; } void UbuntuPackageStep::setDebugMode(UbuntuPackageStep::DebugMode arg) { if (m_debugMode != arg) { m_debugMode = arg; emit packageModeChanged(arg); } } /*! * \brief UbuntuPackageStep::setupAndStartProcess * Setups the interal QProcess and connects the required SIGNALS * also makes sure the process has a clean output parser */ void UbuntuPackageStep::setupAndStartProcess(const ProjectExplorer::ProcessParameters ¶ms) { if (m_process) { m_process->disconnect(this); m_process->kill(); m_process->deleteLater(); } QDir wd(params.effectiveWorkingDirectory()); if (!wd.exists()) wd.mkpath(wd.absolutePath()); QString effectiveCommand = params.effectiveCommand(); if (!QFileInfo(effectiveCommand).exists()) { onProcessFailedToStart(); return; } m_process = new Utils::QtcProcess(); connect(m_process,SIGNAL(finished(int)),this,SLOT(doNextStep())); connect(m_process,SIGNAL(readyReadStandardOutput()),this,SLOT(onProcessStdOut())); connect(m_process,SIGNAL(readyReadStandardError()),this,SLOT(onProcessStdErr())); m_process->setCommand(params.effectiveCommand(),params.effectiveArguments()); m_process->setEnvironment(params.environment()); m_process->setWorkingDirectory(wd.absolutePath()); emit addOutput(tr("Starting: \"%1 %2\"").arg(params.effectiveCommand(),params.effectiveArguments()), BuildStep::MessageOutput); ProjectExplorer::IOutputParser *parser = target()->kit()->createOutputParser(); //add special parser on click review step if(m_state == ClickReview) { UbuntuPackageOutputParser *packageStepParser = new UbuntuPackageOutputParser; packageStepParser->setTreatAllErrorsAsWarnings(m_treatClickErrorsAsWarnings); connect(this,SIGNAL(currentSubStepFinished()),packageStepParser,SLOT(setEndOfData())); if (parser) parser->appendOutputParser(packageStepParser); else parser = packageStepParser; } if(m_outputParserChain) { delete m_outputParserChain; m_outputParserChain = 0; } if(parser) { m_outputParserChain = parser; m_outputParserChain->setWorkingDirectory(params.effectiveWorkingDirectory()); connect(m_outputParserChain,SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)), this,SLOT(outputAdded(QString,ProjectExplorer::BuildStep::OutputFormat))); connect(m_outputParserChain,SIGNAL(addTask(ProjectExplorer::Task)), this,SLOT(taskAdded(ProjectExplorer::Task))); } m_process->start(); if(!m_process->waitForStarted()) { onProcessFailedToStart(); return; } } /*! * \brief UbuntuPackageStep::checkLastProcessSuccess * Checks if the last process has run without any errors */ bool UbuntuPackageStep::processFinished(FinishedCheckMode mode) { //make sure all data has been read QString line = QString::fromLocal8Bit(m_process->readAllStandardError()); if (!line.isEmpty()) stdError(line); line = QString::fromLocal8Bit(m_process->readAllStandardOutput()); if (!line.isEmpty()) stdOutput(line); emit currentSubStepFinished(); bool success = true; if (m_outputParserChain) { m_outputParserChain->flush(); if(m_outputParserChain->hasFatalErrors()) success = false; } if(success) { QString command; if(m_state == MakeInstall) command = QDir::toNativeSeparators(m_MakeParam.effectiveCommand()); else command = QDir::toNativeSeparators(m_ClickParam.effectiveCommand()); if (m_process->exitStatus() == QProcess::NormalExit && m_process->exitCode() == 0) { emit addOutput(tr("The process \"%1\" exited normally.").arg(command), BuildStep::MessageOutput); } else if (m_process->exitStatus() == QProcess::NormalExit) { emit addOutput(tr("The process \"%1\" exited with code %2.") .arg(command, QString::number(m_process->exitCode())), BuildStep::ErrorMessageOutput); if(mode == CheckReturnCode) //error success = false; else { emit addOutput(tr("Ignoring return code for this step"),BuildStep::ErrorMessageOutput); } } else { emit addOutput(tr("The process \"%1\" crashed.").arg(command), BuildStep::ErrorMessageOutput); //error success = false; } } //the process failed, lets clean up if (!success) { m_futureInterface->reportResult(false); cleanup(); emit finished(); } return success; } void UbuntuPackageStep::cleanup() { if (m_process) { m_process->disconnect(this); m_process->kill(); m_process->deleteLater(); m_process = 0; } //not owned by us m_futureInterface = 0; if (m_outputParserChain) { delete m_outputParserChain; m_outputParserChain = 0; } //reset params m_MakeParam = m_ClickParam = m_ReviewParam = ProjectExplorer::ProcessParameters(); } void UbuntuPackageStep::stdOutput(const QString &line) { m_lastLine = line; if (m_outputParserChain) m_outputParserChain->stdOutput(line); emit addOutput(line, BuildStep::NormalOutput, BuildStep::DontAppendNewline); } void UbuntuPackageStep::stdError(const QString &line) { if (m_outputParserChain) m_outputParserChain->stdError(line); emit addOutput(line, BuildStep::ErrorOutput, BuildStep::DontAppendNewline); } QString UbuntuPackageStep::makeCommand(ProjectExplorer::ToolChain *tc, const Utils::Environment &env) const { if (tc) return tc->makeCommand(env); return QString(); } /*! * \brief UbuntuPackageStep::injectDebugHelperStep * Checks if its required to inject the debug helpers and does that * accordingly */ void UbuntuPackageStep::injectDebugHelperStep() { ProjectExplorer::BuildConfiguration *bc = referenceBuildConfig(); if(!bc) { QTimer::singleShot(0,this,SLOT(doNextStep())); return; } bool ubuntuDevice = ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(bc->target()->kit()).toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID)); bool injectDebugScript = (m_debugMode == EnableDebugScript || m_debugMode == AutoEnableDebugScript); //debughelper script name, source and destination path const QString debScript = QStringLiteral("qtc_device_debughelper.py"); const QString debSourcePath = QStringLiteral("%1/%2").arg(Constants::UBUNTU_SCRIPTPATH).arg(debScript); if(ubuntuDevice) { QRegularExpression deskExecRegex(QStringLiteral("^(\\s*[Ee][Xx][Ee][cC]=.*)$"),QRegularExpression::MultilineOption); UbuntuClickManifest manifest; QString err; QString manifestFileName = Utils::FileName::fromString(m_deployDir) .appendPath(QStringLiteral("manifest.json")).toString(); if(!QFile::exists(manifestFileName)) { emit addOutput(tr("Could not find the manifest.json file in %1.\nPlease check if it is added to the install targets in your project file") .arg(m_deployDir), BuildStep::ErrorMessageOutput); m_futureInterface->reportResult(false); cleanup(); emit finished(); return; } if(!manifest.load(manifestFileName,nullptr,&err)) { emit addOutput(tr("Could not open the manifest.json file in %1.\n %2") .arg(m_deployDir) .arg(err), BuildStep::ErrorMessageOutput); m_futureInterface->reportResult(false); cleanup(); emit finished(); return; } QList hooks = manifest.hooks(); foreach ( const UbuntuClickManifest::Hook &hook, hooks) { UbuntuClickManifest appArmor; if (!appArmor.load(Utils::FileName::fromString(m_deployDir) .appendPath(hook.appArmorFile) .toString())) { qWarning()<<"Could not open the apparmor file for: "<kit()); if(!tc || tc->type() != QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) { qWarning()<<"Incompatible Toolchain for hook"<(tc)->gnutriplet()); QString commTemplate = QStringLiteral("./%S scope %1 %C") .arg(manifest.name()+QStringLiteral("_")+hook.appId); //tell our script the appid if(!UbuntuProjectHelper::injectScopeDebugHelper(iniFilePath, debScript, commTemplate, defaultSubCmd)) qWarning()<<"Could not write the updated ini file"; //copy the helper script to the click package tree if(QFile::exists(debSourcePath)) QFile::copy(debSourcePath,debTargetPath); } else if(!hook.desktopFile.isEmpty() && hook.scope.isEmpty()){ const QString debTargetPath = Utils::FileName::fromString(m_deployDir) .appendPath(debScript).toString(); //make sure there is no old script in case we don't want to package it if(QFile::exists(debTargetPath)) QFile::remove(debTargetPath); if(!injectDebugScript) continue; //copy the helper script to the click package tree if(QFile::exists(debSourcePath)) QFile::copy(debSourcePath,debTargetPath); //inject the debug helper into the Exec line in the desktop file //@BUG if there are env vars set in the Exec line (Var=something command) this will fail QFile deskFileFd(Utils::FileName::fromString(m_deployDir) .appendPath(hook.desktopFile) .toString()); if(!deskFileFd.open(QIODevice::ReadOnly)) continue; QString contents; { QTextStream deskInStream(&deskFileFd); contents = deskInStream.readAll(); } deskFileFd.close(); QRegularExpressionMatch m = deskExecRegex.match(contents); if(!m.hasMatch()) continue; QString exec = m.captured(1); int idxOfEq = exec.indexOf(QStringLiteral("=")); exec.remove(0,idxOfEq+1); //replaces the exec line with out patched version contents.replace(m.capturedStart(1), m.capturedLength(1), QStringLiteral("Exec=./%1 app \"%2\"").arg(debScript).arg(exec)); if(!deskFileFd.open(QIODevice::WriteOnly | QIODevice::Truncate)) { continue; } QTextStream outStream(&deskFileFd); outStream << contents; deskFileFd.close(); } else { qWarning()<<"Ambiguous configuration for hook "<buildType() == ProjectExplorer::BuildConfiguration::Debug) { //enable debugging in the apparmor file, this will inject the debug policy if (!appArmor.enableDebugging()) qWarning() <<"Could not inject debug policy, debugging with gdb will not work"; //} appArmor.save(); } } QTimer::singleShot(0,this,SLOT(doNextStep())); } void UbuntuPackageStep::doNextStep() { switch (m_state) { case Idle: { m_futureInterface->setProgressValueAndText(0,tr("Make install")); switch (m_packageMode) { case OnlyClickBuild: m_state = PreparePackage; doNextStep(); return; case Default: case OnlyMakeInstall: m_state = MakeInstall; if (m_cleanDeployDirectory && //paranoid double check m_deployDir.endsWith(QDir::separator()+QLatin1String(Constants::UBUNTU_DEPLOY_DESTDIR))) { //make sure we always use a clean deploy dir QDir deplDir(m_deployDir); if(deplDir.exists()) deplDir.removeRecursively(); } setupAndStartProcess(m_MakeParam); break; } break; } case MakeInstall: { if (!processFinished()) return; if (m_packageMode == OnlyMakeInstall) { m_futureInterface->reportResult(true); cleanup(); emit finished(); return; } m_futureInterface->setProgressValueAndText(1,tr("Preparing click package tree")); m_state = PreparePackage; //return to MainLoop to update the progressBar QTimer::singleShot(0,this,SLOT(injectDebugHelperStep())); break; } case PreparePackage: { m_futureInterface->setProgressValueAndText(2,tr("Building click package")); m_state = ClickBuild; m_lastLine.clear(); m_clickPackageName.clear(); setupAndStartProcess(m_ClickParam); break; } case ClickBuild: { if (!processFinished()) return; QRegularExpression exp(QLatin1String(Constants::UBUNTU_CLICK_SUCCESS_PACKAGE_REGEX)); QRegularExpressionMatch m = exp.match(m_lastLine); if(m.hasMatch()) { m_clickPackageName = m.captured(1); emit addOutput(tr("The click package has been created in %1").arg(clickWorkingDir()) , ProjectExplorer::BuildStep::MessageOutput); } m_futureInterface->setProgressValueAndText(3,tr("Reviewing click package")); m_state = ClickReview; m_ReviewParam.setArguments(QString::fromLatin1(Constants::CLICK_REVIEWERSTOOLS_ARGS).arg(packagePath())); m_ReviewParam.resolveAll(); setupAndStartProcess(m_ReviewParam); break; } case ClickReview: { //we need to ignore the return code for now, //until we have proper support for ignoring specific errors if (!processFinished(IgnoreReturnCode)) return; m_futureInterface->reportResult(true); cleanup(); emit finished(); } default: break; } } void UbuntuPackageStep::onProcessStdOut() { m_process->setReadChannel(QProcess::StandardOutput); while (m_process->canReadLine()) { QString line = QString::fromLocal8Bit(m_process->readLine()); stdOutput(line); } } void UbuntuPackageStep::onProcessStdErr() { m_process->setReadChannel(QProcess::StandardError); while (m_process->canReadLine()) { QString line = QString::fromLocal8Bit(m_process->readLine()); stdError(line); } } void UbuntuPackageStep::onProcessFailedToStart() { m_futureInterface->reportResult(false); ProjectExplorer::ProcessParameters *params; if (m_state == MakeInstall) params = &m_MakeParam; else params = &m_ClickParam; emit addOutput(tr("Could not start process \"%1\" %2") .arg(QDir::toNativeSeparators(params->effectiveCommand()), params->prettyArguments()), BuildStep::ErrorMessageOutput); emit finished(); cleanup(); } void UbuntuPackageStep::outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format) { emit addOutput(string, format, BuildStep::DontAppendNewline); } void UbuntuPackageStep::taskAdded(const ProjectExplorer::Task &task) { emit addTask(task); } bool UbuntuPackageStep::cleanDeployDirectory() const { return m_cleanDeployDirectory; } void UbuntuPackageStep::setCleanDeployDirectory(bool cleanDeployDirectory) { m_cleanDeployDirectory = cleanDeployDirectory; } bool UbuntuPackageStep::treatClickErrorsAsWarnings() const { return m_treatClickErrorsAsWarnings; } void UbuntuPackageStep::setTreatClickErrorsAsWarnings(bool arg) { if (m_treatClickErrorsAsWarnings != arg) { m_treatClickErrorsAsWarnings = arg; emit treatClickErrorsAsWarningsChanged(arg); } } ProjectExplorer::BuildConfiguration *UbuntuPackageStep::referenceBuildConfig() const { if(m_referenceBuildConfig) return m_referenceBuildConfig.data(); return target()->activeBuildConfiguration(); } void UbuntuPackageStep::setReferenceBuildConfig(ProjectExplorer::BuildConfiguration *referenceBuildConfig) { m_referenceBuildConfig = referenceBuildConfig; } QString UbuntuPackageStep::overrideClickWorkingDir() const { return m_overrideClickWorkingDir; } void UbuntuPackageStep::setOverrideClickWorkingDir(const QString &overrideClickWorkingDir) { m_overrideClickWorkingDir = overrideClickWorkingDir; } QString UbuntuPackageStep::clickWorkingDir() const { if (!m_overrideClickWorkingDir.isEmpty()) return m_overrideClickWorkingDir; return m_buildDir; } QString UbuntuPackageStep::overrideInstallDir() const { return m_overrideDeployDir; } void UbuntuPackageStep::setOverrideDeployDir(const QString &overrideInstallDir) { m_overrideDeployDir = overrideInstallDir; } UbuntuPackageStep::PackageMode UbuntuPackageStep::packageMode() const { return m_packageMode; } void UbuntuPackageStep::setPackageMode(const UbuntuPackageStep::PackageMode &packageMode) { m_packageMode = packageMode; } UbuntuPackageStepConfigWidget::UbuntuPackageStepConfigWidget(UbuntuPackageStep *step) : SimpleBuildStepConfigWidget(step), ui(new Ui::UbuntuPackageStepConfigWidget), m_isUpdating(false) { ui->setupUi(this); ui->comboBoxMode->addItem(tr("Yes") ,static_cast(UbuntuPackageStep::EnableDebugScript)); ui->comboBoxMode->addItem(tr("No") ,static_cast(UbuntuPackageStep::DisableDebugScript)); connect(step,SIGNAL(packageModeChanged(DebugMode)),this,SLOT(updateMode())); connect(step,SIGNAL(treatClickErrorsAsWarningsChanged(bool)),this,SLOT(updateMode())); connect(ui->comboBoxMode,SIGNAL(activated(int)),this,SLOT(onModeSelected(int))); connect(ui->checkBox,SIGNAL(clicked(bool)), this, SLOT(onClickErrorsToggled(bool))); updateMode(); } UbuntuPackageStepConfigWidget::~UbuntuPackageStepConfigWidget() { delete ui; } bool UbuntuPackageStepConfigWidget::showWidget() const { return true; } void UbuntuPackageStepConfigWidget::updateMode() { if (m_isUpdating) return; UbuntuPackageStep *myStep = static_cast(step()); int mode = myStep->debugMode(); m_isUpdating = true; int idx = ui->comboBoxMode->findData(mode); if(idx >= 0) ui->comboBoxMode->setCurrentIndex(idx); ui->checkBox->setChecked(myStep->treatClickErrorsAsWarnings()); m_isUpdating = false; } void UbuntuPackageStepConfigWidget::onModeSelected(const int index) { if (m_isUpdating) return; int mode = ui->comboBoxMode->itemData(index).toInt(); if ( mode >= UbuntuPackageStep::AutoEnableDebugScript && mode <= UbuntuPackageStep::DisableDebugScript ) { m_isUpdating = true; static_cast(step())->setDebugMode(static_cast(mode)); m_isUpdating = false; } } void UbuntuPackageStepConfigWidget::onClickErrorsToggled(const bool checked) { if (m_isUpdating) return; m_isUpdating = true; static_cast(step())->setTreatClickErrorsAsWarnings(checked); m_isUpdating = false; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubunturemoterunner.cpp0000644000015600001650000002463512705421114017711 0ustar jenkinsjenkins#include "ubunturemoterunner.h" #include "ubuntudevice.h" #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { const QString SSH_BASE_COMMAND = QStringLiteral("ssh -i %1 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p%2 %3@%4"); enum { debug = 0 }; class UbuntuRemoteClickApplicationRunnerPrivate { public: UbuntuRemoteClickApplicationRunnerPrivate() : m_cppDebugPort(0), m_qmlDebugPort(0), m_launcherPid(-1), m_appPid(-1), m_stopRequested(false), m_forceInstall(false), m_uninstall(true) { } quint16 m_cppDebugPort; quint16 m_qmlDebugPort; Utils::Environment m_env; QPointer m_proc; UbuntuDevice::ConstPtr m_dev; QString m_packageName; QString m_hook; QString m_launcherOutput; int m_launcherPid; int m_appPid; bool m_stopRequested; bool m_forceInstall; bool m_uninstall; }; UbuntuRemoteClickApplicationRunner::UbuntuRemoteClickApplicationRunner(QObject *parent) : QObject(parent), d (new UbuntuRemoteClickApplicationRunnerPrivate()) { } UbuntuRemoteClickApplicationRunner::~UbuntuRemoteClickApplicationRunner() { delete d; } void UbuntuRemoteClickApplicationRunner::start( UbuntuDevice::ConstPtr device, const QString &clickPackageName, const QString &hook) { QTC_ASSERT(d->m_proc.isNull(), return); QTC_ASSERT(device, return); d->m_dev = device; d->m_proc = new QProcess(this); d->m_stopRequested = false; d->m_packageName = clickPackageName; d->m_hook = hook; connect(d->m_proc,SIGNAL(finished(int)),this,SLOT(handleLauncherProcessFinished())); connect(d->m_proc,SIGNAL(readyReadStandardOutput()),this,SLOT(handleLauncherStdOut())); connect(d->m_proc,SIGNAL(readyReadStandardError()),this,SLOT(handleLauncherStdErr())); connect(d->m_proc,SIGNAL(error(QProcess::ProcessError)),this,SLOT(handleLauncherProcessError(QProcess::ProcessError))); QStringList args; args << QStringLiteral("/tmp/%1").arg(clickPackageName) << QStringLiteral("--hook") << hook; Utils::Environment &env = d->m_env; Utils::Environment::const_iterator i = env.constBegin(); for(;i!=env.constEnd();i++) { args << QStringLiteral("--env") << QStringLiteral("%1:%2").arg(i.key()).arg(i.value()); } if (d->m_qmlDebugPort > 0) args.append(QStringLiteral("--qmldebug=port:%1,block").arg(d->m_qmlDebugPort)); if (d->m_cppDebugPort > 0) args.append(QStringLiteral("--cppdebug=%1").arg(d->m_cppDebugPort)); if (d->m_forceInstall) args.append(QStringLiteral("--force-install")); if (!d->m_uninstall) args.append(QStringLiteral("--no-uninstall")); #if 0 QString subCommand = QStringLiteral("cd /tmp && ./qtc_device_applaunch.py %1") .arg(Utils::QtcProcess::joinArgs(args)); QString command = QStringLiteral("adb"); QStringList adbArgs = QStringList() << QStringLiteral("-s") << d->m_dev->serialNumber() << QStringLiteral("shell") << QStringLiteral("sudo") << QStringLiteral("-i") << QStringLiteral("-u") << d->m_dev->sshParameters().userName << QStringLiteral("bash") << QStringLiteral("-c") << subCommand; if(debug) qDebug()<<"Starting application: "<m_proc->setProgram(command); d->m_proc->setArguments(adbArgs); d->m_launcherOutput.clear(); d->m_proc->start(); #else QString subCommand = QStringLiteral("bash -l -c '/tmp/qtc_device_applaunch.py %1'") .arg(Utils::QtcProcess::joinArgs(args)); QSsh::SshConnectionParameters params = device->sshParameters(); QString command = SSH_BASE_COMMAND .arg(params.privateKeyFile) .arg(params.port) .arg(params.userName) .arg(params.host); QStringList sshArgs = Utils::QtcProcess::splitArgs(command) << subCommand; command = sshArgs.takeFirst(); if(debug) qDebug()<<"Starting application: "<m_proc->setProgram(command); d->m_proc->setArguments(sshArgs); d->m_launcherOutput.clear(); d->m_proc->start(); #endif } void UbuntuRemoteClickApplicationRunner::stop() { if (d->m_launcherPid > 0 || d->m_appPid > 0) { int success = QProcess::execute(QStringLiteral("adb"), QStringList() << QStringLiteral("-s") << d->m_dev->serialNumber() << QStringLiteral("shell") << QStringLiteral("kill") << QStringLiteral("-SIGINT") << QString::number(d->m_appPid > 0 ? d->m_appPid : d->m_launcherPid)); if( success != 0 ) emit reportError(tr("Could not stop the application")); } else d->m_stopRequested = true; } quint16 UbuntuRemoteClickApplicationRunner::cppDebugPort() const { return d->m_cppDebugPort; } void UbuntuRemoteClickApplicationRunner::setCppDebugPort(const quint16 &cppDebugPort) { d->m_cppDebugPort = cppDebugPort; } quint16 UbuntuRemoteClickApplicationRunner::qmlDebugPort() const { return d->m_qmlDebugPort; } void UbuntuRemoteClickApplicationRunner::setQmlDebugPort(const quint16 &qmlDebugPort) { d->m_qmlDebugPort = qmlDebugPort; } Utils::Environment UbuntuRemoteClickApplicationRunner::env() const { return d->m_env; } void UbuntuRemoteClickApplicationRunner::setEnv(const Utils::Environment &env) { d->m_env = env; } void UbuntuRemoteClickApplicationRunner::setForceInstall(const bool set) { d->m_forceInstall = set; } void UbuntuRemoteClickApplicationRunner::setUninstall(const bool set) { d->m_uninstall = set; } void UbuntuRemoteClickApplicationRunner::cleanup(CleanupMode mode) { d->m_proc->disconnect(this); d->m_proc->deleteLater(); d->m_proc.clear(); d->m_launcherPid = -1; d->m_appPid = -1; d->m_stopRequested = false; d->m_dev.clear(); if(mode == CleanSettings) { d->m_cppDebugPort = 0; d->m_qmlDebugPort = 0; d->m_packageName.clear(); d->m_hook.clear(); d->m_forceInstall = false; d->m_uninstall = true; d->m_env.clear(); } } void UbuntuRemoteClickApplicationRunner::handleLauncherProcessError(QProcess::ProcessError error) { emit reportError(tr("Error launching the application: %1 %2").arg(error).arg(d->m_proc->errorString())); emit finished(false); cleanup(); } void UbuntuRemoteClickApplicationRunner::handleLauncherProcessFinished() { int exitCode = d->m_proc->exitCode(); if(exitCode == 100) { //Application already installed int choice = QMessageBox::question(Core::ICore::mainWindow(), tr("Application already installed"), tr("The Application is already installed on the device, do you want to override it?\n(This will uninstall the application completely after the debug session.)")); if(choice == QMessageBox::Yes) { //restart UbuntuDevice::ConstPtr dev = d->m_dev; cleanup(KeepSettings); setForceInstall(true); start(dev, d->m_packageName,d->m_hook); return; } } emit finished( d->m_proc->exitCode() == 0 ); cleanup(); } void UbuntuRemoteClickApplicationRunner::handleLauncherStdOut() { QTC_ASSERT(!d->m_proc.isNull(),return); QByteArray output = d->m_proc->readAllStandardOutput(); emit launcherStdout(output); } void UbuntuRemoteClickApplicationRunner::handleLauncherStdErr() { QTC_ASSERT(!d->m_proc.isNull(),return); QByteArray output = d->m_proc->readAllStandardError(); d->m_launcherOutput.append( QString::fromUtf8(output) ); while(true) { int idx = d->m_launcherOutput.indexOf(QStringLiteral("\n")); if (idx < 0) break; QString line = d->m_launcherOutput.mid(0,idx).trimmed(); d->m_launcherOutput = d->m_launcherOutput.mid(idx+1); if (debug) { qDebug()<<"Processing Line: "<m_launcherPid <= 0) { QRegularExpression exp (QStringLiteral("Launcher PID: ([0-9]+)")); QRegularExpressionMatch match = exp.match(line); if(match.hasMatch()) { bool ok = false; d->m_launcherPid = match.captured(1).toInt(&ok); if(!ok) d->m_launcherPid = -1; else { if(debug) qDebug()<<"Launcher PID: "<m_launcherPid; if(d->m_stopRequested) stop(); else emit launcherProcessStarted(d->m_launcherPid); } } } if (d->m_appPid <= 0) { QRegularExpression exp (QStringLiteral("Application started: ([0-9]+)")); QRegularExpressionMatch match = exp.match(line); if(match.hasMatch()) { bool ok = false; d->m_appPid = match.captured(1).toInt(&ok); if(!ok) d->m_appPid = -1; else { if(debug) { qDebug()<<"Application PID: "<m_appPid; } emit clickApplicationStarted(d->m_appPid); } } } if (line.startsWith(QStringLiteral("Syslog>")) && line.contains(QStringLiteral("apparmor=\"DENIED\""))) { if (debug) { qDebug()<<"Found a AppArmor denial"; } //remove the prompt line = line.mid(7); if (debug) { qDebug()<<"Reporting a AppArmor denial "; } ProjectExplorer::TaskHub::addTask(ProjectExplorer::Task::Error, tr("There has been a AppArmor denial for the application. It usually means it is missing a policy in the AppArmor file:\n%1").arg(line), ProjectExplorer::Constants::TASK_CATEGORY_DEPLOYMENT); } } emit launcherStderr(output); } } } ./src/ubuntu/ubuntulocalscopedebugsupport.h0000644000015600001650000000306412705421114021412 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTULOCALSCOPEDEBUGSUPPORT_H #define UBUNTU_INTERNAL_UBUNTULOCALSCOPEDEBUGSUPPORT_H #include #include #include namespace Debugger{ class DebuggerRunControl; } namespace Ubuntu { namespace Internal { class UbuntuLocalRunConfiguration; class UbuntuLocalScopeDebugSupport : public QObject { Q_OBJECT public: explicit UbuntuLocalScopeDebugSupport(UbuntuLocalRunConfiguration *runConfig, Debugger::DebuggerRunControl *runControl, const QString &scopeRunnerPath); virtual ~UbuntuLocalScopeDebugSupport(); ProjectExplorer::ApplicationLauncher::Mode appLauncherMode() const; void setAppLauncherMode(const ProjectExplorer::ApplicationLauncher::Mode &appLauncherMode); private: quint16 getLocalPort() const; private slots: void handleRemoteSetupRequested(); void handleProcessStarted(); void handleProcessExited(int exitCode, QProcess::ExitStatus); void handleError(QProcess::ProcessError error); void handleStateChanged(Debugger::DebuggerState state); private: quint16 m_port; QString m_scopeRunnerPath; QString m_executable; QString m_commandLineArguments; Debugger::DebuggerRunControl *m_runControl; ProjectExplorer::ApplicationLauncher m_launcher; ProjectExplorer::ApplicationLauncher::Mode m_appLauncherMode; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTULOCALSCOPEDEBUGSUPPORT_H ./src/ubuntu/ubuntuproject.h0000644000015600001650000000632512705421114016273 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPROJECT_H #define UBUNTUPROJECT_H #include "ubuntuprojectmanager.h" #include "ubuntuprojectfile.h" #include "ubuntuprojectnode.h" #include "ubuntuconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuProjectManager; class UbuntuProjectFile; class UbuntuProjectNode; class UbuntuKitMatcher : public ProjectExplorer::KitMatcher { public: explicit UbuntuKitMatcher(); static bool matches(const ProjectExplorer::Kit *k); }; class UbuntuProject : public ProjectExplorer::Project { Q_OBJECT public: UbuntuProject(UbuntuProjectManager *manager, const QString &fileName); QString displayName() const override; Core::IDocument *document() const override; ProjectExplorer::IProjectManager *projectManager() const override; ProjectExplorer::ProjectNode *rootProjectNode() const override; QStringList files(FilesMode fileMode) const override; QDir projectDir() const { return projectDirectory().toString(); } QString filesFileName() const { return m_fileName; } QString mainFile() const { return m_mainFile; } // Project interface bool supportsKit(ProjectExplorer::Kit *k, QString *errorMessage) const override; bool needsConfiguration() const override; bool requiresTargetPanel() const override; static QString shadowBuildDirectory(const QString &proFilePath, const ProjectExplorer::Kit *k, const QString &suffix = QString()); private: void extractProjectFileData(const QString& filename); UbuntuProjectManager *m_manager; QString m_projectName; QSharedPointer m_file; QString m_mainFile; QString m_fileName; QSharedPointer m_rootNode; }; } } #endif // UBUNTUPROJECT_H ./src/ubuntu/ubuntuversion.h0000644000015600001650000000262012705421114016304 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUVERSION_H #define UBUNTUVERSION_H #include #include #include "ubuntuconstants.h" namespace Ubuntu { namespace Internal { class UbuntuVersion : public QObject { Q_OBJECT public: explicit UbuntuVersion(); public slots: QString id() { return m_id; } QString release() { return m_release; } QString codename() { return m_codename; } QString description() { return m_description; } Core::FeatureSet features(); static UbuntuVersion *fromLsbFile( const QString &fileName ); protected: QString m_id; QString m_release; QString m_codename; QString m_description; }; } } #endif // UBUNTUVERSION_H ./src/ubuntu/abstractremoterunsupport.cpp0000644000015600001650000000722212705421114021113 0ustar jenkinsjenkins#include "abstractremoterunsupport.h" #include "ubunturemoterunner.h" #include "ubunturemoterunconfiguration.h" #include "ubuntudevice.h" #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class AbstractRemoteRunSupportPrivate { public: AbstractRemoteRunSupportPrivate( const UbuntuRemoteRunConfiguration *runConfig ) : clickPackage(runConfig->clickPackage()), hook(runConfig->appId()), dev(ProjectExplorer::DeviceKitInformation::device(runConfig->target()->kit())), env(runConfig->environment()), freePorts(dev->freePorts()), m_state(AbstractRemoteRunSupport::Idle){} QString clickPackage; QString hook; const ProjectExplorer::IDevice::ConstPtr dev; Utils::Environment env; Utils::PortList freePorts; UbuntuRemoteClickApplicationRunner runner; AbstractRemoteRunSupport::State m_state; ProjectExplorer::DeviceUsedPortsGatherer portScanner; }; AbstractRemoteRunSupport::AbstractRemoteRunSupport(UbuntuRemoteRunConfiguration* runConfig, QObject *parent) : QObject(parent), d(new AbstractRemoteRunSupportPrivate(runConfig)) { d->runner.setForceInstall(runConfig->forceInstall()); d->runner.setUninstall(runConfig->uninstall()); } AbstractRemoteRunSupport::~AbstractRemoteRunSupport() { delete d; } AbstractRemoteRunSupport::State AbstractRemoteRunSupport::state() const { return d->m_state; } void AbstractRemoteRunSupport::setState(const State &state) { d->m_state = state; } ProjectExplorer::IDevice::ConstPtr AbstractRemoteRunSupport::device() const { return d->dev; } QString AbstractRemoteRunSupport::clickPackage() const { return d->clickPackage; } QString AbstractRemoteRunSupport::hook() const { return d->hook; } Utils::Environment AbstractRemoteRunSupport::environment() const { return d->env; } UbuntuRemoteClickApplicationRunner *AbstractRemoteRunSupport::appRunner() const { return &d->runner; } bool AbstractRemoteRunSupport::assignNextFreePort(int *port) { *port = d->portScanner.getNextFreePort(&d->freePorts); if (*port == -1) { handleAdapterSetupFailed(tr("Not enough free ports on device for debugging.")); return false; } return true; } void AbstractRemoteRunSupport::setFinished() { if (d->m_state == Idle) return; if (d->m_state == Running) d->runner.stop(); d->m_state = Idle; } void AbstractRemoteRunSupport::reset() { d->portScanner.disconnect(this); d->runner.disconnect(this); d->m_state = Idle; } void AbstractRemoteRunSupport::handleRemoteSetupRequested() { QTC_ASSERT(d->m_state == Idle, return); d->m_state = ScanningPorts; connect(&d->portScanner,SIGNAL(portListReady()),this,SLOT(handlePortScannerReady())); connect(&d->portScanner,SIGNAL(error(QString)),this,SLOT(handlePortScannerError(QString))); d->portScanner.start(d->dev); } void AbstractRemoteRunSupport::handleAdapterSetupDone() { d->m_state = Running; } void AbstractRemoteRunSupport::handlePortScannerReady() { QTC_ASSERT(d->m_state == ScanningPorts, return); d->freePorts = d->dev->freePorts(); startExecution(); } void AbstractRemoteRunSupport::handlePortScannerError(const QString &message) { QTC_ASSERT( d->m_state == ScanningPorts , return ); handleAdapterSetupFailed(message); } void AbstractRemoteRunSupport::handleAdapterSetupFailed(const QString &) { setFinished(); reset(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntusecuritypolicypickerdialog.ui0000644000015600001650000001374612705421114022465 0ustar jenkinsjenkins UbuntuSecurityPolicyPickerDialog 0 0 744 404 Add Security Policies 1 Qt::Vertical 20 40 Reading available policy groups from device.. 0 0 Qt::Vertical 20 40 Qt::StrongFocus Available Policy Groups 200 16777215 Qt::StrongFocus false true QAbstractItemView::ExtendedSelection QTextEdit::NoWrap true Qt::Vertical 20 40 Could not automatically detect policy groups (device not connected). Policy group: Qt::Vertical 20 40 Cancel Qt::Horizontal 40 20 Add listViewPolicyGroups textBrowserEditInfo pushButtonAdd pushButtonCancel lineEditPolicyGroup ./src/ubuntu/ubuntucmakemakestep.cpp0000644000015600001650000001267612705421114020000 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntucmakemakestep.h" #include "ubuntuconstants.h" #include "ubuntuprojecthelper.h" #include "clicktoolchain.h" #include "ubuntupackagestep.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuCMakeMakeStepFactory * Factory class to create UbuntuCMakeMakeStep * build steps */ QList UbuntuCMakeMakeStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const { if(!canHandle(parent->target())) return QList(); return QList() << Core::Id(Constants::UBUNTU_CLICK_CMAKE_MAKESTEP_ID); } /*! * \brief UbuntuCMakeBuildConfigurationFactory::canHandle * checks if we can create buildconfigurations for the given target */ bool UbuntuCMakeMakeStepFactory::canHandle(const ProjectExplorer::Target *t) const { QTC_ASSERT(t, return false); if (!t->project()->supportsKit(t->kit())) return false; if(ProjectExplorer::DeviceKitInformation::deviceId(t->kit()) != ProjectExplorer::Constants::DESKTOP_DEVICE_ID) { ProjectExplorer::ToolChain* tc = ProjectExplorer::ToolChainKitInformation::toolChain(t->kit()); if(!tc || tc->type() != QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) return false; } return t->project()->id() == Core::Id(CMakeProjectManager::Constants::CMAKEPROJECT_ID); } QString UbuntuCMakeMakeStepFactory::displayNameForId(const Core::Id id) const { if (id == Constants::UBUNTU_CLICK_CMAKE_MAKESTEP_ID) return tr("UbuntuSDK-Make", "Display name for UbuntuCMakeMakeStep id."); return QString(); } bool UbuntuCMakeMakeStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const { if (canHandle(parent->target())) return availableCreationIds(parent).contains(id); return false; } ProjectExplorer::BuildStep *UbuntuCMakeMakeStepFactory::create(ProjectExplorer::BuildStepList *parent, const Core::Id id) { if (!canCreate(parent, id)) return 0; UbuntuCMakeMakeStep *step = new UbuntuCMakeMakeStep(parent); if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_CLEAN) { step->setUseNinja(false); step->setClean(true); step->setAdditionalArguments(QLatin1String("clean")); } return step; } bool UbuntuCMakeMakeStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const { Core::Id toRestore = ProjectExplorer::idFromMap(map); return canCreate(parent, toRestore); } ProjectExplorer::BuildStep *UbuntuCMakeMakeStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) { if (!canRestore(parent, map)) return 0; Core::Id toRestore = ProjectExplorer::idFromMap(map); ProjectExplorer::BuildStep* step = create(parent,toRestore); if(step->fromMap(map)) return step; delete step; return 0; } bool UbuntuCMakeMakeStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const { return canCreate(parent, product->id()); } ProjectExplorer::BuildStep *UbuntuCMakeMakeStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) { if (!canClone(parent, product)) return 0; if(product->id() == Core::Id(Constants::UBUNTU_CLICK_CMAKE_MAKESTEP_ID)) return new UbuntuCMakeMakeStep(parent, static_cast(product)); QTC_ASSERT(false,return 0); } /*! * \class UbuntuCMakeMakeStep * Represents a make or make clean call in the Ubuntu-SDK build chain */ UbuntuCMakeMakeStep::UbuntuCMakeMakeStep(ProjectExplorer::BuildStepList *bsl) : MakeStep(bsl,Core::Id(Constants::UBUNTU_CLICK_CMAKE_MAKESTEP_ID)) { setDefaultDisplayName(tr("Ubuntu SDK Make")); } UbuntuCMakeMakeStep::UbuntuCMakeMakeStep(ProjectExplorer::BuildStepList *bsl, UbuntuCMakeMakeStep *bs) : MakeStep(bsl,bs) { } UbuntuCMakeMakeStep::~UbuntuCMakeMakeStep() { } QString UbuntuCMakeMakeStep::makeCommand(ProjectExplorer::ToolChain *tc, const Utils::Environment &env) const { if (tc) return tc->makeCommand(env); return QString::fromLatin1(Constants::UBUNTU_CLICK_MAKE_WRAPPER).arg(Constants::UBUNTU_SCRIPTPATH); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuprojecthelper.h0000644000015600001650000000362412705421114017472 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUPROJECTGUESSER_H #define UBUNTU_INTERNAL_UBUNTUPROJECTGUESSER_H #include #include namespace ProjectExplorer { class Project; class Target; } namespace Ubuntu { namespace Internal { class UbuntuProjectHelper { public: UbuntuProjectHelper(); static Utils::FileName findScopesIniRecursive (const Utils::FileName &searchdir, const QString &appid); static Utils::FileName findFileRecursive (const Utils::FileName &searchdir, const QString ®exp); static Utils::FileName findFileRecursive (const Utils::FileName &searchdir, const QRegularExpression ®exp); static QList findFilesRecursive(const Utils::FileName &searchdir, const QRegularExpression ®exp); static QString getManifestPath (ProjectExplorer::Project * p, const QString &defaultValue); static QString getManifestPath(ProjectExplorer::Target *target, const QString &defaultValue); static bool injectScopeDebugHelper (const QString &iniFilePath, const QString &scriptName, const QString &commandTemplate, const QString &defaultSubCmd); }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUPROJECTGUESSER_H ./src/ubuntu/ubuntucmaketool.cpp0000644000015600001650000000457512705421114017143 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntucmaketool.h" #include "ubuntuconstants.h" #include namespace Ubuntu { namespace Internal { /*! * \brief createId * Adds a suffix to every tool id, so every kit has its * own cmake instance */ static Core::Id createId() { Core::Id newId(Constants::UBUNTU_CLICK_CMAKE_TOOL_ID); return newId.withSuffix(QUuid::createUuid().toString()); } UbuntuCMakeTool::UbuntuCMakeTool(QObject *parent) : CMakeProjectManager::CMakeTool(parent) , m_hasEnvironment(false) { setId(createId()); } UbuntuCMakeTool::UbuntuCMakeTool(const Core::Id &id, QObject *parent) : CMakeProjectManager::CMakeTool(parent) , m_hasEnvironment(false) { setId(id); } QString UbuntuCMakeTool::displayName() { return tr("Ubuntu Chroot CMake"); } void UbuntuCMakeTool::addToEnvironment(Utils::Environment &env) const { Utils::Environment::const_iterator i = m_env.constBegin(); for(;i != m_env.constEnd();i++) env.set(i.key(),i.value()); } bool UbuntuCMakeTool::isValid() const { if(!m_hasEnvironment) return false; return CMakeTool::isValid(); } void UbuntuCMakeTool::setEnvironment(const Utils::Environment &env) { m_hasEnvironment = true; m_env = env; setCMakeExecutable(QString::fromLatin1(Constants::UBUNTU_CLICK_CMAKE_WRAPPER).arg(Constants::UBUNTU_SCRIPTPATH)); } bool UbuntuCMakeToolFactory::canCreate(const Core::Id &id) const { return (id.toString().startsWith(QLatin1String(Constants::UBUNTU_CLICK_CMAKE_TOOL_ID))); } CMakeProjectManager::ICMakeTool *UbuntuCMakeToolFactory::create(const Core::Id &id) { if(!canCreate(id)) return 0; return new UbuntuCMakeTool(id); } } //namespace Internal } //namespace Ubuntu ./src/ubuntu/ubuntucreatenewchrootdialog.h0000644000015600001650000000276512705421114021205 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUCREATENEWCHROOTDIALOG_H #define UBUNTU_INTERNAL_UBUNTUCREATENEWCHROOTDIALOG_H #include #include #include "ubuntuclicktool.h" namespace Ubuntu { namespace Internal { namespace Ui { class UbuntuCreateNewChrootDialog; } class UbuntuCreateNewChrootDialog : public QDialog { Q_OBJECT public: explicit UbuntuCreateNewChrootDialog(const QString &arch = QString(), const QString &framework = QString(), QWidget *parent = 0); ~UbuntuCreateNewChrootDialog(); static bool getNewChrootTarget(UbuntuClickTool::Target *target, const QString &arch, const QString &framework, QWidget *parent = 0); private: Ui::UbuntuCreateNewChrootDialog *ui; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUCREATENEWCHROOTDIALOG_H ./src/ubuntu/localportsmanager.h0000644000015600001650000000301212705421114017065 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_LOCALPORTSMANAGER_H #define UBUNTU_INTERNAL_LOCALPORTSMANAGER_H #include #include #include namespace Ubuntu { namespace Internal { class UbuntuLocalPortsManager : public QObject { Q_OBJECT public: explicit UbuntuLocalPortsManager(); ~UbuntuLocalPortsManager(); static void setPortsRange (const int first, const int last); static Utils::PortList getFreeRange ( const QString &serial, const int count); static Utils::PortList getFreeRange ( const QString &serial, const int count, QIODevice *in ); signals: public slots: private: static UbuntuLocalPortsManager *m_instance; int m_first; int m_last; Utils::PortList m_usedPorts; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_LOCALPORTSMANAGER_H ./src/ubuntu/ubuntulocaldeployconfiguration.cpp0000644000015600001650000001077512705421114022263 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntulocaldeployconfiguration.h" #include "ubuntuprojecthelper.h" #include "ubuntuconstants.h" #include "ubuntupackagestep.h" #include #include #include #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuLocalDeployConfigurationFactory * Creates the required deploy configuration for locally * run click apps. Click apps need a extra step to group * all components into one directory and make them easily executable * * \note NOT IN USE CURRENTLY */ UbuntuLocalDeployConfigurationFactory::UbuntuLocalDeployConfigurationFactory(QObject *parent) :DeployConfigurationFactory(parent) { } QList UbuntuLocalDeployConfigurationFactory::availableCreationIds(ProjectExplorer::Target *parent) const { QList ids; if (!parent->project()->supportsKit(parent->kit())) return ids; if(ProjectExplorer::DeviceKitInformation::deviceId(parent->kit()) != ProjectExplorer::Constants::DESKTOP_DEVICE_ID) return ids; ids << Core::Id(Constants::UBUNTU_LOCAL_DEPLOYCONFIGURATION_ID); return ids; } QString UbuntuLocalDeployConfigurationFactory::displayNameForId(const Core::Id id) const { if( Core::Id(Constants::UBUNTU_LOCAL_DEPLOYCONFIGURATION_ID) == id ) return tr("Ubuntu SDK Local Deployment"); return QString(); } bool UbuntuLocalDeployConfigurationFactory::canCreate(ProjectExplorer::Target *parent, const Core::Id id) const { return availableCreationIds(parent).contains(id); } ProjectExplorer::DeployConfiguration *UbuntuLocalDeployConfigurationFactory::create(ProjectExplorer::Target *parent, const Core::Id id) { if(!canCreate(parent,id)) return 0; UbuntuLocalDeployConfiguration* conf = new UbuntuLocalDeployConfiguration(parent,id); ProjectExplorer::BuildStepList* steps = conf->stepList(); UbuntuPackageStep* depl = new UbuntuPackageStep(steps); steps->insertStep(0,depl); return conf; } bool UbuntuLocalDeployConfigurationFactory::canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const { return canCreate(parent, ProjectExplorer::idFromMap(map)); } ProjectExplorer::DeployConfiguration *UbuntuLocalDeployConfigurationFactory::restore(ProjectExplorer::Target *parent, const QVariantMap &map) { if (!canRestore(parent, map)) return 0; Core::Id id = ProjectExplorer::idFromMap(map); UbuntuLocalDeployConfiguration* const dc = new UbuntuLocalDeployConfiguration(parent, id); if (!dc->fromMap(map)) { delete dc; return 0; } return dc; } ProjectExplorer::DeployConfiguration *UbuntuLocalDeployConfigurationFactory::clone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *product) { if (!canClone(parent, product)) return 0; return new UbuntuLocalDeployConfiguration(parent,qobject_cast(product)); } /// // UbuntuLocalDeployConfiguration /// UbuntuLocalDeployConfiguration::UbuntuLocalDeployConfiguration(ProjectExplorer::Target *target, const Core::Id id) : DeployConfiguration(target, id) { setDefaultDisplayName(tr("UbuntuSDK Deploy locally")); //this should run after the configurations are set up //and selects us as the default one, its ugly but works QTimer::singleShot(0,this,SLOT(selectAsDefaultHack())); } UbuntuLocalDeployConfiguration::UbuntuLocalDeployConfiguration(ProjectExplorer::Target *target, UbuntuLocalDeployConfiguration *source) : DeployConfiguration(target, source) { cloneSteps(source); } void UbuntuLocalDeployConfiguration::selectAsDefaultHack() { target()->setActiveDeployConfiguration(this); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntudevicesignaloperation.cpp0000644000015600001650000000540712705421114021536 0ustar jenkinsjenkins#include "ubuntudevicesignaloperation.h" #include "ubuntuconstants.h" #include namespace Ubuntu { namespace Internal { UbuntuDeviceSignalOperation::UbuntuDeviceSignalOperation(UbuntuDevice::ConstPtr device) : m_device(device) { } void UbuntuDeviceSignalOperation::killProcess(int pid) { sendSignal(pid,9); } void UbuntuDeviceSignalOperation::killProcess(const QString &filePath) { Q_UNUSED(filePath) emit finished(tr("Sending signals to processes by filePath is not supported on Ubuntu Devices")); } void UbuntuDeviceSignalOperation::interruptProcess(int pid) { sendSignal(pid,2); } void UbuntuDeviceSignalOperation::interruptProcess(const QString &filePath) { Q_UNUSED(filePath) emit finished(tr("Sending signals to processes by filePath is not supported on Ubuntu Devices")); } void UbuntuDeviceSignalOperation::sendSignal(int pid, int signal) { QProcess *proc = new QProcess(this); if(!m_device) { emit finished(tr("There was a internal error when trying to kill the process")); return; } proc->setProgram(QStringLiteral("adb")); proc->setArguments(QStringList() << QStringLiteral("-s") << m_device->serialNumber() << QStringLiteral("shell") << QStringLiteral("kill -%1").arg(signal) << QString::number(pid)); connect(proc,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(processFinished(int,QProcess::ExitStatus))); connect(proc,SIGNAL(finished(int,QProcess::ExitStatus)),proc,SLOT(deleteLater())); connect(proc,SIGNAL(error(QProcess::ProcessError)),this,SLOT(processError(QProcess::ProcessError))); proc->start(); } void UbuntuDeviceSignalOperation::processFinished(int exitCode ,QProcess::ExitStatus exitState) { if(exitCode == 0 && exitState == QProcess::NormalExit) { emit finished(QString()); return; } QString error = QStringLiteral("Can not kill the process. Exit Code: %1").arg(exitCode); QProcess *proc = qobject_cast(sender()); if(proc) { QString err; if(exitState != QProcess::NormalExit) err = proc->errorString(); else err = QString::fromLatin1(proc->readAllStandardError()); if(!err.isEmpty()) error.append(QStringLiteral("\n%1").arg(err)); } emit finished(error); } void UbuntuDeviceSignalOperation::processError(QProcess::ProcessError procErr) { QProcess *proc = qobject_cast(sender()); if(proc) { proc->deleteLater(); QString error = QStringLiteral("Can not kill the process. Error: %1 %2").arg(procErr).arg(proc->errorString()); emit finished(error); } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntusettingsprojectdefaultspage.cpp0000644000015600001650000000364112705421114022772 0ustar jenkinsjenkins#include "ubuntusettingsprojectdefaultspage.h" #include "ubuntuconstants.h" #include "settings.h" #include namespace Ubuntu { namespace Internal { UbuntuSettingsProjectDefaultsPage::UbuntuSettingsProjectDefaultsPage(QObject *parent) : Core::IOptionsPage(parent), m_ui(0) { setId("B.ProjectDefaults"); setDisplayName(tr("Project defaults")); setCategory("Ubuntu"); setDisplayCategory(QLatin1String("Ubuntu")); setCategoryIcon(QLatin1String(Ubuntu::Constants::UBUNTU_SETTINGS_ICON)); } UbuntuSettingsProjectDefaultsPage::~UbuntuSettingsProjectDefaultsPage() { if (m_ui) delete m_ui; } QWidget *UbuntuSettingsProjectDefaultsPage::widget() { if (!m_widget) { m_widget = new QWidget(); m_ui = new ::Ui::UbuntuSettingsDefaultPage; m_ui->setupUi(m_widget); Settings::ProjectDefaults def = Settings::projectDefaults(); m_ui->checkBoxDebugHelper->setChecked(def.enableDebugHelper); m_ui->checkBoxOverrideApps->setChecked(def.overrideAppsByDefault); m_ui->checkBoxUninstallApps->setChecked(def.uninstallAppsByDefault); m_ui->checkBoxClickErrors->setChecked(def.reviewErrorsAsWarnings); } return m_widget; } void UbuntuSettingsProjectDefaultsPage::apply() { Settings::ProjectDefaults newDef; newDef.enableDebugHelper = m_ui->checkBoxDebugHelper->checkState() == Qt::Checked; newDef.overrideAppsByDefault = m_ui->checkBoxOverrideApps->checkState() == Qt::Checked; newDef.reviewErrorsAsWarnings = m_ui->checkBoxClickErrors->checkState() == Qt::Checked; newDef.uninstallAppsByDefault = m_ui->checkBoxUninstallApps->checkState() == Qt::Checked; Settings::setProjectDefaults(newDef); Settings::flushSettings(); } void UbuntuSettingsProjectDefaultsPage::finish() { if (m_widget) { delete m_widget; delete m_ui; m_ui = 0; } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubunturemoteruncontrol.h0000644000015600001650000000466712705421114020255 0ustar jenkinsjenkins/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Copyright (C) 2014 Canonical Ltd. ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #ifndef UBUNTUREMOTERUNCONTROL_H #define UBUNTUREMOTERUNCONTROL_H #include namespace Ubuntu { namespace Internal { class UbuntuRemoteRunControl : public ProjectExplorer::RunControl { Q_OBJECT public: explicit UbuntuRemoteRunControl(ProjectExplorer::RunConfiguration *runConfig); virtual ~UbuntuRemoteRunControl(); virtual void start() override; virtual StopResult stop() override; virtual bool isRunning() const override; private slots: void handleErrorMessage(const QString &error); void handleRunnerFinished(); void handleRemoteOutput(const QByteArray &output); void handleRemoteErrorOutput(const QByteArray &output); void handleProgressReport(const QString &progressString); void handleDeviceReady (); void handleWaitDialogCanceled(); private: void setFinished(); class UbuntuRemoteRunControlPrivate; UbuntuRemoteRunControlPrivate * const d; }; } } #endif // UBUNTUREMOTERUNCONTROL_H ./src/ubuntu/ubuntudevice.cpp0000644000015600001650000012610512705421114016416 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntudevice.h" #include "ubuntudevicenotifier.h" #include "ubuntuemulatornotifier.h" #include "ubuntuprocess.h" #include "ubuntuconstants.h" #include "ubuntudevicesignaloperation.h" #include "ubuntuclicktool.h" #include "localportsmanager.h" #include "clicktoolchain.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; const QString DEVICE_SCALE_FACTOR(QStringLiteral("UbuntuDevice.EmulatorScaleFactor")); const QString DEVICE_MEMORY_SETTING(QStringLiteral("UbuntuDevice.EmulatorMemory")); const QString DEVICE_FRAMEWORK(QStringLiteral("UbuntuDevice.Framework")); const QSet supportedScaleFactors {QStringLiteral("1.0"), QStringLiteral("0.9"), QStringLiteral("0.8"), QStringLiteral("0.7"), QStringLiteral("0.6"),QStringLiteral("0.5"), QStringLiteral("0.4"), QStringLiteral("0.3"), QStringLiteral("0.2"), QStringLiteral("0.1")}; const QSet supportedMemorySettings {QStringLiteral("512"), QStringLiteral("768"), QStringLiteral("1024")}; /*! * \class UbuntuDeviceHelper * Helper class that collects informations about the * device, and can be used to monitor connect/disconnect * and other device events */ UbuntuDeviceHelper::UbuntuDeviceHelper(UbuntuDevice *dev) : QObject(0) , m_clonedNwCount(0) , m_errorCount(0) , m_dev(dev) , m_process(0) , m_deviceWatcher(0) { #if 0 connect(m_process,SIGNAL(finished(QString,int)),this,SLOT(processFinished(QString,int))); connect(m_process,SIGNAL(message(QString)),this,SLOT(onMessage(QString))); connect(m_process,SIGNAL(error(QString)),this,SLOT(onError(QString))); #endif resetToDefaults(); } UbuntuDeviceHelper::~UbuntuDeviceHelper() { if(m_deviceWatcher) m_deviceWatcher->stopMonitoring(); if(m_process) stopProcess(); } UbuntuDevice *UbuntuDeviceHelper::device() const { return m_dev; } QString UbuntuDeviceHelper::log() const { return m_log; } void UbuntuDeviceHelper::refresh() { deviceDisconnected(); if(m_deviceWatcher && m_deviceWatcher->isConnected()) { deviceConnected(); } } void UbuntuDeviceHelper::init() { if(!m_deviceWatcher) { QString watchKey; if(m_dev->machineType() == ProjectExplorer::IDevice::Emulator) { if (!m_dev->imageName().isEmpty()) { m_deviceWatcher = new UbuntuEmulatorNotifier(this); watchKey = m_dev->imageName(); } } else { if (!m_dev->serialNumber().isEmpty()) { m_deviceWatcher = new UbuntuDeviceNotifier(this); watchKey = m_dev->serialNumber(); } } if (m_deviceWatcher) { m_deviceWatcher->stopMonitoring(); m_deviceWatcher->startMonitoring(watchKey); connect(m_deviceWatcher,SIGNAL(deviceConnected()),this,SLOT(deviceConnected())); connect(m_deviceWatcher,SIGNAL(deviceDisconnected()),this,SLOT(deviceDisconnected())); } } if(m_deviceWatcher && m_deviceWatcher->isConnected()) { deviceConnected(); } } void UbuntuDeviceHelper::waitForEmulatorStart() { setProcessState(UbuntuDevice::WaitForEmulatorStart); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_WAIT_FOR_EMULATOR_MESSAGE)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_WAIT_FOR_EMULATOR_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->imageName())); } void UbuntuDeviceHelper::waitForBoot() { //at this point the serial ID should be known m_dev->setupPrivateKey(); setProcessState(UbuntuDevice::WaitForBootAdbAccess); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_WAIT_FOR_BOOT_MESSAGE)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_WAIT_FOR_BOOT_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::processFinished(const QString &, const int code) { Q_UNUSED(code) if(m_errorCount > 3) { setProcessState(UbuntuDevice::Failed); m_reply.clear(); return; } switch(m_dev->m_processState) { case UbuntuDevice::WaitForEmulatorStart: { if(code != 0) { m_errorCount++; waitForEmulatorStart(); break; } else { m_errorCount = 0; } m_dev->m_emulatorSerial = m_reply.trimmed(); emit deviceInfoUpdated(); waitForBoot(); break; } case UbuntuDevice::WaitForBoot: case UbuntuDevice::WaitForBootAdbAccess: case UbuntuDevice::WaitForBootDeviceLock: { if(code != 0) { m_errorCount++; waitForBoot(); break; } else { m_errorCount = 0; } //for now the script will wait forever until the shell is available detect(); break; } case UbuntuDevice::DetectDeviceVersion:{ if(code != 0) { m_errorCount++; detect(); break; } QStringList options = m_reply.split(QStringLiteral("\n"),QString::SkipEmptyParts); if(debug) qDebug()<m_framework = framework; //will trigger the device updated signal m_dev->setDeviceInfo(options[1].trimmed(), options[0].trimmed(), options[2].trimmed()); detectHasNetworkConnection(); break; } case UbuntuDevice::DetectNetworkConnection:{ if(code != 0) { m_errorCount++; detectHasNetworkConnection(); break; } else { m_errorCount = 0; } if (m_reply.trimmed() == QString::fromLatin1(Constants::ONE_STR)) { // we have network m_clonedNwCount = 0; if(m_dev->m_hasNetworkConnection != UbuntuDevice::Available) { m_dev->m_hasNetworkConnection = UbuntuDevice::Available; } emit featureDetected(); detectOpenSsh(); } else { if(m_dev->m_hasNetworkConnection != UbuntuDevice::NotAvailable) { m_dev->m_hasNetworkConnection = UbuntuDevice::NotAvailable; } emit featureDetected(); emit deviceNeedsSetup(); //detect other features detectOpenSsh(); } break; } case UbuntuDevice::CloneNetwork:{ m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_NETWORK_CONF_COPIED)); detectHasNetworkConnection(); break; } case UbuntuDevice::DetectOpenSSH:{ if(code != 0) { m_errorCount++; detectOpenSsh(); break; } else { m_errorCount = 0; } if (m_reply.trimmed() != QLatin1String(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_NONE) && m_reply.trimmed() != QLatin1String(Constants::EMPTY)) { if(m_dev->m_hasOpenSSHServer != UbuntuDevice::Available) { m_dev->m_hasOpenSSHServer = UbuntuDevice::Available; } emit featureDetected(); ProjectExplorer::DeviceManager::instance()->setDeviceState(m_dev->id(),ProjectExplorer::IDevice::DeviceConnected); endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_SSH_IS_INSTALLED).arg(m_reply.trimmed())); startSshService(); } else { //device is only connected but not ready for use ProjectExplorer::DeviceManager::instance()->setDeviceState(m_dev->id(),ProjectExplorer::IDevice::DeviceConnected); if(m_dev->m_hasOpenSSHServer != UbuntuDevice::NotAvailable) { m_dev->m_hasOpenSSHServer = UbuntuDevice::NotAvailable; } emit featureDetected(); emit deviceNeedsSetup(); endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_SSH_NOT_INSTALLED)); //detect other options detectDeviceWritableImage(); } break; } case UbuntuDevice::InstallOpenSSH:{ m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_SSH_WAS_INSTALLED)); detectOpenSsh(); break; } case UbuntuDevice::RemoveOpenSSH:{ m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_SSH_WAS_REMOVED)); detectOpenSsh(); break; } case UbuntuDevice::StartOpenSSH:{ if(code != 0) { m_errorCount++; startSshService(); break; } else { m_errorCount = 0; } if(m_dev->m_openSSHStarted != UbuntuDevice::Available) { m_dev->m_openSSHStarted = UbuntuDevice::Available; emit featureDetected(); } endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_SSH_WAS_STARTED)); enablePortForward(); break; } case UbuntuDevice::EnablePortForwarding:{ m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_PORTS_FORWARDED)); deployPublicKey(); break; } case UbuntuDevice::DeployPublicKey:{ if(code != 0) { m_errorCount++; deployPublicKey(); break; } else { m_errorCount = 0; } //ready to use ProjectExplorer::DeviceManager::instance()->setDeviceState(m_dev->id(),ProjectExplorer::IDevice::DeviceReadyToUse); endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_PUBLICKEY_AUTH_SET)); detectDeviceWritableImage(); break; } case UbuntuDevice::DetectDeviceWriteableImage:{ if(code != 0) { m_errorCount++; detectDeviceWritableImage(); break; } else { m_errorCount = 0; } if (m_reply.trimmed() == QLatin1String(Constants::ZERO_STR)) { m_dev->m_hasWriteableImage = UbuntuDevice::Available; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_WRITABLEIMAGE)); } else { m_dev->m_hasWriteableImage = UbuntuDevice::NotAvailable; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_READONLYIMAGE)); } emit featureDetected(); detectDeveloperTools(); break; } case UbuntuDevice::DetectDeveloperTools:{ if(code != 0) { m_errorCount++; detectDeveloperTools(); break; } else { m_errorCount = 0; } setProcessState(UbuntuDevice::Done); stopProcess(); if (m_reply.trimmed() == QLatin1String(Constants::ZERO_STR)) { m_dev->m_hasDeveloperTools = UbuntuDevice::NotAvailable; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_NOT_INSTALLED)); } else { m_dev->m_hasDeveloperTools = UbuntuDevice::Available; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_INSTALLED)); } emit featureDetected(); break; } case UbuntuDevice::EnableRWImage: m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_WRITABLE_ENABLED)); detectDeviceWritableImage(); break; case UbuntuDevice::DisableRWImage: m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_WRITABLE_DISABLED)); detectDeviceWritableImage(); break; case UbuntuDevice::InstallDevTools: m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_WAS_INSTALLED)); detectDeveloperTools(); break; case UbuntuDevice::RemoveDevTools: m_errorCount = 0; endAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_REMOVED)); detectDeveloperTools(); break; default:{ break; } } m_reply.clear(); } void UbuntuDeviceHelper::onMessage(const QString &msg) { m_reply.append(msg); if(m_dev->m_processState == UbuntuDevice::WaitForBootAdbAccess) { if(m_reply.contains(QStringLiteral("DevLocked"))) { setProcessState(UbuntuDevice::WaitForBootDeviceLock); } else if(m_reply.contains(QStringLiteral("DevUnLocked"))) { setProcessState(UbuntuDevice::WaitForBoot); } } } void UbuntuDeviceHelper::onError(const QString &error) { addToLog(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ONERROR).arg(error)); } void UbuntuDeviceHelper::onStdOut(const QString &stdout) { QString message = stdout; message.replace(QChar::fromLatin1('\n'),QLatin1String("
")); addToLog(message); } void UbuntuDeviceHelper::detect() { //start feature detection detectDeviceVersion(); } void UbuntuDeviceHelper::detectOpenSsh() { setProcessState(UbuntuDevice::DetectOpenSSH); m_dev->m_hasOpenSSHServer = UbuntuDevice::Unknown; emit featureDetected(); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTOPENSSH)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTOPENSSH_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::startSshService() { setProcessState(UbuntuDevice::StartOpenSSH); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_STARTSSHSERVICE)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_STARTSSHSERVICE_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::enableDeveloperMode() { setProcessState(UbuntuDevice::InstallOpenSSH); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_SSH_INSTALL)); m_dev->m_hasOpenSSHServer = UbuntuDevice::Unknown; emit featureDetected(); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_SSH_INSTALL_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::disableDeveloperMode() { setProcessState(UbuntuDevice::RemoveOpenSSH); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_SSH_REMOVE)); m_dev->m_hasOpenSSHServer = UbuntuDevice::Unknown; emit featureDetected(); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_SSH_REMOVE_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::detectHasNetworkConnection() { //@TODO use adb shell nmcli -t -f DEVICE,TYPE,STATE dev status // adb shell nmcli dev list iface | grep IP4.ADDRESS // to find out the devices ip address setProcessState(UbuntuDevice::DetectNetworkConnection); m_dev->m_hasNetworkConnection = UbuntuDevice::Unknown; emit featureDetected(); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_HASNETWORK)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_HASNETWORK_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::detectDeviceVersion() { setProcessState(UbuntuDevice::DetectDeviceVersion); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTDEVICEVERSION)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTDEVICEVERSION_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::detectDeviceWritableImage() { setProcessState(UbuntuDevice::DetectDeviceWriteableImage); m_dev->m_hasWriteableImage = UbuntuDevice::Unknown; emit featureDetected(); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTWRITABLEIMAGE)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTWRITABLEIMAGE_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::detectDeveloperTools() { setProcessState(UbuntuDevice::DetectDeveloperTools); m_dev->m_hasDeveloperTools = UbuntuDevice::Unknown; emit featureDetected(); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTDEVELOPERTOOLS)); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DETECTDEVELOPERTOOLS_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::deviceConnected() { if(debug) qDebug()<<"Device "<id().toString()<<" connected"; ProjectExplorer::DeviceManager::instance()->setDeviceState(m_dev->id(),ProjectExplorer::IDevice::DeviceConnected); if(m_dev->machineType() == ProjectExplorer::IDevice::Emulator) waitForEmulatorStart(); else waitForBoot(); } void UbuntuDeviceHelper::deviceDisconnected() { if(debug) qDebug()<<"Device "<id().toString()<<" disconnected"; ProjectExplorer::DeviceManager::instance()->setDeviceState(m_dev->id(),ProjectExplorer::IDevice::DeviceDisconnected); setProcessState(UbuntuDevice::NotStarted); stopProcess(); resetToDefaults(); emit disconnected(); } void UbuntuDeviceHelper::readProcessOutput(QProcess *proc) { QString stderr = QString::fromLocal8Bit(proc->readAllStandardError()); QString stdout = QString::fromLocal8Bit(proc->readAllStandardOutput()); QString msg; if (!stderr.isEmpty()) { msg.append(stderr); onError(stderr); } if (!stdout.isEmpty()) { msg.append(stdout); onStdOut(stdout); } if(!msg.isEmpty()) onMessage(msg); } void UbuntuDeviceHelper::stopProcess() { if(m_process) { m_process->disconnect(this); if(m_process->state() != QProcess::NotRunning) { m_process->kill(); m_process->waitForFinished(); } m_process->deleteLater(); m_process = 0; } } void UbuntuDeviceHelper::addToLog(const QString &msg) { m_log.append(msg); emit message(msg); } void UbuntuDeviceHelper::setProcessState(const int newState) { Q_ASSERT_X(newState >= UbuntuDevice::NotStarted && newState <= UbuntuDevice::MaxState, Q_FUNC_INFO, "State variable out of bounds"); if( newState == m_dev->m_processState) return; if(m_dev->m_processState == UbuntuDevice::NotStarted || m_dev->m_processState == UbuntuDevice::Done || m_dev->m_processState == UbuntuDevice::Failed) emit beginQueryDevice(); else if((newState == UbuntuDevice::Done || newState == UbuntuDevice::NotStarted || newState == UbuntuDevice::Failed)) emit endQueryDevice(); m_dev->m_processState = static_cast(newState); emit detectionStateChanged(); } void UbuntuDeviceHelper::resetToDefaults() { m_clonedNwCount = 0; m_reply.clear(); m_dev->m_openSSHStarted = UbuntuDevice::Unknown; m_dev->m_hasOpenSSHServer = UbuntuDevice::Unknown; m_dev->m_hasNetworkConnection = UbuntuDevice::Unknown; m_dev->m_hasWriteableImage = UbuntuDevice::Unknown; m_dev->m_hasDeveloperTools = UbuntuDevice::Unknown; setProcessState(UbuntuDevice::NotStarted); emit featureDetected(); } void UbuntuDeviceHelper::enableRWImage() { if(m_dev->m_processState < UbuntuDevice::FirstNonCriticalTask && m_dev->m_processState != UbuntuDevice::NotStarted) return; setProcessState(UbuntuDevice::EnableRWImage); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_MAKEFSWRITABLE)); stopProcess(); m_dev->m_hasWriteableImage = UbuntuDevice::Unknown; emit featureDetected(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_MAKEFSWRITABLE_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::disableRWImage() { if(m_dev->m_processState < UbuntuDevice::FirstNonCriticalTask && m_dev->m_processState != UbuntuDevice::NotStarted) return; setProcessState(UbuntuDevice::DisableRWImage); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_MAKEFSREADONLY)); stopProcess(); m_dev->m_hasWriteableImage = UbuntuDevice::Unknown; emit featureDetected(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_MAKEFSREADONLY_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } /*! * \brief UbuntuDeviceHelper::enablePortForward * Sets up the port forwarding for the device, this is executed * synchronously and will block the eventloop to make sure we only * use the same port once */ void UbuntuDeviceHelper::enablePortForward() { setProcessState(UbuntuDevice::EnablePortForwarding); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_PORTFORWARD)); stopProcess(); /* always query for a free range! When detaching and reattaching device1 * while plugging in device2 inbetween, it could happen that we reuse * ports that were assigned to device2 in the meantime * See bug LP:1396406 */ m_dev->m_localForwardedPorts = UbuntuLocalPortsManager::getFreeRange(m_dev->serialNumber(),10); if(!m_dev->m_localForwardedPorts.hasMore()) { //Oh noes , no ports available endAction(tr("No ports available on the host, please detach some devices")); setProcessState(UbuntuDevice::Failed); return; } Utils::PortList copy = m_dev->m_localForwardedPorts; //first port is SSH port QSsh::SshConnectionParameters connParms = m_dev->sshParameters(); connParms.port = copy.getNext(); m_dev->setSshParameters(connParms); m_dev->setFreePorts(copy); QStringList ports; while(copy.hasMore()) ports.append(QString::number(copy.getNext())); //@TODO per device settings QString deviceSshPort = QString::number(connParms.port); QStringList args = QStringList() << m_dev->serialNumber() <m_processState < UbuntuDevice::FirstNonCriticalTask && m_dev->m_processState != UbuntuDevice::NotStarted) return; setProcessState(UbuntuDevice::InstallDevTools); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ENABLEPLATFORMDEVELOPMENT)); stopProcess(); m_dev->m_hasDeveloperTools = UbuntuDevice::Unknown; emit featureDetected(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_ENABLEPLATFORMDEVELOPMENT_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::removeDevTools() { if(m_dev->m_processState < UbuntuDevice::FirstNonCriticalTask && m_dev->m_processState != UbuntuDevice::NotStarted) return; setProcessState(UbuntuDevice::RemoveDevTools); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DISABLEPLATFORMDEVELOPMENT)); stopProcess(); m_dev->m_hasDeveloperTools = UbuntuDevice::Unknown; emit featureDetected(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_DISABLEPLATFORMDEVELOPMENT_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::deployPublicKey() { setProcessState(UbuntuDevice::DeployPublicKey); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_SETUP_PUBKEY_AUTH)); QString deviceUsername = Settings::deviceConnectivity().user; stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_SETUP_PUBKEY_AUTH_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber()) .arg(deviceUsername)); } void UbuntuDeviceHelper::cloneNetwork() { m_clonedNwCount++; setProcessState(UbuntuDevice::CloneNetwork); beginAction(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_CLONENETWORK)); m_dev->m_hasNetworkConnection = UbuntuDevice::Unknown; emit featureDetected(); stopProcess(); startProcess(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_CLONENETWORK_SCRIPT) .arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) .arg(m_dev->serialNumber())); } void UbuntuDeviceHelper::startProcess(const QString &command) { if(debug) qDebug()<<"Starting process: "<setWorkingDirectory(QCoreApplication::applicationDirPath()); m_process->start(command); } void UbuntuDeviceHelper::onProcessReadyRead() { readProcessOutput(m_process); } void UbuntuDeviceHelper::onProcessFinished(const int code) { QString errorMsg = QString::fromLocal8Bit(m_process->readAllStandardError()); if (errorMsg.trimmed().length()>0) onError(errorMsg); QString msg = QString::fromLocal8Bit(m_process->readAllStandardOutput()); if (msg.trimmed().length()>0) onMessage(msg); processFinished(m_process->program(),code); } void UbuntuDeviceHelper::onProcessError(const QProcess::ProcessError error) { QString errorString = QStringLiteral("ERROR: (%0) %1 %2").arg(m_process->program()).arg(m_process->errorString()).arg(error); if(debug) qDebug()<<"Received Process Error: "<init(); } UbuntuDevice::UbuntuDevice(const UbuntuDevice &other) : LinuxDevice(other) , m_helper(new UbuntuDeviceHelper(this)) , m_processState(NotStarted) , m_deviceInfo(other.m_deviceInfo) , m_modelInfo(other.m_modelInfo) , m_productInfo(other.m_productInfo) , m_ubuntuVersion(other.m_ubuntuVersion) , m_deviceVersion(other.m_deviceVersion) , m_imageVersion(other.m_imageVersion) , m_scaleFactor(other.m_scaleFactor) , m_memory(other.m_memory) { setDeviceState(ProjectExplorer::IDevice::DeviceDisconnected); m_helper->init(); } /*! * \brief UbuntuDevice::loadDefaultConfig * loads the default ssh connect and generic parameters from settings * * \note call this before fromMap() so every device can have its own settings */ void UbuntuDevice::loadDefaultConfig() { Settings::DeviceConnectivity devConn = Settings::deviceConnectivity(); QString ip = devConn.ip; QString username = devConn.user; //even though this is set here, it will be changed dynamically when the device is connected QString deviceSshPort = QString::number(devConn.sshPort); QSsh::SshConnectionParameters params; params.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePublicKey; params.host = ip; params.port = deviceSshPort.toUInt(); params.userName = username; params.timeout = 20; setSshParameters(params); } void UbuntuDevice::setupPrivateKey() { if(!id().isValid()) return; QSsh::SshConnectionParameters params = this->sshParameters(); params.privateKeyFile = Settings::settingsPath() .appendPath(QLatin1String(Constants::UBUNTU_DEVICE_SSHIDENTITY)) .toString(); setSshParameters(params); } UbuntuDevice::~UbuntuDevice() { delete m_helper; } UbuntuDevice::Ptr UbuntuDevice::create() { return Ptr(new UbuntuDevice()); } QString UbuntuDevice::serialNumber() const { if(machineType() == Emulator) return m_emulatorSerial; return id().toSetting().toString(); } UbuntuDeviceHelper *UbuntuDevice::helper() const { return m_helper; } void UbuntuDevice::cloneNetwork() { m_helper->cloneNetwork(); } /*! * \brief UbuntuDevice::openTerminal * Opens a detached terminal, logged into the device */ void UbuntuDevice::openTerminal() { QProcess p; QStringList args = QStringList() << serialNumber() << QString::number(sshParameters().port) << sshParameters().userName << sshParameters().host; p.startDetached(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_SSHCONNECT_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) , args , QCoreApplication::applicationDirPath()); } void UbuntuDevice::enablePortForward() { m_helper->enablePortForward(); } void UbuntuDevice::deployPublicKey() { m_helper->deployPublicKey(); } void UbuntuDevice::setDeveloperModeEnabled(const bool enabled) { if(m_processState != Done) return; if(enabled && m_hasOpenSSHServer == NotAvailable) m_helper->enableDeveloperMode(); else if(!enabled && m_hasOpenSSHServer == Available) m_helper->disableDeveloperMode(); } UbuntuDevice::FeatureState UbuntuDevice::developerModeEnabled() const { return m_hasOpenSSHServer; } void UbuntuDevice::setWriteableImageEnabled(const bool enabled) { if(m_hasWriteableImage == Unknown) return; if(enabled && m_hasWriteableImage != Available) m_helper->enableRWImage(); else if(!enabled && m_hasWriteableImage != NotAvailable) m_helper->disableRWImage(); } void UbuntuDevice::setDeveloperToolsInstalled(const bool installed) { if(installed && m_hasDeveloperTools != Available) m_helper->installDevTools(); else if(!installed && m_hasDeveloperTools != NotAvailable) m_helper->removeDevTools(); } void UbuntuDevice::setDeviceInfo(const QString &productInfo, const QString &modelInfo, const QString &deviceInfo) { m_productInfo = productInfo; m_modelInfo = modelInfo; m_deviceInfo = deviceInfo; emit m_helper->deviceInfoUpdated(); } QString UbuntuDevice::modelInfo() const { return m_modelInfo; } QString UbuntuDevice::deviceInfo() const { return m_deviceInfo; } QString UbuntuDevice::productInfo() const { return m_productInfo; } void UbuntuDevice::setEmulatorInfo(const QString &ubuntuVersion, const QString &deviceVersion, const QString &imageVersion) { m_ubuntuVersion = ubuntuVersion; m_deviceVersion = deviceVersion; m_imageVersion = imageVersion; emit m_helper->deviceInfoUpdated(); } QString UbuntuDevice::ubuntuVersion() const { return m_ubuntuVersion; } QString UbuntuDevice::deviceVersion() const { return m_deviceVersion; } QString UbuntuDevice::imageVersion() const { return m_imageVersion; } bool UbuntuDevice::startEmulator() { if ( machineType() != IDevice::Emulator ) return false; QStringList args = QStringList() << imageName() << m_memory << m_scaleFactor; return QProcess::startDetached(QString::fromLatin1(Constants::UBUNTUDEVICESWIDGET_LOCAL_START_EMULATOR_SCRIPT).arg(Ubuntu::Constants::UBUNTU_SCRIPTPATH) ,args ,QCoreApplication::applicationDirPath()); } QString UbuntuDevice::imageName() const { return id().toSetting().toString(); } QString UbuntuDevice::architecture() const { QString arch = this->type().suffixAfter(Constants::UBUNTU_DEVICE_TYPE_ID); if(debug) qDebug()<<"Reporting device architecture as: "<deviceInfoUpdated(); return true; } return false; } QString UbuntuDevice::memorySetting() const { return m_memory; } bool UbuntuDevice::setMemorySetting(const QString &memory) { if(supportedMemorySettings.contains(memory)) { m_memory = memory; emit m_helper->deviceInfoUpdated(); return true; } return false; } ProjectExplorer::IDeviceWidget *UbuntuDevice::createWidget() { return new RemoteLinux::GenericLinuxDeviceConfigurationWidget(sharedFromThis()); //return new UbuntuDeviceConfigurationWidget(sharedFromThis()); } QList UbuntuDevice::actionIds() const { return QList(); } QString UbuntuDevice::displayType() const { return tr("Ubuntu Device (%1)").arg(architecture()); } ProjectExplorer::IDevice::Ptr UbuntuDevice::clone() const { return UbuntuDevice::Ptr(new UbuntuDevice(*this)); } void UbuntuDevice::fromMap(const QVariantMap &map) { LinuxDevice::fromMap(map); if (map.contains(DEVICE_SCALE_FACTOR)) { m_scaleFactor = map[DEVICE_SCALE_FACTOR].toString(); if(!supportedScaleFactors.contains(m_scaleFactor)) m_scaleFactor = QStringLiteral("1.0"); } if (map.contains(DEVICE_MEMORY_SETTING)) { m_memory = map[DEVICE_MEMORY_SETTING].toString(); if(!supportedMemorySettings.contains(m_memory)) m_memory = QStringLiteral("512"); } if (map.contains(DEVICE_FRAMEWORK)) { m_framework = map[DEVICE_FRAMEWORK].toString(); //if a invalid framework is in the device, let it redetect if(!UbuntuClickFrameworkProvider::getSupportedFrameworks().contains(m_framework)) m_framework.clear(); } m_helper->init(); } QVariantMap UbuntuDevice::toMap() const { QVariantMap map = LinuxDevice::toMap(); map.insert(DEVICE_SCALE_FACTOR, m_scaleFactor); map.insert(DEVICE_MEMORY_SETTING, m_memory); map.insert(DEVICE_FRAMEWORK, m_framework); return map; } ProjectExplorer::DeviceProcess *UbuntuDevice::createProcess(QObject *parent) const { return new UbuntuDeviceProcess(sharedFromThis(), parent); } ProjectExplorer::DeviceProcessSignalOperation::Ptr UbuntuDevice::signalOperation() const { UbuntuDeviceSignalOperation::Ptr p(new UbuntuDeviceSignalOperation(sharedFromThis())); return p; } UbuntuDevice::Ptr UbuntuDevice::sharedFromThis() { return qSharedPointerCast(LinuxDevice::sharedFromThis()); } UbuntuDevice::ConstPtr UbuntuDevice::sharedFromThis() const { return qSharedPointerCast(LinuxDevice::sharedFromThis()); } UbuntuDevice::Ptr UbuntuDevice::create(const QString &name, const QString& serial, ProjectExplorer::IDevice::MachineType machineType, const QString &archName, ProjectExplorer::IDevice::Origin origin) { return Ptr(new UbuntuDevice(name,machineType,origin,Core::Id::fromSetting(serial),archName)); } ////////////// /// UbuntuDeviceProcess ///////////// static QString quote(const QString &s) { return Utils::QtcProcess::quoteArgUnix(s); } UbuntuDeviceProcess::UbuntuDeviceProcess(const QSharedPointer &device, QObject *parent) : RemoteLinux::LinuxDeviceProcess(device,parent) { } void UbuntuDeviceProcess::setWorkingDirectory(const QString &directory) { m_workingDir = directory; } void UbuntuDeviceProcess::terminate() { LinuxDeviceProcess::terminate(); } QString UbuntuDeviceProcess::fullCommandLine() const { //return QStringLiteral("%1 %2").arg(quote(executable()),Utils::QtcProcess::joinArgsUnix(arguments())); QStringList rcFiles = QStringList() << QLatin1String("/etc/profile") << QLatin1String("$HOME/.profile") << QLatin1String("$HOME/.bashrc"); QString fullCommandLine; foreach (const QString &filePath, rcFiles) fullCommandLine += QString::fromLatin1("test -f %1 && . %1;").arg(filePath); if (!m_workingDir.isEmpty()) { fullCommandLine.append(QLatin1String("cd ")).append(quote(m_workingDir)) .append(QLatin1String(" && ")); } fullCommandLine.append(quote(executable())); if (!arguments().isEmpty()) { fullCommandLine.append(QLatin1Char(' ')); fullCommandLine.append(Utils::QtcProcess::joinArgs(arguments(),Utils::OsTypeLinux)); } if(debug) qDebug()<. * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_TARGETUPGRADEMANAGER_H #define UBUNTU_INTERNAL_TARGETUPGRADEMANAGER_H #include #include #include #include #include "ubuntuclicktool.h" class QProcess; namespace Ubuntu { namespace Internal { namespace Ui { class TargetUpgradeManagerDialog; } class TargetUpgradeManager : public QObject { Q_OBJECT enum State { Idle, CollectPendingUpdates }; struct Task { UbuntuClickTool::Target target; QPointer proc; }; public: explicit TargetUpgradeManager(QObject *parent = 0); public slots: void checkForUpgrades (); private slots: void processFinished (); void coreAboutToClose (); private: QMap m_running; QList m_outdatedChroots; State m_state; }; class TargetUpgradeManagerDialog : public QDialog { Q_OBJECT public: TargetUpgradeManagerDialog(QWidget *parent = 0); ~TargetUpgradeManagerDialog(); static void selectAndUpgradeTargets (QList targets,QWidget *parent); private: Ui::TargetUpgradeManagerDialog *m_ui; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_TARGETUPGRADEMANAGER_H ./src/ubuntu/ubuntusettingsclickwidget.cpp0000644000015600001650000001346412705421114021234 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntusettingsclickwidget.h" #include "ui_ubuntusettingsclickwidget.h" #include "ubuntuconstants.h" #include "ubuntuclicktool.h" #include "ubuntuclickdialog.h" #include "settings.h" #include #include #include #include #include enum { debug = 0 }; namespace Ubuntu { namespace Internal { UbuntuSettingsClickWidget::UbuntuSettingsClickWidget(QWidget *parent) : QWidget(parent), ui(new ::Ui::UbuntuSettingsClickWidget) { ui->setupUi(this); Settings::ChrootSettings def = Settings::chrootSettings(); ui->enableUpdateCheckerCheckBox->setChecked(def.autoCheckForUpdates); ui->checkBoxLocalMirror->setChecked(def.useLocalMirror); m_deleteMapper = new QSignalMapper(this); connect(m_deleteMapper, SIGNAL(mapped(int)),this, SLOT(on_deleteClickChroot(int))); m_maintainMapper = new QSignalMapper(this); connect(m_maintainMapper, SIGNAL(mapped(int)),this, SLOT(on_maintainClickChroot(int))); m_updateMapper = new QSignalMapper(this); connect(m_updateMapper, SIGNAL(mapped(int)),this, SLOT(on_upgradeClickChroot(int))); QStringList headers; headers << tr("Series")<< tr("Framework") << tr("Architecture")<treeWidgetClickTargets->setHeaderLabels(headers); ui->treeWidgetClickTargets->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); ui->treeWidgetClickTargets->header()->setSectionResizeMode(1, QHeaderView::Stretch); ui->treeWidgetClickTargets->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); ui->treeWidgetClickTargets->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); ui->treeWidgetClickTargets->header()->setSectionResizeMode(4, QHeaderView::ResizeToContents); ui->treeWidgetClickTargets->header()->setSectionResizeMode(5, QHeaderView::ResizeToContents); listExistingClickTargets(); } void UbuntuSettingsClickWidget::apply() { Settings::ChrootSettings set; set.autoCheckForUpdates = ui->enableUpdateCheckerCheckBox->checkState() == Qt::Checked; set.useLocalMirror = ui->checkBoxLocalMirror->checkState() == Qt::Checked; Settings::setChrootSettings(set); Settings::flushSettings(); } UbuntuSettingsClickWidget::~UbuntuSettingsClickWidget() { delete ui; } void UbuntuSettingsClickWidget::on_pushButtonCreateClickTarget_clicked() { //make sure the current settings are stored apply(); Internal::UbuntuClickDialog::createClickChrootModal(true, QString(), QString(), this); listExistingClickTargets(); } void UbuntuSettingsClickWidget::on_deleteClickChroot(const int index) { if(index < 0 || index > m_availableTargets.size()) return; if(debug) qDebug()<<"Destroying chroot "<< m_availableTargets.at(index); Internal::UbuntuClickDialog::maintainClickModal(m_availableTargets.at(index),Internal::UbuntuClickTool::Delete); listExistingClickTargets(); } void UbuntuSettingsClickWidget::on_maintainClickChroot(const int index) { if(index < 0 || index > m_availableTargets.size()) return; Internal::UbuntuClickTool::openChrootTerminal(m_availableTargets.at(index)); } void UbuntuSettingsClickWidget::on_upgradeClickChroot(const int index) { if(index < 0 || index > m_availableTargets.size()) return; Internal::UbuntuClickDialog::maintainClickModal(m_availableTargets.at(index),Internal::UbuntuClickTool::Upgrade); } /** * @brief UbuntuSettingsClickWidget::listExistingClickTargets * Lists all existing click targets in /var/lib/schroot/chroots * that match the click-- pattern */ void UbuntuSettingsClickWidget::listExistingClickTargets() { //this should hopefully also delete all mapped pushbuttons ui->treeWidgetClickTargets->clear(); QList items = Internal::UbuntuClickTool::listAvailableTargets(); m_availableTargets = items; QAbstractItemModel* model = ui->treeWidgetClickTargets->model(); //fill the treeview with all existing chroots for(int i = 0; i < items.size(); i++) { const Internal::UbuntuClickTool::Target& target = items.at(i); QTreeWidgetItem* chrootItem = new QTreeWidgetItem; chrootItem->setText(0,target.series); chrootItem->setText(1,target.framework); chrootItem->setText(2,target.architecture); ui->treeWidgetClickTargets->addTopLevelItem(chrootItem); QPushButton* push = new QPushButton(tr("Update")); m_updateMapper->setMapping(push,i); connect(push,SIGNAL(clicked()),m_updateMapper,SLOT(map())); ui->treeWidgetClickTargets->setIndexWidget(model->index(i,3), push); push = new QPushButton(tr("Maintain")); m_maintainMapper->setMapping(push,i); connect(push,SIGNAL(clicked()),m_maintainMapper,SLOT(map())); ui->treeWidgetClickTargets->setIndexWidget(model->index(i,4), push); push = new QPushButton(tr("Delete")); m_deleteMapper->setMapping(push,i); connect(push,SIGNAL(clicked()),m_deleteMapper,SLOT(map())); ui->treeWidgetClickTargets->setIndexWidget(model->index(i,5), push); } } }} ./src/ubuntu/ubuntu_global.h0000644000015600001650000000166312705421114016224 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTU_GLOBAL_H #define UBUNTU_GLOBAL_H #include #if defined(UBUNTU_LIBRARY) # define UBUNTUSHARED_EXPORT Q_DECL_EXPORT #else # define UBUNTUSHARED_EXPORT Q_DECL_IMPORT #endif #endif // UBUNTU_GLOBAL_H ./src/ubuntu/ubuntucmakecache.cpp0000644000015600001650000001013112705421114017212 0ustar jenkinsjenkins#include "ubuntucmakecache.h" #include #include #include #include #include #include #include namespace Ubuntu{ namespace Internal { enum { debug = 0 }; /*! * \class UbuntuCMakeCache::UbuntuCMakeCache * Automatic updating value cache, this reads all relevant CMakeCache files * and stores some for quick querying */ UbuntuCMakeCache * UbuntuCMakeCache::m_instance = nullptr; UbuntuCMakeCache::UbuntuCMakeCache(QObject *parent) : QObject(parent) { Q_ASSERT_X(m_instance == nullptr,Q_FUNC_INFO,"There can be only one UbuntuCMakeCache instance"); m_instance = this; connect(ProjectExplorer::SessionManager::instance(),SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)), this,SLOT(onAboutToRemoveProject(ProjectExplorer::Project*))); } QVariant UbuntuCMakeCache::getValue(const QString &key, ProjectExplorer::BuildConfiguration *bc, const QVariant defaultValue) { UbuntuCMakeCache *inst = instance(); if(!inst || !bc) return defaultValue; Utils::FileName cacheFile = bc->buildDirectory().appendPath(QStringLiteral("CMakeCache.txt")); QString cacheKey = inst->normalize(cacheFile.toString()); bool needsRefresh = false; if(!inst->m_map.contains(cacheKey)) { //we need to read it needsRefresh = true; } else { //check if the file has changed since the last read inst->m_map[cacheKey].cacheFile.refresh(); if(debug) qDebug()<<"File Last Read "<m_map[cacheKey].cacheFile.lastModified() <<"Cache Last Read "<m_map[cacheKey].lastRead; if(inst->m_map[cacheKey].cacheFile.lastModified() != inst->m_map[cacheKey].lastRead) { needsRefresh = true; } } if(needsRefresh) inst->refreshCache(cacheKey, cacheFile); if(inst->m_map[cacheKey].values.contains(key)) return inst->m_map[cacheKey].values[key]; return defaultValue; } UbuntuCMakeCache *UbuntuCMakeCache::instance() { return m_instance; } void UbuntuCMakeCache::onAboutToRemoveProject(ProjectExplorer::Project *p) { if(!p) return; //remove all values that belong to the project for (ProjectExplorer::Target *t : p->targets()) { for(ProjectExplorer::BuildConfiguration *bc : t->buildConfigurations()) { Utils::FileName cacheFile = bc->buildDirectory().appendPath(QStringLiteral("CMakeCache.txt")); m_map.remove(normalize(cacheFile.toString())); } } } QString UbuntuCMakeCache::normalize(const QString &path) const { return QDir::cleanPath(path); } void UbuntuCMakeCache::refreshCache(const QString &key, const Utils::FileName &fName) { if(debug) qDebug()<<"Rebuilding cache for: "<lastRead = fName.toFileInfo().lastModified(); } else { QFileInfo fInfo = fName.toFileInfo(); entry = &m_map.insert(key,UbuntuCMakeCacheEntry{fInfo,fInfo.lastModified(),CMakeCacheValueMap()}).value(); } QFile cache(fName.toString()); if(cache.exists() && cache.open(QIODevice::ReadOnly)) { static const QRegularExpression regExpPType(QLatin1String("^UBUNTU_PROJECT_TYPE:(.*)=\\s*(\\S*)\\s*$")); static const QRegularExpression regExpManifestPath (QLatin1String("^UBUNTU_MANIFEST_PATH:(.*)=\\s*(\\S*)\\s*$")); QTextStream in(&cache); while (!in.atEnd()) { QString contents = in.readLine(); QRegularExpressionMatch m = regExpPType.match(contents); if(m.hasMatch()) { entry->values.insert(QStringLiteral("UBUNTU_PROJECT_TYPE"),m.captured(2)); continue; } m = regExpManifestPath.match(contents); if(m.hasMatch()) { entry->values.insert(QStringLiteral("UBUNTU_MANIFEST_PATH"),m.captured(2)); continue; } } } } }} ./src/ubuntu/ubuntupackagingmode.h0000644000015600001650000000310012705421114017402 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPACKAGINGMODE_H #define UBUNTUPACKAGINGMODE_H #include #include "ubuntupackagingmodel.h" #include #include class QQuickView; namespace Ubuntu { namespace Internal { class UbuntuClickManifest; class UbuntuPackagingModel; class UbuntuPackagingMode : public Core::IMode { Q_OBJECT public: explicit UbuntuPackagingMode(QObject *parent = 0); void initialize(); protected slots: void modeChanged(Core::IMode*); void on_projectAdded(ProjectExplorer::Project *project); void on_projectRemoved(ProjectExplorer::Project *project); void updateModeState(); protected: QWidget* m_modeWidget; Core::Id previousMode; private: static UbuntuPackagingMode* m_instance; QQuickView *m_modeView; UbuntuPackagingModel *m_viewModel; }; } } #endif // UBUNTUPACKAGINGMODE_H ./src/ubuntu/ubuntuclicktool.cpp0000644000015600001650000006724112705421114017147 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuclicktool.h" #include "ubuntuclickmanifest.h" #include "ubuntuconstants.h" #include "ubuntushared.h" #include "clicktoolchain.h" #include "settings.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 namespace Ubuntu { namespace Internal { enum { debug = 0 }; static QString getChrootSuffixFromEnv () { QByteArray value = qgetenv(Constants::UBUNTU_CLICK_CHROOT_SUFFIX_ENV_VAR); if(value.isNull()) return QLatin1String(Constants::UBUNTU_CLICK_CHROOT_DEFAULT_NAME); return QString::fromLatin1(value); } /** * Initialize the m_strClickChrootSuffix from the environment variable */ QString UbuntuClickTool::m_strClickChrootSuffix = getChrootSuffixFromEnv(); /** * @brief UbuntuClickTool::UbuntuClickTool * Implements functionality needed for executing the click * tool */ UbuntuClickTool::UbuntuClickTool() { } /** * @brief UbuntuClickTool::clickChrootSuffix * Returns the click chroot suffix to be used with click operations */ QString UbuntuClickTool::clickChrootSuffix() { return m_strClickChrootSuffix; } /** * @brief UbuntuClickTool::parametersForCreateChroot * Initializes a ProjectExplorer::ProcessParameters object with command and arguments * to create a new chroot */ void UbuntuClickTool::parametersForCreateChroot(const Target &target, ProjectExplorer::ProcessParameters *params) { QString command = QString::fromLatin1(Constants::UBUNTU_CLICK_CHROOT_CREATE_ARGS) .arg(Constants::UBUNTU_SCRIPTPATH) .arg(target.architecture) .arg(target.framework) .arg(target.series) .arg(clickChrootSuffix()); if(!Settings::chrootSettings().useLocalMirror) command.prepend(QStringLiteral("env CLICK_NO_LOCAL_MIRROR=1 ")); params->setCommand(QLatin1String(Constants::UBUNTU_SUDO_BINARY)); params->setEnvironment(Utils::Environment::systemEnvironment()); params->setArguments(command); } /** * @brief UbuntuClickTool::parametersForMaintainChroot * Initializes params with the arguments for maintaining the chroot * @note does not call ProjectExplorer::ProcessParameters::resolveAll() */ void UbuntuClickTool::parametersForMaintainChroot(const UbuntuClickTool::MaintainMode &mode, const Target &target, ProjectExplorer::ProcessParameters *params) { QString arguments; switch (mode) { case Upgrade: params->setCommand(QLatin1String(Constants::UBUNTU_CLICK_BINARY)); arguments = QString::fromLatin1(Constants::UBUNTU_CLICK_CHROOT_UPGRADE_ARGS) .arg(target.architecture) .arg(target.framework) .arg(target.series) .arg(clickChrootSuffix()); break; case Delete: params->setCommand(QLatin1String(Constants::UBUNTU_SUDO_BINARY)); arguments = QString::fromLatin1(Constants::UBUNTU_CLICK_CHROOT_DESTROY_ARGS) .arg(Constants::UBUNTU_SCRIPTPATH) .arg(target.architecture) .arg(target.framework) .arg(target.series) .arg(clickChrootSuffix()); break; } params->setEnvironment(Utils::Environment::systemEnvironment()); params->setArguments(arguments); } /** * @brief UbuntuClickTool::openChrootTerminal * Opens a new terminal logged into the chroot specified by \a target * The terminal emulator used is specified in the Creator environment option page */ void UbuntuClickTool::openChrootTerminal(const UbuntuClickTool::Target &target) { QStringList args = Utils::QtcProcess::splitArgs(Utils::ConsoleProcess::terminalEmulator(Core::ICore::settings())); QString term = args.takeFirst(); args << QString(QLatin1String(Constants::UBUNTU_CLICK_OPEN_TERMINAL)) .arg(target.architecture) .arg(target.framework) .arg(target.series) .arg(clickChrootSuffix()); if(!QProcess::startDetached(term,args,QDir::homePath())) { printToOutputPane(QLatin1String(Constants::UBUNTU_CLICK_OPEN_TERMINAL_ERROR)); } } bool UbuntuClickTool::getTargetFromUser(Target *target, const QString &framework) { QList targets = UbuntuClickTool::listAvailableTargets(framework); if (!targets.size()) { QString message = QCoreApplication::translate("UbuntuClickTool",Constants::UBUNTU_CLICK_NOTARGETS_MESSAGE); if(!framework.isEmpty()) { message = QCoreApplication::translate("UbuntuClickTool",Constants::UBUNTU_CLICK_NOTARGETS_FRAMEWORK_MESSAGE) .arg(framework); } QMessageBox::warning(Core::ICore::mainWindow(), QCoreApplication::translate("UbuntuClickTool",Constants::UBUNTU_CLICK_NOTARGETS_TITLE), message); return false; } //if we have only 1 target there is nothing to choose if(targets.size() == 1){ *target = targets[0]; return true; } QStringList items; foreach(const UbuntuClickTool::Target& t, targets) items << QString::fromLatin1("%0-%1").arg(t.framework).arg(t.architecture); bool ok = false; QString item = QInputDialog::getItem(Core::ICore::mainWindow() ,QCoreApplication::translate("UbuntuClickTool",Constants::UBUNTU_CLICK_SELECT_TARGET_TITLE) ,QCoreApplication::translate("UbuntuClickTool",Constants::UBUNTU_CLICK_SELECT_TARGET_LABEL) ,items,0,false,&ok); //get index of item in the targets list int idx = items.indexOf(item); if(!ok || idx < 0 || idx >= targets.size()) return false; *target = targets[idx]; return true; } QString UbuntuClickTool::targetBasePath(const UbuntuClickTool::Target &target) { return QString::fromLatin1("%1/%2-%3-%4") .arg(QLatin1String(Constants::UBUNTU_CLICK_CHROOT_BASEPATH)) .arg(clickChrootSuffix()) .arg(target.framework) .arg(target.architecture); } /*! * \brief UbuntuClickTool::targetExists * checks if the target is still available */ bool UbuntuClickTool::targetExists(const UbuntuClickTool::Target &target) { QPair targetVer = targetVersion(target); if(targetVer.first == -1) return false; return true; } /** * @brief UbuntuClickTool::listAvailableTargets * @return all currently existing chroot targets in the system */ QList UbuntuClickTool::listAvailableTargets(const QString &framework) { QList items; QDir chrootDir(QLatin1String(Constants::UBUNTU_CLICK_CHROOT_BASEPATH)); QString filterRegex; filterRegex = QString::fromLatin1(Constants::UBUNTU_CLICK_TARGETS_REGEX).arg(clickChrootSuffix()); if(!framework.isEmpty()) { QRegularExpression expr(QLatin1String(Constants::UBUNTU_CLICK_BASE_FRAMEWORK_REGEX)); QRegularExpressionMatch match = expr.match(framework); if(match.hasMatch()) { if(debug) qDebug()<<"Filtering for base framework: "< UbuntuClickTool::targetVersion(const UbuntuClickTool::Target &target) { QFile f(QString::fromLatin1("%1/%2") .arg(targetBasePath(target)) .arg(QLatin1String("etc/lsb-release"))); if (!f.open(QIODevice::ReadOnly)) { //there is no lsb-release file... what now? return qMakePair(-1,-1); } QString info = QString::fromLatin1(f.readAll()); QRegularExpression grep(QLatin1String(Constants::UBUNTU_CLICK_VERSION_REGEX),QRegularExpression::MultilineOption); QRegularExpressionMatch match = grep.match(info); if(!match.hasMatch()) { return qMakePair(-1,-1); } bool ok = false; int majorV = match.captured(1).toInt(&ok); if(!ok) { return qMakePair(-1,-1); } int minorV = match.captured(2).toInt(); return qMakePair(majorV,minorV); } /*! * \brief UbuntuClickTool::targetFromPath * returns true if the given path is a click target * if it is, \a tg will be initialized with that targets values */ bool UbuntuClickTool::targetFromPath(const QString &targetPath, UbuntuClickTool::Target *tg) { QRegularExpression clickFilter(QString::fromLatin1(Constants::UBUNTU_CLICK_TARGETS_REGEX).arg(clickChrootSuffix())); QRegularExpressionMatch match = clickFilter.match(targetPath); if(!match.hasMatch()) { return false; } Target t; t.maybeBroken = false; //we are optimistic t.framework = match.captured(1); t.architecture = match.captured(2); //now read informations about the target QFile f(QString::fromLatin1("%1/%2") .arg(targetBasePath(t)) .arg(QLatin1String("/etc/lsb-release"))); if (!f.open(QIODevice::ReadOnly)) { //there is no lsb-release file... what now? t.maybeBroken = true; } else { QString info = QString::fromLatin1(f.readAll()); //read version QRegularExpression grep(QLatin1String(Constants::UBUNTU_CLICK_VERSION_REGEX),QRegularExpression::MultilineOption); QRegularExpressionMatch match = grep.match(info); if(!match.hasMatch()) { t.maybeBroken = true; } else { bool ok = false; t.majorVersion = match.captured(1).toInt(&ok); if(!ok) { t.maybeBroken = true; t.majorVersion = -1; } t.minorVersion = match.captured(2).toInt(&ok); if(!ok) { t.maybeBroken = true; t.minorVersion = -1; } } //read series grep.setPattern(QString::fromLatin1(Constants::UBUNTU_CLICK_SERIES_REGEX)); grep.setPatternOptions(QRegularExpression::MultilineOption); match = grep.match(info); if(!match.hasMatch()) { t.maybeBroken = true; } else { t.series = match.captured(1); } } *tg = t; return true; } /*! * \brief UbuntuClickTool::clickTargetFromTarget * Tries to get the Click target from a projectconfiguration, * \returns 0 if nothing was found */ const UbuntuClickTool::Target *UbuntuClickTool::clickTargetFromTarget(ProjectExplorer::Target *t) { #ifndef IN_TEST_PROJECT if(!t) return 0; ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(t->kit()); if(!tc || (tc->type() != QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID))) return 0; ClickToolChain *clickTc = static_cast(tc); if(!clickTc) return 0; return &clickTc->clickTarget(); #else Q_UNUSED(t); return 0; #endif } QString UbuntuClickTool::findOrCreateGccWrapper (const UbuntuClickTool::Target &target) { QString compiler; if(target.architecture == QStringLiteral("armhf")) compiler = QStringLiteral("arm-linux-gnueabihf-gcc"); else if(target.architecture == QStringLiteral("i386")) compiler = QStringLiteral("i686-linux-gnu-gcc"); else if(target.architecture == QStringLiteral("amd64")) compiler = QStringLiteral("x86_64-linux-gnu-gcc"); else { qWarning()<<"Invalid architecture, can not create gcc wrapper link"; return QString(); } return UbuntuClickTool::findOrCreateToolWrapper(compiler,target); } QString UbuntuClickTool::findOrCreateQMakeWrapper (const UbuntuClickTool::Target &target) { QString qmake; if(target.architecture == QStringLiteral("armhf")) qmake = QStringLiteral("qt5-qmake-arm-linux-gnueabihf"); else qmake = QStringLiteral("qmake"); return UbuntuClickTool::findOrCreateToolWrapper(qmake,target); } QString UbuntuClickTool::findOrCreateMakeWrapper (const UbuntuClickTool::Target &target) { return UbuntuClickTool::findOrCreateToolWrapper(QStringLiteral("make"),target); } QString UbuntuClickTool::mapIncludePathsForCMake(ProjectExplorer::Kit *k, const QString &in) { if (in.isEmpty()) return in; bool canMap = ProjectExplorer::ToolChainKitInformation::toolChain(k) && ProjectExplorer::ToolChainKitInformation::toolChain(k)->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID) && !ProjectExplorer::SysRootKitInformation::sysRoot(k).isEmpty(); if (!canMap) return in; QString tmp = in; QString replace = QString::fromLatin1("\\1%1/\\2").arg(ProjectExplorer::SysRootKitInformation::sysRoot(k).toUserOutput()); QStringList pathsToMap = { QLatin1String("var"),QLatin1String("bin"),QLatin1String("boot"),QLatin1String("dev"), QLatin1String("etc"),QLatin1String("lib"),QLatin1String("lib64"),QLatin1String("media"), QLatin1String("mnt"),QLatin1String("opt"),QLatin1String("proc"),QLatin1String("root"), QLatin1String("run"),QLatin1String("sbin"),QLatin1String("srv"),QLatin1String("sys"), QLatin1String("usr") }; for (const QString &path : pathsToMap) { QRegularExpression exp(QString::fromLatin1("(^|[^\\w+]|\\s+|[-=]\\w)\\/(%1)").arg(path)); tmp.replace(exp,replace); } return tmp; } QString UbuntuClickTool::findOrCreateToolWrapper (const QString &tool, const UbuntuClickTool::Target &target) { QString baseDir = Settings::settingsPath() .appendPath(QStringLiteral("%1-%2") .arg(target.framework) .arg(target.architecture)).toString(); QDir d(baseDir); if(!d.exists()) { if(!d.mkpath(baseDir)){ qWarning()<<"Could not create config directory."; return QString(); } } QString toolWrapper = (Utils::FileName::fromString(baseDir).appendPath(tool).toString()); QString toolTarget = QString::fromLatin1(Constants::UBUNTU_CLICK_CHROOT_WRAPPER).arg(Constants::UBUNTU_SCRIPTPATH); QFileInfo symlinkInfo(toolWrapper); if(!symlinkInfo.exists() || toolTarget != symlinkInfo.symLinkTarget()) { //in case of a broken link QFile::exists also will return false //lets try to delete it and ignore the error in case the file //simply does not exist QFile::remove(toolWrapper); if(!QFile::link(toolTarget,toolWrapper)) { qWarning()<<"Unable to create link for the tool wrapper: "< policy mapping is still hardcoded because there is no current API to query * it * * \note If no network connection is available the cache falls back to the last successful query or * the shipped framework list in the resource file */ struct FrameworkDesc{ FrameworkDesc() : develVersion(INT_MAX) {} QString base; QString baseVersion; QString sub; int develVersion; }; //this is a horrible complicated sorting function, it should be replaced with something more trivial static FrameworkDesc fwDescFromString (const QString &fw) { FrameworkDesc fwDesc; QStringList ext; fwDesc.base = UbuntuClickFrameworkProvider::getBaseFramework(fw,&ext); int lastDash = fwDesc.base.lastIndexOf(QStringLiteral("-")); if(lastDash > 0) { fwDesc.baseVersion = fwDesc.base.mid(lastDash+1); fwDesc.base = fwDesc.base.mid(0,lastDash); } QString sub; while(ext.size()) { sub = ext.takeFirst(); if(sub.startsWith(QStringLiteral("dev"))) { fwDesc.develVersion = sub.remove(QStringLiteral("dev")).toInt(); } else { fwDesc.sub = sub; } } return fwDesc; } static bool caseInsensitiveFWLessThan(const QString &s1, const QString &s2) { FrameworkDesc fwDesc1 = fwDescFromString(s1); FrameworkDesc fwDesc2 = fwDescFromString(s2); QCollator coll; coll.setNumericMode(true); coll.setCaseSensitivity(Qt::CaseInsensitive); int comp = coll.compare(fwDesc1.baseVersion,fwDesc2.baseVersion); if(comp < 0) return false; else if(comp > 0) return true; else { //from here on we deal only with the same base framework if(fwDesc1.sub.isEmpty() && !fwDesc2.sub.isEmpty()) return true; else if(!fwDesc1.sub.isEmpty() && fwDesc2.sub.isEmpty()) return false; else if(fwDesc1.sub == fwDesc2.sub) return fwDesc1.develVersion > fwDesc2.develVersion; else return fwDesc1.sub > fwDesc2.sub; } //this should never be reached return s1 > s2; } UbuntuClickFrameworkProvider *UbuntuClickFrameworkProvider::m_instance = nullptr; UbuntuClickFrameworkProvider::UbuntuClickFrameworkProvider() : QObject(0), m_policyCache { {QStringLiteral("ubuntu-sdk-13.10"),QStringLiteral("1.0")}, {QStringLiteral("ubuntu-sdk-14.04"),QStringLiteral("1.1")}, {QStringLiteral("ubuntu-sdk-14.10"),QStringLiteral("1.2")}, {QStringLiteral("ubuntu-sdk-15.04"),QStringLiteral("1.3")} }, m_manager(nullptr), m_currentRequest(nullptr), m_cacheUpdateTimer(nullptr) { Q_ASSERT_X(m_instance == nullptr,Q_FUNC_INFO,"UbuntuClickFrameworkProvider can only be instantiated once"); m_instance = this; m_cacheFilePath = Settings::settingsPath() .appendPath(QStringLiteral("framework-cache.json")) .toString(); m_manager = new QNetworkAccessManager(this); m_cacheUpdateTimer = new QTimer(this); m_cacheUpdateTimer->setInterval( 60 * 60 * 1000); //fire every hour m_cacheUpdateTimer->start(); connect(m_cacheUpdateTimer,SIGNAL(timeout()),this,SLOT(updateFrameworks())); //read the current state readCache(); //fire a update updateFrameworks(true); } UbuntuClickFrameworkProvider *UbuntuClickFrameworkProvider::instance() { return m_instance; } QStringList UbuntuClickFrameworkProvider::supportedFrameworks() const { return m_frameworkCache; } /*! * \brief UbuntuClickTool::getMostRecentFramework * returns the framework with the highest number supporting the subFramework * or a empty string of no framework with the given \a subFramework was found */ QString UbuntuClickFrameworkProvider::mostRecentFramework(const QString &subFramework) { //cache is ordered from newest -> oldest framework QString currFw; foreach(const QString &framework, m_frameworkCache) { QString basename; QStringList extensions; basename = getBaseFramework(framework,&extensions); if(basename.isEmpty()) continue; //this is a multi purpose framework if (extensions.size() == 0 || (extensions.size() == 1 && extensions[0].startsWith(QLatin1String("dev")) )) { if (currFw.isEmpty()) { currFw = framework; } //if the subframework is empty we return //the first baseframework we can find if(subFramework.isEmpty()) return currFw; continue; } if(extensions.contains(subFramework)) return framework; } return currFw; } QString UbuntuClickFrameworkProvider::frameworkPolicy(const QString &fw) const { #if 0 QProcess proc; proc.setProgram(QStringLiteral("aa-clickquery")); proc.setArguments(QStringList{ QStringLiteral("--click-framework=%1").arg(fw), QStringLiteral("--query=policy_version")}); proc.start(); proc.waitForFinished(); if(proc.exitCode() == 0 && proc.exitStatus() == QProcess::NormalExit) { return QString::fromUtf8(proc.readAllStandardOutput(); } #endif QString base = getBaseFramework(fw); if(m_policyCache.contains(base)) return m_policyCache[base]; return QString(); } QStringList UbuntuClickFrameworkProvider::getSupportedFrameworks() { return instance()->supportedFrameworks(); } /*! * \brief UbuntuClickTool::getMostRecentFramework * \sa UbuntuClickFrameworkProvider::mostRecentFramework */ QString UbuntuClickFrameworkProvider::getMostRecentFramework(const QString &subFramework) { return instance()->mostRecentFramework(subFramework); } QString UbuntuClickFrameworkProvider::getBaseFramework(const QString &framework, QStringList *extensions) { QRegularExpression expr(QLatin1String(Constants::UBUNTU_CLICK_BASE_FRAMEWORK_REGEX)); QRegularExpressionMatch match = expr.match(framework); if(match.hasMatch()) { QString basename = match.captured(1); if(extensions) { *extensions = QString(framework).replace(basename, QString()).split(QChar::fromLatin1('-'), QString::SkipEmptyParts); } return basename; } return QString(); } void UbuntuClickFrameworkProvider::requestFinished() { //if the current request is not set anymore we already called deleteLater if(!m_currentRequest) return; QByteArray data = m_currentRequest->readAll(); //make sure everything is cleaned up m_currentRequest->deleteLater(); m_currentRequest = nullptr; //if we received nothing, stop if(data.isEmpty()) return; //make sure we got valid data QStringList newData = parseData(data); if(newData.isEmpty()) return; bool cacheIsUp2Date = (newData == m_frameworkCache); if(!cacheIsUp2Date) { QFile cache(m_cacheFilePath); if(cache.open(QIODevice::WriteOnly | QIODevice::Truncate)) { cache.write(data); cache.close(); } else { qWarning()<<"Could not create framework cache file, falling back to default values"; } m_frameworkCache = newData; emit frameworksUpdated(); } } void UbuntuClickFrameworkProvider::requestError() { //if the current request is not set anymore we already called deleteLater if(!m_currentRequest) return; qWarning()<<"Could not update the framework cache file. Probably there is no network connection."; m_currentRequest->deleteLater(); m_currentRequest = nullptr; } void UbuntuClickFrameworkProvider::updateFrameworks(bool force) { if(m_currentRequest) return; if(!force) { //update every 12 hours QFileInfo info(m_cacheFilePath); if(info.exists() && info.lastModified().secsTo(QDateTime::currentDateTime()) < (12*60*60)) return; } //fire the request m_currentRequest = m_manager->get(QNetworkRequest(QUrl(QStringLiteral("https://myapps.developer.ubuntu.com/dev/api/click-framework/")))); connect(m_currentRequest,SIGNAL(finished()),this,SLOT(requestFinished())); connect(m_currentRequest,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(requestError())); } void UbuntuClickFrameworkProvider::readCache() { QFile cache(m_cacheFilePath); if(!cache.exists() || !cache.open(QIODevice::ReadOnly)) { readDefaultValues(); return; } QStringList data = parseData(cache.readAll()); if(!data.isEmpty()) { m_frameworkCache = data; emit frameworksUpdated(); } else { //if the cache is empty fall back to the default values if(m_frameworkCache.isEmpty()) readDefaultValues(); } } void UbuntuClickFrameworkProvider::readDefaultValues() { QFile cache(QStringLiteral(":/ubuntu/click-framework.json")); if(Q_UNLIKELY(cache.open(QIODevice::ReadOnly) == false)) { //This codepath is very unlikely, but lets still make sure there is a message to the user qWarning()<<"Could not read cache file OR default values. No frameworks are available to select from"; return; } QStringList data = parseData(cache.readAll()); if(!data.isEmpty()) { m_frameworkCache = data; emit frameworksUpdated(); } } QStringList UbuntuClickFrameworkProvider::parseData(const QByteArray &data) const { QJsonParseError parseError; parseError.error = QJsonParseError::NoError; QJsonDocument doc = QJsonDocument::fromJson(data,&parseError); if(parseError.error != QJsonParseError::NoError) { qWarning()<< "Could not parse the framework cache: " << parseError.errorString(); return QStringList(); } QJsonObject obj = doc.object(); QStringList result; for(auto i = obj.constBegin(); i != obj.constEnd(); i++ ) { if(!i.key().startsWith(QStringLiteral("ubuntu-sdk"))) continue; if(i.value().toString() == QStringLiteral("available")) { result += i.key(); } } qSort(result.begin(),result.end(),caseInsensitiveFWLessThan); return result; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuclickmanifest.cpp0000644000015600001650000003267212705421114020000 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuclickmanifest.h" #include "ubuntuconstants.h" #include "ubuntuclicktool.h" #include "ubuntushared.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu::Internal; enum { debug = 0 }; UbuntuClickManifest::UbuntuClickManifest(QObject *parent) : QObject(parent), m_bInitialized(false), m_bNameDashReplaced(false) { QFile manifestAppFile(QLatin1String(":/ubuntu/manifestlib.js")); if (!manifestAppFile.open(QIODevice::ReadOnly)) { if(debug) qDebug() << QLatin1String("unable to open js app"); return; } QString manifestApp = QString::fromLatin1(manifestAppFile.readAll()); manifestAppFile.close(); QJSValue val = engine.evaluate(manifestApp,QStringLiteral("manifestlib.js")); if (val.isNull()) { qWarning() << QLatin1String("unable to process app"); return; } } void UbuntuClickManifest::setName(QString name) { if (!isInitialized()) { return; } callSetStringFunction(QLatin1String("setName"),name); emit nameChanged(); } QString UbuntuClickManifest::name() { return callGetStringFunction(QLatin1String("getName")); } void UbuntuClickManifest::setVersion(QString version) { if (!isInitialized()) { return; } callSetStringFunction(QLatin1String("setVersion"),version); emit versionChanged(); } QString UbuntuClickManifest::version() { return callGetStringFunction(QLatin1String("getVersion")); } void UbuntuClickManifest::setDescription(QString description) { if (!isInitialized()) { return; } callSetStringFunction(QLatin1String("setDescription"),description); emit descriptionChanged(); } QString UbuntuClickManifest::description() { return callGetStringFunction(QLatin1String("getDescription")); } void UbuntuClickManifest::setMaintainer(QString maintainer) { if (!isInitialized()) { return; } callSetStringFunction(QLatin1String("setMaintainer"),maintainer); emit maintainerChanged(); } QString UbuntuClickManifest::maintainer() { if (!isInitialized()) { return QString(); } return callGetStringFunction(QLatin1String("getMaintainer")); } void UbuntuClickManifest::setTitle(QString title) { if (!isInitialized()) { return; } callSetStringFunction(QLatin1String("setTitle"),title); emit titleChanged(); } QString UbuntuClickManifest::title() { if (!isInitialized()) { return QString(); } return callGetStringFunction(QLatin1String("getTitle")); } void UbuntuClickManifest::setPolicyVersion(const QString &version) { if (!isInitialized()) { return; } QStringList args; args << version; callSetStringListFunction(QLatin1String("setPolicyVersion"),args); emit policyVersionChanged(); } QString UbuntuClickManifest::policyVersion () { if (!isInitialized()) { return QString(); } return callGetStringFunction(QLatin1String("getPolicyVersion")); } void UbuntuClickManifest::setPolicyGroups(QStringList groups) { if (!isInitialized()) { return; } QStringList args; args << groups.join(QLatin1String(" ")); callSetStringListFunction(QLatin1String("setPolicyGroups"),args); emit policyGroupsChanged(); } QStringList UbuntuClickManifest::policyGroups() { if (!isInitialized()) { return QStringList(); } QStringList retval = callGetStringListFunction(QLatin1String("getPolicyGroups")); return retval; } QList UbuntuClickManifest::hooks() { QList hooks; if (!isInitialized()) { return hooks; } QJSValue scriptHooks = callGetFunction(QLatin1String("getHooks"),QJSValueList()); if(!scriptHooks.isObject()) return hooks; QJSValueIterator it(scriptHooks); while (it.hasNext()) { it.next(); QJSValue appDescriptor = it.value(); if(!appDescriptor.isObject()) { printToOutputPane(tr("Invalid hook in manifest.json file.")); continue; } if(!appDescriptor.hasProperty(QLatin1String("apparmor"))) { printToOutputPane(tr("The apparmor path is missing in the manifest file")); continue; } bool isScope = appDescriptor.hasProperty(QLatin1String("scope")); bool isApp = appDescriptor.hasProperty(QLatin1String("desktop")); if( (isScope && isApp) || (!isScope && !isApp)) { printToOutputPane(tr("The manifest file needs to specify if this is a app or a scope")); continue; } Hook app; app.appId = it.name(); if(isApp) app.desktopFile = it.value().property(QLatin1String("desktop")).toString(); if(isScope) app.scope = it.value().property(QLatin1String("scope")).toString(); app.appArmorFile = it.value().property(QLatin1String("apparmor")).toString(); hooks.append(app); } return hooks; } void UbuntuClickManifest::setHook(const UbuntuClickManifest::Hook &hook) { Q_UNUSED(hook); QJSValue scriptValue = engine.newObject(); scriptValue.setProperty(QStringLiteral("appId"),hook.appId); scriptValue.setProperty(QStringLiteral("apparmor"),hook.appArmorFile); if(!hook.desktopFile.isEmpty()) { scriptValue.setProperty(QStringLiteral("desktop"),hook.desktopFile); } else if(!hook.appArmorFile.isEmpty()) { scriptValue.setProperty(QStringLiteral("scope"),hook.scope); } else //not known return; callSetFunction(QStringLiteral("setHook"),QJSValueList{scriptValue}); } void UbuntuClickManifest::setFrameworkName(const QString &name) { if (!isInitialized()) { return; } callSetStringFunction(QLatin1String("setFrameworkName"),name); emit frameworkNameChanged(name); } QString UbuntuClickManifest::frameworkName() { if (!isInitialized()) { return QString(); } return callGetStringFunction(QLatin1String("getFrameworkName")); } QString UbuntuClickManifest::appArmorFileName(const QString &appId) { if (!isInitialized()) { return QString(); } QJSValue v = callGetFunction(QLatin1String("getAppArmorFileName"),QJSValueList()<projectManager()->mimeType(); QString proName = proj->projectFilePath().toString(); bool isUbuntuProject = (mimeType == QLatin1String(Constants::UBUNTUPROJECT_MIMETYPE)); bool isUbuntuHtmlProject = proName.endsWith(QLatin1String(Constants::UBUNTUHTMLPROJECT_SUFFIX)); QString defFramework; if(isUbuntuProject && isUbuntuHtmlProject) { defFramework = UbuntuClickFrameworkProvider::getMostRecentFramework(QLatin1String("html")); if(defFramework.isEmpty()) defFramework = QLatin1String(Constants::UBUNTU_DEFAULT_HTML_FRAMEWORK); } else { defFramework = UbuntuClickFrameworkProvider::getMostRecentFramework(QLatin1String("qml")); if(defFramework.isEmpty()) defFramework = QLatin1String(Constants::UBUNTU_DEFAULT_QML_FRAMEWORK); } data.replace(QLatin1String("myFramework"),defFramework); QString tmpProjectName = proj->displayName(); tmpProjectName.replace(QLatin1String("_"),QLatin1String("-")); data.replace(QLatin1String("myapp"),tmpProjectName); QString original_security_manifest_name = QString(QLatin1String("%0.json")).arg(tmpProjectName); QString replaced_security_manifest_name = original_security_manifest_name; replaced_security_manifest_name.replace(QLatin1String("_"),QLatin1String("-")); data.replace(original_security_manifest_name,replaced_security_manifest_name); QString original_desktop_file = QString(QLatin1String("%0.desktop")).arg(tmpProjectName); if (m_bNameDashReplaced) { original_desktop_file.replace(QLatin1String("-"),QLatin1String("_")); data.replace(QString(QLatin1String("%0.desktop")).arg(tmpProjectName),original_desktop_file); } } if(!loadFromString(data)){ if(errorMessage) *errorMessage = tr("Parsing failed, please check if the syntax is correct."); return false; } return true; } bool UbuntuClickManifest::loadFromString(const QString &data) { //@TODO probably return the error message QJSValue ret = callFunction(QStringLiteral("fromJSON"),QJSValueList{QJSValue(data)}); bool success = ret.toBool(); if(success) { m_bInitialized = true; emit loaded(); } return success; } QJSValue UbuntuClickManifest::callFunction(QString functionName, QJSValueList args) { QJSValue global = engine.globalObject(); QJSValue cmd = global.property(functionName); return cmd.call(args); } void UbuntuClickManifest::callSetFunction(QString functionName, QJSValueList args) { callFunction(functionName,args); } QJSValue UbuntuClickManifest::callGetFunction(QString functionName, QJSValueList args) { return callFunction(functionName,args); } QStringList UbuntuClickManifest::callGetStringListFunction(QString functionName) { QJSValue retval = callFunction(functionName,QJSValueList()); return retval.toVariant().toStringList(); } QString UbuntuClickManifest::callGetStringFunction(QString functionName) { QJSValue retval = callFunction(functionName,QJSValueList()); return retval.toVariant().toString(); } void UbuntuClickManifest::callSetStringListFunction(QString functionName, QStringList args) { QJSValueList vargs; foreach (QString arg, args) vargs << QJSValue(arg); callSetFunction(functionName,vargs); } void UbuntuClickManifest::callSetStringFunction(QString functionName, QString args) { QJSValueList vargs; vargs << QJSValue(args); callSetFunction(functionName,vargs); } QStringList UbuntuClickManifest::callGetStringListFunction(QString functionName, QString args) { QJSValueList vargs; vargs << QJSValue(args); QJSValue retval = callFunction(functionName,vargs); return retval.toVariant().toStringList(); } /*! * \brief UbuntuClickManifest::Hook::type * Returns the hook type depending on which fields are not empty */ UbuntuClickManifest::Hook::Type UbuntuClickManifest::Hook::type() const { if(!desktopFile.isEmpty()) return Application; else if(!scope.isEmpty()) return Scope; return Invalid; } ./src/ubuntu/ubunturemotedeployconfiguration.h0000644000015600001650000000463212705421114022124 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUDEPLOYCONFIGURATION_H #define UBUNTU_INTERNAL_UBUNTUDEPLOYCONFIGURATION_H #include namespace Ubuntu { namespace Internal { class UbuntuRemoteDeployConfiguration : public ProjectExplorer::DeployConfiguration { Q_OBJECT public: UbuntuRemoteDeployConfiguration(ProjectExplorer::Target *target); UbuntuRemoteDeployConfiguration(ProjectExplorer::Target *target, UbuntuRemoteDeployConfiguration *source); ProjectExplorer::NamedWidget *createConfigWidget() override; }; class UbuntuRemoteDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory { Q_OBJECT public: explicit UbuntuRemoteDeployConfigurationFactory(QObject *parent = 0); QList availableCreationIds(ProjectExplorer::Target *parent) const override; QString displayNameForId(const Core::Id id) const override; bool canCreate(ProjectExplorer::Target *parent, const Core::Id id) const override; ProjectExplorer::DeployConfiguration *create(ProjectExplorer::Target *parent, const Core::Id id) override; bool canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const override; ProjectExplorer::DeployConfiguration *restore(ProjectExplorer::Target *parent, const QVariantMap &map) override; bool canClone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *product) const override; ProjectExplorer::DeployConfiguration *clone(ProjectExplorer::Target *parent, ProjectExplorer::DeployConfiguration *product) override; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUDEPLOYCONFIGURATION_H ./src/ubuntu/ubuntupolicygroupmodel.cpp0000644000015600001650000000513112705421114020547 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntupolicygroupmodel.h" #include using namespace Ubuntu::Internal; UbuntuPolicyGroupModel::UbuntuPolicyGroupModel(QObject *parent) : QStringListModel(parent), m_policyVersion(QStringLiteral("1.1")), m_bLocal(false) { connect(&m_process,SIGNAL(message(QString)),this,SLOT(onMessage(QString))); connect(&m_process,SIGNAL(finished(QString,int)),this,SLOT(onFinished(QString,int))); } void UbuntuPolicyGroupModel::setPolicyVersion(const QString &policyVersion) { if(!policyVersion.isEmpty()) m_policyVersion = policyVersion; } QString UbuntuPolicyGroupModel::policyVersion() const { return m_policyVersion; } void UbuntuPolicyGroupModel::scanPolicyGroups() { QStringList cmd; if (!isLocal()) { cmd << QString::fromLatin1("adb shell aa-easyprof --list-policy-groups --policy-vendor=ubuntu --policy-version=%1") .arg(m_policyVersion); } else { cmd << QString::fromLatin1("aa-easyprof --list-policy-groups --policy-vendor=ubuntu --policy-version=%1") .arg(m_policyVersion); } m_process.append(cmd); m_process.start(QLatin1String("Scanning policy groups")); } void UbuntuPolicyGroupModel::onMessage(QString line) { m_replies.append(line.trimmed().replace(QLatin1String("\r"),QLatin1String("")).split(QLatin1String("\n"))); } void UbuntuPolicyGroupModel::onFinished(QString, int result) { if (result != 0 && !isLocal()) { //first try on the device failed, fall back to local m_bLocal = true; m_replies.clear(); scanPolicyGroups(); return; } else if ( result == 0) { //we got a result lets show it setStringList(m_replies); m_replies.clear(); emit scanComplete(true); return; } //local and device failed, no result :/ setStringList(m_replies); m_replies.clear(); emit scanComplete(false); } ./src/ubuntu/ubuntumenu.cpp0000644000015600001650000012235712705421114016130 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntumenu.h" #include "ubuntushared.h" #include "ubuntuconstants.h" #include "ubuntudevicemode.h" #include "ubuntuproject.h" #include "ubuntuclicktool.h" #include "ubuntudevice.h" #include "ubuntuclickmanifest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu; using namespace Ubuntu::Internal; enum { debug = 0 }; UbuntuMenu *UbuntuMenu::m_instance = 0; UbuntuMenu *UbuntuMenu::instance() { return m_instance; } QAction *UbuntuMenu::menuAction(const Core::Id &id) { UbuntuMenu* men = UbuntuMenu::instance(); if(men->m_actions.contains(id)) return men->m_actions[id]; return 0; } UbuntuMenu::UbuntuMenu(QObject *parent) : QObject(parent) { m_instance = this; m_obj = getMenuJSON(); connect(&m_ubuntuProcess,SIGNAL(message(QString)),this,SLOT(onMessage(QString))); connect(&m_ubuntuProcess,SIGNAL(finished(QString,int)),this,SLOT(onFinished(QString,int))); connect(&m_ubuntuProcess,SIGNAL(finished(const QProcess*,QString,int)),this,SLOT(onFinished(const QProcess*,QString,int))); connect(&m_ubuntuProcess,SIGNAL(started(QString)),this,SLOT(onStarted(QString))); connect(&m_ubuntuProcess,SIGNAL(error(QString)),this,SLOT(onError(QString))); connect(ProjectExplorer::ProjectExplorerPlugin::instance(),SIGNAL(updateRunActions()),this,SLOT(slotUpdateActions())); connect(UbuntuDeviceMode::instance(),SIGNAL(updateDeviceActions()),this,SLOT(slotUpdateActions())); ProjectExplorer::ProjectTree *ptree = ProjectExplorer::ProjectTree::instance(); connect(ptree, SIGNAL(aboutToShowContextMenu(ProjectExplorer::Project*,ProjectExplorer::Node*)), this, SLOT(setContextMenuProject(ProjectExplorer::Project*))); } void UbuntuMenu::slotUpdateActions() { ProjectExplorer::Project* startupProject = ProjectExplorer::SessionManager::startupProject(); bool isQmlProject = false; bool isQmakeProject = false; bool isUbuntuProject = false; bool isUbuntuHtmlProject = false; bool isClickTarget = false; if (startupProject) { isQmlProject = (startupProject->projectManager()->mimeType() == QLatin1String(Constants::QMLPROJECT_MIMETYPE)); isQmakeProject = (startupProject->projectManager()->mimeType() == QLatin1String(Constants::QMAKE_MIMETYPE)); isUbuntuProject = (startupProject->projectManager()->mimeType() == QLatin1String(Constants::UBUNTUPROJECT_MIMETYPE)); isUbuntuHtmlProject = isProperUbuntuHtmlProject(startupProject); isUbuntuProject = isUbuntuProject || isUbuntuHtmlProject || isQmlProject; isClickTarget = startupProject->activeTarget() && startupProject->activeTarget()->kit() && ProjectExplorer::ToolChainKitInformation::toolChain(startupProject->activeTarget()->kit()) && ProjectExplorer::ToolChainKitInformation::toolChain(startupProject->activeTarget()->kit())->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID); } //bool canRun = projectExplorerInstance->canRun(startupProject,ProjectExplorer::NormalRunMode); bool projectOpen = (startupProject!=NULL); bool deviceDetected = !UbuntuDeviceMode::instance()->device().isNull(); foreach(QAction* act, m_actions) { bool requiresDevice = act->property(Constants::UBUNTU_MENUJSON_DEVICEREQUIRED).toBool(); bool requiresProject = act->property(Constants::UBUNTU_MENUJSON_PROJECTREQUIRED).toBool(); bool requiresQmlProject = act->property(Constants::UBUNTU_MENUJSON_QMLPROJECTREQUIRED).toBool(); bool requiresQmakeProject = act->property(Constants::UBUNTU_MENUJSON_QMAKEPROJECTREQUIRED).toBool(); bool requiresUbuntuProject = act->property(Constants::UBUNTU_MENUJSON_UBUNTUPROJECTREQUIRED).toBool(); bool requiresUbuntuHtmlProject = act->property(Constants::UBUNTU_MENUJSON_UBUNTUHTMLPROJECTREQUIRED).toBool(); bool requiresClickToolchain = act->property(Constants::UBUNTU_MENUJSON_CLICKTOOLCHAINREQUIRED).toBool(); bool actionEnabled = ( (requiresQmakeProject ? isQmakeProject : true) && (requiresQmlProject ? isQmlProject : true) && (requiresUbuntuHtmlProject ? isUbuntuHtmlProject : true) && (requiresDevice ? deviceDetected : true) && (requiresProject ? projectOpen : true) && (requiresUbuntuProject ? isUbuntuProject : true) && (requiresClickToolchain ? isClickTarget : true) ); act->setEnabled( actionEnabled ); } } void UbuntuMenu::onStarted(QString cmd) { printToOutputPane(QString::fromLatin1(Constants::UBUNTUMENU_ONSTARTED).arg(cmd)); } void UbuntuMenu::onMessage(QString msg) { printToOutputPane(msg); } void UbuntuMenu::onError(QString msg) { printToOutputPane(QString::fromLatin1(Constants::UBUNTUMENU_ONERROR).arg(msg)); } void UbuntuMenu::onFinished(QString cmd, int code) { emit finished_action(cmd); printToOutputPane(QString::fromLatin1(Constants::UBUNTUMENU_ONFINISHED).arg(cmd).arg(code)); } void UbuntuMenu::onFinished(const QProcess *programm, QString cmd, int) { emit finished_action(programm,cmd); } void UbuntuMenu::createManifestFile() { if(Q_UNLIKELY(!m_ctxMenuProject)) return; bool changed = false; QString manifestFilePath = m_ctxMenuProject->projectDirectory().toString() + QDir::separator() + QLatin1String("manifest.json"); UbuntuClickManifest manifest; if(QFile::exists(manifestFilePath)) { if(!manifest.load(manifestFilePath)) { QMessageBox::warning(Core::ICore::mainWindow(),tr("Error"), tr("The manifest.json file already exists, but can not be opened.")); return; } } else { if(!manifest.load(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_DEFAULT_MANIFEST),m_ctxMenuProject.data())) { QMessageBox::warning(Core::ICore::mainWindow(),tr("Error"), tr("Could not open the manifest.json template")); return; } changed = true; manifest.setFileName(manifestFilePath); manifest.save(); } QList hooks = manifest.hooks(); foreach(const UbuntuClickManifest::Hook &hook, hooks) { if(!hook.appArmorFile.isEmpty()) { UbuntuClickManifest aaFile; QString aaFilePath = QDir::cleanPath(m_ctxMenuProject->projectDirectory().toString() + QDir::separator() + hook.appArmorFile); if(QFile::exists(aaFilePath)) continue; if(!aaFile.load(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_DEFAULT_MYAPP))) { printToOutputPane(tr("Could not open the apparmor template")); continue; } changed = true; aaFile.setFileName(aaFilePath); aaFile.save(); } } QMessageBox::information(Core::ICore::mainWindow(), tr("Files created"), changed ? tr("The manifest.json and apparmor files have been created in the project directory.\nPlease make sure to add them to your project file.") : tr("All required files already exist in your project directory")); } void UbuntuMenu::setContextMenuProject(ProjectExplorer::Project *p) { m_ctxMenuProject = p; } QString UbuntuMenu::menuPath(QString fileName) { return Constants::UBUNTU_MENUPATH + fileName; } QJsonDocument UbuntuMenu::getMenuJSON() { QByteArray contents; QString errorMsg; if (readFile(menuPath(QLatin1String(Constants::UBUNTU_MENUJSON)),&contents, &errorMsg) == false) { qWarning() << __PRETTY_FUNCTION__ << errorMsg; } QJsonParseError error; QJsonDocument retval = QJsonDocument::fromJson(contents,&error); if (error.error != QJsonParseError::NoError) { qWarning() << error.errorString(); } return retval; } void UbuntuMenu::parseMenu(QJsonObject obj, Core::ActionContainer*& parent, const Core::Id &group) { if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_SUBMENU))) { QString menuName, menuId; if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_NAME))) { menuName = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_NAME)).toString(); } if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_ID))) { menuId = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_ID)).toString(); } Core::ActionContainer *actionContainer = Core::ActionManager::createMenu(Core::Id(menuId.toUtf8().constData())); actionContainer->menu()->setTitle(menuName); actionContainer->menu()->setObjectName(menuId); QJsonValue submenu = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_SUBMENU)); if (submenu.isArray()) { QJsonArray submenuArray = submenu.toArray(); for (int idx=0; idxaddMenu(actionContainer,group); } } else if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_ACTIONS))) { QString actionName; QString actionId; QString actionKeySequence; QString actionWorkingDirectory; QStringList contexts = QStringList()<setObjectName(actionId); act->setProperty(Constants::UBUNTU_MENUJSON_PROJECTREQUIRED,actionProjectRequired); act->setProperty(Constants::UBUNTU_MENUJSON_DEVICEREQUIRED,actionDeviceRequired); act->setProperty(Constants::UBUNTU_MENUJSON_QMAKEPROJECTREQUIRED,actionQmakeProjectRequired); act->setProperty(Constants::UBUNTU_MENUJSON_QMLPROJECTREQUIRED,actionQmlProjectRequired); act->setProperty(Constants::UBUNTU_MENUJSON_UBUNTUPROJECTREQUIRED,actionUbuntuProjectRequired); act->setProperty(Constants::UBUNTU_MENUJSON_UBUNTUHTMLPROJECTREQUIRED,actionUbuntuHtmlProjectRequired); act->setProperty(Constants::UBUNTU_MENUJSON_SAVEREQUIRED,actionSaveRequired); act->setProperty(Constants::UBUNTU_MENUJSON_CLICKTARGETREQUIRED,clickTargetRequired); act->setProperty(Constants::UBUNTU_MENUJSON_CLICKTOOLCHAINREQUIRED,clickToolchainRequired); connect(act, SIGNAL(triggered()), this, SLOT(menuItemTriggered())); m_actions.insert(Core::Id(actionId.toUtf8().constData()),act); foreach(const QString& context,contexts) { Core::Command *cmd = Core::ActionManager::registerAction(act, Core::Id(actionId.toUtf8().constData()), Core::Context(context.toUtf8().constData())); if (actionKeySequence.isEmpty() == false) { cmd->setDefaultKeySequence(QKeySequence(actionKeySequence)); } if (actionWorkingDirectory.isEmpty() == false) { act->setProperty(Constants::UBUNTU_MENUJSON_WORKINGDIRECTORY,actionWorkingDirectory); } //hide if the context does not match creators current context cmd->setAttribute(Core::Command::CA_Hide); if (parent == NULL) { qWarning() << Constants::ERROR_MSG_NO_MENU_DEFINED; } else { parent->addAction(cmd,group); } } } } bool UbuntuMenu::isProperUbuntuHtmlProject(ProjectExplorer::Project *project) const { if (Q_UNLIKELY(NULL == project)) return false; UbuntuProject* ubuntuProject = qobject_cast(project); if (NULL == ubuntuProject) return false; return ubuntuProject->filesFileName().endsWith(QLatin1String(Constants::UBUNTUHTMLPROJECT_SUFFIX)); } void UbuntuMenu::menuItemTriggered() { QAction* act = qobject_cast(sender()); if (act) { // if we are executing something now, then kill it! if (m_ubuntuProcess.state() != QProcess::NotRunning) { m_ubuntuProcess.stop(); } QVariant projectRequired = act->property(Constants::UBUNTU_MENUJSON_PROJECTREQUIRED); ProjectExplorer::Project* project = NULL; UbuntuClickManifest manifest; if (projectRequired.isValid() && projectRequired.toBool() == true) { project = ProjectExplorer::SessionManager::startupProject(); if (project == NULL) { QMessageBox::information(Core::ICore::mainWindow(),QLatin1String(Constants::UBUNTU_DIALOG_NO_PROJECTOPEN_TITLE),QLatin1String(Constants::UBUNTU_DIALOG_NO_PROJECTOPEN_TEXT)); return; } else { QString manifestPath = project->projectDirectory() .appendPath(QStringLiteral("manifest.json")).toString(); if (!manifest.load(manifestPath)) { qWarning() << "Could not load manifest from path" << manifestPath; } } } QVariant saveModifiedFilesRequired = act->property(Constants::UBUNTU_MENUJSON_SAVEREQUIRED); if (saveModifiedFilesRequired.isValid() && saveModifiedFilesRequired.toBool()==true) { ProjectExplorer::ProjectExplorerPlugin::instance()->saveModifiedFiles(); } if (m_commandMap.contains(act->objectName())) { QJsonValueList actions = m_commandMap.value(act->objectName()); QString queryData; bool bQueryOk = false; bool bQuery = false; for (int idx=0; idx < actions.size(); idx++) { QJsonValue value = actions.at(idx); if (value.isObject()) { QJsonObject obj = value.toObject(); // check if the object is a querydialog if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_QUERYDIALOG))) { QJsonValue queryDialog = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_QUERYDIALOG)); if (queryDialog.isObject()) { QJsonObject queryDialogObj = queryDialog.toObject(); QString queryDialogTitle; QString queryDialogMessage; QString queryDialogValue; if (queryDialogObj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_TITLE))) { queryDialogTitle = queryDialogObj.value(QLatin1String(Constants::UBUNTU_MENUJSON_TITLE)).toString(); } if (queryDialogObj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_MESSAGE))) { queryDialogMessage = queryDialogObj.value(QLatin1String(Constants::UBUNTU_MENUJSON_MESSAGE)).toString(); } if (queryDialogObj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_VALUE))) { queryDialogValue = queryDialogObj.value(QLatin1String(Constants::UBUNTU_MENUJSON_VALUE)).toString(); } queryData = QInputDialog::getText(Core::ICore::mainWindow(), queryDialogTitle, queryDialogMessage, QLineEdit::Normal, queryDialogValue, &bQueryOk); // raise a flag that there is query data available for future actions bQuery = true; // if user has cancelled if (bQueryOk == false) { // clear queue m_ubuntuProcess.clear(); return; } } // check if messageDialog } else if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_MESSAGEDIALOG))) { QJsonValue messageDialog = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_MESSAGEDIALOG)); if (messageDialog.isObject()) { QJsonObject messageDialogObj = messageDialog.toObject(); QString messageDialogTitle; QString messageDialogMessage; if (messageDialogObj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_TITLE))) { messageDialogTitle = messageDialogObj.value(QLatin1String(Constants::UBUNTU_MENUJSON_TITLE)).toString(); } if (messageDialogObj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_MESSAGE))) { messageDialogMessage = messageDialogObj.value(QLatin1String(Constants::UBUNTU_MENUJSON_MESSAGE)).toString(); } QMessageBox::information(Core::ICore::mainWindow(), messageDialogTitle, messageDialogMessage); } //check if metacall } else if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_METACALL))) { QJsonValue metaCall = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_METACALL)); if (metaCall.isObject()) { QJsonObject metaCallObj = metaCall.toObject(); QByteArray methodName; QList args; if(!metaCallObj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_METACALL_METHOD))) { qWarning()<<"Metacall menuitem does not contain a method name"; return; } methodName = metaCallObj[QLatin1String(Constants::UBUNTU_MENUJSON_METACALL_METHOD)].toString().toLocal8Bit(); if(methodName.isEmpty()){ qWarning()<< "Property method of a metacall menuitem has to be a string and can not be empty"; return; } //we first need to fill a list of variant values, otherwise we get dangling pointers in QGenericArgument //because of QVariants getting out of scope QVariantList varArgs; if(metaCallObj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_METACALL_ARGS))) { QJsonArray arr = metaCallObj[QLatin1String(Constants::UBUNTU_MENUJSON_METACALL_ARGS)].toArray(); foreach(const QJsonValue &val,arr) { switch(val.type()) { case QJsonValue::Undefined: case QJsonValue::Null: { qWarning()<<"Arguments of a metacall can not be null"; return; break; } case QJsonValue::Bool: case QJsonValue::Double: case QJsonValue::String: case QJsonValue::Array: case QJsonValue::Object: { varArgs.append(val.toVariant()); break; } } } } foreach(const QVariant &val,varArgs) args.append(QGenericArgument(val.typeName(),val.data())); bool ok = false; int argsCount = args.size(); if (argsCount > 9) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2)), (args.at(3)), (args.at(4)), (args.at(5)), (args.at(6)), (args.at(7)), (args.at(8)), (args.at(9))); } else if (argsCount > 8) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2)), (args.at(3)), (args.at(4)), (args.at(5)), (args.at(6)), (args.at(7)), (args.at(8))); } else if (argsCount > 7) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2)), (args.at(3)), (args.at(4)), (args.at(5)), (args.at(6)), (args.at(7))); } else if (argsCount > 6) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2)), (args.at(3)), (args.at(4)), (args.at(5)), (args.at(6))); } else if (argsCount > 5) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2)), (args.at(3)), (args.at(4)), (args.at(5))); } else if (argsCount > 4) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2)), (args.at(3)), (args.at(4))); } else if (argsCount > 3) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2)), (args.at(3))); } else if (argsCount > 2) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1)), (args.at(2))); } else if (argsCount > 1) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0)), (args.at(1))); } else if (argsCount > 0) { ok = QMetaObject::invokeMethod(this,methodName.data(), (args.at(0))); } else { ok = QMetaObject::invokeMethod(this,methodName.data()); } if(!ok) qWarning()<<"Invoke of "<projectDirectory().toString(); QString displayName = project->displayName(); QString folderName = projectDirectory; // Bug 1212937 workaround folderName = folderName.replace(QString(QLatin1String("%0/")).arg(QFileInfo(projectDirectory).path()),QLatin1String("")); QStringList projectFiles = project->files(ProjectExplorer::Project::AllFiles); QString workingDirectoryData = act->property(Constants::UBUNTU_MENUJSON_WORKINGDIRECTORY).toString(); if (workingDirectoryData.isEmpty() == false) { workingDirectory = workingDirectoryData.arg(projectDirectory); } else { workingDirectory = projectDirectory; } command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_PROJECTDIRECTORY),projectDirectory); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_FOLDERNAME),folderName); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_DISPLAYNAME),displayName); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_PROJECTFILES),projectFiles.join(QLatin1String(" "))); if (manifest.isInitialized()) { command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_NAME_FROM_MANIFEST),manifest.name()); } if (isProperUbuntuHtmlProject(project)) { command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_APP_RUNNER_EXECNAME), QLatin1String(Constants::UBUNTUHTML_PROJECT_LAUNCHER_EXE)); } QVariant clickTargetRequired = act->property(Constants::UBUNTU_MENUJSON_CLICKTARGETREQUIRED); if(clickTargetRequired.isValid() && clickTargetRequired.toBool()) { UbuntuClickTool::Target clickTarget; if(!UbuntuClickTool::getTargetFromUser(&clickTarget)) return; command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_CLICK_PACKAGING_FOLDER) ,QString::fromLatin1(Constants::UBUNTU_CLICK_CLICK_PACKAGE_DIR)); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_CLICK_ARCH),clickTarget.architecture); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_CLICK_FRAMEWORK),clickTarget.framework); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_CLICK_SERIES),clickTarget.series); //this is a clicktarget, so we change the builddirectory to the current active buildconfig //directory ProjectExplorer::Target* qtcTarget = project->activeTarget(); if(!qtcTarget) return; ProjectExplorer::BuildConfiguration* qtcBuildConfig = qtcTarget->activeBuildConfiguration(); if(!qtcBuildConfig) return; workingDirectory = qtcBuildConfig->buildDirectory().toString(); } if(project->id() == Constants::GO_PROJECT_ID) { QVariant ret(QVariant::String); QGenericReturnArgument genRet(ret.typeName(),ret.data()); if(QMetaObject::invokeMethod(project,"applicationNames",genRet)) { command = command.replace(QLatin1String(Constants::UBUNTU_GO_BUILD_TARGETS),ret.toString()); } } } bool requiresDevice = act->property(Constants::UBUNTU_MENUJSON_DEVICEREQUIRED).toBool(); if(requiresDevice) { UbuntuDevice::ConstPtr device = UbuntuDeviceMode::instance()->device(); if (device) { if( device->deviceState() != ProjectExplorer::IDevice::DeviceReadyToUse ) { QMessageBox::warning(Core::ICore::mainWindow(),tr("Device not ready"),tr("The currently selected device is not ready, please select another one on the devices mode")); return; } command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_DEVICE_IP),device->sshParameters().host); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_DEVICE_USERNAME),device->sshParameters().userName); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_DEVICE_PORT),QString::number(device->sshParameters().port)); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_SERIALNUMBER),device->serialNumber()); } } command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_SHAREDIRECTORY),Constants::UBUNTU_SHAREPATH); command = command.replace(QLatin1String(Constants::UBUNTU_ACTION_SCRIPTDIRECTORY),Constants::UBUNTU_SCRIPTPATH); if (bQuery && bQueryOk) { command = QString(command).arg(queryData); } QStringList cmdList; cmdList << command << workingDirectory; if(debug) qDebug()<text()); } else { qWarning() << __PRETTY_FUNCTION__ << Constants::ERROR_MSG_NOACTIONS; } } else { qWarning() << __PRETTY_FUNCTION__ << Constants::ERROR_MSG_COULD_NOT_CAST_TO_ACTION; } } void UbuntuMenu::initialize() { if (m_obj.isObject()) { QJsonObject tmp = m_obj.object(); foreach (QString key, tmp.keys()) { if (tmp.contains(key)) { QJsonObject obj = tmp.value(key).toObject(); Core::ActionContainer *actionContainer = NULL; if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_PARENT))) { QString parentValue = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_PARENT)).toString(); if (parentValue == QLatin1String(Constants::UBUNTU_MENUJSON_PARENT_TOOLS)) actionContainer = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS); else if (parentValue == QLatin1String(Constants::UBUNTU_MENUJSON_PARENT_EDIT)) actionContainer = Core::ActionManager::actionContainer(Core::Constants::M_EDIT); else if (parentValue == QLatin1String(Constants::UBUNTU_MENUJSON_PARENT_HELP)) actionContainer = Core::ActionManager::actionContainer(Core::Constants::M_HELP); else if (parentValue == QLatin1String(Constants::UBUNTU_MENUJSON_PARENT_WINDOW)) actionContainer = Core::ActionManager::actionContainer(Core::Constants::M_WINDOW); else if (parentValue == QLatin1String(Constants::UBUNTU_MENUJSON_PARENT_FILE)) actionContainer = Core::ActionManager::actionContainer(Core::Constants::M_FILE); else if (parentValue == QLatin1String(Constants::UBUNTU_MENUJSON_PARENT_BUILD)) actionContainer = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); else if (parentValue == QLatin1String(Constants::UBUNTU_MENUJSON_PARENT_TOP)) actionContainer = Core::ActionManager::actionContainer(Core::Constants::MENU_BAR); else actionContainer = Core::ActionManager::actionContainer(Core::Id(parentValue.toUtf8().constData())); } else { actionContainer = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS); } QString group; if (obj.contains(QLatin1String(Constants::UBUNTU_MENUJSON_GROUP))) { group = obj.value(QLatin1String(Constants::UBUNTU_MENUJSON_GROUP)).toString(); } Core::Id groupId; if (!group.isEmpty()) { groupId = Core::Id(group.toUtf8().constData()); } parseMenu(obj,actionContainer,groupId); } } } else { qWarning() << Constants::ERROR_MSG_UNABLE_TO_PARSE_MENUJSON; } } ./src/ubuntu/ubuntusettingsdeviceconnectivitywidget.h0000644000015600001650000000233412705421114023504 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUSETTINGSDEVICECONNECTIVITYWIDGET_H #define UBUNTUSETTINGSDEVICECONNECTIVITYWIDGET_H #include namespace Ui { class UbuntuSettingsDeviceConnectivityWidget; } class UbuntuSettingsDeviceConnectivityWidget : public QWidget { Q_OBJECT public: explicit UbuntuSettingsDeviceConnectivityWidget(QWidget *parent = 0); ~UbuntuSettingsDeviceConnectivityWidget(); void apply(); private: Ui::UbuntuSettingsDeviceConnectivityWidget *ui; }; #endif // UBUNTUSETTINGSDEVICECONNECTIVITYWIDGET_H ./src/ubuntu/wizards/0000755000015600001650000000000012705421114014666 5ustar jenkinsjenkins./src/ubuntu/wizards/ubuntufirstrunwizard.cpp0000644000015600001650000002112212705421114021730 0ustar jenkinsjenkins#include "ubuntufirstrunwizard.h" #include "../ubuntuconstants.h" #include "../ubuntukitmanager.h" #include "../ubuntuclickdialog.h" #include "../clicktoolchain.h" #include "../ubuntudevicesmodel.h" #include "../ubuntuprocess.h" #include "../ubuntudevice.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; UbuntuFirstRunWizard::UbuntuFirstRunWizard(QWidget *parent) : Utils::Wizard(parent) { const int introPageId = addPage(new UbuntuIntroductionWizardPage); const int chrootsPageId = addPage(new UbuntuSetupChrootWizardPage); const int emulatorPageId = addPage(new UbuntuSetupEmulatorWizardPage); Utils::WizardProgress *progress = wizardProgress(); progress->item(introPageId)->setTitle(tr("Intro")); progress->item(chrootsPageId)->setTitle(tr("Kits and Toolchains")); progress->item(emulatorPageId)->setTitle(tr("Devices and Emulators")); setMinimumSize(800,600); } UbuntuIntroductionWizardPage::UbuntuIntroductionWizardPage(QWidget *parent) : QWizardPage(parent) { QLabel *label = new QLabel(tr("

Welcome to the Ubuntu-SDK

" "

This Wizard will help to setup a development environment to create Applications for the Ubuntu platform.

" "

At any time later it is possible to create:" "

  • new targets in the "Tools -> Options -> Ubuntu" Settings Page,
  • " "
  • new emulators on the Devices pages by clicking on the "+" button
" "

" "

Have a lot of fun!

")); label->setWordWrap(true); QCheckBox *check = new QCheckBox(tr("Do not show this Wizard the next time.")); check->setChecked(true); registerField(QStringLiteral("disableWizard"),check); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); layout->addStretch(); layout->addWidget(check); setLayout(layout); } void UbuntuIntroductionWizardPage::initializePage() { } bool UbuntuIntroductionWizardPage::isComplete() const { return true; } UbuntuSetupChrootWizardPage::UbuntuSetupChrootWizardPage(QWidget *parent) : QWizardPage(parent) , m_complete(false) { setTitle(tr("Build targets")); QLabel *label = new QLabel(tr("

In order to create Apps for the Ubuntu platform, it is required to create Kits. Kits enable cross-platform and cross-configuration development. Kits consist of a set of values that define one environment, such as a target device, sysroot to build against, toolchain to build with, platform specific API set, and some metadata.

" "

Note: It is recommended to create Kits for each possible target architecture (i386, armhf). When developing with the emulator, the best experience is provided by using a i386 emulator and Kit

")); label->setWordWrap(true); m_kitExistsLabel = new QLabel(tr("These Kits are already available on the machine, but it is also possible to create new ones.")); m_kitExistsLabel->setWordWrap(true); m_kitExistsLabel->setVisible(false); m_kitList = new QTreeWidget; m_kitList->setEditTriggers(QAbstractItemView::NoEditTriggers); m_kitList->setSelectionMode(QAbstractItemView::NoSelection); m_kitList->setItemsExpandable(false); m_kitList->header()->setStretchLastSection(true); m_kitList->header()->setSectionResizeMode(QHeaderView::ResizeToContents); m_kitList->setColumnCount(1); QStringList headers; headers << tr("Kit Name"); m_kitList->setHeaderLabels(headers); m_createKitButton = new QPushButton(tr("Create new Kit")); connect(m_createKitButton,SIGNAL(clicked()),this,SLOT(onCreateKitButtonClicked())); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); layout->addWidget(m_kitExistsLabel); layout->addWidget(m_kitList); layout->addWidget(m_createKitButton); setLayout(layout); } void UbuntuSetupChrootWizardPage::initializePage() { QList allKits = ProjectExplorer::KitManager::kits(); m_kitList->clear(); bool found = false; foreach(ProjectExplorer::Kit *curr, allKits) { ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(curr); const Core::Id devId = ProjectExplorer::DeviceKitInformation::deviceId(curr); //we just care about Kits with a toolchain and a qt version if (!tc || !QtSupport::QtKitInformation::qtVersion(curr)) continue; if (tc->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID) || devId == ProjectExplorer::Constants::DESKTOP_DEVICE_ID) { found = true; QTreeWidgetItem* kitItem = new QTreeWidgetItem; kitItem->setText(0,curr->displayName()); kitItem->setData(0,Qt::UserRole,curr->id().uniqueIdentifier()); m_kitList->addTopLevelItem(kitItem); } } m_kitExistsLabel->setVisible(found); if(m_complete != found) { m_complete = found; emit completeChanged(); } } bool UbuntuSetupChrootWizardPage::isComplete() const { return m_complete; } void UbuntuSetupChrootWizardPage::onCreateKitButtonClicked() { UbuntuClickDialog::createClickChrootModal(true,QString(), QString(),this); initializePage(); } UbuntuSetupEmulatorWizardPage::UbuntuSetupEmulatorWizardPage(QWidget *parent) : QWizardPage(parent) { QLabel *label = new QLabel(tr("

To be able to run applications either a physical device or a Ubuntu Emulator can be used.
" "Please enable the checkbox below if a emulator should be created right away and after pressing the "Finish" button the device page will be opened.

" "

Skip that step if only physical devices are used.

" "

Note: Currently we suggest to use only the i386 emulator, because it provides the best experience

")); label->setWordWrap(true); m_createEmulatorCheckBox = new QCheckBox(tr("Create emulator")); registerField(QStringLiteral("createEmulator"),m_createEmulatorCheckBox); m_devicesList =new QTreeWidget; m_devicesList->setEditTriggers(QAbstractItemView::NoEditTriggers); m_devicesList->setSelectionMode(QAbstractItemView::NoSelection); m_devicesList->setItemsExpandable(false); m_devicesList->header()->setStretchLastSection(true); m_devicesList->header()->setSectionResizeMode(QHeaderView::ResizeToContents); m_devicesList->setColumnCount(1); QStringList headers; headers << tr("Device Name"); m_devicesList->setHeaderLabels(headers); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); layout->addSpacing(10); layout->addWidget(new QLabel(tr("List of available devices"))); layout->addWidget(m_devicesList); layout->addWidget(m_createEmulatorCheckBox); setLayout(layout); connect(ProjectExplorer::DeviceManager::instance(),SIGNAL(deviceAdded(Core::Id)),this,SLOT(updateDevicesList())); connect(ProjectExplorer::DeviceManager::instance(),SIGNAL(deviceRemoved(Core::Id)),this,SLOT(updateDevicesList())); connect(ProjectExplorer::DeviceManager::instance(),SIGNAL(deviceListReplaced()),this,SLOT(updateDevicesList())); } void UbuntuSetupEmulatorWizardPage::updateDevicesList() { m_devicesList->clear(); ProjectExplorer::DeviceManager *devMgr = ProjectExplorer::DeviceManager::instance(); for(int i = 0; i < devMgr->deviceCount(); i++) { ProjectExplorer::IDevice::ConstPtr dev = devMgr->deviceAt(i); if(!dev) continue; if(!dev->type().toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID))) continue; QTreeWidgetItem* devItem = new QTreeWidgetItem; devItem->setText(0,dev->displayName()); m_devicesList->addTopLevelItem(devItem); } } void UbuntuSetupEmulatorWizardPage::initializePage() { updateDevicesList(); } bool UbuntuSetupEmulatorWizardPage::isComplete() const { return true; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/wizards/ubuntufatpackagingwizard.cpp0000644000015600001650000002154112705421114022500 0ustar jenkinsjenkins#include "ubuntufatpackagingwizard.h" #include "../ubuntuclicktool.h" #include "../ubuntuconstants.h" #include "../clicktoolchain.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { UbuntuFatPackagingWizard::UbuntuFatPackagingWizard(QmakeProjectManager::QmakeProject *project, QWidget *parent) : Utils::Wizard(parent), m_targetPage(0) { m_introPage = new UbuntuFatPackagingIntroPage(project); const int introPageId = addPage(m_introPage); m_targetPage = new UbuntuChooseTargetPage(project); const int targetPageId = addPage(m_targetPage); Utils::WizardProgress *progress = wizardProgress(); progress->item(introPageId)->setTitle(tr("Intro")); progress->item(targetPageId)->setTitle(tr("Targets")); setMinimumSize(800,600); } QList UbuntuFatPackagingWizard::selectedTargets() { return m_targetPage->selectedSuspects(); } UbuntuFatPackagingWizard::BuildMode UbuntuFatPackagingWizard::mode() const { return m_introPage->mode(); } UbuntuFatPackagingIntroPage::UbuntuFatPackagingIntroPage(QmakeProjectManager::QmakeProject *project, QWidget *parent) : QWizardPage(parent) , m_project(project) { QLabel *label = new QLabel(tr("

Click packaging

" "

This Wizard will help to build a click package for uploading into the store.

" "

If the project has native compiled parts like executables and libraries, select the
" ""Project with binary executables and libraries" mode, 
" "otherwise "Project without binary executables and libraries (pure qml/html projects)"
" "should be selected. 

"),this); label->setWordWrap(true); QRadioButton *compiledPartsButton = new QRadioButton(tr("Project with binary executables and libraries."), this); QRadioButton *noCompiledPartsButton = new QRadioButton(tr("Project without binary executables and libraries (pure qml/html projects)"), this); m_modeGroup = new QButtonGroup(this); m_modeGroup->addButton(compiledPartsButton, UbuntuFatPackagingWizard::CompiledPartsMode); m_modeGroup->addButton(noCompiledPartsButton, UbuntuFatPackagingWizard::NoCompiledPartsMode); connect(m_modeGroup, SIGNAL(buttonClicked(int)), this, SLOT(buildModeChanged(int))); QString path = QString::fromLatin1("/tmp/click-build-%1").arg(m_project->displayName()); m_clickPackagePath = new Utils::PathChooser(this); m_clickPackagePath->setPath(path); m_clickPackagePath->setExpectedKind(Utils::PathChooser::Directory); registerField(QStringLiteral("targetDirectory"),m_clickPackagePath,"path","pathChanged"); connect(m_clickPackagePath,SIGNAL(pathChanged(QString)),this,SIGNAL(completeChanged())); QFormLayout *layout = new QFormLayout; layout->addRow(label); layout->addRow(tr("Destination directory"),m_clickPackagePath); layout->addRow(compiledPartsButton); layout->addRow(noCompiledPartsButton); setLayout(layout); } UbuntuFatPackagingWizard::BuildMode UbuntuFatPackagingIntroPage::mode() const { return static_cast(m_modeGroup->checkedId()); } void UbuntuFatPackagingIntroPage::initializePage() { bool likelyHasBinaryParts = false; QList nodes = m_project->allProFiles(); foreach(QmakeProjectManager::QmakeProFileNode * node, nodes) { if (node->projectType() == QmakeProjectManager::ApplicationTemplate || node->projectType() == QmakeProjectManager::SharedLibraryTemplate || node->projectType() == QmakeProjectManager::StaticLibraryTemplate) { likelyHasBinaryParts = true; break; } } if (likelyHasBinaryParts) m_modeGroup->button(UbuntuFatPackagingWizard::CompiledPartsMode)->setChecked(true); else m_modeGroup->button(UbuntuFatPackagingWizard::NoCompiledPartsMode)->setChecked(true); } bool UbuntuFatPackagingIntroPage::isComplete() const { return m_clickPackagePath->isValid(); } int UbuntuFatPackagingIntroPage::nextId() const { if (m_modeGroup->checkedId() == UbuntuFatPackagingWizard::NoCompiledPartsMode) return -1; return QWizardPage::nextId(); } void UbuntuFatPackagingIntroPage::buildModeChanged(const int mode) { setFinalPage( mode == UbuntuFatPackagingWizard::NoCompiledPartsMode ); } UbuntuChooseTargetPage::UbuntuChooseTargetPage(QmakeProjectManager::QmakeProject *project, QWidget *parent) : QWizardPage(parent), m_project(project) { m_targetView = new QTreeWidget(this); QSizePolicy sizePolicy = m_targetView->sizePolicy(); sizePolicy.setVerticalPolicy(QSizePolicy::Expanding); sizePolicy.setVerticalStretch(1); m_targetView->setSizePolicy(sizePolicy); m_targetView->setHeaderLabel(tr("Architectures")); connect(m_targetView, &QTreeWidget::itemChanged, this, &UbuntuChooseTargetPage::completeChanged); m_errorLabel = new QLabel(this); m_errorLabel->setVisible(false); m_errorLabel->setStyleSheet(QStringLiteral("background: yellow")); m_noTargetsLabel = new QLabel(tr("

No Targets available

" "

In order to build a click package, Ubuntu Kits need to be " "added to the project. This is possible by adding them in the " "\"Projects\" tab.

"),this); m_noTargetsLabel->setWordWrap(true); m_noTargetsLabel->hide(); QFormLayout *layout = new QFormLayout; layout->addRow(m_targetView); layout->addRow(m_noTargetsLabel); layout->addRow(m_errorLabel); setLayout(layout); } void UbuntuChooseTargetPage::initializePage() { m_suspects.clear(); m_targetView->clear(); foreach(ProjectExplorer::Target *t , m_project->targets()) { ProjectExplorer::Kit* k = t->kit(); if(!k) continue; if(!ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(k).toString().startsWith(QLatin1String(Ubuntu::Constants::UBUNTU_DEVICE_TYPE_ID))) continue; foreach(ProjectExplorer::BuildConfiguration *b, t->buildConfigurations()) { if(b->buildType() == ProjectExplorer::BuildConfiguration::Debug) continue; m_suspects << b; } } if (m_suspects.isEmpty()) { //show some label that tells the user to add Kits m_targetView->hide(); m_noTargetsLabel->show(); } else { m_targetView->show(); m_noTargetsLabel->hide(); foreach(ProjectExplorer::BuildConfiguration *conf, m_suspects) { QTreeWidgetItem* targetItem = new QTreeWidgetItem; targetItem->setText(0,conf->target()->displayName()+QStringLiteral(" ")+conf->displayName()); targetItem->setCheckState(0,Qt::Unchecked); m_targetView->addTopLevelItem(targetItem); } } } bool UbuntuChooseTargetPage::isComplete() const { if (!selectedSuspects().isEmpty()) return isValid(); return false; } bool UbuntuChooseTargetPage::validatePage() { return isValid(); } QList UbuntuChooseTargetPage::selectedSuspects() const { QList selected; for(int i = 0; i < m_suspects.size(); i++) { if(m_targetView->topLevelItem(i)->checkState(0) == Qt::Checked) selected << m_suspects[i]; } return selected; } bool UbuntuChooseTargetPage::isValid() const { m_errorLabel->hide(); QString fw; foreach (ProjectExplorer::BuildConfiguration *conf, selectedSuspects()) { ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(conf->target()->kit()); if (tc && tc->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) { ClickToolChain *cTc = static_cast(tc); if (fw.isEmpty()) fw = cTc->clickTarget().framework; else if (fw != cTc->clickTarget().framework) { m_errorLabel->setText(tr("It is not possible to combine different frameworks in the same click package!")); m_errorLabel->show(); return false; } } } return true; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/wizards/ubuntuprojectapplicationwizard.cpp0000644000015600001650000003212312705421114023751 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuprojectapplicationwizard.h" #include "ubuntufirstrunwizard.h" #include "../ubuntushared.h" #include "../ubuntuconstants.h" #include "../ubuntuproject.h" #include "../ubuntubzr.h" #include "../ubuntuclicktool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu::Internal; enum { debug = 0 }; UbuntuProjectApplicationWizard::UbuntuProjectApplicationWizard(ProjectType type) : m_type(type) { setRequiredFeatures(requiredFeatures()); } Core::BaseFileWizard *UbuntuProjectApplicationWizard::create(QWidget *parent, const Core::WizardDialogParameters &wizardDialogParameters) const { QTC_ASSERT(!parameters().isNull(), return 0); UbuntuProjectApplicationWizardDialog *projectDialog = new UbuntuProjectApplicationWizardDialog(this, parent, m_type , wizardDialogParameters); projectDialog->addChrootSetupPage(12); projectDialog->addTargetSetupPage(13); initProjectWizardDialog(projectDialog, wizardDialogParameters.defaultPath(), projectDialog->extensionPages()); QString maintainer = QStringLiteral("username"); QString whoami = QStringLiteral("Firstname Surname "); UbuntuBzr *bzr = UbuntuBzr::instance(); if(!bzr->isInitialized()) { bzr->initialize(); bzr->waitForFinished(); } if(bzr->isInitialized()) { maintainer = bzr->launchpadId(); whoami = bzr->whoami(); } projectDialog->setField(QStringLiteral("ClickMaintainer"),whoami); projectDialog->setField(QStringLiteral("ClickDomain"),maintainer); QList boxes = projectDialog->findChildren(); foreach(QComboBox* box, boxes){ if(box->currentData().toString() == QStringLiteral("ubuntu-sdk-dummy-framework")) { QStringList allFrameworks = UbuntuClickFrameworkProvider::getSupportedFrameworks(); box->clear(); int running = -1; int defaultIdx = -1; foreach(const QString &fw, allFrameworks) { if(defaultIdx == -1) { running++; if(!fw.contains(QStringLiteral("-dev"))) defaultIdx = running; } box->addItem(fw,fw); } if(defaultIdx >= 0) box->setCurrentIndex(defaultIdx); break; } } return projectDialog; } bool UbuntuProjectApplicationWizard::postGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage) const { const UbuntuProjectApplicationWizardDialog *dialog = qobject_cast(w); // Generate user settings foreach (const Core::GeneratedFile &file, l) if (file.attributes() & Core::GeneratedFile::OpenProjectAttribute) { dialog->writeUserFile(file.path()); break; } // Post-Generate: Open the projects/editors return ProjectExplorer::CustomProjectWizard::postGenerateOpen(l ,errorMessage); } Core::GeneratedFiles UbuntuProjectApplicationWizard::generateFiles(const QWizard *w, QString *errorMessage) const { QString requiredPolicy = UbuntuClickFrameworkProvider::instance()->frameworkPolicy(w->field(QStringLiteral("ClickFrameworkVersion")).toString()); if(requiredPolicy.isEmpty()) requiredPolicy = QStringLiteral("1.3"); //some sane default value context()->baseReplacements.insert(QStringLiteral("ClickAAPolicyVersion"),requiredPolicy); return ProjectExplorer::CustomProjectWizard::generateFiles(w,errorMessage); } Core::FeatureSet UbuntuProjectApplicationWizard::requiredFeatures() const { #ifdef Q_PROCESSOR_ARM return CustomProjectWizard::requiredFeatures(); #else return CustomProjectWizard::requiredFeatures() | Core::Feature(QtSupport::Constants::FEATURE_QMLPROJECT) | Core::Feature::versionedFeature(QtSupport::Constants::FEATURE_QT_QUICK_PREFIX, 2); #endif } UbuntuProjectApplicationWizardDialog::UbuntuProjectApplicationWizardDialog(const Core::BaseFileWizardFactory *factory, QWidget *parent, UbuntuProjectApplicationWizard::ProjectType type, const Core::WizardDialogParameters ¶meters) : ProjectExplorer::BaseProjectWizardDialog(factory, parent, parameters) , m_targetSetupPage(0) , m_type(type) { init(); } UbuntuProjectApplicationWizardDialog::~UbuntuProjectApplicationWizardDialog() { if (m_targetSetupPage && !m_targetSetupPage->parent()) delete m_targetSetupPage; } bool UbuntuProjectApplicationWizardDialog::writeUserFile(const QString &projectFileName) const { if (!m_targetSetupPage) return false; QFileInfo fi = QFileInfo(projectFileName); if (!fi.exists()) return false; QString filePath = fi.canonicalFilePath(); Utils::MimeDatabase mimeDb; const Utils::MimeType mt = mimeDb.mimeTypeForFile(fi); if (mt.isValid()) { QList allProjectManagers = ExtensionSystem::PluginManager::getObjects(); foreach (ProjectExplorer::IProjectManager *manager, allProjectManagers) { if (manager->mimeType() == mt.name()) { QString tmp; if (ProjectExplorer::Project *pro = manager->openProject(filePath, &tmp)) { if(debug) qDebug()<<"Storing project type settings: "<id().toSetting(); bool success = m_targetSetupPage->setupProject(pro); if(success) { pro->saveSettings(); } delete pro; return success; } break; } } } return false; } void UbuntuProjectApplicationWizardDialog::init() { setWindowTitle(tr("New Ubuntu Project")); setIntroDescription(tr("This wizard generates a Ubuntu project based on Ubuntu Components.")); connect(this, SIGNAL(projectParametersChanged(QString,QString)), this, SLOT(generateProfileName(QString,QString))); } void UbuntuProjectApplicationWizardDialog::addChrootSetupPage(int id) { QList allKits = ProjectExplorer::KitManager::kits(); bool found = false; foreach(ProjectExplorer::Kit *curr, allKits) { ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(curr); if (tc->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) { found = true; break; } } if(found) return; if (id >= 0) setPage(id, new UbuntuSetupChrootWizardPage); else id = addPage(new UbuntuSetupChrootWizardPage); } void UbuntuProjectApplicationWizardDialog::addTargetSetupPage(int id) { m_targetSetupPage = new ProjectExplorer::TargetSetupPage; const QString platform = selectedPlatform(); //prefer Qt Desktop or Platform Kit Core::FeatureSet features = Core::FeatureSet(QtSupport::Constants::FEATURE_DESKTOP); if (platform.isEmpty()) m_targetSetupPage->setPreferredKitMatcher(QtSupport::QtKitInformation::qtVersionMatcher(features)); else m_targetSetupPage->setPreferredKitMatcher(QtSupport::QtKitInformation::platformMatcher(platform)); switch (m_type) { case UbuntuProjectApplicationWizard::CMakeProject:{ auto cmakeKitMatcher = [](const ProjectExplorer::Kit *k){ auto tool = CMakeProjectManager::CMakeKitInformation::cmakeTool(k); return tool && tool->isValid(); }; m_targetSetupPage->setRequiredKitMatcher(ProjectExplorer::KitMatcher(cmakeKitMatcher)); break; } case UbuntuProjectApplicationWizard::GoProject: { #if 0 ProjectExplorer::KitMatcher *matcher = 0; const char* retTypeName = QMetaType::typeName(qMetaTypeId()); void **arg = reinterpret_cast(&matcher); ProjectExplorer::IProjectManager *manager = qobject_cast(ExtensionSystem::PluginManager::getObjectByClassName(QStringLiteral("GoLang::Internal::Manager"))); if (manager) { bool success = QMetaObject::invokeMethod(manager, "createKitMatcher", QGenericReturnArgument(retTypeName,arg)); if(!success && debug) qDebug()<<"Invoke failed"; } if (matcher) m_targetSetupPage->setRequiredKitMatcher(matcher); else #endif //this is just a fallback for now to remove all ubuntu kits until cross compiling is sorted out //it should not be hit at all but i keep it there just to be safe m_targetSetupPage->setRequiredKitMatcher(QtSupport::QtKitInformation::platformMatcher(QLatin1String(QtSupport::Constants::DESKTOP_PLATFORM))); break; } case UbuntuProjectApplicationWizard::QMakeProject: case UbuntuProjectApplicationWizard::UbuntuQMLProject: case UbuntuProjectApplicationWizard::UbuntuHTMLProject: { m_targetSetupPage->setRequiredKitMatcher(UbuntuKitMatcher()); break; } default: break; } resize(900, 450); if (id >= 0) setPage(id, m_targetSetupPage); else id = addPage(m_targetSetupPage); wizardProgress()->item(id)->setTitle(tr("Kits")); } QList UbuntuProjectApplicationWizardDialog::selectedKits() const { if(m_targetSetupPage) return m_targetSetupPage->selectedKits(); return QList(); } void UbuntuProjectApplicationWizardDialog::generateProfileName(const QString &projectName, const QString &path) { if(!m_targetSetupPage) return; switch(m_type) { case UbuntuProjectApplicationWizard::QMakeProject: { m_targetSetupPage->setProjectPath(path+QDir::separator() +projectName +QDir::separator() +QString::fromLatin1("%1.pro").arg(projectName)); break; } case UbuntuProjectApplicationWizard::GoProject: { m_targetSetupPage->setProjectPath(path+QDir::separator() +projectName +QDir::separator() +QString::fromLatin1("%1.goproject").arg(projectName)); break; } case UbuntuProjectApplicationWizard::UbuntuHTMLProject: { m_targetSetupPage->setProjectPath(path+QDir::separator() +projectName +QDir::separator() +QString::fromLatin1("%1.ubuntuhtmlproject").arg(projectName)); break; } case UbuntuProjectApplicationWizard::UbuntuQMLProject: { m_targetSetupPage->setProjectPath(path+QDir::separator() +projectName +QDir::separator() +QString::fromLatin1("%1.qmlproject").arg(projectName)); break; } default: { m_targetSetupPage->setProjectPath(path+QDir::separator()+projectName+QDir::separator()+QLatin1String("CMakeLists.txt")); } } } ./src/ubuntu/wizards/ubuntuprojectmigrationwizard.cpp0000644000015600001650000006060212705421114023442 0ustar jenkinsjenkins#include "ubuntuprojectmigrationwizard.h" #include "../ubuntuclicktool.h" #include "../ubuntubzr.h" #include "../ubuntuscopefinalizer.h" #include "../ubuntuconstants.h" #include "../ubuntushared.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { static bool createFileFromTemplate (const QString &templateFile, const QString &targetFile, const QMap replacements) { QString templateText; QFile inFile(templateFile); if(Q_UNLIKELY(!inFile.open(QIODevice::ReadOnly))) { printToOutputPane(QCoreApplication::translate("UbuntuProjectMigrationWizard","Could not open template file %1. The project will miss this file.") .arg(templateFile)); return false; } QFile outFile(targetFile); QFileInfo outFileInfo(targetFile); QDir d; if(Q_UNLIKELY(!d.mkpath(outFileInfo.absolutePath()))) { printToOutputPane(QCoreApplication::translate("UbuntuProjectMigrationWizard","Could not create the path %1. The project will miss files.") .arg(outFileInfo.absolutePath())); return false; } if(!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { printToOutputPane(QCoreApplication::translate("UbuntuProjectMigrationWizard","Could not create %1. The project will miss files.") .arg(targetFile)); return false; } QTextStream in(&inFile); templateText = in.readAll(); for(auto i = replacements.constBegin(); i != replacements.constEnd(); i++) { templateText.replace(QString::fromLatin1("%%1%").arg(i.key()),i.value()); templateText.replace(QString::fromLatin1("%%1:l%").arg(i.key()),i.value().toLower()); } QTextStream out(&outFile); out<item(introPageId)->setTitle(tr("Intro")); progress->item(selectTargetPageId)->setTitle(tr("Targets")); progress->item(projectsInfoPageId)->setTitle(tr("Details")); setMinimumSize(800,600); } QmakeProjectManager::QmakeProject *UbuntuProjectMigrationWizard::project() const { return m_project; } QStringList UbuntuProjectMigrationWizard::relevantTargets() const { QStringList result; QList nodes = m_project->allProFiles(); foreach(const QmakeProjectManager::QmakeProFileNode * node, nodes) { if(node->projectType() == QmakeProjectManager::ApplicationTemplate) result += node->targetInformation().target; } return result; } QStringList UbuntuProjectMigrationWizard::selectedTargets() const { QStringList result; foreach(const QString &targetName, relevantTargets()) { if(field(QStringLiteral("addTarget_")+targetName).toBool()) result += targetName; } return result; } QString UbuntuProjectMigrationWizard::maintainer() const { return field(QStringLiteral("maintainer")).toString(); } QString UbuntuProjectMigrationWizard::domain() const { return field(QStringLiteral("domain")).toString(); } QString UbuntuProjectMigrationWizard::framework() const { return field(QStringLiteral("framework")).toString(); } void UbuntuProjectMigrationWizard::doMigrateProject(QmakeProjectManager::QmakeProject *project, QWidget *parent) { UbuntuProjectMigrationWizard wiz(project,parent); if(wiz.exec() == QDialog::Accepted) { bool multiTargetProject = project->rootQmakeProjectNode()->projectType() == QmakeProjectManager::SubDirsTemplate; QMap base_replacements; base_replacements.insert(QStringLiteral("ProjectName"),project->displayName()); base_replacements.insert(QStringLiteral("ClickDomain"),wiz.domain()); base_replacements.insert(QStringLiteral("ClickMaintainer"),wiz.maintainer()); QString framework = wiz.framework(); QString aaPolicy = UbuntuClickFrameworkProvider::instance()->frameworkPolicy(framework); base_replacements.insert(QStringLiteral("ClickFrameworkVersion"),framework); base_replacements.insert(QStringLiteral("ClickAAPolicyVersion"),aaPolicy.isEmpty() ? QStringLiteral("1.3") : aaPolicy); QStringList hookTargets = wiz.selectedTargets(); //contains all old paths mapped to the path they should have inthe click package QMap mapPaths; QList nodes = project->allProFiles(); foreach(QmakeProjectManager::QmakeProFileNode * node, nodes) { if(node->projectType() == QmakeProjectManager::SubDirsTemplate) continue; QtSupport::ProFileReader *reader = project->createProFileReader(node); OnScopeExit { project->destroyProFileReader(reader); }; bool canRead = true; //setup the file reader correctly if (ProFile *pro = reader->parsedProFile(node->path().toString())) { if(!reader->accept(pro, QMakeEvaluator::LoadAll)) { canRead = false; } pro->deref(); } else { canRead = false; } if(!canRead) { printToOutputPane(tr("Can not parse %1, skipping migration.").arg(node->path().toString())); continue; } QMap variablesToSetAtEnd; auto setVarAtEnd = [&] (const QString &name, const QStringList &list) { if(variablesToSetAtEnd.contains(name)) variablesToSetAtEnd[name].append(list); else variablesToSetAtEnd.insert(name,list); }; QString targetInstallPath; const auto projectType = node->projectType(); //inspect installs QStringList installs = reader->values(QStringLiteral("INSTALLS")); bool hasInstallTarget = installs.contains(QStringLiteral("target")); bool isLikelyQmlPlugin = false; //we don't want to iterate over the install target again installs.removeAll(QStringLiteral("target")); if(hasInstallTarget) { // try to map to the click way const QString path = reader->value(QStringLiteral("target.path")); if(projectType == QmakeProjectManager::SharedLibraryTemplate) { //this is probably a qml plugin, need to analyze more foreach(const QString &install, installs) { if(install == QStringLiteral("target")) continue; bool found = false; QStringList files = reader->values(install+QStringLiteral(".files")); foreach(const QString &file,files) { QFileInfo info(file); if(info.completeBaseName() == QStringLiteral("qmldir")) { //-> we have a qml plugin //use the last directory of the path isLikelyQmlPlugin = true; QString inst_path = reader->value(install+QStringLiteral(".path")); QString suffix = inst_path.split(QStringLiteral("/")).last(); targetInstallPath = QStringLiteral("/lib/$$basename(qt_install_libs)/qt5/qml/")+suffix; //make sure this install targets contents go there as well mapPaths.insert(QDir::cleanPath(inst_path),QDir::cleanPath(targetInstallPath)); found = true; break; } } if(found) break; } } //make sure all files targeted to that path go into the same direction mapPaths.insert(QDir::cleanPath(path),targetInstallPath); } if(!hasInstallTarget || !isLikelyQmlPlugin) { if(projectType == QmakeProjectManager::SharedLibraryTemplate) { targetInstallPath = QStringLiteral("/lib/$$basename(qt_install_libs)"); } else if(projectType == QmakeProjectManager::ApplicationTemplate) { targetInstallPath = QStringLiteral("/lib/$$basename(qt_install_libs)/bin"); } } //BEGIN WRITE node->setProVariable(QStringLiteral("CONFIG"), QStringList()<setProVariable(QStringLiteral("qt_install_libs"), QStringList()<setProVariable(QStringLiteral("target.path"), QStringList()<value(install+QStringLiteral(".path"))); QString mappedPath = path; if(mapPaths.contains(path)) { mappedPath = mapPaths[path]; } else { //check if this installs into a subdirectory of a already changed path bool found = false; foreach(const QString &key,mapPaths.keys()) { if(path.startsWith(key)) { QString suffix = path; suffix.remove(key); mappedPath = mapPaths[key] + QStringLiteral("/") + suffix; mapPaths.insert(path,mappedPath); found = true; break; } } if(!found) { mappedPath.remove(QStringLiteral("/usr")); } } node->setProVariable(install+QStringLiteral(".path"), QStringList()<path().toString()); QmakeProjectManager::TargetInformation targetInfo = node->targetInformation(); if(hookTargets.contains(targetInfo.target)) { QMap replacements = base_replacements; replacements.insert(QStringLiteral("ClickHookName"),targetInfo.target); if(!multiTargetProject) { createFileFromTemplate( QString::fromLatin1("%1/templates/wizards/ubuntu/bin_app-qmake/manifest/manifest.json.in").arg(Constants::UBUNTU_RESOURCE_PATH), QString::fromLatin1("%1/manifest.json.in").arg(proFilePath.absolutePath()), replacements ); node->setProVariable(QStringLiteral("UBUNTU_MANIFEST_FILE"),QStringList()<setProVariable(QStringLiteral("CLICK_ARCH"),QStringList()<setProVariable(QStringLiteral("manifest.target"),QStringList()<setProVariable(QStringLiteral("manifest.commands"),QStringList()< $$manifest.target"),QStringLiteral("ubuntu-click")); node->setProVariable(QStringLiteral("manifest.path"),QStringList()<setProVariable(QStringLiteral("manifest_install.path"), QStringList() << QStringLiteral("/"), QStringLiteral("ubuntu-click")); node->setProVariable(QStringLiteral("manifest_install.files"), QStringList()<addFiles(QStringList()<addFiles(QStringList()<< aaFileName << deskFileName << iconName); node->setProVariable(QStringLiteral("appDeps.path"), QStringList() << QString::fromLatin1("/%1").arg(targetInfo.target), QStringLiteral("ubuntu-click"), QmakeProjectManager::Internal::ProWriter::AppendValues); node->setProVariable(QStringLiteral("appDeps.files"), QStringList()<setProVariable(i.key(), i.value(), QStringLiteral("ubuntu-click"), QmakeProjectManager::Internal::ProWriter::AppendValues | QmakeProjectManager::Internal::ProWriter::AppendOperator | QmakeProjectManager::Internal::ProWriter::MultiLine); } } if(multiTargetProject) { QString manifestFilePath = QString::fromLatin1("%1/%2").arg(project->projectDirectory().toString()).arg(QStringLiteral("/manifest.json.in")); //add manifest pro file createFileFromTemplate( QString::fromLatin1("%1/templates/wizards/ubuntu/bin_app-qmake/manifest/manifest.pro").arg(Constants::UBUNTU_RESOURCE_PATH), QString::fromLatin1("%1/%2").arg(project->projectDirectory().toString()).arg(QStringLiteral("/manifest.pro")), base_replacements ); createFileFromTemplate( QString::fromLatin1("%1/templates/wizards/ubuntu/bin_app-qmake/manifest/manifest.json.in").arg(Constants::UBUNTU_RESOURCE_PATH), manifestFilePath, base_replacements ); //replacements.insert(QStringLiteral("ClickHookName"),targetInfo.target); //now we need to add all the selected hooks QFile manifestFile(manifestFilePath); if(Q_UNLIKELY(!manifestFile.open(QIODevice::ReadOnly))) { printToOutputPane(tr("Can not open manifest file for reading. Hooks need to be added manually.")); return; } QJsonDocument doc = QJsonDocument::fromJson(manifestFile.readAll()); QJsonObject rootObj = doc.object(); manifestFile.close(); QJsonObject hooksObj; foreach (const QString &hookName, hookTargets) { QJsonObject hook; hook.insert(QStringLiteral("apparmor"),QString::fromLatin1("%1/%2.apparmor").arg(hookName).arg(hookName)); hook.insert(QStringLiteral("desktop"),QString::fromLatin1("%1/%2.desktop").arg(hookName).arg(hookName)); hooksObj.insert(hookName,hook); } rootObj[QStringLiteral("hooks")] = hooksObj; doc.setObject(rootObj); if(!manifestFile.open(QIODevice::WriteOnly | QIODevice::Truncate)){ printToOutputPane(tr("Can not open manifest file for writing. Hooks need to be added manually.")); return; } manifestFile.write(doc.toJson()); manifestFile.close(); project->rootQmakeProjectNode()->addSubProjects(QStringList()<projectDirectory().toString()).arg(QStringLiteral("/manifest.pro"))); } } } UbuntuProjectMigrationIntroPage::UbuntuProjectMigrationIntroPage(QWidget *parent) : QWizardPage(parent) { QLabel *label = new QLabel(tr("

Ubuntu Project migration wizard

" "

This wizard rewrites a qmake based project to generate a click package compatible install target.
" "Where possible, changes will be put into the ubuntu-click scope." "

" "

" "Please make sure your project adds all required files for your project to the INSTALL target." "

" "

" "

" "Note: This wizard may produce a faulty configuration in case of complex projects.
" "Warning: Please make sure to backup your project before running this wizard!" "

" )); label->setWordWrap(true); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); setLayout(layout); } UbuntuSelectSubProjectsPage::UbuntuSelectSubProjectsPage(QWidget *parent) : QWizardPage(parent) { QLabel *label = new QLabel(tr("

Please select the applications you want to add to the manifest file

")); label->setWordWrap(true); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); setLayout(layout); } void UbuntuSelectSubProjectsPage::initializePage() { QWizardPage::initializePage(); UbuntuProjectMigrationWizard *wiz = qobject_cast(wizard()); if(wiz) { foreach(const QString &targetName, wiz->relevantTargets()) { QCheckBox *check = new QCheckBox(targetName); check->setChecked(true); layout()->addWidget(check); connect(check,SIGNAL(stateChanged(int)),this,SIGNAL(completeChanged())); registerField(QStringLiteral("addTarget_")+targetName,check); } } } bool UbuntuSelectSubProjectsPage::isComplete() const { //check if at least one item is selected UbuntuProjectMigrationWizard *wiz = qobject_cast(wizard()); if(wiz) { foreach(const QString &targetName, wiz->relevantTargets()) { if(field(QStringLiteral("addTarget_")+targetName).toBool()) return true; } } return false; } UbuntuProjectDetailsPage::UbuntuProjectDetailsPage(QWidget *parent) : QWizardPage(parent), m_initialized(false) { QFormLayout *layout = new QFormLayout; QLineEdit *domainLineEdit = new QLineEdit; layout->addRow(tr("Domain"),domainLineEdit); registerField(QStringLiteral("domain*"),domainLineEdit); QLineEdit *maintainerLineEdit = new QLineEdit; layout->addRow(tr("Maintainer"),maintainerLineEdit); registerField(QStringLiteral("maintainer*"),maintainerLineEdit); QComboBox *box = new QComboBox; QStringList allFrameworks = UbuntuClickFrameworkProvider::getSupportedFrameworks(); int running = -1; int defaultIdx = -1; foreach(const QString &fw, allFrameworks) { if(defaultIdx == -1) { running++; if(!fw.contains(QStringLiteral("-dev"))) defaultIdx = running; } box->addItem(fw,fw); } if(defaultIdx >= 0) box->setCurrentIndex(defaultIdx); layout->addRow(tr("Framework"),box); registerField(QStringLiteral("framework"),box,"currentText",SIGNAL(currentTextChanged(QString))); connect(box,SIGNAL(currentTextChanged(QString)),this,SIGNAL(completeChanged())); setLayout(layout); } void UbuntuProjectDetailsPage::initializePage() { QWizardPage::initializePage(); if(!m_initialized) { m_initialized = true; QString maintainer = QStringLiteral("username"); QString whoami = QStringLiteral("maintainerName"); UbuntuBzr *bzr = UbuntuBzr::instance(); if(!bzr->isInitialized()) { bzr->initialize(); bzr->waitForFinished(); } if(bzr->isInitialized()) { maintainer = bzr->launchpadId(); whoami = bzr->whoami(); } wizard()->setField(QStringLiteral("domain"),maintainer); wizard()->setField(QStringLiteral("maintainer"),whoami); } } bool UbuntuProjectDetailsPage::isComplete() const { return QWizardPage::isComplete(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/wizards/ubuntuprojectmigrationwizard.h0000644000015600001650000000321312705421114023102 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUPROJECTMIGRATIONWIZARD_H #define UBUNTU_INTERNAL_UBUNTUPROJECTMIGRATIONWIZARD_H #include #include namespace QmakeProjectManager{ class QmakeProject; } namespace Ubuntu { namespace Internal { class UbuntuProjectMigrationWizard : public Utils::Wizard { Q_OBJECT public: QmakeProjectManager::QmakeProject *project() const; QStringList relevantTargets () const; QStringList selectedTargets () const; QString maintainer () const; QString domain () const; QString framework () const; static void doMigrateProject (QmakeProjectManager::QmakeProject *project, QWidget *parent = 0); signals: public slots: private: explicit UbuntuProjectMigrationWizard(QmakeProjectManager::QmakeProject *project, QWidget *parent = 0); QmakeProjectManager::QmakeProject *m_project; }; class UbuntuProjectMigrationIntroPage : public QWizardPage { Q_OBJECT public: explicit UbuntuProjectMigrationIntroPage(QWidget *parent = 0); }; class UbuntuSelectSubProjectsPage : public QWizardPage { Q_OBJECT public: explicit UbuntuSelectSubProjectsPage(QWidget *parent = 0); // QWizardPage interface public: virtual void initializePage(); virtual bool isComplete() const; }; class UbuntuProjectDetailsPage : public QWizardPage { Q_OBJECT public: explicit UbuntuProjectDetailsPage(QWidget *parent = 0); // QWizardPage interface public: virtual void initializePage(); virtual bool isComplete() const; private: bool m_initialized; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUPROJECTMIGRATIONWIZARD_H ./src/ubuntu/wizards/ubuntufatpackagingwizard.h0000644000015600001650000000445412705421114022151 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUFATPACKAGINGWIZARD_H #define UBUNTU_INTERNAL_UBUNTUFATPACKAGINGWIZARD_H #include #include class QLabel; class QButtonGroup; class QTreeWidget; class QTreeWidgetItem; namespace ProjectExplorer{ class Project; class BuildConfiguration; } namespace QmakeProjectManager { class QmakeProject; } namespace Utils { class PathChooser; } namespace Ubuntu { namespace Internal { class UbuntuFatPackagingIntroPage; class UbuntuChooseTargetPage; class UbuntuFatPackagingWizard : public Utils::Wizard { Q_OBJECT public: enum BuildMode{ NoCompiledPartsMode = 0, CompiledPartsMode }; explicit UbuntuFatPackagingWizard(QmakeProjectManager::QmakeProject *project, QWidget *parent = 0); QList selectedTargets(); BuildMode mode () const; signals: public slots: private: UbuntuFatPackagingIntroPage *m_introPage; UbuntuChooseTargetPage *m_targetPage; }; class UbuntuFatPackagingIntroPage : public QWizardPage { Q_OBJECT public: UbuntuFatPackagingIntroPage (QmakeProjectManager::QmakeProject *project, QWidget *parent = 0); UbuntuFatPackagingWizard::BuildMode mode () const; // QWizardPage interface virtual void initializePage() override; virtual bool isComplete() const override; virtual int nextId() const override; protected slots: void buildModeChanged ( const int mode ); private: QmakeProjectManager::QmakeProject *m_project; QButtonGroup *m_modeGroup; Utils::PathChooser *m_clickPackagePath; }; class UbuntuChooseTargetPage : public QWizardPage { Q_OBJECT public: UbuntuChooseTargetPage (QmakeProjectManager::QmakeProject *project, QWidget *parent = 0); // QWizardPage interface virtual void initializePage() override; virtual bool isComplete() const override; virtual bool validatePage() override; QList selectedSuspects() const; private: bool isValid () const; private: QmakeProjectManager::QmakeProject *m_project; mutable QLabel *m_errorLabel; QLabel *m_noTargetsLabel; QTreeWidget *m_targetView; QList m_suspects; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUFATPACKAGINGWIZARD_H ./src/ubuntu/wizards/ubuntufirstrunwizard.h0000644000015600001650000000311212705421114021374 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUFIRSTRUNWIZARD_H #define UBUNTU_INTERNAL_UBUNTUFIRSTRUNWIZARD_H #include #include class QLabel; class QPushButton; class QTreeWidget; class QCheckBox; namespace ProjectExplorer { class Kit; } namespace Ubuntu { namespace Internal { class UbuntuProcess; class UbuntuFirstRunWizard : public Utils::Wizard { Q_OBJECT public: explicit UbuntuFirstRunWizard(QWidget *parent = 0); signals: public slots: }; class UbuntuIntroductionWizardPage : public QWizardPage { Q_OBJECT public: UbuntuIntroductionWizardPage (QWidget *parent = 0); // QWizardPage interface virtual void initializePage(); virtual bool isComplete() const; }; class UbuntuSetupChrootWizardPage : public QWizardPage { Q_OBJECT public: UbuntuSetupChrootWizardPage (QWidget *parent = 0); // QWizardPage interface virtual void initializePage(); virtual bool isComplete() const; protected slots: void onCreateKitButtonClicked (); private: QLabel *m_kitExistsLabel; QTreeWidget *m_kitList; QPushButton *m_createKitButton; bool m_complete; }; class UbuntuSetupEmulatorWizardPage : public QWizardPage { Q_OBJECT public: UbuntuSetupEmulatorWizardPage (QWidget *parent = 0); // QWizardPage interface virtual void initializePage(); virtual bool isComplete() const; protected slots: void updateDevicesList (); private: QCheckBox *m_createEmulatorCheckBox; QTreeWidget *m_devicesList; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUFIRSTRUNWIZARD_H ./src/ubuntu/wizards/ubuntuprojectapplicationwizard.h0000644000015600001650000000655712705421114023432 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPROJECTAPPLICATIONWIZARD_H #define UBUNTUPROJECTAPPLICATIONWIZARD_H #include #include #include QT_BEGIN_NAMESPACE class QLabel; QT_END_NAMESPACE namespace Utils { class PathChooser; } namespace Ubuntu { namespace Internal { class UbuntuProjectApplicationWizard : public ProjectExplorer::CustomProjectWizard { Q_OBJECT public: enum ProjectType{ UbuntuHTMLProject, UbuntuQMLProject, CMakeProject, GoProject, QMakeProject }; UbuntuProjectApplicationWizard(ProjectType type); protected: // BaseFileWizard interface virtual Core::GeneratedFiles generateFiles(const QWizard *w, QString *errorMessage) const override; Core::BaseFileWizard *create(QWidget *parent, const Core::WizardDialogParameters &wizardDialogParameters) const override; bool postGenerateFiles(const QWizard *, const Core::GeneratedFiles &l, QString *errorMessage) const override; private: Core::FeatureSet requiredFeatures() const; ProjectType m_type; }; class UbuntuProjectApplicationWizardDialog : public ProjectExplorer::BaseProjectWizardDialog { Q_OBJECT public: explicit UbuntuProjectApplicationWizardDialog(const Core::BaseFileWizardFactory *factory, QWidget *parent, UbuntuProjectApplicationWizard::ProjectType type, const Core::WizardDialogParameters ¶meters); virtual ~UbuntuProjectApplicationWizardDialog(); void addChrootSetupPage(int id = -1); void addTargetSetupPage(int id = -1); QList selectedKits() const; bool writeUserFile(const QString &projectFileName) const; private slots: void generateProfileName(const QString &projectName, const QString &path); private: ProjectExplorer::TargetSetupPage *m_targetSetupPage; void init(); UbuntuProjectApplicationWizard::ProjectType m_type; }; template class UbuntuWizardFactory : public ProjectExplorer::ICustomWizardMetaFactory { public: UbuntuWizardFactory(const QString &klass, Core::IWizardFactory::WizardKind kind) : ICustomWizardMetaFactory(klass, kind) { } UbuntuWizardFactory(Core::IWizardFactory::WizardKind kind) : ICustomWizardMetaFactory(QString(), kind) { } ProjectExplorer::CustomWizard *create() const override { return new Wizard(type); } }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTUPROJECTAPPLICATIONWIZARD_H ./src/ubuntu/ubunturemoteruncontrolfactory.cpp0000644000015600001650000001313712705421114022170 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubunturemoteruncontrolfactory.h" #include "ubunturemoterunconfiguration.h" #include "ubunturemotedebugsupport.h" #include "ubunturemoteruncontrol.h" #include "ubunturemoteanalyzesupport.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu::Internal; enum { debug = 0 }; bool UbuntuRemoteRunControlFactory::canRun(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode) const { if(qobject_cast(runConfiguration)) { if (mode != ProjectExplorer::Constants::NORMAL_RUN_MODE && mode != ProjectExplorer::Constants::DEBUG_RUN_MODE && mode != ProjectExplorer::Constants::DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN && mode != ProjectExplorer::Constants::QML_PROFILER_RUN_MODE) { return false; } return runConfiguration->isEnabled(); } return false; } ProjectExplorer::RunControl *UbuntuRemoteRunControlFactory::create(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode, QString *errorMessage) { if (qobject_cast(runConfiguration)) { /* * Taken from remotelinuxruncontrolfactory.cpp and adapted * to work here. */ QTC_ASSERT(canRun(runConfiguration, mode), return 0); UbuntuRemoteRunConfiguration *rc = static_cast(runConfiguration); if (!rc->aboutToStart(errorMessage)) return 0; QTC_ASSERT(rc, return 0); if (mode == ProjectExplorer::Constants::NORMAL_RUN_MODE) { return new UbuntuRemoteRunControl(rc); } else if ( mode == ProjectExplorer::Constants::DEBUG_RUN_MODE || mode == ProjectExplorer::Constants::DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN ) { ProjectExplorer::IDevice::ConstPtr dev = ProjectExplorer::DeviceKitInformation::device(rc->target()->kit()); if (!dev) { *errorMessage = tr("Cannot debug: Kit has no device."); return 0; } if(debug) qDebug()<<"Free ports on the device: "<freePorts().count(); if (rc->localExecutableFilePath().isEmpty()) { if (errorMessage) *errorMessage = tr("The current project has no support for running in a debugger."); return 0; } if (2 > dev->freePorts().count()) { *errorMessage = tr("Cannot debug: Not enough free ports available."); return 0; } Debugger::DebuggerStartParameters params = RemoteLinux::LinuxDeviceDebugSupport::startParameters(rc); params.solibSearchPath.append(rc->soLibSearchPaths()); //Always leave this empty or the debugger backend tries to execute //the binary on the phone instead of attaching and continuing the already //running app params.remoteExecutable = QString(); //params.expectedSignals.append("SIGTRAP"); if(debug) qDebug()<<"Solib search path : "< #include #include #include #include namespace Ubuntu { namespace Internal { const QRegularExpression DEBUG_POLICY_REGEX(QStringLiteral("security_policy_groups_safe_\\S+\\s+\\((\\S+)\\)")); const QRegularExpression DEBUG_POLICY_REGEX_NEW(QStringLiteral("security:policy_groups_safe:[^:]+:(.*)")); const QRegularExpression DEBUG_INAPPROPRIATE_GROUP_TEXT_REGEX(QStringLiteral("found inappropriate policy groups:\\s+(.*)")); const QRegularExpression DEBUG_UNUSUAL_GROUP_TEXT_REGEX(QStringLiteral("found unusual policy groups:\\s+(.*)")); const QRegularExpression ARCHITECTURE_ERROR_REGEX(QStringLiteral("lint_(control|manifest)_architecture_valid")); const QString DEBUG_POLICY_NAME(QStringLiteral("debug")); UbuntuPackageOutputParser::UbuntuPackageOutputParser() : ProjectExplorer::IOutputParser(), m_fatalError(false), m_endOfData(false), m_treatAllErrorsAsWarnings(false) { m_subParser.beginRecieveData(); connect(&m_subParser,&ClickRunChecksParser::parsedNewTopLevelItem,this,&UbuntuPackageOutputParser::onParsedNewTopLevelItem); } void UbuntuPackageOutputParser::stdOutput(const QString &line) { IOutputParser::stdOutput(line); m_subParser.addRecievedData(line); } void UbuntuPackageOutputParser::stdError(const QString &line) { IOutputParser::stdError(line); m_subParser.addRecievedData(line); } bool UbuntuPackageOutputParser::hasFatalErrors() const { //commented out due to bug lp:1377094 "Click review errors prevent applications from being deployed to the device" //return IOutputParser::hasFatalErrors() || m_fatalError; return IOutputParser::hasFatalErrors(); } void UbuntuPackageOutputParser::setTreatAllErrorsAsWarnings(const bool set) { m_treatAllErrorsAsWarnings = set; } /*! * \brief UbuntuPackageOutputParser::setEndOfData * Tells the parser that the next flush() call can read till end of buffer, * because the click-review process has finished */ void UbuntuPackageOutputParser::setEndOfData() { m_endOfData = true; } void UbuntuPackageOutputParser::onParsedNewTopLevelItem(ClickRunChecksParser::DataItem *item) { emitTasks(item); //do not leak the items delete item; } void UbuntuPackageOutputParser::doFlush() { if(m_endOfData) m_subParser.endRecieveData(); else m_subParser.addRecievedData(QString()); } void UbuntuPackageOutputParser::emitTasks(const ClickRunChecksParser::DataItem *item, int level) { for(int i = 0; i < item->children.size(); i++) { emitTasks(item->children[i], level+1); } if(level == 0) return; if(item->icon != ClickRunChecksParser::Error && item->icon != ClickRunChecksParser::Warning) return; bool error = isError(item); if(error) m_fatalError = true; ProjectExplorer::Task task(error && !m_treatAllErrorsAsWarnings ? ProjectExplorer::Task::Error : ProjectExplorer::Task::Warning, QStringLiteral(""), //empty description for now Utils::FileName(), //we have no file to show -1, //line number ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); QString desc = ((QString)QStringLiteral("%1: %2")).arg(item->type).arg(item->text); if(item->link.isValid()) desc.append(QStringLiteral("\n")).append(item->link.toString()); QRegularExpressionMatch match = DEBUG_POLICY_REGEX.match(item->type); QRegularExpressionMatch match2 = DEBUG_POLICY_REGEX_NEW.match(item->type); if(match.captured(1) == DEBUG_POLICY_NAME || match2.captured(1) == DEBUG_POLICY_NAME) desc.append(QStringLiteral("\n")).append(tr("The debug policy group is automatically injected and should only be used for development.\nPlease use the publish mode to create a package for the store!")); task.description = desc; addTask(task); } bool UbuntuPackageOutputParser::isError(const ClickRunChecksParser::DataItem *item) { bool isError = (item->icon == ClickRunChecksParser::Error); if(isError) { //add other error item types here if we just want them treated as warnings QRegularExpressionMatch match = DEBUG_POLICY_REGEX.match(item->type); if(match.hasMatch()) { if(match.captured(1) == DEBUG_POLICY_NAME) return false; } match = DEBUG_POLICY_REGEX_NEW.match(item->type); if(match.hasMatch()) { if(match.captured(1) == DEBUG_POLICY_NAME) return false; } match = DEBUG_INAPPROPRIATE_GROUP_TEXT_REGEX.match(item->text); if(match.hasMatch()) { if(match.captured(1).contains(DEBUG_POLICY_NAME)) return false; } match = DEBUG_UNUSUAL_GROUP_TEXT_REGEX.match(item->text); if(match.hasMatch()) { if(match.captured(1).contains(DEBUG_POLICY_NAME)) return false; } match = ARCHITECTURE_ERROR_REGEX.match(item->type); if(match.hasMatch()) { if(item->text.contains(QStringLiteral("i386"))) return false; } } return isError; } bool UbuntuClickReviewTaskHandler::canHandle(const ProjectExplorer::Task &task) const { return getUrl(task).isValid(); } void UbuntuClickReviewTaskHandler::handle(const ProjectExplorer::Task &task) { QUrl url = getUrl(task); if(url.isValid()) QDesktopServices::openUrl(url); } QAction *UbuntuClickReviewTaskHandler::createAction(QObject *parent) const { QAction *showAction = new QAction(tr("Open URL"), parent); showAction->setToolTip(tr("Open the URL.")); //showAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); return showAction; } QUrl UbuntuClickReviewTaskHandler::getUrl(const ProjectExplorer::Task &task) const { QStringList lines = task.description.split(QStringLiteral("\n")); if(lines.size() < 2) return QUrl(); QUrl url = QUrl::fromUserInput(lines[1]); if(url.isValid() && (url.scheme() == QStringLiteral("http") || url.scheme() == QStringLiteral("https"))) return url; return QUrl(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuabstractguieditorwidget.cpp0000644000015600001650000001354312705421114022103 0ustar jenkinsjenkins/* * Copyright 2014 Digia Plc and/or its subsidiary(-ies). * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuabstractguieditorwidget.h" #include "ubuntumanifesteditorwidget.h" #include "ubuntuconstants.h" #include "ubuntuabstractguieditordocument.h" #include "ubuntumanifesteditor.h" #include "ubuntuclicktool.h" #include "clicktoolchain.h" #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { const char infoBarId[] = "UbuntuProjectManager.UbuntuManifestEditor.InfoBar"; //try to find the project by the file path ProjectExplorer::Project *ubuntuProject(const QString &file) { //Project *SessionManager::projectForFile(const Utils::FileName &fileName) Utils::FileName fileName = Utils::FileName::fromString(file); foreach (ProjectExplorer::Project *project, ProjectExplorer::SessionManager::projects()) { if (!project->activeTarget()) continue; #if 0 ProjectExplorer::Kit *kit = project->activeTarget()->kit(); ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(kit); if (tc && tc->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID) && fileName.isChildOf(Utils::FileName::fromString(project->projectDirectory()))) #endif if(fileName.isChildOf(project->projectDirectory())) return project; } return 0; } UbuntuAbstractGuiEditorWidget::UbuntuAbstractGuiEditorWidget(const QString &mimeType) : QScrollArea(), m_widgetStack(0), m_sourceEditor(0), m_dirty(false) { //this contains the source and GUI based editor m_widgetStack = new QStackedWidget; m_sourceEditor = new UbuntuManifestTextEditorWidget(mimeType, this); setWidgetResizable(true); setWidget(m_widgetStack); connect(m_sourceEditor->textDocument(), &TextEditor::TextDocument::aboutToOpen, this, &UbuntuAbstractGuiEditorWidget::aboutToOpen); connect(m_sourceEditor->textDocument(), &TextEditor::TextDocument::reloadFinished, this, [this](bool success) { if (success) updateAfterFileLoad(); }); connect(m_sourceEditor->textDocument(), &TextEditor::TextDocument::openFinishedSuccessfully, this, &UbuntuAbstractGuiEditorWidget::updateAfterFileLoad); connect(m_widgetStack, &QStackedWidget::currentChanged, this, &UbuntuAbstractGuiEditorWidget::editorViewChanged); } UbuntuAbstractGuiEditorWidget::~UbuntuAbstractGuiEditorWidget() { } void UbuntuAbstractGuiEditorWidget::aboutToOpen(const QString &, const QString &) { //do nothing } void UbuntuAbstractGuiEditorWidget::updateAfterFileLoad() { //do nothing } bool UbuntuAbstractGuiEditorWidget::isModified() const { return m_dirty; } UbuntuAbstractGuiEditorWidget::EditorPage UbuntuAbstractGuiEditorWidget::activePage() const { return static_cast(m_widgetStack->currentIndex()); } bool UbuntuAbstractGuiEditorWidget::setActivePage(UbuntuAbstractGuiEditorWidget::EditorPage page) { if(page == static_cast(m_widgetStack->currentIndex())) return true; if(page == UbuntuAbstractGuiEditorWidget::Source) syncToSource(); else { //this could fail because the user edited the source manually if(!syncToWidgets()){ return false; } } m_widgetStack->setCurrentIndex(page); return true; } /*! * \brief UbuntuAbstractGuiEditorWidget::preSave * Called right before the document is stored */ bool UbuntuAbstractGuiEditorWidget::preSave() { //sync the current widget state to source, so we save the correct data if(activePage() == General) syncToSource(); else return syncToWidgets(); return true; } TextEditor::TextEditorWidget *UbuntuAbstractGuiEditorWidget::textEditorWidget() const { return m_sourceEditor; } void UbuntuAbstractGuiEditorWidget::setDirty() { m_dirty = true; emit uiEditorChanged(); } void UbuntuAbstractGuiEditorWidget::createUI() { m_widgetStack->insertWidget(General,createMainWidget()); m_widgetStack->insertWidget(Source,m_sourceEditor); } void UbuntuAbstractGuiEditorWidget::saved() { //default impl does nothing } void UbuntuAbstractGuiEditorWidget::updateInfoBar(const QString &errorMessage) { Core::InfoBar *infoBar = m_sourceEditor->textDocument()->infoBar(); infoBar->removeInfo(infoBarId); if(!errorMessage.isEmpty()){ Core::InfoBarEntry infoBarEntry(infoBarId, errorMessage); infoBar->addInfo(infoBarEntry); } } UbuntuManifestTextEditorWidget::UbuntuManifestTextEditorWidget(QString mimeType, UbuntuAbstractGuiEditorWidget *parent) : TextEditor::TextEditorWidget(parent), m_parent(parent), m_mimeType(mimeType) { setTextDocument(TextEditor::TextDocumentPtr(new UbuntuAbstractGuiEditorDocument(mimeType,parent))); textDocument()->setMimeType(mimeType); setupGenericHighlighter(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntupolicygroupinfo.h0000644000015600001650000000264212705421114020053 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPOLICYGROUPINFO_H #define UBUNTUPOLICYGROUPINFO_H #include #include "ubuntuprocess.h" namespace Ubuntu { namespace Internal { class UbuntuPolicyGroupInfo : public QObject { Q_OBJECT public: explicit UbuntuPolicyGroupInfo(QObject *parent = 0); QString info() { return m_replies; } bool isLocal() { return m_bLocal; } public slots: void getInfo(const QString &, const QString &policyVersion); void onMessage(QString); void onFinished(QString, int); signals: void infoReady(bool); protected: UbuntuProcess m_process; QString m_replies; QString m_policyGroup; QString m_policyVersion; bool m_bLocal; }; } } #endif // UBUNTUPOLICYGROUPINFO_H ./src/ubuntu/ubuntusecuritypolicypickerdialog.h0000644000015600001650000000311112705421114022260 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef SECURITYPOLICYPICKERDIALOG_H #define SECURITYPOLICYPICKERDIALOG_H #include #include "ubuntupolicygroupmodel.h" #include "ubuntupolicygroupinfo.h" using namespace Ubuntu::Internal; namespace Ui { class UbuntuSecurityPolicyPickerDialog; } class UbuntuSecurityPolicyPickerDialog : public QDialog { Q_OBJECT public: explicit UbuntuSecurityPolicyPickerDialog(const QString &policyVersion, QWidget *parent = 0); ~UbuntuSecurityPolicyPickerDialog(); QStringList selectedPolicyGroups(); protected slots: void on_pushButtonCancel_clicked(); void on_pushButtonAdd_clicked(); void onScanComplete(bool ok); void onInfoChanged(bool ok); void onPolicyClicked(QModelIndex); private: Ui::UbuntuSecurityPolicyPickerDialog *ui; UbuntuPolicyGroupModel m_model; UbuntuPolicyGroupInfo m_info; }; #endif // SECURITYPOLICYPICKERDIALOG_H ./src/ubuntu/ubuntuscopefinalizer.h0000644000015600001650000000333712705421114017642 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller * * Based on the information found on: * http://stackoverflow.com/questions/10270328/the-simplest-and-neatest-c11-scopeguard * http://stackoverflow.com/questions/1597007/creating-c-macro-with-and-line-token-concatenation-with-positioning-macr */ #ifndef UBUNTUSCOPEFINALIZER_H #define UBUNTUSCOPEFINALIZER_H #include /*! \class ScopeFinalizer Defines a way to clean up a scope nicely when its exited */ template struct ScopeFinalizer { inline ScopeFinalizer(){} inline ScopeFinalizer& operator<<(CleanupFunc &&lambda){ m_lambda = std::move(lambda); return *this; } inline ~ScopeFinalizer(){ m_lambda(); } CleanupFunc m_lambda; }; #define ScopeFinalizer_TOKENPASTE(x, y) x ## y #define ScopeFinalizer_TOKENPASTE2(x, y) ScopeFinalizer_TOKENPASTE(x, y) #define OnScopeExit ScopeFinalizer< std::function > ScopeFinalizer_TOKENPASTE2(OnScopeExit_,__LINE__); ScopeFinalizer_TOKENPASTE2(OnScopeExit_,__LINE__) << [&]() #endif // UBUNTUSCOPEFINALIZER_H ./src/ubuntu/ubuntulocalrunconfigurationfactory.cpp0000644000015600001650000002020712705421114023152 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntulocalrunconfigurationfactory.h" #include "ubunturemoterunconfiguration.h" #include "ubuntuprojecthelper.h" #include "ubuntudevice.h" #include "clicktoolchain.h" #include "ubuntuclickmanifest.h" #include "ubuntucmakecache.h" #include "ubuntushared.h" #include #include #include #include #include #include #include using namespace Ubuntu; using namespace Ubuntu::Internal; enum { debug = 0 }; QList UbuntuLocalRunConfigurationFactory::availableCreationIds(ProjectExplorer::Target *parent, CreationMode mode) const { Q_UNUSED(mode); QList types; Core::Id targetDevice = ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(parent->kit()); if(targetDevice != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE && !targetDevice.toString().startsWith(QLatin1String(Ubuntu::Constants::UBUNTU_DEVICE_TYPE_ID))) { if(debug) qDebug()<<"Rejecting device type: "<project()->id() == CMakeProjectManager::Constants::CMAKEPROJECT_ID; bool isHTML = parent->project()->id() == Ubuntu::Constants::UBUNTUPROJECT_ID; bool isQML = parent->project()->id() == "QmlProjectManager.QmlProject"; bool isQMake = parent->project()->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID; if (!isCMake && !isHTML &&!isQML &&!isQMake) return types; if (isRemote) { //IF we have a remote device we just support a ubuntu toolchain ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(parent->kit()); if(tc && tc->type() != QLatin1String(Ubuntu::Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) return types; } QString defaultPath = QDir::cleanPath(parent->project()->projectDirectory() .appendPath(QStringLiteral("manifest.json")) .toString()); QString manifestPath = UbuntuProjectHelper::getManifestPath(parent,defaultPath); qDebug()<<"Using the manifest path: "< hooks = manifest.hooks(); if (!isRemote) { foreach (const UbuntuClickManifest::Hook &hook, hooks) { if(hook.type() == UbuntuClickManifest::Hook::Application) types << Core::Id(Constants::UBUNTUPROJECT_RUNCONTROL_APP_ID).withSuffix(hook.appId); else if (hook.type() == UbuntuClickManifest::Hook::Scope) types << Core::Id(Constants::UBUNTUPROJECT_RUNCONTROL_SCOPE_ID).withSuffix(hook.appId); } } else if (isRemote) { if (isCMake || isHTML || isQML || isQMake) { foreach (const UbuntuClickManifest::Hook &hook, hooks) { if(hook.type() == UbuntuClickManifest::Hook::Application) types << Core::Id(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_APP_ID).withSuffix(hook.appId); else if (hook.type() == UbuntuClickManifest::Hook::Scope) types << Core::Id(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_SCOPE_ID).withSuffix(hook.appId); } } } return types; } QString UbuntuLocalRunConfigurationFactory::displayNameForId(const Core::Id id) const { if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_APP_ID ))) return id.suffixAfter(Constants::UBUNTUPROJECT_RUNCONTROL_APP_ID); else if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_SCOPE_ID ))) return id.suffixAfter(Constants::UBUNTUPROJECT_RUNCONTROL_SCOPE_ID); else if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_APP_ID ))) return id.suffixAfter(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_APP_ID); else if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_SCOPE_ID ))) return id.suffixAfter(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_SCOPE_ID); return QString(); } bool UbuntuLocalRunConfigurationFactory::canCreate(ProjectExplorer::Target *parent, const Core::Id id) const { if (!parent) return false; if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_APP_ID ))) return true; else if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_SCOPE_ID ))) return true; else if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_APP_ID ))) return true; else if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_SCOPE_ID ))) return true; return false; } bool UbuntuLocalRunConfigurationFactory::canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const { if (!parent) return false; Core::Id id = ProjectExplorer::idFromMap(map); return canCreate(parent, id); } ProjectExplorer::RunConfiguration *UbuntuLocalRunConfigurationFactory::doCreate(ProjectExplorer::Target *parent, const Core::Id id) { if (!canCreate(parent, id)) return NULL; if ( id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_BASE_ID))) return new UbuntuRemoteRunConfiguration(parent, id); else if (id.toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_BASE_ID))) return new UbuntuLocalRunConfiguration(parent, id); return 0; } ProjectExplorer::RunConfiguration *UbuntuLocalRunConfigurationFactory::doRestore(ProjectExplorer::Target *parent, const QVariantMap &map) { if (!canRestore(parent, map)) return NULL; ProjectExplorer::RunConfiguration *conf = create(parent,ProjectExplorer::idFromMap(map)); if(!conf) return NULL; if(!conf->fromMap(map)) { delete conf; return NULL; } return conf; } bool UbuntuLocalRunConfigurationFactory::canClone(ProjectExplorer::Target *parent, ProjectExplorer::RunConfiguration *product) const { return canCreate(parent,product->id()); } ProjectExplorer::RunConfiguration *UbuntuLocalRunConfigurationFactory::clone(ProjectExplorer::Target *parent, ProjectExplorer::RunConfiguration *source) { if (!canClone(parent, source)) return NULL; if(source->id().toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_BASE_ID))) return new UbuntuRemoteRunConfiguration(parent,static_cast(source)); return new UbuntuLocalRunConfiguration(parent,static_cast(source)); } ./src/ubuntu/ubuntuprojectnode.h0000644000015600001650000000601312705421114017133 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPROJECTNODE_H #define UBUNTUPROJECTNODE_H #include #include "ubuntuproject.h" #include "ubuntuprojectmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuProject; class UbuntuProjectNode : public ProjectExplorer::ProjectNode { public: UbuntuProjectNode(UbuntuProject *project, Core::IDocument *projectFile); Core::IDocument *projectFile() const; QString projectFilePath() const; QList supportedActions(Node *node) const override; bool canAddSubProject(const QString &proFilePath) const override; bool addSubProjects(const QStringList &proFilePaths) override; bool removeSubProjects(const QStringList &proFilePaths) override; bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = 0) override; bool deleteFiles(const QStringList &filePaths) override; bool renameFile(const QString &filePath, const QString &newFilePath) override; QList runConfigurations( ) const override; void refresh(); private: FolderNode *findOrCreateFolderByName(const QString &filePath); FolderNode *findOrCreateFolderByName(const QStringList &components, int end); private: UbuntuProject *m_project; Core::IDocument *m_projectFile; QHash m_folderByName; }; } } #endif // UBUNTUPROJECTNODE_H ./src/ubuntu/ubuntudevicemode.h0000644000015600001650000000416012705421114016724 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUDEVICEMODE_H #define UBUNTUDEVICEMODE_H #include "ubuntudevice.h" #include class QQuickView; namespace Ubuntu { namespace Internal { class UbuntuDevicesModel; class UbuntuEmulatorModel; class UbuntuDeviceMode; class UbuntuQMLDeviceMode : public QObject { Q_OBJECT public: UbuntuQMLDeviceMode( UbuntuDeviceMode *parent ); void showAddEmulatorDialog (); public slots: void deviceSelected ( const QVariant index ); void addText (const QString &arg); void addErrorText (const QString &error); signals: void logChanged(const QString &arg); void appendText(const QString &newText); void openAddEmulatorDialog (); private: UbuntuDeviceMode* m_mode; }; class UbuntuDeviceMode : public Core::IMode { Q_OBJECT public: UbuntuDeviceMode(QObject *parent = 0); void initialize(); static UbuntuDeviceMode *instance(); UbuntuDevice::ConstPtr device(); void deviceSelected ( const QVariant index ); public slots: void showAddEmulatorDialog (); signals: void updateDeviceActions (); protected slots: void modeChanged(Core::IMode*); protected: static UbuntuDeviceMode *m_instance; UbuntuDevicesModel *m_devicesModel; UbuntuQMLDeviceMode *m_qmlControl; QQuickView *m_modeView; QWidget* m_modeWidget; QVariant m_deviceIndex; QString m_log; }; } // Internal } // Ubuntu #endif // UBUNTUDEVICEMODE_H ./src/ubuntu/ubuntudirectuploadstep.cpp0000644000015600001650000001370412705421114020532 0ustar jenkinsjenkins#include "ubuntudirectuploadstep.h" #include "ubuntupackagestep.h" #include "ubuntuconstants.h" #include "ubuntudevice.h" #include "ubunturemotedeployconfiguration.h" #include "ubuntuwaitfordevicedialog.h" #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; static UbuntuDevice::ConstPtr deviceFromTarget (ProjectExplorer::Target *target) { if(!target || !target->kit()) return UbuntuDevice::ConstPtr(); ProjectExplorer::IDevice::ConstPtr dev = ProjectExplorer::DeviceKitInformation::device(target->kit()); if(!dev) return UbuntuDevice::ConstPtr(); if(!dev->type().toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID))) return UbuntuDevice::ConstPtr(); return qSharedPointerCast(dev); } UbuntuDirectUploadStep::UbuntuDirectUploadStep(ProjectExplorer::BuildStepList *bsl) : AbstractRemoteLinuxDeployStep(bsl, UbuntuDirectUploadStep::stepId()) , m_deployService(new RemoteLinux::GenericDirectUploadService(this)) , m_future(0) { setDefaultDisplayName(displayName()); } UbuntuDirectUploadStep::UbuntuDirectUploadStep(ProjectExplorer::BuildStepList *bsl, UbuntuDirectUploadStep *other) : AbstractRemoteLinuxDeployStep(bsl, other) , m_deployService(new RemoteLinux::GenericDirectUploadService(this)) , m_future(0) { setDefaultDisplayName(displayName()); connect(target()->project(),SIGNAL(displayNameChanged()),this,SLOT(projectNameChanged())); connect(target(),SIGNAL(applicationTargetsChanged()),this,SLOT(projectNameChanged())); } UbuntuDirectUploadStep::~UbuntuDirectUploadStep() { } void UbuntuDirectUploadStep::run(QFutureInterface &fi) { m_foundClickPackage = false; projectNameChanged(); if(!m_foundClickPackage) { emit addOutput(tr("Deploy step failed. No click package was created"), ErrorMessageOutput); fi.reportResult(false); emit finished(); return; } m_deployService->setIncrementalDeployment(false); m_deployService->setIgnoreMissingFiles(false); UbuntuDevice::ConstPtr dev = deviceFromTarget(target()); if(!dev) { emit addOutput(tr("Deploy step failed. No valid device configured"), ErrorMessageOutput); fi.reportResult(false); emit finished(); return; } m_future = &fi; if(dev->deviceState() != ProjectExplorer::IDevice::DeviceReadyToUse) { //we are already waiting if (m_waitDialog) return; m_waitDialog = new UbuntuWaitForDeviceDialog(Core::ICore::mainWindow()); connect(m_waitDialog.data(),SIGNAL(canceled()),this,SLOT(handleWaitDialogCanceled())); connect(m_waitDialog.data(),SIGNAL(deviceReady()),this,SLOT(handleDeviceReady())); m_waitDialog->show(dev); if(dev->machineType() == ProjectExplorer::IDevice::Emulator && dev->deviceState() == ProjectExplorer::IDevice::DeviceDisconnected) dev->helper()->device()->startEmulator(); } else { handleDeviceReady(); } } void UbuntuDirectUploadStep::handleWaitDialogCanceled( ) { m_waitDialog->deleteLater(); emit addOutput(tr("Deploy step failed"), ErrorMessageOutput); m_future->reportResult(false); m_future = 0; emit finished(); } void UbuntuDirectUploadStep::handleDeviceReady() { m_waitDialog->deleteLater(); QString whyNot; if(!deployService()->isDeploymentPossible(&whyNot)) { emit addOutput(tr("Deploy step failed. %1").arg(whyNot), ErrorMessageOutput); m_future->reportResult(false); m_future = 0; emit finished(); return; } AbstractRemoteLinuxDeployStep::run(*m_future); m_future = 0; } ProjectExplorer::BuildStepConfigWidget *UbuntuDirectUploadStep::createConfigWidget() { return new ProjectExplorer::SimpleBuildStepConfigWidget(this); } bool UbuntuDirectUploadStep::initInternal(QString *error) { Q_UNUSED(error) return true; } RemoteLinux::AbstractRemoteLinuxDeployService *UbuntuDirectUploadStep::deployService() const { return m_deployService; } bool UbuntuDirectUploadStep::fromMap(const QVariantMap &map) { if (!AbstractRemoteLinuxDeployStep::fromMap(map)) return false; return true; } QVariantMap UbuntuDirectUploadStep::toMap() const { return AbstractRemoteLinuxDeployStep::toMap(); } Core::Id UbuntuDirectUploadStep::stepId() { return Constants::UBUNTU_DEPLOY_UPLOADSTEP_ID; } QString UbuntuDirectUploadStep::displayName() { return tr("Upload files to Ubuntu Device"); } void UbuntuDirectUploadStep::projectNameChanged() { if(debug) qDebug()<<"------------------------ Updating DEPLOYLIST ---------------------------"; ProjectExplorer::BuildStepList *bsList = qobject_cast(BuildStep::deployConfiguration())->stepList(); QList list; foreach(ProjectExplorer::BuildStep *currStep ,bsList->steps()) { UbuntuPackageStep *pckStep = qobject_cast(currStep); if(!pckStep) continue; QFileInfo info(pckStep->packagePath()); if(info.exists()) { list.append(ProjectExplorer::DeployableFile(info.filePath(), QStringLiteral("/tmp"))); list.append(ProjectExplorer::DeployableFile(QStringLiteral("%1/qtc_device_applaunch.py").arg(Constants::UBUNTU_SCRIPTPATH), QStringLiteral("/tmp"))); m_deployService->setDeployableFiles(list); m_foundClickPackage = true; break; } } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntumenu.h0000644000015600001650000000505612705421114015571 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUMENU_H #define UBUNTUMENU_H #include #include "ubuntuprocess.h" #include "ubuntuconstants.h" #include #include #include #include #include namespace ProjectExplorer { class Project; } namespace Ubuntu { namespace Internal { class UbuntuMenu : public QObject { Q_OBJECT public: explicit UbuntuMenu(QObject *parent = 0); void initialize(); static UbuntuMenu* instance(); static QAction* menuAction(const Core::Id& id); void parseMenu(QJsonObject obj, Core::ActionContainer*& parent, const Core::Id &group = Core::Id()); QString menuPath(QString fileName); QJsonDocument getMenuJSON(); public slots: void slotUpdateActions(); signals: void finished_action(QString); void finished_action(const QProcess* process, QString cmd); void requestBuildAndInstallProject (); //triggered from menu.json void requestBuildAndVerifyProject (); //triggered from menu.json void requestBuildProject (); //triggered from menu.json protected slots: void menuItemTriggered(); void onStarted(QString); void onMessage(QString); void onError(QString); void onFinished(QString cmd, int code); void onFinished(const QProcess* programm, QString cmd, int code); void createManifestFile(); void setContextMenuProject(ProjectExplorer::Project* p); protected: typedef QList QJsonValueList; QJsonDocument m_obj; QMap m_commandMap; UbuntuProcess m_ubuntuProcess; QMap m_actions; private: bool isProperUbuntuHtmlProject(ProjectExplorer::Project *project) const; static UbuntuMenu *m_instance; QPointer m_ctxMenuProject; }; } // Internal } // Ubuntu #endif // UBUNTUMENU_H ./src/ubuntu/ubuntuversionmanager.h0000644000015600001650000000277712705421114017654 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUVERSIONMANAGER_H #define UBUNTUVERSIONMANAGER_H #include #include #include #include "ubuntuversion.h" #include "ubuntuconstants.h" namespace Ubuntu { namespace Internal { class UbuntuVersionManager : public QObject { Q_OBJECT public: UbuntuVersionManager(QObject *parent = 0); ~UbuntuVersionManager (); void detectAvailableVersions(); Core::FeatureSet availableFeatures(const QString &platformName) const; QStringList availablePlatforms() const; QString displayNameForPlatform(const QString &string) const; static UbuntuVersionManager* instance(); protected: static UbuntuVersionManager *m_self; UbuntuVersion* m_hostVersion; }; } } #endif // UBUNTUVERSIONMANAGER_H ./src/ubuntu/ubuntudevicefactory.h0000644000015600001650000000306312705421114017450 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUDEVICEFACTORY_H #define UBUNTU_INTERNAL_UBUNTUDEVICEFACTORY_H #include namespace Ubuntu { namespace Internal { class UbuntuDeviceFactory : public ProjectExplorer::IDeviceFactory { Q_OBJECT public: UbuntuDeviceFactory(QObject* parent = 0); // IDeviceFactory interface virtual QString displayNameForId(Core::Id type) const override; virtual QList availableCreationIds() const override; virtual bool canCreate() const override; virtual ProjectExplorer::IDevice::Ptr create(Core::Id id) const override; virtual bool canRestore(const QVariantMap &map) const override; virtual ProjectExplorer::IDevice::Ptr restore(const QVariantMap &map) const override; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUDEVICEFACTORY_H ./src/ubuntu/ubuntutestcontrol.h0000644000015600001650000000150312705421114017176 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUTESTCONTROL_H #define UBUNTU_INTERNAL_UBUNTUTESTCONTROL_H #include #include namespace Ubuntu { namespace Internal { class UbuntuTestControl : public QObject { Q_OBJECT Q_PROPERTY(bool lastBuildSuccess READ lastBuildSuccess) public: explicit UbuntuTestControl(QObject *parent = 0); signals: void buildFinished (); void lastBuildSuccessChanged(bool arg); public slots: void setLastBuildSuccess(bool arg); bool lastBuildSuccess() const; void triggerCommand(const QString &command); ProjectExplorer::BuildManager *buildManager(); protected: bool eventFilter(QObject *, QEvent *event); private: bool m_lastBuildSuccess; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUTESTCONTROL_H ./src/ubuntu/ubuntulocalrunconfiguration.cpp0000644000015600001650000005562312705421114021574 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntulocalrunconfiguration.h" #include "ubuntuproject.h" #include "ubuntuprojecthelper.h" #include "ubuntuclickmanifest.h" #include "ubunturemoterunconfiguration.h" #include "ubuntucmakecache.h" #include "ubuntuprojecthelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu::Internal; enum { debug = 0 }; UbuntuLocalEnvironmentAspect::UbuntuLocalEnvironmentAspect(ProjectExplorer::RunConfiguration *parent) : LocalEnvironmentAspect(parent) { } Utils::Environment UbuntuLocalEnvironmentAspect::baseEnvironment() const { Utils::Environment env = LocalEnvironmentAspect::baseEnvironment(); if (const UbuntuLocalRunConfiguration *rc = qobject_cast(runConfiguration())) rc->addToBaseEnvironment(env); return env; } UbuntuLocalRunConfiguration::UbuntuLocalRunConfiguration(ProjectExplorer::Target *parent, Core::Id id) : ProjectExplorer::RunConfiguration(parent, id) { setDisplayName(appId()); addExtraAspect(new UbuntuLocalEnvironmentAspect(this)); } UbuntuLocalRunConfiguration::UbuntuLocalRunConfiguration(ProjectExplorer::Target *parent, UbuntuLocalRunConfiguration *source) : ProjectExplorer::RunConfiguration(parent,source) { } QWidget *UbuntuLocalRunConfiguration::createConfigurationWidget() { return NULL; } bool UbuntuLocalRunConfiguration::isEnabled() const { return true; } QString UbuntuLocalRunConfiguration::appId() const { if(id().toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_APP_ID))) return id().suffixAfter(Constants::UBUNTUPROJECT_RUNCONTROL_APP_ID); else return id().suffixAfter(Constants::UBUNTUPROJECT_RUNCONTROL_SCOPE_ID); } QString UbuntuLocalRunConfiguration::executable() const { return m_executable; } QString UbuntuLocalRunConfiguration::workingDirectory() const { return m_workingDir.toString(); } QString UbuntuLocalRunConfiguration::commandLineArguments() const { return Utils::QtcProcess::joinArgs(m_args); } ProjectExplorer::ApplicationLauncher::Mode UbuntuLocalRunConfiguration::runMode() const { return ProjectExplorer::ApplicationLauncher::Gui; } ProjectExplorer::RunConfiguration::ConfigurationState UbuntuLocalRunConfiguration::ensureConfigured(QString *) { return Configured; } bool UbuntuLocalRunConfiguration::aboutToStart(QString *errorMessage) { if(target()->project()->id() != Constants::UBUNTUPROJECT_ID) { QString idString = id().toString(); if(idString.startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_APP_ID))) { if (ensureClickAppConfigured(errorMessage)) return true; else return false; } else if(idString.startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_SCOPE_ID))) { if (ensureScopesAppConfigured(errorMessage)) return true; else return false; } //all other hook types can not be configured //should never happen if(errorMessage) *errorMessage = tr("Unknown hook type, only scope and app hooks are supported"); return false; } if (ensureUbuntuProjectConfigured(errorMessage)) return true; return false; } QString UbuntuLocalRunConfiguration::getDesktopFile(ProjectExplorer::RunConfiguration* config, QString appId, QString *errorMessage) { //lambda reads the desktop file from the manifest auto getDesktopFromManifest = [&]( UbuntuClickManifest &manifest ){ QList hooks = manifest.hooks(); if (hooks.isEmpty()) { if(errorMessage) *errorMessage = tr("No valid hooks property in the manifest"); return QString(); } foreach(const UbuntuClickManifest::Hook& hook, hooks) { if( hook.appId == appId ) return hook.desktopFile; } return QString(); }; QString manifestPath; if(qobject_cast(config)) { //For a locally compiled project, we have no CLICK_MODE enabled, that means we have to search //the project and build trees for our desktop file so we can query the main qml file from the desktop file Utils::FileName projectDir = config->target()->project()->projectDirectory(); //lambda that searches for the desktop file in the project and build directory auto searchDesktopFile = [&]( const QString &desktopFileName ){ QRegularExpression desktopFinder = QRegularExpression(QString::fromLatin1("^%1$") .arg(desktopFileName)); QFileInfo desktopInfo = UbuntuProjectHelper::findFileRecursive(config->target()->activeBuildConfiguration()->buildDirectory(), desktopFinder).toFileInfo(); if (!desktopInfo.exists()) { //search again in the project directory desktopInfo = UbuntuProjectHelper::findFileRecursive(config->target()->project()->projectDirectory(), desktopFinder).toFileInfo(); if(!desktopInfo.exists()) return QString(); } return desktopInfo.absoluteFilePath(); }; //first lets check if the informations in the manifest file are helpful QString manifestPath = UbuntuProjectHelper::getManifestPath(config->target(),QStringLiteral("manifest.json")); //make the path relative to the project dir QDir projectDirDir(projectDir.toString()); manifestPath = projectDirDir.relativeFilePath(manifestPath); //search for the manifest file in the project dir AND in the builddir QList searchPaths{ projectDir.appendPath(manifestPath), config->target()->activeBuildConfiguration()->buildDirectory() .appendPath(QFileInfo(manifestPath).path()) .appendPath(QStringLiteral("manifest.json")) }; for(Utils::FileName path : searchPaths) { QFileInfo manifestFile(path.toString()); if(debug) qDebug()<<"Searching for the manifest file: "<target()->project()->displayName().append(QStringLiteral(".desktop"))); if(desk.isEmpty()) { if(errorMessage) *errorMessage = tr("Could not find a desktop file for the hook: %1, \nmaybe it is missing in the install targets.").arg(appId); return QString(); } return desk; } UbuntuRemoteRunConfiguration *remConf = qobject_cast(config); if (!remConf) { if(errorMessage) *errorMessage = tr("Invalid configuration type"); return QString(); } QDir package_dir(remConf->packageDir()); if(!package_dir.exists()) { if(errorMessage) *errorMessage = tr("No packaging directory available, please check if the deploy configuration is correct."); return QString(); } manifestPath = package_dir.absoluteFilePath(QStringLiteral("manifest.json")); //read the manifest UbuntuClickManifest manifest; QString manifestErrMsg; if(!manifest.load(manifestPath,nullptr,&manifestErrMsg)) { if(errorMessage) *errorMessage = tr("Could not open the manifest file in the package directory: %1.").arg(manifestErrMsg); return QString(); } QString desk = getDesktopFromManifest(manifest); if(!desk.isEmpty()) return QDir::cleanPath(package_dir.absoluteFilePath(desk)); if (errorMessage) *errorMessage = tr("Could not find a %1 hook in the manifest file").arg(appId); return QString(); } bool UbuntuLocalRunConfiguration::readDesktopFile(const QString &desktopFile, QString *executable, QStringList *arguments, QString *errorMessage) { QFile desktop(desktopFile); if(!desktop.exists()) { if(errorMessage) *errorMessage = tr("Desktop file does not exist"); return false; } if(!desktop.open(QIODevice::ReadOnly)) { if(errorMessage) *errorMessage = tr("Could not open desktop file for reading"); return false; } QString execLine; QString name; QTextStream in(&desktop); while(!in.atEnd()) { QString line = in.readLine(); if(line.startsWith(QLatin1String("#"))) continue; line = line.mid(0,line.indexOf(QChar::fromLatin1('#'))).trimmed(); if(line.startsWith(QLatin1String("Exec"),Qt::CaseInsensitive)) { execLine = line.mid(line.indexOf(QChar::fromLatin1('='))+1); if(debug) qDebug()<<"Found exec line: "<contains(QStringLiteral("qtc_device_debughelper.py"))) { args = Utils::QtcProcess::splitArgs(args[1]); *executable = args.takeFirst(); } *arguments = args; qDebug()<activeBuildConfiguration()->buildDirectory(); bool isCMake = target()->project()->id() == CMakeProjectManager::Constants::CMAKEPROJECT_ID; //bool isHTML = target()->project()->id() == Ubuntu::Constants::UBUNTUPROJECT_ID; //bool isQML = target()->project()->id() == "QmlProjectManager.QmlProject"; bool isQMake = target()->project()->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID; QFileInfo commInfo(command); if(commInfo.fileName().startsWith(QLatin1String("qmlscene"))) { m_executable = QtSupport::QtKitInformation::qtVersion(target()->kit())->qmlsceneCommand(); QString mainQml; foreach(const QString &arg, args) { if(arg.contains(QLatin1String(".qml"))) { QFileInfo info(arg); mainQml = info.fileName(); } } if(mainQml.isEmpty()) { if(errorMessage) *errorMessage = tr("Could not get the main qml file from the desktop EXEC line."); return false; } QFileInfo mainFileInfo = UbuntuProjectHelper::findFileRecursive(target()->project()->projectDirectory(), QString::fromLatin1("^%1$").arg(mainQml)).toFileInfo(); if(!mainFileInfo.exists()) { if(errorMessage) *errorMessage = tr("Could not find the main qml file (%1) in the project directory.").arg(mainQml); return false; } m_args = QStringList()< (target()->project()); foreach(const QmakeProjectManager::QmakeProFileNode* applPro, pro->applicationProFiles()) { QmakeProjectManager::TargetInformation info = applPro->targetInformation(); if(applPro->targetInformation().valid) { if(info.target == commInfo.fileName()) { m_executable = info.buildDir + QDir::separator() + info.target; break; } } } } else if(isCMake){ CMakeProjectManager::CMakeProject* proj = static_cast (target()->project()); QList targets = proj->buildTargets(); foreach (const CMakeProjectManager::CMakeBuildTarget& t, targets) { if(t.targetType != CMakeProjectManager::ExecutableType) continue; QFileInfo targetInfo(t.executable); if(!targetInfo.exists()) continue; if(targetInfo.fileName() == commInfo.fileName()) { m_executable = targetInfo.absoluteFilePath(); break; } } } if (m_executable.isEmpty()) { if(errorMessage) *errorMessage = tr("Could not find executable specified in the desktop file"); return false; } m_args = args; m_workingDir = target()->project()->projectDirectory(); } if(debug) qDebug()<<"Configured with: "<activeBuildConfiguration()->buildDirectory(); Utils::FileName exec = Utils::FileName::fromString(Ubuntu::Constants::UBUNTU_SCRIPTPATH); exec.appendPath(QLatin1String(Ubuntu::Constants::UBUNTUSCOPES_PROJECT_LAUNCHER_EXE)); m_executable = exec.toString(); Utils::FileName iniFile = UbuntuProjectHelper::findScopesIniRecursive(target()->activeBuildConfiguration()->buildDirectory(),appId()); if(iniFile.toFileInfo().exists()) m_args = QStringList()<(target()->project()); if (ubuntuProject) { m_workingDir = ubuntuProject->projectDirectory(); if (ubuntuProject->mainFile().compare(QString::fromLatin1("www/index.html"), Qt::CaseInsensitive) == 0) { Utils::Environment env = Utils::Environment::systemEnvironment(); m_executable = env.searchInPath(QString::fromLatin1(Ubuntu::Constants::UBUNTUHTML_PROJECT_LAUNCHER_EXE)).toString(); m_args = QStringList()<projectDirectory().toString()) <mainFile().endsWith(QStringLiteral(".desktop"), Qt::CaseInsensitive)) { Utils::Environment env = Utils::Environment::systemEnvironment(); if(!readDesktopFile(ubuntuProject->projectDirectory() .appendPath(ubuntuProject->mainFile()) .toString() ,&m_executable,&m_args,errorMessage)) return false; m_executable = env.searchInPath(QString::fromLatin1(Ubuntu::Constants::UBUNTUWEBAPP_PROJECT_LAUNCHER_EXE)).toString(); } else { m_executable = QtSupport::QtKitInformation::qtVersion(target()->kit())->qmlsceneCommand(); m_args = QStringList()<displayName()); } if(m_executable.isEmpty()) { if(errorMessage) *errorMessage = tr("Could not find a launcher for this projecttype in path"); return false; } return true; } if(errorMessage) *errorMessage = tr("Unsupported Project Type used with UbuntuRunConfiguration"); return false; } void UbuntuLocalRunConfiguration::addToBaseEnvironment(Utils::Environment &env) const { QSet usedPaths; //lambda checks if the executable is in a qmldir and add its to QML2_IMPORT_PATH if //required auto loc_addToImportPath = [&usedPaths,&env] (const QString &loc_buildDir) { const QString absolutePath = loc_buildDir; if(debug) qDebug()<<"Looking in the dir: "<project()->id() == CMakeProjectManager::Constants::CMAKEPROJECT_ID) { CMakeProjectManager::CMakeProject* proj = static_cast (target()->project()); QList targets = proj->buildTargets(); foreach (const CMakeProjectManager::CMakeBuildTarget& t, targets) { if(t.targetType == CMakeProjectManager::DynamicLibraryType) loc_addToImportPath(t.workingDirectory); } } else if (target()->project()->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID) { QmakeProjectManager::QmakeProject* pro = static_cast (target()->project()); foreach(const QmakeProjectManager::QmakeProFileNode* applPro, pro->allProFiles()) { if(applPro->projectType() != QmakeProjectManager::ApplicationTemplate && applPro->projectType() != QmakeProjectManager::SharedLibraryTemplate && applPro->projectType() != QmakeProjectManager::ScriptTemplate && applPro->projectType() != QmakeProjectManager::AuxTemplate) { continue; } QmakeProjectManager::TargetInformation info = applPro->targetInformation(); if(applPro->targetInformation().valid) loc_addToImportPath(info.buildDir); // The user could be linking to a library found via a -L/some/dir switch // to find those libraries while actually running we explicitly prepend those // dirs to the library search path const QStringList libDirectories = applPro->variableValue(QmakeProjectManager::LibDirectoriesVar); if (!libDirectories.isEmpty()) { const QString proDirectory = applPro->buildDir(); foreach (QString dir, libDirectories) { // Fix up relative entries like "LIBS+=-L.." const QFileInfo fi(dir); if (!fi.isAbsolute()) dir = QDir::cleanPath(proDirectory + QLatin1Char('/') + dir); env.prependOrSetLibrarySearchPath(dir); } // foreach } // libDirectories } } QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(target()->kit()); if (qtVersion) env.prependOrSetLibrarySearchPath(qtVersion->qmakeProperty("QT_INSTALL_LIBS")); } bool UbuntuLocalRunConfiguration::isConfigured() const { return true; } ./src/ubuntu/settings.cpp0000644000015600001650000002313212705421114015550 0ustar jenkinsjenkins#include "settings.h" #include "ubuntuconstants.h" #include #include #include #include #include #include #include #include #include #include namespace { static const char UBUNTUSDK_DATA_KEY[] = "UbuntuSdk."; static const char UBUNTUSDK_FILE_VERSION_KEY[] = "Version"; static const char UBUNTUSDK_FILENAME[] = "/qtcreator/ubuntu-sdk/config.xml"; static const bool DEFAULT_DEVICES_AUTOTOGGLE = true; static const char KEY_USERNAME[] = "DeviceConnectivity.Username"; static const char KEY_IP[] = "DeviceConnectivity.IP"; static const char KEY_SSH[] = "DeviceConnectivity.SSH"; static const char KEY_AUTOTOGGLE[] = "Devices.Auto_Toggle"; static const char KEY_AUTO_CHECK_CHROOT_UPDATES[] = "Click.Auto_Check_Chroot_Updates"; static const char KEY_CHROOT_USE_LOCAL_MIRROR[] = "Click.Chroot_Use_Local_Mirror"; static const char KEY_TREAT_REVIEW_ERRORS_AS_WARNINGS[] = "ProjectDefaults.Treat_Review_Warnings_As_Errors"; static const char KEY_ENABLE_DEBUG_HELPER_DEFAULT[] = "ProjectDefaults.Enable_Debug_Helper_By_Default"; static const char KEY_UNINSTALL_APPS_FROM_DEVICE_DEFAULT[] = "ProjectDefaults.Uninstall_Apps_From_Device_By_Default"; static const char KEY_OVERRIDE_APPS_BY_DEFAULT[] = "ProjectDefaults.Override_Apps_By_Default"; } using namespace Utils; static FileName settingsFileName(const QString &path, QSettings::Scope scope = QSettings::UserScope) { QFileInfo settingsLocation(Core::ICore::settings(scope)->fileName()); return FileName::fromString(settingsLocation.absolutePath() + path); } namespace Ubuntu { namespace Internal { Settings *Settings::m_instance = nullptr; Settings::Settings() : QObject(nullptr) { Q_ASSERT_X(!m_instance, Q_FUNC_INFO, "There can be only one Settings instance"); m_instance = this; //set default values setChrootSettings(ChrootSettings()); setDeviceConnectivity(DeviceConnectivity()); setProjectDefaults(ProjectDefaults()); setDeviceAutoToggle(DEFAULT_DEVICES_AUTOTOGGLE); //load migrate old settings migrateSettings(); //create ubuntu-sdk directory if it does not exist QString confdir = settingsPath().toString(); QDir d = QDir::root(); if(!d.exists(confdir)) { if(!d.mkpath(confdir)) qWarning()<<"Unable to create Ubuntu-SDK configuration directory "<m_settings[QLatin1String(UBUNTUSDK_FILE_VERSION_KEY)] = 1; m_instance->m_writer->save(m_instance->m_settings, Core::ICore::mainWindow()); } Settings::DeviceConnectivity Settings::deviceConnectivity() { DeviceConnectivity val; val.ip = m_instance->m_settings.value(QLatin1String(KEY_IP),val.ip).toString(); val.user = m_instance->m_settings.value(QLatin1String(KEY_USERNAME),val.user).toString(); val.sshPort = m_instance->m_settings.value(QLatin1String(KEY_SSH),val.sshPort).toInt(); return val; } void Settings::setDeviceConnectivity(const Settings::DeviceConnectivity &settings) { m_instance->m_settings[QLatin1String(KEY_IP)] = settings.ip; m_instance->m_settings[QLatin1String(KEY_USERNAME)] = settings.user; m_instance->m_settings[QLatin1String(KEY_SSH)] = settings.sshPort; } Settings::ProjectDefaults Settings::projectDefaults() { ProjectDefaults defaults; defaults.enableDebugHelper = m_instance->m_settings.value(QLatin1String(KEY_ENABLE_DEBUG_HELPER_DEFAULT), defaults.enableDebugHelper).toBool(); defaults.overrideAppsByDefault = m_instance->m_settings.value(QLatin1String(KEY_OVERRIDE_APPS_BY_DEFAULT), defaults.overrideAppsByDefault).toBool(); defaults.reviewErrorsAsWarnings = m_instance->m_settings.value(QLatin1String(KEY_TREAT_REVIEW_ERRORS_AS_WARNINGS), defaults.reviewErrorsAsWarnings).toBool(); defaults.uninstallAppsByDefault = m_instance->m_settings.value(QLatin1String(KEY_UNINSTALL_APPS_FROM_DEVICE_DEFAULT), defaults.uninstallAppsByDefault).toBool(); return defaults; } void Settings::setProjectDefaults(const Settings::ProjectDefaults &settings) { m_instance->m_settings[QLatin1String(KEY_ENABLE_DEBUG_HELPER_DEFAULT)] = settings.enableDebugHelper; m_instance->m_settings[QLatin1String(KEY_OVERRIDE_APPS_BY_DEFAULT)] = settings.overrideAppsByDefault; m_instance->m_settings[QLatin1String(KEY_TREAT_REVIEW_ERRORS_AS_WARNINGS)] = settings.reviewErrorsAsWarnings; m_instance->m_settings[QLatin1String(KEY_UNINSTALL_APPS_FROM_DEVICE_DEFAULT)] = settings.uninstallAppsByDefault; } Settings::ChrootSettings Settings::chrootSettings() { ChrootSettings val; val.autoCheckForUpdates = m_instance->m_settings.value(QLatin1String(KEY_AUTO_CHECK_CHROOT_UPDATES),val.autoCheckForUpdates).toBool(); val.useLocalMirror = m_instance->m_settings.value(QLatin1String(KEY_CHROOT_USE_LOCAL_MIRROR),val.useLocalMirror).toBool(); return val; } void Settings::setChrootSettings(const Settings::ChrootSettings &settings) { m_instance->m_settings[QLatin1String(KEY_AUTO_CHECK_CHROOT_UPDATES)] = settings.autoCheckForUpdates; m_instance->m_settings[QLatin1String(KEY_CHROOT_USE_LOCAL_MIRROR)] = settings.useLocalMirror; } bool Settings::deviceAutoToggle() { return m_instance->m_settings.value(QLatin1String(KEY_AUTOTOGGLE), DEFAULT_DEVICES_AUTOTOGGLE).toBool(); } void Settings::setDeviceAutoToggle(const bool set) { m_instance->m_settings[QLatin1String(KEY_AUTOTOGGLE)] = set; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntucreatenewchrootdialog.ui0000644000015600001650000000411212705421114021357 0ustar jenkinsjenkins Ubuntu::Internal::UbuntuCreateNewChrootDialog 0 0 257 111 Dialog Architecture Framework Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() Ubuntu::Internal::UbuntuCreateNewChrootDialog accept() 248 254 157 274 buttonBox rejected() Ubuntu::Internal::UbuntuCreateNewChrootDialog reject() 316 260 286 274 ./src/ubuntu/ubuntuabstractguieditorwidget.h0000644000015600001650000000510512705421114021543 0ustar jenkinsjenkins/* * Copyright 2014 Digia Plc and/or its subsidiary(-ies). * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTUABSTRACTGUIEDITORWIDGET_H #define UBUNTUABSTRACTGUIEDITORWIDGET_H #include "ubuntuclickmanifest.h" #include #include #include "ui_ubuntumanifesteditor.h" class QStackedWidget; namespace ProjectExplorer {class Project;} namespace Ubuntu { namespace Internal { class UbuntuAbstractGuiEditor; class UbuntuAbstractGuiEditorWidget; class UbuntuClickManifest; class UbuntuManifestTextEditorWidget : public TextEditor::TextEditorWidget { public: UbuntuManifestTextEditorWidget(QString mimeType, UbuntuAbstractGuiEditorWidget *parent = 0); protected: UbuntuAbstractGuiEditorWidget *m_parent; QString m_mimeType; }; class UbuntuAbstractGuiEditorWidget : public QScrollArea { Q_OBJECT public: enum EditorPage { General = 0, Source = 1 }; explicit UbuntuAbstractGuiEditorWidget(const QString &mimeType); ~UbuntuAbstractGuiEditorWidget(); virtual bool isModified() const; EditorPage activePage() const; bool setActivePage(EditorPage page); bool preSave(); TextEditor::TextEditorWidget *textEditorWidget() const; virtual void saved (); protected slots: void setDirty (); void createUI (); protected: virtual void aboutToOpen(const QString &, const QString &); virtual void updateAfterFileLoad (); virtual bool syncToWidgets () = 0; virtual void syncToSource () = 0; virtual QWidget *createMainWidget () = 0; void updateInfoBar(const QString &errorMessage); signals: void uiEditorChanged(); void editorViewChanged(); protected: QStackedWidget *m_widgetStack; TextEditor::TextEditorWidget *m_sourceEditor; bool m_dirty; }; ProjectExplorer::Project *ubuntuProject(const QString &file); } // namespace Internal } // namespace Ubuntu #endif // UBUNTUABSTRACTGUIEDITORWIDGET_H ./src/ubuntu/ubuntusettingsdeviceconnectivitywidget.ui0000644000015600001650000000336712705421114023701 0ustar jenkinsjenkins UbuntuSettingsDeviceConnectivityWidget 0 0 661 533 Form Switch to Devices -tab when USB device is connected. QFormLayout::AllNonFixedFieldsGrow Username true phablet IP 127.0.0.1 ./src/ubuntu/ubuntuconstants.h0000644000015600001650000005577212705421114016653 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUCONSTANTS2_H #define UBUNTUCONSTANTS2_H #include #include namespace Ubuntu { namespace Constants { const char LINEFEED[] = "\n"; const char UNDERLINE[] = "_"; const char DASH[] = "-"; const char EMPTY[] = ""; const char SPACE[] = " "; const char TAB[] = " "; const char INSTALLED[] = "ii"; const char ZERO_STR[] = "0"; const char ONE_STR[] = "1"; const char USERNAME[] = "username"; const char UBUNTU_MIMETYPE_XML[] = ":/ubuntu/UbuntuProject.mimetypes.xml"; const char UBUNTUDEVICESWIDGET_ONERROR[] = "

%0

"; const char UBUNTUDEVICESWIDGET_ACTION_BEGIN[] = "

%0

"; const char UBUNTUDEVICESWIDGET_ACTION_END[] = "

%0

"; const char UBUNTUDEVICESWIDGET_ONFINISHED_LOCAL_NO_EMULATOR_INSTALLED[] = "The package is not installed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_ADB_REGEX[] = "(\\S+)\\s+(.*)"; const char UBUNTUDEVICESWIDGET_ONFINISHED_ADB_NOACCESS[] = "???"; const char UBUNTUDEVICESWIDGET_ONFINISHED_SSH_WAS_STARTED[] = "..openssh-server was started."; const char UBUNTUDEVICESWIDGET_ONFINISHED_NONE[] = "(none)"; const char UBUNTUDEVICESWIDGET_ONFINISHED_SSH_IS_INSTALLED[] = "..openssh-server (%0) is installed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_SSH_NOT_INSTALLED[] = "..openssh-server was not installed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_UNABLE_TO_FETCH[] = "E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?"; const char UBUNTUWIDGETS_ONFINISHED_SCRIPT_LOCAL_PACKAGE_INSTALLED[] = "%0/local_package_installed"; const char UBUNTUDEVICESWIDGET_ONFINISHED_SCRIPT_LOCAL_INSTALL_EMULATOR[] = "%0/local_install_emulator"; const char UBUNTUDEVICESWIDGET_ONFINISHED_WRITABLE_ENABLED[] = "..writable image has been enabled, device is rebooting."; const char UBUNTUDEVICESWIDGET_ONFINISHED_WRITABLE_DISABLED[] = "..writable image has been disabled, device is rebooting."; const char UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_REMOVED[] = "..developer tools have been removed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_NOT_INSTALLED[] = "..developer tools are not installed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_INSTALLED[] = "..developer tools are already installed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_WRITABLEIMAGE[] = "..writable image."; const char UBUNTUDEVICESWIDGET_ONFINISHED_READONLYIMAGE[] = "..read-only image."; const char UBUNTUDEVICESWIDGET_ONFINISHED_DEVELOPERTOOLS_WAS_INSTALLED[] = "..platform development was enabled."; const char UBUNTUDEVICESWIDGET_ONFINISHED_SSH_WAS_REMOVED[] = "..openssh-server was removed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_SCRIPT_SSH_INSTALL[] = "%0/openssh_install"; const char UBUNTUDEVICESWIDGET_ONFINISHED_SSH_WAS_INSTALLED[] = "..openssh-server was installed."; const char UBUNTUDEVICESWIDGET_ONFINISHED_SCRIPT_PORTFORWARD[] = "%0/device_portforward"; const char UBUNTUDEVICESWIDGET_ONFINISHED_PORTS_FORWARDED[] = "..ports forwarded."; const char UBUNTUDEVICESWIDGET_ONFINISHED_PUBLICKEY_AUTH_SET[] = "..public key authentication is now set."; const char UBUNTUDEVICESWIDGET_ONFINISHED_NETWORK_CONF_COPIED[] = "..network configuration copied."; const char EMULATOR_PACKAGE_NAME[] = "ubuntu-emulator"; const char DEFAULT_EMULATOR_PATH[] = "ubuntu-emulator"; const char REVIEWER_PACKAGE_NAME[] = "click-reviewers-tools"; const char UBUNTUDEVICESWIDGET_LOCAL_EMULATOR_INSTALLED[] = "Checking installed emulator package."; const char UBUNTUDEVICESWIDGET_INSTALL_EMULATOR_PACKAGE[] = "Install the emulator package on the system.."; const char UBUNTUDEVICESWIDGET_INSTALL_EMULATOR_PACKAGE_SCRIPT[] = "%0/local_install_emulator %1"; const char UBUNTUDEVICESWIDGET_LOCAL_SEARCH_IMAGES_SCRIPT[] = "%0/local_search_images"; const char UBUNTUDEVICESWIDGET_LOCAL_SEARCH_IMAGES[] = "Search configured emulator instances."; const char UBUNTUDEVICESWIDGET_LOCAL_CREATE_EMULATOR[] = "Creating new emulator instance."; const char UBUNTUDEVICESWIDGET_LOCAL_CREATE_EMULATOR_SCRIPT[] = "%0 %1/local_create_emulator %2 %3 %4 %5 %6 %7 %8"; const char UBUNTUDEVICESWIDGET_LOCAL_START_EMULATOR[] = "Starting the selected emulator."; const char UBUNTUDEVICESWIDGET_LOCAL_START_EMULATOR_SCRIPT[] = "%0/local_start_emulator"; const char UBUNTUDEVICESWIDGET_LOCAL_STOP_EMULATOR_SCRIPT[] = "%0/local_stop_emulator"; const char UBUNTUDEVICESWIDGET_LOCAL_DELETE_EMULATOR_SCRIPT[] = "%0/local_delete_emulator"; const char UBUNTUDEVICESWIDGET_STARTSSHSERVICE[] = "Start ssh service on device.."; const char UBUNTUDEVICESWIDGET_STARTSSHSERVICE_SCRIPT[] = "%0/device_service_ssh_start %1"; const char UBUNTUDEVICESWIDGET_MAKEFSWRITABLE[] = "Make filesystem writable.."; const char UBUNTUDEVICESWIDGET_MAKEFSWRITABLE_SCRIPT[] = "%0/device_writableimage_set %1"; const char UBUNTUDEVICESWIDGET_MAKEFSREADONLY[] = "Make filesystem read-only.."; const char UBUNTUDEVICESWIDGET_MAKEFSREADONLY_SCRIPT[] = "%0/device_writableimage_unset %1"; const char UBUNTUDEVICESWIDGET_DETECTDEVELOPERTOOLS[] = "Are developer tools installed.."; const char UBUNTUDEVICESWIDGET_DETECTDEVELOPERTOOLS_SCRIPT[] = "%0/device_developertools_has %1"; const char UBUNTUDEVICESWIDGET_DETECTWRITABLEIMAGE[] = "Is device image read-only or writable.."; const char UBUNTUDEVICESWIDGET_DETECTWRITABLEIMAGE_SCRIPT[] = "%0/device_writableimage_has %1"; const char UBUNTUDEVICESWIDGET_DISABLEPLATFORMDEVELOPMENT[] = "Disable Platform Development.."; const char UBUNTUDEVICESWIDGET_DISABLEPLATFORMDEVELOPMENT_SCRIPT[] = "%0/device_developertools_remove %1"; const char UBUNTUDEVICESWIDGET_ENABLEPLATFORMDEVELOPMENT[] = "Enable Platform Development.."; const char UBUNTUDEVICESWIDGET_ENABLEPLATFORMDEVELOPMENT_SCRIPT[] = "%0/device_developertools_install %1"; const char UBUNTUPROJECT_MIMETYPE[] = "application/x-ubuntuproject"; const char UBUNTUPROJECT_ID[] = "UbuntuProjectManager.UbuntuProject"; const char UBUNTUPROJECT_PROJECTCONTEXT[] = "UbuntuProject.ProjectContext"; const char UBUNTUPROJECT_SUFFIX[] = ".ubuntuproject"; const char UBUNTUHTMLPROJECT_SUFFIX[] = ".ubuntuhtmlproject"; const char UBUNTUPROJECT_RUNCONTROL_BASE_ID[] = "UbuntuProjectManager.UbuntuRunConfiguration"; const char UBUNTUPROJECT_RUNCONTROL_SCOPE_ID[] = "UbuntuProjectManager.UbuntuRunConfiguration.Scope"; const char UBUNTUPROJECT_RUNCONTROL_APP_ID[] = "UbuntuProjectManager.UbuntuRunConfiguration.App"; const char UBUNTUPROJECT_REMOTE_RUNCONTROL_BASE_ID[] = "UbuntuProjectManager.RemoteRunConfiguration"; const char UBUNTUPROJECT_REMOTE_RUNCONTROL_SCOPE_ID[] = "UbuntuProjectManager.RemoteRunConfiguration.Scope"; const char UBUNTUPROJECT_REMOTE_RUNCONTROL_APP_ID[] = "UbuntuProjectManager.RemoteRunConfiguration.App"; const char UBUNTUHTML_PROJECT_LAUNCHER_EXE[] = "ubuntu-html5-app-launcher"; const char UBUNTUWEBAPP_PROJECT_LAUNCHER_EXE[] = "webapp-container"; const char UBUNTUSCOPES_PROJECT_LAUNCHER_EXE[] = "qtc_desktop_scoperunner.py"; const char UBUNTUBZR_INITIALIZE[] ="%0/qtc_bzr_info"; const char UBUNTUPACKAGINGWIDGET_MENU_REMOVE[] ="Remove"; const char UBUNTUPACKAGINGWIDGET_BUILDPACKAGE_ID[] ="Ubuntu.Build.Package"; const char UBUNTUPACKAGINGWIDGET_DEFAULT_MANIFEST[] =":/ubuntu/manifest.json.template"; const char UBUNTUPACKAGINGWIDGET_DEFAULT_MYAPP[] =":/ubuntu/myapp.json.template"; const char UBUNTUPACKAGINGWIDGET_DEFAULT_NAME[] ="%0.%1"; const char UBUNTUPACKAGINGWIDGET_LOCAL_REVIEWER_INSTALLED[] = "Checking installed click reviewer tools package."; const char UBUNTUPACKAGINGWIDGET_CLICK_REVIEWER_TOOLS_AGAINST_PACKAGE[] = "Click Reviewers tools against %0"; const char UBUNTUPACKAGINGWIDGET_CLICK_DEPLOY_SCRIPT[] = "%1/qtc_project_click_deploy %2 %3 %4 %5 %6 %7"; const char UBUNTUPACKAGINGWIDGET_CLICK_DEPLOY_MESSAGE[] = "Installing click package on device"; const char UBUNTU_CLICK_PACKAGE_MASK[] = "*.click"; const char UBUNTU_CLICK_PACKAGE_SELECTOR_TEXT[] = "Select click package which you want to test"; const char QMAKE_MIMETYPE[] = "application/vnd.qt.qmakeprofile"; const char QMLPROJECT_MIMETYPE[] = "application/x-qmlproject"; const char UBUNTUMENU_ONFINISHED[] = "%0 finished with code %1"; const char UBUNTUMENU_ONERROR[] = "%0"; const char UBUNTUMENU_ONSTARTED[] = "Started %0"; const char ERROR_MSG_NOACTIONS[] = "No actions defined in map"; const char ERROR_MSG_NO_MENU_DEFINED[] = "No menu defined"; const char ERROR_MSG_COULD_NOT_CAST_TO_ACTION[] = "Could not cast to action"; const char ERROR_MSG_UNABLE_TO_PARSE_MENUJSON[] = "Unable to parse menu.json"; const char UBUNTUWIDGETS_LOCAL_PACKAGE_INSTALLED_SCRIPT[] = "%0/local_package_installed %1"; const char UBUNTUDEVICESWIDGET_DETECTOPENSSH[] = "Detecting if openssh-server is installed.."; const char UBUNTUDEVICESWIDGET_DETECTOPENSSH_SCRIPT[] = "%0/openssh_version %1"; const char UBUNTUDEVICESWIDGET_DETECTDEVICES[] = "Detecting device.."; const char UBUNTUDEVICESWIDGET_DETECTDEVICES_SCRIPT[] = "%1/device_search %2"; const char UBUNTUDEVICESWIDGET_SSHCONNECT_SCRIPT[] = "%0/openssh_connect"; const char UBUNTUDEVICESWIDGET_CLONENETWORK[] = "Clone network configuration from host to device.."; const char UBUNTUDEVICESWIDGET_CLONENETWORK_SCRIPT[] = "%0/device_network_clone %1"; const char UBUNTUDEVICESWIDGET_PORTFORWARD[] = "Enabling port forward.."; const char UBUNTUDEVICESWIDGET_PORTFORWARD_SCRIPT[] = "%0/device_portforward"; const char UBUNTUDEVICESWIDGET_SETUP_PUBKEY_AUTH[] = "Setting up public key authentication.."; const char UBUNTUDEVICESWIDGET_SETUP_PUBKEY_AUTH_SCRIPT[] = "%0/openssh_publickey %1 %2"; const char UBUNTUDEVICESWIDGET_HASNETWORK[] = "Check if the device is connected to a network.."; const char UBUNTUDEVICESWIDGET_HASNETWORK_SCRIPT[] = "%0/device_hasnetwork %1"; const char UBUNTUDEVICESWIDGET_DETECTDEVICEVERSION[] = "Check device image version.."; const char UBUNTUDEVICESWIDGET_WAIT_FOR_BOOT_MESSAGE[] = "Waiting for device to come up.."; const char UBUNTUDEVICESWIDGET_WAIT_FOR_BOOT_SCRIPT[] = "%0/device_wait_for_shell %1"; const char UBUNTUDEVICESWIDGET_WAIT_FOR_EMULATOR_MESSAGE[] = "Waiting for emulator tool to come up.."; const char UBUNTUDEVICESWIDGET_WAIT_FOR_EMULATOR_SCRIPT[] = "%1/local_wait_for_emulator %2"; const char UBUNTUDEVICESWIDGET_DETECTDEVICEVERSION_SCRIPT[] = "%0/device_version %1"; const char UBUNTUDEVICESWIDGET_SSH_INSTALL[] = "Installing openssh-server.."; const char UBUNTUDEVICESWIDGET_SSH_INSTALL_SCRIPT[] = "%0/openssh_install %1"; const char UBUNTUDEVICESWIDGET_SSH_REMOVE[] = "Removing openssh-server.."; const char UBUNTUDEVICESWIDGET_SSH_REMOVE_SCRIPT[] = "%0/openssh_remove %1"; #ifdef UBUNTU_BUILD_LOCAL const QString UBUNTU_RESOURCE_PATH = QLatin1String(UBUNTU_RESOURCE_PATH_LOCAL); #else const QString UBUNTU_RESOURCE_PATH = Core::ICore::resourcePath(); #endif const QString UBUNTU_WELCOMESCREEN_QML = UBUNTU_RESOURCE_PATH + QLatin1String("/ubuntu/qml/welcome.qml"); const QString UBUNTU_DEVICESCREEN_QML = UBUNTU_RESOURCE_PATH + QLatin1String("/ubuntu/qml/devicespage.qml"); const QString UBUNTU_PUBLISHSCREEN_QML = UBUNTU_RESOURCE_PATH + QLatin1String("/ubuntu/qml/publishpage.qml"); const QString UBUNTU_DEVICESCREEN_ROOT = UBUNTU_RESOURCE_PATH + QLatin1String("/ubuntu/qml"); const QString UBUNTU_MENUPATH = UBUNTU_RESOURCE_PATH + QLatin1String("/ubuntu/"); const QString UBUNTU_SHAREPATH = UBUNTU_RESOURCE_PATH + QLatin1String("/ubuntu/"); const QString UBUNTU_SCRIPTPATH = UBUNTU_RESOURCE_PATH + QLatin1String("/ubuntu/scripts"); const char UBUNTU_MENUJSON[] = "menu.json"; const char UBUNTU_MENUJSON_NAME[] = "name"; const char UBUNTU_MENUJSON_ID[] = "id"; const char UBUNTU_MENUJSON_ACTIONS[] = "actions"; const char UBUNTU_MENUJSON_SUBMENU[] = "submenu"; const char UBUNTU_MENUJSON_KEYSEQUENCE[] = "keysequence"; const char UBUNTU_MENUJSON_QUERYDIALOG[] = "queryDialog"; const char UBUNTU_MENUJSON_TITLE[] = "title"; const char UBUNTU_MENUJSON_MESSAGE[] = "message"; const char UBUNTU_MENUJSON_VALUE[] = "value"; const char UBUNTU_MENUJSON_PARENT[] = "parent"; const char UBUNTU_MENUJSON_GROUP[] = "group"; const char UBUNTU_MENUJSON_WORKINGDIRECTORY[] = "workingDirectory"; const char UBUNTU_MENUJSON_PROJECTREQUIRED[] = "projectRequired"; const char UBUNTU_MENUJSON_DEVICEREQUIRED[] = "deviceRequired"; const char UBUNTU_MENUJSON_QMLPROJECTREQUIRED[] = "qmlProjectRequired"; const char UBUNTU_MENUJSON_QMAKEPROJECTREQUIRED[] = "qmakeProjectRequired"; const char UBUNTU_MENUJSON_UBUNTUPROJECTREQUIRED[] = "ubuntuProjectRequired"; const char UBUNTU_MENUJSON_UBUNTUHTMLPROJECTREQUIRED[] = "ubuntuHtmlProjectRequired"; const char UBUNTU_MENUJSON_CLICKTARGETREQUIRED[] = "needsClickTarget"; //will ask the user to choose a click target const char UBUNTU_MENUJSON_CLICKTOOLCHAINREQUIRED[] = "needsClickToolchain"; //requires a click toolchain const char UBUNTU_MENUJSON_SAVEREQUIRED[] = "saveRequired"; const char UBUNTU_MENUJSON_MESSAGEDIALOG[] = "messageDialog"; const char UBUNTU_MENUJSON_METACALL[] = "metacall"; const char UBUNTU_MENUJSON_METACALL_ARGS[] = "args"; const char UBUNTU_MENUJSON_METACALL_METHOD[] = "method"; const char UBUNTU_MENUJSON_CONTEXT[] = "context"; const char UBUNTU_MENUJSON_PARENT_TOOLS[] = "Tools"; const char UBUNTU_MENUJSON_PARENT_WINDOW[] = "Window"; const char UBUNTU_MENUJSON_PARENT_HELP[] = "Help"; const char UBUNTU_MENUJSON_PARENT_BUILD[] = "Build"; const char UBUNTU_MENUJSON_PARENT_FILE[] = "File"; const char UBUNTU_MENUJSON_PARENT_EDIT[] = "Edit"; const char UBUNTU_MENUJSON_PARENT_TOP[] = "TOP"; const char UBUNTU_DIALOG_NO_PROJECTOPEN_TITLE[] = "No project open"; const char UBUNTU_DIALOG_NO_PROJECTOPEN_TEXT[] = "Open a project or create a new one."; const char UBUNTU_ACTION_FOLDERNAME[] = "%FOLDERNAME%"; const char UBUNTU_ACTION_PROJECTDIRECTORY[] = "%PROJECTDIRECTORY%"; const char UBUNTU_ACTION_DISPLAYNAME[] = "%DISPLAYNAME%"; const char UBUNTU_ACTION_PROJECTFILES[] = "%PROJECTFILES%"; const char UBUNTU_ACTION_NAME_FROM_MANIFEST[] = "%NAME_FROM_MANIFEST%"; const char UBUNTU_ACTION_SCRIPTDIRECTORY[] = "%SCRIPTDIRECTORY%"; const char UBUNTU_ACTION_SHAREDIRECTORY[] = "%SHAREDIRECTORY%"; const char UBUNTU_ACTION_SERIALNUMBER[] = "%SERIALNUMBER%"; const char UBUNTU_ACTION_DEVICE_USERNAME[] = "%USERNAME%"; const char UBUNTU_ACTION_DEVICE_IP[] = "%IP%"; const char UBUNTU_ACTION_DEVICE_PORT[] = "%PORT%"; const char UBUNTU_ACTION_APP_RUNNER_EXECNAME[] = "%APPRUNNEREXECNAME%"; const char UBUNTU_ACTION_CLICK_ARCH[] = "%CLICK_ARCH%"; const char UBUNTU_ACTION_CLICK_FRAMEWORK[] = "%CLICK_FRAMEWORK%"; const char UBUNTU_ACTION_CLICK_SERIES[] = "%CLICK_SERIES%"; const char UBUNTU_ACTION_CLICK_PACKAGING_FOLDER[] = "%CLICK_PACKAGING_FOLDER%"; const char UBUNTU_MODE_PACKAGING[] = "UbuntuPackaging"; const char UBUNTU_MODE_PACKAGING_DISPLAYNAME[] = "Publish"; const char UBUNTU_MODE_PACKAGING_ICON[] = ":/ubuntu/images/packaging.png"; const int UBUNTU_MODE_PACKAGING_PRIORITY = 80; const char UBUNTU_MODE_DEVICES[] = "UbuntuDevices"; const char UBUNTU_MODE_DEVICES_DISPLAYNAME[] = "Devices"; const char UBUNTU_MODE_DEVICES_ICON[] = ":/ubuntu/images/device.png"; const int UBUNTU_MODE_DEVICES_PRIORITY = 11; const char UBUNTU_ICON[] = ":/ubuntu/images/ubuntu-32.png"; const char FEATURE_UNITY_SCOPE[] = "Ubuntu.Wizards.FeatureUnityScope"; const char FEATURE_UBUNTU_PRECISE[] = "Ubuntu.Wizards.FeatureUbuntuPrecise"; const char FEATURE_UBUNTU_QUANTAL[] = "Ubuntu.Wizards.FeatureUbuntuQuantal"; const char FEATURE_UBUNTU_RARING[] = "Ubuntu.Wizards.FeatureUbuntuRaring"; const char FEATURE_UBUNTU_SAUCY[] = "Ubuntu.Wizards.FeatureUbuntuSaucy"; const char FEATURE_UBUNTU_TRUSTY[] = "Ubuntu.Wizards.FeatureUbuntuTrusty"; const char FEATURE_UBUNTU_UTOPIC[] = "Ubuntu.Wizards.FeatureUbuntuUtopic"; const char DISTRIB_ID[] = "DISTRIB_ID="; const char DISTRIB_CODENAME[] = "DISTRIB_CODENAME="; const char DISTRIB_RELEASE[] = "DISTRIB_RELEASE="; const char DISTRIB_DESCRIPTION[] = "DISTRIB_DESCRIPTION="; const char LSB_RELEASE[] = "/etc/lsb-release"; const char PRECISE[] = "precise"; const char QUANTAL[] = "quantal"; const char RARING[] = "raring"; const char SAUCY[] = "saucy"; const char TRUSTY[] = "trusty"; const char UTOPIC[] = "utopic"; const char PLATFORM_DESKTOP[] = "Desktop"; const char PLATFORM_DESKTOP_DISPLAYNAME[] = "Ubuntu %0"; const char TASK_DEVICE_SCRIPT[] = "Ubuntu.Task.DeviceScript"; const char UBUNTU_SETTINGS_ICON[] = ":/ubuntu/images/ubuntu-32.png"; //review tools const char CLICK_REVIEWERSTOOLS_BINARY[] = "/usr/bin/click-review"; const char CLICK_REVIEWERSTOOLS_ARGS[] = "--sdk \"%0\""; const char CLICK_REVIEWERSTOOLS_LOCATION[] = "/usr/bin/click-review --sdk \"%0\""; //build configuration const char UBUNTU_CLICK_OPEN_TERMINAL_ERROR[] = "Error when starting terminal"; const char UBUNTU_CLICK_TARGETS_REGEX[] = "^%1-(.*)-([A-Za-z0-9]+)$"; const char UBUNTU_CLICK_TARGETS_FRAMEWORK_REGEX[] = "^%1-(%2)-([A-Za-z0-9]+)$"; const char UBUNTU_CLICK_BASE_FRAMEWORK_REGEX[] = "(ubuntu-(.*)-[\\d]{1,2}\\.[\\d]{1,2}(\\.[\\d]+)?)"; const char UBUNTU_CLICK_VERSION_REGEX[] = "^DISTRIB_RELEASE=([0-9]+)\\.([0-9]+)$"; const char UBUNTU_CLICK_SERIES_REGEX[] = "^DISTRIB_CODENAME=([A-Za-z]+)$"; const char UBUNTU_CLICK_CHROOT_SUFFIX_ENV_VAR[] = "CLICK_CHROOT_SUFFIX"; const char UBUNTU_CLICK_CHROOT_DEFAULT_NAME[] = "click"; const char UBUNTU_CLICK_BINARY[] = "/usr/bin/click"; const char UBUNTU_SUDO_BINARY[] = "/usr/bin/pkexec"; const char UBUNTU_CLICK_CHROOT_BASEPATH[] = "/var/lib/schroot/chroots"; const char UBUNTU_CLICK_CLICK_PACKAGE_DIR[] = "%CLICK_FRAMEWORK%-%CLICK_ARCH%/click_package"; const char UBUNTU_CLICK_CHROOT_CREATE_ARGS[] = "%0/click_create_target %1 %2 %3 %4"; const char UBUNTU_CLICK_CHROOT_DESTROY_ARGS[] = "%0/click_destroy_target %1 %2 %3 %4"; const char UBUNTU_CLICK_CHROOT_UPGRADE_ARGS[] = "chroot -a %0 -f %1 -s %2 -n %3 upgrade"; const char UBUNTU_CLICK_OPEN_TERMINAL[] = "click chroot -a %0 -f %1 -s %2 -n %3 maint /bin/bash"; const char UBUNTU_CLICK_DELETE_TITLE[] = "Delete click chroot"; const char UBUNTU_CLICK_DELETE_MESSAGE[] = "Are you sure you want to delete this chroot?"; const char UBUNTU_CLICK_STOP_TITLE[] = "Stop click tool"; const char UBUNTU_CLICK_STOP_MESSAGE[] = "Are you sure you want to stop click? This could break your chroot!"; const char UBUNTU_CLICK_STOP_WAIT_MESSAGE[] = "Waiting for click to stop"; const char UBUNTU_CLICK_ERROR_EXIT_MESSAGE[] = "Click exited with errors, please check the output"; const char UBUNTU_CLICK_SUCCESS_EXIT_MESSAGE[] = "Click exited with no errors"; const char UBUNTU_CLICK_NOTARGETS_TITLE[] = "No click build targets available"; const char UBUNTU_CLICK_NOTARGETS_MESSAGE[] = "There are no click build targets available.\nPlease create a target in the Ubuntu option page."; const char UBUNTU_CLICK_NOTARGETS_FRAMEWORK_MESSAGE[] = "There are no click build targets for framework %1 available.\nPlease create a target in the Ubuntu option page."; const char UBUNTU_CLICK_SELECT_TARGET_TITLE[] = "Select build target"; const char UBUNTU_CLICK_SELECT_TARGET_LABEL[] = "Build target"; extern const char* UBUNTU_CLICK_SUPPORTED_ARCHS[]; extern const char* UBUNTU_CLICK_SUPPORTED_TARGETS[][3]; //Buildsupport const char UBUNTU_CLICK_TOOLCHAIN_ID[] = "UbuntuProjectManager.UbuntuGccToolChain"; const char UBUNTU_CLICK_CHROOT_WRAPPER[] = "%0/qtc_chroot_wrapper.py"; const char UBUNTU_CLICK_HTML_BC_ID[] = "UbuntuProjectManager.UbuntuHTML5.BuildConfiguration"; const char UBUNTU_CLICK_QML_BC_ID[] = "UbuntuProjectManager.UbuntuQml.BuildConfiguration"; const char UBUNTU_CLICK_QML_UPDATE_TRANSL_MAKESTEP[] = "UbuntuProjectManager.UbuntuQml.UpdateTranslationTemplateMakeStep"; const char UBUNTU_CLICK_QML_BUILD_TRANSL_MAKESTEP[] = "UbuntuProjectManager.UbuntuQml.BuildTranslationMakeStep"; const char UBUNTU_CLICK_QML_BUILD_TRANSL_DIR[] = "mo"; //Devicesupport const char UBUNTU_DEVICE_TYPE_ID[] = "UbuntuProjectManager.DeviceTypeId"; const char UBUNTU_DEVICE_SSHIDENTITY[] = "ubuntudevice_id_rsa"; //Deploysupport const char UBUNTU_DEPLOYCONFIGURATION_ID[] = "UbuntuProjectManager.DeployConfiguration"; const char UBUNTU_LOCAL_DEPLOYCONFIGURATION_ID[] = "UbuntuProjectManager.LocalDeployConfiguration"; const char UBUNTU_DEPLOY_UPLOADSTEP_ID[] = "UbuntuProjectManager.UploadStep"; const char UBUNTU_DEPLOY_MAKESTEP_ID[] = "UbuntuProjectManager.UbuntuCMake.DeployMakeStep"; const char UBUNTU_CLICK_PACKAGESTEP_ID[] = "UbuntuProjectManager.ClickPackageStep"; const char UBUNTU_DEPLOY_DESTDIR[] = ".ubuntu-sdk-deploy"; const char UBUNTU_CLICK_SUCCESS_PACKAGE_REGEX[] = "^.*'(.*)'.$"; //Frameworks const char UBUNTU_DEFAULT_QML_FRAMEWORK[] = "ubuntu-sdk-14.10-qml"; const char UBUNTU_DEFAULT_HTML_FRAMEWORK[] = "ubuntu-sdk-14.10-html"; const char UBUNTU_UNKNOWN_FRAMEWORK_NAME[] = "Unknown framework"; const int UBUNTU_UNKNOWN_FRAMEWORK_DATA = 0xdeadbeef; /* * GOLANG constants, copied to remove the need to * depend on the golang plugin. Remove as soon * as there is a released version of it */ const char GO_PROJECT_MIMETYPE[] = "application/x-goproject"; const char GO_PROJECT_ID[] = "GoProjectManager.GoProject"; const char GO_PROJECT_PROJECTCONTEXT[] = "GoProject.ProjectContext"; const char GO_PROJECT_SUFFIX[] = ".goproject"; const char LANG_GO[] = "GOLANG"; const char GO_TOOLCHAIN_ID[] = "GoLang.Toolchain"; const char TOOLCHAIN_SETTINGS_PAGE_ID[] = "GoLang.SettingsPage"; const char GO_BUILDCONFIGURATION_ID[] = "GoLang.Buildconfiguration"; const char GO_GOSTEP_ID[] = "GoLang.BuildConfiguration.GoStep"; const char GO_RUNCONFIG_ID[] = "GoLang.GoRunConfiguration"; const char GO_SUPPORT_FEATURE[] = "GoLang.GoSupport"; const char UBUNTU_GO_BUILD_TARGETS[] = "%GOBUILDTARGETS%"; //Qtversion support const char UBUNTU_QTVERSION_TYPE[] = "UbuntuProjectManager.QtVersion"; const char UBUNTU_PLATFORM_NAME[] = "Ubuntu Phone"; const char UBUNTU_PLATFORM_NAME_TR[] = QT_TRANSLATE_NOOP("UbuntuProjectManager", "Ubuntu Phone"); //Manifest Editor const char UBUNTU_MANIFEST_MIME_TYPE[] = "application/vnd.canonical.click.manifest"; const char UBUNTU_APPARMOR_MIME_TYPE[] = "application/vnd.canonical.click.apparmor"; const char UBUNTU_MANIFEST_EDITOR_ID[] = "UbuntuProjectManager.UbuntuManifestEditor.Id"; const char UBUNTU_MANIFEST_EDITOR_CONTEXT[] = "UbuntuProjectManager.UbuntuManifestEditor.Context.Id"; const char UBUNTU_APPARMOR_EDITOR_ID[] = "UbuntuProjectManager.UbuntuApparmorEditor.Id"; const char UBUNTU_APPARMOR_EDITOR_CONTEXT[] = "UbuntuProjectManager.UbuntuApparmorEditor.Context.Id"; //Actions const char UBUNTU_MIGRATE_QMAKE_PROJECT[] = "UbuntuProjectManager.MigrateQMakeProject"; //TargetUpgradeManager const char CHROOT_UPDATE_LIST_SCRIPT[] = "%1/ubuntu/scripts/qtc_chroot_get_upgrades.py %2 %3"; } // namespace Ubuntu } // namespace Constants #endif // UBUNTUCONSTANTS_H ./src/ubuntu/manifest.json.template0000644000015600001650000000053312705421114017517 0ustar jenkinsjenkins{ "name": "myapp.username", "description": "description of myapp", "framework": "myFramework", "architecture": "all", "title": "myapp", "hooks": { "myapp": { "apparmor": "myapp.apparmor", "desktop": "myapp.desktop" } }, "version": "0.1", "maintainer": "maintainerName" } ./src/ubuntu/ubuntucmakecache.h0000644000015600001650000000223412705421114016664 0ustar jenkinsjenkins#ifndef UBUNTUCMAKECACHE_H #define UBUNTUCMAKECACHE_H #include #include #include #include #include #include #include namespace ProjectExplorer { class Project; class Target; class BuildConfiguration; } namespace Ubuntu{ namespace Internal { typedef QMap CMakeCacheValueMap; struct UbuntuCMakeCacheEntry { QFileInfo cacheFile; QDateTime lastRead; CMakeCacheValueMap values; }; typedef QMap CMakeCache; class UbuntuCMakeCache : public QObject { Q_OBJECT public: explicit UbuntuCMakeCache(QObject *parent = 0); static QVariant getValue (const QString &key, ProjectExplorer::BuildConfiguration *bc, const QVariant defaultValue = QVariant()); static UbuntuCMakeCache *instance(); private slots: void onAboutToRemoveProject (ProjectExplorer::Project *p); private: QString normalize (const QString &path) const; void refreshCache (const QString &key, const Utils::FileName &fName); private: CMakeCache m_map; static UbuntuCMakeCache *m_instance; }; }} #endif // UBUNTUCMAKECACHE_H ./src/ubuntu/ubuntumanifesteditorwidget.h0000644000015600001650000000425612705421114021047 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUMANIFESTEDITORWIDGET_H #define UBUNTU_INTERNAL_UBUNTUMANIFESTEDITORWIDGET_H #include "ubuntuclickmanifest.h" #include #include #include "ubuntuabstractguieditorwidget.h" #include "ui_ubuntumanifesteditor.h" class QStackedWidget; namespace Ubuntu { namespace Internal { class UbuntuAbstractGuiEditor; class UbuntuManifestEditorWidget; class UbuntuClickManifest; class UbuntuManifestEditorWidget : public UbuntuAbstractGuiEditorWidget { Q_OBJECT public: explicit UbuntuManifestEditorWidget(); ~UbuntuManifestEditorWidget(); static QString createPackageName (const QString &userName, const QString &projectName); virtual void saved() override; protected: virtual void updateAfterFileLoad() override; virtual void aboutToOpen(const QString &fileName, const QString &realFileName) override; bool syncToWidgets (); bool syncToWidgets (UbuntuClickManifest *source); void syncToSource (); QWidget *createMainWidget(); void addMissingFieldsToManifest(QString fileName); protected slots: void bzrChanged (); void onFrameworkChanged (); void updateFrameworkList (); private: QWidget *createHookWidget (const UbuntuClickManifest::Hook &hook); void selectFramework(const QString &fw); private: Ui::UbuntuManifestEditor *m_ui; QSharedPointer m_manifest; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUMANIFESTEDITORWIDGET_H ./src/ubuntu/ubuntuplugin.h0000644000015600001650000000407012705421114016116 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTU_H #define UBUNTU_H #include "ubuntu_global.h" #include "ubuntuwelcomemode.h" #include "ubuntudevicemode.h" #include "ubuntumenu.h" #include "ubuntuprojectmanager.h" #include "ubuntufeatureprovider.h" #include "ubuntuversionmanager.h" #include "ubuntupackagingmode.h" #include "ubuntusettingsdeviceconnectivitypage.h" #include "ubuntusettingsclickpage.h" #include "settings.h" #include namespace Ubuntu { namespace Internal { class UBUNTUSHARED_EXPORT UbuntuPlugin: public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Ubuntu.json") public: UbuntuPlugin(); ~UbuntuPlugin(); virtual bool initialize(const QStringList &arguments, QString *errorString) override; virtual void extensionsInitialized() override; private slots: void onKitsLoaded (); void showFirstStartWizard (); void updateContextMenu(ProjectExplorer::Project *project,ProjectExplorer::Node *node); void migrateProject (); protected: UbuntuDeviceMode *m_ubuntuDeviceMode; UbuntuMenu *m_ubuntuMenu; UbuntuPackagingMode *m_ubuntuPackagingMode; QAction *m_migrateProjectAction; Settings m_settings; ProjectExplorer::Project *m_currentContextMenuProject; }; } // Internal } // Ubuntu #endif // UBUNTU_H ./src/ubuntu/images/0000755000015600001650000000000012705421114014450 5ustar jenkinsjenkins./src/ubuntu/images/view-refresh.png0000644000015600001650000000371512705421114017572 0ustar jenkinsjenkinsPNG  IHDR szzsBIT|dIDATXWklTf>]/yCy,!?@ )ZԤ7U͏Jm6*J# 6 TyjBDPBG /o{_]ݘ?i4w9wΞnl&FhYT8qq\s0 ve&<;mJZtk:Rk@(Yaeuz^5GJ񤁁Ѥ{HpE8W/dfpӾq6-uMo?X_$ .3$6:٧voܧ(i kn|W@ Y"$$H$e*+ \ij5I&MuN\F!|?Tp6? yDE5:ە [Lۅ Qkk??ݴ ~& ڴ~|}^Utлvx3FDY")8Tpʐ2]Md: `ǖUaCUJ.o^poHsӲ~v}=GC׺cȚlER>vv!~EQ81᨟ǁѩjfSa˟K+yVSF&biR򑯽'_|7Z^Sb.~2(>UQ!0u k}yâ3NyTTW@-V3Y8Lrry<=8 "o&qa[ 6i 8ck@:ӯ2!ol|bFijNЫUp@qe_cdJ"p0ҋhKNXNKUeꥏ#mG8Qsʄɔ@3枾سQ:Bfd,dC 3p *$ DTl" +->AEyqo`"c߽⎖ n܊Co{+2[6Uc6 B0A1*$ L K1hpI* $)X1iԣt򗎉5z?YtfHو,r$L[BY5Bfur] F3F|`WgY$Q_ׇSc7/6tw>13?O>[ WWr߅(If+6#hWsgr8,$+\Ev$3O^}mo[]W=d<ʃ~YY}i* a!Hh}^D\ʪoYYpE^VU cANJ4bC]#N sQ uYՏ(L']Э`]-Nݜc'0,rvBRFnb‹"G[ /&VDX ^NE8O`SdD!vx5l IL =ˆvIENDB`./src/ubuntu/images/device.png0000644000015600001650000000323012705421114016413 0ustar jenkinsjenkinsPNG  IHDRr ߔsRGBbKGD pHYs  tIME $>{IDATHǕs\W}45r4dE& (X@%kȆ *ņRC6qd+RdI#YҌF3Y7LL̫OO?ggl|Kܔ^QM_%|`ZOydp9"!R |EUg8JQin|B˭kqBQRD=R|LSDr'8xW 6]&7C]0 1%wX]]p;H˯ԊiGGq] sk_DiA@R$bdc^w7Ҷ5"#[`s \CrEފU#d2( ̵;m0׀vKk E,掺L;KY֌.skvk ܨ`N`joedY@3yc\%'ܬ#a`1 A(ks( zӥ֝I`n[c1NiaHQEzL&'CuoާY7#+ ))EO^2\}IDMg<EYbu#fՔoIK+&cғm%X`VEA,t."HW¹!{\ "TH֧lA$! $'@A S" ! +Y("H@JKjJUG Idsgc64IRI#ֱXP(4.TԠrgNeN@)FtxGؓ5wn}w%K4mNFYq$錔xf73Ho3(+Z37bt E'XO.OYX ;]Z '|̔{kUC_ +F5RB}ʂ}ލvmy)3SY/ {'Q?X٣'o11!0~}έ%;SZFK\EX32 >Be&ؐe@D8S+k:A-?mD { T-[V;~}'ˈ ZF !0PߦwlѩGa}DFȿBA?cC @ϙp}:Sv#glUqEoψX6$.ޫOJhij>CYR+k=&El' yRe%V/;gPD|oɠLfɖӷwNA/F?'hrɬQrxԪ45z <\"XU5PA g6hY+1nxĹ1hvOoi埋GI`C&nW}]L:[8ӌ~V}y5͓bFN#]vN3 |q  t}S)j:;>1s½6]}FVQڢ~)1Sk& pt0ZCt8:H">`e(f/w3\.b.IfYS5Pf.({U)S @`ӶK/Qgpւ3xsb(]Yw_5W6`P>h#?hxaJܭ>CO֚ ]01u$137EEY 0'$oXF#+A5BݜyR|GXZ9{@=K"߂ B9#OOBbZf'r5oԡaI-Aӧcx{1pCH|F0~"]%@B>{5Cr F ܜ-tGR^JE_@$aKX4{I)50T$SGqq,A'֖)nY=@_ g=Nu)^1ӝ?ghĨiFM~j;gA3w/\BVaAy蛪$5p^?-&VhmLMSʛ4 `0a9G~qm`v gW _iJ_gd}2\;soWQQYVHtYpz= ` 7b)Ƽ*::vt$$.yϋE?GJ1h$Q3@ l|J8yZTLS}ʠg|>/AoX@ jTJgФL| ۷58`<1Q(%&Kz(9ے?uwdNv4-GEgѐ-88`| 틙()۳ub*C|۴RbZ}R8sct FoS !e+xB>'21;K߮scHQűlPo{p[ak@7gz3W~ h7s3G $IENDB`./src/ubuntu/images/ubuntu-48.png0000644000015600001650000000642512705421114016740 0ustar jenkinsjenkinsPNG  IHDR00WbKGD pHYs B(xtIME %, IDAThޭ{pTu?緻z Lx8صl.hǏqZ\28Nnt4i ƱShJ`j B+i+ !iq$Vº3;{>~sι5KKeӌe% Sr~(%"PQT`]ꆨYVL "yXy9?zlo BRTBP}AP@DQqNPE巢( W9&UPׇ ׂU 82+"SQW D%UtcM8@Kp(00X˟Tx,oMMf頵?(g {Ou|y ۢckƺwڞW=":KA'KxNJ %eۢ4m" Knc5*ZN :fjbCAj±+0B$\lEŇ#L1ؽ=D4A-E %TLU=6hDyd;do[x|yu#S=JT%T+pbE[c]Z4kP$ QB#7,v/y04vOו&E-U WN Zi9e@85e9Zi47~ɟ2dmwב< {IxdAh7Q[ e2A>sXpYxaH:zdAHo^UX-{E+l!Q3cqfƋ;ɪrsutN[9u}tÉ\`uFGv,6Vֵ k< IA7)hɟof jIm*rϼu=UkГΟ = ƃlcXQRg{Ϧuj?sEϘTzAή.q~S@&2?&;\q1J-I~(g5#|=qh,֥h7{, z<—E.~?ۅ͡XO:+4 yeUeD"_ Cc}a x7A7_!8< GbFM&33QxIC0Lq%;l^D:7\bmn|XK8ޘ[* 1("uP&okF\/zIr,3V&Tɩ[J_oL^T9HQAwg*G߾]\WT"`?%E 4,;Q딯4B-/8z3iTtznS=nJ I'C|: "}~Var j[뽄x|f)|30N@NU5i3ըjYDil,G:6=KTE+AʯϓHuC5j wj± U*Ɂ0qI۩T8'3 m|i 1>, aXQUE72 image/svg+xml ./src/ubuntu/images/list-add.png0000644000015600001650000000113112705421114016653 0ustar jenkinsjenkinsPNG  IHDR szzsBIT|dIDATX햱kAH$D+AX?B[CF+AA\aai#v"B,LD"hlryMv݋Zx ;70cDU۳z?v{:d֕3vO"ae3No3A d홆9{!K|=Swz"(-g˙Z|w͛ܐOK1ђ<'!${V3K8̈́ N΁u}'l('Aq)m/Ǒp0VLq &o3 s R7~1j>,$jt mPcT&tlV%~~3# jiw] (0[@ 4w$H^wIH|4N rZc'2Se_T0/]X&΍-OV +0JOhC3o? WLvIENDB`./src/ubuntu/images/view-expand.svg0000644000015600001650000001320712705421114017423 0ustar jenkinsjenkins image/svg+xml ./src/ubuntu/images/ubuntu-64.png0000644000015600001650000000624512705421114016736 0ustar jenkinsjenkinsPNG  IHDR@@iqsRGBbKGD pHYs B(xtIME  $ %IDATx՛ixTΝIBH *$AV,b]TZV[BK+Db "H-*" !d/H̝$s3I9y{`cCGa<0dd$ 1!VS2k|(kwDA1FU|G"k7SwT,LH1HZ[1RV9SI6%.dp)Wg($4d ?OH9W24WܓYTspP(hJXcI},}(uNhs8䨢ycD2I%9mYh<M{ yaRk 󐧊 XS޳q309Y6^1zdoPĂh1/]8q\E Ca<+0lrB5+l#l=>mX{0 d\ 3%0)Ł). )5:BnetS1>q|lWRSb,jٵS!CA$&KGq՟̨\>ʇy,p"~3|?H'h`'H5KpR]ǙE ](uL')u\AæK,x9+'P~Sՙm*o\M'M.ϵ}# ߊc oobe=|-1Yf'=e9 , 08{5FiW]E9#>6VV}sxF=wR=FXA-&o^%68r @LΈǒ'ibg\zAI6[pt(!iVgT+o o뎅ԲYktcIjh D^A-y>~qS'L>cs~x-PJPJg$2"C_eݱWw죨C4l,AicN2:OLMPDCYa~ëD:l(\:X,DЫ b1%ۿ+[^DQ@4oߠ_jC:5EtiOm%c~׼͘ p,} _7ۀqo_bRQFxEa-hl#I{Mb;+&K Aűg?ElްU"R1 LN&π.ȏEH)qM뒣_PY7Y>7 LqfG~A޶Έקi}kN@kRA4s^-qSgb1{d?-u8/ٟG4a^tn,CuIhV@zJN=<Ҷo;oGLZΌ5}4ĔDu DB*;CT,y8T[*bBuymNܔh {su}Sh=B8Nu%١>g.K"eRIȹ]wm!B9bdr{o>WGd8sAHaL}Ra)?cuJwr2YYF!];+Ɂ]PIպC5ԟ9*dKeB>aUN8.RЄZZXNNMMړ[dYƞGڪ;3n^Zl1vv2B!5%r-!OA:G27U-$t71–Ǟ1̶@a';[%c}%a1/4<7qau$^#2^7K"%K1fz8;,@ 4m]9-]0e%P9c6"$sy,=zaZd<"֝Q~%%TcZ+I;:A]S~I7ޅm/ p{yE[c]E.Z1Jyǰ-ZG~JB )Fmo۷_݉{fOBAhք8.Ł(͵.ORSGw+IaQD'KU(1Vԅ  Kkd^Z8W/+R" 4^-Xu؛ޤ~ÓHo·Ѝ&[wGjC4YdOŽóiں`melжo1&bn?6k[B&,-nt\WiX\D%5y5/;Iz@SǠ%xiZ{\u[Yh5> A?קM(ܔZIئZֶMпJ=W•}" )ֆ{*fm@lFS@ S? S@ R> P=fP[Fs[O<i'Q=T@ :CI7R>dN v\w\jvK̨Tڳ\biifqZ`2BQ`%/9fOtRNS "1CWm[oIDATAj0z%m0&M@iO% @& U@Q@[HrD}6-&G_a̬Z[rxu0ȵϵr#|?3iIENDB`./src/ubuntu/images/reload.svg0000644000015600001650000001405312705421114016442 0ustar jenkinsjenkins image/svg+xml ./src/ubuntu/images/list-remove.png0000644000015600001650000000047512705421114017432 0ustar jenkinsjenkinsPNG  IHDR szzsBIT|dIDATX!NCA}k8 p pE3 HȈRҤo+7$I$g_>~r }绋ugv.^N@)5Ng= ^HKc/Jj{kT퓦{5Sd L07-Mj%/^^g˶o^UFCj.a&c?pb>RUf/I$I2odIENDB`./src/ubuntu/images/ubuntu-qtcreator.png0000644000015600001650000000311412705421114020501 0ustar jenkinsjenkinsPNG  IHDR szzsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATXKGsDdHģ0;AK 6( +.H"X!Bd/1=ęsXTW߾ISRVwy^7yۢ 3;,˲"wŇU-//ƳY{̌=UED5ޑ$ "B%8J—LYI+< @:>j*+ffp/|)6KQ3-,@Ha3+?B T'U`hYvTpA,1ĂcÊpSʀ SC msBwtc_{[O^LhɊ2@цAkCC# ) ,sNM 3K^&ONgxg9:5 0~~k_{]TKEީ:y7{o;KK8L-M՟xJ`UB{ȂW_N߽o=_}o^^4Is" b\S {8qM%)އMwv=?rm WPܺihd!y1{xog7Yiq;̧D@$Q/lvW/nWog]$ػwLGNF]zOo 6+Jlܺu~@F,he vu}+00)V^@DX[[.F^!kܟBp"!jr%2">y>eK͔{+os2*nfZ B>sOQըB܏cཕB#"aF"JX>y>f<4 /)B2?*Ṋʹ_A{_0Pk/^>g`%+Z΃*YE,y{{{N8TFUvf[j3ceel4t]$5@=/N6fEs8bfW?L3,-Y<-S 0'ޟ&̎IeR/gⲹLIENDB`./src/ubuntu/images/ubuntu-32.png0000644000015600001650000000407512705421114016730 0ustar jenkinsjenkinsPNG  IHDR szzsRGBbKGD pHYs B(xtIME -sIDATXÕilTl6 M &%PHPlC 4QԪBiK$ЪK6UKTC4j%Meq68RD((cl ^ƞwTyg _B ˀBZ %jLi}a]˿K XRxLY[!EuhrbvdoOuѵ^y}>0 (~jg7S 5b o) {͡TȮ74BݶF8ӗ!h_@Uq?+K3G"ûGB׮u$6 @>B{W姸!sP0_݌). Ϟ<72{D iz@:iO q׏k"ٴ P77KDDKSDn Q M tU= MN1#C`֯aUyn7ُ78@ٚwsc`gc1,RVw$O$+Ґ*&%Λy;7|1l{ -!Q"so7277G}7&-2*q(b۸4|$R C1D{toYK$SL͗E% .>gk7z {Pi"0~t|p,ݝ8UV`X? *l@U5/g~k5 ("IeHQG?Tϋ$H 8PVI0U* 3mn %f#EQ=hyZgQ`۵[ot,Xl&sYM즥={1NB,5u/S$oD1rHU.CZK##`NL<&&/Kja_&GW- GJοr"Y)&Vz8z[f -dʵR]WŠ:,m؝?gæ}nƽo#>/iEsָ;d9/;Lhޞs Lt|2jQ3mX,Wo0W霷w``*$-ͶRՌIENDB`./src/ubuntu/images/view-collapse.svg0000644000015600001650000001305212705421114017744 0ustar jenkinsjenkins image/svg+xml ./src/ubuntu/images/error.png0000644000015600001650000000040612705421114016307 0ustar jenkinsjenkinsPNG  IHDR(-SHPLTE]]aaee//}} ""ss##&&((,,..1144 tRNS+NkIDATWmY C7eS b}ɴQSv@LH2H>pC>&R gIlU뚣]s h''=T:j۰}Go:z)RRIENDB`./src/ubuntu/images/security-alert.svg0000644000015600001650000001375712705421114020162 0ustar jenkinsjenkins image/svg+xml ./src/ubuntu/images/clear.png0000644000015600001650000000016512705421114016246 0ustar jenkinsjenkinsPNG  IHDR |lނx6c'f/BA% C#l`e>K tpS?h0.WYaj uk|B~4<`$zRP $)( l ؍FdOMs?|*n , p:tU&HːZ@UC X@!"TʬAq6#؀S@49 *{B@&ӑI"!(?$E~Lfq\pT?sIENDB`./src/ubuntu/images/ubuntu-128.png0000644000015600001650000001461212705421114017014 0ustar jenkinsjenkinsPNG  IHDR>asRGBbKGD pHYs B(xtIME  "iE IDATx]i`Se~ޛ=i%iY HApc. n38.̌Χ#0(0QTTvdTڴJiM,r]MӛEJ'j:Wh6rJɛֈ@qFb0GH Z~8d'\ G"cg\ߐ͈#1 K?3"`4ƈ6P.h$g:c 33B&-# WCs4ȓ/4?? /Pc=x[=gz0PnW] H޶̜vTOToYP0pjeE 0Cm"H[`Jv`zaIP@@`rM/v>ޠ(+! Ib#8m:vy|O0Vn4͏ 'Bfdy c 7>z'  3gM@9V\k w@Aa~8>r -˧t &;lZV4(tOwFv$:Ue.! iAGtٚYeCE,SS(aiH Yȓ@R9l6x-౔SV poUE{[] ES \;` g3 Iv~F8 +4⟠9>{zLZ:+y@:G;(ڭ_ +S =v'ɕAe}SQu-\E .}Obi?yg e X]6k6z?4M :}bGkp=MN}/D)avSQȓ:\5LKx_q OoUCgJ<呜Wg%dD8j~w<ը ,Iteop<gQ[@nx xtsT9 ^FA;q:?\q3 OUykmn9QCΏAMPQok*S Ƃɕ 'QL+ĸNZhmSOǧC P _1 π>Ly\>^@B9D>?:ޚHNrї:A[N73g qtU@@K7 RKϡqU$TѝZZ>4`X17upA&P{ͥ]0 ch<۪[-k~.ꉖ[kaKݽ1wv~}"Mo+D癷yb?s_|6CkQ B5-_( Ff/CP)]v?s v_A}NY#i734]8PJWl] +D۪~CRsc T|3jt$wم@^jf$1:8OMxzC5z"_别$x\(- lcD^py~P嵇P7AvV%AMj苘6{.8uR܃7Þ@k&`Yz#d141ΆzzU̝൚;OIqsBtl k)C V[?d?GvTN~}wNPb(. \lXYCZaMO{H]v وe<&Z8Ny3&$KZWpAaT ?c'n2skT)u!ڤ"|\QؾZN.pAqs[G<{|@ќ<g#.1i/xk}}> :)G$'Kw䡐25v':{qv0 JCCEI MVS۾t] 3z\"z!xܨ>^NhC>6Q2XCh߿|m8m``~،m]@P];!V 1rpĘ)U XWZgEcM SE<dR#xio"2s'瓪?] naIrP>]6cig GlbxƘ9N Q伆7h [5ʖφƿfBs5>ɐ'F%WB1`k\~?"3G H}(V.j;a{Q'(5v6Y^9heE,ª}O~/X,UF\NB P}~#i\"z;ܥpբ$΀>+6P_65ގHhv.m5pEy78 ׵ģ~7eU Ip'f 5b=qkaDPR;*A׈\[8&}٢N|<ӯp!N~}1s<-k8,S#؛ lC:Hjs|3E_017?YQ$b{riqb=ܾ7Oxc0,}**|~ ^|BݗdեVi'FizrdpKwlX@ju vH %F19ٲ&+Azݴ+bFY7 ^U)nֽݹ<|E6[!D?!TpFT-umӳb0qsv=V~" >ڪNk4/zG8)n}H|h蝶nCFZVC|fHh/Ҷ{Emv>+sΛnFg\}6r/!)2LTQo۪a^u] V&r-൚Qa~bD"⠆9mbg.A5{# Bų .;?[7 R+Lj'iMNd WobJ5Io כ8C&Jը4c3'v+Qvh.Y/\`ʼZ|6{Zqs~cX^xa`DڪQѐIo CB18iPd(SC[':>$7ph'߷ھ—'"1'i(XX%TO}ƨr0 az~aFƀЪjmɹy,~g_`^Bfw rշ`"~΃TQ٭ 7}O_UL1pt0 'Ht2%ڼ~A[zP.TfG/}o!fI<õڂo%YbCG;[)^AH˳lp4dL^X^7i )?뇫Zw>ڡ)V.7EPQU{@ J7WQ$,{]/X{fJtqL8^㠹DࢢAn7gEݎD,L_+rnvND#Ibtz+=NQFuV̿X"u8 GVʵBciyr_+1r6k6 cyTol:f oanyD,;-ϔn|7I˩T %O,Oć-{[Ep&`e¯ODDɧqd E d}:}2y ZD|)m{y@a78=w$*s0N&+bPz`۲TBc+RwTCDži80=]V>4O=c3;˗E)89H%C`o@8z_yPȓC4\&M h|2x poazGOk43ىM^9ASwT.JO+U <ܥEpQo]é(UmRJy nL|G]vJi`  }] ok5{P4Q >7w tGw-d7rh `qyk5 G%Кh<qcp. = q0@IĘ`ZS`T0Qsd")NXK?B{OlIENDB`./src/ubuntu/images/run.png0000644000015600001650000000326712705421114015772 0ustar jenkinsjenkinsPNG  IHDR szz~IDATXýyPuȊ@)Rܬ ,+YS3divybZʡy6NeZ'e1NM$tL#&晗>~s^HG'B_rvٻXyQXf@'kԴv;01 ) CȐN   5_Q1y"EM&E r?dwp  }9o=@&D(DPP_#f ]@n`¡P<Ռ"(ɠ[}+~rȢk"BvZMa3  "ep{@ʻGC8XV t&˯;䵫ZpE+"B!BEBᐔ|oyECwC1rk{BS.18O^)""TDP&  }ߏI}w81`R;]ӵ Y$uQbyk rLl _(`w=3w:DkVI6ej ކ^N {]/9́c1S`Ar1ϜM[s/V\</@ +4}øx`NİW t:vVG#3%p7[ W o`oB1)BKb?>IENDB`./src/ubuntu/images/irc.png0000644000015600001650000000106512705421114015735 0ustar jenkinsjenkinsPNG  IHDR(-SsBITOPLTElSLJ3-zrխ8;r_RDD8,( eNVH(&?4:0r[E8znɹdU^H0*u񢛼$%kT=1]H4-G>ǽN=j"?62.yazbXƵzq< pHYs  ~tEXtCreation Time21/9/11FtEXtSoftwareAdobe Fireworks CS5q6IDATMP@M-lSѩd"0݌i|,еݸtv58 v>yEP _̮l] B|C'!&nJ`E 遏V'.aJfLliZe#IASF2:ݰ1Xk2qbnC~QEIENDB`./src/ubuntu/images/coreapps.png0000644000015600001650000000123312705421114016771 0ustar jenkinsjenkinsPNG  IHDR szzsRGBbKGD pHYs&&lUtIME 6M IDATXݗkQ?8lRDLb!(b? 暀@EJ[ +<M,$iR"g#.6 XyƏ͋zJ5/,fUs:{BZ*c)%C >)\K`T˰3A2pQ]T7Ue 9Ju'fp ԧQ7fURK p X !|K!l NмQWyv?E'ׯLdIENDB`./src/ubuntu/images/list-remove.svg0000644000015600001650000001043512705421114017442 0ustar jenkinsjenkins image/svg+xml ./src/ubuntu/images/packaging.png0000644000015600001650000000676312705421114017116 0ustar jenkinsjenkinsPNG  IHDR@@iqbKGDtIME .0 IDATx\u?kvfv׋jce7BZJڤ)MP?JZED#6m&EQ@"U D*MҀ7(]ٙy;^/RJWo}{yosXa/u퀉-h!2D_W]udq h}w<Уҍ|7c1~h?v[%:_6v W:F Wvѵj3Ϗ͟G+rzdߦ׈mo<@AB_kDNDFC'(B<;Z.28oJkv=xnSi;,KwXj ,5!`XjвTUL7Q8ZEB @XU(J/KN@0j7c};;ks^_/($΁85+UFF8FFHSz;_iV.t;<_)&J99Q!5mY%%cuJlXC#1m>t/''BiΟe^&)cIzڹqXI&c-:{=c_O=T>wL$G+9C)8<8ƱqؚC()㕄Bzv\޴k?gP*nO{lݼF&sC[0a:U9&8eͻߵ) ~߻tr 3ܼ 1*nKF-:uJ"eˎ>5U*B[P(O|.: os-)a]{fc:B![.:uXܼkgO<=,*Is]rGrm2" 8# )!Tckwak'7͞x5\qJǢ<@p8Qm %X,bz5!W}/=?s0#ʼnΞ|u&K~iYfUM> bۖymQ~57p9>6k3d՞czcƸ4@ Dsx8Xym |'ׯ- ko+wR5qj$KHj9_Ȍ$R\S@~Y\}%=2_?h@rV/_ƩR#J Q@Ogyc-+#6;_wqjXSaO:hQ ^:w8ndmcW!j !_yte(OQRm2ӅB#DکHSK6=l _]xWg O|j=!s@.B?Pz[ fG\v6$6,_#{X(Ē񕢚οz2HysGi}OR`x[3|J]H dŲW k_E9>f6Q@RKACTcHSM"@mX_ʻz@%,.Pm#+)XEZkʕRs7) @L8!@JTJ GowQ)_tV Ɔ|.bdrI4qbW*$Fj8}度)aE!6nj%:ran[ι D|OJP[Ȩ%.0<2Ԥs!Vdk,~!43^AO)pt\ 9 _"%23+e ۊZi.#l[gF A> =#SO@:h6q:J\ zn>qZ| F)˦Riu=A6d~W"p! nzjֳ焵5hU"KZ3& cLAgIphSs3MX:ژ,Zɰu7jy [\zkqsZ)%IjRHxrMκdfF1h<`^K5uDA\L]:9z^y;m8d: Q$FY|?b;tcāPiG_['C`1xvUYjteנuL dA^<3211|=Bcy=h벘mOeSJJl,~*xԩPh%k!Ӯ~>N8hq Hݑ{iWoyPi&,B<"H/ڄYK7F)pB9.xmgغ/pX485xE55̃92%1f^Msã6Mw~CˌNGw.e /#@I:S O>Ԛ8Ru|% =K4q@ .dH,p[DjJfeG%׮3TFB `Y>r']71 敃z7G HfvSov*O~r$(ך a=+U3ϡzZSLVMSdK־\αŗ/ݻahYajOs b'5I <ž0B|)xG˥]<%p<Zk3XkP"EQ1DPEkx!} =ʼnQJ׿7駟-nqhg uWL'?heuvmٸ!>jZRsSw' "6:e^=xT&^yO}~H@;X;)% >Ak|vus!5| #I5J̩1=pbhbt/_飳 }l sA@{&C?WS,wK%/qnZrXZ\z}Fmy@86djXC3CА}dIENDB`./src/ubuntu/ubuntuapparmoreditor.cpp0000644000015600001650000001457012705421114020211 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuapparmoreditor.h" #include "ubuntuconstants.h" #include "ubuntuclickmanifest.h" #include "ubuntusecuritypolicypickerdialog.h" #include #include namespace Ubuntu { namespace Internal { UbuntuApparmorEditor::UbuntuApparmorEditor() : UbuntuAbstractGuiEditor(Core::Context(Constants::UBUNTU_APPARMOR_EDITOR_CONTEXT)), m_editorWidget(0) { createUi(); } UbuntuApparmorEditor::~UbuntuApparmorEditor() { if(m_editorWidget) delete m_editorWidget; } UbuntuApparmorEditorWidget *UbuntuApparmorEditor::guiEditor() const { return m_editorWidget; } UbuntuAbstractGuiEditorWidget *UbuntuApparmorEditor::createGuiEditor() { if(m_editorWidget == 0) m_editorWidget = new UbuntuApparmorEditorWidget(); return m_editorWidget; } UbuntuApparmorEditorWidget::UbuntuApparmorEditorWidget() : UbuntuAbstractGuiEditorWidget(QLatin1String(Constants::UBUNTU_APPARMOR_MIME_TYPE)), m_ui(0) { createUI(); } UbuntuApparmorEditorWidget::~UbuntuApparmorEditorWidget() { if(m_ui) delete m_ui; } void UbuntuApparmorEditorWidget::updateAfterFileLoad() { //let see if we have valid data m_apparmor = QSharedPointer(new UbuntuClickManifest); if(m_apparmor->loadFromString(m_sourceEditor->toPlainText())) { if(activePage() != Source) syncToWidgets(m_apparmor.data()); } else { //switch to source page without syncing m_widgetStack->setCurrentIndex(Source); updateInfoBar(tr("There is a error in the file, please check the syntax.")); } } void UbuntuApparmorEditorWidget::setVersion(const QString &version) { //make sure all changes are in source if(activePage() != Source) syncToSource(); UbuntuClickManifest aa; if(aa.loadFromString(textEditorWidget()->textDocument()->plainText())) { aa.setPolicyVersion(version); textEditorWidget()->textDocument()->setPlainText(aa.raw()+QStringLiteral("\n")); textEditorWidget()->document()->setModified(true); if(activePage() == General) syncToWidgets(); } } bool UbuntuApparmorEditorWidget::syncToWidgets() { QSharedPointer man(new UbuntuClickManifest); if(man->loadFromString(m_sourceEditor->toPlainText())) { m_apparmor.swap(man); syncToWidgets(m_apparmor.data()); updateInfoBar(QString()); return true; } QString text = tr("There is a error in the file, please check the syntax."); updateInfoBar(text); return false; } bool UbuntuApparmorEditorWidget::syncToWidgets(UbuntuClickManifest *source) { QStringList groups = source->policyGroups(); QStringList currGroups; for (int i=0; i < m_ui->listWidget->count(); i++) { // Fix bug #1221407 - make sure that there are no empty policy groups. QString policyGroup = m_ui->listWidget->item(i)->text().trimmed(); if (!policyGroup.isEmpty()) { currGroups.append(m_ui->listWidget->item(i)->text()); } } if(groups != currGroups){ m_ui->listWidget->clear(); foreach(const QString &polGroup, groups) { m_ui->listWidget->addItem(polGroup); } } m_dirty = false; emit uiEditorChanged(); return true; } void UbuntuApparmorEditorWidget::syncToSource() { QStringList items; for (int i=0; i < m_ui->listWidget->count(); i++) { // Fix bug #1221407 - make sure that there are no empty policy groups. QString policyGroup = m_ui->listWidget->item(i)->text().trimmed(); if (!policyGroup.isEmpty()) { items.append(m_ui->listWidget->item(i)->text()); } } m_apparmor->setPolicyGroups(items); QString result = m_apparmor->raw()+QStringLiteral("\n"); QString src = m_sourceEditor->toPlainText(); if (result == src) return; m_sourceEditor->setPlainText(result); m_sourceEditor->document()->setModified(true); m_dirty = false; emit uiEditorChanged(); } QWidget *UbuntuApparmorEditorWidget::createMainWidget() { QWidget *mainWidget = new QWidget; m_ui = new Ui::UbuntuAppArmorEditor(); m_ui->setupUi(mainWidget); m_ui->listWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_ui->listWidget,SIGNAL(itemChanged(QListWidgetItem*)),this,SLOT(setDirty())); connect(m_ui->listWidget,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(on_listWidget_customContextMenuRequested(QPoint))); connect(m_ui->listWidget->model(),SIGNAL(rowsInserted(QModelIndex,int,int)),this,SLOT(setDirty())); connect(m_ui->listWidget->model(),SIGNAL(columnsInserted(QModelIndex,int,int)),this,SLOT(setDirty())); connect(m_ui->listWidget->model(),SIGNAL(rowsRemoved(QModelIndex,int,int)),this,SLOT(setDirty())); connect(m_ui->listWidget->model(),SIGNAL(columnsRemoved(QModelIndex,int,int)),this,SLOT(setDirty())); connect(m_ui->pushButton_addpolicy,SIGNAL(clicked()),this,SLOT(on_pushButton_addpolicy_clicked())); return mainWidget; } void UbuntuApparmorEditorWidget::on_pushButton_addpolicy_clicked() { UbuntuSecurityPolicyPickerDialog dialog(m_apparmor->policyVersion()); if (dialog.exec()) { m_ui->listWidget->addItems(dialog.selectedPolicyGroups()); } } void UbuntuApparmorEditorWidget::on_listWidget_customContextMenuRequested(const QPoint &p) { if (m_ui->listWidget->selectedItems().count()==0) { return; } QMenu contextMenu; contextMenu.addAction(QLatin1String(Constants::UBUNTUPACKAGINGWIDGET_MENU_REMOVE)); QAction* selectedItem = contextMenu.exec(m_ui->listWidget->mapToGlobal(p)); if (selectedItem) { delete m_ui->listWidget->currentItem(); } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuprojectfile.h0000644000015600001650000000403212705421114017124 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPROJECTFILE_H #define UBUNTUPROJECTFILE_H #include #include "ubuntuconstants.h" #include "ubuntuproject.h" #include "ubuntuprojectmanager.h" #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuProject; class UbuntuProjectFile : public Core::IDocument { Q_OBJECT public: UbuntuProjectFile(UbuntuProject *parent, QString fileName); ~UbuntuProjectFile() {} bool save(QString *errorString, const QString &fileName, bool autoSave) override; QString defaultPath() const override; QString suggestedFileName() const override; bool isModified() const override; bool isSaveAsAllowed() const override; ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; bool reload(QString *errorString, ReloadFlag flag, ChangeType) override; private: UbuntuProject *m_project; QString m_fileName; }; } } #endif // UBUNTUPROJECTFILE_H ./src/ubuntu/ubuntusettingsclickpage.cpp0000644000015600001650000000275012705421114020661 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntusettingsclickpage.h" #include "ubuntuconstants.h" using namespace Ubuntu::Internal; UbuntuSettingsClickPage::UbuntuSettingsClickPage() : m_widget(0) { setId("A.Click"); setDisplayName(tr("Click")); setCategory("Ubuntu"); setDisplayCategory(QLatin1String("Ubuntu")); setCategoryIcon(QLatin1String(Ubuntu::Constants::UBUNTU_SETTINGS_ICON)); } UbuntuSettingsClickPage::~UbuntuSettingsClickPage() { } QWidget *UbuntuSettingsClickPage::widget( ) { if(!m_widget) m_widget = new UbuntuSettingsClickWidget(); return m_widget; } void UbuntuSettingsClickPage::apply() { if (!m_widget) // page was never shown return; m_widget->apply(); } void UbuntuSettingsClickPage::finish() { if (m_widget) delete m_widget; } ./src/ubuntu/ubuntuhtmlbuildconfiguration.cpp0000644000015600001650000001450512705421114021733 0ustar jenkinsjenkins#include "ubuntuhtmlbuildconfiguration.h" #include "ubuntuconstants.h" #include "ubuntuproject.h" #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuHtmlBuildConfiguration * * Even though HTML projects don't need to be built, we need * that BuildConfiguration to store the Builddirectory (where the click * package goes) and to enable the Target page to show us some actual * targets */ UbuntuHtmlBuildConfiguration::UbuntuHtmlBuildConfiguration(ProjectExplorer::Target *target) : BuildConfiguration(target,Constants::UBUNTU_CLICK_HTML_BC_ID) { } UbuntuHtmlBuildConfiguration::UbuntuHtmlBuildConfiguration(ProjectExplorer::Target *target, UbuntuHtmlBuildConfiguration *source) : BuildConfiguration(target,source) { } bool UbuntuHtmlBuildConfiguration::fromMap(const QVariantMap &map) { return BuildConfiguration::fromMap(map); } QVariantMap UbuntuHtmlBuildConfiguration::toMap() const { return BuildConfiguration::toMap(); } ProjectExplorer::NamedWidget *UbuntuHtmlBuildConfiguration::createConfigWidget() { return new UbuntuHtmlBuildSettingsWidget(this); } ProjectExplorer::BuildConfiguration::BuildType UbuntuHtmlBuildConfiguration::buildType() const { return Release; } UbuntuHtmlBuildConfigurationFactory::UbuntuHtmlBuildConfigurationFactory(QObject *parent) : IBuildConfigurationFactory(parent) { } UbuntuHtmlBuildConfigurationFactory::~UbuntuHtmlBuildConfigurationFactory() { } int UbuntuHtmlBuildConfigurationFactory::priority(const ProjectExplorer::Target *parent) const { if (canHandle(parent)) return 100; return -1; } QList UbuntuHtmlBuildConfigurationFactory::availableBuilds(const ProjectExplorer::Target *parent) const { if(!canHandle(parent)) return QList(); return createBuildInfos(parent->kit(),parent->project()->projectFilePath().toString()); } int UbuntuHtmlBuildConfigurationFactory::priority(const ProjectExplorer::Kit *k, const QString &projectPath) const { return (k && Utils::MimeDatabase().mimeTypeForFile(projectPath) .matchesName(QLatin1String(Constants::UBUNTUPROJECT_MIMETYPE))) ? 100 : -1; } QList UbuntuHtmlBuildConfigurationFactory::availableSetups(const ProjectExplorer::Kit *k, const QString &projectPath) const { UbuntuKitMatcher m; if(priority(k,projectPath) < 0 || !m.matches(k)) return QList(); return createBuildInfos(k,projectPath); } UbuntuHtmlBuildConfiguration *UbuntuHtmlBuildConfigurationFactory::create(ProjectExplorer::Target *parent, const ProjectExplorer::BuildInfo *info) const { QTC_ASSERT(info->factory() == this, return 0); QTC_ASSERT(info->kitId == parent->kit()->id(), return 0); QTC_ASSERT(!info->displayName.isEmpty(), return 0); UbuntuHtmlBuildConfiguration *conf = new UbuntuHtmlBuildConfiguration(parent); conf->setBuildDirectory(info->buildDirectory); conf->setDefaultDisplayName(info->displayName); conf->setDisplayName(info->displayName); return conf; } bool UbuntuHtmlBuildConfigurationFactory::canRestore(const ProjectExplorer::Target *parent, const QVariantMap &map) const { if (!canHandle(parent)) return false; return ProjectExplorer::idFromMap(map) == Constants::UBUNTU_CLICK_HTML_BC_ID; } UbuntuHtmlBuildConfiguration *UbuntuHtmlBuildConfigurationFactory::restore(ProjectExplorer::Target *parent, const QVariantMap &map) { if (!canRestore(parent,map) ) return 0; UbuntuHtmlBuildConfiguration *conf = new UbuntuHtmlBuildConfiguration(parent); if (conf->fromMap(map)) return conf; delete conf; return 0; } bool UbuntuHtmlBuildConfigurationFactory::canClone(const ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) const { if (!canHandle(parent)) return false; if (product->id() != Constants::UBUNTU_CLICK_HTML_BC_ID ) return false; return true; } UbuntuHtmlBuildConfiguration *UbuntuHtmlBuildConfigurationFactory::clone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) { if (!canClone(parent,product)) return 0; return new UbuntuHtmlBuildConfiguration(parent,static_cast(product)); } bool UbuntuHtmlBuildConfigurationFactory::canHandle(const ProjectExplorer::Target *t) const { UbuntuKitMatcher m; if(!m.matches(t->kit())) return false; if (t->project()->projectManager()->mimeType() != QLatin1String(Constants::UBUNTUPROJECT_MIMETYPE)) return false; return true; } QList UbuntuHtmlBuildConfigurationFactory::createBuildInfos(const ProjectExplorer::Kit *k, const QString &projectDir) const { QList builds; ProjectExplorer::BuildInfo *info = new ProjectExplorer::BuildInfo(this); info->buildDirectory = Utils::FileName::fromString(UbuntuProject::shadowBuildDirectory(projectDir,k,QStringLiteral("default"))); info->typeName = tr("Html5"); info->kitId = k->id(); info->displayName = tr("Default"); builds << info; return builds; } UbuntuHtmlBuildSettingsWidget::UbuntuHtmlBuildSettingsWidget(UbuntuHtmlBuildConfiguration *conf, QWidget *parent) : NamedWidget(parent) , m_buildConfiguration(conf) { QFormLayout *fl = new QFormLayout(this); fl->setContentsMargins(20, -1, 0, -1); fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); setLayout(fl); m_pathChooser = new Utils::PathChooser(this); m_pathChooser->setPath(m_buildConfiguration->rawBuildDirectory().toString()); fl->addRow(tr("Build directory:"), m_pathChooser); connect(m_pathChooser->lineEdit(),SIGNAL(editingFinished()),this,SLOT(onBuilddirChanged())); connect(m_buildConfiguration,SIGNAL(buildDirectoryChanged()),this,SLOT(updateBuildDirectory())); } void UbuntuHtmlBuildSettingsWidget::updateBuildDirectory() const { m_pathChooser->blockSignals(true); m_pathChooser->setPath(m_buildConfiguration->rawBuildDirectory().toString()); m_pathChooser->blockSignals(false); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuprojectmanager.h0000644000015600001650000000437012705421114017624 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPROJECTMANAGER_H #define UBUNTUPROJECTMANAGER_H #include #include "ubuntuconstants.h" #include "ubuntuproject.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuProject; class UbuntuProjectManager : public ProjectExplorer::IProjectManager { Q_OBJECT public: UbuntuProjectManager(); virtual QString mimeType() const override; ProjectExplorer::Project* openProject(const QString &filePath, QString *errorString) override; void registerProject(UbuntuProject *project); void unregisterProject(UbuntuProject *project); protected slots: void onProjectAdded(ProjectExplorer::Project*); private: QList m_projects; }; } // Internal } // Ubuntu #endif // UBUNTUPROJECTMANAGER_H ./src/ubuntu/ubuntufeatureprovider.cpp0000644000015600001650000000236112705421114020362 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntufeatureprovider.h" using namespace Ubuntu::Internal; Core::FeatureSet UbuntuFeatureProvider::availableFeatures(const QString &platformName) const { return UbuntuVersionManager::instance()->availableFeatures(platformName); } QStringList UbuntuFeatureProvider::availablePlatforms() const { return UbuntuVersionManager::instance()->availablePlatforms(); } QString UbuntuFeatureProvider::displayNameForPlatform(const QString &string) const { return UbuntuVersionManager::instance()->displayNameForPlatform(string); } ./src/ubuntu/ubuntudevicefactory.cpp0000644000015600001650000001075412705421114020010 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntudevicefactory.h" #include "ubuntuconstants.h" #include "ubuntudevice.h" #include #include #include #include #include namespace Ubuntu { namespace Internal { //copied from IDevice const char TypeKey[] = "OsType"; const char MachineTypeKey[] = "Type"; UbuntuDeviceFactory::UbuntuDeviceFactory(QObject *parent) : ProjectExplorer::IDeviceFactory(parent) { } } // namespace Internal } // namespace Ubuntu QString Ubuntu::Internal::UbuntuDeviceFactory::displayNameForId(Core::Id type) const { QTC_ASSERT(type.toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID)), return QString()); return tr("Ubuntu Device (%1)").arg(type.suffixAfter(Constants::UBUNTU_DEVICE_TYPE_ID)); } QList Ubuntu::Internal::UbuntuDeviceFactory::availableCreationIds() const { return QList() << Core::Id(Constants::UBUNTU_DEVICE_TYPE_ID).withSuffix("armhf") << Core::Id(Constants::UBUNTU_DEVICE_TYPE_ID).withSuffix("i386"); } bool Ubuntu::Internal::UbuntuDeviceFactory::canCreate() const { return false; } ProjectExplorer::IDevice::Ptr Ubuntu::Internal::UbuntuDeviceFactory::create(Core::Id id) const { Q_UNUSED(id); return UbuntuDevice::Ptr(); } bool Ubuntu::Internal::UbuntuDeviceFactory::canRestore(const QVariantMap &map) const { return ProjectExplorer::IDevice::typeFromMap(map).toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID)); } ProjectExplorer::IDevice::Ptr Ubuntu::Internal::UbuntuDeviceFactory::restore(const QVariantMap &map) const { QTC_ASSERT(canRestore(map), return UbuntuDevice::Ptr()); //fix up old device types if ( ProjectExplorer::IDevice::typeFromMap(map) == Constants::UBUNTU_DEVICE_TYPE_ID ) { //if those values are not available in the map we can not restore the devices anyway, its better to //return invalid device in that case QTC_ASSERT(map.contains(QLatin1String(TypeKey)), return UbuntuDevice::Ptr()); QTC_ASSERT(map.contains(QLatin1String(MachineTypeKey)), return UbuntuDevice::Ptr()); Core::Id newType; ProjectExplorer::IDevice::MachineType mType = static_cast(map.value(QLatin1String(MachineTypeKey), ProjectExplorer::IDevice::Hardware).toInt()); if (mType == ProjectExplorer::IDevice::Emulator) { QString emuName = ProjectExplorer::IDevice::idFromMap(map).toSetting().toString(); QString emuArchFileName = QStringLiteral("%1/ubuntu-emulator/%2/.device") .arg(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)) .arg(emuName); QString archType; if (QFile::exists(emuArchFileName)) { QFile emuArchFile(emuArchFileName); if (emuArchFile.open(QIODevice::ReadOnly)) { QTextStream in(&emuArchFile); archType = in.readAll().simplified(); } } if (archType.isEmpty()) { //if there is no arch file, make a guess archType = QStringLiteral("i386"); } newType = Core::Id(Constants::UBUNTU_DEVICE_TYPE_ID).withSuffix(archType); } else { newType = Core::Id(Constants::UBUNTU_DEVICE_TYPE_ID).withSuffix("armhf"); } QVariantMap newMap = map; newMap[QLatin1String(TypeKey)] = newType.toString(); const ProjectExplorer::IDevice::Ptr device = UbuntuDevice::create(); device->fromMap(newMap); return device; } const ProjectExplorer::IDevice::Ptr device = UbuntuDevice::create(); device->fromMap(map); return device; } ./src/ubuntu/ubuntusettingsclickwidget.h0000644000015600001650000000322712705421114020675 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUSETTINGSCLICKWIDGET_H #define UBUNTUSETTINGSCLICKWIDGET_H #include #include #include "ubuntuclicktool.h" namespace Ui { class UbuntuSettingsClickWidget; } namespace Ubuntu { namespace Internal { class UbuntuSettingsClickWidget : public QWidget { Q_OBJECT public: explicit UbuntuSettingsClickWidget(QWidget *parent = 0); ~UbuntuSettingsClickWidget(); void apply(); protected slots: void on_pushButtonCreateClickTarget_clicked(); void on_deleteClickChroot (const int index); void on_maintainClickChroot (const int index); void on_upgradeClickChroot (const int index); private: void listExistingClickTargets (); private: Ui::UbuntuSettingsClickWidget *ui; QSignalMapper* m_deleteMapper; QSignalMapper* m_updateMapper; QSignalMapper* m_maintainMapper; QList m_availableTargets; }; }} #endif // UBUNTUSETTINGSCLICKWIDGET_H ./src/ubuntu/ubuntulocalruncontrolfactory.cpp0000644000015600001650000001724112705421114021767 0ustar jenkinsjenkins#include "ubuntulocalruncontrolfactory.h" #include "ubuntulocalrunconfiguration.h" #include "ubuntulocalscopedebugsupport.h" #include "ubuntuconstants.h" #include "clicktoolchain.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { bool UbuntuLocalRunControlFactory::canRun(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode) const { if(qobject_cast(runConfiguration)) { if (mode != ProjectExplorer::Constants::NORMAL_RUN_MODE && mode != ProjectExplorer::Constants::DEBUG_RUN_MODE && mode != ProjectExplorer::Constants::DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN && mode != ProjectExplorer::Constants::QML_PROFILER_RUN_MODE && mode != Valgrind::Internal::CALLGRIND_RUN_MODE && mode != Valgrind::MEMCHECK_RUN_MODE && mode != Valgrind::MEMCHECK_WITH_GDB_RUN_MODE) { return false; } return runConfiguration->isEnabled(); } return false; } ProjectExplorer::RunControl *UbuntuLocalRunControlFactory::create(ProjectExplorer::RunConfiguration *runConfiguration, Core::Id mode, QString *errorMessage) { UbuntuLocalRunConfiguration *ubuntuRC = qobject_cast(runConfiguration); if (ubuntuRC) { QTC_ASSERT(canRun(runConfiguration, mode), return 0); if (!ubuntuRC->aboutToStart(errorMessage)) return 0; if (mode == ProjectExplorer::Constants::NORMAL_RUN_MODE) { ProjectExplorer::LocalApplicationRunControl *runControl = new ProjectExplorer::LocalApplicationRunControl(ubuntuRC, mode); runControl->setCommand(ubuntuRC->executable(), ubuntuRC->commandLineArguments()); runControl->setApplicationLauncherMode(ubuntuRC->runMode()); runControl->setWorkingDirectory(ubuntuRC->workingDirectory()); return runControl; } else if(mode == ProjectExplorer::Constants::DEBUG_RUN_MODE || mode == ProjectExplorer::Constants::DEBUG_RUN_MODE_WITH_BREAK_ON_MAIN) { QString rcId = runConfiguration->id().toString(); bool isScope = rcId.startsWith(QLatin1String(Constants::UBUNTUPROJECT_RUNCONTROL_SCOPE_ID)); Debugger::DebuggerStartParameters params; // Normalize to work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch'...) params.workingDirectory = Utils::FileUtils::normalizePathName(ubuntuRC->workingDirectory()); params.useTerminal = ubuntuRC->runMode() == ProjectExplorer::ApplicationLauncher::Console; params.executable = ubuntuRC->executable(); if (params.executable.isEmpty()) return 0; params.processArgs = ubuntuRC->commandLineArguments(); if (isScope) { ProjectExplorer::Abi hostAbi = ProjectExplorer::Abi::hostAbi(); if (hostAbi.os() == ProjectExplorer::Abi::LinuxOS) { QString triplet = ClickToolChain::gnutriplet(hostAbi); if (triplet.isEmpty()) { if (errorMessage) *errorMessage = tr("Unsupported host architecture"); return 0; } params.executable = QString::fromLatin1("/usr/lib/%1/unity-scopes/scoperunner") .arg(triplet); } else { if (errorMessage) *errorMessage = tr("Running scopes is not implemented on this OS."); return 0; } params.processArgs.clear(); params.continueAfterAttach = true; params.startMode = Debugger::AttachToRemoteServer; params.remoteSetupNeeded = true; params.connParams.host = QStringLiteral("127.0.0.1"); } Debugger::DebuggerRunControl *runControl = Debugger::createDebuggerRunControl(params, ubuntuRC, errorMessage, mode); if (isScope) { //runControl takes ownership of this pointer new UbuntuLocalScopeDebugSupport(ubuntuRC, runControl, params.executable); } return runControl; } else if(mode == ProjectExplorer::Constants::QML_PROFILER_RUN_MODE) { ProjectExplorer::EnvironmentAspect *environment = ubuntuRC->extraAspect(); Analyzer::AnalyzerStartParameters sp; sp.runMode = mode; sp.workingDirectory = ubuntuRC->workingDirectory(); sp.debuggee = ubuntuRC->executable(); sp.debuggeeArgs = ubuntuRC->commandLineArguments(); sp.displayName = ubuntuRC->displayName(); if (environment) sp.environment = environment->environment(); sp.analyzerPort = QmlProfiler::LocalQmlProfilerRunner::findFreePort(sp.analyzerHost); if(sp.analyzerPort == 0) { *errorMessage = tr("Cannot open port on host for QML profiling."); return 0; } return QmlProfiler::LocalQmlProfilerRunner::createLocalRunControl(runConfiguration, sp,errorMessage); } else if(mode == Valgrind::Internal::CALLGRIND_RUN_MODE || mode == Valgrind::MEMCHECK_RUN_MODE || mode == Valgrind::MEMCHECK_WITH_GDB_RUN_MODE) { Analyzer::AnalyzerStartParameters sp; sp.displayName = ubuntuRC->displayName(); sp.runMode = mode; ProjectExplorer::EnvironmentAspect *aspect = ubuntuRC->extraAspect(); if (aspect) sp.environment = aspect->environment(); sp.workingDirectory = ubuntuRC->workingDirectory(); sp.debuggee = ubuntuRC->executable(); sp.debuggeeArgs = ubuntuRC->commandLineArguments(); const ProjectExplorer::IDevice::ConstPtr device = ProjectExplorer::DeviceKitInformation::device(ubuntuRC->target()->kit()); QTC_ASSERT(device, return 0); QTC_ASSERT(device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE, return 0); QTcpServer server; if (!server.listen(QHostAddress::LocalHost) && !server.listen(QHostAddress::LocalHostIPv6)) { *errorMessage = tr("Cannot open port on host for profiling."); return 0; } sp.connParams.host = server.serverAddress().toString(); sp.connParams.port = server.serverPort(); sp.localRunMode = static_cast(ubuntuRC->runMode()); return Analyzer::AnalyzerManager::createRunControl(sp, runConfiguration); } QTC_ASSERT(false, return 0); } return 0; } QString UbuntuLocalRunControlFactory::displayName() const { return tr("Run Ubuntu project locally"); } } //namespace Internal } //namespace Ubuntu ./src/ubuntu/ubuntuwelcomemode.cpp0000644000015600001650000000453212705421120017453 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen * Author: Benjamin Zeller */ #include "ubuntuwelcomemode.h" #include "ubuntuconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu; using namespace Ubuntu::Internal; QUrl UbuntuWelcomePage::pageLocation() const { // normalize paths so QML doesn't freak out if it's wrongly capitalized on Windows const QString resourcePath = Utils::FileUtils::normalizePathName(Constants::UBUNTU_WELCOMESCREEN_QML); return QUrl::fromLocalFile(resourcePath); } QString UbuntuWelcomePage::title() const { return tr("Ubuntu-SDK"); } int UbuntuWelcomePage::priority() const { return 0; } void UbuntuWelcomePage::facilitateQml(QQmlEngine *engine) { engine->setOutputWarningsToStandardError(true); QQmlContext *context = engine->rootContext(); context->setContextProperty(QLatin1String("ubuntuWelcomeMode"), this); } Core::Id UbuntuWelcomePage::id() const { return "UbuntuSdkPage"; } void UbuntuWelcomePage::newProject() { Core::ICore::showNewItemDialog(tr("New Project"), Core::IWizardFactory::wizardFactoriesOfKind(Core::IWizardFactory::ProjectWizard)); } void UbuntuWelcomePage::openProject() { ProjectExplorer::ProjectExplorerPlugin::instance()->openOpenProjectDialog(); } void UbuntuWelcomePage::openGallery() { QProcess::startDetached(QString::fromLatin1("%1/qtc_launch_gallery").arg(Constants::UBUNTU_SCRIPTPATH)); } ./src/ubuntu/localportsmanager.cpp0000644000015600001650000001032312705421114017423 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "localportsmanager.h" #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; UbuntuLocalPortsManager* UbuntuLocalPortsManager::m_instance = 0; UbuntuLocalPortsManager::UbuntuLocalPortsManager( ) : QObject(0), m_first(10000), m_last (10029) //there are max 30 ports available, more would break adb { Q_ASSERT_X(m_instance == 0, Q_FUNC_INFO,"There can be only one instance of UbuntuLocalPortsManager"); m_instance = this; } UbuntuLocalPortsManager::~UbuntuLocalPortsManager() { if(m_instance == this) m_instance = 0; } void UbuntuLocalPortsManager::setPortsRange(const int first, const int last) { m_instance->m_first = first; m_instance->m_last = last; } /*! * \brief UbuntuLocalPortsManager::getFreeRange * Queries adb which ports are already in use and returns a list * of ports available. * \bug As long as there is only once instance of QtC running this should * be no problem, but when a second one is started it may result in a race condition * and ports could get reassigned and leave one QtC with a wrong portlist */ Utils::PortList UbuntuLocalPortsManager::getFreeRange(const QString &serial, const int count) { QProcess adb; adb.start(QLatin1String("adb") ,QStringList()<readAll()); QRegularExpression regExpPorts(QLatin1String("^\\s*(\\S+)\\s*tcp:([0-9]+)\\s*tcp:([0-9]+)"),QRegularExpression::MultilineOption); QRegularExpressionMatchIterator matchIter = regExpPorts.globalMatch(str); Utils::PortList freePorts; Utils::PortList usedPorts; while(matchIter.hasNext()) { QRegularExpressionMatch match = matchIter.next(); QString device = match.captured(1); int localPort = match.captured(2).toInt(); QString remotePort = match.captured(3); //if a port is already forwarded to the device, we can just reuse it if (serial == device && localPort >= m_instance->m_first && localPort <= m_instance->m_last) { if(debug) qDebug()<<"Found port already linked to device: "<m_first; for(int i = 0, found = 0; found < required && firstPort+i < m_instance->m_last; i++) { int port = firstPort + i; //is the port in use? if(usedPorts.contains(port)) continue; //is that port already assigned to us? if(freePorts.contains(port)) continue; freePorts.addPort(port); found++; if(debug) qDebug()<<"Found free port: "<. * * Author: Juhapekka Piiroinen */ #include "ubuntusettingsdeviceconnectivitypage.h" #include "ubuntuconstants.h" using namespace Ubuntu::Internal; UbuntuSettingsDeviceConnectivityPage::UbuntuSettingsDeviceConnectivityPage() : m_widget(0) { setId("A.DeviceConnectivity"); setDisplayName(tr("Devices")); setCategory("Ubuntu"); setDisplayCategory(QLatin1String("Ubuntu")); setCategoryIcon(QLatin1String(Ubuntu::Constants::UBUNTU_SETTINGS_ICON)); } UbuntuSettingsDeviceConnectivityPage::~UbuntuSettingsDeviceConnectivityPage() { } QWidget *UbuntuSettingsDeviceConnectivityPage::widget() { if (!m_widget) m_widget = new UbuntuSettingsDeviceConnectivityWidget(); return m_widget; } void UbuntuSettingsDeviceConnectivityPage::apply() { if (!m_widget) // page was never shown return; m_widget->apply(); } void UbuntuSettingsDeviceConnectivityPage::finish() { if(m_widget) delete m_widget; } ./src/ubuntu/ubuntupackagingmode.cpp0000644000015600001650000001217712705421114017753 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntupackagingmode.h" #include "ubuntupackagingmodel.h" #include "ubuntuconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ubuntu::Internal; UbuntuPackagingMode *UbuntuPackagingMode::m_instance = 0; UbuntuPackagingMode::UbuntuPackagingMode(QObject *parent) : Core::IMode(parent) { Q_ASSERT(m_instance == 0); m_instance = this; setDisplayName(tr(Ubuntu::Constants::UBUNTU_MODE_PACKAGING_DISPLAYNAME)); setIcon(QIcon(QLatin1String(Ubuntu::Constants::UBUNTU_MODE_PACKAGING_ICON))); setPriority(Ubuntu::Constants::UBUNTU_MODE_PACKAGING_PRIORITY); setId(Ubuntu::Constants::UBUNTU_MODE_PACKAGING); setObjectName(QLatin1String(Ubuntu::Constants::UBUNTU_MODE_PACKAGING)); m_modeWidget = new QWidget; QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); layout->setSpacing(0); m_modeWidget->setLayout(layout); Utils::StyledBar* styledBar = new Utils::StyledBar(m_modeWidget); layout->addWidget(styledBar); m_modeView = new QQuickView; m_modeView->setResizeMode(QQuickView::SizeRootObjectToView); m_viewModel = new UbuntuPackagingModel(m_modeView); QWidget* container = QWidget::createWindowContainer(m_modeView); container->setMinimumWidth(860); container->setMinimumHeight(548); container->setFocusPolicy(Qt::TabFocus); layout->addWidget(container); m_modeView->rootContext()->setContextProperty(QLatin1String("publishModel") ,m_viewModel); m_modeView->rootContext()->setContextProperty(QLatin1String("resourceRoot") ,Constants::UBUNTU_DEVICESCREEN_ROOT); m_modeView->setSource(QUrl::fromLocalFile(Constants::UBUNTU_PUBLISHSCREEN_QML)); connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)), SLOT(modeChanged(Core::IMode*))); QObject* sessionManager = ProjectExplorer::SessionManager::instance(); connect(sessionManager,SIGNAL(projectAdded(ProjectExplorer::Project*)),SLOT(on_projectAdded(ProjectExplorer::Project*))); connect(sessionManager,SIGNAL(projectRemoved(ProjectExplorer::Project*)),SLOT(on_projectRemoved(ProjectExplorer::Project*))); connect(sessionManager,SIGNAL(startupProjectChanged(ProjectExplorer::Project*)),SLOT(on_projectAdded(ProjectExplorer::Project*))); connect(m_viewModel,SIGNAL(reviewToolsInstalledChanged(bool)),this,SLOT(updateModeState())); setWidget(m_modeWidget); setEnabled(false); } void UbuntuPackagingMode::initialize() { } void UbuntuPackagingMode::modeChanged(Core::IMode* currentMode) { previousMode = currentMode->id(); } void UbuntuPackagingMode::updateModeState() { ProjectExplorer::Project* startupProject = ProjectExplorer::SessionManager::startupProject(); bool isQmlProject = false; //bool isQmakeProject = false; bool isUbuntuProject = false; bool isCMakeProject = false; bool isGoProject = false; bool reviewToolsInstalled = m_viewModel->reviewToolsInstalled(); if (startupProject) { isQmlProject = (startupProject->projectManager()->mimeType() == QLatin1String(Constants::QMLPROJECT_MIMETYPE)); //isQmakeProject = (startupProject->projectManager()->mimeType() == QLatin1String(Constants::QMAKE_MIMETYPE)); isUbuntuProject = (startupProject->projectManager()->mimeType() == QLatin1String(Constants::UBUNTUPROJECT_MIMETYPE)); isCMakeProject = (startupProject->projectManager()->mimeType() == QLatin1String(CMakeProjectManager::Constants::CMAKEMIMETYPE)); isGoProject = (startupProject->projectManager()->mimeType() == QLatin1String(Constants::GO_PROJECT_MIMETYPE)); } this->setEnabled((isQmlProject || isUbuntuProject || isCMakeProject || isGoProject || reviewToolsInstalled)); } void UbuntuPackagingMode::on_projectAdded(ProjectExplorer::Project *project) { Q_UNUSED(project); updateModeState(); } void UbuntuPackagingMode::on_projectRemoved(ProjectExplorer::Project *project) { Q_UNUSED(project); updateModeState(); } ./src/ubuntu/ubuntuprojectfile.cpp0000644000015600001650000000346412705421114017467 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuprojectfile.h" using namespace Ubuntu::Internal; UbuntuProjectFile::UbuntuProjectFile(UbuntuProject *parent, QString fileName) : Core::IDocument(parent), m_project(parent), m_fileName(fileName) { QTC_CHECK(m_project); QTC_CHECK(!fileName.isEmpty()); setFilePath(Utils::FileName::fromString(fileName)); setMimeType(QLatin1String(Constants::UBUNTUPROJECT_MIMETYPE)); } bool UbuntuProjectFile::save(QString *, const QString &, bool) { return false; } QString UbuntuProjectFile::defaultPath() const { return QString(); } QString UbuntuProjectFile::suggestedFileName() const { return QString(); } bool UbuntuProjectFile::isModified() const { return false; } bool UbuntuProjectFile::isSaveAsAllowed() const { return false; } Core::IDocument::ReloadBehavior UbuntuProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const { Q_UNUSED(state) Q_UNUSED(type) return BehaviorSilent; } bool UbuntuProjectFile::reload(QString *errorString, ReloadFlag flag, ChangeType) { Q_UNUSED(errorString) Q_UNUSED(flag) return true; } ./src/ubuntu/ubuntuclickdialog.cpp0000644000015600001650000001756612705421114017436 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuclickdialog.h" #include "ui_ubuntuclickdialog.h" #include "ubuntuconstants.h" #include "clicktoolchain.h" #include "ubuntukitmanager.h" #include #include #include #include #include #include "ubuntucreatenewchrootdialog.h" #include "clickchrootagent_interface.h" namespace Ubuntu { namespace Internal { UbuntuClickDialog::UbuntuClickDialog(QWidget *parent) : QDialog(parent) ,m_ui(new Ui::UbuntuClickDialog) { m_ui->setupUi(this); QFont f(TextEditor::FontSettings::defaultFixedFontFamily()); f.setStyleHint(QFont::TypeWriter); m_ui->output->setFont(f); m_ui->exitStatusLabel->setVisible(false); m_process = new Utils::QtcProcess(this); connect(m_process,SIGNAL(readyReadStandardOutput()),this,SLOT(on_clickReadyReadStandardOutput())); connect(m_process,SIGNAL(readyReadStandardError()),this,SLOT(on_clickReadyReadStandardError())); connect(m_process,SIGNAL(finished(int)),this,SLOT(on_clickFinished(int))); } UbuntuClickDialog::~UbuntuClickDialog() { delete m_ui; } void UbuntuClickDialog::setParameters(const QList ¶ms) { m_tasks = params; } int UbuntuClickDialog::lastExitCode() const { return m_exitCode; } void UbuntuClickDialog::runClick( ) { #if 0 //change the button to cancel m_buttonBox->clear(); m_buttonBox->addButton(QDialogButtonBox::Cancel); #endif disableCloseButton(true); nextTask(); } int UbuntuClickDialog::runClickModal(const ProjectExplorer::ProcessParameters ¶ms, QWidget *parent) { return runClickModal(QList()< ¶ms, QWidget *parent) { UbuntuClickDialog dlg( parent ? parent : Core::ICore::mainWindow()); dlg.setParameters(params); QMetaObject::invokeMethod(&dlg,"runClick",Qt::QueuedConnection); dlg.exec(); return dlg.m_exitCode; } bool UbuntuClickDialog::createClickChrootModal(bool redetectKits, const QString &arch, const QString &framework, QWidget *parent) { UbuntuClickTool::Target t; if(!UbuntuCreateNewChrootDialog::getNewChrootTarget(&t,arch,framework,parent)) return false; ProjectExplorer::ProcessParameters params; UbuntuClickTool::parametersForCreateChroot(t,¶ms); bool success = (runClickModal(params,parent) == 0); if(success) { ClickToolChain* tc = new ClickToolChain(t, ProjectExplorer::ToolChain::AutoDetection); ProjectExplorer::ToolChainManager::registerToolChain(tc); if(redetectKits) UbuntuKitManager::autoDetectKits(); } return success; } int UbuntuClickDialog::maintainClickModal(const UbuntuClickTool::Target &target, const UbuntuClickTool::MaintainMode &mode) { return maintainClickModal(QList()< &targetList, const UbuntuClickTool::MaintainMode &mode) { QList paramList; foreach(const UbuntuClickTool::Target &target, targetList) { if(mode == UbuntuClickTool::Delete) { QString title = tr(Constants::UBUNTU_CLICK_DELETE_TITLE); QString text = tr(Constants::UBUNTU_CLICK_DELETE_MESSAGE); if( QMessageBox::question(Core::ICore::mainWindow(),title,text) != QMessageBox::Yes ) return 0; if(UbuntuClickTool::clickChrootSuffix() == QLatin1String(Constants::UBUNTU_CLICK_CHROOT_DEFAULT_NAME)) { ComUbuntuSdkClickChrootAgentInterface clickAgent(QStringLiteral("com.ubuntu.sdk.ClickChrootAgent"), QStringLiteral("/com/ubuntu/sdk/ClickChrootAgent"), QDBusConnection::sessionBus()); if(clickAgent.isValid()) { QDBusPendingReply ret = clickAgent.releaseSession(target.framework,target.architecture); if(ret.isError()) qDebug()<state() != QProcess::NotRunning) { //ask the user if he really wants to do that QString title = tr(Constants::UBUNTU_CLICK_STOP_TITLE); QString text = tr(Constants::UBUNTU_CLICK_STOP_MESSAGE); if( QMessageBox::question(Core::ICore::mainWindow(),title,text)!= QMessageBox::Yes ) return; m_process->terminate(); m_process->waitForFinished(100); m_process->kill(); m_ui->exitStatusLabel->setText(tr(Constants::UBUNTU_CLICK_STOP_WAIT_MESSAGE)); m_ui->exitStatusLabel->setVisible(true); return; } } QDialog::done(code); } void UbuntuClickDialog::disableCloseButton(const bool &disabled) { QPushButton* bt = m_ui->buttonBox->button(QDialogButtonBox::Close); if(bt) bt->setDisabled(disabled); } void UbuntuClickDialog::nextTask() { if(m_tasks.length() <= 0) return; ProjectExplorer::ProcessParameters params = m_tasks.takeFirst(); params.resolveAll(); m_process->setCommand(params.command(),params.arguments()); m_process->setEnvironment(params.environment()); m_process->setWorkingDirectory(params.workingDirectory()); m_process->start(); } void UbuntuClickDialog::on_clickFinished(int exitCode) { if (exitCode != 0) { on_clickReadyReadStandardError(tr("---%0---").arg(QLatin1String(Constants::UBUNTU_CLICK_ERROR_EXIT_MESSAGE))); } else { on_clickReadyReadStandardOutput(tr("---%0---").arg(QLatin1String(Constants::UBUNTU_CLICK_SUCCESS_EXIT_MESSAGE))); } if(m_tasks.length() > 0) { nextTask(); return; } disableCloseButton(false); #if 0 //set the button to close again m_buttonBox->clear(); m_buttonBox->addButton(QDialogButtonBox::Close); #endif m_exitCode = exitCode; } void UbuntuClickDialog::on_clickReadyReadStandardOutput(const QString txt) { QString outText = QString::fromLocal8Bit("
"); if(txt.isEmpty()) outText.append(QString::fromLocal8Bit(m_process->readAllStandardOutput())); else outText.append(txt); outText.append(QString::fromLocal8Bit("
")); m_ui->output->append(outText); } void UbuntuClickDialog::on_clickReadyReadStandardError(const QString txt) { QString outText = QString::fromLocal8Bit("
"); if(txt.isEmpty()) outText.append(QString::fromLocal8Bit(m_process->readAllStandardError())); else outText.append(txt); outText.append(QString::fromLocal8Bit("
")); m_ui->output->append(outText); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntudeploystepfactory.h0000644000015600001650000000250012705421114020374 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUDEPLOYSTEPFACTORY_H #define UBUNTU_INTERNAL_UBUNTUDEPLOYSTEPFACTORY_H #include #include namespace Ubuntu { namespace Internal { class UbuntuDeployStepFactory : public ProjectExplorer::IBuildStepFactory { Q_OBJECT public: // IBuildStepFactory interface virtual QList availableCreationIds(ProjectExplorer::BuildStepList *parent) const override; virtual QString displayNameForId(const Core::Id id) const override; virtual bool canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const override; virtual ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id) override; virtual bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const override; virtual ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) override; virtual bool canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const override; virtual ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) override; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUDEPLOYSTEPFACTORY_H ./src/ubuntu/ubuntuversion.cpp0000644000015600001650000000612712705421114016645 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuversion.h" #include #include using namespace Ubuntu::Internal; UbuntuVersion::UbuntuVersion() { } Core::FeatureSet UbuntuVersion::features() { Core::FeatureSet retval; QString cName = codename(); if (cName==QLatin1String(Constants::PRECISE)) { retval |= Core::FeatureSet(Constants::FEATURE_UBUNTU_PRECISE); } else if (cName==QLatin1String(Constants::QUANTAL)) { retval |= Core::FeatureSet(Constants::FEATURE_UBUNTU_QUANTAL); } else if (cName==QLatin1String(Constants::RARING)) { retval |= Core::FeatureSet(Constants::FEATURE_UBUNTU_RARING); } else if (cName==QLatin1String(Constants::SAUCY)) { retval |= Core::FeatureSet(Constants::FEATURE_UBUNTU_SAUCY); retval |= Core::FeatureSet(Constants::FEATURE_UNITY_SCOPE); } else if (cName==QLatin1String(Constants::TRUSTY)) { retval |= Core::FeatureSet(Constants::FEATURE_UBUNTU_TRUSTY); retval |= Core::FeatureSet(Constants::FEATURE_UNITY_SCOPE); } else if (cName==QLatin1String(Constants::UTOPIC)) { retval |= Core::FeatureSet(Constants::FEATURE_UBUNTU_UTOPIC); retval |= Core::FeatureSet(Constants::FEATURE_UNITY_SCOPE); } return retval; } UbuntuVersion *UbuntuVersion::fromLsbFile(const QString &fileName) { QFile lsbRelease(fileName); if (lsbRelease.open(QIODevice::ReadOnly)) { QByteArray data = lsbRelease.readAll(); lsbRelease.close(); UbuntuVersion *ver = new UbuntuVersion; foreach(QString line, QString::fromLatin1(data).split(QLatin1String("\n"))) { if (line.startsWith(QLatin1String(Constants::DISTRIB_ID))) { ver->m_id = line.replace(QLatin1String(Constants::DISTRIB_ID),QLatin1String("")); } else if (line.startsWith(QLatin1String(Constants::DISTRIB_RELEASE))) { ver->m_release = line.replace(QLatin1String(Constants::DISTRIB_RELEASE),QLatin1String("")); } else if (line.startsWith(QLatin1String(Constants::DISTRIB_CODENAME))) { ver->m_codename = line.replace(QLatin1String(Constants::DISTRIB_CODENAME),QLatin1String("")); } else if (line.startsWith(QLatin1String(Constants::DISTRIB_DESCRIPTION))) { ver->m_description = line.replace(QLatin1String(Constants::DISTRIB_DESCRIPTION),QLatin1String("")); } } return ver; } return 0; } ./src/ubuntu/ubuntuapparmoreditor.h0000644000015600001650000000430312705421114017647 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUAPPARMOREDITOR_H #define UBUNTU_INTERNAL_UBUNTUAPPARMOREDITOR_H #include "ubuntuabstractguieditor.h" #include "ubuntuabstractguieditorwidget.h" #include "ui_ubuntuapparmoreditor.h" #include namespace Ubuntu { namespace Internal { class UbuntuClickManifest; class UbuntuApparmorEditorWidget; class UbuntuApparmorEditor : public UbuntuAbstractGuiEditor { Q_OBJECT public: UbuntuApparmorEditor(); ~UbuntuApparmorEditor(); UbuntuApparmorEditorWidget *guiEditor() const; protected: // UbuntuAbstractGuiEditor interface virtual UbuntuAbstractGuiEditorWidget *createGuiEditor(); private: UbuntuApparmorEditorWidget *m_editorWidget; }; class UbuntuApparmorEditorWidget : public UbuntuAbstractGuiEditorWidget { Q_OBJECT public: UbuntuApparmorEditorWidget(); ~UbuntuApparmorEditorWidget(); // UbuntuAbstractGuiEditorWidget interface public: void setVersion (const QString &version); protected: virtual void updateAfterFileLoad() override; virtual bool syncToWidgets(); bool syncToWidgets(UbuntuClickManifest *source); virtual void syncToSource(); virtual QWidget *createMainWidget(); protected slots: void on_pushButton_addpolicy_clicked(); void on_listWidget_customContextMenuRequested(const QPoint &p); private: QSharedPointer m_apparmor; Ui::UbuntuAppArmorEditor *m_ui; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUAPPARMOREDITOR_H ./src/ubuntu/ubuntuemulatornotifier.h0000644000015600001650000000320512705421114020207 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUEMULATORNOTIFIER_H #define UBUNTU_INTERNAL_UBUNTUEMULATORNOTIFIER_H #include "ubuntudevicenotifier.h" #include class QTimer; namespace Ubuntu { namespace Internal { class UbuntuEmulatorNotifier : public IUbuntuDeviceNotifier { Q_OBJECT public: explicit UbuntuEmulatorNotifier(QObject *parent = 0); // IUbuntuDeviceNotifier interface virtual void startMonitoring(const QString &imageName) override; virtual void stopMonitoring() override; virtual bool isConnected() const override; private slots: void pollTimeout (); void pollProcessReadyRead (); void pollProcessFinished (int exitCode, QProcess::ExitStatus exitStatus); private: QByteArray m_buffer; QString m_imageName; QTimer *m_pollTimout; QProcess *m_pollProcess; bool m_connected; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUEMULATORNOTIFIER_H ./src/ubuntu/ubuntucmakebuildconfiguration.cpp0000644000015600001650000002433212705421114022046 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntucmakebuildconfiguration.h" #include "ubuntuconstants.h" #include "clicktoolchain.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; /*! * \class UbuntuCMakeBuildConfiguration * Represents the build configuration for a Ubuntu-SDK * CMake Project */ UbuntuCMakeBuildConfiguration::UbuntuCMakeBuildConfiguration(ProjectExplorer::Target *parent) :CMakeBuildConfiguration(parent,Core::Id(Constants::UBUNTU_CLICK_CMAKE_BC_ID)) { } ProjectExplorer::NamedWidget *UbuntuCMakeBuildConfiguration::createConfigWidget() { return new UbuntuCMakeBuildSettingsWidget(this); } UbuntuCMakeBuildConfiguration::UbuntuCMakeBuildConfiguration(ProjectExplorer::Target *parent, UbuntuCMakeBuildConfiguration *source) : CMakeBuildConfiguration(parent,source) { } bool UbuntuCMakeBuildConfiguration::fromMap(const QVariantMap &map) { if(!CMakeBuildConfiguration::fromMap(map)) return false; this->setUseNinja(false); return true; } /*! * \class UbuntuCMakeBuildConfigurationFactory * Factory class to create UbuntuCMakeBuildConfiguration * instances. */ UbuntuCMakeBuildConfigurationFactory::UbuntuCMakeBuildConfigurationFactory(QObject *parent) : CMakeBuildConfigurationFactory(parent) { } UbuntuCMakeBuildConfigurationFactory::~UbuntuCMakeBuildConfigurationFactory() { } int UbuntuCMakeBuildConfigurationFactory::priority(const ProjectExplorer::Target *parent) const { if(canHandle(parent)) return 10; return -1; } QList UbuntuCMakeBuildConfigurationFactory::availableBuilds(const ProjectExplorer::Target *parent) const { if(!canHandle(parent)) QList(); QList infos = CMakeBuildConfigurationFactory::availableBuilds(parent); foreach(ProjectExplorer::BuildInfo* info, infos) info->typeName = tr("Ubuntu SDK Build"); return infos; } int UbuntuCMakeBuildConfigurationFactory::priority(const ProjectExplorer::Kit *k, const QString &projectPath) const { if(!k) return false; ProjectExplorer::ToolChain* tc = ProjectExplorer::ToolChainKitInformation::toolChain(k); if(!tc || tc->type() != QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) return false; return (Core::MimeDatabase::findByFile(QFileInfo(projectPath)).matchesType(QLatin1String(CMakeProjectManager::Constants::CMAKEMIMETYPE))) ? 10 : -1; } QList UbuntuCMakeBuildConfigurationFactory::availableSetups(const ProjectExplorer::Kit *k, const QString &projectPath) const { QList infos = CMakeBuildConfigurationFactory::availableSetups(k,projectPath); foreach(ProjectExplorer::BuildInfo* info, infos) info->typeName = tr("Ubuntu SDK Build"); return infos; } UbuntuCMakeBuildConfiguration *UbuntuCMakeBuildConfigurationFactory::create(ProjectExplorer::Target *parent, const ProjectExplorer::BuildInfo *info) const { QTC_ASSERT(info->factory() == this, return 0); QTC_ASSERT(info->kitId == parent->kit()->id(), return 0); QTC_ASSERT(!info->displayName.isEmpty(), return 0); QTC_ASSERT(canHandle(parent),return 0); CMakeProjectManager::CMakeBuildInfo copy(*static_cast(info)); CMakeProjectManager::CMakeProject *project = static_cast(parent->project()); if (copy.buildDirectory.isEmpty()) copy.buildDirectory = Utils::FileName::fromString(project->shadowBuildDirectory(project->projectFilePath(), parent->kit(), copy.displayName)); UbuntuCMakeBuildConfiguration *bc = new UbuntuCMakeBuildConfiguration(parent); bc->setDisplayName(copy.displayName); bc->setDefaultDisplayName(copy.displayName); ProjectExplorer::BuildStepList *buildSteps = bc->stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD); ProjectExplorer::BuildStepList *cleanSteps = bc->stepList(ProjectExplorer::Constants::BUILDSTEPS_CLEAN); UbuntuCMakeMakeStep *makeStep = new UbuntuCMakeMakeStep(buildSteps); buildSteps->insertStep(0, makeStep); UbuntuCMakeMakeStep *cleanMakeStep = new UbuntuCMakeMakeStep(cleanSteps); cleanSteps->insertStep(0, cleanMakeStep); cleanMakeStep->setAdditionalArguments(QLatin1String("clean")); cleanMakeStep->setClean(true); bc->setBuildDirectory(Utils::FileName::fromString(copy.buildDirectory.toString())); bc->setUseNinja(false); // Default to all if (project->hasBuildTarget(QLatin1String("all"))) makeStep->setBuildTarget(QLatin1String("all"), true); return bc; } bool UbuntuCMakeBuildConfigurationFactory::canRestore(const ProjectExplorer::Target *parent, const QVariantMap &map) const { if (!canHandle(parent)) return false; return ProjectExplorer::idFromMap(map) == Constants::UBUNTU_CLICK_CMAKE_BC_ID; } UbuntuCMakeBuildConfiguration *UbuntuCMakeBuildConfigurationFactory::restore(ProjectExplorer::Target *parent, const QVariantMap &map) { if (!canRestore(parent, map)) return 0; UbuntuCMakeBuildConfiguration *bc = new UbuntuCMakeBuildConfiguration(parent); if (bc->fromMap(map)) return bc; delete bc; return 0; } bool UbuntuCMakeBuildConfigurationFactory::canClone(const ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) const { if (!canHandle(parent)) return false; return product->id() == Constants::UBUNTU_CLICK_CMAKE_BC_ID; } UbuntuCMakeBuildConfiguration *UbuntuCMakeBuildConfigurationFactory::clone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) { if (!canClone(parent, product)) return 0; UbuntuCMakeBuildConfiguration *old = static_cast(product); return new UbuntuCMakeBuildConfiguration(parent, old); } /*! * \brief UbuntuCMakeBuildConfigurationFactory::canHandle * checks if we can create buildconfigurations for the given target */ bool UbuntuCMakeBuildConfigurationFactory::canHandle(const ProjectExplorer::Target *t) const { QTC_ASSERT(t, return false); if (!t->project()->supportsKit(t->kit())) return false; ProjectExplorer::ToolChain* tc = ProjectExplorer::ToolChainKitInformation::toolChain(t->kit()); if(!tc || tc->type() != QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) return false; return t->project()->id() == Core::Id(CMakeProjectManager::Constants::CMAKEPROJECT_ID); } UbuntuCMakeBuildSettingsWidget::UbuntuCMakeBuildSettingsWidget(UbuntuCMakeBuildConfiguration *bc) : m_buildConfiguration(bc) { QFormLayout *fl = new QFormLayout(this); fl->setContentsMargins(20, -1, 0, -1); fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); setLayout(fl); bool inSource = CMakeProjectManager::CMakeProject::hasInSourceBuild(bc->target()->project()->projectDirectory()); if(inSource) { QLabel* inSourceLabel = new QLabel(this); inSourceLabel->setWordWrap(true); inSourceLabel->setText(tr("Qt Creator has detected an in-source-build in %1 " "which prevents shadow builds. Qt Creator will not allow you to change the build directory. " "If you want a shadow build, clean your source directory and re-open the project.") .arg(bc->target()->project()->projectDirectory())); fl->addRow(inSourceLabel); } if(!inSource) { m_pathChooser = new Utils::PathChooser(this); m_pathChooser->setPath(m_buildConfiguration->rawBuildDirectory().toString()); fl->addRow(tr("Build directory:"), m_pathChooser); connect(m_pathChooser->lineEdit(),SIGNAL(editingFinished()),this,SLOT(onBuilddirChanged())); } m_userArguments = new CMakeProjectManager::ArgumentsLineEdit(this); fl->addRow(tr("CMake arguments:"),m_userArguments); m_userArguments->setText(Utils::QtcProcess::joinArgs(m_buildConfiguration->arguments())); //m_userArguments->setHistoryCompleter(QLatin1String("CMakeArgumentsLineEdit")); connect(m_userArguments,SIGNAL(editingFinished()),this,SLOT(onArgumentsChanged())); setDisplayName(tr("Ubuntu SDK CMake")); } void UbuntuCMakeBuildSettingsWidget::onArgumentsChanged() { if(!m_userArguments->isValid()) return; QStringList args = Utils::QtcProcess::splitArgs(m_userArguments->text()); if(m_buildConfiguration->arguments() != args) m_buildConfiguration->setArguments(args); } void UbuntuCMakeBuildSettingsWidget::onBuilddirChanged() { if(debug) qDebug()<<"Changing builddir to: "<fileName().toString(); m_buildConfiguration->setBuildDirectory(m_pathChooser->fileName()); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubunturemoterunconfigurationwidget.ui0000644000015600001650000000235012705421114023021 0ustar jenkinsjenkins UbuntuRemoteRunconfigurationWidget 0 0 400 133 Form When enabled, already installed apps on the phone will be overridden.\nThose apps will be uninstalled after the run has finished. Override if already installed Uninstall application after it finished ./src/ubuntu/ubunturemoterunconfiguration.cpp0000644000015600001650000004617212705421114021774 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubunturemoterunconfiguration.h" #include "clicktoolchain.h" #include "ubuntuclicktool.h" #include "ubuntuconstants.h" #include "ubuntulocalrunconfiguration.h" #include "ubunturemotedeployconfiguration.h" #include "ubuntupackagestep.h" #include "ubuntuclickmanifest.h" #include "ui_ubunturemoterunconfigurationwidget.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; const char FORCE_INSTALL_KEY[]="UbuntuRemoteRunConfiguration.ForceInstall"; const char UNINSTALL_KEY[]="UbuntuRemoteRunConfiguration.Uninstall"; UbuntuRemoteRunConfiguration::UbuntuRemoteRunConfiguration(ProjectExplorer::Target *parent, Core::Id id) : AbstractRemoteLinuxRunConfiguration(parent,id), m_running(false), m_forceInstall(false), m_uninstall(true) { setDisplayName(appId()); addExtraAspect(new RemoteLinux::RemoteLinuxEnvironmentAspect(this)); m_forceInstall = Settings::projectDefaults().overrideAppsByDefault; m_uninstall = Settings::projectDefaults().uninstallAppsByDefault; } UbuntuRemoteRunConfiguration::UbuntuRemoteRunConfiguration(ProjectExplorer::Target *parent, UbuntuRemoteRunConfiguration *source) : AbstractRemoteLinuxRunConfiguration(parent,source), m_running(false), m_forceInstall(false), m_uninstall(true) { } QString UbuntuRemoteRunConfiguration::localExecutableFilePath() const { return m_localExecutable; } QString UbuntuRemoteRunConfiguration::remoteExecutableFilePath() const { return m_remoteExecutable; } QStringList UbuntuRemoteRunConfiguration::arguments() const { return m_arguments; } QString UbuntuRemoteRunConfiguration::workingDirectory() const { return QString::fromLatin1("/home/phablet"); } Utils::Environment UbuntuRemoteRunConfiguration::environment() const { RemoteLinux::RemoteLinuxEnvironmentAspect *aspect = extraAspect(); QTC_ASSERT(aspect, return Utils::Environment()); Utils::Environment env(Utils::OsTypeLinux); env.modify(aspect->userEnvironmentChanges()); //work around a problem with loading OPENSSL triggering a SIGILL, injecting that //environment variable disables the check that causes the problem if(ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(target()->kit()) == Core::Id(Constants::UBUNTU_DEVICE_TYPE_ID).withSuffix(QStringLiteral("armhf"))) { env.set(QStringLiteral("OPENSSL_armcap"),QStringLiteral("0")); } return env; } QStringList UbuntuRemoteRunConfiguration::soLibSearchPaths() const { QStringList paths; CMakeProjectManager::CMakeProject *cmakeProj = qobject_cast(target()->project()); if(cmakeProj) { QList targets = cmakeProj->buildTargets(); foreach(const CMakeProjectManager::CMakeBuildTarget& target, targets) { QFileInfo binary(target.executable); if(binary.exists()) { if(debug) qDebug()<<"Adding path "<(target()->project()); if(qmakeProj) { foreach (const QmakeProjectManager::QmakeProFileNode* pro, qmakeProj->allProFiles()) { if(pro->projectType() == QmakeProjectManager::ApplicationTemplate || pro->projectType() == QmakeProjectManager::SharedLibraryTemplate) { QmakeProjectManager::TargetInformation info = pro->targetInformation(); if(!info.valid) continue; if(debug) qDebug()<<"Adding path "</package. That means we can not cache that * information, because it could change anytime */ bool UbuntuRemoteRunConfiguration::aboutToStart(QString *errorMessage) { if(debug) qDebug()<<"--------------------- Reconfiguring RunConfiguration ----------------------------"; m_arguments.clear(); m_clickPackage.clear(); QDir package_dir(packageDir()); if(!package_dir.exists()) { if(errorMessage) *errorMessage = tr("No packaging directory available, please check if the deploy configuration is correct."); return false; } ProjectExplorer::DeployConfiguration *deplConf = qobject_cast(target()->activeDeployConfiguration()); if(!deplConf) { if(errorMessage) *errorMessage = tr("No valid deploy configuration is set."); return false; } ProjectExplorer::BuildStepList *bsList = deplConf->stepList(); foreach(ProjectExplorer::BuildStep *currStep ,bsList->steps()) { UbuntuPackageStep *pckStep = qobject_cast(currStep); if(!pckStep) continue; QFileInfo info(pckStep->packagePath()); if(info.exists()) { m_clickPackage = info.fileName(); break; } } if(m_clickPackage.isEmpty()) { if (errorMessage) *errorMessage = tr("Could not find a click package to run, please check if the deploy configuration has a click package step"); return false; } ProjectExplorer::ToolChain* tc = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit()); if(tc->type() != QString::fromLatin1(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) { if(errorMessage) *errorMessage = tr("Wrong toolchain type. Please check your build configuration."); return false; } ClickToolChain* clickTc = static_cast(tc); ProjectExplorer::BuildConfiguration *bc = target()->activeBuildConfiguration(); if(!bc) { if(errorMessage) *errorMessage = tr("Invalid buildconfiguration"); return false; } if(id().toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_APP_ID))) { QString desktopFile = UbuntuLocalRunConfiguration::getDesktopFile(this,appId(),errorMessage); if(desktopFile.isEmpty()) return false; /* * Tries to read the Exec line from the desktop file, to * extract arguments and to know which "executor" is used on * the phone */ QStringList args; QString command; if(!UbuntuLocalRunConfiguration::readDesktopFile(desktopFile,&command,&args,errorMessage)) return false; QFileInfo commInfo(command); QString executor = commInfo.completeBaseName(); if(executor.startsWith(QStringLiteral("qmlscene"))) { m_localExecutable = QString::fromLatin1("%1/usr/lib/%2/qt5/bin/qmlscene") .arg(UbuntuClickTool::targetBasePath(clickTc->clickTarget())) .arg(clickTc->gnutriplet()); m_remoteExecutable = QStringLiteral("/usr/bin/qmlscene"); m_arguments = args; } else if(executor.startsWith(QStringLiteral("ubuntu-html5-app-launcher")) || executor.startsWith(QStringLiteral("webapp-container"))) { m_remoteExecutable = QStringLiteral(""); m_arguments = args; } else { //looks like a application without a launcher if(target()->project()->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID) { QmakeProjectManager::QmakeProject* pro = static_cast (target()->project()); foreach(const QmakeProjectManager::QmakeProFileNode* applPro, pro->applicationProFiles()) { QmakeProjectManager::TargetInformation info = applPro->targetInformation(); if(applPro->targetInformation().valid) { if(info.target == commInfo.fileName()) { m_localExecutable = info.buildDir + QDir::separator() + info.target; break; } } } } else { CMakeProjectManager::CMakeProject* pro = static_cast (target()->project()); foreach(const CMakeProjectManager::CMakeBuildTarget &t, pro->buildTargets()) { if(t.targetType != CMakeProjectManager::ExecutableType|| t.executable.isEmpty()) continue; QFileInfo execInfo(t.executable); if(execInfo.fileName() == commInfo.fileName()) m_localExecutable = t.executable; } } if (m_localExecutable.isEmpty()) { if(errorMessage) *errorMessage = tr("Could not find %1 in the project targets").arg(command); return false; } m_remoteExecutable = command; } return true; } else if (id().toString().startsWith(QLatin1String(Constants::UBUNTUPROJECT_REMOTE_RUNCONTROL_SCOPE_ID))) { QDir package_dir(packageDir()); if(!package_dir.exists()) { if(errorMessage) *errorMessage = tr("No packaging directory available, please check if the deploy configuration is correct."); return false; } QString manifestPath = package_dir.absoluteFilePath(QStringLiteral("manifest.json")); //read the manifest if(!QFile::exists(manifestPath)) { if(errorMessage) *errorMessage = tr("Could not find the manifest file in the package directory, make sure its installed into the root of the click package."); return false; } UbuntuClickManifest manifest; QString manifestErrMsg; if(!manifest.load(manifestPath,nullptr,&manifestErrMsg)) { if(errorMessage) *errorMessage = tr("Could not read the manifest file in the package directory: %1").arg(manifestErrMsg); return false; } QString iniFilePath; for(const UbuntuClickManifest::Hook &hook : manifest.hooks()) { if(hook.appId == appId()) { iniFilePath = bc->buildDirectory() .appendPath(QLatin1String(Constants::UBUNTU_DEPLOY_DESTDIR)) .appendPath(hook.scope) .appendPath(manifest.name()+QStringLiteral("_")+hook.appId+QStringLiteral(".ini")) .toString(); } } if(iniFilePath.isEmpty()) { if(errorMessage) *errorMessage = tr("Could not find a hook with id %1 in the manifest file.").arg(appId()); return false; } if(!QFile::exists(iniFilePath)){ if(errorMessage) *errorMessage = tr("Ini file for scope: %1 does not exist.").arg(appId()); return false; } QSettings iniFile(iniFilePath,QSettings::IniFormat); if(iniFile.status() != QSettings::NoError) { if(errorMessage) *errorMessage = tr("Could not read the ini file for scope: .").arg(appId()); return false; } iniFile.beginGroup(QStringLiteral("ScopeConfig")); //the default exec line QString execLine = QStringLiteral("/usr/lib/%1/unity-scopes/scoperunner %R %S").arg(static_cast(tc)->gnutriplet()); QString srKey(QStringLiteral("ScopeRunner")); if(iniFile.contains(srKey)) execLine = iniFile.value(srKey).toString(); QStringList args = Utils::QtcProcess::splitArgs(execLine); QString executable = args.takeFirst(); //if debugging is enabled we inject the debughelper, so we need //to remove it here if(executable.contains(QStringLiteral("qtc_device_debughelper.py"))) { //remove the mode and the scope id argument args.takeFirst(); args.takeFirst(); args = Utils::QtcProcess::splitArgs(args[0]); executable = args.takeFirst(); } QFileInfo commandInfo(executable); if(commandInfo.completeBaseName().startsWith(QStringLiteral("scoperunner"))) { m_localExecutable = QString::fromLatin1("%1/usr/lib/%2/unity-scopes/scoperunner") .arg(UbuntuClickTool::targetBasePath(clickTc->clickTarget())) .arg(clickTc->gnutriplet()); m_remoteExecutable = QStringLiteral("/usr/lib/%1/unity-scopes/scoperunner").arg(clickTc->gnutriplet()); m_arguments = args; return true; } else { //no debug support for custom scoperunners m_localExecutable = QString(); m_remoteExecutable = executable; return true; } } if(errorMessage) *errorMessage = tr("Incompatible runconfiguration type id"); return false; } bool UbuntuRemoteRunConfiguration::fromMap(const QVariantMap &map) { if(debug) qDebug()<project(); if (p->id() == CMakeProjectManager::Constants::CMAKEPROJECT_ID || p->id() == QmakeProjectManager::Constants::QMAKEPROJECT_ID) return target()->activeBuildConfiguration()->buildDirectory().toString()+QDir::separator()+QLatin1String(Constants::UBUNTU_DEPLOY_DESTDIR); else if (p->id() == Ubuntu::Constants::UBUNTUPROJECT_ID || p->id() == "QmlProjectManager.QmlProject") { if (!target()->activeBuildConfiguration()) { //backwards compatibility, try to not crash QtC for old projects //they did not create a buildconfiguration back then QDir pDir(p->projectDirectory().toString()); return p->projectDirectory() .appendPath(QStringLiteral("..")) .appendPath(pDir.dirName()+QStringLiteral("_build")) .appendPath(QLatin1String(Constants::UBUNTU_DEPLOY_DESTDIR)).toString(); } else return target()->activeBuildConfiguration()->buildDirectory() .appendPath(QLatin1String(Constants::UBUNTU_DEPLOY_DESTDIR)).toString(); } return QString(); } void UbuntuRemoteRunConfiguration::setRunning(const bool set) { if(m_running != set) { m_running = set; emit enabledChanged(); } } bool UbuntuRemoteRunConfiguration::forceInstall() const { return m_forceInstall; } void UbuntuRemoteRunConfiguration::setForceInstall(bool forceInstall) { if(m_forceInstall == forceInstall) return; m_forceInstall = forceInstall; emit forceInstallChanged(forceInstall); } bool UbuntuRemoteRunConfiguration::uninstall() const { return m_uninstall; } void UbuntuRemoteRunConfiguration::setUninstall(bool uninstall) { if(m_uninstall == uninstall) return; m_uninstall = uninstall; emit uninstallChanged(uninstall); } UbuntuRemoteRunConfigurationWidget::UbuntuRemoteRunConfigurationWidget(UbuntuRemoteRunConfiguration *config, QWidget *parent) : QWidget(parent), m_config(config) { m_ui = new Ui::UbuntuRemoteRunconfigurationWidget; m_ui->setupUi(this); m_ui->checkBoxForceInstall->setChecked(config->forceInstall()); m_ui->checkBoxUninstall->setChecked(config->uninstall()); connect(m_ui->checkBoxForceInstall,&QCheckBox::toggled,config,&UbuntuRemoteRunConfiguration::setForceInstall); connect(m_ui->checkBoxUninstall,&QCheckBox::toggled,config,&UbuntuRemoteRunConfiguration::setUninstall); connect(config,&UbuntuRemoteRunConfiguration::forceInstallChanged,m_ui->checkBoxForceInstall,&QCheckBox::setChecked); connect(config,&UbuntuRemoteRunConfiguration::uninstallChanged,m_ui->checkBoxUninstall,&QCheckBox::setChecked); } UbuntuRemoteRunConfigurationWidget::~UbuntuRemoteRunConfigurationWidget() { delete m_ui; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuemulatornotifier.cpp0000644000015600001650000000524212705421114020545 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuemulatornotifier.h" #include "ubuntuconstants.h" #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuEmulatorNotifier * Polls adb if a specific */ UbuntuEmulatorNotifier::UbuntuEmulatorNotifier(QObject *parent) : IUbuntuDeviceNotifier(parent), m_connected(false) { m_pollTimout = new QTimer(this); m_pollProcess = new QProcess(this); connect(m_pollTimout,SIGNAL(timeout()),this,SLOT(pollTimeout())); connect(m_pollProcess,SIGNAL(readyRead()),this,SLOT(pollProcessReadyRead())); connect(m_pollProcess,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(pollProcessFinished(int,QProcess::ExitStatus))); } void UbuntuEmulatorNotifier::startMonitoring(const QString &imageName) { m_imageName = imageName; m_pollTimout->setInterval(5000); m_pollTimout->start(); } void UbuntuEmulatorNotifier::stopMonitoring() { m_pollTimout->stop(); m_pollProcess->kill(); } bool UbuntuEmulatorNotifier::isConnected() const { return m_connected; } void UbuntuEmulatorNotifier::pollTimeout() { m_buffer.clear(); m_pollTimout->stop(); m_pollProcess->setWorkingDirectory(QStringLiteral("/tmp")); m_pollProcess->start(QStringLiteral("%1/local_emulator_pid").arg(Constants::UBUNTU_SCRIPTPATH), QStringList() <readAllStandardOutput()); } void UbuntuEmulatorNotifier::pollProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus); m_pollTimout->start(); pollProcessReadyRead(); if (exitCode == 0) { if (!m_connected) { m_connected = true; emit deviceConnected(); } } else { if (m_connected) { m_connected = false; emit deviceDisconnected(); } } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntutestcontrol.cpp0000644000015600001650000000410412705421114017531 0ustar jenkinsjenkins#include "ubuntutestcontrol.h" #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuTestControl::UbuntuTestControl * Used only for testing purposes, provides functionality otherwise not reachable * because its not part of the object tree */ UbuntuTestControl::UbuntuTestControl(QObject *parent) : QObject(parent), m_lastBuildSuccess(false) { connect(ProjectExplorer::BuildManager::instance(),SIGNAL(buildQueueFinished(bool)), this,SLOT(setLastBuildSuccess(bool))); //QCoreApplication::instance()->installEventFilter(this); } bool UbuntuTestControl::eventFilter(QObject *, QEvent *event) { if(event->type() == QEvent::MouseButtonPress) { static ulong last_timestamp = 0; QMouseEvent *mouseEvent = static_cast(event); if(last_timestamp != mouseEvent->timestamp()) { last_timestamp = mouseEvent->timestamp(); qDebug()<globalPos().x()).arg(mouseEvent->globalPos().y())); } } return false; } bool UbuntuTestControl::lastBuildSuccess() const { return m_lastBuildSuccess; } void UbuntuTestControl::triggerCommand(const QString &command) { Core::Id id = Core::Id::fromString(command); Core::Command *cmd = Core::ActionManager::command(id); if(cmd) cmd->action()->trigger(); } void UbuntuTestControl::setLastBuildSuccess(bool arg) { if (m_lastBuildSuccess != arg) { qDebug()<<"Setting build success to "<(ProjectExplorer::BuildManager::instance()); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntusettingsdeviceconnectivitywidget.cpp0000644000015600001650000000363612705421114024045 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntusettingsdeviceconnectivitywidget.h" #include "ui_ubuntusettingsdeviceconnectivitywidget.h" #include "ubuntuconstants.h" #include "settings.h" using namespace Ubuntu; UbuntuSettingsDeviceConnectivityWidget::UbuntuSettingsDeviceConnectivityWidget(QWidget *parent) : QWidget(parent), ui(new Ui::UbuntuSettingsDeviceConnectivityWidget) { ui->setupUi(this); Internal::Settings::DeviceConnectivity devConn = Internal::Settings::deviceConnectivity(); ui->lineEditDeviceUserName->setText(devConn.user); ui->lineEditDeviceIP->setText(devConn.ip); ui->checkBox_mode_devices_autotoggle->setChecked(Internal::Settings::deviceAutoToggle()); } UbuntuSettingsDeviceConnectivityWidget::~UbuntuSettingsDeviceConnectivityWidget() { delete ui; } void UbuntuSettingsDeviceConnectivityWidget::apply() { Internal::Settings::DeviceConnectivity devConn = Internal::Settings::deviceConnectivity(); devConn.user = ui->lineEditDeviceUserName->text(); devConn.ip = ui->lineEditDeviceIP->text(); Internal::Settings::setDeviceConnectivity(devConn); Internal::Settings::setDeviceAutoToggle(ui->checkBox_mode_devices_autotoggle->isChecked()); Internal::Settings::flushSettings(); } ./src/ubuntu/click-framework.json0000644000015600001650000000173712705421114017166 0ustar jenkinsjenkins{"ubuntu-sdk-14.04-html": "available", "ubuntu-sdk-14.04-papi-dev1": "deprecated", "ubuntu-sdk-14.10-papi": "available", "ubuntu-sdk-14.10-qml": "available", "ubuntu-sdk-14.04-dev1": "deprecated", "ubuntu-sdk-14.04-html-dev1": "deprecated", "ubuntu-sdk-14.04": "available", "ubuntu-sdk-14.10-html": "available", "ubuntu-sdk-14.04-papi": "available", "ubuntu-sdk-13.10": "deprecated", "ubuntu-sdk-14.10-qml-dev3": "available", "ubuntu-sdk-14.10-qml-dev2": "available", "ubuntu-sdk-14.10-qml-dev1": "obsolete", "ubuntu-sdk-14.04-qml-dev1": "deprecated", "ubuntu-sdk-14.10-papi-dev2": "available", "ubuntu-sdk-14.10-papi-dev3": "available", "ubuntu-sdk-14.10-papi-dev1": "obsolete", "ubuntu-sdk-14.10": "available", "ubuntu-sdk-14.04-qml": "available", "ubuntu-sdk-14.10-dev1": "obsolete", "ubuntu-sdk-14.10-dev2": "available", "ubuntu-sdk-14.10-dev3": "available", "ubuntu-sdk-14.10-html-dev3": "available", "ubuntu-sdk-14.10-html-dev2": "available", "ubuntu-sdk-14.10-html-dev1": "obsolete"} ./src/ubuntu/ubunturemoteanalyzesupport.h0000644000015600001650000000263412705421114021140 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUREMOTEANALYZESUPPORT_H #define UBUNTU_INTERNAL_UBUNTUREMOTEANALYZESUPPORT_H #include #include "abstractremoterunsupport.h" #include namespace Ubuntu { namespace Internal { class UbuntuRemoteRunConfiguration; class UbuntuRemoteAnalyzeSupportPrivate; class UbuntuRemoteAnalyzeSupport : public AbstractRemoteRunSupport { Q_OBJECT public: UbuntuRemoteAnalyzeSupport(UbuntuRemoteRunConfiguration *runConfig, Analyzer::AnalyzerRunControl *engine, Core::Id runMode); ~UbuntuRemoteAnalyzeSupport() override; protected: void startExecution() override; void handleAdapterSetupFailed(const QString &error) override; private slots: void handleRemoteSetupRequested() override; void handleAppRunnerError(const QString &error) override; void handleRemoteOutput(const QByteArray &output) override; void handleRemoteErrorOutput(const QByteArray &output) override; void handleAppRunnerFinished(bool success) override; void handleProgressReport(const QString &progressOutput) override; void handleRemoteProcessStarted(); void handleProfilingFinished(); void remoteIsRunning(); private: void showMessage(const QString &, Utils::OutputFormat); UbuntuRemoteAnalyzeSupportPrivate * const d; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUREMOTEANALYZESUPPORT_H ./src/ubuntu/ubuntuprojectnode.cpp0000644000015600001650000001227612705421114017476 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuprojectnode.h" using namespace Ubuntu::Internal; UbuntuProjectNode::UbuntuProjectNode(UbuntuProject *project, Core::IDocument *projectFile) : ProjectExplorer::ProjectNode(projectFile->filePath()), m_project(project), m_projectFile(projectFile) { setDisplayName(projectFile->filePath().toFileInfo().completeBaseName()); refresh(); } Core::IDocument *UbuntuProjectNode::projectFile() const { return m_projectFile; } QString UbuntuProjectNode::projectFilePath() const { return m_projectFile->filePath().toString(); } void UbuntuProjectNode::refresh() { using namespace ProjectExplorer; removeFileNodes(fileNodes()); removeFolderNodes(subFolderNodes()); QStringList files = m_project->files(Project::AllFiles); files.removeAll(m_project->filesFileName()); QHash filesInDirectory; foreach (const QString &fileName, files) { QFileInfo fileInfo(fileName); QString absoluteFilePath; QString relativeDirectory; if (fileInfo.isAbsolute()) { absoluteFilePath = fileInfo.filePath(); relativeDirectory = m_project->projectDir().relativeFilePath(fileInfo.path()); } else { absoluteFilePath = m_project->projectDir().absoluteFilePath(fileInfo.filePath()); relativeDirectory = fileInfo.path(); if (relativeDirectory == QLatin1String(".")) relativeDirectory.clear(); } filesInDirectory[relativeDirectory].append(absoluteFilePath); } const QHash::ConstIterator cend = filesInDirectory.constEnd(); for (QHash::ConstIterator it = filesInDirectory.constBegin(); it != cend; ++it) { FolderNode *folder = findOrCreateFolderByName(it.key()); QList fileNodes; foreach (const QString &file, it.value()) { FileType fileType = SourceType; // ### FIXME FileNode *fileNode = new FileNode(Utils::FileName::fromString(file), fileType, false); fileNodes.append(fileNode); } folder->addFileNodes(fileNodes); } m_folderByName.clear(); } ProjectExplorer::FolderNode *UbuntuProjectNode::findOrCreateFolderByName(const QStringList &components, int end) { if (! end) return 0; QString baseDir = path().toFileInfo().path(); QString folderName; for (int i = 0; i < end; ++i) { folderName.append(components.at(i)); folderName += QLatin1Char('/'); } const QString component = components.at(end - 1); if (component.isEmpty()) return this; else if (FolderNode *folder = m_folderByName.value(folderName)) return folder; FolderNode *folder = new FolderNode(Utils::FileName::fromString(baseDir + QLatin1Char('/') + folderName)); folder->setDisplayName(component); m_folderByName.insert(folderName, folder); FolderNode *parent = findOrCreateFolderByName(components, end - 1); if (! parent) parent = this; parent->addFolderNodes(QList() << folder); return folder; } ProjectExplorer::FolderNode *UbuntuProjectNode::findOrCreateFolderByName(const QString &filePath) { QStringList components = filePath.split(QLatin1Char('/')); return findOrCreateFolderByName(components, components.length()); } QList UbuntuProjectNode::supportedActions(Node *node) const { Q_UNUSED(node); QList actions; actions.append(ProjectExplorer::AddNewFile); actions.append(ProjectExplorer::EraseFile); actions.append(ProjectExplorer::Rename); return actions; } bool UbuntuProjectNode::canAddSubProject(const QString &proFilePath) const { Q_UNUSED(proFilePath) return false; } bool UbuntuProjectNode::addSubProjects(const QStringList &proFilePaths) { Q_UNUSED(proFilePaths) return false; } bool UbuntuProjectNode::removeSubProjects(const QStringList &proFilePaths) { Q_UNUSED(proFilePaths) return false; } bool UbuntuProjectNode::addFiles(const QStringList &, QStringList *) { return false; } bool UbuntuProjectNode::removeFiles(const QStringList &, QStringList *) { return false; } bool UbuntuProjectNode::deleteFiles(const QStringList &) { return true; } bool UbuntuProjectNode::renameFile(const QString &, const QString &) { return true; } QList UbuntuProjectNode::runConfigurations( ) const { return QList(); } ./src/ubuntu/ubuntusettingsclickwidget.ui0000644000015600001650000000442212705421114021061 0ustar jenkinsjenkins UbuntuSettingsClickWidget 0 0 925 512 Form Click Build Targets Automatically check for updates Use local mirror for new chroots QAbstractItemView::NoEditTriggers QAbstractItemView::NoSelection false false 1 Create Click Target Qt::Vertical 20 40 ./src/ubuntu/ubuntufixmanifeststep.h0000644000015600001650000000152012705421114020026 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUFIXMANIFESTSTEP_H #define UBUNTU_INTERNAL_UBUNTUFIXMANIFESTSTEP_H #include namespace Ubuntu { namespace Internal { class UbuntuFixManifestStep : public ProjectExplorer::BuildStep { public: UbuntuFixManifestStep(ProjectExplorer::BuildStepList *bsl); QStringList architectures() const; void setArchitectures(const QStringList &architectures); QString packageDir() const; void setPackageDir(const QString &packageDir); // BuildStep interface virtual bool init(); virtual void run(QFutureInterface &fi); virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); private: QString m_packageDir; QStringList m_architectures; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUFIXMANIFESTSTEP_H ./src/ubuntu/ubuntuversionmanager.cpp0000644000015600001650000000411512705421114020173 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuversionmanager.h" #include #include using namespace Ubuntu::Internal; UbuntuVersionManager *UbuntuVersionManager::m_self = 0; UbuntuVersionManager::UbuntuVersionManager(QObject *parent) : QObject(parent) { m_hostVersion = UbuntuVersion::fromLsbFile(QLatin1String(Constants::LSB_RELEASE)); m_self = this; } UbuntuVersionManager::~UbuntuVersionManager() { if(m_hostVersion) delete m_hostVersion; } void UbuntuVersionManager::detectAvailableVersions() { // no other platforms yet, add support for Touch devices. // now just the desktop platform. } Core::FeatureSet UbuntuVersionManager::availableFeatures(const QString &platformName) const { Q_UNUSED(platformName); if(m_hostVersion) return m_hostVersion->features(); return Core::FeatureSet(); } QStringList UbuntuVersionManager::availablePlatforms() const { QStringList platforms; platforms << QLatin1String(Constants::PLATFORM_DESKTOP); return platforms; } QString UbuntuVersionManager::displayNameForPlatform(const QString &string) const { Q_UNUSED(string); return QString(QLatin1String(Constants::PLATFORM_DESKTOP_DISPLAYNAME)) .arg( (m_hostVersion != 0) ? m_hostVersion->release() : tr("Unknown")); } UbuntuVersionManager *UbuntuVersionManager::instance() { return m_self; } ./src/ubuntu/ubuntupackagestep.h0000644000015600001650000001127112705421114017110 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUPACKAGESTEP_H #define UBUNTU_INTERNAL_UBUNTUPACKAGESTEP_H #include #include #include namespace ProjectExplorer{ class ToolChain; } namespace Ui { class UbuntuPackageStepConfigWidget; } namespace Ubuntu { namespace Internal { class UbuntuPackageStep : public ProjectExplorer::BuildStep { Q_OBJECT Q_PROPERTY(DebugMode debugMode READ debugMode WRITE setDebugMode NOTIFY packageModeChanged) Q_PROPERTY(bool treatClickErrorsAsWarnings READ treatClickErrorsAsWarnings WRITE setTreatClickErrorsAsWarnings NOTIFY treatClickErrorsAsWarningsChanged) public: enum State { Idle, MakeInstall, PreparePackage, ClickBuild, ClickReview }; enum FinishedCheckMode { CheckReturnCode, IgnoreReturnCode }; enum DebugMode { AutoEnableDebugScript, //Deprecated EnableDebugScript, DisableDebugScript }; enum PackageMode { Default, OnlyMakeInstall, OnlyClickBuild }; UbuntuPackageStep(ProjectExplorer::BuildStepList *bsl); UbuntuPackageStep(ProjectExplorer::BuildStepList *bsl, UbuntuPackageStep *other); virtual ~UbuntuPackageStep(); public: // BuildStep interface virtual bool init() override; virtual void run(QFutureInterface &fi) override; virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; virtual bool immutable() const override; virtual bool runInGuiThread() const override; // ProjectConfiguration interface virtual bool fromMap(const QVariantMap &map) override; virtual QVariantMap toMap() const override; QString packagePath () const; DebugMode debugMode() const; void setDebugMode(DebugMode arg); PackageMode packageMode() const; void setPackageMode(const PackageMode &packageMode); QString overrideInstallDir() const; void setOverrideDeployDir(const QString &overrideInstallDir); QString overrideClickWorkingDir() const; void setOverrideClickWorkingDir(const QString &overrideClickWorkingDir); QString clickWorkingDir() const; ProjectExplorer::BuildConfiguration *referenceBuildConfig() const; void setReferenceBuildConfig(ProjectExplorer::BuildConfiguration *referenceBuildConfig); bool cleanDeployDirectory() const; void setCleanDeployDirectory(bool cleanDeployDirectory); bool treatClickErrorsAsWarnings() const; void setTreatClickErrorsAsWarnings(bool arg); signals: void packageModeChanged(DebugMode arg); void currentSubStepFinished(); void treatClickErrorsAsWarningsChanged(bool arg); protected: void internalInit (); void setupAndStartProcess ( const ProjectExplorer::ProcessParameters ¶ms ); bool processFinished (FinishedCheckMode mode = CheckReturnCode); void cleanup (); void stdOutput ( const QString &line ); void stdError ( const QString &line ); QString makeCommand(ProjectExplorer::ToolChain *tc, const Utils::Environment &env) const; protected slots: void doNextStep (); void injectDebugHelperStep (); void onProcessStdOut (); void onProcessStdErr (); void onProcessFailedToStart (); void outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format); void taskAdded (const ProjectExplorer::Task & task); private: State m_state; QString m_lastLine; QString m_clickPackageName; QString m_buildDir; QString m_deployDir; QList m_tasks; QFutureInterface *m_futureInterface; ProjectExplorer::ProcessParameters m_MakeParam; ProjectExplorer::ProcessParameters m_ClickParam; ProjectExplorer::ProcessParameters m_ReviewParam; Utils::QtcProcess *m_process; ProjectExplorer::IOutputParser *m_outputParserChain; DebugMode m_debugMode; PackageMode m_packageMode; QString m_overrideDeployDir; QString m_overrideClickWorkingDir; QPointer m_referenceBuildConfig; bool m_cleanDeployDirectory; bool m_treatClickErrorsAsWarnings; }; class UbuntuPackageStepConfigWidget : public ProjectExplorer::SimpleBuildStepConfigWidget { Q_OBJECT public: UbuntuPackageStepConfigWidget(UbuntuPackageStep *step); ~UbuntuPackageStepConfigWidget(); // BuildStepConfigWidget interface virtual bool showWidget() const; public slots: void updateMode (); void onModeSelected (const int index); void onClickErrorsToggled (const bool checked); private: Ui::UbuntuPackageStepConfigWidget *ui; bool m_isUpdating; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUPACKAGESTEP_H ./src/ubuntu/ubuntuwaitfordevicedialog.cpp0000644000015600001650000000361412705421114021171 0ustar jenkinsjenkins#include "ubuntuwaitfordevicedialog.h" #include namespace Ubuntu{ namespace Internal{ UbuntuWaitForDeviceDialog::UbuntuWaitForDeviceDialog(QWidget *parent) : QProgressDialog(parent) { connect(this,SIGNAL(canceled()),this,SLOT(handleCanceled())); } void UbuntuWaitForDeviceDialog::show(UbuntuDevice::ConstPtr device) { m_dev = device; connect(ProjectExplorer::DeviceManager::instance(),SIGNAL(deviceUpdated(Core::Id)), this,SLOT(handleDeviceUpdated())); setMinimum(0); setMaximum(0); this->open(); handleDeviceUpdated(); } void UbuntuWaitForDeviceDialog::handleDeviceUpdated() { if(!m_dev) { cancel(); return; } if(m_dev->deviceState() == ProjectExplorer::IDevice::DeviceReadyToUse) { ProjectExplorer::DeviceManager::instance()->disconnect(this); accept(); emit deviceReady(); return; } updateLabelText(); } void UbuntuWaitForDeviceDialog::handleCanceled() { ProjectExplorer::DeviceManager::instance()->disconnect(this); } void UbuntuWaitForDeviceDialog::updateLabelText() { bool isEmulator = m_dev->machineType() == ProjectExplorer::IDevice::Emulator; switch (m_dev->deviceState()) { case ProjectExplorer::IDevice::DeviceDisconnected: case ProjectExplorer::IDevice::DeviceStateUnknown: setLabelText(isEmulator ? tr("Please start your emulator: %1").arg(m_dev->displayName()) : tr("Please attach your device: %1").arg(m_dev->displayName())); break; case ProjectExplorer::IDevice::DeviceConnected: setLabelText(tr("Waiting for your %1 to get ready").arg(isEmulator ? tr("emulator") : tr("device"))); break; case ProjectExplorer::IDevice::DeviceReadyToUse: setLabelText(tr("Finished")); break; } } } } ./src/ubuntu/manifestlib.js0000644000015600001650000000635112705421114016043 0ustar jenkinsjenkinsvar jsonData = undefined; function fromJSON($data) { try{ jsonData = JSON.parse($data); }catch(err){ //catch all exceptions return false; } return (jsonData !== undefined) } function toJSON() { return JSON.stringify(jsonData,null,4); } function getName() { return jsonData.name; } function setName($name) { jsonData.name = $name; } function setMaintainer($maintainer) { jsonData.maintainer = $maintainer; } function getMaintainer() { return jsonData.maintainer; } function setTitle($title) { jsonData.title = $title; } function getTitle() { return jsonData.title; } function setVersion($version) { jsonData.version = $version; } function getVersion() { return jsonData.version; } function setArchitecture($architecture) { jsonData.version = $architecture; } function getArchitecture() { return jsonData.architecture; } function setDescription($description) { jsonData.description = $description; } function getDescription() { return jsonData.description; } function getHooks() { return jsonData.hooks; } function setHook($hook) { if(!jsonData.hooks.hasOwnProperty($hook.appId)) jsonData.hooks[$hook.appId] = {}; if($hook.hasOwnProperty("scope")) { jsonData.hooks[$hook.appId]["scope"] = $hook["scope"]; jsonData.hooks[$hook.appId]["apparmor"] = $hook["apparmor"]; } else if ($hook.hasOwnProperty("desktop")) { jsonData.hooks[$hook.appId]["desktop"] = $hook["desktop"]; jsonData.hooks[$hook.appId]["apparmor"] = $hook["apparmor"]; } } function getFrameworkName() { return jsonData.framework; } function setFrameworkName($name) { jsonData.framework = $name; } function getPolicyGroups() { /* var appProfile = jsonData.security.profiles[$appname]; if (appProfile===undefined) { jsonData.security.profiles.push( { $appname: { policy_groups: "" } } ); } return appProfile.policy_groups; */ return jsonData.policy_groups; } function setPolicyGroups($groups) { if ($groups.length === 0) { jsonData.policy_groups = []; return; } var $parsed_groups = $groups.split(" "); jsonData.policy_groups = $parsed_groups; } function getPolicyVersion() { return jsonData.policy_version.toFixed(1).toString(); } function setPolicyVersion($string_version) { jsonData.policy_version = parseFloat($string_version); } function getAppArmorFileName($appId) { if(!jsonData.hooks.hasOwnProperty($appId)) return null; if(!jsonData.hooks[$appId].hasOwnProperty("apparmor")) return null; return jsonData.hooks[$appId].apparmor; } function setAppArmorFileName($appId, $appArmorFile) { if(!jsonData.hooks.hasOwnProperty($appId)) return false; if(!jsonData.hooks[$appId].hasOwnProperty("apparmor")) return false; jsonData.hooks[$appId].apparmor = $appArmorFile; return true; } function injectDebugPolicy () { if(!jsonData.hasOwnProperty("policy_groups")) return false; var debugPolicy= "debug"; for(var i = 0; i < jsonData.policy_groups.length; i++) { if(jsonData.policy_groups[i] === debugPolicy ) return true; } jsonData.policy_groups.push(debugPolicy); return true; } ./src/ubuntu/ubunturemotedebugsupport.cpp0000644000015600001650000001757412705421114021127 0ustar jenkinsjenkins/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "ubunturemotedebugsupport.h" #include "ubunturemoterunconfiguration.h" #include "ubunturemoterunner.h" #include "ubuntuconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { class UbuntuRemoteDebugSupportPrivate { public: UbuntuRemoteDebugSupportPrivate(const UbuntuRemoteRunConfiguration *runConfig, Debugger::DebuggerRunControl *engine) : runControl(engine), qmlDebugging(runConfig->extraAspect()->useQmlDebugger()), cppDebugging(runConfig->extraAspect()->useCppDebugger()), gdbServerPort(-1), qmlPort(-1) { } const QPointer runControl; bool qmlDebugging; bool cppDebugging; QByteArray gdbserverOutput; int gdbServerPort; int qmlPort; }; UbuntuRemoteDebugSupport::UbuntuRemoteDebugSupport(UbuntuRemoteRunConfiguration* runConfig, Debugger::DebuggerRunControl *runControl) : AbstractRemoteRunSupport(runConfig,runControl), d(new UbuntuRemoteDebugSupportPrivate(runConfig, runControl)) { connect(d->runControl, SIGNAL(requestRemoteSetup()), this, SLOT(handleRemoteSetupRequested())); } UbuntuRemoteDebugSupport::~UbuntuRemoteDebugSupport() { delete d; } void UbuntuRemoteDebugSupport::showMessage(const QString &msg, int channel) { if (state() != Idle && d->runControl) d->runControl->showMessage(msg, channel); } void UbuntuRemoteDebugSupport::handleRemoteSetupRequested() { showMessage(tr("Checking available ports...") + QLatin1Char('\n'), Debugger::LogStatus); AbstractRemoteRunSupport::handleRemoteSetupRequested(); } void UbuntuRemoteDebugSupport::startExecution() { QTC_ASSERT(state() == ScanningPorts, return); setState(Starting); if (d->cppDebugging && !assignNextFreePort(&d->gdbServerPort)) return; if (d->qmlDebugging && !assignNextFreePort(&d->qmlPort)) return; d->gdbserverOutput.clear(); UbuntuRemoteClickApplicationRunner *launcher = appRunner(); connect(launcher, SIGNAL(launcherStderr(QByteArray)), SLOT(handleRemoteErrorOutput(QByteArray))); connect(launcher, SIGNAL(launcherStdout(QByteArray)), SLOT(handleRemoteOutput(QByteArray))); if (d->qmlDebugging && !d->cppDebugging) connect(launcher, SIGNAL(clickApplicationStarted(quint16)), SLOT(handleRemoteProcessStarted(quint16))); if(d->cppDebugging) launcher->setCppDebugPort(d->gdbServerPort); if(d->qmlDebugging) launcher->setQmlDebugPort(d->qmlPort); launcher->setEnv(environment()); connect(launcher, SIGNAL(finished(bool)), SLOT(handleAppRunnerFinished(bool))); connect(launcher, SIGNAL(reportError(QString)), SLOT(handleAppRunnerError(QString))); QTC_ASSERT(device()->type().toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID)),return); launcher->start(qSharedPointerCast(device()),clickPackage(),hook()); } void UbuntuRemoteDebugSupport::handleAppRunnerError(const QString &error) { if (state() == Running) { showMessage(error, Debugger::AppError); if (d->runControl) d->runControl->notifyInferiorIll(); } else if (state() != Idle) { handleAdapterSetupFailed(error); } } void UbuntuRemoteDebugSupport::handleAppRunnerFinished(bool success) { if (!d->runControl || state() == Idle) return; if (state() == Running) { // The QML engine does not realize on its own that the application has finished. if (d->qmlDebugging && !d->cppDebugging) d->runControl->quitDebugger(); else if (!success) d->runControl->notifyInferiorIll(); } else if (state() == Starting){ Debugger::RemoteSetupResult res; res.success = false; res.reason = tr("Debugging failed"); d->runControl->notifyEngineRemoteSetupFinished(res); } reset(); } void UbuntuRemoteDebugSupport::handleDebuggingFinished() { setFinished(); reset(); } void UbuntuRemoteDebugSupport::handleRemoteOutput(const QByteArray &output) { showMessage(QString::fromUtf8(output), Debugger::AppOutput); } void UbuntuRemoteDebugSupport::handleRemoteErrorOutput(const QByteArray &output) { if( state() != ScanningPorts ) { if (!d->runControl) return; if (state() == Starting && d->cppDebugging) { d->gdbserverOutput += output; if (d->gdbserverOutput.contains("Listening on port")) { //wait a second for gdb to come up handleAdapterSetupDone(); d->gdbserverOutput.clear(); } } } showMessage(QString::fromUtf8(output), Debugger::AppError); } void UbuntuRemoteDebugSupport::handleProgressReport(const QString &progressOutput) { showMessage(progressOutput + QLatin1Char('\n'), Debugger::LogStatus); } void UbuntuRemoteDebugSupport::handleAdapterSetupFailed(const QString &error) { AbstractRemoteRunSupport::handleAdapterSetupFailed(error); Debugger::RemoteSetupResult result; result.success = false; result.reason = tr("Initial setup failed: %1").arg(error); d->runControl->notifyEngineRemoteSetupFinished(result); } void UbuntuRemoteDebugSupport::handleAdapterSetupDone() { if (state() == AbstractRemoteRunSupport::Running) return; AbstractRemoteRunSupport::handleAdapterSetupDone(); Debugger::RemoteSetupResult result; result.success = true; result.inferiorPid = d->runControl->startParameters().attachPID; result.gdbServerPort = d->gdbServerPort; result.qmlServerPort = d->qmlPort; d->runControl->notifyEngineRemoteSetupFinished(result); } void UbuntuRemoteDebugSupport::handleRemoteProcessStarted(quint16 pid) { QTC_ASSERT(state() == Starting, return); QTC_ASSERT(d->qmlDebugging && !d->cppDebugging, return); d->runControl->startParameters().attachPID = pid; handleAdapterSetupDone(); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubunturemoterunconfiguration.h0000644000015600001650000000666212705421114021441 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUREMOTERUNCONFIGURATION_H #define UBUNTU_INTERNAL_UBUNTUREMOTERUNCONFIGURATION_H #include #include namespace Ui { class UbuntuRemoteRunconfigurationWidget; } namespace Ubuntu { namespace Internal { class UbuntuRemoteRunConfiguration : public RemoteLinux::AbstractRemoteLinuxRunConfiguration { Q_OBJECT Q_PROPERTY(bool forceInstall READ forceInstall WRITE setForceInstall NOTIFY forceInstallChanged) Q_PROPERTY(bool uninstall READ uninstall WRITE setUninstall NOTIFY uninstallChanged) public: UbuntuRemoteRunConfiguration(ProjectExplorer::Target *parent, Core::Id id); UbuntuRemoteRunConfiguration(ProjectExplorer::Target *parent, UbuntuRemoteRunConfiguration *source); // AbstractRemoteLinuxRunConfiguration interface public: virtual QString localExecutableFilePath() const override; virtual QString remoteExecutableFilePath() const override; virtual QStringList arguments() const override; virtual QString workingDirectory() const override; virtual Utils::Environment environment() const override; virtual QStringList soLibSearchPaths () const; bool aboutToStart (QString *errorMessage); // RunConfiguration interface virtual QWidget *createConfigurationWidget() override; virtual bool isEnabled() const override; virtual QString disabledReason() const override; virtual bool isConfigured() const override; virtual ConfigurationState ensureConfigured(QString *) override; // ProjectConfiguration interface virtual bool fromMap(const QVariantMap &map) override; virtual QVariantMap toMap() const override; QString appId () const; QString clickPackage () const; void setArguments (const QStringList &args); QString packageDir () const; void setRunning (const bool set = true); bool forceInstall() const; void setForceInstall(bool forceInstall); bool uninstall() const; void setUninstall(bool uninstall); signals: void forceInstallChanged(bool arg); void uninstallChanged(bool arg); private: QString m_clickPackage; QString m_appId; QString m_localExecutable; QString m_remoteExecutable; QStringList m_arguments; bool m_running; bool m_forceInstall; bool m_uninstall; }; class UbuntuRemoteRunConfigurationWidget : public QWidget { Q_OBJECT public: UbuntuRemoteRunConfigurationWidget(UbuntuRemoteRunConfiguration *config, QWidget *parent = 0); ~UbuntuRemoteRunConfigurationWidget(); private: UbuntuRemoteRunConfiguration *m_config; Ui::UbuntuRemoteRunconfigurationWidget *m_ui; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUREMOTERUNCONFIGURATION_H ./src/ubuntu/ubuntupackagingwidget.ui0000644000015600001650000001463312705421114020144 0ustar jenkinsjenkins UbuntuPackagingWidget 0 0 975 770 Form 0 0 0 0 0 0 0 color: #DD4818 QFrame::Plain 3 Qt::Horizontal 0 50 Ubuntu 26 Ubuntu Publish Qt::Horizontal 40 20 0 20 false color: rgba(0,0,0,0.2) QFrame::Plain 20 0 Qt::Horizontal 0 Validation Validate existing Click package Create and validate Click package Install on device 0 0 0 0 Error Information Type: Description: 16777215 80 true true ./src/ubuntu/ubuntufeatureprovider.h0000644000015600001650000000235312705421114020030 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUFEATUREPROVIDER_H #define UBUNTUFEATUREPROVIDER_H #include #include "ubuntuversionmanager.h" namespace Ubuntu { namespace Internal { class UbuntuFeatureProvider : public Core::IFeatureProvider { public: UbuntuFeatureProvider() {} Core::FeatureSet availableFeatures(const QString &platformName) const override; QStringList availablePlatforms() const override; QString displayNameForPlatform(const QString &string) const override; }; } } #endif // UBUNTUFEATUREPROVIDER_H ./src/ubuntu/ubuntusettingsprojectdefaultspage.h0000644000015600001650000000143512705421114022436 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUSETTINGSPROJECTDEFAULTS_H #define UBUNTU_INTERNAL_UBUNTUSETTINGSPROJECTDEFAULTS_H #include "ui_ubuntusettingsprojectdefaultspage.h" #include #include class QWidget; namespace Ubuntu { namespace Internal { class UbuntuSettingsProjectDefaultsPage: public Core::IOptionsPage { Q_OBJECT public: explicit UbuntuSettingsProjectDefaultsPage(QObject *parent = 0); ~UbuntuSettingsProjectDefaultsPage(); QWidget *widget( ) override; void apply() override; void finish() override; signals: public slots: private: QPointer m_widget; ::Ui::UbuntuSettingsDefaultPage *m_ui; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUSETTINGSPROJECTDEFAULTS_H ./src/ubuntu/ubuntuabstractguieditor.h0000644000015600001650000000357512705421114020350 0ustar jenkinsjenkins/* * Copyright 2014 Digia Plc and/or its subsidiary(-ies). * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUABSTRACTGUIEDITOR_H #define UBUNTU_INTERNAL_UBUNTUABSTRACTGUIEDITOR_H #include #include class QToolBar; class QActionGroup; namespace Ubuntu { namespace Internal { class UbuntuAbstractGuiEditorWidget; class UbuntuAbstractGuiEditor : public Core::IEditor { Q_OBJECT public: UbuntuAbstractGuiEditor(const Core::Context &context); QWidget *toolBar(); UbuntuAbstractGuiEditorWidget *editorWidget() const; Core::IDocument *document(); TextEditor::TextEditorWidget *textEditor() const; int currentLine() const; int currentColumn() const; void gotoLine(int line, int column = 0) { textEditor()->gotoLine(line, column); } protected: virtual UbuntuAbstractGuiEditorWidget * createGuiEditor () = 0; void createUi (); private: void syncCurrentAction (); private slots: void changeEditorPage(QAction *action); private: QString m_displayName; QToolBar *m_toolBar; QActionGroup *m_actionGroup; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUABSTRACTGUIEDITOR_H ./src/ubuntu/ubuntupackagingmodel.h0000644000015600001650000000742212705421114017571 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTUPACKAGINGWIDGET_H #define UBUNTUPACKAGINGWIDGET_H #include #include #include #include #include "ubuntubzr.h" #include "ubuntuclickmanifest.h" #include "ubuntuprocess.h" #include namespace Ui { class UbuntuPackagingWidget; } namespace Ubuntu{ namespace Internal{ class UbuntuValidationResultModel; class ClickRunChecksParser; class UbuntuPackagingModel : public QObject { Q_OBJECT Q_PROPERTY(bool showValidationUi READ showValidationUi WRITE setShowValidationUi NOTIFY showValidationUiChanged) Q_PROPERTY(bool canBuild READ canBuild WRITE setCanBuild NOTIFY canBuildChanged) Q_PROPERTY(QAbstractItemModel* validationModel READ validationModel NOTIFY validationModelChanged) Q_PROPERTY(QString log READ log WRITE setLog NOTIFY logChanged) public: enum ClickPackageTask { None, //after the project has packaged do nothing Verify, //after the project has packaged verify it Install //after the project has packaged install it on the device }; explicit UbuntuPackagingModel(QObject *parent = 0); ~UbuntuPackagingModel(); bool reviewToolsInstalled (); bool showValidationUi() const; bool canBuild() const; QAbstractItemModel* validationModel() const; QString log() const; public slots: void setShowValidationUi(bool arg); void setCanBuild(bool arg); void buildAndInstallPackageRequested (); void buildAndVerifyPackageRequested(); void on_pushButtonClickPackage_clicked(); void on_pushButtonReviewersTools_clicked(); void setLog(QString arg); void appendLog(QString arg); protected slots: void onMessage(QString msg); void onFinished(QString cmd, int code); void onError(QString msg); void onStarted(QString); void onFinishedAction(const QProcess* proc,QString cmd); void checkClickReviewerTool(); void buildFinished (const bool success); void buildPackageRequested(); void targetChanged(); signals: void reviewToolsInstalledChanged(const bool& installed); void showValidationUiChanged(bool arg); void canBuildChanged(bool arg); void validationModelChanged(QAbstractItemModel* arg); void logChanged(QString arg); void beginValidation(); private: void buildClickPackage (); void clearPackageBuildList (); void updateFrameworkList (); void resetValidationResult (); private: bool m_reviewToolsInstalled; QMetaObject::Connection m_UbuntuMenu_connection; QProcess m_click; QString m_projectName; QString m_projectDir; QString m_reply; QString m_reviewesToolsLocation; UbuntuProcess m_ubuntuProcess; UbuntuValidationResultModel *m_validationModel; ClickRunChecksParser* m_inputParser; //packaging support with buildsteps QList > m_packageBuildSteps; QMetaObject::Connection m_buildManagerConnection; ClickPackageTask m_postPackageTask; bool m_showValidationUi; bool m_canBuild; QString m_log; }; } } #endif // UBUNTUPACKAGINGWIDGET_H ./src/ubuntu/ubuntucreatenewchrootdialog.cpp0000644000015600001650000000663312705421114021536 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntucreatenewchrootdialog.h" #include "ui_ubuntucreatenewchrootdialog.h" #include "ubuntuconstants.h" #include namespace Ubuntu { namespace Constants { const char* UBUNTU_CLICK_SUPPORTED_ARCHS[] = {"armhf","i386","amd64","\0"}; //lists all currently supported targets by the plugin const char* UBUNTU_CLICK_SUPPORTED_TARGETS[][3] = { //Series Framework Displayname {"vivid","ubuntu-sdk-15.04","Framework-15.04"}, {"utopic","ubuntu-sdk-14.10","Framework-14.10"}, {"trusty","ubuntu-sdk-14.04","Framework-14.04"}, {"saucy" ,"ubuntu-sdk-13.10","Framework-13.10"}, {"\0","\0","\0"} }; } namespace Internal { UbuntuCreateNewChrootDialog::UbuntuCreateNewChrootDialog(const QString &arch, const QString &framework, QWidget *parent) : QDialog(parent), ui(new Ui::UbuntuCreateNewChrootDialog) { ui->setupUi(this); //add supported targets for(int i = 0; Constants::UBUNTU_CLICK_SUPPORTED_TARGETS[i][0][0] != '\0'; i++){ const QString currFwDisplayName = QLatin1String(Constants::UBUNTU_CLICK_SUPPORTED_TARGETS[i][2]); const QString currFw = QLatin1String(Constants::UBUNTU_CLICK_SUPPORTED_TARGETS[i][1]); if ( framework.isNull() || currFw == framework) ui->comboBoxSeries->addItem(currFwDisplayName,i); } //add supported architectures for(int i = 0; Constants::UBUNTU_CLICK_SUPPORTED_ARCHS[i][0] != '\0' ;i++) { QString currArch = QLatin1String(Constants::UBUNTU_CLICK_SUPPORTED_ARCHS[i]); if( arch.isNull() || currArch == arch ) ui->comboBoxArch->addItem(currArch); } } UbuntuCreateNewChrootDialog::~UbuntuCreateNewChrootDialog() { delete ui; } /** * @brief UbuntuCreateNewChrootDialog::getNewChrootParams * Opens a dialog that lets the user select a new chroot, returns false * if the user pressed cancel */ bool UbuntuCreateNewChrootDialog::getNewChrootTarget(UbuntuClickTool::Target *target, const QString &arch, const QString &framework, QWidget *parent) { UbuntuCreateNewChrootDialog dlg(arch, framework, parent ? parent : Core::ICore::mainWindow()); if( dlg.exec() == QDialog::Accepted) { bool ok = false; int idx = dlg.ui->comboBoxSeries->itemData(dlg.ui->comboBoxSeries->currentIndex()).toInt(&ok); if(!ok) return false; target->architecture = dlg.ui->comboBoxArch->currentText(); target->series = QString::fromLatin1(Constants::UBUNTU_CLICK_SUPPORTED_TARGETS[idx][0]); target->framework = QString::fromLatin1(Constants::UBUNTU_CLICK_SUPPORTED_TARGETS[idx][1]); return true; } return false; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuqmlbuildconfiguration.h0000644000015600001650000001205112705421114021217 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUQMLBUILDCONFIGURATION_H #define UBUNTU_INTERNAL_UBUNTUQMLBUILDCONFIGURATION_H #include #include #include #include namespace ProjectExplorer { class ToolChain; } namespace Ubuntu { namespace Internal { class UbuntuQmlBuildConfiguration : public ProjectExplorer::BuildConfiguration { Q_OBJECT public: UbuntuQmlBuildConfiguration(ProjectExplorer::Target *target); UbuntuQmlBuildConfiguration(ProjectExplorer::Target *target, UbuntuQmlBuildConfiguration *source); // ProjectConfiguration interface virtual bool fromMap(const QVariantMap &map) override; virtual QVariantMap toMap() const override; // BuildConfiguration interface virtual ProjectExplorer::NamedWidget *createConfigWidget() override; virtual BuildType buildType() const override; private: friend class UbuntuQmlBuildConfigurationFactory; }; class UbuntuQmlBuildSettingsWidget : public ProjectExplorer::NamedWidget { Q_OBJECT public: explicit UbuntuQmlBuildSettingsWidget(UbuntuQmlBuildConfiguration *conf, QWidget *parent = 0); private slots: void updateBuildDirectory () const; private: Utils::PathChooser *m_pathChooser; UbuntuQmlBuildConfiguration *m_buildConfiguration; }; class UbuntuQmlUpdateTranslationTemplateStep : public ProjectExplorer::AbstractProcessStep { Q_OBJECT public: UbuntuQmlUpdateTranslationTemplateStep(ProjectExplorer::BuildStepList *bsl); UbuntuQmlUpdateTranslationTemplateStep(ProjectExplorer::BuildStepList *bsl, Core::Id typeId); UbuntuQmlUpdateTranslationTemplateStep(ProjectExplorer::BuildStepList *bsl, UbuntuQmlUpdateTranslationTemplateStep *bs); // BuildStep interface virtual bool init(); virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); QString makeCommand(ProjectExplorer::ToolChain *tc, const Utils::Environment &env) const; }; class UbuntuQmlBuildTranslationStep : public UbuntuQmlUpdateTranslationTemplateStep { Q_OBJECT public: UbuntuQmlBuildTranslationStep(ProjectExplorer::BuildStepList *bsl); UbuntuQmlBuildTranslationStep(ProjectExplorer::BuildStepList *bsl, UbuntuQmlBuildTranslationStep *bs); // BuildStep interface virtual bool init(); virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); void run(QFutureInterface &fi); private: QString m_translationDir; }; class UbuntuQmlBuildConfigurationFactory : public ProjectExplorer::IBuildConfigurationFactory { Q_OBJECT public: UbuntuQmlBuildConfigurationFactory(QObject *parent = 0); ~UbuntuQmlBuildConfigurationFactory(); // IBuildConfigurationFactory interface virtual int priority(const ProjectExplorer::Target *parent) const override; virtual QList availableBuilds(const ProjectExplorer::Target *parent) const override; virtual int priority(const ProjectExplorer::Kit *k, const QString &projectPath) const override; virtual QList availableSetups(const ProjectExplorer::Kit *k, const QString &projectPath) const override; virtual UbuntuQmlBuildConfiguration *create(ProjectExplorer::Target *parent, const ProjectExplorer::BuildInfo *info) const override; virtual bool canRestore(const ProjectExplorer::Target *parent, const QVariantMap &map) const override; virtual UbuntuQmlBuildConfiguration *restore(ProjectExplorer::Target *parent, const QVariantMap &map) override; virtual bool canClone(const ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) const override; virtual UbuntuQmlBuildConfiguration*clone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) override; private: bool canHandle(const ProjectExplorer::Target *t) const; QList createBuildInfos (const ProjectExplorer::Kit *k, const QString &projectDir) const; }; class UbuntuQmlBuildStepFactory : public ProjectExplorer::IBuildStepFactory { Q_OBJECT public: // IBuildStepFactory interface virtual QList availableCreationIds(ProjectExplorer::BuildStepList *parent) const override; virtual QString displayNameForId(const Core::Id id) const override; virtual bool canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const override; virtual ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id) override; virtual bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const override; virtual ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) override; virtual bool canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const override; virtual ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) override; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUQMLBUILDCONFIGURATION_H ./src/ubuntu/ubuntuabstractguieditordocument.cpp0000644000015600001650000000502412705421114022431 0ustar jenkinsjenkins/* * Copyright 2014 Digia Plc and/or its subsidiary(-ies). * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuabstractguieditordocument.h" #include "ubuntuabstractguieditor.h" #include "ubuntuabstractguieditorwidget.h" #include "ubuntuconstants.h" #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuAbstractGuiEditorDocument::UbuntuAbstractGuiEditorDocument * This is basically a bare PlainTextDocument, however in order to be * able to sync between the Widget based and Text based manifest editor * we need to hook into save() and isModified() */ UbuntuAbstractGuiEditorDocument::UbuntuAbstractGuiEditorDocument(const QString &mimeType, UbuntuAbstractGuiEditorWidget *editorWidget) : TextEditor::TextDocument(), m_editorWidget(editorWidget) { setId("UbuntuAbstractGuiEditorDocument"); setMimeType(mimeType); connect(editorWidget, SIGNAL(uiEditorChanged()),this,SIGNAL(changed())); } bool UbuntuAbstractGuiEditorDocument::save(QString *errorString, const QString &fileName, bool autoSave) { if(!m_editorWidget->preSave()) { *errorString = tr("Please check the info box in the editor."); return false; } if(TextDocument::save(errorString, fileName, autoSave)) { m_editorWidget->saved(); return true; } return false; } QString UbuntuAbstractGuiEditorDocument::defaultPath() const { return filePath().toFileInfo().absolutePath(); } QString UbuntuAbstractGuiEditorDocument::suggestedFileName() const { return filePath().toFileInfo().fileName(); } bool UbuntuAbstractGuiEditorDocument::isModified() const { return TextDocument::isModified() || m_editorWidget->isModified(); } bool UbuntuAbstractGuiEditorDocument::isSaveAsAllowed() const { return false; } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuclickmanifest.h0000644000015600001650000000743312705421114017442 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUCLICKMANIFEST_H #define UBUNTUCLICKMANIFEST_H #include #include #include namespace ProjectExplorer { class Project; } namespace Ubuntu { namespace Internal { class UbuntuClickManifest : public QObject { Q_OBJECT public: explicit UbuntuClickManifest(QObject *parent = 0); struct Hook { enum Type{ Invalid, Scope, Application, Helper }; QString appId; QString desktopFile; QString scope; QString appArmorFile; Type type() const; }; signals: void nameChanged(); void maintainerChanged(); void titleChanged(); void policyGroupsChanged(); void policyVersionChanged(); void versionChanged(); void descriptionChanged(); void saved(); void loaded(); void error(); void fileNameChanged(QString); void frameworkNameChanged(const QString &name); void appArmorFileNameChanged (const QString &appId, const QString &name); public slots: void setName(QString name); QString name(); void setMaintainer(QString maintainer); QString maintainer(); void setTitle(QString title); QString title(); void setVersion(QString version); QString version(); void setDescription(QString description); QString description(); void setPolicyGroups(QStringList groups); QStringList policyGroups(); QList hooks (); void setHook (const Hook &hook); void setPolicyVersion(const QString &version); QString policyVersion(); void setFrameworkName (const QString& name); QString frameworkName (); QString appArmorFileName ( const QString &appId ); bool setAppArmorFileName( const QString &appId, const QString &name ); bool enableDebugging(); void save() { save(m_fileName); } void save(QString fileName); bool load(const QString &fileName, ProjectExplorer::Project *proj = 0, QString *errorMessage = 0); bool loadFromString(const QString &data); void reload(); QString raw(); void setRaw(QString); QString fileName() { return m_fileName; } void setFileName(QString fileName) { m_fileName = fileName; emit fileNameChanged(fileName); } bool isInitialized() const { return m_bInitialized; } void nameDashReplaced(){ m_bNameDashReplaced = true; } protected: void callSetFunction(QString functionName, QJSValueList args); void callSetStringListFunction(QString functionName, QStringList args); void callSetStringFunction(QString functionName, QString args); QJSValue callGetFunction(QString functionName, QJSValueList args); QStringList callGetStringListFunction(QString functionName); QStringList callGetStringListFunction(QString functionName, QString args); QString callGetStringFunction(QString functionName); QJSValue callFunction(QString functionName, QJSValueList args); QJSEngine engine; QString m_userName; QString m_fileName; QString m_projectName; bool m_bInitialized; bool m_bNameDashReplaced; }; } } #endif // UBUNTUCLICKMANIFEST_H ./src/ubuntu/settings.h0000644000015600001650000000317112705421114015216 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_SETTINGS_H #define UBUNTU_INTERNAL_SETTINGS_H #include #include #include namespace Utils { class PersistentSettingsWriter; } namespace Ubuntu { namespace Internal { class Settings : public QObject { Q_OBJECT public: struct DeviceConnectivity { QString user = QStringLiteral("phablet"); QString ip = QStringLiteral("127.0.0.1"); int sshPort = 2222; }; struct ProjectDefaults { bool reviewErrorsAsWarnings = false; bool enableDebugHelper = true; bool uninstallAppsByDefault = true; bool overrideAppsByDefault = false; }; struct ChrootSettings { bool useLocalMirror = false; bool autoCheckForUpdates = true; }; explicit Settings(); virtual ~Settings(); void restoreSettings (); static void flushSettings (); static Utils::FileName settingsPath (); static DeviceConnectivity deviceConnectivity (); static void setDeviceConnectivity (const DeviceConnectivity &settings); static ProjectDefaults projectDefaults (); static void setProjectDefaults (const ProjectDefaults &settings); static ChrootSettings chrootSettings (); static void setChrootSettings (const ChrootSettings &settings); static bool deviceAutoToggle (); static void setDeviceAutoToggle (const bool set); private: void migrateSettings (); private: static Settings *m_instance; QVariantMap m_settings; Utils::PersistentSettingsWriter *m_writer = 0; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_SETTINGS_H ./src/ubuntu/ubuntuwelcomemode.h0000644000015600001650000000247612705421120017125 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUWELCOMEMODE_H #define UBUNTUWELCOMEMODE_H #include #include #include namespace Ubuntu { namespace Internal { class UbuntuWelcomePage : public Core::IWelcomePage { Q_OBJECT public: // IWelcomePage interface virtual QUrl pageLocation() const; virtual QString title() const; virtual int priority() const; virtual void facilitateQml(QQmlEngine *engine); virtual Core::Id id() const; public slots: void newProject(); void openProject(); void openGallery(); }; } // Internal } // Ubuntu #endif // UBUNTUWELCOMEMODE_H ./src/ubuntu/ubuntuprocess.h0000644000015600001650000000422712705421114016302 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUPROCESS_H #define UBUNTUPROCESS_H #include #include #include #include "ubuntu_global.h" namespace Ubuntu { namespace Internal { class UBUNTUSHARED_EXPORT UbuntuProcess : public QObject { Q_OBJECT public: explicit UbuntuProcess(QObject *parent = 0); void clear() { m_pendingProcesses.clear(); } void append(QStringList cmds) { m_pendingProcesses << cmds; } QProcess::ProcessState state() { return m_currentProcess->state(); } public slots: void stop(); void start(QString taskTitle); signals: void message(const QString&); void error (const QString&); void stdOut(const QString&); void finished(QString,int); void finished(const QProcess*,QString,int); void started(QString); protected slots: void processStarted(); void processReadyRead(); void processFinished(int code); void processError(QProcess::ProcessError error); void processCmdQueue(); protected: void close(); void kill(); QList m_pendingProcesses; QProcess *m_currentProcess; QFutureInterface *m_futureInterface; void initializeProgressBar(QString title, int max); void setProgressBarFinished(); void increaseProgress(QString msg); void setProgressBarCancelled(); void setProgressBarStarted(); bool m_bForceStop; private: Q_DISABLE_COPY(UbuntuProcess) }; } // Internal } // Ubuntu #endif // UBUNTUPROCESS_H ./src/ubuntu/ubuntudevice.h0000644000015600001650000002000512705421114016053 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUDEVICE_H #define UBUNTU_INTERNAL_UBUNTUDEVICE_H #include #include #include #include #include #include class IUbuntuDeviceNotifier; namespace Ubuntu { namespace Internal { class UbuntuDevice; class UbuntuDeviceFactory; class UbuntuDeviceHelper : public QObject { Q_OBJECT friend class UbuntuDevice; public: explicit UbuntuDeviceHelper(UbuntuDevice* dev); ~UbuntuDeviceHelper(); UbuntuDevice *device () const; QString log () const; void refresh (); signals: void beginQueryDevice (); void endQueryDevice (); void detectionStateChanged (); void connected (); void disconnected (); void deviceNeedsSetup (); void featureDetected (); void deviceInfoUpdated (); void message (const QString &message); protected: void init (); void waitForEmulatorStart(); void waitForBoot (); void detect (); void detectOpenSsh(); void startSshService(); void enableDeveloperMode(); void disableDeveloperMode(); void detectHasNetworkConnection(); void detectDeviceVersion(); void detectDeviceWritableImage(); void detectDeveloperTools(); void beginAction(QString msg); void endAction(QString msg); void resetToDefaults(); void enableRWImage (); void disableRWImage (); void enablePortForward (); void installDevTools (); void removeDevTools (); void deployPublicKey (); void cloneNetwork (); void startProcess (const QString& command); protected slots: void onProcessReadyRead (); void onProcessFinished (const int code); void onProcessError (const QProcess::ProcessError error); void onProcessStateChanged (QProcess::ProcessState newState); void onMessage(const QString &msg); void onError(const QString &error); void onStdOut(const QString &stdout); void processFinished (const QString&, const int code); void deviceConnected (); void deviceDisconnected (); protected: void readProcessOutput (QProcess* proc); void stopProcess (); void addToLog (const QString &msg); void setProcessState (const int newState); private: QString m_log; QString m_reply; int m_clonedNwCount; int m_errorCount; UbuntuDevice *m_dev; QProcess *m_process; IUbuntuDeviceNotifier *m_deviceWatcher; }; class UbuntuDevice : public RemoteLinux::LinuxDevice { Q_DECLARE_TR_FUNCTIONS(Ubuntu::Internal::UbuntuDevice) friend class UbuntuDeviceHelper; friend class UbuntuDeviceFactory; public: enum FeatureState { NotAvailable, //Feature is not available on the device Unknown, //Feature is not detected yet Available //Feature is available on the device }; enum ProcessState { NotStarted, WaitForEmulatorStart, WaitForBootAdbAccess, WaitForBootDeviceLock, WaitForBoot, DetectDeviceVersion, DetectNetworkConnection, CloneNetwork, DetectOpenSSH, InstallOpenSSH, RemoveOpenSSH, StartOpenSSH, EnablePortForwarding, DeployPublicKey, DetectDeviceWriteableImage, DetectDeveloperTools, FirstNonCriticalTask, EnableRWImage, DisableRWImage, InstallDevTools, RemoveDevTools, Done, Failed, MaxState = Failed }; typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~UbuntuDevice (); static Ptr create(); static Ptr create(const QString &name,const QString &serial, MachineType machineType, const QString &archName, Origin origin = ManuallyAdded); QString serialNumber () const; UbuntuDeviceHelper *helper () const; void cloneNetwork (); void openTerminal (); void enablePortForward (); void deployPublicKey (); void setDeveloperModeEnabled ( const bool enabled = true ); void setWriteableImageEnabled ( const bool enabled = true ); void setDeveloperToolsInstalled ( const bool installed = true ); void setDeviceInfo (const QString &productInfo, const QString &modelInfo, const QString &deviceInfo); QString modelInfo() const; QString deviceInfo() const; QString productInfo() const; QString imageName() const; QString architecture() const; QString framework() const; void setEmulatorInfo (const QString &ubuntuVersion, const QString &deviceVersion, const QString &imageVersion); QString ubuntuVersion() const; QString deviceVersion() const; QString imageVersion() const; bool startEmulator (); FeatureState developerModeEnabled () const; FeatureState hasNetworkConnection () const; FeatureState hasWriteableImage () const; FeatureState hasDeveloperTools () const; ProcessState detectionState () const; QString detectionStateString () const; QString scaleFactor () const; bool setScaleFactor (const QString &factor); QString memorySetting () const; bool setMemorySetting (const QString &memory); // IDevice interface virtual ProjectExplorer::IDeviceWidget *createWidget() override; virtual QList actionIds() const override; virtual QString displayType() const override; virtual ProjectExplorer::IDevice::Ptr clone() const override; virtual void fromMap(const QVariantMap &map) override; virtual QVariantMap toMap() const override; virtual ProjectExplorer::DeviceProcess *createProcess(QObject *parent) const override; virtual ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override; Ptr sharedFromThis (); ConstPtr sharedFromThis() const; protected: UbuntuDevice(); UbuntuDevice(const QString &name,MachineType machineType, Origin origin, Core::Id id, const QString &architecture); UbuntuDevice(const UbuntuDevice &other); void loadDefaultConfig(); void setupPrivateKey (); private: UbuntuDeviceHelper* m_helper; FeatureState m_openSSHStarted; FeatureState m_hasOpenSSHServer; FeatureState m_hasNetworkConnection; FeatureState m_hasWriteableImage; FeatureState m_hasDeveloperTools; ProcessState m_processState; QString m_deviceInfo; QString m_modelInfo; QString m_productInfo; QString m_emulatorSerial; QString m_ubuntuVersion; QString m_deviceVersion; QString m_imageVersion; QString m_scaleFactor; QString m_memory; QString m_framework; Utils::PortList m_localForwardedPorts; private: UbuntuDevice &operator=(const UbuntuDevice &); }; class UbuntuDeviceProcess : public RemoteLinux::LinuxDeviceProcess { Q_OBJECT public: explicit UbuntuDeviceProcess(const QSharedPointer &device, QObject *parent = 0); void setWorkingDirectory(const QString &directory) override; // DeviceProcess interface public: virtual void terminate() override; private: // SshDeviceProcess interface virtual QString fullCommandLine() const override; QString m_workingDir; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUDEVICE_H ./src/ubuntu/ubuntuqtversion.cpp0000644000015600001650000000615612705421114017214 0ustar jenkinsjenkins#include "ubuntuqtversion.h" #include "ubuntuconstants.h" #include "settings.h" #include #include #include #include namespace Ubuntu { namespace Internal { const char SCRIPT_VERSION_KEY[] = "UbuntuQtVersion.ScriptVersion"; /*! * \brief MIN_SCRIPT_VERSION * Increment this version if all qmake scripts in /ubuntu-sdk * need to be recreated */ const int MIN_SCRIPT_VERSION = 2; UbuntuQtVersion::UbuntuQtVersion() : BaseQtVersion(), m_scriptVersion(MIN_SCRIPT_VERSION) { } UbuntuQtVersion::UbuntuQtVersion(const Utils::FileName &path, bool isAutodetected, const QString &autodetectionSource) : BaseQtVersion(path, isAutodetected, autodetectionSource), m_scriptVersion(MIN_SCRIPT_VERSION) { setUnexpandedDisplayName(defaultUnexpandedDisplayName(path, false)); } UbuntuQtVersion::~UbuntuQtVersion() {} void UbuntuQtVersion::fromMap(const QVariantMap &map) { BaseQtVersion::fromMap(map); m_scriptVersion = map.value(QLatin1String(SCRIPT_VERSION_KEY),0).toInt(); } QVariantMap UbuntuQtVersion::toMap() const { QVariantMap map = BaseQtVersion::toMap(); map.insert(QLatin1String(SCRIPT_VERSION_KEY),m_scriptVersion); return map; } UbuntuQtVersion *UbuntuQtVersion::clone() const { return new UbuntuQtVersion(*this); } QString UbuntuQtVersion::type() const { return QLatin1String(Constants::UBUNTU_QTVERSION_TYPE); } QList UbuntuQtVersion::detectQtAbis() const { return qtAbisFromLibrary(qtCorePaths(versionInfo(), qtVersionString())); } QString UbuntuQtVersion::description() const { return QCoreApplication::translate("QtVersion", "Ubuntu Phone", "Qt Version is used for Ubuntu Phone development"); } QString UbuntuQtVersion::platformName() const { return QLatin1String(Constants::UBUNTU_PLATFORM_NAME); } QString UbuntuQtVersion::platformDisplayName() const { return QLatin1String(Constants::UBUNTU_PLATFORM_NAME_TR); } int UbuntuQtVersion::scriptVersion() const { return m_scriptVersion; } void UbuntuQtVersion::setScriptVersion(int scriptVersion) { m_scriptVersion = scriptVersion; } int UbuntuQtVersion::minimalScriptVersion() { return MIN_SCRIPT_VERSION; } bool UbuntuQtVersionFactory::canRestore(const QString &type) { return type == QLatin1String(Constants::UBUNTU_QTVERSION_TYPE); } QtSupport::BaseQtVersion *UbuntuQtVersionFactory::restore(const QString &type, const QVariantMap &data) { if(!canRestore(type)) return 0; UbuntuQtVersion *v = new UbuntuQtVersion(); v->fromMap(data); return v; } int UbuntuQtVersionFactory::priority() const { return 100; } QtSupport::BaseQtVersion *UbuntuQtVersionFactory::create(const Utils::FileName &qmakePath, ProFileEvaluator *evaluator, bool isAutoDetected, const QString &autoDetectionSource) { Q_UNUSED(evaluator); //we only care about our qmakes if(!qmakePath.toFileInfo().absolutePath().contains(Settings::settingsPath().toString())) return 0; return new UbuntuQtVersion(qmakePath,isAutoDetected,autoDetectionSource); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuhtmlbuildconfiguration.h0000644000015600001650000000554712705421114021406 0ustar jenkinsjenkins#ifndef UBUNTU_INTERNAL_UBUNTUHTMLBUILDCONFIGURATION_H #define UBUNTU_INTERNAL_UBUNTUHTMLBUILDCONFIGURATION_H #include #include #include namespace Ubuntu { namespace Internal { class UbuntuHtmlBuildConfiguration : public ProjectExplorer::BuildConfiguration { Q_OBJECT public: UbuntuHtmlBuildConfiguration(ProjectExplorer::Target *target); UbuntuHtmlBuildConfiguration(ProjectExplorer::Target *target, UbuntuHtmlBuildConfiguration *source); // ProjectConfiguration interface virtual bool fromMap(const QVariantMap &map) override; virtual QVariantMap toMap() const override; // BuildConfiguration interface virtual ProjectExplorer::NamedWidget *createConfigWidget() override; virtual BuildType buildType() const override; private: friend class UbuntuHtmlBuildConfigurationFactory; }; class UbuntuHtmlBuildSettingsWidget : public ProjectExplorer::NamedWidget { Q_OBJECT public: explicit UbuntuHtmlBuildSettingsWidget(UbuntuHtmlBuildConfiguration *conf, QWidget *parent = 0); private slots: void updateBuildDirectory () const; private: Utils::PathChooser *m_pathChooser; UbuntuHtmlBuildConfiguration *m_buildConfiguration; }; class UbuntuHtmlBuildConfigurationFactory : public ProjectExplorer::IBuildConfigurationFactory { Q_OBJECT public: UbuntuHtmlBuildConfigurationFactory(QObject *parent = 0); ~UbuntuHtmlBuildConfigurationFactory(); // IBuildConfigurationFactory interface virtual int priority(const ProjectExplorer::Target *parent) const override; virtual QList availableBuilds(const ProjectExplorer::Target *parent) const override; virtual int priority(const ProjectExplorer::Kit *k, const QString &projectPath) const override; virtual QList availableSetups(const ProjectExplorer::Kit *k, const QString &projectPath) const override; virtual UbuntuHtmlBuildConfiguration *create(ProjectExplorer::Target *parent, const ProjectExplorer::BuildInfo *info) const override; virtual bool canRestore(const ProjectExplorer::Target *parent, const QVariantMap &map) const override; virtual UbuntuHtmlBuildConfiguration *restore(ProjectExplorer::Target *parent, const QVariantMap &map) override; virtual bool canClone(const ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) const override; virtual UbuntuHtmlBuildConfiguration*clone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) override; private: bool canHandle(const ProjectExplorer::Target *t) const; QList createBuildInfos (const ProjectExplorer::Kit *k, const QString &projectDir) const; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUHTMLBUILDCONFIGURATION_H ./src/ubuntu/ubuntukitmanager.cpp0000644000015600001650000004471412705421114017306 0ustar jenkinsjenkins#include "ubuntukitmanager.h" #include "clicktoolchain.h" #include "ubuntuconstants.h" #include "ubuntudevice.h" #include "ubuntuclickdialog.h" #include "ubuntuqtversion.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { enum { debug = 0 }; static bool equalKits(ProjectExplorer::Kit *a, ProjectExplorer::Kit *b) { return ProjectExplorer::ToolChainKitInformation::toolChain(a) == ProjectExplorer::ToolChainKitInformation::ToolChainKitInformation::toolChain(b); } static bool lessThanToolchain (const ClickToolChain* left, const ClickToolChain* right) { const UbuntuClickTool::Target &leftTarget = left->clickTarget(); const UbuntuClickTool::Target &rightTarget = right->clickTarget(); if(leftTarget.majorVersion < rightTarget.majorVersion) return true; if(leftTarget.minorVersion < rightTarget.minorVersion) return true; return false; } UbuntuKitManager::UbuntuKitManager() { } QList UbuntuKitManager::clickToolChains() { QList toolchains; // having a empty toolchains list will remove all autodetected kits for android // exactly what we want in that case foreach (ProjectExplorer::ToolChain *tc, ProjectExplorer::ToolChainManager::toolChains()) { if(tc) { if (!tc->isAutoDetected()) continue; if (tc->type() != QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) continue; toolchains << static_cast(tc); } } return toolchains; } UbuntuQtVersion *UbuntuKitManager::createOrFindQtVersion(ClickToolChain *tc) { QString qmakePath = UbuntuClickTool::findOrCreateQMakeWrapper(tc->clickTarget()); if(!QFile::exists(qmakePath)) { return 0; } else { //try to find a already existing Qt Version for this chroot foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) { if (qtVersion->type() != QLatin1String(Constants::UBUNTU_QTVERSION_TYPE)) continue; if (qtVersion->qmakeCommand().toFileInfo().absoluteFilePath() == QFileInfo(qmakePath).absoluteFilePath()) return static_cast (qtVersion); } } UbuntuQtVersion *qtVersion = new UbuntuQtVersion(Utils::FileName::fromString(qmakePath),false); QtSupport::QtVersionManager::addVersion(qtVersion); return qtVersion; } CMakeProjectManager::CMakeTool *UbuntuKitManager::createOrFindCMakeTool(ClickToolChain *tc) { QString cmakePathStr = UbuntuClickTool::findOrCreateToolWrapper(QStringLiteral("cmake"), tc->clickTarget()); Utils::FileName cmakePath = Utils::FileName::fromString(cmakePathStr); CMakeProjectManager::CMakeTool *cmake = CMakeProjectManager::CMakeToolManager::findByCommand(cmakePath); if (cmake) return cmake; cmake = createCMakeTool(tc); if (!CMakeProjectManager::CMakeToolManager::registerCMakeTool(cmake)) { delete cmake; return 0; } return cmake; } CMakeProjectManager::CMakeTool *UbuntuKitManager::createCMakeTool(ClickToolChain *tc) { return createCMakeTool(tc->clickTarget()); } CMakeProjectManager::CMakeTool *UbuntuKitManager::createCMakeTool(const UbuntuClickTool::Target &target) { QString cmakePathStr = UbuntuClickTool::findOrCreateToolWrapper(QStringLiteral("cmake"), target); Utils::FileName cmakePath = Utils::FileName::fromString(cmakePathStr); CMakeProjectManager::CMakeTool *cmake = new CMakeProjectManager::CMakeTool(CMakeProjectManager::CMakeTool::AutoDetection); cmake->setCMakeExecutable(cmakePath); cmake->setDisplayName(tr("Ubuntu SDK cmake (%1-%2-%3)") .arg(target.architecture) .arg(target.framework) .arg(target.series)); return cmake; } void UbuntuKitManager::autoCreateKit(UbuntuDevice::Ptr device) { ProjectExplorer::Abi requiredAbi = ClickToolChain::architectureNameToAbi(device->architecture()); if(requiredAbi.isNull()) { QMessageBox::warning(Core::ICore::mainWindow(), tr("Unknown device architecture"), tr("Kit autocreation for %1 is not supported!") .arg(device->architecture())); return; } if(device->framework().isEmpty()) { QMessageBox::warning(Core::ICore::mainWindow(), tr("Device framework is unknown."), tr("The supported framework of the device is not known, please make sure to redetect the device features.")); return; } QList toolchains = clickToolChains(); auto findCompatibleTc = [&](){ ClickToolChain* match = 0; if(toolchains.size() > 0) { qSort(toolchains.begin(),toolchains.end(),lessThanToolchain); for( int i = toolchains.size() -1; i >= 0; i-- ) { ClickToolChain* tc = toolchains[i]; if (tc->clickTarget().framework != device->framework()) continue; if( tc->targetAbi() == requiredAbi ) { match = tc; break; } //the abi is compatible but not exactly the same //lets continue and see if we find a better candidate if(tc->targetAbi().isCompatibleWith(requiredAbi)) match = tc; } } return match; }; //search a tk with a compatible arch ClickToolChain* match = findCompatibleTc(); while(!match) { //create target int choice = QMessageBox::question(Core::ICore::mainWindow(), tr("No target available"), tr("There is no compatible chroot available on your system, do you want to create it now?")); if(choice == QMessageBox::Yes) { if(!UbuntuClickDialog::createClickChrootModal(false, device->architecture(), device->framework())) return; toolchains = clickToolChains(); match = findCompatibleTc(); } else return; } ProjectExplorer::Kit* newKit = createKit(match); if(newKit) { fixKit(newKit); newKit->setUnexpandedDisplayName(tr("%1 (GCC %2-%3-%4)") .arg(device->displayName()) .arg(match->clickTarget().architecture) .arg(match->clickTarget().framework) .arg(match->clickTarget().series)); ProjectExplorer::DeviceKitInformation::setDevice(newKit,device); ProjectExplorer::KitManager::registerKit(newKit); } } void UbuntuKitManager::autoDetectKits() { //destroy all obsolete QtVersions, they are recreated later on in fixKit() foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) { if (qtVersion->type() != QLatin1String(Constants::UBUNTU_QTVERSION_TYPE)) continue; UbuntuQtVersion* ver = static_cast (qtVersion); if(ver->scriptVersion() < UbuntuQtVersion::minimalScriptVersion() || !qtVersion->isValid()) { //we need to remove that QtVersion QFile::remove(ver->qmakeCommand().toString()); QtSupport::QtVersionManager::removeVersion(ver); } } // having a empty toolchains list will remove all autodetected kits for ubuntu // exactly what we want in that case QList toolchains = clickToolChains(); QList existingKits; foreach (ProjectExplorer::Kit *k, ProjectExplorer::KitManager::kits()) { if (k->isSdkProvided()) continue; ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(k); if (tc && tc->type() != QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) continue; //@TODO check for ubuntu device information if(debug) qDebug()<<"Found possible Ubuntu Kit: "<displayName(); existingKits << k; } // create new kits QList newKits; foreach (ClickToolChain *tc, toolchains) { ProjectExplorer::Kit* newKit = createKit(tc); newKit->makeSticky(); newKits << newKit; } //remove already existing kits for (int i = existingKits.count() - 1; i >= 0; --i) { ProjectExplorer::Kit *existingKit = existingKits.at(i); for (int j = 0; j < newKits.count(); ++j) { ProjectExplorer::Kit *newKit = newKits.at(j); if (equalKits(existingKit, newKit)) { // Kit is already registered, nothing to do ProjectExplorer::Kit *oldKit = existingKits.takeAt(i); oldKit->makeSticky(); //make sure kit has all required informations fixKit(oldKit); newKits.removeAt(j); ProjectExplorer::KitManager::deleteKit(newKit); j = newKits.count(); } } } //all kits remaining need to be removed if they don't have all informations foreach (ProjectExplorer::Kit *k, existingKits) { ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(k); CMakeProjectManager::CMakeTool* cmake = CMakeProjectManager::CMakeKitInformation::cmakeTool(k); if (tc && tc->type() == QLatin1String(Constants::UBUNTU_CLICK_TOOLCHAIN_ID) && cmake //&& icmake->id().toString().startsWith(QLatin1String(Constants::UBUNTU_CLICK_CMAKE_TOOL_ID)) && cmake->isValid()) { fixKit(k); //existing targets are not autodetected anymore k->makeUnSticky(); k->setAutoDetected(false); } else { //has not all informations, go away ProjectExplorer::KitManager::deregisterKit(k); } } foreach (ProjectExplorer::Kit *kit, newKits) { ClickToolChain *tc = static_cast(ProjectExplorer::ToolChainKitInformation::toolChain(kit)); kit->setUnexpandedDisplayName(tr("UbuntuSDK for %1 (GCC %2-%3)") .arg(tc->clickTarget().architecture) .arg(tc->clickTarget().framework) .arg(tc->clickTarget().series)); ProjectExplorer::KitManager::registerKit(kit); fixKit(kit); } auto cmakeUpdater = [](const Core::Id &id){ CMakeProjectManager::CMakeTool *tool = CMakeProjectManager::CMakeToolManager::findById(id); if (!tool) return; QString basePath = Settings::settingsPath().toString(); if (tool->cmakeExecutable().toString().startsWith(basePath)) { qDebug()<<"Setting mapper to "<displayName(); tool->setPathMapper(&UbuntuClickTool::mapIncludePathsForCMake); } else { qDebug()<<"Unsetting mapper from "<displayName(); tool->setPathMapper(CMakeProjectManager::CMakeTool::PathMapper()); } }; connect(CMakeProjectManager::CMakeToolManager::instance(), &CMakeProjectManager::CMakeToolManager::cmakeAdded, cmakeUpdater); connect(CMakeProjectManager::CMakeToolManager::instance(), &CMakeProjectManager::CMakeToolManager::cmakeRemoved, cmakeUpdater); } /*! * \brief UbuntuKitManager::createKit * Creates a new Kit for the Ubunut toolchain and sets default * values */ ProjectExplorer::Kit *UbuntuKitManager::createKit(ClickToolChain *tc) { //@TODO find a qt version ProjectExplorer::Kit* newKit = new ProjectExplorer::Kit; newKit->setAutoDetected(false); //let the user delete that stuff newKit->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::UBUNTU_ICON))); ProjectExplorer::ToolChainKitInformation::setToolChain(newKit, tc); CMakeProjectManager::CMakeTool *cmake = createOrFindCMakeTool(tc); if (cmake) { cmake->setPathMapper(&UbuntuClickTool::mapIncludePathsForCMake); CMakeProjectManager::CMakeKitInformation::setCMakeTool(newKit, cmake->id()); } ProjectExplorer::SysRootKitInformation::setSysRoot(newKit,Utils::FileName::fromString(UbuntuClickTool::targetBasePath(tc->clickTarget()))); //we always want a ubuntu device Core::Id devTypeId = Core::Id(Constants::UBUNTU_DEVICE_TYPE_ID).withSuffix(tc->clickTarget().architecture); ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(newKit,devTypeId); //@TODO add gdbserver support QtSupport::QtKitInformation::setQtVersion(newKit, createOrFindQtVersion(tc)); return newKit; } /*! * \brief UbuntuKitManager::createOrFindDebugger * Tries to find a already existing ubuntu debugger, if it can not find one * it is registered and returned */ QVariant UbuntuKitManager::createOrFindDebugger(const Utils::FileName &path) { if(path.isEmpty()) return QVariant(); QList debuggers = Debugger::DebuggerItemManager::debuggers(); foreach(const Debugger::DebuggerItem& debugger,debuggers) { if(debugger.command() == path) { return debugger.id(); } } Debugger::DebuggerItem debugger; debugger.setCommand(path); debugger.setEngineType(Debugger::GdbEngineType); debugger.setUnexpandedDisplayName(tr("Ubuntu SDK Debugger")); debugger.setAutoDetected(true); //multiarch debugger ProjectExplorer::Abi abi(ProjectExplorer::Abi::UnknownArchitecture ,ProjectExplorer::Abi::LinuxOS ,ProjectExplorer::Abi::GenericLinuxFlavor ,ProjectExplorer::Abi::UnknownFormat ,0); debugger.setAbi(abi); return Debugger::DebuggerItemManager::registerDebugger(debugger); } /*! * \brief UbuntuKitManager::fixKit * Tries to fix a Kit if there is missing information */ void UbuntuKitManager::fixKit(ProjectExplorer::Kit *k) { k->setAutoDetected(false); ClickToolChain* tc = static_cast (ProjectExplorer::ToolChainKitInformation::toolChain(k)); if(!tc) { return; } //make sure we have the multiarch debugger QVariant dId = createOrFindDebugger(tc->suggestedDebugger()); const Debugger::DebuggerItem *debugger = Debugger::DebuggerKitInformation::debugger(k); if(!debugger) { if(dId.isValid()) Debugger::DebuggerKitInformation::setDebugger(k,dId); } else if (debugger->id() != dId){ if(dId.isValid()) Debugger::DebuggerKitInformation::setDebugger(k,dId); } if(ProjectExplorer::SysRootKitInformation::sysRoot(k).isEmpty()) { ProjectExplorer::SysRootKitInformation::setSysRoot(k,Utils::FileName::fromString(UbuntuClickTool::targetBasePath(tc->clickTarget()))); } //make sure we point to a ubuntu device Core::Id devId = ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(k); if ( !devId.isValid() || //invalid type devId == Constants::UBUNTU_DEVICE_TYPE_ID || //Kit uses still the old device type ids !devId.toString().startsWith(QLatin1String(Constants::UBUNTU_DEVICE_TYPE_ID)) //kit has a wrong device type ) { //if this kit still uses the old type ids, we try to find the correct device by name bool doMigration = (devId == Constants::UBUNTU_DEVICE_TYPE_ID); //a old kit with a incorrect device ID, lets set the correct device type id Core::Id devTypeId = Core::Id(Constants::UBUNTU_DEVICE_TYPE_ID).withSuffix(tc->clickTarget().architecture); ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(k,devTypeId); if (doMigration) { UbuntuDevice::ConstPtr fuzzyMatch; UbuntuDevice::ConstPtr fullMatch; //lets search for a device ProjectExplorer::DeviceManager *devMgr = ProjectExplorer::DeviceManager::instance(); for (int i = 0; ideviceCount(); i++) { ProjectExplorer::IDevice::ConstPtr dev = devMgr->deviceAt(i); if(!dev) continue; //the type ID also checks if the architecture is correct if(dev->type() != devTypeId) continue; UbuntuDevice::ConstPtr ubuntuDev = qSharedPointerCast(dev); //we found a possible Device! if(!fuzzyMatch) fuzzyMatch = ubuntuDev; //this is most likely the device that was used with this kit by using the autocreate button QRegularExpression regExp (QStringLiteral("^(%1\\s+\\(.*\\))$").arg(ubuntuDev->displayName())); QRegularExpressionMatch m = regExp.match(ubuntuDev->displayName()); if (m.hasMatch()) { fullMatch = ubuntuDev; break; } } ProjectExplorer::DeviceKitInformation::setDevice(k,!fullMatch.isNull() ? fullMatch : fuzzyMatch); } } //values the user can change k->setSticky(ProjectExplorer::DeviceKitInformation::id(),false); k->setSticky(Debugger::DebuggerKitInformation::id(),false); //values the user cannot change k->setSticky(ProjectExplorer::SysRootKitInformation::id(),true); k->setMutable(ProjectExplorer::SysRootKitInformation::id(),false); //make sure we use a ubuntu Qt version QtSupport::QtKitInformation::setQtVersion(k, createOrFindQtVersion(tc)); //make sure we use a ubuntu cmake CMakeProjectManager::CMakeTool *cmake = createOrFindCMakeTool(tc); if(cmake) { cmake->setPathMapper(&UbuntuClickTool::mapIncludePathsForCMake); CMakeProjectManager::CMakeKitInformation::setCMakeTool(k, cmake->id()); } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntusettingsprojectdefaultspage.ui0000644000015600001650000000437112705421114022626 0ustar jenkinsjenkins UbuntuSettingsDefaultPage 0 0 720 300 Form Project default settings This settings are used to initialize new project configurations, they will not change existing projects. true Enable debug helper Override apps on the device if they are installed Uninstall apps from the device after running Treat click review errors as warnings Qt::Vertical 20 40 ./src/ubuntu/ubuntuclicktool.h0000644000015600001650000001064412705421114016607 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTU_INTERNAL_UBUNTUCLICKTOOL_H #define UBUNTU_INTERNAL_UBUNTUCLICKTOOL_H #include #include #include #include #include #include #include #include class QDialogButtonBox; class QPlainTextEdit; class QLabel; class QAction; class QTimer; class QNetworkAccessManager; class QNetworkReply; namespace ProjectExplorer { class Project; class Target; class Kit; } namespace Ubuntu { namespace Internal { class UbuntuClickBuildConfiguration; class UbuntuClickTool { public: enum MaintainMode { Upgrade,//runs click chroot upgrade Delete //runs click chroot delete }; struct Target { bool maybeBroken; int majorVersion; int minorVersion; QString series; QString framework; QString architecture; }; UbuntuClickTool(); static void parametersForCreateChroot (const Target &target, ProjectExplorer::ProcessParameters* params); static void parametersForMaintainChroot (const MaintainMode &mode,const Target& target,ProjectExplorer::ProcessParameters* params); static void openChrootTerminal (const Target& target); static QString targetBasePath (const Target& target); static bool getTargetFromUser (Target* target, const QString &framework=QString()); static QStringList getSupportedFrameworks (const Target *target); static QString getMostRecentFramework ( const QString &subFramework, const Target *target ); static QString findOrCreateGccWrapper(const UbuntuClickTool::Target &target); static QString findOrCreateToolWrapper(const QString &tool, const UbuntuClickTool::Target &target); static QString findOrCreateQMakeWrapper(const UbuntuClickTool::Target &target); static QString findOrCreateMakeWrapper(const UbuntuClickTool::Target &target); static QString mapIncludePathsForCMake(ProjectExplorer::Kit *k, const QString &in); static bool targetExists (const Target& target); static QList listAvailableTargets (const QString &framework=QString()); static QPair targetVersion (const Target& target); static bool targetFromPath(const QString& targetPath, Target* tg); static const Target *clickTargetFromTarget(ProjectExplorer::Target *t); static QString clickChrootSuffix (); static QString m_strClickChrootSuffix; }; QDebug operator<<(QDebug dbg, const UbuntuClickTool::Target& t); class UbuntuClickFrameworkProvider : public QObject { Q_OBJECT public: UbuntuClickFrameworkProvider(); static UbuntuClickFrameworkProvider *instance(); QStringList supportedFrameworks() const; QString mostRecentFramework ( const QString &subFramework); QString frameworkPolicy (const QString &fw) const; static QStringList getSupportedFrameworks (); static QString getMostRecentFramework ( const QString &subFramework); static QString getBaseFramework (const QString &framework, QStringList *extensions = 0); signals: void frameworksUpdated(); private slots: void requestFinished (); void requestError (); void updateFrameworks (bool force = false); private: void readCache (); void readDefaultValues (); QStringList parseData ( const QByteArray &data) const; private: QStringList m_frameworkCache; QString m_cacheFilePath; QMap m_policyCache; QNetworkAccessManager *m_manager; QNetworkReply *m_currentRequest; QTimer *m_cacheUpdateTimer; static UbuntuClickFrameworkProvider *m_instance; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUCLICKTOOL_H ./src/ubuntu/ubuntudevicesmodel.h0000644000015600001650000001725212705421114017271 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTUDEVICESMODEL_H #define UBUNTUDEVICESMODEL_H #include "ubuntudevice.h" #include "ubuntudevicenotifier.h" #include "ubuntuprocess.h" #include #include #include namespace ProjectExplorer { class Kit; } namespace Ubuntu { namespace Internal { class UbuntuDevicesItem; class UbuntuDevicesModel : public QAbstractListModel { Q_OBJECT public: enum Roles { ConnectionStateRole = Qt::UserRole, UniqueIdRole, ConnectionStateStringRole, DetectionStateRole, DetectionStateStringRole, KitListRole, DeveloperModeRole, NetworkConnectionRole, WriteableImageRole, DeveloperToolsRole, LogRole, SerialIdRole, ModelInfoRole, DeviceInfoRole, ProductInfoRole, MachineTypeRole, FrameworkRole, EmulatorImageRole, EmulatorDeviceVersionRole, EmulatorUbuntuVersionRole, EmulatorImageVersionRole, EmulatorScaleFactorRole, EmulatorMemorySettingRole }; enum State { Initial, CheckEmulatorInstalled, InstallEmulator, CreateEmulatorImage, ReadFromSettings, FindImages, AdbList, Idle }; Q_PROPERTY(bool emulatorInstalled READ emulatorInstalled WRITE setEmulatorInstalled NOTIFY emulatorInstalledChanged) Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) Q_PROPERTY(bool cancellable READ cancellable NOTIFY cancellableChanged) Q_PROPERTY(QString state READ state NOTIFY stateChanged) explicit UbuntuDevicesModel(QObject *parent = 0); Q_INVOKABLE bool set(int index, const QString &role, const QVariant &value); int findDevice(int uniqueIdentifier) const; bool hasDevice (int uniqueIdentifier) const; UbuntuDevice::ConstPtr device ( const int index ); // QAbstractItemModel interface virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role); virtual QVariant data(const QModelIndex &index, int role) const; virtual QHash roleNames() const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; bool cancellable() const; QString state() const; bool busy() const; bool emulatorInstalled() const; static void doCreateEmulatorImage ( UbuntuProcess *process, const QString &name, const QString &arch, const QString &channel, const QString &passwd ); signals: void logMessage (const QString &str); void stdOutMessage (const QString &str); void stdErrMessage (const QString &str); void busyChanged(bool arg); void emulatorInstalledChanged(bool arg); void cancellableChanged(bool arg); void stateChanged(QString arg); public slots: void triggerPortForwarding ( const int devId ); void triggerSSHSetup ( const int devId ); void triggerSSHConnection ( const int devId ); void triggerKitAutocreate ( const int devId ); void triggerKitRemove ( const int devId, const QVariant &kitid ); void triggerRedetect ( const int devId ); void deleteDevice ( const int devId ); void createEmulatorImage ( const QString &name, const QString &arch, const QString &channel, const QString &passwd ); void startEmulator ( const QString &name ); void stopEmulator ( const QString &name ); void deleteEmulator ( const QString &name ); QVariant validateEmulatorName(const QString &name ); void refresh (); void cancel(); protected: void clear (); UbuntuDevicesItem *createItem (UbuntuDevice::Ptr dev); int indexFromHelper (QObject* possibleHelper); void deviceChanged(QObject* possibleHelper, const QVector &relatedRoles); void registerNewDevice(const QString &serial, const QString &arch); void setState(UbuntuDevicesModel::State newState); void setBusy(bool arg); void setEmulatorInstalled(bool arg); void setCancellable(bool arg); void beginAction(const QString &msg); void endAction(const QString &msg); void checkEmulatorInstalled(); void findEmulatorImages(); void installEmulator(); void queryAdb(); protected slots: void readDevicesFromSettings(); void detectionStateChanged (); void featureDetected (); void deviceInfoUpdated(); void logUpdated (); void kitsChanged (); void deviceAdded(const Core::Id &id); void deviceRemoved(const Core::Id &id); void deviceUpdated(const Core::Id &id); void deviceConnected(const QString&id); void onMessage(const QString &msg); void processFinished(const QString &, int exitCode); private: QList m_knownDevices; UbuntuDeviceNotifier *m_deviceNotifier; bool m_busy; bool m_emulatorInstalled; bool m_cancellable; State m_state; QString m_reply; UbuntuProcess *m_process; }; class UbuntuQmlFeatureState : public QObject { Q_OBJECT public: enum FeatureState { NotAvailable = UbuntuDevice::NotAvailable, Unknown = UbuntuDevice::Unknown, Available = UbuntuDevice::Available }; Q_ENUMS(FeatureState) }; class UbuntuQmlDeviceDetectionState : public QObject { Q_OBJECT public: enum DeviceDetectionState { NotStarted = UbuntuDevice::NotStarted, WaitForEmulator = UbuntuDevice::WaitForEmulatorStart, Booting = UbuntuDevice::WaitForBoot, Detecting, Done = UbuntuDevice::Done, Error = UbuntuDevice::Failed }; Q_ENUMS(DeviceDetectionState) }; class UbuntuQmlDeviceConnectionState : public QObject { Q_OBJECT public: enum DeviceConnectionState { ReadyToUse = ProjectExplorer::IDevice::DeviceReadyToUse, Connected = ProjectExplorer::IDevice::DeviceConnected, Disconnected = ProjectExplorer::IDevice::DeviceDisconnected, StateUnknown = ProjectExplorer::IDevice::DeviceStateUnknown }; Q_ENUMS(DeviceConnectionState) }; class UbuntuQmlDeviceMachineType : public QObject { Q_OBJECT public: enum MachineType { Hardware = ProjectExplorer::IDevice::Hardware, Emulator = ProjectExplorer::IDevice::Emulator }; Q_ENUMS(MachineType) }; class UbuntuDevicesItem : public QObject { Q_OBJECT public: UbuntuDevicesItem(Ubuntu::Internal::UbuntuDevice::Ptr device, QObject* parent = 0); Core::Id id() const; UbuntuDevice::Ptr device() const; QVariantList kits() const; signals: void kitsChanged (); void connectionChanged (); void detectionStateChanged (); void featureDetected (); void logUpdated(); void deviceInfoUpdated(); private slots: void onKitAdded(ProjectExplorer::Kit *k); void onKitRemoved(ProjectExplorer::Kit *k); void onKitUpdated(ProjectExplorer::Kit *k); void deviceStateChanged (); private: Ubuntu::Internal::UbuntuDevice::Ptr m_device; QSet m_myKits; }; } } #endif // UBUNTUDEVICESMODEL_H ./src/ubuntu/ubuntucmakebuildconfiguration.h0000644000015600001650000000677012705421114021521 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUCMAKEBUILDCONFIGURATION_H #define UBUNTU_INTERNAL_UBUNTUCMAKEBUILDCONFIGURATION_H #include #include #include #include "ubuntucmakemakestep.h" namespace Utils{ class PathChooser; } namespace CMakeProjectManager{ class ArgumentsLineEdit; } namespace Ubuntu { namespace Internal { class UbuntuCMakeBuildConfiguration : public CMakeProjectManager::CMakeBuildConfiguration { Q_OBJECT public: UbuntuCMakeBuildConfiguration(ProjectExplorer::Target *parent); virtual ProjectExplorer::NamedWidget *createConfigWidget() override; protected: UbuntuCMakeBuildConfiguration(ProjectExplorer::Target *parent, UbuntuCMakeBuildConfiguration *source); virtual bool fromMap(const QVariantMap &map) override; friend class UbuntuCMakeBuildConfigurationFactory; }; class UbuntuCMakeBuildConfigurationFactory : public CMakeProjectManager::CMakeBuildConfigurationFactory { Q_OBJECT public: UbuntuCMakeBuildConfigurationFactory(QObject *parent = 0); ~UbuntuCMakeBuildConfigurationFactory(); // IBuildConfigurationFactory interface virtual int priority(const ProjectExplorer::Target *parent) const override; virtual QList availableBuilds(const ProjectExplorer::Target *parent) const override; virtual int priority(const ProjectExplorer::Kit *k, const QString &projectPath) const override; virtual QList availableSetups(const ProjectExplorer::Kit *k, const QString &projectPath) const override; virtual UbuntuCMakeBuildConfiguration *create(ProjectExplorer::Target *parent, const ProjectExplorer::BuildInfo *info) const override; virtual bool canRestore(const ProjectExplorer::Target *parent, const QVariantMap &map) const override; virtual UbuntuCMakeBuildConfiguration *restore(ProjectExplorer::Target *parent, const QVariantMap &map) override; virtual bool canClone(const ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) const override; virtual UbuntuCMakeBuildConfiguration*clone(ProjectExplorer::Target *parent, ProjectExplorer::BuildConfiguration *product) override; private: bool canHandle(const ProjectExplorer::Target *t) const; }; class UbuntuCMakeBuildSettingsWidget : public ProjectExplorer::NamedWidget { Q_OBJECT public: UbuntuCMakeBuildSettingsWidget(UbuntuCMakeBuildConfiguration *bc); private slots: void onArgumentsChanged(); void onBuilddirChanged(); private: Utils::PathChooser *m_pathChooser; CMakeProjectManager::ArgumentsLineEdit *m_userArguments; UbuntuCMakeBuildConfiguration *m_buildConfiguration; }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUCMAKEBUILDCONFIGURATION_H ./src/ubuntu/ubuntumanifesteditor.ui0000644000015600001650000000634612705421114020033 0ustar jenkinsjenkins UbuntuManifestEditor 0 0 1144 733 Form Main Name Maintainer Title Description Version Framework Hooks comboBoxHook activated(int) stackedWidget setCurrentIndex(int) 635 40 647 105 ./src/ubuntu/ubuntuvalidationresultmodel.cpp0000644000015600001650000003610212705421114021566 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntuvalidationresultmodel.h" #include #include #include #include #include #include #include #include #include namespace Ubuntu { namespace Internal { /*! * \class UbuntuValidationResultModel * Implements a model to show the output of the click-run-checks parser */ UbuntuValidationResultModel::UbuntuValidationResultModel(QObject *parent) : QAbstractItemModel(parent) , m_rootItem(new ClickRunChecksParser::DataItem()) , m_nextSectionOffset(0) { m_rootItem->type = QLatin1String("RootItem"); } UbuntuValidationResultModel::~UbuntuValidationResultModel() { delete m_rootItem; } void UbuntuValidationResultModel::clear() { beginRemoveRows(QModelIndex(),0,m_rootItem->children.size()-1); delete m_rootItem; m_rootItem = new ClickRunChecksParser::DataItem(); m_rootItem->type = QLatin1String("RootItem"); endRemoveRows(); } QModelIndex UbuntuValidationResultModel::index(int row, int column, const QModelIndex &parent) const { ClickRunChecksParser::DataItem* item = getItem(parent); if(!item) { return QModelIndex(); } if(row < 0 || row > item->children.length()-1 || column != 0){ return QModelIndex(); } ClickRunChecksParser::DataItem* indexItem = item->children[row]; return createIndex(row,column,indexItem); } QModelIndex UbuntuValidationResultModel::parent(const QModelIndex &child) const { if(!child.isValid()) return QModelIndex(); ClickRunChecksParser::DataItem* item = getItem(child); ClickRunChecksParser::DataItem* parentItem = item->parent; if(!parentItem || parentItem == m_rootItem) return QModelIndex(); ClickRunChecksParser::DataItem* ppItem = parentItem->parent; if(!ppItem) return QModelIndex(); int row = ppItem->children.indexOf(parentItem); Q_ASSERT_X(row >= 0,Q_FUNC_INFO,"Child must be a element of parent list"); return createIndex(row,0,parentItem); } int UbuntuValidationResultModel::rowCount(const QModelIndex &parent) const { int rC = 1; ClickRunChecksParser::DataItem* item = getItem(parent); if(item) rC = item->children.size(); return rC; } int UbuntuValidationResultModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } QVariant UbuntuValidationResultModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); ClickRunChecksParser::DataItem* item = getItem(index); if(item == m_rootItem) return QVariant(); switch(role) { case Qt::DisplayRole: case Qt::EditRole: { QString text = item->type; return text; break; } case TypeRole: { return item->type; break; } case DescriptionRole: { return item->text; break; } case LinkRole: { return item->link.toString(); break; } case ImageRole: case Qt::DecorationRole: { QString iconUrl; switch(item->icon){ case ClickRunChecksParser::Warning: iconUrl = QStringLiteral(":/ubuntu/images/warning.png"); break; case ClickRunChecksParser::Error: iconUrl = QStringLiteral(":/ubuntu/images/error.png"); break; case ClickRunChecksParser::Check: iconUrl = QStringLiteral(":/ubuntu/images/run.png"); break; default: break; } if(!iconUrl.isEmpty()) { if(role == ImageRole) return QString::fromLatin1("qrc%1").arg(iconUrl); else return QIcon(iconUrl); } break; } case ShouldExpandRole: { if(item->icon == ClickRunChecksParser::Error || item->icon == ClickRunChecksParser::Warning) return true; return false; } } return QVariant(); } Qt::ItemFlags UbuntuValidationResultModel::flags(const QModelIndex &index) const { return QAbstractItemModel::flags(index); } QHash UbuntuValidationResultModel::roleNames() const { QHash roleNames = QAbstractItemModel::roleNames(); roleNames[TypeRole] = "TypeRole"; roleNames[LinkRole] = "LinkRole"; roleNames[DescriptionRole] = "DescriptionRole"; roleNames[ImageRole] = "ImageRole"; roleNames[ShouldExpandRole] = "ShouldExpandRole"; return roleNames; } QModelIndex UbuntuValidationResultModel::findFirstErrorItem() const { int parentRow = 0; foreach(const ClickRunChecksParser::DataItem* parentItem, m_rootItem->children) { int innerRow = 0; bool found = false; foreach(const ClickRunChecksParser::DataItem* item, parentItem->children) { if(item->icon == ClickRunChecksParser::Error || item->icon == ClickRunChecksParser::Warning) { found = true; break; } innerRow++; } if(found) { QModelIndex idx = index(innerRow,0,index(parentRow,0,QModelIndex())); return idx; } parentRow++; } return QModelIndex(); } void UbuntuValidationResultModel::appendItem(ClickRunChecksParser::DataItem *item) { beginInsertRows(QModelIndex(),m_rootItem->children.length(),m_rootItem->children.length()); item->parent = m_rootItem; m_rootItem->children.append(item); endInsertRows(); } ClickRunChecksParser::DataItem *UbuntuValidationResultModel::getItem(const QModelIndex &index) const { if(index.isValid()){ ClickRunChecksParser::DataItem* elem = static_cast(index.internalPointer()); if(elem) { return elem; } } return m_rootItem; } ClickRunChecksParser::DataItem::DataItem() : parent(0) , icon(ClickRunChecksParser::NoIcon) { } ClickRunChecksParser::DataItem::~DataItem() { qDeleteAll(children.begin(),children.end()); children.clear(); } /*! * \class ClickRunChecksParser * Implements a incremental parser for the click-run-checks output * As soon as a complete section is available (marked by start of * a new one) the section is parsed and a new item is emitted */ ClickRunChecksParser::ClickRunChecksParser(QObject *parent) : QObject(parent) , m_nextSectionOffset(0) , m_errorCount(0) , m_warnCount(0) { } /*! * \brief ClickRunChecksParser::beginRecieveData * Clear alls internal data and starts a completely new parse */ void ClickRunChecksParser::beginRecieveData(const QString &data) { m_data.clear(); m_nextSectionOffset = m_errorCount = m_warnCount = 0; m_data.append(data); emit begin(); bool canContinue = true; while(canContinue) canContinue=tryParseNextSection(); } /*! * \brief ClickRunChecksParser::addRecievedData * Appends new \a data to the internal buffer and tries to read * as far as possible and emit new items, the parser always requires * to find a new section start before considering the current section * to be available completely */ void ClickRunChecksParser::addRecievedData(const QString &data) { m_data.append(data); bool canContinue = true; while(canContinue) canContinue=tryParseNextSection(); } /*! * \brief ClickRunChecksParser::endRecieveData * Tells the parser every data is available now and it should just * parse until the end of the internal buffer */ void ClickRunChecksParser::endRecieveData(const QString &data) { m_data.append(data); bool canContinue = true; while(canContinue) canContinue=tryParseNextSection(true); emit finished(); } /*! * \brief UbuntuValidationResultModel::tryParseNextSection * Tries to parse the next output section from the dataString * * \return false if there is no next section */ bool ClickRunChecksParser::tryParseNextSection(bool dataComplete) { QRegularExpression re(QStringLiteral("^([=]+[\\s]+[A-Za-z0-9\\-_]+[\\s]+[=]+)$")); // match section beginnings re.setPatternOptions(QRegularExpression::MultilineOption); QRegularExpressionMatchIterator matchIter = re.globalMatch(m_data,m_nextSectionOffset); if(!matchIter.hasNext()) return false; QRegularExpressionMatch match = matchIter.next(); int startOffset = match.capturedStart(1) + match.capturedLength(1); int endOffset = -1; if(matchIter.hasNext()) { QRegularExpressionMatch match = matchIter.next(); endOffset = match.capturedStart(1); } if(endOffset < 0 && dataComplete) endOffset = m_data.length()-1; if(startOffset == -1 || endOffset == -1) return false; //set the offset for the next parse m_nextSectionOffset = endOffset; QString type = match.captured(1); type.remove(QLatin1String("=")); type = type.trimmed(); //prior to 5.4.0 we had to add +1 to fix the offset #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) parseJsonSection(type,startOffset,(endOffset-startOffset)); #else parseJsonSection(type,startOffset,(endOffset-startOffset)+1); #endif return true; } /*! * \brief ClickRunChecksParser::parseJsonSection * Parses a part of the input data, that can contain only a JSON object * or alternatively a single line starting with the string ERROR: * emits parsedNewItem if the section was parsed successfully */ void ClickRunChecksParser::parseJsonSection(const QString §ionName, int offset, int length) { QJsonParseError error; QString sectionData = m_data.mid(offset,length); sectionData = sectionData.trimmed(); if(sectionData.startsWith(QLatin1String("ERROR:"))) { QStringList lines = sectionData.split(QLatin1String("\n")); QString errLine = lines.size() > 0 ? lines[0] : sectionData; errLine.remove(QLatin1String("ERROR:")); emitTextItem(sectionName,errLine,Error); return; } QJsonDocument doc = QJsonDocument::fromJson(sectionData.toUtf8(),&error); if(error.error != QJsonParseError::NoError) { emitParseErrorItem(QString::fromLocal8Bit("Json Parse Error: %0").arg(error.errorString())); return; } if(!doc.isObject()) { emitParseErrorItem(QString::fromLocal8Bit("Json unexpected format")); return; } bool hasErrors = false; bool hasWarnings = false; DataItem* item = new DataItem(); item->text = QLatin1String("No description"); item->type = sectionName; item->icon = Check; // we are optimisic QJsonObject obj = doc.object(); QJsonValue errValue = obj.value(QLatin1String("error")); QJsonValue warnValue = obj.value(QLatin1String("warn")); QJsonValue infoValue = obj.value(QLatin1String("info")); if(errValue.isObject()) { QJsonObject errors = errValue.toObject(); QStringList keys = errors.keys(); hasErrors = (errors.keys().size() > 0); foreach(const QString &key, keys) { QJsonValue messageValue = errors.value(key); if(messageValue.isObject()){ QJsonObject messageObject = messageValue.toObject(); QString text = messageObject.value(QLatin1String("text")).toString(); DataItem* subItem = new DataItem(); subItem->parent = item; subItem->text = text; subItem->type = key; subItem->icon = Error; if(messageObject.keys().contains(QLatin1String("link"))) { subItem->link = QUrl::fromUserInput(messageObject.value(QLatin1String("link")).toString()); } item->children.append(subItem); } } } if(warnValue.isObject()) { QJsonObject warnings = warnValue.toObject(); QStringList keys = warnings.keys(); hasWarnings = (warnings.keys().size() > 0); foreach(const QString &key, keys) { QJsonValue messageValue = warnings.value(key); if(messageValue.isObject()){ QJsonObject messageObject = messageValue.toObject(); QString text = messageObject.value(QLatin1String("text")).toString(); DataItem* subItem = new DataItem(); subItem->parent = item; subItem->text = text; subItem->type = key; subItem->icon = Warning; if(messageObject.keys().contains(QLatin1String("link"))) { subItem->link = QUrl::fromUserInput(messageObject.value(QLatin1String("link")).toString()); } item->children.append(subItem); } } } if(infoValue.isObject()) { QJsonObject infos = infoValue.toObject(); QStringList keys = infos.keys(); foreach(const QString &key, keys) { QJsonValue messageValue = infos.value(key); if(messageValue.isObject()){ QJsonObject messageObject = messageValue.toObject(); QString text = messageObject.value(QLatin1String("text")).toString(); DataItem* subItem = new DataItem(); subItem->parent = item; subItem->type = key; subItem->text = text; subItem->icon = Check; if(messageObject.keys().contains(QLatin1String("link"))) { subItem->link = QUrl::fromUserInput(messageObject.value(QLatin1String("link")).toString()); } item->children.append(subItem); } } } if(hasErrors) item->icon = Error; else if(hasWarnings) item->icon = Warning; emit parsedNewTopLevelItem(item); } /*! * \brief ClickRunChecksParser::emitParseErrorItem * Creates a new Error DataItem and emits it */ void ClickRunChecksParser::emitParseErrorItem(const QString &text) { DataItem *elem = new DataItem(); elem->icon = Error; elem->type = QLatin1String("Error"); elem->text = text; emit parsedNewTopLevelItem(elem); } /*! * \brief ClickRunChecksParser::emitTextItem * Creates a new text item and emits it */ void ClickRunChecksParser::emitTextItem(const QString& type,const QString &text, const ItemIcon icon) { DataItem *elem = new DataItem(); elem->icon = icon; elem->type = type; elem->text = text; emit parsedNewTopLevelItem(elem); } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuwaitfordevicedialog.h0000644000015600001650000000115412705421114020633 0ustar jenkinsjenkins#ifndef UBUNTUWAITFORDEVICEDIALOG_H #define UBUNTUWAITFORDEVICEDIALOG_H #include #include "ubuntudevice.h" #include namespace Ubuntu{ namespace Internal{ class UbuntuWaitForDeviceDialog : public QProgressDialog { Q_OBJECT public: explicit UbuntuWaitForDeviceDialog(QWidget *parent = 0); void show (UbuntuDevice::ConstPtr device); signals: void deviceReady (); protected slots: void handleDeviceUpdated (); void handleCanceled (); void updateLabelText (); private: UbuntuDevice::ConstPtr m_dev; }; } } #endif // UBUNTUWAITFORDEVICEDIALOG_H ./src/ubuntu/ubunturemoterunner.h0000644000015600001650000000315312705421114017346 0ustar jenkinsjenkins#ifndef UBUNTUREMOTERUNNER_H #define UBUNTUREMOTERUNNER_H #include "ubuntudevice.h" #include #include #include namespace Ubuntu { namespace Internal { class UbuntuRemoteClickApplicationRunnerPrivate; class UbuntuRemoteClickApplicationRunner : public QObject { Q_OBJECT public: enum CleanupMode{ CleanSettings, KeepSettings }; explicit UbuntuRemoteClickApplicationRunner(QObject *parent = 0); virtual ~UbuntuRemoteClickApplicationRunner(); void start (UbuntuDevice::ConstPtr device, const QString &clickPackageName , const QString &hook); void stop (); quint16 cppDebugPort() const; void setCppDebugPort(const quint16 &cppDebugPort); quint16 qmlDebugPort() const; void setQmlDebugPort(const quint16 &qmlDebugPort); Utils::Environment env() const; void setEnv(const Utils::Environment &env); void setForceInstall (const bool set); void setUninstall (const bool set); protected: void cleanup (CleanupMode mode = CleanSettings); signals: void launcherStdout(const QByteArray &output); void launcherStderr(const QByteArray &output); void reportError(const QString &errorOutput); void launcherProcessStarted(quint16 pid); void clickApplicationStarted(quint16 pid); void finished(bool success); private slots: void handleLauncherProcessError(QProcess::ProcessError error); void handleLauncherProcessFinished(); void handleLauncherStdOut(); void handleLauncherStdErr(); private: UbuntuRemoteClickApplicationRunnerPrivate *d; }; }} #endif // UBUNTUREMOTERUNNER_H ./src/ubuntu/ubuntukitmanager.h0000644000015600001650000000355412705421114016750 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #ifndef UBUNTU_INTERNAL_UBUNTUKITMANAGER_H #define UBUNTU_INTERNAL_UBUNTUKITMANAGER_H #include "clicktoolchain.h" #include "ubuntudevice.h" #include namespace Debugger{ class DebuggerItem; } namespace CMakeProjectManager{ class CMakeTool; } namespace Ubuntu { namespace Internal { class UbuntuQtVersion; class UbuntuKitManager : public QObject { Q_OBJECT public: UbuntuKitManager(); static void autoCreateKit ( UbuntuDevice::Ptr device ); static void autoDetectKits (); static ProjectExplorer::Kit *createKit (ClickToolChain* tc); static QVariant createOrFindDebugger(const Utils::FileName &path); static void fixKit (ProjectExplorer::Kit* k); static QList clickToolChains(); static UbuntuQtVersion *createOrFindQtVersion(ClickToolChain* tc); static CMakeProjectManager::CMakeTool *createOrFindCMakeTool(ClickToolChain* tc); static CMakeProjectManager::CMakeTool *createCMakeTool(ClickToolChain *tc); static CMakeProjectManager::CMakeTool *createCMakeTool(const UbuntuClickTool::Target &target); }; } // namespace Internal } // namespace Ubuntu #endif // UBUNTU_INTERNAL_UBUNTUKITMANAGER_H ./src/ubuntu/targetupgrademanager.cpp0000644000015600001650000001230712705421114020103 0ustar jenkinsjenkins/* * Copyright 2015 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "targetupgrademanager.h" #include "ubuntuconstants.h" #include "ubuntuclickdialog.h" #include "ui_targetupgrademanagerdialog.h" #include "settings.h" #include #include #include #include #include #include namespace Ubuntu { namespace Internal { TargetUpgradeManager::TargetUpgradeManager(QObject *parent) : QObject(parent), m_state(Idle) { connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(coreAboutToClose())); } void TargetUpgradeManager::checkForUpgrades() { bool set = Settings::chrootSettings().autoCheckForUpdates; if(set && m_state == Idle) { m_state = CollectPendingUpdates; m_outdatedChroots.clear(); foreach(const UbuntuClickTool::Target &chroot, UbuntuClickTool::listAvailableTargets()) { QPointer proc(new QProcess(this)); connect(proc.data(),SIGNAL(finished(int)),this,SLOT(processFinished())); proc->start(QString::fromLatin1(Constants::CHROOT_UPDATE_LIST_SCRIPT) .arg(Constants::UBUNTU_RESOURCE_PATH) .arg(chroot.architecture) .arg(chroot.framework)); Task t; t.proc = proc; t.target = chroot; m_running.insert(reinterpret_cast(proc.data()),t); } } } void TargetUpgradeManager::processFinished() { qintptr id = reinterpret_cast(sender()); QTC_ASSERT(m_running.contains(id),return); switch(m_state) { case CollectPendingUpdates:{ Task task = m_running.take(id); task.proc->deleteLater(); if(task.proc->exitStatus() == QProcess::NormalExit && task.proc->exitCode() > 0) m_outdatedChroots.append(task.target); if(m_running.isEmpty()) { m_state = Idle; if(m_outdatedChroots.isEmpty()) break; TargetUpgradeManagerDialog::selectAndUpgradeTargets(m_outdatedChroots,Core::ICore::mainWindow()); m_outdatedChroots.clear(); } break; } default: break; } } void TargetUpgradeManager::coreAboutToClose() { QProgressDialog dlg; dlg.setCancelButton(0); dlg.setRange(0,0); dlg.setLabelText(tr("Waiting for background processes to terminate.")); dlg.open(); for(auto i = m_running.begin(); i != m_running.end(); i++) { Task &t = i.value(); if (t.proc) { t.proc->disconnect(this); t.proc->terminate(); //polling is ugly, but in this case there is no clean way of handling this //since we need to block in this function until all things are sorted out int msecs = 30000; int timeframe = 10; while(!t.proc->waitForFinished(timeframe)) { msecs -= timeframe; if (msecs <= 0) { t.proc->kill(); break; } QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (t.proc->error() != QProcess::Timedout) break; } delete t.proc; } } m_state = Idle; m_running.clear(); } TargetUpgradeManagerDialog::TargetUpgradeManagerDialog(QWidget *parent) : QDialog(parent) { m_ui = new Ubuntu::Internal::Ui::TargetUpgradeManagerDialog; m_ui->setupUi(this); } TargetUpgradeManagerDialog::~TargetUpgradeManagerDialog() { delete m_ui; } void TargetUpgradeManagerDialog::selectAndUpgradeTargets(QList targets,QWidget *parent) { TargetUpgradeManagerDialog dlg(parent); for(int i = 0; i < targets.size(); i++) { QTreeWidgetItem *item = new QTreeWidgetItem; item->setCheckState(0,Qt::Unchecked); item->setText(0,targets.at(i).framework+QStringLiteral("-")+targets.at(i).architecture); dlg.m_ui->treeWidget->addTopLevelItem(item); } if( dlg.exec() == QDialog::Accepted ) { QList selectedTargets; for(int i = 0; i < targets.size(); i++) { if(dlg.m_ui->treeWidget->topLevelItem(i)->checkState(0) == Qt::Checked) { selectedTargets << targets.at(i); } } if(selectedTargets.size() > 0) UbuntuClickDialog::maintainClickModal(selectedTargets,UbuntuClickTool::Upgrade); } } } // namespace Internal } // namespace Ubuntu ./src/ubuntu/ubuntuproject.cpp0000644000015600001650000001305512705421114016624 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #include "ubuntuproject.h" #include #include #include #include #include #include #include #include using namespace Ubuntu; using namespace Ubuntu::Internal; UbuntuProject::UbuntuProject(UbuntuProjectManager *manager, const QString &fileName) : m_manager(manager), m_fileName(fileName) { setId(Constants::UBUNTUPROJECT_ID); setRequiredKitMatcher(UbuntuKitMatcher()); setPreferredKitMatcher(QtSupport::QtKitInformation::qtVersionMatcher(Core::FeatureSet(QtSupport::Constants::FEATURE_DESKTOP))); setProjectContext(Core::Context(Constants::UBUNTUPROJECT_PROJECTCONTEXT)); QFileInfo fileInfo(m_fileName); m_projectName = fileInfo.completeBaseName(); m_file = QSharedPointer(new UbuntuProjectFile(this, fileName)); Core::DocumentManager::addDocument(m_file.data(), true); m_rootNode = QSharedPointer(new UbuntuProjectNode(this, m_file.data())); m_manager->registerProject(this); extractProjectFileData(fileName); } void UbuntuProject::extractProjectFileData(const QString& filename) { QmlJS::SimpleReader reader; const QmlJS::SimpleReaderNode::Ptr root = reader.readFile(filename); if(!reader.errors().isEmpty()) { foreach(const QString &error, reader.errors()) { qWarning()<name().compare(QString::fromLatin1("Project")) == 0) { QVariant mainFileVariant = root->property(QLatin1String("mainFile")); if (mainFileVariant.isValid()) m_mainFile = mainFileVariant.toString(); } else { qWarning()<< tr("There is no Project root element in the projectfile"); } } QString UbuntuProject::displayName() const { return m_projectName; } Core::IDocument *UbuntuProject::document() const { return m_file.data(); } ProjectExplorer::IProjectManager *UbuntuProject::projectManager() const { return m_manager; } ProjectExplorer::ProjectNode *UbuntuProject::rootProjectNode() const { return m_rootNode.data(); } static void enumChild(const QDir &dir, QStringList &res) { foreach (const QFileInfo &info, dir.entryInfoList(QDir::NoDotAndDotDot|QDir::Dirs|QDir::Files|QDir::Hidden)) { if (info.fileName().indexOf(QLatin1String(Constants::UBUNTUPROJECT_SUFFIX)) != -1 || info.fileName().indexOf(QLatin1String(Constants::UBUNTUHTMLPROJECT_SUFFIX)) != -1) continue; if (info.isFile()) { if(info.isHidden() && info.fileName() != QLatin1String(".excludes")) continue; res.append(info.absoluteFilePath()); } else if (info.isDir()) { enumChild(QDir(info.absoluteFilePath()), res); } } } QStringList UbuntuProject::files(FilesMode) const { QStringList files; enumChild(projectDir(), files); return files; } bool UbuntuProject::supportsKit(ProjectExplorer::Kit *k, QString *errorMessage) const { UbuntuKitMatcher matcher; if (!matcher.matches(k)) { if(errorMessage) *errorMessage = tr("Only Desktop and Ubuntu Kits are supported"); return false; } return true; } bool UbuntuProject::needsConfiguration() const { return targets().size() == 0; } bool UbuntuProject::requiresTargetPanel() const { return true; } QString UbuntuProject::shadowBuildDirectory(const QString &proFilePath, const ProjectExplorer::Kit *k, const QString &suffix) { if (proFilePath.isEmpty()) return QString(); QFileInfo info(proFilePath); QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(k); if (version) return info.absolutePath(); const QString projectName = QFileInfo(proFilePath).completeBaseName(); ProjectExplorer::ProjectMacroExpander expander(projectName, k, suffix); QDir projectDir = QDir(projectDirectory(Utils::FileName::fromString(proFilePath)).toString()); QString buildPath = expander.expand(Core::DocumentManager::buildDirectory()); return QDir::cleanPath(projectDir.absoluteFilePath(buildPath)); } UbuntuKitMatcher::UbuntuKitMatcher() : KitMatcher(&UbuntuKitMatcher::matches) { } bool UbuntuKitMatcher::matches(const ProjectExplorer::Kit *k) { ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(k); if (tc->type() == QLatin1String(Ubuntu::Constants::UBUNTU_CLICK_TOOLCHAIN_ID)) return true; if (ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(k) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) return true; return false; } ./src/ubuntu/ubuntudevicenotifier.h0000644000015600001650000000351612705421114017623 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ #ifndef UBUNTUDEVICENOTIFIER_H #define UBUNTUDEVICENOTIFIER_H #include #include #include #include class IUbuntuDeviceNotifier : public QObject { Q_OBJECT public: IUbuntuDeviceNotifier (QObject *parent = 0); virtual void startMonitoring(const QString &serialNumber) = 0; virtual void stopMonitoring() = 0; virtual bool isConnected () const = 0; signals: void deviceConnected(); void deviceConnected(const QString &serialNumber); void deviceDisconnected(); }; class UbuntuDeviceNotifier : public IUbuntuDeviceNotifier { Q_OBJECT public: explicit UbuntuDeviceNotifier(QObject *parent = 0); ~UbuntuDeviceNotifier(); public slots: void startMonitoring(const QString &serialNumber) override; void stopMonitoring() override; bool isConnected () const override; protected slots: void on_udev_event(); private: struct udev *m_dev; struct udev_monitor *m_udevMonitor; int m_udevMonitorFileDescriptor; QSocketNotifier* m_udevSocketNotifier; QString m_devNode; QString m_serialNumber; }; #endif // UBUNTUDEVICENOTIFIER_H ./src/ubuntu/ubuntumanifesteditor.cpp0000644000015600001650000000272112705421114020171 0ustar jenkinsjenkins/* * Copyright 2014 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Benjamin Zeller */ #include "ubuntumanifesteditor.h" #include "ubuntumanifesteditorwidget.h" #include "ubuntuconstants.h" #include #include #include #include namespace Ubuntu { namespace Internal { UbuntuManifestEditor::UbuntuManifestEditor() : UbuntuAbstractGuiEditor(Core::Context(Constants::UBUNTU_MANIFEST_EDITOR_CONTEXT)), m_editorWidget(0) { createUi(); } UbuntuManifestEditor::~UbuntuManifestEditor() { if(m_editorWidget) delete m_editorWidget; } UbuntuAbstractGuiEditorWidget *UbuntuManifestEditor::createGuiEditor() { if(m_editorWidget == 0) m_editorWidget = new UbuntuManifestEditorWidget(); return m_editorWidget; } } // namespace Internal } // namespace Ubuntu ./src/plugin.pri0000644000015600001650000000540012705421114013672 0ustar jenkinsjenkinsPROVIDER = Canonical #support compiling inside the QtC source tree exists( $$PWD/../../plugins.pro ) { message( "Building ubuntu plugin as part of QtCreator" ) include($$PWD/../../../qtcreatorplugin.pri) USDK_DATA_DIRS = \ $$PWD/../share/qtcreator/templates \ $$PWD/../share/qtcreator/ubuntu \ for(data_dir, USDK_DATA_DIRS) { files = $$files($$data_dir/*, true) # Info.plist.in are handled below for(file, files):!exists($$file/*): { USDK_FILES += $$file } } #seems $$files can not find hidden files --- USDK_FILES += $$PWD/../share/qtcreator/templates/wizards/ubuntu/share/.excludes ubuntusdk_copy2build.input = USDK_FILES ubuntusdk_copy2build.output = $$IDE_DATA_PATH/../../${QMAKE_FUNC_FILE_IN_stripSrcDir} ubuntusdk_copy2build.variable_out = PRE_TARGETDEPS ubuntusdk_copy2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} ubuntusdk_copy2build.name = COPY ${QMAKE_FILE_IN} ubuntusdk_copy2build.CONFIG += no_link QMAKE_EXTRA_COMPILERS += ubuntusdk_copy2build for(data_dir, USDK_DATA_DIRS) { eval($${data_dir}.files = $$IDE_DATA_PATH/../../$$stripSrcDir($$data_dir)) eval($${data_dir}.path = $$QTC_PREFIX/share/qtcreator) eval($${data_dir}.CONFIG += no_check_exist) INSTALLS += $$data_dir } } else { ## Where the Qt Creator headers are located at QTCREATOR_SOURCES = $$(QTC_SOURCE) isEmpty(QTCREATOR_SOURCES):QTCREATOR_SOURCES=/usr/src/qtcreator ## Where our plugin will be compiled to IDE_BUILD_TREE = $$(QTC_BUILD) isEmpty(IDE_BUILD_TREE):IDE_BUILD_TREE=../../builddir UBUNTU_LOCAL_BUILD = $$(UBUNTU_QTC_PLUGIN_LOCALBUILD) !isEmpty(UBUNTU_LOCAL_BUILD) { message("!!!!!!!!!!BUILDING LOCAL VERSION OF PLUGIN !!!!!!!!!!!!!!!!!!!") USE_USER_DESTDIR = yes PATHSTR = '\\"$${PWD}/../share/qtcreator\\"' DEFINES += UBUNTU_RESOURCE_PATH_LOCAL=\"$${PATHSTR}\" UBUNTU_BUILD_LOCAL BUILD_ROOT_STR = '\\"$$clean_path($${OUT_PWD}/../../)\\"' DEFINES += UBUNTU_BUILD_ROOT=\"$${BUILD_ROOT_STR}\" #create a link so we get our wizards in the new project wizard system("rm '$$(HOME)/.config/QtProject/qtcreator/templates'") system("ln -s '$${PWD}/../share/qtcreator/templates' '$$(HOME)/.config/QtProject/qtcreator/templates'") } include($$QTCREATOR_SOURCES/src/qtcreatorplugin.pri) INCLUDEPATH += $$QTCREATOR_SOURCES/src/ ## make sure the QtProject libs are available when building locally !isEmpty(UBUNTU_LOCAL_BUILD) { LIBS += -L$$DESTDIRBASE/QtProject/$$DESTDIRAPPNAME/plugins/$$QTCREATOR_VERSION/QtProject } LIBS += -L$$[QT_INSTALL_LIBS]/qtcreator LIBS += -L$$[QT_INSTALL_LIBS]/qtcreator/plugins } ./LICENSE.LGPL0000644000015600001650000006350212705421114012642 0ustar jenkinsjenkins GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ./README0000644000015600001650000000053412705421114011754 0ustar jenkinsjenkinsThis is the qtcreator plugin for ubuntu. To build it run: $ mkdir build $ cd build $ qmake .. $ make To test (if you already have the package "qtcreator-plugin-ubuntu" installed): $ sudo cp ./builddir/lib/qtcreator/plugins/Canonical/libUbuntu.so \ /usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH )/qtcreator/plugins/Canonical/libUbuntu.so ./chroot-agent/0000755000015600001650000000000012705421114013464 5ustar jenkinsjenkins./chroot-agent/chrootagent.h0000644000015600001650000000340112705421114016150 0ustar jenkinsjenkins#ifndef CHROOTAGENT_H #define CHROOTAGENT_H #include #include #include #include class QFileSystemWatcher; class ChrootAgent : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.ubuntu.sdk.ClickChrootAgent") struct Chroot{ QString internalName; QString framework; QString architecture; QString session; }; public: explicit ChrootAgent(QObject *parent = 0); static ChrootAgent *instance (); signals: void chrootListChanged (); void sessionListChanged (); public slots: QString spawnSession (const QString &framework, const QString &architecture); bool releaseSession (const QString &framework, const QString &architecture); QStringList sessionInfo () const; Q_NOREPLY void shutdown (); void hangup (); private slots: void initialize (); void directoryChanged (); private: void findExistingChrootSessions(); bool chrootFromPath(const QString &targetPath, ChrootAgent::Chroot *tg) const; QString chrootBasePath(const ChrootAgent::Chroot &target) const; QString toInternalName (const QString &framework, const QString &arch) const; QString createClickSession(const Chroot &ch); bool endClickSession(Chroot &ch); bool endClickSession(const QString &framework, const QString &architecture, const QString &sessionName); bool validateClickSession (const QString &framework, const QString &architecture, const QString &sessionName); private: QMap m_knownChroots; QFileSystemWatcher *m_chrootDirWatcher; QString m_sessionPrefix; static ChrootAgent *m_instance; QList listAvailableClickChroots(); }; #endif // CHROOTAGENT_H ./chroot-agent/main.cpp0000644000015600001650000001760412705421114015124 0ustar jenkinsjenkins/** * * based on code taken from http://www.enderunix.org/docs/eng/daemon.php */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chrootagent.h" #include "clickchrootagent_interface.h" int cliMode (int argc, char *argv[]); int serviceMode (int argc, char *argv[]); void log_message (const char *message, int logType = LOG_INFO); void signal_handler(int sig); void daemonize(); void useage (); void syslogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { bool do_abort = false; openlog ("click-chroot-agent", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); QByteArray localMsg = msg.toLocal8Bit(); switch (type) { case QtDebugMsg: syslog (LOG_DEBUG, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtWarningMsg: syslog (LOG_WARNING,"Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtCriticalMsg: syslog (LOG_CRIT, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtFatalMsg: syslog (LOG_EMERG, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); do_abort=true; break; #if QT_VERSION >= 0x050500 case QtInfoMsg: syslog (LOG_INFO, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; #endif } closelog(); if(do_abort) abort(); } void log_message (const char *message, int logType) { openlog ("click-chroot-agent", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); syslog (logType, message,0); closelog(); } void signal_handler(int sig) { switch(sig) { case SIGHUP: log_message("hangup signal catched"); ChrootAgent::instance()->hangup(); break; case SIGTERM: log_message("terminate signal catched"); ChrootAgent::instance()->shutdown(); break; } } void daemonize() { if(getppid()==1) return; /* already a daemon */ int i=fork(); if (i<0) { log_message("Could not fork.",LOG_EMERG); exit(1); /* fork error */ } if (i>0) { _exit(0); /* parent exits */ } /* child (daemon) continues */ setsid(); /* obtain a new process group */ for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */ /* handle standart I/O */ i=open("/dev/null",O_RDWR); if(dup(i) < 0) log_message("Could not redirect std filedescriptor",LOG_ERR); if(dup(i) < 0) log_message("Could not redirect std filedescriptor",LOG_ERR); umask(027); /* set newly created file permissions */ QString lockFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)+ QDir::separator()+ QStringLiteral("click-chroot-agent.pid"); int lfp=open(qPrintable(lockFile),O_RDWR|O_CREAT,0640); if (lfp<0) { log_message("Can not open lockfile",LOG_ERR); exit(1); /* can not open */ } if (lockf(lfp,F_TLOCK,0)<0) { log_message("Can not lock lockfile, click-chroot-agent is probably already running."); exit(2); /* can not lock, server already running */ } /* first instance continues */ QByteArray arr = QByteArray::number(getpid()); if(write(lfp,arr.constData(),strlen(arr.constData())) < 0) /* record pid to lockfile */ log_message("Could not write pid to the lockfile"); signal(SIGCHLD,SIG_IGN); /* ignore child */ signal(SIGTSTP,SIG_IGN); /* ignore tty signals */ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); } void useage () { puts("Usage: click-chroot-agent [OPTION]"); puts("-s, --stop Stops the currently running instance of the click-chroot-agent"); puts("-r, --reload Recreates sessions for all exising chroots"); puts("-i, --info Shows currently mounted sessions"); puts("-h, --help Shows this text"); } /*! * \brief cliMode * Runs the command in CLI mode to allow a more convenient use */ int cliMode (int argc, char *argv[]) { if(argc > 2) { puts("Too many arguments."); useage(); return 0; } QCoreApplication a(argc, argv); Q_UNUSED(a); static struct option long_options[] = { {"stop", no_argument, 0, 's'}, {"reload", no_argument, 0, 'r'}, {"info", no_argument, 0, 'i'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; ComUbuntuSdkClickChrootAgentInterface clickAgent(QStringLiteral("com.ubuntu.sdk.ClickChrootAgent"), QStringLiteral("/com/ubuntu/sdk/ClickChrootAgent"), QDBusConnection::sessionBus()); if(!clickAgent.isValid()) { puts("Could not connect to click-chroot-agent service"); return 1; } bool cont = true; while(cont) { int option_index = 0; int c = getopt_long (argc, argv, "srih",long_options, &option_index); if (c == -1) break; switch(c) { case 's': { clickAgent.shutdown(); cont = false; break; } case 'r': { QDBusPendingReply ret = clickAgent.hangup(); if(ret.isError()) puts(qPrintable(ret.error().message())); else ret.waitForFinished(); cont = false; break; } case 'i': { QDBusPendingReply ret = clickAgent.sessionInfo(); if(ret.isError()) { puts(qPrintable(ret.error().message())); } else { ret.waitForFinished(); foreach(const QString &desc, ret.value()) { puts(qPrintable(desc)); } } cont = false; break; } case 'h': case '?': { cont = false; useage(); break; } } } return 0; } /*! * \brief serviceMode * starts the click-chroot-agents service and returns only if its shut down */ int serviceMode (int argc, char *argv[]) { qInstallMessageHandler(syslogMessageHandler); QCoreApplication a(argc, argv); //the control signals signal(SIGHUP,signal_handler); /* catch hangup signal */ signal(SIGTERM,signal_handler); /* catch term signal */ ChrootAgent agent; QDBusConnection sessionBus = QDBusConnection::sessionBus(); //when the session bus goes down we want to exit if(!sessionBus.connect(QString(),QString(),QStringLiteral("org.freedesktop.DBus.Local"),QStringLiteral("Disconnected"), &agent, SLOT(shutdown()))) { log_message("Could not connect to DBUS session bus",LOG_EMERG); return 1; } if(!sessionBus.registerObject(QStringLiteral("/com/ubuntu/sdk/ClickChrootAgent"),&agent)) { log_message("Could not register DBUS interface",LOG_EMERG); qDebug()< 1) return cliMode(argc,argv); daemonize(); return serviceMode(argc,argv); } ./chroot-agent/chroot-agent.pro0000644000015600001650000000246212705421114016604 0ustar jenkinsjenkins#------------------------------------------------- # # Project created by QtCreator 2014-12-09T14:24:45 # #------------------------------------------------- QT += core dbus QT -= gui TARGET = click-chroot-agent CONFIG += console CONFIG -= app_bundle QMAKE_CXXFLAGS += -Werror CONFIG += c++11 dbusadaptors dbusinterfaces TEMPLATE = app #support compiling inside the QtC source tree exists( $$PWD/../../plugins.pro ) { include(../../../../qtcreator.pri) DESTDIR=$$IDE_LIBEXEC_PATH target.path=$$QTC_PREFIX/bin } else { ## Where the Qt Creator headers are located at QTCREATOR_SOURCES = $$(QTC_SOURCE) isEmpty(QTCREATOR_SOURCES):QTCREATOR_SOURCES=/usr/src/qtcreator ## Where our plugin will be compiled to IDE_BUILD_TREE = $$(QTC_BUILD) isEmpty(IDE_BUILD_TREE):IDE_BUILD_TREE=../../builddir include($$QTCREATOR_SOURCES/qtcreator.pri) target.path=/bin } SOURCES += main.cpp \ chrootagent.cpp HEADERS += \ chrootagent.h INCLUDEPATH += $$OUT_PWD xml_desc.target=com.ubuntu.sdk.ClickChrootAgent.xml xml_desc.commands=$$[QT_INSTALL_BINS]/qdbuscpp2xml -o $$xml_desc.target $$PWD/chrootagent.h xml_desc.depends=$$PWD/chrootagent.h QMAKE_EXTRA_TARGETS+=xml_desc DBUS_ADAPTORS += $$xml_desc.target DBUS_INTERFACES += $$xml_desc.target INSTALLS+=target ./chroot-agent/chrootagent.cpp0000644000015600001650000002611112705421114016506 0ustar jenkinsjenkins#include "chrootagent.h" #include "clickchrootagent_adaptor.h" #include #include #include #include #include #include #include #include #include ChrootAgent *ChrootAgent::m_instance = nullptr; const char UBUNTU_CLICK_CHROOT_BASEPATH[] = "/var/lib/schroot/chroots"; const char UBUNTU_CLICK_TARGETS_REGEX[] = "^click-(.*)-([A-Za-z0-9]+)$"; ChrootAgent::ChrootAgent(QObject *parent) : QObject(parent), m_chrootDirWatcher(nullptr) { m_instance = this; new ClickChrootAgentAdaptor(this); m_sessionPrefix = QString(QStringLiteral("ucca-%1")).arg(getuid()); //invoke the initialize once we enter the event loop QMetaObject::invokeMethod(this,"initialize",Qt::QueuedConnection); } void ChrootAgent::initialize() { foreach(const Chroot &ch, listAvailableClickChroots()) { m_knownChroots.insert( toInternalName(ch.framework,ch.architecture), ch); } findExistingChrootSessions(); foreach(const QString &key, m_knownChroots.keys()) { if(!m_knownChroots[key].session.isEmpty()) continue; m_knownChroots[key].session = createClickSession(m_knownChroots[key]); } if(!m_chrootDirWatcher) { m_chrootDirWatcher = new QFileSystemWatcher(QStringList{QStringLiteral("/etc/schroot/chroot.d")},this); connect(m_chrootDirWatcher,SIGNAL(directoryChanged(QString)),this,SLOT(directoryChanged())); } emit chrootListChanged(); emit sessionListChanged(); } void ChrootAgent::directoryChanged() { QList chRootOnSys = listAvailableClickChroots(); QSet chRootOnSysKeys; foreach(const Chroot &ch, chRootOnSys) chRootOnSysKeys.insert(toInternalName(ch.framework,ch.architecture)); bool changed = false; foreach(const QString &key, m_knownChroots.keys()) { if(!chRootOnSysKeys.contains(key)) { qDebug()<<"Removing chroot"<session = createClickSession(*ch); QTimer::singleShot(0,this,SIGNAL(sessionListChanged())); return ch->session; } return QString(); } bool ChrootAgent::releaseSession(const QString &framework, const QString &architecture) { qDebug()<<"Request to release session "<exit(0); } void ChrootAgent::hangup() { foreach(const QString &key, m_knownChroots.keys()) endClickSession(m_knownChroots[key]); m_knownChroots.clear(); initialize(); } /** * @brief UbuntuClickTool::listAvailableTargets * @return all currently existing chroot targets in the system */ QList ChrootAgent::listAvailableClickChroots( ) { QList items; //use the etc dir, config files show only up if we can actually create session QDir chrootDir( QStringLiteral("/etc/schroot/chroot.d") ); //if the dir does not exist there are no available chroots if(!chrootDir.exists()) return items; QStringList availableChroots = chrootDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); QRegularExpression clickFilter(QString::fromLatin1(UBUNTU_CLICK_TARGETS_REGEX)); //iterate over all chroots and check if they are click chroots foreach (const QString &chroot, availableChroots) { QRegularExpressionMatch match = clickFilter.match(chroot); if(!match.hasMatch()) continue; Chroot t; if(!chrootFromPath(chroot,&t)) continue; items.append(t); } return items; } void ChrootAgent::findExistingChrootSessions() { QStringList args{ QStringLiteral("--list"), QStringLiteral("--all-sessions") }; QProcess proc; proc.setProgram(QStringLiteral("schroot")); proc.setArguments(args); proc.start(); proc.waitForFinished(); //session string format //session:click-ubuntu-sdk-15.04-armhf-13e7f626-a933-47f1-a501-329749612445 QRegularExpression exp(QString::fromLatin1("session:click-(ubuntu-sdk-[0-9.]*)-([\\w]*)-(%1-.*)").arg(m_sessionPrefix)); while(proc.canReadLine()) { QString line = QString::fromLocal8Bit(proc.readLine()); QRegularExpressionMatch match = exp.match(line); if(!match.hasMatch()) continue; QString fw = match.captured(1); QString arch = match.captured(2); QString internal_name = toInternalName(fw,arch); qDebug()<<"Pickung up existing session: "<asBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATx}i$uޗU=,q,N !"iJ&"@١PlK6V! %-[hraS,9lSuE*HB&D .b]=fgzG|Uӳ; Ծ|W܀pn 7܀q * xke-X@|w$bL47 @`G0m]'A rcqR -i9EQs9YȲ@ v@)Wm Mt6*kMZ;愒szr$O[eVTȑR* '\3j4#H6BZkC\F`(L eLCiyY& K"JyHBϹ\N; [qVdr #K6$+GQ/Avr^|ɴsy]myX90s$9%ǽZ.̝옜Qtqxy\;k5Mn%ord=8\^7 Ea2y$[9đsYb O#;|^E4GZVb!GDvWYy=gq-E@ O[YD`Rr"Hy0t)]]euiD%8?1ey][q^`.il=x r4i" ] a~0=s5MȹinVo.0sO$~(LnJ)^1-WhTMJeHoPڳPBU^F BNVJ b}ԐEºF[ĺDEZLx*FGFxW(7QSE0iV\w 8}Tr`+uk.y쮫ն0T#2.1燹DFBU"Q䷣ަIH.K(>E!9+ԍʅ@UJDzٴ4J}t (.]B<=P %ϒ#?J}D)–0)ÿLقL 0DV -L|+`M*PȬ0I=>vynv "ͫ.;DT$YiјC"=< t8G5OaR#lPrR iȧ/ QINVOEHLեz:)ix0]I[O_m=IE1mZV,5G04jbe08p)C"xڍ U}QiG%/9:{ _5ޗ 3Ӓ>},^ F* ߮w0ܝ2ijpv-ԶsRڦ0UT]/G~Z 5h͚JfBYp]sC*{5U /rI<o1"(TJ.0\gU*Xb'B~#&SO '@'uDmVގz$]N%BaJ'؜Lqo'z!VpUV\yҪ}PExCG~& Ê~F~"@xZu·TMd}ï #" zVXۨ9j: (-' >3nt˩4DJaq`:UN@|As2%,FeԶhUOǿktƁ8wyS{Юɮ ̥67WQI81BAt kQܽB1K?pb[ \O􀎶IB[?rJy/ 80yw/9:P&.(y,r ak07%ps˃"qCYHPNy~z P}K@bs.z ʷ-@EB(&˃( o#Czp%徖ڴN9=OPW2iH"I8a"7 +QVʭ B_|N@]^OQFu'!%'*hrezvL/s@4c>8PJ&sv^:cV4,![^.q-./{گ\sM}+{,pk0{ea+ !jPx%iW~^,u~=^Vب'sXBݸh=z8 ƸpoCPbNȟ lg,'?B|xOu&am z1.5p*^]XN]1668rH[.1-uRWqxkxc İqW ak'W586K8si%#Fhi ;(\ċ'pqUg0ߟ)^%p_4ؒ6Hȹv/[+N }(Լ=qp` U !ϞǠ1թZگhJ .%.k4DG$RImN_"13P;qo޲v+2Ȼ$?{i?~VXZ#Z8t թ Dhog}m޽s[}mWp(7@TI^W_~4"6'Mp,-^8seaUj!cTᩗ/766`M=s*ZAs J|̓f yϭ8z(ʔWF|%.(1eL dȞw$t8$Edh΂x鉰*`C܎ /g^8s[e:w:T6(Ypa}37ux n?r8CpDf1 z ڈ$9:(uY0hiqFvz VE3g؏n7`iPW>q45vhxzBJy?l%|7'q &_6K!6#gj ^O'nM7XiJBgh+VpeNuvv3nqp%${IRcHj{wɹo{o K/c}J5yŝ8!c&JpOKXۨy Π'$,FT)MD|H?%J]~GرM yxyHܦF+3?z/GV[bs^EK\ޘᖒx?XYF}v-xqW~lVIZ)Ʒ3"vG?5* "9O6^@厷W/1{o>}Ke.mL_<d $s.N!N(Kt, D*  OЬz } l^"=sy/'x~-yjE)@p;r,xvw/'mв{wcup:Q"\^yKiJ&UQol$&|[Kmf .%bB5H6POꐕH+c6g^OK>^r['?tBvdAi(Id퐻ݭl_ZW'nC-Z/StìMʑW/J!̙(9Ch D0]}!ܿ>Ɵ>g^L.ĦK^1h4k:;m.q|u|':}d~kF엤o , Ϡ&$87fq+A!,&l`ؖ,.oLǗ:k"HSU5 '!38oٕŇUC9A|emsuPNk2 O|{ⵄFut/z<|ԢODV,"s\xj\𬸩ں!؆pqYkQ q3bṂϞN:Ҡi|C?ܽ?)Kk&w5McZNaj:g}"C8WP/:&*;wZ;ȑ%Sbm\)d~!@'}D㤿u>G]GMUxs9O|ܙݎ+TSui:t; ul>XS)D-68._= [1/Q= Z &MZɗS߆=֮gO3xzy OK'/xB|YV_%}(nd'݃sk1q<=?S ܏ k#?{'ήcsR >nݿ&E|2 屵{pML ~8v2N1Ĥv3K%v/pxrwU[;M\n&>~3_y{ɹu_,+~֒s2[˾H4ylkw0gN҈7|HeGCQ#nn{Vx]<Ǩ۟?Рgpm{pm{f`R7hH>ᥛ6F5_Ul BUO"8N-Ēɿu"^|Jcow;:Fv`Enyqb7.վ 9SWh\rWSVf́Ȯ_y1{ww &/OZ ^ M׵d^iB&.ց{pf $ 90\.!^=[0UQ$Z%+4bRk ܥA& ȗgDt3!u-ը&~rPD|&MR=)jE0+8KkUr ۩ݻR/^{tt(R6v9oeZqM=4YJFmq}%UJ/W"Z>WAa%\f )4䎍ePjâ|ԏ/XҜc6GtcwY=`AcN_//C@|L&bNuLA%fy9`KصkW6X/RZAi!m aZc}+y10%-59 0ENJAݧPp-Z)T;,>&F +V}o__Ei,d4L/_V4~~,^9ML"ͥ-t/oFuσ`G`H&A @"@5IB] sʄa*Sgn+Wi46FORp i>pxd2ƤEPUJڋBkVZe6$w2/$ea#(CQj4*:@VP [pSQ6&K]I(a>~4Y8.S+}\٬QIWF]>K}1McL9x5{ #PMKLFW/[ ¶!]GhoJV biDdx}t+_$wsfTcJ/|IU~kFG❷pe?닱ZA΋Y⾆Ngâۻ}(TpKS7r ݲ 40˿qpZE/_AeWB;Y;s\Y(P8w,kvxUOv-"iwCrfu.+5pms|ɝqLPU꨷W [T5[~ u]k4M;z8S&1:<'27٪g"cmZz="j{noדp-/4K{ЍLϽ7pBU~X \Apd9-5{aDuxH㎟EQb0-"Hu Ujz=e,ÂXIrox a̵s UݡF{*Bp]N2,8*"WH 'tn6jA(8{v<ԩSH: d84"iL&y "e& PǴ3j+]F EPd8l ɓ'^/፵ ߼]}$'M01E]W4X__b k-ʲDQ-.'}HtY$}v,ZBesqi^%pMu.\`/=s_:1ʏ=|VR:Pm#iZEx[LAsM& $H 9릤\<|;>)ePֈ)D^^h4qkbܵŠӶ®%d+f$jFﺘcDŽTn91 )-Qs|\-lP^)ϮBs KGRًc:ac ~Or%yhܵ{  d(0r"VlnnN% ~%,j j\wqjNׅԍjcZy컁86ƾ8s V O݊VLƨk Z#Yx3H )E.ܠLƉ:?k4z~= 㖝My 8\T?y30`sHgՙ-iH A*sZlllҥKXYY8o70`y1)ӃV-%^Vd+t|E , sk0 qs5pJ I(|!bpY+C?|}1TɎ{^5w"\e8` A,:t(ŊhW O|ypMJE23eSix!(qc?y2n^gQk;iZ#qN9WNésk:5:1 hƠ( nCZmk,K9~,'=s.Zc4[=ٞ:=2\wT WD rtcTF( Px"Ey~bP wǗ_+~H\n +"wNh[hL)M W ?_ #ZJ6S7)Q{Z8V%; {aHtXrIb’aQػwClv-TXUx!r!,JO#NWZa f!_pM@I0Tœ]obh{CCRk^Jٻ|>ΖaY~!ǂR K;/SC\X~aJ,\)Y1_Y&!K|^+c^/_q!ػʒFCK.q#g zfd,j· Wm\*nL<(J#nopx^:9LNpnW>! y3 vF6*+_ʏVpik|[5ŏXl6DӔ< 9D@U $W@.}R &#O׍ [ (*5«[U$DrØh}#u>w(~13cG z=<ZIb_`sb7zgb8@:ui\OƮ8;y. 2{JXjxp<=/sUpHM7Q %>^I[1c8Zcߔ~}:wiD\Ɛ&:S;x4X\ڨ`+APyx^GH3B h1bkDH`G|>&~!7m9 \7:M'$cjDl'\S3= 8ȇ@hlf!!^o׻yfFJ[&N-4PU)" J w{0҄|(vv @66k5v7|U@U5Ad~O߆ؕ`4 (.tJ%0taf媪 ϟGyWqE@N*1'L* #3\\A)wh[aձˁR Dlrq|R l6.\3rل2K/}r͂YX؝#2e29iPh{3{LOi}(wy[jPO^Zv-&{O}cs5S z/_MPW0v:mL>[lsݐ?o~gBW?RL'8Dgv֤ۿ,Jrp4Ee[ǃ)N/|1NO?o<@> nW/ Rz6ۜ)vN+v%SzL{/!tȟi{P9|`´w x]| ҙIcXh'\a]fIxN#=kIh,VSOc=vUxRX@9}~NFZ+AL.ylxfD[Uz9ԠE!G&oy]NCD9_ֳٔcUE0$#W/GmkaSİkcxgw[0o8mN*hwG/AI״yx3[[03|0nExPby6Rˠf zvd:Zw᪻Ӈ2?N4{Y{5Ñ3D W=쳟|G }:@W0:@.}F"ŕAjDD>&ZL4|jogK:CH5yh;i֏9@G]em7޴y]ods`n?@^3 dӼ}kz(wxw\5QKo2h8gc=KޤA;>UڊT"é3<_ݻ:# +)%+|OM³4:rC<䓟_`pG'H ^V*xܩ`M{x[G纮q%|gg'|//Hr vo|!|/4)v|RO?k'~)' g_؊h++zB] Vq)~?|0KxS̐Id2SO=k?s?+j<i#"eUZ]׵뺨hXk5_Dgyt OyNѬbD4[z#;\@;$px4G*dUJ5EQTZj}}j{/—ȟM`4p e}/7Ywڿ/-..A_~vLs,d ?ƛhmt9qƿSH.UY7.Ug,EJ{~d,"rsbμj jH]I[A/Έu5ip= `@:sOsi@/D .] iu/!Gx~?/|[恭d4~!܀pfxG{@IENDB`./ubuntu.pro0000644000015600001650000000177212705421114013145 0ustar jenkinsjenkinsTEMPLATE = subdirs SUBDIRS = UbuntuPlugin.pro \ #tests \ chroot-agent #QML files QML_ROOT="$${PWD}/share/qtcreator/ubuntu" QML_FILES += \ $$QML_ROOT/qml/Components/Link.qml \ $$QML_ROOT/qml/Components/NewsBox.qml\ $$QML_ROOT/qml/Components/FeatureStateItem.qml \ $$QML_ROOT/qml/Components/ScrollableView.qml \ $$QML_ROOT/qml/Components/SectionItem.qml \ $$QML_ROOT/qml/Components/Link.qml \ $$QML_ROOT/qml/Components/UbuntuStyleToolbar.qml \ $$QML_ROOT/qml/DevicesPage/DevicePage.qml \ $$QML_ROOT/qml/DevicesPage/NewEmulatorDialog.qml \ $$QML_ROOT/qml/DevicesPage/DeleteDeviceDialog.qml \ $$QML_ROOT/qml/DevicesPage/EmulatorNotInstalled.qml \ $$QML_ROOT/qml/welcome.qml \ $$QML_ROOT/qml/publishpage.qml \ $$QML_ROOT/qml/devicespage.qml OTHER_FILES += \ share/qtcreator/ubuntu/scripts/*.py \ $$QML_FILES #support compiling inside the QtC source tree !exists( $$PWD/../plugins.pro ) { qt_install_libs = $$[QT_INSTALL_LIBS] } ./share/0000755000015600001650000000000012705421114012174 5ustar jenkinsjenkins./share/qtcreator/0000755000015600001650000000000012705421114014200 5ustar jenkinsjenkins./share/qtcreator/ubuntu/0000755000015600001650000000000012705421114015522 5ustar jenkinsjenkins./share/qtcreator/ubuntu/scripts/0000755000015600001650000000000012705421114017211 5ustar jenkinsjenkins./share/qtcreator/ubuntu/scripts/device_screenshot0000755000015600001650000000160112705421114022631 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh . `dirname $0`/functions.inc LOCAL_TIME_STAMP=`date +20%y%m%d-%H%M%S` phablet-screenshot -s ${SERIALNUMBER} /tmp/screenshot-${LOCAL_TIME_STAMP}.png xdg-open /tmp/screenshot-${LOCAL_TIME_STAMP}.png ./share/qtcreator/ubuntu/scripts/qtc_chroot_wrapper.py0000755000015600001650000001534612705421114023504 0ustar jenkinsjenkins#!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # QTC device chroot wrapper # Copyright (C) 2014 Canonical # # This program is free software: you can redistribute 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 . # # Author: Benjamin Zeller # This script basically starts the tool passed in sys.argv[0] in a chroot # and tries to map all path that are printed to the console to the host # os. It is primarily used to teach qtcreator how to handle the chroots # approach we use in Ubuntu. The tool needs to be in PATH when logged into # the chroot. All arguments are forwarded import sys import os import os.path import shutil import subprocess import select import re import fcntl import uuid import signal import dbus #find out the directory holding the link dirname = os.path.basename(os.path.dirname(os.path.abspath(sys.argv[0]))) idx = dirname.rfind("-") if(idx is -1): print("The parent directory must contain the click chroot name (e.g ubuntu-sdk-14.10-armhf)") sys.exit(1) #find out the path this script is in, required for local plugin installation scriptpath = os.path.dirname(os.path.realpath(__file__)) #get the click prefix from env, or use click if the var is not set #if the variable is set we do not try to reach the click-chroot-agent chroot_name_prefix = os.getenv('CLICK_CHROOT_SUFFIX', "click") architecture = dirname[idx+1:] framework = dirname[0:idx] args = sys.argv[1:] command = os.path.basename(sys.argv[0]) if (command.startswith("qt5-qmake")): command_path="/var/lib/schroot/chroots/"+chroot_name_prefix+"-"+dirname+"/usr/bin/"+command if (not os.path.isfile(command_path)): legacy_script = scriptpath+"/qtc_chroot_qmake_legacy" success = subprocess.call(["/bin/bash",legacy_script,framework,architecture,chroot_name_prefix]+args,stdout=sys.stdout,stderr=sys.stderr) sys.exit(success) #ugly hack to prevent from teh moc error when running in the chroots if (command == "cmake"): if any("help" not in s for s in args): work_dir = os.getcwd() if (os.path.exists(work_dir+"/CMakeCache.txt")): print("-- Removing build artifacts") shutil.rmtree(work_dir+'/CMakeFiles') os.remove(work_dir+"/CMakeCache.txt") os.remove(work_dir+"/cmake_install.cmake") os.remove(work_dir+"/Makefile") subproc = None session_id = "" click = shutil.which("click") pre_spawned_session = False if( click is None ): print("Could not find click in the path, please make sure it is installed") sys.exit(1) def _doMapAllPaths (matchobj): return matchobj.group(1)+"/var/lib/schroot/chroots/"+chroot_name_prefix+"-"+dirname+"/"+matchobj.group(2) def mapPaths (text): paths = ["var","bin","boot","dev","etc","lib","lib64","media","mnt","opt","proc","root","run","sbin","srv","sys","usr"] for path in paths: text = re.sub("(^|[^\w+]|\s+|-\w)\/("+path+")", _doMapAllPaths , text) return text def exit_gracefully(arg1,arg2): if(subproc is not None): try: subproc.kill() except ProcessLookupError: #process is already gone pass if (not pre_spawned_session): subprocess.call([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"end-session",session_id],stdout=subprocess.DEVNULL) sys.exit(1) signal.signal(signal.SIGTERM, exit_gracefully) signal.signal(signal.SIGINT , exit_gracefully) signal.signal(signal.SIGHUP , exit_gracefully) #only ask the chroot-agent if we use the default click prefix if chroot_name_prefix == "click": try: sessionBus = dbus.SessionBus() clickChrootAgent = sessionBus.get_object('com.ubuntu.sdk.ClickChrootAgent','/com/ubuntu/sdk/ClickChrootAgent') clickChrootAgentIFace = dbus.Interface(clickChrootAgent,dbus_interface='com.ubuntu.sdk.ClickChrootAgent') session_id = clickChrootAgentIFace.spawnSession(framework,architecture) except dbus.exceptions.DBusException: session_id = "" if (len(session_id) == 0): session_id = str(uuid.uuid4()) pre_spawned_session = False else: pre_spawned_session = True if ( not pre_spawned_session ): success = subprocess.call([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"begin-session",session_id],stdout=subprocess.DEVNULL) subproc = subprocess.Popen([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"run","-n",session_id]+[command]+args,stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = "" stderr = "" #even though after select a read should never block, it can happen sometimes #so better make them nonblocking stdoutfd = subproc.stdout.fileno() fl = fcntl.fcntl(stdoutfd, fcntl.F_GETFL) fcntl.fcntl(stdoutfd, fcntl.F_SETFL, fl | os.O_NONBLOCK) stderrfd = subproc.stderr.fileno() fl = fcntl.fcntl(stderrfd, fcntl.F_GETFL) fcntl.fcntl(stderrfd, fcntl.F_SETFL, fl | os.O_NONBLOCK) while True: reads = [stdoutfd, stderrfd] ret = select.select(reads, [], []) for fd in ret[0]: if fd == subproc.stdout.fileno(): read = subproc.stdout.read() stdout += read.decode() try: while(True): idx = stdout.index("\n") line = stdout[0:idx+1] stdout = stdout[idx+1:] sys.stdout.write(mapPaths(line)) except ValueError: pass if fd == subproc.stderr.fileno(): read = subproc.stderr.read() stderr += read.decode() try: while(True): idx = stderr.index("\n") line = stderr[0:idx+1] stderr = stderr[idx+1:] sys.stderr.write(mapPaths(line)) except ValueError: pass if subproc.poll() != None: break if(len(stdout) != 0): sys.stdout.write(str(mapPaths(stdout).encode("utf-8"))) if(len(stderr) != 0): sys.stderr.write(str(mapPaths(stderr).encode("utf-8"))) if (not pre_spawned_session): subprocess.call([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"end-session",session_id],stdout=subprocess.DEVNULL) sys.exit(subproc.returncode) ./share/qtcreator/ubuntu/scripts/local_stop_emulator0000755000015600001650000000157712705421114023220 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller SCRIPTPATH=`dirname $0` EMULATOR=$1 PID=$(${SCRIPTPATH}/local_emulator_pid ${EMULATOR}) if [ -z "${PID}" ] then echo "Emulator ${EMULATOR} is not running" exit 1 fi kill -SIGTERM ${PID} ./share/qtcreator/ubuntu/scripts/device_developertools_has0000755000015600001650000000175012705421114024362 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc PACKAGES=`cat ${SCRIPTPATH}/device_developertools_packages` for PACKAGE in ${PACKAGES} do IS_INSTALLED=`adb_shell dpkg -s ${PACKAGE} |grep Status|grep -o "install ok installed"|wc -l` if [[ ${IS_INSTALLED} -eq "0" ]]; then echo "0" exit fi done echo "1"./share/qtcreator/ubuntu/scripts/qtc_chroot_get_upgrades.py0000755000015600001650000000535312705421114024472 0ustar jenkinsjenkins#!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # QTC chroot update check # Copyright (C) 2015 Canonical # # This program is free software: you can redistribute 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 . # # Author: Benjamin Zeller import signal import subprocess import sys import os import uuid import shutil def splitIgnoreEmptyParts(s, delim=None): return [x for x in s.split(delim) if x] if (len(sys.argv) < 3): print("Useage: qtc_chroot_get_upgrades ") sys.exit(-1) click = shutil.which("click") session_id = "" chroot_name_prefix = os.getenv('CLICK_CHROOT_SUFFIX', "click") architecture = sys.argv[1] framework = sys.argv[2] subproc = None if (len(session_id) == 0): session_id = str(uuid.uuid4()) pre_spawned_session = False else: pre_spawned_session = True def endSession(): subprocess.call([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"end-session",session_id],stdout=subprocess.DEVNULL) def exit_gracefully(arg1,arg2): if(subproc is not None): subproc.kill() endSession() sys.exit(-1) signal.signal(signal.SIGTERM, exit_gracefully) signal.signal(signal.SIGINT , exit_gracefully) signal.signal(signal.SIGHUP , exit_gracefully) if ( not pre_spawned_session ): success = subprocess.call([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"begin-session",session_id],stdout=subprocess.DEVNULL) subproc = subprocess.Popen([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"maint","-n",session_id ,"env","LC_ALL=C","apt-get","update"],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL) subproc.wait() subproc = subprocess.Popen([click, "chroot","-a",architecture,"-f",framework,"-n",chroot_name_prefix,"maint","-n",session_id ,"env","LC_ALL=C","apt","list","--upgradable"],stdout=subprocess.PIPE,stderr=subprocess.DEVNULL, universal_newlines=True) stdout, stderr = subproc.communicate() endSession() packages = splitIgnoreEmptyParts(stdout,"\n") if(len(packages) == 0): sys.exit(0) packages.pop(0) sys.exit(len(packages)) ./share/qtcreator/ubuntu/scripts/qtc_chroot_qmake_legacy0000755000015600001650000000635712705421114024021 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh CLICK_CHROOT_FW=$1 shift CLICK_CHROOT_ARCH=$1 shift CLICK_CHROOT_NAME=$1 shift if [ -z "$CLICK_CHROOT_ARCH" ]; then echo "Need to set CLICK_CHROOT_ARCH" exit 1 fi if [ -z "$CLICK_CHROOT_FW" ]; then echo "Need to set CLICK_CHROOT_FW" exit 1 fi if [ -z "$CLICK_CHROOT_NAME" ]; then echo "Need to set CLICK_CHROOT_NAME" exit 1 fi if [[ $1 != "-query" ]]; then exit 1 fi ARCH_TRIPLET=${CLICK_CHROOT_ARCH/armhf/arm} if [[ ${ARCH_TRIPLET} = "arm" ]]; then POSTFIX="eabihf" else POSTFIX="" fi SYSROOT_PATH="/var/lib/schroot/chroots/${CLICK_CHROOT_NAME}-${CLICK_CHROOT_FW}-${CLICK_CHROOT_ARCH}" QMAKE_PATH="${SYSROOT_PATH}/usr/lib/${ARCH_TRIPLET}-linux-gnu${POSTFIX}/qt5/bin/qmake" STRINGS=`strings ${QMAKE_PATH}|egrep "qt_ssrtpath|qt_prfxpath|qt_adatpath|qt_datapath|qt_docspath|qt_hdrspath|qt_binspat|qt_libspath|qt_lbexpath|t_tstspath|qt_plugpath|qt_impspath|qt_qml2path|qt_trnspath|qt_stngpath|qt_xmplpath|qt_xmplpath|qt_hpfxpath|qt_hdatpath|qt_hbinpath|qt_hlibpath|qt_hostspec|qt_targspec|Generated by qmake "` STRINGS=${STRINGS/qt_ssrtpath=/QT_SYSROOT:$SYSROOT_PATH} STRINGS=${STRINGS/qt_prfxpath=/QT_INSTALL_PREFIX:$SYSROOT_PATH} STRINGS=${STRINGS/qt_adatpath=/QT_INSTALL_ARCHDATA:$SYSROOT_PATH} STRINGS=${STRINGS/qt_datapath=/QT_INSTALL_DATA:$SYSROOT_PATH} STRINGS=${STRINGS/qt_docspath=/QT_INSTALL_DOCS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_hdrspath=/QT_INSTALL_HEADERS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_binspath=/QT_INSTALL_BINS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_libspath=/QT_INSTALL_LIBS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_lbexpath=/QT_INSTALL_LIBEXECS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_tstspath=/QT_INSTALL_TESTS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_plugpath=/QT_INSTALL_PLUGINS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_impspath=/QT_INSTALL_IMPORTS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_qml2path=/QT_INSTALL_QML:$SYSROOT_PATH} STRINGS=${STRINGS/qt_trnspath=/QT_INSTALL_TRANSLATIONS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_stngpath=/QT_INSTALL_CONFIGURATION:$SYSROOT_PATH} STRINGS=${STRINGS/qt_xmplpath=/QT_INSTALL_EXAMPLES:$SYSROOT_PATH} STRINGS=${STRINGS/qt_xmplpath=/QT_INSTALL_DEMOS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_hpfxpath=/QT_HOST_PREFIX:$SYSROOT_PATH} STRINGS=${STRINGS/qt_hdatpath=/QT_HOST_DATA:$SYSROOT_PATH} STRINGS=${STRINGS/qt_hbinpath=/QT_HOST_BINS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_hlibpath=/QT_HOST_LIBS:$SYSROOT_PATH} STRINGS=${STRINGS/qt_hostspec=/QMAKE_SPEC:} STRINGS=${STRINGS/qt_targspec=/QMAKE_XSPEC:} STRINGS=${STRINGS//# Generated by qmake (/QMAKE_VERSION:} STRINGS=${STRINGS//) (Qt /$'\n'QT_VERSION:} STRINGS=${STRINGS//)/} echo "$STRINGS" ./share/qtcreator/ubuntu/scripts/openssh_publickey0000755000015600001650000000514312705421114022670 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc USERNAME=$2 MIN_KEYLEN="2048" if [ -z "$USERNAME" ]; then echo "Username missing" echo "Usage: openssh_publickey " exit 1 fi function check_key { printf "Checking keylen.." KEY_LEN=$(ssh-keygen -l -f $SSHIDENTITY.pub | cut -f1 -d " ") printf " ${KEY_LEN}\n" if [ "$KEY_LEN" -lt "${MIN_KEYLEN}" ]; then echo "SSH key len ${KEY_LEN} is too weak. Creating a new one" generate_key deploy_key else echo "Checking for keys.." KEY=`cat $SSHIDENTITY.pub` set +e PHABLET_KEYS=`adb_shell "cat /home/$USERNAME/.ssh/authorized_keys2"` KEYS=`echo $PHABLET_KEYS | grep "$KEY"` set -e echo echo "Host key is:" echo $KEY echo echo "We have following keys on the device: " echo $KEYS if [[ -z $KEYS || $KEYS == *No\ such* ]]; then echo "*no keys*" echo deploy_key else echo echo "The host key has been already deployed." fi fi } function deploy_key { echo "Deploy the host key to the device.." KEY=`cat $SSHIDENTITY.pub` adb_shell "mkdir -p /home/$USERNAME/.ssh" echo "..key folder created!" adb_shell "echo $KEY >> /home/$USERNAME/.ssh/authorized_keys2" echo "..key deployed!" adb_shell "chown -R $USERNAME:$USERNAME /home/$USERNAME/.ssh" adb_shell "chmod 0700 /home/$USERNAME/.ssh" adb_shell "chmod 0600 /home/$USERNAME/.ssh/authorized_keys2" echo "..permissions updated!" } function generate_key { echo "Generating host key.." if [[ -f $SSHIDENTITY ]]; then echo -e 'y\n'|ssh-keygen -t rsa -N '' -f $SSHIDENTITY -b ${MIN_KEYLEN} else ssh-keygen -t rsa -N '' -f $SSHIDENTITY -b ${MIN_KEYLEN} fi } ################# if [[ -f $SSHIDENTITY ]]; then check_key else generate_key deploy_key fi ./share/qtcreator/ubuntu/scripts/qtc_desktop_scoperunner.py0000755000015600001650000000664012705421114024537 0ustar jenkinsjenkins#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # QTC device applauncher # Copyright (C) 2014 Canonical # # This program is free software: you can redistribute 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 . # # Author: Benjamin Zeller import json import os import os.path import subprocess import argparse import shutil import sys import re # register options to the argument parser parser = argparse.ArgumentParser(description="SDK Scope launcher") parser.add_argument('scope_ini',action="store") parser.add_argument('--cppdebug', action='store', dest='gdbPort', help="Runs the app in gdbserver listening on specified port") options = parser.parse_args() if not os.path.isfile(options.scope_ini): print("Scope ini file does not exist") sys.exit(1) # remove the .ini app_id = os.path.basename(options.scope_ini)[:-4] regex = re.compile("_"); try: (packagename,hookname) = regex.split(app_id) except ValueError as err: print("Invalid Application ID "+app_id+" "+repr(err),file=sys.stderr) sys.exit(1) #the scope tmpdir, as this is desktop its always unconfined (for now) tmpdir = os.path.expanduser('~')+"/.local/share/unity-scopes/unconfined/"+packagename+"/" if(not os.path.exists(tmpdir)): os.makedirs(tmpdir) print ("ScopeRunner> Setting up environment") print ("ScopeRunner> TmpDir: "+tmpdir) print ("ScopeRunner> AppId: "+app_id) command = shutil.which("unity-scope-tool") if command is None: print("ScopeRunner> unity-scope-tool was not found in the PATH. Please run \"apt-get install unity-scope-tool\"") sys.exit(1) if app_id is None: sys.exit(1) #the config file debug_file_name = tmpdir+app_id+"_debug.json" needs_debug_conf=False conf_obj={} if options.gdbPort is not None: needs_debug_conf=True print("Sdk-Launcher> Checking if gdbserver is installed...") gdbserver_path = shutil.which("gdbserver") if gdbserver_path is None: print("Sdk-Launcher> gdbserver was not found in the PATH.") print("Sdk-Launcher> Please install the gdbserver package on the phone.") sys.exit(1) conf_obj['gdbPort'] = options.gdbPort print("Sdk-Launcher> GDB Port"+options.gdbPort,flush=True) #create the debug description file if required or delete it if not if needs_debug_conf: try: f = open(debug_file_name, 'w') json.dump(conf_obj,f) f.close() except OSError: #error we need to stop print("Sdk-Launcher> Could not create the debug description file") sys.exit(1) elif os.path.isfile(debug_file_name): os.remove(debug_file_name) print ("Debug-helper> Environment initialized, starting the application") print ("Debug-helper> Executing "+command+" "+options.scope_ini) #flush all descriptors sys.stdout.flush() sys.stderr.flush() #replace us with the new process os.execv(command,[command,options.scope_ini]) ./share/qtcreator/ubuntu/scripts/openssh_remove0000755000015600001650000000142012705421114022170 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc adb_shell apt-get remove openssh-server -y ./share/qtcreator/ubuntu/scripts/local_delete_emulator0000755000015600001650000000171312705421114023465 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller SCRIPTPATH=`dirname $0` EMULATOR=$1 #wait for the emulator to go away while pid=$("${SCRIPTPATH}/local_emulator_pid" "${EMULATOR}"); do "${SCRIPTPATH}/local_stop_emulator" "${EMULATOR}" sleep 0.5; done; set -e ubuntu-emulator destroy "${EMULATOR}" --yes set +e ./share/qtcreator/ubuntu/scripts/manifest_description0000755000015600001650000000024712705421114023353 0ustar jenkinsjenkins#!/usr/bin/python import json import sys import codecs f = codecs.open(sys.argv[1], "r", "utf-8") obj = json.loads(f.read()) print obj["description"].encode('utf-8') ./share/qtcreator/ubuntu/scripts/device_writableimage_set0000755000015600001650000000151112705421114024143 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc WRITABLE_IMAGE_FILE=/userdata/.writable_image adb_shell touch ${WRITABLE_IMAGE_FILE} adb_shell reboot./share/qtcreator/ubuntu/scripts/i18n_build_translations0000755000015600001650000000212412705421114023675 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Authors: Niklas Wenzel # Christian Dywan PROJECT_NAME=$1 PROJECT_DIR=$2 PO_FILES=$(ls $PROJECT_DIR/po/*.po) for PO_FILE in $PO_FILES; do FILENAME="${PO_FILE##*/}" LOCALE="${FILENAME%.*}" LOCALE_DIR="$PROJECT_DIR/share/locale/$LOCALE/LC_MESSAGES" mkdir -p $LOCALE_DIR MO_FILE="$LOCALE_DIR/$PROJECT_NAME.mo" msgfmt -o $MO_FILE $PO_FILE done echo "Built translations!" ./share/qtcreator/ubuntu/scripts/local_install_emulator0000755000015600001650000000141612705421114023671 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh set -e EMULATOR_PACKAGE=$1 pkexec sh -c "apt-get -y -f install ${EMULATOR_PACKAGE}" ./share/qtcreator/ubuntu/scripts/qtc_fixmoc0000755000015600001650000000325512705421114021300 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller USAGE="Usage: $0 targetArch targetFramework targetSeries makeOptions" if [ $# -lt 3 ]; then echo "$USAGE" exit 1 fi ARCH=$1 shift FRAMEWORK=$1 shift SERIES=$1 shift SCHROOT_PATH="/var/lib/schroot/chroots/click-${FRAMEWORK}-${ARCH}" SCHROOT_LSBRELEASE="${SCHROOT_PATH}/etc/lsb-release" if [ ! -d ${SCHROOT_PATH} ] then echo "No target for the specified architecture and framework available" exit 1 fi if [ ! -f ${SCHROOT_LSBRELEASE} ] then echo "Could not read any version information from the target, it might be broken" exit 1 fi #get the chroot framework major version set -e MAJ_VER=$( egrep "^DISTRIB_RELEASE=.*" ${SCHROOT_PATH}/etc/lsb-release | cut -d= -f2 | cut -d. -f1 ) set +e if [[ ${MAJ_VER} -ge 14 ]] then echo "Skipping FixMoc step, building for framework version >= 14.x" exit 0 fi find . -name AutomocInfo.cmake | xargs sed -i 's;AM_QT_MOC_EXECUTABLE .*;AM_QT_MOC_EXECUTABLE "/usr/lib/'$(dpkg-architecture -qDEB_BUILD_MULTIARCH)'/qt5/bin/moc");' ./share/qtcreator/ubuntu/scripts/qtc_device_run_go_app0000755000015600001650000000345312705421114023463 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc FOLDERNAME=$2 TARGET_DEVICE=$3 TARGET_DEVICE_PORT=$4 TARGET_DEVICE_HOME=$5 BUILDTARGETS=$6 USAGE="$0 [serialnumber] [foldername] [target_device] [target_device_port] [target_device_home]" set +e #first build on the device ${SCRIPTPATH}/qtc_device_buildpackage_go ${1} ${FOLDERNAME} ${TARGET_DEVICE} ${TARGET_DEVICE_PORT} ${TARGET_DEVICE_HOME} ${BUILDTARGETS} > /tmp/qtc_click_create.log if [[ $? -eq 0 ]]; then set -e PACKAGENAME=$(cat /tmp/qtc_click_create.log | tail -n1 | sed "s/^Successfully built package in //g;s/[']//g;s/.$//g") echo "$PACKAGENAME has been created." else cat /tmp/qtc_click_create.log exit -1 fi SCP="scp -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P${TARGET_DEVICE_PORT}" SSH="ssh -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p${TARGET_DEVICE_PORT} ${TARGET_DEVICE}" adb -s ${SERIALNUMBER} push "${SCRIPTPATH}/qtc_device_applaunch.py" /tmp adb -s ${SERIALNUMBER} push "${PACKAGENAME}" /tmp $SSH "bash -ic 'source /etc/profile; /tmp/qtc_device_applaunch.py /tmp/$(basename ${PACKAGENAME})'" ./share/qtcreator/ubuntu/scripts/qtc_goproject_click_createanddeploy0000755000015600001650000000272712705421114026402 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller . `dirname $0`/functions.inc shift FOLDERNAME=$1 shift TARGET_DEVICE=$1 shift TARGET_DEVICE_PORT=$1 shift TARGET_DEVICE_HOME=$1 shift TARGET_DEVICE_USERNAME=$1 shift BUILDTARGETS=$1 shift set +e echo "${@}" ${SCRIPTPATH}/qtc_device_buildpackage_go ${SERIALNUMBER} "${FOLDERNAME}" "${TARGET_DEVICE}" "${TARGET_DEVICE_PORT}" "${TARGET_DEVICE_HOME}" "${BUILDTARGETS}" > /tmp/qtc_click_create.log if [[ $? -eq 0 ]]; then set -e PACKAGENAME=$(cat /tmp/qtc_click_create.log | tail -n1 | sed "s/^Successfully built package in //g;s/[']//g;s/.$//g") echo "$PACKAGENAME has been created." else cat /tmp/qtc_click_create.log exit -1 fi ${SCRIPTPATH}/qtc_project_click_deploy ${SERIALNUMBER} ${PACKAGENAME} ${TARGET_DEVICE} ${TARGET_DEVICE_PORT} ${TARGET_DEVICE_HOME} ${TARGET_DEVICE_USERNAME} ./share/qtcreator/ubuntu/scripts/qtc_device_buildpackage_go0000755000015600001650000000246212705421114024431 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller SCRIPTPATH=$(dirname $0) . `dirname $0`/functions.inc FOLDERNAME=$2 TARGET_DEVICE=$3 TARGET_DEVICE_PORT=$4 TARGET_DEVICE_HOME=$5 BUILDTARGETS=$6 USAGE="$0 [serialnumber] [foldername] [target_device] [target_device_port] [target_device_home]" if [[ -z $FOLDERNAME ]]; then echo ${USAGE} exit fi ${SCRIPTPATH}/qtc_device_build_go "${1}" "${FOLDERNAME}" "${TARGET_DEVICE}" "${TARGET_DEVICE_PORT}" "${TARGET_DEVICE_HOME}" "${BUILDTARGETS}" echo "Building package" pushd "${FOLDERNAME}/click" ${SCRIPTPATH}/qtc_fix_desktop_comment $(${SCRIPTPATH}/qtc_find_desktopfile ${FOLDERNAME}) popd click build "${FOLDERNAME}/click" ./share/qtcreator/ubuntu/scripts/openssh_version0000755000015600001650000000154012705421114022363 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc set +e ssh-add ~/.ssh/ubuntudevice*id_rsa &> /dev/null set -e adb_shell dpkg -l openssh-server|egrep "^ii"|awk '{print $3}' ./share/qtcreator/ubuntu/scripts/qtc_project_make0000755000015600001650000000133712705421114022455 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen TARGET=$1 make ${TARGET} ./share/qtcreator/ubuntu/scripts/local_search_images0000755000015600001650000000216412705421114023106 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh set -e IFS=$'\n' for EMULATOR_INFO in `ubuntu-emulator list 2>&1|grep -v "no such file or directory"` do if [[ ${EMULATOR_INFO} =~ ^([A-Za-z0-9._%+-]+)([^ ]*) ]]; then NAME="${BASH_REMATCH[1]}" REST="${BASH_REMATCH[2]}" DEVICE_FILE="${HOME}/.local/share/ubuntu-emulator/${NAME}/.device" if [ -f ${DEVICE_FILE} ]; then ARCH=`cat ${DEVICE_FILE}` ARCH=${ARCH//[[:blank:]]/} echo "$NAME${REST},arch=$ARCH" fi fi done ./share/qtcreator/ubuntu/scripts/qtc_device_buildpackage0000755000015600001650000001161712705421114023746 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen SCRIPTPATH=$(dirname $0) DEBEMAIL=`grep -G "^DEBEMAIL" ~/.bashrc` DEBFULLNAME=`grep -G "^DEBFULLNAME" ~/.bashrc` if [[ ! -z "$DEBEMAIL" ]]; then export $DEBEMAIL else CMD=`grep -G "export DEBEMAIL=" ~/.bashrc|sed "s/\"//g"` if [[ ! -z $CMD ]]; then $CMD fi fi if [[ ! -z $DEBFULLNAME ]]; then export $DEBFULLNAME else CMD=`grep -G "export DEBFULLNAME=" ~/.bashrc|sed "s/\"//g"|sed "s/export DEBFULLNAME=//g"` if [[ ! -z $CMD ]]; then export DEBFULLNAME="$CMD" fi fi . `dirname $0`/functions.inc FOLDERNAME=$2 TARGET_DEVICE=$3 TARGET_DEVICE_PORT=$4 TARGET_DEVICE_HOME=$5 USAGE="$0 [serialnumber] [foldername] [target_device] [target_device_port] [target_device_home]" if [[ -z $FOLDERNAME ]]; then echo ${USAGE} exit fi if [[ -z ${TARGET_DEVICE_PORT} ]]; then TARGET_DEVICE_PORT=2222 fi if [[ -z ${TARGET_DEVICE} ]]; then TARGET_DEVICE=phablet@127.0.0.1 fi if [[ -z ${TARGET_DEVICE_HOME} ]]; then TARGET_DEVICE_HOME=/home/phablet/dev_tmp fi SSH_CRED="ssh -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p${TARGET_DEVICE_PORT}" SCP="scp -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P${TARGET_DEVICE_PORT}" SSH="${SSH_CRED} ${TARGET_DEVICE}" RSYNC="rsync --delete -avzP -e" DEV_PROJECT_DIR="${TARGET_DEVICE_HOME}/${FOLDERNAME}" CMAKE_BUILDDIR="build-${FOLDERNAME}-device" echo "Rsync command: ${RSYNC} ${FOLDERNAME} ${TARGET_DEVICE}:${DEV_PROJECT_DIR}/src" pushd "${FOLDERNAME}" set +e if [[ -f Makefile ]]; then make distclean fi set -e popd if [[ ! -e "${FOLDERNAME}/CMakeLists.txt" ]]; then tar -cjf "${FOLDERNAME}.tar.bz2" "${FOLDERNAME}" # remove old files if [[ ! -z ${TARGET_DEVICE_HOME} ]]; then $SSH rm -rf ${TARGET_DEVICE_HOME}/* fi # make sure that the device has the target directory $SSH mkdir -p ${TARGET_DEVICE_HOME} $SCP "${FOLDERNAME}.tar.bz2" ${TARGET_DEVICE}:${TARGET_DEVICE_HOME} $SSH "cd ${TARGET_DEVICE_HOME}; tar -xvf ${FOLDERNAME}.tar.bz2" else # make sure that the device has the target directory $SSH mkdir -p ${DEV_PROJECT_DIR} $RSYNC "${SSH_CRED}" ${FOLDERNAME}/ ${TARGET_DEVICE}:${DEV_PROJECT_DIR}/src fi if [[ -d "${FOLDERNAME}/debian" ]]; then echo "Packaging already exists for project." $SSH "cd ${TARGET_DEVICE_HOME}/${FOLDERNAME}; debuild --no-tgz-check -i -I -S -sa -us -uc" else if [[ -e "${FOLDERNAME}/CMakeLists.txt" ]]; then $SSH "cd ${DEV_PROJECT_DIR}; mkdir ${CMAKE_BUILDDIR}; cd ${CMAKE_BUILDDIR}; cmake -DCLICK_MODE=1 ../src" if [[ $? -gt 0 ]]; then echo "Have you enabled Platform Development Mode? (Devices > Advanced)" exit fi $SSH "cd ${DEV_PROJECT_DIR}/${CMAKE_BUILDDIR}; make && make DESTDIR=package install" if [[ $? -gt 0 ]]; then exit fi echo "Transferring files from device to host.." $RSYNC "${SSH_CRED}" ${TARGET_DEVICE}:${DEV_PROJECT_DIR}/${CMAKE_BUILDDIR}/ ${FOLDERNAME}/../build-${FOLDERNAME}-device echo "Building package" pushd "${CMAKE_BUILDDIR}/package" ${SCRIPTPATH}/qtc_fix_desktop_comment $(${SCRIPTPATH}/qtc_find_desktopfile ${FOLDERNAME}) popd pushd "${CMAKE_BUILDDIR}" click build "package" popd else $SSH "cd ${TARGET_DEVICE_HOME}/${FOLDERNAME}; dh_make -p ${FOLDERNAME}_0.1 -s -y --createorig" if [[ $? -gt 0 ]]; then echo "Have you enabled Platform Development Mode? (Devices > Advanced)" exit fi $SSH "cd ${TARGET_DEVICE_HOME}/${FOLDERNAME}; debuild -i -I -S -sa -us -uc" MISSING_DEPENDENCIES=`$SSH "cd ${TARGET_DEVICE_HOME}/${FOLDERNAME}; dpkg-checkbuilddeps 2>&1|sed 's/dpkg-checkbuilddeps: Unmet build dependencies://'"` adb_shell apt-get --assume-yes install ${MISSING_DEPENDENCIES} $SSH "cd ${TARGET_DEVICE_HOME}/${FOLDERNAME}; dpkg-buildpackage -us -uc -nc" echo "Transferring files from device to host.." FILES=`$SSH "cd ${TARGET_DEVICE_HOME}; ls -1|grep -v ${FOLDERNAME}.tar.bz2|grep -v ${FOLDERNAME}$"` echo $FILES | tr ' ' '\n' | xargs -I FILE $SCP ${TARGET_DEVICE}:${TARGET_DEVICE_HOME}/FILE . echo "..transfer complete!" echo echo "Transferred following files: " echo $FILES | tr ' ' '\n' echo echo "@ $PWD" echo adb_shell apt-get --assume-yes remove ${MISSING_DEPENDENCIES} fi fi ./share/qtcreator/ubuntu/scripts/device_writableimage_has0000755000015600001650000000152612705421114024131 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc WRITABLE_IMAGE_FILE=/userdata/.writable_image adb_shell cat ${WRITABLE_IMAGE_FILE} |grep -oi "no such file"|wc -l ./share/qtcreator/ubuntu/scripts/qtc_device_build_go0000755000015600001650000000455112705421114023116 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller SCRIPTPATH=$(dirname $0) . `dirname $0`/functions.inc FOLDERNAME=$2 TARGET_DEVICE=$3 TARGET_DEVICE_PORT=$4 TARGET_DEVICE_HOME=$5 BUILDTARGETS=$6 USAGE="$0 [serialnumber] [foldername] [target_device] [target_device_port] [target_device_home]" echo "BUILDINGSCRIPT" if [[ -z $FOLDERNAME ]]; then echo ${USAGE} exit fi if [[ -z ${TARGET_DEVICE_PORT} ]]; then TARGET_DEVICE_PORT=2222 fi if [[ -z ${TARGET_DEVICE} ]]; then TARGET_DEVICE=phablet@127.0.0.1 fi if [[ -z ${TARGET_DEVICE_HOME} ]]; then TARGET_DEVICE_HOME=/home/phablet/dev_tmp fi SSH_CRED="ssh -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p${TARGET_DEVICE_PORT}" SSH="${SSH_CRED} ${TARGET_DEVICE}" RSYNC="rsync --delete -avzP -e" LOCAL_RSYNC="rsync --delete -avP" DEV_PROJECT_DIR="${TARGET_DEVICE_HOME}/${FOLDERNAME}" echo "Rsync command: ${RSYNC} ${FOLDERNAME} ${TARGET_DEVICE}:${DEV_PROJECT_DIR}" # make sure that the device has the target directory $SSH mkdir -p ${DEV_PROJECT_DIR} $RSYNC "${SSH_CRED}" ${FOLDERNAME}/ ${TARGET_DEVICE}:${DEV_PROJECT_DIR} DEVICETRIPLET=$($SSH "gcc -dumpmachine") BINDIR="${DEV_PROJECT_DIR}/click/lib/${DEVICETRIPLET}/bin" $SSH mkdir -p "${BINDIR}" $SSH "cd ${DEV_PROJECT_DIR};GOPATH=${DEV_PROJECT_DIR} GOBIN=${BINDIR} TMPDIR=/tmp go install ${BUILDTARGETS}" if [[ $? -gt 0 ]]; then echo "Have you enabled Platform Development Mode? (Devices > Advanced)" exit fi echo "Transferring files from device to host.." $RSYNC "${SSH_CRED}" ${TARGET_DEVICE}:${DEV_PROJECT_DIR}/ ${FOLDERNAME} echo "Syncing files to click package dir" $LOCAL_RSYNC ${FOLDERNAME}/share ${FOLDERNAME}/manifest.json ${FOLDERNAME}/*.desktop ${FOLDERNAME}/*.json ${FOLDERNAME}/click ./share/qtcreator/ubuntu/scripts/qtc_project_click_deploy0000755000015600001650000000575212705421114024206 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen # Author: Benjamin Zeller set -e . $(dirname $0)/functions.inc PACKAGENAME=$2 TARGET_DEVICE=$3 TARGET_DEVICE_PORT=$4 TARGET_DEVICE_HOME=$5 TARGET_DEVICE_USERNAME=$6 if [[ -z ${FOLDERNAME} ]]; then FOLDERNAME=`pwd` fi if [[ -z ${TARGET_DEVICE_PORT} ]]; then TARGET_DEVICE_PORT=2222 fi if [[ -z ${TARGET_DEVICE_USERNAME} ]]; then TARGET_DEVICE_USERNAME=phablet fi if [[ -z ${TARGET_DEVICE} ]]; then TARGET_DEVICE=${TARGET_DEVICE_USERNAME}@127.0.0.1 fi if [[ -z ${TARGET_DEVICE_HOME} ]]; then TARGET_DEVICE_HOME=/home/${TARGET_DEVICE_USERNAME}/dev_tmp/ fi SCP="scp -i ${SSHIDENTITY} -o LogLevel=quiet -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P${TARGET_DEVICE_PORT}" SSH="ssh -i ${SSHIDENTITY} -o LogLevel=quiet -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p${TARGET_DEVICE_PORT} ${TARGET_DEVICE}" # remove old files if [[ ! -z ${TARGET_DEVICE_HOME} ]]; then echo "Lets clear the device tmp folder at ${TARGET_DEVICE}:${TARGET_DEVICE_PORT}${TARGET_DEVICE_HOME}" $SSH rm -rf ${TARGET_DEVICE_HOME}/* fi # make sure that the device has the target directory echo "Lets create the device tmp folder to ${TARGET_DEVICE}:${TARGET_DEVICE_PORT}${TARGET_DEVICE_HOME}" $SSH mkdir -vp ${TARGET_DEVICE_HOME} # transfer click package to device PCK="$(pwd)/${PACKAGENAME}" echo "Transfer the click package ${PCK} to the device" $SCP ${PACKAGENAME} ${TARGET_DEVICE}:${TARGET_DEVICE_HOME} if [[ ${?} -eq 0 ]]; then echo "..transfer complete!" else echo " /!\\ transfer failed /!\\" exit -1 fi echo "List all available click packages on the device" FILES=`$SSH -q "cd ${TARGET_DEVICE_HOME}; ls -1 *.click"` echo $FILES for FILE in $FILES; do echo "Installing $FILE to device.." # note adb shell and adb_shell are two different things, adb_shell comes from functions.inc LOG="$(mktemp)" adb_shell sudo -H -u ${TARGET_DEVICE_USERNAME} pkcon -p install-local --allow-untrusted ${TARGET_DEVICE_HOME}/${FILE} 2>&1 | tee $LOG # adb shell does not return the exit code of what was run which is horrible # (https://code.google.com/p/android/issues/detail?id=3254) if grep "^Fatal error:" $LOG; then echo "pkcon failed" rm -f $LOG exit 1; fi rm -rf $LOG done echo "All files installed succesfully" ./share/qtcreator/ubuntu/scripts/qtc_bzr_info0000755000015600001650000000146312705421114021622 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen bzr whoami LP_ID=`bzr launchpad-login` if [ $? -ne 0 ]; then echo "unknown" else echo ${LP_ID} fi ./share/qtcreator/ubuntu/scripts/openssh_connect0000755000015600001650000000222012705421114022323 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc PORT=$2 USERNAME=$3 IP=$4 SSH_SERVICE_STATUS=`adb_shell getprop persist.service.ssh` if [[ ${SSH_SERVICE_STATUS=} =~ "false" ]]; then adb_shell gdbus call -y -d com.canonical.PropertyService -o /com/canonical/PropertyService -m com.canonical.PropertyService.SetProperty ssh true >/dev/null fi x-terminal-emulator -e "ssh -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $PORT $USERNAME@$IP" & ./share/qtcreator/ubuntu/scripts/click_destroy_target0000755000015600001650000000345412705421114023351 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller ARCHITECTURE=$1 FRAMEWORK=$2 SERIES=$3 NAME=$4 CLEANUP_DIRS=("/var/lib/schroot/chroots/${NAME}-${FRAMEWORK}-${ARCHITECTURE}") CLEANUP_FILES=("/etc/schroot/chroot.d/${NAME}-${FRAMEWORK}-${ARCHITECTURE}") click chroot -a $ARCHITECTURE -f $FRAMEWORK -n $NAME destroy if [ $? -eq 0 ] then echo "click target was removed successfully" exit 0 fi for MOUNTPOINT in $(mount|grep $NAME-$FRAMEWORK-$ARCHITECTURE|awk '{print $3}') do echo "Unmounting ${MOUNTPOINT}" umount $MOUNTPOINT if [ $? -ne 0 ] then echo "Unmount failed... trying to force" umount -f -l $MOUNTPOINT if [ $? -ne 0 ] then echo "Unmounting ${MOUNTPOINT} failed" fi fi done for FILE in $CLEANUP_FILES do echo "Deleting ${FILE}" /bin/rm -f $FILE if [ $? -ne 0 ] then echo "Could not remove file: ${FILE}" exit 1 fi done for DIR in $CLEANUP_DIRS do echo "Deleting ${DIR}" /bin/rm -rf $DIR if [ $? -ne 0 ] then echo "Could not remove directory: ${DIR}" exit 1 fi done echo "click target was removed successfully" exit 0 ./share/qtcreator/ubuntu/scripts/device_developertools_packages0000755000015600001650000000065412705421114025367 0ustar jenkinsjenkinsautopilot-touch qtdeclarative5-test-plugin gdebi-core fakeroot dh-make build-essential qt5-default qtbase5-dev qtdeclarative5-dev libqt5xmlpatterns5-dev qtscript5-dev qttools5-dev qt3d5-dev qtmultimedia5-dev libqt5svg5-dev qtdeclarative5-dev-tools qttools5-dev-tools qtlocation5-dev qtsensors5-dev qtpim5-dev ubuntu-dev-tools debhelper cmake qtbase5-private-dev qtdeclarative5-private-dev libqt5opengl5-dev golang-go pkg-config ./share/qtcreator/ubuntu/scripts/qtc_fix_desktop_comment0000755000015600001650000000244612705421114024055 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen # Author: Benjamin Zeller SCRIPTPATH=$(dirname $0) DESKTOPFILE=$1 # lets read the manifest description and set that to desktop comment - fix bug #1223388 MANIFEST_DESCRIPTION=`${SCRIPTPATH}/manifest_description manifest.json` #lets check if there is a Comment grep -e "^Comment=.*$" ${DESKTOPFILE} if [ $? -eq 0 ] then #replace the Comment with description from manifest, if it is empty sed -i "s/^Comment=.*/Comment=${MANIFEST_DESCRIPTION}/g" ${DESKTOPFILE} else #add a comment if none is in the desktopfile echo -e "Comment=${MANIFEST_DESCRIPTION}\n" >> ${DESKTOPFILE} fi ./share/qtcreator/ubuntu/scripts/local_wait_for_emulator0000755000015600001650000000256112705421114024037 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh set -e SCRIPTPATH=`dirname $0` EMULATOR_NAME=$1 # wait for the emulator process to come up until pid=$(${SCRIPTPATH}/local_emulator_pid ${EMULATOR_NAME}); do sleep 0.1; done; # wait for the emulator to assign the port until port=$(netstat -anp 2>&1|grep tcp|grep ${pid}|head -1|awk '{print $4}'|sed -n 's/.*\:\([0-9]*$\)/\1/p'|egrep -v "^$") do sleep 0.1; done; # disable the setup wizard for the emulator, so it can boot straight to the shell set +e adb -s emulator-${port} wait-for-device phablet-config -s emulator-${port} welcome-wizard --disable|grep -v "already disabled" set -e # return the serial number the adb will use for that emulator echo "emulator-${port}" ./share/qtcreator/ubuntu/scripts/functions.inc0000755000015600001650000000625412705421114021726 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen # prefix the logs with the username, so this does not clash on a multiuser machine # Bug lp:1371807 echo $0 >> /tmp/${USER}_sdk.logs set -e SCRIPTPATH=`dirname $0` SERIALNUMBER=$1 if [ -z "$USDK_CONF_DIR" ]; then echo "USDK_CONF_DIR must be set in order for this scripts to work" exit 1 else CONFIGDIR=$USDK_CONF_DIR fi SSHIDENTITY=${CONFIGDIR}/ubuntudevice_id_rsa USERNAME=phablet function adb_wait { adb -s ${SERIALNUMBER} wait-for-device &> /dev/null } function has_ubuntu_chroot { local HAS_UBUNTU_CHROOT=`adb -s ${SERIALNUMBER} shell "which ubuntu_chroot"` if [[ ${HAS_UBUNTU_CHROOT} ]]; then echo 1 else echo 0 fi } function adbd_restart { adb kill-server &> /dev/null adb start-server &> /dev/null } function adb_devices { adb devices -l } function adb_forward_tcp { set +e adb -s ${SERIALNUMBER} forward tcp:$1 tcp:$2 set -e } function adb_shell { set +e if [[ `has_ubuntu_chroot` -eq "1" ]]; then adb -s ${SERIALNUMBER} shell chroot /data/ubuntu /usr/bin/env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin $@ else adb -s ${SERIALNUMBER} shell $@ fi set -e } function phablet_shell { adb_shell "su ${USERNAME} -c \"$@\"" } function phablet_tools_new_syntax { installed_version=$(dpkg-query --show --showformat '${Version}' phablet-tools) dpkg --compare-versions $installed_version ge 1.0 echo $? } function ubuntu_stamp { adb_shell cat /etc/ubuntu-build } function device_hardware { adb_devices |grep -o device:.*|sed "s/device://" } function device_image_hardware { adb_shell getprop "ro.build.product"|tr -d '\r' } function does_device_hw_match_image_hw { if [[ `device_hardware` == `device_image_hardware` ]]; then echo 1 else echo 0 fi } function device_image_version { ubuntu_stamp } function device_network_state_connected { local NM_STATE=`adb_shell nmcli nm | head -n+2 | awk '{print $2}'` if [[ $NM_STATE != *disconnected* ]]; then echo 1 else echo 0 fi } function wait_for_network_state_switch_on { local RESULT=`device_network_state_connected` while [[ "$RESULT" -eq "0" ]]; do sleep 1 echo -n . RESULT=`device_network_state_connected` done } function clone_network_setup_from_host_to_device { pkexec phablet-network -s ${SERIALNUMBER} } ############################################################# if [[ -z ${SERIALNUMBER} ]]; then echo "Please pass a device serial number." echo adb_devices exit -1 fi if [ ! -d ${CONFIGDIR} ]; then mkdir -p ${CONFIGDIR} fi adb_wait ./share/qtcreator/ubuntu/scripts/device_network_clone0000755000015600001650000000153212705421114023330 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc clone_network_setup_from_host_to_device wait_for_network_state_switch_on echo "The device has connected to a network."./share/qtcreator/ubuntu/scripts/device_hasnetwork0000755000015600001650000000140212705421114022640 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc device_network_state_connected ./share/qtcreator/ubuntu/scripts/device_developertools_remove0000755000015600001650000000154712705421114025110 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc PACKAGES=`cat ${SCRIPTPATH}/device_developertools_packages` adb_shell apt-get -y -f install adb_shell apt-get remove ${PACKAGES} -y./share/qtcreator/ubuntu/scripts/local_start_emulator0000755000015600001650000000172312705421114023361 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh SCRIPTPATH=`dirname $0` EMULATOR=$1 MEMORY=$2 SCALE=$3 PID=$(${SCRIPTPATH}/local_emulator_pid ${EMULATOR}) if [ ! -z "${PID}" ] then echo "Emulator already running with pid ${PID}" exit 1 fi set -e ubuntu-emulator run ${EMULATOR} --memory=${MEMORY} --scale=${SCALE} & set +e ./share/qtcreator/ubuntu/scripts/qtc_device_applaunch.py0000755000015600001650000005016712705421114023740 0ustar jenkinsjenkins#!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # QTC device applauncher # Copyright (C) 2014 Canonical # # This program is free software: you can redistribute 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 . # # Author: Benjamin Zeller import gi from gi.repository import GLib from gi.repository import GObject from gi.repository import Click from click.json_helpers import json_array_to_python try: gi.Repository.get_default().require("UbuntuAppLaunch") from gi.repository import UbuntuAppLaunch as UAL except: #fall back to the old name from gi.repository import UpstartAppLaunch as UAL import json import os import sys import signal import subprocess import argparse import fcntl import shutil import dbus import dbus.service import dbus.mainloop.glib #make glib the default dbus event loop dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) #--------------------------- globals ------------------------------ #buffer for the syslog output, always contains the last or parts of the last line syslogBuffer="" hook_name = None package_name = None package_version = None package_arch = None manifest = None app_id = None tmp_dir = "/tmp/" apparmor_path = None skip_apparmor_denials = 3 # Runner to handle scopes class ScopeRunner(dbus.service.Object): def __init__(self,appid,loop): busName = dbus.service.BusName('com.ubuntu.SDKAppLaunch', bus = dbus.SessionBus()) dbus.service.Object.__init__(self, busName, '/ScopeRegistryCallback') self.appId = appid self.loop = loop @dbus.service.method("com.ubuntu.SDKAppLaunch",in_signature='si', out_signature='') def ScopeLoaded(self, name, pid): if(name == self.appId): #Do NOT change this line, its interpreted by the IDE print("Sdk-Launcher> Application started: "+str(pid), file=sys.stderr,flush=True) @dbus.service.method("com.ubuntu.SDKAppLaunch",in_signature='s', out_signature='') def ScopeStopped(self, name): print("Sdk-Launcher> Scope stopped, exiting",flush=True) self.loop.quit() def launch(self): try: scopeUrl = "scope://"+self.appId if(self._dispatchUrl(scopeUrl)): self.loop.run() #just make the default scope visible again self._dispatchUrl("scope://clickscope") else: print("Sdk-Launcher> Error: Could start the scope.",flush=True,file=sys.stderr) return 1 except KeyboardInterrupt: pass return 0 def _dispatchUrl(self,url): try: bus = dbus.SessionBus() urlDispatcher = bus.get_object('com.canonical.URLDispatcher', '/com/canonical/URLDispatcher') urlDispatcher.DispatchURL(url,"", dbus_interface='com.canonical.URLDispatcher') except dbus.DBusException: print("Sdk-Launcher> Error: Could not start the scope.",flush=True,file=sys.stderr) return False return True def stop(self): self.loop.quit() return None # Runner to handle apps class AppRunner: def __init__(self,appid,loop): self.appid = appid self.exitCode = 0 self.message = "" self.loop = loop def on_failed(self,launched_app_id, failure_type): print("Sdk-Launcher> Received a failed event",flush=True) if launched_app_id == self.appid: if failure_type == UAL.AppFailed.CRASH: self.message = 'Application crashed.' self.exitCode = 1 elif failure_type == UAL.AppFailed.START_FAILURE: self.message = 'Application failed to start.' self.exitCode = 1 self.loop.quit() def on_started(self,launched_app_id): if launched_app_id == self.appid: #Do NOT change this line, its interpreted by the IDE print("Sdk-Launcher> Application started: "+str(UAL.get_primary_pid(self.appid)), file=sys.stderr,flush=True) def on_stopped(self,stopped_app_id): if stopped_app_id == self.appid: print("Sdk-Launcher> Stopping Application",flush=True) self.exitCode = 0 self.loop.quit() def on_resume(self,resumed_app_id): if resumed_app_id == self.appid: print("Sdk-Launcher> Application was resumed",flush=True) def on_focus(self,focused_app_id): if focused_app_id == self.appid: print("Sdk-Launcher> Application was focused",flush=True) def launch(self): UAL.observer_add_app_failed(self.on_failed) UAL.observer_add_app_started(self.on_started) UAL.observer_add_app_focus(self.on_focus) UAL.observer_add_app_stop(self.on_stopped) UAL.observer_add_app_resume(self.on_resume) #start up the application UAL.start_application(self.appid) try: self.loop.run() except KeyboardInterrupt: pass print ("Sdk-Launcher> The Application exited, cleaning up") UAL.observer_delete_app_failed(self.on_failed) UAL.observer_delete_app_started(self.on_started) UAL.observer_delete_app_focus(self.on_focus) UAL.observer_delete_app_stop(self.on_stopped) UAL.observer_delete_app_resume(self.on_resume) return self.exitCode def stop(self): UAL.stop_application(self.appid) def on_sigterm(runner): print("Sdk-Launcher> Received exit signal, stopping application",flush=True) runner.stop() def prepareFileHandle(handle, callback): # make handle non-blocking: fl = fcntl.fcntl(handle, fcntl.F_GETFL) fcntl.fcntl(handle, fcntl.F_SETFL, fl | os.O_NONBLOCK) if GObject.pygobject_version < (3,7,2): GObject.io_add_watch(handle,GObject.IO_IN | GObject.IO_HUP,callback) else: GLib.io_add_watch(handle,GLib.PRIORITY_DEFAULT,GObject.IO_IN | GObject.IO_HUP,callback) return handle def create_procpipe(path,callback): if(os.path.exists(path)): os.unlink(path) os.mkfifo(path) pipe = os.open(path,os.O_RDONLY | os.O_NONBLOCK) return prepareFileHandle(pipe, callback) def readPipe(pipe): output="" while True: try: output += os.read(pipe,256).decode(); except OSError as err: if err.errno == os.errno.EAGAIN or err.errno == os.errno.EWOULDBLOCK: break else: raise # something else has happened -- better reraise if len(output) <= 0 : break return output; def on_proc_stdout(file, condition): output = readPipe(file) print (output,end="",flush=True) return True def on_proc_stderr(file, condition): output = readPipe(file) print (output ,file=sys.stderr,end="",flush=True) return True def create_filelistener(path, callback): if(not os.path.exists(path)): return None handle = os.open(path,os.O_RDONLY | os.O_NONBLOCK) os.lseek(handle,0,os.SEEK_END) return prepareFileHandle(handle, callback) def filter_syslog_line(line): global skip_apparmor_denials if app_id in line: if skip_apparmor_denials > 0: skip_apparmor_denials-=1 return sys.stderr.write("Sdk-Launcher> There has been a AppArmor denial for your application.\n") sys.stderr.write("Sdk-Launcher> Most likely it is missing a policy in the AppArmor file.\n") # do not change this line, it is interpreted by QtCreator sys.stderr.write("Syslog> "+line) def on_syslog_update(fd, condition): global syslogBuffer while True: chunk = os.read(fd,256).decode(); if (len(chunk) == 0): break syslogBuffer += chunk if len(syslogBuffer) <= 0 : return True #read the buffer and filter every complete line try: while(True): idx = syslogBuffer.index("\n") line = syslogBuffer[0:idx+1] syslogBuffer = syslogBuffer[idx+1:] filter_syslog_line(line) except ValueError: pass return True def is_confined (manifest_obj, hook_name): if "apparmor" not in manifest_obj['hooks'][hook_name]: raise Exception("Error: Invalid Manifest file") if "name" not in manifest_obj: raise Exception("Error: Invalid Manifest file") install_dir = "/opt/click.ubuntu.com/" apparmor_path = install_dir+manifest_obj['name']+"/current/"+manifest_obj['hooks'][hook_name]["apparmor"] if (not os.path.isfile(apparmor_path)): raise Exception("Error: Apparmor path is not valid: "+apparmor_path) try: json_file = open(apparmor_path,"r") apparmor=json.load(json_file) json_file.close() except Exception as err: raise Exception("Error: Could not read the apparmor file "+str(err)) if ("template" in apparmor): return apparmor["template"] != "unconfined" #without a template the app is always confined return True; # register options to the argument parser parser = argparse.ArgumentParser(description="SDK application launcher") parser.add_argument('clickPck',action="store") parser.add_argument('--env', action='append', dest='environmentList', metavar="key:value", help="Adds one environment variable to the applications env" ) parser.add_argument('--cppdebug', action='store', dest='gdbPort', help="Runs the app in gdbserver listening on specified port") parser.add_argument('--qmldebug', action='store', dest='qmlDebug', help="Value passed to the --qmldebug switch") parser.add_argument('--hook', action='store', dest='targetHook', help="Specify the application hook to run from the click package") parser.add_argument('--force-install', action='store_true', dest='forceInstall', help="Do not check if the click package is already installed", default=False) parser.add_argument('--no-uninstall', action='store_true', dest='noUninstall', help="Do remove the click package after execution is finished", default=False) options = parser.parse_args() print("Sdk-Launcher> Executing: "+options.clickPck,flush=True) print("Sdk-Launcher> Force Install: "+str(options.forceInstall),flush=True) print("Sdk-Launcher> Skip Uninstall:"+str(options.noUninstall),flush=True) #Do NOT change this line, its interpreted by the IDE print("Sdk-Launcher> Launcher PID: "+str(os.getpid()), file=sys.stderr,flush=True) needs_debug_conf=False conf_obj={} if options.environmentList is not None: print("Sdk-Launcher> Setting env "+", ".join(options.environmentList),flush=True) needs_debug_conf=True conf_obj['env'] = {} for env in options.environmentList: envset = env.split(":") if(len(envset) != 2): continue conf_obj['env'][envset[0]] = envset[1] if options.gdbPort is not None: needs_debug_conf=True print("Sdk-Launcher> Checking if gdbserver is installed...",flush=True) gdbserver_path = shutil.which("gdbserver") if gdbserver_path is None: print("Sdk-Launcher> gdbserver was not found in the PATH.",file=sys.stderr,flush=True) print("Sdk-Launcher> Please install the gdbserver package on the phone.",file=sys.stderr,flush=True) sys.exit(1) conf_obj['gdbPort'] = options.gdbPort print("Sdk-Launcher> GDB Port"+options.gdbPort,flush=True) if options.qmlDebug is not None: needs_debug_conf=True conf_obj['qmlDebug'] = options.qmlDebug print("Sdk-Launcher> QML Debug Settings:"+options.qmlDebug,flush=True) #get the manifest information from the click package try: manifest_json = subprocess.check_output(["click","info",options.clickPck]) manifest=json.loads(manifest_json.decode()) except subprocess.CalledProcessError: print("Sdk-Launcher> Could not call click",file=sys.stderr,flush=True) sys.exit(1) #get the hook name we want to execute if len(manifest['hooks']) == 1: hook_name = list(manifest['hooks'].keys())[0] else: if options.targetHook is None: print("Sdk-Launcher> There are multiple hooks in the manifest file, please specify one",flush=True,file=sys.stderr) sys.exit(1) else: if options.targetHook in manifest['hooks']: hook_name = options.targetHook apparmor_path = manifest['hooks'][hook_name]["apparmor"] else: print("Sdk-Launcher> Unknown hook selected",file=sys.stderr,flush=True) sys.exit(1) if 'version' not in manifest: print("Sdk-Launcher> Version key is missing from the manifest file",flush=True,file=sys.stderr) sys.exit(1) if 'name' not in manifest: print("Sdk-Launcher> Package name not in the manifest file",flush=True,file=sys.stderr) sys.exit(1) package_name = manifest['name'] package_version = manifest['version'] #get the package arch # (well, even more strictly it would match what "dpkg-deb -f foo.click Architecture" says) try: package_arch = subprocess.check_output(["dpkg","-f",options.clickPck,"Architecture"]) package_arch = package_arch.decode() except subprocess.CalledProcessError: print("Sdk-Launcher> Could not query architecture from the package",flush=True,file=sys.stderr) sys.exit(1) #check if the app is already installed on the device, so we do not break existing installations db = Click.DB() db.read(db_dir=None) arr = json_array_to_python(db.get_manifests(all_versions=False)) for installAppManifest in arr: if installAppManifest["name"] == package_name: if (not options.forceInstall): print("Sdk-Launcher> Error: This application is already installed on the device, uninstall it or temporarily change the name in the manifest.json file!",flush=True,file=sys.stderr) sys.exit(100) else: print("Sdk-Launcher> Uninstalling already installed package (--force-install)") success = subprocess.call(["pkcon","remove",package_name+";"+package_version+";"+package_arch+";local:click","-p"],stdout=subprocess.DEVNULL) if success != 0: print("Sdk-Launcher> Uninstalling the application failed",flush=True) # Continue even though we could not uninstall the app, the user wanted to override it anyway # but for scopes we need to stop, because the scope may still be running and we need a clean state if "scope" in manifest['hooks'][hook_name]: sys.exit(1) #build the appid app_id = None debug_file_name = None app_mode = None powerd = None loop = GLib.MainLoop() runner = None if "scope" in manifest['hooks'][hook_name]: app_mode = False app_id = package_name+"_"+hook_name runner = ScopeRunner(app_id,loop) if(not os.path.exists(tmp_dir)): os.mkdir(tmp_dir) elif "desktop" in manifest['hooks'][hook_name]: app_mode = True app_id = package_name+"_"+hook_name+"_"+package_version runner = AppRunner(app_id,loop) else: print("Sdk-Launcher> Hook is not supported, only scope and app hooks can be executed",flush=True) sys.exit(1) print("Sdk-Launcher> Installing application .....",flush=True) #we have all informations, now install the click package success = subprocess.call( ["pkcon","--allow-untrusted","install-local",options.clickPck,"-p"]) if success != 0: print("Sdk-Launcher> Installing the application failed",flush=True,file=sys.stderr) sys.exit(1) print("Sdk-Launcher> Application installed successfully",flush=True) debug_file_name = None stdoutPipeName = None procStdOut = None stderrPipeName = None procStdErr = None syslogHandle = None try: confined = is_confined(manifest,hook_name) if (app_mode is True and confined): tmp_dir = os.path.expanduser('~')+"/.local/share/"+package_name+"/" elif (app_mode is True and not confined): tmp_dir = os.path.expanduser('~')+"/.local/share/"+package_name+"/" elif (app_mode is False and confined): tmp_dir = os.path.expanduser('~')+"/.local/share/unity-scopes/leaf-net/"+package_name+"/" elif (app_mode is False and not confined): tmp_dir = os.path.expanduser('~')+"/.local/share/unity-scopes/unconfined/"+package_name+"/" else: #error we need to stop raise Exception("There was a error specifying the communication directory.") debug_file_name = tmp_dir+app_id+"_debug.json" if(not os.path.exists(tmp_dir)): os.makedirs(tmp_dir) print("Sdk-Launcher> AppId: "+app_id,flush=True) print("Sdk-Launcher> Architecture: "+package_arch,flush=True,end="") print("Sdk-Launcher> Application confined: "+str(confined),flush=True) print("Sdk-Launcher> Communication directory: "+tmp_dir,flush=True) #create the debug description file if required if needs_debug_conf: try: f = open(debug_file_name, 'w') json.dump(conf_obj,f) f.close() except OSError: #error we need to stop raise Exception("Could not create the debug description file") #create 2 named pipes and listen for data stdoutPipeName = tmp_dir+app_id+".stdout" procStdOut = create_procpipe(stdoutPipeName,on_proc_stdout) stderrPipeName = tmp_dir+app_id+".stderr" procStdErr = create_procpipe(stderrPipeName,on_proc_stderr) try: syslogFileName = "/var/log/syslog" syslogHandle = create_filelistener("/var/log/syslog",on_syslog_update) except OSError: print("Sdk-Launcher> Not able to open syslog, apparmor errors will not be reported.",flush=True,file=sys.stderr) if "unix_signal_add" in dir(GLib): GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, on_sigterm, runner) GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, on_sigterm, runner) GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGHUP, on_sigterm, runner) else: GLib.unix_signal_add_full(GLib.PRIORITY_HIGH, signal.SIGTERM, on_sigterm, runner) GLib.unix_signal_add_full(GLib.PRIORITY_HIGH, signal.SIGINT, on_sigterm, runner) GLib.unix_signal_add_full(GLib.PRIORITY_HIGH, signal.SIGHUP, on_sigterm, runner) #unlock the phone powerd = subprocess.Popen(["powerd-cli", "display", "on"],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL) sessionBus = dbus.SessionBus() unityGreeter = sessionBus.get_object('com.canonical.UnityGreeter','/') unityGreeterIFace = dbus.Interface(unityGreeter,dbus_interface='com.canonical.UnityGreeter') unityGreeterIFace.HideGreeter() #execute the hook, this will not return before the app or scope finished to run exitCode = runner.launch() except Exception as err: print(repr(err),flush=True) exitCode = 1 if(powerd): powerd.terminate() #clean up the debug conf file if it still exists if needs_debug_conf: try: if (debug_file_name != None and os.path.isfile(debug_file_name)): os.remove(debug_file_name) except: print("Sdk-Launcher> Could not remove the debug description file: "+debug_file_name+"\n Please delete it manually",flush=True,file=sys.stderr) #close the pipes if (stdoutPipeName != None and os.path.exists(stdoutPipeName)): os.close(procStdOut) os.unlink(stdoutPipeName) if (stderrPipeName != None and os.path.exists(stderrPipeName)): os.close(procStdErr) os.unlink(stderrPipeName) if (syslogHandle): os.close(syslogHandle) if (options.noUninstall): print("Sdk-Launcher> Skipping uninstall step (--no-uninstall)") else: success = subprocess.call(["pkcon","remove",package_name+";"+package_version+";"+package_arch+";local:click","-p"],stdout=subprocess.DEVNULL) if success != 0: print("Sdk-Launcher> Uninstalling the application failed",flush=True) print("Sdk-Launcher> Finished",flush=True) sys.exit(exitCode) ./share/qtcreator/ubuntu/scripts/device_search0000755000015600001650000000251512705421114021726 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen if [ "$1" = "1" ]; then adb kill-server > /dev/null adb start-server > /dev/null fi IFS=$'\n' for DEVICES_INFO in `adb devices -l | grep -Ev "List of devices attached" | grep -Ev "emulator-" | sed "/^$/d"` do if [[ ${DEVICES_INFO} =~ ^([a-zA-Z0-9]+)(.*)$ ]]; then NAME="${BASH_REMATCH[1]}" REST="${BASH_REMATCH[2]}" ARCH=`adb -s ${NAME} shell dpkg --print-architecture 2>/dev/null|egrep -v "device not found"` if [ $? -ne 0 ] then continue fi ARCH=${ARCH/arm*/armhf} ARCH=${ARCH//[[:blank:]]/} echo "$NAME$REST arch:$ARCH" fi done ./share/qtcreator/ubuntu/scripts/qtc_device_buildanddeploypackage0000755000015600001650000000277312705421114025651 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc FOLDERNAME=$2 TARGET_DEVICE=$3 TARGET_DEVICE_PORT=$4 TARGET_DEVICE_HOME=$5 if [[ -z ${TARGET_DEVICE_PORT} ]]; then TARGET_DEVICE_PORT=2222 fi if [[ -z ${TARGET_DEVICE} ]]; then TARGET_DEVICE=phablet@127.0.0.1 fi if [[ -z ${TARGET_DEVICE_HOME} ]]; then TARGET_DEVICE_HOME=/home/phablet/dev_tmp fi SCP="scp -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P${TARGET_DEVICE_PORT}" SSH="ssh -i ${SSHIDENTITY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p${TARGET_DEVICE_PORT} ${TARGET_DEVICE}" ${SCRIPTPATH}/qtc_device_buildpackage ${SERIALNUMBER} ${FOLDERNAME} FILES=`$SSH "cd ${TARGET_DEVICE_HOME}; ls -1 *.deb"` for FILE in $FILES; do echo "Installing $FILE to device.." adb_shell gdebi --n ${TARGET_DEVICE_HOME}/${FILE} done ./share/qtcreator/ubuntu/scripts/device_developertools_install0000755000015600001650000000163612705421114025260 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc PACKAGES=`cat ${SCRIPTPATH}/device_developertools_packages` adb_shell apt-get -y -f install adb_shell apt-get update adb_shell apt-get upgrade -y adb_shell apt-get install ${PACKAGES} -y./share/qtcreator/ubuntu/scripts/qtc_find_desktopfile0000755000015600001650000000252212705421114023320 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen # Author: Benjamin Zeller SCRIPTPATH=$(dirname $0) PROJECTNAME=$1 DESKTOPFILE_COUNT=`ls *.desktop|wc -l` DESKTOPFILE="" if [[ ${DESKTOPFILE_COUNT} -eq 1 ]]; then DESKTOPFILE=`ls *.desktop` elif [[ ${DESKTOPFILE_COUNT} -eq 0 ]]; then exit 1 else # do we have a desktop file which has the same name as projectname if [[ -f ${PROJECTNAME}.desktop ]]; then # found one lets use it DESKTOPFILE=${PROJECTNAME}.desktop else # it seems that we did not have one, lets use the latest desktop file. DESKTOPFILE=`ls -t *.desktop|head -n1` fi fi echo "${DESKTOPFILE}" exit 0 ./share/qtcreator/ubuntu/scripts/device_wait_for_shell0000755000015600001650000000223312705421114023457 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh SERIALNUMBER=$1 # Waiting for device on ADB set -e adb -s ${SERIALNUMBER} wait-for-device # The device is accessible on ADB" set +e error=$(adb -s ${SERIALNUMBER} shell echo ok 2>&1) if [[ ${error} =~ "closed" ]]; then echo "DevLocked" else echo "DevUnLocked" fi set -e # Start waiting for Unity8" until pids=$(adb -s ${SERIALNUMBER} shell pidof unity8 2>/dev/null|egrep -v "^$"); do sleep 0.1; done; echo "Unity8 is up with PID: ${pids}" set +e ./share/qtcreator/ubuntu/scripts/device_service_ssh_start0000755000015600001650000000170612705421114024214 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc if [ ! "$(adb_shell getprop persist.service.ssh)" = "true" ];then adb_shell gdbus call -y -d com.canonical.PropertyService -o /com/canonical/PropertyService -m com.canonical.PropertyService.SetProperty ssh true >/dev/null fi ./share/qtcreator/ubuntu/scripts/qtc_launch_gallery0000755000015600001650000000266612705421114023011 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen if [[ `dirname $0` =~ ubuntu-sdk-ide ]]; then echo Run from IDE EXAMPLES_PATH="$(/usr/ubuntu-sdk-dev/bin/qmake -query QT_INSTALL_EXAMPLES)/ubuntu-ui-toolkit/examples" export LD_LIBRARY_PATH=/usr/ubuntu-sdk-dev/lib/ else EXAMPLES_PATH="/usr/lib/ubuntu-ui-toolkit/examples" fi if [ ! -d "${EXAMPLES_PATH}" ] then echo "Could not find the ubuntu-ui-toolkit examples, please make sure they are installed" exit 1 fi echo "Copying ubuntu-ui-toolkit-gallery from ${EXAMPLES_PATH} to /tmp" mkdir -p /tmp/ubuntu-ui-toolkit-gallery cp -r "${EXAMPLES_PATH}/ubuntu-ui-toolkit-gallery"/* /tmp/ubuntu-ui-toolkit-gallery echo "Opening project in Qt Creator" ubuntu-sdk -client /tmp/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery-click.pro & ./share/qtcreator/ubuntu/scripts/device_is_ubuntu0000755000015600001650000000153412705421114022476 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller . `dirname $0`/functions.inc adb_shell "su -c 'test -f /etc/lsb-release && (cat /etc/lsb-release | grep DISTRIB_ID) || echo \"DISTRIB_ID=UnknownAdbDevice\"'" ./share/qtcreator/ubuntu/scripts/openssh_install0000755000015600001650000000157612705421114022355 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc #${SCRIPTPATH}/qtc_device_developertools ${SERIALNUMBER} adb_shell apt-get -f install adb_shell apt-get update adb_shell apt-get install openssh-server -y ./share/qtcreator/ubuntu/scripts/device_version0000755000015600001650000000202012705421114022135 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc adb_shell getprop "ro.product.model" adb_shell getprop "ro.product.name" device_image_hardware device_image_version #adb_shell uname -p|sed 's/v7l/hf/' adb_shell 'dpkg --print-architecture 2>/dev/null ' adb_shell click framework list|sort -n|egrep "^ubuntu-sdk-[0-9]{2}\.[0-9]{2}[[:space:]]$"|tail -1 ./share/qtcreator/ubuntu/scripts/qtc_device_debughelper.py0000755000015600001650000001104612705421114024244 0ustar jenkinsjenkins#!/usr/bin/python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # QTC device applauncher # Copyright (C) 2014 Canonical # # This program is free software: you can redistribute 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 . # # Author: Benjamin Zeller import json import os import os.path import sys import subprocess import shlex import shutil import re def is_confined (): test_dir = os.path.expanduser('~')+"/.local/share" try: os.listdir(test_dir) #if we reach the return statement we are not in confinement return False except: pass return True app_id = None args = None tmpdir = "/tmp/" mode = sys.argv[1] confined = is_confined() if mode == "scope": app_id = sys.argv[2] args = sys.argv[3:] regex = re.compile("_"); try: (packagename,hookname) = regex.split(app_id) except ValueError as err: print("Debug-helper> Invalid Application ID "+app_id+" "+repr(err),file=sys.stderr) sys.exit(1) if(confined): tmpdir = os.path.expanduser('~')+"/.local/share/unity-scopes/leaf-net/"+packagename+"/" else: tmpdir = os.path.expanduser('~')+"/.local/share/unity-scopes/unconfined/"+packagename+"/" elif mode == "app": app_id = os.environ.get('APP_ID') #i hope this works pos = app_id.rfind("_") pos = app_id.rfind("_",0,pos) regex = re.compile("_"); try: (packagename,hookname,version) = regex.split(app_id) except ValueError as err: print("Debug-helper> Invalid Application ID "+app_id+" "+repr(err),file=sys.stderr) sys.exit(1) args = shlex.split(sys.argv[2]) tmpdir = os.path.expanduser('~')+"/.local/share/"+packagename+"/" else: print("Debug-helper> Unsupported script mode (scope|app)") sys.exit(1) stdoutPipeName = tmpdir+app_id+".stdout" if(os.path.exists(stdoutPipeName)): newStdOut = os.open(stdoutPipeName,os.O_WRONLY | os.O_NONBLOCK) os.dup2(newStdOut, sys.stdout.fileno()); stderrPipeName = tmpdir+app_id+".stderr" if(os.path.exists(stderrPipeName)): newStdErr = os.open(stderrPipeName,os.O_WRONLY | os.O_NONBLOCK) os.dup2(newStdErr, sys.stderr.fileno()); print ("Debug-helper> Setting up environment") print ("Debug-helper> TmpDir: "+tmpdir) print ("Debug-helper> AppId: "+app_id) print ("Debug-helper> Environment: "+("confined" if is_confined() else "unconfined")) if (args[0][0] == "/"): effective_cmd = command = args.pop(0) else: effective_cmd = command = shutil.which(args.pop(0)) if command is None: print("Debug-helper> Executable was not found in the PATH") sys.exit(1) if app_id is None: sys.exit(1) debug_file_name = tmpdir+app_id+"_debug.json" if os.path.isfile(debug_file_name): f = open(debug_file_name,"r") try: debug_settings = json.load(f) except: print("Debug-helper> Could not load the settings file") sys.exit(1) if "qmlDebug" in debug_settings: args.insert(0,"-qmljsdebugger="+debug_settings["qmlDebug"]) if "gdbPort" in debug_settings: effective_cmd = shutil.which("gdbserver") if effective_cmd is None: print("Debug-helper> gdbserver was not found in the PATH") sys.exit(1) args.insert(0,":"+debug_settings["gdbPort"]) args.insert(1,command) #work around bug LP:#1327216, cgroups authentication for Mir not available #remove this line as soon as its fixed if mode == "app": args.append("--desktop_file_hint="+app_id) if "env" in debug_settings: for key in debug_settings["env"]: os.environ[key] = debug_settings["env"][key] f.close() os.remove(debug_file_name) #execv wants the command again in the arguments args.insert(0,effective_cmd) print ("Debug-helper> Environment initialized, starting the application") print ("Debug-helper> Executing "+effective_cmd+str(args)) #flush all descriptors sys.stdout.flush() #replace us with the new process os.execv(effective_cmd,args) ./share/qtcreator/ubuntu/scripts/click_create_target0000755000015600001650000000254512705421114023123 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Benjamin Zeller ARCHITECTURE=$1 FRAMEWORK=$2 SERIES=$3 NAME=$4 SCRIPTPATH=$(dirname $0) click chroot -a $ARCHITECTURE -f $FRAMEWORK -n $NAME exists if [ $? -eq 0 ] then echo "The click target ${NAME}-${FRAMEWORK}-${ARCHITECTURE} exists already" exit 1 fi click chroot -a $ARCHITECTURE -f $FRAMEWORK -n $NAME create if [ $? -eq 0 ] then echo "The click target was created successfully, installing required packages" click chroot -a $ARCHITECTURE -f $FRAMEWORK -n $NAME maint apt-get install -y cmake exit 0 fi echo "There was an error creating the click target, cleaning up" "${SCRIPTPATH}/click_destroy_target" ${ARCHITECTURE} ${FRAMEWORK} ${SERIES} ${NAME} exit 1 ./share/qtcreator/ubuntu/scripts/device_portforward0000755000015600001650000000173212705421114023032 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc shift SSHPORT=$1 shift if [[ -z $SSHPORT ]]; then SSHPORT=2222 fi echo "Forward Local ${SSHPORT} to Remote 22" adb_forward_tcp ${SSHPORT} 22 while (( "$#" )); do echo "Forward Local ${1} to Remote ${1}" adb_forward_tcp ${1} ${1} shift done ./share/qtcreator/ubuntu/scripts/i18n_update_template0000755000015600001650000000252112705421114023153 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen SCRIPTPATH=`dirname $0` DISPLAYNAME=$1 PROJECTFILES=${@:2} SOURCEFILES= for PROJECTFILE in ${PROJECTFILES}; do case ${PROJECTFILE} in *tests*) ;; # ignore tests folder *qml) SOURCEFILES+="${PROJECTFILE#$PWD/} ";; esac done ############################################################ echo echo "Creating po/${DISPLAYNAME}.pot from" for SOURCEFILE in ${SOURCEFILES}; do echo ${SOURCEFILE} done ############################################################ mkdir -p po xgettext -o po/${DISPLAYNAME}.pot --qt --c++ --add-comments=TRANSLATORS --keyword=tr --keyword=tr:1,2 ${SOURCEFILES} --from-code=UTF-8 echo "..created!" ./share/qtcreator/ubuntu/scripts/local_package_installed0000755000015600001650000000156212705421114023747 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh set -e EMULATOR_PACKAGE=$1 dpkg -l ${EMULATOR_PACKAGE} 2>&1 |egrep -v "no packages found matching"| awk '{print $1 " " $2 " " $3}' | grep "ii" || echo "The package is not installed." ./share/qtcreator/ubuntu/scripts/device_writableimage_unset0000755000015600001650000000150612705421114024512 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Juhapekka Piiroinen . `dirname $0`/functions.inc WRITABLE_IMAGE_FILE=/userdata/.writable_image adb_shell rm ${WRITABLE_IMAGE_FILE} adb_shell reboot./share/qtcreator/ubuntu/scripts/local_emulator_pid0000755000015600001650000000145312705421114023000 0ustar jenkinsjenkins#!/bin/bash # Copyright 2014 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh set -e EMULATOR_NAME=$1 ps -efww|grep "${EMULATOR_NAME}/sdcard.img"|grep -v grep|awk '{print $2}'|egrep -v "^$" ./share/qtcreator/ubuntu/scripts/local_create_emulator0000755000015600001650000000224212705421114023464 0ustar jenkinsjenkins#!/bin/bash # Copyright 2013 Canonical Ltd. # # This program 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; version 2.1. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Author: Zoltán Balogh set -e EMULATOR_PATH=$1 EMULATOR_NAME=$2 EMULATOR_ARCH=$3 EMULATOR_CHANNEL=$4 EMULATOR_PASSWORD=$5 WHOAMI=$6 USERHOME=$7 GROUP=$(id -gn ${WHOAMI}) if [[ -z ${EMULATOR_CHANNEL} ]]; then EMULATOR_CHANNEL="devel" fi if [ ! -d ${EMULATOR_PATH} ] then mkdir -p ${EMULATOR_PATH} fi HOME=${USERHOME} /usr/bin/ubuntu-emulator create ${EMULATOR_NAME} --arch=${EMULATOR_ARCH} --channel=${EMULATOR_CHANNEL} --password=${EMULATOR_PASSWORD} chown ${WHOAMI}:${GROUP} ${EMULATOR_PATH} -R ./share/qtcreator/ubuntu/qml/0000755000015600001650000000000012705421120016310 5ustar jenkinsjenkins./share/qtcreator/ubuntu/qml/devicespage.qml0000644000015600001650000000144412705421114021310 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 import "Components" import "DevicesPage" MainView { id: modeRoot width: 860 height: 548 Page { header: PageHeader { id: header title: "Ubuntu Devices" sections.model: ["Devices", "Log"] } DevicePage{ anchors.top: header.bottom anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right visible: header.sections.selectedIndex == 0 } LogPage{ anchors.top: header.bottom anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right visible: header.sections.selectedIndex == 1 } } } ./share/qtcreator/ubuntu/qml/welcome.qml0000644000015600001650000001565412705421120020471 0ustar jenkinsjenkins/* * Copyright 2016 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen * Author: Benjamin Zeller */ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.1 import "Components" Rectangle { color: "#F7F6F5" property int maximumWidth : 900 anchors.fill: parent Image { anchors.fill: parent fillMode: Image.Tile source: "images/bg_dotted.png" } Rectangle { id: contentBackground color: "#fff" anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.bottom: parent.bottom anchors.margins: 0 width: parent.width > maximumWidth ? maximumWidth : parent.width } ScrollView { anchors.fill: parent ColumnLayout { id: topLayout x: contentBackground.x width: contentBackground.width spacing: 0 GridLayout { id: grid Layout.alignment: Qt.AlignTop Layout.leftMargin: 20 Layout.rightMargin: 0 Layout.topMargin: 10 Layout.fillWidth: true Layout.maximumWidth: topLayout.width - 20 flow: GridLayout.TopToBottom rows: 3 Text { wrapMode: Text.WordWrap font.family: "Ubuntu" font.weight: Font.Light font.pointSize: 42 font.letterSpacing: 2 text: "Make it Ubuntu" Layout.fillWidth: true Layout.topMargin: 30 } Text { wrapMode: Text.WordWrap font.family: "Ubuntu" font.weight: Font.Light font.pointSize: 20 font.letterSpacing: 1.5 textFormat: Text.RichText text: "Get started today with the Ubuntu SDK and the App Design Guides." onLinkActivated: { Qt.openUrlExternally(link); } Layout.fillWidth: true Layout.topMargin: 20 } Item { Layout.fillHeight: true Layout.fillWidth: true } Column { Layout.rowSpan: 3 Layout.margins: 0 Layout.minimumWidth: topLayout.width / 2 Layout.maximumWidth: topLayout.width / 2 Layout.alignment: Qt.AlignTop Image { width: parent.width source: "images/devices.png" fillMode: Image.PreserveAspectFit } } } Column { id: topLinks Layout.minimumWidth: topLayout.width Layout.maximumWidth: topLayout.width Layout.margins: 20 Layout.topMargin: 0 spacing: 5 Link { width: parent.width title: "Create a New Project >" onClicked: ubuntuWelcomeMode.newProject() pixelSize: 20 } Link { width: parent.width title: "See Ubuntu Touch core apps @ Launchpad >" link: "https://launchpad.net/ubuntu-phone-coreapps/" pixelSize: 20 } Link { width: parent.width title: "Design something beautiful >" link: "http://design.ubuntu.com/apps" pixelSize: 20 } Link { width: parent.width title: "Build something solid in QML >" link: "http://developer.ubuntu.com/api/qml/current/" pixelSize: 20 } Link { width: parent.width title: "Build something solid in HTML5 >" link: "http://developer.ubuntu.com/api/html5/current/" pixelSize: 20 } Link { width: parent.width title: "Open UI Toolkit component gallery >" onClicked: ubuntuWelcomeMode.openGallery() pixelSize: 20 } Link { width: parent.width title: "Stay up to date with the developer community >" link: "http://developer.ubuntu.com/blog" pixelSize: 20 } } Column { id: bottomBox Layout.fillWidth: true Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter Layout.topMargin: 20 Layout.leftMargin: 20 Layout.bottomMargin: 0 spacing: 5 Text { wrapMode: Text.WordWrap width: parent.width font.family: "Ubuntu" font.pixelSize: 26 font.weight: Font.Light text: "Get in touch" } Text { wrapMode: Text.WordWrap width: parent.width font.family: "Ubuntu" font.pixelSize: 18 textFormat: Text.RichText font.weight: Font.Light text: "By joining our mailing list and for real-time communication join #ubuntu-touch on freenode." onLinkActivated: { Qt.openUrlExternally(link); } } } } } } ./share/qtcreator/ubuntu/qml/Components/0000755000015600001650000000000012705421114020440 5ustar jenkinsjenkins./share/qtcreator/ubuntu/qml/Components/Link.qml0000644000015600001650000000407612705421114022057 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ import QtQuick 2.4 Rectangle { id: linkRoot width: linkTitle.paintedWidth height: 60 color: "transparent" opacity: 0.8 property alias title : linkTitle.text property alias pixelSize : linkTitle.font.pixelSize property url link property color defaultColor : "#DD4814" property color hoverColor : "#eee" signal clicked() Rectangle { id: background color: hoverColor opacity: 0 anchors.fill: parent Behavior on opacity { NumberAnimation { duration: 250 } } } Text { id: linkTitle anchors.centerIn: parent wrapMode: Text.WordWrap width: parent.width - 20 font.family: "Ubuntu" font.letterSpacing: 1.5 color: defaultColor font.weight: Font.Light textFormat: Text.RichText text: "" } MouseArea { anchors.fill: parent hoverEnabled: true onEntered: { background.opacity = 1; } onExited: { background.opacity = 0; } onClicked: { if (link !== undefined) Qt.openUrlExternally(link); linkRoot.clicked() } onPressed: { linkTitle.color = Qt.lighter(defaultColor); } onReleased: { linkTitle.color = defaultColor; } } } ./share/qtcreator/ubuntu/qml/Components/UbuntuStyleToolbar.qml0000644000015600001650000000134412705421114025003 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtQuick.Controls 1.3 as Controls import QtQuick.Controls.Styles 1.3 import Ubuntu.Components 1.3 Controls.ToolBar { id: toolbar style: ToolBarStyle { background: Rectangle { id: styledItem implicitWidth: 100 implicitHeight: units.gu(5) + divider.height color: theme.palette.normal.background Rectangle { id: divider anchors { left: parent.left right: parent.right bottom: parent.bottom } height: units.dp(1) color: Qt.darker(theme.palette.normal.background, 1.1) } } } } ./share/qtcreator/ubuntu/qml/Components/FeatureStateItem.qml0000644000015600001650000000232512705421114024370 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtQuick.Layouts 1.0 import Ubuntu.Components 1.3 import Ubuntu.Components.ListItems 1.3 as ListItem import Ubuntu.DevicesModel 0.1 ListItem.Standard { id: item property var input: null property string inputRole property alias checkable: switchbox.enabled onInputChanged: { if(input == FeatureState.Available) switchbox.checked = true; else switchbox.checked = false; } selected: false control: Row { ActivityIndicator { visible: input === FeatureState.Unknown running: visible } Switch { id: switchbox visible: input !== FeatureState.Unknown checked: input === FeatureState.Available enabled: checkable onCheckedChanged: { if (checked && input == FeatureState.NotAvailable) { devicesModel.set(index,inputRole,true); checked = true; } else if ((!checked) && input == FeatureState.Available) { devicesModel.set(index,inputRole,false); checked = false; } } } } } ./share/qtcreator/ubuntu/qml/Components/ScrollableView.qml0000644000015600001650000000306112705421114024070 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.4 import Ubuntu.Components 1.3 Item { id: template width: units.gu(40) height: units.gu(75) default property alias content: layout.children property alias spacing: layout.spacing property Item tools: null property Flickable flickable: flickable Flickable { id: flickable objectName: "TemplateFlickable" anchors.fill: parent anchors.topMargin: units.gu(2) anchors.bottomMargin: units.gu(2) contentHeight: layout.height interactive: contentHeight > height Column { id: layout spacing: units.gu(6) anchors.left: parent.left anchors.right: parent.right anchors.margins: units.gu(2) } } Scrollbar { id: sb objectName: "TemplateScrollbar" flickableItem: flickable property alias interactive: sb.__interactive } } ./share/qtcreator/ubuntu/qml/Components/SectionItem.qml0000644000015600001650000000423112705421114023376 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.4 import Ubuntu.Components 1.3 import Ubuntu.Components.ListItems 1.3 as ListItem ListItem.Expandable { id: expandingColumnItem expandedHeight: contentColumn.height + units.gu(1) collapsedHeight: headerRow.height divider.visible: false property string title property string imageSource default property alias data: contentColumn.data onClicked: { expanded = !expanded; } Column { id: contentColumn anchors { top: parent.top; left: parent.left; right: parent.right } Row{ id: headerRow anchors { left: parent.left; right: parent.right} height: Math.max(24,label.paintedHeight) Image { source: expandingColumnItem.expanded ? "qrc:/ubuntu/images/view-collapse.svg" : "qrc:/ubuntu/images/view-expand.svg" width: 24 height: 24 } Item{ width: units.gu(1) height: 1 } Image { source: imageSource width: 16 height: 16 visible: imageSource.length > 0 anchors.verticalCenter: parent.verticalCenter } Item{ visible: imageSource.length > 0 width: units.gu(1) height: 1 } Label { id: label text: expandingColumnItem.title fontSize: "large" } } } } ./share/qtcreator/ubuntu/qml/Components/NewsBox.qml0000644000015600001650000000453012705421114022542 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program 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; version 2.1. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Juhapekka Piiroinen */ import QtQuick 2.4 import QtQuick.XmlListModel 2.0 ListView { clip: true property url link model: XmlListModel { source: link query: "/rss/channel/item" XmlRole { name: "title"; query: "title/string()" } XmlRole { name: "link"; query: "link/string()" } XmlRole { name: "description"; query: "description/string()" } XmlRole { name: "pubDate"; query: "pubDate/string()" } } delegate: Item { id: delegate height: itemContent.height+ 20 width: delegate.ListView.view.width Column { id: itemContent width: parent.width Text { text: pubDate font.pixelSize: 10 font.family: "Ubuntu" font.weight: Font.Light } Text { text: title font.family: "Ubuntu" font.pixelSize: 20 font.letterSpacing: 1.5 wrapMode: Text.WordWrap font.weight: Font.Light width: parent.width baseUrl: link } Text { text: description font.family: "Ubuntu" font.pixelSize: 12 wrapMode: Text.WordWrap font.weight: Font.Light font.letterSpacing: 1.5 width: parent.width textFormat: Text.RichText baseUrl: link } } MouseArea { anchors.fill: parent onClicked: { Qt.openUrlExternally(link) } } } } ./share/qtcreator/ubuntu/qml/Components/DeviceStatusTab.qml0000644000015600001650000000426412705421114024213 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtQuick.Controls 1.0 as Controls import QtQuick.Layouts 1.0 import Ubuntu.Components 1.3 import Ubuntu.Components.ListItems 1.3 as ListItem import Ubuntu.DevicesModel 0.1 RowLayout { Controls.ScrollView { id: scrollView Layout.fillHeight: true Layout.minimumWidth: units.gu(78) ColumnLayout { width: scrollView.width Row { Label { text:"Device Status: "+detectionStateString fontSize: "large" } Item { width: units.gu(2) height: parent.height } ActivityIndicator{ visible: deviceItemView.deviceBusy running: visible } } ListItem.SingleValue { text:i18n.tr("Serial ID") Layout.fillWidth: true value: serial } ListItem.SingleValue { text:i18n.tr("Device") Layout.fillWidth: true value: deviceInfo } ListItem.SingleValue { text:i18n.tr("Model") Layout.fillWidth: true value: modelInfo } ListItem.SingleValue { text:i18n.tr("Product") Layout.fillWidth: true value: productInfo } FeatureStateItem { text: "Has network connection" input: hasNetworkConnection inputRole: "hasNetworkConnection" Layout.fillWidth: true checkable: hasNetworkConnection == FeatureState.NotAvailable && !deviceItemView.deviceBusy } FeatureStateItem { text: "Has developer mode enabled" input: developerModeEnabled inputRole: "developerModeEnabled" Layout.fillWidth: true checkable: !deviceItemView.deviceBusy } ListItem.Divider{} DeviceKitManager{ Layout.fillWidth: true } } } } ./share/qtcreator/ubuntu/qml/publishpage.qml0000644000015600001650000001225012705421114021331 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 import Ubuntu.Components.ListItems 1.3 import "Components" MainView { width: units.gu(48) height: units.gu(60) Component { id: validationDelegate Rectangle { height: descText.height width: scrollView.width color: !(index % 2) ? "#FFFFFF" : "#E5E4E2" //block mouse events MouseArea { anchors.fill: parent } Row { id: headerRow height: descText.height spacing: 2 //spacing Item{ width: units.gu(5) height: 1 } Image { anchors.verticalCenter: parent.verticalCenter width: 16 height: 16 source: ImageRole } Column { id: descText anchors.verticalCenter: parent.verticalCenter Text { text: display + "\n" + DescriptionRole } Link { width: parent.width height: LinkRole.length > 0 ? 30 : 0 title: LinkRole link: LinkRole } } } } } Page { title: "Ubuntu Publish" Item { id: spacing anchors.top: parent.top height: units.gu(2) width: 1 } Connections { target: publishModel onBeginValidation: { validationSection.expanded=true; logSection.expanded=false; } } Row { id: topRow anchors.top: spacing.bottom anchors.horizontalCenter: parent.horizontalCenter spacing: units.gu(2) Button { visible: publishModel.showValidationUi text: "Validate existing click package" onClicked: { publishModel.on_pushButtonReviewersTools_clicked(); } } Button { visible: publishModel.showValidationUi enabled: publishModel.canBuild text: "Build and validate click package" onClicked: { publishModel.on_pushButtonClickPackage_clicked(); } } Button { text: "Install on device" onClicked: { publishModel.log = ""; publishModel.buildAndInstallPackageRequested(); validationSection.expanded=false; logSection.expanded=true; } } } Row { id: warningsRow anchors.top: topRow.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: units.gu(2) spacing: units.gu(2) visible: !publishModel.canBuild Icon { source: "qrc:/ubuntu/images/warning.png" } Label { text: "The selected Kit does not support building click packages. Please make sure to select a Ubuntu Kit." } } ScrollableView { id: scrollView clip: true anchors.top: warningsRow.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom SectionItem { id: validationSection title: "Validation" visible: publishModel.showValidationUi Repeater { model: VisualDataModel{ model: publishModel.validationModel delegate: SectionItem{ title: TypeRole imageSource: ImageRole expanded: ShouldExpandRole Column { Repeater{ model: VisualDataModel{ model: publishModel.validationModel rootIndex: modelIndex(index) delegate: validationDelegate } } } } } } } SectionItem { id: logSection title: "Log" expanded: true TextArea { autoSize: true maximumLineCount: 0 anchors.left: parent.left anchors.right: parent.right height: units.gu(60) highlighted: true readOnly: true text: publishModel.log textFormat: TextEdit.AutoText } } } } } ./share/qtcreator/ubuntu/qml/images/0000755000015600001650000000000012705421114017560 5ustar jenkinsjenkins./share/qtcreator/ubuntu/qml/images/bg_dotted.png0000644000015600001650000000017712705421114022226 0ustar jenkinsjenkinsPNG  IHDR/nLtEXtSoftwareAdobe ImageReadyqe<PLTEaIDATxb`d'IENDB`./share/qtcreator/ubuntu/qml/images/devices.png0000644000015600001650000166573612705421114021740 0ustar jenkinsjenkinsPNG  IHDRvbKGD pHYs  tIME4+ IDATx[m[vzc*Uc <"䪼F)yED.!+ Eײ rթ֜~+.IkM}k1{ ~܏q??̾ S߫*D??ԠPTa`̀_W _Ļ{BBпu D7W C?@ *~]NXπD4ߟIDGC ]+"B"ey7< 5^pk`fi{kWk]/֯ӣ߷q?w8Wfd`N S֟<0Tb)qzP 8*1cA1ߋA) & 0l@`k_s`f(fEL U] L 2ߵ~Qe^o]wY )o,^(=?gœ,`@&10`Y`"a 8SA1Dc P 0_k" 9` 57M 08!D#211EG0Sf3c1p')d- @c''p=56ve40DſjPav@e?`6pDS nPu05uKX˽)hLPcv`b1_')Va*0Rb8&CU ]k|-0TbߠC0r)!#R 5"P0B@cC`a~M0@gn Uwv-V "& ٿ[1 50yu{~o \vfNB:̀C`B6 @Ɓ;af~]ݏ6_z8|ݏU4߾mhvxxиAcLm}GѷA~4m;zSx/иw}@mL Uo{ ֱ;m( "F߷o &m03l^?Awf~܏q?M(T]maa4pb?zûテc8%:L;pַWcD< Lb@lP%܎+ R!' 4&1T @S$Ek1@F/4e^^;ne@hhAXqDG3D@~N@ qCkbu4J7r vy! 8p5$h 0@;Z'xӉ!XRvmcy7sUlA7\Txx:L'./јt{A@ . SeF)0"4~-}1d0!0t\o7̾.ÈagL5(ȿsž{cL@CcÐcvDOo 36F"}24r0s393F@ocPZ`v at,.` #q iFp o<; ` o,<f@̀ij e(\r'mh`*R!F8`G^`>x @ gvg o1w{B HyT| C}ۘp rwl״0c8%./ C pJܖNT+L'F[4pa̸q?<1pnoޮxop=xz oq':OCH=_ɟ!q}zrv8y8v 9 G8Do|5>+ۉ@"xk@!)"xg7PԂY1MQDžX09ж }cr&x'lBBhێFwNˇ qž?ٟnWy1Ɓm7@ƌ} f@`p?]Q1e3Ġ:PjAط9o(6A~71{6!^X- 1Xwqމs1~]o#*@@lڎSbP]vv@m8gmzq ^|S3f;Woim+vyㆶo8Ex$4"w0x?7W~x˿u÷t eALcAiz)C@vC.#SڀEһSqml T wh{W 7Qܮ^mλ4ذ?8QL0(Nco q+& "<+ps8`Vah8+^|S#xE`_@fnR /Ay^Q1-7<%5Aox{;Af0!c8I^cDDjطr*3m8_p]aɳ9Su0`>e]"&qew%Jh=9C3jfƾx<]D$ Br fz=A䍀ˋdv쭹Ѐm!3Rj e 9oxx}sV 6h[p\qٲЈq;N <ԪeC8qYFa BBhՃ7Sp`7{9CjC 5O~3ܚ73U`~܏q?q7s׿5 msۧ+a_t15po[)xȥqCT7yDj)W/a">]Aж|t H,ˎF:YQ C},XfG eypn⌨qÛ%: q\ 1NϽ`90T2B1I 8p\df b8'°5gy9 ՁuƸCt' c;; Jt2pԂU8}PbqJ}F_Q Fc 1usz{1ZK<\6l䌍5OpR971BVIddqy W۬A0r3 U/:LVL{Й} :' ALBr 4$d uyl#@ d6@vC#P% W5Ћpfh(|>M(39l!gli\LјpDF:k?,+w etŭ uVg\PTgPgg8}s&|欬Ѓ4 ŞS@i݆?$:p =A`W}!b|3B kF_bȜp!%'`>OSoAp0t@$c~pxCDI` X߻}տ/B+oęT%^,|ޓw0x?ov^͛7x+{÷>w 7meppA9No;TN{Go ;|ɢgnx\ܡocxQLl Ftoy*c y*Pr*8E`ⲽSnPu u}zo>=9vޢm79|4~>p0@8;P/4t0"rC75QHNНe!g ÔЙ}P.Ed?8A ƄN(8bAPlMޜQBĄC61ZG514ac6stݠ&s<3LhZ0k3[aFyF Ux>XaCtD`9ɜs!la03l%/K4!l‡, 1bErQjQc@4>-"5s.{7mQPSu~l0 D@b̀ YDr<>^1Dx%^nf.Aa. ͉FC:h1Kbv 禍Aa%SjĀ=AB@F}m98gmFj~7nd^($ 8|6ùr*qKPh˩#+h"yD} 매 {㌵pWKlUWCX O3Q!0kb.9o3{0OҌ+xpF=&/.ckmp;qcq4 ?#F`19Bf (-#gk|Jys(F }*_йmnz1;6#hێ}`ryͶ |6eBr@ V >k%|N3`XJ={l4g`H,]|OYfJ03!YDw[$3/T@!c)REp~1/%-BVNa ԋg5Ooiۢ8Iً18+J[ /L_؎`#Őt pdk &晛5MZ3b/AŪ=j P8y]-`Ӓ;>-0"(E f.w0h@b1b)\\yJW\Mf$`.?&S(Ec7c})̺䠳ExuFVK~gɺj--ip P jhsf5kjxI 3y@$إv4 i5;c1e}lޜoIԻA h~ ݁X6b׌əfPePv ȀR4(L9?3)0pH>0@PMl7*NlqI~w|G~uh(0!JfA AxK{jC4c;Ec$vX;@9{! T1Zc͊Lkǟˈ]\b O+gHy303g__Pw‹] b0 .[)@4/ә5mh<),*U!hZEjLEPu~GFs JJŹ(5qFjNA#R"-6$FYca(NSp֘r?kcenVBϋ`3#3B'buhf): K:GVeAcaD!fQQ]|*q͢`F!l[\WAN6@X7*űV9,*/" @]ke3-gnizkqUZmG5 m{0zΖwz(k#p<; nrM) LRޛWF3D଻38#e Ѳ: y5ϧ!o)^Ȓmm$٥<Rdq8h8+{WΌ'펣0mᜬNnb֕ÌIA((l\V-y-f&MRGB3anMZ{d]ۖfl8abuQعWE3Ǽ1_5Qf(;&Ê5O؋o(iXUA{`a8ֵVce;~܏~~p' ^Q<<0R2@s(zbvC댗//_w~ ү3 IDAT*_v;eË}ë}þm ˎ}۰]6g }ktw*3D5+ LU]HUΖu5Yd RȘU.VO\- K_ƃ33h)|;^~񙪐p@ Qdȴo7]^p#,`aҁkP {o uq ,OӒ\farI %Q%x &`uZ% 33f,ym<(wm=\2cf jb瓷:,Jq, #ۈnKg%8 J{VBUF&Z{wCY.ι%*f#筼H\Pwqad邻K63hO$,Kk'pr;%.E3I "ǬN6&d\g8Swk-hXPYuAH5L0y6,ܴ4Tkdd#X VC OhEaF)cu':`2n?j;Bow$e˫8c~8P9V(Vu'sͼl.iH}=V YyC:K/Ϲ@ VJ2-Hl(g%gZĖP0؟PHNIaFO 4k+Tی9`c$q5,Z.2|fejR51L<Ă%q?H(˿i|w~;q]o779^| ~x?߈ߍO|+za*1cĠ|x z0Ֆ>?M䄊]ʮ>L-T#ҫkn,lakjެEyȐb**3jМ 8%Ts8I@NZ"iL9}O*$V"h9x6ɜq ́@g.㼝=Hg 3ms%HgJըhXϜ52`LZ%͋kr#GKtLfĜ/ף!&곌 ?Δga(e!Ye'%YkN-i0v-f\&3k(5SyFwjd`SZ[@[0i S0iCȜJjJ@hL9x| GP(MOL6>?L,V1~j)v+59{Tfp@ɲr LʃfTL/?Z{\6 ۦ(9o W S]L `, d~$KY=׽:ܿ)Δ+S D|F=)94A'^!FS,:_`0`ʵh#5}PTRy- [6]e1[E<7J }QMukG;+ڱI&rc.݈F3TJS~_x=NQ|;?O|,e掑"3Og?GS-||S» cH7);ǹ@@vSC\ҼdU]dB M5>++GQv '" )eXbCJSv@,-Q{ƶ%X * M+)G7~"c/CgsW̢M(3-PugLE>Y;ӐR9ّN(y,k:g^6 5dR3£}L;GM&yUF $! JA5ELӑs jm!dX967@Q˔ҜL\ElrcK !Yh-d,VjLhhQ:GT!`|(P?o% 8aSg(ewbF`$ d"a HGt`%]52)d,"4 gq!- 뜅LkKCBRKF3S5Mе֦E6n3kg>l{P|'F!Vg8E֙E třgUQa!( i!芢$.;'W?0 "FFap0*dSD-"RP2gM-Ŕa26&М,  x 4$Ԅ>[̀a [̃jΣ#siWc!Naʛf@[g>Ww|w>|o4^^.85c6 ¬ ;3IN&ٌJ򂳹W/f-D9Ee xTDQO̚+JM"?%i.Ub>E@H{h|AU$$aX qI+PN@M.$xiZZ%q?Ƿ3?S>Oӟ${C-"CK'77|ⓟ߆?W.> b)'SEXjMb)`O#d-7YQ0盒1JH,mϜ a><衭\]` l)c>ك ,X2EᓳuQ5WB+,#Jg%Io:הTa\iaKg,hqN7CٴZ[#,AYJ?ҵV2UI0E.cdx5iP" XZ @iBnsL$,!6jX?3J5J*b-.0:50͵2ggV֜jϒ gAOlݼ60LR5RL7\yTFKL|n3%sӐc5Ak|{YU/dkY5̪e I 8*jo1(%.ԝe0gx9oco:H2O6>s 463WhJ-q?V<~sٟ<>OmÛ qcw<ݮ?8~om|w~_;~309bs%,JCiDeoQ4q4y93Vp+ lΤaJkj!*j19slt,Ci:k>`qPǵfK)t5ôD_YTLldfa&UТy TLZ&֔ѳ9 DRVd,,@*X,kY[Rrsa 0OQ̼I0.|*i&=sI-E佹,_}n9 ~4|5PP.)JrkD\!M+;0$fb|oy{- S3Bj)*[30KļPbʵ"cQ@ $gˌ&VN44: 6[m!={C}T->єǺSbaL@Ya\lxq?D:tzV[&8owI3in6֘67s9%B@9h/"{bT1:{Dh54:Y0.QV8X> {6)Y8Bo/(7v 'H#,3 79v4bMNKO2r4Ry111ٴ\.sB!eO"nFlRbE#%˾51v[<2LeJ!Ki$ьV𙳛XR]cwf~܏-vyo~8#q,>cwo~+>At*x$Yncҵ9K;Z Dg&*<vLeΕvJ?bMDe6Eens$rCSWnvVf)4$$CwGu]%)в(<`"3:y]f|iB7af($(MweUov~).nRg˿i2|'e Y*DLL[S!c3,3b}pFAj3&i>2zTM&sF suP0)l($^4 GbbB{h ]}ssT1({?oσ~Z>Q8g6zzxmz %GCit,K$PXQ*H9GQC̋3IX^ni:ZGxJ%p.k4Ue嚁r?['ȱcQx2K9e7 -ӎ, "hg /M9URo' Hvuviώa5k&!ҔH4د̬M>PiF#jr ʲ='=ta"8X6 of̜) dbo[de6B!P4: @RiH϶UJ,M 1 WעXꀧ;NT9Ҝ@">VGAo1eZBK7Yr"bi-y%`r EYsvU6!.ղ"xq-l[q\LK+ ɮ=$5ŌS| r|UH3:6q" )zEłL]\cQn:Y(䈗"|"@ . ((%u3(rYEv[:Hm-Sz QI Q`,9-Zu3nP25nL(= #@TUdSgХA#`[$绛[?Yo~t)Ul[q<>=~?"~w ۲/ V'P)u4N|m!6̹'HCNƢBZHP(3gCW*F4;fVYYJfTeR+JYy9PcXYJ896Xekɧθ4SUmVt2YE2TΖ9>}Rsh1(X(/dfadtkaN3$ʸBMGy?q1@c?_V*(=V'\4^<)S͘L$-: 月e-,֥ЧcDItaD+yмӄFU4-fNd*9 dLf4gW̡,-᨟w\U6̚7A"=uY}G !?Ȋ @$W0fdcM6% ʠ+#5Ee$Fo 'kkǝP)%Nqa""A IS%Vt~L: k>9Myk IDAT[HlpY }>˙sJ&ɴ@,/Nך!Xg.khr0)ϧX=Q("l~9U*kuHgO``Yb_,Ms" 0I1Fk/%w΍ےy~܏_Woya'^YO+Sp!??OO_w +wW NX\7 ߬I)kJdVԜ%!d_yp "j6a9%%3l-C+0N\큦$fv!c _e' fVVA/E>^$Ԓ9r1RO^ (2А%K&qar͊uZLi? .s4؃H+ȁ h& fQwT+)H\"Jw*m6DQ2Oy]FJygmR{6),nMdkΪ(-ӝ1=sjlV 4y?/ \m[医ȒpAs%@j:K4Bx1k9HYJ;)T ,>%7.4l9}{vJ! 5:ýkNSgCOz 9&l,,g;dٖ_t6c)9"Mgj:gr>Y}FJHsj<b;T҉B[lo2(զu8~{x?z|o~G[381udva\p"Ig>~o>|˿?bS)[V;R RIt+CISv}+=+Rȩ.e)ks $hdIA.MZ};kPu6zUY}4Jdy)$. w8"G0]bsʈ dQE*ElF Cvh ԗߛνI(R$CiJ(P&RxhԬkZƭ58ud1pyaÀ$)1gQ# 5T7}_C'p6 tRޔR'ץEFDľL1Mlr6%1ךq6-4gAHbs/& x>fQ٢i4=eZSXTL.T'\*` WIQ%(哾3 CYN5鸗N\Idgֆ9Ì&gh֟^Hc4o\ϛ`Ls?' t$+O%`,8h>C9T30f|-nf\KQs/]-1c 2Vݙq?ǯCD%뻾L5y dґ銧)ğiG=׿)]7xDk-:QT~$M9eF0@bԦu>o!tSiɎ>-La5ϗ'%HCpiTuN^C)\ŊԷ5j;Zt̕^Y'.e瘅%0íf"۪xEjMAu gP+72!<Cս zk>KTdfKU Mfȵm1br]d=}9% ТnD+,Q.ɮdLw=_W4֜=>'[-5#P)sjݿ- L17qwmͮZ\rN**\쀅 x!O CO,%Dl Hl/qٱBlSR_yhؚ5JGOfZ^FD S# PN޹YFOH)6o9=Y51eQ(;a(,*k #15"m_ s`Gϓͅ架gR꒰\"R~܏6^oxc#;p<8^} ?~~ __//}cJmd #ΰbF 0!G(6Uu` lFSf'Q e$i4 `/)aM 8fV^.'P/ -Js7Dd8hWИIƔ4/oQؼR?` j˜:؜.$V0=َ7ORAlkC$LEӦ@M)YM)*TQNwt(#6律Ԧnŭf# Q֣< x RMCE;'HW~[xZ6ȷ|ygtCHZ릈T󤊃T-vy/u}ĄD4VEkb*hmDczi{}V9Wx>[ņsRN3?OD,n ^h_pxPEI,.4`s=M:EL.鉷M 3:D6>@RQ~ĸO?kDgRyV-\JHw{eH;Iֺe#F$aƥ*T\b IA^ ޏq??|7/~Wtρ<]7ϟ?k'< Rn#c @ɇ$f$Pջey.d$2*PdRm]zCsWX&o4@dKfizYFo|L֍eYcE#a.BfClw6ruWjkӺ/FCwb;lh" s^C ~PLc';SLGnBP3Rx,^7({eY\ m;_GJO9]5/6XC©j a oy*Pw!uDYRzT+d:Fݪ]BF@DgFi{9@IbޒHVb6%GA ؎&PK cwjW_8Y1BʏH&8+e6@F>`X0KˣKM+psBJm!,UEI;I<×`Į:epaji0DtER`{d+SA[A?o79ydE -X5bh9?>]q'O,]y_EGlTf# {*~|?v5,Qnw!ϧZAj\䣐A.LH9"/6Β 1RT,F#VĈz%{7B\'Cթ гϖ`8sZ b&ih6&|©5u0#s,ׂM9'?w0z'ma;NK}ʣc^ ޏq?3?oWqc%'|v W/_Ͼ ~ }6c#LKV@\!y1\h,[lM(hbXܕfiYG#薛0#Zbbe *1`03|rBTh\V8XN  RBެZc q L VԾ` 8ÛW_}@vp%5K8?o,xW Xgv,kb(w::Vs*{ZrIzGfo,n$Wq^# ?J ŒܸCLmX iJj(c:)*Azʢk?YsÏZ>}3+3,}Z-|0CeG4Χ{=Szm@fa̙h]՚3{ x} ^<<<׿ oYo@ F.ؙ0ݲjw{+jʫ2UI+$&WnInyŰ.D3JGh@'m\uE6l1οshe9JYFĻ e+\s|XgiT} #]`h".ll6bw®ު?nSl fReiz5*R0h TGO?sCvGQ3$br[zz{tr+)^|_;='(FR,;8Μ@A܀SH Q6ީ;oKILO73XeQJS!f- y3kN:a!|f?Ž"yKEDijZl©8>~܏7>=?]8Wx8+!|W|G7~׽˃f_JbƍXfMR&ImɣJnv*с"Dr sz}3ΚF9hwXo&wQzgiFlNv$h$i.k+ ɛNb=7E ހUVE@h{U!am~oվNG#ỨoWdB c Z*K:/G-y[­9.m7OLg^_3k/OĻkgA!Y~lic{&TŢ2`,yѢK)#[ D2_fg1QAnQdԞRXhL;/&Uf]5N\0`ڡ\szZLsr%ˋR[~B6ԼijAMo246ͿeyyPr|Hʢ~7Qcs$/Z^2U1 f鋊 ٢R5ɨ$^M % bfkzp:2JxȲ:-^& 1 %QGA)MjlX%!_ 5Ȣ) GOuW 8YǂkI"Ǩ >Y="ݱ~h:̂zR8pL[plXxAuIjBJTE^,V*b\N:v.8.u]ナg̑ZiH$2GK1?`.ᙴ.7e0{Ue5CzqC5vE䳤eQ^]q\<} .~~K<]DH|+57P1LDx%؛$)3 to^Nҹsڡ3<#Z: aFB1Eau-YeLAWu&4Hl:*BʏMxI0<\KjB+u-U,1|vL hSf(llF꘏EHd^Z>g,D>$zI'[ 9!Fň14,:K,UhDej\R"zR# g/~o+ Gxx2)!lj.0NaqS ]-%lzHsMo-"Oe#x7Phf.s9<˺Ϙ==GK 02}n2oӤK3ƵHå WL;8Qʎ5G@ԀgQ+3bAQkrY[=pH'|G8&bLlړQڧd:Н}IҾ/W Wh/"4#]7Ȼ!E Udɯ%o-7+'Bgw^;=p|nb5)e0t]Z, kY]KXWSăUн U֢KMR%zr^3|+/"_n|募GS<>#<1Yd"\0GgSIEA8Q|N7;p`}O6|YQ[^@ cn̼b V=WTƄH3n| j`={`qCBcLMZ9=vQBwcXw/lL|۪bĊd vS淰|ssl\HuʑbXrLF* 7]]7ԴCmUkcgc=@kơWG Ȼ)>p|F9xݡ>iJN/gͳ rb*o=Nm\(0$vsIE/D4E S16t ظ(sk(=,4\ ׽( B; VR'S"z$La"DTt`=x9՟~?3_k~?;|f} {j&y٢ ˢ2f4<#&*ګ$F -U eYŵjdٸYn}j -2/ץՓ=FoZÐRƃa%:=-]D7B%]wZͿk#'5FNeyK< "wNdQl!܄V%{E̸ ..ZQ"+b"״nfppv-E~܏o}!Wóc]2u0; /_?g #ÿy6Y5*Xq E.ar>&8 Alk|,Ɯ>c+ 0!%dسR@hu,dNZ R6RAM@.M񵃙u+˯讖~PQ=qC7l&>GN7I"8}sô*ɃJmF~Pa-6 o6 >|a{d:&_lԒx@uͣM&^2Vc@Evg GyQ@,f*.gz&FI!fO&*P*Vn4A$3H]M[ABf4SkuM d,eCŸ,&Kyc&MFNKWgSr]tQ,[oz^ax%y睚k֌9"RvhzIXiU4H"J9KP9Qc!+ʕ{X#"I QAHEoͯ x$cO1:PuT XM[jX'}VA# UfrJ{XC12V:ʥgCakatpdP%dkU/j\6VjEÆAC]bRu [$oۑk_ƖGnZ0 ~6{1x?x#__>k ANӉ◿/xPa.Z̀R2΃^#:A0]k6\J0Lcn3 1I(ۢOʿB{qt]@4BQu7!++zSmO$h/ 1@4:b3_1squPwV_Rq*#WM^V`ƉQA0(dQɨ>}C<䯋GPgP|AOPJx+9,TRNh8-,57v5'KA@Gh!񛛟ń,Nѳcy"}Q|ǜVMÚ"ɖEK,9&5~8QF`84hO` d'h &Os"Q^I!wb'ʏ,5jmXykFQz3?S~,gsg\;̠FlҞ2[H8zD*U4J'FttqN* Jhm".r޵'ٌS1%L;?=Vll7e]A3C_ :JR{8=ݨVӹr`cMkd$8BR=0MA-T8-q{dh,pw^hoRϲq5hahЁ}A~y]%T7[=`֘ZK gNS~G~/wdǧxz^O27"MsOHoٍ+e6nn؎whOL0F5xZgpͲ{% Wa1򟒋K9=` + J:Y)iRUpgјTn,RѮ յ*鷨8.ЕS)dc #{c`AwsOE.ȇ ( D'eY*sJVf ȍEO:ϱޜ Fm[CRfI]55zI%" %p(Z vp E+}Egf>  e%u,'.Mfbac#Be 7QC.ۚT /na"-Q|=NtI+-VXեq_M% F2T}3`³_^@W"{JU`M#3Q{Ft8 7;JXS/Tǐ\c10'+5yg-5n|żԹ=h U^mw <~竿>~ΠS8 7s)^~*Gi&o5{"hwM2)"lP[Pu:25M]xB89Sv n>~@Q:J/@~9[̽3B|FS5_:0aI!o#MŠ㨬4m˫# :6Jĝ1s*^*+P1`kN4=뉗8^]ذICU ,NgZz7Kӆ4c8֝Et X99fy4H{!1/ɓkM.E]c>H9\D( k-._ySD Ei,׽ EBVH$/'6׋1 L=%#P+]4%6ӚbXEcT.NeL=n 0(bKm#*f! "'/JNHMJƭ $^,깲l |lPEV*[yw`iչ5Kf'Ef=p\̫E(볯҄4Nz :fEFX QF:f lD[ebl{gͰlp cTejCꉦXb>=m8s޶h|f柇$PkXensb%;M/`s1D:Rf8jbh<+%/`KZhJ)#HJ'D«l/k#<`f [[^b8B!C܋q?Ǜu;~|o^q8 l8.\ )7Wkλ>Y{5d 1Mӥ&rα\"lE. KpHnifO*, ) 9\o׫)իqQaՀ`%ˑgՙHf t`; K`ӟaimɱJjr| rjy!1dBτ)o%COɣ*jd4^ҭ P\sn,7,ȎfC=h&ޡ)7SM+}z$|2qֶ)):g0pcC)5=9 H5֨JM6\^ g>H6l*6Φ[eM\88]Z-4n4]bN_V{ʜjy~JI*(vfe*d<11GjAŽ8eQOqݣ Ūိ8mFWtЀ֖D^I"&[R^jd 0[,$n 消(顓Dsby&qT3C`~Ng)-HU8.; r4T"u.Q;-Sf'. ;7ͫ lxy-:GsJu22鿰 VM(* =0P63l(D$u|If%,Iؾ1C_<ݐbL5,$M{1x?x?|ű.x8.7y^ ??>?o _RXIW0P޽ky߿(R9I%l(2TPz_#+(;{(h_*pzyvNZ/]ouY-"(Znw廙oD UTkQT<gj,r+vQ IDATk7x[}fTs1dt'70rΪ>%ciʽ$c xi3K30XkNW7,Q@K1z$1[DN;I&n<ҳ.{MCm7 (f=]7nu#ϘR@f&Ex1%Mu>S'wÒx<1SSޭ{aT)O$S>KP :3(H5'>X Ooi)bל`檗 ;bLS2 O0֢{;{(79n>uM-2['#ires]h8/rGzOek`NjDTurY@h;@~܏F7pyc"ϞX\ύs^_`[)uQ\(hs8a)ހhI oݴP'{gW]P &[V )TX,I`@K5g`t;e;y7zp˻%G|(U1r ʌ*>=g6mS6vfsׄZlM2Znd~" 8J:SXKp6r =#̼ʹ&v㦴v.ʬ4>_**..b, hN ޅRQ9"e=v jx>]g'I4q~C,̮wr3$4mRw=^4v7r5_Z6 rbu[0QH/ҿV6}jsYZ"Jެ^*rQ+2PqIDwVn 6 ˃,Mj l: ƔχW95[0l,*76 eyXk"E oRuޤ`]\q78V wpioNF["[;))HJcpǥHkq>* dԲ!P);fKeRΩvNBDU%UPQOIZJFmlx`e!ެPoPC1~~h)9.j, 3,4q?u~7q1\|X-x| ۯ ={ 9O ZZk3U5֚&yEPh78ahoPDcd}IPǭd]S@ >C_2pKӁ#R.NwNhDcsb:Bd>WXE8ECl̔BLMND/Av<F( qm]aGD/)^ί~^[w洏GSv97Po}T".*}ǽI fDB+~ ]%THwpd<'Q0X79G  afUq(25DMRNQʮfPvI8jS+Qxe,@GwPrq[ H.[^)]&D ̮D\4*H- T[rᒣ`dV\$Z(Z泵]A6?QϣQI&d+.1T6Oy`0&AAuBBrhȧi\+r1$ /rjmUq{M':0 o=8#h:E9h*/{ T>}yaC/NFP ]^&1MvNk5+}^a"IySWFdQ m׵[o'ú!gk1p߯q?x#G޿/].8ϟr,z?r7Ë/٥~3g|6KfmMhֆ@Ee#[Z Dc $Oh# ;W&*؎. N#8CD# rd@1.e2֟Ia8M ͤu`Xp3vV&)׽ȺmrYG(u ,vޗht0C-N1`u8S}(b~܏>#|?g<]p=Ogr9pqa|~ sK䣀ӑ aK3_Q5 Tf`7O!IkPuZ28D#L\!J E,Tٞ \>w" /c <}ojɈE6L)0[:s=/7Iu.d7%**qjhs jIsOVhures{p6TUwQtM+tN %$S|eD2k7&*o!O5*=9}.gXJYgDF0Z.k+=QT1 pMq:mhTDfq<+kzlYy(elEQUMJ6-o2%Noy5da\Plh(Dn*Z[瓨 Y$j^ScjT`Br R VgPj70O*H<hr\+אđ p8 ^m=V̬7ˬ496NͧAb,;͚yI2El؇.b zs Ilkao춦*^6â*s0;$ƲHzwK@]@ZDSjJ&:HL Щܦz9!nDRFm',L~|JI ?:K΋U*F$7e٨=J&bQɷ0^Z<0]w,E-AB.UbaO昼Jp-{- = ^B6BS+w*R0}8b$aZ^!sYL_񩉫6^,5S YnO\•${2ad3M8o/:4AW6: z#j>IN &qx23 8ca$ISͩ^9yar}1lxgHQ7e &ccLQY=}p&(j /L' gj0?eSN\#+6l]?B}(IߘA:_;:pc9Odm}2x?xco|x xoٳ(i[Dz^?3<&yV 2 O~EԺ`e Ol 1n\M5A8!**_oA 0N @BQFv[k =m}!ȕ=BJswTբ0WnQGɐduP7'z&.Ԉ(ܬwm怭CH!3ӯ:ȟ@)eFVք:7E /f2cKy523IҤ>ZMΗu]y70aanZKRMEJX廊З=^ P>Ěk!=G;iԿ l)S#kWm ݱTLN˓Z{VY/F[>De# ؤ @{Lrr9A\zȫ:Ȥ5h/ymZH$ׅUD K%7]5 "rZk7qs>h@*䆆M%>Ss1,<خ\Py`3+e`X39r ؈Ѻ@Y;֘reAzpMvej]2YYQ@HkVPzLkwUSp-F\RN~(s+;IXy'I KFib~܏f7/_yb=ٳ'wۂSUyF CXmRqfw53vfö&sڀL j&bC sblH%aC]/KtTKN/]A72WK-#!O0]bs?]t2a*s7A9y\u-\єrCym&VEvw2 M3/@MR;l *h|E=8Єesj#˫L|s-х옦U pS,txr͜2 VymG d#jZ"KtmX.iz`ZE B2Z|hVa,13#90f(^`]]unZz{|IH\VL<nr|4@Yi1FS"ʕ9ᔶ7Q0Mü=#jGSe `YCYrr 2ƜYYʚ ,J>J۠_,  cԼ^+ ~ tqZ4`+nr,AMVQŮYD"*n XQS:Đ{պ1u/}RD'|EŐ(к ˍg鋍Ei J#Ėϣc=՛ )tlvwA "$S}m#嵵;DZY 'sE]zq?r<=>?۟y ^?[/bgqsw脡+Wמws aQUqnDF~RC]_bM]P) ANXHjP{ѣ79q^Wot69L^SEj;۽+́;X*p(HS' MXX}MiyWx\zݣCB![-tʪ Cm|"F༣AGLFW98{td1%j@(G~/IF96J&|QeUq~ QĸMoT zots`]XTn:L!hi`+>j_̸QlVP `L8BG2/jS8wC>e'R 7 Xhf,E!V̔f)Zσ!l&GD6EhHUt):8[A(z zV^%EIE+&8^;54Б7276l kMr;{Xkx O[}xΌ̪,9$j+"[aEN]HƔH&N1*]lڲl-ncxw&5YËZ&R*^6?kV9R 3\S8.~ZrV\D5>J49oqm${[pT>/[~Hާ5W4AЯjxZU%- \=@++,u5E`>3L Eȏmf>S7\-h'\'kֱp^q^D~^q=x:O쭗w} K//8ì&qRQd;*ncf35alfU'rJ4[-Ӌ*M IDAT(B9j6Qv,壂07~ I_$1-nEIqDG9ҦgAKSZxMTE  f[QۊQf=̘Gbm 'F1q-'b͂G2aQv)I"v#'U-?OS9faULh?ǬHy[Jz3E3 /PӓgW>p.Yk48I91nƠpn Vp֒ )f@Kx{Df09+*&qɧZ~ ;{xNXpפ55En ɞB{0\f9ڃrmb7ki8ڂU`zzʍzN`e+Ba_[XdXH>5(h#Ŵh ' SşZ&6w;RO]e u׳,VE,u]ڑx~K?Crfʎ3&:7:gXTs{3~=Ae=eFm? mt~n)f|DR#ݷ~܏7 84<OG0;py8WxxeLX6|s!ϐ V:#T(-M|; ٭ I!1׬gfDuژv =لkUĂYH D8Z|hv#]Ey\~|eHvk,󪲃.#<7Ik`#@ߌuќ05PA_P \DA˫XYd'›uOW%6C.]WI fT SAȂ$rҪe;kI4/d"]geۑ c e[ fa%kgUU@Nm$ !K1̛>;C`1gMUפ=|kލ2ZK ]T2ӚR(:agq&6MrfYVOc$|>+?u;ȸ*S$4*4}SEʗ5\k|OS*$C R)I2L.,,z dhcQ^R}@s묏&C krҞz>vH7ɓY\UVWJ WTcjTT öz̯  ̫UffMP.5(<+-`Ncʍ _wx8P1۴Q[ݣ%o׈pgW)m|xyGKKyTC̖2F0} ?˙ClSW!68 į=~/}> vؒL2 HGՃ뿈.(~S%8E%Ӛ:^MѱՈWҘْViǎhCXf!^;{Iڂh4J`SzvDdjlIp+4FLӶ>]-EӖ=&Yx~ZEMZ\"c;|MXKdC:HQ arP@;t[:KN}y(`IqW$0Ejncf^@[(Vk4GRd[&0vlڠNդZIDχs&9o-xD53e8jDN^0n)t]uk0#5Ewͧ#XݯessՖ2QG FHc _qEbhBX[7Hư R3$bR-Jhw&4: IkU85,V(:Z~ o6tK){=[jMActg&2熨t(GYн14`tȭ&[~"#^9IugؗLdd~ܰ`#L$f7)=`` f4<cʯ Q4:2"$ N,qܥMbA&U k|^cxkݛ)6,b8*h%e6Y6PtӡS@p/*A%PGF? i}DЖ.}Y`8AaTcz8o 9{es=ގ5gpy\^?-FFMۥKrW"x湜h؅y if#KME 48VQPqZ1& }MƊzqָ4MnԴI#̣iVF@u~?S-#/귌CAu5j[J}|X}|=nTe'scPQp\|(v| ll*JJf<ܻ 67+,IlsZn=d8}$Bh)6=Mԛ`HGWB,6E=@}(_`HE,Ms/ڙ`h_|FuI-q'㢉ˡ/ 1Y4h[`؄幧ADkڡXdEKS47c쏇q ;wfKòYpPW6+{c~ ?3[k_~=YMf$\ni/ T h\x#e 0!V$P6^tc*߰[WSNy zIͧ%1Q֭ʼ2.iIi!-Dn`Fx![g) kĵ0XqwF\2rʖLqƶ2[V-I_P!,{R"k@q* A/IVC 'ln K 0vY+lJtgPPƖ3mp ?,Cɴ&~)OfH6Mpf(kB@]/P; do4uKU *8#z|ǿj4rN}1F ;gmT.I]T 'ce2}nytzTir@}LgL v:&[̴f=8Lj˜XKfț ?%CҍSG"JN 12͹|erQN.eT&Iv*ꃝJʔc^fF:ԝAt2Ӷ#.G͈砵rX441Lo E}z/+:<_/.Ž܅ 8iNxt-j9ߗ*shPf(ZG(@Tk5g^v]Ha* "ZCo*.2Tt*AfS6_ evAEEp"iRHz+x|iI'KrmME,QÙUȊ41drpsͫE0c~VV,5%OBX牼-Ir5ecȅ!`'nsj{{a(;Hz}fg~G?#|>̿D'߿sӵ]9)Ny.a3"=; p`@uAc<4[9<@p.^?ċ_b||6{?>p9AS%I-4 Aks@LSg t6\IjdV]pvrxig&2Sࠬ9 !s$¯GŭLհ7B,޷95y3]ZKث<[9Prcbd KBM_M&$" {͏[!HyZk.-U OoBfWn*D0O,Юr3%Dq rJ #i.YDgȂ)ۃQ/Jii ieX>]TkH酱`L#5Z?=xJ%>H=L sWtC_U臹y ͈X""M^=Kp]uc.MZ">Vtl$pc3PRUT6 qLTQ75Up-)Ԍ_cIkunRb(ƌӟ*cj n8sdPtl( VA2^ȊgЛm5BNI: >NٰaF4E5Fyܔ{Z"len opCX 7ejW4V㢕S P/(vq~d^h m2r uG])}:↠t>A?1 l6yk 3FÐQKܑF5PP0}% gFcO 11BĔOYe|hSdR) yNzX͗~b\9#h]K@fCz-?8_lx#g>9x/"Kk'&Ϟ?=Ɲ {>yo+i9ndOc{]$*z#ހK8ϊ)IN#ID7fqz 1 i&fN~B8$6٤zLG|`BUVn/zjHeʍf5mɦ()Llb^0  12Λ}w5"i[1"h)iH,Љ=tX)=cSZz}gGyYۊ#4XM6boskX񒗼?1٭8{"ZÏ%xϝw&jP[Xvm?w_Ÿr=_gᵯ=nkwxswOx?OZb9]ًWpםw Oh?<>?#/k8<$2 ʄh%vD|(L[f uc4`'k{-KJui2 ^p+Q;);Mf6>SFV)7BϢiL5%j_. 7V +"ÓÕtt$u82 dڟׇ̜a8yy+Vy#?RmaV4j9oNC gF2Շdfk>Oǽ ^җs>¿wOzvYxoxW}^qOz"˿{_e,xڧ=wU'O|">/O?g|G{G= px ΣݽV$?xpG7p5\8vȨo+XH4&6V=͌4iB !dy(ujeeN4lLQbiT0kqVSq-XS0UYʴfHz )0Y `I0ZUO/HYƉ>ڇ#O+U \1X-ݣLh3K' `ӠQWˡcRtl IK6ZC :|_#J H%=^k$hHmsyՎI t5\_[X5k7+K4Ulpalh?PW/_El6^pwGK|ї}ڲd3XkrspE\zfTW̹s軎e0?WpupGGGxSG>1x{q[w;lgucD 9Ţ-jFEdiL cV ^sQh e@rSTdE6уkOյeRnEaBvVTD u= h 0F3 N6s2QsJ4ZṦVt^s^N{1_ĨӸ1SGٴ^'L2s &ΐJ,BJCugQ.%4jv'ֻP#^qQ'a3<jn@d\2OdF0+`sd`ZgFFQ:A0Szv:)CowFkt2'aCnN<1bG$G7~xp?|~.>//Og z˿wnZ~_/!яW73?я} 7nڕpu= j*on.<D Q7(%'6H衊hF=XP9Dب` 2IgjFUFنpng بEH7hz*"3e6 S bi2չ1sYOtE܋2Zp29FJaFBy&M,uA:6oum?Mf>^ÎFД9:!FA V;JrVM,Jky܎;뷚;07s+N1 zjI憃 䈍H< JJMEx6~nhs zicsj~fz7sإ͌I+k̡g}&zyFI1)~xp6-ox[Izi ׮^g|oxOx[ނt S[~7^㮻{^K Qp4"mmVv}T1Ov\IK}"³WE'7%*B&}Uٍ3z} o$!YDRnئ6i io*r `gbHoŎC30\0&VD s$4M2Gf*Y3sr9p.2É$[gpyшSQۜM=e䚲le\OO|F4UɜUf9$zO!貁]Ca/$Y\!W+eEQ|&pҰ(3̻oʤ:;gl9nF Rc5A78a!ub[MuB"C+Lxcim6!*w.3AK34qH92G̀`-@!p:gΘgR-sVD1n4[x>Iy;q7yRo6[O\o6U<3fEpu[ve@ݸc<W!Ο? q+G[|K/WoַhG3لVd ih tt ]xd0Q_TdkCS,}RH efK-ucD >ӑd3zxu8j#0N]P ^RZN`f6)Ar*<\I˗c0["] /͑RTa\d}Y7f,5*^&+ ҋ)x3g,(y -t[ C0d] Bଡ଼\YOG(tP9 0 dd#rFM7ZB́EePKE Sψ&^A[dN Ć2 k`DZf>O WPc"|6(cU6g;0V d\'[;vR6bT9d6c Y$Һ2OgPT9 f,zZ˲JAnCpFrnY`{tWvg7+d/۾c<c7߀a ?׀_/9_xcpt4lm4,,2r&M)a)h} 9MH<1/tr bNBy@s4^sNB0!Hx֑&V 4iN i3!*2_W]:6r >ћR%2lF\Kӄ9,:C8ʴfR֤QcMN dͅL\+5Oexfn"Lx2b]pU5->p`gEJh 31ll'2%IS˅3 e6>}У@9p9bPw f %JԪ2ƌBz٢F@oI2&Oƪ*59J:#٣Qz}f'+QѮQDިe8ReZ {qp=ݻ]+7mҮdё4b`<MAv=1e&6Mj|c ,M)^Dx:QR}3?x^, K[`>)O8گ.ދzG=o{APКTe6 ΄'C UACcC,5#[P(x㷷f8 nogqx\IAlͷDanFhFQZ0g1mZS~-CO7TrE1Y=筵N09G2R7$щ# 6ᴈu` F(ѼϪalR? ˧es\@膌- A+䔲fV< [r-ΧrBZ(XDwa?B. ZKwۅhq.Ԇ{o%?,^|) "09v@ 3΁$J-dH9|U4l ~Wt5ov4otnPT%yxա3lKse02'8e~|u􋿃&JSjv-ZA7Sc-w?'77XZ!wr~3E8B x _+y1+=2#N-U֩ *ww۱Ku(Q;wRQ5\QX#≛D&&}]FMF{Td: 93=4=3SVf ;JjNmK5鵄 ߺTBKlfp?Ξ9v|Y2<я?ě^|K_߄sapiƾ" xs"I4m҄-(P@DmUlΖSȶҌc(Q tn!LpHYW H]!8 gh0;62f/@N:MSqNծsf V~*M(Dd0Y{f˲@;&-AD+j!CH޸ЋnG`B|bƯw2 ]E0n1S!f?+ +[}N 9zHVT|Ǒ1cSc#'wE6VI c Ub-8cYκ;핁{5Y i){gZl,Z=j^4!td\:-cZlA?a3!֡#W8 1 jW߇Pu,ދQӄ֢f3̫kstB|wnF8>SY$rj2RP2dWl$ T͸!}4It.ZX~f֌[btMhuɏmY2_wj"'WbZ{R"1x׾jy+r&ò,s\*z,vdl0+w`:ere|2kI-5uceD[lǹZ{K4YdNǣ,P,&t$xIt)P?bj]lb4q8%!ʖJHF 2XڒZr̠/ӃgF6cJ{ Q˟&k|GeTː֞Sئ<YlZ eE5nkz7i65*xN7JN)aWࠑ>koim.\FMy } LdVVaRSɢ!cqmƶ1އ*<(lie IDATh&5%4ٍWG*Mjh8c#{a? mGF"D^P~ʄ"ߠ%To2qV UAE:H륦޽ky[+tj׈#L&o d̛(u+FX6Vg}~%$?Q(zߕ,NcU B`QǶ׎My>?lZ9`0I}ue? I5/PO1('Il9P:*esP{R/z!ȮrF^&;6kFqSlkWTznfK#d 4c CZ9ٓW*Lth{ÖLԿK 2዆WYKI!ɄI$v'3jqc<ȏ6 W_ݎ+7c=nٌ @!=f"&hGj5"}eѐIC"=IƦ 8/7Sbْ >VyKTa8r U3h+7mk홎R (O?ɋ6(4/z41D%bQXǸvcIF nHҪxLMySh37E5' M!9 #19>6!L%)S5" 3&svŎ9D!9JŤYM*@}6(j%n2qP 5h9)qM]U{*s,fTgu+T@(Ò е<گA ^ a!"hYc)b tV!HB&FL4nu+&f=UVcej2a묎W L$0%u9SD4tbb/m4[Z4WPb-#-/~ڲ!y׼ D'3gඔ`"j(]S%PFɦh2{TT287d͔SD{lr֣&2wr(֗!ք4sw|@~?)Ƴ^Y|V!J%gs5tډPuw]YzdHC Q@Fc¹ssfbP^ḣX66V#⎆lB=7Np]4 S!82"9)X!ANa>2Fk#z94Agl1o`:K^#A] 4MSdT[tE353@Ĭ֑:m e DWYp-̀hGSV{vsZ9щ8ƤUedF46Չ!C LSUEa&k40Sfn0PN1(4nVu-лb蛕Yл60hrv]ݳ9GeJL6[ePwqh@sj*E\?kH+KQ]0 i %cJ2$`\P0kA_/R:{ƞ2c<E=3]7|?Ļs~~ W_{]8w,J!͐r)bܾ1w)xԣ#r2aEٯxԽc?y&j/϶Nό6iH)e{ (I}?x0f78<<@3#x;x9O·~۷\K!38Ƞ1fFnՠ,ȅU{0n0cAjx1 VS=qg`n܀hmKN@ T\ "6*١- yQ ZZȏ\8% ةI $ {*lI,][x:UNe V)ӥ[8B6.U1 g<$ӊ/UMjd!ؤDL\B=N&7DSc(ե9+hr wp$5HWs}[3֊O{UdEِI/(dzTH(Mnb#A8A!f8vQH/Ks]5l Ҡۑ;U\[= &ь6l{E&.eMR˦XYg\:B,N#>+ztkLl⤚IgFk;DԼQVYXf({VS(4(A-i{N{d03@CipWڗe[Z"Bs:zN4;;bcB}]u][#DU5hwG B*=(͓Z6 S:Š#.ΜRjq ?獌Y =9G|^l=!4STc<gp߽`kbt `{ Hkv,5*n&&#ٜ]o†Z6wІNQ-Mr2lnfDl)@LHef# =Uq7ZYMː&9-F̽4] 5b͋P \g"0UF]ptQD Ȯ[0Kqop$3x0g@Q LUF4 Xj%Em~6Ě["s4MHʲuW69Hz%¬1)&* %q$L8baM]hQP1Hp(Sћ0֘!BS]GTla-:QQ7fl8G4a|eV\ 5AÅ68ZWEhndy֠;%dKcj/5y@`ByJ0(T 4ۜ-+&J2bLp83SqKJp *LT)2Xeɫ8ۧsǓ!LX!7:eYpL(ĂFq@ ^ i˒cxZ2!/:! Ƚ7>s;uoцӄ_ʶ#s(cא潁#I4QF]ӣ |3cdmB&X!Vn*VFFAҙc/nƍTbh#{<~\O=wyӁUä -M(qNZi6uSϏlQIE\ ipk$GnͨPD/y+h[|^EJgdڢNLc›Ýv, u.O4aa3>4+)?ң0\Bc͞Zu ̄dg<; ]BYN{4gV x+ˌ<#cПi$&?xP;X`{Mo} 7|ӷ?o~]_/²4;{82ݤ6xE5miAsڑ`iK*u ?ctRS[p zM&|KتII'T` -t2Xf:i&U-ڲ?IMVtrj>:7.uM5_O{O]ghEQLX:XJî VEh"! &K +8(jYdQ1JʡZF/ce&nh<!ӎ^0PxrUVs.#3-|E 5 D$8猿d\IsoZis͹/JZ;SK5ء7c<؛j{ttkWp=bzǣp #$LcQd^,)O)T^)ǬM UŠ%m ?,i0BEбLVB dW%J"'J5OB?02TQZY^P i9Hk֛]\3r$=b UZBG]i(ѢhDLv9?տ :T;q#gڎzcouB-CY˫Z K- O$%=CtޙyŁwkBgskҰ(DwS|}}%|i\CS!IHǽ_Zt*0;IJhR|GE5`Wk|TZqmBcy؃3ATJ0hxQPG//˪c&D* r?ǃnnqtt=i񎷽 k IDATy_Ю8{@RTMM\ n!yӦFZ4wvTK.nQv .\‚ڣ.'edaXhMf!C2ͨX&rʄcrO1v}iI5QiMyqˋIJ|8Pd1AfXaDMm8~ZRGu\agFwO̚9$JuӚ#jhY\尖ze&٬黮JJްO:(d0(fW D%njF+Ig4WO4ōJO>ՂA0S4('& Qcm\(N"ADq:#*IF d-f N/H06M IEdpjDH&):!y9Cuvn,4"v}Zwe ]y5ٵtqV GrQ$]7sI9=Rci8SDM{ "";]uH)FYd75nD;̖|]5ӤcoP{dp>Im4(nG>1qw}ت?8cZ .wCn2ȻHdVkyZLvX )8~!JyD !2M(((#礴|DV҂ZBYGPK[YAEl#S),QYjDgmx-H'azJ4=թ I@c0BIADiQ:E-(nn:a9519[+Z1\ `C&}9#'+ 7tTDմCB1:"[rFb :+r]5>t*A/& !a xYJL"p5-{4N{_j R)=Ҙr&J^7Q a.#k-ය!ܗwk x(ft#)ۧxZC;܈v# rtgݟ1XaG1F"FED4ʴg 蕞.6@51cTY6vDyOEPg;E `'i#JMޤۣ!&cQE |<=5Ðo<sT@M( TLjXDc{|f [2^A{? O[8c->8m}: w<9H$iYZSF-g,v;}1$ghbc9HÝGqIY&BAL!~im MsrkwLJyPns/՚z*ѣr8ޏʓ%uZt&UT5_΢eIeEMTG{o'С蜥u-uXk-"pr57!Tأsj^YxbޔVGU[hS!h&r4ʵ^z=[>$ W???¯ߏo~3ͯ ٴ\N0wwQoMEf[ѕ\V '9:@RCSբ: t -jWV" nMP2nRL<U+. rd WT0wyF{7>Ib:#}ZQ zo  B;!M-|g A"srTףw@8gFsL:<`FP.T!:J˕MҐɴck')A(佒jJ4lP%m-rAA_to6nHj;F(kf\z5VN-`9 $/*@GLR' J]&8^s7]V$氊#aIYqV~/ZƖ LL'H. a&jW.MHOs3^:DkRCC'!cB:"z, LA R-3CV}aa!J3)<~ҌL9r6R1Zלu3~>y!Xwg88o&3xsxCܝoq A ]8NtӲ4eHi8hVmQ{oI:sȶvO;@;irJF+OF!HE3@k820F @V@.򎁆Ԅը^5=(hiz2 ̶<iS@OKpMDj͇Huj0 mfL\2SQ F׉7RGZ7 L|oGuk YWX*+ɢQӋ@u\ebCH/"B ΢; {Hpg9.Md۷tٲ=;4z<c|n#:6eDeq3N~O:*lN;3轛z;BZ>ƌzs2s)94(,^,ζBj5rHph.oHevSÔ2Y+2)j-L04NxOz VAB4 )haNNq/u"oɐTn=^[171d%5u>J2/SpKB3O u6/9*Ѽ_H]u3}ʓ`)<~o;g?¿'!0<|p`nĊ" @b-VۚmAQ@Ф.G3HQFL4OI6}%):de_zK`P8!.=Zrpx*phm?@ݴ.|Jt6x"((WM=(?dÂEy]GQHz֎8j]N ^rq?3P6iʁpnM,#45n֓&9p֪JkB$dpQ\Ƒ10,:!bZ NpW&>lvA9.z`YD̋Hr+z;4lU'J^_ʼnڻ~9D:0C7y+ϳMH=Mn(yAEya{(m4[`gMI9E\24h#nXU e| PDj5v"^fksOY!̘ufz]#t@̓x#8w}~W|?z:@[`7wa:ִB2Y8+'~V*3)jVbt&{ZTlwl 7|O:(#zJj')3EX01P#<. gPuP05uzNWvqsj Eм8ܓݤ "}uֽu>5Vȅ=G[QN/tq]t2/!~SHBfmjva9(/QQфƎmwb*A,L\!(TEnŸHTV!z?h1Rz6G0VgQjm"GL4զ\6a;%r̀h ]5'E@Ԑj+ 絚`Ea ɺpETA q5ͪj@5F>uP̱S:iLoDTk! fI*&125ؾV=#=kJpdjAiJhHkxH9(t H4Zgk!^͆)H;Qp}Q^5z]gz-n6`[fx㋟^:<~O@V@9iDgu1nOژ ]QƆ+n.Fj4mBB&rS*U&}V4Dn I#X(IK\L͞tS5J҅k5Qq] $kڎ] $*O)sڮN4Q:V7U wujOw =ޓM6ŸtLZfSB  K-X{d@l( ?'S倅PWw:=l hjpu( I'D-ͤ._;8/2 `GOd5]6Qբl JhV1ni9=lu/Z/ZRYHő2i\Zt/&L#2Вz丯+2MբLǩ7jʍlKP1Ǧd!R4NALȡb *zinAI[d3 sJ#|2О l/%9U~VJNMA$h;a9QZS='0 ѳUs*yΐ XS(g iʪhRbeRsOz,JTp(&ﮐPG!-9CU,ƨhG+=g)9Otù-6|\XEF諝aiR w(]6R8)Dz*d5MjYK9~ +G+zl:a&B{2m̺^0b=a>擝È_eh{7cjރvFk+BYsnj.Άfc` Qm>F4IR%Mcvr@;V;r@rM+F~oԺXh0KёߘVi[ܨEP1Pi’*9U οM#K{fBNh :KD^u{w ;( _y?߂'O^{EK}3=8`#0a]."9uf Gu T8ځ2̬5=B{:\KlΫ,nTΩZO4$f"9l֫B˃=|EXHohGld'mjTR6j&s!7W&ՊbV&1(>ӈ艴Q.r ̹bLqATGq̖q#$\#[2|ʹz>}m^+}Ps?? 9=?_%gs$q%B`N:}ȉ{F J\5@" }( (D$[uq0uZ,grY ütwA?Q /z]6*40{֛8]ܣuO-#.VS5ڶϋH)4k0_*MS/D=rR'Amr\SevђюYls&Ѕ w*Թr@+ U+*4Zq eC 5"Caj-ʧ3[4 Kx٠U@z03os!B)k*e. Ȃ6@?NҎ,Oly[j^E=Uq~1JP~~j ؑQ4Bzz8#YpZFTîX`Vh4GI\E5'X}߷H4Z+t7u!v˖zIqR8R&Ζ!]Ӣ 1HjQBE'=6D/I>'믭\s=r*N YC7mC:8q6.2O`qu^z=ۗnNnx@/<~xᵯ/˟M&f Ȣ0GcDClg&`z~U|K6m\{_YR"yh|FiBrGeNZKgfgE'fs[ y!U e AZ a+~/];椘O[\$cLk.*cBa5 9@iL5H-PFlp""clȒƗMTB fAt7abrC)\_TNCxcn1An.[A54N)+KN[h55L&C ̦)15ɍ#t=zXچ:q8oS Ռ8 zO Sb5^ m!RF%Y0g(٦GQ: C3P6\0rBɓ5el5^&Zsi,kU J# v8LsEgFM2h< ]e(U6~AMu6C姼_K+/\X^M=5XVtnv2I#t&F"ȾrMCP5CC Ե2s0^h٬P}i\c_ՙ5f#{kxs^mpwwӍ=y|f/269dOak9MՑdmώN`^|o0BZ4f*D ۢ4ǵ9g?T :Yuߢ0>?QEFAߓ-8=6v;GtfevR7Hi`MP*$2fA:Z"l"M3*0(j2l"CvQU۬l#>_~#Ud?sfd["EqHӳ623*kІACDNqT7yJS[4{Ij-V\G0~3mNۙqw5Hza DJ${ n]"Sjދ FVL>Y h .UKCP*DoGbֲ0bkyVDЌhS¹{,p5~>RG辋R7#%$ɭeb|!zlCRλǭILd$) d7=dA`2r ;+ a@Ivhu.hb7J2 \)TT2oK곇r#H5@.*W>J>#3JߛYH+}u^z=Ƞ߭:X=z;}_{__v<=#<:2=aGiWV(qA*w 4Ơp?+Gwn۽P\% 1zA@{ji&щiWtxgq^h!Rz) bR6E*0psscЩq"]^|w&stó pɪY@u2\۲ d,R}ҁFFdK|!' 9_)eߋ6sP-1PƟM|UE |7u̍ Ķs!\GaG4DpPHrH mE8!_徹 |>J)Αc"{MLKS~J.5tƏHZѩ{p5'Rq(3}4e([F,Li j0c,͔.ZMǏg5FSͮFV9ƀ.Sv*2k-`C E{F sEj:ys*fBblACmelRsDK*]g{cD7B #z%R }ۺ>*QŠ v潲l`<׉!Y`K30j}#T4u3|;G0n?O}3Jc9)>??nzڞ%nm54,&\&بljnVLgr,D<&g[у(*AgQ{rr2ZJ#d'i~3j{{C.6ݪ:S2 5<L|Vê̯+1ȶ1 NN"^10,:]dۘ(GIvhDkFVI+3 (~궾9lFV9f6JHgLR; T;MB1TG| Y^z=Ƞn+DY8x 'y/n|;\nkvn.rV11%G#1bp~ {)F-B謫P1D(vM޳Hs,;{az)UqMv;} A,%S.c`MKfN(ܜr_ky -FXb*`ZWI:/2*I}*uA5 ^DQvr;"=YUTzyBպOp Tu 3bv4f4)JNf36 ii"^# 8S8321 wi;Yz>7uVG#Ȥ7AYvt2p'd>]FIKL^Vdbfߕ}E7g^T|)gsy?%SKw/ok}߅Cc&X 2MJVÙ@)W fuPaKh@ȥt pU`f49X2ƽ0{/^l [uSsySDę)$vbӈ99\ 4n(2aShdlPxflMk$ ^uf=pw>t7ox+/T<X.8L09oyM17Oʹg"P,OF=At;Vۚ́sd93tYH@p>'E쩫EQhfeZ'#T]eWnlY)` !\ H 4-7xRvO44vct8#_eK4$)[ZNhEj x"C2aP@Fdy]<FSf2])vEsxQgs rӠٺw4Im֚TKkt&%iaPЙź̚|GT34Τtv7^(s.i=FêXMAH6Yٝͤ>^]h9p臭2)3e^ Se$d6Bbs=:AE#̸nM3h{bḽf7.SعI 8<3]ˆZhR %]κRCFZw:R_MnU 0()G@J`J<;TGqhcJ¼EFۘC4˵`c](e2=:w ۟8BKkP5l')Wr{5 O2Fǫ6P.6 3ûȻیP;/eE(vb=r# M&'d ;6Rk4Bj 07"JH.^vw拑%jdL kQ18:RʬB*M-ڑz `#a˵`k5@z!l:XnyRQS^cXZYru i( i2CaEMEf#jC3h] 9mJ'uܬUuƠgHW[Op,2hg*ԭX{hF)^ ,]/2BCGT}fM\E4Hd4 {kV橣=(t,sPD=5HV뿁>pi; JSfhE]ꂚQ* ۼ \ ^! ׳/@!b\('99E1U0㦄7]R*USC*5EP Pq *?4:d=ZMs|M5fFX~9.e PR7Z":ʺAL:CnhYq.$DS'Dak"E4@EFꤔl˛*IxgCf+izFolZgւ#t5͈u˦5y\3c'ͷ׫K*y -Dd&b ZHcyu5ԂNL D' _hEaKk"(O睨@%CW!D;C&j֠K!h7:Yjqd R~Vc,?yPcK/_.@X  -s=05@ :]F#R L&%D[=3ei!c,JK=txP. L6\PnrAlLp4\`j9Ҥ+݌)~mc5u=$/5t[HyEØc^FSfX33 '^‹Mxxs?3m*Xt\6f#jLS4ljD Ī2 eF+dt-KhZ2=c3poƖF"\ٱ܈uI2EڀD[۵lmUe]%&Dž}4<[ˉqyfّWFhT8+B.-".t6ݓ&(B05>CF-C*9jBrTً4@:tj$Js[m@V^^&He鵤 O@7*%nҸ ,oatbeR#M5*PTt pifE9ZDkh kq\Ծ"5k sP2+YJk@aBOFL|ÞskfL7Y* MX6zPGji=Y!x.YW4:{US=4 ڏFk@hݐ}Fwtq0 %C,5p҄i'Fyf; |j̚Ό~eF `34v~]6sS?zju9 BgI1=̆wZƵQh ! cr\iz]gZpب8ߍ7 ~˯)#a~@*rJ.zJ\&d' 8,c%ylj)&q6rs4_F htYH!PB-DM+ g"+*tkԏB xkoB3Ƞt' JKlE~;D@^R LD7bZ21@ȁP3STLɰr9%aI6"ϑQL/F2RHhRLtcP3Ŀ+t4lfMS͂4shN5.t0Bi똇Zf"'dTjƎC F: McAj.x~hW68]|>PZǎ2h-\#^ 3UZٌ,[J َיF))}.DDMt,E"aەJF|%1cmvCiӫ2"!m =2Μբge>3ΝQJȽ(iz,ZOAj%D:55Ȧ ) BS71YȈv͈˘( Ys9'ʩLV3?frߵk3xzve0;$xx=y?/wci{on׼ʍ8}#tͲb!VOm@1҉,-Y"#J#~"]MO4dGyWWfRZj[RX_H Jy[I' =thht/S :GeÂDQL0.aCґsQ ָZFPPziVfZQڽo8 Qʴ:U$Ӊ&QRPC67MWgS.  =B,=hLyꞲτܺ)\F>#!Ϧl}AB0A߯eRvF,{?US#rϥ܊K]sB['F}FGE4VۙrvdRWn'QԨ!A{2r23)zV|!]tٟ9Ίh0qr ckEJ(iebfE]fskms'䍔pmPua507ۼuN(n^/s"uߘ, !)Lr:;:t~c?֡BcqoGzGOz]ufP[g<}z?Y)lC5G;xE\`:rϙZΫRˊ!QMEZek3xzvA;d;N;=O^,~~^<6>c? &fO:GMi¢PR U:pL -IxPQaE&4ݷF6fs7X<FhIY MTHՈ# G䋆JU+#TS<;Òyb4\JZJ.|EAR3h$OFؙ/*87p~Le.L+Ls/QcTְ& ݑ ^{5M7Zh*:#S~J=j(i2G7*XKPAST2VV@Lnf*E0vkXCk ϱ%M<]BAOLʧpjb/lӹrM\y-f#l*'=\AQ|sT]b< 9\FFT)M&xY>XvɇH@shaAP$GY Ò!T셼WeGJb-Q16e@>+j\@%ܧ%}ᔓNzNYJex{& GP32 Ij[臭#_hƍ#A٨:8_u3{g6tڮ׏>O6ܜN_etm% Ťq+Ҫ8"w>9n7 S JvS"HUwj~E4>!&4JەGw\6]trTedj:KRi:k *$#^P@+ࠄ:˸ژc/3MB9z0CŲװ jKm[J?J?ypІ{98O;-x:|7d@F#DZnSDD. ,f7X1.(OXSfQBi TBOk1hYi;{# bbaDlcɔ^!Z4jD5a:~BFH@S7B!ShdVFyG$:oSIZ^^0ȕeF& HGnJ&E2oefeSDf8gcA%آ2#In9e0t20c*h[eJ {J6Z"7XxPLQ"4]C鄚JD^Bbp >uoxg|w|Eo/} >|ۧŃCs;B uު-@mW=BgYN΁Qc{Tj@AC V'C@(@\p<#tv]8 vyY]TPF'(팹BjMEmIm{ҺvB.LK|n0@f70MK`TIC"eGei7)̻L3kA%T Fx\Ԁm!\FIhEh䬴+;c+)=\*B>;į9z]g:)ˏ苾o}oǧ?>~|)|wӅe;pP4hP kxmthMM#뎢)TCv>};vN.ZdZ͞O<Z Zx(yA'ҵ.:ҡix-yĕp[kP Ř[MS'$[zT&r{5Sx$>pFI`֙>ڌ!T־7;Җg(gqʓ+Z+)]B>%6//XktX zm!\@8l9o}ߎ5{ 6fI%B%ȋ㪶C`:b2rwϠF0Y;0e4Yez LLs WI]uCJ=P0rj vןQ/vs| N6nњ5PQlTP/5륜1==25~uO6|Aa'2yiͯtPhR8;;9i #Xٓfe3 jog!PCVmkUwk "0G#䳠5[:30A!V(,inj1( mokPux8!{׹jKIJ=7sŃ33lOsbp1OA2hrƕ&zzۻ;q:)n__G~qKf 8N gwL\zna ®[[r&wݒ!$W1p0(ŭGTn5oGC\8T]giz4v:gΓxtA4?38ckQQΪgS+ pͅލ8Eٱu솨t,|tzeiQHe;r`|g#Wjj- MK4jm !]#WIHa Lp~)&H>uSSW!?SKg#)ӈ뵍0 㗤/2(/VӔv,c\P@9myz78J .ұGer& d#(qD̓K~JzR 7dEQDJ+Xa9F²)_Gt#ȟ0 ݄6O@S[9CabȈ^keN'MF(Fc }^?ǿ ?nA>ڮh1 EhXV!<ígArrU慭yCFMe4ΞMIMf8vU(.jb~c872ŸS{c.TB_uYZ24*ؓ \4Le֕`v Sr͂?PCƉE}IQ`FS$+ J:*FE)IHҔW xV1Pt' oO Y,.Xa](Ŵ i5QrBK3|AYD,j$^VF}jmPj L؊e>ڰDil7?v^Dre$1_zk3̭cKn0FYDxKhթeUX@*=3&cUUg D(/IQec6Jhz>T)bOKf~7I5$[{rj!1l`|4Xo26.>O29ȑ:菲)ËWkמ#!b]F/ynFr45Ni1Bmk߮*&K:eJ6^#kAG  i'IڎfqD4[pPG"-_\iz]g}Z[|SWk}~‡ʏ/u<~7 MM i!;ԂJ4 (4q8RK@,7^PtJ_p37aY;avC)^(ˤR֚E>M03%`% ' +[%zMB& +]I5tb]}*끑%^_k&ޜJCb;j;M2cL9r{ZГnOl4)QRj/h ) c;=awP1i~V?g4bX,IS`D09h(#*#TټwB`sm^p3x«^o¯-_ex=Ňpso|qslρf i2ѢFvGcW74-Hb|LN`~@̨Myq@W[Qx ꧯBpay^J""٬ePZȒkIW6o-C٪#{ w߼'BJ|/CU, g̰D}+M@/yң`!hPVvE_sƒ$)8&5Y.e屽$#p`$!uz={'B^.Bύ(Sh=VkZ}YIl*{mu]1#PilUŅgMek5B9xVvyzh_r5ۺԹ~86Ri9Mx|c`FJ=-VMq*La Q8DyX\Cr>ν7Ε4Adtl ?}=D˩ N p*ˌLJv^ϊLDrä ~9EmLH s,Xt@DZf8/rpQ[VnFK7W{FTThrviMY1C衹z&=:v2N&ށd{J1|zLl#^{O\RG4M7wUpDV )3|kyzf]-+O~-[O~ߌYi]8Lifmd,}Q dnzi*(N#Q.D588F ui!ĠDEUf!b2=^뙽nol yki|?~#/AL94wSI@ ?Zk$G]NHǍ+m.ls01n`q ]wHkb0 2G=!K0FN5#B7ypCAvH56c [鵩1)g-]z}GӲS2:νhH2åh%qhhDJEsYHhƳF4jw31m\vjN,ׯY06 s/ȹ NzOYWût8ifz]^v{3;ܞoq>/˾_o|߀گ xX[_=/=dU 轴&aU&؇$,F5H@6D*+h G̼:ԗuoӦ )6cjX8,r4yYT^vmSJu?JNITKs2@f(%6q)( )^sn#nN[ݹHZ@P 4H6} )R]#^EaOTȄF&^G6Q솅|6n J*Bk1_"TMK ^338怵!ffsY֚A.Uû%l@:KݜF>\kqqՊ*B,'n6DD5t뤛X!L`ۆ/-n]ZIڧfaET.RCMv`NZ% ,̅4EkgCn $ ӭ#dSaeDI֡iYZkP=c0I2HHLޫ@R=t TdB1-k^뙽nw{//x.g/_7m ;Ғ`ऊ9_q~& '̡5t*eH94ۍ/ڍ4}vM4*Bܱ|yrhH]}P( ê 4B ~.Q:n@mKNkI GF#: I\km༂vl,D4,52 yPIɡ:(F(B5F&RcZO;[{f-X[p##iMi*A٬) >i57Uࣚ?+5M۳f(5F{f)` }I-F8mlB."hՔҢ# r-m*Ttۂ-2 B,\FLo&3iX(Φ3c%Hbf6El D|Y:4u@ya1i$s@1.ԤcWRgZ1,Qv]yEcbvE6P@sW4L0s $I.w(Qs-I[u\.m3bb]qz̸ლ{d҇\v2_iz]g]9pOgo}[7 {8?}|bJ{|5];peVom%ZT\"7GIL128`M@ҟA Q#@5I'*TF4)cJ^YPV8Ibeu-r κ+ƷS*5H(A#!]nbjĕzihʌB\9&_!Z,l8}[#"?tfxՋ{ڸxk@h!؂GD:r Wű!OI \w&g#Gr/;vEՄ pj,+[˗<J+Ci Òv*DjN⹤> 54Z!~AHiXpx YbP(%Py&:5 h&&q"5gcfٲA_!ʤp<ƒǓn~fS<( hĥ}R7j>QlDG&@l_`H0 ~DOr>`j@^-Yf؋RjmxkFt bֶϳ#y'"#_)%@UJuN)Uͪ4n@2{d@yL_+RM%m01(6e\=#str䀆s>$s qds%Gh%2`ČmRmFhe`G@Wk"mHB Z-J)]&)"[&rzr'`f úi&y>뿅g?7ƭ Tm/C袌$j"Wv^16ꏔ_ l }43͋lR6dEL$`Di-T3,bF!\$vD a:o>1mXb2Nի(uSVy~Bt4w4nԩRd%uP,$nBjN[ɎU3Tg bgԠ_KqtO H[SD^Np.ѱ5ē5iņ5,ڴ` YXjz%xslT$6P>gܽ{LzqXd:)~EɦP)o>X)"(b53КƮ5G{4A/EaW龎K6C985ADM5HV`Jp6f(;Jhӭ 5jY7ɜ-OKG+m;XJƂ M3Z=487 4WM UZ#yݯ:ү-u밺O!늠toaYTS %ؚJ1"t!7qů6wi&!r3PCj̀RŁ:¶7TUE|f#b .KTu{iQ?qA94o1]*a@  2xr'c X.GS[ޏ}7og~XɏR"h漘~ ]ltVYJ*J4hPma8btXj6)TCf ZM8JC\]v33mzLvʖy[G5Pꢢ$njE"#g`_i0'F;5O#UF3 (qOY/%pmX4#֘$hk! MYTui{.z['R' 4bue +U#uu+Yf]+HL9hb(1gBг%隞h|=)SS)GȮԁ6u܋HHsb_b2tXGq~TLBikJH'Y$P*C+a~T*'qrA39|G%L& %RG INl/g[i=_dð+ D ~qn?SFb0,\JžVE[%P9z"1R u02Wl"$*+K[ BQ99ۇ֚aP6\jA|(T-^Z߈fhQj1 Hbl&JR@I `Gg8::a7_f$v ^7|7gh<~7[.@lz'vμOoܹmAL&#| e5ob *󩎢ESj:k3C8FYaAq %0t^8q)0t:jtómb@4rRySFcy~nhkӲ6q2@C W pjRj U 6Ҳ`. MƓR@8cJ9Jɫbs"AfmMcFbT:2IPVMJ`[h|)|PS898l&mS}@ XK#zKRjgt`^-~]X1Qn37=h\6ꂋJ89 E~/S=0hK[sp1+prEشCPjx=G5aGbUX)-01 85=խ!eu>M.M x[B K4!phaButVm&if!Ů4=iXQVZ5R(`,X0jlg'1 C9%5Ki{{p,6HqeLKdHhϝ xК` X0B^"BY{`y{9`%\Rϛ`GVJ2Y]7+@|4z+[Ch$Cp{3xCWY@Mmqd%֘aSI3xr'`>c\bXx6qܺy wۥ1aocPJ8U1 Yf(K1vh."R|GY,>t 3s -I2HAφi v>4 ${͍M${69wQU^J 68#3U5;\_;SɾGoZ@!j pw!ZGZe`E`IpnH+;PlLR]GЧs!ܙρ7fKE9F4k\VƆ nI"b. FG Z8 ٓ|0tHm Sj/ِP_zjFdpmdC/jz'4ѓCs,Knw;wp5\r׮]7Ã=Mg8Ͱ/0_,\.NBSѨ  :n`50:p1=>ė&{y̎qyAo'_O/ȏ0H'LC̗Ktg_/˗o_+Wџ IDATx7]BNAbEFGQM>WjlKA Y)jڤ6i3L6[}m((а1CȄ֒;ZG[zEHjKO!.*W J El΄)NDԽ˧ncb78Z14mxȊ&Dp9Mu2K 0yDB7 4]C( :^LIPԡ2sݨ'4)C:U5s6c@ A U\@6ٴdȣA2xHStʕDc + =Ȟ%[|FDՈ5"3 $>}+B5|mޤ7~)тDܚiv{BMu09jJ[͆J4 pQjW4[iM]rd_D zXt% BQC2hQMC/}C~Ɇ J@sQ>uҥDC +~y/ ޶!YRMVlDe6+16fV EuL# Η!j 5IFC:֨txDJh{#Π@%eX"{̭oY.[Lm ">]Uh@"+f-眃.Tǵ0gh\ImlY6P&_BY%Mq:Ѩh{XX6]+4G[uH%dhdzNf< f5k] 3W]%xP0'l 0p &wn30Ό3< 6-VasMh+s]7K8=,TR#Kqt| P>о~&K^֯)t{CoFiba幥% 9p+M A~1X'qr|8ppxÃC]ܸq^~%ܸq {MX  #FF &X[[66711uZ`B9s s;l\%Ff.x\i7Px4`47W/b}7nc_bco?W_7ݧqxei=4]ׯ~—>=K_ݸ ]:*ߩlȇBrؐ2]RI9TNLNƵO=/?ps=P ɥjHiR1kF! 8F)3QZ\x f(IX":W@uE:gu䠡HuS訯L/hsDKf[H ˀ2@nrEVnBexV! 559[sȢ$(^ "U&fZ3UoE!9Zbx6F̕G;ztB3a)+$"rh0{tJF SŃ Ua) b! u§,i2ۧ9tUTd\֨\ w,^s~Vr3|&8,  a+W8u]*ۄlωj9nANhE1Y̩-Q[7|!w#&6k:rB0";vOmf.hfQhۨj!o\ '|nkwl ΌG|-V.h-y!'"8=ynELC7s@#JD`W&6iK;3F vBVFef[3x{n]KOh'c1c`ׯ+ʗ_ŵko`wwwe}'':)oncc}k /gFȜ~:wI PՕR)|YA@]P6^}ᾳ#'c?Wēoë/}?/ _ȣxۓ[-8];';woB8cڗ0̪ap=s؄ ũoS(אD)"UkIoutO?5;ä#虂GtbV ZM#KjF玞`1ib|;͓q=D8GuSUo*-/4'p2.ܙ1n WD JiQv`vțB(j׌6\p7x4t?F< "I)K^b &)? ay>JuӺCTh-&`Au U̜)!,.'~)[@ϣjv3V.؜Li4Z@]HW)j@V-CFQMPϰ#5^KE9P;9};ۧx`s#%}?C8QnۛlLV ո1mtbƵEF1??~\~as};8͛xhprXf|{ ӣO4[7O=p]*P ųDK^z2Gl?aͥXNc( !< dhPTTR2M({QC[lEt;MԟR8>^ɮK_ꩥWw67?mR5c0  qEy3S}n'h'ZGP+šy%#dl4fqX%r ;DэTǀtƑ5FRpc|WrE1CCOmyJf_S=:E~̵.&u0 )ib3;dFS F,͏F Q`&H(sEprxjTOnXEA+(4(; ങ4;QaCLjaT@18H^z:M7%Vr=~$j2(rMc PA$e2xH;>7Js0 X&B/KuB݆MͲT/ a|ݜa\6y|qAF!1$lv?~O$z<$ :ue 8*=5mCd7\CON^Pmx|]w=395eTymC& 14OP�cNSf'rF7Usلd?r|ʫ7@V{hN|l6ǝwpzu\r׮_k/ƍ{{8 " 'ƙӧqG 10>ϱ\,gahc-X_$4yjAFmnĆQER[1d$t}} ~mx쑇qM\"TSPT㷢M-Ȉ185NY%a ɔJZ>loK!iAt E͉ t+7144h3C ;ۜ1Sw!맦@}ĭp D'ժ6XsׄB2RK=3 9{ nՁU5I&pa\KwJ(AR(T}Ntœk}.sHМjޏ+@nPDLaIJEpց 4TS٪MKA0> eL `n`ILZݠh]_% ]]ưcJɻS䱫1BNl2ڸHjAn Gܗyz$^7E0uu,%[sfهf=k2뜬a}S 3Q’3xj-xj1$¥q"ѕ!+2 ٫ϓo,ndKz{ ;gōzzT$έVۊQF,lql\?f\7D]4rAkS`[ (~ ÝSS@fSk58lX'X.MqܺuׯK.ҥqK{9kulnnb{-lom`2Y{,Kpkʩ6Җ֜T qEEsGZk|8a0)@0HqrLNo0\/~>Oᡇ`ss Ky<k۟Ω &[M藽E&FM+{|$ PD/ILK?J  NEs39}~99F[Y eצbEːb`SL@O-L.^5g":b8 tphVtliDJFXW6Z;!#@1 +[,6iW-j(s ~ T.ZA"&N45h0Ȝk7yӜ7ceXƁ!CEs?<樥7ឤX-%XX{ʔMh:$H!ayR -\%"pJtGC e ZFM;ƨk{U)JG-Bo-&K(]\Ă&Bre7<EޱaY0*CÌh@{D@f uJVsd#[JpGGH3c\f%16ʨ'#e< gtS'O_DhuIڞDfEmqF/ťUZW _8`X MGkm|B3?À0#a 7=q A[t6o5jmk2x tpYkMęq4f7Q/~ 'ˣ{L?8ݻw/K_"^^| M!BFOֱ33gp}8}zz%ˌ~l>9BɁ I,]Z4ߊ'畓7a#kt($C \.,X[D01ttٯ ]3W>_ykkG88i|3oG^bq4ٳ`=L#lZC ]*.[v1葪 LbyMf:ie]5P.nzGȍB*4hE)xh\#Өp&*k~[jtaPc!V*L&ʢ4uUd{[@X%ͻsHNAWrgOQ hJ\`BPF iMGJ@3DUF6j^iBѽ&75p;ބF'C4T؛%cfHHe}fbbd@[)*XђȚKB@y !bYECfQPz5|3ꉮ08 5 5iD2jU3q`5*DS=#M؅&Ji"=+˥Sq86r7cfGK#4~-a&=:`ܕƜ=GJήJEJdXI9DT\.cAɸ'N0c&Y,H#E1qWhxV+5EpU-؛S.ـ\8?̘*LȘAAcP)ZF _wv¯|zpz Sj%zREE]iu!Yt !+QUAcq5>ܴҘ=;"ܑ}Mړf89oE?qM\zExKxWp+eag,=Sxۓo.ckkAy_\?-TT:,4,Pq(I/;'jVʢYN#͂U X,X_/`6%5ܼy(DRf9%=Ν9ݻ'ct 8{,8/!t_?]_5/2>]G7@<JlQ BBjAF:f,[$6rCI@\ UE"ˤ?{P@9.1-CEZ\0-HP! T N}K+[нәjL2dYCS 馘E`B؅YbnS,HVsͶG8G;!/8ňTPEgfk/UĂ5 ﺚz9<7+U9H TW- τo}!(b Q M6w@6wËtutQ4 XU>OT @cͥ԰tsYJc>Jbw$EJj$Ռϓju(-pjZ#Y߇ۚ%}w*SU 'XTܲ:cρcdl/A&u)MX tsV4c4. ᢴ y0˂v:ֲ䛠w]<Ԗڿ.\;{ 318 s!|:8HnT]ꛆN^"HgRMӰ6A~sWpf@![)cDѸ6d>8P'`yL_L6]i!렙E@MgŸ&ANٓuUֽ<9NjD\._.˗/_^"nܺC ={xy:{X!`3fsݵޝ8r$.6v 67|jd,Slj")b[s!"db9éSx'1/H#<=a0Theݻ8mF ?QD\{B`JúP7*f]%)BЃC3 ^ 0zϾ;n,8湲O:֜!3 g-B_@єwJULD= L6dNua^ L2'tm_vl+!q9_ؾ9 8,ﺯ!mC+;;4еXĥ2YLGKXaNQD@H'k|1FiIT+<9N`f3ܼy׮]ë=9\|wvoxhӧws8sΜ9-qCj4Y^A;j(K7\qE-`uR~Yz Y9>ck{ OcN` 3ݝ̴Qc4@GSolaskۧOcc61=:ؽ{?_Y|~c4 ( j爅qkmyOjtZߣbS9D% O s'\Rq:զid)B,Nt u3DImy-9@P K7VuiNaST+TK̸uQV=`-dpBQאg_[hs|D1=$jFPRjX޷ /t7Vͳ`(NOsNL.54^ 3Q\.dOow3 {R9Lh iˋT<&-Nb'&1=^JM(Ĉ t$YTXoY=h\m{ %EZm.g]2⍌31 bJ߮몶\ZĺO d aԳx˱IN:bYtJp^-:ς@6,HhoHSоj(QK0fXm!Mb/f4)51\K K̦];޳i~:\;jw|\KBx熌ܡAtOH04r|<k]Hr??NuV Y*1s͗4zjA0˂=}Ci ⠇EORd3<+ [X \ޞ%S((i.M#?Ww2jk _WēfCy038?ěo|3g?Ͽ޼v :lnnasso{qxE:s ]JwުEEmt :3 d(^%YdԵ=Be~O lJօ 􉣴F,}h4c=4 ̧A갾=LA2EiB$3`eKHTz$X[&vG8 p_~NEF}Lɩ8lYQ?B.5 fCNѲޏu6;RR(8qK0QzW6gB 7Ƚ5#f'ݨ!hc5ڸ:hQA̪&NKEpUGRgv>dʙhNc4EkU3\hc8'ZSh(fϐǶhR*SmÒ,uW-5 FlRm*Ts%2#0D*#_/44f:82qyjՄ+dy2>bydE{'BȧנRZCgL:^\]$e}rQ 9y<ٚԫVW^[B$ p*lN6P`wf5=(wQ@"F*RhC(m&Ct}I 1I#QV8&FJ Rk$Il+lz!$4,i%:V,Xp0nMbl%ƨ6ð"fD<&<73֒p&T=:9[Ӽ{`_A*)YmUד)s?p^ SE9F+  oQ#F:u. ց !#hXcCY΂hV(=!qT5'[.88<^>Y<{ᥗ^ׯbDL&lon{>8.\wFu:îClVm }VL656:YmPbCtjQ:d[|'6e02 "GڝD7R uʝH!C\Q.iPt=T Z"I]'ϝ9sJ ̠.UDC@OJoӆ6:h5 !\e\(*PQdYp`hyqQ#M9hˌ@w,A!~0Ad7g#@NP`> [ tfu5k<ܐdbMg]A$6lCHMEV՘ƩT)\jb`gE*d!03b&kUtwW HjJh,Zyz3-U;),*EĀ W1CJSH ?-.c=WEh7)uBx 7+z K=qy@!;ůs)b5#=;ND/AoG4SN#Zi|YqnX)cê BX-\Tb{'<0\[$\[v8[X-I|7=7ٰ ՇX¸L?3EeP[Ɲ3uZ #0ZD7l6]WwH!j#ֺ"N dNxi%?_t ={xҋNj/Cp'8"}ÏܹӘ X1)ӣq.:3H2uɵ:i+."@8w>3_l}]:dҹ1LA҃i L#QwrtS>X6Ą)%1HW_yׯp<?'! ׉7̘ Nq簵xݻa}ß߃͵*58"UQ:q\Ч:ӜRU_ܪ5!МwiQT\ב_NY`AkȅcaDHoi3FD5.)"13PdIWZhFI.#FSBuPDR~O=Kd9S!@ό͔<תѩXx9KЯ;]JR1R6wNely]mr#Ga]C zm #*r@R23IJϣHQs z4;b>2ugeOͳf*"bk |Mf`g< iX0{6階0LIGxzƅ^Ý0NAl Ś `) 1 &CASe5q&C){w)8?&|w 0K$nφiyTM?!^zRyDG ŋ`]^Qh\mKg%rI3xrG W^ _}ܼyƒ=oxxxr)g/JlZLVguUu No(J TΌbCӣ)g9%760- 'r*f_Z10yG<&u*&IǰxnTٔҘ(a>$X_[C7_q饗qU fs&I 0;Չ/ kXǃ^D/kkLd&Cܺ~ _[]_gXΏ1=Q )ji<)xY5k2ĢU6Q1a]\֮{NHP3@!l6]Bɦ$!h' ( Kw 1jR}{ E8LT݈!]`hRBSy &EСK)8lj%/zp5b/ZN[JMAyV#(!jhhf@*rLnLR];:E1+-!URzO{pR]5a588VTE34Xzzj)G]4"(ِҰ1[;MQT:[ {).2lȦ4=[F5!5FU0whߪC=-Bqe4!TDAB{j@, *WA(,|-jR`2 9F mUg`6KSY1ӲLC+Y_xMhЛϊg!<=Yc=M`|EP8!0ĸ;H_,Y˜!;P 7F XKa`# T^[l^ L;= Uz}xpBxYCgU{i&TRیL"`JgO-U:ij DHi%8Lscͧ'g*[ io!Uy'N+c_}=>k+8<:)U2:uuH@af.A6S+V|3G]bEiji/)=Λhִ!L-CT#u]CTp.٣ 7jWӋL^8Џ%MnOl\6}ZXnc;Ɏ^DEk & ]B:!D{c6=VfBdקBEwdHS^) 9$wSDs$gޓPQJgd85MȨZC,pj֥I=K o2^JY_} C5-h!wE7CaI레z:)47T/uVt43W=nr`ynkgcDE/aiX 5BSM̆ו 9;ms2iJ I$0u 7 #XdϮ BX0yB)d)p.LG$qepPڗh M#)i/#*^r K fW^JCR|~mLN0'Np~P~)ƍb3$#z:=) 0ϪU`].j(]Qӧ-Rkfb={}|xҦ^ѓf9>{ >3o 1f8:>[7iOrәMLvτ)\k e}bwo8a1[`.aq4;d@pEOp.:Hθ7a.% 0@y=x{ߋ#3 A0d>CeW *jtTL( ιifAo8X0 Mf%xtx¯J6ۛX[_SO=d 0^[G63?OM~|wny>&z$+Z[b,[EM%ׄR⚁U_MfR:%>+jB\L("D]BWUdLscz.(R* =?,J>R#hB{Co=FD'",-䂜jiNrn8B!'Ӛus`-Hꪮ b:Z,K3펹XNJv(͈iIy4ٴDhÚ+e: T[iemFU+M5 7f6)YŎuhB^m|Լ&PunP}Y V ~KQGR͎D@Cm(:uews)74å IDATE̍E2ڪ?X`tr& KdQ)"N dgmkXhсLFRV֒1ppIQ@UoɈ P,П-no|pi,8&G֋6n|0}$$ `l,U]<}Q|`s1wV,`hژ0gBW. B/L;twGFo 7Q'-\PEܞ07{#y<1"SH9D'Q40I$Öb Anc-A uU1Ҋ6Ѫ35M(N+ {.>я/}& 'ov<猣C޹SOяSu +ҡ[tS],A̶LVIùDI1m *+ fBl[0a1J@ Uq@U,hV!,.E E}wY&|ggQgZ}:LB*IcXaTKW Q** "-d16q+*PbdqdO( Hےmڡ}alqy*5tCL[e8 L 1s‰.>YbYCĉzMǑzx TAQ`v 3 w"ϣ@a7C6G( )%E1)HFNjkDD˪& $6$2 VNXV%sjȑEg5od'PՃjZd}hE%9Vx:i(=7o@RRzÚ w @)KA(I?~{L+pJ%>b2JJ 3I&pK2cZ>\cUͣy-8foq[fl/BoY2bךԼ`pBgU=K7 a U8XN^|0Ѩab0,sa`lN*; OMI|`XfGs:UE&KͷHY1'C-/Y>1͔=̊z+q|bo__O> 6^~}XY^1qx@6=k4Pqs6icep=x5Džͳs&s\vϿ}aX 100k8yϟ/`9,ceiUL:l6l^.*8o3HI%Kꃤfm+H.tw t cd@]T)akgn3tEsЬll ]YQpK8Qs|@f 6 VjhHht4Yd;B S+sDWPqXd>EdmNM@J|mANVjG3Ew*s֔b`:a{'Q}]*d@5E d 9L3Y}Vɲ(gIQoyk@p‘cK:e`?$ liU^&i\լjBi5}s/ Wr±| rXY.xdyD4n06Y7zg 1޲>NjLrR-$ޜcɮJ7d!RoTل03c%2; h tX@6/ۤ< 3,MD-jIB{- '|3xJ~ɟ?SO>'xի((eݝmlCN%?Ok5[lB.iO}X8Pq-p1=>agq.^@-H"c~)c:;ƣ>c,- Xd\~ ;;&K&K~W/y)o^d肎:uDBJWct5YRѱ̒tFѮo ^@%#f*]~gK)PfR>SYYػ%(0zQSIB<uVTiht)e j1C {N(  vPR2(J!уn]_fI][)t . ]~HْOֈK_B׀D^%*Q'9rV@Bκ^k!ėtOHF5Or-}v56CԄ4KT'"jdy[B "U[=\m{b|BT:8S iC>lsPj8L.bAre)aϰ:M( ⸼;$'~{/_m}:rdT3\HQ95/x#a0VD޵ZqnB8*)u0bH-#| ,FǞF$p7Qc"+c54±6DyT:/DàpX#ƒ*ppXs\NÚ[_SgG{qU9wOKH S}b\O;^o~́Eqvp?ٟv&_$E|cfk2 +ު ,DPU1iOٟÙC?^}xE,LDib n߇zL:ΤԮ& Àz 8]_),JAJ&KX^]Kcey8s<6ϝÙ3X,,X,0_1ݞlC Ű[,1I&MчT]=+r_j7NEu\GD1J^{2%cRZZdͳg׾y[7{pgX0nw~?ۿ%%PNM2۳uRX >) ѐH *&ӈ1-]CdFզ] ATILjYkmY) ri#HܹjvQHu)JƼ9BDm=TO'N,w 1`36)GɕsD8;$߃!u0,M}¡#LAvN اwH6}Iee6V2hk`!bdOdH )aZMJI[ (AaADF̷EN`9%}׳T:*vQ Kn٫0;ętZc:SjH*!c3W5Lk)9YC\H ^T)Mj9gRlj.bE2=9GF@$Yի~p[+8KQehN贜=?.ꦌ_a3I(^=&Nz ̞'gH2R,BbUTrLkhOۤ(|Ji2%Kw^%}Rzb6[xc}Vإ7+HrsĸZa,'nx"i$ڟ̹e M…%q5 ,=?y6aH?-\#[dM7[;Js !Al!<,KSP(ǑYm =-O__Q{??|of|wu{6<5=nJqJz ~/U 9[P/ /߁.pas&v`CK=$_E2Ҧ'.k~b<5yc:e$o& Ah6٪\ ӿ XMղqSgmA:s&6ܸq+fJ]`{{9ﶴceL@95TnB2#I+"πBjm6ɬ&Ck©a/˲sD@D}޻^3£MDΣbQ Zv6Y*s%R.nɤ`&s6V ١<(f@8M!qRV7ZI*E9Mۙm\hC-S8ɿL=ȶRx@@Y> 6OTvãO-X(ND& 1];dț1ƅm,q(.c23LfXvV c xxgq)S akP0 !?gH}HkreL7JS/_%ɾsik̲:hj˼0&If,Q( \RuaNM65ɮ:V" R0Crn̄IVxŃzLہP jp+|M F <Gx+Tl|}GG5L ULb 3m)rNB2l\ɠ~gs,-3!CI(EG`Qk*S{x1K.!Ã#,--cؽqXZZFNX`3ɀ<<$9y;)`O,2:])O25y9)lBB((`w+MhUI:!t~ '^EhT A-րR4!ywǏZRȬ y)L8"$ [+ 0F3Eٰ51 |wz7cOCސ@Uxyj ׂHi"+i!rxl8kE4LX,95.mUqVڤ5X r'LvXiRN4Rތ_OaBҐImngpPǙtt^ l 3@6"IҔm>qI%ьQlH?;[i¬` <{ Xy]p]Ҷ!łT0)Wp<eb?xlVVcȝ,.p<ҳh)V (ը;\ +{ MZX]z|_{A yh|nDԂ7YtEA2&56294׊WשIJX׋$Fzeg*\/0cZ':{>Py~E V%[9D)9] !K)N%`SclH[っ&Z"Ot@3 )%tH)lw ߍ":Y(y8ʔ =T*?CB_dǟ%D?yxEfv0 |4 oCm?7MxU t -<(0ž)jaJZdNId>V|o L>-Ŧ:RNi EMUҍЈj>ғGϸ6hoEoQ#C9߹+HVFjT8`QK աE^0K>ؖш!2!S^H!=PvAXNq8U\RC"3䊵1˷(!BK^kn '>Cޓ+L`*1։ØsiLO|8/ka=rŬק+裳5Q7F֮^Z{iM%8躥;SU 7yܟ}&O=/O_|wGzvRqب"K0WC/kUE &e^ 2Q) {fUV`%S; t?d(yI\&)QT{%#B2YudQFvasטSI&QrꞍ8΄g>G39s@n#?ݝlQ0ΰŢ /8{v|3V>٢"'m~`YAvP _/ԯ 4t0 l Y 1ahg#5 |:M!d]-0 *^_Si0Bt~IdBnv'Z*Rj? Fn]| F x: 2b eNBa5ODP28t[&":Q6b?ӧM*4\|z lGY*몤H->0Y0÷BU0Fs#_9[1TΫ=C)QG,ZPfj GQHt+qȱ  2pR |& .%E꽭XɈ#]7zvuULO$RjТKXk(_9):0TNm='ĶbEdSvX"R ȣi0`PlG}k5qFjma)j );Cp?|FX˹v܈ /Fy5zIL"̴h 쭐 dJHhj*^*6bJ+4x t@+Hܭ}ns`&'E~;1}B"sD04+SuYm|DS??sA ^Br)4 gZdH0Ep_zw㸿+80'g'׾-.\8|$U/V}&곁'*/t;= RC7&ńA2v7tm#{!٪]MzTòfpڹ0r-ݸiuddVPc8.QPQ!X&{#Žއ^c IDAT%`}u+/`X`o{y dmȃ/ÿ?DkUHh!QiHtU'gr5^b!W(SȎ0 Peր" RnkQ-B`W>FdaHZb,/M?2dUr ũXd+ etLFzɷR\Ѥ4*RY@15s tiW偰:%S4Y Rx4PZdeΊFS'h L'R G_d΄!UlfƬShQ~+ʴRB]pD@̪U5K3 $k\J"YIwl$ H9AYN)c!y3;f|bpR|. &w۫^u1j|@ɤ햹iDQV3$eF6O׿ׯʯo;ߎ߁} :[c =[#A_/+|U2g3/6=d7l.-͙CNЦwDAVj8*l2:0p<đKXL"ka;LU_2uԜ4JRucArDdUgϜA&ƃ>up4/̀$f`muΟǬ,01I)X7̿]ܲA5dr`xûvdSngh2Zimmh 1һ\ HQ=o eg_U.-Jwe1"xZ\n]+hIlwX|d^(`F  GV3-/N*4S=vE]įrN' _XV &+~稓 On:9!?NF@u]lZZx @ѨE@;竦  zGR4qW,pO#.G'AJ8%L$Ch8p5i7 JԂ5$ʏ1RkuC{ڡzz[N$wEg3z.4˂4$R20RVrg!l3{/ 8O=i5V2 +rMۦ$H-nW'L=&gҩ6dRŢJTܦ|XN$1yт0YiamQDžp\ G Ba!xQhRc-12lnA&b#3Vqf`\T5XJQE:Y$_Z1r,'h)-c;B"2TsJڦk3h`1!rb`9Sn@F>v|*3%ٽ̵؅U//:ݚqao$d&G1g 6%4׿o;o|_w~[ٿlf!e{my+. F4I2WQFw$ ɢd%gmdª&C4%0U &/X9LK$)䷺o'Ȱ؜a:ӭxHޘTd `;ϸ6%\&8M18*$)L}@sNxߍRA X"jb\Tle+ SOPR94|U[.',d2F qyy "Yƕ $!x=?qɪg'C{4W<}}_y{#?<~۷l?rl-j۩=@,\х3d&pkmQ$A)%_ G|J?Ï<7[˿kX]_kGHPHD&r+ ݏ(WFOjӀVK4R"dI\1G+$j׫@Ȕ5K#v960I v;Bԃm24 ]Erd[Jk=IFVn*Ehk>)L1*w,ΰF@=eKEtMIlRZd:BA- 0^e͖6IN9"#3oW3R&eG$~PbżBy腐6ƣbWеO,>&}ѳ *U-Y3dFȕ|0-QfJQ<6٭1ׯ8=TmQE2I5=GYCI:7, *[h %0ʐL;H/uR,D\[R+ӌ a8\$f0$`MI"(:eZ0XsFGD 'U@+>:t@&Ҥѵ[ό%A,ďRF%pNQnYIT06qH1C0ȾF4 89BMz4-4vO)V2 Nj"6L6^HH7{Xqz Ytz4ZhiQ<2I'0Lxa4}¸qsG9 {G9zя|oy_G""mxOj(r&Lʝ0Pȫ'U5h6 +Q F̈LkX㐽{*G`S JSJ>]EKdc3xtF0WZ@0G%VuzQJi FLC.sZ]f@ըKpYdDA,U!M 8DphHkdܦE ly + ;QH #jaIAByy`dx΢"ƑWU2qOlrH#GZN45x$ֶA(>&s|ܪ7"9 8-Cbm BEGStĉ++Srta`%3X՟,W'"CxLaH]'p*F6k_-vIHDmz30p1˂ R%ĽYaۦv5$JA?%+Qqrk,r2 WgL+a/NeH"-Y"&D^,&yi;%l8~&pqR.<2 S|b=3ʸ 'G>.XcQL;hlbSҹ>ɫktZTh2nabQt]gAD9z^)/ĄQ8#`3*9(R'JcNO__l6?GGvt,$56pۨT=Rϼq;SmRs P0r0*Zc5=/S׳K5dY}N1n N0M{KnUlI_g0 X n(L7=e%{>b)OsF)G!qx|Ս38{,&pK>bްߋ[( c>=I\S<܅ m̫*W*Yo>l9*`2te/LW'vj%RE!Y@i'Ee-E/9J;[7SY6IY5 aW&>`tsp7m#yqe7Te { Q׹CNAA:RdO'Q:%у6ǩ s8$>+gSʡ[M^$4=.%=T|x!Gʎ=B\o45=' ̎Pt>I0`QQzHdW)ze!$TMA$|;1>lN sgCw #9U)M)T$(b3fOKht6*]U4q-pygDZo 3n /P{L*-ZL2 &k#$6QƆ%djIќ1˞=kIG[6 @bZÌO BpfB0a7M&14zf\%: aWPb3 =aT2#v9 3VRF46a{ܻx/ MRM0z⦲1ȡ PMSB6@iDrg[be{ c6;rFɝAi_8.)VZUIFr8#m7۳X)7[_b2Ѕ_I0uZA$2H ?yO1 HiN0PY a!ׂc簨I{8ycǼTu,8b 77a{w.p*$$S@cdjK0$kGCܬL\U2h-A&d*F\M}C, z '2\ȯ^hhKՅX6θy-cK" J ',MMl24`L?>(]9:dSK#9G'Z'˯ˉ:8S_C]HW|  h/9ΣL):JFĘ Uytr %~(U:3/VۡWY\ d=Q+wgFt:ňt*w O4,bfji#@,)+]KQNNo5!@'ɚڜpLM+>d!BhՈ. bzt)L@)˄{bMR1+Vtkt؞PiYQ1Yƙyei%xA_$Q˶; ڞ'VsG㚱F pi ~pFɵPg 6TsQLȣ P ?DV0։w wfc{\*fco9wGWgxJ[.+iSĂˬ<=s4ǃª'L+;U gOf{.9yTt*%p'Sy\U(qDU ߵ´OTdJ[1F*ak?<}}Y^OSwތ3r Z;'Mj4>[LpbR'*!Y.OXT ТBWh\b\PFdp !˰vM3ٗ% \;K1kdG8?9!O&&Ki [6a^ nwn`o/x;;ץ`ȘOqP"|ыWeB7ppޓ{}.Xf6gAԙ|Q5L3%?*aUYźNE!WPB!10%sD$jҀd '!tclagw[[ǘ8><-yٽ`yhzuq9ុX ؾq W+W?!*0 1LThC`в[W#n0jW)XV>c&K<+RML7- .AK}S'g؀.6_<<0^" {r(MHU~&ن}ozo$V*įM/$>,QLc(|٤)gI](JA Nhpi`OtXt T*2]ˏr)u;T+;4KAj0Ծ(!ܦـўSY[mP}F%Pr@RLEs Je3>k_U2yS $1=/|Ng*SR߳Mn^-`U";lNz۩}ytT]biY{xp{-ywJ'aO4Ygֶ7IL*ZerM9Δ1(>Y@04(.z/.<.ȌI d˩@ E"#I+Su6L049btqqy{S{;B:]V g v01s{DXs%F09 rOחm\"u_  T~u4dBX'<@Mog0E#nETw&/EXR*ePp:{JBpMǭKs'@R{MىhçB2V&,--T)c>7w}7pttt#pWp/Hvvwq]wȸt229JS.nܸXJU ۸KE|Yk+b3r+6iJKfXP6M ymes2$ʭ r{Ɏ֤܊;1yF`JvPTыLPJ=lBvQC(uLTo4!;3D)vsP3ܟ`TI:\i@OݔJ N )baIHct֧f*&80 )~ :y` J rj1iBn)LV%8vcטփ(xtI$$YgM]<?c8 ;㒰Bae2 -!l!2ڻh.W/~>p e4J tLa" !BL31%qT>vW^~ן=+fYdl/,,p aY>5_¾btNEŃ1 c9=&n%SĦeC=oG?6jcvk癧ui1x^zo]w݉g}){T XxM .^$,b3C-X`4&a h o.G*B-6e.6)TvIA%ZGgfx#eۄR0WL-c>[..>㵵5'>wWacu/Xl:$/zի1dQ{/;?@ J#{.λi·|0>6L"{@A'ʝ6ГIpeTbEZgdؐL3PrXKX6_ E.,:R䈨c!2BO FsS"7HOIjҚ-G '$onIO~HW!R= /6el]=sGZoT$`ʞ~%H.1{WfZ$y BIk-KQjVDr̦(AMmROo{ztEw " p`+`ET7 [%ܘN˛d.]cYF\uR^MH 2Q;B.1D)3s4i-3mo|q߼ugęc<:v/iTġc?0zMb4R$d.llnVVK^+=`D@) py^j h4v`Q|-dj bV[Myc1jR_Z,d'Nח߰}spczKCLN0Ɉ`Fj*Vke+!ԀD^Bb'E`uefGGbPeD*mjBׅ HT aC0Y 2f )3l6s;nVUMo!w#0 _E}xO'~eLRJ,JA&X湳xWawweQeP-Lg++sEEKO~S@?၇^Gyjm=jj%+LY*R9T3PAD(Zv|2In:/$pjmøgyחN{dJBA ޴ HSHNX'R[EΒ>`[UަOe&OAxQfDqn$ zN HtH""[a"g H >idN/g$&fpF12&bP]&N- v}ߪ nL֩hzurd$赋v4IJIaW'Coj~u8z49] xϊ8s#F^h(PqC[z'_(e9J1I10ddeBtGqs7w}Elm)f)LJS(8яo|#>^'>Ȯs8:>By{&.<sbW]'i1oƷVX߼ ,O|`DlVҊ슨E8jl故]%VtX;pe(iq)x'9LF1M NoͭK.M)&r{GD1ehQ f5)ΒY\IcP'yڽo)qWQWhdPkqZ֑_4Cz8AN4km?FLt>@YH!Pėki "5!+!˜{EBe2KE)y]>#Ϙ*i4 uSe޳0\BAxxώmDK d1~V&i\mɲv?'Q\bsUA@ ab ?P[=Fz?PGi6d|'pwfh Z$S,m-Wz4 &LmT)!^$Y5'D:D% 3OOp}Q8̤IALRj4h- /ɒMn]ek0[-ucFE!+^GR# d䊊cs. y޷7m-p*#i'ZsJeMbRmĭ$<j#Zp~\=ao^qsl뙰b.م]s ) 1s 6s`mBQ 8FSzqOחUk??ի/'MP y"/R8*84%=UiHHMj~BaM NErCNX'#}k R.țO,K@Oo0-׊qVOql &fq8VA5 5QhH[,mg  cP XM Zp>gED9W"X,7DdFhJ{^=k'6iS' ^dSDAwB6 4L* 5Uce'6]vQBij#_7ܘ[`Črf6Abn>Ԛ'JէQʮY%ʵ"mZcX݌/Lp\j_r<^ٌ"wE2LhW(S {c_xO-LpG`6ŁKkugqXn, 3Qo#)L k8KX2gOٳP+{vZyr٫+ڜ5c\>}6qvpb'59Ae~]8DހΦ`sk@!S hhKQ7L 9?h<}}I^7oSO=K݆ k 1F[M8)pBba  ҵj\0Hvhu 6QXo&KBÀaX0}6[7ag&ͦ8ͭmbs}Y^<ØL&X]]|mo{~~ 9goą | ߀Gym$yO4/.\>a*>O#ߺS_R9UۻZPslxե!bemOX3\vD W1 ̫pU<ɊRj6o~tKVq+#ҨOx4ε"0U)_(E&q֕Ly2nғm "$hmJ9}ɦhQN*)*"x{`f!?J-ʣzN(H(gP *9\L/N"(Qa,[95df-EF n79a}Du3źXo cWihV~J")"^6 oQq}>TO־}+&g:!]*]i@Qf~gU̵Zo}EQ >D=I;* Y4EEeꪠZ|)N ] ln.#X<޺VrfdƕO ~<`c \.8 :֊LIERq">LvSF.N !Hs1Σ,xǜd_n Vݱĸ=^2>|xo;{Wf605^&ND} Lkۧ6c*6b, v- cV]J0r*cVDt]˯:|xQ73yc,71wƿ4,0,`¸#JD~U KG>k׮ޞ"C:A݄S%pм!LQ8&%fȔ}+}xyXM#Lg ulm6vwNEQ_.\~bss'1LO~o{p5]z~\rO oxV<H)ilY{srVݪ{%$$,D`H6%!  3aC&alHjM u%zQwrLD$DuVf`c|4,R[$*Eu+퀘h hSRrBW Tڼ" Φ]hEcߣcg#;b%fPL96(lS H7%@ME`VRY P.!W:Rް[II4rLUU#Y4B6f䵥%6)$~t5 VzA1n-+P_aYW PZU #*qU(Ŕ较Ғ`f'6s^ة8Pdh?+(b4r#'z fpI10*f"ٙjPzʡ ^㍨嫥"7S@Ky N4,=lMng:+,[UaG MiürloIoq^{RZ-M ^ŮOA)>gy|kGs~%N6Z)@.u}~V!ƌ f,D+f" 2 hJ`ut:nٹy67.8|d"Ř~ϸ"lQȩ5A!(6o%9k`cO-UKjPdJQeҊ =.]f{s\Pe}}=񦧧{ĉEV.s?G|WW;xg>>p8hp-p/|/| y|8h;|VIӍ)_gNNEiEiЬ7(hwEԧ9w~5~L' lPJT@ c@JAz sssQ"}}Qpw?)._Lkn7xq$qe>}ם<3S`G#CfbMYƛѨloԚmjXʢVSrhL\чcjf~LMM:T2(52h* UqRlLl[8ʱehI=~Udt*Ub2.Bq/6] tl,VωAQ1?Hϒ/T-7|M6 㯊RД2;u\^T sByM&JcJ(08yMC^y;4'dZ3FhjWt7b9@Zi4̅ȵW.P9{<X V=n/J?Ũpk5ʲd0pFQg9:,[X8k}V<,X\37JiN ߩ'z/wʘD./KPmDl`-:IJ؃8R)4J 4kրNʔ0]@/V:_L,1N3cibkQ+3CPMXڤ/(97]#٥[̦b#߷FtF@XCC=3([22c(?,Xi!Un6AE$+[g3K?.9Vf"W iXdgk35Jό1u0{ٹWxVCZ)Jf/vag1g iLbI3:JUB+"93+JTnxrwzɏolShJ!]2]*@Ӧ-T+JKZzq >Դiř 7-hτ XŠ"dx&дJĪ=%-vuf~cxk_%ӊGQe"zFq "R +&ըs1=v:VVVX__eeu~OЧaks^o}N>?pș3g[W‡>(_e8~8VK.q(뮻xYYY!soE0}ؠ|| nGyj 퍂S|a9PL`ng8+,jg2V`7 7e7(`8dmc044n̈&~ba~/?;oF皕ɔ%ό A(aS%hs!0< D\c0Y%Jg }PhէPd !5(! y<1d= *b%90+l@Ƥ#2rUR׮RBd)~~4d9G[p{d%,kmr9X &瀃st g7p۝|kOsXv7?nos4 N9Iv{`yꚓ%*\=G m//?a}eO|]o}3. MUP`3?lsG%Dڱ=71QOW1 ~#-yh7lV9P.oB%B( W:.2ASzAGJLgCbJʊ&Šn<0ɗn˩x:WU `O&BU{ =('PSFnQA{zçOvs>}W9DQv=-_bhZ\~&mts m._e4shMNr2'nݥ`qy 3*>{AXx KesssD TE#CK AFk8Ӏ[pkp6y0; \X4[@h<zճ` )PZq a41 jCFhyM={IwO g~-y')\π|V0Mo +1V%c3/)5"Džhs? m-J/~˟3Tn֕]Uo-PAG8 [? MY"/6Tj  }Po-bڤpcX R4ޅ #|G,rw{<կsInxt ^LsƵ5MW.\dqLn7cwԁYZm~Ȳ1vdkh7>sFEIj,^o?MsMsrBa$ȇ<UUlu/\{Wm0xBwEsB݈Q*%o&@JI1SIk g즗P=-8LYƒf)^@clb貕+!N&kk:[Js~}/gg?&wG/_O~rȵ}Jq{p[k%'%smj9LNÁCtr4ۋgY;tSR4&<ț[r6%-kMNrUzaͺ 0&9v(kEҚ^ufZSbLIMgP1eA9*hjw F{Rs)z}+"{Rl/щLYK%i0[}!F: an]M$PáLeQtnx c$MU`cRȮՔRT\zv.&/#},cUU[y ZLӢg.0 !Jߢ2T i3nyZLLcԛ OQ9ɝo~͕U,5o$N#v&ϙ°(VRLq1{aR Ka,&NfעTA!tS(4A /m*Nh&5jy]1%~M6X_cekk+vv )c=?5~7~YEQadQR8n)iuoĜE/Vv1]Q4j怘9j=&MQL,C-H(S!S05P>2`b:iZݰ^wcG{ތRx iwQ/>,e1dӚnW1 sEA'v>FkȸNq+Ǐr9Nfy59[9tt6pK^ĥ_lp)C=,~("SZ4I\hV֦@F6A{^$$aFJ@6:-V:;J%lV:{Q%'*qzay YĞSk4ӆT"lM'-«sc)VT,kIu#<ȄIhT z|=L,+.± PUSqMiQ*lt5#+-DnRs82~IXlwrk׈!h*=p gk֥-;CZ(xo~]{C[a]N,chʔk,b[~4-> h) žTSC;(hfYPQO䞿8}<ǗӨȕu!*ΰҶ(B\y/ *G{;p \iU -m/r>r [} )Is{̑Mh@b{t l<§j*?_:etunrKlbr-lO J"_y[{u0x<ʲůR]ZgyzA&nGg{KJKcggU2#5ZpkcĝMX.a,pm9<c|Nw(,1]WQvoDq o_|EW68Fl"rEQV:Ң&mJKV&oq[}K62>ܢqCMb%8:R Q9Fڥ9T62p ROfXv}[݈\C|t!(^cŁ)F&ɤU+BуŲ4uƀ\qHSd$P5vk.Z\~4noA{r/l\s._IVZ]9fZK~ c,./]tJ.bpJ^oa\},vS5-*4A,,04S;oZbP(k{OJET;[;\tu>[^ccm?gZgVc#?2-Fɒ2,+8FɉV$42-ك0l)8Dc2Dž IDAT0-^[iM[ Zc69Dybwhrrk|뉧ćNEffgi.Ȗ+~E7^jT6#?k-|iU9$M,Q&ْ0$2@W)f=<»$}I9Q.=jRCkcD)2CF1pdF#v(;7h]ȲDS>Br Xj-b1#/Yn8B`冞9lNuvlnk&'^ߤM`;/nΎ[s-:[lQkԨ7XkYr6}P )?z T`L6 +6D)d#>?h qM"S mDV1.Xqo*W]ΉCL;e<*m]E&  >jb0f><%k6J/Ӯ1vIil$VcL>`ѪC؊MBq5@1ǓnQ&*| |nj))Wx'ZW'`YX421eHru{8r=˨+xbF+LdT{h?K?*UQjh,^W;Ƞቯ<Ļu|k_g1W)>`[ES\ċbBMќ^ұEKcbr/ė~8}< h'E*Po(Qk>^dw`#eC QT8*x?̯'Fl -f"W#`{UJ UiUz2,CY|~JUR+}d);4}~-H.-Cc})`/bM=1p)1!5,|zxv9s,[[~n$~2kkp>z*J)~_Y[[ :_#3, ˝MQ;YvY3@sp0,epfgz4YK2wӫS[틓ML\֢2_5*cLJHڂ%\n ˔[Q Ε^0jAvޯW Vpa7X{BdLe+ո!V.<(-BBpq=f3vʅ8p^R9'Y iKdyNgqH7(Y;66O&,y,f)Qx[%eY*##ٞq6WN!+ZLIa0T.,Tlaƚ$CJT)*5P O&9^8NZjIUmf4QGJggX \3ɰ2MeފN1B4sTb}~86!:w{#T nj&ąndYIᓿSk]"3-T<j r/XJU5543;/vrk``ܿ}? Puߙ,cwCܡWٚ@_Z#_ GOMw{iG19E#nښ̈́?JL%F`>hZ=fp?k:nol2Yervc dR{{v(c3թRF-7KC|{r֠* Cu_-5pXZ:*HoH8Nl[X0][vt tY|lLJ0 *ʆpL)_*X\]: ?!v{`N`,kk|c'kK;]{ѣG1p%n6~iww'x .vQJ?LNNIO77FՀ38s-kC{Aáp,t[)&N-`g&2-3aMIYd ̲ϰv˜tZV{V4睒a9s}]PT1C]߅2(F6eJg t-  h吵579IKu"yA>m)7FM(1~[x.Ak5/uj/QucQ}~tr] h#|@H\݄ (Uo*JކQe+qYUzB9KVDdlїq@ ̨6Jm6=Jc336z+})$iƝjd-lLL$Ɛ78Fp0=INVG?2}LǸ'5yP:  A!֤p 0~O1){EKB4Mוd PV&afF-)h{c,OY6CUML$ ;Lp۸)LfSiEXBJb SK]̂M+( @rlLZzِRQxm)Kȕ[Y8kǿV-YjgP°އhdVƅ݌隊@0Vj&D^ W XuRtv69r4o,{*2E3ɧXte&',\ 5]gs^`}ZN-ˡ(iT BURlmt:׹ySc]cG>z,QZgFm 52;|=Ly3=[[(x`0`ss;3~ӧO?j5~￟#> sZkWgZ[ࠁֲ驂9h/a<Ka$Lmqi(p{Lv0vزdTq 70솄f/,K0{bq i( ^ +ww?~VEY )B"TvOT{)i ʼnz3=7)E͊`bRäE]7*yuSRZS )Ź|ij%Vx/n [YKJбJْ&:?yQǏoN9a3>8OOO1??WO}n{w__]|ӟfjjcǎC_8x k"+++ûəhhnnJJXRp7O(YI>[bFQe_'L)ϔNY!=Sn݁sǻqe0k<طysZ8%f۝mNVC4 ._:%b@Up#d䛏=FK5V1woyo~Ő _nFO Pc(@X D^ 77bba|)&VDVQQ!OZ"P6"F CcRdRI5V:"P~#}]_ӊ30KԐ4P+.UrδåR"f0SML:uJpEGϫxq %p8J阭Wĉ^]Pr|y++:E%8^=4-;"F[]Jb6?mEZTTm#{VJrmW/ A>&H Oq+jMH*J3x: NNk@h0Ab8H8/L^`֕ +~~`ɲ,v ,K_r]D6$%@|20={$ i^1_&+n- ʲ@Qcnn~JA1$YMaCi˲,e7^\֊~kxOpӭTM +C[,| X6.cZ ?õhR` 7fGdߒ@OII*i!q"Pu66Lq1L˦)9ILVJrCuD c*cB' yiJ"5REư Mr'c [zڃPO, W01\Ɲ+*CY1`iz4 tjLvՈv+csA! 0)F'sQ[F:/`)%'bem (_&r#;"TŅ8Bo V6-$IzOl漼sF_^MIu V.]ǞMx'jPw1G)>Xxyu#FE*Zz6;8 ؐv >[}7/umX=ЫcVU3glՇ%XkmK( sִe>>P1P~9?\mK9sA3S\*6 h(/5b1 JtM jeY,Y+X;>bg ,'cXy}'u0x|< KZ^b8T:Z,_*333-oQ.^}{g>]w݅1f'> ](Jբ_ƫ3[N7,7:ܜYN NM$ `n2,x#o#gi<İަlԨeN9Le <b*0>@ZgJF0L? /VEpP/?fx])n\sO?4ʖ` vҌn`ksѨd0`Qltss fg8y8_zzAV>mYfcso}9_Bg:Sh L=h"!Br1Q%0jb{;&LƋJUaUL[t^B!54 QV;t>Q2 rdu{g5I"@IІL|*U=1wE`qnm~t[wdf(~/חFs8JLE~Gc)R " 3|+ Rce)!/ۆo^HY`U#Q)ҁC2Z,P@<&>hLPUKǩ2:JJ:\}Ո C[=~J)S^J]("B['yD!1 Ochw#*\9h, :._PYlibf-iutr) +|J[}鞖9 Νت^N;؊}M`"L ڬV4бNJ7Q;?s?As&fg U[ 5DJ5l`eGtFdɆV MI) ǫ/]\o97 7Jސ-a1ܕNCmiX`b@ ?Ï}/~w})k(nmR3ܨaÙ@/.>3|fA6ٞ9=t|n95"vayڔJ=5[ arR㌋[ǣWwJ&x|p-A "*֚CSdZQ˦P,-ϞFיʵU3hu8slnnb4>>s8< O^&M(7h7 C`Ûٍ@p^%u(V{LIuN@Qզ^4e1S5$- ֡mC-GʈNMTX@/UJB1 H*ȑm AvucR{)N%$k胉VJFc@E_L 4"$'u%@bV%Ĥ$V0*cC1ue1EZ Tr/zC͊ykgN\}-ic Jjl%ŝb?S[a Q!`I ƳfV{.ņ#Rrv&HeIEȂrDPD2!fl"HcyJqp^5&3M+O*W3 WnC-lCHժA*9V{0rڱўD5 RdWʾQR7_*tz}Z;n^{*q?*yBˋr0N]h7ӗhKfSD>-[}92y5 m)h 5-/`V!MӢBE8IFE\<+Pc>9s,Ύg /Ovu]>q{u!(Hka '!09qn||q^"@8$ٹOy2D腗T,aSa]V4a8`M|_Nr}' ɚ@(k$X4Bnfo| 0\8_ a:!+9)M r[R<},&52d]}δ.""IZނ5)|dMFn#Kq6q7cR/F"Ov# clFx&,= 3Į@8HBc!*6ZTک bÙKv{\Qַ\zDNP'q@i,!3{(JDi*x6"5 Gzdn6tXȅMJu%t y>t.huھ]~l>/W8! 33+24Ajc8@hWF8W@L||@|aJ an4@wbe+)Sp(;lcue[p㼀6ps شiIB(l߱&vځm۶)N-3U c[Ρ9UCQ@/V3>ERh}pLhX(2ZZiB3G\* g Ŗ^P9 ÄrC/?s_(wUe)$+=DlJW 6' |ƥ&*/DFAnCB<<2TcdMYx.‘$@"Af6ցزIPbʅ0LjA/)LlY԰;oH7&NJOBEW^Lǒ 0 Q ʉPS)MDaymL^8`5Ol |O_T!mk3$&-+&\n+,T82$HBABb*50D^hBy3fEi^ll4S~AxbBN@P>:w^pX<_>$F>jB&L,wibbPOWbTm0 sP>~Vy7OU!Z`ÌchQII=|ҫX;~G_? 2C/ W`Ӂ9Nd`x<),_a׮AK֗V /?#ؾs_^wo?QC:;?]97ϖ#[)an_i aa +q)vɢ ]>FDŽG GX`'i _uT ,w x:6"BLE Exx k- LKf+f?⬸At8w K!%jC ůA_}6xO =vDFD&t** !Y؍ggt3։^- >THMK)H`#3"H]J-%MQH2Kc[4EV8GM5!f 9\ea\uB0b9sh]0.\_rC,; -̺O8Eki"(upxiCh&) PO&d ܧ^EN2ʇA=*) 2xRqٺU]$ ' cWMb&;Zk;3۽}7~~kB*e.2ň4Tr1WG45 RD/@IMU,#T0cGhpק ?r#i/;ƃ1e PŞ"R), 6ƿڲc%֌U+,S#fUOΙ$$Ѡش12h᝾DrCb[@C׈(dK&V(Vz*zߌ13q~/igOuv0ظ é;(7n5 uX4l*C5tYKݬV .!9R p7˪_UvFϜI/94 |1(a1 LIydI P$񩦓*t:Xx <ff6@S66 ݷk>y^@)sHa8v5r8Y5VD(tP_ʗpn4<;1t[ǁ$+g[ 4"^-ZwQ^CHBφX+1 /牃O9gw9iP8HIpPqE-uNkH,qhLF+cB~hOdjGkGS/Sa×!23#6I-UgX:>SR`ADybdw@ZM2ʌ%AABuZ&|_-!FN~Nz})ykMO3^I YDRU㎑5ʴ;?(i(kS9/K/)(z9R{ q݅+S00[[9Ck{3mpzI{a/do^[t>!p^]k,Ll65K+:ʁVaj ]OQ^R-s8hۨcDa2lk.A5G.I9AFLi`|pBV\3ITɓ/I@= O~ͩ֕ݯOA025hiߗF8>& t [D_ ˙ $!:| `XP$KfT;Tb=Nו3"+KP灥0}W?6 _ӝfpq؋*vc~uг;d@Sl&^G0ec8νln0c)6$%xbf޺*Й''?2S a !9jR@U1c7(Ȋ5@-KqߤB='3߽fX6%qj`?8zg R)/6!滿I_,Z2$F`%Bb.K_ E'ʡ& !&<zr [jD D, RY|zin"/ 1>LuvW3O$崓NA[+:1xoT7!4E;pwewG?g>*@s-$ga4^$ I⤥Yq}P 6a#|߹\4R Myaϭ1R(AApuJ^SAI')Y0ZFY/MT#A*p"ךxcytWt5X~: Yd`Z!{vRTO2؍dRVwR\]Q຃ m.W.Y|᥏ӓbV) ~.ޜ~I\7zX-`KVgƮؑi\ nV8=R -5Ie ze٦Lc}$apfY&\w+d]>bnGoU0x[8 .bvnzMpb0 w>R΢v@b,O>27U٥f333xDZeu]X]]E^R +++(YMHJ<4T2z UjUjYnIhe _i&ݪUUwR$*"eD&ؿ$}c+h48z-̟>[n'xށN;7׀H^! hm`cՃ*6$7c$!_CA5UO<-OtƈBk)2r!%3~)9RQQ5Kȏ3^"ic;y(=;n!-T \qn{"@T08JG 5/J)<6"o7".V^BK8Utx?Y,38"@ 4\n4K46~⒛qr&F<łܤBh,!]L0# gKLo%e{76tH)N  N%ZEH֕,Վ6η)0~e$mJx'O6(R=D;\2ر,-&D[(x)C])4BbBO@_!&h,BxEԲZ{AΡQ{"Sv7yK) )@fUIk?/`J!LShzKlpY[-$ )&֋jZJBUeb$Z7 _/ !C薄uT? d]Le{RլXRwaCbnQ=Cdw !qpwn~IʼnL4zz75z),..  ؾk'i/j2̤Z.K1hZظq#:[J7x:.y_>zYu{\z U֍[U %c֤Pf4^Y("2X](?,TETUQl]x\`Bg&z}CILS?xE{jSn:?z60e,`(Aq]h; f:NIi|2kU4mJB CyI@aV3%NE Q€T8ч Qr0,5QJϐ`y4ၱ[xC^ut%Ug( VN^UeAHSü/\y$ F[lσeD*eZz!X=!;a;B>cu5y4K-=]. \Ǣ?kpC2X d:G,`X^&&AՁWC)Uȗ>eNTa;̺ǃW-\8qOӕa tŞP[$IXA[JP28'.! m 1QJ=/x8T`MyuA ՗$sBRz:ʝo{ J"RT`.2`Hfk;um"}&zU[cueEteYY#]gIF*eSS#N^f*N_{KYNǧS$-Ǜ)ruuC9ٶR 7 <݈?_A-Z~@]a2z620ɄH;O>";+džhCN4еG*`L@ IDATͫajЅ9qj'MZǖ_]mee?3h6|hk}m`0@lbӦMhZ0ԩSX\\H$RӘ`Æ 8vq9sfFgϞEŻヒӧOxGuV| _ދ<1#T-<)5LDx Mc &GKh4O3O|_ 00yɪlUJB,ǗY^zo6f:m/q뮭b:رcvrmƣ1iϾz{b4VCQ@Qo_ ~G; LHUN$$'C״-62tle,ADNU ._H _37/3[6a<sa˞'0{^+5]ɏa {-d)e-?O,8ad Ëzlߡ/K'] ژH^IrRrf[*S">B V10ؐ[q-IgG1M^PZ^HgݱK#o/3X 'S2 B( W)-S~K]cG,ma .]_=SB$26Hya| jK@jvn7^ =ʟxh}]jKudIs6_-=%2$4?a:FXM 'Hu ݏ/#@!L%x?< uHǁ  )4Nh%[} 2pcķVx%NAc$P^XH#:?xڹ9$sa*%d Χ5JP|:8*x2 g,s]0ЦDquFNs'#G055凰,055,qF~{^ .ˈ, 3c9L wfuaB 8PKr2j&iLHeɮ֞Y?d*E[l"$HpK1IJ<>۴06{[u YUGmo&5+#}Fl`PRoq>A2>/bB0 N^dcI`H)$AE`h+G,lҍA4OA,u+H<'Fhl1vݰ^|x=vvv?4ޅK+xG߅߂eZ7y;vELoڀs'Og>kn܇c/w_~ ~K5(GK\XD[tB%kQ"fD B} l\ǫg ` -K)!iDȁFCx`*׵(+,U|G}Ut@Z@p3z rL2cJ#uO.0|,U<Sݑd(^8"HaQ"%kR|dpLq.d;ϔ%ȊKD*(50.4IN)" K]ĵ jン@NaA 35e\NN( zRG3sBҌ?2$Eb/(ȗq@!d {B-nM(/}81yؑ"qݱ] xQNY-GnG)aA^ѣϕJ-38Wq؄E&iSeo)$ {SVJC:;x-9:A;5XaʁM#$DCxUfvxG=bDiGmq=wb`01z "o6ߏ]vazzO=~i̠ng籺z =ٳg=ԧ>c ;vo/&Z-8tկ'? x (Iv*`O,^5u3%(j3EOf.3! UJ^#2v^p%9sǏǩSsŋX]] 0efff0==NFM7wOL5 6n؂Fصs.^p8GQX[[?y|غu^y5t:#~ *K3 z5<e>)|qz$Yl,08(Kq}0KQHP* _sк 6#"R T$ W I~@/%F$5(e̛tS28LvPJFe7Dպ,uZqra l|KU0 }F5.7omy8N6n>a/v݃^Ozn#~Z'޸3ߏQk6pǃaBvO%:C`{Ua:p1N܁R8w4^|Aم> ޅc/= ڎCfw=l۳߀Qߗִ2>+[TbzGZHv=LB 0H^S]:YA׈%$$[LQ1ڠ,!?DZ/Uތ+%e!F(QڗDՄ@Y0iAhȃ .*)R^j-$\T4xS[5ރEMDǙ?c/(Qjˆϳ:XEV [p`cfϪ*6u!:ƣ"&uW}wdyX,Lz\^8OgeJ1M@ՆmM()G'wep=9B~-`f%Z[ZnwފzsD{4>cm7c8 N-n:W~ ܁qi<9^1&y^xo ՐF>HhK\wMX<3$I_]0r|%,wvxv/Kϝh[5a/νw*Ms'Na`mxG݅q̼(0(- LtNO`40ɂy5(!%VZkw>5O'5=Om|Le?HayWR(Lj\>E0:"|ŀ\芟M{lb'Cy{Rb~?#{{mXcfIkEq`s]ra,b3Q9ƝK 7 2T/'gB̈́2#Dq$E`KFi|uwm|[6b4M$/&E3A<^隊]LHRԓP%7?T)>ˬo2@[QP w_D>QPJ>Gi;!B-ɐY !Td86Z\dGh\`DZ&yo5D,j}%; `]0A1TjYRZ@9)U+ޮޮpxh1qɿ8|0o6N>ٵM6|I:tN›oE p>cnn/fggϟwߍǏ#szzX^^ƙ3gpY<#XXX'> z=?D`"UdLz$3'fJIbgQ&xJ`Vajj u7hs4ŋ8{,N<Ǐɓ8wҥKv HV N{ tjk Ǩ=Ĺ%[hOխyF4MPKnqQpcHT wu:k&un#f6T.\%G2r<#_X?bizME(aS<ˏiRY Y}A EL&"HOl^쟯JQ-\o)aG"B(8gFBȃ ~VuQEA!J ZA_et;_EbR5'e[Xx h6ў8ر:p-?xV_Y^,Eszi"ϡK4fFa hP94?)oocj v j53]hMa ;p`Ax1k;6ʢq>~ay̲> e#u!>)1AtOd`J%<_08g=܃]9] g´`83Cy6bmxQ!5l^ -"w1gu (|Q}:B…`t'^" J^6 SBxK@T2x @o쪱"GR xf cT^J@FX2Nga1L)$e*,!!M,0(OQ MIqlh0 Dw;rA5wHHa&)q\uZU q^dQ%UTΖƧS!W2BL#XRR>I$wW}n.=WovKjłTFj~ǭHh48s ,>1l۞={onkw]<'?=cvw^R .`nno&=o~ؽ{7sϡ^/}Kظq#VWW}j' W u,d0>?`\UY;cqjp8) =||Q>BrU٭({,@=Fz1Ӭ(TA{amXY]`88ϭ^xgOc 7ݙƏ ڭ6PxGchmq;К0u;ڝ6.]z-[@)Bs$hD[}^8R MzW (ѳH7(0{fk.*LP&cm ]Z xf}JۮMB?3[BKُi$,DᷚTH"^mLy Te#hQSwñH 4d(;xGa<s|k*WaNaf lن7}S6/ >H @cj k.\Po6ADX]'Na7|o?ޅ{ }~N0CS;q .Nu ޼;]w^y'<~;?_{Ewen>ذy SM$iE)* TCn M'އO5AnBW.C >>Y9UV%q+3\gyi,Ԕ{1ץ̧uTbm7݀btٱZLDyM1|P.MK),D>Pؓn{HHOJ?P=GL ʂ@n~Y/dKQ>o﷓**LTؔK!zE`Qǹ3xsh:F/'Lcd"E ѓ,&zD,Ux#I di.͓\$Vϟ=PT8rBo^6-\8u|x  IDATȏʅ5}H'R "4p<ߌ I|+GopW;G*W%R%@YՄDQ M2`0n4ʦ/~ snZk<Ѕ]]D ^]v4 +سg:8dffN~a/ݼJ_u(pi&yAy]v8o>`۶mȲ vyceeeK%Y<ƒOjH:?fY9xp_R'uizgݔle7K_)J2T& 3x[ܙs+n}[Yp~Ο(J+K9y|YwQɳAnkAQ Y!1j஝8uq2F:rU$H&9hρAA{+f`yGlvz.o{ ZڲnƂm2%!T~(~]Z D/Rw>)Htz32:MW.*RӐUպ-Kmϴ15vC@{ cOnv.]1+.@k-x'i˫eif)}=Lu0`ὓ81lބ`'hmƑ?c<kwcT?^_q^=,4 'ADxv`mev }d:~EDZ0e3j:Z'OcIԧ\#y~?oĥ*7mQY)63VJ׼-BOg*BI紐 bi$-ǁ*E,ĖDJY86:04#<[uOY-Ip*e?5TBQW$?1j}4U@$I4AK/K']F#ldAT% JvD6,(f xdDH4Z5 ɪ60\ş>K˸p,+L/**Άz=3Aٗee1B$ "?492? |#I\h`~\Mz"}?Kg/0 + _}/>4.-j!IzǬ6W )Bu8( )S/gv+Kf tfַwLʒXTFkPr5}Sd{Aݬn6 q27Ta}-uЁiOaiiqI|s,˰W_}+%8Çcaa+++9zl޼^D $Cxǐ"7򘩅_1|^ P-û&L;K0PؒY-?' DG"Y/oU%IRHk5Z끠Xga&쓎PC@H뛯ƥ+WNćmބ;g"i+ZƐrASwTFG3ՠU4-PYLjl\ /1"WU_aDөb#ck#E^IZ 4ӹ^D / S*b:H#0/I"x$W λw[1 ]$@Ez7^jwN wg,..}aii {A*"ZYv>euu՟4Y#dOpU's,6:~4P=f9Іmd?ǩ)!oCYb*; kJ8u8f7nU33Q9v\s v/.-`4a8A9@ | \w?oóGC2EV[Yu }#$W8< C?@1О!dށAPqXekP(=8, M.E XAy_vTq@t6w7VN]wTc]]CRYJ=ҫev12\&L,:Cb$3'4)y`OTy_${X̏tŹCoMXx0+v!?Yj[D!LZ;ǿ$ozɯ2ʕ$Tq!ɞKTs[o5rqaL37J:ށcH$F1ѕL >&/tQE@-HL'aj"̓\ǮwLx?LKx{(W(UB|c+w! dM{,)^΋'tɥw+'2-KTdkEEj<6UD#v$6\dۂFj_C(~& ^ ;}RUaFkJAeB#=,#R>ZBh'W3W 3Wzʽ)T&yA&&VB FkvȰ95}2CChGB0z^B3)Vp&-yL3:?y jWK8IPϬG] *ۜ J88fR{ ޮxbCwmItu7߆;n=p].$ѣGaAM`*>#2,//{3cl۶I(+$C&%2=^&|q 1XYY^o8,C{?|e6!7 {wuC,Ms3HsW%,Td!LNBP؇B2fٕR?`Ɂ&-PU(fq qԾR>1@r؊7< [YAlTt9׎2Nz^@`Y H^x %Qzf{ yȫػ3Sf !`O I(6WdQ-q.t"b/Rk$$z_(j87Y^p݊(!׫^UH~#=Qo[~d;%%ߏpf)"z!T.D.+`2z#xC-vR4)?:\PiNeI6NV}sOԡc"Ӳ%|%aadd>TI sI-q1&8a>jW*o*rGE0 ( )0j@aT. RȨ/)J>ud9tKF1)\d£-}eq o&Q$q,l!Md F u:7>ZjZV#Uf_f`g7ZHUSl9[Ȯ$Ӊr; 4dUiw>WQR^o;0&4LH!$F@CaP$E\2dݿ ޮx[YYũSm/c6^aiiKWVVp7㮻BшVݮ,l^/v1ơ.r1h&6I?>0Zc0x`&XYyP%zR)Ad&I9Cij9a>Y`-6?/ } :ӈd`DZ%T߇Rb,U2z]94wKs7ni$8 r 18"':3ʄB'pͦ~R$ v]Wb}ڠ{F@i^RN% iP._E|' ebXriO bϋʝkLPP=`hWzjЩ%Y50#n0,찞(|_B>9](Q]\XWo`-32]XJZj3DPj]*W;9\_wVu"9p*&ޯZ:%"; EB8GXJ+ ^(4FLq؄[j =.~=( PͶ +2.7_0Zr @s ^]ѣXZZֹu)h<q3ֳ˥Ve d ?w!t>eUDL*VZyԍej9G_Ǯ3>d[9} VWWQeK~Gc5;52eOpV 5lپzF#L:~ o} o|?:V1 g`t݃^C檒ksIe!]יط97^׹h4"$E=fҌdרƣri4Qy?䢦j$U2Gb)FHhtݯ_񜽗^VI ~}ù眽% ɔS.o0^B)&^ Ȫu!PGX2KqՃ Ȃ#ͱD0`U1sJ;%uzVS+4pLtqפ_E0Yu)j<&m$3 J-!V61Γ )]i''q`j1 .Kn>\H_L=%ɡMK{j0ׁ:[ӕ~:OҚԓwOfHcTZ\KXwWУ!lb|J!v~c J^ϸ_QaC1Xn2 udDF92OK@R]7{ C|p0\MB&c8rU<'A4 jJ*8fpsȂB=%b*X)bs}Yޏf9l@.TL 3 'xz(00JC眒թ!8$L%І5٠J=;~# O9z'i`]Zsxt왋x9L#Bb1L~|?FmP'.NC9#1WƄ >`bD;#]"sE:2'|˔9)i<\Hc86LEVkB_g}4 3Q7g8qx"ql9RbHT=E<*PX˘>HA)`?6՜Zp&XdoOϣcLCL@E rC0p5tɡÓC5btҲy H4k@ **P.c ʒPDj6gy-Lɨ`Kp'L890hn֬ǴT72%SϤHlS4ȤGPh/"zoBeєt. 7dbRyZLFTAU|2ua\Z̳Vl4 g g% aMisi|9ufQ iKJgF1YBx草EE¿`cǙ3p޽N$a] <}<+Rw $S0[777QU꺎N&,ED9M3ӟa:$B}$ *@KdٵQ_jiNb6oZ}Z檽Z XO8v(^?.FٳS>QhJ<}yx2{ 'OٓqQll\A; CeU]2biSǎhc5Q%\5AA#ù&Ğ!z_\ 2flL(KF`KK"^>€x8$BJa,aiaoo"JH$^R T76bZJIbLQLlR7lgeYJ׋"ajH gzBRхwNPj[GKd= &X+ߋI(sw%sYrv[[5Y/D#y҂$)L6g"B)>FAf.2΀xcS%`\0Ǵ:DEFep&cr ՝"U0./=f|K.2px(-/lJ;OŽL(f9Ze%TP^dp1dA 1ok5^{;fԕǢڥlս "[ gkli0.G j@[$3 o}8xO}{Y5k99zF\|Vp1( 4l=Iο-yJ%\$VN!TV(c?[x!BN_ô} l嫀dV:vD 2f4hX\!6\)3+ qnB8SЕ=*K$:1. ic,ZBur:H1A ןzxCv IDATL=Woxŗ0WdL;wsu2ZVAUUk"ZhfNR0'P>]ߠ}z:TF+h[~¤*O,zS.WXHi'GS 'LЪQ' l6cͿ} g.˾]-Xf/Š2VpuX]`mQM& ן* cGמ[pن)w;e70c2to.8r _y|LJރofd^Y_"I_.DH^ڰᲺmb/"W rz± -& :&zs>vCK3Dy)㹀  aɀl2(RS+4k)"ОuJ")>?ak$k3qEY|+P~.l1 " ZedqK~xǪ{ BQR03䗘B}9ȞכBRQ#XCz1˴d 󊔖 j"Tٷg p%igŁb2#aNi1X&.2S8gqZ^g ɟL9uO hOxPXXGUcfFn0@p`/T3 烋{UרSё38/PAV|LyZTl&amo OtQwiOuR=4yߚx*eR'oa 95XӕGA MMWͳ1͌/0C>r?|iQ_YGl MyV_; pn\qƱ807 O N Z( - I|ZE|Ք/5+%FR;K2FE]2Lc&| Gh[Fqd07x`]d~A%[Pؿx=}it2M3`}F{`J6E7?+TۼRy1^yY$ra7 NG WVVP~?/26}~`g/_djuPւ1Awac2̙ ~ 0nz,-qY'uxAO|+xǰk^z (x; y2QcE bjq_p“_*>by$ 1&QPO @Q_Oq.%HwF/I$KpM|R%|b\ӼK3)_Eh aIqzUr% 9pӾOdER6H|.]LLҬQ{(˥x,%eљ):)`E 3ϳfd9vܚmd 1 t bzƩXra'!.ޟ kʇs.ƻbH8lRQi1NL#+! zVHŰS< | \m#kDgw~vy^&VdA"CHF {a:NKMȆW#*C/G0X;IJu q3OZ|.$w-KU)vMPQ׎I 󦊎1ƿ֐ څ١QmE^%?֩j1 p}Mw߉z2Ag@y*ɹ2RDhsPiU=4laє4QN< ۘxWk[CVL5QMqL~Yr20|'v_w_?gGg7˰$2IP gp_w -1v^Rk`'@ z=Hmpn ҫ;BUOl "8v~~?) >?^{09+1Ftb<auu5a0Z$Ҳ,yiH=E\n6(v;,@dlYe􀯀 1"CeOy}^SNW@4l6dsp \~`83j`yy 7t:nQ:MTAFQǿ -p+5xÏᗿEc,t֗Q_5^g0EdܳkTWU3gN3p)^NݿM?F[L ݅~p9' {iiBYv ynZ(@|2D+&=Xqe3r&h9TP&GYu%X3AbLMӲ@N8@G@V)Yј$ףDKN)F"pVR7es!)DSFm] ;1 t][ŭŔΦ(/ 5Mr¸MNZ*T@!6EJFZv?ҰII0:MeExה&/3N)^GW<:,[$&)DodD_CQùkC&0cշ ĺ9 aD.iLүI_a(BJ-ǰ"^w> 7%{fcޓc̡XʇITlddKjgbHƨ V3V61[ӧp):|'O_īI /BYq{EVrt=~~ wàg(@Vw{~w;w>x UWR6& 2طZ[&JT5ŗ_FwayhZh4XXh{sB"l5Ʊ×q .]( E)CT &B_"&L $1A+Cp:f]H+`N>L42q15J嬄]|G-J ofĘ9s~&H)\_'g>߂&*Akq`Rs.QtAZB\u)k,$|XzM4 u]h0S"c< zf1 آio~Ӭ4  yPJ @Q7kF979j倮IǮDNS~hn}9LxZsQdOCx]}`5ٍC X~C6IXw[861$,XöaTfGǚr3SyQDlt`ccmm |ƣ1"ļGE\㺃ce.]%"N'`j*ݟe8_p o6h4EfZЉ?Od):\bQ`VţX|ϝëѣqY c&cmLs-x@ml(X>S Ye:y+nh76q󭷢ລV&ů|_WBWNcss N<pq+WU_gP4JS[oʛ3?  #9nvzxKzOs&™=K(A"ʤ/2Fh Oce7Ͽ.fLp( 5ɝ8B t}ilF5N3&p<|*ĩ l\t(FaH 9k#N$J5d]5Zv&]hfvSU.a1q,ǍLh~P%z*=ϩxvɔy3R["Lg/ U* [4/teTL:ٓAYVތ莯 @1h%J $O[TYQD'p)H'č0u09ub,T4'<$7X`Q  e xc,&Jr%uXg-@ *n0FA T^9t.oĒOTp.W?'Ra.Oc<a4ױm6d' Ձ"Oq"y9Mk#t-[TМך{H5 5ij4 ,YInvKWbDlt}}i-.`*>NoWΞGwyi8Ik_ICγN8+E˄jB̈́rA-\^.L8(xQGFb p\~=^W3~);z؏p' WVpݷdO嚺o;p.L K3s.f)QNwW3T-#fRBhcN [qI:q'Nŷ}.7k\pxy[^f oE<+ 73TX_.4ŷt`0[Ku[Xlq+{' k-=}fc 0pp sx;n ̌'՗_?A]q,,tP#8ZL&,܁[^O?ŕULX͝!#dp !\R|$KA6NV?R{(Xyv"q7b] Eb(c8ѫRwHG|2Q40cHCSTj@IL82Y6 ؜&XJdYHRrL%'&xYO٨)6>4]CScz1IH;;X1/Ȋi 홝و%6ɧ'Lx0K1U1MWk7 ͦC\dRl:yo X@ک= !tEE~B}Ȅ51)H?X'Fu5$q J2Yb.ኤT>CJ4 *]UirL(ԪQ3e0ˌa Z_Lu~bhU}ϡQa]4%墬jBhboa [Ic=y YAL=KI\^{K tsTn7[ܨ4IըkhfWaum>2ƃ!}tbϖM{a4`n^!: C Ƶ "x$nUiKqtX{HC7bu~ NiWG7ݻvbey qڽX[RK]_ȴ$T 'O].`t<O]PeDM m[oqugÑ#qY?G?+#~2N]X÷^Kk} hw+;k,lql=C)Aj//v yt:+;n+/_<[PcvWpwF7ǰpX9ؘ47tÎ]qwa}cUU8XYލ{y8*@eNY`o ^zS?8zzI<l5|X |q,R62t9|լ 0p)`d". )3> 509ʤv& 0 >#S<)~(T$l) s_NPzYk!̣3& ->SdIg=dt'%9Ռ)0V9C Iq.H0pzom fߔL/ '@ &%cP J\aR+瀯,0S׊ ݂F*ɴf*ZD cIty85 lvy*0 9p!]Ed"U5N\!9YDP<ʼ~]2Oڅk2 X- , + Z%?ec Mb(I_`)(hG~]7{|]a?VзP)Jv0\{6l5&jg1q]wlKBK صk 樚LP:8u$f GYc bi-/ 5iΨ-Y S \ħif` AE&JMDW-ɤjCé H xN\\ޟ|U3SY"|_)B(JލH{Z0MP((` *4ij)T@Ϻ_1$'.XT&VFF%`5"pُ nA3;4˲KgazcoS7^@SXW :F:$eP5[)VΒAY :;=@GIGp ^`MEB )֏cN iI5i,8i+%!t &WC_١p(hB80R\(+YUtXGqtZ:ҐAj/3ED@ v6mMFAY?D΅c {S#ڇe5 ;VMCMþ7օddC87[ &4Ġ֙$Օ:'(FUl< i!OI_ؤs^}zkV9EEe=f汆4 - < YGse(ɠ$VΑ]yjJ)AezRC/6NWĪ3 Q11@6Ep^5F}m ?)e:*rhLCH՝HdZJ(3r ^}dI$QṐH %Hm/wďE@IDuL0zjG1F^H=5n9EOsqDwP@&a1O8 fQ=W\sIO&HqyRPps1$UPMK8s&c٠}2EdE+ w_RYgQʹ_6JiAH NciFlCI?2i`%!6OqSxᔨ\ddpH&xB ce,U& :X}(huڨ  岺=Abd s0#fX< !ֹ<@ Qo86>1p(qu"7RxP肰thMcmmO|I,,t+jp:~=wށ5tX ާk pP3TИB$ʿӠ1Lq _OgX? ܶtz;{ ڳ~# Txh6esL@6`'R :(wRQ jlM~,0s?](O~8{<#?@cđ?,`BYoؽ m+;[1cB<L/amm8Oቯ=}oFQ;o{[b 2dj7qE|cg~c| __㻾^ "0quMD7Ssz# , SyHY$?2.JjTA9Dƅ)^afX|TZMJáL%2!l{YBZ^WG"Dd\THN!ԵEo(OTRFٯ/iyrS81%d(+kdsA2KLbjRI2uӁt{ZZ<L/+6N2UUC"& Tr> !U#bPM-@(.YHҘ5 y{IKɘ ȕ.ڽ6P"I>)1K8 <ڈ@"ސFN,\(#}o,5$0IE3a1)[Iv-ٱ>aL,*0b"p1!Tj,kAthIZ7 `PZ0uy eM3\6p:*dRB{R!#4a iP %0b䌖fՄչbL٥č'47e6J6d9x_^V3-3T5, !SqhAK1ti(00t1C& kx,Tp %9ߛ25Ƅe-9:H 17%cTQ]Wqq ,//+_4 Lwޑ%E,3Na"rF mn64ML&/IH Vn۟?_+MGy*Q%O 7C@,jEpv7^#w݉hBGM9 8 \yVAnV%ǹȜ rfHJ2:br_L>D0 4H\_C|d/(y+)GEGU!6%ϓ'E 0RE_,3 *vf Qq!j SiJ#D0&8MN&߻,b౱A``Q{(J@4b(`L 坌qi{A=d:itt\F*85Aٚn3dxn Yrh*ER~uט7J \m[)2`uk֕IZAeRոr2z+h;FH;::YeM4CAU4e!9|.'dT߭đMK3Tʻ)ҋp]@8X6Je;+-"Np"JWlTڅ . F5aAo fiV)KU`oRV :jȘebLa2\e g,keg e`8U0VĴB-J596XWD>6O<41mۆf?>!m}co & ̟2Ơn粬Zk冡^ª0LPU+v'x s1'cwfIN n?/7k@ke%.Pgd<(<+sTǰg:ECH7qHYM&v\F.^y},hKKKx|OD([(7lۍkðCGQ;ܳض+3~c0Xl[ށB>x? u= @p0؄A;A|3Ko,Ǎcha2gCC@bz-e}.FIUz/Z 9&kx%J-ɌLH hrW?2ԄsCfT "h(O@C3"4DoM͈=rqDm@Q`V=NgV4-Y^> h3ąg4m7A {i:LgIs/Zs:1Nd.%Շ ϩI @6I?R$P#C,|VYч'ɒN-+rx$|ZD` p1{(0⍲6UIfczΰ = f}# fGV^Ę#%&@DvS}$|b6o%K^yV0CzAW ?[Qp0Xi1 cM` p|4Ț32xX1ӯ Sf_!!OQ[9s'Kw!E-OJi|K'&er)M3*vޫ? 6}gJ0{(\u3dW>|O xv/^!:xiK" p\rd6dhZJ`&|,aݕNV"0޷rf,PRqu >1g.7y\UX޾{3iUsn\t UUa}}ϟ 4EYC,Q6Xth6a\ʃGDXo[hw:U_sعs?~מNYo\Ӄ6v^P/ `086>\TcX=6e  ]ʀl_zZػ ,å#A?-_FO>c/Qvl`>Ƕxo`2X{css`k`׮e<}pLOjlnc<NjXL}r-&Ο>{T[|?+;v{K=?}.\mԭE'#N8t0%(X3 1i!Xq3",eAg*yEO,T2K4'izur}T ̑Bؐ($/dRR|BM5' 2!@N>m)?8Hu- :mX)c@fn]gP:XR %koD:j"%%u561D-9I"#'?%f=]Lr<ث̩}/֪viPYđ|zd>y*[NyBuՅ?lgJ@0ʳw@LSA ΪEm! <4%W~}=\ tUFC,܁V% ]cpxmƱq w4Xe . 0VOk\2^:b5'8JyiZ[Jʱ #­u_|nNv;\Y{߉h4=ExEkh6h[mt5 󋈵6lZI?L{((Ca0դBDF]ױ*zc ~-pM'}5mWr#D5<tA2Pcxyi .6z.:` | mI5hq;Kt/o=WB{Twm]Ɨ%QڈDvS0L9z0z8 v& Ei&U֨ Q W{vΑV/vر$[A/Qmظt>/S|_>)<DJ}>)S0F Pqn༔)d`u| A[3 6 ]㴘) s@3F7>rBN&\RB.jW{V¶dN fLT((&0npC EmfSwRF?a ~6kڀ.孤tV .YCE5-9~ ec<&(-n89Ād8¹sS+{vb<6HCZ(Z˱ˑa35${]&Z<QmׄIT3+֖bI r9dxiRTZ9SVBy4ffNuAmB"F^)Ot 73D=cevҕ亙*'EIjr(MWR_a TDB8|XV!G/vr Bd^8 qU5qfbʡpWpvdp*↶tZd-I-k0nuq)|onԦxpl߳FYQprtb9r0yWTL왐d)-hEԱ&J {j3|f}/еE%ƶz:gYtDMx0n/~ 60~84pOs\IKqp]zAX$F:`3胰$Zxb>a{pYڷQWu!4ձC~!4͌.,ɓ8~86ߟMc9Xg1O|Կ1)Nn~| 1z|/pS6)*6! b'ҨV5?hiUNln`Wo>:-^`~֤0Eޏ?GZ|kOKo9y I z X\Zĭ߆p01@@UPA-vx\( u h!M%%>/ǥ3g c_[G5]x8`cL@oi E@llb<mR€)mU8!npձ=2>|%$]N r9!G;HIB Tu·^E65메G]ddH `:}PT:td` Ya)tĊ;N-cM|cbL?Es\7UjbPEgݾK IDAT,):S#,S":$pP@y8(];TH29OV)$,6|(fL)UuX(5}pڿ}ۺۧtVh:r^x `sYVzVɩ)N<4yy+~4ՌP:Y*s6LGU,ǨaAVQbdk8C[1&6~|\Z ]|F~elZXy(j[X#MV8g*}f 0t^?=W J&ˆ! 8λt 񔼝υ`JC3PK_ XYYK"`C\wxQW(]֢ѣ8v(nF8p11jDLڄj|h0k 7|mTW%ۗq],~k~ ic` A]TQ8svO''q0=ӟgk(͸olnbhwk^ݵ.C`0KL&=ea@➻;hlaJo춰sNF:r m\ր|B(yFۊ!^պE!HF9EÈ,T|pyP) 6('/JitC ԧd:;d($uj&[E/FybY7aY'N24JHrV(J$"HEV??qn81 ظ{(ۃ3um2ƣ/w3P%f\Z=_W^X=knNE]U 0;pߋ+.j=v;|ETq B~4T ,a5r M.M O1f3P81K Qz>S$֡TaڴpYڃb%QZ?s>27n )1JNy֜s9llUEe}k/<3rF#E?u^TZUNUhk+|swpU>| _+k1E*m\TeV`Hm/h[j"Dv*Ĉ R?I00_CIXj?Lg/1z,.V$cg7Ibaۦ>cPQ1Ix Ir#&)}1KIZl8^J ht@Y>|e2 4=|_r_ϰ!< ^e.}kx͏|/C 8 y/eZB;fNg>o9ܳoUWr+/=ocʷ_qu?|0#ә"kD~?-V!&lLMRiu-lRSnH:MN>(Ry@ӼEI ,WX2xsId;T{DFTiyG9b zeB8k8> e$8bЎF?7*OOV5$ IE* axFiHko<0( yuս6׼<S:Ϫƃmt\NGUg)rsFUG[>&v|:PN{Ly%m%$F>Ḯ)AjDȔV2$[e ɹY>,7zN:Lܗ(\YS%f#ZO( Le͙PLѭVŅy=0~yF SSS G#€< (ˊ;v0 €9*`q8J;'WgN:Xfnl^3O?oV9,4mTTUU4sK8&l ?rQř{hLJtfțbtSY>'{5"UI7x?9{2+}ܿ+TpL1(DaHe+ ӵ2M$R&(T\Oc,{cjJF{!:$M `Pc@a݅XB5G)!Žw7 JKa.~Օ\ɗ?~=VgrT&_cw<8Ze?Z^VG9ﴗp׾2|z|E\pŜr9uoʫ板a 3m*uB ,SuxDijƪ!10ޱZ@H O߫k"γ ZK_q-D!:x傧RW6Eڭg[53Ƃ8hfOفLz+KiCc_.#"2b20R099sig9x F'-~2IJcqCKA3wH 'Uˉ ^]YJ! 2E",Fq7Ö$/nџjlR& y)vÀq 0j$Tу-*KH$$QJcؚQ&F%2jK~J|X>iwos3Ϣ /osmw%A=.WOcTSU=|}wrMG<IVc4A>N=\F_f^?xcn{`<1=4R[ą`pVX$][KR,U=Rk<亭 Kjw|NQVbMPR lXgJڌҏn`kMj|JJm =nnJ򝵞.>4\9|$!4l׫~R"FAe"zI5ɿ&^1V+ $Sv^0J91 Ihbznއjnvօó\xlqENqSPJ0BB \x||#e֊ \$ԿJVZI]Ma%\__SיiS%=UoSlHNUc.H g\nȣou?#d:9'Zqo`jmvњ^7P~69s233r]Xbnz5< @пSCAS%5B[U9'>Ex@\"ް|@-nv 4RJJ5y$BogdkҎA2I r"Wx4z:ět*oyOp4.o<$%E4[-^=D;O~oyͽ_o5?ρ]yկaۉ;))ٵD>kv/?cN9,<vFm,_^j~yAşc2:n;jo=9FRQ?+IeD%)3Ln(8Te$݄ok{5G@\}^sśv(v džM]Cϩ[ȕ,.@$OE0z]Z^rɉш9.輰h6:t5-,Z?3=C۹f8hLOOoy[MX\Ƒ#b i6,--q>͑#GW%^&pT ntR{#49|>8qƂ<:* ޙr@k\Y+\+w|<}` Ukrī=~;B55΅y 0裪 %&Z?0 u9p(ckߦonc~7{{i{v|BnN٧1=]r zt&YyK_`8bmu^C?`зu0* ,CLaɧ}> BŨ,Q(Fxu\z9p*CJʋſkΠB1]REzFA&Jĥ pV֣ccu*^H͊i!I$AQ10|d(vI'VEPP3m V4tȄ4ay$Jv|,0ӚK}=ows>~뺟O})~W~xGYxO7_1ݞh5V*{?ĿCg>VO=c'>00_gx/2s9OSJo&׾x^a:̬&*2&ʈ917r*ޝlKPzÁ\h!0c5J%}~xqÐOM M=$3P0$.}gHiwH[K&H&D/wv0QecT{>45LlyPF6)C0 ɜS cPqlQ+1ce@$-2Mek5Jm t7ʡBk2Q MEAF==D$٤v^YE6h>3Fdc a(ZC7P\:#h  r@w.SJ=ݮkK/hTrW*vu菆უ?J^za$s.S L+w#UYreqgp}1kkYlr)ˊѨlpƙ@gU2 /r2%=&&OM-N=$^h4 n(Hv6`7 *q,FFEG(a1q਼DoVA{ܔQ:.}h-V֟j 5݂@cbp >Cl}e w ;/}דH^e[DTTbYgMj,S:y ;aUY2=73j ڙ08+ Fޚ!3 L9Tm߆rǸnӚNTNe,.8v0x8 [37b]cgæU`y=[擙k&IP._1Jت3b63 7#@b}D%˪ WL[0]UY d,>|O'd6 iDT FuF)ejWC-TEDWϐQpJ >n9ukh dKQl\~/Y)k8TqY4㩠B [gg8*]|ޣkMm%L#),@h&tƣBڸB3i'k ʂU3>%xhkm\yd)F0 ;>*5֋qeGe~s|/3ۏ]l_`XsOsJST<Ų ahvh`1@iJF]em,S$;.1Dq*9.f!T%"GЊu-9bJ~EVBc;I ζanO>s'ĀC,m1TS|kA\1hJFyYg`8dcc`2z.SVlzdYJNuXXXO;B|]ihd854'ڔȎF$REQS YGDqlV歳AfENcx1Nmu|]3zTcb#16.Z-L&O%IRQ 0 &{S(HU} B?dnSot ZC8<0i5N]_ߥ֮۲/{|J |_%#cfqf\2tf,6l =ǭ'kKz5TnRb k0p}#!c+WsXYY =NO oш;,YZ^B)䔓O.l5y}߿NgPMLLpÍ7n.|K2S'')N1C88sAqIb JDPD^DEdȒڜN$Fzz$a/\MJD}ЇG ^bftgPHM)]FдB"_Y_r)Wf_T. hͯ=̄K H"Ǿ4&2TyTH/u4oih;hfZkZD)֛c!}O>%ْ Y|K $ āmՀJ-5:+`SaB |[w'1ok=0I]VZó2ZU]֩ JnJSƾލ( Bp~YcH&\"a5hwm::Z8,B_,0Tg=pt(m2yaL[ q3^%&\c| |b7<,6=)LYh/ur=K+{RI=eвJ4M[yvmqG3ٱn`h6LMN/}[yrŕWrر`A(1FAyGc\X)hh@<{^(vcѱBpo1m=sVLtt˞TU7׬[cܥRQHRJJSdIn`( (_)F|৮q* |pg?%&gwH{w{O=:2ԣϑ]0Owc49Y?:zF 1E]֩}kS2*GmZJ be1`¸t^ܩ|<kV>X^>D?`V F* vXT&gԆKwY"S 3*;v˅ іQZi!5fL IgPA>%z[ Z!6 2*`v),tF Nt=* ؅Op\} dI keޮL\Bϝ[0X: `&rw" XW+b:e ,bNˣku@SĦ:Z( 0 R$d$FG SI*o7 :p_߿P-3є$V҅cJB[%ZT pm҇4ƨMl[r\@XꃹA; Rk-ߤg # UMc/QMcF(:Qz: 2K`P^.} RaXU.!߁ 2p,4{|ENr/Iƅ=W560i3P Eq*״Pb]P0ʂH2"bRk1b 5AIU#ڴ,w= nX]uP R\鹕Ec Y8_K)i .K_zvmL]]a, yh}>;gfZ_/=s|zU<(5C[h/YnLOu  C*cOePVNH`4 Ӛa5bzj388K(QIYX妯<¶x;ʑ㻏q湧E Sd7d=Rā=^\J/ֻ#NTeA\@`w4JeQ+ j}`O:ew "KBDicH0(>T%Ez$ ^& O1ϳveZRڪ,, wQ$eU"^9b @Ĵ˨`0:8WBDPaL{T}L++T *&rJ<T4E K`^5{W!^Y c{*0 <Ƕ2[ $Hbc§]U2ȩT0T)&nLF [b@nCQU cJg#SvL%i '$Ȓ:B m=Ne|mEdΩD)C2!xy+to!LDl)#[!qW|c P79Cd_3-%3*MeW=܂/LܭxU-2AUa 8UUIQ4`b0;Qԃ1vǧWebJ$LJԯk#™+煛X0SMX[.bd @\^7ؽ{w%knƷ\ KKKa8wLOMQUU(EP)ʒD9nv>rK^5o~ҕYI88r(2l۶O=gԔ upy(X[Кhy`.1P|=VF5 #swVt|<< P8DŽsx @M/Mm 1Qtr^y;rK8FoK1/CT5Ln6*7(4 j`CEJxq7t)t"<$gFiVmp^ Ozn_gg0X34ss$~2 C-QUE12v1UϨ%V0geEAUPd p8&J9` 3FB9 aH^e EUi|~Q%RwM"vRv$B"SQu ^h,R tDg { ]b7)2H?e0#5u.X"uSB(w+ v!E^XkL7ݪPԿ 4b$* \%'̥r7^:?m}8}Yω!:X|`呼nٚ4Si$VP~A4*wA"L凨 Mr;u]vJgHeRE^DYRܾ_1NY$n[K} 8fiNI k/8B>1n8t ؅Hcl_d'T)AX{rAJBĹQ'qio6>d T0i߁&z|HUʻf6{W{Kl[8^N'yldy? @EAȖI]T,`ڣז({PWx^5mkrc* {o;]m]|@Z9@ro0977iЖzӃ)͈zDrVE5oR[xF@q<Y,gqzQq_F;ʋ`wsLO0X`?Bca6~U׶Z m۔UY+5γ#p8n33=wɧ>?<~'2["2fO}o_K/GrAv)+*AVLv&C1N`ߡc>Y B#S rJR>^,2*t} ldq!RQTԴ0ipEL7n?9v%rd@eɨ"Ϛ4Ce4 ZJ3B"RE|WJ[  w&&xx>Ξl3WY3v !zn.rkwN\ripًE)*U%T^GwcѨ)TSig}uH}x͔W=̧Uep~h&w$let(WʧdBGc7eg~qI. r nTHZ0X"M`c{D6UKatR% =^&W9Ra&v{m[$,*)}>gs2tE]9[hd9KabbjW1H%7,a3TSěl ,,&>-ISԺ?d S6r' [uKߓAu?Y-Ң, !U$b{J=^żk"Ü&j$ $|.DF:痗S4iMF:YLS!!$tZ $'lKLv!-=Gx0s>faS14H.+ bA](J>[/#u?tUKE G¨L1ٰ^%k{o$LvSL PS.b04 j IDATz\x9킳Y>|uÍ[l1j $)qd\fȖ95z]tfl AuJr]M$ts4#حShF02U/ǫF#LUq;崗^=7~'D'w9;'Eۀn5E^q ] 9x}hhH0jKeUI֢/~@1v)&N;0ncK+94_oY$IJ2[W`kˤK7'?y=]t!32EN/|37|3ojyӛLLpх\d,)K_boԙ011aI 3'3Z9'\.lτE r8)g9m`&=/4LhaB @S e2m [^>_/920 I )EΧh6'12B0YAeuc:X9翔]:jz 9e~+:g4A>ۘzvΘi8l! RLNN.;0b C;U犗\|wVU6G*_>'cp($!-FPѣ]~Ũؾ@~qeF5Tځ2j uDd( h\D:)WHጄZW%}Qz^4KkD83@J|a`C0@kI!L2FV#2w|*cc2rH FY*(mmeFYx`5{'K00mʥ94AU䈲IVvgjwUWΫ\)آS_Dibu_ J#\9:Jcsg_v?gc ƾ&QV_*0ZQ/Fb!'yW"JQi_k11tG#l>쓹((VjlCA[*ьd[7SŴ]I/4susQ DD쁩 rIҙxfY%U-VcU΢ -u#W4\CYO=*QNX8b4!55+UYL7O^*VKIÃ%% 5LRY$?+}{ɷ"UK~8Uca'c5<ꗧ[<ۭ䯌679n>HL)xll,rY_|W{h I=$ԙLT(e H]Y2\zoOvUIgMzʌ X֤R#L+ևxf'a[Hs.SO$]b0 I?\r Qt8H!F7cj|N 6ۨQnU#(Z Mh@fR/8c8%iq05 % bm??{ U+W ŲyI|(}iKUKné ZٻO`%wDyK2L7"0nX jjƞ>[^sug * %c1[C>:]) [/e7Uβzb,.+LU<.%T[M^cIfA$LIn_-4;yO0'^]|NʴQTkQs餒_5-kI̵<oJq4<&f"#/룃h_1Z 1%_z|}ow>vE99y=a768SR󜵵5RaL\SNncLͦYM;kUy"|wjԘ^H_ʇ3T-<.Jk0.*F|> :8G1KW1ǿ0 Qu%Ɔ(,Gx 2YWJUZS#Si|[c#j4hE %*4TlebjHȵ\Kql1UH5.T%LP9Ѡ((NQg|IwZ0U80(n}fѰ5J1ģj@isՂ$U%9w}.2>O?Tg&ʧW(`%36 ͦk(4<,X]jh=MǁJf'TlèA$o`q)C1#5w A)7T^B8E#Lbje kGy*K=LOT>iwڿ'N:_>m<{7y.k~w3&,+m)'Ph5,c4쁌h'A6 (Y]Z3/﹚pl?l| 6oLQ <=*%SSMs6;<xRSuNQUZl۾f9g=~ +{cJ1dYYg`vEp VW;M}9z`ዔeEY)n$Xu`{k>-_`?.$:xA :\}t{Z^FS*Ixڱ&x7Q bXs^ZRB͐]/"s!< .#!&MDb{ƴd7)Fqb=OEli9L}JI Z C1e*'5NY仵ܤ9nU qvV15J E O'4sLg:&Ʀ}1ULLO Đ$`!NKjz07>V$L$cI33/֙dgc-9}/O3L "@$HHBEzMYW+wm쒵UvIZd˶,Y) 94&73/?No̿h*7o9wt9.0w˜ш`}:$3If-7R5Fǟ):'!/4up D.atF> ut׆ ӼEs oQ˼NU?Z嵔>i@}TV"Ih/>.Шzx]HF\^c DK'Xh-!m12ͽ_y.ÛXY]B{0~J(tf7:xnTa^K~OAsl_YnuH)lu;~,3sQziӣ))|Oեvݱwgya#NsҔNk"hЍG@QWGMk[F*gz0wZ! "fJGw?CУC}{ 8(v: U*Ȳ4v8yuRb`QJI3AMF ĂjX,X8=Y7ekXij`ɨ~ /ʞ'D dr8*"QBvADF| 4DV/_#8Ӕw+.#i-.D=xe(Miu1Z)jVk7,..vɲ~?jR(:wZ Hs={v/Xmtq#ȉ˜9ug4_&ZKdk"A*D$5wpZO/N&  7OOa韈Rp $y^NB#:3E0p<M̩9:Pk!$}ghhlit`T}ضtV*+~6kqPVx(iRXfSՕUjRBC)M^gqvr]۹x<~.+Klݵ2q"AL$c)lݳy: 8by~(ذm#nj:[vm(i5:Wٸc3r":Ӭ,,20dݖ \:uHF[s8Q$ikw RQ+ fi y.Zn~z s;bڷtfy=tNQ+JIL_g&I85pA7ʩ"l$]ήXchzӤs+mÉf[U撠j5Ւbveg)9Ї|iy뭷R R Ơ(޳7E?(\_Le9RF4|GxW,ID [-7,J}:^ &FWʫK/q޽<M N3-O4MQr%V=hTbjȺ *Rǚ8DR.҃ (Z!Tjj'GV-Xņq( I}rum&;!Tz(61߶;XYJkZӧ8kN\tX=*Y$ FF@B۬o6t2TFaݺ[lR[ XgT2kl "Yh@Jsct|~Sr9vJRJ@eRVWP*ўU̝aẅ́II}qlPܫz:p qVfHfIidhtnfs5ڛEa(Ptq$1'{9Qg;#On:>@#/o< ǖ;RrcV;G#/cͧ~CUZ˼ͧY_T-̦][(5̜}{4"O3(˫KcnOKO>Mo/ރJ4{/o>47V? I<ՃS=[.Š=΋P$4K`. h2\`_]g^ҝUq6J?_ kR)b*W:}ם  2d"q ]8`ɻ'{?'?y\fjzӧOoCi4diJ^gÆ t?#~{9z{?)_K4MVTD)r$I|$QH&LpdPƺdC]3Z.KFIPObb[IShI{hC$෾sxoRT7Вj@I/믘j;*JjMTa:߇fK47M[n&Ҝ>e=pnnt˃uW8?3ǔRLS* %m\U!(;bdt׳o/.)VmڭY֧Yk§Jm[``P^߼8+%IN3:SgB+hEй^&p5 !@uS IDATE X;*/0: Z*E9"aF,ފ:p2T5!BJ12]kV[K9h+) h<*rѹ)7Z_4 iHK{nj7PE]Af7[u>~cGJ+gq'U]R=ɺRyʨMEo yV1|B? LaG*C? y6Ņ誧\T]k0wcQA^CwgQG][+oBш5HP#7$tp{Qd u;9[PNAH!yFe 8Ȇ!zo C{P(2JYawk:rgVZ܍WWdZ:! :Kju@.KT=(g@k?RuV4NY e~9E/]Z2}?/e,W_m~WN$cJ=$ ~?cxݨ7sdso=O8>n͞[q%JstW2Q,?E^yE~3\g~Gn_bx8W9+ẃ#*x7xOry~UOygɲRdhy(_ <>" O}I>ϳCo.?'w!_~L3u=#[ֱ<};@">v*>_o?vg|'>Ϧo>} VWZ*$Y}ESMU $-k'@!%]k͍rZr"J/76+`KUYIq$!f&) y(w'B| Oui._`9H/~Go2uNceSٕ\JJ,,.2diiO,Nv*_+?K$*MΏ},K9RJ* 7|3{vf֭ӔEd_#Ԗ2")0ڬZTTi!sceW(.:@紥8(6x6y*?߂W@Fn5aQB%4nHbjI\2.-Pn)r"7PtO2Ь7OO36y6'=#TKqʼn<穀5ˑh?}G3V[nݔQ*2C#Cp͑)=VVV3R4CjA ORKvl5ʡTQ3449}4zi}?ts4wTyJygY0 0z'evszZ80!avvл5d>9lYXt箘Ez*ⅡfIXx}6r9vyJDV2}Ȩ\$A2C>/Pw!MTk5v+G_z4˨Ԫ,-@YWJt[AcVmr%$ Qd51j 47O^#֨mwh%j$)ZeT9a a0@(zZRsM 'ϔ5͖1]Ϳ{(-̩LFAT)6,d>}eAu!}JTCqصbs,}C݄pKxDPl],1h]N=Ӣu.F~e?SkBHҙu ̣\ijRFsr6#$q`18r} Umt4_q7@8 {ݰJ0U_5:v⣋5n"6@PTe3Ig~`G8Fl|?G)SҦ\w`7q.q'ص/24U*l^Ǻ 8 }ʉ$?]8Z|ulf Z_b}ՓC[W`Z䀿g\8D [B #R2tE\J=ǘ8"JoY~M.V}0ZK)/qvDNs:|;s u,=Ewh3r)6mq342Ķ4%lټYv)% sLOO#``I^#2VWWI[/>RL)Ic M^+Mؼ Rǂ(.j;k/125ƇLO'}ZzGiHзh-l($4/݇yov۽[67ԾuCccQI69e)z 7عsz s()~*NtFN[$.%lۺL3($}!ZP7ng'yy'S.gNgrN!*WHڙK]T!6SeNe Et?ʠ"2ZEWz#_kHr$Q@6ш\ȝ .#:&p &+r*s?+yy`"#3$7!hrϠ6xB]n^3)Y]^O_gL]]ۉb˞ < CZe͑!J ^{" laW(t2vlg Y;>Lj%^?h@wa|X\;1)ĵ*ʞ3ALA6ɍerq\Ye4Q$ xuUAwfJ0B`A'pkGTpt&h.L념'>T: 3jixgJ _| LM`CN6Qh y Y}`OlH!XC"$3*JSjz{ rͩnI&2bϼc"<"/:|gxm: $3nV"nVFG_{ 2ă?_(*8$+T:_x{ 7Mʵmr@o :2@ ^z9JQ cʜ:zc<8\.S4ye~f[IqDDFSUfZQtXYd$*%̒^Gm ͡!xcE ݣ=V]<3{-۔4FXZY.F~/ܦڬCEVo7WNtZ)1HMl:}m^fw\;歰q1?Hk> ':1}kn.n8XW6qiMpJheJ(@ʕQ"όeӟ$I6uB*2e `uuFE6nXOZVRՈ{hE)K Yh)JSJ!#4zQh"i/^R/1dKAlŜ"A`ѤhRδ&Ӽ@6L>Ji85*e匧$"RZ]E/Qk4HbM&#$Vx3) E@.K֬.a:n:e5y@J=}]?ǧv—{"/޳Vk?TL j5i6Q8p+++YF?Mu;}EdJSIb~l ǀ:68'Y\\Gb|b* 2TD֩R`wSk h;&S4Z3kX@/-!ḿ"-: 2ߏ^ YY s+r7C  {ar0 ދ]@aH O!u=LgIz˟S*ek(\hWʂL,KS( m9NEZ壟$Qȥ3gQiFkyn3ٲk'v㯽1nG_z3'mi6-vSnQپwJ{{lfp|!o"oM;O~fm䑢A-2g! s1-q!!g ׮.U/hrAƼF{mP !57v?DF8*ksZQKBʇBK3Wds2o`ϋr4СO::f*0H c-rH uzZa%*wRҾ JdJϠ$"-J!DAD Yu4d&v5TD8^Α"]Pp@(>EjswR2qSt]\4K)+{=Ugru3_<w_|G9 gϳ~?D Q O3.w?x[xFbWw&JQy;߿ OCƪv\*Қy.]"4{~Wmѣ4*xoP%FajTNyx/Q]OL|觿ϑ4ihPmN}5P.=5:uN+ E>M&B[5MPb "J7RL$zfu9q0?ã#T`9ZOŐ`Sf'‹f&?D\.3<^ !#}1D MHDP=biJR)^hac)6 D/3@AIh~GY~# ؈c؞p ,J2i>oܺo?~gh"6+^<:rŢ]ިmVV[V82LR%@$Rk,C}Z];wAab R1_Q +]Z]$&cqD13ea@gH RF2Mୣ4eR`28QYNZȂM"֪T0V犲h$DB,VXW"RD8=)$s%m5k^#f5&#:sTQ[ټ!$$'N.;Sn[J)4G\t!;j"JbnV˺IzEy>y7> LgϱnFnYElٳ)omOsyGG;tV[tU뙛YFR'uap`^ fJ+8ziuQU@3Y:(ր2ˆŞ"[ |yz'm/aúe ymq2kk2h J5{tT`18=]njmHP ׭8Mh:Z>}c "`ð [O0Dļ+k3GL/Lqh:StTK| L-pYs_YG{yBolxWʙK<Ǐ2cM9QL_fyn{&f/ λ&\tXl@zFZ\Qsc0]y59,~Oկ0eǿ0_̱494ǏCR5`o> ;nY~ Ml; '^;Jsd r<ӌY~So A;qε6AE}C3- !HtD3\k,דEqNRs'9щHUl힥CxMzJ2hJS-3@^e1Fh A˿˿a7MJGi/a!o~v)1qj΢h'Q1[Y]EIƀTJXZ^BIE,,,099(~zJ0O>Q148ĥK\|m۶yM+6jrB*SS,ϰqdjz)&>RdJ!xںsFڔ2APѿg̝}r-\24LgT ̨UK+e$/bq" )$ךv[34Gh! "ST_lۺv]J7^|]na:>wl=u'y!z+$e[oc-q!N7^}-;2;D`li@SC\6)LpPc# Bi:>wh,65.(L"3Kk/mm0iOѾpqD..{h _!0I~:-iSvOXdZƁƱP@S) mGy6O*"e$I{Y*b,S4M׍ңOrY6 <݇ٶg*Mz3'[-~V?A\{o>9tW{߼2'N226F`(vnhg4G‰6Orq".Jf,WE"ȲʽHs?5ho kMAWB`آ-Jg,Ճ]hC]K8 KJQ@;=ZKJ'0z!~3E"<)*h{R*l SS=hc+52kE4ݧ~fR։; "IfK M-JAz$"uiŤBRr GPx n"83*_poءQm( 0"8'2CI߀2;Ki훸|2UNg$ Uʩ㧙<9A5$1_}J1wm#f Ng;6X[`$œ)TۺK'Σ`M^?{{/E9(aeeq8}Mt)bmYƕ3D\:u'/ U昿tSo>>t{]&N eGs z@3}MJ;6M\=sR3yf7ҩ xKNۙV֥~0 g5B$C#LpFڟ)4%G7W->*iD:@Iʑ D:<nDWHA-L|j(/R,z`!2CuE.^@e6)IsGH3ݧVf+eNcHJV[- jSӜ;w[0::˯BkMnb9He8++ ##Cäi cc4 6)% v6g$%* Nu )3.P+EıBmȭCdFg̉SlĂ$ӊ ?;OSVѢ@sGDhD$(I D"QzERTYY08T{hA3RLOAO+DRKRۚ,έV>vd~Д[DFAS(ΣV\:"0r 3۷qܤֿ.l"GZeʛ$ IDATQFLK?5(t;]$M~kWۃ8Y~ǿ7҃ar*^sYW(pTˢgXf苤ȋh%tfI6%CH+Z !/MBCt=YST@CJ!gob@㥣3^w2İcpSL-4(@"κ>rYv!.Zz1CgT:wNu.vBmAj pN.n,3ya7 9>W]`Pof+ij,134EEkʼn7e8}˗7,Pm4XcJ).>Iӹ>λj BY= J!t¬,JtÍy!(xB7XC}r T>wu&CNGn.,j(B׬YhPGIMXiS|O)E${.{.EohL싣= >ṱ9\_&ߋR˱vT@rkZszTomRM9k$đY7m@&t!+2զy6M4 &SfvDά@oiFg-J2\Q=92$\FX h<^yyTiZF\aeng(1ՊuO}22Ai{ JbWx(I)!A2ż BM:rs<(3<ԫA5Pd* BZ+K U@MENıNd󭵍Mk!УAkjT#*_Y-T/fGpfKe'xŅ9FFFq,,,pӡmTP)C݌%rfަ^Zk$I8s(ػ\ +++>s736>Fej5:6;wdjjAn6ɲ;u~>{z[t4q%o&h>MSdTbn "c#6˺IUasmG9G*ME C댡FXH1lfT+1C5fgWY^lQH^7H'T$I +uM|Td y>rBUhMe,--{>/3}um 6ܸ7c|/eyQ䭰22zZ®YmwhwNvQiR(͟iEie-Hb!EDf2{M7}r=jdbMn] t`A^Tʚ imMK뀈G2e( Rb>͡'LI|=#umF5ZdHqcAŲk RkWuά Ma7W`j('ȊINqNn!Rǘ&j:Fh!B$n'(U+@z=NsZN%Nb&'&E1;֚^KQʱ_c`hɋL> nxRLݦVopPh\+)u{Tk5.BZ#&_̽@G EE(RzMTk ":\?i bͽixʼExݷ7QDhr(344&Cz4\,Y|D/h kwrQgvyW_gx¯YϕBZ%3S \L kQ t)n].|/,*i{Tup})ij-UN+`kNW\QHARo< s3 4 k5a̠:²phA5۽&'S)IĦ&ʍBF1BYс1IJ qyaz*}9R$f|x0֪qaHy4ޑёjJm]ncF֍k&de97:$~I4F@ݾ}U!93@Fn CC,P: Å`we)[L3 36LӐيGhBt."o: d*BXP^rRGu0PCNVoSsD++w#R"d>LV}PkS,,-r]w/pIV[rwUOxErF@G@?s IRY'1QT'J;.j)U ֪ЦQG)EZEUbYuАJ^ç\z ]Z1Epi5(¹LLE@{S#zga"G273NrȣV2sc6!ɳͳ6^g3eQD8OKÌ0F{ aEn._ TTF W.5py 5~~-*}祢%Ž".{mS7Zʹ_X93G%.Ih]p [Ⱬ":CwK0̼zPCFHM.ʹ""#Ok)8e 0l'kqaU^zI-\Wy|f0'Ӟ}=͚NXߺGukdk \kX^kjs gkq`U›NXګ.-R ;5{:=>g~}*ƭX) Ҝ:-Tjj,4YҒʝ'z3TP/%`Û{)E,e' R曶  ~9s^"P>AYG;t|RRĹs_%,T*yfWVX^^ƽ{&bxW8|0Ü8q3gϲ~@hq&&&&ؿ*QY)jj:e(z+U? A- (kF!v(9 ͤs|ѷ'ys܃{VعT>J DR:>$^Xb2NKfif1:MUIAJq4iflHJD%ty>wݶߡC3334Mn#vk8~,'^{zD$z^Nj: KK(mVj whP`41QJiMnx<8~ɫd2ِ=CT^H ccu/0Hl =R#hj\ɔuްCa΀A;m,Ma) gD+8L j0s]S$DQd<ծͿ($A*oT hSJ@!1Bnpac3mHFʵw*VAxd6QA#7xQAB qp͵ZɀR?zod*SӁQHP)4Bi8Z񺡄,ҭRBnk SUm`Puks-UT #iU 9 6}bE`!kx8@4"4ZA4u"suېb3kDQ׫Bm8l<̾('5YᶈG_7 詈H!98B ͙9ei*Lj$$t=g@"6Aɻ׿+|/{ogƒk uFoi4~0%.O뽂<4GPBZ]E_sE4 4k-E5%q 3x7Ynw`PƙT!y5!2:Rp6xy5f/i&Hg]!27J3&kyCNǣw@02TjH X7|vv -6RװAt]T%gMi;MTedl0$ 0"HqZ-_fd3qûE(㠧%JK_(%J 066*Ft'=hdYV8CC5BtM_pK(X^]+3o38>/@#N#K ՚5aL!׳Ъ=zACk^]ntۇuN I# |z'[e-o\Ï"AxBf9d(˞FԄ3exR@, Dhɹ4Ts1T+v;GXhWu9lIVeuZ%!2C511sid6N:t:]nAS8v;,3ͤDD^GRaff6n|!Nh066Nc||[-6#$,.- I4NNG4\M,ɴ}`T9^;z\a\2;mu'[U{NoLQ$%Kn%Yró#r$24|? $HH6'NG#dk )"%R$ϴUjվRn " Hw~lp/ٳxoW NV\X`4蜇ZƂapܠm .]y؆zFO&xo0a‚=N`L; >SFitU}KJ^!!sQG'_|dM+a^awmx{q ֫.&=0fxN'Ǟ&_!`^a6 7ߺC=#b1mU"cJ1Ip[2\jM{u5 dII("DhRsHB.Ift&oJ/R*Ϭ}"%v5-2{]ЫU*$QKc%9T~CG>1C}3DٷcX,_5w-2xT9V!&2 [iـ 8hb0+_HK7sabTRCNT91wa,WL!DLIވ.\a@My,c&y#KCP,WOq9%,OyX Rb9S*^|3żjX$׉EI0yأJ)3%է&y#PukoR_oaqgpyjh(]vX!SR#}Le8slJR*szѰJE?:o+;WW: .`:b,mrFU˒U[0ZU?䅝ڸ!H#u`ԑZ: Z:.QbyCu~ɤlb/O/OKtg'fm{𦳹m7Tt (g,lF*̩sZoO~{%f Mxj?}0bxՖTpGE;ias>'aj>VeLYH4]ef#驜^1=@js6pnYb3Otx3]$=."ڵ}=ƵV[\ٙh74H[`p؟L1k@6'z}17qޠ1M#O|\ x[|O }"Ou0n9%)uc6ן6^z9&y 8[.cggx No:,gAhжuڇӴt:}X0Jc_](3D!Rnr؋Q&oάNo$NzDI\!Q@*֞fog\nx,\R7). ׁ|:;>8 &y*)3RW&6"o8At$ ,0E{KHI9 (z)Ӧ98?M]e0{8F4w74܇ꪒSV)1Np3S NQq"C9#.TYf"-G !<jF)DE,%^8PJ^qs=pg1T GH+wRO7Nhm]G[&Jxk:E3M XלdZs@>VcBiCI9(9R)A}M!]ea| Kpqa5iCBc9-ErP1ԞblհZp1S-u@-gz l5Oo,:&H \ 岸U=2 d 崼\IEgtP>|=̳/`Zcq*t֭C? }!x>CnXq{ѴmflZ' s^ ˳PիWsH37~-\Si3*pҍަ? IDAT_|)p 0ۯG?VCN80sIYq^ajstÎi1ۀ@6  M-(xB|u7OOw1̜Kcj.dZ(RR96>"'ػ/9G}sX6Xobbb@ 2Y]9c#x6zϾ|1O8Trǽ5 21)Pd\}e|/^;LѶG .{6ɐf agbkq<;s6{0!Q1$qN ؟O#f.)&ǠnHr},k_+̍&QhA^C܃c]cMcq/ֹ]?&F)`}]9!} Kkxg~3?Ut,=L MKcSd%6'tq̢$$L m#!F2q faT7 ϳWDo Y CAL^)- c 9lƅ\={ n(`t\w\s>&z2HTsaT[-穦# U#*PByj6c =Va%>=D;xg#FCTKOtpe+\|"k,|\ӹ4Wk ."9tEFGkǑFRRbu ՅJ*шE/״ 7W 1T Z6y!F&vx}[FП#;D͹# ` %IV@ LcaEet-A,eQtby$֤s|aytM#(R@Tjm:3YaDyi~sׇ֔!QdP1ed r1y'1C= (k_Iy0!Z+̀Ώ>u xZW,{қJzRWD JoNyi(~D3_[Xa*yd8]w߅Zk^(!M7fIC@'ކt:åK3Lg1z}ZV XXt}l6O`m|ڑFV #/;88\4[kWq0 ;Y]J 6>~nۮC37X&o⎊)X'у7g?F.0 YN_k< 爐&JiNOpU"|0o-X,6pn@wX,ΰ=\ud  ;o9060[{_|[lw EjDI4I7/sSIC$ ϠhBC fdik %6LQICl)7) S'gd}2#IjaDHL%$$ R.EUeB!ѴB %@%S}=ֶQ-$yxԾ4ꐛ=sZH1(,3 g%738yAV6ڬEP)yHt#mg?)݌)4|58USt%XH<"o9%\iaJu=EA@8J # q#>D72.Ϣg'Kyq  Ìc0UqYR+_jCo.:RSTpA(~%%rnJC{L%^Hq͇)/| ;D18VuTf\w‘aVN9x۫xD|^t.*YoC9Z-:ITR*q80:g3F)En1N=pc6FIE Sx$8UތVR(h9;7`=u^y{GbO+<\*$ʕ|Y"/l&Ki[o)郾ཋæ5FӴ9Zƈ!KPyOg;xK^R.l oewB/)E'+<-ﴘB;,:L1_[(x 8]| 3Ű^E8 e12B4 [|oq_%U" O^ }ޥ4@p]wރ=<#Qb|t8=;æ30`Ld!CX|􉏼klؽ߀Uġ1qÇ,۔DcR)exu4 4HUa=ef@<7a ʐkIf5pޗ61jmlT+CF*S5JٮHKr*}i 8oVB|JR[)~.1:{8(٨OD:9RJI2jpKDdE@d4,簤Yj S, | ~LDQқ(ysb(bc! f 9By9#ɞyHtb60pimNW5&&ܲ ؉ 6" }(?X93`?L [e+j@;(UI\tp>΢af 64QBoRnUqi%.*~LevH3sw|A|p#^}&BY<)+?iUo 딠*~:]T8\|x8(F/Q[||?@^SV2BPsi'ړQVd),˛l]N2 4` W=Jc`lj't;T`X^ cVRdZ:FJVݑܓ{oo}d 'ہzo/b-b2xL,[&c#6MyJmᜃs.y՘sWo|/ZIra.~$qG0`ϑ `wgo~U|{8hg;Ww6N[xĘOsZ\zh20îi3"tLh9BOM O=>%ڲbJ9~{gS8OaC~8| wu/8<:ºXtCc:!Pi6 ׯ]^{s$>1'@Hv(ziDS.|7&mؐnP̦\T`HMa*1yP CDqq#I u vF9hw0\TFDzbOhL HTŒw#1U`$!]&s JU1w^<#1NY%B%I9G{ MB5W cҀn%4J1C\Tku#J^)ŐC$˵&6KD9I%3 V6˙NbekOpHDluB[߀!\$|vBHT갖.zʯQ[T=)V s`UYLJJ$ye׹rKʼkӀ֞2Z;Ȫ0B B0v155a+3tpqm;ƪS 8MU592ok%8l ,h]}\}Fɫ[VqH0챬XXfGTI]rM_a )QeGUp jr*ׂUqǬ׳F.Z֝Cd< fߖ{-kZ&x<=m))+('] *`MYJzp^`{=Wz.Ve< dp|xxCnA;{{>-;#oM"i"'}⼋[4UKYy/m'8:|`ke&&mfTA\ ѯ|ۯdL 6 Wvq.bld!z&` g7O`v'w-ڶAO@gK9ƙ a2M|[GЇnc܋_WlJIBʣm33f|[׾W8o@hSl6=c؝O^%g'"@֢m-vwpgMm`<ǟx?cHJ¦^.Mg(ASe'Xx4؇u]mRܞ|In-[ȐX&u;ϱ/PE c},RY*#MIAyI Z TX=#3c@ G.WTH]D.-́0dr}vЭ%13sW=|#HJK\΀Sa6l‚\ǡB$ė(LJy71`"{ɔA$Va>>lN$i)tPæJ)B*(ASbDŽLˀ\i$t܇QjVA9W{fClk89Lp>JJ,*^XP}IH.??*GIursIlN;&Y13HPĜg&&`bJBʻ z_3(^2ڣ{^fɕԳXSgn/5Q j|Q1S@_lJa^ On^?\.:>#%ʈ@Dѹ`T1~h.1 9`"XĖS uQydWR)"EM!vP#x{kg:Lس'/v,S_jyq%J9G87{J}N;/Q2#W_n}٬s}6]G2β|RL7 pawl%Qj s.]e/0|`pITb9{{T'9^`l4BaY9ξlNoboobͺ} }4o6`Zuxf)&D@QbI&lGLC/akh@>QT __3XNpDXmVqi~>\-X.cAާei$5x#KJ;X&=| ^>OO$~m8cZ`RL瘃ixF3t]?)In&5ȍ3D9dH\웤WR}bԇk2PEc3<`@fiE2bauu}| H1lXydT fTTrGȟu;xtLh(k\Ttp$WDi,)G 8sFLT 00| tg}0yodr7ˤY(-l۶ u74MrƣJX8wPC?68y׮]g(r9.MCp>`{4&n= `ywj+Ǹuap|}w^t2nSv<1Z0cY!v34 ~bZgڒrC!5Ɛ)]VICzo0-Fxmi;bDIRcuX3XN`&. FPo5uWYS[F[!k+TŇ cH|Tf.P\Ki+sp5U2ZbK?5)V!nP* Js.O\μri,;u7D~q Ʃ7lMw>bCo"KTqwxD~[*8nS+uL![,^PTi%YJ}x"s|^G}(1M!Wli* 试Ên`ӲQ>y.k-% %ȻA MC2J2\k >`9S(5İ:矿ŀ?40XDf5v︊@[FbMҮG&[yCOΥ*K3QAxƛco\aÐB`,+tC^o k=Aŝwގwt@}Clݗ^?ã7bx_)J|)́0kɧ MTݤK<. dE"r t 5h (RГߵ\Qǝb{xPdt۟ P%Ogm\/9~(gcVыjX=.=˭m9\yL/ ዙժ#pϱb/~؄u-#]wo>jt2zVzmט1$͜4s0nvK~>{q7a&m9Q,VoerK%1%[?~Y,L'{|rG~']N h6_\$iJ^R6?)P)̟^%VǘL&0aL iHD`9zyk0LYo8w^(0y`M' &zg yм &+SlEb!4: ̰`SɆlk`ZJDq0KG$Qor@$lAVѓB ' Ăr#eHs1v~Z1IP 9g18Yu"jYȂxQ(D:szC5 C9SR|JJVtL;sc\&Z8˺1&j8X@p0({ <"}Uڥ\9}8;:=܏ ?Qfx تA J8֯pʕb&5{M ~W9wkU)c;pfpPn:sw◅yi Ҷ{;N0)F_C|VggɌ *?CFτ ـPX*V~;S &/0=aHRT˝&O^^}5l6CޒU@8<:ŧ>p%, H_/cY_{}af[uuLwM1s I+ ]@ <a aq갳3[7 '΃ƙXHɓi$ݍFH )}*X#$y5Ρ:"w$;1kL FOGtT#z#KQt%Ka_tm@2Px1Qy@TϕnΟ  uzFwgaȞΐp3^dh`ν,^~hM%Ѡ xT!<~0>ARh$04;Lȡez:QQ1c8q0ҕ+p۠MN8T]^>un65*q٪$R@ >6s"ocKt|ݨ4=pÆ0akkS:t:)v/dWmA$z'@c% pVK3kc |#/|v~&JuMTM`/⭷t$h81cB4'l$$ڦD- լ^9"ՀQ{㬵M[7^p!z4lm#0ZChĠ<{lz? -":M,^z>wF= ?yYLfXYѴXl捷I tX{8Pڨ;2DpujiHI_?|M}X4Dr& } ֓׮\+W},h<0mmض _ /mC6 IĤ V zFDJLB\$T8֙P,"tCB6}=] (ؔc jD-&B  o!mI'L?s x a+s|8d3:#g3=c*&R޹ɔwAu5a\e9U1v:HycG"3 0(4|<O|S9/(1v0T/.Ie,ƤG,HE75plK1˦b̡TkH?& xT*F5蒽A5R x ۂ- 䤙|F | (ȂR)]da \H] ΆxMlHK!2A߭{րYk%[eRAR } |ϿG?1+|7\\mWɬ4EQtJ _kwb^d Ŵ9Z'M=D 9^fϽv?oj[N$W=y_t-?eL3" `XkxpvvappX-W] {ئC#9y%0i'5 جv2 (kՃL5i!). 1%YQ<+˫Icʠ),ʵK(߉ R i rxH>+`x2=' (F2}%P2h7dYHDY/RgR@I~JࢀB'{Vg1_U^7+)d!n :T/kYP 1ʟifV&+P)Pd*<9bkEhRj|JKyK)L`%e@- ԫ(xIGHƎYPU X|_Z:('\W/3^~RQP壜jBr(--d}|40pL8^FV&' ŞyL $k&z.׋->~M1Ow,O6e1vڿ\x$cjHo} @h#EJc  [Kj;!lޣQs뼛bf T,5є4U(\/ T5J׮Mۂ +_S?֫cў'Y lVi)k=nKmL'L y©Qa! >mLu?3dJoo,3`TBw-}?_.޺y 2ƁRC" ~ k)gV~:Z ƲDat`N_"sU*4uH&̌mqrtcf/tba '_ܔn6Vށ=@0m <Ť1iBɪzѭ50 }z;Wn|W`:Bv ay'v>fF;` f+@8]4TV?)(Rto ^xEf`s9 }vI;w^89=w6CԾ;Z. %1x6O<#94q< _~}=L/.`\8BԢbeCGf/013^4 gʰk,V^,D̯5N!7Q2HDi%r\|^KyCK*ֲ4!2k-c/K#pЈ 'Iā @gP]wㅊ$$j5%"\mPq£$ ;R**4 %ՀN^: `6wJӔDKA<&3H>%L*3w!*8T,夒ث2E-׻P|+=U8k'͔V`$Z j&S}/+PK<@PĐ2piI}*?eT/2IY 4F'Z4;}&&,\:cP37+RcpyQm5=f9=0M;SycU%Xf^k39ys8n Uwg^D T.?rc.Vp_]6pG4K/Bo1Ɠ4JEp)T8]uÈhd .vׯ߅|RНC AAb-\41. {v "ؔoTM̬ >JE:!%O=4Np`]bp5|Gs0J# O2Nͮ.G2k'`p,70q*nwR, t=g,Gsl7a0b&m+C8[8]x lN.1%` -gpd9`@s'-.~I٤govGSpCZ}Q,M_0202n<ȣh aXwq''.v>`r^C?#9-Q. 88pzxW=flP*Tf)+d{P{ N5$ȑnT 6JȔ&zؑIZ8/(\,Z~Ma˂b܅Oťݖ:{u CGtaCHL`Ca~pz{1圁b@\(u[K-d%#7 +aOZp+4C%ǐA1p$`xLzJjaz\"E8W<>rk7(U* fk ^XCʹs 'Jj-ۈƑBdDtX%hˀIl}G=: kae٢y&(tjsպ L8 [ʔǐ,)0S0zt1ω%*)+N,lȀS[jrH7Zex >sx`Za6q7z[xGq`TN !xCP;yǯJ0Rl|qf gk/mZ }ڦŦ88: =0hNɴvl pX cpp`p5 g`hZ ̘ρv:p0sڃ=v5sB+1`8;9_r-+*_縎C˶m0 ~mSG;0=K{ۙGbC[8"x `=xʇABVx IDAT=N`y!lcM8u!FPl@rvI q2\Lfi!ZrñD{bxb4vE~B*!L2E(K2&^lA*KyB%q+/BދB3w!owy UIɫƚ$LIx"~,U-¦X-K|%0'a[59?::}=큅Hc_>Fis}*EgRblc_VaRq%.|@ʪ"蘝 ^kޚĭ*8΂ŧ/mp{3gcHcƜjxO0z&|V@` 'Ke| }32t >oa1˿c]חMbjYQJ)ߣ<-A-egУ}Mc^.ptraSI o,| v0Sz`؁`>`Oxǭbpew  ;6`4,}%,︌ 0LE\i;k7Oawa݉&ѫfb?Sb<¯4~zu6>'j7`^/ ol PeI0`owwyv7~3cp3a~3 ?;gf\N|3?*~w^iQJLSڗ3帙$VGxBCJf8EO ,QL!  b3B,gy,+ҿF6G-%Ւ,-S7,ICx JPJYg֌#x,%e|nYB%r̖=(Xľ4"AQ7CaӋ`S@ !(Q[ !Na#˝k\ILVbJग़,;FHPfCZRgA9O:sNJ!o;WaKq|{k^'m!1UFPhX?JʼnBha1(e$)Jm9PIWC&M.Z" -!:ez RIBd}NPazEAOiIE.$Ml|&6CbnM5ւef,p 3RT,eE ɴKa,:*x -dHޙxaMZ (eUICW1s!/Hq KUٞ ҍӉC,:j+ưQ͖ő@$*#GsBtXK D7:iZ2orj |-Vm +Vl1Y.9T]L 3?o?n޼7:~Vջ=3XHp IDpeZ# ->}rKd;q(E#Y-[EAR\ ܱ  0koKս7Tu|!sw?, =pwu'x~J_dR` ca1hFʏ?QnC._ƆPrP ŠP Jtڰ;nk3/S+{JJⵍ nOxW.h9 U4Qerle~1S+Y_;e~if5v{$'};o<Oj`Y&vd>}e(֓O7aJ1^-'oW*FetZ?4Rό/VAmfP}ϛ~`wClK~ʪ9 Nc\Q 2?K(@QITm|<%}*G( ۍ*st8&RJs3p(!6k=hζuNeYrMo636>﹞dSC}~Nt79n^gcde`="x1f7d1R)ԉ#?#[phIr·>eJ1 ԨZ3jgP~(R(|A=bpaid_ 9$1JUWlcph0+0u!ngwo?EYf6C\b"xPSk(2E1(>A 0?3~xyv35}PR׎~Y`l0΃",AɠՖє/5w^=A .+ YC?tͰ4RUc:tөaC4R9tTP;Cΰ\Fy;,%~.:0EqsS TNQ+'o9ɠS7qJ5dYXǩ&H/؛I 5*E[E~_z:p[E2F CG7iZ%!8v-|V$,ӸQM&IڝИuli k$+"XVI+7nYIYBLUP+|'Q3;!KU .sN9%M?!}RHOg׆ Ĵ9VJl6WS9!*='3-o?P_"·^)Mu%v*oIE ]]KA~3AB؄`:G[]a.?IHA8M/ ,D< OUIP:x縔ZpL'ٲ#tD0IڬcXTV?/:4ұxA:*T,wBc} s=EcJŌ¾cpIe>TUKaZ a0t AZPuaY,"{bSdKER?*Ng;k;J|lmlsyK2P@&Z'EQ?1CM&mmS% pF(x}K`لJ. (ݨ4H1Ur"TEBک@/&.+ dz6Ae9Tva&G+KH}"ySOk΅A/N}Yv@يYmdvE86)Q6%y:!dhLCo 9n ] (AElLNͅ" 祰:Jsc ^Pˬ6%dxq(hUUBl:l%.u㥠J-{d>isCǮ>יt JF3  [|둯2̱v8\EQư̓ !i3aP Z?c~"鄝M#kELDXKs2=Q;wb*#4AkF;,- e쩃0xCCR?y-vJX\pH]QRٕ+dW-! PSX;ȣ:w*й#3֓[P=f)UYb-GV^IBwIӹnCWp!%Z ʒ͏gqywA}F1cFci #o_l7҆;(Vcj!t"fV~Z2ܽ_o_;K }\uwuGZdC0!ASP2Q] iksQq|LodEj`(fPä6Oh\BWP*%c؛P Lfl썹z}n\܅13Ǯ3jpE;Vh.m`&p*,r\;jq|S]Ϗ? 2U,NA͔ǹ?/O3WjTQ&3X\\1VWָqwk_cowi Wy7!]]PwkyiN\}*?!g困l(.P8K}h pC<VC↮"eSYڼ bUJJVId!N*(& l /MkBNFÆ4KeÐG$DD+Ȗ$d_<~!}^Ĝ5})p׾|#awcp-|S6Ԏ{vVO}I=g&g{?/><#cqeo}.lc}䇸ʫ<Ǚͦ S|_dkT>{9#Ɠ1rr/?;䏸p,·~`5f^E޶(E8e9w':&nl9иP|[`d IqNC< I maA'?Eoe/+lL% #Q6Yß7N-'2 @iP6H:%_}QFP*3ܵJۘ-qs][R$T+!;-2!r.yq]P(&^͠ߣi UQAV {R1FMw9yz='|d%0xr-!TИX,T?R+SQ]{ *H(#=qh/LlCmߏu2dͤWK\81 o^z42SXDPj/ ~l}{v9 mɱ  IDAT`+rװRwad-G*Q/! &3j!OC[e 0.AhT.xOp ׳.$B"*>6y2\QLA#M`~3%ayP(5(g8ŴzmedPzQ'4u~xq͉c f} 1=UЫ?(^pačo=wવVW\wU|FùMukX.cu_y[Yϗ)7ko`0r71Tv 5ao2(4M]TZݠ0yӎ{aA( cQa2fqysY,GCqiCO_,NI:I`湺r4:,zM79 @ (a{2;l\t4~8|0G?69RDIAҶ$K.RbkyWCË3Ʀ͡N6lQRY(+*dR'+%<9ѓO=|EQcwa1<1~>^F_ ?AF{,G>Gq11^>KcV|hw*_g/rmwS'_gn{8@~-ev1u;BJKc:b~?u"j]TfyDCKڌTQ*do{B'ˍ p^j 6 fT,Vł.6TE'!Dΰ} ;yϟ(םwG_%UWuXpAA Us!;RFNKӮ4Z$T.alOx4>̚;DBji'j`kxa?b2ڣ%4{@BO<{m̌הksOdP ϺF/>He/ zawܪQ⺊Tq8yo 6(u: 0:'dqa k(mz}xY*M{1Aՙ%ؾt1by٥_[|iXZZӿI&#Q4[{c(N-u-Tc06쏦&ꅅ{{x邪,tמXcmm z%~A) Xﳾ Ulݝi$ڧgJ,_7'x܆>;׿|ąi99Ñ#{=O&FXkkl2a23 ,MUYT9q*~`0H\Lӊos-{Iv8l1LQ3#څr |$TĔ=1m*w)E794:ʬZ9=^D%K\hJUSY;kEg&/ eLn X`-@YxhtW Cop6r$6iE@Ej 4ee!(X`w/[5G8v_~I?poc6_1y_Ǽ?¯_b \w`e=$_OEoݤ.hճ/1!}:$T>{xїˀ=!C28!QbjY#bZ*YR9zwqc΁Rց.دX6u]ur?@?طyO}ǾPͷ\b2960gQaHɁ$?rQ+l]{x\:[dj;\XZ_׿ϑg~q!'dwXlPp\n4u}}vN+`tA9tʖ<# Nk_M:sUZ(+bDV1()g/7k<}ڣi0Ri+4o^(ʥ=rw3Y\Y7"pT48v&;-&HJ r66EB[hM: *AU#S;s$;:*&E>]lhM/ꆽ]>Os{9zU/C|8wO> z{ eUanaxAU?Ɖg%n} ټp%藹p-ܐܐ\$םc020%v;l6c\{M|_`4_S ISQ(Ρ,3#K]xHDk]%[kCL`AX]b=#-tH?uY$_:eL DRT+ZbL1Q^$Jmb_9>;!2;p)Y5bXD8r!{ sF1+44V$կMϟg,vX`"Kve>ARh]b)ǗR*WQ G`/>YJJ+T /Qơ=ו~"nԌ()Qu%uuVr- p󲵁YvAθѸ|/z.|n2G4Qmqjuk/gu)T|Hq3LsV>.VJzMxr\ܤ@;zOxUDQif:no('7>,JygSVO nk $,L2R&Q*h#5bZk666(˒!)KY&,"g)9vvw[fWbCmZߞ|l҃)ex6Mcބؖ_R)EKYVP ΋/mRsyp~v7݅s 18lBҠ.bZKvۭ?$fЇ$StYQ>[{,rXg0M(Qѱ7D6{EF+/a]؛#/݀B kf\f.`mI¦\ZSI9ITiS*irsstŜH+:&SH*Ά(SGncC`+ qa\M`,ˢ`q~3Ͻ듼?nw'mO{~'>걣| L/5_ϰ236N9N?$ KK>WDZksm^9< KhsE7M392<<. ʧ=eU/}W3r[_C6/\[Nrߏh-1ڞ(cS?sftBO Og͌ݽ}z勗x9.p֥K\< [לt#sXtZ,cZϸ՜6._03 (13C: gA9 ~|WZQiGT 7XZ>‡>a^~4[p~x4^(d>3x'rrw)^^Uư cw?iHAM+V}rn:1ܒp!vt[Bh2KNŽ`bG` ` MKS <,D0܊"ВSߖ-9̦3V֎x<_0c|of>/% VO׾Cn@QaUgKQi+&JaGrd*Њ,Ǭl*jct^:u@"u;T|;tn`dHUCCjSd" 4&)+:1Xp䠺|&[!;_6lvC3X|Dl2q`FN7U"T)i\qf 8~qiv)sQa3*|^DN4;Niu<ұ\)^; 6qyGGh@Fz>;z/c }1J4bt{Z3,I)ddIyuad"+}|EO>NI`)Cg$r;R dxNW6_ψ v#pﳰpg]̅m.?{|sgx;۸ބ^0j|1N5TAULaIXgO',e>ظįʝ_`oSRԔ(g= (j#*z7{~7ysh/Q0KeՅ,H u q(p6%`yMtRY:&)NDշ2%*o@Soξ1c4Lah[}rJCv`2A{E򾔻?ПTVV15(X=Ϭ6ӰtdWE%IʑPbAT6p4S |d-ok j0冪W7Cπ!kQ?Ô*6WISLUyE{udz B >O9E)uBj8 N )T4R{u(p&LbMnd;u"͝c`R87Bu[ -#їN\i<٣q"j3>;mSa4PCJߏ <ʴ" @tyg6kb8O@2T(+U'SaZy0f+%Zcϻ+ڥfN IRo5N;1VhY^g$##PY^Tj"S,rBCs-U&Fslsqk~@HɈ~Ykf^~E&ә5145uLd7y9uy 8mj-gw?o$V…fr ]Pf-NM͹w ay Q m"(-ٸ"'Z6 CL+n?zN̕Е|"0J*8} q &Q XdrRT8x.D#zt9J}MF|]!$kD&d'MtVYNf)*oskS.ڛ;r+fۧnfOvR&S`\iF6/XJ6DBk֜A`VM$Xݝ *q›F7&~?$mU t 50bo3Lwvw.1:MW(ǻXG};`H(f},Ǟu?v5_X9[s{⓿?+ -2!qn~/_~1> \S̬\ulS/n7,>oo`'#zaAYTeE]X\:Jo}%we/},[ێS.F %аUǯbqi[ok wHC9-5^>]C FPI|YU}Uy^= o{-,/-{i(O0ryQN jN29~ H(I7 ( 紲,ʧjKk'7((tJiIG1nD lKZQsisb鉒Qq>m* qmDŽ sƴ"%{Nh2 6n*k܈ H#rhszS#2',JJ$@}a`1p!ۯ3Nt\z#z8±˵5Z{(w({%Ʉ`"TXwaQEk];ʪoI&^á\m))eN…9-|{Rrv~u RPWkmBi똊TBڂ(% hl=N$qұW`O񗵖ш^L7/؆m~g9s RJ33ߋߋA4Kp}m1?7`~+\g,;Ok- ,4f/Yc^O74MÍ ѳ nlrÛoalɋ}AM4T],*@0-;NEMrsNX?9-` cEwe1ˮ:T QEHu/$dFMRZoe O-jMcc|<7Ċy(+X+-a@ёq%TȻ8ѭ/M-T4ly8w8AYD!(S[.hIF) CVt{̹'}tXl2v.ۭs!0Ή~!VLL_?7>?Ʋ+sT.J`C2q^"脜N+uZ*bϟ;5ǗF[ry{rʳ/qE$+G)˒nW_:@&JCY& 4NK6G=0G-7o޳hvg`fvi=,Y6}Ě`6.%f0d1FpD^o7豸wvg 3Sy d<baX8 Dž(0 d?)P]dFlz =Ffؔ}{)S.1ѳc6n:)4PFȝY;VLBڸ}W!+t 1jXA1qf"n'hNVi"cBI2a( HDN[^C>x5mFfX[^2{me1F&kRNt]ֆBqVe%Y)%WK,7PH@U*葶 +Pvڄùgy優 0eniM''R!Z`8[4 qdiK*3I9?.wP1)MFpMϩj=V|_GV;~8r׽Pk X{V* 핧 1 08ӥsfj.= =kX?l^>OѭW.=hv\ͅYaҵjxYGiPtn؀!KotE^i6۔>BSԑyNVњОm6UUӔN}(>"PL#1 [xߝjs;ʢ,4[}i2w))es-37;7 JA-#D2Jy{=wSi^~G'oaQxFcDkf+gҖKym(PY(7DUxKLBhIRį{[t.&]eTcH{n ^oaq?ߤ <#{%$ 3q,ҲخS81IoGIYt Fu' C)DZ<|V&ao\XS+72~F$p3biIBTe׉MĪqz6_?CT IDAT|F٭wKj'gNUv|g*ūs :/{8e-JHކ4FSM@0} Ǻ4 i2&Lp6]Ѭrm&Rkovڿ'PԚO|&L 7 تV J,3'5,3jBӑ:<=LT2s)*v~E ׃ԁ[ *GE`foүsUE1 ex4d~nwXf"~z@ 4T.G/;Mȴ8jOo_}> e5`ư hOpc9?hl^8C?_2s osS:?W ˘A:7A k Va@>;.(7TI5!ޗeYY-")&eh"'d^L0WRi!x jv8KY2QDi A+O_{u7I0 1 VNâ<Dt+sF Dj҃ʝK~9e0<2myb*lndB%IHLRׁrǗE&D~ C7å{V'ɼǔ[O4  I!9:OmJQ2w*d>%.MD~mh5;Řghf=}bkk3\\2M ۠* QQNl=@JYBJ)<T=??8+_-hNAwFs?{߲yWA#|m(?o2l|쟠Jo<<}G~_rlLf3\!nv0^6^oĵ& 1ooػ)Nv,lnn$.#%>Ş̖sԄv&w> 2W# ԜՙffnxճPi{FH}+>B"QY: gP8ycv r=k lRh0M+RX N!Syfx`눰A'yÚ7, vOPy=i@}\9+|r|zXTZQ ڿ!!xgҺ?s>4D&Bj 2)]w F$ &I cyYkHu8RrL6*߫]$K=,-1dOQcDkY=/jG{kt"M 3TA#iM03KإqFM)34ұ{/k&<XW4rY-|h$W4z-K %?4]pv?ZbY3F9=4$׉`Laz,NIU!&<bY! '쾞G.k:/4ڡ4p"va+Y`;|<{빫oOЧfB^\'-L=be*@CUJDDMvA]B&=~b^W5a:2=&zJh\$ݜA\,Wi6F/*"%^f(Y|RJMyoS{Sw eY C_o~Cm]%֛Аךy{Q˟G׾|?O}~g~|8w^G'0C0m:*_j'E= Cx13yğ174fjMEݤ3ӡ6!3{q>Mӡ֨١E //hO3zg~e˴%_s%,}#(s(S"̐wްK 14˘QfcICQ9• O(,k IBtDk^R韇Hn2ː2CkӔ09VÃ8EB  13!:G)<gD]t%kO%6ωg䦕~8N_)CSvkR7&B3)OU9f#(2nQѭ,=M[cH[Z%CTc@9a t݋IN` {M'$B%Cj ɱWi'k3&%iz!aFRV*8Τi}J]?Le2ȧAtG&c-,ׇp}((M z kRP$yH4rA]z`SP"*. RP>s-j'Jo]z&aOf ".T=_+(P ةܴ PYh}zrM]:&cJW"ŪȔIJNV{`,+3\s7ܳ_5 3~oA&wc?K MۆӍZu˝0<.gEŊxM=1@&t +- {y.^TAw݌ǣZ< K6ӲwzHwSl)1rR(<B?NOOY~)vLk.h!32{g=·ax]_|A/?Owq4vc<7W^a{G3K>[)@hnSc4C>bjN1=۠^T!itOq] H;RtA.?_BfԝJ )n~nVGr4h%KhMx1h{JN֗ТFdI1 HzIL ' .!C 1/Mgzu)1xWM LI-qG3#}^ p%l(s-k' @!${3wly&=o8>0*L5Ѷr['ns;]I[1t]h.gKPwK2-MrNEbj!&ɰcKg͜H>μT@e"'-RRj6` LV%j2zlonrdAoX7(6 ?N'AWy7?ψfRYeb)&([mE!FL<+Ll=awl_waH]`Nv (@5 hezjz֍Fă l%jF ù;g%0>s?'^9Tj%/Ic:j\PK6JAx5Tcq`\'Gv,ZFB@O:IQw" 龌1|ˏPf5.†; >n > o^[P .id`X~8?/Mx??䈹 S0.WՀRAv6j/gO>n[p.ܶȓEn ߣly=U zM}Wp2fcQgڑȄeXn~sW4'bXÁ;Ӡ=%4s{ VJОiӚLwivjX)Df9f6 {f9p?9O+ghwxiyHd [oe~~#oլI&%*}Ԅb B 7ωK{T`8{E/ٗNO7f 'WRW$3ao&$\&mRߓ 6!jj  j-$B-̢}zhPciPSW!0W)`PM->!-Fَc~rlO҄'$;؇ LRv!t),Uhv1U&#K)\'8ٌ`ITYA{s~S< C>&,"0I: dDsDƑ" EøT _l~Oo%@{tW9$5uRP9DńN=- >3W?i{=(y$WT=!Q *g:K&EĴE%vN?oҡUJzġԸ-t`B.l@iRSHTcT1II'}Kh Vc^|0*<H#[L l*$71s;;N.-YAbQn?n R;;5L 4T`du8trݪȫM/hƍ׿xR2 zq},>ȹٗQ+N(kvdbԿkRa[c6/pA DSqlxc19xGrYPz;cU^IVH&!|_ 6پ9{V'7A1voQL6$` ~Urr.eYN֤SSS\[];B6ȆT;!#(  @w_' '̸knܥaOo»Ni|2lZ5nty=|7&zMi ,CYH!ܧ>`] J3TiMZyɠ:534t>Km)F1v)%۽>4bz)~O]`gV|zxu8E{x4\Po6r=w0.FXa1UI/Fkh<}o&,Hg(e\`FX~oHݦYӬ7T/B-n*Cw(qU&3y@-ƊKiL,fPq},tQF I] I}22WMP,faBb2Ըh#}1+;H/,\TJGʌf>WQclL 鱢-&1)TiMDRvY[TIʣM~yr`*"A r'M} lVZ,˕\|Xn$0%iDu&=]66@'ҟXh]eg΃jnàb藺rm9/h[LcM&xorCB4Q^=h UBeN)d2%e.2¡ڗ`-2RTC6np=pa@&]/HIծrA$Z'@#l1՝f4l i]qTJELW^&0~5=Nï"BIMĐ.'4iK,}* 2Uxpp0/.q9>O]f:3PF/HCM"zv׆j ݩM:MV +*f'*LCS54r{60;9`l#DHR$D+Dz$෭(8,J۞ǟʵkW 0ɸr{{p8yp2t~j_/8y@ȴ0 QJ@%F|Y l&oagYaJ=K8%~o%J=GdRYa ໖є  y 8\}ΠDY €h_d3ٕ{u. {`dnfN\zfZ :ynDػp}Ȧ4[Wޠ6f:S3/]P22ULdHٻ EY G#4pUS ` F'dg =eNV˨3.~rwq^—yyڝvH3F8q~ ibMu"d{`ll9 뉱Uw#nˣR^a*p1TDAPiwa[y "̳y SFFpP=\:"R%qp=HXvPL`B< TLsA{=z=LΠb40,rEqTQDc`V}Bd,w>2G=nRNF qպ Rr QoHcG%WVJ})+Aҋz/_kx0ꊴP=]"Hs0fsJ37Ȟyzֶ$epX0B+jD)*B~ ƺD+Š?3< ||Swh8 2W`;\ۋ [ad2>hP/R/:ƞeEuV@:L!4&jAw/$1n.]ޥLU* .35IhqLܖ}WY(1E5:i2?.-Pv.$MHIokC'q{ud{s`@1,zlom3Y/֚~Gs3 }al70{{lolR%B ƣmQ}ڏơ;/7 d N'1  xf.^E"H+DO {r^xAo%P`bxBr"C)۝%;,0LHCR LO 1I )bIog8aa Q:O2~ υ򔾾BDtl~h$+ClXD&Yq:D~B/&NJdzp2f*45t}m*Qd3$!N&VcHkM5ێG(;[U"Ek4J *IŒ \dH/9I(vBO]]t4~Af_ |s_`q2aX(Nl++AaYn?Jk%3sޚ&JS[HQXTdAaf8(+A >^ClOWڎFxNN$}7ݹBMMnpf~27իW9:&Й88=Ў@ZVCE3)1M{I6ͩ9zPNB:Oy_+…-%n05*5Ynr(|كAZ.K:ZmSedW,ezYO?aアl8Y֑ym"3TYШ7wnSN1  RJV:Rȼ%SIӜ @OR:/"л-cYj:Q( 4C8/D/}*EI"+kEn%*mJ3`ᖺ(ƿ5ɕ<.hg_j,Т܉ZbWС+6u?6$OIL||Ǐg<HRFwܨvNVdMI'5_a\z*40cuyS e)ѝ> Ù`tMYl|kOlppy-66\]71o `d/<ȽQ8?A}j[I{X g6`[#M{Jjl֬QjCf2yN&%րyzRt:u_AjkdQ[^cɿ3={g[j6JN1Z#@pDwzF.Xڳ}X]**)E?(V Uj7Ԇz<d&Q%qTzQ eQJEII$ 1Hb9$B8pHa=X^fgz c)E`Ӆp2&$ݛγ4GJ`񤣐,I!#I uEԄa;''Ϯq@.`"x0-/OK#x+gYڿ/;Xܷ믾mS|}ٿk.s;ᙯ?NosiZyY<~V8wefgxYYڷLQ z}Ǐ/2_Z\'5y-x5>%n\FciGt4uB%KGs ȤL KC% o, ,l䁈O3}߭;2V`2 USBD5n]D"Ǧv~Ez ~CuRi*M! ]`2(*R'4~iFH'mSud(81c I1{!v2Iva毣RP\mN Z3ikv $s9|^#%r|i3enVkf@sɱ[ ]L@(ӏ|[>reZNgb81k'PT2!lh;TIP 17>;񷒑$3 {aX>ew{1GV֮^F}> RwݩQ6/ #>떿V6~ s `&7憕RUF *(`O=_.(ÎQEz ;wMTN>5A2ODopģ~)2QΟGXUb-{1m "#MDɯrݏ!q]WtrsXw=}%ުS}|V`t/Su(n׮3`aVjZV>[C z=8pp) f}+}6oЍgxOSV:ӔEa7zCwswظxξ5ɍAvڶ ^B>d^k7~8BhM Bʜb{jM;µXYٗf^ꅋu4B*%d Ztl $ \>M a/{.\"ddD# 士|`T%>G! 0Qm`*J1/Tm}1r Lw+4%zGp Z9Ŋ s%9Q̰O|w÷g>2iVZSW*W xbޱƧO]>kcLT . c!}xg!%hKtfܰK+@)ޥEffg)F!qh<,~ʱU:6M`zt͉nsQoAah5k oT2[OCU:D@K]$|A^CL{2R,q"}a0" 0?{փϥp7(tJ\nUԸTh"&8'IiRD &$ xc}\YsB* I|~$xa41c`0l7,Xf4.Ҥ|DFѠI-nNWo0]ajvVqx^fx,fag{={@3ϲ[Ka8Po67^˸zNK,]́ ɦ*)~^N~X!MNMKq ޹[*Spߧ&}#H;vsDBZ(TH[;X,%BiE&3w-dy1q |1X^: *JjL|J_q*\ga\qdr7U`&jUΟQG)D@{3&q.:&wt25Gu@= oDflE)nnS3ioĈLɭ/Jr]ڲZh\Pߺέ8s4YHSQh\'\x)1<3L+d;^S5 zPѻ|?c =}BK CZ?Wo0,hicZԔsx81fyzm-H.ۣ1_ lc65բ̸qc<лc}GڜS<'/:N bPhܞff35a(;xǚ`6Hz,]23=-qce4E1bssQ1&خ>!3r x}w[YMl4PeIYdymrhz)cu3{hw}Dj_'r^d<./QƵ\yw7,/+XXa8h6ٻp=lom9u=ł=RiC=A6:Oރn"rC4=*lO{ZtU/c4i?OLLP&j022m/%D/g껕BN@b 2?ǽjddeRNM*+jeF_ 5i{PǠaB¸$1kll2W9yp//]hL]`R;᧛I z0$ҧIng*t5eb}b,>3╗a"`lRY}L& $6Cէ%=]gF,fU\x3ә#a9!4Mý >uz:kCyU4.!V; 3Ұ4#m{W@AecS_#)ߓ`^0xc OV _[[< 6IGۺNÃ|GeJS9gI/rId*iw5Z\KCmzoN-ɻsFd 3Aht`܇Gq7>fÉ.]NW6ϭMZ9(̬ cmcd ul4f9JnˮlK/u FYnh%:`iyYNOԚ eٞܟ>~xgיڳ.ЉeT5y$g?%Lw%}L}@ױ%͢ci~zӧOg-䊤6Z. FSjZѨs7yQsj(5 {}z[="ˑYF. -"z֡9^ d]J# }LHW><\Ԝ537.YI$a+ ef 瓺+c[:H-Af6魋A01Dv_'ieƬr:%sFQ) 73V w^~O%FsQ= i4[ +W3]ϠJdR0=5˫Ͻ@1cc,9޷23=ӳo"9N-v,GeCc"@#@'1 F(r@("Eqi٧{޻k}{s=}U=_" LvW꽻! ;5)i%>\8wkϭp dPZ_gcuSJ e 1T,^1֥,z|yB*Q#jkaHdbxoJF^̹H^DD~?iLcc?\Ax* oz/jsИYgf ,ک5c qG'AoƟ YSX)\RZBL4,˿'qAn =CR5A`;}>:kzZo<ߠ£^]ORqhp3Tcfh$-.f|2dhRkD"-0[OZKՀ,,!ZuDH3ֶY><ωZ\sk51ؒmlt)i<ЃFc&)ZLd\n_sUz 3,Tpؑ"MR27 &<胠 /0ui!4rAYAKÕLk7z`0&Aʐ%-0T`#ܦH`p)^!:[kF1ʔ%LS dRDǹphʩVɘ0(Hj=6n1el@$UγVz)= s .y"bDJa!nY1}(C i2jBr岋V#Rwuk&4-$E`@$\|<I^&1FVd4BkM`<!u +5k<%}Lg%{13 A&*%J%u*qR&ycME"B%!T%2<ܥrfN!L"!8`(Uo&Ȝ C!I42>7[5--tj3btek!駴 W98B.ܢ(Rg|@[p66Ćevw)}DV$0ذiOU;qsW$q*-8+>Lw@Uݻ+4Y\Y>]a%x730j[$Qbx歯“p<>sK)p!tFMwQˌ_* ]c;,*cݢ'zYҡ0sGr"_poS7{?EA f|R3ϝ;EG>ci}`z-q@?Ork IXimnU:껥Ji"*i)_ *SH5g0ޠ T0++v]RfqA{u`|-XΘRsПCZW !;dzk78}^VX젙 a8΅ 2P5ޛSTƐVZ^_5oςvf{`5[Wi4d**R9e >tݠD:s+4 ο=+cD`b _|[ $Ddc||5gv- 1D$aaV ⭷."$!`<p>U$.űy1soo͋Z &gYx81[8+V08]qRTn fՄD;BG^jqRe%b~䗚A1Ea;>tGxyy_fT;uhF~D$&uMG"J,Ͻ*]TH1"8iZD$h{V Lu^$Ǥ'P0RĽyT CYc\,cqUgwXY^/Z^̜Y垣xcNo|-L $+{&VGzx5^%xe.`Q܆ q PWA*i~͍Pdl+8\ghk4M~W_|7_z9csDFpa- xi:\-W_9oY'5.c"^$L03@owXJ ]߀ ?K)1 .>`5y K _ַ*HaXO=ő#G vQP꘽xW]D3w7Z')utюYF5y$^o?ljdw:IBZި4ך˼_3"l{8 VaKm!ɥM`VAe30XMsndN3n:] >yA}sa0^XFl}\Fi>|*;Iu/Q,;¾5ZGg3ZFMt d͂ƑeP`T}(l;[ߢ=C"0X&{Ax֚Blv1{5TZኩg % i јN}C|`leΎ1RPDy}-Ɍ&.SP2ҸH©ѡm^EH[& 1:/oPt^i5%:r2@qȂywbc"LT\9DM/:[86O"iXET*wID$'byccH3,sdgmܚ`BITt DLQbq=n{PescIcd`|dehA.QO3QDqi%v8Eϻc jO,z(%5,1*m5AA ϼ׵UK$dz\2xm'J+H,2Q F"腉uDz'O;҃0(]R?\ʚ+1r'{h;Mtד쮯0 -d:sfNk?|?ze'F]c9>ZDLg@{C6ag4NGމUC'֋[:ȁ:eѺIy},ɍ`S׋j$rؒY(j!V-dH(&/mRRnegwO( {%dحu^ Ӽ=~<+Kkl17`|s=EQTa\IeyY0W0pكea9aԛ73цiq[wcnѻM:5Fu ~ݿ!iį  e[)ԟva}d*$kzW_b:H{2dg2 )א>yg\BHy n 9LH87YzĀih0ie/sGX[]e4QZ`D>Sx*PB@jӧNTXߟƯ|߷ъ<# ex{@u; 2lG24Fy/L;LDb ,3V$ ̄A* @he#0~.uEl&b]W@ND}knM%)2a7[1l';~UёJW9cr׍(AJ"R`!:ߘEsHu-k,y;e4:KJe;63.6,wcnR&hUv&[sN, ow5d˄!D} " .\Et,!!( MǴlX zuTyhK RE"bӬiK =Y?, Q|En 6@d{(+,۝O%T=TfD@ոD{` hR3+7FE)&ƲRb}`E2:KH jD2 [HH=H+RH=c[\xolQ[y^oqF9Ȳ$H]Y5xd/)ip:Ƽ]̙L4wQ{||i._Q92I{Vzv0]ZV+$i@IŻ>}!s?ݜ3`괫J]nY\6+gFÍfSg7$]b KMG= kb mf䤷gÝ4cࢪQɡ@&M٧)jV_<}x@0J_xu_{gp+s3x_.]ʕUR2m7vr|#d<W}Y kjj`;47}9`u3&*n:ڝ}IB(tB20&1d\ij|5y5 `Ԡۦ ZnmB}N 4a9rk$/sMG9F'*yg΃w䏿RuZmοVy?e^d#~ jinoS[^&/q,$1]Ի4pK/ew31fLTc`$9)l WHU,xgX޷wMV7>L`OvwO!qQe HʆG~IRlZk\~<ZOS7y=ۡG! e趓K"mO ;}LQzݰj*CY'|Q0|4pIR:<(KJJ}V*m* ~"Р}K u%nn!I-R"oO=sCt  ?p+dRS_ 42(B*Lk`qKCf\<:Yu-U.jYVIf?PF﮴^Km4L˜J/Rkv"5}°,T=JB6nмN}qV;C-=+ "?@v.`0Cy?0Gd !C&Z*x*Clè 3O=B$o) oa&F(:Hy)1-e. sK|홗 I&/ $cia,͸瞻 RLpx<.eb\HV:<#?Ge!D#I.,/<;Ou-H`$҅.6Vt6q~ iY3^^(d HlHBA2F(& >ILEv\ug; ]m"H<)!X(+*SM:fov/R+4Qݴ>`vA1%ﺆXD_$m!qP6r?Ț)@m*\{(TFeʘMţ{uSzv]pvRE+eUc9'-ӒS63,| p Y& 13aDvycE~s$+) +ѱm._)fq)B߿X=qrr E)φZ҆%!\&µ U)HHu1!Bh[D]l|Ȉep݄NZDq4enAJDQbkHA;3L RI! ɰ~*²lorh}2 F,؅bhuo76sì?D@PT$Hd W)r73?˨ GIN7F>L}Y~#"I ū$j2 ;\TM2AڔhDYhѣG9tb+0AGw8A)t Em$M~0X9*AZ&]xMZy8~|2]"tNcG gGQ؛2D@z#w ږۡ_X5ewݡLA 3yi>LADh6\*)-嶄.#zyR/[YL]Ê'?*m{A-q4Xء5c'$M{)FFl=K("0R!WIי{%zu4|DkB)hu|s%X"/|hs›ug$SeBץs?+7^jcʛz"yZۥtpC!)it~WhgF`nM@B{bʴQA;hSNi(@/]rn{u4( HSW)7#D1QA_'%U)ﳾX] vޅCbg/A) LiI5j$i8{XbcWeOCUD:Y-, LxՁ3܎5s >lBDCLM kj$ !RA4,ށ^WkCQ\ԇrv"+/ bE[!rX*<% {t,{iɢa5UWe6S]I&u`ځkLEﳉHYJ,M!vy(I"_ jD:* }(m2q!]_bE&k13/wvR]nXkִDAGD?Ra*nQL7rʔ2c0*,Mbw,bo䗖1V+?O+X[ЊТ^cRfW.__׌#,] I?G7XZ9F t6q'1 tm~JntYΟǩ]zƠŰsZ#cH8`zVIޜs%8wֆp7|KѿM)~'Еɨ0ܾJkǨop0d:ڍ`20 $SLPJSXjL'ijHO87Ǘ9V׶/~FqsݴVDcJrLCh0Ldq -ˬLp#"aeŠ6& n aCnN",{)}m1) zтVx R+A.(CG :g€z4S-e20E(SpC:HKE@ f,'#! WDJF HET{}hNu4DP"b,QJ,ߖLi[(Q*%\m-Mڗ$BIC"t`2-jMVV?嵔ΐf&ISYdjϊ ’g ] 0F~yz[d, [ڛX7fEŚ;v)0ϺnlGq{*%lW p;7)Wc?07Pj6-XHdcχ򘜊Ne9a)NPF 3lrɦBp|Cwe'EI+^fܱ)/QwOkmmW_}%޶!O,[{=A[QeAɒͲ1s$kp~A "8g_jM~&̝L>z7 Ks-Ts& SP'7 ~_R0QEHKM G]vD@΍_e_!kEZ,D(32 Izwh1꿉,!hU#!9 h5s#~dȊ-nzWR=^*)d]Tjlj$"V۸:(m{+l)f%g*Á8lR-/<'Y2_Kees i̱>H< d3׮FJ~JK ];Ṭ"jyxD9XNjǘńѾ'UFŰ|@ |+5/hlM%T == hWY`P< e7T:LScKT k*&f3au &!GI jhzۗ䌯iqTQ.E;`'@&))rkm~?Ǐ^/h!29_; $(0IRÍ4>(f2W./ɏ(RDf[I1(S̨_O߃Nd5Xo3nto9kؗ/bM6Z!nNlO~_\jqioZ$"OhG0z?shLqLȖv2&.p^G1<^|DœW5iEvt/2ČjvOo8@m)W^ŗK EZK0XqVG肳G`GiEY $( chZ9syOE>z~˵kk;L-c{eTrTDYK$AzyC %cF{~5FZ(p ҮJ]-pZL<é|1)͠D d3112z9SvQD>9b_Бo% *P_F[ƒا%bX^8&N[{?DزlAsxqOE^FlCm]AT,?3kCRa*Q;tDJ8VF { #$*񊮘].rlAlk\Tic,C$Q r %|sU*bWcх\yl+_RxFHTvxoee}&0ne:&0B9pRcLa]#_-ڃ{8ǑY#}ϙݫ6DdˠD@a{ *e|"ZX(9V(%-;9"_"܆><g L޻RvDK:Z*f(L! j3yQ3vnŪ"5D@^X9\)Ưc=O7^0-33=KR$”w;L`E ׏D zں5Yr-lTdBESM6U)M9Rus#RH T&'^*?in/JԲ}û*$fk"uqb{c/p}{y߫UGf5PL IiCL>ZнA2gpyMSl{ M-gLGhLcޜr#49=.[F=9;S6)zVkY*zt=O7X~ 2_0:F}Na-j+מE6謜!_>=0J"4=P /\gtk S̓:t?WxA.nF9*'K3ڝwh2ҞNdRB1Zh;uNg\I iVGEtP`Mv< [SCi(b6A*!څ4H{qw.4CU– !BNnPZt]DZdnkco"6[OZ(hF@dVB  >wI+M q̅m0&i˫CYa+eC@ZI CE#L[p5ȜP*{@f, "x|S)g.Uʶ5UGb@S%5.Z$ljrMr`mQ& Ou4QUcECrtߪr$ḣE[lW)SdRz03Kh22L<`p_zXP9eAaV$]o}/Kbo*GZN6fp'+SBij&t"U?f$q0!"Rqu_A'і$Țq/L)# Rb<;c #}.FоSȕaaבJSa4E-:Gkqk+ѪIDVf=0e*ϣIY<{f!3SDn tXT5vXqP4ֻ)xWZhmZ3XNW5ET|/THSGo.4aBX`[A%WȒc$a-QbGNIPϲxis c~}WXZX9r GV"/ 2߲KyԳDS'Y^^bS\,lp%^=?4ٿgllO-KzCKV~ڃBe;Ral56,>N-XV+iz)$Ȓ3K/ QO9]2M<$#n"A5cϊHB _AYo0Bխ,Xdf&OM,;qTVq*+֑F4) E(z!">BxeJ q6J &"\B7>P—W奼TR:/I6THS0h(CP(ǘX:X2h IURX5pcQJuUzYJ# 2:j8?g*k@.7*(%L jy85\^R9FcLB`RV"gcz`0\eyy#鰹5a2i5/'Hl".Qx kVAL ЙIfhp[\5wK'k\$w3LJ )&vm='v%΢[uY):.zZ0TTxv^SY)Dt C^ΰsz=;`ogMCa8T(!$[[|077NwK}1 ebyS^E9RJ:~M?OMTCd "-G\$ 'uoSG8|h;0ݥIZUNu|p&9r-1Iã:;;>S'۬9;ܠe "2g煿ddhM287~O)H:Fd9AF/zO@4&b IDATFS#Ǩ-`fj~1QdedɄ:f4Po(MQPHYZj ?3ui]ǜ>s|Wˋ`he & ߹f|EEweMJEc -M@(\PU5 d8%m}K U"ܶ?󔇉uUOR)"").(&n |S]`C,~7C8(j, ͨ>"t#=7N BL""0`~WRlejΫ{~*A{ĸ:I<)J*iXN,JԞ% ɱ5u\*((ńA*.s'Llb+t.ewL7FSe;̈́Cð-d^Gx??W#QJ6(;dJ/TZyiY(c]д?||//Ly.{Phh&. 0e@]!Q4^))_Aa ZIa3&ghL|II&]/.27 $Fl<**\1n hYǽ0t dލ=z3{ Gar5g~_AL}A&ߣy>䁣ۊB{Yk|SBgY8\ڝy;{dꇶnϰ'2uLJDZG(F4ksޟ-0h 2+kuF._Ha복rG,Z|ߍHxu PjH6!mMq,,bq`b$2Xm2t`XF-~I`˭,ud-i dKVeBm;&0(څFUs3!PYOaUV츰~cBK XRL ST{)~ޥ˵t]ׄ˄ R-FP hؔ~'KD!0!dfs}N{Emɭ[})h6P1$'59D˳1QZ"X1+%z fq:Vincdͥ1Lӌp(?kf2gUǛ̄ؾM1~gKϱf$\,2t8 (ceN.P][0yH*| ֵaCi)"V_z4Mrlo''g<lxbc,x6!5XvnY7?~n͛&#똹}Q,H}j$msܹDG\(2jsKu.%Y+9CK m9z`e )g~(X9!k ƈ~ -jr̚L&7 8r7"OI2fGaatz]8I( tɇ/g͂-Zk=@ t7@ RX$hQ&ʒPVD8TU*N+RrQr$ۊhE%EH# bhhts=k}ykM? z}d' kq7e̼?Mw֗_ax,/կ\m >&Y`kwnp1n9u3oшia{k*cI+ Qszq7VKmpG,l9rͬ׾ֈ DrB %I/=tvHѶP#4Tn""+e9cđ*M{[ZS*vB#} KQ1݄ؓ+Vu˵U&Ę\2.M*FAF S$U[ 3=r$IwK#i#eVfMtf^ Pom<"S0H4Pف!$)K(|ҩ.Hʠ]@v{<\d Z 3,2x)t0ґ)lq&kv.-=})HKXуԩ)3yNMBkThʾMFaKJBjk,'X %ՐVu hB-eLC<:FrX؛zKopVCvgS|S3ɤ^}7)EL.DN0z u6lOWJm؏~|ט x0ad?Cz4+?@rzgfOcfkkE9֗Z„ G0Nllsg1wF 3gjqiMC㌰{szMd^s3yLŶ*BT(6JԠ#;SmNc:olL[ dbj񴊕 K0rBY׆HrdTܺu*|W2W2!NK3?cU/Q:rRo%M({/%ZP;е0-b<(kr -܉r(59`"CN89ɌAsݭdЀ,$b[E#^l~7p MZpY],M!Eg1)%I6gl,C?xӝ뜿(簽^51l&e&nOYX35UǼYKTTP0g] V= T# 5-X%Ϣd_woKuhϤkwGjכ/ƅK1ix^N&{):w+<8{ex1mng:8M7w}>X1?=orN6_| #6_ɔMǽ0ܤo:{&s ]f4S\~2iLop{0YuH_^`.`p4ӷ^Adi:dƯ.U|'xkm텴2> ,,]wc{gt:%dxJS ~$̘s@*~5z=T:Ҁ+o]S|_ _}=D!JH\$z;,香v>D6N20l Lv RK{N5DhCKNyh.ÏKOmM7)jR#ɣ*/􄔤\8(TՐ mp7tQؘbǓT%BI$#1M`6cjh,񾔇{ yZ$8;/B`.AR][hmI4ocl!'Ƈ"J "&~8  Ԁ$1yvC ֩$}VG)ʪt~6Ja.*&n+ԚAB+4XzqmP DPh4e1RdgA'l.@7{M;54S|~ǥU0àI_<{>Z~C-rOd,x, $V-C+Ca慁XOAg .j[S)hC *ԥ=pfܠ39bof4ʐkkphC6w1a8pS38bւXf1SՇ[\wx5Hoۯ؟R* oM\/x،Sl3x`*Λ݀@-8BP7JVL$fOm(7}H.A i [s),9]d+U5 AL~IrAĪxH+|?0߷'x흝aRl]rOOp]w12K.f?u 2^G]:&{Suͤ2v&o8R7^!,z'y/aBG̛_?]/̋ :%Lz4O09J+9LwO}) ^]>ss6y}>EovӫO`"w7 [O>y@=}k/ X;3L^z_b̃:шQG7hzx]x16?µ9aL=j{0F8y$OқPag4f:B[Z7MBMU wq奷5Sot|Ao|)768|Z"H8'Gᔨ mJ .JB{v| !v=aɶQCXuSG yio4ȉboEY%orx#@? SN5’T'/kƖdYyx/ƨpcԹmZBbXX!qsLݫ찱ڑ%](3=I [^gRyv1aׁ]ϡٗZd0 >?zzǎ6p7.}'@ܣM,isc?낽}Z]}PrPcW|E0))f\9ư^ #8lǴ6YE(C},-->tbՕEui>0~@(cP5׷],fwrDVҟiFR/?E^z .]Lw\7v4bAv!43|kN3tTy?~ ;U3e[o~Oӌי|gh.*1\w>owWp9~v;ec9x=lٛߦ韺p]<7c@,=n4åu׻pְ~Mxovf5M3Fz;s` X++8W/or{!4vm2h+|2 >>ri&2KN.A#O:"?i`oABˌAYd '!Eu$wdDyJl02)O +&RCAZ`U":W-ltMYPk\Ԙ0$̌jie(Jv5>zϻk '<3Ђ@S%\qiٴ)8VŰbZ?C^:E5-P,eGۡOcPl~()cǞA2RȽa{TbҴ5$wҦKE]Y'swiαw[/ }&I`ԭx3˧aBs$i}ZW=-`PkF0NEb(yp+տŊq[Į`;\nQ~ Y1lsmz Wxi.0_ap3xOs?4Gع53̀G\?sM votaYZ7, T4*ncޙ;}I>7=J\ctOY~Wϟgoboq l^I1yXTKµo<ù<;oW-b`-~޷݉ؼ~o?@p IDAT}a~?|Mg̜<%fdqm%zD ^\i,`0u 530Q A8$I+)]*moQ"#<:raL?Qcf#IW@ˬf~tetR3vk>|+p>UeTpYpj1b^HM/*9U8F%/>Hn- g"h]Pgbƿ| PZč) }[0) Tef{`ե%C3M ZuSciAeVԈYp Ix=8wx8`9ٛw1Mx"kGkj3?_,+FX2Bq&Lb1N0L8A^I鷯saW'0HjNJФv~P kLo4|d+fU"<ݴJKrY#|}?[̨@]/7VʀKP0&IUU1;̢Y@QΏ|0wߧ?1M:s&{Cgw: Ϭ  Vg#X9;E;Y 1xn"VN3?WA;a}[zU{Í8Q `p^w֙]1KCYczÏIUڡ ~hپ9f?f?nܭWdn) wڳ_fz4\k|% c2#mG Nbcc7߼HBxBF;'@8r~-66h x׹3>|myݴ?d'8o:7>O^ 5y뷒qJaEl0eY(3-fn o cP JeqbKw'1~DO=x y)QT_Q%iY\CiZ-B<{bc)^CYL7>wAcEF)qؕ\J@M.Rv~#$KhSxHӤe9Q8}(B͝^QqRR\%yp[N}AyE3^ɧtR,)Ci%ҳŔkCXd>a}<,;`{֥Km16oBӰ>}l م4$=-$id>٨fM/`g OSxk,{ɋ 1v*1&Lw5 Cm<_4gn{az'>+\ǹw5Oѿ,Qn1{W3slo{_zmn1뜋mVCpÛ?K'@eS\%/&=w6.?d)#K{saa勘8}~u,oyS- 1uӰʰgY01 c`>t2a_G9aeǠg{޶׎0.`_xg <{+qTbڛ +m}( oJf]@@x1s]eT2@mYБ%2*J-V)Xߕ44iEA.^O)[1E:*veX/>ό#Р!62*iMDXa1.WuG%&$ue?f[vhIIٸ5k=f$Diu=ӤRhT:f̰dgU@V[5;Mвacya<d=ak΅f'.}F剹=SɩF5 Ӹp,*{}=XZN/լ1aU&fε5׀ThQQݿ( }$n$ML 1b% D/KX:,7f3}җ˗1ۙL&zw UK]H8(i?O*}4MC?/J5==C-UXQ h@8rLjݏ[ϡP]g^G~m˝C.8|qWԁoݫ׿{ӝO_`#s,? `:c*枎v9'J0=&٥ wauzwQg=Ek.-al`dflo#f?Ke=zpn8Vpi_e51^=jVW8yۙffM'Sv pV%v:7߼M z}Ν=v^|CxBf9q+ .rC{OCdDlF({IV)qЁT舉/m7BOQu$'R -LNHL:S>C Ǩ[mr]k_Rg8Ӓ-eKm?c{؜\ähˮS]%V!rJyMTwLӦ4PЄlHH4u |^5,s50.Vu *\5~oC92`osqj!ctցzh|hBh/",Zicgelpr`q'Y\Z=X3s3MmkZX1Bw{M ,~S\xzi EJ:$uŖרw|i`sk0PR"QBV%QU76':qA-E7L0=j~`,M >t6%c>V'e' y{Q*E5\ J4Lu=ZxKsω[[OUyJ7G6noMƸY_g?b)\b#|*|s&>'ac2~Ao2uM^\czzC!cz8?[?BUM_Ȼ"5isGނ ?Gcϕij(zJLi|ɱ*bhSJ[Y[_қ!0=׮]c:׷xvXZ);~z񾡙'GŋO/}1ϴ[&Jͬ1.4CzBR` j܂MYNj-2<MRmrdTV/./Хm} 6KLp=1R>1M> R(N_^!3!! UN,2,[Jf ֓(.#=iSA:Di^| as/YhS?sdvn(Id,[X&X2hQ,:?כ$Pg]R@AX$V:!&{MnY\F6 h\$T`e0j(=b+/PlgFPOȡ9/($BWtiDKAUߢwbCEh\;JKzbbay[(BNY޿8O*rDP_GfKk+JY:nS:V%uR$u@-Z8ܓ܍B8Glk/̽b<E'Ó@tIM,c:+ o6UXe>zϝK灥ь4BU9Uu7`c:"eX:}ӳ[%Gϟf|:c4k%i WLRL!ϲ*}2;_"GS!7_؉+ɬ}txe&C#W"6)_o*>s}I^J-!yzxK~vm9f妹7O|e_ݲnL +&OȊJ$cww:-?sV.A0^Ŋg6\~#.Տws)VU`T+q#YY, LfڵU`9?X%҂&$|SQ)r#_SOܾz!>^c_{9*iZU?{GBhXZZº{.Dd s0SOaxs۱OUEBs~57O_~OG?p(Թ8xQ}y()2jt$"n1N,dɩ!Z-B s/aGGJXIYiKC[Xx候$pfJ]Ί)%PW?RhLriaw) AR*k=8kDB$l*R=܍X^%̏})pƩ#RD7 ;5 BυTn6wudxHOÕ:I5 TeVL6XTsȉQ93s 3/]L)Ґ墨"&׉3.w$%.0.j\FYQ흎DTZ-qp˲tދ鼇 C"k&bS#nrM2c%&R\O2Z l¡ %6̃aIH4F$ĉ- PQ2 [h756Bk:MoMN~Zצ@"k͜{^z_|ݮ -kӦX:.U!o4\y*ͦeLd]dYAPZ<"(M/2'}/{OElqlt:Cu'X:y{85\oOYz!uIуQ>XĴ*Ojjf`F̛ݷWUB( { ޳յp@NZMVWVx5οk uLLsVVVYYYfee)KL30^UaDxu?cu0XZ殻x{^k$fM U[o?p~%q' 4(vPB> Qgڈ]LWL%iݖP" e趚ʕ - &I7^%K`rENBt)?hK#Ȣ|!BIpԾYc1T L4JKǥhϬ$"_ 1j$c. Z\EfpnG,ǔ(2'Ml ޕI"U2_0mLZF! ,a+"_ (^̐+B/g:GM"AVuyCD KZV/гi:p1 UwO:kq!lS[P,SeI $9x{0Qe´"]&W ~i Xz~ʖ&7B$wC꠰vi:uiXr7QcG<Ob# ҃ˎ~,~.TRG)OT]=g KCAݢ΂m_j>0@wT83Rֲ+LM[S^#Ӹ%g/N.)! )S?]=$-n@P|hJLAӝk~I9 $)h%~5ARr y*53}I2,}sYiJF@w710Y$w)2/,,*"A0=_ pBBHJIQ+YN4o;c)ұ4Z]H D TONJ j849DEЩ~7zHlȷbϝ:FW]hUL$쓗&b ¸z?/r@,ҡөH2 >#z.OI%pF ^(rb,:CY=1!'. ڠ  IDAT};ҋP}PrYmvE1BK2e)g޲Sa{%Il;,f\ C'G.Soia& FڮvjFSё Z|Кg ϠGK ?;B޷'&%D쒲J(4 dɻQ>[YH\TR9}RNokU~/OU)Ͼ2'o:c ^;x4`7/}VHΡvc"EJb NYY؃v0n IEN2 Jf͒<- bQRz$,f p$BzKE,Z@!OC6)C8m8S1^GJإ,DY  @ڠ:LכP8YH !3yCk:i 26ʅ\{(!$Ā 15+G mQ$Fy 9hKu{-A/ʑ}\Z {\ߛYeI Xy7!og-iD5ܭBR\)}xXXTM0_"- Y˙Nąb2U zuLJu\1#9\q (tOJ#ZB%jɾ܇b؎GtΘokD5̃,p8KDô,A:,⠤R#!nj1a 3D:yhO`PxQ&d2י/ʈ B.&ª`B!":Lk oM ώ-gtsUhF\F`NDӟ{t~hݶ@Y|5Œ"w foXKGQ8P{/m!r\ǥÂ$E!qSN\F[ss^0= !oaeu^8s4.ix p1ds졇 זbpՐ4~ƿ3wŏ'?a2x~E5( { %JLH8s6Gm1Y 4ID *$ i 48a6[ HI'Y64ۗ$rN7\àƶWvӦ|F+ gct}SX (/ ~9;2CǢM,޽t<\L$A1>ARxa:u0 $zGs/ݽ|NLղ $@SQ\%}-n].O׌tS!XlaaKcƥD*&G9JǢf\SЈ1+%4Y){c:ԩ_(Y7y 준CϣّV% Zg͠-\pj!ST0}\ +3k0_+g+i:@GaNEy`yhu !P$9)隒@m ¸i圕+ ̕Tr~0PU &xuKy *LY ̍0 HЁh cԙ J{5`U9 he}S>c?̙Y@1RvpS*Z YR{?ڧ2$iB\d Z, ~p{\٢mǵ)al>=ɽ&#YXĘM3] z ~.]zg*ux}_pI7$JLb4 _R4ӥFI??~yC<7tkkko2@\,/};]+r#z=zy1̄Nʁ Q%?V]x,zlfYR+p~JlT:h 9t ˢM~i*rk4 Y E͕%is?/b*soN!]X\eh+L,9$E?vcmX4XKL,?$<` mg 0VZO0SNlT"u'3VfCk1K-ja+͟ Zm WFdͦWNQ˂[o3jzt>}hf[Q% Dt6]5JL,7 5mepl/sW#_֘L^'-TBGGZ\W]RZtD;`{? k M]g\=>LҬy- 7953%LXq% i fI,wQG}ԾŦi:-^+Z1~Lr[ȼ|?I.-iZ26 iH&{OX__gk:}+<\ʵ-fW8rV9z|XkL>tpsw u  3lol>[o]Б8瞻:r*3bҝag4i>h:O=>PGN4[`LbQJCMrж0{H Plo!j- b%`M煀H${׈LK.߯# -{5Xoc>ߓ >oQy˻+J9bgȠA_[St דWW6Ҵ|P=-Q,8!c[g)1dBA {QbXiPL V鼖 2@ZgP}./m?!6vD-5Xq6~Ɗ%1EQ&cҕ-VQ,MCZbԦVj .G  +N!.&t)d&]v9SaQ>g0rdОMI&Fը/pn~s6\ޠG߃ d+UK0i3&9ydV3؝ٛ؛NF{9Mۛ7M/tu|EiVVYZZ_gqQy,08r(gUV?/1߼$|>emm sgf2Ù5쎶q?Np˱uXFX_yDѫz7L~>ˏmo7ͼHy_X[][|m NWgyHR&К%]i7ˤ{ JjZr4Tm(iD,dY_Ƃk\AMR2PRb|b72U_mQrIRЛ{t{F>#v0C)d-;5Q(L <3fcTA8t:$tYS0ٙI2iS cFa']uF7yoJpu9m*!#+A,IzE \>F,,-bB>߄TR8uqt:UA4;Tµ<,lG-SYqѨ ~R'I/%ěgܫ4Ddƶ̦ ZuPR}rNd5!E.(JR.fJ3h-~;~TVK߶޼%mCP-‘EY=*%-tO [Ws%}\c&;e0Zfݰ‚u lyk_a`&_RF눴ACYQn]~8\8LgM6qlˀSNFK](t:U4iI탈-/T_碨k,"Lŕ)YvR5T{lxA-r:KQ :\_yŗݚd C]Y_[lt?JjbU^cjo1%:jy}ӝ۫`}:?0ڝ3fw2e23d>g:Mg̚6n3cړ~慹Qfڛ<`+5b- k{Ag3y6{䮛wOrׯo\a-Y[ O=~?NG3awIUx__zv0"L~4UUq-lꫯ~oo-)=_6w̭7s/%c-ހI3c3|?l^oqbeml Be&%v˥Bs ))Ηg!&qеA*,Ʋ<sVef[Ud7siRHdSh$ah4/8 `A5-A X#KCHHі8Zfz_z=כw9D~?8O"ͮ{o$H ($,6)J5æ`0r]LQD-h I@.$\2Ikr5wil$D Jҷc ͘$ep ̂j&cvj.7 ʋ#gΪK՛G]eB<3z4UP\qAI 2^A豩)Ǚ\4]CnY7êaHF\R<و&9 4;m D4ɧFuX5pR&ZdzΉ*Ez(WVF5YFIS}`Utd*b Q,Kb0A![N%r,"k'ł?ZSs憲j3 +jub+#` ߣ)'S2w Tؙ82dA׳ v.9&:hbb'!JĢ k`Cb"")x`xXvBku "ݘДY}{Js/ tJk/w 4 ք 'G4&&p_@ g-DٗLt>'e+TMp {@yau ~?.̉dQMG>?Of)}Lu4=)VMQx`\= b:.]d YI+a# FUkuѠƠ`u0p8h4pX j ZHY#d5g]>~% _7 #/^|K8z۝O&;p mqmeMEe|;ئԴ-laei ~^xߡmpv&}NB`PYx%Tuj?  0Hm5 s&xE=ZL\2D2LƣSMр#!l@;l ϮP4e-'Tiʓ<`LzEtZW<\%$jmkZ)ub1ʘ&VB@CGm|8i}KscsJ|J'H%K_d+qHl{}?Jmeh!܌LETлd{p#HSy՗0 喛YAʱg!nн;B$uB6TF@1kc M%)FNqw\D>j(xiP)A$J_ X5X^;:ajLBH/ק!VƐ.?_oA<2Z!;Ztk3)/REYqM"VC)- KGYPB{<O❙w+gכ.OSڦKa\H\]53TSQX:"L"x*T7.\k;hԣ'h&t…ިOǏ{@6c4evF(z}@!]y ^{ T04NY3/@DALBߴ &u]4q1:mib4tgS9kرcL&&{VBV@U'_x {u<;E 0\XD=h8ufˣa0[ 0T kQ[ MRķ0YL] J$nR{tùV\/z.g0v?ǯo.O=pa$izlDl<,ߞX9ChdR5u< v" V(k3*tƬ\O"|@}YFS&( aM{Ϧ3߭y&4zɔ3Ǡ(( ΌVJ!ͶLm8+Lq:"pj,|pG=71 |X\Œ2fDi^뢤ii]IQ'µkؚ&} vƷ"B3 7ƸOciTcyeM;Ul]sO| {7⎟}מ9] `17:F!.\޹m|[;cǰ'횗 % C$ݓ*c贃dyE 4xGbq”ÿQAQ]t[.0!6}rc2 UOQEZh$8&`a_mcDz%+ gAs5r$NR dJgr" 튉HU(W\̍Eϖ.TAP26h^#)!j:{g,ʘ }[Q >\B $ MgP%izfXPJq Xd:)ȹH[Xos줙J?ƀԻK:6$IDDDE!]kU*ES .ġ}vL  E -ձ$:BHC@lz$茌hjݑM3Y&$e)djNG" V7uR [dGr^RAY¾`òeA !M\BU른jQ,q [K>uA2B9w z4S̹Jmxel[X:,U=j 2`T ]Ƶ >Eߨ[Q4&j y/96~:; S[`Tm9g߹i{W\Fgf% ZvfV| E3u֗RS]|җs h.L SIs&~g‰0`Fc .]w}]Up$cd"\P6*0 }[UGݟ !ƒ>NP_=4?zGޅ_/ дElA} pC5S*^V.#LDAk5=3tYJmX*ЪPCǿz /bs 2z>|W;?#qK'1{2L5u= GC\8m`:k;.8PZ̺0p`a G>W+#4vu΀<9`au ?31n] P0۲32cwޤ`>2bԡلiTu`̠]thHbYLeca%S2ı̛JEѴD8]ŝVtE22p96 'QTF I#lx.l%w4&M#AHMJ=ZrHt`>7q^*<,v FS(;*Ec: 3|6y'i@W|Iϔ~MÇy[̈́(I؛KfL#S{L k,HN|jMPj+2MұB*lY1Kڜ0pZE$ BUl4ye 179vC=ZSBIX*i jVw&z((SF4 4~&RLSQh*,n !G)|x8X2m[f[Jk^ u {a)s=HED}3fcQZMI˨ucA09t^S-ZVQ\gEDO6Pi%U" y葘/7A ,"B1NHG7,cfdƁze.,%L*# |OSik~ֺ]#߄r8M ?0}x7Xh[3k;%m[:7n_(!'p0@](59)ހ.~v;lJ.cQ E{﹈[1hkxzpg1ٟbo<hgl 501Qs`PhɮZ-뙑p#GmԁŢ0q+]|]isuDG#sBLk1㩧G;mpGzN7jC:H{a}[>H $ePNɜzs6gM=& *R`uɲ+ShPLtL2 @:"1ˤW f b X92du>XE]?^FWYdB>ʺjKo|(QEtqYVD{5Bmr5#kP&zhxYr 9,Zj۝ćaڌ0$``ə:I{` =a!nu! zsGwr+c6(W|$ h[0"0џ|rY.3j2H̿rqccK6 !t̸m?l0wLJk@($*}Vem %z#$7.bNdK΄7G鳗ϱ4ΩCV%ӥXYE$JnZ5JKK$h俌bu:ިRѼz2_84b.4°(Ig PMƁ +_n9VF) A`R4Y=2 6[cdž=i94LS + &8T1_9p!̜H1C8àKO<]^QCv ,;UN%vQS6?`ܶ=c,:!g,#EqYzL"(R~5kBO^4?zYT<;,Lsdiww9z*&.R< 0(jbXq՗2?o/+,~f8S V1}MM`>Z޳i0a*\x& flڶii{;/G1u~ZR{b6k} 0Nq U8[lmRP8 Y >lZl/3Etd[ 'ԜHkr ?4Dbil/t-fBI2bc{FgUZEeCGQ{'.H17gVJUfw`_g$ 0*[ ~`*ԔInb=ՓHK p8xiYtFSl`Dh& Қ^G6u(V"B~_ϚISMNY§1Bt2H='qD *ؿw P牺z3HEԐr  /L DԜjWlb` A 9Mk&!y_EgP;KWD_Meld jD/《Tc71MqJ.l 2u<7%4,,U {˟3J2i1gTS뀽6ڨak,ׄ)O> 䳶 k?6`DX0 g|>ߕ))ٽ)J!1~&"ܣʜFr]s`s%!>ŏe q_d* [xkJ/[\i"]55k)Ƚ`*dnve&ˍ2V iJ7nH)B(ҢQTmbhGp*?4?g};{lE& N8W Oc|mI`IORTSQ)"rM\)q{ zrX$P505]/4v& !/Z&mA{ m&ZgbGÕ2EnNJRڙ\zR"~"#^W(ui@¤4Rn##(T gkgWڔ[-|ϰl䕿?Ί*iVS)Wrl%簚td$D\tgڶT)c$SGX![BGә$ 0A(8xe7xB*h: 3ϘYCv7hF*NcPϾ`I02L:, ^5:0(]GHhşFW*P~2RJN)M9 6.FPn;ӫ;JEFP @ ޑs~U|9ipcdJFrPPQbupb~HkA[0!|_9<FM@v~wq;[XX&cqm73A'޼=K98& -wsjX9r\tI0+wqi ={]TزkITCĆqcɓ``<,9XUH15#Y/E`Lar}m]I6pW_j7u7Gj.{Cwh)wXmd*,XƵ[lÇQ 9r}`d2]w_ɓsϠ?qTtwe'VfӁcq05trpM !f+@etx$dbѓARPL(,B.s-U(| ))A2PM <بu)bMQv؊B%B%T@{ST`S!`aP"#Q=L&%Ec>Kc/Yx`|,MF&칅ab@6OB%|.!,Ңdt@!5XDK4;ftQD= Xp}L--\^B_"FJ)qQ"Fi'&?0p߸wFOYE}N6vo5 c{:-;^(shkdM IDAT"& 'SMh9q`׫0xsps.\0 pBiv"%M-Yf .6 5.S' C^,5 fJgk(p_hqцiZ@Y/tB(7 SvTEER2I0xa.%4HO^eb̀2K%:O#`zd^ 8I)?IdBFV9*htIoHjhn [F$'2Q RʜYߖ^WNZ%{?K'`4IQAeX)^WR{*Q_4:`nnVPPY!5\9*,2@lSC c8TcQGm=*{#D.rSZOp0XgRv9i!!<6`v@~ùzKh=(9`7 y1$Kӧg+H3ggՂ=ӗ6F# HG]U8vz8M6Ƶs^(bA4h/^Ҳ 04umq=Nch%,@REC?YS̺R; ^N nuSSܼWWZ >M&F^:885 05눲q!1˞RH}knO#-ttV:P;f6m{'C1$IML/+d5YnIVTSGlza(RULF? ںz@R-@jK˝IUZHVQh8vTWdPۀ{ޯ1EԤQeDK'gu: &`z b`=fbcm1c%|hL[P̨Y&E`EgF u_<ÌDҁ]ϩ"ڭiT zV9?4>{>Cpy7  F69tc8f .2z2%8&{Dʽٚ%e<4ۂR2?t:s?iV6-O} OΎ C\t L&M%!YLR@(BfzNZ.0ev蛼t9vD R膸ÂM3>Ĕ`k"u,+քI"s|ҁiU)]hn.x3hgnMpE4.”:PQXSq$-"^iV5ZE aB2KdD3dv$f3)|%+@QiIvt)v%kZYu#L/wPfʨA&\LVg҉).+|ݩ^Lh+4WX ,0"o2gƩ gcrNJN0r`h V?]gj V]ClX1hbQ2rR1&rv]^3EāQ~iu:-ZVFE оptf֠cB$@XFޠu0U.$ kN̏I--`>CGGMdr${O[#b2˝,G|mI0*߱s; 03.!&.dRk24u[I>@DͤBXP|%dMo+_E5R/x6p>O E4ڶ3cmphEplMw~{^KKKx1 _5;{}{ **T6ܮ`0ĭ[0(ucwgu]cccŔe6 hUޱ\>ec9:҅Ё",S/kbj6Uj?(d2 GLxXEEqc>'`sw_sȄcZ^|ؙq㭫X_4yi`lo;X^^O݂ |&ϟtlboww-y70Cy{}_z0@yX(7]Õ+{qxAqASZUE>] >}J^,z50NS[\~T'aRDhFf˥hZ)4KQ*ڝ/mW3 ĉahCF,5G9$کiAD@X:DMX璜#}.L|{ et(Ǟs1e~~3 ._-Ktꁍ?BMJtM^!sZPD559!3O;f4JS,!rru=uأWA"8\ Qum)RLFWFQ*QtW w#CCHe ,5qPyYj\H&A|ώϕφG1346~{>!/kZ }::`21=ZG0 c";Of()$M@m xზ1:h9'[%cV+2c~rwQ4|1!!kHExqb#_n7z Had "L(jM {e~KJD4ގBù{Lب{2yf}h> UWɨo2@& 訐Ę!:''g{Q2h?דOVZ,ciq o ?2Z 7]TU=FW'o` p"Dinޥ'l8@*6jl5!"CE1B%LXƃdS Bp@5u!: 1ǩh3dSן <^η17wq~KˋX<õh.@`0Nq$OӧyH6&S=H 1ޣaD>c@ @06Z;~d,YB1Lp._>"oB MPڜ0H$gӛ} GdRyE"P%Oz e$P{Afp̅yXQ }ᶦQ8⤯3E)${Fb"w/+]f^qe!ff8?&+6hʎD o6o]L((QiR3 !BB[>giR̽Bֲ#i#JEF=# fs˩pnf)/\ЂiGO39VMxJ=\Lp6lrŀa @0$"Ajvr "m\D[ e@25a#;R^&eWW,$J9SA\>]\.0'Dd=}O>10 KTF3]>CU5ʥdPMPl."h{bfV4{1rNT88A)#Y4i-KrvZh tb8<YsmѨhf}\cGl8m 6a<'>tLɂ s9Q NTXuqQ0mdV,Y0,ZmL,X1] 9G΅<捔G(J[rY$BcTLڵ>JI2]FKጔ R--Z.{BTW,˩| e%N&$qd s4Z1|( ړxPf0$q9H Td$ɔV`T_ bXk/O".92eC:e$,n$HOjA@X!a9eY/$0UBU :^B4r=F5d)84f{@^FA;;1!9KZ/a46Q3]i6R[?IUPi3wV{eL3=5F뵥q@1u*> jl%JFӆ*P}StP.i%Ƭ euQ9: g)9͊z]ɮܑRzV.='<,ė~MLZ@j`!>]Y 1 c'v߾1NYa=S7d3`W11e1Yk7އem  Gc~mÕ lH@m=&F>w(|2IHw**}4Iq3 Xf7J@QA!D ?%߱'1P6A'8u1V|92Dz2|}(޽vnw{>q7qasC-b6GyTf)׏c*;07h{9&M׆PCÇ}sBP[gN]w| oJ]+cS^+Q8I D1&dl^a-螽 %̣BEI$oGʲR T/FFrNܡ 3fga(]eY,Ѝ)vqm#Tŭ4u=sP6ŨJ罥 *(N (Al(3DM2fF'N JгԈTy̸DBґG=7[ppC%Me `&*gIQ".-+)bPHZڙ[UAGfKvYC)5ϔcNP9Io ,(YFa1pqK#8qMÍ!f)jUu`]y*\_~'O=܃ljŪg=~?$_2d2F(X4Mkk fRH*Wwp1870c ߀C]Y͒.e=s0B?9^M>n* ӀJ_#(&F &J<3fudߘ\&'=OEND8GbzCgU亦CUŸ6CB^eřQ+υ/NI=ዏQ>: 7b$pFSM9|ʠi}_"/Ldy̶JuMMu`XM+Ja(\ (RFdmnpYY +EE3@Quc E3=) T_IѢ bftZ"r˜ IDATTCQ0Vr] L~ޅ{ɺSMm;6 }ॱp~ تǙJ Lw}ou1C/h XA9 peJxj-hQy2[,LyJVt(/uс`aV捣khH38asx*%F'at _[*Bt8NG8-\R`dfޠea{820Ę{ @h#G|hp5N8>|wpc8q6^p8^ jgp Ds<= n.ThTcwof=)v7QYKk  2m3sg{h= . OHG )FʱPB#Q/XSCJP4):I̊&̳>Wў8y2XA˔$[4- e9뜃ySeVΣTPSHp4iȉp[^^~]nUӞK\DkGHef@V!^zKtku&-ϥ#)dl L-zj3)1Ei]odEl)!216vAN**:,l"JU~F~!664 JpCWӺ-Zy4LyJ#ͥN)0exV)?OºR\|@vc:` lp@>JY|% " #RJmC#VG'3-ۄS k5p"cA5yE݉?QCӗClbM³+3pe,q,|E6ʑeBu92sab( FXiZg d"v3L zPh[# %H4{nނ1@cA[,i@斖$AgHJ0|V <3ksԧ>d))4ncii ]'<=/N>-ɖLpa4Mh;ڛjQlat6J?l) N51x>3*bi#i#dhC`UK7zx(4C6~wܰxzk8w0CxWQ;WI{?W<hXIP)ZىMbLoO#J"%@$@›m_U]>s<'O~H  ܛ7s86pjLq4x4pM S׀B]^Yh4 0>:BvA8< vj(;;;gY#"T:c`*]\{ ǖzxp~kH^6\XoViҐs4%,l)܃ Ga6Z\NNjbDZWM@Ew$&Q+""H\/ aaW!D_<ҠȤPMgMAHRVFt:j+Hs:ch2@7㦢GyEՕ%*c8Z=%!R`N+ qhѴ&r̀&}QRX;*HQ葬E$I4SCbH4fIכ"2}AUfz ()/+]y6` &w(iˤϝ~LEOF@v=U ڮCeLoݺ?ߏ1dBe 9rɜ)AJs>! ǎ{]?C5lnm3*[aqa_~{xꩧ#;&TU.iLYK6fD/bh Ga kCbH0y0gK n` 2f ^#X,C/iɄ)R aȴ"C jo%?!N,x:~maf6w+M=[PNg{qr8>#L'38p&`㜃3F!ZX߹s}&^׺j5-ݏ^y?*~k_ţ?m8w&>ς 5 (C.@`6l!Q6.R*laȅ 61Tt=fRv W$#I(L08iP|ĢrqœmCDڤX+ SH|TBPO:DZ5(~HT*6\).1FlJA@ȹҺ5Rgjjv& )bIMSZ4LMse- ᢩ)tK%APjX6QT_5b\TV Ƭ?߮C+}*hΘAB#\>&!uѱH0$e3&_4'Me/t@U`hR։&hV6+4HT='4lQe5c$(##FHTPQ%-0IpL#6#`V 0qx\O6 G5b0~yxe+ރiժ2xʄ2v Lr<r\V,0.WSe^XYG] 6aHjT:_91Ԓ#+CAv4yp拘!pdjĎHS^oZÝHe?3mf cZ7ŸW^~;; ,@wF#<ŭjkX\XH^c-Oo/?;&PY#9SZ,//7s{1|k EHBӵ=hB0 X$F]5<~XxTX jc}8ĬE-Qpyv{S4cxSvl Fj ʹa)҃bJ/87ksT ^rh +ZA#ݜ8r(h&(>4DVKB)|畆ϫ:.҅955VyPW.VōŠ>#,L1VHY.ޣH+R(& /[înRS:?kJ0JYFT*};#I;:AYE;vԤDeMwƉQ\)=B #r([NqQK,ǂA $ ޫQ8=_S#B|U!GHYKD"ǟ͜©.P"_gyPr\qPyMkL0ǘTyՏk{.dfe9>)M(q IybW\; *LW5*a]Ы9QݨK"Ы\gn* T1IվkphfDxvֹ!H t-k`vSX9 ءˆQ94pG-a1]t\NPlFkHbD#En sB ,*+İq8h G Z lVJDimLa;vɏl#}ƒ ߐ59 "=0 0,Ҏj~0$kdLgs/ y3/z*^y5,ƬiԨ`o/*P8fl6CUh‘ZJڶ:O=nܺuKu!Z};XVlebFZ`!E23TTHM悒 )b6Xs>0daԹQF앉>DDT@IPڱkb"Rj 5Ĺύ'BH> 8-/ ݸ* xz\L o{ ТXY%(c^5)S`3T h%YQϠ(I Xu]\Ϭ8Ӌ1JEJ45r:O-eՌ`(o)XCF@$Y^ UWPghb& n.$tR75Q^\>`u"jJ&SA N͌ 7AOسaeuEfr{6zŒa oɴD"ԊQ7hzf;0{Y_桦!ڱ @CLFV2=4iFc Z0( ՒL| tBbs!*D ;WD>d>^#2["!Nh暚m>oy?x㍷19/_Dx_C=x"_]{A_LbSz>y<#O?`}}[bKnU>ERO t"LV8LiSc9GcV0nIdX$>ډ161P9Y{b0~xb,SXP`Kytfy>BJoSFYV4 .յ&(#`Ԑ PIge9/Vy5kU2Q,4E1#Mv,&N%5\Wrmt I{j`FG|63}%Z>ÜVOBߖr~_Z]\m!p H[3,Sq K˧F>}!lh8rJ2|Xsv)$\.809еhRc^fQs5B].GM:n\h ׈TaSEkA8%Lp',*vÖsBmH\AS5!`#1a^gpЅ=6 R]߳i$WAjr<$I10yU[$NO1I˨,|rN&SL_ϑ+/K|E 9fdo|4MYpmӠyW8ygk-oSu??_ŋA]UXCjs{= F 1 X狩(8@!P| r\z6Ȥ2M=,iMpo}f4lC؜gtΡ裇 pa8$>n%l,nnl7^;+9dm $irxnݔ~(e:V$Gd`l2a/%  ܔ49>*|;Y_V23Ч&RLJPnIÑ2&sf9r`wASbVz@AXLQrD79hf(-Sn++ʌR@w]D(|3B5ajYfWθIXb"N2,L%TEfm*#Qw7BOq'aPClt V>HaY!ERtEMqN kW_:(*X;rI-x)q]ڔ ~@#Ss񬍔QM u.b+8w'mc2,~C~ XK+挽|9ITթ|X+hhF&Yg4MPQ(Y|s> \Ŝfn J*~,?LԵL񼳭6~I'SP)RXzPPH ׇҔ7_ slOTk,5ٙ-Ws+QQ_T(zXȆ>%,TA_;1+,U}q-"K)lTЪ!yzr<ة9uI-Z:s{W,:ᑨdgU\7&֒y0kq̞Ц.FцVi"[RY2( lOHκjhF!Z]ȹR>\ PyeM@;GҾo/:$4P/> 6P y%0B/RSg8/yOZ g8<`:m](vE~ga6ϫ2666k\_w0}85B3ef }᭷ .\mzaL%Pwk $Lu0&0 0ІIL99G1{+[G޽ip*zfQۙɣ/Ii׉Eq&Uܼ\49Ƥp·bȓ>owεױ{c{xl珡;?ʼn5`:Mh.VSw􌃂q48vld .\E\z981i:lƴ ?OCh ɡ=Y<أgSU!{7@`6kOⷿull⅟9~Yc">= lJ"8LKb"!LeV6b qX O] "|2D(d)G? *ʅK:WTH&:69 ~C?I&v'K/J= iʱO"{I:$S1ins %#u9a?1(9";l 3!#Rμja#R%Z,2Hgd%)L,dOOשbh 9T~%Xc*[ T UBF3SStΜM,ȕSsf_xEL2Q7nviQ3c#6vp{qA}aZ]dI[So^i Qؼ=&QR-9"F%Eˉ2pB ^ް(i K:kPE)tUCP8 HjƢQ) h##-&ٹRhJR7WZKLᡩ^ `u=-9p(Lb..(\3GaVd丑,H#37|P* =g 5Wζi1̔//fT s铢;ʨ3Dt+M i"VMxn$eȦIeSsZuR{6fTOdkB IK(^k@_x^OZTք);;۸_l6 '8D0-L/_| O> \psV'QUqQ75?xwo} |;;.gsUh,cv19bui=k}.$ИLhpugŅ>^_e6dv:|Djކ=@%F?kdP3"=4YQ{X 6~ckKh;' quX]]!7%LŹ3 A7łptA6X_2&}"*kq E< ckƧhY(V'Kʀ -WL9ٖ|ԋ5+Rϛd&d L/j)yv̏N)#(sN}ڛrN0A(b! Cr6;])ha}?ma Ѳx8vl {{غ˗sCz)nzE(6n`2s8V){c2<'Ϡ`eewy{{;Nef=ME׵BǍG|P0Dr& ˫/=£u vwQA8i?Tqlb+(bx ,=Ȉ 0BDB4blWpXhNICª$&W5l 0WX iQyI^ Xt Y\XX>Ϣ VT(( d|^w1+b/ÿ.CXF>f5F?B#NHiDi";!2+-U* (aLJOѡD$ܨG#v*fqg':0*|:sWlyZ2)&?49 PSuhUXr(t&8xwvl 9%"NF5$S es?$Оa~.@3Me t(#n^|3#,N=B:k0dbC4Zs%QyQd"C+a.}g BK-q45S-j*4+!$sRP*3gJa⠨/q(kVCùۦl =d({OZkQH qv2SY1?ʍ$ͅgfn(%sj&Cj̅/+[ SxyLXaρvc ]ejIf65oh#qkgJ sKA+@R 'Rnx뭷4 f ͬ x3g;VѭcvHM&i6MAo}[X\\@* ~gfʂ=cii״~{X\\ a;;QOi:@;TU^m ۣ{%,$ <mLpbipZٕn5LohMG`Ɉ!.'("{Ik{Bned!tpsybC&a5^⫣%@,D 9͋k!+>\*$3=3_@ޖ'.VF0I=IlP$R!*L;r∔ŽP9b&jLY[\ès /ȅ!O BQ5=3! X>?OTg`96YWSlT[c;Q.X%9R2PB4%Wۤ2ɣ.7S'm6:NA:>K " J(E5tj "j55( EsZhug$ɨ}N#ꔸgy輤[(f>N9-P`Af^a>D=;cd} )J8*3#=iJÛu;&JKPgKbsU_Y+zsBHƈ?KD2($jXƲ~Jܣ+FNrMHeJQ$ mfM%0fY!DAܻs<#ygvDɟ7n/ 95iB·-<ύ\tM8!dL'S8/XǿͭP14MAzҼ̚6t&)lv옢*9 =- C fCUUQ@ҍ .4XTFܷ:q. f]ȡYY8O,zx}wew; k 1k &*& >>U,pxhs5-fUX IDATat. p*fF=%\r,g=l._o> tM0iWao#h"`:AVWx·s5o`693 N9|ؼv5aP@;.mX),7f5>j-(A}t%g'XBz8# PXHAz<-/]rQ[6HOH+]*H]:$@ U%e*t]> -NhoJO ?79ڀ`u"5^\8P3o\ 2VaM,(\ A͔}hiU1ҍo#:NR4lrh"ǁN)R7uM%N!Jj=NhU\R>z!\ Q i- %u^aeC\HYߊzQ&]lB850'Y\sJJ)fNhz1Ҏ}L{8D(ϕ)SK'q~Sm=/xWV9B6PE"H,gO4+ԱюpL!|G8لT3T"ѩO Pu4d$4c! iHM9/Hvr8%M '^|E7~>ea6mk24b98vA7gm0HaڶvNfUYMUht όpU]…0{wŋ#H܋ WacPYAFe<уS W12f;ֆo}YABm/;ֆX[`{Ѥywr Yð2w-N,0ܼz WFؓhTuXY0 5>VL~Eg Op2&c'Fx>to\!&7;yNqf+x,]:DGq=`ai+8s4vѴ-ڮE6l<10y> E\|ε9X[M,,-$uXDP Qd.rN0I HuظM^ˌ${žiVs&` 42Km.fɈ"$O!)q|ԺSg'Q*Fϲ7B܄Ḥ02•`( |}LBb 3sJ\ ِ[ 󲄪9p2=I]jIeq~xuqcLa(2CVzИ2av=>?~:Ok J0x5wfR;NGf>EdJq6b!g<>°?Dp>.\x7B/vBڦF@ {O&8::~DЭC Cԕ666v֎1LPW.>N9-({UUE${XXZDUo^d'*<~2gazpekW5xr} ;x&`)pO^k -έ/a}m /~~a, jtsyJ!mTz#XTRedڒl!ڹpSM*»QŜ*1N>,'mHd[ӵ)EYF|ߋنj"Ux 'WZm,ay=\lIpe-亊$;DQ~PA.t]U xpVLy&XZZ躠~ ͏HEW6%Y7(t`,3sZPG€đh40*]R(ܸy4()F9r( sI:kBT(MrZgA&EdGZOqTTijQQm'()9.%4+a;yR=P;R#g" MJ;tTPUsxavʕęo 0{Khr̪(rfˈ܆(g*b#:; r2y@>8׊Q<̾0,)KQݴB9 X>ԟKo_yfyf"&ͯ8<Hνjrvқ}"lGQ6˴V%tJD,S0td=RZD ghOz&iBұ+ ڑ)Z<"א\og1Y 5G{QTuU \WQ⏍~^'64>RSJ¿F.k(+M*1!͑nG!މ91):gGtp|2q - yL< pdMHx{j\_s@D %269ncZ//ɛ4>i@V=Ǵ8'X!ٹ;lxYAE|>wQD_~ uBdzUG~XnFUUBl=jn.666pi\z {xG` |wy'apvVWV/} ׯ_GvÝwމv-omԩf]`[`o~OAw7S# knM0Y,*GSg/VxsC Fdž->>ؚ\5ӷ;WG cei`q xd S;ltxe\ۚϞJw롙:lp2mNx镏f[{3襏s`^x5/]ÿqqG E96.=.mN5Ǘ/oU2m&va;x[-zAn`m G{1 p uY\~ AU8:8x.tlj͎>! jl7]c* p;&޻q .t 7~c's4x!Ykd*^4B@% M~sxBnBy4"ЉBйQ&yLCѺ*u-Hct\,$}|m P 6Lɣ392E* "^C R=W׸p~(5mKRzq΍P%)(QI(،t(=Q/-MC2ztv$BNLiE$o|\,l#J9/pF #YeCvsnvwsZ*Cg\yx)H{&xiTS)~tf60V? P%l2`X?SXszlL"BknKcԬ;|DGʾ,\lQXER/-gwAU:OQ؊(5yȤ?{D_&NY?  B̊ 4>H_peedSf25F6ҟ/8N`("+Ͽ^EN"XL@? /*4;56E`raCq6^&Lam}797arw/Y!-1IUvcECܸq_K#L'GhN^ͭ-<#x/`:fmSԠu] >hN:~+x_z*{@x' z>x ޮ8.-/?xk㩧2Hd~FvE8+[x~󑻰s]kM}}M29=Lk3=aqvxo{wTXXhЃéN/q*ZGX\^™ ᛿0;܋? Ͽ8=;{3mYQƸzx?lwOvgXΟ޼1F۵8k@:O ?DZcj0v'|t Wؐ١9tX:=Btm˜8uΡ-:sLpA\יwnf^hlXA9ܤ2EɴG#1c#OXvh{ȒǞЌFiDI&%Il@b@7zWm{=7fC"ݨ~^or,e@J(ȡjc 9娪wUZL'nm[opjA(-P=ϾeR;5I3RC2c_B R ȿh`юU v >iCJ(#2Esҽuxk 9%'ŲWP(J5c cJ3#d<\ $6\2RJLYTh N$Ki7Q͐P]24W5Kל\iž5vㄹB-w^9Lj$Y0n8"M;/l23v00Jdb` %S;/ R !)XHN;_;&X!J: 40{D6l1uPp4] i9.ހB8r`9$)(ATP f AtfoVNZd8b 1Av֯p** mp;7g>,ȨQm͞Sψ\ XDŽkéY);LёIqI)*%[=9vIgrʹ*]~M DŽmuFP b8zi*ZX$YyUpR"՞FR5$\Riz1\N~I~𝮃k|+%:^?>:aURŜ&cccs7|)lXXX=܍m ۸{11pY[nSO?Q9rpF(9ä"\ppu 2K9Zc4PYW0l5%YagkW\Fk׾,ν ho?o%v1w߳8t{S/m̴z3ZԡfoZ~fz.lN1*3-qoOЭ ve^c-zXWƻ+v#=.L*f0q(Q*{#;vh u^-mč7~m!sԥes0yk0;7ÎŇd{ Bdgݹyz0&d`XVc )ه%l`h92PflrEgj:+EH" qVRǒmլRI}ub)o~B6. p-^C~Yҳ \;ܿR ;LE@Ω$WUBߖхӻ<@|:) bz3Zd'J# ph_U5A7$g P= a 72 =k\ױ9-憗0nJe ?p gE]шW]ȻBBube9ᤒ!IDLM,Iy;ÙA8n-~Dcb0dgeWԷ>R/>Da39\ZXp9Pb\˴xgyxTJ P5$աNϑA&H.CkV@>7$u.MXX-xh{- Iy>9a%)+1,"4Pw& hdBQ&!k: g"3yZ#zȌLsų5* 2сK]F)%R`UU^yu3d L%,/K} ÝNEgr㥗^cʕ uwS>v^ѣGq xw? ;,VWWqi<칳X^^BoWwe'ډ(3pmcrm sEJ0%UJPM G2̶2lMK +o vZ/]w> ~.^^{ 8]7.bdvN Scc4lK#t38vM8{/ ˃-m-ܴw-FMLރ7 r լdJ dFZ@'̀Ǚ~g1oLw9Yn@ B5_XtGGPȨCUb?RV1+NB"8m"4DPl(Hk1zz%L'[4\e ). dM@W(T M&xm$?$mqh=+#\:vɼYsNJ O>)F*A%+98֏ *a0H쫾rucFrF*T>(GEX3h¼1-0amU~Ƨc t0OM!Hlgj`c*5S$+V:2KSź 6JXaTJߐ<2LZ# 04| E$y0^ŋu3?ȅ7|3vvv!BU!QUZ 7x4?^dsss;Qٺ^;1pXo VNcǒ{ @ӑ+{.V7q ,#sSplNC3_pr_k[a 2LŶL{r 8we<՗뿇n 'h\_8X`RGTxoS_<9ܾoG8qϲġg+dN0gVpD޽I]WBHR7~`a._Wc@tIwQ$oLmFk 506AN}NEK AڋHrhq $@ @rw)փLo_GKb=T3E]8Hj9QU+A/ ¹vg2d# YR 5< \.C&vy *ra%OTo c Ԩ%;.%!@gJ.MƨoZZRԩJTFz^xW70$_<|UUa4`Z嘛GU`aa9L'B3tf}HJx:6ؙ>~a , ؔط(#\0U7,8O-l9wf%.U۸P]Yܲw?upm-^\[<|9;8?{t#sxmq~Cvv yV f; =̠İXh`rTAzο7vfF|bS],ϲ nWb2 Ępb'6ћM'c{: MWMDeR &pe..`؎ɾ6P- +^:s"^ %^bң|r6;CKā9r0Z$\˨=m@$MMҮ T"T@ T{~v*\&N$3)SZj k޿-Kvg\HJ C>R:,b4൓bw=R_aP=30 (Gߧ~$4)QF.ܟ>50lFIi*- xp" _50P%YB|G,ץfI.&%/t0!h($"Hu@= f"Pgu7=,I 29b_ñYAB¨Ughg9aU@_iLZCoX@2PqUHљaaq`@+xR[fM]ٖ9Nq*=}">q}]_f%OJHacZmF!(vRcNk4 gfi{'8nV!=za.oV˞hebsYX>V+Yp% ɌQ{X^~嵺t@۸ď<kcIEUUyכxy?*#Tr:ae(~)C!& 5`T$7d`+Vc]yQ=TUkсB x\lB1"l 7=~+,ȃqɛq|p9|Ocw".c*AnE|CڋoXLq6#=lm]A{iV6}|ī8}iE"Xt(@on Z`/btS.XXX[Y%>XOkvd]6K38IfF28Q`|i3`=a5‚ t~yb/ɩSN+ŋQx%LIf7DKQ?u@FfMӌ +Y:K*!!Z"7z-)f%>"h[d]ßU@.&@#{"R~RfiBPI N=n Mr:)ƵlcgT}dY&.a!p>XNjGil꿗2v3=PfIv;o p e'-|xn9ƅ),O7qXS#8tdV617S`گpGg5*8vn|pxo%Ǚ Sc3o [,1m2\u7bت,p]1{5oc}c]dp#nut[<oHJgώhO}q>wt2V,K]t!B֚/7" {XIDR0,Շq}vSC"dRasv9Rfq6Pha JTDGƣaϚ@ KM2|AXNgTm\NP6j`4< 1gq_|`a=3Ќ+)؋O^+ȟɦiT jz&d U4"ҽeȤ^M!1͚F$Yt0 NxB' FR:)a!Y]dWnz q̺.T)o)T=NdzIsFY "Qq`%ud_8Ti .1&2}Y3:\U6$ O`vUUFXf'5 GqT 뺉Y1s&D?5+K:%IueV='rU¤E?8r/~]JJR`@Sʔ#e$W)/.V<4VX'?kZ^>p2| e*GYõ43_2RVj yp&B.ׂ8U!ňr&x:-3&fؤY>"j⁏Op^k:7Ϡ,+lP ,C*}TcqѴ'--ň[}oN=Bي~_`2`27;өaVJ}{1rl t ^y9O;;_}3X٩,j,Eb9Z&@gv/6Ab g 1uCdl\hm sՍG>S,Π )<=` /]FwsnVXx7| fQأb]'LuA1Hr\ bQjWZU~"+F| ;fR`j8d(d4u"/bY5:ߓ(V1I֋6xQPD+H&IeOzk:p Mz=lv5y3'M˳ək! ~<# J*Ei!)p"I3"kQyr,Ǯv>CH>0 KO2THa29U)@oK8TƳ*Vx<5, 샦}5HiN -ʯ)_k%2(c]; 9P' kb t(R]HKj@[>O?wF_VJٞ(dNx'w'itcɺxo1$W|} OXjy|1h^4lȪD`$yTgtV'xj:0CRi ^h5%F\*J 0iÖU PFwq;H{Oӿ~ 9y6r{[DZreCEN5r+[%.;_`@9^ʰDus3x;qC46,̷Dk0b`KZ-Ъ6W܆펰]`0py";i 2ڽl*ccZa{2ƺb8by|C(]_%|駾/ԗwhn}_sO>Nb% zsp֓YQ} ,+k(K l;zm~Cjfժ=y&Xe`eX\xᔣDX25t3Fq5kVK>2_S~{m|8P UIPR5${<qQUs kH @T`9L6.IaPK$tf!JU*d g")*]o71Q\Hx (ȰAb_s/%vj1h뎑[!vI`g0VxSό&()/(+ːIA0$}6) HHIX41=tmS*=Pw'ZNJ]VgYFwԇe}~b%]PoMXX'QZF JhK5dψZP)SL\$gkIJ^tr7J'd?H*-Af D%1S?bM$QXFe/ (]˓,%9y~ IȍOW\%pYgYC5SKT ZY+(z I-Ѱe46MHŐ?]-_׬>VwVȂS>M%o]K6!>qYV9|AYC똒_@ IDATu0`ss/2Yb`<D'XZ^?\Lb<tZY|1^E8e?d2,(EQHm-NpX]i0&ϼo|. W12sx` 7^]G+ϱgҠfdM(ޥy'?{>et:r69*jJ=,r,6Ю82ڋsңe|xq.άob'Q>\@gy\٘b0( oοw?{O[wE"4=yc8*cjj&hɃx4ۏ??}:G[\:6n&LcTDp[>B|dUYdY/|%v gFEw?=wy\G}yLrǂj >0XA[1%-2|*@0,,,Z(|WDK]bSVJ4+'et1\-PɧNw;O ,5n aV,0P4@KMPDQ?tIGV(yĆ* (?y@jd4W=f4R@ 6%Q) 1՞o%_WkTdc2kTWvK’~Yg#OxIC]rDJIchkGmșrOBE:$>N `>p XrTaBA;j/Njtpztzl,L{"BN0"d*=\mupj,{m9, -v~#O~WQ"OukLnTVVV0P#Uh.=`O+o]`[(K,/0Opz^<ćxc~KӷB7`I ,F]Cԃ?]I+ GLFsU k)U⠓Q(4R e{bDZOIr &m*#pHϢ,4-gH+3& /GL3L(RA4$ճyNv@Q U]Qoxc!~ ]GW'@#ne!z/ԉH+$V.F")}2Z)8(WbL81h&5Y=$Y4IE`b=!ygnX -οv Dt1XH;${09sDS'! w ƾF1BRY/H5aEg6GPk!ROC]VM&2pγ !5HPu~$ m VdHF#ubLݪ>YB'7ţ(jFUỤo7\ aap^%.ot_D?_vC/-^=9=ENGya>H5"d'̾.w .$y/Jy!ՃK ޔX+SҹErrsK#:3Y^xvFY} P4%c<a2 3e<, PL~N,KYZvU{KL]PZ88K93z1Mћ]B9c•XFZO3z@]!1~ |7_~;>uη}س<z?n;O:Awقj\V*?8%*R"5E8eLVт$4Xh@T_e0 *- /LK&96a@$C$]PokvUM׆epP";V@H(`EZHҔ ϏL 9rb{qʯ^1~02ʛ3%E? %Դ.7>hϱk(!V 'D8#1&*0o5:)ѣe" 3 K~7!NmRQj:cX27hiq:mFkc }IJa~%Ÿ&vBƒr)dE[]]w߅  Ut13t:`T%`26+I=Tl&v9CN ,{Q ~0kN 8BL !rT`(T"$2zF"00P)ojC#z4vtU"A:X<,!!d{8Y8Hǝ^90 ) q*2AFdj@ 1QIQF3 i+x.bj riA` }fq<3'|_xK%7@R/¢5l33$#Q^3>KW뇨1LkM,?`KVYX }|L5@sh?4):vqX9|+W{bp+n)@&d!Gi ow`rqrZ'<E㭝~7%h&c}.֛hqfg뇉0qpaۿ{GCvFCyCu'S t]7ױ=W~ב5 Peѝ=8]{|p nZ=.3 UQ[|G,~A.oqɐ<5Qŧ`=CI@Ӑlj'Q0,ZxV*ӣELEtɍ{^.)xGIʪ?tNF9eT!dqBwǨJ"F}# r|@CfK=d *>1X H8Qi`HB:P,t,(%fld^DV,Md?KbY\VCi Gz!I^xBH& UIVsl oɼL\>S̋Y9! rQ Y@O3*TbM$).zFb=/XףNiqqi uRצ.FĿ ݽB:($ϳZ@~9Vv >V 8fY<ꪣXU?c管nx$Yd.)@OuK'$V6HTVYXE`K7B\2X.) ֑9E5 j ,e^R*i7H:En09gkѠfcQhWrΡR%ExWRb~pggΜҞErGa4`~q ??AMYS<"d[<#@f5Q{ ̲ .kxwqX!+ lPN];xUepA8/0DŽ--xM)/?!z']a;E{<5+XS|~Fӳx0ry@9 Ct-Lg,a:ZDrX ՟Uלs(P%wʢ ٷn|w3V,pY!<Q*k+W0{:WVC1 /'/|g^?'^ ăhwZu|;5T0D,`'o3PlG"#3jȒp(g䥦 c8_3QJE ~/qZL>0Bdx" (_s2ɗtqp eW&a',;_(V4c4ImKvSq&aq '/X쥙qӒ9^$ JZh4R^NvB:l엄ӡ)*J=bWZO u 2,B GR}a1Fu '`BIWR2BQ0%QNlsRUm_bIyܔw~ V M+f DFZ×B >2a~Қ+9’ĤY~Yfu2' 1bN` TKW_si2Fo]&ߩT5Jvc;8;n DٵZ0gbG!%aQ"s/ 1`kCO1:57>Vů~xu=7A;po?ڸ0Wx7<ޟb`vKX[n^s[3v6u]q;}pv C10k:ضvyy92tZfWx>)PK* |3 ^Dutz5JcN opv #2.:XvOm#_.j@]} }yǮ)&w$5VXLi,԰nQS:lD%ߥ$Ad$úiPlDյjx͜$̩D]3B;#G2XVb(G;C@B[{RW+KPa4JJ)5kT°E1 oL)m V-D80#;:tL.gC*UoQ5F{T`{ĝl/EV5ĿUp&Jc# C!dsN|ӤpM#U6+-U/֩C䤒#Jq aߴ?T F͙m7y+!9d&l _\hC%~JW9 륛/Ua< 6* 2b؊dW=k6()?Z0ŗ^UZTjˏ яރՒriɦfūk EYB@H QƯ sXr ޽n% '#`dEV'X,/Ϡcظ|C 溋=_9 VV.ag0@ZX{v£[,cϒZ| O >/~:Jnrk;BիuE;/¸X[W_Ao~~Fw:>VkW{?5sO( ^d#GPCX\Z¹s*760Aa0JP.VW`̀ YLXI"5m/U؍F*4f'Z% X:U/wEDm;ࢯ5*bQ$ʩd[QpK&tR* - $0h,zڰHG9 IDAT_9ɡ%$ZJ/>RFR `8)zsII*(H!P)+Wu^?f룴=~L~>g[☼J 6*IYN)R= Yº ^YYgLd`2A lpa)m9yb`f5سkv+|tb*s3̌Ɵ<^n?&Uia1vVv*؍+X\^x~. 0w 9#tƸCxc8!+?wOAw Wѻ3c7>E _'^1؋[rNtpem s c?_>ӧDN2NY397u|g0C1<vqSx4s@Ōmof !+ _]e|GVU>U5Ż#Gp]wsg /GՆWQ byD}#=q.醺bVtF`n0rhDž!d`Xd  Fͩ4-tjY $U}/CRh!98Y)f'Y[2"_ vAN"%6C%$%ʋ3lƟS{B1qxoV,4V=kE HߝEڍ>@~aAp>6!csq>dK[:J bv `%LxgCRdj58iG1yL= 5bg0"Ł a&4ۘxEu!Dr4\Ƨ*TĉL l6*BAH@b @xҤ0F4$N]̻$Ӝpl1s%?[YOV˓c4ЈP1CxS$06q\^K#g1p -)&;Sᣵ1@: 3Yrԟ+-`/wz+)T9J)q⁦fh_4J%25FʬmN Q5*JkHڇDz{ jZY+ς I;ʔ(2a'zL7ľϝ\*YE,RKdI$vda6cF 8h8NF%qdfH"Yd՛;W~}^տ"`U}^k}SԀbGT"(cDs(z/(rgj# mt!2i7G仛IÙ%VUE}6M.j"%J Dɵc@y{ȶK-IgNɵS)5>bޘ!72حI0G9*eIBKI GXE쨀Fe٣pZpfu6JLeCfWft~:yW_tjCr% JAkrjڤdii[|I;wh1%)GZhܭX2Ø]EgDU2\"3[CZ7T!{639 sˮ3i@) ϴ9-2)CW^FYCꘌڸg:S26il{#ZX5}l&{#,T2Eϭ=XbDD.j$!ƌ0b.yĖ2,BpTwPm~ޮ)E_Fr6 loㅳ/amcU9)+\CgΜPw]h_kB>FMMtJNxbzTq<nB4^?op2~:i1ۃ=LK 6;.m`:I`0KvqY0 ,cx`sq_gs?G縇?~oW_  /7\F[_~8q>9NGc|+™Cs:ΞN=-^۟믟c.D!!Hj6EL?~hX ?Б[An6,זQF`\VnkAњK(#ff`eUDZ(9 xǻ6^|E=v N p$1P*%+5}T'PK#E=(jzDz@HS¤5`oٮ,2FIOqmKs |F_'z\Dl)ũ }M(FyRW/xe]PԘU clZ;ƍ&L&ݢgj~PCXkN)SEjQ_#pz3[Y1հϑ7hp5&n D1NJȨRQ/j9'5+uvS7CR<4K 8QcY'7d葅j6gcwRhc4$RK-RȡA!ݬTpLHvtX]Ϥμ>o4x/"{Ap.~"iKTepev!sfĈ> k^ 5cAק~K15(*:k .>8u2b !3G&lg^rB"oB89xv6T ?/^5sU7甡 daI\R#oj42MSV#R' d> hYU\\<2i aICPZV129g4lAuWꫯ 66G`3uqȇnR'xyx5PF(d gMb móSm,̞6w?qt0.&t{ؼtkKqqc?:pٗ_'xϾw0O)+L];ݿpW#ݍųן|+_-sX٪0@ =ѽ3J=rn [[}\~سw/:w2O\/e)lllى&01Z.n9rvS'o`06ZGbp@%6qY7fu ` e9N.F5as{nĉ@n"ZI'S4F6Lm^+c6-18O0XǵWfZ)CP%`%lߏݨPO#9'$5>'t 4dJE E!Ԧ{ӵ5h_a-~z&R~ukƝ]%hwWAk:ReFi #}FtIptI˲XgUYWHa1JmfNyʛ)6iҤ. 9r<'b6 ekt(dSo VD%n Hf QdyflsMTu%Tb.iʖ%ny`FKM(7@:զTHb.ՙrk&7v|eCE-4ڤwWH)JKՍn( 6v0!qvPc=XL *V%e+Tm3k|k8vHk^rOKHgh2UIm^L%HkJulҜe]TvFz}muhJRlMjz5@IDܐ5$.\,Ͳb)O.KWxFF7/- -Xl *9xʰw}}"];E0}<ݤE@ nF]5#1PGưЭz.]OCR!9s5=$͟J2hR?BoaqY߇~>puc`e{՗ǟ|Wo} 2 X9DPBAd{׿~@=>;UAƊCFjDdEEFuGG }hЕkB`-z "ɇûHrZ H/ӧzDGFe\i^8EeH24ʜQRW=PVzH ?"8Ču JIaP uw,v{ `B)3ZX hqn)3SbC3g43fhYAÜܕ8"rJ6h  0x'E}N ʴ`,f MK^Sfk7ےlTz7Qoݴ" PFJ渎,B6b3Z{5&x4újϞ?~_֨$ݽ8UwaϧFoW|NiTB$`0cu,noc|} pz}3k.bNaz~bf~"'RUÍul'(|*`s>VPO Yx@)0G@n?y ],-]MPc,..as6Rg@ XY]F G>7jh(N@+i'ְ6_7ILLamKSeh4pܐ!Ѭ˹Se+.QٰUCj԰#$zR"`;CcU$4 OAҪ,-He/Oo殑\qyce1j R%9 5~­-:>QE1r3}fFEm\H\.m0҃+L!4ڄ`r:b3J-R@E1p:9E8tB5#Mj4@O⥄ 1lH@iMG-ah Ȉ9fQO3 OmRC 2M )2ʶ7CQ <)3aZ6iHL1ŮU?Puh kG.|N6m(wP4'd= GbސuFGi!^Hg*"ΐ:|ia3s5E Vd) YW.B/GѰc j2F<ʌ^WtORJx,rmtהdBfFǂs U9"èྗ4jeUEAsٮm 'h ēObI "@ Μ9~ۻ2i=P@ z3ʟy(^~O! 3czf?zG/8勸5vK}?&f ݇V~e|/-ֶX.1/[-tۄמhčvN%*)B 0Ae_[fSxslĉb}x3?ĩ0"=q{c/_šYln 11Ƶe3w}pt8CAr+v,ЕHFM.温jJ;p-&D!Ӊw(ŽS qN.BfZ5+^;<\?zXeE*S)PL;u̩lyfh Z G5kQ<2=pr\YR 32 vL^kRHב7nB~+oC j\.{gbc@#f0ifzcP bHQYh"U!b.R!Cޥ]+YcBp)iȐQd,؄ґd"{2wM@%(stúά8bQ@.9ܠ.3,Ž]|RM3o%3g'eȽu9c`f="6jN?TcXX؃k׮e/^'wcڸ5Awѽ&M78Pq/$N܃LJc'q"/_7.bvFS]`s  Ea3St;=Pdnoưm"bvj E~\^z/}|Oq%LLMjazj n9ڠ%V8zxjMp,9~ͭu-^}~wׯ^hb U7`Qڨ{ma3sxsa۩y>v {qa-c4*^HfBU(V+zunv{?l|XU`*-:*TbX ӚѢ @HdIH4W)l5% 6Hz%ijB,k}mPb4!t`<਒܂Z8FhMHth-fS#UFT%(P*L |{CY|e/)7TzM O3-]1dwt@6&M5(,(ӬXp594.df(RFD3 IDAT5m5 ;J *9|2 ם::kJOKBwPV?饀(UBZo,U: 2c(8cHqT m ]`I^=Fdؙ;SPh5E8uz59Z7ήaR6!wMhN"8i}Y 9zn;(#Cܩ!t e4p۬[ hMRw}+Tnb}F;~/e ^z\˄ET0wЗA9c'CF$dS̃ r+rR;#3ZF*gUFHuC "|#[m 1Vij$lAyۍ&իװXıv ݻw/zu6ffGTd0iZF#5C'5.P5n}:I\92GFi#88E}h>`C\(bz~BLeb0#01E9胸^ 6&[S= g p(~o]wZ] TcDre ոpjd csk {q`^Nkk[LЛpQ,QRc'0 wy˲DUUhZwH\Gv`c}{&jBUq-S'1=õ/ZDtÝ>jBѨ~@C1VXvO~fdg먩E¥e|Ĩo -TEԌ"5(HIVzĕ9s3$-+^tz,vvUB-LvnaM7ԕ/euqD$t9kF+ Z{bl6 ca3&r:5jYj9R`Lh89LNml}M7Ftp2Cg4" ") `h^hS혳L[sͺQLcrO {od6՗S}eu#4F- m\}(ܷ,bpc0HQ=nD]upy"vVXZ!)j"r4.?1]sWvEgb"5:}7gdŻQ* z:uk)i%ξgb+ jhgeXPB5NDhIq4tP ԻwP i!PXȹ~6Bftuz\6״D4->H (o[3mVA픣D((ȺLzLs 7lAM39aAz5vQZvyJ.j p+nH]:K|Ez>)3B==" >L9jicO`euךAP0(ׯ_#vVVV1E6Co$ &&M#iZ(/qRBEzZ@^_Ewj*Zz6aĵ:ILt;赻5q[ u\t)ܿEr&yu_c}N݉KO} \]~a] 3OAȣ\r ~L4,ܥʿb/F7F )O5.s,^}դ ޜn{š4Q΍+ResLwkhZ0qsr qˑhCͭ!/+b{OZ F"/-akk ObjjfBQ,!HWˋ8}r|3XXC9 :=] \je j`{w"`VH撩H% QQF8r)o]4*t>QWzHUnaЉB4v18ˌdA-9a><[]AQ45ءfHaYJ1!N)IՊ8 y:"!S@#l>Prnw]^ܴ{o-9 nNͶ!Ng+!B/iNd#f36\( SToo8*E3~Ill@Ǚ7P}8x.^R}o>i <2l}#(>*7g>.!P8| L3sQ&Zcv3&Pq[*nj!*bnf(@ЛhG;ɉ #ˈPtڠt:wN DF{\EN je&zPE6( +t9_K,T1pǟL/9133 'opGG FpHCPEX~Oގ?siE\Z-qǏǷ-\O/ c'@erũ J4MC{kQMzBԃBEX'"`Z3 ei|}@[ˈn lRAZ">rԴ`.4X`fiҤ:]CcSYp~068jVCpABQfGPѬ9wSHkr6S)dt0ېoN]DFRnݠ 9{yu ·3,2dZ!˨+vBVxXjt ,(ӷjb WË~P0aɽ$rz@4ˇ3]D eIMf$L*55ۤةјJޟ`OPL󾕒Jz͂ s-\dh `E}RIFŴɜ "jԌ *rmQ+؀Lt8yZStF[M#qٻ [pϠy7 H -Ec[PkfT-5az8ʈ᠏qq?KKK U`uq kFmBbׯc}.#Eo+pЛ_s?8wG/E{K 0d!By3}m]_lH29-Pib.e#dV!FarsAʼ&167;I-_ЊiMb`\SDW0dMUtVТ2XaJ1Qq 3La,WBA%ʚ'es'~8FIe@QBEAIBF7UzjD2WxYkkMdYSʲ5@n7r5$ȫN 5۲ءlUj "2);# i.\5MSeEqn|2ĴHt g&3P t55E3&-/uM^Z Vi򸵦%mMtTdȯa5sυ8!hsct3U좯57'2&5cE* v71wRc# BP(Yɟi*@]`BO5rDRH f ֣J 9 8)4VX!ȮǷCDO<&&_^DYSFdחC~{֠ݨ*FZn6o0Ó%=]Olll& ano`{{ nn @h[kWbbr >4NC XYZǩ;o~n?^_x zUU m4N0/dJX.ɶLU7% \F4敬4Ә6Iٌ2)GQ"4 9%t D73'Ն^Dpƌj':~J3ͨ ]fʔ6@<԰>;+`S!RT}Tt{V7yyFA6:`pdqU-}8e "áujM%SMwRY'6-'s 9-(ϻn_r\үk&rw 8ᆭ˝yAn1悳746C+)x[LbKs*=dמBM bt5 6t7L7"GgϻyUG)SstMC,sWNk~b׬F}Fɳ*w)i+>i8s$]Y{3 $=kL?ܽ+'dQ&2xF9c b5a̲?FGlzrps,ojsX3XX*zlXPhJO3g LSr]rmLsI1 ޹) kOMs| 3b#I)LETHHHfCv M}!vcLB?_d~Nݫ|=< h!f\'0U62f *x 4dfk\3KxqYH`LJ>Uy z8~8n喷~/^믿y⋘ɓ'3ࡇ‰'~)4ߧ~ΝCko?p`n*^j[݆n#Gc\va5mn]PL*VW0 _LLn. N:DY*FFG?gێo;8sN Y\d0z{QFfwL<#uJXHhBLa<=>8:'; *J`fX&v89*#gy\P }n_~ IDATc w:'!YUrtF:mǶrT(W2ycWt{r0fQgs|ZmPsE"EqBrГh"Dj)H5f3Z/`k FiagJdqt#:Й3&$[yU~28O99P:zc%ϕƚE玻(,B=nϗ_QNnXgD͐f߲ڌZir13dag TJ-oɎȢPG:8(mUNtSAf剱g!Geg#5"(ʠ̬(աdF6ʦ*MP7ѱ$E(EUel0s$յޜ{AY= Fa*ks @c7BwTjdtyN 5GsfZ035Jlz`b)Z|A"}jsv ]G1hZN~uݘ!}N{{`1Lr&>>H܃)%6NB`: &Yt #,=w{AކqqU<ǽgYuk_VVV033ݻ^y,'y;w>,>Wꫯ[nO??6>OO/} ǎooСC?c\pǾSO?v.f@Qh8*%ej̈`.66xfgg~Dͺ*0199F5g?Y9v>ꠊ(%LL,YЯdHF&N? 3PFND`gf|N 5VL/ ً•1*Nܕ<22c37FfC TIJAe2ɚN!w}YRᨇLP7Yk5XɮfT&r" L?YP {nJ&mTrhyi]U2يn4fad/F6!a5lЦU3W:d+:T)eI1X1GBAbaaΝñc}}LOM$'c<`aaSss*FbU!յ5-0+ױ{9o4Ӽ&zIE6~XػyxWO㮻OԍzBDhM.9Hj2 =X\D%߯)nX u jΨ#&.3LwVMut4R0(jS#TfkY7 db>3L! Wkr 6Ƥ M&r{u1w}JRVSҼʍ̓Cb!.:wZ;jzg'f! :HbCsPjHBq3/*ٓ{C\_U_ɤ(C䩃pzZFb:iNAV?BM7\4(=bq$uQ)g!!z1g 41R+Fю6n`}c#ʲ ) ሜ^觠wAwFVZ5n \?;-9r>Λ666pg/o9<}uu{ފ]{1<8{,:^qbqq׮]ŋ1==gƋ/ۏ?=3*qɻgnǏ ν|eQVxЪ=.Z* ,__ _S'" wq .^BʼnnGoʢvf(i͵CjRƉT9EH(LQ-@L%a5ɂµj"dQnlV HS2C:V] V.))jAqQmM45"CT"Fv6UQ+\jKzUE[hQFaҗݡ Ә4 XXevwce/ص]'F1F5JkVգχ{LA%#\3jᨥ 'Z6rLS#Pu1|;Pp֣p 3>)!RLki@ӄ(&wv{UMͭ那JV5fH޲kykV ,,ݴfq7~r%qsM\S驡`9j: U=Q}Ip!߇3A{ O9e8H4x\|IYang?D4*YSmgD7 l`&Q݀vSrդϮd.@(p]wA}nPzw/ pGr1Z+5R^ \ zdXeuex-7@|;d,DpUطn;A&>&._YG>:xf7Dxʕf {AQo|2&&>W_} o=;~}|{íފk׮accn7133/}+(҃4.YZƣ!Usrť%e +R@(@X]]2ۋC7OFdOU}8M!"ܹ‘#GRDi NuTKev}I(l.@ݘBƬ٧_bSe{ep r Ҝj*V@lLߤŤ dgF`5^ 5BGEI vZ2k"+w!z!4;tz&Ga#7 ÄzDH2 4BRPj|zϘI’]jMTg@n-ADh_=ުO:sr,((ؖe&_2Ň5tzxٴ*sϐtGE "6%<4oQih~jԥPpЙAHHÞ1:41r5ay)7<1 QUPxU1 T29}vpEyjEYަoJ$z7<־5AOQ=7p 1 zݘQMFsB>XDM2͊BNtLN97i(\?B>khwAGOO5ϖ@K_s,M*ٚ682t|ǛC70bggOK@BuɄKOެ/jx177K.1\^pxGpm~w/}% \EzS3'?󯾂/ុϠagX"rQ0'Î-8{ɲ+ $s;d(Qd\c>DXYOYBJTR,6~,F>DL0%OJ#CY]Nı]@#W0sh\ ']!H<]JH)9kv\зݑR) Qb0:d3JNM)f;̭2B aFi]rfr؈Pӿ+6rf\ 73e*hF LD.M<$A, 0qf%mޑ!yeֹrybN!ZtSClmt5+! t(o< LSi()/ҲFH1Ga,L>iE64,F5ZvWr+RތXnH Ԍڨw46C|sÝ 2N΃˭,@! %KՏ~.iF>r8_ܐCX֟rqcLFG!4œc3ޭ,uT-ޡcni(hM+<=mL35tcv}5X*zzsn#ߛ SM>fYL%;TFzşMdp<= T0ߗiNv+q̛pq=F#>gyھ .bm`rrO~ ؿnnVu]6?|U˘x8Cћ[15He,c8Yx:ckskkhԧ>v}sAYTUȄV2V J<8q>_>p>F#Īnʪ8dxgh9jVfa\BPg2FyXb.Е\js]ѢrM]T w>ˉƌ_\EFTW"ub1WV8;q_`7d_*]I.YOO2;DqlsDKً[Gt$ 3F[Wؔ3 (rJ@Z1K] %$~2\ ^Z6]W8h7h nӁ!Y\.:AvȊkR*f1 E x}Lϱd Z*xtӊlD" 6.$!`sWJ1-ba31kJBy3.pq1EGeU WÚ왊 +zF2t_LZ`&s"4Ex+:*HFn\HZ`#+Ej+ETcFmO'c7bsi;3.x#;3b6V.fNFWԜL4K=uܣFy׎rv 甭H'f::k,5SBv]9rb?$ՈYklN~d'DRe ^{KK15D(^B!2>|zϣ(7}tEQ7$[@ƭ݆~>bۛ~}r#B.x}kk8|>я\Pٙ3Ft-:m֖WN\UZfɎ#Pt"4޳kklJnD3(&ch$Q= TbdWeBMU pL}RWJj+< "9 ٦p,Rmu\V*ssY$922ō*mŌ2YQq1dhw54 rnA5J,g4\#3ȊtW(w;ýQeŪ2؅,͵d01$%fHsu\֢\/YW;prTB2'CCHbpPY$kE-W;J12|ih4p6HkZ!ɔ n(S=N66w^ipO3n@n""F >=n):XEx}S%LS2k,oUa}4JE %_PS?; `#w\3 9/c)h{ԡUڧ)l(:%u~uw5ƑE#\ܵ%YKE=6xk.G*u124\[ӕJLA-@,iFX ٳb3\\lx'Pr1Or`{*¢$߰ښ"iVb7Mlnoj-@N_Yeܹsx饗p)mw}5<8qj~o C|k_5_oED>9z=t]W̪!}Y>'h v(od!c2@Qh# ͣf`qw:kHfnN<#1 pdϙϘ4g\YYSO?M&:q"iii zx^«|;Շpmm x{NMM'!Flmmȑ#e'yu]|CK/^>Ϡ͖^ՙ{͛sR9JhDBr$@]]?п_:nGU*`02CbMn^>) *O$s}z {8uo|&iݵR7n'>ӧO[ܑfv`^uq@{ؽs.z~׹sou\ȳy|?};+OAmTjb΅/EA f3R`r1wCH^ ronC⟹B@[Q 8]rɠRXLoi2uM5Cm.2({+,ki Ȍ0IHFђd~[$H>XX 7Z#BA4zq׆i8f(4 [eg45!DCG(')+Ew->j}7O v#YKAP1,<>=Ѹ`UKFQ|2іaD-;kJs;{L v|?mj0.nT)Ac IDAT}wR)kz--t\ ŊnDaP۰UhKY-?*IckE_:73XYm:nq],I 䲟LĘ,`YQx&]< Z%TLĐ&Рeネ},jMHr]ZM⠌2Oa6T ~" 5Ho%Wʽ0Y|Q/>Pd P=]XYm4}JfVCJtF:+ah EQrgsP/\s/b7 M]O=\L?ٳ6P o-:~c|Wl.6'tnMDiG%?j1n8 >ќr2)`D4l 3cJezus'&UVc~R"IwWx*B h G~) 9}lͧVSО(+MWT- QՂ?se%Fuh^c2X?6]?Sje3c5jW Ru0I:-dV-54GmۨfoZȴJ.DlLsݳ2ELh4hh S,+awgKZpӨtu,#OߏbD)kJ!9`r٤siFI ƬsP=cMͨ+5NO!%"~ "^Q93E EkJLoIa m6`o%LDkͯfHE;C3YE -:}hum!Z FWJCd ']Qgu QHdž)>gc TO#M? H< nȣFTܔ8j>a?3BهrZX ^Dq8h觽Fut(fޱ{"Au{/T3xtt/~/0 c\)O_;;x @>׏xYDGΜJ%.]kW=۷na"&߽ݽLL/ЖG'p]ܿ e§>I\zEN> NX`og_/+_>/>3g`nI; +t}A!]G-(ݘΦ4H1ENQm%ʄNdpY~H~+V8f*fRi@t T5B5@)*ٰDT  0ˌFfP0ō%Qx>o ź]H.!NClȨΨBtp ) 4Y*-V@X-5M+E JI#dwi "Jߵ"HtQ.6A"iHA}bwOb?s9zRj{_ 9iux#ٰZb77@A3 V#ag:XsTŒyu:F9/a"[c-Hny:k? l)L:k!i4C7A7cg QRzI`hهf176qb0);LgߣjH$RI{J J__f{o`NsBxWq _׿u\p eH.]B? fTz֖+w. 9Q7mKs{E^[['qeYmLXwϾ^ /'³78:^aVl"2 Xx[.Cb_E* bt ]Ḛ3ht 50i:unCR4\DoSP{$1u j5gڛxS?7C ܮlMwZ͊O\Cw蚴 7.BFqthZ8΀lHzԊ g)zCdzDc\J50m\cKF%^4̨)W3((U]cy kQӍfu*.Y4 " Xbs̭ӡx6<&]2_ jShX+tm8454Tt!,ƨgHKXQQG%,?π2Y@:\1ru*fUNIe RKچrr&JA[" ,j< {漉 )V.ڇQ>h7ukK)m("#.Z aMY=P'.y(BftGRai0C29'^E@5LxjftD CB ; 2`N]"$c_)ޤyRp@kŢTQ@1Z$GDH3wd1®gA`׾78<:[[ 3o|g|凴rM;znoo"}wwxbSdkkۅgϝ嫏b|٧qժOܾrbs%T^LmȼK/}/~ŇxM?uقO|Ο͛7?k׮{PmXL6P#Y>dhR4fzQ#36="^28ER@!{Y7[6a- ̣Kuİ ḧ́Z dE 3 B!5GZ,2 І,&&b/I ;MsyH᎖t*Ě@Y@bA'h^/ui0q Ϥc➘Z5XFCXY$]O:ZSXsdʆ}v#b ;* 1'0ɔkM^3fAGZqCRhOԨAJ "TR%%ȅ[5Y(1'0(<և5( ȌbN^|q `LP@Eb"I7T^0B]S_J)v^+fzTc%5*dQ쟥g4Q2wĘ(f8DQ-CwMH OԔ0Nꃞ\EEƠԢ Ҍȯ[s BsQ.5a’U(KbJOE׿y_j‹/iM#<;w7rf/o6ܹׯׯ_j۷O୷¥K03{={x饗~C6{9ׯq%;xGgΜy_pIh88\ڍ'[X]3fӔO<ϸ{6p |X1_?uom!؜޻SxO>o7[v0M8Q:hYlXMAq Bzt%Nt'#hjL!u&b+9lXhf >4@JX,X;eYѼ3PDc(0 ID~畵,"fӏc7~@t7'[zli ,кaOTp.+IJ!8aZ yM',X0iCӅr7;5VP.1[*/ݤ B}Д'Ƭ5Z wȃۙmּ2{3H- } "wT0bU 2T'sLO)c2F.:SH;Ra$5DUBڴ?C UEM l({I6paצk<_H:-] A L=7&u1[6 dM-s‚Nz^[4O XP_3{P9M)Pi& wB{|ža cXS:.Vnό.Zsh+q/tiiĂ]BϖHC}Xl@I~.D4gjHЇTNe6xIR s<>t֠?#[{|0čd>,t&.ٙ<aL0zpAJDˡ0FYE5[wJ7&JAS 4^bViKqq8i; ` #VK3I틡Yӧ7|(M 5_b=3 MٽyGFHq,&g%hhʘm>yZ2f(oÅ !lEgovָp3CrovɊ-(s"BdH1٭5_&s,mM%jR+ܩ-AԤN|3yX JEdʘ*F UU% rW} 3+aV: }Ap(f[l^ot@1>O7s=CN:z GGG{/\>??ë<38}|ϛ7oҥKWU\x.._;ww/7x~pcy pE\~"Nnᩧ%`ۻ4bb~cxatzQ%VabbAWK(68\JA?: ӋX.lT7 &4N L%~yF$ypϑR\)cq ½wՂ5eQdSGL]O`aiGȯFM^Tw:FQd栚&sњPQҤ2əVҭ1[4G&׽X'#E:O1ޙf!VJh.FEmzqǽaOb}pUNsT7* 8i@ԛoo`ygm:VCuՠ,p̌ITk/JK26Dh)X5ާϮ d͖<f'.h1>.ݕ2Je>Z>Bk;V΀B1=qO q\ߧI&K،†я^駮77qU\pjڌr;NSxmgϞ?y:pa}G.= 4 d"^H6N::>­e&*ߨR:M8 ~IP0Bv"5/$QٍaT9 ׌t;펝hPoK^񚫫LtdDhy`{/G|=xCid0̎ 57ԋU62-M *-DFH(F*#itF ٔ]#IWDH1;L#R%[瀆F, w2˟"P0J `UBCؾ{CZs1%Ѱ"ja6Ċ?c ?7M&ЬJТW(z!ִjyb4c& -hoPoo2&E4ЪqAZL6~H خ<ͽ&;S"̀pth8c$({50~Obh "a"|NaK~ި\Fvki1&Hc+z\g[ ~ 1mP{R_& D$N[o${ߝ5} ӧP Dخ]Ml*=&q drM؈sx T*kWѼ0U_vSlYB&gie:j3Lfe!(nM4=}/Aːm¿w/"O?M\v->OQh`}?~;wgϞř3gBW_/ҏ|˗/i?&Cիzo=o- fiX3Oc /yy!6VcL h/ loF4s>W>E.]X&MZ@"pb1rZף*D&2EE5V IDAT,QC4нOmACuQB9iD|'$wӄB#(fꇈȘqd@&CLOV^Gia}b)pBuh3l&6Iގ dTɵյ"M1k.o*@D!F6bŶ6,ؚvS9պMf)ܔ>,ڊPt)L}Є~ )Jf$ WҪQtF-6dzIX(&P@̨ZR+ksP9Kz&͑`MŦqӊ>qӖFD}VJ#(% [K,~џ uQ7(qlbjv(:diT0Lɨd}0>:?aS5W:X%t~ތ-\{IZ+@w1j m,"Nw8F/ٺ!&5ښbaz9o[A JM q`A2dHnDEŤ>%;] M$>)M?+. jYHq3h™r!Y!"*-{mؐ[d*jg|±D9v__aw'XL966;j+^K΢3X.gx1lll8{1 3 Zt}n*x+4#W.#j9{aU;PL8>  )b+F%⃈Џ/AVM,P vOI(EHF/A< >&+aE@:Fn†I}nLጪESGu}:6 PHytd} J]JAծzȁ9Mj i &h&Tljw*mШO]ߩzCV]9RF @2E,a@DΛJQk̸6#Niڣ䭻D"C)~/7!کli0ž~JQ]#(1קu:hA<_ivd#wSdegB]"ӥ!D&@к3VÔNoъUgP%gPE׶~7 uVZM[HB* p3iPlHWߓ&A)eFZX0FqA;k4YlOmy\ A!1u.ߛ% zGDk&QTcHG[hlFkC u^ v]ߗm,1|Pi"atz?jӇUEdhdTHv(g⨔Tgz ORTq\2iE/-:!c):l5JI 8ِ ^4uZEZsRU|+`>pF\rw9g=~ `kMHG%_ Y'N=ȩgx #p rkY}:ݱzeǤwo|8L?ZÉ[xӄ0ڼqaowjSә vwpp_ŋ/s)iO3?6Ny‰dE\[#az^)PKuhQht[0S3QseJ/6dCfj1½M÷́))t,!w Dn.PA/٢Җf45WDJh $=uMBYIA1hʘ4uM Y%&RfQҍhK~Gdʢ:zj2y\BvOp=[gn]3܌,`Xrh9y瞣j'R/ڤuI58MT:7͹;.O3of%}j[Qe'fd P-`Dns\/:7uT6u= !B]M 7Cąх`sCF[b L۩ Jho \c,XrfI @I7\IRԚNuʞOE(ܝ ԨeSyFWDQ=\K\hu^W̸#\<%? 5X,Ic4Z}UuO}]SR Bg3`9(ȱ>j98Ze/Q+f({E~fcl+r9wxBARb=H S\!F DC:%ڛ>lqMuuSG)T~Cgp}Sf [( b*d,6ynӠ Ld.o;8w,vcjqqrًr v3O-criڝRVj> E"\LTT,SJQDZegQYr@taM(IB>M2M#ĬaŮ[ɲ{I6c;gP ԅDb˷;:30 /^pOwoaG ޽m̭&bƄÃlor“O|+xع^?'sրa4 L(a塩`Qf9gAq GW|C,Ok]=~ P(ւ8kX;(CѤ~Ch :mirt}zDχkajvbNAPAQ9M*lvSj-B 4|R~W?x5j\vD~ k%mϢhbn<3jgAz@"2Oq'=Tf Z_GeڑWoܸ! }l(&+ashxkdŨL7KVi5I(0MʑH.vO5͖X= Pd"toAgAzT:布=rٰ%7"타ĜFe 6&h f0(^F#8ӣ-bjD.ͩ垙)vT#\:Ց2^o1Xb1Uq2iZp-hzjĊ 8:#XXu iҌuybⵍG(p1{x6gyB]kQfB4LEj T-$̘ b[r$H7\W:oE0F\pk 4K xDݡWY%jXq@@/TK ԇD9uz7^+1q':,~uzm,W X@enI (!w!"ibW,PNe]O@ lEp2i T3dRQô rk.8!lYA6̑ V.t2GGx+AW6Y0`ffWXl0Ξ>rm )^3O ܾ}jgm`{ST@]a11~|ر_yqM\zVMX08"- 9g (TnnHQ"3N\jC JJ ;q$PP[h"C5_c:-8vy-MJ"*Dt ފa#źO㦖=jV(w 2P 64(WLqHd(qUåQ i5a<9w #)3 AUxQ7 tGxᾙD, S\^MyHvn1:d>en\ aD_-Q*LH߲5]'ATjܳҌG"xBQjAsk-:RM1*qEF-4pJJA/+j1֓ Q 4*fF@!F@N*g \l7C.h ȇ~}.`<%!7+s8OeoXñFsji V5V"gvx6uȼN<:] ;d5dxH V9V½-B1E .O$ /'Mb %}/ H$al(Q&4tXFi%Pf9q/[4U,u4}lnS<8O'0@:%^6~7DHt4Qo_1Bf8{>IKA` ܐI*PMйͺAࡂ)bQ+8R0z?՛ H1G=b; u eZ`nA v+, m2#ʴڰ;9:11I "30RϊwagŦJ.PHE("U]}چQ|V]3>2(1U E@ke[-Ubj >\@szg昌ad!H[U {WΩn^Q\iYkju6lA[-hx,ζg|Hɕ5{^i-0S1t^_81ȐZ5+-"fv7sIM#UJqZb{w2P34 !CTIp RTr+CQdDxYϐ|@e~SJY:Lk3k{]m2"2"Fxч$o!xzZL=稈Q`a^Sbٴ=4hnSɊi#ucGTbݮ[m3TDAt]8b\?CZLJ? S|GPMw;. GJS=vovaqQdž}:s79+B f(H##Tz11 1%X$9Xi’dע .4["rFsIG9SiyaKcFXP=e-,L ~Ƴb0cdGb[fXDD͟C%$iS)r:Ug% jQM +dV-[3V#HTR"}JZ8q$zC`b<܇qt|܁`j=8ZkK 60 Οǧ>I\vaS~9w/Qx1dBc6N,@D['ø#AHG@bsݳɡ 1wӆMFWp L@+أS-yRCKu`E±Pmj`‚zHzbԱqa:G56+f~zAz3۬xIiFJ@ef6mF\DFH|ClQ#nmND aͯ^)!Z9[|+2 3v!^(Gf =/Όbh kgO_8OtBiY+q o;"ق{ZZnª38[sh`':Pu2G\kYz8A׌,YYzNߝBz@ODuG^)Yx?JV+-L.a "1&3P] m!̑^yVd踙@h6"߃zpi'$EM"`Q'GcoIb 25c-{F V>Ɍ*~PMidU\Ccr%u0ln50$A,T<,łi0sсrEe{ԅ84B]v*18?̹Xc CЅhDsl}Q%jGM势I (R%J^ /5GK{^7Jh t['򟰚Nl<1LtMT#GM{ZPT5 ^+\R=̦ IhbE~偱Pp IDAT{cY}zV 3K8i@Vٴ?A% gO 8f.E)m1J7ɗO.4,n+n7y$\Ċ}|8s]w~{88:ry{;aMц}Fk />O[~㳟ABel!׭4T"&ly*U18h):5(=\F-Yv$ xËqdt%ٴ)tt9t}D|#SZPFNsn>ʆ5p*D-Y|+6)57S48Di+, wcB5Ek%gޒ7״u@q4^7kߓ4)JBܴ Oh!'0'I3k];{2ܳy^PIEUZ|f`Ѳ5* b*̒anf$p՚Y̆A89dj1)l&MyX<`*#hԳ}/@UC1S0^mu'3D7R||îZKrX3i+Wpt'V~]̫c`w 4Ql߻\pԧ3O?~FDZ1`f <ϝY(=Fu;,uS'[m&ʁH LN~b8&ЛJ-S 6s3VQS҃h쨸ićLk֘q9HE@bQ!;f}܄uW&KlR>M>4xAW Ż{LYMHIӄFtItDiQ"LS["LZc&4)8=( a>& )!]:vn6vBE7((YE_xD|V:$]w 1(q`#IcdC;52M^,Pbs; ⇠\,gI83ϘX[GT4pжk1m LQ1q& kѽ ZPa+ތsn4 5  ktm. ( d%8Y L81>fũ [x5 =0&1DRfFMja4I|"ixgl0шZ%Tr3&MT͐ʃ ɦ:7|d L I!zrM"\icFO65yNwZܶ86t[Ί#m{1~( 8jvT-aA1('>ԒRqrvZJiU_24]<~vv̓OⅳK^-`<Mk6ܿDOs,66I^<, M,PZ=OJ!m,3Gǜ< 8A ly}9QӪW.4cu4RY1Z=(SA\ bC' "\muVWJWߗ4hMk%<[VvZT\`!]aQ "BfŖkNݵZSj3]P4efrf9+K5L{3tUFE}AE"HtmCv4:1ʙBq.ZoJ(}Eݓ5xbTI6mSVyx)ɣK.kiCM+Cٖ1h&CH u74%S:]"F"oB$IO/B+v o DfN_YHE|-1Ѷd֏34K-7ĚhN}il!XJ8Ql#-4&qCw"U_-EAkz0~nX@FVy5MxFuס\&Mlհ' šNJ10IYckhZ:`@45i  t Ms4|=dTUqCfRmkM 53!'t3 A\5A4fs$NSDGS G=țqC%Ϭ=J^4Z~jzѿ Ɛ-GIpa&c-~^A㵯|'Nli GG5Q@sEdn]7a]…G^gxQ|53F`s  Xl,ʌؒb{ՠv+Ŝ7FelQBc ] m3Z]8s2 RHzѴ7d(\sHDL =[CaaޖŝR[w3AZdRh m05aAi!tD&QE Zcwjԥ2Jh@EH E8i ׇ:Bᖜ aF>)uuȪG4)uMNdٰ}GPO6!v0Q@^}'/MhR|& 1T pvP,ٴ1f1JsF GĔ/M2S"T (T؝N_%?"t='Q W's$7mu~a#}?YF | dVNk|xOV;8rYIG=U ,:?vlxQqHi6h*۩qfvJ2`N)FQyW҂ e{ޛP)Ne9[>`ٌbuS"t^\4.HЙR#Іz"+~ak5TYT2u` +n* 31+#%Vf \Zh2C^du@#LRN7^HHJ IBnv-e()Ɍq:ﵛiTQdOA>ڬ͛8>:Ɖ Hk.h j:BRX$!4r|!oufa MrcC 5]hT PXަH?}Y4uJBM{ΊͩWc(IQ,G9~TR8P@-(^EԴ`wo}E9{ [nappt;q:McgkNLwzW{L f07BӍpM ֩M7batTʩA N˓Fh:gOB`.ٝ^,e6TknСhz$i`(dM91Ad0#7#.kט:"omjH1HFjlj ՈA&NY9:f4MdLZxdƊ&iIS+V }VwpBl hCo ג & h%-ܥ{!FFn@5GPr64;9ř;l lbMK %@`g5N3˃Z\#^RTPuD$gnLq4%b1C rwzIq03;Ol}N9ZȿcB1b G-oe"t *e4{1p}s$tmJ^u@"GT}7DjAF֩}3BB\ki$z/ Xۿ DR$>\(ARI`9Л-72d)L.ۃx@:JYN ]Bvj>C){?ed$D?QM㧢 gj;FQ /n='j9ޚ w9FbZ&Չ rHO:!sYH˪ wsR )x$ 閄+Nj7m;p},x70-`R/4u{յR,%Mtp muiQPB4@!cM+8NʛQ2)Z>ƨ2N= (\gCl!/q:w8֤gi̓PqVRc&';qEKNj^aooG]'moGqֻ888~ױg\v} ?쿇深sgMCU-oAaj8s4662q-r#2HQKq3>!ZQrߤ5qHV'B]B49I"AaSNb;N;,Dw"oXOI誴Yɺ TUL*y Huר]XNC}0Ԑ? 6LRa t Z } $&{X5j07S1t:"AMdƳ TS)zkv!*p%N^ Dzf N 3r2Q+fO@rC^4k:-$I&b=r2 ,-]Ρd͂\z XVWhrsT@{!nJQI2dsrꗫ-BYqzl8`1d0xb)$ΦͣK va;y1L=vg蟄f޻Z}}?k7g{m' `Lj(!i *R&HIRQUDmET()4*i&Clc1`wۧwxӳ ٌayu3V{-;tH]%7lwl6[;/ 7ԋ8{TDL%|pdI%\J6!J88ƛBL5B5/MVdqHXsAbI<(J54k %[ScF*mK' ھ `ZQej82 KR"َAqy4 6B)e6VJp #!# `hxN BHxk1|M%j#89܅}-"yVfvŗN՘4Kip[SS#$-6lMV% 5U8k?liWQOQ+,'} @Rz@c*E?\qo|ӛqϽw'D o݂fC/}n .;z!q=|U%֪n muvaXd*vI8=9J-?K_B,B4eNu[#lJk STX p0O-i@8[.'La+0mJ]&5٩d>RLhGɔ`tp| &pȎMGmùFmҊoe֩A3w|۶M'O$Gb,)`煔X8CuB .PyqL$J̍RX> hƭ\sI;\v',ŤڤYDc4EnhV@8:[OrQ5Y-l4i QM>&Es Z dx/Qvu+@+Pఆq6&5f %!7ɐ1T"UZJtp \[ bMlh,unrFpcT,[&GA3qYԆ BkQ`JLvAZc-ǒ{EͧTSJ3^dMeD3[ư6?e}"EvZ.ԜORz:eɺbRyQgu}3JEܕiV>R!$Jh5(kg*.ɑA5ɜTCo>/p  X9q"zg"ąþ(vxsOU<{|)lw;l8=='Nq-%rn|o\T׿q…ݗ8?']\= vYpU4 q%pz|L'[fejRKz` IDATne '5̳"ݣӆ⿒vr/eQ͡˘')b_ D bg*qi9xif$eTdٶ}` oGxZ^@[!? MoݙufM.Y;F@#eC%s%0GS@0&ωTB/~EPH9\cw*csUY]rXϛzCgbQ73Ë3l S$F2;'K)xQZ-x BXǙLc)?o/f,Os||vOҠ>U+,UB)ȱb~b*[xze $93^:b} m@"ILdԤRcTZ@45@4- ng0&*,DBG .SNnxqx;މ> >`͂ӓ-N6nO==[#Kn?)z?#?'|/^|o>_sR< Wq۠Yl3EݲZҕK8<$36{ЕT(%xy)\Y=NN@RF9TbdmgoEPD1 z,bR@*Ǟkk5Yݏ9%I}J'kkM_c3w>jmh02\d^)rJAdD;zt&jzHQ ܟ=HA0 lFt'U6),kLV^DՂ  zy_rFG{xr=oTы|eM%eέ߇bjS>/n1iU)~IMQZ&1<1$ƪQ#N (hsqI 8Xx ;H*ޥ u&%óEk\eiЌ* L9m-}:ֲMY 5 +,#2Ċ1>z i&M)7JGr2 Myҫ ?r(2dk"Υ:SS S!#rFeCP3M rQ CCˆ6, o1 %(Ň n4~4SnA rϭ[9=vKn^%)ByhLa ևf)RSV_*Ir†Pm2r`@L8JuJ@ l^y/,c{h2b`CSS?p(-ܴb[1n|=^l >k\NqG8>= qx;Y,;lzWܾn^=|~/^k׼_9PLfSԈB9Qc!b1pso2&'XOfWX gۀU(3.]zEuqM.7ouοͶ'PP)T-h&3( -砎wⱉQ.GϞPxC![ BnmMo Mp}9]`BgA-Dm4nH³ u΂m8hĔIЩzF[ *[6bX2S/p .%Քq"G-`.L̀;˃%w 7eFSAzyp E}V8}~Ki6(6oJNwwH ,urKmv‡#Sϓ%ۤe$! mOјօ'?!hYDmc(,zޅZ=᪞MÈg(!g)LAR- uVTԸ9)ouy|-]ܚՙDE(. SuPz^2kN6;o ŤZF`damLn(p=ln;21SVml u_K"ϝ)b@cr#QQ%lG/K_,c3 /}wbo_ȿ /lOwXvlv}t[/vvC_m<˂7<&[|~~,v/;Th_/Z}?í[++:JƳ"G[$3[\t/3\8IIh`"z#b8Hn%Y!ij+r/#"3(?V @gP)d[.:hFuFIV! ~VrKNxm\48LN,AJb)R&-{oCU_jKVHJHL(8jSn!6j.$ i8] oqQ] o@IIႦWQQqE* Q]O #CSFZi񇝉BDeU|^[J/qq64?# 4Qw吝&7ԞV6CwpJrržp9}W9Zb bYh-Aj[dcc׻Ikdy{?AU7&Ƴ,H[P-?kur'b%#ո7voQיY6H Vg׳ 8 fSQ Mgֹ?q _nN׽xsPl D2.;B_ij%0ܷ.9e Ǣ CzQ"W"GP$XHQShJsC0\MޤjQHunY4iZGBzr')d\)8>d*q-8ӿF %4ȐN:E H \ͽߧ~l"sψK^T4eOPiwex#. &xZc745I4Y밋}:ʼnDX%?DTZ9H9”EhU|lyBnR0-%hX zsJwiOnLFV^R}asy޳#4 Գ` oQ] ْD\y.SFHW7jJa5lc}7xV3DJgA[RwDVk:j UK_0*L1*9O5U QQzU@XbiDi M2'EI*Zhl!}4mJ50f N̷u.Mh|FVY$Qm^y׉@S](e"b\ESI )t!% /Hn*·d$#?N"V n^[oc}>OV4ұ '68=9 N6[ޱ, vן!._?Gyo|ӛ^/ <3l4*XnEv+/< ﯠ`,,_u 76! + Wh,"_+sD93Nt4.oj, 1C0Tg`i-{.0MٺHAsE*x*Iri͊f!^|aqQ61L}H=v8UrRr钞@dqK[+ w$|/x4EaOy y3sSB5=Ƨa.=t߉= žeᥫ)n,%gcH‘oF pg6a.Ils.'grHA[0: rad^c]q|x U鍞nơAT|Qə?RˆGւ"+[ gLbطM9izs7uԂD''Yl#ZmN%Hg4Aqv="yAK&Ne>z5 Kk]{LJ<}įa[V+l7l7'X[lv;lONa` ?9W w{qꕗ_ Mxy'.n ]؜;܌GI}Ծ2 55l;<r2G-~}Jg؉]y1C*PƬR刲mHyӪ*bQr0{^)5RCR*͐4jWasl(CI\!E H ?aË&ـH/>E隅G<zBFGn\7nVc7]+WHb(JcTݗFg/"1Mnxk6sQ%JR!@eJc+שſOsu=ȇb!2HH s&J>_#qH .R Piz vIQfFNCmf*2[Ag|={ovmES.匕Y~*Q~FNLX ,PRxr!dZiWE 9#侈khl[RxDMj&HC=U{FVܫѴ:O/k7bjz\m c 3mhyedb/9bCF(YkHH8uB/,]U۔3)ҠQ)V2r^J; 0kDekn@bJ"xI5.]w߃+W/۠֊DLe+Ihp7ϖ)ANgNkLQ9%CpOP+GcYc%ֲuVAb<Ċz@6 %B4fJV4=7")S,nw8>9t 25K[cO~G{}:l?=,v!>=Ac-e\r]fya)%(ij |5q\Yr؉.UrBN_WBI9UEǐ&we#E:Z Tm\.mV<>3-8.RW Ajd|UKu2t{HĕIH$T~opY3ɣAƵA^uR491Dih`ˎcJo>eG G؎pNbJ!tqDٶvb_ ~ +.1I`:͞Wڬ*Rǐ?(ȕ#_2=`ŰT5`3v1g&1[< F6jb$k4XQǮeB6ԀԸ4f@ (圉rmXaԣ^!UvIB 9ĝf8I2uTbӓQFtee'1 Me&|-$#8%IqXGGM{ޗV6X7i.~6mr}N> ?a^FC ܠP(|`/s6ݜҎ4G Vm{$ xIAf]m8٭M-CR8*NYF,O={|/~/e`u,],['Yqn@q|x݂oWG_DW{]?<as4[qH!ʔ7u_a2z6}@1=XpdT`Uq e\OWQ 5Ay+X<(.]=+w̻2jCirb((eí4(!ehB{,VYZ]ϥβ/\=+ti"nN{~Fob .Ja2ZbLKMAve;HfKd.q%|I$8/<ǔ1-M*r8Ór`ZFޣ!^˶9O\JjRiE99ަFn3msHyQךg#ZbS+1` U6$~V.6JD:fIʷ`@qSϕ R`W i92@Lès*  B66(8Ȥ~^L9j`0ڎ',7ER8 IDATѳ)A& o'S%m%l)EZ"6y"+۞ ͤ i{0irG -f 0dڽ_,R!R#+cFSǝ:nAT'JQK2V3j)DZr)ֳ,̚һ05Ged5f^`NV\b+gQE^Bf14Ԭ)؞Q $eCZKZ' lZ!XT3SM";  *q QJV%U1HhEl9@X1ӯcÖ'E}wV9A_X"]TAy퍌o($d34D3v1y/n U{&\< CG~[+##҆<5a\d_#?q6gW( 1bT (F,]J T˘x+kPɯ?LXDK78.]f]liBtΚ+B1oQ$-I*_3>Dٕ$"9$5mr8F2dP:ĠB_ƶ'ASΉC26-ǹ֐F!#QQS?%4<8=Mw36 x ].<3NNNZlH26* 2"CV%Kࡇ?~/7/p^iwt,ːt…sznݼY=0j9 'An9qAU /!_dݥHs}"FgI.@%p[p%[# pot:ۖT]b%-P `(%T(!3f-IS'(Tv#Q?кqh'MR :!.TUIdPRQDTȲ %I\RhlE@3#ejQ=ɼd{E։1xVٳPdzMݭpK嶛rk1O0(dDI $'QOZoj.'leC#D"*ki6lKrYIK`yjQ`AǠ.GɄTraP r\@ɹ2"6nkϥQJ̓^IFsB>!mZc%LY)8(r5Ui\RrxE&,E)? g ^w8(ke9bpM.J1SɓTߠPv˲$>D*@.uJPYH2C0q};.-TqZdmEir~j2y~_ E%>9|3W܃~䣸~:yAv;HO˜F3'kY X> gCf%T)JfA φ@S# *U2oKJc zRޒ!g2w0gT`?bR[\ x˲>/xի^{O|ħ>mn d650mn65Ɵߏmx[߂zr#Ez{?q=wa_U{4B ]w݁[/ .^0r*JQa, @'<%c*m*6Prȷ+W~,&chFdLT8 G}S¼|U?{nЋ0TxnFN:c+_Λ"*!MOb T&=<ec4OzEc:qVT$`ECFzVdylJ vi{dM.O?G%@,2͇.cpXb볢-r3{CVSC%Ajud$a<,̝lgSVӬIS#8,CSY0͙> }ʔcPlVĵPgf\f w`lz@qga毠}v)efmԉTfQ%Ê|AXty>tƵVZFGg#W,>\W9@LS v&(0!=3ls|?jj %Led7ϓO<>|?v{];&"&B6 ve'7\L_kT}<(4iAuCGy{ vOM++;B)EJLµWtJoYZߔB z Nu`U? _1[GíW~#>gyJ Y:[vC7W<{}x[ߊ<(^r#k[O~q\p.pQw#ҥ ubXDM>f\cR(ܣm*!1Y<Ƅp DN@iPTFMiKY9B)jH T[F{A(Nw}RyTXc؆LԞtNψvi +(ϳS ›^~f )[=ҩ3'B'ryHF4ElQrZkٛ!2y_S:fqCˈGXT(c^u .^sNF!o捡*i$\7"N#,6ŜPNW60oerZ1 .flM\p'r*V!J\-Teh$FG$9!uݽGuk/}5g<`UK=JS5Q6l⹠TЕgF~(`]? FUm"z qQD  !*Z%qfRVDɶهsbF]<ȞSjqCJSlN+EY@!z ʴ{nV#Z% pNf;2~E259|/QƧ!ʧ]Hy4h;|0 jkQ${qz#k\r'7coo6"cs5V!pFo"%27K$s{ڇ[)g'G*vCIϘvpg,) QLyU;$+G((&UxpW=t \D1 @giJ;d?n#lNјq+>uUowx'>'|ǧXRgu< 6T7wx7x5_NDO;..\@9疼B[1Zk8"~\t cƛ-ZV$Oi(FScE4H0vJ)5d,Iyizxf@;6{[H4.1HH $ߥ]黝o`磭 "@H؂RlI)&l =)!狧k+)]*6ZSݚFAl6M~gx\( FlIXݿ $Xs`zFLG@ih*,Έز IfѐT?xZCilyyFSDXYDnJ})v8K56j {ZRM6UMPnoI~0՞W8<)S^J.x<+: ygpY}RQEjý*S3;"IgOOq&B:|vb+*ZC|e2,2|(F-=M3k`SdK" )Q6՚?7WF}zMJVj5zV]nOUbxs;R2CWU(glwEF~>⡇_pwo~=<Vmm7-,'+MH~rz> n+d\JclYhGr>JN7=2U>ݝkH̼*'i+hP,Fa4vS֭[8<R=w߃W=^75{wsO<8~㓿O'oNVkq+xL`h`Zxo8_nK|"^۔Ϟ7$\AlM_CUqFsu-QS6ρ&;.>&-FD*=CXՋIP̀ Īs{1o9C"!ebVeWjP dnY, n. KpΝWZÍ7q5p.}ՈJo8~b#|bnfū~zիq+_իWFFЛu3>WohppK:sp=W==SO>zKWKIRwXBVAlJCYVHv1:54ÚǰLV!ugc>R8ޙl,6>;utmm!32R/Ic,X*)?w%*dC'1$M3k,gS|/jvh+Yap}$r~x3Ob9+P˓sʦ3GFIQT0а .دoDC&Xw'nE jJBkzL|6l`XJin3|Q cI)6okc`-vz "dמqSNqD]KRAQۖZpCBl"U ߝ 9 )⌀>Pբd'S B4>y K DR5O)>s&*,7A KyƧ-YMW|hh SSdBס颓|<dIA=ZRkr!D >fƳ>}iWptW¹q얎'O<8n|'[Hpc;8>Z50YH0σʖ?$3!Q4eD ao]=DUcspH/!IlS@ N0Ow} +lX-%_Em:6~(z_}hܰ^Z{8<./➻ƥ˗@ ,}x7?nѭy_ U+s55fKO%l\=W <#x[ߊ3?8I K|7|߯?U_q07RAm8_eӟy;Uq A,F* zN)AU[ 3S2ώ!ڨ)ٲݐyAs5ZK4@ \ 2ʞ Xtݻ R ފJ+ dPuj|x /TH?:8TmI5i\&^3ƻv C̯EN[{Ԩx4 Qq\QpBzrOl1,-(`Z b*Q%a(Eu/{wP6Щ-T I!iwnhCrJa0.bfծ7THq\I8xnZM{( fH3et>{_lC.Ҥ >Yt=zx\}4g>zs5辚)i>71pI E EJ\K ` jA*YT'5H2'B:mFzW^/: CbPq8óڸDƥ@1䏡 8ٙcRJx6kضsԳTQ lMWlI"Ha0ߧνU8ܒJDS6&dFb#(m&3#Z0ǐFdg>X3GYF6_j{>:m#"$( IDATOy)/_Fkk{p^?"n#<5ܼqnv.2f*7j*907:!J~SBzN2#S=n(l7SN3 HDL@`|@}W*!˂"[ fj@KX7⅋p.^Z5N;1n͛89>7n^[8lnfk:%V s\z.];v=ƿ}F+~6_knd:3T忊 zŤmxo1Dz:p>nܼko`W/_t[ 4;l`ij)O(|l^L/Cz8 v b +&J\`)p6&D6H[N baHjxz}W Sy0j] 6m $ vo\iYccAx( e˥B-\nYZ XvqI 0r$+C".ϾPg"4k4dIdų yRhb)0z1? I58੖O"v6NJJI"k P4ATZn3}n0. )j5?#D!pТ|J?L@CkFEAo+aw ;ew&+הu QH ]tikJ(Ba,?e'ZZ22j Ԅ2 (]k2iBU:SP ɸ/[#^)Z< lcj`d;KMCBrT^3oK(ZZш@< ''nNqrzۇۜd-16ШsRP#CDѽΟ;@[p߽=o!uZXVcTįDWRj΅:}|95g~_K Ow`[bְj+嫸r"}i\ykePu |s6^K,e4.߯7!C|$*R~ y&T"e rh۶BF uJ8O 9 rم'UQ!A> Qh㥈zm)fac`lOj4 N=,(V-E>XJX!@m<~]RiYDYRCΈBdtԳ)w f9qi\. KOKpJ؋bhpiEAsP~ƪ)Oߪb[q.jf6rGM#ƚn~10Ɍx)nh}z)D>T"#P=#RrL1_kqIC۶*zwIe0_-񥂉$767j4uZ oo7> O4Շ?qH8ٶV^`/^9c`6n)wHBa`]u$RnT֐!PSλ0|4cVdFǔX'5*~mj ZC^@޽=coƄ;w0ds1X;a,X,`srnvv, 6}Az UPD&(g)f+t";q7_/ij6}_;V| Yq~7~'|jk?&)Yjj{/]מōp¹sԎ.jB=RNp + F#,T#8|*Aa$ehjhH)&J?B,Ana/G_~Me #5$ήfjTKh1BᒋdP<F1fG^F2~)JC0PyO%G_(E6MQޓXlimC*2?V&,CItmJN)[SNy&7'MlR%MYȉ{h/k S#/>l &OÈP膇fՑH8k ;LzzfE֐KE})[%LKI&IEbq.tp``a[h)~KM2ųaR4UgVxi-+臨đ`xJe,GLAWmQ6-ʶ0QFɤ'Ȋ%:W:w){:GPk 2"d^"V٤sTS3C8|K9HU|]]dw/-Q$ZVߙje9VʜZDӰ+i<"շYc85+3$ֈkυq~|#(|x:.PagDTSaYEnܸÿqxt C~npVnk0nV5.\Xj+k@kv !D NOO,5? !N.PX$Rb`b)XC9b2Zkhm> 6-X;v]1>eò9;6e&yneNvɐj3GT:X9B~KqU;oy mƟ j5{_-}KWzfpU-?ҥK8::p 8V0 f __y?7n<7od;0b2Z78EDpYv;GErD% 9A^g!u@*iAAeJzC&]s?vwkw ߞF(w$izĦp.3 tB!/c">Y%(fکbaoR5fQm>agaq sSc L5@zqInP&]ITg5%$U c۬D9 Cue?{I QY9Ԟq|/UɕN`>ʚz!$+U3{]Ô$"<$1As@jb)2q4109 h :-إĂDCf/msjr,Qpj/"RjffSz^[H]웢ОnY fLMbS[8׭Dk [ 1%foL|j弜^R tنІ2+9B0Va[qi_* H(ؾqـ&q6"5'Cp&J3ЖQCgMfmZr,CK?)[˦HnM;>Ā(ab}[8ŨR"/VPnrߋwN??o|O} GGS bs[[[Bm;b^7TnXj֫5{`SteAk7RE?6lFzl!LJV ̌ӓS D K@Khv6Ѩfa9jnv~a;0v;`ٍv`[q˲` wkz!~ÏZ5:yN4)#^.BtQD@̸x"N;z7 o&|w}^7|4_FkJÿy{fp|Os~?_?+XDp!eg ޮY[yN۷{f̀` /ARtI~pqd!AF)M Wws2r}2G:snU<{Z?yw}vx)5L፫\#`)\aP=9\J&YiԦR/7E>qB(y}IW kdiB+;^OHTJ&;}FfCn/CjR*dRq9!R/Llr#cTS%M!ʔEQY9 N5 h$052b3-V k,(4df/]]Feə<\n@ϲIsœžs]wF,ԝIY*3iI2i(Wp8s鲩 t2F )6Fx̂Iduo?E6J~œϹfަu˒6쾨Zߘ^dg4 )oƢSŠ8R*Ms|!(cqzKw8; YDYCJa&nee>42Ei"bPNdiPi\{p4^di2~1SQ$&I%; ,"3c]Cj QYrGÈ`LVe, l% WCJ@ZMV,N H XZ2 :ޓ?^ʣl*96iIwC)YLMQX ئ ʒJiR=-\2sZe" x!sկ~{? WO~S|u l?`Ɯ(HB+A(=q.pw=}zsl.F>;,<z'ngtF0}t/H~Gz߱,a?q&ЏNP =q 8`C ?#!xυNHR *$DdVSUXuM?e|t- cY<|?>| ַ\%O9v߽{kӨuf!Pq~m<}/w?tȰK麎=| ]>@nqs|Ǹgw=:PqFY Ę":4JfV rVEfT^)1:FيفfNR p& s(xY"x`wvLxz`ɦ6.BH\x~MVĄ&e{ne>x9T#ɥgYeFG<U2wӱ7]%`vFBIwT""}y* BYv $]XѲ* bD!Q%VE~()2(<P"2k=+*Q 8g=x)(:A7rʬYWـșlߡNͬtXb(%p6\ Rpb IDdBC9Js5!N29ٝ8dBB8rCkMs($4ʵёfD 5P7aN,lz)ܿ6Agiyସ2sYL#NYPNP Xg) ,RRT~M OqVʵ<. 'O_>G}>8$e4і72{,O1(L] Wf(4u\P璢M!9Yed:,B JNh<N}l9b&W;dD k&l SeӜQ:VAgM3U5X .,5q^$%\287guII&EN)NT;MMK͐Qi&M)g.N'yrhÈsd{2jU&Y̐k `Юl!z@9S9ɼp%9<2rq0ؕ?ycpApD؀qADx?O'c?gOqww[|',@4@G%G3DPJ#`m #}yaC0֡c'bkcMlv"4>1C|v3hDz1fM<`p*9US If1,36uؐ@M]Oj@e,HtO~gP\$l΁ TL@y62 :u5 ʴg$e$D c'&2DF=@1! ҵNב0mrX{$ծR̉~v'f<'@x (;쮫P LM$k!3%)נ_sQoL3H)ܤL;\c'LlaLOSIwAɑ}6Rtқ#2R2,>AH:t&1HH%IM (%9^JDc\Sd'WX2yLw KE~O Dnt͋xA07| OyCqH0k&nDFjiF T IDATs{"9 jW;vJK (Xalje(Ɍι̗\F?F'7b2 7^$i#IJ_ŐtcgsFy1/D&T4uLq`f`\2!\2huufX IK8x1~~ 777я~??_|!ןcv?"1d ?R+"#:_tfMr ڽ{-ˡG\\u7O)7~*oëk9Oъ|-`!L-_ =?~wy~ۿw!>\ZDZ6ْd D#Gx7wx./uW;Q>>qD؏0FJiٮ!ƨJla-8z{;ؘBisޜA dSf&riIݜu&f"2y#6O'AQNGJdiP ĸ"准zADB&Rl`INʼn}1J\DhrX@eA5S t~Ua&,FC[~WW"2cUWٸ5^ î1$̂YˉhNϳFMuZx;Tmd0ɤN%ՀL1(bc#r ^lZ$Qx"K@n؈yev\$&늪k7~ube}dv󣙢o K0(E^!JCy7g}??G|X? BaLt0x㦬 XpkJsvz3ݯ(̤2 fMm<GkfEk+5j<jy5ۓWrCqqyy.vرZyGo&?yҧ%l_X=Yc LkpXu`XAk%0 ?}љY`vg]. o\\^rwwn.0xx w|4l"Q,0XgX|1^D ϾjM D y.E,3;垭jX.mH#Gl`}6KHOgDdr7> 4;b99^:Rzǜϐ@Ȃ!M[YaC&[K3̞tjɠh e$jif騮CR mf-`Xvua4şa%ٳ </|?/_WOcT\_v]ĎS, L3u43ud'YLcsBmKDP9=r  ϞGӢkT&I*ECpa|7xgϞg?o~O1wC4h]CRjt͚.{}ˋz \\t03йȨHy;ca2zMGF=g`!`*C#MvjDlVcցΡ5_Wn-f A-8>)@{/ϟ>ᅬ?ϟ=gO٧bFw:3X|emuBVgN?'P:%{{O; _4)OK!;޼9%r$xt;n1 \л9[\~>/5(.b{LHmkwzcX2KKـ&kFXAQ$ɲu׋lT='? O?+>9F@xPSNyJH.ecc4X:r3pcz\vE":Z]^!ߣwQ#x]8?ml ;GŮwck]w1-p] ! {L~3aa7ny}a0w;8p~0G^,d$NJgv<.Yv DGEHW,ePba<15AdT% $;ߓP?4$cóg~@^鶥qC_ARr UpDɝ-f01acf<.\[3X\]=@7:w;{]p xy}BU[P/y-K`p. ֕u1YAvkT-01>TD3X@DPXAquupssO?|}!>#<} ?}k5www-ny}IBf&Yah?ŖҔeQ<KJg- &`2J7Ьu0#u,л ׂZt] c>}=v'dۮ1;~s1ꃥ9}pn;fO'v}dx֜#.\( ,G@ƙ0D0% 7 4.qr: L-1{1dpPZMKR5Go G RCV{4J}|O̡XM䨘 jbG<:ab ^ˆKlHy]rIV^ZYM,_L3xqtIH~фFusaȠsBdl*cb{͍ ɖ kLIW+"mU=X|I$hU{d"!;@#IŘfhYLf]̹RSuNܝUV#d'$'4md6g SaJ(PRe[RNnmM3FA)O>AsќBglrZk' 5Uݗ-)c!ր8^0[`)9,%O q ]Ge-Q;Ź׾G׹P~0c-nb-noopw{,a"6Y0c\7|a޳F,SA LR1UP8!$7- MC5,E3 TZuΤYc]\heyѸk-X9Ha v6BZ~90vQ(aD.;>9Z  1;a`t2Mlg<8=zcG8sNĖ=0Ţȏ:gqF`Ec̲3ѽˆ%s~aealFx0!KP=qn/nD<{ߣsuFvZ4v0p7".;zSx?`q: =?GYG}./9sYE8ȀuB%W -gҴðXN@cqcj0xnHp ]0>zk-w8zqh{ۡ/9T#^Y&:XPۄ,1`0FX\a(X"9k  mzIuQG:D#7ˆ׀'t;1fxڙشqb yZF^ 2%;Ф-fe`22Y 6grps@dca뢏`aj366X 6Ng d"4b\'#G8l;vdkYU y٦c&b1l=O=f<b}]AWM#Ⲍ+}S9D)89I^B:6/Yy׃"{9V~p`1Q I; ƬY"ߋ)6G`,"q$=(]FQlWqFFK^VXg.%nl8خG R Au=GtvG E@YH|)fHx~0R1>.-ώXs;հ5qAll75O0/#.(.̌k+'ـq Y7aC2We.&Ƹx<.=KwZ6l@6ےİs]碌:X!_C]6 l)8[\4ϟ?0zt=ŃTTX3b.A~̌ Y6޳(Gxc1`#Mb 7QUYI#޹"|0IX{$f8C4QN~W6~ azc\\^ϟCg,¸l `U& 0#l$lkzk08>ȳ"qw\?1dzd0r +4Ffqxp}wzps7b5:5EbsLq=*&ba1ޣ3s q+s\laIøGg:>>0&"3r4l .*2qւLl3d %1gS׀~ϩ< ԅ_%,At[SӅrtˌapaw{f9׊ ~ǟ<q7@NHnXr C=6u.Y~aw h"CtCGow.?~ 6+fh2x1Z$0Y CJ(!1 VG q  Kǁ!>lgV6PlA1gY[c>r=ޒ,_^ԕNAtlL6bQs9F!'(# y#Y 'uI3,OG6J- 8f\ ^tDW,,g"ܤx^R9ѹ>Jl@n9ͦ: @ʖiT$A6saI!Q2|Rib]<Ȝ_4=ŗXˌz?HZ2esi1D5 i`ys6#)83:r35fbAu;*3B{XqcŧNO>κPV2|$:G m݊>E9_#|F9jIRW"[x9(짠Ƃdxb<]Ɖ@&"c)ͶidSʩL R[0 Fy Gs<ˬgB>`~aŅ OӒl M&lRNMfMT׍!6()g&֘ Sh5V:Du0(ϺȘHY-1lrc8e{e*Bq-icA "&ՠdK(k;KKCk3k(=TL\n@w{s(Ï>'<ŧ?pss4H7]8qųpnwG*i[%../p./.q WWW =z<%zvA},|cy,X9 8DƔfr*p8woySfkMƐ`-gIVg w?d ڐS-;MmPGH3XeInrsqvux4IhšIڇrPb)O|H L`S>W0ɓg+P?۳Σ# G0r ipS!$#('?*xVSU k&H˫#~ 2;t5a8R-(f$Il<.z&Ė!ęKg4 s;w!Gقpl\HAFlEoXffw0&ALJ$ZrΞ0ָ 8=ix7J53`M(RanH\ hg699z⴪]H3m!|JYx%FA8S$1 i&0u Ck$&3vy2H96EIDn0@i_4HEI;*.9҇-Kj@EM`JGDYg_]ߣ/77ˍD$HA!8!xDʄP\dO&`\ <+7]SP9)X=3YIIYPZ0 ܲ˧4] ~7bB9\r#}cqJ [r}668LSE 5>)Y KvLY:%yvO@ &'iƞ4)s#/%'i\D+Lj O'H6 (=S8s|ecZdPwI'1GQ.Hd j Ս@Ivry$A._ i2o8L,l.2ɓ̒Z(z2i;`;z6e)²hR4/X+b&%1TKs=5P-Ӳ іYqN EbWc1Ȗ}%'68%j@Q~|t-(l55w Z` nqm,д^S麲|JC-F >#sNƣ m Tk"{<(1r֠J =X՚#}5)Û3DgZͨu3w_W[K5~2(Rkb}nkҲ]&0bnyQ%}Y ;4kU kdT^(Mk@l>~zkMڵܬ ԔWן&$6N@f٧6 N" %1fAzܨH}ҥ1m(L5M3X3:HdHES_8眫(X>Co6 0*OLqp_O=*@oqn`þdZvk`)T85dt(ɱys׃Lڑi8d }t1z]D]t\V3R w ziN* B05#ʄURyޯgs+Kn&{c2SϽ~/D5iG[[$AXj;@Yc,ϯ} sq,'={Rhg!5P+ v]woqwh(%scٿ5Y2ܧ5dZo-9F%tL6\9'bK}%@^t|TϠk|ցZ1oyk著-^ʱb)]_bjW[[j &rimʵqzO[@tР) (Yl8f<=\dnQfKfZcy6UݚKO,k(ORm!׺;-VjJ9K)^~a(u ,WmW\g5 Z]ٯuJ&d4xה.UmiC6TcyQܗO-b,S>XZ%c٩@W>4bo)BQUP>Qy5IOV-oiW6F"YPF?K-0 @AF[3t(JV6s xUj=[klefS0%/}뚖JA6֤aҭ.e-XpowuͤϤ@KgjY'S& iҳ]znptQMje>-sqK@aj쫙\O*}rx1~ q}XӵAT[fXbn9c -ϜkfX249庤ZS.T ι6[3k*^SR 2ƊPXU z7amQ_M97{ؒ浭H95v,tKf4'L\_ XRM5YfM>%W;>OY ^oWk!")mݟTBޣA5rn )EV3&Y2* u+7b-79zvQ_7Y.-I/Ml6ãw"[T=rZz9iRŲ6v6YdUvFeNr[k,5uZ9ZKȶZ㐁Y7}M ;9vd0KϜ7Hꭵ=Vݠwy?yֽ^wjJW[k@99wm?g%kzKS/Lst`ښh%Iila[5@ŖJ"%8XN-ojRvY $ޕkQt,ig~Nl5kA9 9j3X-35ߩZNjR+:wϖpc+iQj:VKrN;epeM=Jyf)LL=pR}HͲLz>PV}Ziq[GLÿ́9nŬ1Uky-Jktk&/;`jik5\\d[/Y{5k%lI2#Zui1jpֳZ-I-f|fe;_{i`L{kX2/Bv1%d@.IKe[56*aj3K,ǜL),AP 7CM2Z(ZPc!kفs99 ym4kVҲ8]ֆAAȹH5UV`jF>=p];s<zJsT>y}jSpj کUf[ldt&K9HKϩ֞tZԊ&(g%56|p!KktՀ6Q[@Q^3*3ت|\)euo*h5wO6l ^fA]ﴉT4{:wO8+zP6Zr֯RYܓsL@us7cK^fq벷̴z-7~-7[n?>"[1+jn 5Yd Jr$n5XޯfnӒXo:h}EܳKR%r-5dw95@g-{i(g ZԵmkny_b[ ym?k ~hp?u%eSV11,l.(j]  4;Y^|vlPW3cYsաQl.Zv8=F5Ub"%V,5pֈ%[{%sVo1m5c5#\slRђ5@Y XSY2\cZaLtXΥT&98nkuQ2-s~k}xϖ|+lJ/-leb~gf4,ٳؔΜs&$-mSA[ Q%.VWs ymƉUhl~ Y[:\\ւzQ E.֝i-tּ~k uijs\ԏ8.&-f3j7U[EFĖBzij8>CTk-Fe3f:YbE.V9HTѝ9fBc6;LJr͹: 2Gt5\6gLjcR2[ic]M|0AYk5L5AKtmҹkZ)S-f[j2,gYOϥ:C oIN& lt/f2\[` 9 NL^]jf2-5R/V^I1kh@5X׶m$KL}%VccX˹׮1Y:Fsc9THâdq-ٲnkR~n#lzρm͂)3U]kAs˿s5 ZfzlŶ]vT{MÆ6Xb8d릔yVS\4DzkY9VmX<[GA^vINԈԙRT#G[i6 KI5=&UMUZ}9ZJ;WOUՎ˽e/ k%W5Φ5ݳe3?[)5Ƭ8nδBk)>YԺ׬񶀝-z-pe&1[#$N [,[m)[NKpY,~KRS5[+9uq;v|sς9T־/Ks=ify9ga椕4vLY "XCM$M6W-t6-5{ .[k5j9~-\kbך1^3Bt仵kLg^[,5[Y%bC&C_;xo0*cAXԚAKOݍ`0m]y57hQ@` H7͵]́%9HeW-9 ݭtmN*n(kƒ[ֽih*vVik*t~́zpa?4}KrCͤm?sʜ4 @g pm&4,֊i58[ҽ^nſ{M@Oa3~1f1&AC欖wؕ9lp<+5rAM*\ϑ9`NK2Z^M oY8Znnnq!?rl嫭-tZ1"K- g `[~l. cYەhOk]kLX-YIxl)Sg,|*]je$8=̵M%\M[lz[e6OfX q2[αZ=Y+_j5c7NoGԚY-nܭoQ@mُ5s$D}זƒjyI@CAEv{s+;ڥ͒Ԛ ~/@G[:Ɔ=׼ւv-[fd5Yܵ&xFZ9FK `m~q]inm]\%WU:plZ؎%w;fC}F&ѩ TIDATkܿoj2i`k~(h[JiKN}9mU;=VZBwl.ӹ8us+ Z籍-S45͂}ye]ji]jmnTgI%g+_`#wBqCk5oP.޵7R BE@Vx,X}<\Kdsյei{>T2-w px95/\c:QZ\X+'";k1 Ou,[\ b JZ3kcÔ`T@~tu߭ňbnٯ2ޤlyms-7ѥ895ښ+_@xJwyF װ5I[ı-9[B T~4cޚE_6|fdNhLss8 uF&ifLr-l5Nžm[3-Qss}y>ڠ/ZUpcqJ5ĭN/yq_@"F:֢K=g[ F+ }?GU&^G}e_{ET ʫb`tAlZ]{&{ 6> -iR}`MJzxljt\mU̹{1-a9p: j-4tk 095{s_$2ssM Wu^\ߋXO1?*vh_Vt׻v۶5~iYBUZcPW;Z;J>=haވ/S[By;om_%u:E`l1Z I =UNy,3cq޾kyv8g0x ؏vvy;uDmu3= 0 ? idx : 0; } onActivated: { emulatorScaleFactor = textAt(index); } } } ListItem.Standard { //show this listitem only when device is not connected visible: machineType === DeviceMachineType.Emulator && !deviceItemView.deviceConnected text: "Memory" control: Controls.ComboBox { id: emulatorMemoryComboBox model: ["512", "768", "1024"] currentIndex: { var idx = find(emulatorMemorySetting); return idx >= 0 ? idx : 0; } onActivated: { emulatorMemorySetting = textAt(index); } } } ListItem.SingleValue { text:i18n.tr("Device") value: deviceInfo visible: deviceItemView.deviceConnected } ListItem.SingleValue { text:i18n.tr("Model") value: modelInfo visible: deviceItemView.deviceConnected } ListItem.SingleValue { text:i18n.tr("Product") value: productInfo visible: deviceItemView.deviceConnected } FeatureStateItem { text: "Has network connection" input: hasNetworkConnection inputRole: "hasNetworkConnection" checkable: hasNetworkConnection == FeatureState.NotAvailable && !deviceItemView.deviceBusy && !deviceItemView.detectionError visible: deviceItemView.deviceConnected } FeatureStateItem { text: "Has developer mode enabled" input: developerModeEnabled inputRole: "developerModeEnabled" checkable: !deviceItemView.deviceBusy && !deviceItemView.detectionError visible: deviceItemView.deviceConnected } /* FeatureStateItem { text: "Has writeable image" input: hasWriteableImage inputRole: "hasWriteableImage" checkable: false visible: deviceItemView.deviceConnected } */ } } SectionItem { title: "Kits" expanded: true Column { anchors.left: parent.left anchors.right: parent.right Repeater { model: kits delegate: ListItem.Standard { text: modelData.displayName Layout.fillWidth: true control: Button{ text: "Remove" enabled: !deviceItemView.deviceBusy onClicked: devicesModel.triggerKitRemove(deviceId,modelData.id) } } } Item { clip: true visible: kits.length === 0 height: label.contentHeight + units.gu(15) width: parent.width Label { id:label anchors.centerIn: parent anchors.bottom: button.top fontSize: "large" text: "There is currently no Kit defined for your device.\n In order to use the device in your Projects,\n you can either add a existing Kit " +"\nor let Qt Creator autocreate one for you." } Button { id: button anchors.left: label.left anchors.right: label.right anchors.top: label.bottom anchors.bottom: parent.bottom anchors.topMargin: units.gu(2) text: "Autocreate" enabled: !deviceItemView.deviceBusy onClicked: devicesModel.triggerKitAutocreate(deviceId) } } } } SectionItem { title: "Control" visible: deviceItemView.deviceConnected Column { anchors.left: parent.left anchors.right: parent.right ListItem.Standard { text:"Enable port forwarding" control: Button{ text: "Execute" enabled: !deviceItemView.deviceBusy && !deviceItemView.detectionError onClicked: devicesModel.triggerPortForwarding(deviceId) } } ListItem.Standard { text:"Setup public key authentication" control: Button{ text: "Execute" enabled: !deviceItemView.deviceBusy && !deviceItemView.detectionError onClicked: devicesModel.triggerSSHSetup(deviceId) } } ListItem.Standard { text:"Open SSH connection to the device" control: Button{ text: "Execute" enabled: !deviceItemView.deviceBusy && !deviceItemView.detectionError onClicked: devicesModel.triggerSSHConnection(deviceId) } } } } SectionItem { title: "Log" Column { anchors.left: parent.left anchors.right: parent.right TextArea { autoSize: true maximumLineCount: 0 anchors.left: parent.left anchors.right: parent.right height: units.gu(60) highlighted: true readOnly: true text: deviceLog textFormat: TextEdit.AutoText } } } } } } } } } } ./share/qtcreator/ubuntu/qml/DevicesPage/EmulatorNotInstalled.qml0000644000015600001650000000240412705421114025316 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 import Ubuntu.Components.ListItems 1.3 as ListItem import Ubuntu.Components.Popups 1.0 import "../Components" Popover { id: popover height: containerLayout.childrenRect.height Column { id: containerLayout anchors { left: parent.left top: parent.top right: parent.right } ListItem.Header { text: "Ubuntu Emulator is not installed" __foregroundColor: UbuntuColors.orange } ListItem.Empty { showDivider: false Label { anchors.fill: parent anchors.margins: units.gu(2) text: "Install the emulator package \"ubuntu-emulator\" in order to create emulator instances." wrapMode: Text.Wrap } } //add a spacer item Item { height: units.gu(2) width: units.gu(1) } ListItem.SingleControl { highlightWhenPressed: false control: Button { text: "Close" anchors { margins: units.gu(1) } onClicked: PopupUtils.close(popover) } } } } ./share/qtcreator/ubuntu/qml/DevicesPage/NewEmulatorDialog.qml0000644000015600001650000000726612705421114024602 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 import Ubuntu.Components.ListItems 1.3 as ListItem import Ubuntu.Components.Popups 1.0 import "../Components" Dialog { id: dialogue title: i18n.tr("Create emulator") text: i18n.tr("Please select a name for the emulator") modal: true TextField { id: inputName placeholderText: i18n.tr("Emulator name") property string lastError property bool hasError onTextChanged: validate() Component.onCompleted: validate() function validate() { var result = devicesModel.validateEmulatorName(text); hasError = !result.valid; lastError = result.error; } } Label { horizontalAlignment: Text.AlignHCenter text: inputName.lastError color: "red" visible: inputName.hasError } ListItem.ItemSelector { id: arch model: [i18n.tr("i386"), i18n.tr("armhf")] } ListItem.ItemSelector { id: channel model: ListModel{ id: channelModel ListElement { displayName: "devel" value: "ubuntu-touch/devel/ubuntu" } ListElement { displayName: "devel-proposed" value: "ubuntu-touch/devel-proposed/ubuntu" } ListElement { displayName: "bq-stable" value: "ubuntu-touch/stable/bq-aquaris.en" } ListElement { displayName: "bq-rc" value: "ubuntu-touch/rc/bq-aquaris.en" } ListElement { displayName: "rc-proposed" value: "ubuntu-touch/rc-proposed/ubuntu" } ListElement { displayName: "custom channel" value: "custom channel" } } delegate: OptionSelectorDelegate{ text: displayName } property string selectedChannel: { return channelModel.get(channel.selectedIndex).value } } TextField { id: inputChannelName placeholderText: i18n.tr("Emulator channel") visible: channel.selectedChannel === "custom channel" } ListItem.ItemSelector { id: custom_pwd model: ["Use default password (0000)", "Set custom password"] } TextField { id: inputCustomPassword echoMode: TextInput.Password placeholderText: i18n.tr("Password") visible: custom_pwd.model[custom_pwd.selectedIndex] === "Set custom password" } Label { id: inputChannelNameError horizontalAlignment: Text.AlignHCenter text: "Channel name can not be empty" color: "red" visible: inputChannelName.visible && inputChannelName.text.length == 0 } Button { text: "Cancel" color: UbuntuColors.warmGrey onClicked: PopupUtils.close(dialogue) } Button { text: "Create" color: UbuntuColors.orange enabled: !inputName.hasError && !inputChannelNameError.visible onClicked: { if(inputName.hasError || inputChannelNameError.visible) return; devicesModel.createEmulatorImage(inputName.text, arch.model[arch.selectedIndex], (inputChannelName.visible ? inputChannelName.text : channel.selectedChannel), (inputCustomPassword.visible ? inputCustomPassword.text : "0000") ); PopupUtils.close(dialogue); } } } ./share/qtcreator/ubuntu/qml/DevicesPage/DeleteDeviceDialog.qml0000644000015600001650000000205412705421114024650 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 import Ubuntu.Components.Popups 1.0 import "../Components" Dialog { id: dialogue title: deviceId >= 0 ? i18n.tr("Delete device") : i18n.tr("Delete emulator") text: deviceId >= 0 ? i18n.tr("Are you sure you want to delete this device?") : i18n.tr("Are you sure you want to delete this emulator?") property string emulatorImageName property int deviceId: -1 Button { text: i18n.tr("Cancel") color: UbuntuColors.warmGrey onClicked: PopupUtils.close(dialogue) } Button { text: i18n.tr("Delete") color: UbuntuColors.orange onClicked: { if(deviceId >= 0) { console.log("Deleting device: "+dialogue.deviceId); devicesModel.deleteDevice(dialogue.deviceId); } else { console.log("Deleting emu: "+dialogue.emulatorImageName); devicesModel.deleteEmulator(dialogue.emulatorImageName); } PopupUtils.close(dialogue); } } } ./share/qtcreator/ubuntu/qml/DevicesPage/LogPage.qml0000644000015600001650000000103412705421114022521 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtQuick.Layouts 1.0 import QtQuick.Controls 1.0 as Controls import Ubuntu.Components 1.3 import Ubuntu.Components.ListItems 0.1 as ListItem import Ubuntu.Components.Popups 0.1 Item { Controls.TextArea { id: logTextArea anchors.fill: parent readOnly: true textFormat: TextEdit.AutoText Component.onCompleted: { deviceMode.appendText.connect(appendToLog); } function appendToLog (txt) { logTextArea.append(txt); } } } ./share/qtcreator/ubuntu/qml/.excludes0000644000015600001650000000000012705421114020116 0ustar jenkinsjenkins./share/qtcreator/ubuntu/menu.json0000644000015600001650000002756112705421114017374 0ustar jenkinsjenkins{ "ubuntu.make": { "name": "make", "id": "Ubuntu.Make", "parent": "Build", "projectRequired": true, "group": "ProjectExplorer.Group.Run", "submenu": [ { "name": "check", "id": "Ubuntu.Make.Check", "projectRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_project_make check" ] }, { "name": "autopilot", "id": "Ubuntu.Make.Autopilot", "projectRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_project_make autopilot" ] }, { "name": "install", "id": "Ubuntu.Make.Install", "projectRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_project_make install" ] }, { "name": "uninstall", "id": "Ubuntu.Make.Uninstall", "projectRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_project_make uninstall" ] } ] }, "ubuntu": { "name": "Ubuntu", "id": "Ubuntu.Build", "parent": "Build", "group": "ProjectExplorer.Group.Run", "submenu": [ { "name": "Create manifest files", "id": "Ubuntu.Build.CreateManifest", "workingDirectory": "%0/..", "projectRequired": true, "context": ["UbuntuProject.ProjectContext","QmlProject.ProjectContext","CMakeProject.ProjectContext"], "actions": [ { "metacall": { "method":"createManifestFile", "args":[] } } ] }, { "name": "Run Application on Device", "id": "Ubuntu.Build.Run.Go", "workingDirectory": "%0/..", "projectRequired": true, "keysequence": "Ctrl+F12", "deviceRequired": true, "saveRequired": true, "context" : "GoProject.ProjectContext", "actions": [ "%SCRIPTDIRECTORY%/qtc_device_run_go_app %SERIALNUMBER% %FOLDERNAME% %USERNAME%@%IP% %PORT% /home/%USERNAME%/dev_tmp \"%GOBUILDTARGETS%\"" ] }, { "name": "Close Application on Device", "id": "Ubuntu.Build.CancelRun", "keysequence": "Ctrl+Shift+F12", "projectRequired": true, "deviceRequired": true, "qmlProjectRequired": true, "context": ["GoProject.ProjectContext"], "actions": [ "%SCRIPTDIRECTORY%/qtc_device_close_all_apps %SERIALNUMBER% %DISPLAYNAME%" ] }, { "name": "Create Click package", "id": "Ubuntu.Build.CreateClickPackage", "workingDirectory": "%0/..", "projectRequired": true, "context": ["UbuntuProject.ProjectContext","QmlProject.ProjectContext","CMakeProject.ProjectContext"], "saveRequired": true, "needsClickToolchain" : true, "actions": [ { "metacall": { "method":"requestBuildProject", "args":[] } } ] }, { "name": "Install Application on Device", "id": "Ubuntu.Build.PackageInstall.Html", "workingDirectory": "%0/..", "projectRequired": true, "context": ["UbuntuProject.ProjectContext","QmlProject.ProjectContext"], "saveRequired": true, "needsClickToolchain" : true, "actions": [ { "metacall": { "method":"requestBuildAndInstallProject", "args":[] } } ] }, { "name": "Install Application on Device", "id": "Ubuntu.Build.PackageInstall.CMake", "workingDirectory": "%0/..", "projectRequired": true, "context": ["CMakeProject.ProjectContext"], "saveRequired": true, "needsClickToolchain" : true, "actions": [ { "metacall": { "method":"requestBuildAndInstallProject", "args":[] } } ] }, { "name": "Install Application on Device", "id": "Ubuntu.Build.PackageInstall.Go", "workingDirectory": "%0/..", "projectRequired": true, "deviceRequired": true, "context": ["GoProject.ProjectContext"], "saveRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_goproject_click_createanddeploy %SERIALNUMBER% %FOLDERNAME% %USERNAME%@%IP% %PORT% /home/%USERNAME%/dev_tmp %USERNAME% \"%GOBUILDTARGETS%\"" ] }, { "name": "Build Application on Device", "id": "Ubuntu.Build.Go", "workingDirectory": "%0/..", "projectRequired": true, "deviceRequired": true, "context": ["GoProject.ProjectContext"], "saveRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_device_buildpackage_go %SERIALNUMBER% %FOLDERNAME% %USERNAME%@%IP% %PORT% /home/%USERNAME%/dev_tmp \"%GOBUILDTARGETS%\"" ] }, { "name": "Build Application on Device", "id": "Ubuntu.Build.Cpp", "workingDirectory": "%0/..", "projectRequired": true, "deviceRequired": true, "context": ["Qt4.Qt4Project","CMakeProject.ProjectContext"], "saveRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_device_buildpackage %SERIALNUMBER% %FOLDERNAME% %USERNAME%@%IP% %PORT% /home/%USERNAME%/dev_tmp" ] }, { "name": "Build and Install Application on Device", "id": "Ubuntu.BuildAndInstall.Cpp", "workingDirectory": "%0/..", "projectRequired": true, "deviceRequired": true, "qmakeProjectRequired": true, "saveRequired": true, "actions": [ "%SCRIPTDIRECTORY%/qtc_device_buildanddeploypackage %SERIALNUMBER% %FOLDERNAME% %USERNAME%@%IP% %PORT% /home/%USERNAME%/dev_tmp" ] }, { "name": "Translations", "id": "Ubuntu.Menu.i18n", "submenu": [ { "name": "Update Translations Template", "id": "Ubuntu.Menu.i18n.pot", "projectRequired": true, "saveRequired": true, "context": ["UbuntuProject.ProjectContext","QmlProject.ProjectContext","CMakeProject.ProjectContext"], "actions": [ "%SCRIPTDIRECTORY%/i18n_update_template %DISPLAYNAME% %PROJECTFILES%" ] }, { "name": "Build Translations", "id": "Ubuntu.Menu.i18n.mo", "projectRequired": true, "saveRequired": true, "context": ["UbuntuProject.ProjectContext","QmlProject.ProjectContext","CMakeProject.ProjectContext"], "actions": [ "%SCRIPTDIRECTORY%/i18n_build_translations %NAME_FROM_MANIFEST% %PROJECTDIRECTORY%" ] } ] } ] }, "ubuntuhelp": { "name": "Ubuntu", "id": "Ubuntu.Help", "parent": "Help", "submenu": [ { "name": "Getting Started", "id": "Ubuntu.Help.GettingStarted", "submenu": [ { "name": "Go Mobile at developer.ubuntu.com", "id": "Ubuntu.Help.GettingStarted.GoMobile", "actions": [ "xdg-open http://developer.ubuntu.com/get-started/gomobile" ] }, { "name": "App Design Guides at design.ubuntu.com", "id": "Ubuntu.Help.GettingStarted.AppDesignGuides", "actions": [ "xdg-open http://design.ubuntu.com/apps" ] } ] }, { "name": "Launchpad", "id": "Ubuntu.Help.Launchpad", "submenu": [ { "name": "Create an Account", "id": "Ubuntu.Help.Launchpad.CreateAccount", "actions": [ "xdg-open https://launchpad.net/+login" ] }, { "name": "Register a Project", "id": "Ubuntu.Help.Launchpad.RegisterProject", "actions": [ "xdg-open https://launchpad.net/projects/+new" ] }, { "name": "User Guide", "id": "Ubuntu.Help.Launchpad.UserGuide", "actions": [ "xdg-open https://help.launchpad.net" ] } ] } ] }, "ubuntutools": { "name": "Ubuntu", "id": "Ubuntu.Menu", "parent": "Tools", "submenu": [ { "name": "Showcase Gallery", "keysequence": "Ctrl+Shift+S", "id": "Ubuntu.Menu.Showcase", "actions": [ "%SCRIPTDIRECTORY%/qtc_launch_gallery" ] }, { "name": "Take a screenshot", "id": "Ubuntu.Menu.Screenshot", "keysequence": "Ctrl+Shift+P", "deviceRequired": true, "actions": [ "%SCRIPTDIRECTORY%/device_screenshot %SERIALNUMBER%" ] }, { "name": "Mount Device Filesystem", "id": "Ubuntu.Menu.MountDeviceFileSystem", "keysequence": "Ctrl+Shift+F1", "deviceRequired": true, "actions": [ "nautilus ssh://%USERNAME%@%IP%:%PORT%" ] }, { "name": "GPG", "id": "Ubuntu.Menu.gpg", "submenu": [ { "name": "Passwords && Keys", "id": "Ubuntu.Menu.gpg.seahorse", "actions": [ "seahorse" ] } ] } ] } } ./share/qtcreator/templates/0000755000015600001650000000000012705421114016176 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/0000755000015600001650000000000012705421114017661 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/0000755000015600001650000000000012705421114021203 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/0000755000015600001650000000000012705421114022755 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/wizard.xml0000644000015600001650000001366612705421114025013 0ustar jenkinsjenkins ../share/ubuntu.png A simple C++ based Unity Scope Unity Scope (14.10) Ubuntu Click package parameters Nickname: Maintainer: Scope name: Dummy Framework Framework: Scope with network access Confinement type: Qt scope using HTTP+JSON API Qt scope using HTTP+XML API Pure C++ scope using HTTP+JSON API Empty scope Template type: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/cmake/0000755000015600001650000000000012705421114024035 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/cmake/FindGMock.cmake0000644000015600001650000000107412705421114026642 0ustar jenkinsjenkins# Build with system gmock and embedded gtest set (GMOCK_INCLUDE_DIRS "/usr/include/gmock/include" CACHE PATH "gmock source include directory") set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") set (GTEST_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory") add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") set(GTEST_LIBRARIES gtest) set(GTEST_MAIN_LIBRARIES gtest_main) set(GMOCK_LIBRARIES gmock gmock_main) set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/cmake/FindIntltool.cmake0000644000015600001650000001566412705421114027460 0ustar jenkinsjenkins# Copyright (C) 2014 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # This package provides macros that wrap the intltool programs. # # An example of common usage is: # # For an ini file: # # intltool_merge_translations( # "foo.ini.in" # "${CMAKE_CURRENT_BINARY_DIR}/foo.ini" # ALL # UTF8 # ) # # Inside po/CMakeLists.txt: # # intltool_update_potfile( # ALL # GETTEXT_PACKAGE ${GETTEXT_PACKAGE} # ) # # intltool_install_translations( # ALL # GETTEXT_PACKAGE ${GETTEXT_PACKAGE} # ) find_package(Gettext REQUIRED) find_program(INTLTOOL_UPDATE_EXECUTABLE intltool-update) if(INTLTOOL_UPDATE_EXECUTABLE) execute_process( COMMAND ${INTLTOOL_UPDATE_EXECUTABLE} --version OUTPUT_VARIABLE intltool_update_version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (intltool_update_version MATCHES "^intltool-update \\(.*\\) [0-9]") string( REGEX REPLACE "^intltool-update \\([^\\)]*\\) ([0-9\\.]+[^ \n]*).*" "\\1" INTLTOOL_UPDATE_VERSION_STRING "${intltool_update_version}" ) endif() unset(intltool_update_version) endif() find_program(INTLTOOL_MERGE_EXECUTABLE intltool-merge) if(INTLTOOL_MERGE_EXECUTABLE) execute_process( COMMAND ${INTLTOOL_MERGE_EXECUTABLE} --version OUTPUT_VARIABLE intltool_merge_version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (intltool_update_version MATCHES "^intltool-merge \\(.*\\) [0-9]") string( REGEX REPLACE "^intltool-merge \\([^\\)]*\\) ([0-9\\.]+[^ \n]*).*" "\\1" INTLTOOL_MERGE_VERSION_STRING "${intltool_merge_version}" ) endif() unset(intltool_merge_version) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Intltool REQUIRED_VARS INTLTOOL_UPDATE_EXECUTABLE INTLTOOL_MERGE_EXECUTABLE VERSION_VAR INTLTOOL_UPDATE_VERSION_STRING HANDLE_COMPONENTS ) function(APPEND_EACH LISTNAME GLUE OUTPUT) set(_tmp_list "") foreach(VAL ${${LISTNAME}}) list(APPEND _tmp_list "${GLUE}${VAL}") endforeach(VAL ${${LISTNAME}}) set(${OUTPUT} "${_tmp_list}" PARENT_SCOPE) endfunction() function(INTLTOOL_UPDATE_POTFILE) set(_options ALL) set(_oneValueArgs GETTEXT_PACKAGE OUTPUT_FILE PO_DIRECTORY) cmake_parse_arguments(_ARG "${_options}" "${_oneValueArgs}" "" ${ARGN}) set(_POT_FILE "${PROJECT}.pot") set(_GETTEXT_PACKAGE "") if(_ARG_GETTEXT_PACKAGE) set(_POT_FILE "${_ARG_GETTEXT_PACKAGE}.pot") set(_GETTEXT_PACKAGE --gettext-package="${_ARG_GETTEXT_PACKAGE}") endif() set(_OUTPUT_FILE "") if(_ARG_OUTPUT_FILE) set(_POT_FILE "${_ARG_OUTPUT_FILE}") set(_OUTPUT_FILE --output-file="${_ARG_OUTPUT_FILE}") endif() set(_PO_DIRECTORY "${CMAKE_SOURCE_DIR}/po") if(_ARG_PO_DIRECTORY) set(_PO_DIRECTORY "${_ARG_PO_DIRECTORY}") endif() file( GLOB_RECURSE _CODE_SOURCES ${CMAKE_SOURCE_DIR}/*.cpp ${CMAKE_SOURCE_DIR}/*.cc ${CMAKE_SOURCE_DIR}/*.cxx ${CMAKE_SOURCE_DIR}/*.vala ${CMAKE_SOURCE_DIR}/*.c ${CMAKE_SOURCE_DIR}/*.h ${CMAKE_SOURCE_DIR}/*.ini.in ) add_custom_command( OUTPUT "${_PO_DIRECTORY}/${_POT_FILE}" COMMAND ${INTLTOOL_UPDATE_EXECUTABLE} --pot ${_OUTPUT_FILE} ${_GETTEXT_PACKAGE} DEPENDS "${_PO_DIRECTORY}/POTFILES.in" ${_CODE_SOURCES} WORKING_DIRECTORY ${_PO_DIRECTORY} ) _GETTEXT_GET_UNIQUE_TARGET_NAME(${_POT_FILE} _UNIQUE_TARGET_NAME) if(_ARG_ALL) add_custom_target( ${_UNIQUE_TARGET_NAME} ALL DEPENDS "${_PO_DIRECTORY}/${_POT_FILE}" ) else() add_custom_target( ${_UNIQUE_TARGET_NAME} DEPENDS "${_PO_DIRECTORY}/${_POT_FILE}" ) endif() endfunction() function(INTLTOOL_INSTALL_TRANSLATIONS) set(_options ALL) set(_oneValueArgs GETTEXT_PACKAGE POT_FILE) cmake_parse_arguments(_ARG "${_options}" "${_oneValueArgs}" "" ${ARGN}) set(_POT_FILE "${PROJECT}.pot") if(_ARG_GETTEXT_PACKAGE) set(_POT_FILE "${_ARG_GETTEXT_PACKAGE}.pot") endif() if(_ARG_OUTPUT_FILE) set(_POT_FILE "${_ARG_OUTPUT_FILE}") endif() file( GLOB _PO_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.po ) get_filename_component(_ABS_POT_FILE ${_POT_FILE} ABSOLUTE) foreach(_PO_FILE ${_PO_FILES}) add_custom_command( OUTPUT ${_PO_FILE} COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --quiet --update --backup=none -s ${_PO_FILE} ${_ABS_POT_FILE} DEPENDS ${_ABS_POT_FILE} ) endforeach() if(_ARG_ALL) gettext_create_translations( ${_POT_FILE} ALL ${_PO_FILES} ) else() gettext_create_translations( ${_POT_FILE} ${_PO_FILES} ) endif() endfunction() function(INTLTOOL_MERGE_TRANSLATIONS FILENAME OUTPUT_FILE) set(_options ALL UTF8 PASS_THROUGH) set(_oneValueArgs PO_DIRECTORY) cmake_parse_arguments(_ARG "${_options}" "${_oneValueArgs}" "" ${ARGN}) get_filename_component(_ABS_FILENAME ${FILENAME} ABSOLUTE) set(_PO_DIRECTORY "${CMAKE_SOURCE_DIR}/po") if(_ARG_PO_DIRECTORY) set(_PO_DIRECTORY "${_ARG_PO_DIRECTORY}") endif() set(_UTF8 "") if(_ARG_UTF8) set(_UTF8 "--utf8") endif() set(_PASS_THROUGH "") if(_ARG_PASS_THROUGH) set(_PASS_THROUGH "--pass-through") endif() file( GLOB_RECURSE _PO_FILES ${_PO_DIRECTORY}/*.po ) add_custom_command( OUTPUT ${OUTPUT_FILE} COMMAND ${INTLTOOL_MERGE_EXECUTABLE} --desktop-style --quiet ${_UTF8} ${_PASS_THROUGH} ${_PO_DIRECTORY} ${FILENAME} ${OUTPUT_FILE} DEPENDS ${_ABS_FILENAME} ${_PO_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) get_filename_component(_OUTPUT_NAME ${OUTPUT_FILE} NAME) _GETTEXT_GET_UNIQUE_TARGET_NAME(${_OUTPUT_NAME} _UNIQUE_TARGET_NAME) if(_ARG_ALL) add_custom_target( ${_UNIQUE_TARGET_NAME} ALL DEPENDS ${OUTPUT_FILE} ) else() add_custom_target( ${_UNIQUE_TARGET_NAME} DEPENDS ${OUTPUT_FILE} ) endif() endfunction() ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/manifest.json.in0000644000015600001650000000103512705421114026062 0ustar jenkinsjenkins{ @if "%ContentType%".substring(0, "network".length) === "network" "description": "A simple Unity scope that accesses the network", @else "description": "A simple Unity scope that accesses local content", @endif "maintainer": "%ClickMaintainer%", "architecture": "@CLICK_ARCH@", "name": "%ProjectName:l%.%ClickDomain:l%", "title": "%ProjectName:l%", "framework" : "%ClickFrameworkVersion%", "hooks": { "%ClickHookName:l%": { "scope": "%ClickHookName:l%", "apparmor": "%ClickHookName:l%.apparmor" } } } ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/0000755000015600001650000000000012705421114024117 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/0000755000015600001650000000000012705421114025425 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/forecast/0000755000015600001650000000000012705421114027233 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/forecast/daily/0000755000015600001650000000000012705421114030335 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/forecast/daily/London,uk.json0000644000015600001650000000371612705421114033104 0ustar jenkinsjenkins{"cod":"200","message":0.1117,"city":{"id":2643743,"name":"London","coord":{"lon":-0.12574,"lat":51.50853},"country":"GB","population":0,"sys":{"population":0}},"cnt":7,"list":[{"dt":1407412800,"temp":{"day":23.33,"min":18.84,"max":25.09,"night":18.84,"eve":24.17,"morn":20.76},"pressure":1018.23,"humidity":83,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.87,"deg":295,"clouds":12,"rain":0.5},{"dt":1407499200,"temp":{"day":19.44,"min":15.54,"max":20.86,"night":15.54,"eve":18.28,"morn":16.21},"pressure":1010.57,"humidity":97,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":4.01,"deg":143,"clouds":92,"rain":8},{"dt":1407585600,"temp":{"day":18.78,"min":13.19,"max":19.58,"night":13.19,"eve":18.95,"morn":14.03},"pressure":1010.78,"humidity":83,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":5.46,"deg":281,"clouds":32},{"dt":1407672000,"temp":{"day":18.09,"min":13.46,"max":18.09,"night":13.46,"eve":16.58,"morn":13.91},"pressure":1002.33,"humidity":67,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.68,"deg":162,"clouds":92,"rain":4},{"dt":1407758400,"temp":{"day":17.38,"min":15.39,"max":17.38,"night":16.3,"eve":17.03,"morn":15.39},"pressure":1008.2,"humidity":0,"weather":[{"id":502,"main":"Rain","description":"heavy intensity rain","icon":"10d"}],"speed":13.52,"deg":245,"clouds":74,"rain":12.63},{"dt":1407844800,"temp":{"day":18.44,"min":16.17,"max":18.44,"night":16.83,"eve":18.02,"morn":16.17},"pressure":1009.94,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.93,"deg":251,"clouds":60,"rain":5.25},{"dt":1407931200,"temp":{"day":18.8,"min":16.67,"max":19.02,"night":16.77,"eve":19.02,"morn":16.67},"pressure":1013.17,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":9.88,"deg":275,"clouds":85,"rain":1.35}]} ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/forecast/daily/London,uk.xml0000644000015600001650000000751512705421114032734 0ustar jenkinsjenkins London GB 0.1469 ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/forecast/daily/Manchester,uk.xml0000644000015600001650000000743012705421114033570 0ustar jenkinsjenkins Manchester GB 0.0021 ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/forecast/daily/Manchester,uk.json./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/forecast/daily/Manchester,uk.jso0000644000015600001650000000367412705421114033571 0ustar jenkinsjenkins{"cod":"200","message":0.0056,"city":{"id":2643123,"name":"Manchester","coord":{"lon":-2.23743,"lat":53.480949},"country":"GB","population":0,"sys":{"population":0}},"cnt":7,"list":[{"dt":1407412800,"temp":{"day":17.91,"min":11.96,"max":18.83,"night":11.96,"eve":18.01,"morn":16.13},"pressure":1014.82,"humidity":77,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":3.32,"deg":303,"clouds":0},{"dt":1407499200,"temp":{"day":18.59,"min":12.33,"max":18.59,"night":12.33,"eve":14.55,"morn":14.69},"pressure":1008.22,"humidity":79,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":3.46,"deg":163,"clouds":92,"rain":11},{"dt":1407585600,"temp":{"day":15.8,"min":10.79,"max":17.11,"night":10.79,"eve":16.89,"morn":13.19},"pressure":1005.76,"humidity":92,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"speed":5.61,"deg":279,"clouds":56},{"dt":1407672000,"temp":{"day":15.29,"min":12.21,"max":15.98,"night":12.41,"eve":15.98,"morn":12.21},"pressure":995.36,"humidity":95,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.75,"deg":137,"clouds":92,"rain":5},{"dt":1407758400,"temp":{"day":15.73,"min":12.99,"max":15.73,"night":13.8,"eve":15.23,"morn":12.99},"pressure":990.16,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":9.02,"deg":239,"clouds":99,"rain":1.15},{"dt":1407844800,"temp":{"day":17.64,"min":14.08,"max":17.64,"night":14.08,"eve":14.92,"morn":14.12},"pressure":992.52,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.36,"deg":240,"clouds":45,"rain":4.84},{"dt":1407931200,"temp":{"day":14.35,"min":13.37,"max":15.52,"night":13.77,"eve":15.52,"morn":13.37},"pressure":999.39,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":7.79,"deg":285,"clouds":92,"rain":5.98}]} ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/server.py0000755000015600001650000000337112705421114027314 0ustar jenkinsjenkins#!/usr/bin/env python3 import http.server import os import socketserver import sys from urllib.parse import urlparse,parse_qs def read_file(path): file = os.path.join(os.path.dirname(__file__), path) if os.path.isfile(file): with open(file, 'r') as fp: content = fp.read() else: content = '' return content class MyRequestHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): sys.stderr.write("GET: %s\n" % self.path) sys.stderr.flush() parse = urlparse(self.path) path = parse.path query = parse_qs(parse.query) if path == '/data/2.5/weather': self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() mode = 'json' if 'mode' in query: mode = query['mode'][0] self.wfile.write(bytes(read_file('weather/%s.%s' % (query['q'][0], mode)), 'UTF-8')) elif path == '/data/2.5/forecast/daily': self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() mode = 'json' if 'mode' in query: mode = query['mode'][0] self.wfile.write(bytes(read_file('forecast/daily/%s.%s' % (query['q'][0], mode)), 'UTF-8')) else: self.send_response(404) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(bytes('ERROR', 'UTF-8')) if __name__ == "__main__": Handler = MyRequestHandler httpd = socketserver.TCPServer(("127.0.0.1", 0), Handler) sys.stdout.write('%d\n' % httpd.server_address[1]) sys.stdout.flush() httpd.serve_forever() ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/weather/0000755000015600001650000000000012705421114027064 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/weather/London,uk.json0000644000015600001650000000064212705421114031626 0ustar jenkinsjenkins{"coord":{"lon":-0.13,"lat":51.51},"sys":{"type":1,"id":5091,"message":0.29,"country":"GB","sunrise":1407386057,"sunset":1407440289},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"base":"cmc stations","main":{"temp":21.83,"pressure":1014,"humidity":53,"temp_min":20,"temp_max":24},"wind":{"speed":1.5,"deg":0},"clouds":{"all":20},"dt":1407408276,"id":2643743,"name":"London","cod":200}./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/weather/London,uk.xml0000644000015600001650000000123712705421114031456 0ustar jenkinsjenkins GB ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/weather/Manchester,uk.xml0000644000015600001650000000121212705421114032307 0ustar jenkinsjenkins GB ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/server/weather/Manchester,uk.json0000644000015600001650000000071312705421114032465 0ustar jenkinsjenkins{"coord":{"lon":-2.24,"lat":53.48},"sys":{"type":1,"id":5060,"message":0.2423,"country":"GB","sunrise":1407386141,"sunset":1407441219},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"base":"cmc stations","main":{"temp":17.35,"pressure":1016,"humidity":77,"temp_min":17,"temp_max":18},"wind":{"speed":2.6,"deg":20,"var_beg":330,"var_end":80},"clouds":{"all":40},"dt":1407408600,"id":2643123,"name":"Manchester","cod":200} ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/unit/0000755000015600001650000000000012705421114025076 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/unit/scope/0000755000015600001650000000000012705421114026207 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/unit/scope/test-scope.cpp0000644000015600001650000002243012705421114031002 0ustar jenkinsjenkins#include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace testing; using namespace scope; namespace posix = core::posix; namespace sc = unity::scopes; namespace sct = unity::scopes::testing; /** * Keep the tests in an anonymous namespace */ namespace { /** * Custom matcher to check the properties of search results */ MATCHER_P2(ResultProp, prop, value, "") { if (arg.contains(prop)) { *result_listener << "result[" << prop << "] is " << arg[prop].serialize_json(); } else { *result_listener << "result[" << prop << "] is not set"; } return arg.contains(prop) && arg[prop] == sc::Variant(value); } /** * Custom matcher to check the presence of departments */ MATCHER_P(IsDepartment, department, "") { return arg->serialize() == department->serialize(); } typedef sct::TypedScopeFixture TypedScopeFixtureScope; class TestScope: public TypedScopeFixtureScope { protected: @if "%ContentType%".substring(0, "network".length) === "network" void SetUp() override { // Start up Python-based fake OpenWeatherMap server fake_server_ = posix::exec("/usr/bin/python3", { FAKE_SERVER }, { }, posix::StandardStream::stdout); // Check it's running ASSERT_GT(fake_server_.pid(), 0); string port; // The server will print out the random port it is using fake_server_.cout() >> port; // Check we have a port ASSERT_FALSE(port.empty()); // Build up the API root string apiroot = "http://127.0.0.1:" + port; // Override the API root that the scope will use setenv("NETWORK_SCOPE_APIROOT", apiroot.c_str(), true); // Do the parent SetUp TypedScopeFixture::set_scope_directory(TEST_SCOPE_DIRECTORY); TypedScopeFixtureScope::SetUp(); } /** * Start by assuming the server is invalid */ posix::ChildProcess fake_server_ = posix::ChildProcess::invalid(); @endif }; TEST_F(TestScope, empty_search_string) { @if "%ContentType%".substring(0, "network".length) === "network" const sc::CategoryRenderer renderer; NiceMock reply; // Build a query with an empty search string sc::CannedQuery query(SCOPE_NAME, "", ""); // Expect the current weather category EXPECT_CALL(reply, register_category("current", "London, GB", "", _)).Times(1) .WillOnce(Return(make_shared("current", "London, GB", "", renderer))); // With one result EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "21.8°C"), ResultProp("art", "http://openweathermap.org/img/w/02d.png"), ResultProp("subtitle", "few clouds") )))).WillOnce( Return(true)); // Expect the forecast category EXPECT_CALL(reply, register_category("forecast", "7 day forecast", "", _)).Times(1) .WillOnce(Return(make_shared("forecast", "7 day forecast", "", renderer))); // With seven results EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "25.1°C to 18.8°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "light rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "20.9°C to 15.5°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "moderate rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "19.6°C to 13.2°C"), ResultProp("art", "http://openweathermap.org/img/w/03d.png"), ResultProp("subtitle", "scattered clouds") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "18.1°C to 13.5°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "moderate rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "17.4°C to 15.4°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "heavy intensity rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "18.4°C to 16.2°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "moderate rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "19°C to 16.7°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "light rain") )))).WillOnce(Return(true)); sc::SearchReplyProxy reply_proxy(&reply, [](sc::SearchReply*) {}); // note: this is a std::shared_ptr with empty deleter sc::SearchMetadata meta_data("en_EN", "phone"); // Create a query object auto search_query = scope->search(query, meta_data); ASSERT_NE(nullptr, search_query); // Run the search search_query->run(reply_proxy); // Google Mock will make assertions when the mocks are destructed. @endif } TEST_F(TestScope, search) { @if "%ContentType%".substring(0, "network".length) === "network" const sc::CategoryRenderer renderer; NiceMock reply; // Build a query with a non-empty search string sc::CannedQuery query(SCOPE_NAME, "Manchester,uk", ""); // Expect the current weather category EXPECT_CALL(reply, register_category("current", "Manchester, GB", "", _)).Times(1) .WillOnce(Return(make_shared("current", "Manchester, GB", "", renderer))); // With one result EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "17.4°C"), ResultProp("art", "http://openweathermap.org/img/w/03d.png"), ResultProp("subtitle", "scattered clouds") )))).WillOnce( Return(true)); // Expect the forecast category EXPECT_CALL(reply, register_category("forecast", "7 day forecast", "", _)).Times(1) .WillOnce(Return(make_shared("forecast", "7 day forecast", "", renderer))); // With seven results EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "18.8°C to 12°C"), ResultProp("art", "http://openweathermap.org/img/w/01d.png"), ResultProp("subtitle", "sky is clear") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "18.6°C to 12.3°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "moderate rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "17.1°C to 10.8°C"), ResultProp("art", "http://openweathermap.org/img/w/04d.png"), ResultProp("subtitle", "broken clouds") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "16°C to 12.2°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "moderate rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "15.7°C to 13°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "light rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "17.6°C to 14.1°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "moderate rain") )))).WillOnce(Return(true)); EXPECT_CALL(reply, push(Matcher(AllOf( ResultProp("title", "15.5°C to 13.4°C"), ResultProp("art", "http://openweathermap.org/img/w/10d.png"), ResultProp("subtitle", "moderate rain") )))).WillOnce(Return(true)); sc::SearchReplyProxy reply_proxy(&reply, [](sc::SearchReply*) {}); // note: this is a std::shared_ptr with empty deleter sc::SearchMetadata meta_data("en_EN", "phone"); // Create a query object auto search_query = scope->search(query, meta_data); ASSERT_NE(nullptr, search_query); // Run the search search_query->run(reply_proxy); // Google Mock will make assertions when the mocks are destructed. @endif } } // namespace ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/unit/CMakeLists.txt0000644000015600001650000000112112705421114027631 0ustar jenkinsjenkins # Our test executable. # It includes the object code from the scope add_executable( scope-unit-tests scope/test-scope.cpp $ ) # Link against the scope, and all of our test lib dependencies target_link_libraries( scope-unit-tests ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARIES} ${SCOPE_LDFLAGS} ${TEST_LDFLAGS} ${Boost_LIBRARIES} ) @if "%ContentType%".substring(0, "network-netcpp-q".length) === "network-netcpp-q" qt5_use_modules( scope-unit-tests Core ) @endif # Register the test with CTest add_test( scope-unit-tests scope-unit-tests ) ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/tests/CMakeLists.txt0000644000015600001650000000161112705421114026656 0ustar jenkinsjenkins # Google Mock unfortunately has to be compiled from source include(FindGMock) @if "%ContentType%".substring(0, "network".length) === "network" # We need process-cpp to launch the python test server pkg_check_modules( TEST process-cpp REQUIRED ) @endif # Include our test library headers include_directories( ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS} ${TEST_INCLUDE_DIRS} ) # Where to find the scope ini file and .so add_definitions( -DTEST_SCOPE_DIRECTORY="${CMAKE_BINARY_DIR}/src" ) @if "%ContentType%".substring(0, "network".length) === "network" file(GLOB_RECURSE TEST_FIXTURES "server/*" ) # Make this file show up in QtCreator add_custom_target(hidden_test_fixtures ALL SOURCES ${TEST_FIXTURES} ) # Where to find the test server binary add_definitions( -DFAKE_SERVER="${CMAKE_CURRENT_SOURCE_DIR}/server/server.py" ) @endif # Add the unit tests add_subdirectory(unit) ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/0000755000015600001650000000000012705421114024400 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/api/0000755000015600001650000000000012705421114025151 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/api/client.h0000644000015600001650000000737712705421114026616 0ustar jenkinsjenkins#ifndef API_CLIENT_H_ #define API_CLIENT_H_ #include #include #include #include #include @if "%ContentType%".substring(0, "network".length) === "network" #include #include @if "%ContentType%" == "network-netcpp-json" namespace Json { class Value; } @elsif "%ContentType%" == "network-netcpp-qjson" #include @elsif "%ContentType%" == "network-netcpp-qxml" #include @endif @endif namespace api { /** * Provide a nice way to access the HTTP API. * * We don't want our scope's code to be mixed together with HTTP and JSON handling. */ class Client { public: @if "%ContentType%" == "empty" /** * Result struct */ struct Result { std::string uri; std::string title; std::string art; std::string subtitle; std::string description; }; /** * A list of weather information */ typedef std::deque ResultList; @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Information about a City */ struct City { unsigned int id; std::string name; std::string country; }; /** * Temperature information for a day. */ struct Temp { double max; double min; double cur; }; /** * Weather information for a day. */ struct Weather { unsigned int id; std::string main; std::string description; std::string icon; Temp temp; }; /** * A list of weather information */ typedef std::deque WeatherList; /** * Weather information about the current day */ struct Current { City city; Weather weather; }; /** * Forecast information about a city */ struct Forecast { City city; WeatherList weather; }; @endif Client(Config::Ptr config); virtual ~Client() = default; @if "%ContentType%" == "empty" /** * Search for results */ virtual ResultList search(const std::string &query); @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Get the current weather for the specified location */ virtual Current weather(const std::string &query); /** * Get the weather forecast for the specified location and duration */ virtual Forecast forecast_daily(const std::string &query, unsigned int days = 7); @endif /** * Cancel any pending queries (this method can be called from a different thread) */ virtual void cancel(); virtual Config::Ptr config(); protected: @if "%ContentType%" == "network-netcpp-json" void get(const core::net::Uri::Path &path, const core::net::Uri::QueryParameters ¶meters, Json::Value &root); @elsif "%ContentType%" == "network-netcpp-qjson" void get(const core::net::Uri::Path &path, const core::net::Uri::QueryParameters ¶meters, QJsonDocument &root); @elsif "%ContentType%" == "network-netcpp-qxml" void get(const core::net::Uri::Path &path, const core::net::Uri::QueryParameters ¶meters, QXmlStreamReader &reader); @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Progress callback that allows the query to cancel pending HTTP requests. */ core::net::http::Request::Progress::Next progress_report( const core::net::http::Request::Progress& progress); @endif /** * Hang onto the configuration information */ Config::Ptr config_; /** * Thread-safe cancelled flag */ std::atomic cancelled_; }; } #endif // API_CLIENT_H_ ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/api/config.h0000644000015600001650000000101012705421114026557 0ustar jenkinsjenkins#ifndef API_CONFIG_H_ #define API_CONFIG_H_ #include #include namespace api { struct Config { typedef std::shared_ptr Ptr; @if "%ContentType%".substring(0, "network".length) === "network" /* * The root of all API request URLs */ std::string apiroot { "http://api.openweathermap.org" }; /* * The custom HTTP user agent string for this library */ std::string user_agent { "example-network-scope 0.1; (foo)" }; @endif }; } #endif /* API_CONFIG_H_ */ ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/scope/0000755000015600001650000000000012705421114025511 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/scope/preview.h0000644000015600001650000000132012705421114027337 0ustar jenkinsjenkins#ifndef SCOPE_PREVIEW_H_ #define SCOPE_PREVIEW_H_ #include namespace unity { namespace scopes { class Result; } } namespace scope { /** * Represents an individual preview request. * * Each time a result is previewed in the UI a new Preview * object is created. */ class Preview: public unity::scopes::PreviewQueryBase { public: Preview(const unity::scopes::Result &result, const unity::scopes::ActionMetadata &metadata); ~Preview() = default; void cancelled() override; /** * Populates the reply object with preview information. */ void run(unity::scopes::PreviewReplyProxy const& reply) override; }; } #endif // SCOPE_PREVIEW_H_ ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/scope/query.h0000644000015600001650000000140512705421114027027 0ustar jenkinsjenkins#ifndef SCOPE_QUERY_H_ #define SCOPE_QUERY_H_ #include #include #include namespace scope { /** * Represents an individual query. * * A new Query object will be constructed for each query. It is * given query information, metadata about the search, and * some scope-specific configuration. */ class Query: public unity::scopes::SearchQueryBase { public: Query(const unity::scopes::CannedQuery &query, const unity::scopes::SearchMetadata &metadata, api::Config::Ptr config); ~Query() = default; void cancelled() override; void run(const unity::scopes::SearchReplyProxy &reply) override; private: api::Client client_; }; } #endif // SCOPE_QUERY_H_ ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/scope/localization.h0000644000015600001650000000102312705421114030346 0ustar jenkinsjenkins#ifndef SCOPE_LOCALIZATION_H_ #define SCOPE_LOCALIZATION_H_ #include #include inline char * _(const char *__msgid) { return dgettext(GETTEXT_PACKAGE, __msgid); } inline std::string _(const char *__msgid1, const char *__msgid2, unsigned long int __n) { char buffer [256]; if (snprintf ( buffer, 256, dngettext(GETTEXT_PACKAGE, __msgid1, __msgid2, __n), __n ) >= 0) { return buffer; } else { return std::string(); } } #endif // SCOPE_LOCALIZATION_H_ ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/include/scope/scope.h0000644000015600001650000000240012705421114026767 0ustar jenkinsjenkins#ifndef SCOPE_SCOPE_H_ #define SCOPE_SCOPE_H_ #include #include #include #include #include #include namespace scope { /** * Defines the lifecycle of scope plugin, and acts as a factory * for Query and Preview objects. * * Note that the #preview and #search methods are each called on * different threads, so some form of interlocking is required * if shared data structures are used. */ class Scope: public unity::scopes::ScopeBase { public: /** * Called once at startup */ void start(std::string const&) override; /** * Called at shutdown */ void stop() override; /** * Called each time a new preview is requested */ unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result&, const unity::scopes::ActionMetadata&) override; /** * Called each time a new query is requested */ unity::scopes::SearchQueryBase::UPtr search( unity::scopes::CannedQuery const& q, unity::scopes::SearchMetadata const&) override; protected: api::Config::Ptr config_; }; } #endif // SCOPE_SCOPE_H_ ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/0000755000015600001650000000000012705421114023544 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/api/0000755000015600001650000000000012705421114024315 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/api/client.cpp0000644000015600001650000004111112705421114026275 0ustar jenkinsjenkins#include @if "%ContentType%".substring(0, "network-netcpp".length) === "network-netcpp" #include #include #include #include @if "%ContentType%" == "network-netcpp-json" #include @elsif "%ContentType%" == "network-netcpp-qjson" #include @endif namespace http = core::net::http; namespace net = core::net; @if "%ContentType%" == "network-netcpp-json" namespace json = Json; @endif @endif using namespace api; using namespace std; Client::Client(Config::Ptr config) : config_(config), cancelled_(false) { } @if "%ContentType%" == "network-netcpp-qxml" namespace { static QString readText(QXmlStreamReader& xml) { xml.readNext(); if (xml.tokenType() != QXmlStreamReader::Characters) { return QString(); } return xml.text().toString(); } static void parseCity(Client::City& city, QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("id")) { city.id = attributes.value("id").toUInt(); } if (attributes.hasAttribute("name")) { city.name = attributes.value("name").toString().toStdString(); } xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "city")) { if (xml.isStartElement()) { if (xml.name() == "country") { city.country = readText(xml).toStdString(); } } xml.readNext(); } } static void parseWeather(Client::Weather& weather, QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("id")) { weather.id = attributes.value("id").toUInt(); } if (attributes.hasAttribute("value")) { weather.description = attributes.value("value").toString().toStdString(); } if (attributes.hasAttribute("icon")) { weather.icon = "http://openweathermap.org/img/w/" + attributes.value("icon").toString().toStdString() + ".png"; } xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "weather")) { xml.readNext(); } } static void parseTemperature(Client::Temp& temp, QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("value")) { temp.cur = attributes.value("value").toDouble(); } if (attributes.hasAttribute("max")) { temp.max = attributes.value("max").toDouble(); } if (attributes.hasAttribute("min")) { temp.min = attributes.value("min").toDouble(); } xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "temperature")) { xml.readNext(); } } static void parseLocation(Client::City& city, QXmlStreamReader& xml) { xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "location")) { if (xml.name() == "name") { city.name = readText(xml).toStdString(); } else if (xml.name() == "country") { city.country = readText(xml).toStdString(); } xml.readNext(); } } static void parseTime(Client::Weather& weather, QXmlStreamReader& xml) { xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "time")) { if (xml.name() == "symbol") { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("name")) { weather.description = attributes.value("name").toString().toStdString(); } if (attributes.hasAttribute("var")) { weather.icon = "http://openweathermap.org/img/w/" + attributes.value("var").toString().toStdString() + ".png"; } } else if (xml.name() == "temperature") { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("max")) { weather.temp.max = attributes.value("max").toDouble(); } if (attributes.hasAttribute("min")) { weather.temp.min = attributes.value("min").toDouble(); } if (attributes.hasAttribute("day")) { weather.temp.cur = attributes.value("day").toDouble(); } } xml.readNext(); } } static void parseForecast(Client::WeatherList& weather_list, QXmlStreamReader& xml) { xml.readNext(); unsigned int weather_id = 1000000; while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "forecast")) { if (xml.name() == "time") { Client::Weather weather; weather.id = weather_id++; parseTime(weather, xml); weather_list.emplace_back(weather); } xml.readNext(); } } } @endif @if "%ContentType%" == "empty" Client::ResultList Client::search(const string &query) { ResultList results; // This is the method that we will call from the Query class. // It just returns some results. // You can add here your code to get results from an http API, from your local disk // or anywhere. // In this case we just create some results withouth accessing any other source of // data { Result result; result.uri = "uri"; result.title = query; result.art = "art.png"; result.subtitle = "subtitle"; result.description = "description"; results.emplace_back(result); } { Result result; result.uri = "uri2"; result.title = query; result.art = "art2.png"; result.subtitle = "subtitle2"; result.description = "description2"; results.emplace_back(result); } return results; } @endif @if "%ContentType%".substring(0, "network-netcpp".length) === "network-netcpp" @if "%ContentType%" == "network-netcpp-json" void Client::get(const net::Uri::Path &path, const net::Uri::QueryParameters ¶meters, json::Value &root) { @elsif "%ContentType%" == "network-netcpp-qjson" void Client::get(const net::Uri::Path &path, const net::Uri::QueryParameters ¶meters, QJsonDocument &root) { @elsif "%ContentType%" == "network-netcpp-qxml" void Client::get(const net::Uri::Path &path, const net::Uri::QueryParameters ¶meters, QXmlStreamReader &reader) { @endif // Create a new HTTP client auto client = http::make_client(); // Start building the request configuration http::Request::Configuration configuration; // Build the URI from its components net::Uri uri = net::make_uri(config_->apiroot, path, parameters); configuration.uri = client->uri_to_string(uri); // Give out a user agent string configuration.header.add("User-Agent", config_->user_agent); // Build a HTTP request object from our configuration auto request = client->head(configuration); try { // Synchronously make the HTTP request // We bind the cancellable callback to #progress_report auto response = request->execute( bind(&Client::progress_report, this, placeholders::_1)); // Check that we got a sensible HTTP status code if (response.status != http::Status::ok) { throw domain_error(response.body); } @if "%ContentType%" == "network-netcpp-json" // Parse the JSON from the response json::Reader reader; reader.parse(response.body, root); // Open weather map API error code can either be a string or int json::Value cod = root["cod"]; if ((cod.isString() && cod.asString() != "200") || (cod.isUInt() && cod.asUInt() != 200)) { throw domain_error(root["message"].asString()); } @elsif "%ContentType%" == "network-netcpp-qjson" // Parse the JSON from the response root = QJsonDocument::fromJson(response.body.c_str()); // Open weather map API error code can either be a string or int QVariant cod = root.toVariant().toMap()["cod"]; if ((cod.canConvert() && cod.toString() != "200") || (cod.canConvert() && cod.toUInt() != 200)) { throw domain_error(root.toVariant().toMap()["message"].toString().toStdString()); } @elsif "%ContentType%" == "network-netcpp-qxml" // Parse the Xml from the response reader.addData(response.body.c_str()); @endif } catch (net::Error &) { } } Client::Current Client::weather(const string& query) { // This is the method that we will call from the Query class. // It connects to an HTTP source and returns the results. @if "%ContentType%" == "network-netcpp-json" // In this case we are going to retrieve JSON data. json::Value root; @elsif "%ContentType%" == "network-netcpp-qjson" // In this case we are going to retrieve JSON data. QJsonDocument root; @elsif "%ContentType%" == "network-netcpp-qxml" // In this case we are going to retrieve XML data. QXmlStreamReader root; @endif // Build a URI and get the contents. // The fist parameter forms the path part of the URI. // The second parameter forms the CGI parameters. get( { "data", "2.5", "weather" }, { { "q", query }, { "units", "metric" } , { "APPID", "2b12bf09b4e0ab0c1aa5e32a9a3f0cdc" } @if "%ContentType%" == "network-netcpp-qxml" , { "mode", "xml" } @endif }, root); // e.g. http://api.openweathermap.org/data/2.5/weather?q=QUERY&units=metric Current result; @if "%ContentType%" == "network-netcpp-json" // Read out the city we found json::Value sys = root["sys"]; result.city.id = sys["id"].asUInt(); result.city.name = root["name"].asString(); result.city.country = sys["country"].asString(); // Read the weather json::Value weather = root["weather"].get(json::ArrayIndex(0), json::Value()); result.weather.id = weather["id"].asUInt(); result.weather.main = weather["main"].asString(); result.weather.description = weather["description"].asString(); result.weather.icon = "http://openweathermap.org/img/w/" + weather["icon"].asString() + ".png"; // Read the temps json::Value main = root["main"]; result.weather.temp.cur = main["temp"].asDouble(); result.weather.temp.max = main["temp_max"].asDouble(); result.weather.temp.min = main["temp_min"].asDouble(); @elsif "%ContentType%" == "network-netcpp-qjson" // Read out the city we found QVariantMap variant = root.toVariant().toMap(); QVariantMap sys = variant["sys"].toMap(); result.city.id = sys["id"].toUInt(); result.city.name = variant["name"].toString().toStdString(); result.city.country = sys["country"].toString().toStdString(); // Read the weather QVariantMap weather = variant["weather"].toList().first().toMap(); result.weather.id = weather["id"].toUInt(); result.weather.main = weather["main"].toString().toStdString(); result.weather.description = weather["description"].toString().toStdString(); result.weather.icon = "http://openweathermap.org/img/w/" + weather["icon"].toString().toStdString() + ".png"; // Read the temps QVariantMap main = variant["main"].toMap(); result.weather.temp.cur = main["temp"].toDouble(); result.weather.temp.max = main["temp_max"].toDouble(); result.weather.temp.min = main["temp_min"].toDouble(); @elsif "%ContentType%" == "network-netcpp-qxml" while (!root.atEnd() && !root.hasError()) { QXmlStreamReader::TokenType token = root.readNext(); /* If token is just StartDocument, we'll go to next.*/ if (token == QXmlStreamReader::StartDocument) { continue; } /* If token is StartElement, we'll see if we can read it.*/ if (token == QXmlStreamReader::StartElement) { if (root.name() == "city") { parseCity(result.city, root); } else if (root.name() == "weather") { parseWeather(result.weather, root); } else if (root.name() == "temperature") { parseTemperature(result.weather.temp, root); } } } if (root.hasError()) { throw domain_error(root.errorString().toStdString()); } @endif return result; } Client::Forecast Client::forecast_daily(const string& query, unsigned int cnt) { @if "%ContentType%" == "network-netcpp-json" json::Value root; @elsif "%ContentType%" == "network-netcpp-qjson" QJsonDocument root; @elsif "%ContentType%" == "network-netcpp-qxml" QXmlStreamReader root; @endif // Build a URI and get the contents // The fist parameter forms the path part of the URI. // The second parameter forms the CGI parameters. get( { "data", "2.5", "forecast", "daily" }, { { "q", query }, { "units", "metric" }, { "cnt", to_string(cnt) } , { "APPID", "2b12bf09b4e0ab0c1aa5e32a9a3f0cdc" } @if "%ContentType%" == "network-netcpp-qxml" , { "mode", "xml" } @endif }, root); // e.g. http://api.openweathermap.org/data/2.5/forecast/daily/?q=QUERY&units=metric&cnt=7 Forecast result; @if "%ContentType%" == "network-netcpp-json" // Read out the city we found json::Value city = root["city"]; result.city.id = city["id"].asUInt(); result.city.name = city["name"].asString(); result.city.country = city["country"].asString(); // Iterate through the weather data json::Value list = root["list"]; for (json::ArrayIndex index = 0; index < list.size(); ++index) { json::Value item = list.get(index, json::Value()); // Extract the first weather item json::Value weather_list = item["weather"]; json::Value weather = weather_list.get(json::ArrayIndex(0), json::Value()); // Extract the temperature data json::Value temp = item["temp"]; // Add a result to the weather list result.weather.emplace_back( Weather { weather["id"].asUInt(), weather["main"].asString(), weather["description"].asString(), "http://openweathermap.org/img/w/" + weather["icon"].asString() + ".png", Temp { temp["max"].asDouble(), temp["min"].asDouble(), 0.0 } }); } @elsif "%ContentType%" == "network-netcpp-qjson" QVariantMap variant = root.toVariant().toMap(); // Read out the city we found QVariantMap city = variant["city"].toMap(); result.city.id = city["id"].toUInt(); result.city.name = city["name"].toString().toStdString(); result.city.country = city["country"].toString().toStdString(); // Iterate through the weather data for (const QVariant &i : variant["list"].toList()) { QVariantMap item = i.toMap(); // Extract the first weather item QVariantList weather_list = item["weather"].toList(); QVariantMap weather = weather_list.first().toMap(); // Extract the temperature data QVariantMap temp = item["temp"].toMap(); // Add a result to the weather list result.weather.emplace_back( Weather { weather["id"].toUInt(), weather["main"].toString().toStdString(), weather["description"].toString().toStdString(), "http://openweathermap.org/img/w/" + weather["icon"].toString().toStdString() + ".png", Temp { temp["max"].toDouble(), temp["min"].toDouble(), 0.0 } }); } @elsif "%ContentType%" == "network-netcpp-qxml" while (!root.atEnd() && !root.hasError()) { QXmlStreamReader::TokenType token = root.readNext(); /* If token is just StartDocument, we'll go to next.*/ if (token == QXmlStreamReader::StartDocument) { continue; } /* If token is StartElement, we'll see if we can read it.*/ if (token == QXmlStreamReader::StartElement) { if (root.name() == "location") { parseLocation(result.city, root); } else if (root.name() == "forecast") { parseForecast(result.weather, root); } } } if (root.hasError()) { throw domain_error(root.errorString().toStdString()); } @endif return result; } http::Request::Progress::Next Client::progress_report( const http::Request::Progress&) { return cancelled_ ? http::Request::Progress::Next::abort_operation : http::Request::Progress::Next::continue_operation; } @endif void Client::cancel() { cancelled_ = true; } Config::Ptr Client::config() { return config_; } ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/scope/0000755000015600001650000000000012705421114024655 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/scope/scope.cpp0000644000015600001650000000320112705421114026466 0ustar jenkinsjenkins#include #include #include #include #include #include #include namespace sc = unity::scopes; using namespace std; using namespace api; using namespace scope; void Scope::start(string const&) { config_ = make_shared(); setlocale(LC_ALL, ""); string translation_directory = ScopeBase::scope_directory() + "/../share/locale/"; bindtextdomain(GETTEXT_PACKAGE, translation_directory.c_str()); @if "%ContentType%".substring(0, "network".length) === "network" // Under test we set a different API root char *apiroot = getenv("NETWORK_SCOPE_APIROOT"); if (apiroot) { config_->apiroot = apiroot; } @endif } void Scope::stop() { } sc::SearchQueryBase::UPtr Scope::search(const sc::CannedQuery &query, const sc::SearchMetadata &metadata) { // Boilerplate construction of Query return sc::SearchQueryBase::UPtr(new Query(query, metadata, config_)); } sc::PreviewQueryBase::UPtr Scope::preview(sc::Result const& result, sc::ActionMetadata const& metadata) { // Boilerplate construction of Preview return sc::PreviewQueryBase::UPtr(new Preview(result, metadata)); } #define EXPORT __attribute__ ((visibility ("default"))) // These functions define the entry points for the scope plugin extern "C" { EXPORT unity::scopes::ScopeBase* // cppcheck-suppress unusedFunction UNITY_SCOPE_CREATE_FUNCTION() { return new Scope(); } EXPORT void // cppcheck-suppress unusedFunction UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) { delete scope_base; } } ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/scope/query.cpp0000644000015600001650000001765012705421114026537 0ustar jenkinsjenkins#include #include #include #include #include #include #include #include #include #include #include namespace sc = unity::scopes; namespace alg = boost::algorithm; using namespace std; using namespace api; using namespace scope; @if "%ContentType%" == "empty" /** * Define the layout for theresults * * The icon size is medium, and ask for the card layout * itself to be horizontal. I.e. the text will be placed * next to the image. */ const static string CATEGORY_TEMPLATE = R"( { "schema-version": 1, "template": { "category-layout": "grid", "card-layout": "horizontal", "card-size": "medium" }, "components": { "title": "title", "art" : { "field": "art" }, "subtitle": "subtitle" } } )"; @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Define the layout for the forecast results * * The icon size is small, and ask for the card layout * itself to be horizontal. I.e. the text will be placed * next to the image. */ const static string WEATHER_TEMPLATE = R"( { "schema-version": 1, "template": { "category-layout": "grid", "card-layout": "horizontal", "card-size": "small" }, "components": { "title": "title", "art" : { "field": "art" }, "subtitle": "subtitle" } } )"; /** * Define the larger "current weather" layout. * * The icons are larger. */ const static string CITY_TEMPLATE = R"( { "schema-version": 1, "template": { "category-layout": "grid", "card-size": "medium" }, "components": { "title": "title", "art" : { "field": "art" }, "subtitle": "subtitle" } } )"; @endif Query::Query(const sc::CannedQuery &query, const sc::SearchMetadata &metadata, Config::Ptr config) : sc::SearchQueryBase(query, metadata), client_(config) { } void Query::cancelled() { client_.cancel(); } @if "%ContentType%" == "empty" void Query::run(sc::SearchReplyProxy const& reply) { try { // Start by getting information about the query const sc::CannedQuery &query(sc::SearchQueryBase::query()); // Trim the query string of whitespace string query_string = alg::trim_copy(query.query_string()); // the Client is the helper class that provides the results // without mixing APIs and scopes code. // Add your code to retreive xml, json, or any other kind of result // in the client. Client::ResultList results; if (query_string.empty()) { // If the string is empty, pick a default results = client_.search("default"); } else { // otherwise, use the search string results = client_.search(query_string); } // Register a category auto cat = reply->register_category("results", _("1 result", "%d results", results.size()), "", sc::CategoryRenderer(CATEGORY_TEMPLATE)); for (const auto &result : results) { sc::CategorisedResult res(cat); // We must have a URI res.set_uri(result.uri); res.set_title(result.title); // Set the rest of the attributes, art, description, etc res.set_art(result.art); res["subtitle"] = result.subtitle; res["description"] = result.description; // Push the result if (!reply->push(res)) { // If we fail to push, it means the query has been cancelled. // So don't continue; return; } } } catch (domain_error &e) { // Handle exceptions being thrown by the client API cerr << e.what() << endl; reply->error(current_exception()); } } @endif @if "%ContentType%".substring(0, "network".length) === "network" void Query::run(sc::SearchReplyProxy const& reply) { try { // Start by getting information about the query const sc::CannedQuery &query(sc::SearchQueryBase::query()); // Trim the query string of whitespace string query_string = alg::trim_copy(query.query_string()); // the Client is the helper class that provides the results // without mixing APIs and scopes code. // Add your code to retreive xml, json, or any other kind of result // in the client. Client::Current current; if (query_string.empty()) { // If the string is empty, get the current weather for London current = client_.weather("London,uk"); } else { // otherwise, get the current weather for the search string current = client_.weather(query_string); } // Build up the description for the city stringstream ss(stringstream::in | stringstream::out); ss << current.city.name << ", " << current.city.country; // Register a category for the current weather, with the title we just built auto location_cat = reply->register_category("current", ss.str(), "", sc::CategoryRenderer(CITY_TEMPLATE)); { // Create a single result for the current weather category sc::CategorisedResult res(location_cat); // We must have a URI res.set_uri(to_string(current.city.id)); // Build up the description for the current weather stringstream ss(stringstream::in | stringstream::out); ss << setprecision(3) << current.weather.temp.cur; ss << "°C"; res.set_title(ss.str()); // Set the rest of the attributes, art, description, etc res.set_art(current.weather.icon); res["subtitle"] = current.weather.description; res["description"] = "A description of the result"; // Push the result if (!reply->push(res)) { // If we fail to push, it means the query has been cancelled. // So don't continue; return; } } Client::Forecast forecast; if (query_string.empty()) { // If there is no search string, get the forecast for London forecast = client_.forecast_daily("London,uk"); } else { // otherwise, get the forecast for the search string forecast = client_.forecast_daily(query_string); } // Register a category for the forecast auto forecast_cat = reply->register_category("forecast", _("7 day forecast"), "", sc::CategoryRenderer(WEATHER_TEMPLATE)); // For each of the forecast days for (const auto &weather : forecast.weather) { // Create a result sc::CategorisedResult res(forecast_cat); // We must have a URI res.set_uri(to_string(weather.id)); // Build the description for the result stringstream ss(stringstream::in | stringstream::out); ss << setprecision(3) << weather.temp.max; ss << "°C to "; ss << setprecision(3) << weather.temp.min; ss << "°C"; res.set_title(ss.str()); // Set the rest of the attributes res.set_art(weather.icon); res["subtitle"] = weather.description; res["description"] = "A description of the result"; // Push the result if (!reply->push(res)) { // If we fail to push, it means the query has been cancelled. // So don't continue; return; } } } catch (domain_error &e) { // Handle exceptions being thrown by the client API cerr << e.what() << endl; reply->error(current_exception()); } } @endif ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/scope/preview.cpp0000644000015600001650000000434012705421114027043 0ustar jenkinsjenkins#include #include #include #include #include #include #include namespace sc = unity::scopes; using namespace std; using namespace scope; Preview::Preview(const sc::Result &result, const sc::ActionMetadata &metadata) : sc::PreviewQueryBase(result, metadata) { } void Preview::cancelled() { } void Preview::run(sc::PreviewReplyProxy const& reply) { // Support three different column layouts sc::ColumnLayout layout1col(1), layout2col(2), layout3col(3); // We define 3 different layouts, that will be used depending on the // device. The shell (view) will decide which layout fits best. // If, for instance, we are executing in a tablet probably the view will use // 2 or more columns. // Column layout definitions are optional. // However, we recommend that scopes define layouts for the best visual appearance. // Single column layout layout1col.add_column( { "image", "header", "summary" }); // Two column layout layout2col.add_column( { "image" }); layout2col.add_column( { "header", "summary" }); // Three cokumn layout layout3col.add_column( { "image" }); layout3col.add_column( { "header", "summary" }); layout3col.add_column( { }); // Register the layouts we just created reply->register_layout( { layout1col, layout2col, layout3col }); // Define the header section sc::PreviewWidget header("header", "header"); // It has title and a subtitle properties header.add_attribute_mapping("title", "title"); header.add_attribute_mapping("subtitle", "subtitle"); // Define the image section sc::PreviewWidget image("image", "image"); // It has a single source property, mapped to the result's art property image.add_attribute_mapping("source", "art"); // Define the summary section sc::PreviewWidget description("summary", "text"); // It has a text property, mapped to the result's description property description.add_attribute_mapping("text", "description"); // Push each of the sections reply->push( { image, header, description }); } ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/src/CMakeLists.txt0000644000015600001650000000320712705421114026306 0ustar jenkinsjenkins # Put the ini file in the build directory next to the scope # .so file so test tools can find both easily. intltool_merge_translations( "${CMAKE_SOURCE_DIR}/data/${SCOPE_NAME}.ini.in" "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_NAME}.ini" ALL UTF8 ) # Install the scope ini file install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_NAME}.ini" DESTINATION ${SCOPE_INSTALL_DIR} ) configure_file( "${CMAKE_SOURCE_DIR}/data/logo.png" "${CMAKE_CURRENT_BINARY_DIR}/logo.png" @ONLY COPYONLY ) # The sources to build the scope set(SCOPE_SOURCES api/client.cpp scope/preview.cpp scope/query.cpp scope/scope.cpp ) # Find all the headers file(GLOB_RECURSE SCOPE_HEADERS "${CMAKE_SOURCE_DIR}/include/*.h" ) # Build an object library for the scope code add_library( scope-static OBJECT ${SCOPE_SOURCES} ${SCOPE_HEADERS} ) # Ensure we export all the symbols set_target_properties( scope-static PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols" ) # Build a shared library containing our scope code. # This will be the actual plugin that is loaded. add_library( scope SHARED $ ) # Link against the object library and our external library dependencies target_link_libraries( scope ${SCOPE_LDFLAGS} ${Boost_LIBRARIES} ) @if "%ContentType%".substring(0, "network-netcpp-q".length) === "network-netcpp-q" qt5_use_modules( scope Core ) @endif # Set the correct library output name to conform to the securiry policy set_target_properties( scope PROPERTIES OUTPUT_NAME "${SCOPE_NAME}" ) # Install the scope shared library install( TARGETS scope LIBRARY DESTINATION ${SCOPE_INSTALL_DIR} ) ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/po/0000755000015600001650000000000012705421114023373 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/po/Makefile.in.in0000644000015600001650000000010012705421114026034 0ustar jenkinsjenkinsXGETTEXT_KEYWORDS=--c++ --keyword=_ --keyword=N_ --keyword=_:1,2./share/qtcreator/templates/wizards/ubuntu/scope-14.10/po/displayName.pot0000644000015600001650000000000012705421114026353 0ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/po/CMakeLists.txt0000644000015600001650000000022312705421114026130 0ustar jenkinsjenkinsintltool_update_potfile( ALL GETTEXT_PACKAGE ${GETTEXT_PACKAGE} ) intltool_install_translations( ALL GETTEXT_PACKAGE ${GETTEXT_PACKAGE} ) ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/po/POTFILES.in0000644000015600001650000000045612705421114025155 0ustar jenkinsjenkins[type: gettext/ini] data/%ProjectName:l%.%ClickDomain:l%_%ClickHookName:l%.ini.in include/api/config.h include/api/client.h include/scope/preview.h include/scope/localization.h include/scope/query.h include/scope/scope.h src/api/client.cpp src/scope/query.cpp src/scope/scope.cpp src/scope/preview.cpp ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/displayName.apparmor0000644000015600001650000000016012705421114026763 0ustar jenkinsjenkins{ "template": "%ConfinementType%", "policy_groups": [], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/readme.txt0000644000015600001650000000165212705421114024757 0ustar jenkinsjenkinsUnity scope template Building -------- To build this scope outside of QtCreator, please install phablet-tools and run click-buddy. Localization ------------ intltool is used to perform localization of the scope. Ensure whenever you add / rename files that contain localizable strings you update "po/POTFILES.in". When you want to add a new language to the translation catalogue: * Copy the .pot file from the "po" directory and rename it to e.g. "zh_CN.po", where "zh_CN" is the language code for China. * Enter the language code in "Language:". * Enter the "UTF-8" as the "Content-Type:". * Enter the translations in the msgstr tags. For the new translation to show up, cmake needs to be re-run to enable the recusive search macro to delect the new translation. In QtCreator this can be done with the "Build->Run Cmake" menu entry. Remember! When you copy the .pot file, make sure you change the file extension to ".po". ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/data/0000755000015600001650000000000012705421114023666 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope-14.10/data/displayName.ini.in0000644000015600001650000000030012705421114027233 0ustar jenkinsjenkins[ScopeConfig] _DisplayName=%ProjectName:c% Scope _Description=This is a %ProjectName:c% scope Art=screenshot.png Author=Firstname Lastname Icon=icon.png [Appearance] PageHeader.Logo=logo.png ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/data/icon.png0000644000015600001650000003305012705421114025325 0ustar jenkinsjenkinsPNG  IHDRxbKGD pHYs  tIME  93Be IDATx_Lw"28S~ ź3$$@={pQܣpzˏFS׮kYu]{z7 t~lI6 \B;i 󑘶;|?#xʏhzw,֯_??≤pdz55w H]_L'_oeBA쟱p5Owkݎzք@r߿G=T +N>ٿ@ b`}30p@FezV$@;H-b4?a [_tN0wHY-3:#ё~3a{ѪG&hI=<]4;W}UڢU ج?n4u;ؤӢ̏HV: <:~hE @@KK:w%}DǏ,A2vh -] юր. @RӴ,"(d{~XYO4 @?~G)v2, ?_Qd&>Z6 ;~&!<4`P 2*&@:)2K'i8*}rtԿk4kϺ~`}y% 6+7Eئ$- ,S+`Ϣl{t.EQR炧k})AH)6B:t@Z$~s9O$39Tt@F)@]5if οGLxf@xD;٠0 }P@Z;10A?+B;*Ti{^x~#ZjahPZ O@9F+9$GmƄB`@w;4a0@[h &"EJ\)=oi-@?-6)5yBVT ?0j_vJZ (q- @@Nu=? RR{^7؇4slt{.V' W A翁>V&;4~1TLRtt@cWO>j>x]:д*xT?:vc:I*;+д?LGϪSm5m+q쭏ѥ G}DW9#mGM]HR!QR3-F}}Z.* :H\iZ&wzkVRqm]I=щ +b)u&9A Kd*pW٩#w,Q6>c|û'a?R[Tm8F3xUR7mjce_+5uj[V^&`?@:%#3p ژKB{x`$ă>; ;`eIh [iu Uߍ K_WX`mld+o7éңgU~_r>mq8Οҿ+Uv*۾Ts9U߈aӯY\f;F. diq\/kymkOy <8+̎_"y ߈˷,QM޸l/4΍|Am4C#lPS+ƭ_ i`23?"5بB2Al=+`|!`Z1hǭ L$ h4&o^62ͮqXԣ>#rMw2q/(2/ i;1x)MLl]J\qg5Lw+ܸ]OEpu:ʶ/XoN^ҞK < >7éwUzlR +;s-WJh3GRpDߞ;B8_t2X6*mОKU֎:Mww${雯TTSG`BJ5{3҂fvh%R?RyEiЧ?'Wܤl;@@ ?Ȍ]'.jSL*rp{gKZKфf/Ӧ/.] 0}K+~}i(4["fgfDZ^=ݳ{^- &<*\U~E =4:ƄCߗ4?Ч;mZܓL^ hN?<S? fc3?kTv '-G䁄;JmFV'9*S*RX^j)8^-U[UzJk'o\NjIX"!w61`.VMé3KS~yeV@q(c9}'h{fc٣@;=*&k+*BK |R^)忪]/d<\*`oئ2NsxUTMMk)8 _䳤T@%1?쭏ul{˷Un{R]`  ѿ*^7gufl7;;U3ִ3Gm3 / \7sST詖WTju&%|܃cw@ мGQ23 Q,;sh)8پN j7M|QSzУ>6JC%5f ?'Y%<|2~-84ਖZ~΄˼ UsTҎu/B@ʵy&ޠx~Rl$:(W4Dj`C C0 ,-KOds zUv 3 >?"OK :l2<ĴM*OrIz{:РV!-",G7c:WTݷ4~MR xNTz矊uHvB`eIw iahPTTUJ*L(h}[S|Iآw۟{o$3!ګ\.G4߫-|OJQېv*JOHgv(׫-Yd'_wFzlݎ𷪸A6nLH55@ _Kǻd%sdpJ?䳧~n;( Tqm\Yy΀nG \EH1 egN3 [mGszt:[d@wYR3-HO>3n2Z2N[,+P˿}ڪ9i|4Cp@v;$fmAy\oBU?@Gۋo*_&|׾%dsEd7sN=|WEn:Aw4reٿG{Q M^st@n~IU*=j6j9}'YX7#Z XT_ ,WY]ϾI;oBSO6$L߉ RHSmثpH 72I׾wZ q)=z2K˶xɛW=Jn`A^rQ3MwN[ iyZ_J7.[暷Tf @0\HFsm%71#9-qGMJ6qOe>mգ>vۂ>62v\dU@C^Xp??fSl")=:rA{L$`CѤ[OK$>2}9yƯЧ_CM#= U#Iq;iIaPDMÓ3`n! Iy!z0TQT5zB`@L,7 h {cefb/UGOV&cw'PH}=R@zP&L]@g$|o0 ;@g$  9M{ B@;FH@:?wGS ฑgPH\wM _=1بndij/hu_}0?gQ&Z dh}I2yqv)C 4A|J 7{ &ֺq<0Dk&N[ iC[РqJA3aD)+{[iXȠK}L+{!{z0R>s81<7 $Y44:߫>n UFn^`" $+:uHبQg' w 6T߆&o^6f`^ĭ& bmaPI4Ě[Z OiÜkO{ 24^vߜ;jpn+2?بoQz`e7aʄs `+m%.!h`;f?`qhИY 37_=eF-p3dI F5i>)pW]O=Z i10šA3VS \Ce9}+DYx@f@éF9jTTS|{p-4}C}I;5! |OjY OiV!'T6c%y\9}-I[詎(oMxRwT讀+3!rN^Kg?MңgUJKۨ\wە_^y_Fwln2% |q*oMi ';pH9Z0`)KUrI?䳌ڷ*;s-3)|/57{o!Xb/5EJU[kswyEKva}fTXw0Ez{wUzJh'0)FHѴxWTr3@@lj61?Ч4vsgBd"SlƘW6ٸ6sX~>T,%rvع} ?0!F4tGj3[>}&$M HtdQ^)+T`Ӧ_7s#QGL'kY`ʺU͔cH} D-G~ $ָ(%o{zǾ n7  ek-SZt6d!m»_ +1mܴB`ui8`onRw =T@@;j容]iX 潽 `QM޼BC ΂m5l l`e>6>{ `{)^KM`` j2!`V.wb5 p6p+ta?Zޘ+@c"NȊ؝ϊ7 -oj TllCsRo~s_<cG~CQW?|;_jxjkA=0݊-s9.UdRp$웑ﭵAta =x`S!]m)O@6BΓl_بe&twl5چ~9{NtM<0:=Dݷz8ud$4`_3 2a/pW|>r"I ?Ԉ+]C ⽿5_VfBMpY jCwp]-`ңgθx&>Ҕ%m%.U|lRsF^Rb@QIDATBM+j%?! *chLwb>vHL 4so{Op*Xv7)/S3O T`~%q0HsECn*! 2Q!%!Uǭ}̉7_ 1 |SanƇwY"띿$M޼̺ݏ\iĘ^$%iR\`l׹a2JÎ3)TO>crNnkՠ3j 7~cbqvN\?Xzoi~ar˖m%.1G '),q+3!F CNSFV|̍Kwzk |Ck aIX|+_[`1; L@5@F-* wq(=zV/~nde&񫧸ɣJÏL*›+ ;gQL)oG4 W3}U`˷#O[l rW5}Z"9)?xk[ h&<+Reg,Tw7Aimk 6h׉/}*j%i}Y )qxYޏh ؚw6q-; >"Te߽ۗQoséF|',o3ߞ;BWiʧ 5 &ˆ)WTfIv$8Qo#TB kmPqm%'mdm:FT*+JgYt&o\N׽|vhqhШ~QMm;[&h[7@!$%]~YVF+;s-+3!jnW} y UQS:KOB`@+Rェk}oĚؔ(9ؤ2n2SX{؈Z Om9U{WRJ*#^dtz?.Ev|ְ^գݞK=?a98w!$oM>̹N~=+3!=x*tiqΟux&#ְFX $uڟw²[6JM\ n<y ;j*~ s +ð$0=:oQnMwR^gJ-pWi&YK *ûŒ@+sIu@nGSVi84 Ⱥ b,C@2W9nA? 33qUvE" !`,YN+=vp6\7[x T@ fvHR$;k29z*?* `V$>0yNRQJY tA*dqhPOB` _Ŀچ3}w輏#Xh8 @ v8Uvc9~m2;IReDN/T~E Go`!0'\/k)i5usɯj/7?ULQN =šG{ۿ!ב̞%bCUVM޼9@@gdA~yJ\IMx=jmM=r1(DǙ^XDM?-YcIuE^: &&Jjg*pWq͕gc[;t'zrJ\sOOk6@@z_ +9Ħ?wU%! HY0vρ?q%ub;!v9uK4Hwl?IuE^^m1 =*=ʫx"*d,F*9D#l Ŀ%,u2UI?,-#RKh/6:+61,i? ;r,j]Ϳ7'(ikμ"X l]e`Tms3?f ^XCLg_Qޫ;jsv珢XlW{MNE5u?Jx)8پN˭xxPCY}DE$QʵwjQ]%^-57g"ȭ?曓:,`fpI/i-bOy UQSJWʹK-G5߫`ĭvqݏo@_|.Hz_<]|3`Pr4 A, WTn:ž}i10TZF}eM޼ eO/&U/I .:c5ql0KjOKR(Yu&R;? X OYyӓbT0k`  VXOl Y5LȮiT?&6I0\/޳%gȀb`Сl`I| vqt0 R!`e&d @!?T0"k{Lu޻cٕ `9Y#;N-Y`i [A N @F1!E}L qcMw߲6EOcϞJh%I @5Gk=@Lȼz. }.aF5~Q4}K:м)O9;0L5&Y+IҎ_B`@{Nיî}5 ,=`ɛW4}+Ʊ?cPuq}s`X=^iP J>x=Cy]&~`oB<1/W"k?)@ݎ>zh `C> x%oX4$^z_ |IYZ*6B.{gPD'AT}̏$ݜ90@5rp 3Q+zhX܅gQ?$\"4]a==ю}F *9$wx"M/"aI>wʶ/ @J$;CAO@O!us$mثM @{9wWY-C+m;wwb@9;?ww ZrƳ UG44E^rʎ+R~G!z$]pwǵwRpDͿ隠 |- }s93*@_?o6z /ۘԯ%+ >( H6E'쨩S~y%- C0&ԋUEMڤpˠ س?ؤm%kV 싎Yt:6:-@8WdR?D}xJ3w;3T9ȺJc:I*>[I?V>韎ԾEcu)@D3}K}k0` =tɛ2cFyFc%俾~6  dE&5KzAҁg;VS6^lg:QSgĵjqh0/pD; _C:Qh*vIq CVc";jMdUEt4yr҅I?$%EvǝNÏ|بoi .S&D-G}_GGo( V! ^gXezGTWOE}m%.뛠JvN1yJ:?r2?\p?ߏUCZ Qې+'{z*0fwR;#D hQ Vܘɚ~xͿ?UYo:6J# U*pWi{E,IACZ w6+;s-kfoic]k@} WbdͿ?0!N#e& 2`,mAE5uY Ml_FD:lN+ܞ;W6 kuV2f2`5@ȵIENDB`./share/qtcreator/templates/wizards/ubuntu/scope-14.10/data/logo.png0000644000015600001650000000563012705421114025340 0ustar jenkinsjenkinsPNG  IHDR@bKGD pHYs  tIME  : + %IDATxAlW^J v@vW |e4T7V9BV% .KMPCgUT+aNXsn09 la](=g3.? 3}6-Ե8x% IB4ÎՌ!^7䓅rEB,8CH*,KjENom Bi-6zl[YM]RWc=A=1 Bbph:!!D'K!Aشgse(8\Dl"ؙw\@_v7_aİ3[AK@1LCzbE b@iiXJ^L,3wJ"^~{˦L]PpHX`ȕǦˌn^~{+*6FcXU쫋ӎ6,+BzǻIشg/8ԇNNƳ˟[^x`Qv8)i !ql?zOg?xrn~=TBzM,3V\GMj~;>S,3\.2u-Y*B?JA2 ׼~'֧ǰR?i%:BOb# oh!V߿pyq0*:|1 !dmx]#x%C,;ɽb #20-o9xOg?0 Ԫ Hi!tOhBzxqJyq Ç}M3UnlưL',kTdme3!%p$~]Vz`'~< L85ɔ~l_n$SL}y )ϚV8ժ tYCBӵ.Nү:V9|ەp-Y((굟e뵭Zy:_QqjuZn]ݹ0MJ"pU *T0iX+m4kTkL < ȳ}qITaBa8_§sgecfX,[Om5Y(-=jÛF0?tغnڳ;>iU4\(y⢀v,>B"X\g_]lUt+ÇmF74!l?5w\hȁSϪnB91x2um?Qy+ #e3Û.fi*zDX=#qEL #N6E3#,VjU<9cܴYlil K5 {Gi?@3.:^{\UQs/n|j~vͪWoa\5%{dKΐVKԵd<ƶ.X{TUL]+r껗7u-J9Q's/ !`>Y(n^ujY㲦uuZ -2:k<p3jFExqklɹ$*z=*#/gN@ CUl}+rgܨ1A/PvlB~2L4ב0p *-7=:]qќXN0e`(qW^޿&6-D \WtqVߎdr:L3.:ϧ8j3g 䜂^أ`4-љ91h9|$[,级 q'ǘ«.ș|O{e/))Z;e!bkfr.eS"3!O\(8]X%i&hbܞElyĭVxt?ͪZ'A{8VӈVHmvBZ5 M-+iTwn,7k$3ICpd aV.dv>(A l!8*ÁԵBTjc3rφhk1 A6P˶ q5LY5ljDy& A0Ufjt@S[p&:wEg4FntT!:u&`*]dۏg\D=?)^؎F_>ڸ(bb:0 k}'6+zmRXKdUw@ H%a>6T;Ǟw?TK"0MַkZs=KA5d!>){uB|IENDB`./share/qtcreator/templates/wizards/ubuntu/scope-14.10/data/CMakeLists.txt0000644000015600001650000000021312705421114026422 0ustar jenkinsjenkins # Install the scope images install( FILES "icon.png" "logo.png" "screenshot.png" DESTINATION "${SCOPE_INSTALL_DIR}" ) ./share/qtcreator/templates/wizards/ubuntu/scope-14.10/data/screenshot.png0000644000015600001650000051542512705421114026565 0ustar jenkinsjenkinsPNG  IHDR@?ntViCCPICC ProfileX Yw\\b $4Hc~ PP@nhoo } dYW0Fzj/HyAp2x1jX~amgGcpP$L ] ʡuL>PzH㪃<2!!adb &$_&`Bg } ’&d,oAaVdLP/[; n$B~hsޑЗ %<\ c1 m~17v%9 %(kgF:A:G4z\POT] b:":G2#y4!mG y*Qۢ}|ML!EeDC POto2]⫑1N>"9o@%yBѳQd10 ^ by  _ vM(,@B!OvF۵ j+ kc[ !.Fc Kx+a40 g3p(j o ֑`dža?`ؗq+ oKm'1Gm8+c`F j1B6 Ũ@K 16UH=uǗlMS`OJ| rOt'}]d[gmG ?<  3 J>?@8,O}zzFƘf7M4M:M)M3+EZZZ1ZڝQehh1Y%}7KGC_EC?@ `@`8ĐP0Į̇hxq $dt;f4017Q̏Xp,,,,i,7YYXXUX]Y>`gCYa6ȶn~}À×#K5NNS γ8pas]z5­͝}{#Sųǻ77<_ _6_ ?3??!@y6AAshBnu!q!$[Bo5[DElDD*DFD)E5DDsEEWRMs[ljWJJKDHI H$5$$/JJTzQjD}2XMP"!YYC 969k${rEwʟoTPUVZIR1IA񛒔RҀ2rrWi_K*ê̪6)?Hjj3" ԇ4X45Nithb5455WԴj}і.מչNWH[;'O޸>AH@Ǡ࣡a F F$c-&h&&ݦL.L̄*vl52?k>dkmQf`nyв͊դ5ɺecie3j+jj{YeٽswwTk hVWzݮe+n&nnݟypy=vw,ٹt׹]SUw'#ν\{>$xVsWo`G(",{Yxx-x{zdf~  '/^\ * v B/>)4(-/@X_txrxVĹ$Yh1z11?:x @WT؏qfq1 G&,<:Ԛ(x,q׏P :vR.#4c뙌qljSyQɹK;~|]H^Fƅ /o,Xs˼.]!^.QX[$VS+)pj5ke%\%i%?KCKǯ;^o+S/++)?SFMunowg*jZ6v^:z톚rK>8Dtiaw?hsh~lٓG;t;;:j<M}&gg[j&L&&&_~7>Աr>,Vn15;5>>Sg_ t-/L}%}vjstIeu~y{?j}\߿8SgæVV8D؎аDZ{u!2y $m[(>i4-: #FdpŔTǩdӖ 1p22U lw88=sJ>)%/V$^"qM< DX*;Uհjok45Zf":(IVbFz&f_v|4krjzfvnCcӀns(w3aɰ+'E 2FB̊3=%N&,`졬ěyqdS+ҺN}K9cqlu|9ϜW.H{xBW]k**VF[.Ua~fJe٭geүI]7@q_QmCfVGm:5(wHwvn<|v+n }~J/ڗYCv _- n&y,$~r]>֙Y?)/[ =-(ZZXܿ Xa 4DQ~?]:q(QSP<]aHdlcFƳUdႻ2O o)_;gBD ؐ*QYmKQWXQQ*QWVR}VMV7:DýFT}&馆 fw0hijbbjawCcUx[W!n/ܫ= vur=y{ozCU{/H@q`dPxphHph`1A$FG@:z쁃Iq)^̻'_$0Il 7D3%KXWxyD*)(&J>9z%fdか q;92#ϒ:k?ޞܞģO=?=>~f.cZz*]rr{7\͟$|YXoV_6YvLܹK7Tfq׳*fvNޱơ&CfBKFk,O4t,;Z_׷n˞k}^ v ۿz3?V=h4ْ&{N N"L,@ip(!<-@,QT.!Dx 4 C r@EΡPhJ*PALf =m1n6E9%2rʞ >u=*M2m ]39};$c( fvu]_؎spXRz}G71\!ga&~cq{V@QwW*+y*Ш|PmU~Xi%MϽzo\53:k|$4MlmxNxgJW&7nw1ƻ\v$lKoR+B@]EXrÈHƭ$8r8. }4Dr ד iO˥fDgdxq Gհm7^V~B}\zDSޑ/ÆZGFcƆ'3=K;7)ⷌ%;+j?jKlo@k [:ȷd`ѵo?~nln0+ aFw@' bx!p7!0#3ՎݍyTL/}]3))`nU5?u'M]=a1ɝy,[98's6O6o_DdTE F>EJj#M>-k8C8u@Bצf; Y(cV[GuLO6nwe+n*snݩ/8Lrq䓤gwOF_}6Z?~Bu{k^3sR9͂[<QWT,,|w޿bU5u?)~ܔLٜ2*$H?e%#Lmm-٭1 @s9dfx&k0X_iTXtXML:com.adobe.xmp 320 575 g@IDATxiЭYw3tZ=I- ,@ؒma8L`1S\&**Uv~$Ic bɌ6 @ ԍ-ݢ9p׷zzgt裏;C:+p}`}TpWq'Ux N8T*@P | \8W*p8{P*^C'a*pWp_ŋw~>C Nx8=pU\ |/ށ+p8;P*{ | \_Evۋ_?y~~_ z8zЮ \xg~g~G~$'/'~闾Moy y{~~!}N籘+O>w~w#n{gw}7~3g~?}Wկ>uG힯Rp ۾>я?_W>~w|K_oo{[ʯmb}<_ȝs^{?9A~+kܺ;~x_iПvWArٯb//ڄ 7yaЇ>>ϟڹsۿ=|iYӻ8(a;*-{;~OOy\:wy}u_u_w9.϶9B?& H&9RA2U__W2cWkk.9L*OoE/'? |9u>\țΛf_?ejoƜÿ鷦\_pʲ*i~s'Z>ٿ;گv}nT{8 g(|1odi7pç 9N '%_5>9~sٵ{G-ܗƿ7 \7W~k&߉~{{.w~~e~t5ɷƟyw]w=>.`so{aR~R̐n|򪛷[[˷|K~b3 ?kO>? >XKwp*{vۜO<|e~SVI7 Qr~6;wݿK7?p3,wBrB=_=y~FSs=_O|Z,*p[C.ku-㫫ZC.W$k/Nz{-ٳgfWg3˼%[|+p?? CG}4pN>['̿3>{M7XK@8(U9=|\~>x8T෩M?=T~>x8T෩ <|;vѱ9x좂 La>P&vðtC5Yn \rjf}V!0 03Eq m#VN < ᮯnaettt8c] l c'Mc&ٻȚz*5$a1uEripZ[h宣jT~ThN6<Wt_F`] /]8~RPdƵÊ<䄇#U(YOfGZ-CbzEM8qsYҲ5\h ]A 'Pdd.)Y5K@[*h2Xi:C+#UCY1-,P+c7n$%E<1C pb5GCifPZ1`m0áРg8~ L฾z,g/3?o OqJҎz&n";4wD=3*@bw},x#1?VQ+5AљbC/Np):rjU#с/ƀ *9ˡ!$Q#3@W̬'kFdX5}a8x. d7>;b:K1+OdP㋝'&c+LPl\( '-"嵙 f]8%p!*5F-a9 X*nq8i&kn|! ʹ2Pb0w rszDჭ mcM$$ H9%Nq> S<NXCN|'78{`Wb`Xȑ)aH)$swca#Ӣrf͇i&c`%+X@to#X1΅ȢQJ ހ]1dP|[#B8@yL7.S@A" \u.g@Sq$v gpk:y_H†ˬ_cojVU>-*Y$9*˝sAYgOj"\ 3'qnnox TN@J{_7 C>9XOZ]&'8pd>*7Nʐ;'\b/džM]o4_SUEEYU0['_MY q# pSB@d&&JKHZ\j+#/LlVh7kU e'LѪhMyFs6drjAƤа$ʼ{[Hb"SAQ6VJ-NG8u,e'ݞ4Լ4[SGp8389ߧ`Ix7 39:obE E&*t$.p'{lVHT!o2F/so6cQ4#5ѐp)CW4-08цbzz='(Mѱ.x+Ӣ.%:j4}M Y&OO)D{/8GĬTS3,.^m0JUΥ߸''  KTjXTtHb.+Is]F3{$?vrMUkFS9aadQB< T f;_7$<8s\?( 9{+Z䎚ik-WY9jVǻ~E^[E~9cTE$hJNKH`c60L6v"U2a pE`s@mp";182SbhȐf zˡu:yi`N6 )N*["@dE`0ؾnRÎcnp H,Fx-!bSm> 1#,/9׌'J R ' ϯ# ͖ /s Qy lºb&y> W,IEV*0:O҉-q0 Rpjd/x@(r<-t$+(9S"# 7WHcIxQkJBaέ3iD\8uꈰr h#?'뵑c 1ʜ°)aiWYE,un/hD¤$x̀q\i{,P*y *Ƿd L%ܳ|4T0`X"H1k3)B;5T<3`FPTW AYAתBXӵD1-GPDBeIח{ X8O11wyC8F\OZvL!Q ^Vdѵl'S;p$X8Lh{`3K| [G9:'G{oo=z4gA,s0sq>|zNUH$_254,a| Y|܎3G?}?o|_p:oWkN][^|gNS C3fZ6NvGr1:SƝ7c*:ƅx-Xh{$ EBJ@I%abWƵ7Jk(Bu?N۰$LC3# 3Z1\%ZEM ~Ys{0Bښ$rD.Bp2&$5SD3`YI*9bЍ#qOY%A['`Aиl1{zlCDSچfe+]J FG7o{ǽOǨ@1!Ikgy)"3f lݻM'PÀڠbVDD"yƅ(.]]<o'|7w~~w>#:ԇsgΞ'qf!)dΜrKJ5$a&ٗ2 M~D`RX 牀ļ^#p~#/Łcral!S,:W/q.Dvq"3!bʫ''4bAz.x?wUO[|,Ou9voy/WqCg쀒=%؉(`s8LoC 48C!Z CހRm-50bTK.-L ˃:T@ ųtL.V@$bn;'N`.+9C"Bu@@ dFVc-YΈ)9_t{I⯘F&^#Fp18\Hg Ԟ5ەzGۧV8NJΊ1rE@V<WDإؚ3$$~[>ɳw~/{ Ofם.:㯽-7q>ԽĹ?~-z_}Oxkn;"}?'W>zO;v;˿+^s ?bʳtd~ŗlL.Wk)ݨ_“}KNd8kV):,&#DUZV8 PcZM|YBq> 2Wh9 n͇O X,6ˉ+b` s2'{#i(nl)}9q3b0[LSS8#\;e[GYȖz;u8):tB!2RϹE )i|Z=51XLqԯˊ=a]1ps)ҢOK_OO4tzWok{ٵO}ox//xAιO|WMw9vo?|;~W|{n=s_'}?#k׽LѾ_ӯ= =O|Οko~}'{'n+_@ R0xcw{< h#jY9ru՛^o4h&R\ hv ʼnV3(vG5 5DSn]ȅ4`'(T]Ȑ(%УpٳeGge y63& A$N^[좡AF,~eZuzUMjqaHFoȄvŽYŝ|w2q^m bzUD`EZ Ae)i8WgΉR?e$;Ulʖ0Wh'SDWm`bxt~_/!0׼ ?uknSg=}ıK^/G/_My/?ӟ]{/{cA;~ԭ/y 5}S7t&%ϵbt?w?Y|쑋W~՛8[2.&׭4)ScxW#R1B3:s>o15 w;Y;t dF&uixڌ! go^}v3YJ%z;h  EL0dc^px*xXB,%~ eפH9lx?yr\ SSVg\i6 2L8X<5bJ1]5 TE,3#4u7<}BR7ђ`Xg̐bND [J$i#rVOPjՉ y!- ʳ!8c M!6dpCX!ْiTdbl8+(ͨ~5,"xmDqE4 zcw\3/ڇɮeBd^|۵&D0 >- o؊U_p7s)ibZl+g?W3 $d/چ_*EC.HdJպA&8#F堣R,Ń |Y'JX槥ϝGQMXdoMyB)v Y[)Eo)4GKȁya)t4jr-xPoqduQkWg̼2cȓkT:84.޹2 zFA1:DVhFBV.P=N[wLdDbZQ H-jTMx%9ouo8 ?~ ^wx}b8y!ʎɟ}?;ۿ׼G9̏xש_:e*rxVHGn/~w}"%47z u('S}moO|k_q -.#)qۭ{9әO 9v0}&1CX;םrM/=pBB G]0$Fj0'$z7 p(RDˮX% $kC* #.u U7x ĉT # }C ~:ODVlg5HD12"993&Q`8jlG|驋zP= or[ӿHx~>;sh .S/}g~N_xG 8:?C]gvGRYrɢa ÿ>7e6 37ts?G=b/lgͯ.=Yh2)_9rRBÅ-[SxЃ9%e7)O@7CS7@77,#p\ N3mDO_8gP(Y1c=n Ss:?z<r/>C=p'AS>t_uf.;wɬC¿/O5+$~<箮=bz¹לGwV gon" m.4]<䣬eHnőHu\$qkT@ +}>~,_Y*5@/x͡L"LZ,+8h;Gy.ȀcGdy4#"-K"`Y*)[nYW-=903B$~Fk fq9r~Us%мtZ({v5c&L6A' e Bc5O[w졊\7OT6S_DQMpڛn+&[rV>C`x(?|kA?}ͷ^JJ$nj1%wwaa r-/<)E'N;y /0T)x$'()Bd3B5Id.iN5z(3EW hqF0+8F-3"1S*"1Hc$1A,i{f#Z aBYF ХEDXPяxr 003dՖqlƓ@yF[V,WVF L4s8C bm+\Xs]Rbs!ab& jC ͂ K wٳ_ 7Vy-14"(dij(TVB]zEw?^tu@&}5"gZjA|75M֭ΫXFV‘ #f6 έ**X&p5)ORJ`,n.@nQ j=F&Mڕ<r0⮤/fRlIK^"{9ʧXel Ld[$2%mQ6 ˬi̓) Q#h7Sme%G7ޭkFDl@7ځE ߟ)Kz։UX*Ú r׾& װuAbAΓ(z3:I+7AejZ^OLsB7bQ/ d\&or\cK"iﰈ)h!4uX~mfƆ+ a zR M_1b9v HGD}%|QNc9^!5، i.Y3 DHƁad?*+^Y :w;P̨9sL꺴xweW cqD$XpJ[h'EYT Z$Z@D(˜`=:A~St!=+2e#g-27LE(h9Co)%4ث34|wA߈m ({MMD<%<+ NwfқFn'}2#l0Ϝk8Z_Ckؠ"6߰!S08? gKadGk= AdcWvQCU,/}L 9$ӌ(宂$s AS3<"ϫs?xdfq0QZ:)nz:9je1`1Rb>J 9,dlgV$_7Hm+9u{!Ҷ;Z-` tEH#.FI dI(` I5ruL_Fb=XqJ@c$A!t}-sw:^f]ޘ:V3YD']o"4j=(ć 114E1n:fg+EI pڄlf Z6v@d{!?A ܿ/Vc”th`o2Ǔ*lGj. EK  EKp |ŲDH#Mg0IVn&WZ/e'B/$~k0Y* !EȼDm2u.@NFnTpHN6Je}16:ȰbOHD1xv V釼`qY()4үW(p"1bP2B bD#eP rע"Sy&1m^E`n8q!9b,tZY+OqÚ(=n;YD8& %ֈ j֮+ȄZ̓']<`շJlل,@;G(-dGҪ-QM  idhrPP,n4LB`KQօ< 1Ca+<ҌĀhLu4 QAC|ncD޴v%\ѴHg@ A =;ĝt1)"NÁ'SݩF(peWF*Qb=ng;VǶFN GFU Dޣv #YxmzϜaf(H(SDh)],jo[# D; b]Ui&F[׸8kƜB9mQeb- #\xao䩮X\e@v/hTỜx< U҉&RPҀ!Ea%SDYHF:G@PrL*F;t,%_bʚTm9B4f :N⃫n 1*L>,,Th%KnYck0k(E0%#riv6AJg+ؽV̎Qǎ.I#Bj*EgY-6`͢ye)i##DBQ]1qъ۴0ͧVa`(mwXXFT.(Zrc ta"}}Ĩ!L;Q&̉!Sv0 ZKT7g:2o#>y{EԎD1!uҋ R npE0112ަ!}p_ hc Ok[UZUynWHo$D@#n(Ѱ;_ Bgu^(eoגּa秭hV>ԓOJ7p;x^vΜ9ݲQ&"G;}9`p92k+yllYeq $Z3]b)/AgX Ǵ`Y hJbYlpr"y%8jh C(D2؁oۨ17ixr"bN`tFdav.=s5xo ™ӧN%%LhV^d;FULw4/]6Tf %+3~(#[Q:z pl(% c5g}\yw֣~-0s6R2RēΑk׽3h˸'6\b]QBpO]>$ {G~};n뮻ܼ4EofD/+L ^=d(IEv@/Fn*=Sr.̉L}y*qncn(VF)pA&%8p1sDL6|E Zs>_[{MWewj:R~c$V,W1?M1Nmk1MS&VU"Iyqhs_0t]Ųе_(X DpӥX` k!)vMVz!d"Z2BMf~t_~je>v//p wu7tiߠɢDNL1Lڅ^vaN5 (VY= P<1S"<+G=zL);%He0fXOLBՂbCaTHoSz~ÿ#|o'Ll̜yu&d tB+֧ p5cgT͈v8.=k iTPﵽ0uVPz{s0nrNq,p 7b[%6H#ZeyaEC #pSavRvƿ.ɒ;I^y=:Ѝ7|7ߔܶ(*nH62_,ݪaFdjl|\bJgH 2,@1hk\^ WYXkg1n i!UUbdRʨUHLSzbXLR>:s~ӧ?~C\w}픞;~2qVOP )HR .V!\IE)l"kfK7&qg8#oV.xPnʚ$nJOL 1g,W6\ P2gvEw\Ƹb\M:=l"7u7ߜk 7J-j zf g{ʺJYU֨f˒7-&VM5b#T2jE{2rt/r%DvUT:Ra!rJ9/+EKܦݟ#͍U``gN6ѣ+RH5T҉~kqKL.)BpU$֐nتecڲPQ59dP SK2B[itL/I$rg:Jt[H׭ށ[zoyM7=9I0GFM"@điI?㽂H6`ba(k> ʵ>w"V!׌Kh.i;Ovi "=4enzAts?Z@e+x> ÉLjUM3H4x !m8AǨI벱B;Lf0Gk,}; !+cZbԅTS$%-Ѷ4,k67;4MK <؃dnwbp&dKTMc<-+I7) ILFWٷL0h 0n05-a'Vx ѕB!U>D*"Mq[n-|ޛʸ\KX3AXu) ^2O aKqj yml2xAhS'l;,YI9#,hpT-D*(^ 8`ÉbAtsھ`Djqdtf-)^5l|\X3U>1h&9;kO?(x@iX:C tܘ@?Ax jrJ6Vuq=&<0+"1Aჲqi4;CEv<fT>$ kb8d=|)Bۤ&+%1m896),m\`rI␆1) tW\U3Dʙ4d,o?{aښ/ kvL ɓ.`4-x'Naۡ q}E hfb=scK:v2Yb~ЏLUv1&K^~ f ,JGO7Y39Xfq`5֛i25"FlW_d&Ҙ֍uGq1!Ŵ֛ԣ]Nj3S|ަ=]ҩX2LfDZ-͖tKǫ~1Iȣe b+ԊA^Kdd5uBP˚<+Ů+V0 : pk5CdSȤ9Zyj7)%3Let}5EQA w2'Q`b,[Vjn6H1}K | Mnaş\!sos׏D:cDtL6RFkmPzF%QxZCzq5H kSj] ܮkqZ&;q@("DLU6za($X1c䯽])qX\=a1K\.+]w gg(< >)#+eKcߛ /ʼnZK4SwBC19MjHѝ:[!e68Hc17[ b\[Y6#EHW 5)!5&,U ~^P #fߝ*#dYije"[c+%o&D 븜幡ɻOR`$e0 |)|7VH~ .rMvUtAy+F=V!g*j,n%@}vc8N|߿yXAGK\GnmzhdQkn=.wc7^C~~RriجhHAcvL6g;cygQgyI[f\wVJ\^ yYͻH(::ŷ&2㘔wůT;G'0ۨbn0FvG\L t} gjP1; >Q(@zU1$ö K dN٨-I4= 6:+SX+",uZnҁe?"z@c/&NWu)(z:̳qŋmS~dǢˊv| lfm6%DxzUeXXugAm OmLĀ =9ņ@!|1LFm$F ` ؃vofF*)cָKgqEA?&c8JC\d CqXImFq,30Y¤in0J?z#c->K* e-kJNTJۑ6ӲWßH6m_P|,恶OAP=e\(u]YD>VH]?)O\oe;g/| Ѩ!kbi$dS|TrG%R垐D`9lLS eV[SQ,8%]h F1isna8/^:{] ĂfZ!ѐ<):}AQ(cq2Xp)4ce-Đ,x-CLqaA#%[`#z5ۺHa9 u뢎mLcE-'|8p-9tM+[~% |lP6!3L,NbhE|]2ҸGkHF%̜_(>|*?,7jRb6}5Yd5fq4Zb%51X*2kp`S1Dd\tkiި& [9 Lgp4Kh6H+Ӆ,^#h3Y {-R`IH-wJl9T {3R&'l`opeF9O_K 90i$9U[CK^moY,+^ |UwS Ƅ1IlSJRdlyS֠vU+)j N&VeOb$""XcLЎn,?셪x\F^5[`]ݞ}D"`SϜ+3.l,sŒe`1:jlBnka&{u0VIl@G7t"f%e#Ɣ0.p M|W5Uk\#i9Tv̓'!UGj9JT+q(R0SgBhb~؅aE! 8s@ϊE1D *F٪A@܉kN+Ulq.DԗvBj14|?!@p;F`fUx|FBh z0+M.ݵyiŤc`Fc9l iNNu*QA8&1Y e0 '8 D@G3LccrQSϜO ^ ,1Yj᜶7gnKk` H-@ܖ1#C?+1ֺ0#Uu1xdL'lWP"9f:ƣm:c&:T)¬Zk!.cM6Zq Qk34` e溂"fkgPy\@0S Pu)tLza=1~,f1\D"{2 ,K2UBqӷSvЃ YT}HA+jRbù{YI4H]61 1-'pIZk'jI}q3D.%xDŋ Mc<9bMykma7&gb5Po]뫥.PH;9e_'@lE%P8J#-wc-(Aa (]+M{!2qVCFHh4~5nM35s FoaP`k~ʿ00´(RXU%N;t)B#4re11Ɠ|:p$R$1;DB-@gq:m]}t JgY>ZbW\EVb 1hYET%M ]Q-ZFRD13F[ZL7Ι[Qqƞ푇1!D5+H>Y?a:ufu0QN>FF$6Vf]eIHQI3۞YvS4q2"}GdVV3cIǴiCn8V&a5.O{Eip|bM'02߅ټVcA-JMiџ̕͡9kWe)GE9wvx>goNɰ$@-髝#V: "Cûe\-L T^2J?Z2YyiYe8Vuԓ#⡣a?i,H.&LMҠFj~bq M8>'g̱QT=XU|܁,$bk|Kj"l)bU*Yڽ?%H9W)[w+ XD&rdՋ6;Gt=Ij93^lkc::AaS-nFN;C-4yiJv;T;]|5?A _F7p cL|GbG*6e3eE1YowuKYq/t(C&i1馅Xd%f}\Ƈe4[ӲC?_p_. ^"~x]p~ׅN>Yk9^ˏi-zI*4rp:d`on"9$VL]lYċ!Jf$T᪦L?i%m>=*\G T*$z,ӣ1-{ZD78~QXEX^ !B-yV@{--{Fz *br3_XcBp sGlѓȸ# nuF.fϵ Y#MG b t_AxW'rc^1"@>fΗ+UWp28|eʦog&)&"+sQZ*EXy\<1DQf"gu9My#Ȍfp]gH {]zHd(y =ݮY7}nt&:!Paox,gowME!%@@F\2s$W&|-~uۑBQ2N]6ûimy ]R>^䛯*W*l`8*39G#$0Vhf[ S(0ݜ>jCp iؖbet,Fe Ƙ4/:_JLtE7K0ES|4ЁEPȯ/s,ܩT˵C;d \}_VD8.['>-~0HL28͒e/~Ï?~a@hԖ'.Okߍ=;%H>Jc*Z]:UrE)&&eb`0;cd̄N]ȈPXOǩQ5":.Sgen+ QR/Tp)*G8阘t(wy2ldK s$B<ȒpA17ЗJV*=X:Y`/3pǘFr|MT*NHQP3IEc4SL=r2ʙks(ĵw rBFTIzӠ>7,?_@BKZ(W:~BU^%K#OLFbމҨ(IJpM^pKwv1k}$5B`` cզbȀ@Mc2G7WѣXC W39J='_egp'qb $C^|[k,{hax9,6?~h%C~='-l^ݔY0]w& m ?XTx^;i^p!z6$;[Jo~4T<ƦZ~khdPUsoK< W\+D3W*W 0}jOwNaBFWtfR2\hۮ&֥!#e%9n?6[.g*<؞(>&.vXuj8~(-J yt-Wp&Y,tNJRc0 #PT^%oLI-,9;2=u-읺7(oKσ $q*o KX@9pX>*ݸrx9Ar2tjOeKE[7duvBІJ bceʫ~t1IB>T|Ui'k4thqmo5v'.A '( m֕JyJy ,)J,E ߈\@nP拪!cC LCSOOނ5잡 N3Eoq;'Md*,X-$핈K@WE4ZHx]j+Aԇw2( Uaԥlkq|`G%N#tѦw0p0yr$*δ{YMSXq|rκ e,}*=]Dq <Rmɡ'B. 0'w$ b~ZxFq dfMa8+C"Z!ҥC˳(V+ 1 ;(qv+ݚ=qpE.jp3ЄYrS@kso]#u{9e[mɶ@"2i$/.nHS,GWOfR|PR#W*8Um4`a|:֎AχX%O8J&%(ˊ[&teɳZtt-4%:8});?k?/l;mtCiU}#)n_ڡ5:H4j/k- S-U-> Mķ.KՓx.S!8Pe^Ȍ% >gx%54oǵ5/"ZeH@/Xe*FD8AyO\QJWi >O CўaGY704^_\g:6=5%tL]$Z ZW{c 4`GDO/߶` c}G7e0wdTsG}ļv+Z;(WR+BC閯̼d'VAVJz>,8/hCj$2k.f2 @R v^|an7sq 񕟛*.B Zk/f~ѽ~Z*5%^.up 2/]M vě=xgB|޾0 su|A*k_Jf<\Z ofW4_X@ =Ӣtf"UVh:D t'u\Ӱ2R' H]qlRn^K :hlxoAK.?G8&_ɼ"iG~xUCNm6ybag_˲$E6-snA+7!P٨=L@R\=s,c&?${۪ J˗!ȮD&L֞z.D2>N7Md eT'4T׃0rTZz1Bٲ7`[G*윻gZczmLՒ8.u$ahMA*s4n7_O׆ ,9 28; ?kp=Q)c_ܨ5$TZU:J-*:h/M*7Fl򞜭-("ፃ &K+h*}`f@ܔKPP4hV=`I5]K1e%y(6q/Oul(pJɖdTdqK[&Ztِշk6B j\t_ObIsv"[+\Α\<)LomB7}i}i+'C=n ]%"0?[hIN-̖4VaJʈ3gSN0j_fՓ&]f  hm$AkEb;WXdz=#~o;.*ڷfs^_Z7PRPf<ׄb.:/`w"{Q0N#z.` y4CE"zD'u䧞,n-o+ֈn*>E 6ė]`ھ/W#m"'hup­*e/D|iy2[%Y[OUگ(VpN1dC;e>j݋)76ܼ[B5]* d*7D㞿`Yq Q8qi$vUB7c}Ԕ#xGf4A˜PHqF@g#?ɣzZ'ބ˲,xhM)rS臁)j73N@ :C4GɃo}pp-;e6 װ5_%%*9ʘ<_~Ug[+I=rOsp,lgE OzD]aȄv3I Y=[@雥@H1RWŴ!VUds|7P+b4V@tr3Dؠ bH] ˕٨I[&טVL:f*X ji6֬)*^ނY.^x2̤_ 2kuL@юXo2)y _}/Pdǥ o?O`o/=5/ǿDŽzOZ1<V-YT˪_Vc1Ur^W0_hgP֤ PxwwF -ꉜgKl`(?˰(K2u\nXk'ZiR-f-XVZ{jgbl2wED#E/>ʛ1zl\̜,Fa7*E@Z.Fc;+#2?ȑ!J 5);yٚor-zyJӞP_Qp}Gaɫ??xOx_ r:9 Amlo>rj'L>%G+h1(11f4v8"d[G*iR2'Z_2T|iLR^h gH!5| =Zn#x˱w KRl(.U-vd@]06XdXX8Dyy@*UZGtl$ 3D9VxyddnP*:])pŅp$ùc谭"GWU i6fhB8v;BjFӮ'|Fzƪˁq $ }.+K$KK#: Q&<'*Oֲ5A*jS1SaeGϢu/LZ2[5:VN68I|q!i>Z-;m>WxkvKJI,eO8p{4P m#љ2DRsz͌FABh tKK~ ŧ #5MOjNhPp?K`&kO |‰nٖIH?m}D 5)ȹc;is"TL+knVd&@މy{S{+Z-aO3Jv#g5]ŸmInwcNZN~_79)"u' ˩D]_ev\+>wQXLXg'_|EelME!Ó懭P5Oۉ-Up]u7y()`b!86R޷YoiŒkUx59\DK|kawT,&Qe-X[Y'm wmɹ2 {JOQA =UYt(FGJU%'P![VŭCpEQrAu^P ͟3Ї{ZeIH֭y,%AΩRrVybxq[rј9.G x1|2؃"7lf_zE%橔Sjgb0cKMw[u{Su*"58Ӿ#ez 8մd.e}M:XZl X7KܷüPOU!:x[{;o =3;(9cvo[Yf_r7tkz]z&8<Y]9CbE}Ʋ@UU!/IYrUIEڻ|SwLfp B^sYK2DxYħ3>&;pYysKڗt¢?@}8 Yk׍GS/쯯-ErKGIuyNm3w/"vG+\h۬0u8ТxO:>5\6YHe\ 8kA܁8P肚K*u9]4GύcEe1EsD_b:OL.' fZGl ~Em<Ö͡ab0Qf Uj=〞;BS;; u [bSr+e`ӧT**k-l˞pUM{y [GLO[;/?rKd`yDVik?۝@p9MtjEZyWtˤg>SdAn [n }?kiB)^iաa [wjk\H;Cq~$ Q9[6d.5&?TK#v~Wx!vl f7REE7$y6tstH*ܳ˖ ث:;hݧB(Dn<_'|]/ipOaIVY^[2-7\mmX-Qj"nY.Ska mr L59:d8AXўMwvߞRO_2fy߲[Ӄt 2OT;~u 3ENҠ4QSLӆ(0"F(CF5j mzVMSgVȣzzӕ^I 7y\!?NJ\~KP\V&^%DJ.W 7:?+-ݭ{pŔ [ёy?C3=tbE[$; B㎔ 4jGyZm¬k` nXYo-|vB;؞|HD=:=Bz]N0Qv]P*6}ښq?2J@^dAho2Bw4 ʶTrah[s 4_pl˃s88,iZ] 4㠌:4Jq~/ `NWND/UQuX&LYyjƽ_k| P1]hMdoJhf(m")l0]ѺUAE",TZqÐ\ <3mc͑(pxOM,B0ک@Q]n{28X"" ܃n5|uo !M?#b0਎G!!(jfA^GOfۿq:=Ehg)i?_eECbk6mӡ}p&OmNϏ!h$ tП5u.r>rF+Y/Щt7x$G`Н4 ˠ+O?fnff(-Ӥ"9SRjg;nAl&~)\}ǁ_H^)cPHJ|#_)GSń^Po>45 bи[o4^99Mt~qޮQ=gw~4vQ/<='om?W)7AFn[=\n^: ?ú#?:5cdzJa(mPGv†&Nyva2e1s\o U4(?U7 #&b)e_}`qV8đtΫⅮOfkt +n/?Ϝ?oû+zo&elQ}{^bɿ8~ik+,`%RT +F[ z7U9ފ4V ,1N+a Iy~9T~ FYHS<Ǟ6V\+OJz  L!̎'Hm5K]DMpթsj]tH<@C)tҢ,2!>t<.#<3pd7/#}9=~EwS~#F4^ؿR/Aݦ_)gc\)պة^Q.ROǜͷ:~`њM$R=  eoFv8BznaݺY/c_P+JgfnUB6:a'z+S9mv!U"bg1(If]VB>urR`نsPӎ6`TǝRJlU3=*N @Α΀oSWOێ~amR$2.!s)*:Z=i;"6/MN;P>siw7Z$ ±%E!p,ɜYAUZ}HqjU_~?Ls/żS|Z `?oG?L3{ΘZR|xάnG+C6?3Vl-[bWgOxF/t‚ZLXy<гc+9Vu sə SFt?ٻVr9츃=Ownaj)7CuR-!m|C+쪈okh|qCb\8n /综A[?f_E``|>sH%. N0FôoC| +>8):9Ko;D1~Րln B&y3rB ꇂ?Pm?mZʟւ.5 lFH&16ͥ1I&픳`'v/(c`:5@UQ]5L3TshZs@(D:4W5m_%6[4O_z_]02j_GIT<&$Q˗K0_~gӜƿ~gB[_v04JtVbv21!'Oe7o,ϜԊQQn4n&]|0Prno~%%=SM:HM<=ɸxZa᫲xڭ$Gp'd^39d/Şib!Ive43ӓl*#-b7M:7z69IHOAg Buls´p>vsu mϨeTGCG' . ;^^clm$<S,6jϏ5ס[.KdS=B]m')#DnW6 s\P4)ݫT'ka]CoSiBzk^' =qw^pL'ЪŬnUD+{+yZ%r{`0x-ɔn!/2ǧy}UZ459fU|mĆG?!>q#ٖIL/o(]z-ǧ=yQ.8;ظpnZe}D(P'{ 27wY h2 |y[>gQ[H08B1(.RF襓^_{*; [9JOѼLZ;Hv7}U'V_Ren pqHc]DZzV:k5c`LD78K:c 8/\f` yqXX9>O1d>j%]Ⱥ]/M{aQ:*"W;yz+58NXm4Zj"bY:,*Sg+#)r+`͢0~}lʳj2m'>8(R1|AŠjzP(GG3pơF0Q3^g4I%}UUez +6Tb}l7o9~1ᑺ ? [YUBO@Al8qu$DE+^ZIՐ>2rzf3ˆ3,1>$ &ckvi6#S~1J8H}>)/ԗ.O(%.'Pu=:~[g>"ZOJcmWo}WS=Xc&Zy6# l ILlE7,7wG|= S>7 c~iԅ^viV39簆YO[06ӹj|mw?_ew>lz(>ZGTƞ7 Oww/V;Hs10mgzQk/ Wc~(-6&urOfg/q/a16>" :m6:ϕY xCč{K~G=7%F L^MfnȞWy[#nlR(JSt{]G]}J =~dvqXGPWJg-"ؾpovS܎TeWsp ny`fK3xY)-R0@L|x4(Gl#O|IJqR0W^P~JOBA/,$2c M\ }"W( >-'~tU~x';#DU馿Tg·<ǻi` fiPn32W="BkتPL"5FW[Y-! ^<1q QY[cxfA]ZN vr ]-.YoG@-:o_+ZP9Eo]CXLXOVB3M1f%1z.ȣG k1ΤlFUg3^4`W%5S%z>T;~XB __\/或U$t+ A ɶ4U$mMljl4d&e|0q溟NCEvŪ9LgMVp")Y>b䫚iM +'5 \wk_0.o0 2Fr%N;geѭiJmr$ȿ{`ubfNR|yoH_rO ښ'.| 2g?ma>x5µ,_^3Q^N_prQUZ|.ooO&;u xd2)yrq  79{4A) ~EvLpe>F*h׆rd~M>3!Tb҂w}bn58Dt&[D'}#$xI칛"D gtSڕHґҒՒwH`z=ͅZ fWo ׀Houݾ#mmk/NlXӡET̎RTwE S?]]D4Uq0=զ/2-϶*.oݸq7,C8*xvV|,!Ux_?fy^{hX>jѵfZ.Ch ;FˇM5$4Dx< 6IHh;mTŠ_[J)ؕz /+4T,E6[dŸ/}&^ sˆ[b;, 4("lZ /zQ8R:XPLgHn-DG1Lq}#~wͮ w$(2a'R{}**IUGo㡙$kq\އwzQM/2?- x4NSP7| JY%dU"-Ga ǹ<&oTC ,m[VW5';RA!Ǔ}3 &Tnj`/WW|uz*6bVI2(YTqBIT誆 v_I^Pua%զSMi;1!#SNW׿!klUeifa eۣz`x)T-2vCWHk~i `aZ+7 xQ I#"l([Yn7Fn }$^ M)- ݶڭZIK4X̅Cz-x0FPtK]jbl ѥ$ 0keQMX (b 01S{-L btstI}4g ?&oqz s7˦C[I? .E(.X )w\T8!:@W{1ks^|cc{5[|uO60 |#Ic? Xm酦l}>cK2S!̗`n+miT1{{ Qk~,U?ndnHD^wXhY\4Pk3q88vB9 cƹi?xpvt <+W-&.ie;|dJbSj>qO@uV0*)ϛJ1VϢMČ1{X*i4[ 6dD`xE[}\(?&ustS7Z2aO%3G;Ǐ3#3}=hTm 8-k_or2V K%*8ҪW̫Bف͇<-F1IK{26*-ߢ`htu_)R 5 ʬK@ :dLQ D*tX̝_!7Vm%um z >AHMJ32ZXzg;ٴ3Ee paҴuB=aYQ^/d`3Jp7ӂpA>zb~KJ\#,qC*Koހ3zrum\t8(6tfJ6VB2/j鳩hfPq w($@g{~Ġ0xWh<)=Ӣ8 (0CĂᩐ Lhy+VTiqph1U Ǜ"3y^" V0+ p,M1D5Ϫs젂{0C뱁0=Jhcw?ƸD Q8`[>ULt0b{X9F20loYr0,[JSƕJZ>a%JJ)L$BŚ+8m`)T%W'LSB˕ +ERX1#v[tU}ˢ׾肓ˤˇC} &KyKԨ*?- U:7neBeab_ix~"x3JFiNh0Sol zpAX7W@?VbP!VKl> 7%zd­R*qnN2!t44.4FGM=.@l7>h]8$ G$ cXIi}7xT(: }#Rga:Qil0SF_X}4w1.f.%*fQS~fxtcvSxݩᆴ>"Ln̵(ᴏMA47nH<ɎeA *Z`ţ ͚Qnd)˟lwFe3 ҏQ4)vu(T|<#өޒv*?~Vق8[R#oZ5K:>ýbAQ`&Z'@/hs=&[Х점U̿ʬj_@tvO$5K03@C@E#in'ri!Z;{8QjeԣB 6_A0A_lhsNMX9${ދ&/GcgG|0,5WJ9[⡔hOX-G YvA&B>Cx 3WPSAPtX|(W΃JFjOїlg0U!6OTc˗qLW)ą.?b(zu,5J<ԮSE-5mGqsas  L#d{fDw86Xg䮪$tbb|aXюލK|6~1tMX1bikpm|h6v_bnc1 U} <|P( =`4 . ]Ĩ4b@{F4Pšĕȡ_e]i'Z:24]6EX f$vG9x~3w;I 2?(PV!E-~[̉j25A<jxYbVJLD.xQb'mg\^Ǚ`;1RCM ]WD3}{.QH!D`lO&LG ]F.:J ETeϻj&">=tgNa.]ecu*B *:| ߠO!;Jb|>]DZ0DH!ǂs!U5QQ{9˂Y,C T-&Xł{aѺnUyާ́ vAHcl4FIʈM[ *Q?j$ƨ## i(F (XbR1A:@0۞{ϩ{z}U53̹.[G|- Q(< X|4e bHTC>Dch$,9-] 7.7+А0GpAFoE@IDAT k"W$N&)sf|- {#{P rHQqb1TzakھV n㺷.2,'1[$p!KleL1*r ' ̀I<ё 26k:Ň^M/`UM4!mUdՃT)UԠP9G %NdIDgF E$>w?` >D 'O#FuxVMцԳAy] QFS0|wUEeL2`#XAE報 劐2U'#3FO$3C Z{[ IuWb[n21h 6 l"e2w)Y z`t!7Jdq(S2,9+8t42p J&!S{B9OJUIle:>f %!=93 @#"U9sQtmdDn]MaGV4Kk@a\ȜCHˈW]%`)Aq5)A,6eԸᗒ+1XrpFHyd 1p#;a>}fZ(j !3{FWiY1۾ L21b q~Ou*?} ʯoKs%\y'scAz6U-\PGDAЭd;W|q0732jvmgvR Ԗ1a2E X! L`G)lr?2f01dJ0O#1>C9/6(cfoĵ~6a7AA\m j[, kO$y<8vr9VT%@QpS8ҜiyGWKǘK$2f8D$ @B9QLQU 26 oQBd02Φβi!,Qh95UQ%m 6R-!G-M"{/й>,y6"\-;r| Rל2s _fh= pVr)4FRɆNACSMې52J,nm 5%Y|c,49n 8Mb,ҷyp#nrM`VCIIG,w|kNN? Tbp'/rg) FAKbahSO6v0S}LI%+d \O84H?K6cs"  \PUِL{0wT9qdS=%{e)H_A;>(C1 $^RPЪrTNތG8R2Xbą FrɄy4΍F Ƌ &1qk%4hUn7$%q3,tj!H~wY:$REMް*sR^V"I/t: A"< [E/ d7~kq )m'E,e`j‚#2Bb0"i]4Z@b;(u}(h `A<OժA!#IDдVGIxV1C .<5 Hm~b.T&45TAG;(&fL`!Ylj6@< #&VkRr"(F fD -aerN$y:r5I.RBe}{砆AO?bɂജ\ AQ)n)WDž -}eng['̊}C2T1]7nb'sFu.Iey)qP+d vHk%3v'.`ˣy3W/%Q *%'m@jB흘䑈̎Ƴy^ƦI@,-@eh@&UɓHED3b aEBxB0LrQ9:-g)? vu|6\07!B-H"AY)c̞E1)&6cP/"T8WJ Vؘ%mG$:}Lyd]R2ҫ8xfl |{a[56Uv0Mɟ\h[F)Cm-b$'E:P"z)I(% կbL`'X')F@ȀƓK`8I g_*@R8l&^b 붉+l=cpUmh+ b1-5%g:FTKX@F :f2wP0G2X&3'ǁ]yG1# @/j*ysGyI]p&H7.5^dΖ&@ր#q}T@kxo v ;||b:ILRFi u`5A y 5фWǚ;ngOR ,YJZ\e D43|9`F?oFÂ1Ii"E>~UCNg?љ"W%*؎d$8*c<2 '!A'tQR1&y`BC?CR-q <($rTܛ TC^ڬ9Ptc6 f8\X:=4C.O`4C TrraDP q9`k$L':1C_-@@*m>~̔AX $N($\:Øs51ǞhR]ecouIOYsR[83&=0FzaS 9Gkhxq;D" %hNB|L5*BَU.,a)F> {@H"ID' ^w!%ᬎϴu碚drR4&|Hp*HSA*i?`{%>X񤛧 t\|o`C 7je_x`h,O3v&v툨Ŕ dK<($ Ϲ\g┃XSЊPdWur]!t_M8vcyB93TE$ku}JEvvS "ߠ.ZGLFkH9gDȀ1YeE!q\p8U\:XTۋ0et&#*YM)(4.hvحSTY?0r|5UU!Op<Ǧ|UwH&1)32Ⱥ&a\PDWhL4NsK92nucbQk6?X$urX#A7G49'7B(suq}8 ^v;m?8q22N+u/4*Sw/жBHe۫ݗU [&Sl"21Js}h(3۸V4$if>#©'\!8 /4@e@ udAus%_19"Uz? ElSU \w^xB+0p,:Tgx O6g³D%I("]tfBs݉aR:&>OE!8J06 :|7Bm"6D:4Ģcb`]F$`|۞8KtF&|̉MX"[Ӱ@P]D cB  eE]# `dPasġ 6V-1L4L G)2^!3n490玘ɉ >VB0}1pESSܤ)sF<7o\ ՜(Ƌ˞_e,g08b69A)amtٷ!G.(Tp|-O৊ Sp(LG6+-V 8H \'ڜ/oPރp`w *O*R  3zbÁ7$)OW Oܮ_w7/JGFPKlə&D0'.hMdU_$AI@ioc$P8Єb^Y_cm#ہt_|ujR2"|_R=\W7A3++ɗ5f[g_NY]yz')̙i0C_{E "dQ˧ "eCQ;CT%4 p a+.#h 6n:kkp3NbLf8:t";f5ZOB>'`#:LL-Sق tFJCEwn`"W9A-1aU8& )O(&^-zɐWKսOGSlŒ+7s4pxwC=(ne\rX4i.Dk 8t^*++(?!CJ/ENL5>|tŎ[u7QH >5W BxEx"@ۘ,) [0nt*Ĕ!r! ""djRumg0xD?ŝ`?:`M !P<qѺ2)`<]vZ., bTjOb= |Ӈ8ؗqF39>|Ɇ6e2K,2T[+c9dA,an:[VBQa^]h^><'ҍ%}q*6KEmvH!*$ :B:e 1kϠ|(_ q #Chƹ(cZs07Ǯ߸~Ő8+z@VbJO`iQņEZ$+WfJbn^|:1 |1(/ <*!ByshsZd'iȱD(o1cU(C\`d,."(&2%x\f),jv540RJ(PIb8+h9M`ȧkqMK6™'[ܖ;f^lezO}鈚|Nc.xY$^*IO9MTY[z(_|?8j̽ź#`2[1!**Ƚ %* vxH`.KGH ARTj .qn18cY2()\Lج7L%@.cq–1 Dy(,1.: *YZׅ4 t+l #uATn n>&2t9չy|O~ƕKW"N-b?!>$XWlʶ7I=gZ>ʭ5E"qa2+";kh-p+ ~@˼ȹč\ٱrkjL6Gf\hB Ʊɼ' l7eB{,NY}^~SOL`J0ܑ9!}ډVk8@ܶ"xl4 uQhZBsyJ)<C$(OYQȦ >q| q]°pRer 풲rqXc)0;LыGEB9L ʈv7T<0#kO7|9Ia qkl6RqGdVe.XJj ܓJ&jOrU(8҇TfdC뫥ƒCͭu]# ,E,P|OǙ0FPY T&;k)2'vEȉMfKpyi-)!6ez !0[ K\KCiHQ`yCK![h7D3C\u7*ӐG;zh i].Uly]u\K;NV4dp;ˏSȿV s}" 0S^% K'2J)G$ ˑqÙН)ysJz083qPNuNBNh~ۦE jL{q`0g~<;v4g~©'Gɑ\cT*|hY mυ`͙X~4I!b!)&S$5|iĐ5XaAt .R Ap ? k@Æ1rbab/u;DfEFk*@1 #h(-ì@EgŚ,1!G( 6:n䞜 &!~Uƾ ЊPbHnOӐ:2s]j)OrLUB $(ְS.Rԉ_xy9&#f3˃[uμTq?Î%*c?6ŠЧ" @/TDSf ?h aw`{TP^!^jkb Yִ#jk79hJp賭G h;MfDYCG9;}Ca&&:Xo &6xˊ@`\r*}NlAjG.Py  DpEX"9J|D3wcqdG k`}@%6bMDr8Đ/CVO&( rH E\r}+aI#gh \B s6Y&4fS448H[E"Yw!nrM㘹jp;XeP|w,bL!0x4Rc7L!k.BFBZSAL8#̱ 2k``WB0>oۑ19GJ|VE;TM|w f|5CE耫*ٝ=HQ}!Pʃh.(4mDЋ(8>2g?pW-( @Д\*pT\ 4[*NTہ}Jˀ[¯+5-P ]٪vgNT1 E&PuX2RFԺ͕1 a-KPL äK<&t98sְ#W&(N[ͩ2~耎2Xhgr\ A. '#Qu<ߴ9%10'\h#&93t'9'D9sJ6(H@8lMg^}0' c*hNB@ΒU|_z!}wԊZC͉Fv;+Mp6>ԔFR:a,8F/84i+Z^MYy]p&|8gsJN ˕2צ֌Ks0%VUG1.y4׃82J02@diъ!!NF%aZ2(2=a4ldC Hp.ght9z^MS YO9D,Z6LN{@{^ \Ӆ*FM#O(iBEARqBJSZ *&N>VcgdpbM3^w_@Ok,HU%r[֔ 'Q_罌{_Udj$Bm,Wia9;~3%9. J k>/M5tMYĔʖͦ >h`|Մ *l2 8q0-QӀp[.r%*ߔ &-swC̤ Ŕr$x&W1Q 8SnՁ/2C](HFdq(x6sHKՒ1Ḿ<<%>&8^9AiLu$&uFW!7ThŰqDrL>ޟ盺i|@ 䔭FM.%3_zc$li \zE)~Rq`jba9w2Eʴ!BVEMT. ⫍'-UlGLqljKgA0\^Bh%a[΄,ɼ!8ω$眡S8YqL \WL3)fD6[vdE5ݎk9+$Eñ4N' OBGr ey- )$ ( 4vT 適\XUL/49m= #d0:꣘GK60~ ;@97:rPYst'Py I9J586s !nd48pԼE&򢙡2=Q L)l2Jve =Gm Y ϑ< Z\#)I{Zb,* lh"|TMO[(4S9\ SJ&80YNMMXx仗v$`NoipQIxuv;Kj 8Z-&+ʒS]?mMh% ɽf<*s3Β&rw#***b_.MXJ*C ?RL#D duOJ~;f6"`H P9UF.*'N8J76+E$RJڀ6"E8Xq">I0wْLvN|q {U3"˚xÈB$ȓ߬vVsr .hNtGeYGXH|SL3GI z0 # YVB22 X|@( IL%3 F]X2_9n\}ǯ(K 1 d2c%((i O+',>?fFg̘fAB VM)p@5{hޔfsXZ T5ѕ(J"EɈYWukp˜A@~P["LAn^3n !MG&b\rs\T %9PeF+mhSnb`,b"/ 6P96H)fk͉PI`Vz4Ʊ80t,`б32̋>T5و:\h*gpYk<~W>eOɟ`Z{_?Ը ? {חݿoWO<?|'OkUQmR~K`{,J{_ 1P"[=3Hq8~̋tvPNL.A,Kje!@&q=YJ1J1J,w%<;?Gz'(i}AC201L^l K}jfa}>~ A9@oY,RvIVHcc 9Ueda0lM8JwGرSv f =KO~tmyy~~5?ߗ?WDήǟ_?O=3{W8rc{WW۟_<og|ybɬF6 &vQ6Piՙ qW ]ֹ͟q=K>_x%^cxOȧ}|?3}wٿeϽ}'?k_-uOϸ3B,nbo\X _<@\70C с\Mx9 O/H. `XD¶k.mTõҥ3~:%awe!OҲ7"<ԠIH)[ lbwFUq*|$ؽ:jPY+Cy;J^*9)x`G["`HK`Ⱥ:c! %amNlՄ kE=QU?|"S#"4s]מ?㞻"LSl4r_FrIOrԡqi@64x6 7d>6k{=wpLupBw=]$&*~O3;RdkeWD_fӞɠ&i,HZp(JRGf2CtG,$]$$ׅv (L!ć:x, Ep5^j-TDjRѵʿzk}9__i[D饲<~=h > 8u \21CFuCܪ tcf}>p: ɔP "d0ߔ{F$SmghlB` eɹIPd+LA=p8245G \lAf8 s.3F1CF[݅nTi`q2P[YN8,9 _OLy.3j5 } .F<1qË Gf *ޥW竾? IFbÇ]:|ɠHdΧ~{[`;b-:9zɹG@+~eYt؆]McF$)lVG L""k(QPpCgjZ;#!*@5㛎!4m%f?Qoh?ihHWa<7X Zh@>u3APΐmh4NF&GNr,GLUwUy&{7}w>ix gz‡{OuyKN3 T?'OܿWͿy;տˠ=3w6[6Zl5F2|[,L 5|2l4 bxo)1M* &WAc2fDOZ\628dD3,kVZI ] m4ɬrIgM "S3˭>˪l`w,T(~Y3)R<tqh$Q2kHjʜ7e 7 %:k￟\~ʵBkR>v?KEylN Qym/ANj߻~ƒ^r;8۲IbNBԆS{-I3Dʼn9h##M%9j \۷ZX3zCjCL9j154G`qb3Ab)%&N,jZ-E۱j890@Zdb)$`7cՍ@K\DW`2*bB˦Ip^W'G̐ hoh cW?2%۴Ӎ>A-p+;cC2nN p&8Nw:dʁ 5fq-JTBZAcf"ފUUD.F0q,ȯ8`s):mdơ. ȄrggrL @t}IG q+Ҝ(c֣N#F36H4T$YSh\C6GV/2XqʞQ=w)4@IDAT,䭹3% y]fo6VV5lâNdT@iCX)w0mؚ{?1/ 0)a5FdM7$hm2CH z20aҜ-gf}PQԋ 2e%7 ș5.٠!<.9@"6LOhU!ﶵ}<["2k%tOndpCVg,:LյĽi-h`.j> A:+ ;h1D\q}m.֓!:fj-ys za Fvê8&E.V1)R"4o7,YP,IS`h~bg2lQ#!ހZߐ<\B2IlX 0zB li D 8'0bac= cluM6qq6z9U$u&i,9'w]ygeFАJ67|!ǁZk22Rq\mQRT“?S }'S;Gfy3&#k`I:)Vnc$̈TF6lb2f80NEl|8DZD|UzdU.mC{Q w$ 1-TaIa_Y{d (FsbecP'9eh7Y!܋u;0 q`YpnX)' bbmk S:Ϧc,gKD3ra`&ثN .SLSPfJ߉'R4to!QhMb&t&v(> C+m4tJVclHP;7$QW8'N"Hݟ1 D#09x$` ǎ0 tօyWEZk92wonOd<хeMpg[;M<.?d֓d1Mⱨֺ،]D# T9gh4k&qMcz ܶXʲLހ0'͍FAc"d^aVlDG41|&Dlm[bqҴo Qj$sx±Q|5f,.qofl+4(3Mv!@ׅ֔g\dn\ze1Sezr ]ʿP y6'x2#h|].zh_3-B8]t[ɜ-2; GĽxk- c(3 puޡmva pEhd %*] Tp;mH ;FϱV-] ؂⫝̸2Mw#na_k_U3.okbVKtM3`hVٖO^ݙvbP-0pi|`2WΥig${+|_A=0E IԔREu>k2եg;!:Yn0$i)WJ( 5q Z/'M%8Cޑ:Ƈƅ6pQĶfGn p_ֽo%Q=ʡa"F5%ʩ'pv{il"1cs*xn, B gZИ\`_zx|s.#߱EcKI Aʾ9ӪU D`k, Ba'?r 環*beY7E;ٛ#fQ]ȓf9h s@ocNNxf:Y8sLT:I0lDwcTp(BUApQ!ܑlg"[nܪ|6s2b]7~͋Yv(xʕ)2NSl"<$ImԘ ޚk`0]Q7G,y5_v=XMDh_Zh)/!0+^ ymտ;}o>}5?o'_yѽ ?~pu̳o~ݧ^W/s{7o<=nβQ3?w{iĝ|7'x_i??W{拿+_qză//zꑼr~m?u|o#g{O?W?[?#.8_,=r0𞩖PmXˈE.g/Nr7=CL26ufأpd,6Dhs5vYCՔD֝Ҙ=vLW1Co!>?8ԫr nVdpG{< H[䪻Q[,ȺzgՁQ"k;,?f&Gs{{m7>zsso_#{/9gN7+g/o;9zlޛ79-{W͑W5_Sk+_{w}Sn^x{Nݸvcoz_i'y竿>^w~ñ/n^|3]uhILs vt]SuC(lH3 d6:w{CgMPቋq-YtCA(P8eiYnq@1覑! )#]#U9D'S̶ڮF۰+.́rjf*8 CC湣bU!` 6P)Ç=t߃Sӧso;sD9}TBԡSwc ӲN=~Ho_O-q3^/̙H⇏9Ȼ~}O/~s;O=w/ϽCGNr@E=7/o[:vIֳ'?^rσUJ.jn|r3_;|:|~JrO݃f!kͶ7NTGp奔s#"6b 3L f +rێ0 خ%ڗ36;|QՅBLط rK[X82->iw 1/<-9uLI!l3@1, ̦cX{& _!vkuB /}?cv=?\.G5y+M>~d _R`E{??KO< ZĠƅ]G]9n\?/]x{>wxO>Ꮌ0μ0}~3÷}?za@crAݺTٳ1Vm xP@ r#_9vŨ--QcA?Ziʹ%IlrU,m%wTyU "!ڎ2n&cAg3oe'#j";TX&[zba,P@Ә;aM FKΠφӋO3_Wo}/tdKI̋;rԡ3GgGy>M/{ 3(異fuȈKeo {m1ԷN;}-+O$&6r[mظ7o>f);ùhpkNB'lJ[PSۊ RJy*HhhZ祖yV6ԁKu,Ey unX fb KUsSh` O~xS}o^v詓` O/Ov&3LG䯉gz1O;g $ 3O~aONOKd§|ͷ<^^Csck7{)!{=wdt=+ȟGcܧ_׿;uOf~_W*4%)ݰ&fzҁy `mU7!Rye"׋В{;w1RE!a Tx}ɵȸ)K-0:N^!єtF5&i|I}r6F0Nf6 Hs*I/^҃ [Cqv?L:G{﷮u=_gn<;?}~ǵsO/>׾™sg_;_;n_ыN[3/z/wg.!}Sj{;_\S}3{Yn^/҇O}DZ>бzۑ?{}/񿐠Y;3gb΄۝ ̟hfB8}$rq'"v\ݰ0lrvd GUn:4cXY&9k+^xzRvF aAs*Q` b $ӭ~:w҉+-!GX~_83ݸCˑoHW\7>s~Gy>u>)/tSgO\.>ܼ~cH'tGrk7<'2N^xB~sۙI.]?y,Vr'[o!7"gn;DkqC]Ϟ]F?CΞ=S=ۦ9ABpK;D+,ϒanڊUe-.Ifk<AfWE39IG=Ux'BC٠)~~.MH.X9;2oɛ"P[\|cyC+0Ey]'b) e2+D O$Bm'`X3#=ꑂa-lF3n V̤ԈM0̂F$EFִkw}eÑ:zQGPA=k }c/t(^El'&JYS,|лHT[gFOa±Y0B8y'>jyem e,,Ln15vYg8c 5ϤxEA;TCԆG&,oy=4WfK =T9l2Bv,!-Pf-bt0\$G K5@+ XL l*V$& h1<cy F'Ļ3I%eȁm2ٌ@ ;3rRݺ?G2[Z $ oڳ{ tC\8O{sqDfk\km8þuތN0k6_%z4JgV3YPGZ1&ӧaD$`FTJ ! (mH([&hW,8 ȏ[FÈ yf3aNyL0h)IJQ 3_~U(*g P6#=X!BE[@- %T$$PwN1&phZ 3JtS uڀCƷ?"dd%Yq콄^pA8hcbTOJ&gĤb&eǶN$BG\q#-!/qaZ2@S3^9{Xo8Eoq5-<+emLKWS,DM%"yM/]9u( <'$\ŴAYW]!jILaQ@HRW"&ƹ;&rQ$tR+ =7+##jS]-!#41@76S!js)G.3%eR"_c'.a4`i096+lE"=#jqs.8yee =kpTh(6x\GfrbkQ8-T Ʀ Grس9xZj^4EXtofےjJR]IJit`rddRgjBQ @ߘ[8Su' MJ*,{'~[?|qKĽ?'mUtKSI<<5r TJBŕQk r2ƛa\txbGIuWDp:*-q?&Ͷh6B&~2mPr3 %F0TD(ӣ}0̄FZzq*'"0(XL!B=$؂Z[r'*Zjp Ӵ+կMbl&ܭ\ <>s4~j*7Oؙwմ@|&UIEgF&(vPEE.;-{v2"v eqpaz1aQKB5r xF"c,X47K^4@Zkro>E%XFԗFǐAvM"$I耬6LCV)pC.Ȧ2ͬ>:궩Kp@`(CMK0A?ߍX*6k`#j2jCȵ.p33 UM+cVfBAe[ ֘_ƉFO]B1 c4Cz bJ܍h,8K|-aG=/}]Siw(5Q YMזY*N% +f(hab_O%"53Sho3f@ &"WYo—F͕1&p77`P_\N׋~@ nKaOhI(_Ȉ 5 ꃆ(L*E1bsp$|ÈzK-42X\m)D3H R@.J72x@yDG[=sI#-qH4$PX55nH$ ȦXNC-*Rc |:'fR lͽ٘6: !hZU" =`UӨOBg33_ h6k 3 d/oJ9x08|In*^h2M,XDE-f%O1 Fɏ/9Z͓lB!Cp,8గ4i .[sؙɊ?쑟eN2W^}aWC6sm'{R.?b[[xz0{Oh& \4B>gXE'H+nLD)>0!䜴_((kGPrGݽAɅ3f:7BlŃmm-p8|^ͨH j"&@΀ wx\5! "KBZEsw?[쀋,wH!+C4@ H02": oH)& 8&@R54eKCe{r⟇1EL{IU쉹tx#r+ɬE:kXp\ִ C-7,93", 5'Lh0 Ԣ",D3j#OqօJ2ODdA~߿iV 9He< `|ɘMHV q^CS= ؛Fйx1'@>QZF qƸt:pEo@/WNK> MqC^l5’ru-,u)P Pt8:2R  #w"zzjzȍSJRB. D,-RZz`“#0qUѩSXkιCķ>5I X/v +<zV͕O>K&[P y&v n5L jc GۖZcF <(Z}t oM79iA@]X0t1N08t96ЕS hkȪ/WBt2,cjƜ hMv) f2ODDu1fK-STL*K=x )@WYjR7. x;h1>6 (T F쑰#6!J`Ab&5UPRFحT`e3RRL P6^B@Q7[_d] K"[a i$W| 0k0I &a;󸚙$L@xK\&i"ۀUEƞ}g@*ZCȜ,ʸqO?8G>aAOl.\dJIU41 ߆`K24)XUf"?hvCqEL T%VS!{յkLu>z#UK [w`# #M)5qp*\ö|R< INd۷crd5J#oJS#*<:-7X\k*!B0}TD%g#뤝JX 6.~^Gr$bG.pjd, A҆X"3/!K8i!4@h .qZMLٱoT_ZQeƅs#G^"Ά5 DDsKO " u!X Bk4Q$?2K1ɚ7tN1'_Pl]DrsK1^xQ#FTY5%$%!y8Ybl31F4B2mKO3&ΣGL3s$3GĘ'm{;]>ga 2Sy7]gFQ\>`叽nXmYgTG:ϝN^^1(d'KFӅ"~a;?ĄM_c5Q0D9@NckK~#!1 {(F*؂+0Jl\|6ս$ ]t|(6+~5D] P ۢSܪMe5HKkq'ȳyT|<z^2Sb ɵLY7z\ G9{|ҊRzr\D"ᙗ"Z GR;L3B-2 NPkG߹tHb͛Ͻn ,QB ._""L .}ndE'@5f]DnJ$& |Z &|D%úN <-* c_pNx9ƒ|mAڸY8.dD; zxr3S7-A B5 2r' %UN@|֙Z$++a+{4fGXE{Z{*PHH|TJLhJm.,*C.pqML5'MYRKo}#vF`<{}0LģL,N9ztҹiu4ZN< Z1! *Б! ڔr]<+_/Zn?O2Pz2z l$ wb-U?,`OCr$3`X"(= mY>H 06B,UA.tuӦ2Ȑ>:Msj\c%VXυGT  i8DBؓq4;R6z|Px鸂dКm˖N8P8x OnɛKB-$)[$A܁-S[rbCf-V5D9x[kYAL8CBsX9!^z} XeF#)(nZ32cȄߓg>t2d2$4x( P ;M' WE$)u(Ԙj"=XJy@ͨM9&I 6KQ0w^H]ڽ=zM@1~te?ᔹ(|{kP,+6Ba:lUDkuYOlfeh!SA`Qn{Q۷oB8`24"{$Af$b8^M$aN45Fxbo 9Q R$#Ky]؁"&!*``gzrʛp`#kht$MP+Q^SҬx,h?%c̛ES%#>x tSŴ}lHH$HHE&H730F=<2lRLuר'yR{9R@_- b{"WKfA] F -VLF)p% ߑ 3SH38ԛ3.V?3#6RQsx֦[B'a aUZJnYm嬫yIS2|O0}if ?n% GvLO؅ VK@nm+p4h">lٙx՗w 96_7QH"xbOTTVhuzۑ|IvL%ca`,cXCi(/˾:)= zqփ.)F.g}QPD&ə?Ϸ>׷?1%g`sYQ2r4Gzܔ'a٨:4/#&iLFy!rߜ$h `P@&9ʜq9NJ6өԉ~yi"]8x4DwN'oo/ZSznr$K"+5%FsEQ8ȇ.3$IZRF2\= 8q 7;lrr{lj[?~+'P%Rf|[!']7mIv~s^L:M/(&05x$a./@=AͯM=8޶!3?-Cuoə6Ή8?.X;J3';\gbI)Ҷ[}aA)'(&a/z@K:瞬k7+?/ Wz懳\?gE3bQ`g8c4Ad\eQ3VɁ}QʃO(IH5"5o'dUa){(Gx i9:3p~p1Vq/6\U1,GV& !gDAwEFHz"ɟb #Q 3ou]_Lr"&4쮅T+1&(T$6R2aKDAdfڼfUi W,q#w &U}q41Ň#1ڡ6lL5OL$%OƢ#Cc2̔)Z(T“o\܉!%l58vgƝ|pX0,Z|^&:s' fȮnǑ?$ʟ3)p<4 ݯHC)|i[1r(I Ap͉rfS+ !f1lrY4程v7x @ (iz:nF9|x.2\tPĂN:V8aem=@İ=Xd i9gr,* @&r|K䘧\*E` /985DipQEm|wvp"vRO$Rz] :WIH1jMMvxcI۱>a!>xQ7#09;Aδݛ)ԵATj<-K޹d3UbPiAtLBXh1p-:HbCg1 q }OƲ"v{1{0?LT6Bъ%V/΅rU,]ORf ,^+sWO[yuoeqpE̮aXi;|xMB VYǕv$3b=ּlX)xǐf8XXBC]o^PMsEfS`UWI,dHP,qqPFFy&_1g}1-1c j@0c92p8VSN|PiͲU$ %Uy2@ Sk@:Wfst=5Pk(`=E#6L`"r[ɨv%tobօw;kn(&p׭L\ <7%/2 @ ^ ":O@o?t,WV!LJ$Ly2jW싱tK"Q 3f%`Iz*BxǙ@6iDХ!1@IY &<2EH(e\ UAFqaoiﱎ\UM- EYVO1F\=t`o e %7~4110OzG5GI0,`{!\jkD Z ~"6!V52sC+e^Ikc19[}dBls~@\4UzH&=(Vw@\x8IX_f}U3!-yQ._U@IDATLU.qyshK(kZ޷Gt&6Ds]q"Ui yMr?Z/+~KJSs>TQ24>\|q҂O'Qݼ 9Lj8S [ h+Zl(t"hһ7cZq#b &.W ne|]_eW4*Fb (cxUHR13md ^zXm"0F4X/UaQH\,L, `ƃ^4 -X6IM0ߖ .1ׁb+7^3&9h$eIUaogIMhi^EsDt2}HHX$QRdV#M& $9ќyR\IkMk``\jr+:c!& kwe= CW1%L0aE#Zj]1E{ldܮkM1IThV+MԸ2Z$jD"29[w !VMe"uy7LOU|[U^+!g,DVq)$yVeaK٦i+odA`F5KQ4mK5Og"+n#yfg  *^|9|sWXԦHr WC,i|ĝAS#̓T(0GJ: SCT;v)S"c#>;f&UV"CM%H6r#iULj-!5I, od)O,D 7_v=}~ByF6 0_|NUZ@-DYrk]b}dOH8s$Uʜ*8f_͉՛IYEd匃,K՘\јa1Jm0)p(wUT\SƄAEC\'2']~c `+`ɗKgQT$t_:G<1T!A[ḱ)*l $ %;htU}om0;×{Wy'8Y"2fG XzGusn#$N\ D`3d K6N߸o 9B.'^m>}ɿs҅RG$D{"f&\3ǣM'EBDlI:E׹&$)ֵs 'K2UKU„ZXbIM"2̐B ;'=ɓtVAjUiR@Yzd \ ~ (Ai"'|ۗYm͘)( M6bQW1%5|U$-TQ6k1[A0T zHލR8 TUgHBĝzv*󾄃 C`۠#JB'FsSu[Q2~|mH|@=buPKIUiL@f%8JHu!K5L#])Vz^1r3A1"f,&qhGd:89#atĄJbdf$2dW#!%sx0UxW 0pGMʠ9nt ;>gfǔ:oC*Ft0s9E$9Vdd" % cKԔEʱSg/BEv6i6>Ȍ.*1B̌C+*JjsK4O݀mq{hXyS'3D#zD)$О³ *8&š!+:ffτԜX$)5oc*jO3g4{}D>(q:^b Y\exJt 9WppqWv)<X*Xt7\hGE3mIƐ^3v V#iȅ|gLɸD4ZφEO~#/cyV:(^`llSw M.38`曀̵;ͩ1r>ƒ!zj*ӻƙ\gܜCb@a9ˎKA:@ lCsĨY( z)W .QxmrD}{td ψ&Q6v;iqkzsSW _d0}CbdJ(h3ӜCТL:!\E.S#@ܪyEM}up䉏u\?8/O״ kP?D 6cYs]Ft?/ix*.=pN2R uwm4tbrPgML pzG.~M7g{uM,рTUeJP%^!{GB+pHX{sT6X8*+QDZG"I۔A2q Є!SQ]M/: EdsY- %{Q4KLH+琰l)m6ĈסޅO"(xQIfw\&dʃ)3j[e#f;J &S,JBVDI)o) >177ѤD5 C`K17BHqIB$=k,h8Ayf§>*5r;_v唨|?׏zwago͏>v걳_xk^]/}Ʊ~?|4yƏ~}?5OYG/~w}7>M;Koy{>7_ܣ>;?.=sWG;_rk~?R?_xp쳾~/ٱsxOrkgϾأWo?/?+y|$Qy+C_zYPd5&{?˨az+'`Q,7cw Q~5OUԍ'8GRHUS]`2vG LbV-36Z+Yմ P$#f~8H|peʐBkb #v V P!`c?dHb0C' 4DjȪN;,صŇkB\ n {9.~¢c?o.{ȅOɯg~_;{/~x\%_|/O}sΘ$؎\?y׷_v#):៾o/{֛O~;O\>t~iǯ]x ow_=?^x}Soz/?K?rc{p䭷|8vE7x$40`wд C  -D1e;,lka8ް 8f 9MW0ئ.tF%mNM?x}~9_꧑Ö$?oΛokV"q{?|}?qpG/>yȑ3_{wП}~\NvT9c^|=;?O~=|_GZrp?uܱwTG]K|ӧzVRƑK]~+'o>zK׎{W]?/_qǹ2Kk* #sfEظ: kJEMb&']]̚q !lKD#K r;~ mR]\/obmI9 Ygnf`nIӸ0IY|DU4M ^9vIۑw^$`x4\d%I fց2ƜJavCl^Z*AFR0`؊/I4Rgtѓ75P$pz}˕y+<9/Sw| Ӣ6a)rOgu_{O={/wWs.zٷ]'?{_=q]8?p]cw|鋟A#R=܅=xsϘ\^zw}77{O\d=8z;_w7_/[N8g V]Bg8LkmnlӰhNV(}a!1Q)Ŏ, gjpΦP4D FΥ-; ' eti z84^LҲS-ayL)c V7sL}.Iy]lPKNjeS#44qU5z͜G(S!4j혳싊)-ι?Ǔ;pG>q;KWh:u[#:8s<$>spKz;z89Fv|󑫘?_<]&|#ȹ|ݟ+#+6/ys>-ɋ̣gX_N"^rgo>G{\׮^Fn,i`!4-' - Y Q:œ/*QC}guĐQޜ+YӀ'K]i=@`ԀiE+"qd) ER{.ʘ] 2LJ2 6L3$a {&{` 0{Eؘ{) r00oŬv 80I?¥ӻ'^pS0롃ywO̹SG.>ǿ8ox᫾?37xK/?d! w}7?_/?ImVʐ35׼ֳg]<_׿Vug_=?ȿ|?o||.=[_#OwWo[zGo;__~#/|=}G}A>}^z ׎x97ʇ~՛}óHB3zv|E$z#͟  `D4ēy)&廪T.OXWGNF_~M̏^zMNcjo]'Ν;sBҳ^a)Z%sQXn~;,~Oy]r{lSs+TL)tpshoH5r|Cz%oii-ffA7ޭg$AY[pyX+)U MhD:RD$%\&,85 prz1풭-LoڬZ|ϸvC}ji[f,xr.a3H\cY'x#7o;cMNͤ%Q(-_R y ~ѪK˒ r9ʘlB@9HNץuF_x}(c[K Hnl]IgS*}NcNp+YffhN$Ͳ XÐKZh a& D1Xipc% kч_pJD.f74=l$@[Z!#3hт l"y$15=MfѲ$Y! h./?fQ[B֔?1V!wXG;m"0݄Eo='xH ԖtR)}6=c 2L9XռF! P{bZs&[+#sV~ u$!uin-90]GpH94KcvZ SfN9l-q9B/~eb0 3aK̨łEg\˜iA4-AF l, 4*K Cq sёK&FlQ#>o О1*"]7l'$n?{/9+l6*)2'D'$Lu*`ϔ ZЃQ54+:ؚh(z⁕su(+.HFk5>i}3o!IYTqr=^\ "B2@'\; U耆',+ʂ'ci[UZ[2X$&e+=Ǘ@D3hTh o(暺j |͜ rxEM[DBx^aI*Mj c{i-@X`$2S6ĺD! KKN.ªhHŜb̻jc%ES XDo; X*}dΨ۶4r9*V96GX@]Żq% :W=s)$mٻ0h.2tYVi-A~ aQj09L1"c<ףW9SQ *FM,S&Xݲ7My=H;S2w[#m2#<6 G$،[ABs`3ZwK =3$kc 'DK9zCa`3kVdw44ޘ[L@a%l!흴K5A;i$S' H69q[˂OLk %7Cű1Xih`ʗiBT&4̓&o+9S*| BMEqvscBi nLd{uf5rz9PTOd #hDEpO:.k49G6\D5(rmi:롩j3]l8T;K7!)W"Y'Ԛ3sC,0% cs5ZCqm*c$1{iee]gviAy]qnM%asy½D^pC[ugUY L춄<![&ܢRYpﺊ3tԌbd7wHh{">ήٙ x4-}q1+G/wJg㑌S )^l 4eJlV5lne60Yt$e">].kM>ǕDƎ0w۹^;Ҳ@-k&Y kSd')GyNи69)N-Wv]iߤُN=a%@C=X޽pA2pvڅ'|~{'|K.]rm~_u ':d{ PT98&>䟯;q"ݩSN>}[oiO{Z?_BL(&4aMW+!Vd4:V!gFsC`8d,k"diXL˹5c'l-Ӗk3T;'RbߔcMIXcXyW2nŬ9*b]tUMՀ^EǼ!Z…R(dY4`~9Md;VvyW`Wt-s w <ЃAd{wjDz]|c>~?~capdǏg]ɰ2V%0fw ]+/]tŋbȝ*٠WLyAS؂lCT?J]a!U/s ~;dA&FTm䛞b<7RF4<1$+&HȷS>h2veQ=)S=sTrQϠqS`ÿP:m|?Ї|˹Ν9s&2LOY큻f5yXzL3'Npy>M{ʕK/^p'4gw}W^xQH잰j :!3A}xB;F6 `POq8hSZoqfb.{ 5z~5M8ql΍bo|t{n 4`v*5/vSWMlמoVG2xU 6X) tJOQ:R&W>b=8#)VHgm}}=g;{ 7侍%% d[ibӅ= 3VxO͗I.K3T-G^/^x> _;nPڵaA\AǑkK 750mp`︂<ˋiIiDm<MeYtjux-}J=V֭:+[k~{dnKaح7MF4dd 7.T66bIOr-nߜ6hHdg Du7xv{eK|CRcpc>fGu>w칳wy筷ܒ[כoͮJ5-)L^jq# .FG \ü,s#~喛o9rΓ:ClK'~%1!Erv 8HާNgޖrٟ}gN;:/@ѥFym'46]VuLɛjmL2o-ϴ%G N%SBFX^$=7@vmCNpق6`/Z<Ͱ=ln{aLX:+[S]3q!L2`EPvl D r]wr ֕+D{1[OvڗJEG[}ߗ|Ϩ˺+,qhxH2;lm 1Ry{p[ӲsS95*b*((fP&Q `"%`DԤ\i+eҝ?:bDCDP@TbF@b3[{揾C{{gyk-<Ԉi7TW/+27/ќ28G ϩ;:i\;Ř`5zTO<)D#fu4C3h(K'} L4"(S;D ( Y |ۋ D3%զhrΞ#|j|e"#a###F&iWf3Ji"[ x!hiXQ ]31!C?{ r}D#&֙j]R4EP**N>WvmRmǎˋԱ(Av`ɑuJĂ JDqӥۉsm`4ڐj3 67نɄc@Y<7LpIMnӁ~#51,mH fɟl2)QS\1 h3e( B[XN q'6غkKQjfѪĴxl̋KgO%Wgբfp.ڿ%W#࿑b@ANٔ,.  *16&WKˡ=q">*gsѳ\rޭ}?^'m忯% ݵyuWsKұ.KA fik{ב3;/<O7%.uXOjuaG;lyrEȋϮD% G@IDAT0%FՓy﹭\r`oϿ߿o[ӯ˖(GWNÿھgvdΉj0DFkPTe$N5ePdȡ)C+z,H#7ïAJ_.VcpgJLʪPn;bmҵD@mjl ta <;ʊLdkYCےPgi} 1] Q,:4vveC/G2]Q~wbZpV|~?+'i[.~~us׽w?yϣH l~m߼b1_xg?G~>~ύOhv[~Y~3_{P`ny׾_d#H0b爰5YX?w5r~ޞj[;_?ݰ38-e;ŷkl ?|cOy~ڑ۟+׎}7XmE{ϼ{9mT|2UZ{#-DK )m2.IUk3bS[\ BJ- U+"&/ #tvGddaQL'O߷P&&IA?'j -[>'yk <֕Qlu J_4ʾtAZ HDV0("ZTMpJ*HE" Er{?1ěϾa7]rgOuv}x8̹E{s-]ܺ{[|wy+я߿c'/x39tO~샯?Rm-w:RG]O mav~O>Mxo嶓k$ovq /uێGUϿ^;oy5/<g+civ}ﷶ=I3y3wǾu8Ӝ|X2 j5BP +,'MA$8Rl\T.G HhI p pP8V7` ?Sx 2q;M /h40'6*!T P`S0( ϸt*n +`G@ˢ &C7W؃*{2EFh)/ȿ@(C| '~, I"',R.~+SMo{|؉/[Q7=wXY ~ɕ\/ Dg7GO߹?7>?OW<1؃ؑ1&~ïX_×ް_~]Y}<^a)@|C]{sywIy⩯|3g໿n%xd>;r׏.^g>W<w䦿{[zW>G^Kf?~ W>U~->:gX>DzSj}03{W Q( !a # lOy@:h,[ RmIѕ5AM$5 IݪB^_j?g'=Ha@n}~A%'0bb* 6-'JZ|B[*|޳tyY_ ",%pϳB;2Ԛœo}G_50~߲t}psK?7y}ᅮO_yг~ϒ]_ݫ)VY3%˶X?wүЁԟ~iu?zH˛O|W0Z3_>s'^VzکGNϮ=۶wW='OLs<o$SI@ьoL|"ic( k#ę4j! fepAx+"l&4N(7zdg<(F~XRϸ3XnA?޼ag␖S%96A.0p@e+?SQ eg0%dj}V~fwڻX߽w|T˩t6`saSFlٞͯOxK [y_h{]^+W~7~}իs714CUkPOKo&WٙGx_ϼlq?AX\^ƹC2ŋ҃~[Oc +j$϶ݻ|E ,it%!7I!QMM~hF]~؂:iIItz`/$8Ar/INkS  sTtm?'55NVه=ƍ|G2=Ќ)HnIv)ӂjL ^$8F :2咞ca Ϡܞׅg\_W-<vSJP^6OTKݜܹ;/my3hL)_~/vַ/w~ɯxLX۞.>;|-JS{z/|W[{`[g+.܀m#H5ֳ n|G/sn٥/!Kܱ<[9v,ϭ4vgC"iKr"i]X(t> - Y&V 9aXPJw4<ܠLev$DJHNcq; ilRpN1]O9AO88|%@|f7d`[8{ұO.壟\8RCi 18?x|qa?=bnBd7_W wKO;|ص,?ʋg ] O~C>\D m4/mQçVvAL1NBdfh- \ _^~xQߒޔ+t7q}U?w7l۳g _壟_ѭ]wu{?]{uŽg?BhyeFQSiͯB >; >ܟzw?r_~[w[>.o8^_ Ϻ};~?~S~֕#_[|ϋvV4cBo 3ڥeJIygٗiS>,.%QXVٱa-%8: ͕NrԀ8*X+Ξ(ФC2ɫkU+S a@7 kΝ;R Z^2]Xڶշ,;~u|C^te >qp61g{nݵ~ _|W߻vd/n^Y]QFE/fqمǿWppԤw}챻9~͟~{]~˥BJoOMU?n!:k?_lG#s1~%O:`^"q^3"zyiod9tyteaO7sxǾ?9'fF)VoxOh7⵰L\sw i$etkI"Io)'&i롶7Z=w}ܰrȽGϬo{#Vٻ{-Ži'm}-o][ʗ* \'s-mٺ+_ Ϯ$|+ni!j}"MGUY<]]͗ڃ9*DX)CP 'oxxgfVOWf[8۶c=+W=?v5{K.vu. ޥKr,UC) w٢95> _1WU,5D %)a.!V Td&f!G+i֙ 7~5ƜFPLU2.ꈍ6XTfy9*D(d+W'b% -hhc:4לa5Y^0M ZF9lxS]wr̢u MDƮy!]z9󷟼򼼔o-G81aX^#=7_7Gu>ٱfnY>?H'>7w^p Ζ Vjl&(R!YIБ F ݎ%q>c%rl+\"Tǥ_R`q!va/H0A A<-1 B15)a$l@(c,X @Ò'^8|d3#A7(ⷾ,(5Ky2M 5Ot_c}71$db"Z1Q Th1rE&xލ%f`ҳbT&+F`6sjBAd`Ѥ@yl|ّ0ȡf 6'5cl']>uvݎUgf|dv(_^I!w]rxKvV/~r^]2S<).lH~M p E[ҲΖ!{l)  B"[:ϕVRc>N΅l$áj 3#dvz\#Ghˠ"G̳s9mB_^\<k^GV)fD1 DhN}G8_X9:K"q$Z0ʰm2I8)Y&*2 %kCev_Q3ldWCa`(9DEgAN,`b5r[ū\5[6g}٭^2gΝ] DM$NJ1BST{ư,H%z\ cu<ŕ\HFv-a 7q*K3MDϽ!>8Cv$Ĵ`҄^CVV;>?hp*(tO$*ox_pϟ=]{fXͻ:Ͷm]cKps_ĵ-ou h,L)̈́k s+^<.FuQ@bf BY-$SGؒ :UBʘ Ø0$znPUt Rbf*FDW=ō@Sp g#k2d"̍7HYU+7Qeb4GC7 H$qD$M0#LA!5(Alz^Alb>42܀f X1{+k"R$.q)"~ty˛-YM7Y:cEP(G 1uˑ, $6sdl}3lm-ՅC.<ٮ׏nY420[ G$E2VFiPklC^sX6S.45bzPF[ݗ#(ob ;~mëDpZ, 2@$wѦ:܎ `1Oh@be6^J 8AqXq΁H4y}:ΐÈ0 -Ѻ 9 5`-7sW1)/UGrWB4=qpS CcuN,}ZgNO1#={cg;vζRcGgwko.oe,…O-- 쾌,%7}>~Ǚm|_rf&ٯonctǓ_kr_'s_"w_O>po|ϗ.=%v/^Yyc_gY!¾=?W+_z/C^]w3^_croۯWc ?&Vb3c#h$>5 -銭 /P 'OX$1.`7i=xSx<4?z]\RLiLN=lŀMHD|Q",%)&JFР\Nʳ1W\XC_ KZu9&.ty*G`3Hm@*}hRcdF|,jm-N^ѶY+~§bێ.ڎ/,$oMԭ'2wz#vO^e1~y݇?_{y퓷^,jr<[^Ϳ^vpyɳp?rp#'v,|Y.?߻/W\vۻ?ϼQ^'߷}yvKo7A~Ε߸mwßq/tq ˯_~wTĪҧzoKn|~Σ.F w=w]oME:tO(#=vrɷ}T^\={k{Iܽ8{gYl[PrYQnLU9XĠdD!Hh(XI|]4Abmzv_ X$\! (9';<._jkDqSO;NX#$nY/ISdO-`{F#S"P&G*g! Yg0XH\>n f>Q$aVK]7p2c/~',䛪>3l AM1zY*eX_q^r7XXț}G_O<|.~/~|zם_Z~ / w$ҁG?Ͼ g~O/o߹td.cKp+\۷'\ЪG>;y7ox?}%Ks_ws/DObJ)sM-Lq, .Jgڀ {pp-Xē 72lV HxK}"% rlUNrd7p$ Z JVI}%;1$9';"&0phBeTI 1pDn^yTR NBKRj ۔Khďa+daAtu۷o *:ș)}vw mٲoiq]N`.\p^cA -~{N/fE?篼wi?o~ώm?hɳTIWaßc烍 \q䭷|Ͻjϻlz;;ݗ/Mɖu i]٩'ae`Lv3i c0j2֘ WiXv!;7˂A5vMOu(M=q(uΉL$c.qc421c{M#iA M(&{-NHKy29 i؉,AлF1~Ԁ"w^ǠuN,Yȧ:A?B6s&H($ܹݼ򜼾+8Fs@0dMdLРw+-_}{I(zȳ˿3.oO|/%;M](Ͻ߽k/޽vΣl]g|,;q]}Wt/]x龥Sc?={5׿mY=qWE5D"&Pc=t!c|'.VB](LHʢhhFqf.E,4:o`^ZQ|qg%-j^y'ºH@ }]ZGQ $a̭恜!2'"mcp](Xl%e4G".ETK؍_W\K:6 D:̑GN:+p(mSv^ٻwoݻMffBy݉vW4=3G0\=whXc޼䄮_-mɷt*_}Ǖ8?/~.^___b#'/ڷoT&H'8kO[={N#\^Ou"=~z};wl|r..S磓MI6ÐQ9+@}*(?Z/sBQI,2R) dx`0v4oDB\B0g,-WZǃT^' leJu!{0H(FajÇP=jXdZDi颳|EX/r͊  mk1ڶe[UСCο#͓0l;9pw^71ZO#P#ʘɴ{ ޟ gka=xޑ>o__CwJ~|eg.FM[IN&Xei-.ڷ?,=kew5c"}#8 9 cÚ`25] (A;4 FؑFȱ{C!l2>zcΐsSAEvYa ua&9CD[xBil礒N qLH^S]kS%tq-:"`QsɈO+8fq3٤~:\Y.[>q5ė\+D]ĒlF ]f{ԢDpqV1Xl&EC]n[ 'e:q:![ 7RʈKMa&m]F{0þٱʨСze,(qҁ'pBj Ah,EMcE' NSA Vsh*ORh.) fՓbK_˦wq1,gPlCMxyIoA NY/; uy֩-f]cԳDz#Ƞp٦81NMG> 16[l ?f̡Ytxu#7WqAY'1eb jYc@bX0[feE L!9\2C iS[qA(G6RfLrh>IG}6 "U0p~vXD *dIM2;y$dUx`烸( LQ!K-@h3b;R̘oP 13J&n9 "@1/8+Qb wi dm b}UJl4![VN 7Y(Q띎[Vd j }Zm Qs27ͷPA"%t߆K9ŒcS^hxkf)/vd8.E$A2AbĜmS(*vcoAʏ `n0Myj?"0J%KURrnD44OnJ"pvUZ,X4 Fd"'ڀ7:`)HEU"a7qc4dnK+|H5tq2A3.hd3%HDƹvbT`cOˆrZȉCaṔψPTt㠣*Ag-Stp8̎('X'd0x U&8 5 (ԲG]FGHڊ<@ղRڴiAʠ&Er]0Q2LxC‚.W r:; Xi~F?KSw(:QŌ@ WaDa$ܚ0M "M]amq^?jDZ7Yg=Ñ߀xby*vnklتL i I10}4mJUDµ53e$6ȎS9^R-:0k"0%Pd=1$ `PgT4E%JAr8=YPs*$?RL:)HGDjޮ8>̛bIyNs"u@#tYBcReRJg2zhR1RC3&U0~ls7nQ0f:eca@+=K83EH}GA>R- *NhSSԃO>YFY(ø1 k _.wxQ?DbәHOPV|"Pc 6/# ?2n.8"kҵce,qq@o!ZRWdy7ΌKb &Z~/9zprGvtŌZW;)aq$n7ZAaRp_v'A)n-)Ab̈!;G|ࢧE-^՝2Pw'w#J Т㝑!,cshNt;Qx4Ê|$L"3y")5,bfÏs,q1N e?B ? qWƯ#yHkP[h4V? YԐl@ ~~ E=~ω'apQЂvS/ 4d<,bV#Kc0 'aLDsO\xtk,Pp` yR|~I(udMĞ0ZUDDlhD!;YF#P7a`$t3bqpX|3L *r. f 0t=vfO$2>ЬRlߕ*3˅U޺Bij5~ Q̡Nj *A,j%l * eGd# l^%#줓M{PB%u8֣*CI_ɰuƃRPҡDm:iӓm-i \(gH0`!'29dae1&Q?&V}yr&>nbc@|ZD ďQ TthM&mT E{/k +}f=6*D^&yLmd ׽q8i /%:G{Ւ8ȕ`P*a*x C٧ayU+ ÂG``395bC/쪐]l(d# [~m<)7/FB\m#!6yɆͨAd尬qn7-W`|k<㓄)4ÄV\Y8P30%c{xq,V0M 穂`0AD2,x(KgF& QEF<,倈T2LDRme#+ZlHgkΌCp%|!G#,Ӵ` -$@IDATm]2C!%:f[rjy T9}mN&PĮ)y҉ 㨈-.q 6#,*v$A]H%,t&Ly}'?&qWQPT9uc6X  xm­LD쳂)C*erJa&g, *(|d&Fq1xVs"5Ra8$%0ZG*Muh\va3B%ΑGI|[麄М=wߒgyZe0 lcOM"o$yP`^wBjk hr1_][{m&YE(=ef$*GFhpB)3}-}FX;QZ4F/ Șܦ<@EX$24M: 0/Pti\S>P'5쇆Tw):W6lӨ&R]pΖ#u#ʄi7Biuv25ҹmtCiUi_J%*2= q.Qm[Qr 2hA5RBRԼF%9 aD&ikkye떜["& Bd( Պ h:J9DpzD# ӐӔs`0.LvLFT 1c2E9$vgݨe<>iK)^JP{Q%>!#nГHQʪZBE.1&UPMᲉˤ'WyO #˽GD/ѥ#L Gr+}ÛWˆw rs(iDp!憈n*1c.3*-S4u NP.ZȴAuwң,+QdeY ZmQ~Qh!nEH&U6W[tCFJSXS 0~/F A"EsgK[s.$qwt >vx*x sy+p"q{O!H,&}]dH@c fxQ"&R&hc.ԞÌ c?٨ƺ p,Mky zBPd`8 )P',œCʭU㣩<7YM71fOW^8; g S +&s@!ȜeK措}# 3شUU2 bNNA|UI1o"vINXdAC M #k[M8(C7k,XDd؄YjSL@jKz t2ʰ2 faWV e^DoEO"wc.eudzfjNլ5Yy)u/.i 7lA8V|<gYQx6t#d>uŮQ264pE6bC0##/^;Ȧ; tThx;[hҰIPBDg66B_6H%T;ꑨ%'T%3O6Q  t,[!L1IuOge5nD-V7X7 ona ?-ka(Y 8KgxqfRc;.uBcw#=\;c[r t(TYjHF/tԥ[aFi>[lţ;٣}5-XKA]fr2>x^^> Mz#4 ?qjɦ-a<|Pcȹ%]pѻ3.stQ =Ot)@L$Mi"P -D-IpB> &5*u0i iͭ"P2 WtYfHLC S49!`L L'4g)'^hN7 IYH2T͘ll6҈.GMA&݄$>X]a5#"jY"c8[H<6& 4/h=aq&QGjYU2I@R8*۱ƓXh'+ձZ`xԏtbF}pS+>61# 65T7%6D1c$BD_Cr] P6V9âԆEws&NM!g\udHrǔsNOFD0ZO#Bl&F ="bNfsf,c.ɏRmXYMDU0Juh~ 5̘'.U>j(R(we?Z@\z&\S 9)}{%fSl$kaH DeEr Ҿ o4ް<4Ad#O-:6z-Z(%ac8[H$w-F܍HȲqgBv3BYDCLЀ]F6捋 Gtg$en&dCAD=Aq #M}/io a@`C &~`Q=pȳ)L^s&U\ SI.CxveiMKtZl H]&FfiX&Ga-RxZfR@Bv6b4ādQ8dk1(&TM#6ۛGYzUWŜC$RD"1O2Xc 66T^VZ^]6 * %f%dI ,BBCj EJyΘosx)\Oپ=>{xAOhȜPHI֪ 0;y32~joLW.&FjHb#][mL2XmH3nҵ,q AiÖO}#ahreѦU_ 4Hޘ;7ՏaO`0-.Ӈ:|'N:5=3=7GW[d2:Ip#C`h!,:T\yߕwjԪU+''LMX1!p2|%EgU2ݲY$"%S5fVyeh౶ÈtIʲp@GrtFYƮarY?խD$A`@9N1-6FMҙrd5eQZL1oUHsA#_#iD*x3(},  ;4+vS1j=eW/-ZF-ۂ``%P9)#Gxdh9'2 r5'Nسgc=~aAZmEQ|$[ޱN :-̬$Vڶm眽zrF\)KVVPISv(あtkws;=YHܺ.xVK"ؘ/@o5[5!"XGek<EiD pPff1QzƝF!9WmI1R9;;35OZh1Ѿkc9qDuW[j|P Bɹ)eL-1Ӏ=rxdX$$%Cw~'&׬^ .ccORhG "A,&G wb1<73}ɓǎ?z:wSq Vԗy0o#k܂[njט0TDၭN9bp G 4vU1n4$Qk&X$.2)ppN.ӉaC$ # PY"%q d}w{utUheEJ_6i:!l/ %1b3 %vckA3Q֏^adC(Fms2ʁ퓿Wa 9Nz K7{|۶6l\jRWR+COn3 ˉؤqWm|nI23uw /sw+T9k@69T#K (}`l >Dtk:$SlXХv5 +قH*14 dcd,Ze}g(~%W&K JU{S 44Ftb,f%ʶv{pM"-Hq23(f ,N* R]!R8U-ƬwTҼD-In5^<u~=Y}lw=z[V_UftFgfAVX^Oxo37f6bo҆٨MוY7:=#'O˶mA1^<$Ш2Dfm7'mTڶ\ʹ%m|Z&j^He&.G( WLȇ]th tM)&3L\-imKAj(@PK`v0xV 9wʨ(8@۰U)c)MPINŵԕ_3KS®k{֔aZ)~ K}gWfakx $3dGw:HaOaAQ8 9X%rjZD`a6IW2̻2@DKb4i2z ۲FbTUHԴ L,K i旔5n&Y5>[:Lr,Ǝa)%IXd4L=ώmSKA9ɲN[=,vu oU}Vp֣$V;\m>PfPSZ"])6 ,@RED#e p"nrᡳ&}Zs/I h",rvj@T^F\Tz@I??5yZlQJ]Ը(]uu'DR2Ǧߜdn6VJ;㤸B?$e2`$KQts.zFecFp@1] ؑ+'?T!lFP̱Ύۼy>YILټyDv?:bYSF;>?|¿|CNXaQܴ3+3 q5ohLxgF7l޴RZJyS=?>67׼?KwW:[f%,3(y#gPڔgPϓ 1p1Yxڮ˴GӃj%rTf׫mi`_4$X/pn`ʤN3YBi q/-:VR$2K6ts9T7hz I씍,N&L7 ћwtd7mL5VDY:r÷>ʯSw~;_^7Ѝ|[9/}{^uxs3RB"BZ#_:MKo?/?"Ckx 33_|}Shsݱ ?tyc?ܟWwld3~.d+(iJ+N¸$YUs-SJV~$-6,ES_q$PѺe?*7YNkړJ9D27nG%΀u"JnDuk`vMU╓F?*qq\8ȝ>&!ˆIficC}[_ صeȗ/wj6{g>;^cĝGzѯk>}W=4f;>ޏqrݥ/y.?o撳u>1=Q=o߉|U"oK:'pWy7|o[廢S^glw!Gl^=z[;gnCO{k_zֱ'߱bR7 \F醢r`cgb,EDt {DuF \hqRk~W0ڠPfd{;=EݎDjFѰg$-ti^u 9WH2o|sS-V\+Ǯŷ`sWo޹?y9|U˯&0uY;<]u.l){2ڴdin6ScXiB9B #ݎ2r/Ø=u`/Ǵ;>!|+N͡~ԯ'kP!Xq"lN3alc؋Ȟ,e/o4yU5X$<  S4ż72s`hQ1|id҄)FOd̼N@5Ko.ۙGn}ԮRm>4)W1ܥ5ܛ?qǏs⩛wª+qņȀ\U/}ꖱ^샟}߇rhS_S[~~W\*>^9r?Qm5;k[=~pwք3Mss'o|]ϼ5|{yï=r:}{gGٵk{/$"ϔIS[2w T+Ňl*:n@T mU'::W$}>Mً%|`u ,M|umDO28XJ5ļ:K<EbSw\ќ !asڽ5p L˭ mFbT@ZG{ٝ&'pbb^е#4k,hy;^EGVz^^g_sז'&xɧPOL/׮| R3Y? F%8}Ǿy~;^twzϱ+Y#3EYtGj ^pҴ## Ow}7}y_?}Q%0{|ol>a8 ;xX*lç͡X 3?j*1>Ii@Z =|!]b20a.Ve۶μHKEr ~d2m@ei W ]r{#[*)pH+D%hSeNz,Vil+,rMb8p ±W E,gsdϗ4nlӋVlXAtLG jXoEUg¤F^wrT-{z]oٵV 5#OuG7~[qCG\掯޸u_6+LJz>08JaRP ͉S|:֟v<[3|s{ӫtýE}M_^пke5ok97'n-/g9>uPFW긟;~|z:'Ea ܁);5ܕҘK'am:֖$B[*D|[)Բjus}$sj&)Ե)C1+'@uėVvoS-عeZe6mWI79Gu,-$N" ?Kt4єƂaO#S'O?qO>5;7QTReCc|Ç7rJ0r כO9C7r{$b:/\FHܘDAwO=gn3~^䵿睥Wpito} V}xb˦,g; [wrvT&u5rjIO};|rax媉J&6n=iM}{ώLm8%gGʡ n#Ǐcmz5^idz b*{d՘*×楽}e!XvQ=e;xX4,H,aNh ~R A~Ϡ鐄ETW7E$ftl9$ &Nv3lhxX/H$_Y@aa68Ӝ"]1]}:[.,o'K4&8>t{Wŵ,9xpHwcdXcC:r{&vX= (QkD[p^:/ݶyDCϿjw|{]MKF{_ Wz{>pǭ>:q^u䯸t%.xK.w/çy}ݮ5=}{֝8:z%Ox͝w~m9Oql뼐&tŮ__ɝt/~ꦡS??__ywv\t{=+/ݱi֊AUg'=|yb#w=!$vgAT ٦AUNwFđzdxEʄ53M(taDǒMt$Cۂ9wMd`rf(r` Rr)5Vӆ$`ރ=x9aeCɉ-֖iWO-D%ԆcH_jҗ%Yqꏮܥs[<; 3w7dixUolk-~'wި nݳχ\dKy>װ@Ks'5{wz̉cs+Nl3Z@$8}aݴONMXC[>v„h,Y>rȚzO~]uEыӇ[i:͟<|lx:,͟:zꙹ< ӥ4sС3Sk'm+Vfᰢ:(CF.ܫ&ǭvH )zjXFaz #iqlמ4&,|Hx8X lMG:v fdLE#8dijk[;(GghT7;A+* 0?r T,zx眣?c``jZ10Sn{Vp{/>IP&f쾿ȒdfVb8t⡉{-}7qwb]t![:һ <7bԁ/N4ډcϣ<xUb UlKD pV)#i fl"⎺jnh\2KEw<̝%HY2,xEU#=-Vi$U^_COTu %%tk>%#L=U8ygM gG0gQ=ЁטCsjgJņlhrn@>3q&4$rpCoHYlp[#OJ/ [.ĦŝkGUؼľyi$<2vm: 6iXаSUfX`pQejKÜnF1h2'6l JD #u]>zs[d蹉Z.s=.Wz:uo;h=ꬿɴwZao;_vc ['5Ʌ|ge&SuœQt.bg2A1AխCDA^S װ$"lh%oS !H!">졍HI%3aX\b)]#VMNG[z],]l&K0@4[AP c.۸59Hz&C=l D ;U֚`%2k 4\+@r( V0Oh8W֎"&) .E׮5xױj+azRD*;=:ܻ Y3o&8j%GF6k\Z-_a"$7f\L+B(KTa\8f4(mЪoVKIpd`.tD"&m”Dg][Xh DfH/jSEθ,n2pIFXUf]Čj#!1e]20!~$ẤΨzT.E f,KN,%X <"Ch!hYS(փ:Akxmo f_JyBNvV\3ƶ2.D0+`X}An9v  aVmma{ߺw萙5 w?͡L]>څ.э<SMrҸON6Ψ m 11OUԁ&$R;SH*fv&gzd16`fVk#֪^e9-YVKjCZi#P!` b'1?BVbSoa$DcQJnPGqA'mp5C$-j4brKE6hGjeʯ0K5Vf:1w9*'ԻA h{@[ٙSz#w,.sST,J/m>q><NYLR♁-gnhZYQ[ZY0Ƽ[ 3„uSOF4Lb $LEiɾ8 TI&KSɛXG!)fM/ ;gў@IDAT]O:S`DkcPi,Q7p||P@a +Čҳ `Q(сr.bϞt`4blM,$IyW{ɐږɞ2 =]a)`#O+~w饽U{]g7>߉ 58VG29u69DH$,>L'h9hfL \ A Șm]z6 A#u@h,٥o{ ǑD_tmKP۶z˸8"&3qlAxd-=]Wr 0љp炷h/\zfj‹g;p 4K\8ϯyƥ #2[CjU;y'ߝ_fh7AJ,=$< MJcO'J5 J.wž/׿מ3Za$0񸙰KpH?V\m\k2uFOx2oL2W-^H>ҺȌbh:0(;LAg:NCmTbuVC^L},s1H)U݌Ȳ uDAkevyV]pDB ARaɪϟ`~?2?r˵oz|/oC77 */s6P|SxK{ w6~AN?忺뻏._wZY^Ζ9Ԝh N/Sz dj9eE &m2OtTN#k##b@cUX-b#%.Քo(I,(Ygf^XqlZ ML! 9pK!l]Xࢫ-: ^Uǐ/iGF\l ]-["UCːKJ=V c&VAR/5-L<6۾cq]>ǷZ=\z|+ã3~i长]?ܦۇ ^_pszc?zxaUIu{+7|]ݷm[>5=D&}g~y~}j]l?ZOzٗwX7eÑ.G@G|J֥l^;o )ppPބCX|_@0r3yQR`(r(1Y QArmki p-xN4+&9ͨSb MZx掭^C#vbP!Dg:X',h/%a6=Pɀ|VᕳCJ'-ǘӀ.,46/)e2*q/L b-tGJf)Gg*2eee%qP.EFqhIXx_r'!ީGN=rC|qvSs?™ k|؝?|.xc;b@9txqEP8}ҦV1i4 Coܸʓ+礷4ozg?sg=?+7Uv]:it Ac* "Cn).9tK$Bˤ3 <-(Z>J7D`5 )C+!@lQ/UɁ,HcI^`ūtf#K w̰7j D\'|B:t"ikLtz` +!zJ'>$Fľȃ.a#ڦe#+[EcG+>:}CWiI?GS}}xxӋ?xZx3*%sHxc+.~dNUI?=[N=>u sw>C>kǸm 1bbRe1''O--YSF}on7so<ΝK'OG_OO}/|_wekSn/S* Vu>e{-/OBؐ"bZr]S+̈^c>lDp rq/0%>p(/.uRyPE ;E1"j6fZsOjdʣȦl\.W-[@Ss։d@O$;rR+*f _뢫P=\Mk71}{@Y7"^"D)&yͅOY}-=r38:Kf?o}K֭~y7~_T z۟oW - V~ǯzr}q+u`_u[6Bei][o=~鋶r̉HX̬Rts;ߕ/)+ #-wu HQF db,kb'aAA:+@ 9 2c ,!+&T.U>f:5 ǡ |^bZ@n63*[T8%~NN17`6)@3Ƚ;6jrI\Zp;*Rq:\yyd18ljOB`)ֽgsd sS |vr+WonްyuF>-xu]k޽A r5oxZem詭7w|xW~t~?>sk3q˯ߺ~Ư“W~g7ްzX?]~G6_[_lk|/@!v$k5&XjĴCX}]’a_}$s(Z V<@uw")b T>'\aM |'-N\GCp OH qs2acevD!dP [UIc7m\-?8CQ-xfZSǰj ?t*T&֬YvV(}V9U녅=9oNZZKpe'Z}cna3C+׮^\a72ʇ#>7U4õrA0رᕫ'b-NK/Z+9z4wfɕ+Vʪ'\kv1!ӎ91?2vJ 4۸&qֵ@қmw+2='760"!+ÚrF*D*o@R#dBH.̭Z% u Ppu!f=%´Q(@ .' Ӌu [Q5a/tyIs)Œk[ȀZ6%V"&V_[wСÇMź wk$ݟ*䪕tt\"=JHu :ҍjQ:No_`-^iR6Z.)h &nXFV3|o &-cod庍+2}aKCZ1ba"6Ld.VҐ$!X1Jݗ]5儧2 rpc$brxJEwh0H:hmFEӗFT˜r?ٙӺO;eN] KFjڸxB(l t8TwYRϨ୔Tj 6!y9ȥɼBPe;p7_$6Q:;lgߴEzU-k7$!Hpc4=q"4$%\)kK)6o j>N^K"P^y3؄ Bp2%Gh(c$Ny㬀?V&^c5zZ\ ` PZ] %nn֭ٴiQ}?c=OY/Nn6Y"8)w+WNMM_~@c00ao|fҔ8uâ3$ AQUÐ2DG6P2q1`Ӳ/(pTD3f\* E0J\}Ĺ =ϓ$\ݨuҡ@R.vGeKdKmZ AN|X&`,;0':$`L`) %׬7{P hȧb "eC~-ndj]H +UsFݵ"߸*D-5,‘) << `ILt@0 4\ڌ?4:ÎtuBMvXD!& ຘ!|O[bQH#h@ h뫏OBx]W˹=6R%j7 {F S#;`BZ"fIԞ Q"\R8윥5eĶ}A+)1iO%6nueeU.s)r7S V:%:rf!Fp7dTÊ=JPčM}{Yii3>a1'"=@2#MRRMll8t@"374Ȓ;D 4#Gf&Lc2  %KCs-[ʊ 2j  9j% SFrןP91?yP匡ЯFPiQQWD̡/P: w  !OF77JX8B;6@b$3%DS*3aɓ8J1R)BU";Fqk h_TbH$B5Tz\z 'qP_h񶃃ig#(?!/ B܊R9+2U%2b!]\*T 9 oТ%/j9/-pyn{ ] x3zXGa:e){ݲZ,Mt=;~(0@Jc4e>R@X lB(V?F/'fGǛz3ܠt\GUL@ `@6^#Rþ O5GcJWLճ9GH!?Y/ ݭ"QuN kOHJd`<|׍m44kjQL0!)c ,7uiy+eҋa}%:jȔj\A'TI %M>6& {=|}$pML@H 0ǛFaegQcs&CP B84ݘz@8$2=@Vc-0p{z9Pgq01WtddsiM ip$AY GHH i`YӖ 吖a!b!9|%VTS;EI{ė=R ebS-hm)T@1:2(z(sDrZTrJ9۲b ;ql>DQ jZ! ow -ɢgY8uH՘ ң+ԁa53.eb]iWPh=C0#~d8BBEBT nFjoxz,1AW):N6EAYxX2U@0"T[GsGlCbs G; 8/&Kd}QZ# IbdZD6ҞZSA9҅S3XƋ'3"x7D*hL$3i |KŇa uzX{&\>T!TWj])) H` Y[a- _?gRBufApNJ~#^>C@ 5rCD]m9`"3%;.j 4b*㉅hLY 3sZNm "Kp<nSb,ƢtN=\M:<ܔH\Rѐ1 FJ¥#$"pÁbAkʍ9WMe0!xmHU!%-5Qj;e)[i(#4\\\AلE,5i8F# Ր ¤diXoGoi@,:TJHX  .& Ac h-GHPt^$Lqdq5 ?Bw<y)T.REQ*4AhvTsa3ql͆PAFRupe%4̍x} XfO?r;O؞H S& ]%'J'ҡHJܤ8yF6@Xm359v(pi8ڜl+c:l`PvaDl((@i?5;ibCkDC<};\cR" g[q,KJgcцזHv A `3C`"600i-IDJTNuNľˀF@i@lD=B囋fP6 1湊i+9E-w%8VΩqT)Լ'bIoX>ɐpbԯ9u;IR+Y_rF^f69qѴ5"5kJd_Y?Gcc؅K1 \C H\R sX39u';*%XpJL\f$.]\+}bƊ" A-U|\garҥfĺ{Tيtj%H*&YmQQ)C@؆q4`?H͆&-`Tk%]eL CDak~@̍~eűttò%QHOcessVE/xW7૨pqHld/@ 4UB",Oeb16U6huϏ&"c8Z:;fLZ)%DZ5hTYf\C@SDW%X@K>QV\Jm}fa^ކv ꒖h2-dJf"B#Q}ΆT*H8ض^b8vT:b;;Uh\3N B Oa{s"ܒ]rJk.0 ut 1'OnHUB˿ڸ }or d$rDFD#.B@y <-[Nc: !OV@cngú"D%E$`-C]^ Eq O,<0ܚ렂@ȇ #> ^w|'4 g̨:xDHXxN' W%¦1~f)ICR/<ž~#0n\l0R:r >vN]DƠ. ;ig80&V2Pc+O9d+Ǔgł,d>6b̴ kd]pE%-K-h~}8UimTG-Йub^6jCQjgb;AGV_51arf7&Jп~!>PC;zi A{&[#؈(.1AT  RZ5J0Ö_CNP,3F,OM +WMNq"$JR}/ū並9$\&XeM3o0rqF6 *C̰͑ Z7L-!/SӞ@5º2GW^ G$4 Pf5WrDI#`DPQWV(8,GJ<w|iȤr3)AOR=قq@`=./ R8 F'd1oA4}9!fPo^7ktT L!hAb$8w @-SI0h^&ǖP [[r^SN-W+9G@NB@{6)v4!A*:۵O~R"[PSU);8z5<`!XZVcB9/y*.0;gtµM J287 㿇r" ye!D:[w)tT˱qbbH*Brrޫ䉌.+]] i*]͛r,d5ɰFSGxYm'oHV̱7I˶N ʤ3Ӭ2{R4ML"6v3$xFqxfa2\r9qf, |鶸rf޴cjN&(eU!3sz @z #׫Tg 'prMif.^I dM(NTfZ y` \cJJdXwL<@\Gcuݮ>K{N=[VCTH$rr4Tihj%/  !KE&@,@ Dh(Hi{-lN{z9ws}931ﲾޑC=ņiZk[a氃T\l>7 ~I3q$ˠ[p'-h`q>M,1@|9l\ȭФ% 4g4ѫ3ZI6Cn"ʬ2+M&!2)I<2A- ďU_л*!7_aIB-$V*SkA b""#a F)1A/g[U&*&a{Gr?.4ty3/'_Ւa*ڱ   $s7Lv kp+fdϖhYh6Fe-v;)#T`!4.9bd -#BBL yv%e^Vt[Ky} t(}{L/6I|@}]U˪ѫ2cH+2ND]r!q_rXIj'3qȆU,$pMARRE%]0'VQ~ .$k7Qhy;VH^ieӖ0d*vB",`4Q=lwr˝daٽ|I1w8Q\r3-xEHa8ϮY'MIj2SU7ž2bdPĂΦ˯ HB$)> <2(&/!Fa9vP)ԱK:&{E>M|SQxS52Ԥyfi\җPpEg@99N0>צqSy5?|1Sٸ5Sɛ\ N84dcLd[R+1"`^J|v1s>Y:@$ǖYXq S[my1 *UNQ;n=Q\>9H28C{[&tDޔjṵ"EyܚTm[hGxU9hdt VY{\!oc%aa pn IhQfȂ3nXK'6qg,7;. Oڄ _ C,Y3#6*|Rw a(U0mb8#@PWq Q0-5dVaH ;凲_ApQP^QHAN9efD4po/,鑼/ = :Lg3`M<[Y6Y!X LG\o܆UTQB+A%Yauʋ ur}JN&[NSu7k![{F#% _b,EV^'-xf.rl$,Чة -LGPL0;( |+ W |v{p5͈^X|2 ܴ{@;ПD=x,{I) Y * XހGK46A=l똰厍uk&SpņKÈzz,OL@]11&N`ή##x[xd3C)D+35ҷJ",J!;SRso˒^p3s,G#LY\D$U(Kf$t҂ 6!jY{3BEF!e!kd.VQ⎨H68l#b|;l+:G;Mw` pon &lW[JwS1(OpBb^_AvE kD֘1P S` (_AZ,LrTV 65!+FٜIjI^C(!B>1bX(6# 7Sm2@3'w(*v,B^0&nP96rP\LpH%9 V\z2j攐E9MX$m4OiEƌdp<7cO0 M '!u!'*3l)t:4VCY@H7Z2f9jups`Mzw6=) ƕ48?' [aY&!t ۂ dvhz틖E~DAPKbR[~6ubAI +w 8d0ȸhO A u@~ M{%  1}Ќ! &'؎TLb*0QTdã#ԙrjC .gNf3 F*H>Ό򕓈v~2 gI7FJpn$j _Pktn,g6ZiΜ&cDdD•<Č,.& J *>Ne 6k9eթk%C&zŘ. 2dgnxP tL>+@yaZ0q2xōb!;̠”âh=r9r6$'˪#uXUxN[-'`5 a8,O_ыmHX;4- k,#\;۴`*>S*v QfSk,^ŜQs$0>N! \!.=w1rZd ۏ=A# vkB:nD$ܹN|ڋcgO#yn' nKS !~fl&K3dǜqu,-ń;b40Aښta Rwj 7P:Q);]pԬDP Ƴ 3YO'\A+/(/$*WhTǑ"ڋnl (ZصGV8\.~' ϴXLjJrS>@k7$IY86KYdƴ$UQpPk=TӬ8cE&M߈Eʾ b{R u ӣ.^C`2(E~MHfMМ=0MDK` K92RhZV؅[ɰ2ZbXGR.`f-H8ЫTlhܩtˠ'Fnib 8UU^pTf cӀ`fXfd .sʘ|btL$iP!Q;%GNGCB`Jܛ;CdÎЍɃi]LJ8|J`yضA>2MOŔ$eTQ=sqמ}t#M*iysf1L/W'MLm+ưՑjb Fϸ]1BcǍaD7,9CɆwr毠3Uw Ki3Yd32s N3N5m;%Y9YoW: ?[Wb Ѡ`k4e˖ǥ~cO֘5WcДc曤8,+ { UU /7Af.tן~tي[l4S:I~?/ɿ! K13m2*Mv|6TR8SA*Vw8v1[,AQ%8SZˌ K+`;E\ ƱL+??O'm wF_@c5bdv_(ؖ%T$I֔ir0ۘ|84OEEJ׊r-::'jerOM?᧋o_/J̩PoE`rέp[8qL}2m7:A4'(]M+~.JmFX [ JV-z4mq seQJ\Yp3ϽlAC0ndRoFxئQb /#taق| ]xܱNO>؏h\!HbI;$IJ pfc۳&qdk tO|k^zܸ7o}0l* w`UTMp`&sN}]A2L&sɸ_s睕"Kk܍>'/]^E$Awٟ|M?[m/| _do|?o~ȫ/ֽ$Sҥ}ԇ˾~?BrtTX f7j[hQouE`EJ} .Zv". oLzij"us0ܗ ֻ!`js2 n.ˤ3-Lr&GƻzmM bektBNw,"NEJYcrBsUr*~{ѭ0;N֕f[9&^`89}yΒW }8oڦE -U.0RicbĹ/tc/|_/xg?}WԽ/{gm۾y<'<_YxxOsهՓjTtjuNGfP=ę<3?"*4LA+0+}D"HA3_@4!z  )L6;?3#O4qNuZnL.s}gfyD.x#rC`4O+H>_-%!Z}e2:9Qfg󲴯 뱨 /ʋmeJ(yťX7DƻemHqCsIrKE:4{'ݭVKx:Oz  Gp zITk\]1o$sJr=zZd B;mROqLX8)Lbӛ ^:P+W#MSI_Ⱥy$u7#7y(XoV(%y*+AUХA :e6'u(ڛw{¯_杏|G;/__/R>txWk^}w/wp0S n^z}?]o_K%/3܇~]7}v)5^z{~ܫ /G]|\}ɏ{Mgv^t/?ߗgG#?ڿYi9uoO&v{- - Fg@ֳވ.;?APdLn[⥄esEZw՚Ѝ(q̚ǚaD #MGh/n4} %yoi"{A `Y*؍j8>傥-Eo~D@ 5  Z"&|^@cATMg4 z*=B?ܸA|·>k_/r祿Ưد~8y?y~7ÿʃ\}W]7/}՗/[~Q;HuW_w#Ovߋė>3{UM`XjQ]ukZ1(9.3z0A ຐ%^.дq#]y k58Ӝ(BђHd-r, gdfJf8y`POJGЁdzl:H>PV${~?zD(,& G{?rp d<]vd--/謥"}HZH*y3O~O>{{| Ѝoy߃dOמ={xbUzzxn~Tg|/=x O>/N[&gEd{I, &nS2hv ϔ9ǐ\,aNXSp !V RN-1`YTۨbTg/%s|P$C7<=sk2 # ,=V x[By9TZ} 2-=ܴbkUUTORbEeZ 2Io54-3XTZpPHhzO }#d'އ6VC2B :MֳL0N]ˤnҘ̭%5[Am=$PpfW^ 9\Pyru_a@!PřhBΑAr,A$;cvf1Xupd{t8ذE;ȼ)lZeF2 +x%Z+}L+9="4 ȉ}"0L=@wvB$:b jhTX.@и12ksL<SO,6LγM^Q qbcXOڜ eC#Fxs ̉eFᷙNE)-lt>p )pk\D*P1.F ԱB%1JIe&"":,;Ε 5DbVfkӻ8s.lJS0KoxEr3^eJQ䑺I-IGrtd"Asa6P1m17z#VI$͌"{< s̊J–pș]nUkM( LD=aZ`GXexcqAF!ɶ?BVDӑ]; Ig)H ]eJ(K+$8fm0s=w;A uAfԒV (S}ivztqKJeeJ2}D+G낈ULۻo`saki>cR,ȩ*Ь\PJXs3<[PDAA ’S\ 8JlkC]YCht,[Tubl^KKdnEq}KlL$c)Cg`Rڦ^>dO;8zW[v1whW!8FA,mŠaCʹ؝n"(&njC9 چq=ل$XXK=ր(/oK5f]V5bB씌d 2,F9ǝA:cI6+@'D UNV8ʝ9ܝsj`" g[X=D&Dց^bR4 J;`.ą m-ҍ+01UVlJP"38-MBmb l.$ u̎ˉlYTKɡp0%5㨛r&a{ ? p(y.IQj|H] 66ҫ U4ȒDUPD$qeXf 4lJ!/0B&"E ϔ0^k2L%1&v*$&& MbUAu{Z (n=Բ{0lPZ@0S\g) B?vMl|eQ4{( "d6G Ӱ'`&dJ>af醑)Pptdբαq uS"Kdz6UrvA! xë7}y/HKԍ.&'(<ɮf0d>$JPv'su^ 8|/bd|DWj6#ynwH| $_CkDb{agAsP>SʈKpnS2D~SivrYt٧^{Wǟ)uI!fa>ԐU@f[DFI>_T0r)(" va Q:#!`d3enx -\!Dp jdI<<+"`p0s\ۆq@#!d=${b:XS΂l=,Js%դd{ixSoa:Q`$Ir;4Hl퀛Ns@zyk-A+2;J5MA,G`0.q .14-Dy {-A{xfjAn+2,CRs*jg:⬲O )Sp1qdCa=88%!8 h;Ю;$mVyG4O`i}4 )Ġ$q._ni`^;x -F>ɶ:%#dt!؋ NL]qȽ9݀Ly;8 jT1EsR :Fs 90hH]a6>0{Mɛ`N!<(bBivt; ?֡3% ;Ij1azB%bΠdJ4^MDz4Q ,<B3*򚖟R+Tfj6"U63F,ubp~86>,G*Q+jZDT-(Ev_L/OFˤdP&#mz̯&%fӕ|hwD[\3f-miqsI᭶XU4i'Lq6߿bCŕZڔXʈZ?cz!(P6ob``0͜E a6gӿ4PNҘ( Lq8 \wq*[+=GM`Qhxj 2xpTif1 nS߭6"6orݶ9O Ή6RNzgZ7 !l{KV?1{J!g|y.%R^ʅ'3]ĨZ\s]31!3b% ڌH ',#G̀oN0/sb c M ɉȬ{(H8KAKBLSI ڋm>PE4d6xP[c(#<"R>%{Hg̱zk1LfT3Yrg+1zyQ0o@HHNg(4r>ݰ lj' H8`12 C-* "8'AÊsR0KcN3)\i#m⽲! fcB |&p醙 -ZV9\r!l\) "ÀJ|ZrTfr7_шD/,P.ŋ\#4H&]/ܤTPȊ;8{hFd)ʌ~|A|D jIy ֬P'BfVd# zDr-zxP^C[^*fZORx=//>!%JR6D&0hb Ux%d4Mb,;D/Eg 猍9nZ% j]Dhk5HeD<'_E3Zr֥V`hzrAnxyZybUk\LD U}Ly'`(#OCj${<3<`У"(~!h2ȤӍZقLVb➋7iޜ'0dzWـrOji̼7F*,Jn&L.^!Q`JugDc0<-R=2As{®BuBt tRdd8ƄMnvX2$.A9,3,_,&WKpFf0+̱esл΁;BAQ\ΪJjBY~8;&5> UFrg]Fw< j]S@HVd6M *|+lʓS&a1SA#Q}8'eŢZ~D?Q\"TB鲘<~RX~.@^K,.^ඪJGqP촏 c Tl X],vֳʭ֜&hu"IXA BA|k_xH |l JdGKvNzCFHh$ʀc*`wHꀆk˧[9 lӑ5U ~Y ka &O9r" {}U夵{9Is KTՌؓFw`6Zb( K ,|F"8 2ˊ?ēH^9c9-F U~4YΩl̶(qO~<Њ\8K Z5]^ƜW2QW"#rqL7jjWqNA{5]llvJfKs-KN=륊3s7jV{/q 7b72XN6w+<|dc0=y!5&PzBqi@v7+{z5H4Fe\F6GRJSDW#4nfERH(w=V$g~lnb 7˛땤yBQB97=(26{F݄a%4F 2b%_3Ci卫 ,l{ EhzœQ_; 3iE``V 9nH#*W#8-8zV2:cL'BI5ݨ|C u&4VI'L,p@[Jxiωrv-$T,YQ9 cw {}uϴr2XV%(جՒ R(s+y3sxpP-:(J '.Sfapƍ6fێ'ʫs(P ԪAaO^Ly&gre*Tkj ~LY8lD5LLorwK!Wf$^.s lM9!ݳ[4H:3$ T3eZxLЬ4ޠl/K9GTexpxx$@ɕSՐp|G?=&3d=b-U65 6V v<14ll$UiTa y^Y]H@Qf̆< Yf  v4LlQ%dB7-~jk57ٳy]@SV^Cjza9ryliSM TFt2{a؍o+k/ U%"?rNޢa*C䉇48mڔ;eؕ Y1'|>Q :9X8KP+Mr6uW^mIf44&vHW<{M"$:i2s#uMBtp ͊Se; x2΍D e85<"pEf[k>}0k# Ru#9 2EN5 ` ьRBs么Ws9T +W2ZRu1O&79|s@ J P7ư̦3 o $,oْJ&vR`M sgnl/ /$4 f'gL`Ll >XEĚKX33=Pu_ i>Խr_T" 0_!Vu{Gy1a: bTa Oܰ )t0qF'Ci,Kт ׫"{Ӥxmj9Y-p0hJ=7=(yġRW<0(}J3RrKj[dTGSl!}mQ5$,N˝+ZNV&vA9X t2xG s,唫 {a'[&xɁ[L.)tvD1ԋ/WXۛ2d /:k1dp !;)/}e&k3]#J(YleUȊZ1;P͓FYDd-:TR}@E wioW;0F_"4"h(瞬2|l;Cnq ihP}NmR`' D9HB\F7HCf("y(h"F.!—I !QaFy.'3icp5+QBW>< X 0>8yi76wDHf&x:1^<4 ѐJB` ~>IPmq |E$gU Zgxvjie)8B&3bzv/.D!T| Ovar3E,{ !JPs̝iir3VvIT G2>l-M QtD}7I;iL_˪)Er`fHJ# L2h|5Ckh<)A'#j |[@~D|#_&*:r)c!,S;OҭEeSFY!r Sf#J,'\Z'.b`{}bJ2MfKs!+GϨn^9Q29ܠg 3wR]fY̻2ͯGKjhZO )-#/\4"FԳya=[C~mCA>j4.3$ϷDBF$˕:o*JvK2;l %xEHg36 ˻3jE2+AiByBVS-x0:(&plAK1?cOKsQq5oLm2n"|̕ c/9l4kr{ě%S&2Qy pf$7>iSTXP. y<3-;Jr==ϵmp[6YeQR=VO9*A-6R-S%< 8h4\J¦A\j]) 4 BPёǶ^ \t~؃7,THj#+"UDd3FY6;HxYIDATyTg>Kz^.b@I.zd qm6V.?qGVjFH g̹*Кڨ\O?(1yqEqIENDB`./share/qtcreator/templates/wizards/ubuntu/scope-14.10/CMakeLists.txt0000644000015600001650000000604112705421114025516 0ustar jenkinsjenkinsproject(%ProjectName:l% CXX) cmake_minimum_required(VERSION 2.8.10) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}") # We require g++ 4.9, to avoid ABI breakage with earlier version. set(cxx_version_required 4.9) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") if (NOT CMAKE_CXX_COMPILER_VERSION MATCHES "^${cxx_version_required}") message(FATAL_ERROR "g++ version must be ${cxx_version_required}!") endif() endif() # Set strict and naggy C++ compiler flags, and enable C++11 add_definitions( -fno-permissive -std=c++11 -pedantic -Wall -Wextra -fPIC -DQT_NO_KEYWORDS ) include(GNUInstallDirs) find_package(PkgConfig) find_package(Intltool) # We depend on Boost for string trimming find_package( Boost REQUIRED ) # Search for our dependencies pkg_check_modules( SCOPE libunity-scopes>=0.6.0 @if "%ContentType%" == "network-netcpp-json" jsoncpp @endif @if "%ContentType%".substring(0, "network-netcpp".length) === "network-netcpp" net-cpp>=1.1.0 @endif REQUIRED ) @if "%ContentType%".substring(0, "network-netcpp-q".length) === "network-netcpp-q" find_package(Qt5Core REQUIRED) include_directories(${Qt5Core_INCLUDE_DIRS}) @endif # Add our dependencies to the include paths include_directories( "${CMAKE_SOURCE_DIR}/include" ${Boost_INCLUDE_DIRS} ${SCOPE_INCLUDE_DIRS} ) # Do not remove this line, its required for the correct functionality of the Ubuntu-SDK set(UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file") set(UBUNTU_PROJECT_TYPE "Scope" CACHE INTERNAL "Tells QtCreator this is a Scope project") # Important project paths set(CMAKE_INSTALL_PREFIX /) set(SCOPE_INSTALL_DIR "/%ClickHookName:l%") set(GETTEXT_PACKAGE "%ProjectName:l%") set(PACKAGE_NAME "%ProjectName:l%.%ClickDomain:l%") set(SCOPE_NAME "${PACKAGE_NAME}_%ClickHookName:l%") # If we need to refer to the scope's name or package in code, these definitions will help add_definitions(-DPACKAGE_NAME="${PACKAGE_NAME}") add_definitions(-DSCOPE_NAME="${SCOPE_NAME}") add_definitions(-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}") #This command figures out the target architecture and puts it into the manifest file execute_process( COMMAND dpkg-architecture -qDEB_HOST_ARCH OUTPUT_VARIABLE CLICK_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE ) configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json) # Install the click manifest install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION "/") install(FILES "%ClickHookName:l%.apparmor" DESTINATION "/") # Make these files show up in QtCreator file(GLOB_RECURSE _PO_FILES "po/*.po" "po/*.pot" ) add_custom_target(hidden_files ALL SOURCES manifest.json.in %ClickHookName:l%.apparmor data/${SCOPE_NAME}.ini.in po/POTFILES.in ${_PO_FILES} ) # Add our main directories add_subdirectory(src) add_subdirectory(data) add_subdirectory(po) # Set up the tests enable_testing() add_subdirectory(tests) add_custom_target( check ${CMAKE_CTEST_COMMAND} --force-new-ctest-process --output-on-failure ) ./share/qtcreator/templates/wizards/ubuntu/scope/0000755000015600001650000000000012705421114022314 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/wizard.xml0000644000015600001650000001337412705421114024346 0ustar jenkinsjenkins ../share/ubuntu.png A simple C++ based Unity Scope Unity Scope (Current) Ubuntu Click package parameters Nickname: Maintainer: Scope name: Dummy Framework Framework: Scope with network access Confinement type: Qt scope using HTTP+JSON API Qt scope using HTTP+XML API Pure C++ scope using HTTP+JSON API Empty scope Template type: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/scope/manifest.json.in0000644000015600001650000000103512705421114025421 0ustar jenkinsjenkins{ @if "%ContentType%".substring(0, "network".length) === "network" "description": "A simple Unity scope that accesses the network", @else "description": "A simple Unity scope that accesses local content", @endif "maintainer": "%ClickMaintainer%", "architecture": "@CLICK_ARCH@", "name": "%ProjectName:l%.%ClickDomain:l%", "title": "%ProjectName:l%", "framework" : "%ClickFrameworkVersion%", "hooks": { "%ClickHookName:l%": { "scope": "%ClickHookName:l%", "apparmor": "%ClickHookName:l%.apparmor" } } } ./share/qtcreator/templates/wizards/ubuntu/scope/tests/0000755000015600001650000000000012705421114023456 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/0000755000015600001650000000000012705421114024764 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/0000755000015600001650000000000012705421114026572 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/London,uk.json0000644000015600001650000000371612705421114031341 0ustar jenkinsjenkins{"cod":"200","message":0.1117,"city":{"id":2643743,"name":"London","coord":{"lon":-0.12574,"lat":51.50853},"country":"GB","population":0,"sys":{"population":0}},"cnt":7,"list":[{"dt":1407412800,"temp":{"day":23.33,"min":18.84,"max":25.09,"night":18.84,"eve":24.17,"morn":20.76},"pressure":1018.23,"humidity":83,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.87,"deg":295,"clouds":12,"rain":0.5},{"dt":1407499200,"temp":{"day":19.44,"min":15.54,"max":20.86,"night":15.54,"eve":18.28,"morn":16.21},"pressure":1010.57,"humidity":97,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":4.01,"deg":143,"clouds":92,"rain":8},{"dt":1407585600,"temp":{"day":18.78,"min":13.19,"max":19.58,"night":13.19,"eve":18.95,"morn":14.03},"pressure":1010.78,"humidity":83,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":5.46,"deg":281,"clouds":32},{"dt":1407672000,"temp":{"day":18.09,"min":13.46,"max":18.09,"night":13.46,"eve":16.58,"morn":13.91},"pressure":1002.33,"humidity":67,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.68,"deg":162,"clouds":92,"rain":4},{"dt":1407758400,"temp":{"day":17.38,"min":15.39,"max":17.38,"night":16.3,"eve":17.03,"morn":15.39},"pressure":1008.2,"humidity":0,"weather":[{"id":502,"main":"Rain","description":"heavy intensity rain","icon":"10d"}],"speed":13.52,"deg":245,"clouds":74,"rain":12.63},{"dt":1407844800,"temp":{"day":18.44,"min":16.17,"max":18.44,"night":16.83,"eve":18.02,"morn":16.17},"pressure":1009.94,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.93,"deg":251,"clouds":60,"rain":5.25},{"dt":1407931200,"temp":{"day":18.8,"min":16.67,"max":19.02,"night":16.77,"eve":19.02,"morn":16.67},"pressure":1013.17,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":9.88,"deg":275,"clouds":85,"rain":1.35}]} ./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/London,uk.xml0000644000015600001650000000751512705421114031171 0ustar jenkinsjenkins London GB 0.1469 ./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/Manchester,uk.xml0000644000015600001650000000743012705421114032025 0ustar jenkinsjenkins Manchester GB 0.0021 ./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/Manchester,uk.json0000644000015600001650000000367412705421114032204 0ustar jenkinsjenkins{"cod":"200","message":0.0056,"city":{"id":2643123,"name":"Manchester","coord":{"lon":-2.23743,"lat":53.480949},"country":"GB","population":0,"sys":{"population":0}},"cnt":7,"list":[{"dt":1407412800,"temp":{"day":17.91,"min":11.96,"max":18.83,"night":11.96,"eve":18.01,"morn":16.13},"pressure":1014.82,"humidity":77,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":3.32,"deg":303,"clouds":0},{"dt":1407499200,"temp":{"day":18.59,"min":12.33,"max":18.59,"night":12.33,"eve":14.55,"morn":14.69},"pressure":1008.22,"humidity":79,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":3.46,"deg":163,"clouds":92,"rain":11},{"dt":1407585600,"temp":{"day":15.8,"min":10.79,"max":17.11,"night":10.79,"eve":16.89,"morn":13.19},"pressure":1005.76,"humidity":92,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"speed":5.61,"deg":279,"clouds":56},{"dt":1407672000,"temp":{"day":15.29,"min":12.21,"max":15.98,"night":12.41,"eve":15.98,"morn":12.21},"pressure":995.36,"humidity":95,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.75,"deg":137,"clouds":92,"rain":5},{"dt":1407758400,"temp":{"day":15.73,"min":12.99,"max":15.73,"night":13.8,"eve":15.23,"morn":12.99},"pressure":990.16,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":9.02,"deg":239,"clouds":99,"rain":1.15},{"dt":1407844800,"temp":{"day":17.64,"min":14.08,"max":17.64,"night":14.08,"eve":14.92,"morn":14.12},"pressure":992.52,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.36,"deg":240,"clouds":45,"rain":4.84},{"dt":1407931200,"temp":{"day":14.35,"min":13.37,"max":15.52,"night":13.77,"eve":15.52,"morn":13.37},"pressure":999.39,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":7.79,"deg":285,"clouds":92,"rain":5.98}]} ./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/server.py0000755000015600001650000000336312705421114026654 0ustar jenkinsjenkins#!/usr/bin/env python3 import http.server import os import socketserver import sys from urllib.parse import urlparse,parse_qs def read_file(path): file = os.path.join(os.path.dirname(__file__), path) if os.path.isfile(file): with open(file, 'r') as fp: content = fp.read() else: content = '' return content class MyRequestHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): sys.stderr.write("GET: %s\n" % self.path) sys.stderr.flush() parse = urlparse(self.path) path = parse.path query = parse_qs(parse.query) if path == '/data/2.5/weather': self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() mode = 'json' if 'mode' in query: mode = query['mode'][0] self.wfile.write(bytes(read_file('weather/%s.%s' % (query['q'][0], mode)), 'UTF-8')) elif path == '/data/2.5/forecast/daily': self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() mode = 'json' if 'mode' in query: mode = query['mode'][0] self.wfile.write(bytes(read_file('forecast/%s.%s' % (query['q'][0], mode)), 'UTF-8')) else: self.send_response(404) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(bytes('ERROR', 'UTF-8')) if __name__ == "__main__": Handler = MyRequestHandler httpd = socketserver.TCPServer(("127.0.0.1", 0), Handler) sys.stdout.write('%d\n' % httpd.server_address[1]) sys.stdout.flush() httpd.serve_forever() ./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/0000755000015600001650000000000012705421114026423 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/London,uk.json0000644000015600001650000000064212705421114031165 0ustar jenkinsjenkins{"coord":{"lon":-0.13,"lat":51.51},"sys":{"type":1,"id":5091,"message":0.29,"country":"GB","sunrise":1407386057,"sunset":1407440289},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"base":"cmc stations","main":{"temp":21.83,"pressure":1014,"humidity":53,"temp_min":20,"temp_max":24},"wind":{"speed":1.5,"deg":0},"clouds":{"all":20},"dt":1407408276,"id":2643743,"name":"London","cod":200}./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/London,uk.xml0000644000015600001650000000123712705421114031015 0ustar jenkinsjenkins GB ./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/Manchester,uk.xml0000644000015600001650000000121212705421114031646 0ustar jenkinsjenkins GB ./share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/Manchester,uk.json0000644000015600001650000000071312705421114032024 0ustar jenkinsjenkins{"coord":{"lon":-2.24,"lat":53.48},"sys":{"type":1,"id":5060,"message":0.2423,"country":"GB","sunrise":1407386141,"sunset":1407441219},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"base":"cmc stations","main":{"temp":17.35,"pressure":1016,"humidity":77,"temp_min":17,"temp_max":18},"wind":{"speed":2.6,"deg":20,"var_beg":330,"var_end":80},"clouds":{"all":40},"dt":1407408600,"id":2643123,"name":"Manchester","cod":200} ./share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/0000755000015600001650000000000012705421114024435 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/test-scope.py0000755000015600001650000003022612705421114027103 0ustar jenkinsjenkins#!/usr/bin/env python3 from scope_harness import ( CategoryListMatcher, CategoryListMatcherMode, CategoryMatcher, Parameters, ResultMatcher, ScopeHarness ) from scope_harness.testing import * import unittest, sys, os from subprocess import Popen, PIPE @if "%ContentType%".substring(0, "network".length) === "network" class AppsTest (ScopeHarnessTestCase): @classmethod def setUpClass(cls): cls.process = Popen(["/usr/bin/python3", FAKE_SERVER], stdout=PIPE) port = cls.process.stdout.readline().decode("utf-8").rstrip('\n') os.environ["NETWORK_SCOPE_APIROOT"] = "http://127.0.0.1:" + port @classmethod def tearDownClass(cls): cls.process.terminate() def start_harness(self): self.harness = ScopeHarness.new_from_scope_list(Parameters([SCOPE_INI])) self.view = self.harness.results_view self.view.active_scope = SCOPE_NAME @if "%ContentType%" == "network-netcpp-qxml" def test_surfacing_results(self): self.start_harness() self.view.search_query = '' match = CategoryListMatcher() \ .has_exactly(2) \ .mode(CategoryListMatcherMode.BY_ID) \ .category(CategoryMatcher("current") \ .title("London, GB") \ .has_at_least(1) \ .result(ResultMatcher("2643743") \ .title("21.8°C") \ .art("http://openweathermap.org/img/w/02d.png") \ .subtitle("few clouds") ) ) \ .category(CategoryMatcher("forecast") \ .title("7 day forecast") \ .has_at_least(7) \ .result(ResultMatcher("1000000") \ .title("25.1°C to 18.8°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("light rain") ) \ .result(ResultMatcher("1000001") \ .title("20.9°C to 15.5°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("1000002") \ .title("19.6°C to 13.2°C") \ .art("http://openweathermap.org/img/w/03d.png") \ .subtitle("scattered clouds") ) \ .result(ResultMatcher("1000003") \ .title("18.1°C to 13.5°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("1000004") \ .title("17.4°C to 15.4°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("heavy intensity rain") ) \ .result(ResultMatcher("1000005") \ .title("18.4°C to 16.2°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("1000006") \ .title("19°C to 16.7°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("light rain") ) ) \ .match(self.view.categories) self.assertMatchResult(match) @else def test_surfacing_results(self): self.start_harness() self.view.search_query = '' match = CategoryListMatcher() \ .has_exactly(2) \ .mode(CategoryListMatcherMode.BY_ID) \ .category(CategoryMatcher("current") \ .title("London, GB") \ .has_at_least(1) \ .result(ResultMatcher("5091") \ .title("21.8°C") \ .art("http://openweathermap.org/img/w/02d.png") \ .subtitle("few clouds") ) ) \ .category(CategoryMatcher("forecast") \ .title("7 day forecast") \ .has_at_least(7) \ .result(ResultMatcher("500") \ .title("25.1°C to 18.8°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("light rain") ) \ .result(ResultMatcher("501") \ .title("20.9°C to 15.5°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("802") \ .title("19.6°C to 13.2°C") \ .art("http://openweathermap.org/img/w/03d.png") \ .subtitle("scattered clouds") ) \ .result(ResultMatcher("501") \ .title("18.1°C to 13.5°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("502") \ .title("17.4°C to 15.4°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("heavy intensity rain") ) \ .result(ResultMatcher("501") \ .title("18.4°C to 16.2°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("500") \ .title("19°C to 16.7°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("light rain") ) ) \ .match(self.view.categories) self.assertMatchResult(match) @endif @if "%ContentType%" == "network-netcpp-qxml" def test_search_results(self): self.start_harness() self.view.search_query = 'Manchester,uk' match = CategoryListMatcher() \ .has_exactly(2) \ .mode(CategoryListMatcherMode.BY_ID) \ .category(CategoryMatcher("current") \ .title("Manchester, GB") \ .has_at_least(1) \ .result(ResultMatcher("2643123") \ .title("17.4°C") \ .art("http://openweathermap.org/img/w/03d.png") \ .subtitle("scattered clouds") ) ) \ .category(CategoryMatcher("forecast") \ .title("7 day forecast") \ .has_at_least(7) \ .result(ResultMatcher("1000000") \ .title("18.8°C to 12°C") \ .art("http://openweathermap.org/img/w/01d.png") \ .subtitle("sky is clear") ) \ .result(ResultMatcher("1000001") \ .title("18.6°C to 12.3°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("1000002") \ .title("17.1°C to 10.8°C") \ .art("http://openweathermap.org/img/w/04d.png") \ .subtitle("broken clouds") ) \ .result(ResultMatcher("1000003") \ .title("16°C to 12.2°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("1000004") \ .title("15.7°C to 13°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("light rain") ) \ .result(ResultMatcher("1000005") \ .title("17.6°C to 14.1°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("1000006") \ .title("15.5°C to 13.4°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) ) \ .match(self.view.categories) self.assertMatchResult(match) @else def test_search_results(self): self.start_harness() self.view.search_query = 'Manchester,uk' match = CategoryListMatcher() \ .has_exactly(2) \ .mode(CategoryListMatcherMode.BY_ID) \ .category(CategoryMatcher("current") \ .title("Manchester, GB") \ .has_at_least(1) \ .result(ResultMatcher("5060") \ .title("17.4°C") \ .art("http://openweathermap.org/img/w/03d.png") \ .subtitle("scattered clouds") ) ) \ .category(CategoryMatcher("forecast") \ .title("7 day forecast") \ .has_at_least(7) \ .result(ResultMatcher("800") \ .title("18.8°C to 12°C") \ .art("http://openweathermap.org/img/w/01d.png") \ .subtitle("sky is clear") ) \ .result(ResultMatcher("501") \ .title("18.6°C to 12.3°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("803") \ .title("17.1°C to 10.8°C") \ .art("http://openweathermap.org/img/w/04d.png") \ .subtitle("broken clouds") ) \ .result(ResultMatcher("501") \ .title("16°C to 12.2°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("500") \ .title("15.7°C to 13°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("light rain") ) \ .result(ResultMatcher("501") \ .title("17.6°C to 14.1°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) \ .result(ResultMatcher("501") \ .title("15.5°C to 13.4°C") \ .art("http://openweathermap.org/img/w/10d.png") \ .subtitle("moderate rain") ) ) \ .match(self.view.categories) self.assertMatchResult(match) @endif if __name__ == '__main__': SCOPE_NAME = sys.argv[1] SCOPE_INI = sys.argv[2] FAKE_SERVER = sys.argv[3] unittest.main(argv = sys.argv[:1]) @elsif "%ContentType%" == "empty" class AppsTest (ScopeHarnessTestCase): def start_harness(self): self.harness = ScopeHarness.new_from_scope_list(Parameters([SCOPE_INI])) self.view = self.harness.results_view self.view.active_scope = SCOPE_NAME def test_surfacing_results(self): self.start_harness() self.view.search_query = '' match = CategoryListMatcher() \ .has_exactly(1) \ .mode(CategoryListMatcherMode.BY_ID) \ .category(CategoryMatcher("results") \ .title("2 results") \ .has_at_least(2) \ .result(ResultMatcher("uri") \ .title("default") \ .art("art.png") \ .subtitle("subtitle") ) \ .result(ResultMatcher("uri2") \ .title("default") \ .art("art2.png") \ .subtitle("subtitle2") ) ) \ .match(self.view.categories) self.assertMatchResult(match) def test_search_results(self): self.start_harness() self.view.search_query = 'test' match = CategoryListMatcher() \ .has_exactly(1) \ .mode(CategoryListMatcherMode.BY_ID) \ .category(CategoryMatcher("results") \ .title("2 results") \ .has_at_least(2) \ .result(ResultMatcher("uri") \ .title("test") \ .art("art.png") \ .subtitle("subtitle") ) \ .result(ResultMatcher("uri2") \ .title("test") \ .art("art2.png") \ .subtitle("subtitle2") ) ) \ .match(self.view.categories) self.assertMatchResult(match) if __name__ == '__main__': SCOPE_NAME = sys.argv[1] SCOPE_INI = sys.argv[2] unittest.main(argv = sys.argv[:1]) @endif ./share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/CMakeLists.txt0000644000015600001650000000103612705421114027175 0ustar jenkinsjenkins # This command ensures that test-scope.py has execute permission execute_process(COMMAND chmod +x "${CMAKE_CURRENT_SOURCE_DIR}/test-scope.py") # Register the test with CTest add_test( scope-unit-tests @if "%ContentType%" == "empty" "${CMAKE_CURRENT_SOURCE_DIR}/test-scope.py" "${SCOPE_NAME}" "${TEST_SCOPE_DIRECTORY}/${SCOPE_NAME}.ini" @elsif "%ContentType%".substring(0, "network".length) === "network" "${CMAKE_CURRENT_SOURCE_DIR}/test-scope.py" "${SCOPE_NAME}" "${TEST_SCOPE_DIRECTORY}/${SCOPE_NAME}.ini" "${FAKE_SERVER}" @endif ) ./share/qtcreator/templates/wizards/ubuntu/scope/tests/CMakeLists.txt0000644000015600001650000000215412705421114026220 0ustar jenkinsjenkins # Google Mock unfortunately has to be compiled from source include(FindGMock) @if "%ContentType%".substring(0, "network".length) === "network" # We need process-cpp to launch the python test server pkg_check_modules( TEST process-cpp REQUIRED ) @endif # Include our test library headers include_directories( ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS} ${TEST_INCLUDE_DIRS} ) # Where to find the scope ini file and .so set(TEST_SCOPE_DIRECTORY "${CMAKE_BINARY_DIR}/src") file(GLOB_RECURSE TEST_FIXTURES @if "%ContentType%" == "empty" "unit/*.py" @elsif "%ContentType%" == "network-netcpp-json" "server/*.json" "*.py" @elsif "%ContentType%" == "network-netcpp-qjson" "server/*.json" "*.py" @elsif "%ContentType%" == "network-netcpp-qxml" "server/*.xml" "*.py" @endif ) # Make this file show up in QtCreator add_custom_target(hidden_test_fixtures ALL SOURCES ${TEST_FIXTURES} ) @if "%ContentType%".substring(0, "network".length) === "network" # Where to find the test server binary set(FAKE_SERVER "${CMAKE_CURRENT_SOURCE_DIR}/server/server.py") @endif # Add the unit tests add_subdirectory(unit) ./share/qtcreator/templates/wizards/ubuntu/scope/src/0000755000015600001650000000000012705421114023103 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/src/scope.cpp0000644000015600001650000000310512705421114024717 0ustar jenkinsjenkins#include #include #include #include #include #include #include namespace sc = unity::scopes; using namespace std; void Scope::start(string const&) { config_ = make_shared(); setlocale(LC_ALL, ""); string translation_directory = ScopeBase::scope_directory() + "/../share/locale/"; bindtextdomain(GETTEXT_PACKAGE, translation_directory.c_str()); @if "%ContentType%".substring(0, "network".length) === "network" // Under test we set a different API root char *apiroot = getenv("NETWORK_SCOPE_APIROOT"); if (apiroot) { config_->apiroot = apiroot; } @endif } void Scope::stop() { } sc::SearchQueryBase::UPtr Scope::search(const sc::CannedQuery &query, const sc::SearchMetadata &metadata) { // Boilerplate construction of Query return sc::SearchQueryBase::UPtr(new Query(query, metadata, config_)); } sc::PreviewQueryBase::UPtr Scope::preview(sc::Result const& result, sc::ActionMetadata const& metadata) { // Boilerplate construction of Preview return sc::PreviewQueryBase::UPtr(new Preview(result, metadata)); } #define EXPORT __attribute__ ((visibility ("default"))) // These functions define the entry points for the scope plugin extern "C" { EXPORT unity::scopes::ScopeBase* // cppcheck-suppress unusedFunction UNITY_SCOPE_CREATE_FUNCTION() { return new Scope(); } EXPORT void // cppcheck-suppress unusedFunction UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) { delete scope_base; } } ./share/qtcreator/templates/wizards/ubuntu/scope/src/client.h0000644000015600001650000001010112705421114024523 0ustar jenkinsjenkins#ifndef CLIENT_H_ #define CLIENT_H_ #include #include #include #include #include @if "%ContentType%".substring(0, "network".length) === "network" #include #include @if "%ContentType%" == "network-netcpp-json" namespace Json { class Value; } @elsif "%ContentType%" == "network-netcpp-qjson" #include @elsif "%ContentType%" == "network-netcpp-qxml" #include @endif @endif /** * Provide a nice way to access the HTTP API. * * We don't want our scope's code to be mixed together with HTTP and JSON handling. */ class Client { public: /** * Client configuration */ struct Config { typedef std::shared_ptr Ptr; // The root of all API request URLs std::string apiroot { "http://api.openweathermap.org" }; // The custom HTTP user agent string for this library std::string user_agent { "example-network-scope 0.1; (foo)" }; }; @if "%ContentType%" == "empty" /** * Result struct */ struct Result { std::string uri; std::string title; std::string art; std::string subtitle; std::string description; }; /** * A list of weather information */ typedef std::deque ResultList; @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Information about a City */ struct City { unsigned int id; std::string name; std::string country; }; /** * Temperature information for a day. */ struct Temp { double max; double min; double cur; }; /** * Weather information for a day. */ struct Weather { unsigned int id; std::string main; std::string description; std::string icon; Temp temp; }; /** * A list of weather information */ typedef std::deque WeatherList; /** * Weather information about the current day */ struct Current { City city; Weather weather; }; /** * Forecast information about a city */ struct Forecast { City city; WeatherList weather; }; @endif Client(Config::Ptr config); virtual ~Client() = default; @if "%ContentType%" == "empty" /** * Search for results */ virtual ResultList search(const std::string &query); @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Get the current weather for the specified location */ virtual Current weather(const std::string &query); /** * Get the weather forecast for the specified location and duration */ virtual Forecast forecast_daily(const std::string &query, unsigned int days = 7); @endif /** * Cancel any pending queries (this method can be called from a different thread) */ virtual void cancel(); virtual Config::Ptr config(); protected: @if "%ContentType%" == "network-netcpp-json" void get(const core::net::Uri::Path &path, const core::net::Uri::QueryParameters ¶meters, Json::Value &root); @elsif "%ContentType%" == "network-netcpp-qjson" void get(const core::net::Uri::Path &path, const core::net::Uri::QueryParameters ¶meters, QJsonDocument &root); @elsif "%ContentType%" == "network-netcpp-qxml" void get(const core::net::Uri::Path &path, const core::net::Uri::QueryParameters ¶meters, QXmlStreamReader &reader); @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Progress callback that allows the query to cancel pending HTTP requests. */ core::net::http::Request::Progress::Next progress_report( const core::net::http::Request::Progress& progress); @endif /** * Hang onto the configuration information */ Config::Ptr config_; /** * Thread-safe cancelled flag */ std::atomic cancelled_; }; #endif // CLIENT_H_ ./share/qtcreator/templates/wizards/ubuntu/scope/src/query.cpp0000644000015600001650000001764012705421114024764 0ustar jenkinsjenkins#include #include #include #include #include #include #include #include #include #include namespace sc = unity::scopes; using namespace std; @if "%ContentType%" == "empty" /** * Define the layout for the results. * * The icon size is medium, and ask for the card layout * itself to be horizontal. I.e. the text will be placed * next to the image. */ const static string CATEGORY_TEMPLATE = R"( { "schema-version": 1, "template": { "category-layout": "grid", "card-layout": "horizontal", "card-size": "medium" }, "components": { "title": "title", "art" : { "field": "art" }, "subtitle": "subtitle" } } )"; @endif @if "%ContentType%".substring(0, "network".length) === "network" /** * Define the larger "current weather" layout. * * The icons are larger. */ const static string CURRENT_TEMPLATE = R"( { "schema-version": 1, "template": { "category-layout": "grid", "card-size": "medium" }, "components": { "title": "title", "art" : { "field": "art" }, "subtitle": "subtitle" } } )"; /** * Define the layout for the forecast results. * * The icon size is small, and ask for the card layout * itself to be horizontal. I.e. the text will be placed * next to the image. */ const static string FORECAST_TEMPLATE = R"( { "schema-version": 1, "template": { "category-layout": "grid", "card-layout": "horizontal", "card-size": "small" }, "components": { "title": "title", "art" : { "field": "art" }, "subtitle": "subtitle" } } )"; @endif Query::Query(const sc::CannedQuery &query, const sc::SearchMetadata &metadata, Client::Config::Ptr config) : sc::SearchQueryBase(query, metadata), client_(config) { } void Query::cancelled() { client_.cancel(); } @if "%ContentType%" == "empty" void Query::run(sc::SearchReplyProxy const& reply) { try { // Start by getting information about the query const sc::CannedQuery &query(sc::SearchQueryBase::query()); // Get the query string string query_string = query.query_string(); // the Client is the helper class that provides the results // without mixing APIs and scopes code. // Add your code to retreive xml, json, or any other kind of result // in the client. Client::ResultList results; if (query_string.empty()) { // If the string is empty, pick a default results = client_.search("default"); } else { // otherwise, use the search string results = client_.search(query_string); } // Register a category auto cat = reply->register_category("results", _("1 result", "%d results", results.size()), "", sc::CategoryRenderer(CATEGORY_TEMPLATE)); for (const auto &result : results) { sc::CategorisedResult res(cat); // We must have a URI res.set_uri(result.uri); res.set_title(result.title); // Set the rest of the attributes, art, description, etc res.set_art(result.art); res["subtitle"] = result.subtitle; res["description"] = result.description; // Push the result if (!reply->push(res)) { // If we fail to push, it means the query has been cancelled. // So don't continue; return; } } } catch (domain_error &e) { // Handle exceptions being thrown by the client API cerr << e.what() << endl; reply->error(current_exception()); } } @endif @if "%ContentType%".substring(0, "network".length) === "network" void Query::run(sc::SearchReplyProxy const& reply) { try { // Start by getting information about the query const sc::CannedQuery &query(sc::SearchQueryBase::query()); // Get the query string string query_string = query.query_string(); /// Populate current weather category // the Client is the helper class that provides the results // without mixing APIs and scopes code. // Add your code to retreive xml, json, or any other kind of result // in the client. Client::Current current; if (query_string.empty()) { // If the string is empty, get the current weather for London current = client_.weather("London,uk"); } else { // otherwise, get the current weather for the search string current = client_.weather(query_string); } // Build up the description for the city stringstream ss(stringstream::in | stringstream::out); ss << current.city.name << ", " << current.city.country; // Register a category for the current weather, with the title we just built auto location_cat = reply->register_category("current", ss.str(), "", sc::CategoryRenderer(CURRENT_TEMPLATE)); { // Create a single result for the current weather category sc::CategorisedResult res(location_cat); // We must have a URI res.set_uri(to_string(current.city.id)); // Build up the description for the current weather stringstream ss(stringstream::in | stringstream::out); ss << setprecision(3) << current.weather.temp.cur; ss << "°C"; res.set_title(ss.str()); // Set the rest of the attributes, art, description, etc res.set_art(current.weather.icon); res["subtitle"] = current.weather.description; res["description"] = "A description of the result"; // Push the result if (!reply->push(res)) { // If we fail to push, it means the query has been cancelled. // So don't continue; return; } } /// Populate weather forecast category Client::Forecast forecast; if (query_string.empty()) { // If there is no search string, get the forecast for London forecast = client_.forecast_daily("London,uk"); } else { // otherwise, get the forecast for the search string forecast = client_.forecast_daily(query_string); } // Register a category for the forecast auto forecast_cat = reply->register_category("forecast", _("7 day forecast"), "", sc::CategoryRenderer(FORECAST_TEMPLATE)); // For each of the forecast days for (const auto &weather : forecast.weather) { // Create a result sc::CategorisedResult res(forecast_cat); // We must have a URI res.set_uri(to_string(weather.id)); // Build the description for the result stringstream ss(stringstream::in | stringstream::out); ss << setprecision(3) << weather.temp.max; ss << "°C to "; ss << setprecision(3) << weather.temp.min; ss << "°C"; res.set_title(ss.str()); // Set the rest of the attributes res.set_art(weather.icon); res["subtitle"] = weather.description; res["description"] = "A description of the result"; // Push the result if (!reply->push(res)) { // If we fail to push, it means the query has been cancelled. // So don't continue; return; } } } catch (domain_error &e) { // Handle exceptions being thrown by the client API cerr << e.what() << endl; reply->error(current_exception()); } } @endif ./share/qtcreator/templates/wizards/ubuntu/scope/src/data/0000755000015600001650000000000012705421114024014 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/src/data/displayName-settings.ini.in0000644000015600001650000000103112705421114031221 0ustar jenkinsjenkins@if "%ContentType%".substring(0, "network".length) === "network" # Below are some example settings. You can access your scope's # settings by calling settings() from the Query::run() method. # E.g. auto location = settings().at("location").get_string(); #[location] #type = string #defaultValue = London,uk #_displayName = Default Location #[units] #type = list #_displayName = Temperature Units #_displayValues = Metric;Imperial #defaultValue = 0 #[forecast] #type = boolean #defaultValue = true #_displayName = Show Forecast @endif ./share/qtcreator/templates/wizards/ubuntu/scope/src/data/displayName.ini.in0000644000015600001650000000032112705421114027364 0ustar jenkinsjenkins[ScopeConfig] _DisplayName=%ProjectName:c% Scope _Description=This is a %ProjectName:c% scope Art=screenshot.png Author=Firstname Lastname Icon=icon.png Keywords=weather [Appearance] PageHeader.Logo=logo.png ./share/qtcreator/templates/wizards/ubuntu/scope/src/data/icon.png0000644000015600001650000003305012705421114025453 0ustar jenkinsjenkinsPNG  IHDRxbKGD pHYs  tIME  93Be IDATx_Lw"28S~ ź3$$@={pQܣpzˏFS׮kYu]{z7 t~lI6 \B;i 󑘶;|?#xʏhzw,֯_??≤pdz55w H]_L'_oeBA쟱p5Owkݎzք@r߿G=T +N>ٿ@ b`}30p@FezV$@;H-b4?a [_tN0wHY-3:#ё~3a{ѪG&hI=<]4;W}UڢU ج?n4u;ؤӢ̏HV: <:~hE @@KK:w%}DǏ,A2vh -] юր. @RӴ,"(d{~XYO4 @?~G)v2, ?_Qd&>Z6 ;~&!<4`P 2*&@:)2K'i8*}rtԿk4kϺ~`}y% 6+7Eئ$- ,S+`Ϣl{t.EQR炧k})AH)6B:t@Z$~s9O$39Tt@F)@]5if οGLxf@xD;٠0 }P@Z;10A?+B;*Ti{^x~#ZjahPZ O@9F+9$GmƄB`@w;4a0@[h &"EJ\)=oi-@?-6)5yBVT ?0j_vJZ (q- @@Nu=? RR{^7؇4slt{.V' W A翁>V&;4~1TLRtt@cWO>j>x]:д*xT?:vc:I*;+д?LGϪSm5m+q쭏ѥ G}DW9#mGM]HR!QR3-F}}Z.* :H\iZ&wzkVRqm]I=щ +b)u&9A Kd*pW٩#w,Q6>c|û'a?R[Tm8F3xUR7mjce_+5uj[V^&`?@:%#3p ژKB{x`$ă>; ;`eIh [iu Uߍ K_WX`mld+o7éңgU~_r>mq8Οҿ+Uv*۾Ts9U߈aӯY\f;F. diq\/kymkOy <8+̎_"y ߈˷,QM޸l/4΍|Am4C#lPS+ƭ_ i`23?"5بB2Al=+`|!`Z1hǭ L$ h4&o^62ͮqXԣ>#rMw2q/(2/ i;1x)MLl]J\qg5Lw+ܸ]OEpu:ʶ/XoN^ҞK < >7éwUzlR +;s-WJh3GRpDߞ;B8_t2X6*mОKU֎:Mww${雯TTSG`BJ5{3҂fvh%R?RyEiЧ?'Wܤl;@@ ?Ȍ]'.jSL*rp{gKZKфf/Ӧ/.] 0}K+~}i(4["fgfDZ^=ݳ{^- &<*\U~E =4:ƄCߗ4?Ч;mZܓL^ hN?<S? fc3?kTv '-G䁄;JmFV'9*S*RX^j)8^-U[UzJk'o\NjIX"!w61`.VMé3KS~yeV@q(c9}'h{fc٣@;=*&k+*BK |R^)忪]/d<\*`oئ2NsxUTMMk)8 _䳤T@%1?쭏ul{˷Un{R]`  ѿ*^7gufl7;;U3ִ3Gm3 / \7sST詖WTju&%|܃cw@ мGQ23 Q,;sh)8پN j7M|QSzУ>6JC%5f ?'Y%<|2~-84ਖZ~΄˼ UsTҎu/B@ʵy&ޠx~Rl$:(W4Dj`C C0 ,-KOds zUv 3 >?"OK :l2<ĴM*OrIz{:РV!-",G7c:WTݷ4~MR xNTz矊uHvB`eIw iahPTTUJ*L(h}[S|Iآw۟{o$3!ګ\.G4߫-|OJQېv*JOHgv(׫-Yd'_wFzlݎ𷪸A6nLH55@ _Kǻd%sdpJ?䳧~n;( Tqm\Yy΀nG \EH1 egN3 [mGszt:[d@wYR3-HO>3n2Z2N[,+P˿}ڪ9i|4Cp@v;$fmAy\oBU?@Gۋo*_&|׾%dsEd7sN=|WEn:Aw4reٿG{Q M^st@n~IU*=j6j9}'YX7#Z XT_ ,WY]ϾI;oBSO6$L߉ RHSmثpH 72I׾wZ q)=z2K˶xɛW=Jn`A^rQ3MwN[ iyZ_J7.[暷Tf @0\HFsm%71#9-qGMJ6qOe>mգ>vۂ>62v\dU@C^Xp??fSl")=:rA{L$`CѤ[OK$>2}9yƯЧ_CM#= U#Iq;iIaPDMÓ3`n! Iy!z0TQT5zB`@L,7 h {cefb/UGOV&cw'PH}=R@zP&L]@g$|o0 ;@g$  9M{ B@;FH@:?wGS ฑgPH\wM _=1بndij/hu_}0?gQ&Z dh}I2yqv)C 4A|J 7{ &ֺq<0Dk&N[ iC[РqJA3aD)+{[iXȠK}L+{!{z0R>s81<7 $Y44:߫>n UFn^`" $+:uHبQg' w 6T߆&o^6f`^ĭ& bmaPI4Ě[Z OiÜkO{ 24^vߜ;jpn+2?بoQz`e7aʄs `+m%.!h`;f?`qhИY 37_=eF-p3dI F5i>)pW]O=Z i10šA3VS \Ce9}+DYx@f@éF9jTTS|{p-4}C}I;5! |OjY OiV!'T6c%y\9}-I[詎(oMxRwT讀+3!rN^Kg?MңgUJKۨ\wە_^y_Fwln2% |q*oMi ';pH9Z0`)KUrI?䳌ڷ*;s-3)|/57{o!Xb/5EJU[kswyEKva}fTXw0Ez{wUzJh'0)FHѴxWTr3@@lj61?Ч4vsgBd"SlƘW6ٸ6sX~>T,%rvع} ?0!F4tGj3[>}&$M HtdQ^)+T`Ӧ_7s#QGL'kY`ʺU͔cH} D-G~ $ָ(%o{zǾ n7  ek-SZt6d!m»_ +1mܴB`ui8`onRw =T@@;j容]iX 潽 `QM޼BC ΂m5l l`e>6>{ `{)^KM`` j2!`V.wb5 p6p+ta?Zޘ+@c"NȊ؝ϊ7 -oj TllCsRo~s_<cG~CQW?|;_jxjkA=0݊-s9.UdRp$웑ﭵAta =x`S!]m)O@6BΓl_بe&twl5چ~9{NtM<0:=Dݷz8ud$4`_3 2a/pW|>r"I ?Ԉ+]C ⽿5_VfBMpY jCwp]-`ңgθx&>Ҕ%m%.U|lRsF^Rb@QIDATBM+j%?! *chLwb>vHL 4so{Op*Xv7)/S3O T`~%q0HsECn*! 2Q!%!Uǭ}̉7_ 1 |SanƇwY"띿$M޼̺ݏ\iĘ^$%iR\`l׹a2JÎ3)TO>crNnkՠ3j 7~cbqvN\?Xzoi~ar˖m%.1G '),q+3!F CNSFV|̍Kwzk |Ck aIX|+_[`1; L@5@F-* wq(=zV/~nde&񫧸ɣJÏL*›+ ;gQL)oG4 W3}U`˷#O[l rW5}Z"9)?xk[ h&<+Reg,Tw7Aimk 6h׉/}*j%i}Y )qxYޏh ؚw6q-; >"Te߽ۗQoséF|',o3ߞ;BWiʧ 5 &ˆ)WTfIv$8Qo#TB kmPqm%'mdm:FT*+JgYt&o\N׽|vhqhШ~QMm;[&h[7@!$%]~YVF+;s-+3!jnW} y UQS:KOB`@+Rェk}oĚؔ(9ؤ2n2SX{؈Z Om9U{WRJ*#^dtz?.Ev|ְ^գݞK=?a98w!$oM>̹N~=+3!=x*tiqΟux&#ְFX $uڟw²[6JM\ n<y ;j*~ s +ð$0=:oQnMwR^gJ-pWi&YK *ûŒ@+sIu@nGSVi84 Ⱥ b,C@2W9nA? 33qUvE" !`,YN+=vp6\7[x T@ fvHR$;k29z*?* `V$>0yNRQJY tA*dqhPOB` _Ŀچ3}w輏#Xh8 @ v8Uvc9~m2;IReDN/T~E Go`!0'\/k)i5usɯj/7?ULQN =šG{ۿ!ב̞%bCUVM޼9@@gdA~yJ\IMx=jmM=r1(DǙ^XDM?-YcIuE^: &&Jjg*pWq͕gc[;t'zrJ\sOOk6@@z_ +9Ħ?wU%! HY0vρ?q%ub;!v9uK4Hwl?IuE^^m1 =*=ʫx"*d,F*9D#l Ŀ%,u2UI?,-#RKh/6:+61,i? ;r,j]Ϳ7'(ikμ"X l]e`Tms3?f ^XCLg_Qޫ;jsv珢XlW{MNE5u?Jx)8پN˭xxPCY}DE$QʵwjQ]%^-57g"ȭ?曓:,`fpI/i-bOy UQSJWʹK-G5߫`ĭvqݏo@_|.Hz_<]|3`Pr4 A, WTn:ž}i10TZF}eM޼ eO/&U/I .:c5ql0KjOKR(Yu&R;? X OYyӓbT0k`  VXOl Y5LȮiT?&6I0\/޳%gȀb`Сl`I| vqt0 R!`e&d @!?T0"k{Lu޻cٕ `9Y#;N-Y`i [A N @F1!E}L qcMw߲6EOcϞJh%I @5Gk=@Lȼz. }.aF5~Q4}K:м)O9;0L5&Y+IҎ_B`@{Nיî}5 ,=`ɛW4}+Ʊ?cPuq}s`X=^iP J>x=Cy]&~`oB<1/W"k?)@ݎ>zh `C> x%oX4$^z_ |IYZ*6B.{gPD'AT}̏$ݜ90@5rp 3Q+zhX܅gQ?$\"4]a==ю}F *9$wx"M/"aI>wʶ/ @J$;CAO@O!us$mثM @{9wWY-C+m;wwb@9;?ww ZrƳ UG44E^rʎ+R~G!z$]pwǵwRpDͿ隠 |- }s93*@_?o6z /ۘԯ%+ >( H6E'쨩S~y%- C0&ԋUEMڤpˠ س?ؤm%kV 싎Yt:6:-@8WdR?D}xJ3w;3T9ȺJc:I*>[I?V>韎ԾEcu)@D3}K}k0` =tɛ2cFyFc%俾~6  dE&5KzAҁg;VS6^lg:QSgĵjqh0/pD; _C:Qh*vIq CVc";jMdUEt4yr҅I?$%EvǝNÏ|بoi .S&D-G}_GGo( V! ^gXezGTWOE}m%.뛠JvN1yJ:?r2?\p?ߏUCZ Qې+'{z*0fwR;#D hQ Vܘɚ~xͿ?UYo:6J# U*pWi{E,IACZ w6+;s-kfoic]k@} WbdͿ?0!N#e& 2`,mAE5uY Ml_FD:lN+ܞ;W6 kuV2f2`5@ȵIENDB`./share/qtcreator/templates/wizards/ubuntu/scope/src/data/logo.png0000644000015600001650000000563012705421114025466 0ustar jenkinsjenkinsPNG  IHDR@bKGD pHYs  tIME  : + %IDATxAlW^J v@vW |e4T7V9BV% .KMPCgUT+aNXsn09 la](=g3.? 3}6-Ե8x% IB4ÎՌ!^7䓅rEB,8CH*,KjENom Bi-6zl[YM]RWc=A=1 Bbph:!!D'K!Aشgse(8\Dl"ؙw\@_v7_aİ3[AK@1LCzbE b@iiXJ^L,3wJ"^~{˦L]PpHX`ȕǦˌn^~{+*6FcXU쫋ӎ6,+BzǻIشg/8ԇNNƳ˟[^x`Qv8)i !ql?zOg?xrn~=TBzM,3V\GMj~;>S,3\.2u-Y*B?JA2 ׼~'֧ǰR?i%:BOb# oh!V߿pyq0*:|1 !dmx]#x%C,;ɽb #20-o9xOg?0 Ԫ Hi!tOhBzxqJyq Ç}M3UnlưL',kTdme3!%p$~]Vz`'~< L85ɔ~l_n$SL}y )ϚV8ժ tYCBӵ.Nү:V9|ەp-Y((굟e뵭Zy:_QqjuZn]ݹ0MJ"pU *T0iX+m4kTkL < ȳ}qITaBa8_§sgecfX,[Om5Y(-=jÛF0?tغnڳ;>iU4\(y⢀v,>B"X\g_]lUt+ÇmF74!l?5w\hȁSϪnB91x2um?Qy+ #e3Û.fi*zDX=#qEL #N6E3#,VjU<9cܴYlil K5 {Gi?@3.:^{\UQs/n|j~vͪWoa\5%{dKΐVKԵd<ƶ.X{TUL]+r껗7u-J9Q's/ !`>Y(n^ujY㲦uuZ -2:k<p3jFExqklɹ$*z=*#/gN@ CUl}+rgܨ1A/PvlB~2L4ב0p *-7=:]qќXN0e`(qW^޿&6-D \WtqVߎdr:L3.:ϧ8j3g 䜂^أ`4-љ91h9|$[,级 q'ǘ«.ș|O{e/))Z;e!bkfr.eS"3!O\(8]X%i&hbܞElyĭVxt?ͪZ'A{8VӈVHmvBZ5 M-+iTwn,7k$3ICpd aV.dv>(A l!8*ÁԵBTjc3rφhk1 A6P˶ q5LY5ljDy& A0Ufjt@S[p&:wEg4FntT!:u&`*]dۏg\D=?)^؎F_>ڸ(bb:0 k}'6+zmRXKdUw@ H%a>6T;Ǟw?TK"0MַkZs=KA5d!>){uB|IENDB`./share/qtcreator/templates/wizards/ubuntu/scope/src/data/screenshot.png0000644000015600001650000051542512705421114026713 0ustar jenkinsjenkinsPNG  IHDR@?ntViCCPICC ProfileX Yw\\b $4Hc~ PP@nhoo } dYW0Fzj/HyAp2x1jX~amgGcpP$L ] ʡuL>PzH㪃<2!!adb &$_&`Bg } ’&d,oAaVdLP/[; n$B~hsޑЗ %<\ c1 m~17v%9 %(kgF:A:G4z\POT] b:":G2#y4!mG y*Qۢ}|ML!EeDC POto2]⫑1N>"9o@%yBѳQd10 ^ by  _ vM(,@B!OvF۵ j+ kc[ !.Fc Kx+a40 g3p(j o ֑`dža?`ؗq+ oKm'1Gm8+c`F j1B6 Ũ@K 16UH=uǗlMS`OJ| rOt'}]d[gmG ?<  3 J>?@8,O}zzFƘf7M4M:M)M3+EZZZ1ZڝQehh1Y%}7KGC_EC?@ `@`8ĐP0Į̇hxq $dt;f4017Q̏Xp,,,,i,7YYXXUX]Y>`gCYa6ȶn~}À×#K5NNS γ8pas]z5­͝}{#Sųǻ77<_ _6_ ?3??!@y6AAshBnu!q!$[Bo5[DElDD*DFD)E5DDsEEWRMs[ljWJJKDHI H$5$$/JJTzQjD}2XMP"!YYC 969k${rEwʟoTPUVZIR1IA񛒔RҀ2rrWi_K*ê̪6)?Hjj3" ԇ4X45Nithb5455WԴj}і.מչNWH[;'O޸>AH@Ǡ࣡a F F$c-&h&&ݦL.L̄*vl52?k>dkmQf`nyв͊դ5ɺecie3j+jj{YeٽswwTk hVWzݮe+n&nnݟypy=vw,ٹt׹]SUw'#ν\{>$xVsWo`G(",{Yxx-x{zdf~  '/^\ * v B/>)4(-/@X_txrxVĹ$Yh1z11?:x @WT؏qfq1 G&,<:Ԛ(x,q׏P :vR.#4c뙌qljSyQɹK;~|]H^Fƅ /o,Xs˼.]!^.QX[$VS+)pj5ke%\%i%?KCKǯ;^o+S/++)?SFMunowg*jZ6v^:z톚rK>8Dtiaw?hsh~lٓG;t;;:j<M}&gg[j&L&&&_~7>Աr>,Vn15;5>>Sg_ t-/L}%}vjstIeu~y{?j}\߿8SgæVV8D؎аDZ{u!2y $m[(>i4-: #FdpŔTǩdӖ 1p22U lw88=sJ>)%/V$^"qM< DX*;Uհjok45Zf":(IVbFz&f_v|4krjzfvnCcӀns(w3aɰ+'E 2FB̊3=%N&,`졬ěyqdS+ҺN}K9cqlu|9ϜW.H{xBW]k**VF[.Ua~fJe٭geүI]7@q_QmCfVGm:5(wHwvn<|v+n }~J/ڗYCv _- n&y,$~r]>֙Y?)/[ =-(ZZXܿ Xa 4DQ~?]:q(QSP<]aHdlcFƳUdႻ2O o)_;gBD ؐ*QYmKQWXQQ*QWVR}VMV7:DýFT}&馆 fw0hijbbjawCcUx[W!n/ܫ= vur=y{ozCU{/H@q`dPxphHph`1A$FG@:z쁃Iq)^̻'_$0Il 7D3%KXWxyD*)(&J>9z%fdか q;92#ϒ:k?ޞܞģO=?=>~f.cZz*]rr{7\͟$|YXoV_6YvLܹK7Tfq׳*fvNޱơ&CfBKFk,O4t,;Z_׷n˞k}^ v ۿz3?V=h4ْ&{N N"L,@ip(!<-@,QT.!Dx 4 C r@EΡPhJ*PALf =m1n6E9%2rʞ >u=*M2m ]39};$c( fvu]_؎spXRz}G71\!ga&~cq{V@QwW*+y*Ш|PmU~Xi%MϽzo\53:k|$4MlmxNxgJW&7nw1ƻ\v$lKoR+B@]EXrÈHƭ$8r8. }4Dr ד iO˥fDgdxq Gհm7^V~B}\zDSޑ/ÆZGFcƆ'3=K;7)ⷌ%;+j?jKlo@k [:ȷd`ѵo?~nln0+ aFw@' bx!p7!0#3ՎݍyTL/}]3))`nU5?u'M]=a1ɝy,[98's6O6o_DdTE F>EJj#M>-k8C8u@Bצf; Y(cV[GuLO6nwe+n*snݩ/8Lrq䓤gwOF_}6Z?~Bu{k^3sR9͂[<QWT,,|w޿bU5u?)~ܔLٜ2*$H?e%#Lmm-٭1 @s9dfx&k0X_iTXtXML:com.adobe.xmp 320 575 g@IDATxiЭYw3tZ=I- ,@ؒma8L`1S\&**Uv~$Ic bɌ6 @ ԍ-ݢ9p׷zzgt裏;C:+p}`}TpWq'Ux N8T*@P | \8W*p8{P*^C'a*pWp_ŋw~>C Nx8=pU\ |/ށ+p8;P*{ | \_Evۋ_?y~~_ z8zЮ \xg~g~G~$'/'~闾Moy y{~~!}N籘+O>w~w#n{gw}7~3g~?}Wկ>uG힯Rp ۾>я?_W>~w|K_oo{[ʯmb}<_ȝs^{?9A~+kܺ;~x_iПvWArٯb//ڄ 7yaЇ>>ϟڹsۿ=|iYӻ8(a;*-{;~OOy\:wy}u_u_w9.϶9B?& H&9RA2U__W2cWkk.9L*OoE/'? |9u>\țΛf_?ejoƜÿ鷦\_pʲ*i~s'Z>ٿ;گv}nT{8 g(|1odi7pç 9N '%_5>9~sٵ{G-ܗƿ7 \7W~k&߉~{{.w~~e~t5ɷƟyw]w=>.`so{aR~R̐n|򪛷[[˷|K~b3 ?kO>? >XKwp*{vۜO<|e~SVI7 Qr~6;wݿK7?p3,wBrB=_=y~FSs=_O|Z,*p[C.ku-㫫ZC.W$k/Nz{-ٳgfWg3˼%[|+p?? CG}4pN>['̿3>{M7XK@8(U9=|\~>x8T෩M?=T~>x8T෩ <|;vѱ9x좂 La>P&vðtC5Yn \rjf}V!0 03Eq m#VN < ᮯnaettt8c] l c'Mc&ٻȚz*5$a1uEripZ[h宣jT~ThN6<Wt_F`] /]8~RPdƵÊ<䄇#U(YOfGZ-CbzEM8qsYҲ5\h ]A 'Pdd.)Y5K@[*h2Xi:C+#UCY1-,P+c7n$%E<1C pb5GCifPZ1`m0áРg8~ L฾z,g/3?o OqJҎz&n";4wD=3*@bw},x#1?VQ+5AљbC/Np):rjU#с/ƀ *9ˡ!$Q#3@W̬'kFdX5}a8x. d7>;b:K1+OdP㋝'&c+LPl\( '-"嵙 f]8%p!*5F-a9 X*nq8i&kn|! ʹ2Pb0w rszDჭ mcM$$ H9%Nq> S<NXCN|'78{`Wb`Xȑ)aH)$swca#Ӣrf͇i&c`%+X@to#X1΅ȢQJ ހ]1dP|[#B8@yL7.S@A" \u.g@Sq$v gpk:y_H†ˬ_cojVU>-*Y$9*˝sAYgOj"\ 3'qnnox TN@J{_7 C>9XOZ]&'8pd>*7Nʐ;'\b/džM]o4_SUEEYU0['_MY q# pSB@d&&JKHZ\j+#/LlVh7kU e'LѪhMyFs6drjAƤа$ʼ{[Hb"SAQ6VJ-NG8u,e'ݞ4Լ4[SGp8389ߧ`Ix7 39:obE E&*t$.p'{lVHT!o2F/so6cQ4#5ѐp)CW4-08цbzz='(Mѱ.x+Ӣ.%:j4}M Y&OO)D{/8GĬTS3,.^m0JUΥ߸''  KTjXTtHb.+Is]F3{$?vrMUkFS9aadQB< T f;_7$<8s\?( 9{+Z䎚ik-WY9jVǻ~E^[E~9cTE$hJNKH`c60L6v"U2a pE`s@mp";182SbhȐf zˡu:yi`N6 )N*["@dE`0ؾnRÎcnp H,Fx-!bSm> 1#,/9׌'J R ' ϯ# ͖ /s Qy lºb&y> W,IEV*0:O҉-q0 Rpjd/x@(r<-t$+(9S"# 7WHcIxQkJBaέ3iD\8uꈰr h#?'뵑c 1ʜ°)aiWYE,un/hD¤$x̀q\i{,P*y *Ƿd L%ܳ|4T0`X"H1k3)B;5T<3`FPTW AYAתBXӵD1-GPDBeIח{ X8O11wyC8F\OZvL!Q ^Vdѵl'S;p$X8Lh{`3K| [G9:'G{oo=z4gA,s0sq>|zNUH$_254,a| Y|܎3G?}?o|_p:oWkN][^|gNS C3fZ6NvGr1:SƝ7c*:ƅx-Xh{$ EBJ@I%abWƵ7Jk(Bu?N۰$LC3# 3Z1\%ZEM ~Ys{0Bښ$rD.Bp2&$5SD3`YI*9bЍ#qOY%A['`Aиl1{zlCDSچfe+]J FG7o{ǽOǨ@1!Ikgy)"3f lݻM'PÀڠbVDD"yƅ(.]]<o'|7w~~w>#:ԇsgΞ'qf!)dΜrKJ5$a&ٗ2 M~D`RX 牀ļ^#p~#/Łcral!S,:W/q.Dvq"3!bʫ''4bAz.x?wUO[|,Ou9voy/WqCg쀒=%؉(`s8LoC 48C!Z CހRm-50bTK.-L ˃:T@ ųtL.V@$bn;'N`.+9C"Bu@@ dFVc-YΈ)9_t{I⯘F&^#Fp18\Hg Ԟ5ەzGۧV8NJΊ1rE@V<WDإؚ3$$~[>ɳw~/{ Ofם.:㯽-7q>ԽĹ?~-z_}Oxkn;"}?'W>zO;v;˿+^s ?bʳtd~ŗlL.Wk)ݨ_“}KNd8kV):,&#DUZV8 PcZM|YBq> 2Wh9 n͇O X,6ˉ+b` s2'{#i(nl)}9q3b0[LSS8#\;e[GYȖz;u8):tB!2RϹE )i|Z=51XLqԯˊ=a]1ps)ҢOK_OO4tzWok{ٵO}ox//xAιO|WMw9vo?|;~W|{n=s_'}?#k׽LѾ_ӯ= =O|Οko~}'{'n+_@ R0xcw{< h#jY9ru՛^o4h&R\ hv ʼnV3(vG5 5DSn]ȅ4`'(T]Ȑ(%УpٳeGge y63& A$N^[좡AF,~eZuzUMjqaHFoȄvŽYŝ|w2q^m bzUD`EZ Ae)i8WgΉR?e$;Ulʖ0Wh'SDWm`bxt~_/!0׼ ?uknSg=}ıK^/G/_My/?ӟ]{/{cA;~ԭ/y 5}S7t&%ϵbt?w?Y|쑋W~՛8[2.&׭4)ScxW#R1B3:s>o15 w;Y;t dF&uixڌ! go^}v3YJ%z;h  EL0dc^px*xXB,%~ eפH9lx?yr\ SSVg\i6 2L8X<5bJ1]5 TE,3#4u7<}BR7ђ`Xg̐bND [J$i#rVOPjՉ y!- ʳ!8c M!6dpCX!ْiTdbl8+(ͨ~5,"xmDqE4 zcw\3/ڇɮeBd^|۵&D0 >- o؊U_p7s)ibZl+g?W3 $d/چ_*EC.HdJպA&8#F堣R,Ń |Y'JX槥ϝGQMXdoMyB)v Y[)Eo)4GKȁya)t4jr-xPoqduQkWg̼2cȓkT:84.޹2 zFA1:DVhFBV.P=N[wLdDbZQ H-jTMx%9ouo8 ?~ ^wx}b8y!ʎɟ}?;ۿ׼G9̏xש_:e*rxVHGn/~w}"%47z u('S}moO|k_q -.#)qۭ{9әO 9v0}&1CX;םrM/=pBB G]0$Fj0'$z7 p(RDˮX% $kC* #.u U7x ĉT # }C ~:ODVlg5HD12"993&Q`8jlG|驋zP= or[ӿHx~>;sh .S/}g~N_xG 8:?C]gvGRYrɢa ÿ>7e6 37ts?G=b/lgͯ.=Yh2)_9rRBÅ-[SxЃ9%e7)O@7CS7@77,#p\ N3mDO_8gP(Y1c=n Ss:?z<r/>C=p'AS>t_uf.;wɬC¿/O5+$~<箮=bz¹לGwV gon" m.4]<䣬eHnőHu\$qkT@ +}>~,_Y*5@/x͡L"LZ,+8h;Gy.ȀcGdy4#"-K"`Y*)[nYW-=903B$~Fk fq9r~Us%мtZ({v5c&L6A' e Bc5O[w졊\7OT6S_DQMpڛn+&[rV>C`x(?|kA?}ͷ^JJ$nj1%wwaa r-/<)E'N;y /0T)x$'()Bd3B5Id.iN5z(3EW hqF0+8F-3"1S*"1Hc$1A,i{f#Z aBYF ХEDXPяxr 003dՖqlƓ@yF[V,WVF L4s8C bm+\Xs]Rbs!ab& jC ͂ K wٳ_ 7Vy-14"(dij(TVB]zEw?^tu@&}5"gZjA|75M֭ΫXFV‘ #f6 έ**X&p5)ORJ`,n.@nQ j=F&Mڕ<r0⮤/fRlIK^"{9ʧXel Ld[$2%mQ6 ˬi̓) Q#h7Sme%G7ޭkFDl@7ځE ߟ)Kz։UX*Ú r׾& װuAbAΓ(z3:I+7AejZ^OLsB7bQ/ d\&or\cK"iﰈ)h!4uX~mfƆ+ a zR M_1b9v HGD}%|QNc9^!5، i.Y3 DHƁad?*+^Y :w;P̨9sL꺴xweW cqD$XpJ[h'EYT Z$Z@D(˜`=:A~St!=+2e#g-27LE(h9Co)%4ث34|wA߈m ({MMD<%<+ NwfқFn'}2#l0Ϝk8Z_Ckؠ"6߰!S08? gKadGk= AdcWvQCU,/}L 9$ӌ(宂$s AS3<"ϫs?xdfq0QZ:)nz:9je1`1Rb>J 9,dlgV$_7Hm+9u{!Ҷ;Z-` tEH#.FI dI(` I5ruL_Fb=XqJ@c$A!t}-sw:^f]ޘ:V3YD']o"4j=(ć 114E1n:fg+EI pڄlf Z6v@d{!?A ܿ/Vc”th`o2Ǔ*lGj. EK  EKp |ŲDH#Mg0IVn&WZ/e'B/$~k0Y* !EȼDm2u.@NFnTpHN6Je}16:ȰbOHD1xv V釼`qY()4үW(p"1bP2B bD#eP rע"Sy&1m^E`n8q!9b,tZY+OqÚ(=n;YD8& %ֈ j֮+ȄZ̓']<`շJlل,@;G(-dGҪ-QM  idhrPP,n4LB`KQօ< 1Ca+<ҌĀhLu4 QAC|ncD޴v%\ѴHg@ A =;ĝt1)"NÁ'SݩF(peWF*Qb=ng;VǶFN GFU Dޣv #YxmzϜaf(H(SDh)],jo[# D; b]Ui&F[׸8kƜB9mQeb- #\xao䩮X\e@v/hTỜx< U҉&RPҀ!Ea%SDYHF:G@PrL*F;t,%_bʚTm9B4f :N⃫n 1*L>,,Th%KnYck0k(E0%#riv6AJg+ؽV̎Qǎ.I#Bj*EgY-6`͢ye)i##DBQ]1qъ۴0ͧVa`(mwXXFT.(Zrc ta"}}Ĩ!L;Q&̉!Sv0 ZKT7g:2o#>y{EԎD1!uҋ R npE0112ަ!}p_ hc Ok[UZUynWHo$D@#n(Ѱ;_ Bgu^(eoגּa秭hV>ԓOJ7p;x^vΜ9ݲQ&"G;}9`p92k+yllYeq $Z3]b)/AgX Ǵ`Y hJbYlpr"y%8jh C(D2؁oۨ17ixr"bN`tFdav.=s5xo ™ӧN%%LhV^d;FULw4/]6Tf %+3~(#[Q:z pl(% c5g}\yw֣~-0s6R2RēΑk׽3h˸'6\b]QBpO]>$ {G~};n뮻ܼ4EofD/+L ^=d(IEv@/Fn*=Sr.̉L}y*qncn(VF)pA&%8p1sDL6|E Zs>_[{MWewj:R~c$V,W1?M1Nmk1MS&VU"Iyqhs_0t]Ųе_(X DpӥX` k!)vMVz!d"Z2BMf~t_~je>v//p wu7tiߠɢDNL1Lڅ^vaN5 (VY= P<1S"<+G=zL);%He0fXOLBՂbCaTHoSz~ÿ#|o'Ll̜yu&d tB+֧ p5cgT͈v8.=k iTPﵽ0uVPz{s0nrNq,p 7b[%6H#ZeyaEC #pSavRvƿ.ɒ;I^y=:Ѝ7|7ߔܶ(*nH62_,ݪaFdjl|\bJgH 2,@1hk\^ WYXkg1n i!UUbdRʨUHLSzbXLR>:s~ӧ?~C\w}픞;~2qVOP )HR .V!\IE)l"kfK7&qg8#oV.xPnʚ$nJOL 1g,W6\ P2gvEw\Ƹb\M:=l"7u7ߜk 7J-j zf g{ʺJYU֨f˒7-&VM5b#T2jE{2rt/r%DvUT:Ra!rJ9/+EKܦݟ#͍U``gN6ѣ+RH5T҉~kqKL.)BpU$֐nتecڲPQ59dP SK2B[itL/I$rg:Jt[H׭ށ[zoyM7=9I0GFM"@điI?㽂H6`ba(k> ʵ>w"V!׌Kh.i;Ovi "=4enzAts?Z@e+x> ÉLjUM3H4x !m8AǨI벱B;Lf0Gk,}; !+cZbԅTS$%-Ѷ4,k67;4MK <؃dnwbp&dKTMc<-+I7) ILFWٷL0h 0n05-a'Vx ѕB!U>D*"Mq[n-|ޛʸ\KX3AXu) ^2O aKqj yml2xAhS'l;,YI9#,hpT-D*(^ 8`ÉbAtsھ`Djqdtf-)^5l|\X3U>1h&9;kO?(x@iX:C tܘ@?Ax jrJ6Vuq=&<0+"1Aჲqi4;CEv<fT>$ kb8d=|)Bۤ&+%1m896),m\`rI␆1) tW\U3Dʙ4d,o?{aښ/ kvL ɓ.`4-x'Naۡ q}E hfb=scK:v2Yb~ЏLUv1&K^~ f ,JGO7Y39Xfq`5֛i25"FlW_d&Ҙ֍uGq1!Ŵ֛ԣ]Nj3S|ަ=]ҩX2LfDZ-͖tKǫ~1Iȣe b+ԊA^Kdd5uBP˚<+Ů+V0 : pk5CdSȤ9Zyj7)%3Let}5EQA w2'Q`b,[Vjn6H1}K | Mnaş\!sos׏D:cDtL6RFkmPzF%QxZCzq5H kSj] ܮkqZ&;q@("DLU6za($X1c䯽])qX\=a1K\.+]w gg(< >)#+eKcߛ /ʼnZK4SwBC19MjHѝ:[!e68Hc17[ b\[Y6#EHW 5)!5&,U ~^P #fߝ*#dYije"[c+%o&D 븜幡ɻOR`$e0 |)|7VH~ .rMvUtAy+F=V!g*j,n%@}vc8N|߿yXAGK\GnmzhdQkn=.wc7^C~~RriجhHAcvL6g;cygQgyI[f\wVJ\^ yYͻH(::ŷ&2㘔wůT;G'0ۨbn0FvG\L t} gjP1; >Q(@zU1$ö K dN٨-I4= 6:+SX+",uZnҁe?"z@c/&NWu)(z:̳qŋmS~dǢˊv| lfm6%DxzUeXXugAm OmLĀ =9ņ@!|1LFm$F ` ؃vofF*)cָKgqEA?&c8JC\d CqXImFq,30Y¤in0J?z#c->K* e-kJNTJۑ6ӲWßH6m_P|,恶OAP=e\(u]YD>VH]?)O\oe;g/| Ѩ!kbi$dS|TrG%R垐D`9lLS eV[SQ,8%]h F1isna8/^:{] ĂfZ!ѐ<):}AQ(cq2Xp)4ce-Đ,x-CLqaA#%[`#z5ۺHa9 u뢎mLcE-'|8p-9tM+[~% |lP6!3L,NbhE|]2ҸGkHF%̜_(>|*?,7jRb6}5Yd5fq4Zb%51X*2kp`S1Dd\tkiި& [9 Lgp4Kh6H+Ӆ,^#h3Y {-R`IH-wJl9T {3R&'l`opeF9O_K 90i$9U[CK^moY,+^ |UwS Ƅ1IlSJRdlyS֠vU+)j N&VeOb$""XcLЎn,?셪x\F^5[`]ݞ}D"`SϜ+3.l,sŒe`1:jlBnka&{u0VIl@G7t"f%e#Ɣ0.p M|W5Uk\#i9Tv̓'!UGj9JT+q(R0SgBhb~؅aE! 8s@ϊE1D *F٪A@܉kN+Ulq.DԗvBj14|?!@p;F`fUx|FBh z0+M.ݵyiŤc`Fc9l iNNu*QA8&1Y e0 '8 D@G3LccrQSϜO ^ ,1Yj᜶7gnKk` H-@ܖ1#C?+1ֺ0#Uu1xdL'lWP"9f:ƣm:c&:T)¬Zk!.cM6Zq Qk34` e溂"fkgPy\@0S Pu)tLza=1~,f1\D"{2 ,K2UBqӷSvЃ YT}HA+jRbù{YI4H]61 1-'pIZk'jI}q3D.%xDŋ Mc<9bMykma7&gb5Po]뫥.PH;9e_'@lE%P8J#-wc-(Aa (]+M{!2qVCFHh4~5nM35s FoaP`k~ʿ00´(RXU%N;t)B#4re11Ɠ|:p$R$1;DB-@gq:m]}t JgY>ZbW\EVb 1hYET%M ]Q-ZFRD13F[ZL7Ι[Qqƞ푇1!D5+H>Y?a:ufu0QN>FF$6Vf]eIHQI3۞YvS4q2"}GdVV3cIǴiCn8V&a5.O{Eip|bM'02߅ټVcA-JMiџ̕͡9kWe)GE9wvx>goNɰ$@-髝#V: "Cûe\-L T^2J?Z2YyiYe8Vuԓ#⡣a?i,H.&LMҠFj~bq M8>'g̱QT=XU|܁,$bk|Kj"l)bU*Yڽ?%H9W)[w+ XD&rdՋ6;Gt=Ij93^lkc::AaS-nFN;C-4yiJv;T;]|5?A _F7p cL|GbG*6e3eE1YowuKYq/t(C&i1馅Xd%f}\Ƈe4[ӲC?_p_. ^"~x]p~ׅN>Yk9^ˏi-zI*4rp:d`on"9$VL]lYċ!Jf$T᪦L?i%m>=*\G T*$z,ӣ1-{ZD78~QXEX^ !B-yV@{--{Fz *br3_XcBp sGlѓȸ# nuF.fϵ Y#MG b t_AxW'rc^1"@>fΗ+UWp28|eʦog&)&"+sQZ*EXy\<1DQf"gu9My#Ȍfp]gH {]zHd(y =ݮY7}nt&:!Paox,gowME!%@@F\2s$W&|-~uۑBQ2N]6ûimy ]R>^䛯*W*l`8*39G#$0Vhf[ S(0ݜ>jCp iؖbet,Fe Ƙ4/:_JLtE7K0ES|4ЁEPȯ/s,ܩT˵C;d \}_VD8.['>-~0HL28͒e/~Ï?~a@hԖ'.Okߍ=;%H>Jc*Z]:UrE)&&eb`0;cd̄N]ȈPXOǩQ5":.Sgen+ QR/Tp)*G8阘t(wy2ldK s$B<ȒpA17ЗJV*=X:Y`/3pǘFr|MT*NHQP3IEc4SL=r2ʙks(ĵw rBFTIzӠ>7,?_@BKZ(W:~BU^%K#OLFbމҨ(IJpM^pKwv1k}$5B`` cզbȀ@Mc2G7WѣXC W39J='_egp'qb $C^|[k,{hax9,6?~h%C~='-l^ݔY0]w& m ?XTx^;i^p!z6$;[Jo~4T<ƦZ~khdPUsoK< W\+D3W*W 0}jOwNaBFWtfR2\hۮ&֥!#e%9n?6[.g*<؞(>&.vXuj8~(-J yt-Wp&Y,tNJRc0 #PT^%oLI-,9;2=u-읺7(oKσ $q*o KX@9pX>*ݸrx9Ar2tjOeKE[7duvBІJ bceʫ~t1IB>T|Ui'k4thqmo5v'.A '( m֕JyJy ,)J,E ߈\@nP拪!cC LCSOOނ5잡 N3Eoq;'Md*,X-$핈K@WE4ZHx]j+Aԇw2( Uaԥlkq|`G%N#tѦw0p0yr$*δ{YMSXq|rκ e,}*=]Dq <Rmɡ'B. 0'w$ b~ZxFq dfMa8+C"Z!ҥC˳(V+ 1 ;(qv+ݚ=qpE.jp3ЄYrS@kso]#u{9e[mɶ@"2i$/.nHS,GWOfR|PR#W*8Um4`a|:֎AχX%O8J&%(ˊ[&teɳZtt-4%:8});?k?/l;mtCiU}#)n_ڡ5:H4j/k- S-U-> Mķ.KՓx.S!8Pe^Ȍ% >gx%54oǵ5/"ZeH@/Xe*FD8AyO\QJWi >O CўaGY704^_\g:6=5%tL]$Z ZW{c 4`GDO/߶` c}G7e0wdTsG}ļv+Z;(WR+BC閯̼d'VAVJz>,8/hCj$2k.f2 @R v^|an7sq 񕟛*.B Zk/f~ѽ~Z*5%^.up 2/]M vě=xgB|޾0 su|A*k_Jf<\Z ofW4_X@ =Ӣtf"UVh:D t'u\Ӱ2R' H]qlRn^K :hlxoAK.?G8&_ɼ"iG~xUCNm6ybag_˲$E6-snA+7!P٨=L@R\=s,c&?${۪ J˗!ȮD&L֞z.D2>N7Md eT'4T׃0rTZz1Bٲ7`[G*윻gZczmLՒ8.u$ahMA*s4n7_O׆ ,9 28; ?kp=Q)c_ܨ5$TZU:J-*:h/M*7Fl򞜭-("ፃ &K+h*}`f@ܔKPP4hV=`I5]K1e%y(6q/Oul(pJɖdTdqK[&Ztِշk6B j\t_ObIsv"[+\Α\<)LomB7}i}i+'C=n ]%"0?[hIN-̖4VaJʈ3gSN0j_fՓ&]f  hm$AkEb;WXdz=#~o;.*ڷfs^_Z7PRPf<ׄb.:/`w"{Q0N#z.` y4CE"zD'u䧞,n-o+ֈn*>E 6ė]`ھ/W#m"'hup­*e/D|iy2[%Y[OUگ(VpN1dC;e>j݋)76ܼ[B5]* d*7D㞿`Yq Q8qi$vUB7c}Ԕ#xGf4A˜PHqF@g#?ɣzZ'ބ˲,xhM)rS臁)j73N@ :C4GɃo}pp-;e6 װ5_%%*9ʘ<_~Ug[+I=rOsp,lgE OzD]aȄv3I Y=[@雥@H1RWŴ!VUds|7P+b4V@tr3Dؠ bH] ˕٨I[&טVL:f*X ji6֬)*^ނY.^x2̤_ 2kuL@юXo2)y _}/Pdǥ o?O`o/=5/ǿDŽzOZ1<V-YT˪_Vc1Ur^W0_hgP֤ PxwwF -ꉜgKl`(?˰(K2u\nXk'ZiR-f-XVZ{jgbl2wED#E/>ʛ1zl\̜,Fa7*E@Z.Fc;+#2?ȑ!J 5);yٚor-zyJӞP_Qp}Gaɫ??xOx_ r:9 Amlo>rj'L>%G+h1(11f4v8"d[G*iR2'Z_2T|iLR^h gH!5| =Zn#x˱w KRl(.U-vd@]06XdXX8Dyy@*UZGtl$ 3D9VxyddnP*:])pŅp$ùc谭"GWU i6fhB8v;BjFӮ'|Fzƪˁq $ }.+K$KK#: Q&<'*Oֲ5A*jS1SaeGϢu/LZ2[5:VN68I|q!i>Z-;m>WxkvKJI,eO8p{4P m#љ2DRsz͌FABh tKK~ ŧ #5MOjNhPp?K`&kO |‰nٖIH?m}D 5)ȹc;is"TL+knVd&@މy{S{+Z-aO3Jv#g5]ŸmInwcNZN~_79)"u' ˩D]_ev\+>wQXLXg'_|EelME!Ó懭P5Oۉ-Up]u7y()`b!86R޷YoiŒkUx59\DK|kawT,&Qe-X[Y'm wmɹ2 {JOQA =UYt(FGJU%'P![VŭCpEQrAu^P ͟3Ї{ZeIH֭y,%AΩRrVybxq[rј9.G x1|2؃"7lf_zE%橔Sjgb0cKMw[u{Su*"58Ӿ#ez 8մd.e}M:XZl X7KܷüPOU!:x[{;o =3;(9cvo[Yf_r7tkz]z&8<Y]9CbE}Ʋ@UU!/IYrUIEڻ|SwLfp B^sYK2DxYħ3>&;pYysKڗt¢?@}8 Yk׍GS/쯯-ErKGIuyNm3w/"vG+\h۬0u8ТxO:>5\6YHe\ 8kA܁8P肚K*u9]4GύcEe1EsD_b:OL.' fZGl ~Em<Ö͡ab0Qf Uj=〞;BS;; u [bSr+e`ӧT**k-l˞pUM{y [GLO[;/?rKd`yDVik?۝@p9MtjEZyWtˤg>SdAn [n }?kiB)^iաa [wjk\H;Cq~$ Q9[6d.5&?TK#v~Wx!vl f7REE7$y6tstH*ܳ˖ ث:;hݧB(Dn<_'|]/ipOaIVY^[2-7\mmX-Qj"nY.Ska mr L59:d8AXўMwvߞRO_2fy߲[Ӄt 2OT;~u 3ENҠ4QSLӆ(0"F(CF5j mzVMSgVȣzzӕ^I 7y\!?NJ\~KP\V&^%DJ.W 7:?+-ݭ{pŔ [ёy?C3=tbE[$; B㎔ 4jGyZm¬k` nXYo-|vB;؞|HD=:=Bz]N0Qv]P*6}ښq?2J@^dAho2Bw4 ʶTrah[s 4_pl˃s88,iZ] 4㠌:4Jq~/ `NWND/UQuX&LYyjƽ_k| P1]hMdoJhf(m")l0]ѺUAE",TZqÐ\ <3mc͑(pxOM,B0ک@Q]n{28X"" ܃n5|uo !M?#b0਎G!!(jfA^GOfۿq:=Ehg)i?_eECbk6mӡ}p&OmNϏ!h$ tП5u.r>rF+Y/Щt7x$G`Н4 ˠ+O?fnff(-Ӥ"9SRjg;nAl&~)\}ǁ_H^)cPHJ|#_)GSń^Po>45 bи[o4^99Mt~qޮQ=gw~4vQ/<='om?W)7AFn[=\n^: ?ú#?:5cdzJa(mPGv†&Nyva2e1s\o U4(?U7 #&b)e_}`qV8đtΫⅮOfkt +n/?Ϝ?oû+zo&elQ}{^bɿ8~ik+,`%RT +F[ z7U9ފ4V ,1N+a Iy~9T~ FYHS<Ǟ6V\+OJz  L!̎'Hm5K]DMpթsj]tH<@C)tҢ,2!>t<.#<3pd7/#}9=~EwS~#F4^ؿR/Aݦ_)gc\)պة^Q.ROǜͷ:~`њM$R=  eoFv8BznaݺY/c_P+JgfnUB6:a'z+S9mv!U"bg1(If]VB>urR`نsPӎ6`TǝRJlU3=*N @Α΀oSWOێ~amR$2.!s)*:Z=i;"6/MN;P>siw7Z$ ±%E!p,ɜYAUZ}HqjU_~?Ls/żS|Z `?oG?L3{ΘZR|xάnG+C6?3Vl-[bWgOxF/t‚ZLXy<гc+9Vu sə SFt?ٻVr9츃=Ownaj)7CuR-!m|C+쪈okh|qCb\8n /综A[?f_E``|>sH%. N0FôoC| +>8):9Ko;D1~Րln B&y3rB ꇂ?Pm?mZʟւ.5 lFH&16ͥ1I&픳`'v/(c`:5@UQ]5L3TshZs@(D:4W5m_%6[4O_z_]02j_GIT<&$Q˗K0_~gӜƿ~gB[_v04JtVbv21!'Oe7o,ϜԊQQn4n&]|0Prno~%%=SM:HM<=ɸxZa᫲xڭ$Gp'd^39d/Şib!Ive43ӓl*#-b7M:7z69IHOAg Buls´p>vsu mϨeTGCG' . ;^^clm$<S,6jϏ5ס[.KdS=B]m')#DnW6 s\P4)ݫT'ka]CoSiBzk^' =qw^pL'ЪŬnUD+{+yZ%r{`0x-ɔn!/2ǧy}UZ459fU|mĆG?!>q#ٖIL/o(]z-ǧ=yQ.8;ظpnZe}D(P'{ 27wY h2 |y[>gQ[H08B1(.RF襓^_{*; [9JOѼLZ;Hv7}U'V_Ren pqHc]DZzV:k5c`LD78K:c 8/\f` yqXX9>O1d>j%]Ⱥ]/M{aQ:*"W;yz+58NXm4Zj"bY:,*Sg+#)r+`͢0~}lʳj2m'>8(R1|AŠjzP(GG3pơF0Q3^g4I%}UUez +6Tb}l7o9~1ᑺ ? [YUBO@Al8qu$DE+^ZIՐ>2rzf3ˆ3,1>$ &ckvi6#S~1J8H}>)/ԗ.O(%.'Pu=:~[g>"ZOJcmWo}WS=Xc&Zy6# l ILlE7,7wG|= S>7 c~iԅ^viV39簆YO[06ӹj|mw?_ew>lz(>ZGTƞ7 Oww/V;Hs10mgzQk/ Wc~(-6&urOfg/q/a16>" :m6:ϕY xCč{K~G=7%F L^MfnȞWy[#nlR(JSt{]G]}J =~dvqXGPWJg-"ؾpovS܎TeWsp ny`fK3xY)-R0@L|x4(Gl#O|IJqR0W^P~JOBA/,$2c M\ }"W( >-'~tU~x';#DU馿Tg·<ǻi` fiPn32W="BkتPL"5FW[Y-! ^<1q QY[cxfA]ZN vr ]-.YoG@-:o_+ZP9Eo]CXLXOVB3M1f%1z.ȣG k1ΤlFUg3^4`W%5S%z>T;~XB __\/或U$t+ A ɶ4U$mMljl4d&e|0q溟NCEvŪ9LgMVp")Y>b䫚iM +'5 \wk_0.o0 2Fr%N;geѭiJmr$ȿ{`ubfNR|yoH_rO ښ'.| 2g?ma>x5µ,_^3Q^N_prQUZ|.ooO&;u xd2)yrq  79{4A) ~EvLpe>F*h׆rd~M>3!Tb҂w}bn58Dt&[D'}#$xI칛"D gtSڕHґҒՒwH`z=ͅZ fWo ׀Houݾ#mmk/NlXӡET̎RTwE S?]]D4Uq0=զ/2-϶*.oݸq7,C8*xvV|,!Ux_?fy^{hX>jѵfZ.Ch ;FˇM5$4Dx< 6IHh;mTŠ_[J)ؕz /+4T,E6[dŸ/}&^ sˆ[b;, 4("lZ /zQ8R:XPLgHn-DG1Lq}#~wͮ w$(2a'R{}**IUGo㡙$kq\އwzQM/2?- x4NSP7| JY%dU"-Ga ǹ<&oTC ,m[VW5';RA!Ǔ}3 &Tnj`/WW|uz*6bVI2(YTqBIT誆 v_I^Pua%զSMi;1!#SNW׿!klUeifa eۣz`x)T-2vCWHk~i `aZ+7 xQ I#"l([Yn7Fn }$^ M)- ݶڭZIK4X̅Cz-x0FPtK]jbl ѥ$ 0keQMX (b 01S{-L btstI}4g ?&oqz s7˦C[I? .E(.X )w\T8!:@W{1ks^|cc{5[|uO60 |#Ic? Xm酦l}>cK2S!̗`n+miT1{{ Qk~,U?ndnHD^wXhY\4Pk3q88vB9 cƹi?xpvt <+W-&.ie;|dJbSj>qO@uV0*)ϛJ1VϢMČ1{X*i4[ 6dD`xE[}\(?&ustS7Z2aO%3G;Ǐ3#3}=hTm 8-k_or2V K%*8ҪW̫Bف͇<-F1IK{26*-ߢ`htu_)R 5 ʬK@ :dLQ D*tX̝_!7Vm%um z >AHMJ32ZXzg;ٴ3Ee paҴuB=aYQ^/d`3Jp7ӂpA>zb~KJ\#,qC*Koހ3zrum\t8(6tfJ6VB2/j鳩hfPq w($@g{~Ġ0xWh<)=Ӣ8 (0CĂᩐ Lhy+VTiqph1U Ǜ"3y^" V0+ p,M1D5Ϫs젂{0C뱁0=Jhcw?ƸD Q8`[>ULt0b{X9F20loYr0,[JSƕJZ>a%JJ)L$BŚ+8m`)T%W'LSB˕ +ERX1#v[tU}ˢ׾肓ˤˇC} &KyKԨ*?- U:7neBeab_ix~"x3JFiNh0Sol zpAX7W@?VbP!VKl> 7%zd­R*qnN2!t44.4FGM=.@l7>h]8$ G$ cXIi}7xT(: }#Rga:Qil0SF_X}4w1.f.%*fQS~fxtcvSxݩᆴ>"Ln̵(ᴏMA47nH<ɎeA *Z`ţ ͚Qnd)˟lwFe3 ҏQ4)vu(T|<#өޒv*?~Vق8[R#oZ5K:>ýbAQ`&Z'@/hs=&[Х점U̿ʬj_@tvO$5K03@C@E#in'ri!Z;{8QjeԣB 6_A0A_lhsNMX9${ދ&/GcgG|0,5WJ9[⡔hOX-G YvA&B>Cx 3WPSAPtX|(W΃JFjOїlg0U!6OTc˗qLW)ą.?b(zu,5J<ԮSE-5mGqsas  L#d{fDw86Xg䮪$tbb|aXюލK|6~1tMX1bikpm|h6v_bnc1 U} <|P( =`4 . ]Ĩ4b@{F4Pšĕȡ_e]i'Z:24]6EX f$vG9x~3w;I 2?(PV!E-~[̉j25A<jxYbVJLD.xQb'mg\^Ǚ`;1RCM ]WD3}{.QH!D`lO&LG ]F.:J ETeϻj&">=tgNa.]ecu*B *:| ߠO!;Jb|>]DZ0DH!ǂs!U5QQ{9˂Y,C T-&Xł{aѺnUyާ́ vAHcl4FIʈM[ *Q?j$ƨ## i(F (XbR1A:@0۞{ϩ{z}U53̹.[G|- Q(< X|4e bHTC>Dch$,9-] 7.7+А0GpAFoE@IDAT k"W$N&)sf|- {#{P rHQqb1TzakھV n㺷.2,'1[$p!KleL1*r ' ̀I<ё 26k:Ň^M/`UM4!mUdՃT)UԠP9G %NdIDgF E$>w?` >D 'O#FuxVMцԳAy] QFS0|wUEeL2`#XAE報 劐2U'#3FO$3C Z{[ IuWb[n21h 6 l"e2w)Y z`t!7Jdq(S2,9+8t42p J&!S{B9OJUIle:>f %!=93 @#"U9sQtmdDn]MaGV4Kk@a\ȜCHˈW]%`)Aq5)A,6eԸᗒ+1XrpFHyd 1p#;a>}fZ(j !3{FWiY1۾ L21b q~Ou*?} ʯoKs%\y'scAz6U-\PGDAЭd;W|q0732jvmgvR Ԗ1a2E X! L`G)lr?2f01dJ0O#1>C9/6(cfoĵ~6a7AA\m j[, kO$y<8vr9VT%@QpS8ҜiyGWKǘK$2f8D$ @B9QLQU 26 oQBd02Φβi!,Qh95UQ%m 6R-!G-M"{/й>,y6"\-;r| Rל2s _fh= pVr)4FRɆNACSMې52J,nm 5%Y|c,49n 8Mb,ҷyp#nrM`VCIIG,w|kNN? Tbp'/rg) FAKbahSO6v0S}LI%+d \O84H?K6cs"  \PUِL{0wT9qdS=%{e)H_A;>(C1 $^RPЪrTNތG8R2Xbą FrɄy4΍F Ƌ &1qk%4hUn7$%q3,tj!H~wY:$REMް*sR^V"I/t: A"< [E/ d7~kq )m'E,e`j‚#2Bb0"i]4Z@b;(u}(h `A<OժA!#IDдVGIxV1C .<5 Hm~b.T&45TAG;(&fL`!Ylj6@< #&VkRr"(F fD -aerN$y:r5I.RBe}{砆AO?bɂജ\ AQ)n)WDž -}eng['̊}C2T1]7nb'sFu.Iey)qP+d vHk%3v'.`ˣy3W/%Q *%'m@jB흘䑈̎Ƴy^ƦI@,-@eh@&UɓHED3b aEBxB0LrQ9:-g)? vu|6\07!B-H"AY)c̞E1)&6cP/"T8WJ Vؘ%mG$:}Lyd]R2ҫ8xfl |{a[56Uv0Mɟ\h[F)Cm-b$'E:P"z)I(% կbL`'X')F@ȀƓK`8I g_*@R8l&^b 붉+l=cpUmh+ b1-5%g:FTKX@F :f2wP0G2X&3'ǁ]yG1# @/j*ysGyI]p&H7.5^dΖ&@ր#q}T@kxo v ;||b:ILRFi u`5A y 5фWǚ;ngOR ,YJZ\e D43|9`F?oFÂ1Ii"E>~UCNg?љ"W%*؎d$8*c<2 '!A'tQR1&y`BC?CR-q <($rTܛ TC^ڬ9Ptc6 f8\X:=4C.O`4C TrraDP q9`k$L':1C_-@@*m>~̔AX $N($\:Øs51ǞhR]ecouIOYsR[83&=0FzaS 9Gkhxq;D" %hNB|L5*BَU.,a)F> {@H"ID' ^w!%ᬎϴu碚drR4&|Hp*HSA*i?`{%>X񤛧 t\|o`C 7je_x`h,O3v&v툨Ŕ dK<($ Ϲ\g┃XSЊPdWur]!t_M8vcyB93TE$ku}JEvvS "ߠ.ZGLFkH9gDȀ1YeE!q\p8U\:XTۋ0et&#*YM)(4.hvحSTY?0r|5UU!Op<Ǧ|UwH&1)32Ⱥ&a\PDWhL4NsK92nucbQk6?X$urX#A7G49'7B(suq}8 ^v;m?8q22N+u/4*Sw/жBHe۫ݗU [&Sl"21Js}h(3۸V4$if>#©'\!8 /4@e@ udAus%_19"Uz? ElSU \w^xB+0p,:Tgx O6g³D%I("]tfBs݉aR:&>OE!8J06 :|7Bm"6D:4Ģcb`]F$`|۞8KtF&|̉MX"[Ӱ@P]D cB  eE]# `dPasġ 6V-1L4L G)2^!3n490玘ɉ >VB0}1pESSܤ)sF<7o\ ՜(Ƌ˞_e,g08b69A)amtٷ!G.(Tp|-O৊ Sp(LG6+-V 8H \'ڜ/oPރp`w *O*R  3zbÁ7$)OW Oܮ_w7/JGFPKlə&D0'.hMdU_$AI@ioc$P8Єb^Y_cm#ہt_|ujR2"|_R=\W7A3++ɗ5f[g_NY]yz')̙i0C_{E "dQ˧ "eCQ;CT%4 p a+.#h 6n:kkp3NbLf8:t";f5ZOB>'`#:LL-Sق tFJCEwn`"W9A-1aU8& )O(&^-zɐWKսOGSlŒ+7s4pxwC=(ne\rX4i.Dk 8t^*++(?!CJ/ENL5>|tŎ[u7QH >5W BxEx"@ۘ,) [0nt*Ĕ!r! ""djRumg0xD?ŝ`?:`M !P<qѺ2)`<]vZ., bTjOb= |Ӈ8ؗqF39>|Ɇ6e2K,2T[+c9dA,an:[VBQa^]h^><'ҍ%}q*6KEmvH!*$ :B:e 1kϠ|(_ q #Chƹ(cZs07Ǯ߸~Ő8+z@VbJO`iQņEZ$+WfJbn^|:1 |1(/ <*!ByshsZd'iȱD(o1cU(C\`d,."(&2%x\f),jv540RJ(PIb8+h9M`ȧkqMK6™'[ܖ;f^lezO}鈚|Nc.xY$^*IO9MTY[z(_|?8j̽ź#`2[1!**Ƚ %* vxH`.KGH ARTj .qn18cY2()\Lج7L%@.cq–1 Dy(,1.: *YZׅ4 t+l #uATn n>&2t9չy|O~ƕKW"N-b?!>$XWlʶ7I=gZ>ʭ5E"qa2+";kh-p+ ~@˼ȹč\ٱrkjL6Gf\hB Ʊɼ' l7eB{,NY}^~SOL`J0ܑ9!}ډVk8@ܶ"xl4 uQhZBsyJ)<C$(OYQȦ >q| q]°pRer 풲rqXc)0;LыGEB9L ʈv7T<0#kO7|9Ia qkl6RqGdVe.XJj ܓJ&jOrU(8҇TfdC뫥ƒCͭu]# ,E,P|OǙ0FPY T&;k)2'vEȉMfKpyi-)!6ez !0[ K\KCiHQ`yCK![h7D3C\u7*ӐG;zh i].Uly]u\K;NV4dp;ˏSȿV s}" 0S^% K'2J)G$ ˑqÙН)ysJz083qPNuNBNh~ۦE jL{q`0g~<;v4g~©'Gɑ\cT*|hY mυ`͙X~4I!b!)&S$5|iĐ5XaAt .R Ap ? k@Æ1rbab/u;DfEFk*@1 #h(-ì@EgŚ,1!G( 6:n䞜 &!~Uƾ ЊPbHnOӐ:2s]j)OrLUB $(ְS.Rԉ_xy9&#f3˃[uμTq?Î%*c?6ŠЧ" @/TDSf ?h aw`{TP^!^jkb Yִ#jk79hJp賭G h;MfDYCG9;}Ca&&:Xo &6xˊ@`\r*}NlAjG.Py  DpEX"9J|D3wcqdG k`}@%6bMDr8Đ/CVO&( rH E\r}+aI#gh \B s6Y&4fS448H[E"Yw!nrM㘹jp;XeP|w,bL!0x4Rc7L!k.BFBZSAL8#̱ 2k``WB0>oۑ19GJ|VE;TM|w f|5CE耫*ٝ=HQ}!Pʃh.(4mDЋ(8>2g?pW-( @Д\*pT\ 4[*NTہ}Jˀ[¯+5-P ]٪vgNT1 E&PuX2RFԺ͕1 a-KPL äK<&t98sְ#W&(N[ͩ2~耎2Xhgr\ A. '#Qu<ߴ9%10'\h#&93t'9'D9sJ6(H@8lMg^}0' c*hNB@ΒU|_z!}wԊZC͉Fv;+Mp6>ԔFR:a,8F/84i+Z^MYy]p&|8gsJN ˕2צ֌Ks0%VUG1.y4׃82J02@diъ!!NF%aZ2(2=a4ldC Hp.ght9z^MS YO9D,Z6LN{@{^ \Ӆ*FM#O(iBEARqBJSZ *&N>VcgdpbM3^w_@Ok,HU%r[֔ 'Q_罌{_Udj$Bm,Wia9;~3%9. J k>/M5tMYĔʖͦ >h`|Մ *l2 8q0-QӀp[.r%*ߔ &-swC̤ Ŕr$x&W1Q 8SnՁ/2C](HFdq(x6sHKՒ1Ḿ<<%>&8^9AiLu$&uFW!7ThŰqDrL>ޟ盺i|@ 䔭FM.%3_zc$li \zE)~Rq`jba9w2Eʴ!BVEMT. ⫍'-UlGLqljKgA0\^Bh%a[΄,ɼ!8ω$眡S8YqL \WL3)fD6[vdE5ݎk9+$Eñ4N' OBGr ey- )$ ( 4vT 適\XUL/49m= #d0:꣘GK60~ ;@97:rPYst'Py I9J586s !nd48pԼE&򢙡2=Q L)l2Jve =Gm Y ϑ< Z\#)I{Zb,* lh"|TMO[(4S9\ SJ&80YNMMXx仗v$`NoipQIxuv;Kj 8Z-&+ʒS]?mMh% ɽf<*s3Β&rw#***b_.MXJ*C ?RL#D duOJ~;f6"`H P9UF.*'N8J76+E$RJڀ6"E8Xq">I0wْLvN|q {U3"˚xÈB$ȓ߬vVsr .hNtGeYGXH|SL3GI z0 # YVB22 X|@( IL%3 F]X2_9n\}ǯ(K 1 d2c%((i O+',>?fFg̘fAB VM)p@5{hޔfsXZ T5ѕ(J"EɈYWukp˜A@~P["LAn^3n !MG&b\rs\T %9PeF+mhSnb`,b"/ 6P96H)fk͉PI`Vz4Ʊ80t,`б32̋>T5و:\h*gpYk<~W>eOɟ`Z{_?Ը ? {חݿoWO<?|'OkUQmR~K`{,J{_ 1P"[=3Hq8~̋tvPNL.A,Kje!@&q=YJ1J1J,w%<;?Gz'(i}AC201L^l K}jfa}>~ A9@oY,RvIVHcc 9Ueda0lM8JwGرSv f =KO~tmyy~~5?ߗ?WDήǟ_?O=3{W8rc{WW۟_<og|ybɬF6 &vQ6Piՙ qW ]ֹ͟q=K>_x%^cxOȧ}|?3}wٿeϽ}'?k_-uOϸ3B,nbo\X _<@\70C с\Mx9 O/H. `XD¶k.mTõҥ3~:%awe!OҲ7"<ԠIH)[ lbwFUq*|$ؽ:jPY+Cy;J^*9)x`G["`HK`Ⱥ:c! %amNlՄ kE=QU?|"S#"4s]מ?㞻"LSl4r_FrIOrԡqi@64x6 7d>6k{=wpLupBw=]$&*~O3;RdkeWD_fӞɠ&i,HZp(JRGf2CtG,$]$$ׅv (L!ć:x, Ep5^j-TDjRѵʿzk}9__i[D饲<~=h > 8u \21CFuCܪ tcf}>p: ɔP "d0ߔ{F$SmghlB` eɹIPd+LA=p8245G \lAf8 s.3F1CF[݅nTi`q2P[YN8,9 _OLy.3j5 } .F<1qË Gf *ޥW竾? IFbÇ]:|ɠHdΧ~{[`;b-:9zɹG@+~eYt؆]McF$)lVG L""k(QPpCgjZ;#!*@5㛎!4m%f?Qoh?ihHWa<7X Zh@>u3APΐmh4NF&GNr,GLUwUy&{7}w>ix gz‡{OuyKN3 T?'OܿWͿy;տˠ=3w6[6Zl5F2|[,L 5|2l4 bxo)1M* &WAc2fDOZ\628dD3,kVZI ] m4ɬrIgM "S3˭>˪l`w,T(~Y3)R<tqh$Q2kHjʜ7e 7 %:k￟\~ʵBkR>v?KEylN Qym/ANj߻~ƒ^r;8۲IbNBԆS{-I3Dʼn9h##M%9j \۷ZX3zCjCL9j154G`qb3Ab)%&N,jZ-E۱j890@Zdb)$`7cՍ@K\DW`2*bB˦Ip^W'G̐ hoh cW?2%۴Ӎ>A-p+;cC2nN p&8Nw:dʁ 5fq-JTBZAcf"ފUUD.F0q,ȯ8`s):mdơ. ȄrggrL @t}IG q+Ҝ(c֣N#F36H4T$YSh\C6GV/2XqʞQ=w)4@IDAT,䭹3% y]fo6VV5lâNdT@iCX)w0mؚ{?1/ 0)a5FdM7$hm2CH z20aҜ-gf}PQԋ 2e%7 ș5.٠!<.9@"6LOhU!ﶵ}<["2k%tOndpCVg,:LյĽi-h`.j> A:+ ;h1D\q}m.֓!:fj-ys za Fvê8&E.V1)R"4o7,YP,IS`h~bg2lQ#!ހZߐ<\B2IlX 0zB li D 8'0bac= cluM6qq6z9U$u&i,9'w]ygeFАJ67|!ǁZk22Rq\mQRT“?S }'S;Gfy3&#k`I:)Vnc$̈TF6lb2f80NEl|8DZD|UzdU.mC{Q w$ 1-TaIa_Y{d (FsbecP'9eh7Y!܋u;0 q`YpnX)' bbmk S:Ϧc,gKD3ra`&ثN .SLSPfJ߉'R4to!QhMb&t&v(> C+m4tJVclHP;7$QW8'N"Hݟ1 D#09x$` ǎ0 tօyWEZk92wonOd<хeMpg[;M<.?d֓d1Mⱨֺ،]D# T9gh4k&qMcz ܶXʲLހ0'͍FAc"d^aVlDG41|&Dlm[bqҴo Qj$sx±Q|5f,.qofl+4(3Mv!@ׅ֔g\dn\ze1Sezr ]ʿP y6'x2#h|].zh_3-B8]t[ɜ-2; GĽxk- c(3 puޡmva pEhd %*] Tp;mH ;FϱV-] ؂⫝̸2Mw#na_k_U3.okbVKtM3`hVٖO^ݙvbP-0pi|`2WΥig${+|_A=0E IԔREu>k2եg;!:Yn0$i)WJ( 5q Z/'M%8Cޑ:Ƈƅ6pQĶfGn p_ֽo%Q=ʡa"F5%ʩ'pv{il"1cs*xn, B gZИ\`_zx|s.#߱EcKI Aʾ9ӪU D`k, Ba'?r 環*beY7E;ٛ#fQ]ȓf9h s@ocNNxf:Y8sLT:I0lDwcTp(BUApQ!ܑlg"[nܪ|6s2b]7~͋Yv(xʕ)2NSl"<$ImԘ ޚk`0]Q7G,y5_v=XMDh_Zh)/!0+^ ymտ;}o>}5?o'_yѽ ?~pu̳o~ݧ^W/s{7o<=nβQ3?w{iĝ|7'x_i??W{拿+_qză//zꑼr~m?u|o#g{O?W?[?#.8_,=r0𞩖PmXˈE.g/Nr7=CL26ufأpd,6Dhs5vYCՔD֝Ҙ=vLW1Co!>?8ԫr nVdpG{< H[䪻Q[,ȺzgՁQ"k;,?f&Gs{{m7>zsso_#{/9gN7+g/o;9zlޛ79-{W͑W5_Sk+_{w}Sn^x{Nݸvcoz_i'y竿>^w~ñ/n^|3]uhILs vt]SuC(lH3 d6:w{CgMPቋq-YtCA(P8eiYnq@1覑! )#]#U9D'S̶ڮF۰+.́rjf*8 CC湣bU!` 6P)Ç=t߃Sӧso;sD9}TBԡSwc ӲN=~Ho_O-q3^/̙H⇏9Ȼ~}O/~s;O=w/ϽCGNr@E=7/o[:vIֳ'?^rσUJ.jn|r3_;|:|~JrO݃f!kͶ7NTGp奔s#"6b 3L f +rێ0 خ%ڗ36;|QՅBLط rK[X82->iw 1/<-9uLI!l3@1, ̦cX{& _!vkuB /}?cv=?\.G5y+M>~d _R`E{??KO< ZĠƅ]G]9n\?/]x{>wxO>Ꮌ0μ0}~3÷}?za@crAݺTٳ1Vm xP@ r#_9vŨ--QcA?Ziʹ%IlrU,m%wTyU "!ڎ2n&cAg3oe'#j";TX&[zba,P@Ә;aM FKΠφӋO3_Wo}/tdKI̋;rԡ3GgGy>M/{ 3(異fuȈKeo {m1ԷN;}-+O$&6r[mظ7o>f);ùhpkNB'lJ[PSۊ RJy*HhhZ祖yV6ԁKu,Ey unX fb KUsSh` O~xS}o^v詓` O/Ov&3LG䯉gz1O;g $ 3O~aONOKd§|ͷ<^^Csck7{)!{=wdt=+ȟGcܧ_׿;uOf~_W*4%)ݰ&fzҁy `mU7!Rye"׋В{;w1RE!a Tx}ɵȸ)K-0:N^!єtF5&i|I}r6F0Nf6 Hs*I/^҃ [Cqv?L:G{﷮u=_gn<;?}~ǵsO/>׾™sg_;_;n_ыN[3/z/wg.!}Sj{;_\S}3{Yn^/҇O}DZ>бzۑ?{}/񿐠Y;3gb΄۝ ̟hfB8}$rq'"v\ݰ0lrvd GUn:4cXY&9k+^xzRvF aAs*Q` b $ӭ~:w҉+-!GX~_83ݸCˑoHW\7>s~Gy>u>)/tSgO\.>ܼ~cH'tGrk7<'2N^xB~sۙI.]?y,Vr'[o!7"gn;DkqC]Ϟ]F?CΞ=S=ۦ9ABpK;D+,ϒanڊUe-.Ifk<AfWE39IG=Ux'BC٠)~~.MH.X9;2oɛ"P[\|cyC+0Ey]'b) e2+D O$Bm'`X3#=ꑂa-lF3n V̤ԈM0̂F$EFִkw}eÑ:zQGPA=k }c/t(^El'&JYS,|лHT[gFOa±Y0B8y'>jyem e,,Ln15vYg8c 5ϤxEA;TCԆG&,oy=4WfK =T9l2Bv,!-Pf-bt0\$G K5@+ XL l*V$& h1<cy F'Ļ3I%eȁm2ٌ@ ;3rRݺ?G2[Z $ oڳ{ tC\8O{sqDfk\km8þuތN0k6_%z4JgV3YPGZ1&ӧaD$`FTJ ! (mH([&hW,8 ȏ[FÈ yf3aNyL0h)IJQ 3_~U(*g P6#=X!BE[@- %T$$PwN1&phZ 3JtS uڀCƷ?"dd%Yq콄^pA8hcbTOJ&gĤb&eǶN$BG\q#-!/qaZ2@S3^9{Xo8Eoq5-<+emLKWS,DM%"yM/]9u( <'$\ŴAYW]!jILaQ@HRW"&ƹ;&rQ$tR+ =7+##jS]-!#41@76S!js)G.3%eR"_c'.a4`i096+lE"=#jqs.8yee =kpTh(6x\GfrbkQ8-T Ʀ Grس9xZj^4EXtofےjJR]IJit`rddRgjBQ @ߘ[8Su' MJ*,{'~[?|qKĽ?'mUtKSI<<5r TJBŕQk r2ƛa\txbGIuWDp:*-q?&Ͷh6B&~2mPr3 %F0TD(ӣ}0̄FZzq*'"0(XL!B=$؂Z[r'*Zjp Ӵ+կMbl&ܭ\ <>s4~j*7Oؙwմ@|&UIEgF&(vPEE.;-{v2"v eqpaz1aQKB5r xF"c,X47K^4@Zkro>E%XFԗFǐAvM"$I耬6LCV)pC.Ȧ2ͬ>:궩Kp@`(CMK0A?ߍX*6k`#j2jCȵ.p33 UM+cVfBAe[ ֘_ƉFO]B1 c4Cz bJ܍h,8K|-aG=/}]Siw(5Q YMזY*N% +f(hab_O%"53Sho3f@ &"WYo—F͕1&p77`P_\N׋~@ nKaOhI(_Ȉ 5 ꃆ(L*E1bsp$|ÈzK-42X\m)D3H R@.J72x@yDG[=sI#-qH4$PX55nH$ ȦXNC-*Rc |:'fR lͽ٘6: !hZU" =`UӨOBg33_ h6k 3 d/oJ9x08|In*^h2M,XDE-f%O1 Fɏ/9Z͓lB!Cp,8గ4i .[sؙɊ?쑟eN2W^}aWC6sm'{R.?b[[xz0{Oh& \4B>gXE'H+nLD)>0!䜴_((kGPrGݽAɅ3f:7BlŃmm-p8|^ͨH j"&@΀ wx\5! "KBZEsw?[쀋,wH!+C4@ H02": oH)& 8&@R54eKCe{r⟇1EL{IU쉹tx#r+ɬE:kXp\ִ C-7,93", 5'Lh0 Ԣ",D3j#OqօJ2ODdA~߿iV 9He< `|ɘMHV q^CS= ؛Fйx1'@>QZF qƸt:pEo@/WNK> MqC^l5’ru-,u)P Pt8:2R  #w"zzjzȍSJRB. D,-RZz`“#0qUѩSXkιCķ>5I X/v +<zV͕O>K&[P y&v n5L jc GۖZcF <(Z}t oM79iA@]X0t1N08t96ЕS hkȪ/WBt2,cjƜ hMv) f2ODDu1fK-STL*K=x )@WYjR7. x;h1>6 (T F쑰#6!J`Ab&5UPRFحT`e3RRL P6^B@Q7[_d] K"[a i$W| 0k0I &a;󸚙$L@xK\&i"ۀUEƞ}g@*ZCȜ,ʸqO?8G>aAOl.\dJIU41 ߆`K24)XUf"?hvCqEL T%VS!{յkLu>z#UK [w`# #M)5qp*\ö|R< INd۷crd5J#oJS#*<:-7X\k*!B0}TD%g#뤝JX 6.~^Gr$bG.pjd, A҆X"3/!K8i!4@h .qZMLٱoT_ZQeƅs#G^"Ά5 DDsKO " u!X Bk4Q$?2K1ɚ7tN1'_Pl]DrsK1^xQ#FTY5%$%!y8Ybl31F4B2mKO3&ΣGL3s$3GĘ'm{;]>ga 2Sy7]gFQ\>`叽nXmYgTG:ϝN^^1(d'KFӅ"~a;?ĄM_c5Q0D9@NckK~#!1 {(F*؂+0Jl\|6ս$ ]t|(6+~5D] P ۢSܪMe5HKkq'ȳyT|<z^2Sb ɵLY7z\ G9{|ҊRzr\D"ᙗ"Z GR;L3B-2 NPkG߹tHb͛Ͻn ,QB ._""L .}ndE'@5f]DnJ$& |Z &|D%úN <-* c_pNx9ƒ|mAڸY8.dD; zxr3S7-A B5 2r' %UN@|֙Z$++a+{4fGXE{Z{*PHH|TJLhJm.,*C.pqML5'MYRKo}#vF`<{}0LģL,N9ztҹiu4ZN< Z1! *Б! ڔr]<+_/Zn?O2Pz2z l$ wb-U?,`OCr$3`X"(= mY>H 06B,UA.tuӦ2Ȑ>:Msj\c%VXυGT  i8DBؓq4;R6z|Px鸂dКm˖N8P8x OnɛKB-$)[$A܁-S[rbCf-V5D9x[kYAL8CBsX9!^z} XeF#)(nZ32cȄߓg>t2d2$4x( P ;M' WE$)u(Ԙj"=XJy@ͨM9&I 6KQ0w^H]ڽ=zM@1~te?ᔹ(|{kP,+6Ba:lUDkuYOlfeh!SA`Qn{Q۷oB8`24"{$Af$b8^M$aN45Fxbo 9Q R$#Ky]؁"&!*``gzrʛp`#kht$MP+Q^SҬx,h?%c̛ES%#>x tSŴ}lHH$HHE&H730F=<2lRLuר'yR{9R@_- b{"WKfA] F -VLF)p% ߑ 3SH38ԛ3.V?3#6RQsx֦[B'a aUZJnYm嬫yIS2|O0}if ?n% GvLO؅ VK@nm+p4h">lٙx՗w 96_7QH"xbOTTVhuzۑ|IvL%ca`,cXCi(/˾:)= zqփ.)F.g}QPD&ə?Ϸ>׷?1%g`sYQ2r4Gzܔ'a٨:4/#&iLFy!rߜ$h `P@&9ʜq9NJ6өԉ~yi"]8x4DwN'oo/ZSznr$K"+5%FsEQ8ȇ.3$IZRF2\= 8q 7;lrr{lj[?~+'P%Rf|[!']7mIv~s^L:M/(&05x$a./@=AͯM=8޶!3?-Cuoə6Ή8?.X;J3';\gbI)Ҷ[}aA)'(&a/z@K:瞬k7+?/ Wz懳\?gE3bQ`g8c4Ad\eQ3VɁ}QʃO(IH5"5o'dUa){(Gx i9:3p~p1Vq/6\U1,GV& !gDAwEFHz"ɟb #Q 3ou]_Lr"&4쮅T+1&(T$6R2aKDAdfڼfUi W,q#w &U}q41Ň#1ڡ6lL5OL$%OƢ#Cc2̔)Z(T“o\܉!%l58vgƝ|pX0,Z|^&:s' fȮnǑ?$ʟ3)p<4 ݯHC)|i[1r(I Ap͉rfS+ !f1lrY4程v7x @ (iz:nF9|x.2\tPĂN:V8aem=@İ=Xd i9gr,* @&r|K䘧\*E` /985DipQEm|wvp"vRO$Rz] :WIH1jMMvxcI۱>a!>xQ7#09;Aδݛ)ԵATj<-K޹d3UbPiAtLBXh1p-:HbCg1 q }OƲ"v{1{0?LT6Bъ%V/΅rU,]ORf ,^+sWO[yuoeqpE̮aXi;|xMB VYǕv$3b=ּlX)xǐf8XXBC]o^PMsEfS`UWI,dHP,qqPFFy&_1g}1-1c j@0c92p8VSN|PiͲU$ %Uy2@ Sk@:Wfst=5Pk(`=E#6L`"r[ɨv%tobօw;kn(&p׭L\ <7%/2 @ ^ ":O@o?t,WV!LJ$Ly2jW싱tK"Q 3f%`Iz*BxǙ@6iDХ!1@IY &<2EH(e\ UAFqaoiﱎ\UM- EYVO1F\=t`o e %7~4110OzG5GI0,`{!\jkD Z ~"6!V52sC+e^Ikc19[}dBls~@\4UzH&=(Vw@\x8IX_f}U3!-yQ._U@IDATLU.qyshK(kZ޷Gt&6Ds]q"Ui yMr?Z/+~KJSs>TQ24>\|q҂O'Qݼ 9Lj8S [ h+Zl(t"hһ7cZq#b &.W ne|]_eW4*Fb (cxUHR13md ^zXm"0F4X/UaQH\,L, `ƃ^4 -X6IM0ߖ .1ׁb+7^3&9h$eIUaogIMhi^EsDt2}HHX$QRdV#M& $9ќyR\IkMk``\jr+:c!& kwe= CW1%L0aE#Zj]1E{ldܮkM1IThV+MԸ2Z$jD"29[w !VMe"uy7LOU|[U^+!g,DVq)$yVeaK٦i+odA`F5KQ4mK5Og"+n#yfg  *^|9|sWXԦHr WC,i|ĝAS#̓T(0GJ: SCT;v)S"c#>;f&UV"CM%H6r#iULj-!5I, od)O,D 7_v=}~ByF6 0_|NUZ@-DYrk]b}dOH8s$Uʜ*8f_͉՛IYEd匃,K՘\јa1Jm0)p(wUT\SƄAEC\'2']~c `+`ɗKgQT$t_:G<1T!A[ḱ)*l $ %;htU}om0;×{Wy'8Y"2fG XzGusn#$N\ D`3d K6N߸o 9B.'^m>}ɿs҅RG$D{"f&\3ǣM'EBDlI:E׹&$)ֵs 'K2UKU„ZXbIM"2̐B ;'=ɓtVAjUiR@Yzd \ ~ (Ai"'|ۗYm͘)( M6bQW1%5|U$-TQ6k1[A0T zHލR8 TUgHBĝzv*󾄃 C`۠#JB'FsSu[Q2~|mH|@=buPKIUiL@f%8JHu!K5L#])Vz^1r3A1"f,&qhGd:89#atĄJbdf$2dW#!%sx0UxW 0pGMʠ9nt ;>gfǔ:oC*Ft0s9E$9Vdd" % cKԔEʱSg/BEv6i6>Ȍ.*1B̌C+*JjsK4O݀mq{hXyS'3D#zD)$О³ *8&š!+:ffτԜX$)5oc*jO3g4{}D>(q:^b Y\exJt 9WppqWv)<X*Xt7\hGE3mIƐ^3v V#iȅ|gLɸD4ZφEO~#/cyV:(^`llSw M.38`曀̵;ͩ1r>ƒ!zj*ӻƙ\gܜCb@a9ˎKA:@ lCsĨY( z)W .QxmrD}{td ψ&Q6v;iqkzsSW _d0}CbdJ(h3ӜCТL:!\E.S#@ܪyEM}up䉏u\?8/O״ kP?D 6cYs]Ft?/ix*.=pN2R uwm4tbrPgML pzG.~M7g{uM,рTUeJP%^!{GB+pHX{sT6X8*+QDZG"I۔A2q Є!SQ]M/: EdsY- %{Q4KLH+琰l)m6ĈסޅO"(xQIfw\&dʃ)3j[e#f;J &S,JBVDI)o) >177ѤD5 C`K17BHqIB$=k,h8Ayf§>*5r;_v唨|?׏zwago͏>v걳_xk^]/}Ʊ~?|4yƏ~}?5OYG/~w}7>M;Koy{>7_ܣ>;?.=sWG;_rk~?R?_xp쳾~/ٱsxOrkgϾأWo?/?+y|$Qy+C_zYPd5&{?˨az+'`Q,7cw Q~5OUԍ'8GRHUS]`2vG LbV-36Z+Yմ P$#f~8H|peʐBkb #v V P!`c?dHb0C' 4DjȪN;,صŇkB\ n {9.~¢c?o.{ȅOɯg~_;{/~x\%_|/O}sΘ$؎\?y׷_v#):៾o/{֛O~;O\>t~iǯ]x ow_=?^x}Soz/?K?rc{p䭷|8vE7x$40`wд C  -D1e;,lka8ް 8f 9MW0ئ.tF%mNM?x}~9_꧑Ö$?oΛokV"q{?|}?qpG/>yȑ3_{wП}~\NvT9c^|=;?O~=|_GZrp?uܱwTG]K|ӧzVRƑK]~+'o>zK׎{W]?/_qǹ2Kk* #sfEظ: kJEMb&']]̚q !lKD#K r;~ mR]\/obmI9 Ygnf`nIӸ0IY|DU4M ^9vIۑw^$`x4\d%I fց2ƜJavCl^Z*AFR0`؊/I4Rgtѓ75P$pz}˕y+<9/Sw| Ӣ6a)rOgu_{O={/wWs.zٷ]'?{_=q]8?p]cw|鋟A#R=܅=xsϘ\^zw}77{O\d=8z;_w7_/[N8g V]Bg8LkmnlӰhNV(}a!1Q)Ŏ, gjpΦP4D FΥ-; ' eti z84^LҲS-ayL)c V7sL}.Iy]lPKNjeS#44qU5z͜G(S!4j혳싊)-ι?Ǔ;pG>q;KWh:u[#:8s<$>spKz;z89Fv|󑫘?_<]&|#ȹ|ݟ+#+6/ys>-ɋ̣gX_N"^rgo>G{\׮^Fn,i`!4-' - Y Q:œ/*QC}guĐQޜ+YӀ'K]i=@`ԀiE+"qd) ER{.ʘ] 2LJ2 6L3$a {&{` 0{Eؘ{) r00oŬv 80I?¥ӻ'^pS0롃ywO̹SG.>ǿ8ox᫾?37xK/?d! w}7?_/?ImVʐ35׼ֳg]<_׿Vug_=?ȿ|?o||.=[_#OwWo[zGo;__~#/|=}G}A>}^z ׎x97ʇ~՛}óHB3zv|E$z#͟  `D4ēy)&廪T.OXWGNF_~M̏^zMNcjo]'Ν;sBҳ^a)Z%sQXn~;,~Oy]r{lSs+TL)tpshoH5r|Cz%oii-ffA7ޭg$AY[pyX+)U MhD:RD$%\&,85 prz1풭-LoڬZ|ϸvC}ji[f,xr.a3H\cY'x#7o;cMNͤ%Q(-_R y ~ѪK˒ r9ʘlB@9HNץuF_x}(c[K Hnl]IgS*}NcNp+YffhN$Ͳ XÐKZh a& D1Xipc% kч_pJD.f74=l$@[Z!#3hт l"y$15=MfѲ$Y! h./?fQ[B֔?1V!wXG;m"0݄Eo='xH ԖtR)}6=c 2L9XռF! P{bZs&[+#sV~ u$!uin-90]GpH94KcvZ SfN9l-q9B/~eb0 3aK̨łEg\˜iA4-AF l, 4*K Cq sёK&FlQ#>o О1*"]7l'$n?{/9+l6*)2'D'$Lu*`ϔ ZЃQ54+:ؚh(z⁕su(+.HFk5>i}3o!IYTqr=^\ "B2@'\; U耆',+ʂ'ci[UZ[2X$&e+=Ǘ@D3hTh o(暺j |͜ rxEM[DBx^aI*Mj c{i-@X`$2S6ĺD! KKN.ªhHŜb̻jc%ES XDo; X*}dΨ۶4r9*V96GX@]Żq% :W=s)$mٻ0h.2tYVi-A~ aQj09L1"c<ףW9SQ *FM,S&Xݲ7My=H;S2w[#m2#<6 G$،[ABs`3ZwK =3$kc 'DK9zCa`3kVdw44ޘ[L@a%l!흴K5A;i$S' H69q[˂OLk %7Cű1Xih`ʗiBT&4̓&o+9S*| BMEqvscBi nLd{uf5rz9PTOd #hDEpO:.k49G6\D5(rmi:롩j3]l8T;K7!)W"Y'Ԛ3sC,0% cs5ZCqm*c$1{iee]gviAy]qnM%asy½D^pC[ugUY L춄<![&ܢRYpﺊ3tԌbd7wHh{">ήٙ x4-}q1+G/wJg㑌S )^l 4eJlV5lne60Yt$e">].kM>ǕDƎ0w۹^;Ҳ@-k&Y kSd')GyNи69)N-Wv]iߤُN=a%@C=X޽pA2pvڅ'|~{'|K.]rm~_u ':d{ PT98&>䟯;q"ݩSN>}[oiO{Z?_BL(&4aMW+!Vd4:V!gFsC`8d,k"diXL˹5c'l-Ӗk3T;'RbߔcMIXcXyW2nŬ9*b]tUMՀ^EǼ!Z…R(dY4`~9Md;VvyW`Wt-s w <ЃAd{wjDz]|c>~?~capdǏg]ɰ2V%0fw ]+/]tŋbȝ*٠WLyAS؂lCT?J]a!U/s ~;dA&FTm䛞b<7RF4<1$+&HȷS>h2veQ=)S=sTrQϠqS`ÿP:m|?Ї|˹Ν9s&2LOY큻f5yXzL3'Npy>M{ʕK/^p'4gw}W^xQH잰j :!3A}xB;F6 `POq8hSZoqfb.{ 5z~5M8ql΍bo|t{n 4`v*5/vSWMlמoVG2xU 6X) tJOQ:R&W>b=8#)VHgm}}=g;{ 7侍%% d[ibӅ= 3VxO͗I.K3T-G^/^x> _;nPڵaA\AǑkK 750mp`︂<ˋiIiDm<MeYtjux-}J=V֭:+[k~{dnKaح7MF4dd 7.T66bIOr-nߜ6hHdg Du7xv{eK|CRcpc>fGu>w칳wy筷ܒ[כoͮJ5-)L^jq# .FG \ü,s#~喛o9rΓ:ClK'~%1!Erv 8HާNgޖrٟ}gN;:/@ѥFym'46]VuLɛjmL2o-ϴ%G N%SBFX^$=7@vmCNpق6`/Z<Ͱ=ln{aLX:+[S]3q!L2`EPvl D r]wr ֕+D{1[OvڗJEG[}ߗ|Ϩ˺+,qhxH2;lm 1Ry{p[ӲsS95*b*((fP&Q `"%`DԤ\i+eҝ?:bDCDP@TbF@b3[{揾C{{gyk-<Ԉi7TW/+27/ќ28G ϩ;:i\;Ř`5zTO<)D#fu4C3h(K'} L4"(S;D ( Y |ۋ D3%զhrΞ#|j|e"#a###F&iWf3Ji"[ x!hiXQ ]31!C?{ r}D#&֙j]R4EP**N>WvmRmǎˋԱ(Av`ɑuJĂ JDqӥۉsm`4ڐj3 67نɄc@Y<7LpIMnӁ~#51,mH fɟl2)QS\1 h3e( B[XN q'6غkKQjfѪĴxl̋KgO%Wgբfp.ڿ%W#࿑b@ANٔ,.  *16&WKˡ=q">*gsѳ\rޭ}?^'m忯% ݵyuWsKұ.KA fik{ב3;/<O7%.uXOjuaG;lyrEȋϮD% G@IDAT0%FՓy﹭\r`oϿ߿o[ӯ˖(GWNÿھgvdΉj0DFkPTe$N5ePdȡ)C+z,H#7ïAJ_.VcpgJLʪPn;bmҵD@mjl ta <;ʊLdkYCےPgi} 1] Q,:4vveC/G2]Q~wbZpV|~?+'i[.~~us׽w?yϣH l~m߼b1_xg?G~>~ύOhv[~Y~3_{P`ny׾_d#H0b爰5YX?w5r~ޞj[;_?ݰ38-e;ŷkl ?|cOy~ڑ۟+׎}7XmE{ϼ{9mT|2UZ{#-DK )m2.IUk3bS[\ BJ- U+"&/ #tvGddaQL'O߷P&&IA?'j -[>'yk <֕Qlu J_4ʾtAZ HDV0("ZTMpJ*HE" Er{?1ěϾa7]rgOuv}x8̹E{s-]ܺ{[|wy+я߿c'/x39tO~샯?Rm-w:RG]O mav~O>Mxo嶓k$ovq /uێGUϿ^;oy5/<g+civ}ﷶ=I3y3wǾu8Ӝ|X2 j5BP +,'MA$8Rl\T.G HhI p pP8V7` ?Sx 2q;M /h40'6*!T P`S0( ϸt*n +`G@ˢ &C7W؃*{2EFh)/ȿ@(C| '~, I"',R.~+SMo{|؉/[Q7=wXY ~ɕ\/ Dg7GO߹?7>?OW<1؃ؑ1&~ïX_×ް_~]Y}<^a)@|C]{sywIy⩯|3g໿n%xd>;r׏.^g>W<w䦿{[zW>G^Kf?~ W>U~->:gX>DzSj}03{W Q( !a # lOy@:h,[ RmIѕ5AM$5 IݪB^_j?g'=Ha@n}~A%'0bb* 6-'JZ|B[*|޳tyY_ ",%pϳB;2Ԛœo}G_50~߲t}psK?7y}ᅮO_yг~ϒ]_ݫ)VY3%˶X?wүЁԟ~iu?zH˛O|W0Z3_>s'^VzکGNϮ=۶wW='OLs<o$SI@ьoL|"ic( k#ę4j! fepAx+"l&4N(7zdg<(F~XRϸ3XnA?޼ag␖S%96A.0p@e+?SQ eg0%dj}V~fwڻX߽w|T˩t6`saSFlٞͯOxK [y_h{]^+W~7~}իs714CUkPOKo&WٙGx_ϼlq?AX\^ƹC2ŋ҃~[Oc +j$϶ݻ|E ,it%!7I!QMM~hF]~؂:iIItz`/$8Ar/INkS  sTtm?'55NVه=ƍ|G2=Ќ)HnIv)ӂjL ^$8F :2咞ca Ϡܞׅg\_W-<vSJP^6OTKݜܹ;/my3hL)_~/vַ/w~ɯxLX۞.>;|-JS{z/|W[{`[g+.܀m#H5ֳ n|G/sn٥/!Kܱ<[9v,ϭ4vgC"iKr"i]X(t> - Y&V 9aXPJw4<ܠLev$DJHNcq; ilRpN1]O9AO88|%@|f7d`[8{ұO.壟\8RCi 18?x|qa?=bnBd7_W wKO;|ص,?ʋg ] O~C>\D m4/mQçVvAL1NBdfh- \ _^~xQߒޔ+t7q}U?w7l۳g _壟_ѭ]wu{?]{uŽg?BhyeFQSiͯB >; >ܟzw?r_~[w[>.o8^_ Ϻ};~?~S~֕#_[|ϋvV4cBo 3ڥeJIygٗiS>,.%QXVٱa-%8: ͕NrԀ8*X+Ξ(ФC2ɫkU+S a@7 kΝ;R Z^2]Xڶշ,;~u|C^te >qp61g{nݵ~ _|W߻vd/n^Y]QFE/fqمǿWppԤw}챻9~͟~{]~˥BJoOMU?n!:k?_lG#s1~%O:`^"q^3"zyiod9tyteaO7sxǾ?9'fF)VoxOh7⵰L\sw i$etkI"Io)'&i롶7Z=w}ܰrȽGϬo{#Vٻ{-Ži'm}-o][ʗ* \'s-mٺ+_ Ϯ$|+ni!j}"MGUY<]]͗ڃ9*DX)CP 'oxxgfVOWf[8۶c=+W=?v5{K.vu. ޥKr,UC) w٢95> _1WU,5D %)a.!V Td&f!G+i֙ 7~5ƜFPLU2.ꈍ6XTfy9*D(d+W'b% -hhc:4לa5Y^0M ZF9lxS]wr̢u MDƮy!]z9󷟼򼼔o-G81aX^#=7_7Gu>ٱfnY>?H'>7w^p Ζ Vjl&(R!YIБ F ݎ%q>c%rl+\"Tǥ_R`q!va/H0A A<-1 B15)a$l@(c,X @Ò'^8|d3#A7(ⷾ,(5Ky2M 5Ot_c}71$db"Z1Q Th1rE&xލ%f`ҳbT&+F`6sjBAd`Ѥ@yl|ّ0ȡf 6'5cl']>uvݎUgf|dv(_^I!w]rxKvV/~r^]2S<).lH~M p E[ҲΖ!{l)  B"[:ϕVRc>N΅l$áj 3#dvz\#Ghˠ"G̳s9mB_^\<k^GV)fD1 DhN}G8_X9:K"q$Z0ʰm2I8)Y&*2 %kCev_Q3ldWCa`(9DEgAN,`b5r[ū\5[6g}٭^2gΝ] DM$NJ1BST{ư,H%z\ cu<ŕ\HFv-a 7q*K3MDϽ!>8Cv$Ĵ`҄^CVV;>?hp*(tO$*ox_pϟ=]{fXͻ:Ͷm]cKps_ĵ-ou h,L)̈́k s+^<.FuQ@bf BY-$SGؒ :UBʘ Ø0$znPUt Rbf*FDW=ō@Sp g#k2d"̍7HYU+7Qeb4GC7 H$qD$M0#LA!5(Alz^Alb>42܀f X1{+k"R$.q)"~ty˛-YM7Y:cEP(G 1uˑ, $6sdl}3lm-ՅC.<ٮ׏nY420[ G$E2VFiPklC^sX6S.45bzPF[ݗ#(ob ;~mëDpZ, 2@$wѦ:܎ `1Oh@be6^J 8AqXq΁H4y}:ΐÈ0 -Ѻ 9 5`-7sW1)/UGrWB4=qpS CcuN,}ZgNO1#={cg;vζRcGgwko.oe,…O-- 쾌,%7}>~Ǚm|_rf&ٯonctǓ_kr_'s_"w_O>po|ϗ.=%v/^Yyc_gY!¾=?W+_z/C^]w3^_croۯWc ?&Vb3c#h$>5 -銭 /P 'OX$1.`7i=xSx<4?z]\RLiLN=lŀMHD|Q",%)&JFР\Nʳ1W\XC_ KZu9&.ty*G`3Hm@*}hRcdF|,jm-N^ѶY+~§bێ.ڎ/,$oMԭ'2wz#vO^e1~y݇?_{y퓷^,jr<[^Ϳ^vpyɳp?rp#'v,|Y.?߻/W\vۻ?ϼQ^'߷}yvKo7A~Ε߸mwßq/tq ˯_~wTĪҧzoKn|~Σ.F w=w]oME:tO(#=vrɷ}T^\={k{Iܽ8{gYl[PrYQnLU9XĠdD!Hh(XI|]4Abmzv_ X$\! (9';<._jkDqSO;NX#$nY/ISdO-`{F#S"P&G*g! Yg0XH\>n f>Q$aVK]7p2c/~',䛪>3l AM1zY*eX_q^r7XXț}G_O<|.~/~|zם_Z~ / w$ҁG?Ͼ g~O/o߹td.cKp+\۷'\ЪG>;y7ox?}%Ks_ws/DObJ)sM-Lq, .Jgڀ {pp-Xē 72lV HxK}"% rlUNrd7p$ Z JVI}%;1$9';"&0phBeTI 1pDn^yTR NBKRj ۔Khďa+daAtu۷o *:ș)}vw mٲoiq]N`.\p^cA -~{N/fE?篼wi?o~ώm?hɳTIWaßc烍 \q䭷|Ͻjϻlz;;ݗ/Mɖu i]٩'ae`Lv3i c0j2֘ WiXv!;7˂A5vMOu(M=q(uΉL$c.qc421c{M#iA M(&{-NHKy29 i؉,AлF1~Ԁ"w^ǠuN,Yȧ:A?B6s&H($ܹݼ򜼾+8Fs@0dMdLРw+-_}{I(zȳ˿3.oO|/%;M](Ͻ߽k/޽vΣl]g|,;q]}Wt/]x龥Sc?={5׿mY=qWE5D"&Pc=t!c|'.VB](LHʢhhFqf.E,4:o`^ZQ|qg%-j^y'ºH@ }]ZGQ $a̭恜!2'"mcp](Xl%e4G".ETK؍_W\K:6 D:̑GN:+p(mSv^ٻwoݻMffBy݉vW4=3G0\=whXc޼䄮_-mɷt*_}Ǖ8?/~.^___b#'/ڷoT&H'8kO[={N#\^Ou"=~z};wl|r..S磓MI6ÐQ9+@}*(?Z/sBQI,2R) dx`0v4oDB\B0g,-WZǃT^' leJu!{0H(FajÇP=jXdZDi颳|EX/r͊  mk1ڶe[UСCο#͓0l;9pw^71ZO#P#ʘɴ{ ޟ gka=xޑ>o__CwJ~|eg.FM[IN&Xei-.ڷ?,=kew5c"}#8 9 cÚ`25] (A;4 FؑFȱ{C!l2>zcΐsSAEvYa ua&9CD[xBil礒N qLH^S]kS%tq-:"`QsɈO+8fq3٤~:\Y.[>q5ė\+D]ĒlF ]f{ԢDpqV1Xl&EC]n[ 'e:q:![ 7RʈKMa&m]F{0þٱʨСze,(qҁ'pBj Ah,EMcE' NSA Vsh*ORh.) fՓbK_˦wq1,gPlCMxyIoA NY/; uy֩-f]cԳDz#Ƞp٦81NMG> 16[l ?f̡Ytxu#7WqAY'1eb jYc@bX0[feE L!9\2C iS[qA(G6RfLrh>IG}6 "U0p~vXD *dIM2;y$dUx`烸( LQ!K-@h3b;R̘oP 13J&n9 "@1/8+Qb wi dm b}UJl4![VN 7Y(Q띎[Vd j }Zm Qs27ͷPA"%t߆K9ŒcS^hxkf)/vd8.E$A2AbĜmS(*vcoAʏ `n0Myj?"0J%KURrnD44OnJ"pvUZ,X4 Fd"'ڀ7:`)HEU"a7qc4dnK+|H5tq2A3.hd3%HDƹvbT`cOˆrZȉCaṔψPTt㠣*Ag-Stp8̎('X'd0x U&8 5 (ԲG]FGHڊ<@ղRڴiAʠ&Er]0Q2LxC‚.W r:; Xi~F?KSw(:QŌ@ WaDa$ܚ0M "M]amq^?jDZ7Yg=Ñ߀xby*vnklتL i I10}4mJUDµ53e$6ȎS9^R-:0k"0%Pd=1$ `PgT4E%JAr8=YPs*$?RL:)HGDjޮ8>̛bIyNs"u@#tYBcReRJg2zhR1RC3&U0~ls7nQ0f:eca@+=K83EH}GA>R- *NhSSԃO>YFY(ø1 k _.wxQ?DbәHOPV|"Pc 6/# ?2n.8"kҵce,qq@o!ZRWdy7ΌKb &Z~/9zprGvtŌZW;)aq$n7ZAaRp_v'A)n-)Ab̈!;G|ࢧE-^՝2Pw'w#J Т㝑!,cshNt;Qx4Ê|$L"3y")5,bfÏs,q1N e?B ? qWƯ#yHkP[h4V? YԐl@ ~~ E=~ω'apQЂvS/ 4d<,bV#Kc0 'aLDsO\xtk,Pp` yR|~I(udMĞ0ZUDDlhD!;YF#P7a`$t3bqpX|3L *r. f 0t=vfO$2>ЬRlߕ*3˅U޺Bij5~ Q̡Nj *A,j%l * eGd# l^%#줓M{PB%u8֣*CI_ɰuƃRPҡDm:iӓm-i \(gH0`!'29dae1&Q?&V}yr&>nbc@|ZD ďQ TthM&mT E{/k +}f=6*D^&yLmd ׽q8i /%:G{Ւ8ȕ`P*a*x C٧ayU+ ÂG``395bC/쪐]l(d# [~m<)7/FB\m#!6yɆͨAd尬qn7-W`|k<㓄)4ÄV\Y8P30%c{xq,V0M 穂`0AD2,x(KgF& QEF<,倈T2LDRme#+ZlHgkΌCp%|!G#,Ӵ` -$@IDATm]2C!%:f[rjy T9}mN&PĮ)y҉ 㨈-.q 6#,*v$A]H%,t&Ly}'?&qWQPT9uc6X  xm­LD쳂)C*erJa&g, *(|d&Fq1xVs"5Ra8$%0ZG*Muh\va3B%ΑGI|[麄М=wߒgyZe0 lcOM"o$yP`^wBjk hr1_][{m&YE(=ef$*GFhpB)3}-}FX;QZ4F/ Șܦ<@EX$24M: 0/Pti\S>P'5쇆Tw):W6lӨ&R]pΖ#u#ʄi7Biuv25ҹmtCiUi_J%*2= q.Qm[Qr 2hA5RBRԼF%9 aD&ikkye떜["& Bd( Պ h:J9DpzD# ӐӔs`0.LvLFT 1c2E9$vgݨe<>iK)^JP{Q%>!#nГHQʪZBE.1&UPMᲉˤ'WyO #˽GD/ѥ#L Gr+}ÛWˆw rs(iDp!憈n*1c.3*-S4u NP.ZȴAuwң,+QdeY ZmQ~Qh!nEH&U6W[tCFJSXS 0~/F A"EsgK[s.$qwt >vx*x sy+p"q{O!H,&}]dH@c fxQ"&R&hc.ԞÌ c?٨ƺ p,Mky zBPd`8 )P',œCʭU㣩<7YM71fOW^8; g S +&s@!ȜeK措}# 3شUU2 bNNA|UI1o"vINXdAC M #k[M8(C7k,XDd؄YjSL@jKz t2ʰ2 faWV e^DoEO"wc.eudzfjNլ5Yy)u/.i 7lA8V|<gYQx6t#d>uŮQ264pE6bC0##/^;Ȧ; tThx;[hҰIPBDg66B_6H%T;ꑨ%'T%3O6Q  t,[!L1IuOge5nD-V7X7 ona ?-ka(Y 8KgxqfRc;.uBcw#=\;c[r t(TYjHF/tԥ[aFi>[lţ;٣}5-XKA]fr2>x^^> Mz#4 ?qjɦ-a<|Pcȹ%]pѻ3.stQ =Ot)@L$Mi"P -D-IpB> &5*u0i iͭ"P2 WtYfHLC S49!`L L'4g)'^hN7 IYH2T͘ll6҈.GMA&݄$>X]a5#"jY"c8[H<6& 4/h=aq&QGjYU2I@R8*۱ƓXh'+ձZ`xԏtbF}pS+>61# 65T7%6D1c$BD_Cr] P6V9âԆEws&NM!g\udHrǔsNOFD0ZO#Bl&F ="bNfsf,c.ɏRmXYMDU0Juh~ 5̘'.U>j(R(we?Z@\z&\S 9)}{%fSl$kaH DeEr Ҿ o4ް<4Ad#O-:6z-Z(%ac8[H$w-F܍HȲqgBv3BYDCLЀ]F6捋 Gtg$en&dCAD=Aq #M}/io a@`C &~`Q=pȳ)L^s&U\ SI.CxveiMKtZl H]&FfiX&Ga-RxZfR@Bv6b4ādQ8dk1(&TM#6ۛGYzUWŜC$RD"1O2Xc 66T^VZ^]6 * %f%dI ,BBCj EJyΘosx)\Oپ=>{xAOhȜPHI֪ 0;y32~joLW.&FjHb#][mL2XmH3nҵ,q AiÖO}#ahreѦU_ 4Hޘ;7ՏaO`0-.Ӈ:|'N:5=3=7GW[d2:Ip#C`h!,:T\yߕwjԪU+''LMX1!p2|%EgU2ݲY$"%S5fVyeh౶ÈtIʲp@GrtFYƮarY?խD$A`@9N1-6FMҙrd5eQZL1oUHsA#_#iD*x3(},  ;4+vS1j=eW/-ZF-ۂ``%P9)#Gxdh9'2 r5'Nسgc=~aAZmEQ|$[ޱN :-̬$Vڶm眽zrF\)KVVPISv(あtkws;=YHܺ.xVK"ؘ/@o5[5!"XGek<EiD pPff1QzƝF!9WmI1R9;;35OZh1Ѿkc9qDuW[j|P Bɹ)eL-1Ӏ=rxdX$$%Cw~'&׬^ .ccORhG "A,&G wb1<73}ɓǎ?z:wSq Vԗy0o#k܂[njט0TDၭN9bp G 4vU1n4$Qk&X$.2)ppN.ӉaC$ # PY"%q d}w{utUheEJ_6i:!l/ %1b3 %vckA3Q֏^adC(Fms2ʁ퓿Wa 9Nz K7{|۶6l\jRWR+COn3 ˉؤqWm|nI23uw /sw+T9k@69T#K (}`l >Dtk:$SlXХv5 +قH*14 dcd,Ze}g(~%W&K JU{S 44Ftb,f%ʶv{pM"-Hq23(f ,N* R]!R8U-ƬwTҼD-In5^<u~=Y}lw=z[V_UftFgfAVX^Oxo37f6bo҆٨MוY7:=#'O˶mA1^<$Ш2Dfm7'mTڶ\ʹ%m|Z&j^He&.G( WLȇ]th tM)&3L\-imKAj(@PK`v0xV 9wʨ(8@۰U)c)MPINŵԕ_3KS®k{֔aZ)~ K}gWfakx $3dGw:HaOaAQ8 9X%rjZD`a6IW2̻2@DKb4i2z ۲FbTUHԴ L,K i旔5n&Y5>[:Lr,Ǝa)%IXd4L=ώmSKA9ɲN[=,vu oU}Vp֣$V;\m>PfPSZ"])6 ,@RED#e p"nrᡳ&}Zs/I h",rvj@T^F\Tz@I??5yZlQJ]Ը(]uu'DR2Ǧߜdn6VJ;㤸B?$e2`$KQts.zFecFp@1] ؑ+'?T!lFP̱Ύۼy>YILټyDv?:bYSF;>?|¿|CNXaQܴ3+3 q5ohLxgF7l޴RZJyS=?>67׼?KwW:[f%,3(y#gPڔgPϓ 1p1Yxڮ˴GӃj%rTf׫mi`_4$X/pn`ʤN3YBi q/-:VR$2K6ts9T7hz I씍,N&L7 ћwtd7mL5VDY:r÷>ʯSw~;_^7Ѝ|[9/}{^uxs3RB"BZ#_:MKo?/?"Ckx 33_|}Shsݱ ?tyc?ܟWwld3~.d+(iJ+N¸$YUs-SJV~$-6,ES_q$PѺe?*7YNkړJ9D27nG%΀u"JnDuk`vMU╓F?*qq\8ȝ>&!ˆIficC}[_ صeȗ/wj6{g>;^cĝGzѯk>}W=4f;>ޏqrݥ/y.?o撳u>1=Q=o߉|U"oK:'pWy7|o[廢S^glw!Gl^=z[;gnCO{k_zֱ'߱bR7 \F醢r`cgb,EDt {DuF \hqRk~W0ڠPfd{;=EݎDjFѰg$-ti^u 9WH2o|sS-V\+Ǯŷ`sWo޹?y9|U˯&0uY;<]u.l){2ڴdin6ScXiB9B #ݎ2r/Ø=u`/Ǵ;>!|+N͡~ԯ'kP!Xq"lN3alc؋Ȟ,e/o4yU5X$<  S4ż72s`hQ1|id҄)FOd̼N@5Ko.ۙGn}ԮRm>4)W1ܥ5ܛ?qǏs⩛wª+qņȀ\U/}ꖱ^샟}߇rhS_S[~~W\*>^9r?Qm5;k[=~pwք3Mss'o|]ϼ5|{yï=r:}{gGٵk{/$"ϔIS[2w T+Ňl*:n@T mU'::W$}>Mً%|`u ,M|umDO28XJ5ļ:K<EbSw\ќ !asڽ5p L˭ mFbT@ZG{ٝ&'pbb^е#4k,hy;^EGVz^^g_sז'&xɧPOL/׮| R3Y? F%8}Ǿy~;^twzϱ+Y#3EYtGj ^pҴ## Ow}7}y_?}Q%0{|ol>a8 ;xX*lç͡X 3?j*1>Ii@Z =|!]b20a.Ve۶μHKEr ~d2m@ei W ]r{#[*)pH+D%hSeNz,Vil+,rMb8p ±W E,gsdϗ4nlӋVlXAtLG jXoEUg¤F^wrT-{z]oٵV 5#OuG7~[qCG\掯޸u_6+LJz>08JaRP ͉S|:֟v<[3|s{ӫtýE}M_^пke5ok97'n-/g9>uPFW긟;~|z:'Ea ܁);5ܕҘK'am:֖$B[*D|[)Բjus}$sj&)Ե)C1+'@uėVvoS-عeZe6mWI79Gu,-$N" ?Kt4єƂaO#S'O?qO>5;7QTReCc|Ç7rJ0r כO9C7r{$b:/\FHܘDAwO=gn3~^䵿睥Wpito} V}xb˦,g; [wrvT&u5rjIO};|rax媉J&6n=iM}{ώLm8%gGʡ n#Ǐcmz5^idz b*{d՘*×楽}e!XvQ=e;xX4,H,aNh ~R A~Ϡ鐄ETW7E$ftl9$ &Nv3lhxX/H$_Y@aa68Ӝ"]1]}:[.,o'K4&8>t{Wŵ,9xpHwcdXcC:r{&vX= (QkD[p^:/ݶyDCϿjw|{]MKF{_ Wz{>pǭ>:q^u䯸t%.xK.w/çy}ݮ5=}{֝8:z%Ox͝w~m9Oql뼐&tŮ__ɝt/~ꦡS??__ywv\t{=+/ݱi֊AUg'=|yb#w=!$vgAT ٦AUNwFđzdxEʄ53M(taDǒMt$Cۂ9wMd`rf(r` Rr)5Vӆ$`ރ=x9aeCɉ-֖iWO-D%ԆcH_jҗ%Yqꏮܥs[<; 3w7dixUolk-~'wި nݳχ\dKy>װ@Ks'5{wz̉cs+Nl3Z@$8}aݴONMXC[>v„h,Y>rȚzO~]uEыӇ[i:͟<|lx:,͟:zꙹ< ӥ4sС3Sk'm+Vfᰢ:(CF.ܫ&ǭvH )zjXFaz #iqlמ4&,|Hx8X lMG:v fdLE#8dijk[;(GghT7;A+* 0?r T,zx眣?c``jZ10Sn{Vp{/>IP&f쾿ȒdfVb8t⡉{-}7qwb]t![:һ <7bԁ/N4ډcϣ<xUb UlKD pV)#i fl"⎺jnh\2KEw<̝%HY2,xEU#=-Vi$U^_COTu %%tk>%#L=U8ygM gG0gQ=ЁטCsjgJņlhrn@>3q&4$rpCoHYlp[#OJ/ [.ĦŝkGUؼľyi$<2vm: 6iXаSUfX`pQejKÜnF1h2'6l JD #u]>zs[d蹉Z.s=.Wz:uo;h=ꬿɴwZao;_vc ['5Ʌ|ge&SuœQt.bg2A1AխCDA^S װ$"lh%oS !H!">졍HI%3aX\b)]#VMNG[z],]l&K0@4[AP c.۸59Hz&C=l D ;U֚`%2k 4\+@r( V0Oh8W֎"&) .E׮5xױj+azRD*;=:ܻ Y3o&8j%GF6k\Z-_a"$7f\L+B(KTa\8f4(mЪoVKIpd`.tD"&m”Dg][Xh DfH/jSEθ,n2pIFXUf]Čj#!1e]20!~$ẤΨzT.E f,KN,%X <"Ch!hYS(փ:Akxmo f_JyBNvV\3ƶ2.D0+`X}An9v  aVmma{ߺw萙5 w?͡L]>څ.э<SMrҸON6Ψ m 11OUԁ&$R;SH*fv&gzd16`fVk#֪^e9-YVKjCZi#P!` b'1?BVbSoa$DcQJnPGqA'mp5C$-j4brKE6hGjeʯ0K5Vf:1w9*'ԻA h{@[ٙSz#w,.sST,J/m>q><NYLR♁-gnhZYQ[ZY0Ƽ[ 3„uSOF4Lb $LEiɾ8 TI&KSɛXG!)fM/ ;gў@IDAT]O:S`DkcPi,Q7p||P@a +Čҳ `Q(сr.bϞt`4blM,$IyW{ɐږɞ2 =]a)`#O+~w饽U{]g7>߉ 58VG29u69DH$,>L'h9hfL \ A Șm]z6 A#u@h,٥o{ ǑD_tmKP۶z˸8"&3qlAxd-=]Wr 0љp炷h/\zfj‹g;p 4K\8ϯyƥ #2[CjU;y'ߝ_fh7AJ,=$< MJcO'J5 J.wž/׿מ3Za$0񸙰KpH?V\m\k2uFOx2oL2W-^H>ҺȌbh:0(;LAg:NCmTbuVC^L},s1H)U݌Ȳ uDAkevyV]pDB ARaɪϟ`~?2?r˵oz|/oC77 */s6P|SxK{ w6~AN?忺뻏._wZY^Ζ9Ԝh N/Sz dj9eE &m2OtTN#k##b@cUX-b#%.Քo(I,(Ygf^XqlZ ML! 9pK!l]Xࢫ-: ^Uǐ/iGF\l ]-["UCːKJ=V c&VAR/5-L<6۾cq]>ǷZ=\z|+ã3~i长]?ܦۇ ^_pszc?zxaUIu{+7|]ݷm[>5=D&}g~y~}j]l?ZOzٗwX7eÑ.G@G|J֥l^;o )ppPބCX|_@0r3yQR`(r(1Y QArmki p-xN4+&9ͨSb MZx掭^C#vbP!Dg:X',h/%a6=Pɀ|VᕳCJ'-ǘӀ.,46/)e2*q/L b-tGJf)Gg*2eee%qP.EFqhIXx_r'!ީGN=rC|qvSs?™ k|؝?|.xc;b@9txqEP8}ҦV1i4 Coܸʓ+礷4ozg?sg=?+7Uv]:it Ac* "Cn).9tK$Bˤ3 <-(Z>J7D`5 )C+!@lQ/UɁ,HcI^`ūtf#K w̰7j D\'|B:t"ikLtz` +!zJ'>$Fľȃ.a#ڦe#+[EcG+>:}CWiI?GS}}xxӋ?xZx3*%sHxc+.~dNUI?=[N=>u sw>C>kǸm 1bbRe1''O--YSF}on7so<ΝK'OG_OO}/|_wekSn/S* Vu>e{-/OBؐ"bZr]S+̈^c>lDp rq/0%>p(/.uRyPE ;E1"j6fZsOjdʣȦl\.W-[@Ss։d@O$;rR+*f _뢫P=\Mk71}{@Y7"^"D)&yͅOY}-=r38:Kf?o}K֭~y7~_T z۟oW - V~ǯzr}q+u`_u[6Bei][o=~鋶r̉HX̬Rts;ߕ/)+ #-wu HQF db,kb'aAA:+@ 9 2c ,!+&T.U>f:5 ǡ |^bZ@n63*[T8%~NN17`6)@3Ƚ;6jrI\Zp;*Rq:\yyd18ljOB`)ֽgsd sS |vr+WonްyuF>-xu]k޽A r5oxZem詭7w|xW~t~?>sk3q˯ߺ~Ư“W~g7ްzX?]~G6_[_lk|/@!v$k5&XjĴCX}]’a_}$s(Z V<@uw")b T>'\aM |'-N\GCp OH qs2acevD!dP [UIc7m\-?8CQ-xfZSǰj ?t*T&֬YvV(}V9U녅=9oNZZKpe'Z}cna3C+׮^\a72ʇ#>7U4õrA0رᕫ'b-NK/Z+9z4wfɕ+Vʪ'\kv1!ӎ91?2vJ 4۸&qֵ@қmw+2='760"!+ÚrF*D*o@R#dBH.̭Z% u Ppu!f=%´Q(@ .' Ӌu [Q5a/tyIs)Œk[ȀZ6%V"&V_[wСÇMź wk$ݟ*䪕tt\"=JHu :ҍjQ:No_`-^iR6Z.)h &nXFV3|o &-cod庍+2}aKCZ1ba"6Ld.VҐ$!X1Jݗ]5儧2 rpc$brxJEwh0H:hmFEӗFT˜r?ٙӺO;eN] KFjڸxB(l t8TwYRϨ୔Tj 6!y9ȥɼBPe;p7_$6Q:;lgߴEzU-k7$!Hpc4=q"4$%\)kK)6o j>N^K"P^y3؄ Bp2%Gh(c$Ny㬀?V&^c5zZ\ ` PZ] %nn֭ٴiQ}?c=OY/Nn6Y"8)w+WNMM_~@c00ao|fҔ8uâ3$ AQUÐ2DG6P2q1`Ӳ/(pTD3f\* E0J\}Ĺ =ϓ$\ݨuҡ@R.vGeKdKmZ AN|X&`,;0':$`L`) %׬7{P hȧb "eC~-ndj]H +UsFݵ"߸*D-5,‘) << `ILt@0 4\ڌ?4:ÎtuBMvXD!& ຘ!|O[bQH#h@ h뫏OBx]W˹=6R%j7 {F S#;`BZ"fIԞ Q"\R8윥5eĶ}A+)1iO%6nueeU.s)r7S V:%:rf!Fp7dTÊ=JPčM}{Yii3>a1'"=@2#MRRMll8t@"374Ȓ;D 4#Gf&Lc2  %KCs-[ʊ 2j  9j% SFrןP91?yP匡ЯFPiQQWD̡/P: w  !OF77JX8B;6@b$3%DS*3aɓ8J1R)BU";Fqk h_TbH$B5Tz\z 'qP_h񶃃ig#(?!/ B܊R9+2U%2b!]\*T 9 oТ%/j9/-pyn{ ] x3zXGa:e){ݲZ,Mt=;~(0@Jc4e>R@X lB(V?F/'fGǛz3ܠt\GUL@ `@6^#Rþ O5GcJWLճ9GH!?Y/ ݭ"QuN kOHJd`<|׍m44kjQL0!)c ,7uiy+eҋa}%:jȔj\A'TI %M>6& {=|}$pML@H 0ǛFaegQcs&CP B84ݘz@8$2=@Vc-0p{z9Pgq01WtddsiM ip$AY GHH i`YӖ 吖a!b!9|%VTS;EI{ė=R ebS-hm)T@1:2(z(sDrZTrJ9۲b ;ql>DQ jZ! ow -ɢgY8uH՘ ң+ԁa53.eb]iWPh=C0#~d8BBEBT nFjoxz,1AW):N6EAYxX2U@0"T[GsGlCbs G; 8/&Kd}QZ# IbdZD6ҞZSA9҅S3XƋ'3"x7D*hL$3i |KŇa uzX{&\>T!TWj])) H` Y[a- _?gRBufApNJ~#^>C@ 5rCD]m9`"3%;.j 4b*㉅hLY 3sZNm "Kp<nSb,ƢtN=\M:<ܔH\Rѐ1 FJ¥#$"pÁbAkʍ9WMe0!xmHU!%-5Qj;e)[i(#4\\\AلE,5i8F# Ր ¤diXoGoi@,:TJHX  .& Ac h-GHPt^$Lqdq5 ?Bw<y)T.REQ*4AhvTsa3ql͆PAFRupe%4̍x} XfO?r;O؞H S& ]%'J'ҡHJܤ8yF6@Xm359v(pi8ڜl+c:l`PvaDl((@i?5;ibCkDC<};\cR" g[q,KJgcцזHv A `3C`"600i-IDJTNuNľˀF@i@lD=B囋fP6 1湊i+9E-w%8VΩqT)Լ'bIoX>ɐpbԯ9u;IR+Y_rF^f69qѴ5"5kJd_Y?Gcc؅K1 \C H\R sX39u';*%XpJL\f$.]\+}bƊ" A-U|\garҥfĺ{Tيtj%H*&YmQQ)C@؆q4`?H͆&-`Tk%]eL CDak~@̍~eűttò%QHOcessVE/xW7૨pqHld/@ 4UB",Oeb16U6huϏ&"c8Z:;fLZ)%DZ5hTYf\C@SDW%X@K>QV\Jm}fa^ކv ꒖h2-dJf"B#Q}ΆT*H8ض^b8vT:b;;Uh\3N B Oa{s"ܒ]rJk.0 ut 1'OnHUB˿ڸ }or d$rDFD#.B@y <-[Nc: !OV@cngú"D%E$`-C]^ Eq O,<0ܚ렂@ȇ #> ^w|'4 g̨:xDHXxN' W%¦1~f)ICR/<ž~#0n\l0R:r >vN]DƠ. ;ig80&V2Pc+O9d+Ǔgł,d>6b̴ kd]pE%-K-h~}8UimTG-Йub^6jCQjgb;AGV_51arf7&Jп~!>PC;zi A{&[#؈(.1AT  RZ5J0Ö_CNP,3F,OM +WMNq"$JR}/ū並9$\&XeM3o0rqF6 *C̰͑ Z7L-!/SӞ@5º2GW^ G$4 Pf5WrDI#`DPQWV(8,GJ<w|iȤr3)AOR=قq@`=./ R8 F'd1oA4}9!fPo^7ktT L!hAb$8w @-SI0h^&ǖP [[r^SN-W+9G@NB@{6)v4!A*:۵O~R"[PSU);8z5<`!XZVcB9/y*.0;gtµM J287 㿇r" ye!D:[w)tT˱qbbH*Brrޫ䉌.+]] i*]͛r,d5ɰFSGxYm'oHV̱7I˶N ʤ3Ӭ2{R4ML"6v3$xFqxfa2\r9qf, |鶸rf޴cjN&(eU!3sz @z #׫Tg 'prMif.^I dM(NTfZ y` \cJJdXwL<@\Gcuݮ>K{N=[VCTH$rr4Tihj%/  !KE&@,@ Dh(Hi{-lN{z9ws}931ﲾޑC=ņiZk[a氃T\l>7 ~I3q$ˠ[p'-h`q>M,1@|9l\ȭФ% 4g4ѫ3ZI6Cn"ʬ2+M&!2)I<2A- ďU_л*!7_aIB-$V*SkA b""#a F)1A/g[U&*&a{Gr?.4ty3/'_Ւa*ڱ   $s7Lv kp+fdϖhYh6Fe-v;)#T`!4.9bd -#BBL yv%e^Vt[Ky} t(}{L/6I|@}]U˪ѫ2cH+2ND]r!q_rXIj'3qȆU,$pMARRE%]0'VQ~ .$k7Qhy;VH^ieӖ0d*vB",`4Q=lwr˝daٽ|I1w8Q\r3-xEHa8ϮY'MIj2SU7ž2bdPĂΦ˯ HB$)> <2(&/!Fa9vP)ԱK:&{E>M|SQxS52Ԥyfi\җPpEg@99N0>צqSy5?|1Sٸ5Sɛ\ N84dcLd[R+1"`^J|v1s>Y:@$ǖYXq S[my1 *UNQ;n=Q\>9H28C{[&tDޔjṵ"EyܚTm[hGxU9hdt VY{\!oc%aa pn IhQfȂ3nXK'6qg,7;. Oڄ _ C,Y3#6*|Rw a(U0mb8#@PWq Q0-5dVaH ;凲_ApQP^QHAN9efD4po/,鑼/ = :Lg3`M<[Y6Y!X LG\o܆UTQB+A%Yauʋ ur}JN&[NSu7k![{F#% _b,EV^'-xf.rl$,Чة -LGPL0;( |+ W |v{p5͈^X|2 ܴ{@;ПD=x,{I) Y * XހGK46A=l똰厍uk&SpņKÈzz,OL@]11&N`ή##x[xd3C)D+35ҷJ",J!;SRso˒^p3s,G#LY\D$U(Kf$t҂ 6!jY{3BEF!e!kd.VQ⎨H68l#b|;l+:G;Mw` pon &lW[JwS1(OpBb^_AvE kD֘1P S` (_AZ,LrTV 65!+FٜIjI^C(!B>1bX(6# 7Sm2@3'w(*v,B^0&nP96rP\LpH%9 V\z2j攐E9MX$m4OiEƌdp<7cO0 M '!u!'*3l)t:4VCY@H7Z2f9jups`Mzw6=) ƕ48?' [aY&!t ۂ dvhz틖E~DAPKbR[~6ubAI +w 8d0ȸhO A u@~ M{%  1}Ќ! &'؎TLb*0QTdã#ԙrjC .gNf3 F*H>Ό򕓈v~2 gI7FJpn$j _Pktn,g6ZiΜ&cDdD•<Č,.& J *>Ne 6k9eթk%C&zŘ. 2dgnxP tL>+@yaZ0q2xōb!;̠”âh=r9r6$'˪#uXUxN[-'`5 a8,O_ыmHX;4- k,#\;۴`*>S*v QfSk,^ŜQs$0>N! \!.=w1rZd ۏ=A# vkB:nD$ܹN|ڋcgO#yn' nKS !~fl&K3dǜqu,-ń;b40Aښta Rwj 7P:Q);]pԬDP Ƴ 3YO'\A+/(/$*WhTǑ"ڋnl (ZصGV8\.~' ϴXLjJrS>@k7$IY86KYdƴ$UQpPk=TӬ8cE&M߈Eʾ b{R u ӣ.^C`2(E~MHfMМ=0MDK` K92RhZV؅[ɰ2ZbXGR.`f-H8ЫTlhܩtˠ'Fnib 8UU^pTf cӀ`fXfd .sʘ|btL$iP!Q;%GNGCB`Jܛ;CdÎЍɃi]LJ8|J`yضA>2MOŔ$eTQ=sqמ}t#M*iysf1L/W'MLm+ưՑjb Fϸ]1BcǍaD7,9CɆwr毠3Uw Ki3Yd32s N3N5m;%Y9YoW: ?[Wb Ѡ`k4e˖ǥ~cO֘5WcДc曤8,+ { UU /7Af.tן~tي[l4S:I~?/ɿ! K13m2*Mv|6TR8SA*Vw8v1[,AQ%8SZˌ K+`;E\ ƱL+??O'm wF_@c5bdv_(ؖ%T$I֔ir0ۘ|84OEEJ׊r-::'jerOM?᧋o_/J̩PoE`rέp[8qL}2m7:A4'(]M+~.JmFX [ JV-z4mq seQJ\Yp3ϽlAC0ndRoFxئQb /#taق| ]xܱNO>؏h\!HbI;$IJ pfc۳&qdk tO|k^zܸ7o}0l* w`UTMp`&sN}]A2L&sɸ_s睕"Kk܍>'/]^E$Awٟ|M?[m/| _do|?o~ȫ/ֽ$Sҥ}ԇ˾~?BrtTX f7j[hQouE`EJ} .Zv". oLzij"us0ܗ ֻ!`js2 n.ˤ3-Lr&GƻzmM bektBNw,"NEJYcrBsUr*~{ѭ0;N֕f[9&^`89}yΒW }8oڦE -U.0RicbĹ/tc/|_/xg?}WԽ/{gm۾y<'<_YxxOsهՓjTtjuNGfP=ę<3?"*4LA+0+}D"HA3_@4!z  )L6;?3#O4qNuZnL.s}gfyD.x#rC`4O+H>_-%!Z}e2:9Qfg󲴯 뱨 /ʋmeJ(yťX7DƻemHqCsIrKE:4{'ݭVKx:Oz  Gp zITk\]1o$sJr=zZd B;mROqLX8)Lbӛ ^:P+W#MSI_Ⱥy$u7#7y(XoV(%y*+AUХA :e6'u(ڛw{¯_杏|G;/__/R>txWk^}w/wp0S n^z}?]o_K%/3܇~]7}v)5^z{~ܫ /G]|\}ɏ{Mgv^t/?ߗgG#?ڿYi9uoO&v{- - Fg@ֳވ.;?APdLn[⥄esEZw՚Ѝ(q̚ǚaD #MGh/n4} %yoi"{A `Y*؍j8>傥-Eo~D@ 5  Z"&|^@cATMg4 z*=B?ܸA|·>k_/r祿Ưد~8y?y~7ÿʃ\}W]7/}՗/[~Q;HuW_w#Ovߋė>3{UM`XjQ]ukZ1(9.3z0A ຐ%^.дq#]y k58Ӝ(BђHd-r, gdfJf8y`POJGЁdzl:H>PV${~?zD(,& G{?rp d<]vd--/謥"}HZH*y3O~O>{{| Ѝoy߃dOמ={xbUzzxn~Tg|/=x O>/N[&gEd{I, &nS2hv ϔ9ǐ\,aNXSp !V RN-1`YTۨbTg/%s|P$C7<=sk2 # ,=V x[By9TZ} 2-=ܴbkUUTORbEeZ 2Io54-3XTZpPHhzO }#d'އ6VC2B :MֳL0N]ˤnҘ̭%5[Am=$PpfW^ 9\Pyru_a@!PřhBΑAr,A$;cvf1Xupd{t8ذE;ȼ)lZeF2 +x%Z+}L+9="4 ȉ}"0L=@wvB$:b jhTX.@и12ksL<SO,6LγM^Q qbcXOڜ eC#Fxs ̉eFᷙNE)-lt>p )pk\D*P1.F ԱB%1JIe&"":,;Ε 5DbVfkӻ8s.lJS0KoxEr3^eJQ䑺I-IGrtd"Asa6P1m17z#VI$͌"{< s̊J–pș]nUkM( LD=aZ`GXexcqAF!ɶ?BVDӑ]; Ig)H ]eJ(K+$8fm0s=w;A uAfԒV (S}ivztqKJeeJ2}D+G낈ULۻo`saki>cR,ȩ*Ь\PJXs3<[PDAA ’S\ 8JlkC]YCht,[Tubl^KKdnEq}KlL$c)Cg`Rڦ^>dO;8zW[v1whW!8FA,mŠaCʹ؝n"(&njC9 چq=ل$XXK=ր(/oK5f]V5bB씌d 2,F9ǝA:cI6+@'D UNV8ʝ9ܝsj`" g[X=D&Dց^bR4 J;`.ą m-ҍ+01UVlJP"38-MBmb l.$ u̎ˉlYTKɡp0%5㨛r&a{ ? p(y.IQj|H] 66ҫ U4ȒDUPD$qeXf 4lJ!/0B&"E ϔ0^k2L%1&v*$&& MbUAu{Z (n=Բ{0lPZ@0S\g) B?vMl|eQ4{( "d6G Ӱ'`&dJ>af醑)Pptdբαq uS"Kdz6UrvA! xë7}y/HKԍ.&'(<ɮf0d>$JPv'su^ 8|/bd|DWj6#ynwH| $_CkDb{agAsP>SʈKpnS2D~SivrYt٧^{Wǟ)uI!fa>ԐU@f[DFI>_T0r)(" va Q:#!`d3enx -\!Dp jdI<<+"`p0s\ۆq@#!d=${b:XS΂l=,Js%դd{ixSoa:Q`$Ir;4Hl퀛Ns@zyk-A+2;J5MA,G`0.q .14-Dy {-A{xfjAn+2,CRs*jg:⬲O )Sp1qdCa=88%!8 h;Ю;$mVyG4O`i}4 )Ġ$q._ni`^;x -F>ɶ:%#dt!؋ NL]qȽ9݀Ly;8 jT1EsR :Fs 90hH]a6>0{Mɛ`N!<(bBivt; ?֡3% ;Ij1azB%bΠdJ4^MDz4Q ,<B3*򚖟R+Tfj6"U63F,ubp~86>,G*Q+jZDT-(Ev_L/OFˤdP&#mz̯&%fӕ|hwD[\3f-miqsI᭶XU4i'Lq6߿bCŕZڔXʈZ?cz!(P6ob``0͜E a6gӿ4PNҘ( Lq8 \wq*[+=GM`Qhxj 2xpTif1 nS߭6"6orݶ9O Ή6RNzgZ7 !l{KV?1{J!g|y.%R^ʅ'3]ĨZ\s]31!3b% ڌH ',#G̀oN0/sb c M ɉȬ{(H8KAKBLSI ڋm>PE4d6xP[c(#<"R>%{Hg̱zk1LfT3Yrg+1zyQ0o@HHNg(4r>ݰ lj' H8`12 C-* "8'AÊsR0KcN3)\i#m⽲! fcB |&p醙 -ZV9\r!l\) "ÀJ|ZrTfr7_шD/,P.ŋ\#4H&]/ܤTPȊ;8{hFd)ʌ~|A|D jIy ֬P'BfVd# zDr-zxP^C[^*fZORx=//>!%JR6D&0hb Ux%d4Mb,;D/Eg 猍9nZ% j]Dhk5HeD<'_E3Zr֥V`hzrAnxyZybUk\LD U}Ly'`(#OCj${<3<`У"(~!h2ȤӍZقLVb➋7iޜ'0dzWـrOji̼7F*,Jn&L.^!Q`JugDc0<-R=2As{®BuBt tRdd8ƄMnvX2$.A9,3,_,&WKpFf0+̱esл΁;BAQ\ΪJjBY~8;&5> UFrg]Fw< j]S@HVd6M *|+lʓS&a1SA#Q}8'eŢZ~D?Q\"TB鲘<~RX~.@^K,.^ඪJGqP촏 c Tl X],vֳʭ֜&hu"IXA BA|k_xH |l JdGKvNzCFHh$ʀc*`wHꀆk˧[9 lӑ5U ~Y ka &O9r" {}U夵{9Is KTՌؓFw`6Zb( K ,|F"8 2ˊ?ēH^9c9-F U~4YΩl̶(qO~<Њ\8K Z5]^ƜW2QW"#rqL7jjWqNA{5]llvJfKs-KN=륊3s7jV{/q 7b72XN6w+<|dc0=y!5&PzBqi@v7+{z5H4Fe\F6GRJSDW#4nfERH(w=V$g~lnb 7˛땤yBQB97=(26{F݄a%4F 2b%_3Ci卫 ,l{ EhzœQ_; 3iE``V 9nH#*W#8-8zV2:cL'BI5ݨ|C u&4VI'L,p@[Jxiωrv-$T,YQ9 cw {}uϴr2XV%(جՒ R(s+y3sxpP-:(J '.Sfapƍ6fێ'ʫs(P ԪAaO^Ly&gre*Tkj ~LY8lD5LLorwK!Wf$^.s lM9!ݳ[4H:3$ T3eZxLЬ4ޠl/K9GTexpxx$@ɕSՐp|G?=&3d=b-U65 6V v<14ll$UiTa y^Y]H@Qf̆< Yf  v4LlQ%dB7-~jk57ٳy]@SV^Cjza9ryliSM TFt2{a؍o+k/ U%"?rNޢa*C䉇48mڔ;eؕ Y1'|>Q :9X8KP+Mr6uW^mIf44&vHW<{M"$:i2s#uMBtp ͊Se; x2΍D e85<"pEf[k>}0k# Ru#9 2EN5 ` ьRBs么Ws9T +W2ZRu1O&79|s@ J P7ư̦3 o $,oْJ&vR`M sgnl/ /$4 f'gL`Ll >XEĚKX33=Pu_ i>Խr_T" 0_!Vu{Gy1a: bTa Oܰ )t0qF'Ci,Kт ׫"{Ӥxmj9Y-p0hJ=7=(yġRW<0(}J3RrKj[dTGSl!}mQ5$,N˝+ZNV&vA9X t2xG s,唫 {a'[&xɁ[L.)tvD1ԋ/WXۛ2d /:k1dp !;)/}e&k3]#J(YleUȊZ1;P͓FYDd-:TR}@E wioW;0F_"4"h(瞬2|l;Cnq ihP}NmR`' D9HB\F7HCf("y(h"F.!—I !QaFy.'3icp5+QBW>< X 0>8yi76wDHf&x:1^<4 ѐJB` ~>IPmq |E$gU Zgxvjie)8B&3bzv/.D!T| Ovar3E,{ !JPs̝iir3VvIT G2>l-M QtD}7I;iL_˪)Er`fHJ# L2h|5Ckh<)A'#j |[@~D|#_&*:r)c!,S;OҭEeSFY!r Sf#J,'\Z'.b`{}bJ2MfKs!+GϨn^9Q29ܠg 3wR]fY̻2ͯGKjhZO )-#/\4"FԳya=[C~mCA>j4.3$ϷDBF$˕:o*JvK2;l %xEHg36 ˻3jE2+AiByBVS-x0:(&plAK1?cOKsQq5oLm2n"|̕ c/9l4kr{ě%S&2Qy pf$7>iSTXP. y<3-;Jr==ϵmp[6YeQR=VO9*A-6R-S%< 8h4\J¦A\j]) 4 BPёǶ^ \t~؃7,THj#+"UDd3FY6;HxYIDATyTg>Kz^.b@I.zd qm6V.?qGVjFH g̹*Кڨ\O?(1yqEqIENDB`./share/qtcreator/templates/wizards/ubuntu/scope/src/preview.h0000644000015600001650000000125012705421114024733 0ustar jenkinsjenkins#ifndef PREVIEW_H_ #define PREVIEW_H_ #include namespace unity { namespace scopes { class Result; } } /** * Represents an individual preview request. * * Each time a result is previewed in the UI a new Preview * object is created. */ class Preview: public unity::scopes::PreviewQueryBase { public: Preview(const unity::scopes::Result &result, const unity::scopes::ActionMetadata &metadata); ~Preview() = default; void cancelled() override; /** * Populates the reply object with preview information. */ void run(unity::scopes::PreviewReplyProxy const& reply) override; }; #endif // PREVIEW_H_ ./share/qtcreator/templates/wizards/ubuntu/scope/src/query.h0000644000015600001650000000132712705421114024424 0ustar jenkinsjenkins#ifndef QUERY_H_ #define QUERY_H_ #include #include #include /** * Represents an individual query. * * A new Query object will be constructed for each query. It is * given query information, metadata about the search, and * some scope-specific configuration. */ class Query: public unity::scopes::SearchQueryBase { public: Query(const unity::scopes::CannedQuery &query, const unity::scopes::SearchMetadata &metadata, Client::Config::Ptr config); ~Query() = default; void cancelled() override; void run(const unity::scopes::SearchReplyProxy &reply) override; private: Client client_; }; #endif // QUERY_H_ ./share/qtcreator/templates/wizards/ubuntu/scope/src/localization.h0000644000015600001650000000100112705421114025734 0ustar jenkinsjenkins#ifndef LOCALIZATION_H_ #define LOCALIZATION_H_ #include #include inline char * _(const char *__msgid) { return dgettext(GETTEXT_PACKAGE, __msgid); } inline std::string _(const char *__msgid1, const char *__msgid2, unsigned long int __n) { char buffer [256]; if (snprintf ( buffer, 256, dngettext(GETTEXT_PACKAGE, __msgid1, __msgid2, __n), __n ) >= 0) { return buffer; } else { return std::string(); } } #endif // LOCALIZATION_H_ ./share/qtcreator/templates/wizards/ubuntu/scope/src/scope.h0000644000015600001650000000232712705421114024371 0ustar jenkinsjenkins#ifndef SCOPE_H_ #define SCOPE_H_ #include #include #include #include #include #include /** * Defines the lifecycle of scope plugin, and acts as a factory * for Query and Preview objects. * * Note that the #preview and #search methods are each called on * different threads, so some form of interlocking is required * if shared data structures are used. */ class Scope: public unity::scopes::ScopeBase { public: /** * Called once at startup */ void start(std::string const&) override; /** * Called at shutdown */ void stop() override; /** * Called each time a new preview is requested */ unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result&, const unity::scopes::ActionMetadata&) override; /** * Called each time a new query is requested */ unity::scopes::SearchQueryBase::UPtr search( unity::scopes::CannedQuery const& q, unity::scopes::SearchMetadata const&) override; protected: Client::Config::Ptr config_; }; #endif // SCOPE_H_ ./share/qtcreator/templates/wizards/ubuntu/scope/src/client.cpp0000644000015600001650000004107012705421114025067 0ustar jenkinsjenkins#include @if "%ContentType%".substring(0, "network-netcpp".length) === "network-netcpp" #include #include #include #include @if "%ContentType%" == "network-netcpp-json" #include @elsif "%ContentType%" == "network-netcpp-qjson" #include @endif namespace http = core::net::http; namespace net = core::net; @if "%ContentType%" == "network-netcpp-json" namespace json = Json; @endif @endif using namespace std; Client::Client(Config::Ptr config) : config_(config), cancelled_(false) { } @if "%ContentType%" == "network-netcpp-qxml" namespace { static QString readText(QXmlStreamReader& xml) { xml.readNext(); if (xml.tokenType() != QXmlStreamReader::Characters) { return QString(); } return xml.text().toString(); } static void parseCity(Client::City& city, QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("id")) { city.id = attributes.value("id").toUInt(); } if (attributes.hasAttribute("name")) { city.name = attributes.value("name").toString().toStdString(); } xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "city")) { if (xml.isStartElement()) { if (xml.name() == "country") { city.country = readText(xml).toStdString(); } } xml.readNext(); } } static void parseWeather(Client::Weather& weather, QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("id")) { weather.id = attributes.value("id").toUInt(); } if (attributes.hasAttribute("value")) { weather.description = attributes.value("value").toString().toStdString(); } if (attributes.hasAttribute("icon")) { weather.icon = "http://openweathermap.org/img/w/" + attributes.value("icon").toString().toStdString() + ".png"; } xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "weather")) { xml.readNext(); } } static void parseTemperature(Client::Temp& temp, QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("value")) { temp.cur = attributes.value("value").toDouble(); } if (attributes.hasAttribute("max")) { temp.max = attributes.value("max").toDouble(); } if (attributes.hasAttribute("min")) { temp.min = attributes.value("min").toDouble(); } xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "temperature")) { xml.readNext(); } } static void parseLocation(Client::City& city, QXmlStreamReader& xml) { xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "location")) { if (xml.name() == "name") { city.name = readText(xml).toStdString(); } else if (xml.name() == "country") { city.country = readText(xml).toStdString(); } xml.readNext(); } } static void parseTime(Client::Weather& weather, QXmlStreamReader& xml) { xml.readNext(); while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "time")) { if (xml.name() == "symbol") { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("name")) { weather.description = attributes.value("name").toString().toStdString(); } if (attributes.hasAttribute("var")) { weather.icon = "http://openweathermap.org/img/w/" + attributes.value("var").toString().toStdString() + ".png"; } } else if (xml.name() == "temperature") { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("max")) { weather.temp.max = attributes.value("max").toDouble(); } if (attributes.hasAttribute("min")) { weather.temp.min = attributes.value("min").toDouble(); } if (attributes.hasAttribute("day")) { weather.temp.cur = attributes.value("day").toDouble(); } } xml.readNext(); } } static void parseForecast(Client::WeatherList& weather_list, QXmlStreamReader& xml) { xml.readNext(); unsigned int weather_id = 1000000; while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "forecast")) { if (xml.name() == "time") { Client::Weather weather; weather.id = weather_id++; parseTime(weather, xml); weather_list.emplace_back(weather); } xml.readNext(); } } } @endif @if "%ContentType%" == "empty" Client::ResultList Client::search(const string &query) { ResultList results; // This is the method that we will call from the Query class. // It just returns some results. // You can add here your code to get results from an http API, from your local disk // or anywhere. // In this case we just create some results withouth accessing any other source of // data { Result result; result.uri = "uri"; result.title = query; result.art = "art.png"; result.subtitle = "subtitle"; result.description = "description"; results.emplace_back(result); } { Result result; result.uri = "uri2"; result.title = query; result.art = "art2.png"; result.subtitle = "subtitle2"; result.description = "description2"; results.emplace_back(result); } return results; } @endif @if "%ContentType%".substring(0, "network-netcpp".length) === "network-netcpp" @if "%ContentType%" == "network-netcpp-json" void Client::get(const net::Uri::Path &path, const net::Uri::QueryParameters ¶meters, json::Value &root) { @elsif "%ContentType%" == "network-netcpp-qjson" void Client::get(const net::Uri::Path &path, const net::Uri::QueryParameters ¶meters, QJsonDocument &root) { @elsif "%ContentType%" == "network-netcpp-qxml" void Client::get(const net::Uri::Path &path, const net::Uri::QueryParameters ¶meters, QXmlStreamReader &reader) { @endif // Create a new HTTP client auto client = http::make_client(); // Start building the request configuration http::Request::Configuration configuration; // Build the URI from its components net::Uri uri = net::make_uri(config_->apiroot, path, parameters); configuration.uri = client->uri_to_string(uri); // Give out a user agent string configuration.header.add("User-Agent", config_->user_agent); // Build a HTTP request object from our configuration auto request = client->head(configuration); try { // Synchronously make the HTTP request // We bind the cancellable callback to #progress_report auto response = request->execute( bind(&Client::progress_report, this, placeholders::_1)); // Check that we got a sensible HTTP status code if (response.status != http::Status::ok) { throw domain_error(response.body); } @if "%ContentType%" == "network-netcpp-json" // Parse the JSON from the response json::Reader reader; reader.parse(response.body, root); // Open weather map API error code can either be a string or int json::Value cod = root["cod"]; if ((cod.isString() && cod.asString() != "200") || (cod.isUInt() && cod.asUInt() != 200)) { throw domain_error(root["message"].asString()); } @elsif "%ContentType%" == "network-netcpp-qjson" // Parse the JSON from the response root = QJsonDocument::fromJson(response.body.c_str()); // Open weather map API error code can either be a string or int QVariant cod = root.toVariant().toMap()["cod"]; if ((cod.canConvert() && cod.toString() != "200") || (cod.canConvert() && cod.toUInt() != 200)) { throw domain_error(root.toVariant().toMap()["message"].toString().toStdString()); } @elsif "%ContentType%" == "network-netcpp-qxml" // Parse the Xml from the response reader.addData(response.body.c_str()); @endif } catch (net::Error &) { } } Client::Current Client::weather(const string& query) { // This is the method that we will call from the Query class. // It connects to an HTTP source and returns the results. @if "%ContentType%" == "network-netcpp-json" // In this case we are going to retrieve JSON data. json::Value root; @elsif "%ContentType%" == "network-netcpp-qjson" // In this case we are going to retrieve JSON data. QJsonDocument root; @elsif "%ContentType%" == "network-netcpp-qxml" // In this case we are going to retrieve XML data. QXmlStreamReader root; @endif // Build a URI and get the contents. // The fist parameter forms the path part of the URI. // The second parameter forms the CGI parameters. get( { "data", "2.5", "weather" }, { { "q", query }, { "units", "metric" } , { "APPID", "2b12bf09b4e0ab0c1aa5e32a9a3f0cdc" } @if "%ContentType%" == "network-netcpp-qxml" , { "mode", "xml" } @endif }, root); // e.g. http://api.openweathermap.org/data/2.5/weather?q=QUERY&units=metric Current result; @if "%ContentType%" == "network-netcpp-json" // Read out the city we found json::Value sys = root["sys"]; result.city.id = sys["id"].asUInt(); result.city.name = root["name"].asString(); result.city.country = sys["country"].asString(); // Read the weather json::Value weather = root["weather"].get(json::ArrayIndex(0), json::Value()); result.weather.id = weather["id"].asUInt(); result.weather.main = weather["main"].asString(); result.weather.description = weather["description"].asString(); result.weather.icon = "http://openweathermap.org/img/w/" + weather["icon"].asString() + ".png"; // Read the temps json::Value main = root["main"]; result.weather.temp.cur = main["temp"].asDouble(); result.weather.temp.max = main["temp_max"].asDouble(); result.weather.temp.min = main["temp_min"].asDouble(); @elsif "%ContentType%" == "network-netcpp-qjson" // Read out the city we found QVariantMap variant = root.toVariant().toMap(); QVariantMap sys = variant["sys"].toMap(); result.city.id = sys["id"].toUInt(); result.city.name = variant["name"].toString().toStdString(); result.city.country = sys["country"].toString().toStdString(); // Read the weather QVariantMap weather = variant["weather"].toList().first().toMap(); result.weather.id = weather["id"].toUInt(); result.weather.main = weather["main"].toString().toStdString(); result.weather.description = weather["description"].toString().toStdString(); result.weather.icon = "http://openweathermap.org/img/w/" + weather["icon"].toString().toStdString() + ".png"; // Read the temps QVariantMap main = variant["main"].toMap(); result.weather.temp.cur = main["temp"].toDouble(); result.weather.temp.max = main["temp_max"].toDouble(); result.weather.temp.min = main["temp_min"].toDouble(); @elsif "%ContentType%" == "network-netcpp-qxml" while (!root.atEnd() && !root.hasError()) { QXmlStreamReader::TokenType token = root.readNext(); /* If token is just StartDocument, we'll go to next.*/ if (token == QXmlStreamReader::StartDocument) { continue; } /* If token is StartElement, we'll see if we can read it.*/ if (token == QXmlStreamReader::StartElement) { if (root.name() == "city") { parseCity(result.city, root); } else if (root.name() == "weather") { parseWeather(result.weather, root); } else if (root.name() == "temperature") { parseTemperature(result.weather.temp, root); } } } if (root.hasError()) { throw domain_error(root.errorString().toStdString()); } @endif return result; } Client::Forecast Client::forecast_daily(const string& query, unsigned int cnt) { @if "%ContentType%" == "network-netcpp-json" json::Value root; @elsif "%ContentType%" == "network-netcpp-qjson" QJsonDocument root; @elsif "%ContentType%" == "network-netcpp-qxml" QXmlStreamReader root; @endif // Build a URI and get the contents // The fist parameter forms the path part of the URI. // The second parameter forms the CGI parameters. get( { "data", "2.5", "forecast", "daily" }, { { "q", query }, { "units", "metric" }, { "cnt", to_string(cnt) } , { "APPID", "2b12bf09b4e0ab0c1aa5e32a9a3f0cdc" } @if "%ContentType%" == "network-netcpp-qxml" , { "mode", "xml" } @endif }, root); // e.g. http://api.openweathermap.org/data/2.5/forecast/daily/?q=QUERY&units=metric&cnt=7 Forecast result; @if "%ContentType%" == "network-netcpp-json" // Read out the city we found json::Value city = root["city"]; result.city.id = city["id"].asUInt(); result.city.name = city["name"].asString(); result.city.country = city["country"].asString(); // Iterate through the weather data json::Value list = root["list"]; for (json::ArrayIndex index = 0; index < list.size(); ++index) { json::Value item = list.get(index, json::Value()); // Extract the first weather item json::Value weather_list = item["weather"]; json::Value weather = weather_list.get(json::ArrayIndex(0), json::Value()); // Extract the temperature data json::Value temp = item["temp"]; // Add a result to the weather list result.weather.emplace_back( Weather { weather["id"].asUInt(), weather["main"].asString(), weather["description"].asString(), "http://openweathermap.org/img/w/" + weather["icon"].asString() + ".png", Temp { temp["max"].asDouble(), temp["min"].asDouble(), 0.0 } }); } @elsif "%ContentType%" == "network-netcpp-qjson" QVariantMap variant = root.toVariant().toMap(); // Read out the city we found QVariantMap city = variant["city"].toMap(); result.city.id = city["id"].toUInt(); result.city.name = city["name"].toString().toStdString(); result.city.country = city["country"].toString().toStdString(); // Iterate through the weather data for (const QVariant &i : variant["list"].toList()) { QVariantMap item = i.toMap(); // Extract the first weather item QVariantList weather_list = item["weather"].toList(); QVariantMap weather = weather_list.first().toMap(); // Extract the temperature data QVariantMap temp = item["temp"].toMap(); // Add a result to the weather list result.weather.emplace_back( Weather { weather["id"].toUInt(), weather["main"].toString().toStdString(), weather["description"].toString().toStdString(), "http://openweathermap.org/img/w/" + weather["icon"].toString().toStdString() + ".png", Temp { temp["max"].toDouble(), temp["min"].toDouble(), 0.0 } }); } @elsif "%ContentType%" == "network-netcpp-qxml" while (!root.atEnd() && !root.hasError()) { QXmlStreamReader::TokenType token = root.readNext(); /* If token is just StartDocument, we'll go to next.*/ if (token == QXmlStreamReader::StartDocument) { continue; } /* If token is StartElement, we'll see if we can read it.*/ if (token == QXmlStreamReader::StartElement) { if (root.name() == "location") { parseLocation(result.city, root); } else if (root.name() == "forecast") { parseForecast(result.weather, root); } } } if (root.hasError()) { throw domain_error(root.errorString().toStdString()); } @endif return result; } http::Request::Progress::Next Client::progress_report( const http::Request::Progress&) { return cancelled_ ? http::Request::Progress::Next::abort_operation : http::Request::Progress::Next::continue_operation; } @endif void Client::cancel() { cancelled_ = true; } Client::Config::Ptr Client::config() { return config_; } ./share/qtcreator/templates/wizards/ubuntu/scope/src/preview.cpp0000644000015600001650000000443212705421114025273 0ustar jenkinsjenkins#include #include #include #include #include #include #include namespace sc = unity::scopes; using namespace std; Preview::Preview(const sc::Result &result, const sc::ActionMetadata &metadata) : sc::PreviewQueryBase(result, metadata) { } void Preview::cancelled() { } void Preview::run(sc::PreviewReplyProxy const& reply) { // Support three different column layouts sc::ColumnLayout layout1col(1), layout2col(2), layout3col(3); // We define 3 different layouts, that will be used depending on the // device. The shell (view) will decide which layout fits best. // If, for instance, we are executing in a tablet probably the view will use // 2 or more columns. // Column layout definitions are optional. // However, we recommend that scopes define layouts for the best visual appearance. // Single column layout layout1col.add_column( { "image_widget", "header_widget", "summary_widget" } ); // Two column layout layout2col.add_column( { "image_widget" } ); layout2col.add_column( { "header_widget", "summary_widget" } ); // Three cokumn layout layout3col.add_column( { "image_widget" }); layout3col.add_column( { "header_widget", "summary_widget" } ); layout3col.add_column( { } ); // Register the layouts we just created reply->register_layout( { layout1col, layout2col, layout3col } ); // Define the image section sc::PreviewWidget image("image_widget", "image"); // It has a single "source" property, mapped to the result's "art" property image.add_attribute_mapping("source", "art"); // Define the header section sc::PreviewWidget header("header_widget", "header"); // It has a "title" and a "subtitle" property header.add_attribute_mapping("title", "title"); header.add_attribute_mapping("subtitle", "subtitle"); // Define the summary section sc::PreviewWidget summary("summary_widget", "text"); // It has a "text" property, mapped to the result's "description" property summary.add_attribute_mapping("text", "description"); // Push each of the sections reply->push( { image, header, summary } ); } ./share/qtcreator/templates/wizards/ubuntu/scope/src/CMakeLists.txt0000644000015600001650000000314112705421114025642 0ustar jenkinsjenkins # Put the ini files in the build directory next to the scope # .so file so that the test tools can find them. intltool_merge_translations( "data/%ClickHookName:l%.ini.in" "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_NAME}.ini" ALL UTF8 ) intltool_merge_translations( "data/%ClickHookName:l%-settings.ini.in" "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_NAME}-settings.ini" ALL UTF8 ) # Install the scope ini files install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_NAME}.ini" "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_NAME}-settings.ini" DESTINATION ${SCOPE_INSTALL_DIR} ) # Put the logo file in the build directory next to the scope # .ini file so that the test tools can find it. configure_file( "data/logo.png" "${CMAKE_CURRENT_BINARY_DIR}/logo.png" @ONLY COPYONLY ) # Install the scope images install( FILES "data/icon.png" "data/logo.png" "data/screenshot.png" DESTINATION ${SCOPE_INSTALL_DIR} ) # Find all the sources file(GLOB_RECURSE SCOPE_SOURCES "*.cpp" "*.h" ) # Build a shared library containing our scope code. add_library( scope SHARED ${SCOPE_SOURCES} ) # Link against the object library and our external library dependencies target_link_libraries( scope ${SCOPE_LDFLAGS} ) @if "%ContentType%".substring(0, "network-netcpp-q".length) === "network-netcpp-q" qt5_use_modules( scope Core ) @endif # Set the correct library output name to conform to the securiry policy set_target_properties( scope PROPERTIES OUTPUT_NAME "${SCOPE_NAME}" ) # Install the scope shared library install( TARGETS scope LIBRARY DESTINATION ${SCOPE_INSTALL_DIR} ) ./share/qtcreator/templates/wizards/ubuntu/scope/po/0000755000015600001650000000000012705421114022732 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/po/Makefile.in.in0000644000015600001650000000010012705421114025373 0ustar jenkinsjenkinsXGETTEXT_KEYWORDS=--c++ --keyword=_ --keyword=N_ --keyword=_:1,2./share/qtcreator/templates/wizards/ubuntu/scope/po/displayName.pot0000644000015600001650000000000012705421114025712 0ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/scope/po/CMakeLists.txt0000644000015600001650000000022312705421114025467 0ustar jenkinsjenkinsintltool_update_potfile( ALL GETTEXT_PACKAGE ${GETTEXT_PACKAGE} ) intltool_install_translations( ALL GETTEXT_PACKAGE ${GETTEXT_PACKAGE} ) ./share/qtcreator/templates/wizards/ubuntu/scope/po/POTFILES.in0000644000015600001650000000036612705421114024514 0ustar jenkinsjenkins[type: gettext/ini] src/data/%ClickHookName:l%.ini.in [type: gettext/ini] src/data/%ClickHookName:l%-settings.ini.in src/client.cpp src/client.h src/localization.h src/preview.cpp src/preview.h src/query.cpp src/query.h src/scope.cpp src/scope.h ./share/qtcreator/templates/wizards/ubuntu/scope/displayName.apparmor0000644000015600001650000000016012705421114026322 0ustar jenkinsjenkins{ "template": "%ConfinementType%", "policy_groups": [], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/scope/readme.txt0000644000015600001650000000165212705421114024316 0ustar jenkinsjenkinsUnity scope template Building -------- To build this scope outside of QtCreator, please install phablet-tools and run click-buddy. Localization ------------ intltool is used to perform localization of the scope. Ensure whenever you add / rename files that contain localizable strings you update "po/POTFILES.in". When you want to add a new language to the translation catalogue: * Copy the .pot file from the "po" directory and rename it to e.g. "zh_CN.po", where "zh_CN" is the language code for China. * Enter the language code in "Language:". * Enter the "UTF-8" as the "Content-Type:". * Enter the translations in the msgstr tags. For the new translation to show up, cmake needs to be re-run to enable the recusive search macro to delect the new translation. In QtCreator this can be done with the "Build->Run Cmake" menu entry. Remember! When you copy the .pot file, make sure you change the file extension to ".po". ./share/qtcreator/templates/wizards/ubuntu/scope/CMakeLists.txt0000644000015600001650000000577412705421114025071 0ustar jenkinsjenkinsproject(%ProjectName:l% CXX) cmake_minimum_required(VERSION 2.8.10) # We require at least g++ 4.9, to avoid ABI breakage with earlier versions. set(cxx_version_required 4.9) if (CMAKE_COMPILER_IS_GNUCXX) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${cxx_version_required}) message(FATAL_ERROR "g++ version must be at least ${cxx_version_required}!") endif() endif() # Set strict and naggy C++ compiler flags, and enable C++11 add_definitions( -fno-permissive -std=c++11 -pedantic -Wall -Wextra -fPIC -DQT_NO_KEYWORDS ) # Search for our dependencies include(GNUInstallDirs) find_package(PkgConfig) find_package(Intltool) @if "%ContentType%".substring(0, "network-netcpp-q".length) === "network-netcpp-q" find_package(Qt5Core REQUIRED) @endif pkg_check_modules( SCOPE libunity-scopes>=0.6.0 @if "%ContentType%" == "network-netcpp-json" jsoncpp @endif @if "%ContentType%".substring(0, "network-netcpp".length) === "network-netcpp" net-cpp>=1.1.0 @endif REQUIRED ) # Add our dependencies to the include paths include_directories( "${CMAKE_SOURCE_DIR}/src" ${SCOPE_INCLUDE_DIRS} @if "%ContentType%".substring(0, "network-netcpp-q".length) === "network-netcpp-q" ${Qt5Core_INCLUDE_DIRS} @endif ) # Do not remove these 2 lines, they are required for the correct functionality of the Ubuntu-SDK set(UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file") set(UBUNTU_PROJECT_TYPE "Scope" CACHE INTERNAL "Tells QtCreator this is a Scope project") # Important project paths set(CMAKE_INSTALL_PREFIX /) set(SCOPE_INSTALL_DIR "/%ClickHookName:l%") set(GETTEXT_PACKAGE "%ProjectName:l%") set(PACKAGE_NAME "%ProjectName:l%.%ClickDomain:l%") set(SCOPE_NAME "${PACKAGE_NAME}_%ClickHookName:l%") # If we need to refer to the scope's name or package in code, these definitions will help add_definitions(-DPACKAGE_NAME="${PACKAGE_NAME}") add_definitions(-DSCOPE_NAME="${SCOPE_NAME}") add_definitions(-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}") # This command figures out the target architecture and puts it into the manifest file execute_process( COMMAND dpkg-architecture -qDEB_HOST_ARCH OUTPUT_VARIABLE CLICK_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE ) # Configure and install the click manifest and apparmor files configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION "/") install(FILES "%ClickHookName:l%.apparmor" DESTINATION "/") # Make these files show up in QtCreator file(GLOB_RECURSE _PO_FILES "po/*.po" "po/*.pot" ) add_custom_target(hidden_files ALL SOURCES manifest.json.in %ClickHookName:l%.apparmor src/data/%ClickHookName:l%.ini.in src/data/%ClickHookName:l%-settings.ini.in po/POTFILES.in ${_PO_FILES} ) # Add our main directories add_subdirectory(po) add_subdirectory(src) # Set up the tests enable_testing() add_subdirectory(tests) add_custom_target( check ${CMAKE_CTEST_COMMAND} --force-new-ctest-process --output-on-failure ) ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/0000755000015600001650000000000012705421114024346 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/projectName.pro0000644000015600001650000000254212705421114027342 0ustar jenkinsjenkins# This is the basic qmake template for the Ubuntu-SDK # it handles creation and installation of the manifest # file and takes care of subprojects TEMPLATE = subdirs #load Ubuntu specific features load(ubuntu-click) SUBDIRS += %ClickHookName% # specify the manifest file, this file is required for click # packaging and for the IDE to create runconfigurations UBUNTU_MANIFEST_FILE=manifest.json.in # specify translation domain, this must be equal with the # app name in the manifest file UBUNTU_TRANSLATION_DOMAIN="%ProjectName:l%.%ClickDomain:l%" # specify the source files that should be included into # the translation file, from those files a translation # template is created in po/template.pot, to create a # translation copy the template to e.g. de.po and edit the sources UBUNTU_TRANSLATION_SOURCES+= \ $$files(*.qml,true) \ $$files(*.js,true) \ $$files(*.desktop,true) # specifies all translations files and makes sure they are # compiled and installed into the right place in the click package UBUNTU_PO_FILES+=$$files(po/*.po) aptest.target = autopilot aptest.commands = bash $$PWD/%ClickHookName%/tests/autopilot/run aptest.depends = sub-%ClickHookName% unittest.target = check unittest.commands = /usr/bin/qmltestrunner -input $$PWD/%ClickHookName%/tests/unit unittest.depends = sub-%ClickHookName% QMAKE_EXTRA_TARGETS += aptest unittest ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/wizard.xml0000644000015600001650000001005512705421114026371 0ustar jenkinsjenkins ../share/ubuntu.png Creates a Qt Quick 2 application project with a sample UI containing a Label and a Button. Also includes unit and functional tests. This project contains QML code only. This templates requires a Kit using at least a 15.04 click chroot. QML App with Simple UI (qmake) Ubuntu Click package parameters Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/manifest.json.in0000644000015600001650000000072512705421114027460 0ustar jenkinsjenkins{ "name": "%ProjectName:l%.%ClickDomain:l%", "description": "description of %ProjectName%", "architecture": "@CLICK_ARCH@", "title": "%ProjectName%", "hooks": { "%ClickHookName%": { "apparmor": "%ClickHookName%/%ClickHookName%.apparmor", "desktop": "%ClickHookName%/%ClickHookName%.desktop" } }, "version": "0.1", "maintainer": "%ClickMaintainer%", "framework" : "%ClickFrameworkVersion%" } ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/0000755000015600001650000000000012705421114025727 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/0000755000015600001650000000000012705421114027071 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/0000755000015600001650000000000012705421114031111 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/run0000755000015600001650000000027412705421114031646 0ustar jenkinsjenkins#!/bin/bash if [[ -z `which autopilot3` ]]; then echo "python3-autopilot is not installed. Skip" exit fi SCRIPTPATH=`dirname $0` pushd ${SCRIPTPATH} autopilot3 run %ProjectName% popd./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/0000755000015600001650000000000012705421114033357 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/__init__.py./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/__in0000644000015600001650000000205312705421114034206 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Application autopilot helpers.""" import logging import ubuntuuitoolkit logger = logging.getLogger(__name__) class AppException(ubuntuuitoolkit.ToolkitException): """Exception raised when there are problems with the Application.""" class TouchApp(object): """Autopilot helper object for the application.""" def __init__(self, app_proxy, test_type): self.app = app_proxy self.test_type = test_type self.main_view = self.app.select_single(MainView) @property def pointing_device(self): return self.app.pointing_device class MainView(ubuntuuitoolkit.MainView): """A helper that makes it easy to interact with the mainview""" def __init__(self, *args): super(MainView, self).__init__(*args) self.visible.wait_for(True, 30) def get_button(self): return self.select_single('Button', objectName="button") def get_label(self): return self.select_single('Label', objectName="label")././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/tests/./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/test0000755000015600001650000000000012705421114034257 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000016200000000000011602 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/tests/__init__.py./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/test0000644000015600001650000000324612705421114034266 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Ubuntu Touch App Autopilot tests.""" import os import logging import %ProjectName% from autopilot.testcase import AutopilotTestCase from autopilot import logging as autopilot_logging import ubuntuuitoolkit from ubuntuuitoolkit import base logger = logging.getLogger(__name__) class BaseTestCase(AutopilotTestCase): """A common test case class """ local_location = os.path.dirname(os.path.dirname(os.getcwd())) local_location_qml = os.path.join(local_location, 'Main.qml') click_package = '{0}.{1}'.format('%ProjectName%', '%ClickDomain%') def setUp(self): super(BaseTestCase, self).setUp() self.launcher, self.test_type = self.get_launcher_and_type() self.app = %ProjectName%.TouchApp(self.launcher(), self.test_type) def get_launcher_and_type(self): if os.path.exists(self.local_location_qml): launcher = self.launch_test_local test_type = 'local' else: launcher = self.launch_test_click test_type = 'click' return launcher, test_type @autopilot_logging.log_action(logger.info) def launch_test_local(self): return self.launch_test_application( base.get_qmlscene_launch_command(), self.local_location_qml, app_type='qt', emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) @autopilot_logging.log_action(logger.info) def launch_test_click(self): return self.launch_click_package( self.click_package, emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)././@LongLink0000644000000000000000000000016300000000000011603 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/tests/test_main.py./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/autopilot/displayName/test0000644000015600001650000000124412705421114034262 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- from autopilot.matchers import Eventually from testtools.matchers import Equals from %ProjectName% import tests class MainViewTestCase(tests.BaseTestCase): """Tests for the mainview""" def setUp(self): super(MainViewTestCase, self).setUp() def test_click_button(self): # Find and click the button button = self.app.main_view.get_button() self.app.pointing_device.click_object(button) # Make an assertion about what should happen label = self.app.main_view.get_label() self.assertThat(label.text, Eventually(Equals('..world!')))./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/unit/0000755000015600001650000000000012705421114030050 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/tests/unit/tst_main.qml0000644000015600001650000000220712705421114032402 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtTest 1.0 import Ubuntu.Test 1.0 import "../../" // See more details at https://developer.ubuntu.com/api/qml/sdk-14.10/Ubuntu.Test.UbuntuTestCase // Execute these tests with: // qmltestrunner Item { width: units.gu(100) height: units.gu(75) // The objects Main { id: main } UbuntuTestCase { name: "MainTestCase" when: windowShown function init() { var label = findChild(main, "label"); // See the compare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#compare-method compare("Hello..", label.text); } function test_clickButtonMustChangeLabel() { var button = findChild(main, "button"); var buttonCenter = centerOf(button) mouseClick(button, buttonCenter.x, buttonCenter.y); var label = findChild(main, "label"); // See the tryCompare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#tryCompare-method tryCompare(label, "text", "..world!", 1); } } } ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/appName.png0000644000015600001650000011407012705421114030021 0ustar jenkinsjenkinsPNG  IHDR\rfbKGD pHYs B(xtIME 22 IDATx[r\gQ(poGaD% @@UPŧZ'7-D($@a7̕k|{@#7bgf|H;j}I6WnFșufpgUޅmԼq;V<9N2s%c|oVMNZԫjHO^x'k#@d=SEkuMeϝON.ffykd뢴kas4O]COOO8y=ڂ5 87;N3ǬVa@r׉;S}f|.2gSה.p{ Ùxݷ;n8 Y*d}-3 `iɑ k|1<==MdyuzͅXiys'HIrSdYZYו7+33a‚3h b[??+̃RϽVevo;߱ӢKxsCZM}^=Bs扚'~FLxZîZr.n{tt4sԿe0V;z/'⺟|O= tl%3&fL*da epfFk$nc8אi<y][Vi3=3A}zAҧHICT_f)꿹 '/n"Zk/_t>67q R,s^SWY=b$trn=C+bJn Olfu+@@9YfT\|~ @)]m~ZvQEZ$D6a899|߹3Z/ɇ0nlNk0A5~>|Y/Ȏw.]3L0R!B̀e_=~sml6lr]0.q !ZJ{yɻawחˊ*LϲDW-,櫛'oi$)6#=l)憪NxhVb^_CX$G!W-{ `| A!{ٽk`*OGdpz '/!Lsst!g9lIZZ^oq[2զ@6v noowbl(>dޑx` {<>>].?Kd},NZN;Fzd.2eg͑vxr-i2gGnDv9X 3;07sn fBN)1?3e\k<=dy``krp$t Zw 6dEq||<{"TycD)f1#)6yF*,)eXO&K Fkw2e&%6= 7{ k[93׫& dpdG;@&.lH, 2:d7wGN0ٕOO߾}D1X_'Ed\U5$Kř۩(5HivR`HFwn$̉x2h3Ӳ>x:H>%OQq3'e uvlz#Xpt&+ 3j/ Ys呆46Q_Ѝ{ʛcaAq21"g=<{jT~l@iQcbZ[-Фbw؈=cDY#Ʈ%8\A<)v˃z?@%l]c; Tܥr5,emOOOxT#ia(!Ð$% 2uZ`Yf;/Ҝ`LR^ɸ~>EYӉaV0N',SRCyhTeo`|?E>ʞ1SdI"9DpBfD{ ˾>߉ѳE j05:b'wd)@bFN:{C?SbM4;Sk@Fe>3ʴZY=ѳ.I; ZKyM'oCl}l0"fE]i)wRlr X{7͘ L%S~OKt;9âyw炫/;HɛI46m." ix3%U'-S:%s*`--ߟѢ;Yt~߷oߔk;"gn"7! UP^=SpIҪ'ƃ$Y}N{(b)5d {G6N0z$.On. 2qdtN_C2 pl/V Hz)rA0iƠtb%g9Kw zցY,۷o-iܬ^-kM-e  pPYD̙; R_)l 31/o"I6DQl0.a ğaZW3g=1YS4]lZaמM]yMsIAI.6of,:~c )Zַ5aB i3do f'4߁Wdڻq`1i=DǔӨTgX:Y#<10FHPd5HU3"LyĜ hIصORȔѯh |\ J |Y0>2ґIyxdWF= &bZi v\zSuސr`$3{\- rS%Gx32@+Olo[SٱBϯ^9.qHKϠu5&<3c7>3%5fkemӍ&^ ޙ*(AV7vSΞ;ue35$)SL ʍAwxY} rcGb7[fZCF?-\͎SgNu HeY. &}9eWMqiQg&66uG[4i_\D VAXWfXj8 lHtʲײ Dm(#vqҴv5MDWYo=?I&nQP$&[֝d12[,K5*:;ǜXǷa']H&1SPVcMR:] gik%-ÃzxÞ45Us4x 1 s6!ā)2{ gYOg9Ip}|3ec:Y4qGgc Q5Й WҰ4#:ÉL)iXii[**ɕ E:c3-XʣAs3tĐz֯1$Ȏ =M4a%4o.%UwT -\*d^Vɝ΅i5hh,EΤi` 0.߯\uxSQ3yL(T}벎cLvf+C.Yw_t񝴕fډ)P{7dj|0q32Nf>;;ۓgΤC@j(5Q$,k$ӗO(-Yv]| q bN3 Dɨw\uB5C,خ3tryoYˈ[6ݑL)[XXpS"ND,<\5Y noS2KTtxv$ M$b3)&ɏ9tԥ'Ӎd_\\*PiϻѣqRrMdYǓ `Vޣ 7`0v#TIKL%ρ?;))5YQs^RYw!@G-+LMh an継Huj b͸`K&k\Miy3j z"9g k]ia(^gxZMڳ 0x6ּߌܵUsݷ6%FwAN?fW8LmQsQIM|wi[sy [ug;4=']vr]<?TKA5Ñtno-13=M`KRW=G\z5L6:1JZwGTnIq5xߛQm5ɆLچ%KO-IT=מD,ͩc3=hݼ) dO|;54GNfbO?AQpvO!H\Q5Jj2b4y*7 +V}gw?g|xAe΀a IfSw{v&wN<gV g] sp,fbcgcjv4oD*u6s">-X!|hsJ%#*Xiabr+c$u-WHAJ~ E`}ӿ,:ZkƎ#n35& N=#hь-Sb-OΛ>\ tKBQLJO2W@5aތsr&1%oE;Eb ff~ڦ dS~:>#rgdΡy[KgZaxxxU4"YfwTjn8#SL3g?S%2S!ulja&`Ğw^kX9r䕺Sesib&VJY/s:zsm*@s6HVđg2s(5%f$e~s5x X/jQd6N CF2Qn_><<'?-,0qxHgLdzsjN^KKXs2U,S֒'''{vڿg߆aXC~a%삌1-g*OG,zlLXf5̇y\-I3cJu,J/AnO`bƒ2RWM!\̬{n3>Ale1笜v;Js)C~՝/@KcZxR6Z^'ו hb,=;텵{||f3 7G@Aɫ?b "߾}e2~E?KCMm"r;NSueVs\iA IDAT.0s6e6"N\Y$5G jfXcFz*+͎+eptt4&YQeO]wM_sINYjw`um~N [v~?>45ˣ (?\W{خbff5N.=:' ۡ6sӟ G` L/-G oHkRd91&w4JF$ƐD:ܬv͸Al޷۷ɋ_{$,œ3Z1 m'7tGUf4HQ].wHB[z+vR~^gbےIfVԞAxd@~0s|}:ڙI+T#cXՍ9_4?VZl-sZuuu5md)Bϟ >eƕG-u4Ș54@LJ?|0f|_ |3`*54dZb)y&AXQ7Zq3}Ew&v;~a vh2#b&ƚu94_'F&!y"G8Ce`Hd9> :,(L>{\۷yΕ`dw 檳z,ӻ䆵֠vso}Q]~dVJ6蔬 6AD;ϜQ❢RlnLnKRe:'Y zxx| $KCz:^s,c2!*وgĜ[20 -a^AAm9qR' 6[*];'0C#`AyL/ijSZl}|Ky܍FۦΔRRkrQ̚F1kvҵ'K1;gn\puرMsH.|vUa4ac.tR s3Z3L:yMfyc9f]C2^v`CSux2Fud`eɔ٬˗7umnxf4%tlɚ ?"*Y)Xs{{;^|ԝ\pA''ی U,ӳkea[E$&x0;1(1Zi fi \9Ӻ+Lˌo6 rd_Y3eQc3DZj_ud&e<Wž[ym -Q|ظ/fnAڰ1\u2爽l?#E;ov#tGdkʅbd`JߓcQhoP|uRw)l6%N]d3龷mjCUğTjj,ؖdc͟%Zkw,I05F,hoD6"#64&Fԙ\b5$3n2#f.L`E76?cAgSƔXFFXw݌ Bl7مV2|5pXmFN L 3LzPv22x1@yemeL("5s|ب(f | 7 \Ox34._DiNUtr<S6j}6M 0OrvPL[Pl8==,.cp)v0Ça)I/{Vb&S)VF73M'Հֿ+eIXɸc9atw"rڲzxxnnnZ]7o~fP5Q P90;=*Pouyy91E;U+r/{::mhu\g`_Y\ w,O#[e}62~3}"|Ҷ^(Z2v:ez;\lO< K)H:Okkۑngr\;i$7b޿+_P֪4u[&۶/N?$`V'9(8לeɝш#K͙62a T^>>H3gvO~=kvI:y9gv߰R]__޽#`,NW2N03kSL\ ӭ[k957;:Wl>OFCdDdfvVv t&wƲ% G>oLŊdZ{g SLr*On]Vjc=`]'Q.~B#("Ш;"\<13(܋NI`0]86^QK:rԪr/MXbIS>HzH,"OKh  s)_Lˤ|/l.DRu'=AVt4pєG_kmKtyxxP46"kD 888kCfd?+>kf+ f$Xe6qIuߜO[ЩJBE~aXDNcӥ@(fp؉խEf sV[wtƒJ[VÛ7ox :,)O>Ǐў+|ݤʸ9aHq d9a"2\@;q=,[]3Z)$dke\{ 攲n'CEkT:_AwV{"R#[Rf Գ?::NNN&>Z۬U eo]% ܵ,;#zd+ڌ6P<cNމɖ 9Fv"efnnooK}iTyD;_fD9/b^w2V@I 5:sW;??...ԕƦ_t|/6n_jHyLn`^>*| f$ 7tIJ6 lE2JI jSg7s 4LXA|x||t0 wwwsB"VdϹtn˗/Yf /'fN۳ُseK d o!ܦ2#N6vq!fzd/jgvG8%b0 5锻L`~9?(k٭>,L[k7Mr9yf&pvv6DYl(Ꝧԑ뫺Mx?<<< F F~;L3߽h=eիWŅt*OɅsc*lCcgvc{0 v>|yn$?sOYW1 張n I)MƐ{7F1cǽQV j^~=yF_|_~~2( O{zznoo8_^^F{NlcuH2 ƿDߐYcJyBa:U圹bV+mH]///rK[-`K3}fgO(Lќ; =lJMޫ ͟R.Z 0xԦD+߼y3F,bJ/7I}X;4>wj18UfXG$js'Ӷ74K%٥f&0}:2wqϗ|g Q\'[*L:A }~z_6[!իW4''''#'~$ unB/;iAt_&A~ɹ,7،=l&ley ]/0ů.@YUrRҀCjXjy͡fEfiNJ$=܌svL߿WO<l:eoyo޼4yo߾J9wtt4C3 \C̠S RPlZ_޽{7ǣ 7իQV{s=ڵϕ[fuǏ6ʔ|*ln07FV,X |iͺ-44[:Yo>6) [1ly7YC8n]%U8 &pķ ܤ=N"䗿kIsn*i8%RirYli1׽M9țbBz7pѡ/OU@Uh`b:>݀|._mYj L[;|'Ǐ hIfj`{֬ę>??q32C;g샫QU>3gT~t:1H'pq ڈ9TuAVK,Xo2߿~;Ov;r d`R꺘te;-'''٤gb']&BF*L2ze_]cR*Ort]#~gց乁Y~Ӓ/Z?;{򶉜{룓fi|v)%fAwaMLkɐF|xx8}vsB'aLp* JC0*3# T|o 8YYY<}vB¯ ,]vѱHJz=Sme3*`|'Ӳdv4  4n;^f&S(aV_t%4F梳{߾}ڜd5U)4e#\l?g'p~NMVYך_a.ʺ7o;z"fjK ㇺ M,s1JC<;"EoucN߿tNynjceԦXb &TN.yLI2VZO:,sMxN˜jYFz3=S42heͬܐEK˗c""dA9:1*6Ң>}&Mc߹Tt3:muXO7\׭Cm,X|:<<7{|luRb1 5ߌ Jx;r&L)Q^nTA^ H۷ɜDRdI6h=7oLv_u #Aw\I~˔ߘH ]lNc|EA999P22\ ]Mکe՜;HQ,&VsCSi|qg???o޼Q"E%,%d0͘z>;D0x ~zO)Af;irxxx+\C~ f ͿgB1irJ% dVKV_k: +F'Ґͦ9*_lֲu}\NN~)$+Q]բ] S{/ IDAT鲊Ji0?YÕDXVU Y*LAb0)m=0K'} ,:muxx8<<< mʂMV쬦b6Ym.u/˃Zih"G'2`/ceˮDM[ʅk K,ܵ{IA_~Ke\=rg`H7TJwxf%YDf׀@;͢bUזEt%a96~nl5x||O|ٶ ?///CnE1 [JUM[יڳi=;Ɓ?::'یMg0^o߾8Yz/r&K 6b/l)FxZ͙N䟧kLxsDImLu|IDكgZ˙Vo XЪKQn`n PW>:e)3AA&.0~Lo T3R-%k)l$3WvRsc|I<];<1rꭥ\dߛl0e%bmXWU4<#sɬ IR6QނڑNr[|ڛ=Ɔ?qxDE'aleQ>N>?s;¨hVy@9p'?Fȅa&˥2i[f=cFeKCHY::WXG3Tl;k6mF;jGcCvª3&vp,HBcuS`ggҦ~O #PXMbjI4M%N.3?r':ns>A+d.IlX0V['lYIzӉ%J;iTm8&y*8CbK \h녓Er1c{{2`aiYFQGzʡ#̔ y6,+͵Z-ugy(TYfJTHME?20F@gfˍrRnĪjadmlpYK|5OXj$2|+͡s3먿[c'պf1ZL٘PsE9w2'qEiƩLR5 qV*>Kc([$F!CQV8|ymH3F^؏?ƀS_nLhm@*sBܠKUY.SKwʑ/, BaYbTYvb5Nf6?F*PTS߲ȇcщ5UR@SƑcLapʠE=. e@Ǐij1޿L,2,6m mL-RQH,Rps5 ˒dCL;MBJm[&@i i84mre6\9.YM]O6(ݢS+@)X> 1 %eܬکN7cVQypp0/{>SÇ{Az3[q3Pe)Ɩu6+O}r]HPnn(j7fGj`<]`ڜӗ-C,0hw!)O@{2 {;N8#f aeV~gHI<ǙqE0ʀܷojiRMM^JMf"фupg\YUkZgќ;+UMMi7SnLRX}p9+~{˒g9FeCnd=V%矉0-J 2d"Q`a Q&' `Zl*&QegY'i[`6jo'h&df}~; $V&w@"{>xs+dk&3lO5 * 0>݆q9cfΥB3ȧ;r Hh ,d\0H w"{%;fGEzQ-Z%"_DYl&$3 W" ;\<|]:β6YfL0ߛa",CX LZsv)xbHIIJSshdHŊ̒it|l1u|᝶(2xOfC0K BfGOrA)[Tnz5$TDU1x/ͬ8ds}_93=ˮ~"cL!*):Ev ВS"ּT v9Kn~NhL5q=ivґp2U6IjVle\H1uo93°nDxsQr'Y3*[F`5?kN% dW+ґ:9* }5YsKiƩ/_Q!Ɣp3FleKK@X)Ŷ`; ;v.)`L: hc #lɿ r-VP׽^'<0j33#-0;'d`fRJQnΔ]V|l9ʝe2 yŝ%iItlf<m0ԚOb^rCaf9=\t.H4lhc[^zGh1"X7#JezO4IwHP& ,,S\I|V:&{HNncZ0ي҉ֵA^S(ѻFRCDLENj<hhx|/BΔN.cd"Y#G$$ޘ X:ϿYu'?yd~ ro[cmsPobC[ιaXFiY{fH3 #,tZpv;/_())udo}e~~^z`NrT8yh$Y7Xpg0SWPeeQ GaLNНs馵Y7ͤ ՍNJ;ڂB<&]5F1ȧ1p56a $S(:%=X{QYߓɚ_}>oooϟ?OOOOׯGzfi7.e!ÍI^L]DtTfumrX$3 qq z>f3X>...=P7qe899tD~KB3يDYW\yZuƜ3N0f gvdXZ'/].+6cONca&'Β-D-s$$Sl0ue۰ˇԹ$fe"# ZPƺxLci]C& Xzz:G)Tٜ3xCuwzۦxfFAni3@%󱴾?q!fۀΘgE^})յً֮4ڴ:\Np6{f $dDҍ4dnڌ!s:m+ɓP_f\@cܐ_]kx899cɮ\HHp\[=6x%%?R;3|弅|T Yv&&r&rghZfb.ngq1]Tդ b43t^Mʸ-)mԲEfvQ6N,^z5ӦI!1%K#9z5ߏl6bɃ[iTw3!/+n8 K1gK6ͳ\tުSSš&~oN |x]d` 瞱rzzw;D krܼ|؛fL~6ׯ%[YWZ$@ `Yj8K-]CgXVIOuIm6gZkT TFz겏L#-1'ò43Wa&ȅeCW"Z=kG؎Y.vUgG!o 2,N2H/; ĩgƢf nzxr443ImNcG,\דuE 8KTm50؅@E;p]n+Tge }%ͷ,t/5dfg?^4bcaٔڸٛ7ϴfdMV *TeIOޣM[a#do`O[|xED<;!s(իIgNlڹyPC6ٙ2+􃃃QY@' y $QV672H*{DԫU6{=OEk1fr\N(2>zxxe7MZ=Lehٕ0Kw8%I*g#hY!˞H33Wf@F2j|2I6;63+ }%9܄ϸOԀ_&1%6ԃiKzec|!ӥ8[cIIEc {Jf % g0r"uFqjM*J .$X1:/y{̺?3٬My OOOz+X2Yۨ᤬#qxggg~-ѤsZbM`=GaY݃ONNu#tae![VWX9y8@IBB:慿yffB!|e\F5!eo0#z}}wJp@- ᒡFx >f}ɭg*Iw&ȓe^2T7 =7ij ,= sV24biXrӧ~`' Ht;INQ}#CgV $⨑yzKEחikݎ`uXr)el6cL\ A+˔,Hyfx/ F նʹfφ˼ i9䦫MQeGNY6;`*TTa Y]YP|q876\:&8S/([.˛Ouy.aْD*I+~gm@)KH7f’T:)2e8;;MffYZnX-(˅:ﯿKԄY W8|Vd弆:Z>ZhN 'Kߔ^ZLqɠ &Ud,e-ljMBf[mj:)8Nu}Mn|///X~=??^~=|a8???gin.ࣣ׺@D\*7YME:=]}v M'+8Z>%}Q>ݜi-OZ9 8쏲8;fHnUȠ3TƉfۄU/mnz#K9Rlr ɶ,e~:l6 }e{\qd]=F,yd@\nG<)UmY}SKob)ðySgN_^^N8L5e5NgT籎/NyyȋD۷{וdj5:a |'-Y///HEdG.pUv;ϯÍuU]gPA&LYgcaf/\dhiNBWΚ(Ai@Z$ \j5Aɹx2“a,.3C13@pww7<1)F!VlФSdڌov`a IDATۥ+W z||@Rɀk:Tۦ7:p,JLcݛF:Div`|ߴ3ay&#)Ȕ*'M`FЖ1ˀXE4 ^oZ ڵlϟޓrAeWRgvh7ԟ?>< h]e}eIj.Mcx/UfRrKe+Unl]|4\Dbu TevG2F-;3x؃@4ZE:oujeVONc$v^<<<Vhfࡅw D@@+h߇ϟ?OZKdL ~{eEYLDN 3/{8T3aIaї ]Rl#Vظ$;e3XPύW/bH#ЖʲnIr ߾}KU.o޼lI^uHH٭юSG.f2X,(ap>*>|Ƨ9>>>~8e ! MʍXrf|mGl3wu4Wa'$KEq0jf5k#+cVDkt.SIlyFx6,R Eӡ+3#nDt}RΔͦ3ȓ(s1OFJIrssf}v*`3r{nTL'T`܈U}d;͚tfI" <)5J#3EGԲι2EK:/BF4o}NpdI!LAMGHpRNIQ9f,iҹ&PV3˪5jih91e~f6UH|1dR땀)q.6,+6ugY3u1 J2]bj `ͷe#y_6, &nv >,N(Qfl6ϟG ؉Xm] ydt{KWA&($pu71==NNNRi9ʞ쎕G,|5eYu~Z\X/Ot: g4\o2xq. n^v#9_~~:qbgƲ0ώD(Wdñ0|^R $gw 8vׯu&π99%ׅ 2 O^2i8|3աP^K3˴?kXk10Ӌ9+L |Y ck*5b *>92v><|eGѐNrg+~5Qz4pi653Uv ^^^FY'kRi0qvejLzOFhK:  Wj`fK_KLa\߿bKAn^>r9VS Wwyy9q%&К(&gf>L9F)X۫F͚7 qDf5^?>>fŘ5s>OrE(l5'P#s11S22+e@(ɼ4ga1B4Y阍Yt%B׋M{ g'IJ_ɵyjכymb& 3 ׯ_WWW*)Znqlujn}OIa57Nml"2tn.aWȔ9MDbde烃͛7%9yH7J2x@){*, $e!  [.\$X"-$Cٮ)vY_xԋrsP@’\ؼyjLIT471|)K-^'ߺBsYs0j}vi5sO]3k5q%yzzsafʝMtI}\{ֶJMAw(;Q"VEZ6VꬁYi̺ȜA$w:jx*96E}³Y]B5˴3X$摘jS|lb.׌Zfkf¤|l2vؾ.EY*s*p(Kbl}-cF^xY[4c+]~t&S_;xXsWtrL_TeC<*9 ia?(0l4܏ScfPNgb $5qJU{,/<<07l\JG-A&qrsRp2=/"%CV >5Iu%txrNe4\p<92K;K;!c?cptg^ Ů]KIZӓ&BeF~t)=:rR= .}fMAϴ@̌LH&)"AƙFu8R+;V[%6_2 `LK~n׺Ln%y&[w*5|ʞ?>xQZuuuKf/_&~GGcfʌÇ{8a7u;h&yP$a&;8G2dFA:.0}IdFʗ=OF=G3bCIxzz'"(M\v:/Y a^Ç&|܈g.tbIY ePk,mMG2Ek6Aٜw?˗/;0L{MAHrCr2d/ $cb {d@i {=w[̍/+d{`ݎr\+:F ǐYǝu6$]J6Ǐ0:Ce9m R{5ޑN\#ƙ$W`iAŗP> AL;j͚2ìВ/%?4]!'ۗ8l!LgjCOfU;bKA mz?9 ZvrIAiL>5 #O:۽I.d/2ݵys =S.ԆXB)7OiN#6.:Lh ^Vnk {hRy8?? f1%Y#իWÇ4Oʶ );bv#0. ;I* #FletQ[T>'En>>>N5 mLF]0X7r|gq?;ʭe_:W$Cy{w٥eg6aZQ PXv7GUq+23z< n۴ K988" &%#AM:C dEplUe/6E8 *ģoۤ1$4$U"^m\;q0a65eLWd^W'x%F3ԕ*ĵ\A] zۭN+r3ʌ~qAuSlsg`}ݐ hq'~WIBFcNtmHC^ywwwÏ?S)&kxsx!THs2S+49djy?t`>3аq_u:ǖLN˿ t:KeHDf ξ|7l! \Fh>LsV<>>5+K8<0Qt͡YuF81߂3ZVnnmvkY 1fԵH<7\DnFќ_^^>-2 R7JmQRrSu}3&g͞2ImYm0ş Uh439\j7{nس=M Vd^$Pl;>UМNsnMׯ_OƇ d", &mD&s)"c4)b՘ E7kϟw2逢Ǩɇidƀu)TPYtusN62! z?13eJKIZ ^:M9:yrdFrf=gfckLS2jK'mA)h4b}}gKfhŪkOPǏfٓM[J&hB͍E cnf@%]l8??\a%PI6.p:05׵@3ck$~.Ft\j R PO|XD9#B'oB$t]o YkPT:v.O4vͅ#xrqцְ.sIEV材2T s Lֺź~goCF3wcrHm;|(3y]Ǭ2Q+Cm) 0&,~G/lXmV)ȗXݰlt22O#tXT&6n;BfI"~+$KZfIL˯(` 3IS:<朘XѓѸUuO2RPmVN|l&Y=(Rz-B7`d ?f ɓl!"Rc9(C aFw3✆O;;;1gXu(NKñQ6gB$kѿ2=;[w1u69ײM2\ş` =6" k/I!Tgg+p~v\JOķfExxx8hȺ8  W f\ &Z~Wħ: emm~&Ć~v:J30 ZZ*aofbLӸ12K Sٹ 2v ,~?UmOuasٚ!)c2 3"N|Nf>۴guMfTqWPM7Yzf֑֜aaY 7;l;`ka3eHSYBj4 =0I+m2]O95!x:;(3@rZ ??fĞ0_pּ4j4sUyh^u$*ZVn#1H:cƓmܤ gpYF 6 SYr-eڡjl_3Fu%2N6d_N2s(N`s\vu79q3w5uP.4*ggS8cI $'2-Ό؉VJ}dM޽i,̰ڦ:Xk7F,b59Otn|A|@Fdji[𬹈TzĹvzfxtt4MR9vJLI3SbBKa_ASmZs | rȇQ=''=Xa&uL"MkN[6qmXr&]$xh>@_6,ۭ@li1 `\E^yeM57'#v7(Pf[E5A-fbUN06$~OY~ܟ3|٥.^?5$E{vf-ɖ_]hUkӀIwnXX<=LtQE6Ն" zzzg I~ɔ&d$zaٳK{1/9ڜfRJ୛ `-ۆls %@f IDATmN%6l)e+K1ד13RN$sa:>ݔر~c\N-8,OL'9^t'miϳzM eչ"`;.0jj\_x*5p3kN;mf)MJlUOO )gw>ON˺7a^ا l8Q$?jo;lN 20&nsS 6hN`B|<2;5; (g4LY2$Sp >U$fAYyΥM,wd)'OfOS&֞w :ПWf>Rn]zߟ"d2PU- Dl02rTv¢?ZNOO=)9mz.c9&trSZﷲf3AM=F,~qJeG#<,ĉ=lA2SH}dE'j")ݜiAğ_3k\5&:FN}߿C2J930Kr*̻dFr"2Ѱm<={jL@B^4B0}8(_A|D9U/~5QǕ2tign>,]n+2}fH~Y,ިqj keY}l{"4_ffd'ZKB3.#='R)tXOiX !,M(F$蜙 L^Ոm6arD 'iFEtac}%w#%Jdm?3` 2U:l'7Fz tS30Tښz?3ELh '$GY!XjJ;M[ AJt: /IV&ׂ@㞩kr㪲0Ze0vpP&A0\x!268Uk_"$$Uus?ߏƦRRDvs$jj5xDdY'Qv٬%Ƹ]̬g0-n Tq2Si 04$L LP&ΡD:-(/7jD~'i]IE҉(K2d,'v8~oue}[t:)h΋P'`KnݓO+@lXE#9g Dd&@ 0f^8DTe$d ;Sb dY N W: 5dn*l +P͔2Ԫ![b&Qw67%ujn)-]qy]Jjx 7|8ZE% U,I^kn+ve9чkl$*${3V bITu#gցFFqd4oYJ؀hѓ1I9 8UVA-A&gfU>&kbӌ]2NeUq JK)K85u*g&ٽ5|Տyׯ_o|qg}駩?IY׿YYYf//{eYp%mYŋҴpаK}>q*i,`R)2qM˜k_o5j󗆟59+^ ~AV\YTI~`Q:WW ǴS LT)K_u]Wlӓy]NF{kINo7vib3a)\B|dflj *=J{hF8FONze'-*=0 >d]O3Ȓ.otsIߞ׸u&Cxx>_֒/j~zJ[5<%).3lzL}ݧ؆zFQ}6c9i1f"N?aSrI&&ӣ1?H~ g.T\m6'w\|"(fJt} rފ2ܘhM2rym @K~j𹽽M~5[ &AN4&^>c/rS b8CØ'bĵyմp]6uO][-Ӈj"j4~Oc\ejٳ=5}SR`[]\c'/n?ܐ4g (F ~g)'cB,܎W5 Ge`%#p5Lr  !R]3aH vo~!=!szΘFxE^q~l\`8RΈUOܙF\ͱ+ @#)OYrUGZ̜GngWWWyj{Vo* SQS4&CFsMxl-3MbuS% ks>̅I/0Im2^/7?.Mqgyr^6Ƥ줯Ϲ;}ςbxcB<0 sOeIʠT'&Ws8~0v<̜Nr "w޷Rwۇ,ũ p+%TS̍^?9Vcmӕ^:(wy \Xw{3,s7ULde_&D0ks jЫ<իW"f\⏳SI}e/Mz-zS=+Tc1HoV.X9.ܼTӘQmLTԨt5m5$ #ϵdOP&aI'3wL҆d%7G˩F0Q)r}V)Ě{lTR-~id f???М :Xh).:+eǧG9aqS`Û:!]L'I.&$iaq}S`AIf8Q^˥[ABߓRd[J8'0T{Y+;xh^:T&j̢Rb]odt LCcplZ&}UY'4#k0ɋO)bŷ}1\KSKh)K) L-еH-feT E oj@QlArsOY$eVE6jg61ޖbĔʟ}rf-)p\x'/jB1?͌_@Uq0'`1-TNps鸞B:Z' `SnUkO_Y)2tIoĸ]V7Ϋ>S|wwww#ĻMe}~~ul5,Zfa#)LlJLoP_egi7fό0ՠMMJ`BUV L?Vˀ_LA[3TӢ+44XzrD}/sP(?{/^ݛ7oՍz :}ﴼO^; IENDB`./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/appName.pro0000644000015600001650000000171412705421114030035 0ustar jenkinsjenkinsTEMPLATE = aux TARGET = %ClickHookName% RESOURCES += %ClickHookName%.qrc QML_FILES += $$files(*.qml,true) \ $$files(*.js,true) CONF_FILES += %ClickHookName%.apparmor \ %ClickHookName%.png AP_TEST_FILES += tests/autopilot/run \ $$files(tests/*.py,true) OTHER_FILES += $${CONF_FILES} \ $${QML_FILES} \ $${AP_TEST_FILES} \ %ClickHookName%.desktop #specify where the qml/js files are installed to qml_files.path = /%ClickHookName% qml_files.files += $${QML_FILES} #specify where the config files are installed to config_files.path = /%ClickHookName% config_files.files += $${CONF_FILES} #install the desktop file, a translated version is #automatically created in the build directory desktop_file.path = /%ClickHookName% desktop_file.files = $$OUT_PWD/%ClickHookName%.desktop desktop_file.CONFIG += no_check_exist INSTALLS+=config_files qml_files desktop_file ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/appName.desktop0000644000015600001650000000025512705421114030705 0ustar jenkinsjenkins[Desktop Entry] _Name=%ClickHookName% Exec=qmlscene %U %ClickHookName%/Main.qml Icon=%ClickHookName%/%ClickHookName%.png Terminal=false Type=Application X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/appName.apparmor0000644000015600001650000000014612705421114031054 0ustar jenkinsjenkins{ "policy_groups": [ "networking" ], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/simple-app-qmake/appName/Main.qml0000644000015600001650000000260212705421114027326 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 /*! \brief MainView with a Label and Button elements. */ MainView { // objectName for functional testing purposes (autopilot-qt5) objectName: "mainView" // Note! applicationName needs to match the "name" field of the click manifest applicationName: "%ProjectName:l%.%ClickDomain:l%" width: units.gu(100) height: units.gu(75) Page { header: PageHeader { id: pageHeader title: i18n.tr("%ClickHookName%") StyleHints { foregroundColor: UbuntuColors.orange backgroundColor: UbuntuColors.porcelain dividerColor: UbuntuColors.slate } } Label { id: label objectName: "label" anchors { horizontalCenter: parent.horizontalCenter top: pageHeader.bottom topMargin: units.gu(2) } text: i18n.tr("Hello..") } Button { objectName: "button" anchors { horizontalCenter: parent.horizontalCenter top: label.bottom topMargin: units.gu(2) } width: parent.width text: i18n.tr("Tap me!") onClicked: { label.text = i18n.tr("..world!") } } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/0000755000015600001650000000000012705421114024426 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/wizard.xml0000644000015600001650000001343312705421114026454 0ustar jenkinsjenkins ../share/ubuntu.png A simple C++ based QtQuick2 Extension Library with simple UI written in QML. Also includes: - a unit test for C++ - unit and functional tests for QML QML App with C++ plugin (cmake) Ubuntu Click package parameters Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/cmake/0000755000015600001650000000000012705421114025506 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/cmake/Click.cmake0000644000015600001650000000151512705421114027537 0ustar jenkinsjenkinsif(CLICK_MODE) STRING(REPLACE "/usr/" "/" QT_IMPORTS_DIR ${QT_IMPORTS_DIR}) set(CMAKE_INSTALL_PREFIX /) set(CMAKE_INSTALL_BINDIR /) set(DATA_DIR /) set(EXEC "qmlscene $@ -I .${QT_IMPORTS_DIR} ${%ProjectName:u%_DIR}/${MAIN_QML}") set(DESKTOP_DIR ${DATA_DIR}) set(ICON ".${DATA_DIR}/${ICON}") install(FILES manifest.json DESTINATION ${CMAKE_INSTALL_PREFIX}) install(DIRECTORY "app/graphics" DESTINATION ${DATA_DIR}) install(FILES "${CMAKE_PROJECT_NAME}.json" DESTINATION ${DATA_DIR}) else(CLICK_MODE) set(DATA_DIR ${CMAKE_INSTALL_DATADIR}/${APP_NAME}) set(EXEC "qmlscene $@ ${CMAKE_INSTALL_PREFIX}/${DATA_DIR}/qml/${MAIN_QML}") set(ICON "${CMAKE_INSTALL_PREFIX}/${DATA_DIR}/${ICON}") set(DESKTOP_DIR ${CMAKE_INSTALL_DATADIR}/applications) endif(CLICK_MODE) ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/manifest.json.in0000644000015600001650000000063312705421114027536 0ustar jenkinsjenkins{ "name": "@APP_ID@", "description": "description of %ProjectName%", "architecture": "@CLICK_ARCH@", "title": "@APP_NAME@", "hooks": { "%ClickHookName%": { "apparmor": "%ClickHookName%.apparmor", "desktop": "%ClickHookName%.desktop" } }, "version": "0.1", "maintainer": "%ClickMaintainer%", "framework" : "%ClickFrameworkVersion%" } ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/0000755000015600001650000000000012705421114025206 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/displayName.desktop.in0000644000015600001650000000022112705421114031447 0ustar jenkinsjenkins[Desktop Entry] _Name=%ProjectName% Comment=My project description Exec=@EXEC@ Icon=@ICON@ Terminal=false Type=Application X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/0000755000015600001650000000000012705421114026350 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/0000755000015600001650000000000012705421114030370 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/run0000755000015600001650000000027412705421114031125 0ustar jenkinsjenkins#!/bin/bash if [[ -z `which autopilot3` ]]; then echo "python3-autopilot is not installed. Skip" exit fi SCRIPTPATH=`dirname $0` pushd ${SCRIPTPATH} autopilot3 run %ProjectName% popd./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/0000755000015600001650000000000012705421114032636 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/__init__.py./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/__init_0000644000015600001650000000205312705421114034161 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Application autopilot helpers.""" import logging import ubuntuuitoolkit logger = logging.getLogger(__name__) class AppException(ubuntuuitoolkit.ToolkitException): """Exception raised when there are problems with the Application.""" class TouchApp(object): """Autopilot helper object for the application.""" def __init__(self, app_proxy, test_type): self.app = app_proxy self.test_type = test_type self.main_view = self.app.select_single(MainView) @property def pointing_device(self): return self.app.pointing_device class MainView(ubuntuuitoolkit.MainView): """A helper that makes it easy to interact with the mainview""" def __init__(self, *args): super(MainView, self).__init__(*args) self.visible.wait_for(True, 30) def get_button(self): return self.select_single('Button', objectName="button") def get_label(self): return self.select_single('Label', objectName="label")./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/tests/0000755000015600001650000000000012705421114034000 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015700000000000011606 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/tests/__init__.py./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/tests/_0000644000015600001650000000324612705421114034146 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Ubuntu Touch App Autopilot tests.""" import os import logging import %ProjectName% from autopilot.testcase import AutopilotTestCase from autopilot import logging as autopilot_logging import ubuntuuitoolkit from ubuntuuitoolkit import base logger = logging.getLogger(__name__) class BaseTestCase(AutopilotTestCase): """A common test case class """ local_location = os.path.dirname(os.path.dirname(os.getcwd())) local_location_qml = os.path.join(local_location, 'Main.qml') click_package = '{0}.{1}'.format('%ProjectName%', '%ClickDomain%') def setUp(self): super(BaseTestCase, self).setUp() self.launcher, self.test_type = self.get_launcher_and_type() self.app = %ProjectName%.TouchApp(self.launcher(), self.test_type) def get_launcher_and_type(self): if os.path.exists(self.local_location_qml): launcher = self.launch_test_local test_type = 'local' else: launcher = self.launch_test_click test_type = 'click' return launcher, test_type @autopilot_logging.log_action(logger.info) def launch_test_local(self): return self.launch_test_application( base.get_qmlscene_launch_command(), self.local_location_qml, app_type='qt', emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) @autopilot_logging.log_action(logger.info) def launch_test_click(self): return self.launch_click_package( self.click_package, emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)././@LongLink0000644000000000000000000000016000000000000011600 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/tests/test_main.py./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/autopilot/displayName/tests/t0000644000015600001650000000124412705421114034167 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- from autopilot.matchers import Eventually from testtools.matchers import Equals from %ProjectName% import tests class MainViewTestCase(tests.BaseTestCase): """Tests for the mainview""" def setUp(self): super(MainViewTestCase, self).setUp() def test_click_button(self): # Find and click the button button = self.app.main_view.get_button() self.app.pointing_device.click_object(button) # Make an assertion about what should happen label = self.app.main_view.get_label() self.assertThat(label.text, Eventually(Equals('..world!')))./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/unit/0000755000015600001650000000000012705421114027327 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/tests/unit/tst_main.qml0000644000015600001650000000222712705421114031663 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtTest 1.0 import Ubuntu.Test 1.0 import "../../" // See more details at https://developer.ubuntu.com/api/qml/sdk-14.10/Ubuntu.Test.UbuntuTestCase // Execute these tests with: // qmltestrunner Item { width: units.gu(100) height: units.gu(75) // The objects Main { id: main } UbuntuTestCase { name: "MainTestCase" when: windowShown function init() { var label = findChild(main, "label"); // See the compare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#compare-method compare("Hello world..", label.text); } function test_clickButtonMustChangeLabel() { var button = findChild(main, "button"); var buttonCenter = centerOf(button) mouseClick(button, buttonCenter.x, buttonCenter.y); var label = findChild(main, "label"); // See the tryCompare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#tryCompare-method tryCompare(label, "text", "..from Cpp Backend", 1); } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/Main.qml0000644000015600001650000000312512705421114026606 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 import %ClickHookName:s% 1.0 /*! \brief MainView with a Label and Button elements. */ MainView { // objectName for functional testing purposes (autopilot-qt5) objectName: "mainView" // Note! applicationName needs to match the "name" field of the click manifest applicationName: "%ProjectName:l%.%ClickDomain:l%" width: units.gu(100) height: units.gu(75) Page { header: PageHeader { id: pageHeader title: i18n.tr("%ClickHookName%") StyleHints { foregroundColor: UbuntuColors.orange backgroundColor: UbuntuColors.porcelain dividerColor: UbuntuColors.slate } } MyType { id: myType Component.onCompleted: { myType.helloWorld = i18n.tr("Hello world..") } } Label { id: label objectName: "label" anchors { horizontalCenter: parent.horizontalCenter top: pageHeader.bottom topMargin: units.gu(2) } text: myType.helloWorld } Button { objectName: "button" anchors { horizontalCenter: parent.horizontalCenter top: label.bottom topMargin: units.gu(2) } width: parent.width text: i18n.tr("Tap me!") onClicked: { myType.helloWorld = i18n.tr("..from Cpp Backend") } } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/CMakeLists.txt0000644000015600001650000000144012705421114027745 0ustar jenkinsjenkinsfile(GLOB QML_JS_FILES *.qml *.js) # Make the files visible on qtcreator add_custom_target(%ProjectName:l%_QMlFiles ALL SOURCES ${QML_JS_FILES}) # Substitute variables in the desktop file configure_file(${DESKTOP_FILE_NAME}.in ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME}.in) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME} DESTINATION ${DESKTOP_DIR}) install(FILES ${QML_JS_FILES} DESTINATION ${%ProjectName:u%_DIR}) # Make the autpilot files visible in qtcreator file(GLOB_RECURSE AUTOPILOT_TEST_FILES *.py) add_custom_target(%ProjectName:l%_AutopilotFiles ALL SOURCES ${AUTOPILOT_TEST_FILES}) # Make the unit test files visible in qtcreator file(GLOB_RECURSE UNIT_TEST_FILES tests/unit/*.qml) add_custom_target(%ProjectName:l%_UnitTestFiles ALL SOURCES ${UNIT_TEST_FILES}) ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/graphics/0000755000015600001650000000000012705421114027006 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/app/graphics/toolbarIcon@8.png0000644000015600001650000000443612705421114032166 0ustar jenkinsjenkinsPNG  IHDR$$tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp -NIDATxڤXIO:t2vCH! BB\י0Yj:,$c.WW/&H薾q."ʲ̡ߎAt:wa,Ǡ%I2HB<#Ad"dzVŻ]>Ӝ; 3@cAˀe`lü;orfprp]9waf4}Ӵ\+;H#k}L"L8Ƃ~ي|K57fAxf vʊuSSS29DfQ??9ZZ$@r?lx'dkkˍkڱg QdSd@?Hh,6'yw ߂A[ZZ VrnjlT̊U]MQQs`½=fEs'r༁YB_6XP榛A_زuȑ jV3̀.//I1o 6h! 0k LY 4ic^̖Ea]cMӉewnG Q ykp=&n!qr#b 5dM&S;~&33Y[k뛞o6]/Ԙd%A޶t٦V RYj:=|!߼ Q܌U׾!h: JSlMLP*> R߅eɌEb6#YYe=ǵ } Z4#c;/溘tȔG+~$1N>ҵ/C TBNOOU_\rYA2LfKJDmsXxqtK&p&ѢՉP;]2[('/KJ'𸟚 q'b\e2wp `, SQv 0eK<נ =Aʣq9F_4tCgJJg8} q$2U{^P `ۭi;H\&x4s@1yEpKI w/[]]lXxx㣯zؤ'WM 4gmll]=9oQ-O#p|{@#7bgf|H;j}I6WnFșufpgUޅmԼq;V<9N2s%c|oVMNZԫjHO^x'k#@d=SEkuMeϝON.ffykd뢴kas4O]COOO8y=ڂ5 87;N3ǬVa@r׉;S}f|.2gSה.p{ Ùxݷ;n8 Y*d}-3 `iɑ k|1<==MdyuzͅXiys'HIrSdYZYו7+33a‚3h b[??+̃RϽVevo;߱ӢKxsCZM}^=Bs扚'~FLxZîZr.n{tt4sԿe0V;z/'⺟|O= tl%3&fL*da epfFk$nc8אi<y][Vi3=3A}zAҧHICT_f)꿹 '/n"Zk/_t>67q R,s^SWY=b$trn=C+bJn Olfu+@@9YfT\|~ @)]m~ZvQEZ$D6a899|߹3Z/ɇ0nlNk0A5~>|Y/Ȏw.]3L0R!B̀e_=~sml6lr]0.q !ZJ{yɻawחˊ*LϲDW-,櫛'oi$)6#=l)憪NxhVb^_CX$G!W-{ `| A!{ٽk`*OGdpz '/!Lsst!g9lIZZ^oq[2զ@6v noowbl(>dޑx` {<>>].?Kd},NZN;Fzd.2eg͑vxr-i2gGnDv9X 3;07sn fBN)1?3e\k<=dy``krp$t Zw 6dEq||<{"TycD)f1#)6yF*,)eXO&K Fkw2e&%6= 7{ k[93׫& dpdG;@&.lH, 2:d7wGN0ٕOO߾}D1X_'Ed\U5$Kř۩(5HivR`HFwn$̉x2h3Ӳ>x:H>%OQq3'e uvlz#Xpt&+ 3j/ Ys呆46Q_Ѝ{ʛcaAq21"g=<{jT~l@iQcbZ[-Фbw؈=cDY#Ʈ%8\A<)v˃z?@%l]c; Tܥr5,emOOOxT#ia(!Ð$% 2uZ`Yf;/Ҝ`LR^ɸ~>EYӉaV0N',SRCyhTeo`|?E>ʞ1SdI"9DpBfD{ ˾>߉ѳE j05:b'wd)@bFN:{C?SbM4;Sk@Fe>3ʴZY=ѳ.I; ZKyM'oCl}l0"fE]i)wRlr X{7͘ L%S~OKt;9âyw炫/;HɛI46m." ix3%U'-S:%s*`--ߟѢ;Yt~߷oߔk;"gn"7! UP^=SpIҪ'ƃ$Y}N{(b)5d {G6N0z$.On. 2qdtN_C2 pl/V Hz)rA0iƠtb%g9Kw zցY,۷o-iܬ^-kM-e  pPYD̙; R_)l 31/o"I6DQl0.a ğaZW3g=1YS4]lZaמM]yMsIAI.6of,:~c )Zַ5aB i3do f'4߁Wdڻq`1i=DǔӨTgX:Y#<10FHPd5HU3"LyĜ hIصORȔѯh |\ J |Y0>2ґIyxdWF= &bZi v\zSuސr`$3{\- rS%Gx32@+Olo[SٱBϯ^9.qHKϠu5&<3c7>3%5fkemӍ&^ ޙ*(AV7vSΞ;ue35$)SL ʍAwxY} rcGb7[fZCF?-\͎SgNu HeY. &}9eWMqiQg&66uG[4i_\D VAXWfXj8 lHtʲײ Dm(#vqҴv5MDWYo=?I&nQP$&[֝d12[,K5*:;ǜXǷa']H&1SPVcMR:] gik%-ÃzxÞ45Us4x 1 s6!ā)2{ gYOg9Ip}|3ec:Y4qGgc Q5Й WҰ4#:ÉL)iXii[**ɕ E:c3-XʣAs3tĐz֯1$Ȏ =M4a%4o.%UwT -\*d^Vɝ΅i5hh,EΤi` 0.߯\uxSQ3yL(T}벎cLvf+C.Yw_t񝴕fډ)P{7dj|0q32Nf>;;ۓgΤC@j(5Q$,k$ӗO(-Yv]| q bN3 Dɨw\uB5C,خ3tryoYˈ[6ݑL)[XXpS"ND,<\5Y noS2KTtxv$ M$b3)&ɏ9tԥ'Ӎd_\\*PiϻѣqRrMdYǓ `Vޣ 7`0v#TIKL%ρ?;))5YQs^RYw!@G-+LMh an継Huj b͸`K&k\Miy3j z"9g k]ia(^gxZMڳ 0x6ּߌܵUsݷ6%FwAN?fW8LmQsQIM|wi[sy [ug;4=']vr]<?TKA5Ñtno-13=M`KRW=G\z5L6:1JZwGTnIq5xߛQm5ɆLچ%KO-IT=מD,ͩc3=hݼ) dO|;54GNfbO?AQpvO!H\Q5Jj2b4y*7 +V}gw?g|xAe΀a IfSw{v&wN<gV g] sp,fbcgcjv4oD*u6s">-X!|hsJ%#*Xiabr+c$u-WHAJ~ E`}ӿ,:ZkƎ#n35& N=#hь-Sb-OΛ>\ tKBQLJO2W@5aތsr&1%oE;Eb ff~ڦ dS~:>#rgdΡy[KgZaxxxU4"YfwTjn8#SL3g?S%2S!ulja&`Ğw^kX9r䕺Sesib&VJY/s:zsm*@s6HVđg2s(5%f$e~s5x X/jQd6N CF2Qn_><<'?-,0qxHgLdzsjN^KKXs2U,S֒'''{vڿg߆aXC~a%삌1-g*OG,zlLXf5̇y\-I3cJu,J/AnO`bƒ2RWM!\̬{n3>Ale1笜v;Js)C~՝/@KcZxR6Z^'ו hb,=;텵{||f3 7G@Aɫ?b "߾}e2~E?KCMm"r;NSueVs\iA IDAT.0s6e6"N\Y$5G jfXcFz*+͎+eptt4&YQeO]wM_sINYjw`um~N [v~?>45ˣ (?\W{خbff5N.=:' ۡ6sӟ G` L/-G oHkRd91&w4JF$ƐD:ܬv͸Al޷۷ɋ_{$,œ3Z1 m'7tGUf4HQ].wHB[z+vR~^gbےIfVԞAxd@~0s|}:ڙI+T#cXՍ9_4?VZl-sZuuu5md)Bϟ >eƕG-u4Ș54@LJ?|0f|_ |3`*54dZb)y&AXQ7Zq3}Ew&v;~a vh2#b&ƚu94_'F&!y"G8Ce`Hd9> :,(L>{\۷yΕ`dw 檳z,ӻ䆵֠vso}Q]~dVJ6蔬 6AD;ϜQ❢RlnLnKRe:'Y zxx| $KCz:^s,c2!*وgĜ[20 -a^AAm9qR' 6[*];'0C#`AyL/ijSZl}|Ky܍FۦΔRRkrQ̚F1kvҵ'K1;gn\puرMsH.|vUa4ac.tR s3Z3L:yMfyc9f]C2^v`CSux2Fud`eɔ٬˗7umnxf4%tlɚ ?"*Y)Xs{{;^|ԝ\pA''ی U,ӳkea[E$&x0;1(1Zi fi \9Ӻ+Lˌo6 rd_Y3eQc3DZj_ud&e<Wž[ym -Q|ظ/fnAڰ1\u2爽l?#E;ov#tGdkʅbd`JߓcQhoP|uRw)l6%N]d3龷mjCUğTjj,ؖdc͟%Zkw,I05F,hoD6"#64&Fԙ\b5$3n2#f.L`E76?cAgSƔXFFXw݌ Bl7مV2|5pXmFN L 3LzPv22x1@yemeL("5s|ب(f | 7 \Ox34._DiNUtr<S6j}6M 0OrvPL[Pl8==,.cp)v0Ça)I/{Vb&S)VF73M'Հֿ+eIXɸc9atw"rڲzxxnnnZ]7o~fP5Q P90;=*Pouyy91E;U+r/{::mhu\g`_Y\ w,O#[e}62~3}"|Ҷ^(Z2v:ez;\lO< K)H:Okkۑngr\;i$7b޿+_P֪4u[&۶/N?$`V'9(8לeɝш#K͙62a T^>>H3gvO~=kvI:y9gv߰R]__޽#`,NW2N03kSL\ ӭ[k957;:Wl>OFCdDdfvVv t&wƲ% G>oLŊdZ{g SLr*On]Vjc=`]'Q.~B#("Ш;"\<13(܋NI`0]86^QK:rԪr/MXbIS>HzH,"OKh  s)_Lˤ|/l.DRu'=AVt4pєG_kmKtyxxP46"kD 888kCfd?+>kf+ f$Xe6qIuߜO[ЩJBE~aXDNcӥ@(fp؉խEf sV[wtƒJ[VÛ7ox :,)O>Ǐў+|ݤʸ9aHq d9a"2\@;q=,[]3Z)$dke\{ 攲n'CEkT:_AwV{"R#[Rf Գ?::NNN&>Z۬U eo]% ܵ,;#zd+ڌ6P<cNމɖ 9Fv"efnnooK}iTyD;_fD9/b^w2V@I 5:sW;??...ԕƦ_t|/6n_jHyLn`^>*| f$ 7tIJ6 lE2JI jSg7s 4LXA|x||t0 wwwsB"VdϹtn˗/Yf /'fN۳ُseK d o!ܦ2#N6vq!fzd/jgvG8%b0 5锻L`~9?(k٭>,L[k7Mr9yf&pvv6DYl(Ꝧԑ뫺Mx?<<< F F~;L3߽h=eիWŅt*OɅsc*lCcgvc{0 v>|yn$?sOYW1 張n I)MƐ{7F1cǽQV j^~=yF_|_~~2( O{zznoo8_^^F{NlcuH2 ƿDߐYcJyBa:U圹bV+mH]///rK[-`K3}fgO(Lќ; =lJMޫ ͟R.Z 0xԦD+߼y3F,bJ/7I}X;4>wj18UfXG$js'Ӷ74K%٥f&0}:2wqϗ|g Q\'[*L:A }~z_6[!իW4''''#'~$ unB/;iAt_&A~ɹ,7،=l&ley ]/0ů.@YUrRҀCjXjy͡fEfiNJ$=܌svL߿WO<l:eoyo޼4yo߾J9wtt4C3 \C̠S RPlZ_޽{7ǣ 7իQV{s=ڵϕ[fuǏ6ʔ|*ln07FV,X |iͺ-44[:Yo>6) [1ly7YC8n]%U8 &pķ ܤ=N"䗿kIsn*i8%RirYli1׽M9țbBz7pѡ/OU@Uh`b:>݀|._mYj L[;|'Ǐ hIfj`{֬ę>??q32C;g샫QU>3gT~t:1H'pq ڈ9TuAVK,Xo2߿~;Ov;r d`R꺘te;-'''٤gb']&BF*L2ze_]cR*Ort]#~gց乁Y~Ӓ/Z?;{򶉜{룓fi|v)%fAwaMLkɐF|xx8}vsB'aLp* JC0*3# T|o 8YYY<}vB¯ ,]vѱHJz=Sme3*`|'Ӳdv4  4n;^f&S(aV_t%4F梳{߾}ڜd5U)4e#\l?g'p~NMVYך_a.ʺ7o;z"fjK ㇺ M,s1JC<;"EoucN߿tNynjceԦXb &TN.yLI2VZO:,sMxN˜jYFz3=S42heͬܐEK˗c""dA9:1*6Ң>}&Mc߹Tt3:muXO7\׭Cm,X|:<<7{|luRb1 5ߌ Jx;r&L)Q^nTA^ H۷ɜDRdI6h=7oLv_u #Aw\I~˔ߘH ]lNc|EA999P22\ ]Mکe՜;HQ,&VsCSi|qg???o޼Q"E%,%d0͘z>;D0x ~zO)Af;irxxx+\C~ f ͿgB1irJ% dVKV_k: +F'Ґͦ9*_lֲu}\NN~)$+Q]բ] S{/ IDAT鲊Ji0?YÕDXVU Y*LAb0)m=0K'} ,:muxx8<<< mʂMV쬦b6Ym.u/˃Zih"G'2`/ceˮDM[ʅk K,ܵ{IA_~Ke\=rg`H7TJwxf%YDf׀@;͢bUזEt%a96~nl5x||O|ٶ ?///CnE1 [JUM[יڳi=;Ɓ?::'یMg0^o߾8Yz/r&K 6b/l)FxZ͙N䟧kLxsDImLu|IDكgZ˙Vo XЪKQn`n PW>:e)3AA&.0~Lo T3R-%k)l$3WvRsc|I<];<1rꭥ\dߛl0e%bmXWU4<#sɬ IR6QނڑNr[|ڛ=Ɔ?qxDE'aleQ>N>?s;¨hVy@9p'?Fȅa&˥2i[f=cFeKCHY::WXG3Tl;k6mF;jGcCvª3&vp,HBcuS`ggҦ~O #PXMbjI4M%N.3?r':ns>A+d.IlX0V['lYIzӉ%J;iTm8&y*8CbK \h녓Er1c{{2`aiYFQGzʡ#̔ y6,+͵Z-ugy(TYfJTHME?20F@gfˍrRnĪjadmlpYK|5OXj$2|+͡s3먿[c'պf1ZL٘PsE9w2'qEiƩLR5 qV*>Kc([$F!CQV8|ymH3F^؏?ƀS_nLhm@*sBܠKUY.SKwʑ/, BaYbTYvb5Nf6?F*PTS߲ȇcщ5UR@SƑcLapʠE=. e@Ǐij1޿L,2,6m mL-RQH,Rps5 ˒dCL;MBJm[&@i i84mre6\9.YM]O6(ݢS+@)X> 1 %eܬکN7cVQypp0/{>SÇ{Az3[q3Pe)Ɩu6+O}r]HPnn(j7fGj`<]`ڜӗ-C,0hw!)O@{2 {;N8#f aeV~gHI<ǙqE0ʀܷojiRMM^JMf"фupg\YUkZgќ;+UMMi7SnLRX}p9+~{˒g9FeCnd=V%矉0-J 2d"Q`a Q&' `Zl*&QegY'i[`6jo'h&df}~; $V&w@"{>xs+dk&3lO5 * 0>݆q9cfΥB3ȧ;r Hh ,d\0H w"{%;fGEzQ-Z%"_DYl&$3 W" ;\<|]:β6YfL0ߛa",CX LZsv)xbHIIJSshdHŊ̒it|l1u|᝶(2xOfC0K BfGOrA)[Tnz5$TDU1x/ͬ8ds}_93=ˮ~"cL!*):Ev ВS"ּT v9Kn~NhL5q=ivґp2U6IjVle\H1uo93°nDxsQr'Y3*[F`5?kN% dW+ґ:9* }5YsKiƩ/_Q!Ɣp3FleKK@X)Ŷ`; ;v.)`L: hc #lɿ r-VP׽^'<0j33#-0;'d`fRJQnΔ]V|l9ʝe2 yŝ%iItlf<m0ԚOb^rCaf9=\t.H4lhc[^zGh1"X7#JezO4IwHP& ,,S\I|V:&{HNncZ0ي҉ֵA^S(ѻFRCDLENj<hhx|/BΔN.cd"Y#G$$ޘ X:ϿYu'?yd~ ro[cmsPobC[ιaXFiY{fH3 #,tZpv;/_())udo}e~~^z`NrT8yh$Y7Xpg0SWPeeQ GaLNНs馵Y7ͤ ՍNJ;ڂB<&]5F1ȧ1p56a $S(:%=X{QYߓɚ_}>oooϟ?OOOOׯGzfi7.e!ÍI^L]DtTfumrX$3 qq z>f3X>...=P7qe899tD~KB3يDYW\yZuƜ3N0f gvdXZ'/].+6cONca&'Β-D-s$$Sl0ue۰ˇԹ$fe"# ZPƺxLci]C& Xzz:G)Tٜ3xCuwzۦxfFAni3@%󱴾?q!fۀΘgE^})յً֮4ڴ:\Np6{f $dDҍ4dnڌ!s:m+ɓP_f\@cܐ_]kx899cɮ\HHp\[=6x%%?R;3|弅|T Yv&&r&rghZfb.ngq1]Tդ b43t^Mʸ-)mԲEfvQ6N,^z5ӦI!1%K#9z5ߏl6bɃ[iTw3!/+n8 K1gK6ͳ\tުSSš&~oN |x]d` 瞱rzzw;D krܼ|؛fL~6ׯ%[YWZ$@ `Yj8K-]CgXVIOuIm6gZkT TFz겏L#-1'ò43Wa&ȅeCW"Z=kG؎Y.vUgG!o 2,N2H/; ĩgƢf nzxr443ImNcG,\דuE 8KTm50؅@E;p]n+Tge }%ͷ,t/5dfg?^4bcaٔڸٛ7ϴfdMV *TeIOޣM[a#do`O[|xED<;!s(իIgNlڹyPC6ٙ2+􃃃QY@' y $QV672H*{DԫU6{=OEk1fr\N(2>zxxe7MZ=Lehٕ0Kw8%I*g#hY!˞H33Wf@F2j|2I6;63+ }%9܄ϸOԀ_&1%6ԃiKzec|!ӥ8[cIIEc {Jf % g0r"uFqjM*J .$X1:/y{̺?3٬My OOOz+X2Yۨ᤬#qxggg~-ѤsZbM`=GaY݃ONNu#tae![VWX9y8@IBB:慿yffB!|e\F5!eo0#z}}wJp@- ᒡFx >f}ɭg*Iw&ȓe^2T7 =7ij ,= sV24biXrӧ~`' Ht;INQ}#CgV $⨑yzKEחikݎ`uXr)el6cL\ A+˔,Hyfx/ F նʹfφ˼ i9䦫MQeGNY6;`*TTa Y]YP|q876\:&8S/([.˛Ouy.aْD*I+~gm@)KH7f’T:)2e8;;MffYZnX-(˅:ﯿKԄY W8|Vd弆:Z>ZhN 'Kߔ^ZLqɠ &Ud,e-ljMBf[mj:)8Nu}Mn|///X~=??^~=|a8???gin.ࣣ׺@D\*7YME:=]}v M'+8Z>%}Q>ݜi-OZ9 8쏲8;fHnUȠ3TƉfۄU/mnz#K9Rlr ɶ,e~:l6 }e{\qd]=F,yd@\nG<)UmY}SKob)ðySgN_^^N8L5e5NgT籎/NyyȋD۷{וdj5:a |'-Y///HEdG.pUv;ϯÍuU]gPA&LYgcaf/\dhiNBWΚ(Ai@Z$ \j5Aɹx2“a,.3C13@pww7<1)F!VlФSdڌov`a IDATۥ+W z||@Rɀk:Tۦ7:p,JLcݛF:Div`|ߴ3ay&#)Ȕ*'M`FЖ1ˀXE4 ^oZ ڵlϟޓrAeWRgvh7ԟ?>< h]e}eIj.Mcx/UfRrKe+Unl]|4\Dbu TevG2F-;3x؃@4ZE:oujeVONc$v^<<<Vhfࡅw D@@+h߇ϟ?OZKdL ~{eEYLDN 3/{8T3aIaї ]Rl#Vظ$;e3XPύW/bH#ЖʲnIr ߾}KU.o޼lI^uHH٭юSG.f2X,(ap>*>|Ƨ9>>>~8e ! MʍXrf|mGl3wu4Wa'$KEq0jf5k#+cVDkt.SIlyFx6,R Eӡ+3#nDt}RΔͦ3ȓ(s1OFJIrssf}v*`3r{nTL'T`܈U}d;͚tfI" <)5J#3EGԲι2EK:/BF4o}NpdI!LAMGHpRNIQ9f,iҹ&PV3˪5jih91e~f6UH|1dR땀)q.6,+6ugY3u1 J2]bj `ͷe#y_6, &nv >,N(Qfl6ϟG ؉Xm] ydt{KWA&($pu71==NNNRi9ʞ쎕G,|5eYu~Z\X/Ot: g4\o2xq. n^v#9_~~:qbgƲ0ώD(Wdñ0|^R $gw 8vׯu&π99%ׅ 2 O^2i8|3աP^K3˴?kXk10Ӌ9+L |Y ck*5b *>92v><|eGѐNrg+~5Qz4pi653Uv ^^^FY'kRi0qvejLzOFhK:  Wj`fK_KLa\߿bKAn^>r9VS Wwyy9q%&К(&gf>L9F)X۫F͚7 qDf5^?>>fŘ5s>OrE(l5'P#s11S22+e@(ɼ4ga1B4Y阍Yt%B׋M{ g'IJ_ɵyjכymb& 3 ׯ_WWW*)Znqlujn}OIa57Nml"2tn.aWȔ9MDbde烃͛7%9yH7J2x@){*, $e!  [.\$X"-$Cٮ)vY_xԋrsP@’\ؼyjLIT471|)K-^'ߺBsYs0j}vi5sO]3k5q%yzzsafʝMtI}\{ֶJMAw(;Q"VEZ6VꬁYi̺ȜA$w:jx*96E}³Y]B5˴3X$摘jS|lb.׌Zfkf¤|l2vؾ.EY*s*p(Kbl}-cF^xY[4c+]~t&S_;xXsWtrL_TeC<*9 ia?(0l4܏ScfPNgb $5qJU{,/<<07l\JG-A&qrsRp2=/"%CV >5Iu%txrNe4\p<92K;K;!c?cptg^ Ů]KIZӓ&BeF~t)=:rR= .}fMAϴ@̌LH&)"AƙFu8R+;V[%6_2 `LK~n׺Ln%y&[w*5|ʞ?>xQZuuuKf/_&~GGcfʌÇ{8a7u;h&yP$a&;8G2dFA:.0}IdFʗ=OF=G3bCIxzz'"(M\v:/Y a^Ç&|܈g.tbIY ePk,mMG2Ek6Aٜw?˗/;0L{MAHrCr2d/ $cb {d@i {=w[̍/+d{`ݎr\+:F ǐYǝu6$]J6Ǐ0:Ce9m R{5ޑN\#ƙ$W`iAŗP> AL;j͚2ìВ/%?4]!'ۗ8l!LgjCOfU;bKA mz?9 ZvrIAiL>5 #O:۽I.d/2ݵys =S.ԆXB)7OiN#6.:Lh ^Vnk {hRy8?? f1%Y#իWÇ4Oʶ );bv#0. ;I* #FletQ[T>'En>>>N5 mLF]0X7r|gq?;ʭe_:W$Cy{w٥eg6aZQ PXv7GUq+23z< n۴ K988" &%#AM:C dEplUe/6E8 *ģoۤ1$4$U"^m\;q0a65eLWd^W'x%F3ԕ*ĵ\A] zۭN+r3ʌ~qAuSlsg`}ݐ hq'~WIBFcNtmHC^ywwwÏ?S)&kxsx!THs2S+49djy?t`>3аq_u:ǖLN˿ t:KeHDf ξ|7l! \Fh>LsV<>>5+K8<0Qt͡YuF81߂3ZVnnmvkY 1fԵH<7\DnFќ_^^>-2 R7JmQRrSu}3&g͞2ImYm0ş Uh439\j7{nس=M Vd^$Pl;>UМNsnMׯ_OƇ d", &mD&s)"c4)b՘ E7kϟw2逢Ǩɇidƀu)TPYtusN62! z?13eJKIZ ^:M9:yrdFrf=gfckLS2jK'mA)h4b}}gKfhŪkOPǏfٓM[J&hB͍E cnf@%]l8??\a%PI6.p:05׵@3ck$~.Ft\j R PO|XD9#B'oB$t]o YkPT:v.O4vͅ#xrqцְ.sIEV材2T s Lֺź~goCF3wcrHm;|(3y]Ǭ2Q+Cm) 0&,~G/lXmV)ȗXݰlt22O#tXT&6n;BfI"~+$KZfIL˯(` 3IS:<朘XѓѸUuO2RPmVN|l&Y=(Rz-B7`d ?f ɓl!"Rc9(C aFw3✆O;;;1gXu(NKñQ6gB$kѿ2=;[w1u69ײM2\ş` =6" k/I!Tgg+p~v\JOķfExxx8hȺ8  W f\ &Z~Wħ: emm~&Ć~v:J30 ZZ*aofbLӸ12K Sٹ 2v ,~?UmOuasٚ!)c2 3"N|Nf>۴guMfTqWPM7Yzf֑֜aaY 7;l;`ka3eHSYBj4 =0I+m2]O95!x:;(3@rZ ??fĞ0_pּ4j4sUyh^u$*ZVn#1H:cƓmܤ gpYF 6 SYr-eڡjl_3Fu%2N6d_N2s(N`s\vu79q3w5uP.4*ggS8cI $'2-Ό؉VJ}dM޽i,̰ڦ:Xk7F,b59Otn|A|@Fdji[𬹈TzĹvzfxtt4MR9vJLI3SbBKa_ASmZs | rȇQ=''=Xa&uL"MkN[6qmXr&]$xh>@_6,ۭ@li1 `\E^yeM57'#v7(Pf[E5A-fbUN06$~OY~ܟ3|٥.^?5$E{vf-ɖ_]hUkӀIwnXX<=LtQE6Ն" zzzg I~ɔ&d$zaٳK{1/9ڜfRJ୛ `-ۆls %@f IDATmN%6l)e+K1ד13RN$sa:>ݔر~c\N-8,OL'9^t'miϳzM eչ"`;.0jj\_x*5p3kN;mf)MJlUOO )gw>ON˺7a^ا l8Q$?jo;lN 20&nsS 6hN`B|<2;5; (g4LY2$Sp >U$fAYyΥM,wd)'OfOS&֞w :ПWf>Rn]zߟ"d2PU- Dl02rTv¢?ZNOO=)9mz.c9&trSZﷲf3AM=F,~qJeG#<,ĉ=lA2SH}dE'j")ݜiAğ_3k\5&:FN}߿C2J930Kr*̻dFr"2Ѱm<={jL@B^4B0}8(_A|D9U/~5QǕ2tign>,]n+2}fH~Y,ިqj keY}l{"4_ffd'ZKB3.#='R)tXOiX !,M(F$蜙 L^Ոm6arD 'iFEtac}%w#%Jdm?3` 2U:l'7Fz tS30Tښz?3ELh '$GY!XjJ;M[ AJt: /IV&ׂ@㞩kr㪲0Ze0vpP&A0\x!268Uk_"$$Uus?ߏƦRRDvs$jj5xDdY'Qv٬%Ƹ]̬g0-n Tq2Si 04$L LP&ΡD:-(/7jD~'i]IE҉(K2d,'v8~oue}[t:)h΋P'`KnݓO+@lXE#9g Dd&@ 0f^8DTe$d ;Sb dY N W: 5dn*l +P͔2Ԫ![b&Qw67%ujn)-]qy]Jjx 7|8ZE% U,I^kn+ve9чkl$*${3V bITu#gցFFqd4oYJ؀hѓ1I9 8UVA-A&gfU>&kbӌ]2NeUq JK)K85u*g&ٽ5|Տyׯ_o|qg}駩?IY׿YYYf//{eYp%mYŋҴpаK}>q*i,`R)2qM˜k_o5j󗆟59+^ ~AV\YTI~`Q:WW ǴS LT)K_u]Wlӓy]NF{kINo7vib3a)\B|dflj *=J{hF8FONze'-*=0 >d]O3Ȓ.otsIߞ׸u&Cxx>_֒/j~zJ[5<%).3lzL}ݧ؆zFQ}6c9i1f"N?aSrI&&ӣ1?H~ g.T\m6'w\|"(fJt} rފ2ܘhM2rym @K~j𹽽M~5[ &AN4&^>c/rS b8CØ'bĵyմp]6uO][-Ӈj"j4~Oc\ejٳ=5}SR`[]\c'/n?ܐ4g (F ~g)'cB,܎W5 Ge`%#p5Lr  !R]3aH vo~!=!szΘFxE^q~l\`8RΈUOܙF\ͱ+ @#)OYrUGZ̜GngWWWyj{Vo* SQS4&CFsMxl-3MbuS% ks>̅I/0Im2^/7?.Mqgyr^6Ƥ줯Ϲ;}ςbxcB<0 sOeIʠT'&Ws8~0v<̜Nr "w޷Rwۇ,ũ p+%TS̍^?9Vcmӕ^:(wy \Xw{3,s7ULde_&D0ks jЫ<իW"f\⏳SI}e/Mz-zS=+Tc1HoV.X9.ܼTӘQmLTԨt5m5$ #ϵdOP&aI'3wL҆d%7G˩F0Q)r}V)Ě{lTR-~id f???М :Xh).:+eǧG9aqS`Û:!]L'I.&$iaq}S`AIf8Q^˥[ABߓRd[J8'0T{Y+;xh^:T&j̢Rb]odt LCcplZ&}UY'4#k0ɋO)bŷ}1\KSKh)K) L-еH-feT E oj@QlArsOY$eVE6jg61ޖbĔʟ}rf-)p\x'/jB1?͌_@Uq0'`1-TNps鸞B:Z' `SnUkO_Y)2tIoĸ]V7Ϋ>S|wwww#ĻMe}~~ul5,Zfa#)LlJLoP_egi7fό0ՠMMJ`BUV L?Vˀ_LA[3TӢ+44XzrD}/sP(?{/^ݛ7oՍz :}ﴼO^; IENDB`./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/po/0000755000015600001650000000000012705421114025044 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/po/CMakeLists.txt0000644000015600001650000000416112705421114027606 0ustar jenkinsjenkinsinclude(FindGettext) # Find the translation tools find_program(INTLTOOL_MERGE intltool-merge) if(NOT INTLTOOL_MERGE) message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package") endif() find_program(INTLTOOL_EXTRACT intltool-extract) if(NOT INTLTOOL_EXTRACT) message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package") endif() if(NOT CMAKE_INSTALL_LOCALEDIR) set(CMAKE_INSTALL_LOCALEDIR "share/locale") endif() find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) set(DOMAIN ${APP_ID}) set(POT_FILE ${DOMAIN}.pot) file(GLOB PO_FILES *.po) file(GLOB_RECURSE I18N_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/*.qml ${CMAKE_SOURCE_DIR}/*.js ${CMAKE_SOURCE_DIR}/*.cpp ${CMAKE_SOURCE_DIR}/*.h ) list(APPEND I18N_SRC_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME}.in.h) list(SORT I18N_SRC_FILES) add_custom_target(${POT_FILE} ALL COMMENT "Generating translation template" # Extract the translatable messages from the desktop file COMMAND ${INTLTOOL_EXTRACT} --update --type=gettext/ini --srcdir=${CMAKE_BINARY_DIR}/app ${DESKTOP_FILE_NAME}.in # Update the translation file COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} -o ${POT_FILE} --from-code=UTF-8 --c++ --qt --add-comments=TRANSLATORS --keyword=tr --keyword=tr:1,2 --keyword=N_ --package-name='${PROJECT}' -D ${CMAKE_CURRENT_SOURCE_DIR} ${I18N_SRC_FILES} # Copy the up2date translation file to the source directory COMMAND ${CMAKE_COMMAND} -E copy ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR} ) foreach(PO_FILE ${PO_FILES}) get_filename_component(LANG ${PO_FILE} NAME_WE) gettext_process_po_files(${LANG} ALL PO_FILES ${PO_FILE}) set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/${LANG}/LC_MESSAGES) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo DESTINATION ${INSTALL_DIR} RENAME ${DOMAIN}.mo) endforeach(PO_FILE) add_custom_target(${DESKTOP_FILE_NAME} ALL COMMENT "Merging translations into ${DESKTOP_FILE_NAME}" COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${CMAKE_BINARY_DIR}/app/${DESKTOP_FILE_NAME}.in ${CMAKE_BINARY_DIR}/app/${DESKTOP_FILE_NAME} > /dev/null ) ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/CMakeLists.txt0000644000015600001650000000554112705421114027173 0ustar jenkinsjenkinsproject(%ProjectName% C CXX) cmake_minimum_required(VERSION 2.8.9) # Do not remove this line, its required for the correct functionality of the Ubuntu-SDK set(UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file") set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(Qt5Core) find_package(Qt5Qml) find_package(Qt5Quick) # find_package(ubuntu-sdk-libs) # Automatically create moc files set(CMAKE_AUTOMOC ON) # Components PATH execute_process( COMMAND dpkg-architecture -qDEB_HOST_MULTIARCH OUTPUT_VARIABLE ARCH_TRIPLET OUTPUT_STRIP_TRAILING_WHITESPACE ) set(QT_IMPORTS_DIR "lib/${ARCH_TRIPLET}") option(INSTALL_TESTS "Install the tests on make install" on) set(APP_NAME %ProjectName%) set(APP_ID "%ProjectName:l%.%ClickDomain:l%") set(%ProjectName:u%_DIR "share/qml/%ProjectName%") set(MAIN_QML "Main.qml") set(ICON "graphics/%ProjectName%.png") # Set install paths set(CMAKE_INSTALL_PREFIX /) set(DATA_DIR /) set(DESKTOP_DIR ${DATA_DIR}) set(DESKTOP_FILE_NAME "%ClickHookName%.desktop") set(EXEC "qmlscene %U ${%ProjectName:u%_DIR}/${MAIN_QML}") # This command figures out the target architecture for use in the manifest file execute_process( COMMAND dpkg-architecture -qDEB_HOST_ARCH OUTPUT_VARIABLE CLICK_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE ) configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION ${CMAKE_INSTALL_PREFIX}) install(DIRECTORY "app/graphics" DESTINATION ${DATA_DIR}) install(FILES "%ClickHookName%.apparmor" DESTINATION ${DATA_DIR}) add_subdirectory(app) add_subdirectory(backend) add_subdirectory(po) add_custom_target("autopilot" chmod +x ${CMAKE_SOURCE_DIR}/app/tests/autopilot/run COMMAND ${CMAKE_SOURCE_DIR}/app/tests/autopilot/run DEPENDS %ClickHookName:s%backend %ClickHookName:s%backend-qmldir WORKING_DIRECTORY ./app) add_custom_target("check" COMMAND /usr/bin/qmltestrunner -input ${CMAKE_SOURCE_DIR}/backend/tests/unit -import ${CMAKE_BINARY_DIR}/backend COMMAND /usr/bin/qmltestrunner -input ${CMAKE_SOURCE_DIR}/app/tests/unit -import ${CMAKE_BINARY_DIR}/backend DEPENDS %ClickHookName:s%backend %ClickHookName:s%backend-qmldir WORKING_DIRECTORY ./app) add_custom_target("run" /usr/bin/qmlscene -I ${CMAKE_BINARY_DIR}/backend ${CMAKE_SOURCE_DIR}/app/Main.qml DEPENDS %ClickHookName:s%backend %ClickHookName:s%backend-qmldir WORKING_DIRECTORY ./app) # No op custom target for all not compiled files, so they show up in the QtCreator project tree add_custom_target("%ProjectName:l%_ClickFiles" ALL SOURCES "%ClickHookName%.apparmor" "manifest.json.in") ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/0000755000015600001650000000000012705421114026015 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/tests/0000755000015600001650000000000012705421114027157 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/tests/unit/0000755000015600001650000000000012705421114030136 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/tests/unit/tst_mytype.qml0000644000015600001650000000225412705421114033075 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtTest 1.0 import Ubuntu.Components 1.3 import %ClickHookName:s% 1.0 // See more details @ http://qt-project.org/doc/qt-5.0/qtquick/qml-testcase.html // Execute tests with: // qmltestrunner Item { // The objects MyType { id: objectUnderTest } TestCase { name: "MyType" function init() { console.debug(">> init"); compare("",objectUnderTest.helloWorld,"text was not empty on init"); console.debug("<< init"); } function cleanup() { console.debug(">> cleanup"); console.debug("<< cleanup"); } function initTestCase() { console.debug(">> initTestCase"); console.debug("<< initTestCase"); } function cleanupTestCase() { console.debug(">> cleanupTestCase"); console.debug("<< cleanupTestCase"); } function test_canReadAndWriteText() { var expected = "Hello World 2"; objectUnderTest.helloWorld = expected; compare(expected,objectUnderTest.helloWorld,"expected did not equal result"); } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/modules/0000755000015600001650000000000012705421114027465 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/modules/displayName/0000755000015600001650000000000012705421114031733 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/modules/displayName/mytype.cpp0000644000015600001650000000020012705421114033756 0ustar jenkinsjenkins#include "mytype.h" MyType::MyType(QObject *parent) : QObject(parent), m_message("") { } MyType::~MyType() { } ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/modules/displayName/backend.cpp0000644000015600001650000000060312705421114034025 0ustar jenkinsjenkins#include #include #include "backend.h" #include "mytype.h" void BackendPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("%ClickHookName:s%")); qmlRegisterType(uri, 1, 0, "MyType"); } void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { QQmlExtensionPlugin::initializeEngine(engine, uri); } ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/modules/displayName/qmldir0000644000015600001650000000007112705421114033144 0ustar jenkinsjenkinsmodule %ClickHookName:s% plugin %ClickHookName:s%backend ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/modules/displayName/mytype.h0000644000015600001650000000076112705421114033437 0ustar jenkinsjenkins#ifndef MYTYPE_H #define MYTYPE_H #include class MyType : public QObject { Q_OBJECT Q_PROPERTY( QString helloWorld READ helloWorld WRITE setHelloWorld NOTIFY helloWorldChanged ) public: explicit MyType(QObject *parent = 0); ~MyType(); Q_SIGNALS: void helloWorldChanged(); protected: QString helloWorld() { return m_message; } void setHelloWorld(QString msg) { m_message = msg; Q_EMIT helloWorldChanged(); } QString m_message; }; #endif // MYTYPE_H ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/modules/displayName/backend.h0000644000015600001650000000116012705421114033471 0ustar jenkinsjenkins#ifndef BACKEND_PLUGIN_H #define BACKEND_PLUGIN_H #include #include /* ----8<----- import %ClickHookName:s% 1.0 Rectangle { width: 200 height: 200 MyType { id: helloType } Text { anchors.centerIn: parent text: helloType.helloworld } } -----8<------ */ class BackendPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: void registerTypes(const char *uri); void initializeEngine(QQmlEngine *engine, const char *uri); }; #endif // BACKEND_PLUGIN_H ./share/qtcreator/templates/wizards/ubuntu/backend-app-cmake/backend/CMakeLists.txt0000644000015600001650000000204212705421114030553 0ustar jenkinsjenkinsinclude_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) set( %ClickHookName:s%backend_SRCS modules/%ClickHookName:s%/backend.cpp modules/%ClickHookName:s%/mytype.cpp ) # Make the unit test files visible on qtcreator add_custom_target(%ClickHookName:s%backend_UNITTEST_QML_FILES ALL SOURCES "tests/unit/tst_mytype.qml") add_library(%ClickHookName:s%backend MODULE ${%ClickHookName:s%backend_SRCS} ) set_target_properties(%ClickHookName:s%backend PROPERTIES LIBRARY_OUTPUT_DIRECTORY %ClickHookName:s%) qt5_use_modules(%ClickHookName:s%backend Gui Qml Quick) # Copy qmldir file to build dir for running in QtCreator add_custom_target(%ClickHookName:s%backend-qmldir ALL COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/modules/%ClickHookName:s%/qmldir ${CMAKE_CURRENT_BINARY_DIR}/%ClickHookName:s% DEPENDS ${QMLFILES} ) # Install plugin file install(TARGETS %ClickHookName:s%backend DESTINATION ${QT_IMPORTS_DIR}/%ClickHookName:s%/) install(FILES modules/%ClickHookName:s%/qmldir DESTINATION ${QT_IMPORTS_DIR}/%ClickHookName:s%/) ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/0000755000015600001650000000000012705421114024536 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/projectName.pro0000644000015600001650000000254212705421114027532 0ustar jenkinsjenkins# This is the basic qmake template for the Ubuntu-SDK # it handles creation and installation of the manifest # file and takes care of subprojects TEMPLATE = subdirs #load Ubuntu specific features load(ubuntu-click) SUBDIRS += %ClickHookName% # specify the manifest file, this file is required for click # packaging and for the IDE to create runconfigurations UBUNTU_MANIFEST_FILE=manifest.json.in # specify translation domain, this must be equal with the # app name in the manifest file UBUNTU_TRANSLATION_DOMAIN="%ProjectName:l%.%ClickDomain:l%" # specify the source files that should be included into # the translation file, from those files a translation # template is created in po/template.pot, to create a # translation copy the template to e.g. de.po and edit the sources UBUNTU_TRANSLATION_SOURCES+= \ $$files(*.qml,true) \ $$files(*.js,true) \ $$files(*.desktop,true) # specifies all translations files and makes sure they are # compiled and installed into the right place in the click package UBUNTU_PO_FILES+=$$files(po/*.po) aptest.target = autopilot aptest.commands = bash $$PWD/%ClickHookName%/tests/autopilot/run aptest.depends = sub-%ClickHookName% unittest.target = check unittest.commands = /usr/bin/qmltestrunner -input $$PWD/%ClickHookName%/tests/unit unittest.depends = sub-%ClickHookName% QMAKE_EXTRA_TARGETS += aptest unittest ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/wizard.xml0000644000015600001650000001037012705421114026561 0ustar jenkinsjenkins ../share/ubuntu.png Creates a C++ Ubuntu application project with a sample UI containing a Label and a Button. Also includes: - unit and functional tests for QML This templates requires a Kit using at least a 15.04 click chroot. QtQuick App with QML UI (qmake) Ubuntu Click package parameters Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/manifest.json.in0000644000015600001650000000072512705421114027650 0ustar jenkinsjenkins{ "name": "%ProjectName:l%.%ClickDomain:l%", "description": "description of %ProjectName%", "architecture": "@CLICK_ARCH@", "title": "%ProjectName%", "hooks": { "%ClickHookName%": { "apparmor": "%ClickHookName%/%ClickHookName%.apparmor", "desktop": "%ClickHookName%/%ClickHookName%.desktop" } }, "version": "0.1", "maintainer": "%ClickMaintainer%", "framework" : "%ClickFrameworkVersion%" } ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/0000755000015600001650000000000012705421114026117 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/0000755000015600001650000000000012705421114027261 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/0000755000015600001650000000000012705421114031301 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/run0000755000015600001650000000027412705421114032036 0ustar jenkinsjenkins#!/bin/bash if [[ -z `which autopilot3` ]]; then echo "python3-autopilot is not installed. Skip" exit fi SCRIPTPATH=`dirname $0` pushd ${SCRIPTPATH} autopilot3 run %ProjectName% popd./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/0000755000015600001650000000000012705421114033547 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/__init__.py./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/__i0000644000015600001650000000205312705421114034220 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Application autopilot helpers.""" import logging import ubuntuuitoolkit logger = logging.getLogger(__name__) class AppException(ubuntuuitoolkit.ToolkitException): """Exception raised when there are problems with the Application.""" class TouchApp(object): """Autopilot helper object for the application.""" def __init__(self, app_proxy, test_type): self.app = app_proxy self.test_type = test_type self.main_view = self.app.select_single(MainView) @property def pointing_device(self): return self.app.pointing_device class MainView(ubuntuuitoolkit.MainView): """A helper that makes it easy to interact with the mainview""" def __init__(self, *args): super(MainView, self).__init__(*args) self.visible.wait_for(True, 30) def get_button(self): return self.select_single('Button', objectName="button") def get_label(self): return self.select_single('Label', objectName="label")././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/tests/./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/tes0000755000015600001650000000000012705421114034263 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000016300000000000011603 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/tests/__init__.py./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/tes0000644000015600001650000000324612705421114034272 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Ubuntu Touch App Autopilot tests.""" import os import logging import %ProjectName% from autopilot.testcase import AutopilotTestCase from autopilot import logging as autopilot_logging import ubuntuuitoolkit from ubuntuuitoolkit import base logger = logging.getLogger(__name__) class BaseTestCase(AutopilotTestCase): """A common test case class """ local_location = os.path.dirname(os.path.dirname(os.getcwd())) local_location_qml = os.path.join(local_location, 'Main.qml') click_package = '{0}.{1}'.format('%ProjectName%', '%ClickDomain%') def setUp(self): super(BaseTestCase, self).setUp() self.launcher, self.test_type = self.get_launcher_and_type() self.app = %ProjectName%.TouchApp(self.launcher(), self.test_type) def get_launcher_and_type(self): if os.path.exists(self.local_location_qml): launcher = self.launch_test_local test_type = 'local' else: launcher = self.launch_test_click test_type = 'click' return launcher, test_type @autopilot_logging.log_action(logger.info) def launch_test_local(self): return self.launch_test_application( base.get_qmlscene_launch_command(), self.local_location_qml, app_type='qt', emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) @autopilot_logging.log_action(logger.info) def launch_test_click(self): return self.launch_click_package( self.click_package, emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)././@LongLink0000644000000000000000000000016400000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/tests/test_main.py./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/autopilot/displayName/tes0000644000015600001650000000124412705421114034266 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- from autopilot.matchers import Eventually from testtools.matchers import Equals from %ProjectName% import tests class MainViewTestCase(tests.BaseTestCase): """Tests for the mainview""" def setUp(self): super(MainViewTestCase, self).setUp() def test_click_button(self): # Find and click the button button = self.app.main_view.get_button() self.app.pointing_device.click_object(button) # Make an assertion about what should happen label = self.app.main_view.get_label() self.assertThat(label.text, Eventually(Equals('..world!')))./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/unit/0000755000015600001650000000000012705421114030240 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/tests/unit/tst_main.qml0000644000015600001650000000220712705421114032572 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtTest 1.0 import Ubuntu.Test 1.0 import "../../" // See more details at https://developer.ubuntu.com/api/qml/sdk-14.10/Ubuntu.Test.UbuntuTestCase // Execute these tests with: // qmltestrunner Item { width: units.gu(100) height: units.gu(75) // The objects Main { id: main } UbuntuTestCase { name: "MainTestCase" when: windowShown function init() { var label = findChild(main, "label"); // See the compare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#compare-method compare("Hello..", label.text); } function test_clickButtonMustChangeLabel() { var button = findChild(main, "button"); var buttonCenter = centerOf(button) mouseClick(button, buttonCenter.x, buttonCenter.y); var label = findChild(main, "label"); // See the tryCompare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#tryCompare-method tryCompare(label, "text", "..world!", 1); } } } ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/main.cpp0000644000015600001650000000052212705421114027546 0ustar jenkinsjenkins#include #include #include int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView view; view.setSource(QUrl(QStringLiteral("qrc:///Main.qml"))); view.setResizeMode(QQuickView::SizeRootObjectToView); view.show(); return app.exec(); } ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/appName.png0000644000015600001650000011407012705421114030211 0ustar jenkinsjenkinsPNG  IHDR\rfbKGD pHYs B(xtIME 22 IDATx[r\gQ(poGaD% @@UPŧZ'7-D($@a7̕k|{@#7bgf|H;j}I6WnFșufpgUޅmԼq;V<9N2s%c|oVMNZԫjHO^x'k#@d=SEkuMeϝON.ffykd뢴kas4O]COOO8y=ڂ5 87;N3ǬVa@r׉;S}f|.2gSה.p{ Ùxݷ;n8 Y*d}-3 `iɑ k|1<==MdyuzͅXiys'HIrSdYZYו7+33a‚3h b[??+̃RϽVevo;߱ӢKxsCZM}^=Bs扚'~FLxZîZr.n{tt4sԿe0V;z/'⺟|O= tl%3&fL*da epfFk$nc8אi<y][Vi3=3A}zAҧHICT_f)꿹 '/n"Zk/_t>67q R,s^SWY=b$trn=C+bJn Olfu+@@9YfT\|~ @)]m~ZvQEZ$D6a899|߹3Z/ɇ0nlNk0A5~>|Y/Ȏw.]3L0R!B̀e_=~sml6lr]0.q !ZJ{yɻawחˊ*LϲDW-,櫛'oi$)6#=l)憪NxhVb^_CX$G!W-{ `| A!{ٽk`*OGdpz '/!Lsst!g9lIZZ^oq[2զ@6v noowbl(>dޑx` {<>>].?Kd},NZN;Fzd.2eg͑vxr-i2gGnDv9X 3;07sn fBN)1?3e\k<=dy``krp$t Zw 6dEq||<{"TycD)f1#)6yF*,)eXO&K Fkw2e&%6= 7{ k[93׫& dpdG;@&.lH, 2:d7wGN0ٕOO߾}D1X_'Ed\U5$Kř۩(5HivR`HFwn$̉x2h3Ӳ>x:H>%OQq3'e uvlz#Xpt&+ 3j/ Ys呆46Q_Ѝ{ʛcaAq21"g=<{jT~l@iQcbZ[-Фbw؈=cDY#Ʈ%8\A<)v˃z?@%l]c; Tܥr5,emOOOxT#ia(!Ð$% 2uZ`Yf;/Ҝ`LR^ɸ~>EYӉaV0N',SRCyhTeo`|?E>ʞ1SdI"9DpBfD{ ˾>߉ѳE j05:b'wd)@bFN:{C?SbM4;Sk@Fe>3ʴZY=ѳ.I; ZKyM'oCl}l0"fE]i)wRlr X{7͘ L%S~OKt;9âyw炫/;HɛI46m." ix3%U'-S:%s*`--ߟѢ;Yt~߷oߔk;"gn"7! UP^=SpIҪ'ƃ$Y}N{(b)5d {G6N0z$.On. 2qdtN_C2 pl/V Hz)rA0iƠtb%g9Kw zցY,۷o-iܬ^-kM-e  pPYD̙; R_)l 31/o"I6DQl0.a ğaZW3g=1YS4]lZaמM]yMsIAI.6of,:~c )Zַ5aB i3do f'4߁Wdڻq`1i=DǔӨTgX:Y#<10FHPd5HU3"LyĜ hIصORȔѯh |\ J |Y0>2ґIyxdWF= &bZi v\zSuސr`$3{\- rS%Gx32@+Olo[SٱBϯ^9.qHKϠu5&<3c7>3%5fkemӍ&^ ޙ*(AV7vSΞ;ue35$)SL ʍAwxY} rcGb7[fZCF?-\͎SgNu HeY. &}9eWMqiQg&66uG[4i_\D VAXWfXj8 lHtʲײ Dm(#vqҴv5MDWYo=?I&nQP$&[֝d12[,K5*:;ǜXǷa']H&1SPVcMR:] gik%-ÃzxÞ45Us4x 1 s6!ā)2{ gYOg9Ip}|3ec:Y4qGgc Q5Й WҰ4#:ÉL)iXii[**ɕ E:c3-XʣAs3tĐz֯1$Ȏ =M4a%4o.%UwT -\*d^Vɝ΅i5hh,EΤi` 0.߯\uxSQ3yL(T}벎cLvf+C.Yw_t񝴕fډ)P{7dj|0q32Nf>;;ۓgΤC@j(5Q$,k$ӗO(-Yv]| q bN3 Dɨw\uB5C,خ3tryoYˈ[6ݑL)[XXpS"ND,<\5Y noS2KTtxv$ M$b3)&ɏ9tԥ'Ӎd_\\*PiϻѣqRrMdYǓ `Vޣ 7`0v#TIKL%ρ?;))5YQs^RYw!@G-+LMh an継Huj b͸`K&k\Miy3j z"9g k]ia(^gxZMڳ 0x6ּߌܵUsݷ6%FwAN?fW8LmQsQIM|wi[sy [ug;4=']vr]<?TKA5Ñtno-13=M`KRW=G\z5L6:1JZwGTnIq5xߛQm5ɆLچ%KO-IT=מD,ͩc3=hݼ) dO|;54GNfbO?AQpvO!H\Q5Jj2b4y*7 +V}gw?g|xAe΀a IfSw{v&wN<gV g] sp,fbcgcjv4oD*u6s">-X!|hsJ%#*Xiabr+c$u-WHAJ~ E`}ӿ,:ZkƎ#n35& N=#hь-Sb-OΛ>\ tKBQLJO2W@5aތsr&1%oE;Eb ff~ڦ dS~:>#rgdΡy[KgZaxxxU4"YfwTjn8#SL3g?S%2S!ulja&`Ğw^kX9r䕺Sesib&VJY/s:zsm*@s6HVđg2s(5%f$e~s5x X/jQd6N CF2Qn_><<'?-,0qxHgLdzsjN^KKXs2U,S֒'''{vڿg߆aXC~a%삌1-g*OG,zlLXf5̇y\-I3cJu,J/AnO`bƒ2RWM!\̬{n3>Ale1笜v;Js)C~՝/@KcZxR6Z^'ו hb,=;텵{||f3 7G@Aɫ?b "߾}e2~E?KCMm"r;NSueVs\iA IDAT.0s6e6"N\Y$5G jfXcFz*+͎+eptt4&YQeO]wM_sINYjw`um~N [v~?>45ˣ (?\W{خbff5N.=:' ۡ6sӟ G` L/-G oHkRd91&w4JF$ƐD:ܬv͸Al޷۷ɋ_{$,œ3Z1 m'7tGUf4HQ].wHB[z+vR~^gbےIfVԞAxd@~0s|}:ڙI+T#cXՍ9_4?VZl-sZuuu5md)Bϟ >eƕG-u4Ș54@LJ?|0f|_ |3`*54dZb)y&AXQ7Zq3}Ew&v;~a vh2#b&ƚu94_'F&!y"G8Ce`Hd9> :,(L>{\۷yΕ`dw 檳z,ӻ䆵֠vso}Q]~dVJ6蔬 6AD;ϜQ❢RlnLnKRe:'Y zxx| $KCz:^s,c2!*وgĜ[20 -a^AAm9qR' 6[*];'0C#`AyL/ijSZl}|Ky܍FۦΔRRkrQ̚F1kvҵ'K1;gn\puرMsH.|vUa4ac.tR s3Z3L:yMfyc9f]C2^v`CSux2Fud`eɔ٬˗7umnxf4%tlɚ ?"*Y)Xs{{;^|ԝ\pA''ی U,ӳkea[E$&x0;1(1Zi fi \9Ӻ+Lˌo6 rd_Y3eQc3DZj_ud&e<Wž[ym -Q|ظ/fnAڰ1\u2爽l?#E;ov#tGdkʅbd`JߓcQhoP|uRw)l6%N]d3龷mjCUğTjj,ؖdc͟%Zkw,I05F,hoD6"#64&Fԙ\b5$3n2#f.L`E76?cAgSƔXFFXw݌ Bl7مV2|5pXmFN L 3LzPv22x1@yemeL("5s|ب(f | 7 \Ox34._DiNUtr<S6j}6M 0OrvPL[Pl8==,.cp)v0Ça)I/{Vb&S)VF73M'Հֿ+eIXɸc9atw"rڲzxxnnnZ]7o~fP5Q P90;=*Pouyy91E;U+r/{::mhu\g`_Y\ w,O#[e}62~3}"|Ҷ^(Z2v:ez;\lO< K)H:Okkۑngr\;i$7b޿+_P֪4u[&۶/N?$`V'9(8לeɝш#K͙62a T^>>H3gvO~=kvI:y9gv߰R]__޽#`,NW2N03kSL\ ӭ[k957;:Wl>OFCdDdfvVv t&wƲ% G>oLŊdZ{g SLr*On]Vjc=`]'Q.~B#("Ш;"\<13(܋NI`0]86^QK:rԪr/MXbIS>HzH,"OKh  s)_Lˤ|/l.DRu'=AVt4pєG_kmKtyxxP46"kD 888kCfd?+>kf+ f$Xe6qIuߜO[ЩJBE~aXDNcӥ@(fp؉խEf sV[wtƒJ[VÛ7ox :,)O>Ǐў+|ݤʸ9aHq d9a"2\@;q=,[]3Z)$dke\{ 攲n'CEkT:_AwV{"R#[Rf Գ?::NNN&>Z۬U eo]% ܵ,;#zd+ڌ6P<cNމɖ 9Fv"efnnooK}iTyD;_fD9/b^w2V@I 5:sW;??...ԕƦ_t|/6n_jHyLn`^>*| f$ 7tIJ6 lE2JI jSg7s 4LXA|x||t0 wwwsB"VdϹtn˗/Yf /'fN۳ُseK d o!ܦ2#N6vq!fzd/jgvG8%b0 5锻L`~9?(k٭>,L[k7Mr9yf&pvv6DYl(Ꝧԑ뫺Mx?<<< F F~;L3߽h=eիWŅt*OɅsc*lCcgvc{0 v>|yn$?sOYW1 張n I)MƐ{7F1cǽQV j^~=yF_|_~~2( O{zznoo8_^^F{NlcuH2 ƿDߐYcJyBa:U圹bV+mH]///rK[-`K3}fgO(Lќ; =lJMޫ ͟R.Z 0xԦD+߼y3F,bJ/7I}X;4>wj18UfXG$js'Ӷ74K%٥f&0}:2wqϗ|g Q\'[*L:A }~z_6[!իW4''''#'~$ unB/;iAt_&A~ɹ,7،=l&ley ]/0ů.@YUrRҀCjXjy͡fEfiNJ$=܌svL߿WO<l:eoyo޼4yo߾J9wtt4C3 \C̠S RPlZ_޽{7ǣ 7իQV{s=ڵϕ[fuǏ6ʔ|*ln07FV,X |iͺ-44[:Yo>6) [1ly7YC8n]%U8 &pķ ܤ=N"䗿kIsn*i8%RirYli1׽M9țbBz7pѡ/OU@Uh`b:>݀|._mYj L[;|'Ǐ hIfj`{֬ę>??q32C;g샫QU>3gT~t:1H'pq ڈ9TuAVK,Xo2߿~;Ov;r d`R꺘te;-'''٤gb']&BF*L2ze_]cR*Ort]#~gց乁Y~Ӓ/Z?;{򶉜{룓fi|v)%fAwaMLkɐF|xx8}vsB'aLp* JC0*3# T|o 8YYY<}vB¯ ,]vѱHJz=Sme3*`|'Ӳdv4  4n;^f&S(aV_t%4F梳{߾}ڜd5U)4e#\l?g'p~NMVYך_a.ʺ7o;z"fjK ㇺ M,s1JC<;"EoucN߿tNynjceԦXb &TN.yLI2VZO:,sMxN˜jYFz3=S42heͬܐEK˗c""dA9:1*6Ң>}&Mc߹Tt3:muXO7\׭Cm,X|:<<7{|luRb1 5ߌ Jx;r&L)Q^nTA^ H۷ɜDRdI6h=7oLv_u #Aw\I~˔ߘH ]lNc|EA999P22\ ]Mکe՜;HQ,&VsCSi|qg???o޼Q"E%,%d0͘z>;D0x ~zO)Af;irxxx+\C~ f ͿgB1irJ% dVKV_k: +F'Ґͦ9*_lֲu}\NN~)$+Q]բ] S{/ IDAT鲊Ji0?YÕDXVU Y*LAb0)m=0K'} ,:muxx8<<< mʂMV쬦b6Ym.u/˃Zih"G'2`/ceˮDM[ʅk K,ܵ{IA_~Ke\=rg`H7TJwxf%YDf׀@;͢bUזEt%a96~nl5x||O|ٶ ?///CnE1 [JUM[יڳi=;Ɓ?::'یMg0^o߾8Yz/r&K 6b/l)FxZ͙N䟧kLxsDImLu|IDكgZ˙Vo XЪKQn`n PW>:e)3AA&.0~Lo T3R-%k)l$3WvRsc|I<];<1rꭥ\dߛl0e%bmXWU4<#sɬ IR6QނڑNr[|ڛ=Ɔ?qxDE'aleQ>N>?s;¨hVy@9p'?Fȅa&˥2i[f=cFeKCHY::WXG3Tl;k6mF;jGcCvª3&vp,HBcuS`ggҦ~O #PXMbjI4M%N.3?r':ns>A+d.IlX0V['lYIzӉ%J;iTm8&y*8CbK \h녓Er1c{{2`aiYFQGzʡ#̔ y6,+͵Z-ugy(TYfJTHME?20F@gfˍrRnĪjadmlpYK|5OXj$2|+͡s3먿[c'պf1ZL٘PsE9w2'qEiƩLR5 qV*>Kc([$F!CQV8|ymH3F^؏?ƀS_nLhm@*sBܠKUY.SKwʑ/, BaYbTYvb5Nf6?F*PTS߲ȇcщ5UR@SƑcLapʠE=. e@Ǐij1޿L,2,6m mL-RQH,Rps5 ˒dCL;MBJm[&@i i84mre6\9.YM]O6(ݢS+@)X> 1 %eܬکN7cVQypp0/{>SÇ{Az3[q3Pe)Ɩu6+O}r]HPnn(j7fGj`<]`ڜӗ-C,0hw!)O@{2 {;N8#f aeV~gHI<ǙqE0ʀܷojiRMM^JMf"фupg\YUkZgќ;+UMMi7SnLRX}p9+~{˒g9FeCnd=V%矉0-J 2d"Q`a Q&' `Zl*&QegY'i[`6jo'h&df}~; $V&w@"{>xs+dk&3lO5 * 0>݆q9cfΥB3ȧ;r Hh ,d\0H w"{%;fGEzQ-Z%"_DYl&$3 W" ;\<|]:β6YfL0ߛa",CX LZsv)xbHIIJSshdHŊ̒it|l1u|᝶(2xOfC0K BfGOrA)[Tnz5$TDU1x/ͬ8ds}_93=ˮ~"cL!*):Ev ВS"ּT v9Kn~NhL5q=ivґp2U6IjVle\H1uo93°nDxsQr'Y3*[F`5?kN% dW+ґ:9* }5YsKiƩ/_Q!Ɣp3FleKK@X)Ŷ`; ;v.)`L: hc #lɿ r-VP׽^'<0j33#-0;'d`fRJQnΔ]V|l9ʝe2 yŝ%iItlf<m0ԚOb^rCaf9=\t.H4lhc[^zGh1"X7#JezO4IwHP& ,,S\I|V:&{HNncZ0ي҉ֵA^S(ѻFRCDLENj<hhx|/BΔN.cd"Y#G$$ޘ X:ϿYu'?yd~ ro[cmsPobC[ιaXFiY{fH3 #,tZpv;/_())udo}e~~^z`NrT8yh$Y7Xpg0SWPeeQ GaLNНs馵Y7ͤ ՍNJ;ڂB<&]5F1ȧ1p56a $S(:%=X{QYߓɚ_}>oooϟ?OOOOׯGzfi7.e!ÍI^L]DtTfumrX$3 qq z>f3X>...=P7qe899tD~KB3يDYW\yZuƜ3N0f gvdXZ'/].+6cONca&'Β-D-s$$Sl0ue۰ˇԹ$fe"# ZPƺxLci]C& Xzz:G)Tٜ3xCuwzۦxfFAni3@%󱴾?q!fۀΘgE^})յً֮4ڴ:\Np6{f $dDҍ4dnڌ!s:m+ɓP_f\@cܐ_]kx899cɮ\HHp\[=6x%%?R;3|弅|T Yv&&r&rghZfb.ngq1]Tդ b43t^Mʸ-)mԲEfvQ6N,^z5ӦI!1%K#9z5ߏl6bɃ[iTw3!/+n8 K1gK6ͳ\tުSSš&~oN |x]d` 瞱rzzw;D krܼ|؛fL~6ׯ%[YWZ$@ `Yj8K-]CgXVIOuIm6gZkT TFz겏L#-1'ò43Wa&ȅeCW"Z=kG؎Y.vUgG!o 2,N2H/; ĩgƢf nzxr443ImNcG,\דuE 8KTm50؅@E;p]n+Tge }%ͷ,t/5dfg?^4bcaٔڸٛ7ϴfdMV *TeIOޣM[a#do`O[|xED<;!s(իIgNlڹyPC6ٙ2+􃃃QY@' y $QV672H*{DԫU6{=OEk1fr\N(2>zxxe7MZ=Lehٕ0Kw8%I*g#hY!˞H33Wf@F2j|2I6;63+ }%9܄ϸOԀ_&1%6ԃiKzec|!ӥ8[cIIEc {Jf % g0r"uFqjM*J .$X1:/y{̺?3٬My OOOz+X2Yۨ᤬#qxggg~-ѤsZbM`=GaY݃ONNu#tae![VWX9y8@IBB:慿yffB!|e\F5!eo0#z}}wJp@- ᒡFx >f}ɭg*Iw&ȓe^2T7 =7ij ,= sV24biXrӧ~`' Ht;INQ}#CgV $⨑yzKEחikݎ`uXr)el6cL\ A+˔,Hyfx/ F նʹfφ˼ i9䦫MQeGNY6;`*TTa Y]YP|q876\:&8S/([.˛Ouy.aْD*I+~gm@)KH7f’T:)2e8;;MffYZnX-(˅:ﯿKԄY W8|Vd弆:Z>ZhN 'Kߔ^ZLqɠ &Ud,e-ljMBf[mj:)8Nu}Mn|///X~=??^~=|a8???gin.ࣣ׺@D\*7YME:=]}v M'+8Z>%}Q>ݜi-OZ9 8쏲8;fHnUȠ3TƉfۄU/mnz#K9Rlr ɶ,e~:l6 }e{\qd]=F,yd@\nG<)UmY}SKob)ðySgN_^^N8L5e5NgT籎/NyyȋD۷{וdj5:a |'-Y///HEdG.pUv;ϯÍuU]gPA&LYgcaf/\dhiNBWΚ(Ai@Z$ \j5Aɹx2“a,.3C13@pww7<1)F!VlФSdڌov`a IDATۥ+W z||@Rɀk:Tۦ7:p,JLcݛF:Div`|ߴ3ay&#)Ȕ*'M`FЖ1ˀXE4 ^oZ ڵlϟޓrAeWRgvh7ԟ?>< h]e}eIj.Mcx/UfRrKe+Unl]|4\Dbu TevG2F-;3x؃@4ZE:oujeVONc$v^<<<Vhfࡅw D@@+h߇ϟ?OZKdL ~{eEYLDN 3/{8T3aIaї ]Rl#Vظ$;e3XPύW/bH#ЖʲnIr ߾}KU.o޼lI^uHH٭юSG.f2X,(ap>*>|Ƨ9>>>~8e ! MʍXrf|mGl3wu4Wa'$KEq0jf5k#+cVDkt.SIlyFx6,R Eӡ+3#nDt}RΔͦ3ȓ(s1OFJIrssf}v*`3r{nTL'T`܈U}d;͚tfI" <)5J#3EGԲι2EK:/BF4o}NpdI!LAMGHpRNIQ9f,iҹ&PV3˪5jih91e~f6UH|1dR땀)q.6,+6ugY3u1 J2]bj `ͷe#y_6, &nv >,N(Qfl6ϟG ؉Xm] ydt{KWA&($pu71==NNNRi9ʞ쎕G,|5eYu~Z\X/Ot: g4\o2xq. n^v#9_~~:qbgƲ0ώD(Wdñ0|^R $gw 8vׯu&π99%ׅ 2 O^2i8|3աP^K3˴?kXk10Ӌ9+L |Y ck*5b *>92v><|eGѐNrg+~5Qz4pi653Uv ^^^FY'kRi0qvejLzOFhK:  Wj`fK_KLa\߿bKAn^>r9VS Wwyy9q%&К(&gf>L9F)X۫F͚7 qDf5^?>>fŘ5s>OrE(l5'P#s11S22+e@(ɼ4ga1B4Y阍Yt%B׋M{ g'IJ_ɵyjכymb& 3 ׯ_WWW*)Znqlujn}OIa57Nml"2tn.aWȔ9MDbde烃͛7%9yH7J2x@){*, $e!  [.\$X"-$Cٮ)vY_xԋrsP@’\ؼyjLIT471|)K-^'ߺBsYs0j}vi5sO]3k5q%yzzsafʝMtI}\{ֶJMAw(;Q"VEZ6VꬁYi̺ȜA$w:jx*96E}³Y]B5˴3X$摘jS|lb.׌Zfkf¤|l2vؾ.EY*s*p(Kbl}-cF^xY[4c+]~t&S_;xXsWtrL_TeC<*9 ia?(0l4܏ScfPNgb $5qJU{,/<<07l\JG-A&qrsRp2=/"%CV >5Iu%txrNe4\p<92K;K;!c?cptg^ Ů]KIZӓ&BeF~t)=:rR= .}fMAϴ@̌LH&)"AƙFu8R+;V[%6_2 `LK~n׺Ln%y&[w*5|ʞ?>xQZuuuKf/_&~GGcfʌÇ{8a7u;h&yP$a&;8G2dFA:.0}IdFʗ=OF=G3bCIxzz'"(M\v:/Y a^Ç&|܈g.tbIY ePk,mMG2Ek6Aٜw?˗/;0L{MAHrCr2d/ $cb {d@i {=w[̍/+d{`ݎr\+:F ǐYǝu6$]J6Ǐ0:Ce9m R{5ޑN\#ƙ$W`iAŗP> AL;j͚2ìВ/%?4]!'ۗ8l!LgjCOfU;bKA mz?9 ZvrIAiL>5 #O:۽I.d/2ݵys =S.ԆXB)7OiN#6.:Lh ^Vnk {hRy8?? f1%Y#իWÇ4Oʶ );bv#0. ;I* #FletQ[T>'En>>>N5 mLF]0X7r|gq?;ʭe_:W$Cy{w٥eg6aZQ PXv7GUq+23z< n۴ K988" &%#AM:C dEplUe/6E8 *ģoۤ1$4$U"^m\;q0a65eLWd^W'x%F3ԕ*ĵ\A] zۭN+r3ʌ~qAuSlsg`}ݐ hq'~WIBFcNtmHC^ywwwÏ?S)&kxsx!THs2S+49djy?t`>3аq_u:ǖLN˿ t:KeHDf ξ|7l! \Fh>LsV<>>5+K8<0Qt͡YuF81߂3ZVnnmvkY 1fԵH<7\DnFќ_^^>-2 R7JmQRrSu}3&g͞2ImYm0ş Uh439\j7{nس=M Vd^$Pl;>UМNsnMׯ_OƇ d", &mD&s)"c4)b՘ E7kϟw2逢Ǩɇidƀu)TPYtusN62! z?13eJKIZ ^:M9:yrdFrf=gfckLS2jK'mA)h4b}}gKfhŪkOPǏfٓM[J&hB͍E cnf@%]l8??\a%PI6.p:05׵@3ck$~.Ft\j R PO|XD9#B'oB$t]o YkPT:v.O4vͅ#xrqцְ.sIEV材2T s Lֺź~goCF3wcrHm;|(3y]Ǭ2Q+Cm) 0&,~G/lXmV)ȗXݰlt22O#tXT&6n;BfI"~+$KZfIL˯(` 3IS:<朘XѓѸUuO2RPmVN|l&Y=(Rz-B7`d ?f ɓl!"Rc9(C aFw3✆O;;;1gXu(NKñQ6gB$kѿ2=;[w1u69ײM2\ş` =6" k/I!Tgg+p~v\JOķfExxx8hȺ8  W f\ &Z~Wħ: emm~&Ć~v:J30 ZZ*aofbLӸ12K Sٹ 2v ,~?UmOuasٚ!)c2 3"N|Nf>۴guMfTqWPM7Yzf֑֜aaY 7;l;`ka3eHSYBj4 =0I+m2]O95!x:;(3@rZ ??fĞ0_pּ4j4sUyh^u$*ZVn#1H:cƓmܤ gpYF 6 SYr-eڡjl_3Fu%2N6d_N2s(N`s\vu79q3w5uP.4*ggS8cI $'2-Ό؉VJ}dM޽i,̰ڦ:Xk7F,b59Otn|A|@Fdji[𬹈TzĹvzfxtt4MR9vJLI3SbBKa_ASmZs | rȇQ=''=Xa&uL"MkN[6qmXr&]$xh>@_6,ۭ@li1 `\E^yeM57'#v7(Pf[E5A-fbUN06$~OY~ܟ3|٥.^?5$E{vf-ɖ_]hUkӀIwnXX<=LtQE6Ն" zzzg I~ɔ&d$zaٳK{1/9ڜfRJ୛ `-ۆls %@f IDATmN%6l)e+K1ד13RN$sa:>ݔر~c\N-8,OL'9^t'miϳzM eչ"`;.0jj\_x*5p3kN;mf)MJlUOO )gw>ON˺7a^ا l8Q$?jo;lN 20&nsS 6hN`B|<2;5; (g4LY2$Sp >U$fAYyΥM,wd)'OfOS&֞w :ПWf>Rn]zߟ"d2PU- Dl02rTv¢?ZNOO=)9mz.c9&trSZﷲf3AM=F,~qJeG#<,ĉ=lA2SH}dE'j")ݜiAğ_3k\5&:FN}߿C2J930Kr*̻dFr"2Ѱm<={jL@B^4B0}8(_A|D9U/~5QǕ2tign>,]n+2}fH~Y,ިqj keY}l{"4_ffd'ZKB3.#='R)tXOiX !,M(F$蜙 L^Ոm6arD 'iFEtac}%w#%Jdm?3` 2U:l'7Fz tS30Tښz?3ELh '$GY!XjJ;M[ AJt: /IV&ׂ@㞩kr㪲0Ze0vpP&A0\x!268Uk_"$$Uus?ߏƦRRDvs$jj5xDdY'Qv٬%Ƹ]̬g0-n Tq2Si 04$L LP&ΡD:-(/7jD~'i]IE҉(K2d,'v8~oue}[t:)h΋P'`KnݓO+@lXE#9g Dd&@ 0f^8DTe$d ;Sb dY N W: 5dn*l +P͔2Ԫ![b&Qw67%ujn)-]qy]Jjx 7|8ZE% U,I^kn+ve9чkl$*${3V bITu#gցFFqd4oYJ؀hѓ1I9 8UVA-A&gfU>&kbӌ]2NeUq JK)K85u*g&ٽ5|Տyׯ_o|qg}駩?IY׿YYYf//{eYp%mYŋҴpаK}>q*i,`R)2qM˜k_o5j󗆟59+^ ~AV\YTI~`Q:WW ǴS LT)K_u]Wlӓy]NF{kINo7vib3a)\B|dflj *=J{hF8FONze'-*=0 >d]O3Ȓ.otsIߞ׸u&Cxx>_֒/j~zJ[5<%).3lzL}ݧ؆zFQ}6c9i1f"N?aSrI&&ӣ1?H~ g.T\m6'w\|"(fJt} rފ2ܘhM2rym @K~j𹽽M~5[ &AN4&^>c/rS b8CØ'bĵyմp]6uO][-Ӈj"j4~Oc\ejٳ=5}SR`[]\c'/n?ܐ4g (F ~g)'cB,܎W5 Ge`%#p5Lr  !R]3aH vo~!=!szΘFxE^q~l\`8RΈUOܙF\ͱ+ @#)OYrUGZ̜GngWWWyj{Vo* SQS4&CFsMxl-3MbuS% ks>̅I/0Im2^/7?.Mqgyr^6Ƥ줯Ϲ;}ςbxcB<0 sOeIʠT'&Ws8~0v<̜Nr "w޷Rwۇ,ũ p+%TS̍^?9Vcmӕ^:(wy \Xw{3,s7ULde_&D0ks jЫ<իW"f\⏳SI}e/Mz-zS=+Tc1HoV.X9.ܼTӘQmLTԨt5m5$ #ϵdOP&aI'3wL҆d%7G˩F0Q)r}V)Ě{lTR-~id f???М :Xh).:+eǧG9aqS`Û:!]L'I.&$iaq}S`AIf8Q^˥[ABߓRd[J8'0T{Y+;xh^:T&j̢Rb]odt LCcplZ&}UY'4#k0ɋO)bŷ}1\KSKh)K) L-еH-feT E oj@QlArsOY$eVE6jg61ޖbĔʟ}rf-)p\x'/jB1?͌_@Uq0'`1-TNps鸞B:Z' `SnUkO_Y)2tIoĸ]V7Ϋ>S|wwww#ĻMe}~~ul5,Zfa#)LlJLoP_egi7fό0ՠMMJ`BUV L?Vˀ_LA[3TӢ+44XzrD}/sP(?{/^ݛ7oՍz :}ﴼO^; IENDB`./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/appName.pro0000644000015600001650000000177712705421114030236 0ustar jenkinsjenkinsTEMPLATE = app TARGET = %ClickHookName% load(ubuntu-click) QT += qml quick SOURCES += main.cpp RESOURCES += %ClickHookName%.qrc QML_FILES += $$files(*.qml,true) \ $$files(*.js,true) CONF_FILES += %ClickHookName%.apparmor \ %ClickHookName%.png AP_TEST_FILES += tests/autopilot/run \ $$files(tests/*.py,true) #show all the files in QtCreator OTHER_FILES += $${CONF_FILES} \ $${QML_FILES} \ $${AP_TEST_FILES} \ %ClickHookName%.desktop #specify where the config files are installed to config_files.path = /%ClickHookName% config_files.files += $${CONF_FILES} INSTALLS+=config_files #install the desktop file, a translated version is #automatically created in the build directory desktop_file.path = /%ClickHookName% desktop_file.files = $$OUT_PWD/%ClickHookName%.desktop desktop_file.CONFIG += no_check_exist INSTALLS+=desktop_file # Default rules for deployment. target.path = $${UBUNTU_CLICK_BINARY_PATH} INSTALLS+=target ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/appName.desktop0000644000015600001650000000023012705421114031066 0ustar jenkinsjenkins[Desktop Entry] _Name=%ClickHookName% Exec=%ClickHookName% Icon=%ClickHookName%/%ClickHookName%.png Terminal=false Type=Application X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/appName.qrc0000644000015600001650000000012712705421114030207 0ustar jenkinsjenkins Main.qml ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/appName.apparmor0000644000015600001650000000014612705421114031244 0ustar jenkinsjenkins{ "policy_groups": [ "networking" ], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/qtquick-app-qmake/appName/Main.qml0000644000015600001650000000260212705421114027516 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 /*! \brief MainView with a Label and Button elements. */ MainView { // objectName for functional testing purposes (autopilot-qt5) objectName: "mainView" // Note! applicationName needs to match the "name" field of the click manifest applicationName: "%ProjectName:l%.%ClickDomain:l%" width: units.gu(100) height: units.gu(75) Page { header: PageHeader { id: pageHeader title: i18n.tr("%ClickHookName%") StyleHints { foregroundColor: UbuntuColors.orange backgroundColor: UbuntuColors.porcelain dividerColor: UbuntuColors.slate } } Label { id: label objectName: "label" anchors { horizontalCenter: parent.horizontalCenter top: pageHeader.bottom topMargin: units.gu(2) } text: i18n.tr("Hello..") } Button { objectName: "button" anchors { horizontalCenter: parent.horizontalCenter top: label.bottom topMargin: units.gu(2) } width: parent.width text: i18n.tr("Tap me!") onClicked: { label.text = i18n.tr("..world!") } } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/0000755000015600001650000000000012705421114024444 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/projectName.pro0000644000015600001650000000320212705421114027432 0ustar jenkinsjenkins# This is the basic qmake template for the Ubuntu-SDK # it handles creation and installation of the manifest # file and takes care of subprojects TEMPLATE = subdirs #load Ubuntu specific features load(ubuntu-click) SUBDIRS += %ClickHookName% \ backend/%ClickHookName:s% # specify the manifest file, this file is required for click # packaging and for the IDE to create runconfigurations UBUNTU_MANIFEST_FILE=manifest.json.in # specify translation domain, this must be equal with the # app name in the manifest file UBUNTU_TRANSLATION_DOMAIN="%ProjectName:l%.%ClickDomain:l%" # specify the source files that should be included into # the translation file, from those files a translation # template is created in po/template.pot, to create a # translation copy the template to e.g. de.po and edit the sources UBUNTU_TRANSLATION_SOURCES+= \ $$files(*.qml,true) \ $$files(*.js,true) \ $$files(*.cpp,true) \ $$files(*.h,true) \ $$files(*.desktop,true) # specifies all translations files and makes sure they are # compiled and installed into the right place in the click package UBUNTU_PO_FILES+=$$files(po/*.po) aptest.target = autopilot aptest.commands = QML2_IMPORT_PATH=$$OUT_PWD/backend bash $$PWD/%ClickHookName%/tests/autopilot/run aptest.depends = sub-%ClickHookName% sub-backend-App unittest.target = check unittest.commands = /usr/bin/qmltestrunner -input $$PWD/backend/tests/unit -import $$OUT_PWD/backend unittest.commands += && /usr/bin/qmltestrunner -input $$PWD/%ClickHookName%/tests/unit -import $$OUT_PWD/backend unittest.depends = sub-%ClickHookName% sub-backend-App QMAKE_EXTRA_TARGETS += aptest unittest ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/wizard.xml0000644000015600001650000001151712705421114026473 0ustar jenkinsjenkins ../share/ubuntu.png A simple C++ based QtQuick2 Extension Library with simple UI written in QML. Also includes: - a unit test for C++ - unit and functional tests for QML This templates requires a Kit using at least a 15.04 click chroot. QML App with C++ plugin (qmake) Ubuntu Click package parameters Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/manifest.json.in0000644000015600001650000000072512705421114027556 0ustar jenkinsjenkins{ "name": "%ProjectName:l%.%ClickDomain:l%", "description": "description of %ProjectName%", "architecture": "@CLICK_ARCH@", "title": "%ProjectName%", "hooks": { "%ClickHookName%": { "apparmor": "%ClickHookName%/%ClickHookName%.apparmor", "desktop": "%ClickHookName%/%ClickHookName%.desktop" } }, "version": "0.1", "maintainer": "%ClickMaintainer%", "framework" : "%ClickFrameworkVersion%" } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/0000755000015600001650000000000012705421114026025 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/0000755000015600001650000000000012705421114027167 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/0000755000015600001650000000000012705421114031207 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/run0000755000015600001650000000027412705421114031744 0ustar jenkinsjenkins#!/bin/bash if [[ -z `which autopilot3` ]]; then echo "python3-autopilot is not installed. Skip" exit fi SCRIPTPATH=`dirname $0` pushd ${SCRIPTPATH} autopilot3 run %ProjectName% popd./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/0000755000015600001650000000000012705421114033455 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/__init__.py./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/__i0000644000015600001650000000205312705421114034126 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Application autopilot helpers.""" import logging import ubuntuuitoolkit logger = logging.getLogger(__name__) class AppException(ubuntuuitoolkit.ToolkitException): """Exception raised when there are problems with the Application.""" class TouchApp(object): """Autopilot helper object for the application.""" def __init__(self, app_proxy, test_type): self.app = app_proxy self.test_type = test_type self.main_view = self.app.select_single(MainView) @property def pointing_device(self): return self.app.pointing_device class MainView(ubuntuuitoolkit.MainView): """A helper that makes it easy to interact with the mainview""" def __init__(self, *args): super(MainView, self).__init__(*args) self.visible.wait_for(True, 30) def get_button(self): return self.select_single('Button', objectName="button") def get_label(self): return self.select_single('Label', objectName="label")././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/tests/./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/tes0000755000015600001650000000000012705421114034171 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000016300000000000011603 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/tests/__init__.py./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/tes0000644000015600001650000000324612705421114034200 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Ubuntu Touch App Autopilot tests.""" import os import logging import %ProjectName% from autopilot.testcase import AutopilotTestCase from autopilot import logging as autopilot_logging import ubuntuuitoolkit from ubuntuuitoolkit import base logger = logging.getLogger(__name__) class BaseTestCase(AutopilotTestCase): """A common test case class """ local_location = os.path.dirname(os.path.dirname(os.getcwd())) local_location_qml = os.path.join(local_location, 'Main.qml') click_package = '{0}.{1}'.format('%ProjectName%', '%ClickDomain%') def setUp(self): super(BaseTestCase, self).setUp() self.launcher, self.test_type = self.get_launcher_and_type() self.app = %ProjectName%.TouchApp(self.launcher(), self.test_type) def get_launcher_and_type(self): if os.path.exists(self.local_location_qml): launcher = self.launch_test_local test_type = 'local' else: launcher = self.launch_test_click test_type = 'click' return launcher, test_type @autopilot_logging.log_action(logger.info) def launch_test_local(self): return self.launch_test_application( base.get_qmlscene_launch_command(), self.local_location_qml, app_type='qt', emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) @autopilot_logging.log_action(logger.info) def launch_test_click(self): return self.launch_click_package( self.click_package, emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)././@LongLink0000644000000000000000000000016400000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/tests/test_main.py./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/autopilot/displayName/tes0000644000015600001650000000124412705421114034174 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- from autopilot.matchers import Eventually from testtools.matchers import Equals from %ProjectName% import tests class MainViewTestCase(tests.BaseTestCase): """Tests for the mainview""" def setUp(self): super(MainViewTestCase, self).setUp() def test_click_button(self): # Find and click the button button = self.app.main_view.get_button() self.app.pointing_device.click_object(button) # Make an assertion about what should happen label = self.app.main_view.get_label() self.assertThat(label.text, Eventually(Equals('..world!')))./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/unit/0000755000015600001650000000000012705421114030146 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/tests/unit/tst_main.qml0000644000015600001650000000222712705421114032502 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtTest 1.0 import Ubuntu.Test 1.0 import "../../" // See more details at https://developer.ubuntu.com/api/qml/sdk-14.10/Ubuntu.Test.UbuntuTestCase // Execute these tests with: // qmltestrunner Item { width: units.gu(100) height: units.gu(75) // The objects Main { id: main } UbuntuTestCase { name: "MainTestCase" when: windowShown function init() { var label = findChild(main, "label"); // See the compare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#compare-method compare("Hello world..", label.text); } function test_clickButtonMustChangeLabel() { var button = findChild(main, "button"); var buttonCenter = centerOf(button) mouseClick(button, buttonCenter.x, buttonCenter.y); var label = findChild(main, "label"); // See the tryCompare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#tryCompare-method tryCompare(label, "text", "..from Cpp Backend", 1); } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/appName.png0000644000015600001650000011407012705421114030117 0ustar jenkinsjenkinsPNG  IHDR\rfbKGD pHYs B(xtIME 22 IDATx[r\gQ(poGaD% @@UPŧZ'7-D($@a7̕k|{@#7bgf|H;j}I6WnFșufpgUޅmԼq;V<9N2s%c|oVMNZԫjHO^x'k#@d=SEkuMeϝON.ffykd뢴kas4O]COOO8y=ڂ5 87;N3ǬVa@r׉;S}f|.2gSה.p{ Ùxݷ;n8 Y*d}-3 `iɑ k|1<==MdyuzͅXiys'HIrSdYZYו7+33a‚3h b[??+̃RϽVevo;߱ӢKxsCZM}^=Bs扚'~FLxZîZr.n{tt4sԿe0V;z/'⺟|O= tl%3&fL*da epfFk$nc8אi<y][Vi3=3A}zAҧHICT_f)꿹 '/n"Zk/_t>67q R,s^SWY=b$trn=C+bJn Olfu+@@9YfT\|~ @)]m~ZvQEZ$D6a899|߹3Z/ɇ0nlNk0A5~>|Y/Ȏw.]3L0R!B̀e_=~sml6lr]0.q !ZJ{yɻawחˊ*LϲDW-,櫛'oi$)6#=l)憪NxhVb^_CX$G!W-{ `| A!{ٽk`*OGdpz '/!Lsst!g9lIZZ^oq[2զ@6v noowbl(>dޑx` {<>>].?Kd},NZN;Fzd.2eg͑vxr-i2gGnDv9X 3;07sn fBN)1?3e\k<=dy``krp$t Zw 6dEq||<{"TycD)f1#)6yF*,)eXO&K Fkw2e&%6= 7{ k[93׫& dpdG;@&.lH, 2:d7wGN0ٕOO߾}D1X_'Ed\U5$Kř۩(5HivR`HFwn$̉x2h3Ӳ>x:H>%OQq3'e uvlz#Xpt&+ 3j/ Ys呆46Q_Ѝ{ʛcaAq21"g=<{jT~l@iQcbZ[-Фbw؈=cDY#Ʈ%8\A<)v˃z?@%l]c; Tܥr5,emOOOxT#ia(!Ð$% 2uZ`Yf;/Ҝ`LR^ɸ~>EYӉaV0N',SRCyhTeo`|?E>ʞ1SdI"9DpBfD{ ˾>߉ѳE j05:b'wd)@bFN:{C?SbM4;Sk@Fe>3ʴZY=ѳ.I; ZKyM'oCl}l0"fE]i)wRlr X{7͘ L%S~OKt;9âyw炫/;HɛI46m." ix3%U'-S:%s*`--ߟѢ;Yt~߷oߔk;"gn"7! UP^=SpIҪ'ƃ$Y}N{(b)5d {G6N0z$.On. 2qdtN_C2 pl/V Hz)rA0iƠtb%g9Kw zցY,۷o-iܬ^-kM-e  pPYD̙; R_)l 31/o"I6DQl0.a ğaZW3g=1YS4]lZaמM]yMsIAI.6of,:~c )Zַ5aB i3do f'4߁Wdڻq`1i=DǔӨTgX:Y#<10FHPd5HU3"LyĜ hIصORȔѯh |\ J |Y0>2ґIyxdWF= &bZi v\zSuސr`$3{\- rS%Gx32@+Olo[SٱBϯ^9.qHKϠu5&<3c7>3%5fkemӍ&^ ޙ*(AV7vSΞ;ue35$)SL ʍAwxY} rcGb7[fZCF?-\͎SgNu HeY. &}9eWMqiQg&66uG[4i_\D VAXWfXj8 lHtʲײ Dm(#vqҴv5MDWYo=?I&nQP$&[֝d12[,K5*:;ǜXǷa']H&1SPVcMR:] gik%-ÃzxÞ45Us4x 1 s6!ā)2{ gYOg9Ip}|3ec:Y4qGgc Q5Й WҰ4#:ÉL)iXii[**ɕ E:c3-XʣAs3tĐz֯1$Ȏ =M4a%4o.%UwT -\*d^Vɝ΅i5hh,EΤi` 0.߯\uxSQ3yL(T}벎cLvf+C.Yw_t񝴕fډ)P{7dj|0q32Nf>;;ۓgΤC@j(5Q$,k$ӗO(-Yv]| q bN3 Dɨw\uB5C,خ3tryoYˈ[6ݑL)[XXpS"ND,<\5Y noS2KTtxv$ M$b3)&ɏ9tԥ'Ӎd_\\*PiϻѣqRrMdYǓ `Vޣ 7`0v#TIKL%ρ?;))5YQs^RYw!@G-+LMh an継Huj b͸`K&k\Miy3j z"9g k]ia(^gxZMڳ 0x6ּߌܵUsݷ6%FwAN?fW8LmQsQIM|wi[sy [ug;4=']vr]<?TKA5Ñtno-13=M`KRW=G\z5L6:1JZwGTnIq5xߛQm5ɆLچ%KO-IT=מD,ͩc3=hݼ) dO|;54GNfbO?AQpvO!H\Q5Jj2b4y*7 +V}gw?g|xAe΀a IfSw{v&wN<gV g] sp,fbcgcjv4oD*u6s">-X!|hsJ%#*Xiabr+c$u-WHAJ~ E`}ӿ,:ZkƎ#n35& N=#hь-Sb-OΛ>\ tKBQLJO2W@5aތsr&1%oE;Eb ff~ڦ dS~:>#rgdΡy[KgZaxxxU4"YfwTjn8#SL3g?S%2S!ulja&`Ğw^kX9r䕺Sesib&VJY/s:zsm*@s6HVđg2s(5%f$e~s5x X/jQd6N CF2Qn_><<'?-,0qxHgLdzsjN^KKXs2U,S֒'''{vڿg߆aXC~a%삌1-g*OG,zlLXf5̇y\-I3cJu,J/AnO`bƒ2RWM!\̬{n3>Ale1笜v;Js)C~՝/@KcZxR6Z^'ו hb,=;텵{||f3 7G@Aɫ?b "߾}e2~E?KCMm"r;NSueVs\iA IDAT.0s6e6"N\Y$5G jfXcFz*+͎+eptt4&YQeO]wM_sINYjw`um~N [v~?>45ˣ (?\W{خbff5N.=:' ۡ6sӟ G` L/-G oHkRd91&w4JF$ƐD:ܬv͸Al޷۷ɋ_{$,œ3Z1 m'7tGUf4HQ].wHB[z+vR~^gbےIfVԞAxd@~0s|}:ڙI+T#cXՍ9_4?VZl-sZuuu5md)Bϟ >eƕG-u4Ș54@LJ?|0f|_ |3`*54dZb)y&AXQ7Zq3}Ew&v;~a vh2#b&ƚu94_'F&!y"G8Ce`Hd9> :,(L>{\۷yΕ`dw 檳z,ӻ䆵֠vso}Q]~dVJ6蔬 6AD;ϜQ❢RlnLnKRe:'Y zxx| $KCz:^s,c2!*وgĜ[20 -a^AAm9qR' 6[*];'0C#`AyL/ijSZl}|Ky܍FۦΔRRkrQ̚F1kvҵ'K1;gn\puرMsH.|vUa4ac.tR s3Z3L:yMfyc9f]C2^v`CSux2Fud`eɔ٬˗7umnxf4%tlɚ ?"*Y)Xs{{;^|ԝ\pA''ی U,ӳkea[E$&x0;1(1Zi fi \9Ӻ+Lˌo6 rd_Y3eQc3DZj_ud&e<Wž[ym -Q|ظ/fnAڰ1\u2爽l?#E;ov#tGdkʅbd`JߓcQhoP|uRw)l6%N]d3龷mjCUğTjj,ؖdc͟%Zkw,I05F,hoD6"#64&Fԙ\b5$3n2#f.L`E76?cAgSƔXFFXw݌ Bl7مV2|5pXmFN L 3LzPv22x1@yemeL("5s|ب(f | 7 \Ox34._DiNUtr<S6j}6M 0OrvPL[Pl8==,.cp)v0Ça)I/{Vb&S)VF73M'Հֿ+eIXɸc9atw"rڲzxxnnnZ]7o~fP5Q P90;=*Pouyy91E;U+r/{::mhu\g`_Y\ w,O#[e}62~3}"|Ҷ^(Z2v:ez;\lO< K)H:Okkۑngr\;i$7b޿+_P֪4u[&۶/N?$`V'9(8לeɝш#K͙62a T^>>H3gvO~=kvI:y9gv߰R]__޽#`,NW2N03kSL\ ӭ[k957;:Wl>OFCdDdfvVv t&wƲ% G>oLŊdZ{g SLr*On]Vjc=`]'Q.~B#("Ш;"\<13(܋NI`0]86^QK:rԪr/MXbIS>HzH,"OKh  s)_Lˤ|/l.DRu'=AVt4pєG_kmKtyxxP46"kD 888kCfd?+>kf+ f$Xe6qIuߜO[ЩJBE~aXDNcӥ@(fp؉խEf sV[wtƒJ[VÛ7ox :,)O>Ǐў+|ݤʸ9aHq d9a"2\@;q=,[]3Z)$dke\{ 攲n'CEkT:_AwV{"R#[Rf Գ?::NNN&>Z۬U eo]% ܵ,;#zd+ڌ6P<cNމɖ 9Fv"efnnooK}iTyD;_fD9/b^w2V@I 5:sW;??...ԕƦ_t|/6n_jHyLn`^>*| f$ 7tIJ6 lE2JI jSg7s 4LXA|x||t0 wwwsB"VdϹtn˗/Yf /'fN۳ُseK d o!ܦ2#N6vq!fzd/jgvG8%b0 5锻L`~9?(k٭>,L[k7Mr9yf&pvv6DYl(Ꝧԑ뫺Mx?<<< F F~;L3߽h=eիWŅt*OɅsc*lCcgvc{0 v>|yn$?sOYW1 張n I)MƐ{7F1cǽQV j^~=yF_|_~~2( O{zznoo8_^^F{NlcuH2 ƿDߐYcJyBa:U圹bV+mH]///rK[-`K3}fgO(Lќ; =lJMޫ ͟R.Z 0xԦD+߼y3F,bJ/7I}X;4>wj18UfXG$js'Ӷ74K%٥f&0}:2wqϗ|g Q\'[*L:A }~z_6[!իW4''''#'~$ unB/;iAt_&A~ɹ,7،=l&ley ]/0ů.@YUrRҀCjXjy͡fEfiNJ$=܌svL߿WO<l:eoyo޼4yo߾J9wtt4C3 \C̠S RPlZ_޽{7ǣ 7իQV{s=ڵϕ[fuǏ6ʔ|*ln07FV,X |iͺ-44[:Yo>6) [1ly7YC8n]%U8 &pķ ܤ=N"䗿kIsn*i8%RirYli1׽M9țbBz7pѡ/OU@Uh`b:>݀|._mYj L[;|'Ǐ hIfj`{֬ę>??q32C;g샫QU>3gT~t:1H'pq ڈ9TuAVK,Xo2߿~;Ov;r d`R꺘te;-'''٤gb']&BF*L2ze_]cR*Ort]#~gց乁Y~Ӓ/Z?;{򶉜{룓fi|v)%fAwaMLkɐF|xx8}vsB'aLp* JC0*3# T|o 8YYY<}vB¯ ,]vѱHJz=Sme3*`|'Ӳdv4  4n;^f&S(aV_t%4F梳{߾}ڜd5U)4e#\l?g'p~NMVYך_a.ʺ7o;z"fjK ㇺ M,s1JC<;"EoucN߿tNynjceԦXb &TN.yLI2VZO:,sMxN˜jYFz3=S42heͬܐEK˗c""dA9:1*6Ң>}&Mc߹Tt3:muXO7\׭Cm,X|:<<7{|luRb1 5ߌ Jx;r&L)Q^nTA^ H۷ɜDRdI6h=7oLv_u #Aw\I~˔ߘH ]lNc|EA999P22\ ]Mکe՜;HQ,&VsCSi|qg???o޼Q"E%,%d0͘z>;D0x ~zO)Af;irxxx+\C~ f ͿgB1irJ% dVKV_k: +F'Ґͦ9*_lֲu}\NN~)$+Q]բ] S{/ IDAT鲊Ji0?YÕDXVU Y*LAb0)m=0K'} ,:muxx8<<< mʂMV쬦b6Ym.u/˃Zih"G'2`/ceˮDM[ʅk K,ܵ{IA_~Ke\=rg`H7TJwxf%YDf׀@;͢bUזEt%a96~nl5x||O|ٶ ?///CnE1 [JUM[יڳi=;Ɓ?::'یMg0^o߾8Yz/r&K 6b/l)FxZ͙N䟧kLxsDImLu|IDكgZ˙Vo XЪKQn`n PW>:e)3AA&.0~Lo T3R-%k)l$3WvRsc|I<];<1rꭥ\dߛl0e%bmXWU4<#sɬ IR6QނڑNr[|ڛ=Ɔ?qxDE'aleQ>N>?s;¨hVy@9p'?Fȅa&˥2i[f=cFeKCHY::WXG3Tl;k6mF;jGcCvª3&vp,HBcuS`ggҦ~O #PXMbjI4M%N.3?r':ns>A+d.IlX0V['lYIzӉ%J;iTm8&y*8CbK \h녓Er1c{{2`aiYFQGzʡ#̔ y6,+͵Z-ugy(TYfJTHME?20F@gfˍrRnĪjadmlpYK|5OXj$2|+͡s3먿[c'պf1ZL٘PsE9w2'qEiƩLR5 qV*>Kc([$F!CQV8|ymH3F^؏?ƀS_nLhm@*sBܠKUY.SKwʑ/, BaYbTYvb5Nf6?F*PTS߲ȇcщ5UR@SƑcLapʠE=. e@Ǐij1޿L,2,6m mL-RQH,Rps5 ˒dCL;MBJm[&@i i84mre6\9.YM]O6(ݢS+@)X> 1 %eܬکN7cVQypp0/{>SÇ{Az3[q3Pe)Ɩu6+O}r]HPnn(j7fGj`<]`ڜӗ-C,0hw!)O@{2 {;N8#f aeV~gHI<ǙqE0ʀܷojiRMM^JMf"фupg\YUkZgќ;+UMMi7SnLRX}p9+~{˒g9FeCnd=V%矉0-J 2d"Q`a Q&' `Zl*&QegY'i[`6jo'h&df}~; $V&w@"{>xs+dk&3lO5 * 0>݆q9cfΥB3ȧ;r Hh ,d\0H w"{%;fGEzQ-Z%"_DYl&$3 W" ;\<|]:β6YfL0ߛa",CX LZsv)xbHIIJSshdHŊ̒it|l1u|᝶(2xOfC0K BfGOrA)[Tnz5$TDU1x/ͬ8ds}_93=ˮ~"cL!*):Ev ВS"ּT v9Kn~NhL5q=ivґp2U6IjVle\H1uo93°nDxsQr'Y3*[F`5?kN% dW+ґ:9* }5YsKiƩ/_Q!Ɣp3FleKK@X)Ŷ`; ;v.)`L: hc #lɿ r-VP׽^'<0j33#-0;'d`fRJQnΔ]V|l9ʝe2 yŝ%iItlf<m0ԚOb^rCaf9=\t.H4lhc[^zGh1"X7#JezO4IwHP& ,,S\I|V:&{HNncZ0ي҉ֵA^S(ѻFRCDLENj<hhx|/BΔN.cd"Y#G$$ޘ X:ϿYu'?yd~ ro[cmsPobC[ιaXFiY{fH3 #,tZpv;/_())udo}e~~^z`NrT8yh$Y7Xpg0SWPeeQ GaLNНs馵Y7ͤ ՍNJ;ڂB<&]5F1ȧ1p56a $S(:%=X{QYߓɚ_}>oooϟ?OOOOׯGzfi7.e!ÍI^L]DtTfumrX$3 qq z>f3X>...=P7qe899tD~KB3يDYW\yZuƜ3N0f gvdXZ'/].+6cONca&'Β-D-s$$Sl0ue۰ˇԹ$fe"# ZPƺxLci]C& Xzz:G)Tٜ3xCuwzۦxfFAni3@%󱴾?q!fۀΘgE^})յً֮4ڴ:\Np6{f $dDҍ4dnڌ!s:m+ɓP_f\@cܐ_]kx899cɮ\HHp\[=6x%%?R;3|弅|T Yv&&r&rghZfb.ngq1]Tդ b43t^Mʸ-)mԲEfvQ6N,^z5ӦI!1%K#9z5ߏl6bɃ[iTw3!/+n8 K1gK6ͳ\tުSSš&~oN |x]d` 瞱rzzw;D krܼ|؛fL~6ׯ%[YWZ$@ `Yj8K-]CgXVIOuIm6gZkT TFz겏L#-1'ò43Wa&ȅeCW"Z=kG؎Y.vUgG!o 2,N2H/; ĩgƢf nzxr443ImNcG,\דuE 8KTm50؅@E;p]n+Tge }%ͷ,t/5dfg?^4bcaٔڸٛ7ϴfdMV *TeIOޣM[a#do`O[|xED<;!s(իIgNlڹyPC6ٙ2+􃃃QY@' y $QV672H*{DԫU6{=OEk1fr\N(2>zxxe7MZ=Lehٕ0Kw8%I*g#hY!˞H33Wf@F2j|2I6;63+ }%9܄ϸOԀ_&1%6ԃiKzec|!ӥ8[cIIEc {Jf % g0r"uFqjM*J .$X1:/y{̺?3٬My OOOz+X2Yۨ᤬#qxggg~-ѤsZbM`=GaY݃ONNu#tae![VWX9y8@IBB:慿yffB!|e\F5!eo0#z}}wJp@- ᒡFx >f}ɭg*Iw&ȓe^2T7 =7ij ,= sV24biXrӧ~`' Ht;INQ}#CgV $⨑yzKEחikݎ`uXr)el6cL\ A+˔,Hyfx/ F նʹfφ˼ i9䦫MQeGNY6;`*TTa Y]YP|q876\:&8S/([.˛Ouy.aْD*I+~gm@)KH7f’T:)2e8;;MffYZnX-(˅:ﯿKԄY W8|Vd弆:Z>ZhN 'Kߔ^ZLqɠ &Ud,e-ljMBf[mj:)8Nu}Mn|///X~=??^~=|a8???gin.ࣣ׺@D\*7YME:=]}v M'+8Z>%}Q>ݜi-OZ9 8쏲8;fHnUȠ3TƉfۄU/mnz#K9Rlr ɶ,e~:l6 }e{\qd]=F,yd@\nG<)UmY}SKob)ðySgN_^^N8L5e5NgT籎/NyyȋD۷{וdj5:a |'-Y///HEdG.pUv;ϯÍuU]gPA&LYgcaf/\dhiNBWΚ(Ai@Z$ \j5Aɹx2“a,.3C13@pww7<1)F!VlФSdڌov`a IDATۥ+W z||@Rɀk:Tۦ7:p,JLcݛF:Div`|ߴ3ay&#)Ȕ*'M`FЖ1ˀXE4 ^oZ ڵlϟޓrAeWRgvh7ԟ?>< h]e}eIj.Mcx/UfRrKe+Unl]|4\Dbu TevG2F-;3x؃@4ZE:oujeVONc$v^<<<Vhfࡅw D@@+h߇ϟ?OZKdL ~{eEYLDN 3/{8T3aIaї ]Rl#Vظ$;e3XPύW/bH#ЖʲnIr ߾}KU.o޼lI^uHH٭юSG.f2X,(ap>*>|Ƨ9>>>~8e ! MʍXrf|mGl3wu4Wa'$KEq0jf5k#+cVDkt.SIlyFx6,R Eӡ+3#nDt}RΔͦ3ȓ(s1OFJIrssf}v*`3r{nTL'T`܈U}d;͚tfI" <)5J#3EGԲι2EK:/BF4o}NpdI!LAMGHpRNIQ9f,iҹ&PV3˪5jih91e~f6UH|1dR땀)q.6,+6ugY3u1 J2]bj `ͷe#y_6, &nv >,N(Qfl6ϟG ؉Xm] ydt{KWA&($pu71==NNNRi9ʞ쎕G,|5eYu~Z\X/Ot: g4\o2xq. n^v#9_~~:qbgƲ0ώD(Wdñ0|^R $gw 8vׯu&π99%ׅ 2 O^2i8|3աP^K3˴?kXk10Ӌ9+L |Y ck*5b *>92v><|eGѐNrg+~5Qz4pi653Uv ^^^FY'kRi0qvejLzOFhK:  Wj`fK_KLa\߿bKAn^>r9VS Wwyy9q%&К(&gf>L9F)X۫F͚7 qDf5^?>>fŘ5s>OrE(l5'P#s11S22+e@(ɼ4ga1B4Y阍Yt%B׋M{ g'IJ_ɵyjכymb& 3 ׯ_WWW*)Znqlujn}OIa57Nml"2tn.aWȔ9MDbde烃͛7%9yH7J2x@){*, $e!  [.\$X"-$Cٮ)vY_xԋrsP@’\ؼyjLIT471|)K-^'ߺBsYs0j}vi5sO]3k5q%yzzsafʝMtI}\{ֶJMAw(;Q"VEZ6VꬁYi̺ȜA$w:jx*96E}³Y]B5˴3X$摘jS|lb.׌Zfkf¤|l2vؾ.EY*s*p(Kbl}-cF^xY[4c+]~t&S_;xXsWtrL_TeC<*9 ia?(0l4܏ScfPNgb $5qJU{,/<<07l\JG-A&qrsRp2=/"%CV >5Iu%txrNe4\p<92K;K;!c?cptg^ Ů]KIZӓ&BeF~t)=:rR= .}fMAϴ@̌LH&)"AƙFu8R+;V[%6_2 `LK~n׺Ln%y&[w*5|ʞ?>xQZuuuKf/_&~GGcfʌÇ{8a7u;h&yP$a&;8G2dFA:.0}IdFʗ=OF=G3bCIxzz'"(M\v:/Y a^Ç&|܈g.tbIY ePk,mMG2Ek6Aٜw?˗/;0L{MAHrCr2d/ $cb {d@i {=w[̍/+d{`ݎr\+:F ǐYǝu6$]J6Ǐ0:Ce9m R{5ޑN\#ƙ$W`iAŗP> AL;j͚2ìВ/%?4]!'ۗ8l!LgjCOfU;bKA mz?9 ZvrIAiL>5 #O:۽I.d/2ݵys =S.ԆXB)7OiN#6.:Lh ^Vnk {hRy8?? f1%Y#իWÇ4Oʶ );bv#0. ;I* #FletQ[T>'En>>>N5 mLF]0X7r|gq?;ʭe_:W$Cy{w٥eg6aZQ PXv7GUq+23z< n۴ K988" &%#AM:C dEplUe/6E8 *ģoۤ1$4$U"^m\;q0a65eLWd^W'x%F3ԕ*ĵ\A] zۭN+r3ʌ~qAuSlsg`}ݐ hq'~WIBFcNtmHC^ywwwÏ?S)&kxsx!THs2S+49djy?t`>3аq_u:ǖLN˿ t:KeHDf ξ|7l! \Fh>LsV<>>5+K8<0Qt͡YuF81߂3ZVnnmvkY 1fԵH<7\DnFќ_^^>-2 R7JmQRrSu}3&g͞2ImYm0ş Uh439\j7{nس=M Vd^$Pl;>UМNsnMׯ_OƇ d", &mD&s)"c4)b՘ E7kϟw2逢Ǩɇidƀu)TPYtusN62! z?13eJKIZ ^:M9:yrdFrf=gfckLS2jK'mA)h4b}}gKfhŪkOPǏfٓM[J&hB͍E cnf@%]l8??\a%PI6.p:05׵@3ck$~.Ft\j R PO|XD9#B'oB$t]o YkPT:v.O4vͅ#xrqцְ.sIEV材2T s Lֺź~goCF3wcrHm;|(3y]Ǭ2Q+Cm) 0&,~G/lXmV)ȗXݰlt22O#tXT&6n;BfI"~+$KZfIL˯(` 3IS:<朘XѓѸUuO2RPmVN|l&Y=(Rz-B7`d ?f ɓl!"Rc9(C aFw3✆O;;;1gXu(NKñQ6gB$kѿ2=;[w1u69ײM2\ş` =6" k/I!Tgg+p~v\JOķfExxx8hȺ8  W f\ &Z~Wħ: emm~&Ć~v:J30 ZZ*aofbLӸ12K Sٹ 2v ,~?UmOuasٚ!)c2 3"N|Nf>۴guMfTqWPM7Yzf֑֜aaY 7;l;`ka3eHSYBj4 =0I+m2]O95!x:;(3@rZ ??fĞ0_pּ4j4sUyh^u$*ZVn#1H:cƓmܤ gpYF 6 SYr-eڡjl_3Fu%2N6d_N2s(N`s\vu79q3w5uP.4*ggS8cI $'2-Ό؉VJ}dM޽i,̰ڦ:Xk7F,b59Otn|A|@Fdji[𬹈TzĹvzfxtt4MR9vJLI3SbBKa_ASmZs | rȇQ=''=Xa&uL"MkN[6qmXr&]$xh>@_6,ۭ@li1 `\E^yeM57'#v7(Pf[E5A-fbUN06$~OY~ܟ3|٥.^?5$E{vf-ɖ_]hUkӀIwnXX<=LtQE6Ն" zzzg I~ɔ&d$zaٳK{1/9ڜfRJ୛ `-ۆls %@f IDATmN%6l)e+K1ד13RN$sa:>ݔر~c\N-8,OL'9^t'miϳzM eչ"`;.0jj\_x*5p3kN;mf)MJlUOO )gw>ON˺7a^ا l8Q$?jo;lN 20&nsS 6hN`B|<2;5; (g4LY2$Sp >U$fAYyΥM,wd)'OfOS&֞w :ПWf>Rn]zߟ"d2PU- Dl02rTv¢?ZNOO=)9mz.c9&trSZﷲf3AM=F,~qJeG#<,ĉ=lA2SH}dE'j")ݜiAğ_3k\5&:FN}߿C2J930Kr*̻dFr"2Ѱm<={jL@B^4B0}8(_A|D9U/~5QǕ2tign>,]n+2}fH~Y,ިqj keY}l{"4_ffd'ZKB3.#='R)tXOiX !,M(F$蜙 L^Ոm6arD 'iFEtac}%w#%Jdm?3` 2U:l'7Fz tS30Tښz?3ELh '$GY!XjJ;M[ AJt: /IV&ׂ@㞩kr㪲0Ze0vpP&A0\x!268Uk_"$$Uus?ߏƦRRDvs$jj5xDdY'Qv٬%Ƹ]̬g0-n Tq2Si 04$L LP&ΡD:-(/7jD~'i]IE҉(K2d,'v8~oue}[t:)h΋P'`KnݓO+@lXE#9g Dd&@ 0f^8DTe$d ;Sb dY N W: 5dn*l +P͔2Ԫ![b&Qw67%ujn)-]qy]Jjx 7|8ZE% U,I^kn+ve9чkl$*${3V bITu#gցFFqd4oYJ؀hѓ1I9 8UVA-A&gfU>&kbӌ]2NeUq JK)K85u*g&ٽ5|Տyׯ_o|qg}駩?IY׿YYYf//{eYp%mYŋҴpаK}>q*i,`R)2qM˜k_o5j󗆟59+^ ~AV\YTI~`Q:WW ǴS LT)K_u]Wlӓy]NF{kINo7vib3a)\B|dflj *=J{hF8FONze'-*=0 >d]O3Ȓ.otsIߞ׸u&Cxx>_֒/j~zJ[5<%).3lzL}ݧ؆zFQ}6c9i1f"N?aSrI&&ӣ1?H~ g.T\m6'w\|"(fJt} rފ2ܘhM2rym @K~j𹽽M~5[ &AN4&^>c/rS b8CØ'bĵyմp]6uO][-Ӈj"j4~Oc\ejٳ=5}SR`[]\c'/n?ܐ4g (F ~g)'cB,܎W5 Ge`%#p5Lr  !R]3aH vo~!=!szΘFxE^q~l\`8RΈUOܙF\ͱ+ @#)OYrUGZ̜GngWWWyj{Vo* SQS4&CFsMxl-3MbuS% ks>̅I/0Im2^/7?.Mqgyr^6Ƥ줯Ϲ;}ςbxcB<0 sOeIʠT'&Ws8~0v<̜Nr "w޷Rwۇ,ũ p+%TS̍^?9Vcmӕ^:(wy \Xw{3,s7ULde_&D0ks jЫ<իW"f\⏳SI}e/Mz-zS=+Tc1HoV.X9.ܼTӘQmLTԨt5m5$ #ϵdOP&aI'3wL҆d%7G˩F0Q)r}V)Ě{lTR-~id f???М :Xh).:+eǧG9aqS`Û:!]L'I.&$iaq}S`AIf8Q^˥[ABߓRd[J8'0T{Y+;xh^:T&j̢Rb]odt LCcplZ&}UY'4#k0ɋO)bŷ}1\KSKh)K) L-еH-feT E oj@QlArsOY$eVE6jg61ޖbĔʟ}rf-)p\x'/jB1?͌_@Uq0'`1-TNps鸞B:Z' `SnUkO_Y)2tIoĸ]V7Ϋ>S|wwww#ĻMe}~~ul5,Zfa#)LlJLoP_egi7fό0ՠMMJ`BUV L?Vˀ_LA[3TӢ+44XzrD}/sP(?{/^ݛ7oՍz :}ﴼO^; IENDB`./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/appName.pro0000644000015600001650000000170012705421114030126 0ustar jenkinsjenkinsTEMPLATE = aux TARGET = %ClickHookName% RESOURCES += %ClickHookName%.qrc QML_FILES += $$files(*.qml,true) \ $$files(*.js,true) CONF_FILES += %ClickHookName%.apparmor \ %ClickHookName%.png AP_TEST_FILES += tests/autopilot/run \ $$files(tests/*.py,true) OTHER_FILES += $${CONF_FILES} \ $${QML_FILES} \ $${AP_TEST_FILES} \ %ClickHookName%.desktop #specify where the qml/js files are installed to qml_files.path = /%ClickHookName% qml_files.files += $${QML_FILES} #specify where the config files are installed to config_files.path = /%ClickHookName% config_files.files += $${CONF_FILES} #install the desktop file, a translated version is automatically created in #the build directory desktop_file.path = /%ClickHookName% desktop_file.files = $$OUT_PWD/%ClickHookName%.desktop desktop_file.CONFIG += no_check_exist INSTALLS+=config_files qml_files desktop_file ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/appName.desktop0000644000015600001650000000025512705421114031003 0ustar jenkinsjenkins[Desktop Entry] _Name=%ClickHookName% Exec=qmlscene %U %ClickHookName%/Main.qml Icon=%ClickHookName%/%ClickHookName%.png Terminal=false Type=Application X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/appName.apparmor0000644000015600001650000000014612705421114031152 0ustar jenkinsjenkins{ "policy_groups": [ "networking" ], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/appName/Main.qml0000644000015600001650000000312512705421114027425 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 import %ClickHookName:s% 1.0 /*! \brief MainView with a Label and Button elements. */ MainView { // objectName for functional testing purposes (autopilot-qt5) objectName: "mainView" // Note! applicationName needs to match the "name" field of the click manifest applicationName: "%ProjectName:l%.%ClickDomain:l%" width: units.gu(100) height: units.gu(75) Page { header: PageHeader { id: pageHeader title: i18n.tr("%ClickHookName%") StyleHints { foregroundColor: UbuntuColors.orange backgroundColor: UbuntuColors.porcelain dividerColor: UbuntuColors.slate } } MyType { id: myType Component.onCompleted: { myType.helloWorld = i18n.tr("Hello world..") } } Label { id: label objectName: "label" anchors { horizontalCenter: parent.horizontalCenter top: pageHeader.bottom topMargin: units.gu(2) } text: myType.helloWorld } Button { objectName: "button" anchors { horizontalCenter: parent.horizontalCenter top: label.bottom topMargin: units.gu(2) } width: parent.width text: i18n.tr("Tap me!") onClicked: { myType.helloWorld = i18n.tr("..from Cpp Backend") } } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/0000755000015600001650000000000012705421114026033 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/tests/0000755000015600001650000000000012705421114027175 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/tests/unit/0000755000015600001650000000000012705421114030154 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/tests/unit/tst_mytype.qml0000644000015600001650000000225412705421114033113 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtTest 1.0 import Ubuntu.Components 1.3 import %ClickHookName:s% 1.0 // See more details @ http://qt-project.org/doc/qt-5.0/qtquick/qml-testcase.html // Execute tests with: // qmltestrunner Item { // The objects MyType { id: objectUnderTest } TestCase { name: "MyType" function init() { console.debug(">> init"); compare("",objectUnderTest.helloWorld,"text was not empty on init"); console.debug("<< init"); } function cleanup() { console.debug(">> cleanup"); console.debug("<< cleanup"); } function initTestCase() { console.debug(">> initTestCase"); console.debug("<< initTestCase"); } function cleanupTestCase() { console.debug(">> cleanupTestCase"); console.debug("<< cleanupTestCase"); } function test_canReadAndWriteText() { var expected = "Hello World 2"; objectUnderTest.helloWorld = expected; compare(expected,objectUnderTest.helloWorld,"expected did not equal result"); } } } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/displayName/0000755000015600001650000000000012705421114030301 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/displayName/mytype.cpp0000644000015600001650000000020012705421114032324 0ustar jenkinsjenkins#include "mytype.h" MyType::MyType(QObject *parent) : QObject(parent), m_message("") { } MyType::~MyType() { } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/displayName/displayName.pro0000644000015600001650000000143112705421114033270 0ustar jenkinsjenkinsTEMPLATE = lib TARGET = %ClickHookName:s%backend QT += qml quick CONFIG += qt plugin load(ubuntu-click) TARGET = $$qtLibraryTarget($$TARGET) # Input SOURCES += \ backend.cpp \ mytype.cpp HEADERS += \ backend.h \ mytype.h OTHER_FILES = qmldir !equals(_PRO_FILE_PWD_, $$OUT_PWD) { copy_qmldir.target = $$OUT_PWD/qmldir copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir copy_qmldir.commands = $(COPY_FILE) \"$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)\" QMAKE_EXTRA_TARGETS += copy_qmldir PRE_TARGETDEPS += $$copy_qmldir.target } qmldir.files = qmldir installPath = $${UBUNTU_CLICK_PLUGIN_PATH}/%ClickHookName:s% qmldir.path = $$installPath target.path = $$installPath INSTALLS += target qmldir ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/displayName/backend.cpp0000644000015600001650000000060312705421114032373 0ustar jenkinsjenkins#include #include #include "backend.h" #include "mytype.h" void BackendPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("%ClickHookName:s%")); qmlRegisterType(uri, 1, 0, "MyType"); } void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { QQmlExtensionPlugin::initializeEngine(engine, uri); } ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/displayName/qmldir0000644000015600001650000000007112705421114031512 0ustar jenkinsjenkinsmodule %ClickHookName:s% plugin %ClickHookName:s%backend ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/displayName/mytype.h0000644000015600001650000000076112705421114032005 0ustar jenkinsjenkins#ifndef MYTYPE_H #define MYTYPE_H #include class MyType : public QObject { Q_OBJECT Q_PROPERTY( QString helloWorld READ helloWorld WRITE setHelloWorld NOTIFY helloWorldChanged ) public: explicit MyType(QObject *parent = 0); ~MyType(); Q_SIGNALS: void helloWorldChanged(); protected: QString helloWorld() { return m_message; } void setHelloWorld(QString msg) { m_message = msg; Q_EMIT helloWorldChanged(); } QString m_message; }; #endif // MYTYPE_H ./share/qtcreator/templates/wizards/ubuntu/backend-app-qmake/backend/displayName/backend.h0000644000015600001650000000116012705421114032037 0ustar jenkinsjenkins#ifndef BACKEND_PLUGIN_H #define BACKEND_PLUGIN_H #include #include /* ----8<----- import %ClickHookName:s% 1.0 Rectangle { width: 200 height: 200 MyType { id: helloType } Text { anchors.centerIn: parent text: helloType.helloworld } } -----8<------ */ class BackendPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: void registerTypes(const char *uri); void initializeEngine(QQmlEngine *engine, const char *uri); }; #endif // BACKEND_PLUGIN_H ./share/qtcreator/templates/wizards/ubuntu/goproject/0000755000015600001650000000000012705421114023177 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/goproject/wizard.xml0000644000015600001650000000563112705421114025226 0ustar jenkinsjenkins ../share/ubuntu.png A Click application using go Go app with QML UI Ubuntu Details Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/goproject/displayName.goproject0000644000015600001650000000121612705421114027363 0ustar jenkinsjenkins/* File generated by Qt Creator (with Ubuntu Plugin), version 3.0.1 */ import GoProject 1.1 GoProject { Application { name: "%ProjectName%" GoFiles { } /* Include .qml, .js, and image files from main directory and subdirectories */ QmlFiles { directory:"share/%ProjectName%" } JavaScriptFiles { directory:"share/%ProjectName%" } Files { directory: "." paths: ["manifest.json","%ClickHookName%.apparmor"] } } Package { name: "gopkg.in/qml.v0" Files { filter: "*.*" } } } ./share/qtcreator/templates/wizards/ubuntu/goproject/src/0000755000015600001650000000000012705421114023766 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/goproject/src/displayName/0000755000015600001650000000000012705421114026234 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/goproject/src/displayName/main.go0000644000015600001650000000124212705421114027506 0ustar jenkinsjenkinspackage main import ( "gopkg.in/qml.v0" "math/rand" "time" ) func main() { qml.Init(nil) engine := qml.NewEngine() component, err := engine.LoadFile("share/%ProjectName%/Main.qml") if err != nil { panic(err) } ctrl := Control{Message: "Hello from Go!"} context := engine.Context() context.SetVar("ctrl", &ctrl) window := component.CreateWindow(nil) ctrl.Root = window.Root() rand.Seed(time.Now().Unix()) window.Show() window.Wait() } type Control struct { Root qml.Object Message string } func (ctrl *Control) Hello() { go func() { ctrl.Message = "Hello from Go Again!" qml.Changed(ctrl, &ctrl.Message) }() } ./share/qtcreator/templates/wizards/ubuntu/goproject/displayName.desktop0000644000015600001650000000020312705421114027033 0ustar jenkinsjenkins[Desktop Entry] Name=%ClickHookName% Exec=%ProjectName% Icon=%ProjectName%.png Terminal=false Type=Application X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/goproject/share/0000755000015600001650000000000012705421114024301 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/goproject/share/displayName/0000755000015600001650000000000012705421114026547 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/goproject/share/displayName/Main.qml0000644000015600001650000000260212705421114030146 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 /*! \brief MainView with a Label and Button elements. */ MainView { // objectName for functional testing purposes (autopilot-qt5) objectName: "mainView" // Note! applicationName needs to match the "name" field of the click manifest applicationName: "%ProjectName:l%.%ClickDomain:l%" width: units.gu(100) height: units.gu(75) Page { header: PageHeader { id: pageHeader title: i18n.tr("%ClickHookName%") StyleHints { foregroundColor: UbuntuColors.orange backgroundColor: UbuntuColors.porcelain dividerColor: UbuntuColors.slate } } Label { id: label objectName: "label" anchors { horizontalCenter: parent.horizontalCenter top: pageHeader.bottom topMargin: units.gu(2) } text: i18n.tr("Hello..") } Button { objectName: "button" anchors { horizontalCenter: parent.horizontalCenter top: label.bottom topMargin: units.gu(2) } width: parent.width text: i18n.tr("Tap me!") onClicked: { label.text = i18n.tr("..world!") } } } } ./share/qtcreator/templates/wizards/ubuntu/goproject/displayName.png0000644000015600001650000011407012705421114026156 0ustar jenkinsjenkinsPNG  IHDR\rfbKGD pHYs B(xtIME 22 IDATx[r\gQ(poGaD% @@UPŧZ'7-D($@a7̕k|{@#7bgf|H;j}I6WnFșufpgUޅmԼq;V<9N2s%c|oVMNZԫjHO^x'k#@d=SEkuMeϝON.ffykd뢴kas4O]COOO8y=ڂ5 87;N3ǬVa@r׉;S}f|.2gSה.p{ Ùxݷ;n8 Y*d}-3 `iɑ k|1<==MdyuzͅXiys'HIrSdYZYו7+33a‚3h b[??+̃RϽVevo;߱ӢKxsCZM}^=Bs扚'~FLxZîZr.n{tt4sԿe0V;z/'⺟|O= tl%3&fL*da epfFk$nc8אi<y][Vi3=3A}zAҧHICT_f)꿹 '/n"Zk/_t>67q R,s^SWY=b$trn=C+bJn Olfu+@@9YfT\|~ @)]m~ZvQEZ$D6a899|߹3Z/ɇ0nlNk0A5~>|Y/Ȏw.]3L0R!B̀e_=~sml6lr]0.q !ZJ{yɻawחˊ*LϲDW-,櫛'oi$)6#=l)憪NxhVb^_CX$G!W-{ `| A!{ٽk`*OGdpz '/!Lsst!g9lIZZ^oq[2զ@6v noowbl(>dޑx` {<>>].?Kd},NZN;Fzd.2eg͑vxr-i2gGnDv9X 3;07sn fBN)1?3e\k<=dy``krp$t Zw 6dEq||<{"TycD)f1#)6yF*,)eXO&K Fkw2e&%6= 7{ k[93׫& dpdG;@&.lH, 2:d7wGN0ٕOO߾}D1X_'Ed\U5$Kř۩(5HivR`HFwn$̉x2h3Ӳ>x:H>%OQq3'e uvlz#Xpt&+ 3j/ Ys呆46Q_Ѝ{ʛcaAq21"g=<{jT~l@iQcbZ[-Фbw؈=cDY#Ʈ%8\A<)v˃z?@%l]c; Tܥr5,emOOOxT#ia(!Ð$% 2uZ`Yf;/Ҝ`LR^ɸ~>EYӉaV0N',SRCyhTeo`|?E>ʞ1SdI"9DpBfD{ ˾>߉ѳE j05:b'wd)@bFN:{C?SbM4;Sk@Fe>3ʴZY=ѳ.I; ZKyM'oCl}l0"fE]i)wRlr X{7͘ L%S~OKt;9âyw炫/;HɛI46m." ix3%U'-S:%s*`--ߟѢ;Yt~߷oߔk;"gn"7! UP^=SpIҪ'ƃ$Y}N{(b)5d {G6N0z$.On. 2qdtN_C2 pl/V Hz)rA0iƠtb%g9Kw zցY,۷o-iܬ^-kM-e  pPYD̙; R_)l 31/o"I6DQl0.a ğaZW3g=1YS4]lZaמM]yMsIAI.6of,:~c )Zַ5aB i3do f'4߁Wdڻq`1i=DǔӨTgX:Y#<10FHPd5HU3"LyĜ hIصORȔѯh |\ J |Y0>2ґIyxdWF= &bZi v\zSuސr`$3{\- rS%Gx32@+Olo[SٱBϯ^9.qHKϠu5&<3c7>3%5fkemӍ&^ ޙ*(AV7vSΞ;ue35$)SL ʍAwxY} rcGb7[fZCF?-\͎SgNu HeY. &}9eWMqiQg&66uG[4i_\D VAXWfXj8 lHtʲײ Dm(#vqҴv5MDWYo=?I&nQP$&[֝d12[,K5*:;ǜXǷa']H&1SPVcMR:] gik%-ÃzxÞ45Us4x 1 s6!ā)2{ gYOg9Ip}|3ec:Y4qGgc Q5Й WҰ4#:ÉL)iXii[**ɕ E:c3-XʣAs3tĐz֯1$Ȏ =M4a%4o.%UwT -\*d^Vɝ΅i5hh,EΤi` 0.߯\uxSQ3yL(T}벎cLvf+C.Yw_t񝴕fډ)P{7dj|0q32Nf>;;ۓgΤC@j(5Q$,k$ӗO(-Yv]| q bN3 Dɨw\uB5C,خ3tryoYˈ[6ݑL)[XXpS"ND,<\5Y noS2KTtxv$ M$b3)&ɏ9tԥ'Ӎd_\\*PiϻѣqRrMdYǓ `Vޣ 7`0v#TIKL%ρ?;))5YQs^RYw!@G-+LMh an継Huj b͸`K&k\Miy3j z"9g k]ia(^gxZMڳ 0x6ּߌܵUsݷ6%FwAN?fW8LmQsQIM|wi[sy [ug;4=']vr]<?TKA5Ñtno-13=M`KRW=G\z5L6:1JZwGTnIq5xߛQm5ɆLچ%KO-IT=מD,ͩc3=hݼ) dO|;54GNfbO?AQpvO!H\Q5Jj2b4y*7 +V}gw?g|xAe΀a IfSw{v&wN<gV g] sp,fbcgcjv4oD*u6s">-X!|hsJ%#*Xiabr+c$u-WHAJ~ E`}ӿ,:ZkƎ#n35& N=#hь-Sb-OΛ>\ tKBQLJO2W@5aތsr&1%oE;Eb ff~ڦ dS~:>#rgdΡy[KgZaxxxU4"YfwTjn8#SL3g?S%2S!ulja&`Ğw^kX9r䕺Sesib&VJY/s:zsm*@s6HVđg2s(5%f$e~s5x X/jQd6N CF2Qn_><<'?-,0qxHgLdzsjN^KKXs2U,S֒'''{vڿg߆aXC~a%삌1-g*OG,zlLXf5̇y\-I3cJu,J/AnO`bƒ2RWM!\̬{n3>Ale1笜v;Js)C~՝/@KcZxR6Z^'ו hb,=;텵{||f3 7G@Aɫ?b "߾}e2~E?KCMm"r;NSueVs\iA IDAT.0s6e6"N\Y$5G jfXcFz*+͎+eptt4&YQeO]wM_sINYjw`um~N [v~?>45ˣ (?\W{خbff5N.=:' ۡ6sӟ G` L/-G oHkRd91&w4JF$ƐD:ܬv͸Al޷۷ɋ_{$,œ3Z1 m'7tGUf4HQ].wHB[z+vR~^gbےIfVԞAxd@~0s|}:ڙI+T#cXՍ9_4?VZl-sZuuu5md)Bϟ >eƕG-u4Ș54@LJ?|0f|_ |3`*54dZb)y&AXQ7Zq3}Ew&v;~a vh2#b&ƚu94_'F&!y"G8Ce`Hd9> :,(L>{\۷yΕ`dw 檳z,ӻ䆵֠vso}Q]~dVJ6蔬 6AD;ϜQ❢RlnLnKRe:'Y zxx| $KCz:^s,c2!*وgĜ[20 -a^AAm9qR' 6[*];'0C#`AyL/ijSZl}|Ky܍FۦΔRRkrQ̚F1kvҵ'K1;gn\puرMsH.|vUa4ac.tR s3Z3L:yMfyc9f]C2^v`CSux2Fud`eɔ٬˗7umnxf4%tlɚ ?"*Y)Xs{{;^|ԝ\pA''ی U,ӳkea[E$&x0;1(1Zi fi \9Ӻ+Lˌo6 rd_Y3eQc3DZj_ud&e<Wž[ym -Q|ظ/fnAڰ1\u2爽l?#E;ov#tGdkʅbd`JߓcQhoP|uRw)l6%N]d3龷mjCUğTjj,ؖdc͟%Zkw,I05F,hoD6"#64&Fԙ\b5$3n2#f.L`E76?cAgSƔXFFXw݌ Bl7مV2|5pXmFN L 3LzPv22x1@yemeL("5s|ب(f | 7 \Ox34._DiNUtr<S6j}6M 0OrvPL[Pl8==,.cp)v0Ça)I/{Vb&S)VF73M'Հֿ+eIXɸc9atw"rڲzxxnnnZ]7o~fP5Q P90;=*Pouyy91E;U+r/{::mhu\g`_Y\ w,O#[e}62~3}"|Ҷ^(Z2v:ez;\lO< K)H:Okkۑngr\;i$7b޿+_P֪4u[&۶/N?$`V'9(8לeɝш#K͙62a T^>>H3gvO~=kvI:y9gv߰R]__޽#`,NW2N03kSL\ ӭ[k957;:Wl>OFCdDdfvVv t&wƲ% G>oLŊdZ{g SLr*On]Vjc=`]'Q.~B#("Ш;"\<13(܋NI`0]86^QK:rԪr/MXbIS>HzH,"OKh  s)_Lˤ|/l.DRu'=AVt4pєG_kmKtyxxP46"kD 888kCfd?+>kf+ f$Xe6qIuߜO[ЩJBE~aXDNcӥ@(fp؉խEf sV[wtƒJ[VÛ7ox :,)O>Ǐў+|ݤʸ9aHq d9a"2\@;q=,[]3Z)$dke\{ 攲n'CEkT:_AwV{"R#[Rf Գ?::NNN&>Z۬U eo]% ܵ,;#zd+ڌ6P<cNމɖ 9Fv"efnnooK}iTyD;_fD9/b^w2V@I 5:sW;??...ԕƦ_t|/6n_jHyLn`^>*| f$ 7tIJ6 lE2JI jSg7s 4LXA|x||t0 wwwsB"VdϹtn˗/Yf /'fN۳ُseK d o!ܦ2#N6vq!fzd/jgvG8%b0 5锻L`~9?(k٭>,L[k7Mr9yf&pvv6DYl(Ꝧԑ뫺Mx?<<< F F~;L3߽h=eիWŅt*OɅsc*lCcgvc{0 v>|yn$?sOYW1 張n I)MƐ{7F1cǽQV j^~=yF_|_~~2( O{zznoo8_^^F{NlcuH2 ƿDߐYcJyBa:U圹bV+mH]///rK[-`K3}fgO(Lќ; =lJMޫ ͟R.Z 0xԦD+߼y3F,bJ/7I}X;4>wj18UfXG$js'Ӷ74K%٥f&0}:2wqϗ|g Q\'[*L:A }~z_6[!իW4''''#'~$ unB/;iAt_&A~ɹ,7،=l&ley ]/0ů.@YUrRҀCjXjy͡fEfiNJ$=܌svL߿WO<l:eoyo޼4yo߾J9wtt4C3 \C̠S RPlZ_޽{7ǣ 7իQV{s=ڵϕ[fuǏ6ʔ|*ln07FV,X |iͺ-44[:Yo>6) [1ly7YC8n]%U8 &pķ ܤ=N"䗿kIsn*i8%RirYli1׽M9țbBz7pѡ/OU@Uh`b:>݀|._mYj L[;|'Ǐ hIfj`{֬ę>??q32C;g샫QU>3gT~t:1H'pq ڈ9TuAVK,Xo2߿~;Ov;r d`R꺘te;-'''٤gb']&BF*L2ze_]cR*Ort]#~gց乁Y~Ӓ/Z?;{򶉜{룓fi|v)%fAwaMLkɐF|xx8}vsB'aLp* JC0*3# T|o 8YYY<}vB¯ ,]vѱHJz=Sme3*`|'Ӳdv4  4n;^f&S(aV_t%4F梳{߾}ڜd5U)4e#\l?g'p~NMVYך_a.ʺ7o;z"fjK ㇺ M,s1JC<;"EoucN߿tNynjceԦXb &TN.yLI2VZO:,sMxN˜jYFz3=S42heͬܐEK˗c""dA9:1*6Ң>}&Mc߹Tt3:muXO7\׭Cm,X|:<<7{|luRb1 5ߌ Jx;r&L)Q^nTA^ H۷ɜDRdI6h=7oLv_u #Aw\I~˔ߘH ]lNc|EA999P22\ ]Mکe՜;HQ,&VsCSi|qg???o޼Q"E%,%d0͘z>;D0x ~zO)Af;irxxx+\C~ f ͿgB1irJ% dVKV_k: +F'Ґͦ9*_lֲu}\NN~)$+Q]բ] S{/ IDAT鲊Ji0?YÕDXVU Y*LAb0)m=0K'} ,:muxx8<<< mʂMV쬦b6Ym.u/˃Zih"G'2`/ceˮDM[ʅk K,ܵ{IA_~Ke\=rg`H7TJwxf%YDf׀@;͢bUזEt%a96~nl5x||O|ٶ ?///CnE1 [JUM[יڳi=;Ɓ?::'یMg0^o߾8Yz/r&K 6b/l)FxZ͙N䟧kLxsDImLu|IDكgZ˙Vo XЪKQn`n PW>:e)3AA&.0~Lo T3R-%k)l$3WvRsc|I<];<1rꭥ\dߛl0e%bmXWU4<#sɬ IR6QނڑNr[|ڛ=Ɔ?qxDE'aleQ>N>?s;¨hVy@9p'?Fȅa&˥2i[f=cFeKCHY::WXG3Tl;k6mF;jGcCvª3&vp,HBcuS`ggҦ~O #PXMbjI4M%N.3?r':ns>A+d.IlX0V['lYIzӉ%J;iTm8&y*8CbK \h녓Er1c{{2`aiYFQGzʡ#̔ y6,+͵Z-ugy(TYfJTHME?20F@gfˍrRnĪjadmlpYK|5OXj$2|+͡s3먿[c'պf1ZL٘PsE9w2'qEiƩLR5 qV*>Kc([$F!CQV8|ymH3F^؏?ƀS_nLhm@*sBܠKUY.SKwʑ/, BaYbTYvb5Nf6?F*PTS߲ȇcщ5UR@SƑcLapʠE=. e@Ǐij1޿L,2,6m mL-RQH,Rps5 ˒dCL;MBJm[&@i i84mre6\9.YM]O6(ݢS+@)X> 1 %eܬکN7cVQypp0/{>SÇ{Az3[q3Pe)Ɩu6+O}r]HPnn(j7fGj`<]`ڜӗ-C,0hw!)O@{2 {;N8#f aeV~gHI<ǙqE0ʀܷojiRMM^JMf"фupg\YUkZgќ;+UMMi7SnLRX}p9+~{˒g9FeCnd=V%矉0-J 2d"Q`a Q&' `Zl*&QegY'i[`6jo'h&df}~; $V&w@"{>xs+dk&3lO5 * 0>݆q9cfΥB3ȧ;r Hh ,d\0H w"{%;fGEzQ-Z%"_DYl&$3 W" ;\<|]:β6YfL0ߛa",CX LZsv)xbHIIJSshdHŊ̒it|l1u|᝶(2xOfC0K BfGOrA)[Tnz5$TDU1x/ͬ8ds}_93=ˮ~"cL!*):Ev ВS"ּT v9Kn~NhL5q=ivґp2U6IjVle\H1uo93°nDxsQr'Y3*[F`5?kN% dW+ґ:9* }5YsKiƩ/_Q!Ɣp3FleKK@X)Ŷ`; ;v.)`L: hc #lɿ r-VP׽^'<0j33#-0;'d`fRJQnΔ]V|l9ʝe2 yŝ%iItlf<m0ԚOb^rCaf9=\t.H4lhc[^zGh1"X7#JezO4IwHP& ,,S\I|V:&{HNncZ0ي҉ֵA^S(ѻFRCDLENj<hhx|/BΔN.cd"Y#G$$ޘ X:ϿYu'?yd~ ro[cmsPobC[ιaXFiY{fH3 #,tZpv;/_())udo}e~~^z`NrT8yh$Y7Xpg0SWPeeQ GaLNНs馵Y7ͤ ՍNJ;ڂB<&]5F1ȧ1p56a $S(:%=X{QYߓɚ_}>oooϟ?OOOOׯGzfi7.e!ÍI^L]DtTfumrX$3 qq z>f3X>...=P7qe899tD~KB3يDYW\yZuƜ3N0f gvdXZ'/].+6cONca&'Β-D-s$$Sl0ue۰ˇԹ$fe"# ZPƺxLci]C& Xzz:G)Tٜ3xCuwzۦxfFAni3@%󱴾?q!fۀΘgE^})յً֮4ڴ:\Np6{f $dDҍ4dnڌ!s:m+ɓP_f\@cܐ_]kx899cɮ\HHp\[=6x%%?R;3|弅|T Yv&&r&rghZfb.ngq1]Tդ b43t^Mʸ-)mԲEfvQ6N,^z5ӦI!1%K#9z5ߏl6bɃ[iTw3!/+n8 K1gK6ͳ\tުSSš&~oN |x]d` 瞱rzzw;D krܼ|؛fL~6ׯ%[YWZ$@ `Yj8K-]CgXVIOuIm6gZkT TFz겏L#-1'ò43Wa&ȅeCW"Z=kG؎Y.vUgG!o 2,N2H/; ĩgƢf nzxr443ImNcG,\דuE 8KTm50؅@E;p]n+Tge }%ͷ,t/5dfg?^4bcaٔڸٛ7ϴfdMV *TeIOޣM[a#do`O[|xED<;!s(իIgNlڹyPC6ٙ2+􃃃QY@' y $QV672H*{DԫU6{=OEk1fr\N(2>zxxe7MZ=Lehٕ0Kw8%I*g#hY!˞H33Wf@F2j|2I6;63+ }%9܄ϸOԀ_&1%6ԃiKzec|!ӥ8[cIIEc {Jf % g0r"uFqjM*J .$X1:/y{̺?3٬My OOOz+X2Yۨ᤬#qxggg~-ѤsZbM`=GaY݃ONNu#tae![VWX9y8@IBB:慿yffB!|e\F5!eo0#z}}wJp@- ᒡFx >f}ɭg*Iw&ȓe^2T7 =7ij ,= sV24biXrӧ~`' Ht;INQ}#CgV $⨑yzKEחikݎ`uXr)el6cL\ A+˔,Hyfx/ F նʹfφ˼ i9䦫MQeGNY6;`*TTa Y]YP|q876\:&8S/([.˛Ouy.aْD*I+~gm@)KH7f’T:)2e8;;MffYZnX-(˅:ﯿKԄY W8|Vd弆:Z>ZhN 'Kߔ^ZLqɠ &Ud,e-ljMBf[mj:)8Nu}Mn|///X~=??^~=|a8???gin.ࣣ׺@D\*7YME:=]}v M'+8Z>%}Q>ݜi-OZ9 8쏲8;fHnUȠ3TƉfۄU/mnz#K9Rlr ɶ,e~:l6 }e{\qd]=F,yd@\nG<)UmY}SKob)ðySgN_^^N8L5e5NgT籎/NyyȋD۷{וdj5:a |'-Y///HEdG.pUv;ϯÍuU]gPA&LYgcaf/\dhiNBWΚ(Ai@Z$ \j5Aɹx2“a,.3C13@pww7<1)F!VlФSdڌov`a IDATۥ+W z||@Rɀk:Tۦ7:p,JLcݛF:Div`|ߴ3ay&#)Ȕ*'M`FЖ1ˀXE4 ^oZ ڵlϟޓrAeWRgvh7ԟ?>< h]e}eIj.Mcx/UfRrKe+Unl]|4\Dbu TevG2F-;3x؃@4ZE:oujeVONc$v^<<<Vhfࡅw D@@+h߇ϟ?OZKdL ~{eEYLDN 3/{8T3aIaї ]Rl#Vظ$;e3XPύW/bH#ЖʲnIr ߾}KU.o޼lI^uHH٭юSG.f2X,(ap>*>|Ƨ9>>>~8e ! MʍXrf|mGl3wu4Wa'$KEq0jf5k#+cVDkt.SIlyFx6,R Eӡ+3#nDt}RΔͦ3ȓ(s1OFJIrssf}v*`3r{nTL'T`܈U}d;͚tfI" <)5J#3EGԲι2EK:/BF4o}NpdI!LAMGHpRNIQ9f,iҹ&PV3˪5jih91e~f6UH|1dR땀)q.6,+6ugY3u1 J2]bj `ͷe#y_6, &nv >,N(Qfl6ϟG ؉Xm] ydt{KWA&($pu71==NNNRi9ʞ쎕G,|5eYu~Z\X/Ot: g4\o2xq. n^v#9_~~:qbgƲ0ώD(Wdñ0|^R $gw 8vׯu&π99%ׅ 2 O^2i8|3աP^K3˴?kXk10Ӌ9+L |Y ck*5b *>92v><|eGѐNrg+~5Qz4pi653Uv ^^^FY'kRi0qvejLzOFhK:  Wj`fK_KLa\߿bKAn^>r9VS Wwyy9q%&К(&gf>L9F)X۫F͚7 qDf5^?>>fŘ5s>OrE(l5'P#s11S22+e@(ɼ4ga1B4Y阍Yt%B׋M{ g'IJ_ɵyjכymb& 3 ׯ_WWW*)Znqlujn}OIa57Nml"2tn.aWȔ9MDbde烃͛7%9yH7J2x@){*, $e!  [.\$X"-$Cٮ)vY_xԋrsP@’\ؼyjLIT471|)K-^'ߺBsYs0j}vi5sO]3k5q%yzzsafʝMtI}\{ֶJMAw(;Q"VEZ6VꬁYi̺ȜA$w:jx*96E}³Y]B5˴3X$摘jS|lb.׌Zfkf¤|l2vؾ.EY*s*p(Kbl}-cF^xY[4c+]~t&S_;xXsWtrL_TeC<*9 ia?(0l4܏ScfPNgb $5qJU{,/<<07l\JG-A&qrsRp2=/"%CV >5Iu%txrNe4\p<92K;K;!c?cptg^ Ů]KIZӓ&BeF~t)=:rR= .}fMAϴ@̌LH&)"AƙFu8R+;V[%6_2 `LK~n׺Ln%y&[w*5|ʞ?>xQZuuuKf/_&~GGcfʌÇ{8a7u;h&yP$a&;8G2dFA:.0}IdFʗ=OF=G3bCIxzz'"(M\v:/Y a^Ç&|܈g.tbIY ePk,mMG2Ek6Aٜw?˗/;0L{MAHrCr2d/ $cb {d@i {=w[̍/+d{`ݎr\+:F ǐYǝu6$]J6Ǐ0:Ce9m R{5ޑN\#ƙ$W`iAŗP> AL;j͚2ìВ/%?4]!'ۗ8l!LgjCOfU;bKA mz?9 ZvrIAiL>5 #O:۽I.d/2ݵys =S.ԆXB)7OiN#6.:Lh ^Vnk {hRy8?? f1%Y#իWÇ4Oʶ );bv#0. ;I* #FletQ[T>'En>>>N5 mLF]0X7r|gq?;ʭe_:W$Cy{w٥eg6aZQ PXv7GUq+23z< n۴ K988" &%#AM:C dEplUe/6E8 *ģoۤ1$4$U"^m\;q0a65eLWd^W'x%F3ԕ*ĵ\A] zۭN+r3ʌ~qAuSlsg`}ݐ hq'~WIBFcNtmHC^ywwwÏ?S)&kxsx!THs2S+49djy?t`>3аq_u:ǖLN˿ t:KeHDf ξ|7l! \Fh>LsV<>>5+K8<0Qt͡YuF81߂3ZVnnmvkY 1fԵH<7\DnFќ_^^>-2 R7JmQRrSu}3&g͞2ImYm0ş Uh439\j7{nس=M Vd^$Pl;>UМNsnMׯ_OƇ d", &mD&s)"c4)b՘ E7kϟw2逢Ǩɇidƀu)TPYtusN62! z?13eJKIZ ^:M9:yrdFrf=gfckLS2jK'mA)h4b}}gKfhŪkOPǏfٓM[J&hB͍E cnf@%]l8??\a%PI6.p:05׵@3ck$~.Ft\j R PO|XD9#B'oB$t]o YkPT:v.O4vͅ#xrqцְ.sIEV材2T s Lֺź~goCF3wcrHm;|(3y]Ǭ2Q+Cm) 0&,~G/lXmV)ȗXݰlt22O#tXT&6n;BfI"~+$KZfIL˯(` 3IS:<朘XѓѸUuO2RPmVN|l&Y=(Rz-B7`d ?f ɓl!"Rc9(C aFw3✆O;;;1gXu(NKñQ6gB$kѿ2=;[w1u69ײM2\ş` =6" k/I!Tgg+p~v\JOķfExxx8hȺ8  W f\ &Z~Wħ: emm~&Ć~v:J30 ZZ*aofbLӸ12K Sٹ 2v ,~?UmOuasٚ!)c2 3"N|Nf>۴guMfTqWPM7Yzf֑֜aaY 7;l;`ka3eHSYBj4 =0I+m2]O95!x:;(3@rZ ??fĞ0_pּ4j4sUyh^u$*ZVn#1H:cƓmܤ gpYF 6 SYr-eڡjl_3Fu%2N6d_N2s(N`s\vu79q3w5uP.4*ggS8cI $'2-Ό؉VJ}dM޽i,̰ڦ:Xk7F,b59Otn|A|@Fdji[𬹈TzĹvzfxtt4MR9vJLI3SbBKa_ASmZs | rȇQ=''=Xa&uL"MkN[6qmXr&]$xh>@_6,ۭ@li1 `\E^yeM57'#v7(Pf[E5A-fbUN06$~OY~ܟ3|٥.^?5$E{vf-ɖ_]hUkӀIwnXX<=LtQE6Ն" zzzg I~ɔ&d$zaٳK{1/9ڜfRJ୛ `-ۆls %@f IDATmN%6l)e+K1ד13RN$sa:>ݔر~c\N-8,OL'9^t'miϳzM eչ"`;.0jj\_x*5p3kN;mf)MJlUOO )gw>ON˺7a^ا l8Q$?jo;lN 20&nsS 6hN`B|<2;5; (g4LY2$Sp >U$fAYyΥM,wd)'OfOS&֞w :ПWf>Rn]zߟ"d2PU- Dl02rTv¢?ZNOO=)9mz.c9&trSZﷲf3AM=F,~qJeG#<,ĉ=lA2SH}dE'j")ݜiAğ_3k\5&:FN}߿C2J930Kr*̻dFr"2Ѱm<={jL@B^4B0}8(_A|D9U/~5QǕ2tign>,]n+2}fH~Y,ިqj keY}l{"4_ffd'ZKB3.#='R)tXOiX !,M(F$蜙 L^Ոm6arD 'iFEtac}%w#%Jdm?3` 2U:l'7Fz tS30Tښz?3ELh '$GY!XjJ;M[ AJt: /IV&ׂ@㞩kr㪲0Ze0vpP&A0\x!268Uk_"$$Uus?ߏƦRRDvs$jj5xDdY'Qv٬%Ƹ]̬g0-n Tq2Si 04$L LP&ΡD:-(/7jD~'i]IE҉(K2d,'v8~oue}[t:)h΋P'`KnݓO+@lXE#9g Dd&@ 0f^8DTe$d ;Sb dY N W: 5dn*l +P͔2Ԫ![b&Qw67%ujn)-]qy]Jjx 7|8ZE% U,I^kn+ve9чkl$*${3V bITu#gցFFqd4oYJ؀hѓ1I9 8UVA-A&gfU>&kbӌ]2NeUq JK)K85u*g&ٽ5|Տyׯ_o|qg}駩?IY׿YYYf//{eYp%mYŋҴpаK}>q*i,`R)2qM˜k_o5j󗆟59+^ ~AV\YTI~`Q:WW ǴS LT)K_u]Wlӓy]NF{kINo7vib3a)\B|dflj *=J{hF8FONze'-*=0 >d]O3Ȓ.otsIߞ׸u&Cxx>_֒/j~zJ[5<%).3lzL}ݧ؆zFQ}6c9i1f"N?aSrI&&ӣ1?H~ g.T\m6'w\|"(fJt} rފ2ܘhM2rym @K~j𹽽M~5[ &AN4&^>c/rS b8CØ'bĵyմp]6uO][-Ӈj"j4~Oc\ejٳ=5}SR`[]\c'/n?ܐ4g (F ~g)'cB,܎W5 Ge`%#p5Lr  !R]3aH vo~!=!szΘFxE^q~l\`8RΈUOܙF\ͱ+ @#)OYrUGZ̜GngWWWyj{Vo* SQS4&CFsMxl-3MbuS% ks>̅I/0Im2^/7?.Mqgyr^6Ƥ줯Ϲ;}ςbxcB<0 sOeIʠT'&Ws8~0v<̜Nr "w޷Rwۇ,ũ p+%TS̍^?9Vcmӕ^:(wy \Xw{3,s7ULde_&D0ks jЫ<իW"f\⏳SI}e/Mz-zS=+Tc1HoV.X9.ܼTӘQmLTԨt5m5$ #ϵdOP&aI'3wL҆d%7G˩F0Q)r}V)Ě{lTR-~id f???М :Xh).:+eǧG9aqS`Û:!]L'I.&$iaq}S`AIf8Q^˥[ABߓRd[J8'0T{Y+;xh^:T&j̢Rb]odt LCcplZ&}UY'4#k0ɋO)bŷ}1\KSKh)K) L-еH-feT E oj@QlArsOY$eVE6jg61ޖbĔʟ}rf-)p\x'/jB1?͌_@Uq0'`1-TNps鸞B:Z' `SnUkO_Y)2tIoĸ]V7Ϋ>S|wwww#ĻMe}~~ul5,Zfa#)LlJLoP_egi7fό0ՠMMJ`BUV L?Vˀ_LA[3TӢ+44XzrD}/sP(?{/^ݛ7oՍz :}ﴼO^; IENDB`./share/qtcreator/templates/wizards/ubuntu/share/0000755000015600001650000000000012705421114022305 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/share/displayName.apparmor0000644000015600001650000000017012705421114026314 0ustar jenkinsjenkins{ "policy_groups": [ "networking", "webview" ], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/share/.excludes0000644000015600001650000000003412705421114024117 0ustar jenkinsjenkinsMakefile *.tmp .bzr .git po ./share/qtcreator/templates/wizards/ubuntu/share/ubuntu.png0000644000015600001650000000407512705421114024343 0ustar jenkinsjenkinsPNG  IHDR szzsRGBbKGD pHYs B(xtIME -sIDATXÕilTl6 M &%PHPlC 4QԪBiK$ЪK6UKTC4j%Meq68RD((cl ^ƞwTyg _B ˀBZ %jLi}a]˿K XRxLY[!EuhrbvdoOuѵ^y}>0 (~jg7S 5b o) {͡TȮ74BݶF8ӗ!h_@Uq?+K3G"ûGB׮u$6 @>B{W姸!sP0_݌). Ϟ<72{D iz@:iO q׏k"ٴ P77KDDKSDn Q M tU= MN1#C`֯aUyn7ُ78@ٚwsc`gc1,RVw$O$+Ґ*&%Λy;7|1l{ -!Q"so7277G}7&-2*q(b۸4|$R C1D{toYK$SL͗E% .>gk7z {Pi"0~t|p,ݝ8UV`X? *l@U5/g~k5 ("IeHQG?Tϋ$H 8PVI0U* 3mn %f#EQ=hyZgQ`۵[ot,Xl&sYM즥={1NB,5u/S$oD1rHU.CZK##`NL<&&/Kja_&GW- GJοr"Y)&Vz8z[f -dʵR]WŠ:,m؝?gæ}nƽo#>/iEsָ;d9/;Lhޞs Lt|2jQ3mX,Wo0W霷w``*$-ͶRՌIENDB`./share/qtcreator/templates/wizards/ubuntu/share/manifest.json0000644000015600001650000000065412705421114025013 0ustar jenkinsjenkins{ "name": "%ProjectName:l%.%ClickDomain:l%", "description": "description of %ProjectName%", "architecture": "all", "title": "%ProjectName%", "hooks": { "%ClickHookName%": { "apparmor": "%ClickHookName%.apparmor", "desktop": "%ClickHookName%.desktop" } }, "version": "0.1", "maintainer": "%ClickMaintainer%", "framework" : "%ClickFrameworkVersion%" } ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/0000755000015600001650000000000012705421114024327 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/wizard.xml0000644000015600001650000001005512705421114026352 0ustar jenkinsjenkins ../share/ubuntu.png A simple QML app with localization support. Also includes unit and functional tests. QML App with Simple UI (cmake) Ubuntu Click package parameters Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/manifest.json.in0000644000015600001650000000063312705421114027437 0ustar jenkinsjenkins{ "name": "@APP_ID@", "description": "description of %ProjectName%", "architecture": "@CLICK_ARCH@", "title": "@APP_NAME@", "hooks": { "%ClickHookName%": { "apparmor": "%ClickHookName%.apparmor", "desktop": "%ClickHookName%.desktop" } }, "version": "0.1", "maintainer": "%ClickMaintainer%", "framework" : "%ClickFrameworkVersion%" } ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/0000755000015600001650000000000012705421114025107 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/displayName.desktop.in0000644000015600001650000000022112705421114031350 0ustar jenkinsjenkins[Desktop Entry] _Name=%ProjectName% Comment=My project description Exec=@EXEC@ Icon=@ICON@ Terminal=false Type=Application X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/0000755000015600001650000000000012705421114026251 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/0000755000015600001650000000000012705421114030271 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/run0000755000015600001650000000027412705421114031026 0ustar jenkinsjenkins#!/bin/bash if [[ -z `which autopilot3` ]]; then echo "python3-autopilot is not installed. Skip" exit fi SCRIPTPATH=`dirname $0` pushd ${SCRIPTPATH} autopilot3 run %ProjectName% popd./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/0000755000015600001650000000000012705421114032537 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/__init__.py./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/__init_0000644000015600001650000000205312705421114034062 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Application autopilot helpers.""" import logging import ubuntuuitoolkit logger = logging.getLogger(__name__) class AppException(ubuntuuitoolkit.ToolkitException): """Exception raised when there are problems with the Application.""" class TouchApp(object): """Autopilot helper object for the application.""" def __init__(self, app_proxy, test_type): self.app = app_proxy self.test_type = test_type self.main_view = self.app.select_single(MainView) @property def pointing_device(self): return self.app.pointing_device class MainView(ubuntuuitoolkit.MainView): """A helper that makes it easy to interact with the mainview""" def __init__(self, *args): super(MainView, self).__init__(*args) self.visible.wait_for(True, 30) def get_button(self): return self.select_single('Button', objectName="button") def get_label(self): return self.select_single('Label', objectName="label")./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/tests/0000755000015600001650000000000012705421114033701 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015700000000000011606 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/tests/__init__.py./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/tests/_0000644000015600001650000000324612705421114034047 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """Ubuntu Touch App Autopilot tests.""" import os import logging import %ProjectName% from autopilot.testcase import AutopilotTestCase from autopilot import logging as autopilot_logging import ubuntuuitoolkit from ubuntuuitoolkit import base logger = logging.getLogger(__name__) class BaseTestCase(AutopilotTestCase): """A common test case class """ local_location = os.path.dirname(os.path.dirname(os.getcwd())) local_location_qml = os.path.join(local_location, 'Main.qml') click_package = '{0}.{1}'.format('%ProjectName%', '%ClickDomain%') def setUp(self): super(BaseTestCase, self).setUp() self.launcher, self.test_type = self.get_launcher_and_type() self.app = %ProjectName%.TouchApp(self.launcher(), self.test_type) def get_launcher_and_type(self): if os.path.exists(self.local_location_qml): launcher = self.launch_test_local test_type = 'local' else: launcher = self.launch_test_click test_type = 'click' return launcher, test_type @autopilot_logging.log_action(logger.info) def launch_test_local(self): return self.launch_test_application( base.get_qmlscene_launch_command(), self.local_location_qml, app_type='qt', emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase) @autopilot_logging.log_action(logger.info) def launch_test_click(self): return self.launch_click_package( self.click_package, emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)././@LongLink0000644000000000000000000000016000000000000011600 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/tests/test_main.py./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/autopilot/displayName/tests/t0000644000015600001650000000124412705421114034070 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- from autopilot.matchers import Eventually from testtools.matchers import Equals from %ProjectName% import tests class MainViewTestCase(tests.BaseTestCase): """Tests for the mainview""" def setUp(self): super(MainViewTestCase, self).setUp() def test_click_button(self): # Find and click the button button = self.app.main_view.get_button() self.app.pointing_device.click_object(button) # Make an assertion about what should happen label = self.app.main_view.get_label() self.assertThat(label.text, Eventually(Equals('..world!')))./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/unit/0000755000015600001650000000000012705421114027230 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/tests/unit/tst_main.qml0000644000015600001650000000220712705421114031562 0ustar jenkinsjenkinsimport QtQuick 2.4 import QtTest 1.0 import Ubuntu.Test 1.0 import "../../" // See more details at https://developer.ubuntu.com/api/qml/sdk-14.10/Ubuntu.Test.UbuntuTestCase // Execute these tests with: // qmltestrunner Item { width: units.gu(100) height: units.gu(75) // The objects Main { id: main } UbuntuTestCase { name: "MainTestCase" when: windowShown function init() { var label = findChild(main, "label"); // See the compare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#compare-method compare("Hello..", label.text); } function test_clickButtonMustChangeLabel() { var button = findChild(main, "button"); var buttonCenter = centerOf(button) mouseClick(button, buttonCenter.x, buttonCenter.y); var label = findChild(main, "label"); // See the tryCompare method documentation at https://developer.ubuntu.com/api/qml/sdk-14.10/QtTest.TestCase/#tryCompare-method tryCompare(label, "text", "..world!", 1); } } } ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/Main.qml0000644000015600001650000000260212705421114026506 0ustar jenkinsjenkinsimport QtQuick 2.4 import Ubuntu.Components 1.3 /*! \brief MainView with a Label and Button elements. */ MainView { // objectName for functional testing purposes (autopilot-qt5) objectName: "mainView" // Note! applicationName needs to match the "name" field of the click manifest applicationName: "%ProjectName:l%.%ClickDomain:l%" width: units.gu(100) height: units.gu(75) Page { header: PageHeader { id: pageHeader title: i18n.tr("%ClickHookName%") StyleHints { foregroundColor: UbuntuColors.orange backgroundColor: UbuntuColors.porcelain dividerColor: UbuntuColors.slate } } Label { id: label objectName: "label" anchors { horizontalCenter: parent.horizontalCenter top: pageHeader.bottom topMargin: units.gu(2) } text: i18n.tr("Hello..") } Button { objectName: "button" anchors { horizontalCenter: parent.horizontalCenter top: label.bottom topMargin: units.gu(2) } width: parent.width text: i18n.tr("Tap me!") onClicked: { label.text = i18n.tr("..world!") } } } } ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/CMakeLists.txt0000644000015600001650000000174412705421114027655 0ustar jenkinsjenkins# Searches for all qml and javascript files in the current directory # to add them to the project, if you add new files in the directory # rerun cmake to make sure they show up in the project tree file(GLOB QML_JS_FILES *.qml *.js) # Make the files visible in qtcreator add_custom_target(%ProjectName:l%_QMlFiles ALL SOURCES ${QML_JS_FILES}) # Substitute variables in the desktop file configure_file(${DESKTOP_FILE_NAME}.in ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME}.in) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME} DESTINATION ${DESKTOP_DIR}) install(FILES ${QML_JS_FILES} DESTINATION ${%ProjectName:u%_DIR}) # Make the autpilot files visible in qtcreator file(GLOB_RECURSE AUTOPILOT_TEST_FILES *.py) add_custom_target(%ProjectName:l%_AutopilotFiles ALL SOURCES ${AUTOPILOT_TEST_FILES}) # Make the qml test files visible in qtcreator file(GLOB_RECURSE UNIT_TEST_FILES tests/unit/*.qml) add_custom_target(%ProjectName:l%_UnitTestFiles ALL SOURCES ${UNIT_TEST_FILES}) ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/graphics/0000755000015600001650000000000012705421114026707 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/app/graphics/toolbarIcon@8.png0000644000015600001650000000443612705421114032067 0ustar jenkinsjenkinsPNG  IHDR$$tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp -NIDATxڤXIO:t2vCH! BB\י0Yj:,$c.WW/&H薾q."ʲ̡ߎAt:wa,Ǡ%I2HB<#Ad"dzVŻ]>Ӝ; 3@cAˀe`lü;orfprp]9waf4}Ӵ\+;H#k}L"L8Ƃ~ي|K57fAxf vʊuSSS29DfQ??9ZZ$@r?lx'dkkˍkڱg QdSd@?Hh,6'yw ߂A[ZZ VrnjlT̊U]MQQs`½=fEs'r༁YB_6XP榛A_زuȑ jV3̀.//I1o 6h! 0k LY 4ic^̖Ea]cMӉewnG Q ykp=&n!qr#b 5dM&S;~&33Y[k뛞o6]/Ԙd%A޶t٦V RYj:=|!߼ Q܌U׾!h: JSlMLP*> R߅eɌEb6#YYe=ǵ } Z4#c;/溘tȔG+~$1N>ҵ/C TBNOOU_\rYA2LfKJDmsXxqtK&p&ѢՉP;]2[('/KJ'𸟚 q'b\e2wp `, SQv 0eK<נ =Aʣq9F_4tCgJJg8} q$2U{^P `ۭi;H\&x4s@1yEpKI w/[]]lXxx㣯zؤ'WM 4gmll]=9oQ-O#p|{@#7bgf|H;j}I6WnFșufpgUޅmԼq;V<9N2s%c|oVMNZԫjHO^x'k#@d=SEkuMeϝON.ffykd뢴kas4O]COOO8y=ڂ5 87;N3ǬVa@r׉;S}f|.2gSה.p{ Ùxݷ;n8 Y*d}-3 `iɑ k|1<==MdyuzͅXiys'HIrSdYZYו7+33a‚3h b[??+̃RϽVevo;߱ӢKxsCZM}^=Bs扚'~FLxZîZr.n{tt4sԿe0V;z/'⺟|O= tl%3&fL*da epfFk$nc8אi<y][Vi3=3A}zAҧHICT_f)꿹 '/n"Zk/_t>67q R,s^SWY=b$trn=C+bJn Olfu+@@9YfT\|~ @)]m~ZvQEZ$D6a899|߹3Z/ɇ0nlNk0A5~>|Y/Ȏw.]3L0R!B̀e_=~sml6lr]0.q !ZJ{yɻawחˊ*LϲDW-,櫛'oi$)6#=l)憪NxhVb^_CX$G!W-{ `| A!{ٽk`*OGdpz '/!Lsst!g9lIZZ^oq[2զ@6v noowbl(>dޑx` {<>>].?Kd},NZN;Fzd.2eg͑vxr-i2gGnDv9X 3;07sn fBN)1?3e\k<=dy``krp$t Zw 6dEq||<{"TycD)f1#)6yF*,)eXO&K Fkw2e&%6= 7{ k[93׫& dpdG;@&.lH, 2:d7wGN0ٕOO߾}D1X_'Ed\U5$Kř۩(5HivR`HFwn$̉x2h3Ӳ>x:H>%OQq3'e uvlz#Xpt&+ 3j/ Ys呆46Q_Ѝ{ʛcaAq21"g=<{jT~l@iQcbZ[-Фbw؈=cDY#Ʈ%8\A<)v˃z?@%l]c; Tܥr5,emOOOxT#ia(!Ð$% 2uZ`Yf;/Ҝ`LR^ɸ~>EYӉaV0N',SRCyhTeo`|?E>ʞ1SdI"9DpBfD{ ˾>߉ѳE j05:b'wd)@bFN:{C?SbM4;Sk@Fe>3ʴZY=ѳ.I; ZKyM'oCl}l0"fE]i)wRlr X{7͘ L%S~OKt;9âyw炫/;HɛI46m." ix3%U'-S:%s*`--ߟѢ;Yt~߷oߔk;"gn"7! UP^=SpIҪ'ƃ$Y}N{(b)5d {G6N0z$.On. 2qdtN_C2 pl/V Hz)rA0iƠtb%g9Kw zցY,۷o-iܬ^-kM-e  pPYD̙; R_)l 31/o"I6DQl0.a ğaZW3g=1YS4]lZaמM]yMsIAI.6of,:~c )Zַ5aB i3do f'4߁Wdڻq`1i=DǔӨTgX:Y#<10FHPd5HU3"LyĜ hIصORȔѯh |\ J |Y0>2ґIyxdWF= &bZi v\zSuސr`$3{\- rS%Gx32@+Olo[SٱBϯ^9.qHKϠu5&<3c7>3%5fkemӍ&^ ޙ*(AV7vSΞ;ue35$)SL ʍAwxY} rcGb7[fZCF?-\͎SgNu HeY. &}9eWMqiQg&66uG[4i_\D VAXWfXj8 lHtʲײ Dm(#vqҴv5MDWYo=?I&nQP$&[֝d12[,K5*:;ǜXǷa']H&1SPVcMR:] gik%-ÃzxÞ45Us4x 1 s6!ā)2{ gYOg9Ip}|3ec:Y4qGgc Q5Й WҰ4#:ÉL)iXii[**ɕ E:c3-XʣAs3tĐz֯1$Ȏ =M4a%4o.%UwT -\*d^Vɝ΅i5hh,EΤi` 0.߯\uxSQ3yL(T}벎cLvf+C.Yw_t񝴕fډ)P{7dj|0q32Nf>;;ۓgΤC@j(5Q$,k$ӗO(-Yv]| q bN3 Dɨw\uB5C,خ3tryoYˈ[6ݑL)[XXpS"ND,<\5Y noS2KTtxv$ M$b3)&ɏ9tԥ'Ӎd_\\*PiϻѣqRrMdYǓ `Vޣ 7`0v#TIKL%ρ?;))5YQs^RYw!@G-+LMh an継Huj b͸`K&k\Miy3j z"9g k]ia(^gxZMڳ 0x6ּߌܵUsݷ6%FwAN?fW8LmQsQIM|wi[sy [ug;4=']vr]<?TKA5Ñtno-13=M`KRW=G\z5L6:1JZwGTnIq5xߛQm5ɆLچ%KO-IT=מD,ͩc3=hݼ) dO|;54GNfbO?AQpvO!H\Q5Jj2b4y*7 +V}gw?g|xAe΀a IfSw{v&wN<gV g] sp,fbcgcjv4oD*u6s">-X!|hsJ%#*Xiabr+c$u-WHAJ~ E`}ӿ,:ZkƎ#n35& N=#hь-Sb-OΛ>\ tKBQLJO2W@5aތsr&1%oE;Eb ff~ڦ dS~:>#rgdΡy[KgZaxxxU4"YfwTjn8#SL3g?S%2S!ulja&`Ğw^kX9r䕺Sesib&VJY/s:zsm*@s6HVđg2s(5%f$e~s5x X/jQd6N CF2Qn_><<'?-,0qxHgLdzsjN^KKXs2U,S֒'''{vڿg߆aXC~a%삌1-g*OG,zlLXf5̇y\-I3cJu,J/AnO`bƒ2RWM!\̬{n3>Ale1笜v;Js)C~՝/@KcZxR6Z^'ו hb,=;텵{||f3 7G@Aɫ?b "߾}e2~E?KCMm"r;NSueVs\iA IDAT.0s6e6"N\Y$5G jfXcFz*+͎+eptt4&YQeO]wM_sINYjw`um~N [v~?>45ˣ (?\W{خbff5N.=:' ۡ6sӟ G` L/-G oHkRd91&w4JF$ƐD:ܬv͸Al޷۷ɋ_{$,œ3Z1 m'7tGUf4HQ].wHB[z+vR~^gbےIfVԞAxd@~0s|}:ڙI+T#cXՍ9_4?VZl-sZuuu5md)Bϟ >eƕG-u4Ș54@LJ?|0f|_ |3`*54dZb)y&AXQ7Zq3}Ew&v;~a vh2#b&ƚu94_'F&!y"G8Ce`Hd9> :,(L>{\۷yΕ`dw 檳z,ӻ䆵֠vso}Q]~dVJ6蔬 6AD;ϜQ❢RlnLnKRe:'Y zxx| $KCz:^s,c2!*وgĜ[20 -a^AAm9qR' 6[*];'0C#`AyL/ijSZl}|Ky܍FۦΔRRkrQ̚F1kvҵ'K1;gn\puرMsH.|vUa4ac.tR s3Z3L:yMfyc9f]C2^v`CSux2Fud`eɔ٬˗7umnxf4%tlɚ ?"*Y)Xs{{;^|ԝ\pA''ی U,ӳkea[E$&x0;1(1Zi fi \9Ӻ+Lˌo6 rd_Y3eQc3DZj_ud&e<Wž[ym -Q|ظ/fnAڰ1\u2爽l?#E;ov#tGdkʅbd`JߓcQhoP|uRw)l6%N]d3龷mjCUğTjj,ؖdc͟%Zkw,I05F,hoD6"#64&Fԙ\b5$3n2#f.L`E76?cAgSƔXFFXw݌ Bl7مV2|5pXmFN L 3LzPv22x1@yemeL("5s|ب(f | 7 \Ox34._DiNUtr<S6j}6M 0OrvPL[Pl8==,.cp)v0Ça)I/{Vb&S)VF73M'Հֿ+eIXɸc9atw"rڲzxxnnnZ]7o~fP5Q P90;=*Pouyy91E;U+r/{::mhu\g`_Y\ w,O#[e}62~3}"|Ҷ^(Z2v:ez;\lO< K)H:Okkۑngr\;i$7b޿+_P֪4u[&۶/N?$`V'9(8לeɝш#K͙62a T^>>H3gvO~=kvI:y9gv߰R]__޽#`,NW2N03kSL\ ӭ[k957;:Wl>OFCdDdfvVv t&wƲ% G>oLŊdZ{g SLr*On]Vjc=`]'Q.~B#("Ш;"\<13(܋NI`0]86^QK:rԪr/MXbIS>HzH,"OKh  s)_Lˤ|/l.DRu'=AVt4pєG_kmKtyxxP46"kD 888kCfd?+>kf+ f$Xe6qIuߜO[ЩJBE~aXDNcӥ@(fp؉խEf sV[wtƒJ[VÛ7ox :,)O>Ǐў+|ݤʸ9aHq d9a"2\@;q=,[]3Z)$dke\{ 攲n'CEkT:_AwV{"R#[Rf Գ?::NNN&>Z۬U eo]% ܵ,;#zd+ڌ6P<cNމɖ 9Fv"efnnooK}iTyD;_fD9/b^w2V@I 5:sW;??...ԕƦ_t|/6n_jHyLn`^>*| f$ 7tIJ6 lE2JI jSg7s 4LXA|x||t0 wwwsB"VdϹtn˗/Yf /'fN۳ُseK d o!ܦ2#N6vq!fzd/jgvG8%b0 5锻L`~9?(k٭>,L[k7Mr9yf&pvv6DYl(Ꝧԑ뫺Mx?<<< F F~;L3߽h=eիWŅt*OɅsc*lCcgvc{0 v>|yn$?sOYW1 張n I)MƐ{7F1cǽQV j^~=yF_|_~~2( O{zznoo8_^^F{NlcuH2 ƿDߐYcJyBa:U圹bV+mH]///rK[-`K3}fgO(Lќ; =lJMޫ ͟R.Z 0xԦD+߼y3F,bJ/7I}X;4>wj18UfXG$js'Ӷ74K%٥f&0}:2wqϗ|g Q\'[*L:A }~z_6[!իW4''''#'~$ unB/;iAt_&A~ɹ,7،=l&ley ]/0ů.@YUrRҀCjXjy͡fEfiNJ$=܌svL߿WO<l:eoyo޼4yo߾J9wtt4C3 \C̠S RPlZ_޽{7ǣ 7իQV{s=ڵϕ[fuǏ6ʔ|*ln07FV,X |iͺ-44[:Yo>6) [1ly7YC8n]%U8 &pķ ܤ=N"䗿kIsn*i8%RirYli1׽M9țbBz7pѡ/OU@Uh`b:>݀|._mYj L[;|'Ǐ hIfj`{֬ę>??q32C;g샫QU>3gT~t:1H'pq ڈ9TuAVK,Xo2߿~;Ov;r d`R꺘te;-'''٤gb']&BF*L2ze_]cR*Ort]#~gց乁Y~Ӓ/Z?;{򶉜{룓fi|v)%fAwaMLkɐF|xx8}vsB'aLp* JC0*3# T|o 8YYY<}vB¯ ,]vѱHJz=Sme3*`|'Ӳdv4  4n;^f&S(aV_t%4F梳{߾}ڜd5U)4e#\l?g'p~NMVYך_a.ʺ7o;z"fjK ㇺ M,s1JC<;"EoucN߿tNynjceԦXb &TN.yLI2VZO:,sMxN˜jYFz3=S42heͬܐEK˗c""dA9:1*6Ң>}&Mc߹Tt3:muXO7\׭Cm,X|:<<7{|luRb1 5ߌ Jx;r&L)Q^nTA^ H۷ɜDRdI6h=7oLv_u #Aw\I~˔ߘH ]lNc|EA999P22\ ]Mکe՜;HQ,&VsCSi|qg???o޼Q"E%,%d0͘z>;D0x ~zO)Af;irxxx+\C~ f ͿgB1irJ% dVKV_k: +F'Ґͦ9*_lֲu}\NN~)$+Q]բ] S{/ IDAT鲊Ji0?YÕDXVU Y*LAb0)m=0K'} ,:muxx8<<< mʂMV쬦b6Ym.u/˃Zih"G'2`/ceˮDM[ʅk K,ܵ{IA_~Ke\=rg`H7TJwxf%YDf׀@;͢bUזEt%a96~nl5x||O|ٶ ?///CnE1 [JUM[יڳi=;Ɓ?::'یMg0^o߾8Yz/r&K 6b/l)FxZ͙N䟧kLxsDImLu|IDكgZ˙Vo XЪKQn`n PW>:e)3AA&.0~Lo T3R-%k)l$3WvRsc|I<];<1rꭥ\dߛl0e%bmXWU4<#sɬ IR6QނڑNr[|ڛ=Ɔ?qxDE'aleQ>N>?s;¨hVy@9p'?Fȅa&˥2i[f=cFeKCHY::WXG3Tl;k6mF;jGcCvª3&vp,HBcuS`ggҦ~O #PXMbjI4M%N.3?r':ns>A+d.IlX0V['lYIzӉ%J;iTm8&y*8CbK \h녓Er1c{{2`aiYFQGzʡ#̔ y6,+͵Z-ugy(TYfJTHME?20F@gfˍrRnĪjadmlpYK|5OXj$2|+͡s3먿[c'պf1ZL٘PsE9w2'qEiƩLR5 qV*>Kc([$F!CQV8|ymH3F^؏?ƀS_nLhm@*sBܠKUY.SKwʑ/, BaYbTYvb5Nf6?F*PTS߲ȇcщ5UR@SƑcLapʠE=. e@Ǐij1޿L,2,6m mL-RQH,Rps5 ˒dCL;MBJm[&@i i84mre6\9.YM]O6(ݢS+@)X> 1 %eܬکN7cVQypp0/{>SÇ{Az3[q3Pe)Ɩu6+O}r]HPnn(j7fGj`<]`ڜӗ-C,0hw!)O@{2 {;N8#f aeV~gHI<ǙqE0ʀܷojiRMM^JMf"фupg\YUkZgќ;+UMMi7SnLRX}p9+~{˒g9FeCnd=V%矉0-J 2d"Q`a Q&' `Zl*&QegY'i[`6jo'h&df}~; $V&w@"{>xs+dk&3lO5 * 0>݆q9cfΥB3ȧ;r Hh ,d\0H w"{%;fGEzQ-Z%"_DYl&$3 W" ;\<|]:β6YfL0ߛa",CX LZsv)xbHIIJSshdHŊ̒it|l1u|᝶(2xOfC0K BfGOrA)[Tnz5$TDU1x/ͬ8ds}_93=ˮ~"cL!*):Ev ВS"ּT v9Kn~NhL5q=ivґp2U6IjVle\H1uo93°nDxsQr'Y3*[F`5?kN% dW+ґ:9* }5YsKiƩ/_Q!Ɣp3FleKK@X)Ŷ`; ;v.)`L: hc #lɿ r-VP׽^'<0j33#-0;'d`fRJQnΔ]V|l9ʝe2 yŝ%iItlf<m0ԚOb^rCaf9=\t.H4lhc[^zGh1"X7#JezO4IwHP& ,,S\I|V:&{HNncZ0ي҉ֵA^S(ѻFRCDLENj<hhx|/BΔN.cd"Y#G$$ޘ X:ϿYu'?yd~ ro[cmsPobC[ιaXFiY{fH3 #,tZpv;/_())udo}e~~^z`NrT8yh$Y7Xpg0SWPeeQ GaLNНs馵Y7ͤ ՍNJ;ڂB<&]5F1ȧ1p56a $S(:%=X{QYߓɚ_}>oooϟ?OOOOׯGzfi7.e!ÍI^L]DtTfumrX$3 qq z>f3X>...=P7qe899tD~KB3يDYW\yZuƜ3N0f gvdXZ'/].+6cONca&'Β-D-s$$Sl0ue۰ˇԹ$fe"# ZPƺxLci]C& Xzz:G)Tٜ3xCuwzۦxfFAni3@%󱴾?q!fۀΘgE^})յً֮4ڴ:\Np6{f $dDҍ4dnڌ!s:m+ɓP_f\@cܐ_]kx899cɮ\HHp\[=6x%%?R;3|弅|T Yv&&r&rghZfb.ngq1]Tդ b43t^Mʸ-)mԲEfvQ6N,^z5ӦI!1%K#9z5ߏl6bɃ[iTw3!/+n8 K1gK6ͳ\tުSSš&~oN |x]d` 瞱rzzw;D krܼ|؛fL~6ׯ%[YWZ$@ `Yj8K-]CgXVIOuIm6gZkT TFz겏L#-1'ò43Wa&ȅeCW"Z=kG؎Y.vUgG!o 2,N2H/; ĩgƢf nzxr443ImNcG,\דuE 8KTm50؅@E;p]n+Tge }%ͷ,t/5dfg?^4bcaٔڸٛ7ϴfdMV *TeIOޣM[a#do`O[|xED<;!s(իIgNlڹyPC6ٙ2+􃃃QY@' y $QV672H*{DԫU6{=OEk1fr\N(2>zxxe7MZ=Lehٕ0Kw8%I*g#hY!˞H33Wf@F2j|2I6;63+ }%9܄ϸOԀ_&1%6ԃiKzec|!ӥ8[cIIEc {Jf % g0r"uFqjM*J .$X1:/y{̺?3٬My OOOz+X2Yۨ᤬#qxggg~-ѤsZbM`=GaY݃ONNu#tae![VWX9y8@IBB:慿yffB!|e\F5!eo0#z}}wJp@- ᒡFx >f}ɭg*Iw&ȓe^2T7 =7ij ,= sV24biXrӧ~`' Ht;INQ}#CgV $⨑yzKEחikݎ`uXr)el6cL\ A+˔,Hyfx/ F նʹfφ˼ i9䦫MQeGNY6;`*TTa Y]YP|q876\:&8S/([.˛Ouy.aْD*I+~gm@)KH7f’T:)2e8;;MffYZnX-(˅:ﯿKԄY W8|Vd弆:Z>ZhN 'Kߔ^ZLqɠ &Ud,e-ljMBf[mj:)8Nu}Mn|///X~=??^~=|a8???gin.ࣣ׺@D\*7YME:=]}v M'+8Z>%}Q>ݜi-OZ9 8쏲8;fHnUȠ3TƉfۄU/mnz#K9Rlr ɶ,e~:l6 }e{\qd]=F,yd@\nG<)UmY}SKob)ðySgN_^^N8L5e5NgT籎/NyyȋD۷{וdj5:a |'-Y///HEdG.pUv;ϯÍuU]gPA&LYgcaf/\dhiNBWΚ(Ai@Z$ \j5Aɹx2“a,.3C13@pww7<1)F!VlФSdڌov`a IDATۥ+W z||@Rɀk:Tۦ7:p,JLcݛF:Div`|ߴ3ay&#)Ȕ*'M`FЖ1ˀXE4 ^oZ ڵlϟޓrAeWRgvh7ԟ?>< h]e}eIj.Mcx/UfRrKe+Unl]|4\Dbu TevG2F-;3x؃@4ZE:oujeVONc$v^<<<Vhfࡅw D@@+h߇ϟ?OZKdL ~{eEYLDN 3/{8T3aIaї ]Rl#Vظ$;e3XPύW/bH#ЖʲnIr ߾}KU.o޼lI^uHH٭юSG.f2X,(ap>*>|Ƨ9>>>~8e ! MʍXrf|mGl3wu4Wa'$KEq0jf5k#+cVDkt.SIlyFx6,R Eӡ+3#nDt}RΔͦ3ȓ(s1OFJIrssf}v*`3r{nTL'T`܈U}d;͚tfI" <)5J#3EGԲι2EK:/BF4o}NpdI!LAMGHpRNIQ9f,iҹ&PV3˪5jih91e~f6UH|1dR땀)q.6,+6ugY3u1 J2]bj `ͷe#y_6, &nv >,N(Qfl6ϟG ؉Xm] ydt{KWA&($pu71==NNNRi9ʞ쎕G,|5eYu~Z\X/Ot: g4\o2xq. n^v#9_~~:qbgƲ0ώD(Wdñ0|^R $gw 8vׯu&π99%ׅ 2 O^2i8|3աP^K3˴?kXk10Ӌ9+L |Y ck*5b *>92v><|eGѐNrg+~5Qz4pi653Uv ^^^FY'kRi0qvejLzOFhK:  Wj`fK_KLa\߿bKAn^>r9VS Wwyy9q%&К(&gf>L9F)X۫F͚7 qDf5^?>>fŘ5s>OrE(l5'P#s11S22+e@(ɼ4ga1B4Y阍Yt%B׋M{ g'IJ_ɵyjכymb& 3 ׯ_WWW*)Znqlujn}OIa57Nml"2tn.aWȔ9MDbde烃͛7%9yH7J2x@){*, $e!  [.\$X"-$Cٮ)vY_xԋrsP@’\ؼyjLIT471|)K-^'ߺBsYs0j}vi5sO]3k5q%yzzsafʝMtI}\{ֶJMAw(;Q"VEZ6VꬁYi̺ȜA$w:jx*96E}³Y]B5˴3X$摘jS|lb.׌Zfkf¤|l2vؾ.EY*s*p(Kbl}-cF^xY[4c+]~t&S_;xXsWtrL_TeC<*9 ia?(0l4܏ScfPNgb $5qJU{,/<<07l\JG-A&qrsRp2=/"%CV >5Iu%txrNe4\p<92K;K;!c?cptg^ Ů]KIZӓ&BeF~t)=:rR= .}fMAϴ@̌LH&)"AƙFu8R+;V[%6_2 `LK~n׺Ln%y&[w*5|ʞ?>xQZuuuKf/_&~GGcfʌÇ{8a7u;h&yP$a&;8G2dFA:.0}IdFʗ=OF=G3bCIxzz'"(M\v:/Y a^Ç&|܈g.tbIY ePk,mMG2Ek6Aٜw?˗/;0L{MAHrCr2d/ $cb {d@i {=w[̍/+d{`ݎr\+:F ǐYǝu6$]J6Ǐ0:Ce9m R{5ޑN\#ƙ$W`iAŗP> AL;j͚2ìВ/%?4]!'ۗ8l!LgjCOfU;bKA mz?9 ZvrIAiL>5 #O:۽I.d/2ݵys =S.ԆXB)7OiN#6.:Lh ^Vnk {hRy8?? f1%Y#իWÇ4Oʶ );bv#0. ;I* #FletQ[T>'En>>>N5 mLF]0X7r|gq?;ʭe_:W$Cy{w٥eg6aZQ PXv7GUq+23z< n۴ K988" &%#AM:C dEplUe/6E8 *ģoۤ1$4$U"^m\;q0a65eLWd^W'x%F3ԕ*ĵ\A] zۭN+r3ʌ~qAuSlsg`}ݐ hq'~WIBFcNtmHC^ywwwÏ?S)&kxsx!THs2S+49djy?t`>3аq_u:ǖLN˿ t:KeHDf ξ|7l! \Fh>LsV<>>5+K8<0Qt͡YuF81߂3ZVnnmvkY 1fԵH<7\DnFќ_^^>-2 R7JmQRrSu}3&g͞2ImYm0ş Uh439\j7{nس=M Vd^$Pl;>UМNsnMׯ_OƇ d", &mD&s)"c4)b՘ E7kϟw2逢Ǩɇidƀu)TPYtusN62! z?13eJKIZ ^:M9:yrdFrf=gfckLS2jK'mA)h4b}}gKfhŪkOPǏfٓM[J&hB͍E cnf@%]l8??\a%PI6.p:05׵@3ck$~.Ft\j R PO|XD9#B'oB$t]o YkPT:v.O4vͅ#xrqцְ.sIEV材2T s Lֺź~goCF3wcrHm;|(3y]Ǭ2Q+Cm) 0&,~G/lXmV)ȗXݰlt22O#tXT&6n;BfI"~+$KZfIL˯(` 3IS:<朘XѓѸUuO2RPmVN|l&Y=(Rz-B7`d ?f ɓl!"Rc9(C aFw3✆O;;;1gXu(NKñQ6gB$kѿ2=;[w1u69ײM2\ş` =6" k/I!Tgg+p~v\JOķfExxx8hȺ8  W f\ &Z~Wħ: emm~&Ć~v:J30 ZZ*aofbLӸ12K Sٹ 2v ,~?UmOuasٚ!)c2 3"N|Nf>۴guMfTqWPM7Yzf֑֜aaY 7;l;`ka3eHSYBj4 =0I+m2]O95!x:;(3@rZ ??fĞ0_pּ4j4sUyh^u$*ZVn#1H:cƓmܤ gpYF 6 SYr-eڡjl_3Fu%2N6d_N2s(N`s\vu79q3w5uP.4*ggS8cI $'2-Ό؉VJ}dM޽i,̰ڦ:Xk7F,b59Otn|A|@Fdji[𬹈TzĹvzfxtt4MR9vJLI3SbBKa_ASmZs | rȇQ=''=Xa&uL"MkN[6qmXr&]$xh>@_6,ۭ@li1 `\E^yeM57'#v7(Pf[E5A-fbUN06$~OY~ܟ3|٥.^?5$E{vf-ɖ_]hUkӀIwnXX<=LtQE6Ն" zzzg I~ɔ&d$zaٳK{1/9ڜfRJ୛ `-ۆls %@f IDATmN%6l)e+K1ד13RN$sa:>ݔر~c\N-8,OL'9^t'miϳzM eչ"`;.0jj\_x*5p3kN;mf)MJlUOO )gw>ON˺7a^ا l8Q$?jo;lN 20&nsS 6hN`B|<2;5; (g4LY2$Sp >U$fAYyΥM,wd)'OfOS&֞w :ПWf>Rn]zߟ"d2PU- Dl02rTv¢?ZNOO=)9mz.c9&trSZﷲf3AM=F,~qJeG#<,ĉ=lA2SH}dE'j")ݜiAğ_3k\5&:FN}߿C2J930Kr*̻dFr"2Ѱm<={jL@B^4B0}8(_A|D9U/~5QǕ2tign>,]n+2}fH~Y,ިqj keY}l{"4_ffd'ZKB3.#='R)tXOiX !,M(F$蜙 L^Ոm6arD 'iFEtac}%w#%Jdm?3` 2U:l'7Fz tS30Tښz?3ELh '$GY!XjJ;M[ AJt: /IV&ׂ@㞩kr㪲0Ze0vpP&A0\x!268Uk_"$$Uus?ߏƦRRDvs$jj5xDdY'Qv٬%Ƹ]̬g0-n Tq2Si 04$L LP&ΡD:-(/7jD~'i]IE҉(K2d,'v8~oue}[t:)h΋P'`KnݓO+@lXE#9g Dd&@ 0f^8DTe$d ;Sb dY N W: 5dn*l +P͔2Ԫ![b&Qw67%ujn)-]qy]Jjx 7|8ZE% U,I^kn+ve9чkl$*${3V bITu#gցFFqd4oYJ؀hѓ1I9 8UVA-A&gfU>&kbӌ]2NeUq JK)K85u*g&ٽ5|Տyׯ_o|qg}駩?IY׿YYYf//{eYp%mYŋҴpаK}>q*i,`R)2qM˜k_o5j󗆟59+^ ~AV\YTI~`Q:WW ǴS LT)K_u]Wlӓy]NF{kINo7vib3a)\B|dflj *=J{hF8FONze'-*=0 >d]O3Ȓ.otsIߞ׸u&Cxx>_֒/j~zJ[5<%).3lzL}ݧ؆zFQ}6c9i1f"N?aSrI&&ӣ1?H~ g.T\m6'w\|"(fJt} rފ2ܘhM2rym @K~j𹽽M~5[ &AN4&^>c/rS b8CØ'bĵyմp]6uO][-Ӈj"j4~Oc\ejٳ=5}SR`[]\c'/n?ܐ4g (F ~g)'cB,܎W5 Ge`%#p5Lr  !R]3aH vo~!=!szΘFxE^q~l\`8RΈUOܙF\ͱ+ @#)OYrUGZ̜GngWWWyj{Vo* SQS4&CFsMxl-3MbuS% ks>̅I/0Im2^/7?.Mqgyr^6Ƥ줯Ϲ;}ςbxcB<0 sOeIʠT'&Ws8~0v<̜Nr "w޷Rwۇ,ũ p+%TS̍^?9Vcmӕ^:(wy \Xw{3,s7ULde_&D0ks jЫ<իW"f\⏳SI}e/Mz-zS=+Tc1HoV.X9.ܼTӘQmLTԨt5m5$ #ϵdOP&aI'3wL҆d%7G˩F0Q)r}V)Ě{lTR-~id f???М :Xh).:+eǧG9aqS`Û:!]L'I.&$iaq}S`AIf8Q^˥[ABߓRd[J8'0T{Y+;xh^:T&j̢Rb]odt LCcplZ&}UY'4#k0ɋO)bŷ}1\KSKh)K) L-еH-feT E oj@QlArsOY$eVE6jg61ޖbĔʟ}rf-)p\x'/jB1?͌_@Uq0'`1-TNps鸞B:Z' `SnUkO_Y)2tIoĸ]V7Ϋ>S|wwww#ĻMe}~~ul5,Zfa#)LlJLoP_egi7fό0ՠMMJ`BUV L?Vˀ_LA[3TӢ+44XzrD}/sP(?{/^ݛ7oՍz :}ﴼO^; IENDB`./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/po/0000755000015600001650000000000012705421114024745 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/po/CMakeLists.txt0000644000015600001650000000416112705421114027507 0ustar jenkinsjenkinsinclude(FindGettext) # Find the translation tools find_program(INTLTOOL_MERGE intltool-merge) if(NOT INTLTOOL_MERGE) message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package") endif() find_program(INTLTOOL_EXTRACT intltool-extract) if(NOT INTLTOOL_EXTRACT) message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package") endif() if(NOT CMAKE_INSTALL_LOCALEDIR) set(CMAKE_INSTALL_LOCALEDIR "share/locale") endif() find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) set(DOMAIN ${APP_ID}) set(POT_FILE ${DOMAIN}.pot) file(GLOB PO_FILES *.po) file(GLOB_RECURSE I18N_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/*.qml ${CMAKE_SOURCE_DIR}/*.js ${CMAKE_SOURCE_DIR}/*.cpp ${CMAKE_SOURCE_DIR}/*.h ) list(APPEND I18N_SRC_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME}.in.h) list(SORT I18N_SRC_FILES) add_custom_target(${POT_FILE} ALL COMMENT "Generating translation template" # Extract the translatable messages from the desktop file COMMAND ${INTLTOOL_EXTRACT} --update --type=gettext/ini --srcdir=${CMAKE_BINARY_DIR}/app ${DESKTOP_FILE_NAME}.in # Update the translation file COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} -o ${POT_FILE} --from-code=UTF-8 --c++ --qt --add-comments=TRANSLATORS --keyword=tr --keyword=tr:1,2 --keyword=N_ --package-name='${PROJECT}' -D ${CMAKE_CURRENT_SOURCE_DIR} ${I18N_SRC_FILES} # Copy the up2date translation file to the source directory COMMAND ${CMAKE_COMMAND} -E copy ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR} ) foreach(PO_FILE ${PO_FILES}) get_filename_component(LANG ${PO_FILE} NAME_WE) gettext_process_po_files(${LANG} ALL PO_FILES ${PO_FILE}) set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/${LANG}/LC_MESSAGES) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo DESTINATION ${INSTALL_DIR} RENAME ${DOMAIN}.mo) endforeach(PO_FILE) add_custom_target(${DESKTOP_FILE_NAME} ALL COMMENT "Merging translations into ${DESKTOP_FILE_NAME}" COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${CMAKE_BINARY_DIR}/app/${DESKTOP_FILE_NAME}.in ${CMAKE_BINARY_DIR}/app/${DESKTOP_FILE_NAME} > /dev/null ) ./share/qtcreator/templates/wizards/ubuntu/simple-i18n-cmake/CMakeLists.txt0000644000015600001650000000565212705421114027077 0ustar jenkinsjenkinsproject(%ProjectName% C CXX) cmake_minimum_required(VERSION 2.8.9) # Do not remove this line, its required for the correct functionality of the Ubuntu-SDK set(UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file") find_package(Qt5Core) find_package(Qt5Qml) find_package(Qt5Quick) # Find_package(ubuntu-sdk-libs) #automatically create moc files set(CMAKE_AUTOMOC ON) # Figure out the component install path execute_process( COMMAND dpkg-architecture -qDEB_HOST_MULTIARCH OUTPUT_VARIABLE ARCH_TRIPLET OUTPUT_STRIP_TRAILING_WHITESPACE ) set(CLICK_ARCH "all") # If you want to add native code to your project, replace the set CLICK_ARCH command # with the following part. # This command figures out the target architecture for use in the manifest file # execute_process( # COMMAND dpkg-architecture -qDEB_HOST_ARCH # OUTPUT_VARIABLE CLICK_ARCH # OUTPUT_STRIP_TRAILING_WHITESPACE # ) set(QT_IMPORTS_DIR "lib/${ARCH_TRIPLET}") option(INSTALL_TESTS "Install the tests on make install" on) set(APP_NAME %ProjectName%) set(APP_ID "%ProjectName:l%.%ClickDomain:l%") set(%ProjectName:u%_DIR "share/qml/%ProjectName%") set(MAIN_QML "Main.qml") set(ICON "graphics/%ProjectName%.png") # Set install paths set(CMAKE_INSTALL_PREFIX /) set(DATA_DIR /) set(DESKTOP_DIR ${DATA_DIR}) set(DESKTOP_FILE_NAME "%ClickHookName%.desktop") # This sets the commandline that is executed on the device set(EXEC "qmlscene %U ${%ProjectName:u%_DIR}/${MAIN_QML}") # Configures the manifest file. The manifest file describes the click package # to the target system. All cmake variables that are defined at this point # can be used in the manifest file and will be automatically replaced by cmake configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION ${CMAKE_INSTALL_PREFIX}) install(DIRECTORY "app/graphics" DESTINATION ${DATA_DIR}) install(FILES "%ClickHookName%.apparmor" DESTINATION ${DATA_DIR}) add_subdirectory(app) add_subdirectory(po) add_custom_target("autopilot" chmod +x ${CMAKE_SOURCE_DIR}/app/tests/autopilot/run COMMAND ${CMAKE_SOURCE_DIR}/app/tests/autopilot/run WORKING_DIRECTORY ./app) add_custom_target("check" /usr/bin/qmltestrunner -input ${CMAKE_SOURCE_DIR}/app/tests/unit -import ${CMAKE_BINARY_DIR}/backend WORKING_DIRECTORY ./app) add_custom_target("run" /usr/bin/qmlscene -I ${CMAKE_BINARY_DIR}/backend ${CMAKE_SOURCE_DIR}/app/%ProjectName%.qml WORKING_DIRECTORY ./app) # Normally QtCreator would only show files that are part of a target, but we need it to show also files # that are not compiled. Therefore we add a custom target that just does nothing but list the files add_custom_target("%ProjectName:l%_ClickFiles" ALL SOURCES "%ClickHookName%.apparmor" "manifest.json.in") ./share/qtcreator/templates/wizards/ubuntu/html5-simple/0000755000015600001650000000000012705421114023523 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/wizard.xml0000644000015600001650000001256412705421114025555 0ustar jenkinsjenkins ../share/ubuntu.png Template for creating a native HTML5 app Native HTML5 apps have unrestricted access to Platform APIs. This project contains only HTML/CSS and Javascript code. The UI is designed for a mobile form-factor and uses the Ubuntu Touch design building blocks. Unlike the default Qt HTML5 template, there is no C++ code or compilation necessary for this application type HTML5 App Ubuntu Click package parameters Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/0000755000015600001650000000000012705421114024665 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/0000755000015600001650000000000012705421114026705 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/0000755000015600001650000000000012705421114032432 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/__init__.py./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/__init__.p0000644000015600001650000000014412705421114034351 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """ autopilot tests """ ./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/tests/0000755000015600001650000000000012705421114033574 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/tests/__init__.py./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/tests/__in0000644000015600001650000000643312705421114034431 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- """ Autopilot tests """ import os import subprocess from selenium import webdriver from selenium.webdriver.chrome.options import Options import ubuntuuitoolkit as uitk from ubuntuuitoolkit import fixture_setup from autopilot import platform from testtools.matchers import NotEquals, EndsWith from autopilot.matchers import Eventually from autopilot.testcase import AutopilotTestCase CURRENT_ARCHITECTURE = subprocess.check_output( ["dpkg-architecture", "-qDEB_HOST_MULTIARCH"]).strip() CHROMEDRIVER_EXEC_PATH = \ "/usr/lib/{}/oxide-qt/chromedriver".format( CURRENT_ARCHITECTURE.decode("utf-8")) DEFAULT_WEBVIEW_INSPECTOR_IP = '127.0.0.1' DEFAULT_WEBVIEW_INSPECTOR_PORT = 9221 class HTML5TestCaseBase(AutopilotTestCase): def setUp(self): self.driver = None self.app_proxy = None super().setUp() if platform.model() == 'Desktop': self.patch_environment( 'UBUNTU_WEBVIEW_DEVTOOLS_HOST', DEFAULT_WEBVIEW_INSPECTOR_IP) self.patch_environment( 'UBUNTU_WEBVIEW_DEVTOOLS_PORT', str(DEFAULT_WEBVIEW_INSPECTOR_PORT)) else: self.useFixture(fixture_setup.InitctlEnvironmentVariable( global_=True, UBUNTU_WEBVIEW_DEVTOOLS_HOST=DEFAULT_WEBVIEW_INSPECTOR_IP, UBUNTU_WEBVIEW_DEVTOOLS_PORT=str( DEFAULT_WEBVIEW_INSPECTOR_PORT) )) def tearDown(self): if self.driver: self.page.close() self.page.quit() # XXX: This should not be there but AP hangs # if we dont extra force the process to be killed # (although AP already tries to kill part of its teardown) if platform.model() == 'Desktop' \ and self.app_proxy \ and self.app_proxy.process: self.app_proxy.process.kill() super(HTML5TestCaseBase, self).tearDown() def get_webview(self): return self.app_proxy.select_single('UbuntuWebView02') def launch_webdriver(self): options = Options() options.binary_location = '' options.debugger_address = '{}:{}'.format( DEFAULT_WEBVIEW_INSPECTOR_IP, DEFAULT_WEBVIEW_INSPECTOR_PORT) self.driver = webdriver.Chrome( executable_path=CHROMEDRIVER_EXEC_PATH, chrome_options=options) self.assertThat(self.driver, NotEquals(None)) @property def page(self): return self.driver def launch_html5_app_inline(self, args): return self.launch_test_application( 'ubuntu-html5-app-launcher', *args, emulator_base=uitk.UbuntuUIToolkitCustomProxyObjectBase) def launch_html5_app(self): self.app_proxy = self.launch_html5_app_inline( ['--www={}/{}'.format( os.path.dirname( os.path.realpath(__file__)), '../../../../www')]) self.wait_for_app() self.launch_webdriver() def wait_for_app(self): self.assertThat(self.app_proxy, NotEquals(None)) webview = self.get_webview() self.assertThat( lambda: webview.url, Eventually(EndsWith("/index.html"))) ././@LongLink0000644000000000000000000000016300000000000011603 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/tests/test_app_launch.py./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/autopilot/application_tests/tests/test0000644000015600001650000000110512705421114034473 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- from application_tests.tests import HTML5TestCaseBase from testtools.matchers import NotEquals, Equals class LaunchTestCaseBase(HTML5TestCaseBase): def setUp(self): super(LaunchTestCaseBase, self).setUp() def test_basic_launch(self): self.launch_html5_app() html5_doc_buttons = self.page.find_elements_by_css_selector( "#hello-page a") self.assertThat(html5_doc_buttons, NotEquals(None)) self.assertThat(len(html5_doc_buttons), Equals(2)) ./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/0000755000015600001650000000000012705421114025644 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/0000755000015600001650000000000012705421114030175 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/0000755000015600001650000000000012705421114031623 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/MIT.LICENSE0000644000015600001650000000204512705421114033261 0ustar jenkinsjenkinsCopyright (c) 2008-2014 Pivotal Labs 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. ./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/0000755000015600001650000000000012705421114032371 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-2.2.0/./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-20000755000015600001650000000000012705421114034077 5ustar jenkinsjenkins././@LongLink0000644000000000000000000000016400000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-2.2.0/jasmine.js./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-20000644000015600001650000023545112705421114034113 0ustar jenkinsjenkins/* Copyright (c) 2008-2015 Pivotal Labs 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. */ var getJasmineRequireObj = (function (jasmineGlobal) { var jasmineRequire; if (typeof module !== 'undefined' && module.exports) { jasmineGlobal = global; jasmineRequire = exports; } else { if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') { jasmineGlobal = window; } jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {}; } function getJasmineRequire() { return jasmineRequire; } getJasmineRequire().core = function(jRequire) { var j$ = {}; jRequire.base(j$, jasmineGlobal); j$.util = jRequire.util(); j$.Any = jRequire.Any(); j$.Anything = jRequire.Anything(j$); j$.CallTracker = jRequire.CallTracker(); j$.MockDate = jRequire.MockDate(); j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); j$.Env = jRequire.Env(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(); j$.Expectation = jRequire.Expectation(); j$.buildExpectationResult = jRequire.buildExpectationResult(); j$.JsApiReporter = jRequire.JsApiReporter(); j$.matchersUtil = jRequire.matchersUtil(j$); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); j$.pp = jRequire.pp(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.ReportDispatcher = jRequire.ReportDispatcher(); j$.Spec = jRequire.Spec(j$); j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(); j$.StringMatching = jRequire.StringMatching(j$); j$.Suite = jRequire.Suite(); j$.Timer = jRequire.Timer(); j$.version = jRequire.version(); j$.matchers = jRequire.requireMatchers(jRequire, j$); return j$; }; return getJasmineRequire; })(this); getJasmineRequireObj().requireMatchers = function(jRequire, j$) { var availableMatchers = [ 'toBe', 'toBeCloseTo', 'toBeDefined', 'toBeFalsy', 'toBeGreaterThan', 'toBeLessThan', 'toBeNaN', 'toBeNull', 'toBeTruthy', 'toBeUndefined', 'toContain', 'toEqual', 'toHaveBeenCalled', 'toHaveBeenCalledWith', 'toMatch', 'toThrow', 'toThrowError' ], matchers = {}; for (var i = 0; i < availableMatchers.length; i++) { var name = availableMatchers[i]; matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().base = function(j$, jasmineGlobal) { j$.unimplementedMethod_ = function() { throw new Error('unimplemented method'); }; j$.MAX_PRETTY_PRINT_DEPTH = 40; j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100; j$.DEFAULT_TIMEOUT_INTERVAL = 5000; j$.getGlobal = function() { return jasmineGlobal; }; j$.getEnv = function(options) { var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); //jasmine. singletons in here (setTimeout blah blah). return env; }; j$.isArray_ = function(value) { return j$.isA_('Array', value); }; j$.isString_ = function(value) { return j$.isA_('String', value); }; j$.isNumber_ = function(value) { return j$.isA_('Number', value); }; j$.isA_ = function(typeName, value) { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; }; j$.isDomNode = function(obj) { return obj.nodeType > 0; }; j$.fnNameFor = function(func) { return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1]; }; j$.any = function(clazz) { return new j$.Any(clazz); }; j$.anything = function() { return new j$.Anything(); }; j$.objectContaining = function(sample) { return new j$.ObjectContaining(sample); }; j$.stringMatching = function(expected) { return new j$.StringMatching(expected); }; j$.arrayContaining = function(sample) { return new j$.ArrayContaining(sample); }; j$.createSpy = function(name, originalFn) { var spyStrategy = new j$.SpyStrategy({ name: name, fn: originalFn, getSpy: function() { return spy; } }), callTracker = new j$.CallTracker(), spy = function() { var callData = { object: this, args: Array.prototype.slice.apply(arguments) }; callTracker.track(callData); var returnValue = spyStrategy.exec.apply(this, arguments); callData.returnValue = returnValue; return returnValue; }; for (var prop in originalFn) { if (prop === 'and' || prop === 'calls') { throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon'); } spy[prop] = originalFn[prop]; } spy.and = spyStrategy; spy.calls = callTracker; return spy; }; j$.isSpy = function(putativeSpy) { if (!putativeSpy) { return false; } return putativeSpy.and instanceof j$.SpyStrategy && putativeSpy.calls instanceof j$.CallTracker; }; j$.createSpyObj = function(baseName, methodNames) { if (j$.isArray_(baseName) && j$.util.isUndefined(methodNames)) { methodNames = baseName; baseName = 'unknown'; } if (!j$.isArray_(methodNames) || methodNames.length === 0) { throw 'createSpyObj requires a non-empty array of method names to create spies for'; } var obj = {}; for (var i = 0; i < methodNames.length; i++) { obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); } return obj; }; }; getJasmineRequireObj().util = function() { var util = {}; util.inherit = function(childClass, parentClass) { var Subclass = function() { }; Subclass.prototype = parentClass.prototype; childClass.prototype = new Subclass(); }; util.htmlEscape = function(str) { if (!str) { return str; } return str.replace(/&/g, '&') .replace(//g, '>'); }; util.argsToArray = function(args) { var arrayOfArgs = []; for (var i = 0; i < args.length; i++) { arrayOfArgs.push(args[i]); } return arrayOfArgs; }; util.isUndefined = function(obj) { return obj === void 0; }; util.arrayContains = function(array, search) { var i = array.length; while (i--) { if (array[i] === search) { return true; } } return false; }; util.clone = function(obj) { if (Object.prototype.toString.apply(obj) === '[object Array]') { return obj.slice(); } var cloned = {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { cloned[prop] = obj[prop]; } } return cloned; }; return util; }; getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; }; this.userContext = attrs.userContext || function() { return {}; }; this.onStart = attrs.onStart || function() {}; this.getSpecName = attrs.getSpecName || function() { return ''; }; this.expectationResultFactory = attrs.expectationResultFactory || function() { }; this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; if (!this.queueableFn.fn) { this.pend(); } this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], pendingReason: '' }; } Spec.prototype.addExpectationResult = function(passed, data) { var expectationResult = this.expectationResultFactory(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { this.result.failedExpectations.push(expectationResult); } }; Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Spec.prototype.execute = function(onComplete) { var self = this; this.onStart(this); if (this.markedPending || this.disabled) { complete(); return; } var fns = this.beforeAndAfterFns(); var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters); this.queueRunnerFactory({ queueableFns: allFns, onException: function() { self.onException.apply(self, arguments); }, onComplete: complete, userContext: this.userContext() }); function complete() { self.result.status = self.status(); self.resultCallback(self.result); if (onComplete) { onComplete(); } } }; Spec.prototype.onException = function onException(e) { if (Spec.isPendingSpecException(e)) { this.pend(extractCustomPendingMessage(e)); return; } this.addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', error: e }); }; Spec.prototype.disable = function() { this.disabled = true; }; Spec.prototype.pend = function(message) { this.markedPending = true; if (message) { this.result.pendingReason = message; } }; Spec.prototype.status = function() { if (this.disabled) { return 'disabled'; } if (this.markedPending) { return 'pending'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'passed'; } }; Spec.prototype.isExecutable = function() { return !this.disabled && !this.markedPending; }; Spec.prototype.getFullName = function() { return this.getSpecName(this); }; var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; return fullMessage.substr(boilerplateEnd); }; Spec.pendingSpecExceptionMessage = '=> marked Pending'; Spec.isPendingSpecException = function(e) { return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1); }; return Spec; }; if (typeof window == void 0 && typeof exports == 'object') { exports.Spec = jasmineRequire.Spec; } getJasmineRequireObj().Env = function(j$) { function Env(options) { options = options || {}; var self = this; var global = options.global || j$.getGlobal(); var totalSpecsDefined = 0; var catchExceptions = true; var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global)); var runnableLookupTable = {}; var runnableResources = {}; var currentSpec = null; var currentlyExecutingSuites = []; var currentDeclarationSuite = null; var currentSuite = function() { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; }; var currentRunnable = function() { return currentSpec || currentSuite(); }; var reporter = new j$.ReportDispatcher([ 'jasmineStarted', 'jasmineDone', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); this.specFilter = function() { return true; }; this.addCustomEqualityTester = function(tester) { if(!currentRunnable()) { throw new Error('Custom Equalities must be added in a before function or a spec'); } runnableResources[currentRunnable().id].customEqualityTesters.push(tester); }; this.addMatchers = function(matchersToAdd) { if(!currentRunnable()) { throw new Error('Matchers must be added in a before function or a spec'); } var customMatchers = runnableResources[currentRunnable().id].customMatchers; for (var matcherName in matchersToAdd) { customMatchers[matcherName] = matchersToAdd[matcherName]; } }; j$.Expectation.addCoreMatchers(j$.matchers); var nextSpecId = 0; var getNextSpecId = function() { return 'spec' + nextSpecId++; }; var nextSuiteId = 0; var getNextSuiteId = function() { return 'suite' + nextSuiteId++; }; var expectationFactory = function(actual, spec) { return j$.Expectation.Factory({ util: j$.matchersUtil, customEqualityTesters: runnableResources[spec.id].customEqualityTesters, customMatchers: runnableResources[spec.id].customMatchers, actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; var defaultResourcesForRunnable = function(id, parentRunnableId) { var resources = {spies: [], customEqualityTesters: [], customMatchers: {}}; if(runnableResources[parentRunnableId]){ resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters); resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers); } runnableResources[id] = resources; }; var clearResourcesForRunnable = function(id) { spyRegistry.clearSpies(); delete runnableResources[id]; }; var beforeAndAfterFns = function(suite, runnablesExplictlySet) { return function() { var befores = [], afters = [], beforeAlls = [], afterAlls = []; while(suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); if (runnablesExplictlySet()) { beforeAlls = beforeAlls.concat(suite.beforeAllFns); afterAlls = afterAlls.concat(suite.afterAllFns); } suite = suite.parentSuite; } return { befores: beforeAlls.reverse().concat(befores.reverse()), afters: afters.concat(afterAlls) }; }; }; var getSpecName = function(spec, suite) { return suite.getFullName() + ' ' + spec.description; }; // TODO: we may just be able to pass in the fn instead of wrapping here var buildExpectationResult = j$.buildExpectationResult, exceptionFormatter = new j$.ExceptionFormatter(), expectationResultFactory = function(attrs) { attrs.messageFormatter = exceptionFormatter.message; attrs.stackFormatter = exceptionFormatter.stack; return buildExpectationResult(attrs); }; // TODO: fix this naming, and here's where the value comes in this.catchExceptions = function(value) { catchExceptions = !!value; return catchExceptions; }; this.catchingExceptions = function() { return catchExceptions; }; var maximumSpecCallbackDepth = 20; var currentSpecCallbackDepth = 0; function clearStack(fn) { currentSpecCallbackDepth++; if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) { currentSpecCallbackDepth = 0; realSetTimeout(fn, 0); } else { fn(); } } var catchException = function(e) { return j$.Spec.isPendingSpecException(e) || catchExceptions; }; var queueRunnerFactory = function(options) { options.catchException = catchException; options.clearStack = options.clearStack || clearStack; options.timer = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; options.fail = self.fail; new j$.QueueRunner(options).execute(); }; var topSuite = new j$.Suite({ env: this, id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', queueRunner: queueRunnerFactory }); runnableLookupTable[topSuite.id] = topSuite; defaultResourcesForRunnable(topSuite.id); currentDeclarationSuite = topSuite; this.topSuite = function() { return topSuite; }; this.execute = function(runnablesToRun) { if(runnablesToRun) { runnablesExplictlySet = true; } else if (focusedRunnables.length) { runnablesExplictlySet = true; runnablesToRun = focusedRunnables; } else { runnablesToRun = [topSuite.id]; } var allFns = []; for(var i = 0; i < runnablesToRun.length; i++) { var runnable = runnableLookupTable[runnablesToRun[i]]; allFns.push((function(runnable) { return { fn: function(done) { runnable.execute(done); } }; })(runnable)); } reporter.jasmineStarted({ totalSpecsDefined: totalSpecsDefined }); queueRunnerFactory({queueableFns: allFns, onComplete: reporter.jasmineDone}); }; this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; var spyRegistry = new j$.SpyRegistry({currentSpies: function() { if(!currentRunnable()) { throw new Error('Spies must be created in a before function or a spec'); } return runnableResources[currentRunnable().id].spies; }}); this.spyOn = function() { return spyRegistry.spyOn.apply(spyRegistry, arguments); }; var suiteFactory = function(description) { var suite = new j$.Suite({ env: self, id: getNextSuiteId(), description: description, parentSuite: currentDeclarationSuite, queueRunner: queueRunnerFactory, onStart: suiteStarted, expectationFactory: expectationFactory, expectationResultFactory: expectationResultFactory, runnablesExplictlySetGetter: runnablesExplictlySetGetter, resultCallback: function(attrs) { if (!suite.disabled) { clearResourcesForRunnable(suite.id); } currentlyExecutingSuites.pop(); reporter.suiteDone(attrs); } }); runnableLookupTable[suite.id] = suite; return suite; function suiteStarted(suite) { currentlyExecutingSuites.push(suite); defaultResourcesForRunnable(suite.id, suite.parentSuite.id); reporter.suiteStarted(suite.result); } }; this.describe = function(description, specDefinitions) { var suite = suiteFactory(description); addSpecsToSuite(suite, specDefinitions); return suite; }; this.xdescribe = function(description, specDefinitions) { var suite = this.describe(description, specDefinitions); suite.disable(); return suite; }; var focusedRunnables = []; this.fdescribe = function(description, specDefinitions) { var suite = suiteFactory(description); suite.isFocused = true; focusedRunnables.push(suite.id); unfocusAncestor(); addSpecsToSuite(suite, specDefinitions); return suite; }; function addSpecsToSuite(suite, specDefinitions) { var parentSuite = currentDeclarationSuite; parentSuite.addChild(suite); currentDeclarationSuite = suite; var declarationError = null; try { specDefinitions.call(suite); } catch (e) { declarationError = e; } if (declarationError) { self.it('encountered a declaration exception', function() { throw declarationError; }); } currentDeclarationSuite = parentSuite; } function findFocusedAncestor(suite) { while (suite) { if (suite.isFocused) { return suite.id; } suite = suite.parentSuite; } return null; } function unfocusAncestor() { var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); if (focusedAncestor) { for (var i = 0; i < focusedRunnables.length; i++) { if (focusedRunnables[i] === focusedAncestor) { focusedRunnables.splice(i, 1); break; } } } } var runnablesExplictlySet = false; var runnablesExplictlySetGetter = function(){ return runnablesExplictlySet; }; var specFactory = function(description, fn, suite, timeout) { totalSpecsDefined++; var spec = new j$.Spec({ id: getNextSpecId(), beforeAndAfterFns: beforeAndAfterFns(suite, runnablesExplictlySetGetter), expectationFactory: expectationFactory, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); }, onStart: specStarted, description: description, expectationResultFactory: expectationResultFactory, queueRunnerFactory: queueRunnerFactory, userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } } }); runnableLookupTable[spec.id] = spec; if (!self.specFilter(spec)) { spec.disable(); } return spec; function specResultCallback(result) { clearResourcesForRunnable(spec.id); currentSpec = null; reporter.specDone(result); } function specStarted(spec) { currentSpec = spec; defaultResourcesForRunnable(spec.id, suite.id); reporter.specStarted(spec.result); } }; this.it = function(description, fn, timeout) { var spec = specFactory(description, fn, currentDeclarationSuite, timeout); currentDeclarationSuite.addChild(spec); return spec; }; this.xit = function() { var spec = this.it.apply(this, arguments); spec.pend(); return spec; }; this.fit = function(){ var spec = this.it.apply(this, arguments); focusedRunnables.push(spec.id); unfocusAncestor(); return spec; }; this.expect = function(actual) { if (!currentRunnable()) { throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out'); } return currentRunnable().expect(actual); }; this.beforeEach = function(beforeEachFunction, timeout) { currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.beforeAll = function(beforeAllFunction, timeout) { currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterEach = function(afterEachFunction, timeout) { currentDeclarationSuite.afterEach({ fn: afterEachFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterAll = function(afterAllFunction, timeout) { currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.pending = function(message) { var fullMessage = j$.Spec.pendingSpecExceptionMessage; if(message) { fullMessage += message; } throw fullMessage; }; this.fail = function(error) { var message = 'Failed'; if (error) { message += ': '; message += error.message || error; } currentRunnable().addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message: message, error: error && error.message ? error : null }); }; } return Env; }; getJasmineRequireObj().JsApiReporter = function() { var noopTimer = { start: function(){}, elapsed: function(){ return 0; } }; function JsApiReporter(options) { var timer = options.timer || noopTimer, status = 'loaded'; this.started = false; this.finished = false; this.jasmineStarted = function() { this.started = true; status = 'started'; timer.start(); }; var executionTime; this.jasmineDone = function() { this.finished = true; executionTime = timer.elapsed(); status = 'done'; }; this.status = function() { return status; }; var suites = [], suites_hash = {}; this.suiteStarted = function(result) { suites_hash[result.id] = result; }; this.suiteDone = function(result) { storeSuite(result); }; this.suiteResults = function(index, length) { return suites.slice(index, index + length); }; function storeSuite(result) { suites.push(result); suites_hash[result.id] = result; } this.suites = function() { return suites_hash; }; var specs = []; this.specDone = function(result) { specs.push(result); }; this.specResults = function(index, length) { return specs.slice(index, index + length); }; this.specs = function() { return specs; }; this.executionTime = function() { return executionTime; }; } return JsApiReporter; }; getJasmineRequireObj().CallTracker = function() { function CallTracker() { var calls = []; this.track = function(context) { calls.push(context); }; this.any = function() { return !!calls.length; }; this.count = function() { return calls.length; }; this.argsFor = function(index) { var call = calls[index]; return call ? call.args : []; }; this.all = function() { return calls; }; this.allArgs = function() { var callArgs = []; for(var i = 0; i < calls.length; i++){ callArgs.push(calls[i].args); } return callArgs; }; this.first = function() { return calls[0]; }; this.mostRecent = function() { return calls[calls.length - 1]; }; this.reset = function() { calls = []; }; } return CallTracker; }; getJasmineRequireObj().Clock = function() { function Clock(global, delayedFunctionScheduler, mockDate) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, clearTimeout: global.clearTimeout, setInterval: global.setInterval, clearInterval: global.clearInterval }, fakeTimingFunctions = { setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval }, installed = false, timer; self.install = function() { replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; installed = true; return self; }; self.uninstall = function() { delayedFunctionScheduler.reset(); mockDate.uninstall(); replace(global, realTimingFunctions); timer = realTimingFunctions; installed = false; }; self.mockDate = function(initialDate) { mockDate.install(initialDate); }; self.setTimeout = function(fn, delay, params) { if (legacyIE()) { if (arguments.length > 2) { throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill'); } return timer.setTimeout(fn, delay); } return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]); }; self.setInterval = function(fn, delay, params) { if (legacyIE()) { if (arguments.length > 2) { throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill'); } return timer.setInterval(fn, delay); } return Function.prototype.apply.apply(timer.setInterval, [global, arguments]); }; self.clearTimeout = function(id) { return Function.prototype.call.apply(timer.clearTimeout, [global, id]); }; self.clearInterval = function(id) { return Function.prototype.call.apply(timer.clearInterval, [global, id]); }; self.tick = function(millis) { if (installed) { mockDate.tick(millis); delayedFunctionScheduler.tick(millis); } else { throw new Error('Mock clock is not installed, use jasmine.clock().install()'); } }; return self; function legacyIE() { //if these methods are polyfilled, apply will be present return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; } function replace(dest, source) { for (var prop in source) { dest[prop] = source[prop]; } } function setTimeout(fn, delay) { return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); } function clearTimeout(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function setInterval(fn, interval) { return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); } function clearInterval(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function argSlice(argsObj, n) { return Array.prototype.slice.call(argsObj, n); } } return Clock; }; getJasmineRequireObj().DelayedFunctionScheduler = function() { function DelayedFunctionScheduler() { var self = this; var scheduledLookup = []; var scheduledFunctions = {}; var currentTime = 0; var delayedFnCount = 0; self.tick = function(millis) { millis = millis || 0; var endTime = currentTime + millis; runScheduledFunctions(endTime); currentTime = endTime; }; self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) { var f; if (typeof(funcToCall) === 'string') { /* jshint evil: true */ f = function() { return eval(funcToCall); }; /* jshint evil: false */ } else { f = funcToCall; } millis = millis || 0; timeoutKey = timeoutKey || ++delayedFnCount; runAtMillis = runAtMillis || (currentTime + millis); var funcToSchedule = { runAtMillis: runAtMillis, funcToCall: f, recurring: recurring, params: params, timeoutKey: timeoutKey, millis: millis }; if (runAtMillis in scheduledFunctions) { scheduledFunctions[runAtMillis].push(funcToSchedule); } else { scheduledFunctions[runAtMillis] = [funcToSchedule]; scheduledLookup.push(runAtMillis); scheduledLookup.sort(function (a, b) { return a - b; }); } return timeoutKey; }; self.removeFunctionWithId = function(timeoutKey) { for (var runAtMillis in scheduledFunctions) { var funcs = scheduledFunctions[runAtMillis]; var i = indexOfFirstToPass(funcs, function (func) { return func.timeoutKey === timeoutKey; }); if (i > -1) { if (funcs.length === 1) { delete scheduledFunctions[runAtMillis]; deleteFromLookup(runAtMillis); } else { funcs.splice(i, 1); } // intervals get rescheduled when executed, so there's never more // than a single scheduled function with a given timeoutKey break; } } }; self.reset = function() { currentTime = 0; scheduledLookup = []; scheduledFunctions = {}; delayedFnCount = 0; }; return self; function indexOfFirstToPass(array, testFn) { var index = -1; for (var i = 0; i < array.length; ++i) { if (testFn(array[i])) { index = i; break; } } return index; } function deleteFromLookup(key) { var value = Number(key); var i = indexOfFirstToPass(scheduledLookup, function (millis) { return millis === value; }); if (i > -1) { scheduledLookup.splice(i, 1); } } function reschedule(scheduledFn) { self.scheduleFunction(scheduledFn.funcToCall, scheduledFn.millis, scheduledFn.params, true, scheduledFn.timeoutKey, scheduledFn.runAtMillis + scheduledFn.millis); } function forEachFunction(funcsToRun, callback) { for (var i = 0; i < funcsToRun.length; ++i) { callback(funcsToRun[i]); } } function runScheduledFunctions(endTime) { if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { return; } do { currentTime = scheduledLookup.shift(); var funcsToRun = scheduledFunctions[currentTime]; delete scheduledFunctions[currentTime]; forEachFunction(funcsToRun, function(funcToRun) { if (funcToRun.recurring) { reschedule(funcToRun); } }); forEachFunction(funcsToRun, function(funcToRun) { funcToRun.funcToCall.apply(null, funcToRun.params || []); }); } while (scheduledLookup.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration currentTime !== endTime && scheduledLookup[0] <= endTime); } } return DelayedFunctionScheduler; }; getJasmineRequireObj().ExceptionFormatter = function() { function ExceptionFormatter() { this.message = function(error) { var message = ''; if (error.name && error.message) { message += error.name + ': ' + error.message; } else { message += error.toString() + ' thrown'; } if (error.fileName || error.sourceURL) { message += ' in ' + (error.fileName || error.sourceURL); } if (error.line || error.lineNumber) { message += ' (line ' + (error.line || error.lineNumber) + ')'; } return message; }; this.stack = function(error) { return error ? error.stack : null; }; } return ExceptionFormatter; }; getJasmineRequireObj().Expectation = function() { function Expectation(options) { this.util = options.util || { buildFailureMessage: function() {} }; this.customEqualityTesters = options.customEqualityTesters || []; this.actual = options.actual; this.addExpectationResult = options.addExpectationResult || function(){}; this.isNot = options.isNot; var customMatchers = options.customMatchers || {}; for (var matcherName in customMatchers) { this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]); } } Expectation.prototype.wrapCompare = function(name, matcherFactory) { return function() { var args = Array.prototype.slice.call(arguments, 0), expected = args.slice(0), message = ''; args.unshift(this.actual); var matcher = matcherFactory(this.util, this.customEqualityTesters), matcherCompare = matcher.compare; function defaultNegativeCompare() { var result = matcher.compare.apply(null, args); result.pass = !result.pass; return result; } if (this.isNot) { matcherCompare = matcher.negativeCompare || defaultNegativeCompare; } var result = matcherCompare.apply(null, args); if (!result.pass) { if (!result.message) { args.unshift(this.isNot); args.unshift(name); message = this.util.buildFailureMessage.apply(null, args); } else { if (Object.prototype.toString.apply(result.message) === '[object Function]') { message = result.message(); } else { message = result.message; } } } if (expected.length == 1) { expected = expected[0]; } // TODO: how many of these params are needed? this.addExpectationResult( result.pass, { matcherName: name, passed: result.pass, message: message, actual: this.actual, expected: expected // TODO: this may need to be arrayified/sliced } ); }; }; Expectation.addCoreMatchers = function(matchers) { var prototype = Expectation.prototype; for (var matcherName in matchers) { var matcher = matchers[matcherName]; prototype[matcherName] = prototype.wrapCompare(matcherName, matcher); } }; Expectation.Factory = function(options) { options = options || {}; var expect = new Expectation(options); // TODO: this would be nice as its own Object - NegativeExpectation // TODO: copy instead of mutate options options.isNot = true; expect.not = new Expectation(options); return expect; }; return Expectation; }; //TODO: expectation result may make more sense as a presentation of an expectation. getJasmineRequireObj().buildExpectationResult = function() { function buildExpectationResult(options) { var messageFormatter = options.messageFormatter || function() {}, stackFormatter = options.stackFormatter || function() {}; var result = { matcherName: options.matcherName, message: message(), stack: stack(), passed: options.passed }; if(!result.passed) { result.expected = options.expected; result.actual = options.actual; } return result; function message() { if (options.passed) { return 'Passed.'; } else if (options.message) { return options.message; } else if (options.error) { return messageFormatter(options.error); } return ''; } function stack() { if (options.passed) { return ''; } var error = options.error; if (!error) { try { throw new Error(message()); } catch (e) { error = e; } } return stackFormatter(error); } } return buildExpectationResult; }; getJasmineRequireObj().MockDate = function() { function MockDate(global) { var self = this; var currentTime = 0; if (!global || !global.Date) { self.install = function() {}; self.tick = function() {}; self.uninstall = function() {}; return self; } var GlobalDate = global.Date; self.install = function(mockDate) { if (mockDate instanceof GlobalDate) { currentTime = mockDate.getTime(); } else { currentTime = new GlobalDate().getTime(); } global.Date = FakeDate; }; self.tick = function(millis) { millis = millis || 0; currentTime = currentTime + millis; }; self.uninstall = function() { currentTime = 0; global.Date = GlobalDate; }; createDateProperties(); return self; function FakeDate() { switch(arguments.length) { case 0: return new GlobalDate(currentTime); case 1: return new GlobalDate(arguments[0]); case 2: return new GlobalDate(arguments[0], arguments[1]); case 3: return new GlobalDate(arguments[0], arguments[1], arguments[2]); case 4: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]); case 5: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]); case 6: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); default: return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); } } function createDateProperties() { FakeDate.prototype = GlobalDate.prototype; FakeDate.now = function() { if (GlobalDate.now) { return currentTime; } else { throw new Error('Browser does not support Date.now()'); } }; FakeDate.toSource = GlobalDate.toSource; FakeDate.toString = GlobalDate.toString; FakeDate.parse = GlobalDate.parse; FakeDate.UTC = GlobalDate.UTC; } } return MockDate; }; getJasmineRequireObj().pp = function(j$) { function PrettyPrinter() { this.ppNestLevel_ = 0; this.seen = []; } PrettyPrinter.prototype.format = function(value) { this.ppNestLevel_++; try { if (j$.util.isUndefined(value)) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === 0 && 1/value === -Infinity) { this.emitScalar('-0'); } else if (value === j$.getGlobal()) { this.emitScalar(''); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString()); } else if (typeof value === 'string') { this.emitString(value); } else if (j$.isSpy(value)) { this.emitScalar('spy on ' + value.and.identity()); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (typeof value.nodeType === 'number') { this.emitScalar('HTMLNode'); } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (j$.util.arrayContains(this.seen, value)) { this.emitScalar(''); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { this.seen.push(value); if (j$.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } this.seen.pop(); } else { this.emitScalar(value.toString()); } } finally { this.ppNestLevel_--; } }; PrettyPrinter.prototype.iterateObject = function(obj, fn) { for (var property in obj) { if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; } fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) && obj.__lookupGetter__(property) !== null) : false); } }; PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; function StringPrettyPrinter() { PrettyPrinter.call(this); this.string = ''; } j$.util.inherit(StringPrettyPrinter, PrettyPrinter); StringPrettyPrinter.prototype.emitScalar = function(value) { this.append(value); }; StringPrettyPrinter.prototype.emitString = function(value) { this.append('\'' + value + '\''); }; StringPrettyPrinter.prototype.emitArray = function(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; } var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); this.append('[ '); for (var i = 0; i < length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } if(array.length > length){ this.append(', ...'); } this.append(' ]'); }; StringPrettyPrinter.prototype.emitObject = function(obj) { var constructorName = obj.constructor ? j$.fnNameFor(obj.constructor) : 'null'; this.append(constructorName); if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { return; } var self = this; this.append('({ '); var first = true; this.iterateObject(obj, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.append(property); self.append(': '); if (isGetter) { self.append(''); } else { self.format(obj[property]); } }); this.append(' })'); }; StringPrettyPrinter.prototype.append = function(value) { this.string += value; }; return function(value) { var stringPrettyPrinter = new StringPrettyPrinter(); stringPrettyPrinter.format(value); return stringPrettyPrinter.string; }; }; getJasmineRequireObj().QueueRunner = function(j$) { function once(fn) { var called = false; return function() { if (!called) { called = true; fn(); } }; } function QueueRunner(attrs) { this.queueableFns = attrs.queueableFns || []; this.onComplete = attrs.onComplete || function() {}; this.clearStack = attrs.clearStack || function(fn) {fn();}; this.onException = attrs.onException || function() {}; this.catchException = attrs.catchException || function() { return true; }; this.userContext = attrs.userContext || {}; this.timer = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; this.fail = attrs.fail || function() {}; } QueueRunner.prototype.execute = function() { this.run(this.queueableFns, 0); }; QueueRunner.prototype.run = function(queueableFns, recursiveIndex) { var length = queueableFns.length, self = this, iterativeIndex; for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) { var queueableFn = queueableFns[iterativeIndex]; if (queueableFn.fn.length > 0) { attemptAsync(queueableFn); return; } else { attemptSync(queueableFn); } } var runnerDone = iterativeIndex >= length; if (runnerDone) { this.clearStack(this.onComplete); } function attemptSync(queueableFn) { try { queueableFn.fn.call(self.userContext); } catch (e) { handleException(e, queueableFn); } } function attemptAsync(queueableFn) { var clearTimeout = function () { Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeoutId]]); }, next = once(function () { clearTimeout(timeoutId); self.run(queueableFns, iterativeIndex + 1); }), timeoutId; next.fail = function() { self.fail.apply(null, arguments); next(); }; if (queueableFn.timeout) { timeoutId = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'); onException(error, queueableFn); next(); }, queueableFn.timeout()]]); } try { queueableFn.fn.call(self.userContext, next); } catch (e) { handleException(e, queueableFn); next(); } } function onException(e, queueableFn) { self.onException(e); } function handleException(e, queueableFn) { onException(e, queueableFn); if (!self.catchException(e)) { //TODO: set a var when we catch an exception and //use a finally block to close the loop in a nice way.. throw e; } } }; return QueueRunner; }; getJasmineRequireObj().ReportDispatcher = function() { function ReportDispatcher(methods) { var dispatchedMethods = methods || []; for (var i = 0; i < dispatchedMethods.length; i++) { var method = dispatchedMethods[i]; this[method] = (function(m) { return function() { dispatch(m, arguments); }; }(method)); } var reporters = []; this.addReporter = function(reporter) { reporters.push(reporter); }; return this; function dispatch(method, args) { for (var i = 0; i < reporters.length; i++) { var reporter = reporters[i]; if (reporter[method]) { reporter[method].apply(reporter, args); } } } } return ReportDispatcher; }; getJasmineRequireObj().SpyRegistry = function(j$) { function SpyRegistry(options) { options = options || {}; var currentSpies = options.currentSpies || function() { return []; }; this.spyOn = function(obj, methodName) { if (j$.util.isUndefined(obj)) { throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()'); } if (j$.util.isUndefined(methodName)) { throw new Error('No method name supplied'); } if (j$.util.isUndefined(obj[methodName])) { throw new Error(methodName + '() method does not exist'); } if (obj[methodName] && j$.isSpy(obj[methodName])) { //TODO?: should this return the current spy? Downside: may cause user confusion about spy state throw new Error(methodName + ' has already been spied upon'); } var spy = j$.createSpy(methodName, obj[methodName]); currentSpies().push({ spy: spy, baseObj: obj, methodName: methodName, originalValue: obj[methodName] }); obj[methodName] = spy; return spy; }; this.clearSpies = function() { var spies = currentSpies(); for (var i = 0; i < spies.length; i++) { var spyEntry = spies[i]; spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; } }; } return SpyRegistry; }; getJasmineRequireObj().SpyStrategy = function() { function SpyStrategy(options) { options = options || {}; var identity = options.name || 'unknown', originalFn = options.fn || function() {}, getSpy = options.getSpy || function() {}, plan = function() {}; this.identity = function() { return identity; }; this.exec = function() { return plan.apply(this, arguments); }; this.callThrough = function() { plan = originalFn; return getSpy(); }; this.returnValue = function(value) { plan = function() { return value; }; return getSpy(); }; this.returnValues = function() { var values = Array.prototype.slice.call(arguments); plan = function () { return values.shift(); }; return getSpy(); }; this.throwError = function(something) { var error = (something instanceof Error) ? something : new Error(something); plan = function() { throw error; }; return getSpy(); }; this.callFake = function(fn) { plan = fn; return getSpy(); }; this.stub = function(fn) { plan = function() {}; return getSpy(); }; } return SpyStrategy; }; getJasmineRequireObj().Suite = function() { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.onStart = attrs.onStart || function() {}; this.resultCallback = attrs.resultCallback || function() {}; this.clearStack = attrs.clearStack || function(fn) {fn();}; this.expectationFactory = attrs.expectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; this.runnablesExplictlySetGetter = attrs.runnablesExplictlySetGetter || function() {}; this.beforeFns = []; this.afterFns = []; this.beforeAllFns = []; this.afterAllFns = []; this.queueRunner = attrs.queueRunner || function() {}; this.disabled = false; this.children = []; this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [] }; } Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Suite.prototype.getFullName = function() { var fullName = this.description; for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { if (parentSuite.parentSuite) { fullName = parentSuite.description + ' ' + fullName; } } return fullName; }; Suite.prototype.disable = function() { this.disabled = true; }; Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift(fn); }; Suite.prototype.beforeAll = function(fn) { this.beforeAllFns.push(fn); }; Suite.prototype.afterEach = function(fn) { this.afterFns.unshift(fn); }; Suite.prototype.afterAll = function(fn) { this.afterAllFns.push(fn); }; Suite.prototype.addChild = function(child) { this.children.push(child); }; Suite.prototype.status = function() { if (this.disabled) { return 'disabled'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'finished'; } }; Suite.prototype.execute = function(onComplete) { var self = this; this.onStart(this); if (this.disabled) { complete(); return; } var allFns = []; for (var i = 0; i < this.children.length; i++) { allFns.push(wrapChildAsAsync(this.children[i])); } if (this.isExecutable()) { allFns = this.beforeAllFns.concat(allFns); allFns = allFns.concat(this.afterAllFns); } this.queueRunner({ queueableFns: allFns, onComplete: complete, userContext: this.sharedUserContext(), onException: function() { self.onException.apply(self, arguments); } }); function complete() { self.result.status = self.status(); self.resultCallback(self.result); if (onComplete) { onComplete(); } } function wrapChildAsAsync(child) { return { fn: function(done) { child.execute(done); } }; } }; Suite.prototype.isExecutable = function() { var runnablesExplicitlySet = this.runnablesExplictlySetGetter(); return !runnablesExplicitlySet && hasExecutableChild(this.children); }; Suite.prototype.sharedUserContext = function() { if (!this.sharedContext) { this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {}; } return this.sharedContext; }; Suite.prototype.clonedSharedUserContext = function() { return clone(this.sharedUserContext()); }; Suite.prototype.onException = function() { if(isAfterAll(this.children)) { var data = { matcherName: '', passed: false, expected: '', actual: '', error: arguments[0] }; this.result.failedExpectations.push(this.expectationResultFactory(data)); } else { for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; child.onException.apply(child, arguments); } } }; Suite.prototype.addExpectationResult = function () { if(isAfterAll(this.children) && isFailure(arguments)){ var data = arguments[1]; this.result.failedExpectations.push(this.expectationResultFactory(data)); } else { for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; child.addExpectationResult.apply(child, arguments); } } }; function isAfterAll(children) { return children && children[0].result.status; } function isFailure(args) { return !args[0]; } function hasExecutableChild(children) { var foundActive = false; for (var i = 0; i < children.length; i++) { if (children[i].isExecutable()) { foundActive = true; break; } } return foundActive; } function clone(obj) { var clonedObj = {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { clonedObj[prop] = obj[prop]; } } return clonedObj; } return Suite; }; if (typeof window == void 0 && typeof exports == 'object') { exports.Suite = jasmineRequire.Suite; } getJasmineRequireObj().Timer = function() { var defaultNow = (function(Date) { return function() { return new Date().getTime(); }; })(Date); function Timer(options) { options = options || {}; var now = options.now || defaultNow, startTime; this.start = function() { startTime = now(); }; this.elapsed = function() { return now() - startTime; }; } return Timer; }; getJasmineRequireObj().Any = function() { function Any(expectedObject) { this.expectedObject = expectedObject; } Any.prototype.asymmetricMatch = function(other) { if (this.expectedObject == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedObject == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedObject == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedObject == Object) { return typeof other == 'object'; } if (this.expectedObject == Boolean) { return typeof other == 'boolean'; } return other instanceof this.expectedObject; }; Any.prototype.jasmineToString = function() { return ''; }; return Any; }; getJasmineRequireObj().Anything = function(j$) { function Anything() {} Anything.prototype.asymmetricMatch = function(other) { return !j$.util.isUndefined(other) && other !== null; }; Anything.prototype.jasmineToString = function() { return ''; }; return Anything; }; getJasmineRequireObj().ArrayContaining = function(j$) { function ArrayContaining(sample) { this.sample = sample; } ArrayContaining.prototype.asymmetricMatch = function(other) { var className = Object.prototype.toString.call(this.sample); if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); } for (var i = 0; i < this.sample.length; i++) { var item = this.sample[i]; if (!j$.matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayContaining.prototype.jasmineToString = function () { return ''; }; return ArrayContaining; }; getJasmineRequireObj().ObjectContaining = function(j$) { function ObjectContaining(sample) { this.sample = sample; } ObjectContaining.prototype.asymmetricMatch = function(other) { if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } for (var property in this.sample) { if (!Object.prototype.hasOwnProperty.call(other, property) || !j$.matchersUtil.equals(this.sample[property], other[property])) { return false; } } return true; }; ObjectContaining.prototype.jasmineToString = function() { return ''; }; return ObjectContaining; }; getJasmineRequireObj().StringMatching = function(j$) { function StringMatching(expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error('Expected is not a String or a RegExp'); } this.regexp = new RegExp(expected); } StringMatching.prototype.asymmetricMatch = function(other) { return this.regexp.test(other); }; StringMatching.prototype.jasmineToString = function() { return ''; }; return StringMatching; }; getJasmineRequireObj().matchersUtil = function(j$) { // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? return { equals: function(a, b, customTesters) { customTesters = customTesters || []; return eq(a, b, [], [], customTesters); }, contains: function(haystack, needle, customTesters) { customTesters = customTesters || []; if ((Object.prototype.toString.apply(haystack) === '[object Array]') || (!!haystack && !haystack.indexOf)) { for (var i = 0; i < haystack.length; i++) { if (eq(haystack[i], needle, [], [], customTesters)) { return true; } } return false; } return !!haystack && haystack.indexOf(needle) >= 0; }, buildFailureMessage: function() { var args = Array.prototype.slice.call(arguments, 0), matcherName = args[0], isNot = args[1], actual = args[2], expected = args.slice(3), englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); var message = 'Expected ' + j$.pp(actual) + (isNot ? ' not ' : ' ') + englishyPredicate; if (expected.length > 0) { for (var i = 0; i < expected.length; i++) { if (i > 0) { message += ','; } message += ' ' + j$.pp(expected[i]); } } return message + '.'; } }; function isAsymmetric(obj) { return obj && j$.isA_('Function', obj.asymmetricMatch); } function asymmetricMatch(a, b) { var asymmetricA = isAsymmetric(a), asymmetricB = isAsymmetric(b); if (asymmetricA && asymmetricB) { return undefined; } if (asymmetricA) { return a.asymmetricMatch(b); } if (asymmetricB) { return b.asymmetricMatch(a); } } // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) function eq(a, b, aStack, bStack, customTesters) { var result = true; var asymmetricResult = asymmetricMatch(a, b); if (!j$.util.isUndefined(asymmetricResult)) { return asymmetricResult; } for (var i = 0; i < customTesters.length; i++) { var customTesterResult = customTesters[i](a, b); if (!j$.util.isUndefined(customTesterResult)) { return customTesterResult; } } if (a instanceof Error && b instanceof Error) { return a.message == b.message; } // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) { return a !== 0 || 1 / a == 1 / b; } // A strict comparison is necessary because `null == undefined`. if (a === null || b === null) { return a === b; } var className = Object.prototype.toString.call(a); if (className != Object.prototype.toString.call(b)) { return false; } switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return a == String(b); case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a == +b; // RegExps are compared by their source patterns and flags. case '[object RegExp]': return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') { return false; } var aIsDomNode = j$.isDomNode(a); var bIsDomNode = j$.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode if (a.isEqualNode) { return a.isEqualNode(b); } // IE8 doesn't support isEqualNode, try to use outerHTML && innerText var aIsElement = a instanceof Element; var bIsElement = b instanceof Element; if (aIsElement && bIsElement) { return a.outerHTML == b.outerHTML; } if (aIsElement || bIsElement) { return false; } return a.innerText == b.innerText && a.textContent == b.textContent; } if (aIsDomNode || bIsDomNode) { return false; } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) { return bStack[length] == b; } } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size = 0; // Recursively compare objects and arrays. // Compare array lengths to determine if a deep comparison is necessary. if (className == '[object Array]' && a.length !== b.length) { result = false; } if (result) { // Objects with different constructors are not equivalent, but `Object`s // from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) && isFunction(bCtor) && (bCtor instanceof bCtor))) { return false; } // Deep compare objects. for (var key in a) { if (has(a, key)) { // Count the expected number of properties. size++; // Deep compare each member. if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; } } } // Ensure that both objects contain the same number of properties. if (result) { for (key in b) { if (has(b, key) && !(size--)) { break; } } result = !size; } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; function has(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); } function isFunction(obj) { return typeof obj === 'function'; } } }; getJasmineRequireObj().toBe = function() { function toBe() { return { compare: function(actual, expected) { return { pass: actual === expected }; } }; } return toBe; }; getJasmineRequireObj().toBeCloseTo = function() { function toBeCloseTo() { return { compare: function(actual, expected, precision) { if (precision !== 0) { precision = precision || 2; } return { pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2) }; } }; } return toBeCloseTo; }; getJasmineRequireObj().toBeDefined = function() { function toBeDefined() { return { compare: function(actual) { return { pass: (void 0 !== actual) }; } }; } return toBeDefined; }; getJasmineRequireObj().toBeFalsy = function() { function toBeFalsy() { return { compare: function(actual) { return { pass: !!!actual }; } }; } return toBeFalsy; }; getJasmineRequireObj().toBeGreaterThan = function() { function toBeGreaterThan() { return { compare: function(actual, expected) { return { pass: actual > expected }; } }; } return toBeGreaterThan; }; getJasmineRequireObj().toBeLessThan = function() { function toBeLessThan() { return { compare: function(actual, expected) { return { pass: actual < expected }; } }; } return toBeLessThan; }; getJasmineRequireObj().toBeNaN = function(j$) { function toBeNaN() { return { compare: function(actual) { var result = { pass: (actual !== actual) }; if (result.pass) { result.message = 'Expected actual not to be NaN.'; } else { result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; }; } return result; } }; } return toBeNaN; }; getJasmineRequireObj().toBeNull = function() { function toBeNull() { return { compare: function(actual) { return { pass: actual === null }; } }; } return toBeNull; }; getJasmineRequireObj().toBeTruthy = function() { function toBeTruthy() { return { compare: function(actual) { return { pass: !!actual }; } }; } return toBeTruthy; }; getJasmineRequireObj().toBeUndefined = function() { function toBeUndefined() { return { compare: function(actual) { return { pass: void 0 === actual }; } }; } return toBeUndefined; }; getJasmineRequireObj().toContain = function() { function toContain(util, customEqualityTesters) { customEqualityTesters = customEqualityTesters || []; return { compare: function(actual, expected) { return { pass: util.contains(actual, expected, customEqualityTesters) }; } }; } return toContain; }; getJasmineRequireObj().toEqual = function() { function toEqual(util, customEqualityTesters) { customEqualityTesters = customEqualityTesters || []; return { compare: function(actual, expected) { var result = { pass: false }; result.pass = util.equals(actual, expected, customEqualityTesters); return result; } }; } return toEqual; }; getJasmineRequireObj().toHaveBeenCalled = function(j$) { function toHaveBeenCalled() { return { compare: function(actual) { var result = {}; if (!j$.isSpy(actual)) { throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); } if (arguments.length > 1) { throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); } result.pass = actual.calls.any(); result.message = result.pass ? 'Expected spy ' + actual.and.identity() + ' not to have been called.' : 'Expected spy ' + actual.and.identity() + ' to have been called.'; return result; } }; } return toHaveBeenCalled; }; getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { function toHaveBeenCalledWith(util, customEqualityTesters) { return { compare: function() { var args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1), result = { pass: false }; if (!j$.isSpy(actual)) { throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); } if (!actual.calls.any()) { result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; }; return result; } if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) { result.pass = true; result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; }; } else { result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; }; } return result; } }; } return toHaveBeenCalledWith; }; getJasmineRequireObj().toMatch = function(j$) { function toMatch() { return { compare: function(actual, expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error('Expected is not a String or a RegExp'); } var regexp = new RegExp(expected); return { pass: regexp.test(actual) }; } }; } return toMatch; }; getJasmineRequireObj().toThrow = function(j$) { function toThrow(util) { return { compare: function(actual, expected) { var result = { pass: false }, threw = false, thrown; if (typeof actual != 'function') { throw new Error('Actual is not a Function'); } try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { result.message = 'Expected function to throw an exception.'; return result; } if (arguments.length == 1) { result.pass = true; result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; }; return result; } if (util.equals(thrown, expected)) { result.pass = true; result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; }; } else { result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; }; } return result; } }; } return toThrow; }; getJasmineRequireObj().toThrowError = function(j$) { function toThrowError (util) { return { compare: function(actual) { var threw = false, pass = {pass: true}, fail = {pass: false}, thrown; if (typeof actual != 'function') { throw new Error('Actual is not a Function'); } var errorMatcher = getMatcher.apply(null, arguments); try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { fail.message = 'Expected function to throw an Error.'; return fail; } if (!(thrown instanceof Error)) { fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; }; return fail; } if (errorMatcher.hasNoSpecifics()) { pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.'; return pass; } if (errorMatcher.matches(thrown)) { pass.message = function() { return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.'; }; return pass; } else { fail.message = function() { return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.'; }; return fail; } } }; function getMatcher() { var expected = null, errorType = null; if (arguments.length == 2) { expected = arguments[1]; if (isAnErrorType(expected)) { errorType = expected; expected = null; } } else if (arguments.length > 2) { errorType = arguments[1]; expected = arguments[2]; if (!isAnErrorType(errorType)) { throw new Error('Expected error type is not an Error.'); } } if (expected && !isStringOrRegExp(expected)) { if (errorType) { throw new Error('Expected error message is not a string or RegExp.'); } else { throw new Error('Expected is not an Error, string, or RegExp.'); } } function messageMatch(message) { if (typeof expected == 'string') { return expected == message; } else { return expected.test(message); } } return { errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception', thrownDescription: function(thrown) { var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', thrownMessage = ''; if (expected) { thrownMessage = ' with message ' + j$.pp(thrown.message); } return thrownName + thrownMessage; }, messageDescription: function() { if (expected === null) { return ''; } else if (expected instanceof RegExp) { return ' with a message matching ' + j$.pp(expected); } else { return ' with message ' + j$.pp(expected); } }, hasNoSpecifics: function() { return expected === null && errorType === null; }, matches: function(error) { return (errorType === null || error.constructor === errorType) && (expected === null || messageMatch(error.message)); } }; } function isStringOrRegExp(potential) { return potential instanceof RegExp || (typeof potential == 'string'); } function isAnErrorType(type) { if (typeof type !== 'function') { return false; } var Surrogate = function() {}; Surrogate.prototype = type.prototype; return (new Surrogate()) instanceof Error; } } return toThrowError; }; getJasmineRequireObj().interface = function(jasmine, env) { var jasmineInterface = { describe: function(description, specDefinitions) { return env.describe(description, specDefinitions); }, xdescribe: function(description, specDefinitions) { return env.xdescribe(description, specDefinitions); }, fdescribe: function(description, specDefinitions) { return env.fdescribe(description, specDefinitions); }, it: function() { return env.it.apply(env, arguments); }, xit: function() { return env.xit.apply(env, arguments); }, fit: function() { return env.fit.apply(env, arguments); }, beforeEach: function() { return env.beforeEach.apply(env, arguments); }, afterEach: function() { return env.afterEach.apply(env, arguments); }, beforeAll: function() { return env.beforeAll.apply(env, arguments); }, afterAll: function() { return env.afterAll.apply(env, arguments); }, expect: function(actual) { return env.expect(actual); }, pending: function() { return env.pending.apply(env, arguments); }, fail: function() { return env.fail.apply(env, arguments); }, spyOn: function(obj, methodName) { return env.spyOn(obj, methodName); }, jsApiReporter: new jasmine.JsApiReporter({ timer: new jasmine.Timer() }), jasmine: jasmine }; jasmine.addCustomEqualityTester = function(tester) { env.addCustomEqualityTester(tester); }; jasmine.addMatchers = function(matchers) { return env.addMatchers(matchers); }; jasmine.clock = function() { return env.clock; }; return jasmineInterface; }; getJasmineRequireObj().version = function() { return '2.2.0'; }; ././@LongLink0000644000000000000000000000016100000000000011601 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-2.2.0/boot.js./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-20000644000015600001650000001150112705421114034077 0ustar jenkinsjenkins/** Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. [jasmine-gem]: http://github.com/pivotal/jasmine-gem */ (function() { /** * ## Require & Instantiate * * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. */ window.jasmine = jasmineRequire.core(jasmineRequire); /** * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. */ jasmineRequire.html(jasmine); /** * Create the Jasmine environment. This is used to run all specs in a project. */ var env = jasmine.getEnv(); /** * ## The Global Interface * * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. */ var jasmineInterface = jasmineRequire.interface(jasmine, env); /** * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. */ if (typeof window == "undefined" && typeof exports == "object") { extend(exports, jasmineInterface); } else { extend(window, jasmineInterface); } /** * ## Runner Parameters * * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. */ var queryString = new jasmine.QueryString({ getWindowLocation: function() { return window.location; } }); var catchingExceptions = queryString.getParam("catch"); env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); /** * ## Reporters * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). */ var htmlReporter = new jasmine.HtmlReporter({ env: env, onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); }, addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, getContainer: function() { return document.body; }, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, timer: new jasmine.Timer() }); /** * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. */ env.addReporter(jasmineInterface.jsApiReporter); env.addReporter(htmlReporter); /** * Filter which specs will be run by matching the start of the full name against the `spec` query param. */ var specFilter = new jasmine.HtmlSpecFilter({ filterString: function() { return queryString.getParam("spec"); } }); env.specFilter = function(spec) { return specFilter.matches(spec.getFullName()); }; /** * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. */ window.setTimeout = window.setTimeout; window.setInterval = window.setInterval; window.clearTimeout = window.clearTimeout; window.clearInterval = window.clearInterval; /** * ## Execution * * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. */ var currentWindowOnload = window.onload; window.onload = function() { if (currentWindowOnload) { currentWindowOnload(); } htmlReporter.initialize(); env.execute(); }; /** * Helper function for readability above. */ function extend(destination, source) { for (var property in source) destination[property] = source[property]; return destination; } }()); ././@LongLink0000644000000000000000000000016400000000000011604 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-2.2.0/console.js./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-20000644000015600001650000001215012705421114034100 0ustar jenkinsjenkins/* Copyright (c) 2008-2015 Pivotal Labs 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. */ function getJasmineRequireObj() { if (typeof module !== 'undefined' && module.exports) { return exports; } else { window.jasmineRequire = window.jasmineRequire || {}; return window.jasmineRequire; } } getJasmineRequireObj().console = function(jRequire, j$) { j$.ConsoleReporter = jRequire.ConsoleReporter(); }; getJasmineRequireObj().ConsoleReporter = function() { var noopTimer = { start: function(){}, elapsed: function(){ return 0; } }; function ConsoleReporter(options) { var print = options.print, showColors = options.showColors || false, onComplete = options.onComplete || function() {}, timer = options.timer || noopTimer, specCount, failureCount, failedSpecs = [], pendingCount, ansi = { green: '\x1B[32m', red: '\x1B[31m', yellow: '\x1B[33m', none: '\x1B[0m' }, failedSuites = []; print('ConsoleReporter is deprecated and will be removed in a future version.'); this.jasmineStarted = function() { specCount = 0; failureCount = 0; pendingCount = 0; print('Started'); printNewline(); timer.start(); }; this.jasmineDone = function() { printNewline(); for (var i = 0; i < failedSpecs.length; i++) { specFailureDetails(failedSpecs[i]); } if(specCount > 0) { printNewline(); var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' + failureCount + ' ' + plural('failure', failureCount); if (pendingCount) { specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount); } print(specCounts); } else { print('No specs found'); } printNewline(); var seconds = timer.elapsed() / 1000; print('Finished in ' + seconds + ' ' + plural('second', seconds)); printNewline(); for(i = 0; i < failedSuites.length; i++) { suiteFailureDetails(failedSuites[i]); } onComplete(failureCount === 0); }; this.specDone = function(result) { specCount++; if (result.status == 'pending') { pendingCount++; print(colored('yellow', '*')); return; } if (result.status == 'passed') { print(colored('green', '.')); return; } if (result.status == 'failed') { failureCount++; failedSpecs.push(result); print(colored('red', 'F')); } }; this.suiteDone = function(result) { if (result.failedExpectations && result.failedExpectations.length > 0) { failureCount++; failedSuites.push(result); } }; return this; function printNewline() { print('\n'); } function colored(color, str) { return showColors ? (ansi[color] + str + ansi.none) : str; } function plural(str, count) { return count == 1 ? str : str + 's'; } function repeat(thing, times) { var arr = []; for (var i = 0; i < times; i++) { arr.push(thing); } return arr; } function indent(str, spaces) { var lines = (str || '').split('\n'); var newArr = []; for (var i = 0; i < lines.length; i++) { newArr.push(repeat(' ', spaces).join('') + lines[i]); } return newArr.join('\n'); } function specFailureDetails(result) { printNewline(); print(result.fullName); for (var i = 0; i < result.failedExpectations.length; i++) { var failedExpectation = result.failedExpectations[i]; printNewline(); print(indent(failedExpectation.message, 2)); print(indent(failedExpectation.stack, 2)); } printNewline(); } function suiteFailureDetails(result) { for (var i = 0; i < result.failedExpectations.length; i++) { printNewline(); print(colored('red', 'An error was thrown in an afterAll')); printNewline(); print(colored('red', 'AfterAll ' + result.failedExpectations[i].message)); } printNewline(); } } return ConsoleReporter; }; ././@LongLink0000644000000000000000000000016500000000000011605 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-2.2.0/jasmine.css./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-20000644000015600001650000004623012705421114034106 0ustar jenkinsjenkinsbody { overflow-y: scroll; } .jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } .jasmine_html-reporter a { text-decoration: none; } .jasmine_html-reporter a:hover { text-decoration: underline; } .jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } .jasmine_html-reporter .banner, .jasmine_html-reporter .symbol-summary, .jasmine_html-reporter .summary, .jasmine_html-reporter .result-message, .jasmine_html-reporter .spec .description, .jasmine_html-reporter .spec-detail .description, .jasmine_html-reporter .alert .bar, .jasmine_html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } .jasmine_html-reporter .banner { position: relative; } .jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } .jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; } .jasmine_html-reporter .banner .duration { position: absolute; right: 14px; top: 6px; } .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } .jasmine_html-reporter .version { color: #aaa; } .jasmine_html-reporter .banner { margin-top: 14px; } .jasmine_html-reporter .duration { color: #aaa; float: right; } .jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } .jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } .jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; } .jasmine_html-reporter .symbol-summary li.passed:before { color: #007069; content: "\02022"; } .jasmine_html-reporter .symbol-summary li.failed { line-height: 9px; } .jasmine_html-reporter .symbol-summary li.failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; } .jasmine_html-reporter .symbol-summary li.disabled { font-size: 14px; } .jasmine_html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } .jasmine_html-reporter .symbol-summary li.pending { line-height: 17px; } .jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } .jasmine_html-reporter .symbol-summary li.empty { font-size: 14px; } .jasmine_html-reporter .symbol-summary li.empty:before { color: #ba9d37; content: "\02022"; } .jasmine_html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } .jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } .jasmine_html-reporter .bar.failed { background-color: #ca3a11; } .jasmine_html-reporter .bar.passed { background-color: #007069; } .jasmine_html-reporter .bar.skipped { background-color: #bababa; } .jasmine_html-reporter .bar.errored { background-color: #ca3a11; } .jasmine_html-reporter .bar.menu { background-color: #fff; color: #aaa; } .jasmine_html-reporter .bar.menu a { color: #333; } .jasmine_html-reporter .bar a { color: white; } .jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; } .jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; } .jasmine_html-reporter .running-alert { background-color: #666; } .jasmine_html-reporter .results { margin-top: 14px; } .jasmine_html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } .jasmine_html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } .jasmine_html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } .jasmine_html-reporter.showDetails .summary { display: none; } .jasmine_html-reporter.showDetails #details { display: block; } .jasmine_html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } .jasmine_html-reporter .summary { margin-top: 14px; } .jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } .jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } .jasmine_html-reporter .summary li.passed a { color: #007069; } .jasmine_html-reporter .summary li.failed a { color: #ca3a11; } .jasmine_html-reporter .summary li.empty a { color: #ba9d37; } .jasmine_html-reporter .summary li.pending a { color: #ba9d37; } .jasmine_html-reporter .description + .suite { margin-top: 0; } .jasmine_html-reporter .suite { margin-top: 14px; } .jasmine_html-reporter .suite a { color: #333; } .jasmine_html-reporter .failures .spec-detail { margin-bottom: 28px; } .jasmine_html-reporter .failures .spec-detail .description { background-color: #ca3a11; } .jasmine_html-reporter .failures .spec-detail .description a { color: white; } .jasmine_html-reporter .result-message { padding-top: 14px; color: #333; white-space: pre; } .jasmine_html-reporter .result-message span.result { display: block; } .jasmine_html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; } ././@LongLink0000644000000000000000000000017100000000000011602 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-2.2.0/jasmine-html.js./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-20000644000015600001650000003212712705421114034106 0ustar jenkinsjenkins/* Copyright (c) 2008-2015 Pivotal Labs 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. */ jasmineRequire.html = function(j$) { j$.ResultsNode = jasmineRequire.ResultsNode(); j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); j$.QueryString = jasmineRequire.QueryString(); j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); }; jasmineRequire.HtmlReporter = function(j$) { var noopTimer = { start: function() {}, elapsed: function() { return 0; } }; function HtmlReporter(options) { var env = options.env || {}, getContainer = options.getContainer, createElement = options.createElement, createTextNode = options.createTextNode, onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, timer = options.timer || noopTimer, results = [], specsExecuted = 0, failureCount = 0, pendingSpecCount = 0, htmlReporterMain, symbols, failedSuites = []; this.initialize = function() { clearPrior(); htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'}, createDom('div', {className: 'banner'}, createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}), createDom('span', {className: 'version'}, j$.version) ), createDom('ul', {className: 'symbol-summary'}), createDom('div', {className: 'alert'}), createDom('div', {className: 'results'}, createDom('div', {className: 'failures'}) ) ); getContainer().appendChild(htmlReporterMain); symbols = find('.symbol-summary'); }; var totalSpecsDefined; this.jasmineStarted = function(options) { totalSpecsDefined = options.totalSpecsDefined || 0; timer.start(); }; var summary = createDom('div', {className: 'summary'}); var topResults = new j$.ResultsNode({}, '', null), currentParent = topResults; this.suiteStarted = function(result) { currentParent.addChild(result, 'suite'); currentParent = currentParent.last(); }; this.suiteDone = function(result) { if (result.status == 'failed') { failedSuites.push(result); } if (currentParent == topResults) { return; } currentParent = currentParent.parent; }; this.specStarted = function(result) { currentParent.addChild(result, 'spec'); }; var failures = []; this.specDone = function(result) { if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') { console.error('Spec \'' + result.fullName + '\' has no expectations.'); } if (result.status != 'disabled') { specsExecuted++; } symbols.appendChild(createDom('li', { className: noExpectations(result) ? 'empty' : result.status, id: 'spec_' + result.id, title: result.fullName } )); if (result.status == 'failed') { failureCount++; var failure = createDom('div', {className: 'spec-detail failed'}, createDom('div', {className: 'description'}, createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName) ), createDom('div', {className: 'messages'}) ); var messages = failure.childNodes[1]; for (var i = 0; i < result.failedExpectations.length; i++) { var expectation = result.failedExpectations[i]; messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message)); messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack)); } failures.push(failure); } if (result.status == 'pending') { pendingSpecCount++; } }; this.jasmineDone = function() { var banner = find('.banner'); banner.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); var alert = find('.alert'); alert.appendChild(createDom('span', { className: 'exceptions' }, createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions'), createDom('input', { className: 'raise', id: 'raise-exceptions', type: 'checkbox' }) )); var checkbox = find('#raise-exceptions'); checkbox.checked = !env.catchingExceptions(); checkbox.onclick = onRaiseExceptionsClick; if (specsExecuted < totalSpecsDefined) { var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; alert.appendChild( createDom('span', {className: 'bar skipped'}, createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage) ) ); } var statusBarMessage = ''; var statusBarClassName = 'bar '; if (totalSpecsDefined > 0) { statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount); if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); } statusBarClassName += (failureCount > 0) ? 'failed' : 'passed'; } else { statusBarClassName += 'skipped'; statusBarMessage += 'No specs found'; } alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage)); for(i = 0; i < failedSuites.length; i++) { var failedSuite = failedSuites[i]; for(var j = 0; j < failedSuite.failedExpectations.length; j++) { var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message; var errorBarClassName = 'bar errored'; alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage)); } } var results = find('.results'); results.appendChild(summary); summaryList(topResults, summary); function summaryList(resultsTree, domParent) { var specListNode; for (var i = 0; i < resultsTree.children.length; i++) { var resultNode = resultsTree.children[i]; if (resultNode.type == 'suite') { var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id}, createDom('li', {className: 'suite-detail'}, createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description) ) ); summaryList(resultNode, suiteListNode); domParent.appendChild(suiteListNode); } if (resultNode.type == 'spec') { if (domParent.getAttribute('class') != 'specs') { specListNode = createDom('ul', {className: 'specs'}); domParent.appendChild(specListNode); } var specDescription = resultNode.result.description; if(noExpectations(resultNode.result)) { specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; } if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; } specListNode.appendChild( createDom('li', { className: resultNode.result.status, id: 'spec-' + resultNode.result.id }, createDom('a', {href: specHref(resultNode.result)}, specDescription) ) ); } } } if (failures.length) { alert.appendChild( createDom('span', {className: 'menu bar spec-list'}, createDom('span', {}, 'Spec List | '), createDom('a', {className: 'failures-menu', href: '#'}, 'Failures'))); alert.appendChild( createDom('span', {className: 'menu bar failure-list'}, createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'), createDom('span', {}, ' | Failures '))); find('.failures-menu').onclick = function() { setMenuModeTo('failure-list'); }; find('.spec-list-menu').onclick = function() { setMenuModeTo('spec-list'); }; setMenuModeTo('failure-list'); var failureNode = find('.failures'); for (var i = 0; i < failures.length; i++) { failureNode.appendChild(failures[i]); } } }; return this; function find(selector) { return getContainer().querySelector('.jasmine_html-reporter ' + selector); } function clearPrior() { // return the reporter var oldReporter = find(''); if(oldReporter) { getContainer().removeChild(oldReporter); } } function createDom(type, attrs, childrenVarArgs) { var el = createElement(type); for (var i = 2; i < arguments.length; i++) { var child = arguments[i]; if (typeof child === 'string') { el.appendChild(createTextNode(child)); } else { if (child) { el.appendChild(child); } } } for (var attr in attrs) { if (attr == 'className') { el[attr] = attrs[attr]; } else { el.setAttribute(attr, attrs[attr]); } } return el; } function pluralize(singular, count) { var word = (count == 1 ? singular : singular + 's'); return '' + count + ' ' + word; } function specHref(result) { return addToExistingQueryString('spec', result.fullName); } function defaultQueryString(key, value) { return '?' + key + '=' + value; } function setMenuModeTo(mode) { htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); } function noExpectations(result) { return (result.failedExpectations.length + result.passedExpectations.length) === 0 && result.status === 'passed'; } } return HtmlReporter; }; jasmineRequire.HtmlSpecFilter = function() { function HtmlSpecFilter(options) { var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); var filterPattern = new RegExp(filterString); this.matches = function(specName) { return filterPattern.test(specName); }; } return HtmlSpecFilter; }; jasmineRequire.ResultsNode = function() { function ResultsNode(result, type, parent) { this.result = result; this.type = type; this.parent = parent; this.children = []; this.addChild = function(result, type) { this.children.push(new ResultsNode(result, type, this)); }; this.last = function() { return this.children[this.children.length - 1]; }; } return ResultsNode; }; jasmineRequire.QueryString = function() { function QueryString(options) { this.navigateWithNewParam = function(key, value) { options.getWindowLocation().search = this.fullStringWithNewParam(key, value); }; this.fullStringWithNewParam = function(key, value) { var paramMap = queryStringToParamMap(); paramMap[key] = value; return toQueryString(paramMap); }; this.getParam = function(key) { return queryStringToParamMap()[key]; }; return this; function toQueryString(paramMap) { var qStrPairs = []; for (var prop in paramMap) { qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])); } return '?' + qStrPairs.join('&'); } function queryStringToParamMap() { var paramStr = options.getWindowLocation().search.substring(1), params = [], paramMap = {}; if (paramStr.length > 0) { params = paramStr.split('&'); for (var i = 0; i < params.length; i++) { var p = params[i].split('='); var value = decodeURIComponent(p[1]); if (value === 'true' || value === 'false') { value = JSON.parse(value); } paramMap[decodeURIComponent(p[0])] = value; } } return paramMap; } } return QueryString; }; ././@LongLink0000644000000000000000000000017500000000000011606 Lustar rootroot./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-2.2.0/jasmine_favicon.png./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/third_party/jasmine/lib/jasmine-20000644000015600001650000000271612705421114034107 0ustar jenkinsjenkinsPNG  IHDR DOPLTEU@fUI`M]UN[UPZQUQYURURXURXUSSUSWSQUSSUSRUSRTRTRUTRUTUTSUSTSUTSUTUTSUTSUTSUSUTSUTTSUTSSTTTTUTTUTSTTSTTSTTSTSTSTTSTTSTSTTSTTSTSTTSUTTUTTUTTUTUTTUTTUTUTUTTUTTUTUUTSTTSTTSTSTTTTTTTTTTTTTTTTTTTTTTTTT0tRNS  !"%'()+,-.1345678:;=>?@ABCEFGHJLMNOPTUWXYZ[\]^_`bcdefgjklmnqstwz{|}~XFjIDATx^m[QTSFEZdD=$;B {!;DDv(% R 9\J9:s~{mS;'ZP F-+r%33U( Uu@[v)wy4jCeR Tzds? d.ɶ *% yX$ˠv(A)Sy9B A[pJ~ewe$t^H=B&rLp0H'P2X,G-# UNF'tts ,0KP"/$IrO?dIZOSe$/%4=%[JX^N4QZdb݃9{ ̓/| TѲC*ʅnRt0?XD`o,wIsܟ`Usc -YT:HHթx#)@^^ vΛΑk|IqW@}[@ g p"H lzVeeeL~ |LQ"[Ven܄q(PJoųeK# nW[NtXZ,u[{IENDB`./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/specs/0000755000015600001650000000000012705421114026761 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/tests/unit/specs/app-specs.js0000644000015600001650000000056012705421114031213 0ustar jenkinsjenkinsdescribe("App", function() { var application; var MockedUIContext = function() {} MockedUIContext.prototype.init = function() { } beforeEach(function() { application = new Application(MockedUIContext); }); it("should have a proper initialized flag", function() { application.init(); expect(application.initialized()).toBeTruthy(); }); }); ./share/qtcreator/templates/wizards/ubuntu/html5-simple/README0000644000015600001650000000264612705421114024413 0ustar jenkinsjenkinsHTML5 Application Template -------------------------- www/ : the source files of the template application, tests/ : the application functional and unit tests package.json : grunt package file used to install dependencies Gruntfile.js : grunt file for the jasmine unit tests Unit Tests: ----------- To run the unit tests, from the project folder: - Have nodejs and grunt installed on your system, sudo apt-get install nodejs - Install the node dependencies: npm install - Run the tests: npm test Autopilot (functional) tests ---------------------------- - Make sure that you have oxide-chromedriver and selenium installed: sudo apt-get install oxideqt-chromedriver sudo apt-add-repository ppa:canonical-platform-qa/selenium sudo apt-get update sudo apt-get install python3-selenium - Run the tests: cd tests/autopilot autopilot3 list application_tests autopilot3 run application_tests Notes: ---------------------------- - The .desktop file uses the executable 'webapp-container' as an HTML5 application launcher. Previously the executable 'ubuntu-html5-app-launcher' was used for this purpose, it has now been deprecated. For any application targetting the application framework 14.10 and apparmor policy 1.2 though, one can and should still use 'ubuntu-html5-app-launcher'. The Exec line in the .desktop file of the current application then becomes: Exec=ubuntu-html5-app-launcher $@ --www=www --inspector ./share/qtcreator/templates/wizards/ubuntu/html5-simple/package.json0000644000015600001650000000044612705421114026015 0ustar jenkinsjenkins{ "name": "my-app", "version": "0.1.0", "devDependencies": { "grunt": "^0.4.5", "grunt-contrib-jasmine": "^0.8.2", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.5.0" }, "scripts": { "test": "node -e \"require('grunt').tasks(['jasmine']);\"" } } ./share/qtcreator/templates/wizards/ubuntu/html5-simple/displayName.ubuntuhtmlproject0000644000015600001650000000206612705421114031515 0ustar jenkinsjenkinsimport QmlProject 1.1 Project { mainFile: "www/index.html" /* Include .qml, .js, and image files from current directory and subdirectories */ QmlFiles { directory: "." } JavaScriptFiles { directory: "." } ImageFiles { directory: "." } Files { filter: "*.desktop" } Files { filter: ".excludes" } Files { filter: "www/*.html" } Files { filter: "Makefile" } Files { filter: "*.apparmor" } Files { filter: "*.json" } Files { directory: "www" filter: "*" } Files { directory: "www/img/" filter: "*" } Files { directory: "www/css/" filter: "*" } Files { directory: "www/js/" filter: "*" } Files { directory: "tests/" filter: "*" } Files { directory: "debian" filter: "*" } /* List of plugin directories passed to QML runtime */ importPaths: [ "." ,"/usr/bin","/usr/lib/x86_64-linux-gnu/qt5/qml" ] } ./share/qtcreator/templates/wizards/ubuntu/html5-simple/displayName.apparmor0000644000015600001650000000032712705421114027536 0ustar jenkinsjenkins{ "template": "ubuntu-webapp", "policy_groups": [ "networking", "audio", "content_exchange", "video", "webview" ], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/html5-simple/Gruntfile.js0000644000015600001650000000044712705421114026025 0ustar jenkinsjenkinsmodule.exports = function(grunt) { grunt.initConfig({ jasmine : { src : 'www/js/application.js', options: { specs : 'tests/unit/specs/*specs.js' } } }); grunt.loadNpmTasks('grunt-contrib-jasmine'); grunt.registerTask('default', 'jasmine'); }; ./share/qtcreator/templates/wizards/ubuntu/html5-simple/displayName.desktop0000644000015600001650000000024312705421114027363 0ustar jenkinsjenkins[Desktop Entry] Name=%ClickHookName% Exec=webapp-container --inspector ./www/index.html Icon=%ProjectName%.png Terminal=false Type=Application X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/html5-simple/displayName.png0000644000015600001650000011407012705421114026502 0ustar jenkinsjenkinsPNG  IHDR\rfbKGD pHYs B(xtIME 22 IDATx[r\gQ(poGaD% @@UPŧZ'7-D($@a7̕k|{@#7bgf|H;j}I6WnFșufpgUޅmԼq;V<9N2s%c|oVMNZԫjHO^x'k#@d=SEkuMeϝON.ffykd뢴kas4O]COOO8y=ڂ5 87;N3ǬVa@r׉;S}f|.2gSה.p{ Ùxݷ;n8 Y*d}-3 `iɑ k|1<==MdyuzͅXiys'HIrSdYZYו7+33a‚3h b[??+̃RϽVevo;߱ӢKxsCZM}^=Bs扚'~FLxZîZr.n{tt4sԿe0V;z/'⺟|O= tl%3&fL*da epfFk$nc8אi<y][Vi3=3A}zAҧHICT_f)꿹 '/n"Zk/_t>67q R,s^SWY=b$trn=C+bJn Olfu+@@9YfT\|~ @)]m~ZvQEZ$D6a899|߹3Z/ɇ0nlNk0A5~>|Y/Ȏw.]3L0R!B̀e_=~sml6lr]0.q !ZJ{yɻawחˊ*LϲDW-,櫛'oi$)6#=l)憪NxhVb^_CX$G!W-{ `| A!{ٽk`*OGdpz '/!Lsst!g9lIZZ^oq[2զ@6v noowbl(>dޑx` {<>>].?Kd},NZN;Fzd.2eg͑vxr-i2gGnDv9X 3;07sn fBN)1?3e\k<=dy``krp$t Zw 6dEq||<{"TycD)f1#)6yF*,)eXO&K Fkw2e&%6= 7{ k[93׫& dpdG;@&.lH, 2:d7wGN0ٕOO߾}D1X_'Ed\U5$Kř۩(5HivR`HFwn$̉x2h3Ӳ>x:H>%OQq3'e uvlz#Xpt&+ 3j/ Ys呆46Q_Ѝ{ʛcaAq21"g=<{jT~l@iQcbZ[-Фbw؈=cDY#Ʈ%8\A<)v˃z?@%l]c; Tܥr5,emOOOxT#ia(!Ð$% 2uZ`Yf;/Ҝ`LR^ɸ~>EYӉaV0N',SRCyhTeo`|?E>ʞ1SdI"9DpBfD{ ˾>߉ѳE j05:b'wd)@bFN:{C?SbM4;Sk@Fe>3ʴZY=ѳ.I; ZKyM'oCl}l0"fE]i)wRlr X{7͘ L%S~OKt;9âyw炫/;HɛI46m." ix3%U'-S:%s*`--ߟѢ;Yt~߷oߔk;"gn"7! UP^=SpIҪ'ƃ$Y}N{(b)5d {G6N0z$.On. 2qdtN_C2 pl/V Hz)rA0iƠtb%g9Kw zցY,۷o-iܬ^-kM-e  pPYD̙; R_)l 31/o"I6DQl0.a ğaZW3g=1YS4]lZaמM]yMsIAI.6of,:~c )Zַ5aB i3do f'4߁Wdڻq`1i=DǔӨTgX:Y#<10FHPd5HU3"LyĜ hIصORȔѯh |\ J |Y0>2ґIyxdWF= &bZi v\zSuސr`$3{\- rS%Gx32@+Olo[SٱBϯ^9.qHKϠu5&<3c7>3%5fkemӍ&^ ޙ*(AV7vSΞ;ue35$)SL ʍAwxY} rcGb7[fZCF?-\͎SgNu HeY. &}9eWMqiQg&66uG[4i_\D VAXWfXj8 lHtʲײ Dm(#vqҴv5MDWYo=?I&nQP$&[֝d12[,K5*:;ǜXǷa']H&1SPVcMR:] gik%-ÃzxÞ45Us4x 1 s6!ā)2{ gYOg9Ip}|3ec:Y4qGgc Q5Й WҰ4#:ÉL)iXii[**ɕ E:c3-XʣAs3tĐz֯1$Ȏ =M4a%4o.%UwT -\*d^Vɝ΅i5hh,EΤi` 0.߯\uxSQ3yL(T}벎cLvf+C.Yw_t񝴕fډ)P{7dj|0q32Nf>;;ۓgΤC@j(5Q$,k$ӗO(-Yv]| q bN3 Dɨw\uB5C,خ3tryoYˈ[6ݑL)[XXpS"ND,<\5Y noS2KTtxv$ M$b3)&ɏ9tԥ'Ӎd_\\*PiϻѣqRrMdYǓ `Vޣ 7`0v#TIKL%ρ?;))5YQs^RYw!@G-+LMh an継Huj b͸`K&k\Miy3j z"9g k]ia(^gxZMڳ 0x6ּߌܵUsݷ6%FwAN?fW8LmQsQIM|wi[sy [ug;4=']vr]<?TKA5Ñtno-13=M`KRW=G\z5L6:1JZwGTnIq5xߛQm5ɆLچ%KO-IT=מD,ͩc3=hݼ) dO|;54GNfbO?AQpvO!H\Q5Jj2b4y*7 +V}gw?g|xAe΀a IfSw{v&wN<gV g] sp,fbcgcjv4oD*u6s">-X!|hsJ%#*Xiabr+c$u-WHAJ~ E`}ӿ,:ZkƎ#n35& N=#hь-Sb-OΛ>\ tKBQLJO2W@5aތsr&1%oE;Eb ff~ڦ dS~:>#rgdΡy[KgZaxxxU4"YfwTjn8#SL3g?S%2S!ulja&`Ğw^kX9r䕺Sesib&VJY/s:zsm*@s6HVđg2s(5%f$e~s5x X/jQd6N CF2Qn_><<'?-,0qxHgLdzsjN^KKXs2U,S֒'''{vڿg߆aXC~a%삌1-g*OG,zlLXf5̇y\-I3cJu,J/AnO`bƒ2RWM!\̬{n3>Ale1笜v;Js)C~՝/@KcZxR6Z^'ו hb,=;텵{||f3 7G@Aɫ?b "߾}e2~E?KCMm"r;NSueVs\iA IDAT.0s6e6"N\Y$5G jfXcFz*+͎+eptt4&YQeO]wM_sINYjw`um~N [v~?>45ˣ (?\W{خbff5N.=:' ۡ6sӟ G` L/-G oHkRd91&w4JF$ƐD:ܬv͸Al޷۷ɋ_{$,œ3Z1 m'7tGUf4HQ].wHB[z+vR~^gbےIfVԞAxd@~0s|}:ڙI+T#cXՍ9_4?VZl-sZuuu5md)Bϟ >eƕG-u4Ș54@LJ?|0f|_ |3`*54dZb)y&AXQ7Zq3}Ew&v;~a vh2#b&ƚu94_'F&!y"G8Ce`Hd9> :,(L>{\۷yΕ`dw 檳z,ӻ䆵֠vso}Q]~dVJ6蔬 6AD;ϜQ❢RlnLnKRe:'Y zxx| $KCz:^s,c2!*وgĜ[20 -a^AAm9qR' 6[*];'0C#`AyL/ijSZl}|Ky܍FۦΔRRkrQ̚F1kvҵ'K1;gn\puرMsH.|vUa4ac.tR s3Z3L:yMfyc9f]C2^v`CSux2Fud`eɔ٬˗7umnxf4%tlɚ ?"*Y)Xs{{;^|ԝ\pA''ی U,ӳkea[E$&x0;1(1Zi fi \9Ӻ+Lˌo6 rd_Y3eQc3DZj_ud&e<Wž[ym -Q|ظ/fnAڰ1\u2爽l?#E;ov#tGdkʅbd`JߓcQhoP|uRw)l6%N]d3龷mjCUğTjj,ؖdc͟%Zkw,I05F,hoD6"#64&Fԙ\b5$3n2#f.L`E76?cAgSƔXFFXw݌ Bl7مV2|5pXmFN L 3LzPv22x1@yemeL("5s|ب(f | 7 \Ox34._DiNUtr<S6j}6M 0OrvPL[Pl8==,.cp)v0Ça)I/{Vb&S)VF73M'Հֿ+eIXɸc9atw"rڲzxxnnnZ]7o~fP5Q P90;=*Pouyy91E;U+r/{::mhu\g`_Y\ w,O#[e}62~3}"|Ҷ^(Z2v:ez;\lO< K)H:Okkۑngr\;i$7b޿+_P֪4u[&۶/N?$`V'9(8לeɝш#K͙62a T^>>H3gvO~=kvI:y9gv߰R]__޽#`,NW2N03kSL\ ӭ[k957;:Wl>OFCdDdfvVv t&wƲ% G>oLŊdZ{g SLr*On]Vjc=`]'Q.~B#("Ш;"\<13(܋NI`0]86^QK:rԪr/MXbIS>HzH,"OKh  s)_Lˤ|/l.DRu'=AVt4pєG_kmKtyxxP46"kD 888kCfd?+>kf+ f$Xe6qIuߜO[ЩJBE~aXDNcӥ@(fp؉խEf sV[wtƒJ[VÛ7ox :,)O>Ǐў+|ݤʸ9aHq d9a"2\@;q=,[]3Z)$dke\{ 攲n'CEkT:_AwV{"R#[Rf Գ?::NNN&>Z۬U eo]% ܵ,;#zd+ڌ6P<cNމɖ 9Fv"efnnooK}iTyD;_fD9/b^w2V@I 5:sW;??...ԕƦ_t|/6n_jHyLn`^>*| f$ 7tIJ6 lE2JI jSg7s 4LXA|x||t0 wwwsB"VdϹtn˗/Yf /'fN۳ُseK d o!ܦ2#N6vq!fzd/jgvG8%b0 5锻L`~9?(k٭>,L[k7Mr9yf&pvv6DYl(Ꝧԑ뫺Mx?<<< F F~;L3߽h=eիWŅt*OɅsc*lCcgvc{0 v>|yn$?sOYW1 張n I)MƐ{7F1cǽQV j^~=yF_|_~~2( O{zznoo8_^^F{NlcuH2 ƿDߐYcJyBa:U圹bV+mH]///rK[-`K3}fgO(Lќ; =lJMޫ ͟R.Z 0xԦD+߼y3F,bJ/7I}X;4>wj18UfXG$js'Ӷ74K%٥f&0}:2wqϗ|g Q\'[*L:A }~z_6[!իW4''''#'~$ unB/;iAt_&A~ɹ,7،=l&ley ]/0ů.@YUrRҀCjXjy͡fEfiNJ$=܌svL߿WO<l:eoyo޼4yo߾J9wtt4C3 \C̠S RPlZ_޽{7ǣ 7իQV{s=ڵϕ[fuǏ6ʔ|*ln07FV,X |iͺ-44[:Yo>6) [1ly7YC8n]%U8 &pķ ܤ=N"䗿kIsn*i8%RirYli1׽M9țbBz7pѡ/OU@Uh`b:>݀|._mYj L[;|'Ǐ hIfj`{֬ę>??q32C;g샫QU>3gT~t:1H'pq ڈ9TuAVK,Xo2߿~;Ov;r d`R꺘te;-'''٤gb']&BF*L2ze_]cR*Ort]#~gց乁Y~Ӓ/Z?;{򶉜{룓fi|v)%fAwaMLkɐF|xx8}vsB'aLp* JC0*3# T|o 8YYY<}vB¯ ,]vѱHJz=Sme3*`|'Ӳdv4  4n;^f&S(aV_t%4F梳{߾}ڜd5U)4e#\l?g'p~NMVYך_a.ʺ7o;z"fjK ㇺ M,s1JC<;"EoucN߿tNynjceԦXb &TN.yLI2VZO:,sMxN˜jYFz3=S42heͬܐEK˗c""dA9:1*6Ң>}&Mc߹Tt3:muXO7\׭Cm,X|:<<7{|luRb1 5ߌ Jx;r&L)Q^nTA^ H۷ɜDRdI6h=7oLv_u #Aw\I~˔ߘH ]lNc|EA999P22\ ]Mکe՜;HQ,&VsCSi|qg???o޼Q"E%,%d0͘z>;D0x ~zO)Af;irxxx+\C~ f ͿgB1irJ% dVKV_k: +F'Ґͦ9*_lֲu}\NN~)$+Q]բ] S{/ IDAT鲊Ji0?YÕDXVU Y*LAb0)m=0K'} ,:muxx8<<< mʂMV쬦b6Ym.u/˃Zih"G'2`/ceˮDM[ʅk K,ܵ{IA_~Ke\=rg`H7TJwxf%YDf׀@;͢bUזEt%a96~nl5x||O|ٶ ?///CnE1 [JUM[יڳi=;Ɓ?::'یMg0^o߾8Yz/r&K 6b/l)FxZ͙N䟧kLxsDImLu|IDكgZ˙Vo XЪKQn`n PW>:e)3AA&.0~Lo T3R-%k)l$3WvRsc|I<];<1rꭥ\dߛl0e%bmXWU4<#sɬ IR6QނڑNr[|ڛ=Ɔ?qxDE'aleQ>N>?s;¨hVy@9p'?Fȅa&˥2i[f=cFeKCHY::WXG3Tl;k6mF;jGcCvª3&vp,HBcuS`ggҦ~O #PXMbjI4M%N.3?r':ns>A+d.IlX0V['lYIzӉ%J;iTm8&y*8CbK \h녓Er1c{{2`aiYFQGzʡ#̔ y6,+͵Z-ugy(TYfJTHME?20F@gfˍrRnĪjadmlpYK|5OXj$2|+͡s3먿[c'պf1ZL٘PsE9w2'qEiƩLR5 qV*>Kc([$F!CQV8|ymH3F^؏?ƀS_nLhm@*sBܠKUY.SKwʑ/, BaYbTYvb5Nf6?F*PTS߲ȇcщ5UR@SƑcLapʠE=. e@Ǐij1޿L,2,6m mL-RQH,Rps5 ˒dCL;MBJm[&@i i84mre6\9.YM]O6(ݢS+@)X> 1 %eܬکN7cVQypp0/{>SÇ{Az3[q3Pe)Ɩu6+O}r]HPnn(j7fGj`<]`ڜӗ-C,0hw!)O@{2 {;N8#f aeV~gHI<ǙqE0ʀܷojiRMM^JMf"фupg\YUkZgќ;+UMMi7SnLRX}p9+~{˒g9FeCnd=V%矉0-J 2d"Q`a Q&' `Zl*&QegY'i[`6jo'h&df}~; $V&w@"{>xs+dk&3lO5 * 0>݆q9cfΥB3ȧ;r Hh ,d\0H w"{%;fGEzQ-Z%"_DYl&$3 W" ;\<|]:β6YfL0ߛa",CX LZsv)xbHIIJSshdHŊ̒it|l1u|᝶(2xOfC0K BfGOrA)[Tnz5$TDU1x/ͬ8ds}_93=ˮ~"cL!*):Ev ВS"ּT v9Kn~NhL5q=ivґp2U6IjVle\H1uo93°nDxsQr'Y3*[F`5?kN% dW+ґ:9* }5YsKiƩ/_Q!Ɣp3FleKK@X)Ŷ`; ;v.)`L: hc #lɿ r-VP׽^'<0j33#-0;'d`fRJQnΔ]V|l9ʝe2 yŝ%iItlf<m0ԚOb^rCaf9=\t.H4lhc[^zGh1"X7#JezO4IwHP& ,,S\I|V:&{HNncZ0ي҉ֵA^S(ѻFRCDLENj<hhx|/BΔN.cd"Y#G$$ޘ X:ϿYu'?yd~ ro[cmsPobC[ιaXFiY{fH3 #,tZpv;/_())udo}e~~^z`NrT8yh$Y7Xpg0SWPeeQ GaLNНs馵Y7ͤ ՍNJ;ڂB<&]5F1ȧ1p56a $S(:%=X{QYߓɚ_}>oooϟ?OOOOׯGzfi7.e!ÍI^L]DtTfumrX$3 qq z>f3X>...=P7qe899tD~KB3يDYW\yZuƜ3N0f gvdXZ'/].+6cONca&'Β-D-s$$Sl0ue۰ˇԹ$fe"# ZPƺxLci]C& Xzz:G)Tٜ3xCuwzۦxfFAni3@%󱴾?q!fۀΘgE^})յً֮4ڴ:\Np6{f $dDҍ4dnڌ!s:m+ɓP_f\@cܐ_]kx899cɮ\HHp\[=6x%%?R;3|弅|T Yv&&r&rghZfb.ngq1]Tդ b43t^Mʸ-)mԲEfvQ6N,^z5ӦI!1%K#9z5ߏl6bɃ[iTw3!/+n8 K1gK6ͳ\tުSSš&~oN |x]d` 瞱rzzw;D krܼ|؛fL~6ׯ%[YWZ$@ `Yj8K-]CgXVIOuIm6gZkT TFz겏L#-1'ò43Wa&ȅeCW"Z=kG؎Y.vUgG!o 2,N2H/; ĩgƢf nzxr443ImNcG,\דuE 8KTm50؅@E;p]n+Tge }%ͷ,t/5dfg?^4bcaٔڸٛ7ϴfdMV *TeIOޣM[a#do`O[|xED<;!s(իIgNlڹyPC6ٙ2+􃃃QY@' y $QV672H*{DԫU6{=OEk1fr\N(2>zxxe7MZ=Lehٕ0Kw8%I*g#hY!˞H33Wf@F2j|2I6;63+ }%9܄ϸOԀ_&1%6ԃiKzec|!ӥ8[cIIEc {Jf % g0r"uFqjM*J .$X1:/y{̺?3٬My OOOz+X2Yۨ᤬#qxggg~-ѤsZbM`=GaY݃ONNu#tae![VWX9y8@IBB:慿yffB!|e\F5!eo0#z}}wJp@- ᒡFx >f}ɭg*Iw&ȓe^2T7 =7ij ,= sV24biXrӧ~`' Ht;INQ}#CgV $⨑yzKEחikݎ`uXr)el6cL\ A+˔,Hyfx/ F նʹfφ˼ i9䦫MQeGNY6;`*TTa Y]YP|q876\:&8S/([.˛Ouy.aْD*I+~gm@)KH7f’T:)2e8;;MffYZnX-(˅:ﯿKԄY W8|Vd弆:Z>ZhN 'Kߔ^ZLqɠ &Ud,e-ljMBf[mj:)8Nu}Mn|///X~=??^~=|a8???gin.ࣣ׺@D\*7YME:=]}v M'+8Z>%}Q>ݜi-OZ9 8쏲8;fHnUȠ3TƉfۄU/mnz#K9Rlr ɶ,e~:l6 }e{\qd]=F,yd@\nG<)UmY}SKob)ðySgN_^^N8L5e5NgT籎/NyyȋD۷{וdj5:a |'-Y///HEdG.pUv;ϯÍuU]gPA&LYgcaf/\dhiNBWΚ(Ai@Z$ \j5Aɹx2“a,.3C13@pww7<1)F!VlФSdڌov`a IDATۥ+W z||@Rɀk:Tۦ7:p,JLcݛF:Div`|ߴ3ay&#)Ȕ*'M`FЖ1ˀXE4 ^oZ ڵlϟޓrAeWRgvh7ԟ?>< h]e}eIj.Mcx/UfRrKe+Unl]|4\Dbu TevG2F-;3x؃@4ZE:oujeVONc$v^<<<Vhfࡅw D@@+h߇ϟ?OZKdL ~{eEYLDN 3/{8T3aIaї ]Rl#Vظ$;e3XPύW/bH#ЖʲnIr ߾}KU.o޼lI^uHH٭юSG.f2X,(ap>*>|Ƨ9>>>~8e ! MʍXrf|mGl3wu4Wa'$KEq0jf5k#+cVDkt.SIlyFx6,R Eӡ+3#nDt}RΔͦ3ȓ(s1OFJIrssf}v*`3r{nTL'T`܈U}d;͚tfI" <)5J#3EGԲι2EK:/BF4o}NpdI!LAMGHpRNIQ9f,iҹ&PV3˪5jih91e~f6UH|1dR땀)q.6,+6ugY3u1 J2]bj `ͷe#y_6, &nv >,N(Qfl6ϟG ؉Xm] ydt{KWA&($pu71==NNNRi9ʞ쎕G,|5eYu~Z\X/Ot: g4\o2xq. n^v#9_~~:qbgƲ0ώD(Wdñ0|^R $gw 8vׯu&π99%ׅ 2 O^2i8|3աP^K3˴?kXk10Ӌ9+L |Y ck*5b *>92v><|eGѐNrg+~5Qz4pi653Uv ^^^FY'kRi0qvejLzOFhK:  Wj`fK_KLa\߿bKAn^>r9VS Wwyy9q%&К(&gf>L9F)X۫F͚7 qDf5^?>>fŘ5s>OrE(l5'P#s11S22+e@(ɼ4ga1B4Y阍Yt%B׋M{ g'IJ_ɵyjכymb& 3 ׯ_WWW*)Znqlujn}OIa57Nml"2tn.aWȔ9MDbde烃͛7%9yH7J2x@){*, $e!  [.\$X"-$Cٮ)vY_xԋrsP@’\ؼyjLIT471|)K-^'ߺBsYs0j}vi5sO]3k5q%yzzsafʝMtI}\{ֶJMAw(;Q"VEZ6VꬁYi̺ȜA$w:jx*96E}³Y]B5˴3X$摘jS|lb.׌Zfkf¤|l2vؾ.EY*s*p(Kbl}-cF^xY[4c+]~t&S_;xXsWtrL_TeC<*9 ia?(0l4܏ScfPNgb $5qJU{,/<<07l\JG-A&qrsRp2=/"%CV >5Iu%txrNe4\p<92K;K;!c?cptg^ Ů]KIZӓ&BeF~t)=:rR= .}fMAϴ@̌LH&)"AƙFu8R+;V[%6_2 `LK~n׺Ln%y&[w*5|ʞ?>xQZuuuKf/_&~GGcfʌÇ{8a7u;h&yP$a&;8G2dFA:.0}IdFʗ=OF=G3bCIxzz'"(M\v:/Y a^Ç&|܈g.tbIY ePk,mMG2Ek6Aٜw?˗/;0L{MAHrCr2d/ $cb {d@i {=w[̍/+d{`ݎr\+:F ǐYǝu6$]J6Ǐ0:Ce9m R{5ޑN\#ƙ$W`iAŗP> AL;j͚2ìВ/%?4]!'ۗ8l!LgjCOfU;bKA mz?9 ZvrIAiL>5 #O:۽I.d/2ݵys =S.ԆXB)7OiN#6.:Lh ^Vnk {hRy8?? f1%Y#իWÇ4Oʶ );bv#0. ;I* #FletQ[T>'En>>>N5 mLF]0X7r|gq?;ʭe_:W$Cy{w٥eg6aZQ PXv7GUq+23z< n۴ K988" &%#AM:C dEplUe/6E8 *ģoۤ1$4$U"^m\;q0a65eLWd^W'x%F3ԕ*ĵ\A] zۭN+r3ʌ~qAuSlsg`}ݐ hq'~WIBFcNtmHC^ywwwÏ?S)&kxsx!THs2S+49djy?t`>3аq_u:ǖLN˿ t:KeHDf ξ|7l! \Fh>LsV<>>5+K8<0Qt͡YuF81߂3ZVnnmvkY 1fԵH<7\DnFќ_^^>-2 R7JmQRrSu}3&g͞2ImYm0ş Uh439\j7{nس=M Vd^$Pl;>UМNsnMׯ_OƇ d", &mD&s)"c4)b՘ E7kϟw2逢Ǩɇidƀu)TPYtusN62! z?13eJKIZ ^:M9:yrdFrf=gfckLS2jK'mA)h4b}}gKfhŪkOPǏfٓM[J&hB͍E cnf@%]l8??\a%PI6.p:05׵@3ck$~.Ft\j R PO|XD9#B'oB$t]o YkPT:v.O4vͅ#xrqцְ.sIEV材2T s Lֺź~goCF3wcrHm;|(3y]Ǭ2Q+Cm) 0&,~G/lXmV)ȗXݰlt22O#tXT&6n;BfI"~+$KZfIL˯(` 3IS:<朘XѓѸUuO2RPmVN|l&Y=(Rz-B7`d ?f ɓl!"Rc9(C aFw3✆O;;;1gXu(NKñQ6gB$kѿ2=;[w1u69ײM2\ş` =6" k/I!Tgg+p~v\JOķfExxx8hȺ8  W f\ &Z~Wħ: emm~&Ć~v:J30 ZZ*aofbLӸ12K Sٹ 2v ,~?UmOuasٚ!)c2 3"N|Nf>۴guMfTqWPM7Yzf֑֜aaY 7;l;`ka3eHSYBj4 =0I+m2]O95!x:;(3@rZ ??fĞ0_pּ4j4sUyh^u$*ZVn#1H:cƓmܤ gpYF 6 SYr-eڡjl_3Fu%2N6d_N2s(N`s\vu79q3w5uP.4*ggS8cI $'2-Ό؉VJ}dM޽i,̰ڦ:Xk7F,b59Otn|A|@Fdji[𬹈TzĹvzfxtt4MR9vJLI3SbBKa_ASmZs | rȇQ=''=Xa&uL"MkN[6qmXr&]$xh>@_6,ۭ@li1 `\E^yeM57'#v7(Pf[E5A-fbUN06$~OY~ܟ3|٥.^?5$E{vf-ɖ_]hUkӀIwnXX<=LtQE6Ն" zzzg I~ɔ&d$zaٳK{1/9ڜfRJ୛ `-ۆls %@f IDATmN%6l)e+K1ד13RN$sa:>ݔر~c\N-8,OL'9^t'miϳzM eչ"`;.0jj\_x*5p3kN;mf)MJlUOO )gw>ON˺7a^ا l8Q$?jo;lN 20&nsS 6hN`B|<2;5; (g4LY2$Sp >U$fAYyΥM,wd)'OfOS&֞w :ПWf>Rn]zߟ"d2PU- Dl02rTv¢?ZNOO=)9mz.c9&trSZﷲf3AM=F,~qJeG#<,ĉ=lA2SH}dE'j")ݜiAğ_3k\5&:FN}߿C2J930Kr*̻dFr"2Ѱm<={jL@B^4B0}8(_A|D9U/~5QǕ2tign>,]n+2}fH~Y,ިqj keY}l{"4_ffd'ZKB3.#='R)tXOiX !,M(F$蜙 L^Ոm6arD 'iFEtac}%w#%Jdm?3` 2U:l'7Fz tS30Tښz?3ELh '$GY!XjJ;M[ AJt: /IV&ׂ@㞩kr㪲0Ze0vpP&A0\x!268Uk_"$$Uus?ߏƦRRDvs$jj5xDdY'Qv٬%Ƹ]̬g0-n Tq2Si 04$L LP&ΡD:-(/7jD~'i]IE҉(K2d,'v8~oue}[t:)h΋P'`KnݓO+@lXE#9g Dd&@ 0f^8DTe$d ;Sb dY N W: 5dn*l +P͔2Ԫ![b&Qw67%ujn)-]qy]Jjx 7|8ZE% U,I^kn+ve9чkl$*${3V bITu#gցFFqd4oYJ؀hѓ1I9 8UVA-A&gfU>&kbӌ]2NeUq JK)K85u*g&ٽ5|Տyׯ_o|qg}駩?IY׿YYYf//{eYp%mYŋҴpаK}>q*i,`R)2qM˜k_o5j󗆟59+^ ~AV\YTI~`Q:WW ǴS LT)K_u]Wlӓy]NF{kINo7vib3a)\B|dflj *=J{hF8FONze'-*=0 >d]O3Ȓ.otsIߞ׸u&Cxx>_֒/j~zJ[5<%).3lzL}ݧ؆zFQ}6c9i1f"N?aSrI&&ӣ1?H~ g.T\m6'w\|"(fJt} rފ2ܘhM2rym @K~j𹽽M~5[ &AN4&^>c/rS b8CØ'bĵyմp]6uO][-Ӈj"j4~Oc\ejٳ=5}SR`[]\c'/n?ܐ4g (F ~g)'cB,܎W5 Ge`%#p5Lr  !R]3aH vo~!=!szΘFxE^q~l\`8RΈUOܙF\ͱ+ @#)OYrUGZ̜GngWWWyj{Vo* SQS4&CFsMxl-3MbuS% ks>̅I/0Im2^/7?.Mqgyr^6Ƥ줯Ϲ;}ςbxcB<0 sOeIʠT'&Ws8~0v<̜Nr "w޷Rwۇ,ũ p+%TS̍^?9Vcmӕ^:(wy \Xw{3,s7ULde_&D0ks jЫ<իW"f\⏳SI}e/Mz-zS=+Tc1HoV.X9.ܼTӘQmLTԨt5m5$ #ϵdOP&aI'3wL҆d%7G˩F0Q)r}V)Ě{lTR-~id f???М :Xh).:+eǧG9aqS`Û:!]L'I.&$iaq}S`AIf8Q^˥[ABߓRd[J8'0T{Y+;xh^:T&j̢Rb]odt LCcplZ&}UY'4#k0ɋO)bŷ}1\KSKh)K) L-еH-feT E oj@QlArsOY$eVE6jg61ޖbĔʟ}rf-)p\x'/jB1?͌_@Uq0'`1-TNps鸞B:Z' `SnUkO_Y)2tIoĸ]V7Ϋ>S|wwww#ĻMe}~~ul5,Zfa#)LlJLoP_egi7fό0ՠMMJ`BUV L?Vˀ_LA[3TӢ+44XzrD}/sP(?{/^ݛ7oՍz :}ﴼO^; IENDB`./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/0000755000015600001650000000000012705421114024347 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/js/0000755000015600001650000000000012705421114024763 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/js/application.js0000644000015600001650000000114512705421114027625 0ustar jenkinsjenkinsfunction Application(UIContext) { this._uiContextClass = UIContext; this._initialized = false; }; Application.prototype.init = function() { if (this._uiContextClass && !this._initialized) { this._initialized = true; var UI = new this._uiContextClass(); UI.init(); // hello-button event handler UI.button('hello-button').click( function() { var helloLabel = document.getElementById("hello-label"); helloLabel.innerHTML = "...World!"; }); } }; Application.prototype.initialized = function() { return this._initialized; }; ./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/js/ui.js0000644000015600001650000000301612705421114025736 0ustar jenkinsjenkins/** * Wait before the DOM has been loaded before initializing the Ubuntu UI layer */ window.onload = function () { function addClass(elem, className) { if (elem) { elem.className += ' ' + className; } }; function removeClass(elem, className) { if (elem) { elem.className = elem.className.replace(className, ''); } }; var app = new Application(UbuntuUI); app.init(); // Detect if Cordova script is uncommented or not and show the appropriate status. var hasCordovaScript = false; var scripts = [].slice.call(document.querySelectorAll('script')); scripts.forEach(function (element) { var attributes = element.attributes; if (attributes && attributes.src && attributes.src.value.indexOf('cordova.js') !== -1) { hasCordovaScript = true; } }); var cordovaLoadingIndicator = document.querySelector('.load-cordova'); if (!hasCordovaScript) { removeClass(document.querySelector('.ko-cordova'), 'is-hidden'); addClass(cordovaLoadingIndicator, 'is-hidden'); } // Add an event listener that is pending on the initialization // of the platform layer API, if it is being used. document.addEventListener("deviceready", function() { if (console && console.log) { console.log('Platform layer API ready'); } removeClass(document.querySelector('.ok-cordova'), 'is-hidden'); addClass(cordovaLoadingIndicator, 'is-hidden'); }, false); }; ./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/css/0000755000015600001650000000000012705421114025137 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/css/app.css0000644000015600001650000000101212705421114026423 0ustar jenkinsjenkins#hello-page{ padding: 1rem; } .title, .introduction, .button { margin-bottom: 1rem; } .is-hidden { display: none; } .img-cordova { display: block; margin: 0 auto; } .cordova-indicator { text-align: center; } @keyframes fade { from { opacity: 1.0; } 50% { opacity: 0.4; } to { opacity: 1.0; } } @-webkit-keyframes fade { from { opacity: 1.0; } 50% { opacity: 0.4; } to { opacity: 1.0; } } .blink { animation:fade 3000ms infinite; -webkit-animation:fade 3000ms infinite; } ./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/index.html0000644000015600001650000000457012705421114026352 0ustar jenkinsjenkins An Ubuntu HTML5 application
  • Project Title
Hello...
./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/img/0000755000015600001650000000000012705421114025123 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/html5-simple/www/img/cordova.png0000644000015600001650000005246612705421114027303 0ustar jenkinsjenkinsPNG  IHDRxJz pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FJaIDATxi%uo6X`XC$-;H*L+d;d "eX *) )$,ٺrnիʘ~Kf̼=wǨ|;8۫a3zmV E{lPDT;'̌ \YVR_8a~~~uݳi OzB>iC鐵oٱe`[u& (vgnng8 9s.*x?%O `Exςd%"(<\W82׎p 8-Xψ0m۶Ϛd=s@e掔rSJ/Wg 0~ʕ+p.Q<+rs XC{Mr}Y`/D6k}2yosfQR^R} uf8eY̲sT/H)v?UJytʕ7Yx{LJs6`6`ef1!_= 9@UJ='YIskvF_^g@埑RvAlVxדgRR n3,۬RJcaazg=3_98οnZn_@eU;XgRJ,,,g~ï}`}PW6Q) Bݟ6,W?1~ZOz~`Viހ.lDQ((P`%fgYךO?+R iҥKϞN?ා;p3 -XS|9p=o$R B4kyߪbxlgZ$PU> 2Ozw.L#5 `˸t„dMKG qJVUf ?[X]b ؿO3T8®5*Hh4BGGZ4y,:{>b UO?RÈSr6nCPP~V  j`S0._X:qg"I,/-e<&{>} m mgX)4M\<%'!dN3>A#W\yNQatC*VIHP*<)f 6Xu A\.,bqa~NA}5 h5h6[V2, ?ހO~OA~lgi =؟X}/q7nAzN4}ؔKɿt],--`~l)Z} m]jf@w%6>93`"1>B)A'JK`V޾p !)' D(^}%7y@Fӆ!"ɷ6x`U1{%%~;|$Xgsso*Ѵ Y1U|N=PL<4JDrlf9TV cƯq?(` 묂hGQ!*DÌ&Q$kr N+Qtu7m4.齇'V}aA3NAfHaV-Iɩ; %Dƅmx {`ڐMI̲dnplvZy>~♧龔Z>/߲O{@y4@^^`dʶcP9s_2%Ԅ.MigT BIJw$)]}ZtҷU?#x'wtYˬ&}Ϗc$%<_;-iUzN^m?I[3cz5&yH;M8sn M͖ј3)|kM|3gyd%GCNi4'gw> ! pl@^poTSO {;xU0 9DFM2T2*P%B4`h*awPfJ!MgG V2Q FYRBrx!y\w2>g}xO^"1ثFx< -_^mt;0x̟D  y@Q?}+@NR542=&gl$d8 nJNVD_@5H]TZ*ء o)D봰?N&Uo`0 'r?;M<ċmp&iis@(˰ 4YEB(ms&.9ot *M¦,F5 is ʼnjvju2jYcUJتz bbw !v~/xikw!ڭVvX(Jzع$e9TEh22Ge(Y٫H&HIxh *ߋ$Nw@IUxw øQ|i9@W cG}*sA61JSp|zPɨ eƹ@  J!_Rq|83E~u\؎3?/.G*:wNe]aυaE-"_[ Gw>wgމÂo~dk (l6?h4ޟL+"{p&ݽwñN('n.!a 2~Ll'# 2 PX+?FL *TA>j,w?B߽mqp(qnV6u(,W8'quU=m|_=h''2!;k UrggGR)Оc׈Nf#&k $ f#&|%T\IZ9$(C, Mƒ1)] uq=KE53BL#9# v(UUf2" +wOz}BE \, ji],_DԚT4! P*XQ9)9ir`%W 0Mp6ZMˏ Ł# "X.WBˋ 24yOuVL)Wӂ2Ýaf"j`6 8%U"Ȯ&MKlipPMAō 4:-7g!P.T'wbZA)\ ( nTK4 ^"L8i4Yv ZBF``Em5Ԃ?䗘X{8p$սkhW*p?6pK`Vj0Z:ʄ(N@%…D@t*x}=hL!Y JˋD?ʀw ˌ j!{nX"!4$iB"i?&~$؏)p{Gڭ[0 :^7eZ~sd8qr˪zu0 ND)jVhZS~9˂T}ʼ iGK+#4V+{ 0vL &2A5JQSo!)]uqB9ֶ_/NLȴ@DoO=)`OA mi]T muannCi$jٺV&بemC)?Ud$6Mрh}|߻L՗`] =ǧ8b"COHƭS^ze焓_i6}4`^*ZV3X ?p@c`^|B\\:{kZ>*)Ti (G8T@t';X ːO=',?Os€RrQ7)N٨C$As6?K a-pw♧T x xH ;On4: 3َ9tm@pYMLJT !VN>_!dI.`6a?q{0w o|#ېbvO%baǪ(TiIȦk&=~gYpgi {!`4K1+nV/Ͱ{ mx"tT X^ZF!%5z/zalfmT(V< O XAhZ0MOz>7EM=k"0pa op )7(/WF=lg*NN`CZ +b)Tl&KX2yOG/,bl SruTI%"hB<4l63/tI4I#1zZC%&l<NJiAvh{X#b׃cA-*i)peI(6uUvNFWS= ,0ho,307 F0,&-Qĥ K0 RZKtտE3EDJu5F+"Th6L+a)qwC % QAv{-hS tZ9a@ ϕ@dNI(}.H]]] LIAh X-4zX4Epρ4B\y,\u>ج%>j+ץo9"Ve&8ig*Odh#ŜhZz\4N Aw0i3еtPF*N|NdkTTRg"30JKPn! 0Me`90UP+Tqqaqslۮ2&ڮsv&4UM_eYZ  :4IFQ}XF n5@S3l!GdTORq4\7+m麤4wF{{?8iPR%hH j%_[k^0iҀԾbyy rt-ζ<-S CD+@=@'ԍean QeFOpaK^( (N2@ݐA@<H@)R՝,%D'J퇻RlGZ*0 iB5h4h#d@eTG>=c{&Q!C~n7FR뺹>dr33 @K驉ael2Y'g#T7MH!!gSSbYwaVp*0qxt}7q>d!ՀahjbjZf5ՠDJ,-.ٰ00NauհsU:JiA2%tTÅ <@QZ3,wX;R E..I_uROD6 >lAKIv!LZ(~!Hu\XZ[H̾Em)m).' Pf xi·A2ȷg-ZFf BH \P$ .1ORi0d_7Yƶݽ} N:# 0 3uľbϦgǎ+Ѱ,(%gfVEXUJwO @\*0հ7ֆi EGāo 3 ^*TmTN(!L_e=x4iYZ!>ZsKʨiOy+<ÅEHp'R?Ono{꬏aXLm*{K ,%.Kd)E R )UDE`dri|bo?3:JaQ%0p(ȩO~ Z$Z6p]ϯZWA8_G TKfBՄԑ;}sQ 4$hWwu2cW Ho(dFX,4qxx LطH'ugKD9KE&%'%..aax8ӟ+VXC˲04o[&:3?̩`)A@]h+` o8cAz^`8*+K?Kn)a`x43v 26N Gp`\ ?_(%J鑍Kl4 =6}Rʕ2ageFŒ iL-v݀0p8`d$ˉ]ja 8PHcp=t}A0dPȜL3֊b;]x RFJ(09tSץX)9 B"PJNd b*&DU)janԬ)igzJ@ȍ YBDƝ{[,#*` #XF%kUp5幀|C>d Hf)"\gj cF;/Oٰ& &("(蔒v[X^\뺥ѨL@-VڨkDdh΂7;FK],1g 4)l%d ]KkpbzIjC`<N`z8 J mpbJY<_;T՟8Ȓ)qQUJfBׁ<(D݂z5BF%ۄ|Rx DfTՏ D'Ҵk6$2@Zzyw:p\p|!e!J[9ӏ;>|v Apl%HJTjvifJT~ '#ae@apifLfbⱥE4 8[ۃ, ޯT-6fRטG"TtaE\"@ʙ}Yi=T*:RR[;YXpW }cqB<UŇOB@ /^@{x+nT*ק'Y< C,` BT'{SK**R Ẹ\O2M7 {Eظnq:R5Zf8qFvB݂em԰.&c.~"=Ur=ĝU|NAj_M{P_d$Cx`@'+Juhpa lo8_Z0df#to.@R~̿DׁRgK-b:.c_!j6077۬qh+L\3MT&[b H0??Vֱa. [DãRm۵B gtD灄h;ij9N_NLc%6M:d g'SwYvft غ:)FRuc]yye٠ڻ3OJc?dէ~y:kPԅqή8WAqM&W@ӫfL5-ЋY4vkGup813QZ~^NruK%@Zw߲jX}O=Ե5PZU;3JʎKWt| s *Ql9MZfYF͏~'ƭwrg9VfuҤ*Pvq8.~BqZ |2>Oʯڪ"Ze*]Litqz\踀>@uIOD))}G677o6tW_>}wY]C9GD*:O-;_rY$"clnn^F%Qikk _WV*pA =W޵ڎ i^w P`0{ӘfinND2faVe LfĴ:öS]T, զilZQf]02Ul4@HY؄'vz^|΁_wz4JP)|kU2}U4 :]f3Y_Ƕ?Ƿ핕's1<7-˺5 ^4h4N[zVU3s0@D+,4ޞw[~떦,iAg4x: 7uu:@-Ŵ_>V_Dz,4̵CbB,AVz\gk ;o0 E?Z "NEB`a~uG4 j>;4 _ܽ{7j;]4Mܼy _tW9?ܽ}8[ƭ[0LY2 E6wWh_bs'xAׯm۸x7b0meaoon]o?R}s7GQn4ϋBo\JVytmt:onnzfi6tXUg^&g[V.Z&ZaYCјrTq8ڽ^+++7nܸJFE$ _m4rs*e0 c"l`}}8{`q`FT~RqHa`0 5nݺuӱu<Ͷgf]J~X~H>UB:w677odcsO0 c™oZh@>|hMiUU8߭XSkׯmTf0ڰT&0p)Q)͸z*^~~6@RXRJ-ۧcc}:n0@fԍ g<{YJDU9GB`ww7=7=p3V/`Qf0"?[5 jV$ʴEE4afBaC^}#$@cssarg!v0& 뷌T`F.HgP80ƭVk=nj*8͛7Wu*||M0-Jqlyq݀#}9d_Ut3r= QLN> >pL XNREw޽[5U(0E*,}DZ{`VbO0poH p'QRt/}yqxu1 45k@eZ<1[ͫW:)S*ЙS ü#h0dHaZaƍ:2\'5DuGu]:D5SOG)j“4c4&XX&*~Ty*?뷶6YyRC5~5MkWn@5R`5`6Mz"UNZM!pkk{{7|XŅh4nPRjjznܸq3/:KRLH"?t"n @O nᡝV NKWR.m۬shKlnmnZhX=f G9/#e#._ꍬYq 0oWWV(#5Pޞy^RYD}t+:g>uYjdP|HT Fٝju륗^J]/Q Id+ Df7o{JZ[7Q]u]! ZXu/CCL `޵m?ߡh 1Wο\_7#{_s+"YMgIiX)4,+w Qălq*W'jT ,:"Jy9QYo޼;0,u:9N7״nϩBOַtul:V:4nX(c۶zH9uttdeq؉o|a4XYaTnR4)׊8|%,WP\"]Uƿ&MK**pήd>5Oa Gf{Nq}d2d;e*ʌ"[XyU&}*&8&gԑe@crJMoV!0 `֑Uzz*#r"h:-TY;M~A9CKd7QQ/WTxUmVHҪpISyi@RC)Xߡ s /v?"M5(9HUBy@f,(b Phr3UHO;SYʡ9Ku/W1>eq#Ԭ(P狖ٞUj$.*<%`X(Z䔁)Eρ:=PڮU[PeeU{*jNGr 2]X`5ZT6@S74[@ T/|"]QU<ӛeVHrrRVk`cuw/m -u䫀0it!愮Baq/sc')=_@gV4 97 fmU%4q9ғ?N\8>椉1fYf3ͤ650>=n=_ ηzg;ISNMOh@;w~A{bW!}vAwbpNo r6v3sО5s@L[xEZmIENDB`./share/qtcreator/templates/wizards/ubuntu/webapp/0000755000015600001650000000000012705421114022461 5ustar jenkinsjenkins./share/qtcreator/templates/wizards/ubuntu/webapp/wizard.xml0000644000015600001650000000561612705421114024513 0ustar jenkinsjenkins ../share/ubuntu.png Web App template, for converting a web site into a standalone application. Web Apps are restricted to using available W3C APIs only, unlike HTML5 Apps which have full platform access. Web App Ubuntu Click package parameters Nickname: Maintainer: App name: Dummy Framework Framework: Invalid format for maintainer (should be like "Joe Bloggs <joe.bloggs@isp.com>") ./share/qtcreator/templates/wizards/ubuntu/webapp/README0000644000015600001650000000072112705421114023341 0ustar jenkinsjenkinsThree simple steps to your final webapp: - ensure the url in this file is correct in the %ClickHookName%.desktop - replace the default %ProjectName%.png with an 256x256 icon - build the package in a commandline terminal with "click build ." or on the Publish tab of the IDE with the Create package button - publish the generated click package at https://myapps.developer.ubuntu.com/dev/click-apps/ More details on http://developer.ubuntu.com/publish/webapp/ ./share/qtcreator/templates/wizards/ubuntu/webapp/displayName.ubuntuhtmlproject0000644000015600001650000000074712705421114030457 0ustar jenkinsjenkinsimport QmlProject 1.1 Project { mainFile: "%ClickHookName%.desktop" /* Include .qml, .js, and image files from current directory and subdirectories */ /* QmlFiles { directory: "." } JavaScriptFiles { directory: "." } ImageFiles { directory: "." } */ Files { filter: "*.desktop" } /* List of plugin directories passed to QML runtime */ importPaths: [ "." ,"/usr/bin","/usr/lib/x86_64-linux-gnu/qt5/qml" ] } ./share/qtcreator/templates/wizards/ubuntu/webapp/displayName.apparmor0000644000015600001650000000032712705421114026474 0ustar jenkinsjenkins{ "template": "ubuntu-webapp", "policy_groups": [ "networking", "audio", "content_exchange", "video", "webview" ], "policy_version": %ClickAAPolicyVersion% } ./share/qtcreator/templates/wizards/ubuntu/webapp/displayName.desktop0000644000015600001650000000044612705421114026326 0ustar jenkinsjenkins[Desktop Entry] Name=%ClickHookName% Comment=webapp for %ProjectName% Type=Application Icon=%ProjectName%.png Exec=webapp-container --enable-back-forward --store-session-cookies --webappUrlPatterns=https?://m.%ProjectName%.com/* http://m.%ProjectName%.com %u Terminal=false X-Ubuntu-Touch=true ./share/qtcreator/templates/wizards/ubuntu/webapp/displayName.png0000644000015600001650000005750412705421114025450 0ustar jenkinsjenkinsPNG  IHDRkXT pHYs B(xtIME 22PLTEqqq ,,,LLLYYYFI*tRNSڿbKGD-]IDAT]u]9>rX$@}i˒HKNh( 3k~q}WkÄ__||W,&U*xXb+„W\Skkk+B/7_|W߃ K`XRVb;0+ _3W,_|k8/?o_o}w߾{o޽xko~߼=o[fxҗwڞz\{e,B-ݖpv.-H=۵lD]n.ز-am]ˡ=n˺vr8=V7XdJ43l!n2}Yi&S DRlcVNg>O?A=!PBޣ%QFJh@(H(`x^9s~,cE05%_a6N:'۫$%QԆ:1i M$^Av@arK%R? AdhVNyp۾9fmwx"P5u/Gu\If%FkieK7 R6U%~7ߎt6hRˤ6SM6PAcrD+9v8fllEvfOUAs(1E9u Y^69;jQ\DhJB`~?o+!J9S 6Z)p w,: B RqH 0@r鵩4U,^{bO+4g!B E(w}IWL7$ԲbSZr= )RյA^Ed랤)CRAX6+ 繺)a&cOsq h\8Z%;MǦҌt/2[CDb\ ֣t }PNU1a[*}qI!J\UCb+)q7ҁl CNJb0V"iR#h))+f ;TjtޤiAMS|; 5m6md)Y+4ͺ){Iޏ6(g6gGI}6شBo 'Kh%^DmcaE:ʋm֢͉un{_\R*=׺u+Zhi*ͱQ*"{D] 5YBȞUЫ kj[N9d+gLS@^ė;Šf6UG44VR -JᐚNP΃ Ɇ E|AG7ҀH& JP-+k愈b;uT%TXn0>?q4ifʿhv7}({f14!YkKn# mJb9ZF)M7dJLdmnX n@δVd~m76vʭL1v-5w(Zj #¢n:6X`iv\aڈzȈRaU*~?om*l7Yx@i@HnZ^tl ''(9Q KȎr~SچEаAT6b\jumlim[*ahMXD Jqјf*ޘ6wdŵ Qv‹IMȆ#5mCv 0novSByS-669C3mSӈ9k~|2({- XRϰy9v;Rx^IZuMzJ'0un,+PhP9XɇyɊleŦZzFNqUuUK`pcm7wbgmȺW7l*6Ĥ9|~tݴi?Z BtӜqA6ڎb4Y;!Q7U1=v uk Vz-m4`wX" `Shw\k(ZzOucP+PvpC;KE%+<5 ;K/uQ ݈ϴ; q}#֎$DRti#˲gy l6MݱY:96H;E)d-6lXqU͙Ju}a(SL-*xDVi=umК@I7O9-:6eq tlS%E!P2='u0f=8+a܆m66U$hפbs!yڏTh`=E*2č0k6mRl8n,t^nuLG89Fӵ9tiȪغڀ HiNvS:btY gY,*vMӦq'ej 6N="xu5m(^0 $md.f٘jZ,h@06, űicW)ݼ˵CuM ՝;fS"K6)ꉭJRG[VU%g/ 9nE©Tñ J}$l6dN9fa .W[S,%{-c9w׽Xb&7%)CRUBP9hzMw&s}5j67-v&699N݋jîdɹˬ띵+RjK,E#V|t#qXINÙjyWeJh.mMŞ0ޔv(svvہrnmRȲK)Z >縭뱵Ou tY>J މ)GWyNUP&k]1y =3F]cËhsȦAwTc`xIח!Ȭ!:}<jhtS@Jm|\;~AѐeÚV#k͆N.l` ;IaЫ&&hZ6l J "n6^)dA>z_,k+aaz7]:r!Q,t`:Se 7ΦyƸS}J,іU0b)$6Ě-f[TF5',"/lo- i'E{o0m\ˎ6XHw4.ts7*HeR94{.JcQ g uXJȲBFE̳_t/$"KڬXL4lzrdCZmWiO{GG7`fQQp,qK4Tx`"EjfiJ:V"U^7"f{*6Y։JI SDeFHli!\X8 kn 7*].BNlŵ[jvVB#FP%;@16űDtqe:-P9= Hr$ jZhyK+Qvv+A;xAƗ%jJ%%-'YjTeH(/%q gF/ (`\*$'-B%v-<dDž0cS] Ҕ4KPB Y2;+fkFuN"b&^[ ˟]%u|b',hRt6 mZ(D̦4 zp7iɑ/UkYvM PR (ˢ +,#Trc|7oA 'zĀtHk;֣7ZZ<#+%K `K#aJh" QJ Ik%Y +"%J`EX)~w3!"q-g(6v/w*vaj6*ZҀ??>yt ЮRKG)JSa Ҭx5 QwS˚Zk~vXRRq`vaN 5G2m J%dsPk?O~ml?ӽimT8E"¬BjyyyHmMP*XlAo2%ʚRIm%TZS*J7&9c1ܟ>n6n*E sv y-_X0,굕X 7HD]ײ7Ue `) hk?nV9(^>~>Xhf}:EKS M?I:ֶN VAtR=grFh+vZE wHue4h"uuZ$3tuِ֥8^~R"]*SHRY Iha`YuBTHiʜyKu؏l@7٤i84 ^IdN uL%!5Zs];4;.s?؍(_rQSܜ[^Ti%*Pl<%)B/ÏAXI EԦB%D@/:{4*ZPVk16S\,{fctJEt)Y~?j;AMX֑YzFW "_~<=6޽x+tzrzRϏ+4uB+VkcNl9Hؐl#5׏ߐ'F69@NpՆpP^@Ȣ9^kKVV"]tz8o: uU\NsXԤ%:D=D,d[69ǀNI=Sgj e<,r`p|y*V+)"J'` "mh+ں~N#{8WM*RoB;5 ljYyTA|7V ϩ]uX/eBiiǝ.ڔ pp7raBZNvRњ'A.KfIRTֲ+ZOg"Úcl#k^^nY}w\'S3 !xL":i"N{Fmӭ+z>MVD,xڐt~z,R\cC*Gȭ9; d R3f֩U%'` @R矾'v4K$l,tG -ͷaI]F7R/WKUYHMnZWtj=%E wZEh %mm>X6M], T4=v۪Bbr H7lwPpiţd3pDt 4V~R?$k^gve9>_ J]JER$qF"λ1ʛ! j7ŘE+frҥjZLbU[]6@ܴR7= IS0Jcmں4'[k|{Y.ꎱU/cJzԶ`M;XZ4h25B@ee'"9aǬWmi~ȺITΈU.(H2?Rt67J46B$KGӲO既A 1M6*4&ڞ !"GhdC\j6"E()NjKMVATda;w#^m҅{d 5{P T:uM%d/k ;lD\B;HS|T7V[V6)"&xUM*’OxmhV:P%4 Js)d+Tj3gAJN4#Mk}s3D :(|ڒvxo Z˹(!.9 ӇN'?mc;|~Ě2aĥz#Ц^`-(f~P!fEVrǵHM#Ve~]ъ&ڞcP|TokBOOԆ{M$;Z9fc=Lϓ-IH@C(4?llriѵB'Jl,*h_#S-[N1pnq/:)jIB7x=}yu(t@CJkCOc\T !ǍP6ny~6Y$TօMkA;NsuZ/+6XK"q͑ºSM ҒW`t\Au }5w0AԔ#R]4?@ıNٱ:WdUb:}_!uI7: '!{w$vӗIk&jfpJ@ S=}e@9ɲvLJ FPV[j5ԪiLSb@BJ)țޓumrZ˾(Y Beiٜ5:,wٗbq]6N31ĝv'pt9gPPu TLKS6I%V~|`k.UGaˇ(VbݰQAXHz=`؈ŎZwN iz4.=`AjQ iUvy#oRFKPk h|ؠb~Hi΅m! w-/;hs,M+_,vjsRק*MJ,D:4J(.V7+O޽1{FZ~l Xʒ Tؐ jrDhS Mw%.{!i{j#Ӝ|}n*9qR J|x{d=z~(ծڝklSd:왱7 6$eo7w5[fwj ̏7R#Bɡ$Yۧ 1݌i^Tk>N.WZז {O~}գWA]׏Ahk\NcMbjGpgu#vOΔ#8)3vR 7l슍eLJ)e49_Sv0_ЮbVUαUN:4ҖhSh~¸]4䜫Ll#v+ "u TxJLc)]ȹv:%_M[R*Ub +IkJKQ ~X҉ڸ'FdG:.ґ' UlvCϵK6J %M1Z owcv -6T@}5M ؀t%-y6ZڟVhWRgTNwS$zrيrw..`YM<}bw󮧶.Nj{\he`6akNl7@dOl&z.E#7dX(Ɗ6oLA͎]9/_v_ZAHR+X.o5vUc6d^^훓_' ih\-`] ʞx:`s?RKlݖ4Vt%Ea뺵Q,Kl~uOA@L K*V6hpI4TA=#$K OzJ\x›a{UWvoވȮc~}஦7yf-ȑ]Hǹ+_~ԊZd}pG-M՞ҲB R tO@ VZi01kͩ_ODj@'PRO7f JIiNOeH)*ꪥpG¶ B↸C +֓Ʀ2뮈a-Ru40ޟ͆,fQzt!Jxad>K@HFUgAa S7v}T ><q5S:/W4EεjcP+-!TR",?o\߃ ڈH:=;^W1wd) M-Ze-QkZpLc6zϿWƦZ_޴6tS׸r(g V*=P1"doTTjMe#͒w6Y}eCZil-ADWdh7IrY27&MsM{ҝZH 1n81HEPV +F PZ ) g~VVkΦYM R֥,*`+NP{Z?]NxRK% Z;KPڽOX*7Z6ګ_5 9̏+Nm{wnNH+9崣xHKV:#6 L*|,xZRRO %n~PpO_cA~פ!(03O=E2nֵwZo,Sk˜QN Eh&4c۲E**MS"X<Rm)+(E;KQw TPsi}~xCp{!4^SZdK{@*).\Vk|)`KuyATvozR~B 5lΦ^E*J;Ԓ$<=E<_k*; qVXђZ^hʛ&Ԣ}4Ll5ݩP9OKvM#'n"{O̊"'ZQYxS_A5P0ZnZԒc*Χl^#j p0Wm٤FPq~@Zr蘦acS_c>bS^mfUWw]"V覯Zw l6 hYFkh,2}}gC; ͂Fe~7~`)- DίK[i;Vdvz󛅾k!bX%lMzb2f0P;%Ҥ%^숸A#̙b fgɍ ҒVOTÛ.PԽ>o WA8ƀ(Z6!Wmsg<3x}=-hVv#@vYH5 (%`y}t\ &XY%X^yʑVvmdAVƶHIo_^~__mMX6YJydv3fjj hYq~,Bj-bb;-~BwO,RqŗywaK7e(<ZȰ_}qUJUHhmpZBh;WA^Luc4K'~".MOSPrr3H65'(E H_i3TER |jwkKR-*o֠"TCz̒ǬS+ܙix*ȲQٻus s2(궱R 㛗no" )-xzufr=t%Ycv7)}Xccۘ;XSӮU٤>=3O6-}}`) QS+rg SQ_#}hو٬(G>Ŋde˧qo5ۉصa ; [l<{~~z|KBpm1+tR /ԠŸ?V4Փ)H2No- )vû|ow(ŭkR7"-RŠ/_FͽIp.ePR7"^#w(AhN"T{Gے;_M]ƗteKC/7M\FgMT ҘZ[:ivyZOL)h{QhlYe4?K{Mdq9Jj)j _k -y|J_VyXP+LIׅ?kP?*e i#"* ^NvHnv{;Ɲi}d- U| {<%| vq6xBT;澾w'0WIPMͷ^ ۜg\p~T وSPݼ M;؈pڐ|mj5Reѕq/λwU E l t^SY?EAʲk߈ R]_ wwU!A>"ˁ=Fb= v aMm PݘBmnJK>7š;\HzB㦴9iU0s~yѮ.dݴv*ZWvn*\J4Rb" {q~!gJT;%Xx[6Kk>F| &c*n|i%]M6IP0@`2hٓ0E1 P篞l0kjۓ:*QC~s,Ay ֦l_Bϵ)lWT W%W.n*!K6@>J,TTDT3][¶"PEY @k[pu=)'Ci9osv]n44RY+Һ~XlqeTrdP]vR9r~hS1 q7WVi, ܄4r3Hf\EiTb~4b A7o&iF|ջWŀ`֭]6'B[Sl0"&yܓ֪ԆF}*}-:J3,IU*udK҈`(|@ κEYQM K|O$d )V{z*KFK%F u4h'P:WvµqRDق%=;lP6ReINQ E 0M|RwWr& 1RnZ>Q(M X(CaYkyjSH={@`/7*HM}pE{aōvlW@V ȿH*u-M u594֮Cc#frpw;rݎH=ݼl"Ҹwd*Ri/dA<}B2iq`B7eJ,I;5P+ju]C<MWM"ue/W-*XI I/Uݟ CsǏqx5Cs=qd!eln:^?wv@ӹ^k$m+u"t̸B1_(wOwjT\'M?>6_R@tw/Vt^'p0=)a2pt I]m~xv6a?#EBV-uyy*Os`iC_}Tƒl]fHv| 1{1VC)X+d6;yBTrDkZK]TT6Ј–y\mSvmẞ޾52,+Ժ n:6Jn, *TmS~5wD"7AC Zćߴ)PfҀ%CNmK_}w_=^WBpF?m؃t4U[# Ug6%ҝ]Њ6VQ/Z&#$†r66H,W i<_\?[DÂDi^~ߟ"UTl*;cRu;(M{Og/*UЭ۝_il/J;05 Z U0p C旻CJN`9Ql`z_ {RPUe7 "a eO`Ԧ`EzPW@4{"MiZ3磛JԒb'|Uz}„C e%nv?~ j˥-ZQ 2-%5PPZ[7↜tt|W Ȏ[=Uv*%{}ag]`ݬn`<.2'6BǜF $ީo-CZmiT7 Ea6e ͦ?Dͪ;50 JesGT/޾;zW;z7_?;(EĢ{clވiJuVak/4el 2'YwLhŵيhKT bs?Ld_}ۧr+vHcSl*ߖȱ2@֒*}燝L 4elnN7a%P^ J=G)@ G㷊lg eJ%F鸮Bb0]З.M+vatYaJagm}uVPh9Y-- PCzv'K?Wkd![ͅ;x6jvՓ,"n)kgBJJH׆(2(Z ͈_W@X|+ڲrҁ@&;'%Rʫl,Rl^R' OCƺ6k܃Ŷ4V$ /b/\ADAGiq񘋈/>oڵܣ,Bk( `eЍUPVa9j v~u)g mHp>в&#L-o_^|9˗~ yV4E;wPN7hMijQ֬Edb ZB?rr?@gze'Mlʫem5'4\_@]{a!Le; g`_|O//#k>4BqldEW j2j6ZѦ(Z>) rn8Зq|b^9h&"C|T,WX v()' aA{b* <~1' KupCDkda ù`=6dU^-5Qq Z$nJ1|~G7EARNDŠ1U'!.!Jij6JDiuO)6KѫMA/9C%whֶZwa?= hδ dR)Pg`Zt?_`e%@{kiܦЈ܊6 vY*?_۰t̒2: zm1=%̗^M)АƕkU?=K@_.#Mm^x+2*`GVԺ edl@U NAm~ئz.ΙyɆNy1ʺh H*PKA^I4c^HCx= >u~iaϏnt!.rnv,tY HVdӭrXѦ0 k݆<>}$4†Ε#$@}6HO),;b$3ĎgdgmCMUF~FNJ^gRRyh;nVD#j(զ+.M`"ͦؔUKΟ;uIhhJ/2T^=(aМau WCht ;n*`zO3'jvi k!ʫZ[ X-U^lr_TßF,\aNm6C9 {NЦk]j`&͑h[Z]i-Y4}M W= B{j_*RQ@@aZ֐j7-fx5Z6t3G*ifTMi#thS}vm“+"GpئHgu2܉K7()] PUQ!X*5)mPKsj] K|N='OZȉH Vڜ"jj`t8i}mD`:TmRxQ6.HO"`Jֈ6 3cVeUdf6 -5F;[jU8jRӷDBXJ $* ͝C!Xֵ?M,Nz+CsFesRKDe3ES65nڑX9h^V+U ǯx --5tpE/h赴ʚۮD*lvRh=,esq>\z"Pds']i]ۮY < [nvtEԵPR=˖Z#;z]W9ҤZHMl]S!ndZZpZ˃aP075RV(%@ T@V HV,ĴwVΪGΦPVyUb"\.e,"_ UpeI#`ѕT `)0s67\-ᄿ_5xu+F jtRVz{ mGQtq$$:Xh]?-/  *ر@mJ0N+)d$Ob\w4CZNSKxȗodDӞ"^;o$B/"4"ah$.}}͞de,ۄ֫ڵ4k9NYy)Eʝ4AQt؉b0-JƢ'vAJuX9:A6R}nBHcŵ;k# >_6Z0(q`T65p_$'>ğpeM' RXtͶ/M锝yz3XA¹87xF|yxLJZGQN3Q@)XM+M:aqqCN4/!'mRHdTRNZO v/?[9RinJB2o?<C Nm+W`״.=5H%;rvӎ@KlJn 6hQE$Iz.6@7,4T*!ݹ'%]&0m^ӵz@(*%4?r_ha6!ٰS㢄sm^X@D_N\e.P\o-ߵW H7|FԒUG1b<)Xs*~^H8wtNP-ZKZJtW=Zր \o>|xl+kQ6tۯ>ȦcvaeKz(4 kIKX^.El XNl{xZCib# b~pyzLJ벼L6l L{A +`@VLV)}rAXϐ;#,9ڲ4tmiDzHڴ6ιLk̽LL[yxx}LJ+G"{BUf-C, Axj;nn4Li^9 ͑U_^nZcnNs:X]V)NU ljE T4)"OPS{N"A]CQtIMegyҮ"غTlSt!B^*Mxyd6R4B]Ct\9DjY*2.t`/u{mͩin K6]WM\Af%$^EO-YVЈr%yRzZd^ f65t5W!?z^DvM n Hp V;tͅ xj#wfi`%z WlE='iz0PfmA 6* y*CJhKӸ@Ñ&PifRkݴ&~:>/Bv ;̱$aKgLS݄Xs0H64@Zj0eZa6<ާqM 9s{g,93T*a輏"z3m1ݫU@;*S;6mլT9S6t;$4_؊ wxz8cu`K6=zܱvgiOͮ, ,VN+,xlx ZZz[tC;\ۣocvZi(NCinw89YUHE^4h( ({-'dAo8v!Tbh*f' 6LekyrNa=;ǂ !aiQΕEf+%w'bq7g$\$^DMaRjw{f/۝eQ|YmK;=YYuav8bK=* tݘTxPԭllN{Iu$luyWy*.RBK`s̭&5CءMG Km/7L;mAslxfLduki*`3JP0qV6q3 $$s ;>*%*aPH+nYca)x\X^rwUVKR =g"6_T&T[ޯd1$%GݦRSE^l =H@Aa5K%XvD::~u^4*`#JGQZ PBu~n =Eڔ]*g]Rym4paGL_]mjٜؐF0-DΔ-4< .%ټCcgˆP9b۸ `a<5i8q5J{xaP6JDp+d %J:ѣH@l ;EOMC< v!]ϐZL{fgm9{\L'ϸ41H:Ҧع",A`D,P X) k VXANX4e~9PI bLe kͦ}؄溈xGIݴS !PvJŗs"(8B3 {ky pZCfsBӷO4'[-;Ԣ+uK > 4y>!N1ݰFKZT6uRW"߿]R [YV!C'YA yxf7=ݜxLʖ>T"vR mJCWKµЀUdՅjJH1Aa \h1/etZ"岺ZcFC5rݰ M#9Y6U6UF R\;-uM݉Ԇ"&KUPj8Hm@h6V I#z HR b՜8hDRh*4 ! 6 MhPqHs.r6<\X{!c;Jj0;}9;aw3&h[?X;JRm'ZvU1r0O(V.Sh҉Ylt଩6 nbY #kx֨piѦBbSZ$eSN<@NSe%ķPA=ێҸ.aPl(1M5eBGsR*BTQ7{k4e}$de}g&v:u0V,H0ne>+,tjr e9A,)sTƅnjk{Lo~5;mXI em z:2M(yI5ō3[tط;K[NO>(ZF@SW@{gCz64%^EMs.vZlpDb&arl9Ӱam)G,4F 紃7掹 Bτ(iVO!9llڪl;šR-δzS#RwR%ԣRKivXDC|Ϥp}Ry^lԆR:I0kNNj;h95E2ڬ1H|A`sfZA-lR Svpm8fYRWC{~JQ!YPvY-mbV1O) FX/ 1 b!nXWBM ȒkX[Yx4R4 kqSܵ# 4u6R%3wLN{O7g,+TZ]R *[, ɱD,r6; 4[nmNM߃@`$ju,NqeE؈FAoEi(ZQ/X6ܩ{ eyO`S#ۥ!PC/wL`b'Yz2Cj)*;ݢbHީ%pds_tkSD1-* T %j4XmJc)G6?XHc&ϱش7dtTMi31iVlx!PE zg{USκ]i8t*,J(zQKxVش;^nk ]HmE0!@\ JB=j9K-/́-nQظf/ДDKD-h(CS\vƊƭVmwJ ls̲ YX(+٬)B WjM7`<XAwWiUquuT&B4'%m~\ R%9؞ `A Mu4E[ 6PQ  2hZM9G_|cOaG#ooo,#G.^n|k0|$GM˯||c'1> ~W||chC9IENDB`./ubuntu_dependencies.pri0000644000015600001650000000046112705421114015637 0ustar jenkinsjenkinsQTC_PLUGIN_NAME = Ubuntu QTC_LIB_DEPENDS += \ utils \ qmljs QTC_PLUGIN_DEPENDS += \ coreplugin \ projectexplorer \ qmakeprojectmanager \ qmlprojectmanager \ cmakeprojectmanager \ debugger \ qtsupport \ remotelinux \ qtsupport \ qmlprofiler \ valgrind ./.excludes0000644000015600001650000000000012705421114012675 0ustar jenkinsjenkins./UbuntuPlugin.pro0000644000015600001650000001777312705421114014274 0ustar jenkinsjenkinsQT += network qml quick dbus include(src/plugin.pri) QMAKE_CXXFLAGS_RELEASE -= -O2 QMAKE_CXXFLAGS_RELEASE += -O1 QMAKE_CXXFLAGS += -Werror CONFIG += c++11 dbusinterfaces ##################################### # required for Ubuntu Device Notifier CONFIG += link_pkgconfig PKGCONFIG += libudev glib-2.0 ##################################### ##################################### ## Project files FORMS += \ src/ubuntu/ubuntupackagingwidget.ui \ src/ubuntu/ubuntusecuritypolicypickerdialog.ui \ src/ubuntu/ubuntusettingsdeviceconnectivitywidget.ui \ src/ubuntu/ubuntusettingsclickwidget.ui \ src/ubuntu/ubuntuclickdialog.ui \ src/ubuntu/ubuntucreatenewchrootdialog.ui \ src/ubuntu/ubuntupackagestepconfigwidget.ui \ src/ubuntu/ubuntumanifesteditor.ui \ src/ubuntu/ubuntuapparmoreditor.ui \ src/ubuntu/ubunturemoterunconfigurationwidget.ui \ src/ubuntu/targetupgrademanagerdialog.ui \ src/ubuntu/ubuntusettingsprojectdefaultspage.ui RESOURCES += \ src/ubuntu/resources.qrc OTHER_FILES += \ src/ubuntu/UbuntuProject.mimetypes.xml \ src/ubuntu/manifest.json.template \ src/ubuntu/myapp.json.template \ src/ubuntu/manifestlib.js SOURCES += \ src/ubuntu/ubuntuplugin.cpp \ src/ubuntu/ubuntuwelcomemode.cpp \ src/ubuntu/wizards/ubuntuprojectapplicationwizard.cpp \ src/ubuntu/ubuntumenu.cpp \ src/ubuntu/ubuntuprojectmanager.cpp \ src/ubuntu/ubuntuproject.cpp \ src/ubuntu/ubuntuprojectfile.cpp \ src/ubuntu/ubuntuprojectnode.cpp \ src/ubuntu/ubuntuversion.cpp \ src/ubuntu/ubuntufeatureprovider.cpp \ src/ubuntu/ubuntuversionmanager.cpp \ src/ubuntu/ubuntupackagingmode.cpp \ src/ubuntu/ubuntubzr.cpp \ src/ubuntu/ubuntuclickmanifest.cpp \ src/ubuntu/ubuntudevicemode.cpp \ src/ubuntu/ubuntuprocess.cpp \ src/ubuntu/ubuntudevicenotifier.cpp \ src/ubuntu/ubuntusecuritypolicypickerdialog.cpp \ src/ubuntu/ubuntupolicygroupmodel.cpp \ src/ubuntu/ubuntupolicygroupinfo.cpp \ src/ubuntu/ubuntusettingsdeviceconnectivitypage.cpp \ src/ubuntu/ubuntusettingsdeviceconnectivitywidget.cpp \ src/ubuntu/ubuntusettingsclickpage.cpp \ src/ubuntu/ubuntusettingsclickwidget.cpp \ src/ubuntu/ubuntuclicktool.cpp \ src/ubuntu/ubuntucreatenewchrootdialog.cpp \ src/ubuntu/ubuntuclickdialog.cpp \ src/ubuntu/ubuntuvalidationresultmodel.cpp \ src/ubuntu/clicktoolchain.cpp \ src/ubuntu/ubuntukitmanager.cpp \ src/ubuntu/ubuntudevicefactory.cpp \ src/ubuntu/ubuntudevice.cpp \ src/ubuntu/ubunturemoterunconfiguration.cpp \ src/ubuntu/ubuntuemulatornotifier.cpp \ src/ubuntu/localportsmanager.cpp \ src/ubuntu/ubuntulocaldeployconfiguration.cpp \ src/ubuntu/ubunturemotedeployconfiguration.cpp \ src/ubuntu/ubuntulocalrunconfigurationfactory.cpp \ src/ubuntu/ubunturemoteruncontrolfactory.cpp \ src/ubuntu/ubuntulocalrunconfiguration.cpp \ src/ubuntu/ubuntudevicesmodel.cpp \ src/ubuntu/ubunturemoteruncontrol.cpp \ src/ubuntu/ubunturemotedebugsupport.cpp \ src/ubuntu/ubunturemoteanalyzesupport.cpp \ src/ubuntu/ubuntudevicesignaloperation.cpp \ src/ubuntu/ubunturemoterunner.cpp \ src/ubuntu/abstractremoterunsupport.cpp\ src/ubuntu/ubuntushared.cpp \ src/ubuntu/ubuntupackagestep.cpp \ src/ubuntu/ubuntuqtversion.cpp \ src/ubuntu/ubuntudeploystepfactory.cpp \ src/ubuntu/ubuntudirectuploadstep.cpp \ src/ubuntu/ubuntuhtmlbuildconfiguration.cpp \ src/ubuntu/ubuntuqmlbuildconfiguration.cpp \ src/ubuntu/wizards/ubuntufirstrunwizard.cpp \ src/ubuntu/ubuntuwaitfordevicedialog.cpp \ src/ubuntu/ubuntumanifesteditor.cpp \ src/ubuntu/ubuntumanifesteditorwidget.cpp \ src/ubuntu/ubuntuabstractguieditorwidget.cpp \ src/ubuntu/ubuntuabstractguieditor.cpp \ src/ubuntu/ubuntuabstractguieditordocument.cpp \ src/ubuntu/ubuntuapparmoreditor.cpp \ src/ubuntu/ubuntueditorfactory.cpp \ src/ubuntu/ubuntucmakecache.cpp \ src/ubuntu/ubuntutestcontrol.cpp \ src/ubuntu/ubuntupackageoutputparser.cpp \ src/ubuntu/ubuntuprojecthelper.cpp \ src/ubuntu/wizards/ubuntuprojectmigrationwizard.cpp \ src/ubuntu/targetupgrademanager.cpp \ src/ubuntu/ubuntupackagingmodel.cpp \ src/ubuntu/ubuntufixmanifeststep.cpp \ src/ubuntu/wizards/ubuntufatpackagingwizard.cpp \ src/ubuntu/ubuntulocalruncontrolfactory.cpp \ src/ubuntu/ubuntulocalscopedebugsupport.cpp \ src/ubuntu/ubuntusettingsprojectdefaultspage.cpp \ src/ubuntu/settings.cpp HEADERS += \ src/ubuntu/ubuntuplugin.h \ src/ubuntu/ubuntu_global.h \ src/ubuntu/ubuntuconstants.h \ src/ubuntu/ubuntuwelcomemode.h \ src/ubuntu/wizards/ubuntuprojectapplicationwizard.h \ src/ubuntu/ubuntumenu.h \ src/ubuntu/ubuntushared.h \ src/ubuntu/ubuntuprojectmanager.h \ src/ubuntu/ubuntuproject.h \ src/ubuntu/ubuntuprojectfile.h \ src/ubuntu/ubuntuprojectnode.h \ src/ubuntu/ubuntuversion.h \ src/ubuntu/ubuntufeatureprovider.h \ src/ubuntu/ubuntuversionmanager.h \ src/ubuntu/ubuntupackagingmode.h \ src/ubuntu/ubuntubzr.h \ src/ubuntu/ubuntuclickmanifest.h \ src/ubuntu/ubuntudevicemode.h \ src/ubuntu/ubuntuprocess.h \ src/ubuntu/ubuntudevicenotifier.h \ src/ubuntu/ubuntusecuritypolicypickerdialog.h \ src/ubuntu/ubuntupolicygroupmodel.h \ src/ubuntu/ubuntupolicygroupinfo.h \ src/ubuntu/ubuntusettingsdeviceconnectivitypage.h \ src/ubuntu/ubuntusettingsdeviceconnectivitywidget.h \ src/ubuntu/ubuntusettingsclickpage.h \ src/ubuntu/ubuntusettingsclickwidget.h \ src/ubuntu/ubuntuclicktool.h \ src/ubuntu/ubuntucreatenewchrootdialog.h \ src/ubuntu/ubuntuclickdialog.h \ src/ubuntu/ubuntuvalidationresultmodel.h \ src/ubuntu/clicktoolchain.h \ src/ubuntu/ubuntukitmanager.h \ src/ubuntu/ubuntudevicefactory.h \ src/ubuntu/ubuntudevice.h \ src/ubuntu/ubunturemoterunconfiguration.h \ src/ubuntu/ubuntuemulatornotifier.h \ src/ubuntu/localportsmanager.h \ src/ubuntu/ubuntulocaldeployconfiguration.h \ src/ubuntu/ubunturemotedeployconfiguration.h \ src/ubuntu/ubuntulocalrunconfigurationfactory.h \ src/ubuntu/ubunturemoteruncontrolfactory.h \ src/ubuntu/ubuntulocalrunconfiguration.h \ src/ubuntu/ubuntudevicesmodel.h \ src/ubuntu/ubunturemoteruncontrol.h \ src/ubuntu/ubunturemotedebugsupport.h \ src/ubuntu/ubunturemoteanalyzesupport.h \ src/ubuntu/ubuntudevicesignaloperation.h \ src/ubuntu/ubunturemoterunner.h \ src/ubuntu/abstractremoterunsupport.h \ src/ubuntu/ubuntupackagestep.h \ src/ubuntu/ubuntuqtversion.h \ src/ubuntu/ubuntudeploystepfactory.h \ src/ubuntu/ubuntudirectuploadstep.h \ src/ubuntu/ubuntuhtmlbuildconfiguration.h \ src/ubuntu/ubuntuqmlbuildconfiguration.h \ src/ubuntu/wizards/ubuntufirstrunwizard.h \ src/ubuntu/ubuntuwaitfordevicedialog.h \ src/ubuntu/ubuntumanifesteditor.h \ src/ubuntu/ubuntumanifesteditorwidget.h \ src/ubuntu/ubuntuabstractguieditorwidget.h \ src/ubuntu/ubuntuabstractguieditor.h \ src/ubuntu/ubuntuabstractguieditordocument.h \ src/ubuntu/ubuntuapparmoreditor.h \ src/ubuntu/ubuntueditorfactory.h \ src/ubuntu/ubuntucmakecache.h \ src/ubuntu/ubuntutestcontrol.h \ src/ubuntu/ubuntupackageoutputparser.h \ src/ubuntu/ubuntuprojecthelper.h \ src/ubuntu/ubuntuscopefinalizer.h \ src/ubuntu/wizards/ubuntuprojectmigrationwizard.h \ src/ubuntu/targetupgrademanager.h \ src/ubuntu/ubuntupackagingmodel.h \ src/ubuntu/ubuntufixmanifeststep.h \ src/ubuntu/wizards/ubuntufatpackagingwizard.h \ src/ubuntu/ubuntulocalruncontrolfactory.h \ src/ubuntu/ubuntulocalscopedebugsupport.h\ src/ubuntu/ubuntusettingsprojectdefaultspage.h \ src/ubuntu/settings.h INCLUDEPATH+=$$OUT_PWD xml_desc.target=com.ubuntu.sdk.ClickChrootAgent.xml xml_desc.commands=$$[QT_INSTALL_BINS]/qdbuscpp2xml -o $$xml_desc.target $$PWD/chroot-agent/chrootagent.h xml_desc.depends=$$PWD/chroot-agent/chrootagent.h QMAKE_EXTRA_TARGETS+=xml_desc DBUS_INTERFACES += $$xml_desc.target ./Ubuntu.json.in0000644000015600001650000000060312705421114013653 0ustar jenkinsjenkins{ \"Name\" : \"Ubuntu\", \"Version\" : \"$$QTCREATOR_VERSION\", \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", \"Vendor\" : \"Canonical\", \"Copyright\" : \"(C) Canonical Ltd.\", \"License\" : [\"LGPL\"], \"Category\" : \"Device Support\", \"Description\" : \"Ubuntu support library\", \"Url\" : \"http://www.ubuntu.com\", $$dependencyList }