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

Konsole

Filter.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright 2007-2008 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 <QtCore/QTextStream>
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 #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 //QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const;
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     // reset all filters and hotspots
00138     reset();
00139 
00140     PlainTextDecoder decoder;
00141     decoder.setTrailingWhitespace(false);
00142     
00143     // setup new shared buffers for the filters to process on
00144     QString* newBuffer = new QString();
00145     QList<int>* newLinePositions = new QList<int>();
00146     setBuffer( newBuffer , newLinePositions );
00147 
00148     // free the old buffers
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         // pretend that each line ends with a newline character.
00164         // this prevents a link that occurs at the end of one line
00165         // being treated as part of a link that occurs at the start of the next line
00166         //
00167         // the downside is that links which are spread over more than one line are not
00168         // highlighted.  
00169         //
00170         // TODO - Use the "line wrapped" attribute associated with lines in a
00171         // terminal image to avoid adding this imaginary character for wrapped
00172         // lines
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 /*void Filter::addLine(const QString& text)
00231 {
00232     _linePositions << _buffer.length();
00233     _buffer.append(text);
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 /*void RegExpFilter::reset(int)
00353 {
00354     _buffer = QString();
00355 }*/
00356 void RegExpFilter::process()
00357 {
00358     int pos = 0;
00359     const QString* text = buffer();
00360 
00361     Q_ASSERT( text );
00362 
00363     // ignore any regular expressions which match an empty string.
00364     // otherwise the while loop below will run indefinitely
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             // if matchedLength == 0, the program will get stuck in an infinite loop
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             // if the URL path does not include the protocol ( eg. "www.kde.org" ) then
00459             // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" )
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 // Note:  Altering these regular expressions can have a major effect on the performance of the filters 
00475 // used for finding URLs in the text, especially if they are very general and could match very long
00476 // pieces of text.
00477 // Please be careful when altering them.
00478 
00479 //regexp matches:
00480 // full url:  
00481 // protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot
00482 const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]");
00483 // email address:
00484 // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
00485 const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
00486 
00487 // matches full url or email address
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     // object names are set here so that the hotspot performs the
00526     // correct action when activated() is called with the triggered
00527     // action passed as a parameter.
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"

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.7
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