2014-11-13 19:30:51 +02:00
/***************************************************************************
* Copyright 2007 by Enrico Ros < enrico . ros @ gmail . com > *
* Copyright 2007 by Riccardo Iaconelli < ruphy @ kde . org > *
* Copyright 2008 by Aaron Seigo < aseigo @ kde . org > *
* Copyright 2008 by Davide Bettio < davide . bettio @ kdemail . net > *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the *
* Free Software Foundation , Inc . , *
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "resultscene.h"
# include <QtCore/QDebug>
2015-08-12 13:11:16 +03:00
# include <QtGui/qevent.h>
# include <QtCore/qmutex.h>
2014-11-13 19:30:51 +02:00
# include <QtGui/QPainter>
# include <QtCore/QTimeLine>
# include <QtGui/QGraphicsGridLayout>
# include <QtGui/QGraphicsWidget>
# include <QtGui/QGraphicsProxyWidget>
# include <QtCore/QCoreApplication>
2015-05-20 13:39:58 +00:00
# include <KDebug>
# include <KIconLoader>
# include <KLineEdit>
2014-11-13 19:30:51 +02:00
# include <Plasma/AbstractRunner>
# include <Plasma/RunnerManager>
# include "selectionbar.h"
ResultScene : : ResultScene ( SharedResultData * resultData , Plasma : : RunnerManager * manager , QWidget * focusBase , QObject * parent )
: QGraphicsScene ( parent ) ,
m_runnerManager ( manager ) ,
m_viewableHeight ( 0 ) ,
m_currentIndex ( 0 ) ,
m_focusBase ( focusBase ) ,
m_resultData ( resultData )
{
setItemIndexMethod ( NoIndex ) ;
connect ( m_runnerManager , SIGNAL ( matchesChanged ( QList < Plasma : : QueryMatch > ) ) ,
this , SLOT ( setQueryMatches ( QList < Plasma : : QueryMatch > ) ) ) ;
m_clearTimer . setSingleShot ( true ) ;
m_clearTimer . setInterval ( 200 ) ;
connect ( & m_clearTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( clearMatches ( ) ) ) ;
m_arrangeTimer . setSingleShot ( true ) ;
m_arrangeTimer . setInterval ( 50 ) ;
connect ( & m_arrangeTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( arrangeItems ( ) ) ) ;
m_selectionBar = new SelectionBar ( 0 ) ;
connect ( m_selectionBar , SIGNAL ( appearanceChanged ( ) ) , this , SLOT ( updateItemMargins ( ) ) ) ;
connect ( m_selectionBar , SIGNAL ( targetItemReached ( QGraphicsItem * ) ) , this , SLOT ( highlightItem ( QGraphicsItem * ) ) ) ;
m_selectionBar - > hide ( ) ;
updateItemMargins ( ) ;
addItem ( m_selectionBar ) ;
}
ResultScene : : ~ ResultScene ( )
{
clearMatches ( ) ;
delete m_selectionBar ;
}
QSize ResultScene : : minimumSizeHint ( ) const
{
QFontMetrics fm ( font ( ) ) ;
return QSize ( KIconLoader : : SizeMedium * 4 , ( fm . height ( ) * 5 ) * 3 ) ;
}
void ResultScene : : setWidth ( int width )
{
const bool resizeItems = width ! = sceneRect ( ) . width ( ) ;
m_selectionBar - > resize ( width , m_selectionBar - > size ( ) . height ( ) ) ;
if ( resizeItems ) {
foreach ( ResultItem * item , m_items ) {
item - > calculateSize ( width ) ;
}
setSceneRect ( itemsBoundingRect ( ) ) ;
}
}
void ResultScene : : clearMatches ( )
{
clearSelection ( ) ;
Plasma : : QueryMatch dummy ( 0 ) ;
foreach ( ResultItem * item , m_items ) {
item - > hide ( ) ;
item - > setMatch ( dummy ) ;
}
m_viewableHeight = 0 ;
emit matchCountChanged ( 0 ) ;
}
bool ResultScene : : canMoveItemFocus ( ) const
{
// We prevent a late query result from stealing the item focus from the user
// The item focus can be moved only if
// 1) there is no item currently focused
// 2) the currently focused item is not visible anymore
// 3) the focusBase widget (the khistorycombobox) has focus (i.e. the user is still typing or waiting) AND the currently focused item has not been hovered
ResultItem * focusedItem = currentlyFocusedItem ( ) ;
return ! ( focusedItem ) | |
( ! m_items . contains ( focusedItem ) ) | |
( m_focusBase - > hasFocus ( ) & & ! focusedItem - > mouseHovered ( ) ) ;
}
int ResultScene : : viewableHeight ( ) const
{
return m_viewableHeight ;
}
void ResultScene : : setQueryMatches ( const QList < Plasma : : QueryMatch > & m )
{
//kDebug() << "============================" << endl << "matches retrieved: " << m.count();
/*
foreach ( const Plasma : : QueryMatch & match , m ) {
kDebug ( ) < < " " < < match . id ( ) < < match . text ( ) ;
}
*/
if ( m . isEmpty ( ) ) {
//kDebug() << "clearing";
//resize(width(), 0);
m_clearTimer . start ( ) ;
return ;
}
m_clearTimer . stop ( ) ;
const int maxItemsAllowed = 50 ;
if ( m_items . isEmpty ( ) ) {
//QTime t;
//t.start();
for ( int i = 0 ; i < maxItemsAllowed ; + + i ) {
ResultItem * item = new ResultItem ( m_resultData , 0 ) ;
item - > setContentsMargins ( m_itemMarginLeft , m_itemMarginTop ,
m_itemMarginRight , m_itemMarginBottom ) ;
item - > hide ( ) ;
item - > setIndex ( i ) ;
connect ( item , SIGNAL ( ensureVisibility ( QGraphicsItem * ) ) , this , SIGNAL ( ensureVisibility ( QGraphicsItem * ) ) ) ;
connect ( item , SIGNAL ( activated ( ResultItem * ) ) , this , SIGNAL ( itemActivated ( ResultItem * ) ) ) ;
connect ( item , SIGNAL ( sizeChanged ( ResultItem * ) ) , this , SLOT ( scheduleArrangeItems ( ) ) ) ;
m_items < < item ;
addItem ( item ) ;
}
arrangeItems ( ) ;
//kDebug() << "creating all items took" << t.elapsed();
}
// we keep track of what was previously focused so if the user changes focus
// and more items come in ... we don't reset that on them unecessarily
// see the keepFocus bool down below
ResultItem * currentFocus = currentlyFocusedItem ( ) ;
QString lastFocusId ;
if ( currentFocus & & currentFocus - > isValid ( ) ) {
lastFocusId = currentFocus - > id ( ) ;
}
QList < Plasma : : QueryMatch > matches = m ;
qSort ( matches . begin ( ) , matches . end ( ) ) ;
QListIterator < Plasma : : QueryMatch > mit ( matches ) ;
mit . toBack ( ) ;
QListIterator < ResultItem * > rit ( m_items ) ;
QGraphicsWidget * prevTabItem = 0 ;
const int viewableHeight = m_viewableHeight ;
while ( mit . hasPrevious ( ) & & rit . hasNext ( ) ) {
ResultItem * item = rit . next ( ) ;
item - > setMatch ( mit . previous ( ) ) ;
prevTabItem = item - > arrangeTabOrder ( prevTabItem ) ;
item - > show ( ) ;
m_viewableHeight = item - > sceneBoundingRect ( ) . bottom ( ) ;
}
Plasma : : QueryMatch dummy ( 0 ) ;
while ( rit . hasNext ( ) ) {
ResultItem * item = rit . next ( ) ;
item - > hide ( ) ;
if ( item - > isValid ( ) ) {
item - > setMatch ( dummy ) ;
}
}
const bool keepFocus = currentFocus & & currentFocus - > isValid ( ) & & currentFocus - > id ( ) = = lastFocusId ;
if ( keepFocus ) {
m_selectionBar - > show ( ) ;
emit ensureVisibility ( currentFocus ) ;
} else {
clearSelection ( ) ;
ResultItem * first = m_items . at ( 0 ) ;
setFocusItem ( first ) ;
first - > setSelected ( true ) ;
first - > highlight ( true ) ;
emit ensureVisibility ( first ) ;
}
emit matchCountChanged ( qMin ( m . count ( ) , maxItemsAllowed ) ) ;
if ( viewableHeight ! = m_viewableHeight ) {
emit viewableHeightChanged ( ) ;
}
}
void ResultScene : : scheduleArrangeItems ( )
{
if ( ! m_arrangeTimer . isActive ( ) ) {
m_arrangeTimer . start ( ) ;
}
}
void ResultScene : : arrangeItems ( )
{
int y = 0 ;
QListIterator < ResultItem * > matchIt ( m_items ) ;
const int viewableHeight = m_viewableHeight ;
while ( matchIt . hasNext ( ) ) {
ResultItem * item = matchIt . next ( ) ;
//kDebug() << item->name() << item->id() << item->priority() << i;
item - > setPos ( 0 , y ) ;
//kDebug() << item->pos();
y + = item - > geometry ( ) . height ( ) ;
if ( item - > isVisible ( ) ) {
m_viewableHeight = item - > sceneBoundingRect ( ) . bottom ( ) ;
}
}
//kDebug() << "setting scene rect to" << itemsBoundingRect();
const QRectF newRect = itemsBoundingRect ( ) ;
setSceneRect ( newRect ) ;
if ( viewableHeight ! = m_viewableHeight ) {
emit viewableHeightChanged ( ) ;
}
}
void ResultScene : : highlightItem ( QGraphicsItem * item )
{
ResultItem * rItem = dynamic_cast < ResultItem * > ( item ) ;
if ( rItem ) {
rItem - > highlight ( true ) ;
}
}
void ResultScene : : focusInEvent ( QFocusEvent * focusEvent )
{
// The default implementation of focusInEvent assumes that if the scene has no focus
// then it has no focused item; thus, when a scene gains focus, focusInEvent gives
// focus to the last focused item.
// In our case this assumption is not true, as an item can be focused before the scene,
// therefore we revert the behaviour by re-selecting the previously selected item
ResultItem * currentFocus = currentlyFocusedItem ( ) ;
QGraphicsScene : : focusInEvent ( focusEvent ) ;
switch ( focusEvent - > reason ( ) ) {
case Qt : : TabFocusReason :
case Qt : : BacktabFocusReason :
case Qt : : OtherFocusReason :
// on tab focus in, we want to actually select the second item
// since the first item is always "passively" selected by default
if ( ! currentFocus | | currentFocus = = m_items . first ( ) ) {
ResultItem * newFocus = m_items [ 0 ] ;
if ( newFocus - > firstTabItem ( ) ! = newFocus ) {
setFocusItem ( newFocus - > firstTabItem ( ) ) ;
} else {
newFocus = m_items [ 1 ] ;
if ( newFocus - > isVisible ( ) ) {
setFocusItem ( newFocus ) ;
emit ensureVisibility ( newFocus ) ;
}
}
} else if ( currentFocus ) {
setFocusItem ( currentFocus ) ;
}
break ;
default :
if ( currentFocus ) {
setFocusItem ( currentFocus ) ;
}
break ;
}
}
void ResultScene : : keyPressEvent ( QKeyEvent * keyEvent )
{
//kDebug() << "m_items (size): " << m_items.size() << "\n";
switch ( keyEvent - > key ( ) ) {
case Qt : : Key_Up :
case Qt : : Key_Left :
selectPreviousItem ( ) ;
break ;
case Qt : : Key_Down :
case Qt : : Key_Right :
selectNextItem ( ) ;
break ;
default :
// pass the event to the item
QGraphicsScene : : keyPressEvent ( keyEvent ) ;
if ( ! keyEvent - > isAccepted ( ) & & m_focusBase & &
( keyEvent - > key ( ) ! = Qt : : Key_Tab ) & &
( keyEvent - > key ( ) ! = Qt : : Key_PageUp ) & &
( keyEvent - > key ( ) ! = Qt : : Key_PageDown ) & &
( keyEvent - > modifiers ( ) = = Qt : : NoModifier ) ) {
m_focusBase - > setFocus ( ) ;
QCoreApplication : : sendEvent ( m_focusBase , keyEvent ) ;
}
return ;
break ;
}
}
ResultItem * ResultScene : : currentlyFocusedItem ( ) const
{
QGraphicsWidget * widget = static_cast < QGraphicsWidget * > ( focusItem ( ) ) ;
if ( ! widget ) {
return 0 ;
}
ResultItem * currentFocus = qobject_cast < ResultItem * > ( widget ) ;
if ( ! currentFocus ) {
//If we focused an action button, find the resultItem
//FIXME: the config button
currentFocus = qobject_cast < ResultItem * > ( widget - > parentWidget ( ) - > parentWidget ( ) ) ;
}
return currentFocus ;
}
void ResultScene : : selectPreviousItem ( )
{
ResultItem * currentFocus = currentlyFocusedItem ( ) ;
int currentIndex = currentFocus ? currentFocus - > index ( ) : 0 ;
if ( currentIndex = = 0 ) {
m_focusBase - > setFocus ( ) ;
return ;
}
currentFocus = m_items . at ( currentIndex - 1 ) ;
if ( currentFocus - > isVisible ( ) ) {
setFocusItem ( currentFocus ) ;
emit ensureVisibility ( currentFocus ) ;
}
}
void ResultScene : : selectNextItem ( )
{
ResultItem * currentFocus = currentlyFocusedItem ( ) ;
int currentIndex = currentFocus ? currentFocus - > index ( ) : 0 ;
do {
+ + currentIndex ;
if ( currentIndex > = m_items . size ( ) ) {
return ;
}
currentFocus = m_items . at ( currentIndex ) ;
} while ( ! currentFocus - > isVisible ( ) & & currentIndex < m_items . size ( ) ) ;
if ( currentFocus - > isVisible ( ) ) {
setFocusItem ( currentFocus ) ;
emit ensureVisibility ( currentFocus ) ;
}
}
void ResultScene : : queryCleared ( )
{
//m_selectionBar->setTargetItem(0);
setFocusItem ( 0 ) ;
clearSelection ( ) ;
}
ResultItem * ResultScene : : defaultResultItem ( ) const
{
if ( m_items . isEmpty ( ) ) {
kDebug ( ) < < " empty " ;
return 0 ;
}
kDebug ( ) < < ( QObject * ) m_items [ 0 ] < < m_items . count ( ) ;
return m_items [ 0 ] ;
}
void ResultScene : : updateItemMargins ( )
{
m_selectionBar - > getContentsMargins ( & m_itemMarginLeft , & m_itemMarginTop ,
& m_itemMarginRight , & m_itemMarginBottom ) ;
foreach ( ResultItem * item , m_items ) {
item - > setContentsMargins ( m_itemMarginLeft , m_itemMarginTop ,
m_itemMarginRight , m_itemMarginBottom ) ;
}
}
2015-02-27 09:28:46 +00:00
# include "moc_resultscene.cpp"
2014-11-13 19:30:51 +02:00