2
Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27
#include <QtWidgets/QAction>
28
#include <QtWidgets/QApplication>
29
#include <QtGui/QClipboard>
30
#include <QtCore/QString>
31
#include <QtCore/QTextStream>
32
#include <QtCore/QSharedData>
33
#include <QtCore/QFile>
34
#include <QDesktopServices>
42
#include "TerminalCharacterDecoder.h"
43
#include "konsole_wcwidth.h"
46
FilterChain::~FilterChain()
48
QMutableListIterator<Filter*> iter(*this);
50
while ( iter.hasNext() )
52
Filter* filter = iter.next();
58
void FilterChain::addFilter(Filter* filter)
62
void FilterChain::removeFilter(Filter* filter)
66
bool FilterChain::containsFilter(Filter* filter)
68
return contains(filter);
70
void FilterChain::reset()
72
QListIterator<Filter*> iter(*this);
73
while (iter.hasNext())
76
void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions)
78
QListIterator<Filter*> iter(*this);
79
while (iter.hasNext())
80
iter.next()->setBuffer(buffer,linePositions);
82
void FilterChain::process()
84
QListIterator<Filter*> iter(*this);
85
while (iter.hasNext())
86
iter.next()->process();
88
void FilterChain::clear()
90
QList<Filter*>::clear();
92
Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const
94
QListIterator<Filter*> iter(*this);
95
while (iter.hasNext())
97
Filter* filter = iter.next();
98
Filter::HotSpot* spot = filter->hotSpotAt(line,column);
108
QList<Filter::HotSpot*> FilterChain::hotSpots() const
110
QList<Filter::HotSpot*> list;
111
QListIterator<Filter*> iter(*this);
112
while (iter.hasNext())
114
Filter* filter = iter.next();
115
list << filter->hotSpots();
119
//QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const;
121
TerminalImageFilterChain::TerminalImageFilterChain()
127
TerminalImageFilterChain::~TerminalImageFilterChain()
130
delete _linePositions;
133
void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties)
138
// reset all filters and hotspots
141
PlainTextDecoder decoder;
142
decoder.setTrailingWhitespace(false);
144
// setup new shared buffers for the filters to process on
145
QString* newBuffer = new QString();
146
QList<int>* newLinePositions = new QList<int>();
147
setBuffer( newBuffer , newLinePositions );
149
// free the old buffers
151
delete _linePositions;
154
_linePositions = newLinePositions;
156
QTextStream lineStream(_buffer);
157
decoder.begin(&lineStream);
159
for (int i=0 ; i < lines ; i++)
161
_linePositions->append(_buffer->length());
162
decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT);
164
// pretend that each line ends with a newline character.
165
// this prevents a link that occurs at the end of one line
166
// being treated as part of a link that occurs at the start of the next line
168
// the downside is that links which are spread over more than one line are not
171
// TODO - Use the "line wrapped" attribute associated with lines in a
172
// terminal image to avoid adding this imaginary character for wrapped
174
if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) )
175
lineStream << QChar('\n');
188
QListIterator<HotSpot*> iter(_hotspotList);
189
while (iter.hasNext())
197
_hotspotList.clear();
200
void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions)
203
_linePositions = linePositions;
206
void Filter::getLineColumn(int position , int& startLine , int& startColumn)
208
Q_ASSERT( _linePositions );
212
for (int i = 0 ; i < _linePositions->count() ; i++)
216
if ( i == _linePositions->count()-1 )
217
nextLine = _buffer->length() + 1;
219
nextLine = _linePositions->value(i+1);
221
if ( _linePositions->value(i) <= position && position < nextLine )
224
startColumn = string_width(buffer()->mid(_linePositions->value(i),position - _linePositions->value(i)));
231
/*void Filter::addLine(const QString& text)
233
_linePositions << _buffer.length();
234
_buffer.append(text);
237
const QString* Filter::buffer()
241
Filter::HotSpot::~HotSpot()
244
void Filter::addHotSpot(HotSpot* spot)
246
_hotspotList << spot;
248
for (int line = spot->startLine() ; line <= spot->endLine() ; line++)
250
_hotspots.insert(line,spot);
253
QList<Filter::HotSpot*> Filter::hotSpots() const
257
QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const
259
return _hotspots.values(line);
262
Filter::HotSpot* Filter::hotSpotAt(int line , int column) const
264
QListIterator<HotSpot*> spotIter(_hotspots.values(line));
266
while (spotIter.hasNext())
268
HotSpot* spot = spotIter.next();
270
if ( spot->startLine() == line && spot->startColumn() > column )
272
if ( spot->endLine() == line && spot->endColumn() < column )
281
Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn)
282
: _startLine(startLine)
283
, _startColumn(startColumn)
285
, _endColumn(endColumn)
286
, _type(NotSpecified)
289
QString Filter::HotSpot::tooltip() const
293
QList<QAction*> Filter::HotSpot::actions()
295
return QList<QAction*>();
297
int Filter::HotSpot::startLine() const
301
int Filter::HotSpot::endLine() const
305
int Filter::HotSpot::startColumn() const
309
int Filter::HotSpot::endColumn() const
313
Filter::HotSpot::Type Filter::HotSpot::type() const
317
void Filter::HotSpot::setType(Type type)
322
RegExpFilter::RegExpFilter()
326
RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
327
: Filter::HotSpot(startLine,startColumn,endLine,endColumn)
332
void RegExpFilter::HotSpot::activate(QObject*)
336
void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
338
_capturedTexts = texts;
340
QStringList RegExpFilter::HotSpot::capturedTexts() const
342
return _capturedTexts;
345
void RegExpFilter::setRegExp(const QRegExp& regExp)
347
_searchText = regExp;
349
QRegExp RegExpFilter::regExp() const
353
/*void RegExpFilter::reset(int)
357
void RegExpFilter::process()
360
const QString* text = buffer();
364
// ignore any regular expressions which match an empty string.
365
// otherwise the while loop below will run indefinitely
366
static const QString emptyString("");
367
if ( _searchText.exactMatch(emptyString) )
372
pos = _searchText.indexIn(*text,pos);
381
getLineColumn(pos,startLine,startColumn);
382
getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
384
RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
386
spot->setCapturedTexts(_searchText.capturedTexts());
389
pos += _searchText.matchedLength();
391
// if matchedLength == 0, the program will get stuck in an infinite loop
392
if ( _searchText.matchedLength() == 0 )
398
RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn,
399
int endLine,int endColumn)
401
return new RegExpFilter::HotSpot(startLine,startColumn,
404
RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
407
return new UrlFilter::HotSpot(startLine,startColumn,
410
UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
411
: RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn)
412
, _urlObject(new FilterObject(this))
416
QString UrlFilter::HotSpot::tooltip() const
418
QString url = capturedTexts().first();
420
const UrlType kind = urlType();
422
if ( kind == StandardUrl )
424
else if ( kind == Email )
429
UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const
431
QString url = capturedTexts().first();
433
if ( FullUrlRegExp.exactMatch(url) )
435
else if ( EmailAddressRegExp.exactMatch(url) )
441
void UrlFilter::HotSpot::activate(QObject* object)
443
QString url = capturedTexts().first();
445
const UrlType kind = urlType();
447
const QString& actionName = object ? object->objectName() : QString();
449
if ( actionName == "copy-action" )
451
QApplication::clipboard()->setText(url);
455
if ( !object || actionName == "open-action" )
457
if ( kind == StandardUrl )
459
// if the URL path does not include the protocol ( eg. "www.kde.org" ) then
460
// prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" )
461
if (!url.contains("://"))
463
url.prepend("http://");
466
else if ( kind == Email )
468
url.prepend("mailto:");
471
QDesktopServices::openUrl(QUrl(url));
472
//new KRun(url,QApplication::activeWindow());
476
// Note: Altering these regular expressions can have a major effect on the performance of the filters
477
// used for finding URLs in the text, especially if they are very general and could match very long
479
// Please be careful when altering them.
483
// protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot
484
const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]");
486
// [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
487
const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
489
// matches full url or email address
490
const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+
491
EmailAddressRegExp.pattern()+')');
493
UrlFilter::UrlFilter()
495
setRegExp( CompleteUrlRegExp );
497
UrlFilter::HotSpot::~HotSpot()
501
void FilterObject::activated()
503
_filter->activate(sender());
505
QList<QAction*> UrlFilter::HotSpot::actions()
507
QList<QAction*> list;
509
const UrlType kind = urlType();
511
QAction* openAction = new QAction(_urlObject);
512
QAction* copyAction = new QAction(_urlObject);;
514
Q_ASSERT( kind == StandardUrl || kind == Email );
516
if ( kind == StandardUrl )
518
openAction->setText(QObject::tr("Open Link"));
519
copyAction->setText(QObject::tr("Copy Link Address"));
521
else if ( kind == Email )
523
openAction->setText(QObject::tr("Send Email To..."));
524
copyAction->setText(QObject::tr("Copy Email Address"));
527
// object names are set here so that the hotspot performs the
528
// correct action when activated() is called with the triggered
529
// action passed as a parameter.
530
openAction->setObjectName( QLatin1String("open-action" ));
531
copyAction->setObjectName( QLatin1String("copy-action" ));
533
QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
534
QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
542
//#include "Filter.moc"