source: trunk/src/gui/widgets/qtabbar.cpp@ 877

Last change on this file since 877 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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