Merge remote branch 'kde-qt/patches/0225-invalidate-tabbar-geometry-on-refresh' into...
[qt:kde-qt.git] / src / gui / widgets / qtabbar.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial Usage
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file.  Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
35 **
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qlayoutengine_p.h"
43 #include "qabstractitemdelegate.h"
44 #include "qapplication.h"
45 #include "qbitmap.h"
46 #include "qcursor.h"
47 #include "qevent.h"
48 #include "qpainter.h"
49 #include "qstyle.h"
50 #include "qstyleoption.h"
51 #include "qstylepainter.h"
52 #include "qtabwidget.h"
53 #include "qtooltip.h"
54 #include "qwhatsthis.h"
55 #include "private/qtextengine_p.h"
56 #ifndef QT_NO_ACCESSIBILITY
57 #include "qaccessible.h"
58 #endif
59
60 #include "qdebug.h"
61 #include "private/qtabbar_p.h"
62
63 #ifndef QT_NO_TABBAR
64
65 #ifdef Q_WS_MAC
66 #include <private/qt_mac_p.h>
67 #include <private/qt_cocoa_helpers_mac_p.h>
68 #endif
69
70 #ifndef QT_NO_STYLE_S60
71 #include "qs60style.h"
72 #endif
73
74 QT_BEGIN_NAMESPACE
75
76 inline static bool verticalTabs(QTabBar::Shape shape)
77 {
78     return shape == QTabBar::RoundedWest
79            || shape == QTabBar::RoundedEast
80            || shape == QTabBar::TriangularWest
81            || shape == QTabBar::TriangularEast;
82 }
83
84 void QTabBarPrivate::updateMacBorderMetrics()
85 {
86 #if (defined Q_WS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
87     if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
88         Q_Q(QTabBar);
89         ::HIContentBorderMetrics metrics;
90
91         // TODO: get metrics to preserve the bottom value
92         // TODO: test tab bar position
93
94         OSWindowRef window = qt_mac_window_for(q);
95
96         // push base line separator down to the client are so we can paint over it (Carbon)
97         metrics.top = (documentMode && q->isVisible()) ? 1 : 0;
98         metrics.bottom = 0;
99         metrics.left = 0;
100         metrics.right = 0;
101         qt_mac_updateContentBorderMetricts(window, metrics);
102         
103         // hide the base line separator if the tabs have docuemnt mode enabled (Cocoa)
104         qt_mac_showBaseLineSeparator(window, !documentMode);
105     }
106 #endif
107 }
108
109 /*!
110     Initialize \a option with the values from the tab at \a tabIndex. This method
111     is useful for subclasses when they need a QStyleOptionTab, QStyleOptionTabV2,
112     or QStyleOptionTabV3 but don't want to fill in all the information themselves.
113     This function will check the version of the QStyleOptionTab and fill in the
114     additional values for a QStyleOptionTabV2 and QStyleOptionTabV3.
115
116     \sa QStyleOption::initFrom() QTabWidget::initStyleOption()
117 */
118 void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
119 {
120     Q_D(const QTabBar);
121     int totalTabs = d->tabList.size();
122
123     if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
124         return;
125
126     const QTabBarPrivate::Tab &tab = d->tabList.at(tabIndex);
127     option->initFrom(this);
128     option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
129     option->rect = tabRect(tabIndex);
130     bool isCurrent = tabIndex == d->currentIndex;
131     option->row = 0;
132     if (tabIndex == d->pressedIndex)
133         option->state |= QStyle::State_Sunken;
134     if (isCurrent)
135         option->state |= QStyle::State_Selected;
136     if (isCurrent && hasFocus())
137         option->state |= QStyle::State_HasFocus;
138     if (!tab.enabled)
139         option->state &= ~QStyle::State_Enabled;
140     if (isActiveWindow())
141         option->state |= QStyle::State_Active;
142     if (!d->dragInProgress && option->rect == d->hoverRect)
143         option->state |= QStyle::State_MouseOver;
144     option->shape = d->shape;
145     option->text = tab.text;
146
147     if (tab.textColor.isValid())
148         option->palette.setColor(foregroundRole(), tab.textColor);
149
150     option->icon = tab.icon;
151     if (QStyleOptionTabV2 *optionV2 = qstyleoption_cast<QStyleOptionTabV2 *>(option))
152         optionV2->iconSize = iconSize();  // Will get the default value then.
153
154     if (QStyleOptionTabV3 *optionV3 = qstyleoption_cast<QStyleOptionTabV3 *>(option)) {
155         optionV3->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
156         optionV3->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
157         optionV3->documentMode = d->documentMode;
158     }
159
160     if (tabIndex > 0 && tabIndex - 1 == d->currentIndex)
161         option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
162     else if (tabIndex < totalTabs - 1 && tabIndex + 1 == d->currentIndex)
163         option->selectedPosition = QStyleOptionTab::NextIsSelected;
164     else
165         option->selectedPosition = QStyleOptionTab::NotAdjacent;
166
167     bool paintBeginning = (tabIndex == 0) || (d->dragInProgress && tabIndex == d->pressedIndex + 1);
168     bool paintEnd = (tabIndex == totalTabs - 1) || (d->dragInProgress && tabIndex == d->pressedIndex - 1);
169     if (paintBeginning) {
170         if (paintEnd)
171             option->position = QStyleOptionTab::OnlyOneTab;
172         else
173             option->position = QStyleOptionTab::Beginning;
174     } else if (paintEnd) {
175         option->position = QStyleOptionTab::End;
176     } else {
177         option->position = QStyleOptionTab::Middle;
178     }
179
180 #ifndef QT_NO_TABWIDGET
181     if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(parentWidget())) {
182         if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
183             option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
184         if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
185             option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
186     }
187 #endif
188
189     QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this);
190     option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
191                         Qt::TextShowMnemonic);
192 }
193
194 /*!
195     \class QTabBar
196     \brief The QTabBar class provides a tab bar, e.g. for use in tabbed dialogs.
197
198     \ingroup basicwidgets
199
200
201     QTabBar is straightforward to use; it draws the tabs using one of
202     the predefined \link QTabBar::Shape shapes\endlink, and emits a
203     signal when a tab is selected. It can be subclassed to tailor the
204     look and feel. Qt also provides a ready-made \l{QTabWidget}.
205
206     Each tab has a tabText(), an optional tabIcon(), an optional
207     tabToolTip(), optional tabWhatsThis() and optional tabData().
208     The tabs's attributes can be changed with setTabText(), setTabIcon(),
209     setTabToolTip(), setTabWhatsThis and setTabData(). Each tabs can be
210     enabled or disabled individually with setTabEnabled().
211
212     Each tab can display text in a distinct color. The current text color
213     for a tab can be found with the tabTextColor() function. Set the text
214     color for a particular tab with setTabTextColor().
215
216     Tabs are added using addTab(), or inserted at particular positions
217     using insertTab(). The total number of tabs is given by
218     count(). Tabs can be removed from the tab bar with
219     removeTab(). Combining removeTab() and insertTab() allows you to
220     move tabs to different positions.
221
222     The \l shape property defines the tabs' appearance. The choice of
223     shape is a matter of taste, although tab dialogs (for preferences
224     and similar) invariably use \l RoundedNorth.
225     Tab controls in windows other than dialogs almost
226     always use either \l RoundedSouth or \l TriangularSouth. Many
227     spreadsheets and other tab controls in which all the pages are
228     essentially similar use \l TriangularSouth, whereas \l
229     RoundedSouth is used mostly when the pages are different (e.g. a
230     multi-page tool palette). The default in QTabBar is \l
231     RoundedNorth.
232
233     The most important part of QTabBar's API is the currentChanged()
234     signal.  This is emitted whenever the current tab changes (even at
235     startup, when the current tab changes from 'none'). There is also
236     a slot, setCurrentIndex(), which can be used to select a tab
237     programmatically. The function currentIndex() returns the index of
238     the current tab, \l count holds the number of tabs.
239
240     QTabBar creates automatic mnemonic keys in the manner of QAbstractButton;
241     e.g. if a tab's label is "\&Graphics", Alt+G becomes a shortcut
242     key for switching to that tab.
243
244     The following virtual functions may need to be reimplemented in
245     order to tailor the look and feel or store extra data with each
246     tab:
247
248     \list
249     \i tabSizeHint() calcuates the size of a tab.
250     \i tabInserted() notifies that a new tab was added.
251     \i tabRemoved() notifies that a tab was removed.
252     \i tabLayoutChange() notifies that the tabs have been re-laid out.
253     \i paintEvent() paints all tabs.
254     \endlist
255
256     For subclasses, you might also need the tabRect() functions which
257     returns the visual geometry of a single tab.
258
259     \table 100%
260     \row \o \inlineimage plastique-tabbar.png Screenshot of a Plastique style tab bar
261          \o A tab bar shown in the Plastique widget style.
262     \row \o \inlineimage plastique-tabbar-truncated.png Screenshot of a truncated Plastique tab bar
263          \o A truncated tab bar shown in the Plastique widget style.
264     \endtable
265
266     \sa QTabWidget
267 */
268
269 /*!
270     \enum QTabBar::Shape
271
272     This enum type lists the built-in shapes supported by QTabBar. Treat these
273     as hints as some styles may not render some of the shapes. However,
274     position should be honored.
275
276     \value RoundedNorth  The normal rounded look above the pages
277
278     \value RoundedSouth  The normal rounded look below the pages
279
280     \value RoundedWest  The normal rounded look on the left side of the pages
281
282     \value RoundedEast  The normal rounded look on the right side the pages
283
284     \value TriangularNorth  Triangular tabs above the pages.
285
286     \value TriangularSouth  Triangular tabs similar to those used in
287     the Excel spreadsheet, for example
288
289     \value TriangularWest  Triangular tabs on the left of the pages.
290
291     \value TriangularEast  Triangular tabs on the right of the pages.
292     \omitvalue RoundedAbove
293     \omitvalue RoundedBelow
294     \omitvalue TriangularAbove
295     \omitvalue TriangularBelow
296 */
297
298 /*!
299     \fn void QTabBar::currentChanged(int index)
300
301     This signal is emitted when the tab bar's current tab changes. The
302     new current has the given \a index, or -1 if there isn't a new one
303     (for example, if there are no tab in the QTabBar)
304 */
305
306 /*!
307     \fn void QTabBar::tabCloseRequested(int index)
308     \since 4.5
309
310     This signal is emitted when the close button on a tab is clicked.
311     The \a index is the index that should be removed.
312
313     \sa setTabsClosable()
314 */
315
316 /*!
317     \fn void QTabBar::tabMoved(int from, int to)
318     \since 4.5
319
320     This signal is emitted when the tab has moved the tab
321     at index position \a from to index position \a to.
322
323     note: QTabWidget will automatically move the page when
324     this signal is emitted from its tab bar.
325
326     \sa moveTab()
327 */
328
329 int QTabBarPrivate::extraWidth() const
330 {
331     Q_Q(const QTabBar);
332     return 2 * qMax(q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, 0, q),
333                     QApplication::globalStrut().width());
334 }
335
336 void QTabBarPrivate::init()
337 {
338     Q_Q(QTabBar);
339     leftB = new QToolButton(q);
340     leftB->setAutoRepeat(true);
341     QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
342     leftB->hide();
343     rightB = new QToolButton(q);
344     rightB->setAutoRepeat(true);
345     QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
346     rightB->hide();
347 #ifdef QT_KEYPAD_NAVIGATION
348     if (QApplication::keypadNavigationEnabled()) {
349         leftB->setFocusPolicy(Qt::NoFocus);
350         rightB->setFocusPolicy(Qt::NoFocus);
351         q->setFocusPolicy(Qt::NoFocus);
352     } else
353 #endif
354     q->setFocusPolicy(Qt::TabFocus);
355     q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
356     elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, q));
357     useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, q);
358 }
359
360 QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
361 {
362     return validIndex(index)?&tabList[index]:0;
363 }
364
365 const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
366 {
367     return validIndex(index)?&tabList[index]:0;
368 }
369
370 int QTabBarPrivate::indexAtPos(const QPoint &p) const
371 {
372     Q_Q(const QTabBar);
373     if (q->tabRect(currentIndex).contains(p))
374         return currentIndex;
375     for (int i = 0; i < tabList.count(); ++i)
376         if (tabList.at(i).enabled && q->tabRect(i).contains(p))
377             return i;
378     return -1;
379 }
380
381 void QTabBarPrivate::layoutTabs()
382 {
383     Q_Q(QTabBar);
384     scrollOffset = 0;
385     layoutDirty = false;
386     QSize size = q->size();
387     int last, available;
388     int maxExtent;
389     int i;
390     bool vertTabs = verticalTabs(shape);
391     int tabChainIndex = 0;
392
393     Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, 0, q));
394     QVector<QLayoutStruct> tabChain(tabList.count() + 2);
395
396     // We put an empty item at the front and back and set its expansive attribute
397     // depending on tabAlignment.
398     tabChain[tabChainIndex].init();
399     tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignLeft)
400                                         && (tabAlignment != Qt::AlignJustify);
401     tabChain[tabChainIndex].empty = true;
402     ++tabChainIndex;
403
404     // We now go through our list of tabs and set the minimum size and the size hint
405     // This will allow us to elide text if necessary. Since we don't set
406     // a maximum size, tabs will EXPAND to fill up the empty space.
407     // Since tab widget is rather *ahem* strict about keeping the geometry of the
408     // tab bar to its absolute minimum, this won't bleed through, but will show up
409     // if you use tab bar on its own (a.k.a. not a bug, but a feature).
410     // Update: if expanding is false, we DO set a maximum size to prevent the tabs
411     // being wider than necessary.
412     if (!vertTabs) {
413         int minx = 0;
414         int x = 0;
415         int maxHeight = 0;
416         for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
417             QSize sz = q->tabSizeHint(i);
418             tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
419             x += sz.width();
420             maxHeight = qMax(maxHeight, sz.height());
421             sz = minimumTabSizeHint(i);
422             tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
423             minx += sz.width();
424             tabChain[tabChainIndex].init();
425             tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
426             tabChain[tabChainIndex].minimumSize = sz.width();
427             tabChain[tabChainIndex].empty = false;
428             tabChain[tabChainIndex].expansive = true;
429
430             if (!expanding)
431                 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
432         }
433
434         last = minx;
435         available = size.width();
436         maxExtent = maxHeight;
437     } else {
438         int miny = 0;
439         int y = 0;
440         int maxWidth = 0;
441         for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) {
442             QSize sz = q->tabSizeHint(i);
443             tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
444             y += sz.height();
445             maxWidth = qMax(maxWidth, sz.width());
446             sz = minimumTabSizeHint(i);
447             tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
448             miny += sz.height();
449             tabChain[tabChainIndex].init();
450             tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
451             tabChain[tabChainIndex].minimumSize = sz.height();
452             tabChain[tabChainIndex].empty = false;
453             tabChain[tabChainIndex].expansive = true;
454
455             if (!expanding)
456                 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
457         }
458
459         last = miny;
460         available = size.height();
461         maxExtent = maxWidth;
462     }
463
464     Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure.
465     // Mirror our front item.
466     tabChain[tabChainIndex].init();
467     tabChain[tabChainIndex].expansive = (tabAlignment != Qt::AlignRight)
468                                         && (tabAlignment != Qt::AlignJustify);
469     tabChain[tabChainIndex].empty = true;
470
471     // Do the calculation
472     qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);
473
474     // Use the results
475     for (i = 0; i < tabList.count(); ++i) {
476         const QLayoutStruct &lstruct = tabChain.at(i + 1);
477         if (!vertTabs)
478             tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
479         else
480             tabList[i].rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
481     }
482
483     if (useScrollButtons && tabList.count() && last > available) {
484         int extra = extraWidth();
485 #ifndef QT_NO_STYLE_S60
486         QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style());
487 #endif
488         if (!vertTabs) {
489             Qt::LayoutDirection ld = q->layoutDirection();
490             QRect arrows = QStyle::visualRect(ld, q->rect(),
491                                               QRect(available - extra, 0, extra, size.height()));
492             int buttonOverlap = q->style()->pixelMetric(QStyle::PM_TabBar_ScrollButtonOverlap, 0, q);
493
494             if (ld == Qt::LeftToRight) {
495 // In S60style, tab scroll buttons are layoutted separately, on the sides of the tabbar.
496 #ifndef QT_NO_STYLE_S60
497                 if (s60Style) {
498                     rightB->setGeometry(arrows.left() + extra / 2, arrows.top(), extra / 2, arrows.height());
499                     leftB->setGeometry(0, arrows.top(), extra / 2, arrows.height());
500                 } else {
501 #endif
502                 leftB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
503                 rightB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
504                                     extra/2, arrows.height());
505 #ifndef QT_NO_STYLE_S60
506                 }
507 #endif
508                 leftB->setArrowType(Qt::LeftArrow);
509                 rightB->setArrowType(Qt::RightArrow);
510             } else {
511 #ifndef QT_NO_STYLE_S60
512                 if (s60Style) {
513                     rightB->setGeometry(arrows.left() + extra / 2, arrows.top(), extra / 2, arrows.height());
514                     leftB->setGeometry(0, arrows.top(), extra / 2, arrows.height());
515                 } else {
516 #endif
517                 rightB->setGeometry(arrows.left(), arrows.top(), extra/2, arrows.height());
518                 leftB->setGeometry(arrows.right() - extra/2 + buttonOverlap, arrows.top(),
519                                     extra/2, arrows.height());
520 #ifndef QT_NO_STYLE_S60
521                 }
522 #endif
523                 rightB->setArrowType(Qt::LeftArrow);
524                 leftB->setArrowType(Qt::RightArrow);
525             }
526         } else {
527 #ifndef QT_NO_STYLE_S60
528             if (s60Style) {
529                 QRect arrows = QRect(0, 0, size.width(), available );
530                 leftB->setGeometry(arrows.left(), arrows.top(), arrows.width(), extra / 2);
531                 leftB->setArrowType(Qt::UpArrow);
532                 rightB->setGeometry(arrows.left(), arrows.bottom() - extra / 2 + 1,
533                                     arrows.width(), extra / 2);
534                 rightB->setArrowType(Qt::DownArrow);
535             } else {
536 #endif
537             QRect arrows = QRect(0, available - extra, size.width(), extra );
538             leftB->setGeometry(arrows.left(), arrows.top(), arrows.width(), extra/2);
539             leftB->setArrowType(Qt::UpArrow);
540             rightB->setGeometry(arrows.left(), arrows.bottom() - extra/2 + 1,
541                                 arrows.width(), extra/2);
542             rightB->setArrowType(Qt::DownArrow);
543 #ifndef QT_NO_STYLE_S60
544             }
545 #endif
546         }
547         leftB->setEnabled(scrollOffset > 0);
548         rightB->setEnabled(last - scrollOffset >= available - extra);
549         leftB->show();
550         rightB->show();
551     } else {
552         rightB->hide();
553         leftB->hide();
554     }
555
556     layoutWidgets();
557     q->tabLayoutChange();
558 }
559
560 void QTabBarPrivate::makeVisible(int index)
561 {
562     Q_Q(QTabBar);
563     if (!validIndex(index) || leftB->isHidden())
564         return;
565
566     const QRect tabRect = tabList.at(index).rect;
567     const int oldScrollOffset = scrollOffset;
568     const bool horiz = !verticalTabs(shape);
569     const int available = (horiz ? q->width() : q->height()) - extraWidth();
570     const int start = horiz ? tabRect.left() : tabRect.top();
571     const int end = horiz ? tabRect.right() : tabRect.bottom();
572     if (start < scrollOffset) // too far left
573         scrollOffset = start - (index ? 8 : 0);
574     else if (end > scrollOffset + available) // too far right
575         scrollOffset = end - available + 1;
576
577     leftB->setEnabled(scrollOffset > 0);
578     const int last = horiz ? tabList.last().rect.right() : tabList.last().rect.bottom();
579     rightB->setEnabled(last - scrollOffset >= available);
580     if (oldScrollOffset != scrollOffset) {
581         q->update();
582         layoutWidgets();
583     }
584 }
585
586 void QTabBarPrivate::layoutTab(int index)
587 {
588     Q_Q(QTabBar);
589     Q_ASSERT(index >= 0);
590
591     Tab &tab = tabList[index];
592     bool vertical = verticalTabs(shape);
593     if (!(tab.leftWidget || tab.rightWidget))
594         return;
595
596     QStyleOptionTabV3 opt;
597     q->initStyleOption(&opt, index);
598     if (tab.leftWidget) {
599         QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
600         QPoint p = rect.topLeft();
601         if ((index == pressedIndex) || paintWithOffsets) {
602             if (vertical)
603                 p.setY(p.y() + tabList[index].dragOffset);
604             else
605                 p.setX(p.x() + tabList[index].dragOffset);
606         }
607         tab.leftWidget->move(p);
608     }
609     if (tab.rightWidget) {
610         QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
611         QPoint p = rect.topLeft();
612         if ((index == pressedIndex) || paintWithOffsets) {
613             if (vertical)
614                 p.setY(p.y() + tab.dragOffset);
615             else
616                 p.setX(p.x() + tab.dragOffset);
617         }
618         tab.rightWidget->move(p);
619     }
620 }
621
622 void QTabBarPrivate::layoutWidgets(int index)
623 {
624     Q_Q(QTabBar);
625     int start = 0;
626     int end = q->count();
627     if (index != -1) {
628         start = qMax(index, 0);
629         end = qMin(end, start + 1);
630     }
631     for (int i = start; i < end; ++i) {
632         layoutTab(i);
633     }
634 }
635
636 void QTabBarPrivate::_q_closeTab()
637 {
638     Q_Q(QTabBar);
639     QObject *object = q->sender();
640     int tabToClose = -1;
641     QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, q);
642     for (int i = 0; i < tabList.count(); ++i) {
643         if (closeSide == QTabBar::LeftSide) {
644             if (tabList.at(i).leftWidget == object) {
645                 tabToClose = i;
646                 break;
647             }
648         } else {
649             if (tabList.at(i).rightWidget == object) {
650                 tabToClose = i;
651                 break;
652             }
653         }
654     }
655     if (tabToClose != -1)
656         emit q->tabCloseRequested(tabToClose);
657 }
658
659 void QTabBarPrivate::_q_scrollTabs()
660 {
661     Q_Q(QTabBar);
662     const QObject *sender = q->sender();
663     int i = -1;
664     if (!verticalTabs(shape)) {
665         if (sender == leftB) {
666             for (i = tabList.count() - 1; i >= 0; --i) {
667                 if (tabList.at(i).rect.left() - scrollOffset < 0) {
668                     makeVisible(i);
669                     return;
670                 }
671             }
672         } else if (sender == rightB) {
673             int availableWidth = q->width() - extraWidth();
674             for (i = 0; i < tabList.count(); ++i) {
675                 if (tabList.at(i).rect.right() - scrollOffset > availableWidth) {
676                     makeVisible(i);
677                     return;
678                 }
679             }
680         }
681     } else { // vertical
682         if (sender == leftB) {
683             for (i = tabList.count() - 1; i >= 0; --i) {
684                 if (tabList.at(i).rect.top() - scrollOffset < 0) {
685                     makeVisible(i);
686                     return;
687                 }
688             }
689         } else if (sender == rightB) {
690             int available = q->height() - extraWidth();
691             for (i = 0; i < tabList.count(); ++i) {
692                 if (tabList.at(i).rect.bottom() - scrollOffset > available) {
693                     makeVisible(i);
694                     return;
695                 }
696             }
697         }
698     }
699 }
700
701 void QTabBarPrivate::refresh()
702 {
703     Q_Q(QTabBar);
704
705     // be safe in case a subclass is also handling move with the tabs
706     if (pressedIndex != -1
707         && movable
708         && QApplication::mouseButtons() == Qt::NoButton) {
709         moveTabFinished(pressedIndex);
710         if (!validIndex(pressedIndex))
711             pressedIndex = -1;
712     }
713
714     if (!q->isVisible()) {
715         layoutDirty = true;
716     } else {
717         layoutTabs();
718         makeVisible(currentIndex);
719         q->update();
720     }
721     q->updateGeometry();
722 }
723
724 /*!
725     Creates a new tab bar with the given \a parent.
726 */
727 QTabBar::QTabBar(QWidget* parent)
728     :QWidget(*new QTabBarPrivate, parent, 0)
729 {
730     Q_D(QTabBar);
731     d->init();
732 }
733
734
735 /*!
736     Destroys the tab bar.
737 */
738 QTabBar::~QTabBar()
739 {
740 }
741
742 /*!
743     \property QTabBar::shape
744     \brief the shape of the tabs in the tab bar
745
746     Possible values for this property are described by the Shape enum.
747 */
748
749
750 QTabBar::Shape QTabBar::shape() const
751 {
752     Q_D(const QTabBar);
753     return d->shape;
754 }
755
756 void QTabBar::setShape(Shape shape)
757 {
758     Q_D(QTabBar);
759     if (d->shape == shape)
760         return;
761     d->shape = shape;
762     d->refresh();
763 }
764
765 /*!
766     \property QTabBar::drawBase
767     \brief defines whether or not tab bar should draw its base.
768
769     If true then QTabBar draws a base in relation to the styles overlab.
770     Otherwise only the tabs are drawn.
771
772     \sa QStyle::pixelMetric() QStyle::PM_TabBarBaseOverlap QStyleOptionTabBarBaseV2
773 */
774
775 void QTabBar::setDrawBase(bool drawBase)
776 {
777     Q_D(QTabBar);
778     if (d->drawBase == drawBase)
779         return;
780     d->drawBase = drawBase;
781     update();
782 }
783
784 bool QTabBar::drawBase() const
785 {
786     Q_D(const QTabBar);
787     return d->drawBase;
788 }
789
790 /*!
791     Adds a new tab with text \a text. Returns the new
792     tab's index.
793 */
794 int QTabBar::addTab(const QString &text)
795 {
796     return insertTab(-1, text);
797 }
798
799 /*!
800     \overload
801
802     Adds a new tab with icon \a icon and text \a
803     text. Returns the new tab's index.
804 */
805 int QTabBar::addTab(const QIcon& icon, const QString &text)
806 {
807     return insertTab(-1, icon, text);
808 }
809
810 /*!
811     Inserts a new tab with text \a text at position \a index. If \a
812     index is out of range, the new tab is appened. Returns the new
813     tab's index.
814 */
815 int QTabBar::insertTab(int index, const QString &text)
816 {
817     return insertTab(index, QIcon(), text);
818 }
819
820 /*!\overload
821
822     Inserts a new tab with icon \a icon and text \a text at position
823     \a index. If \a index is out of range, the new tab is
824     appended. Returns the new tab's index.
825
826     If the QTabBar was empty before this function is called, the inserted tab
827     becomes the current tab.
828
829     Inserting a new tab at an index less than or equal to the current index
830     will increment the current index, but keep the current tab.
831 */
832 int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
833 {
834     Q_D(QTabBar);
835     if (!d->validIndex(index)) {
836         index = d->tabList.count();
837         d->tabList.append(QTabBarPrivate::Tab(icon, text));
838     } else {
839         d->tabList.insert(index, QTabBarPrivate::Tab(icon, text));
840     }
841 #ifndef QT_NO_SHORTCUT
842     d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text));
843 #endif
844     d->refresh();
845     if (d->tabList.count() == 1)
846         setCurrentIndex(index);
847     else if (index <= d->currentIndex)
848         ++d->currentIndex;
849
850     if (d->closeButtonOnTabs) {
851         QStyleOptionTabV3 opt;
852         initStyleOption(&opt, index);
853         ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
854         QAbstractButton *closeButton = new CloseButton(this);
855         connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
856         setTabButton(index, closeSide, closeButton);
857     }
858
859     for (int i = 0; i < d->tabList.count(); ++i) {
860         if (d->tabList[i].lastTab >= index)
861             ++d->tabList[i].lastTab;
862     }
863
864     tabInserted(index);
865     return index;
866 }
867
868
869 /*!
870     Removes the tab at position \a index.
871
872     \sa SelectionBehavior
873  */
874 void QTabBar::removeTab(int index)
875 {
876     Q_D(QTabBar);
877     if (d->validIndex(index)) {
878 #ifndef QT_NO_SHORTCUT
879         releaseShortcut(d->tabList.at(index).shortcutId);
880 #endif
881         if (d->tabList[index].leftWidget) {
882             d->tabList[index].leftWidget->hide();
883             d->tabList[index].leftWidget->deleteLater();
884             d->tabList[index].leftWidget = 0;
885         }
886         if (d->tabList[index].rightWidget) {
887             d->tabList[index].rightWidget->hide();
888             d->tabList[index].rightWidget->deleteLater();
889             d->tabList[index].rightWidget = 0;
890         }
891
892         int newIndex = d->tabList[index].lastTab;
893         d->tabList.removeAt(index);
894         for (int i = 0; i < d->tabList.count(); ++i) {
895             if (d->tabList[i].lastTab == index)
896                 d->tabList[i].lastTab = -1;
897             if (d->tabList[i].lastTab > index)
898                 --d->tabList[i].lastTab;
899         }
900         if (index == d->currentIndex) {
901             // The current tab is going away, in order to make sure
902             // we emit that "current has changed", we need to reset this
903             // around.
904             d->currentIndex = -1;
905             if (d->tabList.size() > 0) {
906                 switch(d->selectionBehaviorOnRemove) {
907                 case SelectPreviousTab:
908                     if (newIndex > index)
909                         newIndex--;
910                     if (d->validIndex(newIndex))
911                         break;
912                     // else fallthrough
913                 case SelectRightTab:
914                     newIndex = index;
915                     if (newIndex >= d->tabList.size())
916                         newIndex = d->tabList.size() - 1;
917                     break;
918                 case SelectLeftTab:
919                     newIndex = index - 1;
920                     if (newIndex < 0)
921                         newIndex = 0;
922                     break;
923                 default:
924                     break;
925                 }
926
927                 if (d->validIndex(newIndex)) {
928                     // don't loose newIndex's old through setCurrentIndex
929                     int bump = d->tabList[newIndex].lastTab;
930                     setCurrentIndex(newIndex);
931                     d->tabList[newIndex].lastTab = bump;
932                 }
933             } else {
934                 emit currentChanged(-1);
935             }
936         } else if (index < d->currentIndex) {
937             setCurrentIndex(d->currentIndex - 1);
938         }
939         d->refresh();
940         tabRemoved(index);
941     }
942 }
943
944
945 /*!
946     Returns true if the tab at position \a index is enabled; otherwise
947     returns false.
948 */
949 bool QTabBar::isTabEnabled(int index) const
950 {
951     Q_D(const QTabBar);
952     if (const QTabBarPrivate::Tab *tab = d->at(index))
953         return tab->enabled;
954     return false;
955 }
956
957 /*!
958     If \a enabled is true then the tab at position \a index is
959     enabled; otherwise the item at position \a index is disabled.
960 */
961 void QTabBar::setTabEnabled(int index, bool enabled)
962 {
963     Q_D(QTabBar);
964     if (QTabBarPrivate::Tab *tab = d->at(index)) {
965         tab->enabled = enabled;
966 #ifndef QT_NO_SHORTCUT
967         setShortcutEnabled(tab->shortcutId, enabled);
968 #endif
969         update();
970         if (!enabled && index == d->currentIndex)
971             setCurrentIndex(d->validIndex(index+1)?index+1:0);
972         else if (enabled && !d->validIndex(d->currentIndex))
973             setCurrentIndex(index);
974     }
975 }
976
977
978 /*!
979     Returns the text of the tab at position \a index, or an empty
980     string if \a index is out of range.
981 */
982 QString QTabBar::tabText(int index) const
983 {
984     Q_D(const QTabBar);
985     if (const QTabBarPrivate::Tab *tab = d->at(index))
986         return tab->text;
987     return QString();
988 }
989
990 /*!
991     Sets the text of the tab at position \a index to \a text.
992 */
993 void QTabBar::setTabText(int index, const QString &text)
994 {
995     Q_D(QTabBar);
996     if (QTabBarPrivate::Tab *tab = d->at(index)) {
997         tab->text = text;
998 #ifndef QT_NO_SHORTCUT
999         releaseShortcut(tab->shortcutId);
1000         tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
1001         setShortcutEnabled(tab->shortcutId, tab->enabled);
1002 #endif
1003         d->refresh();
1004     }
1005 }
1006
1007 /*!
1008     Returns the text color of the tab with the given \a index, or a invalid
1009     color if \a index is out of range.
1010
1011     \sa setTabTextColor()
1012 */
1013 QColor QTabBar::tabTextColor(int index) const
1014 {
1015     Q_D(const QTabBar);
1016     if (const QTabBarPrivate::Tab *tab = d->at(index))
1017         return tab->textColor;
1018     return QColor();
1019 }
1020
1021 /*!
1022     Sets the color of the text in the tab with the given \a index to the specified \a color.
1023
1024     If an invalid color is specified, the tab will use the QTabBar foreground role instead.
1025
1026     \sa tabTextColor()
1027 */
1028 void QTabBar::setTabTextColor(int index, const QColor &color)
1029 {
1030     Q_D(QTabBar);
1031     if (QTabBarPrivate::Tab *tab = d->at(index)) {
1032         tab->textColor = color;
1033         update(tabRect(index));
1034     }
1035 }
1036
1037 /*!
1038     Returns the icon of the tab at position \a index, or a null icon
1039     if \a index is out of range.
1040 */
1041 QIcon QTabBar::tabIcon(int index) const
1042 {
1043     Q_D(const QTabBar);
1044     if (const QTabBarPrivate::Tab *tab = d->at(index))
1045         return tab->icon;
1046     return QIcon();
1047 }
1048
1049 /*!
1050     Sets the icon of the tab at position \a index to \a icon.
1051 */
1052 void QTabBar::setTabIcon(int index, const QIcon & icon)
1053 {
1054     Q_D(QTabBar);
1055     if (QTabBarPrivate::Tab *tab = d->at(index)) {
1056         bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1057         tab->icon = icon;
1058         if (simpleIconChange)
1059             update(tabRect(index));
1060         else
1061             d->refresh();
1062     }
1063 }
1064
1065 #ifndef QT_NO_TOOLTIP
1066 /*!
1067     Sets the tool tip of the tab at position \a index to \a tip.
1068 */
1069 void QTabBar::setTabToolTip(int index, const QString & tip)
1070 {
1071     Q_D(QTabBar);
1072     if (QTabBarPrivate::Tab *tab = d->at(index))
1073         tab->toolTip = tip;
1074 }
1075
1076 /*!
1077     Returns the tool tip of the tab at position \a index, or an empty
1078     string if \a index is out of range.
1079 */
1080 QString QTabBar::tabToolTip(int index) const
1081 {
1082     Q_D(const QTabBar);
1083     if (const QTabBarPrivate::Tab *tab = d->at(index))
1084         return tab->toolTip;
1085     return QString();
1086 }
1087 #endif // QT_NO_TOOLTIP
1088
1089 #ifndef QT_NO_WHATSTHIS
1090 /*!
1091     \since 4.1
1092
1093     Sets the What's This help text of the tab at position \a index
1094     to \a text.
1095 */
1096 void QTabBar::setTabWhatsThis(int index, const QString &text)
1097 {
1098     Q_D(QTabBar);
1099     if (QTabBarPrivate::Tab *tab = d->at(index))
1100         tab->whatsThis = text;
1101 }
1102
1103 /*!
1104     \since 4.1
1105
1106     Returns the What's This help text of the tab at position \a index,
1107     or an empty string if \a index is out of range.
1108 */
1109 QString QTabBar::tabWhatsThis(int index) const
1110 {
1111     Q_D(const QTabBar);
1112     if (const QTabBarPrivate::Tab *tab = d->at(index))
1113         return tab->whatsThis;
1114     return QString();
1115 }
1116
1117 #endif // QT_NO_WHATSTHIS
1118
1119 /*!
1120     Sets the data of the tab at position \a index to \a data.
1121 */
1122 void QTabBar::setTabData(int index, const QVariant & data)
1123 {
1124     Q_D(QTabBar);
1125     if (QTabBarPrivate::Tab *tab = d->at(index))
1126         tab->data = data;
1127 }
1128
1129 /*!
1130     Returns the data of the tab at position \a index, or a null
1131     variant if \a index is out of range.
1132 */
1133 QVariant QTabBar::tabData(int index) const
1134 {
1135     Q_D(const QTabBar);
1136     if (const QTabBarPrivate::Tab *tab = d->at(index))
1137         return tab->data;
1138     return QVariant();
1139 }
1140
1141 /*!
1142     Returns the visual rectangle of the tab at position \a
1143     index, or a null rectangle if \a index is out of range.
1144 */
1145 QRect QTabBar::tabRect(int index) const
1146 {
1147     Q_D(const QTabBar);
1148     if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1149         if (d->layoutDirty)
1150             const_cast<QTabBarPrivate*>(d)->layoutTabs();
1151         QRect r = tab->rect;
1152         if (verticalTabs(d->shape))
1153             r.translate(0, -d->scrollOffset);
1154         else
1155             r.translate(-d->scrollOffset, 0);
1156         if (!verticalTabs(d->shape))
1157             r = QStyle::visualRect(layoutDirection(), rect(), r);
1158         return r;
1159     }
1160     return QRect();
1161 }
1162
1163 /*!
1164     \since 4.3
1165     Returns the index of the tab that covers \a position or -1 if no
1166     tab covers \a position;
1167 */
1168
1169 int QTabBar::tabAt(const QPoint &position) const
1170 {
1171     Q_D(const QTabBar);
1172     if (d->validIndex(d->currentIndex)
1173         && tabRect(d->currentIndex).contains(position)) {
1174         return d->currentIndex;
1175     }
1176     const int max = d->tabList.size();
1177     for (int i = 0; i < max; ++i) {
1178         if (tabRect(i).contains(position)) {
1179             return i;
1180         }
1181     }
1182     return -1;
1183 }
1184
1185 /*!
1186     \property QTabBar::currentIndex
1187     \brief the index of the tab bar's visible tab
1188
1189     The current index is -1 if there is no current tab.
1190 */
1191
1192 int QTabBar::currentIndex() const
1193 {
1194     Q_D(const QTabBar);
1195     if (d->validIndex(d->currentIndex))
1196         return d->currentIndex;
1197     return -1;
1198 }
1199
1200
1201 void QTabBar::setCurrentIndex(int index)
1202 {
1203     Q_D(QTabBar);
1204     if (d->dragInProgress && d->pressedIndex != -1)
1205         return;
1206
1207     int oldIndex = d->currentIndex;
1208     if (d->validIndex(index) && d->currentIndex != index) {
1209         d->currentIndex = index;
1210         update();
1211         d->makeVisible(index);
1212         d->tabList[index].lastTab = oldIndex;
1213         d->layoutWidgets(oldIndex);
1214         d->layoutWidgets(index);
1215 #ifdef QT3_SUPPORT
1216         emit selected(index);
1217 #endif
1218         emit currentChanged(index);
1219     }
1220 }
1221
1222 /*!
1223     \property QTabBar::iconSize
1224     \brief The size for icons in the tab bar
1225     \since 4.1
1226
1227     The default value is style-dependent. \c iconSize is a maximum
1228     size; icons that are smaller are not scaled up.
1229
1230     \sa QTabWidget::iconSize
1231 */
1232 QSize QTabBar::iconSize() const
1233 {
1234     Q_D(const QTabBar);
1235     if (d->iconSize.isValid())
1236         return d->iconSize;
1237     int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, 0, this);
1238     return QSize(iconExtent, iconExtent);
1239
1240 }
1241
1242 void QTabBar::setIconSize(const QSize &size)
1243 {
1244     Q_D(QTabBar);
1245     d->iconSize = size;
1246     d->layoutDirty = true;
1247     update();
1248     updateGeometry();
1249 }
1250
1251 /*!
1252     \property QTabBar::count
1253     \brief the number of tabs in the tab bar
1254 */
1255
1256 int QTabBar::count() const
1257 {
1258     Q_D(const QTabBar);
1259     return d->tabList.count();
1260 }
1261
1262
1263 /*!\reimp
1264  */
1265 QSize QTabBar::sizeHint() const
1266 {
1267     Q_D(const QTabBar);
1268     if (d->layoutDirty)
1269         const_cast<QTabBarPrivate*>(d)->layoutTabs();
1270     QRect r;
1271     for (int i = 0; i < d->tabList.count(); ++i)
1272         r = r.united(d->tabList.at(i).maxRect);
1273     QSize sz = QApplication::globalStrut();
1274     return r.size().expandedTo(sz);
1275 }
1276
1277 /*!\reimp
1278  */
1279 QSize QTabBar::minimumSizeHint() const
1280 {
1281     Q_D(const QTabBar);
1282     if (!d->useScrollButtons) {
1283         QRect r;
1284         for (int i = 0; i < d->tabList.count(); ++i)
1285             r = r.united(d->tabList.at(i).minRect);
1286         return r.size().expandedTo(QApplication::globalStrut());
1287     }
1288     if (verticalTabs(d->shape))
1289         return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1290     else
1291         return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1292 }
1293
1294 static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
1295 {
1296     if (text.length() <= 7)
1297         return text;
1298
1299     static const QLatin1String Ellipses("...");
1300     QString ret;
1301     switch (mode) {
1302     case Qt::ElideRight:
1303         ret = text.left(4) + Ellipses;
1304         break;
1305     case Qt::ElideMiddle:
1306         ret = text.left(2) + Ellipses + text.right(2);
1307         break;
1308     case Qt::ElideLeft:
1309         ret = Ellipses + text.right(4);
1310         break;
1311     case Qt::ElideNone:
1312         ret = text;
1313         break;
1314     }
1315     return ret;
1316 }
1317
1318 QSize QTabBarPrivate::minimumTabSizeHint(int index)
1319 {
1320     Q_Q(QTabBar);
1321     // ### Qt 5: make this a protected virtual function in QTabBar
1322     Tab &tab = tabList[index];
1323     QString oldText = tab.text;
1324     tab.text = computeElidedText(elideMode, oldText);
1325     QSize size = q->tabSizeHint(index);
1326     tab.text = oldText;
1327     return size;
1328 }
1329
1330 /*!
1331     Returns the size hint for the tab at position \a index.
1332 */
1333 QSize QTabBar::tabSizeHint(int index) const
1334 {
1335     //Note: this must match with the computations in QCommonStylePrivate::tabLayout
1336     Q_D(const QTabBar);
1337     if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1338         QStyleOptionTabV3 opt;
1339         initStyleOption(&opt, index);
1340         opt.text = d->tabList.at(index).text;
1341         QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1342         int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
1343         int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt, this);
1344         const QFontMetrics fm = fontMetrics();
1345
1346         int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1347         int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1348
1349         int widgetWidth = 0;
1350         int widgetHeight = 0;
1351         int padding = 0;
1352         if (!opt.leftButtonSize.isEmpty()) {
1353             padding += 4;
1354             widgetWidth += opt.leftButtonSize.width();
1355             widgetHeight += opt.leftButtonSize.height();
1356         }
1357         if (!opt.rightButtonSize.isEmpty()) {
1358             padding += 4;
1359             widgetWidth += opt.rightButtonSize.width();
1360             widgetHeight += opt.rightButtonSize.height();
1361         }
1362         if (!opt.icon.isNull())
1363             padding += 4;
1364
1365         QSize csz;
1366         if (verticalTabs(d->shape)) {
1367             csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1368                     fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe + widgetHeight + padding);
1369         } else {
1370             csz = QSize(fm.size(Qt::TextShowMnemonic, tab->text).width() + iconSize.width() + hframe
1371                   + widgetWidth + padding,
1372                   qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1373         }
1374
1375         QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this);
1376         return retSize;
1377     }
1378     return QSize();
1379 }
1380
1381 /*!
1382   This virtual handler is called after a new tab was added or
1383   inserted at position \a index.
1384
1385   \sa tabRemoved()
1386  */
1387 void QTabBar::tabInserted(int index)
1388 {
1389     Q_UNUSED(index)
1390 }
1391
1392 /*!
1393   This virtual handler is called after a tab was removed from
1394   position \a index.
1395
1396   \sa tabInserted()
1397  */
1398 void QTabBar::tabRemoved(int index)
1399 {
1400     Q_UNUSED(index)
1401 }
1402
1403 /*!
1404   This virtual handler is called whenever the tab layout changes.
1405
1406   \sa tabRect()
1407  */
1408 void QTabBar::tabLayoutChange()
1409 {
1410 }
1411
1412
1413 /*!\reimp
1414  */
1415 void QTabBar::showEvent(QShowEvent *)
1416 {
1417     Q_D(QTabBar);
1418     if (d->layoutDirty)
1419         d->refresh();
1420     if (!d->validIndex(d->currentIndex))
1421         setCurrentIndex(0);
1422     d->updateMacBorderMetrics();
1423 }
1424
1425 /*!\reimp
1426  */
1427 void QTabBar::hideEvent(QHideEvent *)
1428 {
1429     Q_D(QTabBar);
1430     d->updateMacBorderMetrics();
1431 }
1432
1433 /*!\reimp
1434  */
1435 bool QTabBar::event(QEvent *event)
1436 {
1437     Q_D(QTabBar);
1438     if (event->type() == QEvent::HoverMove
1439         || event->type() == QEvent::HoverEnter) {
1440         QHoverEvent *he = static_cast<QHoverEvent *>(event);
1441         if (!d->hoverRect.contains(he->pos())) {
1442             QRect oldHoverRect = d->hoverRect;
1443             for (int i = 0; i < d->tabList.count(); ++i) {
1444                 QRect area = tabRect(i);
1445                 if (area.contains(he->pos())) {
1446                     d->hoverRect = area;
1447                     break;
1448                 }
1449             }
1450             if (he->oldPos() != QPoint(-1, -1))
1451                 update(oldHoverRect);
1452             update(d->hoverRect);
1453         }
1454         return true;
1455     } else if (event->type() == QEvent::HoverLeave ) {
1456         QRect oldHoverRect = d->hoverRect;
1457         d->hoverRect = QRect();
1458         update(oldHoverRect);
1459         return true;
1460 #ifndef QT_NO_TOOLTIP
1461     } else if (event->type() == QEvent::ToolTip) {
1462         if (const QTabBarPrivate::Tab *tab = d->at(tabAt(static_cast<QHelpEvent*>(event)->pos()))) {
1463             if (!tab->toolTip.isEmpty()) {
1464                 QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip, this);
1465                 return true;
1466             }
1467         }
1468 #endif // QT_NO_TOOLTIP
1469 #ifndef QT_NO_WHATSTHIS
1470     } else if (event->type() == QEvent::QueryWhatsThis) {
1471         const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()));
1472         if (!tab || tab->whatsThis.isEmpty())
1473             event->ignore();
1474         return true;
1475     } else if (event->type() == QEvent::WhatsThis) {
1476         if (const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()))) {
1477             if (!tab->whatsThis.isEmpty()) {
1478                 QWhatsThis::showText(static_cast<QHelpEvent*>(event)->globalPos(),
1479                                      tab->whatsThis, this);
1480                 return true;
1481             }
1482         }
1483 #endif // QT_NO_WHATSTHIS
1484 #ifndef QT_NO_SHORTCUT
1485     } else if (event->type() == QEvent::Shortcut) {
1486         QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
1487         for (int i = 0; i < d->tabList.count(); ++i) {
1488             const QTabBarPrivate::Tab *tab = &d->tabList.at(i);
1489             if (tab->shortcutId == se->shortcutId()) {
1490                 setCurrentIndex(i);
1491                 return true;
1492             }
1493         }
1494 #endif
1495     }
1496     return QWidget::event(event);
1497 }
1498
1499 /*!\reimp
1500  */
1501 void QTabBar::resizeEvent(QResizeEvent *)
1502 {
1503     Q_D(QTabBar);
1504     if (d->layoutDirty)
1505         updateGeometry();
1506     d->layoutTabs();
1507
1508     d->makeVisible(d->currentIndex);
1509 }
1510
1511 /*!\reimp
1512  */
1513 void QTabBar::paintEvent(QPaintEvent *)
1514 {
1515     Q_D(QTabBar);
1516
1517     QStyleOptionTabBarBaseV2 optTabBase;
1518     QTabBarPrivate::initStyleBaseOption(&optTabBase, this, size());
1519
1520     QStylePainter p(this);
1521     int selected = -1;
1522     int cut = -1;
1523     bool rtl = optTabBase.direction == Qt::RightToLeft;
1524     bool vertical = verticalTabs(d->shape);
1525     QStyleOptionTab cutTab;
1526     selected = d->currentIndex;
1527     if (d->dragInProgress)
1528         selected = d->pressedIndex;
1529
1530     for (int i = 0; i < d->tabList.count(); ++i)
1531          optTabBase.tabBarRect |= tabRect(i);
1532
1533     optTabBase.selectedTabRect = tabRect(selected);
1534
1535     if (d->drawBase)
1536         p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1537
1538     for (int i = 0; i < d->tabList.count(); ++i) {
1539         QStyleOptionTabV3 tab;
1540         initStyleOption(&tab, i);
1541         if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
1542             if (vertical) {
1543                 tab.rect.moveTop(tab.rect.y() + d->tabList[i].dragOffset);
1544             } else {
1545                 tab.rect.moveLeft(tab.rect.x() + d->tabList[i].dragOffset);
1546             }
1547         }
1548         if (!(tab.state & QStyle::State_Enabled)) {
1549             tab.palette.setCurrentColorGroup(QPalette::Disabled);
1550         }
1551         // If this tab is partially obscured, make a note of it so that we can pass the information
1552         // along when we draw the tear.
1553         if (((!vertical && (!rtl && tab.rect.left() < 0)) || (rtl && tab.rect.right() > width()))
1554             || (vertical && tab.rect.top() < 0)) {
1555             cut = i;
1556             cutTab = tab;
1557         }
1558         // Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
1559         if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
1560             || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
1561             continue;
1562
1563         optTabBase.tabBarRect |= tab.rect;
1564         if (i == selected)
1565             continue;
1566
1567         p.drawControl(QStyle::CE_TabBarTab, tab);
1568     }
1569
1570     // Draw the selected tab last to get it "on top"
1571     if (selected >= 0) {
1572         QStyleOptionTabV3 tab;
1573         initStyleOption(&tab, selected);
1574         if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
1575             if (vertical)
1576                 tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
1577             else
1578                 tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
1579         }
1580         if (!d->dragInProgress)
1581             p.drawControl(QStyle::CE_TabBarTab, tab);
1582         else {
1583             int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0, this);
1584             d->movingTab->setGeometry(tab.rect.adjusted(-taboverlap, 0, taboverlap, 0));
1585         }
1586     }
1587
1588     // Only draw the tear indicator if necessary. Most of the time we don't need too.
1589     if (d->leftB->isVisible() && cut >= 0) {
1590         cutTab.rect = rect();
1591         cutTab.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicator, &cutTab, this);
1592         p.drawPrimitive(QStyle::PE_IndicatorTabTear, cutTab);
1593     }
1594 }
1595
1596 /*
1597     Given that index at position from moved to position to where return where index goes.
1598  */
1599 int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const
1600 {
1601     if (index == from)
1602         return to;
1603
1604     int start = qMin(from, to);
1605     int end = qMax(from, to);
1606     if (index >= start && index <= end)
1607         index += (from < to) ? -1 : 1;
1608     return index;
1609 }
1610
1611 /*!
1612     Moves the item at index position \a from to index position \a to.
1613     \since 4.5
1614
1615     \sa tabMoved(), tabLayoutChange()
1616  */
1617 void QTabBar::moveTab(int from, int to)
1618 {
1619     Q_D(QTabBar);
1620     if (from == to
1621         || !d->validIndex(from)
1622         || !d->validIndex(to))
1623         return;
1624
1625     bool vertical = verticalTabs(d->shape);
1626     int oldPressedPosition = 0;
1627     if (d->pressedIndex != -1) {
1628         // Record the position of the pressed tab before reordering the tabs.
1629         oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
1630                              : d->tabList[d->pressedIndex].rect.x();
1631     }
1632
1633     // Update the locations of the tabs first
1634     int start = qMin(from, to);
1635     int end = qMax(from, to);
1636     int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
1637     if (from < to)
1638         width *= -1;
1639     bool rtl = isRightToLeft();
1640     for (int i = start; i <= end; ++i) {
1641         if (i == from)
1642             continue;
1643         if (vertical)
1644             d->tabList[i].rect.moveTop(d->tabList[i].rect.y() + width);
1645         else
1646             d->tabList[i].rect.moveLeft(d->tabList[i].rect.x() + width);
1647         int direction = -1;
1648         if (rtl && !vertical)
1649             direction *= -1;
1650         if (d->tabList[i].dragOffset != 0)
1651             d->tabList[i].dragOffset += (direction * width);
1652     }
1653
1654     if (vertical) {
1655         if (from < to)
1656             d->tabList[from].rect.moveTop(d->tabList[to].rect.bottom() + 1);
1657         else
1658             d->tabList[from].rect.moveTop(d->tabList[to].rect.top() - width);
1659     } else {
1660         if (from < to)
1661             d->tabList[from].rect.moveLeft(d->tabList[to].rect.right() + 1);
1662         else
1663             d->tabList[from].rect.moveLeft(d->tabList[to].rect.left() - width);
1664     }
1665
1666     // Move the actual data structures
1667     d->tabList.move(from, to);
1668
1669     // update lastTab locations
1670     for (int i = 0; i < d->tabList.count(); ++i)
1671         d->tabList[i].lastTab = d->calculateNewPosition(from, to, d->tabList[i].lastTab);
1672
1673     // update external variables
1674     d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
1675
1676     // If we are in the middle of a drag update the dragStartPosition
1677     if (d->pressedIndex != -1) {
1678         d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
1679         int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
1680         int diff = oldPressedPosition - newPressedPosition;
1681         if (isRightToLeft() && !vertical)
1682             diff *= -1;
1683         if (vertical)
1684             d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
1685         else
1686             d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
1687     }
1688
1689     d->layoutWidgets(start);
1690     update();
1691     emit tabMoved(from, to);
1692     emit tabLayoutChange();
1693 }
1694
1695 void QTabBarPrivate::slide(int from, int to)
1696 {
1697     Q_Q(QTabBar);
1698     if (from == to
1699             || !validIndex(from)
1700             || !validIndex(to))
1701         return;
1702     bool vertical = verticalTabs(shape);
1703     int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
1704     q->setUpdatesEnabled(false);
1705     q->moveTab(from, to);
1706     q->setUpdatesEnabled(true);
1707     int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
1708     int length = postLocation - preLocation;
1709     tabList[to].dragOffset -= length;
1710     tabList[to].startAnimation(this, ANIMATION_DURATION);
1711 }
1712
1713 void QTabBarPrivate::moveTab(int index, int offset)
1714 {
1715     if (!validIndex(index))
1716         return;
1717     tabList[index].dragOffset = offset;
1718     layoutTab(index); // Make buttons follow tab
1719     q_func()->update();
1720 }
1721
1722 /*!\reimp
1723 */
1724 void QTabBar::mousePressEvent(QMouseEvent *event)
1725 {
1726     Q_D(QTabBar);
1727     if (event->button() != Qt::LeftButton) {
1728         event->ignore();
1729         return;
1730     }
1731     // Be safe!
1732     if (d->pressedIndex != -1 && d->movable)
1733         d->moveTabFinished(d->pressedIndex);
1734
1735     d->pressedIndex = d->indexAtPos(event->pos());
1736 #ifdef Q_WS_MAC
1737     d->previousPressedIndex = d->pressedIndex;
1738 #endif
1739     if (d->validIndex(d->pressedIndex)) {
1740         QStyleOptionTabBarBaseV2 optTabBase;
1741         optTabBase.init(this);
1742         optTabBase.documentMode = d->documentMode;
1743         if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this))
1744             setCurrentIndex(d->pressedIndex);
1745         else
1746             repaint(tabRect(d->pressedIndex));
1747         if (d->movable) {
1748             d->dragStartPosition = event->pos();
1749         }
1750     }
1751 }
1752
1753 /*!\reimp
1754  */
1755 void QTabBar::mouseMoveEvent(QMouseEvent *event)
1756 {
1757     Q_D(QTabBar);
1758     if (d->movable) {
1759         // Be safe!
1760         if (d->pressedIndex != -1
1761             && event->buttons() == Qt::NoButton)
1762             d->moveTabFinished(d->pressedIndex);
1763         
1764         // Start drag
1765         if (!d->dragInProgress && d->pressedIndex != -1) {
1766             if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
1767                 d->dragInProgress = true;
1768                 d->setupMovableTab();
1769             }
1770         }
1771
1772         int offset = (event->pos() - d->dragStartPosition).manhattanLength();
1773         if (event->buttons() == Qt::LeftButton
1774             && offset > QApplication::startDragDistance()
1775             && d->validIndex(d->pressedIndex)) {
1776             bool vertical = verticalTabs(d->shape);
1777             int dragDistance;
1778             if (vertical) {
1779                 dragDistance = (event->pos().y() - d->dragStartPosition.y());
1780             } else {
1781                 dragDistance = (event->pos().x() - d->dragStartPosition.x());
1782             }
1783             d->tabList[d->pressedIndex].dragOffset = dragDistance;
1784
1785             QRect startingRect = tabRect(d->pressedIndex);
1786             if (vertical)
1787                 startingRect.moveTop(startingRect.y() + dragDistance);
1788             else
1789                 startingRect.moveLeft(startingRect.x() + dragDistance);
1790
1791             int overIndex;
1792             if (dragDistance < 0)
1793                 overIndex = tabAt(startingRect.topLeft());
1794             else
1795                 overIndex = tabAt(startingRect.topRight());
1796
1797             if (overIndex != d->pressedIndex && overIndex != -1) {
1798                 int offset = 1;
1799                 if (isRightToLeft() && !vertical)
1800                     offset *= -1;
1801                 if (dragDistance < 0) {
1802                     dragDistance *= -1;
1803                     offset *= -1;
1804                 }
1805                 for (int i = d->pressedIndex;
1806                      offset > 0 ? i < overIndex : i > overIndex;
1807                      i += offset) {
1808                     QRect overIndexRect = tabRect(overIndex);
1809                     int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
1810                     if (dragDistance > needsToBeOver)
1811                         d->slide(i + offset, d->pressedIndex);
1812                 }
1813             }
1814             // Buttons needs to follow the dragged tab
1815             d->layoutTab(d->pressedIndex);
1816
1817             update();
1818         }
1819 #ifdef Q_WS_MAC
1820     } else if (!d->documentMode && event->buttons() == Qt::LeftButton && d->previousPressedIndex != -1) {
1821         int newPressedIndex = d->indexAtPos(event->pos());
1822         if (d->pressedIndex == -1 && d->previousPressedIndex == newPressedIndex) {
1823             d->pressedIndex = d->previousPressedIndex;
1824             update(tabRect(d->pressedIndex));
1825         } else if(d->pressedIndex != newPressedIndex) {
1826             d->pressedIndex = -1;
1827             update(tabRect(d->previousPressedIndex));
1828         }
1829 #endif
1830     }
1831
1832     if (event->buttons() != Qt::LeftButton) {
1833         event->ignore();
1834         return;
1835     }
1836     QStyleOptionTabBarBaseV2 optTabBase;
1837     optTabBase.init(this);
1838     optTabBase.documentMode = d->documentMode;
1839 }
1840
1841 void QTabBarPrivate::setupMovableTab()
1842 {
1843     Q_Q(QTabBar);
1844     if (!movingTab)
1845         movingTab = new QWidget(q);
1846
1847     int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap, 0 ,q);
1848     QRect grabRect = q->tabRect(pressedIndex);
1849     grabRect.adjust(-taboverlap, 0, taboverlap, 0);
1850
1851     QPixmap grabImage(grabRect.size());
1852     grabImage.fill(Qt::transparent);
1853     QStylePainter p(&grabImage, q);
1854     p.initFrom(q);
1855
1856     QStyleOptionTabV3 tab;
1857     q->initStyleOption(&tab, pressedIndex);
1858     tab.rect.moveTopLeft(QPoint(taboverlap, 0));
1859     p.drawControl(QStyle::CE_TabBarTab, tab);
1860     p.end();
1861
1862     QPalette pal;
1863     pal.setBrush(QPalette::All, QPalette::Window, grabImage);
1864     movingTab->setPalette(pal);
1865     movingTab->setGeometry(grabRect);
1866     movingTab->setAutoFillBackground(true);
1867     movingTab->raise();
1868
1869     // Re-arrange widget order to avoid overlaps
1870     if (tabList[pressedIndex].leftWidget)
1871         tabList[pressedIndex].leftWidget->raise();
1872     if (tabList[pressedIndex].rightWidget)
1873         tabList[pressedIndex].rightWidget->raise();
1874     if (leftB)
1875         leftB->raise();
1876     if (rightB)
1877         rightB->raise();
1878     movingTab->setVisible(true);
1879 }
1880
1881 void QTabBarPrivate::moveTabFinished(int index)
1882 {
1883     Q_Q(QTabBar);
1884     bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
1885     bool allAnimationsFinished = true;
1886 #ifndef QT_NO_ANIMATION
1887     for(int i = 0; allAnimationsFinished && i < tabList.count(); ++i) {
1888         const Tab &t = tabList.at(i);
1889         if (t.animation && t.animation->state() == QAbstractAnimation::Running)
1890             allAnimationsFinished = false;
1891     }
1892 #endif //QT_NO_ANIMATION
1893     if (allAnimationsFinished && cleanup) {
1894         if(movingTab)
1895             movingTab->setVisible(false); // We might not get a mouse release
1896         for (int i = 0; i < tabList.count(); ++i) {
1897             tabList[i].dragOffset = 0;
1898         }
1899         if (pressedIndex != -1 && movable) {
1900             pressedIndex = -1;
1901             dragInProgress = false;
1902             dragStartPosition = QPoint();
1903         }
1904         layoutWidgets();
1905     } else {
1906         if (!validIndex(index))
1907             return;
1908         tabList[index].dragOffset = 0;
1909     }
1910     q->update();
1911 }
1912
1913 /*!\reimp
1914 */
1915 void QTabBar::mouseReleaseEvent(QMouseEvent *event)
1916 {
1917     Q_D(QTabBar);
1918     if (event->button() != Qt::LeftButton) {
1919         event->ignore();
1920         return;
1921     }
1922 #ifdef Q_WS_MAC
1923     d->previousPressedIndex = -1;
1924 #endif
1925     if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
1926         int length = d->tabList[d->pressedIndex].dragOffset;
1927         int width = verticalTabs(d->shape)
1928             ? tabRect(d->pressedIndex).height()
1929             : tabRect(d->pressedIndex).width();
1930         int duration = qMin(ANIMATION_DURATION,
1931                 (qAbs(length) * ANIMATION_DURATION) / width);
1932         d->tabList[d->pressedIndex].startAnimation(d, duration);
1933         d->dragInProgress = false;
1934         d->movingTab->setVisible(false);
1935         d->dragStartPosition = QPoint();
1936     }
1937
1938     int i = d->indexAtPos(event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
1939     d->pressedIndex = -1;
1940     QStyleOptionTabBarBaseV2 optTabBase;
1941     optTabBase.initFrom(this);
1942     optTabBase.documentMode = d->documentMode;
1943     if (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this) == QEvent::MouseButtonRelease)
1944         setCurrentIndex(i);
1945 }
1946
1947 /*!\reimp
1948  */
1949 void QTabBar::keyPressEvent(QKeyEvent *event)
1950 {
1951     Q_D(QTabBar);
1952     if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
1953         event->ignore();
1954         return;
1955     }
1956     int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;    
1957     d->setCurrentNextEnabledIndex(offset);
1958 }
1959
1960 /*!\reimp
1961  */
1962 #ifndef QT_NO_WHEELEVENT
1963 void QTabBar::wheelEvent(QWheelEvent *event)
1964 {
1965     Q_D(QTabBar);
1966     int offset = event->delta() > 0 ? -1 : 1;
1967     d->setCurrentNextEnabledIndex(offset);
1968     QWidget::wheelEvent(event);
1969 }
1970 #endif //QT_NO_WHEELEVENT
1971
1972 void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
1973 {
1974     Q_Q(QTabBar);
1975     for (int index = currentIndex + offset; validIndex(index); index += offset) {
1976         if (tabList.at(index).enabled) {
1977             q->setCurrentIndex(index);
1978             break;
1979         }
1980     }
1981 }
1982
1983 /*!\reimp
1984  */
1985 void QTabBar::changeEvent(QEvent *event)
1986 {
1987     Q_D(QTabBar);
1988     if (event->type() == QEvent::StyleChange) {
1989         d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this));
1990         if (!d->useScrollButtonsSetByUser)
1991             d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this);
1992         d->refresh();
1993     } else if (event->type() == QEvent::FontChange) {
1994         d->refresh();
1995     }
1996     QWidget::changeEvent(event);
1997 }
1998
1999 /*!
2000     \property QTabBar::elideMode
2001     \brief how to elide text in the tab bar
2002     \since 4.2
2003
2004     This property controls how items are elided when there is not
2005     enough space to show them for a given tab bar size.
2006
2007     By default the value is style dependent.
2008
2009     \sa QTabWidget::elideMode usesScrollButtons QStyle::SH_TabBar_ElideMode
2010 */
2011
2012 Qt::TextElideMode QTabBar::elideMode() const
2013 {
2014     Q_D(const QTabBar);
2015     return d->elideMode;
2016 }
2017
2018 void QTabBar::setElideMode(Qt::TextElideMode mode)
2019 {
2020     Q_D(QTabBar);
2021     d->elideMode = mode;
2022     d->refresh();
2023 }
2024
2025 /*!
2026     \property QTabBar::usesScrollButtons
2027     \brief Whether or not a tab bar should use buttons to scroll tabs when it
2028     has many tabs.
2029     \since 4.2
2030
2031     When there are too many tabs in a tab bar for its size, the tab bar can either choose
2032     to expand its size or to add buttons that allow you to scroll through the tabs.
2033
2034     By default the value is style dependant.
2035
2036     \sa elideMode QTabWidget::usesScrollButtons QStyle::SH_TabBar_PreferNoArrows
2037 */
2038 bool QTabBar::usesScrollButtons() const
2039 {
2040     return d_func()->useScrollButtons;
2041 }
2042
2043 void QTabBar::setUsesScrollButtons(bool useButtons)
2044 {
2045     Q_D(QTabBar);
2046     d->useScrollButtonsSetByUser = true;
2047     if (d->useScrollButtons == useButtons)
2048         return;
2049     d->useScrollButtons = useButtons;
2050     d->refresh();
2051 }
2052
2053 /*!
2054     \fn void QTabBar::setCurrentTab(int index)
2055
2056     Use setCurrentIndex() instead.
2057 */
2058
2059 /*!
2060     \fn void QTabBar::selected(int index);
2061
2062     Use currentChanged() instead.
2063 */
2064
2065
2066 /*!
2067     \property QTabBar::tabsClosable
2068     \brief Whether or not a tab bar should place close buttons on each tab
2069     \since 4.5
2070
2071     When tabsClosable is set to true a close button will appear on the tab on
2072     either the left or right hand side depending upon the style.  When the button
2073     is clicked the tab the signal tabCloseRequested will be emitted.
2074
2075     By default the value is false.
2076
2077     \sa setTabButton(), tabRemoved()
2078 */
2079
2080 bool QTabBar::tabsClosable() const
2081 {
2082     Q_D(const QTabBar);
2083     return d->closeButtonOnTabs;
2084 }
2085
2086 void QTabBar::setTabsClosable(bool closable)
2087 {
2088     Q_D(QTabBar);
2089     if (d->closeButtonOnTabs == closable)
2090         return;
2091     d->closeButtonOnTabs = closable;
2092     ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
2093     if (!closable) {
2094         for (int i = 0; i < d->tabList.count(); ++i) {
2095             if (closeSide == LeftSide && d->tabList[i].leftWidget) {
2096                 d->tabList[i].leftWidget->deleteLater();
2097                 d->tabList[i].leftWidget = 0;
2098             }
2099             if (closeSide == RightSide && d->tabList[i].rightWidget) {
2100                 d->tabList[i].rightWidget->deleteLater();
2101                 d->tabList[i].rightWidget = 0;
2102             }
2103         }
2104     } else {
2105         bool newButtons = false;
2106         for (int i = 0; i < d->tabList.count(); ++i) {
2107             if (tabButton(i, closeSide))
2108                 continue;
2109             newButtons = true;
2110             QAbstractButton *closeButton = new CloseButton(this);
2111             connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
2112             setTabButton(i, closeSide, closeButton);
2113         }
2114         if (newButtons)
2115             d->layoutTabs();
2116     }
2117     update();
2118 }
2119
2120 /*!
2121     \enum QTabBar::ButtonPosition
2122     \since 4.5
2123
2124     This enum type lists the location of the widget on a tab.
2125
2126     \value LeftSide Left side of the tab.
2127
2128     \value RightSide Right side of the tab.
2129
2130 */
2131
2132 /*!
2133     \enum QTabBar::SelectionBehavior
2134     \since 4.5
2135
2136     This enum type lists the behavior of QTabBar when a tab is removed
2137     and the tab being removed is also the current tab.
2138
2139     \value SelectLeftTab  Select the tab to the left of the one being removed.
2140
2141     \value SelectRightTab  Select the tab to the right of the one being removed.
2142
2143     \value SelectPreviousTab  Select the previously selected tab.
2144
2145 */
2146
2147 /*!
2148     \property QTabBar::selectionBehaviorOnRemove
2149     \brief What tab should be set as current when removeTab is called if
2150     the removed tab is also the current tab.
2151     \since 4.5
2152
2153     By default the value is SelectRightTab.
2154
2155     \sa removeTab()
2156 */
2157
2158
2159 QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove() const
2160 {
2161     Q_D(const QTabBar);
2162     return d->selectionBehaviorOnRemove;
2163 }
2164
2165 void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2166 {
2167     Q_D(QTabBar);
2168     d->selectionBehaviorOnRemove = behavior;
2169 }
2170
2171 /*!
2172     \property QTabBar::expanding
2173     \brief When expanding is true QTabBar will expand the tabs to use the empty space.
2174     \since 4.5
2175
2176     By default the value is true.
2177
2178     \sa QTabWidget::documentMode
2179 */
2180
2181 bool QTabBar::expanding() const
2182 {
2183     Q_D(const QTabBar);
2184     return d->expanding;
2185 }
2186
2187 void QTabBar::setExpanding(bool enabled)
2188 {
2189     Q_D(QTabBar);
2190     if (d->expanding == enabled)
2191         return;
2192     d->expanding = enabled;
2193     d->layoutTabs();
2194 }
2195
2196 /*!
2197     \property QTabBar::movable
2198     \brief This property holds whether the user can move the tabs
2199     within the tabbar area.
2200
2201     \since 4.5
2202
2203     By default, this property is false;
2204 */
2205
2206 bool QTabBar::isMovable() const
2207 {
2208     Q_D(const QTabBar);
2209     return d->movable;
2210 }
2211
2212 void QTabBar::setMovable(bool movable)
2213 {
2214     Q_D(QTabBar);
2215     d->movable = movable;
2216 }
2217
2218
2219 /*!
2220     \property QTabBar::documentMode
2221     \brief Whether or not the tab bar is rendered in a mode suitable for the main window.
2222     \since 4.5
2223
2224     This property is used as a hint for styles to draw the tabs in a different
2225     way then they would normally look in a tab widget.  On Mac OS X this will
2226     look similar to the tabs in Safari or Leopard's Terminal.app.
2227
2228     \sa QTabWidget::documentMode
2229 */
2230 bool QTabBar::documentMode() const
2231 {
2232     return d_func()->documentMode;
2233 }
2234
2235 void QTabBar::setDocumentMode(bool enabled)
2236 {
2237     Q_D(QTabBar);
2238     d->documentMode = enabled;
2239     d->updateMacBorderMetrics();
2240 }
2241
2242 /*!
2243     Sets \a widget on the tab \a index.  The widget is placed
2244     on the left or right hand side depending upon the \a position.
2245     \since 4.5
2246
2247     Any previously set widget in \a position is hidden.
2248
2249     The tab bar will take ownership of the widget and so all widgets set here
2250     will be deleted by the tab bar when it is destroyed unless you separately
2251     reparent the widget after setting some other widget (or 0).
2252
2253     \sa tabsClosable()
2254   */
2255 void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
2256 {
2257     Q_D(QTabBar);
2258     if (index < 0 || index >= d->tabList.count())
2259         return;
2260     if (widget) {
2261         widget->setParent(this);
2262         // make sure our left and right widgets stay on top
2263         widget->lower();
2264         widget->show();
2265     }
2266     if (position == LeftSide) {
2267         if (d->tabList[index].leftWidget)
2268             d->tabList[index].leftWidget->hide();
2269         d->tabList[index].leftWidget = widget;
2270     } else {
2271         if (d->tabList[index].rightWidget)
2272             d->tabList[index].rightWidget->hide();
2273         d->tabList[index].rightWidget = widget;
2274     }
2275     d->layoutTabs();
2276     d->refresh();
2277     update();
2278 }
2279
2280 /*!
2281     Returns the widget set a tab \a index and \a position or 0 if
2282     one is not set.
2283   */
2284 QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
2285 {
2286     Q_D(const QTabBar);
2287     if (index < 0 || index >= d->tabList.count())
2288         return 0;
2289     if (position == LeftSide)
2290         return d->tabList.at(index).leftWidget;
2291     else
2292         return d->tabList.at(index).rightWidget;
2293 }
2294
2295 CloseButton::CloseButton(QWidget *parent)
2296     : QAbstractButton(parent)
2297 {
2298     setFocusPolicy(Qt::NoFocus);
2299 #ifndef QT_NO_CURSOR
2300     setCursor(Qt::ArrowCursor);
2301 #endif
2302 #ifndef QT_NO_TOOLTIP
2303     setToolTip(tr("Close Tab"));
2304 #endif
2305     resize(sizeHint());
2306 }
2307
2308 QSize CloseButton::sizeHint() const
2309 {
2310     ensurePolished();
2311     int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this);
2312     int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this);
2313     return QSize(width, height);
2314 }
2315
2316 void CloseButton::enterEvent(QEvent *event)
2317 {
2318     if (isEnabled())
2319         update();
2320     QAbstractButton::enterEvent(event);
2321 }
2322
2323 void CloseButton::leaveEvent(QEvent *event)
2324 {
2325     if (isEnabled())
2326         update();
2327     QAbstractButton::leaveEvent(event);
2328 }
2329
2330 void CloseButton::paintEvent(QPaintEvent *)
2331 {
2332     QPainter p(this);
2333     QStyleOption opt;
2334     opt.init(this);
2335     opt.state |= QStyle::State_AutoRaise;
2336     if (isEnabled() && underMouse() && !isChecked() && !isDown())
2337         opt.state |= QStyle::State_Raised;
2338     if (isChecked())
2339         opt.state |= QStyle::State_On;
2340     if (isDown())
2341         opt.state |= QStyle::State_Sunken;
2342
2343     if (const QTabBar *tb = qobject_cast<const QTabBar *>(parent())) {
2344         int index = tb->currentIndex();
2345         QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb);
2346         if (tb->tabButton(index, position) == this)
2347             opt.state |= QStyle::State_Selected;
2348     }
2349
2350     style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this);
2351 }
2352
2353 QT_END_NAMESPACE
2354
2355 #include "moc_qtabbar.cpp"
2356
2357 #endif // QT_NO_TABBAR