You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tastymenu/src/tastylistview.cpp

601 lines
16 KiB

/***************************************************************************
* Copyright (C) 2006 by Marco Martin *
* notmart@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the Lesser 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "tastylistview.h"
#include "misc.h"
#include <kdeversion.h>
#include <kstringhandler.h>
#include <qimage.h>
#include <qpainter.h>
#include <qheader.h>
#include <klocale.h>
#include <kurldrag.h>
#include <qapplication.h>
TastyListView::TastyListView( QWidget * parent, const char * name)
: KListView(parent, name), highLightGroups(true), easyOpen(true)
{
onItemTimer = new QTimer(this, "onItemTimer");
underCursorItem = openItem = NULL;
mouseDown = false;
actionIconSize = 16;
actionIconSpace = 32;
listItemTip = new TastyListViewToolTip(viewport(), this);
connect(this, SIGNAL(onItem(QListViewItem *) ), SLOT(slotOnItem(QListViewItem *) ) );
connect(onItemTimer, SIGNAL(timeout()), this, SLOT(slotTimeout()) );
}
TastyListView::~TastyListView()
{
}
void TastyListView::startDrag()
{
if( !currentItem() )
return;
TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
if( !item )
return;
QDragObject *d = new KURLDrag( KURL(item->getDeskopEntryPath()) , viewport() );
if(!d)
return;
if (d->drag() && d->target() != viewport())
emit moved();
}
void TastyListView::contentsMouseReleaseEvent( QMouseEvent * e )
{
int x = e->x();
if( x > width() || x < 0)
return;
if( !currentItem() )
return;
TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
if( !item )
return;
if( e->button() == RightButton )
emit(contextMenuRequested( currentItem(), e->globalPos(), 0) );
else
emit(activated( currentItem(), QPoint(x, e->y()), 0) );
if(item && (item->getType() == TastyListViewItem::ServiceGroup))
{
if( !openItem )
{
openItem = currentItem();
return;
}
TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
openItem = currentItem();
if( !oldOpenItem || !oldOpenItem->listView() )
return;
oldOpenItem->repaint();
}
KListView::contentsMouseReleaseEvent(e);
}
void TastyListView::contentsMouseMoveEvent( QMouseEvent * e )
{
KListView::contentsMouseMoveEvent(e);
mouseDown = (e->state() & Qt::LeftButton);
if( itemAt( contentsToViewport(QPoint(e->x(), e->y()))) == 0 )
underCursorItem = NULL;
}
void TastyListView::leaveEvent( QEvent * e )
{
KListView::leaveEvent( e );
onItemTimer->stop();
if( openItem )
setCurrentItem( openItem );
}
void TastyListView::keyPressEvent( QKeyEvent * e )
{
switch(e->key())
{
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Space:
{
emit(activated(currentItem(), QPoint(0,0), 0));
if(!currentItem())
return;
TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
if(item && item->getType() == TastyListViewItem::ServiceGroup)
{
if( !openItem )
{
openItem = currentItem();
return;
}
TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
openItem = currentItem();
if( !oldOpenItem || !oldOpenItem->listView() )
return;
oldOpenItem->repaint();
}
}
break;
case Qt::Key_Up:
case Qt::Key_Down:
KListView::keyPressEvent( e );
break;
case Qt::Key_Right:
{
if(!currentItem())
return;
TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
if(item && easyOpen && !QApplication::reverseLayout() && item->getType() == TastyListViewItem::ServiceGroup)
{
emit( activated( currentItem(), QPoint(0,0), 0));
if( !openItem )
{
openItem = currentItem();
return;
}
TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
openItem = currentItem();
if( !oldOpenItem || !oldOpenItem->listView() )
return;
oldOpenItem->repaint();
}
focusNextPrevChild(!QApplication::reverseLayout());
break;
}
case Qt::Key_Left:
{
if( !currentItem() )
return;
TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem());
if(item && easyOpen && QApplication::reverseLayout() && item->getType() == TastyListViewItem::ServiceGroup)
{
emit( activated( currentItem(), QPoint(0,0), 0));
if( !openItem )
{
openItem = currentItem();
return;
}
TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
openItem = currentItem();
if( !oldOpenItem || !oldOpenItem->listView() )
return;
oldOpenItem->repaint();
}
focusNextPrevChild(QApplication::reverseLayout());
break;
}
case Qt::Key_Tab:
KListView::keyPressEvent( e );
break;
default:
break;
}
}
void TastyListView::slotOnItem( QListViewItem * listItem )
{
if( !listItem || listItem->listView() != this )
return;
if( listItem != underCursorItem )
{
underCursorItem = listItem;
setCurrentItem(listItem);
if(mouseDown)
onItemTimer->start(250, true);
else
onItemTimer->start(1000, true);
}
}
void TastyListView::slotTimeout( )
{
if( !underCursorItem /*|| !openItem*/ )
return;
TastyListViewItem *tastyUnderCursorItem = dynamic_cast<TastyListViewItem *>(underCursorItem);
if( easyOpen && tastyUnderCursorItem &&
tastyUnderCursorItem->getType() == TastyListViewItem::ServiceGroup )
{
emit(activated(underCursorItem, QPoint(underCursorItem->listView()->width()/2,1), 0));
TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem);
openItem = currentItem();
if( !oldOpenItem || !oldOpenItem->listView() )
return;
oldOpenItem->repaint();
}
}
///////////TASTYLISTVIEWTOOLTIP
TastyListViewToolTip::TastyListViewToolTip( QWidget *parent, TastyListView *tListView )
: QToolTip( parent ), listView( tListView )
{
}
void TastyListViewToolTip::maybeTip( const QPoint &pos )
{
if( !parentWidget() || !listView || !listView->showToolTips() )
return;
TastyListViewItem *item = static_cast<TastyListViewItem *>(listView->itemAt( pos ));
QPoint contentsPos = listView->viewportToContents( pos );
if( !item || !listView->columns() )
return;
int actionWidth = 0;
TastyListViewItem::ActionType actionType = item->getActionType();
if( actionType != TastyListViewItem::NoAction )
actionWidth = listView->getActionIconSpace();
int column = listView->header()->sectionAt( contentsPos.x() );
QRect r = listView->itemRect( item );
int headerPos = listView->header()->sectionPos( column );
r.setLeft( headerPos );
r.setRight( headerPos + listView->header()->sectionSize( column ) );
int actionLeft = r.right()-actionWidth;
if( pos.x() >= actionLeft )
{
r.setLeft( actionLeft );
switch( actionType )
{
case TastyListViewItem::AddBookMark:
tip( r, i18n( "Add" )+" \""+item->text( column )+"\" "+i18n( "to your favourite applications" ) );
return;
case TastyListViewItem::RemoveBookMark:
tip( r, i18n( "Remove" )+" \""+item->text( column )+"\" "+i18n( "from your favourite applications" ) );
return;
case TastyListViewItem::OpenGroup:
tip( r, i18n( "Browse" )+" \""+item->text( column )+"\"" );
return;
case TastyListViewItem::Expand:
tip( r, i18n( "Expand" )+" \""+item->text( column )+"\"" );
return;
case TastyListViewItem::Collapse:
tip( r, i18n( "Collapse" )+" \""+item->text( column )+"\"" );
return;
default:
break;
}
}
else if( actionType == TastyListViewItem::OpenGroup && !item->hasEllipsis() )
{
tip( r, i18n( "Browse" )+" \""+item->text( column )+"\"" );
return;
}
if( !item->hasEllipsis() )
return;
tip( r, item->text( column )+"\n"+item->getSubText() );
}
///////////TASTYLISTVIEWITEM
TastyListViewItem::TastyListViewItem( TastyListView * parent )
: KListViewItem(parent)
{commonConstructor();}
TastyListViewItem::TastyListViewItem( TastyListViewItem * parent )
: KListViewItem(parent)
{commonConstructor();}
TastyListViewItem::TastyListViewItem( TastyListView * parent, TastyListViewItem * after, QString label1 )
: KListViewItem(parent, after, label1)
{commonConstructor();cellText = label1;}
TastyListViewItem::TastyListViewItem( TastyListViewItem * parent, TastyListViewItem * after, QString label1 )
: KListViewItem(parent, after, label1)
{commonConstructor();cellText = label1;}
TastyListViewItem::TastyListViewItem( TastyListView * parent, QString label1 )
: KListViewItem(parent, label1)
{commonConstructor();cellText = label1;}
TastyListViewItem::TastyListViewItem( TastyListViewItem * parent, QString label1 )
: KListViewItem(parent, label1)
{commonConstructor();cellText = label1;}
TastyListViewItem::~TastyListViewItem()
{
}
void TastyListViewItem::commonConstructor()
{
subText="";cellText="";
actionType = NoAction;
actionPix = QPixmap();
menuId = QString();
desktopEntryPath = QString();
path = QString();
ellipsis = false;
highLight = false;
displaySubText = true;
}
void TastyListViewItem::loadPixmap()
{
QString iconFile = "";
iconLoader = KGlobal::iconLoader();
if( !listView() )
return;
TastyListView *lv = dynamic_cast<TastyListView *>(listView());
if( !lv )
return;
switch( actionType )
{
case AddBookMark:
actionPix = iconLoader->loadIcon("bookmark_add", KIcon::Small, lv->getActionIconSize());
break;
case RemoveBookMark:
actionPix = iconLoader->loadIcon("remove", KIcon::Small, lv->getActionIconSize());
break;
case OpenGroup:
if( QApplication::reverseLayout() )
actionPix = iconLoader->loadIcon("1leftarrow", KIcon::Small, lv->getActionIconSize());
else
actionPix = iconLoader->loadIcon("1rightarrow", KIcon::Small, lv->getActionIconSize());
break;
case Expand:
actionPix = iconLoader->loadIcon("1downarrow", KIcon::Small, lv->getActionIconSize());
break;
case Collapse:
actionPix = iconLoader->loadIcon("1uparrow", KIcon::Small, lv->getActionIconSize());
break;
default:
return;
}
if ( actionPix.height () > lv->getActionIconSize())
{
QImage img = actionPix.convertToImage();
if( !img.isNull() )
{
img = img.smoothScale ( lv->getActionIconSize(), lv->getActionIconSize());
actionPix = QPixmap (img);
}
}
}
QString TastyListViewItem::key( int column, bool ascending ) const
{
ascending = ascending;
QString prefix;
//ensure all the categories are before the leaf items
if( itemType == ServiceGroup )
prefix = "0";
else
prefix = "1";
return prefix.append(text( column ));
}
void TastyListViewItem::setup ( )
{
//KListViewItem::setup();
//calculate listitem height
QFontMetrics fm( listView()->font() );
int pixmapHeight = 5;
if( pixmap(0) )
pixmapHeight = pixmap(0)->height()+4;
if( displaySubText && !subText.isEmpty() )
{
int textHeight = (int)(fm.height()*2.5);
setHeight( (pixmapHeight > textHeight) ? pixmapHeight : textHeight );
}
else
setHeight( pixmapHeight );
}
/*Adapted from Amarok's Statistic listView Copyright (c) 2006 Seb Ruiz*/
void TastyListViewItem::paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align )
{
int textHeight = height();
QString name = cellText;
int textX = 0;
QColor fillColor, textColor;
# if KDE_VERSION < KDE_MAKE_VERSION(3,3,91)
# define BackgroundColor backgroundColor()
# else
# define BackgroundColor backgroundColor(0)
# endif
fillColor = isSelected() ? cg.highlight() : BackgroundColor;
textColor = isSelected() ? cg.highlightedText() : cg.text();
if( !listView() )
return;
TastyListView *lv = dynamic_cast<TastyListView *>( listView() );
if( !lv )
return;
QFont font( lv->font() );
if( !isSelected() && (lv->getOpenItem() == this||
(lv->getHighLightGroups() && itemType == ServiceGroup)) )
fillColor = alphaBlendColors( fillColor, cg.highlight(), 200);
else if( !isSelected() && highLight )
{
int hue, saturation, value;
cg.highlight().getHsv(&hue, &saturation, &value);
//calculate the inverse color 128 means rotating the spectral value by 180 degrees
fillColor.setHsv( (hue+128)%256, saturation/2, value );
}
else if( isSelected() && !lv->hasFocus() )
fillColor = alphaBlendColors( fillColor, BackgroundColor, 150);
QFontMetrics fm( font );
widthChanged(column);
QPixmap buffer(width*2, textHeight);
if( buffer.isNull() )
return;
buffer.fill( fillColor );
QPainter pBuf(&buffer);
if( pixmap( column ) )
{
int y = (textHeight - pixmap(column)->height())/2;
pBuf.drawPixmap( 0, y, *pixmap(column) );
textX += pixmap(column)->width() + 4;
}
//Calculate the ellipsis for the MAIN text
int extraSpace = fm.width("...") + textX + lv->getActionIconSpace();
ellipsis = false;
while( (fm.width(name)+extraSpace) > width && name.length() > 4)
{
name.truncate(name.length()-1);
ellipsis = true;
}
if( ellipsis )
name.append("...");
if( name == "separator" )
{
int y = textHeight/2;
pBuf.setPen(cg.background().dark(140));
pBuf.drawLine(textX, y, width, y);
pBuf.setPen(textColor);
pBuf.end();
p->drawPixmap( 0, 0, buffer );
return;
}
if( fm.width( name ) + textX + lv->itemMargin()*2 > width )
{
const int _width = width - textX - lv->itemMargin()*2;
name = KStringHandler::rPixelSqueeze( name, pBuf.fontMetrics(), _width );
}
pBuf.setPen(textColor);
pBuf.drawText( textX, 3, width, textHeight, AlignTop, name );
if( displaySubText && !subText.isEmpty() )
{
font.setPointSize( max((int)(font.pointSize()/1.2), 7) );
pBuf.setFont( font );
QString subTextCopy = subText;
QFontMetrics sfm( font );
//Calculate the ellipsis for the subtext
int extraSpace = fm.width("...") + textX + lv->getActionIconSpace();
bool ellipsisSubText = false;
while( (sfm.width(subTextCopy)+extraSpace) > width && subTextCopy.length() > 4)
{
subTextCopy.truncate(subTextCopy.length()-1);
ellipsisSubText = true;
}
if( ellipsisSubText )
{
subTextCopy.append("...");
ellipsis = true;
}
pBuf.setPen(cg.background().dark(140));
pBuf.drawLine(textX, fm.height() + 3, width-textX-5, fm.height() + 3);
pBuf.setPen(textColor.light(130));
pBuf.drawText( textX, fm.height() + 4, width, fm.height(), AlignTop, subTextCopy );
}
if( !actionPix.isNull() &&
(actionType == OpenGroup ||
actionType == Expand ||
actionType == Collapse ||
lv->currentItem() == this) )
{
int y = (textHeight - actionPix.height())/2;
pBuf.drawPixmap( width-(actionPix.width()+5), y, actionPix );
}
pBuf.end();
p->drawPixmap( 0, 0, buffer );
}
//EOF