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: {
294 // value can be changed by menu, so a binding won't work.
295 checked = serverChecked;
298 menuModel.activate(menuIndex);
307 objectName: "switchMenu"
308 property QtObject menuData: null
309 property int menuIndex: -1
310 property bool serverChecked: menuData && menuData.isToggled || false
312 text: menuData && menuData.label || ""
313 iconSource: menuData && menuData.icon || ""
314 enabled: menuData && menuData.sensitive || false
315 checked: serverChecked
316 highlightWhenPressed: false
318 onServerCheckedChanged: {
319 // value can be changed by menu, so a binding won't work.
320 checked = serverChecked;
323 menuModel.activate(menuIndex);
332 objectName: "alarmMenu"
333 property QtObject menuData: null
334 property var menuModel: menuFactory.menuModel
335 property int menuIndex: -1
336 property var extendedData: menuData && menuData.ext || undefined
337 // TODO - bug #1260728
338 property var timeFormatter: Utils.GDateTimeFormatter {
339 time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
340 format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
343 text: menuData && menuData.label || ""
344 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
345 time: timeFormatter.timeString
346 enabled: menuData && menuData.sensitive || false
347 highlightWhenPressed: false
349 onMenuModelChanged: {
352 onMenuIndexChanged: {
356 menuModel.activate(menuIndex);
359 function loadAttributes() {
360 if (!menuModel || menuIndex == -1) return;
361 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
362 'x-canonical-time-format': 'string'});
371 objectName: "appointmentMenu"
372 property QtObject menuData: null
373 property var menuModel: menuFactory.menuModel
374 property int menuIndex: -1
375 property var extendedData: menuData && menuData.ext || undefined
376 // TODO - bug #1260728
377 property var timeFormatter: Utils.GDateTimeFormatter {
378 time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
379 format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
382 text: menuData && menuData.label || ""
383 iconSource: menuData && menuData.icon || "image://theme/calendar"
384 time: timeFormatter.timeString
385 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
386 enabled: menuData && menuData.sensitive || false
387 highlightWhenPressed: false
389 onMenuModelChanged: {
392 onMenuIndexChanged: {
396 menuModel.activate(menuIndex);
399 function loadAttributes() {
400 if (!menuModel || menuIndex == -1) return;
401 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
402 'x-canonical-time': 'int64',
403 'x-canonical-time-format': 'string'});
412 objectName: "wifiSection"
413 property QtObject menuData: null
414 property var menuModel: menuFactory.menuModel
415 property int menuIndex: -1
416 property var extendedData: menuData && menuData.ext || undefined
418 text: menuData && menuData.label || ""
419 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
421 onMenuModelChanged: {
424 onMenuIndexChanged: {
428 function loadAttributes() {
429 if (!menuModel || menuIndex == -1) return;
430 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
438 Menus.AccessPointMenu {
439 objectName: "accessPoint"
440 property QtObject menuData: null
441 property var menuModel: menuFactory.menuModel
442 property int menuIndex: -1
443 property var extendedData: menuData && menuData.ext || undefined
444 property bool serverChecked: menuData && menuData.isToggled || false
446 property var strengthAction: UnityMenuAction {
449 name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
452 text: menuData && menuData.label || ""
453 enabled: menuData && menuData.sensitive || false
454 active: serverChecked
455 secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
456 adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
457 signalStrength: strengthAction.valid ? strengthAction.state : 0
458 highlightWhenPressed: false
460 onServerCheckedChanged: {
461 // value can be changed by menu, so a binding won't work.
462 active = serverChecked;
464 onMenuModelChanged: {
467 onMenuIndexChanged: {
471 menuModel.activate(menuIndex);
474 function loadAttributes() {
475 if (!menuModel || menuIndex == -1) return;
476 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
477 'x-canonical-wifi-ap-is-secure': 'bool',
478 'x-canonical-wifi-ap-strength-action': 'string'});
486 objectName: "modemInfoItem"
487 property QtObject menuData: null
488 property var menuModel: menuFactory.menuModel
489 property int menuIndex: -1
490 property var extendedData: menuData && menuData.ext || undefined
491 highlightWhenPressed: false
493 property var statusLabelAction: UnityMenuAction {
496 name: getExtendedProperty(extendedData, "xCanonicalModemStatusLabelAction", "")
498 statusText: statusLabelAction.valid ? statusLabelAction.state : ""
500 property var statusIconAction: UnityMenuAction {
503 name: getExtendedProperty(extendedData, "xCanonicalModemStatusIconAction", "")
505 statusIcon: statusIconAction.valid ? statusIconAction.state : ""
507 property var connectivityIconAction: UnityMenuAction {
510 name: getExtendedProperty(extendedData, "xCanonicalModemConnectivityIconAction", "")
512 connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
514 property var simIdentifierLabelAction: UnityMenuAction {
517 name: getExtendedProperty(extendedData, "xCanonicalModemSimIdentifierLabelAction", "")
519 simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
521 property var roamingAction: UnityMenuAction {
524 name: getExtendedProperty(extendedData, "xCanonicalModemRoamingAction", "")
526 roaming: roamingAction.valid ? roamingAction.state : false
528 property var unlockAction: UnityMenuAction {
531 name: getExtendedProperty(extendedData, "xCanonicalModemLockedAction", "")
534 unlockAction.activate();
536 locked: unlockAction.valid ? unlockAction.state : false
538 onMenuModelChanged: {
541 onMenuIndexChanged: {
545 function loadAttributes() {
546 if (!menuModel || menuIndex == -1) return;
547 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-modem-status-label-action': 'string',
548 'x-canonical-modem-status-icon-action': 'string',
549 'x-canonical-modem-connectivity-icon-action': 'string',
550 'x-canonical-modem-sim-identifier-label-action': 'string',
551 'x-canonical-modem-roaming-action': 'string',
552 'x-canonical-modem-locked-action': 'string'});
560 MessageMenuItemFactory {
561 objectName: "messageItem"
562 menuModel: menuFactory.menuModel
569 Menus.GroupedMessageMenu {
570 objectName: "groupedMessage"
571 property QtObject menuData: null
572 property var menuModel: menuFactory.menuModel
573 property int menuIndex: -1
574 property var extendedData: menuData && menuData.ext || undefined
576 text: menuData && menuData.label || ""
577 iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
578 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
579 enabled: menuData && menuData.sensitive || false
580 highlightWhenPressed: false
583 onMenuModelChanged: {
586 onMenuIndexChanged: {
590 menuModel.activate(menuIndex, true);
593 menuModel.activate(menuIndex, false);
596 function loadAttributes() {
597 if (!menuModel || menuIndex == -1) return;
598 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
606 Menus.MediaPlayerMenu {
607 objectName: "mediaPayerMenu"
608 property QtObject menuData: null
609 property var menuModel: menuFactory.menuModel
610 property int menuIndex: -1
611 property var actionState: menuData && menuData.actionState || undefined
612 property bool running: getExtendedProperty(actionState, "running", false)
614 playerIcon: menuData && menuData.icon || "image://theme/stock_music"
615 playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
617 albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
618 song: getExtendedProperty(actionState, "title", "")
619 artist: getExtendedProperty(actionState, "artist", "")
620 album: getExtendedProperty(actionState, "album", "")
621 showTrack: running && (state == "Playing" || state == "Paused")
622 state: getExtendedProperty(actionState, "state", "")
623 enabled: menuData && menuData.sensitive || false
624 highlightWhenPressed: false
628 model.activate(modelIndex);
634 id: playbackItemMenu;
636 Menus.PlaybackItemMenu {
637 objectName: "playbackItemMenu"
638 property QtObject menuData: null
639 property var menuModel: menuFactory.menuModel
640 property int menuIndex: -1
641 property var extendedData: menuData && menuData.ext || undefined
643 property var playAction: UnityMenuAction {
646 name: getExtendedProperty(extendedData, "xCanonicalPlayAction", "")
648 property var nextAction: UnityMenuAction {
651 name: getExtendedProperty(extendedData, "xCanonicalNextAction", "")
653 property var previousAction: UnityMenuAction {
656 name: getExtendedProperty(extendedData, "xCanonicalPreviousAction", "")
659 playing: playAction.state === "Playing"
660 canPlay: playAction.valid
661 canGoNext: nextAction.valid
662 canGoPrevious: previousAction.valid
663 enabled: menuData && menuData.sensitive || false
664 highlightWhenPressed: false
667 playAction.activate();
670 nextAction.activate();
673 previousAction.activate();
675 onMenuModelChanged: {
678 onMenuIndexChanged: {
682 function loadAttributes() {
683 if (!menuModel || menuIndex == -1) return;
684 menuModel.loadExtendedAttributes(modelIndex, {'x-canonical-play-action': 'string',
685 'x-canonical-next-action': 'string',
686 'x-canonical-previous-action': 'string'});
695 objectName: "transferMenu"
697 property QtObject menuData: null
698 property var menuModel: menuFactory.menuModel
699 property int menuIndex: -1
700 property var extendedData: menuData && menuData.ext || undefined
701 property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
703 text: menuData && menuData.label || ""
704 iconSource: menuData && menuData.icon || "image://theme/transfer-none"
706 enabled: menuData && menuData.sensitive || false
707 highlightWhenPressed: false
714 busName: rootModel.busName
715 objectPath: rootModel.actions["indicator"]
717 property var activateAction: action("activate-transfer")
718 property var cancelAction: action("cancel-transfer")
719 property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
721 Component.onCompleted: actionGroup.start()
724 property var transferState: {
725 if (actionGroup.transferStateAction === null) return undefined;
726 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
729 property var runningState : transferState !== undefined ? transferState["state"] : undefined
730 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
732 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
733 progress: transferState !== undefined ? transferState["percent"] : 0.0
735 // TODO - Should be in the SDK
736 property var timeRemaining: {
737 if (secondsLeft === undefined) return undefined;
740 var hours = Math.floor(secondsLeft / (60 * 60));
741 var minutes = Math.floor(secondsLeft / 60) % 60;
742 var seconds = secondsLeft % 60;
744 remaining += hours + (hours == 1 ? " hour" : " hours");
747 if (remaining != "") remaining += ", ";
748 remaining += minutes + (minutes == 1 ? " minute" : " minutes");
750 // don't include seconds if hours > 0
751 if (hours == 0 && minutes < 5 && seconds > 0) {
752 if (remaining != "") remaining += ", ";
753 remaining += seconds + (seconds == 1 ? " second" : " seconds");
756 remaining = "0 seconds";
757 return remaining + " remaining";
761 switch (runningState) {
762 case Menus.TransferState.Queued:
763 return i18n.tr("In queue…");
764 case Menus.TransferState.Hashing:
765 case Menus.TransferState.Processing:
766 case Menus.TransferState.Running:
767 return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
768 case Menus.TransferState.Paused:
769 return i18n.tr("Paused, tap to resume");
770 case Menus.TransferState.Canceled:
771 return i18n.tr("Canceled");
772 case Menus.TransferState.Finished:
773 return i18n.tr("Finished");
774 case Menus.TransferState.Error:
775 return i18n.tr("Failed, tap to retry");
780 onMenuModelChanged: {
783 onMenuIndexChanged: {
787 actionGroup.activateAction.activate(uid);
790 actionGroup.cancelAction.activate(uid);
793 function loadAttributes() {
794 if (!menuModel || menuIndex == -1) return;
795 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
801 id: buttonSectionMenu;
804 objectName: "buttonSectionMenu"
805 property QtObject menuData: null
806 property var menuModel: menuFactory.menuModel
807 property int menuIndex: -1
808 property var extendedData: menuData && menuData.ext || undefined
810 iconSource: menuData && menuData.icon || ""
811 enabled: menuData && menuData.sensitive || false
812 highlightWhenPressed: false
813 text: menuData && menuData.label || ""
814 foregroundColor: Theme.palette.normal.backgroundText
816 onMenuModelChanged: {
819 onMenuIndexChanged: {
822 function loadAttributes() {
823 if (!menuModel || menuIndex == -1) return;
824 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
827 component: Component {
829 objectName: "buttonSectionMenuControl"
830 text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
833 menuModel.activate(menuIndex);
840 function load(modelData, context) {
841 if (modelData.type !== undefined && modelData.type !== "") {
842 var component = undefined;
844 var contextComponents = _map[context];
845 if (contextComponents !== undefined) {
846 component = contextComponents[modelData.type];
849 if (component === undefined) {
850 component = _map["default"][modelData.type];
852 if (component !== undefined) {
855 console.debug("Don't know how to make " + modelData.type + " for " + context);
857 if (modelData.isCheck || modelData.isRadio) {
858 return checkableMenu;
860 if (modelData.isSeparator) {
861 return separatorMenu;