1 /***************************************************************************
2 * Copyright 2007 by Enrico Ros <enrico.ros@gmail.com> *
3 * Copyright 2007 by Riccardo Iaconelli <ruphy@kde.org> *
4 * Copyright 2008 by Aaron Seigo <aseigo@kde.org> *
5 * Copyright 2008 by Davide Bettio <davide.bettio@kdemail.net> *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
21 ***************************************************************************/
24 #include "resultscene.h"
25 #include <QtCore/QDebug>
26 #include <QtGui/QKeyEvent>
27 #include <QtCore/QMutexLocker>
28 #include <QtGui/QPainter>
29 #include <QtCore/QTimeLine>
30 #include <QtGui/QGraphicsSceneWheelEvent>
31 #include <QtGui/QGraphicsGridLayout>
32 #include <QtGui/QGraphicsWidget>
33 #include <QtGui/QGraphicsProxyWidget>
36 #include <KDE/KLineEdit>
38 #include <Plasma/AbstractRunner>
39 #include <Plasma/FrameSvg>
40 #include <Plasma/RunnerManager>
42 #include "resultitem.h"
44 ResultScene::ResultScene(Plasma::RunnerManager
*manager
, QObject
*parent
)
45 : QGraphicsScene(parent
),
46 m_runnerManager(manager
),
53 setItemIndexMethod(NoIndex
);
55 connect(m_runnerManager
, SIGNAL(matchesChanged(const QList
<Plasma::QueryMatch
>&)),
56 this, SLOT(setQueryMatches(const QList
<Plasma::QueryMatch
>&)));
58 m_resizeTimer
.setSingleShot(true);
59 connect(&m_resizeTimer
, SIGNAL(timeout()), this, SLOT(layoutIcons()));
61 m_clearTimer
.setSingleShot(true);
62 connect(&m_clearTimer
, SIGNAL(timeout()), this, SLOT(clearMatches()));
64 m_frame
= new Plasma::FrameSvg(this);
67 // lock because setImagePath uses KSycoca
68 QMutexLocker
lock(Plasma::AbstractRunner::bigLock());
69 m_frame
->setImagePath("widgets/viewitem");
72 m_frame
->setCacheAllRenderedFrames(true);
73 m_frame
->setElementPrefix("normal");
74 //QColor bg(255, 255, 255, 126);
75 //setBackgroundBrush(bg);
78 ResultScene::~ResultScene()
82 QSize
ResultScene::minimumSizeHint() const
84 QFontMetrics
fm(font());
85 return QSize(ResultItem::BOUNDING_WIDTH
* 4 + 6, (ResultItem::BOUNDING_HEIGHT
+ fm
.height()) * 2 + 6);
88 void ResultScene::resize(int width
, int height
)
91 if (m_size
.width() == width
&& m_size
.height() == height
) {
95 m_size
= QSize(width
, height
);
96 m_rowStride
= width
/ (ResultItem::BOUNDING_WIDTH
);
97 m_pageStride
= height
/ (ResultItem::BOUNDING_WIDTH
) * m_rowStride
;
98 setSceneRect(0.0, 0.0, (qreal
)width
, (qreal
)height
);
99 m_resizeTimer
.start(150);
102 void ResultScene::layoutIcons()
104 QListIterator
<ResultItem
*> it(m_items
);
106 while (it
.hasNext()) {
107 ResultItem
*item
= it
.next();
108 item
->setRowStride(m_rowStride
);
112 void ResultScene::clearMatches()
114 foreach (ResultItem
*item
, m_items
) {
122 emit
matchCountChanged(0);
125 void ResultScene::setQueryMatches(const QList
<Plasma::QueryMatch
> &m
)
127 // kDebug() << "============================" << endl << "matches retrieved: " << m.count();
129 //kDebug() << "clearing";
130 emit
itemHoverEnter(0);
131 m_clearTimer
.start(200);
138 QList
<Plasma::QueryMatch
> matches
= m
;
139 QMutableListIterator
<Plasma::QueryMatch
> newMatchIt(matches
);
141 // first pass: we try and match up items with existing ids (match persisitence)
142 while (!m_itemsById
.isEmpty() && newMatchIt
.hasNext()) {
143 ResultItem
*item
= addQueryMatch(newMatchIt
.next(), false);
146 m_items
.append(item
);
151 // second pass: now we just use any item that exists (item re-use)
152 newMatchIt
.toFront();
153 while (newMatchIt
.hasNext()) {
154 m_items
.append(addQueryMatch(newMatchIt
.next(), true));
157 // delete the stragglers
158 QMapIterator
<QString
, ResultItem
*> it(m_itemsById
);
159 while (it
.hasNext()) {
160 it
.next().value()->remove();
163 // organize the remainders
167 // this will leave them in *reverse* order
168 qSort(m_items
.begin(), m_items
.end(), ResultItem::compare
);
170 m_pageCount
= m
.count();
171 m_pageCount
= m_pageCount
/ m_pageStride
+ (m_pageCount
% m_pageStride
!= 0 ? 1 : 0);
173 //kDebug() << "gots us" << m_pageCount << "m_pageCount of items";
175 emit
matchCountChanged(m
.count());
177 QListIterator
<ResultItem
*> matchIt(m_items
);
178 QGraphicsWidget
*tab
= 0;
179 while (matchIt
.hasNext()) {
180 ResultItem
*item
= matchIt
.next();
181 //kDebug() << item->name() << item->id() << item->priority() << i;
182 QGraphicsWidget::setTabOrder(tab
, item
);
183 m_itemsById
.insert(item
->id(), item
);
186 // it is vital that focus is set *after* the index
195 emit
itemHoverEnter(m_items
.at(0));
198 ResultItem
* ResultScene::addQueryMatch(const Plasma::QueryMatch
&match
, bool useAnyId
)
200 QMap
<QString
, ResultItem
*>::iterator it
= useAnyId
? m_itemsById
.begin() : m_itemsById
.find(match
.id());
201 ResultItem
*item
= 0;
202 //kDebug() << "attempting" << match.id();
204 if (it
== m_itemsById
.end()) {
205 //kDebug() << "did not find for" << match.id();
207 //kDebug() << "creating for" << match.id();
208 item
= new ResultItem(match
, 0, m_frame
);
211 int rowStride
= sceneRect().width() / (ResultItem::BOUNDING_WIDTH
);
212 item
->setRowStride(rowStride
);
213 connect(item
, SIGNAL(activated(ResultItem
*)), this, SIGNAL(itemActivated(ResultItem
*)));
214 connect(item
, SIGNAL(hoverEnter(ResultItem
*)), this, SIGNAL(itemHoverEnter(ResultItem
*)));
215 connect(item
, SIGNAL(hoverLeave(ResultItem
*)), this, SIGNAL(itemHoverLeave(ResultItem
*)));
217 //kDebug() << "returning failure for" << match.id();
222 //kDebug() << "reusing" << item->name() << "for" << match.id();
223 item
->setMatch(match
);
224 m_itemsById
.erase(it
);
230 void ResultScene::focusOutEvent(QFocusEvent
*focusEvent
)
232 QGraphicsScene::focusOutEvent(focusEvent
);
233 if (!m_items
.isEmpty()) {
234 emit
itemHoverEnter(m_items
.at(0));
238 void ResultScene::keyPressEvent(QKeyEvent
* keyEvent
)
240 //kDebug() << "m_items (size): " << m_items.size() << "\n";
241 ResultItem
*currentFocus
= dynamic_cast<ResultItem
*>(focusItem());
242 int m_cIndex
= currentFocus
? currentFocus
->index() : 0;
243 switch (keyEvent
->key()) {
245 if (m_cIndex
< m_rowStride
) {
246 if (m_items
.size() < m_rowStride
) {
247 // we have less than one row of items, so lets just move to the next item
248 m_cIndex
= (m_cIndex
+ 1) % m_items
.size();
250 m_cIndex
= m_items
.size() - (m_items
.size() % m_rowStride
) - 1 + (m_cIndex
% m_items
.size());
251 if (m_cIndex
>= m_items
.size()) {
252 // we should be on the bottom row, but there is nothing there; move up one row
253 m_cIndex
-= m_rowStride
% m_items
.size();
257 m_cIndex
= m_cIndex
- m_rowStride
;
263 if (m_cIndex
+ m_rowStride
>= m_items
.size()) {
265 m_cIndex
= (m_cIndex
+ 1) % m_rowStride
% m_items
.size();
268 m_cIndex
+= m_rowStride
;
275 m_cIndex
= (m_cIndex
== 0) ? m_items
.size() - 1 : m_cIndex
- 1;
279 m_cIndex
= (m_cIndex
+ 1) % m_items
.size();
286 // pass the event to the item
287 QGraphicsScene::keyPressEvent(keyEvent
);
292 if (m_items
[m_cIndex
]->pos().y() + m_items
[m_cIndex
]->size().height() > sceneRect().y() + sceneRect().height()){
293 setPage(m_currentPage
+ 1);
294 }else if (m_items
[m_cIndex
]->pos().y() < sceneRect().y()){
295 setPage(m_currentPage
- 1);
298 // If we arrive here, it was due to an arrow button.
299 Q_ASSERT(m_cIndex
>= 0);
300 Q_ASSERT(m_cIndex
< m_items
.count());
301 //kDebug() << "m_cIndex: " << m_cIndex << "\n";
302 setFocusItem(m_items
.at(m_cIndex
));
305 void ResultScene::wheelEvent(QGraphicsSceneWheelEvent
*event
)
307 if (event
->delta() > 0) {
308 setPage(m_currentPage
- 1);
310 setPage(m_currentPage
+ 1);
314 void ResultScene::slotArrowResultItemPressed()
319 void ResultScene::slotArrowResultItemReleased()
324 void ResultScene::launchQuery(const QString
&term
)
326 m_runnerManager
->launchQuery(term
);
329 void ResultScene::launchQuery(const QString
&term
, const QString
&runner
)
331 m_runnerManager
->launchQuery(term
, runner
);
334 void ResultScene::clearQuery()
336 m_runnerManager
->reset();
339 ResultItem
* ResultScene::defaultResultItem() const
341 if (m_items
.isEmpty()) {
346 kDebug() << (QObject
*) m_items
[0] << m_items
.count();
350 void ResultScene::run(ResultItem
*item
) const
356 item
->run(m_runnerManager
);
359 Plasma::RunnerManager
* ResultScene::manager() const
361 return m_runnerManager
;
364 uint
ResultScene::pageCount() const
369 void ResultScene::nextPage()
371 setPage(m_currentPage
+ 1);
374 void ResultScene::previousPage()
376 setPage(m_currentPage
- 1);
379 void ResultScene::setPage(uint index
)
381 if (index
> m_pageCount
|| index
== m_currentPage
) {
385 m_currentPage
= index
;
386 setSceneRect(0.0, ((m_currentPage
* (m_pageStride
/ m_rowStride
))) * ResultItem::BOUNDING_HEIGHT
,
389 #include "resultscene.moc"