on eae958a help sign in
unity8/qml/Panel/Indicators/MenuItemFactory.qml
1039 lines 38 kB history raw
1/*
2 * Copyright 2013-2016 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17import QtQuick 2.4
18import QtQuick.Window 2.2
19import Ubuntu.Settings.Menus 0.1 as Menus
20import Ubuntu.Settings.Components 0.1
21import QMenuModel 0.1
22import Utils 0.1 as Utils
23import Ubuntu.Components.ListItems 1.3 as ListItems
24import Ubuntu.Components 1.3
25import Unity.Session 0.1
26import Unity.Platform 1.0
28Item {
29 id: menuFactory
31 property var rootModel: null
32 property var menuModel: null
34 property var _userMap: null
35 readonly property var _typeToComponent: {
36 "default": {
37 "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
38 "unity.widgets.systemsettings.tablet.switch" : switchMenu,
40 "com.canonical.indicator.button" : buttonMenu,
41 "com.canonical.indicator.div" : separatorMenu,
42 "com.canonical.indicator.section" : sectionMenu,
43 "com.canonical.indicator.progress" : progressMenu,
44 "com.canonical.indicator.slider" : sliderMenu,
45 "com.canonical.indicator.switch" : switchMenu,
46 "com.canonical.indicator.alarm" : alarmMenu,
47 "com.canonical.indicator.appointment" : appointmentMenu,
48 "com.canonical.indicator.transfer" : transferMenu,
49 "com.canonical.indicator.button-section" : buttonSectionMenu,
50 "com.canonical.indicator.link" : linkMenu,
52 "com.canonical.indicator.messages.messageitem" : messageItem,
53 "com.canonical.indicator.messages.sourceitem" : groupedMessage,
55 "com.canonical.unity.slider" : sliderMenu,
56 "com.canonical.unity.switch" : switchMenu,
58 "com.canonical.unity.media-player" : mediaPayerMenu,
59 "com.canonical.unity.playback-item" : playbackItemMenu,
61 "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
62 "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
63 "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
65 "com.canonical.indicator.calendar": calendarMenu,
66 "com.canonical.indicator.location": timezoneMenu,
67 },
68 "indicator-session": {
69 "indicator.user-menu-item": Platform.isPC ? userMenuItem : null,
70 "indicator.guest-menu-item": Platform.isPC ? userMenuItem : null,
71 "com.canonical.indicator.switch": Math.min(Screen.width, Screen.height) > units.gu(60) ? switchMenu : null // Desktop mode switch
72 },
73 "indicator-messages": {
74 "com.canonical.indicator.button": messagesButtonMenu
75 }
76 }
78 readonly property var _action_filter_map: {
79 "indicator-session": {
80 "indicator.logout": Platform.isPC ? undefined : null,
81 "indicator.suspend": Platform.isPC ? undefined : null,
82 "indicator.hibernate": Platform.isPC ? undefined : null,
83 "indicator.reboot": Platform.isPC ? undefined : null
84 },
85 "indicator-keyboard": {
86 "indicator.map": null,
87 "indicator.chart": null
88 }
89 }
91 function getComponentForIndicatorEntryType(indicator, type) {
92 var component = undefined;
93 var map = _userMap || _typeToComponent
94 var indicatorComponents = map[indicator];
96 if (type === undefined || type === "") {
97 return component
98 }
100 if (indicatorComponents !== undefined) {
101 component = indicatorComponents[type];
102 }
104 if (component === undefined) {
105 component = map["default"][type];
106 }
108 if (component === undefined) {
109 console.debug("Don't know how to make " + modelData.type + " for " + indicator);
110 }
112 return component
113 }
115 function getComponentForIndicatorEntryAction(indicator, action) {
116 var component = undefined;
117 var indicatorFilter = _action_filter_map[indicator]
119 if (action === undefined || action === "") {
120 return component
121 }
123 if (indicatorFilter !== undefined) {
124 component = indicatorFilter[action];
125 }
126 return component
127 }
129 function getExtendedProperty(object, propertyName, defaultValue) {
130 if (object && object.hasOwnProperty(propertyName)) {
131 return object[propertyName];
132 }
133 return defaultValue;
134 }
136 Component {
137 id: separatorMenu;
139 Menus.SeparatorMenu {
140 objectName: "separatorMenu"
141 }
142 }
144 Component {
145 id: sliderMenu;
147 Menus.SliderMenu {
148 id: sliderItem
149 objectName: "sliderMenu"
150 property QtObject menuData: null
151 property var menuModel: menuFactory.menuModel
152 property int menuIndex: -1
153 property var extendedData: menuData && menuData.ext || undefined
154 property var serverValue: getExtendedProperty(menuData, "actionState", undefined)
156 text: menuData && menuData.label || ""
157 minIcon: getExtendedProperty(extendedData, "minIcon", "")
158 maxIcon: getExtendedProperty(extendedData, "maxIcon", "")
160 minimumValue: getExtendedProperty(extendedData, "minValue", 0.0)
161 maximumValue: {
162 var maximum = getExtendedProperty(extendedData, "maxValue", 1.0);
163 if (maximum <= minimumValue) {
164 return minimumValue + 1;
165 }
166 return maximum;
167 }
168 enabled: menuData && menuData.sensitive || false
169 highlightWhenPressed: false
171 onMenuModelChanged: {
172 loadAttributes();
173 }
174 onMenuIndexChanged: {
175 loadAttributes();
176 }
178 function loadAttributes() {
179 if (!menuModel || menuIndex == -1) return;
180 menuModel.loadExtendedAttributes(menuIndex, {'min-value': 'double',
181 'max-value': 'double',
182 'min-icon': 'icon',
183 'max-icon': 'icon',
184 'x-canonical-sync-action': 'string'});
185 }
187 ServerPropertySynchroniser {
188 id: sliderPropertySync
189 objectName: "sync"
190 syncTimeout: Utils.Constants.indicatorValueTimeout
191 bufferedSyncTimeout: true
192 maximumWaitBufferInterval: 16
194 serverTarget: sliderItem
195 serverProperty: "serverValue"
196 userTarget: sliderItem
197 userProperty: "value"
199 onSyncTriggered: menuModel.changeState(menuIndex, value)
200 }
202 UnityMenuAction {
203 model: menuModel
204 index: menuIndex
205 name: getExtendedProperty(extendedData, "xCanonicalSyncAction", "")
206 onStateChanged: {
207 sliderPropertySync.reset();
208 sliderPropertySync.updateUserValue();
209 }
210 }
211 }
212 }
214 Component {
215 id: buttonMenu;
217 Menus.ButtonMenu {
218 objectName: "buttonMenu"
219 property QtObject menuData: null
220 property var menuModel: menuFactory.menuModel
221 property int menuIndex: -1
223 buttonText: menuData && menuData.label || ""
224 enabled: menuData && menuData.sensitive || false
225 highlightWhenPressed: false
227 onTriggered: {
228 menuModel.activate(menuIndex);
229 }
230 }
231 }
233 Component {
234 id: messagesButtonMenu;
236 Menus.BaseLayoutMenu {
237 objectName: "messagesButtonMenu"
238 property QtObject menuData: null
239 property var menuModel: menuFactory.menuModel
240 property int menuIndex: -1
242 highlightWhenPressed: false
243 enabled: menuData && menuData.sensitive || false
244 text: menuData && menuData.label || ""
245 title.color: theme.palette.selected.backgroundText
246 title.horizontalAlignment: Text.AlignHCenter
247 title.font.bold: true
249 onClicked: menuModel.activate(menuIndex);
250 }
251 }
253 Component {
254 id: sectionMenu;
256 Menus.SectionMenu {
257 objectName: "sectionMenu"
258 property QtObject menuData: null
259 property var menuIndex: undefined
261 text: menuData && menuData.label || ""
262 busy: false
263 }
264 }
266 Component {
267 id: progressMenu;
269 Menus.ProgressValueMenu {
270 objectName: "progressMenu"
271 property QtObject menuData: null
272 property int menuIndex: -1
274 text: menuData && menuData.label || ""
275 iconSource: menuData && menuData.icon || ""
276 value : menuData && menuData.actionState || 0.0
277 enabled: menuData && menuData.sensitive || false
278 }
279 }
281 Component {
282 id: standardMenu;
284 Menus.StandardMenu {
285 objectName: "standardMenu"
286 property QtObject menuData: null
287 property int menuIndex: -1
289 text: menuData && menuData.label || ""
290 iconSource: menuData && menuData.icon || ""
291 enabled: menuData && menuData.sensitive || false
292 highlightWhenPressed: false
294 onTriggered: {
295 menuModel.activate(menuIndex);
296 }
297 }
298 }
300 Component {
301 id: linkMenu;
303 Menus.BaseLayoutMenu {
304 objectName: "linkMenu"
305 property QtObject menuData: null
306 property int menuIndex: -1
308 text: menuData && menuData.label || ""
309 enabled: menuData && menuData.sensitive || false
310 backColor: Qt.rgba(1,1,1,0.07)
311 highlightWhenPressed: false
313 onTriggered: {
314 menuModel.activate(menuIndex);
315 }
317 slots: Icon {
318 source: {
319 if (menuData) {
320 if (menuData.icon && menuData.icon != "") {
321 return menuData.icon
322 } else if (menuData.action.indexOf("settings") > -1) {
323 return "image://theme/settings"
324 }
325 }
326 return ""
327 }
328 height: units.gu(3)
329 width: height
330 color: theme.palette.normal.backgroundText
331 SlotsLayout.position: SlotsLayout.Trailing
332 }
333 }
334 }
336 Component {
337 id: checkableMenu;
339 Menus.CheckableMenu {
340 id: checkItem
341 objectName: "checkableMenu"
342 property QtObject menuData: null
343 property int menuIndex: -1
344 property bool serverChecked: menuData && menuData.isToggled || false
346 text: menuData && menuData.label || ""
347 enabled: menuData && menuData.sensitive || false
348 checked: serverChecked
349 highlightWhenPressed: false
351 ServerPropertySynchroniser {
352 objectName: "sync"
353 syncTimeout: Utils.Constants.indicatorValueTimeout
355 serverTarget: checkItem
356 serverProperty: "serverChecked"
357 userTarget: checkItem
358 userProperty: "checked"
360 onSyncTriggered: menuModel.activate(checkItem.menuIndex)
361 }
362 }
363 }
365 Component {
366 id: switchMenu;
368 Menus.SwitchMenu {
369 id: switchItem
370 objectName: "switchMenu"
371 property QtObject menuData: null
372 property int menuIndex: -1
373 property bool serverChecked: menuData && menuData.isToggled || false
375 text: menuData && menuData.label || ""
376 iconSource: menuData && menuData.icon || ""
377 enabled: menuData && menuData.sensitive || false
378 checked: serverChecked
379 highlightWhenPressed: false
381 ServerPropertySynchroniser {
382 objectName: "sync"
383 syncTimeout: Utils.Constants.indicatorValueTimeout
385 serverTarget: switchItem
386 serverProperty: "serverChecked"
387 userTarget: switchItem
388 userProperty: "checked"
390 onSyncTriggered: menuModel.activate(switchItem.menuIndex);
391 }
392 }
393 }
395 Component {
396 id: alarmMenu;
398 Menus.EventMenu {
399 id: alarmItem
400 objectName: "alarmMenu"
401 property QtObject menuData: null
402 property var menuModel: menuFactory.menuModel
403 property int menuIndex: -1
404 property var extendedData: menuData && menuData.ext || undefined
406 readonly property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
407 LiveTimer {
408 frequency: LiveTimer.Relative
409 relativeTime: alarmItem.serverTime
410 onTrigger: alarmItem.time = i18n.relativeDateTime(alarmItem.serverTime)
411 }
413 text: menuData && menuData.label || ""
414 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
415 time: i18n.relativeDateTime(serverTime)
416 enabled: menuData && menuData.sensitive || false
417 highlightWhenPressed: false
419 onMenuModelChanged: {
420 loadAttributes();
421 }
422 onMenuIndexChanged: {
423 loadAttributes();
424 }
425 onTriggered: {
426 menuModel.activate(menuIndex);
427 }
429 function loadAttributes() {
430 if (!menuModel || menuIndex == -1) return;
431 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64'});
432 }
433 }
434 }
436 Component {
437 id: appointmentMenu;
439 Menus.EventMenu {
440 id: appointmentItem
441 objectName: "appointmentMenu"
442 property QtObject menuData: null
443 property var menuModel: menuFactory.menuModel
444 property int menuIndex: -1
445 property var extendedData: menuData && menuData.ext || undefined
447 readonly property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
449 LiveTimer {
450 frequency: LiveTimer.Relative
451 relativeTime: appointmentItem.serverTime
452 onTrigger: appointmentItem.time = i18n.relativeDateTime(appointmentItem.serverTime)
453 }
455 text: menuData && menuData.label || ""
456 iconSource: menuData && menuData.icon || "image://theme/calendar"
457 time: i18n.relativeDateTime(serverTime)
458 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
459 enabled: menuData && menuData.sensitive || false
460 highlightWhenPressed: false
462 onMenuModelChanged: {
463 loadAttributes();
464 }
465 onMenuIndexChanged: {
466 loadAttributes();
467 }
468 onTriggered: {
469 menuModel.activate(menuIndex);
470 }
472 function loadAttributes() {
473 if (!menuModel || menuIndex == -1) return;
474 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
475 'x-canonical-time': 'int64'});
476 }
477 }
478 }
480 Component {
481 id: userMenuItem
483 Menus.UserSessionMenu {
484 objectName: "userSessionMenu"
485 highlightWhenPressed: false
487 property QtObject menuData: null
488 property var menuModel: menuFactory.menuModel
489 property int menuIndex: -1
491 name: menuData && menuData.label || "" // label is the user's real name
492 iconSource: menuData && menuData.icon || ""
494 // would be better to compare with the logname but sadly the indicator doesn't expose that
495 active: DBusUnitySessionService.RealName() !== "" ? DBusUnitySessionService.RealName() == name
496 : DBusUnitySessionService.UserName() == name
498 onTriggered: {
499 menuModel.activate(menuIndex);
500 }
501 }
502 }
504 Component {
505 id: calendarMenu
507 Menus.CalendarMenu {
508 objectName: "calendarMenu"
509 highlightWhenPressed: false
510 focus: true
511 }
512 }
514 Component {
515 id: timezoneMenu
517 Menus.TimeZoneMenu {
518 id: tzMenuItem
519 objectName: "timezoneMenu"
521 property QtObject menuData: null
522 property var menuModel: menuFactory.menuModel
523 property int menuIndex: -1
524 property var extendedData: menuData && menuData.ext || undefined
525 readonly property string tz: getExtendedProperty(extendedData, "xCanonicalTimezone", "UTC")
526 property var updateTimer: Timer {
527 repeat: true
528 running: tzMenuItem.visible // only run when we're open
529 onTriggered: tzMenuItem.time = Utils.TimezoneFormatter.currentTimeInTimezone(tzMenuItem.tz)
530 }
532 city: menuData && menuData.label || ""
533 time: Utils.TimezoneFormatter.currentTimeInTimezone(tz)
534 enabled: menuData && menuData.sensitive || false
536 onMenuModelChanged: {
537 loadAttributes();
538 }
539 onMenuIndexChanged: {
540 loadAttributes();
541 }
542 onTriggered: {
543 tzActionGroup.setLocation.activate(tz);
544 }
546 QDBusActionGroup {
547 id: tzActionGroup
548 busType: DBus.SessionBus
549 busName: "com.canonical.indicator.datetime"
550 objectPath: "/com/canonical/indicator/datetime"
552 property variant setLocation: action("set-location")
554 Component.onCompleted: tzActionGroup.start()
555 }
557 function loadAttributes() {
558 if (!menuModel || menuIndex == -1) return;
559 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-timezone': 'string'});
560 }
561 }
562 }
564 Component {
565 id: wifiSection;
567 Menus.SectionMenu {
568 objectName: "wifiSection"
569 property QtObject menuData: null
570 property var menuModel: menuFactory.menuModel
571 property int menuIndex: -1
572 property var extendedData: menuData && menuData.ext || undefined
574 text: menuData && menuData.label || ""
575 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
577 onMenuModelChanged: {
578 loadAttributes();
579 }
580 onMenuIndexChanged: {
581 loadAttributes();
582 }
584 function loadAttributes() {
585 if (!menuModel || menuIndex == -1) return;
586 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
587 }
588 }
589 }
591 Component {
592 id: accessPoint;
594 Menus.AccessPointMenu {
595 id: apItem
596 objectName: "accessPoint"
597 property QtObject menuData: null
598 property var menuModel: menuFactory.menuModel
599 property int menuIndex: -1
600 property var extendedData: menuData && menuData.ext || undefined
601 property bool serverChecked: menuData && menuData.isToggled || false
603 property var strengthAction: UnityMenuAction {
604 model: menuModel
605 index: menuIndex
606 name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
607 }
609 text: menuData && menuData.label || ""
610 enabled: menuData && menuData.sensitive || false
611 active: serverChecked
612 secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
613 adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
614 signalStrength: {
615 if (strengthAction.valid) {
616 var state = strengthAction.state; // handle both int and uchar
617 // FIXME remove the special casing when we switch to indicator-network completely
618 if (typeof state == "string") {
619 return state.charCodeAt();
620 }
621 return state;
622 }
623 return 0;
624 }
625 highlightWhenPressed: false
627 onMenuModelChanged: {
628 loadAttributes();
629 }
630 onMenuIndexChanged: {
631 loadAttributes();
632 }
634 function loadAttributes() {
635 if (!menuModel || menuIndex == -1) return;
636 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
637 'x-canonical-wifi-ap-is-secure': 'bool',
638 'x-canonical-wifi-ap-strength-action': 'string'});
639 }
641 ServerPropertySynchroniser {
642 objectName: "sync"
643 syncTimeout: Utils.Constants.indicatorValueTimeout
645 serverTarget: apItem
646 serverProperty: "serverChecked"
647 userTarget: apItem
648 userProperty: "active"
649 userTrigger: "onTriggered"
651 onSyncTriggered: menuModel.activate(apItem.menuIndex)
652 }
653 }
654 }
656 Component {
657 id: modeminfoitem;
658 Menus.ModemInfoItem {
659 objectName: "modemInfoItem"
660 property QtObject menuData: null
661 property var menuModel: menuFactory.menuModel
662 property int menuIndex: -1
663 property var extendedData: menuData && menuData.ext || undefined
664 highlightWhenPressed: false
666 property var statusLabelAction: UnityMenuAction {
667 model: menuModel
668 index: menuIndex
669 name: getExtendedProperty(extendedData, "xCanonicalModemStatusLabelAction", "")
670 }
671 statusText: statusLabelAction.valid ? statusLabelAction.state : ""
673 property var statusIconAction: UnityMenuAction {
674 model: menuModel
675 index: menuIndex
676 name: getExtendedProperty(extendedData, "xCanonicalModemStatusIconAction", "")
677 }
678 statusIcon: statusIconAction.valid ? statusIconAction.state : ""
680 property var connectivityIconAction: UnityMenuAction {
681 model: menuModel
682 index: menuIndex
683 name: getExtendedProperty(extendedData, "xCanonicalModemConnectivityIconAction", "")
684 }
685 connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
687 property var simIdentifierLabelAction: UnityMenuAction {
688 model: menuModel
689 index: menuIndex
690 name: getExtendedProperty(extendedData, "xCanonicalModemSimIdentifierLabelAction", "")
691 }
692 simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
694 property var roamingAction: UnityMenuAction {
695 model: menuModel
696 index: menuIndex
697 name: getExtendedProperty(extendedData, "xCanonicalModemRoamingAction", "")
698 }
699 roaming: roamingAction.valid ? roamingAction.state : false
701 property var unlockAction: UnityMenuAction {
702 model: menuModel
703 index: menuIndex
704 name: getExtendedProperty(extendedData, "xCanonicalModemLockedAction", "")
705 }
706 onUnlock: {
707 unlockAction.activate();
708 }
709 locked: unlockAction.valid ? unlockAction.state : false
711 onMenuModelChanged: {
712 loadAttributes();
713 }
714 onMenuIndexChanged: {
715 loadAttributes();
716 }
718 function loadAttributes() {
719 if (!menuModel || menuIndex == -1) return;
720 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-modem-status-label-action': 'string',
721 'x-canonical-modem-status-icon-action': 'string',
722 'x-canonical-modem-connectivity-icon-action': 'string',
723 'x-canonical-modem-sim-identifier-label-action': 'string',
724 'x-canonical-modem-roaming-action': 'string',
725 'x-canonical-modem-locked-action': 'string'});
726 }
727 }
728 }
730 Component {
731 id: messageItem
733 MessageMenuItemFactory {
734 objectName: "messageItem"
735 menuModel: menuFactory.menuModel
736 }
737 }
739 Component {
740 id: groupedMessage
742 Menus.GroupedMessageMenu {
743 objectName: "groupedMessage"
744 property QtObject menuData: null
745 property var menuModel: menuFactory.menuModel
746 property int menuIndex: -1
747 property var extendedData: menuData && menuData.ext || undefined
749 text: menuData && menuData.label || ""
750 iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
751 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
752 enabled: menuData && menuData.sensitive || false
753 highlightWhenPressed: false
754 removable: true
756 onMenuModelChanged: {
757 loadAttributes();
758 }
759 onMenuIndexChanged: {
760 loadAttributes();
761 }
762 onClicked: {
763 menuModel.activate(menuIndex, true);
764 }
765 onDismissed: {
766 menuModel.activate(menuIndex, false);
767 }
769 function loadAttributes() {
770 if (!menuModel || menuIndex == -1) return;
771 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
772 }
773 }
774 }
776 Component {
777 id: mediaPayerMenu;
779 Menus.MediaPlayerMenu {
780 objectName: "mediaPayerMenu"
781 property QtObject menuData: null
782 property var menuModel: menuFactory.menuModel
783 property int menuIndex: -1
784 property var actionState: menuData && menuData.actionState || undefined
785 property bool running: getExtendedProperty(actionState, "running", false)
787 playerIcon: menuData && menuData.icon || "image://theme/stock_music"
788 playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
790 albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
791 song: getExtendedProperty(actionState, "title", "")
792 artist: getExtendedProperty(actionState, "artist", "")
793 album: getExtendedProperty(actionState, "album", "")
794 showTrack: running && (state == "Playing" || state == "Paused")
795 state: getExtendedProperty(actionState, "state", "")
796 enabled: menuData && menuData.sensitive || false
797 highlightWhenPressed: false
799 onTriggered: {
800 model.activate(modelIndex);
801 }
802 }
803 }
805 Component {
806 id: playbackItemMenu;
808 Menus.PlaybackItemMenu {
809 objectName: "playbackItemMenu"
810 property QtObject menuData: null
811 property var menuModel: menuFactory.menuModel
812 property int menuIndex: -1
813 property var extendedData: menuData && menuData.ext || undefined
815 property var playAction: UnityMenuAction {
816 model: menuModel
817 index: menuIndex
818 name: getExtendedProperty(extendedData, "xCanonicalPlayAction", "")
819 }
820 property var nextAction: UnityMenuAction {
821 model: menuModel
822 index: menuIndex
823 name: getExtendedProperty(extendedData, "xCanonicalNextAction", "")
824 }
825 property var previousAction: UnityMenuAction {
826 model: menuModel
827 index: menuIndex
828 name: getExtendedProperty(extendedData, "xCanonicalPreviousAction", "")
829 }
831 playing: playAction.state === "Playing"
832 canPlay: playAction.valid
833 canGoNext: nextAction.valid
834 canGoPrevious: previousAction.valid
835 enabled: menuData && menuData.sensitive || false
836 highlightWhenPressed: false
838 onPlay: {
839 playAction.activate();
840 }
841 onNext: {
842 nextAction.activate();
843 }
844 onPrevious: {
845 previousAction.activate();
846 }
847 onMenuModelChanged: {
848 loadAttributes();
849 }
850 onMenuIndexChanged: {
851 loadAttributes();
852 }
854 function loadAttributes() {
855 if (!menuModel || menuIndex == -1) return;
856 menuModel.loadExtendedAttributes(modelIndex, {'x-canonical-play-action': 'string',
857 'x-canonical-next-action': 'string',
858 'x-canonical-previous-action': 'string'});
859 }
860 }
861 }
863 Component {
864 id: transferMenu
866 Menus.TransferMenu {
867 objectName: "transferMenu"
868 id: transfer
869 property QtObject menuData: null
870 property var menuModel: menuFactory.menuModel
871 property int menuIndex: -1
872 property var extendedData: menuData && menuData.ext || undefined
873 property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
875 text: menuData && menuData.label || ""
876 iconSource: menuData && menuData.icon || "image://theme/transfer-none"
877 maximum: 1.0
878 enabled: menuData && menuData.sensitive || false
879 highlightWhenPressed: false
880 removable: true
881 confirmRemoval: true
883 QDBusActionGroup {
884 id: actionGroup
885 busType: 1
886 busName: rootModel.busName
887 objectPath: rootModel.actions["indicator"]
889 property var activateAction: action("activate-transfer")
890 property var cancelAction: action("cancel-transfer")
891 property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
893 Component.onCompleted: actionGroup.start()
894 }
896 property var transferState: {
897 if (actionGroup.transferStateAction === null) return undefined;
898 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
899 }
901 property var runningState : transferState !== undefined ? transferState["state"] : undefined
902 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
904 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
905 progress: transferState !== undefined ? transferState["percent"] : 0.0
907 // TODO - Should be in the SDK
908 property var timeRemaining: {
909 if (secondsLeft === undefined) return undefined;
911 var remaining = "";
912 var hours = Math.floor(secondsLeft / (60 * 60));
913 var minutes = Math.floor(secondsLeft / 60) % 60;
914 var seconds = secondsLeft % 60;
915 if (hours > 0) {
916 remaining += i18n.tr("%1 hour", "%1 hours", hours).arg(hours)
917 }
918 if (minutes > 0) {
919 if (remaining != "") remaining += ", ";
920 remaining += i18n.tr("%1 minute", "%1 minutes", minutes).arg(minutes)
921 }
922 // don't include seconds if hours > 0
923 if (hours == 0 && minutes < 5 && seconds > 0) {
924 if (remaining != "") remaining += ", ";
925 remaining += i18n.tr("%1 second", "%1 seconds", seconds).arg(seconds)
926 }
927 if (remaining == "")
928 remaining = i18n.tr("0 seconds");
929 // Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
930 return i18n.tr("%1 remaining").arg(remaining);
931 }
933 stateText: {
934 switch (runningState) {
935 case Menus.TransferState.Queued:
936 return i18n.tr("In queue…");
937 case Menus.TransferState.Hashing:
938 case Menus.TransferState.Processing:
939 case Menus.TransferState.Running:
940 return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
941 case Menus.TransferState.Paused:
942 return i18n.tr("Paused, tap to resume");
943 case Menus.TransferState.Canceled:
944 return i18n.tr("Canceled");
945 case Menus.TransferState.Finished:
946 return i18n.tr("Finished");
947 case Menus.TransferState.Error:
948 return i18n.tr("Failed, tap to retry");
949 }
950 return "";
951 }
953 onMenuModelChanged: {
954 loadAttributes();
955 }
956 onMenuIndexChanged: {
957 loadAttributes();
958 }
959 onTriggered: {
960 actionGroup.activateAction.activate(uid);
961 }
962 onItemRemoved: {
963 actionGroup.cancelAction.activate(uid);
964 }
966 function loadAttributes() {
967 if (!menuModel || menuIndex == -1) return;
968 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
969 }
970 }
971 }
973 Component {
974 id: buttonSectionMenu;
976 Menus.StandardMenu {
977 objectName: "buttonSectionMenu"
978 property QtObject menuData: null
979 property var menuModel: menuFactory.menuModel
980 property int menuIndex: -1
981 property var extendedData: menuData && menuData.ext || undefined
983 iconSource: menuData && menuData.icon || ""
984 enabled: menuData && menuData.sensitive || false
985 highlightWhenPressed: false
986 text: menuData && menuData.label || ""
987 foregroundColor: theme.palette.normal.backgroundText
989 onMenuModelChanged: {
990 loadAttributes();
991 }
992 onMenuIndexChanged: {
993 loadAttributes();
994 }
995 function loadAttributes() {
996 if (!menuModel || menuIndex == -1) return;
997 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
998 }
1000 slots: Button {
1001 objectName: "buttonSectionMenuControl"
1002 text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
1004 onClicked: {
1005 menuModel.activate(menuIndex);
1011 function load(modelData, context) {
1012 if (context && context.indexOf("fake-") == 0) {
1013 context = context.substring("fake-".length)
1016 var component = getComponentForIndicatorEntryAction(context, modelData.action)
1017 if (component !== undefined) {
1018 return component
1021 component = getComponentForIndicatorEntryType(context, modelData.type)
1022 if (component !== undefined) {
1023 return component;
1026 if (modelData.isCheck || modelData.isRadio) {
1027 return checkableMenu;
1029 if (modelData.isSeparator) {
1030 return separatorMenu;
1032 if (modelData.action !== undefined && modelData.action.indexOf("settings") > -1) {
1033 // FIXME : At the moment, the indicators aren't using
1034 // com.canonical.indicators.link for settings menu. Need to fudge it.
1035 return linkMenu;
1037 return standardMenu;