PageRenderTime 104ms CodeModel.GetById 14ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 1ms

/thirdparty/qtcolorpicker-2.6/qtcolorpicker.cpp

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