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

Last change on this file since 277 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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