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