From b6da2d4537e09f40f320c019beb7e1ecb6d0ea8d Mon Sep 17 00:00:00 2001
From: Jarek Kobus <jkobus@trolltech.com>
Date: Thu, 14 Oct 2010 11:54:29 +0200
Subject: [PATCH] Implement "Jump To File Under Cursor" in profile editor

Reviewed-by: dt <qtc-committer@nokia.com>
Reviewed-by: ossi <oswald.buddenhagen@nokia.com>
Task-number: QTCREATORBUG-688
---
 .../qt4projectmanager/profileeditor.cpp       | 95 +++++++++++++++++++
 src/plugins/qt4projectmanager/profileeditor.h |  2 +
 .../qt4projectmanagerconstants.h              |  2 +
 .../qt4projectmanagerplugin.cpp               | 28 +++++-
 .../qt4projectmanagerplugin.h                 |  1 +
 src/plugins/texteditor/basetexteditor.cpp     |  4 +-
 6 files changed, 127 insertions(+), 5 deletions(-)

diff --git a/src/plugins/qt4projectmanager/profileeditor.cpp b/src/plugins/qt4projectmanager/profileeditor.cpp
index d7cf57278af..21a66776e65 100644
--- a/src/plugins/qt4projectmanager/profileeditor.cpp
+++ b/src/plugins/qt4projectmanager/profileeditor.cpp
@@ -44,6 +44,7 @@
 #include <texteditor/texteditorsettings.h>
 
 #include <QtCore/QFileInfo>
+#include <QtCore/QDir>
 #include <QtGui/QMenu>
 
 using namespace Qt4ProjectManager;
@@ -107,6 +108,95 @@ void ProFileEditor::unCommentSelection()
     Utils::unCommentSelection(this, m_commentDefinition);
 }
 
+static bool isValidFileNameChar(const QChar &c)
+{
+    if (c.isLetterOrNumber()
+            || c == QLatin1Char('.')
+            || c == QLatin1Char('_')
+            || c == QLatin1Char('-')
+            || c == QLatin1Char('/')
+            || c == QLatin1Char('\\'))
+        return true;
+    return false;
+}
+
+ProFileEditor::Link ProFileEditor::findLinkAt(const QTextCursor &cursor,
+                                      bool resolveTarget)
+{
+    Link link;
+
+    int lineNumber = 0, positionInBlock = 0;
+    convertPosition(cursor.position(), &lineNumber, &positionInBlock);
+
+    const QString block = cursor.block().text();
+
+    // check if the current position is commented out
+    const int hashPos = block.indexOf(QLatin1Char('#'));
+    if (hashPos >= 0 && hashPos < positionInBlock)
+        return link;
+
+    // find the beginning of a filename
+    QString buffer;
+    int beginPos = positionInBlock - 1;
+    while (beginPos >= 0) {
+        QChar c = block.at(beginPos);
+        if (isValidFileNameChar(c)) {
+            buffer.prepend(c);
+            beginPos--;
+        } else {
+            break;
+        }
+    }
+
+    // find the end of a filename
+    int endPos = positionInBlock;
+    while (endPos < block.count()) {
+        QChar c = block.at(endPos);
+        if (isValidFileNameChar(c)) {
+            buffer.append(c);
+            endPos++;
+        } else {
+            break;
+        }
+    }
+
+    if (buffer.isEmpty())
+        return link;
+
+    // remove trailing '\' since it can be line continuation char
+    if (buffer.at(buffer.size() - 1) == QLatin1Char('\\')) {
+        buffer.chop(1);
+        endPos--;
+    }
+
+    // if the buffer starts with $$PWD accept it
+    if (buffer.startsWith(QLatin1String("PWD/")) ||
+            buffer.startsWith(QLatin1String("PWD\\"))) {
+        if (beginPos > 0 && block.mid(beginPos - 1, 2) == QLatin1String("$$")) {
+            beginPos -=2;
+            buffer = buffer.mid(4);
+        }
+    }
+
+    QDir dir(QFileInfo(file()->fileName()).absolutePath());
+    QString fileName = dir.filePath(buffer);
+    QFileInfo fi(fileName);
+    if (fi.exists()) {
+        if (fi.isDir()) {
+            QDir subDir(fi.absoluteFilePath());
+            QString subProject = subDir.filePath(subDir.dirName() + QLatin1String(".pro"));
+            if (QFileInfo(subProject).exists())
+                fileName = subProject;
+            else
+                return link;
+        }
+        link.fileName = fileName;
+        link.begin = cursor.position() - positionInBlock + beginPos + 1;
+        link.end = cursor.position() - positionInBlock + endPos;
+    }
+    return link;
+}
+
 TextEditor::BaseTextEditorEditable *ProFileEditor::createEditableInterface()
 {
     return new ProFileEditorEditable(this);
@@ -169,6 +259,11 @@ void ProFileEditor::addLibrary()
     editable->insert(snippet);
 }
 
