00001
00022 #include "swatch.hpp"
00023
00024 #include <cmath>
00025 #include <QPainter>
00026 #include <QMouseEvent>
00027 #include <QKeyEvent>
00028 #include <QApplication>
00029 #include <QDrag>
00030 #include <QMimeData>
00031 #include <QDropEvent>
00032 #include <QDragEnterEvent>
00033 #include <QStyleOption>
00034 #include <QToolTip>
00035
00036 namespace color_widgets {
00037
00038 class Swatch::Private
00039 {
00040 public:
00041 ColorPalette palette;
00042 int selected;
00043 QSize color_size;
00044 ColorSizePolicy size_policy;
00045 QPen border;
00046 int forced_rows;
00047 int forced_columns;
00048 bool readonly;
00049
00050 QPoint drag_pos;
00051 int drag_index;
00052 int drop_index;
00053 QColor drop_color;
00054 bool drop_overwrite;
00055
00056 Swatch* owner;
00057
00058 Private(Swatch* owner)
00059 : selected(-1),
00060 color_size(16,16),
00061 size_policy(Hint),
00062 border(Qt::black, 1),
00063 forced_rows(0),
00064 forced_columns(0),
00065 readonly(false),
00066 drag_index(-1),
00067 drop_index(-1),
00068 drop_overwrite(false),
00069 owner(owner)
00070 {}
00071
00075 QSize rowcols()
00076 {
00077 int count = palette.count();
00078 if ( count == 0 )
00079 return QSize();
00080
00081 if ( forced_rows )
00082 return QSize(std::ceil( float(count) / forced_rows ), forced_rows);
00083
00084 int columns = palette.columns();
00085
00086 if ( forced_columns )
00087 columns = forced_columns;
00088 else if ( columns == 0 )
00089 columns = qMin(palette.count(), owner->width() / color_size.width());
00090
00091 int rows = std::ceil( float(count) / columns );
00092
00093 return QSize(columns, rows);
00094 }
00095
00099 void dropEvent(QDropEvent* event)
00100 {
00101
00102 drop_index = owner->indexAt(event->pos());
00103 if ( drop_index == -1 )
00104 drop_index = palette.count();
00105
00106
00107 if ( event->mimeData()->hasColor() )
00108 {
00109 drop_color = event->mimeData()->colorData().value<QColor>();
00110 drop_color.setAlpha(255);
00111 }
00112 else if ( event->mimeData()->hasText() )
00113 {
00114 drop_color = QColor(event->mimeData()->text());
00115 }
00116
00117 drop_overwrite = false;
00118 QRectF drop_rect = indexRect(drop_index);
00119 if ( drop_index < palette.count() && drop_rect.isValid() )
00120 {
00121
00122 if ( palette.columns() == 1 || forced_columns == 1 )
00123 {
00124
00125 if ( event->posF().y() >= drop_rect.top() + drop_rect.height() * 3.0 / 4 )
00126 drop_index++;
00127
00128 else if ( event->posF().x() > drop_rect.top() + drop_rect.height() / 4 &&
00129 ( event->dropAction() != Qt::MoveAction || event->source() != owner ) )
00130 drop_overwrite = true;
00131 }
00132 else
00133 {
00134
00135 if ( event->posF().x() >= drop_rect.left() + drop_rect.width() * 3.0 / 4 )
00136 drop_index++;
00137
00138 else if ( event->posF().x() > drop_rect.left() + drop_rect.width() / 4 &&
00139 ( event->dropAction() != Qt::MoveAction || event->source() != owner ) )
00140 drop_overwrite = true;
00141 }
00142 }
00143
00144 owner->update();
00145 }
00146
00150 void clearDrop()
00151 {
00152 drop_index = -1;
00153 drop_color = QColor();
00154 drop_overwrite = false;
00155
00156 owner->update();
00157 }
00158
00162 QSizeF actualColorSize()
00163 {
00164 QSize rowcols = this->rowcols();
00165 if ( !rowcols.isValid() )
00166 return QSizeF();
00167 return actualColorSize(rowcols);
00168 }
00169
00174 QSizeF actualColorSize(const QSize& rowcols)
00175 {
00176 return QSizeF (float(owner->width()) / rowcols.width(),
00177 float(owner->height()) / rowcols.height());
00178 }
00179
00180
00186 QRectF indexRect(int index, const QSize& rowcols, const QSizeF& color_size)
00187 {
00188 if ( index == -1 )
00189 return QRectF();
00190
00191 return QRectF(
00192 index % rowcols.width() * color_size.width(),
00193 index / rowcols.width() * color_size.height(),
00194 color_size.width(),
00195 color_size.height()
00196 );
00197 }
00201 QRectF indexRect(int index)
00202 {
00203 QSize rc = rowcols();
00204 if ( index == -1 || !rc.isValid() )
00205 return QRectF();
00206 return indexRect(index, rc, actualColorSize(rc));
00207 }
00208 };
00209
00210 Swatch::Swatch(QWidget* parent)
00211 : QWidget(parent), p(new Private(this))
00212 {
00213 connect(&p->palette, &ColorPalette::colorsChanged, this, &Swatch::paletteModified);
00214 connect(&p->palette, &ColorPalette::columnsChanged, this, (void(QWidget::*)())&QWidget::update);
00215 connect(&p->palette, &ColorPalette::colorsUpdated, this, (void(QWidget::*)())&QWidget::update);
00216 connect(&p->palette, &ColorPalette::colorChanged, [this](int index){
00217 if ( index == p->selected )
00218 emit colorSelected( p->palette.colorAt(index) );
00219 });
00220 connect(&p->palette, &ColorPalette::colorRemoved, [this](int index){
00221 if ( index == p->selected )
00222 clearSelection();
00223 });
00224 setFocusPolicy(Qt::StrongFocus);
00225 setAcceptDrops(true);
00226 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
00227 setAttribute(Qt::WA_Hover, true);
00228 }
00229
00230 Swatch::~Swatch()
00231 {
00232 delete p;
00233 }
00234
00235 QSize Swatch::sizeHint() const
00236 {
00237 QSize rowcols = p->rowcols();
00238
00239 if ( !p->color_size.isValid() || !rowcols.isValid() )
00240 return QSize();
00241
00242 return QSize(
00243 p->color_size.width() * rowcols.width(),
00244 p->color_size.height() * rowcols.height()
00245 );
00246 }
00247
00248 QSize Swatch::minimumSizeHint() const
00249 {
00250 if ( p->size_policy != Hint )
00251 return sizeHint();
00252 return QSize();
00253 }
00254
00255 const ColorPalette& Swatch::palette() const
00256 {
00257 return p->palette;
00258 }
00259
00260 ColorPalette& Swatch::palette()
00261 {
00262 return p->palette;
00263 }
00264
00265 int Swatch::selected() const
00266 {
00267 return p->selected;
00268 }
00269
00270 QColor Swatch::selectedColor() const
00271 {
00272 return p->palette.colorAt(p->selected);
00273 }
00274
00275 int Swatch::indexAt(const QPoint& pt)
00276 {
00277 QSize rowcols = p->rowcols();
00278 if ( rowcols.isEmpty() )
00279 return -1;
00280
00281 QSizeF color_size = p->actualColorSize(rowcols);
00282
00283 QPoint point(
00284 qBound<int>(0, pt.x() / color_size.width(), rowcols.width() - 1),
00285 qBound<int>(0, pt.y() / color_size.height(), rowcols.height() - 1)
00286 );
00287
00288 int index = point.y() * rowcols.width() + point.x();
00289 if ( index >= p->palette.count() )
00290 return -1;
00291 return index;
00292 }
00293
00294 QColor Swatch::colorAt(const QPoint& pt)
00295 {
00296 return p->palette.colorAt(indexAt(pt));
00297 }
00298
00299 void Swatch::setPalette(const ColorPalette& palette)
00300 {
00301 clearSelection();
00302 p->palette = palette;
00303 update();
00304 emit paletteChanged(p->palette);
00305 }
00306
00307 void Swatch::setSelected(int selected)
00308 {
00309 if ( selected < 0 || selected >= p->palette.count() )
00310 selected = -1;
00311
00312 if ( selected != p->selected )
00313 {
00314 emit selectedChanged( p->selected = selected );
00315 if ( selected != -1 )
00316 emit colorSelected( p->palette.colorAt(p->selected) );
00317 update();
00318 }
00319 }
00320
00321 void Swatch::clearSelection()
00322 {
00323 setSelected(-1);
00324 }
00325
00326 void Swatch::paintEvent(QPaintEvent* )
00327 {
00328 QSize rowcols = p->rowcols();
00329 if ( rowcols.isEmpty() )
00330 return;
00331
00332 QSizeF color_size = p->actualColorSize(rowcols);
00333 QPainter painter(this);
00334
00335 QStyleOptionFrame panel;
00336 panel.initFrom(this);
00337 panel.lineWidth = 1;
00338 panel.midLineWidth = 0;
00339 panel.state |= QStyle::State_Sunken;
00340 style()->drawPrimitive(QStyle::PE_Frame, &panel, &painter, this);
00341 QRect r = style()->subElementRect(QStyle::SE_FrameContents, &panel, this);
00342 painter.setClipRect(r);
00343
00344 int count = p->palette.count();
00345 painter.setPen(p->border);
00346 for ( int y = 0, i = 0; i < count; y++ )
00347 {
00348 for ( int x = 0; x < rowcols.width() && i < count; x++, i++ )
00349 {
00350 painter.setBrush(p->palette.colorAt(i));
00351 painter.drawRect(p->indexRect(i, rowcols, color_size));
00352 }
00353 }
00354
00355 painter.setClipping(false);
00356
00357 if ( p->drop_index != -1 )
00358 {
00359 QRectF drop_area = p->indexRect(p->drop_index, rowcols, color_size);
00360 if ( p->drop_overwrite )
00361 {
00362 painter.setBrush(p->drop_color);
00363 painter.setPen(QPen(Qt::gray));
00364 painter.drawRect(drop_area);
00365 }
00366 else if ( rowcols.width() == 1 )
00367 {
00368
00369 painter.setPen(QPen(p->drop_color, 2));
00370 painter.setBrush(Qt::transparent);
00371 painter.drawLine(drop_area.topLeft(), drop_area.topRight());
00372 }
00373 else
00374 {
00375 painter.setPen(QPen(p->drop_color, 2));
00376 painter.setBrush(Qt::transparent);
00377 painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft());
00378
00379 if ( p->drop_index % rowcols.width() == 0 && p->drop_index != 0 )
00380 {
00381 drop_area = p->indexRect(p->drop_index-1, rowcols, color_size);
00382 drop_area.translate(color_size.width(), 0);
00383 painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft());
00384 }
00385 }
00386 }
00387
00388 if ( p->selected != -1 )
00389 {
00390 QRectF rect = p->indexRect(p->selected, rowcols, color_size);
00391 painter.setBrush(Qt::transparent);
00392 painter.setPen(QPen(Qt::darkGray, 2));
00393 painter.drawRect(rect);
00394 painter.setPen(QPen(Qt::gray, 2, Qt::DotLine));
00395 painter.drawRect(rect);
00396 }
00397 }
00398
00399 void Swatch::keyPressEvent(QKeyEvent* event)
00400 {
00401 if ( p->palette.count() == 0 )
00402 QWidget::keyPressEvent(event);
00403
00404 int selected = p->selected;
00405 int count = p->palette.count();
00406 QSize rowcols = p->rowcols();
00407 int columns = rowcols.width();
00408 int rows = rowcols.height();
00409 switch ( event->key() )
00410 {
00411 default:
00412 QWidget::keyPressEvent(event);
00413 return;
00414
00415 case Qt::Key_Left:
00416 if ( selected == -1 )
00417 selected = count - 1;
00418 else if ( selected > 0 )
00419 selected--;
00420 break;
00421
00422 case Qt::Key_Right:
00423 if ( selected == -1 )
00424 selected = 0;
00425 else if ( selected < count - 1 )
00426 selected++;
00427 break;
00428
00429 case Qt::Key_Up:
00430 if ( selected == -1 )
00431 selected = count - 1;
00432 else if ( selected >= columns )
00433 selected -= columns;
00434 break;
00435
00436 case Qt::Key_Down:
00437 if ( selected == -1 )
00438 selected = 0;
00439 else if ( selected < count - columns )
00440 selected += columns;
00441 break;
00442
00443 case Qt::Key_Home:
00444 if ( event->modifiers() & Qt::ControlModifier )
00445 selected = 0;
00446 else
00447 selected -= selected % columns;
00448 break;
00449
00450 case Qt::Key_End:
00451 if ( event->modifiers() & Qt::ControlModifier )
00452 selected = count - 1;
00453 else
00454 selected += columns - (selected % columns) - 1;
00455 break;
00456
00457 case Qt::Key_Delete:
00458 removeSelected();
00459 return;
00460
00461 case Qt::Key_Backspace:
00462 if (selected != -1 && !p->readonly )
00463 {
00464 p->palette.eraseColor(selected);
00465 if ( p->palette.count() == 0 )
00466 selected = -1;
00467 else
00468 selected = qMax(selected - 1, 0);
00469 }
00470 break;
00471
00472 case Qt::Key_PageUp:
00473 if ( selected == -1 )
00474 selected = 0;
00475 else
00476 selected = selected % columns;
00477 break;
00478 case Qt::Key_PageDown:
00479 if ( selected == -1 )
00480 {
00481 selected = count - 1;
00482 }
00483 else
00484 {
00485 selected = columns * (rows-1) + selected % columns;
00486 if ( selected >= count )
00487 selected -= columns;
00488 }
00489 break;
00490 }
00491 setSelected(selected);
00492 }
00493
00494 void Swatch::removeSelected()
00495 {
00496 if (p->selected != -1 && !p->readonly )
00497 {
00498 int selected = p->selected;
00499 p->palette.eraseColor(p->selected);
00500 setSelected(qMin(selected, p->palette.count() - 1));
00501 }
00502 }
00503
00504 void Swatch::mousePressEvent(QMouseEvent *event)
00505 {
00506 if ( event->button() == Qt::LeftButton )
00507 {
00508 setSelected(indexAt(event->pos()));
00509 p->drag_pos = event->pos();
00510 p->drag_index = indexAt(event->pos());
00511 }
00512 else if ( event->button() == Qt::RightButton )
00513 {
00514 int index = indexAt(event->pos());
00515 if ( index != -1 )
00516 emit rightClicked(index);
00517 }
00518 }
00519
00520 void Swatch::mouseMoveEvent(QMouseEvent *event)
00521 {
00522 if ( p->drag_index != -1 && (event->buttons() & Qt::LeftButton) &&
00523 (p->drag_pos - event->pos()).manhattanLength() >= QApplication::startDragDistance() )
00524 {
00525 QColor color = p->palette.colorAt(p->drag_index);
00526
00527 QPixmap preview(24,24);
00528 preview.fill(color);
00529
00530 QMimeData *mimedata = new QMimeData;
00531 mimedata->setColorData(color);
00532 mimedata->setText(p->palette.nameAt(p->drag_index));
00533
00534 QDrag *drag = new QDrag(this);
00535 drag->setMimeData(mimedata);
00536 drag->setPixmap(preview);
00537 Qt::DropActions actions = Qt::CopyAction;
00538 if ( !p->readonly )
00539 actions |= Qt::MoveAction;
00540 drag->exec(actions);
00541 }
00542 }
00543
00544 void Swatch::mouseReleaseEvent(QMouseEvent *event)
00545 {
00546 if ( event->button() == Qt::LeftButton )
00547 {
00548 p->drag_index = -1;
00549 }
00550 }
00551
00552 void Swatch::mouseDoubleClickEvent(QMouseEvent *event)
00553 {
00554 if ( event->button() == Qt::LeftButton )
00555 {
00556 int index = indexAt(event->pos());
00557 if ( index != -1 )
00558 emit doubleClicked(index);
00559 }
00560 }
00561
00562 void Swatch::wheelEvent(QWheelEvent* event)
00563 {
00564 if ( event->delta() > 0 )
00565 p->selected = qMin(p->selected + 1, p->palette.count() - 1);
00566 else if ( p->selected == -1 )
00567 p->selected = p->palette.count() - 1;
00568 else if ( p->selected > 0 )
00569 p->selected--;
00570 setSelected(p->selected);
00571 }
00572
00573 void Swatch::dragEnterEvent(QDragEnterEvent *event)
00574 {
00575 if ( p->readonly )
00576 return;
00577
00578 p->dropEvent(event);
00579
00580 if ( p->drop_color.isValid() && p->drop_index != -1 )
00581 {
00582 if ( event->proposedAction() == Qt::MoveAction && event->source() == this )
00583 event->setDropAction(Qt::MoveAction);
00584 else
00585 event->setDropAction(Qt::CopyAction);
00586
00587 event->accept();
00588 }
00589 }
00590
00591 void Swatch::dragMoveEvent(QDragMoveEvent* event)
00592 {
00593 if ( p->readonly )
00594 return;
00595 p->dropEvent(event);
00596 }
00597
00598 void Swatch::dragLeaveEvent(QDragLeaveEvent *)
00599 {
00600 p->clearDrop();
00601 }
00602
00603 void Swatch::dropEvent(QDropEvent *event)
00604 {
00605 if ( p->readonly )
00606 return;
00607
00608 QString name;
00609
00610
00611 if ( event->mimeData()->hasColor() && event->mimeData()->hasText() )
00612 name = event->mimeData()->text();
00613
00614
00615 if ( !p->drop_color.isValid() || p->drop_index == -1 )
00616 return;
00617
00618 p->dropEvent(event);
00619
00620
00621 if ( event->dropAction() == Qt::MoveAction && event->source() == this )
00622 {
00623
00624 if ( p->drop_index != p->drag_index && p->drop_index != p->drag_index + 1 )
00625 {
00626
00627 p->palette.eraseColor(p->drag_index);
00628 if ( p->drop_index > p->drag_index )
00629 p->drop_index--;
00630 p->selected = p->drop_index;
00631
00632 p->palette.insertColor(p->drop_index, p->drop_color, name);
00633 }
00634 }
00635
00636 else if ( p->drop_overwrite )
00637 {
00638 p->palette.setColorAt(p->drop_index, p->drop_color, name);
00639 }
00640
00641 else
00642 {
00643 p->palette.insertColor(p->drop_index, p->drop_color, name);
00644 }
00645
00646
00647 event->accept();
00648 p->drag_index = -1;
00649 p->clearDrop();
00650 }
00651
00652 void Swatch::paletteModified()
00653 {
00654 if ( p->selected >= p->palette.count() )
00655 clearSelection();
00656
00657 if ( p->size_policy == Minimum )
00658 setMinimumSize(sizeHint());
00659 else if ( p->size_policy == Fixed )
00660 setFixedSize(sizeHint());
00661
00662 update();
00663 }
00664
00665 QSize Swatch::colorSize() const
00666 {
00667 return p->color_size;
00668 }
00669
00670 void Swatch::setColorSize(const QSize& colorSize)
00671 {
00672 if ( p->color_size != colorSize )
00673 emit colorSizeChanged(p->color_size = colorSize);
00674 }
00675
00676 Swatch::ColorSizePolicy Swatch::colorSizePolicy() const
00677 {
00678 return p->size_policy;
00679 }
00680
00681 void Swatch::setColorSizePolicy(ColorSizePolicy colorSizePolicy)
00682 {
00683 if ( p->size_policy != colorSizePolicy )
00684 {
00685 setMinimumSize(0,0);
00686 setFixedSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX);
00687 paletteModified();
00688 emit colorSizePolicyChanged(p->size_policy = colorSizePolicy);
00689 }
00690 }
00691
00692 int Swatch::forcedColumns() const
00693 {
00694 return p->forced_columns;
00695 }
00696
00697 int Swatch::forcedRows() const
00698 {
00699 return p->forced_rows;
00700 }
00701
00702 void Swatch::setForcedColumns(int forcedColumns)
00703 {
00704 if ( forcedColumns <= 0 )
00705 forcedColumns = 0;
00706
00707 if ( forcedColumns != p->forced_columns )
00708 {
00709 emit forcedColumnsChanged(p->forced_columns = forcedColumns);
00710 emit forcedRowsChanged(p->forced_rows = 0);
00711 }
00712 }
00713
00714 void Swatch::setForcedRows(int forcedRows)
00715 {
00716 if ( forcedRows <= 0 )
00717 forcedRows = 0;
00718
00719 if ( forcedRows != p->forced_rows )
00720 {
00721 emit forcedColumnsChanged(p->forced_columns = 0);
00722 emit forcedRowsChanged(p->forced_rows = forcedRows);
00723 }
00724 }
00725
00726 bool Swatch::readOnly() const
00727 {
00728 return p->readonly;
00729 }
00730
00731 void Swatch::setReadOnly(bool readOnly)
00732 {
00733 if ( readOnly != p->readonly )
00734 {
00735 emit readOnlyChanged(p->readonly = readOnly);
00736 setAcceptDrops(!p->readonly);
00737 }
00738 }
00739
00740 bool Swatch::event(QEvent* event)
00741 {
00742 if(event->type() == QEvent::ToolTip)
00743 {
00744 QHelpEvent* help_ev = static_cast<QHelpEvent*>(event);
00745 int index = indexAt(help_ev->pos());
00746 if ( index != -1 )
00747 {
00748 QColor color = p->palette.colorAt(index);
00749 QString name = p->palette.nameAt(index);
00750 QString message = color.name();
00751 if ( !name.isEmpty() )
00752 message = tr("%1 (%2)").arg(name).arg(message);
00753 message = "<tt style='background-color:"+color.name()+";color:"+color.name()+";'>MM</tt> "+message.toHtmlEscaped();
00754 QToolTip::showText(help_ev->globalPos(), message, this,
00755 p->indexRect(index).toRect());
00756 event->accept();
00757 }
00758 else
00759 {
00760 QToolTip::hideText();
00761 event->ignore();
00762 }
00763 return true;
00764 }
00765
00766 return QWidget::event(event);
00767 }
00768
00769 QPen Swatch::border() const
00770 {
00771 return p->border;
00772 }
00773
00774 void Swatch::setBorder(const QPen& border)
00775 {
00776 if ( border != p->border )
00777 {
00778 p->border = border;
00779 emit borderChanged(border);
00780 update();
00781 }
00782 }
00783
00784 }