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"