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, {
476 'x-ayatana-subtitle-action': 'string',
481 ServerPropertySynchroniser {
483 syncTimeout: Utils.Constants.indicatorValueTimeout
485 serverTarget: switchItem
486 serverProperty: "serverChecked"
487 userTarget: switchItem
488 userProperty: "checked"
491 /* Figures out if the action's activate() requires a
492 * parameter or not. Works with:
493 * - com.canonical.indicator.switch
494 * - org.ayatana.indicator.switch (with fix)
495 * - com.canonical.indicator.switch mis-labled as
496 * org.ayatana.indicator.switch
497 * https://gitlab.com/ubports/development/core/lomiri-indicator-network/-/issues/87#note_1206883970
499 * If activate() requires a parameter but menu doesn't
500 * specify a target, the menu will be broken in a different
503 if (extendedData.hasOwnProperty('target')) {
504 menuModel.activate(switchItem.menuIndex, switchItem.checked);
506 menuModel.activate(switchItem.menuIndex);
518 objectName: "alarmMenu"
519 property QtObject menuData: null
520 property var menuModel: menuFactory.menuModel
521 property int menuIndex: -1
522 property var extendedData: menuData && menuData.ext || undefined
524 readonly property date serverTime: new Date(getExtendedProperty(extendedData, "xAyatanaTime", 0) * 1000)
526 frequency: LiveTimer.Relative
527 relativeTime: alarmItem.serverTime
528 onTrigger: alarmItem.time = i18n.relativeDateTime(alarmItem.serverTime)
531 text: menuData && menuData.label || ""
532 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
533 time: i18n.relativeDateTime(serverTime)
534 enabled: menuData && menuData.sensitive || false
535 highlightWhenPressed: false
537 onMenuModelChanged: {
540 onMenuIndexChanged: {
544 menuModel.activate(menuIndex);
547 function loadAttributes() {
548 if (!menuModel || menuIndex == -1) return;
549 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-time': 'int64'});
559 objectName: "appointmentMenu"
560 property QtObject menuData: null
561 property var menuModel: menuFactory.menuModel
562 property int menuIndex: -1
563 property var extendedData: menuData && menuData.ext || undefined
565 readonly property date serverTime: new Date(getExtendedProperty(extendedData, "xAyatanaTime", 0) * 1000)
568 frequency: LiveTimer.Relative
569 relativeTime: appointmentItem.serverTime
570 onTrigger: appointmentItem.time = i18n.relativeDateTime(appointmentItem.serverTime)
573 text: menuData && menuData.label || ""
574 iconSource: menuData && menuData.icon || "image://theme/calendar"
575 time: i18n.relativeDateTime(serverTime)
576 eventColor: getExtendedProperty(extendedData, "xAyatanaColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
577 enabled: menuData && menuData.sensitive || false
578 highlightWhenPressed: false
580 onMenuModelChanged: {
583 onMenuIndexChanged: {
587 menuModel.activate(menuIndex);
590 function loadAttributes() {
591 if (!menuModel || menuIndex == -1) return;
592 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-color': 'string',
593 'x-ayatana-time': 'int64'});
601 Menus.UserSessionMenu {
602 objectName: "userSessionMenu"
603 highlightWhenPressed: false
605 property QtObject menuData: null
606 property var menuModel: menuFactory.menuModel
607 property int menuIndex: -1
609 name: menuData && menuData.label || "" // label is the user's real name
610 iconSource: menuData && menuData.icon || ""
612 // would be better to compare with the logname but sadly the indicator doesn't expose that
613 active: DBusLomiriSessionService.RealName() !== "" ? DBusLomiriSessionService.RealName() == name
614 : DBusLomiriSessionService.UserName() == name
617 menuModel.activate(menuIndex);
627 objectName: "calendarMenu"
630 property QtObject menuData: null
631 property var menuModel: menuFactory.menuModel
632 property var actionState: menuData && menuData.actionState || null
633 property real calendarDay: getExtendedProperty(actionState, "calendar-day", 0)
634 property int menuIndex: -1
636 showWeekNumbers: getExtendedProperty(actionState, "show-week-numbers", false)
637 eventDays: getExtendedProperty(actionState, "appointment-days", [])
639 onCalendarDayChanged: {
640 if (calendarDay > 0) {
641 // This would trigger a selectionDateChanged signal, thus
642 // we've to avoid that the subsequent model activation
643 // would cause an infinite loop
644 modelUpdateConnections.enabled = false
645 currentDate = new Date(calendarDay * 1000)
646 modelUpdateConnections.enabled = true
651 id: modelUpdateConnections
652 property bool enabled: true
653 target: (enabled && calendarItem.visible) ? calendarItem : null
655 onSelectedDateChanged: {
656 menuModel.activate(menuIndex, selectedDate.getTime() / 1000 | 0)
667 objectName: "timezoneMenu"
669 property QtObject menuData: null
670 property var menuModel: menuFactory.menuModel
671 property int menuIndex: -1
672 property var extendedData: menuData && menuData.ext || undefined
673 readonly property string tz: getExtendedProperty(extendedData, "xAyatanaTimezone", "UTC")
674 property var updateTimer: Timer {
676 running: tzMenuItem.visible // only run when we're open
677 onTriggered: tzMenuItem.time = Utils.TimezoneFormatter.currentTimeInTimezone(tzMenuItem.tz)
680 city: menuData && menuData.label || ""
681 time: Utils.TimezoneFormatter.currentTimeInTimezone(tz)
682 enabled: menuData && menuData.sensitive || false
684 onMenuModelChanged: {
687 onMenuIndexChanged: {
691 tzActionGroup.setLocation.activate(tz);
696 busType: DBus.SessionBus
697 busName: "org.ayatana.indicator.datetime"
698 objectPath: "/org/ayatana/indicator/datetime"
700 property variant setLocation: action("set-location")
702 Component.onCompleted: tzActionGroup.start()
705 function loadAttributes() {
706 if (!menuModel || menuIndex == -1) return;
707 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-timezone': 'string'});
716 objectName: "wifiSection"
717 property QtObject menuData: null
718 property var menuModel: menuFactory.menuModel
719 property int menuIndex: -1
720 property var extendedData: menuData && menuData.ext || undefined
722 text: menuData && menuData.label || ""
723 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
725 onMenuModelChanged: {
728 onMenuIndexChanged: {
732 function loadAttributes() {
733 if (!menuModel || menuIndex == -1) return;
734 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
742 Menus.AccessPointMenu {
744 objectName: "accessPoint"
745 property QtObject menuData: null
746 property var menuModel: menuFactory.menuModel
747 property int menuIndex: -1
748 property var extendedData: menuData && menuData.ext || undefined
749 property bool serverChecked: menuData && menuData.isToggled || false
751 property var strengthAction: AyatanaMenuAction {
754 name: getExtendedProperty(extendedData, "xAyatanaWifiApStrengthAction", "")
757 text: menuData && menuData.label || ""
758 enabled: menuData && menuData.sensitive || false
759 active: serverChecked
760 secure: getExtendedProperty(extendedData, "xAyatanaWifiApIsSecure", false)
761 adHoc: getExtendedProperty(extendedData, "xAyatanaWifiApIsAdhoc", false)
763 if (strengthAction.valid) {
764 var state = strengthAction.state; // handle both int and uchar
765 // FIXME remove the special casing when we switch to indicator-network completely
766 if (typeof state == "string") {
767 return state.charCodeAt();
773 highlightWhenPressed: false
775 onMenuModelChanged: {
778 onMenuIndexChanged: {
782 function loadAttributes() {
783 if (!menuModel || menuIndex == -1) return;
784 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-wifi-ap-is-adhoc': 'bool',
785 'x-ayatana-wifi-ap-is-secure': 'bool',
786 'x-ayatana-wifi-ap-strength-action': 'string'});
789 ServerPropertySynchroniser {
791 syncTimeout: Utils.Constants.indicatorValueTimeout
794 serverProperty: "serverChecked"
796 userProperty: "active"
797 userTrigger: "onTriggered"
799 onSyncTriggered: menuModel.activate(apItem.menuIndex)
806 Menus.ModemInfoItem {
807 objectName: "modemInfoItem"
808 property QtObject menuData: null
809 property var menuModel: menuFactory.menuModel
810 property int menuIndex: -1
811 property var extendedData: menuData && menuData.ext || undefined
812 highlightWhenPressed: false
814 property var statusLabelAction: AyatanaMenuAction {
817 name: getExtendedProperty(extendedData, "xLomiriModemStatusLabelAction", "")
819 statusText: statusLabelAction.valid ? statusLabelAction.state : ""
821 property var statusIconAction: AyatanaMenuAction {
824 name: getExtendedProperty(extendedData, "xLomiriModemStatusIconAction", "")
826 statusIcon: statusIconAction.valid ? statusIconAction.state : ""
828 property var connectivityIconAction: AyatanaMenuAction {
831 name: getExtendedProperty(extendedData, "xLomiriModemConnectivityIconAction", "")
833 connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
835 property var simIdentifierLabelAction: AyatanaMenuAction {
838 name: getExtendedProperty(extendedData, "xLomiriModemSimIdentifierLabelAction", "")
840 simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
842 property var roamingAction: AyatanaMenuAction {
845 name: getExtendedProperty(extendedData, "xLomiriModemRoamingAction", "")
847 roaming: roamingAction.valid ? roamingAction.state : false
849 property var unlockAction: AyatanaMenuAction {
852 name: getExtendedProperty(extendedData, "xLomiriModemLockedAction", "")
855 unlockAction.activate();
857 locked: unlockAction.valid ? unlockAction.state : false
859 onMenuModelChanged: {
862 onMenuIndexChanged: {
866 function loadAttributes() {
867 if (!menuModel || menuIndex == -1) return;
868 menuModel.loadExtendedAttributes(menuIndex, {'x-lomiri-modem-status-label-action': 'string',
869 'x-lomiri-modem-status-icon-action': 'string',
870 'x-lomiri-modem-connectivity-icon-action': 'string',
871 'x-lomiri-modem-sim-identifier-label-action': 'string',
872 'x-lomiri-modem-roaming-action': 'string',
873 'x-lomiri-modem-locked-action': 'string'});
881 MessageMenuItemFactory {
882 objectName: "messageItem"
883 menuModel: menuFactory.menuModel
890 Menus.GroupedMessageMenu {
891 objectName: "groupedMessage"
892 property QtObject menuData: null
893 property var menuModel: menuFactory.menuModel
894 property int menuIndex: -1
895 property var extendedData: menuData && menuData.ext || undefined
897 text: menuData && menuData.label || ""
898 iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
899 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
900 enabled: menuData && menuData.sensitive || false
901 highlightWhenPressed: false
904 onMenuModelChanged: {
907 onMenuIndexChanged: {
911 menuModel.activate(menuIndex, true);
914 menuModel.activate(menuIndex, false);
917 function loadAttributes() {
918 if (!menuModel || menuIndex == -1) return;
919 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
927 Menus.MediaPlayerMenu {
928 objectName: "mediaPayerMenu"
929 property QtObject menuData: null
930 property var menuModel: menuFactory.menuModel
931 property int menuIndex: -1
932 property var actionState: menuData && menuData.actionState || undefined
933 property bool running: getExtendedProperty(actionState, "running", false)
935 playerIcon: menuData && menuData.icon || "image://theme/stock_music"
936 playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
938 albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
939 song: getExtendedProperty(actionState, "title", "")
940 artist: getExtendedProperty(actionState, "artist", "")
941 album: getExtendedProperty(actionState, "album", "")
942 showTrack: running && (state == "Playing" || state == "Paused")
943 state: getExtendedProperty(actionState, "state", "")
944 enabled: menuData && menuData.sensitive || false
945 highlightWhenPressed: false
948 model.activate(modelIndex);
954 id: playbackItemMenu;
956 Menus.PlaybackItemMenu {
957 objectName: "playbackItemMenu"
958 property QtObject menuData: null
959 property var menuModel: menuFactory.menuModel
960 property int menuIndex: -1
961 property var extendedData: menuData && menuData.ext || undefined
963 property var playAction: AyatanaMenuAction {
966 name: getExtendedProperty(extendedData, "xAyatanaPlayAction", "")
968 property var nextAction: AyatanaMenuAction {
971 name: getExtendedProperty(extendedData, "xAyatanaNextAction", "")
973 property var previousAction: AyatanaMenuAction {
976 name: getExtendedProperty(extendedData, "xAyatanaPreviousAction", "")
979 playing: playAction.state === "Playing"
980 canPlay: playAction.valid
981 canGoNext: nextAction.valid
982 canGoPrevious: previousAction.valid
983 enabled: menuData && menuData.sensitive || false
984 highlightWhenPressed: false
987 playAction.activate();
990 nextAction.activate();
993 previousAction.activate();
995 onMenuModelChanged: {
998 onMenuIndexChanged: {
1002 function loadAttributes() {
1003 if (!menuModel || menuIndex == -1) return;
1004 menuModel.loadExtendedAttributes(modelIndex, {'x-ayatana-play-action': 'string',
1005 'x-ayatana-next-action': 'string',
1006 'x-ayatana-previous-action': 'string'});
1014 Menus.TransferMenu {
1015 objectName: "transferMenu"
1017 property QtObject menuData: null
1018 property var menuModel: menuFactory.menuModel
1019 property int menuIndex: -1
1020 property var extendedData: menuData && menuData.ext || undefined
1021 property var uid: getExtendedProperty(extendedData, "xAyatanaUid", undefined)
1023 text: menuData && menuData.label || ""
1024 iconSource: menuData && menuData.icon || "image://theme/transfer-none"
1026 enabled: menuData && menuData.sensitive || false
1027 highlightWhenPressed: false
1029 confirmRemoval: true
1034 busName: menuFactory.rootModel.busName
1035 objectPath: menuFactory.rootModel.actions["indicator"]
1037 property var activateAction: action("activate-transfer")
1038 property var cancelAction: action("cancel-transfer")
1039 property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
1041 Component.onCompleted: actionGroup.start()
1044 property var transferState: {
1045 if (actionGroup.transferStateAction === null) return undefined;
1046 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
1049 property var runningState : transferState !== undefined ? transferState["state"] : undefined
1050 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
1052 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
1053 progress: transferState !== undefined ? transferState["percent"] : 0.0
1055 // TODO - Should be in the SDK
1056 property var timeRemaining: {
1057 if (secondsLeft === undefined) return undefined;
1060 var hours = Math.floor(secondsLeft / (60 * 60));
1061 var minutes = Math.floor(secondsLeft / 60) % 60;
1062 var seconds = secondsLeft % 60;
1064 remaining += i18n.tr("%1 hour", "%1 hours", hours).arg(hours)
1067 if (remaining != "") remaining += ", ";
1068 remaining += i18n.tr("%1 minute", "%1 minutes", minutes).arg(minutes)
1070 // don't include seconds if hours > 0
1071 if (hours == 0 && minutes < 5 && seconds > 0) {
1072 if (remaining != "") remaining += ", ";
1073 remaining += i18n.tr("%1 second", "%1 seconds", seconds).arg(seconds)
1075 if (remaining == "")
1076 remaining = i18n.tr("0 seconds");
1077 // Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
1078 return i18n.tr("%1 remaining").arg(remaining);
1082 switch (runningState) {
1083 case Menus.TransferState.Queued:
1084 return i18n.tr("In queue…");
1085 case Menus.TransferState.Hashing:
1086 case Menus.TransferState.Processing:
1087 case Menus.TransferState.Running:
1088 return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
1089 case Menus.TransferState.Paused:
1090 return i18n.tr("Paused, tap to resume");
1091 case Menus.TransferState.Canceled:
1092 return i18n.tr("Canceled");
1093 case Menus.TransferState.Finished:
1094 return i18n.tr("Finished");
1095 case Menus.TransferState.Error:
1096 return i18n.tr("Failed, tap to retry");
1101 onMenuModelChanged: {
1104 onMenuIndexChanged: {
1108 actionGroup.activateAction.activate(uid);
1111 actionGroup.cancelAction.activate(uid);
1114 function loadAttributes() {
1115 if (!menuModel || menuIndex == -1) return;
1116 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-uid': 'string'});
1122 id: buttonSectionMenu;
1125 objectName: "buttonSectionMenu"
1126 property QtObject menuData: null
1127 property var menuModel: menuFactory.menuModel
1128 property int menuIndex: -1
1129 property var extendedData: menuData && menuData.ext || undefined
1131 iconSource: menuData && menuData.icon || ""
1132 enabled: menuData && menuData.sensitive || false
1133 highlightWhenPressed: false
1134 text: menuData && menuData.label || ""
1135 foregroundColor: theme.palette.normal.backgroundText
1136 buttonText: getExtendedProperty(extendedData, "xAyatanaExtraLabel", "")
1138 onMenuModelChanged: {
1141 onMenuIndexChanged: {
1144 function loadAttributes() {
1145 if (!menuModel || menuIndex == -1) return;
1146 menuModel.loadExtendedAttributes(menuIndex, {'x-ayatana-extra-label': 'string'});
1149 onButtonClicked: menuModel.activate(menuIndex);
1153 function load(modelData) {
1154 var component = getComponentForIndicatorEntryAction(modelData.action)
1155 if (component !== undefined) {
1159 component = getComponentForIndicatorEntryType(modelData.type)
1160 if (component !== undefined) {
1164 if (modelData.isCheck) {
1165 return checkableMenu;
1167 if (modelData.isRadio) {
1170 if (modelData.isSeparator) {
1171 return separatorMenu;
1173 if (modelData.action !== undefined && modelData.action.indexOf("settings") > -1) {
1174 // FIXME : At the moment, the indicators aren't using
1175 // org.ayatana.indicators.link for settings menu. Need to fudge it.
1178 return standardMenu;