--- /dev/null
+// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File: QtxTreeModel.cxx
+// Author: Sergey ANIKIN, Open CASCADE S.A.S. (sergey.anikin@opencascade.com)
+//
+
+#include "QtxTreeModel.h"
+
+
+/*!
+ \class QtxTreeModel
+ \brief Base tree model class with custom sorting support.
+ \sa QtxProxyModel
+*/
+
+/*!
+ \brief Constructor.
+ \param parent parent object
+*/
+QtxTreeModel::QtxTreeModel( QObject* parent )
+: QAbstractItemModel( parent )
+{
+}
+
+/*!
+ \brief Destructor
+*/
+QtxTreeModel::~QtxTreeModel()
+{
+}
+
+/*!
+ \brief Check if the specified column supports custom sorting.
+ \param column column index on which data is being sorted
+ \return \c true if column requires custom sorting
+ \sa lessThan()
+*/
+bool QtxTreeModel::customSorting( const int /*column*/ ) const
+{
+ return false;
+}
+
+/*!
+ \brief Compares two model indexes for the sorting purposes.
+
+ This method is called only for those columns for which customSorting()
+ method returns \c true. The default implementation is useless, derived classes
+ should re-implement this method in order to use custom sorting rules.
+
+ \param left first index to compare
+ \param right second index to compare
+ \return result of the comparison
+ \sa customSorting()
+*/
+bool QtxTreeModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const
+{
+ return left.row() < right.row();
+}
+
+
+/*!
+ \class QtxProxyModel
+ \brief Proxy model which can be used above the QtxTreeMovel class
+ to enable custom sorting/filtering of the data.
+*/
+
+/*!
+ \brief Constructor.
+ \param parent parent object
+*/
+QtxProxyModel::QtxProxyModel( QObject* parent )
+: QSortFilterProxyModel( parent ),
+ mySortingEnabled( true )
+{
+}
+
+/*!
+ \brief Destructor.
+*/
+QtxProxyModel::~QtxProxyModel()
+{
+}
+
+/*!
+ \brief Check if sorting is enabled.
+ \return \c true if sorting is enabled
+ \sa setSortingEnabled()
+*/
+bool QtxProxyModel::isSortingEnabled() const
+{
+ return mySortingEnabled;
+}
+
+/*!
+ \brief Enable/disable sorting.
+ \param enabled new flag state
+ \sa isSortingEnabled()
+*/
+void QtxProxyModel::setSortingEnabled( bool enabled )
+{
+ mySortingEnabled = enabled;
+ clear();
+}
+
+/*!
+ \brief Compares two model indexes for the sorting purposes.
+ \param left first index to compare
+ \param right second index to compare
+ \return True if item for left is less than item for right
+*/
+bool QtxProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const
+{
+ if ( !isSortingEnabled() && left.isValid() && right.isValid() ) {
+ return left.row() < right.row();
+ }
+ if ( treeModel() && treeModel()->customSorting( left.column() ) ) {
+ return treeModel()->lessThan( left, right );
+ }
+ return QSortFilterProxyModel::lessThan( left, right );
+}
+
+/*
+ \brief Get tree model.
+ \return tree model
+*/
+QtxTreeModel* QtxProxyModel::treeModel() const
+{
+ return dynamic_cast<QtxTreeModel*>( sourceModel() );
+}
+
+/*!
+ \class QtxMultiSortModel
+ \brief Proxy model for multi-column sorting.
+
+ This class can be used when it is necessary to sort the source model
+ by several columns. It provides overloaded sort( columns, order ) method and re-implements
+ lessThan() method to consider several items from the same row.
+ Note that multi-column sorting is less efficient with the current Qt version (4.4.3)
+ than single-column sorting, as it requires significantly more comparisons, so use it with care!
+*/
+
+/*!
+ \brief Constructor.
+ \param parent parent object
+ */
+QtxMultiSortModel::QtxMultiSortModel( QObject* parent )
+: QtxProxyModel( parent )
+{
+}
+
+/*!
+ \brief Destructor.
+ */
+QtxMultiSortModel::~QtxMultiSortModel()
+{
+}
+
+/*!
+ \brief Performs multi-column sorting.
+
+ This is a front-end for multi-column sorting. Indices of columns should be ordered with descending priority
+ (that is, the first element in the array has maximum priority during sorting,
+ and the last one has the minimum priority).
+ Comparison is performed by re-implementation of lessThan().
+ Note that "multiSort" method name is used instead of "sort" to avoid overloading
+ problems between derived classes and the bases.
+
+ \param columns array of column indices
+ \param orders array of individual sort orders for each column in columns
+ \sa lessThan()
+ */
+void QtxMultiSortModel::multiSort( const QVector<int>& columns,
+ const QVector<Qt::SortOrder>& orders )
+{
+ // Non-empty column list means we're already sorting
+ if ( mySortColumns.size() )
+ return;
+
+ // mySortColumns will be used by lessThan()
+ mySortColumns = columns;
+ mySortOrders = orders;
+ // Remember this parameter to use it in lessThan()
+ myMajorOrder = mySortOrders.empty() ? Qt::AscendingOrder : mySortOrders.first();
+ int aDummySortColumn = mySortColumns.empty() ? 0 : mySortColumns.first();
+
+ // Virtual mechanism still works, so sort() can be re-implemented in derived classes
+ invalidate();
+ sort( aDummySortColumn, myMajorOrder );
+
+ mySortColumns.clear();
+ mySortOrders.clear();
+}
+
+/*!
+ \brief Comparison method based on a list of columns
+ \param idx1 Specifies first row for comparison
+ \param idx2 Specifies second row for comparison
+ \return true if items for idx1.row() are less that items for idx2.row()
+ \sa sort()
+ */
+bool QtxMultiSortModel::lessThan( const QModelIndex& idx1, const QModelIndex& idx2 ) const
+{
+ if ( mySortColumns.empty() )
+ return QtxProxyModel::lessThan( idx1, idx2 );
+
+ int aSrcRow1 = idx1.row();
+ int aSrcRow2 = idx2.row();
+ bool aResult = false;
+
+ size_t anIt = 0, anEnd = mySortColumns.size();
+ for ( ; !aResult && anIt != anEnd; anIt++ ){
+ // Do we have to swap the rows to reverse the comparison result?
+ int aRow1 = aSrcRow1, aRow2 = aSrcRow2;
+ QModelIndex aParent1 = idx1.parent(), aParent2 = idx2.parent();
+ if ( mySortOrders[anIt] != myMajorOrder ){
+ std::swap( aRow1, aRow2 );
+ std::swap( aParent1, aParent2 );
+ }
+
+ QModelIndex aSortIdx1 = this->sourceModel()->index( aRow1, mySortColumns[anIt], aParent1 );
+ QModelIndex aSortIdx2 = this->sourceModel()->index( aRow2, mySortColumns[anIt], aParent2 );
+
+ bool localLess = QtxProxyModel::lessThan( aSortIdx1, aSortIdx2 );
+
+ // aSortIdx2 < aSortIdx1 --> it means we should stop here and return false
+ if ( !localLess && QtxProxyModel::lessThan( aSortIdx2, aSortIdx1 ) )
+ break;
+
+ aResult = aResult || localLess;
+ }
+
+ return aResult;
+}
+
--- /dev/null
+// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File: QtxTreeModel.h
+// Author: Sergey ANIKIN, Open CASCADE S.A.S. (sergey.anikin@opencascade.com)
+//
+
+#ifndef QTXTREEMODEL_H
+#define QTXTREEMODEL_H
+
+#include <Qtx.h>
+
+#include <QAbstractItemModel>
+#include <QSortFilterProxyModel>
+
+#ifdef WIN32
+#pragma warning( disable:4251 )
+#endif
+
+class QModelIndex;
+
+class QTX_EXPORT QtxTreeModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ QtxTreeModel( QObject* = 0 );
+ ~QtxTreeModel();
+
+ virtual bool customSorting( const int ) const;
+ virtual bool lessThan( const QModelIndex& left, const QModelIndex& right ) const;
+};
+
+class QTX_EXPORT QtxProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ QtxProxyModel( QObject* = 0 );
+ ~QtxProxyModel();
+
+ bool isSortingEnabled() const;
+
+public slots:
+ void setSortingEnabled( bool );
+
+protected:
+ virtual bool lessThan( const QModelIndex&, const QModelIndex& ) const;
+
+private:
+ QtxTreeModel* treeModel() const;
+
+private:
+ bool mySortingEnabled;
+};
+
+class QTX_EXPORT QtxMultiSortModel : public QtxProxyModel
+{
+ Q_OBJECT
+
+public:
+ QtxMultiSortModel( QObject* = 0 );
+ ~QtxMultiSortModel();
+
+ virtual void multiSort( const QVector<int>&, const QVector<Qt::SortOrder>& );
+
+protected:
+ virtual bool lessThan( const QModelIndex&, const QModelIndex& ) const;
+
+private:
+ QVector<int> mySortColumns;
+ QVector<Qt::SortOrder> mySortOrders;
+ Qt::SortOrder myMajorOrder;
+};
+
+#ifdef WIN32
+#pragma warning( default:4251 )
+#endif
+
+#endif // QTXTREEMODEL_H
//
#include "QtxTreeView.h"
+#include "QtxTreeModel.h"
+#include <QApplication>
#include <QHeaderView>
#include <QMenu>
#include <QMouseEvent>
+#include <QPainter>
+#include <QPalette>
/*!
\class QtxTreeView::Header
class QtxTreeView::Header : public QHeaderView
{
+ Q_OBJECT
public:
Header( const bool, QWidget* = 0 );
~Header();
- void setSortMenuEnabled( const bool );
- bool sortMenuEnabled() const;
+ void setSortMenuEnabled( const bool );
+ bool sortMenuEnabled() const;
- void addMenuAction( QAction* );
+ void setSortingEnabled( const bool );
+ bool sortingEnabled() const;
+ void setMultiSortEnabled( const bool );
+ bool multiSortEnabled() const;
+ void setSortIndicators( const QVector<int>&, const QVector<Qt::SortOrder>& );
+ void sortIndicators( QVector<int>&, QVector<Qt::SortOrder>& ) const;
+ void clearSortIndicators();
+ bool sortIndicatorsShown() const;
+
+ void addMenuAction( QAction* );
protected:
- void contextMenuEvent( QContextMenuEvent* );
+ virtual void contextMenuEvent( QContextMenuEvent* );
+ virtual void paintSection( QPainter* painter, const QRect& rect, int logicalIndex ) const;
+
+private slots:
+ void onSectionClicked( int logicalIndex );
private:
typedef QMap<int, QAction*> ActionsMap;
- bool myEnableSortMenu;
- ActionsMap myActions;
+ bool myEnableSortMenu;
+ ActionsMap myActions;
+ bool myEnableMultiSort;
+ QVector<int> mySortSections;
+ QVector<Qt::SortOrder> mySortOrders;
};
/*!
: QHeaderView( Qt::Horizontal, parent ),
myEnableSortMenu( enableSortMenu )
{
+ setMultiSortEnabled( false );
+ // This connection should be created as early as possible, to perform internal processing
+ // before any external actions
+ connect( this, SIGNAL( sectionClicked( int ) ), this, SLOT( onSectionClicked( int ) ) );
}
/*!
{
}
-/*
+/*!
+ \brief Enable/disable sorting operation for the header.
+ \param enable if \c true, makes the header clickable and shows sort indicator(s)
+ \internal
+*/
+void QtxTreeView::Header::setSortingEnabled( const bool enable )
+{
+ // Suppress default Qt sort indicator for multi-sort case
+ setSortIndicatorShown( enable && !multiSortEnabled() );
+
+ // Clear multi-sort indicators
+ if ( !enable )
+ clearSortIndicators();
+
+ setClickable( enable );
+
+ QtxTreeView* view = qobject_cast<QtxTreeView*>( parent() );
+ if ( view ) {
+ view->emitSortingEnabled( enable );
+ if ( enable ) {
+ connect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked() ) );
+ if ( multiSortEnabled() )
+ {
+ QVector<int> columns;
+ columns.push_back( 0 );
+ QVector<Qt::SortOrder> orders;
+ orders.push_back( Qt::AscendingOrder );
+ setSortIndicators( columns, orders );
+ }
+ view->onHeaderClicked();
+ }
+ else {
+ disconnect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked() ) );
+ view->sortByColumn( 0, Qt::AscendingOrder );
+ }
+ }
+}
+
+/*!
+ \brief Query status of sorting (enabled or disabled).
+ \return \c true if single or multiple sort indicators are shown
+ \internal
+*/
+bool QtxTreeView::Header::sortingEnabled() const
+{
+ return sortIndicatorsShown();
+}
+
+/*!
\brief Enable/disable "Sorting" popup menu command for the header.
\param enableSortMenu if \c true, enable "Sorting" menu command
\internal
myEnableSortMenu = enableSortMenu;
}
-/*
+/*!
\brief Check if "Sorting" popup menu command for the header is enabled.
\return \c true if "Sorting" menu command is enabled
\internal
return myEnableSortMenu;
}
-/*
+/*!
+ \brief Enable/disable multi-column sorting. Sorting itself should be enabled using setSortingEnabled( true )
+ \param enable if \c true, enables multi-column sorting
+ \internal
+*/
+void QtxTreeView::Header::setMultiSortEnabled( const bool enable )
+{
+ myEnableMultiSort = enable;
+ // Multi-sort and usual sort are mutually exclusive
+ setSortIndicatorShown( isSortIndicatorShown() && !enable );
+}
+
+/*!
+ \brief Check multi-column sorting status (enabled or disabled).
+ \return \c true if multi-column sorting is enabled
+ \internal
+*/
+bool QtxTreeView::Header::multiSortEnabled() const
+{
+ return myEnableMultiSort;
+}
+
+/*!
+ \brief Set sorting columns and sort orders for multi-sorting operation.
+ \param logicalIndices Zero-based logical indices of columns to be used for sorting
+ \param orders orders[i] contains sort order for section logicalIndices[i]
+ \internal
+*/
+void QtxTreeView::Header::setSortIndicators( const QVector<int>& logicalIndices,
+ const QVector<Qt::SortOrder>& orders )
+{
+ mySortSections = logicalIndices;
+ mySortOrders = orders;
+}
+
+/*!
+ \brief Query sorting columns and sort orders for multi-sorting operation.
+ \param logicalIndices Out parameter for zero-based logical indices of columns currently used for sorting
+ \param orders Out parameter, orders[i] contains the current sort order for section logicalIndices[i]
+ \internal
+*/
+void QtxTreeView::Header::sortIndicators( QVector<int>& logicalIndices,
+ QVector<Qt::SortOrder>& orders ) const
+{
+ logicalIndices.clear();
+ orders.clear();
+
+ if ( isSortIndicatorShown() ){
+ logicalIndices.push_back( sortIndicatorSection() );
+ orders.push_back ( sortIndicatorOrder () );
+ }
+ else{
+ logicalIndices = mySortSections;
+ orders = mySortOrders;
+ }
+}
+
+/*!
+ \brief Clears sort indicators for multi-column sorting.
+ \internal
+*/
+void QtxTreeView::Header::clearSortIndicators()
+{
+ mySortSections.clear();
+ mySortOrders.clear();
+}
+
+/*!
+ \brief Query sort indicators state (shown or not).
+ \return \c true if usual or multi-column sorting indicator is shown
+ \internal
+*/
+bool QtxTreeView::Header::sortIndicatorsShown() const
+{
+ return isSortIndicatorShown() || ( multiSortEnabled() && mySortSections.size() );
+}
+
+Qt::SortOrder operator~( const Qt::SortOrder& arg )
+{
+ return arg == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
+}
+
+/*!
+ \brief Paints the tree view header section.
+
+ Re-implementation of paintSection() handles multiple sort indicators.
+ It lacks selection support (that is not important for the tree view header in most cases)
+ and some dynamic features such as mouse hover feedback. This can be added if necessary.
+
+ \internal
+*/
+void QtxTreeView::Header::paintSection( QPainter* painter, const QRect& rect, int logicalIndex ) const
+{
+ if (!rect.isValid())
+ return;
+ // get the state of the section
+ QStyleOptionHeader opt;
+ initStyleOption(&opt);
+
+ if (isEnabled())
+ opt.state |= QStyle::State_Enabled;
+ if (window()->isActiveWindow())
+ opt.state |= QStyle::State_Active;
+
+ QVector<int> sortIndices;
+ QVector<Qt::SortOrder> sortOrders;
+ sortIndicators( sortIndices, sortOrders );
+
+ int sortIndex = sortIndices.indexOf( logicalIndex );
+
+ if ( ( isSortIndicatorShown() || multiSortEnabled() ) && sortIndex != -1 )
+ opt.sortIndicator = ( sortOrders[sortIndex] == Qt::AscendingOrder )
+ ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+
+ // setup the style options structure
+ QVariant textAlignment = model()->headerData( logicalIndex, Qt::Horizontal, Qt::TextAlignmentRole );
+ opt.rect = rect;
+ opt.section = logicalIndex;
+ opt.textAlignment = Qt::Alignment( textAlignment.isValid()
+ ? Qt::Alignment( textAlignment.toInt() ) : defaultAlignment() );
+
+ opt.iconAlignment = Qt::AlignVCenter;
+ opt.text = model()->headerData( logicalIndex, Qt::Horizontal, Qt::DisplayRole ).toString();
+
+ QVariant variant = model()->headerData( logicalIndex, Qt::Horizontal, Qt::DecorationRole );
+ opt.icon = qvariant_cast<QIcon>(variant);
+ if (opt.icon.isNull())
+ opt.icon = qvariant_cast<QPixmap>(variant);
+
+ QVariant foregroundBrush = model()->headerData( logicalIndex, Qt::Horizontal, Qt::ForegroundRole);
+ if (qVariantCanConvert<QBrush>(foregroundBrush))
+ opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
+
+ QPointF oldBO = painter->brushOrigin();
+ QVariant backgroundBrush = model()->headerData( logicalIndex, Qt::Horizontal, Qt::BackgroundRole );
+ if ( qVariantCanConvert<QBrush>( backgroundBrush ) ) {
+ opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
+ opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
+ painter->setBrushOrigin( opt.rect.topLeft() );
+ }
+
+ // the section position
+ int visual = visualIndex( logicalIndex );
+ Q_ASSERT(visual != -1);
+ if ( count() == 1 )
+ opt.position = QStyleOptionHeader::OnlyOneSection;
+ else if ( visual == 0 )
+ opt.position = QStyleOptionHeader::Beginning;
+ else if ( visual == count() - 1 )
+ opt.position = QStyleOptionHeader::End;
+ else
+ opt.position = QStyleOptionHeader::Middle;
+
+ opt.orientation = Qt::Horizontal;
+
+ // draw the section (background, label, pixmap and sort indicator)
+ style()->drawControl( QStyle::CE_Header, &opt, painter, this );
+
+ // Optionally, draw the sort priority for the current column
+ if ( sortIndex >=0 && sortIndices.size() > 1 ){
+ QRegion oldClip = painter->clipRegion();
+ painter->setClipRect( rect );
+
+ QFont oldFont = painter->font();
+ QFont fnt( oldFont );
+ fnt.setPointSize( fnt.pointSize() - 2 );
+ painter->setFont(fnt);
+ style()->drawItemText( painter,
+ //rect,
+ QRect( rect.left(), rect.top(), rect.width() - 2, rect.height() ),
+ Qt::AlignRight,
+ opt.palette,
+ ( opt.state & QStyle::State_Enabled ),
+ QString( "%1" ).arg( ++sortIndex ),
+ QPalette::ButtonText );
+
+ painter->setFont( oldFont );
+ painter->setClipRegion( oldClip );
+ }
+
+ painter->setBrushOrigin(oldBO);
+}
+
+/*!
+ \brief Custom mouse click handler, supports clicking multiple header sections with <Ctrl> held.
+ \param logicalIndex the logical index of section
+ \internal
+*/
+void QtxTreeView::Header::onSectionClicked( int logicalIndex )
+{
+ if ( multiSortEnabled() ){
+ // Already using section for sorting -> reverse the order
+ int sortIndex = mySortSections.indexOf( logicalIndex );
+ Qt::SortOrder aDefSortOrder = sortIndex != -1 ? ~mySortOrders[sortIndex] : Qt::AscendingOrder;
+
+ // <Ctrl> not pressed -> clear all sort columns first
+ if ( !( QApplication::keyboardModifiers() & Qt::ControlModifier ) ){
+ mySortSections.clear();
+ mySortOrders.clear();
+ }
+
+ if ( sortIndex >=0 && sortIndex < mySortOrders.size() ){
+ mySortOrders[sortIndex] = aDefSortOrder;
+ }
+ else{
+ mySortSections.push_back( logicalIndex );
+ mySortOrders.push_back ( mySortOrders.empty() ? aDefSortOrder : mySortOrders.last() );
+ }
+ }
+}
+
+/*!
\brief Appends action to header popup menu.
\param action the action
\internal
menu.addSeparator();
sortAction = menu.addAction( tr( "Enable sorting" ) );
sortAction->setCheckable( true );
- sortAction->setChecked( isSortIndicatorShown() );
+ sortAction->setChecked( sortingEnabled() );
}
if ( myActions.size() > 0 ) {
menu.addSeparator();
setSectionHidden( actionMap[ a ], !isSectionHidden( actionMap[ a ] ) );
}
else if ( a && a == sortAction ) {
- setSortIndicatorShown( a->isChecked() );
- setClickable( a->isChecked() );
- QtxTreeView* view = qobject_cast<QtxTreeView*>( parent() );
- if ( view ) {
- view->emitSortingEnabled( a->isChecked() );
- if ( a->isChecked() ) {
- connect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked( int ) ) );
- view->sortByColumn( sortIndicatorSection(), sortIndicatorOrder() );
- }
- else {
- disconnect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked( int ) ) );
- view->sortByColumn( 0, Qt::AscendingOrder );
- }
- }
+ setSortingEnabled( a->isChecked() );
}
}
e->accept();
setColumnWidth( column, sizeHint );
}
+/*!
+ \brief Enable/disable sorting operation for the view.
+ \param enable if \c true, makes the tree view header clickable and shows sort indicator(s)
+ \sa setMultiSortEnabled()
+*/
+void QtxTreeView::setSortingEnabled( const bool enable )
+{
+ Header* h = dynamic_cast<Header*>( header() );
+ if ( h )
+ h->setSortingEnabled( enable );
+}
+
+/*!
+ \brief Query status of sorting (enabled or disabled).
+ \return \c true if single or multiple sort indicators are shown
+ \sa multiSortEnabled()
+*/
+bool QtxTreeView::sortingEnabled() const
+{
+ Header* h = dynamic_cast<Header*>( header() );
+ return h && ( h->sortingEnabled() );
+}
+
+/*!
+ \brief Enables multi-column sorting.
+ As soon as multi-column sorting is enabled, the user can click
+ several header sections in required order while holding <Ctrl> key so as to sort the contents
+ of the tree view on a basis of these columns' values. The column clicked first
+ has maximum sort priority, the column clicked last has minimum sort priority.
+ Each column used for sorting has a sort indicator and sort priority value displayed
+ in its header section.
+ Note that sorting in general should be enabled first using setSortingEnabled().
+ \param enable true to enable, otherwise false.
+ \sa setSortingEnabled()
+*/
+void QtxTreeView::setMultiSortEnabled( const bool enable )
+{
+ Header* h = dynamic_cast<Header*>( header() );
+ if ( h )
+ h->setMultiSortEnabled( enable );
+}
+
+/*!
+ \brief Returns true if multi-sorting is enabled, otherwise returns false.
+ \return Multi-sorting status.
+*/
+bool QtxTreeView::multiSortEnabled() const
+{
+ Header* h = dynamic_cast<Header*>( header() );
+ return h ? h->multiSortEnabled() : false;
+}
+
/*
\brief Called when the header section is clicked.
- \param column header column index
*/
-void QtxTreeView::onHeaderClicked( int column )
+void QtxTreeView::onHeaderClicked()
{
- sortByColumn( column, header()->sortIndicatorOrder() );
+ Header* h = dynamic_cast<Header*>( header() );
+ QtxMultiSortModel* m = dynamic_cast<QtxMultiSortModel*>( model() );
+
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+
+ if ( h && m && multiSortEnabled() ){
+ QVector<int> columns;
+ QVector<Qt::SortOrder> orders;
+ h->sortIndicators( columns, orders );
+ m->multiSort( columns, orders );
+ }
+ else
+ sortByColumn( header()->sortIndicatorSection(), header()->sortIndicatorOrder() );
+
+ QApplication::restoreOverrideCursor();
}
/*!
{
emit( sortingEnabled( enabled ) );
}
+
+#include <QtxTreeView.moc>
void resizeColumnToEncloseContents( int );
+ void setSortingEnabled( const bool );
+ bool sortingEnabled() const;
+
+ void setMultiSortEnabled( const bool );
+ bool multiSortEnabled() const;
+
protected slots:
- void onHeaderClicked( int );
+ void onHeaderClicked();
void rowsAboutToBeRemoved( const QModelIndex&, int, int );
void selectionChanged( const QItemSelection&, const QItemSelection& );
The parameter \a index specifies the column number
(to display, for example, in the tree view widget).
+ There is a tricky point around alpha value for role == Background.
+ Zero alpha is treated as fully transparent, therefore no background
+ is drawn at all (that is, the base color will appear instead of the custom backround color).
+ However, maximum alpha (each QColor has alpha == 1.0f by default) might be also inacceptable
+ since it disables blending effects that might be used by a custom style.
+ Thus applications should choose color's alpha carefully to avoid visual artefacts.
+
\param role color role
\param index column index
\return object color for the specified column
\param parent parent object
*/
SUIT_TreeModel::SUIT_TreeModel( QObject* parent )
-: QAbstractItemModel( parent ),
+: QtxTreeModel( parent ),
myRoot( 0 ),
myRootItem( 0 ),
myAutoDeleteTree( false ),
\param parent parent object
*/
SUIT_TreeModel::SUIT_TreeModel( SUIT_DataObject* root, QObject* parent )
-: QAbstractItemModel( parent ),
+: QtxTreeModel( parent ),
myRoot( root ),
myRootItem( 0 ),
myAutoDeleteTree( false ),
case BackgroundRole:
// data background color for the specified column
c = obj->color( SUIT_DataObject::Background, index.column() );
- if ( !c.isValid() ) // default value
+ // NOTE by san: Zero alpha is treated as fully transparent, therefore no background
+ // is drawn at all (that is, the base color will appear instead of the custom backround).
+ // However, maximum alpha (each QColor has alpha == 1.0f by default) might be also unacceptable
+ // since it disables blending effects that might be used by a custom style.
+ // Thus applications should choose color's alpha themselves to get required visual result.
+ if ( !c.isValid() ){ // default value, should be fully transparent
c = QApplication::palette().color( QPalette::Base );
- c.setAlpha( 0 );
+ c.setAlpha( 0 );
+ }
val = c;
break;
case ForegroundRole:
}
}
}
- return QAbstractItemModel::setData( index, value, role );
+ return QtxTreeModel::setData( index, value, role );
}
/*!
\param parent parent object
*/
SUIT_ProxyModel::SUIT_ProxyModel( QObject* parent )
-: QSortFilterProxyModel( parent ),
- mySortingEnabled( true )
+: QtxMultiSortModel( parent )
{
+ setSortingEnabled( true );
SUIT_TreeModel* model = new SUIT_TreeModel( this );
connect( model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) );
setSourceModel( model );
\param parent parent object
*/
SUIT_ProxyModel::SUIT_ProxyModel( SUIT_DataObject* root, QObject* parent )
-: QSortFilterProxyModel( parent ),
- mySortingEnabled( true )
+: QtxMultiSortModel( parent )
{
+ setSortingEnabled( true );
SUIT_TreeModel* model = new SUIT_TreeModel( root, this );
connect( model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) );
setSourceModel( model );
\param parent parent object
*/
SUIT_ProxyModel::SUIT_ProxyModel( SUIT_TreeModel* model, QObject* parent )
-: QSortFilterProxyModel( parent ),
- mySortingEnabled( true )
+: QtxMultiSortModel( parent )
{
+ setSortingEnabled( true );
connect( model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) );
setSourceModel( model );
}
treeModel()->setAutoUpdate( on, updateImmediately );
}
-/*!
- \brief Check if sorting is enabled.
- \return \c true if sorting is enabled
- \sa setSortingEnabled()
-*/
-bool SUIT_ProxyModel::isSortingEnabled() const
-{
- return mySortingEnabled;
-}
-
/*!
\brief Get item delegate for the model.
\return new item delegate
treeModel()->updateTree( obj );
}
-/*!
- \brief Compares two model indexes for the sorting purposes.
- \param left first index to compare
- \param right second index to compare
- \return result of the comparison
-*/
-bool SUIT_ProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const
-{
- if ( !isSortingEnabled() && left.isValid() && right.isValid() ) {
- return left.row() < right.row();
- }
- if ( treeModel() && treeModel()->customSorting( left.column() ) ) {
- return treeModel()->lessThan( left, right );
- }
- return QSortFilterProxyModel::lessThan( left, right );
-}
-
-/*!
- \brief Enable/disable sorting.
- \param enabled new flag state
- \sa isSortingEnabled()
-*/
-void SUIT_ProxyModel::setSortingEnabled( bool enabled )
-{
- mySortingEnabled = enabled;
- clear();
-}
-
/*
\brief Get tree model.
\return tree model
// Note: we check into account only custom roles; other roles are process
// correctly by the QItemDelegate class
QVariant val = index.data( SUIT_TreeModel::BaseColorRole );
- if ( val.isValid() && val.value<QColor>().isValid() ) {
- QColor aBase = val.value<QColor>();
- aBase.setAlpha( 0 );
+ if ( val.isValid() && val.value<QColor>().isValid() )
opt.palette.setBrush( QPalette::Base, val.value<QColor>() );
- }
val = index.data( SUIT_TreeModel::TextColorRole );
if ( val.isValid() && val.value<QColor>().isValid() )
opt.palette.setBrush( QPalette::Text, val.value<QColor>() );
#include "SUIT.h"
-#include <Qtx.h>
+#include <QtxTreeModel.h>
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
class SUIT_DataObject;
class SUIT_TreeModel;
-class SUIT_EXPORT SUIT_TreeModel : public QAbstractItemModel
+class SUIT_EXPORT SUIT_TreeModel : public QtxTreeModel
{
Q_OBJECT
friend class SUIT_TreeModel::TreeSync;
};
-class SUIT_EXPORT SUIT_ProxyModel : public QSortFilterProxyModel
+class SUIT_EXPORT SUIT_ProxyModel : public QtxMultiSortModel
{
Q_OBJECT
bool autoUpdate() const;
void setAutoUpdate( const bool, const bool = true );
- bool isSortingEnabled() const;
-
QAbstractItemDelegate* delegate() const;
public slots:
virtual void updateTree( const QModelIndex& );
virtual void updateTree( SUIT_DataObject* = 0 );
- void setSortingEnabled( bool );
signals:
void modelUpdated();
-protected:
- virtual bool lessThan( const QModelIndex&, const QModelIndex& ) const;
-
private:
SUIT_TreeModel* treeModel() const;
-
-private:
- bool mySortingEnabled;
};
class SUIT_EXPORT SUIT_ItemDelegate : public QItemDelegate