X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fuisupport%2Fclickable.cpp;h=3e704179847e3612e03039f794f5a8b4bf2ed677;hp=d19083c825a76f838aed8a2f67b533c187e65a4b;hb=c64a887d0f05222590299fb2bb8d56fa9fadb16d;hpb=55579f53f3bd37f2a20d9be7458bdc54b345a052 diff --git a/src/uisupport/clickable.cpp b/src/uisupport/clickable.cpp index d19083c8..3e704179 100644 --- a/src/uisupport/clickable.cpp +++ b/src/uisupport/clickable.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-09 by the Quassel Project * + * Copyright (C) 2005-2012 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -15,72 +15,114 @@ * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include +#include +#include + +#include "buffermodel.h" #include "clickable.h" +#include "client.h" + +void Clickable::activate(NetworkId networkId, const QString &text) const +{ + if (!isValid()) + return; + + QString str = text.mid(start(), length()); + + switch (type()) { + case Clickable::Url: + if (!str.contains("://")) + str = "http://" + str; + QDesktopServices::openUrl(QUrl::fromEncoded(str.toUtf8(), QUrl::TolerantMode)); + break; + case Clickable::Channel: + Client::bufferModel()->switchToOrJoinBuffer(networkId, str); + break; + default: + break; + } +} + // NOTE: This method is not threadsafe and not reentrant! // (RegExps are not constant while matching, and they are static here for efficiency) -ClickableList ClickableList::fromString(const QString &str) { - // For matching URLs - static QString urlEnd("(?:>|[,.;:\"]*\\s|\\b|$)"); - static QString urlChars("(?:[,.;:]*[\\w\\-~@/?&=+$()!%#*|{}\\[\\]'^])"); - - static QRegExp regExp[] = { - // URL - // QRegExp(QString("((?:https?://|s?ftp://|irc://|mailto:|www\\.)%1+|%1+\\.[a-z]{2,4}(?:?=/%1+|\\b))%2").arg(urlChars, urlEnd)), - QRegExp(QString("((?:(?:mailto:|\\w+://)|www\\.)%1+)%2").arg(urlChars, urlEnd), Qt::CaseInsensitive), - - // Channel name - // We don't match for channel names starting with + or &, because that gives us a lot of false positives. - QRegExp("((?:#|![A-Z0-9]{5})[^,:\\s]+(?::[^,:\\s]+)?)\\b", Qt::CaseInsensitive) - - // TODO: Nicks, we'll need a filtering for only matching known nicknames further down if we do this - }; - - static const int regExpCount = 2; // number of regexps in the array above - - qint16 matches[] = { 0, 0, 0 }; - qint16 matchEnd[] = { 0, 0, 0 }; - - ClickableList result; - //QString str = data(ChatLineModel::DisplayRole).toString(); - - qint16 idx = 0; - qint16 minidx; - int type = -1; - - do { - type = -1; - minidx = str.length(); - for(int i = 0; i < regExpCount; i++) { - if(matches[i] < 0 || matchEnd[i] > str.length()) continue; - if(idx >= matchEnd[i]) { - matches[i] = regExp[i].indexIn(str, qMax(matchEnd[i], idx)); - if(matches[i] >= 0) matchEnd[i] = matches[i] + regExp[i].cap(1).length(); - } - if(matches[i] >= 0 && matches[i] < minidx) { - minidx = matches[i]; - type = i; - } - } - if(type >= 0) { - idx = matchEnd[type]; - QString match = str.mid(matches[type], matchEnd[type] - matches[type]); - if(type == Clickable::Url && str.at(idx-1) == ')') { // special case: closing paren only matches if we had an open one - if(!match.contains('(')) { - matchEnd[type]--; - match.chop(1); +ClickableList ClickableList::fromString(const QString &str) +{ + // For matching URLs + static QString scheme("(?:(?:mailto:|(?:[+.-]?\\w)+://)|www(?=\\.\\S+\\.))"); + static QString authority("(?:(?:[,.;@:]?[-\\w]+)+\\.?|\\[[0-9a-f:.]+\\])(?::\\d+)?"); + static QString urlChars("(?:[,.;:]*[\\w~@/?&=+$()!%#*{}\\[\\]\\|'^-])"); + static QString urlEnd("(?:>|[,.;:\"]*\\s|\\b|$)"); + + static QRegExp regExp[] = { + // URL + // QRegExp(QString("((?:https?://|s?ftp://|irc://|mailto:|www\\.)%1+|%1+\\.[a-z]{2,4}(?:?=/%1+|\\b))%2").arg(urlChars, urlEnd)), + QRegExp(QString("\\b(%1%2(?:/%3*)?)%4").arg(scheme, authority, urlChars, urlEnd), Qt::CaseInsensitive), + + // Channel name + // We don't match for channel names starting with + or &, because that gives us a lot of false positives. + QRegExp("((?:#|![A-Z0-9]{5})[^,:\\s]+(?::[^,:\\s]+)?)\\b", Qt::CaseInsensitive) + + // TODO: Nicks, we'll need a filtering for only matching known nicknames further down if we do this + }; + + static const int regExpCount = 2; // number of regexps in the array above + + qint16 matches[] = { 0, 0, 0 }; + qint16 matchEnd[] = { 0, 0, 0 }; + + ClickableList result; + //QString str = data(ChatLineModel::DisplayRole).toString(); + + qint16 idx = 0; + qint16 minidx; + int type = -1; + + do { + type = -1; + minidx = str.length(); + for (int i = 0; i < regExpCount; i++) { + if (matches[i] < 0 || matchEnd[i] > str.length()) continue; + if (idx >= matchEnd[i]) { + matches[i] = regExp[i].indexIn(str, qMax(matchEnd[i], idx)); + if (matches[i] >= 0) matchEnd[i] = matches[i] + regExp[i].cap(1).length(); + } + if (matches[i] >= 0 && matches[i] < minidx) { + minidx = matches[i]; + type = i; + } } - } - if(type == Clickable::Channel) { - // don't make clickable if it could be a #number - if(QRegExp("^#\\d+$").exactMatch(match)) - continue; - } - result.append(Clickable((Clickable::Type)type, matches[type], matchEnd[type] - matches[type])); + if (type >= 0) { + idx = matchEnd[type]; + QString match = str.mid(matches[type], matchEnd[type] - matches[type]); + if (type == Clickable::Url && str.at(idx-1) == ')') { // special case: closing paren only matches if we had an open one + if (!match.contains('(')) { + matchEnd[type]--; + match.chop(1); + } + } + if (type == Clickable::Channel) { + // don't make clickable if it could be a #number + if (QRegExp("^#\\d+$").exactMatch(match)) + continue; + } + result.append(Clickable((Clickable::Type)type, matches[type], matchEnd[type] - matches[type])); + } + } + while (type >= 0); + return result; +} + + +Clickable ClickableList::atCursorPos(int idx) +{ + foreach(const Clickable &click, *this) { + if (idx >= click.start() && idx < click.start() + click.length()) + return click; } - } while(type >= 0); - return result; + return Clickable(); }