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