Unity 8
 All Classes Functions
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 as SettingsComponents
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 0.1
27 
28 Item {
29  id: menuFactory
30 
31  property var rootModel: null
32  property var menuModel: null
33 
34  property var _map: {
35  "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
36  "unity.widgets.systemsettings.tablet.switch" : switchMenu,
37 
38  "com.canonical.indicator.button" : buttonMenu,
39  "com.canonical.indicator.div" : separatorMenu,
40  "com.canonical.indicator.section" : sectionMenu,
41  "com.canonical.indicator.progress" : progressMenu,
42  "com.canonical.indicator.slider" : sliderMenu,
43  "com.canonical.indicator.switch" : switchMenu,
44  "com.canonical.indicator.alarm" : alarmMenu,
45  "com.canonical.indicator.appointment" : appointmentMenu,
46  "com.canonical.indicator.transfer" : transferMenu,
47  "com.canonical.indicator.button-section" : buttonSectionMenu,
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 
63  function getExtendedProperty(object, propertyName, defaultValue) {
64  if (object && object.hasOwnProperty(propertyName)) {
65  return object[propertyName];
66  }
67  return defaultValue;
68  }
69 
70  Component {
71  id: separatorMenu;
72 
73  Menus.SeparatorMenu {
74  objectName: "separatorMenu"
75  }
76  }
77 
78  Component {
79  id: sliderMenu;
80 
81  Menus.SliderMenu {
82  objectName: "sliderMenu"
83  property QtObject menuData: null
84  property var menuModel: menuFactory.menuModel
85  property int menuIndex: -1
86  property var extendedData: menuData && menuData.ext || undefined
87  property var serverValue: getExtendedProperty(menuData, "actionState", undefined)
88 
89  text: menuData && menuData.label || ""
90  iconSource: menuData && menuData.icon || ""
91  minIcon: getExtendedProperty(extendedData, "minIcon", "")
92  maxIcon: getExtendedProperty(extendedData, "maxIcon", "")
93 
94  minimumValue: getExtendedProperty(extendedData, "minValue", 0.0)
95  maximumValue: {
96  var maximum = getExtendedProperty(extendedData, "maxValue", 1.0);
97  if (maximum <= minimumValue) {
98  return minimumValue + 1;
99  }
100  return maximum;
101  }
102  enabled: menuData && menuData.sensitive || false
103 
104  onMenuModelChanged: {
105  loadAttributes();
106  }
107  onMenuIndexChanged: {
108  loadAttributes();
109  }
110  onServerValueChanged: {
111  // value can be changed by slider, so a binding won't work.
112  if (serverValue !== undefined) {
113  value = serverValue;
114  }
115  }
116  onUpdated: {
117  menuModel.changeState(menuIndex, value);
118  }
119 
120  function loadAttributes() {
121  if (!menuModel || menuIndex == -1) return;
122  menuModel.loadExtendedAttributes(menuIndex, {'min-value': 'double',
123  'max-value': 'double',
124  'min-icon': 'icon',
125  'max-icon': 'icon'});
126  }
127  }
128  }
129 
130  Component {
131  id: buttonMenu;
132 
133  Menus.ButtonMenu {
134  objectName: "buttonMenu"
135  property QtObject menuData: null
136  property var menuModel: menuFactory.menuModel
137  property int menuIndex: -1
138 
139  buttonText: menuData && menuData.label || ""
140  enabled: menuData && menuData.sensitive || false
141 
142  onTriggered: {
143  menuModel.activate(menuIndex);
144  }
145  }
146  }
147  Component {
148  id: sectionMenu;
149 
150  Menus.SectionMenu {
151  objectName: "sectionMenu"
152  property QtObject menuData: null
153  property var menuIndex: undefined
154 
155  text: menuData && menuData.label || ""
156  busy: false
157  }
158  }
159 
160  Component {
161  id: progressMenu;
162 
163  Menus.ProgressValueMenu {
164  objectName: "progressMenu"
165  property QtObject menuData: null
166  property int menuIndex: -1
167 
168  text: menuData && menuData.label || ""
169  iconSource: menuData && menuData.icon || ""
170  value : menuData && menuData.actionState || 0.0
171  enabled: menuData && menuData.sensitive || false
172  }
173  }
174 
175  Component {
176  id: standardMenu;
177 
178  Menus.StandardMenu {
179  objectName: "standardMenu"
180  property QtObject menuData: null
181  property int menuIndex: -1
182 
183  text: menuData && menuData.label || ""
184  iconSource: menuData && menuData.icon || ""
185  enabled: menuData && menuData.sensitive || false
186 
187  onTriggered: {
188  menuModel.activate(menuIndex);
189  }
190  }
191  }
192 
193  Component {
194  id: checkableMenu;
195 
196  Menus.CheckableMenu {
197  objectName: "checkableMenu"
198  property QtObject menuData: null
199  property int menuIndex: -1
200 
201  text: menuData && menuData.label || ""
202  enabled: menuData && menuData.sensitive || false
203  checked: menuData && menuData.isToggled || false
204 
205  onTriggered: {
206  menuModel.activate(menuIndex);
207  }
208  }
209  }
210 
211 
212  Component {
213  id: switchMenu;
214 
215  Menus.SwitchMenu {
216  objectName: "switchMenu"
217  property QtObject menuData: null
218  property int menuIndex: -1
219 
220  text: menuData && menuData.label || ""
221  iconSource: menuData && menuData.icon || ""
222  enabled: menuData && menuData.sensitive || false
223  checked: menuData && menuData.isToggled || false
224 
225  onTriggered: {
226  menuModel.activate(menuIndex);
227  }
228  }
229  }
230 
231  Component {
232  id: alarmMenu;
233 
234  Menus.EventMenu {
235  objectName: "alarmMenu"
236  property QtObject menuData: null
237  property var menuModel: menuFactory.menuModel
238  property int menuIndex: -1
239  property var extendedData: menuData && menuData.ext || undefined
240  // TODO - bug #1260728
241  property var timeFormatter: Utils.GDateTimeFormatter {
242  time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
243  format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
244  }
245 
246  text: menuData && menuData.label || ""
247  iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
248  time: timeFormatter.timeString
249  enabled: menuData && menuData.sensitive || false
250 
251  onMenuModelChanged: {
252  loadAttributes();
253  }
254  onMenuIndexChanged: {
255  loadAttributes();
256  }
257  onTriggered: {
258  menuModel.activate(menuIndex);
259  }
260 
261  function loadAttributes() {
262  if (!menuModel || menuIndex == -1) return;
263  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
264  'x-canonical-time-format': 'string'});
265  }
266  }
267  }
268 
269  Component {
270  id: appointmentMenu;
271 
272  Menus.EventMenu {
273  objectName: "appointmentMenu"
274  property QtObject menuData: null
275  property var menuModel: menuFactory.menuModel
276  property int menuIndex: -1
277  property var extendedData: menuData && menuData.ext || undefined
278  // TODO - bug #1260728
279  property var timeFormatter: Utils.GDateTimeFormatter {
280  time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
281  format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
282  }
283 
284  text: menuData && menuData.label || ""
285  iconSource: menuData && menuData.icon || "image://theme/calendar"
286  time: timeFormatter.timeString
287  eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
288  enabled: menuData && menuData.sensitive || false
289 
290  onMenuModelChanged: {
291  loadAttributes();
292  }
293  onMenuIndexChanged: {
294  loadAttributes();
295  }
296  onTriggered: {
297  menuModel.activate(menuIndex);
298  }
299 
300  function loadAttributes() {
301  if (!menuModel || menuIndex == -1) return;
302  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
303  'x-canonical-time': 'int64',
304  'x-canonical-time-format': 'string'});
305  }
306  }
307  }
308 
309  Component {
310  id: wifiSection;
311 
312  Menus.SectionMenu {
313  objectName: "wifiSection"
314  property QtObject menuData: null
315  property var menuModel: menuFactory.menuModel
316  property int menuIndex: -1
317  property var extendedData: menuData && menuData.ext || undefined
318 
319  text: menuData && menuData.label || ""
320  busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
321 
322  onMenuModelChanged: {
323  loadAttributes();
324  }
325  onMenuIndexChanged: {
326  loadAttributes();
327  }
328 
329  function loadAttributes() {
330  if (!menuModel || menuIndex == -1) return;
331  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
332  }
333  }
334  }
335 
336  Component {
337  id: accessPoint;
338 
339  Menus.AccessPointMenu {
340  objectName: "accessPoint"
341  property QtObject menuData: null
342  property var menuModel: menuFactory.menuModel
343  property int menuIndex: -1
344  property var extendedData: menuData && menuData.ext || undefined
345 
346  property var strengthAction: UnityMenuAction {
347  model: menuModel
348  index: menuIndex
349  name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
350  }
351 
352  text: menuData && menuData.label || ""
353  enabled: menuData && menuData.sensitive || false
354  checked: menuData && menuData.isToggled || false
355  secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
356  adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
357  signalStrength: strengthAction.valid ? strengthAction.state : 0
358 
359  onMenuModelChanged: {
360  loadAttributes();
361  }
362  onMenuIndexChanged: {
363  loadAttributes();
364  }
365  onTriggered: {
366  menuModel.activate(menuIndex);
367  }
368 
369  function loadAttributes() {
370  if (!menuModel || menuIndex == -1) return;
371  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
372  'x-canonical-wifi-ap-is-secure': 'bool',
373  'x-canonical-wifi-ap-strength-action': 'string'});
374  }
375  }
376  }
377 
378  Component {
379  id: modeminfoitem;
380  ModemInfoItem {
381  objectName: "modemInfoItem"
382  property QtObject menuData: null
383  property var menuModel: menuFactory.menuModel
384  property int menuIndex: -1
385  property var extendedData: menuData && menuData.ext || undefined
386 
387  property var statusLabelAction: UnityMenuAction {
388  model: menuModel
389  index: menuIndex
390  name: getExtendedProperty(extendedData, "xCanonicalModemStatusLabelAction", "")
391  }
392  statusText: statusLabelAction.valid ? statusLabelAction.state : ""
393 
394  property var statusIconAction: UnityMenuAction {
395  model: menuModel
396  index: menuIndex
397  name: getExtendedProperty(extendedData, "xCanonicalModemStatusIconAction", "")
398  }
399  statusIcon: statusIconAction.valid ? statusIconAction.state : ""
400 
401  property var connectivityIconAction: UnityMenuAction {
402  model: menuModel
403  index: menuIndex
404  name: getExtendedProperty(extendedData, "xCanonicalModemConnectivityIconAction", "")
405  }
406  connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
407 
408  property var simIdentifierLabelAction: UnityMenuAction {
409  model: menuModel
410  index: menuIndex
411  name: getExtendedProperty(extendedData, "xCanonicalModemSimIdentifierLabelAction", "")
412  }
413  simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
414 
415  property var roamingAction: UnityMenuAction {
416  model: menuModel
417  index: menuIndex
418  name: getExtendedProperty(extendedData, "xCanonicalModemRoamingAction", "")
419  }
420  roaming: roamingAction.valid ? roamingAction.state : false
421 
422  property var unlockAction: UnityMenuAction {
423  model: menuModel
424  index: menuIndex
425  name: getExtendedProperty(extendedData, "xCanonicalModemLockedAction", "")
426  }
427  onUnlock: {
428  unlockAction.activate();
429  }
430  locked: unlockAction.valid ? unlockAction.state : false
431 
432  onMenuModelChanged: {
433  loadAttributes();
434  }
435  onMenuIndexChanged: {
436  loadAttributes();
437  }
438 
439  function loadAttributes() {
440  if (!menuModel || menuIndex == -1) return;
441  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-modem-status-label-action': 'string',
442  'x-canonical-modem-status-icon-action': 'string',
443  'x-canonical-modem-connectivity-icon-action': 'string',
444  'x-canonical-modem-sim-identifier-label-action': 'string',
445  'x-canonical-modem-roaming-action': 'string',
446  'x-canonical-modem-locked-action': 'string'});
447  }
448  }
449  }
450 
451  Component {
452  id: messageItem
453 
454  MessageMenuItemFactory {
455  objectName: "messageItem"
456  menuModel: menuFactory.menuModel
457  }
458  }
459 
460  Component {
461  id: groupedMessage
462 
463  Menus.GroupedMessageMenu {
464  objectName: "groupedMessage"
465  property QtObject menuData: null
466  property var menuModel: menuFactory.menuModel
467  property int menuIndex: -1
468  property var extendedData: menuData && menuData.ext || undefined
469 
470  text: menuData && menuData.label || ""
471  iconSource: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_app.svg")
472  count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
473  enabled: menuData && menuData.sensitive || false
474  removable: true
475 
476  onMenuModelChanged: {
477  loadAttributes();
478  }
479  onMenuIndexChanged: {
480  loadAttributes();
481  }
482  onClicked: {
483  menuModel.activate(menuIndex, true);
484  }
485  onDismissed: {
486  menuModel.activate(menuIndex, false);
487  }
488 
489  function loadAttributes() {
490  if (!menuModel || menuIndex == -1) return;
491  menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
492  }
493  }
494  }
495 
496  Component {
497  id: mediaPayerMenu;
498 
499  Menus.MediaPlayerMenu {
500  objectName: "mediaPayerMenu"
501  property QtObject menuData: null
502  property var menuModel: menuFactory.menuModel
503  property int menuIndex: -1
504  property var actionState: menuData && menuData.actionState || undefined
505 
506  playerIcon: menuData && menuData.icon || ""
507  playerName: menuData && menuData.label || ""
508 
509  albumArt: getExtendedProperty(actionState, "art-url", "")
510  song: getExtendedProperty(actionState, "title", "unknown")
511  artist: getExtendedProperty(actionState, "artist", "unknown")
512  album: getExtendedProperty(actionState, "album", "unknown")
513  running: getExtendedProperty(actionState, "running", false)
514  state: getExtendedProperty(actionState, "state", "")
515  enabled: menuData && menuData.sensitive || false
516 
517  onTriggered: {
518  model.activate(modelIndex);
519  }
520  }
521  }
522 
523  Component {
524  id: playbackItemMenu;
525 
526  Menus.PlaybackItemMenu {
527  objectName: "playbackItemMenu"
528  property QtObject menuData: null
529  property var menuModel: menuFactory.menuModel
530  property int menuIndex: -1
531  property var extendedData: menuData && menuData.ext || undefined
532 
533  property var playAction: UnityMenuAction {
534  model: menuModel
535  index: menuIndex
536  name: getExtendedProperty(extendedData, "xCanonicalPlayAction", "")
537  }
538  property var nextAction: UnityMenuAction {
539  model: menuModel
540  index: menuIndex
541  name: getExtendedProperty(extendedData, "xCanonicalNextAction", "")
542  }
543  property var previousAction: UnityMenuAction {
544  model: menuModel
545  index: menuIndex
546  name: getExtendedProperty(extendedData, "xCanonicalPreviousAction", "")
547  }
548 
549  playing: playAction.state === "Playing"
550  canPlay: playAction.valid
551  canGoNext: nextAction.valid
552  canGoPrevious: previousAction.valid
553  enabled: menuData && menuData.sensitive || false
554 
555  onPlay: {
556  playAction.activate();
557  }
558  onNext: {
559  nextAction.activate();
560  }
561  onPrevious: {
562  previousAction.activate();
563  }
564  onMenuModelChanged: {
565  loadAttributes();
566  }
567  onMenuIndexChanged: {
568  loadAttributes();
569  }
570 
571  function loadAttributes() {
572  if (!menuModel || menuIndex == -1) return;
573  menuModel.loadExtendedAttributes(modelIndex, {'x-canonical-play-action': 'string',
574  'x-canonical-next-action': 'string',
575  'x-canonical-previous-action': 'string'});
576  }
577  }
578  }
579 
580  Component {
581  id: transferMenu
582 
583  Menus.TransferMenu {
584  objectName: "transferMenu"
585  id: transfer
586  property QtObject menuData: null
587  property var menuModel: menuFactory.menuModel
588  property int menuIndex: -1
589  property var extendedData: menuData && menuData.ext || undefined
590  property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
591 
592  text: menuData && menuData.label || ""
593  iconSource: menuData && menuData.icon || ""
594  maximum: 1.0
595  enabled: menuData && menuData.sensitive || false
596  removable: true
597  confirmRemoval: true
598 
599  QDBusActionGroup {
600  id: actionGroup
601  busType: 1
602  busName: rootModel.busName
603  objectPath: rootModel.actions["indicator"]
604 
605  property var activateAction: action("activate-transfer")
606  property var cancelAction: action("cancel-transfer")
607  property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
608 
609  Component.onCompleted: actionGroup.start()
610  }
611 
612  property var transferState: {
613  if (actionGroup.transferStateAction === null) return undefined;
614  return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
615  }
616 
617  property var runningState : transferState !== undefined ? transferState["state"] : undefined
618  property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
619 
620  active: runningState !== undefined && runningState !== Menus.TransferState.Finished
621  progress: transferState !== undefined ? transferState["percent"] : 0.0
622 
623  // TODO - Should be in the SDK
624  property var timeRemaining: {
625  if (secondsLeft === undefined) return undefined;
626 
627  var remaining = "";
628  var hours = Math.floor(secondsLeft / (60 * 60));
629  var minutes = Math.floor(secondsLeft / 60) % 60;
630  var seconds = secondsLeft % 60;
631  if (hours > 0) {
632  remaining += hours + (hours == 1 ? " hour" : " hours");
633  }
634  if (minutes > 0) {
635  if (remaining != "") remaining += ", ";
636  remaining += minutes + (minutes == 1 ? " minute" : " minutes");
637  }
638  // don't include seconds if hours > 0
639  if (hours == 0 && minutes < 5 && seconds > 0) {
640  if (remaining != "") remaining += ", ";
641  remaining += seconds + (seconds == 1 ? " second" : " seconds");
642  }
643  if (remaining == "")
644  remaining = "0 seconds";
645  return remaining + " remaining";
646  }
647 
648  stateText: {
649  switch (runningState) {
650  case Menus.TransferState.Queued:
651  return i18n.tr("In queue…");
652  case Menus.TransferState.Hashing:
653  case Menus.TransferState.Processing:
654  case Menus.TransferState.Running:
655  return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
656  case Menus.TransferState.Paused:
657  return i18n.tr("Paused, tap to resume");
658  case Menus.TransferState.Canceled:
659  return i18n.tr("Canceled");
660  case Menus.TransferState.Finished:
661  return i18n.tr("Finished");
662  case Menus.TransferState.Error:
663  return i18n.tr("Failed, tap to retry");
664  }
665  return "";
666  }
667 
668  onMenuModelChanged: {
669  loadAttributes();
670  }
671  onMenuIndexChanged: {
672  loadAttributes();
673  }
674  onTriggered: {
675  actionGroup.activateAction.activate(uid);
676  }
677  onItemRemoved: {
678  actionGroup.cancelAction.activate(uid);
679  }
680 
681  function loadAttributes() {
682  if (!menuModel || menuIndex == -1) return;
683  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
684  }
685  }
686  }
687 
688  Component {
689  id: buttonSectionMenu;
690 
691  ListItems.Standard {
692  objectName: "buttonSectionMenu"
693  property QtObject menuData: null
694  property var menuModel: menuFactory.menuModel
695  property int menuIndex: -1
696  property var extendedData: menuData && menuData.ext || undefined
697 
698  iconSource: menuData && menuData.icon || ""
699  enabled: menuData && menuData.sensitive || false
700  text: menuData && menuData.label || ""
701  showDivider: false
702 
703  onMenuModelChanged: {
704  loadAttributes();
705  }
706  onMenuIndexChanged: {
707  loadAttributes();
708  }
709  function loadAttributes() {
710  if (!menuModel || menuIndex == -1) return;
711  menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
712  }
713 
714  control: Button {
715  text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
716 
717  onClicked: {
718  menuModel.activate(menuIndex);
719  }
720  }
721  }
722  }
723 
724  function load(modelData) {
725  if (modelData.type !== undefined) {
726  var component = _map[modelData.type];
727  if (component !== undefined) {
728  return component;
729  }
730  }
731  if (modelData.isCheck || modelData.isRadio) {
732  return checkableMenu;
733  }
734  if (modelData.isSeparator) {
735  return separatorMenu;
736  }
737  return standardMenu;
738  }
739 }