+void ProFileEditor::jumpToFile()
+{
+    openLink(findLinkAt(textCursor()));
+}
+
 //
 // ProFileDocument
 //
diff --git a/src/plugins/qt4projectmanager/profileeditor.h b/src/plugins/qt4projectmanager/profileeditor.h
index 025cd5edabe..1dc9733e23a 100644
--- a/src/plugins/qt4projectmanager/profileeditor.h
+++ b/src/plugins/qt4projectmanager/profileeditor.h
@@ -82,12 +82,14 @@ public:
 
     void unCommentSelection();
 protected:
+    virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
     TextEditor::BaseTextEditorEditable *createEditableInterface();
     void contextMenuEvent(QContextMenuEvent *);
 
 public slots:
     virtual void setFontSettings(const TextEditor::FontSettings &);
     void addLibrary();
+    void jumpToFile();
 
 private:
     ProFileEditorFactory *m_factory;
diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h
index db3b1719f89..7dfb1132ad3 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h
+++ b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h
@@ -65,6 +65,8 @@ const char * const BUILDSUBDIR         = "Qt4Builder.BuildSubDir";
 const char * const REBUILDSUBDIR       = "Qt4Builder.RebuildSubDir";
 const char * const CLEANSUBDIR         = "Qt4Builder.CleanSubDir";
 const char * const ADDLIBRARY          = "Qt4.AddLibrary";
+const char * const JUMP_TO_FILE        = "Qt4.JumpToFile";
+const char * const SEPARATOR           = "Qt4.Separator";
 
 //configurations
 const char * const CONFIG_DEBUG     = "debug";
diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp
index 6f926c4fd57..68919a58d56 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp
+++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp
@@ -232,19 +232,31 @@ bool Qt4ProjectManagerPlugin::initialize(const QStringList &arguments, QString *
 
     Core::Command *cmd;
 
-    cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION);
-    contextMenu->addAction(cmd);
-
     Core::Context proFileEditorContext = Core::Context(Qt4ProjectManager::Constants::PROJECT_ID);
 
+    QAction *jumpToFile = new QAction(tr("Jump to File Under Cursor"), this);
+    cmd = am->registerAction(jumpToFile,
+        Constants::JUMP_TO_FILE, proFileEditorContext);
+    cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
+    connect(jumpToFile, SIGNAL(triggered()),
+            this, SLOT(jumpToFile()));
+    contextMenu->addAction(cmd);
+
     QAction *addLibrary = new QAction(tr("Add Library..."), this);
     cmd = am->registerAction(addLibrary,
         Constants::ADDLIBRARY, proFileEditorContext);
-    //cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
     connect(addLibrary, SIGNAL(triggered()),
             this, SLOT(addLibrary()));
     contextMenu->addAction(cmd);
 
+    QAction *separator = new QAction(this);
+    separator->setSeparator(true);
+    contextMenu->addAction(am->registerAction(separator,
+                  Core::Id(Constants::SEPARATOR), proFileEditorContext));
+
+    cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION);
+    contextMenu->addAction(cmd);
+
     return true;
 }
 
@@ -306,6 +318,14 @@ void Qt4ProjectManagerPlugin::addLibrary()
         editor->addLibrary();
 }
 
+void Qt4ProjectManagerPlugin::jumpToFile()
+{
+    Core::EditorManager *em = Core::EditorManager::instance();
+    ProFileEditor *editor = qobject_cast<ProFileEditor*>(em->currentEditor()->widget());
+    if (editor)
+        editor->jumpToFile();
+}
+
 #ifdef WITH_TESTS
 void Qt4ProjectManagerPlugin::testBasicProjectLoading()
 {
diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h
index f05803e11db..f2f8164325c 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h
+++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h
@@ -75,6 +75,7 @@ private slots:
     void currentProjectChanged();
     void buildStateChanged(ProjectExplorer::Project *pro);
     void addLibrary();
+    void jumpToFile();
 
 #ifdef WITH_TESTS
     void testBasicProjectLoading(); // Test fails!
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index bd9e67274e5..7259b1b6159 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -4304,7 +4304,9 @@ bool BaseTextEditor::openLink(const Link &link)
         return true;
     }
 
-    return openEditorAt(link.fileName, link.line, link.column);
+    return openEditorAt(link.fileName, link.line, link.column, QString(),
+                          Core::EditorManager::IgnoreNavigationHistory
+                        | Core::EditorManager::ModeSwitch);
 }
 
 void BaseTextEditor::updateLink(QMouseEvent *e)
-- 
GitLab