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 info@qt.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
62 explicit CrumblePathButton(const QString &title, QWidget *parent = 0);
63 void setSegmentType(int type);
65 void setData(const QVariant &data);
66 QVariant data() const;
69 void paintEvent(QPaintEvent *);
70 void mouseMoveEvent(QMouseEvent *e);
71 void leaveEvent(QEvent *);
72 void mousePressEvent(QMouseEvent *e);
73 void mouseReleaseEvent(QMouseEvent *e);
74 void changeEvent(QEvent * e);
87 QImage m_segmentSelected;
88 QImage m_segmentSelectedEnd;
89 QImage m_segmentHover;
90 QImage m_segmentHoverEnd;
91 QImage m_triangleIcon;
97 CrumblePathButton::CrumblePathButton(const QString &title, QWidget *parent)
98 : QPushButton(title, parent), m_isHovering(false), m_isPressed(false), m_isSelected(false), m_isEnd(true)
100 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
102 setMinimumHeight(25);
103 setMaximumHeight(25);
104 setMouseTracking(true);
106 m_textPos.setY(height());
107 m_baseColor = StyleHelper::baseColor();
109 m_segment = QImage(":/utils/images/crumblepath-segment.png");
110 m_segmentSelected = QImage(":/utils/images/crumblepath-segment-selected.png");
111 m_segmentHover = QImage(":/utils/images/crumblepath-segment-hover.png");
112 m_segmentEnd = QImage(":/utils/images/crumblepath-segment-end.png");
113 m_segmentSelectedEnd = QImage(":/utils/images/crumblepath-segment-selected-end.png");
114 m_segmentHoverEnd = QImage(":/utils/images/crumblepath-segment-hover-end.png");
115 m_triangleIcon = QImage(":/utils/images/triangle_vert.png");
120 void CrumblePathButton::paintEvent(QPaintEvent *)
123 QRect geom(0, 0, geometry().width(), geometry().height());
125 if (StyleHelper::baseColor() != m_baseColor) {
126 m_baseColor = StyleHelper::baseColor();
131 if (m_isPressed || m_isSelected) {
132 Utils::StyleHelper::drawCornerImage(m_segmentSelectedEnd, &p, geom, 2, 0, 2, 0);
133 } else if (m_isHovering) {
134 Utils::StyleHelper::drawCornerImage(m_segmentHoverEnd, &p, geom, 2, 0, 2, 0);
136 Utils::StyleHelper::drawCornerImage(m_segmentEnd, &p, geom, 2, 0, 2, 0);
139 if (m_isPressed || m_isSelected) {
140 Utils::StyleHelper::drawCornerImage(m_segmentSelected, &p, geom, 2, 0, 12, 0);
141 } else if (m_isHovering) {
142 Utils::StyleHelper::drawCornerImage(m_segmentHover, &p, geom, 2, 0, 12, 0);
144 Utils::StyleHelper::drawCornerImage(m_segment, &p, geom, 2, 0, 12, 0);
148 p.setPen(StyleHelper::panelTextColor());
150 p.setPen(StyleHelper::panelTextColor().darker());
152 QFontMetrics fm(p.font());
153 QString textToDraw = fm.elidedText(text(), Qt::ElideRight, geom.width() - m_textPos.x());
155 p.drawText(QRectF(m_textPos.x(), 4, geom.width(), geom.height()), textToDraw);
158 p.drawImage(geom.width() - m_triangleIcon.width() - 6,
159 geom.center().y() - m_triangleIcon.height() / 2,
164 void CrumblePathButton::tintImages()
166 StyleHelper::tintImage(m_segmentEnd, m_baseColor);
167 StyleHelper::tintImage(m_segmentSelectedEnd, m_baseColor);
168 StyleHelper::tintImage(m_segmentHoverEnd, m_baseColor);
169 StyleHelper::tintImage(m_segmentSelected, m_baseColor);
170 StyleHelper::tintImage(m_segmentHover, m_baseColor);
171 StyleHelper::tintImage(m_segment, m_baseColor);
174 void CrumblePathButton::leaveEvent(QEvent *e)
176 QPushButton::leaveEvent(e);
177 m_isHovering = false;
181 void CrumblePathButton::mouseMoveEvent(QMouseEvent *e)
185 QPushButton::mouseMoveEvent(e);
190 void CrumblePathButton::mousePressEvent(QMouseEvent *e)
194 QPushButton::mousePressEvent(e);
199 void CrumblePathButton::mouseReleaseEvent(QMouseEvent *e)
201 QPushButton::mouseReleaseEvent(e);
206 void CrumblePathButton::changeEvent(QEvent *e)
208 if (e && e->type() == QEvent::EnabledChange)
212 void CrumblePathButton::select(bool s)
218 void CrumblePathButton::setSegmentType(int type)
220 bool useLeftPadding = !(type & FirstSegment);
221 m_isEnd = (type & LastSegment);
222 m_textPos.setX(useLeftPadding ? 18 : 4);
225 void CrumblePathButton::setData(const QVariant &data)
230 QVariant CrumblePathButton::data() const
235 ///////////////////////////////////////////////////////////////////////////////
237 struct CrumblePathPrivate
239 explicit CrumblePathPrivate(CrumblePath *q);
241 QList<CrumblePathButton*> m_buttons;
244 CrumblePathPrivate::CrumblePathPrivate(CrumblePath *q)
252 CrumblePath::CrumblePath(QWidget *parent) :
253 QWidget(parent), d(new CrumblePathPrivate(this))
255 setMinimumHeight(25);
256 setMaximumHeight(25);
257 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
260 CrumblePath::~CrumblePath()
262 qDeleteAll(d->m_buttons);
263 d->m_buttons.clear();
267 void CrumblePath::selectIndex(int index)
269 if (index > -1 && index < d->m_buttons.length())
270 d->m_buttons[index]->select(true);
273 QVariant CrumblePath::dataForIndex(int index) const
275 if (index > -1 && index < d->m_buttons.length())
276 return d->m_buttons[index]->data();
280 QVariant CrumblePath::dataForLastIndex() const
282 if (d->m_buttons.isEmpty())
284 return d->m_buttons.last()->data();
287 void CrumblePath::pushElement(const QString &title, const QVariant &data)
289 CrumblePathButton *newButton = new CrumblePathButton(title, this);
291 connect(newButton, SIGNAL(clicked()), SLOT(emitElementClicked()));
293 int segType = CrumblePathButton::MiddleSegment;
294 if (!d->m_buttons.isEmpty()) {
295 if (d->m_buttons.length() == 1)
296 segType = segType | CrumblePathButton::FirstSegment;
297 d->m_buttons.last()->setSegmentType(segType);
299 segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
300 newButton->setSegmentType(segType);
302 newButton->setData(data);
303 d->m_buttons.append(newButton);
308 void CrumblePath::addChild(const QString &title, const QVariant &data)
310 QTC_ASSERT(!d->m_buttons.isEmpty(), return);
312 QPushButton *lastButton = d->m_buttons.last();
314 QMenu *childList = lastButton->menu();
316 childList = new QMenu(lastButton);
318 QAction *childAction = new QAction(title, lastButton);
319 childAction->setData(data);
320 connect(childAction, SIGNAL(triggered()), this, SLOT(emitElementClicked()));
321 childList->addAction(childAction);
322 lastButton->setMenu(childList);
325 void CrumblePath::popElement()
327 QWidget *last = d->m_buttons.last();
328 d->m_buttons.removeLast();
332 int segType = CrumblePathButton::MiddleSegment | CrumblePathButton::LastSegment;
333 if (!d->m_buttons.isEmpty()) {
334 if (d->m_buttons.length() == 1)
335 segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
336 d->m_buttons.last()->setSegmentType(segType);
341 void CrumblePath::clear()
343 while (!d->m_buttons.isEmpty())
347 void CrumblePath::resizeEvent(QResizeEvent *)
352 void CrumblePath::resizeButtons()
354 int totalWidthLeft = width();
356 if (!d->m_buttons.isEmpty()) {
357 QPoint nextElementPosition(0, 0);
359 d->m_buttons.first()->raise();
360 // rearrange all items so that the first item is on top (added last).
362 // compute relative sizes
365 for (int i = 0; i < d->m_buttons.length() ; ++i) {
366 CrumblePathButton *button = d->m_buttons.at(i);
368 QFontMetrics fm(button->font());
369 int originalSize = ArrowBorderSize + fm.width(button->text()) + ArrowBorderSize + 12;
370 sizes << originalSize;
371 totalSize += originalSize - ArrowBorderSize;
374 for (int i = 0; i < d->m_buttons.length() ; ++i) {
375 CrumblePathButton *button = d->m_buttons.at(i);
377 int candidateSize = (sizes.at(i) * totalWidthLeft) / totalSize;
378 if (candidateSize < ArrowBorderSize)
379 candidateSize = ArrowBorderSize;
380 if (candidateSize > sizes.at(i) * 1.3)
381 candidateSize = sizes.at(i) * 1.3;
383 button->setMinimumWidth(candidateSize);
384 button->setMaximumWidth(candidateSize);
385 button->move(nextElementPosition);
387 nextElementPosition.rx() += button->width() - ArrowBorderSize;
391 // work-around for a compiler / optimization bug in i686-apple-darwin9-g
392 // without volatile, the optimizer (-O2) seems to do the wrong thing (tm
393 // the d->m_buttons array with an invalid argument.
394 volatile int prevIndex = i - 1;
395 button->stackUnder(d->m_buttons[prevIndex]);
401 void CrumblePath::emitElementClicked()
403 QObject *element = sender();
404 if (QAction *action = qobject_cast<QAction*>(element))
405 emit elementClicked(action->data());
406 else if (CrumblePathButton *button = qobject_cast<CrumblePathButton*>(element))
407 emit elementClicked(button->data());
412 #include "crumblepath.moc"