00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "Filter.h"
00022
00023
00024 #include <iostream>
00025
00026
00027 #include <QtGui/QAction>
00028 #include <QtGui/QApplication>
00029 #include <QtGui/QClipboard>
00030 #include <QtCore/QString>
00031 #include <QtCore/QTextStream>
00032 #include <QtCore/QSharedData>
00033 #include <QtCore/QFile>
00034
00035
00036 #include <KLocale>
00037 #include <KRun>
00038
00039
00040 #include "TerminalCharacterDecoder.h"
00041 #include "konsole_wcwidth.h"
00042
00043 using namespace Konsole;
00044
00045 FilterChain::~FilterChain()
00046 {
00047 QMutableListIterator<Filter*> iter(*this);
00048
00049 while ( iter.hasNext() )
00050 {
00051 Filter* filter = iter.next();
00052 iter.remove();
00053 delete filter;
00054 }
00055 }
00056
00057 void FilterChain::addFilter(Filter* filter)
00058 {
00059 append(filter);
00060 }
00061 void FilterChain::removeFilter(Filter* filter)
00062 {
00063 removeAll(filter);
00064 }
00065 bool FilterChain::containsFilter(Filter* filter)
00066 {
00067 return contains(filter);
00068 }
00069 void FilterChain::reset()
00070 {
00071 QListIterator<Filter*> iter(*this);
00072 while (iter.hasNext())
00073 iter.next()->reset();
00074 }
00075 void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions)
00076 {
00077 QListIterator<Filter*> iter(*this);
00078 while (iter.hasNext())
00079 iter.next()->setBuffer(buffer,linePositions);
00080 }
00081 void FilterChain::process()
00082 {
00083 QListIterator<Filter*> iter(*this);
00084 while (iter.hasNext())
00085 iter.next()->process();
00086 }
00087 void FilterChain::clear()
00088 {
00089 QList<Filter*>::clear();
00090 }
00091 Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const
00092 {
00093 QListIterator<Filter*> iter(*this);
00094 while (iter.hasNext())
00095 {
00096 Filter* filter = iter.next();
00097 Filter::HotSpot* spot = filter->hotSpotAt(line,column);
00098 if ( spot != 0 )
00099 {
00100 return spot;
00101 }
00102 }
00103
00104 return 0;
00105 }
00106
00107 QList<Filter::HotSpot*> FilterChain::hotSpots() const
00108 {
00109 QList<Filter::HotSpot*> list;
00110 QListIterator<Filter*> iter(*this);
00111 while (iter.hasNext())
00112 {
00113 Filter* filter = iter.next();
00114 list << filter->hotSpots();
00115 }
00116 return list;
00117 }
00118
00119
00120 TerminalImageFilterChain::TerminalImageFilterChain()
00121 : _buffer(0)
00122 , _linePositions(0)
00123 {
00124 }
00125
00126 TerminalImageFilterChain::~TerminalImageFilterChain()
00127 {
00128 delete _buffer;
00129 delete _linePositions;
00130 }
00131
00132 void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties)
00133 {
00134 if (empty())
00135 return;
00136
00137
00138 reset();
00139
00140 PlainTextDecoder decoder;
00141 decoder.setTrailingWhitespace(false);
00142
00143
00144 QString* newBuffer = new QString();
00145 QList<int>* newLinePositions = new QList<int>();
00146 setBuffer( newBuffer , newLinePositions );
00147
00148
00149 delete _buffer;
00150 delete _linePositions;
00151
00152 _buffer = newBuffer;
00153 _linePositions = newLinePositions;
00154
00155 QTextStream lineStream(_buffer);
00156 decoder.begin(&lineStream);
00157
00158 for (int i=0 ; i < lines ; i++)
00159 {
00160 _linePositions->append(_buffer->length());
00161 decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT);
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) )
00174 lineStream << QChar('\n');
00175 }
00176 decoder.end();
00177 }
00178
00179 Filter::Filter() :
00180 _linePositions(0),
00181 _buffer(0)
00182 {
00183 }
00184
00185 Filter::~Filter()
00186 {
00187 QListIterator<HotSpot*> iter(_hotspotList);
00188 while (iter.hasNext())
00189 {
00190 delete iter.next();
00191 }
00192 }
00193 void Filter::reset()
00194 {
00195 _hotspots.clear();
00196 _hotspotList.clear();
00197 }
00198
00199 void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions)
00200 {
00201 _buffer = buffer;
00202 _linePositions = linePositions;
00203 }
00204
00205 void Filter::getLineColumn(int position , int& startLine , int& startColumn)
00206 {
00207 Q_ASSERT( _linePositions );
00208 Q_ASSERT( _buffer );
00209
00210
00211 for (int i = 0 ; i < _linePositions->count() ; i++)
00212 {
00213 int nextLine = 0;
00214
00215 if ( i == _linePositions->count()-1 )
00216 nextLine = _buffer->length() + 1;
00217 else
00218 nextLine = _linePositions->value(i+1);
00219
00220 if ( _linePositions->value(i) <= position && position < nextLine )
00221 {
00222 startLine = i;
00223 startColumn = string_width(buffer()->mid(_linePositions->value(i),position - _linePositions->value(i)));
00224 return;
00225 }
00226 }
00227 }
00228
00229
00230
00231
00232
00233
00234
00235
00236 const QString* Filter::buffer()
00237 {
00238 return _buffer;
00239 }
00240 Filter::HotSpot::~HotSpot()
00241 {
00242 }
00243 void Filter::addHotSpot(HotSpot* spot)
00244 {
00245 _hotspotList << spot;
00246
00247 for (int line = spot->startLine() ; line <= spot->endLine() ; line++)
00248 {
00249 _hotspots.insert(line,spot);
00250 }
00251 }
00252 QList<Filter::HotSpot*> Filter::hotSpots() const
00253 {
00254 return _hotspotList;
00255 }
00256 QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const
00257 {
00258 return _hotspots.values(line);
00259 }
00260
00261 Filter::HotSpot* Filter::hotSpotAt(int line , int column) const
00262 {
00263 QListIterator<HotSpot*> spotIter(_hotspots.values(line));
00264
00265 while (spotIter.hasNext())
00266 {
00267 HotSpot* spot = spotIter.next();
00268
00269 if ( spot->startLine() == line && spot->startColumn() > column )
00270 continue;
00271 if ( spot->endLine() == line && spot->endColumn() < column )
00272 continue;
00273
00274 return spot;
00275 }
00276
00277 return 0;
00278 }
00279
00280 Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn)
00281 : _startLine(startLine)
00282 , _startColumn(startColumn)
00283 , _endLine(endLine)
00284 , _endColumn(endColumn)
00285 , _type(NotSpecified)
00286 {
00287 }
00288 QString Filter::HotSpot::tooltip() const
00289 {
00290 return QString();
00291 }
00292 QList<QAction*> Filter::HotSpot::actions()
00293 {
00294 return QList<QAction*>();
00295 }
00296 int Filter::HotSpot::startLine() const
00297 {
00298 return _startLine;
00299 }
00300 int Filter::HotSpot::endLine() const
00301 {
00302 return _endLine;
00303 }
00304 int Filter::HotSpot::startColumn() const
00305 {
00306 return _startColumn;
00307 }
00308 int Filter::HotSpot::endColumn() const
00309 {
00310 return _endColumn;
00311 }
00312 Filter::HotSpot::Type Filter::HotSpot::type() const
00313 {
00314 return _type;
00315 }
00316 void Filter::HotSpot::setType(Type type)
00317 {
00318 _type = type;
00319 }
00320
00321 RegExpFilter::RegExpFilter()
00322 {
00323 }
00324
00325 RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
00326 : Filter::HotSpot(startLine,startColumn,endLine,endColumn)
00327 {
00328 setType(Marker);
00329 }
00330
00331 void RegExpFilter::HotSpot::activate(QObject*)
00332 {
00333 }
00334
00335 void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
00336 {
00337 _capturedTexts = texts;
00338 }
00339 QStringList RegExpFilter::HotSpot::capturedTexts() const
00340 {
00341 return _capturedTexts;
00342 }
00343
00344 void RegExpFilter::setRegExp(const QRegExp& regExp)
00345 {
00346 _searchText = regExp;
00347 }
00348 QRegExp RegExpFilter::regExp() const
00349 {
00350 return _searchText;
00351 }
00352
00353
00354
00355
00356 void RegExpFilter::process()
00357 {
00358 int pos = 0;
00359 const QString* text = buffer();
00360
00361 Q_ASSERT( text );
00362
00363
00364
00365 static const QString emptyString("");
00366 if ( _searchText.exactMatch(emptyString) )
00367 return;
00368
00369 while(pos >= 0)
00370 {
00371 pos = _searchText.indexIn(*text,pos);
00372
00373 if ( pos >= 0 )
00374 {
00375 int startLine = 0;
00376 int endLine = 0;
00377 int startColumn = 0;
00378 int endColumn = 0;
00379
00380 getLineColumn(pos,startLine,startColumn);
00381 getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
00382
00383 RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
00384 endLine,endColumn);
00385 spot->setCapturedTexts(_searchText.capturedTexts());
00386
00387 addHotSpot( spot );
00388 pos += _searchText.matchedLength();
00389
00390
00391 if ( _searchText.matchedLength() == 0 )
00392 pos = -1;
00393 }
00394 }
00395 }
00396
00397 RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn,
00398 int endLine,int endColumn)
00399 {
00400 return new RegExpFilter::HotSpot(startLine,startColumn,
00401 endLine,endColumn);
00402 }
00403 RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
00404 int endColumn)
00405 {
00406 return new UrlFilter::HotSpot(startLine,startColumn,
00407 endLine,endColumn);
00408 }
00409 UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
00410 : RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn)
00411 , _urlObject(new FilterObject(this))
00412 {
00413 setType(Link);
00414 }
00415 QString UrlFilter::HotSpot::tooltip() const
00416 {
00417 QString url = capturedTexts().first();
00418
00419 const UrlType kind = urlType();
00420
00421 if ( kind == StandardUrl )
00422 return QString();
00423 else if ( kind == Email )
00424 return QString();
00425 else
00426 return QString();
00427 }
00428 UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const
00429 {
00430 QString url = capturedTexts().first();
00431
00432 if ( FullUrlRegExp.exactMatch(url) )
00433 return StandardUrl;
00434 else if ( EmailAddressRegExp.exactMatch(url) )
00435 return Email;
00436 else
00437 return Unknown;
00438 }
00439
00440 void UrlFilter::HotSpot::activate(QObject* object)
00441 {
00442 QString url = capturedTexts().first();
00443
00444 const UrlType kind = urlType();
00445
00446 const QString& actionName = object ? object->objectName() : QString();
00447
00448 if ( actionName == "copy-action" )
00449 {
00450 QApplication::clipboard()->setText(url);
00451 return;
00452 }
00453
00454 if ( !object || actionName == "open-action" )
00455 {
00456 if ( kind == StandardUrl )
00457 {
00458
00459
00460 if (!url.contains("://"))
00461 {
00462 url.prepend("http://");
00463 }
00464 }
00465 else if ( kind == Email )
00466 {
00467 url.prepend("mailto:");
00468 }
00469
00470 new KRun(url,QApplication::activeWindow());
00471 }
00472 }
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482 const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]");
00483
00484
00485 const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
00486
00487
00488 const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+
00489 EmailAddressRegExp.pattern()+')');
00490
00491 UrlFilter::UrlFilter()
00492 {
00493 setRegExp( CompleteUrlRegExp );
00494 }
00495 UrlFilter::HotSpot::~HotSpot()
00496 {
00497 delete _urlObject;
00498 }
00499 void FilterObject::activated()
00500 {
00501 _filter->activate(sender());
00502 }
00503 QList<QAction*> UrlFilter::HotSpot::actions()
00504 {
00505 QList<QAction*> list;
00506
00507 const UrlType kind = urlType();
00508
00509 QAction* openAction = new QAction(_urlObject);
00510 QAction* copyAction = new QAction(_urlObject);;
00511
00512 Q_ASSERT( kind == StandardUrl || kind == Email );
00513
00514 if ( kind == StandardUrl )
00515 {
00516 openAction->setText(i18n("Open Link"));
00517 copyAction->setText(i18n("Copy Link Address"));
00518 }
00519 else if ( kind == Email )
00520 {
00521 openAction->setText(i18n("Send Email To..."));
00522 copyAction->setText(i18n("Copy Email Address"));
00523 }
00524
00525
00526
00527
00528 openAction->setObjectName("open-action");
00529 copyAction->setObjectName("copy-action");
00530
00531 QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
00532 QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
00533
00534 list << openAction;
00535 list << copyAction;
00536
00537 return list;
00538 }
00539
00540 #include "Filter.moc"