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 info@qt.nokia.com.
31 **************************************************************************/
33 #include "editormanager.h"
34 #include "editorview.h"
35 #include "openeditorswindow.h"
36 #include "openeditorsview.h"
37 #include "openeditorsmodel.h"
38 #include "openwithdialog.h"
39 #include "filemanager.h"
42 #include "iversioncontrol.h"
43 #include "mimedatabase.h"
44 #include "tabpositionindicator.h"
45 #include "vcsmanager.h"
47 #include <coreplugin/editortoolbar.h>
48 #include <coreplugin/coreconstants.h>
49 #include <coreplugin/modemanager.h>
50 #include <coreplugin/actionmanager/actionmanager.h>
51 #include <coreplugin/actionmanager/actioncontainer.h>
52 #include <coreplugin/actionmanager/command.h>
53 #include <coreplugin/editormanager/ieditorfactory.h>
54 #include <coreplugin/editormanager/iexternaleditor.h>
55 #include <coreplugin/icorelistener.h>
56 #include <coreplugin/infobar.h>
57 #include <coreplugin/imode.h>
58 #include <coreplugin/settingsdatabase.h>
59 #include <coreplugin/variablemanager.h>
60 #include <coreplugin/uniqueidmanager.h>
62 #include <extensionsystem/pluginmanager.h>
64 #include <utils/consoleprocess.h>
65 #include <utils/qtcassert.h>
67 #include <QtCore/QDateTime>
68 #include <QtCore/QDebug>
69 #include <QtCore/QFileInfo>
70 #include <QtCore/QMap>
71 #include <QtCore/QProcess>
72 #include <QtCore/QSet>
73 #include <QtCore/QSettings>
74 #include <QtCore/QTextCodec>
75 #include <QtCore/QTimer>
77 #include <QtGui/QAction>
78 #include <QtGui/QShortcut>
79 #include <QtGui/QApplication>
80 #include <QtGui/QFileDialog>
81 #include <QtGui/QLayout>
82 #include <QtGui/QMainWindow>
83 #include <QtGui/QMenu>
84 #include <QtGui/QMessageBox>
85 #include <QtGui/QPushButton>
86 #include <QtGui/QSplitter>
87 #include <QtGui/QStackedLayout>
89 enum { debugEditorManager=0 };
91 static const char kCurrentDocumentFilePath[] = "CurrentDocument:FilePath";
92 static const char kCurrentDocumentPath[] = "CurrentDocument:Path";
93 static const char kCurrentDocumentXPos[] = "CurrentDocument:XPos";
94 static const char kCurrentDocumentYPos[] = "CurrentDocument:YPos";
96 static inline ExtensionSystem::PluginManager *pluginManager()
98 return ExtensionSystem::PluginManager::instance();
101 //===================EditorClosingCoreListener======================
106 class EditorClosingCoreListener : public ICoreListener
109 EditorClosingCoreListener(EditorManager *em);
110 bool editorAboutToClose(IEditor *editor);
111 bool coreAboutToClose();
117 EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em)
122 bool EditorClosingCoreListener::editorAboutToClose(IEditor *)
127 bool EditorClosingCoreListener::coreAboutToClose()
129 // Do not ask for files to save.
130 // MainWindow::closeEvent has already done that.
131 return m_em->closeAllEditors(false);
134 } // namespace Internal
137 using namespace Core;
138 using namespace Core::Internal;
139 using namespace Utils;
141 //===================EditorManager=====================
143 EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;
145 EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
146 : QWidget(parent), m_mode(mode)
148 setLayout(new QVBoxLayout);
149 layout()->setMargin(0);
150 connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)),
151 this, SLOT(currentModeChanged(Core::IMode *)));
153 currentModeChanged(Core::ModeManager::instance()->currentMode());
156 EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
158 if (m_current == this) {
159 EditorManager::instance()->setParent(0);
160 EditorManager::instance()->hide();
164 void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
166 if (m_current == this) {
168 EditorManager::instance()->setParent(0);
169 EditorManager::instance()->hide();
171 if (m_mode == mode) {
173 layout()->addWidget(EditorManager::instance());
174 EditorManager::instance()->show();
178 EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
183 // ---------------- EditorManager
188 struct EditorManagerPrivate {
189 explicit EditorManagerPrivate(ICore *core, QWidget *parent);
190 ~EditorManagerPrivate();
191 Internal::EditorView *m_view;
192 Internal::SplitterOrView *m_splitter;
193 QPointer<IEditor> m_currentEditor;
194 QPointer<SplitterOrView> m_currentView;
195 QTimer *m_autoSaveTimer;
201 QAction *m_revertToSavedAction;
202 QAction *m_saveAction;
203 QAction *m_saveAsAction;
204 QAction *m_closeCurrentEditorAction;
205 QAction *m_closeAllEditorsAction;
206 QAction *m_closeOtherEditorsAction;
207 QAction *m_gotoNextDocHistoryAction;
208 QAction *m_gotoPreviousDocHistoryAction;
209 QAction *m_goBackAction;
210 QAction *m_goForwardAction;
211 QAction *m_splitAction;
212 QAction *m_splitSideBySideAction;
213 QAction *m_removeCurrentSplitAction;
214 QAction *m_removeAllSplitsAction;
215 QAction *m_gotoOtherSplitAction;
217 Internal::OpenEditorsWindow *m_windowPopup;
218 Internal::EditorClosingCoreListener *m_coreListener;
220 QMap<QString, QVariant> m_editorStates;
221 Internal::OpenEditorsViewFactory *m_openEditorsFactory;
223 OpenEditorsModel *m_editorModel;
225 IFile::ReloadSetting m_reloadSetting;
227 QString m_titleAddition;
229 bool m_autoSaveEnabled;
230 int m_autoSaveInterval;
234 EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
239 m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)),
240 m_saveAction(new QAction(parent)),
241 m_saveAsAction(new QAction(parent)),
242 m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)),
243 m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)),
244 m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
245 m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
246 m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
247 m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)),
248 m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)),
251 m_reloadSetting(IFile::AlwaysAsk),
252 m_autoSaveEnabled(true),
253 m_autoSaveInterval(5)
255 m_editorModel = new OpenEditorsModel(parent);
258 EditorManagerPrivate::~EditorManagerPrivate()
260 // clearNavigationHistory();
263 EditorManager *EditorManager::m_instance = 0;
265 static Command *createSeparator(ActionManager *am, QObject *parent,
267 const Context &context)
269 QAction *tmpaction = new QAction(parent);
270 tmpaction->setSeparator(true);
271 Command *cmd = am->registerAction(tmpaction, name, context);
275 EditorManager::EditorManager(ICore *core, QWidget *parent) :
277 m_d(new EditorManagerPrivate(core, parent))
281 connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)),
282 this, SLOT(handleContextChange(Core::IContext *)));
284 const Context editManagerContext(Constants::C_EDITORMANAGER);
285 // combined context for edit & design modes
286 const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
288 ActionManager *am = m_d->m_core->actionManager();
289 ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
292 m_d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
293 Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
294 Constants::REVERTTOSAVED, editManagerContext);
295 cmd->setAttribute(Command::CA_UpdateText);
296 cmd->setDefaultText(tr("Revert File to Saved"));
297 mfile->addAction(cmd, Constants::G_FILE_SAVE);
298 connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));
301 am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
302 connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
305 am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
306 connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));
309 ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
311 // Window menu separators
312 QAction *tmpaction = new QAction(this);
313 tmpaction->setSeparator(true);
314 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Split", editManagerContext);
315 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
317 tmpaction = new QAction(this);
318 tmpaction->setSeparator(true);
319 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Navigate", editManagerContext);
320 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
323 cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext, true);
324 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
325 cmd->setAttribute(Core::Command::CA_UpdateText);
326 cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
327 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
328 connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));
331 // workaround for QTCREATORBUG-72
332 QShortcut *sc = new QShortcut(parent);
333 cmd = am->registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
334 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
335 cmd->setDefaultText(EditorManager::tr("Close"));
336 connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
340 cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext, true);
341 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W")));
342 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
343 connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));
345 // Close All Others Action
346 cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext, true);
347 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
348 cmd->setAttribute(Core::Command::CA_UpdateText);
349 connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors()));
351 // Goto Previous In History Action
352 cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
354 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
356 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
358 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
359 connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
361 // Goto Next In History Action
362 cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
364 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
366 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
368 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
369 connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
371 // Go back in navigation history
372 cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
374 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left")));
376 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left")));
378 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
379 connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
381 // Go forward in navigation history
382 cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
384 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right")));
386 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right")));
388 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
389 connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
392 QString prefix = tr("Meta+E");
394 QString prefix = tr("Ctrl+E");
397 m_d->m_splitAction = new QAction(tr("Split"), this);
398 cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
399 cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix)));
400 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
401 connect(m_d->m_splitAction, SIGNAL(triggered()), this, SLOT(split()));
403 m_d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this);
404 cmd = am->registerAction(m_d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext);
405 cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix)));
406 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
407 connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));
409 m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
410 cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
411 cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
412 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
413 connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
415 m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
416 cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
417 cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
418 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
419 connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
421 m_d->m_gotoOtherSplitAction = new QAction(tr("Go to Next Split"), this);
422 cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext);
423 cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix)));
424 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
425 connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit()));
427 ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
428 ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
429 medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
430 advancedMenu->menu()->setTitle(tr("Ad&vanced"));
431 advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
432 advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
433 advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
434 advancedMenu->appendGroup(Constants::G_EDIT_FONT);
435 advancedMenu->appendGroup(Constants::G_EDIT_EDITOR);
437 // Advanced menu separators
438 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Collapsing"), editManagerContext);
439 advancedMenu->addAction(cmd, Constants::G_EDIT_COLLAPSING);
440 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext);
441 advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS);
442 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Font"), editManagerContext);
443 advancedMenu->addAction(cmd, Constants::G_EDIT_FONT);
444 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Editor"), editManagerContext);
445 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
448 m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
449 m_d->m_view = m_d->m_splitter->view();
452 QHBoxLayout *layout = new QHBoxLayout(this);
453 layout->setMargin(0);
454 layout->setSpacing(0);
455 layout->addWidget(m_d->m_splitter);
459 m_d->m_windowPopup = new OpenEditorsWindow(this);
461 m_d->m_autoSaveTimer = new QTimer(this);
462 connect(m_d->m_autoSaveTimer, SIGNAL(timeout()), SLOT(autoSave()));
466 EditorManager::~EditorManager()
470 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
471 if (m_d->m_coreListener) {
472 pm->removeObject(m_d->m_coreListener);
473 delete m_d->m_coreListener;
475 pm->removeObject(m_d->m_openEditorsFactory);
476 delete m_d->m_openEditorsFactory;
481 void EditorManager::init()
483 m_d->m_coreListener = new EditorClosingCoreListener(this);
484 pluginManager()->addObject(m_d->m_coreListener);
486 m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
487 pluginManager()->addObject(m_d->m_openEditorsFactory);
489 VariableManager *vm = VariableManager::instance();
490 vm->registerVariable(QLatin1String(kCurrentDocumentFilePath),
491 tr("Full path of the current document including file name."));
492 vm->registerVariable(QLatin1String(kCurrentDocumentPath),
493 tr("Full path of the current document excluding file name."));
494 vm->registerVariable(QLatin1String(kCurrentDocumentXPos),
495 tr("X-coordinate of the current editor's upper left corner, relative to screen."));
496 vm->registerVariable(QLatin1String(kCurrentDocumentYPos),
497 tr("Y-coordinate of the current editor's upper left corner, relative to screen."));
498 connect(vm, SIGNAL(variableUpdateRequested(QString)),
499 this, SLOT(updateVariable(QString)));
502 void EditorManager::updateAutoSave()
504 if (m_d->m_autoSaveEnabled)
505 m_d->m_autoSaveTimer->start(m_d->m_autoSaveInterval * (60 * 1000));
507 m_d->m_autoSaveTimer->stop();
510 EditorToolBar *EditorManager::createToolBar(QWidget *parent)
512 return new EditorToolBar(parent);
515 void EditorManager::removeEditor(IEditor *editor)
517 bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
518 m_d->m_editorModel->removeEditor(editor);
520 m_d->m_core->fileManager()->removeFile(editor->file());
522 m_d->m_core->removeContextObject(editor);
525 void EditorManager::handleContextChange(Core::IContext *context)
527 if (debugEditorManager)
528 qDebug() << Q_FUNC_INFO;
529 IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
531 setCurrentEditor(editor);
537 void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
542 if (m_d->m_currentEditor == editor)
544 if (m_d->m_currentEditor && !ignoreNavigationHistory)
545 addCurrentPositionToNavigationHistory();
547 m_d->m_currentEditor = editor;
549 if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor))
550 splitterOrView->view()->setCurrentEditor(editor);
551 m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history
555 emit currentEditorChanged(editor);
559 void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view)
561 if (view == m_d->m_currentView)
564 SplitterOrView *old = m_d->m_currentView;
565 m_d->m_currentView = view;
572 if (view && !view->editor())
576 Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
578 SplitterOrView *view = m_d->m_currentView;
580 view = m_d->m_currentEditor?
581 m_d->m_splitter->findView(m_d->m_currentEditor):
582 m_d->m_splitter->findFirstView();
584 return m_d->m_splitter;
588 Core::Internal::EditorView *EditorManager::currentEditorView() const
590 return currentSplitterOrView()->view();
593 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
595 QList<IEditor *> found;
596 QString fixedname = FileManager::fixFileName(filename, FileManager::KeepLinks);
597 foreach (IEditor *editor, openedEditors()) {
598 if (fixedname == FileManager::fixFileName(editor->file()->fileName(), FileManager::KeepLinks))
604 QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
606 QList<IEditor *> found;
607 foreach (IEditor *editor, openedEditors()) {
608 if (editor->file() == file)
614 IEditor *EditorManager::currentEditor() const
616 return m_d->m_currentEditor;
619 void EditorManager::emptyView(Core::Internal::EditorView *view)
624 QList<IEditor *> editors = view->editors();
625 foreach (IEditor *editor, editors) {
626 if (!m_d->m_editorModel->isDuplicate(editor)) {
627 editors.removeAll(editor);
628 view->removeEditor(editor);
631 emit editorAboutToClose(editor);
632 removeEditor(editor);
633 view->removeEditor(editor);
635 emit editorsClosed(editors);
636 foreach (IEditor *editor, editors) {
641 void EditorManager::closeView(Core::Internal::EditorView *view)
646 if (view == m_d->m_view) {
647 if (IEditor *e = view->currentEditor())
648 closeEditors(QList<IEditor *>() << e);
652 if (IEditor *e = view->currentEditor()) {
654 when we are closing a view with an original editor which has
655 duplicates, then make one of the duplicates the original.
656 Otherwise the original would be kept around and the user might
657 experience jumping to a missleading position within the file when
658 visiting the file again. With the code below, the position within
659 the file will be the position of the first duplicate which is still
662 if (!m_d->m_editorModel->isDuplicate(e)) {
663 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(e);
664 if (!duplicates.isEmpty()) {
665 m_d->m_editorModel->makeOriginal(duplicates.first());
672 SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
673 Q_ASSERT(splitterOrView);
674 Q_ASSERT(splitterOrView->view() == view);
675 SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
676 Q_ASSERT(splitterOrView->hasEditors() == false);
677 splitterOrView->hide();
678 delete splitterOrView;
682 SplitterOrView *newCurrent = splitter->findFirstView();
684 if (IEditor *e = newCurrent->editor()) {
685 activateEditor(newCurrent->view(), e);
687 setCurrentView(newCurrent);
693 EditorManager::editorsForFiles(QList<IFile*> files) const
695 const QList<IEditor *> editors = openedEditors();
696 QSet<IEditor *> found;
697 foreach (IFile *file, files) {
698 foreach (IEditor *editor, editors) {
699 if (editor->file() == file && !found.contains(editor)) {
704 return found.toList();
707 QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
709 QSet<IEditor *> handledEditors;
710 QList<IFile *> files;
711 foreach (IEditor *editor, editors) {
712 if (!handledEditors.contains(editor)) {
713 files << editor->file();
714 handledEditors.insert(editor);
720 bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
722 m_d->m_editorModel->removeAllRestoredEditors();
723 if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
724 // m_d->clearNavigationHistory();
730 void EditorManager::closeOtherEditors(IEditor *editor)
732 m_d->m_editorModel->removeAllRestoredEditors();
733 QList<IEditor*> editors = openedEditors();
734 editors.removeAll(editor);
735 closeEditors(editors, true);
738 void EditorManager::closeOtherEditors()
740 IEditor *current = currentEditor();
741 QTC_ASSERT(current, return);
742 closeOtherEditors(current);
745 // SLOT connected to action
746 void EditorManager::closeEditor()
748 if (!m_d->m_currentEditor)
750 addCurrentPositionToNavigationHistory();
751 closeEditor(m_d->m_currentEditor);
754 void EditorManager::closeEditor(Core::IEditor *editor)
758 closeEditors(QList<IEditor *>() << editor);
761 void EditorManager::closeEditor(const QModelIndex &index)
763 IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
767 m_d->m_editorModel->removeEditor(index);
770 bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
772 if (editorsToClose.isEmpty())
775 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
777 bool closingFailed = false;
778 QList<IEditor*> acceptedEditors;
779 //ask all core listeners to check whether the editor can be closed
780 const QList<ICoreListener *> listeners =
781 pluginManager()->getObjects<ICoreListener>();
782 foreach (IEditor *editor, editorsToClose) {
783 bool editorAccepted = true;
784 if (m_d->m_editorModel->isDuplicate(editor))
785 editor = m_d->m_editorModel->originalForDuplicate(editor);
786 foreach (ICoreListener *listener, listeners) {
787 if (!listener->editorAboutToClose(editor)) {
788 editorAccepted = false;
789 closingFailed = true;
794 acceptedEditors.append(editor);
796 if (acceptedEditors.isEmpty())
798 //ask whether to save modified files
799 if (askAboutModifiedEditors) {
800 bool cancelled = false;
801 QList<IFile*> list = m_d->m_core->fileManager()->
802 saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
805 if (!list.isEmpty()) {
806 closingFailed = true;
807 QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
808 acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
811 if (acceptedEditors.isEmpty())
815 foreach(IEditor *editor, acceptedEditors)
816 acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);
818 QList<EditorView*> closedViews;
820 // remove the editors
821 foreach (IEditor *editor, acceptedEditors) {
822 emit editorAboutToClose(editor);
823 if (!editor->file()->fileName().isEmpty()
824 && !editor->isTemporary()) {
825 QByteArray state = editor->saveState();
826 if (!state.isEmpty())
827 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
830 removeEditor(editor);
831 if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
832 if (editor == view->view()->currentEditor())
833 closedViews += view->view();
834 view->view()->removeEditor(editor);
838 foreach (EditorView *view, closedViews) {
839 IEditor *newCurrent = view->currentEditor();
841 newCurrent = pickUnusedEditor();
843 activateEditor(view, newCurrent, NoActivate);
845 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
847 activateEditorForIndex(view, idx, NoActivate);
851 emit editorsClosed(acceptedEditors);
853 foreach (IEditor *editor, acceptedEditors) {
857 if (currentSplitterOrView) {
858 if (IEditor *editor = currentSplitterOrView->editor())
859 activateEditor(currentSplitterOrView->view(), editor);
862 if (!currentEditor()) {
863 emit currentEditorChanged(0);
868 return !closingFailed;
871 void EditorManager::closeDuplicate(Core::IEditor *editor)
874 IEditor *original = editor;
875 if (m_d->m_editorModel->isDuplicate(editor))
876 original= m_d->m_editorModel->originalForDuplicate(editor);
877 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(original);
879 if (duplicates.isEmpty()) {
884 if (original== editor)
885 m_d->m_editorModel->makeOriginal(duplicates.first());
887 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
889 emit editorAboutToClose(editor);
891 if(m_d->m_splitter->findView(editor)) {
892 EditorView *view = m_d->m_splitter->findView(editor)->view();
893 removeEditor(editor);
894 view->removeEditor(editor);
896 IEditor *newCurrent = view->currentEditor();
898 newCurrent = pickUnusedEditor();
900 activateEditor(view, newCurrent, NoActivate);
902 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
904 activateEditorForIndex(view, idx, NoActivate);
908 emit editorsClosed(QList<IEditor*>() << editor);
910 if (currentSplitterOrView) {
911 if (IEditor *currentEditor = currentSplitterOrView->editor())
912 activateEditor(currentSplitterOrView->view(), currentEditor);
916 Core::IEditor *EditorManager::pickUnusedEditor() const
918 foreach (IEditor *editor, openedEditors()) {
919 SplitterOrView *view = m_d->m_splitter->findView(editor);
920 if (!view || view->editor() != editor)
926 void EditorManager::activateEditorForIndex(const QModelIndex &index, OpenEditorFlags flags)
928 activateEditorForIndex(currentEditorView(), index, flags);
931 void EditorManager::activateEditorForIndex(Internal::EditorView *view, const QModelIndex &index, OpenEditorFlags flags)
934 IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
936 activateEditor(view, editor, flags);
940 QString fileName = index.data(Qt::UserRole + 1).toString();
941 QString id = index.data(Qt::UserRole + 2).toString();
942 if (!openEditor(view, fileName, id, flags))
943 m_d->m_editorModel->removeEditor(index);
946 Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
948 Q_ASSERT(view && editor);
950 if (view->currentEditor() && view->currentEditor()->file() == editor->file())
951 editor = view->currentEditor();
953 if (!view->hasEditor(editor)) {
954 bool duplicateSupported = editor->duplicateSupported();
955 if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) {
956 if (editor != sourceView->editor() || !duplicateSupported) {
957 sourceView->view()->removeEditor(editor);
958 view->addEditor(editor);
959 view->setCurrentEditor(editor);
960 if (!sourceView->editor()) {
961 if (IEditor *replacement = pickUnusedEditor()) {
962 sourceView->view()->addEditor(replacement);
966 } else if (duplicateSupported) {
967 editor = duplicateEditor(editor);
969 m_d->m_editorModel->makeOriginal(editor);
972 view->addEditor(editor);
977 void EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
979 SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor);
980 EditorView *view = (splitterOrView ? splitterOrView->view() : 0);
981 // TODO an IEditor doesn't have to belong to a view, which makes this method a bit funny
983 view = currentEditorView();
984 activateEditor(view, editor, flags);
987 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
992 if (!m_d->m_currentEditor)
993 setCurrentEditor(0, (flags & IgnoreNavigationHistory));
997 editor = placeEditor(view, editor);
999 if (!(flags & NoActivate)) {
1000 setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
1001 if (flags & ModeSwitch) {
1002 switchToPreferedMode();
1005 editor->widget()->setFocus();
1010 Core::IEditor *EditorManager::activateEditorForFile(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
1013 const QList<IEditor*> editors = editorsForFile(file);
1014 if (editors.isEmpty())
1017 activateEditor(view, editors.first(), flags);
1018 return editors.first();
1021 /* For something that has a 'QStringList mimeTypes' (IEditorFactory
1022 * or IExternalEditor), find the one best matching the mimetype passed in.
1023 * Recurse over the parent classes of the mimetype to find them. */
1024 template <class EditorFactoryLike>
1025 static void mimeTypeFactoryRecursion(const MimeDatabase *db,
1026 const MimeType &mimeType,
1027 const QList<EditorFactoryLike*> &allFactories,
1028 bool firstMatchOnly,
1029 QList<EditorFactoryLike*> *list)
1031 typedef typename QList<EditorFactoryLike*>::const_iterator EditorFactoryLikeListConstIterator;
1032 // Loop factories to find type
1033 const QString type = mimeType.type();
1034 const EditorFactoryLikeListConstIterator fcend = allFactories.constEnd();
1035 for (EditorFactoryLikeListConstIterator fit = allFactories.constBegin(); fit != fcend; ++fit) {
1036 // Exclude duplicates when recursing over xml or C++ -> C -> text.
1037 EditorFactoryLike *factory = *fit;
1038 if (!list->contains(factory) && factory->mimeTypes().contains(type)) {
1039 list->push_back(*fit);
1045 // Any parent mime type classes? -> recurse
1046 QStringList parentTypes = mimeType.subClassesOf();
1047 if (parentTypes.empty())
1049 const QStringList::const_iterator pcend = parentTypes .constEnd();
1050 for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) {
1051 if (const MimeType parent = db->findByType(*pit))
1052 mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list);
1056 EditorManager::EditorFactoryList
1057 EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
1059 EditorFactoryList rc;
1060 const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
1061 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc);
1062 if (debugEditorManager)
1063 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1067 EditorManager::ExternalEditorList
1068 EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const
1070 ExternalEditorList rc;
1071 const ExternalEditorList allEditors = pluginManager()->getObjects<IExternalEditor>();
1072 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allEditors, bestMatchOnly, &rc);
1073 if (debugEditorManager)
1074 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1078 /* For something that has a 'QString id' (IEditorFactory
1079 * or IExternalEditor), find the one matching a id. */
1080 template <class EditorFactoryLike>
1081 inline EditorFactoryLike *findById(ExtensionSystem::PluginManager *pm,
1084 const QList<EditorFactoryLike *> factories = pm->template getObjects<EditorFactoryLike>();
1085 foreach(EditorFactoryLike *efl, factories)
1086 if (id == efl->id())
1091 IEditor *EditorManager::createEditor(const QString &editorId,
1092 const QString &fileName)
1094 if (debugEditorManager)
1095 qDebug() << Q_FUNC_INFO << editorId << fileName;
1097 EditorFactoryList factories;
1098 if (editorId.isEmpty()) {
1099 const QFileInfo fileInfo(fileName);
1100 // Find by mime type
1101 MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(fileInfo);
1103 qWarning("%s unable to determine mime type of %s/%s. Falling back to text/plain",
1104 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1105 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain"));
1107 // open text files > 48 MB in binary editor
1108 if (fileInfo.size() > maxTextFileSize() && mimeType.type().startsWith(QLatin1String("text")))
1109 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("application/octet-stream"));
1110 factories = editorFactories(mimeType, true);
1112 // Find by editor id
1113 if (IEditorFactory *factory = findById<IEditorFactory>(pluginManager(), editorId))
1114 factories.push_back(factory);
1116 if (factories.empty()) {
1117 qWarning("%s: unable to find an editor factory for the file '%s', editor Id '%s'.",
1118 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1122 IEditor *editor = factories.front()->createEditor(this);
1124 connect(editor, SIGNAL(changed()), this, SLOT(handleEditorStateChange()));
1126 emit editorCreated(editor, fileName);
1130 void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
1134 m_d->m_core->addContextObject(editor);
1136 m_d->m_editorModel->addEditor(editor, isDuplicate);
1138 const bool isTemporary = editor->isTemporary();
1139 const bool addWatcher = !isTemporary;
1140 m_d->m_core->fileManager()->addFile(editor->file(), addWatcher);
1142 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName(),
1145 emit editorOpened(editor);
1148 // Run the OpenWithDialog and return the editor id
1149 // selected by the user.
1150 QString EditorManager::getOpenWithEditorId(const QString &fileName,
1151 bool *isExternalEditor) const
1153 // Collect editors that can open the file
1154 const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName);
1157 QStringList allEditorIds;
1158 QStringList externalEditorIds;
1160 const EditorFactoryList editors = editorFactories(mt, false);
1161 const int size = editors.size();
1162 for (int i = 0; i < size; i++) {
1163 allEditorIds.push_back(editors.at(i)->id());
1166 const ExternalEditorList exEditors = externalEditors(mt, false);
1167 const int esize = exEditors.size();
1168 for (int i = 0; i < esize; i++) {
1169 externalEditorIds.push_back(exEditors.at(i)->id());
1170 allEditorIds.push_back(exEditors.at(i)->id());
1172 if (allEditorIds.empty())
1175 OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
1176 dialog.setEditors(allEditorIds);
1177 dialog.setCurrentEditor(0);
1178 if (dialog.exec() != QDialog::Accepted)
1180 const QString selectedId = dialog.editor();
1181 if (isExternalEditor)
1182 *isExternalEditor = externalEditorIds.contains(selectedId);
1186 IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
1187 OpenEditorFlags flags, bool *newEditor)
1189 return openEditor(currentEditorView(), fileName, editorId, flags, newEditor);
1192 int extractLineNumber(QString *fileName)
1194 int i = fileName->length() - 1;
1195 for (; i >= 0; --i) {
1196 if (!fileName->at(i).isNumber())
1201 if (fileName->at(i) == ':' || fileName->at(i) == '+') {
1202 int result = fileName->mid(i+1).toInt();
1204 *fileName = fileName->left(i);
1211 static QString autoSaveName(const QString &fileName)
1213 return fileName + QLatin1String(".autosave");
1216 IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName,
1217 const QString &editorId, OpenEditorFlags flags, bool *newEditor)
1219 if (debugEditorManager)
1220 qDebug() << Q_FUNC_INFO << fileName << editorId;
1222 QString fn = fileName;
1223 int lineNumber = -1;
1224 if (flags && EditorManager::CanContainLineNumber)
1225 lineNumber = extractLineNumber(&fn);
1233 const QList<IEditor *> editors = editorsForFileName(fn);
1234 if (!editors.isEmpty()) {
1235 IEditor *editor = editors.first();
1236 if (flags && EditorManager::CanContainLineNumber)
1237 editor->gotoLine(lineNumber, -1);
1238 return activateEditor(view, editor, flags);
1241 QString realFn = autoSaveName(fn);
1243 QFileInfo rfi(realFn);
1244 if (!fi.exists() || !rfi.exists() || fi.lastModified() >= rfi.lastModified()) {
1245 QFile::remove(realFn);
1249 IEditor *editor = createEditor(editorId, fn);
1250 // If we could not open the file in the requested editor, fall
1251 // back to the default editor:
1253 editor = createEditor(QString(), fn);
1254 if (!editor) // Internal error
1257 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1258 QString errorString;
1259 if (!editor->open(&errorString, fn, realFn)) {
1260 QApplication::restoreOverrideCursor();
1261 QMessageBox::critical(m_d->m_core->mainWindow(), tr("File Error"), errorString);
1266 editor->file()->setRestoredFrom(realFn);
1272 IEditor *result = activateEditor(view, editor, flags);
1273 if (editor == result)
1274 restoreEditorState(editor);
1276 if (flags && EditorManager::CanContainLineNumber)
1277 editor->gotoLine(lineNumber, -1);
1279 QApplication::restoreOverrideCursor();
1283 bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorId)
1285 IExternalEditor *ee = findById<IExternalEditor>(pluginManager(), editorId);
1288 QString errorMessage;
1289 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1290 const bool ok = ee->startEditor(fileName, &errorMessage);
1291 QApplication::restoreOverrideCursor();
1293 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage);
1297 QStringList EditorManager::getOpenFileNames() const
1299 QString selectedFilter;
1300 const QString &fileFilters = m_d->m_core->mimeDatabase()->allFiltersString(&selectedFilter);
1301 return ICore::instance()->fileManager()->getOpenFileNames(fileFilters,
1302 QString(), &selectedFilter);
1306 /// Empty mode == figure out the correct mode from the editor
1307 /// forcePrefered = true, switch to the mode even if the editor is visible in another mode
1308 /// forcePrefered = false, only switch if it is not visible
1309 void EditorManager::switchToPreferedMode()
1311 QString preferedMode;
1312 // Figure out preferred mode for editor
1313 if (m_d->m_currentEditor)
1314 preferedMode = m_d->m_currentEditor->preferredModeType();
1316 if (preferedMode.isEmpty())
1317 preferedMode = Constants::MODE_EDIT_TYPE;
1319 m_d->m_core->modeManager()->activateModeType(preferedMode);
1322 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1323 QString *titlePattern,
1324 const QString &contents)
1326 if (debugEditorManager)
1327 qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1329 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1333 const QChar dollar = QLatin1Char('$');
1335 QString base = *titlePattern;
1337 base = QLatin1String("unnamed$");
1338 if (base.contains(dollar)) {
1340 QSet<QString> docnames;
1341 foreach (IEditor *editor, openedEditors()) {
1342 QString name = editor->file()->fileName();
1343 if (name.isEmpty()) {
1344 name = editor->displayName();
1346 name = QFileInfo(name).completeBaseName();
1353 title.replace(QString(dollar), QString::number(i++));
1354 } while (docnames.contains(title));
1356 title = *titlePattern;
1358 *titlePattern = title;
1361 IEditor *edt = createEditor(editorId, title);
1363 QApplication::restoreOverrideCursor();
1367 if (!edt->createNew(contents)) {
1368 QApplication::restoreOverrideCursor();
1374 if (title.isEmpty())
1375 title = edt->displayName();
1377 edt->setDisplayName(title);
1379 QApplication::restoreOverrideCursor();
1383 bool EditorManager::hasEditor(const QString &fileName) const
1385 return !editorsForFileName(fileName).isEmpty();
1388 void EditorManager::restoreEditorState(IEditor *editor)
1390 QTC_ASSERT(editor, return);
1391 QString fileName = editor->file()->fileName();
1392 editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1395 bool EditorManager::saveEditor(IEditor *editor)
1397 return saveFile(editor->file());
1400 bool EditorManager::saveFile(IFile *fileParam)
1402 IFile *file = fileParam;
1403 if (!file && currentEditor())
1404 file = currentEditor()->file();
1408 file->checkPermissions();
1410 const QString &fileName = file->fileName();
1412 if (fileName.isEmpty())
1413 return saveFileAs(file);
1415 bool success = false;
1418 // try saving, no matter what isReadOnly tells us
1419 success = m_d->m_core->fileManager()->saveFile(file, QString(), &isReadOnly);
1421 if (!success && isReadOnly) {
1422 MakeWritableResult answer =
1423 makeFileWritable(file);
1424 if (answer == Failed)
1426 if (answer == SavedAs)
1429 file->checkPermissions();
1431 success = m_d->m_core->fileManager()->saveFile(file);
1435 addFileToRecentFiles(file);
1441 void EditorManager::autoSave()
1444 // FIXME: the saving should be staggered
1445 foreach (IEditor *editor, openedEditors()) {
1446 IFile *file = editor->file();
1447 if (!file->isModified() || !file->shouldAutoSave())
1449 if (file->fileName().isEmpty()) // FIXME: save them to a dedicated directory
1451 QString errorString;
1452 if (!file->autoSave(&errorString, autoSaveName(file->fileName())))
1453 errors << errorString;
1455 if (!errors.isEmpty())
1456 QMessageBox::critical(m_d->m_core->mainWindow(), tr("File Error"),
1457 errors.join(QLatin1String("\n")));
1461 EditorManager::makeFileWritable(IFile *file)
1465 QString directory = QFileInfo(file->fileName()).absolutePath();
1466 IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory);
1467 const QString &fileName = file->fileName();
1469 switch (FileManager::promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), file->isSaveAsAllowed())) {
1470 case FileManager::RO_OpenVCS:
1471 if (!versionControl->vcsOpen(fileName)) {
1472 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC."));
1475 file->checkPermissions();
1476 return OpenedWithVersionControl;
1477 case FileManager::RO_MakeWriteable: {
1478 const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1480 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
1484 file->checkPermissions();
1485 return MadeWritable;
1486 case FileManager::RO_SaveAs :
1487 return saveFileAs(file) ? SavedAs : Failed;
1488 case FileManager::RO_Cancel:
1494 bool EditorManager::saveFileAs(IFile *fileParam)
1496 IFile *file = fileParam;
1497 if (!file && currentEditor())
1498 file = currentEditor()->file();
1502 const QString &filter = m_d->m_core->mimeDatabase()->allFiltersString();
1503 QString selectedFilter =
1504 m_d->m_core->mimeDatabase()->findByFile(QFileInfo(file->fileName())).filterString();
1505 const QString &absoluteFilePath =
1506 m_d->m_core->fileManager()->getSaveAsFileName(file, filter, &selectedFilter);
1508 if (absoluteFilePath.isEmpty())
1511 if (absoluteFilePath != file->fileName()) {
1512 // close existing editors for the new file name
1513 const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1514 if (!existList.isEmpty()) {
1515 closeEditors(existList, false);
1519 const bool success = m_d->m_core->fileManager()->saveFile(file, absoluteFilePath);
1520 file->checkPermissions();
1522 // @todo: There is an issue to be treated here. The new file might be of a different mime
1523 // type than the original and thus require a different editor. An alternative strategy
1524 // would be to close the current editor and open a new appropriate one, but this is not
1525 // a good way out either (also the undo stack would be lost). Perhaps the best is to
1526 // re-think part of the editors design.
1529 addFileToRecentFiles(file);
1535 /* Adds the file name to the recent files if there is at least one non-temporary editor for it */
1536 void EditorManager::addFileToRecentFiles(IFile *file)
1538 bool isTemporary = true;
1540 QList<IEditor *> editors = editorsForFile(file);
1541 foreach (IEditor *editor, editors) {
1542 if (!editor->isTemporary()) {
1543 editorId = editor->id();
1544 isTemporary = false;
1549 m_d->m_core->fileManager()->addToRecentFiles(file->fileName(), editorId);
1552 void EditorManager::gotoNextDocHistory()
1554 OpenEditorsWindow *dialog = windowPopup();
1555 if (dialog->isVisible()) {
1556 dialog->selectNextEditor();
1558 EditorView *view = currentEditorView();
1559 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1560 dialog->selectNextEditor();
1561 showPopupOrSelectDocument();
1565 void EditorManager::gotoPreviousDocHistory()
1567 OpenEditorsWindow *dialog = windowPopup();
1568 if (dialog->isVisible()) {
1569 dialog->selectPreviousEditor();
1571 EditorView *view = currentEditorView();
1572 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1573 dialog->selectPreviousEditor();
1574 showPopupOrSelectDocument();
1578 void EditorManager::makeCurrentEditorWritable()
1580 if (IEditor* curEditor = currentEditor())
1581 makeFileWritable(curEditor->file());
1584 void EditorManager::updateWindowTitle()
1586 QString windowTitle = tr("Qt Creator");
1587 if (!m_d->m_titleAddition.isEmpty()) {
1588 windowTitle.prepend(m_d->m_titleAddition + " - ");
1590 IEditor *curEditor = currentEditor();
1592 QString editorName = curEditor->displayName();
1593 if (!editorName.isEmpty())
1594 windowTitle.prepend(editorName + " - ");
1595 QString filePath = QFileInfo(curEditor->file()->fileName()).absoluteFilePath();
1596 if (!filePath.isEmpty())
1597 m_d->m_core->mainWindow()->setWindowFilePath(filePath);
1599 m_d->m_core->mainWindow()->setWindowFilePath(QString());
1601 m_d->m_core->mainWindow()->setWindowTitle(windowTitle);
1604 void EditorManager::handleEditorStateChange()
1607 IEditor *theEditor = qobject_cast<IEditor *>(sender());
1608 if (!theEditor->file()->isModified())
1609 theEditor->file()->removeAutoSaveFile();
1610 IEditor *currEditor = currentEditor();
1611 if (theEditor == currEditor) {
1612 updateWindowTitle();
1613 emit currentEditorStateChanged(currEditor);
1617 void EditorManager::updateActions()
1620 IEditor *curEditor = currentEditor();
1621 int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1625 if (!curEditor->file()->fileName().isEmpty()) {
1626 QFileInfo fi(curEditor->file()->fileName());
1627 fName = fi.fileName();
1629 fName = curEditor->displayName();
1633 window()->setWindowModified(curEditor->file()->isModified());
1635 bool ww = curEditor->file()->isModified() && curEditor->file()->isReadOnly();
1636 if (ww != curEditor->file()->hasWriteWarning()) {
1637 curEditor->file()->setWriteWarning(ww);
1639 // we are about to change a read-only file, warn user
1640 InfoBarEntry info(QLatin1String("Core.EditorManager.MakeWritable"),
1641 tr("<b>Warning:</b> You are changing a read-only file."));
1642 info.setCustomButtonInfo(tr("Make writable"), this, SLOT(makeCurrentEditorWritable()));
1643 curEditor->file()->infoBar()->addInfo(info);
1645 curEditor->file()->infoBar()->removeInfo(QLatin1String("Core.EditorManager.MakeWritable"));
1649 } else { // curEditor
1650 window()->setWindowModified(false);
1654 m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified());
1655 m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed());
1656 m_d->m_revertToSavedAction->setEnabled(curEditor != 0
1657 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified());
1660 if (!fName.isEmpty())
1661 quotedName = '"' + fName + '"';
1663 m_d->m_saveAsAction->setText(tr("Save %1 &As...").arg(quotedName));
1664 m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName));
1665 m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName));
1667 m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0);
1668 m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName));
1669 m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0);
1670 m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1);
1671 m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others")));
1673 m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1674 m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1675 EditorView *view = currentEditorView();
1676 m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
1677 m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
1679 bool hasSplitter = m_d->m_splitter->isSplitter();
1680 m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter);
1681 m_d->m_removeAllSplitsAction->setEnabled(hasSplitter);
1682 m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter);
1685 bool EditorManager::hasSplitter() const
1687 return m_d->m_splitter->isSplitter();
1690 QList<IEditor*> EditorManager::visibleEditors() const
1692 QList<IEditor *> editors;
1693 if (m_d->m_splitter->isSplitter()) {
1694 SplitterOrView *firstView = m_d->m_splitter->findFirstView();
1695 SplitterOrView *view = firstView;
1699 editors.append(view->editor());
1700 view = m_d->m_splitter->findNextView(view);
1701 } while (view && view != firstView);
1704 if (m_d->m_splitter->editor()) {
1705 editors.append(m_d->m_splitter->editor());
1711 QList<IEditor*> EditorManager::openedEditors() const
1713 return m_d->m_editorModel->editors();
1716 OpenEditorsModel *EditorManager::openedEditorsModel() const
1718 return m_d->m_editorModel;
1721 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1723 currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1727 void EditorManager::cutForwardNavigationHistory()
1729 currentEditorView()->cutForwardNavigationHistory();
1733 void EditorManager::goBackInNavigationHistory()
1735 currentEditorView()->goBackInNavigationHistory();
1740 void EditorManager::goForwardInNavigationHistory()
1742 currentEditorView()->goForwardInNavigationHistory();
1746 OpenEditorsWindow *EditorManager::windowPopup() const
1748 return m_d->m_windowPopup;
1751 void EditorManager::showPopupOrSelectDocument() const
1753 if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1754 windowPopup()->selectAndHide();
1756 // EditorManager is invisible when invoked from Design Mode.
1757 const QPoint p = isVisible() ?
1758 mapToGlobal(QPoint(0, 0)) :
1759 m_d->m_core->mainWindow()->mapToGlobal(QPoint(0, 0));
1760 windowPopup()->move((width()-m_d->m_windowPopup->width())/2 + p.x(),
1761 (height()-m_d->m_windowPopup->height())/2 + p.y());
1762 windowPopup()->setVisible(true);
1766 // Save state of all non-teporary editors.
1767 QByteArray EditorManager::saveState() const
1770 QDataStream stream(&bytes, QIODevice::WriteOnly);
1772 stream << QByteArray("EditorManagerV4");
1774 QList<IEditor *> editors = openedEditors();
1775 foreach (IEditor *editor, editors) {
1776 if (!editor->file()->fileName().isEmpty()
1777 && !editor->isTemporary()) {
1778 QByteArray state = editor->saveState();
1779 if (!state.isEmpty())
1780 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
1784 stream << m_d->m_editorStates;
1786 QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1787 int entriesCount = 0;
1788 foreach (const OpenEditorsModel::Entry &entry, entries) {
1789 // The editor may be 0 if it was not loaded yet: In that case it is not temporary
1790 if (!entry.editor || !entry.editor->isTemporary())
1794 stream << entriesCount;
1796 foreach (const OpenEditorsModel::Entry &entry, entries) {
1797 if (!entry.editor || !entry.editor->isTemporary())
1798 stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1801 stream << m_d->m_splitter->saveState();
1806 bool EditorManager::restoreState(const QByteArray &state)
1808 closeAllEditors(true);
1810 QDataStream stream(state);
1815 if (version != "EditorManagerV4")
1818 QApplication::setOverrideCursor(Qt::WaitCursor);
1820 stream >> m_d->m_editorStates;
1822 int editorCount = 0;
1823 stream >> editorCount;
1824 while (--editorCount >= 0) {
1827 QString displayName;
1828 stream >> displayName;
1832 if (!fileName.isEmpty() && !displayName.isEmpty()) {
1833 QFileInfo fi(fileName);
1836 QFileInfo rfi(autoSaveName(fileName));
1837 if (rfi.exists() && fi.lastModified() < rfi.lastModified()) {
1838 openEditor(fileName, QString::fromUtf8(id));
1840 m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1845 QByteArray splitterstates;
1846 stream >> splitterstates;
1847 m_d->m_splitter->restoreState(splitterstates);
1849 // splitting and stuff results in focus trouble, that's why we set the focus again after restoration
1850 if (m_d->m_currentEditor) {
1851 m_d->m_currentEditor->widget()->setFocus();
1852 } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) {
1853 if (IEditor *e = view->editor())
1854 e->widget()->setFocus();
1855 else if (view->view())
1856 view->view()->setFocus();
1859 QApplication::restoreOverrideCursor();
1864 static const char documentStatesKey[] = "EditorManager/DocumentStates";
1865 static const char reloadBehaviorKey[] = "EditorManager/ReloadBehavior";
1866 static const char autoSaveEnabledKey[] = "EditorManager/AutoSaveEnabled";
1867 static const char autoSaveIntervalKey[] = "EditorManager/AutoSaveInterval";
1869 void EditorManager::saveSettings()
1871 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1872 settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates);
1873 settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadSetting);
1874 settings->setValue(QLatin1String(autoSaveEnabledKey), m_d->m_autoSaveEnabled);
1875 settings->setValue(QLatin1String(autoSaveIntervalKey), m_d->m_autoSaveInterval);
1878 void EditorManager::readSettings()
1880 // Backward compatibility to old locations for these settings
1881 QSettings *qs = m_d->m_core->settings();
1882 if (qs->contains(QLatin1String(documentStatesKey))) {
1883 m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey))
1884 .value<QMap<QString, QVariant> >();
1885 qs->remove(QLatin1String(documentStatesKey));
1888 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1889 if (settings->contains(QLatin1String(documentStatesKey)))
1890 m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey))
1891 .value<QMap<QString, QVariant> >();
1893 if (settings->contains(QLatin1String(reloadBehaviorKey)))
1894 m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1896 if (settings->contains(QLatin1String(autoSaveEnabledKey))) {
1897 m_d->m_autoSaveEnabled = settings->value(QLatin1String(autoSaveEnabledKey)).toBool();
1898 m_d->m_autoSaveInterval = settings->value(QLatin1String(autoSaveIntervalKey)).toInt();
1904 void EditorManager::revertToSaved()
1906 IEditor *currEditor = currentEditor();
1909 const QString fileName = currEditor->file()->fileName();
1910 if (fileName.isEmpty())
1912 if (currEditor->file()->isModified()) {
1913 QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"),
1914 tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)),
1915 QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow());
1916 msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
1917 msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
1918 msgBox.setDefaultButton(QMessageBox::No);
1919 msgBox.setEscapeButton(QMessageBox::No);
1920 if (msgBox.exec() == QMessageBox::No)
1924 QString errorString;
1925 if (!currEditor->file()->reload(&errorString, IFile::FlagReload, IFile::TypeContents))
1926 QMessageBox::critical(m_d->m_core->mainWindow(), tr("File Error"), errorString);
1929 void EditorManager::showEditorStatusBar(const QString &id,
1930 const QString &infoText,
1931 const QString &buttonText,
1932 QObject *object, const char *member)
1935 currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1938 void EditorManager::hideEditorStatusBar(const QString &id)
1940 currentEditorView()->hideEditorStatusBar(id);
1943 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1945 m_d->m_reloadSetting = behavior;
1948 IFile::ReloadSetting EditorManager::reloadSetting() const
1950 return m_d->m_reloadSetting;
1953 void EditorManager::setAutoSaveEnabled(bool enabled)
1955 m_d->m_autoSaveEnabled = enabled;
1959 bool EditorManager::autoSaveEnabled() const
1961 return m_d->m_autoSaveEnabled;
1964 void EditorManager::setAutoSaveInterval(int interval)
1966 m_d->m_autoSaveInterval = interval;
1970 int EditorManager::autoSaveInterval() const
1972 return m_d->m_autoSaveInterval;
1975 QTextCodec *EditorManager::defaultTextCodec() const
1977 QSettings *settings = Core::ICore::instance()->settings();
1978 if (QTextCodec *candidate = QTextCodec::codecForName(
1979 settings->value(QLatin1String(Constants::SETTINGS_DEFAULTTEXTENCODING)).toByteArray()))
1981 return QTextCodec::codecForLocale();
1984 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
1986 if (!editor->duplicateSupported())
1989 IEditor *duplicate = editor->duplicate(0);
1990 duplicate->restoreState(editor->saveState());
1991 connect(duplicate, SIGNAL(changed()), this, SLOT(handleEditorStateChange()));
1992 emit editorCreated(duplicate, duplicate->file()->fileName());
1993 addEditor(duplicate, true);
1997 void EditorManager::split(Qt::Orientation orientation)
1999 SplitterOrView *view = m_d->m_currentView;
2001 view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor)
2002 : m_d->m_splitter->findFirstView();
2003 if (view && !view->splitter()) {
2004 view->split(orientation);
2009 void EditorManager::split()
2011 split(Qt::Vertical);
2014 void EditorManager::splitSideBySide()
2016 split(Qt::Horizontal);
2019 void EditorManager::removeCurrentSplit()
2021 SplitterOrView *viewToClose = m_d->m_currentView;
2022 if (!viewToClose && m_d->m_currentEditor)
2023 viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor);
2025 if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
2028 closeView(viewToClose->view());
2032 void EditorManager::removeAllSplits()
2034 if (!m_d->m_splitter->isSplitter())
2036 IEditor *editor = m_d->m_currentEditor;
2037 // trigger update below
2038 m_d->m_currentEditor = 0;
2039 if (editor && m_d->m_editorModel->isDuplicate(editor))
2040 m_d->m_editorModel->makeOriginal(editor);
2041 m_d->m_splitter->unsplitAll();
2043 editor = pickUnusedEditor();
2044 activateEditor(editor);
2047 void EditorManager::gotoOtherSplit()
2049 if (m_d->m_splitter->isSplitter()) {
2050 SplitterOrView *currentView = m_d->m_currentView;
2051 if (!currentView && m_d->m_currentEditor)
2052 currentView = m_d->m_splitter->findView(m_d->m_currentEditor);
2054 currentView = m_d->m_splitter->findFirstView();
2055 SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
2057 view = m_d->m_splitter->findFirstView();
2059 if (IEditor *editor = view->editor()) {
2060 setCurrentEditor(editor, true);
2061 editor->widget()->setFocus();
2063 setCurrentView(view);
2069 qint64 EditorManager::maxTextFileSize()
2071 return (qint64(3) << 24);
2074 void EditorManager::setWindowTitleAddition(const QString &addition)
2076 m_d->m_titleAddition = addition;
2077 updateWindowTitle();
2080 QString EditorManager::windowTitleAddition() const
2082 return m_d->m_titleAddition;
2085 void EditorManager::updateVariable(const QString &variable)
2087 if (variable == QLatin1String(kCurrentDocumentFilePath)
2088 || variable == QLatin1String(kCurrentDocumentPath)) {
2090 IEditor *curEditor = currentEditor();
2092 QString fileName = curEditor->file()->fileName();
2093 if (!fileName.isEmpty()) {
2094 if (variable == QLatin1String(kCurrentDocumentFilePath))
2095 value = QFileInfo(fileName).filePath();
2096 else if (variable == QLatin1String(kCurrentDocumentPath))
2097 value = QFileInfo(fileName).path();
2100 VariableManager::instance()->insert(variable, value);
2101 } else if (variable == QLatin1String(kCurrentDocumentXPos)) {
2103 IEditor *curEditor = currentEditor();
2105 value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).x());
2107 VariableManager::instance()->insert(variable, value);
2108 } else if (variable == QLatin1String(kCurrentDocumentYPos)) {
2110 IEditor *curEditor = currentEditor();
2112 value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).y());
2114 VariableManager::instance()->insert(variable, value);