changeset 23404:314ac710f2ae

highlight error messages in gui and provide links to open files (bug #35619) * QTerminal.cc (handleCustomContextMenuReques): use method via selecting the message only in windows * Filter.cpp (Filter::HotSpot::type(), Filter::HotSpot::setType): make Type a type in Filter, not in HotSpot; (RegExpFilter::RegExpFilter) create a filter with the corresponding type instead of different filters for different types; (RegExpFilter::HotSpot::HotSpot): A found hot spot is created with the types of the filter that found that hot spot; (RegExpFilter::process): remove some old debug message, which were already commented out, call newHotSpot with type as new parameter; (RegExpFilter::newHotSpot, UrlFilter::newHotSpot): new parameter type; (UrlFilter::process): derive this method for UrlFilter, too, allowing to connect signals for opening files later on; (UrlFilter::HotSpot::HotSpot): no extra action here, get type from filter; (UrlFilter::HotSpot::urlType): add new url type; (UrlFilter::HotSpot::activate): depending on link type, open link as before or call method for opening the file mentioned in the link; (UrlFilter::ErrorLinkRegExp): const defined reg. expression for error link; (UrlFilter::UrlFilter): set reg. expression depending on type; (UrlFilter::HotSpot::actions): in a case of a file link, only prepare an open, but no copy action for the context menu; (UrlFilter::request_open_file): method emitting the signal to the main window for opening the desired file at desired line; * Filter.h: make Filter class inherit from QObject for using singals, move enum Type from HotSpot to Filter and add the types Error and ErrorLink, Filter and Filter::HotSpot ctors with type parameter, add file opening signal to RegExpFilter and FilterObject, add Q_OBJECT directives, add method to FilterObject returning the instance variable _urlObject, add new urlType ErrorLink, add own process method and open file slot to UrlFilter, FilterObject: open file slot only emitting open file singal * QUnixTerminalImpl.cpp (ctor): initialize instance variable _parent; (initialize): add new filters for errors and for links to related files, connect latters signal to the appropriate main windows slot; * QUnixTerminalImpl.h: add instance variable _parent for signal connections * TerminalView.cpp (paintFilters): underline Link and ErrorLink, draw rectangle with transparency for Error and LinkError; (mousePressEvent, mouseMoveEvent): treat Link and ErrorLink the same way
author Torsten <mttl@mailbox.org>
date Mon, 17 Apr 2017 21:28:55 +0200
parents fee531225679
children 32ec90068be5
files libgui/qterminal/libqterminal/QTerminal.cc libgui/qterminal/libqterminal/unix/Filter.cpp libgui/qterminal/libqterminal/unix/Filter.h libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.cpp libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.h libgui/qterminal/libqterminal/unix/TerminalView.cpp
diffstat 6 files changed, 213 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/qterminal/libqterminal/QTerminal.cc	Mon Apr 17 20:08:24 2017 +0200
+++ b/libgui/qterminal/libqterminal/QTerminal.cc	Mon Apr 17 21:28:55 2017 +0200
@@ -99,6 +99,9 @@
 
     _edit_action->setVisible (false);
 
+#if defined (Q_OS_WIN32)
+    // include this when in windows because there is no filter for
+    // detecting links and error messages yet
     if (has_selected_text)
       {
         QRegExp file ("(?:[ \\t]+)(\\S+) at line (\\d+) column (?:\\d+)");
@@ -119,6 +122,7 @@
             _edit_action->setData (data);
           }
       }
+#endif
 
     _paste_action->setEnabled (cb->text().length() > 0);
     _copy_action->setEnabled (has_selected_text);
--- a/libgui/qterminal/libqterminal/unix/Filter.cpp	Mon Apr 17 20:08:24 2017 +0200
+++ b/libgui/qterminal/libqterminal/unix/Filter.cpp	Mon Apr 17 21:28:55 2017 +0200
@@ -2,6 +2,7 @@
     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
@@ -317,23 +318,25 @@
 {
     return _endColumn;
 }
-Filter::HotSpot::Type Filter::HotSpot::type() const
+Filter::Type Filter::HotSpot::type() const
 {
     return _type;
 }
-void Filter::HotSpot::setType(Type type)
+void Filter::HotSpot::setType(Filter::Type type)
 {
     _type = type;
 }
 
-RegExpFilter::RegExpFilter()
+RegExpFilter::RegExpFilter(Type t)
+    : _type (t)
 {
 }
 
-RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
+RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,
+                               int endLine,int endColumn, Filter::Type t)
     : Filter::HotSpot(startLine,startColumn,endLine,endColumn)
 {
-    setType(Marker);
+    setType(t);
 }
 
 void RegExpFilter::HotSpot::activate(QObject*)
@@ -392,11 +395,8 @@
             getLineColumn(pos,startLine,startColumn);
             getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
 
