Implement a file filter similar to URL filter.

An updated version of the patch by Adam Treat <treat@kde.org>:
    Implement a new file filter using POSIX Portable Filename Character
    Set along with KDE's mimetype database of file extension blobs that
    filters text matching a POSIX portable pathname and underlines it
    very much like links.

REVIEW: 128241
REVIEW: 114376
parent 9cdb51e9
...@@ -964,10 +964,10 @@ ...@@ -964,10 +964,10 @@
<item> <item>
<widget class="QCheckBox" name="underlineLinksButton"> <widget class="QCheckBox" name="underlineLinksButton">
<property name="toolTip"> <property name="toolTip">
<string>Text recognized as a link or an email address will be underlined when hovered by the mouse pointer.</string> <string>Text recognized as a file, link or an email address will be underlined when hovered by the mouse pointer.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Underline links</string> <string>Underline files and links</string>
</property> </property>
</widget> </widget>
</item> </item>
...@@ -995,10 +995,10 @@ ...@@ -995,10 +995,10 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Text recognized as a link or an email address can be opened by direct mouse click.</string> <string>Text recognized as a file, link or an email address can be opened by direct mouse click.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Open links by direct click</string> <string>Open files and links by direct click</string>
</property> </property>
</widget> </widget>
</item> </item>
......
...@@ -21,9 +21,13 @@ ...@@ -21,9 +21,13 @@
#include "Filter.h" #include "Filter.h"
// Qt // Qt
#include <QDebug>
#include <QAction> #include <QAction>
#include <QApplication> #include <QApplication>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QMimeDatabase>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QTextStream> #include <QtCore/QTextStream>
#include <QtCore/QUrl> #include <QtCore/QUrl>
...@@ -33,6 +37,7 @@ ...@@ -33,6 +37,7 @@
#include <KRun> #include <KRun>
// Konsole // Konsole
#include "Session.h"
#include "TerminalCharacterDecoder.h" #include "TerminalCharacterDecoder.h"
#include "konsole_wcwidth.h" #include "konsole_wcwidth.h"
...@@ -295,8 +300,11 @@ RegExpFilter::RegExpFilter() ...@@ -295,8 +300,11 @@ RegExpFilter::RegExpFilter()
{ {
} }
RegExpFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn) RegExpFilter::HotSpot::HotSpot(int startLine, int startColumn,
int endLine, int endColumn,
const QStringList& capturedTexts)
: Filter::HotSpot(startLine, startColumn, endLine, endColumn) : Filter::HotSpot(startLine, startColumn, endLine, endColumn)
, _capturedTexts(capturedTexts)
{ {
setType(Marker); setType(Marker);
} }
...@@ -305,10 +313,6 @@ void RegExpFilter::HotSpot::activate(QObject*) ...@@ -305,10 +313,6 @@ void RegExpFilter::HotSpot::activate(QObject*)
{ {
} }
void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
{
_capturedTexts = texts;
}
QStringList RegExpFilter::HotSpot::capturedTexts() const QStringList RegExpFilter::HotSpot::capturedTexts() const
{ {
return _capturedTexts; return _capturedTexts;
...@@ -350,11 +354,10 @@ void RegExpFilter::process() ...@@ -350,11 +354,10 @@ void RegExpFilter::process()
getLineColumn(pos, startLine, startColumn); getLineColumn(pos, startLine, startColumn);
getLineColumn(pos + _searchText.matchedLength(), endLine, endColumn); getLineColumn(pos + _searchText.matchedLength(), endLine, endColumn);
RegExpFilter::HotSpot* spot = newHotSpot(startLine, startColumn, if (RegExpFilter::HotSpot* spot = newHotSpot(startLine, startColumn,
endLine, endColumn); endLine, endColumn, _searchText.capturedTexts()))
spot->setCapturedTexts(_searchText.capturedTexts()); addHotSpot(spot);
addHotSpot(spot);
pos += _searchText.matchedLength(); pos += _searchText.matchedLength();
// if matchedLength == 0, the program will get stuck in an infinite loop // if matchedLength == 0, the program will get stuck in an infinite loop
...@@ -365,19 +368,20 @@ void RegExpFilter::process() ...@@ -365,19 +368,20 @@ void RegExpFilter::process()
} }
RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine, int startColumn, RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine, int startColumn,
int endLine, int endColumn) int endLine, int endColumn, const QStringList& capturedTexts)
{ {
return new RegExpFilter::HotSpot(startLine, startColumn, return new RegExpFilter::HotSpot(startLine, startColumn,
endLine, endColumn); endLine, endColumn, capturedTexts);
} }
RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine, int startColumn, int endLine, RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine, int startColumn,
int endColumn) int endLine, int endColumn, const QStringList& capturedTexts)
{ {
return new UrlFilter::HotSpot(startLine, startColumn, return new UrlFilter::HotSpot(startLine, startColumn,
endLine, endColumn); endLine, endColumn, capturedTexts);
} }
UrlFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn) UrlFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn,
: RegExpFilter::HotSpot(startLine, startColumn, endLine, endColumn) const QStringList& capturedTexts)
: RegExpFilter::HotSpot(startLine, startColumn, endLine, endColumn, capturedTexts)
, _urlObject(new FilterObject(this)) , _urlObject(new FilterObject(this))
{ {
setType(Link); setType(Link);
...@@ -484,3 +488,75 @@ QList<QAction*> UrlFilter::HotSpot::actions() ...@@ -484,3 +488,75 @@ QList<QAction*> UrlFilter::HotSpot::actions()
return actions; return actions;
} }
/**
* File Filter - Construct a filter that works on local file paths using the
* posix portable filename character set combined with KDE's mimetype filename
* extension blob patterns.
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267
*/
RegExpFilter::HotSpot* FileFilter::newHotSpot(int startLine, int startColumn, int endLine,
int endColumn, const QStringList& capturedTexts)
{
if (!_session) {
qWarning() << "Trying to create new hot spot without session!";
return nullptr;
}
FileFilter::HotSpot *spot = nullptr;
QDir dir(_session->currentWorkingDirectory());
QFileInfo file(dir, capturedTexts.first());
if (file.exists())
spot = new FileFilter::HotSpot(startLine, startColumn, endLine, endColumn, capturedTexts, file);
return spot;
}
FileFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn, const QStringList& capturedTexts, const QFileInfo& file)
: RegExpFilter::HotSpot(startLine, startColumn, endLine, endColumn, capturedTexts)
, _fileObject(new FilterObject(this))
, _file(file)
{
setType(Link);
}
void FileFilter::HotSpot::activate(QObject*)
{
QString file = _file.absoluteFilePath();
new KRun(QUrl::fromLocalFile(file), QApplication::activeWindow());
}
FileFilter::FileFilter(Session* session)
: _session(session)
{
QStringList patterns;
QMimeDatabase mimeDatabase;
for (const QMimeType &mimeType : mimeDatabase.allMimeTypes()) {
patterns.append(mimeType.globPatterns());
}
patterns.removeDuplicates();
patterns.replaceInStrings("*", "");
patterns = patterns.filter(QRegExp("^[A-Za-z0-9\\._-]+$"));
patterns.replaceInStrings(".", "\\.");
QString regex("(\\b|/)+[A-Za-z0-9\\._-/]+(");
regex.append(patterns.join("|"));
regex.append("){1}\\b");
setRegExp(QRegExp(regex));
}
FileFilter::HotSpot::~HotSpot()
{
delete _fileObject;
}
QList<QAction*> FileFilter::HotSpot::actions()
{
QAction* openAction = new QAction(_fileObject);
openAction->setText(i18n("Open File"));
QObject::connect(openAction , SIGNAL(triggered()) , _fileObject , SLOT(activated()));
QList<QAction*> actions;
actions << openAction;
return actions;
}
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
#define FILTER_H #define FILTER_H
// Qt // Qt
#include <QtCore/QFileInfo>
#include <QtCore/QList> #include <QtCore/QList>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QRegExp> #include <QtCore/QRegExp>
#include <QtCore/QMultiHash> #include <QtCore/QMultiHash>
...@@ -34,6 +36,9 @@ class QAction; ...@@ -34,6 +36,9 @@ class QAction;
namespace Konsole namespace Konsole
{ {
class Session;
/** /**
* A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list) * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list)
* and marks the areas which match the filter's patterns as 'hotspots'. * and marks the areas which match the filter's patterns as 'hotspots'.
...@@ -189,11 +194,9 @@ public: ...@@ -189,11 +194,9 @@ public:
class HotSpot : public Filter::HotSpot class HotSpot : public Filter::HotSpot
{ {
public: public:
HotSpot(int startLine, int startColumn, int endLine , int endColumn); HotSpot(int startLine, int startColumn, int endLine , int endColumn, const QStringList& capturedTexts);
virtual void activate(QObject* object = 0); virtual void activate(QObject* object = 0);
/** Sets the captured texts associated with this hotspot */
void setCapturedTexts(const QStringList& texts);
/** Returns the texts found by the filter when matching the filter's regular expression */ /** Returns the texts found by the filter when matching the filter's regular expression */
QStringList capturedTexts() const; QStringList capturedTexts() const;
private: private:
...@@ -227,7 +230,7 @@ protected: ...@@ -227,7 +230,7 @@ protected:
* to return custom hotspot types * to return custom hotspot types
*/ */
virtual RegExpFilter::HotSpot* newHotSpot(int startLine, int startColumn, virtual RegExpFilter::HotSpot* newHotSpot(int startLine, int startColumn,
int endLine, int endColumn); int endLine, int endColumn, const QStringList& capturedTexts);
private: private:
QRegExp _searchText; QRegExp _searchText;
...@@ -246,7 +249,7 @@ public: ...@@ -246,7 +249,7 @@ public:
class HotSpot : public RegExpFilter::HotSpot class HotSpot : public RegExpFilter::HotSpot
{ {
public: public:
HotSpot(int startLine, int startColumn, int endLine, int endColumn); HotSpot(int startLine, int startColumn, int endLine, int endColumn, const QStringList& capturedTexts);
virtual ~HotSpot(); virtual ~HotSpot();
virtual QList<QAction*> actions(); virtual QList<QAction*> actions();
...@@ -271,7 +274,7 @@ public: ...@@ -271,7 +274,7 @@ public:
UrlFilter(); UrlFilter();
protected: protected:
virtual RegExpFilter::HotSpot* newHotSpot(int, int, int, int); virtual RegExpFilter::HotSpot* newHotSpot(int, int, int, int, const QStringList&);
private: private:
static const QRegExp FullUrlRegExp; static const QRegExp FullUrlRegExp;
...@@ -281,6 +284,43 @@ private: ...@@ -281,6 +284,43 @@ private:
static const QRegExp CompleteUrlRegExp; static const QRegExp CompleteUrlRegExp;
}; };
/**
* A filter which matches files according to POSIX Portable Filename Character Set
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267
*/
class FileFilter : public RegExpFilter
{
public:
/**
* Hotspot type created by FileFilter instances.
*/
class HotSpot : public RegExpFilter::HotSpot
{
public:
HotSpot(int startLine, int startColumn, int endLine, int endColumn, const QStringList& capturedTexts, const QFileInfo &file);
virtual ~HotSpot();
virtual QList<QAction*> actions();
/**
* Opens kate for editing the file.
*/
virtual void activate(QObject* object = 0);
private:
FilterObject* _fileObject;
QFileInfo _file;
};
FileFilter(Session* session);
protected:
virtual RegExpFilter::HotSpot* newHotSpot(int, int, int, int, const QStringList&);
private:
QPointer<Session> _session;
};
class FilterObject : public QObject class FilterObject : public QObject
{ {
Q_OBJECT Q_OBJECT
......
...@@ -99,12 +99,13 @@ SessionController::SessionController(Session* session , TerminalDisplay* view, Q ...@@ -99,12 +99,13 @@ SessionController::SessionController(Session* session , TerminalDisplay* view, Q
, _profileList(0) , _profileList(0)
, _previousState(-1) , _previousState(-1)
, _viewUrlFilter(0) , _viewUrlFilter(0)
, _fileFilter(0)
, _searchFilter(0) , _searchFilter(0)
, _copyInputToAllTabsAction(0) , _copyInputToAllTabsAction(0)
, _findAction(0) , _findAction(0)
, _findNextAction(0) , _findNextAction(0)
, _findPreviousAction(0) , _findPreviousAction(0)
, _urlFilterUpdateRequired(false) , _filterUpdateRequired(false)
, _searchStartLine(0) , _searchStartLine(0)
, _prevSearchResultLine(0) , _prevSearchResultLine(0)
, _searchBar(0) , _searchBar(0)
...@@ -242,12 +243,12 @@ void SessionController::interactionHandler() ...@@ -242,12 +243,12 @@ void SessionController::interactionHandler()
_interactionTimer->start(); _interactionTimer->start();
} }
void SessionController::requireUrlFilterUpdate() void SessionController::requireFilterUpdate()
{ {
// this method is called every time the screen window's output changes, so do not // this method is called every time the screen window's output changes, so do not
// do anything expensive here. // do anything expensive here.
_urlFilterUpdateRequired = true; _filterUpdateRequired = true;
} }
void SessionController::snapshot() void SessionController::snapshot()
{ {
...@@ -474,26 +475,30 @@ bool SessionController::eventFilter(QObject* watched , QEvent* event) ...@@ -474,26 +475,30 @@ bool SessionController::eventFilter(QObject* watched , QEvent* event)
copyInputToAllTabs(); copyInputToAllTabs();
} }
} }
// when a mouse move is received, create the URL filter and listen for output changes if // when a mouse move is received, create the filters and listen for output changes if
// it has not already been created. If it already exists, then update only if the output // it has not already been created. If it already exists, then update only if the output
// has changed since the last update ( _urlFilterUpdateRequired == true ) // has changed since the last update ( _filterUpdateRequired == true )
// //
// also check that no mouse buttons are pressed since the URL filter only applies when // also check that no mouse buttons are pressed since the URL filter only applies when
// the mouse is hovering over the view // the mouse is hovering over the view
if (event->type() == QEvent::MouseMove && if (event->type() == QEvent::MouseMove &&
(!_viewUrlFilter || _urlFilterUpdateRequired) && (!_viewUrlFilter || !_fileFilter || _filterUpdateRequired) &&
((QMouseEvent*)event)->buttons() == Qt::NoButton) { ((QMouseEvent*)event)->buttons() == Qt::NoButton) {
if (_view->screenWindow() && !_viewUrlFilter) { if (_view->screenWindow() && !_viewUrlFilter && !_fileFilter) {
connect(_view->screenWindow(), &Konsole::ScreenWindow::scrolled, this, &Konsole::SessionController::requireUrlFilterUpdate); connect(_view->screenWindow(), &Konsole::ScreenWindow::scrolled, this, &Konsole::SessionController::requireFilterUpdate);
connect(_view->screenWindow(), &Konsole::ScreenWindow::outputChanged, this, &Konsole::SessionController::requireUrlFilterUpdate); connect(_view->screenWindow(), &Konsole::ScreenWindow::outputChanged, this, &Konsole::SessionController::requireFilterUpdate);
// install filter on the view to highlight URLs // install filter on the view to highlight URLs
_viewUrlFilter = new UrlFilter(); _viewUrlFilter = new UrlFilter();
_view->filterChain()->addFilter(_viewUrlFilter); _view->filterChain()->addFilter(_viewUrlFilter);
// install filter on the view to highlight Files
_fileFilter = new FileFilter(_session);
_view->filterChain()->addFilter(_fileFilter);
} }
_view->processFilters(); _view->processFilters();
_urlFilterUpdateRequired = false; _filterUpdateRequired = false;
} }
} }
......
...@@ -60,6 +60,7 @@ class TerminalDisplay; ...@@ -60,6 +60,7 @@ class TerminalDisplay;
class IncrementalSearchBar; class IncrementalSearchBar;
class ProfileList; class ProfileList;
class UrlFilter; class UrlFilter;
class FileFilter;
class RegExpFilter; class RegExpFilter;
class EditProfileDialog; class EditProfileDialog;
...@@ -270,7 +271,7 @@ private slots: ...@@ -270,7 +271,7 @@ private slots:
// to take a snapshot of the state of the // to take a snapshot of the state of the
// foreground process in the terminal // foreground process in the terminal
void requireUrlFilterUpdate(); void requireFilterUpdate();
void highlightMatches(bool highlight); void highlightMatches(bool highlight);
void scrollBackOptionsChanged(int mode , int lines); void scrollBackOptionsChanged(int mode , int lines);
void sessionResizeRequest(const QSize& size); void sessionResizeRequest(const QSize& size);
...@@ -318,6 +319,7 @@ private: ...@@ -318,6 +319,7 @@ private:
int _previousState; int _previousState;
UrlFilter* _viewUrlFilter; UrlFilter* _viewUrlFilter;
FileFilter* _fileFilter;
RegExpFilter* _searchFilter; RegExpFilter* _searchFilter;
QAction* _copyInputToAllTabsAction; QAction* _copyInputToAllTabsAction;
...@@ -328,7 +330,7 @@ private: ...@@ -328,7 +330,7 @@ private:
QTimer* _interactionTimer; QTimer* _interactionTimer;
bool _urlFilterUpdateRequired; bool _filterUpdateRequired;
int _searchStartLine; int _searchStartLine;
int _prevSearchResultLine; int _prevSearchResultLine;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment