1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "vcsbaseeditor.h"
35 #include "diffhighlighter.h"
36 #include "baseannotationhighlighter.h"
37 #include "vcsbasetextdocument.h"
38 #include "vcsbaseconstants.h"
40 #include <coreplugin/editormanager/editormanager.h>
41 #include <coreplugin/ifile.h>
42 #include <coreplugin/iversioncontrol.h>
43 #include <coreplugin/coreconstants.h>
44 #include <coreplugin/modemanager.h>
45 #include <extensionsystem/pluginmanager.h>
46 #include <projectexplorer/editorconfiguration.h>
47 #include <projectexplorer/projectexplorer.h>
48 #include <projectexplorer/project.h>
49 #include <projectexplorer/session.h>
50 #include <texteditor/fontsettings.h>
51 #include <texteditor/texteditorconstants.h>
52 #include <utils/qtcassert.h>
54 #include <QtCore/QDebug>
55 #include <QtCore/QFileInfo>
56 #include <QtCore/QProcess>
57 #include <QtCore/QRegExp>
58 #include <QtCore/QSet>
59 #include <QtCore/QTextCodec>
60 #include <QtCore/QTextStream>
61 #include <QtGui/QTextBlock>
62 #include <QtGui/QAction>
63 #include <QtGui/QKeyEvent>
64 #include <QtGui/QLayout>
65 #include <QtGui/QMenu>
66 #include <QtGui/QTextCursor>
67 #include <QtGui/QTextEdit>
68 #include <QtGui/QComboBox>
69 #include <QtGui/QToolBar>
70 #include <QtGui/QClipboard>
71 #include <QtGui/QApplication>
75 // VCSBaseEditor: An editor with no support for duplicates.
76 // Creates a browse combo in the toolbar for diff output.
77 // It also mirrors the signals of the VCSBaseEditor since the editor
78 // manager passes the editor around.
79 class VCSBaseEditor : public TextEditor::BaseTextEditor
83 VCSBaseEditor(VCSBaseEditorWidget *,
84 const VCSBaseEditorParameters *type);
85 Core::Context context() const;
87 bool duplicateSupported() const { return false; }
88 Core::IEditor *duplicate(QWidget * /*parent*/) { return 0; }
89 QString id() const { return m_id; }
91 bool isTemporary() const { return m_temporary; }
92 void setTemporary(bool t) { m_temporary = t; }
95 void describeRequested(const QString &source, const QString &change);
96 void annotateRevisionRequested(const QString &source, const QString &change, int line);
100 Core::Context m_context;
104 VCSBaseEditor::VCSBaseEditor(VCSBaseEditorWidget *widget,
105 const VCSBaseEditorParameters *type) :
106 BaseTextEditor(widget),
108 m_context(type->context, TextEditor::Constants::C_TEXTEDITOR),
113 Core::Context VCSBaseEditor::context() const
118 // Diff editor: creates a browse combo in the toolbar for diff output.
119 class VCSBaseDiffEditor : public VCSBaseEditor
122 VCSBaseDiffEditor(VCSBaseEditorWidget *, const VCSBaseEditorParameters *type);
124 QComboBox *diffFileBrowseComboBox() const { return m_diffFileBrowseComboBox; }
127 QComboBox *m_diffFileBrowseComboBox;
130 VCSBaseDiffEditor::VCSBaseDiffEditor(VCSBaseEditorWidget *w, const VCSBaseEditorParameters *type) :
131 VCSBaseEditor(w, type),
132 m_diffFileBrowseComboBox(new QComboBox)
134 m_diffFileBrowseComboBox->setMinimumContentsLength(20);
135 // Make the combo box prefer to expand
136 QSizePolicy policy = m_diffFileBrowseComboBox->sizePolicy();
137 policy.setHorizontalPolicy(QSizePolicy::Expanding);
138 m_diffFileBrowseComboBox->setSizePolicy(policy);
140 insertExtraToolBarWidget(Left, m_diffFileBrowseComboBox);
143 // ----------- VCSBaseEditorPrivate
145 struct VCSBaseEditorWidgetPrivate
147 VCSBaseEditorWidgetPrivate(const VCSBaseEditorParameters *type);
149 const VCSBaseEditorParameters *m_parameters;
151 QString m_currentChange;
153 QString m_diffBaseDirectory;
155 QRegExp m_diffFilePattern;
156 QList<int> m_diffSections; // line number where this section starts
158 QString m_annotateRevisionTextFormat;
159 QString m_annotatePreviousRevisionTextFormat;
160 QString m_copyRevisionTextFormat;
161 bool m_fileLogAnnotateEnabled;
162 TextEditor::BaseTextEditor *m_editor;
163 QWidget *m_configurationWidget;
166 VCSBaseEditorWidgetPrivate::VCSBaseEditorWidgetPrivate(const VCSBaseEditorParameters *type) :
169 m_annotateRevisionTextFormat(VCSBaseEditorWidget::tr("Annotate \"%1\"")),
170 m_copyRevisionTextFormat(VCSBaseEditorWidget::tr("Copy \"%1\"")),
171 m_fileLogAnnotateEnabled(false),
173 m_configurationWidget(0)
177 // ------------ VCSBaseEditor
178 VCSBaseEditorWidget::VCSBaseEditorWidget(const VCSBaseEditorParameters *type, QWidget *parent)
179 : BaseTextEditorWidget(parent),
180 d(new VCSBaseEditorWidgetPrivate(type))
182 if (VCSBase::Constants::Internal::debug)
183 qDebug() << "VCSBaseEditor::VCSBaseEditor" << type->type << type->id;
185 viewport()->setMouseTracking(true);
186 setBaseTextDocument(new Internal::VCSBaseTextDocument);
187 setMimeType(QLatin1String(d->m_parameters->mimeType));
190 void VCSBaseEditorWidget::init()
192 switch (d->m_parameters->type) {
193 case RegularCommandOutput:
196 // Annotation highlighting depends on contents, which is set later on
197 connect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation()));
200 DiffHighlighter *dh = createDiffHighlighter();
201 setCodeFoldingSupported(true);
202 baseTextDocument()->setSyntaxHighlighter(dh);
203 d->m_diffFilePattern = dh->filePattern();
204 connect(this, SIGNAL(textChanged()), this, SLOT(slotPopulateDiffBrowser()));
205 connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotDiffCursorPositionChanged()));
211 VCSBaseEditorWidget::~VCSBaseEditorWidget()
216 void VCSBaseEditorWidget::setForceReadOnly(bool b)
218 Internal::VCSBaseTextDocument *vbd = qobject_cast<Internal::VCSBaseTextDocument*>(baseTextDocument());
219 VCSBaseEditor *eda = qobject_cast<VCSBaseEditor *>(editor());
220 QTC_ASSERT(vbd != 0 && eda != 0, return);
222 vbd->setForceReadOnly(b);
223 eda->setTemporary(b);
226 bool VCSBaseEditorWidget::isForceReadOnly() const
228 const Internal::VCSBaseTextDocument *vbd = qobject_cast<const Internal::VCSBaseTextDocument*>(baseTextDocument());
229 QTC_ASSERT(vbd, return false);
230 return vbd->isForceReadOnly();
233 QString VCSBaseEditorWidget::source() const
238 void VCSBaseEditorWidget::setSource(const QString &source)
240 d->m_source = source;
243 QString VCSBaseEditorWidget::annotateRevisionTextFormat() const
245 return d->m_annotateRevisionTextFormat;
248 void VCSBaseEditorWidget::setAnnotateRevisionTextFormat(const QString &f)
250 d->m_annotateRevisionTextFormat = f;
253 QString VCSBaseEditorWidget::annotatePreviousRevisionTextFormat() const
255 return d->m_annotatePreviousRevisionTextFormat;
258 void VCSBaseEditorWidget::setAnnotatePreviousRevisionTextFormat(const QString &f)
260 d->m_annotatePreviousRevisionTextFormat = f;
263 QString VCSBaseEditorWidget::copyRevisionTextFormat() const
265 return d->m_copyRevisionTextFormat;
268 void VCSBaseEditorWidget::setCopyRevisionTextFormat(const QString &f)
270 d->m_copyRevisionTextFormat = f;
273 bool VCSBaseEditorWidget::isFileLogAnnotateEnabled() const
275 return d->m_fileLogAnnotateEnabled;
278 void VCSBaseEditorWidget::setFileLogAnnotateEnabled(bool e)
280 d->m_fileLogAnnotateEnabled = e;
283 QString VCSBaseEditorWidget::diffBaseDirectory() const
285 return d->m_diffBaseDirectory;
288 void VCSBaseEditorWidget::setDiffBaseDirectory(const QString &bd)
290 d->m_diffBaseDirectory = bd;
293 QTextCodec *VCSBaseEditorWidget::codec() const
295 return baseTextDocument()->codec();
298 void VCSBaseEditorWidget::setCodec(QTextCodec *c)
301 baseTextDocument()->setCodec(c);
303 qWarning("%s: Attempt to set 0 codec.", Q_FUNC_INFO);
307 EditorContentType VCSBaseEditorWidget::contentType() const
309 return d->m_parameters->type;
312 bool VCSBaseEditorWidget::isModified() const
317 TextEditor::BaseTextEditor *VCSBaseEditorWidget::createEditor()
319 TextEditor::BaseTextEditor *editor = 0;
320 if (d->m_parameters->type == DiffOutput) {
321 // Diff: set up diff file browsing
322 VCSBaseDiffEditor *de = new VCSBaseDiffEditor(this, d->m_parameters);
323 QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
324 connect(diffBrowseComboBox, SIGNAL(activated(int)), this, SLOT(slotDiffBrowse(int)));
327 editor = new VCSBaseEditor(this, d->m_parameters);
329 d->m_editor = editor;
332 connect(this, SIGNAL(describeRequested(QString,QString)),
333 editor, SIGNAL(describeRequested(QString,QString)));
334 connect(this, SIGNAL(annotateRevisionRequested(QString,QString,int)),
335 editor, SIGNAL(annotateRevisionRequested(QString,QString,int)));
339 void VCSBaseEditorWidget::slotPopulateDiffBrowser()
341 VCSBaseDiffEditor *de = static_cast<VCSBaseDiffEditor*>(editor());
342 QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
343 diffBrowseComboBox->clear();
344 d->m_diffSections.clear();
345 // Create a list of section line numbers (diffed files)
346 // and populate combo with filenames.
347 const QTextBlock cend = document()->end();
349 QString lastFileName;
350 for (QTextBlock it = document()->begin(); it != cend; it = it.next(), lineNumber++) {
351 const QString text = it.text();
352 // Check for a new diff section (not repeating the last filename)
353 if (d->m_diffFilePattern.exactMatch(text)) {
354 const QString file = fileNameFromDiffSpecification(it);
355 if (!file.isEmpty() && lastFileName != file) {
357 // ignore any headers
358 d->m_diffSections.push_back(d->m_diffSections.empty() ? 0 : lineNumber);
359 diffBrowseComboBox->addItem(QFileInfo(file).fileName());
365 void VCSBaseEditorWidget::slotDiffBrowse(int index)
367 // goto diffed file as indicated by index/line number
368 if (index < 0 || index >= d->m_diffSections.size())
370 const int lineNumber = d->m_diffSections.at(index) + 1; // TextEdit uses 1..n convention
371 // check if we need to do something, especially to avoid messing up navigation history
372 int currentLine, currentColumn;
373 convertPosition(position(), ¤tLine, ¤tColumn);
374 if (lineNumber != currentLine) {
375 Core::EditorManager *editorManager = Core::EditorManager::instance();
376 editorManager->addCurrentPositionToNavigationHistory();
377 gotoLine(lineNumber, 0);
381 // Locate a line number in the list of diff sections.
382 static int sectionOfLine(int line, const QList<int> §ions)
384 const int sectionCount = sections.size();
387 // The section at s indicates where the section begins.
388 for (int s = 0; s < sectionCount; s++) {
389 if (line < sections.at(s))
392 return sectionCount - 1;
395 void VCSBaseEditorWidget::slotDiffCursorPositionChanged()
397 // Adapt diff file browse combo to new position
398 // if the cursor goes across a file line.
399 QTC_ASSERT(d->m_parameters->type == DiffOutput, return)
400 const int newCursorLine = textCursor().blockNumber();
401 if (newCursorLine == d->m_cursorLine)
403 // Which section does it belong to?
404 d->m_cursorLine = newCursorLine;
405 const int section = sectionOfLine(d->m_cursorLine, d->m_diffSections);
407 VCSBaseDiffEditor *de = static_cast<VCSBaseDiffEditor*>(editor());
408 QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
409 if (diffBrowseComboBox->currentIndex() != section) {
410 const bool blocked = diffBrowseComboBox->blockSignals(true);
411 diffBrowseComboBox->setCurrentIndex(section);
412 diffBrowseComboBox->blockSignals(blocked);
417 QAction *VCSBaseEditorWidget::createDescribeAction(const QString &change)
419 QAction *a = new QAction(tr("Describe change %1").arg(change), 0);
420 connect(a, SIGNAL(triggered()), this, SLOT(describe()));
424 QAction *VCSBaseEditorWidget::createAnnotateAction(const QString &change, bool previous)
426 // Use 'previous' format if desired and available, else default to standard.
427 const QString &format = previous && !d->m_annotatePreviousRevisionTextFormat.isEmpty() ?
428 d->m_annotatePreviousRevisionTextFormat : d->m_annotateRevisionTextFormat;
429 QAction *a = new QAction(format.arg(change), 0);
431 connect(a, SIGNAL(triggered()), this, SLOT(slotAnnotateRevision()));
435 QAction *VCSBaseEditorWidget::createCopyRevisionAction(const QString &change)
437 QAction *a = new QAction(d->m_copyRevisionTextFormat.arg(change), 0);
439 connect(a, SIGNAL(triggered()), this, SLOT(slotCopyRevision()));
443 void VCSBaseEditorWidget::contextMenuEvent(QContextMenuEvent *e)
445 QMenu *menu = createStandardContextMenu();
446 // 'click on change-interaction'
447 if (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput) {
448 d->m_currentChange = changeUnderCursor(cursorForPosition(e->pos()));
449 if (!d->m_currentChange.isEmpty()) {
450 switch (d->m_parameters->type) {
451 case LogOutput: // Describe current / Annotate file of current
452 menu->addSeparator();
453 menu->addAction(createCopyRevisionAction(d->m_currentChange));
454 menu->addAction(createDescribeAction(d->m_currentChange));
455 if (d->m_fileLogAnnotateEnabled)
456 menu->addAction(createAnnotateAction(d->m_currentChange, false));
458 case AnnotateOutput: { // Describe current / annotate previous
459 menu->addSeparator();
460 menu->addAction(createCopyRevisionAction(d->m_currentChange));
461 menu->addAction(createDescribeAction(d->m_currentChange));
462 const QStringList previousVersions = annotationPreviousVersions(d->m_currentChange);
463 if (!previousVersions.isEmpty()) {
464 menu->addSeparator();
465 foreach(const QString &pv, previousVersions)
466 menu->addAction(createAnnotateAction(pv, true));
467 } // has previous versions
473 } // has current change
475 menu->exec(e->globalPos());
479 void VCSBaseEditorWidget::mouseMoveEvent(QMouseEvent *e)
481 bool overrideCursor = false;
482 Qt::CursorShape cursorShape;
484 if (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput) {
485 // Link emulation behaviour for 'click on change-interaction'
486 QTextCursor cursor = cursorForPosition(e->pos());
487 QString change = changeUnderCursor(cursor);
488 if (!change.isEmpty()) {
489 QTextEdit::ExtraSelection sel;
491 sel.cursor.select(QTextCursor::WordUnderCursor);
492 sel.format.setFontUnderline(true);
493 sel.format.setProperty(QTextFormat::UserProperty, change);
494 setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
495 overrideCursor = true;
496 cursorShape = Qt::PointingHandCursor;
499 setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
500 overrideCursor = true;
501 cursorShape = Qt::IBeamCursor;
503 TextEditor::BaseTextEditorWidget::mouseMoveEvent(e);
506 viewport()->setCursor(cursorShape);
509 void VCSBaseEditorWidget::mouseReleaseEvent(QMouseEvent *e)
511 if (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput) {
512 if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) {
513 QTextCursor cursor = cursorForPosition(e->pos());
514 d->m_currentChange = changeUnderCursor(cursor);
515 if (!d->m_currentChange.isEmpty()) {
522 TextEditor::BaseTextEditorWidget::mouseReleaseEvent(e);
525 void VCSBaseEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
527 if (d->m_parameters->type == DiffOutput) {
528 if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) {
529 QTextCursor cursor = cursorForPosition(e->pos());
530 jumpToChangeFromDiff(cursor);
533 TextEditor::BaseTextEditorWidget::mouseDoubleClickEvent(e);
536 void VCSBaseEditorWidget::keyPressEvent(QKeyEvent *e)
538 // Do not intercept return in editable patches.
539 if (d->m_parameters->type == DiffOutput && isReadOnly()
540 && (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)) {
541 jumpToChangeFromDiff(textCursor());
544 BaseTextEditorWidget::keyPressEvent(e);
547 void VCSBaseEditorWidget::describe()
549 if (VCSBase::Constants::Internal::debug)
550 qDebug() << "VCSBaseEditor::describe" << d->m_currentChange;
551 if (!d->m_currentChange.isEmpty())
552 emit describeRequested(d->m_source, d->m_currentChange);
555 void VCSBaseEditorWidget::slotActivateAnnotation()
557 // The annotation highlighting depends on contents (change number
558 // set with assigned colors)
559 if (d->m_parameters->type != AnnotateOutput)
562 const QSet<QString> changes = annotationChanges();
563 if (changes.isEmpty())
565 if (VCSBase::Constants::Internal::debug)
566 qDebug() << "VCSBaseEditor::slotActivateAnnotation(): #" << changes.size();
568 disconnect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation()));
570 if (BaseAnnotationHighlighter *ah = qobject_cast<BaseAnnotationHighlighter *>(baseTextDocument()->syntaxHighlighter())) {
571 ah->setChangeNumbers(changes);
574 baseTextDocument()->setSyntaxHighlighter(createAnnotationHighlighter(changes));
578 // Check for a change chunk "@@ -91,7 +95,7 @@" and return
579 // the modified line number (95).
580 // Note that git appends stuff after " @@" (function names, etc.).
581 static inline bool checkChunkLine(const QString &line, int *modifiedLineNumber)
583 if (!line.startsWith(QLatin1String("@@ ")))
585 const int endPos = line.indexOf(QLatin1String(" @@"), 3);
588 // the first chunk range applies to the original file, the second one to
589 // the modified file, the one we're interested int
590 const int plusPos = line.indexOf(QLatin1Char('+'), 3);
591 if (plusPos == -1 || plusPos > endPos)
593 const int lineNumberPos = plusPos + 1;
594 const int commaPos = line.indexOf(QLatin1Char(','), lineNumberPos);
595 if (commaPos == -1 || commaPos > endPos)
597 const QString lineNumberStr = line.mid(lineNumberPos, commaPos - lineNumberPos);
599 *modifiedLineNumber = lineNumberStr.toInt(&ok);
603 void VCSBaseEditorWidget::jumpToChangeFromDiff(QTextCursor cursor)
607 const QChar deletionIndicator = QLatin1Char('-');
608 // find nearest change hunk
609 QTextBlock block = cursor.block();
610 for ( ; block.isValid() ; block = block.previous()) {
611 const QString line = block.text();
612 if (checkChunkLine(line, &chunkStart)) {
615 if (!line.startsWith(deletionIndicator))
620 if (VCSBase::Constants::Internal::debug)
621 qDebug() << "VCSBaseEditor::jumpToChangeFromDiff()1" << chunkStart << lineCount;
623 if (chunkStart == -1 || lineCount < 0 || !block.isValid())
626 // find the filename in previous line, map depot name back
627 block = block.previous();
628 if (!block.isValid())
630 const QString fileName = fileNameFromDiffSpecification(block);
632 const bool exists = fileName.isEmpty() ? false : QFile::exists(fileName);
634 if (VCSBase::Constants::Internal::debug)
635 qDebug() << "VCSBaseEditor::jumpToChangeFromDiff()2" << fileName << "ex=" << exists << "line" << chunkStart << lineCount;
640 Core::EditorManager *em = Core::EditorManager::instance();
641 Core::IEditor *ed = em->openEditor(fileName, QString(), Core::EditorManager::ModeSwitch);
642 if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ed))
643 editor->gotoLine(chunkStart + lineCount);
646 void VCSBaseEditorWidget::setPlainTextData(const QByteArray &data)
648 if (data.size() > Core::EditorManager::maxTextFileSize()) {
649 setPlainText(msgTextTooLarge(data.size()));
651 setPlainText(codec()->toUnicode(data));
655 void VCSBaseEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
657 TextEditor::BaseTextEditorWidget::setFontSettings(fs);
658 if (d->m_parameters->type == DiffOutput) {
659 if (DiffHighlighter *highlighter = qobject_cast<DiffHighlighter*>(baseTextDocument()->syntaxHighlighter())) {
660 static QVector<QString> categories;
661 if (categories.isEmpty()) {
662 categories << QLatin1String(TextEditor::Constants::C_TEXT)
663 << QLatin1String(TextEditor::Constants::C_ADDED_LINE)
664 << QLatin1String(TextEditor::Constants::C_REMOVED_LINE)
665 << QLatin1String(TextEditor::Constants::C_DIFF_FILE)
666 << QLatin1String(TextEditor::Constants::C_DIFF_LOCATION);
668 highlighter->setFormats(fs.toTextCharFormats(categories));
669 highlighter->rehighlight();
674 const VCSBaseEditorParameters *VCSBaseEditorWidget::findType(const VCSBaseEditorParameters *array,
676 EditorContentType et)
678 for (int i = 0; i < arraySize; i++)
679 if (array[i].type == et)
684 // Find the codec used for a file querying the editor.
685 static QTextCodec *findFileCodec(const QString &source)
687 typedef QList<Core::IEditor *> EditorList;
689 const EditorList editors = Core::EditorManager::instance()->editorsForFileName(source);
690 if (!editors.empty()) {
691 const EditorList::const_iterator ecend = editors.constEnd();
692 for (EditorList::const_iterator it = editors.constBegin(); it != ecend; ++it)
693 if (const TextEditor::BaseTextEditor *be = qobject_cast<const TextEditor::BaseTextEditor *>(*it)) {
694 QTextCodec *codec = be->editorWidget()->textCodec();
695 if (VCSBase::Constants::Internal::debug)
696 qDebug() << Q_FUNC_INFO << source << codec->name();
700 if (VCSBase::Constants::Internal::debug)
701 qDebug() << Q_FUNC_INFO << source << "not found";
705 // Find the codec by checking the projects (root dir of project file)
706 static QTextCodec *findProjectCodec(const QString &dir)
708 typedef QList<ProjectExplorer::Project*> ProjectList;
709 // Try to find a project under which file tree the file is.
710 const ProjectExplorer::SessionManager *sm = ProjectExplorer::ProjectExplorerPlugin::instance()->session();
711 const ProjectList projects = sm->projects();
712 if (!projects.empty()) {
713 const ProjectList::const_iterator pcend = projects.constEnd();
714 for (ProjectList::const_iterator it = projects.constBegin(); it != pcend; ++it)
715 if (const Core::IFile *file = (*it)->file())
716 if (file->fileName().startsWith(dir)) {
717 QTextCodec *codec = (*it)->editorConfiguration()->textCodec();
718 if (VCSBase::Constants::Internal::debug)
719 qDebug() << Q_FUNC_INFO << dir << (*it)->displayName() << codec->name();
723 if (VCSBase::Constants::Internal::debug)
724 qDebug() << Q_FUNC_INFO << dir << "not found";
728 QTextCodec *VCSBaseEditorWidget::getCodec(const QString &source)
730 if (!source.isEmpty()) {
732 const QFileInfo sourceFi(source);
733 if (sourceFi.isFile())
734 if (QTextCodec *fc = findFileCodec(source))
736 // Find by project via directory
737 if (QTextCodec *pc = findProjectCodec(sourceFi.isFile() ? sourceFi.absolutePath() : source))
740 QTextCodec *sys = QTextCodec::codecForLocale();
741 if (VCSBase::Constants::Internal::debug)
742 qDebug() << Q_FUNC_INFO << source << "defaulting to " << sys->name();
746 QTextCodec *VCSBaseEditorWidget::getCodec(const QString &workingDirectory, const QStringList &files)
749 return getCodec(workingDirectory);
750 return getCodec(workingDirectory + QLatin1Char('/') + files.front());
753 VCSBaseEditorWidget *VCSBaseEditorWidget::getVcsBaseEditor(const Core::IEditor *editor)
755 if (const TextEditor::BaseTextEditor *be = qobject_cast<const TextEditor::BaseTextEditor *>(editor))
756 return qobject_cast<VCSBaseEditorWidget *>(be->editorWidget());
760 // Return line number of current editor if it matches.
761 int VCSBaseEditorWidget::lineNumberOfCurrentEditor(const QString ¤tFile)
763 Core::IEditor *ed = Core::EditorManager::instance()->currentEditor();
766 if (!currentFile.isEmpty()) {
767 const Core::IFile *ifile = ed->file();
768 if (!ifile || ifile->fileName() != currentFile)
771 const TextEditor::BaseTextEditor *eda = qobject_cast<const TextEditor::BaseTextEditor *>(ed);
774 return eda->currentLine();
777 bool VCSBaseEditorWidget::gotoLineOfEditor(Core::IEditor *e, int lineNumber)
779 if (lineNumber >= 0 && e) {
780 if (TextEditor::BaseTextEditor *be = qobject_cast<TextEditor::BaseTextEditor*>(e)) {
781 be->gotoLine(lineNumber);
788 // Return source file or directory string depending on parameters
789 // ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file').
790 QString VCSBaseEditorWidget::getSource(const QString &workingDirectory,
791 const QString &fileName)
793 if (fileName.isEmpty())
794 return workingDirectory;
796 QString rc = workingDirectory;
797 const QChar slash = QLatin1Char('/');
798 if (!rc.isEmpty() && !(rc.endsWith(slash) || rc.endsWith(QLatin1Char('\\'))))
804 QString VCSBaseEditorWidget::getSource(const QString &workingDirectory,
805 const QStringList &fileNames)
807 return fileNames.size() == 1 ?
808 getSource(workingDirectory, fileNames.front()) :
812 QString VCSBaseEditorWidget::getTitleId(const QString &workingDirectory,
813 const QStringList &fileNames,
814 const QString &revision)
817 switch (fileNames.size()) {
819 rc = workingDirectory;
822 rc = fileNames.front();
825 rc = fileNames.join(QLatin1String(", "));
828 if (!revision.isEmpty()) {
829 rc += QLatin1Char(':');
835 bool VCSBaseEditorWidget::setConfigurationWidget(QWidget *w)
837 if (!d->m_editor || d->m_configurationWidget)
840 d->m_configurationWidget = w;
841 d->m_editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Right, w);
846 QWidget *VCSBaseEditorWidget::configurationWidget() const
848 return d->m_configurationWidget;
851 // Find the complete file from a diff relative specification.
852 QString VCSBaseEditorWidget::findDiffFile(const QString &f, Core::IVersionControl *control /* = 0 */) const
854 // Try the file itself, expand to absolute.
855 const QFileInfo in(f);
857 return in.isFile() ? f : QString();
859 return in.absoluteFilePath();
861 const QChar slash = QLatin1Char('/');
862 if (!d->m_diffBaseDirectory.isEmpty()) {
863 const QFileInfo baseFileInfo(d->m_diffBaseDirectory + slash + f);
864 if (baseFileInfo.isFile())
865 return baseFileInfo.absoluteFilePath();
867 // 2) Try in source (which can be file or directory)
868 if (source().isEmpty())
870 const QFileInfo sourceInfo(source());
871 const QString sourceDir = sourceInfo.isDir() ? sourceInfo.absoluteFilePath() : sourceInfo.absolutePath();
872 const QFileInfo sourceFileInfo(sourceDir + slash + f);
873 if (sourceFileInfo.isFile())
874 return sourceFileInfo.absoluteFilePath();
875 // Try to locate via repository.
879 if (!control->managesDirectory(sourceDir, &topLevel))
881 const QFileInfo topLevelFileInfo(topLevel + slash + f);
882 if (topLevelFileInfo.isFile())
883 return topLevelFileInfo.absoluteFilePath();
887 void VCSBaseEditorWidget::slotAnnotateRevision()
889 if (const QAction *a = qobject_cast<const QAction *>(sender()))
890 emit annotateRevisionRequested(source(), a->data().toString(),
891 editor()->currentLine());
894 void VCSBaseEditorWidget::slotCopyRevision()
896 QApplication::clipboard()->setText(d->m_currentChange);
899 QStringList VCSBaseEditorWidget::annotationPreviousVersions(const QString &) const
901 return QStringList();
904 } // namespace VCSBase
906 #include "vcsbaseeditor.moc"