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  enabled: menuData && menuData.sensitive || false
232  highlightWhenPressed: false
233  }
234  }
235 
236  Component {
237  id: standardMenu;
238 
239  Menus.StandardMenu {
240  objectName: "standardMenu"
241  property QtObject menuData: null
242  property int menuIndex: -1
243 
244  text: menuData && menuData.label || ""
245  iconSource: menuData && menuData.icon || ""
246  enabled: menuData && menuData.sensitive || false
247  highlightWhenPressed: false
248 
249  onTriggered: {
250  menuModel.activate(menuIndex);
251  }
252 
253  // FIXME : At the moment, the indicators aren't using
254  // com.canonical.indicators.link for settings menu. Need to fudge it.
255  property bool settingsMenu: menuData && menuData.action.indexOf("settings") > -1 || false
256  backColor: settingsMenu ? Qt.rgba(1,1,1,0.07) : "transparent"
257  component: settingsMenu ? buttonForSettings : undefined
258  Component {
259  id: buttonForSettings
260  Icon {
261  name: "settings"
262  height: units.gu(3)
263  width: height
264  color: theme.palette.selected.backgroundText
265  }
266  }
267  }
268  }
269 
270  Component {
271  id: linkMenu;
272 
273  Menus.StandardMenu {
274  objectName: "linkMenu"
275  property QtObject menuData: null
276  property int menuIndex: -1
277 
278  text: menuData && menuData.label || ""
279  iconSource: menuData && menuData.icon || ""
280  enabled: menuData && menuData.sensitive || false
281  highlightWhenPressed: false
282 
283  onTriggered: {
284  menuModel.activate(menuIndex);
285  }
286 
287  backColor: Qt.rgba(1,1,1,0.07)
288 
289  component: menuData.icon ? icon : undefined
290  Component {
291  id: icon
292  Icon {
293  source: menuData.icon
294  height: units.gu(3)
295  width: height
296  color: theme.palette.selected.backgroundText
297  }
298  }
299  }
300  }
301 
302  Component {
303  id: checkableMenu;
304 
305  Menus.CheckableMenu {
306  id: checkItem
307  objectName: "checkableMenu"
308  property QtObject menuData: null
309  property int menuIndex: -1
310  property bool serverChecked: menuData && menuData.isToggled || false
311 
312  text: menuData && menuData.label || ""
313  enabled: menuData && menuData.sensitive || false
314  checked: serverChecked
315  highlightWhenPressed: false
316 
317  ServerPropertySynchroniser {
318  objectName: "sync"
319  syncTimeout: Utils.Constants.indicatorValueTimeout
320 
321  serverTarget: checkItem
322  serverProperty: "serverChecked"
323  userTarget: checkItem
324  userProperty: "checked"
325 
326  onSyncTriggered: menuModel.activate(checkItem.menuIndex)
327  }
328  }
329  }
330 
331  Component {
332  id: switchMenu;
333 
334  Menus.SwitchMenu {
335  id: switchItem
336  objectName: "switchMenu"
337  property QtObject menuData: null
338  property int menuIndex: -1
339  property bool serverChecked: menuData && menuData.isToggled || false
340 
341  text: menuData && menuData.label || ""
342  iconSource: menuData && menuData.icon || ""
343  enabled: menuData && menuData.sensitive || false
344  checked: serverChecked
345  highlightWhenPressed: false
346 
347  ServerPropertySynchroniser {
348  objectName: "sync"
349  syncTimeout: Utils.Constants.indicatorValueTimeout
350 
351  serverTarget: switchItem
352  serverProperty: "serverChecked"
353  userTarget: switchItem
354  userProperty: "checked"
355 
356  onSyncTriggered: menuModel.activate(switchItem.menuIndex);
357  }
358  }
359  }
360 
361  Component {
362  id: alarmMenu;
363 
364  Menus.EventMenu {
365  id: alarmItem
366  objectName: "alarmMenu"
367  property QtObject menuData: null
368  property var menuModel: menuFactory.menuModel
369  property int menuIndex: -1
370  property var extendedData: menuData && menuData.ext || undefined
371 
372  property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
373  LiveTimer {
374  frequency: LiveTimer.Relative
375  relativeTime: alarmItem.serverTime
376  onTrigger: alarmItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
377  }
378 
379  text: menuData && menuData.label || ""
380  iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
381  time: i18n.relativeDateTime(serverTime)
382  enabled: menuData && menuData.sensitive || false
383  highlightWhenPressed: false
384 
385  onMenuModelChanged: {
386  loadAttributes();
387  }
388  onMenuIndexChanged: {
389  loadAttributes();
390  }
391  onTriggered: {
392  menuModel.activate(menuIndex);
393  }
394 
395  function loadAttributes() {
396  if (!menuModel || menuIndex == -1) return;
397  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64'});
398  }
399  }
400  }
401 
402  Component {
403  id: appointmentMenu;
404 
405  Menus.EventMenu {
406  id: appointmentItem
407  objectName: "appointmentMenu"
408  property QtObject menuData: null
409  property var menuModel: menuFactory.menuModel
410  property int menuIndex: -1
411  property var extendedData: menuData && menuData.ext || undefined
412 
413  property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
414  LiveTimer {
415  frequency: LiveTimer.Relative
416  relativeTime: appointmentItem.serverTime
417  onTrigger: appointmentItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
418  }
419 
420  text: menuData && menuData.label || ""
421  iconSource: menuData && menuData.icon || "image://theme/calendar"
422  time: i18n.relativeDateTime(serverTime)
423  eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
424  enabled: menuData && menuData.sensitive || false
425  highlightWhenPressed: false
426 
427  onMenuModelChanged: {
428  loadAttributes();
429  }
430  onMenuIndexChanged: {
431  loadAttributes();
432  }
433  onTriggered: {
434  menuModel.activate(menuIndex);
435  }
436 
437  function loadAttributes() {
438  if (!menuModel || menuIndex == -1) return;
439  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
440  'x-canonical-time': 'int64'});
441  }
442  }
443  }
444 
445  Component {
446  id: userMenuItem
447 
448  Menus.UserSessionMenu {
449  objectName: "userSessionMenu"
450  highlightWhenPressed: false
451 
452  property QtObject menuData: null
453  property var menuModel: menuFactory.menuModel
454  property int menuIndex: -1
455 
456  name: menuData && menuData.label || "" // label is the user's real name
457  iconSource: menuData && menuData.icon || ""
458 
459  // would be better to compare with the logname but sadly the indicator doesn't expose that
460  active: DBusUnitySessionService.RealName() !== "" ? DBusUnitySessionService.RealName() == name
461  : DBusUnitySessionService.UserName() == name
462 
463  onTriggered: {
464  menuModel.activate(menuIndex);
465  }
466  }
467  }
468 
469  Component {
470  id: calendarMenu
471 
472  Menus.CalendarMenu {
473  objectName: "calendarMenu"
474  highlightWhenPressed: false
475  focus: true
476  }
477  }
478 
479  Component {
480  id: timezoneMenu
481 
482  Menus.TimeZoneMenu {
483  id: tzMenuItem
484  objectName: "timezoneMenu"
485 
486  property QtObject menuData: null
487  property var menuModel: menuFactory.menuModel
488  property int menuIndex: -1
489  property var extendedData: menuData && menuData.ext || undefined
490  readonly property string tz: getExtendedProperty(extendedData, "xCanonicalTimezone", "UTC")
491  property var updateTimer: Timer {
492  repeat: true
493  running: tzMenuItem.visible // only run when we're open
494  onTriggered: tzMenuItem.time = Utils.TimezoneFormatter.currentTimeInTimezone(tzMenuItem.tz)
495  }
496 
497  city: menuData && menuData.label || ""
498  time: Utils.TimezoneFormatter.currentTimeInTimezone(tz)
499  enabled: menuData && menuData.sensitive || false
500 
501  onMenuModelChanged: {
502  loadAttributes();
503  }
504  onMenuIndexChanged: {
505  loadAttributes();
506  }
507  onTriggered: {
508  tzActionGroup.setLocation.activate(tz);
509  }
510 
511  QDBusActionGroup {
512  id: tzActionGroup
513  busType: DBus.SessionBus
514  busName: "com.canonical.indicator.datetime"
515  objectPath: "/com/canonical/indicator/datetime"
516 
517  property variant setLocation: action("set-location")
518 
519  Component.onCompleted: tzActionGroup.start()
520  }
521 
522  function loadAttributes() {
523  if (!menuModel || menuIndex == -1) return;
524  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-timezone': 'string'});
525  }
526  }
527  }
528 
529  Component {
530  id: wifiSection;
531 
532  Menus.SectionMenu {
533  objectName: "wifiSection"
534  property QtObject menuData: null
535  property var menuModel: menuFactory.menuModel
536  property int menuIndex: -1
537  property var extendedData: menuData && menuData.ext || undefined
538 
539  text: menuData && menuData.label || ""
540  busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
541 
542  onMenuModelChanged: {
543  loadAttributes();
544  }
545  onMenuIndexChanged: {
546  loadAttributes();
547  }
548 
549  function loadAttributes() {
550  if (!menuModel || menuIndex == -1) return;
551  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
552  }
553  }
554  }
555 
556  Component {
557  id: accessPoint;
558 
559  Menus.AccessPointMenu {
560  id: apItem
561  objectName: "accessPoint"
562  property QtObject menuData: null
563  property var menuModel: menuFactory.menuModel
564  property int menuIndex: -1
565  property var extendedData: menuData && menuData.ext || undefined
566  property bool serverChecked: menuData && menuData.isToggled || false
567 
568  property var strengthAction: UnityMenuAction {
569  model: menuModel
570  index: menuIndex
571  name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
572  }
573 
574  text: menuData && menuData.label || ""
575  enabled: menuData && menuData.sensitive || false
576  active: serverChecked
577  secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
578  adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
579  signalStrength: {
580  if (strengthAction.valid) {
581  var state = strengthAction.state; // handle both int and uchar
582  // FIXME remove the special casing when we switch to indicator-network completely
583  if (typeof state == "string") {
584  return state.charCodeAt();
585  }
586  return state;
587  }
588  return 0;
589  }
590  highlightWhenPressed: false
591 
592  onMenuModelChanged: {
593  loadAttributes();
594  }
595  onMenuIndexChanged: {
596  loadAttributes();
597  }
598 
599  function loadAttributes() {
600  if (!menuModel || menuIndex == -1) return;
601  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
602  'x-canonical-wifi-ap-is-secure': 'bool',
603  'x-canonical-wifi-ap-strength-action': 'string'});
604  }
605 
606  ServerPropertySynchroniser {
607  objectName: "sync"
608  syncTimeout: Utils.Constants.indicatorValueTimeout
609 
610  serverTarget: apItem
611  serverProperty: "serverChecked"
612  userTarget: apItem
613  userProperty: "active"
614  userTrigger: "onTriggered"
615 
616  onSyncTriggered: menuModel.activate(apItem.menuIndex)
617  }
618  }
619  }
620 
621  Component {
622  id: modeminfoitem;
623  ModemInfoItem {
624  objectName: "modemInfoItem"
625  property QtObject menuData: null
626  property var menuModel: menuFactory.menuModel
627  property int menuIndex: -1
628  property var extendedData: menuData && menuData.ext || undefined
629  highlightWhenPressed: false
630 
631  property var statusLabelAction: UnityMenuAction {
632  model: menuModel
633  index: menuIndex
634  name: getExtendedProperty(extendedData, "xCanonicalModemStatusLabelAction", "")
635  }
636  statusText: statusLabelAction.valid ? statusLabelAction.state : ""
637 
638  property var statusIconAction: UnityMenuAction {
639  model: menuModel
640  index: menuIndex
641  name: getExtendedProperty(extendedData, "xCanonicalModemStatusIconAction", "")
642  }
643  statusIcon: statusIconAction.valid ? statusIconAction.state : ""
644 
645  property var connectivityIconAction: UnityMenuAction {
646  model: menuModel
647  index: menuIndex
648  name: getExtendedProperty(extendedData, "xCanonicalModemConnectivityIconAction", "")
649  }
650  connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
651 
652  property var simIdentifierLabelAction: UnityMenuAction {
653  model: menuModel
654  index: menuIndex
655  name: getExtendedProperty(extendedData, "xCanonicalModemSimIdentifierLabelAction", "")
656  }
657  simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
658 
659  property var roamingAction: UnityMenuAction {
660  model: menuModel
661  index: menuIndex
662  name: getExtendedProperty(extendedData, "xCanonicalModemRoamingAction", "")
663  }
664  roaming: roamingAction.valid ? roamingAction.state : false
665 
666  property var unlockAction: UnityMenuAction {
667  model: menuModel
668  index: menuIndex
669  name: getExtendedProperty(extendedData, "xCanonicalModemLockedAction", "")
670  }
671  onUnlock: {
672  unlockAction.activate();
673  }
674  locked: unlockAction.valid ? unlockAction.state : false
675 
676  onMenuModelChanged: {
677  loadAttributes();
678  }
679  onMenuIndexChanged: {
680  loadAttributes();
681  }
682 
683  function loadAttributes() {
684  if (!menuModel || menuIndex == -1) return;
685  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-modem-status-label-action': 'string',
686  'x-canonical-modem-status-icon-action': 'string',
687  'x-canonical-modem-connectivity-icon-action': 'string',
688  'x-canonical-modem-sim-identifier-label-action': 'string',
689  'x-canonical-modem-roaming-action': 'string',
690  'x-canonical-modem-locked-action': 'string'});
691  }
692  }
693  }
694 
695  Component {
696  id: messageItem
697 
698  MessageMenuItemFactory {
699  objectName: "messageItem"
700  menuModel: menuFactory.menuModel
701  }
702  }
703 
704  Component {
705  id: groupedMessage
706 
707  Menus.GroupedMessageMenu {
708  objectName: "groupedMessage"
709  property QtObject menuData: null
710  property var menuModel: menuFactory.menuModel
711  property int menuIndex: -1
712  property var extendedData: menuData && menuData.ext || undefined
713 
714  text: menuData && menuData.label || ""
715  iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
716  count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
717  enabled: menuData && menuData.sensitive || false
718  highlightWhenPressed: false
719  removable: true
720 
721  onMenuModelChanged: {
722  loadAttributes();
723  }
724  onMenuIndexChanged: {
725  loadAttributes();
726  }
727  onClicked: {
728  menuModel.activate(menuIndex, true);
729  }
730  onDismissed: {
731  menuModel.activate(menuIndex, false);
732  }
733 
734  function loadAttributes() {
735  if (!menuModel || menuIndex == -1) return;
736  menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
737  }
738  }
739  }
740 
741  Component {
742  id: mediaPayerMenu;
743 
744  Menus.MediaPlayerMenu {
745  objectName: "mediaPayerMenu"
746  property QtObject menuData: null
747  property var menuModel: menuFactory.menuModel
748  property int menuIndex: -1
749  property var actionState: menuData && menuData.actionState || undefined
750  property bool running: getExtendedProperty(actionState, "running", false)
751 
752  playerIcon: menuData && menuData.icon || "image://theme/stock_music"
753  playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
754 
755  albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
756  song: getExtendedProperty(actionState, "title", "")
757  artist: getExtendedProperty(actionState, "artist", "")
758  album: getExtendedProperty(actionState, "album", "")
759  showTrack: running && (state == "Playing" || state == "Paused")
760  state: getExtendedProperty(actionState, "state", "")
761  enabled: menuData && menuData.sensitive || false
762  highlightWhenPressed: false
763  showDivider: false
764 
765  onTriggered: {
766  model.activate(modelIndex);
767  }
768  }
769  }
770 
771  Component {
772  id: playbackItemMenu;
773 
774  Menus.PlaybackItemMenu {
775  objectName: "playbackItemMenu"
776  property QtObject menuData: null
777  property var menuModel: menuFactory.menuModel
778  property int menuIndex: -1
779  property var extendedData: menuData && menuData.ext || undefined
780 
781  property var playAction: UnityMenuAction {
782  model: menuModel
783  index: menuIndex
784  name: getExtendedProperty(extendedData, "xCanonicalPlayAction", "")
785  }
786  property var nextAction: UnityMenuAction {
787  model: menuModel
788  index: menuIndex
789  name: getExtendedProperty(extendedData, "xCanonicalNextAction", "")
790  }
791  property var previousAction: UnityMenuAction {
792  model: menuModel
793  index: menuIndex
794  name: getExtendedProperty(extendedData, "xCanonicalPreviousAction", "")
795  }
796 
797  playing: playAction.state === "Playing"
798  canPlay: playAction.valid
799  canGoNext: nextAction.valid
800  canGoPrevious: previousAction.valid
801  enabled: menuData && menuData.sensitive || false
802  highlightWhenPressed: false
803 
804  onPlay: {
805  playAction.activate();
806  }
807  onNext: {
808  nextAction.activate();
809  }
810  onPrevious: {
811  previousAction.activate();
812  }
813  onMenuModelChanged: {
814  loadAttributes();
815  }
816  onMenuIndexChanged: {
817  loadAttributes();
818  }
819 
820  function loadAttributes() {
821  if (!menuModel || menuIndex == -1) return;
822  menuModel.loadExtendedAttributes(modelIndex, {'x-canonical-play-action': 'string',
823  'x-canonical-next-action': 'string',
824  'x-canonical-previous-action': 'string'});
825  }
826  }
827  }
828 
829  Component {
830  id: transferMenu
831 
832  Menus.TransferMenu {
833  objectName: "transferMenu"
834  id: transfer
835  property QtObject menuData: null
836  property var menuModel: menuFactory.menuModel
837  property int menuIndex: -1
838  property var extendedData: menuData && menuData.ext || undefined
839  property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
840 
841  text: menuData && menuData.label || ""
842  iconSource: menuData && menuData.icon || "image://theme/transfer-none"
843  maximum: 1.0
844  enabled: menuData && menuData.sensitive || false
845  highlightWhenPressed: false
846  removable: true
847  confirmRemoval: true
848 
849  QDBusActionGroup {
850  id: actionGroup
851  busType: 1
852  busName: rootModel.busName
853  objectPath: rootModel.actions["indicator"]
854 
855  property var activateAction: action("activate-transfer")
856  property var cancelAction: action("cancel-transfer")
857  property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
858 
859  Component.onCompleted: actionGroup.start()
860  }
861 
862  property var transferState: {
863  if (actionGroup.transferStateAction === null) return undefined;
864  return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
865  }
866 
867  property var runningState : transferState !== undefined ? transferState["state"] : undefined
868  property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
869 
870  active: runningState !== undefined && runningState !== Menus.TransferState.Finished
871  progress: transferState !== undefined ? transferState["percent"] : 0.0
872 
873  // TODO - Should be in the SDK
874  property var timeRemaining: {
875  if (secondsLeft === undefined) return undefined;
876 
877  var remaining = "";
878  var hours = Math.floor(secondsLeft / (60 * 60));
879  var minutes = Math.floor(secondsLeft / 60) % 60;
880  var seconds = secondsLeft % 60;
881  if (hours > 0) {
882  remaining += i18n.tr("%1 hour", "%1 hours", hours).arg(hours)
883  }
884  if (minutes > 0) {
885  if (remaining != "") remaining += ", ";
886  remaining += i18n.tr("%1 minute", "%1 minutes", minutes).arg(minutes)
887  }
888  // don't include seconds if hours > 0
889  if (hours == 0 && minutes < 5 && seconds > 0) {
890  if (remaining != "") remaining += ", ";
891  remaining += i18n.tr("%1 second", "%1 seconds", seconds).arg(seconds)
892  }
893  if (remaining == "")
894  remaining = i18n.tr("0 seconds");
895  // Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
896  return i18n.tr("%1 remaining").arg(remaining);
897  }
898 
899  stateText: {
900  switch (runningState) {
901  case Menus.TransferState.Queued:
902  return i18n.tr("In queue…");
903  case Menus.TransferState.Hashing:
904  case Menus.TransferState.Processing:
905  case Menus.TransferState.Running:
906  return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
907  case Menus.TransferState.Paused:
908  return i18n.tr("Paused, tap to resume");
909  case Menus.TransferState.Canceled:
910  return i18n.tr("Canceled");
911  case Menus.TransferState.Finished:
912  return i18n.tr("Finished");
913  case Menus.TransferState.Error:
914  return i18n.tr("Failed, tap to retry");
915  }
916  return "";
917  }
918 
919  onMenuModelChanged: {
920  loadAttributes();
921  }
922  onMenuIndexChanged: {
923  loadAttributes();
924  }
925  onTriggered: {
926  actionGroup.activateAction.activate(uid);
927  }
928  onItemRemoved: {
929  actionGroup.cancelAction.activate(uid);
930  }
931 
932  function loadAttributes() {
933  if (!menuModel || menuIndex == -1) return;
934  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
935  }
936  }
937  }
938 
939  Component {
940  id: buttonSectionMenu;
941 
942  Menus.StandardMenu {
943  objectName: "buttonSectionMenu"
944  property QtObject menuData: null
945  property var menuModel: menuFactory.menuModel
946  property int menuIndex: -1
947  property var extendedData: menuData && menuData.ext || undefined
948 
949  iconSource: menuData && menuData.icon || ""
950  enabled: menuData && menuData.sensitive || false
951  highlightWhenPressed: false
952  text: menuData && menuData.label || ""
953  foregroundColor: theme.palette.normal.backgroundText
954 
955  onMenuModelChanged: {
956  loadAttributes();
957  }
958  onMenuIndexChanged: {
959  loadAttributes();
960  }
961  function loadAttributes() {
962  if (!menuModel || menuIndex == -1) return;
963  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
964  }
965 
966  component: Component {
967  Button {
968  objectName: "buttonSectionMenuControl"
969  text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
970 
971  onClicked: {
972  menuModel.activate(menuIndex);
973  }
974  }
975  }
976  }
977  }
978 
979  function load(modelData, context) {
980  if (modelData.type !== undefined && modelData.type !== "") {
981  var component = undefined;
982 
983  var contextComponents = _map[context];
984  if (contextComponents !== undefined) {
985  component = contextComponents[modelData.type];
986  }
987 
988  if (component === undefined) {
989  component = _map["default"][modelData.type];
990  }
991  if (component !== undefined) {
992  return component;
993  }
994  console.debug("Don't know how to make " + modelData.type + " for " + context);
995  }
996  if (modelData.isCheck || modelData.isRadio) {
997  return checkableMenu;
998  }
999  if (modelData.isSeparator) {
1000  return separatorMenu;
1001  }
1002  return standardMenu;
1003  }
1004 }