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"