1 /* This file is part of the KDE project
2 *
3 * Copyright (C) 2008 Peter Penz <peter.penz19@gmail.com>
4 * Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21 
22 #include "KoViewItemContextBar.h"
23 
24 //Calligra headers
25 #include "KoContextBarButton.h"
26 #include <KoIcon.h>
27 
28 // KF5
29 #include <klocalizedstring.h>
30 
31 //Qt Headers
32 #include <QAbstractItemView>
33 #include <QModelIndex>
34 #include <QApplication>
35 #include <QHBoxLayout>
36 #include <QHoverEvent>
37 
38 /** Space between the item outer rect and the context bar */
39 const int CONTEXTBAR_MARGIN = 4;
40 const int MIN_BUTTON_WIDTH = 24;
41 
KoViewItemContextBar(QAbstractItemView * parent)42 KoViewItemContextBar::KoViewItemContextBar(QAbstractItemView *parent)
43     : QObject(parent)
44     , m_view(parent)
45     , m_enabled(true)
46     , m_showToggleButton(true)
47 {
48     connect(parent, SIGNAL(entered(QModelIndex)),
49             this, SLOT(slotEntered(QModelIndex)));
50     connect(parent, SIGNAL(viewportEntered()),
51             this, SLOT(slotViewportEntered()));
52 
53     m_ContextBar = new QWidget(m_view->viewport());
54     m_ContextBar->hide();
55     m_ToggleSelectionButton = new KoContextBarButton(koIconName("list-add"));
56 
57     m_Layout = new QHBoxLayout(m_ContextBar);
58     m_Layout->setMargin(2);
59     m_Layout->setSpacing(2);
60     m_Layout->addWidget(m_ToggleSelectionButton);
61 
62     connect(m_ToggleSelectionButton, SIGNAL(clicked()),
63             this, SLOT(setItemSelected()));
64     // Hides context bar if item removed
65     connect(m_view->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
66             this, SLOT(slotRowsRemoved(QModelIndex,int,int)));
67 
68     connect(m_view->model(), SIGNAL(modelReset()), this, SLOT(slotModelReset()));
69 
70     m_ContextBar->installEventFilter(this);
71     m_view->viewport()->installEventFilter(this);
72     m_view->setMouseTracking(true);
73 }
74 
eventFilter(QObject * watched,QEvent * event)75 bool KoViewItemContextBar::eventFilter(QObject *watched, QEvent *event)
76 {
77     if (watched == m_view->viewport()) {
78         switch (event->type()) {
79         case QEvent::Leave:
80             if (m_ContextBar->isVisible()) {
81                 m_ContextBar->hide();
82             }
83             break;
84         default:
85             break;
86         }
87     }
88     return QObject::eventFilter(watched, event);
89 }
90 
slotEntered(const QModelIndex & index)91 void KoViewItemContextBar::slotEntered(const QModelIndex &index)
92 {
93     const bool isSelectionCandidate = index.isValid() &&
94             (QApplication::mouseButtons() == Qt::NoButton);
95 
96     if (!m_ContextBar || !m_enabled) {
97         return;
98     }
99 
100     m_ContextBar->hide();
101     if (isSelectionCandidate) {
102         updateHoverUi(index);
103     }
104     else {
105         updateHoverUi(QModelIndex());
106     }
107 }
108 
updateHoverUi(const QModelIndex & index)109 void KoViewItemContextBar::updateHoverUi(const QModelIndex &index)
110 {
111     QModelIndex oldIndex = m_IndexUnderCursor;
112     m_IndexUnderCursor = index;
113     m_view->update(oldIndex);
114 
115     const bool isSelectionCandidate = index.isValid();
116 
117     m_ContextBar->hide();
118     if (isSelectionCandidate) {
119         updateToggleSelectionButton();
120         const QRect rect = m_view->visualRect(m_IndexUnderCursor);
121         showContextBar(rect);
122         m_view->update(m_IndexUnderCursor);
123     } else {
124         m_ContextBar->hide();
125     }
126 }
127 
showContextBar(const QRect & rect)128 void KoViewItemContextBar::showContextBar(const QRect &rect)
129 {
130     // Center bar in FullContextBar mode, left align in
131     // SelectionOnlyContextBar mode
132     const int posX = 0;
133     const int posY = CONTEXTBAR_MARGIN / 4;
134     int numButtons = 0;
135     m_ContextBar->move(rect.topLeft() + QPoint(posX, posY));
136     //Hide buttons if item is too small
137     int width = m_ToggleSelectionButton->width();
138     if (!m_showToggleButton) {
139         m_ToggleSelectionButton->setVisible(false);
140         width = qMin(m_contextBarButtons.at(0)->width(), MIN_BUTTON_WIDTH);
141     }
142     for (int i=m_contextBarButtons.size()-1; i>=0; --i) {
143         if ((rect.width() - 2 * CONTEXTBAR_MARGIN) > ((i + 1) * width)) {
144             m_contextBarButtons.at(i)->setVisible(true);
145             numButtons++;
146             continue;
147         }
148         m_contextBarButtons.at(i)->setVisible(false);
149     }
150     m_ContextBar->adjustSize();
151     if (numButtons > 0) {
152         const int centerX = (rect.width() - m_ContextBar->rect().width()) / 2;
153         m_ContextBar->move(rect.topLeft() + QPoint(centerX, posY));
154     }
155     m_ContextBar->show();
156 }
157 
slotViewportEntered()158 void KoViewItemContextBar::slotViewportEntered()
159 {
160     m_ContextBar->hide();
161 }
162 
setItemSelected()163 void KoViewItemContextBar::setItemSelected()
164 {
165     emit selectionChanged();
166 
167     if (m_IndexUnderCursor.isValid()) {
168         QItemSelectionModel *selModel = m_view->selectionModel();
169         if (!selModel->isSelected(m_IndexUnderCursor)) {
170             selModel->select(m_IndexUnderCursor, QItemSelectionModel::Select);
171         }
172         else {
173             selModel->select(m_IndexUnderCursor, QItemSelectionModel::Deselect);
174         }
175         selModel->setCurrentIndex(m_IndexUnderCursor, QItemSelectionModel::Current);
176     }
177     updateHoverUi(m_IndexUnderCursor);
178 }
179 
slotRowsRemoved(const QModelIndex & parent,int start,int end)180 void KoViewItemContextBar::slotRowsRemoved(const QModelIndex &parent, int start, int end)
181 {
182     Q_UNUSED(parent);
183     Q_UNUSED(start);
184     Q_UNUSED(end);
185     if (m_ContextBar) {
186         m_ContextBar->hide();
187     }
188 }
189 
updateToggleSelectionButton()190 void KoViewItemContextBar::updateToggleSelectionButton()
191 {
192     const bool isHoveredIndexSelected = m_view->selectionModel()->isSelected(m_IndexUnderCursor);
193     const char *const iconName = (isHoveredIndexSelected ? koIconNameCStr("list-remove") : koIconNameCStr("list-add"));
194 
195     m_ToggleSelectionButton->setIcon(QIcon::fromTheme(QLatin1String(iconName)));
196     m_ToggleSelectionButton->setToolTip(isHoveredIndexSelected ? i18n("deselect item") : i18n("select item"));
197 }
198 
update()199 void KoViewItemContextBar::update()
200 {
201     // Check if the current index is still valid and then update the context bar
202     if (m_view->model()->index(currentIndex().row(), currentIndex().column(), currentIndex().parent()).isValid()) {
203         updateHoverUi(currentIndex());
204     }
205     else {
206         updateHoverUi(QModelIndex());
207     }
208 }
209 
addContextButton(const QString & text,const QString & iconName)210 QToolButton * KoViewItemContextBar::addContextButton(const QString &text, const QString &iconName)
211 {
212     KoContextBarButton *newContexButton = new KoContextBarButton(iconName);
213     newContexButton->setToolTip(text);
214     m_Layout->addWidget(newContexButton);
215     m_contextBarButtons.append(newContexButton);
216     return newContexButton;
217 }
218 
currentIndex()219 QModelIndex KoViewItemContextBar::currentIndex()
220 {
221     return m_IndexUnderCursor;
222 }
223 
preferredWidth()224 int KoViewItemContextBar::preferredWidth()
225 {
226     return ((m_contextBarButtons.count()+1)*m_ToggleSelectionButton->sizeHint().width() + 2*CONTEXTBAR_MARGIN);
227 }
228 
setShowSelectionToggleButton(bool enabled)229 void KoViewItemContextBar::setShowSelectionToggleButton(bool enabled)
230 {
231     m_showToggleButton = enabled;
232 }
233 
reset()234 void KoViewItemContextBar::reset()
235 {
236     if (m_ContextBar) {
237         m_ContextBar->hide();
238     }
239 }
240 
slotModelReset()241 void KoViewItemContextBar::slotModelReset()
242 {
243     // reset the model index so it does no longer point to suff no longer available.
244     m_IndexUnderCursor = QModelIndex();
245 }
246 
enableContextBar()247 void KoViewItemContextBar::enableContextBar()
248 {
249     m_enabled = true;
250 }
251 
disableContextBar()252 void KoViewItemContextBar::disableContextBar()
253 {
254     m_enabled = false;
255 }
256