2 * Copyright 2013-2016 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/>.
18import QtQuick.Window 2.2
19import Lomiri.Settings.Menus 0.1 as Menus
20import Lomiri.Settings.Components 0.1
22import Utils 0.1 as Utils
23import Lomiri.Components.ListItems 1.3 as ListItems
24import Lomiri.Components 1.3
25import Lomiri.Session 0.1
26import Lomiri.Platform 1.0
31 property string indicator
32 property var rootModel: null
33 property var menuModel: null
35 property var _userMap: null
36 readonly property var _typeToComponent: {
38 "lomiri.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
39 "lomiri.widgets.systemsettings.tablet.switch" : switchMenu,
41 "com.canonical.indicator.button" : buttonMenu,
42 "com.canonical.indicator.div" : separatorMenu,
43 "com.canonical.indicator.section" : sectionMenu,
44 "com.canonical.indicator.progress" : progressMenu,
45 "com.canonical.indicator.slider" : sliderMenu,
46 "com.canonical.indicator.switch" : switchMenu,
47 "com.canonical.indicator.alarm" : alarmMenu,
48 "com.canonical.indicator.appointment" : appointmentMenu,
49 "com.canonical.indicator.transfer" : transferMenu,
50 "com.canonical.indicator.button-section" : buttonSectionMenu,
51 "com.canonical.indicator.link" : linkMenu,
53 "com.canonical.indicator.messages.messageitem" : messageItem,
54 "com.canonical.indicator.messages.sourceitem" : groupedMessage,
56 "com.canonical.lomiri.slider" : sliderMenu,
57 "com.canonical.lomiri.switch" : switchMenu,
59 "com.canonical.lomiri.media-player" : mediaPayerMenu,
60 "com.canonical.lomiri.playback-item" : playbackItemMenu,
62 "lomiri.widgets.systemsettings.tablet.wifisection" : wifiSection,
63 "lomiri.widgets.systemsettings.tablet.accesspoint" : accessPoint,
64 "com.lomiri.indicator.network.modeminfoitem" : modeminfoitem,
66 "com.canonical.indicator.calendar": calendarMenu,
67 "com.canonical.indicator.location": timezoneMenu,
69 "com.lomiri.indicator.transfer" : transferMenu,
71 "org.ayatana.indicator.button" : buttonMenu,
72 "org.ayatana.indicator.div" : separatorMenu,
73 "org.ayatana.indicator.section" : sectionMenu,
74 "org.ayatana.indicator.progress" : progressMenu,
75 "org.ayatana.indicator.slider" : sliderMenu,
76 "org.ayatana.indicator.switch" : switchMenu,
77 "org.ayatana.indicator.alarm" : alarmMenu,
78 "org.ayatana.indicator.appointment" : appointmentMenu,
79 "org.ayatana.indicator.transfer" : transferMenu,
80 "org.ayatana.indicator.button-section" : buttonSectionMenu,
81 "org.ayatana.indicator.link" : linkMenu,
83 "org.ayatana.indicator.messages.messageitem" : messageItem,
84 "org.ayatana.indicator.messages.sourceitem" : groupedMessage,
86 "org.ayatana.indicator.slider" : sliderMenu,
87 "org.ayatana.indicator.switch" : switchMenu,
89 "org.ayatana.indicator.media-player" : mediaPayerMenu,
90 "org.ayatana.indicator.playback-item" : playbackItemMenu,
92 "org.ayatana.indicator.network.modeminfoitem" : modeminfoitem,
94 "org.ayatana.indicator.calendar": calendarMenu,
95 "org.ayatana.indicator.location": timezoneMenu,
97 "indicator-session": {
98 "indicator.user-menu-item": Platform.isPC ? userMenuItem : null,
99 "indicator.guest-menu-item": Platform.isPC ? userMenuItem : null,
100 "com.canonical.indicator.switch": Math.min(Screen.width, Screen.height) > units.gu(60) ? switchMenu : null // Desktop mode switch
102 "indicator-messages": {
103 "com.canonical.indicator.button": messagesButtonMenu
105 "ayatana-indicator-session": {
106 "org.ayatana.indicator.user-menu-item": Platform.isPC ? userMenuItem : null,
107 "org.ayatana.indicator.guest-menu-item": Platform.isPC ? userMenuItem : null,
108 "org.ayatana.indicator.switch": Math.min(Screen.width, Screen.height) > units.gu(60) ? switchMenu : null // Desktop mode switch
110 "ayatana-indicator-messages": {
111 "org.ayatana.indicator.button": messagesButtonMenu
115 readonly property var _action_filter_map: {
116 "indicator-session": {
117 "indicator.logout": Platform.isPC ? undefined : null,
118 "indicator.suspend": Platform.isPC ? undefined : null,
119 "indicator.hibernate": Platform.isPC ? undefined : null,
120 "indicator.reboot": Platform.isPC ? undefined : null
122 "indicator-keyboard": {
123 "indicator.map": null,
124 "indicator.chart": null
126 "ayatana-indicator-session": {
127 "indicator.logout": Platform.isPC ? undefined : null,
128 "indicator.suspend": Platform.isPC ? undefined : null,
129 "indicator.hibernate": Platform.isPC ? undefined : null,
130 "indicator.reboot": Platform.isPC ? undefined : null
132 "ayatana-indicator-keyboard": {
133 "indicator.map": null,
134 "indicator.chart": null
138 function getComponentForIndicatorEntryType(type) {
139 var component = undefined;
140 var map = _userMap || _typeToComponent
141 var indicatorComponents = map[indicator];
143 if (type === undefined || type === "") {
147 if (indicatorComponents !== undefined) {
148 component = indicatorComponents[type];
151 if (component === undefined) {
152 component = map["default"][type];
155 if (component === undefined) {
156 console.debug("Don't know how to make " + type + " for " + indicator);
162 function getComponentForIndicatorEntryAction(action) {
163 var component = undefined;
164 var indicatorFilter = _action_filter_map[indicator]
166 if (action === undefined || action === "") {
170 if (indicatorFilter !== undefined) {
171 component = indicatorFilter[action];
176 function getExtendedProperty(object, propertyName, defaultValue) {
177 if (object && object.hasOwnProperty(propertyName)) {
178 return object[propertyName];
186 Menus.SeparatorMenu {
187 objectName: "separatorMenu"
196 objectName: "sliderMenu"
197 property QtObject menuData: null
198 property var menuModel: menuFactory.menuModel
199 property int menuIndex: -1
200 property var extendedData: menuData && menuData.ext || undefined
201 property var serverValue: getExtendedProperty(menuData, "actionState", undefined)
203 text: menuData && menuData.label || ""
204 minIcon: getExtendedProperty(extendedData, "minIcon", "")
205 maxIcon: getExtendedProperty(extendedData, "maxIcon", "")
207 minimumValue: getExtendedProperty(extendedData, "minValue", 0.0)
209 var maximum = getExtendedProperty(extendedData, "maxValue", 1.0);
210 if (maximum <= minimumValue) {
211 return minimumValue + 1;
215 enabled: menuData && menuData.sensitive || false
216 highlightWhenPressed: false
218 onMenuModelChanged: {
221 onMenuIndexChanged: {
225 function loadAttributes() {
226 if (!menuModel || menuIndex == -1) return;
227 menuModel.loadExtendedAttributes(menuIndex, {'min-value': 'double',
228 'max-value': 'double',
231 'x-ayatana-sync-action': 'string'});
234 ServerPropertySynchroniser {
235 id: sliderPropertySync
237 syncTimeout: Utils.Constants.indicatorValueTimeout
238 bufferedSyncTimeout: true
239 maximumWaitBufferInterval: 16
241 serverTarget: sliderItem
242 serverProperty: "serverValue"
243 userTarget: sliderItem
244 userProperty: "value"
246 onSyncTriggered: menuModel.changeState(menuIndex, value)
252 name: getExtendedProperty(extendedData, "xAyatanaSyncAction", "")
254 sliderPropertySync.reset();
255 sliderPropertySync.updateUserValue();
265 objectName: "buttonMenu"
266 property QtObject menuData: null
267 property var menuModel: menuFactory.menuModel
268 property int menuIndex: -1
270 buttonText: menuData && menuData.label || ""
271 enabled: menuData && menuData.sensitive || false
272 highlightWhenPressed: false
275 menuModel.activate(menuIndex);
281 id: messagesButtonMenu;
283 Menus.BaseLayoutMenu {
284 objectName: "messagesButtonMenu"
285 property QtObject menuData: null
286 property var menuModel: menuFactory.menuModel
287 property int menuIndex: -1
289 highlightWhenPressed: false
290 enabled: menuData && menuData.sensitive || false
291 text: menuData && menuData.label || ""
292 title.color: theme.palette.selected.backgroundText
293 title.horizontalAlignment: Text.AlignHCenter
294 title.font.bold: true
296 onClicked: menuModel.activate(menuIndex);
304 objectName: "sectionMenu"
305 property QtObject menuData: null
306 property var menuIndex: undefined
308 text: menuData && menuData.label || ""
316 Menus.ProgressValueMenu {
317 objectName: "progressMenu"
318 property QtObject menuData: null
319 property int menuIndex: -1
321 text: menuData && menuData.label || ""
322 iconSource: menuData && menuData.icon || ""
323 value : menuData && menuData.actionState || 0.0
324 enabled: menuData && menuData.sensitive || false
332 objectName: "standardMenu"
333 property QtObject menuData: null
334 property int menuIndex: -1
336 text: menuData && menuData.label || ""
337 iconSource: menuData && menuData.icon || ""
338 enabled: menuData && menuData.sensitive || false
339 highlightWhenPressed: false
342 menuModel.activate(menuIndex);
350 Menus.BaseLayoutMenu {
351 objectName: "linkMenu"
352 property QtObject menuData: null
353 property int menuIndex: -1
355 text: menuData && menuData.label || ""
356 enabled: menuData && menuData.sensitive || false
357 backColor: Qt.rgba(1,1,1,0.07)
358 highlightWhenPressed: false
361 menuModel.activate(menuIndex);
367 if (menuData.icon && menuData.icon != "") {
369 } else if (menuData.action.indexOf("settings") > -1) {
370 return "image://theme/settings"
377 color: theme.palette.normal.backgroundText
378 SlotsLayout.position: SlotsLayout.Trailing
386 Menus.CheckableMenu {
388 objectName: "checkableMenu"
389 property QtObject menuData: null
390 property int menuIndex: -1
391 property bool serverChecked: menuData && menuData.isToggled || false
393 text: menuData && menuData.label || ""
394 enabled: menuData && menuData.sensitive || false
395 checked: serverChecked
396 highlightWhenPressed: false
398 ServerPropertySynchroniser {
400 syncTimeout: Utils.Constants.indicatorValueTimeout
402 serverTarget: checkItem
403 serverProperty: "serverChecked"
404 userTarget: checkItem
405 userProperty: "checked"
407 onSyncTriggered: menuModel.activate(checkItem.menuIndex)
417 objectName: "radioMenu"
418 property QtObject menuData: null
419 property int menuIndex: -1
420 property bool serverChecked: menuData && menuData.isToggled || false
422 text: menuData && menuData.label || ""
423 enabled: menuData && menuData.sensitive || false
424 checked: serverChecked
425 highlightWhenPressed: false
427 ServerPropertySynchroniser {
429 syncTimeout: Utils.Constants.indicatorValueTimeout
431 serverTarget: radioItem
432 serverProperty: "serverChecked"
433 userTarget: radioItem
434 userProperty: "checked"
436 onSyncTriggered: menuModel.activate(radioItem.menuIndex)
446 objectName: "switchMenu"
447 property QtObject menuData: null
448 property var menuModel: menuFactory.menuModel
449 property int menuIndex: -1
450 property var extendedData: menuData && menuData.ext || undefined
451 property bool serverChecked: menuData && menuData.isToggled || false
453 text: menuData && menuData.label || ""
454 iconSource: menuData && menuData.icon || ""
455 enabled: menuData && menuData.sensitive || false
456 checked: serverChecked
457 highlightWhenPressed: false
459 property var subtitleAction: AyatanaMenuAction {
462 name: getExtendedProperty(extendedData, "xAyatanaSubtitleAction", "")
464 subtitle.text: subtitleAction.valid ? subtitleAction.state : ""
466 onMenuModelChanged: {
469 onMenuIndexChanged: {
473 function loadAttributes() {
474 if (!menuModel || menuIndex == -1) return;
475 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-subtitle-action': 'string'});
478 ServerPropertySynchroniser {
480 syncTimeout: Utils.Constants.indicatorValueTimeout
482 serverTarget: switchItem
483 serverProperty: "serverChecked"
484 userTarget: switchItem
485 userProperty: "checked"
489 * - com.canonical.indicator.switch
490 * - org.ayatana.indicator.switch (with fix)
491 * - com.canonical.indicator.switch mis-labled as
492 * org.ayatana.indicator.switch
493 * https://gitlab.com/ubports/development/core/lomiri-indicator-network/-/issues/87#note_1206883970
495 menuModel.changeState(switchItem.menuIndex, switchItem.checked);
506 objectName: "alarmMenu"
507 property QtObject menuData: null
508 property var menuModel: menuFactory.menuModel
509 property int menuIndex: -1
510 property var extendedData: menuData && menuData.ext || undefined
512 readonly property date serverTime: new Date(getExtendedProperty(extendedData, "xAyatanaTime", 0) * 1000)
514 frequency: LiveTimer.Relative
515 relativeTime: alarmItem.serverTime
516 onTrigger: alarmItem.time = i18n.relativeDateTime(alarmItem.serverTime)
519 text: menuData && menuData.label || ""
520 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
521 time: i18n.relativeDateTime(serverTime)
522 enabled: menuData && menuData.sensitive || false
523 highlightWhenPressed: false
525 onMenuModelChanged: {
528 onMenuIndexChanged: {
532 menuModel.activate(menuIndex);
535 function loadAttributes() {
536 if (!menuModel || menuIndex == -1) return;
537 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-time': 'int64'});
547 objectName: "appointmentMenu"
548 property QtObject menuData: null
549 property var menuModel: menuFactory.menuModel
550 property int menuIndex: -1
551 property var extendedData: menuData && menuData.ext || undefined
553 readonly property date serverTime: new Date(getExtendedProperty(extendedData, "xAyatanaTime", 0) * 1000)
556 frequency: LiveTimer.Relative
557 relativeTime: appointmentItem.serverTime
558 onTrigger: appointmentItem.time = i18n.relativeDateTime(appointmentItem.serverTime)
561 text: menuData && menuData.label || ""
562 iconSource: menuData && menuData.icon || "image://theme/calendar"
563 time: i18n.relativeDateTime(serverTime)
564 eventColor: getExtendedProperty(extendedData, "xAyatanaColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
565 enabled: menuData && menuData.sensitive || false
566 highlightWhenPressed: false
568 onMenuModelChanged: {
571 onMenuIndexChanged: {
575 menuModel.activate(menuIndex);
578 function loadAttributes() {
579 if (!menuModel || menuIndex == -1) return;
580 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-color': 'string',
581 'x-ayatana-time': 'int64'});
589 Menus.UserSessionMenu {
590 objectName: "userSessionMenu"
591 highlightWhenPressed: false
593 property QtObject menuData: null
594 property var menuModel: menuFactory.menuModel
595 property int menuIndex: -1
597 name: menuData && menuData.label || "" // label is the user's real name
598 iconSource: menuData && menuData.icon || ""
600 // would be better to compare with the logname but sadly the indicator doesn't expose that
601 active: DBusLomiriSessionService.RealName() !== "" ? DBusLomiriSessionService.RealName() == name
602 : DBusLomiriSessionService.UserName() == name
605 menuModel.activate(menuIndex);
615 objectName: "calendarMenu"
618 property QtObject menuData: null
619 property var menuModel: menuFactory.menuModel
620 property var actionState: menuData && menuData.actionState || null
621 property real calendarDay: getExtendedProperty(actionState, "calendar-day", 0)
622 property int menuIndex: -1
624 showWeekNumbers: getExtendedProperty(actionState, "show-week-numbers", false)
625 eventDays: getExtendedProperty(actionState, "appointment-days", [])
627 onCalendarDayChanged: {
628 if (calendarDay > 0) {
629 // This would trigger a selectionDateChanged signal, thus
630 // we've to avoid that the subsequent model activation
631 // would cause an infinite loop
632 modelUpdateConnections.enabled = false
633 currentDate = new Date(calendarDay * 1000)
634 modelUpdateConnections.enabled = true
639 id: modelUpdateConnections
640 property bool enabled: true
641 target: (enabled && calendarItem.visible) ? calendarItem : null
643 onSelectedDateChanged: {
644 menuModel.activate(menuIndex, selectedDate.getTime() / 1000 | 0)
655 objectName: "timezoneMenu"
657 property QtObject menuData: null
658 property var menuModel: menuFactory.menuModel
659 property int menuIndex: -1
660 property var extendedData: menuData && menuData.ext || undefined
661 readonly property string tz: getExtendedProperty(extendedData, "xAyatanaTimezone", "UTC")
662 property var updateTimer: Timer {
664 running: tzMenuItem.visible // only run when we're open
665 onTriggered: tzMenuItem.time = Utils.TimezoneFormatter.currentTimeInTimezone(tzMenuItem.tz)
668 city: menuData && menuData.label || ""
669 time: Utils.TimezoneFormatter.currentTimeInTimezone(tz)
670 enabled: menuData && menuData.sensitive || false
672 onMenuModelChanged: {
675 onMenuIndexChanged: {
679 tzActionGroup.setLocation.activate(tz);
684 busType: DBus.SessionBus
685 busName: "org.ayatana.indicator.datetime"
686 objectPath: "/org/ayatana/indicator/datetime"
688 property variant setLocation: action("set-location")
690 Component.onCompleted: tzActionGroup.start()
693 function loadAttributes() {
694 if (!menuModel || menuIndex == -1) return;
695 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-timezone': 'string'});
704 objectName: "wifiSection"
705 property QtObject menuData: null
706 property var menuModel: menuFactory.menuModel
707 property int menuIndex: -1
708 property var extendedData: menuData && menuData.ext || undefined
710 text: menuData && menuData.label || ""
711 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
713 onMenuModelChanged: {
716 onMenuIndexChanged: {
720 function loadAttributes() {
721 if (!menuModel || menuIndex == -1) return;
722 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
730 Menus.AccessPointMenu {
732 objectName: "accessPoint"
733 property QtObject menuData: null
734 property var menuModel: menuFactory.menuModel
735 property int menuIndex: -1
736 property var extendedData: menuData && menuData.ext || undefined
737 property bool serverChecked: menuData && menuData.isToggled || false
739 property var strengthAction: AyatanaMenuAction {
742 name: getExtendedProperty(extendedData, "xAyatanaWifiApStrengthAction", "")
745 text: menuData && menuData.label || ""
746 enabled: menuData && menuData.sensitive || false
747 active: serverChecked
748 secure: getExtendedProperty(extendedData, "xAyatanaWifiApIsSecure", false)
749 adHoc: getExtendedProperty(extendedData, "xAyatanaWifiApIsAdhoc", false)
751 if (strengthAction.valid) {
752 var state = strengthAction.state; // handle both int and uchar
753 // FIXME remove the special casing when we switch to indicator-network completely
754 if (typeof state == "string") {
755 return state.charCodeAt();
761 highlightWhenPressed: false
763 onMenuModelChanged: {
766 onMenuIndexChanged: {
770 function loadAttributes() {
771 if (!menuModel || menuIndex == -1) return;
772 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-wifi-ap-is-adhoc': 'bool',
773 'x-ayatana-wifi-ap-is-secure': 'bool',
774 'x-ayatana-wifi-ap-strength-action': 'string'});
777 ServerPropertySynchroniser {
779 syncTimeout: Utils.Constants.indicatorValueTimeout
782 serverProperty: "serverChecked"
784 userProperty: "active"
785 userTrigger: "onTriggered"
787 onSyncTriggered: menuModel.activate(apItem.menuIndex)
794 Menus.ModemInfoItem {
795 objectName: "modemInfoItem"
796 property QtObject menuData: null
797 property var menuModel: menuFactory.menuModel
798 property int menuIndex: -1
799 property var extendedData: menuData && menuData.ext || undefined
800 highlightWhenPressed: false
802 property var statusLabelAction: AyatanaMenuAction {
805 name: getExtendedProperty(extendedData, "xLomiriModemStatusLabelAction", "")
807 statusText: statusLabelAction.valid ? statusLabelAction.state : ""
809 property var statusIconAction: AyatanaMenuAction {
812 name: getExtendedProperty(extendedData, "xLomiriModemStatusIconAction", "")
814 statusIcon: statusIconAction.valid ? statusIconAction.state : ""
816 property var connectivityIconAction: AyatanaMenuAction {
819 name: getExtendedProperty(extendedData, "xLomiriModemConnectivityIconAction", "")
821 connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
823 property var simIdentifierLabelAction: AyatanaMenuAction {
826 name: getExtendedProperty(extendedData, "xLomiriModemSimIdentifierLabelAction", "")
828 simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
830 property var roamingAction: AyatanaMenuAction {
833 name: getExtendedProperty(extendedData, "xLomiriModemRoamingAction", "")
835 roaming: roamingAction.valid ? roamingAction.state : false
837 property var unlockAction: AyatanaMenuAction {
840 name: getExtendedProperty(extendedData, "xLomiriModemLockedAction", "")
843 unlockAction.activate();
845 locked: unlockAction.valid ? unlockAction.state : false
847 onMenuModelChanged: {
850 onMenuIndexChanged: {
854 function loadAttributes() {
855 if (!menuModel || menuIndex == -1) return;
856 menuModel.loadExtendedAttributes(menuIndex, {'x-lomiri-modem-status-label-action': 'string',
857 'x-lomiri-modem-status-icon-action': 'string',
858 'x-lomiri-modem-connectivity-icon-action': 'string',
859 'x-lomiri-modem-sim-identifier-label-action': 'string',
860 'x-lomiri-modem-roaming-action': 'string',
861 'x-lomiri-modem-locked-action': 'string'});
869 MessageMenuItemFactory {
870 objectName: "messageItem"
871 menuModel: menuFactory.menuModel
878 Menus.GroupedMessageMenu {
879 objectName: "groupedMessage"
880 property QtObject menuData: null
881 property var menuModel: menuFactory.menuModel
882 property int menuIndex: -1
883 property var extendedData: menuData && menuData.ext || undefined
885 text: menuData && menuData.label || ""
886 iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
887 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
888 enabled: menuData && menuData.sensitive || false
889 highlightWhenPressed: false
892 onMenuModelChanged: {
895 onMenuIndexChanged: {
899 menuModel.activate(menuIndex, true);
902 menuModel.activate(menuIndex, false);
905 function loadAttributes() {
906 if (!menuModel || menuIndex == -1) return;
907 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
915 Menus.MediaPlayerMenu {
916 objectName: "mediaPayerMenu"
917 property QtObject menuData: null
918 property var menuModel: menuFactory.menuModel
919 property int menuIndex: -1
920 property var actionState: menuData && menuData.actionState || undefined
921 property bool running: getExtendedProperty(actionState, "running", false)
923 playerIcon: menuData && menuData.icon || "image://theme/stock_music"
924 playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
926 albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
927 song: getExtendedProperty(actionState, "title", "")
928 artist: getExtendedProperty(actionState, "artist", "")
929 album: getExtendedProperty(actionState, "album", "")
930 showTrack: running && (state == "Playing" || state == "Paused")
931 state: getExtendedProperty(actionState, "state", "")
932 enabled: menuData && menuData.sensitive || false
933 highlightWhenPressed: false
936 model.activate(modelIndex);
942 id: playbackItemMenu;
944 Menus.PlaybackItemMenu {
945 objectName: "playbackItemMenu"
946 property QtObject menuData: null
947 property var menuModel: menuFactory.menuModel
948 property int menuIndex: -1
949 property var extendedData: menuData && menuData.ext || undefined
951 property var playAction: AyatanaMenuAction {
954 name: getExtendedProperty(extendedData, "xAyatanaPlayAction", "")
956 property var nextAction: AyatanaMenuAction {
959 name: getExtendedProperty(extendedData, "xAyatanaNextAction", "")
961 property var previousAction: AyatanaMenuAction {
964 name: getExtendedProperty(extendedData, "xAyatanaPreviousAction", "")
967 playing: playAction.state === "Playing"
968 canPlay: playAction.valid
969 canGoNext: nextAction.valid
970 canGoPrevious: previousAction.valid
971 enabled: menuData && menuData.sensitive || false
972 highlightWhenPressed: false
975 playAction.activate();
978 nextAction.activate();
981 previousAction.activate();
983 onMenuModelChanged: {
986 onMenuIndexChanged: {
990 function loadAttributes() {
991 if (!menuModel || menuIndex == -1) return;
992 menuModel.loadExtendedAttributes(modelIndex, {'x-ayatana-play-action': 'string',
993 'x-ayatana-next-action': 'string',
994 'x-ayatana-previous-action': 'string'});
1002 Menus.TransferMenu {
1003 objectName: "transferMenu"
1005 property QtObject menuData: null
1006 property var menuModel: menuFactory.menuModel
1007 property int menuIndex: -1
1008 property var extendedData: menuData && menuData.ext || undefined
1009 property var uid: getExtendedProperty(extendedData, "xAyatanaUid", undefined)
1011 text: menuData && menuData.label || ""
1012 iconSource: menuData && menuData.icon || "image://theme/transfer-none"
1014 enabled: menuData && menuData.sensitive || false
1015 highlightWhenPressed: false
1017 confirmRemoval: true
1022 busName: menuFactory.rootModel.busName
1023 objectPath: menuFactory.rootModel.actions["indicator"]
1025 property var activateAction: action("activate-transfer")
1026 property var cancelAction: action("cancel-transfer")
1027 property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
1029 Component.onCompleted: actionGroup.start()
1032 property var transferState: {
1033 if (actionGroup.transferStateAction === null) return undefined;
1034 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
1037 property var runningState : transferState !== undefined ? transferState["state"] : undefined
1038 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
1040 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
1041 progress: transferState !== undefined ? transferState["percent"] : 0.0
1043 // TODO - Should be in the SDK
1044 property var timeRemaining: {
1045 if (secondsLeft === undefined) return undefined;
1048 var hours = Math.floor(secondsLeft / (60 * 60));
1049 var minutes = Math.floor(secondsLeft / 60) % 60;
1050 var seconds = secondsLeft % 60;
1052 remaining += i18n.tr("%1 hour", "%1 hours", hours).arg(hours)
1055 if (remaining != "") remaining += ", ";
1056 remaining += i18n.tr("%1 minute", "%1 minutes", minutes).arg(minutes)
1058 // don't include seconds if hours > 0
1059 if (hours == 0 && minutes < 5 && seconds > 0) {
1060 if (remaining != "") remaining += ", ";
1061 remaining += i18n.tr("%1 second", "%1 seconds", seconds).arg(seconds)
1063 if (remaining == "")
1064 remaining = i18n.tr("0 seconds");
1065 // Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
1066 return i18n.tr("%1 remaining").arg(remaining);
1070 switch (runningState) {
1071 case Menus.TransferState.Queued:
1072 return i18n.tr("In queue…");
1073 case Menus.TransferState.Hashing:
1074 case Menus.TransferState.Processing:
1075 case Menus.TransferState.Running:
1076 return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
1077 case Menus.TransferState.Paused:
1078 return i18n.tr("Paused, tap to resume");
1079 case Menus.TransferState.Canceled:
1080 return i18n.tr("Canceled");
1081 case Menus.TransferState.Finished:
1082 return i18n.tr("Finished");
1083 case Menus.TransferState.Error:
1084 return i18n.tr("Failed, tap to retry");
1089 onMenuModelChanged: {
1092 onMenuIndexChanged: {
1096 actionGroup.activateAction.activate(uid);
1099 actionGroup.cancelAction.activate(uid);
1102 function loadAttributes() {
1103 if (!menuModel || menuIndex == -1) return;
1104 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-uid': 'string'});
1110 id: buttonSectionMenu;
1113 objectName: "buttonSectionMenu"
1114 property QtObject menuData: null
1115 property var menuModel: menuFactory.menuModel
1116 property int menuIndex: -1
1117 property var extendedData: menuData && menuData.ext || undefined
1119 iconSource: menuData && menuData.icon || ""
1120 enabled: menuData && menuData.sensitive || false
1121 highlightWhenPressed: false
1122 text: menuData && menuData.label || ""
1123 foregroundColor: theme.palette.normal.backgroundText
1124 buttonText: getExtendedProperty(extendedData, "xAyatanaExtraLabel", "")
1126 onMenuModelChanged: {
1129 onMenuIndexChanged: {
1132 function loadAttributes() {
1133 if (!menuModel || menuIndex == -1) return;
1134 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-extra-label': 'string'});
1137 onButtonClicked: menuModel.activate(menuIndex);
1141 function load(modelData) {
1142 var component = getComponentForIndicatorEntryAction(modelData.action)
1143 if (component !== undefined) {
1147 component = getComponentForIndicatorEntryType(modelData.type)
1148 if (component !== undefined) {
1152 if (modelData.isCheck) {
1153 return checkableMenu;
1155 if (modelData.isRadio) {
1158 if (modelData.isSeparator) {
1159 return separatorMenu;
1161 if (modelData.action !== undefined && modelData.action.indexOf("settings") > -1) {
1162 // FIXME : At the moment, the indicators aren't using
1163 // org.ayatana.indicators.link for settings menu. Need to fudge it.
1166 return standardMenu;