kontact

iconsidepane.cpp

00001 /*
00002   This file is part of KDE Kontact.
00003 
00004   Copyright (C) 2003 Cornelius Schumacher <schumacher@kde.org>
00005 
00006   This program is free software; you can redistribute it and/or
00007   modify it under the terms of the GNU General Public
00008   License as published by the Free Software Foundation; either
00009   version 2 of the License, or (at your option) any later version.
00010 
00011   This program is distributed in the hope that it will be useful,
00012   but WITHOUT ANY WARRANTY; without even the implied warranty of
00013   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014   General Public License for more details.
00015 
00016   You should have received a copy of the GNU General Public License
00017   along with this program; see the file COPYING.  If not, write to
00018   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019   Boston, MA 02110-1301, USA.
00020  */
00021 
00022 #include <qptrlist.h>
00023 #include <qwidgetstack.h>
00024 #include <qsignal.h>
00025 #include <qobjectlist.h>
00026 #include <qlabel.h>
00027 #include <qimage.h>
00028 #include <qpainter.h>
00029 #include <qbitmap.h>
00030 #include <qfontmetrics.h>
00031 #include <qsignalmapper.h>
00032 #include <qstyle.h>
00033 #include <qframe.h>
00034 #include <qdrawutil.h>
00035 #include <qcursor.h>
00036 #include <qtimer.h>
00037 #include <qtooltip.h>
00038 
00039 #include <kpopupmenu.h>
00040 #include <kapplication.h>
00041 #include <kdialog.h>
00042 #include <klocale.h>
00043 #include <kiconloader.h>
00044 #include <sidebarextension.h>
00045 
00046 #include <kdebug.h>
00047 
00048 #include "mainwindow.h"
00049 
00050 #include "plugin.h"
00051 
00052 #include "prefs.h"
00053 #include "iconsidepane.h"
00054 
00055 namespace Kontact
00056 {
00057 
00058 //ugly wrapper class for adding an operator< to the Plugin class
00059 
00060 class PluginProxy
00061 {
00062   public:
00063     PluginProxy()
00064       : mPlugin( 0 )
00065     { }
00066 
00067     PluginProxy( Plugin *plugin )
00068       : mPlugin( plugin )
00069     { }
00070 
00071     PluginProxy & operator=( Plugin *plugin )
00072     {
00073       mPlugin = plugin;
00074       return *this;
00075     }
00076 
00077     bool operator<( PluginProxy &rhs ) const
00078     {
00079       return mPlugin->weight() < rhs.mPlugin->weight();
00080     }
00081 
00082     Plugin *plugin() const
00083     {
00084       return mPlugin;
00085     }
00086 
00087   private:
00088     Plugin *mPlugin;
00089 };
00090 
00091 } //namespace
00092 
00093 using namespace Kontact;
00094 
00095 EntryItem::EntryItem( Navigator *parent, Kontact::Plugin *plugin )
00096   : QListBoxItem( parent ),
00097     mPlugin( plugin ),
00098     mHasHover( false ),
00099     mPaintActive( false )
00100 {
00101   reloadPixmap();
00102   setCustomHighlighting( true );
00103   setText( plugin->title() );
00104 }
00105 
00106 EntryItem::~EntryItem()
00107 {
00108 }
00109 
00110 void EntryItem::reloadPixmap()
00111 {
00112   int size = (int)navigator()->viewMode();
00113   if ( size != 0 )
00114     mPixmap = KGlobal::iconLoader()->loadIcon( mPlugin->icon(),
00115                                                KIcon::Desktop, size );
00116   else
00117     mPixmap = QPixmap();
00118 }
00119 
00120 Navigator* EntryItem::navigator() const
00121 {
00122   return static_cast<Navigator*>( listBox() );
00123 }
00124 
00125 int EntryItem::width( const QListBox *listbox ) const
00126 {
00127   int w = 0;
00128   if( navigator()->showIcons() ) {
00129     w = navigator()->viewMode();
00130     if ( navigator()->viewMode() == SmallIcons )
00131       w += 4;
00132   }
00133   if( navigator()->showText() ) {
00134     if ( navigator()->viewMode() == SmallIcons )
00135       w += listbox->fontMetrics().width( text() );
00136     else
00137       w = QMAX( w, listbox->fontMetrics().width( text() ) );
00138   }
00139   return w + ( KDialog::marginHint() * 2 );
00140 }
00141 
00142 int EntryItem::height( const QListBox *listbox ) const
00143 {
00144   int h = 0;
00145   if ( navigator()->showIcons() )
00146     h = (int)navigator()->viewMode() + 4;
00147   if ( navigator()->showText() ) {
00148     if ( navigator()->viewMode() == SmallIcons || !navigator()->showIcons() )
00149       h = QMAX( h, listbox->fontMetrics().lineSpacing() ) + KDialog::spacingHint() * 2;
00150     else
00151       h = (int)navigator()->viewMode() + listbox->fontMetrics().lineSpacing() + 4;
00152   }
00153   return h;
00154 }
00155 
00156 void EntryItem::paint( QPainter *p )
00157 {
00158   reloadPixmap();
00159 
00160   QListBox *box = listBox();
00161   bool iconAboveText = ( navigator()->viewMode() > SmallIcons )
00162                      && navigator()->showIcons();
00163   int w = box->viewport()->width();
00164   int y = iconAboveText ? 2 :
00165                         ( ( height( box ) - mPixmap.height() ) / 2 );
00166 
00167   // draw selected
00168   if ( isCurrent() || isSelected() || mHasHover || mPaintActive ) {
00169     int h = height( box );
00170 
00171     QBrush brush;
00172     if ( isCurrent() || isSelected() || mPaintActive )
00173       brush = box->colorGroup().brush( QColorGroup::Highlight );
00174     else
00175       brush = box->colorGroup().highlight().light( 115 );
00176     p->fillRect( 1, 0, w - 2, h - 1, brush );
00177     QPen pen = p->pen();
00178     QPen oldPen = pen;
00179     pen.setColor( box->colorGroup().mid() );
00180     p->setPen( pen );
00181 
00182     p->drawPoint( 1, 0 );
00183     p->drawPoint( 1, h - 2 );
00184     p->drawPoint( w - 2, 0 );
00185     p->drawPoint( w - 2, h - 2 );
00186 
00187     p->setPen( oldPen );
00188   }
00189 
00190   if ( !mPixmap.isNull() && navigator()->showIcons() ) {
00191       int x = iconAboveText ? ( ( w - mPixmap.width() ) / 2 ) :
00192                               KDialog::marginHint();
00193     p->drawPixmap( x, y, mPixmap );
00194   }
00195 
00196   QColor shadowColor = listBox()->colorGroup().background().dark(115);
00197   if ( isCurrent() || isSelected() ) {
00198     p->setPen( box->colorGroup().highlightedText() );
00199   }
00200 
00201   if ( !text().isEmpty() && navigator()->showText() ) {
00202     QFontMetrics fm = p->fontMetrics();
00203 
00204     int x = 0;
00205     if ( iconAboveText ) {
00206       x = ( w - fm.width( text() ) ) / 2;
00207       y += fm.height() - fm.descent();
00208       if ( navigator()->showIcons() )
00209         y += mPixmap.height();
00210     } else {
00211       x = KDialog::marginHint() + 4;
00212       if( navigator()->showIcons() ) {
00213         x += mPixmap.width();
00214       }
00215 
00216       if ( !navigator()->showIcons() || mPixmap.height() < fm.height() )
00217         y = height( box )/2 - fm.height()/2 + fm.ascent();
00218       else
00219         y += mPixmap.height()/2 - fm.height()/2 + fm.ascent();
00220     }
00221 
00222     if ( isCurrent() || isSelected() || mHasHover ) {
00223       p->setPen( box->colorGroup().highlight().dark(115) );
00224       p->drawText( x + ( QApplication::reverseLayout() ? -1 : 1),
00225                    y + 1, text() );
00226       p->setPen( box->colorGroup().highlightedText() );
00227     }
00228     else
00229       p->setPen( box->colorGroup().text() );
00230 
00231     p->drawText( x, y, text() );
00232   }
00233 
00234   // ensure that we don't have a stale flag around
00235   if (  isCurrent() || isSelected() ) mHasHover = false;
00236 }
00237 
00238 void EntryItem::setHover( bool hasHover )
00239 {
00240   mHasHover = hasHover;
00241 }
00242 
00243 void EntryItem::setPaintActive( bool paintActive )
00244 {
00245   mPaintActive = paintActive;
00246 }
00247 
00248 Navigator::Navigator( SidePaneBase *parent, const char *name )
00249   : KListBox( parent, name ), mSidePane( parent ),
00250     mShowIcons( true ), mShowText( true )
00251 {
00252   mMouseOn = 0;
00253   mHighlightItem = 0;
00254   mViewMode = sizeIntToEnum( Prefs::self()->sidePaneIconSize() );
00255   mShowIcons = Prefs::self()->sidePaneShowIcons();
00256   mShowText = Prefs::self()->sidePaneShowText();
00257   setSelectionMode( KListBox::Single );
00258   viewport()->setBackgroundMode( PaletteBackground );
00259   setFrameStyle( QFrame::NoFrame );
00260   setHScrollBarMode( QScrollView::AlwaysOff );
00261   setAcceptDrops( true );
00262 
00263   setFocusPolicy( NoFocus );
00264 
00265   connect( this, SIGNAL( selectionChanged( QListBoxItem* ) ),
00266            SLOT( slotExecuted( QListBoxItem* ) ) );
00267   connect( this, SIGNAL( rightButtonPressed( QListBoxItem*, const QPoint& ) ),
00268            SLOT( slotShowRMBMenu( QListBoxItem*, const QPoint& ) ) );
00269   connect( this, SIGNAL( onItem( QListBoxItem * ) ),
00270             SLOT(  slotMouseOn( QListBoxItem * ) ) );
00271   connect( this, SIGNAL( onViewport() ), SLOT(  slotMouseOff() ) );
00272 
00273   mMapper = new QSignalMapper( this );
00274   connect( mMapper, SIGNAL( mapped( int ) ), SLOT( shortCutSelected( int ) ) );
00275 
00276   QToolTip::remove( this );
00277   if ( !mShowText )
00278     new EntryItemToolTip( this );
00279 
00280 }
00281 
00282 QSize Navigator::sizeHint() const
00283 {
00284   return QSize( 100, 100 );
00285 }
00286 
00287 void Navigator::highlightItem( EntryItem * item )
00288 {
00289   mHighlightItem = item;
00290 
00291   setPaintActiveItem( mHighlightItem, true );
00292 
00293   QTimer::singleShot( 2000, this, SLOT( slotStopHighlight() ) );
00294 }
00295 
00296 void Navigator::slotStopHighlight()
00297 {
00298   setPaintActiveItem( mHighlightItem, false );
00299 }
00300 
00301 void Navigator::setSelected( QListBoxItem *item, bool selected )
00302 {
00303   // Reimplemented to avoid the immediate activation of
00304   // the item. might turn out it doesn't work, we check that
00305   // an confirm from MainWindow::selectPlugin()
00306   if ( selected ) {
00307     EntryItem *entry = static_cast<EntryItem*>( item );
00308     emit pluginActivated( entry->plugin() );
00309   }
00310 }
00311 
00312 void Navigator::updatePlugins( QValueList<Kontact::Plugin*> plugins_ )
00313 {
00314   QValueList<Kontact::PluginProxy> plugins;
00315   QValueList<Kontact::Plugin*>::ConstIterator end_ = plugins_.end();
00316   QValueList<Kontact::Plugin*>::ConstIterator it_ = plugins_.begin();
00317   for ( ; it_ != end_; ++it_ )
00318     plugins += PluginProxy( *it_ );
00319 
00320   clear();
00321 
00322   mActions.setAutoDelete( true );
00323   mActions.clear();
00324   mActions.setAutoDelete( false );
00325 
00326   int counter = 0;
00327   int minWidth = 0;
00328   qBubbleSort( plugins );
00329   QValueList<Kontact::PluginProxy>::ConstIterator end = plugins.end();
00330   QValueList<Kontact::PluginProxy>::ConstIterator it = plugins.begin();
00331   for ( ; it != end; ++it ) {
00332     Kontact::Plugin *plugin = ( *it ).plugin();
00333     if ( !plugin->showInSideBar() )
00334       continue;
00335 
00336     EntryItem *item = new EntryItem( this, plugin );
00337 
00338     if ( item->width( this ) > minWidth )
00339       minWidth = item->width( this );
00340 
00341     QString name = QString( "CTRL+%1" ).arg( counter + 1 );
00342     KAction *action = new KAction( plugin->title(), plugin->icon(), KShortcut( name ),
00343                                    mMapper, SLOT( map() ),
00344                                    mSidePane->actionCollection(), name.latin1() );
00345     mActions.append( action );
00346     mMapper->setMapping( action, counter );
00347     counter++;
00348   }
00349 
00350   parentWidget()->setFixedWidth( minWidth );
00351 }
00352 
00353 void Navigator::dragEnterEvent( QDragEnterEvent *event )
00354 {
00355   kdDebug(5600) << "Navigator::dragEnterEvent()" << endl;
00356 
00357   dragMoveEvent( event );
00358 }
00359 
00360 void Navigator::dragMoveEvent( QDragMoveEvent *event )
00361 {
00362   kdDebug(5600) << "Navigator::dragEnterEvent()" << endl;
00363 
00364   kdDebug(5600) << "  Format: " << event->format() << endl;
00365 
00366   QListBoxItem *item = itemAt( event->pos() );
00367 
00368   if ( !item ) {
00369     event->accept( false );
00370     return;
00371   }
00372 
00373   EntryItem *entry = static_cast<EntryItem*>( item );
00374 
00375   kdDebug(5600) << "  PLUGIN: " << entry->plugin()->identifier() << endl;
00376 
00377   event->accept( entry->plugin()->canDecodeDrag( event ) );
00378 }
00379 
00380 void Navigator::dropEvent( QDropEvent *event )
00381 {
00382   kdDebug(5600) << "Navigator::dropEvent()" << endl;
00383 
00384   QListBoxItem *item = itemAt( event->pos() );
00385 
00386   if ( !item ) {
00387     return;
00388   }
00389 
00390   EntryItem *entry = static_cast<EntryItem*>( item );
00391 
00392   kdDebug(5600) << "  PLUGIN: " << entry->plugin()->identifier() << endl;
00393 
00394   entry->plugin()->processDropEvent( event );
00395 }
00396 
00397 void Navigator::resizeEvent( QResizeEvent *event )
00398 {
00399   QListBox::resizeEvent( event );
00400   triggerUpdate( true );
00401 }
00402 
00403 void Navigator::enterEvent( QEvent *event )
00404 {
00405   // work around Qt behaviour: onItem is not emmitted in enterEvent()
00406   KListBox::enterEvent( event );
00407   emit onItem( itemAt( mapFromGlobal( QCursor::pos() ) ) );
00408 }
00409 
00410 void Navigator::leaveEvent( QEvent *event )
00411 {
00412   KListBox::leaveEvent( event );
00413   slotMouseOn( 0 );
00414   mMouseOn = 0;
00415 }
00416 
00417 void Navigator::slotExecuted( QListBoxItem *item )
00418 {
00419   if ( !item )
00420     return;
00421 
00422   EntryItem *entry = static_cast<EntryItem*>( item );
00423 
00424   emit pluginActivated( entry->plugin() );
00425 }
00426 
00427 IconViewMode Navigator::sizeIntToEnum(int size) const
00428 {
00429   switch ( size ) {
00430     case int(LargeIcons):
00431       return LargeIcons;
00432       break;
00433     case int(NormalIcons):
00434       return NormalIcons;
00435       break;
00436     case int(SmallIcons):
00437       return SmallIcons;
00438       break;
00439     default:
00440       // Stick with sane values
00441       return NormalIcons;
00442       kdDebug() << "View mode not implemented!" << endl;
00443       break;
00444   }
00445 }
00446 
00447 void Navigator::slotShowRMBMenu( QListBoxItem *, const QPoint &pos )
00448 {
00449   KPopupMenu menu;
00450   menu.insertTitle( i18n( "Icon Size" ) );
00451   menu.insertItem( i18n( "Large" ), (int)LargeIcons );
00452   menu.setItemEnabled( (int)LargeIcons, mShowIcons );
00453   menu.insertItem( i18n( "Normal" ), (int)NormalIcons );
00454   menu.setItemEnabled( (int)NormalIcons, mShowIcons );
00455   menu.insertItem( i18n( "Small" ), (int)SmallIcons );
00456   menu.setItemEnabled( (int)SmallIcons, mShowIcons );
00457 
00458   menu.setItemChecked( (int)mViewMode, true );
00459   menu.insertSeparator();
00460 
00461   menu.insertItem( i18n( "Show Icons" ), (int)ShowIcons );
00462   menu.setItemChecked( (int)ShowIcons, mShowIcons );
00463   menu.setItemEnabled( (int)ShowIcons, mShowText );
00464   menu.insertItem( i18n( "Show Text" ), (int)ShowText );
00465   menu.setItemChecked( (int)ShowText, mShowText );
00466   menu.setItemEnabled( (int)ShowText, mShowIcons );
00467   int choice = menu.exec( pos );
00468 
00469   if ( choice == -1 )
00470     return;
00471 
00472   if ( choice >= SmallIcons ) {
00473     mViewMode = sizeIntToEnum( choice );
00474     Prefs::self()->setSidePaneIconSize( choice );
00475   } else {
00476     // either icons or text were toggled
00477     if ( choice == ShowIcons ) {
00478       mShowIcons = !mShowIcons;
00479       Prefs::self()->setSidePaneShowIcons( mShowIcons );
00480       QToolTip::remove( this );
00481       if ( !mShowText )
00482         new EntryItemToolTip( this );
00483     } else {
00484       mShowText = !mShowText;
00485       Prefs::self()->setSidePaneShowText( mShowText );
00486       QToolTip::remove( this );
00487     }
00488   }
00489   int maxWidth = 0;
00490   QListBoxItem* it = 0;
00491   for (int i = 0; (it = item(i)) != 0; ++i)
00492   {
00493     int width = it->width(this);
00494     if (width > maxWidth)
00495       maxWidth = width;
00496   }
00497   parentWidget()->setFixedWidth( maxWidth );
00498 
00499   triggerUpdate( true );
00500 }
00501 
00502 void Navigator::shortCutSelected( int pos )
00503 {
00504   setCurrentItem( pos );
00505 }
00506 
00507 void Navigator::setHoverItem( QListBoxItem* item, bool hover )
00508 {
00509     static_cast<EntryItem*>( item )->setHover( hover );
00510     updateItem( item );
00511 }
00512 
00513 void Navigator::setPaintActiveItem( QListBoxItem* item, bool paintActive )
00514 {
00515     static_cast<EntryItem*>( item )->setPaintActive( paintActive );
00516     updateItem( item );
00517 }
00518 
00519 void Navigator::slotMouseOn( QListBoxItem* newItem )
00520 {
00521   QListBoxItem* oldItem = mMouseOn;
00522   if ( oldItem == newItem ) return;
00523 
00524   if ( oldItem && !oldItem->isCurrent() && !oldItem->isSelected() )
00525   {
00526     setHoverItem( oldItem, false );
00527   }
00528 
00529   if ( newItem && !newItem->isCurrent() && !newItem->isSelected() )
00530   {
00531     setHoverItem( newItem, true );
00532   }
00533   mMouseOn = newItem;
00534 }
00535 
00536 void Navigator::slotMouseOff()
00537 {
00538   slotMouseOn( 0 );
00539 }
00540 
00541 IconSidePane::IconSidePane( Core *core, QWidget *parent, const char *name )
00542   : SidePaneBase( core, parent, name )
00543 {
00544   mNavigator = new Navigator( this );
00545   connect( mNavigator, SIGNAL( pluginActivated( Kontact::Plugin* ) ),
00546            SIGNAL( pluginSelected( Kontact::Plugin* ) ) );
00547 
00548   setAcceptDrops( true );
00549 }
00550 
00551 IconSidePane::~IconSidePane()
00552 {
00553 }
00554 
00555 void IconSidePane::updatePlugins()
00556 {
00557   mNavigator->updatePlugins( core()->pluginList() );
00558 }
00559 
00560 void IconSidePane::selectPlugin( Kontact::Plugin *plugin )
00561 {
00562   bool blocked = signalsBlocked();
00563   blockSignals( true );
00564 
00565   for ( uint i = 0; i < mNavigator->count(); ++i ) {
00566     EntryItem *item = static_cast<EntryItem*>( mNavigator->item( i ) );
00567     if ( item->plugin() == plugin ) {
00568       mNavigator->setCurrentItem( i );
00569       break;
00570     }
00571   }
00572 
00573   blockSignals( blocked );
00574 }
00575 
00576 void IconSidePane::selectPlugin( const QString &name )
00577 {
00578   bool blocked = signalsBlocked();
00579   blockSignals( true );
00580 
00581   for ( uint i = 0; i < mNavigator->count(); ++i ) {
00582     EntryItem *item = static_cast<EntryItem*>( mNavigator->item( i ) );
00583     if ( item->plugin()->identifier() == name ) {
00584       mNavigator->setCurrentItem( i );
00585       break;
00586     }
00587   }
00588 
00589   blockSignals( blocked );
00590 }
00591 
00592 void IconSidePane::indicateForegrunding( Kontact::Plugin *plugin )
00593 {
00594   for ( uint i = 0; i < mNavigator->count(); ++i ) {
00595     EntryItem *item = static_cast<EntryItem*>( mNavigator->item( i ) );
00596     if ( item->plugin() == plugin ) {
00597       mNavigator->highlightItem( item );
00598       break;
00599     }
00600   }
00601 
00602 
00603 }
00604 #include "iconsidepane.moc"
00605 
00606 // vim: sw=2 sts=2 et tw=80
KDE Home | KDE Accessibility Home | Description of Access Keys