on ded2859 help sign in
unity8/qml/Panel/Indicators/MenuItemFactory.qml
665 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 }
144 }
145 }
146 Component {
147 id: sectionMenu;
149 Menus.SectionMenu {
150 objectName: "sectionMenu"
151 property QtObject menuData: null
152 property var menuIndex: undefined
154 text: menuData && menuData.label || ""
155 busy: false
156 }
157 }
159 Component {
160 id: progressMenu;
162 Menus.ProgressValueMenu {
163 objectName: "progressMenu"
164 property QtObject menuData: null
165 property int menuIndex: -1
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 }
174 Component {
175 id: standardMenu;
177 Menus.StandardMenu {
178 objectName: "standardMenu"
179 property QtObject menuData: null
180 property int menuIndex: -1
182 text: menuData && menuData.label || ""
183 iconSource: menuData && menuData.icon || ""
184 enabled: menuData && menuData.sensitive || false
186 onTriggered: {
187 menuModel.activate(menuIndex);
188 }
189 }
190 }
192 Component {
193 id: checkableMenu;
195 Menus.CheckableMenu {
196 objectName: "checkableMenu"
197 property QtObject menuData: null
198 property int menuIndex: -1
200 text: menuData && menuData.label || ""
201 enabled: menuData && menuData.sensitive || false
202 checked: menuData && menuData.isToggled || false
204 onTriggered: {
205 menuModel.activate(menuIndex);
206 }
207 }
208 }
211 Component {
212 id: switchMenu;
214 Menus.SwitchMenu {
215 objectName: "switchMenu"
216 property QtObject menuData: null
217 property int menuIndex: -1
219 text: menuData && menuData.label || ""
220 iconSource: menuData && menuData.icon || ""
221 enabled: menuData && menuData.sensitive || false
222 checked: menuData && menuData.isToggled || false
224 onTriggered: {
225 menuModel.activate(menuIndex);
226 }
227 }
228 }
230 Component {
231 id: alarmMenu;
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 }
245 text: menuData && menuData.label || ""
246 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
247 time: timeFormatter.timeString
248 enabled: menuData && menuData.sensitive || false
250 onMenuModelChanged: {
251 loadAttributes();
252 }
253 onMenuIndexChanged: {
254 loadAttributes();
255 }
256 onTriggered: {
257 menuModel.activate(menuIndex);
258 }
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 }
268 Component {
269 id: appointmentMenu;
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 }
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
289 onMenuModelChanged: {
290 loadAttributes();
291 }
292 onMenuIndexChanged: {
293 loadAttributes();
294 }
295 onTriggered: {
296 menuModel.activate(menuIndex);
297 }
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 }
308 Component {
309 id: wifiSection;
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
318 text: menuData && menuData.label || ""
319 busy: getExtendedProperty(extendedData, "xCanonicalBusyAction", false)
321 onMenuModelChanged: {
322 loadAttributes();
323 }
324 onMenuIndexChanged: {
325 loadAttributes();
326 }
328 function loadAttributes() {
329 if (!menuModel || menuIndex == -1) return;
330 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-busy-action': 'bool'})
331 }
332 }
333 }
335 Component {
336 id: accessPoint;
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
345 property var strengthAction: UnityMenuAction {
346 model: menuModel
347 index: menuIndex
348 name: getExtendedProperty(extendedData, "xCanonicalWifiApStrengthAction", "")
349 }
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
358 onMenuModelChanged: {
359 loadAttributes();
360 }
361 onMenuIndexChanged: {
362 loadAttributes();
363 }
364 onTriggered: {
365 menuModel.activate(menuIndex);
366 }
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 }
377 Component {
378 id: messageItem
380 MessageMenuItemFactory {
381 objectName: "messageItem"
382 menuModel: menuFactory.menuModel
383 }
384 }
386 Component {
387 id: groupedMessage
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
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
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 }
415 function loadAttributes() {
416 if (!menuModel || menuIndex == -1) return;
417 menuModel.loadExtendedAttributes(modelIndex, {'icon': 'icon'});
418 }
419 }
420 }
422 Component {
423 id: mediaPayerMenu;
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
432 playerIcon: menuData && menuData.icon || ""
433 playerName: menuData && menuData.label || ""
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
443 onTriggered: {
444 model.activate(modelIndex);
445 }
446 }
447 }
449 Component {
450 id: playbackItemMenu;
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
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 }
475 playing: playAction.state === "Playing"
476 canPlay: playAction.valid
477 canGoNext: nextAction.valid
478 canGoPrevious: previousAction.valid
479 enabled: menuData && menuData.sensitive || false
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 }
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 }
506 Component {
507 id: transferMenu
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)
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
525 QDBusActionGroup {
526 id: actionGroup
527 busType: 1
528 busName: rootModel.busName
529 objectPath: rootModel.actions["indicator"]
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
535 Component.onCompleted: actionGroup.start()
536 }
538 property var transferState: {
539 if (actionGroup.transferStateAction === null) return undefined;
540 return actionGroup.transferStateAction.valid ? actionGroup.transferStateAction.state : undefined
541 }
543 property var runningState : transferState !== undefined ? transferState["state"] : undefined
544 property var secondsLeft : transferState !== undefined ? transferState["seconds-left"] : undefined
546 active: runningState !== undefined && runningState !== Menus.TransferState.Finished
547 progress: transferState !== undefined ? transferState["percent"] : 0.0
549 // TODO - Should be in the SDK
550 property var timeRemaining: {
551 if (secondsLeft === undefined) return undefined;
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 }
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 }
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 }
607 function loadAttributes() {
608 if (!menuModel || menuIndex == -1) return;
609 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-uid': 'string'});
610 }
611 }
612 }
614 Component {
615 id: buttonSectionMenu;
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
624 iconSource: menuData && menuData.icon || ""
625 enabled: menuData && menuData.sensitive || false
626 text: menuData && menuData.label || ""
627 showDivider: false
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 }
640 control: Button {
641 text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
643 onClicked: {
644 menuModel.activate(menuIndex);
645 }
646 }
647 }
648 }
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 }