File indexing completed on 2024-04-28 05:45:53

0001 /*
0002     SPDX-FileCopyrightText: 2008-2010 Volker Lanz <vl@fidra.de>
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
0004     SPDX-FileCopyrightText: 2014-2017 Andrius Štikonas <andrius@stikonas.eu
0005     SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-3.0-or-later
0008 */
0009 
0010 #include "gui/partwidgetbase.h"
0011 #include "gui/partwidget.h"
0012 
0013 #include "core/partition.h"
0014 
0015 #include <cmath>
0016 
0017 const qint32 PartWidgetBase::m_Spacing = 2;
0018 const qint32 PartWidgetBase::m_BorderWidth = 3;
0019 const qint32 PartWidgetBase::m_BorderHeight = 3;
0020 const qint32 PartWidgetBase::m_MinWidth = 30;
0021 
0022 template<typename T>
0023 T sum(const QList<T>& list)
0024 {
0025     T rval = 0;
0026     for (const T & val : list)
0027         rval += val;
0028     return rval;
0029 }
0030 
0031 bool distributeLostPixels(QList<qint32>& childrenWidth, qint32 lostPixels)
0032 {
0033     if (lostPixels == 0 || childrenWidth.size() == 0)
0034         return false;
0035 
0036     while (lostPixels > 0)
0037         for (qint32 i = 0; i < childrenWidth.size() && lostPixels > 0; i++) {
0038             childrenWidth[i]++;
0039             lostPixels--;
0040         }
0041 
0042     return true;
0043 }
0044 
0045 bool levelChildrenWidths(QList<qint32>& childrenWidth, const QList<qint32>& minChildrenWidth, const qint32 destWidgetWidth)
0046 {
0047     if (childrenWidth.size() == 0)
0048         return false;
0049 
0050     distributeLostPixels(childrenWidth, destWidgetWidth - sum(childrenWidth));
0051 
0052     // if we find out a partition is too narrow, adjust its screen
0053     // width to its minimum width and increase adjust by how much we had to increase the
0054     // screen width. thus, in the end, we have the number of pixels we need
0055     // to find somewhere else in adjust.
0056     qint32 adjust = 0;
0057     for (qint32 i = 0; i < childrenWidth.size(); i++)
0058         if (childrenWidth[i] < minChildrenWidth[i]) {
0059             adjust += minChildrenWidth[i] - childrenWidth[i];
0060             childrenWidth[i] = minChildrenWidth[i];
0061         }
0062 
0063     // find out how many partitions are wide enough to have their width reduced; we'd love to
0064     // check for w > minWidth - (pixels_to_reduce_by), but that last value _depends_ on the
0065     // number we're trying to find here...
0066     qint32 numReducable = 0;
0067     for (qint32 i = 0; i < childrenWidth.size(); i++)
0068         if (childrenWidth[i] > minChildrenWidth[i])
0069             numReducable++;
0070 
0071     // no need to do anything... or nothing can be done because all are too narrow
0072     if (adjust == 0 || numReducable == 0)
0073         return false;
0074 
0075     // if we have adjusted one or more partitions (and not ALL of them, because in that
0076     // case, nothing will help us), go through the partitions again and reduce the
0077     // on screen widths of those big enough anyway
0078     const qint32 reduce = static_cast<qint32>(std::ceil(1.0 * adjust / numReducable));
0079     for (qint32 i = 0; i < childrenWidth.size(); i++)
0080         if (childrenWidth[i] > minChildrenWidth[i])
0081             childrenWidth[i] -= reduce;
0082 
0083     // distribute pixels lost due to rounding errors
0084     distributeLostPixels(childrenWidth, destWidgetWidth - sum(childrenWidth));
0085 
0086     return true;
0087 }
0088 
0089 void PartWidgetBase::positionChildren(const QWidget* destWidget, const PartitionNode::Partitions& partitions, QList<PartWidget*> widgets) const
0090 {
0091     if (partitions.size() == 0)
0092         return;
0093 
0094     QList<qint32> childrenWidth;
0095     QList<qint32> minChildrenWidth;
0096     const qint32 destWidgetWidth = destWidget->width() - 2 * borderWidth() - (partitions.size() - 1) * spacing();
0097 
0098     if (destWidgetWidth < 0)
0099         return;
0100 
0101     qint64 totalLength = 0;
0102     for (const auto &p : partitions)
0103         totalLength += p->length();
0104 
0105     if (totalLength < 1)
0106         return;
0107 
0108     // calculate unleveled width for each child and store it
0109     for (const auto &p : partitions) {
0110         childrenWidth.append(static_cast<qint32>(p->length() * destWidgetWidth / totalLength));
0111 
0112         // Calculate the minimum width for the widget. This is easy for primary and logical partitions: they
0113         // just have a fixed min width (configured in m_MinWidth). But for extended partitions things
0114         // are not quite as simple. We need to calc the sum of the min widths for each child, taking
0115         // spacing and borders into account, and add our own min width.
0116         qint32 min = (minWidth() + 2 * borderWidth() + spacing()) * p->children().size() - spacing() + 2 * borderWidth();
0117 
0118         // if it's too small, this partition is a primary or logical so just use the configured value
0119         if (min < minWidth())
0120             min = minWidth();
0121         minChildrenWidth.append(min);
0122     }
0123 
0124     // now go level the widths as long as required
0125     while (levelChildrenWidths(childrenWidth, minChildrenWidth, destWidgetWidth))
0126         ;
0127 
0128     // move the children to their positions and resize them
0129     for (int i = 0, x = borderWidth(); i < widgets.size(); i++) {
0130         widgets[i]->setMinimumWidth(minChildrenWidth[i]);
0131         widgets[i]->move(x, borderHeight());
0132         widgets[i]->resize(childrenWidth[i], destWidget->height() - 2 * borderHeight());
0133         x += childrenWidth[i] + spacing();
0134     }
0135 }
0136 
0137 const QList<PartWidget*> PartWidgetBase::childWidgets() const
0138 {
0139     QList<PartWidget*> rval;
0140 
0141     for (auto &o : children())
0142         if (PartWidget* w = qobject_cast<PartWidget*>(o))
0143             rval.append(w);
0144 
0145     return rval;
0146 }