1/*
2    SPDX-FileCopyrightText: 2012-2016 Eike Hein <hein@kde.org>
3    SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
4
5    SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8import QtQuick 2.0
9
10import org.kde.plasma.plasmoid 2.0
11
12import org.kde.plasma.core 2.0 as PlasmaCore
13import org.kde.plasma.components 2.0 as PlasmaComponents
14
15import org.kde.taskmanager 0.1 as TaskManager
16
17import "code/layout.js" as LayoutManager
18
19PlasmaComponents.ContextMenu {
20    id: menu
21
22    property QtObject backend
23    property QtObject mpris2Source
24    property var modelIndex
25    readonly property var atm: TaskManager.AbstractTasksModel
26
27    property bool showAllPlaces: false
28
29    placement: {
30        if (plasmoid.location === PlasmaCore.Types.LeftEdge) {
31            return PlasmaCore.Types.RightPosedTopAlignedPopup;
32        } else if (plasmoid.location === PlasmaCore.Types.TopEdge) {
33            return PlasmaCore.Types.BottomPosedLeftAlignedPopup;
34        } else if (plasmoid.location === PlasmaCore.Types.RightEdge) {
35            return PlasmaCore.Types.LeftPosedTopAlignedPopup;
36        } else {
37            return PlasmaCore.Types.TopPosedLeftAlignedPopup;
38        }
39    }
40
41    minimumWidth: visualParent.width
42
43    onStatusChanged: {
44        if (visualParent && get(atm.LauncherUrlWithoutIcon) != "" && status == PlasmaComponents.DialogStatus.Open) {
45            activitiesDesktopsMenu.refresh();
46
47        } else if (status == PlasmaComponents.DialogStatus.Closed) {
48            menu.destroy();
49            backend.ungrabMouse(visualParent);
50        }
51    }
52
53    Component.onCompleted: {
54        // Cannot have "Connections" as child of PlasmaCoponents.ContextMenu.
55        backend.showAllPlaces.connect(showContextMenuWithAllPlaces);
56    }
57
58    Component.onDestruction: {
59        backend.showAllPlaces.disconnect(showContextMenuWithAllPlaces);
60    }
61
62    function showContextMenuWithAllPlaces() {
63        visualParent.showContextMenu({showAllPlaces: true});
64    }
65
66    function get(modelProp) {
67        return tasksModel.data(modelIndex, modelProp)
68    }
69
70    function show() {
71        loadDynamicLaunchActions(get(atm.LauncherUrlWithoutIcon));
72        openRelative();
73    }
74
75    function newMenuItem(parent) {
76        return Qt.createQmlObject(
77            "import org.kde.plasma.components 2.0 as PlasmaComponents;" +
78            "PlasmaComponents.MenuItem {}",
79            parent);
80    }
81
82    function newSeparator(parent) {
83        return Qt.createQmlObject(
84            "import org.kde.plasma.components 2.0 as PlasmaComponents;" +
85            "PlasmaComponents.MenuItem { separator: true }",
86            parent);
87    }
88
89    function loadDynamicLaunchActions(launcherUrl) {
90        var sections = [
91            {
92                title:   i18n("Places"),
93                group:   "places",
94                actions: backend.placesActions(launcherUrl, showAllPlaces, menu)
95            },
96            {
97                title:   i18n("Recent Files"),
98                group:   "recents",
99                actions: backend.recentDocumentActions(launcherUrl, menu)
100            },
101            {
102                title:   i18n("Actions"),
103                group:   "actions",
104                actions: backend.jumpListActions(launcherUrl, menu)
105            }
106        ]
107
108        // QMenu does not limit its width automatically. Even if we set a maximumWidth
109        // it would just cut off text rather than eliding. So we do this manually.
110        var textMetrics = Qt.createQmlObject("import QtQuick 2.4; TextMetrics {}", menu);
111        var maximumWidth = LayoutManager.maximumContextMenuTextWidth();
112
113        sections.forEach(function (section) {
114            if (section["actions"].length > 0 || section["group"] == "actions") {
115                // Don't add the "Actions" header if the menu has nothing but actions
116                // in it, because then it's redundant (all menus have actions)
117                if (
118                    (section["group"] != "actions") ||
119                    (section["group"] == "actions" && (sections[0]["actions"].length > 0 || sections[1]["actions"].length > 0))
120                ) {
121                    var sectionHeader = newMenuItem(menu);
122                    sectionHeader.text = section["title"];
123                    sectionHeader.section = true;
124                    menu.addMenuItem(sectionHeader, startNewInstanceItem);
125                }
126            }
127
128            for (var i = 0; i < section["actions"].length; ++i) {
129                var item = newMenuItem(menu);
130                item.action = section["actions"][i];
131
132                // Crude way of manually eliding...
133                var elided = false;
134                textMetrics.text = Qt.binding(function() {
135                    return item.action.text;
136                });
137
138                while (textMetrics.width > maximumWidth) {
139                    item.action.text = item.action.text.slice(0, -1);
140                    elided = true;
141                }
142
143                if (elided) {
144                    item.action.text += "…";
145                }
146
147                menu.addMenuItem(item, startNewInstanceItem);
148            }
149        });
150
151        // Add Media Player control actions
152        var sourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl, get(atm.AppPid));
153
154        if (sourceName && !(get(atm.WinIdList) !== undefined && get(atm.WinIdList).length > 1)) {
155            var playerData = mpris2Source.data[sourceName]
156
157            if (playerData.CanControl) {
158                var playing = (playerData.PlaybackStatus === "Playing");
159                var menuItem = menu.newMenuItem(menu);
160                menuItem.text = i18nc("Play previous track", "Previous Track");
161                menuItem.icon = "media-skip-backward";
162                menuItem.enabled = Qt.binding(function() {
163                    return playerData.CanGoPrevious;
164                });
165                menuItem.clicked.connect(function() {
166                    mpris2Source.goPrevious(sourceName);
167                });
168                menu.addMenuItem(menuItem, startNewInstanceItem);
169
170                menuItem = menu.newMenuItem(menu);
171                // PlasmaCore Menu doesn't actually handle icons or labels changing at runtime...
172                menuItem.text = Qt.binding(function() {
173                    // if CanPause, toggle the menu entry between Play & Pause, otherwise always use Play
174                    return playing && playerData.CanPause ? i18nc("Pause playback", "Pause") : i18nc("Start playback", "Play");
175                });
176                menuItem.icon = Qt.binding(function() {
177                    return playing && playerData.CanPause ? "media-playback-pause" : "media-playback-start";
178                });
179                menuItem.enabled = Qt.binding(function() {
180                    return playing ? playerData.CanPause : playerData.CanPlay;
181                });
182                menuItem.clicked.connect(function() {
183                    if (playing) {
184                        mpris2Source.pause(sourceName);
185                    } else {
186                        mpris2Source.play(sourceName);
187                    }
188                });
189                menu.addMenuItem(menuItem, startNewInstanceItem);
190
191                menuItem = menu.newMenuItem(menu);
192                menuItem.text = i18nc("Play next track", "Next Track");
193                menuItem.icon = "media-skip-forward";
194                menuItem.enabled = Qt.binding(function() {
195                    return playerData.CanGoNext;
196                });
197                menuItem.clicked.connect(function() {
198                    mpris2Source.goNext(sourceName);
199                });
200                menu.addMenuItem(menuItem, startNewInstanceItem);
201
202                menuItem = menu.newMenuItem(menu);
203                menuItem.text = i18nc("Stop playback", "Stop");
204                menuItem.icon = "media-playback-stop";
205                menuItem.enabled = Qt.binding(function() {
206                    return playerData.PlaybackStatus !== "Stopped";
207                });
208                menuItem.clicked.connect(function() {
209                    mpris2Source.stop(sourceName);
210                });
211                menu.addMenuItem(menuItem, startNewInstanceItem);
212
213                // Technically media controls and audio streams are separate but for the user they're
214                // semantically related, don't add a separator inbetween.
215                if (!menu.visualParent.hasAudioStream) {
216                    menu.addMenuItem(newSeparator(menu), startNewInstanceItem);
217                }
218
219                // If we don't have a window associated with the player but we can quit
220                // it through MPRIS we'll offer a "Quit" option instead of "Close"
221                if (!closeWindowItem.visible && playerData.CanQuit) {
222                    menuItem = menu.newMenuItem(menu);
223                    menuItem.text = i18nc("Quit media player app", "Quit");
224                    menuItem.icon = "application-exit";
225                    menuItem.visible = Qt.binding(function() {
226                        return !closeWindowItem.visible;
227                    });
228                    menuItem.clicked.connect(function() {
229                        mpris2Source.quit(sourceName);
230                    });
231                    menu.addMenuItem(menuItem);
232                }
233
234                // If we don't have a window associated with the player but we can raise
235                // it through MPRIS we'll offer a "Restore" option
236                if (!startNewInstanceItem.visible && playerData.CanRaise) {
237                    menuItem = menu.newMenuItem(menu);
238                    menuItem.text = i18nc("Open or bring to the front window of media player app", "Restore");
239                    menuItem.icon = playerData["Desktop Icon Name"];
240                    menuItem.visible = Qt.binding(function() {
241                        return !startNewInstanceItem.visible;
242                    });
243                    menuItem.clicked.connect(function() {
244                        mpris2Source.raise(sourceName);
245                    });
246                    menu.addMenuItem(menuItem, startNewInstanceItem);
247                }
248            }
249        }
250
251        // We allow mute/unmute whenever an application has a stream, regardless of whether it
252        // is actually playing sound.
253        // This way you can unmute, e.g. a telephony app, even after the conversation has ended,
254        // so you still have it ringing later on.
255        if (menu.visualParent.hasAudioStream) {
256            var muteItem = menu.newMenuItem(menu);
257            muteItem.checkable = true;
258            muteItem.checked = Qt.binding(function() {
259                return menu.visualParent && menu.visualParent.muted;
260            });
261            muteItem.clicked.connect(function() {
262                menu.visualParent.toggleMuted();
263            });
264            muteItem.text = i18n("Mute");
265            muteItem.icon = "audio-volume-muted";
266            menu.addMenuItem(muteItem, startNewInstanceItem);
267
268            menu.addMenuItem(newSeparator(menu), startNewInstanceItem);
269        }
270    }
271
272    PlasmaComponents.MenuItem {
273        id: startNewInstanceItem
274        visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
275
276        enabled: visualParent && get(atm.LauncherUrlWithoutIcon) != ""
277
278        text: i18n("Start New Instance")
279        icon: "list-add-symbolic"
280
281        onClicked: tasksModel.requestNewInstance(modelIndex)
282    }
283
284        PlasmaComponents.MenuItem {
285        id: virtualDesktopsMenuItem
286
287        visible: virtualDesktopInfo.numberOfDesktops > 1
288            && (visualParent && get(atm.IsLauncher) !== true
289            && get(atm.IsStartup) !== true
290            && get(atm.IsVirtualDesktopsChangeable) === true)
291
292        enabled: visible
293
294        text: i18n("Move to &Desktop")
295        icon: "virtual-desktops"
296
297        Connections {
298            target: virtualDesktopInfo
299
300            function onNumberOfDesktopsChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
301            function onDesktopIdsChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
302            function onDesktopNamesChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
303        }
304
305        PlasmaComponents.ContextMenu {
306            id: virtualDesktopsMenu
307
308            visualParent: virtualDesktopsMenuItem.action
309
310            function refresh() {
311                clearMenuItems();
312
313                if (virtualDesktopInfo.numberOfDesktops <= 1) {
314                    return;
315                }
316
317                var menuItem = menu.newMenuItem(virtualDesktopsMenu);
318                menuItem.text = i18n("Move &To Current Desktop");
319                menuItem.enabled = Qt.binding(function() {
320                    return menu.visualParent && menu.get(atm.VirtualDesktops).indexOf(virtualDesktopInfo.currentDesktop) === -1;
321                });
322                menuItem.clicked.connect(function() {
323                    tasksModel.requestVirtualDesktops(menu.modelIndex, [virtualDesktopInfo.currentDesktop]);
324                });
325
326                menuItem = menu.newMenuItem(virtualDesktopsMenu);
327                menuItem.text = i18n("&All Desktops");
328                menuItem.checkable = true;
329                menuItem.checked = Qt.binding(function() {
330                    return menu.visualParent && menu.get(atm.IsOnAllVirtualDesktops) === true;
331                });
332                menuItem.clicked.connect(function() {
333                    tasksModel.requestVirtualDesktops(menu.modelIndex, []);
334                });
335                backend.setActionGroup(menuItem.action);
336
337                menu.newSeparator(virtualDesktopsMenu);
338
339                for (var i = 0; i < virtualDesktopInfo.desktopNames.length; ++i) {
340                    menuItem = menu.newMenuItem(virtualDesktopsMenu);
341                    menuItem.text = i18nc("1 = number of desktop, 2 = desktop name", "&%1 %2", i + 1, virtualDesktopInfo.desktopNames[i]);
342                    menuItem.checkable = true;
343                    menuItem.checked = Qt.binding((function(i) {
344                        return function() { return menu.visualParent && menu.get(atm.VirtualDesktops).indexOf(virtualDesktopInfo.desktopIds[i]) > -1 };
345                    })(i));
346                    menuItem.clicked.connect((function(i) {
347                        return function() { return tasksModel.requestVirtualDesktops(menu.modelIndex, [virtualDesktopInfo.desktopIds[i]]); };
348                    })(i));
349                    backend.setActionGroup(menuItem.action);
350                }
351
352                menu.newSeparator(virtualDesktopsMenu);
353
354                menuItem = menu.newMenuItem(virtualDesktopsMenu);
355                menuItem.text = i18n("&New Desktop");
356                menuItem.clicked.connect(function() {
357                    tasksModel.requestNewVirtualDesktop(menu.modelIndex);
358                });
359            }
360
361            Component.onCompleted: refresh()
362        }
363    }
364
365     PlasmaComponents.MenuItem {
366        id: activitiesDesktopsMenuItem
367
368        visible: activityInfo.numberOfRunningActivities > 1
369            && (visualParent && !get(atm.IsLauncher)
370            && !get(atm.IsStartup))
371
372        enabled: visible
373
374        text: i18n("Show in &Activities")
375        icon: "activities"
376
377        Connections {
378            target: activityInfo
379
380            function onNumberOfRunningActivitiesChanged() {
381                activitiesDesktopsMenu.refresh()
382            }
383        }
384
385        PlasmaComponents.ContextMenu {
386            id: activitiesDesktopsMenu
387
388            visualParent: activitiesDesktopsMenuItem.action
389
390            function refresh() {
391                clearMenuItems();
392
393                if (activityInfo.numberOfRunningActivities <= 1) {
394                    return;
395                }
396
397                var menuItem = menu.newMenuItem(activitiesDesktopsMenu);
398                menuItem.text = i18n("Add To Current Activity");
399                menuItem.enabled = Qt.binding(function() {
400                    return menu.visualParent && menu.get(atm.Activities).length > 0 &&
401                           menu.get(atm.Activities).indexOf(activityInfo.currentActivity) < 0;
402                });
403                menuItem.clicked.connect(function() {
404                    tasksModel.requestActivities(menu.modelIndex, menu.get(atm.Activities).concat(activityInfo.currentActivity));
405                });
406
407                menuItem = menu.newMenuItem(activitiesDesktopsMenu);
408                menuItem.text = i18n("All Activities");
409                menuItem.checkable = true;
410                menuItem.checked = Qt.binding(function() {
411                    return menu.visualParent && menu.get(atm.Activities).length === 0;
412                });
413                menuItem.toggled.connect(function(checked) {
414                    var newActivities = undefined; // will cast to an empty QStringList i.e all activities
415                    if (!checked) {
416                        newActivities = new Array(activityInfo.currentActivity);
417                    }
418                    tasksModel.requestActivities(menu.modelIndex, newActivities);
419                });
420
421                menu.newSeparator(activitiesDesktopsMenu);
422
423                var runningActivities = activityInfo.runningActivities();
424                for (var i = 0; i < runningActivities.length; ++i) {
425                    var activityId = runningActivities[i];
426
427                    menuItem = menu.newMenuItem(activitiesDesktopsMenu);
428                    menuItem.text = activityInfo.activityName(runningActivities[i]);
429                    menuItem.checkable = true;
430                    menuItem.checked = Qt.binding( (function(activityId) {
431                        return function() {
432                            return menu.visualParent && menu.get(atm.Activities).indexOf(activityId) >= 0;
433                        };
434                    })(activityId));
435                    menuItem.toggled.connect((function(activityId) {
436                        return function (checked) {
437                            var newActivities = menu.get(atm.Activities);
438                            if (checked) {
439                                newActivities = newActivities.concat(activityId);
440                            } else {
441                                var index = newActivities.indexOf(activityId)
442                                if (index < 0) {
443                                    return;
444                                }
445
446                                newActivities.splice(index, 1);
447                            }
448                            return tasksModel.requestActivities(menu.modelIndex, newActivities);
449                        };
450                    })(activityId));
451                }
452
453                menu.newSeparator(activitiesDesktopsMenu);
454            }
455
456            Component.onCompleted: refresh()
457        }
458    }
459
460    PlasmaComponents.MenuItem {
461        id: moreActionsMenuItem
462
463        visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
464
465        enabled: visible
466
467        text: i18n("More Actions")
468        icon: "view-more-symbolic"
469
470        PlasmaComponents.ContextMenu {
471            visualParent: moreActionsMenuItem.action
472
473            PlasmaComponents.MenuItem {
474                enabled: menu.visualParent && menu.get(atm.IsMovable) === true
475
476                text: i18n("&Move")
477                icon: "transform-move"
478
479                onClicked: tasksModel.requestMove(menu.modelIndex)
480            }
481
482            PlasmaComponents.MenuItem {
483                enabled: menu.visualParent && menu.get(atm.IsResizable) === true
484
485                text: i18n("Re&size")
486                icon: "transform-scale"
487
488                onClicked: tasksModel.requestResize(menu.modelIndex)
489            }
490
491            PlasmaComponents.MenuItem {
492                visible: (menu.visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
493
494                enabled: menu.visualParent && get(atm.IsMaximizable) === true
495
496                checkable: true
497                checked: menu.visualParent && get(atm.IsMaximized) === true
498
499                text: i18n("Ma&ximize")
500                icon: "window-maximize"
501
502                onClicked: tasksModel.requestToggleMaximized(modelIndex)
503            }
504
505            PlasmaComponents.MenuItem {
506                visible: (menu.visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
507
508                enabled: menu.visualParent && get(atm.IsMinimizable) === true
509
510                checkable: true
511                checked: menu.visualParent && get(atm.IsMinimized) === true
512
513                text: i18n("Mi&nimize")
514                icon: "window-minimize"
515
516                onClicked: tasksModel.requestToggleMinimized(modelIndex)
517            }
518
519            PlasmaComponents.MenuItem {
520                checkable: true
521                checked: menu.visualParent && menu.get(atm.IsKeepAbove) === true
522
523                text: i18n("Keep &Above Others")
524                icon: "window-keep-above"
525
526                onClicked: tasksModel.requestToggleKeepAbove(menu.modelIndex)
527            }
528
529            PlasmaComponents.MenuItem {
530                checkable: true
531                checked: menu.visualParent && menu.get(atm.IsKeepBelow) === true
532
533                text: i18n("Keep &Below Others")
534                icon: "window-keep-below"
535
536                onClicked: tasksModel.requestToggleKeepBelow(menu.modelIndex)
537            }
538
539            PlasmaComponents.MenuItem {
540                enabled: menu.visualParent && menu.get(atm.IsFullScreenable) === true
541
542                checkable: true
543                checked: menu.visualParent && menu.get(atm.IsFullScreen) === true
544
545                text: i18n("&Fullscreen")
546                icon: "view-fullscreen"
547
548                onClicked: tasksModel.requestToggleFullScreen(menu.modelIndex)
549            }
550
551            PlasmaComponents.MenuItem {
552                enabled: menu.visualParent && menu.get(atm.IsShadeable) === true
553
554                checkable: true
555                checked: menu.visualParent && menu.get(atm.IsShaded) === true
556
557                text: i18n("&Shade")
558                icon: "window-shade"
559
560                onClicked: tasksModel.requestToggleShaded(menu.modelIndex)
561            }
562
563            PlasmaComponents.MenuItem {
564                separator: true
565            }
566
567            PlasmaComponents.MenuItem {
568                visible: (plasmoid.configuration.groupingStrategy !== 0) && menu.get(atm.IsWindow) === true
569
570                checkable: true
571                checked: menu.visualParent && menu.get(atm.IsGroupable) === true
572
573                text: i18n("Allow this program to be grouped")
574                icon: "view-group"
575
576                onClicked: tasksModel.requestToggleGrouping(menu.modelIndex)
577            }
578
579            PlasmaComponents.MenuItem {
580                separator: true
581            }
582
583            PlasmaComponents.MenuItem {
584                property QtObject configureAction: null
585
586                enabled: configureAction && configureAction.enabled
587                visible: configureAction && configureAction.visible
588
589                text: configureAction ? configureAction.text : ""
590                icon: configureAction ? configureAction.icon : ""
591
592                onClicked: configureAction.trigger()
593
594                Component.onCompleted: configureAction = plasmoid.action("configure")
595            }
596
597            PlasmaComponents.MenuItem {
598                property QtObject alternativesAction: null
599
600                enabled: alternativesAction && alternativesAction.enabled
601                visible: alternativesAction && alternativesAction.visible
602
603                text: alternativesAction ? alternativesAction.text : ""
604                icon: alternativesAction ? alternativesAction.icon : ""
605
606                onClicked: alternativesAction.trigger()
607
608                Component.onCompleted: alternativesAction = plasmoid.action("alternatives")
609            }
610        }
611    }
612
613    PlasmaComponents.MenuItem {
614        id: launcherToggleAction
615
616        visible: visualParent
617                     && get(atm.IsLauncher) !== true
618                     && get(atm.IsStartup) !== true
619                     && plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
620                     && (activityInfo.numberOfRunningActivities < 2)
621                     && tasksModel.launcherPosition(get(atm.LauncherUrlWithoutIcon)) == -1
622
623        enabled: visualParent && get(atm.LauncherUrlWithoutIcon) != ""
624
625        text: i18n("&Pin to Task Manager")
626        icon: "window-pin"
627
628        onClicked: {
629            if (tasksModel.launcherPosition(get(atm.LauncherUrlWithoutIcon)) !== -1) {
630                tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon));
631            } else {
632                tasksModel.requestAddLauncher(get(atm.LauncherUrl));
633            }
634        }
635    }
636
637    PlasmaComponents.MenuItem {
638        id: showLauncherInActivitiesItem
639
640        text: i18n("&Pin to Task Manager")
641        icon: "window-pin"
642
643        visible: visualParent
644                     && get(atm.IsLauncher) !== true
645                     && get(atm.IsStartup) !== true
646                     && plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
647                     && (activityInfo.numberOfRunningActivities >= 2)
648
649        Connections {
650            target: activityInfo
651            function onNumberOfRunningActivitiesChanged() {
652                activitiesDesktopsMenu.refresh()
653            }
654        }
655
656        PlasmaComponents.ContextMenu {
657            id: activitiesLaunchersMenu
658            visualParent: showLauncherInActivitiesItem.action
659
660            function refresh() {
661                clearMenuItems();
662
663                if (menu.visualParent === null) return;
664
665                var createNewItem = function(id, title, url, activities) {
666                    var result = menu.newMenuItem(activitiesLaunchersMenu);
667                    result.text = title;
668
669                    result.visible = true;
670                    result.checkable = true;
671
672                    result.checked = activities.some(function(activity) { return activity === id });
673
674                    result.clicked.connect(
675                        function() {
676                            if (result.checked) {
677                                tasksModel.requestAddLauncherToActivity(url, id);
678                            } else {
679                                tasksModel.requestRemoveLauncherFromActivity(url, id);
680                            }
681                        }
682                    );
683
684                    return result;
685                }
686
687                if (menu.visualParent === null) return;
688
689                var url = menu.get(atm.LauncherUrlWithoutIcon);
690
691                var activities = tasksModel.launcherActivities(url);
692
693                var NULL_UUID = "00000000-0000-0000-0000-000000000000";
694
695                createNewItem(NULL_UUID, i18n("On All Activities"), url, activities);
696
697                if (activityInfo.numberOfRunningActivities <= 1) {
698                    return;
699                }
700
701                createNewItem(activityInfo.currentActivity, i18n("On The Current Activity"), url, activities);
702
703                menu.newSeparator(activitiesLaunchersMenu);
704
705                var runningActivities = activityInfo.runningActivities();
706
707                runningActivities.forEach(function(id) {
708                    createNewItem(id, activityInfo.activityName(id), url, activities);
709                });
710            }
711
712            Component.onCompleted: {
713                menu.onVisualParentChanged.connect(refresh);
714                refresh();
715            }
716        }
717    }
718
719    PlasmaComponents.MenuItem {
720        visible: (visualParent
721                && plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
722                && !launcherToggleAction.visible
723                && !showLauncherInActivitiesItem.visible)
724
725        text: i18n("Unpin from Task Manager")
726        icon: "window-unpin"
727
728        onClicked: {
729            tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon));
730        }
731    }
732
733    PlasmaComponents.MenuItem { separator: true }
734
735    PlasmaComponents.MenuItem {
736        id: closeWindowItem
737        visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
738
739        enabled: visualParent && get(atm.IsClosable) === true
740
741        text: i18n("&Close")
742        icon: "window-close"
743
744        onClicked: tasksModel.requestClose(modelIndex)
745    }
746}
747