├── metadata.desktop ├── README.md ├── contents ├── config │ ├── config.qml │ └── main.xml ├── tools │ └── Tools.js └── ui │ ├── config │ ├── CustomComboBox.qml │ ├── LockItem.qml │ ├── SlidingBox.qml │ ├── SubstitutionsPopup.qml │ ├── ConfigBehavior.qml │ └── ConfigAppearance.qml │ ├── ActionsMouseArea.qml │ ├── LatteWindowsTracker.qml │ ├── Broadcaster.qml │ ├── PlasmaTasksModel.qml │ ├── TitleLayout.qml │ └── main.qml ├── CHANGELOG.md └── LICENSE /metadata.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Window Title 3 | Comment=Shows the application title and icon of the active window 4 | 5 | Encoding=UTF-8 6 | Icon=preferences-desktop 7 | Keyword=window title icon 8 | Type=Service 9 | 10 | X-Plasma-API=declarativeappletscript 11 | X-Plasma-MainScript=ui/main.qml 12 | 13 | X-KDE-PluginInfo-Author=Michail Vourlakos 14 | X-KDE-PluginInfo-Category=Windows and Tasks 15 | X-KDE-PluginInfo-Email=mvourlakos@gmail.com 16 | X-KDE-PluginInfo-License=GPLv2 17 | X-KDE-PluginInfo-Name=org.kde.windowtitle 18 | X-KDE-PluginInfo-Version=0.7.1 19 | X-KDE-PluginInfo-Website=https://github.com/psifidotos/applet-window-title 20 | X-KDE-ServiceTypes=Plasma/Applet 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Window Title Applet 2 | 3 | This is a Plasma 5 applet that shows the current window title and icon in your panels. This plasmoid is coming from [Latte land](https://phabricator.kde.org/source/latte-dock/repository/master/) but it can also support Plasma panels. 4 | 5 |

6 |
7 | Window Title left to Plasma 5 global menu 8 |

9 | 10 |

11 |
12 | Appearance Settings 13 |

14 | 15 |

16 |
17 | Behavior Settings 18 |

