X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fchatscene.cpp;h=188eba6c0b6baad84c100878355870a325c79563;hp=2c89519f5d26a1ebb448b5198ae8995c2fa02f4b;hb=faa66f7e00aeeea661b46f01bdfc715567eef669;hpb=9d54503555534a2c554f09a33df6afa33d6308ec diff --git a/src/qtui/chatscene.cpp b/src/qtui/chatscene.cpp index 2c89519f..188eba6c 100644 --- a/src/qtui/chatscene.cpp +++ b/src/qtui/chatscene.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2014 by the Quassel Project * + * Copyright (C) 2005-2016 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -20,20 +20,25 @@ #include #include +#include #include #include +#include #include #include #include #include +#include -#ifdef HAVE_KDE +#ifdef HAVE_KDE4 # include #else # include #endif -#ifdef HAVE_WEBKIT +#ifdef HAVE_WEBENGINE +# include +#elif defined HAVE_WEBKIT # include #endif @@ -46,7 +51,6 @@ #include "clientbacklogmanager.h" #include "columnhandleitem.h" #include "contextmenuactionprovider.h" -#include "iconloader.h" #include "mainwin.h" #include "markerlineitem.h" #include "messagefilter.h" @@ -87,12 +91,12 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w connect(this, SIGNAL(sceneRectChanged(const QRectF &)), _markerLine, SLOT(sceneRectChanged(const QRectF &))); ChatViewSettings defaultSettings; - int defaultFirstColHandlePos = defaultSettings.value("FirstColumnHandlePos", 80).toInt(); - int defaultSecondColHandlePos = defaultSettings.value("SecondColumnHandlePos", 200).toInt(); + _defaultFirstColHandlePos = defaultSettings.value("FirstColumnHandlePos", 80).toInt(); + _defaultSecondColHandlePos = defaultSettings.value("SecondColumnHandlePos", 200).toInt(); ChatViewSettings viewSettings(this); - _firstColHandlePos = viewSettings.value("FirstColumnHandlePos", defaultFirstColHandlePos).toInt(); - _secondColHandlePos = viewSettings.value("SecondColumnHandlePos", defaultSecondColHandlePos).toInt(); + _firstColHandlePos = viewSettings.value("FirstColumnHandlePos", _defaultFirstColHandlePos).toInt(); + _secondColHandlePos = viewSettings.value("SecondColumnHandlePos", _defaultSecondColHandlePos).toInt(); _firstColHandle = new ColumnHandleItem(QtUi::style()->firstColumnSeparator()); addItem(_firstColHandle); @@ -120,13 +124,20 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w this, SLOT(rowsRemoved())); connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(dataChanged(QModelIndex, QModelIndex))); -#ifdef HAVE_WEBKIT +#if defined HAVE_WEBKIT || defined HAVE_WEBENGINE webPreview.timer.setSingleShot(true); connect(&webPreview.timer, SIGNAL(timeout()), this, SLOT(webPreviewNextStep())); #endif _showWebPreview = defaultSettings.showWebPreview(); defaultSettings.notify("ShowWebPreview", this, SLOT(showWebPreviewChanged())); + _showSenderBrackets = defaultSettings.showSenderBrackets(); + defaultSettings.notify("ShowSenderBrackets", this, SLOT(showSenderBracketsChanged())); + + _timestampFormatString = defaultSettings.timestampFormatString(); + defaultSettings.notify("TimestampFormat", this, SLOT(timestampFormatStringChanged())); + updateTimestampHasBrackets(); + _clickTimer.setInterval(QApplication::doubleClickInterval()); _clickTimer.setSingleShot(true); connect(&_clickTimer, SIGNAL(timeout()), SLOT(clickTimeout())); @@ -157,6 +168,17 @@ ColumnHandleItem *ChatScene::secondColumnHandle() const return _secondColHandle; } +void ChatScene::resetColumnWidths() +{ + //make sure first column is at least 80 px wide, second 120 px + int firstColHandlePos = qMax(_defaultFirstColHandlePos, + 80); + int secondColHandlePos = qMax(_defaultSecondColHandlePos, + firstColHandlePos + 120); + + _firstColHandle->setXPos(firstColHandlePos); + _secondColHandle->setXPos(secondColHandlePos); +} ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact, bool ignoreDayChange) const { @@ -638,7 +660,7 @@ void ChatScene::firstHandlePositionChanged(qreal xpos) QPointF senderPos(firstColumnHandle()->sceneRight(), 0); while (lineIter != lineIterBegin) { - lineIter--; + --lineIter; (*lineIter)->setFirstColumn(timestampWidth, senderWidth, senderPos); } //setItemIndexMethod(QGraphicsScene::BspTreeIndex); @@ -674,7 +696,7 @@ void ChatScene::secondHandlePositionChanged(qreal xpos) qreal contentsWidth = _sceneRect.width() - secondColumnHandle()->sceneRight(); QPointF contentsPos(secondColumnHandle()->sceneRight(), 0); while (lineIter != lineIterBegin) { - lineIter--; + --lineIter; (*lineIter)->setSecondColumn(senderWidth, contentsWidth, contentsPos, linePos); } //setItemIndexMethod(QGraphicsScene::BspTreeIndex); @@ -810,14 +832,27 @@ void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) // If we have text selected, insert the Copy Selection as first item if (isPosOverSelection(pos)) { QAction *sep = menu.insertSeparator(menu.actions().first()); - QAction *act = new Action(SmallIcon("edit-copy"), tr("Copy Selection"), &menu, this, + QAction *act = new Action(QIcon::fromTheme("edit-copy"), tr("Copy Selection"), &menu, this, SLOT(selectionToClipboard()), QKeySequence::Copy); menu.insertAction(sep, act); + + QString searchSelectionText = selection(); + if (searchSelectionText.length() > _webSearchSelectionTextMaxVisible) + searchSelectionText = searchSelectionText.left(_webSearchSelectionTextMaxVisible).append(QString::fromUtf8("…")); + searchSelectionText = tr("Search '%1'").arg(searchSelectionText); + + QAction *webSearchAction = new Action(QIcon::fromTheme("edit-find"), searchSelectionText, &menu, this, SLOT(webSearchOnSelection())); + menu.insertAction(sep, webSearchAction); } if (QtUi::mainWindow()->menuBar()->isHidden()) menu.addAction(QtUi::actionCollection("General")->action("ToggleMenuBar")); + // show column reset action if columns have been resized in this session or there is at least one very narrow column + if ((_firstColHandlePos != _defaultFirstColHandlePos) || (_secondColHandlePos != _defaultSecondColHandlePos) || + (_firstColHandlePos <= 10) || (_secondColHandlePos - _firstColHandlePos <= 10)) + menu.addAction(new Action(tr("Reset Column Widths"), &menu, this, SLOT(resetColumnWidths()), 0)); + menu.exec(event->screenPos()); } @@ -995,12 +1030,34 @@ QString ChatScene::selection() const return QString(); } QString result; + for (int l = start; l <= end; l++) { - if (_selectionMinCol == ChatLineModel::TimestampColumn) - result += _lines[l]->item(ChatLineModel::TimestampColumn)->data(MessageModel::DisplayRole).toString() + " "; - if (_selectionMinCol <= ChatLineModel::SenderColumn) - result += _lines[l]->item(ChatLineModel::SenderColumn)->data(MessageModel::DisplayRole).toString() + " "; - result += _lines[l]->item(ChatLineModel::ContentsColumn)->data(MessageModel::DisplayRole).toString() + "\n"; + if (_selectionMinCol == ChatLineModel::TimestampColumn) { + ChatItem *item = _lines[l]->item(ChatLineModel::TimestampColumn); + if (!_showSenderBrackets && !_timestampHasBrackets) { + // Only re-add brackets if the current timestamp format does not include them + // -and- sender brackets are disabled. Don't filter on Message::Plain as + // timestamp brackets affect all types of messages. + // Remove any spaces before and after, otherwise it may look weird. + result += QString("[%1] ").arg(item->data(MessageModel::DisplayRole) + .toString().trimmed()); + } else { + result += item->data(MessageModel::DisplayRole).toString() + " "; + } + } + if (_selectionMinCol <= ChatLineModel::SenderColumn) { + ChatItem *item = _lines[l]->item(ChatLineModel::SenderColumn); + if (!_showSenderBrackets && item->chatLine()->msgType() == Message::Plain) { + // Copying to plain-text. Only re-add the sender brackets if they're normally + // hidden. + result += QString("<%1> ").arg(item->data(MessageModel::DisplayRole) + .toString()); + } else { + result += item->data(MessageModel::DisplayRole).toString() + " "; + } + } + result += _lines[l]->item(ChatLineModel::ContentsColumn) + ->data(MessageModel::DisplayRole).toString() + "\n"; } return result; } @@ -1047,6 +1104,21 @@ void ChatScene::clearSelection() } +/******** *************************************************************************************/ + +void ChatScene::webSearchOnSelection() +{ + if (!hasSelection()) + return; + + ChatViewSettings settings; + QString webSearchBaseUrl = settings.webSearchUrlFormatString(); + QString webSearchUrl = webSearchBaseUrl.replace(QString("%s"), selection()); + QUrl url = QUrl::fromUserInput(webSearchUrl); + QDesktopServices::openUrl(url); +} + + /******** *************************************************************************************/ void ChatScene::requestBacklog() @@ -1130,9 +1202,9 @@ void ChatScene::updateSceneRect(const QRectF &rect) // ======================================== -// Webkit Only stuff +// Webkit/WebEngine Only stuff // ======================================== -#ifdef HAVE_WEBKIT +#if defined HAVE_WEBKIT || defined HAVE_WEBENGINE void ChatScene::loadWebPreview(ChatItem *parentItem, const QUrl &url, const QRectF &urlRect) { if (!_showWebPreview) @@ -1261,8 +1333,47 @@ void ChatScene::clearWebPreview(ChatItem *parentItem) // end of webkit only // ======================================== +// Local configuration caching void ChatScene::showWebPreviewChanged() { ChatViewSettings settings; _showWebPreview = settings.showWebPreview(); } + +void ChatScene::showSenderBracketsChanged() +{ + ChatViewSettings settings; + _showSenderBrackets = settings.showSenderBrackets(); +} + +void ChatScene::timestampFormatStringChanged() +{ + ChatViewSettings settings; + _timestampFormatString = settings.timestampFormatString(); + updateTimestampHasBrackets(); +} + +void ChatScene::updateTimestampHasBrackets() +{ + // Calculate these parameters only as needed, rather than on-demand + + // Does the timestamp format contain brackets? For example: + // Classic: "[hh:mm:ss]" + // Modern: " hh:mm:ss" + // + // Match groups of any opening or closing brackets - (), {}, [], <>, (>, {], etc: + // ^\s*[({[<].+[)}\]>]\s*$ + // [...] is a character group containing ... + // ^ matches start of string + // \s* matches any amount of whitespace + // [({[<] matches (, {, [, or < + // .+ matches one or more characters + // [)}\]>] matches ), }, ], or >, escaping the ] + // $ matches end of string + // Alternatively, if opening and closing brackets must be in pairs, use this: + // (^\s*\(.+\)\s*$)|(^\s*\{.+\}\s*$)|(^\s*\[.+\]\s*$)|(^\s*<.+>\s*$) + // Note that '\' must be escaped as '\\' + // Helpful interactive website for debugging and explaining: https://regex101.com/ + const QRegExp regExpMatchBrackets("^\\s*[({[<].+[)}\\]>]\\s*$"); + _timestampHasBrackets = regExpMatchBrackets.exactMatch(_timestampFormatString); +}