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