summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2014-02-08 22:18:32 +0100
committerPetr Mrázek <peterix@gmail.com>2014-02-08 22:18:32 +0100
commitaf33b966848687048f0a9e409444bc1b1f7d12df (patch)
treec978a58fddbcf1e6566f309f67579264ffdd4168
parent53069205faf4063e838bab4beea5089d6ec2a0af (diff)
parentaa41b891f06a629099d108ab1f4df94d1bbc828c (diff)
downloadMultiMC-af33b966848687048f0a9e409444bc1b1f7d12df.tar
MultiMC-af33b966848687048f0a9e409444bc1b1f7d12df.tar.gz
MultiMC-af33b966848687048f0a9e409444bc1b1f7d12df.tar.lz
MultiMC-af33b966848687048f0a9e409444bc1b1f7d12df.tar.xz
MultiMC-af33b966848687048f0a9e409444bc1b1f7d12df.zip
Merge branch 'feature_groupview' into integration_derpstances_groupview
-rw-r--r--CMakeLists.txt19
-rw-r--r--depends/groupview/CMakeLists.txt46
-rw-r--r--depends/groupview/include/categorizedsortfilterproxymodel.h175
-rw-r--r--depends/groupview/include/categorizedview.h332
-rw-r--r--depends/groupview/include/categorydrawer.h179
-rw-r--r--depends/groupview/include/groupview_config.h28
-rw-r--r--depends/groupview/src/categorizedsortfilterproxymodel.cpp168
-rw-r--r--depends/groupview/src/categorizedsortfilterproxymodel_p.h48
-rw-r--r--depends/groupview/src/categorizedview.cpp1692
-rw-r--r--depends/groupview/src/categorizedview_p.h160
-rw-r--r--depends/groupview/src/categorydrawer.cpp231
-rw-r--r--gui/MainWindow.cpp30
-rw-r--r--gui/MainWindow.h6
-rw-r--r--gui/dialogs/IconPickerDialog.cpp2
-rw-r--r--gui/groupview/Group.cpp270
-rw-r--r--gui/groupview/Group.h69
-rw-r--r--gui/groupview/GroupView.cpp929
-rw-r--r--gui/groupview/GroupView.h139
-rw-r--r--gui/groupview/GroupedProxyModel.cpp26
-rw-r--r--gui/groupview/GroupedProxyModel.h15
-rw-r--r--gui/groupview/InstanceDelegate.cpp (renamed from gui/widgets/InstanceDelegate.cpp)57
-rw-r--r--gui/groupview/InstanceDelegate.h (renamed from gui/widgets/InstanceDelegate.h)8
-rw-r--r--logic/lists/InstanceList.cpp8
-rw-r--r--logic/lists/InstanceList.h4
24 files changed, 1542 insertions, 3099 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ba28a042..4e7dea67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -186,7 +186,6 @@ configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/includ
ADD_DEFINITIONS(-DQUAZIP_STATIC)
ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
ADD_DEFINITIONS(-DLIBUTIL_STATIC)
-ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC)
######## Packaging/install paths setup ########
@@ -247,10 +246,6 @@ include_directories(${LIBUTIL_INCLUDE_DIR})
add_subdirectory(depends/settings)
include_directories(${LIBSETTINGS_INCLUDE_DIR})
-# Add the group view library.
-add_subdirectory(depends/groupview)
-include_directories(${LIBGROUPVIEW_INCLUDE_DIR})
-
# Add the updater
add_subdirectory(mmc_updater)
@@ -317,8 +312,6 @@ gui/dialogs/UpdateDialog.cpp
# GUI - widgets
gui/widgets/Common.h
gui/widgets/Common.cpp
-gui/widgets/InstanceDelegate.h
-gui/widgets/InstanceDelegate.cpp
gui/widgets/ModListView.h
gui/widgets/ModListView.cpp
gui/widgets/VersionListView.h
@@ -328,6 +321,16 @@ gui/widgets/LabeledToolButton.cpp
gui/widgets/MCModInfoFrame.h
gui/widgets/MCModInfoFrame.cpp
+# GUI - instance group view
+gui/groupview/Group.cpp
+gui/groupview/Group.h
+gui/groupview/GroupedProxyModel.cpp
+gui/groupview/GroupedProxyModel.h
+gui/groupview/GroupView.cpp
+gui/groupview/GroupView.h
+gui/groupview/InstanceDelegate.cpp
+gui/groupview/InstanceDelegate.h
+
# Base classes and infrastructure
logic/BaseVersion.h
logic/MinecraftVersion.h
@@ -588,7 +591,7 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS})
# Link
TARGET_LINK_LIBRARIES(MultiMC MultiMC_common)
-TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS})
+TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings ${MultiMC_LINK_ADDITIONAL_LIBS})
QT5_USE_MODULES(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
diff --git a/depends/groupview/CMakeLists.txt b/depends/groupview/CMakeLists.txt
deleted file mode 100644
index 417b1d3a..00000000
--- a/depends/groupview/CMakeLists.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-project(libGroupView)
-
-set(CMAKE_AUTOMOC ON)
-
-# Find Qt
-find_package(Qt5Core REQUIRED)
-find_package(Qt5Widgets REQUIRED)
-
-# Include Qt headers.
-include_directories(${Qt5Base_INCLUDE_DIRS})
-
-SET(LIBGROUPVIEW_HEADERS
-include/groupview_config.h
-
-# Public headers
-include/categorizedsortfilterproxymodel.h
-include/categorizedview.h
-include/categorydrawer.h
-
-# Private headers
-src/categorizedsortfilterproxymodel_p.h
-src/categorizedview_p.h
-)
-
-SET(LIBGROUPVIEW_SOURCES
-src/categorizedsortfilterproxymodel.cpp
-src/categorizedview.cpp
-src/categorydrawer.cpp
-)
-
-# Set the include dir path.
-SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
-
-# Include self.
-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-# Static link!
-ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC)
-
-add_definitions(-DLIBGROUPVIEW_LIBRARY)
-
-set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-
-add_library(libGroupView STATIC ${LIBGROUPVIEW_SOURCES} ${LIBGROUPVIEW_HEADERS})
-qt5_use_modules(libGroupView Core Widgets)
diff --git a/depends/groupview/include/categorizedsortfilterproxymodel.h b/depends/groupview/include/categorizedsortfilterproxymodel.h
deleted file mode 100644
index d90fb254..00000000
--- a/depends/groupview/include/categorizedsortfilterproxymodel.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * This file is part of the KDE project
- * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
- * Copyright (C) 2007 John Tapsell <tapsell@kde.org>
- *
- * 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 <QSortFilterProxyModel>
-
-#include <groupview_config.h>
-
-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 <ereslibre@kde.org>
- */
-class LIBGROUPVIEW_EXPORT 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/depends/groupview/include/categorizedview.h b/depends/groupview/include/categorizedview.h
deleted file mode 100644
index 81b1dbb1..00000000
--- a/depends/groupview/include/categorizedview.h
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- * This file is part of the KDE project
- * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
- *
- * 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 <QListView>
-
-#include <groupview_config.h>
-
-class KCategoryDrawer;
-
-/**
- * @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 <ereslibre@kde.org>
- */
-class LIBGROUPVIEW_EXPORT 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<KCategorizedView*>(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();
-
- /**
- * Signify that all item delegates size hints return the same fixed size
- */
- void setUniformItemWidths(bool enable);
-
- /**
- * Do all item delegate size hints return the same fixed size?
- */
- bool uniformItemWidths() const;
-
-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 &current,
- 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();
- virtual void slotCollapseOrExpandClicked ( QModelIndex );
-
-private:
- class Private;
- Private *const d;
-};
-
-#endif // KCATEGORIZEDVIEW_H
diff --git a/depends/groupview/include/categorydrawer.h b/depends/groupview/include/categorydrawer.h
deleted file mode 100644
index f37422ec..00000000
--- a/depends/groupview/include/categorydrawer.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * This file is part of the KDE project
- * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
- *
- * 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 <groupview_config.h>
-
-#include <QtCore/QObject>
-#include <QtGui/QMouseEvent>
-
-class QPainter;
-class QModelIndex;
-class QStyleOption;
-class KCategorizedView;
-
-
-/**
- * @since 4.5
- */
-class LIBGROUPVIEW_EXPORT KCategoryDrawer
- : public QObject
-{
- friend class KCategorizedView;
- Q_OBJECT
-
-
-public:
- KCategoryDrawer ( KCategorizedView *view );
- virtual ~KCategoryDrawer();
-
- /**
- * @return The view this category drawer is associated with.
- */
- KCategorizedView *view() const;
-
- /**
- * 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 );
-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;
-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 );
-};
-
-#endif // KCATEGORYDRAWER_H
diff --git a/depends/groupview/include/groupview_config.h b/depends/groupview/include/groupview_config.h
deleted file mode 100644
index c63acbde..00000000
--- a/depends/groupview/include/groupview_config.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QtCore/QtGlobal>
-
-#ifdef LIBGROUPVIEW_STATIC
- #define LIBGROUPVIEW_EXPORT
-#else
- #ifdef LIBGROUPVIEW_LIBRARY
- #define LIBGROUPVIEW_EXPORT Q_DECL_EXPORT
- #else
- #define LIBGROUPVIEW_EXPORT Q_DECL_IMPORT
- #endif
-#endif
diff --git a/depends/groupview/src/categorizedsortfilterproxymodel.cpp b/depends/groupview/src/categorizedsortfilterproxymodel.cpp
deleted file mode 100644
index 09da9dd3..00000000
--- a/depends/groupview/src/categorizedsortfilterproxymodel.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/**
- * This file is part of the KDE project
- * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
- * Copyright (C) 2007 John Tapsell <tapsell@kde.org>
- *
- * 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 "categorizedsortfilterproxymodel.h"
-#include "categorizedsortfilterproxymodel_p.h"
-
-#include <limits.h>
-
-#include <QItemSelection>
-#include <QStringList>
-#include <QSize>
-
-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 KStringHandler::naturalCompare ( 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/depends/groupview/src/categorizedsortfilterproxymodel_p.h b/depends/groupview/src/categorizedsortfilterproxymodel_p.h
deleted file mode 100644
index d7e7c9a0..00000000
--- a/depends/groupview/src/categorizedsortfilterproxymodel_p.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * This file is part of the KDE project
- * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
- * Copyright (C) 2007 John Tapsell <tapsell@kde.org>
- *
- * 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/depends/groupview/src/categorizedview.cpp b/depends/groupview/src/categorizedview.cpp
deleted file mode 100644
index 1345205c..00000000
--- a/depends/groupview/src/categorizedview.cpp
+++ /dev/null
@@ -1,1692 +0,0 @@
-/**
- * This file is part of the KDE project
- * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
- *
- * 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 "categorizedview.h"
-#include "categorizedview_p.h"
-
-#include <math.h> // trunc on C99 compliant systems
-//#include <kdefakes.h> // trunc for not C99 compliant systems
-
-#include <QPainter>
-#include <QScrollBar>
-#include <QPaintEvent>
-
-#include "categorydrawer.h"
-#include "categorizedsortfilterproxymodel.h"
-
-//BEGIN: Private part
-
-struct KCategorizedView::Private::Item
-{
- Item()
- : topLeft ( QPoint() )
- , size ( QSize() )
- {
- }
-
- QPoint topLeft;
- QSize size;
-};
-
-struct KCategorizedView::Private::Block
-{
- 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 = -1;
- 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<Item> 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 = false;
-
- // should we alternate its color ? is just a hint, could not be used
- bool alternate = false;
- bool collapsed = false;
-};
-
-KCategorizedView::Private::Private ( KCategorizedView *q )
- : q ( q )
- , hoveredBlock ( new Block() )
-{
-}
-
-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<QModelIndex, QModelIndex> 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<QString, Private::Block>::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<QString, Block>::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<Block> blockList = blocks.values();
- qSort ( blockList.begin(), blockList.end(), Block::lessThan );
- QList<int> firstIndexesRows;
- foreach ( const Block &block, blockList )
- {
- firstIndexesRows << block.firstIndex.row();
- }
- //END: order for marking as alternate those blocks that are alternate
- for ( QHash<QString, Private::Block>::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() /*|| q->uniformItemWidths()*/ )
- {
- const int relativeRow = index.row() - firstIndexRow;
- const QSize itemSize = q->sizeHintForIndex ( index );
- //HACK: Why is the -2 needed?
- const int maxItemsPerRow = qMax ( ( viewportWidth() - q->spacing() - 2 ) / ( 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() );
- QRect tempRect = q->visualRect ( prevIndex );
- tempRect = mapFromViewport ( tempRect );
- 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<KCategorizedSortFilterProxyModel*> ( 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::setUniformItemWidths(bool enable)
-{
- d->constantItemWidth = enable;
-}
-
-
-bool KCategorizedView::uniformItemWidths() const
-{
- return d->constantItemWidth;
-}
-
-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 )
-{
- disconnect ( d->categoryDrawer, SIGNAL ( collapseOrExpandClicked ( QModelIndex ) ),
- this, SLOT ( slotCollapseOrExpandClicked ( QModelIndex ) ) );
- d->categoryDrawer = categoryDrawer;
-
- connect ( d->categoryDrawer, SIGNAL ( collapseOrExpandClicked ( QModelIndex ) ),
- this, SLOT ( slotCollapseOrExpandClicked ( QModelIndex ) ) );
-}
-
-int KCategorizedView::categorySpacing() const
-{
- return d->categorySpacing;
-}
-
-void KCategorizedView::setCategorySpacing ( int categorySpacing )
-{
- if ( d->categorySpacing == categorySpacing )
- {
- return;
- }
-
- d->categorySpacing = categorySpacing;
-
- for ( QHash<QString, Private::Block>::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<QModelIndex, QModelIndex> intersecting = d->intersectingIndexesWithRect ( viewport()->rect().intersected ( event->rect() ) );
-
- QPainter p ( viewport() );
- p.save();
-
- Q_ASSERT ( selectionModel()->model() == d->proxyModel );
-
- //BEGIN: draw categories
- QHash<QString, Private::Block>::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<QModelIndex, QModelIndex> 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;
- }
- QHash<QString, Private::Block>::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->categoryDrawer && 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->categoryDrawer->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->categoryDrawer )
- {
- d->categoryDrawer->mouseMoved ( categoryIndex, option.rect, event );
- }
- viewport()->update ( option.rect );
- return;
- }
- ++it;
- }
- if ( d->categoryDrawer && 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->categoryDrawer->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->categoryDrawer )
- {
- QListView::mousePressEvent ( event );
- return;
- }
- QHash<QString, Private::Block>::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->categoryDrawer )
- {
- d->categoryDrawer->mouseButtonPressed ( categoryIndex, option.rect, 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();
- QHash<QString, Private::Block>::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->categoryDrawer )
- {
- d->categoryDrawer->mouseButtonReleased ( categoryIndex, option.rect, 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->categoryDrawer && 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->categoryDrawer->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() || uniformItemWidths() )
- {
- const QModelIndex current = currentIndex();
- const QSize itemSize = d->hasGrid() ? gridSize()
- : sizeHintForIndex ( current );
- const Private::Block &block = d->blocks[d->categoryForIndex ( current )];
- //HACK: Why is the -2 needed?
- const int maxItemsPerRow = qMax ( ( d->viewportWidth() - spacing() - 2 ) / ( itemSize.width() + spacing() ), 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() || uniformItemWidths() )
- {
- const QModelIndex current = currentIndex();
- const QSize itemSize = d->hasGrid() ? gridSize()
- : sizeHintForIndex ( current );
- const Private::Block &block = d->blocks[d->categoryForIndex ( current )];
- //HACK: Why is the -2 needed?
- const int maxItemsPerRow = qMax ( ( d->viewportWidth() - spacing() - 2 ) / ( itemSize.width() + spacing() ), 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<Private::Block> blockList = d->blocks.values();
- qSort ( blockList.begin(), blockList.end(), Private::Block::lessThan );
- QList<int> firstIndexesRows;
- foreach ( const Private::Block &block, blockList )
- {
- firstIndexesRows << block.firstIndex.row();
- }
- //END: order for marking as alternate those blocks that are alternate
- for ( QHash<QString, Private::Block>::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();
- const Qt::ScrollBarPolicy verticalP = verticalScrollBarPolicy(), horizontalP = horizontalScrollBarPolicy();
-
- //BEGIN bugs 213068, 287847 ------------------------------------------------------------
- /*
- * QListView::updateGeometries() has it's own opinion on whether the scrollbars should be visible (valid range) or not
- * and triggers a (sometimes additionally timered) resize through ::layoutChildren()
- * http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/itemviews/qlistview.cpp#line1499
- * (the comment above the main block isn't all accurate, layoutChldren is called regardless of the policy)
- *
- * As a result QListView and KCategorizedView occasionally started a race on the scrollbar visibility, effectively blocking the UI
- * So we prevent QListView from having an own opinion on the scrollbar visibility by
- * fixing it before calling the baseclass QListView::updateGeometries()
- *
- * Since the implicit show/hide by the followin range setting will cause further resizes if the policy is Qt::ScrollBarAsNeeded
- * we keep it static until we're done, then restore the original value and ultimately change the scrollbar visibility ourself.
- */
- if ( d->isCategorized() ) // important! - otherwise we'd pollute the setting if the view is initially not categorized
- {
- setVerticalScrollBarPolicy ( ( verticalP == Qt::ScrollBarAlwaysOn || verticalScrollBar()->isVisibleTo ( this ) ) ?
- Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff );
- setHorizontalScrollBarPolicy ( ( horizontalP == Qt::ScrollBarAlwaysOn || horizontalScrollBar()->isVisibleTo ( this ) ) ?
- Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff );
- }
- //END bugs 213068, 287847 --------------------------------------------------------------
-
- QListView::updateGeometries();
-
- if ( !d->isCategorized() )
- {
- return;
- }
-
- const int rowCount = d->proxyModel->rowCount();
- if ( !rowCount )
- {
- verticalScrollBar()->setRange ( 0, 0 );
- // unconditional, see function end todo
- horizontalScrollBar()->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 );
-
- //BEGIN bugs 213068, 287847 ------------------------------------------------------------
- // restoring values from above ...
- setVerticalScrollBarPolicy ( verticalP );
- setHorizontalScrollBarPolicy ( horizontalP );
- // ... and correct the visibility
- bool validRange = verticalScrollBar()->maximum() != verticalScrollBar()->minimum();
- if ( verticalP == Qt::ScrollBarAsNeeded && ( verticalScrollBar()->isVisibleTo ( this ) != validRange ) )
- verticalScrollBar()->setVisible ( validRange );
- validRange = horizontalScrollBar()->maximum() > horizontalScrollBar()->minimum();
- if ( horizontalP == Qt::ScrollBarAsNeeded && ( horizontalScrollBar()->isVisibleTo ( this ) != validRange ) )
- horizontalScrollBar()->setVisible ( validRange );
- //END bugs 213068, 287847 --------------------------------------------------------------
-}
-
-void KCategorizedView::currentChanged ( const QModelIndex &current,
- 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
-
-void KCategorizedView::slotCollapseOrExpandClicked ( QModelIndex idx )
-{
- d->_k_slotCollapseOrExpandClicked ( idx );
-}
-
-
-#include "categorizedview.moc"
diff --git a/depends/groupview/src/categorizedview_p.h b/depends/groupview/src/categorizedview_p.h
deleted file mode 100644
index 524bba3a..00000000
--- a/depends/groupview/src/categorizedview_p.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * This file is part of the KDE project
- * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
- *
- * 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<QModelIndex, QModelIndex> 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 = nullptr;
- KCategorizedSortFilterProxyModel *proxyModel = nullptr;
- KCategoryDrawer *categoryDrawer = nullptr;
- int categorySpacing = 5;
- bool alternatingBlockColors = false;
- bool collapsibleBlocks = false;
- bool constantItemWidth = false;
-
- // FIXME: this is some really weird logic. Investigate.
- Block *hoveredBlock;
- QString hoveredCategory;
- QModelIndex hoveredIndex;
-
- QPoint pressedPosition;
- QRect rubberBandRect;
-
- QHash<QString, Block> blocks;
-};
-
-#endif // KCATEGORIZEDVIEW_P_H
-
diff --git a/depends/groupview/src/categorydrawer.cpp b/depends/groupview/src/categorydrawer.cpp
deleted file mode 100644
index 214ce3b2..00000000
--- a/depends/groupview/src/categorydrawer.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-/**
- * This file is part of the KDE project
- * Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
- *
- * 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 "categorydrawer.h"
-
-#include <QPainter>
-#include <QStyleOption>
-#include <QApplication>
-
-#include <categorizedview.h>
-#include <categorizedsortfilterproxymodel.h>
-
-#define HORIZONTAL_HINT 3
-
-class KCategoryDrawer::Private
-{
-public:
- Private(KCategorizedView *view)
- : view(view)
- , leftMargin(0)
- , rightMargin(0)
- {
- }
-
- ~Private()
- {
- }
- KCategorizedView *view;
- int leftMargin;
- int rightMargin;
-};
-
-KCategoryDrawer::KCategoryDrawer(KCategorizedView *view)
- : QObject(view)
- , d(new Private(view))
-{
- 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;
- d->view = cd.d->view;
- return *this;
-}
-
-KCategorizedView *KCategoryDrawer::view() const
-{
- return d->view;
-}
-
-void KCategoryDrawer::mouseButtonPressed(const QModelIndex&, const QRect&, QMouseEvent *event)
-{
- event->ignore();
-}
-
-void KCategoryDrawer::mouseButtonReleased(const QModelIndex&, const QRect&, QMouseEvent *event)
-{
- event->ignore();
-}
-
-void KCategoryDrawer::mouseMoved(const QModelIndex&, const QRect&, QMouseEvent *event)
-{
- event->ignore();
-}
-
-void KCategoryDrawer::mouseButtonDoubleClicked(const QModelIndex&, const QRect&, QMouseEvent *event)
-{
- event->ignore();
-}
-
-void KCategoryDrawer::mouseLeft(const QModelIndex&, const QRect&)
-{
-}
-
-#include "categorydrawer.moc"
diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp
index 9977dc75..cd8ec926 100644
--- a/gui/MainWindow.cpp
+++ b/gui/MainWindow.cpp
@@ -26,6 +26,7 @@
#include <QInputDialog>
#include <QDesktopServices>
+#include <QKeyEvent>
#include <QUrl>
#include <QDir>
#include <QFileInfo>
@@ -37,12 +38,12 @@
#include "userutils.h"
#include "pathutils.h"
-#include "categorizedview.h"
-#include "categorydrawer.h"
+#include "gui/groupview/GroupView.h"
+#include "gui/groupview/InstanceDelegate.h"
#include "gui/Platform.h"
-#include "gui/widgets/InstanceDelegate.h"
+
#include "gui/widgets/LabeledToolButton.h"
#include "gui/dialogs/SettingsDialog.h"
@@ -140,21 +141,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
// Create the instance list widget
{
- view = new KCategorizedView(ui->centralWidget);
- drawer = new KCategoryDrawer(view);
+ view = new GroupView(ui->centralWidget);
+
view->setSelectionMode(QAbstractItemView::SingleSelection);
- view->setCategoryDrawer(drawer);
- view->setCollapsibleBlocks(true);
- view->setViewMode(QListView::IconMode);
- view->setFlow(QListView::LeftToRight);
- view->setWordWrap(true);
- view->setMouseTracking(true);
- view->viewport()->setAttribute(Qt::WA_Hover);
+ // view->setCategoryDrawer(drawer);
+ // view->setCollapsibleBlocks(true);
+ // view->setViewMode(QListView::IconMode);
+ // view->setFlow(QListView::LeftToRight);
+ // view->setWordWrap(true);
+ // view->setMouseTracking(true);
+ // view->viewport()->setAttribute(Qt::WA_Hover);
auto delegate = new ListViewDelegate();
view->setItemDelegate(delegate);
- view->setSpacing(10);
- view->setUniformItemWidths(true);
+ // view->setSpacing(10);
+ // view->setUniformItemWidths(true);
// do not show ugly blue border on the mac
view->setAttribute(Qt::WA_MacShowFocusRect, false);
@@ -331,7 +332,6 @@ MainWindow::~MainWindow()
{
delete ui;
delete proxymodel;
- delete drawer;
}
void MainWindow::showInstanceContextMenu(const QPoint &pos)
diff --git a/gui/MainWindow.h b/gui/MainWindow.h
index eeba2c26..4d9e165d 100644
--- a/gui/MainWindow.h
+++ b/gui/MainWindow.h
@@ -27,9 +27,6 @@
class QToolButton;
class LabeledToolButton;
class QLabel;
-class InstanceProxyModel;
-class KCategorizedView;
-class KCategoryDrawer;
class MinecraftProcess;
class ConsoleWindow;
@@ -185,8 +182,7 @@ protected:
private:
Ui::MainWindow *ui;
- KCategoryDrawer *drawer;
- KCategorizedView *view;
+ class GroupView *view;
InstanceProxyModel *proxymodel;
MinecraftProcess *proc;
ConsoleWindow *console;
diff --git a/gui/dialogs/IconPickerDialog.cpp b/gui/dialogs/IconPickerDialog.cpp
index f7970b37..9b1c26ff 100644
--- a/gui/dialogs/IconPickerDialog.cpp
+++ b/gui/dialogs/IconPickerDialog.cpp
@@ -23,7 +23,7 @@
#include "ui_IconPickerDialog.h"
#include "gui/Platform.h"
-#include "gui/widgets/InstanceDelegate.h"
+#include "gui/groupview/InstanceDelegate.h"
#include "logic/icons/IconList.h"
diff --git a/gui/groupview/Group.cpp b/gui/groupview/Group.cpp
new file mode 100644
index 00000000..f3319dc3
--- /dev/null
+++ b/gui/groupview/Group.cpp
@@ -0,0 +1,270 @@
+#include "Group.h"
+
+#include <QModelIndex>
+#include <QPainter>
+#include <QtMath>
+#include <QApplication>
+
+#include "GroupView.h"
+
+Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false)
+{
+}
+
+Group::Group(const Group *other)
+ : view(other->view), text(other->text), collapsed(other->collapsed)
+{
+}
+
+void Group::update()
+{
+ firstItemIndex = firstItem().row();
+
+ rowHeights = QVector<int>(numRows());
+ for (int i = 0; i < numRows(); ++i)
+ {
+ rowHeights[i] = view->categoryRowHeight(
+ view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0));
+ }
+}
+
+Group::HitResults Group::hitScan(const QPoint &pos) const
+{
+ Group::HitResults results = Group::NoHit;
+ int y_start = verticalPosition();
+ int body_start = y_start + headerHeight();
+ int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5?
+ int y = pos.y();
+ // int x = pos.x();
+ if (y < y_start)
+ {
+ results = Group::NoHit;
+ }
+ else if (y < body_start)
+ {
+ results = Group::HeaderHit;
+ int collapseSize = headerHeight() - 4;
+
+ // the icon
+ QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize);
+ if (iconRect.contains(pos))
+ {
+ results |= Group::CheckboxHit;
+ }
+ }
+ else if (y < body_end)
+ {
+ results |= Group::BodyHit;
+ }
+ return results;
+}
+
+void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option)
+{
+ /*
+ QStyleOptionViewItemV4 opt = option;
+ painter->save();
+
+ static const int margin = 2;
+ static const int spacing = 10;
+ int height = headerHeight();
+ int text_height = height - 2 * margin;
+
+ // set the text colors
+ QPalette::ColorGroup cg = QPalette::Normal;
+ painter->setPen(opt.palette.color(cg, QPalette::Text));
+
+ // set up geometry
+ QRect iconRect = QRect(view->m_leftMargin + margin, y + margin, text_height - 1, text_height - 1);
+ QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin);
+ QRect smallRect = iconSubrect.adjusted(margin, margin, -margin, -margin);
+ int midX = iconSubrect.center().x();
+ int midY = iconSubrect.center().y();
+
+ // checkboxy thingy
+ {
+ painter->drawRect(iconSubrect);
+
+ painter->setBrush(opt.palette.text());
+ painter->drawRect(smallRect.x(), midY, smallRect.width(), 2);
+ if(collapsed)
+ {
+ painter->drawRect(midX, smallRect.y(), 2, smallRect.height());
+ }
+ }
+
+ int x_left = iconRect.right();
+
+ if(text.length())
+ {
+ // the text
+ int text_width = painter->fontMetrics().width(text);
+ QRect textRect = QRect(x_left + spacing, y + margin, text_width, text_height);
+ x_left = textRect.right();
+ view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter,
+ opt.palette, true, text);
+ }
+ // the line
+ painter->drawLine(x_left + spacing, midY + 1, view->contentWidth() - view->m_rightMargin,
+ midY + 1);
+
+ painter->restore();
+ */
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ 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, text);
+ painter->restore();
+ }
+ //END: text
+}
+
+int Group::totalHeight() const
+{
+ return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'?
+}
+
+int Group::headerHeight() const
+{
+ int raw = view->viewport()->fontMetrics().height() + 4;
+ // add english. maybe. depends on font height.
+ if(raw % 2 == 0)
+ raw++;
+ return std::min( raw , 25 );
+}
+
+int Group::contentHeight() const
+{
+ if (collapsed)
+ {
+ return 0;
+ }
+ int result = 0;
+ for (int i = 0; i < rowHeights.size(); ++i)
+ {
+ result += rowHeights[i];
+ }
+ return result;
+}
+
+int Group::numRows() const
+{
+ return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow()));
+}
+
+int Group::verticalPosition() const
+{
+ return m_verticalPosition;
+}
+
+QList<QModelIndex> Group::items() const
+{
+ QList<QModelIndex> indices;
+ for (int i = 0; i < view->model()->rowCount(); ++i)
+ {
+ const QModelIndex index = view->model()->index(i, 0);
+ if (index.data(GroupViewRoles::GroupRole).toString() == text)
+ {
+ indices.append(index);
+ }
+ }
+ return indices;
+}
+
+int Group::numItems() const
+{
+ return items().size();
+}
+
+QModelIndex Group::firstItem() const
+{
+ QList<QModelIndex> indices = items();
+ return indices.isEmpty() ? QModelIndex() : indices.first();
+}
+
+QModelIndex Group::lastItem() const
+{
+ QList<QModelIndex> indices = items();
+ return indices.isEmpty() ? QModelIndex() : indices.last();
+}
diff --git a/gui/groupview/Group.h b/gui/groupview/Group.h
new file mode 100644
index 00000000..3b797f4c
--- /dev/null
+++ b/gui/groupview/Group.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <QString>
+#include <QRect>
+#include <QVector>
+#include <QStyleOption>
+
+class GroupView;
+class QPainter;
+class QModelIndex;
+
+struct Group
+{
+/* constructors */
+ Group(const QString &text, GroupView *view);
+ Group(const Group *other);
+
+/* data */
+ GroupView *view = nullptr;
+ QString text;
+ bool collapsed = false;
+ QVector<int> rowHeights;
+ int firstItemIndex = 0;
+ int m_verticalPosition = 0;
+
+/* logic */
+ /// do stuff. and things. TODO: redo.
+ void update();
+
+ /// draw the header at y-position.
+ void drawHeader(QPainter *painter, const QStyleOptionViewItem &option);
+
+ /// height of the group, in total. includes a small bit of padding.
+ int totalHeight() const;
+
+ /// height of the group header, in pixels
+ int headerHeight() const;
+
+ /// height of the group content, in pixels
+ int contentHeight() const;
+
+ /// the number of visual rows this group has
+ int numRows() const;
+
+ /// the height at which this group starts, in pixels
+ int verticalPosition() const;
+
+ enum HitResult
+ {
+ NoHit = 0x0,
+ TextHit = 0x1,
+ CheckboxHit = 0x2,
+ HeaderHit = 0x4,
+ BodyHit = 0x8
+ };
+ Q_DECLARE_FLAGS(HitResults, HitResult)
+
+ /// shoot! BANG! what did we hit?
+ HitResults hitScan (const QPoint &pos) const;
+
+ /// super derpy thing.
+ QList<QModelIndex> items() const;
+ /// I don't even
+ int numItems() const;
+ QModelIndex firstItem() const;
+ QModelIndex lastItem() const;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Group::HitResults)
diff --git a/gui/groupview/GroupView.cpp b/gui/groupview/GroupView.cpp
new file mode 100644
index 00000000..9954d743
--- /dev/null
+++ b/gui/groupview/GroupView.cpp
@@ -0,0 +1,929 @@
+#include "GroupView.h"
+
+#include <QPainter>
+#include <QApplication>
+#include <QtMath>
+#include <QDebug>
+#include <QMouseEvent>
+#include <QListView>
+#include <QPersistentModelIndex>
+#include <QDrag>
+#include <QMimeData>
+#include <QScrollBar>
+
+#include "Group.h"
+
+template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2)
+{
+ for (auto &item : l1)
+ {
+ if (t2.contains(item))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+GroupView::GroupView(QWidget *parent)
+ : QAbstractItemView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5),
+ m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0)
+{
+ // setViewMode(IconMode);
+ // setMovement(Snap);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ // setWordWrap(true);
+ // setDragDropMode(QListView::InternalMove);
+ setAcceptDrops(true);
+ m_spacing = 5;
+}
+
+GroupView::~GroupView()
+{
+ qDeleteAll(m_groups);
+ m_groups.clear();
+}
+
+void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QVector<int> &roles)
+{
+ /*
+ if (roles.contains(GroupViewRoles::GroupRole) || roles.contains(Qt::DisplayRole))
+ {
+ */
+ updateGeometries();
+ /*
+ }
+ */
+ viewport()->update();
+}
+void GroupView::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ updateGeometries();
+ viewport()->update();
+}
+
+void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ updateGeometries();
+ viewport()->update();
+}
+
+void GroupView::updateGeometries()
+{
+ int previousScroll = verticalScrollBar()->value();
+
+ QMap<QString, Group *> cats;
+
+ for (int i = 0; i < model()->rowCount(); ++i)
+ {
+ const QString groupName =
+ model()->index(i, 0).data(GroupViewRoles::GroupRole).toString();
+ if (!cats.contains(groupName))
+ {
+ Group *old = this->category(groupName);
+ if (old)
+ {
+ cats.insert(groupName, new Group(old));
+ }
+ else
+ {
+ cats.insert(groupName, new Group(groupName, this));
+ }
+ }
+ }
+
+ /*if (m_editedCategory)
+ {
+ m_editedCategory = cats[m_editedCategory->text];
+ }*/
+
+ qDeleteAll(m_groups);
+ m_groups = cats.values();
+
+ for (auto cat : m_groups)
+ {
+ cat->update();
+ }
+
+ if (m_groups.isEmpty())
+ {
+ verticalScrollBar()->setRange(0, 0);
+ }
+ else
+ {
+ int totalHeight = 0;
+ // top margin
+ totalHeight += m_categoryMargin;
+ for (auto category : m_groups)
+ {
+ category->m_verticalPosition = totalHeight;
+ totalHeight += category->totalHeight() + m_categoryMargin;
+ }
+ /*
+ // remove the last margin (we don't want it)
+ totalHeight -= m_categoryMargin;
+ // add a margin on top...
+ totalHeight += m_categoryMargin;
+ */
+ totalHeight += m_bottomMargin;
+ verticalScrollBar()->setRange(0, totalHeight - height());
+ }
+
+ verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum()));
+
+ viewport()->update();
+}
+
+bool GroupView::isIndexHidden(const QModelIndex &index) const
+{
+ Group *cat = category(index);
+ if (cat)
+ {
+ return cat->collapsed;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+Group *GroupView::category(const QModelIndex &index) const
+{
+ return category(index.data(GroupViewRoles::GroupRole).toString());
+}
+
+Group *GroupView::category(const QString &cat) const
+{
+ for (auto group : m_groups)
+ {
+ if (group->text == cat)
+ {
+ return group;
+ }
+ }
+ return nullptr;
+}
+
+Group *GroupView::categoryAt(const QPoint &pos) const
+{
+ for (auto group : m_groups)
+ {
+ if(group->hitScan(pos) & Group::CheckboxHit)
+ {
+ return group;
+ }
+ }
+ return nullptr;
+}
+
+int GroupView::itemsPerRow() const
+{
+ return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing));
+}
+
+int GroupView::contentWidth() const
+{
+ return width() - m_leftMargin - m_rightMargin;
+}
+
+int GroupView::itemWidth() const
+{
+ return itemDelegate()
+ ->sizeHint(viewOptions(), model()->index(model()->rowCount() - 1, 0))
+ .width();
+}
+
+int GroupView::categoryRowHeight(const QModelIndex &index) const
+{
+ QModelIndexList indices;
+ int internalRow = categoryInternalPosition(index).second;
+ for (auto &i : category(index)->items())
+ {
+ if (categoryInternalPosition(i).second == internalRow)
+ {
+ indices.append(i);
+ }
+ }
+
+ int largestHeight = 0;
+ for (auto &i : indices)
+ {
+ largestHeight =
+ qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height());
+ }
+ return largestHeight + m_spacing;
+}
+
+QPair<int, int> GroupView::categoryInternalPosition(const QModelIndex &index) const
+{
+ QList<QModelIndex> indices = category(index)->items();
+ int x = 0;
+ int y = 0;
+ const int perRow = itemsPerRow();
+ for (int i = 0; i < indices.size(); ++i)
+ {
+ if (indices.at(i) == index)
+ {
+ break;
+ }
+ ++x;
+ if (x == perRow)
+ {
+ x = 0;
+ ++y;
+ }
+ }
+ return qMakePair(x, y);
+}
+
+int GroupView::categoryInternalRowTop(const QModelIndex &index) const
+{
+ Group *cat = category(index);
+ int categoryInternalRow = categoryInternalPosition(index).second;
+ int result = 0;
+ for (int i = 0; i < categoryInternalRow; ++i)
+ {
+ result += cat->rowHeights.at(i);
+ }
+ return result;
+}
+
+int GroupView::itemHeightForCategoryRow(const Group *category, const int internalRow) const
+{
+ for (auto &i : category->items())
+ {
+ QPair<int, int> pos = categoryInternalPosition(i);
+ if (pos.second == internalRow)
+ {
+ return categoryRowHeight(i);
+ }
+ }
+ return -1;
+}
+
+void GroupView::mousePressEvent(QMouseEvent *event)
+{
+ // endCategoryEditor();
+
+ QPoint pos = event->pos() + offset();
+ QPersistentModelIndex index = indexAt(pos);
+
+ m_pressedIndex = index;
+ m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex);
+ QItemSelectionModel::SelectionFlags selection_flags = selectionCommand(index, event);
+ m_pressedPosition = pos;
+
+ m_pressedCategory = categoryAt(m_pressedPosition);
+ if (m_pressedCategory)
+ {
+ setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState);
+ event->accept();
+ return;
+ }
+
+ if (index.isValid() && (index.flags() & Qt::ItemIsEnabled))
+ {
+ // we disable scrollTo for mouse press so the item doesn't change position
+ // when the user is interacting with it (ie. clicking on it)
+ bool autoScroll = hasAutoScroll();
+ setAutoScroll(false);
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ setAutoScroll(autoScroll);
+ QRect rect(m_pressedPosition, pos);
+ setSelection(rect, QItemSelectionModel::ClearAndSelect);
+
+ // signal handlers may change the model
+ emit pressed(index);
+ }
+ else
+ {
+ // Forces a finalize() even if mouse is pressed, but not on a item
+ selectionModel()->select(QModelIndex(), QItemSelectionModel::Select);
+ }
+}
+
+void GroupView::mouseMoveEvent(QMouseEvent *event)
+{
+ QPoint topLeft;
+ QPoint pos = event->pos() + offset();
+
+ if (state() == ExpandingState || state() == CollapsingState)
+ {
+ return;
+ }
+
+ if (state() == DraggingState)
+ {
+ topLeft = m_pressedPosition - offset();
+ if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance())
+ {
+ m_pressedIndex = QModelIndex();
+ startDrag(model()->supportedDragActions());
+ setState(NoState);
+ stopAutoScroll();
+ }
+ return;
+ }
+
+ if (selectionMode() != SingleSelection)
+ {
+ topLeft = m_pressedPosition - offset();
+ }
+ else
+ {
+ topLeft = pos;
+ }
+
+ if (m_pressedIndex.isValid() && (state() != DragSelectingState) &&
+ (event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty())
+ {
+ setState(DraggingState);
+ return;
+ }
+
+ if ((event->buttons() & Qt::LeftButton) && selectionModel())
+ {
+ setState(DragSelectingState);
+
+ setSelection(QRect(pos, pos), QItemSelectionModel::ClearAndSelect);
+ QModelIndex index = indexAt(pos);
+
+ // set at the end because it might scroll the view
+ if (index.isValid() && (index != selectionModel()->currentIndex()) &&
+ (index.flags() & Qt::ItemIsEnabled))
+ {
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ }
+ }
+}
+
+void GroupView::mouseReleaseEvent(QMouseEvent *event)
+{
+ QPoint pos = event->pos() + offset();
+ QPersistentModelIndex index = indexAt(pos);
+
+ bool click = (index == m_pressedIndex && index.isValid()) ||
+ (m_pressedCategory && m_pressedCategory == categoryAt(pos));
+
+ if (click && m_pressedCategory)
+ {
+ if (state() == ExpandingState)
+ {
+ m_pressedCategory->collapsed = false;
+ updateGeometries();
+ viewport()->update();
+ event->accept();
+ return;
+ }
+ else if (state() == CollapsingState)
+ {
+ m_pressedCategory->collapsed = true;
+ updateGeometries();
+ viewport()->update();
+ event->accept();
+ return;
+ }
+ }
+
+ m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate;
+
+ setState(NoState);
+
+ if (click)
+ {
+ if (event->button() == Qt::LeftButton)
+ {
+ emit clicked(index);
+ }
+ QStyleOptionViewItem option = viewOptions();
+ if (m_pressedAlreadySelected)
+ {
+ option.state |= QStyle::State_Selected;
+ }
+ if ((model()->flags(index) & Qt::ItemIsEnabled) &&
+ style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this))
+ {
+ emit activated(index);
+ }
+ }
+}
+
+void GroupView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ QModelIndex index = indexAt(event->pos());
+ if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index))
+ {
+ QMouseEvent me(QEvent::MouseButtonPress, event->localPos(), event->windowPos(),
+ event->screenPos(), event->button(), event->buttons(),
+ event->modifiers());
+ mousePressEvent(&me);
+ return;
+ }
+ // signal handlers may change the model
+ QPersistentModelIndex persistent = index;
+ emit doubleClicked(persistent);
+}
+
+void GroupView::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this->viewport());
+
+ QStyleOptionViewItemV4 option(viewOptions());
+ option.widget = this;
+
+ int wpWidth = viewport()->width();
+ option.rect.setWidth(wpWidth);
+ for (int i = 0; i < m_groups.size(); ++i)
+ {
+ Group *category = m_groups.at(i);
+ int y = category->verticalPosition();
+ y -= verticalOffset();
+ QRect backup = option.rect;
+ int height = category->totalHeight();
+ option.rect.setTop(y);
+ option.rect.setHeight(height);
+ option.rect.setLeft(m_leftMargin);
+ option.rect.setRight(wpWidth - m_rightMargin);
+ category->drawHeader(&painter, option);
+ y += category->totalHeight() + m_categoryMargin;
+ option.rect = backup;
+ }
+
+ for (int i = 0; i < model()->rowCount(); ++i)
+ {
+ const QModelIndex index = model()->index(i, 0);
+ if (isIndexHidden(index))
+ {
+ continue;
+ }
+ Qt::ItemFlags flags = index.flags();
+ option.rect = visualRect(index);
+ option.features |=
+ QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway?
+ if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index))
+ {
+ 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;
+ }
+ itemDelegate()->paint(&painter, option, index);
+ }
+
+ /*
+ * Drop indicators for manual reordering...
+ */
+#if 0
+ if (!m_lastDragPosition.isNull())
+ {
+ QPair<Group *, int> pair = rowDropPos(m_lastDragPosition);
+ Group *category = pair.first;
+ int row = pair.second;
+ if (category)
+ {
+ int internalRow = row - category->firstItemIndex;
+ QLine line;
+ if (internalRow >= category->numItems())
+ {
+ QRect toTheRightOfRect = visualRect(category->lastItem());
+ line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight());
+ }
+ else
+ {
+ QRect toTheLeftOfRect = visualRect(model()->index(row, 0));
+ line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft());
+ }
+ painter.save();
+ painter.setPen(QPen(Qt::black, 3));
+ painter.drawLine(line);
+ painter.restore();
+ }
+ }
+#endif
+}
+
+void GroupView::resizeEvent(QResizeEvent *event)
+{
+ // QListView::resizeEvent(event);
+
+ // if (m_categoryEditor)
+ // {
+ // m_categoryEditor->resize(qMax(contentWidth() / 2,
+ // m_editedCategory->textRect.width()),
+ // m_categoryEditor->height());
+ // }
+
+ updateGeometries();
+}
+
+void GroupView::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (!isDragEventAccepted(event))
+ {
+ return;
+ }
+ m_lastDragPosition = event->pos() + offset();
+ viewport()->update();
+ event->accept();
+}
+
+void GroupView::dragMoveEvent(QDragMoveEvent *event)
+{
+ if (!isDragEventAccepted(event))
+ {
+ return;
+ }
+ m_lastDragPosition = event->pos() + offset();
+ viewport()->update();
+ event->accept();
+}
+
+void GroupView::dragLeaveEvent(QDragLeaveEvent *event)
+{
+ m_lastDragPosition = QPoint();
+ viewport()->update();
+}
+
+void GroupView::dropEvent(QDropEvent *event)
+{
+ m_lastDragPosition = QPoint();
+
+ stopAutoScroll();
+ setState(NoState);
+
+ if (event->source() != this || !(event->possibleActions() & Qt::MoveAction))
+ {
+ return;
+ }
+
+ QPair<Group *, int> dropPos = rowDropPos(event->pos() + offset());
+ const Group *category = dropPos.first;
+ const int row = dropPos.second;
+
+ if (row == -1)
+ {
+ viewport()->update();
+ return;
+ }
+
+ const QString categoryText = category->text;
+ if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex()))
+ {
+ model()->setData(model()->index(row, 0), categoryText,
+ GroupViewRoles::GroupRole);
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+ updateGeometries();
+ viewport()->update();
+}
+
+void GroupView::startDrag(Qt::DropActions supportedActions)
+{
+ QModelIndexList indexes = selectionModel()->selectedIndexes();
+ if (indexes.count() > 0)
+ {
+ QMimeData *data = model()->mimeData(indexes);
+ if (!data)
+ {
+ return;
+ }
+ QRect rect;
+ QPixmap pixmap = renderToPixmap(indexes, &rect);
+ //rect.translate(offset());
+ // rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
+ QDrag *drag = new QDrag(this);
+ drag->setPixmap(pixmap);
+ drag->setMimeData(data);
+ Qt::DropAction defaultDropAction = Qt::IgnoreAction;
+ if (this->defaultDropAction() != Qt::IgnoreAction &&
+ (supportedActions & this->defaultDropAction()))
+ {
+ defaultDropAction = this->defaultDropAction();
+ }
+ if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction)
+ {
+ const QItemSelection selection = selectionModel()->selection();
+
+ for (auto it = selection.constBegin(); it != selection.constEnd(); ++it)
+ {
+ QModelIndex parent = (*it).parent();
+ if ((*it).left() != 0)
+ {
+ continue;
+ }
+ if ((*it).right() != (model()->columnCount(parent) - 1))
+ {
+ continue;
+ }
+ int count = (*it).bottom() - (*it).top() + 1;
+ model()->removeRows((*it).top(), count, parent);
+ }
+ }
+ }
+}
+
+QRect GroupView::visualRect(const QModelIndex &index) const
+{
+ return geometryRect(index).translated(-offset());
+}
+
+QRect GroupView::geometryRect(const QModelIndex &index) const
+{
+ if (!index.isValid() || isIndexHidden(index) || index.column() > 0)
+ {
+ return QRect();
+ }
+
+ const Group *cat = category(index);
+ QPair<int, int> pos = categoryInternalPosition(index);
+ int x = pos.first;
+ // int y = pos.second;
+
+ QRect out;
+ out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + categoryInternalRowTop(index));
+ out.setLeft(m_spacing + x * (itemWidth() + m_spacing));
+ out.setSize(itemDelegate()->sizeHint(viewOptions(), index));
+
+ return out;
+}
+
+/*
+void CategorizedView::startCategoryEditor(Category *category)
+{
+ if (m_categoryEditor != 0)
+ {
+ return;
+ }
+ m_editedCategory = category;
+ m_categoryEditor = new QLineEdit(m_editedCategory->text, this);
+ QRect rect = m_editedCategory->textRect;
+ rect.setWidth(qMax(contentWidth() / 2, rect.width()));
+ m_categoryEditor->setGeometry(rect);
+ m_categoryEditor->show();
+ m_categoryEditor->setFocus();
+ connect(m_categoryEditor, &QLineEdit::returnPressed, this,
+&CategorizedView::endCategoryEditor);
+}
+
+void CategorizedView::endCategoryEditor()
+{
+ if (m_categoryEditor == 0)
+ {
+ return;
+ }
+ m_editedCategory->text = m_categoryEditor->text();
+ m_updatesDisabled = true;
+ foreach (const QModelIndex &index, itemsForCategory(m_editedCategory))
+ {
+ const_cast<QAbstractItemModel *>(index.model())->setData(index,
+m_categoryEditor->text(), CategoryRole);
+ }
+ m_updatesDisabled = false;
+ delete m_categoryEditor;
+ m_categoryEditor = 0;
+ m_editedCategory = 0;
+ updateGeometries();
+}
+*/
+
+QModelIndex GroupView::indexAt(const QPoint &point) const
+{
+ for (int i = 0; i < model()->rowCount(); ++i)
+ {
+ QModelIndex index = model()->index(i, 0);
+ if (geometryRect(index).contains(point))
+ {
+ return index;
+ }
+ }
+ return QModelIndex();
+}
+
+void GroupView::setSelection(const QRect &rect,
+ const QItemSelectionModel::SelectionFlags commands)
+{
+ for (int i = 0; i < model()->rowCount(); ++i)
+ {
+ QModelIndex index = model()->index(i, 0);
+ QRect itemRect = geometryRect(index);
+ if (itemRect.intersects(rect))
+ {
+ selectionModel()->select(index, commands);
+ update(itemRect.translated(-offset()));
+ }
+ }
+
+}
+
+QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const
+{
+ Q_ASSERT(r);
+ auto paintPairs = draggablePaintPairs(indices, r);
+ if (paintPairs.isEmpty())
+ {
+ return QPixmap();
+ }
+ QPixmap pixmap(r->size());
+ pixmap.fill(Qt::transparent);
+ QPainter painter(&pixmap);
+ QStyleOptionViewItem option = viewOptions();
+ option.state |= QStyle::State_Selected;
+ for (int j = 0; j < paintPairs.count(); ++j)
+ {
+ option.rect = paintPairs.at(j).first.translated(-r->topLeft());
+ const QModelIndex &current = paintPairs.at(j).second;
+ itemDelegate()->paint(&painter, option, current);
+ }
+ return pixmap;
+}
+
+QList<QPair<QRect, QModelIndex>> GroupView::draggablePaintPairs(const QModelIndexList &indices,
+ QRect *r) const
+{
+ Q_ASSERT(r);
+ QRect &rect = *r;
+ QList<QPair<QRect, QModelIndex>> ret;
+ for (int i = 0; i < indices.count(); ++i)
+ {
+ const QModelIndex &index = indices.at(i);
+ const QRect current = geometryRect(index);
+ ret += qMakePair(current, index);
+ rect |= current;
+ }
+ return ret;
+}
+
+bool GroupView::isDragEventAccepted(QDropEvent *event)
+{
+ if (event->source() != this)
+ {
+ return false;
+ }
+ if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes()))
+ {
+ return false;
+ }
+ if (!model()->canDropMimeData(event->mimeData(), event->dropAction(),
+ rowDropPos(event->pos()).second, 0, QModelIndex()))
+ {
+ return false;
+ }
+ return true;
+}
+
+QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
+{
+ // check that we aren't on a category header and calculate which category we're in
+ Group *category = 0;
+ {
+ int y = 0;
+ for (auto cat : m_groups)
+ {
+ if (pos.y() > y && pos.y() < (y + cat->headerHeight()))
+ {
+ return qMakePair(nullptr, -1);
+ }
+ y += cat->totalHeight() + m_categoryMargin;
+ if (pos.y() < y)
+ {
+ category = cat;
+ break;
+ }
+ }
+ if (category == 0)
+ {
+ return qMakePair(nullptr, -1);
+ }
+ }
+
+ QList<QModelIndex> indices = category->items();
+
+ // calculate the internal column
+ int internalColumn = -1;
+ {
+ const int itemWidth = this->itemWidth();
+ if (pos.x() >= (itemWidth * itemsPerRow()))
+ {
+ internalColumn = itemsPerRow();
+ }
+ else
+ {
+ for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 /*spacing()*/, ++c)
+ {
+ if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2))
+ {
+ internalColumn = c;
+ break;
+ }
+ }
+ }
+ if (internalColumn == -1)
+ {
+ return qMakePair(nullptr, -1);
+ }
+ }
+
+ // calculate the internal row
+ int internalRow = -1;
+ {
+ // FIXME rework the drag and drop code
+ const int top = category->verticalPosition();
+ for (int r = 0, h = top; r < category->numRows();
+ h += itemHeightForCategoryRow(category, r), ++r)
+ {
+ if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r)))
+ {
+ internalRow = r;
+ break;
+ }
+ }
+ if (internalRow == -1)
+ {
+ return qMakePair(nullptr, -1);
+ }
+ // this happens if we're in the margin between a one category and another
+ // categories header
+ if (internalRow > (indices.size() / itemsPerRow()))
+ {
+ return qMakePair(nullptr, -1);
+ }
+ }
+
+ // flaten the internalColumn/internalRow to one row
+ int categoryRow = internalRow * itemsPerRow() + internalColumn;
+
+ // this is used if we're past the last item
+ if (categoryRow >= indices.size())
+ {
+ return qMakePair(category, indices.last().row() + 1);
+ }
+
+ return qMakePair(category, indices.at(categoryRow).row());
+}
+
+QPoint GroupView::offset() const
+{
+ return QPoint(horizontalOffset(), verticalOffset());
+}
+
+QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ QRegion region;
+ for (auto &range : selection)
+ {
+ int start_row = range.top();
+ int end_row = range.bottom();
+ for (int row = start_row; row <= end_row; ++row)
+ {
+ int start_column = range.left();
+ int end_column = range.right();
+ for (int column = start_column; column <= end_column; ++column)
+ {
+ QModelIndex index = model()->index(row, column, rootIndex());
+ region += visualRect(index); // OK
+ }
+ }
+ }
+ return region;
+}
+QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction,
+ Qt::KeyboardModifiers modifiers)
+{
+ auto current = currentIndex();
+ if(!current.isValid())
+ {
+ qDebug() << "model row: invalid";
+ return current;
+ }
+ qDebug() << "model row: " << current.row();
+ auto cat = category(current);
+ int i = m_groups.indexOf(cat);
+ if(i >= 0)
+ {
+ // this is a pile of something foul
+ auto real_group = m_groups[i];
+ int beginning_row = 0;
+ for(auto group: m_groups)
+ {
+ if(group == real_group)
+ break;
+ beginning_row += group->numRows();
+ }
+ qDebug() << "category: " << real_group->text;
+ QPair<int, int> pos = categoryInternalPosition(current);
+ int row = beginning_row + pos.second;
+ qDebug() << "row: " << row;
+ qDebug() << "column: " << pos.first;
+ }
+ return current;
+}
diff --git a/gui/groupview/GroupView.h b/gui/groupview/GroupView.h
new file mode 100644
index 00000000..329a3503
--- /dev/null
+++ b/gui/groupview/GroupView.h
@@ -0,0 +1,139 @@
+#pragma once
+
+#include <QListView>
+#include <QLineEdit>
+#include <QScrollBar>
+
+struct GroupViewRoles
+{
+ enum
+ {
+ GroupRole = Qt::UserRole,
+ ProgressValueRole,
+ ProgressMaximumRole
+ };
+};
+
+struct Group;
+
+class GroupView : public QAbstractItemView
+{
+ Q_OBJECT
+
+public:
+ GroupView(QWidget *parent = 0);
+ ~GroupView();
+
+ QRect geometryRect(const QModelIndex &index) const;
+ virtual QRect visualRect(const QModelIndex &index) const override;
+ QModelIndex indexAt(const QPoint &point) const;
+ void setSelection(const QRect &rect,
+ const QItemSelectionModel::SelectionFlags commands) override;
+
+ virtual int horizontalOffset() const override
+ {
+ return horizontalScrollBar()->value();
+ }
+
+ virtual int verticalOffset() const override
+ {
+ return verticalScrollBar()->value();
+ }
+
+ virtual void scrollContentsBy(int dx, int dy) override
+ {
+ scrollDirtyRegion(dx, dy);
+ viewport()->scroll(dx, dy);
+ }
+
+ /*
+ * TODO!
+ */
+ virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override
+ {
+ return;
+ }
+
+ virtual QModelIndex moveCursor(CursorAction cursorAction,
+ Qt::KeyboardModifiers modifiers) override;
+
+ virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override;
+
+protected
+slots:
+ virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QVector<int> &roles) override;
+ virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
+ virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override;
+ virtual void updateGeometries() override;
+
+protected:
+ virtual bool isIndexHidden(const QModelIndex &index) const override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dragMoveEvent(QDragMoveEvent *event) override;
+ void dragLeaveEvent(QDragLeaveEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+
+ void startDrag(Qt::DropActions supportedActions) override;
+
+private:
+ friend struct Group;
+
+ QList<Group *> m_groups;
+
+ int m_leftMargin;
+ int m_rightMargin;
+ int m_bottomMargin;
+ int m_categoryMargin;
+
+ // bool m_updatesDisabled;
+
+ Group *category(const QModelIndex &index) const;
+ Group *category(const QString &cat) const;
+ Group *categoryAt(const QPoint &pos) const;
+
+ int itemsPerRow() const;
+ int contentWidth() const;
+
+private:
+ int itemWidth() const;
+ int categoryRowHeight(const QModelIndex &index) const;
+
+ /*QLineEdit *m_categoryEditor;
+ Category *m_editedCategory;
+ void startCategoryEditor(Category *category);
+
+private slots:
+ void endCategoryEditor();*/
+
+private: /* variables */
+ QPoint m_pressedPosition;
+ QPersistentModelIndex m_pressedIndex;
+ bool m_pressedAlreadySelected;
+ Group *m_pressedCategory;
+ QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag;
+ QPoint m_lastDragPosition;
+ int m_spacing = 5;
+
+private: /* methods */
+ QPair<int, int> categoryInternalPosition(const QModelIndex &index) const;
+ int categoryInternalRowTop(const QModelIndex &index) const;
+ int itemHeightForCategoryRow(const Group *category, const int internalRow) const;
+
+ QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const;
+ QList<QPair<QRect, QModelIndex>> draggablePaintPairs(const QModelIndexList &indices,
+ QRect *r) const;
+
+ bool isDragEventAccepted(QDropEvent *event);
+
+ QPair<Group *, int> rowDropPos(const QPoint &pos);
+
+ QPoint offset() const;
+};
diff --git a/gui/groupview/GroupedProxyModel.cpp b/gui/groupview/GroupedProxyModel.cpp
new file mode 100644
index 00000000..d9d6ac78
--- /dev/null
+++ b/gui/groupview/GroupedProxyModel.cpp
@@ -0,0 +1,26 @@
+#include "GroupedProxyModel.h"
+
+#include "GroupView.h"
+
+GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
+{
+}
+
+bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ const QString leftCategory = left.data(GroupViewRoles::GroupRole).toString();
+ const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString();
+ if (leftCategory == rightCategory)
+ {
+ return subSortLessThan(left, right);
+ }
+ else
+ {
+ return leftCategory < rightCategory;
+ }
+}
+
+bool GroupedProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ return left.row() < right.row();
+}
diff --git a/gui/groupview/GroupedProxyModel.h b/gui/groupview/GroupedProxyModel.h
new file mode 100644
index 00000000..12edee0f
--- /dev/null
+++ b/gui/groupview/GroupedProxyModel.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <QSortFilterProxyModel>
+
+class GroupedProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ GroupedProxyModel(QObject *parent = 0);
+
+protected:
+ virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
+ virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const;
+};
diff --git a/gui/widgets/InstanceDelegate.cpp b/gui/groupview/InstanceDelegate.cpp
index 33da7130..8a273758 100644
--- a/gui/widgets/InstanceDelegate.cpp
+++ b/gui/groupview/InstanceDelegate.cpp
@@ -19,7 +19,32 @@
#include <QTextLayout>
#include <QApplication>
#include <QtCore/qmath.h>
-#include "Common.h"
+
+#include "GroupView.h"
+
+// Origin: Qt
+static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
+ qreal &widthUsed)
+{
+ height = 0;
+ widthUsed = 0;
+ textLayout.beginLayout();
+ QString str = textLayout.text();
+ while (true)
+ {
+ QTextLine line = textLayout.createLine();
+ if (!line.isValid())
+ break;
+ if (line.textLength() == 0)
+ break;
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+ widthUsed = qMax(widthUsed, line.naturalTextWidth());
+ }
+ textLayout.endLayout();
+}
+
#define QFIXED_MAX (INT_MAX / 256)
ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
@@ -62,6 +87,27 @@ void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, cons
painter->setRenderHint(QPainter::Antialiasing);
}
+// TODO this can be made a lot prettier
+void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option,
+ const int value, const int maximum)
+{
+ if (maximum == 0 || value == maximum)
+ {
+ return;
+ }
+
+ painter->save();
+
+ qreal percent = (qreal)value / (qreal)maximum;
+ QColor color = option.palette.color(QPalette::Dark);
+ color.setAlphaF(0.70f);
+ painter->setBrush(color);
+ painter->setPen(QPen(QBrush(), 0));
+ painter->drawPie(option.rect, 90 * 16, -percent * 360 * 16);
+
+ painter->restore();
+}
+
static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option)
{
QStyle *style = option->widget ? option->widget->style() : QApplication::style();
@@ -127,6 +173,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
opt2.palette.setCurrentColorGroup(cg);
// fill in background, if any
+
if (opt.backgroundBrush.style() != Qt::NoBrush)
{
QPointF oldBO = painter->brushOrigin();
@@ -135,6 +182,9 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
painter->setBrushOrigin(oldBO);
}
+ drawSelectionRect(painter, opt2, textHighlightRect);
+
+ /*
if (opt.showDecorationSelected)
{
drawSelectionRect(painter, opt2, opt.rect);
@@ -154,6 +204,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
drawFocusRect(painter, opt2, textHighlightRect);
}
}
+ */
}
// draw the icon
@@ -206,6 +257,10 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
line.draw(painter, position);
}
+ drawProgressOverlay(painter, opt,
+ index.data(GroupViewRoles::ProgressValueRole).toInt(),
+ index.data(GroupViewRoles::ProgressMaximumRole).toInt());
+
painter->restore();
}
diff --git a/gui/widgets/InstanceDelegate.h b/gui/groupview/InstanceDelegate.h
index 6f924405..de2f429b 100644
--- a/gui/widgets/InstanceDelegate.h
+++ b/gui/groupview/InstanceDelegate.h
@@ -20,8 +20,10 @@
class ListViewDelegate : public QStyledItemDelegate
{
public:
- explicit ListViewDelegate ( QObject* parent = 0 );
+ explicit ListViewDelegate(QObject *parent = 0);
+
protected:
- void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
- QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp
index 0d4eab95..935f9fd3 100644
--- a/logic/lists/InstanceList.cpp
+++ b/logic/lists/InstanceList.cpp
@@ -33,6 +33,7 @@
#include "logic/BaseInstance.h"
#include "logic/InstanceFactory.h"
#include "logger/QsLog.h"
+#include <gui/groupview/GroupView.h>
const static int GROUP_FILE_FORMAT_VERSION = 1;
@@ -96,8 +97,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
return MMC->icons()->getIcon(key);
}
// for now.
- case KCategorizedSortFilterProxyModel::CategorySortRole:
- case KCategorizedSortFilterProxyModel::CategoryDisplayRole:
+ case GroupViewRoles::GroupRole:
{
return pdata->group();
}
@@ -585,10 +585,8 @@ void InstanceList::propertiesChanged(BaseInstance *inst)
}
InstanceProxyModel::InstanceProxyModel(QObject *parent)
- : KCategorizedSortFilterProxyModel(parent)
+ : GroupedProxyModel(parent)
{
- // disable since by default we are globally sorting by date:
- setCategorizedModel(true);
}
bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h
index 0ce808e5..cda51a03 100644
--- a/logic/lists/InstanceList.h
+++ b/logic/lists/InstanceList.h
@@ -18,7 +18,7 @@
#include <QObject>
#include <QAbstractListModel>
#include <QSet>
-#include "categorizedsortfilterproxymodel.h"
+#include <gui/groupview/GroupedProxyModel.h>
#include <QIcon>
#include "logic/BaseInstance.h"
@@ -129,7 +129,7 @@ protected:
QSet<QString> m_groups;
};
-class InstanceProxyModel : public KCategorizedSortFilterProxyModel
+class InstanceProxyModel : public GroupedProxyModel
{
public:
explicit InstanceProxyModel(QObject *parent = 0);