File indexing completed on 2024-04-28 17:07:10
0001 /* 0002 This file is part of the Okteta Gui library, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2003, 2007, 2008 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 "columnsview.hpp" 0010 #include "columnsview_p.hpp" 0011 0012 // Qt 0013 #include <QPaintEvent> 0014 #include <QPainter> 0015 #include <QStyle> 0016 #include <QScrollBar> 0017 0018 namespace Okteta { 0019 0020 ColumnsView::ColumnsView(ColumnsViewPrivate* dd, QWidget* parent) 0021 : QAbstractScrollArea(parent) 0022 , d_ptr(dd) 0023 { 0024 Q_D(ColumnsView); 0025 0026 d->init(); 0027 } 0028 0029 ColumnsView::ColumnsView(/*bool R,*/ QWidget* parent) 0030 : QAbstractScrollArea(parent) 0031 , d_ptr(new ColumnsViewPrivate(this)) 0032 { 0033 Q_D(ColumnsView); 0034 0035 d->init(); 0036 } 0037 0038 ColumnsView::~ColumnsView() = default; 0039 0040 LineSize ColumnsView::noOfLines() const 0041 { 0042 Q_D(const ColumnsView); 0043 0044 return d->NoOfLines; 0045 } 0046 0047 PixelY ColumnsView::lineHeight() const 0048 { 0049 Q_D(const ColumnsView); 0050 0051 return d->LineHeight; 0052 } 0053 0054 Line ColumnsView::lineAt(PixelY y) const 0055 { 0056 Q_D(const ColumnsView); 0057 0058 return (d->LineHeight != 0) ? y / d->LineHeight : 0; 0059 } 0060 0061 LineRange ColumnsView::visibleLines() const 0062 { 0063 const PixelYRange ySpan = PixelYRange::fromWidth(yOffset(), visibleHeight()); 0064 return LineRange(lineAt(ySpan.start()), lineAt(ySpan.end())); 0065 } 0066 LineRange ColumnsView::visibleLines(const PixelYRange& yPixels) const 0067 { 0068 return LineRange(lineAt(yPixels.start()), lineAt(yPixels.end())); 0069 } 0070 0071 PixelX ColumnsView::visibleWidth() const 0072 { 0073 return viewport()->width(); 0074 } 0075 0076 PixelY ColumnsView::visibleHeight() const 0077 { 0078 return viewport()->height(); 0079 } 0080 0081 PixelY ColumnsView::columnsHeight() const 0082 { 0083 Q_D(const ColumnsView); 0084 0085 return d->NoOfLines * d->LineHeight; 0086 } 0087 0088 PixelX ColumnsView::columnsWidth() const 0089 { 0090 Q_D(const ColumnsView); 0091 0092 return d->ColumnsWidth; 0093 } 0094 0095 QPoint ColumnsView::viewportToColumns(const QPoint& point) const 0096 { 0097 return QPoint(xOffset(), yOffset()) + point; 0098 } 0099 0100 PixelX ColumnsView::xOffset() const 0101 { 0102 return horizontalScrollBar()->value(); 0103 } 0104 0105 PixelY ColumnsView::yOffset() const 0106 { 0107 return verticalScrollBar()->value(); 0108 } 0109 0110 PixelY ColumnsView::yOffsetOfLine(Line lineIndex) const 0111 { 0112 Q_D(const ColumnsView); 0113 0114 return lineIndex * d->LineHeight - yOffset(); 0115 } 0116 0117 void ColumnsView::setColumnsPos(PixelX x, PixelY y) 0118 { 0119 horizontalScrollBar()->setValue(x); 0120 verticalScrollBar()->setValue(y); 0121 } 0122 0123 void ColumnsView::setNoOfLines(LineSize newNoOfLines) 0124 { 0125 Q_D(ColumnsView); 0126 0127 if (d->NoOfLines == newNoOfLines) { 0128 return; 0129 } 0130 0131 d->NoOfLines = newNoOfLines; 0132 0133 updateScrollBars(); 0134 } 0135 0136 void ColumnsView::setLineHeight(PixelY newLineHeight) 0137 { 0138 Q_D(ColumnsView); 0139 0140 if (newLineHeight == d->LineHeight) { 0141 return; 0142 } 0143 0144 d->LineHeight = newLineHeight; 0145 0146 for (auto column : qAsConst(d->columns)) { 0147 column->setLineHeight(d->LineHeight); 0148 } 0149 0150 verticalScrollBar()->setSingleStep(d->LineHeight); 0151 updateScrollBars(); 0152 } 0153 0154 void ColumnsView::updateWidths() 0155 { 0156 Q_D(ColumnsView); 0157 0158 d->updateWidths(); 0159 0160 updateScrollBars(); 0161 } 0162 0163 void ColumnsView::updateScrollBars() 0164 { 0165 QSize viewSize = maximumViewportSize(); 0166 0167 const int scrollBarWidth = style()->pixelMetric(QStyle::PM_ScrollBarExtent); 0168 const PixelY usedHeight = columnsHeight(); 0169 const PixelX usedWidth = columnsWidth(); 0170 0171 const bool needsVerticalBarDefinitely = (usedHeight > viewSize.height()); 0172 const bool needsHorizontalBarDefinitely = (usedWidth > viewSize.width()); 0173 0174 if (needsVerticalBarDefinitely) { 0175 viewSize.rwidth() -= scrollBarWidth; 0176 } 0177 if (needsHorizontalBarDefinitely) { 0178 viewSize.rheight() -= scrollBarWidth; 0179 } 0180 // check again if bars are not needed now 0181 if (!needsVerticalBarDefinitely && usedHeight > viewSize.height()) { 0182 viewSize.rwidth() -= scrollBarWidth; 0183 } 0184 if (!needsHorizontalBarDefinitely && usedWidth > viewSize.width()) { 0185 viewSize.rheight() -= scrollBarWidth; 0186 } 0187 0188 verticalScrollBar()->setRange(0, usedHeight - viewSize.height()); 0189 verticalScrollBar()->setPageStep(viewSize.height()); 0190 horizontalScrollBar()->setRange(0, usedWidth - viewSize.width()); 0191 horizontalScrollBar()->setPageStep(viewSize.width()); 0192 } 0193 0194 void ColumnsView::updateColumn(AbstractColumnRenderer& columnRenderer) 0195 { 0196 if (columnRenderer.isVisible()) { 0197 viewport()->update(columnRenderer.x() - xOffset(), 0, columnRenderer.width(), visibleHeight()); 0198 } 0199 } 0200 0201 void ColumnsView::updateColumn(AbstractColumnRenderer& columnRenderer, const LineRange& lines) 0202 { 0203 Q_D(ColumnsView); 0204 0205 if (columnRenderer.isVisible()) { // TODO: catch hidden range && columnRenderer.overlaps(Xs) ) 0206 LineRange linesToUpdate = visibleLines(); 0207 linesToUpdate.restrictTo(lines); 0208 if (linesToUpdate.isValid()) { 0209 const PixelX x = columnRenderer.x() - xOffset(); 0210 const PixelY y = yOffsetOfLine(linesToUpdate.start()); 0211 const int width = columnRenderer.width(); 0212 const int height = d->LineHeight * linesToUpdate.width(); 0213 viewport()->update(x, y, width, height); 0214 } 0215 } 0216 } 0217 0218 LineSize ColumnsView::noOfLinesPerPage() const 0219 { 0220 Q_D(const ColumnsView); 0221 0222 if (d->LineHeight < 1) { 0223 return 1; 0224 } 0225 0226 LineSize result = (visibleHeight() - 1) / d->LineHeight; // -1 ensures to get always the last visible line 0227 0228 if (result < 1) { 0229 // ensure to move down at least one line 0230 result = 1; 0231 } 0232 0233 return result; 0234 } 0235 0236 void ColumnsView::addColumn(AbstractColumnRenderer* columnRenderer) 0237 { 0238 Q_D(ColumnsView); 0239 0240 // if( Reversed ) 0241 // Columns.prepend( C ); 0242 // else 0243 d->columns.append(columnRenderer); 0244 0245 updateWidths(); 0246 } 0247 0248 void ColumnsView::removeColumn(AbstractColumnRenderer* columnRenderer) 0249 { 0250 Q_D(ColumnsView); 0251 0252 const int columnRendererIndex = d->columns.indexOf(columnRenderer); 0253 if (columnRendererIndex != -1) { 0254 d->columns.removeAt(columnRendererIndex); 0255 } 0256 0257 delete columnRenderer; 0258 0259 updateWidths(); 0260 } 0261 0262 void ColumnsView::scrollContentsBy(int dx, int dy) 0263 { 0264 viewport()->scroll(dx, dy); 0265 } 0266 0267 bool ColumnsView::event(QEvent* event) 0268 { 0269 if (event->type() == QEvent::StyleChange || event->type() == QEvent::LayoutRequest) { 0270 updateScrollBars(); 0271 } 0272 0273 return QAbstractScrollArea::event(event); 0274 } 0275 0276 void ColumnsView::resizeEvent(QResizeEvent* event) 0277 { 0278 updateScrollBars(); 0279 0280 QAbstractScrollArea::resizeEvent(event); 0281 } 0282 0283 void ColumnsView::paintEvent(QPaintEvent* paintEvent) 0284 { 0285 QAbstractScrollArea::paintEvent(paintEvent); 0286 0287 const PixelX x = xOffset(); 0288 const PixelY y = yOffset(); 0289 0290 QRect dirtyRect = paintEvent->rect(); 0291 dirtyRect.translate(x, y); 0292 0293 QPainter painter(viewport()); 0294 painter.translate(-x, -y); 0295 0296 renderColumns(&painter, dirtyRect.x(), dirtyRect.y(), dirtyRect.width(), dirtyRect.height()); 0297 } 0298 0299 void ColumnsView::renderColumns(QPainter* painter, int cx, int cy, int cw, int ch) 0300 { 0301 Q_D(ColumnsView); 0302 0303 PixelXRange dirtyXs = PixelXRange::fromWidth(cx, cw); 0304 0305 // content to be shown? 0306 if (dirtyXs.startsBefore(d->ColumnsWidth)) { 0307 PixelYRange dirtyYs = PixelYRange::fromWidth(cy, ch); 0308 0309 // collect affected columns 0310 QVector<AbstractColumnRenderer*> dirtyColumns; 0311 dirtyColumns.reserve(d->columns.size()); 0312 for (auto column : qAsConst(d->columns)) { 0313 if (column->isVisible() && column->overlaps(dirtyXs)) { 0314 dirtyColumns.append(column); 0315 } 0316 } 0317 0318 // any lines of any columns to be drawn? 0319 if (d->NoOfLines > 0) { 0320 // calculate affected lines 0321 LineRange dirtyLines = visibleLines(dirtyYs); 0322 dirtyLines.restrictEndTo(d->NoOfLines - 1); 0323 0324 if (dirtyLines.isValid()) { 0325 // paint full columns 0326 for (auto column : qAsConst(d->columns)) { 0327 column->renderColumn(painter, dirtyXs, dirtyYs); 0328 } 0329 0330 PixelY cy = dirtyLines.start() * d->LineHeight; 0331 // qCDebug(LOG_OKTETA_GUI)<<"Dirty lines: "<<dirtyLines.start()<<"-"<<dirtyLines.end(); 0332 // starting painting with the first line 0333 Line line = dirtyLines.start(); 0334 auto it = dirtyColumns.constBegin(); 0335 AbstractColumnRenderer* column = *it; 0336 painter->translate(column->x(), cy); 0337 0338 while (true) { 0339 column->renderFirstLine(painter, dirtyXs, line); 0340 ++it; 0341 if (it == dirtyColumns.constEnd()) { 0342 break; 0343 } 0344 painter->translate(column->width(), 0); 0345 column = *it; 0346 } 0347 painter->translate(-column->x(), 0); 0348 0349 // Go through the other lines 0350 while (true) { 0351 ++line; 0352 0353 if (line > dirtyLines.end()) { 0354 break; 0355 } 0356 0357 it = dirtyColumns.constBegin(); 0358 column = *it; 0359 painter->translate(column->x(), d->LineHeight); 0360 0361 while (true) { 0362 column->renderNextLine(painter); 0363 ++it; 0364 if (it == dirtyColumns.constEnd()) { 0365 break; 0366 } 0367 painter->translate(column->width(), 0); 0368 column = *it; 0369 } 0370 painter->translate(-column->x(), 0); 0371 } 0372 cy = dirtyLines.end() * d->LineHeight; 0373 0374 painter->translate(0, -cy); 0375 } 0376 } 0377 0378 // draw empty columns? 0379 dirtyYs.setStart(columnsHeight()); 0380 if (dirtyYs.isValid()) { 0381 for (auto column : qAsConst(dirtyColumns)) { 0382 column->renderEmptyColumn(painter, dirtyXs, dirtyYs); 0383 } 0384 } 0385 } 0386 0387 // painter empty rects 0388 dirtyXs.setStart(d->ColumnsWidth); 0389 if (dirtyXs.isValid()) { 0390 renderEmptyArea(painter, dirtyXs.start(), cy, dirtyXs.width(), ch); 0391 } 0392 } 0393 0394 void ColumnsView::renderEmptyArea(QPainter* painter, int cx, int cy, int cw, int ch) 0395 { 0396 painter->fillRect(cx, cy, cw, ch, viewport()->palette().brush(QPalette::Base)); // TODO: use stylist here, too 0397 } 0398 0399 } 0400 0401 #include "moc_columnsview.cpp"