X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fuisupport%2Fbufferview.cpp;h=f603bf587e66ed7c5d2e897722ee71bb8dcaf461;hp=7973785e1b332b9237c9fff4570c9d9ff9183b6a;hb=a453c963cf1872e14c83adf1d40a31821c166805;hpb=921e54680da16fcf2adb7a90506875aceb6633a4 diff --git a/src/uisupport/bufferview.cpp b/src/uisupport/bufferview.cpp index 7973785e..f603bf58 100644 --- a/src/uisupport/bufferview.cpp +++ b/src/uisupport/bufferview.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2015 by the Quassel Project * + * Copyright (C) 2005-2019 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -20,47 +20,46 @@ #include "bufferview.h" -#include #include +#include #include #include #include #include #include #include +#include #include "action.h" #include "buffermodel.h" -#include "bufferviewfilter.h" #include "buffersettings.h" #include "buffersyncer.h" +#include "bufferviewfilter.h" #include "client.h" #include "contextmenuactionprovider.h" #include "graphicalui.h" #include "network.h" #include "networkmodel.h" -#include "contextmenuactionprovider.h" /***************************************** -* The TreeView showing the Buffers -*****************************************/ + * The TreeView showing the Buffers + *****************************************/ // Please be carefull when reimplementing methods which are used to inform the view about changes to the data -// to be on the safe side: call QTreeView's method aswell -BufferView::BufferView(QWidget *parent) - : QTreeView(parent) +// to be on the safe side: call QTreeView's method aswell (or TreeViewTouch's) +BufferView::BufferView(QWidget* parent) + : TreeViewTouch(parent) { - connect(this, SIGNAL(collapsed(const QModelIndex &)), SLOT(storeExpandedState(const QModelIndex &))); - connect(this, SIGNAL(expanded(const QModelIndex &)), SLOT(storeExpandedState(const QModelIndex &))); + connect(this, &QTreeView::collapsed, this, &BufferView::storeExpandedState); + connect(this, &QTreeView::expanded, this, &BufferView::storeExpandedState); setSelectionMode(QAbstractItemView::ExtendedSelection); - QAbstractItemDelegate *oldDelegate = itemDelegate(); - BufferViewDelegate *tristateDelegate = new BufferViewDelegate(this); + QAbstractItemDelegate* oldDelegate = itemDelegate(); + auto* tristateDelegate = new BufferViewDelegate(this); setItemDelegate(tristateDelegate); delete oldDelegate; } - void BufferView::init() { header()->setContextMenuPolicy(Qt::ActionsContextMenu); @@ -68,13 +67,13 @@ void BufferView::init() hideColumn(2); setIndentation(10); - expandAll(); + // New entries will be expanded automatically when added; no need to call expandAll() + + header()->hide(); // nobody seems to use this anyway - header()->hide(); // nobody seems to use this anyway + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - // breaks with Qt 4.8 - if (QString("4.8.0") > qVersion()) // FIXME breaks with Qt versions >= 4.10! - setAnimated(true); + setAnimated(true); // FIXME This is to workaround bug #663 setUniformRowHeights(true); @@ -88,27 +87,23 @@ void BufferView::init() setSortingEnabled(true); sortByColumn(0, Qt::AscendingOrder); - // activated() fails on X11 and Qtopia at least -#if defined Q_WS_QWS || defined Q_WS_X11 - disconnect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(joinChannel(QModelIndex))); - connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(joinChannel(QModelIndex))); -#else +#if defined Q_OS_MACOS || defined Q_OS_WIN // afaik this is better on Mac and Windows - disconnect(this, SIGNAL(activated(QModelIndex)), this, SLOT(joinChannel(QModelIndex))); - connect(this, SIGNAL(activated(QModelIndex)), SLOT(joinChannel(QModelIndex))); + connect(this, &QAbstractItemView::activated, this, &BufferView::joinChannel, Qt::UniqueConnection); +#else + connect(this, &QAbstractItemView::doubleClicked, this, &BufferView::joinChannel, Qt::UniqueConnection); #endif } - -void BufferView::setModel(QAbstractItemModel *model) +void BufferView::setModel(QAbstractItemModel* model) { delete selectionModel(); - QTreeView::setModel(model); + TreeViewTouch::setModel(model); init(); // remove old Actions - QList oldactions = header()->actions(); - foreach(QAction *action, oldactions) { + QList oldactions = header()->actions(); + foreach (QAction* action, oldactions) { header()->removeAction(action); action->deleteLater(); } @@ -117,24 +112,28 @@ void BufferView::setModel(QAbstractItemModel *model) return; QString sectionName; - QAction *showSection; + QAction* showSection; for (int i = 1; i < model->columnCount(); i++) { sectionName = (model->headerData(i, Qt::Horizontal, Qt::DisplayRole)).toString(); showSection = new QAction(sectionName, header()); showSection->setCheckable(true); showSection->setChecked(!isColumnHidden(i)); showSection->setProperty("column", i); - connect(showSection, SIGNAL(toggled(bool)), this, SLOT(toggleHeader(bool))); + connect(showSection, &QAction::toggled, this, &BufferView::toggleHeader); header()->addAction(showSection); } - connect(model, SIGNAL(layoutChanged()), this, SLOT(on_layoutChanged())); -} + connect(model, &QAbstractItemModel::layoutChanged, this, &BufferView::on_layoutChanged); + // Make sure collapsation is correct after setting a model + // This might not be needed here, only in BufferView::setFilteredModel(). If issues arise, just + // move down to setFilteredModel (which calls this function). + setExpandedState(); +} -void BufferView::setFilteredModel(QAbstractItemModel *model_, BufferViewConfig *config) +void BufferView::setFilteredModel(QAbstractItemModel* model_, BufferViewConfig* config) { - BufferViewFilter *filter = qobject_cast(model()); + auto* filter = qobject_cast(model()); if (filter) { filter->setConfig(config); setConfig(config); @@ -142,49 +141,33 @@ void BufferView::setFilteredModel(QAbstractItemModel *model_, BufferViewConfig * } if (model()) { - disconnect(this, 0, model(), 0); - disconnect(model(), 0, this, 0); + disconnect(this, nullptr, model(), nullptr); + disconnect(model(), nullptr, this, nullptr); } if (!model_) { setModel(model_); } else { - BufferViewFilter *filter = new BufferViewFilter(model_, config); + auto* filter = new BufferViewFilter(model_, config); setModel(filter); - connect(filter, SIGNAL(configChanged()), this, SLOT(on_configChanged())); + connect(filter, &BufferViewFilter::configChanged, this, &BufferView::on_configChanged); } setConfig(config); } - -void BufferView::setSelectionModel(QItemSelectionModel *selectionModel) -{ - if (QTreeView::selectionModel()) - disconnect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), - model(), SIGNAL(checkPreviousCurrentForRemoval(QModelIndex, QModelIndex))); - - QTreeView::setSelectionModel(selectionModel); - BufferViewFilter *filter = qobject_cast(model()); - if (filter) { - connect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), - filter, SLOT(checkPreviousCurrentForRemoval(QModelIndex, QModelIndex))); - } -} - - -void BufferView::setConfig(BufferViewConfig *config) +void BufferView::setConfig(BufferViewConfig* config) { if (_config == config) return; if (_config) { - disconnect(_config, 0, this, 0); + disconnect(_config, nullptr, this, nullptr); } _config = config; if (config) { - connect(config, SIGNAL(networkIdSet(const NetworkId &)), this, SLOT(setRootIndexForNetworkId(const NetworkId &))); + connect(config, &BufferViewConfig::networkIdSet, this, &BufferView::setRootIndexForNetworkId); setRootIndexForNetworkId(config->networkId()); } else { @@ -193,8 +176,7 @@ void BufferView::setConfig(BufferViewConfig *config) } } - -void BufferView::setRootIndexForNetworkId(const NetworkId &networkId) +void BufferView::setRootIndexForNetworkId(const NetworkId& networkId) { if (!networkId.isValid() || !model()) { setIndentation(10); @@ -212,8 +194,7 @@ void BufferView::setRootIndexForNetworkId(const NetworkId &networkId) } } - -void BufferView::joinChannel(const QModelIndex &index) +void BufferView::joinChannel(const QModelIndex& index) { BufferInfo::Type bufferType = (BufferInfo::Type)index.data(NetworkModel::BufferTypeRole).value(); @@ -225,18 +206,7 @@ void BufferView::joinChannel(const QModelIndex &index) Client::userInput(bufferInfo, QString("/JOIN %1").arg(bufferInfo.bufferName())); } - -void BufferView::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { - event->accept(); - removeSelectedBuffers(); - } - QTreeView::keyPressEvent(event); -} - - -void BufferView::dropEvent(QDropEvent *event) +void BufferView::dropEvent(QDropEvent* event) { QModelIndex index = indexAt(event->pos()); @@ -244,40 +214,57 @@ void BufferView::dropEvent(QDropEvent *event) QPoint cursorPos = event->pos(); // check if we're really _on_ the item and not indicating a move to just above or below the item + // Magic margin number for this is from QAbstractItemViewPrivate::position() const int margin = 2; - if (cursorPos.y() - indexRect.top() < margin - || indexRect.bottom() - cursorPos.y() < margin) - return QTreeView::dropEvent(event); + if (cursorPos.y() - indexRect.top() < margin || indexRect.bottom() - cursorPos.y() < margin) + return TreeViewTouch::dropEvent(event); - QList > bufferList = Client::networkModel()->mimeDataToBufferList(event->mimeData()); + // If more than one buffer was being dragged, treat this as a rearrangement instead of a merge request + QList> bufferList = Client::networkModel()->mimeDataToBufferList(event->mimeData()); if (bufferList.count() != 1) - return QTreeView::dropEvent(event); + return TreeViewTouch::dropEvent(event); - NetworkId networkId = bufferList[0].first; + // Get the Buffer ID of the buffer that was being dragged BufferId bufferId2 = bufferList[0].second; - if (index.data(NetworkModel::ItemTypeRole) != NetworkModel::BufferItemType) - return QTreeView::dropEvent(event); - - if (index.data(NetworkModel::BufferTypeRole) != BufferInfo::QueryBuffer) - return QTreeView::dropEvent(event); - - if (index.data(NetworkModel::NetworkIdRole).value() != networkId) - return QTreeView::dropEvent(event); - + // Get the Buffer ID of the target buffer BufferId bufferId1 = index.data(NetworkModel::BufferIdRole).value(); - if (bufferId1 == bufferId2) - return QTreeView::dropEvent(event); - int res = QMessageBox::question(0, tr("Merge buffers permanently?"), - tr("Do you want to merge the buffer \"%1\" permanently into buffer \"%2\"?\n This cannot be reversed!").arg(Client::networkModel()->bufferName(bufferId2)).arg(Client::networkModel()->bufferName(bufferId1)), - QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + // If the source and target are the same buffer, this was an aborted rearrangement + if (bufferId1 == bufferId2) + return TreeViewTouch::dropEvent(event); + + // Get index of buffer that was being dragged + QModelIndex index2 = Client::networkModel()->bufferIndex(bufferId2); + + // If the buffer being dragged is a channel and we're still joined to it, treat this as a rearrangement + // This prevents us from being joined to a channel with no associated UI elements + if (index2.data(NetworkModel::BufferTypeRole) == BufferInfo::ChannelBuffer && index2.data(NetworkModel::ItemActiveRole) == true) + return TreeViewTouch::dropEvent(event); + + // If the source buffer is not mergeable(AKA not a Channel and not a Query), try rearranging instead + if (index2.data(NetworkModel::BufferTypeRole) != BufferInfo::ChannelBuffer + && index2.data(NetworkModel::BufferTypeRole) != BufferInfo::QueryBuffer) + return TreeViewTouch::dropEvent(event); + + // If the target buffer is not mergeable(AKA not a Channel and not a Query), try rearranging instead + if (index.data(NetworkModel::BufferTypeRole) != BufferInfo::ChannelBuffer + && index.data(NetworkModel::BufferTypeRole) != BufferInfo::QueryBuffer) + return TreeViewTouch::dropEvent(event); + + // Confirm that the user really wants to merge the buffers before doing so + int res = QMessageBox::question(nullptr, + tr("Merge buffers permanently?"), + tr("Do you want to merge the buffer \"%1\" permanently into buffer \"%2\"?\n This cannot be reversed!") + .arg(Client::networkModel()->bufferName(bufferId2)) + .arg(Client::networkModel()->bufferName(bufferId1)), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); if (res == QMessageBox::Yes) { Client::mergeBuffersPermanently(bufferId1, bufferId2); } } - void BufferView::removeSelectedBuffers(bool permanently) { if (!config()) @@ -285,7 +272,7 @@ void BufferView::removeSelectedBuffers(bool permanently) BufferId bufferId; QSet removedRows; - foreach(QModelIndex index, selectionModel()->selectedIndexes()) { + foreach (QModelIndex index, selectionModel()->selectedIndexes()) { if (index.data(NetworkModel::ItemTypeRole) != NetworkModel::BufferItemType) continue; @@ -296,7 +283,7 @@ void BufferView::removeSelectedBuffers(bool permanently) removedRows << bufferId; } - foreach(BufferId bufferId, removedRows) { + foreach (BufferId bufferId, removedRows) { if (permanently) config()->requestRemoveBufferPermanently(bufferId); else @@ -304,10 +291,9 @@ void BufferView::removeSelectedBuffers(bool permanently) } } - -void BufferView::rowsInserted(const QModelIndex &parent, int start, int end) +void BufferView::rowsInserted(const QModelIndex& parent, int start, int end) { - QTreeView::rowsInserted(parent, start, end); + TreeViewTouch::rowsInserted(parent, start, end); // ensure that newly inserted network nodes are expanded per default if (parent.data(NetworkModel::ItemTypeRole) != NetworkModel::NetworkItemType) @@ -316,7 +302,6 @@ void BufferView::rowsInserted(const QModelIndex &parent, int start, int end) setExpandedState(parent); } - void BufferView::on_layoutChanged() { int numNets = model()->rowCount(QModelIndex()); @@ -326,17 +311,27 @@ void BufferView::on_layoutChanged() } } - void BufferView::on_configChanged() { Q_ASSERT(model()); - // expand all active networks... collapse inactive ones... unless manually changed + // Expand/collapse as needed + setExpandedState(); + + if (config()) { + // update selection to current one + Client::bufferModel()->synchronizeView(this); + } +} + +void BufferView::setExpandedState() +{ + // Expand all active networks, collapse inactive ones... unless manually changed QModelIndex networkIdx; NetworkId networkId; for (int row = 0; row < model()->rowCount(); row++) { networkIdx = model()->index(row, 0); - if (model()->rowCount(networkIdx) == 0) + if (model()->rowCount(networkIdx) == 0) continue; networkId = model()->data(networkIdx, NetworkModel::NetworkIdRole).value(); @@ -345,15 +340,9 @@ void BufferView::on_configChanged() setExpandedState(networkIdx); } - - if (config()) { - // update selection to current one - Client::bufferModel()->synchronizeView(this); - } } - -void BufferView::storeExpandedState(const QModelIndex &networkIdx) +void BufferView::storeExpandedState(const QModelIndex& networkIdx) { NetworkId networkId = model()->data(networkIdx, NetworkModel::NetworkIdRole).value(); @@ -366,8 +355,7 @@ void BufferView::storeExpandedState(const QModelIndex &networkIdx) _expandedState[networkId] = oldState; } - -void BufferView::setExpandedState(const QModelIndex &networkIdx) +void BufferView::setExpandedState(const QModelIndex& networkIdx) { if (model()->data(networkIdx, NetworkModel::ItemTypeRole) != NetworkModel::NetworkItemType) return; @@ -389,18 +377,12 @@ void BufferView::setExpandedState(const QModelIndex &networkIdx) update(networkIdx); setExpanded(networkIdx, expandNetwork); } - storeExpandedState(networkIdx); // this call is needed to keep track of the isActive state + storeExpandedState(networkIdx); // this call is needed to keep track of the isActive state } -#if QT_VERSION < 0x050000 -void BufferView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +void BufferView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) { - QTreeView::dataChanged(topLeft, bottomRight); -#else -void BufferView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) -{ - QTreeView::dataChanged(topLeft, bottomRight, roles); -#endif + TreeViewTouch::dataChanged(topLeft, bottomRight, roles); // determine how many items have been changed and if any of them is a networkitem // which just swichted from active to inactive or vice versa @@ -413,15 +395,13 @@ void BufferView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bott } } - void BufferView::toggleHeader(bool checked) { - QAction *action = qobject_cast(sender()); + auto* action = qobject_cast(sender()); header()->setSectionHidden((action->property("column")).toInt(), !checked); } - -void BufferView::contextMenuEvent(QContextMenuEvent *event) +void BufferView::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if (!index.isValid()) @@ -439,34 +419,31 @@ void BufferView::contextMenuEvent(QContextMenuEvent *event) contextMenu.exec(QCursor::pos()); } - -void BufferView::addActionsToMenu(QMenu *contextMenu, const QModelIndex &index) +void BufferView::addActionsToMenu(QMenu* contextMenu, const QModelIndex& index) { QModelIndexList indexList = selectedIndexes(); // make sure the item we clicked on is first indexList.removeAll(index); indexList.prepend(index); - GraphicalUi::contextMenuActionProvider()->addActions(contextMenu, indexList, this, "menuActionTriggered", (bool)config()); + GraphicalUi::contextMenuActionProvider()->addActions(contextMenu, indexList, this, &BufferView::menuActionTriggered, (bool)config()); } - -void BufferView::addFilterActions(QMenu *contextMenu, const QModelIndex &index) +void BufferView::addFilterActions(QMenu* contextMenu, const QModelIndex& index) { - BufferViewFilter *filter = qobject_cast(model()); + auto* filter = qobject_cast(model()); if (filter) { - QList filterActions = filter->actions(index); + QList filterActions = filter->actions(index); if (!filterActions.isEmpty()) { contextMenu->addSeparator(); - foreach(QAction *action, filterActions) { + foreach (QAction* action, filterActions) { contextMenu->addAction(action); } } } } - -void BufferView::menuActionTriggered(QAction *result) +void BufferView::menuActionTriggered(QAction* result) { ContextMenuActionProvider::ActionType type = (ContextMenuActionProvider::ActionType)result->data().toInt(); switch (type) { @@ -481,26 +458,25 @@ void BufferView::menuActionTriggered(QAction *result) } } - void BufferView::nextBuffer() { changeBuffer(Forward); } - void BufferView::previousBuffer() { changeBuffer(Backward); } - void BufferView::changeBuffer(Direction direction) { QModelIndex currentIndex = selectionModel()->currentIndex(); QModelIndex resultingIndex; + QModelIndex lastNetIndex = model()->index(model()->rowCount() - 1, 0, QModelIndex()); + if (currentIndex.parent().isValid()) { - //If we are a child node just switch among siblings unless it's the first/last child + // If we are a child node just switch among siblings unless it's the first/last child resultingIndex = currentIndex.sibling(currentIndex.row() + direction, 0); if (!resultingIndex.isValid()) { @@ -512,40 +488,69 @@ void BufferView::changeBuffer(Direction direction) } } else { - //If we have a toplevel node, try and get an adjacent child + // If we have a toplevel node, try and get an adjacent child if (direction == Backward) { QModelIndex newParent = currentIndex.sibling(currentIndex.row() - 1, 0); + if (currentIndex.row() == 0) + newParent = lastNetIndex; if (model()->hasChildren(newParent)) - resultingIndex = newParent.child(model()->rowCount(newParent) - 1, 0); + resultingIndex = newParent.model()->index(model()->rowCount(newParent) - 1, 0, newParent); else resultingIndex = newParent; } else { if (model()->hasChildren(currentIndex)) - resultingIndex = currentIndex.child(0, 0); + resultingIndex = currentIndex.model()->index(0, 0, currentIndex); else resultingIndex = currentIndex.sibling(currentIndex.row() + 1, 0); } } - if (!resultingIndex.isValid()) - return; + if (!resultingIndex.isValid()) { + if (direction == Forward) + resultingIndex = model()->index(0, 0, QModelIndex()); + else + resultingIndex = lastNetIndex.model()->index(model()->rowCount(lastNetIndex) - 1, 0, lastNetIndex); + } selectionModel()->setCurrentIndex(resultingIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); selectionModel()->select(resultingIndex, QItemSelectionModel::ClearAndSelect); } +void BufferView::selectFirstBuffer() +{ + int networksCount = model()->rowCount(QModelIndex()); + if (networksCount == 0) { + return; + } + + QModelIndex bufferIndex; + for (int row = 0; row < networksCount; row++) { + QModelIndex networkIndex = model()->index(row, 0, QModelIndex()); + int childCount = model()->rowCount(networkIndex); + if (childCount > 0) { + bufferIndex = model()->index(0, 0, networkIndex); + break; + } + } + + if (!bufferIndex.isValid()) { + return; + } + + selectionModel()->setCurrentIndex(bufferIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + selectionModel()->select(bufferIndex, QItemSelectionModel::ClearAndSelect); +} -void BufferView::wheelEvent(QWheelEvent *event) +void BufferView::wheelEvent(QWheelEvent* event) { if (ItemViewSettings().mouseWheelChangesBuffer() == (bool)(event->modifiers() & Qt::AltModifier)) - return QTreeView::wheelEvent(event); + return TreeViewTouch::wheelEvent(event); int rowDelta = (event->delta() > 0) ? -1 : 1; changeBuffer((Direction)rowDelta); } - void BufferView::hideCurrentBuffer() { QModelIndex index = selectionModel()->currentIndex(); @@ -554,56 +559,94 @@ void BufferView::hideCurrentBuffer() BufferId bufferId = index.data(NetworkModel::BufferIdRole).value(); - //The check above means we won't be looking at a network, which should always be the first row, so we can just go backwards. + // The check above means we won't be looking at a network, which should always be the first row, so we can just go backwards. changeBuffer(Backward); - /*if(removedRows.contains(bufferId)) - continue; - - removedRows << bufferId;*/ - /*if(permanently) - config()->requestRemoveBufferPermanently(bufferId); - else*/ config()->requestRemoveBuffer(bufferId); } +void BufferView::filterTextChanged(const QString& filterString) +{ + auto* filter = qobject_cast(model()); + if (!filter) { + return; + } + filter->setFilterString(filterString); + on_configChanged(); // make sure collapsation is correct +} -QSize BufferView::sizeHint() const +void BufferView::changeHighlight(BufferView::Direction direction) { - return QTreeView::sizeHint(); + // If for some weird reason we get a new delegate + auto delegate = qobject_cast(itemDelegate(_currentHighlight)); + if (delegate) { + delegate->currentHighlight = QModelIndex(); + } + + QModelIndex newIndex = _currentHighlight; + if (!newIndex.isValid()) { + newIndex = model()->index(0, 0); + } + + if (direction == Backward) { + newIndex = indexBelow(newIndex); + } + else { + newIndex = indexAbove(newIndex); + } - if (!model()) - return QTreeView::sizeHint(); + if (!newIndex.isValid()) { + return; + } - if (model()->rowCount() == 0) - return QSize(120, 50); + _currentHighlight = newIndex; - int columnSize = 0; - for (int i = 0; i < model()->columnCount(); i++) { - if (!isColumnHidden(i)) - columnSize += sizeHintForColumn(i); + delegate = qobject_cast(itemDelegate(_currentHighlight)); + if (delegate) { + delegate->currentHighlight = _currentHighlight; } - return QSize(columnSize, 50); + viewport()->update(); } +void BufferView::selectHighlighted() +{ + if (_currentHighlight.isValid()) { + selectionModel()->setCurrentIndex(_currentHighlight, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + selectionModel()->select(_currentHighlight, QItemSelectionModel::ClearAndSelect); + } + else { + selectFirstBuffer(); + } + + clearHighlight(); +} + +void BufferView::clearHighlight() +{ + // If for some weird reason we get a new delegate + auto delegate = qobject_cast(itemDelegate(_currentHighlight)); + if (delegate) { + delegate->currentHighlight = QModelIndex(); + } + _currentHighlight = QModelIndex(); + viewport()->update(); +} // **************************************** -// BufferViewDelgate +// BufferViewDelegate // **************************************** class ColorsChangedEvent : public QEvent { public: - ColorsChangedEvent() : QEvent(QEvent::User) {}; + ColorsChangedEvent() + : QEvent(QEvent::User){}; }; - -BufferViewDelegate::BufferViewDelegate(QObject *parent) +BufferViewDelegate::BufferViewDelegate(QObject* parent) : QStyledItemDelegate(parent) -{ -} +{} - -void BufferViewDelegate::customEvent(QEvent *event) +void BufferViewDelegate::customEvent(QEvent* event) { if (event->type() != QEvent::User) return; @@ -611,8 +654,7 @@ void BufferViewDelegate::customEvent(QEvent *event) event->accept(); } - -bool BufferViewDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) +bool BufferViewDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { if (event->type() != QEvent::MouseButtonRelease) return QStyledItemDelegate::editorEvent(event, model, option, index); @@ -624,16 +666,16 @@ bool BufferViewDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, c if (!value.isValid()) return QStyledItemDelegate::editorEvent(event, model, option, index); - QStyleOptionViewItemV4 viewOpt(option); + QStyleOptionViewItem viewOpt(option); initStyleOption(&viewOpt, index); QRect checkRect = viewOpt.widget->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, viewOpt.widget); - QMouseEvent *me = static_cast(event); + auto* me = static_cast(event); if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos())) return QStyledItemDelegate::editorEvent(event, model, option, index); - Qt::CheckState state = static_cast(value.toInt()); + auto state = static_cast(value.toInt()); if (state == Qt::Unchecked) state = Qt::PartiallyChecked; else if (state == Qt::PartiallyChecked) @@ -644,22 +686,48 @@ bool BufferViewDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, c return true; } - // ============================== // BufferView Dock // ============================== -BufferViewDock::BufferViewDock(BufferViewConfig *config, QWidget *parent) - : QDockWidget(parent), - _active(false), - _title(config->bufferViewName()) +BufferViewDock::BufferViewDock(BufferViewConfig* config, QWidget* parent) + : QDockWidget(parent) + , _childWidget(nullptr) + , _widget(new QWidget(parent)) + , _filterEdit(new QLineEdit(parent)) + , _active(false) + , _title(config->bufferViewName()) { setObjectName("BufferViewDock-" + QString::number(config->bufferViewId())); toggleViewAction()->setData(config->bufferViewId()); - setAllowedAreas(Qt::RightDockWidgetArea|Qt::LeftDockWidgetArea); - connect(config, SIGNAL(bufferViewNameSet(const QString &)), this, SLOT(bufferViewRenamed(const QString &))); + setAllowedAreas(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea); + connect(config, &BufferViewConfig::bufferViewNameSet, this, &BufferViewDock::bufferViewRenamed); + connect(config, &BufferViewConfig::configChanged, this, &BufferViewDock::configChanged); updateTitle(); + + _widget->setLayout(new QVBoxLayout); + _widget->layout()->setSpacing(0); + _widget->layout()->setContentsMargins(0, 0, 0, 0); + + // We need to potentially hide it early, so it doesn't flicker + _filterEdit->setVisible(config->showSearch()); + _filterEdit->setFocusPolicy(Qt::ClickFocus); + _filterEdit->installEventFilter(this); + _filterEdit->setPlaceholderText(tr("Search...")); + connect(_filterEdit, &QLineEdit::returnPressed, this, &BufferViewDock::onFilterReturnPressed); + + _widget->layout()->addWidget(_filterEdit); + QDockWidget::setWidget(_widget); } +void BufferViewDock::setLocked(bool locked) +{ + if (locked) { + setFeatures(nullptr); + } + else { + setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); + } +} void BufferViewDock::updateTitle() { @@ -669,29 +737,108 @@ void BufferViewDock::updateTitle() setWindowTitle(title); } +void BufferViewDock::configChanged() +{ + if (_filterEdit->isVisible() != config()->showSearch()) { + _filterEdit->setVisible(config()->showSearch()); + _filterEdit->clear(); + } +} + +void BufferViewDock::onFilterReturnPressed() +{ + if (_oldFocusItem) { + _oldFocusItem->setFocus(); + _oldFocusItem = nullptr; + } + + if (!config()->showSearch()) { + _filterEdit->setVisible(false); + } + + BufferView* view = bufferView(); + if (!view) { + return; + } + + if (!_filterEdit->text().isEmpty()) { + view->selectHighlighted(); + _filterEdit->clear(); + } + else { + view->clearHighlight(); + } +} void BufferViewDock::setActive(bool active) { if (active != isActive()) { _active = active; updateTitle(); - if (active) + if (active) { raise(); // for tabbed docks + } } } +bool BufferViewDock::eventFilter(QObject* object, QEvent* event) +{ + if (object != _filterEdit) { + return false; + } -void BufferViewDock::bufferViewRenamed(const QString &newName) + if (event->type() == QEvent::FocusOut) { + if (!config()->showSearch() && _filterEdit->text().isEmpty()) { + _filterEdit->setVisible(false); + return true; + } + } + else if (event->type() == QEvent::KeyRelease) { + auto keyEvent = static_cast(event); + + BufferView* view = bufferView(); + if (!view) { + return false; + } + + switch (keyEvent->key()) { + case Qt::Key_Escape: { + _filterEdit->clear(); + + if (!_oldFocusItem) { + return false; + } + + _oldFocusItem->setFocus(); + _oldFocusItem = nullptr; + return true; + } + case Qt::Key_Down: + view->changeHighlight(BufferView::Backward); + return true; + case Qt::Key_Up: + view->changeHighlight(BufferView::Forward); + return true; + default: + break; + } + + return false; + } + + return false; +} + +void BufferViewDock::bufferViewRenamed(const QString& newName) { _title = newName; updateTitle(); toggleViewAction()->setText(newName); } - int BufferViewDock::bufferViewId() const { - BufferView *view = bufferView(); + BufferView* view = bufferView(); if (!view) return 0; @@ -701,12 +848,40 @@ int BufferViewDock::bufferViewId() const return 0; } - -BufferViewConfig *BufferViewDock::config() const +BufferViewConfig* BufferViewDock::config() const { - BufferView *view = bufferView(); + BufferView* view = bufferView(); if (!view) - return 0; + return nullptr; else return view->config(); } + +void BufferViewDock::setWidget(QWidget* newWidget) +{ + _widget->layout()->addWidget(newWidget); + _childWidget = newWidget; + + connect(_filterEdit, &QLineEdit::textChanged, bufferView(), &BufferView::filterTextChanged); +} + +void BufferViewDock::activateFilter() +{ + if (!_filterEdit->isVisible()) { + _filterEdit->setVisible(true); + } + + _oldFocusItem = qApp->focusWidget(); + + _filterEdit->setFocus(); +} + + +void BufferViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem newOption = option; + if (index == currentHighlight) { + newOption.state |= QStyle::State_HasFocus; + } + QStyledItemDelegate::paint(painter, newOption, index); +}