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/imode.h>
57 #include <coreplugin/settingsdatabase.h>
58 #include <coreplugin/variablemanager.h>
59 #include <coreplugin/uniqueidmanager.h>
61 #include <extensionsystem/pluginmanager.h>
63 #include <utils/consoleprocess.h>
64 #include <utils/qtcassert.h>
66 #include <QtCore/QDebug>
67 #include <QtCore/QFileInfo>
68 #include <QtCore/QMap>
69 #include <QtCore/QProcess>
70 #include <QtCore/QSet>
71 #include <QtCore/QSettings>
72 #include <QtCore/QTextCodec>
74 #include <QtGui/QAction>
75 #include <QtGui/QShortcut>
76 #include <QtGui/QApplication>
77 #include <QtGui/QFileDialog>
78 #include <QtGui/QLayout>
79 #include <QtGui/QMainWindow>
80 #include <QtGui/QMenu>
81 #include <QtGui/QMessageBox>
82 #include <QtGui/QPushButton>
83 #include <QtGui/QSplitter>
84 #include <QtGui/QStackedLayout>
86 enum { debugEditorManager=0 };
88 static const char kCurrentDocumentFilePath[] = "CurrentDocument:FilePath";
89 static const char kCurrentDocumentPath[] = "CurrentDocument:Path";
90 static const char kCurrentDocumentXPos[] = "CurrentDocument:XPos";
91 static const char kCurrentDocumentYPos[] = "CurrentDocument:YPos";
93 static inline ExtensionSystem::PluginManager *pluginManager()
95 return ExtensionSystem::PluginManager::instance();
98 //===================EditorClosingCoreListener======================
103 class EditorClosingCoreListener : public ICoreListener
106 EditorClosingCoreListener(EditorManager *em);
107 bool editorAboutToClose(IEditor *editor);
108 bool coreAboutToClose();
114 EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em)
119 bool EditorClosingCoreListener::editorAboutToClose(IEditor *)
124 bool EditorClosingCoreListener::coreAboutToClose()
126 // Do not ask for files to save.
127 // MainWindow::closeEvent has already done that.
128 return m_em->closeAllEditors(false);
131 } // namespace Internal
134 using namespace Core;
135 using namespace Core::Internal;
136 using namespace Utils;
138 //===================EditorManager=====================
140 EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;
142 EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
143 : QWidget(parent), m_mode(mode)
145 setLayout(new QVBoxLayout);
146 layout()->setMargin(0);
147 connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)),
148 this, SLOT(currentModeChanged(Core::IMode *)));
150 currentModeChanged(Core::ModeManager::instance()->currentMode());
153 EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
155 if (m_current == this) {
156 EditorManager::instance()->setParent(0);
157 EditorManager::instance()->hide();
161 void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
163 if (m_current == this) {
165 EditorManager::instance()->setParent(0);
166 EditorManager::instance()->hide();
168 if (m_mode == mode) {
170 layout()->addWidget(EditorManager::instance());
171 EditorManager::instance()->show();
175 EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
180 // ---------------- EditorManager
185 struct EditorManagerPrivate {
186 explicit EditorManagerPrivate(ICore *core, QWidget *parent);
187 ~EditorManagerPrivate();
188 Internal::EditorView *m_view;
189 Internal::SplitterOrView *m_splitter;
190 QPointer<IEditor> m_currentEditor;
191 QPointer<SplitterOrView> m_currentView;
197 QAction *m_revertToSavedAction;
198 QAction *m_saveAction;
199 QAction *m_saveAsAction;
200 QAction *m_closeCurrentEditorAction;
201 QAction *m_closeAllEditorsAction;
202 QAction *m_closeOtherEditorsAction;
203 QAction *m_gotoNextDocHistoryAction;
204 QAction *m_gotoPreviousDocHistoryAction;
205 QAction *m_goBackAction;
206 QAction *m_goForwardAction;
207 QAction *m_splitAction;
208 QAction *m_splitSideBySideAction;
209 QAction *m_removeCurrentSplitAction;
210 QAction *m_removeAllSplitsAction;
211 QAction *m_gotoOtherSplitAction;
213 Internal::OpenEditorsWindow *m_windowPopup;
214 Internal::EditorClosingCoreListener *m_coreListener;
216 QMap<QString, QVariant> m_editorStates;
217 Internal::OpenEditorsViewFactory *m_openEditorsFactory;
219 OpenEditorsModel *m_editorModel;
221 IFile::ReloadSetting m_reloadSetting;
223 QString m_titleAddition;
227 EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
231 m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)),
232 m_saveAction(new QAction(parent)),
233 m_saveAsAction(new QAction(parent)),
234 m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)),
235 m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)),
236 m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
237 m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
238 m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
239 m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)),
240 m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)),
243 m_reloadSetting(IFile::AlwaysAsk)
245 m_editorModel = new OpenEditorsModel(parent);
248 EditorManagerPrivate::~EditorManagerPrivate()
250 // clearNavigationHistory();
253 EditorManager *EditorManager::m_instance = 0;
255 static Command *createSeparator(ActionManager *am, QObject *parent,
257 const Context &context)
259 QAction *tmpaction = new QAction(parent);
260 tmpaction->setSeparator(true);
261 Command *cmd = am->registerAction(tmpaction, name, context);
265 EditorManager::EditorManager(ICore *core, QWidget *parent) :
267 m_d(new EditorManagerPrivate(core, parent))
271 connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)),
272 this, SLOT(handleContextChange(Core::IContext *)));
274 const Context editManagerContext(Constants::C_EDITORMANAGER);
275 // combined context for edit & design modes
276 const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
278 ActionManager *am = m_d->m_core->actionManager();
279 ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
282 m_d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
283 Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
284 Constants::REVERTTOSAVED, editManagerContext);
285 cmd->setAttribute(Command::CA_UpdateText);
286 cmd->setDefaultText(tr("Revert File to Saved"));
287 mfile->addAction(cmd, Constants::G_FILE_SAVE);
288 connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));
291 am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
292 connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
295 am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
296 connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));
299 ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
301 // Window menu separators
302 QAction *tmpaction = new QAction(this);
303 tmpaction->setSeparator(true);
304 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Split", editManagerContext);
305 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
307 tmpaction = new QAction(this);
308 tmpaction->setSeparator(true);
309 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Navigate", editManagerContext);
310 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
313 cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext, true);
314 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
315 cmd->setAttribute(Core::Command::CA_UpdateText);
316 cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
317 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
318 connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));
321 // workaround for QTCREATORBUG-72
322 QShortcut *sc = new QShortcut(parent);
323 cmd = am->registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
324 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
325 cmd->setDefaultText(EditorManager::tr("Close"));
326 connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
330 cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext, true);
331 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W")));
332 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
333 connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));
335 // Close All Others Action
336 cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext, true);
337 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
338 cmd->setAttribute(Core::Command::CA_UpdateText);
339 connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors()));
341 // Goto Previous In History Action
342 cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
344 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
346 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
348 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
349 connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
351 // Goto Next In History Action
352 cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
354 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
356 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
358 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
359 connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
361 // Go back in navigation history
362 cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
364 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left")));
366 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left")));
368 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
369 connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
371 // Go forward in navigation history
372 cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
374 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right")));
376 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right")));
378 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
379 connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
382 QString prefix = tr("Meta+E");
384 QString prefix = tr("Ctrl+E");
387 m_d->m_splitAction = new QAction(tr("Split"), this);
388 cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
389 cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix)));
390 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
391 connect(m_d->m_splitAction, SIGNAL(triggered()), this, SLOT(split()));
393 m_d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this);
394 cmd = am->registerAction(m_d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext);
395 cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix)));
396 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
397 connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));
399 m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
400 cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
401 cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
402 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
403 connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
405 m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
406 cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
407 cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
408 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
409 connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
411 m_d->m_gotoOtherSplitAction = new QAction(tr("Go to Next Split"), this);
412 cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext);
413 cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix)));
414 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
415 connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit()));
417 ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
418 ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
419 medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
420 advancedMenu->menu()->setTitle(tr("Ad&vanced"));
421 advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
422 advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
423 advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
424 advancedMenu->appendGroup(Constants::G_EDIT_FONT);
425 advancedMenu->appendGroup(Constants::G_EDIT_EDITOR);
427 // Advanced menu separators
428 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Collapsing"), editManagerContext);
429 advancedMenu->addAction(cmd, Constants::G_EDIT_COLLAPSING);
430 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext);
431 advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS);
432 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Font"), editManagerContext);
433 advancedMenu->addAction(cmd, Constants::G_EDIT_FONT);
434 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Editor"), editManagerContext);
435 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
438 m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
439 m_d->m_view = m_d->m_splitter->view();
442 QHBoxLayout *layout = new QHBoxLayout(this);
443 layout->setMargin(0);
444 layout->setSpacing(0);
445 layout->addWidget(m_d->m_splitter);
449 m_d->m_windowPopup = new OpenEditorsWindow(this);
452 EditorManager::~EditorManager()
456 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
457 if (m_d->m_coreListener) {
458 pm->removeObject(m_d->m_coreListener);
459 delete m_d->m_coreListener;
461 pm->removeObject(m_d->m_openEditorsFactory);
462 delete m_d->m_openEditorsFactory;
467 void EditorManager::init()
469 m_d->m_coreListener = new EditorClosingCoreListener(this);
470 pluginManager()->addObject(m_d->m_coreListener);
472 m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
473 pluginManager()->addObject(m_d->m_openEditorsFactory);
475 VariableManager *vm = VariableManager::instance();
476 vm->registerVariable(QLatin1String(kCurrentDocumentFilePath),
477 tr("Full path of the current document including file name."));
478 vm->registerVariable(QLatin1String(kCurrentDocumentPath),
479 tr("Full path of the current document excluding file name."));
480 vm->registerVariable(QLatin1String(kCurrentDocumentXPos),
481 tr("X-coordinate of the current editor's upper left corner, relative to screen."));
482 vm->registerVariable(QLatin1String(kCurrentDocumentYPos),
483 tr("Y-coordinate of the current editor's upper left corner, relative to screen."));
484 connect(vm, SIGNAL(variableUpdateRequested(QString)),
485 this, SLOT(updateVariable(QString)));
489 EditorToolBar *EditorManager::createToolBar(QWidget *parent)
491 return new EditorToolBar(parent);
494 void EditorManager::removeEditor(IEditor *editor)
496 bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
497 m_d->m_editorModel->removeEditor(editor);
499 m_d->m_core->fileManager()->removeFile(editor->file());
501 m_d->m_core->removeContextObject(editor);
504 void EditorManager::handleContextChange(Core::IContext *context)
506 if (debugEditorManager)
507 qDebug() << Q_FUNC_INFO;
508 IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
510 setCurrentEditor(editor);
516 void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
521 if (m_d->m_currentEditor == editor)
523 if (m_d->m_currentEditor && !ignoreNavigationHistory)
524 addCurrentPositionToNavigationHistory();
526 m_d->m_currentEditor = editor;
528 if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor))
529 splitterOrView->view()->setCurrentEditor(editor);
530 m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history
534 emit currentEditorChanged(editor);
538 void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view)
540 if (view == m_d->m_currentView)
543 SplitterOrView *old = m_d->m_currentView;
544 m_d->m_currentView = view;
551 if (view && !view->editor())
555 Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
557 SplitterOrView *view = m_d->m_currentView;
559 view = m_d->m_currentEditor?
560 m_d->m_splitter->findView(m_d->m_currentEditor):
561 m_d->m_splitter->findFirstView();
563 return m_d->m_splitter;
567 Core::Internal::EditorView *EditorManager::currentEditorView() const
569 return currentSplitterOrView()->view();
572 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
574 QList<IEditor *> found;
575 QString fixedname = FileManager::fixFileName(filename, FileManager::KeepLinks);
576 foreach (IEditor *editor, openedEditors()) {
577 if (fixedname == FileManager::fixFileName(editor->file()->fileName(), FileManager::KeepLinks))
583 QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
585 QList<IEditor *> found;
586 foreach (IEditor *editor, openedEditors()) {
587 if (editor->file() == file)
593 IEditor *EditorManager::currentEditor() const
595 return m_d->m_currentEditor;
598 void EditorManager::emptyView(Core::Internal::EditorView *view)
603 QList<IEditor *> editors = view->editors();
604 foreach (IEditor *editor, editors) {
605 if (!m_d->m_editorModel->isDuplicate(editor)) {
606 editors.removeAll(editor);
607 view->removeEditor(editor);
610 emit editorAboutToClose(editor);
611 removeEditor(editor);
612 view->removeEditor(editor);
614 emit editorsClosed(editors);
615 foreach (IEditor *editor, editors) {
620 void EditorManager::closeView(Core::Internal::EditorView *view)
625 if (view == m_d->m_view) {
626 if (IEditor *e = view->currentEditor())
627 closeEditors(QList<IEditor *>() << e);
631 if (IEditor *e = view->currentEditor()) {
633 when we are closing a view with an original editor which has
634 duplicates, then make one of the duplicates the original.
635 Otherwise the original would be kept around and the user might
636 experience jumping to a missleading position within the file when
637 visiting the file again. With the code below, the position within
638 the file will be the position of the first duplicate which is still
641 if (!m_d->m_editorModel->isDuplicate(e)) {
642 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(e);
643 if (!duplicates.isEmpty()) {
644 m_d->m_editorModel->makeOriginal(duplicates.first());
651 SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
652 Q_ASSERT(splitterOrView);
653 Q_ASSERT(splitterOrView->view() == view);
654 SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
655 Q_ASSERT(splitterOrView->hasEditors() == false);
656 splitterOrView->hide();
657 delete splitterOrView;
661 SplitterOrView *newCurrent = splitter->findFirstView();
663 if (IEditor *e = newCurrent->editor()) {
664 activateEditor(newCurrent->view(), e);
666 setCurrentView(newCurrent);
672 EditorManager::editorsForFiles(QList<IFile*> files) const
674 const QList<IEditor *> editors = openedEditors();
675 QSet<IEditor *> found;
676 foreach (IFile *file, files) {
677 foreach (IEditor *editor, editors) {
678 if (editor->file() == file && !found.contains(editor)) {
683 return found.toList();
686 QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
688 QSet<IEditor *> handledEditors;
689 QList<IFile *> files;
690 foreach (IEditor *editor, editors) {
691 if (!handledEditors.contains(editor)) {
692 files << editor->file();
693 handledEditors.insert(editor);
699 bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
701 m_d->m_editorModel->removeAllRestoredEditors();
702 if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
703 // m_d->clearNavigationHistory();
709 void EditorManager::closeOtherEditors(IEditor *editor)
711 m_d->m_editorModel->removeAllRestoredEditors();
712 QList<IEditor*> editors = openedEditors();
713 editors.removeAll(editor);
714 closeEditors(editors, true);
717 void EditorManager::closeOtherEditors()
719 IEditor *current = currentEditor();
720 QTC_ASSERT(current, return);
721 closeOtherEditors(current);
724 // SLOT connected to action
725 void EditorManager::closeEditor()
727 if (!m_d->m_currentEditor)
729 addCurrentPositionToNavigationHistory();
730 closeEditor(m_d->m_currentEditor);
733 void EditorManager::closeEditor(Core::IEditor *editor)
737 closeEditors(QList<IEditor *>() << editor);
740 void EditorManager::closeEditor(const QModelIndex &index)
742 IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
746 m_d->m_editorModel->removeEditor(index);
749 bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
751 if (editorsToClose.isEmpty())
754 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
756 bool closingFailed = false;
757 QList<IEditor*> acceptedEditors;
758 //ask all core listeners to check whether the editor can be closed
759 const QList<ICoreListener *> listeners =
760 pluginManager()->getObjects<ICoreListener>();
761 foreach (IEditor *editor, editorsToClose) {
762 bool editorAccepted = true;
763 if (m_d->m_editorModel->isDuplicate(editor))
764 editor = m_d->m_editorModel->originalForDuplicate(editor);
765 foreach (ICoreListener *listener, listeners) {
766 if (!listener->editorAboutToClose(editor)) {
767 editorAccepted = false;
768 closingFailed = true;
773 acceptedEditors.append(editor);
775 if (acceptedEditors.isEmpty())
777 //ask whether to save modified files
778 if (askAboutModifiedEditors) {
779 bool cancelled = false;
780 QList<IFile*> list = m_d->m_core->fileManager()->
781 saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
784 if (!list.isEmpty()) {
785 closingFailed = true;
786 QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
787 acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
790 if (acceptedEditors.isEmpty())
794 foreach(IEditor *editor, acceptedEditors)
795 acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);
797 QList<EditorView*> closedViews;
799 // remove the editors
800 foreach (IEditor *editor, acceptedEditors) {
801 emit editorAboutToClose(editor);
802 if (!editor->file()->fileName().isEmpty()
803 && !editor->isTemporary()) {
804 QByteArray state = editor->saveState();
805 if (!state.isEmpty())
806 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
809 removeEditor(editor);
810 if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
811 if (editor == view->view()->currentEditor())
812 closedViews += view->view();
813 view->view()->removeEditor(editor);
817 foreach (EditorView *view, closedViews) {
818 IEditor *newCurrent = view->currentEditor();
820 newCurrent = pickUnusedEditor();
822 activateEditor(view, newCurrent, NoActivate);
824 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
826 activateEditorForIndex(view, idx, NoActivate);
830 emit editorsClosed(acceptedEditors);
832 foreach (IEditor *editor, acceptedEditors) {
836 if (currentSplitterOrView) {
837 if (IEditor *editor = currentSplitterOrView->editor())
838 activateEditor(currentSplitterOrView->view(), editor);
841 if (!currentEditor()) {
842 emit currentEditorChanged(0);
847 return !closingFailed;
850 void EditorManager::closeDuplicate(Core::IEditor *editor)
853 IEditor *original = editor;
854 if (m_d->m_editorModel->isDuplicate(editor))
855 original= m_d->m_editorModel->originalForDuplicate(editor);
856 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(original);
858 if (duplicates.isEmpty()) {
863 if (original== editor)
864 m_d->m_editorModel->makeOriginal(duplicates.first());
866 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
868 emit editorAboutToClose(editor);
870 if(m_d->m_splitter->findView(editor)) {
871 EditorView *view = m_d->m_splitter->findView(editor)->view();
872 removeEditor(editor);
873 view->removeEditor(editor);
875 IEditor *newCurrent = view->currentEditor();
877 newCurrent = pickUnusedEditor();
879 activateEditor(view, newCurrent, NoActivate);
881 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
883 activateEditorForIndex(view, idx, NoActivate);
887 emit editorsClosed(QList<IEditor*>() << editor);
889 if (currentSplitterOrView) {
890 if (IEditor *currentEditor = currentSplitterOrView->editor())
891 activateEditor(currentSplitterOrView->view(), currentEditor);
895 Core::IEditor *EditorManager::pickUnusedEditor() const
897 foreach (IEditor *editor, openedEditors()) {
898 SplitterOrView *view = m_d->m_splitter->findView(editor);
899 if (!view || view->editor() != editor)
905 void EditorManager::activateEditorForIndex(const QModelIndex &index, OpenEditorFlags flags)
907 activateEditorForIndex(currentEditorView(), index, flags);
910 void EditorManager::activateEditorForIndex(Internal::EditorView *view, const QModelIndex &index, OpenEditorFlags flags)
913 IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
915 activateEditor(view, editor, flags);
919 QString fileName = index.data(Qt::UserRole + 1).toString();
920 QString id = index.data(Qt::UserRole + 2).toString();
921 openEditor(view, fileName, id, flags);
924 Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
926 Q_ASSERT(view && editor);
928 if (view->currentEditor() && view->currentEditor()->file() == editor->file())
929 editor = view->currentEditor();
931 if (!view->hasEditor(editor)) {
932 bool duplicateSupported = editor->duplicateSupported();
933 if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) {
934 if (editor != sourceView->editor() || !duplicateSupported) {
935 sourceView->view()->removeEditor(editor);
936 view->addEditor(editor);
937 view->setCurrentEditor(editor);
938 if (!sourceView->editor()) {
939 if (IEditor *replacement = pickUnusedEditor()) {
940 sourceView->view()->addEditor(replacement);
944 } else if (duplicateSupported) {
945 editor = duplicateEditor(editor);
947 m_d->m_editorModel->makeOriginal(editor);
950 view->addEditor(editor);
955 void EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
957 SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor);
958 EditorView *view = (splitterOrView ? splitterOrView->view() : 0);
959 // TODO an IEditor doesn't have to belong to a view, which makes this method a bit funny
961 view = currentEditorView();
962 activateEditor(view, editor, flags);
965 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
970 if (!m_d->m_currentEditor)
971 setCurrentEditor(0, (flags & IgnoreNavigationHistory));
975 editor = placeEditor(view, editor);
977 if (!(flags & NoActivate)) {
978 setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
979 if (flags & ModeSwitch) {
980 switchToPreferedMode();
983 editor->widget()->setFocus();
988 Core::IEditor *EditorManager::activateEditorForFile(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
991 const QList<IEditor*> editors = editorsForFile(file);
992 if (editors.isEmpty())
995 activateEditor(view, editors.first(), flags);
996 return editors.first();
999 /* For something that has a 'QStringList mimeTypes' (IEditorFactory
1000 * or IExternalEditor), find the one best matching the mimetype passed in.
1001 * Recurse over the parent classes of the mimetype to find them. */
1002 template <class EditorFactoryLike>
1003 static void mimeTypeFactoryRecursion(const MimeDatabase *db,
1004 const MimeType &mimeType,
1005 const QList<EditorFactoryLike*> &allFactories,
1006 bool firstMatchOnly,
1007 QList<EditorFactoryLike*> *list)
1009 typedef typename QList<EditorFactoryLike*>::const_iterator EditorFactoryLikeListConstIterator;
1010 // Loop factories to find type
1011 const QString type = mimeType.type();
1012 const EditorFactoryLikeListConstIterator fcend = allFactories.constEnd();
1013 for (EditorFactoryLikeListConstIterator fit = allFactories.constBegin(); fit != fcend; ++fit) {
1014 // Exclude duplicates when recursing over xml or C++ -> C -> text.
1015 EditorFactoryLike *factory = *fit;
1016 if (!list->contains(factory) && factory->mimeTypes().contains(type)) {
1017 list->push_back(*fit);
1023 // Any parent mime type classes? -> recurse
1024 QStringList parentTypes = mimeType.subClassesOf();
1025 if (parentTypes.empty())
1027 const QStringList::const_iterator pcend = parentTypes .constEnd();
1028 for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) {
1029 if (const MimeType parent = db->findByType(*pit))
1030 mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list);
1034 EditorManager::EditorFactoryList
1035 EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
1037 EditorFactoryList rc;
1038 const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
1039 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc);
1040 if (debugEditorManager)
1041 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1045 EditorManager::ExternalEditorList
1046 EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const
1048 ExternalEditorList rc;
1049 const ExternalEditorList allEditors = pluginManager()->getObjects<IExternalEditor>();
1050 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allEditors, bestMatchOnly, &rc);
1051 if (debugEditorManager)
1052 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1056 /* For something that has a 'QString id' (IEditorFactory
1057 * or IExternalEditor), find the one matching a id. */
1058 template <class EditorFactoryLike>
1059 inline EditorFactoryLike *findById(ExtensionSystem::PluginManager *pm,
1062 const QList<EditorFactoryLike *> factories = pm->template getObjects<EditorFactoryLike>();
1063 foreach(EditorFactoryLike *efl, factories)
1064 if (id == efl->id())
1069 IEditor *EditorManager::createEditor(const QString &editorId,
1070 const QString &fileName)
1072 if (debugEditorManager)
1073 qDebug() << Q_FUNC_INFO << editorId << fileName;
1075 EditorFactoryList factories;
1076 if (editorId.isEmpty()) {
1077 const QFileInfo fileInfo(fileName);
1078 // Find by mime type
1079 MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(fileInfo);
1081 qWarning("%s unable to determine mime type of %s/%s. Falling back to text/plain",
1082 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1083 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain"));
1085 // open text files > 48 MB in binary editor
1086 if (fileInfo.size() > maxTextFileSize() && mimeType.type().startsWith(QLatin1String("text")))
1087 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("application/octet-stream"));
1088 factories = editorFactories(mimeType, true);
1090 // Find by editor id
1091 if (IEditorFactory *factory = findById<IEditorFactory>(pluginManager(), editorId))
1092 factories.push_back(factory);
1094 if (factories.empty()) {
1095 qWarning("%s: unable to find an editor factory for the file '%s', editor Id '%s'.",
1096 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1100 IEditor *editor = factories.front()->createEditor(this);
1102 connect(editor, SIGNAL(changed()), this, SLOT(handleEditorStateChange()));
1104 emit editorCreated(editor, fileName);
1108 void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
1112 m_d->m_core->addContextObject(editor);
1114 m_d->m_editorModel->addEditor(editor, isDuplicate);
1116 const bool isTemporary = editor->isTemporary();
1117 const bool addWatcher = !isTemporary;
1118 m_d->m_core->fileManager()->addFile(editor->file(), addWatcher);
1120 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName(),
1123 emit editorOpened(editor);
1126 // Run the OpenWithDialog and return the editor id
1127 // selected by the user.
1128 QString EditorManager::getOpenWithEditorId(const QString &fileName,
1129 bool *isExternalEditor) const
1131 // Collect editors that can open the file
1132 const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName);
1135 QStringList allEditorIds;
1136 QStringList externalEditorIds;
1138 const EditorFactoryList editors = editorFactories(mt, false);
1139 const int size = editors.size();
1140 for (int i = 0; i < size; i++) {
1141 allEditorIds.push_back(editors.at(i)->id());
1144 const ExternalEditorList exEditors = externalEditors(mt, false);
1145 const int esize = exEditors.size();
1146 for (int i = 0; i < esize; i++) {
1147 externalEditorIds.push_back(exEditors.at(i)->id());
1148 allEditorIds.push_back(exEditors.at(i)->id());
1150 if (allEditorIds.empty())
1153 OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
1154 dialog.setEditors(allEditorIds);
1155 dialog.setCurrentEditor(0);
1156 if (dialog.exec() != QDialog::Accepted)
1158 const QString selectedId = dialog.editor();
1159 if (isExternalEditor)
1160 *isExternalEditor = externalEditorIds.contains(selectedId);
1164 IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
1165 OpenEditorFlags flags, bool *newEditor)
1167 return openEditor(currentEditorView(), fileName, editorId, flags, newEditor);
1170 int extractLineNumber(QString *fileName)
1172 int i = fileName->length() - 1;
1173 for (; i >= 0; --i) {
1174 if (!fileName->at(i).isNumber())
1179 if (fileName->at(i) == ':' || fileName->at(i) == '+') {
1180 int result = fileName->mid(i+1).toInt();
1182 *fileName = fileName->left(i);
1189 IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName,
1190 const QString &editorId, OpenEditorFlags flags, bool *newEditor)
1192 if (debugEditorManager)
1193 qDebug() << Q_FUNC_INFO << fileName << editorId;
1195 QString fn = fileName;
1196 int lineNumber = -1;
1197 if (flags && EditorManager::CanContainLineNumber)
1198 lineNumber = extractLineNumber(&fn);
1206 const QList<IEditor *> editors = editorsForFileName(fn);
1207 if (!editors.isEmpty()) {
1208 IEditor *editor = editors.first();
1209 if (flags && EditorManager::CanContainLineNumber)
1210 editor->gotoLine(lineNumber, -1);
1211 return activateEditor(view, editor, flags);
1214 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1215 IEditor *editor = createEditor(editorId, fn);
1216 // If we could not open the file in the requested editor, fall
1217 // back to the default editor:
1219 editor = createEditor(QString(), fn);
1220 QString errorString;
1221 if (!editor || !editor->open(&errorString, fn)) {
1222 QApplication::restoreOverrideCursor();
1223 QMessageBox::critical(m_d->m_core->mainWindow(), tr("File Error"), errorString);
1233 IEditor *result = activateEditor(view, editor, flags);
1234 if (editor == result)
1235 restoreEditorState(editor);
1237 if (flags && EditorManager::CanContainLineNumber)
1238 editor->gotoLine(lineNumber, -1);
1240 QApplication::restoreOverrideCursor();
1244 bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorId)
1246 IExternalEditor *ee = findById<IExternalEditor>(pluginManager(), editorId);
1249 QString errorMessage;
1250 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1251 const bool ok = ee->startEditor(fileName, &errorMessage);
1252 QApplication::restoreOverrideCursor();
1254 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage);
1258 QStringList EditorManager::getOpenFileNames() const
1260 QString selectedFilter;
1261 const QString &fileFilters = m_d->m_core->mimeDatabase()->allFiltersString(&selectedFilter);
1262 return ICore::instance()->fileManager()->getOpenFileNames(fileFilters,
1263 QString(), &selectedFilter);
1267 /// Empty mode == figure out the correct mode from the editor
1268 /// forcePrefered = true, switch to the mode even if the editor is visible in another mode
1269 /// forcePrefered = false, only switch if it is not visible
1270 void EditorManager::switchToPreferedMode()
1272 QString preferedMode;
1273 // Figure out preferred mode for editor
1274 if (m_d->m_currentEditor)
1275 preferedMode = m_d->m_currentEditor->preferredModeType();
1277 if (preferedMode.isEmpty())
1278 preferedMode = Constants::MODE_EDIT_TYPE;
1280 m_d->m_core->modeManager()->activateModeType(preferedMode);
1283 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1284 QString *titlePattern,
1285 const QString &contents)
1287 if (debugEditorManager)
1288 qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1290 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1294 const QChar dollar = QLatin1Char('$');
1296 QString base = *titlePattern;
1298 base = QLatin1String("unnamed$");
1299 if (base.contains(dollar)) {
1301 QSet<QString> docnames;
1302 foreach (IEditor *editor, openedEditors()) {
1303 QString name = editor->file()->fileName();
1304 if (name.isEmpty()) {
1305 name = editor->displayName();
1307 name = QFileInfo(name).completeBaseName();
1314 title.replace(QString(dollar), QString::number(i++));
1315 } while (docnames.contains(title));
1317 title = *titlePattern;
1319 *titlePattern = title;
1322 IEditor *edt = createEditor(editorId, title);
1324 QApplication::restoreOverrideCursor();
1328 if (!edt->createNew(contents)) {
1329 QApplication::restoreOverrideCursor();
1335 if (title.isEmpty())
1336 title = edt->displayName();
1338 edt->setDisplayName(title);
1340 QApplication::restoreOverrideCursor();
1344 bool EditorManager::hasEditor(const QString &fileName) const
1346 return !editorsForFileName(fileName).isEmpty();
1349 void EditorManager::restoreEditorState(IEditor *editor)
1351 QTC_ASSERT(editor, return);
1352 QString fileName = editor->file()->fileName();
1353 editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1356 bool EditorManager::saveEditor(IEditor *editor)
1358 return saveFile(editor->file());
1361 bool EditorManager::saveFile(IFile *fileParam)
1363 IFile *file = fileParam;
1364 if (!file && currentEditor())
1365 file = currentEditor()->file();
1369 file->checkPermissions();
1371 const QString &fileName = file->fileName();
1373 if (fileName.isEmpty())
1374 return saveFileAs(file);
1376 bool success = false;
1379 // try saving, no matter what isReadOnly tells us
1380 success = m_d->m_core->fileManager()->saveFile(file, QString(), &isReadOnly);
1382 if (!success && isReadOnly) {
1383 MakeWritableResult answer =
1384 makeFileWritable(file);
1385 if (answer == Failed)
1387 if (answer == SavedAs)
1390 file->checkPermissions();
1392 success = m_d->m_core->fileManager()->saveFile(file);
1396 addFileToRecentFiles(file);
1403 EditorManager::makeFileWritable(IFile *file)
1407 QString directory = QFileInfo(file->fileName()).absolutePath();
1408 IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory);
1409 const QString &fileName = file->fileName();
1411 switch (FileManager::promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), file->isSaveAsAllowed())) {
1412 case FileManager::RO_OpenVCS:
1413 if (!versionControl->vcsOpen(fileName)) {
1414 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC."));
1417 file->checkPermissions();
1418 return OpenedWithVersionControl;
1419 case FileManager::RO_MakeWriteable: {
1420 const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1422 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
1426 file->checkPermissions();
1427 return MadeWritable;
1428 case FileManager::RO_SaveAs :
1429 return saveFileAs(file) ? SavedAs : Failed;
1430 case FileManager::RO_Cancel:
1436 bool EditorManager::saveFileAs(IFile *fileParam)
1438 IFile *file = fileParam;
1439 if (!file && currentEditor())
1440 file = currentEditor()->file();
1444 const QString &filter = m_d->m_core->mimeDatabase()->allFiltersString();
1445 QString selectedFilter =
1446 m_d->m_core->mimeDatabase()->findByFile(QFileInfo(file->fileName())).filterString();
1447 const QString &absoluteFilePath =
1448 m_d->m_core->fileManager()->getSaveAsFileName(file, filter, &selectedFilter);
1450 if (absoluteFilePath.isEmpty())
1453 if (absoluteFilePath != file->fileName()) {
1454 // close existing editors for the new file name
1455 const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1456 if (!existList.isEmpty()) {
1457 closeEditors(existList, false);
1461 const bool success = m_d->m_core->fileManager()->saveFile(file, absoluteFilePath);
1462 file->checkPermissions();
1464 // @todo: There is an issue to be treated here. The new file might be of a different mime
1465 // type than the original and thus require a different editor. An alternative strategy
1466 // would be to close the current editor and open a new appropriate one, but this is not
1467 // a good way out either (also the undo stack would be lost). Perhaps the best is to
1468 // re-think part of the editors design.
1471 addFileToRecentFiles(file);
1477 /* Adds the file name to the recent files if there is at least one non-temporary editor for it */
1478 void EditorManager::addFileToRecentFiles(IFile *file)
1480 bool isTemporary = true;
1482 QList<IEditor *> editors = editorsForFile(file);
1483 foreach (IEditor *editor, editors) {
1484 if (!editor->isTemporary()) {
1485 editorId = editor->id();
1486 isTemporary = false;
1491 m_d->m_core->fileManager()->addToRecentFiles(file->fileName(), editorId);
1494 void EditorManager::gotoNextDocHistory()
1496 OpenEditorsWindow *dialog = windowPopup();
1497 if (dialog->isVisible()) {
1498 dialog->selectNextEditor();
1500 EditorView *view = currentEditorView();
1501 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1502 dialog->selectNextEditor();
1503 showPopupOrSelectDocument();
1507 void EditorManager::gotoPreviousDocHistory()
1509 OpenEditorsWindow *dialog = windowPopup();
1510 if (dialog->isVisible()) {
1511 dialog->selectPreviousEditor();
1513 EditorView *view = currentEditorView();
1514 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1515 dialog->selectPreviousEditor();
1516 showPopupOrSelectDocument();
1520 void EditorManager::makeCurrentEditorWritable()
1522 if (IEditor* curEditor = currentEditor())
1523 makeFileWritable(curEditor->file());
1526 void EditorManager::updateWindowTitle()
1528 QString windowTitle = tr("Qt Creator");
1529 if (!m_d->m_titleAddition.isEmpty()) {
1530 windowTitle.prepend(m_d->m_titleAddition + " - ");
1532 IEditor *curEditor = currentEditor();
1534 QString editorName = curEditor->displayName();
1535 if (!editorName.isEmpty())
1536 windowTitle.prepend(editorName + " - ");
1537 QString filePath = QFileInfo(curEditor->file()->fileName()).absoluteFilePath();
1538 if (!filePath.isEmpty())
1539 m_d->m_core->mainWindow()->setWindowFilePath(filePath);
1541 m_d->m_core->mainWindow()->setWindowFilePath(QString());
1543 m_d->m_core->mainWindow()->setWindowTitle(windowTitle);
1546 void EditorManager::handleEditorStateChange()
1549 IEditor *currEditor = currentEditor();
1550 if (qobject_cast<IEditor *>(sender()) == currEditor) {
1551 updateWindowTitle();
1552 emit currentEditorStateChanged(currEditor);
1556 void EditorManager::updateActions()
1559 IEditor *curEditor = currentEditor();
1560 int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1564 if (!curEditor->file()->fileName().isEmpty()) {
1565 QFileInfo fi(curEditor->file()->fileName());
1566 fName = fi.fileName();
1568 fName = curEditor->displayName();
1572 window()->setWindowModified(curEditor->file()->isModified());
1574 if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) {
1575 // we are about to change a read-only file, warn user
1576 showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"),
1577 tr("<b>Warning:</b> You are changing a read-only file."),
1578 tr("Make writable"), this, SLOT(makeCurrentEditorWritable()));
1580 hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"));
1583 } else { // curEditor
1584 window()->setWindowModified(false);
1588 m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified());
1589 m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed());
1590 m_d->m_revertToSavedAction->setEnabled(curEditor != 0
1591 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified());
1594 if (!fName.isEmpty())
1595 quotedName = '"' + fName + '"';
1597 m_d->m_saveAsAction->setText(tr("Save %1 &As...").arg(quotedName));
1598 m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName));
1599 m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName));
1601 m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0);
1602 m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName));
1603 m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0);
1604 m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1);
1605 m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others")));
1607 m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1608 m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1609 EditorView *view = currentEditorView();
1610 m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
1611 m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
1613 bool hasSplitter = m_d->m_splitter->isSplitter();
1614 m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter);
1615 m_d->m_removeAllSplitsAction->setEnabled(hasSplitter);
1616 m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter);
1619 bool EditorManager::hasSplitter() const
1621 return m_d->m_splitter->isSplitter();
1624 QList<IEditor*> EditorManager::visibleEditors() const
1626 QList<IEditor *> editors;
1627 if (m_d->m_splitter->isSplitter()) {
1628 SplitterOrView *firstView = m_d->m_splitter->findFirstView();
1629 SplitterOrView *view = firstView;
1633 editors.append(view->editor());
1634 view = m_d->m_splitter->findNextView(view);
1635 } while (view && view != firstView);
1638 if (m_d->m_splitter->editor()) {
1639 editors.append(m_d->m_splitter->editor());
1645 QList<IEditor*> EditorManager::openedEditors() const
1647 return m_d->m_editorModel->editors();
1650 OpenEditorsModel *EditorManager::openedEditorsModel() const
1652 return m_d->m_editorModel;
1655 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1657 currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1661 void EditorManager::cutForwardNavigationHistory()
1663 currentEditorView()->cutForwardNavigationHistory();
1667 void EditorManager::goBackInNavigationHistory()
1669 currentEditorView()->goBackInNavigationHistory();
1674 void EditorManager::goForwardInNavigationHistory()
1676 currentEditorView()->goForwardInNavigationHistory();
1680 OpenEditorsWindow *EditorManager::windowPopup() const
1682 return m_d->m_windowPopup;
1685 void EditorManager::showPopupOrSelectDocument() const
1687 if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1688 windowPopup()->selectAndHide();
1690 // EditorManager is invisible when invoked from Design Mode.
1691 const QPoint p = isVisible() ?
1692 mapToGlobal(QPoint(0, 0)) :
1693 m_d->m_core->mainWindow()->mapToGlobal(QPoint(0, 0));
1694 windowPopup()->move((width()-m_d->m_windowPopup->width())/2 + p.x(),
1695 (height()-m_d->m_windowPopup->height())/2 + p.y());
1696 windowPopup()->setVisible(true);
1700 // Save state of all non-teporary editors.
1701 QByteArray EditorManager::saveState() const
1704 QDataStream stream(&bytes, QIODevice::WriteOnly);
1706 stream << QByteArray("EditorManagerV4");
1708 QList<IEditor *> editors = openedEditors();
1709 foreach (IEditor *editor, editors) {
1710 if (!editor->file()->fileName().isEmpty()
1711 && !editor->isTemporary()) {
1712 QByteArray state = editor->saveState();
1713 if (!state.isEmpty())
1714 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
1718 stream << m_d->m_editorStates;
1720 QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1721 int entriesCount = 0;
1722 foreach (const OpenEditorsModel::Entry &entry, entries) {
1723 // The editor may be 0 if it was not loaded yet: In that case it is not temporary
1724 if (!entry.editor || !entry.editor->isTemporary())
1728 stream << entriesCount;
1730 foreach (const OpenEditorsModel::Entry &entry, entries) {
1731 if (!entry.editor || !entry.editor->isTemporary())
1732 stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1735 stream << m_d->m_splitter->saveState();
1740 bool EditorManager::restoreState(const QByteArray &state)
1742 closeAllEditors(true);
1744 QDataStream stream(state);
1749 if (version != "EditorManagerV4")
1752 QMap<QString, QVariant> editorstates;
1754 QApplication::setOverrideCursor(Qt::WaitCursor);
1756 stream >> editorstates;
1758 QMapIterator<QString, QVariant> i(editorstates);
1759 while (i.hasNext()) {
1761 m_d->m_editorStates.insert(i.key(), i.value());
1764 int editorCount = 0;
1765 stream >> editorCount;
1766 while (--editorCount >= 0) {
1769 QString displayName;
1770 stream >> displayName;
1774 if (!fileName.isEmpty() && !displayName.isEmpty())
1775 m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1778 QByteArray splitterstates;
1779 stream >> splitterstates;
1780 m_d->m_splitter->restoreState(splitterstates);
1782 // splitting and stuff results in focus trouble, that's why we set the focus again after restoration
1783 if (m_d->m_currentEditor) {
1784 m_d->m_currentEditor->widget()->setFocus();
1785 } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) {
1786 if (IEditor *e = view->editor())
1787 e->widget()->setFocus();
1788 else if (view->view())
1789 view->view()->setFocus();
1792 QApplication::restoreOverrideCursor();
1797 static const char documentStatesKey[] = "EditorManager/DocumentStates";
1798 static const char reloadBehaviorKey[] = "EditorManager/ReloadBehavior";
1800 void EditorManager::saveSettings()
1802 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1803 settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates);
1804 settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadSetting);
1807 void EditorManager::readSettings()
1809 // Backward compatibility to old locations for these settings
1810 QSettings *qs = m_d->m_core->settings();
1811 if (qs->contains(QLatin1String(documentStatesKey))) {
1812 m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey))
1813 .value<QMap<QString, QVariant> >();
1814 qs->remove(QLatin1String(documentStatesKey));
1817 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1818 if (settings->contains(QLatin1String(documentStatesKey)))
1819 m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey))
1820 .value<QMap<QString, QVariant> >();
1822 if (settings->contains(QLatin1String(reloadBehaviorKey)))
1823 m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1827 void EditorManager::revertToSaved()
1829 IEditor *currEditor = currentEditor();
1832 const QString fileName = currEditor->file()->fileName();
1833 if (fileName.isEmpty())
1835 if (currEditor->file()->isModified()) {
1836 QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"),
1837 tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)),
1838 QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow());
1839 msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
1840 msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
1841 msgBox.setDefaultButton(QMessageBox::No);
1842 msgBox.setEscapeButton(QMessageBox::No);
1843 if (msgBox.exec() == QMessageBox::No)
1847 QString errorString;
1848 if (!currEditor->file()->reload(&errorString, IFile::FlagReload, IFile::TypeContents))
1849 QMessageBox::critical(m_d->m_core->mainWindow(), tr("File Error"), errorString);
1852 void EditorManager::showEditorInfoBar(const QString &id,
1853 const QString &infoText,
1854 const QString &buttonText,
1855 QObject *object, const char *buttonPressMember,
1856 const char *cancelButtonPressMember)
1858 currentEditorView()->showEditorInfoBar(id, infoText, buttonText, object, buttonPressMember, cancelButtonPressMember);
1862 void EditorManager::hideEditorInfoBar(const QString &id)
1864 Core::Internal::EditorView *cev = currentEditorView();
1866 cev->hideEditorInfoBar(id);
1869 void EditorManager::showEditorStatusBar(const QString &id,
1870 const QString &infoText,
1871 const QString &buttonText,
1872 QObject *object, const char *member)
1875 currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1878 void EditorManager::hideEditorStatusBar(const QString &id)
1880 currentEditorView()->hideEditorStatusBar(id);
1883 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1885 m_d->m_reloadSetting = behavior;
1888 IFile::ReloadSetting EditorManager::reloadSetting() const
1890 return m_d->m_reloadSetting;
1893 QTextCodec *EditorManager::defaultTextCodec() const
1895 QSettings *settings = Core::ICore::instance()->settings();
1896 if (QTextCodec *candidate = QTextCodec::codecForName(
1897 settings->value(QLatin1String(Constants::SETTINGS_DEFAULTTEXTENCODING)).toByteArray()))
1899 return QTextCodec::codecForLocale();
1902 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
1904 if (!editor->duplicateSupported())
1907 IEditor *duplicate = editor->duplicate(0);
1908 duplicate->restoreState(editor->saveState());
1909 connect(duplicate, SIGNAL(changed()), this, SLOT(handleEditorStateChange()));
1910 emit editorCreated(duplicate, duplicate->file()->fileName());
1911 addEditor(duplicate, true);
1915 void EditorManager::split(Qt::Orientation orientation)
1917 SplitterOrView *view = m_d->m_currentView;
1919 view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor)
1920 : m_d->m_splitter->findFirstView();
1921 if (view && !view->splitter()) {
1922 view->split(orientation);
1927 void EditorManager::split()
1929 split(Qt::Vertical);
1932 void EditorManager::splitSideBySide()
1934 split(Qt::Horizontal);
1937 void EditorManager::removeCurrentSplit()
1939 SplitterOrView *viewToClose = m_d->m_currentView;
1940 if (!viewToClose && m_d->m_currentEditor)
1941 viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor);
1943 if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
1946 closeView(viewToClose->view());
1950 void EditorManager::removeAllSplits()
1952 if (!m_d->m_splitter->isSplitter())
1954 IEditor *editor = m_d->m_currentEditor;
1955 // trigger update below
1956 m_d->m_currentEditor = 0;
1957 if (editor && m_d->m_editorModel->isDuplicate(editor))
1958 m_d->m_editorModel->makeOriginal(editor);
1959 m_d->m_splitter->unsplitAll();
1961 editor = pickUnusedEditor();
1962 activateEditor(editor);
1965 void EditorManager::gotoOtherSplit()
1967 if (m_d->m_splitter->isSplitter()) {
1968 SplitterOrView *currentView = m_d->m_currentView;
1969 if (!currentView && m_d->m_currentEditor)
1970 currentView = m_d->m_splitter->findView(m_d->m_currentEditor);
1972 currentView = m_d->m_splitter->findFirstView();
1973 SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
1975 view = m_d->m_splitter->findFirstView();
1977 if (IEditor *editor = view->editor()) {
1978 setCurrentEditor(editor, true);
1979 editor->widget()->setFocus();
1981 setCurrentView(view);
1987 qint64 EditorManager::maxTextFileSize()
1989 return (qint64(3) << 24);
1992 void EditorManager::setWindowTitleAddition(const QString &addition)
1994 m_d->m_titleAddition = addition;
1995 updateWindowTitle();
1998 QString EditorManager::windowTitleAddition() const
2000 return m_d->m_titleAddition;
2003 void EditorManager::updateVariable(const QString &variable)
2005 if (variable == QLatin1String(kCurrentDocumentFilePath)
2006 || variable == QLatin1String(kCurrentDocumentPath)) {
2008 IEditor *curEditor = currentEditor();
2010 QString fileName = curEditor->file()->fileName();
2011 if (!fileName.isEmpty()) {
2012 if (variable == QLatin1String(kCurrentDocumentFilePath))
2013 value = QFileInfo(fileName).filePath();
2014 else if (variable == QLatin1String(kCurrentDocumentPath))
2015 value = QFileInfo(fileName).path();
2018 VariableManager::instance()->insert(variable, value);
2019 } else if (variable == QLatin1String(kCurrentDocumentXPos)) {
2021 IEditor *curEditor = currentEditor();
2023 value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).x());
2025 VariableManager::instance()->insert(variable, value);
2026 } else if (variable == QLatin1String(kCurrentDocumentYPos)) {
2028 IEditor *curEditor = currentEditor();
2030 value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).y());
2032 VariableManager::instance()->insert(variable, value);