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

Last change on this file since 775 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

File size: 70.8 KB
Line 
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
74QT_BEGIN_NAMESPACE
75
76inline 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
84void 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*/
118void 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
329int 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
336void 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
360QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
361{
362 return validIndex(index)?&tabList[index]:0;
363}
364
365const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
366{
367 return validIndex(index)?&tabList[index]:0;
368}
369
370int 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
381void 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
560void 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
586void 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
622void 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
636void 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
659void 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
701void 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 q->updateGeometry();
721 }
722}
723
724/*!
725 Creates a new tab bar with the given \a parent.
726*/
727QTabBar::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*/
738QTabBar::~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
750QTabBar::Shape QTabBar::shape() const
751{
752 Q_D(const QTabBar);
753 return d->shape;
754}
755
756void 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
775void QTabBar::setDrawBase(bool drawBase)
776{
777 Q_D(QTabBar);
778 if (d->drawBase == drawBase)
779 return;
780 d->drawBase = drawBase;
781 update();
782}
783
784bool 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*/
794int 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*/
805int 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*/
815int 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*/
832int 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 */
874void 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*/
949bool 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*/
961void 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*/
982QString 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*/
993void 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*/
1013QColor 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*/
1028void 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*/
1041QIcon 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*/
1052void 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*/
1069void 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*/
1080QString 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*/
1096void 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*/
1109QString 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*/
1122void 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*/
1133QVariant 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*/
1145QRect 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
1169int 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
1192int 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
1201void 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*/
1232QSize 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
1242void 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
1256int QTabBar::count() const
1257{
1258 Q_D(const QTabBar);
1259 return d->tabList.count();
1260}
1261
1262
1263/*!\reimp
1264 */
1265QSize 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 */
1279QSize 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
1294static 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
1318QSize 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*/
1333QSize 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 */
1387void 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 */
1398void 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 */
1408void QTabBar::tabLayoutChange()
1409{
1410}
1411
1412
1413/*!\reimp
1414 */
1415void 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 */
1427void QTabBar::hideEvent(QHideEvent *)
1428{
1429 Q_D(QTabBar);
1430 d->updateMacBorderMetrics();
1431}
1432
1433/*!\reimp
1434 */
1435bool 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 */
1501void 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 */
1513void 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 */
1599int 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 */
1617void 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
1695void 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
1713void 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*/
1724void 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 */
1755void 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
1841void 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
1881void 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*/
1915void 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 */
1949void 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
1963void 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
1972void 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 */
1985void 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
2012Qt::TextElideMode QTabBar::elideMode() const
2013{
2014 Q_D(const QTabBar);
2015 return d->elideMode;
2016}
2017
2018void 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*/
2038bool QTabBar::usesScrollButtons() const
2039{
2040 return d_func()->useScrollButtons;
2041}
2042
2043void 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
2080bool QTabBar::tabsClosable() const
2081{
2082 Q_D(const QTabBar);
2083 return d->closeButtonOnTabs;
2084}
2085
2086void 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
2159QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove() const
2160{
2161 Q_D(const QTabBar);
2162 return d->selectionBehaviorOnRemove;
2163}
2164
2165void 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
2181bool QTabBar::expanding() const
2182{
2183 Q_D(const QTabBar);
2184 return d->expanding;
2185}
2186
2187void 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
2206bool QTabBar::isMovable() const
2207{
2208 Q_D(const QTabBar);
2209 return d->movable;
2210}
2211
2212void 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*/
2230bool QTabBar::documentMode() const
2231{
2232 return d_func()->documentMode;
2233}
2234
2235void 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 */
2255void 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 */
2284QWidget *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
2295CloseButton::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
2308QSize 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
2316void CloseButton::enterEvent(QEvent *event)
2317{
2318 if (isEnabled())
2319 update();
2320 QAbstractButton::enterEvent(event);
2321}
2322
2323void CloseButton::leaveEvent(QEvent *event)
2324{
2325 if (isEnabled())
2326 update();
2327 QAbstractButton::leaveEvent(event);
2328}
2329
2330void 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
2353QT_END_NAMESPACE
2354
2355#include "moc_qtabbar.cpp"
2356
2357#endif // QT_NO_TABBAR
Note: See TracBrowser for help on using the repository browser.