File indexing completed on 2024-04-21 05:53:02

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