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