1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "crumblepath.h"
35 #include "stylehelper.h"
37 #include <QtCore/QList>
38 #include <QtGui/QHBoxLayout>
39 #include <QtGui/QPushButton>
40 #include <QtGui/QMenu>
41 #include <QtGui/QStyle>
42 #include <QtGui/QResizeEvent>
43 #include <QtGui/QPainter>
44 #include <QtGui/QImage>
46 #include <qtcassert.h>
50 static const int ArrowBorderSize = 12;
52 class CrumblePathButton : public QPushButton
61 explicit CrumblePathButton(const QString &title, QWidget *parent = 0);
62 void setSegmentType(int type);
64 void setData(const QVariant &data);
65 QVariant data() const;
68 void paintEvent(QPaintEvent *);
69 void mouseMoveEvent(QMouseEvent *e);
70 void leaveEvent(QEvent *);
71 void mousePressEvent(QMouseEvent *e);
72 void mouseReleaseEvent(QMouseEvent *e);
85 QImage m_segmentSelected;
86 QImage m_segmentSelectedEnd;
87 QImage m_segmentHover;
88 QImage m_segmentHoverEnd;
89 QImage m_triangleIcon;
95 CrumblePathButton::CrumblePathButton(const QString &title, QWidget *parent)
96 : QPushButton(title, parent), m_isHovering(false), m_isPressed(false), m_isSelected(false), m_isEnd(true)
98 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
100 setMinimumHeight(25);
101 setMaximumHeight(25);
102 setMouseTracking(true);
104 m_textPos.setY(height());
105 m_baseColor = StyleHelper::baseColor();
107 m_segment = QImage(":/utils/images/crumblepath-segment.png");
108 m_segmentSelected = QImage(":/utils/images/crumblepath-segment-selected.png");
109 m_segmentHover = QImage(":/utils/images/crumblepath-segment-hover.png");
110 m_segmentEnd = QImage(":/utils/images/crumblepath-segment-end.png");
111 m_segmentSelectedEnd = QImage(":/utils/images/crumblepath-segment-selected-end.png");
112 m_segmentHoverEnd = QImage(":/utils/images/crumblepath-segment-hover-end.png");
113 m_triangleIcon = QImage(":/utils/images/triangle_vert.png");
118 void CrumblePathButton::paintEvent(QPaintEvent *)
121 QRect geom(0, 0, geometry().width(), geometry().height());
123 if (StyleHelper::baseColor() != m_baseColor) {
124 m_baseColor = StyleHelper::baseColor();
129 if (m_isPressed || m_isSelected) {
130 Utils::StyleHelper::drawCornerImage(m_segmentSelectedEnd, &p, geom, 2, 0, 2, 0);
131 } else if (m_isHovering) {
132 Utils::StyleHelper::drawCornerImage(m_segmentHoverEnd, &p, geom, 2, 0, 2, 0);
134 Utils::StyleHelper::drawCornerImage(m_segmentEnd, &p, geom, 2, 0, 2, 0);
137 if (m_isPressed || m_isSelected) {
138 Utils::StyleHelper::drawCornerImage(m_segmentSelected, &p, geom, 2, 0, 12, 0);
139 } else if (m_isHovering) {
140 Utils::StyleHelper::drawCornerImage(m_segmentHover, &p, geom, 2, 0, 12, 0);
142 Utils::StyleHelper::drawCornerImage(m_segment, &p, geom, 2, 0, 12, 0);
145 p.setPen(StyleHelper::panelTextColor());
146 QFontMetrics fm(p.font());
147 QString textToDraw = fm.elidedText(text(), Qt::ElideRight, geom.width() - m_textPos.x());
149 p.drawText(QRectF(m_textPos.x(), 4, geom.width(), geom.height()), textToDraw);
152 p.drawImage(geom.width() - m_triangleIcon.width() - 6,
153 geom.center().y() - m_triangleIcon.height() / 2,
158 void CrumblePathButton::tintImages()
160 StyleHelper::tintImage(m_segmentEnd, m_baseColor);
161 StyleHelper::tintImage(m_segmentSelectedEnd, m_baseColor);
162 StyleHelper::tintImage(m_segmentHoverEnd, m_baseColor);
163 StyleHelper::tintImage(m_segmentSelected, m_baseColor);
164 StyleHelper::tintImage(m_segmentHover, m_baseColor);
165 StyleHelper::tintImage(m_segment, m_baseColor);
168 void CrumblePathButton::leaveEvent(QEvent *e)
170 QPushButton::leaveEvent(e);
171 m_isHovering = false;
175 void CrumblePathButton::mouseMoveEvent(QMouseEvent *e)
177 QPushButton::mouseMoveEvent(e);
182 void CrumblePathButton::mousePressEvent(QMouseEvent *e)
184 QPushButton::mousePressEvent(e);
189 void CrumblePathButton::mouseReleaseEvent(QMouseEvent *e)
191 QPushButton::mouseReleaseEvent(e);
196 void CrumblePathButton::select(bool s)
202 void CrumblePathButton::setSegmentType(int type)
204 bool useLeftPadding = !(type & FirstSegment);
205 m_isEnd = (type & LastSegment);
206 m_textPos.setX(useLeftPadding ? 18 : 4);
209 void CrumblePathButton::setData(const QVariant &data)
214 QVariant CrumblePathButton::data() const
219 ///////////////////////////////////////////////////////////////////////////////
221 struct CrumblePathPrivate
223 explicit CrumblePathPrivate(CrumblePath *q);
225 QList<CrumblePathButton*> m_buttons;
228 CrumblePathPrivate::CrumblePathPrivate(CrumblePath *q)
236 CrumblePath::CrumblePath(QWidget *parent) :
237 QWidget(parent), d(new CrumblePathPrivate(this))
239 setMinimumHeight(25);
240 setMaximumHeight(25);
241 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
244 CrumblePath::~CrumblePath()
246 qDeleteAll(d->m_buttons);
247 d->m_buttons.clear();
250 void CrumblePath::selectIndex(int index)
252 if (index > -1 && index < d->m_buttons.length())
253 d->m_buttons[index]->select(true);
256 QVariant CrumblePath::dataForIndex(int index) const
258 if (index > -1 && index < d->m_buttons.length())
259 return d->m_buttons[index]->data();
263 void CrumblePath::pushElement(const QString &title, const QVariant &data)
265 CrumblePathButton *newButton = new CrumblePathButton(title, this);
267 connect(newButton, SIGNAL(clicked()), SLOT(mapClickToIndex()));
269 int segType = CrumblePathButton::MiddleSegment;
270 if (!d->m_buttons.isEmpty()) {
271 if (d->m_buttons.length() == 1)
272 segType = segType | CrumblePathButton::FirstSegment;
273 d->m_buttons.last()->setSegmentType(segType);
275 segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
276 newButton->setSegmentType(segType);
278 newButton->setData(data);
279 d->m_buttons.append(newButton);
284 void CrumblePath::addChild(const QString &title, const QVariant &data)
286 QTC_ASSERT(d->m_buttons.count() > 0,return);
288 QPushButton *lastButton = d->m_buttons.last();
290 QMenu *childList = lastButton->menu();
292 childList = new QMenu(lastButton);
294 QAction *childAction = new QAction(title, lastButton);
295 childAction->setData(data);
296 connect(childAction, SIGNAL(triggered()), this, SLOT(mapClickToIndex()));
297 childList->addAction(childAction);
298 lastButton->setMenu(childList);
301 void CrumblePath::popElement()
303 QWidget *last = d->m_buttons.last();
304 d->m_buttons.removeLast();
308 int segType = CrumblePathButton::MiddleSegment | CrumblePathButton::LastSegment;
309 if (!d->m_buttons.isEmpty()) {
310 if (d->m_buttons.length() == 1)
311 segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
312 d->m_buttons.last()->setSegmentType(segType);
317 void CrumblePath::clear()
319 while (!d->m_buttons.isEmpty())
323 void CrumblePath::resizeEvent(QResizeEvent *)
328 void CrumblePath::resizeButtons()
330 int totalWidthLeft = width();
332 if (d->m_buttons.length() >= 1) {
333 QPoint nextElementPosition(0, 0);
335 d->m_buttons.first()->raise();
336 // rearrange all items so that the first item is on top (added last).
338 // compute relative sizes
341 for (int i = 0; i < d->m_buttons.length() ; ++i) {
342 CrumblePathButton *button = d->m_buttons.at(i);
344 QFontMetrics fm(button->font());
345 int originalSize = ArrowBorderSize + fm.width(button->text()) + ArrowBorderSize + 12;
346 sizes << originalSize;
347 totalSize += originalSize - ArrowBorderSize;
350 for (int i = 0; i < d->m_buttons.length() ; ++i) {
351 CrumblePathButton *button = d->m_buttons.at(i);
353 int candidateSize = (sizes.at(i) * totalWidthLeft) / totalSize;
354 if (candidateSize < ArrowBorderSize)
355 candidateSize = ArrowBorderSize;
356 if (candidateSize > sizes.at(i) * 1.3)
357 candidateSize = sizes.at(i) * 1.3;
359 button->setMinimumWidth(candidateSize);
360 button->setMaximumWidth(candidateSize);
361 button->move(nextElementPosition);
363 nextElementPosition.rx() += button->width() - ArrowBorderSize;
367 // work-around for a compiler / optimization bug in i686-apple-darwin9-g
368 // without volatile, the optimizer (-O2) seems to do the wrong thing (tm
369 // the d->m_buttons array with an invalid argument.
370 volatile int prevIndex = i - 1;
371 button->stackUnder(d->m_buttons[prevIndex]);
377 void CrumblePath::mapClickToIndex()
379 QObject *element = sender();
380 if (QString("QAction") == element->metaObject()->className())
381 emit elementClicked(static_cast<QAction *>(element)->data().toInt());
382 else if (QString("QPushButton") == element->metaObject()->className())
383 emit elementClicked(static_cast<CrumblePathButton *>(element)->data().toInt());