VCS: Add "Open file" context menu action to VCS log pane

to be used for status/opened output. enabling convenient opening.
Append repository as block data to log text to be able to resolve
relative paths.
This commit is contained in:
Friedemann Kleint
2009-12-15 14:20:06 +01:00
parent 3037328455
commit 730fd82ac8
8 changed files with 167 additions and 13 deletions

View File

@@ -30,28 +30,44 @@
#include "vcsbaseoutputwindow.h"
#include <utils/qtcassert.h>
#include <coreplugin/editormanager/editormanager.h>
#include <QtGui/QPlainTextEdit>
#include <QtGui/QTextCharFormat>
#include <QtGui/QContextMenuEvent>
#include <QtGui/QTextBlock>
#include <QtGui/QMenu>
#include <QtGui/QAction>
#include <QtGui/QTextDocument>
#include <QtGui/QTextBlockUserData>
#include <QtCore/QPointer>
#include <QtCore/QTextCodec>
#include <QtCore/QTime>
#include <QtCore/QPoint>
#include <QtCore/QFileInfo>
namespace VCSBase {
namespace Internal {
// Store repository along with text blocks
class RepositoryUserData : public QTextBlockUserData {
public:
explicit RepositoryUserData(const QString &repo) : m_repository(repo) {}
const QString &repository() const { return m_repository; }
private:
const QString m_repository;
};
// A plain text edit with a special context menu containing "Clear" and
// and functions to append specially formatted entries.
class OutputWindowPlainTextEdit : public QPlainTextEdit {
public:
explicit OutputWindowPlainTextEdit(QWidget *parent);
void appendLines(QString s);
void appendLines(QString s, const QString &repository = QString());
// Append red error text and pop up.
void appendError(const QString &text);
// Append warning error text and pop up.
@@ -63,6 +79,8 @@ protected:
virtual void contextMenuEvent(QContextMenuEvent *event);
private:
QString identifierUnderCursor(const QPoint &pos, QString *repository = 0) const;
const QTextCharFormat m_defaultFormat;
QTextCharFormat m_errorFormat;
QTextCharFormat m_warningFormat;
@@ -83,27 +101,100 @@ OutputWindowPlainTextEdit::OutputWindowPlainTextEdit(QWidget *parent) :
m_commandFormat.setFontWeight(QFont::Bold);
}
// Search back for beginning of word
static inline int firstWordCharacter(const QString &s, int startPos)
{
for ( ; startPos >= 0 ; startPos--) {
if (s.at(startPos).isSpace())
return startPos + 1;
}
return 0;
}
QString OutputWindowPlainTextEdit::identifierUnderCursor(const QPoint &widgetPos, QString *repository) const
{
if (repository)
repository->clear();
// Get the blank-delimited word under cursor. Note that
// using "SelectWordUnderCursor" does not work since it breaks
// at delimiters like '/'. Get the whole line
QTextCursor cursor = cursorForPosition(widgetPos);
const int cursorDocumentPos = cursor.position();
cursor.select(QTextCursor::BlockUnderCursor);
if (!cursor.hasSelection())
return QString();
QString block = cursor.selectedText();
// Determine cursor position within line and find blank-delimited word
const int cursorPos = cursorDocumentPos - cursor.block().position();
const int blockSize = block.size();
if (cursorPos < 0 || cursorPos >= blockSize || block.at(cursorPos).isSpace())
return QString();
// Retrieve repository if desired
if (repository)
if (QTextBlockUserData *data = cursor.block().userData())
*repository = static_cast<const RepositoryUserData*>(data)->repository();
// Find first non-space character of word and find first non-space character past
const int startPos = firstWordCharacter(block, cursorPos);
int endPos = cursorPos;
for ( ; endPos < blockSize && !block.at(endPos).isSpace(); endPos++) ;
return endPos > startPos ? block.mid(startPos, endPos - startPos) : QString();
}
void OutputWindowPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
QMenu *menu = createStandardContextMenu();
// Add 'open file'
QString repository;
const QString token = identifierUnderCursor(event->pos(), &repository);
QAction *openAction = 0;
if (!token.isEmpty()) {
// Check for a file, expand via repository if relative
QFileInfo fi(token);
if (!repository.isEmpty() && !fi.isFile() && fi.isRelative())
fi = QFileInfo(repository + QLatin1Char('/') + token);
if (fi.isFile()) {
menu->addSeparator();
openAction = menu->addAction(VCSBaseOutputWindow::tr("Open \"%1\"").arg(fi.fileName()));
openAction->setData(fi.absoluteFilePath());
}
}
// Add 'clear'
menu->addSeparator();
QAction *clearAction = menu->addAction(VCSBaseOutputWindow::tr("Clear"));
connect(clearAction, SIGNAL(triggered()), this, SLOT(clear()));
menu->exec(event->globalPos());
// Run
QAction *action = menu->exec(event->globalPos());
if (action) {
if (action == clearAction) {
clear();
return;
}
if (action == openAction) {
const QString fileName = action->data().toString();
Core::EditorManager::instance()->openEditor(fileName);
}
}
delete menu;
}
void OutputWindowPlainTextEdit::appendLines(QString s)
void OutputWindowPlainTextEdit::appendLines(QString s, const QString &repository)
{
if (s.isEmpty())
return;
// Avoid additional new line character generated by appendPlainText
if (s.endsWith(QLatin1Char('\n')))
s.truncate(s.size() - 1);
const int previousLineCount = document()->lineCount();
appendPlainText(s);
// Scroll down
moveCursor(QTextCursor::End);
ensureCursorVisible();
if (!repository.isEmpty()) {
// Associate repository with new data.
QTextBlock block = document()->findBlockByLineNumber(previousLineCount);
for ( ; block.isValid(); block = block.next())
block.setUserData(new RepositoryUserData(repository));
}
}
void OutputWindowPlainTextEdit::appendError(const QString &text)
@@ -135,6 +226,7 @@ void OutputWindowPlainTextEdit::appendCommand(const QString &text)
struct VCSBaseOutputWindowPrivate {
static VCSBaseOutputWindow *instance;
QPointer<Internal::OutputWindowPlainTextEdit> plainTextEdit;
QString repository;
};
VCSBaseOutputWindow *VCSBaseOutputWindowPrivate::instance = 0;
@@ -236,7 +328,7 @@ void VCSBaseOutputWindow::setData(const QByteArray &data)
void VCSBaseOutputWindow::appendSilently(const QString &text)
{
QTC_ASSERT(d->plainTextEdit, return)
d->plainTextEdit->appendLines(text);
d->plainTextEdit->appendLines(text, d->repository);
}
void VCSBaseOutputWindow::append(const QString &text)
@@ -292,4 +384,19 @@ VCSBaseOutputWindow *VCSBaseOutputWindow::instance()
return VCSBaseOutputWindowPrivate::instance;
}
QString VCSBaseOutputWindow::repository() const
{
return d->repository;
}
void VCSBaseOutputWindow::setRepository(const QString &r)
{
d->repository = r;
}
void VCSBaseOutputWindow::clearRepository()
{
d->repository.clear();
}
} // namespace VCSBase