-            //kDebug() << "start " << startLine << " / " << startColumn;
-            //kDebug() << "end " << endLine << " / " << endColumn;
-
             RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
-                                           endLine,endColumn);
+                                           endLine,endColumn,_type);
             spot->setCapturedTexts(_searchText.capturedTexts());
 
             addHotSpot( spot );
@@ -409,23 +409,76 @@
 }
 
 RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn,
-                                                int endLine,int endColumn)
+                                                int endLine,int endColumn,
+                                                Filter::Type t)
 {
     return new RegExpFilter::HotSpot(startLine,startColumn,
-                                                  endLine,endColumn);
+                                     endLine,endColumn, t);
 }
-RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
-                                                    int endColumn)
+UrlFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
+                                                    int endColumn, Filter::Type t)
 {
     return new UrlFilter::HotSpot(startLine,startColumn,
-                                               endLine,endColumn);
+                                               endLine,endColumn,t);
 }
-UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
-: RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn)
+
+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("");
+    if ( _searchText.exactMatch(emptyString) )
+        return;
+
+    while(pos >= 0)
+    {
+        pos = _searchText.indexIn(*text,pos);
+
+        if ( pos >= 0 )
+        {
+
+            int startLine = 0;
+            int endLine = 0;
+            int startColumn = 0;
+            int endColumn = 0;
+
+
+            //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength();
+
+            getLineColumn(pos,startLine,startColumn);
+            getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
+
+            UrlFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
+                                           endLine,endColumn,_type);
+            spot->setCapturedTexts(_searchText.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 += _searchText.matchedLength();
+
+            // if matchedLength == 0, the program will get stuck in an infinite loop
+            Q_ASSERT( _searchText.matchedLength() > 0 );
+        }
+    }
+}
+
+UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,
+                            int endLine,int endColumn,Type t)
+: RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn,t)
 , _urlObject(new FilterObject(this))
 {
-    setType(Link);
 }
+
 QString UrlFilter::HotSpot::tooltip() const
 {
     QString url = capturedTexts().first();
@@ -447,6 +500,8 @@
         return StandardUrl;
     else if ( EmailAddressRegExp.exactMatch(url) )
         return Email;
+    else if ( ErrorLinkRegExp.exactMatch(url) )
+        return ErrorLink;
     else
         return Unknown;
 }
@@ -483,7 +538,23 @@
             url.prepend("mailto:");
         }
 
-        QDesktopServices::openUrl (QUrl (url));
+        if (kind == ErrorLink)
+          {
+            int pos = ErrorLinkRegExp.indexIn (url);
+            if (pos > -1)
+              {
+                QString file_name = ErrorLinkRegExp.cap (1);
+                QString line = ErrorLinkRegExp.cap (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
+          {
+            QDesktopServices::openUrl (QUrl (url));
+          }
     }
 }
 
@@ -499,14 +570,19 @@
 // email address:
 // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
 const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
-
 // matches full url or email address
 const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+
                                             EmailAddressRegExp.pattern()+')');
+// error link
+const QRegExp UrlFilter::ErrorLinkRegExp("(\\S+) at line (\\d+) column (?:\\d+)");
 
