File indexing completed on 2024-05-05 04:47:27

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Nikolaj Hald Nielsen <nhn@kde.org>                                *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #define DEBUG_PREFIX "BrowserBreadcrumbWidget"
0018 
0019 #include "BrowserBreadcrumbWidget.h"
0020 
0021 #include "amarokurls/AmarokUrl.h"
0022 #include "BrowserBreadcrumbItem.h"
0023 #include "BrowserCategoryList.h"
0024 #include "browsers/filebrowser/FileBrowser.h"
0025 #include "MainWindow.h"
0026 #include "widgets/BreadcrumbItemButton.h"
0027 
0028 #include <QBoxLayout>
0029 #include <QDir>
0030 #include <QMenu>
0031 #include <QResizeEvent>
0032 #include <QTimer>
0033 
0034 #include <KLocalizedString>
0035 
0036 
0037 BrowserBreadcrumbWidget::BrowserBreadcrumbWidget( QWidget * parent )
0038     : BoxWidget( false, parent)
0039     , m_rootList( nullptr )
0040 {
0041     setFixedHeight( 28 );
0042     setContentsMargins( 3, 0, 3, 0 );
0043 
0044     m_breadcrumbArea = new BoxWidget( false, this );
0045     m_breadcrumbArea->setContentsMargins( 0, 0, 0, 0 );
0046     layout()->setStretchFactor( m_breadcrumbArea, 10 );
0047 
0048     new BreadcrumbUrlMenuButton( QStringLiteral("navigate"), this );
0049 }
0050 
0051 BrowserBreadcrumbWidget::~BrowserBreadcrumbWidget()
0052 {
0053 }
0054 
0055 void
0056 BrowserBreadcrumbWidget::clearCrumbs()
0057 {
0058     const QList<QWidget *> childCrumbs = m_breadcrumbArea->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly);
0059     for( auto item : childCrumbs)
0060     {
0061         item->deleteLater();
0062     }
0063 }
0064 
0065 void
0066 BrowserBreadcrumbWidget::setRootList( BrowserCategoryList * rootList )
0067 {
0068     m_rootList = rootList;
0069 
0070     //update the breadcrumbs every time the view changes.
0071     connect( m_rootList, &BrowserCategoryList::viewChanged, this, &BrowserBreadcrumbWidget::updateBreadcrumbs );
0072 
0073     updateBreadcrumbs();
0074 }
0075 
0076 void
0077 BrowserBreadcrumbWidget::updateBreadcrumbs()
0078 {
0079     if( !m_rootList )
0080         return;
0081 
0082     clearCrumbs();
0083 
0084     addLevel( m_rootList );
0085 
0086     // spacer is the right-most widget
0087     new QWidget(m_breadcrumbArea);
0088 
0089     showAsNeeded();
0090 }
0091 
0092 void
0093 BrowserBreadcrumbWidget::addLevel( BrowserCategoryList *list )
0094 {
0095     BrowserBreadcrumbItem *item = list->breadcrumb();
0096     addBreadCrumbItem( item );
0097 
0098     BrowserCategory *childCategory = list->activeCategory();
0099 
0100     if( childCategory )
0101     {
0102         item->setActive( false );
0103 
0104         //check if this is also a list
0105         BrowserCategoryList *childList = qobject_cast<BrowserCategoryList*>( childCategory );
0106         if( childList )
0107         {
0108             addLevel( childList );
0109         }
0110         else
0111         {
0112             BrowserBreadcrumbItem *leaf = childCategory->breadcrumb();
0113             addBreadCrumbItem( leaf );
0114 
0115             const QList<BrowserBreadcrumbItem*> additionalItems = childCategory->additionalItems();
0116             //no children, but check if there are additional breadcrumb levels (for internal navigation in the category) that should be added anyway.
0117             for( BrowserBreadcrumbItem *addItem : additionalItems )
0118             {
0119                 //hack to ensure that we have not already added it to the front of the breadcrumb...
0120                 addBreadCrumbItem( addItem );
0121             }
0122 
0123             if( !additionalItems.isEmpty() )
0124                 additionalItems.last()->setActive( true );
0125             else
0126                 leaf->setActive( true );
0127         }
0128     }
0129     else
0130     {
0131         //if this item has children, add a menu button for selecting these.
0132         BrowserCategoryList *childList = qobject_cast<BrowserCategoryList*>( list );
0133         if( childList )
0134         {
0135             auto childMenuButton = new BreadcrumbItemMenuButton( m_breadcrumbArea );
0136 
0137             auto menu = new QMenu( item );
0138             menu->hide();
0139 
0140             QMap<QString,BrowserCategory *> childMap =  childList->categories();
0141 
0142             const QStringList childNames = childMap.keys();
0143 
0144             for( const QString &siblingName : childNames )
0145             {
0146                 //no point in adding ourselves to this menu
0147                 if ( siblingName == list->name() )
0148                     continue;
0149 
0150                 BrowserCategory * siblingCategory = childMap.value( siblingName );
0151 
0152                 QAction * action = menu->addAction( siblingCategory->icon(), siblingCategory->prettyName() );
0153                 connect( action, &QAction::triggered, childMap.value( siblingName ), &BrowserCategory::activate );
0154 
0155             }
0156 
0157             childMenuButton->setMenu( menu );
0158 
0159             //do a little magic to line up items in the menu with the current item
0160             int offset = 6;
0161             menu->setContentsMargins( offset, 1, 1, 2 );
0162 
0163         }
0164         item->setActive( true );
0165     }
0166 }
0167 
0168 void
0169 BrowserBreadcrumbWidget::addBreadCrumbItem( BrowserBreadcrumbItem *item )
0170 {
0171     item->setParent( m_breadcrumbArea );
0172 }
0173 
0174 void BrowserBreadcrumbWidget::resizeEvent( QResizeEvent *event )
0175 {
0176     Q_UNUSED( event )
0177     // we need to postpone the call, because showAsNeeded() itself may trigger resizeEvent
0178     QTimer::singleShot( 0 , this, &BrowserBreadcrumbWidget::showAsNeeded );
0179 }
0180 
0181 void BrowserBreadcrumbWidget::showAsNeeded()
0182 {
0183     /* we need to check if there is enough space for all items, if not, we start hiding
0184      * items from the left (excluding the home item) until they fit (we never hide the
0185      * rightmost item) we also add the hidden levels to the drop down menu of the last
0186      * item so they are accessible.
0187      */
0188 
0189     //make a temp list that includes both regular items and add items
0190     QList<BrowserBreadcrumbItem *> allItems;
0191 
0192     allItems.append( m_breadcrumbArea->findChildren<BrowserBreadcrumbItem *>(QString(), Qt::FindDirectChildrenOnly));
0193 
0194     if( allItems.isEmpty() )
0195         return;
0196 
0197     int sizeOfFirst = allItems.first()->nominalWidth();
0198     int sizeOfLast = allItems.last()->nominalWidth();
0199 
0200     int spaceLeft = width() - ( sizeOfFirst + sizeOfLast + 28 );
0201     allItems.first()->show();
0202     allItems.last()->show();
0203 
0204     int numberOfItems = allItems.count();
0205 
0206     for( int i = numberOfItems - 2; i > 0; i-- )
0207     {
0208         if( allItems.at( i )->nominalWidth() <= spaceLeft )
0209         {
0210             allItems.at( i )->show();
0211             spaceLeft -= allItems.at( i )->nominalWidth();
0212         }
0213         else
0214         {
0215             //set spaceLeft to 0 so no items further to the left are shown
0216             spaceLeft = 0;
0217             allItems.at( i )->hide();
0218         }
0219     }
0220 }
0221