1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
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 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "editormanager.h"
31 #include "editorview.h"
32 #include "openeditorswindow.h"
33 #include "openeditorsview.h"
34 #include "openeditorsmodel.h"
35 #include "openwithdialog.h"
36 #include "filemanager.h"
39 #include "iversioncontrol.h"
40 #include "mimedatabase.h"
41 #include "tabpositionindicator.h"
42 #include "vcsmanager.h"
44 #include <coreplugin/editortoolbar.h>
45 #include <coreplugin/coreconstants.h>
46 #include <coreplugin/modemanager.h>
47 #include <coreplugin/actionmanager/actionmanager.h>
48 #include <coreplugin/actionmanager/actioncontainer.h>
49 #include <coreplugin/actionmanager/command.h>
50 #include <coreplugin/editormanager/ieditorfactory.h>
51 #include <coreplugin/editormanager/iexternaleditor.h>
52 #include <coreplugin/icorelistener.h>
53 #include <coreplugin/imode.h>
54 #include <coreplugin/settingsdatabase.h>
55 #include <coreplugin/variablemanager.h>
56 #include <coreplugin/uniqueidmanager.h>
58 #include <extensionsystem/pluginmanager.h>
60 #include <utils/consoleprocess.h>
61 #include <utils/qtcassert.h>
63 #include <QtCore/QDebug>
64 #include <QtCore/QFileInfo>
65 #include <QtCore/QMap>
66 #include <QtCore/QProcess>
67 #include <QtCore/QSet>
68 #include <QtCore/QSettings>
69 #include <QtCore/QTextCodec>
71 #include <QtGui/QAction>
72 #include <QtGui/QShortcut>
73 #include <QtGui/QApplication>
74 #include <QtGui/QFileDialog>
75 #include <QtGui/QLayout>
76 #include <QtGui/QMainWindow>
77 #include <QtGui/QMenu>
78 #include <QtGui/QMessageBox>
79 #include <QtGui/QPushButton>
80 #include <QtGui/QSplitter>
81 #include <QtGui/QStackedLayout>
85 Q_DECLARE_METATYPE(Core::IEditor*)
87 enum { debugEditorManager=0 };
89 static inline ExtensionSystem::PluginManager *pluginManager()
91 return ExtensionSystem::PluginManager::instance();
94 //===================EditorClosingCoreListener======================
99 class EditorClosingCoreListener : public ICoreListener
102 EditorClosingCoreListener(EditorManager *em);
103 bool editorAboutToClose(IEditor *editor);
104 bool coreAboutToClose();
110 EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em)
115 bool EditorClosingCoreListener::editorAboutToClose(IEditor *)
120 bool EditorClosingCoreListener::coreAboutToClose()
122 // Do not ask for files to save.
123 // MainWindow::closeEvent has already done that.
124 return m_em->closeAllEditors(false);
127 } // namespace Internal
130 using namespace Core;
131 using namespace Core::Internal;
132 using namespace Utils;
134 //===================EditorManager=====================
136 EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;
138 EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
139 : QWidget(parent), m_mode(mode)
141 setLayout(new QVBoxLayout);
142 layout()->setMargin(0);
143 connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)),
144 this, SLOT(currentModeChanged(Core::IMode *)));
146 currentModeChanged(Core::ModeManager::instance()->currentMode());
149 EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
151 if (m_current == this) {
152 EditorManager::instance()->setParent(0);
153 EditorManager::instance()->hide();
157 void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
159 if (m_current == this) {
161 EditorManager::instance()->setParent(0);
162 EditorManager::instance()->hide();
164 if (m_mode == mode) {
166 layout()->addWidget(EditorManager::instance());
167 EditorManager::instance()->show();
171 EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
176 // ---------------- EditorManager
181 struct EditorManagerPrivate {
182 explicit EditorManagerPrivate(ICore *core, QWidget *parent);
183 ~EditorManagerPrivate();
184 Internal::EditorView *m_view;
185 Internal::SplitterOrView *m_splitter;
186 QPointer<IEditor> m_currentEditor;
187 QPointer<SplitterOrView> m_currentView;
193 QAction *m_revertToSavedAction;
194 QAction *m_saveAction;
195 QAction *m_saveAsAction;
196 QAction *m_closeCurrentEditorAction;
197 QAction *m_closeAllEditorsAction;
198 QAction *m_closeOtherEditorsAction;
199 QAction *m_gotoNextDocHistoryAction;
200 QAction *m_gotoPreviousDocHistoryAction;
201 QAction *m_goBackAction;
202 QAction *m_goForwardAction;
203 QAction *m_openInExternalEditorAction;
204 QAction *m_splitAction;
205 QAction *m_splitSideBySideAction;
206 QAction *m_removeCurrentSplitAction;
207 QAction *m_removeAllSplitsAction;
208 QAction *m_gotoOtherSplitAction;
210 Internal::OpenEditorsWindow *m_windowPopup;
211 Internal::EditorClosingCoreListener *m_coreListener;
213 QMap<QString, QVariant> m_editorStates;
214 Internal::OpenEditorsViewFactory *m_openEditorsFactory;
216 OpenEditorsModel *m_editorModel;
217 QString m_externalEditor;
219 IFile::ReloadSetting m_reloadSetting;
220 IFile::Utf8BomSetting m_utf8BomSetting;
224 EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
228 m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)),
229 m_saveAction(new QAction(parent)),
230 m_saveAsAction(new QAction(parent)),
231 m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)),
232 m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)),
233 m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
234 m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
235 m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
236 m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)),
237 m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)),
238 m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)),
241 m_reloadSetting(IFile::AlwaysAsk),
242 m_utf8BomSetting(IFile::OnlyKeep)
244 m_editorModel = new OpenEditorsModel(parent);
247 EditorManagerPrivate::~EditorManagerPrivate()
249 // clearNavigationHistory();
252 EditorManager *EditorManager::m_instance = 0;
254 static Command *createSeparator(ActionManager *am, QObject *parent,
256 const Context &context)
258 QAction *tmpaction = new QAction(parent);
259 tmpaction->setSeparator(true);
260 Command *cmd = am->registerAction(tmpaction, name, context);
264 EditorManager::EditorManager(ICore *core, QWidget *parent) :
266 m_d(new EditorManagerPrivate(core, parent))
270 connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)),
271 this, SLOT(handleContextChange(Core::IContext *)));
273 const Context editManagerContext(Constants::C_EDITORMANAGER);
274 // combined context for edit & design modes
275 const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
277 ActionManager *am = m_d->m_core->actionManager();
278 ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
281 m_d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
282 Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
283 Constants::REVERTTOSAVED, editManagerContext);
284 cmd->setAttribute(Command::CA_UpdateText);
285 cmd->setDefaultText(tr("Revert File to Saved"));
286 mfile->addAction(cmd, Constants::G_FILE_SAVE);
287 connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));
290 am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
291 connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
294 am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
295 connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));
298 ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
300 // Window menu separators
301 QAction *tmpaction = new QAction(this);
302 tmpaction->setSeparator(true);
303 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Split", editManagerContext);
304 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
306 tmpaction = new QAction(this);
307 tmpaction->setSeparator(true);
308 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Navigate", editManagerContext);
309 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
312 cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext);
313 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
314 cmd->setAttribute(Core::Command::CA_UpdateText);
315 cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
316 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
317 connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));
320 // workaround for QTCREATORBUG-72
321 QShortcut *sc = new QShortcut(parent);
322 cmd = am->registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
323 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
324 cmd->setDefaultText(EditorManager::tr("Close"));
325 connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
329 cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext);
330 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W")));
331 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
332 connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));
334 // Close All Others Action
335 cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext);
336 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
337 cmd->setAttribute(Core::Command::CA_UpdateText);
338 connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors()));
340 // Goto Previous In History Action
341 cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
343 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
345 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
347 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
348 connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
350 // Goto Next In History Action
351 cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
353 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
355 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
357 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
358 connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
360 // Go back in navigation history
361 cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
363 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left")));
365 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left")));
367 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
368 connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
370 // Go forward in navigation history
371 cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
373 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right")));
375 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right")));
377 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
378 connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
381 QString prefix = tr("Meta+E");
383 QString prefix = tr("Ctrl+E");
386 m_d->m_splitAction = new QAction(tr("Split"), this);
387 cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
388 cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix)));
389 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
390 connect(m_d->m_splitAction, SIGNAL(triggered()), this, SLOT(split()));
392 m_d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this);
393 cmd = am->registerAction(m_d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext);
394 cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix)));
395 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
396 connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));
398 m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
399 cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
400 cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
401 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
402 connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
404 m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
405 cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
406 cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
407 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
408 connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
410 m_d->m_gotoOtherSplitAction = new QAction(tr("Go to Next Split"), this);
411 cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext);
412 cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix)));
413 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
414 connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit()));
416 ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
417 ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
418 medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
419 advancedMenu->menu()->setTitle(tr("&Advanced"));
420 advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
421 advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
422 advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
423 advancedMenu->appendGroup(Constants::G_EDIT_FONT);
424 advancedMenu->appendGroup(Constants::G_EDIT_EDITOR);
426 // Advanced menu separators
427 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Collapsing"), editManagerContext);
428 advancedMenu->addAction(cmd, Constants::G_EDIT_COLLAPSING);
429 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext);
430 advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS);
431 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Font"), editManagerContext);
432 advancedMenu->addAction(cmd, Constants::G_EDIT_FONT);
433 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Editor"), editManagerContext);
434 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
436 cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext);
437 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I")));
438 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
439 connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor()));
441 // Connect to VariableManager for CURRENT_DOCUMENT variable setting
442 VariableManager::initEditorManagerConnections();
444 m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
445 m_d->m_view = m_d->m_splitter->view();
448 QHBoxLayout *layout = new QHBoxLayout(this);
449 layout->setMargin(0);
450 layout->setSpacing(0);
451 layout->addWidget(m_d->m_splitter);
455 m_d->m_windowPopup = new OpenEditorsWindow(this);
458 EditorManager::~EditorManager()
462 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
463 if (m_d->m_coreListener) {
464 pm->removeObject(m_d->m_coreListener);
465 delete m_d->m_coreListener;
467 pm->removeObject(m_d->m_openEditorsFactory);
468 delete m_d->m_openEditorsFactory;
473 void EditorManager::init()
475 m_d->m_coreListener = new EditorClosingCoreListener(this);
476 pluginManager()->addObject(m_d->m_coreListener);
478 m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
479 pluginManager()->addObject(m_d->m_openEditorsFactory);
483 EditorToolBar *EditorManager::createToolBar(QWidget *parent)
485 return new EditorToolBar(parent);
488 QString EditorManager::defaultExternalEditor() const
491 return ConsoleProcess::defaultTerminalEmulator() + QLatin1String(
495 " -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
497 return QLatin1String("notepad %f");
501 void EditorManager::removeEditor(IEditor *editor)
503 bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
504 m_d->m_editorModel->removeEditor(editor);
506 m_d->m_core->fileManager()->removeFile(editor->file());
508 m_d->m_core->removeContextObject(editor);
511 void EditorManager::handleContextChange(Core::IContext *context)
513 if (debugEditorManager)
514 qDebug() << Q_FUNC_INFO;
515 IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
517 setCurrentEditor(editor);
523 void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
528 if (m_d->m_currentEditor == editor)
530 if (m_d->m_currentEditor && !ignoreNavigationHistory)
531 addCurrentPositionToNavigationHistory();
533 m_d->m_currentEditor = editor;
535 if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor))
536 splitterOrView->view()->setCurrentEditor(editor);
537 m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history
540 emit currentEditorChanged(editor);
544 void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view)
546 if (view == m_d->m_currentView)
549 SplitterOrView *old = m_d->m_currentView;
550 m_d->m_currentView = view;
557 if (view && !view->editor())
561 Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
563 SplitterOrView *view = m_d->m_currentView;
565 view = m_d->m_currentEditor?
566 m_d->m_splitter->findView(m_d->m_currentEditor):
567 m_d->m_splitter->findFirstView();
569 return m_d->m_splitter;
573 Core::Internal::EditorView *EditorManager::currentEditorView() const
575 return currentSplitterOrView()->view();
578 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
580 QList<IEditor *> found;
581 QString fixedname = FileManager::fixFileName(filename, FileManager::KeepLinks);
582 foreach (IEditor *editor, openedEditors()) {
583 if (fixedname == FileManager::fixFileName(editor->file()->fileName(), FileManager::KeepLinks))
589 QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
591 QList<IEditor *> found;
592 foreach (IEditor *editor, openedEditors()) {
593 if (editor->file() == file)
599 IEditor *EditorManager::currentEditor() const
601 return m_d->m_currentEditor;
604 void EditorManager::emptyView(Core::Internal::EditorView *view)
609 QList<IEditor *> editors = view->editors();
610 foreach (IEditor *editor, editors) {
611 if (!m_d->m_editorModel->isDuplicate(editor)) {
612 editors.removeAll(editor);
613 view->removeEditor(editor);
616 emit editorAboutToClose(editor);
617 removeEditor(editor);
618 view->removeEditor(editor);
620 emit editorsClosed(editors);
621 foreach (IEditor *editor, editors) {
626 void EditorManager::closeView(Core::Internal::EditorView *view)
631 if (view == m_d->m_view) {
632 if (IEditor *e = view->currentEditor())
633 closeEditors(QList<IEditor *>() << e);
637 if (IEditor *e = view->currentEditor()) {
639 when we are closing a view with an original editor which has
640 duplicates, then make one of the duplicates the original.
641 Otherwise the original would be kept around and the user might
642 experience jumping to a missleading position within the file when
643 visiting the file again. With the code below, the position within
644 the file will be the position of the first duplicate which is still
647 if (!m_d->m_editorModel->isDuplicate(e)) {
648 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(e);
649 if (!duplicates.isEmpty()) {
650 m_d->m_editorModel->makeOriginal(duplicates.first());
657 SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
658 Q_ASSERT(splitterOrView);
659 Q_ASSERT(splitterOrView->view() == view);
660 SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
661 Q_ASSERT(splitterOrView->hasEditors() == false);
662 splitterOrView->hide();
663 delete splitterOrView;
667 SplitterOrView *newCurrent = splitter->findFirstView();
669 if (IEditor *e = newCurrent->editor()) {
670 activateEditor(newCurrent->view(), e);
672 setCurrentView(newCurrent);
678 EditorManager::editorsForFiles(QList<IFile*> files) const
680 const QList<IEditor *> editors = openedEditors();
681 QSet<IEditor *> found;
682 foreach (IFile *file, files) {
683 foreach (IEditor *editor, editors) {
684 if (editor->file() == file && !found.contains(editor)) {
689 return found.toList();
692 QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
694 QSet<IEditor *> handledEditors;
695 QList<IFile *> files;
696 foreach (IEditor *editor, editors) {
697 if (!handledEditors.contains(editor)) {
698 files << editor->file();
699 handledEditors.insert(editor);
705 bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
707 m_d->m_editorModel->removeAllRestoredEditors();
708 if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
709 // m_d->clearNavigationHistory();
715 void EditorManager::closeOtherEditors(IEditor *editor)
717 m_d->m_editorModel->removeAllRestoredEditors();
718 QList<IEditor*> editors = openedEditors();
719 editors.removeAll(editor);
720 closeEditors(editors, true);
723 void EditorManager::closeOtherEditors()
725 IEditor *current = currentEditor();
726 QTC_ASSERT(current, return);
727 closeOtherEditors(current);
730 // SLOT connected to action
731 void EditorManager::closeEditor()
733 if (!m_d->m_currentEditor)
735 addCurrentPositionToNavigationHistory();
736 closeEditor(m_d->m_currentEditor);
739 void EditorManager::closeEditor(Core::IEditor *editor)
743 closeEditors(QList<IEditor *>() << editor);
746 void EditorManager::closeEditor(const QModelIndex &index)
748 IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
752 m_d->m_editorModel->removeEditor(index);
755 bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
757 if (editorsToClose.isEmpty())
760 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
762 bool closingFailed = false;
763 QList<IEditor*> acceptedEditors;
764 //ask all core listeners to check whether the editor can be closed
765 const QList<ICoreListener *> listeners =
766 pluginManager()->getObjects<ICoreListener>();
767 foreach (IEditor *editor, editorsToClose) {
768 bool editorAccepted = true;
769 if (m_d->m_editorModel->isDuplicate(editor))
770 editor = m_d->m_editorModel->originalForDuplicate(editor);
771 foreach (ICoreListener *listener, listeners) {
772 if (!listener->editorAboutToClose(editor)) {
773 editorAccepted = false;
774 closingFailed = true;
779 acceptedEditors.append(editor);
781 if (acceptedEditors.isEmpty())
783 //ask whether to save modified files
784 if (askAboutModifiedEditors) {
785 bool cancelled = false;
786 QList<IFile*> list = m_d->m_core->fileManager()->
787 saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
790 if (!list.isEmpty()) {
791 closingFailed = true;
792 QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
793 acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
796 if (acceptedEditors.isEmpty())
800 foreach(IEditor *editor, acceptedEditors)
801 acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);
803 QList<EditorView*> closedViews;
805 // remove the editors
806 foreach (IEditor *editor, acceptedEditors) {
807 emit editorAboutToClose(editor);
808 if (!editor->file()->fileName().isEmpty()
809 && !editor->isTemporary()) {
810 QByteArray state = editor->saveState();
811 if (!state.isEmpty())
812 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
815 removeEditor(editor);
816 if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
817 if (editor == view->view()->currentEditor())
818 closedViews += view->view();
819 view->view()->removeEditor(editor);
823 foreach (EditorView *view, closedViews) {
824 IEditor *newCurrent = view->currentEditor();
826 newCurrent = pickUnusedEditor();
828 activateEditor(view, newCurrent, NoActivate);
830 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
832 activateEditor(idx, view, NoActivate);
836 emit editorsClosed(acceptedEditors);
838 foreach (IEditor *editor, acceptedEditors) {
842 if (currentSplitterOrView) {
843 if (IEditor *editor = currentSplitterOrView->editor())
844 activateEditor(currentSplitterOrView->view(), editor);
847 if (!currentEditor()) {
848 emit currentEditorChanged(0);
852 return !closingFailed;
855 void EditorManager::closeDuplicate(Core::IEditor *editor)
858 IEditor *original = editor;
859 if (m_d->m_editorModel->isDuplicate(editor))
860 original= m_d->m_editorModel->originalForDuplicate(editor);
861 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(original);
863 if (duplicates.isEmpty()) {
868 if (original== editor)
869 m_d->m_editorModel->makeOriginal(duplicates.first());
871 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
873 emit editorAboutToClose(editor);
875 if(m_d->m_splitter->findView(editor)) {
876 EditorView *view = m_d->m_splitter->findView(editor)->view();
877 removeEditor(editor);
878 view->removeEditor(editor);
880 IEditor *newCurrent = view->currentEditor();
882 newCurrent = pickUnusedEditor();
884 activateEditor(view, newCurrent, NoActivate);
886 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
888 activateEditor(idx, view, NoActivate);
892 emit editorsClosed(QList<IEditor*>() << editor);
894 if (currentSplitterOrView) {
895 if (IEditor *currentEditor = currentSplitterOrView->editor())
896 activateEditor(currentSplitterOrView->view(), currentEditor);
900 Core::IEditor *EditorManager::pickUnusedEditor() const
902 foreach (IEditor *editor, openedEditors()) {
903 SplitterOrView *view = m_d->m_splitter->findView(editor);
904 if (!view || view->editor() != editor)
910 Core::IEditor *EditorManager::activateEditor(const QModelIndex &index, Internal::EditorView *view, OpenEditorFlags flags)
912 IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
914 return activateEditor(view, editor, flags);
917 QString fileName = index.data(Qt::UserRole + 1).toString();
918 QString id = index.data(Qt::UserRole + 2).toString();
919 return openEditor(view, fileName, id, flags);
922 Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
924 Q_ASSERT(view && editor);
926 if (view->currentEditor() && view->currentEditor()->file() == editor->file())
927 editor = view->currentEditor();
929 if (!view->hasEditor(editor)) {
930 bool duplicateSupported = editor->duplicateSupported();
931 if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) {
932 if (editor != sourceView->editor() || !duplicateSupported) {
933 sourceView->view()->removeEditor(editor);
934 view->addEditor(editor);
935 view->setCurrentEditor(editor);
936 if (!sourceView->editor()) {
937 if (IEditor *replacement = pickUnusedEditor()) {
938 sourceView->view()->addEditor(replacement);
942 } else if (duplicateSupported) {
943 editor = duplicateEditor(editor);
945 m_d->m_editorModel->makeOriginal(editor);
948 view->addEditor(editor);
953 Core::IEditor *EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
955 return activateEditor(0, editor, flags);
958 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
961 view = currentEditorView();
966 if (!m_d->m_currentEditor)
967 setCurrentEditor(0, (flags & IgnoreNavigationHistory));
971 editor = placeEditor(view, editor);
973 if (!(flags & NoActivate)) {
974 setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
975 if (flags & ModeSwitch) {
976 switchToPreferedMode();
979 editor->widget()->setFocus();
984 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
986 const QList<IEditor*> editors = editorsForFile(file);
987 if (editors.isEmpty())
990 return activateEditor(view, editors.first(), flags);
993 /* For something that has a 'QStringList mimeTypes' (IEditorFactory
994 * or IExternalEditor), find the one best matching the mimetype passed in.
995 * Recurse over the parent classes of the mimetype to find them. */
996 template <class EditorFactoryLike>
997 static void mimeTypeFactoryRecursion(const MimeDatabase *db,
998 const MimeType &mimeType,
999 const QList<EditorFactoryLike*> &allFactories,
1000 bool firstMatchOnly,
1001 QList<EditorFactoryLike*> *list)
1003 typedef typename QList<EditorFactoryLike*>::const_iterator EditorFactoryLikeListConstIterator;
1004 // Loop factories to find type
1005 const QString type = mimeType.type();
1006 const EditorFactoryLikeListConstIterator fcend = allFactories.constEnd();
1007 for (EditorFactoryLikeListConstIterator fit = allFactories.constBegin(); fit != fcend; ++fit) {
1008 // Exclude duplicates when recursing over xml or C++ -> C -> text.
1009 EditorFactoryLike *factory = *fit;
1010 if (!list->contains(factory) && factory->mimeTypes().contains(type)) {
1011 list->push_back(*fit);
1017 // Any parent mime type classes? -> recurse
1018 QStringList parentTypes = mimeType.subClassesOf();
1019 if (parentTypes.empty())
1021 const QStringList::const_iterator pcend = parentTypes .constEnd();
1022 for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) {
1023 if (const MimeType parent = db->findByType(*pit))
1024 mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list);
1028 EditorManager::EditorFactoryList
1029 EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
1031 EditorFactoryList rc;
1032 const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
1033 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc);
1034 if (debugEditorManager)
1035 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1039 EditorManager::ExternalEditorList
1040 EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const
1042 ExternalEditorList rc;
1043 const ExternalEditorList allEditors = pluginManager()->getObjects<IExternalEditor>();
1044 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allEditors, bestMatchOnly, &rc);
1045 if (debugEditorManager)
1046 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1050 /* For something that has a 'QString id' (IEditorFactory
1051 * or IExternalEditor), find the one matching a id. */
1052 template <class EditorFactoryLike>
1053 inline EditorFactoryLike *findById(ExtensionSystem::PluginManager *pm,
1056 const QList<EditorFactoryLike *> factories = pm->template getObjects<EditorFactoryLike>();
1057 foreach(EditorFactoryLike *efl, factories)
1058 if (id == efl->id())
1063 IEditor *EditorManager::createEditor(const QString &editorId,
1064 const QString &fileName)
1066 if (debugEditorManager)
1067 qDebug() << Q_FUNC_INFO << editorId << fileName;
1069 EditorFactoryList factories;
1070 if (editorId.isEmpty()) {
1071 const QFileInfo fileInfo(fileName);
1072 // Find by mime type
1073 MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(fileInfo);
1075 qWarning("%s unable to determine mime type of %s/%s. Falling back to text/plain",
1076 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1077 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain"));
1079 // open text files > 48 MB in binary editor
1080 if (fileInfo.size() > maxTextFileSize() && mimeType.type().startsWith(QLatin1String("text")))
1081 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("application/octet-stream"));
1082 factories = editorFactories(mimeType, true);
1084 // Find by editor id
1085 if (IEditorFactory *factory = findById<IEditorFactory>(pluginManager(), editorId))
1086 factories.push_back(factory);
1088 if (factories.empty()) {
1089 qWarning("%s: unable to find an editor factory for the file '%s', editor Id '%s'.",
1090 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1094 IEditor *editor = factories.front()->createEditor(this);
1096 connect(editor, SIGNAL(changed()), this, SLOT(updateActions()));
1098 emit editorCreated(editor, fileName);
1102 void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
1106 m_d->m_core->addContextObject(editor);
1108 m_d->m_editorModel->addEditor(editor, isDuplicate);
1110 const bool isTemporary = editor->isTemporary();
1111 const bool addWatcher = !isTemporary;
1112 m_d->m_core->fileManager()->addFile(editor->file(), addWatcher);
1114 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1116 emit editorOpened(editor);
1119 // Run the OpenWithDialog and return the editor id
1120 // selected by the user.
1121 QString EditorManager::getOpenWithEditorId(const QString &fileName,
1122 bool *isExternalEditor) const
1124 // Collect editors that can open the file
1125 const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName);
1128 QStringList allEditorIds;
1129 QStringList externalEditorIds;
1131 const EditorFactoryList editors = editorFactories(mt, false);
1132 const int size = editors.size();
1133 for (int i = 0; i < size; i++) {
1134 allEditorIds.push_back(editors.at(i)->id());
1137 const ExternalEditorList exEditors = externalEditors(mt, false);
1138 const int esize = exEditors.size();
1139 for (int i = 0; i < esize; i++) {
1140 externalEditorIds.push_back(exEditors.at(i)->id());
1141 allEditorIds.push_back(exEditors.at(i)->id());
1143 if (allEditorIds.empty())
1146 OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
1147 dialog.setEditors(allEditorIds);
1148 dialog.setCurrentEditor(0);
1149 if (dialog.exec() != QDialog::Accepted)
1151 const QString selectedId = dialog.editor();
1152 if (isExternalEditor)
1153 *isExternalEditor = externalEditorIds.contains(selectedId);
1157 static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter = 0)
1160 selectedFilter->clear();
1162 // Compile list of filter strings, sort, and remove duplicates (different mime types might
1163 // generate the same filter).
1164 QStringList filters = core->mimeDatabase()->filterStrings();
1165 if (filters.empty())
1168 filters.erase(std::unique(filters.begin(), filters.end()), filters.end());
1170 static const QString allFilesFilter =
1171 QCoreApplication::translate("Core", Constants::ALL_FILES_FILTER);
1173 *selectedFilter = allFilesFilter;
1175 // Prepend all files filter (instead of appending to work around a bug in Qt/Mac).
1176 filters.prepend(allFilesFilter);
1178 return filters.join(QLatin1String(";;"));
1181 IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
1182 OpenEditorFlags flags, bool *newEditor)
1184 return openEditor(0, fileName, editorId, flags, newEditor);
1187 int extractLineNumber(QString *fileName)
1189 int i = fileName->length() - 1;
1190 for (; i >= 0; --i) {
1191 if (!fileName->at(i).isNumber())
1196 if (fileName->at(i) == ':' || fileName->at(i) == '+') {
1197 int result = fileName->mid(i+1).toInt();
1198 *fileName = fileName->left(i);
1204 IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName,
1205 const QString &editorId, OpenEditorFlags flags, bool *newEditor)
1207 if (debugEditorManager)
1208 qDebug() << Q_FUNC_INFO << fileName << editorId;
1210 QString fn = fileName;
1211 int lineNumber = -1;
1212 if (flags && EditorManager::CanContainLineNumber)
1213 lineNumber = extractLineNumber(&fn);
1221 const QList<IEditor *> editors = editorsForFileName(fn);
1222 if (!editors.isEmpty()) {
1223 IEditor *editor = editors.first();
1224 if (flags && EditorManager::CanContainLineNumber)
1225 editor->gotoLine(lineNumber, -1);
1226 return activateEditor(view, editor, flags);
1229 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1230 IEditor *editor = createEditor(editorId, fn);
1231 // If we could not open the file in the requested editor, fall
1232 // back to the default editor:
1234 editor = createEditor(QString(), fn);
1235 if (!editor || !editor->open(fn)) {
1236 QApplication::restoreOverrideCursor();
1237 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), tr("Cannot open file %1!").arg(QDir::toNativeSeparators(fn)));
1247 IEditor *result = activateEditor(view, editor, flags);
1248 if (editor == result)
1249 restoreEditorState(editor);
1251 if (flags && EditorManager::CanContainLineNumber)
1252 editor->gotoLine(lineNumber, -1);
1254 QApplication::restoreOverrideCursor();
1258 bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorId)
1260 IExternalEditor *ee = findById<IExternalEditor>(pluginManager(), editorId);
1263 QString errorMessage;
1264 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1265 const bool ok = ee->startEditor(fileName, &errorMessage);
1266 QApplication::restoreOverrideCursor();
1268 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage);
1272 QStringList EditorManager::getOpenFileNames() const
1274 QString selectedFilter;
1275 const QString &fileFilters = formatFileFilters(m_d->m_core, &selectedFilter);
1276 return ICore::instance()->fileManager()->getOpenFileNames(fileFilters,
1277 QString(), &selectedFilter);
1281 /// Empty mode == figure out the correct mode from the editor
1282 /// forcePrefered = true, switch to the mode even if the editor is visible in another mode
1283 /// forcePrefered = false, only switch if it is not visible
1284 void EditorManager::switchToPreferedMode()
1286 QString preferedMode;
1287 // Figure out preferred mode for editor
1288 if (m_d->m_currentEditor)
1289 preferedMode = m_d->m_currentEditor->preferredModeType();
1291 if (preferedMode.isEmpty())
1292 preferedMode = Constants::MODE_EDIT_TYPE;
1294 if (m_d->m_core->modeManager()->currentMode()->type() != preferedMode) {
1295 m_d->m_core->modeManager()->activateModeType(preferedMode);
1299 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1300 QString *titlePattern,
1301 const QString &contents)
1303 if (debugEditorManager)
1304 qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1306 if (editorId.isEmpty())
1309 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1310 IEditor *edt = createEditor(editorId);
1312 QApplication::restoreOverrideCursor();
1316 if (!edt->createNew(contents)) {
1317 QApplication::restoreOverrideCursor();
1323 QString title = edt->displayName();
1326 const QChar dollar = QLatin1Char('$');
1327 const QChar dot = QLatin1Char('.');
1329 QString base = *titlePattern;
1331 base = QLatin1String("unnamed$");
1332 if (base.contains(dollar)) {
1334 QSet<QString> docnames;
1335 foreach (IEditor *editor, openedEditors()) {
1336 QString name = editor->file()->fileName();
1337 if (name.isEmpty()) {
1338 name = editor->displayName();
1339 name.remove(QLatin1Char('*'));
1341 name = QFileInfo(name).completeBaseName();
1348 title.replace(QString(dollar), QString::number(i++));
1349 } while (docnames.contains(title));
1351 title = *titlePattern;
1353 *titlePattern = title;
1355 edt->setDisplayName(title);
1357 QApplication::restoreOverrideCursor();
1361 bool EditorManager::hasEditor(const QString &fileName) const
1363 return !editorsForFileName(fileName).isEmpty();
1366 void EditorManager::restoreEditorState(IEditor *editor)
1368 QTC_ASSERT(editor, return);
1369 QString fileName = editor->file()->fileName();
1370 editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1373 bool EditorManager::saveEditor(IEditor *editor)
1375 return saveFile(editor);
1378 bool EditorManager::saveFile(IEditor *editor)
1381 editor = currentEditor();
1385 IFile *file = editor->file();
1386 file->checkPermissions();
1388 const QString &fileName = file->fileName();
1390 if (fileName.isEmpty())
1391 return saveFileAs(editor);
1393 bool success = false;
1395 // try saving, no matter what isReadOnly tells us
1396 m_d->m_core->fileManager()->blockFileChange(file);
1397 success = file->save(fileName);
1398 m_d->m_core->fileManager()->unblockFileChange(file);
1401 MakeWritableResult answer =
1402 makeEditorWritable(editor);
1403 if (answer == Failed)
1405 if (answer == SavedAs)
1408 file->checkPermissions();
1410 m_d->m_core->fileManager()->blockFileChange(file);
1411 success = file->save(fileName);
1412 m_d->m_core->fileManager()->unblockFileChange(file);
1415 if (success && !editor->isTemporary())
1416 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1421 EditorManager::ReadOnlyAction
1422 EditorManager::promptReadOnlyFile(const QString &fileName,
1423 const IVersionControl *versionControl,
1425 bool displaySaveAsButton)
1427 // Version Control: If automatic open is desired, open right away.
1428 bool promptVCS = false;
1429 if (versionControl && versionControl->supportsOperation(IVersionControl::OpenOperation)) {
1430 if (versionControl->settingsFlags() & IVersionControl::AutoOpen)
1435 // Create message box.
1436 QMessageBox msgBox(QMessageBox::Question, tr("File is Read Only"),
1437 tr("The file <i>%1</i> is read only.").arg(QDir::toNativeSeparators(fileName)),
1438 QMessageBox::Cancel, parent);
1440 QPushButton *vcsButton = 0;
1442 vcsButton = msgBox.addButton(tr("Open with VCS (%1)").arg(versionControl->displayName()), QMessageBox::AcceptRole);
1444 QPushButton *makeWritableButton = msgBox.addButton(tr("Make writable"), QMessageBox::AcceptRole);
1446 QPushButton *saveAsButton = 0;
1447 if (displaySaveAsButton)
1448 saveAsButton = msgBox.addButton(tr("Save as ..."), QMessageBox::ActionRole);
1450 msgBox.setDefaultButton(vcsButton ? vcsButton : makeWritableButton);
1453 QAbstractButton *clickedButton = msgBox.clickedButton();
1454 if (clickedButton == vcsButton)
1456 if (clickedButton == makeWritableButton)
1457 return RO_MakeWriteable;
1458 if (clickedButton == saveAsButton)
1465 EditorManager::makeEditorWritable(IEditor *editor)
1467 if (!editor || !editor->file())
1469 QString directory = QFileInfo(editor->file()->fileName()).absolutePath();
1470 IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory);
1471 IFile *file = editor->file();
1472 const QString &fileName = file->fileName();
1474 switch (promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), true)) {
1476 if (!versionControl->vcsOpen(fileName)) {
1477 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC."));
1480 file->checkPermissions();
1481 return OpenedWithVersionControl;
1482 case RO_MakeWriteable: {
1483 const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1485 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
1489 file->checkPermissions();
1490 return MadeWritable;
1492 return saveFileAs(editor) ? SavedAs : Failed;
1499 bool EditorManager::saveFileAs(IEditor *editor)
1502 editor = currentEditor();
1506 IFile *file = editor->file();
1507 const QString &filter = formatFileFilters(m_d->m_core);
1508 QString selectedFilter =
1509 m_d->m_core->mimeDatabase()->findByFile(QFileInfo(file->fileName())).filterString();
1510 const QString &absoluteFilePath =
1511 m_d->m_core->fileManager()->getSaveAsFileName(file, filter, &selectedFilter);
1513 if (absoluteFilePath.isEmpty())
1515 if (absoluteFilePath != file->fileName()) {
1516 const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1517 if (!existList.isEmpty()) {
1518 closeEditors(existList, false);
1522 m_d->m_core->fileManager()->blockFileChange(file);
1523 const bool success = file->save(absoluteFilePath);
1524 m_d->m_core->fileManager()->unblockFileChange(file);
1525 file->checkPermissions();
1527 // @todo: There is an issue to be treated here. The new file might be of a different mime
1528 // type than the original and thus require a different editor. An alternative strategy
1529 // would be to close the current editor and open a new appropriate one, but this is not
1530 // a good way out either (also the undo stack would be lost). Perhaps the best is to
1531 // re-think part of the editors design.
1533 if (success && !editor->isTemporary())
1534 m_d->m_core->fileManager()->addToRecentFiles(file->fileName());
1540 void EditorManager::gotoNextDocHistory()
1542 OpenEditorsWindow *dialog = windowPopup();
1543 if (dialog->isVisible()) {
1544 dialog->selectNextEditor();
1546 EditorView *view = currentEditorView();
1547 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1548 dialog->selectNextEditor();
1549 showPopupOrSelectDocument();
1553 void EditorManager::gotoPreviousDocHistory()
1555 OpenEditorsWindow *dialog = windowPopup();
1556 if (dialog->isVisible()) {
1557 dialog->selectPreviousEditor();
1559 EditorView *view = currentEditorView();
1560 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1561 dialog->selectPreviousEditor();
1562 showPopupOrSelectDocument();
1566 void EditorManager::makeCurrentEditorWritable()
1568 if (IEditor* curEditor = currentEditor())
1569 makeEditorWritable(curEditor);
1572 void EditorManager::updateActions()
1575 IEditor *curEditor = currentEditor();
1576 int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1580 if (!curEditor->file()->fileName().isEmpty()) {
1581 QFileInfo fi(curEditor->file()->fileName());
1582 fName = fi.fileName();
1584 fName = curEditor->displayName();
1588 window()->setWindowModified(curEditor->file()->isModified());
1590 if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) {
1591 // we are about to change a read-only file, warn user
1592 showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"),
1593 tr("<b>Warning:</b> You are changing a read-only file."),
1594 tr("Make writable"), this, SLOT(makeCurrentEditorWritable()));
1596 hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"));
1599 } else { // curEditor
1600 window()->setWindowModified(false);
1604 m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified());
1605 m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed());
1606 m_d->m_revertToSavedAction->setEnabled(curEditor != 0
1607 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified());
1610 if (!fName.isEmpty())
1611 quotedName = '"' + fName + '"';
1613 m_d->m_saveAsAction->setText(tr("Save %1 &As...").arg(quotedName));
1614 m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName));
1615 m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName));
1617 m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0);
1618 m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName));
1619 m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0);
1620 m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1);
1621 m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others")));
1623 m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1624 m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1625 EditorView *view = currentEditorView();
1626 m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
1627 m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
1629 bool hasSplitter = m_d->m_splitter->isSplitter();
1630 m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter);
1631 m_d->m_removeAllSplitsAction->setEnabled(hasSplitter);
1632 m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter);
1634 m_d->m_openInExternalEditorAction->setEnabled(curEditor != 0);
1637 bool EditorManager::hasSplitter() const
1639 return m_d->m_splitter->isSplitter();
1642 QList<IEditor*> EditorManager::openedEditors() const
1644 return m_d->m_editorModel->editors();
1647 OpenEditorsModel *EditorManager::openedEditorsModel() const
1649 return m_d->m_editorModel;
1652 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1654 currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1658 void EditorManager::cutForwardNavigationHistory()
1660 currentEditorView()->cutForwardNavigationHistory();
1664 void EditorManager::goBackInNavigationHistory()
1666 currentEditorView()->goBackInNavigationHistory();
1671 void EditorManager::goForwardInNavigationHistory()
1673 currentEditorView()->goForwardInNavigationHistory();
1677 OpenEditorsWindow *EditorManager::windowPopup() const
1679 return m_d->m_windowPopup;
1682 void EditorManager::showPopupOrSelectDocument() const
1684 if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1685 windowPopup()->selectAndHide();
1687 // EditorManager is invisible when invoked from Design Mode.
1688 const QPoint p = isVisible() ?
1689 mapToGlobal(QPoint(0, 0)) :
1690 m_d->m_core->mainWindow()->mapToGlobal(QPoint(0, 0));
1691 windowPopup()->move((width()-m_d->m_windowPopup->width())/2 + p.x(),
1692 (height()-m_d->m_windowPopup->height())/2 + p.y());
1693 windowPopup()->setVisible(true);
1697 // Save state of all non-teporary editors.
1698 QByteArray EditorManager::saveState() const
1701 QDataStream stream(&bytes, QIODevice::WriteOnly);
1703 stream << QByteArray("EditorManagerV4");
1705 QList<IEditor *> editors = openedEditors();
1706 foreach (IEditor *editor, editors) {
1707 if (!editor->file()->fileName().isEmpty()
1708 && !editor->isTemporary()) {
1709 QByteArray state = editor->saveState();
1710 if (!state.isEmpty())
1711 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
1715 stream << m_d->m_editorStates;
1717 QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1718 int entriesCount = 0;
1719 foreach (const OpenEditorsModel::Entry &entry, entries) {
1720 // The editor may be 0 if it was not loaded yet: In that case it is not temporary
1721 if (!entry.editor || !entry.editor->isTemporary())
1725 stream << entriesCount;
1727 foreach (const OpenEditorsModel::Entry &entry, entries) {
1728 if (!entry.editor || !entry.editor->isTemporary())
1729 stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1732 stream << m_d->m_splitter->saveState();
1737 bool EditorManager::restoreState(const QByteArray &state)
1739 closeAllEditors(true);
1741 QDataStream stream(state);
1746 if (version != "EditorManagerV4")
1749 QMap<QString, QVariant> editorstates;
1751 QApplication::setOverrideCursor(Qt::WaitCursor);
1753 stream >> editorstates;
1755 QMapIterator<QString, QVariant> i(editorstates);
1756 while (i.hasNext()) {
1758 m_d->m_editorStates.insert(i.key(), i.value());
1761 int editorCount = 0;
1762 stream >> editorCount;
1763 while (--editorCount >= 0) {
1766 QString displayName;
1767 stream >> displayName;
1771 if (!fileName.isEmpty() && !displayName.isEmpty())
1772 m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1775 QByteArray splitterstates;
1776 stream >> splitterstates;
1777 m_d->m_splitter->restoreState(splitterstates);
1779 // splitting and stuff results in focus trouble, that's why we set the focus again after restoration
1780 if (m_d->m_currentEditor) {
1781 m_d->m_currentEditor->widget()->setFocus();
1782 } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) {
1783 if (IEditor *e = view->editor())
1784 e->widget()->setFocus();
1785 else if (view->view())
1786 view->view()->setFocus();
1789 QApplication::restoreOverrideCursor();
1794 static const char * const documentStatesKey = "EditorManager/DocumentStates";
1795 static const char * const externalEditorKey = "EditorManager/ExternalEditorCommand";
1796 static const char * const reloadBehaviorKey = "EditorManager/ReloadBehavior";
1797 static const char * const utf8BomBehaviorKey = "EditorManager/Utf8BomBehavior";
1799 void EditorManager::saveSettings()
1801 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1802 settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates);
1803 settings->setValue(QLatin1String(externalEditorKey), m_d->m_externalEditor);
1804 settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadSetting);
1805 settings->setValue(QLatin1String(utf8BomBehaviorKey), m_d->m_utf8BomSetting);
1808 void EditorManager::readSettings()
1810 // Backward compatibility to old locations for these settings
1811 QSettings *qs = m_d->m_core->settings();
1812 if (qs->contains(QLatin1String(documentStatesKey))) {
1813 m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey))
1814 .value<QMap<QString, QVariant> >();
1815 qs->remove(QLatin1String(documentStatesKey));
1817 if (qs->contains(QLatin1String(externalEditorKey))) {
1818 m_d->m_externalEditor = qs->value(QLatin1String(externalEditorKey)).toString();
1819 qs->remove(QLatin1String(externalEditorKey));
1822 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1823 if (settings->contains(QLatin1String(documentStatesKey)))
1824 m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey))
1825 .value<QMap<QString, QVariant> >();
1826 if (settings->contains(QLatin1String(externalEditorKey)))
1827 m_d->m_externalEditor = settings->value(QLatin1String(externalEditorKey)).toString();
1829 if (settings->contains(QLatin1String(reloadBehaviorKey)))
1830 m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1832 if (settings->contains(QLatin1String(utf8BomBehaviorKey)))
1833 m_d->m_utf8BomSetting = (IFile::Utf8BomSetting)settings->value(QLatin1String(utf8BomBehaviorKey)).toInt();
1837 void EditorManager::revertToSaved()
1839 IEditor *currEditor = currentEditor();
1842 const QString fileName = currEditor->file()->fileName();
1843 if (fileName.isEmpty())
1845 if (currEditor->file()->isModified()) {
1846 QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"),
1847 tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)),
1848 QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow());
1849 msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
1850 msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
1851 msgBox.setDefaultButton(QMessageBox::No);
1852 msgBox.setEscapeButton(QMessageBox::No);
1853 if (msgBox.exec() == QMessageBox::No)
1857 currEditor->file()->reload(IFile::FlagReload, IFile::TypeContents);
1860 void EditorManager::showEditorInfoBar(const QString &id,
1861 const QString &infoText,
1862 const QString &buttonText,
1863 QObject *object, const char *buttonPressMember,
1864 const char *cancelButtonPressMember)
1866 currentEditorView()->showEditorInfoBar(id, infoText, buttonText, object, buttonPressMember, cancelButtonPressMember);
1870 void EditorManager::hideEditorInfoBar(const QString &id)
1872 Core::Internal::EditorView *cev = currentEditorView();
1874 cev->hideEditorInfoBar(id);
1877 void EditorManager::showEditorStatusBar(const QString &id,
1878 const QString &infoText,
1879 const QString &buttonText,
1880 QObject *object, const char *member)
1883 currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1886 void EditorManager::hideEditorStatusBar(const QString &id)
1888 currentEditorView()->hideEditorStatusBar(id);
1891 QString EditorManager::externalEditorHelpText() const
1894 "<table border=1 cellspacing=0 cellpadding=3>"
1895 "<tr><th>Variable</th><th>Expands to</th></tr>"
1896 "<tr><td>%f</td><td>file name</td></tr>"
1897 "<tr><td>%l</td><td>current line number</td></tr>"
1898 "<tr><td>%c</td><td>current column number</td></tr>"
1899 "<tr><td>%x</td><td>editor's x position on screen</td></tr>"
1900 "<tr><td>%y</td><td>editor's y position on screen</td></tr>"
1901 "<tr><td>%w</td><td>editor's width in pixels</td></tr>"
1902 "<tr><td>%h</td><td>editor's height in pixels</td></tr>"
1903 "<tr><td>%W</td><td>editor's width in characters</td></tr>"
1904 "<tr><td>%H</td><td>editor's height in characters</td></tr>"
1905 "<tr><td>%%</td><td>%</td></tr>"
1910 void EditorManager::openInExternalEditor()
1912 QString command = m_d->m_externalEditor;
1913 if (command.isEmpty())
1914 command = defaultExternalEditor();
1916 if (command.isEmpty())
1919 IEditor *editor = currentEditor();
1922 if (editor->file()->isModified()) {
1923 bool cancelled = false;
1924 QList<IFile*> list = m_d->m_core->fileManager()->
1925 saveModifiedFiles(QList<IFile*>() << editor->file(), &cancelled);
1930 QRect rect = editor->widget()->rect();
1931 QFont font = editor->widget()->font();
1932 QFontMetrics fm(font);
1933 rect.moveTo(editor->widget()->mapToGlobal(QPoint(0,0)));
1935 QString pre = command;
1937 for (int i = 0; i < pre.size(); ++i) {
1938 QChar c = pre.at(i);
1939 if (c == QLatin1Char('%') && i < pre.size()-1) {
1942 if (c == QLatin1Char('f'))
1943 s = editor->file()->fileName();
1944 else if (c == QLatin1Char('l'))
1945 s = QString::number(editor->currentLine());
1946 else if (c == QLatin1Char('c'))
1947 s = QString::number(editor->currentColumn());
1948 else if (c == QLatin1Char('x'))
1949 s = QString::number(rect.x());
1950 else if (c == QLatin1Char('y'))
1951 s = QString::number(rect.y());
1952 else if (c == QLatin1Char('w'))
1953 s = QString::number(rect.width());
1954 else if (c == QLatin1Char('h'))
1955 s = QString::number(rect.height());
1956 else if (c == QLatin1Char('W'))
1957 s = QString::number(rect.width() / fm.width(QLatin1Char('x')));
1958 else if (c == QLatin1Char('H'))
1959 s = QString::number(rect.height() / fm.lineSpacing());
1960 else if (c == QLatin1Char('%'))
1963 s = QLatin1Char('%');
1973 QProcess::startDetached(cmd);
1976 void EditorManager::setExternalEditor(const QString &editor)
1978 if (editor.isEmpty() || editor == defaultExternalEditor())
1979 m_d->m_externalEditor = defaultExternalEditor();
1981 m_d->m_externalEditor = editor;
1984 QString EditorManager::externalEditor() const
1986 if (m_d->m_externalEditor.isEmpty())
1987 return defaultExternalEditor();
1988 return m_d->m_externalEditor;
1991 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1993 m_d->m_reloadSetting = behavior;
1996 IFile::ReloadSetting EditorManager::reloadSetting() const
1998 return m_d->m_reloadSetting;
2001 void EditorManager::setUtf8BomSetting(IFile::Utf8BomSetting behavior)
2003 m_d->m_utf8BomSetting = behavior;
2006 IFile::Utf8BomSetting EditorManager::utf8BomSetting() const
2008 return m_d->m_utf8BomSetting;
2011 QTextCodec *EditorManager::defaultTextEncoding() const
2013 QSettings *settings = Core::ICore::instance()->settings();
2014 if (QTextCodec *candidate = QTextCodec::codecForName(
2015 settings->value(QLatin1String(Constants::SETTINGS_DEFAULTTEXTENCODING)).toByteArray()))
2017 return QTextCodec::codecForLocale();
2020 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
2022 if (!editor->duplicateSupported())
2025 IEditor *duplicate = editor->duplicate(0);
2026 duplicate->restoreState(editor->saveState());
2027 emit editorCreated(duplicate, duplicate->file()->fileName());
2028 addEditor(duplicate, true);
2032 void EditorManager::split(Qt::Orientation orientation)
2034 SplitterOrView *view = m_d->m_currentView;
2036 view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor)
2037 : m_d->m_splitter->findFirstView();
2038 if (view && !view->splitter()) {
2039 view->split(orientation);
2044 void EditorManager::split()
2046 split(Qt::Vertical);
2049 void EditorManager::splitSideBySide()
2051 split(Qt::Horizontal);
2054 void EditorManager::removeCurrentSplit()
2056 SplitterOrView *viewToClose = m_d->m_currentView;
2057 if (!viewToClose && m_d->m_currentEditor)
2058 viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor);
2060 if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
2063 closeView(viewToClose->view());
2067 void EditorManager::removeAllSplits()
2069 if (!m_d->m_splitter->isSplitter())
2071 IEditor *editor = m_d->m_currentEditor;
2072 m_d->m_currentEditor = 0; // trigger update below
2073 if (editor && m_d->m_editorModel->isDuplicate(editor))
2074 m_d->m_editorModel->makeOriginal(editor);
2075 m_d->m_splitter->unsplitAll();
2077 editor = pickUnusedEditor();
2078 activateEditor(editor);
2081 void EditorManager::gotoOtherSplit()
2083 if (m_d->m_splitter->isSplitter()) {
2084 SplitterOrView *currentView = m_d->m_currentView;
2085 if (!currentView && m_d->m_currentEditor)
2086 currentView = m_d->m_splitter->findView(m_d->m_currentEditor);
2088 currentView = m_d->m_splitter->findFirstView();
2089 SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
2091 view = m_d->m_splitter->findFirstView();
2093 if (IEditor *editor = view->editor()) {
2094 setCurrentEditor(editor, true);
2095 editor->widget()->setFocus();
2097 setCurrentView(view);
2103 qint64 EditorManager::maxTextFileSize()
2105 return (qint64(3) << 24);
2107 //===================EditorClosingCoreListener======================