1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7  * A table widget with more features.
8  *
9  * @author Stephane MANKOWSKI / Guillaume DE BURE
10  */
11 #include "skgtablewidget.h"
12 
13 #include <qapplication.h>
14 #include <qclipboard.h>
15 #include <qevent.h>
16 #include <qscrollbar.h>
17 
18 #include <algorithm>
19 
20 #include "skgtraces.h"
21 
SKGTableWidget(QWidget * iParent)22 SKGTableWidget::SKGTableWidget(QWidget* iParent)
23     : QTableWidget(iParent), stickH(false), stickV(false)
24 {
25     this->installEventFilter(this);
26     connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &SKGTableWidget::onActionTriggered);
27     connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &SKGTableWidget::onActionTriggered);
28     connect(horizontalScrollBar(), &QScrollBar::rangeChanged, this, &SKGTableWidget::onRangeChanged);
29     connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &SKGTableWidget::onRangeChanged);
30 }
31 
32 SKGTableWidget::~SKGTableWidget()
33     = default;
34 
onRangeChanged()35 void SKGTableWidget::onRangeChanged()
36 {
37     auto* scrollb = qobject_cast<QScrollBar*>(sender());
38     if ((stickH && scrollb == horizontalScrollBar()) || (stickV && scrollb == verticalScrollBar())) {
39         scrollb->setValue(scrollb->maximum());
40     }
41 }
42 
onActionTriggered()43 void SKGTableWidget::onActionTriggered()
44 {
45     auto* scrollb = qobject_cast<QScrollBar*>(sender());
46     if (scrollb != nullptr) {
47         if (scrollb == horizontalScrollBar()) {
48             stickH = (scrollb->value() == scrollb->maximum());
49         }
50         if (scrollb == verticalScrollBar()) {
51             stickV = (scrollb->value() == scrollb->maximum());
52         }
53     }
54 }
55 
setStickHorizontal(bool iStick)56 void SKGTableWidget::setStickHorizontal(bool iStick)
57 {
58     stickH = iStick;
59 }
60 
stickHorizontal() const61 bool SKGTableWidget::stickHorizontal() const
62 {
63     return stickH;
64 }
65 
setStickVertical(bool iStick)66 void SKGTableWidget::setStickVertical(bool iStick)
67 {
68     stickV = iStick;
69 }
stickVertical() const70 bool SKGTableWidget::stickVertical() const
71 {
72     return stickV;
73 }
74 
eventFilter(QObject * iObject,QEvent * iEvent)75 bool SKGTableWidget::eventFilter(QObject* iObject, QEvent* iEvent)
76 {
77     if (iObject == this && iEvent != nullptr && iEvent->type() == QEvent::KeyPress) {
78         auto* kevent = dynamic_cast<QKeyEvent*>(iEvent);
79         if (kevent != nullptr) {
80             if (kevent->key() == Qt::Key_Delete && state() != QAbstractItemView::EditingState) {
81                 QList<QTableWidgetItem*> listOfSelectedItems = this->selectedItems();
82                 int nb = listOfSelectedItems.count();
83                 if (nb > 0) {
84                     // Build list of indexes
85                     QList<int> listIndex;
86                     listIndex.reserve(nb);
87                     for (int i = 0; i < nb; ++i) {
88                         QModelIndex mIndex = this->indexFromItem(listOfSelectedItems.at(i));
89                         if (!listIndex.contains(mIndex.row())) {
90                             listIndex.append(mIndex.row());
91                         }
92                     }
93 
94                     // Sort reverse
95                     std::sort(listIndex.begin(), listIndex.end(), std::greater<int>());
96 
97                     // Emit events
98                     nb = listIndex.count();
99                     for (int i = 0; i < nb; ++i) {
100                         Q_EMIT removeLine(listIndex.at(i));
101                     }
102 
103                     if (iEvent != nullptr) {
104                         iEvent->accept();
105                     }
106                     return true;  // stop the process
107                 }
108             } else if (kevent->matches(QKeySequence::Copy) && this->state() != QAbstractItemView::EditingState) {
109                 copy();
110                 if (iEvent != nullptr) {
111                     iEvent->accept();
112                 }
113                 return true;  // stop the process
114             }
115         }
116     }
117 
118     return QTableWidget::eventFilter(iObject, iEvent);
119 }
120 
copy()121 void SKGTableWidget::copy()
122 {
123     QItemSelectionModel* selection = selectionModel();
124     if (selection != nullptr) {
125         QModelIndexList indexes = selection->selectedIndexes();
126 
127         if (indexes.empty()) {
128             return;
129         }
130 
131         std::sort(indexes.begin(), indexes.end());
132 
133         // You need a pair of indexes to find the row changes
134         QModelIndex previous = indexes.first();
135         indexes.removeFirst();
136         QString header_text;
137         bool header_done = false;
138         QString selected_text;
139         for (const auto& current : qAsConst(indexes)) {
140             selected_text.append(model()->data(previous).toString());
141             if (!header_done) {
142                 header_text.append(model()->headerData(previous.column(), Qt::Horizontal).toString());
143             }
144             if (current.row() != previous.row()) {
145                 selected_text.append(QLatin1Char('\n'));
146                 header_done = true;
147             } else {
148                 selected_text.append(QLatin1Char(';'));
149                 if (!header_done) {
150                     header_text.append(QLatin1Char(';'));
151                 }
152             }
153             previous = current;
154         }
155 
156         // add last element
157         selected_text.append(model()->data(previous).toString());
158         selected_text.append(QLatin1Char('\n'));
159         QApplication::clipboard()->setText(header_text + '\n' + selected_text);
160     }
161 }
162 
163 
164