1 /* This file is part of the KDE project
2  * Copyright (C) 2007 Thomas Zander <zander@kde.org>
3  * Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "KoPositionSelector.h"
22 
23 #include <QRadioButton>
24 #include <QGridLayout>
25 #include <QButtonGroup>
26 #include <QPainter>
27 #include <WidgetsDebug.h>
28 #include <QStyleOption>
29 
30 #define GAP 0
31 
32 class Q_DECL_HIDDEN KoPositionSelector::Private
33 {
34 public:
Private()35     Private()
36         : position(KoFlake::TopLeftCorner)
37     {
38         topLeft = createButton(KoFlake::TopLeftCorner);
39         topLeft->setChecked(true);
40         topRight = createButton(KoFlake::TopRightCorner);
41         center = createButton(KoFlake::CenteredPosition);
42         bottomRight = createButton(KoFlake::BottomRightCorner);
43         bottomLeft = createButton(KoFlake::BottomLeftCorner);
44     }
45 
createButton(int id)46     QRadioButton *createButton(int id) {
47         QRadioButton *b = new QRadioButton();
48         buttonGroup.addButton(b, id);
49         return b;
50     }
51 
52     QRadioButton *topLeft, *topRight, *center, *bottomRight, *bottomLeft;
53     QButtonGroup buttonGroup;
54     KoFlake::Position position;
55 };
56 
57 class RadioLayout : public QLayout {
58 Q_OBJECT
59 public:
RadioLayout(QWidget * parent)60     RadioLayout(QWidget *parent)
61         : QLayout(parent)
62     {
63     }
64 
~RadioLayout()65     ~RadioLayout() override
66     {
67         foreach( const Item & item, items )
68             delete item.child;
69         items.clear();
70     }
71 
setGeometry(const QRect & geom)72     void setGeometry (const QRect &geom) override {
73         QSize prefSize = calcSizes();
74 
75         qreal columnWidth, rowHeight;
76         if (geom.width() <= minimum.width())
77             columnWidth = geom.width() / (qreal) maxCol;
78         else
79             columnWidth = prefSize.width() + GAP;
80         if (geom.height() <= minimum.height())
81             rowHeight = geom.height() / (qreal) maxRow;
82         else
83             rowHeight = prefSize.height() + GAP;
84         // padding inside row and column so that radio button is centered
85         QPoint padding( qRound(0.5 * (columnWidth - prefSize.width())), qRound(0.5 * (rowHeight - prefSize.height())));
86         // offset so that all the radio button are centered within the widget
87         qreal offsetX = 0.5 * (geom.width()- static_cast<qreal>(maxCol) * columnWidth);
88         qreal offsetY = 0.5 * (geom.height() - static_cast<qreal>(maxRow) * rowHeight);
89         QPoint offset( qRound(offsetX), qRound(offsetY));
90         foreach(const Item & item, items) {
91             QPoint point( qRound(item.column * columnWidth), qRound(item.row * rowHeight) );
92             QRect rect(point + offset + padding + geom.topLeft(), prefSize);
93             item.child->setGeometry(rect);
94         }
95     }
96 
calcSizes()97     QSize calcSizes() {
98         QSize prefSize;
99         maxRow = 0;
100         maxCol = 0;
101         foreach(const Item & item, items) {
102             if(prefSize.isEmpty()) {
103                 QAbstractButton *but = dynamic_cast<QAbstractButton*> (item.child->widget());
104                 Q_ASSERT(but);
105                 QStyleOptionButton opt;
106                 opt.initFrom(but);
107                 prefSize = QSize(but->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &opt, but),
108                         but->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight, &opt, but));
109             }
110             maxRow = qMax(maxRow, item.row);
111             maxCol = qMax(maxCol, item.column);
112         }
113         maxCol++; maxRow++; // due to being zero-based.
114         preferred = QSize(maxCol * prefSize.width() + (maxCol-1) * GAP, maxRow * prefSize.height() + (maxRow-1) * GAP);
115         minimum = QSize(maxCol * prefSize.width(), maxRow * prefSize.height());
116         return prefSize;
117     }
118 
itemAt(int index) const119     QLayoutItem *itemAt (int index) const override {
120         if( index < count() )
121             return items.at(index).child;
122         else
123             return 0;
124     }
125 
takeAt(int index)126     QLayoutItem *takeAt (int index) override {
127         Q_ASSERT(index < count());
128         Item item = items.takeAt(index);
129         return item.child;
130     }
131 
count() const132     int count () const override {
133         return items.count();
134     }
135 
addItem(QLayoutItem *)136     void addItem(QLayoutItem *) override {
137         Q_ASSERT(0);
138     }
139 
sizeHint() const140     QSize sizeHint() const override {
141         if(preferred.isEmpty())
142             const_cast<RadioLayout*> (this)->calcSizes();
143         return preferred;
144     }
145 
minimumSize() const146     QSize minimumSize() const override {
147         if(minimum.isEmpty())
148             const_cast<RadioLayout*> (this)->calcSizes();
149         return minimum;
150     }
151 
addWidget(QRadioButton * widget,int row,int column)152     void addWidget(QRadioButton *widget, int row, int column) {
153         addChildWidget(widget);
154         Item newItem;
155         newItem.child = new QWidgetItem(widget);
156         newItem.row = row;
157         newItem.column = column;
158         items.append(newItem);
159     }
160 
161 private:
162     struct Item {
163         QLayoutItem *child;
164         int column;
165         int row;
166     };
167     QList<Item> items;
168     QSize preferred, minimum;
169     int maxCol, maxRow;
170 };
171 
KoPositionSelector(QWidget * parent)172 KoPositionSelector::KoPositionSelector(QWidget *parent)
173     : QWidget(parent),
174     d(new Private())
175 {
176     setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
177     RadioLayout *lay = new RadioLayout(this);
178     lay->addWidget(d->topLeft, 0, 0);
179     lay->addWidget(d->topRight, 0, 2);
180     lay->addWidget(d->center, 1, 1);
181     lay->addWidget(d->bottomRight, 2, 2);
182     lay->addWidget(d->bottomLeft, 2, 0);
183     setLayout(lay);
184 
185     connect(&d->buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(positionChanged(int)));
186 }
187 
~KoPositionSelector()188 KoPositionSelector::~KoPositionSelector() {
189     delete d;
190 }
191 
position() const192 KoFlake::Position KoPositionSelector::position() const {
193     return d->position;
194 }
195 
setPosition(KoFlake::Position position)196 void KoPositionSelector::setPosition(KoFlake::Position position) {
197     d->position = position;
198     switch(d->position) {
199         case KoFlake::TopLeftCorner:
200             d->topLeft->setChecked(true);
201             break;
202         case KoFlake::TopRightCorner:
203             d->topRight->setChecked(true);
204             break;
205         case KoFlake::CenteredPosition:
206             d->center->setChecked(true);
207             break;
208         case KoFlake::BottomLeftCorner:
209             d->bottomLeft->setChecked(true);
210             break;
211         case KoFlake::BottomRightCorner:
212             d->bottomRight->setChecked(true);
213             break;
214     }
215 }
216 
positionChanged(int position)217 void KoPositionSelector::positionChanged(int position) {
218     d->position = static_cast<KoFlake::Position> (position);
219     emit positionSelected(d->position);
220 }
221 
paintEvent(QPaintEvent *)222 void KoPositionSelector::paintEvent (QPaintEvent *) {
223     QPainter painter( this );
224     QPen pen(Qt::black);
225     int width;
226     if (d->topLeft->width() %2 == 0)
227         width = 2;
228     else
229         width = 3;
230     pen.setWidth(width);
231     painter.setPen(pen);
232     painter.drawRect( QRect( d->topLeft->geometry().center(), d->bottomRight->geometry().center() ) );
233     painter.end();
234 }
235 #include "KoPositionSelector.moc"
236