Updating Doxygen styling and Licenses
[baulk.git] / src / Widgets / BaulkTerm / qtermwidget / Filter.cpp
blobf063c741721b2f4b537ff09a25caad3e376bb259
1 // Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
2 //
3 // Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
4 // Forked for Baulk - Copyright (C) 2008-2009 - Jacob Alexander <haata at users.sf.net>
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // any later version, including version 3 of the License.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // Own
20 #include "Filter.h"
22 // System
23 #include <iostream>
25 // Qt
26 #include <QtGui/QAction>
27 #include <QtGui/QApplication>
28 #include <QtGui/QClipboard>
29 #include <QtCore/QString>
31 #include <QtCore/QSharedData>
32 #include <QtCore>
34 // KDE
35 //#include <KLocale>
36 //#include <KRun>
38 // Konsole
39 #include "TerminalCharacterDecoder.h"
41 using namespace Konsole;
43 FilterChain::~FilterChain()
45 QMutableListIterator<Filter*> iter(*this);
47 while ( iter.hasNext() )
49 Filter* filter = iter.next();
50 iter.remove();
51 delete filter;
55 void FilterChain::addFilter(Filter* filter)
57 append(filter);
59 void FilterChain::removeFilter(Filter* filter)
61 removeAll(filter);
63 bool FilterChain::containsFilter(Filter* filter)
65 return contains(filter);
67 void FilterChain::reset()
69 QListIterator<Filter*> iter(*this);
70 while (iter.hasNext())
71 iter.next()->reset();
73 void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions)
75 QListIterator<Filter*> iter(*this);
76 while (iter.hasNext())
77 iter.next()->setBuffer(buffer,linePositions);
79 void FilterChain::process()
81 QListIterator<Filter*> iter(*this);
82 while (iter.hasNext())
83 iter.next()->process();
85 void FilterChain::clear()
87 QList<Filter*>::clear();
89 Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const
91 QListIterator<Filter*> iter(*this);
92 while (iter.hasNext())
94 Filter* filter = iter.next();
95 Filter::HotSpot* spot = filter->hotSpotAt(line,column);
96 if ( spot != 0 )
98 return spot;
102 return 0;
105 QList<Filter::HotSpot*> FilterChain::hotSpots() const
107 QList<Filter::HotSpot*> list;
108 QListIterator<Filter*> iter(*this);
109 while (iter.hasNext())
111 Filter* filter = iter.next();
112 list << filter->hotSpots();
114 return list;
116 //QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const;
118 TerminalImageFilterChain::TerminalImageFilterChain()
119 : _buffer(0)
120 , _linePositions(0)
124 TerminalImageFilterChain::~TerminalImageFilterChain()
126 delete _buffer;
127 delete _linePositions;
130 void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties)
132 //qDebug("%s %d", __FILE__, __LINE__);
133 if (empty())
134 return;
135 //qDebug("%s %d", __FILE__, __LINE__);
137 // reset all filters and hotspots
138 reset();
139 //qDebug("%s %d", __FILE__, __LINE__);
141 PlainTextDecoder decoder;
142 decoder.setTrailingWhitespace(false);
144 //qDebug("%s %d", __FILE__, __LINE__);
145 // setup new shared buffers for the filters to process on
146 QString* newBuffer = new QString();
147 QList<int>* newLinePositions = new QList<int>();
148 setBuffer( newBuffer , newLinePositions );
150 // free the old buffers
151 delete _buffer;
152 delete _linePositions;
154 _buffer = newBuffer;
155 _linePositions = newLinePositions;
157 QTextStream lineStream(_buffer);
158 decoder.begin(&lineStream);
160 for (int i=0 ; i < lines ; i++)
162 _linePositions->append(_buffer->length());
163 decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT);
165 // pretend that each line ends with a newline character.
166 // this prevents a link that occurs at the end of one line
167 // being treated as part of a link that occurs at the start of the next line
169 // the downside is that links which are spread over more than one line are not
170 // highlighted.
172 // TODO - Use the "line wrapped" attribute associated with lines in a
173 // terminal image to avoid adding this imaginary character for wrapped
174 // lines
175 if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) )
176 lineStream << QChar('\n');
178 decoder.end();
179 // qDebug("%s %d", __FILE__, __LINE__);
182 Filter::Filter() :
183 _linePositions(0),
184 _buffer(0)
188 Filter::~Filter()
190 QListIterator<HotSpot*> iter(_hotspotList);
191 while (iter.hasNext())
193 delete iter.next();
196 void Filter::reset()
198 _hotspots.clear();
199 _hotspotList.clear();
202 void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions)
204 _buffer = buffer;
205 _linePositions = linePositions;
208 void Filter::getLineColumn(int position , int& startLine , int& startColumn)
210 Q_ASSERT( _linePositions );
211 Q_ASSERT( _buffer );
214 for (int i = 0 ; i < _linePositions->count() ; i++)
216 //kDebug() << "line position at " << i << " = " << _linePositions[i];
217 int nextLine = 0;
219 if ( i == _linePositions->count()-1 )
221 nextLine = _buffer->length() + 1;
223 else
225 nextLine = _linePositions->value(i+1);
228 // kDebug() << "pos - " << position << " line pos(" << i<< ") " << _linePositions->value(i) <<
229 // " next = " << nextLine << " buffer len = " << _buffer->length();
231 if ( _linePositions->value(i) <= position && position < nextLine )
233 startLine = i;
234 startColumn = position - _linePositions->value(i);
235 return;
241 /*void Filter::addLine(const QString& text)
243 _linePositions << _buffer.length();
244 _buffer.append(text);
247 const QString* Filter::buffer()
249 return _buffer;
251 Filter::HotSpot::~HotSpot()
254 void Filter::addHotSpot(HotSpot* spot)
256 _hotspotList << spot;
258 for (int line = spot->startLine() ; line <= spot->endLine() ; line++)
260 _hotspots.insert(line,spot);
263 QList<Filter::HotSpot*> Filter::hotSpots() const
265 return _hotspotList;
267 QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const
269 return _hotspots.values(line);
272 Filter::HotSpot* Filter::hotSpotAt(int line , int column) const
274 QListIterator<HotSpot*> spotIter(_hotspots.values(line));
276 while (spotIter.hasNext())
278 HotSpot* spot = spotIter.next();
280 if ( spot->startLine() == line && spot->startColumn() > column )
281 continue;
282 if ( spot->endLine() == line && spot->endColumn() < column )
283 continue;
285 return spot;
288 return 0;
291 Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn)
292 : _startLine(startLine)
293 , _startColumn(startColumn)
294 , _endLine(endLine)
295 , _endColumn(endColumn)
296 , _type(NotSpecified)
299 QString Filter::HotSpot::tooltip() const
301 return QString();
303 QList<QAction*> Filter::HotSpot::actions()
305 return QList<QAction*>();
307 int Filter::HotSpot::startLine() const
309 return _startLine;
311 int Filter::HotSpot::endLine() const
313 return _endLine;
315 int Filter::HotSpot::startColumn() const
317 return _startColumn;
319 int Filter::HotSpot::endColumn() const
321 return _endColumn;
323 Filter::HotSpot::Type Filter::HotSpot::type() const
325 return _type;
327 void Filter::HotSpot::setType(Type type)
329 _type = type;
332 RegExpFilter::RegExpFilter()
336 RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
337 : Filter::HotSpot(startLine,startColumn,endLine,endColumn)
339 setType(Marker);
342 void RegExpFilter::HotSpot::activate(QObject*)
346 void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
348 _capturedTexts = texts;
350 QStringList RegExpFilter::HotSpot::capturedTexts() const
352 return _capturedTexts;
355 void RegExpFilter::setRegExp(const QRegExp& regExp)
357 _searchText = regExp;
359 QRegExp RegExpFilter::regExp() const
361 return _searchText;
363 /*void RegExpFilter::reset(int)
365 _buffer = QString();
367 void RegExpFilter::process()
369 int pos = 0;
370 const QString* text = buffer();
372 Q_ASSERT( text );
374 // ignore any regular expressions which match an empty string.
375 // otherwise the while loop below will run indefinitely
376 static const QString emptyString("");
377 if ( _searchText.exactMatch(emptyString) )
378 return;
380 while(pos >= 0)
382 pos = _searchText.indexIn(*text,pos);
384 if ( pos >= 0 )
387 int startLine = 0;
388 int endLine = 0;
389 int startColumn = 0;
390 int endColumn = 0;
393 //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength();
395 getLineColumn(pos,startLine,startColumn);
396 getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
398 //kDebug() << "start " << startLine << " / " << startColumn;
399 //kDebug() << "end " << endLine << " / " << endColumn;
401 RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
402 endLine,endColumn);
403 spot->setCapturedTexts(_searchText.capturedTexts());
405 addHotSpot( spot );
406 pos += _searchText.matchedLength();
408 // if matchedLength == 0, the program will get stuck in an infinite loop
409 Q_ASSERT( _searchText.matchedLength() > 0 );
414 RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn,
415 int endLine,int endColumn)
417 return new RegExpFilter::HotSpot(startLine,startColumn,
418 endLine,endColumn);
420 RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
421 int endColumn)
423 return new UrlFilter::HotSpot(startLine,startColumn,
424 endLine,endColumn);
426 UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
427 : RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn)
428 , _urlObject(new FilterObject(this))
430 setType(Link);
432 QString UrlFilter::HotSpot::tooltip() const
434 QString url = capturedTexts().first();
436 const UrlType kind = urlType();
438 if ( kind == StandardUrl )
439 return QString();
440 else if ( kind == Email )
441 return QString();
442 else
443 return QString();
445 UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const
447 QString url = capturedTexts().first();
449 if ( FullUrlRegExp.exactMatch(url) )
450 return StandardUrl;
451 else if ( EmailAddressRegExp.exactMatch(url) )
452 return Email;
453 else
454 return Unknown;
457 void UrlFilter::HotSpot::activate(QObject* object)
459 QString url = capturedTexts().first();
461 const UrlType kind = urlType();
463 const QString& actionName = object ? object->objectName() : QString();
465 if ( actionName == "copy-action" )
467 //kDebug() << "Copying url to clipboard:" << url;
469 QApplication::clipboard()->setText(url);
470 return;
473 if ( !object || actionName == "open-action" )
475 if ( kind == StandardUrl )
477 // if the URL path does not include the protocol ( eg. "www.kde.org" ) then
478 // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" )
479 if (!url.contains("://"))
481 url.prepend("http://");
484 else if ( kind == Email )
486 url.prepend("mailto:");
489 // new KRun(url,QApplication::activeWindow());
493 // Note: Altering these regular expressions can have a major effect on the performance of the filters
494 // used for finding URLs in the text, especially if they are very general and could match very long
495 // pieces of text.
496 // Please be careful when altering them.
498 //regexp matches:
499 // full url:
500 // protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot
501 const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]");
502 // email address:
503 // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
504 const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
506 // matches full url or email address
507 const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+
508 EmailAddressRegExp.pattern()+')');
510 UrlFilter::UrlFilter()
512 setRegExp( CompleteUrlRegExp );
514 UrlFilter::HotSpot::~HotSpot()
516 delete _urlObject;
518 void FilterObject::activated()
520 _filter->activate(sender());
522 QList<QAction*> UrlFilter::HotSpot::actions()
524 QList<QAction*> list;
526 const UrlType kind = urlType();
528 QAction* openAction = new QAction(_urlObject);
529 QAction* copyAction = new QAction(_urlObject);;
531 Q_ASSERT( kind == StandardUrl || kind == Email );
533 if ( kind == StandardUrl )
535 openAction->setText(("Open Link"));
536 copyAction->setText(("Copy Link Address"));
538 else if ( kind == Email )
540 openAction->setText(("Send Email To..."));
541 copyAction->setText(("Copy Email Address"));
544 // object names are set here so that the hotspot performs the
545 // correct action when activated() is called with the triggered
546 // action passed as a parameter.
547 openAction->setObjectName("open-action");
548 copyAction->setObjectName("copy-action");
550 QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
551 QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
553 list << openAction;
554 list << copyAction;
556 return list;
559 //#include "moc_Filter.cpp"