OSDN Git Service

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