OSDN Git Service

Merge remote-tracking branch 'qtsw/master'
[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 info@qt.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     Q_OBJECT
54
55 public:
56     enum SegmentType {
57         LastSegment = 1,
58         MiddleSegment = 2,
59         FirstSegment = 4
60     };
61
62     explicit CrumblePathButton(const QString &title, QWidget *parent = 0);
63     void setSegmentType(int type);
64     void select(bool s);
65     void setData(const QVariant &data);
66     QVariant data() const;
67
68 protected:
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);
75
76 private:
77     void tintImages();
78
79 private:
80     bool m_isHovering;
81     bool m_isPressed;
82     bool m_isSelected;
83     bool m_isEnd;
84     QColor m_baseColor;
85     QImage m_segment;
86     QImage m_segmentEnd;
87     QImage m_segmentSelected;
88     QImage m_segmentSelectedEnd;
89     QImage m_segmentHover;
90     QImage m_segmentHoverEnd;
91     QImage m_triangleIcon;
92     QPoint m_textPos;
93
94     QVariant m_data;
95 };
96
97 CrumblePathButton::CrumblePathButton(const QString &title, QWidget *parent)
98     : QPushButton(title, parent), m_isHovering(false), m_isPressed(false), m_isSelected(false), m_isEnd(true)
99 {
100     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
101     setToolTip(title);
102     setMinimumHeight(25);
103     setMaximumHeight(25);
104     setMouseTracking(true);
105     m_textPos.setX(18);
106     m_textPos.setY(height());
107     m_baseColor = StyleHelper::baseColor();
108
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");
116
117     tintImages();
118 }
119
120 void CrumblePathButton::paintEvent(QPaintEvent *)
121 {
122     QPainter p(this);
123     QRect geom(0, 0, geometry().width(), geometry().height());
124
125     if (StyleHelper::baseColor() != m_baseColor) {
126         m_baseColor = StyleHelper::baseColor();
127         tintImages();
128     }
129
130     if (m_isEnd) {
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);
135         } else {
136             Utils::StyleHelper::drawCornerImage(m_segmentEnd, &p, geom, 2, 0, 2, 0);
137         }
138     } else {
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);
143         } else {
144             Utils::StyleHelper::drawCornerImage(m_segment, &p, geom, 2, 0, 12, 0);
145         }
146     }
147     if (isEnabled()) {
148         p.setPen(StyleHelper::panelTextColor());
149     } else {
150         p.setPen(StyleHelper::panelTextColor().darker());
151     }
152     QFontMetrics fm(p.font());
153     QString textToDraw = fm.elidedText(text(), Qt::ElideRight, geom.width() - m_textPos.x());
154
155     p.drawText(QRectF(m_textPos.x(), 4, geom.width(), geom.height()), textToDraw);
156
157     if (menu()) {
158         p.drawImage(geom.width() - m_triangleIcon.width() - 6,
159                     geom.center().y() - m_triangleIcon.height() / 2,
160                     m_triangleIcon);
161     }
162 }
163
164 void CrumblePathButton::tintImages()
165 {
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);
172 }
173
174 void CrumblePathButton::leaveEvent(QEvent *e)
175 {
176     QPushButton::leaveEvent(e);
177     m_isHovering = false;
178     update();
179 }
180
181 void CrumblePathButton::mouseMoveEvent(QMouseEvent *e)
182 {
183     if (!isEnabled())
184         return;
185     QPushButton::mouseMoveEvent(e);
186     m_isHovering = true;
187     update();
188 }
189
190 void CrumblePathButton::mousePressEvent(QMouseEvent *e)
191 {
192     if (!isEnabled())
193         return;
194     QPushButton::mousePressEvent(e);
195     m_isPressed = true;
196     update();
197 }
198
199 void CrumblePathButton::mouseReleaseEvent(QMouseEvent *e)
200 {
201     QPushButton::mouseReleaseEvent(e);
202     m_isPressed = false;
203     update();
204 }
205
206 void CrumblePathButton::changeEvent(QEvent *e)
207 {
208     if (e && e->type() == QEvent::EnabledChange)
209         update();
210 }
211
212 void CrumblePathButton::select(bool s)
213 {
214     m_isSelected = s;
215     update();
216 }
217
218 void CrumblePathButton::setSegmentType(int type)
219 {
220     bool useLeftPadding = !(type & FirstSegment);
221     m_isEnd = (type & LastSegment);
222     m_textPos.setX(useLeftPadding ? 18 : 4);
223 }
224
225 void CrumblePathButton::setData(const QVariant &data)
226 {
227     m_data = data;
228 }
229
230 QVariant CrumblePathButton::data() const
231 {
232     return m_data;
233 }
234
235 ///////////////////////////////////////////////////////////////////////////////
236
237 struct CrumblePathPrivate
238 {
239     explicit CrumblePathPrivate(CrumblePath *q);
240
241     QList<CrumblePathButton*> m_buttons;
242 };
243
244 CrumblePathPrivate::CrumblePathPrivate(CrumblePath *q)
245 {
246     Q_UNUSED(q)
247 }
248
249 //
250 // CrumblePath
251 //
252 CrumblePath::CrumblePath(QWidget *parent) :
253     QWidget(parent), d(new CrumblePathPrivate(this))
254 {
255     setMinimumHeight(25);
256     setMaximumHeight(25);
257     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
258 }
259
260 CrumblePath::~CrumblePath()
261 {
262     qDeleteAll(d->m_buttons);
263     d->m_buttons.clear();
264     delete d;
265 }
266
267 void CrumblePath::selectIndex(int index)
268 {
269     if (index > -1 && index < d->m_buttons.length())
270         d->m_buttons[index]->select(true);
271 }
272
273 QVariant CrumblePath::dataForIndex(int index) const
274 {
275     if (index > -1 && index < d->m_buttons.length())
276         return d->m_buttons[index]->data();
277     return QVariant();
278 }
279
280 QVariant CrumblePath::dataForLastIndex() const
281 {
282     if (d->m_buttons.isEmpty())
283         return QVariant();
284     return d->m_buttons.last()->data();
285 }
286
287 void CrumblePath::pushElement(const QString &title, const QVariant &data)
288 {
289     CrumblePathButton *newButton = new CrumblePathButton(title, this);
290     newButton->hide();
291     connect(newButton, SIGNAL(clicked()), SLOT(emitElementClicked()));
292
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);
298     } else {
299         segType = CrumblePathButton::FirstSegment | CrumblePathButton::LastSegment;
300         newButton->setSegmentType(segType);
301     }
302     newButton->setData(data);
303     d->m_buttons.append(newButton);
304
305     resizeButtons();
306 }
307
308 void CrumblePath::addChild(const QString &title, const QVariant &data)
309 {
310     QTC_ASSERT(!d->m_buttons.isEmpty(), return);
311
312     QPushButton *lastButton = d->m_buttons.last();
313
314     QMenu *childList = lastButton->menu();
315     if (childList == 0)
316         childList = new QMenu(lastButton);
317
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);
323 }
324
325 void CrumblePath::popElement()
326 {
327     QWidget *last = d->m_buttons.last();
328     d->m_buttons.removeLast();
329     last->setParent(0);
330     last->deleteLater();
331
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);
337     }
338     resizeButtons();
339 }
340
341 void CrumblePath::clear()
342 {
343     while (!d->m_buttons.isEmpty())
344         popElement();
345 }
346
347 void CrumblePath::resizeEvent(QResizeEvent *)
348 {
349     resizeButtons();
350 }
351
352 void CrumblePath::resizeButtons()
353 {
354     int totalWidthLeft = width();
355
356     if (!d->m_buttons.isEmpty()) {
357         QPoint nextElementPosition(0, 0);
358
359         d->m_buttons.first()->raise();
360         // rearrange all items so that the first item is on top (added last).
361
362         // compute relative sizes
363         QList<int> sizes;
364         int totalSize = 0;
365         for (int i = 0; i < d->m_buttons.length() ; ++i) {
366             CrumblePathButton *button = d->m_buttons.at(i);
367
368             QFontMetrics fm(button->font());
369             int originalSize = ArrowBorderSize + fm.width(button->text()) + ArrowBorderSize + 12;
370             sizes << originalSize;
371             totalSize += originalSize - ArrowBorderSize;
372         }
373
374         for (int i = 0; i < d->m_buttons.length() ; ++i) {
375             CrumblePathButton *button = d->m_buttons.at(i);
376
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;
382
383             button->setMinimumWidth(candidateSize);
384             button->setMaximumWidth(candidateSize);
385             button->move(nextElementPosition);
386
387             nextElementPosition.rx() += button->width() - ArrowBorderSize;
388
389             button->show();
390             if (i > 0) {
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]);
396             }
397         }
398     }
399 }
400
401 void CrumblePath::emitElementClicked()
402 {
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());
408 }
409
410 } // namespace Utils
411
412 #include "crumblepath.moc"