on xenial help sign in
merge trunk
f7ce847Michael Zanetti
Dash/Apps/FrequentlyUsedAppsModel.qml+1−1
@@ -38,7 +38,7 @@ ListModel {
38 metadata: ""
38 metadata: ""
39 }
39 }
40 ListElement {
40 ListElement {
41− uri: "application:///usr/share/applications/galery-app.desktop"
41+ uri: "application:///usr/share/applications/gallery-app.desktop"
42 icon: "../../graphics/applicationIcons/gallery.png"
42 icon: "../../graphics/applicationIcons/gallery.png"
43 category: 0
43 category: 0
44 mimetype: "application/x-desktop"
44 mimetype: "application/x-desktop"
Launcher/LauncherDelegate.qml+73−7
@@ -21,7 +21,8 @@ Item {
21 id: root
21 id: root
22
22
23 property string iconName
23 property string iconName
24−
24+ property int count: -1
25+ property int progress: -1
25 property bool highlighted: false
26 property bool highlighted: false
26 property real maxAngle: 0
27 property real maxAngle: 0
27 property bool inverted: false
28 property bool inverted: false
@@ -38,6 +39,11 @@ Item {
38 property real itemOpacity: 1
39 property real itemOpacity: 1
39 property real brightness: 0
40 property real brightness: 0
40
41
42+ onIconNameChanged: shaderEffectSource.scheduleUpdate();
43+ onCountChanged: shaderEffectSource.scheduleUpdate();
44+ onProgressChanged: shaderEffectSource.scheduleUpdate();
45+ onHighlightedChanged: shaderEffectSource.scheduleUpdate();
46+
41 Item {
47 Item {
42 id: iconItem
48 id: iconItem
43 width: parent.itemWidth
49 width: parent.itemWidth
@@ -54,9 +60,7 @@ Item {
54 id: iconImage
60 id: iconImage
55 sourceSize.width: iconShape.width
61 sourceSize.width: iconShape.width
56 sourceSize.height: iconShape.height
62 sourceSize.height: iconShape.height
57− source: "../graphics/applicationIcons/" + iconName + ".png"
63+ source: "../graphics/applicationIcons/" + root.iconName + ".png"
58− property string iconName: root.iconName
59− onIconNameChanged: shaderEffectSource.scheduleUpdate();
60 }
64 }
61 }
65 }
62
66
@@ -64,11 +68,73 @@ Item {
64 id: overlayHighlight
68 id: overlayHighlight
65 anchors.centerIn: iconItem
69 anchors.centerIn: iconItem
66 rotation: inverted ? 180 : 0
70 rotation: inverted ? 180 : 0
67− source: isSelected ? "graphics/selected.sci" : "graphics/non-selected.sci"
71+ source: root.highlighted ? "graphics/selected.sci" : "graphics/non-selected.sci"
68 width: root.itemWidth + units.gu(0.5)
72 width: root.itemWidth + units.gu(0.5)
69 height: root.itemHeight + units.gu(0.5)
73 height: root.itemHeight + units.gu(0.5)
70− property bool isSelected: root.highlighted
74+ }
71− onIsSelectedChanged: shaderEffectSource.scheduleUpdate();
75+
76+ BorderImage {
77+ objectName: "countEmblem"
78+ anchors {
79+ right: parent.right
80+ top: parent.top
81+ margins: -units.dp(1)
82+ }
83+ width: Math.min(units.gu(7.5), Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
84+ height: units.gu(3)
85+ source: "graphics/notification.sci"
86+ visible: root.count > 0
87+
88+ Label {
89+ id: countLabel
90+ text: root.count
91+ anchors.centerIn: parent
92+ width: units.gu(6)
93+ horizontalAlignment: Text.AlignHCenter
94+ elide: Text.ElideRight
95+ color: "white"
96+ fontSize: "small"
97+ font.bold: true
98+ }
99+ }
100+
101+ BorderImage {
102+ id: progressOverlay
103+ objectName: "progressOverlay"
104+ anchors {
105+ left: iconItem.left
106+ right: iconItem.right
107+ bottom: iconItem.bottom
108+ leftMargin: units.gu(0.5)
109+ rightMargin: units.gu(0.5)
110+ bottomMargin: units.gu(0.5)
111+ }
112+ height: units.gu(1.5)
113+ visible: root.progress > -1
114+ source: "graphics/progressbar-trough.sci"
115+
116+ // For fill calculation we need to remove the 2 units of border defined in .sci file
117+ property int adjustedWidth: width - units.gu(2)
118+
119+ Item {
120+ anchors {
121+ left: parent.left
122+ top: parent.top
123+ bottom: parent.bottom
124+ }
125+ width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
126+ clip: true
127+
128+ BorderImage {
129+ anchors {
130+ left: parent.left
131+ top: parent.top
132+ bottom: parent.bottom
133+ }
134+ width: progressOverlay.width
135+ source: "graphics/progressbar-fill.sci"
136+ }
137+ }
72 }
138 }
73 }
139 }
74
140
Launcher/LauncherPanel.qml+14−1
@@ -135,6 +135,8 @@ Item {
135 width: itemWidth
135 width: itemWidth
136 height: itemHeight
136 height: itemHeight
137 iconName: model.icon
137 iconName: model.icon
138+ count: model.count
139+ progress: model.progress
138 inverted: root.inverted
140 inverted: root.inverted
139 z: -Math.abs(offset)
141 z: -Math.abs(offset)
140 maxAngle: 55
142 maxAngle: 55
@@ -236,7 +238,7 @@ Item {
236 property int draggedIndex: -1
238 property int draggedIndex: -1
237 property var selectedItem
239 property var selectedItem
238 property bool preDragging: false
240 property bool preDragging: false
239− property bool dragging: selectedItem !== undefined && selectedItem.dragging
241+ property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
240 property bool postDragging: false
242 property bool postDragging: false
241 property int startX
243 property int startX
242 property int startY
244 property int startY
@@ -250,6 +252,11 @@ Item {
250 var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
252 var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
251 var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
253 var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
252
254
255+ // Check if we actually clicked an item or only at the spacing in between
256+ if (clickedItem === null) {
257+ return;
258+ }
259+
253 // First/last item do the scrolling at more than 12 degrees
260 // First/last item do the scrolling at more than 12 degrees
254 if (index == 0 || index == launcherListView.count - 1) {
261 if (index == 0 || index == launcherListView.count - 1) {
255 if (clickedItem.angle > 12) {
262 if (clickedItem.angle > 12) {
@@ -286,6 +293,10 @@ Item {
286 draggedIndex = -1;
293 draggedIndex = -1;
287 }
294 }
288
295
296+ if (!selectedItem) {
297+ return;
298+ }
299+
289 selectedItem.dragging = false;
300 selectedItem.dragging = false;
290 selectedItem = undefined;
301 selectedItem = undefined;
291 preDragging = false;
302 preDragging = false;
@@ -325,6 +336,8 @@ Item {
325 fakeDragItem.y = mouseY - yOffset + launcherListView.anchors.topMargin + launcherListView.topMargin
336 fakeDragItem.y = mouseY - yOffset + launcherListView.anchors.topMargin + launcherListView.topMargin
326 fakeDragItem.angle = selectedItem.angle * (root.inverted ? -1 : 1)
337 fakeDragItem.angle = selectedItem.angle * (root.inverted ? -1 : 1)
327 fakeDragItem.offset = selectedItem.offset * (root.inverted ? -1 : 1)
338 fakeDragItem.offset = selectedItem.offset * (root.inverted ? -1 : 1)
339+ fakeDragItem.count = LauncherModel.get(draggedIndex).count
340+ fakeDragItem.progress = LauncherModel.get(draggedIndex).progress
328 fakeDragItem.flatten()
341 fakeDragItem.flatten()
329 drag.target = fakeDragItem
342 drag.target = fakeDragItem
330
343
Launcher/graphics/notification@30.pngnew file

Binary file not shown.

Launcher/graphics/notification@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 36
3+border.bottom: 36
4+border.right: 36
5+source: notification@30.png
Launcher/graphics/progressbar-fill@30.pngnew file

Binary file not shown.

Launcher/graphics/progressbar-fill@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 16
3+border.bottom: 28
4+border.right: 36
5+source: progressbar-fill@30.png
Launcher/graphics/progressbar-trough@30.pngnew file

Binary file not shown.

Launcher/graphics/progressbar-trough@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 16
3+border.bottom: 26
4+border.right: 36
5+source: progressbar-trough@30.png
Panel/Indicators/NetworkIndicatorPage2.qmlnew file+82
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Unity.Indicators 0.1 as Indicators
22+import Unity.Indicators.Network 0.1 as ICNetwork
23+import Ubuntu.Components 0.1
24+
25+PageStack {
26+ id: networkPage
27+ // Stops the search for a parent tree node.
28+ // We don't want it going up to the indicator tabs.
29+ // FIXME: need a better way of doing this.
30+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
31+ __propagated: null
32+
33+ property alias title: _mainPage.title
34+ property alias emptyText: pluginItem.emptyText
35+ property alias busName: pluginItem.busName
36+ property alias actionsObjectPath : pluginItem.actionsObjectPath
37+ property alias menuObjectPaths : pluginItem.menuObjectPaths
38+
39+ anchors.fill: parent
40+
41+ Page {
42+ id: _mainPage
43+
44+ Indicators.IndicatorPage {
45+ id: pluginItem
46+ anchors.fill: parent
47+ }
48+ }
49+
50+ Component {
51+ id: passwordPageComponent
52+
53+ ICNetwork.PasswordPage {
54+ agent: networkAgent
55+ }
56+ }
57+
58+ ICNetwork.NetworkAgent {
59+ id: networkAgent
60+
61+ onSecretRequested: {
62+ networkPage.push(passwordPageComponent, {"token": token});
63+ }
64+ }
65+
66+ function start()
67+ {
68+ push(_mainPage);
69+ pluginItem.start();
70+ }
71+
72+ function stop()
73+ {
74+ clear();
75+ pluginItem.stop();
76+ }
77+
78+ function reset()
79+ {
80+ pluginItem.reset();
81+ }
82+}
Panel/IndicatorsDataModel.qml+2−10
@@ -33,7 +33,7 @@ Indicators.IndicatorsModel {
33 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
33 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
34 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
34 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
35 },
35 },
36− 'indicator-messaging' : {
36+ 'indicator-messages' : {
37 'title': 'Messaging',
37 'title': 'Messaging',
38 'description' : '',
38 'description' : '',
39 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
39 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
@@ -49,7 +49,7 @@ Indicators.IndicatorsModel {
49 'title': 'Networks',
49 'title': 'Networks',
50 'description' : '',
50 'description' : '',
51 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
51 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
52− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
52+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage2.qml'
53 },
53 },
54 'indicator-power' : {
54 'indicator-power' : {
55 'title': 'Battery',
55 'title': 'Battery',
@@ -68,14 +68,6 @@ Indicators.IndicatorsModel {
68 'description' : '',
68 'description' : '',
69 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
69 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
70 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
70 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
71− },
72−
73− // OLD FORMAT INDICATORS
74− 'chewie-network' : {
75− 'title': 'Networks',
76− 'description' : '',
77− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorWidget.qml',
78− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage.qml'
79 }
71 }
80 }
72 }
81}
73}
build+5−2
@@ -40,11 +40,14 @@ install_dependencies() {
40 echo "Installing Unity 8 dependencies.."
40 echo "Installing Unity 8 dependencies.."
41 sudo apt-get install devscripts \
41 sudo apt-get install devscripts \
42 equivs \
42 equivs \
43+ gsettings-desktop-schemas \
43 qmenumodel-qml \
44 qmenumodel-qml \
44− qtdeclarative5-ubuntu-ui-toolkit-plugin \
45 qtdeclarative5-dee-plugin \
45 qtdeclarative5-dee-plugin \
46+ qtdeclarative5-gsettings1.0 \
47+ qtdeclarative5-ubuntu-ui-toolkit-plugin \
48+ unity-notifications-impl \
49+ qtdeclarative5-xmllistmodel-plugin \
46 ubuntu-mobile-icons \
50 ubuntu-mobile-icons \
47− unity-notifications-impl-1 \
48 unity-scope-home \
51 unity-scope-home \
49 unity-lens-applications \
52 unity-lens-applications \
50 || exit 5
53 || exit 5
debian/changelog+36
@@ -1,3 +1,39 @@
1+unity8 (7.81.3+13.10.20130821-0ubuntu1) saucy; urgency=low
2+
3+ [ Michael Zanetti ]
4+ * added support for count emblems and progress overlays on the
5+ launcher.
6+
7+ [ Pawel Stolowski ]
8+ * Add role for getting filter options model. Add method to activate
9+ option based on index or id.
10+
11+ [ Ted Gould ]
12+ * Mark indicators-client as providing an indicator-renderer.
13+
14+ [ Nick Dedekind ]
15+ * Added flatmenuproxymodel pass-through signals from qmenumodel.
16+ * Renamed indicator-messaging to indicator-messages.
17+
18+ [ Gerry Boland ]
19+ * Typo fix in FrequentlyUsedAppsModel.
20+
21+ [ Ubuntu daily release ]
22+ * Automatic snapshot from revision 224
23+
24+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 08:05:13 +0000
25+
26+unity8 (7.81.3+13.10.20130820.2-0ubuntu1) saucy; urgency=low
27+
28+ [ Michael Zanetti ]
29+ * initial support for quicklists For now they support pinning and
30+ removing of items.
31+
32+ [ Ubuntu daily release ]
33+ * Automatic snapshot from revision 217
34+
35+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 10:09:36 +0000
36+
1unity8 (7.81.3+13.10.20130820-0ubuntu1) saucy; urgency=low
37unity8 (7.81.3+13.10.20130820-0ubuntu1) saucy; urgency=low
2
38
3 [ Andrea Cimitan ]
39 [ Andrea Cimitan ]
debian/control+1
@@ -60,6 +60,7 @@ Description: Indicators client test application
60
60
61Package: unity8
61Package: unity8
62Architecture: any
62Architecture: any
63+Provides: indicator-renderer,
63Depends: gsettings-desktop-schemas,
64Depends: gsettings-desktop-schemas,
64 qmenumodel-qml (>= 0.2.7),
65 qmenumodel-qml (>= 0.2.7),
65 qtdeclarative5-dee-plugin,
66 qtdeclarative5-dee-plugin,
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -12,6 +12,7 @@ find_package(Qt5Core REQUIRED)
12find_package(Qt5Quick REQUIRED)
12find_package(Qt5Quick REQUIRED)
13
13
14add_subdirectory(qml)
14add_subdirectory(qml)
15+add_subdirectory(Network)
15
16
16include_directories(
17include_directories(
17 ${CMAKE_CURRENT_SOURCE_DIR}
18 ${CMAKE_CURRENT_SOURCE_DIR}
plugins/Unity/Indicators/Network/CMakeLists.txtnew file+45
@@ -0,0 +1,45 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+project(IndicatorsNetworkQml)
5+
6+pkg_check_modules(GLIB glib-2.0>=2.32)
7+pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
8+pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
9+
10+if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11+ set(INDICATORS_NETWORK_SRC
12+ networkagent.cpp
13+ plugin.cpp
14+ secret-agent.c
15+ )
16+
17+ add_library(IndicatorsNetworkQml MODULE
18+ ${INDICATORS_NETWORK_SRC}
19+ )
20+
21+ qt5_use_modules(IndicatorsNetworkQml Core Qml Widgets)
22+
23+ set_target_properties(IndicatorsNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
24+
25+ include_directories(
26+ ${CMAKE_CURRENT_SOURCE_DIR}
27+ ${GLIB_INCLUDE_DIRS}
28+ ${LIBNM_GLIB_INCLUDE_DIRS}
29+ ${LIBNM_UTIL_INCLUDE_DIRS}
30+ )
31+
32+ target_link_libraries(IndicatorsNetworkQml
33+ ${GLIB_LIBRARIES}
34+ ${LIBNM_GLIB_LIBRARIES}
35+ ${LIBNM_UTIL_LIBRARIES}
36+ )
37+
38+ # export the qmltypes and plugin files
39+ export_qmlplugin(Unity.Indicators.Network 0.1 Unity/Indicators/Network TARGETS IndicatorsNetworkQml)
40+
41+else()
42+ message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
43+endif()
44+
45+add_subdirectory(qml)
plugins/Unity/Indicators/Network/networkagent.cppnew file+146
@@ -0,0 +1,146 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#include "networkagent.h"
22+
23+#include <QVariant>
24+#include <QApplication>
25+#include <QDebug>
26+
27+class NetworkAgentToken
28+{
29+public:
30+ NMConnection *connection;
31+ NMSettingWirelessSecurity *wisec;
32+ guint id;
33+ QByteArray keyMgmt;
34+
35+ NetworkAgentToken(NMConnection *connection,
36+ NMSettingWirelessSecurity *wisec,
37+ guint id,
38+ QByteArray keyMgmt)
39+ : connection(connection),
40+ wisec(wisec),
41+ id(id),
42+ keyMgmt(keyMgmt)
43+ {
44+ if (connection) {
45+ g_object_ref(connection);
46+ } else {
47+ qWarning() << "invalid connection object";
48+ }
49+ }
50+
51+ ~NetworkAgentToken()
52+ {
53+ if (connection) {
54+ g_object_unref(connection);
55+ }
56+ }
57+};
58+
59+NetworkAgent::NetworkAgent(QObject *parent)
60+ : QObject(parent)
61+{
62+ m_agent = unity_settings_secret_agent_new();
63+ g_signal_connect(G_OBJECT(m_agent),
64+ UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
65+ G_CALLBACK(onSecretRequested),
66+ this);
67+
68+ g_signal_connect(G_OBJECT(m_agent),
69+ UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
70+ G_CALLBACK(onSecretRequestCancelled),
71+ this);
72+}
73+
74+NetworkAgent::~NetworkAgent()
75+{
76+ nm_secret_agent_unregister(NM_SECRET_AGENT(m_agent));
77+ g_object_unref(m_agent);
78+}
79+
80+void NetworkAgent::authenticate(const QVariant &token, const QString &key)
81+{
82+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
83+ if (pToken == NULL) {
84+ return;
85+ }
86+
87+ if ((pToken->keyMgmt == "wpa-none") || (pToken->keyMgmt == "wpa-psk")) {
88+ g_object_set(G_OBJECT(pToken->wisec),
89+ NM_SETTING_WIRELESS_SECURITY_PSK, qPrintable(key),
90+ NULL);
91+ } else if (pToken->keyMgmt == "none") {
92+ g_object_set(G_OBJECT(pToken->wisec),
93+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, qPrintable(key),
94+ NULL);
95+ }
96+
97+ GHashTable *settings = nm_connection_to_hash(pToken->connection,
98+ NM_SETTING_HASH_FLAG_ALL);
99+
100+ unity_settings_secret_agent_provide_secret(m_agent,
101+ pToken->id, settings);
102+ g_hash_table_unref(settings);
103+ delete pToken;
104+}
105+
106+void NetworkAgent::cancel(const QVariant &token)
107+{
108+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
109+ if (pToken != NULL) {
110+ unity_settings_secret_agent_cancel_request(m_agent, pToken->id);
111+ delete pToken;
112+ }
113+}
114+
115+void NetworkAgent::onSecretRequested(UnitySettingsSecretAgent* /*agent*/,
116+ guint id,
117+ NMConnection* connection,
118+ const char* /*setting_name*/,
119+ const char** /*hints*/,
120+ NMSecretAgentGetSecretsFlags /*flags*/,
121+ NetworkAgent* self)
122+{
123+ NetworkAgentToken *token = new NetworkAgentToken(connection,
124+ NULL,
125+ id,
126+ NULL);
127+
128+ token->wisec = nm_connection_get_setting_wireless_security(connection);
129+ if (token->wisec) {
130+ token->keyMgmt = nm_setting_wireless_security_get_key_mgmt(token->wisec);
131+ }
132+
133+ QVariant varToken = qVariantFromValue((void *) token);
134+ Q_EMIT self->secretRequested(varToken);
135+}
136+
137+void NetworkAgent::onSecretRequestCancelled(UnitySettingsSecretAgent* /*agent*/,
138+ guint /*id*/,
139+ NMConnection* /*connection*/,
140+ const char* /*setting_name*/,
141+ const char** /*hints*/,
142+ NMSecretAgentGetSecretsFlags /*flags*/,
143+ NetworkAgent* /*self*/)
144+{
145+ //Do nothing for now
146+}
plugins/Unity/Indicators/Network/networkagent.hnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#ifndef NETWORKAGENT_H
22+#define NETWORKAGENT_H
23+
24+#include "secret-agent.h"
25+
26+#include <QObject>
27+
28+class NetworkAgentToken;
29+
30+class NetworkAgent : public QObject
31+{
32+ Q_OBJECT
33+public:
34+ NetworkAgent(QObject *parent=0);
35+ ~NetworkAgent();
36+
37+ Q_INVOKABLE void authenticate(const QVariant &token, const QString &key);
38+ Q_INVOKABLE void cancel(const QVariant &token);
39+
40+Q_SIGNALS:
41+ void secretRequested(const QVariant &token);
42+ void secretRequestCancelled();
43+
44+private:
45+ UnitySettingsSecretAgent *m_agent;
46+
47+ static void onSecretRequested(UnitySettingsSecretAgent *agent,
48+ guint id,
49+ NMConnection *connection,
50+ const char *setting_name,
51+ const char **hints,
52+ NMSecretAgentGetSecretsFlags flags,
53+ NetworkAgent *self);
54+
55+ static void onSecretRequestCancelled(UnitySettingsSecretAgent *agent,
56+ guint id,
57+ NMConnection *connection,
58+ const char *setting_name,
59+ const char **hints,
60+ NMSecretAgentGetSecretsFlags flags,
61+ NetworkAgent *self);
62+
63+};
64+
65+#endif
plugins/Unity/Indicators/Network/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+// Qt
20+#include <QtQml>
21+
22+// self
23+#include "plugin.h"
24+
25+// local
26+#include "networkagent.h"
27+
28+void IndicatorsNetworkPlugin::registerTypes(const char *uri)
29+{
30+ qmlRegisterType<NetworkAgent>(uri, 0, 1, "NetworkAgent");
31+}
plugins/Unity/Indicators/Network/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef INDICATORS_NETWORK_PLUGIN_H
20+#define INDICATORS_NETWORK_PLUGIN_H
21+
22+#include <QtQml/QQmlExtensionPlugin>
23+
24+class IndicatorsNetworkPlugin : public QQmlExtensionPlugin
25+{
26+ Q_OBJECT
27+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28+public:
29+ void registerTypes(const char *uri);
30+};
31+
32+#endif // INDICATORS_NETWORK_PLUGIN_H
plugins/Unity/Indicators/Network/qml/AccessPoint.qmlnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+import Unity.Indicators 0.1 as Indicators
24+
25+Indicators.FramedMenuItem {
26+ id: accessPoint
27+
28+ readonly property bool checked: menu ? menu.isToggled : false
29+
30+ onCheckedChanged: {
31+ // Can't rely on binding. Checked is assigned on click.
32+ checkBoxActive.checked = checked;
33+ }
34+
35+ // FIXME - we need to get the strength from menu.ext.xCanonicalWifiApStrengthAction
36+ // but UnityMenuModel doesnt support fetching actions not attached to menu item.
37+ function getNetworkIcon(data) {
38+ var imageName = "nm-signal-100"
39+
40+ if (data.ext.xCanonicalWifiApIsAdhoc) {
41+ imageName = "nm-adhoc";
42+ }
43+ if (data.ext.xCanonicalWifiApIsSecure) {
44+ imageName += "-secure";
45+ }
46+
47+ return "image://gicon/" + imageName;
48+ }
49+
50+ icon: menu ? getNetworkIcon(menu) : "image://gicon/wifi-none"
51+ iconFrame: false
52+ control: CheckBox {
53+ id: checkBoxActive
54+ height: units.gu(4)
55+ width: units.gu(4)
56+ anchors.centerIn: parent
57+
58+ onClicked: {
59+ accessPoint.activate();
60+ }
61+ }
62+}
plugins/Unity/Indicators/Network/qml/CMakeLists.txtnew file+5
@@ -0,0 +1,5 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+# export the qmldir, *.qml files.
5+export_qmlfiles(Unity.Indicators.Network Unity/Indicators/Network)
plugins/Unity/Indicators/Network/qml/PasswordPage.qmlnew file+88
@@ -0,0 +1,88 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+import Unity.Indicators 0.1 as Indicators
24+
25+Page {
26+ id: pagePassword
27+
28+ property variant agent
29+ property variant token
30+
31+ anchors.fill: parent
32+ title: "Network Authentication"
33+
34+ Column {
35+ anchors {
36+ left: parent.left
37+ right: parent.right
38+ }
39+
40+ Indicators.SectionMenuItem {
41+ label: "Authentication"
42+ }
43+
44+ Indicators.FramedMenuItem {
45+ implicitHeight: password.height + units.gu(1)
46+
47+ PasswordTextField {
48+ id: password
49+ anchors {
50+ left: parent.left
51+ right: parent.right
52+ margins: units.gu(3)
53+ verticalCenter: parent.verticalCenter
54+ }
55+ }
56+ }
57+
58+ Indicators.FramedMenuItem {
59+ Row {
60+ anchors {
61+ right: parent.right
62+ verticalCenter: parent.verticalCenter
63+ margins: units.gu(3)
64+ }
65+
66+ spacing: units.gu(1)
67+
68+ Button {
69+ text: "Cancel"
70+ width: units.gu(10)
71+ onClicked: {
72+ agent.cancel(token);
73+ pageStack.pop();
74+ }
75+ }
76+
77+ Button {
78+ text: "OK"
79+ width: units.gu(10)
80+ onClicked: {
81+ agent.authenticate(token, password.text);
82+ pageStack.pop();
83+ }
84+ }
85+ }
86+ }
87+ }
88+}
plugins/Unity/Indicators/Network/qml/PasswordTextField.qmlnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+
23+Item {
24+ id: textMenu
25+
26+ property alias text: textField.text
27+
28+ height: contentColumn.height
29+
30+ Column {
31+ id: contentColumn
32+ spacing: units.gu(0.5)
33+ anchors {
34+ left: parent.left
35+ right: parent.right
36+ }
37+
38+ TextField {
39+ id: textField
40+
41+ anchors {
42+ left: parent.left
43+ right: parent.right
44+ }
45+
46+ placeholderText: "Password"
47+ echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password
48+ }
49+
50+ Row {
51+ anchors {
52+ left: parent.left
53+ right: parent.right
54+ }
55+
56+ spacing: units.gu(1)
57+
58+ CheckBox {
59+ id: showPassword
60+ }
61+
62+ Label {
63+ text: "Show password"
64+ anchors.verticalCenter: showPassword.verticalCenter
65+ }
66+ }
67+ }
68+}
plugins/Unity/Indicators/Network/qml/WifiSection.qmlnew file+28
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Unity.Indicators 0.1 as Indicators
23+
24+Indicators.SectionMenuItem {
25+ id: wifiSectionMenu
26+
27+ busy: menu ? menu.ext.xCanonicalBusyAction : false
28+}
plugins/Unity/Indicators/Network/qml/qmldirnew file+9
@@ -0,0 +1,9 @@
1+module Unity.Indicators.Network
2+plugin IndicatorsNetworkQml
3+typeinfo plugin.qmltypes
4+
5+NetworkPage 0.1 NetworkPage.qml
6+AccessPoint 0.1 AccessPoint.qml
7+PasswordPage 0.1 PasswordPage.qml
8+PasswordTextField 0.1 PasswordTextField.qml
9+WifiSection 0.1 WifiSection.qml
plugins/Unity/Indicators/Network/secret-agent.cnew file+382
@@ -0,0 +1,382 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+
22+#include <glib.h>
23+#include <glib-object.h>
24+#include <nm-secret-agent.h>
25+#include "secret-agent.h"
26+
27+#define UNITY_SETTINGS_TYPE_SECRET_AGENT (unity_settings_secret_agent_get_type ())
28+#define UNITY_SETTINGS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgent))
29+#define UNITY_SETTINGS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
30+#define UNITY_SETTINGS_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT))
31+#define UNITY_SETTINGS_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT))
32+#define UNITY_SETTINGS_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
33+#define UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentPrivate))
34+
35+#define AGENT_ID "com.canonical.settings.network.nm-agent"
36+
37+static gpointer unity_settings_secret_agent_parent_class = NULL;
38+
39+typedef struct _UnitySettingsSecretAgentPrivate UnitySettingsSecretAgentPrivate;
40+
41+struct _UnitySettingsSecretAgentPrivate {
42+ GQueue *requests;
43+};
44+
45+typedef struct _SecretRequest {
46+ gint id;
47+ NMSecretAgent *agent;
48+ NMConnection *connection;
49+ const char *connection_path;
50+ const char *setting_name;
51+ const char **hints;
52+ NMSecretAgentGetSecretsFlags flags;
53+ NMSecretAgentGetSecretsFunc callback;
54+ gpointer callback_data;
55+} SecretRequest;
56+
57+GType unity_settings_secret_agent_get_type (void) G_GNUC_CONST;
58+enum {
59+ UNITY_SETTINGS_SECRET_AGENT_DUMMY_PROPERTY
60+};
61+
62+enum {
63+ SECRET_REQUESTED,
64+ REQUEST_CANCELLED,
65+ LAST_SIGNAL
66+};
67+
68+static guint signals[LAST_SIGNAL] = { 0 };
69+
70+UnitySettingsSecretAgent* unity_settings_secret_agent_new (void);
71+UnitySettingsSecretAgent* unity_settings_secret_agent_construct (GType object_type);
72+
73+int
74+secret_request_find (SecretRequest *req,
75+ guint *id)
76+{
77+ if (req->id > *id)
78+ return -1;
79+
80+ if (req->id < *id)
81+ return 1;
82+
83+ return 0;
84+}
85+
86+void
87+unity_settings_secret_agent_provide_secret (UnitySettingsSecretAgent *agent,
88+ guint request,
89+ GHashTable *secrets)
90+{
91+ GList *iter;
92+ SecretRequest *req;
93+ UnitySettingsSecretAgentPrivate *priv = agent->priv;
94+
95+ iter = g_queue_find_custom (priv->requests,
96+ &request,
97+ (GCompareFunc)secret_request_find);
98+
99+ if (iter == NULL || iter->data == NULL)
100+ {
101+ g_warning ("Secret request with id <%d> was not found", (int)request);
102+ return;
103+ }
104+
105+ req = iter->data;
106+
107+ req->callback (NM_SECRET_AGENT (agent),
108+ req->connection,
109+ secrets,
110+ NULL,
111+ req->callback_data);
112+
113+ g_queue_remove_all (priv->requests, req);
114+ g_free (req);
115+ return;
116+}
117+
118+void
119+free_request (SecretRequest *req)
120+{
121+ g_object_unref (req->connection);
122+ g_free (req);
123+}
124+
125+void
126+unity_settings_secret_agent_cancel_request (UnitySettingsSecretAgent *agent,
127+ guint request)
128+{
129+ GList *iter;
130+ SecretRequest *req;
131+ UnitySettingsSecretAgentPrivate *priv = agent->priv;
132+ GError *error;
133+
134+ iter = g_queue_find_custom (priv->requests,
135+ &request,
136+ (GCompareFunc)secret_request_find);
137+
138+ if (iter == NULL || iter->data == NULL)
139+ {
140+ g_warning ("Secret request with id <%d> was not found", (int)request);
141+ return;
142+ }
143+
144+ req = iter->data;
145+ error = g_error_new (NM_SECRET_AGENT_ERROR,
146+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
147+ "This secret request was canceled by the user.");
148+
149+ req->callback (NM_SECRET_AGENT (agent),
150+ req->connection,
151+ NULL,
152+ error,
153+ req->callback_data);
154+
155+ g_queue_remove_all (priv->requests, req);
156+ free_request (req);
157+ return;
158+}
159+
160+static void
161+delete_secrets (NMSecretAgent *agent,
162+ NMConnection *connection,
163+ const char *connection_path,
164+ NMSecretAgentDeleteSecretsFunc callback,
165+ gpointer callback_data)
166+{
167+ g_debug ("delete secrets");
168+}
169+
170+/* If it returns G_MAXUINT it's considered an error */
171+static guint
172+find_available_id (UnitySettingsSecretAgentPrivate *priv)
173+{
174+ guint i = 0;
175+ guint candidate = 0;
176+
177+ if (g_queue_get_length (priv->requests) == G_MAXUINT)
178+ return G_MAXUINT;
179+
180+ while (i < g_queue_get_length (priv->requests))
181+ {
182+ SecretRequest *req = (SecretRequest*)g_queue_peek_nth (priv->requests, i);
183+
184+ if (req->id == candidate)
185+ {
186+ candidate++;
187+ i = 0;
188+ }
189+ else
190+ {
191+ i++;
192+ }
193+ }
194+
195+ return i;
196+}
197+
198+static void
199+get_secrets (NMSecretAgent *agent,
200+ NMConnection *connection,
201+ const char *connection_path,
202+ const char *setting_name,
203+ const char **hints,
204+ NMSecretAgentGetSecretsFlags flags,
205+ NMSecretAgentGetSecretsFunc callback,
206+ gpointer callback_data)
207+{
208+ guint id;
209+ UnitySettingsSecretAgentPrivate *priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (agent);
210+ SecretRequest *req = NULL;
211+
212+ if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE)
213+ {
214+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
215+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
216+ "No password found for this connection.");
217+ callback (agent, connection, NULL, error, callback_data);
218+ g_error_free (error);
219+ return;
220+ }
221+
222+ id = find_available_id (priv);
223+ if (id == G_MAXUINT)
224+ {
225+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
226+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
227+ "Reached maximum number of requests.");
228+ callback (agent, connection, NULL, error, callback_data);
229+ g_error_free (error);
230+ return;
231+ }
232+
233+ /* Adding a request */
234+ req = (SecretRequest*) g_malloc0 (sizeof (SecretRequest));
235+ *req = ((SecretRequest)
236+ { id,
237+ agent,
238+ connection,
239+ connection_path,
240+ setting_name,
241+ hints,
242+ flags,
243+ callback,
244+ callback_data });
245+
246+ g_object_ref (connection);
247+
248+ g_queue_push_tail (priv->requests, req);
249+
250+ g_signal_emit_by_name (agent,
251+ UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
252+ id,
253+ connection,
254+ setting_name,
255+ hints,
256+ flags);
257+}
258+
259+static void
260+save_secrets (NMSecretAgent *agent,
261+ NMConnection *connection,
262+ const char *connection_path,
263+ NMSecretAgentSaveSecretsFunc callback,
264+ gpointer callback_data)
265+{
266+ g_debug ("save secrets");
267+}
268+
269+static void
270+cancel_get_secrets (NMSecretAgent *agent,
271+ const char *connection_path,
272+ const char *setting_name)
273+{
274+ g_debug ("cancel get secrets");
275+}
276+
277+UnitySettingsSecretAgent*
278+unity_settings_secret_agent_construct (GType object_type)
279+{
280+ UnitySettingsSecretAgent * self = NULL;
281+ self = (UnitySettingsSecretAgent*) g_object_new (object_type,
282+ NM_SECRET_AGENT_IDENTIFIER, AGENT_ID,
283+ NULL);
284+ return self;
285+}
286+
287+
288+UnitySettingsSecretAgent*
289+unity_settings_secret_agent_new (void)
290+{
291+ return unity_settings_secret_agent_construct (UNITY_SETTINGS_TYPE_SECRET_AGENT);
292+}
293+
294+static void
295+destroy_pending_request (gpointer data)
296+{
297+ SecretRequest* req = (SecretRequest*)data;
298+ /* Reporting the cancellation of all pending requests */
299+ g_signal_emit_by_name (req->agent,
300+ UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
301+ req->id);
302+
303+ free_request (req);
304+}
305+
306+static void
307+unity_settings_secret_agent_finalize (GObject *agent)
308+{
309+ UnitySettingsSecretAgentPrivate *priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (agent);
310+
311+ g_queue_free_full (priv->requests, destroy_pending_request);
312+}
313+
314+static void
315+unity_settings_secret_agent_class_init (UnitySettingsSecretAgentClass *klass)
316+{
317+ unity_settings_secret_agent_parent_class = g_type_class_peek_parent (klass);
318+ NMSecretAgentClass *parent_class = NM_SECRET_AGENT_CLASS (klass);
319+ parent_class->get_secrets = get_secrets;
320+ parent_class->save_secrets = save_secrets;
321+ parent_class->delete_secrets = delete_secrets;
322+ parent_class->cancel_get_secrets = cancel_get_secrets;
323+
324+ g_type_class_add_private (klass, sizeof(UnitySettingsSecretAgentPrivate));
325+ G_OBJECT_CLASS (klass)->finalize = unity_settings_secret_agent_finalize;
326+
327+
328+ signals[SECRET_REQUESTED] = g_signal_new (UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
329+ G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
330+ G_SIGNAL_RUN_FIRST,
331+ G_STRUCT_OFFSET (UnitySettingsSecretAgentClass, secret_requested),
332+ NULL, NULL, NULL,
333+ G_TYPE_NONE, 5,
334+ G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT);
335+
336+ signals[REQUEST_CANCELLED] = g_signal_new (UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
337+ G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
338+ G_SIGNAL_RUN_FIRST,
339+ G_STRUCT_OFFSET (UnitySettingsSecretAgentClass, request_cancelled),
340+ NULL, NULL, NULL,
341+ G_TYPE_NONE, 1,
342+ G_TYPE_UINT);
343+}
344+
345+
346+static void
347+unity_settings_secret_agent_instance_init (UnitySettingsSecretAgent *self)
348+{
349+ self->priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (self);
350+ self->priv->requests = g_queue_new ();
351+}
352+
353+GType
354+unity_settings_secret_agent_get_type (void)
355+{
356+ static volatile gsize unity_settings_secret_agent_type_id__volatile = 0;
357+ if (g_once_init_enter (&unity_settings_secret_agent_type_id__volatile))
358+ {
359+ static const GTypeInfo g_define_type_info =
360+ {
361+ sizeof (UnitySettingsSecretAgentClass),
362+ (GBaseInitFunc) NULL,
363+ (GBaseFinalizeFunc) NULL,
364+ (GClassInitFunc) unity_settings_secret_agent_class_init,
365+ (GClassFinalizeFunc) NULL,
366+ NULL,
367+ sizeof (UnitySettingsSecretAgent),
368+ 0,
369+ (GInstanceInitFunc) unity_settings_secret_agent_instance_init,
370+ NULL
371+ };
372+ GType unity_settings_secret_agent_type_id;
373+ unity_settings_secret_agent_type_id = g_type_register_static (NM_TYPE_SECRET_AGENT,
374+ "UnitySettingsSecretAgent",
375+ &g_define_type_info,
376+ 0);
377+ g_once_init_leave (&unity_settings_secret_agent_type_id__volatile,
378+ unity_settings_secret_agent_type_id);
379+ }
380+
381+ return unity_settings_secret_agent_type_id__volatile;
382+}
plugins/Unity/Indicators/Network/secret-agent.hnew file+102
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#ifndef __SECRET_AGENT_H__
22+#define __SECRET_AGENT_H__
23+
24+#include <glib.h>
25+#include <glib-object.h>
26+#include <nm-secret-agent.h>
27+
28+/*
29+ * This class is a basic implementation of the NetworkManager SecretAgent base class.
30+ *
31+ * The purpose of this class is to handle credential requests from the network,
32+ * for example, from a WiFi hotspot or a VPN network.
33+ *
34+ * It queues requests objects (SecretRequest) on a GQueue in the private struct
35+ * of the class. And notifies the consumer of the class through the "secret-request"
36+ * and the "request-cancelled" signals with the following callback prototypes:
37+ *
38+ * void (*secret_requested) (UnitySettingsSecretAgent *self,
39+ * guint id,
40+ * NMConnection *connection,
41+ * const char *setting_name,
42+ * const char **hints,
43+ * NMSecretAgentGetSecretsFlags flags,
44+ * gpointer user_data);
45+ *
46+ * void (*request_cancelled) (UnitySettingsSecretAgent *self,
47+ * guint id,
48+ * gpointer user_data);
49+ *
50+ */
51+
52+
53+G_BEGIN_DECLS
54+
55+
56+#define UNITY_SETTINGS_TYPE_SECRET_AGENT (unity_settings_secret_agent_get_type ())
57+#define UNITY_SETTINGS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgent))
58+#define UNITY_SETTINGS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
59+#define UNITY_SETTINGS_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT))
60+#define UNITY_SETTINGS_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT))
61+#define UNITY_SETTINGS_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
62+
63+#define UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED "secret-requested"
64+#define UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED "request-cancelled"
65+
66+typedef struct _UnitySettingsSecretAgent UnitySettingsSecretAgent;
67+typedef struct _UnitySettingsSecretAgentClass UnitySettingsSecretAgentClass;
68+typedef struct _UnitySettingsSecretAgentPrivate UnitySettingsSecretAgentPrivate;
69+
70+struct _UnitySettingsSecretAgent {
71+ NMSecretAgent parent_instance;
72+ UnitySettingsSecretAgentPrivate * priv;
73+};
74+
75+struct _UnitySettingsSecretAgentClass {
76+ NMSecretAgentClass parent_class;
77+
78+ void (*secret_requested) (UnitySettingsSecretAgent *self,
79+ guint id,
80+ NMConnection *connection,
81+ const char *setting_name,
82+ const char **hints,
83+ NMSecretAgentGetSecretsFlags flags);
84+
85+ void (*request_cancelled) (UnitySettingsSecretAgent *self,
86+ guint id);
87+};
88+
89+
90+GType unity_settings_secret_agent_get_type (void) G_GNUC_CONST;
91+UnitySettingsSecretAgent* unity_settings_secret_agent_new (void);
92+UnitySettingsSecretAgent* unity_settings_secret_agent_construct (GType object_type);
93+
94+void unity_settings_secret_agent_provide_secret (UnitySettingsSecretAgent *agent,
95+ guint request,
96+ GHashTable *secrets);
97+void unity_settings_secret_agent_cancel_request (UnitySettingsSecretAgent *agent,
98+ guint request);
99+
100+G_END_DECLS
101+
102+#endif
plugins/Unity/Indicators/qml/MenuItemFactory.qml+21
@@ -19,6 +19,7 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Unity.Indicators 0.1 as Indicators
21import Unity.Indicators 0.1 as Indicators
22+import Unity.Indicators.Network 0.1 as ICNetwork
22
23
23Item {
24Item {
24 id: menuFactory
25 id: menuFactory
@@ -38,6 +39,9 @@ Item {
38
39
39 "com.canonical.unity.slider" : sliderMenu,
40 "com.canonical.unity.slider" : sliderMenu,
40 "com.canonical.unity.switch" : switchMenu,
41 "com.canonical.unity.switch" : switchMenu,
42+
43+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
44+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
41 }
45 }
42
46
43 Component {
47 Component {
@@ -57,6 +61,23 @@ Item {
57 Component { id: progressMenu; Indicators.ProgressMenuItem {} }
61 Component { id: progressMenu; Indicators.ProgressMenuItem {} }
58 Component { id: standardMenu; Indicators.StandardMenuItem {} }
62 Component { id: standardMenu; Indicators.StandardMenuItem {} }
59 Component { id: switchMenu; Indicators.SwitchMenuItem {} }
63 Component { id: switchMenu; Indicators.SwitchMenuItem {} }
64+ Component {
65+ id: wifiSection;
66+ ICNetwork.WifiSection {
67+ Component.onCompleted: {
68+ model.loadExtendedAttributes(modelIndex, {'x-canonical-busy-action': 'bool'});
69+ }
70+ }
71+ }
72+ Component {
73+ id: accessPoint;
74+ ICNetwork.AccessPoint {
75+ Component.onCompleted: {
76+ model.loadExtendedAttributes(modelIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
77+ 'x-canonical-wifi-ap-is-secure': 'bool'});
78+ }
79+ }
80+ }
60
81
61 function load(modelData) {
82 function load(modelData) {
62 if (modelData.type !== undefined) {
83 if (modelData.type !== undefined) {
plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txt+8−8
@@ -1,26 +1,26 @@
1# export_qmlplugin macro
1# export_qmlplugin macro
2include(Plugins)
2include(Plugins)
3
3
4−project(IndicatorsNetworkQml)
4+project(IndicatorsLegacyNetworkQml)
5
5
6pkg_check_modules(GLIB glib-2.0>=2.32)
6pkg_check_modules(GLIB glib-2.0>=2.32)
7pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
7pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
8pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
8pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
9
9
10if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
10if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11− set(INDICATORS_NETWORK_SRC
11+ set(INDICATORS_LEGACY_NETWORK_SRC
12 networkagent.cpp
12 networkagent.cpp
13 plugin.cpp
13 plugin.cpp
14 secret-agent.c
14 secret-agent.c
15 )
15 )
16
16
17− add_library(IndicatorsNetworkQml MODULE
17+ add_library(IndicatorsLegacyNetworkQml MODULE
18− ${INDICATORS_NETWORK_SRC}
18+ ${INDICATORS_LEGACY_NETWORK_SRC}
19 )
19 )
20
20
21− qt5_use_modules(IndicatorsNetworkQml Core Qml Widgets)
21+ qt5_use_modules(IndicatorsLegacyNetworkQml Core Qml Widgets)
22
22
23− set_target_properties(IndicatorsNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
23+ set_target_properties(IndicatorsLegacyNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
24
24
25 include_directories(
25 include_directories(
26 ${CMAKE_CURRENT_SOURCE_DIR}
26 ${CMAKE_CURRENT_SOURCE_DIR}
@@ -29,14 +29,14 @@ if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
29 ${LIBNM_UTIL_INCLUDE_DIRS}
29 ${LIBNM_UTIL_INCLUDE_DIRS}
30 )
30 )
31
31
32− target_link_libraries(IndicatorsNetworkQml
32+ target_link_libraries(IndicatorsLegacyNetworkQml
33 ${GLIB_LIBRARIES}
33 ${GLIB_LIBRARIES}
34 ${LIBNM_GLIB_LIBRARIES}
34 ${LIBNM_GLIB_LIBRARIES}
35 ${LIBNM_UTIL_LIBRARIES}
35 ${LIBNM_UTIL_LIBRARIES}
36 )
36 )
37
37
38 # export the qmltypes and plugin files
38 # export the qmltypes and plugin files
39− export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsNetworkQml)
39+ export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsLegacyNetworkQml)
40
40
41else()
41else()
42 message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
42 message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
plugins/Unity/IndicatorsLegacy/Network/qml/qmldir+1−1
@@ -1,5 +1,5 @@
1module Unity.IndicatorsLegacy.Network
1module Unity.IndicatorsLegacy.Network
2−plugin IndicatorsNetworkQml
2+plugin IndicatorsLegacyNetworkQml
3typeinfo plugin.qmltypes
3typeinfo plugin.qmltypes
4
4
5NetworkPage 0.1 NetworkPage.qml
5NetworkPage 0.1 NetworkPage.qml
plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.cpp+13
@@ -80,6 +80,19 @@ void FlatMenuProxyModel::setSourceModel(QAbstractItemModel *source)
80 SIGNAL(modelReset()),
80 SIGNAL(modelReset()),
81 SLOT(onModelReset()), Qt::QueuedConnection);
81 SLOT(onModelReset()), Qt::QueuedConnection);
82
82
83+ //Connection args
84+ connect(source,
85+ SIGNAL(busTypeChanged(DBusEnums::BusType)),
86+ SIGNAL(busTypeChanged()));
87+
88+ connect(source,
89+ SIGNAL(busNameChanged(const QString&)),
90+ SIGNAL(busNameChanged()));
91+
92+ connect(source,
93+ SIGNAL(objectPathChanged(const QString&)),
94+ SIGNAL(objectPathChanged()));
95+
83 connect(source,
96 connect(source,
84 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
97 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
85 SIGNAL(statusChanged()), Qt::QueuedConnection);
98 SIGNAL(statusChanged()), Qt::QueuedConnection);
plugins/Unity/filters.cpp+4
@@ -23,6 +23,7 @@
23
23
24// Local
24// Local
25#include "filter.h"
25#include "filter.h"
26+#include "genericoptionsmodel.h"
26
27
27// Qt
28// Qt
28#include <QDebug>
29#include <QDebug>
@@ -77,6 +78,8 @@ QVariant Filters::data(const QModelIndex& index, int role) const
77 return filter->collapsed();
78 return filter->collapsed();
78 case Filters::RoleFiltering:
79 case Filters::RoleFiltering:
79 return filter->filtering();
80 return filter->filtering();
81+ case Filters::RoleOptions:
82+ return QVariant::fromValue(filter->options());
80 default:
83 default:
81 break;
84 break;
82 }
85 }
@@ -93,6 +96,7 @@ QHash<int, QByteArray> Filters::roleNames() const
93 roles[Filters::RoleVisible] = "visible";
96 roles[Filters::RoleVisible] = "visible";
94 roles[Filters::RoleCollapsed] = "collapsed";
97 roles[Filters::RoleCollapsed] = "collapsed";
95 roles[Filters::RoleFiltering] = "filtering";
98 roles[Filters::RoleFiltering] = "filtering";
99+ roles[Filters::RoleOptions] = "options";
96 return roles;
100 return roles;
97}
101}
98
102
plugins/Unity/filters.h+2−1
@@ -48,7 +48,8 @@ public:
48 RoleRendererName,
48 RoleRendererName,
49 RoleVisible,
49 RoleVisible,
50 RoleCollapsed,
50 RoleCollapsed,
51− RoleFiltering
51+ RoleFiltering,
52+ RoleOptions
52 };
53 };
53
54
54 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
55 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
plugins/Unity/genericoptionsmodel.cpp+18
@@ -76,6 +76,24 @@ int GenericOptionsModel::rowCount(const QModelIndex& /* parent */) const
76 return m_options.size ();
76 return m_options.size ();
77}
77}
78
78
79+void GenericOptionsModel::setActive(QVector<AbstractFilterOption *>::size_type idx, bool value)
80+{
81+ if (idx < m_options.size())
82+ {
83+ m_options[idx]->setActive(value);
84+ }
85+}
86+
87+void GenericOptionsModel::setActive(const QString &id, bool value)
88+{
89+ Q_FOREACH(auto opt, m_options) {
90+ if (opt->id() == id) {
91+ opt->setActive(value);
92+ break;
93+ }
94+ }
95+}
96+
79AbstractFilterOption* GenericOptionsModel::getRawOption(QVector<AbstractFilterOption *>::size_type idx) const
97AbstractFilterOption* GenericOptionsModel::getRawOption(QVector<AbstractFilterOption *>::size_type idx) const
80{
98{
81 if (idx < m_options.size())
99 if (idx < m_options.size())
plugins/Unity/genericoptionsmodel.h+3
@@ -49,6 +49,9 @@ public:
49 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
49 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
50 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
50 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
51
51
52+ Q_INVOKABLE void setActive(QVector<AbstractFilterOption *>::size_type idx, bool value);
53+ Q_INVOKABLE void setActive(const QString &id, bool value);
54+
52 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
55 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
53
56
54public Q_SLOTS:
57public Q_SLOTS:
plugins/Utils/qlimitproxymodelqml.cpp+90−4
@@ -25,6 +25,8 @@ QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
25 , m_limit(-1)
25 , m_limit(-1)
26 , m_sourceInserting(false)
26 , m_sourceInserting(false)
27 , m_sourceRemoving(false)
27 , m_sourceRemoving(false)
28+ , m_dataChangedBegin(-1)
29+ , m_dataChangedEnd(-1)
28{
30{
29 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
31 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
30 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
32 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
@@ -130,8 +132,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int
130 beginInsertRows(mapFromSource(parent), start, end);
132 beginInsertRows(mapFromSource(parent), start, end);
131 m_sourceInserting = true;
133 m_sourceInserting = true;
132 } else if (start < m_limit) {
134 } else if (start < m_limit) {
135+ const int nSourceAddedItems = end - start + 1;
136+ const int currentCount = QIdentityProxyModel::rowCount();
137+ if (currentCount + nSourceAddedItems <= m_limit) {
138+ // After Inserting items we will be under the limit
139+ // so just proceed with the insertion normally
133− beginInsertRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
140+ beginInsertRows(mapFromSource(parent), start, end);
141+ m_sourceInserting = true;
142+ } else if (currentCount >= m_limit) {
143+ // We are already over the limit so to our users we are not adding items, just
144+ // changing it's data, i.e we had something like
145+ // A B C D E
146+ // with a limit of 5
147+ // after inserting (let's say three 'F' at position 1) we will have
148+ // A F F F B
149+ // so we just need to signal a dataChanged from 1 to 4
150+ m_dataChangedBegin = start;
151+ m_dataChangedEnd = m_limit - 1;
152+ } else { // currentCount < m_limit && currentCount + nSourceAddedItems > m_limit
153+ // We have less items than the limit but after adding them we will be over
154+ // To our users this means we need to insert some items and change the
155+ // data of some others, i.e we had something like
156+ // A B C
157+ // with a limit of 5
158+ // after inserting (let's say three 'F' at position 1) we will have
159+ // A F F F B
160+ // so we need to signal an insetion from position 1 to 2, instead of from
161+ // position 1 to 3 and a after that a data changed from 3 to 4
162+ const int nItemsToInsert = m_limit - currentCount;
163+ beginInsertRows(mapFromSource(parent), start, start + nItemsToInsert - 1);
134− m_sourceInserting = true;
164+ m_sourceInserting = true;
165+ m_dataChangedBegin = start + nItemsToInsert;
166+ m_dataChangedEnd = m_limit - 1;
167+ if (m_dataChangedBegin > m_dataChangedEnd) {
168+ // Just in case we were empty and insert 6 items with a limit of 5
169+ // We don't want to signal a dataChanged from 5 to 4
170+ m_dataChangedBegin = -1;
171+ m_dataChangedEnd = -1;
172+ }
173+ }
135 }
174 }
136}
175}
137
176
@@ -142,8 +181,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int s
142 beginRemoveRows(mapFromSource(parent), start, end);
181 beginRemoveRows(mapFromSource(parent), start, end);
143 m_sourceRemoving = true;
182 m_sourceRemoving = true;
144 } else if (start < m_limit) {
183 } else if (start < m_limit) {
184+ const int nSourceRemovedItems = end - start + 1;
185+ const int currentCount = QIdentityProxyModel::rowCount();
186+ if (currentCount <= m_limit) {
187+ // We are already under the limit so
188+ // so just proceed with the removal normally
145− beginRemoveRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
189+ beginRemoveRows(mapFromSource(parent), start, end);
190+ m_sourceRemoving = true;
191+ } else if (currentCount - nSourceRemovedItems >= m_limit) {
192+ // Even after removing items we will be at or over the limit
193+ // So to our users we are not removing anything, just changing the data
194+ // i.e. we had a internal model with
195+ // A B C D E F G H
196+ // and a limit of 5, our users just see
197+ // A B C D E
198+ // so if we remove 3 items starting at 1 we have to expose
199+ // A E F G H
200+ // that is, a dataChanged from 1 to 4
201+ m_dataChangedBegin = start;
202+ m_dataChangedEnd = m_limit - 1;
203+ } else { // currentCount > m_limit && currentCount - nSourceRemovedItems < m_limit
204+ // We have more items than the limit but after removing we will be below it
205+ // So to our users we both removing and changing the data
206+ // i.e. we had a internal model with
207+ // A B C D E F G
208+ // and a limit of 5, our users just see
209+ // A B C D E
210+ // so if we remove items from 1 to 3 we have to expose
211+ // A E F G
212+ // that is, a remove from 4 to 4 and a dataChanged from 1 to 3
213+ const int nItemsToRemove = m_limit - (currentCount - nSourceRemovedItems);
214+ beginRemoveRows(mapFromSource(parent), m_limit - nItemsToRemove, m_limit - 1);
146− m_sourceRemoving = true;
215+ m_sourceRemoving = true;
216+ m_dataChangedBegin = start;
217+ m_dataChangedEnd = m_limit - nItemsToRemove - 1;
218+ if (m_dataChangedBegin > m_dataChangedEnd) {
219+ m_dataChangedBegin = -1;
220+ m_dataChangedEnd = -1;
221+ }
222+ }
147 }
223 }
148}
224}
149
225
@@ -154,6 +230,11 @@ QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*st
154 endInsertRows();
230 endInsertRows();
155 m_sourceInserting = false;
231 m_sourceInserting = false;
156 }
232 }
233+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
234+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
235+ m_dataChangedBegin = -1;
236+ m_dataChangedEnd = -1;
237+ }
157}
238}
158
239
159void
240void
@@ -163,4 +244,9 @@ QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*sta
163 endRemoveRows();
244 endRemoveRows();
164 m_sourceRemoving = false;
245 m_sourceRemoving = false;
165 }
246 }
247+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
248+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
249+ m_dataChangedBegin = -1;
250+ m_dataChangedEnd = -1;
251+ }
166}
252}
plugins/Utils/qlimitproxymodelqml.h+2
@@ -55,6 +55,8 @@ private:
55 int m_limit;
55 int m_limit;
56 bool m_sourceInserting;
56 bool m_sourceInserting;
57 bool m_sourceRemoving;
57 bool m_sourceRemoving;
58+ int m_dataChangedBegin;
59+ int m_dataChangedEnd;
58};
60};
59
61
60#endif // QLIMITPROXYMODELQML_H
62#endif // QLIMITPROXYMODELQML_H
src/Panel/Indicators/servicefiles/CMakeLists.txt−1
@@ -1,6 +1,5 @@
1file(GLOB INDICATOR_SERVICE_FILES
1file(GLOB INDICATOR_SERVICE_FILES
2 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
2 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
3− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.settings.network
4)
3)
5
4
6# copy indicators files into build directory for shadow builds
5# copy indicators files into build directory for shadow builds
src/Panel/Indicators/servicefiles/com.canonical.indicator.messages+1−1
@@ -1,5 +1,5 @@
1[Indicator Service]
1[Indicator Service]
2−Name=indicator-messaging
2+Name=indicator-messages
3ObjectPath=/com/canonical/indicator/messages
3ObjectPath=/com/canonical/indicator/messages
4Position=50
4Position=50
5
5
src/Panel/Indicators/servicefiles/com.canonical.settings.networkdeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=chewie-network
3−ObjectPath=/com/canonical/settings/network
4−Position=69
5−
6−[phone]
7−ObjectPath=/com/canonical/settings/network/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/settings/network/desktop
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+2−2
@@ -31,8 +31,8 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
31 m_pinned(false),
31 m_pinned(false),
32 m_running(false),
32 m_running(false),
33 m_recent(false),
33 m_recent(false),
34− m_progress(8),
34+ m_progress(-1),
35− m_count(8),
35+ m_count(0),
36 m_quickList(new MockQuickListModel(this))
36 m_quickList(new MockQuickListModel(this))
37{
37{
38
38
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+31−1
@@ -23,22 +23,32 @@
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
24{
24{
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26+ item->setProgress(0);
26 m_list.append(item);
27 m_list.append(item);
27 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
28 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29+ item->setProgress(10);
28 m_list.append(item);
30 m_list.append(item);
29 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
31 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ item->setProgress(50);
30 m_list.append(item);
33 m_list.append(item);
31 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
34 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
35+ item->setProgress(150);
32 m_list.append(item);
36 m_list.append(item);
33 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
37 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ item->setCount(1);
34 m_list.append(item);
39 m_list.append(item);
35 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
40 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
41+ item->setCount(12);
36 m_list.append(item);
42 m_list.append(item);
37 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
43 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
44+ item->setCount(123);
38 m_list.append(item);
45 m_list.append(item);
39 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
46 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
47+ item->setCount(1234567890);
40 m_list.append(item);
48 m_list.append(item);
41 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
49 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50+ item->setProgress(50);
51+ item->setCount(5);
42 m_list.append(item);
52 m_list.append(item);
43 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
53 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
44 m_list.append(item);
54 m_list.append(item);
@@ -91,9 +101,29 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
91
101
92void MockLauncherModel::move(int oldIndex, int newIndex)
102void MockLauncherModel::move(int oldIndex, int newIndex)
93{
103{
104+ // Make sure its not moved outside the lists
105+ if (newIndex < 0) {
106+ newIndex = 0;
107+ }
108+ if (newIndex >= m_list.count()) {
109+ newIndex = m_list.count()-1;
110+ }
111+
112+ // Nothing to do?
113+ if (oldIndex == newIndex) {
114+ return;
115+ }
116+
117+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
118+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
119+ // adjust the model's index by +1 in case we're moving upwards
120+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
121+
94− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
122+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
95 m_list.move(oldIndex, newIndex);
123 m_list.move(oldIndex, newIndex);
96 endMoveRows();
124 endMoveRows();
125+
126+ pin(m_list.at(newIndex)->appId());
97}
127}
98
128
99void MockLauncherModel::pin(const QString &appId, int index)
129void MockLauncherModel::pin(const QString &appId, int index)
tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8
@@ -58,10 +58,17 @@ public:
58 m_roles = roles;
58 m_roles = roles;
59 }
59 }
60
60
61+ void clear()
62+ {
63+ beginRemoveRows(QModelIndex(), 0, m_list.count() - 1);
64+ m_list.clear();
65+ endRemoveRows();
66+ }
67+
61 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
68 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
62 beginInsertRows(parent, row, row+count-1);
69 beginInsertRows(parent, row, row+count-1);
63 for (int i=0; i<count; i++) {
70 for (int i=0; i<count; i++) {
64− m_list.insert(i+row, "test"+i);
71+ m_list.insert(i+row, QString::number(i));
65 }
72 }
66 endInsertRows();
73 endInsertRows();
67 return true;
74 return true;
@@ -88,6 +95,60 @@ private:
88 QHash<int, QByteArray> m_roles;
95 QHash<int, QByteArray> m_roles;
89};
96};
90
97
98+class MirrorModel : public QObject
99+{
100+ Q_OBJECT
101+
102+public:
103+ MirrorModel(QAbstractItemModel *model) : m_mirror(model)
104+ {
105+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
106+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)));
107+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
108+ }
109+
110+ void check()
111+ {
112+ QCOMPARE(m_list.count(), m_mirror->rowCount());
113+ for (int i = 0; i < m_list.count(); ++i) {
114+ QCOMPARE(m_list[i], m_mirror->data(m_mirror->index(i, 0)).toString());
115+ }
116+ }
117+
118+private Q_SLOTS:
119+ void rowsInserted(const QModelIndex &parent, int start, int end)
120+ {
121+ QVERIFY(!parent.isValid());
122+ for (int i = start; i <= end; ++i) {
123+ m_list.insert(i, m_mirror->data(m_mirror->index(i, 0)).toString());
124+ }
125+ }
126+
127+ void rowsRemoved(const QModelIndex &parent, int start, int end)
128+ {
129+ QVERIFY(!parent.isValid());
130+ for (int i = end; i >= start; --i) {
131+ m_list.removeAt(i);
132+ }
133+ }
134+
135+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
136+ {
137+ QVERIFY(topLeft.isValid());
138+ QVERIFY(bottomRight.isValid());
139+ QVERIFY(!topLeft.parent().isValid());
140+ QVERIFY(!bottomRight.parent().isValid());
141+ for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
142+ m_list[i] = m_mirror->data(m_mirror->index(i, 0)).toString();
143+ }
144+ }
145+
146+
147+private:
148+ QStringList m_list;
149+ QAbstractItemModel *m_mirror;
150+};
151+
91class QLimitProxyModelTest : public QObject
152class QLimitProxyModelTest : public QObject
92{
153{
93 Q_OBJECT
154 Q_OBJECT
@@ -401,15 +462,10 @@ private Q_SLOTS:
401 QCOMPARE(spyOnCountChanged.count(), 0);
462 QCOMPARE(spyOnCountChanged.count(), 0);
402
463
403 model.removeRows(2, 2);
464 model.removeRows(2, 2);
404− QCOMPARE(spyOnRowsRemoved.count(), 1);
465+ QCOMPARE(spyOnRowsRemoved.count(), 0);
405 QCOMPARE(spyOnRowsInserted.count(), 0);
466 QCOMPARE(spyOnRowsInserted.count(), 0);
406− QCOMPARE(spyOnCountChanged.count(), 1);
467+ QCOMPARE(spyOnCountChanged.count(), 0);
407− arguments = spyOnRowsRemoved.takeFirst();
408− QCOMPARE(arguments.at(1).toInt(), 2);
409− QCOMPARE(arguments.at(2).toInt(), 3);
410 QCOMPARE(proxy.rowCount(), 7);
468 QCOMPARE(proxy.rowCount(), 7);
411− spyOnRowsInserted.clear();
412− spyOnCountChanged.clear();
413
469
414 model.removeRows(0, 7);
470 model.removeRows(0, 7);
415 QCOMPARE(spyOnRowsRemoved.count(), 1);
471 QCOMPARE(spyOnRowsRemoved.count(), 1);
@@ -479,6 +535,75 @@ private Q_SLOTS:
479 QCOMPARE(proxy.rowCount(), 5);
535 QCOMPARE(proxy.rowCount(), 5);
480 QCOMPARE(spyOnCountChanged.count(), 1);
536 QCOMPARE(spyOnCountChanged.count(), 1);
481 }
537 }
538+
539+ void testMirrorModel() {
540+ QLimitProxyModelQML proxy;
541+ MockListModel model;
542+ MirrorModel mirror(&proxy);
543+
544+ proxy.setModel(&model);
545+ proxy.setLimit(5);
546+
547+ // Checks what happens when the model already has
548+ // more items than the limit and we add stuff to
549+ // the front
550+ model.insertRows(0, 7);
551+ mirror.check();
552+ model.insertRows(1, 3);
553+ mirror.check();
554+ model.clear();
555+ mirror.check();
556+
557+ // Checks what happens when the model does
558+ // not has more items than limit but adding
559+ // stuff to its front makes it go past the limit
560+ model.insertRows(0, 3);
561+ mirror.check();
562+ model.insertRows(1, 3);
563+ mirror.check();
564+ model.clear();
565+ mirror.check();
566+
567+ // Checks what happens when the model does
568+ // not has more items than limit and adding
569+ // stuff to its front makes it not go past the limit
570+ model.insertRows(0, 1);
571+ mirror.check();
572+ model.insertRows(1, 3);
573+ mirror.check();
574+ model.clear();
575+ mirror.check();
576+
577+ // Checks what happens when the model already has
578+ // more items than the limit and we remove stuff from
579+ // the front but it still has more than the limit
580+ model.insertRows(0, 10);
581+ mirror.check();
582+ model.removeRows(1, 3);
583+ mirror.check();
584+ model.clear();
585+ mirror.check();
586+
587+ // Checks what happens when the model already has
588+ // more items than the limit and we remove stuff from
589+ // the front but it has less than the limit
590+ model.insertRows(0, 6);
591+ mirror.check();
592+ model.removeRows(1, 3);
593+ mirror.check();
594+ model.clear();
595+ mirror.check();
596+
597+ // Checks what happens when the model has
598+ // less items than the limit and we remove stuff from
599+ // the front
600+ model.insertRows(0, 4);
601+ mirror.check();
602+ model.removeRows(1, 3);
603+ mirror.check();
604+ model.clear();
605+ mirror.check();
606+ }
482};
607};
483
608
484QTEST_GUILESS_MAIN(QLimitProxyModelTest)
609QTEST_GUILESS_MAIN(QLimitProxyModelTest)
tests/qmltests/Greeter/tst_Clock.qml+1−1
@@ -70,7 +70,7 @@ Rectangle {
70 compare(clock.__timerRunning, true, "Timer should be running")
70 compare(clock.__timerRunning, true, "Timer should be running")
71 wait(0) // spin event loop to trigger the timer
71 wait(0) // spin event loop to trigger the timer
72 verify(dateLabel.text !== dateString)
72 verify(dateLabel.text !== dateString)
73− if (timeLabel.text == "11:13") wait(60000) // next test will fail at 11:13, wait 1 minute
73+ if (timeLabel.text.indexOf("11:13") != -1) wait(60000) // next test will fail at 11:13, wait 1 minute
74 verify(timeLabel.text !== timeString)
74 verify(timeLabel.text !== timeString)
75 }
75 }
76
76
tests/qmltests/Launcher/tst_Launcher.qml+16
@@ -182,6 +182,22 @@ Item {
182 revealer.waitUntilLauncherDisappears();
182 revealer.waitUntilLauncherDisappears();
183 launcher.available = true;
183 launcher.available = true;
184 }
184 }
185+
186+ function test_countEmblems() {
187+ var launcherListView = findChild(launcher, "launcherListView");
188+ for (var i = 0; i < launcherListView.count; ++i) {
189+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
190+ compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).count > 0)
191+ }
192+ }
193+
194+ function test_progressOverlays() {
195+ var launcherListView = findChild(launcher, "launcherListView");
196+ for (var i = 0; i < launcherListView.count; ++i) {
197+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
198+ compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199+ }
200+ }
185 }
201 }
186
202
187 UT.UnityTestCase {
203 UT.UnityTestCase {