1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "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 * const kCurrentDocumentFilePath = "CurrentDocument:FilePath";
89 static const char * const kCurrentDocumentPath = "CurrentDocument:Path";
90 static const char * const kCurrentDocumentXPos = "CurrentDocument:XPos";
91 static const char * const 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 if (!editor || !editor->open(fn)) {
1221 QApplication::restoreOverrideCursor();
1222 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), tr("Cannot open file %1!").arg(QDir::toNativeSeparators(fn)));
1232 IEditor *result = activateEditor(view, editor, flags);
1233 if (editor == result)
1234 restoreEditorState(editor);
1236 if (flags && EditorManager::CanContainLineNumber)
1237 editor->gotoLine(lineNumber, -1);
1239 QApplication::restoreOverrideCursor();
1243 bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorId)
1245 IExternalEditor *ee = findById<IExternalEditor>(pluginManager(), editorId);
1248 QString errorMessage;
1249 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1250 const bool ok = ee->startEditor(fileName, &errorMessage);
1251 QApplication::restoreOverrideCursor();
1253 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage);
1257 QStringList EditorManager::getOpenFileNames() const
1259 QString selectedFilter;
1260 const QString &fileFilters = m_d->m_core->mimeDatabase()->allFiltersString(&selectedFilter);
1261 return ICore::instance()->fileManager()->getOpenFileNames(fileFilters,
1262 QString(), &selectedFilter);
1266 /// Empty mode == figure out the correct mode from the editor
1267 /// forcePrefered = true, switch to the mode even if the editor is visible in another mode
1268 /// forcePrefered = false, only switch if it is not visible
1269 void EditorManager::switchToPreferedMode()
1271 QString preferedMode;
1272 // Figure out preferred mode for editor
1273 if (m_d->m_currentEditor)
1274 preferedMode = m_d->m_currentEditor->preferredModeType();
1276 if (preferedMode.isEmpty())
1277 preferedMode = Constants::MODE_EDIT_TYPE;
1279 m_d->m_core->modeManager()->activateModeType(preferedMode);
1282 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1283 QString *titlePattern,
1284 const QString &contents)
1286 if (debugEditorManager)
1287 qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1289 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1293 const QChar dollar = QLatin1Char('$');
1295 QString base = *titlePattern;
1297 base = QLatin1String("unnamed$");
1298 if (base.contains(dollar)) {
1300 QSet<QString> docnames;
1301 foreach (IEditor *editor, openedEditors()) {
1302 QString name = editor->file()->fileName();
1303 if (name.isEmpty()) {
1304 name = editor->displayName();
1306 name = QFileInfo(name).completeBaseName();
1313 title.replace(QString(dollar), QString::number(i++));
1314 } while (docnames.contains(title));
1316 title = *titlePattern;
1318 *titlePattern = title;
1321 IEditor *edt = createEditor(editorId, title);
1323 QApplication::restoreOverrideCursor();
1327 if (!edt->createNew(contents)) {
1328 QApplication::restoreOverrideCursor();
1334 if (title.isEmpty())
1335 title = edt->displayName();
1337 edt->setDisplayName(title);
1339 QApplication::restoreOverrideCursor();
1343 bool EditorManager::hasEditor(const QString &fileName) const
1345 return !editorsForFileName(fileName).isEmpty();
1348 void EditorManager::restoreEditorState(IEditor *editor)
1350 QTC_ASSERT(editor, return);
1351 QString fileName = editor->file()->fileName();
1352 editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1355 bool EditorManager::saveEditor(IEditor *editor)
1357 return saveFile(editor->file());
1360 bool EditorManager::saveFile(IFile *fileParam)
1362 IFile *file = fileParam;
1363 if (!file && currentEditor())
1364 file = currentEditor()->file();
1368 file->checkPermissions();
1370 const QString &fileName = file->fileName();
1372 if (fileName.isEmpty())
1373 return saveFileAs(file);
1375 bool success = false;
1377 // try saving, no matter what isReadOnly tells us
1378 m_d->m_core->fileManager()->blockFileChange(file);
1379 success = file->save(fileName);
1380 m_d->m_core->fileManager()->unblockFileChange(file);
1383 MakeWritableResult answer =
1384 makeFileWritable(file);
1385 if (answer == Failed)
1387 if (answer == SavedAs)
1390 file->checkPermissions();
1392 m_d->m_core->fileManager()->blockFileChange(file);
1393 success = file->save(fileName);
1394 m_d->m_core->fileManager()->unblockFileChange(file);
1398 addFileToRecentFiles(file);
1405 EditorManager::makeFileWritable(IFile *file)
1409 QString directory = QFileInfo(file->fileName()).absolutePath();
1410 IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory);
1411 const QString &fileName = file->fileName();
1413 switch (FileManager::promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), file->isSaveAsAllowed())) {
1414 case FileManager::RO_OpenVCS:
1415 if (!versionControl->vcsOpen(fileName)) {
1416 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC."));
1419 file->checkPermissions();
1420 return OpenedWithVersionControl;
1421 case FileManager::RO_MakeWriteable: {
1422 const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1424 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
1428 file->checkPermissions();
1429 return MadeWritable;
1430 case FileManager::RO_SaveAs :
1431 return saveFileAs(file) ? SavedAs : Failed;
1432 case FileManager::RO_Cancel:
1438 bool EditorManager::saveFileAs(IFile *fileParam)
1440 IFile *file = fileParam;
1441 if (!file && currentEditor())
1442 file = currentEditor()->file();
1446 const QString &filter = m_d->m_core->mimeDatabase()->allFiltersString();
1447 QString selectedFilter =
1448 m_d->m_core->mimeDatabase()->findByFile(QFileInfo(file->fileName())).filterString();
1449 const QString &absoluteFilePath =
1450 m_d->m_core->fileManager()->getSaveAsFileName(file, filter, &selectedFilter);
1452 if (absoluteFilePath.isEmpty())
1455 if (absoluteFilePath != file->fileName()) {
1456 // close existing editors for the new file name
1457 const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1458 if (!existList.isEmpty()) {
1459 closeEditors(existList, false);
1463 m_d->m_core->fileManager()->blockFileChange(file);
1464 const bool success = file->save(absoluteFilePath);
1465 m_d->m_core->fileManager()->unblockFileChange(file);
1466 file->checkPermissions();
1468 // @todo: There is an issue to be treated here. The new file might be of a different mime
1469 // type than the original and thus require a different editor. An alternative strategy
1470 // would be to close the current editor and open a new appropriate one, but this is not
1471 // a good way out either (also the undo stack would be lost). Perhaps the best is to
1472 // re-think part of the editors design.
1475 addFileToRecentFiles(file);
1481 /* Adds the file name to the recent files if there is at least one non-temporary editor for it */
1482 void EditorManager::addFileToRecentFiles(IFile *file)
1484 bool isTemporary = true;
1486 QList<IEditor *> editors = editorsForFile(file);
1487 foreach (IEditor *editor, editors) {
1488 if (!editor->isTemporary()) {
1489 editorId = editor->id();
1490 isTemporary = false;
1495 m_d->m_core->fileManager()->addToRecentFiles(file->fileName(), editorId);
1498 void EditorManager::gotoNextDocHistory()
1500 OpenEditorsWindow *dialog = windowPopup();
1501 if (dialog->isVisible()) {
1502 dialog->selectNextEditor();
1504 EditorView *view = currentEditorView();
1505 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1506 dialog->selectNextEditor();
1507 showPopupOrSelectDocument();
1511 void EditorManager::gotoPreviousDocHistory()
1513 OpenEditorsWindow *dialog = windowPopup();
1514 if (dialog->isVisible()) {
1515 dialog->selectPreviousEditor();
1517 EditorView *view = currentEditorView();
1518 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1519 dialog->selectPreviousEditor();
1520 showPopupOrSelectDocument();
1524 void EditorManager::makeCurrentEditorWritable()
1526 if (IEditor* curEditor = currentEditor())
1527 makeFileWritable(curEditor->file());
1530 void EditorManager::updateWindowTitle()
1532 QString windowTitle = tr("Qt Creator");
1533 if (!m_d->m_titleAddition.isEmpty()) {
1534 windowTitle.prepend(m_d->m_titleAddition + " - ");
1536 IEditor *curEditor = currentEditor();
1538 QString editorName = curEditor->displayName();
1539 if (!editorName.isEmpty())
1540 windowTitle.prepend(editorName + " - ");
1541 QString filePath = QFileInfo(curEditor->file()->fileName()).absoluteFilePath();
1542 if (!filePath.isEmpty())
1543 m_d->m_core->mainWindow()->setWindowFilePath(filePath);
1545 m_d->m_core->mainWindow()->setWindowFilePath(QString());
1547 m_d->m_core->mainWindow()->setWindowTitle(windowTitle);
1550 void EditorManager::handleEditorStateChange()
1553 IEditor *currEditor = currentEditor();
1554 if (qobject_cast<IEditor *>(sender()) == currEditor) {
1555 updateWindowTitle();
1556 emit currentEditorStateChanged(currEditor);
1560 void EditorManager::updateActions()
1563 IEditor *curEditor = currentEditor();
1564 int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1568 if (!curEditor->file()->fileName().isEmpty()) {
1569 QFileInfo fi(curEditor->file()->fileName());
1570 fName = fi.fileName();
1572 fName = curEditor->displayName();
1576 window()->setWindowModified(curEditor->file()->isModified());
1578 if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) {
1579 // we are about to change a read-only file, warn user
1580 showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"),
1581 tr("<b>Warning:</b> You are changing a read-only file."),
1582 tr("Make writable"), this, SLOT(makeCurrentEditorWritable()));
1584 hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"));
1587 } else { // curEditor
1588 window()->setWindowModified(false);
1592 m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified());
1593 m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed());
1594 m_d->m_revertToSavedAction->setEnabled(curEditor != 0
1595 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified());
1598 if (!fName.isEmpty())
1599 quotedName = '"' + fName + '"';
1601 m_d->m_saveAsAction->setText(tr("Save %1 &As...").arg(quotedName));
1602 m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName));
1603 m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName));
1605 m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0);
1606 m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName));
1607 m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0);
1608 m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1);
1609 m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others")));
1611 m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1612 m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1613 EditorView *view = currentEditorView();
1614 m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
1615 m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
1617 bool hasSplitter = m_d->m_splitter->isSplitter();
1618 m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter);
1619 m_d->m_removeAllSplitsAction->setEnabled(hasSplitter);
1620 m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter);
1623 bool EditorManager::hasSplitter() const
1625 return m_d->m_splitter->isSplitter();
1628 QList<IEditor*> EditorManager::visibleEditors() const
1630 QList<IEditor *> editors;
1631 if (m_d->m_splitter->isSplitter()) {
1632 SplitterOrView *firstView = m_d->m_splitter->findFirstView();
1633 SplitterOrView *view = firstView;
1637 editors.append(view->editor());
1638 view = m_d->m_splitter->findNextView(view);
1639 } while (view && view != firstView);
1642 if (m_d->m_splitter->editor()) {
1643 editors.append(m_d->m_splitter->editor());
1649 QList<IEditor*> EditorManager::openedEditors() const
1651 return m_d->m_editorModel->editors();
1654 OpenEditorsModel *EditorManager::openedEditorsModel() const
1656 return m_d->m_editorModel;
1659 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1661 currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1665 void EditorManager::cutForwardNavigationHistory()
1667 currentEditorView()->cutForwardNavigationHistory();
1671 void EditorManager::goBackInNavigationHistory()
1673 currentEditorView()->goBackInNavigationHistory();
1678 void EditorManager::goForwardInNavigationHistory()
1680 currentEditorView()->goForwardInNavigationHistory();
1684 OpenEditorsWindow *EditorManager::windowPopup() const
1686 return m_d->m_windowPopup;
1689 void EditorManager::showPopupOrSelectDocument() const
1691 if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1692 windowPopup()->selectAndHide();
1694 // EditorManager is invisible when invoked from Design Mode.
1695 const QPoint p = isVisible() ?
1696 mapToGlobal(QPoint(0, 0)) :
1697 m_d->m_core->mainWindow()->mapToGlobal(QPoint(0, 0));
1698 windowPopup()->move((width()-m_d->m_windowPopup->width())/2 + p.x(),
1699 (height()-m_d->m_windowPopup->height())/2 + p.y());
1700 windowPopup()->setVisible(true);
1704 // Save state of all non-teporary editors.
1705 QByteArray EditorManager::saveState() const
1708 QDataStream stream(&bytes, QIODevice::WriteOnly);
1710 stream << QByteArray("EditorManagerV4");
1712 QList<IEditor *> editors = openedEditors();
1713 foreach (IEditor *editor, editors) {
1714 if (!editor->file()->fileName().isEmpty()
1715 && !editor->isTemporary()) {
1716 QByteArray state = editor->saveState();
1717 if (!state.isEmpty())
1718 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
1722 stream << m_d->m_editorStates;
1724 QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1725 int entriesCount = 0;
1726 foreach (const OpenEditorsModel::Entry &entry, entries) {
1727 // The editor may be 0 if it was not loaded yet: In that case it is not temporary
1728 if (!entry.editor || !entry.editor->isTemporary())
1732 stream << entriesCount;
1734 foreach (const OpenEditorsModel::Entry &entry, entries) {
1735 if (!entry.editor || !entry.editor->isTemporary())
1736 stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1739 stream << m_d->m_splitter->saveState();
1744 bool EditorManager::restoreState(const QByteArray &state)
1746 closeAllEditors(true);
1748 QDataStream stream(state);
1753 if (version != "EditorManagerV4")
1756 QMap<QString, QVariant> editorstates;
1758 QApplication::setOverrideCursor(Qt::WaitCursor);
1760 stream >> editorstates;
1762 QMapIterator<QString, QVariant> i(editorstates);
1763 while (i.hasNext()) {
1765 m_d->m_editorStates.insert(i.key(), i.value());
1768 int editorCount = 0;
1769 stream >> editorCount;
1770 while (--editorCount >= 0) {
1773 QString displayName;
1774 stream >> displayName;
1778 if (!fileName.isEmpty() && !displayName.isEmpty())
1779 m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1782 QByteArray splitterstates;
1783 stream >> splitterstates;
1784 m_d->m_splitter->restoreState(splitterstates);
1786 // splitting and stuff results in focus trouble, that's why we set the focus again after restoration
1787 if (m_d->m_currentEditor) {
1788 m_d->m_currentEditor->widget()->setFocus();
1789 } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) {
1790 if (IEditor *e = view->editor())
1791 e->widget()->setFocus();
1792 else if (view->view())
1793 view->view()->setFocus();
1796 QApplication::restoreOverrideCursor();
1801 static const char * const documentStatesKey = "EditorManager/DocumentStates";
1802 static const char * const reloadBehaviorKey = "EditorManager/ReloadBehavior";
1804 void EditorManager::saveSettings()
1806 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1807 settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates);
1808 settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadSetting);
1811 void EditorManager::readSettings()
1813 // Backward compatibility to old locations for these settings
1814 QSettings *qs = m_d->m_core->settings();
1815 if (qs->contains(QLatin1String(documentStatesKey))) {
1816 m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey))
1817 .value<QMap<QString, QVariant> >();
1818 qs->remove(QLatin1String(documentStatesKey));
1821 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1822 if (settings->contains(QLatin1String(documentStatesKey)))
1823 m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey))
1824 .value<QMap<QString, QVariant> >();
1826 if (settings->contains(QLatin1String(reloadBehaviorKey)))
1827 m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1831 void EditorManager::revertToSaved()
1833 IEditor *currEditor = currentEditor();
1836 const QString fileName = currEditor->file()->fileName();
1837 if (fileName.isEmpty())
1839 if (currEditor->file()->isModified()) {
1840 QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"),
1841 tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)),
1842 QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow());
1843 msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
1844 msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
1845 msgBox.setDefaultButton(QMessageBox::No);
1846 msgBox.setEscapeButton(QMessageBox::No);
1847 if (msgBox.exec() == QMessageBox::No)
1851 currEditor->file()->reload(IFile::FlagReload, IFile::TypeContents);
1854 void EditorManager::showEditorInfoBar(const QString &id,
1855 const QString &infoText,
1856 const QString &buttonText,
1857 QObject *object, const char *buttonPressMember,
1858 const char *cancelButtonPressMember)
1860 currentEditorView()->showEditorInfoBar(id, infoText, buttonText, object, buttonPressMember, cancelButtonPressMember);
1864 void EditorManager::hideEditorInfoBar(const QString &id)
1866 Core::Internal::EditorView *cev = currentEditorView();
1868 cev->hideEditorInfoBar(id);
1871 void EditorManager::showEditorStatusBar(const QString &id,
1872 const QString &infoText,
1873 const QString &buttonText,
1874 QObject *object, const char *member)
1877 currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1880 void EditorManager::hideEditorStatusBar(const QString &id)
1882 currentEditorView()->hideEditorStatusBar(id);
1885 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1887 m_d->m_reloadSetting = behavior;
1890 IFile::ReloadSetting EditorManager::reloadSetting() const
1892 return m_d->m_reloadSetting;
1895 QTextCodec *EditorManager::defaultTextCodec() const
1897 QSettings *settings = Core::ICore::instance()->settings();
1898 if (QTextCodec *candidate = QTextCodec::codecForName(
1899 settings->value(QLatin1String(Constants::SETTINGS_DEFAULTTEXTENCODING)).toByteArray()))
1901 return QTextCodec::codecForLocale();
1904 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
1906 if (!editor->duplicateSupported())
1909 IEditor *duplicate = editor->duplicate(0);
1910 duplicate->restoreState(editor->saveState());
1911 connect(duplicate, SIGNAL(changed()), this, SLOT(handleEditorStateChange()));
1912 emit editorCreated(duplicate, duplicate->file()->fileName());
1913 addEditor(duplicate, true);
1917 void EditorManager::split(Qt::Orientation orientation)
1919 SplitterOrView *view = m_d->m_currentView;
1921 view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor)
1922 : m_d->m_splitter->findFirstView();
1923 if (view && !view->splitter()) {
1924 view->split(orientation);
1929 void EditorManager::split()
1931 split(Qt::Vertical);
1934 void EditorManager::splitSideBySide()
1936 split(Qt::Horizontal);
1939 void EditorManager::removeCurrentSplit()
1941 SplitterOrView *viewToClose = m_d->m_currentView;
1942 if (!viewToClose && m_d->m_currentEditor)
1943 viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor);
1945 if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
1948 closeView(viewToClose->view());
1952 void EditorManager::removeAllSplits()
1954 if (!m_d->m_splitter->isSplitter())
1956 IEditor *editor = m_d->m_currentEditor;
1957 // trigger update below
1958 m_d->m_currentEditor = 0;
1959 if (editor && m_d->m_editorModel->isDuplicate(editor))
1960 m_d->m_editorModel->makeOriginal(editor);
1961 m_d->m_splitter->unsplitAll();
1963 editor = pickUnusedEditor();
1964 activateEditor(editor);
1967 void EditorManager::gotoOtherSplit()
1969 if (m_d->m_splitter->isSplitter()) {
1970 SplitterOrView *currentView = m_d->m_currentView;
1971 if (!currentView && m_d->m_currentEditor)
1972 currentView = m_d->m_splitter->findView(m_d->m_currentEditor);
1974 currentView = m_d->m_splitter->findFirstView();
1975 SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
1977 view = m_d->m_splitter->findFirstView();
1979 if (IEditor *editor = view->editor()) {
1980 setCurrentEditor(editor, true);
1981 editor->widget()->setFocus();
1983 setCurrentView(view);
1989 qint64 EditorManager::maxTextFileSize()
1991 return (qint64(3) << 24);
1994 void EditorManager::setWindowTitleAddition(const QString &addition)
1996 m_d->m_titleAddition = addition;
1997 updateWindowTitle();
2000 QString EditorManager::windowTitleAddition() const
2002 return m_d->m_titleAddition;
2005 void EditorManager::updateVariable(const QString &variable)
2007 if (variable == QLatin1String(kCurrentDocumentFilePath)
2008 || variable == QLatin1String(kCurrentDocumentPath)) {
2010 IEditor *curEditor = currentEditor();
2012 QString fileName = curEditor->file()->fileName();
2013 if (!fileName.isEmpty()) {
2014 if (variable == QLatin1String(kCurrentDocumentFilePath))
2015 value = QFileInfo(fileName).filePath();
2016 else if (variable == QLatin1String(kCurrentDocumentPath))
2017 value = QFileInfo(fileName).path();
2020 VariableManager::instance()->insert(variable, value);
2021 } else if (variable == QLatin1String(kCurrentDocumentXPos)) {
2023 IEditor *curEditor = currentEditor();
2025 value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).x());
2027 VariableManager::instance()->insert(variable, value);
2028 } else if (variable == QLatin1String(kCurrentDocumentYPos)) {
2030 IEditor *curEditor = currentEditor();
2032 value = QString::number(curEditor->widget()->mapToGlobal(QPoint(0,0)).y());
2034 VariableManager::instance()->insert(variable, value);