Merge branch 'feature_groupview' into integration_derpstances_groupview
This commit is contained in:
commit
af33b96684
@ -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})
|
||||
|
||||
|
@ -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)
|
@ -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
|
@ -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 ¤t,
|
||||
const QModelIndex &previous );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void dataChanged ( const QModelIndex &topLeft,
|
||||
const QModelIndex &bottomRight );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void rowsInserted ( const QModelIndex &parent,
|
||||
int start,
|
||||
int end );
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* @internal
|
||||
* Reposition items as needed.
|
||||
*/
|
||||
virtual void slotLayoutChanged();
|
||||
virtual void slotCollapseOrExpandClicked ( QModelIndex );
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif // KCATEGORIZEDVIEW_H
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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"
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
270
gui/groupview/Group.cpp
Normal file
270
gui/groupview/Group.cpp
Normal file
@ -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();
|
||||
}
|
69
gui/groupview/Group.h
Normal file
69
gui/groupview/Group.h
Normal file
@ -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)
|
929
gui/groupview/GroupView.cpp
Normal file
929
gui/groupview/GroupView.cpp
Normal file
@ -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 ¤t = 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;
|
||||
}
|
139
gui/groupview/GroupView.h
Normal file
139
gui/groupview/GroupView.h
Normal file
@ -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;
|
||||
};
|
26
gui/groupview/GroupedProxyModel.cpp
Normal file
26
gui/groupview/GroupedProxyModel.cpp
Normal file
@ -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();
|
||||
}
|
15
gui/groupview/GroupedProxyModel.h
Normal file
15
gui/groupview/GroupedProxyModel.h
Normal file
@ -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;
|
||||
};
|
@ -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();
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ class ListViewDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
explicit ListViewDelegate(QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
void paint ( QPainter* painter, 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;
|
||||
};
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user