File indexing completed on 2024-11-24 03:56:08

0001 /*
0002  * SPDX-FileCopyrightText: 2013-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: LGPL-3.0-or-later
0005  */
0006 
0007 #include "QtColorWidgets/swatch.hpp"
0008 #include "QtColorWidgets/qt_compatibility.hpp"
0009 
0010 #include <cmath>
0011 #include <limits>
0012 #include <QPainter>
0013 #include <QMouseEvent>
0014 #include <QKeyEvent>
0015 #include <QApplication>
0016 #include <QDrag>
0017 #include <QMimeData>
0018 #include <QDropEvent>
0019 #include <QDragEnterEvent>
0020 #include <QStyleOption>
0021 #include <QToolTip>
0022 
0023 namespace color_widgets {
0024 
0025 class Swatch::Private
0026 {
0027 public:
0028     ColorPalette palette;    ///< Palette with colors and related metadata
0029     int          selected;   ///< Current selection index (-1 for no selection)
0030     QSize        color_size; ///< Preferred size for the color squares
0031     ColorSizePolicy size_policy;
0032     QPen         border;
0033     QPen         selected_pen{Qt::gray, 2, Qt::DotLine};
0034     int          forced_rows;
0035     int          forced_columns;
0036     bool         readonly;  ///< Whether the palette can be modified via user interaction
0037 
0038     QPoint  drag_pos;       ///< Point used to keep track of dragging
0039     int     drag_index;     ///< Index used by drags
0040     int     drop_index;     ///< Index for a requested drop
0041     QColor  drop_color;     ///< Dropped color
0042     bool    drop_overwrite; ///< Whether the drop will overwrite an existing color
0043 
0044     QSize   max_color_size;  ///< Mazimum size a color square can have
0045 
0046     bool show_clear_color = false;
0047 
0048     Swatch* owner;
0049 
0050     Private(Swatch* owner)
0051         : selected(-1),
0052           color_size(16,16),
0053           size_policy(Hint),
0054           border(Qt::black, 1),
0055           forced_rows(0),
0056           forced_columns(0),
0057           readonly(false),
0058           drag_index(-1),
0059           drop_index(-1),
0060           drop_overwrite(false),
0061           max_color_size(96, 128),
0062           owner(owner)
0063     {
0064         // Ensure rectangle with 90 degree edges - default Qt::BevelJoin causes
0065         // rounded / flatted rectangle
0066         border.setJoinStyle(Qt::MiterJoin);
0067         selected_pen.setJoinStyle(Qt::MiterJoin);
0068     }
0069 
0070     /**
0071      * \brief Number of rows/columns in the palette
0072      */
0073     QSize rowcols()
0074     {
0075         int count = color_count();
0076 
0077         if ( count == 0 )
0078             return QSize();
0079 
0080         if ( forced_rows )
0081             return QSize(std::ceil( float(count) / forced_rows ), forced_rows);
0082 
0083         int columns = palette.columns();
0084         bool flexible_columns = false;
0085 
0086         if ( forced_columns )
0087         {
0088             columns = forced_columns;
0089         }
0090         else if ( columns == 0 )
0091         {
0092             columns = qMin(count, (owner->width() - border.width()) / color_size.width());
0093             flexible_columns = true;
0094         }
0095 
0096         int rows = std::ceil( float(count) / columns );
0097 
0098         // Make nicer looking tables when we don't have many colors
0099         if ( flexible_columns && rows < 4 )
0100         {
0101             int best_fit_rows = std::ceil(float(owner->height() - border.width()) / qMax(1, max_color_size.height()));
0102             if ( rows < best_fit_rows )
0103                 rows = best_fit_rows;
0104 
0105             columns = std::ceil( float(count) / rows );
0106 
0107             // Avoid empty columns
0108             int avail_width = owner->width() - border.width();
0109             if ( columns * max_color_size.width() < avail_width )
0110             {
0111                 columns = std::ceil(float(avail_width) / qMax(1, max_color_size.width()));
0112                 rows = std::ceil( float(count) / columns );
0113             }
0114         }
0115 
0116         return QSize(columns, rows);
0117     }
0118 
0119     int color_count()
0120     {
0121         int count = palette.count();
0122 
0123         if ( show_clear_color )
0124             count++;
0125 
0126         return count;
0127     }
0128 
0129     /**
0130      * \brief Sets the drop properties
0131      */
0132     void dropEvent(QDropEvent* event)
0133     {
0134         // Find the output location
0135         drop_index = indexAt(pos_wrap(event).toPoint());
0136         if ( drop_index == -1 )
0137             drop_index = palette.count();
0138 
0139         // Gather up the color
0140         if ( event->mimeData()->hasColor() )
0141         {
0142             drop_color = event->mimeData()->colorData().value<QColor>();
0143             drop_color.setAlpha(255);
0144         }
0145         else if ( event->mimeData()->hasText() )
0146         {
0147             drop_color = QColor(event->mimeData()->text());
0148         }
0149 
0150         drop_overwrite = false;
0151         QRectF drop_rect = indexRect(drop_index);
0152         if ( drop_index < palette.count() && drop_rect.isValid() )
0153         {
0154             // 1 column => vertical style
0155             if ( palette.columns() == 1 || forced_columns == 1 )
0156             {
0157                 // Dragged to the last quarter of the size of the square, add after
0158                 if ( pos_wrap(event).y() >= drop_rect.top() + drop_rect.height() * 3.0 / 4 )
0159                     drop_index++;
0160                 // Dragged to the middle of the square, overwrite existing color
0161                 else if ( pos_wrap(event).x() > drop_rect.top() + drop_rect.height() / 4 &&
0162                         ( event->dropAction() != Qt::MoveAction || event->source() != owner ) )
0163                     drop_overwrite = true;
0164             }
0165             else
0166             {
0167                 // Dragged to the last quarter of the size of the square, add after
0168                 if ( pos_wrap(event).x() >= drop_rect.left() + drop_rect.width() * 3.0 / 4 )
0169                     drop_index++;
0170                 // Dragged to the middle of the square, overwrite existing color
0171                 else if ( pos_wrap(event).x() > drop_rect.left() + drop_rect.width() / 4 &&
0172                         ( event->dropAction() != Qt::MoveAction || event->source() != owner ) )
0173                     drop_overwrite = true;
0174             }
0175         }
0176 
0177         owner->update();
0178     }
0179 
0180     /**
0181      * \brief Clears drop properties
0182      */
0183     void clearDrop()
0184     {
0185         drop_index = -1;
0186         drop_color = QColor();
0187         drop_overwrite = false;
0188 
0189         owner->update();
0190     }
0191 
0192     /**
0193      * \brief Actual size of a color square
0194      */
0195     QSizeF actualColorSize()
0196     {
0197         QSize rowcols = this->rowcols();
0198         if ( !rowcols.isValid() )
0199             return QSizeF();
0200         return actualColorSize(rowcols);
0201     }
0202 
0203     /**
0204      * \brief Actual size of a color square
0205      * \pre rowcols.isValid() and obtained via rowcols()
0206      */
0207     QSizeF actualColorSize(const QSize& rowcols)
0208     {
0209         return QSizeF (
0210             qMin(qreal(max_color_size.width()), qreal(owner->width() - border.width()) / rowcols.width()),
0211             qMin(qreal(max_color_size.height()), qreal(owner->height()  - border.width()) / rowcols.height())
0212         );
0213     }
0214 
0215 
0216     /**
0217      * \brief Rectangle corresponding to the color at the given index
0218      * \pre rowcols.isValid() and obtained via rowcols()
0219      * \pre color_size obtained via rowlcols(rowcols)
0220      */
0221     QRectF indexRect(int index, const QSize& rowcols, const QSizeF& color_size)
0222     {
0223         if ( index == -1 )
0224             return QRectF();
0225 
0226         return QRectF(
0227             index % rowcols.width() * color_size.width() + border.width() / 2,
0228             index / rowcols.width() * color_size.height() + border.width() / 2,
0229             color_size.width(),
0230             color_size.height()
0231         );
0232     }
0233     /**
0234      * \brief Rectangle corresponding to the color at the given index
0235      */
0236     QRectF indexRect(int index)
0237     {
0238         QSize rc = rowcols();
0239         if ( index == -1 || !rc.isValid() )
0240             return QRectF();
0241         return indexRect(index, rc, actualColorSize(rc));
0242     }
0243 
0244     int indexAt(const QPoint& pt, bool mark_clear = false)
0245     {
0246         QSize rowcols = this->rowcols();
0247         if ( rowcols.isEmpty() )
0248             return -1;
0249 
0250         QSizeF color_size = actualColorSize(rowcols);
0251 
0252         QPoint point(
0253             pt.x() / color_size.width(),
0254             pt.y() / color_size.height()
0255         );
0256 
0257         if ( point.x() < 0 || point.x() >= rowcols.width() || point.y() < 0 || point.y() >= rowcols.height() )
0258             return -1;
0259 
0260         int index = point.y() * rowcols.width() + point.x();
0261 
0262         if ( mark_clear && index == palette.count() && show_clear_color )
0263             return -2;
0264 
0265         if ( index >= palette.count() )
0266             return -1;
0267         return index;
0268     }
0269 };
0270 
0271 Swatch::Swatch(QWidget* parent)
0272     : QWidget(parent), p(new Private(this))
0273 {
0274     connect(&p->palette, &ColorPalette::colorsChanged, this, &Swatch::paletteModified);
0275     connect(&p->palette, &ColorPalette::colorAdded, this, &Swatch::paletteModified);
0276     connect(&p->palette, &ColorPalette::colorRemoved, this, &Swatch::paletteModified);
0277     connect(&p->palette, &ColorPalette::columnsChanged, this, (void(QWidget::*)())&QWidget::update);
0278     connect(&p->palette, &ColorPalette::colorsUpdated, this, (void(QWidget::*)())&QWidget::update);
0279     connect(&p->palette, &ColorPalette::colorChanged, [this](int index){
0280         if ( index == p->selected )
0281             Q_EMIT colorSelected( p->palette.colorAt(index) );
0282     });
0283     setFocusPolicy(Qt::StrongFocus);
0284     setAcceptDrops(true);
0285     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
0286     setAttribute(Qt::WA_Hover, true);
0287 }
0288 
0289 Swatch::~Swatch()
0290 {
0291     delete p;
0292 }
0293 
0294 QSize Swatch::sizeHint() const
0295 {
0296     QSize rowcols = p->rowcols();
0297 
0298     if ( !p->color_size.isValid() || !rowcols.isValid() )
0299         return QSize();
0300 
0301     return QSize(
0302         p->color_size.width()  * rowcols.width(),
0303         p->color_size.height() * rowcols.height()
0304     );
0305 }
0306 
0307 QSize Swatch::minimumSizeHint() const
0308 {
0309     if ( p->size_policy != Hint )
0310         return sizeHint();
0311     return QSize();
0312 }
0313 
0314 const ColorPalette& Swatch::palette() const
0315 {
0316     return p->palette;
0317 }
0318 
0319 ColorPalette& Swatch::palette()
0320 {
0321     return p->palette;
0322 }
0323 
0324 int Swatch::selected() const
0325 {
0326     return p->selected;
0327 }
0328 
0329 QColor Swatch::selectedColor() const
0330 {
0331     return p->palette.colorAt(p->selected);
0332 }
0333 
0334 int Swatch::indexAt(const QPoint& pt)
0335 {
0336     return p->indexAt(pt);
0337 }
0338 
0339 QColor Swatch::colorAt(const QPoint& pt)
0340 {
0341     return p->palette.colorAt(indexAt(pt));
0342 }
0343 
0344 void Swatch::setPalette(const ColorPalette& palette)
0345 {
0346     clearSelection();
0347     p->palette = palette;
0348     update();
0349     Q_EMIT paletteChanged(p->palette);
0350 }
0351 
0352 void Swatch::setSelected(int selected)
0353 {
0354     if ( selected < 0 || selected >= p->palette.count() )
0355         selected = -1;
0356 
0357     if ( selected != p->selected )
0358     {
0359         Q_EMIT selectedChanged( p->selected = selected );
0360         if ( selected != -1 )
0361             Q_EMIT colorSelected( p->palette.colorAt(p->selected) );
0362     }
0363     update();
0364 }
0365 
0366 
0367 bool Swatch::setSelectedColor(const QColor& color)
0368 {
0369     for (int i = 0; i < p->palette.count(); i++)
0370     {
0371         if (p->palette.colorAt(i) == color)
0372         {
0373             setSelected(i);
0374             return true;
0375         }
0376     }
0377 
0378     return false;
0379 }
0380 
0381 void Swatch::clearSelection()
0382 {
0383     setSelected(-1);
0384 }
0385 
0386 void Swatch::paintEvent(QPaintEvent* event)
0387 {
0388     Q_UNUSED(event)
0389     QSize rowcols = p->rowcols();
0390 
0391     QPainter painter(this);
0392 
0393     QStyleOptionFrame panel;
0394     panel.initFrom(this);
0395     panel.lineWidth = 1;
0396     panel.midLineWidth = 0;
0397     panel.state |= QStyle::State_Sunken;
0398     style()->drawPrimitive(QStyle::PE_Frame, &panel, &painter, this);
0399 
0400     if ( rowcols.isEmpty() )
0401         return;
0402 
0403     QSizeF color_size = p->actualColorSize(rowcols);
0404     QRect r = style()->subElementRect(QStyle::SE_FrameContents, &panel, this);
0405     painter.setClipRect(r);
0406 
0407     int count = p->palette.count();
0408     painter.setPen(p->border);
0409     for ( int y = 0, i = 0; i < count; y++ )
0410     {
0411         for ( int x = 0; x < rowcols.width() && i < count; x++, i++ )
0412         {
0413             painter.setBrush(p->palette.colorAt(i));
0414             painter.drawRect(p->indexRect(i, rowcols, color_size));
0415         }
0416     }
0417 
0418     if ( p->show_clear_color )
0419     {
0420         QRectF ir = p->indexRect(count, rowcols, color_size);
0421         painter.setBrush(QColor(255, 255, 255));
0422         painter.drawRect(ir);
0423         painter.setPen(QPen(QColor(0xa40000), qBound(1., 5., color_size.width()/3)));
0424         painter.setBrush(Qt::NoBrush);
0425         painter.setClipRect(ir, Qt::IntersectClip);
0426         painter.setRenderHint(QPainter::Antialiasing);
0427         painter.drawLine(ir.topLeft(), ir.bottomRight());
0428         painter.drawLine(ir.topRight(), ir.bottomLeft());
0429     }
0430 
0431     painter.setClipping(false);
0432 
0433     if ( p->drop_index != -1 )
0434     {
0435         QRectF drop_area = p->indexRect(p->drop_index, rowcols, color_size);
0436         if ( p->drop_overwrite )
0437         {
0438             painter.setBrush(p->drop_color);
0439             painter.setPen(QPen(Qt::gray));
0440             painter.drawRect(drop_area);
0441         }
0442         else if ( rowcols.width() == 1 )
0443         {
0444             // 1 column => vertical style
0445             painter.setPen(QPen(p->drop_color, 2));
0446             painter.setBrush(Qt::transparent);
0447             painter.drawLine(drop_area.topLeft(), drop_area.topRight());
0448         }
0449         else
0450         {
0451             painter.setPen(QPen(p->drop_color, 2));
0452             painter.setBrush(Qt::transparent);
0453             painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft());
0454             // Draw also on the previous line when the first item of a line is selected
0455             if ( p->drop_index % rowcols.width() == 0 && p->drop_index != 0 )
0456             {
0457                 drop_area = p->indexRect(p->drop_index-1, rowcols, color_size);
0458                 drop_area.translate(color_size.width(), 0);
0459                 painter.drawLine(drop_area.topLeft(), drop_area.bottomLeft());
0460             }
0461         }
0462     }
0463 
0464     if ( p->selected != -1 )
0465     {
0466         QRectF rect = p->indexRect(p->selected, rowcols, color_size);
0467         int offset = p->border.width() / 2;
0468         rect.adjust(offset, offset, -offset, -offset);
0469         painter.setBrush(Qt::transparent);
0470         painter.setPen(p->selected_pen);
0471         painter.drawRect(rect);
0472     }
0473 }
0474 
0475 void Swatch::keyPressEvent(QKeyEvent* event)
0476 {
0477     if ( p->palette.count() == 0 )
0478         QWidget::keyPressEvent(event);
0479 
0480     int selected = p->selected;
0481     int count = p->palette.count();
0482     QSize rowcols = p->rowcols();
0483     int columns = rowcols.width();
0484     int rows = rowcols.height();
0485     switch ( event->key() )
0486     {
0487         default:
0488             QWidget::keyPressEvent(event);
0489             return;
0490 
0491         case Qt::Key_Left:
0492             if ( selected == -1 )
0493                 selected = count - 1;
0494             else if ( selected > 0 )
0495                 selected--;
0496             break;
0497 
0498         case Qt::Key_Right:
0499             if ( selected == -1 )
0500                 selected = 0;
0501             else if ( selected < count - 1 )
0502                 selected++;
0503             break;
0504 
0505         case Qt::Key_Up:
0506             if ( selected == -1 )
0507                 selected = count - 1;
0508             else if ( selected >= columns )
0509                 selected -= columns;
0510             break;
0511 
0512         case Qt::Key_Down:
0513             if ( selected == -1 )
0514                 selected = 0;
0515             else if ( selected < count - columns )
0516                 selected += columns;
0517             break;
0518 
0519         case Qt::Key_Home:
0520             if ( event->modifiers() & Qt::ControlModifier )
0521                 selected = 0;
0522             else
0523                 selected -= selected % columns;
0524             break;
0525 
0526         case Qt::Key_End:
0527             if ( event->modifiers() & Qt::ControlModifier )
0528                 selected = count - 1;
0529             else
0530                 selected += columns - (selected % columns) - 1;
0531             break;
0532 
0533         case Qt::Key_Delete:
0534             removeSelected();
0535             return;
0536 
0537         case Qt::Key_Backspace:
0538             if (selected != -1 && !p->readonly )
0539             {
0540                 p->palette.eraseColor(selected);
0541                 if ( p->palette.count() == 0 )
0542                     selected = -1;
0543                 else
0544                     selected = qMax(selected - 1, 0);
0545             }
0546             break;
0547 
0548         case Qt::Key_PageUp:
0549             if ( selected == -1 )
0550                 selected = 0;
0551             else
0552                 selected = selected % columns;
0553             break;
0554         case Qt::Key_PageDown:
0555             if ( selected == -1 )
0556             {
0557                 selected = count - 1;
0558             }
0559             else
0560             {
0561                 selected = columns * (rows-1) + selected % columns;
0562                 if ( selected >= count )
0563                     selected -= columns;
0564             }
0565             break;
0566     }
0567     setSelected(selected);
0568 }
0569 
0570 void Swatch::removeSelected()
0571 {
0572     if (p->selected != -1 && !p->readonly )
0573     {
0574         int selected = p->selected;
0575         p->palette.eraseColor(p->selected);
0576         setSelected(qMin(selected, p->palette.count() - 1));
0577     }
0578 }
0579 
0580 void Swatch::mousePressEvent(QMouseEvent *event)
0581 {
0582     if ( event->button() == Qt::LeftButton )
0583     {
0584         int index = p->indexAt(event->pos(), true);
0585         setSelected(index);
0586         p->drag_pos = event->pos();
0587         p->drag_index = index;
0588         if ( index == -2 )
0589             Q_EMIT clicked(-1, event->modifiers());
0590         else if ( index != -1 )
0591             Q_EMIT clicked(index, event->modifiers());
0592     }
0593     else if ( event->button() == Qt::RightButton )
0594     {
0595         int index = p->indexAt(event->pos(), true);
0596 
0597         if ( index == -2 )
0598             Q_EMIT rightClicked(-1, event->modifiers());
0599         else if ( index != -1 )
0600             Q_EMIT rightClicked(index, event->modifiers());
0601     }
0602 }
0603 
0604 void Swatch::mouseMoveEvent(QMouseEvent *event)
0605 {
0606     if ( p->drag_index != -1 &&  (event->buttons() & Qt::LeftButton) &&
0607         (p->drag_pos - event->pos()).manhattanLength() >= QApplication::startDragDistance() )
0608     {
0609         QColor color = p->palette.colorAt(p->drag_index);
0610 
0611         QPixmap preview(24,24);
0612         preview.fill(color);
0613 
0614         QMimeData *mimedata = new QMimeData;
0615         mimedata->setColorData(color);
0616         mimedata->setText(p->palette.nameAt(p->drag_index));
0617 
0618         QDrag *drag = new QDrag(this);
0619         drag->setMimeData(mimedata);
0620         drag->setPixmap(preview);
0621         Qt::DropActions actions = Qt::CopyAction;
0622         if ( !p->readonly )
0623             actions |= Qt::MoveAction;
0624         drag->exec(actions);
0625     }
0626 }
0627 
0628 void Swatch::mouseReleaseEvent(QMouseEvent *event)
0629 {
0630     if ( event->button() == Qt::LeftButton )
0631     {
0632         p->drag_index = -1;
0633     }
0634 }
0635 
0636 void Swatch::mouseDoubleClickEvent(QMouseEvent *event)
0637 {
0638     if ( event->button() == Qt::LeftButton )
0639     {
0640         int index = p->indexAt(event->pos(), true);
0641 
0642         if ( index == -2 )
0643             Q_EMIT doubleClicked(-1, event->modifiers());
0644         else if ( index != -1 )
0645             Q_EMIT doubleClicked(index, event->modifiers());
0646     }
0647 }
0648 
0649 void Swatch::wheelEvent(QWheelEvent* event)
0650 {
0651     if ( event->angleDelta().y() < 0 )
0652         p->selected = qMin(p->selected + 1, p->palette.count() - 1);
0653     else if ( p->selected == -1 )
0654             p->selected = p->palette.count() - 1;
0655     else if ( p->selected > 0 )
0656         p->selected--;
0657     setSelected(p->selected);
0658 }
0659 
0660 void Swatch::dragEnterEvent(QDragEnterEvent *event)
0661 {
0662     if ( p->readonly )
0663         return;
0664 
0665     p->dropEvent(event);
0666 
0667     if ( p->drop_color.isValid() && p->drop_index != -1 )
0668     {
0669         if ( event->proposedAction() == Qt::MoveAction && event->source() == this )
0670             event->setDropAction(Qt::MoveAction);
0671         else
0672             event->setDropAction(Qt::CopyAction);
0673 
0674         event->accept();
0675     }
0676 }
0677 
0678 void Swatch::dragMoveEvent(QDragMoveEvent* event)
0679 {
0680     if ( p->readonly )
0681         return;
0682     p->dropEvent(event);
0683 }
0684 
0685 void Swatch::dragLeaveEvent(QDragLeaveEvent *event)
0686 {
0687     Q_UNUSED(event)
0688     p->clearDrop();
0689 }
0690 
0691 void Swatch::dropEvent(QDropEvent *event)
0692 {
0693     if ( p->readonly )
0694         return;
0695 
0696     QString name;
0697 
0698     // Gather up the color
0699     if ( event->mimeData()->hasColor() && event->mimeData()->hasText() )
0700             name = event->mimeData()->text();
0701 
0702     // Not a color, discard
0703     if ( !p->drop_color.isValid() || p->drop_index == -1 )
0704         return;
0705 
0706     p->dropEvent(event);
0707 
0708     // Move unto self
0709     if ( event->dropAction() == Qt::MoveAction && event->source() == this )
0710     {
0711         // Not moved => noop
0712         if ( p->drop_index != p->drag_index && p->drop_index != p->drag_index + 1 )
0713         {
0714             // Erase the old color
0715             p->palette.eraseColor(p->drag_index);
0716             if ( p->drop_index > p->drag_index )
0717                 p->drop_index--;
0718             p->selected = p->drop_index;
0719             // Insert the dropped color
0720             p->palette.insertColor(p->drop_index, p->drop_color, name);
0721         }
0722     }
0723     // Move into a color cell
0724     else if ( p->drop_overwrite )
0725     {
0726         p->palette.setColorAt(p->drop_index, p->drop_color, name);
0727     }
0728     // Insert the dropped color
0729     else
0730     {
0731         p->palette.insertColor(p->drop_index, p->drop_color, name);
0732     }
0733 
0734     // Finalize
0735     event->accept();
0736     p->drag_index = -1;
0737     p->clearDrop();
0738 }
0739 
0740 void Swatch::paletteModified()
0741 {
0742     if ( p->selected >= p->palette.count() )
0743         clearSelection();
0744 
0745     if ( p->size_policy != Hint )
0746     {
0747         QSize size_hint = sizeHint();
0748 
0749         if ( size_hint.isValid() )
0750         {
0751             if ( p->size_policy == Minimum )
0752                 setMinimumSize(size_hint);
0753             else if ( p->size_policy == Fixed )
0754                 setFixedSize(size_hint);
0755         }
0756     }
0757 
0758     update();
0759 }
0760 
0761 QSize Swatch::colorSize() const
0762 {
0763     return p->color_size;
0764 }
0765 
0766 void Swatch::setColorSize(const QSize& colorSize)
0767 {
0768     if ( p->color_size != colorSize )
0769         Q_EMIT colorSizeChanged(p->color_size = colorSize);
0770 }
0771 
0772 QSize Swatch::maxColorSize() const
0773 {
0774     return p->max_color_size;
0775 }
0776 
0777 void Swatch::setMaxColorSize(const QSize& colorSize)
0778 {
0779     if ( p->max_color_size != colorSize )
0780         Q_EMIT maxColorSizeChanged(p->max_color_size = colorSize);
0781 }
0782 
0783 Swatch::ColorSizePolicy Swatch::colorSizePolicy() const
0784 {
0785     return p->size_policy;
0786 }
0787 
0788 void Swatch::setColorSizePolicy(ColorSizePolicy colorSizePolicy)
0789 {
0790     if ( p->size_policy != colorSizePolicy )
0791     {
0792         setMinimumSize(0,0);
0793         setFixedSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX);
0794         Q_EMIT colorSizePolicyChanged(p->size_policy = colorSizePolicy);
0795         paletteModified();
0796     }
0797 }
0798 
0799 int Swatch::forcedColumns() const
0800 {
0801     return p->forced_columns;
0802 }
0803 
0804 int Swatch::forcedRows() const
0805 {
0806     return p->forced_rows;
0807 }
0808 
0809 void Swatch::setForcedColumns(int forcedColumns)
0810 {
0811     if ( forcedColumns <= 0 )
0812         forcedColumns = 0;
0813 
0814     if ( forcedColumns != p->forced_columns )
0815     {
0816         Q_EMIT forcedColumnsChanged(p->forced_columns = forcedColumns);
0817         Q_EMIT forcedRowsChanged(p->forced_rows = 0);
0818     }
0819 }
0820 
0821 void Swatch::setForcedRows(int forcedRows)
0822 {
0823     if ( forcedRows <= 0 )
0824         forcedRows = 0;
0825 
0826     if ( forcedRows != p->forced_rows )
0827     {
0828         Q_EMIT forcedColumnsChanged(p->forced_columns = 0);
0829         Q_EMIT forcedRowsChanged(p->forced_rows = forcedRows);
0830     }
0831 }
0832 
0833 bool Swatch::readOnly() const
0834 {
0835     return p->readonly;
0836 }
0837 
0838 void Swatch::setReadOnly(bool readOnly)
0839 {
0840     if ( readOnly != p->readonly )
0841     {
0842         Q_EMIT readOnlyChanged(p->readonly = readOnly);
0843         setAcceptDrops(!p->readonly);
0844     }
0845 }
0846 
0847 bool Swatch::event(QEvent* event)
0848 {
0849     if(event->type() == QEvent::ToolTip)
0850     {
0851         QHelpEvent* help_ev = static_cast<QHelpEvent*>(event);
0852         int index = p->indexAt(help_ev->pos(), true);
0853         if ( index == -2 )
0854         {
0855             QToolTip::showText(help_ev->globalPos(), tr("Clear Color"), this, p->indexRect(index).toRect());
0856             event->accept();
0857         }
0858         else if ( index != -1 )
0859         {
0860             QColor color = p->palette.colorAt(index);
0861             QString name = p->palette.nameAt(index);
0862             QString message = color.name();
0863             if ( !name.isEmpty() )
0864                 message = tr("%1 (%2)").arg(name).arg(message);
0865             message = QStringLiteral("<tt style='background-color:%1;color:%2;'>MM</tt> %3")
0866                           .arg(color.name())
0867                           .arg(color.name())
0868                           .arg(message.toHtmlEscaped());
0869             QToolTip::showText(help_ev->globalPos(), message, this,
0870                                p->indexRect(index).toRect());
0871             event->accept();
0872         }
0873         else
0874         {
0875             QToolTip::hideText();
0876             event->ignore();
0877         }
0878         return true;
0879     }
0880 
0881     return QWidget::event(event);
0882 }
0883 
0884 QPen Swatch::border() const
0885 {
0886     return p->border;
0887 }
0888 
0889 void Swatch::setBorder(const QPen& border)
0890 {
0891     if ( border != p->border )
0892     {
0893         p->border = border;
0894         Q_EMIT borderChanged(border);
0895         update();
0896     }
0897 }
0898 
0899 
0900 void Swatch::setSelectionPen(const QPen& selected)
0901 {
0902     if ( selected != p->selected_pen )
0903     {
0904         p->selected_pen = selected;
0905         update();
0906     }
0907 }
0908 
0909 
0910 QPen Swatch::selectionPen() const
0911 {
0912     return p->selected_pen;
0913 }
0914 
0915 
0916 bool Swatch::showClearColor() const
0917 {
0918     return p->show_clear_color;
0919 }
0920 
0921 void Swatch::setShowClearColor(bool show)
0922 {
0923     if ( show != p->show_clear_color )
0924     {
0925         Q_EMIT showClearColorChanged(p->show_clear_color = show);
0926         update();
0927     }
0928 }
0929 
0930 
0931 } // namespace color_widgets