#ifndef QTHEADERVIEW_H#define QTHEADERVIEW_H#include <QtGui/QHeaderView>/*! Класс виджета-заголовка. \li Показать/скрыть секции виджета \li Сортировать по одному/нескольким столбцам \li set|unset movable, highligh sections* @class HeaderView headerview.h*/class QtHeaderViewPrivate;class QtHeaderView : public QHeaderView{ Q_OBJECT Q_DECLARE_PRIVATE(QtHeaderView); Q_PROPERTY (SortFlag sortFlag READ sortFlag WRITE setSortFlag);protected: QtHeaderView(QtHeaderViewPrivate &dd, Qt::Orientation orientation, QWidget* parent); QtHeaderViewPrivate * const d_ptr;public: /*! Тип сортировки отображаемой виджетом * @enum SortFlag */ enum SortFlag { NoSort, //! Нельзя сортировать SimpleSort, //! Сортировка по одному столбцу MultiSort, //! Сортировка по нескольким столбцам }; Q_ENUMS(SortFlag); QtHeaderView(Qt::Orientation orientation, QWidget* parent = 0); virtual ~QtHeaderView(); SortFlag sortFlag() const; void setSortFlag(SortFlag enable); // MultiSorting // Удалить индикатор сортировки void removeSortIndicator(int logicalIndex); // Удалить все индикаторы сортировки void clearSortIndicators(); // Установлен ли индикатор bool isSortIndicatorShown(int logicalIndex) const; // Установить индикатор, если это уже второй индикатор справа отобразить число порядка // Если clearPrevious все стрелочки в столбцах удаляются void setSortIndicator(int logicalIndex, Qt::SortOrder order); // Какая стрелочка отображена для данной колонки. Если никакой, то результат Qt::Asceding Qt::SortOrder sortIndicatorOrder(int logicalIndex) const; // Список столбцов в которых производилась сортировка в том порядке в котором они добавлялись QList<int> sortIndicatorSections() const; // Остальное для менюшки Скрыть/показать столбцы virtual void setModel(QAbstractItemModel* model);signals: void sortIndicatorsChanged();public slots: void showAllColumns();protected slots: void hideColumnAction(); void showHideActionToggled(bool checked); void showHideMenuAboutToShow(); void sortAscAction(); void sortDescAction(); void removeSortAction(); void setMovableTriggered(bool movable); void setHighlightSectionsTriggered(bool highlight);protected: // Отрисовка virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event); void contextMenuEvent(QContextMenuEvent *event); void checkOneColumn();private: Qt::SortOrder sortIndicatorOrder() const; int sortIndicatorSection() const;};#endif // HEADERVIEW_H
#include "qtheaderview.h"#include "private/qtheaderview_p.h"#include <QtCore/QDebug>#include <QtGui/QApplication>#include <QtGui/QPainter>#include <QtGui/QMenu>#include <QtGui/QAction>#include <QtGui/QMouseEvent>#include <QtGui/QContextMenuEvent>#include <QtGui/QInputDialog>QtHeaderView::QtHeaderView(Qt::Orientation orientation, QWidget* parent) :QHeaderView(orientation, parent) , d_ptr(new QtHeaderViewPrivate()){ Q_D(QtHeaderView); d->q_ptr = this; d->sortFlag = NoSort; d->hideColumnAction = new QAction(tr("Hide"), this); d->hideColumnAction->setObjectName("HideColumnAction"); d->hideColumnAction->setToolTip(tr("Hide table column")); d->showAllColumnsAction = new QAction(tr("Show all"), this); d->showAllColumnsAction->setObjectName("ShowAllColumnsAction"); d->showAllColumnsAction->setToolTip(tr("Show all hidden columns")); d->showhideColumnActionMenu = new QAction(tr("Show/hide columns"), this); d->showHideColumnsMenu = new QMenu(this); d->showhideColumnActionMenu->setMenu(d->showHideColumnsMenu); d->sortSeparator = new QAction(this); d->sortSeparator->setSeparator(true); d->sortAscAction = new QAction(tr("Ascending order"), this); d->sortAscAction->setObjectName("SortAscAction"); d->sortAscAction->setToolTip(tr("Set ascending order for column")); d->sortDescAction = new QAction(tr("Descending order"), this); d->sortDescAction->setObjectName("SortDescAction"); d->sortDescAction->setToolTip(tr("Set descending order for column")); d->removeSortAction = new QAction(tr("Remove sort"), this); d->removeSortAction->setObjectName("RemoveSortAction"); d->removeSortAction->setToolTip(tr("Unset sort for column")); QAction *a; a = d->movableAction = new QAction(tr("Moveable"), this); a->setObjectName("MovableAction"); a->setCheckable(true); a->setChecked(isMovable()); addAction(a); a = d->hightlightAction = new QAction(tr("Highlight"), this); a->setObjectName("HighlightAction"); a->setCheckable(true); a->setChecked(highlightSections()); addAction(a); setClickable(true); addAction(d->hideColumnAction); addAction(d->showAllColumnsAction); addAction(d->showhideColumnActionMenu); connect(d->movableAction, SIGNAL(triggered(bool)) , this, SLOT(setMovableTriggered(bool))); connect(d->hightlightAction, SIGNAL(triggered(bool)) , this, SLOT(setHighlightSectionsTriggered(bool))); connect(d->hideColumnAction, SIGNAL(triggered()), this, SLOT(hideColumnAction())); connect(d->showAllColumnsAction, SIGNAL(triggered()), this, SLOT(showAllColumns())); connect(d->showHideColumnsMenu, SIGNAL(aboutToShow()) , this, SLOT(showHideMenuAboutToShow())); connect(d->sortAscAction, SIGNAL(triggered()), this, SLOT(sortAscAction())); connect(d->sortDescAction, SIGNAL(triggered()), this, SLOT(sortDescAction())); connect(d->removeSortAction, SIGNAL(triggered()), this, SLOT(removeSortAction())); setContextMenuPolicy(Qt::DefaultContextMenu);}QtHeaderView::QtHeaderView(QtHeaderViewPrivate &dd, Qt::Orientation orientation, QWidget* parent) :QHeaderView(orientation, parent) , d_ptr(&dd){ Q_D(QtHeaderView); d->q_ptr = this;}QtHeaderView::~QtHeaderView(){ delete d_ptr;}QtHeaderView::SortFlag QtHeaderView::sortFlag() const{ Q_D(const QtHeaderView); return d->sortFlag;}void QtHeaderView::setSortFlag(SortFlag flag){ Q_D(QtHeaderView); if (d->sortFlag != flag) { if (flag == SimpleSort || flag == MultiSort) { addAction(d->sortSeparator); addAction(d->sortAscAction); addAction(d->sortDescAction); addAction(d->removeSortAction); } else { removeAction(d->sortSeparator); removeAction(d->sortAscAction); removeAction(d->sortDescAction); removeAction(d->removeSortAction); } d->sortFlag = flag; clearSortIndicators(); }}void QtHeaderView::removeSortIndicator(int logicalIndex){ Q_D(QtHeaderView); d->removeSortIndicatorWithoutRepaint(logicalIndex); viewport()->repaint();}void QtHeaderView::clearSortIndicators(){ Q_D(QtHeaderView); d->clearSortIndicatorsWithoutRepaint(); viewport()->repaint();}bool QtHeaderView::isSortIndicatorShown(int logicalIndex) const{ Q_D(const QtHeaderView); return (d->sortingColumns.contains(logicalIndex));}void QtHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order){ Q_D(QtHeaderView); if (logicalIndex > -1 && logicalIndex < count()) { d->sortingColumns.insert(logicalIndex, order); if (!d->orderList.contains(logicalIndex)) d->orderList.append(logicalIndex); } viewport()->repaint();}QList<int> QtHeaderView::sortIndicatorSections() const{ Q_D(const QtHeaderView); return d->orderList;}Qt::SortOrder QtHeaderView::sortIndicatorOrder(int logicalIndex) const{ Q_D(const QtHeaderView); if (d->sortingColumns.contains(logicalIndex)) return d->sortingColumns.value(logicalIndex); return Qt::AscendingOrder;}void QtHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const{ Q_D(const QtHeaderView); if (isSortIndicatorShown(logicalIndex)) { QStyleOptionHeader opt; initStyleOption(&opt); QStyle::State state = QStyle::State_None; if (isEnabled()) state |= QStyle::State_Enabled; if (window()->isActiveWindow()) state |= QStyle::State_Active; QPoint cursorPos = mapFromGlobal(cursor().pos()); bool containMouse = rect.contains(cursorPos); if (isClickable()) { if (containMouse) state |= QStyle::State_MouseOver; if (containMouse && (QApplication::mouseButtons() && (Qt::LeftButton || Qt::RightButton))) state |= QStyle::State_Sunken; else if (highlightSections()) { if (d->sectionIntersectsSelection(logicalIndex)) //if (selectionModel()->columnIntersectsSelection(logicalIndex, rootIndex())) state |= QStyle::State_On;// if (d->isSectionSelected(logicalIndex)) //if (containMouse && (QApplication::mouseButtons() && (Qt::LeftButton || Qt::RightButton))) // state |= QStyle::State_Sunken; } } opt.sortIndicator = d->sortingColumns.value(logicalIndex)==Qt::AscendingOrder ?QStyleOptionHeader::SortDown :QStyleOptionHeader::SortUp; // setup the style options structure QVariant textAlignment = d->model->headerData(logicalIndex, orientation(), Qt::TextAlignmentRole); opt.rect = rect; opt.section = logicalIndex; opt.state |= state; opt.textAlignment = Qt::Alignment(textAlignment.isValid() ? Qt::Alignment(textAlignment.toInt()) : defaultAlignment()); opt.iconAlignment = Qt::AlignVCenter; opt.text = d->model->headerData(logicalIndex, orientation(), Qt::DisplayRole).toString(); if (d->orderList.count() > 1) { int i; for (i = 0; i < d->orderList.count(); ++i) if (d->orderList.at(i) == logicalIndex) break; // create your indication sort ordering sections opt.text.append('/'); opt.text.append(QString::number(i+1)); // http://prog.org.ru /*+ (d->sortingColumns.value(logicalIndex)==Qt::AscendingOrder ?QString(0x2193) : QString(0x2191))*/; } if (textElideMode() != Qt::ElideNone) opt.text = opt.fontMetrics.elidedText(opt.text, textElideMode(), rect.width() - 4); QVariant variant = d->model->headerData(logicalIndex, orientation(), Qt::DecorationRole); opt.icon = qvariant_cast<QIcon>(variant); if (opt.icon.isNull()) opt.icon = qvariant_cast<QPixmap>(variant); QVariant foregroundBrush = d->model->headerData(logicalIndex, orientation(), Qt::ForegroundRole); if (qVariantCanConvert<QBrush>(foregroundBrush)) opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush)); QPointF oldBO = painter->brushOrigin(); QVariant backgroundBrush = d->model->headerData(logicalIndex, orientation(), 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 = orientation(); // the selected position bool previousSelected = selectionModel()->columnIntersectsSelection(this->logicalIndex(visual - 1), rootIndex()); bool nextSelected = selectionModel()->columnIntersectsSelection(this->logicalIndex(visual + 1), rootIndex()); if (previousSelected && nextSelected) opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected; else if (previousSelected) opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected; else if (nextSelected) opt.selectedPosition = QStyleOptionHeader::NextIsSelected; else opt.selectedPosition = QStyleOptionHeader::NotAdjacent; // draw the section style()->drawControl(QStyle::CE_Header, &opt, painter, this); painter->setBrushOrigin(oldBO); } else QHeaderView::paintSection(painter, rect, logicalIndex);}void QtHeaderView::setModel(QAbstractItemModel* model){ Q_D(QtHeaderView); d->model = model; clearSortIndicators(); QHeaderView::setModel(model);}void QtHeaderView::showAllColumns(){ for (int i = 0; i < count(); ++i) setSectionHidden(i, false);}void QtHeaderView::hideColumnAction(){ Q_D(QtHeaderView); hideSection(d->currentLogicalIndex);}void QtHeaderView::sortAscAction(){ Q_D(QtHeaderView); if (d->sortFlag == NoSort) return; if ((d->sortFlag == MultiSort)) { setSortIndicator(d->currentLogicalIndex, Qt::AscendingOrder); } else { d->clearSortIndicatorsWithoutRepaint(); setSortIndicator(d->currentLogicalIndex, Qt::AscendingOrder); } emit sortIndicatorsChanged();}void QtHeaderView::sortDescAction(){ Q_D(QtHeaderView); if (d->sortFlag == NoSort) return; if ((d->sortFlag == MultiSort)) { setSortIndicator(d->currentLogicalIndex, Qt::DescendingOrder); } else { d->clearSortIndicatorsWithoutRepaint(); setSortIndicator(d->currentLogicalIndex, Qt::DescendingOrder); } emit sortIndicatorsChanged();}void QtHeaderView::removeSortAction(){ Q_D(QtHeaderView); if (d->sortFlag == NoSort) return; if (d->sortingColumns.contains(d->currentLogicalIndex)) { removeSortIndicator(d->currentLogicalIndex); emit sortIndicatorsChanged(); }}void QtHeaderView::mousePressEvent(QMouseEvent *event){ Q_D(QtHeaderView); // Remember section for hide|show|sortAsc|sortDesc|removeSort Actions d->currentLogicalIndex = logicalIndexAt(event->pos()); // genereate sectionClicked() // QTableView select column. QHeaderView::mousePressEvent(event);}void QtHeaderView::mouseReleaseEvent(QMouseEvent *event){ Q_D(QtHeaderView); int currentLogicalIndex = logicalIndexAt(event->pos()); if (currentLogicalIndex != -1) { // Можно сортировать if (d->sortFlag != NoSort) { // If multisort and ctrl pressed // Если можно сортировать по нескольким столбцам и зажат контрол if ((d->sortFlag == MultiSort) && (QApplication::keyboardModifiers() & Qt::ControlModifier) && (QApplication::keyboardModifiers() & Qt::ShiftModifier)) { if (sortIndicatorOrder(currentLogicalIndex) == Qt::DescendingOrder) { d->removeSortIndicatorWithoutRepaint(currentLogicalIndex); } else if (!isSortIndicatorShown(currentLogicalIndex)) { setSortIndicator(currentLogicalIndex, Qt::AscendingOrder); } else { setSortIndicator(currentLogicalIndex, Qt::DescendingOrder); } emit sortIndicatorsChanged(); // Нужно соритровать по одному столбцу } else if ((QApplication::keyboardModifiers() & Qt::ShiftModifier)) { if (isSortIndicatorShown(currentLogicalIndex)) { if (sortIndicatorOrder(currentLogicalIndex) == Qt::DescendingOrder) { d->clearSortIndicatorsWithoutRepaint(); } else { if (sortIndicatorSections().count()) { d->clearSortIndicatorsWithoutRepaint(); } setSortIndicator(currentLogicalIndex, Qt::DescendingOrder); } } else { if (sortIndicatorSections().count()) { d->clearSortIndicatorsWithoutRepaint(); } setSortIndicator(currentLogicalIndex, Qt::AscendingOrder); } emit sortIndicatorsChanged(); } } } QHeaderView::mouseReleaseEvent(event);}void QtHeaderView::mouseDoubleClickEvent(QMouseEvent *event){ Q_D(QtHeaderView); int currentLogicalIndex = logicalIndexAt(event->pos()); sectionSize(currentLogicalIndex); if (currentLogicalIndex != -1) { bool ok; QString text = QInputDialog::getText(this, tr("Enter string"), tr("New name for column:"), QLineEdit::Normal, model()->headerData(currentLogicalIndex, orientation() , Qt::EditRole).toString() , &ok); if(ok && !text.isEmpty()) d->model->setHeaderData(currentLogicalIndex, orientation(), text, Qt::EditRole); } QHeaderView::mouseDoubleClickEvent(event);}void QtHeaderView::showHideActionToggled(bool checked){ Q_D(QtHeaderView); QAction *senderAction = qobject_cast<QAction *> (sender()); if (senderAction) setSectionHidden(d->showhideColumnActionList.indexOf(senderAction), !checked);}void QtHeaderView::showHideMenuAboutToShow(){ Q_D(QtHeaderView); foreach (QAction *action, d->showhideColumnActionList) { disconnect(action, SIGNAL(toggled(bool)) , this, SLOT(showHideActionToggled(bool))); } qDeleteAll(d->showhideColumnActionList); d->showhideColumnActionList.clear(); QAction *inserting; for (int i = 0; i < count(); ++i) { inserting = new QAction(d->model->headerData(i, orientation(), Qt::DisplayRole).toString(), this); inserting->setCheckable(true); inserting->setChecked(!isSectionHidden(i)); inserting->setToolTip(d->model->headerData(i, orientation(), Qt::ToolTipRole).toString()); inserting->setIcon(d->model->headerData(i, orientation(), Qt::DecorationRole).value<QIcon>()); inserting->setStatusTip(d->model->headerData(i, orientation(), Qt::StatusTipRole).toString()); inserting->setWhatsThis(d->model->headerData(i, orientation(), Qt::WhatsThisRole).toString()); inserting->setFont(d->model->headerData(i, orientation(), Qt::FontRole).value<QFont>()); d->showhideColumnActionList.append(inserting); connect(inserting, SIGNAL(toggled(bool)), this, SLOT(showHideActionToggled(bool))); } checkOneColumn(); d->showHideColumnsMenu->addActions(d->showhideColumnActionList);}void QtHeaderView::checkOneColumn(){ Q_D(QtHeaderView); if (hiddenSectionCount() + 1 == d->showhideColumnActionList.count()) for (int i = 0; i < d->showhideColumnActionList.count(); ++i) if (d->showhideColumnActionList.value(i)->isChecked()) { d->showhideColumnActionList.value(i)->setEnabled(false); break; }}void QtHeaderView::contextMenuEvent(QContextMenuEvent *event){ Q_D(QtHeaderView); d->movableAction->setChecked(isMovable()); d->hightlightAction->setChecked(highlightSections()); d->hideColumnAction->setEnabled(d->currentLogicalIndex!=-1); if (hiddenSectionCount() + 1 == d->showhideColumnActionList.count()) { d->hideColumnAction->setEnabled(false); } else { d->hideColumnAction->setEnabled(d->currentLogicalIndex!=-1); } d->showAllColumnsAction->setEnabled(hiddenSectionCount()); if (d->sortingColumns.contains(d->currentLogicalIndex)) { d->removeSortAction->setEnabled(true); if (d->sortingColumns.value(d->currentLogicalIndex) == Qt::AscendingOrder) { d->sortDescAction->setEnabled(true); d->sortAscAction->setEnabled(false); } else { d->sortDescAction->setEnabled(false); d->sortAscAction->setEnabled(true); } } else if (d->currentLogicalIndex != -1) { d->removeSortAction->setEnabled(false); d->sortDescAction->setEnabled(true); d->sortAscAction->setEnabled(true); } else { d->removeSortAction->setEnabled(false); d->sortDescAction->setEnabled(false); d->sortAscAction->setEnabled(false); } QMenu *menu = new QMenu(this); menu->addActions(actions()); menu->exec(event->globalPos());}// disabling methodQt::SortOrder QtHeaderView::sortIndicatorOrder() const{ return Qt::AscendingOrder;}int QtHeaderView::sortIndicatorSection() const{ return 0;}void QtHeaderView::setMovableTriggered(bool movable){ QHeaderView::setMovable(movable);}void QtHeaderView::setHighlightSectionsTriggered(bool highlight){ QHeaderView::setHighlightSections(highlight);}
#ifndef QTHEADERVIEW_P_H#define QTHEADERVIEW_P_H#include "qtheaderview.h"class QAction;class QtHeaderViewPrivate { Q_DECLARE_PUBLIC(QtHeaderView);public: QtHeaderViewPrivate(){}; virtual ~QtHeaderViewPrivate(){}; QAbstractItemModel *model; QtHeaderView::SortFlag sortFlag; QHash<int, Qt::SortOrder > sortingColumns; //< section, SortOrder> QList<int> orderList; QtHeaderView * q_ptr; QAction *hideColumnAction; QAction *showAllColumnsAction; QAction *showHideSeparator; QAction *showhideColumnActionMenu; QMenu *showHideColumnsMenu; QList<QAction *> showhideColumnActionList; QAction *sortSeparator; QAction *sortAscAction; QAction *sortDescAction; QAction *removeSortAction; QAction *movableAction; QAction *hightlightAction; int currentLogicalIndex; void clearSortIndicatorsWithoutRepaint() { sortingColumns.clear(); orderList.clear(); } void removeSortIndicatorWithoutRepaint(int logicalIndex) { sortingColumns.remove(logicalIndex); orderList.removeAll(logicalIndex); } inline bool rowIntersectsSelection(int row) const { return (q_ptr->selectionModel() ? q_ptr->selectionModel()->rowIntersectsSelection(row, q_ptr->rootIndex()) : false); } inline bool columnIntersectsSelection(int column) const { return (q_ptr->selectionModel() ? q_ptr->selectionModel()->columnIntersectsSelection(column, q_ptr->rootIndex()) : false); } inline bool sectionIntersectsSelection(int logical) const { return (q_ptr->orientation() == Qt::Horizontal ? columnIntersectsSelection(logical) : rowIntersectsSelection(logical)); } inline bool isRowSelected(int row) const { return (q_ptr->selectionModel() ? q_ptr->selectionModel()->isRowSelected(row, q_ptr->rootIndex()) : false); } inline bool isColumnSelected(int column) const { return (q_ptr->selectionModel() ? q_ptr->selectionModel()->isColumnSelected(column, q_ptr->rootIndex()) : false); }};#endif // HEADERVIEW_P_H