1/* 2 * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> 3 * 4 * SPDX-License-Identifier: LGPL-2.0-or-later 5 */ 6 7import QtQuick 2.12 8import QtQuick.Controls 2.12 9 10import Qt.labs.qmlmodels 1.0 11 12import org.kde.kirigami 2.12 as Kirigami 13import org.kde.kitemmodels 1.0 as KItemModels 14import org.kde.quickcharts 1.0 as Charts 15 16import org.kde.ksysguard.formatter 1.0 as Formatter 17import org.kde.ksysguard.process 1.0 as Process 18import org.kde.ksysguard.table 1.0 as Table 19 20Table.BaseTableView { 21 id: view 22 23 Kirigami.PlaceholderMessage { 24 visible: !appModel.available 25 anchors.fill: parent 26 anchors.margins: Kirigami.Units.largeSpacing 27 icon.name: "action-unavailable-symbolic" 28 text: i18nc("Warning message shown on runtime error", "Applications view is unsupported on your system"); 29 } 30 31 property var enabledColumns: [] 32 property alias columnDisplay: displayModel.columnDisplay 33 property alias sourceModel: appModel 34 35 property alias filterString: sortColumnFilter.filterString 36 37 property var selectedApplications: { 38 var result = [] 39 var rows = {} 40 41 for (var i of selection.selectedIndexes) { 42 if (rows[i.row] != undefined) { 43 continue 44 } 45 rows[i.row] = true 46 47 var index = sortColumnFilter.mapToSource(i) 48 var item = applicationInformation.createObject() 49 item.index = index 50 result.push(item) 51 } 52 53 return result 54 } 55 56 property Component applicationInformation: ApplicationInformation { 57 model: appModel 58 sensorColumns: { 59 "name": appModel.enabledAttributes.indexOf("appName"), 60 "cpu": appModel.enabledAttributes.indexOf("usage"), 61 "memory": appModel.enabledAttributes.indexOf("vmPSS"), 62 "netInbound": appModel.enabledAttributes.indexOf("netInbound"), 63 "netOutbound": appModel.enabledAttributes.indexOf("netOutbound"), 64 "diskRead": appModel.enabledAttributes.indexOf("ioCharactersActuallyReadRate"), 65 "diskWrite": appModel.enabledAttributes.indexOf("ioCharactersActuallyWrittenRate"), 66 "iconName": appModel.enabledAttributes.indexOf("iconName") 67 } 68 role: Process.ProcessDataModel.Value 69 pidsRole: Process.ProcessDataModel.PIDs 70 } 71 72 idRole: "Attribute" 73 74 columnWidths: [200, 100, 100, 100, 100] 75 76 onSort: { 77 sortColumnFilter.sortColumn = column 78 sortColumnFilter.sortOrder = order 79 } 80 81 headerModel: sortColumnFilter 82 83 model: KItemModels.KSortFilterProxyModel { 84 id: sortColumnFilter 85 86 sourceModel: cacheModel 87 filterKeyColumn: appModel.nameColumn 88 filterCaseSensitivity: Qt.CaseInsensitive 89 filterColumnCallback: function(column, parent) { 90 // Note: This assumes displayModel column == appModel column 91 // This may not always hold, but we get incorrect results if we try to 92 // map to source indices when the model is empty. 93 var sensorId = appModel.enabledAttributes[column] 94 if (appModel.hiddenAttributes.indexOf(sensorId) != -1) { 95 return false 96 } 97 return true 98 } 99 filterRowCallback: function(row, parent) { 100 if (filterString.length == 0) { 101 return true 102 } 103 const name = sourceModel.data(sourceModel.index(row, filterKeyColumn, parent), filterRole).toLowerCase() 104 const parts = filterString.toLowerCase().split(",").map(s => s.trim()).filter(s => s.length > 0) 105 return parts.some(part => name.includes(part)) 106 } 107 108 sortRole: "Value" 109 } 110 111 Table.ComponentCacheProxyModel { 112 id: cacheModel 113 sourceModel: displayModel 114 115 component: Charts.HistoryProxySource { 116 id: history 117 source: Charts.ModelSource { 118 model: history.Table.ComponentCacheProxyModel.model 119 column: history.Table.ComponentCacheProxyModel.column 120 roleName: "Value" 121 } 122 item: Table.ComponentCacheProxyModel.row 123 maximumHistory: 10 124 interval: 2000 125 fillMode: Charts.HistoryProxySource.FillFromEnd 126 } 127 } 128 129 Table.ColumnDisplayModel { 130 id: displayModel 131 sourceModel: appModel 132 idRole: "Attribute" 133 } 134 135 Process.ApplicationDataModel { 136 id: appModel 137 138 property int nameColumn: enabledAttributes.indexOf("appName") 139 property int iconColumn: enabledAttributes.indexOf("iconName") 140 141 property var requiredAttributes: [ 142 "iconName", 143 "appName", 144 "usage", 145 "vmPSS", 146 "netInbound", 147 "netOutbound", 148 "ioCharactersActuallyReadRate", 149 "ioCharactersActuallyWrittenRate", 150 ] 151 property var hiddenAttributes: [] 152 153 enabled: view.visible 154 155 enabledAttributes: { 156 var result = [] 157 for (let i of view.enabledColumns) { 158 if (appModel.availableAttributes.includes(i)) { 159 result.push(i) 160 } 161 } 162 163 var hidden = [] 164 for (let i of requiredAttributes) { 165 if (result.indexOf(i) == -1) { 166 result.push(i) 167 hidden.push(i) 168 } 169 } 170 171 hiddenAttributes = hidden 172 return result; 173 } 174 175 Component.onCompleted: { 176 if (!available) { 177 console.error("Implementation matching https://systemd.io/DESKTOP_ENVIRONMENTS/ was not found. ApplicationsView will not be available") 178 } 179 } 180 } 181 182 delegate: DelegateChooser { 183 role: "displayStyle" 184 DelegateChoice { 185 column: view.LayoutMirroring.enabled ? view.model.columnCount() - 1 : 0 186 Table.FirstCellDelegate { 187 iconName: { 188 var index = sortColumnFilter.mapToSource(sortColumnFilter.index(model.row, 0)); 189 index = appModel.index(index.row, appModel.iconColumn) 190 return appModel.data(index) 191 return "" 192 } 193 } 194 } 195 DelegateChoice { 196 roleValue: "line" 197 Table.LineChartCellDelegate { 198 valueSources: model.cachedComponent != undefined ? model.cachedComponent : [] 199 maximum: sortColumnFilter.data(sortColumnFilter.index(model.row, model.column), Process.ProcessDataModel.Maximum) 200 } 201 } 202 DelegateChoice { 203 roleValue: "lineScaled" 204 Table.LineChartCellDelegate { 205 valueSources: model.cachedComponent != undefined ? model.cachedComponent : [] 206 maximum: sortColumnFilter.data(sortColumnFilter.index(model.row, model.column), Process.ProcessDataModel.Maximum) 207 text: Formatter.Formatter.formatValue(parseInt(model.Value) / model.Maximum * 100, model.Unit) 208 } 209 210 } 211 DelegateChoice { 212 roleValue: "textScaled" 213 Table.TextCellDelegate { 214 text: Formatter.Formatter.formatValue(parseInt(model.Value) / model.Maximum * 100, model.Unit) 215 } 216 } 217 DelegateChoice { Table.TextCellDelegate { } } 218 } 219} 220