Warning, file /office/calligra/libs/widgets/KoToolBoxLayout_p.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * Copyright (c) 2005-2009 Thomas Zander <zander@kde.org>
0003  * Copyright (c) 2009 Peter Simonsson <peter.simonsson@gmail.com>
0004  * Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Library General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Library General Public License
0017  * along with this library; see the file COPYING.LIB.  If not, write to
0018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020  */
0021 #ifndef _KO_TOOLBOX_LAYOUT_H_
0022 #define _KO_TOOLBOX_LAYOUT_H_
0023 
0024 #include <WidgetsDebug.h>
0025 #include <QLayout>
0026 #include <QMap>
0027 #include <QRect>
0028 #include <QAbstractButton>
0029 #include <QDesktopWidget>
0030 #include <QApplication>
0031 #include <QMouseEvent>
0032 
0033 class SectionLayout : public QLayout
0034 {
0035 Q_OBJECT
0036 public:
0037     explicit SectionLayout(QWidget *parent)
0038         : QLayout(parent), m_orientation(Qt::Vertical)
0039     {
0040     }
0041 
0042     ~SectionLayout() override
0043     {
0044         qDeleteAll( m_items );
0045         m_items.clear();
0046     }
0047 
0048     void addButton(QAbstractButton *button, int priority)
0049     {
0050         addChildWidget(button);
0051 
0052         m_priorities.insert(button, priority);
0053         int index = 1;
0054         foreach (QWidgetItem *item, m_items) {
0055             if (m_priorities.value(static_cast<QAbstractButton*>(item->widget())) > priority)
0056                 break;
0057             index++;
0058         }
0059         m_items.insert(index-1, new QWidgetItem(button));
0060     }
0061 
0062     QSize sizeHint() const override
0063     {
0064         Q_ASSERT(0);
0065         return QSize();
0066     }
0067 
0068     void addItem(QLayoutItem*) override { Q_ASSERT(0); }
0069 
0070     QLayoutItem* itemAt(int i) const override
0071     {
0072         if (m_items.count() <= i)
0073             return nullptr;
0074         return m_items.at(i);
0075     }
0076 
0077     QLayoutItem* takeAt(int i) override { return m_items.takeAt(i); }
0078 
0079     int count() const override { return m_items.count(); }
0080 
0081     void setGeometry (const QRect &rect) override
0082     {
0083         int x = 0;
0084         int y = 0;
0085         const QSize &size = buttonSize();
0086         if (m_orientation == Qt::Vertical) {
0087             foreach (QWidgetItem* w, m_items) {
0088                 if (w->isEmpty())
0089                     continue;
0090                 w->widget()->setGeometry(QRect(x, y, size.width(), size.height()));
0091                 x += size.width();
0092                 if (x + size.width() > rect.width()) {
0093                     x = 0;
0094                     y += size.height();
0095                 }
0096             }
0097         } else {
0098             foreach (QWidgetItem* w, m_items) {
0099                 if (w->isEmpty())
0100                     continue;
0101                 w->widget()->setGeometry(QRect(x, y, size.width(), size.height()));
0102                 y += size.height();
0103                 if (y + size.height() > rect.height()) {
0104                     x += size.width();
0105                     y = 0;
0106                 }
0107             }
0108         }
0109     }
0110 
0111     void setButtonSize(const QSize size)
0112     {
0113         m_buttonSize = size;
0114     }
0115 
0116     const QSize &buttonSize() const
0117     {
0118         return m_buttonSize;
0119     }
0120 
0121     void setOrientation (Qt::Orientation orientation)
0122     {
0123         m_orientation = orientation;
0124     }
0125 
0126 private:
0127     QSize m_buttonSize;
0128     QMap<QAbstractButton*, int> m_priorities;
0129     QList<QWidgetItem*> m_items;
0130     Qt::Orientation m_orientation;
0131 };
0132 
0133 class Section : public QWidget
0134 {
0135 Q_OBJECT
0136 public:
0137     enum SeparatorFlag {
0138         SeparatorTop = 0x0001,/* SeparatorBottom = 0x0002, SeparatorRight = 0x0004,*/ SeparatorLeft = 0x0008
0139     };
0140     Q_DECLARE_FLAGS(Separators, SeparatorFlag)
0141     explicit Section(QWidget *parent = 0)
0142         : QWidget(parent),
0143         m_layout(new SectionLayout(this))
0144     {
0145         setLayout(m_layout);
0146     }
0147 
0148     void addButton(QAbstractButton *button, int priority)
0149     {
0150         m_layout->addButton(button, priority);
0151     }
0152 
0153     void setName(const QString &name)
0154     {
0155         m_name = name;
0156     }
0157 
0158     QString name() const
0159     {
0160         return m_name;
0161     }
0162 
0163     void setButtonSize(const QSize& size)
0164     {
0165         m_layout->setButtonSize(size);
0166     }
0167 
0168     QSize iconSize() const
0169     {
0170         return m_layout->buttonSize();
0171     }
0172 
0173     int visibleButtonCount() const
0174     {
0175         int count = 0;
0176         for(int i = m_layout->count()-1; i >= 0; --i) {
0177             if (! static_cast<QWidgetItem*> (m_layout->itemAt(i))->isEmpty())
0178                 ++count;
0179         }
0180         return count;
0181     }
0182 
0183     void setSeparator(Separators separators)
0184     {
0185         m_separators = separators;
0186     }
0187 
0188     Separators separators() const
0189     {
0190         return m_separators;
0191     }
0192 
0193     void setOrientation (Qt::Orientation orientation)
0194     {
0195         m_layout->setOrientation(orientation);
0196     }
0197 
0198 private:
0199     SectionLayout *m_layout;
0200     QString m_name;
0201     Separators m_separators;
0202 };
0203 
0204 Q_DECLARE_OPERATORS_FOR_FLAGS(Section::Separators)
0205 
0206 class KoToolBoxLayout : public QLayout
0207 {
0208 Q_OBJECT
0209 public:
0210     explicit KoToolBoxLayout(QWidget *parent)
0211         : QLayout(parent)
0212         , m_orientation(Qt::Vertical)
0213     {
0214         setSpacing(6);
0215     }
0216 
0217     ~KoToolBoxLayout() override;
0218 
0219     QSize sizeHint() const override
0220     {
0221         // Prefer showing one row/column by default
0222         const QSize minSize = minimumSize();
0223         if (!minSize.isValid()) {
0224             return minSize;
0225         }
0226         if (m_orientation == Qt::Vertical) {
0227             return QSize(minSize.width(), minSize.height() + spacing());
0228         } else {
0229             return QSize(minSize.height() + spacing(), minSize.width());
0230         }
0231     }
0232 
0233     QSize minimumSize() const override
0234     {
0235         if (m_sections.isEmpty())
0236             return QSize();
0237         QSize oneIcon = static_cast<Section*> (m_sections[0]->widget())->iconSize();
0238         return oneIcon;
0239     }
0240 
0241     void addSection(Section *section)
0242     {
0243         addChildWidget(section);
0244 
0245         QList<QWidgetItem*>::iterator iterator = m_sections.begin();
0246         int defaults = 2; // skip the first two as they are the 'main' and 'dynamic' sections.
0247         while (iterator != m_sections.end()) {
0248             if (--defaults < 0 && static_cast<Section*> ((*iterator)->widget())->name() > section->name())
0249                 break;
0250             ++iterator;
0251         }
0252         m_sections.insert(iterator, new QWidgetItem(section));
0253     }
0254 
0255     void addItem(QLayoutItem*) override
0256     {
0257         Q_ASSERT(0); // don't let anything else be added. (code depends on this!)
0258     }
0259 
0260     QLayoutItem* itemAt(int i) const override
0261     {
0262         return m_sections.value(i);
0263     }
0264     QLayoutItem* takeAt(int i) override { return m_sections.takeAt(i); }
0265     int count() const override { return m_sections.count(); }
0266 
0267     void setGeometry (const QRect &rect) override
0268     {
0269         QLayout::setGeometry(rect);
0270         doLayout(rect.size(), true);
0271     }
0272 
0273     bool hasHeightForWidth() const override
0274     {
0275         return m_orientation == Qt::Vertical;
0276     }
0277 
0278     int heightForWidth(int width) const override
0279     {
0280         if (m_orientation == Qt::Vertical) {
0281             const int height = doLayout(QSize(width, 0), false);
0282             return height;
0283         } else {
0284             return -1;
0285         }
0286     }
0287 
0288     /**
0289      * For calculating the width from height by KoToolBoxScrollArea.
0290      * QWidget doesn't actually support trading width for height, so it needs to
0291      * be handled specifically.
0292      */
0293     int widthForHeight(int height) const
0294     {
0295         if (m_orientation == Qt::Horizontal) {
0296             const int width = doLayout(QSize(0, height), false);
0297             return width;
0298         } else {
0299             return -1;
0300         }
0301     }
0302 
0303     void setOrientation (Qt::Orientation orientation)
0304     {
0305         m_orientation = orientation;
0306         invalidate();
0307     }
0308 
0309 private:
0310     int doLayout(const QSize &size, bool applyGeometry) const
0311     {
0312         // nothing to do?
0313         if (m_sections.isEmpty()) {
0314             return 0;
0315         }
0316 
0317         // the names of the variables assume a vertical orientation,
0318         // but all calculations are done based on the real orientation
0319         const bool isVertical = m_orientation == Qt::Vertical;
0320 
0321         const QSize iconSize = static_cast<Section*> (m_sections.first()->widget())->iconSize();
0322 
0323         const int maxWidth = isVertical ? size.width() : size.height();
0324         // using min 1 as width to e.g. protect against div by 0 below
0325         const int iconWidth = qMax(1, isVertical ? iconSize.width() : iconSize.height());
0326         const int iconHeight = qMax(1, isVertical ? iconSize.height() : iconSize.width());
0327 
0328         const int maxColumns = qMax(1, (maxWidth / iconWidth));
0329 
0330         int x = 0;
0331         int y = 0;
0332         bool firstSection = true;
0333         if (!applyGeometry) {
0334             foreach (QWidgetItem *wi, m_sections) {
0335                 Section *section = static_cast<Section*> (wi->widget());
0336                 const int buttonCount = section->visibleButtonCount();
0337                 if (buttonCount == 0) {
0338                     continue;
0339                 }
0340 
0341                 // rows needed for the buttons (calculation gets the ceiling value of the plain div)
0342                 const int neededRowCount = ((buttonCount-1) / maxColumns) + 1;
0343 
0344                 if (firstSection) {
0345                     firstSection = false;
0346                 } else {
0347                     // start on a new row, set separator
0348                     x = 0;
0349                     y += iconHeight + spacing();
0350                 }
0351 
0352                 // advance by the icons in the last row
0353                 const int lastRowColumnCount = buttonCount - ((neededRowCount-1) * maxColumns);
0354                 x += (lastRowColumnCount * iconWidth) + spacing();
0355                 // advance by all but the last used row
0356                 y += (neededRowCount - 1) * iconHeight;
0357             }
0358         } else {
0359             foreach (QWidgetItem *wi, m_sections) {
0360                 Section *section = static_cast<Section*> (wi->widget());
0361                 const int buttonCount = section->visibleButtonCount();
0362                 if (buttonCount == 0) {
0363                     section->hide();
0364                     continue;
0365                 }
0366 
0367                 // rows needed for the buttons (calculation gets the ceiling value of the plain div)
0368                 const int neededRowCount = ((buttonCount-1) / maxColumns) + 1;
0369 
0370                 if (firstSection) {
0371                     firstSection = false;
0372                 } else {
0373                     // start on a new row, set separator
0374                     x = 0;
0375                     y += iconHeight + spacing();
0376                     const Section::Separators separator =
0377                         isVertical ? Section::SeparatorTop : Section::SeparatorLeft;
0378                     section->setSeparator( separator );
0379                 }
0380 
0381                 const int usedColumns = qMin(buttonCount, maxColumns);
0382                 if (isVertical) {
0383                     section->setGeometry(x, y,
0384                                          usedColumns * iconWidth, neededRowCount * iconHeight);
0385                 } else {
0386                     section->setGeometry(y, x,
0387                                          neededRowCount * iconHeight, usedColumns * iconWidth);
0388                 }
0389 
0390                 // advance by the icons in the last row
0391                 const int lastRowColumnCount = buttonCount - ((neededRowCount-1) * maxColumns);
0392                 x += (lastRowColumnCount * iconWidth) + spacing();
0393                 // advance by all but the last used row
0394                 y += (neededRowCount - 1) * iconHeight;
0395             }
0396         }
0397 
0398         return y + iconHeight;
0399     }
0400 
0401     QList <QWidgetItem*> m_sections;
0402     Qt::Orientation m_orientation;
0403 };
0404 
0405 #endif