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