1 /* This file is part of the KDE project
2  *
3  * Copyright (c) 2010-2011 C. Boemann <cbo@boemann.dk>
4  * Copyright (c) 2005-2006 Boudewijn Rempt <boud@valdyas.org>
5  * Copyright (c) 2006 Thomas Zander <zander@kde.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 #include "KoToolDocker.h"
23 
24 #include <KoDockWidgetTitleBarButton.h>
25 #include <KoDockWidgetTitleBar.h>
26 #include <KoIcon.h>
27 
28 #include <klocalizedstring.h>
29 #include <kconfiggroup.h>
30 #include <ksharedconfig.h>
31 
32 #include <QIcon>
33 #include <QApplication>
34 #include <QPointer>
35 #include <QGridLayout>
36 #include <QScrollArea>
37 #include <QScrollBar>
38 #include <QLabel>
39 #include <QSet>
40 #include <QAction>
41 #include <QStyleOptionFrame>
42 #include <QToolButton>
43 #include <QTabWidget>
44 
45 #include <WidgetsDebug.h>
46 
47 class Q_DECL_HIDDEN KoToolDocker::Private
48 {
49 public:
Private(KoToolDocker * dock)50     Private(KoToolDocker *dock)
51         : q(dock)
52         , tabbed(false)
53         , tabIcon(koIcon("tab-new"))
54         , unTabIcon(koIcon("tab-close"))
55     {
56     }
57 
58     QList<QPointer<QWidget> > currentWidgetList;
59     QSet<QWidget *> currentAuxWidgets;
60     QScrollArea *scrollArea;
61     QWidget *hiderWidget; // non current widgets are hidden by being children of this
62     QWidget *housekeeperWidget;
63     QGridLayout *housekeeperLayout;
64     KoToolDocker *q;
65     Qt::DockWidgetArea dockingArea;
66     bool tabbed;
67     QIcon tabIcon;
68     QIcon unTabIcon;
69     QToolButton *tabButton;
70 
71 
resetWidgets()72     void resetWidgets()
73     {
74         currentWidgetList.clear();
75         qDeleteAll(currentAuxWidgets);
76         currentAuxWidgets.clear();
77     }
78 
recreateLayout(const QList<QPointer<QWidget>> & optionWidgetList)79     void recreateLayout(const QList<QPointer<QWidget> > &optionWidgetList)
80     {
81         foreach(QPointer<QWidget> widget, currentWidgetList) {
82             if (!widget.isNull() && widget && hiderWidget) {
83                 widget->setParent(hiderWidget);
84             }
85         }
86         qDeleteAll(currentAuxWidgets);
87         currentAuxWidgets.clear();
88 
89         currentWidgetList = optionWidgetList;
90 
91         // need to unstretch row that have previously been stretched
92         housekeeperLayout->setRowStretch(housekeeperLayout->rowCount()-1, 0);
93 
94         if (tabbed && currentWidgetList.size() > 1) {
95             QTabWidget *t;
96             housekeeperLayout->addWidget(t = new QTabWidget(), 0, 0);
97             t->setDocumentMode(true);
98             currentAuxWidgets.insert(t);
99             foreach(QPointer<QWidget> widget, currentWidgetList) {
100                 if (widget.isNull() || widget->objectName().isEmpty()) {
101                     Q_ASSERT(!(widget->objectName().isEmpty()));
102                     continue; // skip this docker in release build when assert don't crash
103                 }
104                 t->addTab(widget, widget->windowTitle());
105             }
106         } else {
107             int cnt = 0;
108             QFrame *s;
109             QLabel *l;
110             switch(dockingArea) {
111             case Qt::TopDockWidgetArea:
112             case Qt::BottomDockWidgetArea:
113                 housekeeperLayout->setHorizontalSpacing(2);
114                 housekeeperLayout->setVerticalSpacing(0);
115                 foreach(QPointer<QWidget> widget, currentWidgetList) {
116                     if (widget.isNull() || widget->objectName().isEmpty()) {
117                         continue; // skip this docker in release build when assert don't crash
118                     }
119                     if (!widget->windowTitle().isEmpty()) {
120                         housekeeperLayout->addWidget(l = new QLabel(widget->windowTitle()), 0, 2*cnt);
121                         currentAuxWidgets.insert(l);
122                     }
123                     housekeeperLayout->addWidget(widget, 1, 2*cnt);
124                     widget->show();
125                     if (widget != currentWidgetList.last()) {
126                         housekeeperLayout->addWidget(s = new QFrame(), 0, 2*cnt+1, 2, 1);
127                         s->setFrameShape(QFrame::VLine);
128                         currentAuxWidgets.insert(s);
129                     }
130                     cnt++;
131                 }
132                 break;
133             case Qt::LeftDockWidgetArea:
134             case Qt::RightDockWidgetArea: {
135                 housekeeperLayout->setHorizontalSpacing(0);
136                 housekeeperLayout->setVerticalSpacing(2);
137                 int specialCount = 0;
138                 foreach(QPointer<QWidget> widget, currentWidgetList) {
139                     if (widget.isNull() || widget->objectName().isEmpty()) {
140                         Q_ASSERT(!(widget->objectName().isEmpty()));
141                         continue; // skip this docker in release build when assert don't crash
142                     }
143                     if (!widget->windowTitle().isEmpty()) {
144                         housekeeperLayout->addWidget(l = new QLabel(widget->windowTitle()), cnt++, 0);
145                         currentAuxWidgets.insert(l);
146                     }
147                     housekeeperLayout->addWidget(widget, cnt++, 0);
148                     QLayout *subLayout = widget->layout();
149                     if (subLayout) {
150                         for (int i = 0; i < subLayout->count(); ++i) {
151                             QWidget *spacerWidget = subLayout->itemAt(i)->widget();
152                             if (spacerWidget && spacerWidget->objectName().contains("SpecialSpacer")) {
153                                 specialCount++;
154                             }
155                         }
156                     }
157                     widget->show();
158                     if (widget != currentWidgetList.last()) {
159                         housekeeperLayout->addWidget(s = new QFrame(), cnt++, 0);
160                         s->setFrameShape(QFrame::HLine);
161                         currentAuxWidgets.insert(s);
162                     }
163                 }
164                 if (specialCount == currentWidgetList.count()) {
165                     housekeeperLayout->setRowStretch(cnt, 10000);
166                 }
167                 break;
168             }
169             default:
170                 break;
171             }
172         }
173         housekeeperLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
174         housekeeperLayout->invalidate();
175     }
176 
locationChanged(Qt::DockWidgetArea area)177     void locationChanged(Qt::DockWidgetArea area)
178     {
179         dockingArea = area;
180         recreateLayout(currentWidgetList);
181     }
182 
toggleTab()183     void toggleTab()
184     {
185         if (!tabbed) {
186             tabbed = true;
187             tabButton->setIcon(unTabIcon);
188         } else {
189             tabbed = false;
190             tabButton->setIcon(tabIcon);
191         }
192         recreateLayout(currentWidgetList);
193     }
194 };
195 
KoToolDocker(QWidget * parent)196 KoToolDocker::KoToolDocker(QWidget *parent)
197     : QDockWidget(i18n("Tool Options"), parent),
198       d(new Private(this))
199 {
200     KConfigGroup cfg =  KSharedConfig::openConfig()->group("DockWidget sharedtooldocker");
201     d->tabbed = cfg.readEntry("TabbedMode", false);
202 
203     toggleViewAction()->setVisible(false); //should always be visible, so hide option in menu
204     setFeatures(DockWidgetMovable|DockWidgetFloatable);
205     setTitleBarWidget(new KoDockWidgetTitleBar(this));
206 
207     connect(this, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(locationChanged(Qt::DockWidgetArea)));
208 
209     d->housekeeperWidget = new QWidget();
210     d->housekeeperLayout = new QGridLayout();
211     d->housekeeperLayout->setContentsMargins(4,4,4,0);
212     d->housekeeperWidget->setLayout(d->housekeeperLayout);
213 
214     d->housekeeperLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
215 
216     d->hiderWidget = new QWidget(d->housekeeperWidget);
217     d->hiderWidget->setVisible(false);
218 
219     d->scrollArea = new QScrollArea();
220     d->scrollArea->setWidget(d->housekeeperWidget);
221     d->scrollArea->setFrameShape(QFrame::NoFrame);
222     d->scrollArea->setWidgetResizable(true);
223     d->scrollArea->setFocusPolicy(Qt::NoFocus);
224 
225     setWidget(d->scrollArea);
226 
227     d->tabButton = new QToolButton(this); // parent hack in toggleLock to keep it clickable
228     d->tabButton->setIcon(d->tabIcon);
229     d->tabButton->setToolTip(i18n("Toggles organizing the options in tabs or not"));
230     d->tabButton->setAutoRaise(true);
231     connect(d->tabButton, SIGNAL(clicked()), SLOT(toggleTab()));
232     d->tabButton->resize(d->tabButton->sizeHint());
233 }
234 
~KoToolDocker()235 KoToolDocker::~KoToolDocker()
236 {
237     KConfigGroup cfg =  KSharedConfig::openConfig()->group("DockWidget sharedtooldocker");
238     cfg.writeEntry("TabbedMode", d->tabbed);
239     cfg.sync();
240 
241     delete d;
242 }
243 
hasOptionWidget()244 bool KoToolDocker::hasOptionWidget()
245 {
246     return !d->currentWidgetList.isEmpty();
247 }
248 
setTabEnabled(bool enabled)249 void KoToolDocker::setTabEnabled(bool enabled)
250 {
251     d->tabButton->setVisible(enabled);
252 }
253 
setOptionWidgets(const QList<QPointer<QWidget>> & optionWidgetList)254 void KoToolDocker::setOptionWidgets(const QList<QPointer<QWidget> > &optionWidgetList)
255 {
256     d->recreateLayout(optionWidgetList);
257 }
258 
resizeEvent(QResizeEvent *)259 void KoToolDocker::resizeEvent(QResizeEvent*)
260 {
261     int fw = isFloating() ? style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, nullptr, this) : 0;
262     d->tabButton->move(width() - d->tabButton->width() - d->scrollArea->verticalScrollBar()->sizeHint().width(), fw);
263 }
264 
resetWidgets()265 void KoToolDocker::resetWidgets()
266 {
267     d->resetWidgets();
268 }
269 
270 
setCanvas(KoCanvasBase * canvas)271 void KoToolDocker::setCanvas(KoCanvasBase *canvas)
272 {
273     setEnabled(canvas != 0);
274 }
275 
unsetCanvas()276 void KoToolDocker::unsetCanvas()
277 {
278     setEnabled(false);
279 }
280 
281 //have to include this because of Q_PRIVATE_SLOT
282 #include <moc_KoToolDocker.cpp>
283