Mercurial > octave
view libgui/qterminal/libqterminal/unix/Filter.cpp @ 31998:4d01a2860f3e
Replace deprecated QRegExp by QRegularExpression in libgui/qterminal/libqterminal/unix.
* libgui/qterminal/libqterminal/unix/Filter.cpp (RegExpFilter::setRegExp,
RegExpFilter::regExp, RegExpFilter::process, UrlFilter::process,
UrlFilter::HotSpot::UrlType, UrlFilter::HotSpot::activate,
UrlFilter::FullUrlRegExp, UrlFilter::EmailAddressRegExp,
UrlFilter::CompleteUrlRegExp, UrlFilter::ErrorLinkRegExp,
UrlFilter::ParseErrorLinkRegExp, UrlFilter::CompleteErrorLinkRegExp,
UrlFilter::HotSpot::actions),
libgui/qterminal/libqterminal/unix/Filter.h,
libgui/qterminal/libqterminal/unix/KeyboardTranslator.cpp
(KeyboardTranslatorReader::tokenize):
Replace deprecated QRegExp by QRegularExpression. Cleanup Qt header includes.
* libgui/qterminal/libqterminal/unix/Emulation.cpp,
libgui/qterminal/libqterminal/unix/TerminalMode.cpp: Cleanup Qt header includes.
author | Markus Mützel <markus.muetzel@gmx.de> |
---|---|
date | Wed, 12 Apr 2023 15:23:05 +0200 |
parents | 2e135e3cb1aa |
children | 398cba1c3971 |
line wrap: on
line source
/* Copyright (C) 2007, 2013 by Robert Knight <robertknight@gmail.com> Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 Adoption to octave by Torsten <mttl@mailbox.org>, Copyright (c) 2017 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Own #include "unix/Filter.h" // Qt #include <QDesktopServices> #include <QAction> #include <QApplication> #include <QClipboard> #include <QString> #include <QSharedData> #include <QtCore> // Konsole #include "unix/TerminalCharacterDecoder.h" FilterChain::~FilterChain() { QMutableListIterator<Filter*> iter(*this); while ( iter.hasNext() ) { Filter* filter = iter.next(); iter.remove(); delete filter; } } void FilterChain::addFilter(Filter* filter) { append(filter); } void FilterChain::removeFilter(Filter* filter) { removeAll(filter); } bool FilterChain::containsFilter(Filter* filter) { return contains(filter); } void FilterChain::reset() { QListIterator<Filter*> iter(*this); while (iter.hasNext()) iter.next()->reset(); } void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions) { QListIterator<Filter*> iter(*this); while (iter.hasNext()) iter.next()->setBuffer(buffer,linePositions); } void FilterChain::process() { QListIterator<Filter*> iter(*this); while (iter.hasNext()) iter.next()->process(); } void FilterChain::clear() { QList<Filter*>::clear(); } Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const { QListIterator<Filter*> iter(*this); while (iter.hasNext()) { Filter* filter = iter.next(); Filter::HotSpot* spot = filter->hotSpotAt(line,column); if ( spot != nullptr ) { return spot; } } return nullptr; } QList<Filter::HotSpot*> FilterChain::hotSpots() const { QList<Filter::HotSpot*> list; QListIterator<Filter*> iter(*this); while (iter.hasNext()) { Filter* filter = iter.next(); list << filter->hotSpots(); } return list; } TerminalImageFilterChain::TerminalImageFilterChain() : _buffer(nullptr) , _linePositions(nullptr) { } TerminalImageFilterChain::~TerminalImageFilterChain() { delete _buffer; delete _linePositions; } void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties) { //qDebug("%s %d", __FILE__, __LINE__); if (empty()) return; //qDebug("%s %d", __FILE__, __LINE__); // reset all filters and hotspots reset(); //qDebug("%s %d", __FILE__, __LINE__); PlainTextDecoder decoder; decoder.setTrailingWhitespace(false); //qDebug("%s %d", __FILE__, __LINE__); // setup new shared buffers for the filters to process on QString* newBuffer = new QString(); QList<int>* newLinePositions = new QList<int>(); setBuffer( newBuffer , newLinePositions ); // free the old buffers delete _buffer; delete _linePositions; _buffer = newBuffer; _linePositions = newLinePositions; QTextStream lineStream(_buffer); decoder.begin(&lineStream); for (int i=0 ; i < lines ; i++) { _linePositions->append(_buffer->length()); decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT); // pretend that each line ends with a newline character. // this prevents a link that occurs at the end of one line // being treated as part of a link that occurs at the start of the next line // // the downside is that links which are spread over more than one line are not // highlighted. // // TODO - Use the "line wrapped" attribute associated with lines in a // terminal image to avoid adding this imaginary character for wrapped // lines if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) ) lineStream << QChar('\n'); } decoder.end(); // qDebug("%s %d", __FILE__, __LINE__); } Filter::Filter() : _linePositions(nullptr), _buffer(nullptr) { } Filter::~Filter() { QListIterator<HotSpot*> iter(_hotspotList); while (iter.hasNext()) { delete iter.next(); } } void Filter::reset() { _hotspots.clear(); _hotspotList.clear(); } void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions) { _buffer = buffer; _linePositions = linePositions; } void Filter::getLineColumn(int position , int& startLine , int& startColumn) { Q_ASSERT( _linePositions ); Q_ASSERT( _buffer ); for (int i = 0 ; i < _linePositions->count() ; i++) { //kDebug() << "line position at " << i << " = " << _linePositions[i]; int nextLine = 0; if ( i == _linePositions->count()-1 ) { nextLine = _buffer->length() + 1; } else { nextLine = _linePositions->value(i+1); } // kDebug() << "pos - " << position << " line pos(" << i<< ") " << _linePositions->value(i) << // " next = " << nextLine << " buffer len = " << _buffer->length(); if ( _linePositions->value(i) <= position && position < nextLine ) { startLine = i; startColumn = position - _linePositions->value(i); return; } } } /*void Filter::addLine(const QString& text) { _linePositions << _buffer.length(); _buffer.append(text); }*/ const QString* Filter::buffer() { return _buffer; } Filter::HotSpot::~HotSpot() { } void Filter::addHotSpot(HotSpot* spot) { _hotspotList << spot; for (int line = spot->startLine() ; line <= spot->endLine() ; line++) { _hotspots.insert(line,spot); } } QList<Filter::HotSpot*> Filter::hotSpots() const { return _hotspotList; } QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const { return _hotspots.values(line); } Filter::HotSpot* Filter::hotSpotAt(int line , int column) const { QListIterator<HotSpot*> spotIter(_hotspots.values(line)); while (spotIter.hasNext()) { HotSpot* spot = spotIter.next(); if ( spot->startLine() == line && spot->startColumn() > column ) continue; if ( spot->endLine() == line && spot->endColumn() < column ) continue; return spot; } return nullptr; } Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn) : _startLine(startLine) , _startColumn(startColumn) , _endLine(endLine) , _endColumn(endColumn) , _type(NotSpecified) { } QString Filter::HotSpot::tooltip() const { return QString(); } QList<QAction*> Filter::HotSpot::actions() { return QList<QAction*>(); } int Filter::HotSpot::startLine() const { return _startLine; } int Filter::HotSpot::endLine() const { return _endLine; } int Filter::HotSpot::startColumn() const { return _startColumn; } int Filter::HotSpot::endColumn() const { return _endColumn; } Filter::Type Filter::HotSpot::type() const { return _type; } void Filter::HotSpot::setType(Filter::Type type) { _type = type; } RegExpFilter::RegExpFilter(Type t) : _type (t) { } RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn, int endLine,int endColumn, Filter::Type t) : Filter::HotSpot(startLine,startColumn,endLine,endColumn) { setType(t); } void RegExpFilter::HotSpot::activate(QObject*) { } void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts) { _capturedTexts = texts; } QStringList RegExpFilter::HotSpot::capturedTexts() const { return _capturedTexts; } void RegExpFilter::setRegExp (const QRegularExpression& regExp) { _searchText = regExp; } QRegularExpression RegExpFilter::regExp () const { return _searchText; } /*void RegExpFilter::reset(int) { _buffer = QString(); }*/ void RegExpFilter::process () { int pos = 0; const QString* text = buffer(); Q_ASSERT( text ); // ignore any regular expressions which match an empty string. // otherwise the while loop below will run indefinitely static const QString emptyString(""); QRegularExpressionMatch match = _searchText.match (emptyString); if ( match.hasMatch () ) return; match = _searchText.match (*text, pos); while (match.hasMatch ()) { pos = match.capturedStart (); int startLine = 0; int endLine = 0; int startColumn = 0; int endColumn = 0; //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength(); getLineColumn (match.capturedStart (), startLine, startColumn); getLineColumn (match.capturedEnd (), endLine, endColumn); RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn, endLine,endColumn,_type); spot->setCapturedTexts (match.capturedTexts ()); addHotSpot( spot ); pos += match.capturedLength (); // if matchedLength == 0, the program will get stuck in an infinite loop Q_ASSERT( match.capturedLength () > 0 ); match = _searchText.match (*text, pos); } } RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn, int endLine,int endColumn, Filter::Type t) { return new RegExpFilter::HotSpot(startLine,startColumn, endLine,endColumn, t); } UrlFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine, int endColumn, Filter::Type t) { return new UrlFilter::HotSpot(startLine,startColumn, endLine,endColumn,t); } void UrlFilter::process () { int pos = 0; const QString* text = buffer(); Q_ASSERT( text ); // ignore any regular expressions which match an empty string. // otherwise the while loop below will run indefinitely static const QString emptyString(""); QRegularExpressionMatch match = _searchText.match (emptyString); if ( match.hasMatch () ) return; match = _searchText.match (*text, pos); while (match.hasMatch ()) { int startLine = 0; int endLine = 0; int startColumn = 0; int endColumn = 0; //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength(); getLineColumn (match.capturedStart (), startLine, startColumn); getLineColumn (match.capturedEnd (), endLine, endColumn); UrlFilter::HotSpot* spot = newHotSpot(startLine,startColumn, endLine,endColumn,_type); spot->setCapturedTexts(match.capturedTexts ()); // Connect the signal of the urlobject to the slot of the filter; // the filter is then signaling to the main window connect (spot->get_urlObject (), SIGNAL (request_open_file_signal (const QString&, int)), this, SLOT (request_open_file (const QString&, int))); addHotSpot( spot ); pos += match.capturedLength(); // if matchedLength == 0, the program will get stuck in an infinite loop Q_ASSERT( match.capturedLength () > 0 ); match = _searchText.match (*text, pos); } } UrlFilter::HotSpot::HotSpot(int startLine,int startColumn, int endLine,int endColumn,Type t) : RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn,t) , _urlObject(new FilterObject(this)) { } QString UrlFilter::HotSpot::tooltip() const { QString url = capturedTexts().first(); const UrlType kind = urlType(); if ( kind == StandardUrl ) return QString(); else if ( kind == Email ) return QString(); else return QString(); } UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType () const { QString url = capturedTexts().first(); QRegularExpressionMatch match = FullUrlRegExp.match (url); if ( match.hasMatch () ) return StandardUrl; match = EmailAddressRegExp.match (url); if ( match.hasMatch () ) return Email; match = ErrorLinkRegExp.match (url); if ( match.hasMatch () ) return ErrorLink; match = ParseErrorLinkRegExp.match (url); if ( match.hasMatch () ) return ParseErrorLink; return Unknown; } void UrlFilter::HotSpot::activate (QObject* object) { QString url = capturedTexts().first(); const UrlType kind = urlType(); const QString& actionName = object ? object->objectName() : QString(); if ( actionName == "copy-action" ) { //kDebug() << "Copying url to clipboard:" << url; QApplication::clipboard()->setText(url); return; } if ( !object || actionName == "open-action" ) { if ( kind == StandardUrl ) { // if the URL path does not include the protocol ( eg. "www.kde.org" ) then // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" ) if (!url.contains("://")) url.prepend("http://"); QDesktopServices::openUrl (QUrl (url)); } else if ( kind == Email ) { url.prepend("mailto:"); QDesktopServices::openUrl (QUrl (url)); } else if (kind == ErrorLink) { QRegularExpressionMatch match = ErrorLinkRegExp.match (url); if (match.hasMatch ()) { QString file_name = match.captured (1); QString line = match.captured (2); // call the urlobject's method for opening a file; this // method then signals to the filter _urlObject->request_open_file (file_name, line.toInt ()); } } else if (kind == ParseErrorLink) { QRegularExpressionMatch match = ParseErrorLinkRegExp.match (url); if (match.hasMatch ()) { QString line = match.captured (1); QString file_name = match.captured (2); // call the urlobject's method for opening a file; this // method then signals to the filter _urlObject->request_open_file (file_name, line.toInt ()); } } } } // Note: Altering these regular expressions can have a major effect on the performance of the filters // used for finding URLs in the text, especially if they are very general and could match very long // pieces of text. // Please be careful when altering them. //regexp matches: // full url: // protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot const QRegularExpression UrlFilter::FullUrlRegExp {"(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]"}; // email address: // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] const QRegularExpression UrlFilter::EmailAddressRegExp {"\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b"}; // matches full url or email address const QRegularExpression UrlFilter::CompleteUrlRegExp {'(' + FullUrlRegExp.pattern() + '|' + EmailAddressRegExp.pattern() + ')'}; // error link: // normal error const QRegularExpression UrlFilter::ErrorLinkRegExp {"(\\S+) at line (\\d+) column (?:\\d+)"}; // parse error const QRegularExpression UrlFilter::ParseErrorLinkRegExp {"parse error near line (\\d+) of file (\\S+)"}; // complete regexp const QRegularExpression UrlFilter::CompleteErrorLinkRegExp {'('+ErrorLinkRegExp.pattern ()+'|'+ ParseErrorLinkRegExp.pattern ()+')'}; UrlFilter::UrlFilter (Type t) : RegExpFilter (t) { if (_type == ErrorLink) setRegExp (CompleteErrorLinkRegExp); else setRegExp (CompleteUrlRegExp); } UrlFilter::HotSpot::~HotSpot() { delete _urlObject; } void FilterObject::activated() { _filter->activate(sender()); } QList<QAction*> UrlFilter::HotSpot::actions () { QList<QAction*> list; const UrlType kind = urlType(); QAction* openAction = new QAction(_urlObject); QAction* copyAction = new QAction(_urlObject); Q_ASSERT (kind == StandardUrl || kind == Email || kind == ErrorLink || kind == ParseErrorLink); if ( kind == StandardUrl ) { openAction->setText(tr ("Open Link")); copyAction->setText(tr ("Copy Link Address")); } else if ( kind == Email ) { openAction->setText(tr ("Send Email To...")); copyAction->setText(tr ("Copy Email Address")); } else if ( kind == ErrorLink ) { QString url = capturedTexts().first(); QRegularExpressionMatch match = ErrorLinkRegExp.match (url); if (match.hasMatch ()) { QString file_name = match.captured (1); QString line = match.captured (2); openAction->setText(tr ("Edit %1 at line %2") .arg (file_name).arg (line)); } } else if ( kind == ParseErrorLink ) { QString url = capturedTexts().first(); QRegularExpressionMatch match = ParseErrorLinkRegExp.match (url); if (match.hasMatch ()) { QString line = match.captured (1); QString file_name = match.captured (2); openAction->setText(tr ("Edit %1 at line %2") .arg (file_name).arg (line)); } } // object names are set here so that the hotspot performs the // correct action when activated() is called with the triggered // action passed as a parameter. openAction->setObjectName("open-action"); copyAction->setObjectName("copy-action"); QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) ); list << openAction; if (kind != ErrorLink && kind != ParseErrorLink) { QObject::connect ( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) ); list << copyAction; } return list; } void UrlFilter::request_open_file (const QString& file, int line) { QFileInfo file_info = QFileInfo (file); // We have to distinguish between a parse error, where we get the full // path of the file or a general error in a script, where we only get // the function name. depending on this we have to invoke different // slots in main_window if (file_info.isAbsolute () && file_info.exists ()) emit request_open_file_signal (file, QString (), line); else emit request_edit_mfile_signal (file, line); } //#include "moc_Filter.cpp"