From 0bb4240b228dd878656207ed1a2eb22393ebdd62 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 27 Nov 2014 17:40:27 +0100 Subject: [PATCH] add quick and dirty ports of nemo wireless settings --- CMakeLists.txt | 2 + settingsmodules/SettingsSheet.qml | 548 +++++++++++++++++++++++++++ settingsmodules/WirelessSettings.qml | 216 +++++++++++ 3 files changed, 766 insertions(+) create mode 100644 settingsmodules/SettingsSheet.qml create mode 100644 settingsmodules/WirelessSettings.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a182634..90a6164a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,3 +41,5 @@ install(DIRECTORY wallpaper/ DESTINATION "${WALLPAPER_INSTALL_DIR}/org.kde.satel add_subdirectory(bin) add_subdirectory(qmlcomponents) add_subdirectory(services) + +install(DIRECTORY settingsmodules DESTINATION ${DATA_INSTALL_DIR}) \ No newline at end of file diff --git a/settingsmodules/SettingsSheet.qml b/settingsmodules/SettingsSheet.qml new file mode 100644 index 00000000..decc470a --- /dev/null +++ b/settingsmodules/SettingsSheet.qml @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2013 Robin Burchell + * Copyright (C) 2012 Jolla Ltd. + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.components 2.0 +import org.kde.plasma.extras 2.0 as PlasmaExtras +import MeeGo.Connman 0.2 + +Item { + id: sheet + + property QtObject network + + onNetworkChanged: { + proxyAutoUrl.checked = ! network.proxyConfig["URL"]; + form.networkName = network.name; + form.ipv4 = network.ipv4; + form.nameservers = network.nameservers; + form.domains = network.domains; + form.proxy = network.proxy; + form.proxyConfig = network.proxyConfig; + } + + RowLayout { + z: 2 + anchors { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + + Button { + text: "Accept" + onClicked: { + var domains = [], + nameservers = [], + ipv4, + proxyconf, + proxy_server_text; + + console.log("Accept"); + + // Domains + if (network.domains.join() !== domainsField.text) { + if (domainsField.text) { + domains = domainsField.text.split(); + } + console.log("Update Domains: " + domains.join()); + network.domainsConfig = domains; + } + + // IPv4 + ipv4 = network.ipv4; + if (ipv4["Method"] !== method.state) { + ipv4["Method"] = method.state; + if (method.state === "manual") { + ipv4["Address"] = address.text + ipv4["Netmask"] = netmask.text + ipv4["Gateway"] = gateway.text + } + network.ipv4Config = ipv4; + } else if (network.ipv4["Method"] === "manual") { + if (ipv4["Address"] !== address.text || + ipv4["Netmask"] !== netmask.text || + ipv4["Gateway"] !== gateway.text) { + + ipv4["Address"] = address.text + ipv4["Netmask"] = netmask.text + ipv4["Gateway"] = gateway.text + network.ipv4Config = ipv4; + } + } + + // Nameservers + if (method.state === "manual" && + network.nameserversConfig.join() !== nameserversField.text) { + nameservers = nameserversField.text.split(); + network.nameserversConfig = nameservers; + } + + // Proxy + proxyconf = network.proxyConfig; + if (proxyconf["Method"] !== proxy.state) { + proxyconf["Method"] = proxy.state; + if (proxy.state === "auto") { + proxyconf["URL"] = proxyurl.text; + } else if (proxy.state === "manual") { + proxyconf["Servers"] = [proxyserver.text + ":" + proxyport.text]; + } + network.proxyConfig = proxyconf; + } else if (proxy.state === "manual") { + proxy_server_text = proxyserver.text + ":" + proxyport.text; + if (proxyconf["Servers"].length === 0 || proxyconf["Servers"][0] !== proxy_server_text) { + proxyconf["Servers"] = [proxy_server_text]; + network.proxyConfig = proxyconf; + } + } else if (proxy.state == "auto") { + if (proxyAutoUrl.checked && proxyconf["URL"]) { + // empty URL to use the provided by DHCP + proxyconf["URL"] = ""; + network.proxyConfig = proxyconf; + } else if (! proxyAutoUrl.checked && proxyurl.text !== proxyconf["URL"]) { + // manual URL is used and it needs update + proxyconf["URL"] = proxyurl.text; + network.proxyConfig = proxyconf; + } + } + + + } + } + Button { + text: "Cancel" + onClicked: { + stackView.pop(); + } + } + } + + PlasmaExtras.ScrollArea { + anchors.fill: parent + Flickable { + id: form + anchors.fill: parent + anchors.leftMargin: 10 + anchors.topMargin: 10 + //contentWidth: mainColumn.width + contentHeight: mainColumn.height + flickableDirection: Flickable.VerticalFlick + + property string networkName: "Error" + property variant ipv4: {"Method": "manual", "Address": "", "Netmask": "", "Gateway": ""} + property variant nameservers: [] + property variant domains: [] + property variant proxy: {"Method": "auto", "URL": ""} + property variant proxyConfig: {"Servers": []} + + Column { + id: mainColumn + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + spacing: 10 + + Rectangle { + anchors { left: parent.left; right: parent.right } + height: 80 + color: "transparent" + + Image { + anchors { left: parent.left; verticalCenter: parent.verticalCenter } + source: "image://theme/icon-m-common-wlan-strength5" + width: 60 + height: 60 + } + + Text { + anchors { left: parent.left; verticalCenter: parent.verticalCenter; leftMargin: 80 } + text: form.networkName + } + } + + Rectangle { + anchors { left: parent.left; right: parent.right } + height: 100 + color: "transparent" + Button { + id: disconnectButton + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + text: "Disconnect" + onClicked: { + console.log("Disconnect clicked"); + network.requestDisconnect(); + sheet.close(); + } + } + } + + Item { + anchors { left: parent.left; right: parent.right } + height: 100 + Text { + anchors { left: parent.left; leftMargin: 20 } + text: "Method" + } + ButtonRow { + id: method + anchors { left: parent.left; right: parent.right; top: parent.top; topMargin: 30; leftMargin: 10; rightMargin: 10 } + state: form.ipv4.Method + + states: [ + State { + name: "dhcp" + PropertyChanges {target: networkInfo; visible: true} + PropertyChanges {target: networkFields; visible: false} + }, + State { + name: "manual" + PropertyChanges {target: networkInfo; visible: false} + PropertyChanges {target: networkFields; visible: true} + } + ] + + Button { + text: "DHCP" + checked: form.ipv4.Method == "dhcp" + onClicked: { + method.state = "dhcp" + } + } + Button { + text: "Static" + checked: form.ipv4.Method == "manual" + onClicked: { + method.state = "manual" + } + } + } + } + + Item { + id: networkInfo + anchors { left: parent.left; right: parent.right } + height: 340 + + Column { + spacing: 50 + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "IP address" + } + Text { + anchors { left: parent.left; leftMargin: 20; top:parent.top; topMargin: 30 } + text: form.ipv4.Address + } + } + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "Subnet mask" + } + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + text: form.ipv4.Netmask + } + } + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "Router" + } + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + text: form.ipv4.Gateway + } + } + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "DNS" + } + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + text: form.nameservers.join() + } + } + } + } + + Item { + id: networkFields + anchors { left: parent.left; right: parent.right } + height: 360 + + Column { + spacing: 50 + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "IP address" + } + TextField { + id: address + anchors { left: parent.left; leftMargin: 20; top:parent.top; topMargin: 30 } + width: 440 + text: form.ipv4.Address + } + } + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "Subnet mask" + } + TextField { + id: netmask + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + width: 440 + text: form.ipv4.Netmask + } + } + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "Router" + } + TextField { + id: gateway + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + width: 440 + text: form.ipv4.Gateway + } + } + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "DNS" + } + TextField { + id: nameserversField + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + width: 440 + text: { + var nservs = ""; + if (sheet.network) { + nservs = sheet.network.nameserversConfig.join(); + return nservs ? nservs : form.nameservers.join(); + } else { + return ""; + } + } + } + } + } + } + + Item { + height: 100 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "Search domains" + } + TextField { + id: domainsField + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + width: 440 + text: form.domains.join() + } + } + + Item { + anchors { left: parent.left; right: parent.right } + height: 100 + Text { + anchors { left: parent.left; leftMargin: 20 } + text: "HTTP Proxy" + } + ButtonRow { + id: proxy + anchors { left: parent.left; right: parent.right; top: parent.top; topMargin: 30; leftMargin: 10; rightMargin: 10 } + state: form.proxy.Method + + states: [ + State { + name: "direct" + PropertyChanges {target: proxyManualFields; visible: false} + PropertyChanges {target: proxyAutoFields; visible: false} + PropertyChanges {target: directProxyButton; checked: true} + PropertyChanges {target: manualProxyButton; checked: false} + PropertyChanges {target: autoProxyButton; checked: false} + }, + State { + name: "auto" + PropertyChanges {target: proxyManualFields; visible: false} + PropertyChanges {target: proxyAutoFields; visible: true} + PropertyChanges {target: directProxyButton; checked: false} + PropertyChanges {target: manualProxyButton; checked: false} + PropertyChanges {target: autoProxyButton; checked: true} + }, + State { + name: "manual" + PropertyChanges {target: proxyManualFields; visible: true} + PropertyChanges {target: proxyAutoFields; visible: false} + PropertyChanges {target: directProxyButton; checked: false} + PropertyChanges {target: manualProxyButton; checked: true} + PropertyChanges {target: autoProxyButton; checked: false} + } + ] + + Button { + id: directProxyButton + text: "Off" + onClicked: { + proxy.state = "direct" + } + } + Button { + id: manualProxyButton + text: "Manual" + onClicked: { + proxy.state = "manual" + } + } + Button { + id: autoProxyButton + text: "Auto" + onClicked: { + proxy.state = "auto" + } + } + } + } + + Item { + id: proxyManualFields + anchors { left: parent.left; right: parent.right } + height: 440 + + Column { + spacing: 50 + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "Server" + } + TextField { + id: proxyserver + anchors { left: parent.left; leftMargin: 20; top:parent.top; topMargin: 30 } + width: 440 + text: form.proxyConfig.Servers ? form.proxyConfig.Servers[0].split(":")[0] : "" + } + } + Item { + height: 40 + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "Port" + } + TextField { + id: proxyport + anchors { left: parent.left; leftMargin: 20; top: parent.top; topMargin: 30 } + width: 440 + text: form.proxyConfig.Servers ? form.proxyConfig.Servers[0].split(":")[1] : "" + // TODO: validator + } + } + } + } + + Item { + id: proxyAutoFields + anchors { left: parent.left; right: parent.right } + height: 440 + + Column { + spacing: 50 + CheckBox { + id: proxyAutoUrl + text: "Use URL provided by DHCP server" + } + Item { + height: 40 + visible: !proxyAutoUrl.checked + anchors { left: parent.left; right: parent.right } + + Text { + anchors { left: parent.left; leftMargin: 20; top: parent.top } + text: "URL" + } + TextField { + id: proxyurl + anchors { left: parent.left; leftMargin: 20; top:parent.top; topMargin: 30 } + width: 440 + readOnly: proxyAutoUrl.checked + text: form.proxy.URL + // TODO: validator + } + } + } + } + } + } + } +} + diff --git a/settingsmodules/WirelessSettings.qml b/settingsmodules/WirelessSettings.qml new file mode 100644 index 00000000..9fe7bede --- /dev/null +++ b/settingsmodules/WirelessSettings.qml @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2013 Robin Burchell + * Copyright (C) 2012 Jolla Ltd. + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import MeeGo.Connman 0.2 +import QtQuick.Controls 1.2 + + +StackView { + id: stackView + initialItem: mainView + + Component { + id: mainView + Item { + id: mainWindow + property Item tools: commonTools + + Timer { + id: scanTimer + interval: 25000 + running: networkingModel.powered + repeat: true + triggeredOnStart: true + onTriggered: networkingModel.requestScan(); + } + + TechnologyModel { + id: networkingModel + name: "wifi" + property bool sheetOpened + property string networkName + + onTechnologiesChanged: { + scanTimer.running = networkingModel.powered; + } + + onPoweredChanged: { + scanTimer.running = networkingModel.powered; + } + } + + UserAgent { + id: userAgent + onUserInputRequested: { + scanTimer.running = false; + scanTimer.triggeredOnStart = false; + console.log("USER INPUT REQUESTED"); + var view = { + "fields": [] + }; + for (var key in fields) { + view.fields.push({ + "name": key, + "id": key.toLowerCase(), + "type": fields[key]["Type"], + "requirement": fields[key]["Requirement"] + }); + console.log(key + ":"); + for (var inkey in fields[key]) { + console.log(" " + inkey + ": " + fields[key][inkey]); + } + } + if (!networkingModel.sheetOpened) { + networkingModel.sheetOpened = true + var sheet = pageStack.openSheet(Qt.resolvedUrl("NetworkSettingsSheet.qml"), { + mustacheView: view, + networkName: networkingModel.networkName, + userAgent: userAgent, + scanTimer: scanTimer + }) + sheet.accepted.connect(function() { networkingModel.sheetOpened = false }) + sheet.rejected.connect(function() { networkingModel.sheetOpened = false }) + // TODO: there was code that checked for pageStack.busy and + // didn't open if it was true. What was that about? + } + } + + onErrorReported: { + console.log("Got error from model: " + error); + if (error == "invalid-key") { + mainpageNotificationBanner.text = "Incorrect value entered. Try again." + } else { + mainpageNotificationBanner.text = "Connect failed" + } + mainpageNotificationBanner.show() + } + } + + PlasmaExtras.Heading { + visible: !networkingModel.available || networkList.count == 0 + text: !networkingModel.available ? "Wireless networking unavailable" : "No wireless networks in range" + } + + PlasmaExtras.ScrollArea { + anchors.fill: parent + ListView { + id: networkList + //header: WirelessApplet { } + anchors.margins: UiConstants.DefaultMargin + anchors.fill: parent + model: networkingModel + delegate: PlasmaComponents.ListItem { + + PlasmaCore.IconItem { + anchors { + left: parent.left + top: parent.top + bottom: parent.bototm + } + width: height + source: { + var strength = modelData.strength; + var str_id = 0; + + if (strength >= 100) { + str_id = 100; + } else if (strength >= 80) { + str_id = 80; + } else if (strength >= 60) { + str_id = 60; + } else if (strength >= 40) { + str_id = 40; + } else if (strength >= 20) { + str_id = 20; + } + return "network-wireless-" + str_id; + } + } + ColumnLayout { + anchors { + left: icon.right + top: parent.top + right: parent.right + bottom: parent.bottom + } + PlasmaExtras.Heading { + level: 2 + text: modelData.name ? modelData.name : "(hidden network)" + } + PlasmaComponents.Label { + text: { + var state = modelData.state; + var security = modelData.security[0]; + + if ((state == "online") || (state == "ready")) { + return "connected"; + } else if (state == "association" || state == "configuration") { + return "connecting..."; + } else { + if (security == "none") { + return "open"; + } else { + return "secure"; + } + } + } + } + } + + // TODO: subtitleColor / subtitleColorPressed bindings + // depending on state like in old delegate? + onClicked: { + console.log("clicked " + modelData.name); + if (modelData.state == "idle" || modelData.state == "failure") { + modelData.requestConnect(); + networkingModel.networkName.text = modelData.name; + } else { + console.log("Show network status page"); + for (var key in modelData.ipv4) { + console.log(key + " -> " + modelData.ipv4[key]); + } + + stackView.push({item: Qt.resolvedUrl("SettingsSheet.qml"), properties: { network: modelData }}) + } + } + } + } + } + } + } +} +