on aa5da49 help sign in
unity8/qml/Panel/Indicators/MenuItemFactory.qml
739 lines 27 kB history raw
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 */
20import QtQuick 2.0
21import Ubuntu.Settings.Menus 0.1 as Menus
22import Ubuntu.Settings.Components 0.1 as SettingsComponents
23import QMenuModel 0.1
24import Utils 0.1 as Utils
25import Ubuntu.Components.ListItems 0.1 as ListItems
26import Ubuntu.Components 0.1
28Item {
29 id: menuFactory
31 property var rootModel: null
32 property var menuModel: null
34 property var _map: {
35 "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
36 "unity.widgets.systemsettings.tablet.switch" : switchMenu,
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,
49 "com.canonical.indicator.messages.messageitem" : messageItem,
50 "com.canonical.indicator.messages.sourceitem" : groupedMessage,
52 "com.canonical.unity.slider" : sliderMenu,
53 "com.canonical.unity.switch" : switchMenu,
55 "com.canonical.unity.media-player" : mediaPayerMenu,
56 "com.canonical.unity.playback-item" : playbackItemMenu,
58 "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
59 "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
60 "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
61 }
63 function getExtendedProperty(object, propertyName, defaultValue) {
64 if (object && object.hasOwnProperty(propertyName)) {
65 return object[propertyName];
66 }
67 return defaultValue;
68 }
70 Component {
71 id: separatorMenu;
73 Menus.SeparatorMenu {
74 objectName: "separatorMenu"
75 }
76 }
78 Component {
79 id: sliderMenu;
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)
89 text: menuData && menuData.label || ""
90 iconSource: menuData && menuData.icon || ""
91 minIcon: getExtendedProperty(extendedData, "minIcon", "")
92 maxIcon: getExtendedProperty(extendedData, "maxIcon", "")
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
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 }
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 }
130 Component {
131 id: buttonMenu;
133 Menus.ButtonMenu {
134 objectName: "buttonMenu"
135 property QtObject menuData: null
136 property var menuModel: menuFactory.menuModel
137 property int menuIndex: -1
139 buttonText: menuData && menuData.label || ""
140 enabled: menuData && menuData.sensitive || false
142 onTriggered: {
143 menuModel.activate(menuIndex);
144 }
145 }
146 }
147 Component {
148 id: sectionMenu;
150 Menus.SectionMenu {
151 objectName: "sectionMenu"
152 property QtObject menuData: null
153 property var menuIndex: undefined
155 text: menuData && menuData.label || ""
156 busy: false
157 }
158 }
160 Component {
161 id: progressMenu;
163 Menus.ProgressValueMenu {
164 objectName: "progressMenu"
165 property QtObject menuData: null
166 property int menuIndex: -1
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 }
175 Component {
176 id: standardMenu;
178 Menus.StandardMenu {
179 objectName: "standardMenu"
180 property QtObject menuData: null
181 property int menuIndex: -1
183 text: menuData && menuData.label || ""
184 iconSource: menuData && menuData.icon || ""
185 enabled: menuData && menuData.sensitive || false
187 onTriggered: {
188 menuModel.activate(menuIndex);
189 }
190 }
191 }
193 Component {
194 id: checkableMenu;
196 Menus.CheckableMenu {
197 objectName: "checkableMenu"
198 property QtObject menuData: null
199 property int menuIndex: -1
201 text: menuData && menuData.label || ""
202 enabled: menuData && menuData.sensitive || false
203 checked: menuData && menuData.isToggled || false
205 onTriggered: {
206 menuModel.activate(menuIndex);
207 }
208 }
209 }
212 Component {
213 id: switchMenu;
215 Menus.SwitchMenu {
216 objectName: "switchMenu"
217 property QtObject menuData: null
218 property int menuIndex: -1
220 text: menuData && menuData.label || ""
221 iconSource: menuData && menuData.icon || ""
222 enabled: menuData && menuData.sensitive || false
223 checked: menuData && menuData.isToggled || false
225 onTriggered: {
226 menuModel.activate(menuIndex);
227 }
228 }
229 }
231 Component {
232 id: alarmMenu;
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 }
246 text: menuData && menuData.label || ""
247 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
248 time: timeFormatter.timeString
249 enabled: menuData && menuData.sensitive || false
251 onMenuModelChanged: {
252 loadAttributes();
253 }
254 onMenuIndexChanged: {
255 loadAttributes();
256 }
257 onTriggered: {
258 menuModel.activate(menuIndex);
259 }
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 }
269 Component {
270 id: appointmentMenu;
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 }
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
290 onMenuModelChanged: {
291 loadAttributes();
292 }
293 onMenuIndexChanged: {
294 loadAttributes();
295 }
296 onTriggered: {
297 menuModel.activate(menuIndex);
298 }
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 }
309 Component {
310 id: wifiSection;
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
319 text: menuData && menuData.label || ""
320 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
322 onMenuModelChanged: {
323 loadAttributes();
324 }
325 onMenuIndexChanged: {
326 loadAttributes();
327 }
329 function loadAttributes() {
330 if (!menuModel || menuIndex == -1) return;
331 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
332 }
333 }
334 }
336 Component {
337 id: accessPoint;
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
346 property var strengthAction: UnityMenuAction {
347 model: menuModel
348 index: menuIndex
349 name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
350 }
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
359 onMenuModelChanged: {
360 loadAttributes();
361 }
362 onMenuIndexChanged: {
363 loadAttributes();
364 }
365 onTriggered: {
366 menuModel.activate(menuIndex);
367 }
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 }
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
387 property var statusLabelAction: UnityMenuAction {
388 model: menuModel
389 index: menuIndex
390 name: getExtendedProperty(extendedData, "xCanonicalModemStatusLabelAction", "")
391 }
392 statusText: statusLabelAction.valid ? statusLabelAction.state : ""
394 property var statusIconAction: UnityMenuAction {
395 model: menuModel
396 index: menuIndex
397 name: getExtendedProperty(extendedData, "xCanonicalModemStatusIconAction", "")
398 }
399 statusIcon: statusIconAction.valid ? statusIconAction.state : ""
401 property var connectivityIconAction: UnityMenuAction {
402 model: menuModel
403 index: menuIndex
404 name: getExtendedProperty(extendedData, "xCanonicalModemConnectivityIconAction", "")
405 }
406 connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
408 property var simIdentifierLabelAction: UnityMenuAction {
409 model: menuModel
410 index: menuIndex
411 name: getExtendedProperty(extendedData, "xCanonicalModemSimIdentifierLabelAction", "")
412 }
413 simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
415 property var roamingAction: UnityMenuAction {
416 model: menuModel
417 index: menuIndex
418 name: getExtendedProperty(extendedData, "xCanonicalModemRoamingAction", "")
419 }
420 roaming: roamingAction.valid ? roamingAction.state : false
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
432 onMenuModelChanged: {
433 loadAttributes();
434 }
435 onMenuIndexChanged: {
436 loadAttributes();
437 }
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 }
451 Component {
452 id: messageItem
454 MessageMenuItemFactory {
455 objectName: "messageItem"
456 menuModel: menuFactory.menuModel
457 }
458 }
460 Component {
461 id: groupedMessage
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
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
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 }
489 function loadAttributes() {
490 if (!menuModel || menuIndex == -1) return;
491 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
492 }
493 }
494 }
496 Component {
497 id: mediaPayerMenu;
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
506 playerIcon: menuData && menuData.icon || ""
507 playerName: menuData && menuData.label || ""
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
517 onTriggered: {
518 model.activate(modelIndex);
519 }
520 }
521 }
523 Component {
524 id: playbackItemMenu;
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
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 }
549 playing: playAction.state === "Playing"
550 canPlay: playAction.valid
551 canGoNext: nextAction.valid
552 canGoPrevious: previousAction.valid
553 enabled: menuData && menuData.sensitive || false
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 }
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 }
580 Component {
581 id: transferMenu
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)
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
599 QDBusActionGroup {
600 id: actionGroup
601 busType: 1
602 busName: rootModel.busName
603 objectPath: rootModel.actions["indicator"]
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
609 Component.onCompleted: actionGroup.start()
610 }
612 property var transferState: {
613 if (actionGroup.transferStateAction === null) return undefined;
614 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
615 }
617 property var runningState : transferState !== undefined ? transferState["state"] : undefined
618 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
620 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
621 progress: transferState !== undefined ? transferState["percent"] : 0.0
623 // TODO - Should be in the SDK
624 property var timeRemaining: {
625 if (secondsLeft === undefined) return undefined;
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 }
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 }
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 }
681 function loadAttributes() {
682 if (!menuModel || menuIndex == -1) return;
683 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
684 }
685 }
686 }
688 Component {
689 id: buttonSectionMenu;
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
698 iconSource: menuData && menuData.icon || ""
699 enabled: menuData && menuData.sensitive || false
700 text: menuData && menuData.label || ""
701 showDivider: false
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 }
714 control: Button {
715 text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
717 onClicked: {
718 menuModel.activate(menuIndex);
719 }
720 }
721 }
722 }
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 }