2009-02-25 09:15:00 +01:00
|
|
|
/**************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2012-01-26 18:33:46 +01:00
|
|
|
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2011-11-02 15:59:12 +01:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
|
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
|
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this file.
|
|
|
|
|
** Please review the following information to ensure the GNU Lesser General
|
|
|
|
|
** Public License version 2.1 requirements will be met:
|
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
2011-04-13 08:42:33 +02:00
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
2010-12-17 16:01:08 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** Other Usage
|
|
|
|
|
**
|
|
|
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
|
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** If you have questions regarding the use of this file, please contact
|
2011-11-02 15:59:12 +01:00
|
|
|
** Nokia at qt-info@nokia.com.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
**************************************************************************/
|
2008-12-02 16:19:05 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "vcsbaseeditor.h"
|
|
|
|
|
#include "diffhighlighter.h"
|
|
|
|
|
#include "baseannotationhighlighter.h"
|
|
|
|
|
#include "vcsbaseconstants.h"
|
2011-03-24 15:44:39 +01:00
|
|
|
#include "vcsbaseoutputwindow.h"
|
|
|
|
|
#include "vcsbaseplugin.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-01-19 12:39:20 +01:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2012-02-03 11:41:57 +01:00
|
|
|
#include <coreplugin/icore.h>
|
2012-02-14 16:43:51 +01:00
|
|
|
#include <coreplugin/idocument.h>
|
2009-12-08 16:50:27 +01:00
|
|
|
#include <coreplugin/iversioncontrol.h>
|
2010-09-03 11:57:46 +02:00
|
|
|
#include <coreplugin/coreconstants.h>
|
|
|
|
|
#include <coreplugin/modemanager.h>
|
2012-02-03 11:41:57 +01:00
|
|
|
#include <coreplugin/vcsmanager.h>
|
2009-01-19 12:39:20 +01:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
#include <projectexplorer/editorconfiguration.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <projectexplorer/projectexplorer.h>
|
2009-09-25 11:35:44 +02:00
|
|
|
#include <projectexplorer/project.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <projectexplorer/session.h>
|
2012-02-14 12:10:29 +01:00
|
|
|
#include <texteditor/basetextdocument.h>
|
2011-04-08 10:40:59 +02:00
|
|
|
#include <texteditor/basetextdocumentlayout.h>
|
2009-01-19 12:39:20 +01:00
|
|
|
#include <texteditor/fontsettings.h>
|
|
|
|
|
#include <texteditor/texteditorconstants.h>
|
2011-06-27 15:00:16 +02:00
|
|
|
#include <texteditor/texteditorsettings.h>
|
2009-03-10 11:02:12 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2011-04-21 15:21:50 +02:00
|
|
|
#include <extensionsystem/invoker.h>
|
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QProcess>
|
|
|
|
|
#include <QRegExp>
|
|
|
|
|
#include <QSet>
|
|
|
|
|
#include <QTextCodec>
|
|
|
|
|
#include <QTextStream>
|
|
|
|
|
#include <QUrl>
|
|
|
|
|
#include <QTextBlock>
|
|
|
|
|
#include <QDesktopServices>
|
|
|
|
|
#include <QAction>
|
|
|
|
|
#include <QKeyEvent>
|
|
|
|
|
#include <QLayout>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QTextCursor>
|
|
|
|
|
#include <QTextEdit>
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QToolBar>
|
|
|
|
|
#include <QClipboard>
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QMessageBox>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-03-28 14:19:17 +02:00
|
|
|
/*!
|
2012-01-07 12:31:48 +01:00
|
|
|
\enum VcsBase::EditorContentType
|
2011-03-28 14:19:17 +02:00
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
\brief Contents of a VcsBaseEditor and its interaction.
|
2011-03-28 14:19:17 +02:00
|
|
|
|
|
|
|
|
\value RegularCommandOutput No special handling.
|
|
|
|
|
\value LogOutput Log of a file under revision control. Provide 'click on change'
|
|
|
|
|
description and 'Annotate' if is the log of a single file.
|
|
|
|
|
\value AnnotateOutput Color contents per change number and provide 'click on change' description.
|
|
|
|
|
Context menu offers "Annotate previous version". Expected format:
|
|
|
|
|
\code
|
|
|
|
|
<change description>: file line
|
|
|
|
|
\endcode
|
|
|
|
|
\value DiffOutput Diff output. Might includes describe output, which consists of a
|
|
|
|
|
header and diffs. Interaction is 'double click in hunk' which
|
|
|
|
|
opens the file. Context menu offers 'Revert chunk'.
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
\sa VcsBase::VcsBaseEditorWidget
|
2011-03-28 14:19:17 +02:00
|
|
|
*/
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
namespace VcsBase {
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-03-28 14:19:17 +02:00
|
|
|
/*!
|
2012-01-07 12:31:48 +01:00
|
|
|
\class VcsBase::DiffChunk
|
2011-03-28 14:19:17 +02:00
|
|
|
|
|
|
|
|
\brief A diff chunk consisting of file name and chunk data.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-03-24 15:44:39 +01:00
|
|
|
bool DiffChunk::isValid() const
|
|
|
|
|
{
|
|
|
|
|
return !fileName.isEmpty() && !chunk.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray DiffChunk::asPatch() const
|
|
|
|
|
{
|
|
|
|
|
const QByteArray fileNameBA = QFile::encodeName(fileName);
|
|
|
|
|
QByteArray rc = "--- ";
|
|
|
|
|
rc += fileNameBA;
|
|
|
|
|
rc += "\n+++ ";
|
|
|
|
|
rc += fileNameBA;
|
|
|
|
|
rc += '\n';
|
|
|
|
|
rc += chunk;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-12 13:42:21 +02:00
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
// Data to be passed to apply/revert diff chunk actions.
|
|
|
|
|
class DiffChunkAction
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DiffChunkAction(const DiffChunk &dc = DiffChunk(), bool revertIn = false) :
|
|
|
|
|
chunk(dc), revert(revertIn) {}
|
|
|
|
|
|
|
|
|
|
DiffChunk chunk;
|
|
|
|
|
bool revert;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
2012-01-07 12:31:48 +01:00
|
|
|
} // namespace VcsBase
|
2011-08-12 13:42:21 +02:00
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
Q_DECLARE_METATYPE(VcsBase::Internal::DiffChunkAction)
|
2011-08-12 13:42:21 +02:00
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
namespace VcsBase {
|
2011-08-12 13:42:21 +02:00
|
|
|
|
2011-03-28 14:19:17 +02:00
|
|
|
/*!
|
2012-01-07 12:31:48 +01:00
|
|
|
\class VcsBase::VcsBaseEditor
|
2011-03-28 14:19:17 +02:00
|
|
|
|
|
|
|
|
\brief An editor with no support for duplicates.
|
|
|
|
|
|
|
|
|
|
Creates a browse combo in the toolbar for diff output.
|
2012-01-07 12:31:48 +01:00
|
|
|
It also mirrors the signals of the VcsBaseEditor since the editor
|
2011-03-28 14:19:17 +02:00
|
|
|
manager passes the editor around.
|
|
|
|
|
*/
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
class VcsBaseEditor : public TextEditor::BaseTextEditor
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-01-06 17:24:40 +01:00
|
|
|
Q_OBJECT
|
2008-12-02 12:01:29 +01:00
|
|
|
public:
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseEditor(VcsBaseEditorWidget *, const VcsBaseEditorParameters *type);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
bool duplicateSupported() const { return false; }
|
|
|
|
|
Core::IEditor *duplicate(QWidget * /*parent*/) { return 0; }
|
2011-11-10 11:36:51 +01:00
|
|
|
Core::Id id() const { return m_id; }
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-06-07 14:53:28 +02:00
|
|
|
bool isTemporary() const { return m_temporary; }
|
|
|
|
|
void setTemporary(bool t) { m_temporary = t; }
|
2009-05-18 19:11:11 +02:00
|
|
|
|
2010-01-06 17:24:40 +01:00
|
|
|
signals:
|
|
|
|
|
void describeRequested(const QString &source, const QString &change);
|
2010-01-07 11:33:30 +01:00
|
|
|
void annotateRevisionRequested(const QString &source, const QString &change, int line);
|
2010-01-06 17:24:40 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
private:
|
2011-11-10 11:36:51 +01:00
|
|
|
Core::Id m_id;
|
2010-06-07 14:53:28 +02:00
|
|
|
bool m_temporary;
|
2008-12-02 12:01:29 +01:00
|
|
|
};
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseEditor::VcsBaseEditor(VcsBaseEditorWidget *widget,
|
|
|
|
|
const VcsBaseEditorParameters *type) :
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditor(widget),
|
2010-06-07 14:53:28 +02:00
|
|
|
m_id(type->id),
|
|
|
|
|
m_temporary(false)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-04-13 13:00:30 +02:00
|
|
|
setContext(Core::Context(type->context, TextEditor::Constants::C_TEXTEDITOR));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
// Diff editor: creates a browse combo in the toolbar for diff output.
|
2012-01-07 12:31:48 +01:00
|
|
|
class VcsBaseDiffEditor : public VcsBaseEditor
|
2009-03-10 11:02:12 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseDiffEditor(VcsBaseEditorWidget *, const VcsBaseEditorParameters *type);
|
2009-03-10 11:02:12 +01:00
|
|
|
|
2011-02-25 15:58:02 +01:00
|
|
|
QComboBox *diffFileBrowseComboBox() const { return m_diffFileBrowseComboBox; }
|
2009-03-10 11:02:12 +01:00
|
|
|
|
|
|
|
|
private:
|
2009-06-05 17:54:28 +02:00
|
|
|
QComboBox *m_diffFileBrowseComboBox;
|
2009-03-10 11:02:12 +01:00
|
|
|
};
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseDiffEditor::VcsBaseDiffEditor(VcsBaseEditorWidget *w, const VcsBaseEditorParameters *type) :
|
|
|
|
|
VcsBaseEditor(w, type),
|
2011-02-25 15:58:02 +01:00
|
|
|
m_diffFileBrowseComboBox(new QComboBox)
|
2009-03-10 11:02:12 +01:00
|
|
|
{
|
|
|
|
|
m_diffFileBrowseComboBox->setMinimumContentsLength(20);
|
|
|
|
|
// Make the combo box prefer to expand
|
|
|
|
|
QSizePolicy policy = m_diffFileBrowseComboBox->sizePolicy();
|
|
|
|
|
policy.setHorizontalPolicy(QSizePolicy::Expanding);
|
|
|
|
|
m_diffFileBrowseComboBox->setSizePolicy(policy);
|
|
|
|
|
|
2011-02-25 15:58:02 +01:00
|
|
|
insertExtraToolBarWidget(Left, m_diffFileBrowseComboBox);
|
2009-06-05 17:54:28 +02:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
// ----------- VcsBaseEditorPrivate
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-12-08 13:07:00 +01:00
|
|
|
namespace Internal {
|
|
|
|
|
|
2012-01-17 16:59:40 +01:00
|
|
|
/*! \class AbstractTextCursorHandler
|
|
|
|
|
* \brief Provides an interface to handle the contents under a text cursor inside an editor
|
|
|
|
|
*/
|
|
|
|
|
class AbstractTextCursorHandler : public QObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
AbstractTextCursorHandler(VcsBaseEditorWidget *editorWidget = 0);
|
|
|
|
|
|
|
|
|
|
/*! \brief Try to find some matching contents under \p cursor
|
|
|
|
|
*
|
|
|
|
|
* It's the first function to be called because it changes the internal state of the handler.
|
|
|
|
|
* Other functions (highlightCurrentContents(), handleCurrentContents(), ...) use the result
|
|
|
|
|
* of the matching
|
|
|
|
|
*
|
|
|
|
|
* \return true If contents could be found
|
|
|
|
|
*/
|
|
|
|
|
virtual bool findContentsUnderCursor(const QTextCursor &cursor);
|
|
|
|
|
|
|
|
|
|
//! Highlight (eg underline) the contents matched with findContentsUnderCursor()
|
|
|
|
|
virtual void highlightCurrentContents() = 0;
|
|
|
|
|
|
|
|
|
|
//! React to user-interaction with the contents matched with findContentsUnderCursor()
|
|
|
|
|
virtual void handleCurrentContents() = 0;
|
|
|
|
|
|
|
|
|
|
//! Contents matched with the last call to findContentsUnderCursor()
|
|
|
|
|
virtual QString currentContents() const = 0;
|
|
|
|
|
|
|
|
|
|
/*! \brief Fill \p menu with contextual actions applying to the contents matched
|
|
|
|
|
* with findContentsUnderCursor()
|
|
|
|
|
*/
|
|
|
|
|
virtual void fillContextMenu(QMenu *menu, EditorContentType type) const = 0;
|
|
|
|
|
|
|
|
|
|
//! Editor passed on construction of this handler
|
|
|
|
|
VcsBaseEditorWidget *editorWidget() const;
|
|
|
|
|
|
|
|
|
|
//! Text cursor used to match contents with findContentsUnderCursor()
|
|
|
|
|
QTextCursor currentCursor() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
VcsBaseEditorWidget *m_editorWidget;
|
|
|
|
|
QTextCursor m_currentCursor;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AbstractTextCursorHandler::AbstractTextCursorHandler(VcsBaseEditorWidget *editorWidget)
|
|
|
|
|
: QObject(editorWidget),
|
|
|
|
|
m_editorWidget(editorWidget)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AbstractTextCursorHandler::findContentsUnderCursor(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
m_currentCursor = cursor;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VcsBaseEditorWidget *AbstractTextCursorHandler::editorWidget() const
|
|
|
|
|
{
|
|
|
|
|
return m_editorWidget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor AbstractTextCursorHandler::currentCursor() const
|
|
|
|
|
{
|
|
|
|
|
return m_currentCursor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \class ChangeTextCursorHandler
|
|
|
|
|
* \brief Provides a handler for VCS change identifiers
|
|
|
|
|
*/
|
|
|
|
|
class ChangeTextCursorHandler : public AbstractTextCursorHandler
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
ChangeTextCursorHandler(VcsBaseEditorWidget *editorWidget = 0);
|
|
|
|
|
|
|
|
|
|
bool findContentsUnderCursor(const QTextCursor &cursor);
|
|
|
|
|
void highlightCurrentContents();
|
|
|
|
|
void handleCurrentContents();
|
|
|
|
|
QString currentContents() const;
|
|
|
|
|
void fillContextMenu(QMenu *menu, EditorContentType type) const;
|
|
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
|
void slotDescribe();
|
|
|
|
|
void slotCopyRevision();
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QAction *createDescribeAction(const QString &change) const;
|
|
|
|
|
QAction *createAnnotateAction(const QString &change, bool previous) const;
|
|
|
|
|
QAction *createCopyRevisionAction(const QString &change) const;
|
|
|
|
|
|
|
|
|
|
QString m_currentChange;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ChangeTextCursorHandler::ChangeTextCursorHandler(VcsBaseEditorWidget *editorWidget)
|
|
|
|
|
: AbstractTextCursorHandler(editorWidget)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ChangeTextCursorHandler::findContentsUnderCursor(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
AbstractTextCursorHandler::findContentsUnderCursor(cursor);
|
|
|
|
|
m_currentChange = editorWidget()->changeUnderCursor(cursor);
|
|
|
|
|
return !m_currentChange.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ChangeTextCursorHandler::highlightCurrentContents()
|
|
|
|
|
{
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.cursor = currentCursor();
|
|
|
|
|
sel.cursor.select(QTextCursor::WordUnderCursor);
|
|
|
|
|
sel.format.setFontUnderline(true);
|
|
|
|
|
sel.format.setProperty(QTextFormat::UserProperty, m_currentChange);
|
|
|
|
|
editorWidget()->setExtraSelections(VcsBaseEditorWidget::OtherSelection,
|
|
|
|
|
QList<QTextEdit::ExtraSelection>() << sel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ChangeTextCursorHandler::handleCurrentContents()
|
|
|
|
|
{
|
|
|
|
|
slotDescribe();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ChangeTextCursorHandler::fillContextMenu(QMenu *menu, EditorContentType type) const
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case LogOutput: { // Describe current / Annotate file of current
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
menu->addAction(createCopyRevisionAction(m_currentChange));
|
|
|
|
|
menu->addAction(createDescribeAction(m_currentChange));
|
|
|
|
|
if (editorWidget()->isFileLogAnnotateEnabled())
|
|
|
|
|
menu->addAction(createAnnotateAction(m_currentChange, false));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case AnnotateOutput: { // Describe current / annotate previous
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
menu->addAction(createCopyRevisionAction(m_currentChange));
|
|
|
|
|
menu->addAction(createDescribeAction(m_currentChange));
|
|
|
|
|
const QStringList previousVersions = editorWidget()->annotationPreviousVersions(m_currentChange);
|
|
|
|
|
if (!previousVersions.isEmpty()) {
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
foreach (const QString &pv, previousVersions)
|
|
|
|
|
menu->addAction(createAnnotateAction(pv, true));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString ChangeTextCursorHandler::currentContents() const
|
|
|
|
|
{
|
|
|
|
|
return m_currentChange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ChangeTextCursorHandler::slotDescribe()
|
|
|
|
|
{
|
|
|
|
|
emit editorWidget()->describeRequested(editorWidget()->source(), m_currentChange);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ChangeTextCursorHandler::slotCopyRevision()
|
|
|
|
|
{
|
|
|
|
|
QApplication::clipboard()->setText(m_currentChange);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QAction *ChangeTextCursorHandler::createDescribeAction(const QString &change) const
|
|
|
|
|
{
|
|
|
|
|
QAction *a = new QAction(VcsBaseEditorWidget::tr("Describe change %1").arg(change), 0);
|
|
|
|
|
connect(a, SIGNAL(triggered()), this, SLOT(slotDescribe()));
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QAction *ChangeTextCursorHandler::createAnnotateAction(const QString &change, bool previous) const
|
|
|
|
|
{
|
|
|
|
|
// Use 'previous' format if desired and available, else default to standard.
|
|
|
|
|
const QString &format =
|
|
|
|
|
previous && !editorWidget()->annotatePreviousRevisionTextFormat().isEmpty() ?
|
|
|
|
|
editorWidget()->annotatePreviousRevisionTextFormat() :
|
|
|
|
|
editorWidget()->annotateRevisionTextFormat();
|
|
|
|
|
QAction *a = new QAction(format.arg(change), 0);
|
|
|
|
|
a->setData(change);
|
|
|
|
|
connect(a, SIGNAL(triggered()), editorWidget(), SLOT(slotAnnotateRevision()));
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QAction *ChangeTextCursorHandler::createCopyRevisionAction(const QString &change) const
|
|
|
|
|
{
|
|
|
|
|
QAction *a = new QAction(editorWidget()->copyRevisionTextFormat().arg(change), 0);
|
|
|
|
|
a->setData(change);
|
|
|
|
|
connect(a, SIGNAL(triggered()), this, SLOT(slotCopyRevision()));
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \class UrlTextCursorHandler
|
|
|
|
|
* \brief Provides a handler for URL like http://www.nokia.com
|
|
|
|
|
*
|
|
|
|
|
* The URL pattern can be redefined in sub-classes with setUrlPattern(), by default the pattern
|
|
|
|
|
* works for hyper-text URL
|
|
|
|
|
*/
|
|
|
|
|
class UrlTextCursorHandler : public AbstractTextCursorHandler
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
UrlTextCursorHandler(VcsBaseEditorWidget *editorWidget = 0);
|
|
|
|
|
|
|
|
|
|
bool findContentsUnderCursor(const QTextCursor &cursor);
|
|
|
|
|
void highlightCurrentContents();
|
|
|
|
|
void handleCurrentContents();
|
|
|
|
|
void fillContextMenu(QMenu *menu, EditorContentType type) const;
|
|
|
|
|
QString currentContents() const;
|
|
|
|
|
|
|
|
|
|
protected slots:
|
|
|
|
|
virtual void slotCopyUrl();
|
|
|
|
|
virtual void slotOpenUrl();
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
void setUrlPattern(const QString &pattern);
|
|
|
|
|
QAction *createOpenUrlAction(const QString &text) const;
|
|
|
|
|
QAction *createCopyUrlAction(const QString &text) const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
class UrlData
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
int startColumn;
|
|
|
|
|
QString url;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UrlData m_urlData;
|
|
|
|
|
QString m_urlPattern;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UrlTextCursorHandler::UrlTextCursorHandler(VcsBaseEditorWidget *editorWidget)
|
|
|
|
|
: AbstractTextCursorHandler(editorWidget)
|
|
|
|
|
{
|
|
|
|
|
setUrlPattern(QLatin1String("https?\\://[^\\s]+"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UrlTextCursorHandler::findContentsUnderCursor(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
AbstractTextCursorHandler::findContentsUnderCursor(cursor);
|
|
|
|
|
|
|
|
|
|
m_urlData.url.clear();
|
|
|
|
|
m_urlData.startColumn = -1;
|
|
|
|
|
|
|
|
|
|
QTextCursor cursorForUrl = cursor;
|
|
|
|
|
cursorForUrl.select(QTextCursor::LineUnderCursor);
|
|
|
|
|
if (cursorForUrl.hasSelection()) {
|
|
|
|
|
const QString line = cursorForUrl.selectedText();
|
|
|
|
|
const int cursorCol = cursor.columnNumber();
|
2012-04-30 12:01:15 +02:00
|
|
|
QRegExp urlRx(m_urlPattern);
|
2012-01-17 16:59:40 +01:00
|
|
|
int urlMatchIndex = -1;
|
|
|
|
|
do {
|
|
|
|
|
urlMatchIndex = urlRx.indexIn(line, urlMatchIndex + 1);
|
|
|
|
|
if (urlMatchIndex != -1) {
|
|
|
|
|
const QString url = urlRx.cap(0);
|
|
|
|
|
if (urlMatchIndex <= cursorCol && cursorCol <= urlMatchIndex + url.length()) {
|
|
|
|
|
m_urlData.startColumn = urlMatchIndex;
|
|
|
|
|
m_urlData.url = url;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (urlMatchIndex != -1 && m_urlData.startColumn == -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m_urlData.startColumn != -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UrlTextCursorHandler::highlightCurrentContents()
|
|
|
|
|
{
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.cursor = currentCursor();
|
|
|
|
|
sel.cursor.setPosition(currentCursor().position()
|
|
|
|
|
- (currentCursor().columnNumber() - m_urlData.startColumn));
|
|
|
|
|
sel.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, m_urlData.url.length());
|
|
|
|
|
sel.format.setFontUnderline(true);
|
|
|
|
|
sel.format.setForeground(Qt::blue);
|
|
|
|
|
sel.format.setUnderlineColor(Qt::blue);
|
|
|
|
|
sel.format.setProperty(QTextFormat::UserProperty, m_urlData.url);
|
|
|
|
|
editorWidget()->setExtraSelections(VcsBaseEditorWidget::OtherSelection,
|
|
|
|
|
QList<QTextEdit::ExtraSelection>() << sel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UrlTextCursorHandler::handleCurrentContents()
|
|
|
|
|
{
|
|
|
|
|
slotOpenUrl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UrlTextCursorHandler::fillContextMenu(QMenu *menu, EditorContentType type) const
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(type);
|
|
|
|
|
menu->addSeparator();
|
2012-03-07 15:50:58 +01:00
|
|
|
menu->addAction(createOpenUrlAction(tr("Open URL in browser...")));
|
2012-01-17 16:59:40 +01:00
|
|
|
menu->addAction(createCopyUrlAction(tr("Copy URL location")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString UrlTextCursorHandler::currentContents() const
|
|
|
|
|
{
|
|
|
|
|
return m_urlData.url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UrlTextCursorHandler::setUrlPattern(const QString &pattern)
|
|
|
|
|
{
|
|
|
|
|
m_urlPattern = pattern;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UrlTextCursorHandler::slotCopyUrl()
|
|
|
|
|
{
|
|
|
|
|
QApplication::clipboard()->setText(m_urlData.url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UrlTextCursorHandler::slotOpenUrl()
|
|
|
|
|
{
|
|
|
|
|
QDesktopServices::openUrl(QUrl(m_urlData.url));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QAction *UrlTextCursorHandler::createOpenUrlAction(const QString &text) const
|
|
|
|
|
{
|
|
|
|
|
QAction *a = new QAction(text, 0);
|
|
|
|
|
a->setData(m_urlData.url);
|
|
|
|
|
connect(a, SIGNAL(triggered()), this, SLOT(slotOpenUrl()));
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QAction *UrlTextCursorHandler::createCopyUrlAction(const QString &text) const
|
|
|
|
|
{
|
|
|
|
|
QAction *a = new QAction(text, 0);
|
|
|
|
|
a->setData(m_urlData.url);
|
|
|
|
|
connect(a, SIGNAL(triggered()), this, SLOT(slotCopyUrl()));
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \class EmailTextCursorHandler
|
|
|
|
|
* \brief Provides a handler for email addresses
|
|
|
|
|
*/
|
|
|
|
|
class EmailTextCursorHandler : public UrlTextCursorHandler
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
EmailTextCursorHandler(VcsBaseEditorWidget *editorWidget = 0);
|
|
|
|
|
void fillContextMenu(QMenu *menu, EditorContentType type) const;
|
|
|
|
|
|
|
|
|
|
protected slots:
|
|
|
|
|
void slotOpenUrl();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
EmailTextCursorHandler::EmailTextCursorHandler(VcsBaseEditorWidget *editorWidget)
|
|
|
|
|
: UrlTextCursorHandler(editorWidget)
|
|
|
|
|
{
|
|
|
|
|
setUrlPattern(QLatin1String("[a-zA-Z0-9_\\.]+@[a-zA-Z0-9_\\.]+"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmailTextCursorHandler::fillContextMenu(QMenu *menu, EditorContentType type) const
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(type);
|
|
|
|
|
menu->addSeparator();
|
2012-03-07 15:50:58 +01:00
|
|
|
menu->addAction(createOpenUrlAction(tr("Send email to...")));
|
2012-01-17 16:59:40 +01:00
|
|
|
menu->addAction(createCopyUrlAction(tr("Copy email address")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmailTextCursorHandler::slotOpenUrl()
|
|
|
|
|
{
|
|
|
|
|
QDesktopServices::openUrl(QUrl(QLatin1String("mailto:") + currentContents()));
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
class VcsBaseEditorWidgetPrivate
|
2009-01-20 11:52:04 +01:00
|
|
|
{
|
2011-12-08 13:07:00 +01:00
|
|
|
public:
|
2012-01-17 16:59:40 +01:00
|
|
|
VcsBaseEditorWidgetPrivate(VcsBaseEditorWidget* editorWidget, const VcsBaseEditorParameters *type);
|
|
|
|
|
|
|
|
|
|
AbstractTextCursorHandler *findTextCursorHandler(const QTextCursor &cursor);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
const VcsBaseEditorParameters *m_parameters;
|
2009-03-10 11:02:12 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QString m_source;
|
2009-12-09 12:41:10 +01:00
|
|
|
QString m_diffBaseDirectory;
|
2009-03-10 11:02:12 +01:00
|
|
|
|
|
|
|
|
QRegExp m_diffFilePattern;
|
|
|
|
|
QList<int> m_diffSections; // line number where this section starts
|
|
|
|
|
int m_cursorLine;
|
2010-01-07 11:33:30 +01:00
|
|
|
QString m_annotateRevisionTextFormat;
|
2010-08-13 09:12:27 +02:00
|
|
|
QString m_annotatePreviousRevisionTextFormat;
|
2010-01-07 17:29:58 +01:00
|
|
|
QString m_copyRevisionTextFormat;
|
2010-01-07 11:33:30 +01:00
|
|
|
bool m_fileLogAnnotateEnabled;
|
2011-02-25 15:58:02 +01:00
|
|
|
TextEditor::BaseTextEditor *m_editor;
|
2010-11-18 16:58:30 +01:00
|
|
|
QWidget *m_configurationWidget;
|
2011-03-24 15:44:39 +01:00
|
|
|
bool m_revertChunkEnabled;
|
2011-04-06 13:59:06 +02:00
|
|
|
bool m_mouseDragging;
|
2012-01-17 16:59:40 +01:00
|
|
|
QList<AbstractTextCursorHandler *> m_textCursorHandlers;
|
2012-03-06 14:32:32 +01:00
|
|
|
|
|
|
|
|
QColor m_backgroundColor;
|
2008-12-02 12:01:29 +01:00
|
|
|
};
|
|
|
|
|
|
2012-01-17 16:59:40 +01:00
|
|
|
VcsBaseEditorWidgetPrivate::VcsBaseEditorWidgetPrivate(VcsBaseEditorWidget *editorWidget,
|
|
|
|
|
const VcsBaseEditorParameters *type) :
|
2010-01-07 11:33:30 +01:00
|
|
|
m_parameters(type),
|
|
|
|
|
m_cursorLine(-1),
|
2012-01-07 12:31:48 +01:00
|
|
|
m_annotateRevisionTextFormat(VcsBaseEditorWidget::tr("Annotate \"%1\"")),
|
|
|
|
|
m_copyRevisionTextFormat(VcsBaseEditorWidget::tr("Copy \"%1\"")),
|
2010-11-18 16:58:30 +01:00
|
|
|
m_fileLogAnnotateEnabled(false),
|
2011-02-25 15:58:02 +01:00
|
|
|
m_editor(0),
|
2011-03-24 15:44:39 +01:00
|
|
|
m_configurationWidget(0),
|
2011-04-06 13:59:06 +02:00
|
|
|
m_revertChunkEnabled(false),
|
|
|
|
|
m_mouseDragging(false)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2012-01-17 16:59:40 +01:00
|
|
|
m_textCursorHandlers.append(new ChangeTextCursorHandler(editorWidget));
|
|
|
|
|
m_textCursorHandlers.append(new UrlTextCursorHandler(editorWidget));
|
|
|
|
|
m_textCursorHandlers.append(new EmailTextCursorHandler(editorWidget));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AbstractTextCursorHandler *VcsBaseEditorWidgetPrivate::findTextCursorHandler(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
foreach (AbstractTextCursorHandler *handler, m_textCursorHandlers) {
|
|
|
|
|
if (handler->findContentsUnderCursor(cursor))
|
|
|
|
|
return handler;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-12-08 13:07:00 +01:00
|
|
|
} // namespace Internal
|
|
|
|
|
|
2011-03-28 14:19:17 +02:00
|
|
|
/*!
|
2012-01-07 12:31:48 +01:00
|
|
|
\struct VcsBase::VcsBaseEditorParameters
|
2011-03-28 14:19:17 +02:00
|
|
|
|
|
|
|
|
\brief Helper struct used to parametrize an editor with mime type, context
|
|
|
|
|
and id. The extension is currently only a suggestion when running
|
|
|
|
|
VCS commands with redirection.
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
\sa VcsBase::VcsBaseEditorWidget, VcsBase::BaseVcsEditorFactory, VcsBase::EditorContentType
|
2011-03-28 14:19:17 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
2012-01-07 12:31:48 +01:00
|
|
|
\class VcsBase::VcsBaseEditorWidget
|
2011-03-28 14:19:17 +02:00
|
|
|
|
|
|
|
|
\brief Base class for editors showing version control system output
|
|
|
|
|
of the type enumerated by EditorContentType.
|
|
|
|
|
|
|
|
|
|
The source property should contain the file or directory the log
|
|
|
|
|
refers to and will be emitted with describeRequested().
|
|
|
|
|
This is for VCS that need a current directory.
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
\sa VcsBase::BaseVcsEditorFactory, VcsBase::VcsBaseEditorParameters, VcsBase::EditorContentType
|
2011-03-28 14:19:17 +02:00
|
|
|
*/
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseEditorWidget::VcsBaseEditorWidget(const VcsBaseEditorParameters *type, QWidget *parent)
|
2011-02-21 16:02:26 +01:00
|
|
|
: BaseTextEditorWidget(parent),
|
2012-01-17 16:59:40 +01:00
|
|
|
d(new Internal::VcsBaseEditorWidgetPrivate(this, type))
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
viewport()->setMouseTracking(true);
|
2009-01-20 11:52:04 +01:00
|
|
|
setMimeType(QLatin1String(d->m_parameters->mimeType));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::init()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-20 11:52:04 +01:00
|
|
|
switch (d->m_parameters->type) {
|
2008-12-02 12:01:29 +01:00
|
|
|
case RegularCommandOutput:
|
|
|
|
|
case LogOutput:
|
|
|
|
|
case AnnotateOutput:
|
|
|
|
|
// Annotation highlighting depends on contents, which is set later on
|
|
|
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation()));
|
|
|
|
|
break;
|
2009-03-10 11:02:12 +01:00
|
|
|
case DiffOutput: {
|
|
|
|
|
DiffHighlighter *dh = createDiffHighlighter();
|
2010-11-21 10:15:12 +01:00
|
|
|
setCodeFoldingSupported(true);
|
2009-03-10 11:02:12 +01:00
|
|
|
baseTextDocument()->setSyntaxHighlighter(dh);
|
|
|
|
|
d->m_diffFilePattern = dh->filePattern();
|
|
|
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(slotPopulateDiffBrowser()));
|
|
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotDiffCursorPositionChanged()));
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2011-06-27 15:00:16 +02:00
|
|
|
TextEditor::TextEditorSettings::instance()->initializeEditor(this);
|
2012-03-06 15:09:08 +01:00
|
|
|
// override revisions display (green or red bar on the left, marking changes):
|
|
|
|
|
setRevisionsVisible(false);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseEditorWidget::~VcsBaseEditorWidget()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-20 11:52:04 +01:00
|
|
|
delete d;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setForceReadOnly(bool b)
|
2010-06-07 14:53:28 +02:00
|
|
|
{
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseEditor *eda = qobject_cast<VcsBaseEditor *>(editor());
|
2012-02-14 12:10:29 +01:00
|
|
|
QTC_ASSERT(eda != 0, return);
|
2010-06-07 14:53:28 +02:00
|
|
|
setReadOnly(b);
|
|
|
|
|
eda->setTemporary(b);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::source() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-20 11:52:04 +01:00
|
|
|
return d->m_source;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setSource(const QString &source)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-20 11:52:04 +01:00
|
|
|
d->m_source = source;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::annotateRevisionTextFormat() const
|
2010-01-07 11:33:30 +01:00
|
|
|
{
|
|
|
|
|
return d->m_annotateRevisionTextFormat;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setAnnotateRevisionTextFormat(const QString &f)
|
2010-01-07 11:33:30 +01:00
|
|
|
{
|
|
|
|
|
d->m_annotateRevisionTextFormat = f;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::annotatePreviousRevisionTextFormat() const
|
2010-08-13 09:12:27 +02:00
|
|
|
{
|
|
|
|
|
return d->m_annotatePreviousRevisionTextFormat;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setAnnotatePreviousRevisionTextFormat(const QString &f)
|
2010-08-13 09:12:27 +02:00
|
|
|
{
|
|
|
|
|
d->m_annotatePreviousRevisionTextFormat = f;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::copyRevisionTextFormat() const
|
2010-01-07 17:29:58 +01:00
|
|
|
{
|
|
|
|
|
return d->m_copyRevisionTextFormat;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setCopyRevisionTextFormat(const QString &f)
|
2010-01-07 17:29:58 +01:00
|
|
|
{
|
|
|
|
|
d->m_copyRevisionTextFormat = f;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
bool VcsBaseEditorWidget::isFileLogAnnotateEnabled() const
|
2010-01-07 11:33:30 +01:00
|
|
|
{
|
|
|
|
|
return d->m_fileLogAnnotateEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setFileLogAnnotateEnabled(bool e)
|
2010-01-07 11:33:30 +01:00
|
|
|
{
|
|
|
|
|
d->m_fileLogAnnotateEnabled = e;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::diffBaseDirectory() const
|
2009-12-09 12:41:10 +01:00
|
|
|
{
|
|
|
|
|
return d->m_diffBaseDirectory;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setDiffBaseDirectory(const QString &bd)
|
2009-12-09 12:41:10 +01:00
|
|
|
{
|
|
|
|
|
d->m_diffBaseDirectory = bd;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QTextCodec *VcsBaseEditorWidget::codec() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-08-25 12:54:20 +02:00
|
|
|
return const_cast<QTextCodec *>(baseTextDocument()->codec());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setCodec(QTextCodec *c)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (c) {
|
|
|
|
|
baseTextDocument()->setCodec(c);
|
|
|
|
|
} else {
|
|
|
|
|
qWarning("%s: Attempt to set 0 codec.", Q_FUNC_INFO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
EditorContentType VcsBaseEditorWidget::contentType() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-20 11:52:04 +01:00
|
|
|
return d->m_parameters->type;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
bool VcsBaseEditorWidget::isModified() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
TextEditor::BaseTextEditor *VcsBaseEditorWidget::createEditor()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditor *editor = 0;
|
2010-01-06 17:24:40 +01:00
|
|
|
if (d->m_parameters->type == DiffOutput) {
|
|
|
|
|
// Diff: set up diff file browsing
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseDiffEditor *de = new VcsBaseDiffEditor(this, d->m_parameters);
|
2010-01-06 17:24:40 +01:00
|
|
|
QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
|
2010-04-14 12:58:39 +02:00
|
|
|
connect(diffBrowseComboBox, SIGNAL(activated(int)), this, SLOT(slotDiffBrowse(int)));
|
2011-02-21 16:02:26 +01:00
|
|
|
editor = de;
|
2010-01-06 17:24:40 +01:00
|
|
|
} else {
|
2012-01-07 12:31:48 +01:00
|
|
|
editor = new VcsBaseEditor(this, d->m_parameters);
|
2010-01-06 17:24:40 +01:00
|
|
|
}
|
2011-02-25 15:58:02 +01:00
|
|
|
d->m_editor = editor;
|
2010-11-18 16:58:30 +01:00
|
|
|
|
2010-01-06 17:24:40 +01:00
|
|
|
// Pass on signals.
|
|
|
|
|
connect(this, SIGNAL(describeRequested(QString,QString)),
|
2011-02-21 16:02:26 +01:00
|
|
|
editor, SIGNAL(describeRequested(QString,QString)));
|
2010-01-07 11:33:30 +01:00
|
|
|
connect(this, SIGNAL(annotateRevisionRequested(QString,QString,int)),
|
2011-02-21 16:02:26 +01:00
|
|
|
editor, SIGNAL(annotateRevisionRequested(QString,QString,int)));
|
|
|
|
|
return editor;
|
2009-03-10 11:02:12 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::slotPopulateDiffBrowser()
|
2009-03-10 11:02:12 +01:00
|
|
|
{
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseDiffEditor *de = static_cast<VcsBaseDiffEditor*>(editor());
|
2009-03-10 11:02:12 +01:00
|
|
|
QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
|
|
|
|
|
diffBrowseComboBox->clear();
|
|
|
|
|
d->m_diffSections.clear();
|
|
|
|
|
// Create a list of section line numbers (diffed files)
|
|
|
|
|
// and populate combo with filenames.
|
|
|
|
|
const QTextBlock cend = document()->end();
|
|
|
|
|
int lineNumber = 0;
|
|
|
|
|
QString lastFileName;
|
|
|
|
|
for (QTextBlock it = document()->begin(); it != cend; it = it.next(), lineNumber++) {
|
|
|
|
|
const QString text = it.text();
|
|
|
|
|
// Check for a new diff section (not repeating the last filename)
|
|
|
|
|
if (d->m_diffFilePattern.exactMatch(text)) {
|
|
|
|
|
const QString file = fileNameFromDiffSpecification(it);
|
|
|
|
|
if (!file.isEmpty() && lastFileName != file) {
|
|
|
|
|
lastFileName = file;
|
|
|
|
|
// ignore any headers
|
|
|
|
|
d->m_diffSections.push_back(d->m_diffSections.empty() ? 0 : lineNumber);
|
|
|
|
|
diffBrowseComboBox->addItem(QFileInfo(file).fileName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::slotDiffBrowse(int index)
|
2009-03-10 11:02:12 +01:00
|
|
|
{
|
|
|
|
|
// goto diffed file as indicated by index/line number
|
|
|
|
|
if (index < 0 || index >= d->m_diffSections.size())
|
2010-01-29 21:33:57 +01:00
|
|
|
return;
|
2010-04-14 12:58:39 +02:00
|
|
|
const int lineNumber = d->m_diffSections.at(index) + 1; // TextEdit uses 1..n convention
|
|
|
|
|
// check if we need to do something, especially to avoid messing up navigation history
|
|
|
|
|
int currentLine, currentColumn;
|
|
|
|
|
convertPosition(position(), ¤tLine, ¤tColumn);
|
|
|
|
|
if (lineNumber != currentLine) {
|
|
|
|
|
Core::EditorManager *editorManager = Core::EditorManager::instance();
|
|
|
|
|
editorManager->addCurrentPositionToNavigationHistory();
|
|
|
|
|
gotoLine(lineNumber, 0);
|
|
|
|
|
}
|
2009-03-10 11:02:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Locate a line number in the list of diff sections.
|
|
|
|
|
static int sectionOfLine(int line, const QList<int> §ions)
|
|
|
|
|
{
|
|
|
|
|
const int sectionCount = sections.size();
|
|
|
|
|
if (!sectionCount)
|
|
|
|
|
return -1;
|
|
|
|
|
// The section at s indicates where the section begins.
|
|
|
|
|
for (int s = 0; s < sectionCount; s++) {
|
|
|
|
|
if (line < sections.at(s))
|
|
|
|
|
return s - 1;
|
|
|
|
|
}
|
|
|
|
|
return sectionCount - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::slotDiffCursorPositionChanged()
|
2009-03-10 11:02:12 +01:00
|
|
|
{
|
|
|
|
|
// Adapt diff file browse combo to new position
|
|
|
|
|
// if the cursor goes across a file line.
|
2012-04-17 08:01:25 +02:00
|
|
|
QTC_ASSERT(d->m_parameters->type == DiffOutput, return);
|
2010-01-29 21:33:57 +01:00
|
|
|
const int newCursorLine = textCursor().blockNumber();
|
2009-03-10 11:02:12 +01:00
|
|
|
if (newCursorLine == d->m_cursorLine)
|
|
|
|
|
return;
|
|
|
|
|
// Which section does it belong to?
|
|
|
|
|
d->m_cursorLine = newCursorLine;
|
|
|
|
|
const int section = sectionOfLine(d->m_cursorLine, d->m_diffSections);
|
|
|
|
|
if (section != -1) {
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseDiffEditor *de = static_cast<VcsBaseDiffEditor*>(editor());
|
2009-03-10 11:02:12 +01:00
|
|
|
QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
|
|
|
|
|
if (diffBrowseComboBox->currentIndex() != section) {
|
|
|
|
|
const bool blocked = diffBrowseComboBox->blockSignals(true);
|
|
|
|
|
diffBrowseComboBox->setCurrentIndex(section);
|
|
|
|
|
diffBrowseComboBox->blockSignals(blocked);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QMenu *menu = createStandardContextMenu();
|
|
|
|
|
// 'click on change-interaction'
|
2011-03-24 15:44:39 +01:00
|
|
|
switch (d->m_parameters->type) {
|
|
|
|
|
case LogOutput:
|
2012-01-17 16:59:40 +01:00
|
|
|
case AnnotateOutput: {
|
|
|
|
|
const QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
Internal::AbstractTextCursorHandler *handler = d->findTextCursorHandler(cursor);
|
|
|
|
|
if (handler != 0)
|
|
|
|
|
handler->fillContextMenu(menu, d->m_parameters->type);
|
2011-03-24 15:44:39 +01:00
|
|
|
break;
|
2012-01-17 16:59:40 +01:00
|
|
|
}
|
2011-03-24 15:44:39 +01:00
|
|
|
case DiffOutput: {
|
|
|
|
|
menu->addSeparator();
|
2011-04-21 15:21:50 +02:00
|
|
|
connect(menu->addAction(tr("Send to CodePaster...")), SIGNAL(triggered()),
|
|
|
|
|
this, SLOT(slotPaste()));
|
|
|
|
|
menu->addSeparator();
|
2011-08-12 13:42:21 +02:00
|
|
|
// Apply/revert diff chunk.
|
2011-03-24 15:44:39 +01:00
|
|
|
const DiffChunk chunk = diffChunk(cursorForPosition(e->pos()));
|
2011-08-12 13:42:21 +02:00
|
|
|
const bool canApply = canApplyDiffChunk(chunk);
|
|
|
|
|
// Apply a chunk from a diff loaded into the editor. This typically will
|
|
|
|
|
// not have the 'source' property set and thus will only work if the working
|
|
|
|
|
// directory matches that of the patch (see findDiffFile()). In addition,
|
|
|
|
|
// the user has "Open With" and choose the right diff editor so that
|
|
|
|
|
// fileNameFromDiffSpecification() works.
|
|
|
|
|
QAction *applyAction = menu->addAction(tr("Apply Chunk..."));
|
|
|
|
|
applyAction->setEnabled(canApply);
|
|
|
|
|
applyAction->setData(qVariantFromValue(Internal::DiffChunkAction(chunk, false)));
|
|
|
|
|
connect(applyAction, SIGNAL(triggered()), this, SLOT(slotApplyDiffChunk()));
|
|
|
|
|
// Revert a chunk from a VCS diff, which might be linked to reloading the diff.
|
|
|
|
|
QAction *revertAction = menu->addAction(tr("Revert Chunk..."));
|
|
|
|
|
revertAction->setEnabled(isRevertDiffChunkEnabled() && canApply);
|
|
|
|
|
revertAction->setData(qVariantFromValue(Internal::DiffChunkAction(chunk, true)));
|
|
|
|
|
connect(revertAction, SIGNAL(triggered()), this, SLOT(slotApplyDiffChunk()));
|
2011-03-24 15:44:39 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
menu->exec(e->globalPos());
|
|
|
|
|
delete menu;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::mouseMoveEvent(QMouseEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-04-06 13:59:06 +02:00
|
|
|
if (e->buttons()) {
|
|
|
|
|
d->m_mouseDragging = true;
|
|
|
|
|
TextEditor::BaseTextEditorWidget::mouseMoveEvent(e);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-10 09:28:53 +01:00
|
|
|
bool overrideCursor = false;
|
|
|
|
|
Qt::CursorShape cursorShape;
|
|
|
|
|
|
2009-01-20 11:52:04 +01:00
|
|
|
if (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput) {
|
2008-12-02 12:01:29 +01:00
|
|
|
// Link emulation behaviour for 'click on change-interaction'
|
2012-01-17 16:59:40 +01:00
|
|
|
const QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
Internal::AbstractTextCursorHandler *handler = d->findTextCursorHandler(cursor);
|
|
|
|
|
if (handler != 0) {
|
|
|
|
|
handler->highlightCurrentContents();
|
2008-12-10 09:28:53 +01:00
|
|
|
overrideCursor = true;
|
|
|
|
|
cursorShape = Qt::PointingHandCursor;
|
2012-01-26 14:10:28 +01:00
|
|
|
} else {
|
|
|
|
|
setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
|
|
|
|
|
overrideCursor = true;
|
|
|
|
|
cursorShape = Qt::IBeamCursor;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditorWidget::mouseMoveEvent(e);
|
2008-12-10 09:28:53 +01:00
|
|
|
|
|
|
|
|
if (overrideCursor)
|
|
|
|
|
viewport()->setCursor(cursorShape);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::mouseReleaseEvent(QMouseEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-04-06 13:59:06 +02:00
|
|
|
const bool wasDragging = d->m_mouseDragging;
|
|
|
|
|
d->m_mouseDragging = false;
|
|
|
|
|
if (!wasDragging && (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput)) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) {
|
2012-01-17 16:59:40 +01:00
|
|
|
const QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
Internal::AbstractTextCursorHandler *handler = d->findTextCursorHandler(cursor);
|
|
|
|
|
if (handler != 0) {
|
|
|
|
|
handler->handleCurrentContents();
|
2008-12-02 12:01:29 +01:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditorWidget::mouseReleaseEvent(e);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-01-20 11:52:04 +01:00
|
|
|
if (d->m_parameters->type == DiffOutput) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) {
|
|
|
|
|
QTextCursor cursor = cursorForPosition(e->pos());
|
|
|
|
|
jumpToChangeFromDiff(cursor);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditorWidget::mouseDoubleClickEvent(e);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::keyPressEvent(QKeyEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-07 14:53:28 +02:00
|
|
|
// Do not intercept return in editable patches.
|
|
|
|
|
if (d->m_parameters->type == DiffOutput && isReadOnly()
|
|
|
|
|
&& (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)) {
|
2008-12-02 12:01:29 +01:00
|
|
|
jumpToChangeFromDiff(textCursor());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditorWidget::keyPressEvent(e);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::slotActivateAnnotation()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
// The annotation highlighting depends on contents (change number
|
|
|
|
|
// set with assigned colors)
|
2009-01-20 11:52:04 +01:00
|
|
|
if (d->m_parameters->type != AnnotateOutput)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QSet<QString> changes = annotationChanges();
|
|
|
|
|
if (changes.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
disconnect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation()));
|
|
|
|
|
|
|
|
|
|
if (BaseAnnotationHighlighter *ah = qobject_cast<BaseAnnotationHighlighter *>(baseTextDocument()->syntaxHighlighter())) {
|
|
|
|
|
ah->setChangeNumbers(changes);
|
|
|
|
|
ah->rehighlight();
|
|
|
|
|
} else {
|
2012-03-06 14:32:32 +01:00
|
|
|
baseTextDocument()->setSyntaxHighlighter(createAnnotationHighlighter(changes, d->m_backgroundColor));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-30 11:31:51 +01:00
|
|
|
// Check for a chunk of
|
|
|
|
|
// - changes : "@@ -91,7 +95,7 @@"
|
|
|
|
|
// - merged conflicts : "@@@ -91,7 +95,7 @@@"
|
|
|
|
|
// and return the modified line number (here 95).
|
|
|
|
|
// Note that git appends stuff after " @@"/" @@@" (function names, etc.).
|
|
|
|
|
static inline bool checkChunkLine(const QString &line, int *modifiedLineNumber, int numberOfAts)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-10-30 11:31:51 +01:00
|
|
|
const QString ats(numberOfAts, QLatin1Char('@'));
|
|
|
|
|
if (!line.startsWith(ats + QLatin1Char(' ')))
|
2008-12-02 12:01:29 +01:00
|
|
|
return false;
|
2011-10-30 11:31:51 +01:00
|
|
|
const int len = ats.size() + 1;
|
|
|
|
|
const int endPos = line.indexOf(QLatin1Char(' ') + ats, len);
|
2008-12-02 12:01:29 +01:00
|
|
|
if (endPos == -1)
|
|
|
|
|
return false;
|
|
|
|
|
// the first chunk range applies to the original file, the second one to
|
|
|
|
|
// the modified file, the one we're interested int
|
2011-10-30 11:31:51 +01:00
|
|
|
const int plusPos = line.indexOf(QLatin1Char('+'), len);
|
2008-12-02 12:01:29 +01:00
|
|
|
if (plusPos == -1 || plusPos > endPos)
|
|
|
|
|
return false;
|
|
|
|
|
const int lineNumberPos = plusPos + 1;
|
|
|
|
|
const int commaPos = line.indexOf(QLatin1Char(','), lineNumberPos);
|
|
|
|
|
if (commaPos == -1 || commaPos > endPos)
|
|
|
|
|
return false;
|
|
|
|
|
const QString lineNumberStr = line.mid(lineNumberPos, commaPos - lineNumberPos);
|
|
|
|
|
bool ok;
|
|
|
|
|
*modifiedLineNumber = lineNumberStr.toInt(&ok);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-30 11:31:51 +01:00
|
|
|
static inline bool checkChunkLine(const QString &line, int *modifiedLineNumber)
|
|
|
|
|
{
|
|
|
|
|
if (checkChunkLine(line, modifiedLineNumber, 2))
|
|
|
|
|
return true;
|
|
|
|
|
return checkChunkLine(line, modifiedLineNumber, 3);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::jumpToChangeFromDiff(QTextCursor cursor)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
int chunkStart = 0;
|
|
|
|
|
int lineCount = -1;
|
|
|
|
|
const QChar deletionIndicator = QLatin1Char('-');
|
|
|
|
|
// find nearest change hunk
|
|
|
|
|
QTextBlock block = cursor.block();
|
2011-04-08 10:40:59 +02:00
|
|
|
if (TextEditor::BaseTextDocumentLayout::foldingIndent(block) <= 1)
|
|
|
|
|
/* We are in a diff header, do not jump anywhere. DiffHighlighter sets the foldingIndent for us. */
|
|
|
|
|
return;
|
2008-12-02 12:01:29 +01:00
|
|
|
for ( ; block.isValid() ; block = block.previous()) {
|
|
|
|
|
const QString line = block.text();
|
|
|
|
|
if (checkChunkLine(line, &chunkStart)) {
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
if (!line.startsWith(deletionIndicator))
|
|
|
|
|
++lineCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chunkStart == -1 || lineCount < 0 || !block.isValid())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// find the filename in previous line, map depot name back
|
|
|
|
|
block = block.previous();
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
return;
|
2012-02-03 15:22:43 +01:00
|
|
|
const QString fileName = findDiffFile(fileNameFromDiffSpecification(block));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
const bool exists = fileName.isEmpty() ? false : QFile::exists(fileName);
|
|
|
|
|
|
|
|
|
|
if (!exists)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-01-21 15:52:34 +01:00
|
|
|
Core::EditorManager *em = Core::EditorManager::instance();
|
2011-11-10 11:36:51 +01:00
|
|
|
Core::IEditor *ed = em->openEditor(fileName, Core::Id(), Core::EditorManager::ModeSwitch);
|
2009-01-20 11:52:04 +01:00
|
|
|
if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ed))
|
2008-12-02 12:01:29 +01:00
|
|
|
editor->gotoLine(chunkStart + lineCount);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-24 15:44:39 +01:00
|
|
|
// cut out chunk and determine file name.
|
2012-01-07 12:31:48 +01:00
|
|
|
DiffChunk VcsBaseEditorWidget::diffChunk(QTextCursor cursor) const
|
2011-03-24 15:44:39 +01:00
|
|
|
{
|
|
|
|
|
DiffChunk rc;
|
2012-04-17 08:01:25 +02:00
|
|
|
QTC_ASSERT(d->m_parameters->type == DiffOutput, return rc);
|
2011-03-24 15:44:39 +01:00
|
|
|
// Search back for start of chunk.
|
|
|
|
|
QTextBlock block = cursor.block();
|
2011-11-13 22:51:43 +02:00
|
|
|
if (block.isValid() && TextEditor::BaseTextDocumentLayout::foldingIndent(block) <= 1)
|
2011-04-08 10:42:07 +02:00
|
|
|
/* We are in a diff header, not in a chunk! DiffHighlighter sets the foldingIndent for us. */
|
|
|
|
|
return rc;
|
|
|
|
|
|
2011-03-24 15:44:39 +01:00
|
|
|
int chunkStart = 0;
|
|
|
|
|
for ( ; block.isValid() ; block = block.previous()) {
|
|
|
|
|
if (checkChunkLine(block.text(), &chunkStart)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!chunkStart || !block.isValid())
|
|
|
|
|
return rc;
|
2012-02-03 15:22:43 +01:00
|
|
|
rc.fileName = findDiffFile(fileNameFromDiffSpecification(block));
|
2011-03-24 15:44:39 +01:00
|
|
|
if (rc.fileName.isEmpty())
|
|
|
|
|
return rc;
|
|
|
|
|
// Concatenate chunk and convert
|
|
|
|
|
QString unicode = block.text();
|
2011-03-28 11:59:26 +02:00
|
|
|
if (!unicode.endsWith(QLatin1Char('\n'))) // Missing in case of hg.
|
|
|
|
|
unicode.append(QLatin1Char('\n'));
|
2011-03-24 15:44:39 +01:00
|
|
|
for (block = block.next() ; block.isValid() ; block = block.next()) {
|
|
|
|
|
const QString line = block.text();
|
|
|
|
|
if (checkChunkLine(line, &chunkStart)) {
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
unicode += line;
|
|
|
|
|
unicode += QLatin1Char('\n');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const QTextCodec *cd = textCodec();
|
|
|
|
|
rc.chunk = cd ? cd->fromUnicode(unicode) : unicode.toLocal8Bit();
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setPlainTextData(const QByteArray &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-07-12 14:15:38 +02:00
|
|
|
if (data.size() > Core::EditorManager::maxTextFileSize()) {
|
|
|
|
|
setPlainText(msgTextTooLarge(data.size()));
|
|
|
|
|
} else {
|
|
|
|
|
setPlainText(codec()->toUnicode(data));
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-02-21 16:02:26 +01:00
|
|
|
TextEditor::BaseTextEditorWidget::setFontSettings(fs);
|
2012-04-26 14:17:42 +02:00
|
|
|
d->m_backgroundColor = fs.toTextCharFormat(TextEditor::C_TEXT)
|
2012-03-06 14:32:32 +01:00
|
|
|
.brushProperty(QTextFormat::BackgroundBrush).color();
|
|
|
|
|
|
2009-01-20 11:52:04 +01:00
|
|
|
if (d->m_parameters->type == DiffOutput) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (DiffHighlighter *highlighter = qobject_cast<DiffHighlighter*>(baseTextDocument()->syntaxHighlighter())) {
|
2012-04-26 14:17:42 +02:00
|
|
|
static QVector<TextEditor::TextStyle> categories;
|
2008-12-02 12:01:29 +01:00
|
|
|
if (categories.isEmpty()) {
|
2012-04-26 14:17:42 +02:00
|
|
|
categories << TextEditor::C_TEXT
|
|
|
|
|
<< TextEditor::C_ADDED_LINE
|
|
|
|
|
<< TextEditor::C_REMOVED_LINE
|
|
|
|
|
<< TextEditor::C_DIFF_FILE
|
|
|
|
|
<< TextEditor::C_DIFF_LOCATION;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
highlighter->setFormats(fs.toTextCharFormats(categories));
|
|
|
|
|
highlighter->rehighlight();
|
|
|
|
|
}
|
2012-03-06 14:32:32 +01:00
|
|
|
} else if (d->m_parameters->type == AnnotateOutput) {
|
|
|
|
|
if (BaseAnnotationHighlighter *highlighter = qobject_cast<BaseAnnotationHighlighter *>(baseTextDocument()->syntaxHighlighter())) {
|
|
|
|
|
highlighter->setBackgroundColor(d->m_backgroundColor);
|
|
|
|
|
highlighter->rehighlight();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
const VcsBaseEditorParameters *VcsBaseEditorWidget::findType(const VcsBaseEditorParameters *array,
|
2008-12-02 12:01:29 +01:00
|
|
|
int arraySize,
|
|
|
|
|
EditorContentType et)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < arraySize; i++)
|
|
|
|
|
if (array[i].type == et)
|
|
|
|
|
return array + i;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the codec used for a file querying the editor.
|
2009-01-20 11:52:04 +01:00
|
|
|
static QTextCodec *findFileCodec(const QString &source)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
typedef QList<Core::IEditor *> EditorList;
|
|
|
|
|
|
2009-01-21 15:52:34 +01:00
|
|
|
const EditorList editors = Core::EditorManager::instance()->editorsForFileName(source);
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!editors.empty()) {
|
|
|
|
|
const EditorList::const_iterator ecend = editors.constEnd();
|
|
|
|
|
for (EditorList::const_iterator it = editors.constBegin(); it != ecend; ++it)
|
2011-02-21 16:02:26 +01:00
|
|
|
if (const TextEditor::BaseTextEditor *be = qobject_cast<const TextEditor::BaseTextEditor *>(*it)) {
|
|
|
|
|
QTextCodec *codec = be->editorWidget()->textCodec();
|
2008-12-02 12:01:29 +01:00
|
|
|
return codec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the codec by checking the projects (root dir of project file)
|
|
|
|
|
static QTextCodec *findProjectCodec(const QString &dir)
|
|
|
|
|
{
|
|
|
|
|
typedef QList<ProjectExplorer::Project*> ProjectList;
|
|
|
|
|
// Try to find a project under which file tree the file is.
|
|
|
|
|
const ProjectExplorer::SessionManager *sm = ProjectExplorer::ProjectExplorerPlugin::instance()->session();
|
|
|
|
|
const ProjectList projects = sm->projects();
|
|
|
|
|
if (!projects.empty()) {
|
|
|
|
|
const ProjectList::const_iterator pcend = projects.constEnd();
|
|
|
|
|
for (ProjectList::const_iterator it = projects.constBegin(); it != pcend; ++it)
|
2012-02-14 16:43:51 +01:00
|
|
|
if (const Core::IDocument *document = (*it)->document())
|
|
|
|
|
if (document->fileName().startsWith(dir)) {
|
2011-02-01 14:13:54 +01:00
|
|
|
QTextCodec *codec = (*it)->editorConfiguration()->textCodec();
|
2008-12-02 12:01:29 +01:00
|
|
|
return codec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QTextCodec *VcsBaseEditorWidget::getCodec(const QString &source)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (!source.isEmpty()) {
|
|
|
|
|
// Check file
|
|
|
|
|
const QFileInfo sourceFi(source);
|
|
|
|
|
if (sourceFi.isFile())
|
2009-01-20 11:52:04 +01:00
|
|
|
if (QTextCodec *fc = findFileCodec(source))
|
2008-12-02 12:01:29 +01:00
|
|
|
return fc;
|
|
|
|
|
// Find by project via directory
|
|
|
|
|
if (QTextCodec *pc = findProjectCodec(sourceFi.isFile() ? sourceFi.absolutePath() : source))
|
|
|
|
|
return pc;
|
|
|
|
|
}
|
|
|
|
|
QTextCodec *sys = QTextCodec::codecForLocale();
|
|
|
|
|
return sys;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QTextCodec *VcsBaseEditorWidget::getCodec(const QString &workingDirectory, const QStringList &files)
|
2009-12-08 16:50:27 +01:00
|
|
|
{
|
|
|
|
|
if (files.empty())
|
|
|
|
|
return getCodec(workingDirectory);
|
|
|
|
|
return getCodec(workingDirectory + QLatin1Char('/') + files.front());
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
VcsBaseEditorWidget *VcsBaseEditorWidget::getVcsBaseEditor(const Core::IEditor *editor)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2011-02-21 16:02:26 +01:00
|
|
|
if (const TextEditor::BaseTextEditor *be = qobject_cast<const TextEditor::BaseTextEditor *>(editor))
|
2012-01-07 12:31:48 +01:00
|
|
|
return qobject_cast<VcsBaseEditorWidget *>(be->editorWidget());
|
2008-12-02 12:01:29 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-01 17:32:26 +02:00
|
|
|
// Return line number of current editor if it matches.
|
2012-01-07 12:31:48 +01:00
|
|
|
int VcsBaseEditorWidget::lineNumberOfCurrentEditor(const QString ¤tFile)
|
2009-10-01 17:32:26 +02:00
|
|
|
{
|
|
|
|
|
Core::IEditor *ed = Core::EditorManager::instance()->currentEditor();
|
|
|
|
|
if (!ed)
|
|
|
|
|
return -1;
|
|
|
|
|
if (!currentFile.isEmpty()) {
|
2012-02-14 16:43:51 +01:00
|
|
|
const Core::IDocument *idocument = ed->document();
|
|
|
|
|
if (!idocument || idocument->fileName() != currentFile)
|
2009-10-01 17:32:26 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-02-21 16:02:26 +01:00
|
|
|
const TextEditor::BaseTextEditor *eda = qobject_cast<const TextEditor::BaseTextEditor *>(ed);
|
2009-10-01 17:32:26 +02:00
|
|
|
if (!eda)
|
|
|
|
|
return -1;
|
|
|
|
|
return eda->currentLine();
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
bool VcsBaseEditorWidget::gotoLineOfEditor(Core::IEditor *e, int lineNumber)
|
2009-10-01 17:32:26 +02:00
|
|
|
{
|
|
|
|
|
if (lineNumber >= 0 && e) {
|
2011-02-21 16:02:26 +01:00
|
|
|
if (TextEditor::BaseTextEditor *be = qobject_cast<TextEditor::BaseTextEditor*>(e)) {
|
2009-10-01 17:32:26 +02:00
|
|
|
be->gotoLine(lineNumber);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-08 14:26:41 +01:00
|
|
|
// Return source file or directory string depending on parameters
|
|
|
|
|
// ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file').
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::getSource(const QString &workingDirectory,
|
2009-12-08 14:26:41 +01:00
|
|
|
const QString &fileName)
|
|
|
|
|
{
|
|
|
|
|
if (fileName.isEmpty())
|
|
|
|
|
return workingDirectory;
|
|
|
|
|
|
|
|
|
|
QString rc = workingDirectory;
|
|
|
|
|
const QChar slash = QLatin1Char('/');
|
|
|
|
|
if (!rc.isEmpty() && !(rc.endsWith(slash) || rc.endsWith(QLatin1Char('\\'))))
|
|
|
|
|
rc += slash;
|
|
|
|
|
rc += fileName;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::getSource(const QString &workingDirectory,
|
2009-12-08 14:26:41 +01:00
|
|
|
const QStringList &fileNames)
|
|
|
|
|
{
|
|
|
|
|
return fileNames.size() == 1 ?
|
|
|
|
|
getSource(workingDirectory, fileNames.front()) :
|
|
|
|
|
workingDirectory;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::getTitleId(const QString &workingDirectory,
|
2010-01-06 17:24:40 +01:00
|
|
|
const QStringList &fileNames,
|
|
|
|
|
const QString &revision)
|
2009-12-08 14:26:41 +01:00
|
|
|
{
|
2010-01-06 17:24:40 +01:00
|
|
|
QString rc;
|
2009-12-08 14:26:41 +01:00
|
|
|
switch (fileNames.size()) {
|
|
|
|
|
case 0:
|
2010-01-06 17:24:40 +01:00
|
|
|
rc = workingDirectory;
|
|
|
|
|
break;
|
2009-12-08 14:26:41 +01:00
|
|
|
case 1:
|
2010-01-06 17:24:40 +01:00
|
|
|
rc = fileNames.front();
|
|
|
|
|
break;
|
2009-12-08 14:26:41 +01:00
|
|
|
default:
|
2010-01-06 17:24:40 +01:00
|
|
|
rc = fileNames.join(QLatin1String(", "));
|
2009-12-08 14:26:41 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2010-01-06 17:24:40 +01:00
|
|
|
if (!revision.isEmpty()) {
|
|
|
|
|
rc += QLatin1Char(':');
|
|
|
|
|
rc += revision;
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
2009-12-08 14:26:41 +01:00
|
|
|
}
|
2009-10-01 17:32:26 +02:00
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
bool VcsBaseEditorWidget::setConfigurationWidget(QWidget *w)
|
2010-11-18 16:58:30 +01:00
|
|
|
{
|
2011-02-25 15:58:02 +01:00
|
|
|
if (!d->m_editor || d->m_configurationWidget)
|
2010-11-18 16:58:30 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
d->m_configurationWidget = w;
|
2011-02-25 15:58:02 +01:00
|
|
|
d->m_editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Right, w);
|
|
|
|
|
|
2010-11-18 16:58:30 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QWidget *VcsBaseEditorWidget::configurationWidget() const
|
2010-11-18 16:58:30 +01:00
|
|
|
{
|
|
|
|
|
return d->m_configurationWidget;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-08 16:50:27 +01:00
|
|
|
// Find the complete file from a diff relative specification.
|
2012-02-03 11:41:57 +01:00
|
|
|
QString VcsBaseEditorWidget::findDiffFile(const QString &f) const
|
2009-12-08 16:50:27 +01:00
|
|
|
{
|
2011-05-03 10:12:10 +02:00
|
|
|
// Check if file is absolute
|
2009-12-08 16:50:27 +01:00
|
|
|
const QFileInfo in(f);
|
|
|
|
|
if (in.isAbsolute())
|
|
|
|
|
return in.isFile() ? f : QString();
|
2011-05-03 10:12:10 +02:00
|
|
|
|
2009-12-09 12:41:10 +01:00
|
|
|
// 1) Try base dir
|
|
|
|
|
const QChar slash = QLatin1Char('/');
|
|
|
|
|
if (!d->m_diffBaseDirectory.isEmpty()) {
|
|
|
|
|
const QFileInfo baseFileInfo(d->m_diffBaseDirectory + slash + f);
|
|
|
|
|
if (baseFileInfo.isFile())
|
|
|
|
|
return baseFileInfo.absoluteFilePath();
|
|
|
|
|
}
|
|
|
|
|
// 2) Try in source (which can be file or directory)
|
2011-05-03 10:12:10 +02:00
|
|
|
if (!source().isEmpty()) {
|
|
|
|
|
const QFileInfo sourceInfo(source());
|
|
|
|
|
const QString sourceDir = sourceInfo.isDir() ? sourceInfo.absoluteFilePath()
|
|
|
|
|
: sourceInfo.absolutePath();
|
|
|
|
|
const QFileInfo sourceFileInfo(sourceDir + slash + f);
|
|
|
|
|
if (sourceFileInfo.isFile())
|
|
|
|
|
return sourceFileInfo.absoluteFilePath();
|
|
|
|
|
|
|
|
|
|
QString topLevel;
|
2012-02-03 11:41:57 +01:00
|
|
|
Core::VcsManager *vcsManager = Core::ICore::vcsManager();
|
|
|
|
|
vcsManager->findVersionControlForDirectory(sourceDir, &topLevel); //
|
|
|
|
|
if (topLevel.isEmpty())
|
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
|
|
const QFileInfo topLevelFileInfo(topLevel + slash + f);
|
|
|
|
|
if (topLevelFileInfo.isFile())
|
|
|
|
|
return topLevelFileInfo.absoluteFilePath();
|
2011-05-03 10:12:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) Try working directory
|
|
|
|
|
if (in.isFile())
|
|
|
|
|
return in.absoluteFilePath();
|
|
|
|
|
|
2009-12-08 16:50:27 +01:00
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::slotAnnotateRevision()
|
2010-01-06 17:24:40 +01:00
|
|
|
{
|
|
|
|
|
if (const QAction *a = qobject_cast<const QAction *>(sender()))
|
2010-01-07 11:33:30 +01:00
|
|
|
emit annotateRevisionRequested(source(), a->data().toString(),
|
2011-02-21 16:02:26 +01:00
|
|
|
editor()->currentLine());
|
2010-01-06 17:24:40 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
QStringList VcsBaseEditorWidget::annotationPreviousVersions(const QString &) const
|
2010-01-06 17:24:40 +01:00
|
|
|
{
|
|
|
|
|
return QStringList();
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::slotPaste()
|
2011-04-21 15:21:50 +02:00
|
|
|
{
|
|
|
|
|
// Retrieve service by soft dependency.
|
|
|
|
|
QObject *pasteService =
|
|
|
|
|
ExtensionSystem::PluginManager::instance()
|
2012-01-31 10:57:10 +01:00
|
|
|
->getObjectByClassName(QLatin1String("CodePaster::CodePasterService"));
|
2011-04-21 15:21:50 +02:00
|
|
|
if (pasteService) {
|
|
|
|
|
QMetaObject::invokeMethod(pasteService, "postCurrentEditor");
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::information(this, tr("Unable to Paste"),
|
|
|
|
|
tr("Code pasting services are not available."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
bool VcsBaseEditorWidget::isRevertDiffChunkEnabled() const
|
2011-03-24 15:44:39 +01:00
|
|
|
{
|
|
|
|
|
return d->m_revertChunkEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::setRevertDiffChunkEnabled(bool e)
|
2011-03-24 15:44:39 +01:00
|
|
|
{
|
|
|
|
|
d->m_revertChunkEnabled = e;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
bool VcsBaseEditorWidget::canApplyDiffChunk(const DiffChunk &dc) const
|
2011-03-24 15:44:39 +01:00
|
|
|
{
|
2011-08-12 13:42:21 +02:00
|
|
|
if (!dc.isValid())
|
2011-03-24 15:44:39 +01:00
|
|
|
return false;
|
|
|
|
|
const QFileInfo fi(dc.fileName);
|
|
|
|
|
// Default implementation using patch.exe relies on absolute paths.
|
|
|
|
|
return fi.isFile() && fi.isAbsolute() && fi.isWritable();
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-12 13:42:21 +02:00
|
|
|
// Default implementation of revert: Apply a chunk by piping it into patch,
|
|
|
|
|
// (passing '-R' for revert), assuming we got absolute paths from the VCS plugins.
|
2012-01-07 12:31:48 +01:00
|
|
|
bool VcsBaseEditorWidget::applyDiffChunk(const DiffChunk &dc, bool revert) const
|
2011-03-24 15:44:39 +01:00
|
|
|
{
|
2012-01-07 12:31:48 +01:00
|
|
|
return VcsBasePlugin::runPatch(dc.asPatch(), QString(), 0, revert);
|
2011-03-24 15:44:39 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::slotApplyDiffChunk()
|
2011-03-24 15:44:39 +01:00
|
|
|
{
|
|
|
|
|
const QAction *a = qobject_cast<QAction *>(sender());
|
2012-04-17 08:01:25 +02:00
|
|
|
QTC_ASSERT(a, return);
|
2011-08-12 13:42:21 +02:00
|
|
|
const Internal::DiffChunkAction chunkAction = qvariant_cast<Internal::DiffChunkAction>(a->data());
|
|
|
|
|
const QString title = chunkAction.revert ? tr("Revert Chunk") : tr("Apply Chunk");
|
|
|
|
|
const QString question = chunkAction.revert ?
|
|
|
|
|
tr("Would you like to revert the chunk?") : tr("Would you like to apply the chunk?");
|
|
|
|
|
if (QMessageBox::No == QMessageBox::question(this, title, question, QMessageBox::Yes|QMessageBox::No))
|
2011-03-24 15:44:39 +01:00
|
|
|
return;
|
|
|
|
|
|
2011-08-12 13:42:21 +02:00
|
|
|
if (applyDiffChunk(chunkAction.chunk, chunkAction.revert)) {
|
|
|
|
|
if (chunkAction.revert) {
|
|
|
|
|
emit diffChunkReverted(chunkAction.chunk);
|
|
|
|
|
} else {
|
|
|
|
|
emit diffChunkApplied(chunkAction.chunk);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-24 15:44:39 +01:00
|
|
|
}
|
|
|
|
|
|
2011-03-31 10:57:33 +02:00
|
|
|
// Tagging of editors for re-use.
|
2012-01-07 12:31:48 +01:00
|
|
|
QString VcsBaseEditorWidget::editorTag(EditorContentType t,
|
2011-03-31 10:57:33 +02:00
|
|
|
const QString &workingDirectory,
|
|
|
|
|
const QStringList &files,
|
|
|
|
|
const QString &revision)
|
|
|
|
|
{
|
|
|
|
|
const QChar colon = QLatin1Char(':');
|
|
|
|
|
QString rc = QString::number(t);
|
|
|
|
|
rc += colon;
|
|
|
|
|
if (!revision.isEmpty()) {
|
|
|
|
|
rc += revision;
|
|
|
|
|
rc += colon;
|
|
|
|
|
}
|
|
|
|
|
rc += workingDirectory;
|
|
|
|
|
if (!files.isEmpty()) {
|
|
|
|
|
rc += colon;
|
|
|
|
|
rc += files.join(QString(colon));
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
static const char tagPropertyC[] = "_q_VcsBaseEditorTag";
|
2011-03-31 10:57:33 +02:00
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
void VcsBaseEditorWidget::tagEditor(Core::IEditor *e, const QString &tag)
|
2011-03-31 10:57:33 +02:00
|
|
|
{
|
|
|
|
|
e->setProperty(tagPropertyC, QVariant(tag));
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
Core::IEditor* VcsBaseEditorWidget::locateEditorByTag(const QString &tag)
|
2011-03-31 10:57:33 +02:00
|
|
|
{
|
|
|
|
|
Core::IEditor *rc = 0;
|
|
|
|
|
foreach (Core::IEditor *ed, Core::EditorManager::instance()->openedEditors()) {
|
|
|
|
|
const QVariant tagPropertyValue = ed->property(tagPropertyC);
|
|
|
|
|
if (tagPropertyValue.type() == QVariant::String && tagPropertyValue.toString() == tag) {
|
|
|
|
|
rc = ed;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-07 12:31:48 +01:00
|
|
|
} // namespace VcsBase
|
2010-01-06 17:24:40 +01:00
|
|
|
|
|
|
|
|
#include "vcsbaseeditor.moc"
|