on f63ab20 help sign in
unity8/qml/Panel/Indicators/MenuItemFactory.qml
671 lines 24 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 }
62 function getExtendedProperty(object, propertyName, defaultValue) {
63 if (object && object.hasOwnProperty(propertyName)) {
64 return object[propertyName];
65 }
66 return defaultValue;
67 }
69 Component {
70 id: separatorMenu;
72 Menus.SeparatorMenu {
73 objectName: "separatorMenu"
74 }
75 }
77 Component {
78 id: sliderMenu;
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)
88 text: menuData && menuData.label || ""
89 iconSource: menuData && menuData.icon || ""
90 minIcon: getExtendedProperty(extendedData, "minIcon", "")
91 maxIcon: getExtendedProperty(extendedData, "maxIcon", "")
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
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 }
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 }
129 Component {
130 id: buttonMenu;
132 Menus.ButtonMenu {
133 objectName: "buttonMenu"
134 property QtObject menuData: null
135 property var menuModel: menuFactory.menuModel
136 property int menuIndex: -1
138 buttonText: menuData && menuData.label || ""
139 enabled: menuData && menuData.sensitive || false
141 onTriggered: {
142 menuModel.activate(menuIndex);
143 shell.hideIndicatorMenu(UbuntuAnimation.FastDuration);
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 shell.hideIndicatorMenu(UbuntuAnimation.BriskDuration);
190 }
191 }
192 }
194 Component {
195 id: checkableMenu;
197 Menus.CheckableMenu {
198 objectName: "checkableMenu"
199 property QtObject menuData: null
200 property int menuIndex: -1
202 text: menuData && menuData.label || ""
203 enabled: menuData && menuData.sensitive || false
204 checked: menuData && menuData.isToggled || false
206 onTriggered: {
207 menuModel.activate(menuIndex);
208 shell.hideIndicatorMenu(UbuntuAnimation.BriskDuration);
209 }
210 }
211 }
214 Component {
215 id: switchMenu;
217 Menus.SwitchMenu {
218 objectName: "switchMenu"
219 property QtObject menuData: null
220 property int menuIndex: -1
222 text: menuData && menuData.label || ""
223 iconSource: menuData && menuData.icon || ""
224 enabled: menuData && menuData.sensitive || false
225 checked: menuData && menuData.isToggled || false
227 onTriggered: {
228 menuModel.activate(menuIndex);
229 shell.hideIndicatorMenu(UbuntuAnimation.BriskDuration);
230 }
231 }
232 }
234 Component {
235 id: alarmMenu;
237 Menus.EventMenu {
238 objectName: "alarmMenu"
239 property QtObject menuData: null
240 property var menuModel: menuFactory.menuModel
241 property int menuIndex: -1
242 property var extendedData: menuData && menuData.ext || undefined
243 // TODO - bug #1260728
244 property var timeFormatter: Utils.GDateTimeFormatter {
245 time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
246 format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
247 }
249 text: menuData && menuData.label || ""
250 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
251 time: timeFormatter.timeString
252 enabled: menuData && menuData.sensitive || false
254 onMenuModelChanged: {
255 loadAttributes();
256 }
257 onMenuIndexChanged: {
258 loadAttributes();
259 }
260 onTriggered: {
261 menuModel.activate(menuIndex);
262 }
264 function loadAttributes() {
265 if (!menuModel || menuIndex == -1) return;
266 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
267 'x-canonical-time-format': 'string'});
268 }
269 }
270 }
272 Component {
273 id: appointmentMenu;
275 Menus.EventMenu {
276 objectName: "appointmentMenu"
277 property QtObject menuData: null
278 property var menuModel: menuFactory.menuModel
279 property int menuIndex: -1
280 property var extendedData: menuData && menuData.ext || undefined
281 // TODO - bug #1260728
282 property var timeFormatter: Utils.GDateTimeFormatter {
283 time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
284 format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
285 }
287 text: menuData && menuData.label || ""
288 iconSource: menuData && menuData.icon || "image://theme/calendar"
289 time: timeFormatter.timeString
290 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
291 enabled: menuData && menuData.sensitive || false
293 onMenuModelChanged: {
294 loadAttributes();
295 }
296 onMenuIndexChanged: {
297 loadAttributes();
298 }
299 onTriggered: {
300 menuModel.activate(menuIndex);
301 }
303 function loadAttributes() {
304 if (!menuModel || menuIndex == -1) return;
305 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
306 'x-canonical-time': 'int64',
307 'x-canonical-time-format': 'string'});
308 }
309 }
310 }
312 Component {
313 id: wifiSection;
315 Menus.SectionMenu {
316 objectName: "wifiSection"
317 property QtObject menuData: null
318 property var menuModel: menuFactory.menuModel
319 property int menuIndex: -1
320 property var extendedData: menuData && menuData.ext || undefined
322 text: menuData && menuData.label || ""
323 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
325 onMenuModelChanged: {
326 loadAttributes();
327 }
328 onMenuIndexChanged: {
329 loadAttributes();
330 }
332 function loadAttributes() {
333 if (!menuModel || menuIndex == -1) return;
334 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
335 }
336 }
337 }
339 Component {
340 id: accessPoint;
342 Menus.AccessPointMenu {
343 objectName: "accessPoint"
344 property QtObject menuData: null
345 property var menuModel: menuFactory.menuModel
346 property int menuIndex: -1
347 property var extendedData: menuData && menuData.ext || undefined
349 property var strengthAction: UnityMenuAction {
350 model: menuModel
351 index: menuIndex
352 name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
353 }
355 text: menuData && menuData.label || ""
356 enabled: menuData && menuData.sensitive || false
357 checked: menuData && menuData.isToggled || false
358 secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
359 adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
360 signalStrength: strengthAction.valid ? strengthAction.state : 0
362 onMenuModelChanged: {
363 loadAttributes();
364 }
365 onMenuIndexChanged: {
366 loadAttributes();
367 }
368 onTriggered: {
369 menuModel.activate(menuIndex);
370 shell.hideIndicatorMenu(UbuntuAnimation.BriskDuration);
371 }
373 function loadAttributes() {
374 if (!menuModel || menuIndex == -1) return;
375 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
376 'x-canonical-wifi-ap-is-secure': 'bool',
377 'x-canonical-wifi-ap-strength-action': 'string'});
378 }
379 }
380 }
382 Component {
383 id: messageItem
385 MessageMenuItemFactory {
386 objectName: "messageItem"
387 menuModel: menuFactory.menuModel
388 }
389 }
391 Component {
392 id: groupedMessage
394 Menus.GroupedMessageMenu {
395 objectName: "groupedMessage"
396 property QtObject menuData: null
397 property var menuModel: menuFactory.menuModel
398 property int menuIndex: -1
399 property var extendedData: menuData && menuData.ext || undefined
401 text: menuData && menuData.label || ""
402 iconSource: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_app.svg")
403 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
404 enabled: menuData && menuData.sensitive || false
405 removable: true
407 onMenuModelChanged: {
408 loadAttributes();
409 }
410 onMenuIndexChanged: {
411 loadAttributes();
412 }
413 onClicked: {
414 menuModel.activate(menuIndex, true);
415 shell.hideIndicatorMenu(UbuntuAnimation.FastDuration);
416 }
417 onDismissed: {
418 menuModel.activate(menuIndex, false);
419 }
421 function loadAttributes() {
422 if (!menuModel || menuIndex == -1) return;
423 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
424 }
425 }
426 }
428 Component {
429 id: mediaPayerMenu;
431 Menus.MediaPlayerMenu {
432 objectName: "mediaPayerMenu"
433 property QtObject menuData: null
434 property var menuModel: menuFactory.menuModel
435 property int menuIndex: -1
436 property var actionState: menuData && menuData.actionState || undefined
438 playerIcon: menuData && menuData.icon || ""
439 playerName: menuData && menuData.label || ""
441 albumArt: getExtendedProperty(actionState, "art-url", "")
442 song: getExtendedProperty(actionState, "title", "unknown")
443 artist: getExtendedProperty(actionState, "artist", "unknown")
444 album: getExtendedProperty(actionState, "album", "unknown")
445 running: getExtendedProperty(actionState, "running", false)
446 state: getExtendedProperty(actionState, "state", "")
447 enabled: menuData && menuData.sensitive || false
449 onTriggered: {
450 model.activate(modelIndex);
451 }
452 }
453 }
455 Component {
456 id: playbackItemMenu;
458 Menus.PlaybackItemMenu {
459 objectName: "playbackItemMenu"
460 property QtObject menuData: null
461 property var menuModel: menuFactory.menuModel
462 property int menuIndex: -1
463 property var extendedData: menuData && menuData.ext || undefined
465 property var playAction: UnityMenuAction {
466 model: menuModel
467 index: menuIndex
468 name: getExtendedProperty(extendedData, "xCanonicalPlayAction", "")
469 }
470 property var nextAction: UnityMenuAction {
471 model: menuModel
472 index: menuIndex
473 name: getExtendedProperty(extendedData, "xCanonicalNextAction", "")
474 }
475 property var previousAction: UnityMenuAction {
476 model: menuModel
477 index: menuIndex
478 name: getExtendedProperty(extendedData, "xCanonicalPreviousAction", "")
479 }
481 playing: playAction.state === "Playing"
482 canPlay: playAction.valid
483 canGoNext: nextAction.valid
484 canGoPrevious: previousAction.valid
485 enabled: menuData && menuData.sensitive || false
487 onPlay: {
488 playAction.activate();
489 }
490 onNext: {
491 nextAction.activate();
492 }
493 onPrevious: {
494 previousAction.activate();
495 }
496 onMenuModelChanged: {
497 loadAttributes();
498 }
499 onMenuIndexChanged: {
500 loadAttributes();
501 }
503 function loadAttributes() {
504 if (!menuModel || menuIndex == -1) return;
505 menuModel.loadExtendedAttributes(modelIndex, {'x-canonical-play-action': 'string',
506 'x-canonical-next-action': 'string',
507 'x-canonical-previous-action': 'string'});
508 }
509 }
510 }
512 Component {
513 id: transferMenu
515 Menus.TransferMenu {
516 objectName: "transferMenu"
517 id: transfer
518 property QtObject menuData: null
519 property var menuModel: menuFactory.menuModel
520 property int menuIndex: -1
521 property var extendedData: menuData && menuData.ext || undefined
522 property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
524 text: menuData && menuData.label || ""
525 iconSource: menuData && menuData.icon || ""
526 maximum: 1.0
527 enabled: menuData && menuData.sensitive || false
528 removable: true
529 confirmRemoval: true
531 QDBusActionGroup {
532 id: actionGroup
533 busType: 1
534 busName: rootModel.busName
535 objectPath: rootModel.actions["indicator"]
537 property var activateAction: action("activate-transfer")
538 property var cancelAction: action("cancel-transfer")
539 property var transferStateAction: uid !== undefined ? action("transfer-state."+uid) : null
541 Component.onCompleted: actionGroup.start()
542 }
544 property var transferState: {
545 if (actionGroup.transferStateAction === null) return undefined;
546 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
547 }
549 property var runningState : transferState !== undefined ? transferState["state"] : undefined
550 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
552 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
553 progress: transferState !== undefined ? transferState["percent"] : 0.0
555 // TODO - Should be in the SDK
556 property var timeRemaining: {
557 if (secondsLeft === undefined) return undefined;
559 var remaining = "";
560 var hours = Math.floor(secondsLeft / (60 * 60));
561 var minutes = Math.floor(secondsLeft / 60) % 60;
562 var seconds = secondsLeft % 60;
563 if (hours > 0) {
564 remaining += hours + (hours == 1 ? " hour" : " hours");
565 }
566 if (minutes > 0) {
567 if (remaining != "") remaining += ", ";
568 remaining += minutes + (minutes == 1 ? " minute" : " minutes");
569 }
570 // don't include seconds if hours > 0
571 if (hours == 0 && minutes < 5 && seconds > 0) {
572 if (remaining != "") remaining += ", ";
573 remaining += seconds + (seconds == 1 ? " second" : " seconds");
574 }
575 if (remaining == "")
576 remaining = "0 seconds";
577 return remaining + " remaining";
578 }
580 stateText: {
581 switch (runningState) {
582 case Menus.TransferState.Queued:
583 return i18n.tr("In queue…");
584 case Menus.TransferState.Hashing:
585 case Menus.TransferState.Processing:
586 case Menus.TransferState.Running:
587 return timeRemaining === undefined ? i18n.tr("Downloading") : timeRemaining;
588 case Menus.TransferState.Paused:
589 return i18n.tr("Paused, tap to resume");
590 case Menus.TransferState.Canceled:
591 return i18n.tr("Canceled");
592 case Menus.TransferState.Finished:
593 return i18n.tr("Finished");
594 case Menus.TransferState.Error:
595 return i18n.tr("Failed, tap to retry");
596 }
597 return "";
598 }
600 onMenuModelChanged: {
601 loadAttributes();
602 }
603 onMenuIndexChanged: {
604 loadAttributes();
605 }
606 onTriggered: {
607 actionGroup.activateAction.activate(uid);
608 }
609 onItemRemoved: {
610 actionGroup.cancelAction.activate(uid);
611 }
613 function loadAttributes() {
614 if (!menuModel || menuIndex == -1) return;
615 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
616 }
617 }
618 }
620 Component {
621 id: buttonSectionMenu;
623 ListItems.Standard {
624 objectName: "buttonSectionMenu"
625 property QtObject menuData: null
626 property var menuModel: menuFactory.menuModel
627 property int menuIndex: -1
628 property var extendedData: menuData && menuData.ext || undefined
630 iconSource: menuData && menuData.icon || ""
631 enabled: menuData && menuData.sensitive || false
632 text: menuData && menuData.label || ""
633 showDivider: false
635 onMenuModelChanged: {
636 loadAttributes();
637 }
638 onMenuIndexChanged: {
639 loadAttributes();
640 }
641 function loadAttributes() {
642 if (!menuModel || menuIndex == -1) return;
643 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
644 }
646 control: Button {
647 text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
649 onClicked: {
650 menuModel.activate(menuIndex);
651 }
652 }
653 }
654 }
656 function load(modelData) {
657 if (modelData.type !== undefined) {
658 var component = _map[modelData.type];
659 if (component !== undefined) {
660 return component;
661 }
662 }
663 if (modelData.isCheck || modelData.isRadio) {
664 return checkableMenu;
665 }
666 if (modelData.isSeparator) {
667 return separatorMenu;
668 }
669 return standardMenu;
670 }