File indexing completed on 2025-04-27 03:58:30
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2005-03-22 0007 * Description : a widget to manage sidebar in GUI. 0008 * 0009 * SPDX-FileCopyrightText: 2005-2006 by Joern Ahrens <joern dot ahrens at kdemail dot net> 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2008-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0012 * SPDX-FileCopyrightText: 2001-2003 by Joseph Wenninger <jowenn at kde dot org> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "sidebar_p.h" 0019 0020 namespace Digikam 0021 { 0022 0023 Sidebar::Sidebar(QWidget* const parent, SidebarSplitter* const sp, Qt::Edge side, bool minimizedDefault) 0024 : DMultiTabBar (side, parent), 0025 StateSavingObject(this), 0026 d (new Private) 0027 { 0028 d->splitter = sp; 0029 d->minimizedDefault = minimizedDefault; 0030 d->stack = new QStackedWidget(d->splitter); 0031 d->dragSwitchTimer = new QTimer(this); 0032 0033 connect(d->dragSwitchTimer, SIGNAL(timeout()), 0034 this, SLOT(slotDragSwitchTimer())); 0035 0036 d->splitter->d->sidebars << this; 0037 0038 setStyle(DMultiTabBar::ActiveIconText); 0039 } 0040 0041 Sidebar::~Sidebar() 0042 { 0043 saveState(); 0044 0045 if (d->splitter) 0046 { 0047 d->splitter->d->sidebars.removeAll(this); 0048 } 0049 0050 delete d; 0051 } 0052 0053 SidebarSplitter* Sidebar::splitter() const 0054 { 0055 return d->splitter; 0056 } 0057 0058 void Sidebar::doLoadState() 0059 { 0060 KConfigGroup group = getConfigGroup(); 0061 int tab = group.readEntry(entryName(d->optionActiveTabEntry), 0); 0062 bool minimized = group.readEntry(entryName(d->optionMinimizedEntry), d->minimizedDefault); 0063 d->restoreSize = group.readEntry(entryName(d->optionRestoreSizeEntry), -1); 0064 0065 // validate 0066 0067 if ((tab >= d->tabs) || (tab < 0)) 0068 { 0069 tab = 0; 0070 } 0071 0072 if (minimized) 0073 { 0074 d->activeTab = tab; 0075 setTab(d->activeTab, false); 0076 d->stack->setCurrentIndex(d->activeTab); 0077 shrink(); 0078 Q_EMIT signalChangedTab(d->stack->currentWidget()); 0079 0080 return; 0081 } 0082 0083 d->activeTab = -1; 0084 slotClicked(tab); 0085 } 0086 0087 void Sidebar::doSaveState() 0088 { 0089 KConfigGroup group = getConfigGroup(); 0090 group.writeEntry(entryName(d->optionActiveTabEntry), d->activeTab); 0091 group.writeEntry(entryName(d->optionMinimizedEntry), d->minimized); 0092 group.writeEntry(entryName(d->optionRestoreSizeEntry), d->minimized ? d->restoreSize : -1); 0093 } 0094 0095 void Sidebar::backup() 0096 { 0097 // backup preview state of sidebar view (shrink or not) 0098 0099 d->isMinimized = d->minimized; 0100 0101 // In all case, shrink sidebar view 0102 0103 shrink(); 0104 0105 DMultiTabBar::hide(); 0106 } 0107 0108 void Sidebar::backup(const QList<QWidget*>& thirdWidgetsToBackup, QList<int>* const sizes) 0109 { 0110 sizes->clear(); 0111 0112 Q_FOREACH (QWidget* const widget, thirdWidgetsToBackup) 0113 { 0114 *sizes << d->splitter->size(widget); 0115 } 0116 0117 backup(); 0118 } 0119 0120 void Sidebar::restore() 0121 { 0122 DMultiTabBar::show(); 0123 0124 // restore preview state of sidebar view, stored in backup() 0125 0126 if (!d->isMinimized) 0127 { 0128 expand(); 0129 } 0130 } 0131 0132 void Sidebar::restore(const QList<QWidget*>& thirdWidgetsToRestore, const QList<int>& sizes) 0133 { 0134 restore(); 0135 0136 if (thirdWidgetsToRestore.size() == sizes.size()) 0137 { 0138 for (int i = 0 ; i < thirdWidgetsToRestore.size() ; ++i) 0139 { 0140 d->splitter->setSize(thirdWidgetsToRestore.at(i), sizes.at(i)); 0141 } 0142 } 0143 } 0144 0145 void Sidebar::appendTab(QWidget* const w, const QIcon& pic, const QString& title) 0146 { 0147 // Store state (but not on initialization) 0148 0149 if (isVisible()) 0150 { 0151 d->appendedTabsStateCache[w] = SidebarState(d->stack->currentWidget(), d->splitter->size(this)); 0152 } 0153 0154 // Add tab 0155 0156 w->setParent(d->stack); 0157 DMultiTabBar::appendTab(pic, d->tabs, title); 0158 d->stack->insertWidget(d->tabs, w); 0159 0160 tab(d->tabs)->setAcceptDrops(true); 0161 tab(d->tabs)->installEventFilter(this); 0162 0163 connect(tab(d->tabs), SIGNAL(signalClicked(int)), 0164 this, SLOT(slotClicked(int))); 0165 0166 d->tabs++; 0167 } 0168 0169 void Sidebar::deleteTab(QWidget* const w) 0170 { 0171 int tab = d->stack->indexOf(w); 0172 0173 if (tab < 0) 0174 { 0175 return; 0176 } 0177 0178 bool removingActiveTab = (tab == d->activeTab); 0179 0180 if (removingActiveTab) 0181 { 0182 d->activeTab = -1; 0183 } 0184 0185 d->stack->removeWidget(d->stack->widget(tab)); 0186 0187 // delete widget 0188 0189 removeTab(tab); 0190 d->tabs--; 0191 0192 // restore or reset active tab and width 0193 0194 if (!d->minimized) 0195 { 0196 // restore to state before adding tab 0197 // using a hash is simple, but does not handle well multiple add/removal operations at a time 0198 0199 SidebarState state = d->appendedTabsStateCache.take(w); 0200 0201 if (state.activeWidget) 0202 { 0203 int atab = d->stack->indexOf(state.activeWidget); 0204 0205 if (atab != -1) 0206 { 0207 switchTabAndStackToTab(atab); 0208 Q_EMIT signalChangedTab(d->stack->currentWidget()); 0209 0210 if (state.size == 0) 0211 { 0212 d->minimized = true; 0213 setTab(d->activeTab, false); 0214 } 0215 0216 d->splitter->setSize(this, state.size); 0217 } 0218 } 0219 else 0220 { 0221 if (removingActiveTab) 0222 { 0223 slotClicked(d->tabs - 1); 0224 } 0225 0226 d->splitter->setSize(this, -1); 0227 } 0228 } 0229 else 0230 { 0231 d->restoreSize = -1; 0232 } 0233 } 0234 0235 void Sidebar::slotClicked(int tab) 0236 { 0237 if ((tab >= d->tabs) || (tab < 0)) 0238 { 0239 return; 0240 } 0241 0242 if (tab == d->activeTab) 0243 { 0244 d->stack->isHidden() ? expand() 0245 : shrink(); 0246 } 0247 else 0248 { 0249 switchTabAndStackToTab(tab); 0250 0251 if (d->minimized) 0252 { 0253 expand(); 0254 } 0255 0256 Q_EMIT signalChangedTab(d->stack->currentWidget()); 0257 } 0258 } 0259 0260 void Sidebar::setActiveTab(QWidget* const w) 0261 { 0262 int tab = d->stack->indexOf(w); 0263 0264 if (tab < 0) 0265 { 0266 return; 0267 } 0268 0269 switchTabAndStackToTab(tab); 0270 0271 if (d->minimized) 0272 { 0273 expand(); 0274 } 0275 0276 Q_EMIT signalChangedTab(d->stack->currentWidget()); 0277 } 0278 0279 void Sidebar::activePreviousTab() 0280 { 0281 int tab = d->stack->indexOf(d->stack->currentWidget()); 0282 0283 if (tab == 0) 0284 { 0285 tab = d->tabs-1; 0286 } 0287 else 0288 { 0289 tab--; 0290 } 0291 0292 setActiveTab(d->stack->widget(tab)); 0293 } 0294 0295 void Sidebar::activeNextTab() 0296 { 0297 int tab = d->stack->indexOf(d->stack->currentWidget()); 0298 0299 if (tab == d->tabs-1) 0300 { 0301 tab = 0; 0302 } 0303 else 0304 { 0305 tab++; 0306 } 0307 0308 setActiveTab(d->stack->widget(tab)); 0309 } 0310 0311 void Sidebar::switchTabAndStackToTab(int tab) 0312 { 0313 if (d->activeTab >= 0) 0314 { 0315 setTab(d->activeTab, false); 0316 } 0317 0318 d->activeTab = tab; 0319 setTab(d->activeTab, true); 0320 d->stack->setCurrentIndex(d->activeTab); 0321 } 0322 0323 QWidget* Sidebar::getActiveTab() const 0324 { 0325 if (d->splitter) 0326 { 0327 return d->stack->currentWidget(); 0328 } 0329 else 0330 { 0331 return nullptr; 0332 } 0333 } 0334 0335 void Sidebar::shrink() 0336 { 0337 d->minimized = true; 0338 0339 // store the size that we had. We may later need it when we restore to visible. 0340 0341 int currentSize = d->splitter->size(this); 0342 0343 if (currentSize) 0344 { 0345 d->restoreSize = currentSize; 0346 } 0347 0348 d->stack->hide(); 0349 Q_EMIT signalViewChanged(); 0350 } 0351 0352 void Sidebar::expand() 0353 { 0354 d->minimized = false; 0355 d->stack->show(); 0356 0357 QTimer::singleShot(0, this, SLOT(slotExpandTimer())); 0358 } 0359 0360 void Sidebar::slotExpandTimer() 0361 { 0362 // Do not expand to size 0 (only splitter handle visible) 0363 // but either to previous size, or the minimum size hint 0364 0365 if (d->splitter->size(this) == 0) 0366 { 0367 setTab(d->activeTab, true); 0368 d->splitter->setSize(this, d->restoreSize ? d->restoreSize : -1); 0369 } 0370 0371 Q_EMIT signalViewChanged(); 0372 } 0373 0374 bool Sidebar::isExpanded() const 0375 { 0376 return !d->minimized; 0377 } 0378 0379 bool Sidebar::eventFilter(QObject* obj, QEvent* ev) 0380 { 0381 for (int i = 0 ; i < d->tabs ; ++i) 0382 { 0383 if (obj == tab(i)) 0384 { 0385 if (ev->type() == QEvent::DragEnter) 0386 { 0387 0388 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0389 0390 QEnterEvent* const e = static_cast<QEnterEvent*>(ev); 0391 0392 #else 0393 0394 QDragEnterEvent* const e = static_cast<QDragEnterEvent*>(ev); 0395 0396 #endif 0397 0398 enterEvent(e); 0399 e->accept(); 0400 0401 return false; 0402 } 0403 else if (ev->type() == QEvent::DragMove) 0404 { 0405 if (!d->dragSwitchTimer->isActive()) 0406 { 0407 d->dragSwitchTimer->setSingleShot(true); 0408 d->dragSwitchTimer->start(800); 0409 d->dragSwitchId = i; 0410 } 0411 0412 return false; 0413 } 0414 else if (ev->type() == QEvent::DragLeave) 0415 { 0416 d->dragSwitchTimer->stop(); 0417 QDragLeaveEvent* const e = static_cast<QDragLeaveEvent*>(ev); 0418 leaveEvent(e); 0419 0420 return false; 0421 } 0422 else if (ev->type() == QEvent::Drop) 0423 { 0424 d->dragSwitchTimer->stop(); 0425 QDropEvent* const e = static_cast<QDropEvent*>(ev); 0426 leaveEvent(e); 0427 0428 return false; 0429 } 0430 else 0431 { 0432 return false; 0433 } 0434 } 0435 } 0436 0437 // Else, pass the event on to the parent class 0438 0439 return DMultiTabBar::eventFilter(obj, ev); 0440 } 0441 0442 void Sidebar::slotDragSwitchTimer() 0443 { 0444 slotClicked(d->dragSwitchId); 0445 } 0446 0447 void Sidebar::slotSplitterBtnClicked() 0448 { 0449 slotClicked(d->activeTab); 0450 } 0451 0452 } // namespace Digikam 0453 0454 #include "moc_sidebar.cpp"