OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / libs / utils / crumblepath.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "crumblepath.h"
34 #include "stylehelper.h"
35
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>
44
45 #include <qtcassert.h>
46
47 namespace Utils {
48
49 static const int ArrowBorderSize = 12;
50
51 class CrumblePathButton : public QPushButton
52 {
53 public:
54     enum SegmentType {
55         LastSegment = 1,
56         MiddleSegment = 2,
57         FirstSegment = 4
58     };
59
60     explicit CrumblePathButton(const QString &title, QWidget *parent = 0);
61     void setSegmentType(int type);
62     void select(bool s);
63     void setData(const QVariant &data);
64     QVariant data() const;
65
66 protected:
67     void paintEvent(QPaintEvent *);
68     void mouseMoveEvent(QMouseEvent *e);
69     void leaveEvent(QEvent *);
70     void mousePressEvent(QMouseEvent *e);
71     void mouseReleaseEvent(QMouseEvent *e);
72
73 private:
74     void tintImages();
75
76 private:
77     bool m_isHovering;
78     bool m_isPressed;
79     bool m_isSelected;
80     bool m_isEnd;
81     QColor m_baseColor;
82     QImage m_segment;
83     QImage m_segmentEnd;
84     QImage m_segmentSelected;
85     QImage m_segmentSelectedEnd;
86     QImage m_segmentHover;
87     QImage m_segmentHoverEnd;
88     QImage m_triangleIcon;
89     QPoint m_textPos;
90
91     QVariant m_data;
92 };
93
94 CrumblePathButton::CrumblePathButton(const QString &title, QWidget *parent)
95     : QPushButton(title, parent), m_isHovering(false), m_isPressed(false), m_isSelected(false), m_isEnd(true)
96 {
97     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
98     setToolTip(title);
99     setMinimumHeight(25);
100     setMaximumHeight(25);
101     setMouseTracking(true);
102     m_textPos.setX(18);
103     m_textPos.setY(height());
104     m_baseColor = StyleHelper::baseColor();
105
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");
113
114     tintImages();
115 }
116
117 void CrumblePathButton::paintEvent(QPaintEvent *)
118 {
119     QPainter p(this);
120     QRect geom(0, 0, geometry().width(), geometry().height());
121
122     if (StyleHelper::baseColor() != m_baseColor) {
123         m_baseColor = StyleHelper::baseColor();
124         tintImages();
125     }
126
127     if (m_isEnd) {
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);
132         } else {
133             Utils::StyleHelper::drawCornerImage(m_segmentEnd, &p, geom, 2, 0, 2, 0);
134         }
135     } else {
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);
140         } else {
141             Utils::StyleHelper::drawCornerImage(m_segment, &p, geom, 2, 0, 12, 0);
142         }
143     }
144     p.setPen(StyleHelper::panelTextColor());
145     QFontMetrics fm(p.font());
146     QString textToDraw = fm.elidedText(text(), Qt::ElideRight, geom.width() - m_textPos.x());
147
148     p.drawText(QRectF(m_textPos.x(), 4, geom.width(), geom.height()), textToDraw);
149
150     if (menu()) {
151         p.drawImage(geom.width() - m_triangleIcon.width() - 6,
152                     geom.center().y() - m_triangleIcon.height() / 2,
153                     m_triangleIcon);
154     }
155 }
156
157 void CrumblePathButton::tintImages()
158 {
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);
165 }
166
167 void CrumblePathButton::leaveEvent(QEvent *e)
168 {
169     QPushButton::leaveEvent(e);
170     m_isHovering = false;
171     update();
172 }
173
174 void CrumblePathButton::mouseMoveEvent(QMouseEvent *e)
175 {
176     QPushButton::mouseMoveEvent(e);
177     m_isHovering = true;
178     update();
179 }
180
181 void CrumblePathButton::mousePressEvent(QMouseEvent *e)
182 {
183     QPushButton::mousePressEvent(e);
184     m_isPressed = true;
185     update();
186 }
187
188 void CrumblePathButton::mouseReleaseEvent(QMouseEvent *e)
189 {
190     QPushButton::mouseReleaseEvent(e);
191     m_isPressed = false;
192     update();
193 }
194
195 void CrumblePathButton::select(bool s)
196 {
197     m_isSelected = s;
198     update();
199 }
200
201 void CrumblePathButton::setSegmentType(int type)
202 {
203     bool useLeftPadding = !(type & FirstSegment);
204     m_isEnd = (type & LastSegment);
205     m_textPos.setX(useLeftPadding ? 18 : 4);
206 }
207
208 void CrumblePathButton::setData(const QVariant &data)
209 {
210     m_data = data;
211 }
212
213 QVariant CrumblePathButton::data() const
214 {
215     return m_data;
216 }
217
218 ///////////////////////////////////////////////////////////////////////////////
219
220 struct CrumblePathPrivate
221 {
222     explicit CrumblePathPrivate(CrumblePath *q);
223
224     QList<CrumblePathButton*> m_buttons;
225 };
226
227 CrumblePathPrivate::CrumblePathPrivate(CrumblePath *q)
228 {
229     Q_UNUSED(q)
230 }
231
232 //
233 // CrumblePath
234 //
235 CrumblePath::CrumblePath(QWidget *parent) :
236     QWidget(parent), d(new CrumblePathPrivate(this))
237 {
238     setMinimumHeight(25);
239     setMaximumHeight(25);
240     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
241 }
242
243 CrumblePath::~CrumblePath()
244 {
245     qDeleteAll(d->m_buttons);
246     d->m_buttons.clear();
247 }
248
249 void CrumblePath::selectIndex(int index)
250 {
251     if (index > -1 && index < d->m_buttons.length())
252         d->m_buttons[index]->select(true);
253 }
254
255 QVariant CrumblePath::dataForIndex(int index) const
256 {
257     if (index > -1 && index < d->m_buttons.length())
258         return d->m_buttons[index]->data();
259     return QVariant();
260 }
261
262 void CrumblePath::pushElement(const QString &title, const QVariant &data)
263 {
264     CrumblePathButton *newButton = new CrumblePathButton(title, this);
265     newButton->hide();
266     connect(newButton, SIGNAL(clicked()), SLOT(mapClickToIndex()));
267
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);
273     } else {
274         segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
275         newButton->setSegmentType(segType);
276     }
277     newButton->setData(data);
278     d->m_buttons.append(newButton);
279
280     resizeButtons();
281 }
282
283 void CrumblePath::addChild(const QString &title, const QVariant &data)
284 {
285     QTC_ASSERT(d->m_buttons.count() > 0,return);
286
287     QPushButton *lastButton = d->m_buttons.last();
288
289     QMenu *childList = lastButton->menu();
290     if (childList == 0)
291         childList = new QMenu(lastButton);
292
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);
298 }
299
300 void CrumblePath::popElement()
301 {
302     QWidget *last = d->m_buttons.last();
303     d->m_buttons.removeLast();
304     last->setParent(0);
305     last->deleteLater();
306
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);
312     }
313     resizeButtons();
314 }
315
316 void CrumblePath::clear()
317 {
318     while (!d->m_buttons.isEmpty())
319         popElement();
320 }
321
322 void CrumblePath::resizeEvent(QResizeEvent *)
323 {
324     resizeButtons();
325 }
326
327 void CrumblePath::resizeButtons()
328 {
329     int totalWidthLeft = width();
330
331     if (d->m_buttons.length() >= 1) {
332         QPoint nextElementPosition(0, 0);
333
334         d->m_buttons.first()->raise();
335         // rearrange all items so that the first item is on top (added last).
336
337         // compute relative sizes
338         QList<int> sizes;
339         int totalSize = 0;
340         for (int i = 0; i < d->m_buttons.length() ; ++i) {
341             CrumblePathButton *button = d->m_buttons.at(i);
342
343             QFontMetrics fm(button->font());
344             int originalSize = ArrowBorderSize + fm.width(button->text()) + ArrowBorderSize + 12;
345             sizes << originalSize;
346             totalSize += originalSize - ArrowBorderSize;
347         }
348
349         for (int i = 0; i < d->m_buttons.length() ; ++i) {
350             CrumblePathButton *button = d->m_buttons.at(i);
351
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;
357
358             button->setMinimumWidth(candidateSize);
359             button->setMaximumWidth(candidateSize);
360             button->move(nextElementPosition);
361
362             nextElementPosition.rx() += button->width() - ArrowBorderSize;
363
364             button->show();
365             if (i > 0) {
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]);
371             }
372         }
373     }
374 }
375
376 void CrumblePath::mapClickToIndex()
377 {
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());
383 }
384
385 } // namespace Utils