1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "vcsbaseeditor.h"
34 #include "diffhighlighter.h"
35 #include "baseannotationhighlighter.h"
36 #include "vcsbasetextdocument.h"
37 #include "vcsbaseconstants.h"
39 #include <coreplugin/editormanager/editormanager.h>
40 #include <coreplugin/ifile.h>
41 #include <coreplugin/iversioncontrol.h>
42 #include <coreplugin/coreconstants.h>
43 #include <coreplugin/modemanager.h>
44 #include <extensionsystem/pluginmanager.h>
45 #include <projectexplorer/editorconfiguration.h>
46 #include <projectexplorer/projectexplorer.h>
47 #include <projectexplorer/project.h>
48 #include <projectexplorer/session.h>
49 #include <texteditor/fontsettings.h>
50 #include <texteditor/texteditorconstants.h>
51 #include <utils/qtcassert.h>
53 #include <QtCore/QDebug>
54 #include <QtCore/QFileInfo>
55 #include <QtCore/QProcess>
56 #include <QtCore/QRegExp>
57 #include <QtCore/QSet>
58 #include <QtCore/QTextCodec>
59 #include <QtCore/QTextStream>
60 #include <QtGui/QTextBlock>
61 #include <QtGui/QAction>
62 #include <QtGui/QKeyEvent>
63 #include <QtGui/QLayout>
64 #include <QtGui/QMenu>
65 #include <QtGui/QTextCursor>
66 #include <QtGui/QTextEdit>
67 #include <QtGui/QComboBox>
68 #include <QtGui/QToolBar>
69 #include <QtGui/QClipboard>
70 #include <QtGui/QApplication>
74 // VCSBaseEditor: An editor with no support for duplicates.
75 // Creates a browse combo in the toolbar for diff output.
76 // It also mirrors the signals of the VCSBaseEditor since the editor
77 // manager passes the editor around.
78 class VCSBaseEditor : public TextEditor::BaseTextEditor
82 VCSBaseEditor(VCSBaseEditorWidget *,
83 const VCSBaseEditorParameters *type);
84 Core::Context context() const;
86 bool duplicateSupported() const { return false; }
87 Core::IEditor *duplicate(QWidget * /*parent*/) { return 0; }
88 QString id() const { return m_id; }
90 bool isTemporary() const { return m_temporary; }
91 void setTemporary(bool t) { m_temporary = t; }
94 void describeRequested(const QString &source, const QString &change);
95 void annotateRevisionRequested(const QString &source, const QString &change, int line);
99 Core::Context m_context;
103 VCSBaseEditor::VCSBaseEditor(VCSBaseEditorWidget *widget,
104 const VCSBaseEditorParameters *type) :
105 BaseTextEditor(widget),
107 m_context(type->context, TextEditor::Constants::C_TEXTEDITOR),
112 Core::Context VCSBaseEditor::context() const
117 // Diff editor: creates a browse combo in the toolbar for diff output.
118 class VCSBaseDiffEditor : public VCSBaseEditor
121 VCSBaseDiffEditor(VCSBaseEditorWidget *, const VCSBaseEditorParameters *type);
123 QComboBox *diffFileBrowseComboBox() const { return m_diffFileBrowseComboBox; }
126 QComboBox *m_diffFileBrowseComboBox;
129 VCSBaseDiffEditor::VCSBaseDiffEditor(VCSBaseEditorWidget *w, const VCSBaseEditorParameters *type) :
130 VCSBaseEditor(w, type),
131 m_diffFileBrowseComboBox(new QComboBox)
133 m_diffFileBrowseComboBox->setMinimumContentsLength(20);
134 // Make the combo box prefer to expand
135 QSizePolicy policy = m_diffFileBrowseComboBox->sizePolicy();
136 policy.setHorizontalPolicy(QSizePolicy::Expanding);
137 m_diffFileBrowseComboBox->setSizePolicy(policy);
139 insertExtraToolBarWidget(Left, m_diffFileBrowseComboBox);
142 // ----------- VCSBaseEditorPrivate
144 struct VCSBaseEditorWidgetPrivate
146 VCSBaseEditorWidgetPrivate(const VCSBaseEditorParameters *type);
148 const VCSBaseEditorParameters *m_parameters;
150 QString m_currentChange;
152 QString m_diffBaseDirectory;
154 QRegExp m_diffFilePattern;
155 QList<int> m_diffSections; // line number where this section starts
157 QString m_annotateRevisionTextFormat;
158 QString m_annotatePreviousRevisionTextFormat;
159 QString m_copyRevisionTextFormat;
160 bool m_fileLogAnnotateEnabled;
161 TextEditor::BaseTextEditor *m_editor;
162 QWidget *m_configurationWidget;
165 VCSBaseEditorWidgetPrivate::VCSBaseEditorWidgetPrivate(const VCSBaseEditorParameters *type) :
168 m_annotateRevisionTextFormat(VCSBaseEditorWidget::tr("Annotate \"%1\"")),
169 m_copyRevisionTextFormat(VCSBaseEditorWidget::tr("Copy \"%1\"")),
170 m_fileLogAnnotateEnabled(false),
172 m_configurationWidget(0)
176 // ------------ VCSBaseEditor
177 VCSBaseEditorWidget::VCSBaseEditorWidget(const VCSBaseEditorParameters *type, QWidget *parent)
178 : BaseTextEditorWidget(parent),
179 d(new VCSBaseEditorWidgetPrivate(type))
181 if (VCSBase::Constants::Internal::debug)
182 qDebug() << "VCSBaseEditor::VCSBaseEditor" << type->type << type->id;
184 viewport()->setMouseTracking(true);
185 setBaseTextDocument(new Internal::VCSBaseTextDocument);
186 setMimeType(QLatin1String(d->m_parameters->mimeType));
189 void VCSBaseEditorWidget::init()
191 switch (d->m_parameters->type) {
192 case RegularCommandOutput:
195 // Annotation highlighting depends on contents, which is set later on
196 connect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation()));
199 DiffHighlighter *dh = createDiffHighlighter();
200 setCodeFoldingSupported(true);
201 baseTextDocument()->setSyntaxHighlighter(dh);
202 d->m_diffFilePattern = dh->filePattern();
203 connect(this, SIGNAL(textChanged()), this, SLOT(slotPopulateDiffBrowser()));
204 connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotDiffCursorPositionChanged()));
210 VCSBaseEditorWidget::~VCSBaseEditorWidget()
215 void VCSBaseEditorWidget::setForceReadOnly(bool b)
217 Internal::VCSBaseTextDocument *vbd = qobject_cast<Internal::VCSBaseTextDocument*>(baseTextDocument());
218 VCSBaseEditor *eda = qobject_cast<VCSBaseEditor *>(editor());
219 QTC_ASSERT(vbd != 0 && eda != 0, return);
221 vbd->setForceReadOnly(b);
222 eda->setTemporary(b);
225 bool VCSBaseEditorWidget::isForceReadOnly() const
227 const Internal::VCSBaseTextDocument *vbd = qobject_cast<const Internal::VCSBaseTextDocument*>(baseTextDocument());
228 QTC_ASSERT(vbd, return false);
229 return vbd->isForceReadOnly();
232 QString VCSBaseEditorWidget::source() const
237 void VCSBaseEditorWidget::setSource(const QString &source)
239 d->m_source = source;
242 QString VCSBaseEditorWidget::annotateRevisionTextFormat() const
244 return d->m_annotateRevisionTextFormat;
247 void VCSBaseEditorWidget::setAnnotateRevisionTextFormat(const QString &f)
249 d->m_annotateRevisionTextFormat = f;
252 QString VCSBaseEditorWidget::annotatePreviousRevisionTextFormat() const
254 return d->m_annotatePreviousRevisionTextFormat;
257 void VCSBaseEditorWidget::setAnnotatePreviousRevisionTextFormat(const QString &f)
259 d->m_annotatePreviousRevisionTextFormat = f;
262 QString VCSBaseEditorWidget::copyRevisionTextFormat() const
264 return d->m_copyRevisionTextFormat;
267 void VCSBaseEditorWidget::setCopyRevisionTextFormat(const QString &f)
269 d->m_copyRevisionTextFormat = f;
272 bool VCSBaseEditorWidget::isFileLogAnnotateEnabled() const
274 return d->m_fileLogAnnotateEnabled;
277 void VCSBaseEditorWidget::setFileLogAnnotateEnabled(bool e)
279 d->m_fileLogAnnotateEnabled = e;
282 QString VCSBaseEditorWidget::diffBaseDirectory() const
284 return d->m_diffBaseDirectory;
287 void VCSBaseEditorWidget::setDiffBaseDirectory(const QString &bd)
289 d->m_diffBaseDirectory = bd;
292 QTextCodec *VCSBaseEditorWidget::codec() const
294 return baseTextDocument()->codec();
297 void VCSBaseEditorWidget::setCodec(QTextCodec *c)
300 baseTextDocument()->setCodec(c);
302 qWarning("%s: Attempt to set 0 codec.", Q_FUNC_INFO);
306 EditorContentType VCSBaseEditorWidget::contentType() const
308 return d->m_parameters->type;
311 bool VCSBaseEditorWidget::isModified() const
316 TextEditor::BaseTextEditor *VCSBaseEditorWidget::createEditor()
318 TextEditor::BaseTextEditor *editor = 0;
319 if (d->m_parameters->type == DiffOutput) {
320 // Diff: set up diff file browsing
321 VCSBaseDiffEditor *de = new VCSBaseDiffEditor(this, d->m_parameters);
322 QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
323 connect(diffBrowseComboBox, SIGNAL(activated(int)), this, SLOT(slotDiffBrowse(int)));
326 editor = new VCSBaseEditor(this, d->m_parameters);
328 d->m_editor = editor;
331 connect(this, SIGNAL(describeRequested(QString,QString)),
332 editor, SIGNAL(describeRequested(QString,QString)));
333 connect(this, SIGNAL(annotateRevisionRequested(QString,QString,int)),
334 editor, SIGNAL(annotateRevisionRequested(QString,QString,int)));
338 void VCSBaseEditorWidget::slotPopulateDiffBrowser()
340 VCSBaseDiffEditor *de = static_cast<VCSBaseDiffEditor*>(editor());
341 QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
342 diffBrowseComboBox->clear();
343 d->m_diffSections.clear();
344 // Create a list of section line numbers (diffed files)
345 // and populate combo with filenames.
346 const QTextBlock cend = document()->end();
348 QString lastFileName;
349 for (QTextBlock it = document()->begin(); it != cend; it = it.next(), lineNumber++) {
350 const QString text = it.text();
351 // Check for a new diff section (not repeating the last filename)
352 if (d->m_diffFilePattern.exactMatch(text)) {
353 const QString file = fileNameFromDiffSpecification(it);
354 if (!file.isEmpty() && lastFileName != file) {
356 // ignore any headers
357 d->m_diffSections.push_back(d->m_diffSections.empty() ? 0 : lineNumber);
358 diffBrowseComboBox->addItem(QFileInfo(file).fileName());
364 void VCSBaseEditorWidget::slotDiffBrowse(int index)
366 // goto diffed file as indicated by index/line number
367 if (index < 0 || index >= d->m_diffSections.size())
369 const int lineNumber = d->m_diffSections.at(index) + 1; // TextEdit uses 1..n convention
370 // check if we need to do something, especially to avoid messing up navigation history
371 int currentLine, currentColumn;
372 convertPosition(position(), ¤tLine, ¤tColumn);
373 if (lineNumber != currentLine) {
374 Core::EditorManager *editorManager = Core::EditorManager::instance();
375 editorManager->addCurrentPositionToNavigationHistory();
376 gotoLine(lineNumber, 0);
380 // Locate a line number in the list of diff sections.
381 static int sectionOfLine(int line, const QList<int> §ions)
383 const int sectionCount = sections.size();
386 // The section at s indicates where the section begins.
387 for (int s = 0; s < sectionCount; s++) {
388 if (line < sections.at(s))
391 return sectionCount - 1;
394 void VCSBaseEditorWidget::slotDiffCursorPositionChanged()
396 // Adapt diff file browse combo to new position
397 // if the cursor goes across a file line.
398 QTC_ASSERT(d->m_parameters->type == DiffOutput, return)
399 const int newCursorLine = textCursor().blockNumber();
400 if (newCursorLine == d->m_cursorLine)
402 // Which section does it belong to?
403 d->m_cursorLine = newCursorLine;
404 const int section = sectionOfLine(d->m_cursorLine, d->m_diffSections);
406 VCSBaseDiffEditor *de = static_cast<VCSBaseDiffEditor*>(editor());
407 QComboBox *diffBrowseComboBox = de->diffFileBrowseComboBox();
408 if (diffBrowseComboBox->currentIndex() != section) {
409 const bool blocked = diffBrowseComboBox->blockSignals(true);
410 diffBrowseComboBox->setCurrentIndex(section);
411 diffBrowseComboBox->blockSignals(blocked);
416 QAction *VCSBaseEditorWidget::createDescribeAction(const QString &change)
418 QAction *a = new QAction(tr("Describe change %1").arg(change), 0);
419 connect(a, SIGNAL(triggered()), this, SLOT(describe()));
423 QAction *VCSBaseEditorWidget::createAnnotateAction(const QString &change, bool previous)
425 // Use 'previous' format if desired and available, else default to standard.
426 const QString &format = previous && !d->m_annotatePreviousRevisionTextFormat.isEmpty() ?
427 d->m_annotatePreviousRevisionTextFormat : d->m_annotateRevisionTextFormat;
428 QAction *a = new QAction(format.arg(change), 0);
430 connect(a, SIGNAL(triggered()), this, SLOT(slotAnnotateRevision()));
434 QAction *VCSBaseEditorWidget::createCopyRevisionAction(const QString &change)
436 QAction *a = new QAction(d->m_copyRevisionTextFormat.arg(change), 0);
438 connect(a, SIGNAL(triggered()), this, SLOT(slotCopyRevision()));
442 void VCSBaseEditorWidget::contextMenuEvent(QContextMenuEvent *e)
444 QMenu *menu = createStandardContextMenu();
445 // 'click on change-interaction'
446 if (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput) {
447 d->m_currentChange = changeUnderCursor(cursorForPosition(e->pos()));
448 if (!d->m_currentChange.isEmpty()) {
449 switch (d->m_parameters->type) {
450 case LogOutput: // Describe current / Annotate file of current
451 menu->addSeparator();
452 menu->addAction(createCopyRevisionAction(d->m_currentChange));
453 menu->addAction(createDescribeAction(d->m_currentChange));
454 if (d->m_fileLogAnnotateEnabled)
455 menu->addAction(createAnnotateAction(d->m_currentChange, false));
457 case AnnotateOutput: { // Describe current / annotate previous
458 menu->addSeparator();
459 menu->addAction(createCopyRevisionAction(d->m_currentChange));
460 menu->addAction(createDescribeAction(d->m_currentChange));
461 const QStringList previousVersions = annotationPreviousVersions(d->m_currentChange);
462 if (!previousVersions.isEmpty()) {
463 menu->addSeparator();
464 foreach(const QString &pv, previousVersions)
465 menu->addAction(createAnnotateAction(pv, true));
466 } // has previous versions
472 } // has current change
474 menu->exec(e->globalPos());
478 void VCSBaseEditorWidget::mouseMoveEvent(QMouseEvent *e)
480 bool overrideCursor = false;
481 Qt::CursorShape cursorShape;
483 if (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput) {
484 // Link emulation behaviour for 'click on change-interaction'
485 QTextCursor cursor = cursorForPosition(e->pos());
486 QString change = changeUnderCursor(cursor);
487 if (!change.isEmpty()) {
488 QTextEdit::ExtraSelection sel;
490 sel.cursor.select(QTextCursor::WordUnderCursor);
491 sel.format.setFontUnderline(true);
492 sel.format.setProperty(QTextFormat::UserProperty, change);
493 setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
494 overrideCursor = true;
495 cursorShape = Qt::PointingHandCursor;
498 setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
499 overrideCursor = true;
500 cursorShape = Qt::IBeamCursor;
502 TextEditor::BaseTextEditorWidget::mouseMoveEvent(e);
505 viewport()->setCursor(cursorShape);
508 void VCSBaseEditorWidget::mouseReleaseEvent(QMouseEvent *e)
510 if (d->m_parameters->type == LogOutput || d->m_parameters->type == AnnotateOutput) {
511 if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) {
512 QTextCursor cursor = cursorForPosition(e->pos());
513 d->m_currentChange = changeUnderCursor(cursor);
514 if (!d->m_currentChange.isEmpty()) {
521 TextEditor::BaseTextEditorWidget::mouseReleaseEvent(e);
524 void VCSBaseEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
526 if (d->m_parameters->type == DiffOutput) {
527 if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) {
528 QTextCursor cursor = cursorForPosition(e->pos());
529 jumpToChangeFromDiff(cursor);
532 TextEditor::BaseTextEditorWidget::mouseDoubleClickEvent(e);
535 void VCSBaseEditorWidget::keyPressEvent(QKeyEvent *e)
537 // Do not intercept return in editable patches.
538 if (d->m_parameters->type == DiffOutput && isReadOnly()
539 && (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)) {
540 jumpToChangeFromDiff(textCursor());
543 BaseTextEditorWidget::keyPressEvent(e);
546 void VCSBaseEditorWidget::describe()
548 if (VCSBase::Constants::Internal::debug)
549 qDebug() << "VCSBaseEditor::describe" << d->m_currentChange;
550 if (!d->m_currentChange.isEmpty())
551 emit describeRequested(d->m_source, d->m_currentChange);
554 void VCSBaseEditorWidget::slotActivateAnnotation()
556 // The annotation highlighting depends on contents (change number
557 // set with assigned colors)
558 if (d->m_parameters->type != AnnotateOutput)
561 const QSet<QString> changes = annotationChanges();
562 if (changes.isEmpty())
564 if (VCSBase::Constants::Internal::debug)
565 qDebug() << "VCSBaseEditor::slotActivateAnnotation(): #" << changes.size();
567 disconnect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation()));
569 if (BaseAnnotationHighlighter *ah = qobject_cast<BaseAnnotationHighlighter *>(baseTextDocument()->syntaxHighlighter())) {
570 ah->setChangeNumbers(changes);
573 baseTextDocument()->setSyntaxHighlighter(createAnnotationHighlighter(changes));
577 // Check for a change chunk "@@ -91,7 +95,7 @@" and return
578 // the modified line number (95).
579 // Note that git appends stuff after " @@" (function names, etc.).
580 static inline bool checkChunkLine(const QString &line, int *modifiedLineNumber)
582 if (!line.startsWith(QLatin1String("@@ ")))
584 const int endPos = line.indexOf(QLatin1String(" @@"), 3);
587 // the first chunk range applies to the original file, the second one to
588 // the modified file, the one we're interested int
589 const int plusPos = line.indexOf(QLatin1Char('+'), 3);
590 if (plusPos == -1 || plusPos > endPos)
592 const int lineNumberPos = plusPos + 1;
593 const int commaPos = line.indexOf(QLatin1Char(','), lineNumberPos);
594 if (commaPos == -1 || commaPos > endPos)
596 const QString lineNumberStr = line.mid(lineNumberPos, commaPos - lineNumberPos);
598 *modifiedLineNumber = lineNumberStr.toInt(&ok);
602 void VCSBaseEditorWidget::jumpToChangeFromDiff(QTextCursor cursor)
606 const QChar deletionIndicator = QLatin1Char('-');
607 // find nearest change hunk
608 QTextBlock block = cursor.block();
609 for ( ; block.isValid() ; block = block.previous()) {
610 const QString line = block.text();
611 if (checkChunkLine(line, &chunkStart)) {
614 if (!line.startsWith(deletionIndicator))
619 if (VCSBase::Constants::Internal::debug)
620 qDebug() << "VCSBaseEditor::jumpToChangeFromDiff()1" << chunkStart << lineCount;
622 if (chunkStart == -1 || lineCount < 0 || !block.isValid())
625 // find the filename in previous line, map depot name back
626 block = block.previous();
627 if (!block.isValid())
629 const QString fileName = fileNameFromDiffSpecification(block);
631 const bool exists = fileName.isEmpty() ? false : QFile::exists(fileName);
633 if (VCSBase::Constants::Internal::debug)
634 qDebug() << "VCSBaseEditor::jumpToChangeFromDiff()2" << fileName << "ex=" << exists << "line" << chunkStart << lineCount;
639 Core::EditorManager *em = Core::EditorManager::instance();
640 Core::IEditor *ed = em->openEditor(fileName, QString(), Core::EditorManager::ModeSwitch);
641 if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ed))
642 editor->gotoLine(chunkStart + lineCount);
645 void VCSBaseEditorWidget::setPlainTextData(const QByteArray &data)
647 if (data.size() > Core::EditorManager::maxTextFileSize()) {
648 setPlainText(msgTextTooLarge(data.size()));
650 setPlainText(codec()->toUnicode(data));
654 void VCSBaseEditorWidget::setFontSettings(const TextEditor::FontSettings &fs)
656 TextEditor::BaseTextEditorWidget::setFontSettings(fs);
657 if (d->m_parameters->type == DiffOutput) {
658 if (DiffHighlighter *highlighter = qobject_cast<DiffHighlighter*>(baseTextDocument()->syntaxHighlighter())) {
659 static QVector<QString> categories;
660 if (categories.isEmpty()) {
661 categories << QLatin1String(TextEditor::Constants::C_TEXT)
662 << QLatin1String(TextEditor::Constants::C_ADDED_LINE)
663 << QLatin1String(TextEditor::Constants::C_REMOVED_LINE)
664 << QLatin1String(TextEditor::Constants::C_DIFF_FILE)
665 << QLatin1String(TextEditor::Constants::C_DIFF_LOCATION);
667 highlighter->setFormats(fs.toTextCharFormats(categories));
668 highlighter->rehighlight();
673 const VCSBaseEditorParameters *VCSBaseEditorWidget::findType(const VCSBaseEditorParameters *array,
675 EditorContentType et)
677 for (int i = 0; i < arraySize; i++)
678 if (array[i].type == et)
683 // Find the codec used for a file querying the editor.
684 static QTextCodec *findFileCodec(const QString &source)
686 typedef QList<Core::IEditor *> EditorList;
688 const EditorList editors = Core::EditorManager::instance()->editorsForFileName(source);
689 if (!editors.empty()) {
690 const EditorList::const_iterator ecend = editors.constEnd();
691 for (EditorList::const_iterator it = editors.constBegin(); it != ecend; ++it)
692 if (const TextEditor::BaseTextEditor *be = qobject_cast<const TextEditor::BaseTextEditor *>(*it)) {
693 QTextCodec *codec = be->editorWidget()->textCodec();
694 if (VCSBase::Constants::Internal::debug)
695 qDebug() << Q_FUNC_INFO << source << codec->name();
699 if (VCSBase::Constants::Internal::debug)
700 qDebug() << Q_FUNC_INFO << source << "not found";
704 // Find the codec by checking the projects (root dir of project file)
705 static QTextCodec *findProjectCodec(const QString &dir)
707 typedef QList<ProjectExplorer::Project*> ProjectList;
708 // Try to find a project under which file tree the file is.
709 const ProjectExplorer::SessionManager *sm = ProjectExplorer::ProjectExplorerPlugin::instance()->session();
710 const ProjectList projects = sm->projects();
711 if (!projects.empty()) {
712 const ProjectList::const_iterator pcend = projects.constEnd();
713 for (ProjectList::const_iterator it = projects.constBegin(); it != pcend; ++it)
714 if (const Core::IFile *file = (*it)->file())
715 if (file->fileName().startsWith(dir)) {
716 QTextCodec *codec = (*it)->editorConfiguration()->textCodec();
717 if (VCSBase::Constants::Internal::debug)
718 qDebug() << Q_FUNC_INFO << dir << (*it)->displayName() << codec->name();
722 if (VCSBase::Constants::Internal::debug)
723 qDebug() << Q_FUNC_INFO << dir << "not found";
727 QTextCodec *VCSBaseEditorWidget::getCodec(const QString &source)
729 if (!source.isEmpty()) {
731 const QFileInfo sourceFi(source);
732 if (sourceFi.isFile())
733 if (QTextCodec *fc = findFileCodec(source))
735 // Find by project via directory
736 if (QTextCodec *pc = findProjectCodec(sourceFi.isFile() ? sourceFi.absolutePath() : source))
739 QTextCodec *sys = QTextCodec::codecForLocale();
740 if (VCSBase::Constants::Internal::debug)
741 qDebug() << Q_FUNC_INFO << source << "defaulting to " << sys->name();
745 QTextCodec *VCSBaseEditorWidget::getCodec(const QString &workingDirectory, const QStringList &files)
748 return getCodec(workingDirectory);
749 return getCodec(workingDirectory + QLatin1Char('/') + files.front());
752 VCSBaseEditorWidget *VCSBaseEditorWidget::getVcsBaseEditor(const Core::IEditor *editor)
754 if (const TextEditor::BaseTextEditor *be = qobject_cast<const TextEditor::BaseTextEditor *>(editor))
755 return qobject_cast<VCSBaseEditorWidget *>(be->editorWidget());
759 // Return line number of current editor if it matches.
760 int VCSBaseEditorWidget::lineNumberOfCurrentEditor(const QString ¤tFile)
762 Core::IEditor *ed = Core::EditorManager::instance()->currentEditor();
765 if (!currentFile.isEmpty()) {
766 const Core::IFile *ifile = ed->file();
767 if (!ifile || ifile->fileName() != currentFile)
770 const TextEditor::BaseTextEditor *eda = qobject_cast<const TextEditor::BaseTextEditor *>(ed);
773 return eda->currentLine();
776 bool VCSBaseEditorWidget::gotoLineOfEditor(Core::IEditor *e, int lineNumber)
778 if (lineNumber >= 0 && e) {
779 if (TextEditor::BaseTextEditor *be = qobject_cast<TextEditor::BaseTextEditor*>(e)) {
780 be->gotoLine(lineNumber);
787 // Return source file or directory string depending on parameters
788 // ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file').
789 QString VCSBaseEditorWidget::getSource(const QString &workingDirectory,
790 const QString &fileName)
792 if (fileName.isEmpty())
793 return workingDirectory;
795 QString rc = workingDirectory;
796 const QChar slash = QLatin1Char('/');
797 if (!rc.isEmpty() && !(rc.endsWith(slash) || rc.endsWith(QLatin1Char('\\'))))
803 QString VCSBaseEditorWidget::getSource(const QString &workingDirectory,
804 const QStringList &fileNames)
806 return fileNames.size() == 1 ?
807 getSource(workingDirectory, fileNames.front()) :
811 QString VCSBaseEditorWidget::getTitleId(const QString &workingDirectory,
812 const QStringList &fileNames,
813 const QString &revision)
816 switch (fileNames.size()) {
818 rc = workingDirectory;
821 rc = fileNames.front();
824 rc = fileNames.join(QLatin1String(", "));
827 if (!revision.isEmpty()) {
828 rc += QLatin1Char(':');
834 bool VCSBaseEditorWidget::setConfigurationWidget(QWidget *w)
836 if (!d->m_editor || d->m_configurationWidget)
839 d->m_configurationWidget = w;
840 d->m_editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Right, w);
845 QWidget *VCSBaseEditorWidget::configurationWidget() const
847 return d->m_configurationWidget;
850 // Find the complete file from a diff relative specification.
851 QString VCSBaseEditorWidget::findDiffFile(const QString &f, Core::IVersionControl *control /* = 0 */) const
853 // Try the file itself, expand to absolute.
854 const QFileInfo in(f);
856 return in.isFile() ? f : QString();
858 return in.absoluteFilePath();
860 const QChar slash = QLatin1Char('/');
861 if (!d->m_diffBaseDirectory.isEmpty()) {
862 const QFileInfo baseFileInfo(d->m_diffBaseDirectory + slash + f);
863 if (baseFileInfo.isFile())
864 return baseFileInfo.absoluteFilePath();
866 // 2) Try in source (which can be file or directory)
867 if (source().isEmpty())
869 const QFileInfo sourceInfo(source());
870 const QString sourceDir = sourceInfo.isDir() ? sourceInfo.absoluteFilePath() : sourceInfo.absolutePath();
871 const QFileInfo sourceFileInfo(sourceDir + slash + f);
872 if (sourceFileInfo.isFile())
873 return sourceFileInfo.absoluteFilePath();
874 // Try to locate via repository.
878 if (!control->managesDirectory(sourceDir, &topLevel))
880 const QFileInfo topLevelFileInfo(topLevel + slash + f);
881 if (topLevelFileInfo.isFile())
882 return topLevelFileInfo.absoluteFilePath();
886 void VCSBaseEditorWidget::slotAnnotateRevision()
888 if (const QAction *a = qobject_cast<const QAction *>(sender()))
889 emit annotateRevisionRequested(source(), a->data().toString(),
890 editor()->currentLine());
893 void VCSBaseEditorWidget::slotCopyRevision()
895 QApplication::clipboard()->setText(d->m_currentChange);
898 QStringList VCSBaseEditorWidget::annotationPreviousVersions(const QString &) const
900 return QStringList();
903 } // namespace VCSBase
905 #include "vcsbaseeditor.moc"