19 | 20 | # Requires 21 | 22 | - Plasma >= 5.8 23 | - KDeclarative 24 | - Kirigami2 25 | 26 | # Install 27 | 28 | This is a QML applet and as such it can be easily installed from Plasma 5 Widgets Explorer or alternative you can execute `kpackagetool5 -i .` in the root directory of the applet. 29 | 30 | 31 | -------------------------------------------------------------------------------- /contents/config/config.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.0 21 | 22 | import org.kde.plasma.configuration 2.0 23 | 24 | ConfigModel { 25 | ConfigCategory { 26 | name: i18n("Appearance") 27 | icon: "preferences-desktop-display-color" 28 | source: "config/ConfigAppearance.qml" 29 | } 30 | ConfigCategory { 31 | name: i18n("Behavior") 32 | icon: "preferences-desktop" 33 | source: "config/ConfigBehavior.qml" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contents/tools/Tools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | function qBound(min,value,max) 21 | { 22 | return Math.max(Math.min(max, root.width - 150), min); 23 | } 24 | 25 | function cleanStringListItem(item) 26 | { 27 | //console.log(item + " * " + item.length + " * " + item.indexOf('"') + " * " + item.lastIndexOf('"')); 28 | if (item.length>=2 && item.indexOf('"')===0 && item.lastIndexOf('"')===item.length-1) { 29 | return item.substring(1, item.length-1); 30 | } else { 31 | return item; 32 | } 33 | } 34 | 35 | function applySubstitutes(text) 36 | { 37 | var minSize = Math.min(plasmoid.configuration.subsMatch.length, plasmoid.configuration.subsReplace.length); 38 | 39 | for (var i = 0; i 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.0 21 | import QtQuick.Controls 1.0 22 | import QtQuick.Controls 2.2 as Controls22 23 | import QtQuick.Layouts 1.0 24 | 25 | Controls22.ComboBox{ 26 | id: combobox 27 | Layout.minimumWidth: 270 28 | Layout.preferredWidth: 350 29 | Layout.maximumWidth: 0.3 * root.width 30 | 31 | model: choices 32 | 33 | property var choices: [] 34 | 35 | signal choiceClicked(int index); 36 | 37 | Connections{ 38 | target: popup 39 | onClosed: root.forceActiveFocus(); 40 | } 41 | 42 | delegate: MouseArea{ 43 | width: combobox.width 44 | height: combobox.height 45 | hoverEnabled: true 46 | 47 | onClicked: { 48 | combobox.currentIndex = index; 49 | combobox.choiceClicked(index); 50 | combobox.popup.close(); 51 | } 52 | 53 | Rectangle{ 54 | id:delegateBackground 55 | anchors.fill: parent 56 | color: { 57 | if (containsMouse) { 58 | return palette.highlight; 59 | } 60 | if (combobox.currentIndex === index) { 61 | return selectedColor; 62 | } 63 | 64 | return "transparent"; 65 | } 66 | 67 | readonly property color selectedColor: Qt.rgba(palette.highlight.r, palette.highlight.g, palette.highlight.b, 0.5); 68 | 69 | Label{ 70 | id: label 71 | anchors.left: parent.left 72 | anchors.leftMargin: units.smallSpacing 73 | anchors.verticalCenter: parent.verticalCenter 74 | text: choices[index]; 75 | color: containsMouse ? palette.highlightedText : palette.text 76 | } 77 | } 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /contents/ui/config/LockItem.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.9 21 | import QtQuick.Controls 1.3 22 | import QtQuick.Layouts 1.0 23 | 24 | import org.kde.plasma.core 2.0 as PlasmaCore 25 | import org.kde.kquickcontrolsaddons 2.0 as KQuickAddons 26 | 27 | Item{ 28 | id: main 29 | 30 | property bool locked: true 31 | 32 | readonly property int verticalSubHeight: height - (button.height/2) 33 | 34 | ColumnLayout{ 35 | id: column 36 | spacing: 0 37 | opacity: locked ? 1 : 0.5 38 | 39 | Rectangle{ 40 | id: subRectTop 41 | Layout.minimumWidth: button.width/2 + Layout.minimumHeight/2 42 | Layout.minimumHeight: 3 43 | Layout.maximumWidth: Layout.minimumWidth 44 | Layout.maximumHeight: Layout.minimumHeight 45 | 46 | color: palette.text 47 | } 48 | 49 | Rectangle { 50 | Layout.leftMargin: subRectTop.Layout.minimumWidth - subRectTop.Layout.minimumHeight 51 | Layout.minimumWidth: subRectTop.Layout.minimumHeight 52 | Layout.minimumHeight: verticalSubHeight 53 | Layout.maximumWidth: Layout.minimumWidth 54 | Layout.maximumHeight: Layout.minimumHeight 55 | color: palette.text 56 | } 57 | 58 | KQuickAddons.QIconItem{ 59 | id: button 60 | width: 24 61 | height: 24 62 | icon: locked ? "lock" : "unlock" 63 | } 64 | 65 | Rectangle { 66 | Layout.leftMargin: subRectTop.Layout.minimumWidth - subRectTop.Layout.minimumHeight 67 | Layout.minimumWidth: subRectTop.Layout.minimumHeight 68 | Layout.minimumHeight: verticalSubHeight 69 | Layout.maximumWidth: Layout.minimumWidth 70 | Layout.maximumHeight: Layout.minimumHeight 71 | color: palette.text 72 | } 73 | 74 | Rectangle{ 75 | Layout.minimumWidth: subRectTop.Layout.minimumWidth 76 | Layout.minimumHeight: subRectTop.Layout.minimumHeight 77 | Layout.maximumWidth: Layout.minimumWidth 78 | Layout.maximumHeight: Layout.minimumHeight 79 | color: palette.text 80 | } 81 | } 82 | 83 | MouseArea{ 84 | anchors.fill: column 85 | onClicked: locked = !locked; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### CHANGELOG 2 | 3 | #### Version 0.7.1 4 | 5 | * elide title in the middle of text 6 | * change window title dash to n-dash 7 | 8 | #### Version 0.7.0 9 | 10 | * small fixes all around the stack 11 | * fixes for unity-mode in latte panels 12 | 13 | #### Version 0.6.0 14 | 15 | * support more window actions with mouse: 16 | - Double Left Click : Maximize/restore active window 17 | - Middle Click : Close active window 18 | - Scroll up : Cycle between tasks 19 | - Scroll down : Minimize tasks 20 | - Ctrl + Scroll up : Maximize active window (this is also possible with double left click) 21 | - Ctrl + Scroll down : Restore active window from maximized state 22 | 23 | * add a placeholder-text option to be shown when no window is visible and the user does not want the current Activity name to be drawn 24 | * option to disable the title tooltip 25 | 26 | #### Version 0.5.2 27 | 28 | * fix bold font kerning issues 29 | 30 | #### Version 0.5.1 31 | 32 | * improve window title discovery for flatpaks 33 | * do not show empty window title when there are information from the application 34 | 35 | #### Version 0.5.0 36 | 37 | * use Latte Windows Tracking interface when in Latte v0.9 38 | * do not use icon size in computations when icon is not visible 39 | * do not draw Application Name/Window Title separator when any of these two could not be retrieved 40 | 41 | #### Version 0.4.1 42 | 43 | * improve signaling for Broadcaster 44 | * close ComboBox popup after selecting an item 45 | * add some debug messages 46 | 47 | #### Version 0.4 48 | 49 | * dont elide if there is free space in the panel 50 | * improve metrics and calculations for better visual results 51 | * support Unity behavior when used with Window AppMenu 0.4 in latest Latte master 52 | * update Appearance settings 53 | 54 | #### Version 0.3 55 | 56 | * improve defaults and behavior for Latte edit mode 57 | * elide text if applet exceeds panel space 58 | * elide only title when needed 59 | * improvements for settings window 60 | * synchronize scrolling for substitutions elements 61 | * support new Latte Communicator mechanism from Latte git version 62 | 63 | #### Version 0.2 64 | 65 | * Fifth Text Style, "Do not show any text" 66 | * Font option to capitalize first letters 67 | * Multi-screen aware in order to locate active window in the current screen or at all screens 68 | * Support user-set maximum icon size 69 | * Double click to maximize/restore active window (for plasma panels) 70 | * Option to hide Activity information when no window is active 71 | * Split settings to Appearance and Behavior 72 | * Show tooltip when hovering to provide more information 73 | * Support Application Name substitutions in Behavior settings page 74 | 75 | #### Version 0.1 76 | 77 | * Four different text styles, [Application, Title, Application - Title, Title - Application] 78 | * Support both horizontal and vertical alignments 79 | * Show window icon 80 | * Maximize window icon to applet thickness (optional) 81 | * Bold/Italic font 82 | * Set maximum length in pixels or use all the required space to show all contents 83 | * Set spacing between icon and title 84 | * Left/Right or Top/Bottom margins 85 | * Multi-screen ready 86 | * Show activity name/icon when there is not any active window 87 | * Double click in order to maximize/restore active window (for plasma panels) 88 | * Latte v0.9 ready 89 | * support new painting 90 | * support maximize/restore active window 91 | * support dragging active window from latte panel 92 | -------------------------------------------------------------------------------- /contents/ui/config/SlidingBox.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.9 21 | import QtQuick.Controls 1.0 22 | import QtGraphicalEffects 1.0 23 | import QtQuick.Controls 2.2 as Controls22 24 | import QtQuick.Layouts 1.0 25 | 26 | import org.kde.plasma.core 2.0 as PlasmaCore 27 | 28 | Item{ 29 | id: box 30 | clip: true 31 | 32 | x: parent.width/2 - width/2 33 | /*y: slideOutFrom === PlasmaCore.Types.BottomEdge ? 0 34 | height: 0*/ 35 | opacity: 0 36 | 37 | property QtObject contentItem: null 38 | property int slideOutFrom: PlasmaCore.Types.TopEdge 39 | 40 | property bool shown: false 41 | 42 | readonly property int availableWidth: width - 2*12 - 2*units.largeSpacing 43 | readonly property int availableHeight: contentItem.childrenRect.height + 2*units.largeSpacing 44 | readonly property int maximumHeight: availableHeight + 2*12 45 | 46 | onContentItemChanged: { 47 | if (contentItem){ 48 | contentItem.parent = centralItem 49 | } 50 | } 51 | 52 | function slideIn() { 53 | if (slideOutFrom === PlasmaCore.Types.TopEdge) { 54 | height = maximumHeight; 55 | y = -maximumHeight; 56 | } else { 57 | height = maximumHeight; 58 | y = parent.height; 59 | } 60 | 61 | opacity = 1; 62 | shown = true; 63 | } 64 | 65 | function slideOut() { 66 | if (slideOutFrom === PlasmaCore.Types.TopEdge) { 67 | height = 0; 68 | y = 0; 69 | } else { 70 | height = 0; 71 | y = parent.height; 72 | } 73 | 74 | opacity = 0; 75 | shown = false; 76 | } 77 | 78 | Behavior on y{ 79 | NumberAnimation { 80 | duration: 300 81 | easing.type: Easing.OutCubic 82 | } 83 | } 84 | 85 | Behavior on height{ 86 | NumberAnimation { 87 | duration: 300 88 | easing.type: Easing.OutCubic 89 | } 90 | } 91 | 92 | Behavior on opacity{ 93 | NumberAnimation { 94 | duration: 300 95 | easing.type: Easing.OutCubic 96 | } 97 | } 98 | 99 | SystemPalette { 100 | id: palette 101 | } 102 | 103 | Item{ 104 | id: mainElement 105 | width: parent.width 106 | height: contentItem ? maximumHeight : 100 107 | 108 | Rectangle{ 109 | id: centralItem 110 | anchors.fill: parent 111 | anchors.margins: 12 112 | color: palette.alternateBase 113 | border.width: 1 114 | border.color: palette.mid 115 | radius: 1 116 | 117 | layer.enabled: true 118 | layer.effect: DropShadow { 119 | id: shadowElement 120 | radius: 12 121 | fast: true 122 | samples: 2 * radius 123 | color: palette.shadow 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /contents/ui/config/SubstitutionsPopup.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.9 21 | import QtQuick.Controls 1.0 22 | import QtQuick.Controls 2.2 as Controls22 23 | import QtQuick.Layouts 1.0 24 | 25 | import org.kde.plasma.core 2.0 as PlasmaCore 26 | 27 | import "../../tools/Tools.js" as Tools 28 | 29 | SlidingBox { 30 | id: popup 31 | width: Tools.qBound(400, 0.6*page.width, 750) 32 | 33 | property QtObject page: null 34 | 35 | function textAreaToList(text) { 36 | var res = text.split("\n"); 37 | return res; 38 | } 39 | 40 | function listToText(text) { 41 | var res = text.join("\n"); 42 | return res; 43 | } 44 | 45 | contentItem: ColumnLayout{ 46 | id: mainColumn 47 | width: popup.availableWidth 48 | anchors.margins: units.largeSpacing 49 | anchors.centerIn: parent 50 | spacing: units.largeSpacing 51 | 52 | Label{ 53 | Layout.fillWidth: true 54 | horizontalAlignment: Qt.AlignHCenter 55 | font.bold: true 56 | text: i18n("Substitutions") 57 | } 58 | 59 | GridLayout { 60 | columns: 2 61 | Label{ 62 | Layout.fillWidth: true 63 | horizontalAlignment: Qt.AlignHCenter 64 | font.bold: true 65 | text: "Match" 66 | } 67 | Label{ 68 | Layout.fillWidth: true 69 | horizontalAlignment: Qt.AlignHCenter 70 | font.bold: true 71 | text: "Replace with" 72 | } 73 | TextArea{ 74 | id: textAreaMatch 75 | 76 | Layout.fillWidth: true 77 | Layout.fillHeight: true 78 | text: listToText(page.selectedMatches) 79 | 80 | onTextChanged: page.selectedMatches = popup.textAreaToList(text) 81 | 82 | flickableItem.onContentYChanged: { 83 | textAreaReplace.flickableItem.contentY = flickableItem.contentY 84 | } 85 | } 86 | TextArea{ 87 | id: textAreaReplace 88 | 89 | Layout.fillWidth: true 90 | Layout.fillHeight: true 91 | text: listToText(page.selectedReplacements) 92 | onTextChanged: page.selectedReplacements = popup.textAreaToList(text) 93 | 94 | flickableItem.onContentYChanged: { 95 | textAreaMatch.flickableItem.contentY = flickableItem.contentY 96 | } 97 | } 98 | } 99 | 100 | Label{ 101 | Layout.fillWidth: true 102 | horizontalAlignment: Qt.AlignHCenter 103 | font.italic: true 104 | color: "#ff0000" 105 | text: { 106 | if (page.selectedMatches.length !== page.selectedReplacements.length) { 107 | return i18n("Warning: Matches and Replacements do not have the same size..."); 108 | } 109 | 110 | return ""; 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /contents/ui/ActionsMouseArea.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.7 21 | 22 | import org.kde.plasma.plasmoid 2.0 23 | 24 | MouseArea { 25 | id: actionsArea 26 | acceptedButtons: Qt.LeftButton | Qt.MidButton 27 | 28 | property bool wheelIsBlocked: false 29 | 30 | onClicked: { 31 | if (existsWindowActive && mouse.button === Qt.MidButton) { 32 | windowInfoLoader.item.requestClose(); 33 | } 34 | } 35 | 36 | onDoubleClicked: { 37 | if (existsWindowActive && mouse.button === Qt.LeftButton) { 38 | windowInfoLoader.item.toggleMaximized(); 39 | } 40 | } 41 | 42 | onWheel: { 43 | if (wheelIsBlocked || !plasmoid.configuration.actionScrollMinimize) { 44 | return; 45 | } 46 | 47 | wheelIsBlocked = true; 48 | scrollDelayer.start(); 49 | 50 | var delta = 0; 51 | 52 | if (wheel.angleDelta.y>=0 && wheel.angleDelta.x>=0) { 53 | delta = Math.max(wheel.angleDelta.y, wheel.angleDelta.x); 54 | } else { 55 | delta = Math.min(wheel.angleDelta.y, wheel.angleDelta.x); 56 | } 57 | 58 | var angle = delta / 8; 59 | 60 | var ctrlPressed = (wheel.modifiers & Qt.ControlModifier); 61 | 62 | if (angle>10) { 63 | //! upwards 64 | if (!ctrlPressed) { 65 | windowInfoLoader.item.activateNextPrevTask(true); 66 | } else if (windowInfoLoader.item.activeTaskItem 67 | && !windowInfoLoader.item.activeTaskItem.isMaximized){ 68 | windowInfoLoader.item.toggleMaximized(); 69 | } 70 | } else if (angle<-10) { 71 | //! downwards 72 | if (!ctrlPressed) { 73 | if (windowInfoLoader.item.activeTaskItem 74 | && !windowInfoLoader.item.activeTaskItem.isMinimized 75 | && windowInfoLoader.item.activeTaskItem.isMaximized){ 76 | //! maximized 77 | windowInfoLoader.item.activeTaskItem.toggleMaximized(); 78 | } else if (windowInfoLoader.item.activeTaskItem 79 | && !windowInfoLoader.item.activeTaskItem.isMinimized 80 | && !windowInfoLoader.item.activeTaskItem.isMaximized) { 81 | //! normal 82 | windowInfoLoader.item.activeTaskItem.toggleMinimized(); 83 | } 84 | } else if (windowInfoLoader.item.activeTaskItem 85 | && windowInfoLoader.item.activeTaskItem.isMaximized) { 86 | windowInfoLoader.item.activeTaskItem.toggleMaximized(); 87 | } 88 | } 89 | } 90 | 91 | //! A timer is needed in order to handle also touchpads that probably 92 | //! send too many signals very fast. This way the signals per sec are limited. 93 | //! The user needs to have a steady normal scroll in order to not 94 | //! notice a annoying delay 95 | Timer{ 96 | id: scrollDelayer 97 | 98 | interval: 200 99 | onTriggered: actionsArea.wheelIsBlocked = false; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /contents/config/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 0 19 | 20 | 21 | true 22 | 23 | 24 | 25 | true 26 | 27 | 28 | 29 | 36 30 | 31 | 32 | 33 | true 34 | 35 | 36 | 37 | false 38 | 39 | 40 | 41 | true 42 | 43 | 44 | 45 | 0 46 | 47 | 48 | 49 | 0 50 | 51 | 52 | 53 | true 54 | 55 | 56 | 57 | false 58 | 59 | 60 | 61 | false 62 | 63 | 64 | 65 | -1 66 | 67 | 68 | 69 | 4 70 | 71 | 72 | 73 | true 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 0 85 | 86 | 87 | 400 88 | 89 | 90 | 91 | 1500 92 | 93 | 94 | 95 | false 96 | 97 | 98 | 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 2 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 0 118 | 119 | 120 | "Telegram Desktop","Gimp-.*","soffice.bin" 121 | 122 | 123 | 124 | "Telegram","Gimp","LibreOffice" 125 | 126 | 127 | 128 | true 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /contents/ui/LatteWindowsTracker.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.7 21 | 22 | Item { 23 | id: latteWindowsTracker 24 | readonly property bool existsWindowActive: selectedTracker.lastActiveWindow.isValid && !activeTaskItem.isMinimized 25 | readonly property bool existsWindowShown: selectedTracker.lastActiveWindow.isValid && !activeTaskItem.isMinimized 26 | 27 | readonly property QtObject selectedTracker: plasmoid.configuration.filterByScreen ? latteBridge.windowsTracker.currentScreen : latteBridge.windowsTracker.allScreens 28 | 29 | readonly property Item activeTaskItem: Item { 30 | id: taskInfoItem 31 | readonly property bool isMinimized: selectedTracker.lastActiveWindow.isMinimized 32 | readonly property bool isMaximized: selectedTracker.lastActiveWindow.isMaximized 33 | readonly property bool isActive: selectedTracker.lastActiveWindow.isActive 34 | readonly property bool isOnAllDesktops: selectedTracker.lastActiveWindow.isOnAllDesktops 35 | readonly property bool isKeepAbove: selectedTracker.lastActiveWindow.isKeepAbove 36 | 37 | readonly property bool isClosable: selectedTracker.lastActiveWindow.hasOwnProperty("isClosable") ? selectedTracker.lastActiveWindow.isClosable : true 38 | readonly property bool isMinimizable: selectedTracker.lastActiveWindow.hasOwnProperty("isMinimizable") ? selectedTracker.lastActiveWindow.isMinimizable : true 39 | readonly property bool isMaximizable: selectedTracker.lastActiveWindow.hasOwnProperty("isMaximizable") ? selectedTracker.lastActiveWindow.isMaximizable : true 40 | readonly property bool isVirtualDesktopsChangeable: selectedTracker.lastActiveWindow.hasOwnProperty("isVirtualDesktopsChangeable") ? 41 | selectedTracker.lastActiveWindow.isVirtualDesktopsChangeable : true 42 | 43 | readonly property int winId: selectedTracker.lastActiveWindow.hasOwnProperty("winId") ? selectedTracker.lastActiveWindow.winId : -1 44 | 45 | 46 | //! appname / title 47 | property var icon: selectedTracker.lastActiveWindow.icon 48 | 49 | readonly property string appName: modelAppName !== "" ? modelAppName : discoveredAppName 50 | readonly property string modelAppName: selectedTracker.lastActiveWindow.appName 51 | readonly property string modelDisplay: selectedTracker.lastActiveWindow.display 52 | 53 | property string title: "" 54 | property string discoveredAppName: "" 55 | 56 | function cleanupTitle() { 57 | var text = modelDisplay; 58 | var t = text; 59 | var sep = t.lastIndexOf(" —– "); 60 | var spacer = 4; 61 | 62 | if (sep === -1) { 63 | sep = t.lastIndexOf(" -- "); 64 | spacer = 4; 65 | } 66 | 67 | if (sep === -1) { 68 | sep = t.lastIndexOf(" -- "); 69 | spacer = 4; 70 | } 71 | 72 | if (sep === -1) { 73 | sep = t.lastIndexOf(" — "); 74 | spacer = 3; 75 | } 76 | 77 | if (sep === -1) { 78 | sep = t.lastIndexOf(" - "); 79 | spacer = 3; 80 | } 81 | 82 | var dTitle = ""; 83 | var dAppName = ""; 84 | 85 | if (sep>-1) { 86 | dTitle = text.substring(0, sep); 87 | discoveredAppName = text.substring(sep+spacer, text.length); 88 | 89 | //if title starts with application name, swap the found records 90 | if (dTitle.startsWith(modelAppName)) { 91 | var firstPart = dTitle; 92 | dTitle = discoveredAppName; 93 | discoveredAppName = firstPart; 94 | } 95 | } 96 | 97 | if (sep>-1) { 98 | title = dTitle; 99 | } else { 100 | title = t; 101 | } 102 | } 103 | 104 | onModelAppNameChanged: taskInfoItem.cleanupTitle() 105 | onModelDisplayChanged: taskInfoItem.cleanupTitle() 106 | Component.onCompleted: taskInfoItem.cleanupTitle() 107 | } 108 | 109 | function toggleMaximized() { 110 | selectedTracker.lastActiveWindow.requestToggleMaximized(); 111 | } 112 | 113 | function toggleMinimized() { 114 | selectedTracker.lastActiveWindow.requestToggleMinimized(); 115 | } 116 | 117 | function toggleClose() { 118 | selectedTracker.lastActiveWindow.requestClose(); 119 | } 120 | 121 | function togglePinToAllDesktops() { 122 | selectedTracker.lastActiveWindow.requestToggleIsOnAllDesktops(); 123 | } 124 | 125 | function toggleKeepAbove(){ 126 | selectedTracker.lastActiveWindow.requestToggleKeepAbove(); 127 | } 128 | } 129 | 130 | -------------------------------------------------------------------------------- /contents/ui/Broadcaster.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.7 21 | 22 | import org.kde.plasma.plasmoid 2.0 23 | import org.kde.plasma.core 2.0 as PlasmaCore 24 | 25 | Item{ 26 | id: broadcaster 27 | 28 | property bool hiddenFromBroadcast: false 29 | 30 | readonly property bool showAppMenuEnabled: plasmoid.configuration.showAppMenuOnMouseEnter 31 | property bool menuIsPresent: false 32 | property var appMenusRequestCooperation: [] 33 | property int appMenusRequestCooperationCount: 0 34 | 35 | readonly property bool cooperationEstablished: appMenusRequestCooperationCount>0 && isActive 36 | readonly property bool isActive: plasmoid.configuration.appMenuIsPresent && showAppMenuEnabled && (plasmoid.formFactor === PlasmaCore.Types.Horizontal) 37 | 38 | readonly property int sendActivateAppMenuCooperationFromEditMode: plasmoid.configuration.sendActivateAppMenuCooperationFromEditMode 39 | 40 | function sendMessage() { 41 | if (cooperationEstablished && menuIsPresent) { 42 | broadcasterDelayer.start(); 43 | } 44 | } 45 | 46 | function cancelMessage() { 47 | if (cooperationEstablished) { 48 | broadcasterDelayer.stop(); 49 | } 50 | } 51 | 52 | Component.onDestruction: broadcoastCooperationRequest(false) 53 | 54 | onIsActiveChanged: { 55 | if (!isActive) { 56 | hiddenFromBroadcast = false; 57 | } 58 | 59 | broadcoastCooperationRequest(isActive) 60 | } 61 | 62 | onCooperationEstablishedChanged: { 63 | if (!cooperationEstablished) { 64 | broadcaster.hiddenFromBroadcast = false; 65 | } 66 | } 67 | 68 | onSendActivateAppMenuCooperationFromEditModeChanged: { 69 | if (plasmoid.configuration.sendActivateAppMenuCooperationFromEditMode >= 0) { 70 | var values = { 71 | appletId: plasmoid.id, 72 | cooperation: plasmoid.configuration.sendActivateAppMenuCooperationFromEditMode 73 | }; 74 | 75 | latteBridge.actions.broadcastToApplet("org.kde.windowappmenu", 76 | "activateAppMenuCooperationFromEditMode", 77 | values); 78 | 79 | releaseSendActivateAppMenuCooperation.start(); 80 | } 81 | } 82 | 83 | function broadcoastCooperationRequest(enabled) { 84 | if (latteBridge) { 85 | var values = { 86 | appletId: plasmoid.id, 87 | cooperation: enabled 88 | }; 89 | latteBridge.actions.broadcastToApplet("org.kde.windowappmenu", "setCooperation", values); 90 | } 91 | } 92 | 93 | Connections { 94 | target: latteBridge 95 | onBroadcasted: { 96 | var updateAppMenuCooperations = false; 97 | 98 | if (broadcaster.cooperationEstablished) { 99 | if (action === "setVisible") { 100 | if (value === true) { 101 | broadcaster.hiddenFromBroadcast = false; 102 | } else { 103 | broadcaster.hiddenFromBroadcast = true; 104 | } 105 | } else if (action === "menuIsPresent") { 106 | broadcaster.menuIsPresent = value; 107 | } 108 | } 109 | 110 | if (action === "isPresent") { 111 | plasmoid.configuration.appMenuIsPresent = value; 112 | } else if (action === "setCooperation") { 113 | updateAppMenuCooperations = true; 114 | } else if (action === "activateWindowTitleCooperationFromEditMode") { 115 | plasmoid.configuration.showAppMenuOnMouseEnter = value.cooperation; 116 | updateAppMenuCooperations = true; 117 | } 118 | 119 | if (updateAppMenuCooperations) { 120 | var indexed = broadcaster.appMenusRequestCooperation.indexOf(value.appletId); 121 | var isFiled = (indexed >= 0); 122 | 123 | if (value.cooperation && !isFiled) { 124 | broadcaster.appMenusRequestCooperation.push(value.appletId); 125 | broadcaster.appMenusRequestCooperationCount++; 126 | } else if (!value.cooperation && isFiled) { 127 | broadcaster.appMenusRequestCooperation.splice(indexed, 1); 128 | broadcaster.appMenusRequestCooperationCount--; 129 | } 130 | } 131 | } 132 | } 133 | 134 | Timer{ 135 | id: broadcasterDelayer 136 | interval: 5 137 | onTriggered: { 138 | if (latteBridge) { 139 | if (broadcasterMouseArea.realContainsMouse && existsWindowActive) { 140 | broadcaster.hiddenFromBroadcast = true; 141 | latteBridge.actions.broadcastToApplet("org.kde.windowappmenu", "setVisible", true); 142 | } else { 143 | broadcaster.hiddenFromBroadcast = false; 144 | latteBridge.actions.broadcastToApplet("org.kde.windowappmenu", "setVisible", false); 145 | } 146 | } 147 | } 148 | } 149 | 150 | Timer { 151 | id: releaseSendActivateAppMenuCooperation 152 | interval: 50 153 | onTriggered: plasmoid.configuration.sendActivateAppMenuCooperationFromEditMode = -1; 154 | } 155 | 156 | //!!!! MouseArea for Broadcaster 157 | MouseArea{ 158 | id: broadcasterMouseArea 159 | anchors.fill: parent 160 | visible: broadcaster.cooperationEstablished && broadcaster.menuIsPresent 161 | hoverEnabled: true 162 | propagateComposedEvents: true 163 | 164 | property int mouseAX: -1 165 | property int mouseAY: -1 166 | 167 | //! HACK :: For some reason containsMouse breaks in some cases 168 | //! this hack is used in order to be sure when the mouse is really 169 | //! inside the MouseArea or not 170 | readonly property bool realContainsMouse: mouseAX !== -1 || mouseAY !== -1 171 | 172 | onContainsMouseChanged: { 173 | mouseAX = -1; 174 | mouseAY = -1; 175 | } 176 | 177 | onMouseXChanged: mouseAX = mouseX; 178 | onMouseYChanged: mouseAY = mouseY; 179 | 180 | onRealContainsMouseChanged: { 181 | if (broadcaster.cooperationEstablished) { 182 | if (realContainsMouse) { 183 | broadcaster.sendMessage(); 184 | } else { 185 | broadcaster.cancelMessage(); 186 | } 187 | } 188 | } 189 | 190 | onPressed: { 191 | mouse.accepted = false; 192 | } 193 | 194 | onReleased: { 195 | mouse.accepted = false; 196 | } 197 | } 198 | 199 | } 200 | -------------------------------------------------------------------------------- /contents/ui/config/ConfigBehavior.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.9 21 | import QtQuick.Controls 1.0 22 | import QtQuick.Layouts 1.0 23 | 24 | import org.kde.plasma.core 2.0 as PlasmaCore 25 | 26 | import org.kde.kirigami 2.4 as Kirigami 27 | 28 | Item { 29 | id: behaviorPage 30 | 31 | property alias cfg_filterByScreen: filterByScreenChk.checked 32 | property alias cfg_filterActivityInfo: filterActivityChk.checked 33 | 34 | property alias cfg_showAppMenuOnMouseEnter: showAppMenuChk.checked 35 | property alias cfg_showTooltip: showTooltip.checked 36 | property alias cfg_actionScrollMinimize: cycleMinimizeChk.checked 37 | 38 | property alias cfg_subsMatch: behaviorPage.selectedMatches 39 | property alias cfg_subsReplace: behaviorPage.selectedReplacements 40 | 41 | property alias cfg_placeHolder: placeHolder.text 42 | 43 | // used as bridge to communicate properly between configuration and ui 44 | property var selectedMatches: [] 45 | property var selectedReplacements: [] 46 | 47 | // used from the ui 48 | readonly property real centerFactor: 0.3 49 | readonly property int minimumWidth: 220 50 | 51 | ColumnLayout { 52 | id:mainColumn 53 | spacing: units.largeSpacing 54 | width:parent.width - anchors.leftMargin * 2 55 | height: parent.height 56 | anchors.top: parent.top 57 | anchors.left: parent.left 58 | anchors.leftMargin: 2 59 | 60 | GridLayout { 61 | columns: 2 62 | 63 | Label{ 64 | Layout.minimumWidth: Math.max(centerFactor * behaviorPage.width, minimumWidth) 65 | text: i18n("Filters:") 66 | horizontalAlignment: Text.AlignRight 67 | } 68 | 69 | CheckBox{ 70 | id: filterByScreenChk 71 | text: i18n("Show only window information from current screen") 72 | } 73 | } 74 | 75 | GridLayout { 76 | columns: 2 77 | 78 | Label{ 79 | Layout.minimumWidth: Math.max(centerFactor * behaviorPage.width, minimumWidth) 80 | text: i18n("Mouse:") 81 | horizontalAlignment: Text.AlignRight 82 | } 83 | 84 | CheckBox{ 85 | id: showTooltip 86 | text: i18n("Show tooltip on hover") 87 | enabled: showAppMenuChk.visible && !showAppMenuChk.checked 88 | } 89 | 90 | Label{ 91 | visible: showAppMenuChk.visible 92 | enabled: showAppMenuChk.enabled 93 | } 94 | 95 | CheckBox{ 96 | id: showAppMenuChk 97 | text: i18n("Show Window AppMenu applet on enter") 98 | visible: plasmoid.configuration.containmentType === 2 /*Latte Containment*/ 99 | enabled: plasmoid.configuration.appMenuIsPresent 100 | } 101 | 102 | Label{ 103 | visible: cycleMinimizeChk.visible 104 | } 105 | 106 | CheckBox { 107 | id: cycleMinimizeChk 108 | text: i18n("Scroll to cycle and minimize through your tasks") 109 | visible: plasmoid.configuration.containmentType === 1 /*Plasma Containment*/ 110 | } 111 | } 112 | 113 | Kirigami.InlineMessage { 114 | id: inlineMessage 115 | Layout.fillWidth: true 116 | Layout.bottomMargin: 5 117 | 118 | type: Kirigami.MessageType.Warning 119 | text: cfg_showAppMenuOnMouseEnter ? 120 | i18n("Would you like also to activate that behavior to surrounding Window AppMenu?") : 121 | i18n("Would you like also to deactivate that behavior to surrounding Window AppMenu?") 122 | 123 | actions: [ 124 | Kirigami.Action { 125 | icon.name: "dialog-yes" 126 | text: i18n("Yes") 127 | onTriggered: { 128 | plasmoid.configuration.sendActivateAppMenuCooperationFromEditMode = cfg_showAppMenuOnMouseEnter; 129 | inlineMessage.visible = false; 130 | } 131 | }, 132 | Kirigami.Action { 133 | icon.name: "dialog-no" 134 | text: "No" 135 | onTriggered: { 136 | inlineMessage.visible = false; 137 | } 138 | } 139 | ] 140 | 141 | readonly property bool showWindowAppMenuTouched: showAppMenuChk.checked !== plasmoid.configuration.showAppMenuOnMouseEnter 142 | 143 | onShowWindowAppMenuTouchedChanged: { 144 | if (plasmoid.configuration.containmentType !== 2 /*Latte Containment*/) { 145 | visible = false; 146 | return; 147 | } 148 | 149 | if (showWindowAppMenuTouched){ 150 | inlineMessage.visible = true; 151 | } else { 152 | inlineMessage.visible = false; 153 | } 154 | } 155 | } 156 | 157 | GridLayout { 158 | columns: 2 159 | Label{ 160 | Layout.minimumWidth: Math.max(centerFactor * behaviorPage.width, minimumWidth) 161 | text: i18n("Placeholder:") 162 | horizontalAlignment: Text.AlignRight 163 | } 164 | 165 | CheckBox{ 166 | id: filterActivityChk 167 | text: i18n("Show activity information") 168 | } 169 | 170 | Label{} 171 | 172 | TextField { 173 | id: placeHolder 174 | text: plasmoid.configuration.placeHolder 175 | Layout.minimumWidth: substitutionsBtn.width * 1.5 176 | Layout.maximumWidth: Layout.minimumWidth 177 | enabled: !filterActivityChk.checked 178 | 179 | placeholderText: i18n("placeholder text...") 180 | } 181 | } 182 | 183 | GridLayout{ 184 | columns: 2 185 | 186 | Label{ 187 | Layout.minimumWidth: Math.max(centerFactor * behaviorPage.width, minimumWidth) 188 | text: i18n("Application name:") 189 | horizontalAlignment: Text.AlignRight 190 | } 191 | 192 | Button{ 193 | id: substitutionsBtn 194 | checkable: true 195 | checked: subsSlidingBox.shown 196 | text: " " + i18n("Manage substitutions...") + " " 197 | onClicked: { 198 | if (subsSlidingBox.shown) { 199 | subsSlidingBox.slideOut(); 200 | } else { 201 | subsSlidingBox.slideIn(); 202 | } 203 | } 204 | 205 | SubstitutionsPopup { 206 | id: subsSlidingBox 207 | page: behaviorPage 208 | slideOutFrom: PlasmaCore.Types.BottomEdge 209 | } 210 | } 211 | } 212 | 213 | Item { 214 | Layout.fillHeight: true 215 | Layout.fillWidth: true 216 | } 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /contents/ui/PlasmaTasksModel.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.7 21 | import QtQml.Models 2.2 22 | 23 | import org.kde.taskmanager 0.1 as TaskManager 24 | 25 | Item { 26 | id: plasmaTasksItem 27 | readonly property bool existsWindowActive: root.activeTaskItem && tasksRepeater.count > 0 && activeTaskItem.isActive 28 | property Item activeTaskItem: null 29 | 30 | TaskManager.TasksModel { 31 | id: tasksModel 32 | sortMode: TaskManager.TasksModel.SortVirtualDesktop 33 | groupMode: TaskManager.TasksModel.GroupDisabled 34 | screenGeometry: plasmoid.screenGeometry 35 | activity: activityInfo.currentActivity 36 | virtualDesktop: virtualDesktopInfo.currentDesktop 37 | 38 | filterByScreen: plasmoid.configuration.filterByScreen 39 | filterByVirtualDesktop: true 40 | filterByActivity: true 41 | } 42 | 43 | Item{ 44 | id: taskList 45 | Repeater{ 46 | id: tasksRepeater 47 | model: tasksModel 48 | Item{ 49 | id: task 50 | readonly property string appName: modelAppName !== "" ? modelAppName : discoveredAppName 51 | readonly property bool isMinimized: IsMinimized === true ? true : false 52 | readonly property bool isMaximized: IsMaximized === true ? true : false 53 | readonly property bool isActive: IsActive === true ? true : false 54 | readonly property bool isOnAllDesktops: IsOnAllVirtualDesktops === true ? true : false 55 | property var icon: decoration 56 | 57 | readonly property string modelAppName: AppName 58 | readonly property string modelDisplay: display 59 | 60 | property string title: "" 61 | property string discoveredAppName: "" 62 | 63 | readonly property var m: model 64 | 65 | function cleanupTitle() { 66 | var text = display; 67 | var t = modelDisplay; 68 | var sep = t.lastIndexOf(" —– "); 69 | var spacer = 4; 70 | 71 | if (sep === -1) { 72 | sep = t.lastIndexOf(" -- "); 73 | spacer = 4; 74 | } 75 | 76 | if (sep === -1) { 77 | sep = t.lastIndexOf(" -- "); 78 | spacer = 4; 79 | } 80 | 81 | if (sep === -1) { 82 | sep = t.lastIndexOf(" — "); 83 | spacer = 3; 84 | } 85 | 86 | if (sep === -1) { 87 | sep = t.lastIndexOf(" - "); 88 | spacer = 3; 89 | } 90 | 91 | var dTitle = ""; 92 | var dAppName = ""; 93 | 94 | if (sep>-1) { 95 | dTitle = text.substring(0, sep); 96 | discoveredAppName = text.substring(sep+spacer, text.length); 97 | 98 | //if title starts with application name, swap the found records 99 | if (dTitle.startsWith(modelAppName)) { 100 | var firstPart = dTitle; 101 | dTitle = discoveredAppName; 102 | discoveredAppName = firstPart; 103 | } 104 | } 105 | 106 | if (sep>-1) { 107 | title = dTitle; 108 | } else { 109 | title = t; 110 | } 111 | } 112 | 113 | onIsActiveChanged: { 114 | if (isActive) { 115 | plasmaTasksItem.activeTaskItem = task; 116 | } 117 | } 118 | 119 | onModelAppNameChanged: task.cleanupTitle() 120 | onModelDisplayChanged: task.cleanupTitle() 121 | Component.onCompleted: task.cleanupTitle() 122 | 123 | Component.onDestruction: { 124 | if (plasmaTasksItem.lastActiveTaskItem === task) { 125 | plasmaTasksItem.lastActiveTaskItem = null; 126 | } 127 | } 128 | 129 | function modelIndex(){ 130 | return tasksModel.makeModelIndex(index); 131 | } 132 | 133 | function toggleMaximized() { 134 | tasksModel.requestToggleMaximized(modelIndex()); 135 | } 136 | 137 | function toggleMinimized() { 138 | tasksModel.requestToggleMinimized(modelIndex()); 139 | } 140 | 141 | function requestClose() { 142 | tasksModel.requestClose(modelIndex()); 143 | } 144 | 145 | function togglePinToAllDesktops() { 146 | if (root.plasma515) { 147 | tasksModel.requestVirtualDesktops(modelIndex(), 0); 148 | } else { 149 | tasksModel.requestVirtualDesktop(modelIndex(), 0); 150 | } 151 | } 152 | 153 | function toggleKeepAbove(){ 154 | tasksModel.requestToggleKeepAbove(modelIndex()); 155 | } 156 | } 157 | } 158 | } 159 | 160 | //! Functionality 161 | 162 | function toggleMaximized() { 163 | if (activeTaskItem) { 164 | activeTaskItem.toggleMaximized(); 165 | } 166 | } 167 | 168 | function toggleMinimized() { 169 | if (activeTaskItem) { 170 | activeTaskItem.toggleMinimized(); 171 | } 172 | } 173 | 174 | function requestClose() { 175 | if (activeTaskItem) { 176 | activeTaskItem.requestClose(); 177 | } 178 | } 179 | 180 | function togglePinToAllDesktops() { 181 | if (activeTaskItem) { 182 | activeTaskItem.togglePinToAllDesktops(); 183 | } 184 | } 185 | 186 | function toggleKeepAbove(){ 187 | if (activeTaskItem) { 188 | activeTaskItem.toggleKeepAbove(); 189 | } 190 | } 191 | 192 | //! Cycle Through Tasks 193 | function activateNextPrevTask(next) { 194 | var taskIndexList = []; 195 | var activeTaskIndex = tasksModel.activeTask; 196 | 197 | for (var i = 0; i < taskList.children.length - 1; ++i) { 198 | var task = taskList.children[i]; 199 | var modelIndex = task.modelIndex(i); 200 | 201 | if (task.m.IsLauncher !== true && task.m.IsStartup !== true) { 202 | if (task.m.IsGroupParent === true) { 203 | for (var j = 0; j < tasksModel.rowCount(modelIndex); ++j) { 204 | taskIndexList.push(tasksModel.makeModelIndex(i, j)); 205 | } 206 | } else { 207 | taskIndexList.push(modelIndex); 208 | } 209 | } 210 | } 211 | 212 | if (!taskIndexList.length) { 213 | return; 214 | } 215 | 216 | var target = taskIndexList[0]; 217 | 218 | for (var i = 0; i < taskIndexList.length; ++i) { 219 | if (taskIndexList[i] === activeTaskIndex) 220 | { 221 | if (next && i < (taskIndexList.length - 1)) { 222 | target = taskIndexList[i + 1]; 223 | } else if (!next) { 224 | if (i) { 225 | target = taskIndexList[i - 1]; 226 | } else { 227 | target = taskIndexList[taskIndexList.length - 1]; 228 | } 229 | } 230 | 231 | break; 232 | } 233 | } 234 | 235 | tasksModel.requestActivate(target); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /contents/ui/config/ConfigAppearance.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.9 21 | import QtQuick.Controls 1.0 22 | import QtQuick.Controls 2.2 as Controls22 23 | import QtGraphicalEffects 1.0 24 | import QtQuick.Layouts 1.0 25 | 26 | import org.kde.plasma.core 2.0 as PlasmaCore 27 | 28 | import "../../tools/Tools.js" as Tools 29 | 30 | Item { 31 | id: root 32 | 33 | property alias cfg_boldFont: boldChk.checked 34 | property alias cfg_italicFont: italicChk.checked 35 | property alias cfg_capitalFont: capitalChk.checked 36 | property alias cfg_showIcon: showIconChk.checked 37 | property alias cfg_iconFillThickness: iconFillChk.checked 38 | property alias cfg_iconSize: iconSizeSpn.value 39 | property alias cfg_lengthPolicy: root.selectedLengthPolicy 40 | property alias cfg_spacing: spacingSpn.value 41 | property alias cfg_style: root.selectedStyle 42 | property alias cfg_lengthFirstMargin: lengthFirstSpn.value 43 | property alias cfg_lengthLastMargin: lengthLastSpn.value 44 | property alias cfg_lengthMarginsLock: lockItem.locked 45 | property alias cfg_fixedLength: fixedLengthSlider.value 46 | property alias cfg_maximumLength: maxLengthSlider.value 47 | 48 | property alias cfg_subsMatch: root.selectedMatches 49 | property alias cfg_subsReplace: root.selectedReplacements 50 | 51 | // used as bridge to communicate properly between configuration and ui 52 | property int selectedLengthPolicy 53 | property int selectedStyle 54 | property var selectedMatches: [] 55 | property var selectedReplacements: [] 56 | 57 | // used from the ui 58 | readonly property real centerFactor: 0.3 59 | readonly property int minimumWidth: 220 60 | 61 | onSelectedStyleChanged: { 62 | if (selectedStyle === 4) { /*NoText*/ 63 | showIconChk.checked = true; 64 | } 65 | } 66 | 67 | SystemPalette { 68 | id: palette 69 | } 70 | 71 | ColumnLayout { 72 | id:mainColumn 73 | spacing: units.largeSpacing 74 | Layout.fillWidth: true 75 | 76 | GridLayout{ 77 | columns: 2 78 | 79 | Label{ 80 | Layout.minimumWidth: Math.max(centerFactor * root.width, minimumWidth) 81 | text: i18n("Text style:") 82 | horizontalAlignment: Text.AlignRight 83 | } 84 | 85 | CustomComboBox{ 86 | id: styleCmb 87 | 88 | choices: [ 89 | i18n("Application"), 90 | i18n("Title"), 91 | i18n("Application - Title"), 92 | i18n("Title - Application"), 93 | i18n("Do not show any text"), 94 | ]; 95 | 96 | Component.onCompleted: currentIndex = plasmoid.configuration.style; 97 | onChoiceClicked: root.selectedStyle = index; 98 | } 99 | } 100 | 101 | GridLayout{ 102 | columns: 2 103 | 104 | Label{ 105 | Layout.minimumWidth: Math.max(centerFactor * root.width, minimumWidth) 106 | text: i18n("Icon:") 107 | horizontalAlignment: Text.AlignRight 108 | } 109 | 110 | CheckBox{ 111 | id: showIconChk 112 | text: i18n("Show when available") 113 | enabled: root.selectedStyle !== 4 /*NoText*/ 114 | } 115 | 116 | Label{ 117 | } 118 | 119 | CheckBox{ 120 | id: iconFillChk 121 | text: i18n("Fill thickness") 122 | enabled: showIconChk.checked 123 | } 124 | 125 | Label{ 126 | } 127 | 128 | RowLayout{ 129 | enabled: !iconFillChk.checked 130 | 131 | SpinBox{ 132 | id: iconSizeSpn 133 | minimumValue: 16 134 | maximumValue: 128 135 | suffix: " " + i18nc("pixels","px.") 136 | enabled: !iconFillChk.checked 137 | } 138 | 139 | Label { 140 | Layout.leftMargin: units.smallSpacing 141 | text: "maximum" 142 | } 143 | } 144 | } 145 | 146 | GridLayout{ 147 | columns: 2 148 | enabled : root.selectedStyle !== 4 /*NoText*/ 149 | 150 | Label{ 151 | Layout.minimumWidth: Math.max(centerFactor * root.width, minimumWidth) 152 | text: i18n("Font:") 153 | horizontalAlignment: Text.AlignRight 154 | } 155 | 156 | CheckBox{ 157 | id: boldChk 158 | text: i18n("Bold") 159 | } 160 | 161 | Label{ 162 | id: italicLbl 163 | font.italic: true 164 | } 165 | 166 | CheckBox{ 167 | id: italicChk 168 | text: i18n("Italic") 169 | } 170 | 171 | Label{ 172 | } 173 | 174 | CheckBox{ 175 | id: capitalChk 176 | text: i18n("First letters capital") 177 | } 178 | } 179 | 180 | GridLayout{ 181 | columns: 2 182 | enabled : root.selectedStyle !== 4 /*NoText*/ 183 | 184 | Label{ 185 | id: lengthLbl2 186 | Layout.minimumWidth: Math.max(centerFactor * root.width, minimumWidth) 187 | text: i18n("Length:") 188 | horizontalAlignment: Text.AlignRight 189 | } 190 | 191 | CustomComboBox{ 192 | id: lengthCmb 193 | 194 | choices: [ 195 | i18n("Based on contents"), 196 | i18n("Fixed size"), 197 | i18n("Maximum"), 198 | i18n("Fill available space") 199 | ]; 200 | 201 | Component.onCompleted: currentIndex = plasmoid.configuration.lengthPolicy 202 | onChoiceClicked: root.selectedLengthPolicy = index; 203 | } 204 | 205 | Label{ 206 | visible: lengthCmb.currentIndex === 1 /*Fixed Length Policy*/ 207 | } 208 | 209 | RowLayout{ 210 | visible: lengthCmb.currentIndex === 1 /*Fixed Length Policy*/ 211 | 212 | Slider { 213 | id: fixedLengthSlider 214 | Layout.minimumWidth: lengthCmb.width 215 | Layout.preferredWidth: Layout.minimumWidth 216 | Layout.maximumWidth: Layout.minimumWidth 217 | 218 | minimumValue: 24 219 | maximumValue: 1500 220 | stepSize: 2 221 | } 222 | Label { 223 | id: fixedLengthLbl 224 | text: fixedLengthSlider.value + " " + i18n("px.") 225 | } 226 | } 227 | 228 | Label{ 229 | visible: lengthCmb.currentIndex === 2 /*Maximum Length Policy*/ 230 | } 231 | 232 | RowLayout{ 233 | visible: lengthCmb.currentIndex === 2 /*Maximum Length Policy*/ 234 | Slider { 235 | id: maxLengthSlider 236 | Layout.minimumWidth: lengthCmb.width 237 | Layout.preferredWidth: Layout.minimumWidth 238 | Layout.maximumWidth: Layout.minimumWidth 239 | 240 | minimumValue: 24 241 | maximumValue: 1500 242 | stepSize: 2 243 | } 244 | Label { 245 | id: maxLengthLbl 246 | text: maxLengthSlider.value + " " + i18n("px.") 247 | } 248 | } 249 | 250 | Label{ 251 | } 252 | 253 | Label { 254 | id: lengthDescriptionLbl 255 | Layout.minimumWidth: lengthCmb.width - 10 256 | Layout.preferredWidth: 0.5 * root.width 257 | Layout.maximumWidth: Layout.preferredWidth 258 | 259 | font.italic: true 260 | wrapMode: Text.WordWrap 261 | 262 | text: { 263 | if (lengthCmb.currentIndex === 0 /*Contents*/){ 264 | return i18n("Contents provide an exact size to be used at all times.") 265 | } else if (lengthCmb.currentIndex === 1 /*Fixed*/) { 266 | return i18n("Length slider decides the exact size to be used at all times."); 267 | } else if (lengthCmb.currentIndex === 2 /*Maximum*/) { 268 | return i18n("Contents provide the preferred size and length slider its highest value."); 269 | } else { /*Fill*/ 270 | return i18n("All available space is filled at all times."); 271 | } 272 | } 273 | } 274 | } 275 | 276 | ColumnLayout{ 277 | GridLayout{ 278 | id: visualSettingsGroup1 279 | columns: 2 280 | enabled: showIconChk.checked && root.selectedStyle !== 4 /*NoText*/ 281 | 282 | Label{ 283 | Layout.minimumWidth: Math.max(centerFactor * root.width, minimumWidth) 284 | text: i18n("Spacing:") 285 | horizontalAlignment: Text.AlignRight 286 | } 287 | 288 | SpinBox{ 289 | id: spacingSpn 290 | minimumValue: 0 291 | maximumValue: 36 292 | suffix: " " + i18nc("pixels","px.") 293 | } 294 | } 295 | 296 | GridLayout{ 297 | id: visualSettingsGroup2 298 | 299 | columns: 3 300 | rows: 2 301 | flow: GridLayout.TopToBottom 302 | columnSpacing: visualSettingsGroup1.columnSpacing 303 | rowSpacing: visualSettingsGroup1.rowSpacing 304 | 305 | property int lockerHeight: firstLengthLbl.height + rowSpacing/2 306 | 307 | Label{ 308 | id: firstLengthLbl 309 | Layout.minimumWidth: Math.max(centerFactor * root.width, minimumWidth) 310 | text: plasmoid.configuration.formFactor===PlasmaCore.Types.Horizontal ? 311 | i18n("Left margin:") : i18n("Top margin:") 312 | horizontalAlignment: Text.AlignRight 313 | } 314 | 315 | Label{ 316 | Layout.minimumWidth: Math.max(centerFactor * root.width, minimumWidth) 317 | text: plasmoid.configuration.formFactor===PlasmaCore.Types.Horizontal ? 318 | i18n("Right margin:") : i18n("Bottom margin:") 319 | horizontalAlignment: Text.AlignRight 320 | 321 | enabled: !lockItem.locked 322 | } 323 | 324 | SpinBox{ 325 | id: lengthFirstSpn 326 | minimumValue: 0 327 | maximumValue: 24 328 | suffix: " " + i18nc("pixels","px.") 329 | 330 | property int lastValue: -1 331 | 332 | onValueChanged: { 333 | if (lockItem.locked) { 334 | var step = value - lastValue > 0 ? 1 : -1; 335 | lastValue = value; 336 | lengthLastSpn.value = lengthLastSpn.value + step; 337 | } 338 | } 339 | 340 | Component.onCompleted: { 341 | lastValue = plasmoid.configuration.lengthFirstMargin; 342 | } 343 | } 344 | 345 | SpinBox{ 346 | id: lengthLastSpn 347 | minimumValue: 0 348 | maximumValue: 24 349 | suffix: " " + i18nc("pixels","px.") 350 | enabled: !lockItem.locked 351 | } 352 | 353 | LockItem{ 354 | id: lockItem 355 | Layout.minimumWidth: 40 356 | Layout.maximumWidth: 40 357 | Layout.alignment: Qt.AlignTop | Qt.AlignLeft 358 | Layout.minimumHeight: visualSettingsGroup2.lockerHeight 359 | Layout.maximumHeight: Layout.minimumHeight 360 | Layout.topMargin: firstLengthLbl.height / 2 361 | Layout.rowSpan: 2 362 | } 363 | } 364 | } // ColumnLayout 365 | } //mainColumn 366 | } 367 | -------------------------------------------------------------------------------- /contents/ui/TitleLayout.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2019 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.7 21 | import QtQml.Models 2.2 22 | import QtQuick.Layouts 1.1 23 | import QtQuick.Controls 1.4 24 | 25 | import org.kde.plasma.plasmoid 2.0 26 | import org.kde.plasma.core 2.0 as PlasmaCore 27 | import org.kde.plasma.components 2.0 as PlasmaComponents 28 | 29 | GridLayout{ 30 | id: titleLayout 31 | rows: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 1 : -1 32 | columns: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? -1 : 1 33 | columnSpacing: 0 34 | rowSpacing: 0 35 | 36 | property bool isUsedForMetrics: false 37 | property bool exceedsAvailableSpace: false 38 | property bool exceedsApplicationText: false 39 | 40 | property int applicationTextLength: { 41 | var applicationLength = 0; 42 | 43 | var midSpacerLength = midSpacer.visible ? 44 | (plasmoid.formFactor === PlasmaCore.Types.Horizontal ? midSpacer.width : midSpacer.height) : 0; 45 | 46 | if (plasmoid.configuration.style === 0 /*Application*/ 47 | || plasmoid.configuration.style === 2) { /*ApplicationTitle*/ 48 | applicationLength = firstTxt.implicitWidth; 49 | } else if (plasmoid.configuration.style === 3) { /*TitleApplication*/ 50 | applicationLength = lastTxt.implicitWidth + midSpacerLength; 51 | } 52 | 53 | var iconLength = mainIcon.visible ? 54 | (plasmoid.formFactor === PlasmaCore.Types.Horizontal ? mainIcon.width : mainIcon.height) : 0; 55 | 56 | var subElements = plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 57 | firstSpacer.width + iconLength + midSpacerLength + lastSpacer.width: 58 | firstSpacer.height + iconLength + midSpacerLength + lastSpacer.height; 59 | 60 | return subElements + applicationLength; 61 | } 62 | 63 | Item{ 64 | id: firstSpacer 65 | Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? plasmoid.configuration.lengthFirstMargin : -1 66 | Layout.preferredWidth: Layout.minimumWidth 67 | Layout.maximumWidth: Layout.minimumWidth 68 | 69 | Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? -1 : plasmoid.configuration.lengthFirstMargin 70 | Layout.preferredHeight: Layout.minimumHeight 71 | Layout.maximumHeight: Layout.minimumHeight 72 | } 73 | 74 | Item { 75 | id: mainIcon 76 | 77 | Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 78 | iconItem.iconSize : root.thickness 79 | Layout.maximumWidth: Layout.minimumWidth 80 | 81 | Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 82 | root.thickness : iconItem.iconSize 83 | Layout.maximumHeight: Layout.minimumHeight 84 | 85 | visible: plasmoid.configuration.showIcon 86 | 87 | PlasmaCore.IconItem{ 88 | id: iconItem 89 | anchors.fill: parent 90 | anchors.topMargin: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? thickMargin : 0 91 | anchors.bottomMargin: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? thickMargin : 0 92 | anchors.leftMargin: plasmoid.formFactor === PlasmaCore.Types.Vertical ? thickMargin : 0 93 | anchors.rightMargin: plasmoid.formFactor === PlasmaCore.Types.Vertical ? thickMargin : 0 94 | roundToIconSize: !root.isInLatte 95 | source: existsWindowActive ? activeTaskItem.icon : fullActivityInfo.icon 96 | 97 | 98 | readonly property int thickMargin: plasmoid.configuration.iconFillThickness ? 99 | 0 : (root.thickness - iconSize) / 2 100 | 101 | readonly property int iconSize: plasmoid.configuration.iconFillThickness ? 102 | root.thickness : Math.min(root.thickness, plasmoid.configuration.iconSize) 103 | } 104 | } 105 | 106 | Item{ 107 | id: midSpacer 108 | Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? plasmoid.configuration.spacing : -1 109 | Layout.preferredWidth: Layout.minimumWidth 110 | Layout.maximumWidth: Layout.minimumWidth 111 | 112 | Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? -1 : plasmoid.configuration.spacing 113 | Layout.preferredHeight: Layout.minimumHeight 114 | Layout.maximumHeight: Layout.minimumHeight 115 | 116 | visible: mainIcon.visible && plasmoid.configuration.style !== 4 /*NoText*/ 117 | } 118 | 119 | Item{ 120 | id: textsContainer 121 | Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? -1 : root.thickness 122 | Layout.preferredWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? textRow.availableSpace : root.thickness 123 | Layout.maximumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? textRow.availableSpace : root.thickness 124 | 125 | Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? root.thickness : -1 126 | Layout.preferredHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? root.thickness : textRow.availableSpace 127 | Layout.maximumHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? root.thickness : textRow.availableSpace 128 | visible: plasmoid.configuration.style !== 4 /*NoText*/ 129 | 130 | RowLayout { 131 | id: textRow 132 | anchors.centerIn: parent 133 | spacing: 0 134 | 135 | width: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? parent.width : parent.height 136 | height: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? parent.height : parent.width 137 | 138 | readonly property int availableSpace: { 139 | if (!titleLayout.isUsedForMetrics) { 140 | if (plasmoid.formFactor === PlasmaCore.Types.Horizontal) { 141 | var iconL = mainIcon.visible ? mainIcon.width : 0; 142 | var midL = midSpacer.visible ? midSpacer.width : 0; 143 | return titleLayout.width - firstSpacer.width - iconL - midL - lastSpacer.width; 144 | } else { 145 | var iconL = mainIcon.visible ? mainIcon.height : 0; 146 | var midL = midSpacer.visible ? midSpacer.height : 0; 147 | return titleLayout.height - firstSpacer.height - iconL - midL - lastSpacer.height; 148 | } 149 | } 150 | 151 | return implicitWidths; 152 | } 153 | 154 | readonly property int implicitWidths: { 155 | return Math.ceil(firstTxt.implicitWidth) + Math.ceil(midTxt.implicitWidth) + Math.ceil(lastTxt.implicitWidth); 156 | } 157 | 158 | transformOrigin: Item.Center 159 | 160 | rotation: { 161 | if (plasmoid.formFactor === PlasmaCore.Types.Horizontal) { 162 | return 0; 163 | } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { 164 | return -90; 165 | } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { 166 | return 90; 167 | } 168 | } 169 | 170 | Label{ 171 | id: firstTxt 172 | Layout.fillWidth: elide === Text.ElideNone ? false : true 173 | width: Text.ElideNone ? implicitWidth : -1 174 | verticalAlignment: Text.AlignVCenter 175 | 176 | text: existsWindowActive ? root.firstTitleText : root.fallBackText 177 | color: enforceLattePalette ? latteBridge.palette.textColor : theme.textColor 178 | font.capitalization: plasmoid.configuration.capitalFont ? Font.Capitalize : Font.MixedCase 179 | font.bold: plasmoid.configuration.boldFont 180 | font.italic: plasmoid.configuration.italicFont 181 | 182 | readonly property bool showsTitleText: plasmoid.configuration.style === 1 /*Title*/ 183 | || plasmoid.configuration.style === 3 /*TitleApplication*/ 184 | 185 | readonly property bool showsApplicationText: plasmoid.configuration.style === 0 /*Application*/ 186 | || plasmoid.configuration.style === 2 /*ApplicationTitle*/ 187 | 188 | elide: { 189 | if (plasmoid.configuration.style === 1 && titleLayout.exceedsAvailableSpace){ /*Title*/ 190 | return Text.ElideMiddle; 191 | } else if (plasmoid.configuration.style === 3 192 | && activeTaskItem 193 | && activeTaskItem.appName !== activeTaskItem.title 194 | && titleLayout.exceedsAvailableSpace){ /*TitleApplication*/ 195 | return Text.ElideMiddle; 196 | } else if (showsApplicationText && !isUsedForMetrics && exceedsApplicationText) { 197 | return Text.ElideMiddle; 198 | } 199 | 200 | return Text.ElideNone; 201 | } 202 | 203 | visible: { 204 | if (!isUsedForMetrics && showsTitleText && exceedsApplicationText) { 205 | return false; 206 | } 207 | 208 | return true; 209 | } 210 | } 211 | 212 | Label{ 213 | id: midTxt 214 | verticalAlignment: firstTxt.verticalAlignment 215 | width: implicitWidth 216 | visible: !exceedsApplicationText && text !== "" 217 | 218 | text: { 219 | if (!existsWindowActive) { 220 | return ""; 221 | } 222 | 223 | if (plasmoid.configuration.style === 2 || plasmoid.configuration.style === 3){ /*ApplicationTitle*/ /*OR*/ /*TitleApplication*/ 224 | if (activeTaskItem.appName !== activeTaskItem.title && activeTaskItem.appName !== "" && activeTaskItem.title !== "") { 225 | return " – "; 226 | } 227 | } 228 | 229 | return ""; 230 | } 231 | 232 | color: firstTxt.color 233 | font.capitalization: firstTxt.font.capitalization 234 | font.bold: firstTxt.font.bold 235 | font.italic: firstTxt.font.italic 236 | } 237 | 238 | Label{ 239 | id: lastTxt 240 | Layout.fillWidth: elide === Text.ElideNone ? false : true 241 | width: Text.ElideNone ? implicitWidth : -1 242 | verticalAlignment: firstTxt.verticalAlignment 243 | 244 | text: existsWindowActive ? root.lastTitleText : "" 245 | 246 | color: firstTxt.color 247 | font.capitalization: firstTxt.font.capitalization 248 | font.bold: firstTxt.font.bold 249 | font.italic: firstTxt.font.italic 250 | 251 | visible: text !== "" && !(showsTitleText && exceedsApplicationText) 252 | 253 | readonly property bool showsTitleText: plasmoid.configuration.style === 2 /*ApplicationTitle*/ 254 | 255 | 256 | elide: { 257 | if (activeTaskItem 258 | && activeTaskItem.appName !== activeTaskItem.title 259 | && plasmoid.configuration.style === 2 /*ApplicationTitle*/ 260 | && titleLayout.exceedsAvailableSpace){ /*AND is shown*/ 261 | return Text.ElideMiddle; 262 | } else if(plasmoid.configuration.style === 3 /*TitleApplication*/ 263 | /*&& exceedsApplicationText*/) { 264 | return Text.ElideNone; 265 | } 266 | 267 | return Text.ElideMiddle; 268 | } 269 | } 270 | } 271 | } 272 | 273 | Item{ 274 | id: lastSpacer 275 | Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? plasmoid.configuration.lengthLastMargin : -1 276 | Layout.preferredWidth: Layout.minimumWidth 277 | Layout.maximumWidth: Layout.minimumWidth 278 | 279 | Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? -1 : plasmoid.configuration.lengthLastMargin 280 | Layout.preferredHeight: Layout.minimumHeight 281 | Layout.maximumHeight: Layout.minimumHeight 282 | } 283 | } 284 | 285 | 286 | -------------------------------------------------------------------------------- /contents/ui/main.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2019 Michail Vourlakos 3 | * 4 | * This file is part of applet-window-title 5 | * 6 | * Latte-Dock is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * Latte-Dock is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import QtQuick 2.7 21 | import QtQuick.Layouts 1.1 22 | import QtQuick.Controls 1.4 23 | 24 | import org.kde.plasma.plasmoid 2.0 25 | import org.kde.plasma.core 2.0 as PlasmaCore 26 | import org.kde.plasma.components 2.0 as PlasmaComponents 27 | import org.kde.taskmanager 0.1 as TaskManager 28 | 29 | import org.kde.activities 0.1 as Activities 30 | 31 | import "../tools/Tools.js" as Tools 32 | 33 | Item { 34 | id: root 35 | clip: true 36 | 37 | Layout.fillWidth: (inFillLengthMode && plasmoid.formFactor === PlasmaCore.Types.Horizontal) 38 | || plasmoid.formFactor === PlasmaCore.Types.Vertical ? true : false 39 | Layout.fillHeight: (inFillLengthMode && plasmoid.formFactor === PlasmaCore.Types.Vertical) 40 | || plasmoid.formFactor === PlasmaCore.Types.Horizontal ? true : false 41 | 42 | Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? minimumLength : 0 43 | Layout.preferredWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? preferredLength : -1 44 | Layout.maximumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? maximumLength : -1 45 | 46 | Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical ? minimumLength : 0 47 | Layout.preferredHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical ? preferredLength : -1 48 | Layout.maximumHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical ? maximumLength : -1 49 | 50 | Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation 51 | Plasmoid.onFormFactorChanged: plasmoid.configuration.formFactor = plasmoid.formFactor; 52 | 53 | Plasmoid.status: { 54 | if ((broadcaster.hiddenFromBroadcast && !inEditMode) 55 | || (!inEditMode && fallBackText === "" && !existsWindowActive)) { 56 | return PlasmaCore.Types.HiddenStatus; 57 | } 58 | 59 | return PlasmaCore.Types.PassiveStatus; 60 | } 61 | 62 | readonly property bool inContentsLengthMode: plasmoid.configuration.lengthPolicy === 0 /*Contents Length Policy*/ 63 | readonly property bool inFixedLengthMode: plasmoid.configuration.lengthPolicy === 1 /*Fixed Length Policy*/ 64 | readonly property bool inMaximumLengthMode: plasmoid.configuration.lengthPolicy === 2 /*Maximum Length Policy*/ 65 | readonly property bool inFillLengthMode: plasmoid.configuration.lengthPolicy === 3 /*Fill Length Policy*/ 66 | 67 | readonly property bool inEditMode: plasmoid.userConfiguring || latteInEditMode 68 | 69 | readonly property int containmentType: plasmoid.configuration.containmentType 70 | readonly property int thickness: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? root.height : root.width 71 | 72 | readonly property int minimumLength: { 73 | if (broadcaster.hiddenFromBroadcast) { 74 | return 0; 75 | } 76 | 77 | if (inContentsLengthMode) { 78 | return implicitTitleLength; 79 | } else if (inFixedLengthMode) { 80 | return plasmoid.configuration.fixedLength; 81 | } else if (inMaximumLengthMode) { 82 | return 0; 83 | } else if (inFillLengthMode) { 84 | return inEditMode ? 48 : 0; 85 | } 86 | } 87 | 88 | readonly property int preferredLength: { 89 | if (broadcaster.hiddenFromBroadcast) { 90 | return 0; 91 | } 92 | 93 | if (inContentsLengthMode) { 94 | return implicitTitleLength; 95 | } else if (inFixedLengthMode) { 96 | return plasmoid.configuration.fixedLength; 97 | } else if (inMaximumLengthMode) { 98 | return Math.min(implicitTitleLength, plasmoid.configuration.maximumLength); 99 | } else if (inFillLengthMode) { 100 | return -1; 101 | } 102 | } 103 | 104 | readonly property int maximumLength: { 105 | if (broadcaster.hiddenFromBroadcast) { 106 | return 0; 107 | } 108 | 109 | if (inContentsLengthMode) { 110 | return implicitTitleLength; 111 | } else if (inFixedLengthMode) { 112 | return plasmoid.configuration.fixedLength; 113 | } else if (inMaximumLengthMode) { 114 | return plasmoid.configuration.maximumLength; 115 | } else if (inFillLengthMode) { 116 | return Infinity; 117 | } 118 | } 119 | 120 | readonly property int implicitTitleLength: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? metricsContents.width : metricsContents.height 121 | 122 | readonly property bool existsWindowActive: windowInfoLoader.item && windowInfoLoader.item.existsWindowActive 123 | readonly property bool isActiveWindowPinned: existsWindowActive && activeTaskItem.isOnAllDesktops 124 | readonly property bool isActiveWindowMaximized: existsWindowActive && activeTaskItem.isMaximized 125 | 126 | readonly property Item activeTaskItem: windowInfoLoader.item.activeTaskItem 127 | 128 | property string fallBackText: { 129 | if (!plasmoid.configuration.filterActivityInfo) { 130 | return plasmoid.configuration.placeHolder; 131 | } else { 132 | return fullActivityInfo.name; 133 | } 134 | } 135 | 136 | readonly property string firstTitleText: { 137 | if (!activeTaskItem) { 138 | return ""; 139 | } 140 | 141 | if (plasmoid.configuration.style === 0){ /*Application*/ 142 | return Tools.applySubstitutes(activeTaskItem.appName); 143 | } else if (plasmoid.configuration.style === 1){ /*Title*/ 144 | return activeTaskItem.title; 145 | } else if (plasmoid.configuration.style === 2){ /*ApplicationTitle*/ 146 | return Tools.applySubstitutes(activeTaskItem.appName); 147 | } else if (plasmoid.configuration.style === 3){ /*TitleApplication*/ 148 | var finalText = activeTaskItem.appName === activeTaskItem.title ? 149 | Tools.applySubstitutes(activeTaskItem.appName) : activeTaskItem.title; 150 | 151 | return finalText; 152 | } else if (plasmoid.configuration.style === 4){ /*NoText*/ 153 | return ""; 154 | } 155 | 156 | return ""; 157 | } 158 | 159 | readonly property string lastTitleText: { 160 | if (!activeTaskItem) { 161 | return ""; 162 | } 163 | 164 | if (plasmoid.configuration.style === 2){ /*ApplicationTitle*/ 165 | var finalText = activeTaskItem.appName === activeTaskItem.title ? "" : activeTaskItem.title; 166 | 167 | return finalText; 168 | } else if (plasmoid.configuration.style === 3){ /*TitleApplication*/ 169 | var finalText = activeTaskItem.appName === activeTaskItem.title ? "" : Tools.applySubstitutes(activeTaskItem.appName); 170 | 171 | return finalText; 172 | } 173 | 174 | return ""; 175 | } 176 | 177 | //BEGIN Latte Dock Communicator 178 | property bool isInLatte: false // deprecated Latte v0.8 API 179 | property QtObject latteBridge: null // current Latte v0.9 API 180 | 181 | onLatteBridgeChanged: { 182 | if (latteBridge) { 183 | plasmoid.configuration.containmentType = 2; /*Latte containment with new API*/ 184 | latteBridge.actions.setProperty(plasmoid.id, "latteSideColoringEnabled", false); 185 | latteBridge.actions.setProperty(plasmoid.id, "windowsTrackingEnabled", true); 186 | } 187 | } 188 | 189 | //END Latte Dock Communicator 190 | //BEGIN Latte based properties 191 | readonly property bool enforceLattePalette: latteBridge && latteBridge.applyPalette && latteBridge.palette 192 | readonly property bool latteInEditMode: latteBridge && latteBridge.inEditMode 193 | //END Latte based properties 194 | 195 | Component.onCompleted: { 196 | plasmoid.configuration.appMenuIsPresent = false; 197 | containmentIdentifierTimer.start(); 198 | } 199 | 200 | // START Tasks logic 201 | // To get current activity name 202 | TaskManager.ActivityInfo { 203 | id: activityInfo 204 | } 205 | 206 | Activities.ActivityInfo { 207 | id: fullActivityInfo 208 | activityId: ":current" 209 | } 210 | 211 | // To get virtual desktop name 212 | TaskManager.VirtualDesktopInfo { 213 | id: virtualDesktopInfo 214 | } 215 | 216 | Loader { 217 | id: windowInfoLoader 218 | sourceComponent: latteBridge 219 | && latteBridge.windowsTracker 220 | && latteBridge.windowsTracker.currentScreen.lastActiveWindow 221 | && latteBridge.windowsTracker.allScreens.lastActiveWindow ? latteTrackerComponent : plasmaTasksModel 222 | 223 | Component{ 224 | id: latteTrackerComponent 225 | LatteWindowsTracker{} 226 | } 227 | 228 | Component{ 229 | id: plasmaTasksModel 230 | PlasmaTasksModel{} 231 | } 232 | } 233 | 234 | // END Tasks logic 235 | 236 | // BEGIN Title Layout(s) 237 | 238 | // This Layout is used to count if the title overceeds the available space 239 | // in order for the Visible Layout to elide its contents 240 | TitleLayout { 241 | id: metricsContents 242 | anchors.top: parent.top 243 | anchors.left: parent.left 244 | //anchors.topMargin: 8 245 | 246 | //visible:false, does not return proper metrics, this is why opacity:0 is preferred 247 | opacity: 0 248 | isUsedForMetrics: true 249 | } 250 | 251 | // This is the reas Visible Layout that is shown to the user 252 | TitleLayout { 253 | id: visibleContents 254 | anchors.top: parent.top 255 | anchors.left: parent.left 256 | 257 | width: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 258 | (!exceedsAvailableSpace ? metricsContents.width : root.width) : thickness 259 | 260 | height: plasmoid.formFactor === PlasmaCore.Types.Vertical ? 261 | (!exceedsAvailableSpace ? metricsContents.height : root.height) : thickness 262 | 263 | exceedsAvailableSpace: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 264 | metricsContents.width > root.width : 265 | metricsContents.height > root.height 266 | 267 | exceedsApplicationText: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 268 | metricsContents.applicationTextLength > root.width : 269 | metricsContents.applicationTextLength > root.height 270 | 271 | visible: !(!plasmoid.configuration.filterActivityInfo && !root.existsWindowActive && !plasmoid.configuration.placeHolder) 272 | } 273 | // END Title Layout(s) 274 | 275 | //! Tooltip Area 276 | PlasmaCore.ToolTipArea { 277 | id: contentsTooltip 278 | anchors.fill: visibleContents 279 | active: text !== "" 280 | interactive: true 281 | location: plasmoid.location 282 | 283 | readonly property string text: { 284 | if (!existsWindowActive 285 | || !plasmoid.configuration.showTooltip 286 | || broadcaster.cooperationEstablished /*can not work correctly when showing appmenus*/) { 287 | return ""; 288 | } 289 | 290 | /* Try to show only information that are not already shown*/ 291 | 292 | if (plasmoid.configuration.style === 0){ /*Application*/ 293 | return activeTaskItem.appName === activeTaskItem.title ? "" : activeTaskItem.title; 294 | } else if (plasmoid.configuration.style === 1 295 | || plasmoid.configuration.style === 2 296 | || plasmoid.configuration.style === 4 ){ /*Title OR ApplicationTitle OR NoText*/ 297 | var finalText = activeTaskItem.appName === activeTaskItem.title ? 298 | Tools.applySubstitutes(activeTaskItem.appName) : 299 | Tools.applySubstitutes(activeTaskItem.appName) + " - " + activeTaskItem.title; 300 | 301 | return finalText; 302 | } else if (plasmoid.configuration.style === 3){ /*TitleApplication*/ 303 | var finalText = activeTaskItem.appName === activeTaskItem.title ? 304 | Tools.applySubstitutes(activeTaskItem.appName) : 305 | activeTaskItem.title + " - " + Tools.applySubstitutes(activeTaskItem.appName); 306 | 307 | return finalText; 308 | } 309 | 310 | return ""; 311 | } 312 | 313 | mainItem: RowLayout { 314 | spacing: units.largeSpacing 315 | Layout.margins: units.smallSpacing 316 | PlasmaCore.IconItem { 317 | Layout.minimumWidth: units.iconSizes.medium 318 | Layout.minimumHeight: units.iconSizes.medium 319 | Layout.maximumWidth: Layout.minimumWidth 320 | Layout.maximumHeight: Layout.minimumHeight 321 | source: existsWindowActive ? activeTaskItem.icon : fullActivityInfo.icon 322 | visible: !plasmoid.configuration.showIcon 323 | } 324 | 325 | PlasmaComponents.Label { 326 | id: fullText 327 | Layout.minimumWidth: 0 328 | Layout.preferredWidth: implicitWidth 329 | Layout.maximumWidth: 750 330 | 331 | Layout.minimumHeight: implicitHeight 332 | Layout.maximumHeight: Layout.minimumHeight 333 | elide: Text.ElideRight 334 | 335 | text: contentsTooltip.text 336 | } 337 | } 338 | } 339 | //! END of ToolTip area 340 | 341 | Loader { 342 | id: actionsLoader 343 | anchors.fill: inFillLengthMode ? parent : visibleContents 344 | active: containmentType === 1 /*plasma or old latte containment*/ 345 | 346 | sourceComponent: ActionsMouseArea { 347 | anchors.fill: parent 348 | } 349 | } 350 | 351 | Broadcaster{ 352 | id: broadcaster 353 | anchors.fill: parent 354 | } 355 | 356 | //! this timer is used in order to identify in which containment the applet is in 357 | //! it should be called only the first time an applet is created and loaded because 358 | //! afterwards the applet has no way to move between different processes such 359 | //! as Plasma and Latte 360 | Timer{ 361 | id: containmentIdentifierTimer 362 | interval: 5000 363 | onTriggered: { 364 | if (latteBridge) { 365 | plasmoid.configuration.containmentType = 2; /*Latte containment with new API*/ 366 | latteBridge.actions.broadcastToApplet("org.kde.windowappmenu", "isPresent", true); 367 | } else { 368 | plasmoid.configuration.containmentType = 1; /*Plasma containment or Latte with old API*/ 369 | } 370 | } 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------