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