• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Konsole

Filter.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301  USA.
00018 */
00019 
00020 // Own
00021 #include "Filter.h"
00022 
00023 // System
00024 #include <iostream>
00025 
00026 // Qt
00027 #include <QtGui/QAction>
00028 #include <QtGui/QApplication>
00029 #include <QtGui/QClipboard>
00030 #include <QtCore/QString>
00031 #include <KDebug>
00032 #include <QtCore/QSharedData>
00033 #include <QtCore/QFile>
00034 
00035 // KDE
00036 #include <KLocale>
00037 #include <KRun>
00038 
00039 // Konsole
00040 #include "TerminalCharacterDecoder.h"
00041 
00042 using namespace Konsole;
00043 
00044 FilterChain::~FilterChain()
00045 {
00046     QMutableListIterator<Filter*> iter(*this);
00047     
00048     while ( iter.hasNext() )
00049     {
00050         Filter* filter = iter.next();
00051         iter.remove();
00052         delete filter;
00053     }
00054 }
00055 
00056 void FilterChain::addFilter(Filter* filter)
00057 {
00058     append(filter);
00059 }
00060 void FilterChain::removeFilter(Filter* filter)
00061 {
00062     removeAll(filter);
00063 }
00064 bool FilterChain::containsFilter(Filter* filter)
00065 {
00066     return contains(filter);
00067 }
00068 void FilterChain::reset()
00069 {
00070     QListIterator<Filter*> iter(*this);
00071     while (iter.hasNext())
00072         iter.next()->reset();
00073 }
00074 void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions)
00075 {
00076     QListIterator<Filter*> iter(*this);
00077     while (iter.hasNext())
00078         iter.next()->setBuffer(buffer,linePositions);
00079 }
00080 void FilterChain::process()
00081 {
00082     QListIterator<Filter*> iter(*this);
00083     while (iter.hasNext())
00084         iter.next()->process();
00085 }
00086 void FilterChain::clear()
00087 {
00088     QList<Filter*>::clear();
00089 }
00090 Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const
00091 {
00092     QListIterator<Filter*> iter(*this);
00093     while (iter.hasNext())
00094     {
00095         Filter* filter = iter.next();
00096         Filter::HotSpot* spot = filter->hotSpotAt(line,column);
00097         if ( spot != 0 )
00098         {
00099             return spot;
00100         }
00101     }
00102 
00103     return 0;
00104 }
00105 
00106 QList<Filter::HotSpot*> FilterChain::hotSpots() const
00107 {
00108     QList<Filter::HotSpot*> list;
00109     QListIterator<Filter*> iter(*this);
00110     while (iter.hasNext())
00111     {
00112         Filter* filter = iter.next();
00113         list << filter->hotSpots();
00114     }
00115     return list;
00116 }
00117 //QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const;
00118 
00119 TerminalImageFilterChain::TerminalImageFilterChain()
00120 : _buffer(0)
00121 , _linePositions(0)
00122 {
00123 }
00124 
00125 TerminalImageFilterChain::~TerminalImageFilterChain()
00126 {
00127     delete _buffer;
00128     delete _linePositions;
00129 }
00130 
00131 void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties)
00132 {
00133     if (empty())
00134         return;
00135 
00136     // reset all filters and hotspots
00137     reset();
00138 
00139     PlainTextDecoder decoder;
00140     decoder.setTrailingWhitespace(false);
00141     
00142     // setup new shared buffers for the filters to process on
00143     QString* newBuffer = new QString();
00144     QList<int>* newLinePositions = new QList<int>();
00145     setBuffer( newBuffer , newLinePositions );
00146 
00147     // free the old buffers
00148     delete _buffer;
00149     delete _linePositions;
00150 
00151     _buffer = newBuffer;
00152     _linePositions = newLinePositions;
00153 
00154     QTextStream lineStream(_buffer);
00155     decoder.begin(&lineStream);
00156 
00157     for (int i=0 ; i < lines ; i++)
00158     {
00159         _linePositions->append(_buffer->length());
00160         decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT);
00161 
00162         // pretend that each line ends with a newline character.
00163         // this prevents a link that occurs at the end of one line
00164         // being treated as part of a link that occurs at the start of the next line
00165         //
00166         // the downside is that links which are spread over more than one line are not
00167         // highlighted.  
00168         //
00169         // TODO - Use the "line wrapped" attribute associated with lines in a
00170         // terminal image to avoid adding this imaginary character for wrapped
00171         // lines
00172         if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) )
00173             lineStream << QChar('\n');
00174     }
00175     decoder.end();
00176 }
00177 
00178 Filter::Filter() :
00179 _linePositions(0),
00180 _buffer(0)
00181 {
00182 }
00183 
00184 Filter::~Filter()
00185 {
00186     QListIterator<HotSpot*> iter(_hotspotList);
00187     while (iter.hasNext())
00188     {
00189         delete iter.next();
00190     }
00191 }
00192 void Filter::reset()
00193 {
00194     _hotspots.clear();
00195     _hotspotList.clear();
00196 }
00197 
00198 void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions)
00199 {
00200     _buffer = buffer;
00201     _linePositions = linePositions;
00202 }
00203 
00204 void Filter::getLineColumn(int position , int& startLine , int& startColumn)
00205 {
00206     Q_ASSERT( _linePositions );
00207     Q_ASSERT( _buffer );
00208 
00209 
00210     for (int i = 0 ; i < _linePositions->count() ; i++)
00211     {
00212         //kDebug() << "line position at " << i << " = " << _linePositions[i];
00213         int nextLine = 0;
00214 
00215         if ( i == _linePositions->count()-1 )
00216         {
00217             nextLine = _buffer->length() + 1;
00218         }
00219         else
00220         {
00221             nextLine = _linePositions->value(i+1);
00222         }
00223 
00224        // kDebug() << "pos - " << position << " line pos(" << i<< ") " << _linePositions->value(i) << 
00225        //     " next = " << nextLine << " buffer len = " << _buffer->length();
00226 
00227         if ( _linePositions->value(i) <= position && position < nextLine ) 
00228         {
00229             startLine = i;
00230             startColumn = position - _linePositions->value(i);
00231             return;
00232         }
00233     }
00234 }
00235     
00236 
00237 /*void Filter::addLine(const QString& text)
00238 {
00239     _linePositions << _buffer.length();
00240     _buffer.append(text);
00241 }*/
00242 
00243 const QString* Filter::buffer()
00244 {
00245     return _buffer;
00246 }
00247 Filter::HotSpot::~HotSpot()
00248 {
00249 }
00250 void Filter::addHotSpot(HotSpot* spot)
00251 {
00252     _hotspotList << spot;
00253 
00254     for (int line = spot->startLine() ; line <= spot->endLine() ; line++)
00255     {
00256         _hotspots.insert(line,spot);
00257     }    
00258 }
00259 QList<Filter::HotSpot*> Filter::hotSpots() const
00260 {
00261     return _hotspotList;
00262 }
00263 QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const
00264 {
00265     return _hotspots.values(line);
00266 }
00267 
00268 Filter::HotSpot* Filter::hotSpotAt(int line , int column) const
00269 {
00270     QListIterator<HotSpot*> spotIter(_hotspots.values(line));
00271 
00272     while (spotIter.hasNext())
00273     {
00274         HotSpot* spot = spotIter.next();
00275         
00276         if ( spot->startLine() == line && spot->startColumn() > column )
00277             continue;
00278         if ( spot->endLine() == line && spot->endColumn() < column )
00279             continue;
00280        
00281         return spot;
00282     }
00283 
00284     return 0;
00285 }
00286 
00287 Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn)
00288     : _startLine(startLine)
00289     , _startColumn(startColumn)
00290     , _endLine(endLine)
00291     , _endColumn(endColumn)
00292     , _type(NotSpecified)
00293 {
00294 }
00295 QString Filter::HotSpot::tooltip() const
00296 {
00297     return QString();
00298 }
00299 QList<QAction*> Filter::HotSpot::actions()
00300 {
00301     return QList<QAction*>();
00302 }
00303 int Filter::HotSpot::startLine() const
00304 {
00305     return _startLine;
00306 }
00307 int Filter::HotSpot::endLine() const
00308 {
00309     return _endLine;
00310 }
00311 int Filter::HotSpot::startColumn() const
00312 {
00313     return _startColumn;
00314 }
00315 int Filter::HotSpot::endColumn() const
00316 {
00317     return _endColumn;
00318 }
00319 Filter::HotSpot::Type Filter::HotSpot::type() const
00320 {
00321     return _type;
00322 }
00323 void Filter::HotSpot::setType(Type type)
00324 {
00325     _type = type;
00326 }
00327 
00328 RegExpFilter::RegExpFilter()
00329 {
00330 }
00331 
00332 RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
00333     : Filter::HotSpot(startLine,startColumn,endLine,endColumn)
00334 {
00335     setType(Marker);
00336 }
00337 
00338 void RegExpFilter::HotSpot::activate(QObject*)
00339 {
00340 }
00341 
00342 void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
00343 {
00344     _capturedTexts = texts;
00345 }
00346 QStringList RegExpFilter::HotSpot::capturedTexts() const
00347 {
00348     return _capturedTexts;
00349 }
00350 
00351 void RegExpFilter::setRegExp(const QRegExp& regExp) 
00352 {
00353     _searchText = regExp;
00354 }
00355 QRegExp RegExpFilter::regExp() const
00356 {
00357     return _searchText;
00358 }
00359 /*void RegExpFilter::reset(int)
00360 {
00361     _buffer = QString();
00362 }*/
00363 void RegExpFilter::process()
00364 {
00365     int pos = 0;
00366     const QString* text = buffer();
00367 
00368     Q_ASSERT( text );
00369 
00370     // ignore any regular expressions which match an empty string.
00371     // otherwise the while loop below will run indefinitely
00372     static const QString emptyString("");
00373     if ( _searchText.exactMatch(emptyString) )
00374         return;
00375 
00376     while(pos >= 0)
00377     {
00378         pos = _searchText.indexIn(*text,pos);
00379 
00380         if ( pos >= 0 )
00381         {
00382 
00383             int startLine = 0;
00384             int endLine = 0;
00385             int startColumn = 0;
00386             int endColumn = 0;
00387 
00388             
00389             //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength();
00390             
00391             getLineColumn(pos,startLine,startColumn);
00392             getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
00393 
00394             //kDebug() << "start " << startLine << " / " << startColumn;
00395             //kDebug() << "end " << endLine << " / " << endColumn;
00396 
00397             RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
00398                                            endLine,endColumn);
00399             spot->setCapturedTexts(_searchText.capturedTexts());
00400 
00401             addHotSpot( spot );  
00402             pos += _searchText.matchedLength();
00403 
00404             // if matchedLength == 0, the program will get stuck in an infinite loop
00405             Q_ASSERT( _searchText.matchedLength() > 0 );
00406         }
00407     }    
00408 }
00409 
00410 RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn,
00411                                                 int endLine,int endColumn)
00412 {
00413     return new RegExpFilter::HotSpot(startLine,startColumn,
00414                                                   endLine,endColumn);
00415 }
00416 RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
00417                                                     int endColumn)
00418 {
00419     return new UrlFilter::HotSpot(startLine,startColumn,
00420                                                endLine,endColumn);
00421 }
00422 UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
00423 : RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn)
00424 , _urlObject(new FilterObject(this))
00425 {
00426     setType(Link);
00427 }
00428 QString UrlFilter::HotSpot::tooltip() const
00429 {
00430     QString url = capturedTexts().first();
00431 
00432     const UrlType kind = urlType();
00433 
00434     if ( kind == StandardUrl )
00435         return QString(); 
00436     else if ( kind == Email )
00437         return QString(); 
00438     else
00439         return QString();
00440 }
00441 UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const
00442 {
00443     QString url = capturedTexts().first();
00444     
00445     if ( FullUrlRegExp.exactMatch(url) )
00446         return StandardUrl;
00447     else if ( EmailAddressRegExp.exactMatch(url) )
00448         return Email;
00449     else
00450         return Unknown;
00451 }
00452 
00453 void UrlFilter::HotSpot::activate(QObject* object)
00454 {
00455     QString url = capturedTexts().first();
00456 
00457     const UrlType kind = urlType();
00458 
00459     const QString& actionName = object ? object->objectName() : QString();
00460 
00461     if ( actionName == "copy-action" )
00462     {
00463         //kDebug() << "Copying url to clipboard:" << url;
00464 
00465         QApplication::clipboard()->setText(url);
00466         return;
00467     }
00468 
00469     if ( !object || actionName == "open-action" )
00470     {
00471         if ( kind == StandardUrl )
00472         {
00473             // if the URL path does not include the protocol ( eg. "www.kde.org" ) then
00474             // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" )
00475             if (!url.contains("://"))
00476             {
00477                 url.prepend("http://");
00478             }
00479         } 
00480         else if ( kind == Email )
00481         {
00482             url.prepend("mailto:");
00483         }
00484     
00485         new KRun(url,QApplication::activeWindow());
00486     }
00487 }
00488 
00489 // Note:  Altering these regular expressions can have a major effect on the performance of the filters 
00490 // used for finding URLs in the text, especially if they are very general and could match very long
00491 // pieces of text.
00492 // Please be careful when altering them.
00493 
00494 //regexp matches:
00495 // full url:  
00496 // protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot
00497 const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]");
00498 // email address:
00499 // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
00500 const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
00501 
00502 // matches full url or email address
00503 const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+
00504                                             EmailAddressRegExp.pattern()+')');
00505 
00506 UrlFilter::UrlFilter()
00507 {
00508     setRegExp( CompleteUrlRegExp );
00509 }
00510 UrlFilter::HotSpot::~HotSpot()
00511 {
00512     delete _urlObject;
00513 }
00514 void FilterObject::activated()
00515 {
00516     _filter->activate(sender());
00517 }
00518 QList<QAction*> UrlFilter::HotSpot::actions()
00519 {
00520     QList<QAction*> list;
00521 
00522     const UrlType kind = urlType();
00523 
00524     QAction* openAction = new QAction(_urlObject);
00525     QAction* copyAction = new QAction(_urlObject);;
00526 
00527     Q_ASSERT( kind == StandardUrl || kind == Email );
00528 
00529     if ( kind == StandardUrl )
00530     {
00531         openAction->setText(i18n("Open Link"));
00532         copyAction->setText(i18n("Copy Link Address"));
00533     }
00534     else if ( kind == Email )
00535     {
00536         openAction->setText(i18n("Send Email To..."));
00537         copyAction->setText(i18n("Copy Email Address"));
00538     }
00539 
00540     // object names are set here so that the hotspot performs the
00541     // correct action when activated() is called with the triggered
00542     // action passed as a parameter.
00543     openAction->setObjectName("open-action");
00544     copyAction->setObjectName("copy-action");
00545 
00546     QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
00547     QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
00548 
00549     list << openAction;
00550     list << copyAction;
00551 
00552     return list; 
00553 }
00554 
00555 #include "Filter.moc"

Konsole

Skip menu "Konsole"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • Konsole
  • Libraries
  •   libkonq
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal