1 /****************************************************************************
2 **
3 ** This file is part of a Qt Solutions component.
4 **
5 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact:  Qt Software Information (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Solutions Commercial License Agreement provided
12 ** with the Software or, alternatively, in accordance with the terms
13 ** contained in a written agreement between you and Nokia.
14 **
15 ** GNU Lesser General Public License Usage
16 ** Alternatively, this file may be used under the terms of the GNU Lesser
17 ** General Public License version 2.1 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.LGPL included in the
19 ** packaging of this file.  Please review the following information to
20 ** ensure the GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22 **
23 ** In addition, as a special exception, Nokia gives you certain
24 ** additional rights. These rights are described in the Nokia Qt LGPL
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26 ** package.
27 **
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file.  Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
35 **
36 ** Please note Third Party Software included with Qt Solutions may impose
37 ** additional restrictions and it is the user's responsibility to ensure
38 ** that they have met the licensing requirements of the GPL, LGPL, or Qt
39 ** Solutions Commercial license and the relevant license of the Third
40 ** Party Software they are using.
41 **
42 ** If you are unsure which license is appropriate for your use, please
43 ** contact the sales department at qt-sales@nokia.com.
44 **
45 ****************************************************************************/
46 
47 #include <QApplication>
48 #include <QDesktopWidget>
49 #include <QPainter>
50 #include <QPushButton>
51 #include <QColorDialog>
52 #include <QtCore/QMap>
53 #include <QLayout>
54 #include <QStyle>
55 #include <QLabel>
56 #include <QToolTip>
57 #include <QPixmap>
58 #include <QFocusEvent>
59 #include <QPaintEvent>
60 #include <QGridLayout>
61 #include <QHideEvent>
62 #include <QKeyEvent>
63 #include <QShowEvent>
64 #include <QMouseEvent>
65 #include <math.h>
66 
67 #include "qtcolorpicker.h"
68 
69 /*! \class QtColorPicker
70 
71     \brief The QtColorPicker class provides a widget for selecting
72     colors from a popup color grid.
73 
74     Users can invoke the color picker by clicking on it, or by
75     navigating to it and pressing Space. They can use the mouse or
76     arrow keys to navigate between colors on the grid, and select a
77     color by clicking or by pressing Enter or Space. The
78     colorChanged() signal is emitted whenever the color picker's color
79     changes.
80 
81     The widget also supports negative selection: Users can click and
82     hold the mouse button on the QtColorPicker widget, then move the
83     mouse over the color grid and release the mouse button over the
84     color they wish to select.
85 
86     The color grid shows a customized selection of colors. An optional
87     ellipsis "..." button (signifying "more") can be added at the
88     bottom of the grid; if the user presses this, a QColorDialog pops
89     up and lets them choose any color they like. This button is made
90     available by using setColorDialogEnabled().
91 
92     When a color is selected, the QtColorPicker widget shows the color
93     and its name. If the name cannot be determined, the translatable
94     name "Custom" is used.
95 
96     The QtColorPicker object is optionally initialized with the number
97     of columns in the color grid. Colors are then added left to right,
98     top to bottom using insertColor(). If the number of columns is not
99     set, QtColorPicker calculates the number of columns and rows that
100     will make the grid as square as possible.
101 
102     \code
103     DrawWidget::DrawWidget(QWidget *parent, const char *name)
104     {
105         QtColorPicker *picker = new QtColorPicker(this);
106         picker->insertColor(red, "Red"));
107         picker->insertColor(QColor("green"), "Green"));
108         picker->insertColor(QColor(0, 0, 255), "Blue"));
109         picker->insertColor(white);
110 
111         connect(colors, SIGNAL(colorChanged(const QColor &)), SLOT(setCurrentColor(const QColor &)));
112     }
113     \endcode
114 
115     An alternative to adding colors manually is to initialize the grid
116     with QColorDialog's standard colors using setStandardColors().
117 
118     QtColorPicker also provides a the static function getColor(),
119     which pops up the grid of standard colors at any given point.
120 
121     \img colorpicker1.png
122     \img colorpicker2.png
123 
124     \sa QColorDialog
125 */
126 
127 /*! \fn QtColorPicker::colorChanged(const QColor &color)
128 
129     This signal is emitted when the QtColorPicker's color is changed.
130     \a color is the new color.
131 
132     To obtain the color's name, use text().
133 */
134 
135 /*
136     A class  that acts very much  like a QPushButton. It's not styled,
137     so we  can  expect  the  exact  same    look,  feel and   geometry
138     everywhere.     Also,  this  button     always emits   clicked  on
139     mouseRelease, even if the mouse button was  not pressed inside the
140     widget.
141 */
142 class ColorPickerButton : public QFrame
143 {
144     Q_OBJECT
145 
146 public:
147     ColorPickerButton(QWidget *parent);
148 
149 signals:
150     void clicked();
151 
152 protected:
153     void mousePressEvent(QMouseEvent *e);
154     void mouseMoveEvent(QMouseEvent *e);
155     void mouseReleaseEvent(QMouseEvent *e);
156     void keyPressEvent(QKeyEvent *e);
157     void keyReleaseEvent(QKeyEvent *e);
158     void paintEvent(QPaintEvent *e);
159     void focusInEvent(QFocusEvent *e);
160     void focusOutEvent(QFocusEvent *e);
161 };
162 
163 /*
164     This class represents each "color" or item in the color grid.
165 */
166 class ColorPickerItem : public QFrame
167 {
168     Q_OBJECT
169 
170 public:
171     ColorPickerItem(const QColor &color = Qt::white, const QString &text = QString(),
172 		      QWidget *parent = 0);
173     ~ColorPickerItem();
174 
175     QColor color() const;
176     QString text() const;
177 
178     void setSelected(bool);
179     bool isSelected() const;
180 signals:
181     void clicked();
182     void selected();
183 
184 public slots:
185     void setColor(const QColor &color, const QString &text = QString());
186 
187 protected:
188     void mousePressEvent(QMouseEvent *e);
189     void mouseReleaseEvent(QMouseEvent *e);
190     void mouseMoveEvent(QMouseEvent *e);
191     void paintEvent(QPaintEvent *e);
192 
193 private:
194     QColor c;
195     QString t;
196     bool sel;
197 };
198 
199 /*
200 
201 */
202 class ColorPickerPopup : public QFrame
203 {
204     Q_OBJECT
205 
206 public:
207     ColorPickerPopup(int width, bool withColorDialog,
208 		       QWidget *parent = 0);
209     ~ColorPickerPopup();
210 
211     void insertColor(const QColor &col, const QString &text, int index);
212     void exec();
213 
214     void setExecFlag();
215 
216     QColor lastSelected() const;
217 
218     ColorPickerItem *find(const QColor &col) const;
219     QColor color(int index) const;
220 
221 signals:
222     void selected(const QColor &);
223     void hid();
224 
225 public slots:
226     void getColorFromDialog();
227 
228 protected slots:
229     void updateSelected();
230 
231 protected:
232     void keyPressEvent(QKeyEvent *e);
233     void showEvent(QShowEvent *e);
234     void hideEvent(QHideEvent *e);
235     void mouseReleaseEvent(QMouseEvent *e);
236 
237     void regenerateGrid();
238 
239 private:
240     QMap<int, QMap<int, QWidget *> > widgetAt;
241     QList<ColorPickerItem *> items;
242     QGridLayout *grid;
243     ColorPickerButton *moreButton;
244     QEventLoop *eventLoop;
245 
246     int lastPos;
247     int cols;
248     QColor lastSel;
249 };
250 
251 /*!
252     Constructs a QtColorPicker widget. The popup will display a grid
253     with \a cols columns, or if \a cols is -1, the number of columns
254     will be calculated automatically.
255 
256     If \a enableColorDialog is true, the popup will also have a "More"
257     button (signified by an ellipsis "...") that presents a
258     QColorDialog when clicked.
259 
260     After constructing a QtColorPicker, call insertColor() to add
261     individual colors to the popup grid, or call setStandardColors()
262     to add all the standard colors in one go.
263 
264     The \a parent argument is passed to QFrame's constructor.
265 
266     \sa QFrame
267 */
QtColorPicker(QWidget * parent,int cols,bool enableColorDialog)268 QtColorPicker::QtColorPicker(QWidget *parent,
269 			     int cols, bool enableColorDialog)
270     : QPushButton(parent), popup(0), withColorDialog(enableColorDialog)
271 {
272     setFocusPolicy(Qt::StrongFocus);
273     setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
274     setAutoDefault(false);
275     setAutoFillBackground(true);
276     setCheckable(true);
277 
278     // Set text
279     setText(tr("Black"));
280     firstInserted = false;
281 
282     // Create and set icon
283     col = Qt::black;
284     dirty = true;
285 
286     // Create color grid popup and connect to it.
287     popup = new ColorPickerPopup(cols, withColorDialog, this);
288     connect(popup, SIGNAL(selected(const QColor &)),
289 	    SLOT(setCurrentColor(const QColor &)));
290     connect(popup, SIGNAL(hid()), SLOT(popupClosed()));
291 
292     // Connect this push button's pressed() signal.
293     connect(this, SIGNAL(toggled(bool)), SLOT(buttonPressed(bool)));
294 }
295 
296 /*!
297     Destructs the QtColorPicker.
298 */
~QtColorPicker()299 QtColorPicker::~QtColorPicker()
300 {
301 }
302 
303 /*! \internal
304 
305     Pops up the color grid, and makes sure the status of
306     QtColorPicker's button is right.
307 */
buttonPressed(bool toggled)308 void QtColorPicker::buttonPressed(bool toggled)
309 {
310     if (!toggled)
311         return;
312 
313     const QRect desktop = QApplication::desktop()->geometry();
314     // Make sure the popup is inside the desktop.
315     QPoint pos = mapToGlobal(rect().bottomLeft());
316     if (pos.x() < desktop.left())
317        pos.setX(desktop.left());
318     if (pos.y() < desktop.top())
319        pos.setY(desktop.top());
320 
321     if ((pos.x() + popup->sizeHint().width()) > desktop.width())
322        pos.setX(desktop.width() - popup->sizeHint().width());
323     if ((pos.y() + popup->sizeHint().height()) > desktop.bottom())
324        pos.setY(desktop.bottom() - popup->sizeHint().height());
325     popup->move(pos);
326 
327     if (ColorPickerItem *item = popup->find(col))
328         item->setSelected(true);
329 
330     // Remove focus from this widget, preventing the focus rect
331     // from showing when the popup is shown. Order an update to
332     // make sure the focus rect is cleared.
333     clearFocus();
334     update();
335 
336     // Allow keyboard navigation as soon as the popup shows.
337     popup->setFocus();
338 
339     // Execute the popup. The popup will enter the event loop.
340     popup->show();
341 }
342 
343 /*!
344     \internal
345 */
paintEvent(QPaintEvent * e)346 void QtColorPicker::paintEvent(QPaintEvent *e)
347 {
348     if (dirty) {
349         int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize);
350         QPixmap pix(iconSize, iconSize);
351         pix.fill(palette().button().color());
352 
353         QPainter p(&pix);
354 
355         int w = pix.width();			// width of cell in pixels
356         int h = pix.height();			// height of cell in pixels
357         p.setPen(QPen(Qt::gray));
358         p.setBrush(col);
359         p.drawRect(2, 2, w - 5, h - 5);
360         setIcon(QIcon(pix));
361 
362         dirty = false;
363     }
364     QPushButton::paintEvent(e);
365 }
366 
367 /*! \internal
368 
369     Makes sure the button isn't pressed when the popup hides.
370 */
popupClosed()371 void QtColorPicker::popupClosed()
372 {
373     setChecked(false);
374     setFocus();
375 }
376 
377 /*!
378     Returns the currently selected color.
379 
380     \sa text()
381 */
currentColor() const382 QColor QtColorPicker::currentColor() const
383 {
384     return col;
385 }
386 
387 /*!
388     Returns the color at position \a index.
389 */
color(int index) const390 QColor QtColorPicker::color(int index) const
391 {
392     return popup->color(index);
393 }
394 
395 /*!
396     Adds the 17 predefined colors from the Qt namespace.
397 
398     (The names given to the colors, "Black", "White", "Red", etc., are
399     all translatable.)
400 
401     \sa insertColor()
402 */
setStandardColors()403 void QtColorPicker::setStandardColors()
404 {
405     insertColor(Qt::black, tr("Black"));
406     insertColor(Qt::white, tr("White"));
407     insertColor(Qt::red, tr("Red"));
408     insertColor(Qt::darkRed, tr("Dark red"));
409     insertColor(Qt::green, tr("Green"));
410     insertColor(Qt::darkGreen, tr("Dark green"));
411     insertColor(Qt::blue, tr("Blue"));
412     insertColor(Qt::darkBlue, tr("Dark blue"));
413     insertColor(Qt::cyan, tr("Cyan"));
414     insertColor(Qt::darkCyan, tr("Dark cyan"));
415     insertColor(Qt::magenta, tr("Magenta"));
416     insertColor(Qt::darkMagenta, tr("Dark magenta"));
417     insertColor(Qt::yellow, tr("Yellow"));
418     insertColor(Qt::darkYellow, tr("Dark yellow"));
419     insertColor(Qt::gray, tr("Gray"));
420     insertColor(Qt::darkGray, tr("Dark gray"));
421     insertColor(Qt::lightGray, tr("Light gray"));
422 }
423 
424 
425 /*!
426     Makes \a color current. If \a color is not already in the color grid, it
427     is inserted with the text "Custom".
428 
429     This function emits the colorChanged() signal if the new color is
430     valid, and different from the old one.
431 */
setCurrentColor(const QColor & color)432 void QtColorPicker::setCurrentColor(const QColor &color)
433 {
434     if (col == color || !color.isValid())
435 	return;
436 
437     ColorPickerItem *item = popup->find(color);
438     if (!item) {
439 	insertColor(color, tr("Custom"));
440 	item = popup->find(color);
441     }
442 
443     col = color;
444     setText(item->text());
445 
446     dirty = true;
447 
448     popup->hide();
449     repaint();
450 
451     item->setSelected(true);
452     emit colorChanged(color);
453 }
454 
455 /*!
456     Adds the color \a color with the name \a text to the color grid,
457     at position \a index. If index is -1, the color is assigned
458     automatically assigned a position, starting from left to right,
459     top to bottom.
460 */
insertColor(const QColor & color,const QString & text,int index)461 void QtColorPicker::insertColor(const QColor &color, const QString &text, int index)
462 {
463     popup->insertColor(color, text, index);
464     if (!firstInserted) {
465 	col = color;
466 	setText(text);
467 	firstInserted = true;
468     }
469 }
470 
471 /*! \property QtColorPicker::colorDialog
472     \brief Whether the ellipsis "..." (more) button is available.
473 
474     If this property is set to TRUE, the color grid popup will include
475     a "More" button (signified by an ellipsis, "...") which pops up a
476     QColorDialog when clicked. The user will then be able to select
477     any custom color they like.
478 */
setColorDialogEnabled(bool enabled)479 void QtColorPicker::setColorDialogEnabled(bool enabled)
480 {
481     withColorDialog = enabled;
482 }
colorDialogEnabled() const483 bool QtColorPicker::colorDialogEnabled() const
484 {
485     return withColorDialog;
486 }
487 
488 /*!
489     Pops up a color grid with Qt default colors at \a point, using
490     global coordinates. If \a allowCustomColors is true, there will
491     also be a button on the popup that invokes QColorDialog.
492 
493     For example:
494 
495     \code
496         void Drawer::mouseReleaseEvent(QMouseEvent *e)
497         {
498 	    if (e->button() & RightButton) {
499                 QColor color = QtColorPicker::getColor(mapToGlobal(e->pos()));
500             }
501         }
502     \endcode
503 */
getColor(const QPoint & point,bool allowCustomColors)504 QColor QtColorPicker::getColor(const QPoint &point, bool allowCustomColors)
505 {
506     ColorPickerPopup popup(-1, allowCustomColors);
507 
508     popup.insertColor(Qt::black, tr("Black"), 0);
509     popup.insertColor(Qt::white, tr("White"), 1);
510     popup.insertColor(Qt::red, tr("Red"), 2);
511     popup.insertColor(Qt::darkRed, tr("Dark red"), 3);
512     popup.insertColor(Qt::green, tr("Green"), 4);
513     popup.insertColor(Qt::darkGreen, tr("Dark green"), 5);
514     popup.insertColor(Qt::blue, tr("Blue"), 6);
515     popup.insertColor(Qt::darkBlue, tr("Dark blue"), 7);
516     popup.insertColor(Qt::cyan, tr("Cyan"), 8);
517     popup.insertColor(Qt::darkCyan, tr("Dark cyan"), 9);
518     popup.insertColor(Qt::magenta, tr("Magenta"), 10);
519     popup.insertColor(Qt::darkMagenta, tr("Dark magenta"), 11);
520     popup.insertColor(Qt::yellow, tr("Yellow"), 12);
521     popup.insertColor(Qt::darkYellow, tr("Dark yellow"), 13);
522     popup.insertColor(Qt::gray, tr("Gray"), 14);
523     popup.insertColor(Qt::darkGray, tr("Dark gray"), 15);
524     popup.insertColor(Qt::lightGray, tr("Light gray"), 16);
525 
526     popup.move(point);
527     popup.exec();
528     return popup.lastSelected();
529 }
530 
531 /*! \internal
532 
533     Constructs the popup widget.
534 */
ColorPickerPopup(int width,bool withColorDialog,QWidget * parent)535 ColorPickerPopup::ColorPickerPopup(int width, bool withColorDialog,
536 				       QWidget *parent)
537     : QFrame(parent, Qt::Popup)
538 {
539     setFrameStyle(QFrame::StyledPanel);
540     setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
541 
542     setFocusPolicy(Qt::StrongFocus);
543     setMouseTracking(true);
544     cols = width;
545 
546     if (withColorDialog) {
547 	moreButton = new ColorPickerButton(this);
548 	moreButton->setFixedWidth(24);
549 	moreButton->setFixedHeight(21);
550 	moreButton->setFrameRect(QRect(2, 2, 20, 17));
551 	connect(moreButton, SIGNAL(clicked()), SLOT(getColorFromDialog()));
552     } else {
553 	moreButton = 0;
554     }
555 
556     eventLoop = 0;
557     grid = 0;
558     regenerateGrid();
559 }
560 
561 
562 /*! \internal
563 
564     Destructs the popup widget.
565 */
~ColorPickerPopup()566 ColorPickerPopup::~ColorPickerPopup()
567 {
568     if (eventLoop)
569         eventLoop->exit();
570 }
571 
572 /*! \internal
573 
574     If there is an item whole color is equal to \a col, returns a
575     pointer to this item; otherwise returns 0.
576 */
find(const QColor & col) const577 ColorPickerItem *ColorPickerPopup::find(const QColor &col) const
578 {
579     for (int i = 0; i < items.size(); ++i) {
580 	if (items.at(i) && items.at(i)->color() == col)
581 	    return items.at(i);
582     }
583 
584     return 0;
585 }
586 
587 /*! \internal
588 
589     Adds \a item to the grid. The items are added from top-left to
590     bottom-right.
591 */
insertColor(const QColor & col,const QString & text,int index)592 void ColorPickerPopup::insertColor(const QColor &col, const QString &text, int index)
593 {
594     // Don't add colors that we have already.
595     ColorPickerItem *existingItem = find(col);
596     ColorPickerItem *lastSelectedItem = find(lastSelected());
597 
598     if (existingItem) {
599         if (lastSelectedItem && existingItem != lastSelectedItem)
600             lastSelectedItem->setSelected(false);
601         existingItem->setFocus();
602         existingItem->setSelected(true);
603         return;
604     }
605 
606     ColorPickerItem *item = new ColorPickerItem(col, text, this);
607 
608     if (lastSelectedItem) {
609         lastSelectedItem->setSelected(false);
610     }
611     else {
612         item->setSelected(true);
613         lastSel = col;
614     }
615     item->setFocus();
616 
617     connect(item, SIGNAL(selected()), SLOT(updateSelected()));
618 
619     if (index == -1)
620 	index = items.count();
621 
622     items.insert((unsigned int)index, item);
623     regenerateGrid();
624 
625     update();
626 }
627 
628 /*! \internal
629 
630 */
color(int index) const631 QColor ColorPickerPopup::color(int index) const
632 {
633     if (index < 0 || index > (int) items.count() - 1)
634         return QColor();
635 
636     ColorPickerPopup *that = (ColorPickerPopup *)this;
637     return that->items.at(index)->color();
638 }
639 
640 /*! \internal
641 
642 */
exec()643 void ColorPickerPopup::exec()
644 {
645     show();
646 
647     QEventLoop e;
648     eventLoop = &e;
649     (void) e.exec();
650     eventLoop = 0;
651 }
652 
653 /*! \internal
654 
655 */
updateSelected()656 void ColorPickerPopup::updateSelected()
657 {
658     QLayoutItem *layoutItem;
659     int i = 0;
660     while ((layoutItem = grid->itemAt(i)) != 0) {
661 	QWidget *w = layoutItem->widget();
662 	if (w && w->inherits("ColorPickerItem")) {
663 	    ColorPickerItem *litem = reinterpret_cast<ColorPickerItem *>(layoutItem->widget());
664 	    if (litem != sender())
665 		litem->setSelected(false);
666 	}
667 	++i;
668     }
669 
670     if (sender() && sender()->inherits("ColorPickerItem")) {
671 	ColorPickerItem *item = (ColorPickerItem *)sender();
672 	lastSel = item->color();
673 	emit selected(item->color());
674     }
675 
676     hide();
677 }
678 
679 /*! \internal
680 
681 */
mouseReleaseEvent(QMouseEvent * e)682 void ColorPickerPopup::mouseReleaseEvent(QMouseEvent *e)
683 {
684     if (!rect().contains(e->pos()))
685 	hide();
686 }
687 
688 /*! \internal
689 
690     Controls keyboard navigation and selection on the color grid.
691 */
keyPressEvent(QKeyEvent * e)692 void ColorPickerPopup::keyPressEvent(QKeyEvent *e)
693 {
694     int curRow = 0;
695     int curCol = 0;
696 
697     bool foundFocus = false;
698     for (int j = 0; !foundFocus && j < grid->rowCount(); ++j) {
699 	for (int i = 0; !foundFocus && i < grid->columnCount(); ++i) {
700 	    if (widgetAt[j][i] && widgetAt[j][i]->hasFocus()) {
701 		curRow = j;
702 		curCol = i;
703 		foundFocus = true;
704 		break;
705 	    }
706 	}
707     }
708 
709     switch (e->key()) {
710 	case Qt::Key_Left:
711 	    if (curCol > 0) --curCol;
712 	    else if (curRow > 0) { --curRow; curCol = grid->columnCount() - 1; }
713 	    break;
714 	case Qt::Key_Right:
715 	    if (curCol < grid->columnCount() - 1 && widgetAt[curRow][curCol + 1]) ++curCol;
716 	    else if (curRow < grid->rowCount() - 1) { ++curRow; curCol = 0; }
717 	    break;
718 	case Qt::Key_Up:
719 	    if (curRow > 0) --curRow;
720 	    else curCol = 0;
721 	    break;
722 	case Qt::Key_Down:
723 	    if (curRow < grid->rowCount() - 1) {
724 		QWidget *w = widgetAt[curRow + 1][curCol];
725 		if (w) {
726 		    ++curRow;
727 		} else for (int i = 1; i < grid->columnCount(); ++i) {
728 		    if (!widgetAt[curRow + 1][i]) {
729 			curCol = i - 1;
730 			++curRow;
731 			break;
732 		    }
733 		}
734 	    }
735 	    break;
736 	case Qt::Key_Space:
737 	case Qt::Key_Return:
738 	case Qt::Key_Enter: {
739 	    QWidget *w = widgetAt[curRow][curCol];
740 	    if (w && w->inherits("ColorPickerItem")) {
741 		ColorPickerItem *wi = reinterpret_cast<ColorPickerItem *>(w);
742 		wi->setSelected(true);
743 
744 		QLayoutItem *layoutItem;
745                 int i = 0;
746 		while ((layoutItem = grid->itemAt(i)) != 0) {
747 		    QWidget *w = layoutItem->widget();
748 		    if (w && w->inherits("ColorPickerItem")) {
749 			ColorPickerItem *litem
750 			    = reinterpret_cast<ColorPickerItem *>(layoutItem->widget());
751 			if (litem != wi)
752 			    litem->setSelected(false);
753 		    }
754 		    ++i;
755 		}
756 
757 		lastSel = wi->color();
758 		emit selected(wi->color());
759 		hide();
760 	    } else if (w && w->inherits("QPushButton")) {
761 		ColorPickerItem *wi = reinterpret_cast<ColorPickerItem *>(w);
762 		wi->setSelected(true);
763 
764 		QLayoutItem *layoutItem;
765                 int i = 0;
766 		while ((layoutItem = grid->itemAt(i)) != 0) {
767 		    QWidget *w = layoutItem->widget();
768 		    if (w && w->inherits("ColorPickerItem")) {
769 			ColorPickerItem *litem
770 			    = reinterpret_cast<ColorPickerItem *>(layoutItem->widget());
771 			if (litem != wi)
772 			    litem->setSelected(false);
773 		    }
774 		    ++i;
775 		}
776 
777 		lastSel = wi->color();
778 		emit selected(wi->color());
779 		hide();
780 	    }
781 	}
782 	break;
783         case Qt::Key_Escape:
784             hide();
785         break;
786 	default:
787 	    e->ignore();
788 	    break;
789     }
790 
791     widgetAt[curRow][curCol]->setFocus();
792 }
793 
794 /*! \internal
795 
796 */
hideEvent(QHideEvent * e)797 void ColorPickerPopup::hideEvent(QHideEvent *e)
798 {
799     if (eventLoop) {
800 	eventLoop->exit();
801     }
802 
803     setFocus();
804 
805     emit hid();
806     QFrame::hideEvent(e);
807 }
808 
809 /*! \internal
810 
811 */
lastSelected() const812 QColor ColorPickerPopup::lastSelected() const
813 {
814     return lastSel;
815 }
816 
817 /*! \internal
818 
819     Sets focus on the popup to enable keyboard navigation. Draws
820     focusRect and selection rect.
821 */
showEvent(QShowEvent *)822 void ColorPickerPopup::showEvent(QShowEvent *)
823 {
824     bool foundSelected = false;
825     for (int i = 0; i < grid->columnCount(); ++i) {
826 	for (int j = 0; j < grid->rowCount(); ++j) {
827 	    QWidget *w = widgetAt[j][i];
828 	    if (w && w->inherits("ColorPickerItem")) {
829 		if (((ColorPickerItem *)w)->isSelected()) {
830 		    w->setFocus();
831 		    foundSelected = true;
832 		    break;
833 		}
834 	    }
835 	}
836     }
837 
838     if (!foundSelected) {
839 	if (items.count() == 0)
840 	    setFocus();
841 	else
842 	    widgetAt[0][0]->setFocus();
843     }
844 }
845 
846 /*!
847 
848 */
regenerateGrid()849 void ColorPickerPopup::regenerateGrid()
850 {
851     widgetAt.clear();
852 
853     int columns = cols;
854     if (columns == -1)
855 	columns = (int) ceil(sqrt((float) items.count()));
856 
857     // When the number of columns grows, the number of rows will
858     // fall. There's no way to shrink a grid, so we create a new
859     // one.
860     if (grid) delete grid;
861     grid = new QGridLayout(this);
862     grid->setMargin(1);
863     grid->setSpacing(0);
864 
865     int ccol = 0, crow = 0;
866     for (int i = 0; i < items.size(); ++i) {
867         if (items.at(i)) {
868             widgetAt[crow][ccol] = items.at(i);
869             grid->addWidget(items.at(i), crow, ccol++);
870             if (ccol == columns) {
871                 ++crow;
872                 ccol = 0;
873             }
874         }
875     }
876 
877     if (moreButton) {
878 	grid->addWidget(moreButton, crow, ccol);
879 	widgetAt[crow][ccol] = moreButton;
880     }
881     updateGeometry();
882 }
883 
884 /*! \internal
885 
886     Copies the color dialog's currently selected item and emits
887     itemSelected().
888 */
getColorFromDialog()889 void ColorPickerPopup::getColorFromDialog()
890 {
891     QColor col = QColorDialog::getColor(lastSel.rgba(), parentWidget());
892     if (!col.isValid())
893 	return;
894 
895     insertColor(col, tr("Custom"), -1);
896     lastSel = col;
897     emit selected(col);
898 }
899 
900 /*!
901     Constructs a ColorPickerItem whose color is set to \a color, and
902     whose name is set to \a text.
903 */
ColorPickerItem(const QColor & color,const QString & text,QWidget * parent)904 ColorPickerItem::ColorPickerItem(const QColor &color, const QString &text,
905 				     QWidget *parent)
906     : QFrame(parent), c(color), t(text), sel(false)
907 {
908     setToolTip(t);
909     setFixedWidth(24);
910     setFixedHeight(21);
911 }
912 
913 /*!
914     Destructs a ColorPickerItem.
915  */
~ColorPickerItem()916 ColorPickerItem::~ColorPickerItem()
917 {
918 }
919 
920 /*!
921     Returns the item's color.
922 
923     \sa text()
924 */
color() const925 QColor ColorPickerItem::color() const
926 {
927     return c;
928 }
929 
930 /*!
931     Returns the item's text.
932 
933     \sa color()
934 */
text() const935 QString ColorPickerItem::text() const
936 {
937     return t;
938 }
939 
940 /*!
941 
942 */
isSelected() const943 bool ColorPickerItem::isSelected() const
944 {
945     return sel;
946 }
947 
948 /*!
949 
950 */
setSelected(bool selected)951 void ColorPickerItem::setSelected(bool selected)
952 {
953     sel = selected;
954     update();
955 }
956 
957 /*!
958     Sets the item's color to \a color, and its name to \a text.
959 */
setColor(const QColor & color,const QString & text)960 void ColorPickerItem::setColor(const QColor &color, const QString &text)
961 {
962     c = color;
963     t = text;
964     setToolTip(t);
965     update();
966 }
967 
968 /*!
969 
970 */
mouseMoveEvent(QMouseEvent *)971 void ColorPickerItem::mouseMoveEvent(QMouseEvent *)
972 {
973     setFocus();
974     update();
975 }
976 
977 /*!
978 
979 */
mouseReleaseEvent(QMouseEvent *)980 void ColorPickerItem::mouseReleaseEvent(QMouseEvent *)
981 {
982     sel = true;
983     emit selected();
984 }
985 
986 /*!
987 
988 */
mousePressEvent(QMouseEvent *)989 void ColorPickerItem::mousePressEvent(QMouseEvent *)
990 {
991     setFocus();
992     update();
993 }
994 
995 /*!
996 
997 */
paintEvent(QPaintEvent *)998 void ColorPickerItem::paintEvent(QPaintEvent *)
999 {
1000     QPainter p(this);
1001     int w = width();			// width of cell in pixels
1002     int h = height();			// height of cell in pixels
1003 
1004     p.setPen( QPen( Qt::gray, 0, Qt::SolidLine ) );
1005 
1006     if (sel)
1007 	p.drawRect(1, 1, w - 3, h - 3);
1008 
1009     p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) );
1010     p.drawRect(3, 3, w - 7, h - 7);
1011     p.fillRect(QRect(4, 4, w - 8, h - 8), QBrush(c));
1012 
1013     if (hasFocus())
1014 	p.drawRect(0, 0, w - 1, h - 1);
1015 }
1016 
1017 /*!
1018 
1019 */
ColorPickerButton(QWidget * parent)1020 ColorPickerButton::ColorPickerButton(QWidget *parent)
1021     : QFrame(parent)
1022 {
1023     setFrameStyle(StyledPanel);
1024 }
1025 
1026 /*!
1027 
1028 */
mousePressEvent(QMouseEvent *)1029 void ColorPickerButton::mousePressEvent(QMouseEvent *)
1030 {
1031     setFrameShadow(Sunken);
1032     update();
1033 }
1034 
1035 /*!
1036 
1037 */
mouseMoveEvent(QMouseEvent *)1038 void ColorPickerButton::mouseMoveEvent(QMouseEvent *)
1039 {
1040     setFocus();
1041     update();
1042 }
1043 
1044 /*!
1045 
1046 */
mouseReleaseEvent(QMouseEvent *)1047 void ColorPickerButton::mouseReleaseEvent(QMouseEvent *)
1048 {
1049     setFrameShadow(Raised);
1050     repaint();
1051     emit clicked();
1052 }
1053 
1054 /*!
1055 
1056 */
keyPressEvent(QKeyEvent * e)1057 void ColorPickerButton::keyPressEvent(QKeyEvent *e)
1058 {
1059     if (e->key() == Qt::Key_Up
1060 	|| e->key() == Qt::Key_Down
1061 	|| e->key() == Qt::Key_Left
1062 	|| e->key() == Qt::Key_Right) {
1063 	qApp->sendEvent(parent(), e);
1064     } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) {
1065 	setFrameShadow(Sunken);
1066 	update();
1067     } else {
1068 	QFrame::keyPressEvent(e);
1069     }
1070 }
1071 
1072 /*!
1073 
1074 */
keyReleaseEvent(QKeyEvent * e)1075 void ColorPickerButton::keyReleaseEvent(QKeyEvent *e)
1076 {
1077     if (e->key() == Qt::Key_Up
1078 	|| e->key() == Qt::Key_Down
1079 	|| e->key() == Qt::Key_Left
1080 	|| e->key() == Qt::Key_Right) {
1081 	qApp->sendEvent(parent(), e);
1082     } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) {
1083 	setFrameShadow(Raised);
1084 	repaint();
1085 	emit clicked();
1086     } else {
1087 	QFrame::keyReleaseEvent(e);
1088     }
1089 
1090 }
1091 
1092 /*!
1093 
1094 */
focusInEvent(QFocusEvent * e)1095 void ColorPickerButton::focusInEvent(QFocusEvent *e)
1096 {
1097     setFrameShadow(Raised);
1098     update();
1099     QFrame::focusOutEvent(e);
1100 }
1101 
1102 /*!
1103 
1104 */
focusOutEvent(QFocusEvent * e)1105 void ColorPickerButton::focusOutEvent(QFocusEvent *e)
1106 {
1107     setFrameShadow(Raised);
1108     update();
1109     QFrame::focusOutEvent(e);
1110 }
1111 
1112 /*!
1113 
1114 */
paintEvent(QPaintEvent * e)1115 void ColorPickerButton::paintEvent(QPaintEvent *e)
1116 {
1117     QFrame::paintEvent(e);
1118 
1119     QPainter p(this);
1120     p.fillRect(contentsRect(), palette().button());
1121 
1122     QRect r = rect();
1123 
1124     int offset = frameShadow() == Sunken ? 1 : 0;
1125 
1126     QPen pen(palette().buttonText(), 1);
1127     p.setPen(pen);
1128 
1129     p.drawRect(r.center().x() + offset - 4, r.center().y() + offset, 1, 1);
1130     p.drawRect(r.center().x() + offset    , r.center().y() + offset, 1, 1);
1131     p.drawRect(r.center().x() + offset + 4, r.center().y() + offset, 1, 1);
1132     if (hasFocus()) {
1133 	p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) );
1134 	p.drawRect(0, 0, width() - 1, height() - 1);
1135     }
1136 
1137     p.end();
1138 
1139 }
1140 
1141 #include "qtcolorpicker.moc"
1142 
1143