00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
00038
00039
00040
00041
00042
00043
00044
00045
00046 class KoTabBarPrivate
00047 {
00048 public:
00049 KoTabBar* tabbar;
00050
00051
00052 QToolButton* scrollFirstButton;
00053 QToolButton* scrollLastButton;
00054 QToolButton* scrollBackButton;
00055 QToolButton* scrollForwardButton;
00056
00057
00058 bool readOnly;
00059
00060
00061 bool reverseLayout;
00062
00063
00064 QStringList tabs;
00065
00066
00067 QValueVector<QRect> tabRects;
00068
00069
00070 int firstTab;
00071
00072
00073 int lastTab;
00074
00075
00076
00077 int activeTab;
00078
00079
00080 int offset;
00081
00082
00083
00084 int targetTab;
00085
00086
00087 bool autoScroll;
00088
00089
00090 void layoutTabs();
00091
00092
00093 void layoutButtons();
00094
00095
00096
00097 int tabAt( const QPoint& pos );
00098
00099
00100 void drawTab( QPainter& painter, QRect& rect, const QString& text, bool active );
00101
00102
00103 void drawMoveMarker( QPainter& painter, int x, int y );
00104
00105
00106 void updateButtons();
00107 };
00108
00109
00110 static const char * arrow_leftmost_xpm[] = {
00111 "10 10 2 1",
00112 " c None",
00113 ". c #000000",
00114 " ",
00115 " . . ",
00116 " . .. ",
00117 " . ... ",
00118 " . .... ",
00119 " . ... ",
00120 " . .. ",
00121 " . . ",
00122 " ",
00123 " "};
00124
00125
00126 static const char * arrow_rightmost_xpm[] = {
00127 "10 10 2 1",
00128 " c None",
00129 ". c #000000",
00130 " ",
00131 " . . ",
00132 " .. . ",
00133 " ... . ",
00134 " .... . ",
00135 " ... . ",
00136 " .. . ",
00137 " . . ",
00138 " ",
00139 " "};
00140
00141
00142 static const char * arrow_left_xpm[] = {
00143 "10 10 2 1",
00144 " c None",
00145 ". c #000000",
00146 " ",
00147 " . ",
00148 " .. ",
00149 " ... ",
00150 " .... ",
00151 " ... ",
00152 " .. ",
00153 " . ",
00154 " ",
00155 " "};
00156
00157
00158 static const char * arrow_right_xpm[] = {
00159 "10 10 2 1",
00160 " c None",
00161 ". c #000000",
00162 " ",
00163 " . ",
00164 " .. ",
00165 " ... ",
00166 " .... ",
00167 " ... ",
00168 " .. ",
00169 " . ",
00170 " ",
00171 " "};
00172
00173
00174 void KoTabBarPrivate::layoutTabs()
00175 {
00176 tabRects.clear();
00177
00178 QPainter painter( tabbar );
00179
00180 QFont f = painter.font();
00181 f.setBold( true );
00182 painter.setFont( f );
00183 QFontMetrics fm = painter.fontMetrics();
00184
00185 if( !reverseLayout )
00186 {
00187
00188 int x = 0;
00189 for( unsigned c = 0; c < tabs.count(); c++ )
00190 {
00191 QRect rect;
00192 if( (int)c >= firstTab-1 )
00193 {
00194 QString text = tabs[ c ];
00195 int tw = fm.width( text ) + 4;
00196 rect = QRect( x, 0, tw + 20, tabbar->height() );
00197 x = x + tw + 10;
00198 }
00199 tabRects.append( rect );
00200 }
00201
00202 lastTab = tabRects.count();
00203 for( unsigned i = 0; i < tabRects.count(); i++ )
00204 if( tabRects[i].right()-10+offset > tabbar->width() )
00205 {
00206 lastTab = i;
00207 break;
00208 }
00209 }
00210 else
00211 {
00212
00213 int x = tabbar->width() - offset;
00214 for( unsigned c = 0; c < tabs.count(); c++ )
00215 {
00216 QRect rect;
00217 if( (int)c >= firstTab-1 )
00218 {
00219 QString text = tabs[ c ];
00220 int tw = fm.width( text ) + 4;
00221 rect = QRect( x - tw - 20, 0, tw + 20, tabbar->height() );
00222 x = x - tw - 10;
00223 }
00224 tabRects.append( rect );
00225 }
00226
00227 lastTab = tabRects.count();
00228 for( unsigned i = 0; i < tabRects.count(); i++ )
00229 if( tabRects[i].right() < 0 )
00230 {
00231 lastTab = i;
00232 break;
00233 }
00234 }
00235 }
00236
00237 int KoTabBarPrivate::tabAt( const QPoint& pos )
00238 {
00239 for( unsigned i = 0; i < tabRects.count(); i++ )
00240 {
00241 QRect rect = tabRects[ i ];
00242 if( rect.isNull() ) continue;
00243 if( rect.contains( pos ) ) return i;
00244 }
00245
00246 return -1;
00247 }
00248
00249 void KoTabBarPrivate::drawTab( QPainter& painter, QRect& rect, const QString& text, bool active )
00250 {
00251 QPointArray pa;
00252 pa.setPoints( 4, rect.x(), rect.y(), rect.x()+10, rect.bottom()-1,
00253 rect.right()-10, rect.bottom()-1, rect.right(), rect.top() );
00254
00255 QColor bgcolor = tabbar->colorGroup().background();
00256 if( active ) bgcolor = tabbar->colorGroup().base();
00257
00258 painter.setClipping( true );
00259 painter.setClipRegion( QRegion( pa ) );
00260 painter.setBackgroundColor( bgcolor );
00261 painter.eraseRect( rect );
00262 painter.setClipping( false );
00263
00264 painter.drawLine( rect.x(), rect.y(), rect.x()+10, rect.bottom()-1 );
00265 painter.drawLine( rect.x()+10, rect.bottom()-1, rect.right()-10, rect.bottom()-1 );
00266 painter.drawLine( rect.right()-10, rect.bottom()-1, rect.right(), rect.top() );
00267 if( !active )
00268 painter.drawLine( rect.x(), rect.y(), rect.right(), rect.y() );
00269
00270 painter.save();
00271 QFont f = painter.font();
00272 if( active ) f.setBold( true );
00273 painter.setFont( f );
00274 QFontMetrics fm = painter.fontMetrics();
00275 int tx = rect.x() + ( rect.width() - fm.width( text ) ) / 2;
00276 int ty = rect.y() + ( rect.height() - fm.height() ) / 2 + fm.ascent();
00277 painter.drawText( tx, ty, text );
00278 painter.restore();
00279 }
00280
00281 void KoTabBarPrivate::drawMoveMarker( QPainter& painter, int x, int y )
00282 {
00283 QPointArray movmark;
00284 movmark.setPoints( 3, x, y, x + 7, y, x + 4, y + 6);
00285 QBrush oldBrush = painter.brush();
00286 painter.setBrush( Qt::black );
00287 painter.drawPolygon(movmark);
00288 painter.setBrush( oldBrush );
00289 }
00290
00291 void KoTabBarPrivate::layoutButtons()
00292 {
00293 int bw = tabbar->height();
00294 int w = tabbar->width();
00295 offset = bw * 4;
00296
00297 if( !reverseLayout )
00298 {
00299 scrollFirstButton->setGeometry( 0, 0, bw, bw );
00300 scrollFirstButton->setPixmap( arrow_leftmost_xpm );
00301 scrollBackButton->setGeometry( bw, 0, bw, bw );
00302 scrollBackButton->setPixmap( arrow_left_xpm );
00303 scrollForwardButton->setGeometry( bw*2, 0, bw, bw );
00304 scrollForwardButton->setPixmap( arrow_right_xpm );
00305 scrollLastButton->setGeometry( bw*3, 0, bw, bw );
00306 scrollLastButton->setPixmap( arrow_rightmost_xpm );
00307 }
00308 else
00309 {
00310 scrollFirstButton->setGeometry( w-bw, 0, bw, bw );
00311 scrollFirstButton->setPixmap( arrow_rightmost_xpm );
00312 scrollBackButton->setGeometry( w-2*bw, 0, bw, bw );
00313 scrollBackButton->setPixmap( arrow_right_xpm );
00314 scrollForwardButton->setGeometry( w-3*bw, 0, bw, bw );
00315 scrollForwardButton->setPixmap( arrow_left_xpm );
00316 scrollLastButton->setGeometry( w-4*bw, 0, bw, bw );
00317 scrollLastButton->setPixmap( arrow_leftmost_xpm );
00318 }
00319 }
00320
00321 void KoTabBarPrivate::updateButtons()
00322 {
00323 scrollFirstButton->setEnabled( tabbar->canScrollBack() );
00324 scrollBackButton->setEnabled( tabbar->canScrollBack() );
00325 scrollForwardButton->setEnabled( tabbar->canScrollForward() );
00326 scrollLastButton->setEnabled( tabbar->canScrollForward() );
00327 }
00328
00329
00330 KoTabBar::KoTabBar( QWidget* parent, const char* name )
00331 : QWidget( parent, name, Qt::WResizeNoErase | Qt::WRepaintNoErase )
00332 {
00333 d = new KoTabBarPrivate;
00334 d->tabbar = this;
00335 d->readOnly = false;
00336 d->reverseLayout = false;
00337 d->firstTab = 1;
00338 d->lastTab = 0;
00339 d->activeTab = 0;
00340 d->targetTab = 0;
00341 d->autoScroll = false;
00342 d->offset = 64;
00343
00344
00345 d->scrollFirstButton = new QToolButton( this );
00346 connect( d->scrollFirstButton, SIGNAL( clicked() ),
00347 this, SLOT( scrollFirst() ) );
00348 d->scrollLastButton = new QToolButton( this );
00349 connect( d->scrollLastButton, SIGNAL( clicked() ),
00350 this, SLOT( scrollLast() ) );
00351 d->scrollBackButton = new QToolButton( this );
00352 connect( d->scrollBackButton, SIGNAL( clicked() ),
00353 this, SLOT( scrollBack() ) );
00354 d->scrollForwardButton = new QToolButton( this );
00355 connect( d->scrollForwardButton, SIGNAL( clicked() ),
00356 this, SLOT( scrollForward() ) );
00357 d->layoutButtons();
00358 d->updateButtons();
00359 }
00360
00361
00362 KoTabBar::~KoTabBar()
00363 {
00364 delete d;
00365 }
00366
00367
00368 void KoTabBar::addTab( const QString& text )
00369 {
00370 d->tabs.append( text );
00371
00372 update();
00373 }
00374
00375
00376 void KoTabBar::removeTab( const QString& text )
00377 {
00378 int i = d->tabs.findIndex( text );
00379 if ( i == -1 ) return;
00380
00381 if ( d->activeTab == i + 1 )
00382 d->activeTab = 0;
00383
00384 d->tabs.remove( text );
00385
00386 update();
00387 }
00388
00389
00390 void KoTabBar::clear()
00391 {
00392 d->tabs.clear();
00393 d->activeTab = 0;
00394 d->firstTab = 1;
00395
00396 update();
00397 }
00398
00399 bool KoTabBar::readOnly() const
00400 {
00401 return d->readOnly;
00402 }
00403
00404 void KoTabBar::setReadOnly( bool ro )
00405 {
00406 d->readOnly = ro;
00407 }
00408
00409 bool KoTabBar::reverseLayout() const
00410 {
00411 return d->reverseLayout;
00412 }
00413
00414 void KoTabBar::setReverseLayout( bool reverse )
00415 {
00416 if( reverse != d->reverseLayout )
00417 {
00418 d->reverseLayout = reverse;
00419 d->layoutTabs();
00420 d->layoutButtons();
00421 d->updateButtons();
00422 update();
00423 }
00424 }
00425
00426 void KoTabBar::setTabs( const QStringList& list )
00427 {
00428 QString left, active;
00429
00430 if( d->activeTab > 0 )
00431 active = d->tabs[ d->activeTab-1 ];
00432 if( d->firstTab > 0 )
00433 left = d->tabs[ d->firstTab-1 ];
00434
00435 d->tabs = list;
00436
00437 if( !left.isNull() )
00438 {
00439 d->firstTab = d->tabs.findIndex( left ) + 1;
00440 if( d->firstTab > (int)d->tabs.count() )
00441 d->firstTab = 1;
00442 if( d->firstTab <= 0 )
00443 d->firstTab = 1;
00444 }
00445
00446 d->activeTab = 0;
00447 if( !active.isNull() )
00448 setActiveTab( active );
00449
00450 update();
00451 }
00452
00453 QStringList KoTabBar::tabs() const
00454 {
00455 return d->tabs;
00456 }
00457
00458 unsigned KoTabBar::count() const
00459 {
00460 return d->tabs.count();
00461 }
00462
00463 bool KoTabBar::canScrollBack() const
00464 {
00465 if ( d->tabs.count() == 0 )
00466 return false;
00467
00468 return d->firstTab > 1;
00469 }
00470
00471 bool KoTabBar::canScrollForward() const
00472 {
00473 if ( d->tabs.count() == 0 )
00474 return false;
00475
00476 return d->lastTab < (int)d->tabs.count();
00477 }
00478
00479 void KoTabBar::scrollBack()
00480 {
00481 if ( !canScrollBack() )
00482 return;
00483
00484 d->firstTab--;
00485 if( d->firstTab < 1 ) d->firstTab = 1;
00486
00487 d->layoutTabs();
00488 d->updateButtons();
00489 update();
00490 }
00491
00492 void KoTabBar::scrollForward()
00493 {
00494 if ( !canScrollForward() )
00495 return;
00496
00497 d->firstTab ++;
00498 if( d->firstTab > (int)d->tabs.count() )
00499 d->firstTab = d->tabs.count();
00500
00501 d->layoutTabs();
00502 d->updateButtons();
00503 update();
00504 }
00505
00506 void KoTabBar::scrollFirst()
00507 {
00508 if ( !canScrollBack() )
00509 return;
00510
00511 d->firstTab = 1;
00512 d->layoutTabs();
00513 d->updateButtons();
00514 update();
00515 }
00516
00517 void KoTabBar::scrollLast()
00518 {
00519 if ( !canScrollForward() )
00520 return;
00521
00522 d->layoutTabs();
00523
00524 if( !d->reverseLayout )
00525 {
00526 int fullWidth = d->tabRects[ d->tabRects.count()-1 ].right();
00527 int delta = fullWidth - width() + d->offset;
00528 for( unsigned i = 0; i < d->tabRects.count(); i++ )
00529 if( d->tabRects[i].x() > delta )
00530 {
00531 d->firstTab = i+1;
00532 break;
00533 }
00534 }
00535 else
00536 {
00537
00538 for( ; d->firstTab <= (int)d->tabRects.count();)
00539 {
00540 int x = d->tabRects[ d->tabRects.count()-1 ].x();
00541 if( x > 0 ) break;
00542 d->firstTab++;
00543 d->layoutTabs();
00544 }
00545 }
00546
00547 d->layoutTabs();
00548 d->updateButtons();
00549 update();
00550 }
00551
00552 void KoTabBar::moveTab( unsigned tab, unsigned target )
00553 {
00554 QString tabName = d->tabs[ tab ];
00555 QStringList::Iterator it;
00556
00557 it = d->tabs.at( tab );
00558 d->tabs.remove( it );
00559
00560 if( target > tab ) target--;
00561 it = d->tabs.at( target );
00562 if( target >= d->tabs.count() )
00563 it = d->tabs.end();
00564 d->tabs.insert( it, tabName );
00565
00566 if( d->activeTab == (int)tab+1 )
00567 d->activeTab = target+1;
00568
00569 update();
00570 }
00571
00572 void KoTabBar::setActiveTab( const QString& text )
00573 {
00574 int i = d->tabs.findIndex( text );
00575 if ( i == -1 )
00576 return;
00577
00578 if ( i + 1 == d->activeTab )
00579 return;
00580
00581 d->activeTab = i + 1;
00582 d->updateButtons();
00583 update();
00584
00585 emit tabChanged( text );
00586 }
00587
00588 void KoTabBar::autoScrollBack()
00589 {
00590 if( !d->autoScroll ) return;
00591
00592 scrollBack();
00593
00594 if( !canScrollBack() )
00595 d->autoScroll = false;
00596 else
00597 QTimer::singleShot( 400, this, SLOT( autoScrollBack() ) );
00598 }
00599
00600 void KoTabBar::autoScrollForward()
00601 {
00602 if( !d->autoScroll ) return;
00603
00604 scrollForward();
00605
00606 if( !canScrollForward() )
00607 d->autoScroll = false;
00608 else
00609 QTimer::singleShot( 400, this, SLOT( autoScrollForward() ) );
00610 }
00611
00612 void KoTabBar::paintEvent( QPaintEvent* )
00613 {
00614 if ( d->tabs.count() == 0 )
00615 {
00616 erase();
00617 return;
00618 }
00619
00620 QPainter painter;
00621 QPixmap pm( size() );
00622 pm.fill( colorGroup().background() );
00623 painter.begin( &pm, this );
00624
00625 QBrush fill( colorGroup().brush( QColorGroup::Background ) );
00626 qDrawShadePanel( &painter, 0, 0, width(),
00627 height(), colorGroup(), FALSE, 1, &fill );
00628
00629 d->layoutTabs();
00630 d->updateButtons();
00631
00632
00633 for( unsigned c = 0; c < d->tabRects.count(); c++ )
00634 {
00635 QRect rect = d->tabRects[ c ];
00636 if( rect.isNull() ) continue;
00637 QString text = d->tabs[ c ];
00638 d->drawTab( painter, rect, text, false );
00639 }
00640
00641
00642 if( d->activeTab > 0 )
00643 {
00644 QRect rect = d->tabRects[ d->activeTab-1 ];
00645 if( !rect.isNull() )
00646 {
00647 QString text = d->tabs[ d->activeTab-1 ];
00648 d->drawTab( painter, rect, text, true );
00649 }
00650 }
00651
00652
00653 if( d->targetTab > 0 )
00654 {
00655 int p = QMIN( d->targetTab, (int)d->tabRects.count() );
00656 QRect rect = d->tabRects[ p-1 ];
00657 if( !rect.isNull() )
00658 {
00659 int x = !d->reverseLayout ? rect.x() : rect.right()-7;
00660 if( d->targetTab > (int)d->tabRects.count() )
00661 x = !d->reverseLayout ? rect.right()-7 : rect.x()-3;
00662 d->drawMoveMarker( painter, x, rect.y() );
00663 }
00664 }
00665
00666 painter.end();
00667
00668 if( !d->reverseLayout )
00669 bitBlt( this, d->offset, 0, &pm );
00670 else
00671 bitBlt( this, 0, 0, &pm );
00672
00673 }
00674
00675 void KoTabBar::resizeEvent( QResizeEvent* )
00676 {
00677 d->layoutButtons();
00678 d->updateButtons();
00679 update();
00680 }
00681
00682 QSize KoTabBar::sizeHint() const
00683 {
00684 return QSize( 40, style().pixelMetric( QStyle::PM_ScrollBarExtent, this ) );
00685 }
00686
00687 void KoTabBar::renameTab( const QString& old_name, const QString& new_name )
00688 {
00689 QStringList::Iterator it = d->tabs.find( old_name );
00690 (*it) = new_name;
00691
00692 update();
00693 }
00694
00695 QString KoTabBar::activeTab() const
00696 {
00697 if( d->activeTab == 0 )
00698 return QString::null;
00699 else
00700 return d->tabs[ d->activeTab ];
00701 }
00702
00703 void KoTabBar::mousePressEvent( QMouseEvent* ev )
00704 {
00705 if ( d->tabs.count() == 0 )
00706 {
00707 erase();
00708 return;
00709 }
00710
00711 d->layoutTabs();
00712
00713 QPoint pos = ev->pos();
00714 if( !d->reverseLayout ) pos = pos - QPoint( d->offset,0 );
00715
00716 int tab = d->tabAt( pos ) + 1;
00717 if( ( tab > 0 ) && ( tab != d->activeTab ) )
00718 {
00719 d->activeTab = tab;
00720 update();
00721
00722 emit tabChanged( d->tabs[ d->activeTab-1] );
00723
00724
00725 if( d->tabRects[ tab-1 ].right() > width() - d->offset )
00726 scrollForward();
00727 }
00728
00729 if( ev->button() == RightButton )
00730 if( !d->readOnly )
00731 emit contextMenu( ev->globalPos() );
00732 }
00733
00734 void KoTabBar::mouseReleaseEvent( QMouseEvent* ev )
00735 {
00736 if ( d->readOnly ) return;
00737
00738 d->autoScroll = false;
00739
00740 if ( ev->button() == LeftButton && d->targetTab != 0 )
00741 {
00742 emit tabMoved( d->activeTab-1, d->targetTab-1 );
00743 d->targetTab = 0;
00744 }
00745 }
00746
00747 void KoTabBar::mouseMoveEvent( QMouseEvent* ev )
00748 {
00749 if ( d->readOnly ) return;
00750
00751 QPoint pos = ev->pos();
00752 if( !d->reverseLayout) pos = pos - QPoint( d->offset,0 );
00753
00754
00755 int i = d->tabAt( pos ) + 1;
00756 if( ( i > 0 ) && ( i != d->targetTab ) )
00757 {
00758 if( i == d->activeTab ) i = 0;
00759 if( i == d->activeTab+1 ) i = 0;
00760
00761 if( i != d->targetTab )
00762 {
00763 d->targetTab = i;
00764 d->autoScroll = false;
00765 update();
00766 }
00767 }
00768
00769
00770
00771 QRect r = d->tabRects[ d->tabRects.count()-1 ];
00772 bool moveToLast = false;
00773 if( r.isValid() )
00774 {
00775 if( !d->reverseLayout )
00776 if( pos.x() > r.right() )
00777 if( pos.x() < width() )
00778 moveToLast = true;
00779 if( d->reverseLayout )
00780 if( pos.x() < r.x() )
00781 if( pos.x() > 0 )
00782 moveToLast = true;
00783 }
00784 if( moveToLast )
00785 if( d->targetTab != (int)d->tabRects.count()+1 )
00786 {
00787 d->targetTab = d->tabRects.count()+1;
00788 d->autoScroll = false;
00789 update();
00790 }
00791
00792
00793 if ( pos.x() < 0 && !d->autoScroll )
00794 {
00795 d->autoScroll = true;
00796 autoScrollBack();
00797 }
00798
00799
00800 int w = width() - d->offset;
00801 if ( pos.x() > w && !d->autoScroll )
00802 {
00803 d->autoScroll = true;
00804 autoScrollForward();
00805 }
00806 }
00807
00808 void KoTabBar::mouseDoubleClickEvent( QMouseEvent* ev )
00809 {
00810 int offset = d->reverseLayout ? 0 : d->offset;
00811 if( ev->pos().x() > offset )
00812 if( !d->readOnly )
00813 emit doubleClicked();
00814 }
00815
00816
00817 #include "kotabbar.moc"