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 <tqptrlist.h>
00023 #include <tqwidgetstack.h>
00024 #include <tqsignal.h>
00025 #include <tqobjectlist.h>
00026 #include <tqlabel.h>
00027 #include <tqimage.h>
00028 #include <tqpainter.h>
00029 #include <tqbitmap.h>
00030 #include <tqfontmetrics.h>
00031 #include <tqsignalmapper.h>
00032 #include <tqstyle.h>
00033 #include <tqframe.h>
00034 #include <tqdrawutil.h>
00035 #include <tqcursor.h>
00036 #include <tqtimer.h>
00037 #include <tqtooltip.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   : TQListBoxItem( 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                                                mPlugin->disabled() ?
00117                                                  KIcon::DisabledState
00118                                                : KIcon::DefaultState);
00119   else
00120     mPixmap = TQPixmap();
00121 }
00122 
00123 Navigator* EntryItem::navigator() const
00124 {
00125   return static_cast<Navigator*>( listBox() );
00126 }
00127 
00128 int EntryItem::width( const TQListBox *listbox ) const
00129 {
00130   int w = 0;
00131   if( navigator()->showIcons() ) {
00132     w = navigator()->viewMode();
00133     if ( navigator()->viewMode() == SmallIcons )
00134       w += 4;
00135   }
00136   if( navigator()->showText() ) {
00137     if ( navigator()->viewMode() == SmallIcons )
00138       w += listbox->fontMetrics().width( text() );
00139     else
00140       w = TQMAX( w, listbox->fontMetrics().width( text() ) );
00141   }
00142   return w + ( KDialog::marginHint() * 2 );
00143 }
00144 
00145 int EntryItem::height( const TQListBox *listbox ) const
00146 {
00147   int h = 0;
00148   if ( navigator()->showIcons() )
00149     h = (int)navigator()->viewMode() + 4;
00150   if ( navigator()->showText() ) {
00151     if ( navigator()->viewMode() == SmallIcons || !navigator()->showIcons() )
00152       h = TQMAX( h, listbox->fontMetrics().lineSpacing() ) + KDialog::spacingHint() * 2;
00153     else
00154       h = (int)navigator()->viewMode() + listbox->fontMetrics().lineSpacing() + 4;
00155   }
00156   return h;
00157 }
00158 
00159 void EntryItem::paint( TQPainter *p )
00160 {
00161   reloadPixmap();
00162 
00163   TQListBox *box = listBox();
00164   bool iconAboveText = ( navigator()->viewMode() > SmallIcons )
00165                      && navigator()->showIcons();
00166   int w = box->viewport()->width();
00167   int y = iconAboveText ? 2 :
00168                         ( ( height( box ) - mPixmap.height() ) / 2 );
00169 
00170   // draw selected
00171   if ( isCurrent() || isSelected() || mHasHover || mPaintActive ) {
00172     int h = height( box );
00173 
00174     TQBrush brush;
00175     if ( isCurrent() || isSelected() || mPaintActive )
00176       brush = box->colorGroup().brush( TQColorGroup::Highlight );
00177     else
00178       brush = TQBrush(box->colorGroup().highlight().light( 115 ));
00179     p->fillRect( 1, 0, w - 2, h - 1, brush );
00180     TQPen pen = p->pen();
00181     TQPen oldPen = pen;
00182     pen.setColor( box->colorGroup().mid() );
00183     p->setPen( pen );
00184 
00185     p->drawPoint( 1, 0 );
00186     p->drawPoint( 1, h - 2 );
00187     p->drawPoint( w - 2, 0 );
00188     p->drawPoint( w - 2, h - 2 );
00189 
00190     p->setPen( oldPen );
00191   }
00192 
00193   if ( !mPixmap.isNull() && navigator()->showIcons() ) {
00194       int x = iconAboveText ? ( ( w - mPixmap.width() ) / 2 ) :
00195                               KDialog::marginHint();
00196     p->drawPixmap( x, y, mPixmap );
00197   }
00198 
00199   TQColor shadowColor = listBox()->colorGroup().background().dark(115);
00200   if ( isCurrent() || isSelected() ) {
00201     p->setPen( box->colorGroup().highlightedText() );
00202   }
00203 
00204   if ( !text().isEmpty() && navigator()->showText() ) {
00205     TQFontMetrics fm = p->fontMetrics();
00206 
00207     int x = 0;
00208     if ( iconAboveText ) {
00209       x = ( w - fm.width( text() ) ) / 2;
00210       y += fm.height() - fm.descent();
00211       if ( navigator()->showIcons() )
00212         y += mPixmap.height();
00213     } else {
00214       x = KDialog::marginHint() + 4;
00215       if( navigator()->showIcons() ) {
00216         x += mPixmap.width();
00217       }
00218 
00219       if ( !navigator()->showIcons() || mPixmap.height() < fm.height() )
00220         y = height( box )/2 - fm.height()/2 + fm.ascent();
00221       else
00222         y += mPixmap.height()/2 - fm.height()/2 + fm.ascent();
00223     }
00224 
00225     if ( plugin()->disabled() ) {
00226       p->setPen( box->palette().disabled().text( ) );
00227     } else if ( isCurrent() || isSelected() || mHasHover ) {
00228       p->setPen( box->colorGroup().highlight().dark(115) );
00229       p->drawText( x + ( TQApplication::reverseLayout() ? -1 : 1),
00230           y + 1, text() );
00231       p->setPen( box->colorGroup().highlightedText() );
00232     }
00233     else
00234       p->setPen( box->colorGroup().text() );
00235 
00236     p->drawText( x, y, text() );
00237   }
00238 
00239   // ensure that we don't have a stale flag around
00240   if (  isCurrent() || isSelected() ) mHasHover = false;
00241 }
00242 
00243 void EntryItem::setHover( bool hasHover )
00244 {
00245   mHasHover = hasHover;
00246 }
00247 
00248 void EntryItem::setPaintActive( bool paintActive )
00249 {
00250   mPaintActive = paintActive;
00251 }
00252 
00253 Navigator::Navigator( IconSidePane *parent, const char *name )
00254   : KListBox( parent, name ), mSidePane( parent ),
00255     mShowIcons( true ), mShowText( true )
00256 {
00257   mMouseOn = 0;
00258   mHighlightItem = 0;
00259   mViewMode = sizeIntToEnum( Prefs::self()->sidePaneIconSize() );
00260   mShowIcons = Prefs::self()->sidePaneShowIcons();
00261   mShowText = Prefs::self()->sidePaneShowText();
00262   setSelectionMode( KListBox::Single );
00263   viewport()->setBackgroundMode( PaletteBackground );
00264   setFrameStyle( TQFrame::NoFrame );
00265   setHScrollBarMode( TQScrollView::AlwaysOff );
00266   setAcceptDrops( true );
00267 
00268   setFocusPolicy( TQ_NoFocus );
00269 
00270   connect( this, TQT_SIGNAL( selectionChanged( TQListBoxItem* ) ),
00271            TQT_SLOT( slotExecuted( TQListBoxItem* ) ) );
00272   connect( this, TQT_SIGNAL( rightButtonPressed( TQListBoxItem*, const TQPoint& ) ),
00273            TQT_SLOT( slotShowRMBMenu( TQListBoxItem*, const TQPoint& ) ) );
00274   connect( this, TQT_SIGNAL( onItem( TQListBoxItem * ) ),
00275             TQT_SLOT(  slotMouseOn( TQListBoxItem * ) ) );
00276   connect( this, TQT_SIGNAL( onViewport() ), TQT_SLOT(  slotMouseOff() ) );
00277 
00278   mMapper = new TQSignalMapper( TQT_TQOBJECT(this) );
00279   connect( mMapper, TQT_SIGNAL( mapped( int ) ), TQT_SLOT( shortCutSelected( int ) ) );
00280 
00281   TQToolTip::remove( this );
00282   if ( !mShowText )
00283     new EntryItemToolTip( this );
00284 
00285 }
00286 
00287 TQSize Navigator::sizeHint() const
00288 {
00289   return TQSize( 100, 100 );
00290 }
00291 
00292 void Navigator::highlightItem( EntryItem * item )
00293 {
00294   mHighlightItem = item;
00295 
00296   setPaintActiveItem( mHighlightItem, true );
00297 
00298   TQTimer::singleShot( 2000, this, TQT_SLOT( slotStopHighlight() ) );
00299 }
00300 
00301 void Navigator::slotStopHighlight()
00302 {
00303   setPaintActiveItem( mHighlightItem, false );
00304 }
00305 
00306 void Navigator::setSelected( TQListBoxItem *item, bool selected )
00307 {
00308   // Reimplemented to avoid the immediate activation of
00309   // the item. might turn out it doesn't work, we check that
00310   // an confirm from MainWindow::selectPlugin()
00311   if ( selected ) {
00312     EntryItem *entry = static_cast<EntryItem*>( item );
00313     emit pluginActivated( entry->plugin() );
00314   }
00315 }
00316 
00317 void Navigator::updatePlugins( TQValueList<Kontact::Plugin*> plugins_ )
00318 {
00319   TQValueList<Kontact::PluginProxy> plugins;
00320   TQValueList<Kontact::Plugin*>::ConstIterator end_ = plugins_.end();
00321   TQValueList<Kontact::Plugin*>::ConstIterator it_ = plugins_.begin();
00322   for ( ; it_ != end_; ++it_ )
00323     plugins += PluginProxy( *it_ );
00324 
00325   clear();
00326 
00327   mActions.setAutoDelete( true );
00328   mActions.clear();
00329   mActions.setAutoDelete( false );
00330 
00331   int minWidth = 0;
00332   qBubbleSort( plugins );
00333   TQValueList<Kontact::PluginProxy>::ConstIterator end = plugins.end();
00334   TQValueList<Kontact::PluginProxy>::ConstIterator it = plugins.begin();
00335   for ( ; it != end; ++it ) {
00336     Kontact::Plugin *plugin = ( *it ).plugin();
00337     if ( !plugin->showInSideBar() )
00338       continue;
00339 
00340     EntryItem *item = new EntryItem( this, plugin );
00341     item->setSelectable( !plugin->disabled() );
00342 
00343     if ( item->width( this ) > minWidth )
00344       minWidth = item->width( this );
00345   }
00346 
00347   parentWidget()->setFixedWidth( minWidth );
00348 }
00349 
00350 void Navigator::dragEnterEvent( TQDragEnterEvent *event )
00351 {
00352   kdDebug(5600) << "Navigator::dragEnterEvent()" << endl;
00353 
00354   dragMoveEvent( event );
00355 }
00356 
00357 void Navigator::dragMoveEvent( TQDragMoveEvent *event )
00358 {
00359   kdDebug(5600) << "Navigator::dragEnterEvent()" << endl;
00360 
00361   kdDebug(5600) << "  Format: " << event->format() << endl;
00362 
00363   TQListBoxItem *item = itemAt( event->pos() );
00364 
00365   if ( !item ) {
00366     event->accept( false );
00367     return;
00368   }
00369 
00370   EntryItem *entry = static_cast<EntryItem*>( item );
00371 
00372   kdDebug(5600) << "  PLUGIN: " << entry->plugin()->identifier() << endl;
00373 
00374   event->accept( entry->plugin()->canDecodeDrag( event ) );
00375 }
00376 
00377 void Navigator::dropEvent( TQDropEvent *event )
00378 {
00379   kdDebug(5600) << "Navigator::dropEvent()" << endl;
00380 
00381   TQListBoxItem *item = itemAt( event->pos() );
00382 
00383   if ( !item ) {
00384     return;
00385   }
00386 
00387   EntryItem *entry = static_cast<EntryItem*>( item );
00388 
00389   kdDebug(5600) << "  PLUGIN: " << entry->plugin()->identifier() << endl;
00390 
00391   entry->plugin()->processDropEvent( event );
00392 }
00393 
00394 void Navigator::resizeEvent( TQResizeEvent *event )
00395 {
00396   TQListBox::resizeEvent( event );
00397   triggerUpdate( true );
00398 }
00399 
00400 void Navigator::enterEvent( TQEvent *event )
00401 {
00402   // work around TQt behaviour: onItem is not emmitted in enterEvent()
00403   KListBox::enterEvent( event );
00404   emit onItem( itemAt( mapFromGlobal( TQCursor::pos() ) ) );
00405 }
00406 
00407 void Navigator::leaveEvent( TQEvent *event )
00408 {
00409   KListBox::leaveEvent( event );
00410   slotMouseOn( 0 );
00411   mMouseOn = 0;
00412 }
00413 
00414 void Navigator::slotExecuted( TQListBoxItem *item )
00415 {
00416   if ( !item )
00417     return;
00418 
00419   EntryItem *entry = static_cast<EntryItem*>( item );
00420 
00421   emit pluginActivated( entry->plugin() );
00422 }
00423 
00424 IconViewMode Navigator::sizeIntToEnum(int size) const
00425 {
00426   switch ( size ) {
00427     case int(LargeIcons):
00428       return LargeIcons;
00429       break;
00430     case int(NormalIcons):
00431       return NormalIcons;
00432       break;
00433     case int(SmallIcons):
00434       return SmallIcons;
00435       break;
00436     default:
00437       // Stick with sane values
00438       return NormalIcons;
00439       kdDebug() << "View mode not implemented!" << endl;
00440       break;
00441   }
00442 }
00443 
00444 void Navigator::slotShowRMBMenu( TQListBoxItem *, const TQPoint &pos )
00445 {
00446   KPopupMenu menu;
00447   menu.insertTitle( i18n( "Icon Size" ) );
00448   menu.insertItem( i18n( "Large" ), (int)LargeIcons );
00449   menu.setItemEnabled( (int)LargeIcons, mShowIcons );
00450   menu.insertItem( i18n( "Normal" ), (int)NormalIcons );
00451   menu.setItemEnabled( (int)NormalIcons, mShowIcons );
00452   menu.insertItem( i18n( "Small" ), (int)SmallIcons );
00453   menu.setItemEnabled( (int)SmallIcons, mShowIcons );
00454 
00455   menu.setItemChecked( (int)mViewMode, true );
00456   menu.insertSeparator();
00457 
00458   menu.insertItem( i18n( "Show Icons" ), (int)ShowIcons );
00459   menu.setItemChecked( (int)ShowIcons, mShowIcons );
00460   menu.setItemEnabled( (int)ShowIcons, mShowText );
00461   menu.insertItem( i18n( "Show Text" ), (int)ShowText );
00462   menu.setItemChecked( (int)ShowText, mShowText );
00463   menu.setItemEnabled( (int)ShowText, mShowIcons );
00464   int choice = menu.exec( pos );
00465 
00466   if ( choice == -1 )
00467     return;
00468 
00469   if ( choice >= SmallIcons ) {
00470     mViewMode = sizeIntToEnum( choice );
00471     Prefs::self()->setSidePaneIconSize( choice );
00472   } else {
00473     // either icons or text were toggled
00474     if ( choice == ShowIcons ) {
00475       mShowIcons = !mShowIcons;
00476       Prefs::self()->setSidePaneShowIcons( mShowIcons );
00477       TQToolTip::remove( this );
00478       if ( !mShowText )
00479         new EntryItemToolTip( this );
00480     } else {
00481       mShowText = !mShowText;
00482       Prefs::self()->setSidePaneShowText( mShowText );
00483       TQToolTip::remove( this );
00484     }
00485   }
00486   int maxWidth = 0;
00487   TQListBoxItem* it = 0;
00488   for (int i = 0; (it = item(i)) != 0; ++i)
00489   {
00490     int width = it->width(this);
00491     if (width > maxWidth)
00492       maxWidth = width;
00493   }
00494   parentWidget()->setFixedWidth( maxWidth );
00495 
00496   triggerUpdate( true );
00497 }
00498 
00499 void Navigator::shortCutSelected( int pos )
00500 {
00501   setCurrentItem( pos );
00502 }
00503 
00504 void Navigator::setHoverItem( TQListBoxItem* item, bool hover )
00505 {
00506     static_cast<EntryItem*>( item )->setHover( hover );
00507     updateItem( item );
00508 }
00509 
00510 void Navigator::setPaintActiveItem( TQListBoxItem* item, bool paintActive )
00511 {
00512     static_cast<EntryItem*>( item )->setPaintActive( paintActive );
00513     updateItem( item );
00514 }
00515 
00516 void Navigator::slotMouseOn( TQListBoxItem* newItem )
00517 {
00518   TQListBoxItem* oldItem = mMouseOn;
00519   if ( oldItem == newItem ) return;
00520 
00521   if ( oldItem && !oldItem->isCurrent() && !oldItem->isSelected() )
00522   {
00523     setHoverItem( oldItem, false );
00524   }
00525 
00526   if ( newItem && !newItem->isCurrent() && !newItem->isSelected() )
00527   {
00528     setHoverItem( newItem, true );
00529   }
00530   mMouseOn = newItem;
00531 }
00532 
00533 void Navigator::slotMouseOff()
00534 {
00535   slotMouseOn( 0 );
00536 }
00537 
00538 IconSidePane::IconSidePane( Core *core, TQWidget *parent, const char *name )
00539   : SidePaneBase( core, parent, name )
00540 {
00541   mNavigator = new Navigator( this );
00542   connect( mNavigator, TQT_SIGNAL( pluginActivated( Kontact::Plugin* ) ),
00543            TQT_SIGNAL( pluginSelected( Kontact::Plugin* ) ) );
00544 
00545   setAcceptDrops( true );
00546 }
00547 
00548 IconSidePane::~IconSidePane()
00549 {
00550 }
00551 
00552 void IconSidePane::updatePlugins()
00553 {
00554   mNavigator->updatePlugins( core()->pluginList() );
00555 }
00556 
00557 void IconSidePane::selectPlugin( Kontact::Plugin *plugin )
00558 {
00559   bool blocked = signalsBlocked();
00560   blockSignals( true );
00561 
00562   for ( uint i = 0; i < mNavigator->count(); ++i ) {
00563     EntryItem *item = static_cast<EntryItem*>( mNavigator->item( i ) );
00564     if ( item->plugin() == plugin ) {
00565       mNavigator->setCurrentItem( i );
00566       break;
00567     }
00568   }
00569 
00570   blockSignals( blocked );
00571 }
00572 
00573 void IconSidePane::selectPlugin( const TQString &name )
00574 {
00575   bool blocked = signalsBlocked();
00576   blockSignals( true );
00577 
00578   for ( uint i = 0; i < mNavigator->count(); ++i ) {
00579     EntryItem *item = static_cast<EntryItem*>( mNavigator->item( i ) );
00580     if ( item->plugin()->identifier() == name ) {
00581       mNavigator->setCurrentItem( i );
00582       break;
00583     }
00584   }
00585 
00586   blockSignals( blocked );
00587 }
00588 
00589 void IconSidePane::indicateForegrunding( Kontact::Plugin *plugin )
00590 {
00591   for ( uint i = 0; i < mNavigator->count(); ++i ) {
00592     EntryItem *item = static_cast<EntryItem*>( mNavigator->item( i ) );
00593     if ( item->plugin() == plugin ) {
00594       mNavigator->highlightItem( item );
00595       break;
00596     }
00597   }
00598 
00599 
00600 }
00601 #include "iconsidepane.moc"
00602 
00603 // vim: sw=2 sts=2 et tw=80