-UrlFilter::UrlFilter()
+UrlFilter::UrlFilter (Type t)
+    : RegExpFilter (t)
 {
-    setRegExp( CompleteUrlRegExp );
+    if (_type == ErrorLink)
+      setRegExp (ErrorLinkRegExp);
+    else
+      setRegExp (CompleteUrlRegExp);
 }
 UrlFilter::HotSpot::~HotSpot()
 {
@@ -525,17 +601,29 @@
     QAction* openAction = new QAction(_urlObject);
     QAction* copyAction = new QAction(_urlObject);;
 
-    Q_ASSERT( kind == StandardUrl || kind == Email );
+    Q_ASSERT (kind == StandardUrl || kind == Email || kind == ErrorLink);
 
     if ( kind == StandardUrl )
     {
-        openAction->setText(("Open Link"));
-        copyAction->setText(("Copy Link Address"));
+        openAction->setText(tr ("Open Link"));
+        copyAction->setText(tr ("Copy Link Address"));
     }
     else if ( kind == Email )
     {
-        openAction->setText(("Send Email To..."));
-        copyAction->setText(("Copy Email Address"));
+        openAction->setText(tr ("Send Email To..."));
+        copyAction->setText(tr ("Copy Email Address"));
+    }
+    else if ( kind == ErrorLink )
+    {
+      QString url = capturedTexts().first();
+      int pos = ErrorLinkRegExp.indexIn (url);
+      if (pos >= 0)
+        {
+          QString file_name = ErrorLinkRegExp.cap (1);
+          QString line = ErrorLinkRegExp.cap (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
@@ -545,12 +633,21 @@
     copyAction->setObjectName("copy-action");
 
     QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
-    QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
+    list << openAction;
 
-    list << openAction;
-    list << copyAction;
-
+    if (kind != ErrorLink)
+      {
+        QObject::connect ( copyAction , SIGNAL(triggered()) ,
+                           _urlObject , SLOT(activated()) );
+        list << copyAction;
+      }
     return list;
 }
 
+void
+UrlFilter::request_open_file (const QString& file, int line)
+{
+  emit request_open_file_signal (file, line);
+}
+
 //#include "moc_Filter.cpp"
--- a/libgui/qterminal/libqterminal/unix/Filter.h	Mon Apr 17 20:08:24 2017 +0200
+++ b/libgui/qterminal/libqterminal/unix/Filter.h	Mon Apr 17 21:28:55 2017 +0200
@@ -2,6 +2,7 @@
     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
@@ -51,8 +52,11 @@
  * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest
  * and add them to the filter's list of hotspots using addHotSpot()
  */
-class Filter
+class Filter : public QObject
 {
+
+   Q_OBJECT
+
 public:
     /**
     * Represents an area of text which matched the pattern a particular filter has been looking for.
@@ -66,8 +70,22 @@
     * Hotspots may have more than one action, in which case the list of actions can be obtained using the
     * actions() method.  These actions may then be displayed in a popup menu or toolbar for example.
     */
-    class HotSpot
+
+    enum Type
     {
+      // the type of the hotspot is not specified
+      NotSpecified,
+      // this hotspot represents a clickable link
+      Link,
+      // this hotspot represents a marker
+      Marker,
+      Error,
+      ErrorLink
+    };
+
+    class HotSpot : public QObject
+    {
+
     public:
        /**
         * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn)
@@ -76,16 +94,6 @@
        HotSpot(int startLine , int startColumn , int endLine , int endColumn);
        virtual ~HotSpot();
 
-       enum Type
-       {
-            // the type of the hotspot is not specified
-            NotSpecified,
-            // this hotspot represents a clickable link
-            Link,
-            // this hotspot represents a marker
-            Marker
-       };
-
        /** Returns the line when the hotspot area starts */
        int startLine() const;
        /** Returns the line where the hotspot area ends */
@@ -190,6 +198,9 @@
  */
 class RegExpFilter : public Filter
 {
+
+   Q_OBJECT
+
 public:
     /**
      * Type of hotspot created by RegExpFilter.  The capturedTexts() method can be used to find the text
@@ -198,19 +209,20 @@
     class HotSpot : public Filter::HotSpot
     {
     public:
-        HotSpot(int startLine, int startColumn, int endLine , int endColumn);
+        HotSpot(int startLine, int startColumn,
+                int endLine , int endColumn, Filter::Type);
         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:
+     private:
         QStringList _capturedTexts;
     };
 
     /** Constructs a new regular expression filter */
-    RegExpFilter();
+    RegExpFilter (Type);
 
     /**
      * Sets the regular expression which the filter searches for in blocks of text.
@@ -230,15 +242,19 @@
      */
     virtual void process();
 
+signals:
+
+    void request_open_file_signal (const QString&, int);
+
 protected:
     /**
      * Called when a match for the regular expression is encountered.  Subclasses should reimplement this
      * to return custom hotspot types
      */
     virtual RegExpFilter::HotSpot* newHotSpot(int startLine,int startColumn,
-                                    int endLine,int endColumn);
+                                    int endLine,int endColumn, Type);
+    Type _type;
 
-private:
     QRegExp _searchText;
 };
 
@@ -247,6 +263,9 @@
 /** A filter which matches URLs in blocks of text */
 class UrlFilter : public RegExpFilter
 {
+
+   Q_OBJECT
+
 public:
     /**
      * Hotspot type created by UrlFilter instances.  The activate() method opens a web browser
@@ -255,7 +274,7 @@
     class HotSpot : public RegExpFilter::HotSpot
     {
     public:
-        HotSpot(int startLine,int startColumn,int endLine,int endColumn);
+        HotSpot(int startLine,int startColumn,int endLine,int endColumn,Type t);
         virtual ~HotSpot();
 
         virtual QList<QAction*> actions();
@@ -267,11 +286,15 @@
         virtual void activate(QObject* object = 0);
 
         virtual QString tooltip() const;
+
+        FilterObject* get_urlObject () { return _urlObject; }
+
     private:
         enum UrlType
         {
             StandardUrl,
             Email,
+            ErrorLink,
             Unknown
         };
         UrlType urlType() const;
@@ -279,15 +302,21 @@
         FilterObject* _urlObject;
     };
 
-    UrlFilter();
+    UrlFilter (Type t = Link);
+
+    virtual void process();
+
+public slots:
+    void request_open_file (const QString&, int);
 
 protected:
-    virtual RegExpFilter::HotSpot* newHotSpot(int,int,int,int);
+    virtual HotSpot* newHotSpot(int,int,int,int,Type);
 
 private:
 
     static const QRegExp FullUrlRegExp;
     static const QRegExp EmailAddressRegExp;
+    static const QRegExp ErrorLinkRegExp;
 
     // combined OR of FullUrlRegExp and EmailAddressRegExp
     static const QRegExp CompleteUrlRegExp;
@@ -298,6 +327,10 @@
 Q_OBJECT
 public:
     FilterObject(Filter::HotSpot* filter) : _filter(filter) {}
+    void request_open_file (const QString& file, int line)
+      { emit request_open_file_signal (file, line); }
+signals:
+    void request_open_file_signal (const QString&, int);
 private slots:
     void activated();
 private:
--- a/libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.cpp	Mon Apr 17 20:08:24 2017 +0200
+++ b/libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.cpp	Mon Apr 17 21:28:55 2017 +0200
@@ -24,8 +24,10 @@
 
 #include <termios.h>
 
-QUnixTerminalImpl::QUnixTerminalImpl(QWidget *parent)
-    : QTerminal(parent) {
+QUnixTerminalImpl::QUnixTerminalImpl(QWidget *p)
+    : QTerminal(p),
+      _parent (p)
+{
     setMinimumSize(300, 200);
     initialize();
 }
@@ -46,6 +48,16 @@
     UrlFilter *url_filter = new UrlFilter();
     m_terminalView->filterChain ()->addFilter (url_filter);
 
+    RegExpFilter *error_filter = new RegExpFilter (Filter::Type::Error);
+    error_filter->setRegExp (QRegExp ("error:"));
+    m_terminalView->filterChain ()->addFilter (error_filter);
+
+    UrlFilter *file_filter = new UrlFilter (Filter::Type::ErrorLink);
+    m_terminalView->filterChain ()->addFilter (file_filter);
+
+    connect (file_filter, SIGNAL (request_open_file_signal (const QString&, int)),
+             _parent, SLOT (edit_mfile (const QString&, int)));
+
     connect(m_terminalView, SIGNAL(customContextMenuRequested(QPoint)),
             this, SLOT(handleCustomContextMenuRequested(QPoint)));
 
--- a/libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.h	Mon Apr 17 20:08:24 2017 +0200
+++ b/libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.h	Mon Apr 17 21:28:55 2017 +0200
@@ -69,6 +69,7 @@
     TerminalModel *m_terminalModel;
     KPty *m_kpty;
     bool _extra_interrupt;
+    QWidget *_parent;
 };
 
 #endif // Q_UNIXTERMINALIMPL
--- a/libgui/qterminal/libqterminal/unix/TerminalView.cpp	Mon Apr 17 20:08:24 2017 +0200
+++ b/libgui/qterminal/libqterminal/unix/TerminalView.cpp	Mon Apr 17 21:28:55 2017 +0200
@@ -1194,7 +1194,8 @@
                        endColumn*_fontWidth - 1, (line+1)*_fontHeight - 1 );
 
           // Underline link hotspots
-          if ( spot->type() == Filter::HotSpot::Link )
+          if ( spot->type() == Filter::Link ||
+               spot->type() == Filter::ErrorLink)
             {
               QFontMetrics metrics(font());
 
@@ -1210,10 +1211,16 @@
             }
           // Marker hotspots simply have a transparent rectanglular shape
           // drawn on top of them
-          else if ( spot->type() == Filter::HotSpot::Marker )
+          else if ( spot->type() == Filter::Error )
             {
               //TODO - Do not use a hardcoded colour for this
-              painter.fillRect(r,QBrush(QColor(255,0,0,120)));
+              painter.fillRect(r,QBrush(QColor(255,0,0,96)));
+            }
+
+          if ( spot->type() == Filter::ErrorLink )
+            {
+              //TODO - Do not use a hardcoded colour for this
+              painter.fillRect(r,QBrush(QColor(255,0,0,96)));
             }
         }
     }
@@ -1564,7 +1571,8 @@
     {
 
       Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn);
-      if ( spot && spot->type() == Filter::HotSpot::Link)
+      if ( spot &&
+          (spot->type() == Filter::Link || spot->type() == Filter::ErrorLink))
         {
           QList<QAction*> actions = spot->actions ();
           if (actions.length ())
@@ -1656,7 +1664,8 @@
   // handle filters
   // change link hot-spot appearance on mouse-over
   Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn);
-  if ( spot && spot->type() == Filter::HotSpot::Link)
+  if ( spot &&
+      (spot->type() == Filter::Link || spot->type() == Filter::ErrorLink))
     {
       // change mouse cursor when mouse is over links
       if (! _mouseOverHotspotArea.isValid())