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