diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index bd09f66c0..6a6f84cab 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -193,6 +193,11 @@ set( libSources widgets/infowidgets/sourceinfowidget.cpp widgets/infowidgets/ArtistInfoWidget.cpp widgets/infowidgets/AlbumInfoWidget.cpp + widgets/kbreadcrumbselectionmodel.cpp + widgets/breadcrumbbar.cpp + widgets/breadcrumbbuttonbase.cpp + widgets/headerbreadcrumb.cpp + widgets/siblingcrumbbutton.cpp kdsingleapplicationguard/kdsingleapplicationguard.cpp kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -379,6 +384,12 @@ set( libHeaders widgets/infowidgets/sourceinfowidget.h widgets/infowidgets/ArtistInfoWidget.h widgets/infowidgets/AlbumInfoWidget.h + widgets/kbreadcrumbselectionmodel.h + widgets/kbreadcrumbselectionmodel_p.h + widgets/breadcrumbbar.h + widgets/breadcrumbbuttonbase.h + widgets/headerbreadcrumb.h + widgets/siblingcrumbbutton.h kdsingleapplicationguard/kdsingleapplicationguard.h ) diff --git a/src/libtomahawk/widgets/breadcrumbbar.cpp b/src/libtomahawk/widgets/breadcrumbbar.cpp new file mode 100644 index 000000000..43e9a22a0 --- /dev/null +++ b/src/libtomahawk/widgets/breadcrumbbar.cpp @@ -0,0 +1,282 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "breadcrumbbar.h" +#include "breadcrumbbuttonbase.h" + +#include +#include +#include +#include +#include +#include + +#include + +BreadcrumbBar::BreadcrumbBar(BreadcrumbButtonFactory *buttonFactory, QWidget *parent) + : QWidget(parent) + , m_model(0) + , m_selectionModel(0) + , m_layout(new QHBoxLayout(this)) + , m_buttonFactory(buttonFactory) + , m_useAnimation(false) + +{ + m_layout->setSpacing(0); + m_layout->setMargin(0); + m_layout->setAlignment(Qt::AlignLeft); + + setAutoFillBackground(false); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + setLayoutDirection(Qt::LeftToRight); + setLayout(m_layout); + setMinimumWidth(100); + show(); +} + +BreadcrumbBar::BreadcrumbBar(QWidget *parent) + : QWidget(parent) + , m_model(0) + , m_selectionModel(0) + , m_layout(new QHBoxLayout(this)) + , m_buttonFactory(0) + , m_useAnimation(false) + +{ + m_layout->setSpacing(0); + m_layout->setMargin(0); + m_layout->setAlignment(Qt::AlignLeft); + + setAutoFillBackground(false); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + setLayoutDirection(Qt::LeftToRight); + setLayout(m_layout); + setMinimumWidth(100); + show(); +} + +BreadcrumbBar::~BreadcrumbBar() +{ +} +void BreadcrumbBar::setButtonFactory(BreadcrumbButtonFactory *buttonFactory) +{ + m_buttonFactory = buttonFactory; +} + +BreadcrumbButtonFactory* BreadcrumbBar::buttonFactory() const +{ + return m_buttonFactory; +} + +void BreadcrumbBar::appendButton(BreadcrumbButtonBase *widget, int stretch) +{ + m_layout->insertWidget(m_layout->count(), widget, stretch); + if( !m_useAnimation ) + return; //we're done here. + + // A nifty trick to force the widget to calculate its position and geometry + //widget->setAttribute(Qt::WA_DontShowOnScreen); + widget->show(); + //widget->setAttribute(Qt::WA_DontShowOnScreen, false); + + if( m_navButtons.size() > 0 ) { + QWidget* neighbor = m_layout->itemAt(m_layout->count()-2)->widget(); + QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos"); + animation->setDuration(300); + animation->setStartValue(neighbor->pos()); + animation->setEndValue(widget->pos()); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } +} + +void BreadcrumbBar::deleteAnimationFinished() +{ + QPropertyAnimation *anim = qobject_cast(sender()); + + if( !anim ) + return; + QObject *obj = anim->targetObject(); + obj->deleteLater(); + anim->deleteLater(); +} + +void BreadcrumbBar::deleteButton(BreadcrumbButtonBase *widget) +{ + if( !m_useAnimation ) { + widget->hide(); + widget->deleteLater(); + return; // all done here + } + + int index = m_layout->indexOf(widget); + if( index != 0 && m_navButtons.size() > 0 ) { + QWidget* neighbor = m_layout->itemAt(index-1)->widget(); + QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos"); + m_layout->removeWidget(widget); + connect(animation, SIGNAL(finished()), SLOT(deleteAnimationFinished())); + animation->setDuration(300); + animation->setStartValue(widget->pos()); + animation->setEndValue(neighbor->pos()); + animation->start(); + } else { + widget->hide(); + widget->deleteLater(); + } +} +void BreadcrumbBar::updateButtons() +{ + if (!m_buttonFactory || !m_selectionModel || !m_selectionModel->currentIndex().isValid()) + return; + + QLinkedList::iterator it = m_navButtons.begin(); + QLinkedList::const_iterator const itEnd = m_navButtons.end(); + bool createButton = false; + + QModelIndex index = m_selectionModel->currentIndex(); + QList indexes; + while (index.parent().isValid()) + { + indexes.prepend(index); + index = index.parent(); + } + qDebug() << index.data().toString(); + indexes.prepend(index); + + int count = indexes.size(), i = 0; + foreach (index, indexes) + { + createButton = (it == itEnd); + bool isLastButton = (++i == count); + + QString const dirName = index.data().toString(); + BreadcrumbButtonBase *button = 0; + if (createButton) + { + button = m_buttonFactory->newButton(index, this); + appendButton(button); + button->setActive(isLastButton); + m_navButtons.append(button); + } + else + { + button = *it; + button->setIndex(index); + button->setActive(isLastButton); + ++it; + } + } + + QLinkedList::Iterator itBegin = it; + while (it != itEnd) + { + deleteButton(*it); + ++it; + } + m_navButtons.erase(itBegin, m_navButtons.end()); + + foreach (BreadcrumbButtonBase *button, m_navButtons) + button->show(); + + adjustSize(); +} + +void BreadcrumbBar::clearButtons() +{ + foreach (BreadcrumbButtonBase *button, m_navButtons) + { + button->hide(); + button->deleteLater(); + } + m_navButtons.clear(); +} + +void BreadcrumbBar::currentIndexChanged() +{ + updateButtons(); +} + +void BreadcrumbBar::setRootIcon(const QIcon &icon) +{ + m_rootIcon = icon; + QLabel *label= new QLabel(this); + label->setPixmap(icon.pixmap(16,16)); + m_layout->insertWidget(0, label); + m_layout->insertSpacing(0,5); + m_layout->insertSpacing(2,5); +} + +void BreadcrumbBar::setRootText(const QString &text) +{ + //TODO: implement this + m_rootText = text; + /*QLabel *label= new QLabel(this); + label->setPixmap(icon.pixmap(16,16)); + m_layout->insertWidget(0, label); + m_layout->insertSpacing(0,5); + m_layout->insertSpacing(2,5);*/ +} + +void BreadcrumbBar::setUseAnimation(bool use) +{ + m_useAnimation = use; +} + +bool BreadcrumbBar::useAnimation() const +{ + return m_useAnimation; +} + +void BreadcrumbBar::setModel(QAbstractItemModel *model) +{ + m_model = model; + updateButtons(); +} + +QAbstractItemModel* BreadcrumbBar::model() +{ + return m_model; +} + +void BreadcrumbBar::setSelectionModel(QItemSelectionModel *selectionModel) +{ + m_selectionModel = selectionModel; + connect(m_selectionModel, + SIGNAL(currentChanged(QModelIndex const&, QModelIndex const&)), + this, SLOT(currentIndexChanged())); + updateButtons(); +} + +QItemSelectionModel* BreadcrumbBar::selectionModel() +{ + return m_selectionModel; +} + +QModelIndex BreadcrumbBar::currentIndex() +{ + return m_selectionModel->currentIndex(); +} + +void BreadcrumbBar::currentChangedTriggered(QModelIndex const& index) +{ + Q_ASSERT(m_selectionModel); + m_selectionModel->setCurrentIndex( index, QItemSelectionModel::SelectCurrent); +} + diff --git a/src/libtomahawk/widgets/breadcrumbbar.h b/src/libtomahawk/widgets/breadcrumbbar.h new file mode 100644 index 000000000..0a6f8b5ef --- /dev/null +++ b/src/libtomahawk/widgets/breadcrumbbar.h @@ -0,0 +1,183 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef BREADCRUMBBAR_H +#define BREADCRUMBBAR_H + + +#include "breadcrumbbuttonbase.h" + +#include +#include +#include +#include + +class QAbstractItemModel; +class QItemSelectionModel; +class QAbstractItemModel; +class QAbstractProxyModel; +class QHBoxLayout; +class QItemSelectionModel; + +/** + * \brief A breadcrumb view for a QAbstractItemModel + * + * This a lean Breadcrumb navigation bar that supports the following features: + * - a QAbstractItemModel data source for MV goodness + * - client provided crumb button widgets (to use your own style and behavior) + * - client provided crumb animation [optional] + * - client provided root item (icon or text) [optional] + * + */ +class BreadcrumbBar : public QWidget +{ + Q_OBJECT + +public: + /** + * \brief breadcrumb bar constructor + * \param buttonFactory the button factory to instantiate bread crumbs from + */ + BreadcrumbBar(BreadcrumbButtonFactory *buttonFactory, QWidget *parent = 0); + /** + * \brief breadcrumb bar constructor + * You must set the button factory using BreadcrumbBar::setButtonFactory + */ + BreadcrumbBar(QWidget *parent = 0); + + virtual ~BreadcrumbBar(); + + /** + * \brief sets the button factory to use to create buttons + * \param buttonFactory the button factory + */ + void setButtonFactory(BreadcrumbButtonFactory *buttonFactory); + BreadcrumbButtonFactory* buttonFactory() const; + + /** + * \brief Set the icon that should be displayed at the root + * \param icon the icon + */ + void setRootIcon(const QIcon &icon); + /** + * \brief Set the text that should be displayed at the root + * \param test the text + */ + void setRootText(const QString &text); + + /** + * \brief Set whether the crumb animation should be used (default: false) + */ + void setUseAnimation(bool use); + bool useAnimation() const; + + /** + * \brief set the item model to use as a data source + * \param model the item model + * + * Although the item model can be of any structure, the breadcrumb bar + * works best when the model's structure is a tree. + */ + void setModel(QAbstractItemModel *model); + QAbstractItemModel *model(); + + /** + * \brief set the selection model used to determine the crumb items + * \param selectionModel the selection model + * + * The selection model is just as important as your model. In fact your + * model can be of any structure (even not-tree-like) as long as your + * selection model understands what it means for an item to be a crumb, + * and knows how to select it. + * + * If you have a standard tree model, you probably should use + * KBreadCrumbSelectionModel which encapsulates the concept of "When an + * item is selected, make a selection which includes its parent and + * ancestor items until the top is reached". + * + * \sa See Stephen Kelley's blog post here http://steveire.wordpress.com/2010/04/21/breadcrumbs-for-your-view + */ + void setSelectionModel(QItemSelectionModel *selectionModel); + QItemSelectionModel *selectionModel(); + + /** + * \brief get the index of the currently active item + * \return the active index + */ + QModelIndex currentIndex(); + + /** + * \brief used by crumbs to notify that the current index has changed + * \param index the new current index + */ + void currentChangedTriggered(QModelIndex const& index); + +protected: + /** + * \brief append a crumb widget + * \param button the crumb button to add + * \param stretch widget stretch factor + * Respects the useAnimation() setting. + */ + void appendButton(BreadcrumbButtonBase *button, int stretch = 0); + + /** + * \brief deletes a crumb from the bar + * \param button the crumb button to delete + * Respects the useAnimation() setting. + */ + void deleteButton(BreadcrumbButtonBase *button); + +protected slots: + + /** + * \brief The current index has changed in the selection model + * When the selection model changes, we get notified here + */ + void currentIndexChanged(); + + /** + * \brief Recreate the button bar from the selection model + */ + void updateButtons(); + /** + * \brief clear breadcrumb buttons + */ + void clearButtons(); + + /** + * Called when the delete animation finishes so we can delete the button + * object. + */ + void deleteAnimationFinished(); + + +private: + BreadcrumbButtonFactory *m_buttonFactory; /*!< Factory used to create new crumbs */ + QAbstractItemModel *m_model; /*!< The source model */ + QItemSelectionModel *m_selectionModel; /*!< The selection model */ + QHBoxLayout *m_layout; /*!< The layout holding out crumb buttons */ + QLinkedList m_navButtons; /*< Our list of crumbs! */ + QIcon m_rootIcon; /*!< The root icon */ + QString m_rootText; /*!< The root text */ + bool m_useAnimation; /* === + * + * Copyright 2011, Casey Link + * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "breadcrumbbuttonbase.h" +#include "breadcrumbbar.h" + +BreadcrumbButtonBase::BreadcrumbButtonBase(BreadcrumbBar *parent) + : QPushButton(parent), m_breadcrumbBar(parent) +{ + setFocusPolicy(Qt::NoFocus); + setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); + setMinimumHeight(parent->minimumHeight()); +} + +BreadcrumbButtonBase::~BreadcrumbButtonBase() +{ +} + +BreadcrumbBar* BreadcrumbButtonBase::breadcrumbBar() const +{ + return m_breadcrumbBar; +} + diff --git a/src/libtomahawk/widgets/breadcrumbbuttonbase.h b/src/libtomahawk/widgets/breadcrumbbuttonbase.h new file mode 100644 index 000000000..7625b6bae --- /dev/null +++ b/src/libtomahawk/widgets/breadcrumbbuttonbase.h @@ -0,0 +1,94 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.org + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef BREADCRUMBBUTTONBASE_P_H +#define BREADCRUMBBUTTONBASE_P_H + +#include +#include + +class BreadcrumbBar; +class BreadcrumbButtonBase; + +/** + * \brief an abstract factory class to create crumb buttons + * Subclass this class and make it return bread crumb buttons of your type. + */ +class BreadcrumbButtonFactory { + + public: + BreadcrumbButtonFactory(){} + + virtual ~BreadcrumbButtonFactory(){} + + /** + * \brief instantiates a new bread crumb button + * \param index the initial index this crumb should hold + * \param parent the breadcrumb bar this button will belong to + * \returns a new bread crumb button allocated on the heap. + * + * This method can be as simple as: + * \code + * return new MyBreadCrumbButton(index, parent); + * \endcode + * + */ + virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent) = 0; + +}; + +/** + * \brief The button base class for the BreadcrumbBar + * Re-implement this to provide your own crumb buttons. Don't forget to supply + * a BreadcrumbButtonFactory as well. + */ +class BreadcrumbButtonBase : public QPushButton +{ + Q_OBJECT + +public: + explicit BreadcrumbButtonBase(BreadcrumbBar *parent); + virtual ~BreadcrumbButtonBase(); + + /** + * \brief retrieve the breadcrumb bar that this button belongs to + * \return the parent breadcrumb bar + */ + BreadcrumbBar* breadcrumbBar() const; + + /** + * \brief set the model item that this button represents + * \param index the index of the model items to display + */ + virtual void setIndex(QModelIndex index) = 0; + virtual QModelIndex index() const = 0; + + /** + * \brief sets whether this button is active or not + * \param active true for active, false for inactive + * You could, for example, make the active button bold. + */ + virtual void setActive(bool active) = 0; + virtual bool isActive() const = 0; + +private: + BreadcrumbBar *m_breadcrumbBar; +}; + +#endif // BREADCRUMBBUTTONBASE_P_H diff --git a/src/libtomahawk/widgets/headerbreadcrumb.cpp b/src/libtomahawk/widgets/headerbreadcrumb.cpp new file mode 100644 index 000000000..80c453672 --- /dev/null +++ b/src/libtomahawk/widgets/headerbreadcrumb.cpp @@ -0,0 +1,46 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "headerbreadcrumb.h" + +#include "utils/stylehelper.h" + +#include +#include +#include + +HeaderBreadCrumb::HeaderBreadCrumb(BreadcrumbButtonFactory *buttonFactory, QWidget *parent) : + BreadcrumbBar(buttonFactory, parent) +{ +} + +HeaderBreadCrumb::HeaderBreadCrumb(QWidget *parent) : + BreadcrumbBar(parent) +{ +} + +HeaderBreadCrumb::~HeaderBreadCrumb() +{ +} + +void HeaderBreadCrumb::paintEvent(QPaintEvent *event) +{ + + QStylePainter p(this); + StyleHelper::horizontalHeader(&p, event->rect()); +} diff --git a/src/libtomahawk/widgets/headerbreadcrumb.h b/src/libtomahawk/widgets/headerbreadcrumb.h new file mode 100644 index 000000000..cfcafa5cf --- /dev/null +++ b/src/libtomahawk/widgets/headerbreadcrumb.h @@ -0,0 +1,44 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef HEADERBREADCRUMB_H +#define HEADERBREADCRUMB_H + +#include "breadcrumbbar.h" + + +class QPaintEvent; + + +/** + * \brief a bread crumb widget with Tomahawk's distinctive header style + */ +class HeaderBreadCrumb : public BreadcrumbBar +{ + Q_OBJECT + public: + HeaderBreadCrumb(BreadcrumbButtonFactory *buttonFactory, QWidget *parent = 0); + HeaderBreadCrumb(QWidget *parent = 0); + ~HeaderBreadCrumb(); + + protected: + + virtual void paintEvent(QPaintEvent *event); +}; + +#endif diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp new file mode 100644 index 000000000..92af04c96 --- /dev/null +++ b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp @@ -0,0 +1,202 @@ +/* + Copyright (C) 2010 Klarälvdalens Datakonsult AB, + a KDAB Group company, info@kdab.net, + author Stephen Kelly + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + + +#include "kbreadcrumbselectionmodel.h" +#include "kbreadcrumbselectionmodel_p.h" + +#include + +KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent) + : QItemSelectionModel(const_cast(selectionModel->model()), parent), + d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, MakeBreadcrumbSelectionInSelf)) +{ + d_ptr->init(); +} + +KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget direction, QObject* parent) + : QItemSelectionModel(const_cast(selectionModel->model()), parent), + d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, direction)) +{ + if ( direction != MakeBreadcrumbSelectionInSelf) + connect(selectionModel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), + this, SLOT(sourceSelectionChanged(const QItemSelection&,const QItemSelection&))); + + d_ptr->init(); +} + +KBreadcrumbSelectionModel::~KBreadcrumbSelectionModel() +{ + delete d_ptr; +} + +bool KBreadcrumbSelectionModel::isActualSelectionIncluded() const +{ + Q_D(const KBreadcrumbSelectionModel); + return d->m_includeActualSelection; +} + +void KBreadcrumbSelectionModel::setActualSelectionIncluded(bool includeActualSelection) +{ + Q_D(KBreadcrumbSelectionModel); + d->m_includeActualSelection = includeActualSelection; +} + +int KBreadcrumbSelectionModel::breadcrumbLength() const +{ + Q_D(const KBreadcrumbSelectionModel); + return d->m_selectionDepth; +} + +void KBreadcrumbSelectionModel::setBreadcrumbLength(int breadcrumbLength) +{ + Q_D(KBreadcrumbSelectionModel); + d->m_selectionDepth = breadcrumbLength; +} + +QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QModelIndex& index) +{ + QItemSelection breadcrumbSelection; + + if (m_includeActualSelection) + breadcrumbSelection.append(QItemSelectionRange(index)); + + QModelIndex parent = index.parent(); + int sumBreadcrumbs = 0; + bool includeAll = m_selectionDepth < 0; + while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) { + breadcrumbSelection.append(QItemSelectionRange(parent)); + parent = parent.parent(); + } + return breadcrumbSelection; +} + +QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QItemSelection& selection) +{ + QItemSelection breadcrumbSelection; + + if (m_includeActualSelection) + breadcrumbSelection = selection; + + QItemSelection::const_iterator it = selection.constBegin(); + const QItemSelection::const_iterator end = selection.constEnd(); + + for ( ; it != end; ++it) + { + QModelIndex parent = it->parent(); + + if (breadcrumbSelection.contains(parent)) + continue; + + int sumBreadcrumbs = 0; + bool includeAll = m_selectionDepth < 0; + + while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) + { + breadcrumbSelection.append(QItemSelectionRange(parent)); + parent = parent.parent(); + + if (breadcrumbSelection.contains(parent)) + break; + + ++sumBreadcrumbs; + } + } + return breadcrumbSelection; +} + +void KBreadcrumbSelectionModelPrivate::sourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) +{ + Q_Q(KBreadcrumbSelectionModel); + QItemSelection deselectedCrumbs = getBreadcrumbSelection(deselected); + QItemSelection selectedCrumbs = getBreadcrumbSelection(selected); + + QItemSelection removed = deselectedCrumbs; + foreach(const QItemSelectionRange &range, selectedCrumbs) + { + removed.removeAll(range); + } + + QItemSelection added = selectedCrumbs; + foreach(const QItemSelectionRange &range, deselectedCrumbs) + { + added.removeAll(range); + } + + if (!removed.isEmpty()) + { + q->QItemSelectionModel::select(removed, QItemSelectionModel::Deselect); + } + if (!added.isEmpty()) + { + q->QItemSelectionModel::select(added, QItemSelectionModel::Select); + } +} + +void KBreadcrumbSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) +{ + Q_D(KBreadcrumbSelectionModel); + // When an item is removed, the current index is set to the top index in the model. + // That causes a selectionChanged signal with a selection which we do not want. + if ( d->m_ignoreCurrentChanged ) + { + d->m_ignoreCurrentChanged = false; + return; + } + if ( d->m_direction == MakeBreadcrumbSelectionInOther ) + { + d->m_selectionModel->select(d->getBreadcrumbSelection(index), command); + QItemSelectionModel::select(index, command); + } else { + d->m_selectionModel->select(index, command); + QItemSelectionModel::select(d->getBreadcrumbSelection(index), command); + } +} + +void KBreadcrumbSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) +{ + Q_D(KBreadcrumbSelectionModel); + QItemSelection bcc = d->getBreadcrumbSelection(selection); + if ( d->m_direction == MakeBreadcrumbSelectionInOther ) + { + d->m_selectionModel->select(selection, command); + QItemSelectionModel::select(bcc, command); + } else { + d->m_selectionModel->select(bcc, command); + QItemSelectionModel::select(selection, command); + } +} + +void KBreadcrumbSelectionModelPrivate::init() +{ + Q_Q(KBreadcrumbSelectionModel); + q->connect(m_selectionModel->model(), SIGNAL(layoutChanged()), SLOT(syncBreadcrumbs())); + q->connect(m_selectionModel->model(), SIGNAL(modelReset()), SLOT(syncBreadcrumbs())); + q->connect(m_selectionModel->model(), SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(syncBreadcrumbs())); + // Don't need to handle insert & remove because they can't change the breadcrumbs on their own. +} + +void KBreadcrumbSelectionModelPrivate::syncBreadcrumbs() +{ + Q_Q(KBreadcrumbSelectionModel); + q->select(m_selectionModel->selection(), QItemSelectionModel::ClearAndSelect); +} + diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h new file mode 100644 index 000000000..751be28ef --- /dev/null +++ b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h @@ -0,0 +1,164 @@ +/* + Copyright (C) 2010 Klarälvdalens Datakonsult AB, + a KDAB Group company, info@kdab.net, + author Stephen Kelly + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef KBREADCRUMBSPROXYMODEL_H +#define KBREADCRUMBSPROXYMODEL_H + +#include +#include +#include + + +class KBreadcrumbSelectionModelPrivate; + +/** + @class KBreadcrumbSelectionModel kbreadcrumbselectionmodel.h + + @brief Selects the parents of selected items to create breadcrumbs + + For example, if the tree is + @verbatim + - A + - B + - - C + - - D + - - - E + - - - - F + @endverbatim + + and E is selected, the selection can contain + + @verbatim + - B + - D + @endverbatim + + or + + @verbatim + - B + - D + - E + @endverbatim + + if isActualSelectionIncluded is true. + + The depth of the selection may also be set. For example if the breadcrumbLength is 1: + + @verbatim + - D + - E + @endverbatim + + And if breadcrumbLength is 2: + + @verbatim + - B + - D + - E + @endverbatim + + A KBreadcrumbsProxyModel with a breadcrumbLength of 0 and including the actual selection is + the same as a KSelectionProxyModel in the KSelectionProxyModel::ExactSelection configuration. + + @code + view1->setModel(rootModel); + + QItemSelectionModel *breadcrumbSelectionModel = new QItemSelectionModel(rootModel, this); + + KBreadcrumbSelectionModel *breadcrumbProxySelector = new KBreadcrumbSelectionModel(breadcrumbSelectionModel, rootModel, this); + + view1->setSelectionModel(breadcrumbProxySelector); + + KSelectionProxyModel *breadcrumbSelectionProxyModel = new KSelectionProxyModel( breadcrumbSelectionModel, this); + breadcrumbSelectionProxyModel->setSourceModel( rootModel ); + breadcrumbSelectionProxyModel->setFilterBehavior( KSelectionProxyModel::ExactSelection ); + + view2->setModel(breadcrumbSelectionProxyModel); + @endcode + + @image html kbreadcrumbselectionmodel.png "KBreadcrumbSelectionModel in several configurations" + + This can work in two directions. One option is for a single selection in the KBreadcrumbSelectionModel to invoke + the breadcrumb selection in its constructor argument. + + The other is for a selection in the itemselectionmodel in the constructor argument to cause a breadcrumb selection + in @p this. + + @since 4.5 + +*/ +class KBreadcrumbSelectionModel : public QItemSelectionModel +{ + Q_OBJECT +public: + enum BreadcrumbTarget + { + MakeBreadcrumbSelectionInOther, + MakeBreadcrumbSelectionInSelf + }; + + explicit KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent = 0); + KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget target, QObject* parent = 0); + virtual ~KBreadcrumbSelectionModel(); + + /** + Returns whether the actual selection in included in the proxy. + + The default is true. + */ + bool isActualSelectionIncluded() const; + + /** + Set whether the actual selection in included in the proxy to @p isActualSelectionIncluded. + */ + void setActualSelectionIncluded(bool isActualSelectionIncluded); + + /** + Returns the depth that the breadcrumb selection should go to. + */ + int breadcrumbLength() const; + + /** + Sets the depth that the breadcrumb selection should go to. + + If the @p breadcrumbLength is -1, all breadcrumbs are selected. + The default is -1 + */ + void setBreadcrumbLength(int breadcrumbLength); + + /* reimp */ void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command); + + /* reimp */ void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); + +protected: + KBreadcrumbSelectionModelPrivate * const d_ptr; +private: + //@cond PRIVATE + Q_DECLARE_PRIVATE(KBreadcrumbSelectionModel) + Q_PRIVATE_SLOT( d_func(),void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)) + Q_PRIVATE_SLOT( d_func(),void syncBreadcrumbs()) + //@cond PRIVATE +}; + + +#include "kbreadcrumbselectionmodel_p.h" //HACK ALERT - Why doesn't it compile without this?! +#endif diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h new file mode 100644 index 000000000..93aac514c --- /dev/null +++ b/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h @@ -0,0 +1,71 @@ +/* + Copyright (C) 2010 Klarälvdalens Datakonsult AB, + a KDAB Group company, info@kdab.net, + author Stephen Kelly + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef KBREADCRUMBSPROXYMODEL_P_H +#define KBREADCRUMBSPROXYMODEL_P_H + +#include "kbreadcrumbselectionmodel.h" + +#include +#include +#include + +class KBreadcrumbSelectionModelPrivate +{ + Q_DECLARE_PUBLIC(KBreadcrumbSelectionModel) + KBreadcrumbSelectionModel * const q_ptr; +public: + KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector, QItemSelectionModel *selectionModel, KBreadcrumbSelectionModel::BreadcrumbTarget direction) + : q_ptr(breadcrumbSelector), + m_includeActualSelection(true), + m_selectionDepth(-1), + m_showHiddenAscendantData(false), + m_selectionModel(selectionModel), + m_direction(direction), + m_ignoreCurrentChanged(false) + { + + } + + /** + Returns a selection containing the breadcrumbs for @p index + */ + QItemSelection getBreadcrumbSelection(const QModelIndex &index); + + /** + Returns a selection containing the breadcrumbs for @p selection + */ + QItemSelection getBreadcrumbSelection(const QItemSelection &selection); + + void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + + void init(); + void syncBreadcrumbs(); + + bool m_includeActualSelection; + int m_selectionDepth; + bool m_showHiddenAscendantData; + QItemSelectionModel *m_selectionModel; + KBreadcrumbSelectionModel::BreadcrumbTarget m_direction; + bool m_ignoreCurrentChanged; +}; + +#endif diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.cpp b/src/libtomahawk/widgets/siblingcrumbbutton.cpp new file mode 100644 index 000000000..ebc607ac5 --- /dev/null +++ b/src/libtomahawk/widgets/siblingcrumbbutton.cpp @@ -0,0 +1,149 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "siblingcrumbbutton.h" + +#include "combobox.h" +#include "utils/stylehelper.h" + +#include +#include +#include +#include +#include + +BreadcrumbButtonBase* SiblingCrumbButtonFactory::newButton(QModelIndex index, BreadcrumbBar *parent) +{ + return new SiblingCrumbButton(index, parent); +} + +SiblingCrumbButton::SiblingCrumbButton( + QModelIndex index, BreadcrumbBar *parent) + : BreadcrumbButtonBase(parent), + m_index(index), m_combo( new ComboBox(this) ) +{ + + setIndex(index); + connect(this, SIGNAL(clicked()), + this, SLOT(updateNavigatorCurrentIndex())); + connect(m_combo, SIGNAL(activated(int)), SLOT(comboboxActivated(int))); + +// QTimer::singleShot(0, this, SLOT(activateSelf())); +} + +void SiblingCrumbButton::setIndex(QModelIndex index) +{ + m_index = index; + setText(index.data().toString()); + qDebug() << "i am " << text(); + fillCombo(); +} + +QModelIndex SiblingCrumbButton::index() const +{ + return m_index; +} + +void SiblingCrumbButton::setActive(bool active) +{ +} + +bool SiblingCrumbButton::isActive() const +{ + return false; +} + +QSize SiblingCrumbButton::sizeHint() const +{ + // our width = width of combo + 20px for right-arrow and spacing + return m_combo->sizeHint() + QSize(20,0); +} + +void SiblingCrumbButton::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter p(this); + QStyleOption opt; + opt.initFrom(this); + QRect r = opt.rect; + + StyleHelper::horizontalHeader(&p, r); // draw the background + + bool reverse = opt.direction == Qt::RightToLeft; + int menuButtonWidth = 12; + int rightSpacing = 10; + int left = !reverse ? r.right()-rightSpacing - menuButtonWidth : r.left(); + int right = !reverse ? r.right()-rightSpacing : r.left() + menuButtonWidth; + int height = sizeHint().height(); + QRect arrowRect((left + right) / 2 + (reverse ? 6 : -6), 0, height, height); + + QStyleOption arrowOpt = opt; + arrowOpt.rect = arrowRect; + + + QLine l1(left, 0, right, height/2); + QLine l2(left, height, right, height/2); + + + p.setRenderHint(QPainter::Antialiasing, true); + + // Draw the shadow + QColor shadow(0, 0, 0, 100); + p.translate(0, -1); + p.setPen(shadow); + p.drawLine(l1); + p.drawLine(l2); + + // Draw the main arrow + QColor foreGround("#747474"); + p.translate(0, 1); + p.setPen(foreGround); + p.drawLine(l1); + p.drawLine(l2); +} + +void SiblingCrumbButton::fillCombo() +{ + QStringList list; + int count = breadcrumbBar()->model()->rowCount(m_index.parent()); + for(int i = 0; i < count; ++i) { + QModelIndex sibling = m_index.sibling(i,0); + if( sibling.isValid() ) + list << sibling.data().toString(); + } + + m_combo->clear(); + m_combo->addItems(list); + m_combo->setCurrentIndex( m_combo->findText(text())); +} + +void SiblingCrumbButton::comboboxActivated(int i) +{ + QModelIndex activated = m_index.sibling(i,0); + int count = breadcrumbBar()->model()->rowCount(activated); + if( count > 0 ) { + qDebug() << "activated" << activated.child(0,0).data().toString(); + breadcrumbBar()->currentChangedTriggered(activated.child(0,0)); + } +} + +void SiblingCrumbButton::activateSelf() +{ + comboboxActivated(m_index.row()); +} diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.h b/src/libtomahawk/widgets/siblingcrumbbutton.h new file mode 100644 index 000000000..598a6ba53 --- /dev/null +++ b/src/libtomahawk/widgets/siblingcrumbbutton.h @@ -0,0 +1,79 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2011, Casey Link + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef SIBLINGCRUMBBUTTON_H +#define SIBLINGCRUMBBUTTON_H + +#include "breadcrumbbuttonbase.h" +#include "breadcrumbbar.h" + +#include "combobox.h" + +#include +#include +#include +#include +#include + + +/** + * \brief A factory for sibling crumb buttons + */ +class SiblingCrumbButtonFactory : public BreadcrumbButtonFactory +{ + + public: + SiblingCrumbButtonFactory(){} + + virtual ~SiblingCrumbButtonFactory(){} + virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent); +}; + +/** + * \brief A crumb button implementation where the dropdowns show sibling items + * Unlike most crumb buttons, this one shows a list of sibling items (as + * opposed to child items). This is desireable in certain circumstances. + */ +class SiblingCrumbButton : public BreadcrumbButtonBase +{ + Q_OBJECT + +public: + SiblingCrumbButton(QModelIndex index, BreadcrumbBar *parent); + + void setIndex(QModelIndex index); + QModelIndex index() const; + void setActive(bool active); + bool isActive() const; + virtual QSize sizeHint() const; + +protected: + virtual void paintEvent(QPaintEvent *event); + +private slots: + void fillCombo(); + void comboboxActivated(int i); + void activateSelf(); + +private: + QModelIndex m_index; /*!< our current index */ + ComboBox *m_combo; /*!< our combobox! */ + +}; + +#endif // SIBLINGCRUMBBUTTON_H