From 93123b2cb3a1c3b9a15de7f5b34198dc6ebacc23 Mon Sep 17 00:00:00 2001 From: Lukáš Lalinský Date: Tue, 7 Apr 2009 18:39:17 +0200 Subject: Support for custom table background colors --- src/items/database/databasetable.cpp | 17 +- src/items/database/databasetable.h | 5 + src/items/database/databasetableproperties.cpp | 33 +- src/items/database/databasetableproperties.h | 1 + src/utils/colorpicker/colorpicker.pri | 7 + src/utils/colorpicker/qtcolorpicker.cpp | 1144 ++++++++++++++++++++++++ src/utils/colorpicker/qtcolorpicker.h | 121 +++ src/utils/utils.pri | 1 + 8 files changed, 1326 insertions(+), 3 deletions(-) create mode 100644 src/utils/colorpicker/colorpicker.pri create mode 100644 src/utils/colorpicker/qtcolorpicker.cpp create mode 100644 src/utils/colorpicker/qtcolorpicker.h diff --git a/src/items/database/databasetable.cpp b/src/items/database/databasetable.cpp index 8f36308..7f73b0d 100644 --- a/src/items/database/databasetable.cpp +++ b/src/items/database/databasetable.cpp @@ -31,6 +31,7 @@ DatabaseTable::DatabaseTable(DiagramItem *parent) setFlag(ItemIsMovable); setFlag(ItemIsSelectable); setHub(new BoxSideHub(this)); + m_color = Qt::white; m_columnList = new ColumnList(this); connect(m_columnList, SIGNAL(columnInserted(int)), this, SLOT(updateLayout())); connect(m_columnList, SIGNAL(columnRemoved(int)), this, SLOT(updateLayout())); @@ -67,8 +68,9 @@ DatabaseTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QPen pen(QPen(QColor(0, 0, 0))); pen.setJoinStyle(Qt::MiterJoin); + pen.setWidth(1); QPen borderPen(pen); - borderPen.setWidthF(1.5); + borderPen.setWidthF(1.3); if (!document()->isPrinting() && isSelected()) { borderPen.setColor(QColor(0, 96, 255)); } @@ -76,7 +78,7 @@ DatabaseTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, painter->setPen(pen); painter->setFont(font); - painter->fillRect(m_outerRect, QColor(255, 255, 255)); + painter->fillRect(m_outerRect, color()); // Draw the table name painter->fillRect(m_nameBgRect, QColor(205, 205, 205)); @@ -119,6 +121,14 @@ DatabaseTable::setName(const QString &name) emit propertyChanged("name", name); } +void +DatabaseTable::setColor(const QColor &color) +{ + m_color = color; + updateLayout(); + emit propertyChanged("color", color); +} + void DatabaseTable::setInitialName(int counter) { @@ -217,6 +227,8 @@ DatabaseTable::loadFromXml(QDomElement element, DiagramDocument *document) QDomElement tableElement = element.firstChildElement("table"); if (!tableElement.isNull()) { setName(readStringElement(tableElement, "name")); + QColor color(readStringElement(tableElement, "color")); + setColor(color.isValid() ? color : Qt::white); QDomElement columnListElement = tableElement.firstChildElement("column-list"); QDomElement columnElement = columnListElement.firstChildElement("column"); while (!columnElement.isNull()) { @@ -239,6 +251,7 @@ DatabaseTable::saveToXml(QDomDocument doc, QDomElement element) QDomElement tableElement = doc.createElement("table"); element.appendChild(tableElement); appendStringElement(doc, tableElement, "name", name()); + appendStringElement(doc, tableElement, "color", color().name()); QDomElement columnListElement = doc.createElement("column-list"); tableElement.appendChild(columnListElement); foreach (Column *column, columnList()->columns()) { diff --git a/src/items/database/databasetable.h b/src/items/database/databasetable.h index df3d630..59eaa31 100644 --- a/src/items/database/databasetable.h +++ b/src/items/database/databasetable.h @@ -30,6 +30,7 @@ class DatabaseTable : public DiagramObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QColor color READ color WRITE setColor) public: DatabaseTable(DiagramItem *parent = 0); @@ -41,6 +42,9 @@ public: void setName(const QString &name); void setInitialName(int counter); + QColor color() const { return m_color; } + void setColor(const QColor &color); + QList primaryKeys() const; ColumnList *columnList() const { return m_columnList; } @@ -71,6 +75,7 @@ protected: private: QString m_name; + QColor m_color; ColumnList *m_columnList; QList m_columnLabels; diff --git a/src/items/database/databasetableproperties.cpp b/src/items/database/databasetableproperties.cpp index 9b92f13..8bce7b5 100644 --- a/src/items/database/databasetableproperties.cpp +++ b/src/items/database/databasetableproperties.cpp @@ -24,6 +24,7 @@ #include "databasetable.h" #include "databasetableproperties.h" #include "diagramdocument.h" +#include "utils/colorpicker/qtcolorpicker.h" class DatabaseTableProperties::PrivateData { @@ -37,6 +38,7 @@ public: QPushButton *removeColumnButton; QPushButton *moveColumnUpButton; QPushButton *moveColumnDownButton; + QtColorPicker *colorPicker; }; DatabaseTableProperties::DatabaseTableProperties(QWidget *parent) @@ -62,7 +64,25 @@ DatabaseTableProperties::createTablePage() connect(d->nameEdit, SIGNAL(textEdited(const QString &)), SLOT(setTableName(const QString &))); layout->addWidget(new QLabel(tr("Name:"), page), 0, 0); layout->addWidget(d->nameEdit, 0, 1); - layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 1, 0, 1, 2); + + d->colorPicker = new QtColorPicker(page); + d->colorPicker->insertColor(Qt::white, tr("White")); + d->colorPicker->insertColor(QColor("#AAAAAA"), tr("Gray")); + d->colorPicker->insertColor(QColor("#777777"), tr("Gray")); + d->colorPicker->insertColor(QColor("#333333"), tr("Gray")); + d->colorPicker->insertColor(QColor("#FFCCCC"), tr("Red")); + d->colorPicker->insertColor(QColor("#CC9900"), tr("Brown")); + d->colorPicker->insertColor(QColor("#FFCCFF"), tr("Pink")); + d->colorPicker->insertColor(QColor("#FFFFCC"), tr("Yellow")); + d->colorPicker->insertColor(QColor("#CCFFCC"), tr("Green")); + d->colorPicker->insertColor(QColor("#CCECFF"), tr("Blue")); + connect(d->colorPicker, SIGNAL(colorChanged(const QColor &)), SLOT(setTableColor(const QColor &))); + layout->addWidget(new QLabel(tr("Color:"), page), 1, 0); + QHBoxLayout *hbox = new QHBoxLayout(); + hbox->addWidget(d->colorPicker); + hbox->addStretch(); + layout->addLayout(hbox, 1, 1); + layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 2, 0, 2, 2); return page; } @@ -106,6 +126,7 @@ DatabaseTableProperties::switchCurrentItem(DiagramItem *oldItem, DiagramItem *) DatabaseTable *table = currentTable(); if (table) { d->nameEdit->setText(table->name()); + d->colorPicker->setCurrentColor(table->color()); d->columnListView->setColumnList(table->columnList()); connect(table, SIGNAL(propertyChanged(const QString &, const QVariant &)), SLOT(updateProperty(const QString &, const QVariant &))); } @@ -122,6 +143,9 @@ DatabaseTableProperties::updateProperty(const QString &name, const QVariant &val if (name == "name") { d->nameEdit->setText(value.toString()); } + else if (name == "color") { + d->colorPicker->setCurrentColor(qVariantValue(value)); + } } void @@ -131,6 +155,13 @@ DatabaseTableProperties::setTableName(const QString &name) table->document()->undoStack()->push(new SetObjectPropertyCommand(table, "name", name)); } +void +DatabaseTableProperties::setTableColor(const QColor &color) +{ + DatabaseTable *table = currentTable(); + table->document()->undoStack()->push(new SetObjectPropertyCommand(table, "color", color)); +} + void DatabaseTableProperties::updateColumnSelection() { diff --git a/src/items/database/databasetableproperties.h b/src/items/database/databasetableproperties.h index cd7fa14..19faf0a 100644 --- a/src/items/database/databasetableproperties.h +++ b/src/items/database/databasetableproperties.h @@ -35,6 +35,7 @@ protected slots: void updateProperty(const QString &name, const QVariant &value); void updateColumnSelection(); void setTableName(const QString &name); + void setTableColor(const QColor &color); private: class PrivateData; diff --git a/src/utils/colorpicker/colorpicker.pri b/src/utils/colorpicker/colorpicker.pri new file mode 100644 index 0000000..ffed503 --- /dev/null +++ b/src/utils/colorpicker/colorpicker.pri @@ -0,0 +1,7 @@ +DEPENDPATH += $$PWD + +SOURCES += \ + qtcolorpicker.cpp + +HEADERS += \ + qtcolorpicker.h diff --git a/src/utils/colorpicker/qtcolorpicker.cpp b/src/utils/colorpicker/qtcolorpicker.cpp new file mode 100644 index 0000000..2a4fff1 --- /dev/null +++ b/src/utils/colorpicker/qtcolorpicker.cpp @@ -0,0 +1,1144 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qtcolorpicker.h" + +/*! \class QtColorPicker + + \brief The QtColorPicker class provides a widget for selecting + colors from a popup color grid. + + Users can invoke the color picker by clicking on it, or by + navigating to it and pressing Space. They can use the mouse or + arrow keys to navigate between colors on the grid, and select a + color by clicking or by pressing Enter or Space. The + colorChanged() signal is emitted whenever the color picker's color + changes. + + The widget also supports negative selection: Users can click and + hold the mouse button on the QtColorPicker widget, then move the + mouse over the color grid and release the mouse button over the + color they wish to select. + + The color grid shows a customized selection of colors. An optional + ellipsis "..." button (signifying "more") can be added at the + bottom of the grid; if the user presses this, a QColorDialog pops + up and lets them choose any color they like. This button is made + available by using setColorDialogEnabled(). + + When a color is selected, the QtColorPicker widget shows the color + and its name. If the name cannot be determined, the translatable + name "Custom" is used. + + The QtColorPicker object is optionally initialized with the number + of columns in the color grid. Colors are then added left to right, + top to bottom using insertColor(). If the number of columns is not + set, QtColorPicker calculates the number of columns and rows that + will make the grid as square as possible. + + \code + DrawWidget::DrawWidget(QWidget *parent, const char *name) + { + QtColorPicker *picker = new QtColorPicker(this); + picker->insertColor(red, "Red")); + picker->insertColor(QColor("green"), "Green")); + picker->insertColor(QColor(0, 0, 255), "Blue")); + picker->insertColor(white); + + connect(colors, SIGNAL(colorChanged(const QColor &)), SLOT(setCurrentColor(const QColor &))); + } + \endcode + + An alternative to adding colors manually is to initialize the grid + with QColorDialog's standard colors using setStandardColors(). + + QtColorPicker also provides a the static function getColor(), + which pops up the grid of standard colors at any given point. + + \img colorpicker1.png + \img colorpicker2.png + + \sa QColorDialog +*/ + +/*! \fn QtColorPicker::colorChanged(const QColor &color) + + This signal is emitted when the QtColorPicker's color is changed. + \a color is the new color. + + To obtain the color's name, use text(). +*/ + +/* + A class that acts very much like a QPushButton. It's not styled, + so we can expect the exact same look, feel and geometry + everywhere. Also, this button always emits clicked on + mouseRelease, even if the mouse button was not pressed inside the + widget. +*/ +class ColorPickerButton : public QFrame +{ + Q_OBJECT + +public: + ColorPickerButton(QWidget *parent); + +signals: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *e); + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); +}; + +/* + This class represents each "color" or item in the color grid. +*/ +class ColorPickerItem : public QFrame +{ + Q_OBJECT + +public: + ColorPickerItem(const QColor &color = Qt::white, const QString &text = QString::null, + QWidget *parent = 0); + ~ColorPickerItem(); + + QColor color() const; + QString text() const; + + void setSelected(bool); + bool isSelected() const; +signals: + void clicked(); + void selected(); + +public slots: + void setColor(const QColor &color, const QString &text = QString()); + +protected: + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void paintEvent(QPaintEvent *e); + +private: + QColor c; + QString t; + bool sel; +}; + +/* + +*/ +class ColorPickerPopup : public QFrame +{ + Q_OBJECT + +public: + ColorPickerPopup(int width, bool withColorDialog, + QWidget *parent = 0); + ~ColorPickerPopup(); + + void insertColor(const QColor &col, const QString &text, int index); + void exec(); + + void setExecFlag(); + + QColor lastSelected() const; + + ColorPickerItem *find(const QColor &col) const; + QColor color(int index) const; + +signals: + void selected(const QColor &); + void hid(); + +public slots: + void getColorFromDialog(); + +protected slots: + void updateSelected(); + +protected: + void keyPressEvent(QKeyEvent *e); + void showEvent(QShowEvent *e); + void hideEvent(QHideEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + + void regenerateGrid(); + +private: + QMap > widgetAt; + QList items; + QGridLayout *grid; + ColorPickerButton *moreButton; + QEventLoop *eventLoop; + + int lastPos; + int cols; + QColor lastSel; +}; + +/*! + Constructs a QtColorPicker widget. The popup will display a grid + with \a cols columns, or if \a cols is -1, the number of columns + will be calculated automatically. + + If \a enableColorDialog is true, the popup will also have a "More" + button (signified by an ellipsis "...") that presents a + QColorDialog when clicked. + + After constructing a QtColorPicker, call insertColor() to add + individual colors to the popup grid, or call setStandardColors() + to add all the standard colors in one go. + + The \a parent argument is passed to QFrame's constructor. + + \sa QFrame +*/ +QtColorPicker::QtColorPicker(QWidget *parent, + int cols, bool enableColorDialog) + : QPushButton(parent), popup(0), withColorDialog(enableColorDialog) +{ + setFocusPolicy(Qt::StrongFocus); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + setAutoDefault(false); + setAutoFillBackground(true); + setCheckable(true); + + // Set text + setText(tr("Black")); + firstInserted = false; + + // Create and set icon + col = Qt::black; + dirty = true; + + // Create color grid popup and connect to it. + popup = new ColorPickerPopup(cols, withColorDialog, this); + connect(popup, SIGNAL(selected(const QColor &)), + SLOT(setCurrentColor(const QColor &))); + connect(popup, SIGNAL(hid()), SLOT(popupClosed())); + + // Connect this push button's pressed() signal. + connect(this, SIGNAL(toggled(bool)), SLOT(buttonPressed(bool))); +} + +/*! + Destructs the QtColorPicker. +*/ +QtColorPicker::~QtColorPicker() +{ +} + +/*! \internal + + Pops up the color grid, and makes sure the status of + QtColorPicker's button is right. +*/ +void QtColorPicker::buttonPressed(bool toggled) +{ + if (!toggled) + return; + + const QRect desktop = QApplication::desktop()->geometry(); + // Make sure the popup is inside the desktop. + QPoint pos = mapToGlobal(rect().bottomLeft()); + if (pos.x() < desktop.left()) + pos.setX(desktop.left()); + if (pos.y() < desktop.top()) + pos.setY(desktop.top()); + + if ((pos.x() + popup->sizeHint().width()) > desktop.width()) + pos.setX(desktop.width() - popup->sizeHint().width()); + if ((pos.y() + popup->sizeHint().height()) > desktop.bottom()) + pos.setY(desktop.bottom() - popup->sizeHint().height()); + popup->move(pos); + + if (ColorPickerItem *item = popup->find(col)) + item->setSelected(true); + + // Remove focus from this widget, preventing the focus rect + // from showing when the popup is shown. Order an update to + // make sure the focus rect is cleared. + clearFocus(); + update(); + + // Allow keyboard navigation as soon as the popup shows. + popup->setFocus(); + + // Execute the popup. The popup will enter the event loop. + popup->show(); +} + +/*! + \internal +*/ +void QtColorPicker::paintEvent(QPaintEvent *e) +{ + if (dirty) { + int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); + QPixmap pix(iconSize, iconSize); + pix.fill(palette().button().color()); + + QPainter p(&pix); + + int w = pix.width(); // width of cell in pixels + int h = pix.height(); // height of cell in pixels + p.setPen(QPen(Qt::gray)); + p.setBrush(col); + p.drawRect(2, 2, w - 5, h - 5); + setIcon(QIcon(pix)); + + dirty = false; + } + QPushButton::paintEvent(e); +} + +/*! \internal + + Makes sure the button isn't pressed when the popup hides. +*/ +void QtColorPicker::popupClosed() +{ + setChecked(false); + setFocus(); +} + +/*! + Returns the currently selected color. + + \sa text() +*/ +QColor QtColorPicker::currentColor() const +{ + return col; +} + +/*! + Returns the color at position \a index. +*/ +QColor QtColorPicker::color(int index) const +{ + return popup->color(index); +} + +/*! + Adds the 17 predefined colors from the Qt namespace. + + (The names given to the colors, "Black", "White", "Red", etc., are + all translatable.) + + \sa insertColor() +*/ +void QtColorPicker::setStandardColors() +{ + insertColor(Qt::black, tr("Black")); + insertColor(Qt::white, tr("White")); + insertColor(Qt::red, tr("Red")); + insertColor(Qt::darkRed, tr("Dark red")); + insertColor(Qt::green, tr("Green")); + insertColor(Qt::darkGreen, tr("Dark green")); + insertColor(Qt::blue, tr("Blue")); + insertColor(Qt::darkBlue, tr("Dark blue")); + insertColor(Qt::cyan, tr("Cyan")); + insertColor(Qt::darkCyan, tr("Dark cyan")); + insertColor(Qt::magenta, tr("Magenta")); + insertColor(Qt::darkMagenta, tr("Dark magenta")); + insertColor(Qt::yellow, tr("Yellow")); + insertColor(Qt::darkYellow, tr("Dark yellow")); + insertColor(Qt::gray, tr("Gray")); + insertColor(Qt::darkGray, tr("Dark gray")); + insertColor(Qt::lightGray, tr("Light gray")); +} + + +/*! + Makes \a color current. If \a color is not already in the color grid, it + is inserted with the text "Custom". + + This function emits the colorChanged() signal if the new color is + valid, and different from the old one. +*/ +void QtColorPicker::setCurrentColor(const QColor &color) +{ + if (col == color || !color.isValid()) + return; + + ColorPickerItem *item = popup->find(color); + if (!item) { + insertColor(color, tr("Custom")); + item = popup->find(color); + } + + col = color; + setText(item->text()); + + dirty = true; + + popup->hide(); + repaint(); + + item->setSelected(true); + emit colorChanged(color); +} + +/*! + Adds the color \a color with the name \a text to the color grid, + at position \a index. If index is -1, the color is assigned + automatically assigned a position, starting from left to right, + top to bottom. +*/ +void QtColorPicker::insertColor(const QColor &color, const QString &text, int index) +{ + popup->insertColor(color, text, index); + if (!firstInserted) { + col = color; + setText(text); + firstInserted = true; + } +} + +/*! \property QtColorPicker::colorDialog + \brief Whether the ellipsis "..." (more) button is available. + + If this property is set to TRUE, the color grid popup will include + a "More" button (signified by an ellipsis, "...") which pops up a + QColorDialog when clicked. The user will then be able to select + any custom color they like. +*/ +void QtColorPicker::setColorDialogEnabled(bool enabled) +{ + withColorDialog = enabled; +} +bool QtColorPicker::colorDialogEnabled() const +{ + return withColorDialog; +} + +/*! + Pops up a color grid with Qt default colors at \a point, using + global coordinates. If \a allowCustomColors is true, there will + also be a button on the popup that invokes QColorDialog. + + For example: + + \code + void Drawer::mouseReleaseEvent(QMouseEvent *e) + { + if (e->button() & RightButton) { + QColor color = QtColorPicker::getColor(mapToGlobal(e->pos())); + } + } + \endcode +*/ +QColor QtColorPicker::getColor(const QPoint &point, bool allowCustomColors) +{ + ColorPickerPopup popup(-1, allowCustomColors); + + popup.insertColor(Qt::black, tr("Black"), 0); + popup.insertColor(Qt::white, tr("White"), 1); + popup.insertColor(Qt::red, tr("Red"), 2); + popup.insertColor(Qt::darkRed, tr("Dark red"), 3); + popup.insertColor(Qt::green, tr("Green"), 4); + popup.insertColor(Qt::darkGreen, tr("Dark green"), 5); + popup.insertColor(Qt::blue, tr("Blue"), 6); + popup.insertColor(Qt::darkBlue, tr("Dark blue"), 7); + popup.insertColor(Qt::cyan, tr("Cyan"), 8); + popup.insertColor(Qt::darkCyan, tr("Dark cyan"), 9); + popup.insertColor(Qt::magenta, tr("Magenta"), 10); + popup.insertColor(Qt::darkMagenta, tr("Dark magenta"), 11); + popup.insertColor(Qt::yellow, tr("Yellow"), 12); + popup.insertColor(Qt::darkYellow, tr("Dark yellow"), 13); + popup.insertColor(Qt::gray, tr("Gray"), 14); + popup.insertColor(Qt::darkGray, tr("Dark gray"), 15); + popup.insertColor(Qt::lightGray, tr("Light gray"), 16); + + popup.move(point); + popup.exec(); + return popup.lastSelected(); +} + +/*! \internal + + Constructs the popup widget. +*/ +ColorPickerPopup::ColorPickerPopup(int width, bool withColorDialog, + QWidget *parent) + : QFrame(parent, Qt::Popup) +{ + setFrameStyle(QFrame::StyledPanel); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); + cols = width; + + if (withColorDialog) { + moreButton = new ColorPickerButton(this); + moreButton->setFixedWidth(24); + moreButton->setFixedHeight(21); + moreButton->setFrameRect(QRect(2, 2, 20, 17)); + connect(moreButton, SIGNAL(clicked()), SLOT(getColorFromDialog())); + } else { + moreButton = 0; + } + + eventLoop = 0; + grid = 0; + regenerateGrid(); +} + + +/*! \internal + + Destructs the popup widget. +*/ +ColorPickerPopup::~ColorPickerPopup() +{ + if (eventLoop) + eventLoop->exit(); +} + +/*! \internal + + If there is an item whole color is equal to \a col, returns a + pointer to this item; otherwise returns 0. +*/ +ColorPickerItem *ColorPickerPopup::find(const QColor &col) const +{ + for (int i = 0; i < items.size(); ++i) { + if (items.at(i) && items.at(i)->color() == col) + return items.at(i); + } + + return 0; +} + +/*! \internal + + Adds \a item to the grid. The items are added from top-left to + bottom-right. +*/ +void ColorPickerPopup::insertColor(const QColor &col, const QString &text, int index) +{ + // Don't add colors that we have already. + ColorPickerItem *existingItem = find(col); + ColorPickerItem *lastSelectedItem = find(lastSelected()); + + if (existingItem) { + if (lastSelectedItem && existingItem != lastSelectedItem) + lastSelectedItem->setSelected(false); + existingItem->setFocus(); + existingItem->setSelected(true); + return; + } + + ColorPickerItem *item = new ColorPickerItem(col, text, this); + + if (lastSelectedItem) { + lastSelectedItem->setSelected(false); + } + else { + item->setSelected(true); + lastSel = col; + } + item->setFocus(); + + connect(item, SIGNAL(selected()), SLOT(updateSelected())); + + if (index == -1) + index = items.count(); + + items.insert((unsigned int)index, item); + regenerateGrid(); + + update(); +} + +/*! \internal + +*/ +QColor ColorPickerPopup::color(int index) const +{ + if (index < 0 || index > (int) items.count() - 1) + return QColor(); + + ColorPickerPopup *that = (ColorPickerPopup *)this; + return that->items.at(index)->color(); +} + +/*! \internal + +*/ +void ColorPickerPopup::exec() +{ + show(); + + QEventLoop e; + eventLoop = &e; + (void) e.exec(); + eventLoop = 0; +} + +/*! \internal + +*/ +void ColorPickerPopup::updateSelected() +{ + QLayoutItem *layoutItem; + int i = 0; + while ((layoutItem = grid->itemAt(i)) != 0) { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *litem = reinterpret_cast(layoutItem->widget()); + if (litem != sender()) + litem->setSelected(false); + } + ++i; + } + + if (sender() && sender()->inherits("ColorPickerItem")) { + ColorPickerItem *item = (ColorPickerItem *)sender(); + lastSel = item->color(); + emit selected(item->color()); + } + + hide(); +} + +/*! \internal + +*/ +void ColorPickerPopup::mouseReleaseEvent(QMouseEvent *e) +{ + if (!rect().contains(e->pos())) + hide(); +} + +/*! \internal + + Controls keyboard navigation and selection on the color grid. +*/ +void ColorPickerPopup::keyPressEvent(QKeyEvent *e) +{ + int curRow = 0; + int curCol = 0; + + bool foundFocus = false; + for (int j = 0; !foundFocus && j < grid->rowCount(); ++j) { + for (int i = 0; !foundFocus && i < grid->columnCount(); ++i) { + if (widgetAt[j][i] && widgetAt[j][i]->hasFocus()) { + curRow = j; + curCol = i; + foundFocus = true; + break; + } + } + } + + switch (e->key()) { + case Qt::Key_Left: + if (curCol > 0) --curCol; + else if (curRow > 0) { --curRow; curCol = grid->columnCount() - 1; } + break; + case Qt::Key_Right: + if (curCol < grid->columnCount() - 1 && widgetAt[curRow][curCol + 1]) ++curCol; + else if (curRow < grid->rowCount() - 1) { ++curRow; curCol = 0; } + break; + case Qt::Key_Up: + if (curRow > 0) --curRow; + else curCol = 0; + break; + case Qt::Key_Down: + if (curRow < grid->rowCount() - 1) { + QWidget *w = widgetAt[curRow + 1][curCol]; + if (w) { + ++curRow; + } else for (int i = 1; i < grid->columnCount(); ++i) { + if (!widgetAt[curRow + 1][i]) { + curCol = i - 1; + ++curRow; + break; + } + } + } + break; + case Qt::Key_Space: + case Qt::Key_Return: + case Qt::Key_Enter: { + QWidget *w = widgetAt[curRow][curCol]; + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *wi = reinterpret_cast(w); + wi->setSelected(true); + + QLayoutItem *layoutItem; + int i = 0; + while ((layoutItem = grid->itemAt(i)) != 0) { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *litem + = reinterpret_cast(layoutItem->widget()); + if (litem != wi) + litem->setSelected(false); + } + ++i; + } + + lastSel = wi->color(); + emit selected(wi->color()); + hide(); + } else if (w && w->inherits("QPushButton")) { + ColorPickerItem *wi = reinterpret_cast(w); + wi->setSelected(true); + + QLayoutItem *layoutItem; + int i = 0; + while ((layoutItem = grid->itemAt(i)) != 0) { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *litem + = reinterpret_cast(layoutItem->widget()); + if (litem != wi) + litem->setSelected(false); + } + ++i; + } + + lastSel = wi->color(); + emit selected(wi->color()); + hide(); + } + } + break; + case Qt::Key_Escape: + hide(); + break; + default: + e->ignore(); + break; + } + + widgetAt[curRow][curCol]->setFocus(); +} + +/*! \internal + +*/ +void ColorPickerPopup::hideEvent(QHideEvent *e) +{ + if (eventLoop) { + eventLoop->exit(); + } + + setFocus(); + + emit hid(); + QFrame::hideEvent(e); +} + +/*! \internal + +*/ +QColor ColorPickerPopup::lastSelected() const +{ + return lastSel; +} + +/*! \internal + + Sets focus on the popup to enable keyboard navigation. Draws + focusRect and selection rect. +*/ +void ColorPickerPopup::showEvent(QShowEvent *) +{ + bool foundSelected = false; + for (int i = 0; i < grid->columnCount(); ++i) { + for (int j = 0; j < grid->rowCount(); ++j) { + QWidget *w = widgetAt[j][i]; + if (w && w->inherits("ColorPickerItem")) { + if (((ColorPickerItem *)w)->isSelected()) { + w->setFocus(); + foundSelected = true; + break; + } + } + } + } + + if (!foundSelected) { + if (items.count() == 0) + setFocus(); + else + widgetAt[0][0]->setFocus(); + } +} + +/*! + +*/ +void ColorPickerPopup::regenerateGrid() +{ + widgetAt.clear(); + + int columns = cols; + if (columns == -1) + columns = (int) ceil(sqrt((float) items.count())); + + // When the number of columns grows, the number of rows will + // fall. There's no way to shrink a grid, so we create a new + // one. + if (grid) delete grid; + grid = new QGridLayout(this); + grid->setMargin(1); + grid->setSpacing(0); + + int ccol = 0, crow = 0; + for (int i = 0; i < items.size(); ++i) { + if (items.at(i)) { + widgetAt[crow][ccol] = items.at(i); + grid->addWidget(items.at(i), crow, ccol++); + if (ccol == columns) { + ++crow; + ccol = 0; + } + } + } + + if (moreButton) { + grid->addWidget(moreButton, crow, ccol); + widgetAt[crow][ccol] = moreButton; + } + updateGeometry(); +} + +/*! \internal + + Copies the color dialog's currently selected item and emits + itemSelected(). +*/ +void ColorPickerPopup::getColorFromDialog() +{ + bool ok; + QRgb rgb = QColorDialog::getRgba(lastSel.rgba(), &ok, parentWidget()); + if (!ok) + return; + + QColor col = QColor::fromRgba(rgb); + insertColor(col, tr("Custom"), -1); + lastSel = col; + emit selected(col); +} + +/*! + Constructs a ColorPickerItem whose color is set to \a color, and + whose name is set to \a text. +*/ +ColorPickerItem::ColorPickerItem(const QColor &color, const QString &text, + QWidget *parent) + : QFrame(parent), c(color), t(text), sel(false) +{ + setToolTip(t); + setFixedWidth(24); + setFixedHeight(21); +} + +/*! + Destructs a ColorPickerItem. + */ +ColorPickerItem::~ColorPickerItem() +{ +} + +/*! + Returns the item's color. + + \sa text() +*/ +QColor ColorPickerItem::color() const +{ + return c; +} + +/*! + Returns the item's text. + + \sa color() +*/ +QString ColorPickerItem::text() const +{ + return t; +} + +/*! + +*/ +bool ColorPickerItem::isSelected() const +{ + return sel; +} + +/*! + +*/ +void ColorPickerItem::setSelected(bool selected) +{ + sel = selected; + update(); +} + +/*! + Sets the item's color to \a color, and its name to \a text. +*/ +void ColorPickerItem::setColor(const QColor &color, const QString &text) +{ + c = color; + t = text; + setToolTip(t); + update(); +} + +/*! + +*/ +void ColorPickerItem::mouseMoveEvent(QMouseEvent *) +{ + setFocus(); + update(); +} + +/*! + +*/ +void ColorPickerItem::mouseReleaseEvent(QMouseEvent *) +{ + sel = true; + emit selected(); +} + +/*! + +*/ +void ColorPickerItem::mousePressEvent(QMouseEvent *) +{ + setFocus(); + update(); +} + +/*! + +*/ +void ColorPickerItem::paintEvent(QPaintEvent *) +{ + QPainter p(this); + int w = width(); // width of cell in pixels + int h = height(); // height of cell in pixels + + p.setPen( QPen( Qt::gray, 0, Qt::SolidLine ) ); + + if (sel) + p.drawRect(1, 1, w - 3, h - 3); + + p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) ); + p.drawRect(3, 3, w - 7, h - 7); + p.fillRect(QRect(4, 4, w - 8, h - 8), QBrush(c)); + + if (hasFocus()) + p.drawRect(0, 0, w - 1, h - 1); +} + +/*! + +*/ +ColorPickerButton::ColorPickerButton(QWidget *parent) + : QFrame(parent) +{ + setFrameStyle(StyledPanel); +} + +/*! + +*/ +void ColorPickerButton::mousePressEvent(QMouseEvent *) +{ + setFrameShadow(Sunken); + update(); +} + +/*! + +*/ +void ColorPickerButton::mouseMoveEvent(QMouseEvent *) +{ + setFocus(); + update(); +} + +/*! + +*/ +void ColorPickerButton::mouseReleaseEvent(QMouseEvent *) +{ + setFrameShadow(Raised); + repaint(); + emit clicked(); +} + +/*! + +*/ +void ColorPickerButton::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Up + || e->key() == Qt::Key_Down + || e->key() == Qt::Key_Left + || e->key() == Qt::Key_Right) { + qApp->sendEvent(parent(), e); + } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) { + setFrameShadow(Sunken); + update(); + } else { + QFrame::keyPressEvent(e); + } +} + +/*! + +*/ +void ColorPickerButton::keyReleaseEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Up + || e->key() == Qt::Key_Down + || e->key() == Qt::Key_Left + || e->key() == Qt::Key_Right) { + qApp->sendEvent(parent(), e); + } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) { + setFrameShadow(Raised); + repaint(); + emit clicked(); + } else { + QFrame::keyReleaseEvent(e); + } + +} + +/*! + +*/ +void ColorPickerButton::focusInEvent(QFocusEvent *e) +{ + setFrameShadow(Raised); + update(); + QFrame::focusOutEvent(e); +} + +/*! + +*/ +void ColorPickerButton::focusOutEvent(QFocusEvent *e) +{ + setFrameShadow(Raised); + update(); + QFrame::focusOutEvent(e); +} + +/*! + +*/ +void ColorPickerButton::paintEvent(QPaintEvent *e) +{ + QFrame::paintEvent(e); + + QPainter p(this); + p.fillRect(contentsRect(), palette().button()); + + QRect r = rect(); + + int offset = frameShadow() == Sunken ? 1 : 0; + + QPen pen(palette().buttonText(), 1); + p.setPen(pen); + + p.drawRect(r.center().x() + offset - 4, r.center().y() + offset, 1, 1); + p.drawRect(r.center().x() + offset , r.center().y() + offset, 1, 1); + p.drawRect(r.center().x() + offset + 4, r.center().y() + offset, 1, 1); + if (hasFocus()) { + p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) ); + p.drawRect(0, 0, width() - 1, height() - 1); + } + + p.end(); + +} + +#include "qtcolorpicker.moc" + diff --git a/src/utils/colorpicker/qtcolorpicker.h b/src/utils/colorpicker/qtcolorpicker.h new file mode 100644 index 0000000..8fcfadb --- /dev/null +++ b/src/utils/colorpicker/qtcolorpicker.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +#ifndef QTCOLORPICKER_H +#define QTCOLORPICKER_H +#include +#include +#include + +#include +#include +#include + +#if defined(Q_WS_WIN) +# if !defined(QT_QTCOLORPICKER_EXPORT) && !defined(QT_QTCOLORPICKER_IMPORT) +# define QT_QTCOLORPICKER_EXPORT +# elif defined(QT_QTCOLORPICKER_IMPORT) +# if defined(QT_QTCOLORPICKER_EXPORT) +# undef QT_QTCOLORPICKER_EXPORT +# endif +# define QT_QTCOLORPICKER_EXPORT __declspec(dllimport) +# elif defined(QT_QTCOLORPICKER_EXPORT) +# undef QT_QTCOLORPICKER_EXPORT +# define QT_QTCOLORPICKER_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTCOLORPICKER_EXPORT +#endif + +class ColorPickerPopup; + +class QT_QTCOLORPICKER_EXPORT QtColorPicker : public QPushButton +{ + Q_OBJECT + + Q_PROPERTY(bool colorDialog READ colorDialogEnabled WRITE setColorDialogEnabled) + +public: + QtColorPicker(QWidget *parent = 0, + int columns = -1, bool enableColorDialog = true); + + ~QtColorPicker(); + + void insertColor(const QColor &color, const QString &text = QString::null, int index = -1); + + QColor currentColor() const; + + QColor color(int index) const; + + void setColorDialogEnabled(bool enabled); + bool colorDialogEnabled() const; + + void setStandardColors(); + + static QColor getColor(const QPoint &pos, bool allowCustomColors = true); + +public Q_SLOTS: + void setCurrentColor(const QColor &col); + +Q_SIGNALS: + void colorChanged(const QColor &); + +protected: + void paintEvent(QPaintEvent *e); + +private Q_SLOTS: + void buttonPressed(bool toggled); + void popupClosed(); + +private: + ColorPickerPopup *popup; + QColor col; + bool withColorDialog; + bool dirty; + bool firstInserted; +}; + +#endif diff --git a/src/utils/utils.pri b/src/utils/utils.pri index 46db201..aa68f2e 100644 --- a/src/utils/utils.pri +++ b/src/utils/utils.pri @@ -1,5 +1,6 @@ DEPENDPATH += $$PWD +include(colorpicker/colorpicker.pri) include(iconloader/iconloader.pri) HEADERS += \ -- cgit v1.2.3-54-g00ecf