File indexing completed on 2024-05-26 05:56:35

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2003, 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 "abstractcolumnframerenderer.hpp"
0010 
0011 // Okteta gui
0012 #include <Okteta/LineRange>
0013 #include <Okteta/AbstractColumnRenderer>
0014 // Qt
0015 #include <QPainter>
0016 #include <QVector>
0017 // Std
0018 #include <utility>
0019 
0020 class AbstractColumnFrameRendererPrivate
0021 {
0022 public:
0023     AbstractColumnFrameRendererPrivate();
0024     ~AbstractColumnFrameRendererPrivate();
0025 
0026 public:
0027     void updateWidths();
0028 
0029 public: // calculated
0030     /** collection of all the columns. All columns will be autodeleted. */
0031     QVector<Okteta::AbstractColumnRenderer*> mColumns;
0032     /** the number of lines which the columnRenderer view has */
0033     Okteta::LineSize mNoOfLines = 0;
0034     /** the height of each line in pixels */
0035     Okteta::PixelY mLineHeight = 1;
0036     /** the width of all visible columns together */
0037     Okteta::PixelX mColumnsWidth = 0;
0038 };
0039 
0040 AbstractColumnFrameRendererPrivate::AbstractColumnFrameRendererPrivate() = default;
0041 
0042 AbstractColumnFrameRendererPrivate::~AbstractColumnFrameRendererPrivate()
0043 {
0044     qDeleteAll(mColumns);
0045 }
0046 
0047 void AbstractColumnFrameRendererPrivate::updateWidths()
0048 {
0049     mColumnsWidth = 0;
0050     for (auto* columnRenderer : std::as_const(mColumns)) {
0051         columnRenderer->setX(mColumnsWidth);
0052         mColumnsWidth += columnRenderer->visibleWidth();
0053     }
0054 }
0055 
0056 AbstractColumnFrameRenderer::AbstractColumnFrameRenderer()
0057     : d(new AbstractColumnFrameRendererPrivate())
0058 {
0059 }
0060 
0061 AbstractColumnFrameRenderer::~AbstractColumnFrameRenderer() = default;
0062 
0063 Okteta::LineSize AbstractColumnFrameRenderer::noOfLines()          const { return d->mNoOfLines; }
0064 Okteta::PixelY AbstractColumnFrameRenderer::lineHeight()     const { return d->mLineHeight; }
0065 
0066 Okteta::PixelY AbstractColumnFrameRenderer::columnsHeight() const { return d->mNoOfLines * d->mLineHeight; }
0067 Okteta::PixelX AbstractColumnFrameRenderer::columnsWidth()  const { return d->mColumnsWidth; }
0068 
0069 void AbstractColumnFrameRenderer::setNoOfLines(Okteta::LineSize noOfLines)
0070 {
0071     if (d->mNoOfLines == noOfLines) {
0072         return;
0073     }
0074 
0075     d->mNoOfLines = noOfLines;
0076 }
0077 
0078 void AbstractColumnFrameRenderer::setLineHeight(Okteta::PixelY newLineHeight)
0079 {
0080     if (newLineHeight == d->mLineHeight) {
0081         return;
0082     }
0083 
0084     if (newLineHeight < 1) {
0085         newLineHeight = 1;
0086     }
0087     d->mLineHeight = newLineHeight;
0088 
0089     for (auto* columnRenderer : std::as_const(d->mColumns)) {
0090         columnRenderer->setLineHeight(d->mLineHeight);
0091     }
0092 }
0093 
0094 void AbstractColumnFrameRenderer::updateWidths()
0095 {
0096     d->updateWidths();
0097 }
0098 
0099 Okteta::LineSize AbstractColumnFrameRenderer::noOfLinesPerFrame() const
0100 {
0101     // TODO: the right reaction?
0102     if (d->mLineHeight < 1) {
0103         return 1;
0104     }
0105 
0106     Okteta::LineSize noOfLinesPerFrame = height() / d->mLineHeight;
0107 
0108     if (noOfLinesPerFrame < 1) {
0109         // ensure at least one line, so there aren't endless frames TODO: think about
0110         noOfLinesPerFrame = 1;
0111     }
0112 
0113     return noOfLinesPerFrame;
0114 }
0115 
0116 void AbstractColumnFrameRenderer::addColumn(Okteta::AbstractColumnRenderer* columnRenderer)
0117 {
0118     d->mColumns.append(columnRenderer);
0119 
0120     updateWidths();
0121 }
0122 
0123 void AbstractColumnFrameRenderer::removeColumn(Okteta::AbstractColumnRenderer* columnRenderer)
0124 {
0125     const int columnPos = d->mColumns.indexOf(columnRenderer);
0126     if (columnPos != -1) {
0127         d->mColumns.removeAt(columnPos);
0128     }
0129 
0130     delete columnRenderer;
0131 
0132     updateWidths();
0133 }
0134 
0135 void AbstractColumnFrameRenderer::renderFrame(QPainter* painter, int frameIndex)
0136 {
0137     Okteta::PixelXRange renderedXs = Okteta::PixelXRange::fromWidth(0, width());
0138 
0139     // content to be shown?
0140     if (renderedXs.startsBefore(d->mColumnsWidth)) {
0141         // collect affected columns
0142         QVector<Okteta::AbstractColumnRenderer*> columnRenderers;
0143         columnRenderers.reserve(d->mColumns.size());
0144         for (auto* columnRenderer : std::as_const(d->mColumns)) {
0145             if (columnRenderer->isVisible() && columnRenderer->overlaps(renderedXs)) {
0146                 columnRenderers.append(columnRenderer);
0147             }
0148         }
0149 
0150         // calculate affected lines
0151         const Okteta::Line baseLine = frameIndex * noOfLinesPerFrame();
0152         Okteta::LineRange renderedLines = Okteta::LineRange::fromWidth(baseLine, noOfLinesPerFrame());
0153         renderedLines.restrictEndTo(noOfLines() - 1);
0154 
0155         Okteta::PixelYRange renderedYs = Okteta::PixelYRange::fromWidth(0, renderedLines.width() * d->mLineHeight);
0156 
0157         // any lines of any columns to be drawn?
0158         if (renderedLines.isValid()) {
0159             // paint full columns
0160             for (auto* columnRenderer : std::as_const(columnRenderers)) {
0161                 columnRenderer->renderColumn(painter, renderedXs, renderedYs);
0162             }
0163 
0164             Okteta::PixelY cy = 0;
0165             // starting painting with the first line
0166             Okteta::Line line = renderedLines.start();
0167             auto it = columnRenderers.constBegin();
0168             Okteta::AbstractColumnRenderer* columnRenderer = *it;
0169             painter->translate(columnRenderer->x(), cy);
0170 
0171             while (true) {
0172                 columnRenderer->renderFirstLine(painter, renderedXs, line);
0173                 ++it;
0174                 if (it == columnRenderers.constEnd()) {
0175                     break;
0176                 }
0177                 painter->translate(columnRenderer->width(), 0);
0178                 columnRenderer = *it;
0179             }
0180             painter->translate(-columnRenderer->x(), 0);
0181 
0182             // Go through the other lines
0183             while (true) {
0184                 ++line;
0185 
0186                 if (line > renderedLines.end()) {
0187                     break;
0188                 }
0189 
0190                 auto it = columnRenderers.constBegin();
0191                 columnRenderer = *it;
0192                 painter->translate(columnRenderer->x(), d->mLineHeight);
0193 
0194                 while (true) {
0195                     columnRenderer->renderNextLine(painter);
0196                     ++it;
0197                     if (it == columnRenderers.constEnd()) {
0198                         break;
0199                     }
0200                     painter->translate(columnRenderer->width(), 0);
0201                     columnRenderer = *it;
0202                 }
0203                 painter->translate(-columnRenderer->x(), 0);
0204             }
0205             cy = (renderedLines.width() - 1) * d->mLineHeight;
0206 
0207             painter->translate(0, -cy);
0208         }
0209 
0210         // draw empty columns?
0211         renderedYs.set(renderedYs.nextBehindEnd(), height() - 1);
0212         if (renderedYs.isValid()) {
0213             for (auto* columnRenderer : std::as_const(columnRenderers)) {
0214                 columnRenderer->renderEmptyColumn(painter, renderedXs, renderedYs);
0215             }
0216         }
0217     }
0218 
0219     // painter empty rects
0220     renderedXs.setStart(d->mColumnsWidth);
0221     if (renderedXs.isValid()) {
0222         drawEmptyArea(painter, renderedXs.start(), 0, renderedXs.width(), height());
0223     }
0224 }
0225 
0226 void AbstractColumnFrameRenderer::drawEmptyArea(QPainter* painter, int cx, int cy, int cw, int ch)
0227 {
0228     painter->fillRect(cx, cy, cw, ch, Qt::white);
0229 }