2 * Copyright 2013 Canonical Ltd.
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.
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.
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/>.
17 * Nick Dedekind <nick.dedekind@canonical.com>
21 import Ubuntu.Settings.Menus 0.1 as Menus
23 import Utils 0.1 as Utils
24 import Ubuntu.Components.ListItems 0.1 as ListItems
25 import Ubuntu.Components 1.1
30 property var rootModel: null
31 property var menuModel: null
35 "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
36 "unity.widgets.systemsettings.tablet.switch" : switchMenu,
38 "com.canonical.indicator.button" : buttonMenu,
39 "com.canonical.indicator.div" : separatorMenu,
40 "com.canonical.indicator.section" : sectionMenu,
41 "com.canonical.indicator.progress" : progressMenu,
42 "com.canonical.indicator.slider" : sliderMenu,
43 "com.canonical.indicator.switch" : switchMenu,
44 "com.canonical.indicator.alarm" : alarmMenu,
45 "com.canonical.indicator.appointment" : appointmentMenu,
46 "com.canonical.indicator.transfer" : transferMenu,
47 "com.canonical.indicator.button-section" : buttonSectionMenu,
48 "com.canonical.indicator.link" : linkMenu,
50 "com.canonical.indicator.messages.messageitem" : messageItem,
51 "com.canonical.indicator.messages.sourceitem" : groupedMessage,
53 "com.canonical.unity.slider" : sliderMenu,
54 "com.canonical.unity.switch" : switchMenu,
56 "com.canonical.unity.media-player" : mediaPayerMenu,
57 "com.canonical.unity.playback-item" : playbackItemMenu,
59 "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
60 "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
61 "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
63 "indicator-messages" : {
64 "com.canonical.indicator.button" : messagesButtonMenu
68 function getExtendedProperty(object, propertyName, defaultValue) {
69 if (object && object.hasOwnProperty(propertyName)) {
70 return object[propertyName];
79 objectName: "separatorMenu"
87 objectName: "sliderMenu"
88 property QtObject menuData: null
89 property var menuModel: menuFactory.menuModel
90 property int menuIndex: -1
91 property var extendedData: menuData && menuData.ext || undefined
92 property var serverValue: getExtendedProperty(menuData, "actionState", undefined)
94 text: menuData && menuData.label || ""
95 iconSource: menuData && menuData.icon || ""
96 minIcon: getExtendedProperty(extendedData, "minIcon", "")
97 maxIcon: getExtendedProperty(extendedData, "maxIcon", "")
99 minimumValue: getExtendedProperty(extendedData, "minValue", 0.0)
101 var maximum = getExtendedProperty(extendedData, "maxValue", 1.0);
102 if (maximum <= minimumValue) {
103 return minimumValue + 1;
107 enabled: menuData && menuData.sensitive || false
108 highlightWhenPressed: false
110 onMenuModelChanged: {
113 onMenuIndexChanged: {
116 onServerValueChanged: {
117 // value can be changed by slider, so a binding won't work.
118 if (serverValue !== undefined) {
123 menuModel.changeState(menuIndex, value);
126 function loadAttributes() {
127 if (!menuModel || menuIndex == -1) return;
128 menuModel.loadExtendedAttributes(menuIndex, {'min-value': 'double',
129 'max-value': 'double',
131 'max-icon': 'icon'});
140 objectName: "buttonMenu"
141 property QtObject menuData: null
142 property var menuModel: menuFactory.menuModel
143 property int menuIndex: -1
145 buttonText: menuData && menuData.label || ""
146 enabled: menuData && menuData.sensitive || false
147 highlightWhenPressed: false
150 menuModel.activate(menuIndex);
156 id: messagesButtonMenu;
159 objectName: "messagesButtonMenu"
160 property QtObject menuData: null
161 property var menuModel: menuFactory.menuModel
162 property int menuIndex: -1
164 implicitHeight: units.gu(5)
165 enabled: menuData && menuData.sensitive || false
169 text: menuData && menuData.label || ""
170 anchors.centerIn: parent
176 fill: buttonMenuLabel
177 margins: units.gu(-1)
179 onClicked: menuModel.activate(menuIndex);
188 objectName: "sectionMenu"
189 property QtObject menuData: null
190 property var menuIndex: undefined
192 text: menuData && menuData.label || ""
200 Menus.ProgressValueMenu {
201 objectName: "progressMenu"
202 property QtObject menuData: null
203 property int menuIndex: -1
205 text: menuData && menuData.label || ""
206 iconSource: menuData && menuData.icon || ""
207 value : menuData && menuData.actionState || 0.0
208 enabled: menuData && menuData.sensitive || false
209 highlightWhenPressed: false
217 objectName: "standardMenu"
218 property QtObject menuData: null
219 property int menuIndex: -1
221 text: menuData && menuData.label || ""
222 iconSource: menuData && menuData.icon || ""
223 enabled: menuData && menuData.sensitive || false
224 highlightWhenPressed: false
227 menuModel.activate(menuIndex);
230 // FIXME : At the moment, the indicators aren't using
231 // com.canonical.indicators.link for settings menu. Need to fudge it.
232 property bool settingsMenu: menuData && menuData.action.indexOf("settings") > -1 || false
233 backColor: settingsMenu ? Qt.rgba(1,1,1,0.07) : "transparent"
234 component: settingsMenu ? buttonForSettings : undefined
236 id: buttonForSettings
241 color: Theme.palette.selected.backgroundText
251 objectName: "linkMenu"
252 property QtObject menuData: null
253 property int menuIndex: -1
255 text: menuData && menuData.label || ""
256 iconSource: menuData && menuData.icon || ""
257 enabled: menuData && menuData.sensitive || false
258 highlightWhenPressed: false
261 menuModel.activate(menuIndex);
264 backColor: Qt.rgba(1,1,1,0.07)
266 component: menuData.icon ? icon : undefined
270 source: menuData.icon
273 color: Theme.palette.selected.backgroundText
282 Menus.CheckableMenu {
283 objectName: "checkableMenu"
284 property QtObject menuData: null
285 property int menuIndex: -1
286 property bool serverChecked: menuData && menuData.isToggled || false
288 text: menuData && menuData.label || ""
289 enabled: menuData && menuData.sensitive || false
290 checked: serverChecked
291 highlightWhenPressed: false
293 onServerCheckedChanged: updateFromServer()
295 menuModel.activate(menuIndex);
296 resyncTimer.restart();
299 // value can be changed by menu, so a binding won't work.
300 function updateFromServer() {
302 if (checked != serverChecked) {
303 checked = serverChecked;
307 // Server value is not guaranteed to change to what we expect from an activation.
308 // In this case, we need to re-assert that we are presenting the UI with the set backend value.
312 onTriggered: updateFromServer()
321 objectName: "switchMenu"
322 property QtObject menuData: null
323 property int menuIndex: -1
324 property bool serverChecked: menuData && menuData.isToggled || false
326 text: menuData && menuData.label || ""
327 iconSource: menuData && menuData.icon || ""
328 enabled: menuData && menuData.sensitive || false
329 checked: serverChecked
330 highlightWhenPressed: false
332 onServerCheckedChanged: updateFromServer()
334 menuModel.activate(menuIndex);
335 resyncTimer.restart();
338 // value can be changed by menu, so a binding won't work.
339 function updateFromServer() {
341 if (checked != serverChecked) {
342 checked = serverChecked;
346 // Server value is not guaranteed to change to what we expect from an activation.
347 // In this case, we need to re-assert that we are presenting the UI with the set backend value.
351 onTriggered: updateFromServer()
360 objectName: "alarmMenu"
361 property QtObject menuData: null
362 property var menuModel: menuFactory.menuModel
363 property int menuIndex: -1
364 property var extendedData: menuData && menuData.ext || undefined
365 // TODO - bug #1260728
366 property var timeFormatter: Utils.GDateTimeFormatter {
367 time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
368 format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
371 text: menuData && menuData.label || ""
372 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
373 time: timeFormatter.timeString
374 enabled: menuData && menuData.sensitive || false
375 highlightWhenPressed: false
377 onMenuModelChanged: {
380 onMenuIndexChanged: {
384 menuModel.activate(menuIndex);
387 function loadAttributes() {
388 if (!menuModel || menuIndex == -1) return;
389 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
390 'x-canonical-time-format': 'string'});
399 objectName: "appointmentMenu"
400 property QtObject menuData: null
401 property var menuModel: menuFactory.menuModel
402 property int menuIndex: -1
403 property var extendedData: menuData && menuData.ext || undefined
404 // TODO - bug #1260728
405 property var timeFormatter: Utils.GDateTimeFormatter {
406 time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
407 format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
410 text: menuData && menuData.label || ""
411 iconSource: menuData && menuData.icon || "image://theme/calendar"
412 time: timeFormatter.timeString
413 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
414 enabled: menuData && menuData.sensitive || false
415 highlightWhenPressed: false
417 onMenuModelChanged: {
420 onMenuIndexChanged: {
424 menuModel.activate(menuIndex);
427 function loadAttributes() {
428 if (!menuModel || menuIndex == -1) return;
429 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
430 'x-canonical-time': 'int64',
431 'x-canonical-time-format': 'string'});
440 objectName: "wifiSection"
441 property QtObject menuData: null
442 property var menuModel: menuFactory.menuModel
443 property int menuIndex: -1
444 property var extendedData: menuData && menuData.ext || undefined
446 text: menuData && menuData.label || ""
447 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
449 onMenuModelChanged: {
452 onMenuIndexChanged: {
456 function loadAttributes() {
457 if (!menuModel || menuIndex == -1) return;
458 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
466 Menus.AccessPointMenu {
467 objectName: "accessPoint"
468 property QtObject menuData: null
469 property var menuModel: menuFactory.menuModel
470 property int menuIndex: -1
471 property var extendedData: menuData && menuData.ext || undefined
472 property bool serverChecked: menuData && menuData.isToggled || false
474 property var strengthAction: UnityMenuAction {
477 name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
480 text: menuData && menuData.label || ""
481 enabled: menuData && menuData.sensitive || false
482 active: serverChecked
483 secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
484 adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
485 signalStrength: strengthAction.valid ? strengthAction.state : 0
486 highlightWhenPressed: false
488 onServerCheckedChanged: {
489 // value can be changed by menu, so a binding won't work.
490 active = serverChecked;
492 onMenuModelChanged: {
495 onMenuIndexChanged: {
499 menuModel.activate(menuIndex);
502 function loadAttributes() {
503 if (!menuModel || menuIndex == -1) return;
504 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
505 'x-canonical-wifi-ap-is-secure': 'bool',
506 'x-canonical-wifi-ap-strength-action': 'string'});
514 objectName: "modemInfoItem"
515 property QtObject menuData: null
516 property var menuModel: menuFactory.menuModel
517 property int menuIndex: -1
518 property var extendedData: menuData && menuData.ext || undefined
519 highlightWhenPressed: false
521 property var statusLabelAction: UnityMenuAction {
524 name: getExtendedProperty(extendedData, "xCanonicalModemStatusLabelAction", "")
526 statusText: statusLabelAction.valid ? statusLabelAction.state : ""
528 property var statusIconAction: UnityMenuAction {
531 name: getExtendedProperty(extendedData, "xCanonicalModemStatusIconAction", "")
533 statusIcon: statusIconAction.valid ? statusIconAction.state : ""
535 property var connectivityIconAction: UnityMenuAction {
538 name: getExtendedProperty(extendedData, "xCanonicalModemConnectivityIconAction", "")
540 connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
542 property var simIdentifierLabelAction: UnityMenuAction {
545 name: getExtendedProperty(extendedData, "xCanonicalModemSimIdentifierLabelAction", "")
547 simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
549 property var roamingAction: UnityMenuAction {
552 name: getExtendedProperty(extendedData, "xCanonicalModemRoamingAction", "")
554 roaming: roamingAction.valid ? roamingAction.state : false
556 property var unlockAction: UnityMenuAction {
559 name: getExtendedProperty(extendedData, "xCanonicalModemLockedAction", "")
562 unlockAction.activate();
564 locked: unlockAction.valid ? unlockAction.state : false
566 onMenuModelChanged: {
569 onMenuIndexChanged: {
573 function loadAttributes() {
574 if (!menuModel || menuIndex == -1) return;
575 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-modem-status-label-action': 'string',
576 'x-canonical-modem-status-icon-action': 'string',
577 'x-canonical-modem-connectivity-icon-action': 'string',
578 'x-canonical-modem-sim-identifier-label-action': 'string',
579 'x-canonical-modem-roaming-action': 'string',
580 'x-canonical-modem-locked-action': 'string'});
588 MessageMenuItemFactory {
589 objectName: "messageItem"
590 menuModel: menuFactory.menuModel
597 Menus.GroupedMessageMenu {
598 objectName: "groupedMessage"
599 property QtObject menuData: null
600 property var menuModel: menuFactory.menuModel
601 property int menuIndex: -1
602 property var extendedData: menuData && menuData.ext || undefined
604 text: menuData && menuData.label || ""
605 iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
606 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
607 enabled: menuData && menuData.sensitive || false
608 highlightWhenPressed: false
611 onMenuModelChanged: {
614 onMenuIndexChanged: {
618 menuModel.activate(menuIndex, true);
621 menuModel.activate(menuIndex, false);
624 function loadAttributes() {
625 if (!menuModel || menuIndex == -1) return;
626 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
634 Menus.MediaPlayerMenu {
635 objectName: "mediaPayerMenu"
636 property QtObject menuData: null
637 property var menuModel: menuFactory.menuModel
638 property int menuIndex: -1
639 property var actionState: menuData && menuData.actionState || undefined
640 property bool running: getExtendedProperty(actionState, "running", false)
642 playerIcon: menuData && menuData.icon || "image://theme/stock_music"
643 playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
645 albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
646 song: getExtendedProperty(actionState, "title", "")
647 artist: getExtendedProperty(actionState, "artist", "")
648 album: getExtendedProperty(actionState, "album", "")
649 showTrack: running && (state == "Playing" || state == "Paused")
650 state: getExtendedProperty(actionState, "state", "")
651 enabled: menuData && menuData.sensitive || false
652 highlightWhenPressed: false
656 model.activate(modelIndex);
662 id: playbackItemMenu;
664 Menus.PlaybackItemMenu {
665 objectName: "playbackItemMenu"
666 property QtObject menuData: null
667 property var menuModel: menuFactory.menuModel
668 property int menuIndex: -1
669 property var extendedData: menuData && menuData.ext || undefined
671 property var playAction: UnityMenuAction {
674 name: getExtendedProperty(extendedData, "xCanonicalPlayAction", "")
676 property var nextAction: UnityMenuAction {
679 name: getExtendedProperty(extendedData, "xCanonicalNextAction", "")
681 property var previousAction: UnityMenuAction {
684 name: getExtendedProperty(extendedData, "xCanonicalPreviousAction", "")
687 playing: playAction.state === "Playing"
688 canPlay: playAction.valid
689 canGoNext: nextAction.valid
690 canGoPrevious: previousAction.valid
691 enabled: menuData && menuData.sensitive || false
692 highlightWhenPressed: false
695 playAction.activate();
698 nextAction.activate();
701 previousAction.activate();
703 onMenuModelChanged: {
706 onMenuIndexChanged: {
710 function loadAttributes() {
711 if (!menuModel || menuIndex == -1) return;
712 menuModel.loadExtendedAttributes(modelIndex, {'x-canonical-play-action': 'string',
713 'x-canonical-next-action': 'string',
714 'x-canonical-previous-action': 'string'});
723 objectName: "transferMenu"
725 property QtObject menuData: null
726 property var menuModel: menuFactory.menuModel
727 property int menuIndex: -1
728 property var extendedData: menuData && menuData.ext || undefined
729 property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
731 text: menuData && menuData.label || ""
732 iconSource: menuData && menuData.icon || "image://theme/transfer-none"
734 enabled: menuData && menuData.sensitive || false
735 highlightWhenPressed: false
742 busName: rootModel.busName
743 objectPath: rootModel.actions["indicator"]
745 property var activateAction: action("activate-transfer")
746 property var cancelAction: action("cancel-transfer")
747 property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
749 Component.onCompleted: actionGroup.start()
752 property var transferState: {
753 if (actionGroup.transferStateAction === null) return undefined;
754 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
757 property var runningState : transferState !== undefined ? transferState["state"] : undefined
758 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
760 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
761 progress: transferState !== undefined ? transferState["percent"] : 0.0
763 // TODO - Should be in the SDK
764 property var timeRemaining: {
765 if (secondsLeft === undefined) return undefined;
768 var hours = Math.floor(secondsLeft / (60 * 60));
769 var minutes = Math.floor(secondsLeft / 60) % 60;
770 var seconds = secondsLeft % 60;
772 remaining += hours + (hours == 1 ? " hour" : " hours");
775 if (remaining != "") remaining += ", ";
776 remaining += minutes + (minutes == 1 ? " minute" : " minutes");
778 // don't include seconds if hours > 0
779 if (hours == 0 && minutes < 5 && seconds > 0) {
780 if (remaining != "") remaining += ", ";
781 remaining += seconds + (seconds == 1 ? " second" : " seconds");
784 remaining = "0 seconds";
785 return remaining + " remaining";
789 switch (runningState) {
790 case Menus.TransferState.Queued:
791 return i18n.tr("In queue…");
792 case Menus.TransferState.Hashing:
793 case Menus.TransferState.Processing:
794 case Menus.TransferState.Running:
795 return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
796 case Menus.TransferState.Paused:
797 return i18n.tr("Paused, tap to resume");
798 case Menus.TransferState.Canceled:
799 return i18n.tr("Canceled");
800 case Menus.TransferState.Finished:
801 return i18n.tr("Finished");
802 case Menus.TransferState.Error:
803 return i18n.tr("Failed, tap to retry");
808 onMenuModelChanged: {
811 onMenuIndexChanged: {
815 actionGroup.activateAction.activate(uid);
818 actionGroup.cancelAction.activate(uid);
821 function loadAttributes() {
822 if (!menuModel || menuIndex == -1) return;
823 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
829 id: buttonSectionMenu;
832 objectName: "buttonSectionMenu"
833 property QtObject menuData: null
834 property var menuModel: menuFactory.menuModel
835 property int menuIndex: -1
836 property var extendedData: menuData && menuData.ext || undefined
838 iconSource: menuData && menuData.icon || ""
839 enabled: menuData && menuData.sensitive || false
840 highlightWhenPressed: false
841 text: menuData && menuData.label || ""
842 foregroundColor: Theme.palette.normal.backgroundText
844 onMenuModelChanged: {
847 onMenuIndexChanged: {
850 function loadAttributes() {
851 if (!menuModel || menuIndex == -1) return;
852 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
855 component: Component {
857 objectName: "buttonSectionMenuControl"
858 text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
861 menuModel.activate(menuIndex);
868 function load(modelData, context) {
869 if (modelData.type !== undefined && modelData.type !== "") {
870 var component = undefined;
872 var contextComponents = _map[context];
873 if (contextComponents !== undefined) {
874 component = contextComponents[modelData.type];
877 if (component === undefined) {
878 component = _map["default"][modelData.type];
880 if (component !== undefined) {
883 console.debug("Don't know how to make " + modelData.type + " for " + context);
885 if (modelData.isCheck || modelData.isRadio) {
886 return checkableMenu;
888 if (modelData.isSeparator) {
889 return separatorMenu;