lib

KoTabBar.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003 Ariya Hidayat <ariya@kde.org>
00003    Copyright (C) 2003 Norbert Andres <nandres@web.de>
00004    Copyright (C) 2002 Laurent Montel <montel@kde.org>
00005    Copyright (C) 1999 David Faure <faure@kde.org>
00006    Copyright (C) 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
00007    Copyright (C) 1998-2000 Torben Weis <weis@kde.org>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License as published by the Free Software Foundation; either
00012    version 2 of the License, or (at your option) any later version.
00013 
00014    This library is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017    Library General Public License for more details.
00018 
00019    You should have received a copy of the GNU Library General Public License
00020    along with this library; see the file COPYING.LIB.  If not, write to
00021    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022  * Boston, MA 02110-1301, USA.
00023 */
00024 
00025 #include "KoTabBar.h"
00026 
00027 #include <qdrawutil.h>
00028 #include <qpainter.h>
00029 #include <qstring.h>
00030 #include <qstringlist.h>
00031 #include <qstyle.h>
00032 #include <qtimer.h>
00033 #include <qtoolbutton.h>
00034 #include <qvaluevector.h>
00035 #include <qwidget.h>
00036 
00037 // TODO
00038 // improvement possibilities
00039 // - use offscreen buffer to reduce flicker even more
00040 // - keep track of tabs, only (re)layout when necessary
00041 // - paint all tabs to buffer, show only by shifting
00042 // - customizable button pixmaps
00043 // - use QStyle to paint the tabs & buttons (is it good/possible?)
00044 
00045 
00046 class KoTabBarPrivate
00047 {
00048 public:
00049     KoTabBar* tabbar;
00050 
00051     // scroll buttons
00052     QToolButton* scrollFirstButton;
00053     QToolButton* scrollLastButton;
00054     QToolButton* scrollBackButton;
00055     QToolButton* scrollForwardButton;
00056 
00057     // read-only: no mouse drag, double-click, right-click
00058     bool readOnly;
00059     
00060     // if true, layout is from right to left
00061     bool reverseLayout;
00062 
00063     // list of all tabs, in order of appearance
00064     QStringList tabs;
00065 
00066     // array of QRect for each visible tabs
00067     QValueVector<QRect> tabRects;
00068 
00069     // leftmost tab (or rightmost if reverseLayout)
00070     int firstTab;
00071     
00072     // rightmost tab (or leftmost if reverseLayout)
00073     int lastTab;
00074 
00075     // the active tab in the range form 1..n.
00076     // if this value is 0, that means that no tab is active.
00077     int activeTab;
00078 
00079     // unusable space on the left, taken by the scroll buttons
00080     int offset;
00081 
00082     // when the user drag the tab (in order to move it)
00083     // this is the target position, it's 0 if no tab is dragged
00084     int targetTab;
00085 
00086     // wheel movement since selected tab was last changed by the
00087     // mouse wheel
00088     int wheelDelta;
00089 
00090     // true if autoscroll is active
00091     bool autoScroll;
00092 
00093     // calculate the bounding rectangle for each visible tab
00094     void layoutTabs();
00095 
00096     // reposition scroll buttons
00097     void layoutButtons();
00098 
00099     // find a tab whose bounding rectangle contains the pos
00100     // return -1 if no such tab is found
00101     int tabAt( const QPoint& pos );
00102 
00103     // draw a single tab
00104     void drawTab( QPainter& painter, QRect& rect, const QString& text, bool active );
00105 
00106     // draw a marker to indicate tab moving
00107     void drawMoveMarker( QPainter& painter, int x, int y );
00108 
00109     // update the enable/disable status of scroll buttons
00110     void updateButtons();
00111 
00112 };
00113 
00114 // built-in pixmap for scroll-first button
00115 static const char * arrow_leftmost_xpm[] = {
00116 "10 10 2 1",
00117 "   c None",
00118 ".  c #000000",
00119 "          ",
00120 "  .    .  ",
00121 "  .   ..  ",
00122 "  .  ...  ",
00123 "  . ....  ",
00124 "  .  ...  ",
00125 "  .   ..  ",
00126 "  .    .  ",
00127 "          ",
00128 "          "};
00129 
00130 // built-in pixmap for scroll-last button
00131 static const char * arrow_rightmost_xpm[] = {
00132 "10 10 2 1",
00133 "   c None",
00134 ".  c #000000",
00135 "          ",
00136 "  .    .  ",
00137 "  ..   .  ",
00138 "  ...  .  ",
00139 "  .... .  ",
00140 "  ...  .  ",
00141 "  ..   .  ",
00142 "  .    .  ",
00143 "          ",
00144 "          "};
00145 
00146 // built-in pixmap for scroll-left button
00147 static const char * arrow_left_xpm[] = {
00148 "10 10 2 1",
00149 "   c None",
00150 ".  c #000000",
00151 "          ",
00152 "      .   ",
00153 "     ..   ",
00154 "    ...   ",
00155 "   ....   ",
00156 "    ...   ",
00157 "     ..   ",
00158 "      .   ",
00159 "          ",
00160 "          "};
00161 
00162 // built-in pixmap for scroll-right button
00163 static const char * arrow_right_xpm[] = {
00164 "10 10 2 1",
00165 "   c None",
00166 ".  c #000000",
00167 "          ",
00168 "   .      ",
00169 "   ..     ",
00170 "   ...    ",
00171 "   ....   ",
00172 "   ...    ",
00173 "   ..     ",
00174 "   .      ",
00175 "          ",
00176 "          "};
00177 
00178 
00179 void KoTabBarPrivate::layoutTabs()
00180 {
00181     tabRects.clear();
00182 
00183     QPainter painter( tabbar );
00184 
00185     QFont f = painter.font();
00186     f.setBold( true );
00187     painter.setFont( f );
00188     QFontMetrics fm = painter.fontMetrics();
00189     
00190     if( !reverseLayout )
00191     {
00192         // left to right
00193         int x = 0;
00194         for( unsigned c = 0; c < tabs.count(); c++ )
00195         {
00196             QRect rect;
00197             if( (int)c >= firstTab-1 )
00198             {
00199                 QString text = tabs[ c ];
00200                 int tw = fm.width( text ) + 4;
00201                 rect = QRect( x, 0, tw + 20, tabbar->height() );
00202                 x = x + tw + 20;
00203             }
00204             tabRects.append( rect );
00205         }
00206 
00207         lastTab = tabRects.count();
00208         for( unsigned i = 0; i < tabRects.count(); i++ )
00209             if( tabRects[i].right()-10+offset > tabbar->width() )
00210             {
00211                 lastTab = i;
00212                 break;
00213             }
00214     }
00215     else
00216     {
00217         // right to left
00218         int x = tabbar->width() - offset;
00219         for( unsigned c = 0; c < tabs.count(); c++ )
00220         {
00221             QRect rect;
00222             if( (int)c >= firstTab-1 )
00223             {
00224                 QString text = tabs[ c ];
00225                 int tw = fm.width( text ) + 4;
00226                 rect = QRect( x - tw - 20, 0, tw + 20, tabbar->height() );
00227                 x = x - tw - 20;
00228             }
00229             tabRects.append( rect );
00230         }
00231 
00232         lastTab = tabRects.count();
00233         for( unsigned i = tabRects.count()-1; i>0; i-- )
00234             if( tabRects[i].left() > 0 )
00235             {
00236                 lastTab = i+1;
00237                 break;
00238             }
00239     }    
00240 }
00241 
00242 int KoTabBarPrivate::tabAt( const QPoint& pos )
00243 {
00244     for( unsigned i = 0; i < tabRects.count(); i++ )
00245     {
00246       QRect rect = tabRects[ i ];
00247       if( rect.isNull() ) continue;
00248       if( rect.contains( pos ) ) return i;
00249     }
00250 
00251     return -1; // not found
00252 }
00253 
00254 void KoTabBarPrivate::drawTab( QPainter& painter, QRect& rect, const QString& text, bool active )
00255 {
00256     QPointArray polygon;
00257     
00258     if( !reverseLayout )
00259         polygon.setPoints( 6, rect.x(), rect.y(),
00260             rect.x(), rect.bottom()-3,
00261             rect.x()+2, rect.bottom(),
00262             rect.right()-4, rect.bottom(),
00263             rect.right()-2, rect.bottom()-2,
00264             rect.right()+5, rect.top() );
00265     else      
00266         polygon.setPoints( 6, rect.right(), rect.top(),
00267             rect.right(), rect.bottom()-3,
00268             rect.right()-2, rect.bottom(),
00269             rect.x()+4, rect.bottom(),
00270             rect.x()+2, rect.bottom()-2,
00271             rect.x()-5, rect.top() );
00272 
00273     painter.save();
00274 
00275     // fill it first  
00276     QBrush bg = tabbar->colorGroup().background();
00277     if( active ) bg = tabbar->colorGroup().base();
00278     painter.setBrush( bg );
00279     painter.setPen( QPen( Qt::NoPen ) );
00280     painter.drawPolygon( polygon );
00281 
00282     // draw the lines
00283     painter.setPen( tabbar->colorGroup().dark() );
00284     if( !active )
00285       painter.drawLine( rect.x()-25, rect.y(), rect.right()+25, rect.top() );
00286     // Qt4: painter.setRenderHint( QPainter::Antialiasing );
00287     painter.drawPolyline( polygon );
00288 
00289     painter.setPen( tabbar->colorGroup().buttonText() );
00290     QFont f = painter.font();
00291     if( active ) f.setBold( true );
00292     painter.setFont( f );
00293     QFontMetrics fm = painter.fontMetrics();
00294     int tx =  rect.x() + ( rect.width() - fm.width( text ) ) / 2;
00295     int ty =  rect.y() + ( rect.height() - fm.height() ) / 2 + fm.ascent();
00296     painter.drawText( tx, ty, text );
00297 
00298     painter.restore();
00299 }
00300 
00301 void KoTabBarPrivate::drawMoveMarker( QPainter& painter, int x, int y )
00302 {
00303     QPointArray movmark;
00304     movmark.setPoints( 3, x, y, x + 7, y, x + 4, y + 6);
00305     QBrush oldBrush = painter.brush();
00306     painter.setBrush( Qt::black );
00307     painter.drawPolygon(movmark);
00308     painter.setBrush( oldBrush );
00309 }
00310 
00311 void KoTabBarPrivate::layoutButtons()
00312 {
00313     int bw = tabbar->height();
00314     int w = tabbar->width();
00315     offset = bw * 4;
00316     
00317     if( !reverseLayout )
00318     {
00319         scrollFirstButton->setGeometry( 0, 0, bw, bw );
00320         scrollFirstButton->setPixmap( arrow_leftmost_xpm );
00321         scrollBackButton->setGeometry( bw, 0, bw, bw );
00322         scrollBackButton->setPixmap( arrow_left_xpm );
00323         scrollForwardButton->setGeometry( bw*2, 0, bw, bw );
00324         scrollForwardButton->setPixmap( arrow_right_xpm );
00325         scrollLastButton->setGeometry( bw*3, 0, bw, bw );
00326         scrollLastButton->setPixmap( arrow_rightmost_xpm );
00327     }
00328     else
00329     {
00330         scrollFirstButton->setGeometry( w-bw, 0, bw, bw );
00331         scrollFirstButton->setPixmap( arrow_rightmost_xpm );
00332         scrollBackButton->setGeometry( w-2*bw, 0, bw, bw );
00333         scrollBackButton->setPixmap( arrow_right_xpm );
00334         scrollForwardButton->setGeometry( w-3*bw, 0, bw, bw );
00335         scrollForwardButton->setPixmap( arrow_left_xpm );
00336         scrollLastButton->setGeometry( w-4*bw, 0, bw, bw );
00337         scrollLastButton->setPixmap( arrow_leftmost_xpm );
00338     }
00339  }
00340 
00341 void KoTabBarPrivate::updateButtons()
00342 {
00343     scrollFirstButton->setEnabled( tabbar->canScrollBack() );
00344     scrollBackButton->setEnabled( tabbar->canScrollBack() );
00345     scrollForwardButton->setEnabled( tabbar->canScrollForward() );
00346     scrollLastButton->setEnabled( tabbar->canScrollForward() );
00347 }
00348 
00349 // creates a new tabbar
00350 KoTabBar::KoTabBar( QWidget* parent, const char* name )
00351     : QWidget( parent, name, Qt::WResizeNoErase | Qt::WRepaintNoErase )
00352 {
00353     d = new KoTabBarPrivate;
00354     d->tabbar = this;
00355     d->readOnly = false;
00356     d->reverseLayout = false;
00357     d->firstTab = 1;
00358     d->lastTab = 0;
00359     d->activeTab = 0;
00360     d->targetTab = 0;
00361     d->wheelDelta = 0;
00362     d->autoScroll = false;
00363     d->offset = 64;
00364 
00365     // initialize the scroll buttons
00366     d->scrollFirstButton = new QToolButton( this );
00367     connect( d->scrollFirstButton, SIGNAL( clicked() ),
00368       this, SLOT( scrollFirst() ) );
00369     d->scrollLastButton = new QToolButton( this );
00370     connect( d->scrollLastButton, SIGNAL( clicked() ),
00371       this, SLOT( scrollLast() ) );
00372     d->scrollBackButton = new QToolButton( this );
00373     connect( d->scrollBackButton, SIGNAL( clicked() ),
00374       this, SLOT( scrollBack() ) );
00375     d->scrollForwardButton = new QToolButton( this );
00376     connect( d->scrollForwardButton, SIGNAL( clicked() ),
00377       this, SLOT( scrollForward() ) );
00378     d->layoutButtons();
00379     d->updateButtons();
00380 }
00381 
00382 // destroys the tabbar
00383 KoTabBar::~KoTabBar()
00384 {
00385     delete d;
00386 }
00387 
00388 // adds a new visible tab
00389 void KoTabBar::addTab( const QString& text )
00390 {
00391     d->tabs.append( text );
00392 
00393     update();
00394 }
00395 
00396 // removes a tab
00397 void KoTabBar::removeTab( const QString& text )
00398 {
00399     int i = d->tabs.findIndex( text );
00400     if ( i == -1 ) return;
00401 
00402     if ( d->activeTab == i + 1 )
00403         d->activeTab = 0;
00404 
00405     d->tabs.remove( text );
00406 
00407     update();
00408 }
00409 
00410 // removes all tabs
00411 void KoTabBar::clear()
00412 {
00413     d->tabs.clear();
00414     d->activeTab = 0;
00415     d->firstTab = 1;
00416 
00417     update();
00418 }
00419 
00420 bool KoTabBar::readOnly() const
00421 {
00422     return d->readOnly;
00423 }
00424 
00425 void KoTabBar::setReadOnly( bool ro )
00426 {
00427     d->readOnly = ro;
00428 }
00429 
00430 bool KoTabBar::reverseLayout() const
00431 {
00432     return d->reverseLayout;
00433 }
00434 
00435 void KoTabBar::setReverseLayout( bool reverse )
00436 {
00437     if( reverse != d->reverseLayout )
00438     {
00439         d->reverseLayout = reverse;
00440         d->layoutTabs();
00441         d->layoutButtons();
00442         d->updateButtons();
00443         update();
00444     }
00445 }
00446 
00447 void KoTabBar::setTabs( const QStringList& list )
00448 {
00449     QString left, active;
00450 
00451     if( d->activeTab > 0 )
00452         active = d->tabs[ d->activeTab-1 ];
00453     if( d->firstTab > 0 )
00454         left = d->tabs[ d->firstTab-1 ];
00455 
00456     d->tabs = list;
00457 
00458     if( !left.isNull() )
00459     {
00460         d->firstTab = d->tabs.findIndex( left ) + 1;
00461         if( d->firstTab > (int)d->tabs.count() )
00462             d->firstTab = 1;
00463         if( d->firstTab <= 0 )
00464             d->firstTab = 1;
00465     }
00466 
00467     d->activeTab = 0;
00468     if( !active.isNull() )
00469         setActiveTab( active );
00470 
00471     update();
00472 }
00473 
00474 QStringList KoTabBar::tabs() const
00475 {
00476     return d->tabs;
00477 }
00478 
00479 unsigned KoTabBar::count() const
00480 {
00481     return d->tabs.count();
00482 }
00483 
00484 bool KoTabBar::canScrollBack() const
00485 {
00486     if ( d->tabs.count() == 0 )
00487         return false;
00488 
00489     return d->firstTab > 1;
00490 }
00491 
00492 bool KoTabBar::canScrollForward() const
00493 {
00494     if ( d->tabs.count() == 0 )
00495         return false;
00496         
00497     return d->lastTab < (int)d->tabs.count();
00498 }
00499 
00500 void KoTabBar::scrollBack()
00501 {
00502     if ( !canScrollBack() )
00503         return;
00504 
00505     d->firstTab--;
00506     if( d->firstTab < 1 ) d->firstTab = 1;
00507 
00508     d->layoutTabs();
00509     d->updateButtons();
00510     update();
00511 }
00512 
00513 void KoTabBar::scrollForward()
00514 {
00515     if ( !canScrollForward() )
00516         return;
00517 
00518     d->firstTab ++;
00519     if( d->firstTab > (int)d->tabs.count() )
00520         d->firstTab = d->tabs.count();
00521 
00522     d->layoutTabs();
00523     d->updateButtons();
00524     update();
00525 }
00526 
00527 void KoTabBar::scrollFirst()
00528 {
00529     if ( !canScrollBack() )
00530         return;
00531 
00532     d->firstTab = 1;
00533     d->layoutTabs();
00534     d->updateButtons();
00535     update();
00536 }
00537 
00538 void KoTabBar::scrollLast()
00539 {
00540     if ( !canScrollForward() )
00541         return;
00542 
00543     d->layoutTabs();
00544 
00545     if( !d->reverseLayout )
00546     {
00547         int fullWidth = d->tabRects[ d->tabRects.count()-1 ].right();
00548         int delta = fullWidth - width() + d->offset;
00549         for( unsigned i = 0; i < d->tabRects.count(); i++ )
00550             if( d->tabRects[i].x() > delta )
00551             {
00552                 d->firstTab = i+1;
00553                 break;
00554             }
00555     }
00556     else
00557     {
00558         // FIXME optimize this, perhaps without loop
00559         for( ; d->firstTab <= (int)d->tabRects.count();)
00560         {
00561             int x = d->tabRects[ d->tabRects.count()-1 ].x();
00562             if( x > 0 ) break;
00563             d->firstTab++;
00564             d->layoutTabs();
00565         }
00566     }
00567 
00568     d->layoutTabs();
00569     d->updateButtons();
00570     update();
00571 }
00572 
00573 void KoTabBar::ensureVisible( const QString& tab )
00574 {
00575     int i = d->tabs.findIndex( tab );
00576     if ( i == -1 )
00577         return;
00578     i++;
00579 
00580     // already visible, then do nothing
00581     if( ( i >= d->firstTab ) && ( i <= d->lastTab ) )
00582       return;
00583 
00584     if( i < d->firstTab )
00585         while( i < d->firstTab )
00586             scrollBack();
00587 
00588     if( i > d->lastTab )
00589         while( i > d->lastTab )
00590             scrollForward();
00591 }
00592 
00593 void KoTabBar::moveTab( unsigned tab, unsigned target )
00594 {
00595     QString tabName = d->tabs[ tab ];
00596     QStringList::Iterator it;
00597 
00598     it = d->tabs.at( tab );
00599     d->tabs.remove( it );
00600 
00601     if( target > tab ) target--;
00602     it = d->tabs.at( target );
00603     if( target >= d->tabs.count() )
00604       it = d->tabs.end();
00605     d->tabs.insert( it, tabName );
00606 
00607     if( d->activeTab == (int)tab+1 )
00608         d->activeTab = target+1;
00609 
00610     update();
00611 }
00612 
00613 void KoTabBar::setActiveTab( const QString& text )
00614 {
00615     int i = d->tabs.findIndex( text );
00616     if ( i == -1 )
00617         return;
00618 
00619     if ( i + 1 == d->activeTab )
00620         return;
00621 
00622     d->activeTab = i + 1;
00623     d->updateButtons();
00624     update();
00625 
00626     emit tabChanged( text );
00627 }
00628 
00629 void KoTabBar::autoScrollBack()
00630 {
00631     if( !d->autoScroll ) return;
00632 
00633     scrollBack();
00634 
00635     if( !canScrollBack() )
00636         d->autoScroll = false;
00637     else
00638         QTimer::singleShot( 400, this, SLOT( autoScrollBack() ) );
00639 }
00640 
00641 void KoTabBar::autoScrollForward()
00642 {
00643     if( !d->autoScroll ) return;
00644 
00645     scrollForward();
00646 
00647     if( !canScrollForward() )
00648         d->autoScroll = false;
00649     else
00650         QTimer::singleShot( 400, this, SLOT( autoScrollForward() ) );
00651 }
00652 
00653 void KoTabBar::paintEvent( QPaintEvent* )
00654 {
00655     if ( d->tabs.count() == 0 )
00656     {
00657         erase();
00658         return;
00659     }
00660 
00661     QPainter painter;
00662     QPixmap pm( size() );
00663     pm.fill( colorGroup().background() );
00664     painter.begin( &pm, this );
00665 
00666     painter.setPen( colorGroup().dark() );
00667     painter.drawLine( 0, 0, width(), 0 );
00668 
00669     if( !d->reverseLayout )
00670         painter.translate( 5, 0 );
00671 
00672     d->layoutTabs();
00673     d->updateButtons();
00674     
00675     // draw first all non-active, visible tabs
00676     for( int c = d->tabRects.count()-1; c>=0; c-- )
00677     {
00678         QRect rect = d->tabRects[ c ];
00679         if( rect.isNull() ) continue;
00680         QString text = d->tabs[ c ];
00681         d->drawTab( painter, rect, text, false );
00682     }
00683 
00684     // draw the active tab
00685     if( d->activeTab > 0 )
00686     {
00687         QRect rect = d->tabRects[ d->activeTab-1 ];
00688         if( !rect.isNull() )
00689         {
00690             QString text = d->tabs[ d->activeTab-1 ];
00691             d->drawTab( painter, rect, text, true );
00692         }
00693     }
00694 
00695     // draw the move marker
00696     if( d->targetTab > 0 )
00697     {
00698         int p = QMIN( d->targetTab, (int)d->tabRects.count() );
00699         QRect rect = d->tabRects[ p-1 ];
00700         if( !rect.isNull() )
00701         {
00702             int x = !d->reverseLayout ? rect.x() : rect.right()-7;
00703             if( d->targetTab > (int)d->tabRects.count() )
00704               x = !d->reverseLayout ? rect.right()-7 : rect.x()-3;
00705             d->drawMoveMarker( painter, x, rect.y() );
00706         }
00707     }
00708 
00709     painter.end();
00710     
00711     if( !d->reverseLayout )
00712          bitBlt( this, d->offset, 0, &pm );
00713     else
00714          bitBlt( this, 0, 0, &pm );
00715 
00716 }
00717 
00718 void KoTabBar::resizeEvent( QResizeEvent* )
00719 {
00720     d->layoutButtons();
00721     d->updateButtons();
00722     update();
00723 }
00724 
00725 QSize KoTabBar::sizeHint() const
00726 {
00727     return QSize( 40, style().pixelMetric( QStyle::PM_ScrollBarExtent, this ) );
00728 }
00729 
00730 void KoTabBar::renameTab( const QString& old_name, const QString& new_name )
00731 {
00732     QStringList::Iterator it = d->tabs.find( old_name );
00733     (*it) = new_name;
00734 
00735     update();
00736 }
00737 
00738 QString KoTabBar::activeTab() const
00739 {
00740     if( d->activeTab == 0 )
00741         return QString::null;
00742     else
00743         return d->tabs[ d->activeTab ];
00744 }
00745 
00746 void KoTabBar::mousePressEvent( QMouseEvent* ev )
00747 {
00748     if ( d->tabs.count() == 0 )
00749     {
00750         erase();
00751         return;
00752     }
00753 
00754     d->layoutTabs();
00755 
00756     QPoint pos = ev->pos();
00757     if( !d->reverseLayout ) pos = pos - QPoint( d->offset,0 );
00758 
00759     int tab = d->tabAt( pos ) + 1;
00760     if( ( tab > 0 ) && ( tab != d->activeTab ) )
00761     {
00762         d->activeTab = tab;
00763         update();
00764 
00765         emit tabChanged( d->tabs[ d->activeTab-1] );
00766 
00767         // scroll if partially visible
00768         if( d->tabRects[ tab-1 ].right() > width() - d->offset )
00769             scrollForward();
00770     }
00771 
00772     if( ev->button() == RightButton )
00773     if( !d->readOnly )
00774         emit contextMenu( ev->globalPos() );
00775 }
00776 
00777 void KoTabBar::mouseReleaseEvent( QMouseEvent* ev )
00778 {
00779     if ( d->readOnly ) return;
00780 
00781     d->autoScroll = false;
00782 
00783     if ( ev->button() == LeftButton && d->targetTab != 0 )
00784     {
00785         emit tabMoved( d->activeTab-1, d->targetTab-1 );
00786         d->targetTab = 0;
00787     }
00788 }
00789 
00790 void KoTabBar::mouseMoveEvent( QMouseEvent* ev )
00791 {
00792     if ( d->readOnly ) return;
00793 
00794     QPoint pos = ev->pos();
00795     if( !d->reverseLayout) pos = pos - QPoint( d->offset,0 );
00796     
00797     // check if user drags a tab to move it
00798     int i = d->tabAt( pos ) + 1;
00799     if( ( i > 0 ) && ( i != d->targetTab ) )
00800     {
00801         if( i == d->activeTab ) i = 0;
00802         if( i == d->activeTab+1 ) i = 0;
00803 
00804         if( i != d->targetTab )
00805         {
00806            d->targetTab = i;
00807            d->autoScroll = false;
00808            update();
00809         }
00810     }
00811 
00812     // drag past the very latest visible tab
00813     // e.g move a tab to the last ordering position
00814     QRect r = d->tabRects[ d->tabRects.count()-1 ];
00815     bool moveToLast = false;
00816     if( r.isValid() )
00817     {
00818         if( !d->reverseLayout )
00819         if( pos.x() > r.right() )
00820         if( pos.x() < width() )
00821             moveToLast = true;
00822         if( d->reverseLayout )
00823         if( pos.x() < r.x() )
00824         if( pos.x() > 0 )
00825             moveToLast = true;
00826     }
00827     if( moveToLast )
00828     if( d->targetTab != (int)d->tabRects.count()+1 )
00829     {
00830         d->targetTab = d->tabRects.count()+1;
00831         d->autoScroll = false;
00832         update();
00833     }
00834 
00835     // outside far too left ? activate autoscroll...
00836     if ( pos.x() < 0 && !d->autoScroll  )
00837     {
00838         d->autoScroll = true;
00839         autoScrollBack();
00840     }
00841 
00842     // outside far too right ? activate autoscroll...
00843     int w = width() - d->offset;
00844     if ( pos.x() > w && !d->autoScroll )
00845     {
00846         d->autoScroll = true;
00847         autoScrollForward();
00848     }
00849 }
00850 
00851 void KoTabBar::mouseDoubleClickEvent( QMouseEvent* ev )
00852 {
00853     int offset = d->reverseLayout ? 0 : d->offset;
00854     if( ev->pos().x() > offset )
00855     if( !d->readOnly )
00856         emit doubleClicked();
00857 }
00858 
00859 void KoTabBar::wheelEvent( QWheelEvent * e )
00860 {
00861   if ( d->tabs.count() == 0 )
00862   {
00863     erase();
00864     return;
00865   }
00866 
00867   // Currently one wheel movement is a delta of 120.
00868   // The 'unused' delta is stored for devices that allow
00869   // a higher scrolling resolution.
00870   // The delta required to move one tab is one wheel movement:
00871   const int deltaRequired = 120;
00872 
00873   d->wheelDelta += e->delta();
00874   int tabDelta = - (d->wheelDelta / deltaRequired);
00875   d->wheelDelta = d->wheelDelta % deltaRequired;
00876   int numTabs = d->tabs.size();
00877 
00878   if(d->activeTab + tabDelta > numTabs)
00879   {
00880     // Would take us past the last tab
00881     d->activeTab = numTabs;
00882   }
00883   else if (d->activeTab + tabDelta < 1)
00884   {
00885     // Would take us before the first tab
00886     d->activeTab = 1;
00887   }
00888   else
00889   {
00890     d->activeTab = d->activeTab + tabDelta;
00891   }
00892 
00893   // Find the left and right edge of the new tab.  If we're
00894   // going forward, and the right of the new tab isn't visible
00895   // then scroll forward.  Likewise, if going back, and the 
00896   // left of the new tab isn't visible, then scroll back.
00897   int activeTabRight = d->tabRects[ d->activeTab-1 ].right();
00898   int activeTabLeft  = d->tabRects[ d->activeTab-1 ].left();
00899   if(tabDelta > 0 && activeTabRight > width() - d->offset )
00900   {
00901     scrollForward();
00902   }
00903   else if(tabDelta < 0 && activeTabLeft < width() - d->offset )
00904   {
00905     scrollBack();
00906   }
00907 
00908   update();
00909   emit tabChanged( d->tabs[ d->activeTab-1] );
00910 }
00911 
00912 
00913 #include "KoTabBar.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys