X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fchatviewsearchcontroller.cpp;h=997c1953b7af4b7268d82c1f68597f6eabc91d6b;hp=591c20e38d497e0360af3cbc751b83a8d0cf9397;hb=f824db0e31b54969e0b7fa0b5405b1e9173d482c;hpb=0789d47e81940cbbdf925466442380063a50ac05 diff --git a/src/qtui/chatviewsearchcontroller.cpp b/src/qtui/chatviewsearchcontroller.cpp index 591c20e3..997c1953 100644 --- a/src/qtui/chatviewsearchcontroller.cpp +++ b/src/qtui/chatviewsearchcontroller.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-08 by the Quassel Project * + * Copyright (C) 2005-09 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -31,6 +31,7 @@ ChatViewSearchController::ChatViewSearchController(QObject *parent) : QObject(parent), _scene(0), + _currentHighlight(0), _caseSensitive(false), _searchSenders(false), _searchMsgs(true), @@ -68,60 +69,175 @@ void ChatViewSearchController::setSearchString(const QString &searchString) { return; connect(_scene, SIGNAL(destroyed()), this, SLOT(sceneDestroyed())); + connect(_scene, SIGNAL(layoutChanged()), this, SLOT(repositionHighlights())); updateHighlights(); } +void ChatViewSearchController::highlightNext() { + if(_highlightItems.isEmpty()) + return; + + if(_currentHighlight < _highlightItems.count()) { + _highlightItems.at(_currentHighlight)->setHighlighted(false); + } + _currentHighlight++; + if(_currentHighlight >= _highlightItems.count()) + _currentHighlight = 0; + _highlightItems.at(_currentHighlight)->setHighlighted(true); + emit newCurrentHighlight(_highlightItems.at(_currentHighlight)); +} + +void ChatViewSearchController::highlightPrev() { + if(_highlightItems.isEmpty()) + return; + + if(_currentHighlight < _highlightItems.count()) { + _highlightItems.at(_currentHighlight)->setHighlighted(false); + } + + _currentHighlight--; + if(_currentHighlight < 0) + _currentHighlight = _highlightItems.count() - 1; + _highlightItems.at(_currentHighlight)->setHighlighted(true); + emit newCurrentHighlight(_highlightItems.at(_currentHighlight)); +} void ChatViewSearchController::updateHighlights(bool reuse) { if(!_scene) return; + if(reuse) { + QSet chatLines; + foreach(SearchHighlightItem *highlightItem, _highlightItems) { + ChatLine *line = qgraphicsitem_cast(highlightItem->parentItem()); + if(line) + chatLines << line; + } + foreach(ChatLine *line, QList(chatLines.toList())) { + updateHighlights(line); + } + } else { + QPointF oldHighlightPos; + if(!_highlightItems.isEmpty() && _currentHighlight < _highlightItems.count()) { + oldHighlightPos = _highlightItems[_currentHighlight]->scenePos(); + } + qDeleteAll(_highlightItems); + _highlightItems.clear(); + Q_ASSERT(_highlightItems.isEmpty()); + + if(searchString().isEmpty() || !(_searchSenders || _searchMsgs)) + return; + + checkMessagesForHighlight(); + + if(!_highlightItems.isEmpty()) { + if(!oldHighlightPos.isNull()) { + int start = 0; int end = _highlightItems.count() - 1; + QPointF startPos; + QPointF endPos; + while(1) { + startPos = _highlightItems[start]->scenePos(); + endPos = _highlightItems[end]->scenePos(); + if(startPos == oldHighlightPos) { + _currentHighlight = start; + break; + } + if(endPos == oldHighlightPos) { + _currentHighlight = end; + break; + } + if(end - start == 1) { + _currentHighlight = start; + break; + } + int pivot = (end + start) / 2; + QPointF pivotPos = _highlightItems[pivot]->scenePos(); + if(startPos.y() == endPos.y()) { + if(oldHighlightPos.x() <= pivotPos.x()) + end = pivot; + else + start = pivot; + } else { + if(oldHighlightPos.y() <= pivotPos.y()) + end = pivot; + else + start = pivot; + } + } + } else { + _currentHighlight = _highlightItems.count() - 1; + } + _highlightItems[_currentHighlight]->setHighlighted(true); + emit newCurrentHighlight(_highlightItems[_currentHighlight]); + } + } +} + +void ChatViewSearchController::checkMessagesForHighlight(int start, int end) { QAbstractItemModel *model = _scene->model(); Q_ASSERT(model); + if(end == -1) { + end = model->rowCount() - 1; + if(end == -1) + return; + } - QList chatLines; - if(reuse) { - foreach(SearchHighlightItem *highlightItem, _highlightItems) { - ChatLine *line = dynamic_cast(highlightItem->parentItem()); - if(!line || chatLines.contains(line)) + QModelIndex index; + for(int row = start; row <= end; row++) { + if(_searchOnlyRegularMsgs) { + index = model->index(row, 0); + if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt())) continue; - chatLines << line; + highlightLine(_scene->chatLine(row)); } } +} - qDeleteAll(_highlightItems); - _highlightItems.clear(); - Q_ASSERT(_highlightItems.isEmpty()); +void ChatViewSearchController::updateHighlights(ChatLine *line) { + QList checkItems; + if(_searchSenders) + checkItems << &(line->item(MessageModel::SenderColumn)); - if(searchString().isEmpty() || !(_searchSenders || _searchMsgs)) - return; + if(_searchMsgs) + checkItems << &(line->item(MessageModel::ContentsColumn)); - if(reuse) { - QModelIndex index; - foreach(ChatLine *line, chatLines) { - if(_searchOnlyRegularMsgs) { - index = model->index(line->row(), 0); - if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt())) - continue; - } - highlightLine(line); + QHash > wordRects; + foreach(ChatItem *item, checkItems) { + foreach(QRectF wordRect, item->findWords(searchString(), caseSensitive())) { + wordRects[wordRect.x() + item->x()][wordRect.y()] = wordRect; } - } else { - // we have to crawl through the data - QModelIndex index; - QString plainText; - int rowCount = model->rowCount(); - for(int row = 0; row < rowCount; row++) { - ChatLine *line = _scene->chatLine(row); - - if(_searchOnlyRegularMsgs) { - index = model->index(row, 0); - if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt())) - continue; + } + + bool deleteAll = false; + QAbstractItemModel *model = _scene->model(); + Q_ASSERT(model); + if(_searchOnlyRegularMsgs) { + QModelIndex index = model->index(line->row(), 0); + if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt())) + deleteAll = true; + } + + + foreach(QGraphicsItem *child, line->childItems()) { + SearchHighlightItem *highlightItem = qgraphicsitem_cast(child); + if(!highlightItem) + continue; + + if(!deleteAll && wordRects.contains(highlightItem->pos().x()) && wordRects[highlightItem->pos().x()].contains(highlightItem->pos().y())) { + QRectF &wordRect = wordRects[highlightItem->pos().x()][highlightItem->pos().y()]; + highlightItem->updateGeometry(wordRect.width(), wordRect.height()); + } else { + int pos = _highlightItems.indexOf(highlightItem); + if(pos == _currentHighlight) { + highlightPrev(); + } else if (pos < _currentHighlight) { + _currentHighlight--; } - highlightLine(line); + + _highlightItems.removeAt(pos); + delete highlightItem; } } } @@ -141,6 +257,50 @@ void ChatViewSearchController::highlightLine(ChatLine *line) { } } +void ChatViewSearchController::repositionHighlights() { + QSet chatLines; + foreach(SearchHighlightItem *item, _highlightItems) { + ChatLine *line = qgraphicsitem_cast(item->parentItem()); + if(line) + chatLines << line; + } + QList chatLineList(chatLines.toList()); + foreach(ChatLine *line, chatLineList) { + repositionHighlights(line); + } +} + +void ChatViewSearchController::repositionHighlights(ChatLine *line) { + QList searchHighlights; + foreach(QGraphicsItem *child, line->childItems()) { + SearchHighlightItem *highlightItem = qgraphicsitem_cast(child); + if(highlightItem) + searchHighlights << highlightItem; + } + + if(searchHighlights.isEmpty()) + return; + + QList wordPos; + if(_searchSenders) { + foreach(QRectF wordRect, line->senderItem().findWords(searchString(), caseSensitive())) { + wordPos << QPointF(wordRect.x() + line->senderItem().x(), wordRect.y()); + } + } + if(_searchMsgs) { + foreach(QRectF wordRect, line->contentsItem().findWords(searchString(), caseSensitive())) { + wordPos << QPointF(wordRect.x() + line->contentsItem().x(), wordRect.y()); + } + } + + qSort(searchHighlights.begin(), searchHighlights.end(), SearchHighlightItem::firstInLine); + + Q_ASSERT(wordPos.count() == searchHighlights.count()); + for(int i = 0; i < searchHighlights.count(); i++) { + searchHighlights.at(i)->setPos(wordPos.at(i)); + } +} + void ChatViewSearchController::sceneDestroyed() { // WARNING: don't call any methods on scene! _scene = 0; @@ -192,21 +352,63 @@ void ChatViewSearchController::setSearchOnlyRegularMsgs(bool searchOnlyRegularMs updateHighlights(searchOnlyRegularMsgs); } + +// ================================================== +// SearchHighlightItem +// ================================================== SearchHighlightItem::SearchHighlightItem(QRectF wordRect, QGraphicsItem *parent) - : QGraphicsItem(parent), - _boundingRect(QRectF(-wordRect.width() / 2, -wordRect.height() / 2, wordRect.width(), wordRect.height())) + : QObject(), + QGraphicsItem(parent), + _highlighted(false), + _alpha(100), + _timeLine(150) { - setPos(wordRect.x() + wordRect.width() / 2 , wordRect.y() + wordRect.height() / 2); - scale(1.2, 1.2); + setPos(wordRect.x(), wordRect.y()); + updateGeometry(wordRect.width(), wordRect.height()); + + connect(&_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(updateHighlight(qreal))); +} + +void SearchHighlightItem::setHighlighted(bool highlighted) { + _highlighted = highlighted; + + if(highlighted) + _timeLine.setDirection(QTimeLine::Forward); + else + _timeLine.setDirection(QTimeLine::Backward); + + if(_timeLine.state() != QTimeLine::Running) + _timeLine.start(); + + update(); +} + +void SearchHighlightItem::updateHighlight(qreal value) { + _alpha = 100 + 155 * value; + update(); } void SearchHighlightItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); - painter->setPen(QPen(Qt::black, 1.5)); - //painter->setBrush(QColor(127, 133, 250)); - painter->setBrush(QColor(254, 237, 45)); + painter->setPen(QPen(QColor(0, 0, 0, _alpha), 1.5)); + painter->setBrush(QColor(254, 237, 45, _alpha)); painter->setRenderHints(QPainter::Antialiasing); - painter->drawRoundedRect(boundingRect(), 30, 30, Qt::RelativeSize); + qreal radius = boundingRect().height() * 0.30; + painter->drawRoundedRect(boundingRect(), radius, radius); +} + +void SearchHighlightItem::updateGeometry(qreal width, qreal height) { + prepareGeometryChange(); + qreal sizedelta = height * 0.1; + _boundingRect = QRectF(-sizedelta, -sizedelta, width + 2 * sizedelta, height + 2 * sizedelta); + update(); +} + +bool SearchHighlightItem::firstInLine(QGraphicsItem *item1, QGraphicsItem *item2) { + if(item1->pos().y() != item2->pos().y()) + return item1->pos().y() < item2->pos().y(); + else + return item1->pos().x() < item2->pos().x(); }