ktabwidget.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2003 Stephan Binner <binner@kde.org> 00003 Copyright (C) 2003 Zack Rusin <zack@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include <qapplication.h> 00022 #include <qstyle.h> 00023 #include <qstylesheet.h> 00024 00025 #include <kconfig.h> 00026 #include <kiconloader.h> 00027 #include <kstringhandler.h> 00028 00029 #include "ktabwidget.h" 00030 #include "ktabbar.h" 00031 00032 class KTabWidgetPrivate { 00033 public: 00034 bool m_automaticResizeTabs; 00035 int m_maxLength; 00036 int m_minLength; 00037 unsigned int m_CurrentMaxLength; 00038 00039 //holds the full names of the tab, otherwise all we 00040 //know about is the shortened name 00041 QStringList m_tabNames; 00042 00043 KTabWidgetPrivate() { 00044 m_automaticResizeTabs = false; 00045 KConfigGroupSaver groupsaver(KGlobal::config(), "General"); 00046 m_maxLength = KGlobal::config()->readNumEntry("MaximumTabLength", 30); 00047 m_minLength = KGlobal::config()->readNumEntry("MinimumTabLength", 3); 00048 m_CurrentMaxLength = m_minLength; 00049 } 00050 }; 00051 00052 KTabWidget::KTabWidget( QWidget *parent, const char *name, WFlags f ) 00053 : QTabWidget( parent, name, f ) 00054 { 00055 d = new KTabWidgetPrivate; 00056 setTabBar( new KTabBar(this, "tabbar") ); 00057 setAcceptDrops( true ); 00058 00059 connect(tabBar(), SIGNAL(contextMenu( int, const QPoint & )), SLOT(contextMenu( int, const QPoint & ))); 00060 connect(tabBar(), SIGNAL(mouseDoubleClick( int )), SLOT(mouseDoubleClick( int ))); 00061 connect(tabBar(), SIGNAL(mouseMiddleClick( int )), SLOT(mouseMiddleClick( int ))); 00062 connect(tabBar(), SIGNAL(initiateDrag( int )), SLOT(initiateDrag( int ))); 00063 connect(tabBar(), SIGNAL(testCanDecode(const QDragMoveEvent *, bool & )), SIGNAL(testCanDecode(const QDragMoveEvent *, bool & ))); 00064 connect(tabBar(), SIGNAL(receivedDropEvent( int, QDropEvent * )), SLOT(receivedDropEvent( int, QDropEvent * ))); 00065 connect(tabBar(), SIGNAL(moveTab( int, int )), SLOT(moveTab( int, int ))); 00066 connect(tabBar(), SIGNAL(closeRequest( int )), SLOT(closeRequest( int ))); 00067 #ifndef QT_NO_WHEELEVENT 00068 connect(tabBar(), SIGNAL(wheelDelta( int )), SLOT(wheelDelta( int ))); 00069 #endif 00070 } 00071 00072 KTabWidget::~KTabWidget() 00073 { 00074 delete d; 00075 } 00076 00077 void KTabWidget::insertTab( QWidget *child, const QString &label, int index ) 00078 { 00079 QTabWidget::insertTab( child, label, index ); 00080 } 00081 00082 void KTabWidget::insertTab( QWidget *child, const QIconSet& iconset, const QString &label, int index ) 00083 { 00084 QTabWidget::insertTab( child, iconset, label, index ); 00085 } 00086 00087 void KTabWidget::insertTab( QWidget *child, QTab *tab, int index ) 00088 { 00089 QTabWidget::insertTab( child, tab, index); 00090 if ( d->m_automaticResizeTabs ) { 00091 if ( index < 0 || index >= count() ) { 00092 d->m_tabNames.append( tab->text() ); 00093 resizeTabs( d->m_tabNames.count()-1 ); 00094 } 00095 else { 00096 d->m_tabNames.insert( d->m_tabNames.at( index ), tab->text() ); 00097 resizeTabs( index ); 00098 } 00099 } 00100 } 00101 00102 void KTabWidget::setTabBarHidden( bool hide ) 00103 { 00104 QWidget *rightcorner = this->cornerWidget( TopRight ); 00105 QWidget *leftcorner = this->cornerWidget( TopLeft ); 00106 00107 if ( hide ) { 00108 if ( leftcorner ) leftcorner->hide(); 00109 if ( rightcorner ) rightcorner->hide(); 00110 tabBar()->hide(); 00111 } else { 00112 tabBar()->show(); 00113 if ( leftcorner ) leftcorner->show(); 00114 if ( rightcorner ) rightcorner->show(); 00115 } 00116 } 00117 00118 bool KTabWidget::isTabBarHidden() const 00119 { 00120 return !( tabBar()->isVisible() ); 00121 } 00122 00123 void KTabWidget::setTabColor( QWidget *w, const QColor& color ) 00124 { 00125 QTab *t = tabBar()->tabAt( indexOf( w ) ); 00126 if (t) { 00127 static_cast<KTabBar*>(tabBar())->setTabColor( t->identifier(), color ); 00128 } 00129 } 00130 00131 QColor KTabWidget::tabColor( QWidget *w ) const 00132 { 00133 QTab *t = tabBar()->tabAt( indexOf( w ) ); 00134 if (t) { 00135 return static_cast<KTabBar*>(tabBar())->tabColor( t->identifier() ); 00136 } else { 00137 return QColor(); 00138 } 00139 } 00140 00141 void KTabWidget::setTabReorderingEnabled( bool on) 00142 { 00143 static_cast<KTabBar*>(tabBar())->setTabReorderingEnabled( on ); 00144 } 00145 00146 bool KTabWidget::isTabReorderingEnabled() const 00147 { 00148 return static_cast<KTabBar*>(tabBar())->isTabReorderingEnabled(); 00149 } 00150 00151 void KTabWidget::setTabCloseActivatePrevious( bool previous) 00152 { 00153 static_cast<KTabBar*>(tabBar())->setTabCloseActivatePrevious( previous ); 00154 } 00155 00156 bool KTabWidget::tabCloseActivatePrevious() const 00157 { 00158 return static_cast<KTabBar*>(tabBar())->tabCloseActivatePrevious(); 00159 } 00160 00161 unsigned int KTabWidget::tabBarWidthForMaxChars( uint maxLength ) 00162 { 00163 int hframe, overlap; 00164 hframe = tabBar()->style().pixelMetric( QStyle::PM_TabBarTabHSpace, tabBar() ); 00165 overlap = tabBar()->style().pixelMetric( QStyle::PM_TabBarTabOverlap, tabBar() ); 00166 00167 QFontMetrics fm = tabBar()->fontMetrics(); 00168 int x = 0; 00169 for( int i=0; i < count(); ++i ) { 00170 QString newTitle = d->m_tabNames[ i ]; 00171 newTitle = KStringHandler::rsqueeze( newTitle, maxLength ).leftJustify( d->m_minLength, ' ' ); 00172 00173 QTab* tab = tabBar()->tabAt( i ); 00174 int lw = fm.width( newTitle ); 00175 int iw = 0; 00176 if ( tab->iconSet() ) 00177 iw = tab->iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4; 00178 x += ( tabBar()->style().sizeFromContents( QStyle::CT_TabBarTab, this, 00179 QSize( QMAX( lw + hframe + iw, QApplication::globalStrut().width() ), 0 ), 00180 QStyleOption( tab ) ) ).width(); 00181 } 00182 return x; 00183 } 00184 00185 void KTabWidget::changeTab( QWidget *w, const QString &label ) 00186 { 00187 QTabWidget::changeTab( w, label ); 00188 if ( d->m_automaticResizeTabs ) { 00189 int index = indexOf( w ); 00190 if ( index != -1 ) { 00191 d->m_tabNames[ index ] = label; 00192 resizeTabs( index ); 00193 } 00194 } 00195 } 00196 00197 void KTabWidget::changeTab( QWidget *w, const QIconSet &iconset, const QString &label ) 00198 { 00199 QTabWidget::changeTab( w, iconset, label ); 00200 if ( d->m_automaticResizeTabs ) { 00201 int index = indexOf( w ); 00202 if ( index != -1 ) { 00203 d->m_tabNames[ index ] = label; 00204 resizeTabs( index ); 00205 } 00206 } 00207 } 00208 00209 QString KTabWidget::label( int index ) const 00210 { 00211 if ( d->m_automaticResizeTabs ) { 00212 if ( index >= 0 && index < count() ) 00213 return d->m_tabNames[ index ]; 00214 else 00215 return QString::null; 00216 } 00217 else 00218 return QTabWidget::label( index ); 00219 } 00220 00221 QString KTabWidget::tabLabel( QWidget * w ) const 00222 { 00223 if ( d->m_automaticResizeTabs ) { 00224 int index = indexOf( w ); 00225 if ( index == -1 ) 00226 return QString::null; 00227 else 00228 return d->m_tabNames[ index ]; 00229 } 00230 else 00231 return QTabWidget::tabLabel( w ); 00232 } 00233 00234 void KTabWidget::setTabLabel( QWidget *w, const QString &l ) 00235 { 00236 QTabWidget::setTabLabel( w, l ); 00237 if ( d->m_automaticResizeTabs ) { 00238 int index = indexOf( w ); 00239 if ( index != -1 ) { 00240 d->m_tabNames[ index ] = l; 00241 resizeTabs( index ); 00242 } 00243 } 00244 } 00245 00246 void KTabWidget::resizeTabs( int changeTabIndex ) 00247 { 00248 uint newMaxLength; 00249 if ( d->m_automaticResizeTabs ) { 00250 // Calculate new max length 00251 newMaxLength=d->m_maxLength; 00252 uint lcw=0, rcw=0; 00253 00254 int tabBarHeight = tabBar()->sizeHint().height(); 00255 if ( cornerWidget( TopLeft ) && cornerWidget( TopLeft )->isVisible() ) 00256 lcw = QMAX( cornerWidget( TopLeft )->width(), tabBarHeight ); 00257 if ( cornerWidget( TopRight ) && cornerWidget( TopRight )->isVisible() ) 00258 rcw = QMAX( cornerWidget( TopRight )->width(), tabBarHeight ); 00259 00260 uint maxTabBarWidth = width() - lcw - rcw; 00261 00262 for ( ; newMaxLength > (uint)d->m_minLength; newMaxLength-- ) { 00263 if ( tabBarWidthForMaxChars( newMaxLength ) < maxTabBarWidth ) 00264 break; 00265 } 00266 } 00267 else 00268 newMaxLength = 4711; 00269 00270 // Update hinted or all tabs 00271 if ( d->m_CurrentMaxLength != newMaxLength ) { 00272 d->m_CurrentMaxLength = newMaxLength; 00273 for( int i = 0; i < count(); ++i ) 00274 updateTab( i ); 00275 } 00276 else if ( changeTabIndex != -1 ) 00277 updateTab( changeTabIndex ); 00278 } 00279 00280 void KTabWidget::updateTab( int index ) 00281 { 00282 QString title = d->m_automaticResizeTabs ? d->m_tabNames[ index ] : QTabWidget::label( index ); 00283 removeTabToolTip( page( index ) ); 00284 if ( title.length() > d->m_CurrentMaxLength ) { 00285 if ( QStyleSheet::mightBeRichText( title ) ) 00286 setTabToolTip( page( index ), QStyleSheet::escape(title) ); 00287 else 00288 setTabToolTip( page( index ), title ); 00289 } 00290 00291 title = KStringHandler::rsqueeze( title, d->m_CurrentMaxLength ).leftJustify( d->m_minLength, ' ' ); 00292 title.replace( '&', "&&" ); 00293 00294 if ( QTabWidget::label( index ) != title ) 00295 QTabWidget::setTabLabel( page( index ), title ); 00296 } 00297 00298 void KTabWidget::dragMoveEvent( QDragMoveEvent *e ) 00299 { 00300 if ( isEmptyTabbarSpace( e->pos() ) ) { 00301 bool accept = false; 00302 // The receivers of the testCanDecode() signal has to adjust 00303 // 'accept' accordingly. 00304 emit testCanDecode( e, accept); 00305 e->accept( accept ); 00306 return; 00307 } 00308 e->accept( false ); 00309 QTabWidget::dragMoveEvent( e ); 00310 } 00311 00312 void KTabWidget::dropEvent( QDropEvent *e ) 00313 { 00314 if ( isEmptyTabbarSpace( e->pos() ) ) { 00315 emit ( receivedDropEvent( e ) ); 00316 return; 00317 } 00318 QTabWidget::dropEvent( e ); 00319 } 00320 00321 #ifndef QT_NO_WHEELEVENT 00322 void KTabWidget::wheelEvent( QWheelEvent *e ) 00323 { 00324 if ( e->orientation() == Horizontal ) 00325 return; 00326 00327 if ( isEmptyTabbarSpace( e->pos() ) ) 00328 wheelDelta( e->delta() ); 00329 else 00330 e->ignore(); 00331 } 00332 00333 void KTabWidget::wheelDelta( int delta ) 00334 { 00335 if ( count() < 2 ) 00336 return; 00337 00338 int page = currentPageIndex(); 00339 if ( delta < 0 ) 00340 page = (page + 1) % count(); 00341 else { 00342 page--; 00343 if ( page < 0 ) 00344 page = count() - 1; 00345 } 00346 setCurrentPage( page ); 00347 } 00348 #endif 00349 00350 void KTabWidget::mouseDoubleClickEvent( QMouseEvent *e ) 00351 { 00352 if( e->button() != LeftButton ) 00353 return; 00354 00355 if ( isEmptyTabbarSpace( e->pos() ) ) { 00356 emit( mouseDoubleClick() ); 00357 return; 00358 } 00359 QTabWidget::mouseDoubleClickEvent( e ); 00360 } 00361 00362 void KTabWidget::mousePressEvent( QMouseEvent *e ) 00363 { 00364 if ( e->button() == RightButton ) { 00365 if ( isEmptyTabbarSpace( e->pos() ) ) { 00366 emit( contextMenu( mapToGlobal( e->pos() ) ) ); 00367 return; 00368 } 00369 } else if ( e->button() == MidButton ) { 00370 if ( isEmptyTabbarSpace( e->pos() ) ) { 00371 emit( mouseMiddleClick() ); 00372 return; 00373 } 00374 } 00375 QTabWidget::mousePressEvent( e ); 00376 } 00377 00378 void KTabWidget::receivedDropEvent( int index, QDropEvent *e ) 00379 { 00380 emit( receivedDropEvent( page( index ), e ) ); 00381 } 00382 00383 void KTabWidget::initiateDrag( int index ) 00384 { 00385 emit( initiateDrag( page( index ) ) ); 00386 } 00387 00388 void KTabWidget::contextMenu( int index, const QPoint &p ) 00389 { 00390 emit( contextMenu( page( index ), p ) ); 00391 } 00392 00393 void KTabWidget::mouseDoubleClick( int index ) 00394 { 00395 emit( mouseDoubleClick( page( index ) ) ); 00396 } 00397 00398 void KTabWidget::mouseMiddleClick( int index ) 00399 { 00400 emit( mouseMiddleClick( page( index ) ) ); 00401 } 00402 00403 void KTabWidget::moveTab( int from, int to ) 00404 { 00405 QString tablabel = label( from ); 00406 QWidget *w = page( from ); 00407 QColor color = tabColor( w ); 00408 QIconSet tabiconset = tabIconSet( w ); 00409 QString tabtooltip = tabToolTip( w ); 00410 bool current = ( w == currentPage() ); 00411 bool enabled = isTabEnabled( w ); 00412 blockSignals(true); 00413 removePage( w ); 00414 00415 // Work-around kmdi brain damage which calls showPage() in insertTab() 00416 QTab * t = new QTab(); 00417 t->setText(tablabel); 00418 QTabWidget::insertTab( w, t, to ); 00419 if ( d->m_automaticResizeTabs ) { 00420 if ( to < 0 || to >= count() ) 00421 d->m_tabNames.append( QString::null ); 00422 else 00423 d->m_tabNames.insert( d->m_tabNames.at( to ), QString::null ); 00424 } 00425 00426 w = page( to ); 00427 changeTab( w, tabiconset, tablabel ); 00428 setTabToolTip( w, tabtooltip ); 00429 setTabColor( w, color ); 00430 if ( current ) 00431 showPage( w ); 00432 setTabEnabled( w, enabled ); 00433 blockSignals(false); 00434 00435 emit ( movedTab( from, to ) ); 00436 } 00437 00438 void KTabWidget::removePage( QWidget * w ) { 00439 if ( d->m_automaticResizeTabs ) { 00440 int index = indexOf( w ); 00441 if ( index != -1 ) 00442 d->m_tabNames.remove( d->m_tabNames.at( index ) ); 00443 } 00444 QTabWidget::removePage( w ); 00445 if ( d->m_automaticResizeTabs ) 00446 resizeTabs(); 00447 } 00448 00449 00450 bool KTabWidget::isEmptyTabbarSpace( const QPoint &point ) const 00451 { 00452 QSize size( tabBar()->sizeHint() ); 00453 if ( ( tabPosition()==Top && point.y()< size.height() ) || ( tabPosition()==Bottom && point.y()>(height()-size.height() ) ) ) { 00454 QWidget *rightcorner = cornerWidget( TopRight ); 00455 if ( rightcorner ) { 00456 if ( point.x()>=width()-rightcorner->width() ) 00457 return false; 00458 } 00459 QWidget *leftcorner = cornerWidget( TopLeft ); 00460 if ( leftcorner ) { 00461 if ( point.x()<=leftcorner->width() ) 00462 return false; 00463 } 00464 QTab *tab = tabBar()->selectTab( tabBar()->mapFromParent( point ) ); 00465 if( !tab ) 00466 return true; 00467 } 00468 return false; 00469 } 00470 00471 void KTabWidget::setHoverCloseButton( bool button ) 00472 { 00473 static_cast<KTabBar*>(tabBar())->setHoverCloseButton( button ); 00474 } 00475 00476 bool KTabWidget::hoverCloseButton() const 00477 { 00478 return static_cast<KTabBar*>(tabBar())->hoverCloseButton(); 00479 } 00480 00481 void KTabWidget::setHoverCloseButtonDelayed( bool delayed ) 00482 { 00483 static_cast<KTabBar*>(tabBar())->setHoverCloseButtonDelayed( delayed ); 00484 } 00485 00486 bool KTabWidget::hoverCloseButtonDelayed() const 00487 { 00488 return static_cast<KTabBar*>(tabBar())->hoverCloseButtonDelayed(); 00489 } 00490 00491 void KTabWidget::setAutomaticResizeTabs( bool enabled ) 00492 { 00493 if ( d->m_automaticResizeTabs==enabled ) 00494 return; 00495 00496 d->m_automaticResizeTabs = enabled; 00497 if ( enabled ) { 00498 d->m_tabNames.clear(); 00499 for( int i = 0; i < count(); ++i ) 00500 d->m_tabNames.append( tabBar()->tabAt( i )->text() ); 00501 } 00502 else 00503 for( int i = 0; i < count(); ++i ) 00504 tabBar()->tabAt( i )->setText( d->m_tabNames[ i ] ); 00505 resizeTabs(); 00506 } 00507 00508 bool KTabWidget::automaticResizeTabs() const 00509 { 00510 return d->m_automaticResizeTabs; 00511 } 00512 00513 void KTabWidget::closeRequest( int index ) 00514 { 00515 emit( closeRequest( page( index ) ) ); 00516 } 00517 00518 void KTabWidget::resizeEvent( QResizeEvent *e ) 00519 { 00520 QTabWidget::resizeEvent( e ); 00521 resizeTabs(); 00522 } 00523 00524 #include "ktabwidget.moc"