1/*
2    SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org>
3    SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com>
4    SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7import QtQuick 2.0
8import QtQuick.Layouts 1.1
9import QtGraphicalEffects 1.0
10
11import org.kde.plasma.core 2.0 as PlasmaCore
12import org.kde.plasma.components 2.0 as PlasmaComponents
13import org.kde.plasma.plasmoid 2.0
14
15import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet
16
17import org.kde.latte.core 0.2 as LatteCore
18import org.kde.latte.private.tasks 0.1 as LatteTasks
19
20import org.kde.latte.abilities.items 0.1 as AbilityItem
21
22import "animations" as TaskAnimations
23
24AbilityItem.BasicItem {
25    id: taskItem
26    visible: false
27    objectName: "TaskItem"
28
29    isHidden: !visible || isForcedHidden
30
31    isHiddenSpacerForcedShow: taskItem.inAttentionBuiltinAnimation || taskItem.inFastRestoreAnimation
32    isHiddenSpacerAnimated: taskItem.inFastRestoreAnimation
33                            || showWindowAnimation.running
34                            || root.inActivityChange
35                            || taskItem.inRemoveStage
36                            || (taskItem.containsMouse && inAttentionBuiltinAnimation && taskItem.parabolicItem.zoom!==taskItem.abilities.parabolic.factor.zoom)
37
38    isMonochromaticForcedContentItem: plasmoid.configuration.forceMonochromaticIcons
39    monochromizedItem: taskIcon.monochromizedItem
40
41    isSeparatorHidden: isSeparator && (lastValidIndex > taskItem.abilities.indexer.lastVisibleItemIndex)
42    isSeparatorInRealLength: isSeparator && root.dragSource
43
44    containsMouse: taskMouseArea.containsMouse || parabolicAreaContainsMouse
45    thinTooltipText: {
46        if (root.showPreviews && !isLauncher) {
47            return "";
48        }
49
50        return isWindow ? model.display : model.AppName;
51    }
52
53    preserveIndicatorInInitialPosition: inBouncingAnimation || inAttentionBuiltinAnimation || inNewWindowBuiltinAnimation
54
55    parabolicItem.isParabolicEventBlocked: root.dragSource
56                                           || !hoverEnabled
57                                           || !taskItem.abilities.myView.isShownFully
58                                           || inAnimation
59                                           || (inBlockingAnimation && !(inAttentionBuiltinAnimation || inFastRestoreAnimation))
60    parabolicItem.isUpdatingOnlySpacers: inAttentionBuiltinAnimation || inBouncingAnimation
61
62    property alias hoverEnabled: taskMouseArea.hoverEnabled
63    property alias pressed: taskMouseArea.pressed
64
65    property bool delayingRemove: ListView.delayRemove
66    //states that exist in windows in a Group of windows
67    property bool hasActive: isActive
68    property bool hasMinimized: (IsGroupParent === true) ? subWindows.hasMinimized : isMinimized
69    property bool hasShown: (IsGroupParent === true) ? subWindows.hasShown : !isMinimized && isWindow
70    property bool inAttention: isDemandingAttention && plasmoid.status === PlasmaCore.Types.NeedsAttentionStatus ? true : false
71
72    /*animations flags*/
73    property bool inAnimation: true
74    property bool inAddRemoveAnimation: true
75    property bool inAttentionBuiltinAnimation: false
76    property bool inBlockingAnimation: false
77    property bool inBouncingAnimation: false
78    property bool inFastRestoreAnimation: false
79    property bool inNewWindowBuiltinAnimation: false
80    property bool inPopup: false
81    property bool inRemoveStage: false
82
83    property bool isLauncherBuiltinAnimationRunning: false
84    property bool isLauncherAnimationRunning: isLauncherBuiltinAnimationRunning
85                                              || (taskItem.abilities.indicators.info.providesTaskLauncherAnimation && isIndicatorTaskLauncherAnimationRunning)
86
87    //! after clicking to show/hide preview enter events are trigerred even though the should not
88    property bool showPreviewsIsBlockedFromReleaseEvent: false
89
90    property bool isAbleToShowPreview: true
91    property bool isActive: (IsActive === true) ? true : false
92    property bool isDemandingAttention: (IsDemandingAttention === true) ? true : false
93    property bool isDragged: false
94    property bool isGroupable: (IsGroupable === true) ? true : false
95    property bool isGroupParent: (IsGroupParent === true) ? true : false
96    property bool isForcedHidden: false
97    property bool isLauncher: (IsLauncher === true) ? true : false
98    property bool hasShownLauncher:  (taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrl)
99                                      || taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrlWithIcon))
100                                     && !root.inActivityChange /*update trigger when changing current activity*/
101    property bool isMinimized: (IsMinimized === true) ? true : false
102    property bool isStartup: (IsStartup === true) ? true : false
103    property bool isWindow: (IsWindow === true) ? true : false
104
105    property bool canPublishGeometries: (isWindow || isStartup || isGroupParent) && visible && width>=taskItem.abilities.metrics.iconSize && height>=taskItem.abilities.metrics.iconSize
106                                        && !taskItem.delayingRemove
107                                        && (taskItem.parabolicItem.zoom===1 || taskItem.parabolicItem.zoom===taskItem.abilities.parabolic.factor.zoom) //don't publish during zoom animation
108
109    property bool hoveredFromDragging: (mouseHandler.hoveredItem === taskItem) || (mouseHandler.ignoredItem === taskItem)
110
111    property bool wheelIsBlocked: false
112    property bool hasAddedWaitingLauncher: false
113
114    property int badgeIndicator: 0 //it is used from external apps
115    property int lastValidIndex: -1 //used for the removal animation
116    property int lastButtonClicked: -1;
117    property int pressX: -1
118    property int pressY: -1
119    property int resistanceDelay: 450
120    property int windowsCount: subWindows.windowsCount
121    property int windowsMinimizedCount: subWindows.windowsMinimized
122
123    property string activity: tasksModel.activity
124
125    readonly property var m: model
126    readonly property int pid: model && model.AppPid ? model.AppPid : -1
127    readonly property string appName: model && model.AppName ? model.AppName : ""
128
129    property string modelLauncherUrl: (LauncherUrlWithoutIcon && LauncherUrlWithoutIcon !== null) ? LauncherUrlWithoutIcon : ""
130    property string modelLauncherUrlWithIcon: (LauncherUrl && LauncherUrl !== null) ? LauncherUrl : ""
131    property string launcherUrl: ""
132    property string launcherUrlWithIcon: ""
133    property string launcherName: ""
134
135    readonly property alias hoveredTimer: taskMouseArea.hoveredTimer
136    readonly property alias mouseArea: taskMouseArea
137    readonly property alias subWindows: subWindows
138
139    readonly property alias showWindowAnimation: _showWindowAnimation
140
141    //! Indicator Properties
142    indicator.isTask: true
143    indicator.isLauncher: taskItem.isLauncher || root.disableAllWindowsFunctionality
144    indicator.isStartup: !root.disableAllWindowsFunctionality && taskItem.isStartup
145    indicator.isWindow: !root.disableAllWindowsFunctionality && taskItem.isWindow
146
147    indicator.isActive: !root.disableAllWindowsFunctionality && (taskItem.hasActive
148                                                                 || (root.showPreviews
149                                                                     && (taskItem.isWindow || taskItem.isGroupParent)
150                                                                     && windowsPreviewDlg.activeItem
151                                                                     && (windowsPreviewDlg.activeItem === taskItem)) )
152
153    indicator.isGroup: !root.disableAllWindowsFunctionality && taskItem.isGroupParent
154    indicator.isHovered: taskItem.containsMouse || (windowsPreviewDlg.containsMouse && (toolTipDelegate.parentTask === taskItem))
155    indicator.isMinimized: !root.disableAllWindowsFunctionality && taskItem.isMinimized
156    indicator.isPressed: taskItem.pressed
157    indicator.inAttention: !root.disableAllWindowsFunctionality && taskItem.inAttention
158    indicator.inRemoving: taskItem.inRemoveStage
159
160    indicator.isSquare: true
161
162    indicator.hasActive: !root.disableAllWindowsFunctionality && taskItem.hasActive
163    indicator.hasMinimized: !root.disableAllWindowsFunctionality && taskItem.hasMinimized
164    indicator.hasShown: !root.disableAllWindowsFunctionality && taskItem.hasShown
165    indicator.windowsCount: !root.disableAllWindowsFunctionality ? taskItem.windowsCount : 0
166    indicator.windowsMinimizedCount: !root.disableAllWindowsFunctionality ? taskItem.windowsMinimizedCount : 0
167
168    indicator.scaleFactor: taskItem.parabolicItem.zoom
169    indicator.panelOpacity: taskItem.abilities.myView.backgroundOpacity
170    indicator.shadowColor: taskItem.abilities.myView.itemShadow.shadowSolidColor
171
172    indicator.progressVisible: taskIcon.progressVisible /*since 0.9.2*/
173    indicator.progress: taskIcon.progress /*since 0.9.2*/
174
175    indicator.palette: taskItem.abilities.myView.palette
176
177    indicator.iconBackgroundColor: taskIcon.backgroundColor
178    indicator.iconGlowColor: taskIcon.glowColor
179    //! Indicator Properties
180
181    onModelLauncherUrlChanged: {
182        if (modelLauncherUrl !== ""){
183            launcherUrl = modelLauncherUrl;
184
185            //!extract the launcherName if possible
186            var nameStarts = launcherUrl.lastIndexOf("/");
187            if (nameStarts === -1){
188                nameStarts = launcherUrl.lastIndexOf(":");
189            }
190
191            var nameEnds = launcherUrl.lastIndexOf(".desktop");
192
193            if (nameStarts!==-1 && nameEnds!==-1 && nameStarts<nameEnds) {
194                launcherName = launcherUrl.substring(nameStarts+1,nameEnds);
195            }
196        }
197
198        if (taskItem.abilities.launchers.isSeparator(modelLauncherUrl)){
199            isSeparator = true;
200        } else {
201            isSeparator = false;
202        }
203    }
204
205    onModelLauncherUrlWithIconChanged: {
206        if (modelLauncherUrlWithIcon !== ""){
207            launcherUrlWithIcon = modelLauncherUrlWithIcon;
208        }
209    }
210
211    onHoveredFromDraggingChanged: {
212        if (hoveredFromDragging) {
213            scrollableList.autoScrollFor(taskItem, true);
214        }
215    }
216
217    ////// Audio streams //////
218    property Item audioStreamOverlay
219    property var audioStreams: []
220    readonly property bool hasAudioStream: root.showAudioBadge && audioStreams.length > 0 && !isLauncher
221    readonly property bool playingAudio: hasAudioStream && audioStreams.some(function (item) {
222        return !item.corked
223    })
224
225    readonly property bool muted: hasAudioStream && audioStreams.every(function (item) {
226        return item.muted
227    })
228
229    readonly property int volume: {
230        if (!hasAudioStream){
231            return 0;
232        }
233
234        var maxVolume = 0;
235        for (var i=0; i<audioStreams.length; ++i){
236            if (audioStreams[i].volume > maxVolume)
237                maxVolume = audioStreams[i].volume;
238        }
239
240        return maxVolume;
241    }
242
243    //! Content Item
244    contentItem: TaskIcon{
245        id:taskIcon
246    }
247    //////
248
249    property QtObject contextMenu: null
250
251    signal checkWindowsStates();
252
253    SubWindows{
254        id: subWindows
255
256        property int previousCount: 0
257
258        onWindowsCountChanged: {
259            if (root.disableAllWindowsFunctionality) {
260                return;
261            }
262
263            if ((windowsCount >= 2)
264                    && (windowsCount > previousCount)
265                    && !(taskItem.containsMouse)
266                    && !root.dragSource ){
267                taskItem.taskGroupedWindowAdded();
268            } else if ((windowsCount >= 1)
269                       && (windowsCount < previousCount)
270                       && !root.dragSource
271                       && !taskItem.delayingRemove){
272                //sometimes this is triggered in dragging with no reason
273                taskItem.taskGroupedWindowRemoved();
274            }
275
276            if (windowsCount>=1) {
277                taskItem.slotPublishGeometries();
278            }
279
280            //! workaround in order to update correctly the previousCount
281            //! windowsCount can not return to zero because is such case
282            //! the window task is removed and the launcher is added from
283            //! libtaskmanager
284            if (windowsCount>=1) {
285                previousCount = windowsCount;
286            }
287        }
288    }
289
290    TaskMouseArea {
291        id: taskMouseArea
292    }
293
294    Timer {
295        id: publishGeometryTimer
296        interval: 800
297        repeat: false
298
299        onTriggered: {
300            slotPublishGeometries();
301
302            if (taskItem.abilities.debug.timersEnabled) {
303                console.log("plasmoid timer: publishGeometryTimer called...");
304            }
305        }
306    }
307
308    ////// Values Changes /////
309    //restore scales when there is no zoom factor for that item or
310    //the mouse is out of the ListView
311    // onItemIndexChanged: {
312    //  }
313
314    onAppNameChanged: updateAudioStreams()
315    onPidChanged: updateAudioStreams()
316    onHasAudioStreamChanged: updateAudioStreams()
317
318    onCanPublishGeometriesChanged: {
319        if (canPublishGeometries) {
320            slotPublishGeometries();
321            publishGeometryTimer.start();
322        }
323    }
324
325    onItemIndexChanged: {
326        if (itemIndex>=0) {
327            lastValidTimer.start();
328        }
329    }
330
331    onIsDraggedChanged: {
332        if (isDragged){
333            root.dragSource = taskItem;
334            dragHelper.startDrag(taskItem, model.MimeType, model.MimeData,
335                                 model.LauncherUrlWithoutIcon, model.decoration);
336            pressX = -1;
337            pressY = -1;
338        }
339    }
340
341    onIsMinimizedChanged: {
342        checkWindowsStates();
343    }
344
345    onIsActiveChanged: {
346        checkWindowsStates();
347        if (isActive) {
348            scrollableList.focusOn(taskItem);
349        }
350    }
351
352    onIsSeparatorChanged: {
353        if (isSeparator) {
354            if (tasksExtendedManager.isLauncherToBeMoved(launcherUrl) && itemIndex>=0) {
355                tasksExtendedManager.moveLauncherToCorrectPos(launcherUrl, itemIndex);
356            }
357        }
358    }
359
360    onLauncherUrlChanged: updateBadge();
361
362    onShortcutRequestedActivate: {
363        if (taskItem.isGroupParent) {
364            taskItem.activateNextTask();
365        } else {
366            taskItem.activateTask();
367        }
368    }
369
370    onShortcutRequestedNewInstance: {
371        tasksModel.requestNewInstance(taskItem.modelIndex());
372    }
373
374    ////// End of Values Changes and Signals /////
375
376
377    //! A timer is needed in order to handle also touchpads that probably
378    //! send too many signals very fast. This way the signals per sec are limited.
379    //! The user needs to have a steady normal scroll in order to not
380    //! notice a annoying delay
381    Timer{
382        id: scrollDelayer
383
384        interval: 400
385
386        onTriggered: taskItem.wheelIsBlocked = false;
387    }
388
389    ///////////////// End Of Mouse Area Events ///////////////////
390
391    ///// Handlers for Signals /////
392    function animationStarted(){
393        //    console.log("Animation started: " + index);
394        inAnimation = true;
395    }
396
397    function animationEnded(){
398        //   console.log("Animation ended: " + index);
399        inAnimation = false;
400    }
401
402    function handlerDraggingFinished(){
403        isDragged = false;
404    }
405    ///// End of Handlers //////
406
407
408
409    ///// Helper functions /////
410    function activateNextTask() {
411        subWindows.activateNextTask();
412    }
413
414    function activateLauncher() {
415        if (LatteCore.WindowSystem.compositingActive) {
416            taskItem.taskLauncherActivated();
417            hasAddedWaitingLauncher = true;
418            tasksExtendedManager.addWaitingLauncher(taskItem.launcherUrl);
419        }
420
421        if (root.disableAllWindowsFunctionality) {
422            tasksModel.requestNewInstance(modelIndex());
423        } else {
424            tasksModel.requestActivate(modelIndex());
425        }
426    }
427
428    function activateTask() {
429        if( taskItem.isLauncher || root.disableAllWindowsFunctionality){
430            activateLauncher();
431        } else{
432            if (model.IsGroupParent) {
433                var canPresentWindowsIsSupported = LatteCore.WindowSystem.compositingActive && (root.plasmaGreaterThan522 ? backend.canPresentWindows : backend.canPresentWindows());
434                if (canPresentWindowsIsSupported) {
435                    root.presentWindows(root.plasma515 ? model.WinIdList: model.LegacyWinIdList );
436                }
437            } else {
438                if (windowsPreviewDlg.visible) {
439                    forceHidePreview(8.3);
440                }
441
442                if (isMinimized) {
443                    var i = modelIndex();
444                    tasksModel.requestToggleMinimized(i);
445                    tasksModel.requestActivate(i);
446                } else if (isActive) {
447                    tasksModel.requestToggleMinimized(modelIndex());
448                } else {
449                    tasksModel.requestActivate(modelIndex());
450                }
451            }
452        }
453    }
454
455    function forceHidePreview(debugtext) {
456        showPreviewsIsBlockedFromReleaseEvent = true;
457        hoveredTimer.stop();
458
459        root.forcePreviewsHiding(debugtext);
460    }
461
462    function showPreviewWindow() {
463        if (root.disableAllWindowsFunctionality || !isAbleToShowPreview) {
464            return;
465        }
466
467        if(windowsPreviewDlg.activeItem !== taskItem){
468            if (!taskItem.abilities.myView.isReady
469                    || (taskItem.abilities.myView.isReady && taskItem.abilities.myView.isShownFully)) {
470                taskItem.preparePreviewWindow(false);
471                windowsPreviewDlg.show(taskItem);
472            }
473        }
474    }
475
476    function hidePreviewWindow() {
477        if(windowsPreviewDlg.activeItem === taskItem){
478            windowsPreviewDlg.hide("14.1");
479        }
480    }
481
482    function preparePreviewWindow(hideClose){
483        windowsPreviewDlg.visualParent = tooltipVisualParent;
484        toolTipDelegate.parentTask = taskItem;
485        toolTipDelegate.rootIndex = tasksModel.makeModelIndex(itemIndex, -1);
486
487        toolTipDelegate.hideCloseButtons = hideClose;
488
489        toolTipDelegate.appName = Qt.binding(function() {
490            return model.AppName;
491        });
492
493        if (!isLauncher) {
494            toolTipDelegate.pidParent = Qt.binding(function() {
495                return model.AppPid;
496            });
497        } else {
498            toolTipDelegate.pidParent = -1;
499        }
500
501        toolTipDelegate.windows = Qt.binding(function() {
502            return root.plasma515 ? model.WinIdList : model.LegacyWinIdList ;
503        });
504        toolTipDelegate.isGroup = Qt.binding(function() {
505            return model.IsGroupParent == true;
506        });
507        toolTipDelegate.icon = Qt.binding(function() {
508            return model.decoration;
509        });
510        toolTipDelegate.launcherUrl = Qt.binding(function() {
511            return model.LauncherUrlWithoutIcon;
512        });
513        toolTipDelegate.isLauncher = Qt.binding(function() {
514            return model.IsLauncher == true;
515        });
516        toolTipDelegate.isMinimizedParent = Qt.binding(function() {
517            return model.IsMinimized == true;
518        });
519        toolTipDelegate.displayParent = Qt.binding(function() {
520            return model.display;
521        });
522        toolTipDelegate.genericName = Qt.binding(function() {
523            return model.GenericName;
524        });
525        toolTipDelegate.virtualDesktopParent = Qt.binding(function() {
526            return (model.VirtualDesktops !== undefined && model.VirtualDesktops.length > 0) ? model.VirtualDesktops : [0];
527        });
528        toolTipDelegate.isOnAllVirtualDesktopsParent = Qt.binding(function() {
529            return model.IsOnAllVirtualDesktops == true;
530        });
531        toolTipDelegate.activitiesParent = Qt.binding(function() {
532            return model.Activities;
533        });
534    }
535
536    ///window previews///
537    function generateSubText(task) {
538        var subTextEntries = new Array();
539
540        if (!plasmoid.configuration.showOnlyCurrentDesktop
541                && virtualDesktopInfo.numberOfDesktops > 1
542                && model.IsOnAllVirtualDesktops !== true
543                && model.VirtualDesktop != -1
544                && model.VirtualDesktop != undefined) {
545            subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[model.VirtualDesktop - 1]));
546        }
547
548        if (model.Activities == undefined) {
549            return subTextEntries.join("\n");
550        }
551
552        if (model.Activities.length == 0 && activityInfo.numberOfRunningActivities > 1) {
553            subTextEntries.push(i18nc("Which virtual desktop a window is currently on",
554                                      "Available on all activities"));
555        } else if (model.Activities.length > 0) {
556            var activityNames = new Array();
557
558            for (var i = 0; i < model.Activities.length; i++) {
559                var activity = model.Activities[i];
560
561                if (plasmoid.configuration.showOnlyCurrentActivity) {
562                    if (activity != activityInfo.currentActivity) {
563                        activityNames.push(activityInfo.activityName(model.Activities[i]));
564                    }
565                } else if (activity != activityInfo.currentActivity) {
566                    activityNames.push(activityInfo.activityName(model.Activities[i]));
567                }
568            }
569
570            if (plasmoid.configuration.showOnlyCurrentActivity) {
571                if (activityNames.length > 0) {
572                    subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)",
573                                              "Also available on %1", activityNames.join(", ")));
574                }
575            } else if (activityNames.length > 0) {
576                subTextEntries.push(i18nc("Which activities a window is currently on",
577                                          "Available on %1", activityNames.join(", ")));
578            }
579        }
580
581        return subTextEntries.join("\n");
582    }
583    ///window previews////
584
585    function modelIndex(){
586        return tasksModel.makeModelIndex(index);
587    }
588
589    function showContextMenu(args) {
590        if (isSeparator && !root.inEditMode)
591            return;
592
593        if (!root.contextMenu) {
594            contextMenu = root.createContextMenu(taskItem, modelIndex(), args);
595            contextMenu.show();
596        } else {
597            //! make sure that context menu isnt deleted multiple times and creates a crash
598            //! bug case: 397635
599            var cMenu = root.contextMenu;
600            root.contextMenu = null;
601            cMenu.destroy();
602        }
603    }
604
605    function modifierAccepted(mouse){
606        if (mouse.modifiers & root.modifierQt){
607            if ((mouse.button === Qt.LeftButton && root.modifierClick === LatteTasks.Types.LeftClick)
608                    || (mouse.button === Qt.MiddleButton && root.modifierClick === LatteTasks.Types.MiddleClick)
609                    || (mouse.button === Qt.RightButton && root.modifierClick === LatteTasks.Types.RightClick))
610                return true;
611        }
612
613        return false;
614    }
615
616    function setBlockingAnimation(value){
617        inBlockingAnimation = value;
618    }
619
620    function slotShowPreviewForTasks(group) {
621        if (group === taskItem && !windowsPreviewDlg.visible) {
622            preparePreviewWindow(true);
623            windowsPreviewDlg.show(taskItem);
624        }
625    }
626
627    function slotPublishGeometries() {
628        //! this way we make sure that layouts that are in different activities that the current layout
629        //! don't publish their geometries
630        if ( canPublishGeometries && (!taskItem.abilities.myView.isReady || (taskItem.abilities.myView.isReady && taskItem.abilities.myView.inCurrentLayout()))) {
631            var globalChoords = backend.globalRect(taskItem.parabolicItem.contentItemContainer);
632            var limits = backend.globalRect(scrollableList);
633
634            //! Limit the published geometries boundaries at scrolling area boundaries
635            var adjX = Math.min(limits.x+limits.width, Math.max(limits.x, globalChoords.x));
636            var adjY = Math.min(limits.y+limits.height, Math.max(limits.y, globalChoords.y));
637
638            var length = taskItem.abilities.metrics.iconSize * taskItem.parabolicItem.zoom;
639            var thickness = length;
640
641            //! Magic Lamp effect doesn't like coordinates outside the screen and
642            //! width,heights of zero value... So we now normalize the geometries
643            //! sent in order to avoid such circumstances
644            if (root.vertical) {
645                if (adjY !== globalChoords.y) {
646                    if (((globalChoords.y+globalChoords.height) < limits.y) || (globalChoords.y)>(limits.y+limits.height)) {
647                        //! totally out of boundaries
648                        length = 4;
649                    } else {
650                        //! semi-part out of boundaries
651                        length = Math.max(4, Math.abs(adjY - globalChoords.y));
652                    }
653
654                    globalChoords.height = length;
655                }
656            } else {
657                if (adjX !== globalChoords.x) {
658                    if (((globalChoords.x+globalChoords.width) < limits.x) || (globalChoords.x)>(limits.x+limits.width)) {
659                        //! totally out of boundaries
660                        length = 4;
661                    } else {
662                        //! semi-part out of boundaries
663                        length = Math.max(4, Math.abs(adjX - globalChoords.x));
664                    }
665
666                    globalChoords.width = length;
667                }
668            }
669
670            globalChoords.x = adjX;
671            globalChoords.y = adjY;
672
673            if (taskItem.abilities.myView.isHidden) {
674                if (root.location === PlasmaCore.Types.BottomEdge) {
675                    globalChoords.y = taskItem.abilities.myView.screenGeometry.y + taskItem.abilities.myView.screenGeometry.height-1;
676                    globalChoords.height = 1;
677                } else if (root.location === PlasmaCore.Types.TopEdge) {
678                    globalChoords.y = taskItem.abilities.myView.screenGeometry.y+1;
679                    globalChoords.height = 1;
680                } else if (root.location === PlasmaCore.Types.LeftEdge) {
681                    globalChoords.x = taskItem.abilities.myView.screenGeometry.x+1;
682                    globalChoords.width = 1;
683                } else if (root.location === PlasmaCore.Types.RightEdge) {
684                    globalChoords.x = taskItem.abilities.myView.screenGeometry.x + taskItem.abilities.myView.screenGeometry.width - 1;
685                    globalChoords.width = 1;
686                }
687            }
688
689            tasksModel.requestPublishDelegateGeometry(taskItem.modelIndex(), globalChoords, taskItem);
690        }
691    }
692
693    function slotWaitingLauncherRemoved(launch) {
694        if ((isWindow || isStartup || isLauncher) && !visible && launch === launcherUrl) {
695            if (!taskItem.abilities.indicators.info.providesTaskLauncherAnimation) {
696                //! this is needed only from in-built launcher animation to restore zoom smoothly
697                taskItem.parabolicItem.zoom = 1;
698            }
699            visible = true;
700        }
701    }
702
703
704    function updateAudioStreams() {
705        if (root.dragSource !== null) {
706            audioStreams = [];
707            return;
708        }
709
710        var pa = pulseAudio.item;
711        if (!pa) {
712            audioStreams = [];
713            return;
714        }
715
716        var streams = pa.streamsForPid(taskItem.pid);
717        if (streams.length) {
718            pa.registerPidMatch(taskItem.appName);
719        } else {
720            // We only want to fall back to appName matching if we never managed to map
721            // a PID to an audio stream window. Otherwise if you have two instances of
722            // an application, one playing and the other not, it will look up appName
723            // for the non-playing instance and erroneously show an indicator on both.
724            if (!pa.hasPidMatch(taskItem.appName)) {
725                streams = pa.streamsForAppName(taskItem.appName);
726            }
727        }
728
729        // fix a binding loop concerning audiostreams, the audiostreams
730        // should be updated only when they have changed
731        var changed = false;
732
733        if (streams.length !== audioStreams.length) {
734            changed = true;
735        } else {
736            for(var i=0; i<streams.length; ++i) {
737                if (streams[i] !== audioStreams[i]) {
738                    changed = true;
739                    break;
740                }
741            }
742        }
743
744        if (changed) {
745            taskItem.audioStreams = streams;
746        }
747    }
748
749    function onLauncherChanged(launcher) {
750        if ((root.showWindowsOnlyFromLaunchers || root.disableAllWindowsFunctionality) && launcher === launcherUrl) {
751            updateVisibilityBasedOnLaunchers()
752        }
753    }
754
755    function updateVisibilityBasedOnLaunchers(){
756        var launcherExists = !(((tasksModel.launcherPosition(taskItem.launcherUrl) == -1)
757                                && (tasksModel.launcherPosition(taskItem.launcherUrlWithIcon) == -1) )
758                               || !taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrl));
759
760        if (root.showWindowsOnlyFromLaunchers || root.disableAllWindowsFunctionality) {
761            var hideWindow =  !launcherExists && (taskItem.isWindow || root.disableAllWindowsFunctionality);
762
763            if (hideWindow) {
764                isForcedHidden = true;
765                taskRealRemovalAnimation.start();
766            } else if (launcherExists && taskItem.isWindow && !taskItem.isVisible) {
767                showWindowAnimation.showWindow();
768                isForcedHidden = false;
769            }
770        } else {
771            var showWindow =  !launcherExists && taskItem.isWindow;
772
773            if (showWindow) {
774                showWindowAnimation.showWindow();
775                isForcedHidden = false;
776            }
777        }
778    }
779
780    function toggleMuted() {
781        if (muted) {
782            taskItem.audioStreams.forEach(function (item) { item.unmute(); });
783        } else {
784            taskItem.audioStreams.forEach(function (item) { item.mute(); });
785        }
786    }
787
788    function increaseVolume() {
789        taskItem.audioStreams.forEach(function (item) { item.increaseVolume(); });
790    }
791
792    function decreaseVolume() {
793        taskItem.audioStreams.forEach(function (item) { item.decreaseVolume(); });
794    }
795
796    function updateBadge() {
797        var badger = root.getBadger(launcherUrl);
798
799        if (badger) {
800            badgeIndicator = parseInt(badger.value);
801        } else {
802            badgeIndicator = 0;
803        }
804    }
805
806    Connections {
807        target: pulseAudio.item
808        ignoreUnknownSignals: true // Plasma-PA might not be available
809        onStreamsChanged: taskItem.updateAudioStreams()
810    }
811
812    //fix bug #478, when changing form factor sometimes the tasks are not positioned
813    //correctly, in such case we make a fast reinitialization for the sizes
814    Connections {
815        target: plasmoid
816        onFormFactorChanged:{
817            taskItem.inAddRemoveAnimation = false;
818        }
819    }
820
821    Connections {
822        target: root
823        //trying to fix #440, showing the audio icon indicator to irrelevant tasks
824        //after dragging an existent task with audio
825        onDragSourceChanged: taskItem.updateAudioStreams()
826        onShowAudioBadgeChanged: taskItem.updateAudioStreams()
827
828        onDisableAllWindowsFunctionalityChanged: {
829            if (!root.inEditMode) {
830                return;
831            }
832
833            taskItem.updateVisibilityBasedOnLaunchers();
834        }
835
836        onShowWindowsOnlyFromLaunchersChanged: {
837            if (!root.inEditMode) {
838                return;
839            }
840
841            taskItem.updateVisibilityBasedOnLaunchers();
842        }
843
844        onInActivityChangeChanged: {
845            if ((root.showWindowsOnlyFromLaunchers || root.disableAllWindowsFunctionality) && !root.inActivityChange) {
846                taskItem.updateVisibilityBasedOnLaunchers();
847            }
848        }
849    }
850
851    Connections {
852        target: scrollableList
853        onAnimationsFinishedChanged: {
854            if (scrollableList.animationsFinished) {
855                taskItem.slotPublishGeometries();
856            }
857        }
858    }
859
860    Connections {
861        target: taskItem.abilities.myView
862        onIsHiddenChanged: {
863            if (taskItem.abilities.myView.isHidden) {
864                taskItem.slotPublishGeometries();
865            }
866        }
867
868        onIsShownFullyChanged: {
869            if (taskItem.abilities.myView.isShownFully) {
870                taskItem.slotPublishGeometries();
871            }
872        }
873    }
874
875    ///// End of Helper functions ////
876
877    Component.onCompleted: {
878        parabolicItem.opacity = 0;
879
880        root.draggingFinished.connect(handlerDraggingFinished);
881        root.publishTasksGeometries.connect(slotPublishGeometries);
882        root.showPreviewForTasks.connect(slotShowPreviewForTasks);
883
884        taskItem.abilities.launchers.launcherChanged.connect(onLauncherChanged);
885        taskItem.abilities.launchers.launcherRemoved.connect(onLauncherChanged);
886
887        //startup without launcher
888        var hideStartup =  ((!hasShownLauncher || !taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrl))
889                            && taskItem.isStartup);
890
891        if (!LatteCore.WindowSystem.compositingActive) {
892            visible = true;
893        } else if ( (isWindow || isStartup || isLauncher) && tasksExtendedManager.waitingLauncherExists(launcherUrl)) {
894            tasksExtendedManager.waitingLauncherRemoved.connect(slotWaitingLauncherRemoved);
895            visible = false;
896        } else if (hideStartup){
897            visible = false;
898        } else {
899            visible = true;
900        }
901
902        showWindowAnimation.showWindow();
903        updateAudioStreams();
904    }
905
906    Component.onDestruction: {
907        root.draggingFinished.disconnect(handlerDraggingFinished);
908        root.publishTasksGeometries.disconnect(slotPublishGeometries);
909        root.showPreviewForTasks.disconnect(slotShowPreviewForTasks);
910
911        taskItem.abilities.launchers.launcherChanged.disconnect(onLauncherChanged);
912        taskItem.abilities.launchers.launcherRemoved.disconnect(onLauncherChanged);
913
914        tasksExtendedManager.waitingLauncherRemoved.disconnect(slotWaitingLauncherRemoved);
915
916        taskItem.parabolicItem.sendEndOfNeedBothAxisAnimation();
917    }
918
919    /////Animations
920    TaskAnimations.ShowWindowAnimation{ id: _showWindowAnimation }
921
922    // when changing activities and desktops the index of the tasks
923    // is updated immediately to -1, this timer protects this indexing
924    // change in order to provide a beautiful removal tasks animation
925    Timer {
926        id: lastValidTimer
927        interval: 100 ///the interval does not follow the animations timing
928        repeat: false
929
930        onTriggered: {
931            if (taskItem.itemIndex >= 0){
932                taskItem.lastValidIndex = taskItem.itemIndex;
933            }
934
935            if (taskItem.abilities.debug.timersEnabled) {
936                console.log("plasmoid timer: lastValidTimer called...");
937            }
938        }
939    }
940
941    ///Item's Removal Animation
942    ListView.onRemove: TaskAnimations.RealRemovalAnimation{ id: taskRealRemovalAnimation }
943
944    onIsLauncherAnimationRunningChanged: {
945        if (!isLauncherAnimationRunning && taskRealRemovalAnimation.paused) {
946            taskRealRemovalAnimation.resume();
947        }
948    }
949}// main Item
950
951