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"