File indexing completed on 2024-04-14 15:52:52
0001 /* 0002 This file is part of the Okteta Gui library, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2003, 2007-2010 Friedrich W. H. Kossebau <kossebau@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include "bytearraycolumnview_p.hpp" 0010 0011 // lib 0012 #include "oktetagui.hpp" 0013 #include "bordercolumnrenderer.hpp" 0014 #include "widgetcolumnstylist.hpp" 0015 #include "controller/dropper.hpp" 0016 #include "cursor.hpp" 0017 // Okteta core 0018 #include <Okteta/ValueCodec> 0019 // Qt 0020 #include <QStyle> 0021 #include <QFontDatabase> 0022 #include <QPainter> 0023 #include <QScrollBar> 0024 #include <QEvent> 0025 0026 namespace Okteta { 0027 0028 ByteArrayColumnViewPrivate::ByteArrayColumnViewPrivate(ByteArrayColumnView* parent) 0029 : AbstractByteArrayViewPrivate(parent) 0030 { 0031 } 0032 0033 ByteArrayColumnViewPrivate::~ByteArrayColumnViewPrivate() = default; 0034 0035 void ByteArrayColumnViewPrivate::init() 0036 { 0037 Q_Q(ByteArrayColumnView); 0038 0039 // creating the columns in the needed order 0040 mValueColumn = 0041 new ValueByteArrayColumnRenderer(mStylist, mByteArrayModel, mTableLayout, mTableRanges); 0042 mMiddleBorderColumn = 0043 new BorderColumnRenderer(mStylist, true); 0044 mCharColumn = 0045 new CharByteArrayColumnRenderer(mStylist, mByteArrayModel, mTableLayout, mTableRanges); 0046 0047 q->addColumn(mOffsetColumn); 0048 q->addColumn(mOffsetBorderColumn); 0049 q->addColumn(mValueColumn); 0050 q->addColumn(mMiddleBorderColumn); 0051 q->addColumn(mCharColumn); 0052 0053 // select the active column 0054 mActiveColumn = mCharColumn; 0055 mInactiveColumn = mValueColumn; 0056 0057 // set char encoding 0058 mValueColumn->setValueCodec((ValueCoding)mValueCoding, mValueCodec); 0059 mValueColumn->setCharCodec(mCharCodec); 0060 mCharColumn->setCharCodec(mCharCodec); 0061 0062 adaptController(); 0063 0064 // do here, not in base class, as changeEvent(fontEvent) needs this init run before 0065 q->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0066 } 0067 0068 AbstractByteArrayView::CodingTypeId ByteArrayColumnViewPrivate::activeCoding() const 0069 { 0070 const bool isValueColumnActive = (mActiveColumn == (AbstractByteArrayColumnRenderer*)mValueColumn); 0071 return isValueColumnActive ? AbstractByteArrayView::ValueCodingId : AbstractByteArrayView::CharCodingId; 0072 } 0073 0074 AbstractByteArrayView::CodingTypes ByteArrayColumnViewPrivate::visibleCodings() const 0075 { 0076 // TODO: try to improve this code 0077 return (AbstractByteArrayView::CodingTypes) 0078 ((mValueColumn->isVisible() ? AbstractByteArrayView::ValueCodingId : 0) 0079 | (mCharColumn->isVisible() ? AbstractByteArrayView::CharCodingId : 0)); 0080 } 0081 0082 void ByteArrayColumnViewPrivate::setByteArrayModel(AbstractByteArrayModel* _byteArrayModel) 0083 { 0084 mValueEditor->reset(); 0085 0086 // TODO: this fails if _byteArrayModel == null 0087 mValueColumn->set(_byteArrayModel); 0088 mCharColumn->set(_byteArrayModel); 0089 0090 AbstractByteArrayViewPrivate::setByteArrayModel(_byteArrayModel); 0091 } 0092 0093 void ByteArrayColumnViewPrivate::setBufferSpacing(PixelX ByteSpacing, int noOfGroupedBytes, PixelX GroupSpacing) 0094 { 0095 if (!mValueColumn->setSpacing(ByteSpacing, noOfGroupedBytes, GroupSpacing)) { 0096 return; 0097 } 0098 0099 updateViewByWidth(); 0100 } 0101 0102 void ByteArrayColumnViewPrivate::setValueCoding(AbstractByteArrayView::ValueCoding valueCoding) 0103 { 0104 Q_Q(ByteArrayColumnView); 0105 0106 if (mValueCoding == valueCoding) { 0107 return; 0108 } 0109 0110 const uint oldCodingWidth = mValueCodec->encodingWidth(); 0111 0112 AbstractByteArrayViewPrivate::setValueCoding(valueCoding); 0113 0114 mValueColumn->setValueCodec((ValueCoding)mValueCoding, mValueCodec); 0115 mValueEditor->adaptToValueCodecChange(); 0116 0117 const uint newCodingWidth = mValueCodec->encodingWidth(); 0118 0119 // no change in the width? 0120 if (newCodingWidth == oldCodingWidth) { 0121 q->updateColumn(*mValueColumn); 0122 } else { 0123 updateViewByWidth(); 0124 } 0125 0126 emit q->valueCodingChanged(valueCoding); 0127 } 0128 0129 void ByteArrayColumnViewPrivate::setByteSpacingWidth(int /*PixelX*/ byteSpacingWidth) 0130 { 0131 if (!mValueColumn->setByteSpacingWidth(byteSpacingWidth)) { 0132 return; 0133 } 0134 updateViewByWidth(); 0135 } 0136 0137 void ByteArrayColumnViewPrivate::setNoOfGroupedBytes(int noOfGroupedBytes) 0138 { 0139 Q_Q(ByteArrayColumnView); 0140 0141 if (!mValueColumn->setNoOfGroupedBytes(noOfGroupedBytes)) { 0142 return; 0143 } 0144 updateViewByWidth(); 0145 0146 emit q->noOfGroupedBytesChanged(noOfGroupedBytes); 0147 } 0148 0149 void ByteArrayColumnViewPrivate::setGroupSpacingWidth(int /*PixelX*/ groupSpacingWidth) 0150 { 0151 if (!mValueColumn->setGroupSpacingWidth(groupSpacingWidth)) { 0152 return; 0153 } 0154 updateViewByWidth(); 0155 } 0156 0157 void ByteArrayColumnViewPrivate::setBinaryGapWidth(int /*PixelX*/ binaryGapWidth) 0158 { 0159 if (!mValueColumn->setBinaryGapWidth(binaryGapWidth)) { 0160 return; 0161 } 0162 updateViewByWidth(); 0163 } 0164 0165 void ByteArrayColumnViewPrivate::setSubstituteChar(QChar substituteChar) 0166 { 0167 Q_Q(ByteArrayColumnView); 0168 0169 if (!mCharColumn->setSubstituteChar(substituteChar)) { 0170 return; 0171 } 0172 pauseCursor(); 0173 q->updateColumn(*mCharColumn); 0174 unpauseCursor(); 0175 0176 emit q->substituteCharChanged(substituteChar); 0177 } 0178 0179 void ByteArrayColumnViewPrivate::setUndefinedChar(QChar undefinedChar) 0180 { 0181 Q_Q(ByteArrayColumnView); 0182 0183 if (!mCharColumn->setUndefinedChar(undefinedChar)) { 0184 return; 0185 } 0186 pauseCursor(); 0187 q->updateColumn(*mCharColumn); 0188 unpauseCursor(); 0189 0190 emit q->undefinedCharChanged(undefinedChar); 0191 } 0192 0193 void ByteArrayColumnViewPrivate::setShowsNonprinting(bool showingNonprinting) 0194 { 0195 Q_Q(ByteArrayColumnView); 0196 0197 if (!mCharColumn->setShowingNonprinting(showingNonprinting)) { 0198 return; 0199 } 0200 pauseCursor(); 0201 q->updateColumn(*mCharColumn); 0202 unpauseCursor(); 0203 0204 emit q->showsNonprintingChanged(showingNonprinting); 0205 } 0206 0207 void ByteArrayColumnViewPrivate::setCharCoding(AbstractByteArrayView::CharCoding charCoding) 0208 { 0209 Q_Q(ByteArrayColumnView); 0210 0211 if (mCharCoding == charCoding) { 0212 return; 0213 } 0214 0215 AbstractByteArrayViewPrivate::setCharCoding(charCoding); 0216 0217 pauseCursor(); 0218 0219 mValueColumn->setCharCodec(mCharCodec); 0220 mCharColumn->setCharCodec(mCharCodec); 0221 0222 q->updateColumn(*mValueColumn); 0223 q->updateColumn(*mCharColumn); 0224 0225 unpauseCursor(); 0226 0227 emit q->charCodecChanged(charCodingName()); 0228 } 0229 0230 // TODO: join with function above! 0231 void ByteArrayColumnViewPrivate::setCharCoding(const QString& newCharCodingName) 0232 { 0233 Q_Q(ByteArrayColumnView); 0234 0235 if (charCodingName() == newCharCodingName) { 0236 return; 0237 } 0238 0239 AbstractByteArrayViewPrivate::setCharCoding(newCharCodingName); 0240 0241 pauseCursor(); 0242 0243 mValueColumn->setCharCodec(mCharCodec); 0244 mCharColumn->setCharCodec(mCharCodec); 0245 0246 q->updateColumn(*mValueColumn); 0247 q->updateColumn(*mCharColumn); 0248 0249 unpauseCursor(); 0250 0251 emit q->charCodecChanged(charCodingName()); 0252 } 0253 0254 void ByteArrayColumnViewPrivate::setByteTypeColored(bool isColored) 0255 { 0256 Q_Q(ByteArrayColumnView); 0257 0258 if (isColored == mValueColumn->isByteTypeColored()) { 0259 return; 0260 } 0261 0262 mValueColumn->setByteTypeColored(isColored); 0263 mCharColumn->setByteTypeColored(isColored); 0264 0265 pauseCursor(); 0266 q->updateColumn(*mValueColumn); 0267 q->updateColumn(*mCharColumn); 0268 unpauseCursor(); 0269 } 0270 0271 void ByteArrayColumnViewPrivate::changeEvent(QEvent* event) 0272 { 0273 Q_Q(ByteArrayColumnView); 0274 0275 // make sure base class deals with this event first, 0276 // as some values which are used here are updated there 0277 q->AbstractByteArrayView::changeEvent(event); 0278 0279 if (event->type() != QEvent::FontChange) { 0280 return; 0281 } 0282 0283 // get new values 0284 const QFontMetrics newFontMetrics = q->fontMetrics(); 0285 const PixelY digitHeight = newFontMetrics.height(); 0286 0287 mOffsetColumn->setFontMetrics(newFontMetrics); 0288 mValueColumn->setFontMetrics(newFontMetrics); 0289 mCharColumn->setFontMetrics(newFontMetrics); 0290 0291 q->setLineHeight(digitHeight); 0292 0293 // update all dependent structures 0294 mTableLayout->setNoOfLinesPerPage(q->noOfLinesPerPage()); 0295 0296 updateViewByWidth(); 0297 } 0298 0299 void ByteArrayColumnViewPrivate::adjustToLayoutNoOfBytesPerLine() 0300 { 0301 Q_Q(ByteArrayColumnView); 0302 0303 mValueColumn->resetXBuffer(); 0304 mCharColumn->resetXBuffer(); 0305 0306 q->updateWidths(); 0307 } 0308 0309 QSize ByteArrayColumnViewPrivate::minimumSizeHint() const 0310 { 0311 Q_Q(const ByteArrayColumnView); 0312 0313 // TODO: better minimal width (visibility!) 0314 const int minWidth = 0315 mOffsetColumn->visibleWidth() 0316 + mOffsetBorderColumn->visibleWidth() 0317 + mMiddleBorderColumn->visibleWidth() 0318 + mValueColumn->byteWidth() 0319 + mCharColumn->byteWidth(); 0320 const int minHeight = 0321 q->lineHeight() 0322 + q->noOfLines() > 1 ? q->style()->pixelMetric(QStyle::PM_ScrollBarExtent) : 0; 0323 0324 return {qMin(minWidth, 100), qMin(minHeight, 100)}; 0325 } 0326 0327 int ByteArrayColumnViewPrivate::fittingBytesPerLine() const 0328 { 0329 Q_Q(const ByteArrayColumnView); 0330 0331 const QSize newSize = q->maximumViewportSize(); 0332 const PixelX reservedWidth = 0333 mOffsetColumn->visibleWidth() 0334 + mOffsetBorderColumn->visibleWidth() 0335 + mMiddleBorderColumn->visibleWidth(); 0336 0337 // abstract offset and border columns width 0338 const PixelX fullWidth = newSize.width() - reservedWidth; 0339 0340 // // no width left for resizeable columns? TODO: put this in resizeEvent 0341 // if( fullWidth < 0 ) 0342 // return; 0343 0344 const PixelY fullHeight = newSize.height(); 0345 0346 // check influence of dis-/appearing of the vertical scrollbar 0347 const bool verticalScrollbarIsVisible = q->verticalScrollBar()->isVisible(); 0348 const PixelX scrollbarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent); 0349 0350 PixelX availableWidth = fullWidth; 0351 if (verticalScrollbarIsVisible) { 0352 availableWidth -= scrollbarExtent; 0353 } 0354 0355 enum MatchTrial 0356 { 0357 FirstRun, 0358 RerunWithScrollbarOn, 0359 TestWithoutScrollbar 0360 }; 0361 MatchTrial matchRun = FirstRun; 0362 0363 // prepare needed values 0364 const PixelX digitWidth = mValueColumn->digitWidth(); 0365 const PixelX charByteWidth = mCharColumn->isVisible() ? digitWidth : 0; 0366 const PixelX valueByteWidth = mValueColumn->isVisible() ? mValueColumn->byteWidth() : 0; 0367 const PixelX byteSpacingWidth = mValueColumn->isVisible() ? mValueColumn->byteSpacingWidth() : 0; 0368 PixelX groupSpacingWidth; 0369 int noOfGroupedBytes = mValueColumn->noOfGroupedBytes(); 0370 // no grouping? 0371 if (noOfGroupedBytes == 0) { 0372 // fake no grouping by grouping with 1 and using byteSpacingWidth 0373 noOfGroupedBytes = 1; 0374 groupSpacingWidth = mValueColumn->isVisible() ? byteSpacingWidth : 0; 0375 } else { 0376 groupSpacingWidth = mValueColumn->isVisible() ? mValueColumn->groupSpacingWidth() : 0; 0377 } 0378 0379 const PixelX valueByteGroupWidth = noOfGroupedBytes * valueByteWidth + (noOfGroupedBytes - 1) * byteSpacingWidth; 0380 const PixelX charByteGroupWidth = noOfGroupedBytes * charByteWidth; 0381 const PixelX totalGroupWidth = valueByteGroupWidth + groupSpacingWidth + charByteGroupWidth; 0382 0383 int fittingBytesPerLine; 0384 int fittingBytesPerLineWithScrollbar = 0; 0385 for (;;) { 0386 // qCDebug(LOG_OKTETA_GUI) << "matchWidth: " << fullWidth 0387 // << " (v:" << visibleWidth() 0388 // << ", f:" << frameWidth() 0389 // << ", A:" << availableWidth 0390 // << ", S:" << scrollbarExtent 0391 // << ", R:" << reservedWidth << ")" << endl; 0392 0393 // calculate fitting groups per line 0394 const int fittingGroupsPerLine = (availableWidth + groupSpacingWidth) // fake spacing after last group 0395 / totalGroupWidth; 0396 0397 // calculate the fitting bytes per line by groups 0398 fittingBytesPerLine = noOfGroupedBytes * fittingGroupsPerLine; 0399 0400 // not only full groups? 0401 if (mResizeStyle == AbstractByteArrayView::FullSizeLayoutStyle && noOfGroupedBytes > 1) { 0402 0403 if (fittingGroupsPerLine > 0) { 0404 availableWidth -= fittingGroupsPerLine * totalGroupWidth; // includes additional spacing after last group 0405 0406 } 0407 // qCDebug(LOG_OKTETA_GUI) << "Left: " << availableWidth << "("<<valueByteWidth<<", "<<charByteWidth<<")" ; 0408 0409 if (availableWidth > 0) { 0410 fittingBytesPerLine += (availableWidth + byteSpacingWidth) / (valueByteWidth + byteSpacingWidth + charByteWidth); 0411 } 0412 0413 // is there not even the space for a single byte? 0414 if (fittingBytesPerLine == 0) { 0415 // ensure at least one byte per line 0416 fittingBytesPerLine = 1; 0417 // and 0418 break; 0419 } 0420 } 0421 // is there not the space for a single group? 0422 else if (fittingBytesPerLine == 0) { 0423 // ensures at least one group 0424 fittingBytesPerLine = noOfGroupedBytes; 0425 break; 0426 } 0427 0428 // qCDebug(LOG_OKTETA_GUI) << "meantime: " << fittingGroupsPerLine << " (T:" << totalGroupWidth 0429 // << ", h:" << valueByteGroupWidth 0430 // << ", t:" << charByteGroupWidth 0431 // << ", s:" << groupSpacingWidth << ") " <<fittingBytesPerLine<< endl; 0432 0433 const int newNoOfLines = (mTableLayout->length() + mTableLayout->startOffset() + fittingBytesPerLine - 1) 0434 / fittingBytesPerLine; 0435 const PixelY newHeight = newNoOfLines * q->lineHeight(); 0436 0437 if (verticalScrollbarIsVisible) { 0438 if (matchRun == TestWithoutScrollbar) { 0439 // did the test without the scrollbar fail, don't the data fit into the view? 0440 if (newHeight > fullHeight) { 0441 // reset to old calculated value 0442 fittingBytesPerLine = fittingBytesPerLineWithScrollbar; 0443 } 0444 break; 0445 } 0446 0447 // a chance for to perhaps fit in height? 0448 if (fittingBytesPerLine <= mTableLayout->noOfBytesPerLine()) { 0449 // remember this trial's result and calc number of bytes with vertical scrollbar on 0450 fittingBytesPerLineWithScrollbar = fittingBytesPerLine; 0451 availableWidth = fullWidth; 0452 matchRun = TestWithoutScrollbar; 0453 // qCDebug(LOG_OKTETA_GUI) << "tested without scrollbar..." ; 0454 continue; 0455 } 0456 } else { 0457 // doesn't it fit into the height anymore? 0458 if (newHeight > fullHeight && matchRun == FirstRun) { 0459 // need for a scrollbar has risen... ->less width, new calculation 0460 availableWidth = fullWidth - scrollbarExtent; 0461 matchRun = RerunWithScrollbarOn; 0462 // qCDebug(LOG_OKTETA_GUI) << "rerun with scrollbar on..." ; 0463 continue; 0464 } 0465 } 0466 0467 break; 0468 } 0469 0470 return fittingBytesPerLine; 0471 } 0472 0473 void ByteArrayColumnViewPrivate::setVisibleCodings(int newColumns) 0474 { 0475 Q_Q(ByteArrayColumnView); 0476 0477 const int oldColumns = visibleCodings(); 0478 0479 // no changes or no column selected? 0480 if (newColumns == oldColumns || !(newColumns & AbstractByteArrayView::ValueAndCharCodings)) { 0481 return; 0482 } 0483 0484 mValueColumn->setVisible(AbstractByteArrayView::ValueCodingId & newColumns); 0485 mCharColumn->setVisible(AbstractByteArrayView::CharCodingId & newColumns); 0486 mMiddleBorderColumn->setVisible(newColumns == AbstractByteArrayView::ValueAndCharCodings); 0487 0488 // active column not visible anymore? 0489 if (!mActiveColumn->isVisible()) { 0490 AbstractByteArrayColumnRenderer* h = mActiveColumn; 0491 mActiveColumn = mInactiveColumn; 0492 mInactiveColumn = h; 0493 adaptController(); 0494 } 0495 0496 updateViewByWidth(); 0497 0498 emit q->visibleByteArrayCodingsChanged(newColumns); 0499 } 0500 0501 void ByteArrayColumnViewPrivate::setActiveCoding(AbstractByteArrayView::CodingTypeId codingId) 0502 { 0503 // no changes or not visible? 0504 if (codingId == activeCoding() 0505 || (codingId == AbstractByteArrayView::ValueCodingId && !mValueColumn->isVisible()) 0506 || (codingId == AbstractByteArrayView::CharCodingId && !mCharColumn->isVisible())) { 0507 return; 0508 } 0509 0510 pauseCursor(); 0511 mValueEditor->finishEdit(); 0512 0513 if (codingId == AbstractByteArrayView::ValueCodingId) { 0514 mActiveColumn = mValueColumn; 0515 mInactiveColumn = mCharColumn; 0516 } else { 0517 mActiveColumn = mCharColumn; 0518 mInactiveColumn = mValueColumn; 0519 } 0520 adaptController(); 0521 0522 ensureCursorVisible(); 0523 unpauseCursor(); 0524 } 0525 0526 void ByteArrayColumnViewPrivate::placeCursor(QPoint point) 0527 { 0528 Q_Q(ByteArrayColumnView); 0529 0530 // switch active column if needed 0531 if (mCharColumn->isVisible() && point.x() >= mCharColumn->x()) { 0532 mActiveColumn = mCharColumn; 0533 mInactiveColumn = mValueColumn; 0534 } else { 0535 mActiveColumn = mValueColumn; 0536 mInactiveColumn = mCharColumn; 0537 } 0538 adaptController(); 0539 0540 // get coord of click and whether this click was closer to the end of the pos 0541 const int linePosition = mActiveColumn->magneticLinePositionOfX(point.x()); 0542 const int lineIndex = q->lineAt(point.y()); 0543 const Coord coord(linePosition, lineIndex); 0544 0545 mTableCursor->gotoCCoord(coord); 0546 emit q->cursorPositionChanged(cursorPosition()); 0547 } 0548 0549 Address ByteArrayColumnViewPrivate::indexByPoint(QPoint point) const 0550 { 0551 Q_Q(const ByteArrayColumnView); 0552 0553 const AbstractByteArrayColumnRenderer* column = 0554 (mCharColumn->isVisible() && point.x() >= mCharColumn->x()) ? 0555 (AbstractByteArrayColumnRenderer*)mCharColumn : (AbstractByteArrayColumnRenderer*)mValueColumn; 0556 0557 const Coord coord(column->linePositionOfX(point.x()), q->lineAt(point.y())); 0558 0559 return mTableLayout->indexAtCCoord(coord); 0560 } 0561 0562 void ByteArrayColumnViewPrivate::blinkCursor() 0563 { 0564 // skip the cursor drawing? 0565 if (mCursorPaused || mValueEditor->isInEditMode()) { 0566 return; 0567 } 0568 0569 // switch the cursor state 0570 mBlinkCursorVisible = !mBlinkCursorVisible; 0571 updateCursor(*mActiveColumn); 0572 } 0573 0574 void ByteArrayColumnViewPrivate::updateCursors() 0575 { 0576 createCursorPixmaps(); 0577 0578 mBlinkCursorVisible = true; 0579 updateCursor(*mActiveColumn); 0580 updateCursor(*mInactiveColumn); 0581 } 0582 0583 void ByteArrayColumnViewPrivate::pauseCursor() 0584 { 0585 mCursorPaused = true; 0586 0587 mBlinkCursorVisible = false; 0588 updateCursor(*mActiveColumn); 0589 updateCursor(*mInactiveColumn); 0590 } 0591 0592 QRect ByteArrayColumnViewPrivate::cursorRect() const 0593 { 0594 Q_Q(const ByteArrayColumnView); 0595 0596 QRect cursorRect = mActiveColumn->byteRect(mTableCursor->coord()); 0597 cursorRect.translate(-q->xOffset(), -q->yOffset()); 0598 0599 return cursorRect; 0600 } 0601 0602 void ByteArrayColumnViewPrivate::updateCursor(const AbstractByteArrayColumnRenderer& column) 0603 { 0604 Q_Q(ByteArrayColumnView); 0605 0606 if (!column.isVisible()) { 0607 return; 0608 } 0609 0610 QRect cursorRect = column.byteRect(mTableCursor->coord()); 0611 cursorRect.translate(-q->xOffset(), -q->yOffset()); 0612 0613 q->viewport()->update(cursorRect); 0614 } 0615 0616 void ByteArrayColumnViewPrivate::createCursorPixmaps() 0617 { 0618 Q_Q(ByteArrayColumnView); 0619 0620 const PixelX byteWidth = mActiveColumn->byteWidth(); 0621 0622 // create mCursorPixmaps 0623 mCursorPixmaps->setSize(byteWidth, q->lineHeight(), q->devicePixelRatio()); 0624 0625 const Address index = mTableCursor->validIndex(); 0626 0627 QPainter painter; 0628 painter.begin(&mCursorPixmaps->offPixmap()); 0629 initPainterFromWidget(&painter); 0630 mActiveColumn->renderByte(&painter, index); 0631 painter.end(); 0632 0633 painter.begin(&mCursorPixmaps->onPixmap()); 0634 initPainterFromWidget(&painter); 0635 mActiveColumn->renderCursor(&painter, index); 0636 painter.end(); 0637 0638 // calculat the shape 0639 PixelX cursorX; 0640 PixelX cursorW; 0641 if (isCursorBehind()) { 0642 cursorX = qMax(0, byteWidth - InsertCursorWidth); 0643 cursorW = InsertCursorWidth; 0644 } else { 0645 cursorX = 0; 0646 cursorW = mOverWrite ? -1 : InsertCursorWidth; 0647 } 0648 mCursorPixmaps->setShape(cursorX, cursorW, q->devicePixelRatio()); 0649 } 0650 0651 void ByteArrayColumnViewPrivate::drawActiveCursor(QPainter* painter) 0652 { 0653 Q_Q(ByteArrayColumnView); 0654 0655 // TODO: review the cursor drawing, not fully matching the new paint only on updates yet 0656 // see also rowView 0657 0658 // any reason to skip the cursor drawing? 0659 if (!q->hasFocus() && !q->viewport()->hasFocus() && !mDropper->isActive()) { 0660 return; 0661 } 0662 0663 const int x = mActiveColumn->xOfLinePosition(mTableCursor->pos()); 0664 const int y = q->lineHeight() * mTableCursor->line(); 0665 0666 painter->translate(x, y); 0667 0668 // paint edited byte? 0669 if (mValueEditor->isInEditMode()) { 0670 const Address index = mTableCursor->index(); 0671 0672 if (mBlinkCursorVisible) { 0673 mValueColumn->renderEditedByte(painter, mValueEditor->value(), mValueEditor->valueAsString()); 0674 } else { 0675 mValueColumn->renderByte(painter, index); 0676 } 0677 } else { 0678 painter->drawPixmap(mCursorPixmaps->cursorX(), 0, 0679 mBlinkCursorVisible ? mCursorPixmaps->onPixmap() : mCursorPixmaps->offPixmap(), 0680 mCursorPixmaps->shapeX(), 0, mCursorPixmaps->shapeW(), -1); 0681 } 0682 0683 painter->translate(-x, -y); 0684 } 0685 0686 void ByteArrayColumnViewPrivate::drawInactiveCursor(QPainter* painter) 0687 { 0688 Q_Q(ByteArrayColumnView); 0689 0690 // any reason to skip the cursor drawing? 0691 if (!mInactiveColumn->isVisible() 0692 || mCursorPaused 0693 || (!mCursorPaused && !q->hasFocus() && !q->viewport()->hasFocus() && !mDropper->isActive())) { 0694 return; 0695 } 0696 0697 const Address index = mTableCursor->validIndex(); 0698 0699 const int x = mInactiveColumn->xOfLinePosition(mTableCursor->pos()); 0700 const int y = q->lineHeight() * mTableCursor->line(); 0701 0702 painter->translate(x, y); 0703 0704 const AbstractByteArrayColumnRenderer::FrameStyle frameStyle = 0705 mTableCursor->isBehind() ? AbstractByteArrayColumnRenderer::Right : 0706 (mOverWrite || mValueEditor->isInEditMode()) ? AbstractByteArrayColumnRenderer::Frame : 0707 AbstractByteArrayColumnRenderer::Left; 0708 mInactiveColumn->renderFramedByte(painter, index, frameStyle); 0709 0710 painter->translate(-x, -y); 0711 } 0712 0713 void ByteArrayColumnViewPrivate::renderColumns(QPainter* painter, int cx, int cy, int cw, int ch) 0714 { 0715 Q_Q(ByteArrayColumnView); 0716 0717 q->AbstractByteArrayView::renderColumns(painter, cx, cy, cw, ch); 0718 // TODO: update non blinking cursors. Should this perhaps be done in the buffercolumn? 0719 // Then it needs to know about inactive, insideByte and the like... well... 0720 // perhaps subclassing the buffer columns even more, to CharByteArrayColumnRenderer and ValueByteArrayColumnRenderer? 0721 0722 if (q->visibleLines(PixelYRange::fromWidth(cy, ch)).includes(mTableCursor->line())) { 0723 drawActiveCursor(painter); 0724 drawInactiveCursor(painter); 0725 } 0726 } 0727 0728 void ByteArrayColumnViewPrivate::updateChanged() 0729 { 0730 Q_Q(ByteArrayColumnView); 0731 0732 const int xOffset = q->xOffset(); 0733 const PixelXRange Xs = PixelXRange::fromWidth(xOffset, q->visibleWidth()); 0734 0735 // do updates in offset column 0736 const LineRange changedOffsetLines = mTableRanges->changedOffsetLines(); 0737 if (!changedOffsetLines.isEmpty()) { 0738 q->updateColumn(*mOffsetColumn, changedOffsetLines); 0739 } 0740 0741 // collect affected buffer columns 0742 QVector<AbstractByteArrayColumnRenderer*> dirtyColumns; 0743 dirtyColumns.reserve(2); 0744 0745 AbstractByteArrayColumnRenderer* column = mValueColumn; 0746 while (true) { 0747 if (column->isVisible() && column->overlaps(Xs)) { 0748 dirtyColumns.append(column); 0749 column->prepareRendering(Xs); 0750 } 0751 0752 if (column == mCharColumn) { 0753 break; 0754 } 0755 column = mCharColumn; 0756 } 0757 0758 // any columns to paint? 0759 if (!dirtyColumns.isEmpty()) { 0760 // calculate affected lines/indizes 0761 const LinePositionRange fullPositions(0, mTableLayout->noOfBytesPerLine() - 1); 0762 CoordRange visibleRange(fullPositions, q->visibleLines()); 0763 0764 const int lineHeight = q->lineHeight(); 0765 CoordRange changedRange; 0766 // as there might be multiple selections on this line redo until no more is changed 0767 while (getNextChangedRange(&changedRange, visibleRange)) { 0768 PixelY cy = q->yOffsetOfLine(changedRange.start().line()); 0769 0770 // only one line? 0771 if (changedRange.start().line() == changedRange.end().line()) { 0772 const LinePositionRange changedPositions(changedRange.start().pos(), changedRange.end().pos()); 0773 for (auto column : qAsConst(dirtyColumns)) { 0774 const PixelXRange xPixels = column->xsOfLinePositionsInclSpaces(changedPositions); 0775 0776 q->viewport()->update(xPixels.start() - xOffset, cy, xPixels.width(), lineHeight); 0777 } 0778 } 0779 // 0780 else { 0781 // first line 0782 const LinePositionRange firstChangedPositions(changedRange.start().pos(), fullPositions.end()); 0783 for (auto column : qAsConst(dirtyColumns)) { 0784 const PixelXRange XPixels = column->xsOfLinePositionsInclSpaces(firstChangedPositions); 0785 0786 q->viewport()->update(XPixels.start() - xOffset, cy, XPixels.width(), lineHeight); 0787 } 0788 0789 // at least one full line? 0790 for (int l = changedRange.start().line() + 1; l < changedRange.end().line(); ++l) { 0791 cy += lineHeight; 0792 for (auto column : qAsConst(dirtyColumns)) { 0793 const PixelXRange XPixels = column->xsOfLinePositionsInclSpaces(fullPositions); 0794 0795 q->viewport()->update(XPixels.start() - xOffset, cy, XPixels.width(), lineHeight); 0796 } 0797 } 0798 0799 // last line 0800 cy += lineHeight; 0801 const LinePositionRange lastChangedPositions(fullPositions.start(), changedRange.end().pos()); 0802 for (auto column : qAsConst(dirtyColumns)) { 0803 const PixelXRange XPixels = column->xsOfLinePositionsInclSpaces(lastChangedPositions); 0804 0805 q->viewport()->update(XPixels.start() - xOffset, cy, XPixels.width(), lineHeight); 0806 } 0807 } 0808 0809 // continue the search at the overnext index 0810 visibleRange.setStart(changedRange.end() + 1); // +2 ); TODO: currently bounding ranges are not merged 0811 if (!visibleRange.isValid()) { 0812 break; 0813 } 0814 } 0815 } 0816 0817 mTableRanges->resetChangedRanges(); 0818 } 0819 0820 void ByteArrayColumnViewPrivate::ensureCursorVisible() 0821 { 0822 ensureVisible(*mActiveColumn, mTableCursor->coord()); 0823 } 0824 0825 void ByteArrayColumnViewPrivate::ensureVisible(const AddressRange& range, bool ensureStartVisible) 0826 { 0827 const CoordRange coords = mTableLayout->coordRangeOfIndizes(range); 0828 0829 // TODO: this is a make-it-work-hack, better do a smart calculation 0830 ensureVisible(*mActiveColumn, ensureStartVisible ? coords.end() : coords.start()); 0831 ensureVisible(*mActiveColumn, ensureStartVisible ? coords.start() : coords.end()); 0832 } 0833 0834 void ByteArrayColumnViewPrivate::ensureVisible(const AbstractByteArrayColumnRenderer& column, Coord coord) 0835 { 0836 Q_Q(ByteArrayColumnView); 0837 0838 const QRect byteRect = column.byteRect(coord); 0839 0840 const PixelXRange byteXs = PixelXRange::fromWidth(byteRect.x(), byteRect.width()); 0841 const PixelYRange byteYs = PixelYRange::fromWidth(byteRect.y(), byteRect.height()); 0842 0843 const PixelXRange visibleXs = PixelXRange::fromWidth(q->xOffset(), q->visibleWidth()); 0844 const PixelYRange visibleYs = PixelXRange::fromWidth(q->yOffset(), q->visibleHeight()); 0845 0846 q->horizontalScrollBar()->setValue(visibleXs.startForInclude(byteXs)); 0847 q->verticalScrollBar()->setValue(visibleYs.startForInclude(byteYs)); 0848 } 0849 0850 }