File indexing completed on 2024-12-22 04:07:31

0001 
0002 /* This file is part of the KDE libraries
0003     Copyright (C) 1997 Martin Jones (mjones@kde.org)
0004     Copyright (C) 2007 Roberto Raggi (roberto@kdevelop.org)
0005     Copyright (C) 2007 Clarence Dang (dang@kde.org)
0006 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Library General Public
0009     License as published by the Free Software Foundation; either
0010     version 2 of the License, or (at your option) any later version.
0011 
0012     This library is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015     Library General Public License for more details.
0016 
0017     You should have received a copy of the GNU Library General Public License
0018     along with this library; see the file COPYING.LIB.  If not, write to
0019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020     Boston, MA 02110-1301, USA.
0021 */
0022 //-----------------------------------------------------------------------------
0023 
0024 #define DEBUG_KP_COLOR_CELLS_BASE 0
0025 
0026 #include "kpColorCellsBase.h"
0027 
0028 #include <QApplication>
0029 #include <QDrag>
0030 #include <QDragEnterEvent>
0031 #include <QDragMoveEvent>
0032 #include <QDropEvent>
0033 #include <QHeaderView>
0034 #include <QItemDelegate>
0035 #include <QMouseEvent>
0036 #include <QPainter>
0037 #include <QImage>
0038 #include "kpLogCategories.h"
0039 
0040 #include <KColorMimeData>
0041 
0042 
0043 class kpColorCellsBase::kpColorCellsBasePrivate
0044 {
0045 public:
0046     explicit kpColorCellsBasePrivate(kpColorCellsBase *q): q(q)
0047     {
0048         colors = nullptr;
0049         inMouse = false;
0050         selected = -1;
0051         shade = false;
0052         acceptDrags = false;
0053         cellsResizable = true;
0054     }
0055 
0056     kpColorCellsBase *q;
0057 
0058     // Note: This is a good thing and is _not_ data duplication with the
0059     //       colors of QTableWidget cells, for the following reasons:
0060     //
0061     //       1. QColor in Qt4 is full-quality RGB.  However, QTableWidget
0062     //          cells are lossy as their colors may be dithered on the screen.
0063     //
0064     //          Eventually, this field will be changed to a kpColor.
0065     //
0066     //       2. We change the QTableWidget cells' colors when the widget is
0067     //          disabled (see changeEvent()).
0068     //
0069     //       Therefore, do not remove this field without better reasons.
0070     QColor *colors;
0071 
0072     QPoint mousePos;
0073     int selected;
0074     bool shade;
0075     bool acceptDrags;
0076     bool cellsResizable;
0077     bool inMouse;
0078 };
0079 
0080 kpColorCellsBase::kpColorCellsBase( QWidget *parent, int rows, int cols )
0081     : QTableWidget( parent ), d(new kpColorCellsBasePrivate(this))
0082 {
0083     setItemDelegate(new QItemDelegate(this));
0084 
0085     setFrameShape(QFrame::NoFrame);
0086     d->shade = true;
0087     setRowCount( rows );
0088     setColumnCount( cols );
0089 
0090     verticalHeader()->setMinimumSectionSize(16);
0091     verticalHeader()->hide();
0092     horizontalHeader()->setMinimumSectionSize(16);
0093     horizontalHeader()->hide();
0094 
0095     d->colors = new QColor [ rows * cols ];
0096 
0097     d->selected = 0;
0098     d->inMouse = false;
0099 
0100     // Drag'n'Drop
0101     setAcceptDrops( true);
0102 
0103     setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
0104     setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
0105     viewport()->setBackgroundRole( QPalette::Window );
0106     setBackgroundRole( QPalette::Window );
0107 }
0108 
0109 kpColorCellsBase::~kpColorCellsBase()
0110 {
0111     delete [] d->colors;
0112 
0113     delete d;
0114 }
0115 
0116 void kpColorCellsBase::invalidateAllColors ()
0117 {
0118     for (int r = 0; r < rowCount (); r++)
0119         for (int c = 0; c < columnCount (); c++)
0120             d->colors [r * columnCount () + c] = QColor ();
0121 }
0122 
0123 void kpColorCellsBase::clear()
0124 {
0125     invalidateAllColors ();
0126     QTableWidget::clear ();
0127 }
0128 
0129 void kpColorCellsBase::clearContents()
0130 {
0131     invalidateAllColors ();
0132     QTableWidget::clearContents ();
0133 }
0134 
0135 void kpColorCellsBase::setRowColumnCounts (int rows, int columns)
0136 {
0137     const int oldRows = rowCount (), oldCols = columnCount ();
0138     const int newRows = rows, newCols = columns;
0139 #if DEBUG_KP_COLOR_CELLS_BASE
0140     qCDebug(kpLogColorCollection) << "oldRows=" << oldRows << "oldCols=" << oldCols
0141         << "newRows=" << newRows << "newCols=" << newCols;
0142 #endif
0143 
0144     if (oldRows == newRows && oldCols == newCols)
0145         return;
0146 
0147     QTableWidget::setColumnCount (newCols);
0148     QTableWidget::setRowCount (newRows);
0149 
0150     QColor *oldColors = d->colors;
0151     d->colors = new QColor [newRows * newCols];
0152 
0153     for (int r = 0; r < qMin (oldRows, newRows); r++)
0154         for (int c = 0; c < qMin (oldCols, newCols); c++)
0155             d->colors [r * newCols + c] = oldColors [r * oldCols + c];
0156 
0157     delete [] oldColors;
0158 }
0159 
0160 void kpColorCellsBase::setColumnCount (int newColumns)
0161 {
0162     setRowColumnCounts (rowCount (), newColumns);
0163 }
0164 
0165 void kpColorCellsBase::setRowCount (int newRows)
0166 {
0167     setRowColumnCounts (newRows, columnCount ());
0168 }
0169 
0170 QColor kpColorCellsBase::color(int index) const
0171 {
0172     return d->colors[index];
0173 }
0174 
0175 int kpColorCellsBase::count() const
0176 {
0177     return rowCount() * columnCount();
0178 }
0179 
0180 void kpColorCellsBase::setShading(bool _shade)
0181 {
0182     d->shade = _shade;
0183 }
0184 
0185 void kpColorCellsBase::setAcceptDrags(bool _acceptDrags)
0186 {
0187     d->acceptDrags = _acceptDrags;
0188 }
0189 
0190 void kpColorCellsBase::setCellsResizable(bool yes)
0191 {
0192     d->cellsResizable = yes;
0193 }
0194 
0195 void kpColorCellsBase::setSelected(int index)
0196 {
0197     Q_ASSERT( index >= 0 && index < count() );
0198 
0199     d->selected = index;
0200 }
0201 
0202 int kpColorCellsBase::selectedIndex() const
0203 {
0204     return d->selected;
0205 }
0206 
0207 //---------------------------------------------------------------------
0208 
0209 static void TableWidgetItemSetColor (QTableWidgetItem *tableItem, const QColor &color)
0210 {
0211     Q_ASSERT (tableItem);
0212     QImage image(16, 16, QImage::Format_ARGB32_Premultiplied);
0213     QPainter painter(&image);
0214     const int StippleSize = 4;
0215     QColor useColor;
0216 
0217     for (int dy = 0; dy < 16; dy += StippleSize)
0218     {
0219         for (int dx = 0; dx < 16; dx += StippleSize)
0220         {
0221             const bool parity = ((dy + dx) / StippleSize) % 2;
0222 
0223             if (!parity)
0224                 useColor = Qt::white;
0225             else
0226                 useColor = Qt::lightGray;
0227 
0228             painter.fillRect(dx, dy, StippleSize, StippleSize, useColor);
0229         }
0230     }
0231 
0232     painter.fillRect(image.rect(), color);
0233     painter.end();
0234 
0235     tableItem->setData(Qt::BackgroundRole , QBrush(image));
0236 }
0237 
0238 //---------------------------------------------------------------------
0239 
0240 void kpColorCellsBase::setColor( int column, const QColor &colorIn )
0241 {
0242     const int tableRow = column / columnCount();
0243     const int tableColumn = column % columnCount();
0244 
0245     Q_ASSERT( tableRow >= 0 && tableRow < rowCount() );
0246     Q_ASSERT( tableColumn >= 0 && tableColumn < columnCount() );
0247 
0248     QColor color = colorIn;
0249 
0250     d->colors[column] = color;
0251 
0252     QTableWidgetItem* tableItem = item(tableRow,tableColumn);
0253 
0254     if (color.isValid ())
0255     {
0256         if ( tableItem == nullptr ) {
0257             tableItem = new QTableWidgetItem();
0258             setItem(tableRow,tableColumn,tableItem);
0259         }
0260 
0261         if (isEnabled ())
0262             ::TableWidgetItemSetColor (tableItem, color);
0263     }
0264     else
0265     {
0266         delete tableItem;
0267     }
0268 
0269     Q_EMIT colorChanged (column, color);
0270 }
0271 
0272 void kpColorCellsBase::changeEvent( QEvent* event )
0273 {
0274     QTableWidget::changeEvent (event);
0275 
0276     if (event->type () != QEvent::EnabledChange)
0277         return;
0278 
0279     for (int r = 0; r < rowCount (); r++)
0280     {
0281         for (int c = 0; c < columnCount (); c++)
0282         {
0283             const int index = r * columnCount () + c;
0284 
0285             QTableWidgetItem* tableItem = item(r, c);
0286 
0287             // See API Doc for this invariant.
0288             Q_ASSERT (!!tableItem == d->colors [index].isValid ());
0289 
0290             if (!tableItem)
0291                 continue;
0292 
0293 
0294             QColor color;
0295             if (isEnabled ())
0296                 color = d->colors [index];
0297             else
0298                 color = palette ().color (backgroundRole ());
0299 
0300             ::TableWidgetItemSetColor (tableItem, color);
0301         }
0302     }
0303 }
0304 
0305 /*void kpColorCellsBase::paintCell( QPainter *painter, int row, int col )
0306 {
0307     painter->setRenderHint( QPainter::Antialiasing , true );
0308 
0309     QBrush brush;
0310     int w = 1;
0311 
0312     if (shade)
0313     {
0314         qDrawShadePanel( painter, 1, 1, cellWidth()-2,
0315                          cellHeight()-2, palette(), true, 1, &brush );
0316         w = 2;
0317     }
0318     QColor color = colors[ row * numCols() + col ];
0319     if (!color.isValid())
0320     {
0321         if (!shade) return;
0322         color = palette().color(backgroundRole());
0323     }
0324 
0325     const QRect colorRect( w, w, cellWidth()-w*2, cellHeight()-w*2 );
0326     painter->fillRect( colorRect, color );
0327 
0328     if ( row * numCols() + col == selected ) {
0329         painter->setPen( qGray(color.rgb())>=127 ? Qt::black : Qt::white );
0330         painter->drawLine( colorRect.topLeft(), colorRect.bottomRight() );
0331         painter->drawLine( colorRect.topRight(), colorRect.bottomLeft() );
0332     }
0333 }*/
0334 
0335 void kpColorCellsBase::resizeEvent( QResizeEvent* e )
0336 {
0337     if (d->cellsResizable)
0338     {
0339         // According to the Qt doc:
0340         //   If you need to set the width of a given column to a fixed value, call
0341         //   QHeaderView::resizeSection() on the table's {horizontal,vertical}
0342         //   header.
0343         // Therefore we iterate over each row and column and set the header section
0344         // size, as the sizeHint does indeed appear to be ignored in favor of a
0345         // minimum size that is larger than what we want.
0346         for ( int index = 0 ; index < columnCount() ; index++ )
0347             horizontalHeader()->resizeSection( index, sizeHintForColumn(index) );
0348         for ( int index = 0 ; index < rowCount() ; index++ )
0349             verticalHeader()->resizeSection( index, sizeHintForRow(index) );
0350     }
0351     else
0352     {
0353         // Update scrollbars if they're forced on by a subclass.
0354         // TODO: Should the d->cellsResizable path (from kdelibs) do this as well?
0355         QTableWidget::resizeEvent (e);
0356     }
0357 }
0358 
0359 int kpColorCellsBase::sizeHintForColumn(int /*column*/) const
0360 {
0361     // TODO: Should it be "(width() - frameWidth() * 2) / columnCount()"?
0362     return width() / columnCount() ;
0363 }
0364 
0365 int kpColorCellsBase::sizeHintForRow(int /*row*/) const
0366 {
0367     // TODO: Should be "(height() - frameWidth() * 2) / rowCount()"?
0368     return height() / rowCount() ;
0369 }
0370 
0371 void kpColorCellsBase::mousePressEvent( QMouseEvent *e )
0372 {
0373     d->inMouse = true;
0374     d->mousePos = e->pos();
0375 }
0376 
0377 
0378 int kpColorCellsBase::positionToCell(const QPoint &pos, bool ignoreBorders,
0379         bool allowEmptyCell) const
0380 {
0381     //TODO ignoreBorders not yet handled
0382     Q_UNUSED( ignoreBorders )
0383 
0384     const int r = indexAt (pos).row (), c = indexAt (pos).column ();
0385 #if DEBUG_KP_COLOR_CELLS_BASE
0386     qCDebug(kpLogColorCollection) << "r=" << r << "c=" << c;
0387 #endif
0388 
0389     if (r == -1 || c == -1)
0390        return -1;
0391 
0392     if (!allowEmptyCell && !itemAt(pos))
0393         return -1;
0394 
0395     const int cell = r * columnCount() + c;
0396 
0397    /*if (!ignoreBorders)
0398    {
0399       int border = 2;
0400       int x = pos.x() - col * cellWidth();
0401       int y = pos.y() - row * cellHeight();
0402       if ( (x < border) || (x > cellWidth()-border) ||
0403            (y < border) || (y > cellHeight()-border))
0404          return -1;
0405    }*/
0406 
0407     return cell;
0408 }
0409 
0410 
0411 void kpColorCellsBase::mouseMoveEvent( QMouseEvent *e )
0412 {
0413     if( !(e->buttons() & Qt::LeftButton)) return;
0414 
0415     if(d->inMouse) {
0416         int delay = QApplication::startDragDistance();
0417         if(e->x() > d->mousePos.x()+delay || e->x() < d->mousePos.x()-delay ||
0418            e->y() > d->mousePos.y()+delay || e->y() < d->mousePos.y()-delay){
0419             // Drag color object
0420             int cell = positionToCell(d->mousePos);
0421             if (cell != -1)
0422             {
0423             #if DEBUG_KP_COLOR_CELLS_BASE
0424                qCDebug(kpLogColorCollection) << "beginning drag from cell=" << cell
0425                          << "color: isValid=" << d->colors [cell].isValid ()
0426                          << " rgba=" << (int *) d->colors [cell].rgba();
0427             #endif
0428                Q_ASSERT (d->colors[cell].isValid());
0429                KColorMimeData::createDrag(d->colors[cell], this)->exec(Qt::CopyAction | Qt::MoveAction);
0430             #if DEBUG_KP_COLOR_CELLS_BASE
0431                qCDebug(kpLogColorCollection) << "finished drag";
0432             #endif
0433             }
0434         }
0435     }
0436 }
0437 
0438 
0439 // LOTODO: I'm not quite clear on how the drop actions logic is supposed
0440 //         to be done e.g.:
0441 //
0442 //         1. Who is supposed to call setDropAction().
0443 //         2. Which variant of accept(), setAccepted(), acceptProposedAction() etc.
0444 //            is supposed to be called to accept a move -- rather than copy --
0445 //            action.
0446 //
0447 //         Nevertheless, it appears to work -- probably because we restrict
0448 //         the non-Qt-default move/swap action to be intrawidget.
0449 static void SetDropAction (QWidget *self, QDropEvent *event)
0450 {
0451      // TODO: Would be nice to default to CopyAction if the destination cell
0452      //       is null.
0453      if (event->source () == self && (event->keyboardModifiers () & Qt::ControlModifier) == 0)
0454          event->setDropAction(Qt::MoveAction);
0455      else
0456          event->setDropAction(Qt::CopyAction);
0457 }
0458 
0459 void kpColorCellsBase::dragEnterEvent( QDragEnterEvent *event)
0460 {
0461 #if DEBUG_KP_COLOR_CELLS_BASE
0462      qCDebug(kpLogColorCollection) << "kpColorCellsBase::dragEnterEvent() acceptDrags="
0463                << d->acceptDrags
0464                << " canDecode=" << KColorMimeData::canDecode(event->mimeData());
0465 #endif
0466      event->setAccepted( d->acceptDrags && KColorMimeData::canDecode( event->mimeData()));
0467      if (event->isAccepted ())
0468          ::SetDropAction (this, event);
0469 }
0470 
0471 // Reimplemented to override QTableWidget's override.  Else dropping doesn't work.
0472 void kpColorCellsBase::dragMoveEvent (QDragMoveEvent *event)
0473 {
0474 #if DEBUG_KP_COLOR_CELLS_BASE
0475      qCDebug(kpLogColorCollection) << "kpColorCellsBase::dragMoveEvent() acceptDrags="
0476                << d->acceptDrags
0477                << " canDecode=" << KColorMimeData::canDecode(event->mimeData());
0478 #endif
0479      // TODO: Disallow drag that isn't onto a cell.
0480      event->setAccepted( d->acceptDrags && KColorMimeData::canDecode( event->mimeData()));
0481      if (event->isAccepted ())
0482          ::SetDropAction (this, event);
0483 }
0484 
0485 void kpColorCellsBase::dropEvent( QDropEvent *event)
0486 {
0487      QColor c=KColorMimeData::fromMimeData(event->mimeData());
0488 
0489      const int dragSourceCell = event->source () == this ?
0490          positionToCell (d->mousePos, true) :
0491          -1;
0492 #if DEBUG_KP_COLOR_CELLS_BASE
0493      qCDebug(kpLogColorCollection) << "kpColorCellsBase::dropEvent()"
0494                << "color: rgba=" << (const int *) c.rgba () << "isValid=" << c.isValid()
0495                << "source=" << event->source () << "dragSourceCell=" << dragSourceCell;
0496 #endif
0497      if( c.isValid()) {
0498           ::SetDropAction (this, event);
0499 
0500           int cell = positionToCell(event->pos(), true, true/*allow empty cell*/);
0501      #if DEBUG_KP_COLOR_CELLS_BASE
0502           qCDebug(kpLogColorCollection) << "\tcell=" << cell;
0503      #endif
0504           // TODO: I believe kdelibs forgets to do this.
0505           if (cell == -1)
0506               return;
0507 
0508           // Avoid NOP.
0509           if (cell == dragSourceCell)
0510               return;
0511 
0512           QColor destOldColor = d->colors [cell];
0513       setColor(cell,c);
0514 
0515     #if DEBUG_KP_COLOR_CELLS_BASE
0516           qCDebug(kpLogColorCollection) << "\tdropAction=" << event->dropAction ()
0517                     << "destOldColor.rgba=" << (const int *) destOldColor.rgba ();
0518     #endif
0519           if (event->dropAction () == Qt::MoveAction && dragSourceCell != -1) {
0520               setColor(dragSourceCell, destOldColor);
0521           }
0522      }
0523 }
0524 
0525 void kpColorCellsBase::mouseReleaseEvent( QMouseEvent *e )
0526 {
0527     int cell = positionToCell(d->mousePos);
0528     int currentCell = positionToCell(e->pos());
0529 
0530         // If we release the mouse in another cell and we don't have
0531         // a drag we should ignore this event.
0532         if (currentCell != cell)
0533            cell = -1;
0534 
0535     if ( (cell != -1) && (d->selected != cell) )
0536     {
0537         d->selected = cell;
0538 
0539         const int newRow = cell/columnCount();
0540         const int newColumn = cell%columnCount();
0541 
0542         clearSelection(); // we do not want old violet selected cells
0543 
0544         item(newRow,newColumn)->setSelected(true);
0545     }
0546 
0547     d->inMouse = false;
0548     if (cell != -1)
0549     {
0550         Q_EMIT colorSelected( cell , color(cell) );
0551         Q_EMIT colorSelectedWhitButton( cell , color(cell), e->button() );
0552     }
0553 }
0554 
0555 void kpColorCellsBase::mouseDoubleClickEvent( QMouseEvent * /*e*/ )
0556 {
0557   int cell = positionToCell(d->mousePos, false, true/*allow empty cell*/);
0558 
0559   if (cell != -1)
0560     Q_EMIT colorDoubleClicked( cell , color(cell) );
0561 }
0562 
0563 #include "moc_kpColorCellsBase.cpp"