diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa8c89971..147945845 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -271,9 +271,7 @@ set(SOURCES widgets/fileviewlist.cpp widgets/freespacebar.cpp widgets/fullscreenhypnotoad.cpp - widgets/kcategorizedsortfilterproxymodel.cpp - widgets/kcategorizedview.cpp - widgets/kcategorydrawer.cpp + widgets/groupediconview.cpp widgets/lineedit.cpp widgets/linetextedit.cpp widgets/multiloadingindicator.cpp @@ -476,8 +474,7 @@ set(HEADERS widgets/fileview.h widgets/fileviewlist.h widgets/freespacebar.h - widgets/kcategorizedview.h - widgets/kcategorydrawer.h + widgets/groupediconview.h widgets/lineedit.h widgets/linetextedit.h widgets/multiloadingindicator.h diff --git a/src/ui/albumcoversearcher.cpp b/src/ui/albumcoversearcher.cpp index 64dad6fd9..22fb29fb2 100644 --- a/src/ui/albumcoversearcher.cpp +++ b/src/ui/albumcoversearcher.cpp @@ -20,7 +20,7 @@ #include "core/logging.h" #include "covers/albumcoverfetcher.h" #include "covers/albumcoverloader.h" -#include "widgets/kcategorizedsortfilterproxymodel.h" +#include "widgets/groupediconview.h" #include #include @@ -28,54 +28,6 @@ #include -const int AlbumCoverCategoryDrawer::kBarThickness = 2; -const int AlbumCoverCategoryDrawer::kBarMarginTop = 3; -const int AlbumCoverCategoryDrawer::kBarMarginBottom = 10; - - -AlbumCoverCategoryDrawer::AlbumCoverCategoryDrawer(KCategorizedView* view) - : KCategoryDrawerV3(view), - total_height_(view->fontMetrics().height() + - kBarMarginTop + kBarThickness + kBarMarginBottom) { - setLeftMargin(kBarMarginBottom); -} - -int AlbumCoverCategoryDrawer::categoryHeight(const QModelIndex&, - const QStyleOption&) const { - return total_height_; -} - -void AlbumCoverCategoryDrawer::drawCategory(const QModelIndex& index, int, - const QStyleOption& option, - QPainter* painter) const { - painter->save(); - - // Bold font - QFont font(view()->font()); - font.setBold(true); - QFontMetrics metrics(font); - - // Draw text - const QString category = tr("Covers from %1").arg( - index.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString()); - painter->setFont(font); - painter->drawText(option.rect, category); - - // Draw a line underneath - const QPoint start(option.rect.left(), - option.rect.top() + metrics.height() + kBarMarginTop); - const QPoint end(option.rect.right(), start.y()); - - painter->setRenderHint(QPainter::Antialiasing, true); - painter->setPen(QPen(option.palette.color(QPalette::Disabled, QPalette::Text), - kBarThickness, Qt::SolidLine, Qt::RoundCap)); - painter->setOpacity(0.5); - painter->drawLine(start, end); - - painter->restore(); -} - - AlbumCoverSearcher::AlbumCoverSearcher(const QIcon& no_cover_icon, QWidget* parent) : QDialog(parent), ui_(new Ui_AlbumCoverSearcher), @@ -88,11 +40,8 @@ AlbumCoverSearcher::AlbumCoverSearcher(const QIcon& no_cover_icon, QWidget* pare ui_->setupUi(this); ui_->busy->hide(); - KCategorizedSortFilterProxyModel* proxy = new KCategorizedSortFilterProxyModel(this); - proxy->setCategorizedModel(true); - proxy->setSourceModel(model_); - ui_->covers->setModel(proxy); - ui_->covers->setCategoryDrawer(new AlbumCoverCategoryDrawer(ui_->covers)); + ui_->covers->set_header_text(tr("Covers from %1")); + ui_->covers->setModel(model_); loader_->Start(true); loader_->Worker()->SetDefaultOutputImage(QImage(":nocover.png")); @@ -174,8 +123,7 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const CoverSearchResults& re item->setData(id, Role_ImageRequestId); item->setData(false, Role_ImageFetchFinished); item->setData(QVariant(Qt::AlignTop | Qt::AlignHCenter), Qt::TextAlignmentRole); - item->setData(result.category, KCategorizedSortFilterProxyModel::CategoryDisplayRole); - item->setData(result.category, KCategorizedSortFilterProxyModel::CategorySortRole); + item->setData(result.category, GroupedIconView::Role_Group); model_->appendRow(item); @@ -192,9 +140,21 @@ void AlbumCoverSearcher::ImageLoaded(quint64 id, const QImage& image) { QIcon icon(QPixmap::fromImage(image)); - // Add an icon that's the right size for the view - icon.addPixmap(QPixmap::fromImage(image.scaled(ui_->covers->iconSize(), - Qt::KeepAspectRatio, Qt::SmoothTransformation))); + // Create a pixmap that's padded and exactly the right size for the icon. + QImage scaled_image(image.scaled(ui_->covers->iconSize(), + Qt::KeepAspectRatio, + Qt::SmoothTransformation)); + + QImage padded_image(ui_->covers->iconSize(), QImage::Format_ARGB32_Premultiplied); + padded_image.fill(0); + + QPainter p(&padded_image); + p.drawImage((padded_image.width() - scaled_image.width()) / 2, + (padded_image.height() - scaled_image.height()) / 2, + scaled_image); + p.end(); + + icon.addPixmap(QPixmap::fromImage(padded_image)); QStandardItem* item = cover_loading_tasks_.take(id); item->setData(true, Role_ImageFetchFinished); diff --git a/src/ui/albumcoversearcher.h b/src/ui/albumcoversearcher.h index 65eb67703..e566997f7 100644 --- a/src/ui/albumcoversearcher.h +++ b/src/ui/albumcoversearcher.h @@ -20,7 +20,6 @@ #include "core/backgroundthread.h" #include "covers/albumcoverfetcher.h" -#include "widgets/kcategorydrawer.h" #include #include @@ -34,22 +33,6 @@ class QModelIndex; class QStandardItem; class QStandardItemModel; -class AlbumCoverCategoryDrawer : public KCategoryDrawerV3 { -public: - AlbumCoverCategoryDrawer(KCategorizedView* view); - - int categoryHeight(const QModelIndex& index, const QStyleOption& option) const; - void drawCategory(const QModelIndex& index, int sortRole, - const QStyleOption& option, QPainter* painter) const; - -private: - static const int kBarThickness; - static const int kBarMarginTop; - static const int kBarMarginBottom; - - int total_height_; -}; - // This is a dialog that lets the user search for album covers class AlbumCoverSearcher : public QDialog { diff --git a/src/ui/albumcoversearcher.ui b/src/ui/albumcoversearcher.ui index 499d824fa..f3ffe1100 100644 --- a/src/ui/albumcoversearcher.ui +++ b/src/ui/albumcoversearcher.ui @@ -49,8 +49,11 @@ - + + Qt::ScrollBarAsNeeded + + Qt::ScrollBarAlwaysOff @@ -59,27 +62,9 @@ 120 - - QListView::Free - - - QListView::LeftToRight - - - QListView::Adjust - 2 - - QListView::IconMode - - - true - - - true - @@ -106,9 +91,9 @@
widgets/busyindicator.h
- KCategorizedView + GroupedIconView QListView -
widgets/kcategorizedview.h
+
widgets/groupediconview.h
diff --git a/src/widgets/groupediconview.cpp b/src/widgets/groupediconview.cpp new file mode 100644 index 000000000..e37d81c95 --- /dev/null +++ b/src/widgets/groupediconview.cpp @@ -0,0 +1,347 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#include "groupediconview.h" + +#include +#include +#include +#include +#include + +const int GroupedIconView::kBarThickness = 2; +const int GroupedIconView::kBarMarginTop = 3; + + +GroupedIconView::GroupedIconView(QWidget* parent) + : QListView(parent), + proxy_model_(new QSortFilterProxyModel(this)), + default_header_height_(fontMetrics().height() + + kBarMarginTop + kBarThickness), + header_spacing_(10), + header_indent_(5), + item_indent_(10), + header_text_("%1") +{ + setFlow(LeftToRight); + setViewMode(IconMode); + setResizeMode(Adjust); + setWordWrap(true); + setDragEnabled(false); + + proxy_model_->setSortRole(Role_Group); + proxy_model_->setDynamicSortFilter(true); + + connect(proxy_model_, SIGNAL(modelReset()), SLOT(LayoutItems())); +} + +void GroupedIconView::setModel(QAbstractItemModel* model) { + proxy_model_->setSourceModel(model); + proxy_model_->sort(0); + + QListView::setModel(proxy_model_); + LayoutItems(); +} + +int GroupedIconView::header_height() const { + return default_header_height_; +} + +void GroupedIconView::DrawHeader(const QModelIndex& index, + const QRect& rect, QPainter* painter) { + painter->save(); + + // Bold font + QFont bold_font(font()); + bold_font.setBold(true); + QFontMetrics metrics(bold_font); + + // Draw text + const QString category = header_text_.arg(index.data(Role_Group).toString()); + painter->setFont(bold_font); + painter->drawText(rect, category); + + // Draw a line underneath + const QPoint start(rect.left(), rect.top() + metrics.height()); + const QPoint end(rect.right(), start.y()); + + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setPen(QPen(palette().color(QPalette::Disabled, QPalette::Text), + kBarThickness, Qt::SolidLine, Qt::RoundCap)); + painter->setOpacity(0.5); + painter->drawLine(start, end); + + painter->restore(); +} + +void GroupedIconView::resizeEvent(QResizeEvent* e) { + QListView::resizeEvent(e); + LayoutItems(); +} + +void GroupedIconView::rowsInserted(const QModelIndex& parent, int start, int end) { + QListView::rowsInserted(parent, start, end); + LayoutItems(); +} + +void GroupedIconView::LayoutItems() { + if (!model()) + return; + + const int count = model()->rowCount(); + + QString last_group; + QPoint next_position(0, 0); + int max_row_height = 0; + + visual_rects_.clear(); + visual_rects_.reserve(count); + headers_.clear(); + + for (int i=0 ; iindex(i, 0)); + const QString group = index.data(Role_Group).toString(); + const QSize size(rectForIndex(index).size()); + + // Is this the first item in a new group? + if (group != last_group) { + // Add the group header. + Header header; + header.y = next_position.y() + max_row_height + header_indent_; + header.first_row = i; + header.text = group; + + if (!last_group.isNull()) { + header.y += header_spacing_; + } + + headers_ << header; + + // Remember this group so we don't add it again. + last_group = group; + + // Move the next item immediately below the header. + next_position.setX(0); + next_position.setY(header.y + header_height() + header_indent_ + header_spacing_); + max_row_height = 0; + } + + // Take into account padding and spacing + QPoint this_position(next_position); + if (this_position.x() == 0) { + this_position.setX(this_position.x() + item_indent_); + } else { + this_position.setX(this_position.x() + spacing()); + } + + // Should this item wrap? + if (next_position.x() != 0 && this_position.x() + size.width() >= viewport()->width()) { + next_position.setX(0); + next_position.setY(next_position.y() + max_row_height); + this_position = next_position; + this_position.setX(this_position.x() + item_indent_); + + max_row_height = 0; + } + + // Set this item's geometry + visual_rects_.append(QRect(this_position, size)); + + // Update next index + next_position.setX(this_position.x() + size.width()); + max_row_height = qMax(max_row_height, size.height()); + } + + verticalScrollBar()->setRange(0, next_position.y() + max_row_height - viewport()->height()); + update(); +} + +QRect GroupedIconView::visualRect(const QModelIndex& index) const { + if (index.row() < 0 || index.row() >= visual_rects_.count()) + return QRect(); + return visual_rects_[index.row()].translated(-horizontalOffset(), -verticalOffset()); +} + +QModelIndex GroupedIconView::indexAt(const QPoint& p) const { + const QPoint viewport_p = p + QPoint(horizontalOffset(), verticalOffset()); + + const int count = visual_rects_.count(); + for (int i=0 ; iindex(i, 0); + } + } + return QModelIndex(); +} + +void GroupedIconView::paintEvent(QPaintEvent* e) { + // This code was adapted from QListView::paintEvent(), changed to use the + // visualRect() of items, and to draw headers. + + QStyleOptionViewItemV4 option(viewOptions()); + if (isWrapping()) + option.features = QStyleOptionViewItemV2::WrapText; + option.locale = locale(); + option.locale.setNumberOptions(QLocale::OmitGroupSeparator); + option.widget = this; + + QPainter painter(viewport()); + + const QRect viewport_rect(e->rect().translated(horizontalOffset(), verticalOffset())); + QVector toBeRendered = IntersectingItems(viewport_rect); + + const QModelIndex current = currentIndex(); + const QAbstractItemModel *itemModel = model(); + const QItemSelectionModel *selections = selectionModel(); + const bool focus = (hasFocus() || viewport()->hasFocus()) && current.isValid(); + const QStyle::State state = option.state; + const QAbstractItemView::State viewState = this->state(); + const bool enabled = (state & QStyle::State_Enabled) != 0; + + int maxSize = (flow() == TopToBottom) + ? viewport()->size().width() - 2 * spacing() + : viewport()->size().height() - 2 * spacing(); + + QVector::const_iterator end = toBeRendered.constEnd(); + for (QVector::const_iterator it = toBeRendered.constBegin(); it != end; ++it) { + if (!it->isValid()) { + continue; + } + + option.rect = visualRect(*it); + + if (flow() == TopToBottom) + option.rect.setWidth(qMin(maxSize, option.rect.width())); + else + option.rect.setHeight(qMin(maxSize, option.rect.height())); + + option.state = state; + if (selections && selections->isSelected(*it)) + option.state |= QStyle::State_Selected; + if (enabled) { + QPalette::ColorGroup cg; + if ((itemModel->flags(*it) & Qt::ItemIsEnabled) == 0) { + option.state &= ~QStyle::State_Enabled; + cg = QPalette::Disabled; + } else { + cg = QPalette::Normal; + } + option.palette.setCurrentColorGroup(cg); + } + if (focus && current == *it) { + option.state |= QStyle::State_HasFocus; + if (viewState == EditingState) + option.state |= QStyle::State_Editing; + } + + itemDelegate()->paint(&painter, option, *it); + } + + // Draw headers + foreach (const Header& header, headers_) { + const QRect header_rect = QRect( + header_indent_, header.y, + viewport()->width() - header_indent_ * 2, header_height()); + + // Is this header contained in the area we're drawing? + if (!header_rect.intersects(viewport_rect)) { + continue; + } + + // Draw the header + DrawHeader(model()->index(header.first_row, 0), + header_rect.translated(-horizontalOffset(), -verticalOffset()), + &painter); + } +} + +void GroupedIconView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) { + QVector indexes(IntersectingItems(rect.translated(horizontalOffset(), verticalOffset()))); + QItemSelection selection; + + foreach (const QModelIndex& index, indexes) { + selection << QItemSelectionRange(index); + } + + selectionModel()->select(selection, command); +} + +QVector GroupedIconView::IntersectingItems(const QRect& rect) const { + QVector ret; + + const int count = visual_rects_.count(); + for (int i=0 ; iindex(i, 0)); + } + } + + return ret; +} + +QRegion GroupedIconView::visualRegionForSelection(const QItemSelection& selection) const { + QRegion ret; + foreach (const QModelIndex& index, selection.indexes()) { + ret += visual_rects_[index.row()]; + } + return ret; +} + +QModelIndex GroupedIconView::moveCursor(CursorAction action, Qt::KeyboardModifiers) { + if (model()->rowCount() == 0) { + return QModelIndex(); + } + + int ret = currentIndex().row(); + if (ret == -1) { + ret = 0; + } + + switch (action) { + case MoveUp: ret = IndexAboveOrBelow(ret, -1); break; + case MovePrevious: + case MoveLeft: ret --; break; + case MoveDown: ret = IndexAboveOrBelow(ret, +1); break; + case MoveNext: + case MoveRight: ret ++; break; + case MovePageUp: + case MoveHome: ret = 0; break; + case MovePageDown: + case MoveEnd: ret = model()->rowCount() - 1; break; + } + + return model()->index(qBound(0, ret, model()->rowCount()), 0); +} + +int GroupedIconView::IndexAboveOrBelow(int index, int d) const { + const QRect orig_rect(visual_rects_[index]); + + while (index >= 0 && index < visual_rects_.count()) { + const QRect rect(visual_rects_[index]); + const QPoint center(rect.center()); + + if ((center.y() <= orig_rect.top() || center.y() >= orig_rect.bottom()) && + center.x() >= orig_rect.left() && + center.x() <= orig_rect.right()) { + return index; + } + + index += d; + } + + return index; +} diff --git a/src/widgets/groupediconview.h b/src/widgets/groupediconview.h new file mode 100644 index 000000000..d1ed52876 --- /dev/null +++ b/src/widgets/groupediconview.h @@ -0,0 +1,108 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef GROUPEDICONVIEW_H +#define GROUPEDICONVIEW_H + +#include + +class QSortFilterProxyModel; + + +class GroupedIconView : public QListView { + Q_OBJECT + + // Vertical space separating a header from the items above and below it. + Q_PROPERTY(int header_spacing READ header_spacing WRITE set_header_spacing) + + // Horizontal space separating a header from the left and right edges of the widget. + Q_PROPERTY(int header_indent READ header_indent WRITE set_header_indent) + + // Horizontal space separating an item from the left and right edges of the widget. + Q_PROPERTY(int item_indent READ item_indent WRITE set_item_indent) + + // The text of each group's header. Must contain "%1". + Q_PROPERTY(QString header_text READ header_text WRITE set_header_text); + +public: + GroupedIconView(QWidget* parent = 0); + + enum Role { + Role_Group = 1158300, + }; + + int header_spacing() const { return header_spacing_; } + int header_indent() const { return header_indent_; } + int item_indent() const { return item_indent_; } + const QString& header_text() const { return header_text_;} + + void set_header_spacing(int value) { header_spacing_ = value; } + void set_header_indent(int value) { header_indent_ = value; } + void set_item_indent(int value) { item_indent_ = value; } + void set_header_text(const QString& value) { header_text_ = value; } + + // QAbstractItemView + QModelIndex moveCursor(CursorAction action, Qt::KeyboardModifiers modifiers); + void setModel(QAbstractItemModel* model); + +protected: + virtual int header_height() const; + virtual void DrawHeader(const QModelIndex& first_child, const QRect& rect, + QPainter* painter); + + // QWidget + void paintEvent(QPaintEvent* e); + void resizeEvent(QResizeEvent* e); + + // QAbstractItemView + QModelIndex indexAt(const QPoint& p) const; + void rowsInserted(const QModelIndex& parent, int start, int end); + void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command); + QRect visualRect(const QModelIndex& index) const; + QRegion visualRegionForSelection(const QItemSelection& selection) const; + +private slots: + void LayoutItems(); + +private: + static const int kBarThickness; + static const int kBarMarginTop; + + struct Header { + int y; + int first_row; + QString text; + }; + + // Returns the items that are wholly or partially inside the rect. + QVector IntersectingItems(const QRect& rect) const; + + // Returns the index of the item above (d=-1) or below (d=+1) the given item. + int IndexAboveOrBelow(int index, int d) const; + + QSortFilterProxyModel* proxy_model_; + QVector visual_rects_; + QVector
headers_; + + const int default_header_height_; + int header_spacing_; + int header_indent_; + int item_indent_; + QString header_text_; +}; + +#endif // GROUPEDICONVIEW_H diff --git a/src/widgets/kcategorizedsortfilterproxymodel.cpp b/src/widgets/kcategorizedsortfilterproxymodel.cpp deleted file mode 100644 index 0cb93acd3..000000000 --- a/src/widgets/kcategorizedsortfilterproxymodel.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López - * Copyright (C) 2007 John Tapsell - * - * 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 "kcategorizedsortfilterproxymodel.h" -#include "kcategorizedsortfilterproxymodel_p.h" - -#include - -#include -#include -#include - -KCategorizedSortFilterProxyModel::KCategorizedSortFilterProxyModel(QObject *parent) - : QSortFilterProxyModel(parent) - , d(new Private()) - -{ -} - -KCategorizedSortFilterProxyModel::~KCategorizedSortFilterProxyModel() -{ - delete d; -} - -void KCategorizedSortFilterProxyModel::sort(int column, Qt::SortOrder order) -{ - d->sortColumn = column; - d->sortOrder = order; - - QSortFilterProxyModel::sort(column, order); -} - -bool KCategorizedSortFilterProxyModel::isCategorizedModel() const -{ - return d->categorizedModel; -} - -void KCategorizedSortFilterProxyModel::setCategorizedModel(bool categorizedModel) -{ - if (categorizedModel == d->categorizedModel) - { - return; - } - - d->categorizedModel = categorizedModel; - - invalidate(); -} - -int KCategorizedSortFilterProxyModel::sortColumn() const -{ - return d->sortColumn; -} - -Qt::SortOrder KCategorizedSortFilterProxyModel::sortOrder() const -{ - return d->sortOrder; -} - -void KCategorizedSortFilterProxyModel::setSortCategoriesByNaturalComparison(bool sortCategoriesByNaturalComparison) -{ - if (sortCategoriesByNaturalComparison == d->sortCategoriesByNaturalComparison) - { - return; - } - - d->sortCategoriesByNaturalComparison = sortCategoriesByNaturalComparison; - - invalidate(); -} - -bool KCategorizedSortFilterProxyModel::sortCategoriesByNaturalComparison() const -{ - return d->sortCategoriesByNaturalComparison; -} - -bool KCategorizedSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - if (d->categorizedModel) - { - int compare = compareCategories(left, right); - - if (compare > 0) // left is greater than right - { - return false; - } - else if (compare < 0) // left is less than right - { - return true; - } - } - - return subSortLessThan(left, right); -} - -bool KCategorizedSortFilterProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const -{ - return QSortFilterProxyModel::lessThan(left, right); -} - -int KCategorizedSortFilterProxyModel::compareCategories(const QModelIndex &left, const QModelIndex &right) const -{ - QVariant l = (left.model() ? left.model()->data(left, CategorySortRole) : QVariant()); - QVariant r = (right.model() ? right.model()->data(right, CategorySortRole) : QVariant()); - - Q_ASSERT(l.isValid()); - Q_ASSERT(r.isValid()); - Q_ASSERT(l.type() == r.type()); - - if (l.type() == QVariant::String) - { - QString lstr = l.toString(); - QString rstr = r.toString(); - - if (d->sortCategoriesByNaturalComparison) - { - return QString::localeAwareCompare(lstr, rstr); - } - else - { - if (lstr < rstr) - { - return -1; - } - - if (lstr > rstr) - { - return 1; - } - - return 0; - } - } - - qlonglong lint = l.toLongLong(); - qlonglong rint = r.toLongLong(); - - if (lint < rint) - { - return -1; - } - - if (lint > rint) - { - return 1; - } - - return 0; -} diff --git a/src/widgets/kcategorizedsortfilterproxymodel.h b/src/widgets/kcategorizedsortfilterproxymodel.h deleted file mode 100644 index de356c3f4..000000000 --- a/src/widgets/kcategorizedsortfilterproxymodel.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López - * Copyright (C) 2007 John Tapsell - * - * 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 KCATEGORIZEDSORTFILTERPROXYMODEL_H -#define KCATEGORIZEDSORTFILTERPROXYMODEL_H - -#include - -class QItemSelection; - - -/** - * This class lets you categorize a view. It is meant to be used along with - * KCategorizedView class. - * - * In general terms all you need to do is to reimplement subSortLessThan() and - * compareCategories() methods. In order to make categorization work, you need - * to also call setCategorizedModel() class to enable it, since the categorization - * is disabled by default. - * - * @see KCategorizedView - * - * @author Rafael Fernández López - */ -class KCategorizedSortFilterProxyModel - : public QSortFilterProxyModel -{ -public: - enum AdditionalRoles { - // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) - // to define additional roles. - CategoryDisplayRole = 0x17CE990A, ///< This role is used for asking the category to a given index - - CategorySortRole = 0x27857E60 ///< This role is used for sorting categories. You can return a - ///< string or a long long value. Strings will be sorted alphabetically - ///< while long long will be sorted by their value. Please note that this - ///< value won't be shown on the view, is only for sorting purposes. What will - ///< be shown as "Category" on the view will be asked with the role - ///< CategoryDisplayRole. - }; - - KCategorizedSortFilterProxyModel(QObject *parent = 0); - virtual ~KCategorizedSortFilterProxyModel(); - - /** - * Overridden from QSortFilterProxyModel. Sorts the source model using - * @p column for the given @p order. - */ - virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); - - /** - * @return whether the model is categorized or not. Disabled by default. - */ - bool isCategorizedModel() const; - - /** - * Enables or disables the categorization feature. - * - * @param categorizedModel whether to enable or disable the categorization feature. - */ - void setCategorizedModel(bool categorizedModel); - - /** - * @return the column being used for sorting. - */ - int sortColumn() const; - - /** - * @return the sort order being used for sorting. - */ - Qt::SortOrder sortOrder() const; - - /** - * Set if the sorting using CategorySortRole will use a natural comparison - * in the case that strings were returned. If enabled, QString::localeAwareCompare - * will be used for sorting. - * - * @param sortCategoriesByNaturalComparison whether to sort using a natural comparison or not. - */ - void setSortCategoriesByNaturalComparison(bool sortCategoriesByNaturalComparison); - - /** - * @return whether it is being used a natural comparison for sorting. Enabled by default. - */ - bool sortCategoriesByNaturalComparison() const; - -protected: - /** - * Overridden from QSortFilterProxyModel. If you are subclassing - * KCategorizedSortFilterProxyModel, you will probably not need to reimplement this - * method. - * - * It calls compareCategories() to sort by category. If the both items are in the - * same category (i.e. compareCategories returns 0), then subSortLessThan is called. - * - * @return Returns true if the item @p left is less than the item @p right when sorting. - * - * @warning You usually won't need to reimplement this method when subclassing - * from KCategorizedSortFilterProxyModel. - */ - virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - - /** - * This method has a similar purpose as lessThan() has on QSortFilterProxyModel. - * It is used for sorting items that are in the same category. - * - * @return Returns true if the item @p left is less than the item @p right when sorting. - */ - virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const; - - /** - * This method compares the category of the @p left index with the category - * of the @p right index. - * - * Internally and if not reimplemented, this method will ask for @p left and - * @p right models for role CategorySortRole. In order to correctly sort - * categories, the data() metod of the model should return a qlonglong (or numeric) value, or - * a QString object. QString objects will be sorted with QString::localeAwareCompare if - * sortCategoriesByNaturalComparison() is true. - * - * @note Please have present that: - * QString(QChar(QChar::ObjectReplacementCharacter)) > - * QString(QChar(QChar::ReplacementCharacter)) > - * [ all possible strings ] > - * QString(); - * - * This means that QString() will be sorted the first one, while - * QString(QChar(QChar::ObjectReplacementCharacter)) and - * QString(QChar(QChar::ReplacementCharacter)) will be sorted in last - * position. - * - * @warning Please note that data() method of the model should return always - * information of the same type. If you return a QString for an index, - * you should return always QStrings for all indexes for role CategorySortRole - * in order to correctly sort categories. You can't mix by returning - * a QString for one index, and a qlonglong for other. - * - * @note If you need a more complex layout, you will have to reimplement this - * method. - * - * @return A negative value if the category of @p left should be placed before the - * category of @p right. 0 if @p left and @p right are on the same category, and - * a positive value if the category of @p left should be placed after the - * category of @p right. - */ - virtual int compareCategories(const QModelIndex &left, const QModelIndex &right) const; - -private: - class Private; - Private *const d; -}; - - -#endif // KCATEGORIZEDSORTFILTERPROXYMODEL_H diff --git a/src/widgets/kcategorizedsortfilterproxymodel_p.h b/src/widgets/kcategorizedsortfilterproxymodel_p.h deleted file mode 100644 index eb1a67b33..000000000 --- a/src/widgets/kcategorizedsortfilterproxymodel_p.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López - * Copyright (C) 2007 John Tapsell - * - * 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 KCATEGORIZEDSORTFILTERPROXYMODEL_P_H -#define KCATEGORIZEDSORTFILTERPROXYMODEL_P_H - -class KCategorizedSortFilterProxyModel; - -class KCategorizedSortFilterProxyModel::Private -{ -public: - Private() - : sortColumn(0) - , sortOrder(Qt::AscendingOrder) - , categorizedModel(false) - , sortCategoriesByNaturalComparison(true) - { - } - - ~Private() - { - } - - int sortColumn; - Qt::SortOrder sortOrder; - bool categorizedModel; - bool sortCategoriesByNaturalComparison; -}; - -#endif diff --git a/src/widgets/kcategorizedview-without-kde.patch b/src/widgets/kcategorizedview-without-kde.patch deleted file mode 100644 index f8722ef46..000000000 --- a/src/widgets/kcategorizedview-without-kde.patch +++ /dev/null @@ -1,263 +0,0 @@ -diff --git a/src/widgets/kcategorizedsortfilterproxymodel.cpp b/src/widgets/kcategorizedsortfilterproxymodel.cpp -index c433c3d..0cb93ac 100644 ---- a/src/widgets/kcategorizedsortfilterproxymodel.cpp -+++ b/src/widgets/kcategorizedsortfilterproxymodel.cpp -@@ -28,8 +28,6 @@ - #include - #include - --#include -- - KCategorizedSortFilterProxyModel::KCategorizedSortFilterProxyModel(QObject *parent) - : QSortFilterProxyModel(parent) - , d(new Private()) -@@ -94,14 +92,6 @@ bool KCategorizedSortFilterProxyModel::sortCategoriesByNaturalComparison() const - return d->sortCategoriesByNaturalComparison; - } - --#ifndef KDE_NO_DEPRECATED --int KCategorizedSortFilterProxyModel::naturalCompare(const QString &a, -- const QString &b) --{ -- return KStringHandler::naturalCompare(a, b); --} --#endif -- - bool KCategorizedSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const - { - if (d->categorizedModel) -@@ -142,7 +132,7 @@ int KCategorizedSortFilterProxyModel::compareCategories(const QModelIndex &left, - - if (d->sortCategoriesByNaturalComparison) - { -- return KStringHandler::naturalCompare(lstr, rstr); -+ return QString::localeAwareCompare(lstr, rstr); - } - else - { -diff --git a/src/widgets/kcategorizedsortfilterproxymodel.h b/src/widgets/kcategorizedsortfilterproxymodel.h -index a3146dc..de356c3 100644 ---- a/src/widgets/kcategorizedsortfilterproxymodel.h -+++ b/src/widgets/kcategorizedsortfilterproxymodel.h -@@ -24,8 +24,6 @@ - - #include - --#include -- - class QItemSelection; - - -@@ -42,7 +40,7 @@ class QItemSelection; - * - * @author Rafael Fernández López - */ --class KDEUI_EXPORT KCategorizedSortFilterProxyModel -+class KCategorizedSortFilterProxyModel - : public QSortFilterProxyModel - { - public: -@@ -104,17 +102,6 @@ public: - */ - bool sortCategoriesByNaturalComparison() const; - -- /** -- * Does a natural comparing of the strings. A negative value is returned if \a a -- * is smaller than \a b. A positive value is returned if \a a is greater than \a b. 0 -- * is returned if both values are equal. -- * @deprecated -- * Use KStringHandler::naturalCompare() instead. -- */ --#ifndef KDE_NO_DEPRECATED -- KDE_DEPRECATED static int naturalCompare(const QString &a, const QString &b); --#endif -- - protected: - /** - * Overridden from QSortFilterProxyModel. If you are subclassing -diff --git a/src/widgets/kcategorizedview.cpp b/src/widgets/kcategorizedview.cpp -index 46a1cde..7e99468 100644 ---- a/src/widgets/kcategorizedview.cpp -+++ b/src/widgets/kcategorizedview.cpp -@@ -33,7 +33,6 @@ - #include "kcategorizedview_p.h" - - #include // trunc on C99 compliant systems --#include // trunc for not C99 compliant systems - - #include - #include -@@ -1455,28 +1454,6 @@ void KCategorizedView::rowsInserted(const QModelIndex &parent, - d->rowsInserted(parent, start, end); - } - --#ifndef KDE_NO_DEPRECATED --void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent, -- int start, -- int end) --{ -- Q_UNUSED(parent); -- Q_UNUSED(start); -- Q_UNUSED(end); --} --#endif -- --#ifndef KDE_NO_DEPRECATED --void KCategorizedView::rowsRemoved(const QModelIndex &parent, -- int start, -- int end) --{ -- Q_UNUSED(parent); -- Q_UNUSED(start); -- Q_UNUSED(end); --} --#endif -- - void KCategorizedView::slotLayoutChanged() - { - if (!d->isCategorized()) { -@@ -1492,5 +1469,3 @@ void KCategorizedView::slotLayoutChanged() - } - - //END: Public part -- --#include "kcategorizedview.moc" -diff --git a/src/widgets/kcategorizedview.h b/src/widgets/kcategorizedview.h -index b03744e..8f5ad9a 100644 ---- a/src/widgets/kcategorizedview.h -+++ b/src/widgets/kcategorizedview.h -@@ -23,8 +23,6 @@ - - #include - --#include -- - class KCategoryDrawer; - class KCategoryDrawerV2; - -@@ -76,7 +74,7 @@ class KCategoryDrawerV2; - * - * @author Rafael Fernández López - */ --class KDEUI_EXPORT KCategorizedView -+class KCategorizedView - : public QListView - { - Q_OBJECT -@@ -310,26 +308,6 @@ protected: - protected Q_SLOTS: - /** - * @internal -- * @warning Deprecated since 4.4. -- */ --#ifndef KDE_NO_DEPRECATED -- virtual KDE_DEPRECATED void rowsInsertedArtifficial(const QModelIndex &parent, -- int start, -- int end); --#endif -- -- /** -- * @internal -- * @warning Deprecated since 4.4. -- */ --#ifndef KDE_NO_DEPRECATED -- virtual KDE_DEPRECATED void rowsRemoved(const QModelIndex &parent, -- int start, -- int end); --#endif -- -- /** -- * @internal - * Reposition items as needed. - */ - virtual void slotLayoutChanged(); -@@ -341,4 +319,6 @@ private: - Q_PRIVATE_SLOT(d, void _k_slotCollapseOrExpandClicked(QModelIndex)) - }; - -+#include "kcategorizedview_p.h" -+ - #endif // KCATEGORIZEDVIEW_H -diff --git a/src/widgets/kcategorydrawer.cpp b/src/widgets/kcategorydrawer.cpp -index 2747e5b..5f5ce78 100644 ---- a/src/widgets/kcategorydrawer.cpp -+++ b/src/widgets/kcategorydrawer.cpp -@@ -24,7 +24,6 @@ - #include - #include - --#include - #include - #include - -@@ -280,5 +279,3 @@ void KCategoryDrawerV3::mouseButtonDoubleClicked(const QModelIndex&, const QRect - void KCategoryDrawerV3::mouseLeft(const QModelIndex&, const QRect&) - { - } -- --#include "kcategorydrawer.moc" -diff --git a/src/widgets/kcategorydrawer.h b/src/widgets/kcategorydrawer.h -index c24a0b8..d69b1b9 100644 ---- a/src/widgets/kcategorydrawer.h -+++ b/src/widgets/kcategorydrawer.h -@@ -21,8 +21,6 @@ - #ifndef KCATEGORYDRAWER_H - #define KCATEGORYDRAWER_H - --#include -- - #include - #include - -@@ -40,10 +38,10 @@ class KCategorizedView; - * - * @warning Please use KCategoryDrawerV3 instead - */ --class KDEUI_EXPORT KCategoryDrawer -+class KCategoryDrawer - { - public: -- KDE_DEPRECATED KCategoryDrawer(); -+ KCategoryDrawer(); - - virtual ~KCategoryDrawer(); - -@@ -110,23 +108,23 @@ private: - * - * @warning Please use KCategoryDrawerV3 instead - */ --class KDEUI_EXPORT KCategoryDrawerV2 -+class KCategoryDrawerV2 - : public QObject - , public KCategoryDrawer - { - Q_OBJECT - - public: -- KDE_DEPRECATED KCategoryDrawerV2(QObject *parent = 0); -+ KCategoryDrawerV2(QObject *parent = 0); - virtual ~KCategoryDrawerV2(); - -- KDE_DEPRECATED virtual void mouseButtonPressed(const QModelIndex &index, QMouseEvent *event); -+ virtual void mouseButtonPressed(const QModelIndex &index, QMouseEvent *event); - -- KDE_DEPRECATED virtual void mouseButtonReleased(const QModelIndex &index, QMouseEvent *event); -+ virtual void mouseButtonReleased(const QModelIndex &index, QMouseEvent *event); - -- KDE_DEPRECATED virtual void mouseButtonMoved(const QModelIndex &index, QMouseEvent *event); -+ virtual void mouseButtonMoved(const QModelIndex &index, QMouseEvent *event); - -- KDE_DEPRECATED virtual void mouseButtonDoubleClicked(const QModelIndex &index, QMouseEvent *event); -+ virtual void mouseButtonDoubleClicked(const QModelIndex &index, QMouseEvent *event); - - Q_SIGNALS: - /** -@@ -147,7 +145,7 @@ Q_SIGNALS: - /** - * @since 4.5 - */ --class KDEUI_EXPORT KCategoryDrawerV3 -+class KCategoryDrawerV3 - : public KCategoryDrawerV2 - { - friend class KCategorizedView; diff --git a/src/widgets/kcategorizedview.cpp b/src/widgets/kcategorizedview.cpp deleted file mode 100644 index 9308c890d..000000000 --- a/src/widgets/kcategorizedview.cpp +++ /dev/null @@ -1,1470 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * 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. - */ - -/** - * IMPLEMENTATION NOTES: - * - * QListView::setRowHidden() and QListView::isRowHidden() are not taken into - * account. This methods should actually not exist. This effect should be handled - * by an hypothetical QSortFilterProxyModel which filters out the desired rows. - * - * In case this needs to be implemented, contact me, but I consider this a faulty - * design. - */ - -#include "kcategorizedview.h" -#include "kcategorizedview_p.h" - -#include // trunc on C99 compliant systems - -#include -#include -#include - -#include "kcategorydrawer.h" -#include "kcategorizedsortfilterproxymodel.h" - -//BEGIN: Private part - -struct KCategorizedView::Private::Item -{ - Item() - : topLeft(QPoint()) - , size(QSize()) - { - } - - QPoint topLeft; - QSize size; -}; - -struct KCategorizedView::Private::Block -{ - Block() - : topLeft(QPoint()) - , height(-1) - , firstIndex(QModelIndex()) - , quarantineStart(QModelIndex()) - , items(QList()) - , outOfQuarantine(false) - , alternate(false) - , collapsed(false) - { - } - - bool operator!=(const Block &rhs) const - { - return firstIndex != rhs.firstIndex; - } - - static bool lessThan(const Block &left, const Block &right) - { - Q_ASSERT(left.firstIndex.isValid()); - Q_ASSERT(right.firstIndex.isValid()); - return left.firstIndex.row() < right.firstIndex.row(); - } - - QPoint topLeft; - int height; - QPersistentModelIndex firstIndex; - // if we have n elements on this block, and we inserted an element at position i. The quarantine - // will start at index (i, column, parent). This means that for all elements j where i <= j <= n, the - // visual rect position of item j will have to be recomputed (cannot use the cached point). The quarantine - // will only affect the current block, since the rest of blocks can be affected only in the way - // that the whole block will have different offset, but items will keep the same relative position - // in terms of their parent blocks. - QPersistentModelIndex quarantineStart; - QList items; - - // this affects the whole block, not items separately. items contain the topLeft point relative - // to the block. Because of insertions or removals a whole block can be moved, so the whole block - // will enter in quarantine, what is faster than moving all items in absolute terms. - bool outOfQuarantine; - - // should we alternate its color ? is just a hint, could not be used - bool alternate; - bool collapsed; -}; - -KCategorizedView::Private::Private(KCategorizedView *q) - : q(q) - , proxyModel(0) - , categoryDrawer(0) - , categoryDrawerV2(0) - , categoryDrawerV3(0) - , categorySpacing(5) - , alternatingBlockColors(false) - , collapsibleBlocks(false) - , hoveredBlock(new Block()) - , hoveredIndex(QModelIndex()) - , pressedPosition(QPoint()) - , rubberBandRect(QRect()) -{ -} - -KCategorizedView::Private::~Private() -{ - delete hoveredBlock; -} - -bool KCategorizedView::Private::isCategorized() const -{ - return proxyModel && categoryDrawer && proxyModel->isCategorizedModel(); -} - -QStyleOptionViewItemV4 KCategorizedView::Private::blockRect(const QModelIndex &representative) -{ - QStyleOptionViewItemV4 option(q->viewOptions()); - const int height = categoryDrawer->categoryHeight(representative, option); - const QString categoryDisplay = representative.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); - QPoint pos = blockPosition(categoryDisplay); - pos.ry() -= height; - option.rect.setTopLeft(pos); - option.rect.setWidth(viewportWidth() + categoryDrawer->leftMargin() + categoryDrawer->rightMargin()); - option.rect.setHeight(height + blockHeight(categoryDisplay)); - option.rect = mapToViewport(option.rect); - - return option; -} - -QPair KCategorizedView::Private::intersectingIndexesWithRect(const QRect &_rect) const -{ - const int rowCount = proxyModel->rowCount(); - - const QRect rect = _rect.normalized(); - - // binary search to find out the top border - int bottom = 0; - int top = rowCount - 1; - while (bottom <= top) { - const int middle = (bottom + top) / 2; - const QModelIndex index = proxyModel->index(middle, q->modelColumn(), q->rootIndex()); - QRect itemRect = q->visualRect(index); - const int verticalOff = q->verticalOffset(); - const int horizontalOff = q->horizontalOffset(); - itemRect.topLeft().ry() += verticalOff; - itemRect.topLeft().rx() += horizontalOff; - itemRect.bottomRight().ry() += verticalOff; - itemRect.bottomRight().rx() += horizontalOff; - if (itemRect.bottomRight().y() <= rect.topLeft().y()) { - bottom = middle + 1; - } else { - top = middle - 1; - } - } - - const QModelIndex bottomIndex = proxyModel->index(bottom, q->modelColumn(), q->rootIndex()); - - // binary search to find out the bottom border - bottom = 0; - top = rowCount - 1; - while (bottom <= top) { - const int middle = (bottom + top) / 2; - const QModelIndex index = proxyModel->index(middle, q->modelColumn(), q->rootIndex()); - QRect itemRect = q->visualRect(index); - const int verticalOff = q->verticalOffset(); - const int horizontalOff = q->horizontalOffset(); - itemRect.topLeft().ry() += verticalOff; - itemRect.topLeft().rx() += horizontalOff; - itemRect.bottomRight().ry() += verticalOff; - itemRect.bottomRight().rx() += horizontalOff; - if (itemRect.topLeft().y() <= rect.bottomRight().y()) { - bottom = middle + 1; - } else { - top = middle - 1; - } - } - - const QModelIndex topIndex = proxyModel->index(top, q->modelColumn(), q->rootIndex()); - - return qMakePair(bottomIndex, topIndex); -} - -QPoint KCategorizedView::Private::blockPosition(const QString &category) -{ - Block &block = blocks[category]; - - if (block.outOfQuarantine && !block.topLeft.isNull()) { - return block.topLeft; - } - - QPoint res(categorySpacing, 0); - - const QModelIndex index = block.firstIndex; - - for (QHash::Iterator it = blocks.begin(); it != blocks.end(); ++it) { - Block &block = *it; - const QModelIndex categoryIndex = block.firstIndex; - if (index.row() < categoryIndex.row()) { - continue; - } - res.ry() += categoryDrawer->categoryHeight(categoryIndex, q->viewOptions()) + categorySpacing; - if (index.row() == categoryIndex.row()) { - continue; - } - res.ry() += blockHeight(it.key()); - } - - block.outOfQuarantine = true; - block.topLeft = res; - - return res; -} - -int KCategorizedView::Private::blockHeight(const QString &category) -{ - Block &block = blocks[category]; - - if (block.collapsed) { - return 0; - } - - if (block.height > -1) { - return block.height; - } - - const QModelIndex firstIndex = block.firstIndex; - const QModelIndex lastIndex = proxyModel->index(firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex()); - const QRect topLeft = q->visualRect(firstIndex); - QRect bottomRight = q->visualRect(lastIndex); - - if (hasGrid()) { - bottomRight.setHeight(qMax(bottomRight.height(), q->gridSize().height())); - } else { - if (!q->uniformItemSizes()) { - bottomRight.setHeight(highestElementInLastRow(block) + q->spacing() * 2); - } - } - - const int height = bottomRight.bottomRight().y() - topLeft.topLeft().y() + 1; - block.height = height; - - return height; -} - -int KCategorizedView::Private::viewportWidth() const -{ - return q->viewport()->width() - categorySpacing * 2 - categoryDrawer->leftMargin() - categoryDrawer->rightMargin(); -} - -void KCategorizedView::Private::regenerateAllElements() -{ - for (QHash::Iterator it = blocks.begin(); it != blocks.end(); ++it) { - Block &block = *it; - block.outOfQuarantine = false; - block.quarantineStart = block.firstIndex; - block.height = -1; - } -} - -void KCategorizedView::Private::rowsInserted(const QModelIndex &parent, int start, int end) -{ - if (!isCategorized()) { - return; - } - - for (int i = start; i <= end; ++i) { - const QModelIndex index = proxyModel->index(i, q->modelColumn(), parent); - - Q_ASSERT(index.isValid()); - - const QString category = categoryForIndex(index); - - Block &block = blocks[category]; - - //BEGIN: update firstIndex - // save as firstIndex in block if - // - it forced the category creation (first element on this category) - // - it is before the first row on that category - const QModelIndex firstIndex = block.firstIndex; - if (!firstIndex.isValid() || index.row() < firstIndex.row()) { - block.firstIndex = index; - } - //END: update firstIndex - - Q_ASSERT(block.firstIndex.isValid()); - - const int firstIndexRow = block.firstIndex.row(); - - block.items.insert(index.row() - firstIndexRow, Private::Item()); - block.height = -1; - - q->visualRect(index); - q->viewport()->update(); - } - - //BEGIN: update the items that are in quarantine in affected categories - { - const QModelIndex lastIndex = proxyModel->index(end, q->modelColumn(), parent); - const QString category = categoryForIndex(lastIndex); - Private::Block &block = blocks[category]; - block.quarantineStart = block.firstIndex; - } - //END: update the items that are in quarantine in affected categories - - //BEGIN: mark as in quarantine those categories that are under the affected ones - { - const QModelIndex firstIndex = proxyModel->index(start, q->modelColumn(), parent); - const QString category = categoryForIndex(firstIndex); - const QModelIndex firstAffectedCategory = blocks[category].firstIndex; - //BEGIN: order for marking as alternate those blocks that are alternate - QList blockList = blocks.values(); - qSort(blockList.begin(), blockList.end(), Block::lessThan); - QList firstIndexesRows; - foreach (const Block &block, blockList) { - firstIndexesRows << block.firstIndex.row(); - } - //END: order for marking as alternate those blocks that are alternate - for (QHash::Iterator it = blocks.begin(); it != blocks.end(); ++it) { - Private::Block &block = *it; - if (block.firstIndex.row() > firstAffectedCategory.row()) { - block.outOfQuarantine = false; - block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2; - } else if (block.firstIndex.row() == firstAffectedCategory.row()) { - block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2; - } - } - } - //END: mark as in quarantine those categories that are under the affected ones -} - -QRect KCategorizedView::Private::mapToViewport(const QRect &rect) const -{ - const int dx = -q->horizontalOffset(); - const int dy = -q->verticalOffset(); - return rect.adjusted(dx, dy, dx, dy); -} - -QRect KCategorizedView::Private::mapFromViewport(const QRect &rect) const -{ - const int dx = q->horizontalOffset(); - const int dy = q->verticalOffset(); - return rect.adjusted(dx, dy, dx, dy); -} - -int KCategorizedView::Private::highestElementInLastRow(const Block &block) const -{ - //Find the highest element in the last row - const QModelIndex lastIndex = proxyModel->index(block.firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex()); - const QRect prevRect = q->visualRect(lastIndex); - int res = prevRect.height(); - QModelIndex prevIndex = proxyModel->index(lastIndex.row() - 1, q->modelColumn(), q->rootIndex()); - if (!prevIndex.isValid()) { - return res; - } - Q_FOREVER { - const QRect tempRect = q->visualRect(prevIndex); - if (tempRect.topLeft().y() < prevRect.topLeft().y()) { - break; - } - res = qMax(res, tempRect.height()); - if (prevIndex == block.firstIndex) { - break; - } - prevIndex = proxyModel->index(prevIndex.row() - 1, q->modelColumn(), q->rootIndex()); - } - - return res; -} - -bool KCategorizedView::Private::hasGrid() const -{ - const QSize gridSize = q->gridSize(); - return gridSize.isValid() && !gridSize.isNull(); -} - -QString KCategorizedView::Private::categoryForIndex(const QModelIndex &index) const -{ - const QModelIndex categoryIndex = index.model()->index(index.row(), proxyModel->sortColumn(), index.parent()); - return categoryIndex.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); -} - -void KCategorizedView::Private::leftToRightVisualRect(const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos) const -{ - const int firstIndexRow = block.firstIndex.row(); - - if (hasGrid()) { - const int relativeRow = index.row() - firstIndexRow; - const int maxItemsPerRow = qMax(viewportWidth() / q->gridSize().width(), 1); - if (q->layoutDirection() == Qt::LeftToRight) { - item.topLeft.rx() = (relativeRow % maxItemsPerRow) * q->gridSize().width() + blockPos.x() + categoryDrawer->leftMargin(); - } else { - item.topLeft.rx() = viewportWidth() - ((relativeRow % maxItemsPerRow) + 1) * q->gridSize().width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = (relativeRow / maxItemsPerRow) * q->gridSize().height(); - } else { - if (q->uniformItemSizes()) { - const int relativeRow = index.row() - firstIndexRow; - const QSize itemSize = q->sizeHintForIndex(index); - const int maxItemsPerRow = qMax((viewportWidth() - q->spacing()) / (itemSize.width() + q->spacing()), 1); - if (q->layoutDirection() == Qt::LeftToRight) { - item.topLeft.rx() = (relativeRow % maxItemsPerRow) * itemSize.width() + blockPos.x() + categoryDrawer->leftMargin(); - } else { - item.topLeft.rx() = viewportWidth() - (relativeRow % maxItemsPerRow) * itemSize.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = (relativeRow / maxItemsPerRow) * itemSize.height(); - } else { - const QSize currSize = q->sizeHintForIndex(index); - if (index != block.firstIndex) { - const int viewportW = viewportWidth() - q->spacing(); - QModelIndex prevIndex = proxyModel->index(index.row() - 1, q->modelColumn(), q->rootIndex()); - QRect prevRect = q->visualRect(prevIndex); - prevRect = mapFromViewport(prevRect); - if ((prevRect.bottomRight().x() + 1) + currSize.width() - blockPos.x() + q->spacing() > viewportW) { - // we have to check the whole previous row, and see which one was the - // highest. - Q_FOREVER { - prevIndex = proxyModel->index(prevIndex.row() - 1, q->modelColumn(), q->rootIndex()); - const QRect tempRect = q->visualRect(prevIndex); - if (tempRect.topLeft().y() < prevRect.topLeft().y()) { - break; - } - if (tempRect.bottomRight().y() > prevRect.bottomRight().y()) { - prevRect = tempRect; - } - if (prevIndex == block.firstIndex) { - break; - } - } - if (q->layoutDirection() == Qt::LeftToRight) { - item.topLeft.rx() = categoryDrawer->leftMargin() + blockPos.x() + q->spacing(); - } else { - item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = (prevRect.bottomRight().y() + 1) + q->spacing() - blockPos.y(); - } else { - if (q->layoutDirection() == Qt::LeftToRight) { - item.topLeft.rx() = (prevRect.bottomRight().x() + 1) + q->spacing(); - } else { - item.topLeft.rx() = (prevRect.bottomLeft().x() - 1) - q->spacing() - item.size.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = prevRect.topLeft().y() - blockPos.y(); - } - } else { - if (q->layoutDirection() == Qt::LeftToRight) { - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); - } else { - item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = q->spacing(); - } - } - } - item.size = q->sizeHintForIndex(index); -} - -void KCategorizedView::Private::topToBottomVisualRect(const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos) const -{ - const int firstIndexRow = block.firstIndex.row(); - - if (hasGrid()) { - const int relativeRow = index.row() - firstIndexRow; - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin(); - item.topLeft.ry() = relativeRow * q->gridSize().height(); - } else { - if (q->uniformItemSizes()) { - const int relativeRow = index.row() - firstIndexRow; - const QSize itemSize = q->sizeHintForIndex(index); - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin(); - item.topLeft.ry() = relativeRow * itemSize.height(); - } else { - if (index != block.firstIndex) { - QModelIndex prevIndex = proxyModel->index(index.row() - 1, q->modelColumn(), q->rootIndex()); - QRect prevRect = q->visualRect(prevIndex); - prevRect = mapFromViewport(prevRect); - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); - item.topLeft.ry() = (prevRect.bottomRight().y() + 1) + q->spacing() - blockPos.y(); - } else { - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); - item.topLeft.ry() = q->spacing(); - } - } - } - item.size = q->sizeHintForIndex(index); - item.size.setWidth(viewportWidth()); -} - -void KCategorizedView::Private::_k_slotCollapseOrExpandClicked(QModelIndex) -{ -} - -//END: Private part - -//BEGIN: Public part - -KCategorizedView::KCategorizedView(QWidget *parent) - : QListView(parent) - , d(new Private(this)) -{ -} - -KCategorizedView::~KCategorizedView() -{ - delete d; -} - -void KCategorizedView::setModel(QAbstractItemModel *model) -{ - if (d->proxyModel == model) { - return; - } - - d->blocks.clear(); - - if (d->proxyModel) { - disconnect(d->proxyModel, SIGNAL(layoutChanged()), this, SLOT(slotLayoutChanged())); - } - - d->proxyModel = dynamic_cast(model); - - if (d->proxyModel) { - connect(d->proxyModel, SIGNAL(layoutChanged()), this, SLOT(slotLayoutChanged())); - } - - QListView::setModel(model); - - // if the model already had information inserted, update our data structures to it - if (model->rowCount()) { - slotLayoutChanged(); - } -} - -void KCategorizedView::setGridSize(const QSize &size) -{ - setGridSizeOwn(size); -} - -void KCategorizedView::setGridSizeOwn(const QSize &size) -{ - d->regenerateAllElements(); - QListView::setGridSize(size); -} - -QRect KCategorizedView::visualRect(const QModelIndex &index) const -{ - if (!d->isCategorized()) { - return QListView::visualRect(index); - } - - if (!index.isValid()) { - return QRect(); - } - - const QString category = d->categoryForIndex(index); - - if (!d->blocks.contains(category)) { - return QRect(); - } - - Private::Block &block = d->blocks[category]; - const int firstIndexRow = block.firstIndex.row(); - - Q_ASSERT(block.firstIndex.isValid()); - - if (index.row() - firstIndexRow < 0 || index.row() - firstIndexRow >= block.items.count()) { - return QRect(); - } - - const QPoint blockPos = d->blockPosition(category); - - Private::Item &ritem = block.items[index.row() - firstIndexRow]; - - if (ritem.topLeft.isNull() || (block.quarantineStart.isValid() && - index.row() >= block.quarantineStart.row())) { - if (flow() == LeftToRight) { - d->leftToRightVisualRect(index, ritem, block, blockPos); - } else { - d->topToBottomVisualRect(index, ritem, block, blockPos); - } - - //BEGIN: update the quarantine start - const bool wasLastIndex = (index.row() == (block.firstIndex.row() + block.items.count() - 1)); - if (index.row() == block.quarantineStart.row()) { - if (wasLastIndex) { - block.quarantineStart = QModelIndex(); - } else { - const QModelIndex nextIndex = d->proxyModel->index(index.row() + 1, modelColumn(), rootIndex()); - block.quarantineStart = nextIndex; - } - } - //END: update the quarantine start - } - - // we get now the absolute position through the relative position of the parent block. do not - // save this on ritem, since this would override the item relative position in block terms. - Private::Item item(ritem); - item.topLeft.ry() += blockPos.y(); - - const QSize sizeHint = item.size; - - if (d->hasGrid()) { - const QSize sizeGrid = gridSize(); - const QSize resultingSize = sizeHint.boundedTo(sizeGrid); - QRect res(item.topLeft.x() + ((sizeGrid.width() - resultingSize.width()) / 2), - item.topLeft.y(), resultingSize.width(), resultingSize.height()); - if (block.collapsed) { - // we can still do binary search, while we "hide" items. We move those items in collapsed - // blocks to the left and set a 0 height. - res.setLeft(-resultingSize.width()); - res.setHeight(0); - } - return d->mapToViewport(res); - } - - QRect res(item.topLeft.x(), item.topLeft.y(), sizeHint.width(), sizeHint.height()); - if (block.collapsed) { - // we can still do binary search, while we "hide" items. We move those items in collapsed - // blocks to the left and set a 0 height. - res.setLeft(-sizeHint.width()); - res.setHeight(0); - } - return d->mapToViewport(res); -} - -KCategoryDrawer *KCategorizedView::categoryDrawer() const -{ - return d->categoryDrawer; -} - -void KCategorizedView::setCategoryDrawer(KCategoryDrawer *categoryDrawer) -{ - if (d->categoryDrawerV2) { - disconnect(d->categoryDrawerV2, SIGNAL(collapseOrExpandClicked(QModelIndex)), - this, SLOT(_k_slotCollapseOrExpandClicked(QModelIndex))); - } - - d->categoryDrawer = categoryDrawer; - d->categoryDrawerV2 = dynamic_cast(categoryDrawer); - d->categoryDrawerV3 = dynamic_cast(categoryDrawer); - - if (d->categoryDrawerV2) { - connect(d->categoryDrawerV2, SIGNAL(collapseOrExpandClicked(QModelIndex)), - this, SLOT(_k_slotCollapseOrExpandClicked(QModelIndex))); - } -} - -int KCategorizedView::categorySpacing() const -{ - return d->categorySpacing; -} - -void KCategorizedView::setCategorySpacing(int categorySpacing) -{ - if (d->categorySpacing == categorySpacing) { - return; - } - - d->categorySpacing = categorySpacing; - - for (QHash::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it) { - Private::Block &block = *it; - block.outOfQuarantine = false; - } -} - -bool KCategorizedView::alternatingBlockColors() const -{ - return d->alternatingBlockColors; -} - -void KCategorizedView::setAlternatingBlockColors(bool enable) -{ - d->alternatingBlockColors = enable; -} - -bool KCategorizedView::collapsibleBlocks() const -{ - return d->collapsibleBlocks; -} - -void KCategorizedView::setCollapsibleBlocks(bool enable) -{ - d->collapsibleBlocks = enable; -} - -QModelIndexList KCategorizedView::block(const QString &category) -{ - QModelIndexList res; - const Private::Block &block = d->blocks[category]; - if (block.height == -1) { - return res; - } - QModelIndex current = block.firstIndex; - const int first = current.row(); - for (int i = 1; i <= block.items.count(); ++i) { - if (current.isValid()) { - res << current; - } - current = d->proxyModel->index(first + i, modelColumn(), rootIndex()); - } - return res; -} - -QModelIndexList KCategorizedView::block(const QModelIndex &representative) -{ - return block(representative.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString()); -} - -QModelIndex KCategorizedView::indexAt(const QPoint &point) const -{ - if (!d->isCategorized()) { - return QListView::indexAt(point); - } - - const int rowCount = d->proxyModel->rowCount(); - if (!rowCount) { - return QModelIndex(); - } - - // Binary search that will try to spot if there is an index under point - int bottom = 0; - int top = rowCount - 1; - while (bottom <= top) { - const int middle = (bottom + top) / 2; - const QModelIndex index = d->proxyModel->index(middle, modelColumn(), rootIndex()); - QRect rect = visualRect(index); - const int verticalOff = verticalOffset(); - int horizontalOff = horizontalOffset(); - if (layoutDirection() == Qt::RightToLeft) { - horizontalOff *= -1; - } - rect.topLeft().ry() += verticalOff; - rect.topLeft().rx() += horizontalOff; - rect.bottomRight().ry() += verticalOff; - rect.bottomRight().rx() += horizontalOff; - if (rect.contains(point)) { - if (index.model()->flags(index) & Qt::ItemIsEnabled) { - return index; - } - return QModelIndex(); - } - bool directionCondition; - if (layoutDirection() == Qt::LeftToRight) { - directionCondition = point.x() > rect.bottomRight().x(); - } else { - directionCondition = point.x() < rect.bottomLeft().x(); - } - if (point.y() > rect.bottomRight().y() || - (point.y() > rect.topLeft().y() && point.y() < rect.bottomRight().y() && directionCondition)) { - bottom = middle + 1; - } else { - top = middle - 1; - } - } - return QModelIndex(); -} - -void KCategorizedView::reset() -{ - d->blocks.clear(); - QListView::reset(); -} - -void KCategorizedView::paintEvent(QPaintEvent *event) -{ - if (!d->isCategorized()) { - QListView::paintEvent(event); - return; - } - - const QPair intersecting = d->intersectingIndexesWithRect(viewport()->rect().intersected(event->rect())); - - QPainter p(viewport()); - p.save(); - - Q_ASSERT(selectionModel()->model() == d->proxyModel); - - //BEGIN: draw categories - QHash::ConstIterator it(d->blocks.constBegin()); - while (it != d->blocks.constEnd()) { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex()); - QStyleOptionViewItemV4 option(viewOptions()); - option.features |= d->alternatingBlockColors && block.alternate ? QStyleOptionViewItemV4::Alternate - : QStyleOptionViewItemV4::None; - option.state |= !d->collapsibleBlocks || !block.collapsed ? QStyle::State_Open - : QStyle::State_None; - const int height = d->categoryDrawer->categoryHeight(categoryIndex, option); - QPoint pos = d->blockPosition(it.key()); - pos.ry() -= height; - option.rect.setTopLeft(pos); - option.rect.setWidth(d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin()); - option.rect.setHeight(height + d->blockHeight(it.key())); - option.rect = d->mapToViewport(option.rect); - if (!option.rect.intersects(viewport()->rect())) { - ++it; - continue; - } - d->categoryDrawer->drawCategory(categoryIndex, d->proxyModel->sortRole(), option, &p); - ++it; - } - //END: draw categories - - if (intersecting.first.isValid() && intersecting.second.isValid()) { - //BEGIN: draw items - int i = intersecting.first.row(); - int indexToCheckIfBlockCollapsed = i; - QModelIndex categoryIndex; - QString category; - Private::Block *block = 0; - while (i <= intersecting.second.row()) { - //BEGIN: first check if the block is collapsed. if so, we have to skip the item painting - if (i == indexToCheckIfBlockCollapsed) { - categoryIndex = d->proxyModel->index(i, d->proxyModel->sortColumn(), rootIndex()); - category = categoryIndex.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); - block = &d->blocks[category]; - indexToCheckIfBlockCollapsed = block->firstIndex.row() + block->items.count(); - if (block->collapsed) { - i = indexToCheckIfBlockCollapsed; - continue; - } - } - //END: first check if the block is collapsed. if so, we have to skip the item painting - - Q_ASSERT(block); - - const bool alternateItem = (i - block->firstIndex.row()) % 2; - - const QModelIndex index = d->proxyModel->index(i, modelColumn(), rootIndex()); - const Qt::ItemFlags flags = d->proxyModel->flags(index); - QStyleOptionViewItemV4 option(viewOptions()); - option.rect = visualRect(index); - option.widget = this; - option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText - : QStyleOptionViewItemV2::None; - option.features |= alternatingRowColors() && alternateItem ? QStyleOptionViewItemV4::Alternate - : QStyleOptionViewItemV4::None; - if (flags & Qt::ItemIsSelectable) { - option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected - : QStyle::State_None; - } else { - option.state &= ~QStyle::State_Selected; - } - option.state |= (index == currentIndex()) ? QStyle::State_HasFocus - : QStyle::State_None; - if (!(flags & Qt::ItemIsEnabled)) { - option.state &= ~QStyle::State_Enabled; - } else { - option.state |= (index == d->hoveredIndex) ? QStyle::State_MouseOver - : QStyle::State_None; - } - - itemDelegate(index)->paint(&p, option, index); - ++i; - } - //END: draw items - } - - //BEGIN: draw selection rect - if (isSelectionRectVisible() && d->rubberBandRect.isValid()) { - QStyleOptionRubberBand opt; - opt.initFrom(this); - opt.shape = QRubberBand::Rectangle; - opt.opaque = false; - opt.rect = d->mapToViewport(d->rubberBandRect).intersected(viewport()->rect().adjusted(-16, -16, 16, 16)); - p.save(); - style()->drawControl(QStyle::CE_RubberBand, &opt, &p); - p.restore(); - } - //END: draw selection rect - - p.restore(); -} - -void KCategorizedView::resizeEvent(QResizeEvent *event) -{ - d->regenerateAllElements(); - QListView::resizeEvent(event); -} - -void KCategorizedView::setSelection(const QRect &rect, - QItemSelectionModel::SelectionFlags flags) -{ - if (!d->isCategorized()) { - QListView::setSelection(rect, flags); - return; - } - - if (rect.topLeft() == rect.bottomRight()) { - const QModelIndex index = indexAt(rect.topLeft()); - selectionModel()->select(index, flags); - return; - } - - const QPair intersecting = d->intersectingIndexesWithRect(rect); - - QItemSelection selection; - - //TODO: think of a faster implementation - QModelIndex firstIndex; - QModelIndex lastIndex; - for (int i = intersecting.first.row(); i <= intersecting.second.row(); ++i) { - const QModelIndex index = d->proxyModel->index(i, modelColumn(), rootIndex()); - const bool visualRectIntersects = visualRect(index).intersects(rect); - if (firstIndex.isValid()) { - if (visualRectIntersects) { - lastIndex = index; - } else { - selection << QItemSelectionRange(firstIndex, lastIndex); - firstIndex = QModelIndex(); - } - } else if (visualRectIntersects) { - firstIndex = index; - lastIndex = index; - } - } - - if (firstIndex.isValid()) { - selection << QItemSelectionRange(firstIndex, lastIndex); - } - - selectionModel()->select(selection, flags); -} - -void KCategorizedView::mouseMoveEvent(QMouseEvent *event) -{ - QListView::mouseMoveEvent(event); - d->hoveredIndex = indexAt(event->pos()); - const SelectionMode itemViewSelectionMode = selectionMode(); - if (state() == DragSelectingState && isSelectionRectVisible() && itemViewSelectionMode != SingleSelection - && itemViewSelectionMode != NoSelection) { - QRect rect(d->pressedPosition, event->pos() + QPoint(horizontalOffset(), verticalOffset())); - rect = rect.normalized(); - update(rect.united(d->rubberBandRect)); - d->rubberBandRect = rect; - } - if (!d->categoryDrawerV2) { - return; - } - QHash::ConstIterator it(d->blocks.constBegin()); - while (it != d->blocks.constEnd()) { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex()); - QStyleOptionViewItemV4 option(viewOptions()); - const int height = d->categoryDrawer->categoryHeight(categoryIndex, option); - QPoint pos = d->blockPosition(it.key()); - pos.ry() -= height; - option.rect.setTopLeft(pos); - option.rect.setWidth(d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin()); - option.rect.setHeight(height + d->blockHeight(it.key())); - option.rect = d->mapToViewport(option.rect); - const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos()); - if (option.rect.contains(mousePos)) { - if (d->categoryDrawerV3 && d->hoveredBlock->height != -1 && *d->hoveredBlock != block) { - const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex()); - const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex); - d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect); - *d->hoveredBlock = block; - d->hoveredCategory = it.key(); - viewport()->update(option.rect); - } else if (d->hoveredBlock->height == -1) { - *d->hoveredBlock = block; - d->hoveredCategory = it.key(); - } else if (d->categoryDrawerV3) { - d->categoryDrawerV3->mouseMoved(categoryIndex, option.rect, event); - } else { - d->categoryDrawerV2->mouseButtonMoved(categoryIndex, event); - } - viewport()->update(option.rect); - return; - } - ++it; - } - if (d->categoryDrawerV3 && d->hoveredBlock->height != -1) { - const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex()); - const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex); - d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect); - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - viewport()->update(option.rect); - } -} - -void KCategorizedView::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) { - d->pressedPosition = event->pos(); - d->pressedPosition.rx() += horizontalOffset(); - d->pressedPosition.ry() += verticalOffset(); - } - if (!d->categoryDrawerV2) { - QListView::mousePressEvent(event); - return; - } - QHash::ConstIterator it(d->blocks.constBegin()); - while (it != d->blocks.constEnd()) { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex()); - const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex); - const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos()); - if (option.rect.contains(mousePos)) { - if (d->categoryDrawerV3) { - d->categoryDrawerV3->mouseButtonPressed(categoryIndex, option.rect, event); - } else { - d->categoryDrawerV2->mouseButtonPressed(categoryIndex, event); - } - viewport()->update(option.rect); - if (!event->isAccepted()) { - QListView::mousePressEvent(event); - } - return; - } - ++it; - } - QListView::mousePressEvent(event); -} - -void KCategorizedView::mouseReleaseEvent(QMouseEvent *event) -{ - d->pressedPosition = QPoint(); - d->rubberBandRect = QRect(); - if (!d->categoryDrawerV2) { - QListView::mouseReleaseEvent(event); - return; - } - QHash::ConstIterator it(d->blocks.constBegin()); - while (it != d->blocks.constEnd()) { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex()); - const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex); - const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos()); - if (option.rect.contains(mousePos)) { - if (d->categoryDrawerV3) { - d->categoryDrawerV3->mouseButtonReleased(categoryIndex, option.rect, event); - } else { - d->categoryDrawerV2->mouseButtonReleased(categoryIndex, event); - } - viewport()->update(option.rect); - if (!event->isAccepted()) { - QListView::mouseReleaseEvent(event); - } - return; - } - ++it; - } - QListView::mouseReleaseEvent(event); -} - -void KCategorizedView::leaveEvent(QEvent *event) -{ - QListView::leaveEvent(event); - if (d->hoveredIndex.isValid()) { - viewport()->update(visualRect(d->hoveredIndex)); - d->hoveredIndex = QModelIndex(); - } - if (d->categoryDrawerV3 && d->hoveredBlock->height != -1) { - const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex()); - const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex); - d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect); - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - viewport()->update(option.rect); - } -} - -void KCategorizedView::startDrag(Qt::DropActions supportedActions) -{ - QListView::startDrag(supportedActions); -} - -void KCategorizedView::dragMoveEvent(QDragMoveEvent *event) -{ - QListView::dragMoveEvent(event); - d->hoveredIndex = indexAt(event->pos()); -} - -void KCategorizedView::dragEnterEvent(QDragEnterEvent *event) -{ - QListView::dragEnterEvent(event); -} - -void KCategorizedView::dragLeaveEvent(QDragLeaveEvent *event) -{ - QListView::dragLeaveEvent(event); -} - -void KCategorizedView::dropEvent(QDropEvent *event) -{ - QListView::dropEvent(event); -} - -//TODO: improve se we take into account collapsed blocks -//TODO: take into account when there is no grid and no uniformItemSizes -QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) -{ - if (!d->isCategorized()) { - return QListView::moveCursor(cursorAction, modifiers); - } - - const QModelIndex current = currentIndex(); - const QRect currentRect = visualRect(current); - if (!current.isValid()) { - const int rowCount = d->proxyModel->rowCount(rootIndex()); - if (!rowCount) { - return QModelIndex(); - } - return d->proxyModel->index(0, modelColumn(), rootIndex()); - } - - switch (cursorAction) { - case MoveLeft: { - if (!current.row()) { - return QModelIndex(); - } - const QModelIndex previous = d->proxyModel->index(current.row() - 1, modelColumn(), rootIndex()); - const QRect previousRect = visualRect(previous); - if (previousRect.top() == currentRect.top()) { - return previous; - } - - return QModelIndex(); - } - case MoveRight: { - if (current.row() == d->proxyModel->rowCount() - 1) { - return QModelIndex(); - } - const QModelIndex next = d->proxyModel->index(current.row() + 1, modelColumn(), rootIndex()); - const QRect nextRect = visualRect(next); - if (nextRect.top() == currentRect.top()) { - return next; - } - - return QModelIndex(); - } - case MoveDown: { - if (d->hasGrid() || uniformItemSizes()) { - const QModelIndex current = currentIndex(); - const QSize itemSize = d->hasGrid() ? gridSize() - : sizeHintForIndex(current); - const Private::Block &block = d->blocks[d->categoryForIndex(current)]; - const int maxItemsPerRow = qMax(d->viewportWidth() / itemSize.width(), 1); - const bool canMove = current.row() + maxItemsPerRow < block.firstIndex.row() + - block.items.count(); - - if (canMove) { - return d->proxyModel->index(current.row() + maxItemsPerRow, modelColumn(), rootIndex()); - } - - const int currentRelativePos = (current.row() - block.firstIndex.row()) % maxItemsPerRow; - const QModelIndex nextIndex = d->proxyModel->index(block.firstIndex.row() + block.items.count(), modelColumn(), rootIndex()); - - if (!nextIndex.isValid()) { - return QModelIndex(); - } - - const Private::Block &nextBlock = d->blocks[d->categoryForIndex(nextIndex)]; - - if (nextBlock.items.count() <= currentRelativePos) { - return QModelIndex(); - } - - if (currentRelativePos < (block.items.count() % maxItemsPerRow)) { - return d->proxyModel->index(nextBlock.firstIndex.row() + currentRelativePos, modelColumn(), rootIndex()); - } - - return QModelIndex(); - } - } - case MoveUp: { - if (d->hasGrid() || uniformItemSizes()) { - const QModelIndex current = currentIndex(); - const QSize itemSize = d->hasGrid() ? gridSize() - : sizeHintForIndex(current); - const Private::Block &block = d->blocks[d->categoryForIndex(current)]; - const int maxItemsPerRow = qMax(d->viewportWidth() / itemSize.width(), 1); - const bool canMove = current.row() - maxItemsPerRow >= block.firstIndex.row(); - - if (canMove) { - return d->proxyModel->index(current.row() - maxItemsPerRow, modelColumn(), rootIndex()); - } - - const int currentRelativePos = (current.row() - block.firstIndex.row()) % maxItemsPerRow; - const QModelIndex prevIndex = d->proxyModel->index(block.firstIndex.row() - 1, modelColumn(), rootIndex()); - - if (!prevIndex.isValid()) { - return QModelIndex(); - } - - const Private::Block &prevBlock = d->blocks[d->categoryForIndex(prevIndex)]; - - if (prevBlock.items.count() <= currentRelativePos) { - return QModelIndex(); - } - - const int remainder = prevBlock.items.count() % maxItemsPerRow; - if (currentRelativePos < remainder) { - return d->proxyModel->index(prevBlock.firstIndex.row() + prevBlock.items.count() - remainder + currentRelativePos, modelColumn(), rootIndex()); - } - - return QModelIndex(); - } - } - default: - break; - } - - return QModelIndex(); -} - -void KCategorizedView::rowsAboutToBeRemoved(const QModelIndex &parent, - int start, - int end) -{ - if (!d->isCategorized()) { - QListView::rowsAboutToBeRemoved(parent, start, end); - return; - } - - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - - if (end - start + 1 == d->proxyModel->rowCount()) { - d->blocks.clear(); - QListView::rowsAboutToBeRemoved(parent, start, end); - return; - } - - // Removal feels a bit more complicated than insertion. Basically we can consider there are - // 3 different cases when going to remove items. (*) represents an item, Items between ([) and - // (]) are the ones which are marked for removal. - // - // - 1st case: - // ... * * * * * * [ * * * ... - // - // The items marked for removal are the last part of this category. No need to mark any item - // of this category as in quarantine, because no special offset will be pushed to items at - // the right because of any changes (since the removed items are those on the right most part - // of the category). - // - // - 2nd case: - // ... * * * * * * ] * * * ... - // - // The items marked for removal are the first part of this category. We have to mark as in - // quarantine all items in this category. Absolutely all. All items will have to be moved to - // the left (or moving up, because rows got a different offset). - // - // - 3rd case: - // ... * * [ * * * * ] * * ... - // - // The items marked for removal are in between of this category. We have to mark as in - // quarantine only those items that are at the right of the end of the removal interval, - // (starting on "]"). - // - // It hasn't been explicitly said, but when we remove, we have to mark all blocks that are - // located under the top most affected category as in quarantine (the block itself, as a whole), - // because such a change can force it to have a different offset (note that items themselves - // contain relative positions to the block, so marking the block as in quarantine is enough). - // - // Also note that removal implicitly means that we have to update correctly firstIndex of each - // block, and in general keep updated the internal information of elements. - - QStringList listOfCategoriesMarkedForRemoval; - - QString lastCategory; - int alreadyRemoved = 0; - for (int i = start; i <= end; ++i) { - const QModelIndex index = d->proxyModel->index(i, modelColumn(), parent); - - Q_ASSERT(index.isValid()); - - const QString category = d->categoryForIndex(index); - - if (lastCategory != category) { - lastCategory = category; - alreadyRemoved = 0; - } - - Private::Block &block = d->blocks[category]; - block.items.removeAt(i - block.firstIndex.row() - alreadyRemoved); - ++alreadyRemoved; - - if (!block.items.count()) { - listOfCategoriesMarkedForRemoval << category; - } - - block.height = -1; - - viewport()->update(); - } - - //BEGIN: update the items that are in quarantine in affected categories - { - const QModelIndex lastIndex = d->proxyModel->index(end, modelColumn(), parent); - const QString category = d->categoryForIndex(lastIndex); - Private::Block &block = d->blocks[category]; - if (block.items.count() && start <= block.firstIndex.row() && end >= block.firstIndex.row()) { - block.firstIndex = d->proxyModel->index(end + 1, modelColumn(), parent); - } - block.quarantineStart = block.firstIndex; - } - //END: update the items that are in quarantine in affected categories - - Q_FOREACH (const QString &category, listOfCategoriesMarkedForRemoval) { - d->blocks.remove(category); - } - - //BEGIN: mark as in quarantine those categories that are under the affected ones - { - //BEGIN: order for marking as alternate those blocks that are alternate - QList blockList = d->blocks.values(); - qSort(blockList.begin(), blockList.end(), Private::Block::lessThan); - QList firstIndexesRows; - foreach (const Private::Block &block, blockList) { - firstIndexesRows << block.firstIndex.row(); - } - //END: order for marking as alternate those blocks that are alternate - for (QHash::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it) { - Private::Block &block = *it; - if (block.firstIndex.row() > start) { - block.outOfQuarantine = false; - block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2; - } else if (block.firstIndex.row() == start) { - block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2; - } - } - } - //END: mark as in quarantine those categories that are under the affected ones - - QListView::rowsAboutToBeRemoved(parent, start, end); -} - -void KCategorizedView::updateGeometries() -{ - const int oldVerticalOffset = verticalOffset(); - - QListView::updateGeometries(); - - if (!d->isCategorized()) { - return; - } - - const int rowCount = d->proxyModel->rowCount(); - if (!rowCount) { - verticalScrollBar()->setRange(0, 0); - return; - } - - const QModelIndex lastIndex = d->proxyModel->index(rowCount - 1, modelColumn(), rootIndex()); - Q_ASSERT(lastIndex.isValid()); - QRect lastItemRect = visualRect(lastIndex); - - if (d->hasGrid()) { - lastItemRect.setSize(lastItemRect.size().expandedTo(gridSize())); - } else { - if (uniformItemSizes()) { - QSize itemSize = sizeHintForIndex(lastIndex); - itemSize.setHeight(itemSize.height() + spacing()); - lastItemRect.setSize(itemSize); - } else { - QSize itemSize = sizeHintForIndex(lastIndex); - const QString category = d->categoryForIndex(lastIndex); - itemSize.setHeight(d->highestElementInLastRow(d->blocks[category]) + spacing()); - lastItemRect.setSize(itemSize); - } - } - - const int bottomRange = lastItemRect.bottomRight().y() + verticalOffset() - viewport()->height(); - - if (verticalScrollMode() == ScrollPerItem) { - verticalScrollBar()->setSingleStep(lastItemRect.height()); - const int rowsPerPage = qMax(viewport()->height() / lastItemRect.height(), 1); - verticalScrollBar()->setPageStep(rowsPerPage * lastItemRect.height()); - } - - verticalScrollBar()->setRange(0, bottomRange); - verticalScrollBar()->setValue(oldVerticalOffset); - - //TODO: also consider working with the horizontal scroll bar. since at this level I am not still - // supporting "top to bottom" flow, there is no real problem. If I support that someday - // (think how to draw categories), we would have to take care of the horizontal scroll bar too. - // In theory, as KCategorizedView has been designed, there is no need of horizontal scroll bar. - horizontalScrollBar()->setRange(0, 0); -} - -void KCategorizedView::currentChanged(const QModelIndex ¤t, - const QModelIndex &previous) -{ - QListView::currentChanged(current, previous); -} - -void KCategorizedView::dataChanged(const QModelIndex &topLeft, - const QModelIndex &bottomRight) -{ - QListView::dataChanged(topLeft, bottomRight); - if (!d->isCategorized()) { - return; - } - - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - - //BEGIN: since the model changed data, we need to reconsider item sizes - int i = topLeft.row(); - int indexToCheck = i; - QModelIndex categoryIndex; - QString category; - Private::Block *block; - while (i <= bottomRight.row()) { - const QModelIndex currIndex = d->proxyModel->index(i, modelColumn(), rootIndex()); - if (i == indexToCheck) { - categoryIndex = d->proxyModel->index(i, d->proxyModel->sortColumn(), rootIndex()); - category = categoryIndex.data(KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); - block = &d->blocks[category]; - block->quarantineStart = currIndex; - indexToCheck = block->firstIndex.row() + block->items.count(); - } - visualRect(currIndex); - ++i; - } - //END: since the model changed data, we need to reconsider item sizes -} - -void KCategorizedView::rowsInserted(const QModelIndex &parent, - int start, - int end) -{ - QListView::rowsInserted(parent, start, end); - if (!d->isCategorized()) { - return; - } - - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - d->rowsInserted(parent, start, end); -} - -void KCategorizedView::slotLayoutChanged() -{ - if (!d->isCategorized()) { - return; - } - - d->blocks.clear(); - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - if (d->proxyModel->rowCount()) { - d->rowsInserted(rootIndex(), 0, d->proxyModel->rowCount() - 1); - } -} - -//END: Public part diff --git a/src/widgets/kcategorizedview.h b/src/widgets/kcategorizedview.h deleted file mode 100644 index 8f5ad9a57..000000000 --- a/src/widgets/kcategorizedview.h +++ /dev/null @@ -1,324 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * 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 KCATEGORIZEDVIEW_H -#define KCATEGORIZEDVIEW_H - -#include - -class KCategoryDrawer; -class KCategoryDrawerV2; - -/** - * @short Item view for listing items in a categorized fashion optionally - * - * KCategorizedView basically has the same functionality as QListView, only that it also lets you - * layout items in a way that they are categorized visually. - * - * For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer - * with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be - * flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true). - * - * The way it works (if categorization enabled): - * - * - When sorting, it does more things than QListView does. It will ask the model for the - * special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return - * a QString or an int in order to tell the view the order of categories. In this sense, for - * instance, if we are sorting by name ascending, "A" would be before than "B". If we are - * sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are - * also sorted. - * - * - When the view has to paint, it will ask the model with the role CategoryDisplayRole - * (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if - * we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example. - * - * For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own - * drawing. - * - * @note All examples cited before talk about filesystems and such, but have present that this - * is a completely generic class, and it can be used for whatever your purpose is. For - * instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In - * this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the - * same ("Mammal" and "Oviparous"). - * - * @note There is a really performance boost if CategorySortRole returns an int instead of a QString. - * Have present that this role is asked (n * log n) times when sorting and compared. Comparing - * ints is always faster than comparing strings, whithout mattering how fast the string - * comparison is. Consider thinking of a way of returning ints instead of QStrings if your - * model can contain a high number of items. - * - * @warning Note that for really drawing items in blocks you will need some things to be done: - * - The model set to this view has to be (or inherit if you want to do special stuff - * in it) KCategorizedSortFilterProxyModel. - * - This model needs to be set setCategorizedModel to true. - * - Set a category drawer by calling setCategoryDrawer. - * - * @see KCategorizedSortFilterProxyModel, KCategoryDrawer - * - * @author Rafael Fernández López - */ -class KCategorizedView - : public QListView -{ - Q_OBJECT - Q_PROPERTY(int categorySpacing READ categorySpacing WRITE setCategorySpacing) - Q_PROPERTY(bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors) - Q_PROPERTY(bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks) - -public: - KCategorizedView(QWidget *parent = 0); - - ~KCategorizedView(); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void setModel(QAbstractItemModel *model); - - /** - * Calls to setGridSizeOwn(). - */ - void setGridSize(const QSize &size); - - /** - * @warning note that setGridSize is not virtual in the base class (QListView), so if you are - * calling to this method, make sure you have a KCategorizedView pointer around. This - * means that something like: - * @code - * QListView *lv = new KCategorizedView(); - * lv->setGridSize(mySize); - * @endcode - * - * will not call to the expected setGridSize method. Instead do something like this: - * - * @code - * QListView *lv; - * ... - * KCategorizedView *cv = qobject_cast(lv); - * if (cv) { - * cv->setGridSizeOwn(mySize); - * } else { - * lv->setGridSize(mySize); - * } - * @endcode - * - * @note this method will call to QListView::setGridSize among other operations. - * - * @since 4.4 - */ - void setGridSizeOwn(const QSize &size); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual QRect visualRect(const QModelIndex &index) const; - - /** - * Returns the current category drawer. - */ - KCategoryDrawer *categoryDrawer() const; - - /** - * The category drawer that will be used for drawing categories. - */ - void setCategoryDrawer(KCategoryDrawer *categoryDrawer); - - /** - * @return Category spacing. The spacing between categories. - * - * @since 4.4 - */ - int categorySpacing() const; - - /** - * Stablishes the category spacing. This is the spacing between categories. - * - * @since 4.4 - */ - void setCategorySpacing(int categorySpacing); - - /** - * @return Whether blocks should be drawn with alternating colors. - * - * @since 4.4 - */ - bool alternatingBlockColors() const; - - /** - * Sets whether blocks should be drawn with alternating colors. - * - * @since 4.4 - */ - void setAlternatingBlockColors(bool enable); - - /** - * @return Whether blocks can be collapsed or not. - * - * @since 4.4 - */ - bool collapsibleBlocks() const; - - /** - * Sets whether blocks can be collapsed or not. - * - * @since 4.4 - */ - void setCollapsibleBlocks(bool enable); - - /** - * @return Block of indexes that are into @p category. - * - * @since 4.5 - */ - QModelIndexList block(const QString &category); - - /** - * @return Block of indexes that are represented by @p representative. - * - * @since 4.5 - */ - QModelIndexList block(const QModelIndex &representative); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual QModelIndex indexAt(const QPoint &point) const; - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void reset(); - -protected: - /** - * Reimplemented from QWidget. - */ - virtual void paintEvent(QPaintEvent *event); - - /** - * Reimplemented from QWidget. - */ - virtual void resizeEvent(QResizeEvent *event); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void setSelection(const QRect &rect, - QItemSelectionModel::SelectionFlags flags); - - /** - * Reimplemented from QWidget. - */ - virtual void mouseMoveEvent(QMouseEvent *event); - - /** - * Reimplemented from QWidget. - */ - virtual void mousePressEvent(QMouseEvent *event); - - /** - * Reimplemented from QWidget. - */ - virtual void mouseReleaseEvent(QMouseEvent *event); - - /** - * Reimplemented from QWidget. - */ - virtual void leaveEvent(QEvent *event); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void startDrag(Qt::DropActions supportedActions); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dragMoveEvent(QDragMoveEvent *event); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dragEnterEvent(QDragEnterEvent *event); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dragLeaveEvent(QDragLeaveEvent *event); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dropEvent(QDropEvent *event); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual QModelIndex moveCursor(CursorAction cursorAction, - Qt::KeyboardModifiers modifiers); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void rowsAboutToBeRemoved(const QModelIndex &parent, - int start, - int end); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void updateGeometries(); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void currentChanged(const QModelIndex ¤t, - const QModelIndex &previous); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dataChanged(const QModelIndex &topLeft, - const QModelIndex &bottomRight); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void rowsInserted(const QModelIndex &parent, - int start, - int end); - -protected Q_SLOTS: - /** - * @internal - * Reposition items as needed. - */ - virtual void slotLayoutChanged(); - -private: - class Private; - Private *const d; - - Q_PRIVATE_SLOT(d, void _k_slotCollapseOrExpandClicked(QModelIndex)) -}; - -#include "kcategorizedview_p.h" - -#endif // KCATEGORIZEDVIEW_H diff --git a/src/widgets/kcategorizedview_p.h b/src/widgets/kcategorizedview_p.h deleted file mode 100644 index 4c175fbb3..000000000 --- a/src/widgets/kcategorizedview_p.h +++ /dev/null @@ -1,159 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * 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 KCATEGORIZEDVIEW_P_H -#define KCATEGORIZEDVIEW_P_H - -class KCategorizedSortFilterProxyModel; -class KCategoryDrawer; -class KCategoryDrawerV2; -class KCategoryDrawerV3; - -/** - * @internal - */ -class KCategorizedView::Private -{ -public: - struct Block; - struct Item; - - Private(KCategorizedView *q); - ~Private(); - - /** - * @return whether this view has all required elements to be categorized. - */ - bool isCategorized() const; - - /** - * @return the block rect for the representative @p representative. - */ - QStyleOptionViewItemV4 blockRect(const QModelIndex &representative); - - /** - * Returns the first and last element that intersects with rect. - * - * @note see that here we cannot take out items between first and last (as we could - * do with the rubberband). - * - * Complexity: O(log(n)) where n is model()->rowCount(). - */ - QPair intersectingIndexesWithRect(const QRect &rect) const; - - /** - * Returns the position of the block of @p category. - * - * Complexity: O(n) where n is the number of different categories when the block has been - * marked as in quarantine. O(1) the rest of the times (the vast majority). - */ - QPoint blockPosition(const QString &category); - - /** - * Returns the height of the block determined by @p category. - */ - int blockHeight(const QString &category); - - /** - * Returns the actual viewport width. - */ - int viewportWidth() const; - - /** - * Marks all elements as in quarantine. - * - * Complexity: O(n) where n is model()->rowCount(). - * - * @warning this is an expensive operation - */ - void regenerateAllElements(); - - /** - * Update internal information, and keep sync with the real information that the model contains. - */ - void rowsInserted(const QModelIndex &parent, int start, int end); - - /** - * Returns @p rect in viewport terms, taking in count horizontal and vertical offsets. - */ - QRect mapToViewport(const QRect &rect) const; - - /** - * Returns @p rect in absolute terms, converted from viewport position. - */ - QRect mapFromViewport(const QRect &rect) const; - - /** - * Returns the height of the highest element in last row. This is only applicable if there is - * no grid set and uniformItemSizes is false. - * - * @param block in which block are we searching. Necessary to stop the search if we hit the - * first item in this block. - */ - int highestElementInLastRow(const Block &block) const; - - /** - * Returns whether the view has a valid grid size. - */ - bool hasGrid() const; - - /** - * Returns the category for the given index. - */ - QString categoryForIndex(const QModelIndex &index) const; - - /** - * Updates the visual rect for item when flow is LeftToRight. - */ - void leftToRightVisualRect(const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos) const; - - /** - * Updates the visual rect for item when flow is TopToBottom. - * @note we only support viewMode == ListMode in this case. - */ - void topToBottomVisualRect(const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos) const; - - /** - * Called when expand or collapse has been clicked on the category drawer. - */ - void _k_slotCollapseOrExpandClicked(QModelIndex); - - KCategorizedView *q; - KCategorizedSortFilterProxyModel *proxyModel; - KCategoryDrawer *categoryDrawer; - KCategoryDrawerV2 *categoryDrawerV2; - KCategoryDrawerV3 *categoryDrawerV3; - int categorySpacing; - bool alternatingBlockColors; - bool collapsibleBlocks; - - Block *hoveredBlock; - QString hoveredCategory; - QModelIndex hoveredIndex; - - QPoint pressedPosition; - QRect rubberBandRect; - - QHash blocks; -}; - -#endif // KCATEGORIZEDVIEW_P_H diff --git a/src/widgets/kcategorydrawer.cpp b/src/widgets/kcategorydrawer.cpp deleted file mode 100644 index 78147f00b..000000000 --- a/src/widgets/kcategorydrawer.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * 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 "kcategorydrawer.h" - -#include -#include -#include - -#include "kcategorizedview.h" -#include "kcategorizedsortfilterproxymodel.h" - -#define HORIZONTAL_HINT 3 - -class KCategoryDrawer::Private -{ -public: - Private() - : leftMargin(0) - , rightMargin(0) - { - } - - ~Private() - { - } - - int leftMargin; - int rightMargin; -}; - -KCategoryDrawer::KCategoryDrawer() - : d(new Private) -{ - setLeftMargin(2); - setRightMargin(2); -} - -KCategoryDrawer::~KCategoryDrawer() -{ - delete d; -} - -void KCategoryDrawer::drawCategory(const QModelIndex &index, - int /*sortRole*/, - const QStyleOption &option, - QPainter *painter) const -{ - painter->setRenderHint(QPainter::Antialiasing); - - const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); - const QRect optRect = option.rect; - QFont font(QApplication::font()); - font.setBold(true); - const QFontMetrics fontMetrics = QFontMetrics(font); - - QColor outlineColor = option.palette.text().color(); - outlineColor.setAlphaF(0.35); - - //BEGIN: top left corner - { - painter->save(); - painter->setPen(outlineColor); - const QPointF topLeft(optRect.topLeft()); - QRectF arc(topLeft, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 1440, 1440); - painter->restore(); - } - //END: top left corner - - //BEGIN: left vertical line - { - QPoint start(optRect.topLeft()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topLeft()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: left vertical line - - //BEGIN: horizontal line - { - QPoint start(optRect.topLeft()); - start.rx() += 3; - QPoint horizontalGradTop(optRect.topLeft()); - horizontalGradTop.rx() += optRect.width() - 6; - painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); - } - //END: horizontal line - - //BEGIN: top right corner - { - painter->save(); - painter->setPen(outlineColor); - QPointF topRight(optRect.topRight()); - topRight.rx() -= 4; - QRectF arc(topRight, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 0, 1440); - painter->restore(); - } - //END: top right corner - - //BEGIN: right vertical line - { - QPoint start(optRect.topRight()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topRight()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: right vertical line - - //BEGIN: text - { - QRect textRect(option.rect); - textRect.setTop(textRect.top() + 7); - textRect.setLeft(textRect.left() + 7); - textRect.setHeight(fontMetrics.height()); - textRect.setRight(textRect.right() - 7); - - painter->save(); - painter->setFont(font); - QColor penColor(option.palette.text().color()); - penColor.setAlphaF(0.6); - painter->setPen(penColor); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category); - painter->restore(); - } - //END: text -} - -int KCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &option) const -{ - Q_UNUSED(index); - Q_UNUSED(option) - - QFont font(QApplication::font()); - font.setBold(true); - QFontMetrics fontMetrics(font); - - const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */ - + 11 /* top and bottom separation */; - return height; -} - -int KCategoryDrawer::leftMargin() const -{ - return d->leftMargin; -} - -void KCategoryDrawer::setLeftMargin(int leftMargin) -{ - d->leftMargin = leftMargin; -} - -int KCategoryDrawer::rightMargin() const -{ - return d->rightMargin; -} - -void KCategoryDrawer::setRightMargin(int rightMargin) -{ - d->rightMargin = rightMargin; -} - -KCategoryDrawer &KCategoryDrawer::operator=(const KCategoryDrawer &cd) -{ - d->leftMargin = cd.d->leftMargin; - d->rightMargin = cd.d->rightMargin; - return *this; -} - -KCategoryDrawerV2::KCategoryDrawerV2(QObject *parent) - : QObject(parent) - , KCategoryDrawer() -{ -} - -KCategoryDrawerV2::~KCategoryDrawerV2() -{ -} - -void KCategoryDrawerV2::mouseButtonPressed(const QModelIndex&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawerV2::mouseButtonReleased(const QModelIndex&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawerV2::mouseButtonMoved(const QModelIndex&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawerV2::mouseButtonDoubleClicked(const QModelIndex&, QMouseEvent *event) -{ - event->ignore(); -} - -class KCategoryDrawerV3::Private -{ -public: - Private(KCategorizedView *view) - : view(view) - { - } - - ~Private() - { - } - - KCategorizedView *view; -}; - -KCategoryDrawerV3::KCategoryDrawerV3(KCategorizedView *view) - : KCategoryDrawerV2(view) - , d(new Private(view)) -{ -} - -KCategoryDrawerV3::~KCategoryDrawerV3() -{ - delete d; -} - -KCategorizedView *KCategoryDrawerV3::view() const -{ - return d->view; -} - -void KCategoryDrawerV3::mouseButtonPressed(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawerV3::mouseButtonReleased(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawerV3::mouseMoved(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawerV3::mouseButtonDoubleClicked(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawerV3::mouseLeft(const QModelIndex&, const QRect&) -{ -} diff --git a/src/widgets/kcategorydrawer.h b/src/widgets/kcategorydrawer.h deleted file mode 100644 index d69b1b93f..000000000 --- a/src/widgets/kcategorydrawer.h +++ /dev/null @@ -1,228 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * 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 KCATEGORYDRAWER_H -#define KCATEGORYDRAWER_H - -#include -#include - -class QPainter; -class QModelIndex; -class QStyleOption; - -class KCategorizedView; - -/** - * @deprecated - * - * The category drawing is performed by this class. It also gives information about the category - * height and margins. - * - * @warning Please use KCategoryDrawerV3 instead - */ -class KCategoryDrawer -{ -public: - KCategoryDrawer(); - - virtual ~KCategoryDrawer(); - - /** - * This method purpose is to draw a category represented by the given - * @param index with the given @param sortRole sorting role - * - * @note This method will be called one time per category, always with the - * first element in that category - */ - virtual void drawCategory(const QModelIndex &index, - int sortRole, - const QStyleOption &option, - QPainter *painter) const; - - /** - * @return The category height for the category representated by index @p index with - * style options @p option. - */ - virtual int categoryHeight(const QModelIndex &index, const QStyleOption &option) const; - - //TODO KDE5: make virtual as leftMargin - /** - * @note 0 by default - * - * @since 4.4 - */ - int leftMargin() const; - - /** - * @note call to this method on the KCategoryDrawer constructor to set the left margin - * - * @since 4.4 - */ - void setLeftMargin(int leftMargin); - - //TODO KDE5: make virtual as rightMargin - /** - * @note 0 by default - * - * @since 4.4 - */ - int rightMargin() const; - - /** - * @note call to this method on the KCategoryDrawer constructor to set the right margin - * - * @since 4.4 - */ - void setRightMargin(int rightMargin); - - KCategoryDrawer &operator=(const KCategoryDrawer &cd); - -private: - class Private; - Private *const d; -}; - - -/** - * @deprecated - * - * @since 4.4 - * - * @warning Please use KCategoryDrawerV3 instead - */ -class KCategoryDrawerV2 - : public QObject - , public KCategoryDrawer -{ - Q_OBJECT - -public: - KCategoryDrawerV2(QObject *parent = 0); - virtual ~KCategoryDrawerV2(); - - virtual void mouseButtonPressed(const QModelIndex &index, QMouseEvent *event); - - virtual void mouseButtonReleased(const QModelIndex &index, QMouseEvent *event); - - virtual void mouseButtonMoved(const QModelIndex &index, QMouseEvent *event); - - virtual void mouseButtonDoubleClicked(const QModelIndex &index, QMouseEvent *event); - -Q_SIGNALS: - /** - * This signal becomes emitted when collapse or expand has been clicked. - */ - void collapseOrExpandClicked(const QModelIndex &index); - - /** - * Emit this signal on your subclass implementation to notify that something happened. Usually - * this will be triggered when you have received an event, and its position matched some "hot spot". - * - * You give this action the integer you want, and having connected this signal to your code, - * the connected slot can perform the needed changes (view, model, selection model, delegate...) - */ - void actionRequested(int action, const QModelIndex &index); -}; - -/** - * @since 4.5 - */ -class KCategoryDrawerV3 - : public KCategoryDrawerV2 -{ - friend class KCategorizedView; - -public: - KCategoryDrawerV3(KCategorizedView *view); - virtual ~KCategoryDrawerV3(); - - /** - * @return The view this category drawer is associated with. - */ - KCategorizedView *view() const; - - using KCategoryDrawerV2::mouseButtonPressed; - using KCategoryDrawerV2::mouseButtonReleased; - using KCategoryDrawerV2::mouseButtonDoubleClicked; - -protected: - /** - * Method called when the mouse button has been pressed. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - * - * @warning You explicitly have to determine whether the event has been accepted or not. You - * have to call event->accept() or event->ignore() at all possible case branches in - * your code. - */ - virtual void mouseButtonPressed(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event); - - /** - * Method called when the mouse button has been released. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - * - * @warning You explicitly have to determine whether the event has been accepted or not. You - * have to call event->accept() or event->ignore() at all possible case branches in - * your code. - */ - virtual void mouseButtonReleased(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event); - - /** - * Method called when the mouse has been moved. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - */ - virtual void mouseMoved(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event); - - /** - * Method called when the mouse button has been double clicked. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - * - * @warning You explicitly have to determine whether the event has been accepted or not. You - * have to call event->accept() or event->ignore() at all possible case branches in - * your code. - */ - virtual void mouseButtonDoubleClicked(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event); - - /** - * Method called when the mouse button has left this block. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - */ - virtual void mouseLeft(const QModelIndex &index, const QRect &blockRect); - -private: - class Private; - Private *const d; -}; - -#endif // KCATEGORYDRAWER_H