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 @@
<item>
<widget class="QCheckBox" name="underlineLinksButton">
<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 name="text">
<string>Underline links</string>
<string>Underline files and links</string>
</property>
</widget>
</item>
......@@ -995,10 +995,10 @@
<bool>false</bool>
</property>
<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 name="text">
<string>Open links by direct click</string>
<string>Open files and links by direct click</string>
</property>
</widget>
</item>
......
......@@ -21,9 +21,13 @@
#include "Filter.h"
// Qt
#include <QDebug>
#include <QAction>
#include <QApplication>
#include <QtGui/QClipboard>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QMimeDatabase>
#include <QtCore/QString>
#include <QtCore/QTextStream>
#include <QtCore/QUrl>
......@@ -33,6 +37,7 @@
#include <KRun>
// Konsole
#include "Session.h"
#include "TerminalCharacterDecoder.h"
#include "konsole_wcwidth.h"
......@@ -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)
, _capturedTexts(capturedTexts)
{
setType(Marker);
}
......@@ -305,10 +313,6 @@ void RegExpFilter::HotSpot::activate(QObject*)
{
}
void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
{
_capturedTexts = texts;
}
QStringList RegExpFilter::HotSpot::capturedTexts() const
{
return _capturedTexts;
......@@ -350,11 +354,10 @@ void RegExpFilter::process()
getLineColumn(pos, startLine, startColumn);
getLineColumn(pos + _searchText.matchedLength(), endLine, endColumn);
RegExpFilter::HotSpot* spot = newHotSpot(startLine, startColumn,
endLine, endColumn);
spot->setCapturedTexts(_searchText.capturedTexts());
if (RegExpFilter::HotSpot* spot = newHotSpot(startLine, startColumn,
endLine, endColumn, _searchText.capturedTexts()))
addHotSpot(spot);
pos += _searchText.matchedLength();
// if matchedLength == 0, the program will get stuck in an infinite loop
......@@ -365,19 +368,20 @@ void RegExpFilter::process()
}
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,
endLine, endColumn);
endLine, endColumn, capturedTexts);
}
RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine, int startColumn, int endLine,
int endColumn)
RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine, int startColumn,
int endLine, int endColumn, const QStringList& capturedTexts)
{
return new UrlFilter::HotSpot(startLine, startColumn,
endLine, endColumn);
endLine, endColumn, capturedTexts);
}
UrlFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn)
: RegExpFilter::HotSpot(startLine, startColumn, endLine, endColumn)
UrlFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn,
const QStringList& capturedTexts)
: RegExpFilter::HotSpot(startLine, startColumn, endLine, endColumn, capturedTexts)
, _urlObject(new FilterObject(this))
{
setType(Link);
......@@ -484,3 +488,75 @@ QList<QAction*> UrlFilter::HotSpot::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 @@
#define FILTER_H
// Qt
#include <QtCore/QFileInfo>
#include <QtCore/QList>
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QStringList>
#include <QtCore/QRegExp>
#include <QtCore/QMultiHash>
......@@ -34,6 +36,9 @@ class QAction;
namespace Konsole
{
class Session;
/**
* 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'.
......@@ -189,11 +194,9 @@ public:
class HotSpot : public Filter::HotSpot
{
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);
/** 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 */
QStringList capturedTexts() const;
private:
......@@ -227,7 +230,7 @@ protected:
* to return custom hotspot types
*/
virtual RegExpFilter::HotSpot* newHotSpot(int startLine, int startColumn,
int endLine, int endColumn);
int endLine, int endColumn, const QStringList& capturedTexts);
private:
QRegExp _searchText;
......@@ -246,7 +249,7 @@ public:
class HotSpot : public RegExpFilter::HotSpot
{
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 QList<QAction*> actions();
......@@ -271,7 +274,7 @@ public:
UrlFilter();
protected:
virtual RegExpFilter::HotSpot* newHotSpot(int, int, int, int);
virtual RegExpFilter::HotSpot* newHotSpot(int, int, int, int, const QStringList&);
private:
static const QRegExp FullUrlRegExp;
......@@ -281,6 +284,43 @@ private:
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
{
Q_OBJECT
......
......@@ -99,12 +99,13 @@ SessionController::SessionController(Session* session , TerminalDisplay* view, Q
, _profileList(0)
, _previousState(-1)
, _viewUrlFilter(0)
, _fileFilter(0)
, _searchFilter(0)
, _copyInputToAllTabsAction(0)
, _findAction(0)
, _findNextAction(0)
, _findPreviousAction(0)
, _urlFilterUpdateRequired(false)
, _filterUpdateRequired(false)
, _searchStartLine(0)
, _prevSearchResultLine(0)
, _searchBar(0)
......@@ -242,12 +243,12 @@ void SessionController::interactionHandler()
_interactionTimer->start();
}
void SessionController::requireUrlFilterUpdate()
void SessionController::requireFilterUpdate()
{
// this method is called every time the screen window's output changes, so do not
// do anything expensive here.
_urlFilterUpdateRequired = true;
_filterUpdateRequired = true;
}
void SessionController::snapshot()
{
......@@ -474,26 +475,30 @@ bool SessionController::eventFilter(QObject* watched , QEvent* event)
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
// 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
// the mouse is hovering over the view
if (event->type() == QEvent::MouseMove &&
(!_viewUrlFilter || _urlFilterUpdateRequired) &&
(!_viewUrlFilter || !_fileFilter || _filterUpdateRequired) &&
((QMouseEvent*)event)->buttons() == Qt::NoButton) {
if (_view->screenWindow() && !_viewUrlFilter) {
connect(_view->screenWindow(), &Konsole::ScreenWindow::scrolled, this, &Konsole::SessionController::requireUrlFilterUpdate);
connect(_view->screenWindow(), &Konsole::ScreenWindow::outputChanged, this, &Konsole::SessionController::requireUrlFilterUpdate);
if (_view->screenWindow() && !_viewUrlFilter && !_fileFilter) {
connect(_view->screenWindow(), &Konsole::ScreenWindow::scrolled, this, &Konsole::SessionController::requireFilterUpdate);
connect(_view->screenWindow(), &Konsole::ScreenWindow::outputChanged, this, &Konsole::SessionController::requireFilterUpdate);
// install filter on the view to highlight URLs
_viewUrlFilter = new UrlFilter();
_view->filterChain()->addFilter(_viewUrlFilter);
// install filter on the view to highlight Files
_fileFilter = new FileFilter(_session);
_view->filterChain()->addFilter(_fileFilter);
}
_view->processFilters();
_urlFilterUpdateRequired = false;
_filterUpdateRequired = false;
}
}
......
......@@ -60,6 +60,7 @@ class TerminalDisplay;
class IncrementalSearchBar;
class ProfileList;
class UrlFilter;
class FileFilter;
class RegExpFilter;
class EditProfileDialog;
......@@ -270,7 +271,7 @@ private slots:
// to take a snapshot of the state of the
// foreground process in the terminal
void requireUrlFilterUpdate();
void requireFilterUpdate();
void highlightMatches(bool highlight);
void scrollBackOptionsChanged(int mode , int lines);
void sessionResizeRequest(const QSize& size);
......@@ -318,6 +319,7 @@ private:
int _previousState;
UrlFilter* _viewUrlFilter;
FileFilter* _fileFilter;
RegExpFilter* _searchFilter;
QAction* _copyInputToAllTabsAction;
......@@ -328,7 +330,7 @@ private:
QTimer* _interactionTimer;
bool _urlFilterUpdateRequired;
bool _filterUpdateRequired;
int _searchStartLine;
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