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