1/**************************************************************************** 2** 3** Copyright (C) 2016 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the Qt Quick Controls module of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40import QtQuick 2.2 41import QtQuick.Controls 1.3 42import QtQuick.Controls.Private 1.0 43import QtQuick.Controls.Styles 1.1 44import QtQuick.Window 2.1 45 46BasicTableView { 47 id: root 48 49 property var model 50 51 readonly property int rowCount: __listView.count 52 property alias currentRow: root.__currentRow 53 54 signal activated(int row) 55 signal clicked(int row) 56 signal doubleClicked(int row) 57 signal pressAndHold(int row) 58 59 function positionViewAtRow(row, mode) { 60 __listView.positionViewAtIndex(row, mode) 61 } 62 63 function rowAt(x, y) { 64 var obj = root.mapToItem(__listView.contentItem, x, y) 65 return __listView.indexAt(obj.x, obj.y) 66 } 67 68 readonly property alias selection: selectionObject 69 70 style: Settings.styleComponent(Settings.style, "TableViewStyle.qml", root) 71 72 Accessible.role: Accessible.Table 73 74 // Internal stuff. Do not look 75 76 onModelChanged: selection.clear() 77 78 __viewTypeName: "TableView" 79 __model: model 80 81 __itemDelegateLoader: TableViewItemDelegateLoader { 82 __style: root.__style 83 __itemDelegate: root.itemDelegate 84 __mouseArea: mousearea 85 } 86 87 __mouseArea: MouseArea { 88 id: mousearea 89 90 parent: __listView 91 width: __listView.width 92 height: __listView.height 93 z: -1 94 propagateComposedEvents: true 95 focus: true 96 97 property bool autoincrement: false 98 property bool autodecrement: false 99 property int previousRow: 0 100 property int clickedRow: -1 101 property int dragRow: -1 102 property int firstKeyRow: -1 103 property int pressedRow: -1 104 property int pressedColumn: -1 105 106 TableViewSelection { 107 id: selectionObject 108 } 109 110 function selected(rowIndex) { 111 if (dragRow > -1 && (rowIndex >= clickedRow && rowIndex <= dragRow 112 || rowIndex <= clickedRow && rowIndex >= dragRow)) 113 return selection.contains(clickedRow) 114 115 return selection.count && selection.contains(rowIndex) 116 } 117 118 onReleased: { 119 pressedRow = -1 120 pressedColumn = -1 121 autoincrement = false 122 autodecrement = false 123 var clickIndex = __listView.indexAt(0, mouseY + __listView.contentY) 124 if (clickIndex > -1) { 125 if (Settings.hasTouchScreen) { 126 __listView.currentIndex = clickIndex 127 mouseSelect(clickIndex, mouse.modifiers) 128 } 129 previousRow = clickIndex 130 } 131 132 if (mousearea.dragRow >= 0) { 133 selection.__select(selection.contains(mousearea.clickedRow), mousearea.clickedRow, mousearea.dragRow) 134 mousearea.dragRow = -1 135 } 136 } 137 138 function decrementCurrentIndex() { 139 __listView.decrementCurrentIndexBlocking(); 140 141 var newIndex = __listView.indexAt(0, __listView.contentY) 142 if (newIndex !== -1) { 143 if (selectionMode > SelectionMode.SingleSelection) 144 mousearea.dragRow = newIndex 145 else if (selectionMode === SelectionMode.SingleSelection) 146 selection.__selectOne(newIndex) 147 } 148 } 149 150 function incrementCurrentIndex() { 151 __listView.incrementCurrentIndexBlocking(); 152 153 var newIndex = Math.max(0, __listView.indexAt(0, __listView.height + __listView.contentY)) 154 if (newIndex !== -1) { 155 if (selectionMode > SelectionMode.SingleSelection) 156 mousearea.dragRow = newIndex 157 else if (selectionMode === SelectionMode.SingleSelection) 158 selection.__selectOne(newIndex) 159 } 160 } 161 162 // Handle vertical scrolling whem dragging mouse outside boundraries 163 Timer { 164 running: mousearea.autoincrement && __verticalScrollBar.visible 165 repeat: true 166 interval: 20 167 onTriggered: mousearea.incrementCurrentIndex() 168 } 169 170 Timer { 171 running: mousearea.autodecrement && __verticalScrollBar.visible 172 repeat: true 173 interval: 20 174 onTriggered: mousearea.decrementCurrentIndex() 175 } 176 177 onPositionChanged: { 178 if (mouseY > __listView.height && pressed) { 179 if (autoincrement) return; 180 autodecrement = false; 181 autoincrement = true; 182 } else if (mouseY < 0 && pressed) { 183 if (autodecrement) return; 184 autoincrement = false; 185 autodecrement = true; 186 } else { 187 autoincrement = false; 188 autodecrement = false; 189 } 190 191 if (pressed && containsMouse) { 192 pressedRow = Math.max(0, __listView.indexAt(0, mouseY + __listView.contentY)) 193 pressedColumn = __listView.columnAt(mouseX) 194 if (!Settings.hasTouchScreen) { 195 if (pressedRow >= 0 && pressedRow !== currentRow) { 196 __listView.currentIndex = pressedRow; 197 if (selectionMode === SelectionMode.SingleSelection) { 198 selection.__selectOne(pressedRow) 199 } else if (selectionMode > 1) { 200 dragRow = pressedRow 201 } 202 } 203 } 204 } 205 } 206 207 onClicked: { 208 var clickIndex = __listView.indexAt(0, mouseY + __listView.contentY) 209 if (clickIndex > -1) { 210 if (root.__activateItemOnSingleClick) 211 root.activated(clickIndex) 212 root.clicked(clickIndex) 213 } 214 } 215 216 onPressed: { 217 pressedRow = __listView.indexAt(0, mouseY + __listView.contentY) 218 pressedColumn = __listView.columnAt(mouseX) 219 __listView.forceActiveFocus() 220 if (pressedRow > -1 && !Settings.hasTouchScreen) { 221 __listView.currentIndex = pressedRow 222 mouseSelect(pressedRow, mouse.modifiers) 223 mousearea.clickedRow = pressedRow 224 } 225 } 226 227 onExited: { 228 mousearea.pressedRow = -1 229 mousearea.pressedColumn = -1 230 } 231 232 onCanceled: { 233 mousearea.pressedRow = -1 234 mousearea.pressedColumn = -1 235 } 236 237 function mouseSelect(index, modifiers) { 238 if (selectionMode) { 239 if (modifiers & Qt.ShiftModifier && (selectionMode === SelectionMode.ExtendedSelection)) { 240 selection.select(previousRow, index) 241 } else if (selectionMode === SelectionMode.MultiSelection || 242 (selectionMode === SelectionMode.ExtendedSelection && modifiers & Qt.ControlModifier)) { 243 selection.__select(!selection.contains(index) , index) 244 } else { 245 selection.__selectOne(index) 246 } 247 } 248 } 249 250 onDoubleClicked: { 251 var clickIndex = __listView.indexAt(0, mouseY + __listView.contentY) 252 if (clickIndex > -1) { 253 if (!root.__activateItemOnSingleClick) 254 root.activated(clickIndex) 255 root.doubleClicked(clickIndex) 256 } 257 } 258 259 onPressAndHold: { 260 var pressIndex = __listView.indexAt(0, mouseY + __listView.contentY) 261 if (pressIndex > -1) 262 root.pressAndHold(pressIndex) 263 } 264 265 // Note: with boolean preventStealing we are keeping the flickable from 266 // eating our mouse press events 267 preventStealing: !Settings.hasTouchScreen 268 269 function keySelect(shiftPressed, row) { 270 if (row < 0 || row > rowCount - 1) 271 return 272 if (shiftPressed && (selectionMode >= SelectionMode.ExtendedSelection)) { 273 selection.__ranges = new Array() 274 selection.select(mousearea.firstKeyRow, row) 275 } else { 276 selection.__selectOne(row) 277 } 278 } 279 280 Keys.forwardTo: [root] 281 282 Keys.onUpPressed: { 283 event.accepted = __listView.decrementCurrentIndexBlocking() 284 if (selectionMode) 285 keySelect(event.modifiers & Qt.ShiftModifier, currentRow) 286 } 287 288 Keys.onDownPressed: { 289 event.accepted = __listView.incrementCurrentIndexBlocking() 290 if (selectionMode) 291 keySelect(event.modifiers & Qt.ShiftModifier, currentRow) 292 } 293 294 Keys.onPressed: { 295 __listView.scrollIfNeeded(event.key) 296 297 if (event.key === Qt.Key_Shift) { 298 firstKeyRow = currentRow 299 } 300 301 if (event.key === Qt.Key_A && event.modifiers & Qt.ControlModifier) { 302 if (selectionMode > 1) 303 selection.selectAll() 304 } 305 } 306 307 Keys.onReleased: { 308 if (event.key === Qt.Key_Shift) 309 firstKeyRow = -1 310 } 311 312 Keys.onReturnPressed: { 313 if (currentRow > -1) 314 root.activated(currentRow); 315 else 316 event.accepted = false 317 } 318 } 319} 320