1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "crumblepath.h"
34 #include "stylehelper.h"
36 #include <QtCore/QList>
37 #include <QtGui/QHBoxLayout>
38 #include <QtGui/QPushButton>
39 #include <QtGui/QMenu>
40 #include <QtGui/QStyle>
41 #include <QtGui/QResizeEvent>
42 #include <QtGui/QPainter>
43 #include <QtGui/QImage>
45 #include <qtcassert.h>
49 static const int ArrowBorderSize = 12;
51 class CrumblePathButton : public QPushButton
60 explicit CrumblePathButton(const QString &title, QWidget *parent = 0);
61 void setSegmentType(int type);
63 void setData(const QVariant &data);
64 QVariant data() const;
67 void paintEvent(QPaintEvent *);
68 void mouseMoveEvent(QMouseEvent *e);
69 void leaveEvent(QEvent *);
70 void mousePressEvent(QMouseEvent *e);
71 void mouseReleaseEvent(QMouseEvent *e);
84 QImage m_segmentSelected;
85 QImage m_segmentSelectedEnd;
86 QImage m_segmentHover;
87 QImage m_segmentHoverEnd;
88 QImage m_triangleIcon;
94 CrumblePathButton::CrumblePathButton(const QString &title, QWidget *parent)
95 : QPushButton(title, parent), m_isHovering(false), m_isPressed(false), m_isSelected(false), m_isEnd(true)
97 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
100 setMaximumHeight(25);
101 setMouseTracking(true);
103 m_textPos.setY(height());
104 m_baseColor = StyleHelper::baseColor();
106 m_segment = QImage(":/utils/images/crumblepath-segment.png");
107 m_segmentSelected = QImage(":/utils/images/crumblepath-segment-selected.png");
108 m_segmentHover = QImage(":/utils/images/crumblepath-segment-hover.png");
109 m_segmentEnd = QImage(":/utils/images/crumblepath-segment-end.png");
110 m_segmentSelectedEnd = QImage(":/utils/images/crumblepath-segment-selected-end.png");
111 m_segmentHoverEnd = QImage(":/utils/images/crumblepath-segment-hover-end.png");
112 m_triangleIcon = QImage(":/utils/images/triangle_vert.png");
117 void CrumblePathButton::paintEvent(QPaintEvent *)
120 QRect geom(0, 0, geometry().width(), geometry().height());
122 if (StyleHelper::baseColor() != m_baseColor) {
123 m_baseColor = StyleHelper::baseColor();
128 if (m_isPressed || m_isSelected) {
129 Utils::StyleHelper::drawCornerImage(m_segmentSelectedEnd, &p, geom, 2, 0, 2, 0);
130 } else if (m_isHovering) {
131 Utils::StyleHelper::drawCornerImage(m_segmentHoverEnd, &p, geom, 2, 0, 2, 0);
133 Utils::StyleHelper::drawCornerImage(m_segmentEnd, &p, geom, 2, 0, 2, 0);
136 if (m_isPressed || m_isSelected) {
137 Utils::StyleHelper::drawCornerImage(m_segmentSelected, &p, geom, 2, 0, 12, 0);
138 } else if (m_isHovering) {
139 Utils::StyleHelper::drawCornerImage(m_segmentHover, &p, geom, 2, 0, 12, 0);
141 Utils::StyleHelper::drawCornerImage(m_segment, &p, geom, 2, 0, 12, 0);
144 p.setPen(StyleHelper::panelTextColor());
145 QFontMetrics fm(p.font());
146 QString textToDraw = fm.elidedText(text(), Qt::ElideRight, geom.width() - m_textPos.x());
148 p.drawText(QRectF(m_textPos.x(), 4, geom.width(), geom.height()), textToDraw);
151 p.drawImage(geom.width() - m_triangleIcon.width() - 6,
152 geom.center().y() - m_triangleIcon.height() / 2,
157 void CrumblePathButton::tintImages()
159 StyleHelper::tintImage(m_segmentEnd, m_baseColor);
160 StyleHelper::tintImage(m_segmentSelectedEnd, m_baseColor);
161 StyleHelper::tintImage(m_segmentHoverEnd, m_baseColor);
162 StyleHelper::tintImage(m_segmentSelected, m_baseColor);
163 StyleHelper::tintImage(m_segmentHover, m_baseColor);
164 StyleHelper::tintImage(m_segment, m_baseColor);
167 void CrumblePathButton::leaveEvent(QEvent *e)
169 QPushButton::leaveEvent(e);
170 m_isHovering = false;
174 void CrumblePathButton::mouseMoveEvent(QMouseEvent *e)
176 QPushButton::mouseMoveEvent(e);
181 void CrumblePathButton::mousePressEvent(QMouseEvent *e)
183 QPushButton::mousePressEvent(e);
188 void CrumblePathButton::mouseReleaseEvent(QMouseEvent *e)
190 QPushButton::mouseReleaseEvent(e);
195 void CrumblePathButton::select(bool s)
201 void CrumblePathButton::setSegmentType(int type)
203 bool useLeftPadding = !(type & FirstSegment);
204 m_isEnd = (type & LastSegment);
205 m_textPos.setX(useLeftPadding ? 18 : 4);
208 void CrumblePathButton::setData(const QVariant &data)
213 QVariant CrumblePathButton::data() const
218 ///////////////////////////////////////////////////////////////////////////////
220 struct CrumblePathPrivate
222 explicit CrumblePathPrivate(CrumblePath *q);
224 QList<CrumblePathButton*> m_buttons;
227 CrumblePathPrivate::CrumblePathPrivate(CrumblePath *q)
235 CrumblePath::CrumblePath(QWidget *parent) :
236 QWidget(parent), d(new CrumblePathPrivate(this))
238 setMinimumHeight(25);
239 setMaximumHeight(25);
240 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
243 CrumblePath::~CrumblePath()
245 qDeleteAll(d->m_buttons);
246 d->m_buttons.clear();
249 void CrumblePath::selectIndex(int index)
251 if (index > -1 && index < d->m_buttons.length())
252 d->m_buttons[index]->select(true);
255 QVariant CrumblePath::dataForIndex(int index) const
257 if (index > -1 && index < d->m_buttons.length())
258 return d->m_buttons[index]->data();
262 void CrumblePath::pushElement(const QString &title, const QVariant &data)
264 CrumblePathButton *newButton = new CrumblePathButton(title, this);
266 connect(newButton, SIGNAL(clicked()), SLOT(mapClickToIndex()));
268 int segType = CrumblePathButton::MiddleSegment;
269 if (!d->m_buttons.isEmpty()) {
270 if (d->m_buttons.length() == 1)
271 segType = segType | CrumblePathButton::FirstSegment;
272 d->m_buttons.last()->setSegmentType(segType);
274 segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
275 newButton->setSegmentType(segType);
277 newButton->setData(data);
278 d->m_buttons.append(newButton);
283 void CrumblePath::addChild(const QString &title, const QVariant &data)
285 QTC_ASSERT(d->m_buttons.count() > 0,return);
287 QPushButton *lastButton = d->m_buttons.last();
289 QMenu *childList = lastButton->menu();
291 childList = new QMenu(lastButton);
293 QAction *childAction = new QAction(title, lastButton);
294 childAction->setData(data);
295 connect(childAction, SIGNAL(triggered()), this, SLOT(mapClickToIndex()));
296 childList->addAction(childAction);
297 lastButton->setMenu(childList);
300 void CrumblePath::popElement()
302 QWidget *last = d->m_buttons.last();
303 d->m_buttons.removeLast();
307 int segType = CrumblePathButton::MiddleSegment | CrumblePathButton::LastSegment;
308 if (!d->m_buttons.isEmpty()) {
309 if (d->m_buttons.length() == 1)
310 segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
311 d->m_buttons.last()->setSegmentType(segType);
316 void CrumblePath::clear()
318 while (!d->m_buttons.isEmpty())
322 void CrumblePath::resizeEvent(QResizeEvent *)
327 void CrumblePath::resizeButtons()
329 int totalWidthLeft = width();
331 if (d->m_buttons.length() >= 1) {
332 QPoint nextElementPosition(0, 0);
334 d->m_buttons.first()->raise();
335 // rearrange all items so that the first item is on top (added last).
337 // compute relative sizes
340 for (int i = 0; i < d->m_buttons.length() ; ++i) {
341 CrumblePathButton *button = d->m_buttons.at(i);
343 QFontMetrics fm(button->font());
344 int originalSize = ArrowBorderSize + fm.width(button->text()) + ArrowBorderSize + 12;
345 sizes << originalSize;
346 totalSize += originalSize - ArrowBorderSize;
349 for (int i = 0; i < d->m_buttons.length() ; ++i) {
350 CrumblePathButton *button = d->m_buttons.at(i);
352 int candidateSize = (sizes.at(i) * totalWidthLeft) / totalSize;
353 if (candidateSize < ArrowBorderSize)
354 candidateSize = ArrowBorderSize;
355 if (candidateSize > sizes.at(i) * 1.3)
356 candidateSize = sizes.at(i) * 1.3;
358 button->setMinimumWidth(candidateSize);
359 button->setMaximumWidth(candidateSize);
360 button->move(nextElementPosition);
362 nextElementPosition.rx() += button->width() - ArrowBorderSize;
366 // work-around for a compiler / optimization bug in i686-apple-darwin9-g
367 // without volatile, the optimizer (-O2) seems to do the wrong thing (tm
368 // the d->m_buttons array with an invalid argument.
369 volatile int prevIndex = i - 1;
370 button->stackUnder(d->m_buttons[prevIndex]);
376 void CrumblePath::mapClickToIndex()
378 QObject *element = sender();
379 if (QString("QAction") == element->metaObject()->className())
380 emit elementClicked(static_cast<QAction *>(element)->data().toInt());
381 else if (QString("QPushButton") == element->metaObject()->className())
382 emit elementClicked(static_cast<CrumblePathButton *>(element)->data().toInt());